From 5e1f646c67c954fe7d55a747e3ffdd07e20cb46a Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 15 Sep 2016 01:52:25 -0400 Subject: ./tools/notsd-move --- src/Makefile | 63 +- src/ac-power/Makefile | 1 - src/ac-power/ac-power.c | 35 - src/activate/Makefile | 1 - src/activate/activate.c | 545 - src/analyze/.gitignore | 1 - src/analyze/Makefile | 1 - src/analyze/analyze-verify.c | 304 - src/analyze/analyze-verify.h | 26 - src/analyze/analyze.c | 1485 - src/ask-password/Makefile | 1 - src/ask-password/ask-password.c | 188 - src/backlight/Makefile | 1 - src/backlight/backlight.c | 434 - src/basic/.gitignore | 16 - src/basic/Makefile | 1 - src/basic/MurmurHash2.c | 86 - src/basic/MurmurHash2.h | 33 - src/basic/af-list.c | 56 - src/basic/af-list.h | 41 - src/basic/alloc-util.c | 83 - src/basic/alloc-util.h | 111 - src/basic/architecture.c | 179 - src/basic/architecture.h | 199 - src/basic/arphrd-list.c | 56 - src/basic/arphrd-list.h | 25 - src/basic/async.c | 94 - src/basic/async.h | 25 - src/basic/audit-util.c | 104 - src/basic/audit-util.h | 31 - src/basic/barrier.c | 415 - src/basic/barrier.h | 91 - src/basic/bitmap.c | 237 - src/basic/bitmap.h | 49 - src/basic/blkid-util.h | 31 - src/basic/btrfs-ctree.h | 96 - src/basic/btrfs-util.c | 2075 - src/basic/btrfs-util.h | 131 - src/basic/build.h | 155 - src/basic/bus-label.c | 98 - src/basic/bus-label.h | 31 - src/basic/calendarspec.c | 1127 - src/basic/calendarspec.h | 58 - src/basic/cap-list.c | 66 - src/basic/cap-list.h | 24 - src/basic/capability-util.c | 361 - src/basic/capability-util.h | 57 - src/basic/cgroup-util.c | 2365 - src/basic/cgroup-util.h | 236 - src/basic/chattr-util.c | 107 - src/basic/chattr-util.h | 26 - src/basic/clock-util.c | 165 - src/basic/clock-util.h | 29 - src/basic/conf-files.c | 166 - src/basic/conf-files.h | 25 - src/basic/copy.c | 603 - src/basic/copy.h | 36 - src/basic/cpu-set-util.c | 114 - src/basic/cpu-set-util.h | 32 - src/basic/def.h | 90 - src/basic/device-nodes.c | 80 - src/basic/device-nodes.h | 26 - src/basic/dirent-util.c | 74 - src/basic/dirent-util.h | 52 - src/basic/env-util.c | 624 - src/basic/env-util.h | 51 - src/basic/errno-list.c | 57 - src/basic/errno-list.h | 25 - src/basic/escape.c | 502 - src/basic/escape.h | 54 - src/basic/ether-addr-util.c | 125 - src/basic/ether-addr-util.h | 39 - src/basic/exit-status.c | 234 - src/basic/exit-status.h | 106 - src/basic/extract-word.c | 298 - src/basic/extract-word.h | 35 - src/basic/fd-util.c | 380 - src/basic/fd-util.h | 80 - src/basic/fileio-label.c | 68 - src/basic/fileio-label.h | 30 - src/basic/fileio.c | 1398 - src/basic/fileio.h | 90 - src/basic/formats-util.h | 79 - src/basic/fs-util.c | 539 - src/basic/fs-util.h | 78 - src/basic/glob-util.c | 70 - src/basic/glob-util.h | 36 - src/basic/gunicode.c | 112 - src/basic/gunicode.h | 30 - src/basic/hash-funcs.c | 81 - src/basic/hash-funcs.h | 65 - src/basic/hashmap.c | 1828 - src/basic/hashmap.h | 372 - src/basic/hexdecoct.c | 754 - src/basic/hexdecoct.h | 56 - src/basic/hostname-util.c | 252 - src/basic/hostname-util.h | 41 - src/basic/in-addr-util.c | 449 - src/basic/in-addr-util.h | 64 - src/basic/io-util.c | 269 - src/basic/io-util.h | 95 - src/basic/ioprio.h | 55 - src/basic/label.c | 82 - src/basic/label.h | 28 - src/basic/list.h | 182 - src/basic/locale-util.c | 322 - src/basic/locale-util.h | 73 - src/basic/lockfile-util.c | 153 - src/basic/lockfile-util.h | 39 - src/basic/log.c | 1170 - src/basic/log.h | 249 - src/basic/login-util.c | 31 - src/basic/login-util.h | 29 - src/basic/macro.h | 415 - src/basic/memfd-util.c | 174 - src/basic/memfd-util.h | 36 - src/basic/mempool.c | 104 - src/basic/mempool.h | 47 - src/basic/missing.h | 1044 - src/basic/missing_syscall.h | 300 - src/basic/mkdir-label.c | 38 - src/basic/mkdir.c | 128 - src/basic/mkdir.h | 38 - src/basic/mount-util.c | 559 - src/basic/mount-util.h | 54 - src/basic/nss-util.h | 199 - src/basic/ordered-set.c | 64 - src/basic/ordered-set.h | 74 - src/basic/parse-util.c | 553 - src/basic/parse-util.h | 109 - src/basic/path-util.c | 816 - src/basic/path-util.h | 127 - src/basic/prioq.c | 320 - src/basic/prioq.h | 43 - src/basic/proc-cmdline.c | 188 - src/basic/proc-cmdline.h | 27 - src/basic/process-util.c | 860 - src/basic/process-util.h | 105 - src/basic/random-util.c | 133 - src/basic/random-util.h | 39 - src/basic/ratelimit.c | 56 - src/basic/ratelimit.h | 58 - src/basic/raw-clone.h | 81 - src/basic/refcnt.h | 34 - src/basic/replace-var.c | 112 - src/basic/replace-var.h | 22 - src/basic/rlimit-util.c | 321 - src/basic/rlimit-util.h | 36 - src/basic/rm-rf.c | 236 - src/basic/rm-rf.h | 41 - src/basic/securebits.h | 45 - src/basic/selinux-util.c | 485 - src/basic/selinux-util.h | 51 - src/basic/set.h | 138 - src/basic/sigbus.c | 152 - src/basic/sigbus.h | 25 - src/basic/signal-util.c | 278 - src/basic/signal-util.h | 56 - src/basic/siphash24.c | 193 - src/basic/siphash24.h | 23 - src/basic/smack-util.c | 241 - src/basic/smack-util.h | 54 - src/basic/socket-label.c | 170 - src/basic/socket-util.c | 1048 - src/basic/socket-util.h | 154 - src/basic/sparse-endian.h | 88 - src/basic/special.h | 119 - src/basic/stat-util.c | 218 - src/basic/stat-util.h | 69 - src/basic/stdio-util.h | 76 - src/basic/strbuf.c | 205 - src/basic/strbuf.h | 54 - src/basic/string-table.c | 34 - src/basic/string-table.h | 119 - src/basic/string-util.c | 855 - src/basic/string-util.h | 194 - src/basic/strv.c | 943 - src/basic/strv.h | 174 - src/basic/strxcpyx.c | 100 - src/basic/strxcpyx.h | 31 - src/basic/syslog-util.c | 114 - src/basic/syslog-util.h | 32 - src/basic/terminal-util.c | 1221 - src/basic/terminal-util.h | 128 - src/basic/time-util.c | 1229 - src/basic/time-util.h | 178 - src/basic/umask-util.h | 46 - src/basic/unaligned.h | 129 - src/basic/unit-name.c | 1049 - src/basic/unit-name.h | 367 - src/basic/user-util.c | 481 - src/basic/user-util.h | 70 - src/basic/utf8.c | 409 - src/basic/utf8.h | 60 - src/basic/util.c | 876 - src/basic/util.h | 192 - src/basic/verbs.c | 101 - src/basic/verbs.h | 33 - src/basic/virt.c | 516 - src/basic/virt.h | 72 - src/basic/web-util.c | 76 - src/basic/web-util.h | 30 - src/basic/xattr-util.c | 200 - src/basic/xattr-util.h | 37 - src/basic/xml.c | 255 - src/basic/xml.h | 32 - src/binfmt/Makefile | 1 - src/binfmt/binfmt.c | 203 - src/boot/Makefile | 1 - src/boot/bootctl.c | 1143 - src/boot/efi/.gitignore | 2 - src/boot/efi/boot.c | 1857 - src/boot/efi/console.c | 135 - src/boot/efi/console.h | 32 - src/boot/efi/disk.c | 49 - src/boot/efi/disk.h | 19 - src/boot/efi/graphics.c | 88 - src/boot/efi/graphics.h | 22 - src/boot/efi/linux.c | 128 - src/boot/efi/linux.h | 22 - src/boot/efi/measure.c | 312 - src/boot/efi/measure.h | 21 - src/boot/efi/pefile.c | 170 - src/boot/efi/pefile.h | 20 - src/boot/efi/splash.c | 321 - src/boot/efi/splash.h | 20 - src/boot/efi/stub.c | 130 - src/boot/efi/util.c | 345 - src/boot/efi/util.h | 48 - src/busctl/Makefile | 37 + src/busctl/busctl-introspect.c | 791 + src/busctl/busctl-introspect.h | 32 + src/busctl/busctl.c | 2088 + src/busctl/busctl.completion.bash | 189 + src/busctl/busctl.completion.zsh | 72 + src/busctl/busctl.xml | 482 + src/cgls/Makefile | 1 - src/cgls/cgls.c | 288 - src/cgroups-agent/Makefile | 1 - src/cgroups-agent/cgroups-agent.c | 67 - src/cgtop/Makefile | 1 - src/cgtop/cgtop.c | 1133 - src/core/.gitignore | 3 - src/core/Makefile | 1 - src/core/audit-fd.c | 73 - src/core/audit-fd.h | 23 - src/core/automount.c | 1122 - src/core/automount.h | 59 - src/core/bus-policy.c | 180 - src/core/bus-policy.h | 64 - src/core/busname.c | 1077 - src/core/busname.h | 69 - src/core/cgroup.c | 1991 - src/core/cgroup.h | 183 - src/core/dbus-automount.c | 34 - src/core/dbus-automount.h | 23 - src/core/dbus-busname.c | 37 - src/core/dbus-busname.h | 23 - src/core/dbus-cgroup.c | 1109 - src/core/dbus-cgroup.h | 28 - src/core/dbus-device.c | 28 - src/core/dbus-device.h | 24 - src/core/dbus-execute.c | 1554 - src/core/dbus-execute.h | 45 - src/core/dbus-job.c | 193 - src/core/dbus-job.h | 31 - src/core/dbus-kill.c | 122 - src/core/dbus-kill.h | 29 - src/core/dbus-manager.c | 2306 - src/core/dbus-manager.h | 28 - src/core/dbus-mount.c | 211 - src/core/dbus-mount.h | 29 - src/core/dbus-path.c | 86 - src/core/dbus-path.h | 24 - src/core/dbus-scope.c | 229 - src/core/dbus-scope.h | 31 - src/core/dbus-service.c | 322 - src/core/dbus-service.h | 29 - src/core/dbus-slice.c | 52 - src/core/dbus-slice.h | 29 - src/core/dbus-socket.c | 184 - src/core/dbus-socket.h | 29 - src/core/dbus-swap.c | 115 - src/core/dbus-swap.h | 30 - src/core/dbus-target.c | 26 - src/core/dbus-target.h | 24 - src/core/dbus-timer.c | 352 - src/core/dbus-timer.h | 28 - src/core/dbus-unit.c | 1423 - src/core/dbus-unit.h | 41 - src/core/dbus.c | 1243 - src/core/dbus.h | 44 - src/core/device.c | 876 - src/core/device.h | 47 - src/core/execute.c | 3284 - src/core/execute.h | 291 - src/core/failure-action.c | 127 - src/core/failure-action.h | 41 - src/core/hostname-setup.c | 68 - src/core/hostname-setup.h | 22 - src/core/ima-setup.c | 80 - src/core/ima-setup.h | 24 - src/core/job.c | 1259 - src/core/job.h | 242 - src/core/kill.c | 68 - src/core/kill.h | 65 - src/core/killall.c | 248 - src/core/killall.h | 22 - src/core/kmod-setup.c | 128 - src/core/kmod-setup.h | 22 - src/core/load-dropin.c | 90 - src/core/load-dropin.h | 34 - src/core/load-fragment-gperf.gperf.m4 | 401 - src/core/load-fragment.c | 4130 - src/core/load-fragment.h | 123 - src/core/locale-setup.c | 124 - src/core/locale-setup.h | 22 - src/core/loopback-setup.c | 90 - src/core/loopback-setup.h | 22 - src/core/machine-id-setup.c | 258 - src/core/machine-id-setup.h | 23 - src/core/macros.systemd.in | 113 - src/core/main.c | 2195 - src/core/manager.c | 3141 - src/core/manager.h | 379 - src/core/mount-setup.c | 419 - src/core/mount-setup.h | 30 - src/core/mount.c | 1882 - src/core/mount.h | 108 - src/core/namespace.c | 663 - src/core/namespace.h | 63 - src/core/org.freedesktop.systemd1.conf | 232 - src/core/org.freedesktop.systemd1.policy.in.in | 70 - src/core/org.freedesktop.systemd1.service | 11 - src/core/path.c | 784 - src/core/path.h | 93 - src/core/scope.c | 621 - src/core/scope.h | 58 - src/core/selinux-access.c | 283 - src/core/selinux-access.h | 45 - src/core/selinux-setup.c | 121 - src/core/selinux-setup.h | 24 - src/core/service.c | 3391 - src/core/service.h | 222 - src/core/show-status.c | 124 - src/core/show-status.h | 39 - src/core/shutdown.c | 444 - src/core/slice.c | 346 - src/core/slice.h | 32 - src/core/smack-setup.c | 346 - src/core/smack-setup.h | 24 - src/core/socket.c | 2992 - src/core/socket.h | 185 - src/core/swap.c | 1532 - src/core/swap.h | 110 - src/core/system.conf | 61 - src/core/systemd.pc.in | 34 - src/core/target.c | 223 - src/core/target.h | 30 - src/core/timer.c | 859 - src/core/timer.h | 89 - src/core/transaction.c | 1102 - src/core/transaction.h | 51 - src/core/triggers.systemd.in | 66 - src/core/umount.c | 614 - src/core/umount.h | 28 - src/core/unit-printf.c | 304 - src/core/unit-printf.h | 26 - src/core/unit.c | 3871 - src/core/unit.h | 643 - src/core/user.conf | 44 - src/coredump/Makefile | 1 - src/coredump/coredump-vacuum.c | 268 - src/coredump/coredump-vacuum.h | 25 - src/coredump/coredump.c | 1185 - src/coredump/coredump.conf | 21 - src/coredump/coredumpctl.c | 878 - src/coredump/stacktrace.c | 200 - src/coredump/stacktrace.h | 22 - src/coredump/test-coredump-vacuum.c | 30 - src/cryptsetup/Makefile | 1 - src/cryptsetup/cryptsetup-generator.c | 509 - src/cryptsetup/cryptsetup.c | 757 - src/dbus1-generator/Makefile | 1 - src/debug-generator/Makefile | 1 - src/debug-generator/debug-generator.c | 202 - src/delta/Makefile | 1 - src/delta/delta.c | 635 - src/detect-virt/Makefile | 1 - src/detect-virt/detect-virt.c | 169 - src/escape/Makefile | 1 - src/escape/escape.c | 237 - src/firstboot/Makefile | 1 - src/firstboot/firstboot.c | 870 - src/fsck/Makefile | 1 - src/fsck/fsck.c | 487 - src/fstab-generator/Makefile | 1 - src/fstab-generator/fstab-generator.c | 723 - src/getty-generator/Makefile | 1 - src/getty-generator/getty-generator.c | 233 - src/gpt-auto-generator/Makefile | 1 - src/gpt-auto-generator/gpt-auto-generator.c | 1040 - src/grp-boot/Makefile | 30 + src/grp-boot/bootctl/Makefile | 53 + src/grp-boot/bootctl/bootctl.c | 1143 + src/grp-boot/bootctl/bootctl.completion.bash | 60 + src/grp-boot/bootctl/bootctl.completion.zsh | 30 + src/grp-boot/bootctl/bootctl.xml | 128 + src/grp-boot/kernel-install/50-depmod.install | 8 + src/grp-boot/kernel-install/90-loaderentry.install | 89 + src/grp-boot/kernel-install/Makefile | 33 + src/grp-boot/kernel-install/kernel-install | 144 + .../kernel-install/kernel-install.completion.bash | 50 + .../kernel-install/kernel-install.completion.zsh | 26 + src/grp-boot/kernel-install/kernel-install.xml | 192 + src/grp-boot/systemd-boot/.gitignore | 2 + src/grp-boot/systemd-boot/Makefile | 193 + src/grp-boot/systemd-boot/boot.c | 1857 + src/grp-boot/systemd-boot/console.c | 135 + src/grp-boot/systemd-boot/console.h | 32 + src/grp-boot/systemd-boot/disk.c | 49 + src/grp-boot/systemd-boot/disk.h | 19 + src/grp-boot/systemd-boot/graphics.c | 88 + src/grp-boot/systemd-boot/graphics.h | 22 + src/grp-boot/systemd-boot/linux.c | 128 + src/grp-boot/systemd-boot/linux.h | 22 + src/grp-boot/systemd-boot/measure.c | 312 + src/grp-boot/systemd-boot/measure.h | 21 + src/grp-boot/systemd-boot/pefile.c | 170 + src/grp-boot/systemd-boot/pefile.h | 20 + src/grp-boot/systemd-boot/splash.c | 321 + src/grp-boot/systemd-boot/splash.h | 20 + src/grp-boot/systemd-boot/stub.c | 130 + src/grp-boot/systemd-boot/test-efi-create-disk.sh | 42 + src/grp-boot/systemd-boot/util.c | 345 + src/grp-boot/systemd-boot/util.h | 48 + src/grp-coredump/Makefile | 29 + src/grp-coredump/coredumpctl/Makefile | 41 + src/grp-coredump/coredumpctl/coredumpctl.c | 878 + .../coredumpctl/coredumpctl.completion.bash | 85 + .../coredumpctl/coredumpctl.completion.zsh | 39 + src/grp-coredump/coredumpctl/coredumpctl.xml | 259 + .../systemd-coredump/50-coredump.sysctl.in | 12 + src/grp-coredump/systemd-coredump/Makefile | 85 + .../systemd-coredump/coredump-vacuum.c | 269 + .../systemd-coredump/coredump-vacuum.h | 25 + src/grp-coredump/systemd-coredump/coredump.c | 1186 + src/grp-coredump/systemd-coredump/coredump.conf | 21 + .../systemd-coredump/coredump.conf.xml | 161 + src/grp-coredump/systemd-coredump/stacktrace.c | 201 + src/grp-coredump/systemd-coredump/stacktrace.h | 22 + .../systemd-coredump/systemd-coredump.socket | 17 + .../systemd-coredump/systemd-coredump.sysusers | 8 + .../systemd-coredump/systemd-coredump.tmpfiles | 10 + .../systemd-coredump/systemd-coredump.xml | 145 + .../systemd-coredump/systemd-coredump@.service.in | 24 + .../systemd-coredump/test-coredump-vacuum.c | 30 + src/grp-hostname/Makefile | 29 + src/grp-hostname/hostnamectl/Makefile | 44 + src/grp-hostname/hostnamectl/hostnamectl.c | 529 + .../hostnamectl/hostnamectl.completion.bash | 64 + .../hostnamectl/hostnamectl.completion.zsh | 80 + src/grp-hostname/hostnamectl/hostnamectl.xml | 260 + src/grp-hostname/systemd-hostnamed/.gitignore | 1 + src/grp-hostname/systemd-hostnamed/Makefile | 64 + src/grp-hostname/systemd-hostnamed/hostnamed.c | 743 + .../org.freedesktop.hostname1.conf | 27 + .../org.freedesktop.hostname1.policy.in | 50 + .../org.freedesktop.hostname1.service | 12 + .../systemd-hostnamed/systemd-hostnamed.service.in | 24 + .../systemd-hostnamed.service.xml | 85 + src/grp-initprogs/Makefile | 44 + src/grp-initprogs/grp-sleep/Makefile | 30 + .../systemd-hibernate-resume-generator/Makefile | 38 + .../hibernate-resume-generator.c | 99 + .../systemd-hibernate-resume-generator.xml | 93 + .../grp-sleep/systemd-hibernate-resume/Makefile | 45 + .../systemd-hibernate-resume/hibernate-resume.c | 82 + .../systemd-hibernate-resume@.service.in | 20 + .../systemd-hibernate-resume@.service.xml | 81 + src/grp-initprogs/grp-sleep/systemd-sleep/Makefile | 49 + .../grp-sleep/systemd-sleep/hibernate.target | 13 + .../grp-sleep/systemd-sleep/hybrid-sleep.target | 13 + src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c | 215 + .../grp-sleep/systemd-sleep/sleep.target | 13 + .../grp-sleep/systemd-sleep/suspend.target | 13 + .../systemd-sleep/systemd-hibernate.service.in | 17 + .../systemd-sleep/systemd-hybrid-sleep.service.in | 17 + .../grp-sleep/systemd-sleep/systemd-sleep.conf.xml | 186 + .../grp-sleep/systemd-sleep/systemd-sleep.xml | 146 + .../systemd-sleep/systemd-suspend.service.in | 17 + src/grp-initprogs/systemd-backlight/Makefile | 43 + src/grp-initprogs/systemd-backlight/backlight.c | 434 + .../systemd-backlight@.service.in | 22 + .../systemd-backlight@.service.xml | 94 + src/grp-initprogs/systemd-binfmt/Makefile | 56 + src/grp-initprogs/systemd-binfmt/binfmt.c | 203 + src/grp-initprogs/systemd-binfmt/binfmt.d.xml | 101 + .../proc-sys-fs-binfmt_misc.automount | 18 + .../systemd-binfmt/proc-sys-fs-binfmt_misc.mount | 17 + .../systemd-binfmt/systemd-binfmt.service.in | 27 + .../systemd-binfmt/systemd-binfmt.service.xml | 75 + src/grp-initprogs/systemd-detect-virt/Makefile | 36 + .../systemd-detect-virt/detect-virt.c | 169 + .../systemd-detect-virt.completion.bash | 40 + .../systemd-detect-virt.completion.zsh | 11 + .../systemd-detect-virt/systemd-detect-virt.xml | 245 + src/grp-initprogs/systemd-firstboot/Makefile | 47 + src/grp-initprogs/systemd-firstboot/firstboot.c | 870 + .../systemd-firstboot/systemd-firstboot.service.in | 24 + .../systemd-firstboot/systemd-firstboot.xml | 259 + src/grp-initprogs/systemd-fsck/Makefile | 33 + src/grp-initprogs/systemd-fsck/fsck.c | 487 + .../systemd-fsck/systemd-fsck@.service.in | 20 + .../systemd-fsck/systemd-fsck@.service.xml | 139 + src/grp-initprogs/systemd-modules-load/Makefile | 59 + .../kmod-static-nodes.service.in | 18 + .../systemd-modules-load/modules-load.c | 283 + .../systemd-modules-load/modules-load.d.xml | 101 + .../systemd-modules-load.service.in | 27 + .../systemd-modules-load.service.xml | 96 + src/grp-initprogs/systemd-quotacheck/Makefile | 46 + src/grp-initprogs/systemd-quotacheck/quotacheck.c | 124 + .../systemd-quotacheck/quotaon.service.in | 19 + .../systemd-quotacheck.service.in | 20 + .../systemd-quotacheck.service.xml | 94 + src/grp-initprogs/systemd-random-seed/Makefile | 47 + .../systemd-random-seed/random-seed.c | 176 + .../systemd-random-seed.service.in | 22 + .../systemd-random-seed.service.xml | 75 + src/grp-initprogs/systemd-rfkill/Makefile | 46 + src/grp-initprogs/systemd-rfkill/rfkill.c | 427 + .../systemd-rfkill/systemd-rfkill.service.in | 21 + .../systemd-rfkill/systemd-rfkill.service.xml | 90 + .../systemd-rfkill/systemd-rfkill.socket | 19 + src/grp-initprogs/systemd-sysctl/50-default.sysctl | 40 + src/grp-initprogs/systemd-sysctl/Makefile | 33 + src/grp-initprogs/systemd-sysctl/sysctl.c | 287 + src/grp-initprogs/systemd-sysctl/sysctl.d.xml | 184 + .../systemd-sysctl/systemd-sysctl.service.in | 21 + .../systemd-sysctl/systemd-sysctl.service.xml | 152 + src/grp-initprogs/systemd-sysusers/.gitignore | 3 + src/grp-initprogs/systemd-sysusers/Makefile | 56 + .../systemd-sysusers/basic.sysusers.in | 36 + .../systemd-sysusers/systemd-sysusers.service.in | 21 + .../systemd-sysusers/systemd-sysusers.xml | 116 + src/grp-initprogs/systemd-sysusers/sysusers.c | 1919 + src/grp-initprogs/systemd-sysusers/sysusers.d.xml | 223 + src/grp-initprogs/systemd-tmpfiles/Makefile | 88 + src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 | 19 + src/grp-initprogs/systemd-tmpfiles/home.tmpfiles | 11 + src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles | 27 + .../systemd-tmpfiles/systemd-nologin.tmpfiles | 11 + .../systemd-tmpfiles-clean.service.in | 19 + .../systemd-tmpfiles/systemd-tmpfiles-clean.timer | 14 + .../systemd-tmpfiles-setup-dev.service.in | 20 + .../systemd-tmpfiles-setup.service.in | 20 + .../systemd-tmpfiles.completion.zsh | 13 + .../systemd-tmpfiles/systemd-tmpfiles.xml | 200 + src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles | 12 + src/grp-initprogs/systemd-tmpfiles/tmpfiles.c | 2343 + src/grp-initprogs/systemd-tmpfiles/tmpfiles.d.xml | 703 + src/grp-initprogs/systemd-tmpfiles/var.tmpfiles | 22 + src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles | 18 + src/grp-initprogs/systemd-update-done/Makefile | 34 + .../systemd-update-done.service.in | 21 + .../systemd-update-done.service.xml | 97 + .../systemd-update-done/update-done.c | 115 + src/grp-initprogs/systemd-update-utmp/Makefile | 41 + .../systemd-update-utmp.service.in | 21 + .../systemd-update-utmp.service.xml | 76 + .../systemd-update-utmp/update-utmp.c | 283 + src/grp-initprogs/systemd-user-sessions/Makefile | 48 + .../systemd-user-sessions.service.in | 17 + .../systemd-user-sessions.service.xml | 75 + .../systemd-user-sessions/user-sessions.c | 84 + .../systemd-vconsole-setup/.gitignore | 1 + .../systemd-vconsole-setup/90-vconsole.rules.in | 10 + src/grp-initprogs/systemd-vconsole-setup/Makefile | 50 + .../systemd-vconsole-setup.service.in | 19 + .../systemd-vconsole-setup.service.xml | 114 + .../systemd-vconsole-setup/vconsole-setup.c | 333 + .../systemd-vconsole-setup/vconsole.conf.xml | 139 + src/grp-journal/.gitignore | 1 + src/grp-journal/90-journald.preset | 10 + src/grp-journal/Makefile | 197 + src/grp-journal/README.in | 26 + src/grp-journal/catalog/.gitignore | 1 + src/grp-journal/catalog/systemd.be.catalog.in | 313 + .../catalog/systemd.be@latin.catalog.in | 318 + src/grp-journal/catalog/systemd.bg.catalog.in | 324 + src/grp-journal/catalog/systemd.catalog.in | 334 + src/grp-journal/catalog/systemd.da.catalog.in | 261 + src/grp-journal/catalog/systemd.fr.catalog.in | 320 + src/grp-journal/catalog/systemd.hr.catalog.in | 314 + src/grp-journal/catalog/systemd.hu.catalog.in | 262 + src/grp-journal/catalog/systemd.it.catalog.in | 254 + src/grp-journal/catalog/systemd.ko.catalog.in | 264 + src/grp-journal/catalog/systemd.pl.catalog.in | 315 + src/grp-journal/catalog/systemd.pt_BR.catalog.in | 264 + src/grp-journal/catalog/systemd.ru.catalog.in | 354 + src/grp-journal/catalog/systemd.sr.catalog.in | 262 + src/grp-journal/catalog/systemd.zh_CN.catalog.in | 253 + src/grp-journal/catalog/systemd.zh_TW.catalog.in | 263 + src/grp-journal/grp-remote/.gitignore | 2 + src/grp-journal/grp-remote/Makefile | 30 + src/grp-journal/grp-remote/browse.html | 544 + src/grp-journal/grp-remote/log-generator.py | 76 + src/grp-journal/grp-remote/microhttpd-util.c | 328 + src/grp-journal/grp-remote/microhttpd-util.h | 60 + .../grp-remote/systemd-journal-gatewayd/Makefile | 66 + .../systemd-journal-gatewayd/journal-gatewayd.c | 1077 + .../systemd-journal-gatewayd.service.in | 29 + .../systemd-journal-gatewayd.service.xml | 302 + .../systemd-journal-gatewayd.socket | 16 + .../systemd-journal-gatewayd.sysusers | 8 + .../grp-remote/systemd-journal-remote/Makefile | 84 + .../systemd-journal-remote/journal-remote-parse.c | 507 + .../systemd-journal-remote/journal-remote-parse.h | 69 + .../systemd-journal-remote/journal-remote-write.c | 169 + .../systemd-journal-remote/journal-remote-write.h | 70 + .../systemd-journal-remote/journal-remote.c | 1602 + .../systemd-journal-remote/journal-remote.conf.in | 6 + .../systemd-journal-remote/journal-remote.conf.xml | 121 + .../systemd-journal-remote/journal-remote.h | 53 + .../systemd-journal-remote.service.in | 25 + .../systemd-journal-remote.socket | 15 + .../systemd-journal-remote.sysusers | 8 + .../systemd-journal-remote.xml | 325 + .../grp-remote/systemd-journal-upload/Makefile | 54 + .../journal-upload-journal.c | 424 + .../systemd-journal-upload/journal-upload.c | 881 + .../systemd-journal-upload/journal-upload.conf.in | 5 + .../systemd-journal-upload/journal-upload.h | 72 + .../systemd-journal-upload.service.in | 27 + .../systemd-journal-upload.sysusers | 8 + .../systemd-journal-upload.xml | 263 + src/grp-journal/grp-remote/systemd-remote.tmpfiles | 13 + src/grp-journal/journal-nocow.tmpfiles | 27 + src/grp-journal/journalctl/Makefile | 61 + src/grp-journal/journalctl/journal-qrcode.c | 135 + src/grp-journal/journalctl/journal-qrcode.h | 27 + src/grp-journal/journalctl/journalctl.c | 2617 + .../journalctl/journalctl.completion.bash | 123 + .../journalctl/journalctl.completion.zsh | 98 + src/grp-journal/journalctl/journalctl.xml | 922 + .../systemd-journal-catalog-update.service.in | 21 + .../journalctl/systemd-journal-flush.service.in | 22 + src/grp-journal/libjournal-core/.gitignore | 4 + src/grp-journal/libjournal-core/Makefile | 56 + src/grp-journal/libjournal-core/journald-audit.c | 565 + src/grp-journal/libjournal-core/journald-audit.h | 28 + src/grp-journal/libjournal-core/journald-console.c | 116 + src/grp-journal/libjournal-core/journald-console.h | 24 + .../libjournal-core/journald-gperf.gperf | 48 + src/grp-journal/libjournal-core/journald-kmsg.c | 474 + src/grp-journal/libjournal-core/journald-kmsg.h | 29 + src/grp-journal/libjournal-core/journald-native.c | 502 + src/grp-journal/libjournal-core/journald-native.h | 35 + .../libjournal-core/journald-rate-limit.c | 272 + .../libjournal-core/journald-rate-limit.h | 28 + src/grp-journal/libjournal-core/journald-server.c | 2009 + src/grp-journal/libjournal-core/journald-server.h | 187 + src/grp-journal/libjournal-core/journald-stream.c | 786 + src/grp-journal/libjournal-core/journald-stream.h | 32 + src/grp-journal/libjournal-core/journald-syslog.c | 455 + src/grp-journal/libjournal-core/journald-syslog.h | 33 + src/grp-journal/libjournal-core/journald-wall.c | 72 + src/grp-journal/libjournal-core/journald-wall.h | 24 + src/grp-journal/libjournal-core/test-audit-type.c | 43 + src/grp-journal/libjournal-core/test-catalog.c | 264 + .../libjournal-core/test-compress-benchmark.c | 180 + src/grp-journal/libjournal-core/test-compress.c | 308 + .../libjournal-core/test-journal-enum.c | 53 + .../libjournal-core/test-journal-flush.c | 75 + .../libjournal-core/test-journal-init.c | 64 + .../libjournal-core/test-journal-interleaving.c | 307 + .../libjournal-core/test-journal-match.c | 76 + .../libjournal-core/test-journal-send.c | 102 + .../libjournal-core/test-journal-stream.c | 196 + .../libjournal-core/test-journal-syslog.c | 45 + .../libjournal-core/test-journal-verify.c | 150 + src/grp-journal/libjournal-core/test-journal.c | 178 + src/grp-journal/libjournal-core/test-mmap-cache.c | 79 + src/grp-journal/systemd-cat/Makefile | 35 + src/grp-journal/systemd-cat/cat.c | 161 + .../systemd-cat/systemd-cat.completion.bash | 57 + .../systemd-cat/systemd-cat.completion.zsh | 12 + src/grp-journal/systemd-cat/systemd-cat.xml | 178 + src/grp-journal/systemd-journald/Makefile | 85 + src/grp-journal/systemd-journald/journald.c | 120 + src/grp-journal/systemd-journald/journald.conf | 41 + src/grp-journal/systemd-journald/journald.conf.xml | 411 + .../systemd-journald/systemd-journald-audit.socket | 20 + .../systemd-journald-dev-log.socket | 32 + .../systemd-journald/systemd-journald.service.in | 34 + .../systemd-journald/systemd-journald.service.xml | 276 + .../systemd-journald/systemd-journald.socket | 26 + .../systemd-journald/systemd-journald.sysusers | 8 + .../systemd-journald/systemd-journald.tmpfiles.m4 | 55 + src/grp-locale/Makefile | 29 + src/grp-locale/localectl/Makefile | 44 + src/grp-locale/localectl/localectl.c | 683 + src/grp-locale/localectl/localectl.completion.bash | 92 + src/grp-locale/localectl/localectl.completion.zsh | 93 + src/grp-locale/localectl/localectl.xml | 230 + src/grp-locale/systemd-localed/.gitignore | 1 + src/grp-locale/systemd-localed/Makefile | 89 + src/grp-locale/systemd-localed/kbd-model-map | 68 + src/grp-locale/systemd-localed/keymap-util.c | 725 + src/grp-locale/systemd-localed/keymap-util.h | 46 + .../systemd-localed/language-fallback-map | 13 + src/grp-locale/systemd-localed/localed.c | 711 + .../systemd-localed/org.freedesktop.locale1.conf | 27 + .../org.freedesktop.locale1.policy.in | 40 + .../org.freedesktop.locale1.service | 12 + .../systemd-localed/systemd-localed.service.in | 24 + .../systemd-localed/systemd-localed.service.xml | 87 + src/grp-locale/systemd-localed/test-keymap-util.c | 221 + src/grp-login/.gitignore | 6 + src/grp-login/Makefile | 63 + src/grp-login/loginctl/Makefile | 42 + src/grp-login/loginctl/loginctl.c | 1588 + src/grp-login/loginctl/loginctl.completion.bash | 111 + src/grp-login/loginctl/loginctl.completion.zsh | 172 + src/grp-login/loginctl/loginctl.xml | 459 + src/grp-login/loginctl/sysfs-show.c | 190 + src/grp-login/loginctl/sysfs-show.h | 22 + src/grp-login/pam_systemd/Makefile | 55 + src/grp-login/pam_systemd/pam_systemd.c | 553 + src/grp-login/pam_systemd/pam_systemd.sym | 15 + src/grp-login/pam_systemd/pam_systemd.xml | 296 + src/grp-login/systemd-inhibit/Makefile | 37 + src/grp-login/systemd-inhibit/inhibit.c | 292 + .../systemd-inhibit/systemd-inhibit.completion.zsh | 33 + src/grp-login/systemd-inhibit/systemd-inhibit.xml | 177 + src/grp-login/systemd-logind/70-power-switch.rules | 18 + src/grp-login/systemd-logind/70-uaccess.rules | 81 + src/grp-login/systemd-logind/71-seat.rules.in | 54 + src/grp-login/systemd-logind/73-seat-late.rules.in | 17 + src/grp-login/systemd-logind/Makefile | 132 + src/grp-login/systemd-logind/logind-acl.c | 293 + src/grp-login/systemd-logind/logind-acl.h | 56 + src/grp-login/systemd-logind/logind-action.c | 179 + src/grp-login/systemd-logind/logind-action.h | 49 + src/grp-login/systemd-logind/logind-button.c | 290 + src/grp-login/systemd-logind/logind-button.h | 44 + src/grp-login/systemd-logind/logind-core.c | 560 + src/grp-login/systemd-logind/logind-dbus.c | 3170 + src/grp-login/systemd-logind/logind-device.c | 125 + src/grp-login/systemd-logind/logind-device.h | 44 + src/grp-login/systemd-logind/logind-gperf.gperf | 41 + src/grp-login/systemd-logind/logind-inhibit.c | 486 + src/grp-login/systemd-logind/logind-inhibit.h | 89 + src/grp-login/systemd-logind/logind-seat-dbus.c | 475 + src/grp-login/systemd-logind/logind-seat.c | 696 + src/grp-login/systemd-logind/logind-seat.h | 96 + src/grp-login/systemd-logind/logind-session-dbus.c | 799 + .../systemd-logind/logind-session-device.c | 482 + .../systemd-logind/logind-session-device.h | 54 + src/grp-login/systemd-logind/logind-session.c | 1275 + src/grp-login/systemd-logind/logind-session.h | 186 + src/grp-login/systemd-logind/logind-user-dbus.c | 399 + src/grp-login/systemd-logind/logind-user.c | 917 + src/grp-login/systemd-logind/logind-user.h | 94 + src/grp-login/systemd-logind/logind-utmp.c | 184 + src/grp-login/systemd-logind/logind.c | 1209 + src/grp-login/systemd-logind/logind.conf.in | 37 + src/grp-login/systemd-logind/logind.conf.xml | 348 + src/grp-login/systemd-logind/logind.h | 199 + .../systemd-logind/org.freedesktop.login1.conf | 274 + .../org.freedesktop.login1.policy.in | 325 + .../systemd-logind/org.freedesktop.login1.service | 12 + .../systemd-logind/systemd-logind.service.in | 33 + .../systemd-logind/systemd-logind.service.xml | 121 + src/grp-login/systemd-logind/systemd-user.pam.m4 | 12 + src/grp-login/systemd-logind/user.slice | 11 + src/grp-login/test-inhibit.c | 112 + src/grp-login/test-login-shared.c | 39 + src/grp-login/test-login-tables.c | 34 + src/grp-machine/Makefile | 31 + src/grp-machine/grp-import/Makefile | 32 + src/grp-machine/grp-import/libimport/Makefile | 41 + .../grp-import/libimport/import-common.c | 222 + .../grp-import/libimport/import-common.h | 26 + .../grp-import/libimport/import-compress.c | 470 + .../grp-import/libimport/import-compress.h | 61 + src/grp-machine/grp-import/libimport/qcow2-util.c | 353 + src/grp-machine/grp-import/libimport/qcow2-util.h | 23 + src/grp-machine/grp-import/libimport/test-qcow2.c | 54 + src/grp-machine/grp-import/systemd-export/Makefile | 50 + .../grp-import/systemd-export/export-raw.c | 353 + .../grp-import/systemd-export/export-raw.h | 35 + .../grp-import/systemd-export/export-tar.c | 329 + .../grp-import/systemd-export/export-tar.h | 35 + src/grp-machine/grp-import/systemd-export/export.c | 321 + src/grp-machine/grp-import/systemd-import/Makefile | 51 + .../grp-import/systemd-import/import-pubring.gpg | Bin 0 -> 9551 bytes .../grp-import/systemd-import/import-raw.c | 468 + .../grp-import/systemd-import/import-raw.h | 35 + .../grp-import/systemd-import/import-tar.c | 389 + .../grp-import/systemd-import/import-tar.h | 35 + src/grp-machine/grp-import/systemd-import/import.c | 338 + .../grp-import/systemd-importd/.gitignore | 1 + .../grp-import/systemd-importd/Makefile | 68 + .../grp-import/systemd-importd/importd.c | 1219 + .../systemd-importd/org.freedesktop.import1.conf | 62 + .../org.freedesktop.import1.policy.in | 49 + .../org.freedesktop.import1.service | 12 + .../systemd-importd/systemd-importd.service.in | 21 + .../systemd-importd/systemd-importd.service.xml | 82 + src/grp-machine/grp-import/systemd-pull/Makefile | 63 + .../grp-import/systemd-pull/curl-util.c | 449 + .../grp-import/systemd-pull/curl-util.h | 56 + .../grp-import/systemd-pull/pull-common.c | 548 + .../grp-import/systemd-pull/pull-common.h | 37 + src/grp-machine/grp-import/systemd-pull/pull-job.c | 619 + src/grp-machine/grp-import/systemd-pull/pull-job.h | 106 + src/grp-machine/grp-import/systemd-pull/pull-raw.c | 653 + src/grp-machine/grp-import/systemd-pull/pull-raw.h | 35 + src/grp-machine/grp-import/systemd-pull/pull-tar.c | 564 + src/grp-machine/grp-import/systemd-pull/pull-tar.h | 35 + src/grp-machine/grp-import/systemd-pull/pull.c | 338 + src/grp-machine/machinectl/Makefile | 42 + src/grp-machine/machinectl/machinectl.c | 2875 + .../machinectl/machinectl.completion.bash | 99 + .../machinectl/machinectl.completion.zsh | 100 + src/grp-machine/machinectl/machinectl.xml | 1021 + src/grp-machine/nss-mymachines/Makefile | 45 + src/grp-machine/nss-mymachines/nss-mymachines.c | 738 + src/grp-machine/nss-mymachines/nss-mymachines.sym | 21 + src/grp-machine/nss-mymachines/nss-mymachines.xml | 113 + src/grp-machine/systemd-machined/.gitignore | 1 + src/grp-machine/systemd-machined/Makefile | 101 + src/grp-machine/systemd-machined/image-dbus.c | 423 + src/grp-machine/systemd-machined/image-dbus.h | 35 + src/grp-machine/systemd-machined/machine-dbus.c | 1473 + src/grp-machine/systemd-machined/machine-dbus.h | 44 + src/grp-machine/systemd-machined/machine.c | 631 + src/grp-machine/systemd-machined/machine.h | 111 + src/grp-machine/systemd-machined/machine.slice | 11 + src/grp-machine/systemd-machined/machined-dbus.c | 1805 + src/grp-machine/systemd-machined/machined.c | 416 + src/grp-machine/systemd-machined/machined.h | 82 + src/grp-machine/systemd-machined/operation.c | 153 + src/grp-machine/systemd-machined/operation.h | 49 + .../systemd-machined/org.freedesktop.machine1.conf | 198 + .../org.freedesktop.machine1.policy.in | 102 + .../org.freedesktop.machine1.service | 12 + .../systemd-machined/systemd-machined.service.in | 25 + .../systemd-machined/systemd-machined.service.xml | 90 + .../systemd-machined/test-machine-tables.c | 30 + src/grp-network/90-networkd.preset | 10 + src/grp-network/Makefile | 85 + src/grp-network/libnetworkd-core/.gitignore | 3 + src/grp-network/libnetworkd-core/Makefile | 97 + .../libnetworkd-core/networkd-address-pool.c | 172 + .../libnetworkd-core/networkd-address-pool.h | 43 + .../libnetworkd-core/networkd-address.c | 864 + .../libnetworkd-core/networkd-address.h | 79 + src/grp-network/libnetworkd-core/networkd-brvlan.c | 331 + src/grp-network/libnetworkd-core/networkd-brvlan.h | 29 + src/grp-network/libnetworkd-core/networkd-conf.c | 112 + src/grp-network/libnetworkd-core/networkd-conf.h | 49 + src/grp-network/libnetworkd-core/networkd-dhcp4.c | 659 + src/grp-network/libnetworkd-core/networkd-dhcp6.c | 266 + src/grp-network/libnetworkd-core/networkd-fdb.c | 254 + src/grp-network/libnetworkd-core/networkd-fdb.h | 47 + .../libnetworkd-core/networkd-gperf.gperf | 20 + src/grp-network/libnetworkd-core/networkd-ipv4ll.c | 243 + .../libnetworkd-core/networkd-link-bus.c | 141 + src/grp-network/libnetworkd-core/networkd-link.c | 3412 + src/grp-network/libnetworkd-core/networkd-link.h | 213 + .../libnetworkd-core/networkd-lldp-tx.c | 417 + .../libnetworkd-core/networkd-lldp-tx.h | 35 + .../libnetworkd-core/networkd-manager-bus.c | 50 + .../libnetworkd-core/networkd-manager.c | 1331 + src/grp-network/libnetworkd-core/networkd-ndisc.c | 664 + src/grp-network/libnetworkd-core/networkd-ndisc.h | 39 + .../libnetworkd-core/networkd-netdev-bond.c | 445 + .../libnetworkd-core/networkd-netdev-bond.h | 172 + .../libnetworkd-core/networkd-netdev-bridge.c | 147 + .../libnetworkd-core/networkd-netdev-bridge.h | 37 + .../libnetworkd-core/networkd-netdev-dummy.c | 28 + .../libnetworkd-core/networkd-netdev-dummy.h | 29 + .../libnetworkd-core/networkd-netdev-gperf.gperf | 111 + .../libnetworkd-core/networkd-netdev-ipvlan.c | 74 + .../libnetworkd-core/networkd-netdev-ipvlan.h | 45 + .../libnetworkd-core/networkd-netdev-macvlan.c | 90 + .../libnetworkd-core/networkd-netdev-macvlan.h | 49 + .../libnetworkd-core/networkd-netdev-tunnel.c | 726 + .../libnetworkd-core/networkd-netdev-tunnel.h | 119 + .../libnetworkd-core/networkd-netdev-tuntap.c | 185 + .../libnetworkd-core/networkd-netdev-tuntap.h | 40 + .../libnetworkd-core/networkd-netdev-veth.c | 112 + .../libnetworkd-core/networkd-netdev-veth.h | 34 + .../libnetworkd-core/networkd-netdev-vlan.c | 79 + .../libnetworkd-core/networkd-netdev-vlan.h | 33 + .../libnetworkd-core/networkd-netdev-vrf.c | 51 + .../libnetworkd-core/networkd-netdev-vrf.h | 33 + .../libnetworkd-core/networkd-netdev-vxlan.c | 295 + .../libnetworkd-core/networkd-netdev-vxlan.h | 92 + src/grp-network/libnetworkd-core/networkd-netdev.c | 717 + src/grp-network/libnetworkd-core/networkd-netdev.h | 200 + .../libnetworkd-core/networkd-network-bus.c | 154 + .../libnetworkd-core/networkd-network-gperf.gperf | 129 + .../libnetworkd-core/networkd-network.c | 1052 + .../libnetworkd-core/networkd-network.h | 247 + src/grp-network/libnetworkd-core/networkd-route.c | 902 + src/grp-network/libnetworkd-core/networkd-route.h | 75 + src/grp-network/libnetworkd-core/networkd-util.c | 102 + src/grp-network/libnetworkd-core/networkd-util.h | 38 + src/grp-network/libnetworkd-core/networkd.h | 113 + src/grp-network/network/80-container-host0.network | 23 + src/grp-network/network/80-container-ve.network | 23 + src/grp-network/network/80-container-vz.network | 22 + src/grp-network/network/99-default.link | 3 + src/grp-network/networkctl/Makefile | 39 + src/grp-network/networkctl/networkctl.c | 1141 + .../networkctl/networkctl.completion.bash | 70 + .../networkctl/networkctl.completion.zsh | 35 + src/grp-network/networkctl/networkctl.xml | 193 + .../systemd-networkd-wait-online/Makefile | 43 + .../networkd-wait-online-link.c | 131 + .../networkd-wait-online-link.h | 44 + .../networkd-wait-online-manager.c | 331 + .../networkd-wait-online.c | 167 + .../networkd-wait-online.h | 54 + .../systemd-networkd-wait-online.service.in | 23 + .../systemd-networkd-wait-online.service.xml | 110 + src/grp-network/systemd-networkd/Makefile | 67 + src/grp-network/systemd-networkd/networkd.c | 139 + src/grp-network/systemd-networkd/networkd.conf.xml | 154 + .../systemd-networkd/org.freedesktop.network1.conf | 42 + .../org.freedesktop.network1.service | 12 + .../systemd-networkd.service.m4.in | 39 + .../systemd-networkd/systemd-networkd.service.xml | 103 + .../systemd-networkd/systemd-networkd.socket | 21 + .../systemd-networkd/systemd-networkd.sysusers | 8 + .../systemd-networkd/systemd-networkd.tmpfiles | 12 + src/grp-network/test-network-tables.c | 26 + src/grp-network/test-network.c | 216 + src/grp-network/test-networkd-conf.c | 141 + src/grp-resolve/90-resolved.preset | 11 + src/grp-resolve/Makefile | 31 + src/grp-resolve/libbasic-dns/Makefile | 109 + src/grp-resolve/libbasic-dns/dns-type.c | 333 + src/grp-resolve/libbasic-dns/dns-type.h | 162 + src/grp-resolve/libbasic-dns/resolved-def.h | 38 + src/grp-resolve/libbasic-dns/resolved-dns-answer.c | 859 + src/grp-resolve/libbasic-dns/resolved-dns-answer.h | 148 + src/grp-resolve/libbasic-dns/resolved-dns-dnssec.c | 2200 + src/grp-resolve/libbasic-dns/resolved-dns-dnssec.h | 103 + src/grp-resolve/libbasic-dns/resolved-dns-packet.c | 2301 + src/grp-resolve/libbasic-dns/resolved-dns-packet.h | 302 + .../libbasic-dns/resolved-dns-question.c | 469 + .../libbasic-dns/resolved-dns-question.h | 74 + src/grp-resolve/libbasic-dns/resolved-dns-rr.c | 1840 + src/grp-resolve/libbasic-dns/resolved-dns-rr.h | 354 + .../test-data/_443._tcp.fedoraproject.org.pkts | Bin 0 -> 169 bytes .../test-data/_openpgpkey.fedoraproject.org.pkts | Bin 0 -> 986 bytes .../libbasic-dns/test-data/fake-caa.pkts | Bin 0 -> 196 bytes .../libbasic-dns/test-data/fedoraproject.org.pkts | Bin 0 -> 1483 bytes .../libbasic-dns/test-data/gandi.net.pkts | Bin 0 -> 1010 bytes .../libbasic-dns/test-data/google.com.pkts | Bin 0 -> 747 bytes .../libbasic-dns/test-data/kyhwana.org.pkts | Bin 0 -> 1803 bytes src/grp-resolve/libbasic-dns/test-data/root.pkts | Bin 0 -> 1061 bytes ...sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts | Bin 0 -> 330 bytes .../libbasic-dns/test-data/teamits.com.pkts | Bin 0 -> 1021 bytes .../test-data/zbyszek@fedoraproject.org.pkts | Bin 0 -> 2533 bytes src/grp-resolve/libbasic-dns/test-dns-packet.c | 133 + src/grp-resolve/libbasic-dns/test-dnssec-complex.c | 237 + src/grp-resolve/libbasic-dns/test-dnssec.c | 344 + src/grp-resolve/libbasic-dns/test-resolve-tables.c | 65 + src/grp-resolve/nss-resolve/Makefile | 46 + src/grp-resolve/nss-resolve/nss-resolve.c | 675 + src/grp-resolve/nss-resolve/nss-resolve.sym | 19 + src/grp-resolve/nss-resolve/nss-resolve.xml | 111 + src/grp-resolve/systemd-resolve/Makefile | 53 + src/grp-resolve/systemd-resolve/resolve-tool.c | 2007 + .../systemd-resolve.completion.bash | 64 + .../systemd-resolve/systemd-resolve.completion.zsh | 64 + .../systemd-resolve/systemd-resolve.xml | 394 + src/grp-resolve/systemd-resolved/.gitignore | 6 + src/grp-resolve/systemd-resolved/Makefile | 141 + src/grp-resolve/systemd-resolved/RFCs | 59 + .../systemd-resolved/dnssec-trust-anchors.d.xml | 200 + .../systemd-resolved/org.freedesktop.resolve1.conf | 27 + .../org.freedesktop.resolve1.service | 12 + src/grp-resolve/systemd-resolved/resolv.conf | 11 + src/grp-resolve/systemd-resolved/resolved-bus.c | 1690 + src/grp-resolve/systemd-resolved/resolved-bus.h | 25 + src/grp-resolve/systemd-resolved/resolved-conf.c | 241 + src/grp-resolve/systemd-resolved/resolved-conf.h | 36 + .../systemd-resolved/resolved-dns-cache.c | 1065 + .../systemd-resolved/resolved-dns-cache.h | 52 + .../systemd-resolved/resolved-dns-query.c | 1122 + .../systemd-resolved/resolved-dns-query.h | 141 + .../systemd-resolved/resolved-dns-scope.c | 1038 + .../systemd-resolved/resolved-dns-scope.h | 111 + .../systemd-resolved/resolved-dns-search-domain.c | 228 + .../systemd-resolved/resolved-dns-search-domain.h | 74 + .../systemd-resolved/resolved-dns-server.c | 778 + .../systemd-resolved/resolved-dns-server.h | 147 + .../systemd-resolved/resolved-dns-stream.c | 421 + .../systemd-resolved/resolved-dns-stream.h | 80 + .../systemd-resolved/resolved-dns-stub.c | 573 + .../systemd-resolved/resolved-dns-stub.h | 31 + .../systemd-resolved/resolved-dns-synthesize.c | 414 + .../systemd-resolved/resolved-dns-synthesize.h | 31 + .../systemd-resolved/resolved-dns-transaction.c | 3108 + .../systemd-resolved/resolved-dns-transaction.h | 180 + .../systemd-resolved/resolved-dns-trust-anchor.c | 744 + .../systemd-resolved/resolved-dns-trust-anchor.h | 43 + .../systemd-resolved/resolved-dns-zone.c | 665 + .../systemd-resolved/resolved-dns-zone.h | 82 + .../systemd-resolved/resolved-etc-hosts.c | 449 + .../systemd-resolved/resolved-etc-hosts.h | 29 + .../systemd-resolved/resolved-gperf.gperf | 24 + .../systemd-resolved/resolved-link-bus.c | 630 + .../systemd-resolved/resolved-link-bus.h | 38 + src/grp-resolve/systemd-resolved/resolved-link.c | 1115 + src/grp-resolve/systemd-resolved/resolved-link.h | 118 + src/grp-resolve/systemd-resolved/resolved-llmnr.c | 472 + src/grp-resolve/systemd-resolved/resolved-llmnr.h | 32 + .../systemd-resolved/resolved-manager.c | 1379 + .../systemd-resolved/resolved-manager.h | 183 + src/grp-resolve/systemd-resolved/resolved-mdns.c | 288 + src/grp-resolve/systemd-resolved/resolved-mdns.h | 30 + .../systemd-resolved/resolved-resolv-conf.c | 267 + .../systemd-resolved/resolved-resolv-conf.h | 27 + src/grp-resolve/systemd-resolved/resolved.c | 121 + src/grp-resolve/systemd-resolved/resolved.conf.in | 20 + src/grp-resolve/systemd-resolved/resolved.conf.xml | 230 + .../systemd-resolved.service.m4.in | 34 + .../systemd-resolved/systemd-resolved.service.xml | 234 + .../systemd-resolved/systemd-resolved.sysusers | 8 + .../systemd-resolved/systemd-resolved.tmpfiles | 10 + src/grp-system/90-systemd.preset | 24 + src/grp-system/Makefile | 32 + src/grp-system/bootup.xml | 305 + src/grp-system/grp-utils/Makefile | 32 + .../grp-utils/systemd-analyze/.gitignore | 1 + src/grp-system/grp-utils/systemd-analyze/Makefile | 39 + .../grp-utils/systemd-analyze/analyze-verify.c | 305 + .../grp-utils/systemd-analyze/analyze-verify.h | 26 + src/grp-system/grp-utils/systemd-analyze/analyze.c | 1486 + .../systemd-analyze.completion.bash | 117 + .../systemd-analyze/systemd-analyze.completion.zsh | 58 + .../grp-utils/systemd-analyze/systemd-analyze.xml | 388 + src/grp-system/grp-utils/systemd-delta/Makefile | 33 + src/grp-system/grp-utils/systemd-delta/delta.c | 635 + .../systemd-delta/systemd-delta.completion.bash | 61 + .../systemd-delta/systemd-delta.completion.zsh | 15 + .../grp-utils/systemd-delta/systemd-delta.xml | 205 + .../grp-utils/systemd-fstab-generator/Makefile | 34 + .../systemd-fstab-generator/fstab-generator.c | 724 + .../systemd-fstab-generator/mount-setup.c | 1 + .../systemd-fstab-generator/mount-setup.h | 1 + .../systemd-fstab-generator.xml | 183 + src/grp-system/grp-utils/systemd-run/Makefile | 33 + src/grp-system/grp-utils/systemd-run/run.c | 1261 + .../systemd-run/systemd-run.completion.bash | 117 + .../systemd-run/systemd-run.completion.zsh | 60 + .../grp-utils/systemd-run/systemd-run.xml | 459 + .../grp-utils/systemd-sysv-generator/.gitignore | 1 + .../grp-utils/systemd-sysv-generator/Makefile | 46 + .../grp-utils/systemd-sysv-generator/README.in | 27 + .../systemd-sysv-generator.xml | 97 + .../systemd-sysv-generator/sysv-generator.c | 982 + src/grp-system/kernel-command-line.xml | 384 + src/grp-system/libcore/.gitignore | 3 + src/grp-system/libcore/Makefile | 168 + src/grp-system/libcore/audit-fd.c | 73 + src/grp-system/libcore/audit-fd.h | 23 + src/grp-system/libcore/automount.c | 1124 + src/grp-system/libcore/automount.h | 59 + src/grp-system/libcore/bus-policy.c | 181 + src/grp-system/libcore/bus-policy.h | 64 + src/grp-system/libcore/busname.c | 1078 + src/grp-system/libcore/busname.h | 69 + src/grp-system/libcore/cgroup.c | 1992 + src/grp-system/libcore/cgroup.h | 183 + src/grp-system/libcore/dbus-automount.c | 35 + src/grp-system/libcore/dbus-automount.h | 23 + src/grp-system/libcore/dbus-busname.c | 38 + src/grp-system/libcore/dbus-busname.h | 23 + src/grp-system/libcore/dbus-cgroup.c | 1110 + src/grp-system/libcore/dbus-cgroup.h | 28 + src/grp-system/libcore/dbus-device.c | 28 + src/grp-system/libcore/dbus-device.h | 24 + src/grp-system/libcore/dbus-execute.c | 1555 + src/grp-system/libcore/dbus-execute.h | 45 + src/grp-system/libcore/dbus-job.c | 194 + src/grp-system/libcore/dbus-job.h | 31 + src/grp-system/libcore/dbus-kill.c | 123 + src/grp-system/libcore/dbus-kill.h | 29 + src/grp-system/libcore/dbus-manager.c | 2307 + src/grp-system/libcore/dbus-manager.h | 28 + src/grp-system/libcore/dbus-mount.c | 212 + src/grp-system/libcore/dbus-mount.h | 29 + src/grp-system/libcore/dbus-path.c | 87 + src/grp-system/libcore/dbus-path.h | 24 + src/grp-system/libcore/dbus-scope.c | 230 + src/grp-system/libcore/dbus-scope.h | 31 + src/grp-system/libcore/dbus-service.c | 323 + src/grp-system/libcore/dbus-service.h | 29 + src/grp-system/libcore/dbus-slice.c | 52 + src/grp-system/libcore/dbus-slice.h | 29 + src/grp-system/libcore/dbus-socket.c | 185 + src/grp-system/libcore/dbus-socket.h | 29 + src/grp-system/libcore/dbus-swap.c | 116 + src/grp-system/libcore/dbus-swap.h | 30 + src/grp-system/libcore/dbus-target.c | 26 + src/grp-system/libcore/dbus-target.h | 24 + src/grp-system/libcore/dbus-timer.c | 353 + src/grp-system/libcore/dbus-timer.h | 28 + src/grp-system/libcore/dbus-unit.c | 1424 + src/grp-system/libcore/dbus-unit.h | 41 + src/grp-system/libcore/dbus.c | 1244 + src/grp-system/libcore/dbus.h | 44 + src/grp-system/libcore/device.c | 877 + src/grp-system/libcore/device.h | 47 + src/grp-system/libcore/execute.c | 3286 + src/grp-system/libcore/execute.h | 292 + src/grp-system/libcore/failure-action.c | 129 + src/grp-system/libcore/failure-action.h | 42 + src/grp-system/libcore/hostname-setup.c | 69 + src/grp-system/libcore/hostname-setup.h | 22 + src/grp-system/libcore/ima-setup.c | 81 + src/grp-system/libcore/ima-setup.h | 24 + src/grp-system/libcore/job.c | 1260 + src/grp-system/libcore/job.h | 242 + src/grp-system/libcore/kill.c | 69 + src/grp-system/libcore/kill.h | 65 + src/grp-system/libcore/killall.c | 249 + src/grp-system/libcore/killall.h | 22 + src/grp-system/libcore/kmod-setup.c | 129 + src/grp-system/libcore/kmod-setup.h | 22 + src/grp-system/libcore/linux/auto_dev-ioctl.h | 228 + src/grp-system/libcore/load-dropin.c | 91 + src/grp-system/libcore/load-dropin.h | 35 + .../libcore/load-fragment-gperf.gperf.m4 | 401 + src/grp-system/libcore/load-fragment.c | 4133 + src/grp-system/libcore/load-fragment.h | 123 + src/grp-system/libcore/locale-setup.c | 125 + src/grp-system/libcore/locale-setup.h | 22 + src/grp-system/libcore/loopback-setup.c | 90 + src/grp-system/libcore/loopback-setup.h | 22 + src/grp-system/libcore/machine-id-setup.c | 259 + src/grp-system/libcore/machine-id-setup.h | 23 + src/grp-system/libcore/manager.c | 3143 + src/grp-system/libcore/manager.h | 380 + src/grp-system/libcore/mount-setup.c | 420 + src/grp-system/libcore/mount-setup.h | 30 + src/grp-system/libcore/mount.c | 1883 + src/grp-system/libcore/mount.h | 108 + src/grp-system/libcore/namespace.c | 665 + src/grp-system/libcore/namespace.h | 63 + src/grp-system/libcore/path.c | 785 + src/grp-system/libcore/path.h | 93 + src/grp-system/libcore/scope.c | 622 + src/grp-system/libcore/scope.h | 58 + src/grp-system/libcore/selinux-access.c | 284 + src/grp-system/libcore/selinux-access.h | 46 + src/grp-system/libcore/selinux-setup.c | 122 + src/grp-system/libcore/selinux-setup.h | 24 + src/grp-system/libcore/service.c | 3392 + src/grp-system/libcore/service.h | 223 + src/grp-system/libcore/show-status.c | 125 + src/grp-system/libcore/show-status.h | 39 + src/grp-system/libcore/slice.c | 347 + src/grp-system/libcore/slice.h | 32 + src/grp-system/libcore/smack-setup.c | 347 + src/grp-system/libcore/smack-setup.h | 24 + src/grp-system/libcore/socket.c | 2994 + src/grp-system/libcore/socket.h | 186 + src/grp-system/libcore/swap.c | 1533 + src/grp-system/libcore/swap.h | 110 + src/grp-system/libcore/target.c | 224 + src/grp-system/libcore/target.h | 30 + src/grp-system/libcore/timer.c | 860 + src/grp-system/libcore/timer.h | 89 + src/grp-system/libcore/transaction.c | 1103 + src/grp-system/libcore/transaction.h | 52 + src/grp-system/libcore/unit-printf.c | 305 + src/grp-system/libcore/unit-printf.h | 26 + src/grp-system/libcore/unit.c | 3872 + src/grp-system/libcore/unit.h | 644 + src/grp-system/systemctl/.gitignore | 2 + src/grp-system/systemctl/Makefile | 33 + src/grp-system/systemctl/halt.xml | 176 + src/grp-system/systemctl/runlevel.xml | 192 + src/grp-system/systemctl/shutdown.xml | 175 + src/grp-system/systemctl/systemctl.c | 8076 ++ .../systemctl/systemctl.completion.bash.in | 284 + .../systemctl/systemctl.completion.zsh.in | 391 + src/grp-system/systemctl/systemctl.xml | 1826 + .../systemctl/systemd-sysv-install.SKELETON | 47 + src/grp-system/systemctl/systemd.preset.xml | 189 + src/grp-system/systemctl/telinit.xml | 179 + src/grp-system/systemd-shutdown/Makefile | 39 + src/grp-system/systemd-shutdown/halt.target | 17 + src/grp-system/systemd-shutdown/kexec.target | 17 + src/grp-system/systemd-shutdown/killall.c | 1 + src/grp-system/systemd-shutdown/killall.h | 1 + src/grp-system/systemd-shutdown/mount-setup.c | 1 + src/grp-system/systemd-shutdown/mount-setup.h | 1 + src/grp-system/systemd-shutdown/poweroff.target | 19 + src/grp-system/systemd-shutdown/reboot.target | 19 + src/grp-system/systemd-shutdown/shutdown.c | 446 + .../systemd-shutdown/systemd-halt.service.in | 17 + .../systemd-shutdown/systemd-kexec.service.in | 17 + .../systemd-shutdown/systemd-poweroff.service.in | 17 + .../systemd-shutdown/systemd-reboot.service.in | 17 + .../systemd-shutdown/systemd-shutdown.xml | 119 + src/grp-system/systemd-shutdown/umount.c | 616 + src/grp-system/systemd-shutdown/umount.h | 28 + src/grp-system/systemd/50-systemd-user.xorg | 7 + src/grp-system/systemd/Makefile | 73 + src/grp-system/systemd/macros.systemd.in | 113 + src/grp-system/systemd/main.c | 2195 + .../systemd/org.freedesktop.systemd1.conf | 232 + .../systemd/org.freedesktop.systemd1.policy.in.in | 70 + .../systemd/org.freedesktop.systemd1.service | 11 + src/grp-system/systemd/system.conf | 61 + src/grp-system/systemd/systemd-system.conf.xml | 393 + src/grp-system/systemd/systemd-tmpfs.tmpfiles | 14 + src/grp-system/systemd/systemd.automount.xml | 173 + src/grp-system/systemd/systemd.device.xml | 182 + src/grp-system/systemd/systemd.exec.xml | 1612 + src/grp-system/systemd/systemd.generator.xml | 348 + src/grp-system/systemd/systemd.journal-fields.xml | 525 + src/grp-system/systemd/systemd.kill.xml | 189 + src/grp-system/systemd/systemd.link.xml | 477 + src/grp-system/systemd/systemd.mount.xml | 406 + src/grp-system/systemd/systemd.netdev.xml | 1164 + src/grp-system/systemd/systemd.network.xml | 1312 + src/grp-system/systemd/systemd.nspawn.xml | 463 + src/grp-system/systemd/systemd.offline-updates.xml | 169 + src/grp-system/systemd/systemd.path.xml | 202 + src/grp-system/systemd/systemd.pc.in | 34 + .../systemd/systemd.resource-control.xml | 696 + src/grp-system/systemd/systemd.scope.xml | 108 + src/grp-system/systemd/systemd.service.xml | 1350 + src/grp-system/systemd/systemd.slice.xml | 132 + src/grp-system/systemd/systemd.socket.xml | 860 + src/grp-system/systemd/systemd.special.xml | 941 + src/grp-system/systemd/systemd.swap.xml | 250 + src/grp-system/systemd/systemd.target.xml | 112 + src/grp-system/systemd/systemd.time.xml | 316 + src/grp-system/systemd/systemd.timer.xml | 313 + src/grp-system/systemd/systemd.tmpfiles | 20 + src/grp-system/systemd/systemd.unit.xml | 1484 + src/grp-system/systemd/systemd.xml | 1155 + src/grp-system/systemd/triggers.systemd.in | 66 + src/grp-system/systemd/user.conf | 44 + src/grp-timedate/Makefile | 29 + src/grp-timedate/systemd-timedated/.gitignore | 1 + src/grp-timedate/systemd-timedated/Makefile | 65 + .../org.freedesktop.timedate1.conf | 27 + .../org.freedesktop.timedate1.policy.in | 62 + .../org.freedesktop.timedate1.service | 12 + .../systemd-timedated/systemd-timedated.service.in | 22 + .../systemd-timedated.service.xml | 85 + src/grp-timedate/systemd-timedated/timedated.c | 747 + src/grp-timedate/timedatectl/Makefile | 43 + src/grp-timedate/timedatectl/timedatectl.c | 507 + .../timedatectl/timedatectl.completion.bash | 76 + .../timedatectl/timedatectl.completion.zsh | 66 + src/grp-timedate/timedatectl/timedatectl.xml | 253 + src/grp-udev/.gitignore | 4 + src/grp-udev/Makefile | 90 + src/grp-udev/ata_id/Makefile | 35 + src/grp-udev/ata_id/ata_id.c | 675 + src/grp-udev/cdrom_id/60-cdrom_id.rules | 25 + src/grp-udev/cdrom_id/Makefile | 38 + src/grp-udev/cdrom_id/cdrom_id.c | 1086 + src/grp-udev/collect/Makefile | 35 + src/grp-udev/collect/collect.c | 491 + src/grp-udev/hwdb/.gitignore | 8 + src/grp-udev/hwdb/20-OUI.hwdb | 74853 +++++++++++++++++ src/grp-udev/hwdb/20-acpi-vendor.hwdb | 7444 ++ src/grp-udev/hwdb/20-acpi-vendor.hwdb.patch | 492 + src/grp-udev/hwdb/20-bluetooth-vendor-product.hwdb | 2830 + src/grp-udev/hwdb/20-net-ifname.hwdb | 5 + src/grp-udev/hwdb/20-pci-classes.hwdb | 564 + src/grp-udev/hwdb/20-pci-vendor-model.hwdb | 83262 +++++++++++++++++++ src/grp-udev/hwdb/20-sdio-classes.hwdb | 33 + src/grp-udev/hwdb/20-sdio-vendor-model.hwdb | 207 + src/grp-udev/hwdb/20-usb-classes.hwdb | 342 + src/grp-udev/hwdb/20-usb-vendor-model.hwdb | 55296 ++++++++++++ src/grp-udev/hwdb/60-evdev.hwdb | 238 + src/grp-udev/hwdb/60-keyboard.hwdb | 1234 + src/grp-udev/hwdb/70-mouse.hwdb | 485 + src/grp-udev/hwdb/70-pointingstick.hwdb | 119 + src/grp-udev/hwdb/70-touchpad.hwdb | 49 + src/grp-udev/hwdb/Makefile | 40 + src/grp-udev/hwdb/acpi-update.py | 79 + src/grp-udev/hwdb/ids-update.pl | 375 + src/grp-udev/hwdb/sdio.ids | 94 + src/grp-udev/libudev-core/Makefile | 99 + src/grp-udev/libudev-core/logind-acl.c | 1 + src/grp-udev/libudev-core/logind-acl.h | 1 + src/grp-udev/libudev-core/net/.gitignore | 1 + src/grp-udev/libudev-core/net/Makefile | 29 + src/grp-udev/libudev-core/net/ethtool-util.c | 210 + src/grp-udev/libudev-core/net/ethtool-util.h | 54 + .../libudev-core/net/link-config-gperf.gperf | 39 + src/grp-udev/libudev-core/net/link-config.c | 519 + src/grp-udev/libudev-core/net/link-config.h | 99 + src/grp-udev/libudev-core/sd-login.c | 1 + src/grp-udev/libudev-core/udev-builtin-blkid.c | 337 + src/grp-udev/libudev-core/udev-builtin-btrfs.c | 58 + src/grp-udev/libudev-core/udev-builtin-hwdb.c | 222 + src/grp-udev/libudev-core/udev-builtin-input_id.c | 341 + src/grp-udev/libudev-core/udev-builtin-keyboard.c | 278 + src/grp-udev/libudev-core/udev-builtin-kmod.c | 123 + src/grp-udev/libudev-core/udev-builtin-net_id.c | 624 + .../libudev-core/udev-builtin-net_setup_link.c | 107 + src/grp-udev/libudev-core/udev-builtin-path_id.c | 761 + src/grp-udev/libudev-core/udev-builtin-uaccess.c | 89 + src/grp-udev/libudev-core/udev-builtin-usb_id.c | 473 + src/grp-udev/libudev-core/udev-builtin.c | 142 + src/grp-udev/libudev-core/udev-ctrl.c | 462 + src/grp-udev/libudev-core/udev-event.c | 943 + src/grp-udev/libudev-core/udev-node.c | 375 + src/grp-udev/libudev-core/udev-rules.c | 2577 + src/grp-udev/libudev-core/udev-watch.c | 152 + src/grp-udev/mtd_probe/75-probe_mtd.rules | 7 + src/grp-udev/mtd_probe/Makefile | 37 + src/grp-udev/mtd_probe/mtd_probe.c | 56 + src/grp-udev/mtd_probe/mtd_probe.h | 51 + src/grp-udev/mtd_probe/probe_smartmedia.c | 96 + src/grp-udev/rules/.gitignore | 1 + src/grp-udev/rules/50-udev-default.rules | 77 + src/grp-udev/rules/60-block.rules | 11 + src/grp-udev/rules/60-drm.rules | 3 + src/grp-udev/rules/60-evdev.rules | 19 + src/grp-udev/rules/60-persistent-alsa.rules | 14 + src/grp-udev/rules/60-persistent-input.rules | 40 + .../rules/60-persistent-storage-tape.rules | 26 + src/grp-udev/rules/60-persistent-storage.rules | 88 + src/grp-udev/rules/60-serial.rules | 26 + src/grp-udev/rules/64-btrfs.rules | 13 + src/grp-udev/rules/70-mouse.rules | 18 + src/grp-udev/rules/70-touchpad.rules | 13 + src/grp-udev/rules/75-net-description.rules | 14 + src/grp-udev/rules/78-sound-card.rules | 89 + src/grp-udev/rules/80-drivers.rules | 13 + src/grp-udev/rules/80-net-setup-link.rules | 13 + src/grp-udev/rules/99-systemd.rules.in | 66 + src/grp-udev/scsi_id/.gitignore | 1 + src/grp-udev/scsi_id/Makefile | 41 + src/grp-udev/scsi_id/README | 4 + src/grp-udev/scsi_id/scsi.h | 99 + src/grp-udev/scsi_id/scsi_id.c | 626 + src/grp-udev/scsi_id/scsi_id.h | 75 + src/grp-udev/scsi_id/scsi_serial.c | 966 + src/grp-udev/systemd-hwdb/Makefile | 77 + src/grp-udev/systemd-hwdb/hwdb.c | 743 + src/grp-udev/systemd-hwdb/hwdb.xml | 85 + src/grp-udev/systemd-hwdb/systemd-hwdb.xml | 93 + src/grp-udev/systemd-udevd/Makefile | 36 + .../systemd-udevd/systemd-udevd.service.in | 27 + .../systemd-udevd/systemd-udevd.service.xml | 188 + src/grp-udev/systemd-udevd/udev.conf | 3 + src/grp-udev/systemd-udevd/udev.conf.xml | 94 + src/grp-udev/systemd-udevd/udevd.c | 1764 + src/grp-udev/udev.pc.in | 5 + src/grp-udev/udev.xml | 755 + src/grp-udev/udevadm/Makefile | 46 + src/grp-udev/udevadm/udevadm-control.c | 163 + src/grp-udev/udevadm/udevadm-hwdb.c | 698 + src/grp-udev/udevadm/udevadm-info.c | 481 + src/grp-udev/udevadm/udevadm-monitor.c | 281 + src/grp-udev/udevadm/udevadm-settle.c | 162 + src/grp-udev/udevadm/udevadm-test-builtin.c | 112 + src/grp-udev/udevadm/udevadm-test.c | 160 + src/grp-udev/udevadm/udevadm-trigger.c | 287 + src/grp-udev/udevadm/udevadm-util.c | 51 + src/grp-udev/udevadm/udevadm-util.h | 24 + src/grp-udev/udevadm/udevadm.c | 137 + src/grp-udev/udevadm/udevadm.completion.bash | 97 + src/grp-udev/udevadm/udevadm.completion.zsh | 141 + src/grp-udev/udevadm/udevadm.xml | 576 + src/grp-udev/v4l_id/60-persistent-v4l.rules | 20 + src/grp-udev/v4l_id/Makefile | 38 + src/grp-udev/v4l_id/v4l_id.c | 87 + src/grp-utils/Makefile | 32 + src/grp-utils/systemd-ac-power/Makefile | 33 + src/grp-utils/systemd-ac-power/ac-power.c | 35 + src/grp-utils/systemd-escape/Makefile | 34 + src/grp-utils/systemd-escape/escape.c | 237 + src/grp-utils/systemd-escape/systemd-escape.xml | 178 + src/grp-utils/systemd-notify/Makefile | 33 + src/grp-utils/systemd-notify/notify.c | 203 + .../systemd-notify/systemd-notify.completion.zsh | 12 + src/grp-utils/systemd-notify/systemd-notify.xml | 185 + src/grp-utils/systemd-path/Makefile | 34 + src/grp-utils/systemd-path/_sd-common.h | 1 + src/grp-utils/systemd-path/path.c | 198 + src/grp-utils/systemd-path/sd-path.c | 638 + src/grp-utils/systemd-path/sd-path.h | 91 + .../systemd-path/systemd-path.completion.bash | 60 + src/grp-utils/systemd-path/systemd-path.xml | 107 + src/grp-utils/systemd-socket-activate/Makefile | 35 + src/grp-utils/systemd-socket-activate/activate.c | 545 + .../systemd-socket-activate.xml | 206 + src/hibernate-resume/Makefile | 1 - src/hibernate-resume/hibernate-resume-generator.c | 99 - src/hibernate-resume/hibernate-resume.c | 82 - src/hostname/.gitignore | 1 - src/hostname/Makefile | 1 - src/hostname/hostnamectl.c | 529 - src/hostname/hostnamed.c | 743 - src/hostname/org.freedesktop.hostname1.conf | 27 - src/hostname/org.freedesktop.hostname1.policy.in | 50 - src/hostname/org.freedesktop.hostname1.service | 12 - src/hwdb/Makefile | 1 - src/hwdb/hwdb.c | 743 - src/import/.gitignore | 1 - src/import/Makefile | 1 - src/import/curl-util.c | 448 - src/import/curl-util.h | 56 - src/import/export-raw.c | 352 - src/import/export-raw.h | 36 - src/import/export-tar.c | 328 - src/import/export-tar.h | 36 - src/import/export.c | 320 - src/import/import-common.c | 221 - src/import/import-common.h | 26 - src/import/import-compress.c | 469 - src/import/import-compress.h | 61 - src/import/import-pubring.gpg | Bin 9551 -> 0 bytes src/import/import-raw.c | 467 - src/import/import-raw.h | 36 - src/import/import-tar.c | 388 - src/import/import-tar.h | 36 - src/import/import.c | 337 - src/import/importd.c | 1219 - src/import/org.freedesktop.import1.conf | 62 - src/import/org.freedesktop.import1.policy.in | 49 - src/import/org.freedesktop.import1.service | 12 - src/import/pull-common.c | 547 - src/import/pull-common.h | 36 - src/import/pull-job.c | 618 - src/import/pull-job.h | 106 - src/import/pull-raw.c | 651 - src/import/pull-raw.h | 36 - src/import/pull-tar.c | 563 - src/import/pull-tar.h | 36 - src/import/pull.c | 337 - src/import/qcow2-util.c | 352 - src/import/qcow2-util.h | 23 - src/import/test-qcow2.c | 53 - src/initctl/Makefile | 1 - src/initctl/initctl.c | 428 - src/journal-remote/.gitignore | 2 - src/journal-remote/Makefile | 1 - src/journal-remote/browse.html | 544 - src/journal-remote/journal-gatewayd.c | 1077 - src/journal-remote/journal-remote-parse.c | 506 - src/journal-remote/journal-remote-parse.h | 69 - src/journal-remote/journal-remote-write.c | 168 - src/journal-remote/journal-remote-write.h | 70 - src/journal-remote/journal-remote.c | 1601 - src/journal-remote/journal-remote.conf.in | 6 - src/journal-remote/journal-remote.h | 52 - src/journal-remote/journal-upload-journal.c | 422 - src/journal-remote/journal-upload.c | 880 - src/journal-remote/journal-upload.conf.in | 5 - src/journal-remote/journal-upload.h | 71 - src/journal-remote/log-generator.py | 76 - src/journal-remote/microhttpd-util.c | 327 - src/journal-remote/microhttpd-util.h | 60 - src/journal/.gitignore | 4 - src/journal/Makefile | 1 - src/journal/audit-type.c | 29 - src/journal/audit-type.h | 37 - src/journal/cat.c | 161 - src/journal/catalog.c | 767 - src/journal/catalog.h | 36 - src/journal/compress.c | 683 - src/journal/compress.h | 85 - src/journal/fsprg.c | 374 - src/journal/fsprg.h | 65 - src/journal/journal-authenticate.c | 551 - src/journal/journal-authenticate.h | 41 - src/journal/journal-def.h | 237 - src/journal/journal-file.c | 3616 - src/journal/journal-file.h | 265 - src/journal/journal-internal.h | 143 - src/journal/journal-qrcode.c | 135 - src/journal/journal-qrcode.h | 27 - src/journal/journal-send.c | 575 - src/journal/journal-vacuum.c | 349 - src/journal/journal-vacuum.h | 27 - src/journal/journal-verify.c | 1308 - src/journal/journal-verify.h | 24 - src/journal/journalctl.c | 2615 - src/journal/journald-audit.c | 564 - src/journal/journald-audit.h | 27 - src/journal/journald-console.c | 115 - src/journal/journald-console.h | 24 - src/journal/journald-gperf.gperf | 46 - src/journal/journald-kmsg.c | 473 - src/journal/journald-kmsg.h | 29 - src/journal/journald-native.c | 501 - src/journal/journald-native.h | 35 - src/journal/journald-rate-limit.c | 271 - src/journal/journald-rate-limit.h | 28 - src/journal/journald-server.c | 2007 - src/journal/journald-server.h | 186 - src/journal/journald-stream.c | 785 - src/journal/journald-stream.h | 31 - src/journal/journald-syslog.c | 454 - src/journal/journald-syslog.h | 33 - src/journal/journald-wall.c | 71 - src/journal/journald-wall.h | 24 - src/journal/journald.c | 120 - src/journal/journald.conf | 41 - src/journal/lookup3.c | 1009 - src/journal/lookup3.h | 22 - src/journal/mmap-cache.c | 725 - src/journal/mmap-cache.h | 49 - src/journal/sd-journal.c | 2985 - src/journal/test-audit-type.c | 42 - src/journal/test-catalog.c | 264 - src/journal/test-compress-benchmark.c | 180 - src/journal/test-compress.c | 308 - src/journal/test-journal-enum.c | 53 - src/journal/test-journal-flush.c | 75 - src/journal/test-journal-init.c | 64 - src/journal/test-journal-interleaving.c | 307 - src/journal/test-journal-match.c | 76 - src/journal/test-journal-send.c | 102 - src/journal/test-journal-stream.c | 196 - src/journal/test-journal-syslog.c | 44 - src/journal/test-journal-verify.c | 150 - src/journal/test-journal.c | 178 - src/journal/test-mmap-cache.c | 79 - src/kernel-install/50-depmod.install | 8 - src/kernel-install/90-loaderentry.install | 89 - src/kernel-install/Makefile | 1 - src/kernel-install/kernel-install | 144 - src/libbasic/Makefile | 29 + src/libbasic/include/Makefile | 28 + src/libbasic/include/basic/.gitignore | 16 + src/libbasic/include/basic/Makefile | 59 + src/libbasic/include/basic/MurmurHash2.h | 33 + src/libbasic/include/basic/af-list.h | 41 + src/libbasic/include/basic/alloc-util.h | 111 + src/libbasic/include/basic/architecture.h | 199 + src/libbasic/include/basic/arphrd-list.h | 25 + src/libbasic/include/basic/async.h | 25 + src/libbasic/include/basic/audit-util.h | 31 + src/libbasic/include/basic/barrier.h | 91 + src/libbasic/include/basic/bitmap.h | 49 + src/libbasic/include/basic/blkid-util.h | 31 + src/libbasic/include/basic/btrfs-ctree.h | 96 + src/libbasic/include/basic/btrfs-util.h | 131 + src/libbasic/include/basic/build.h | 155 + src/libbasic/include/basic/bus-label.h | 31 + src/libbasic/include/basic/calendarspec.h | 58 + src/libbasic/include/basic/cap-list.h | 24 + src/libbasic/include/basic/capability-util.h | 57 + src/libbasic/include/basic/cgroup-util.h | 236 + src/libbasic/include/basic/chattr-util.h | 26 + src/libbasic/include/basic/clock-util.h | 29 + src/libbasic/include/basic/conf-files.h | 25 + src/libbasic/include/basic/copy.h | 36 + src/libbasic/include/basic/cpu-set-util.h | 32 + src/libbasic/include/basic/def.h | 90 + src/libbasic/include/basic/device-nodes.h | 26 + src/libbasic/include/basic/dirent-util.h | 52 + src/libbasic/include/basic/env-util.h | 51 + src/libbasic/include/basic/errno-list.h | 25 + src/libbasic/include/basic/escape.h | 54 + src/libbasic/include/basic/ether-addr-util.h | 39 + src/libbasic/include/basic/exit-status.h | 106 + src/libbasic/include/basic/extract-word.h | 35 + src/libbasic/include/basic/fd-util.h | 80 + src/libbasic/include/basic/fileio-label.h | 30 + src/libbasic/include/basic/fileio.h | 90 + src/libbasic/include/basic/formats-util.h | 79 + src/libbasic/include/basic/fs-util.h | 78 + src/libbasic/include/basic/glob-util.h | 36 + src/libbasic/include/basic/gunicode.h | 30 + src/libbasic/include/basic/hash-funcs.h | 65 + src/libbasic/include/basic/hashmap.h | 372 + src/libbasic/include/basic/hexdecoct.h | 56 + src/libbasic/include/basic/hostname-util.h | 41 + src/libbasic/include/basic/in-addr-util.h | 64 + src/libbasic/include/basic/io-util.h | 95 + src/libbasic/include/basic/ioprio.h | 55 + src/libbasic/include/basic/label.h | 28 + src/libbasic/include/basic/list.h | 182 + src/libbasic/include/basic/locale-util.h | 73 + src/libbasic/include/basic/lockfile-util.h | 39 + src/libbasic/include/basic/log.h | 249 + src/libbasic/include/basic/login-util.h | 29 + src/libbasic/include/basic/macro.h | 415 + src/libbasic/include/basic/memfd-util.h | 36 + src/libbasic/include/basic/mempool.h | 47 + src/libbasic/include/basic/missing.h | 1045 + src/libbasic/include/basic/missing_syscall.h | 300 + src/libbasic/include/basic/mkdir.h | 38 + src/libbasic/include/basic/mount-util.h | 54 + src/libbasic/include/basic/nss-util.h | 199 + src/libbasic/include/basic/ordered-set.h | 74 + src/libbasic/include/basic/parse-util.h | 109 + src/libbasic/include/basic/path-util.h | 127 + src/libbasic/include/basic/prioq.h | 43 + src/libbasic/include/basic/proc-cmdline.h | 27 + src/libbasic/include/basic/process-util.h | 105 + src/libbasic/include/basic/random-util.h | 39 + src/libbasic/include/basic/ratelimit.h | 58 + src/libbasic/include/basic/raw-clone.h | 81 + src/libbasic/include/basic/refcnt.h | 34 + src/libbasic/include/basic/replace-var.h | 22 + src/libbasic/include/basic/rlimit-util.h | 36 + src/libbasic/include/basic/rm-rf.h | 41 + src/libbasic/include/basic/securebits.h | 45 + src/libbasic/include/basic/selinux-util.h | 51 + src/libbasic/include/basic/set.h | 138 + src/libbasic/include/basic/sigbus.h | 25 + src/libbasic/include/basic/signal-util.h | 56 + src/libbasic/include/basic/siphash24.h | 23 + src/libbasic/include/basic/smack-util.h | 54 + src/libbasic/include/basic/socket-util.h | 155 + src/libbasic/include/basic/sparse-endian.h | 88 + src/libbasic/include/basic/special.h | 119 + src/libbasic/include/basic/stat-util.h | 69 + src/libbasic/include/basic/stdio-util.h | 76 + src/libbasic/include/basic/strbuf.h | 54 + src/libbasic/include/basic/string-table.h | 119 + src/libbasic/include/basic/string-util.h | 194 + src/libbasic/include/basic/strv.h | 174 + src/libbasic/include/basic/strxcpyx.h | 31 + src/libbasic/include/basic/syslog-util.h | 32 + src/libbasic/include/basic/terminal-util.h | 128 + src/libbasic/include/basic/time-util.h | 178 + src/libbasic/include/basic/umask-util.h | 46 + src/libbasic/include/basic/unaligned.h | 129 + src/libbasic/include/basic/unit-name.h | 367 + src/libbasic/include/basic/user-util.h | 70 + src/libbasic/include/basic/utf8.h | 60 + src/libbasic/include/basic/util.h | 192 + src/libbasic/include/basic/verbs.h | 33 + src/libbasic/include/basic/virt.h | 72 + src/libbasic/include/basic/web-util.h | 30 + src/libbasic/include/basic/xattr-util.h | 37 + src/libbasic/include/basic/xml.h | 32 + src/libbasic/src/Makefile | 241 + src/libbasic/src/MurmurHash2.c | 86 + src/libbasic/src/af-list.c | 56 + src/libbasic/src/alloc-util.c | 83 + src/libbasic/src/architecture.c | 179 + src/libbasic/src/arphrd-list.c | 56 + src/libbasic/src/async.c | 94 + src/libbasic/src/audit-util.c | 105 + src/libbasic/src/barrier.c | 415 + src/libbasic/src/bitmap.c | 237 + src/libbasic/src/btrfs-util.c | 2076 + src/libbasic/src/bus-label.c | 98 + src/libbasic/src/calendarspec.c | 1127 + src/libbasic/src/cap-list.c | 66 + src/libbasic/src/capability-util.c | 361 + src/libbasic/src/cgroup-util.c | 2365 + src/libbasic/src/chattr-util.c | 108 + src/libbasic/src/clock-util.c | 166 + src/libbasic/src/conf-files.c | 166 + src/libbasic/src/copy.c | 603 + src/libbasic/src/cpu-set-util.c | 114 + src/libbasic/src/device-nodes.c | 80 + src/libbasic/src/dirent-util.c | 74 + src/libbasic/src/env-util.c | 624 + src/libbasic/src/errno-list.c | 57 + src/libbasic/src/escape.c | 502 + src/libbasic/src/ether-addr-util.c | 125 + src/libbasic/src/exit-status.c | 234 + src/libbasic/src/extract-word.c | 298 + src/libbasic/src/fd-util.c | 380 + src/libbasic/src/fileio-label.c | 68 + src/libbasic/src/fileio.c | 1398 + src/libbasic/src/fs-util.c | 539 + src/libbasic/src/glob-util.c | 70 + src/libbasic/src/gunicode.c | 112 + src/libbasic/src/hash-funcs.c | 81 + src/libbasic/src/hashmap.c | 1829 + src/libbasic/src/hexdecoct.c | 754 + src/libbasic/src/hostname-util.c | 252 + src/libbasic/src/in-addr-util.c | 449 + src/libbasic/src/io-util.c | 269 + src/libbasic/src/label.c | 82 + src/libbasic/src/locale-util.c | 322 + src/libbasic/src/lockfile-util.c | 153 + src/libbasic/src/log.c | 1170 + src/libbasic/src/login-util.c | 31 + src/libbasic/src/memfd-util.c | 174 + src/libbasic/src/mempool.c | 104 + src/libbasic/src/mkdir-label.c | 38 + src/libbasic/src/mkdir.c | 128 + src/libbasic/src/mount-util.c | 559 + src/libbasic/src/ordered-set.c | 64 + src/libbasic/src/parse-util.c | 553 + src/libbasic/src/path-util.c | 816 + src/libbasic/src/prioq.c | 320 + src/libbasic/src/proc-cmdline.c | 188 + src/libbasic/src/process-util.c | 861 + src/libbasic/src/random-util.c | 134 + src/libbasic/src/ratelimit.c | 56 + src/libbasic/src/replace-var.c | 112 + src/libbasic/src/rlimit-util.c | 321 + src/libbasic/src/rm-rf.c | 236 + src/libbasic/src/selinux-util.c | 485 + src/libbasic/src/sigbus.c | 152 + src/libbasic/src/signal-util.c | 278 + src/libbasic/src/siphash24.c | 193 + src/libbasic/src/smack-util.c | 241 + src/libbasic/src/socket-label.c | 170 + src/libbasic/src/socket-util.c | 1048 + src/libbasic/src/stat-util.c | 219 + src/libbasic/src/strbuf.c | 205 + src/libbasic/src/string-table.c | 34 + src/libbasic/src/string-util.c | 855 + src/libbasic/src/strv.c | 943 + src/libbasic/src/strxcpyx.c | 100 + src/libbasic/src/syslog-util.c | 114 + src/libbasic/src/terminal-util.c | 1222 + src/libbasic/src/time-util.c | 1229 + src/libbasic/src/unit-name.c | 1049 + src/libbasic/src/user-util.c | 481 + src/libbasic/src/utf8.c | 409 + src/libbasic/src/util.c | 876 + src/libbasic/src/verbs.c | 101 + src/libbasic/src/virt.c | 516 + src/libbasic/src/web-util.c | 76 + src/libbasic/src/xattr-util.c | 200 + src/libbasic/src/xml.c | 255 + src/libfirewall/Makefile | 41 + src/libfirewall/firewall-util.c | 359 + src/libfirewall/firewall-util.h | 83 + src/libshared/Makefile | 28 + src/libshared/include/shared/acl-util.h | 48 + src/libshared/include/shared/acpi-fpdt.h | 24 + src/libshared/include/shared/apparmor-util.h | 24 + src/libshared/include/shared/ask-password-api.h | 38 + src/libshared/include/shared/base-filesystem.h | 24 + src/libshared/include/shared/boot-timestamps.h | 25 + src/libshared/include/shared/bus-unit-util.h | 57 + src/libshared/include/shared/bus-util.h | 160 + src/libshared/include/shared/cgroup-show.h | 32 + src/libshared/include/shared/clean-ipc.h | 24 + src/libshared/include/shared/condition.h | 94 + src/libshared/include/shared/conf-parser.h | 229 + src/libshared/include/shared/dev-setup.h | 24 + src/libshared/include/shared/dns-domain.h | 109 + src/libshared/include/shared/dropin.h | 61 + src/libshared/include/shared/efivars.h | 131 + src/libshared/include/shared/fdset.h | 58 + src/libshared/include/shared/fstab-util.h | 52 + src/libshared/include/shared/gcrypt-util.h | 39 + src/libshared/include/shared/generator.h | 40 + src/libshared/include/shared/gpt.h | 66 + src/libshared/include/shared/ima-util.h | 24 + src/libshared/include/shared/import-util.h | 43 + src/libshared/include/shared/initreq.h | 77 + src/libshared/include/shared/install-printf.h | 24 + src/libshared/include/shared/install.h | 257 + src/libshared/include/shared/logs-show.h | 71 + src/libshared/include/shared/machine-image.h | 103 + src/libshared/include/shared/machine-pool.h | 30 + src/libshared/include/shared/output-mode.h | 57 + src/libshared/include/shared/pager.h | 30 + src/libshared/include/shared/path-lookup.h | 77 + src/libshared/include/shared/ptyfwd.h | 48 + src/libshared/include/shared/resolve-util.h | 60 + src/libshared/include/shared/seccomp-util.h | 35 + src/libshared/include/shared/sleep-config.h | 26 + .../include/shared/spawn-ask-password-agent.h | 23 + src/libshared/include/shared/spawn-polkit-agent.h | 23 + src/libshared/include/shared/specifier.h | 37 + src/libshared/include/shared/switch-root.h | 23 + src/libshared/include/shared/sysctl-util.h | 25 + src/libshared/include/shared/test-tables.h | 60 + src/libshared/include/shared/tests.h | 22 + src/libshared/include/shared/udev-util.h | 44 + src/libshared/include/shared/uid-range.h | 33 + src/libshared/include/shared/utmp-wtmp.h | 74 + src/libshared/include/shared/vlan-util.h | 35 + src/libshared/include/shared/watchdog.h | 29 + src/libshared/src/Makefile | 182 + src/libshared/src/acl-util.c | 429 + src/libshared/src/acpi-fpdt.c | 164 + src/libshared/src/apparmor-util.c | 39 + src/libshared/src/ask-password-api.c | 734 + src/libshared/src/base-filesystem.c | 129 + src/libshared/src/boot-timestamps.c | 64 + src/libshared/src/bus-unit-util.c | 1324 + src/libshared/src/bus-util.c | 1550 + src/libshared/src/cgroup-show.c | 312 + src/libshared/src/clean-ipc.c | 365 + src/libshared/src/condition.c | 542 + src/libshared/src/conf-parser.c | 914 + src/libshared/src/dev-setup.c | 73 + src/libshared/src/dns-domain.c | 1322 + src/libshared/src/dropin.c | 251 + src/libshared/src/efivars.c | 715 + src/libshared/src/fdset.c | 273 + src/libshared/src/fstab-util.c | 263 + src/libshared/src/gcrypt-util.c | 71 + src/libshared/src/generator.c | 207 + src/libshared/src/ima-util.c | 32 + src/libshared/src/import-util.c | 185 + src/libshared/src/install-printf.c | 133 + src/libshared/src/install.c | 2957 + src/libshared/src/logs-show.c | 1310 + src/libshared/src/machine-image.c | 820 + src/libshared/src/machine-pool.c | 427 + src/libshared/src/output-mode.c | 37 + src/libshared/src/pager.c | 226 + src/libshared/src/path-lookup.c | 822 + src/libshared/src/ptyfwd.c | 484 + src/libshared/src/resolve-util.c | 39 + src/libshared/src/seccomp-util.c | 323 + src/libshared/src/sleep-config.c | 278 + src/libshared/src/spawn-ask-password-agent.c | 62 + src/libshared/src/spawn-polkit-agent.c | 102 + src/libshared/src/specifier.c | 188 + src/libshared/src/switch-root.c | 156 + src/libshared/src/sysctl-util.c | 73 + src/libshared/src/tests.c | 33 + src/libshared/src/uid-range.c | 208 + src/libshared/src/utmp-wtmp.c | 445 + src/libshared/src/vlan-util.c | 69 + src/libshared/src/watchdog.c | 165 + src/libsystemd-network/Makefile | 30 +- src/libsystemd-network/arp-util.c | 154 - src/libsystemd-network/arp-util.h | 32 - src/libsystemd-network/dhcp-identifier.c | 129 - src/libsystemd-network/dhcp-identifier.h | 74 - src/libsystemd-network/dhcp-internal.h | 69 - src/libsystemd-network/dhcp-lease-internal.h | 102 - src/libsystemd-network/dhcp-network.c | 235 - src/libsystemd-network/dhcp-option.c | 263 - src/libsystemd-network/dhcp-packet.c | 191 - src/libsystemd-network/dhcp-protocol.h | 113 - src/libsystemd-network/dhcp-server-internal.h | 96 - src/libsystemd-network/dhcp6-internal.h | 81 - src/libsystemd-network/dhcp6-lease-internal.h | 74 - src/libsystemd-network/dhcp6-network.c | 91 - src/libsystemd-network/dhcp6-option.c | 413 - src/libsystemd-network/dhcp6-protocol.h | 106 - src/libsystemd-network/icmp6-util.c | 141 - src/libsystemd-network/icmp6-util.h | 25 - .../include/systemd-network/_sd-common.h | 1 + .../include/systemd-network/arp-util.h | 32 + .../include/systemd-network/dhcp-identifier.h | 74 + .../include/systemd-network/dhcp-internal.h | 70 + .../include/systemd-network/dhcp-lease-internal.h | 103 + .../include/systemd-network/dhcp-protocol.h | 113 + .../include/systemd-network/dhcp-server-internal.h | 97 + .../include/systemd-network/dhcp6-internal.h | 81 + .../include/systemd-network/dhcp6-lease-internal.h | 73 + .../include/systemd-network/dhcp6-protocol.h | 106 + .../include/systemd-network/icmp6-util.h | 25 + .../include/systemd-network/lldp-internal.h | 56 + .../include/systemd-network/lldp-neighbor.h | 108 + .../include/systemd-network/lldp-network.h | 25 + .../include/systemd-network/ndisc-internal.h | 49 + .../include/systemd-network/ndisc-router.h | 62 + .../include/systemd-network/network-internal.h | 81 + .../include/systemd-network/sd-dhcp-client.h | 157 + .../include/systemd-network/sd-dhcp-lease.h | 67 + .../include/systemd-network/sd-dhcp-server.h | 65 + .../include/systemd-network/sd-dhcp6-client.h | 134 + .../include/systemd-network/sd-dhcp6-lease.h | 52 + .../include/systemd-network/sd-ipv4acd.h | 60 + .../include/systemd-network/sd-ipv4ll.h | 60 + .../include/systemd-network/sd-lldp.h | 182 + .../include/systemd-network/sd-ndisc.h | 130 + src/libsystemd-network/lldp-internal.h | 55 - src/libsystemd-network/lldp-neighbor.c | 813 - src/libsystemd-network/lldp-neighbor.h | 108 - src/libsystemd-network/lldp-network.c | 77 - src/libsystemd-network/lldp-network.h | 25 - src/libsystemd-network/ndisc-internal.h | 49 - src/libsystemd-network/ndisc-router.c | 779 - src/libsystemd-network/ndisc-router.h | 62 - src/libsystemd-network/network-internal.c | 556 - src/libsystemd-network/network-internal.h | 81 - src/libsystemd-network/sd-dhcp-client.c | 1906 - src/libsystemd-network/sd-dhcp-lease.c | 1178 - src/libsystemd-network/sd-dhcp-server.c | 1176 - src/libsystemd-network/sd-dhcp6-client.c | 1335 - src/libsystemd-network/sd-dhcp6-lease.c | 410 - src/libsystemd-network/sd-ipv4acd.c | 526 - src/libsystemd-network/sd-ipv4ll.c | 346 - src/libsystemd-network/sd-lldp.c | 539 - src/libsystemd-network/sd-ndisc.c | 422 - src/libsystemd-network/src/Makefile | 83 + src/libsystemd-network/src/arp-util.c | 155 + src/libsystemd-network/src/dhcp-identifier.c | 129 + src/libsystemd-network/src/dhcp-network.c | 236 + src/libsystemd-network/src/dhcp-option.c | 262 + src/libsystemd-network/src/dhcp-packet.c | 191 + src/libsystemd-network/src/dhcp6-network.c | 92 + src/libsystemd-network/src/dhcp6-option.c | 412 + src/libsystemd-network/src/icmp6-util.c | 142 + src/libsystemd-network/src/lldp-neighbor.c | 813 + src/libsystemd-network/src/lldp-network.c | 78 + src/libsystemd-network/src/ndisc-router.c | 778 + src/libsystemd-network/src/network-internal.c | 556 + src/libsystemd-network/src/sd-dhcp-client.c | 1906 + src/libsystemd-network/src/sd-dhcp-lease.c | 1177 + src/libsystemd-network/src/sd-dhcp-server.c | 1175 + src/libsystemd-network/src/sd-dhcp6-client.c | 1335 + src/libsystemd-network/src/sd-dhcp6-lease.c | 410 + src/libsystemd-network/src/sd-ipv4acd.c | 525 + src/libsystemd-network/src/sd-ipv4ll.c | 345 + src/libsystemd-network/src/sd-lldp.c | 538 + src/libsystemd-network/src/sd-ndisc.c | 421 + src/libsystemd-network/test-acd.c | 114 - src/libsystemd-network/test-dhcp-client.c | 513 - src/libsystemd-network/test-dhcp-option.c | 367 - src/libsystemd-network/test-dhcp-server.c | 261 - src/libsystemd-network/test-dhcp6-client.c | 763 - src/libsystemd-network/test-ipv4ll-manual.c | 128 - src/libsystemd-network/test-ipv4ll.c | 221 - src/libsystemd-network/test-lldp.c | 261 - src/libsystemd-network/test-ndisc-rs.c | 317 - src/libsystemd-network/test/Makefile | 118 + src/libsystemd-network/test/test-acd.c | 114 + src/libsystemd-network/test/test-dhcp-client.c | 513 + src/libsystemd-network/test/test-dhcp-option.c | 367 + src/libsystemd-network/test/test-dhcp-server.c | 261 + src/libsystemd-network/test/test-dhcp6-client.c | 763 + src/libsystemd-network/test/test-ipv4ll-manual.c | 129 + src/libsystemd-network/test/test-ipv4ll.c | 220 + src/libsystemd-network/test/test-lldp.c | 261 + src/libsystemd-network/test/test-ndisc-rs.c | 316 + src/libsystemd/Makefile | 104 +- src/libsystemd/include/systemd/_sd-common.h | 83 + src/libsystemd/include/systemd/sd-bus-protocol.h | 102 + src/libsystemd/include/systemd/sd-bus-vtable.h | 141 + src/libsystemd/include/systemd/sd-bus.h | 455 + src/libsystemd/include/systemd/sd-daemon.h | 294 + src/libsystemd/include/systemd/sd-event.h | 143 + src/libsystemd/include/systemd/sd-id128.h | 115 + src/libsystemd/include/systemd/sd-journal.h | 174 + src/libsystemd/include/systemd/sd-login.h | 245 + src/libsystemd/include/systemd/sd-messages.h | 90 + src/libsystemd/include/systemd/sd-utf8.h | 32 + src/libsystemd/libsystemd-pkgconfig.xml | 12 + src/libsystemd/sd-bus-errors.xml | 309 + src/libsystemd/sd-bus.xml | 123 + src/libsystemd/sd-bus/DIFFERENCES | 25 - src/libsystemd/sd-bus/GVARIANT-SERIALIZATION | 110 - src/libsystemd/sd-bus/Makefile | 1 - src/libsystemd/sd-bus/PORTING-DBUS1 | 535 - src/libsystemd/sd-bus/bus-bloom.c | 156 - src/libsystemd/sd-bus/bus-bloom.h | 43 - src/libsystemd/sd-bus/bus-common-errors.c | 87 - src/libsystemd/sd-bus/bus-common-errors.h | 86 - src/libsystemd/sd-bus/bus-container.c | 277 - src/libsystemd/sd-bus/bus-container.h | 25 - src/libsystemd/sd-bus/bus-control.c | 1588 - src/libsystemd/sd-bus/bus-control.h | 32 - src/libsystemd/sd-bus/bus-convenience.c | 626 - src/libsystemd/sd-bus/bus-creds.c | 1349 - src/libsystemd/sd-bus/bus-creds.h | 90 - src/libsystemd/sd-bus/bus-dump.c | 602 - src/libsystemd/sd-bus/bus-dump.h | 37 - src/libsystemd/sd-bus/bus-error.c | 608 - src/libsystemd/sd-bus/bus-error.h | 64 - src/libsystemd/sd-bus/bus-gvariant.c | 311 - src/libsystemd/sd-bus/bus-gvariant.h | 30 - src/libsystemd/sd-bus/bus-internal.c | 374 - src/libsystemd/sd-bus/bus-internal.h | 399 - src/libsystemd/sd-bus/bus-introspect.c | 212 - src/libsystemd/sd-bus/bus-introspect.h | 40 - src/libsystemd/sd-bus/bus-kernel.c | 1782 - src/libsystemd/sd-bus/bus-kernel.h | 93 - src/libsystemd/sd-bus/bus-match.c | 1221 - src/libsystemd/sd-bus/bus-match.h | 100 - src/libsystemd/sd-bus/bus-message.c | 5939 -- src/libsystemd/sd-bus/bus-message.h | 244 - src/libsystemd/sd-bus/bus-objects.c | 2806 - src/libsystemd/sd-bus/bus-objects.h | 25 - src/libsystemd/sd-bus/bus-protocol.h | 180 - src/libsystemd/sd-bus/bus-signature.c | 158 - src/libsystemd/sd-bus/bus-signature.h | 28 - src/libsystemd/sd-bus/bus-slot.c | 286 - src/libsystemd/sd-bus/bus-slot.h | 28 - src/libsystemd/sd-bus/bus-socket.c | 1064 - src/libsystemd/sd-bus/bus-socket.h | 37 - src/libsystemd/sd-bus/bus-track.c | 337 - src/libsystemd/sd-bus/bus-track.h | 22 - src/libsystemd/sd-bus/bus-type.c | 176 - src/libsystemd/sd-bus/bus-type.h | 37 - src/libsystemd/sd-bus/busctl-introspect.c | 790 - src/libsystemd/sd-bus/busctl-introspect.h | 32 - src/libsystemd/sd-bus/busctl.c | 2087 - src/libsystemd/sd-bus/kdbus.h | 980 - src/libsystemd/sd-bus/sd-bus.c | 3791 - src/libsystemd/sd-bus/test-bus-benchmark.c | 371 - src/libsystemd/sd-bus/test-bus-chat.c | 560 - src/libsystemd/sd-bus/test-bus-cleanup.c | 95 - src/libsystemd/sd-bus/test-bus-creds.c | 50 - src/libsystemd/sd-bus/test-bus-error.c | 232 - src/libsystemd/sd-bus/test-bus-gvariant.c | 224 - src/libsystemd/sd-bus/test-bus-introspect.c | 63 - src/libsystemd/sd-bus/test-bus-kernel-bloom.c | 141 - src/libsystemd/sd-bus/test-bus-kernel.c | 190 - src/libsystemd/sd-bus/test-bus-marshal.c | 432 - src/libsystemd/sd-bus/test-bus-match.c | 159 - src/libsystemd/sd-bus/test-bus-objects.c | 555 - src/libsystemd/sd-bus/test-bus-server.c | 216 - src/libsystemd/sd-bus/test-bus-signature.c | 164 - src/libsystemd/sd-bus/test-bus-zero-copy.c | 210 - src/libsystemd/sd-daemon.xml | 144 + src/libsystemd/sd-daemon/Makefile | 1 - src/libsystemd/sd-daemon/sd-daemon.c | 622 - src/libsystemd/sd-device/Makefile | 1 - .../sd-device/device-enumerator-private.h | 34 - src/libsystemd/sd-device/device-enumerator.c | 988 - src/libsystemd/sd-device/device-internal.h | 128 - src/libsystemd/sd-device/device-private.c | 1119 - src/libsystemd/sd-device/device-private.h | 68 - src/libsystemd/sd-device/device-util.h | 52 - src/libsystemd/sd-device/sd-device.c | 1935 - src/libsystemd/sd-event.xml | 187 + src/libsystemd/sd-event/Makefile | 1 - src/libsystemd/sd-event/sd-event.c | 2884 - src/libsystemd/sd-event/test-event.c | 361 - src/libsystemd/sd-hwdb/Makefile | 1 - src/libsystemd/sd-hwdb/hwdb-internal.h | 72 - src/libsystemd/sd-hwdb/hwdb-util.h | 26 - src/libsystemd/sd-hwdb/sd-hwdb.c | 470 - src/libsystemd/sd-id128.xml | 166 + src/libsystemd/sd-id128/Makefile | 1 - src/libsystemd/sd-id128/id128-util.c | 194 - src/libsystemd/sd-id128/id128-util.h | 45 - src/libsystemd/sd-id128/sd-id128.c | 161 - src/libsystemd/sd-journal.xml | 133 + src/libsystemd/sd-login.xml | 135 + src/libsystemd/sd-login/Makefile | 1 - src/libsystemd/sd-login/sd-login.c | 1062 - src/libsystemd/sd-login/test-login.c | 264 - src/libsystemd/sd-netlink/Makefile | 1 - src/libsystemd/sd-netlink/local-addresses.c | 275 - src/libsystemd/sd-netlink/local-addresses.h | 36 - src/libsystemd/sd-netlink/netlink-internal.h | 137 - src/libsystemd/sd-netlink/netlink-message.c | 963 - src/libsystemd/sd-netlink/netlink-socket.c | 474 - src/libsystemd/sd-netlink/netlink-types.c | 692 - src/libsystemd/sd-netlink/netlink-types.h | 95 - src/libsystemd/sd-netlink/netlink-util.c | 170 - src/libsystemd/sd-netlink/netlink-util.h | 39 - src/libsystemd/sd-netlink/rtnl-message.c | 702 - src/libsystemd/sd-netlink/sd-netlink.c | 957 - src/libsystemd/sd-netlink/test-local-addresses.c | 56 - src/libsystemd/sd-netlink/test-netlink.c | 440 - src/libsystemd/sd-network/Makefile | 1 - src/libsystemd/sd-network/network-util.c | 37 - src/libsystemd/sd-network/network-util.h | 24 - src/libsystemd/sd-network/sd-network.c | 400 - src/libsystemd/sd-path/Makefile | 1 - src/libsystemd/sd-path/sd-path.c | 638 - src/libsystemd/sd-resolve/Makefile | 1 - src/libsystemd/sd-resolve/sd-resolve.c | 1243 - src/libsystemd/sd-resolve/test-resolve.c | 114 - src/libsystemd/sd-utf8/Makefile | 1 - src/libsystemd/sd-utf8/sd-utf8.c | 35 - src/libsystemd/sd_booted.xml | 95 + src/libsystemd/sd_bus_add_match.xml | 119 + src/libsystemd/sd_bus_creds_get_pid.xml | 566 + src/libsystemd/sd_bus_creds_new_from_pid.xml | 353 + src/libsystemd/sd_bus_default.xml | 312 + src/libsystemd/sd_bus_error.xml | 389 + src/libsystemd/sd_bus_error_add_map.xml | 173 + src/libsystemd/sd_bus_get_fd.xml | 101 + src/libsystemd/sd_bus_message_append.xml | 264 + src/libsystemd/sd_bus_message_append_array.xml | 213 + src/libsystemd/sd_bus_message_append_basic.xml | 295 + .../sd_bus_message_append_string_memfd.xml | 153 + src/libsystemd/sd_bus_message_append_strv.xml | 116 + src/libsystemd/sd_bus_message_get_cookie.xml | 146 + .../sd_bus_message_get_monotonic_usec.xml | 181 + src/libsystemd/sd_bus_message_read_basic.xml | 113 + src/libsystemd/sd_bus_negotiate_fds.xml | 200 + src/libsystemd/sd_bus_new.xml | 189 + src/libsystemd/sd_bus_path_encode.xml | 188 + src/libsystemd/sd_bus_process.xml | 111 + src/libsystemd/sd_bus_request_name.xml | 213 + src/libsystemd/sd_event_add_child.xml | 246 + src/libsystemd/sd_event_add_defer.xml | 216 + src/libsystemd/sd_event_add_io.xml | 300 + src/libsystemd/sd_event_add_signal.xml | 221 + src/libsystemd/sd_event_add_time.xml | 313 + src/libsystemd/sd_event_exit.xml | 163 + src/libsystemd/sd_event_get_fd-glib-example.c | 68 + src/libsystemd/sd_event_get_fd.xml | 140 + src/libsystemd/sd_event_new.xml | 245 + src/libsystemd/sd_event_now.xml | 146 + src/libsystemd/sd_event_run.xml | 190 + src/libsystemd/sd_event_set_watchdog.xml | 177 + src/libsystemd/sd_event_source_get_event.xml | 100 + src/libsystemd/sd_event_source_get_pending.xml | 167 + src/libsystemd/sd_event_source_set_description.xml | 170 + src/libsystemd/sd_event_source_set_enabled.xml | 179 + src/libsystemd/sd_event_source_set_prepare.xml | 171 + src/libsystemd/sd_event_source_set_priority.xml | 189 + src/libsystemd/sd_event_source_set_userdata.xml | 119 + src/libsystemd/sd_event_source_unref.xml | 142 + src/libsystemd/sd_event_wait.xml | 356 + src/libsystemd/sd_get_seats.xml | 164 + src/libsystemd/sd_id128_get_machine.xml | 129 + src/libsystemd/sd_id128_randomize.xml | 114 + src/libsystemd/sd_id128_to_string.xml | 130 + src/libsystemd/sd_is_fifo.xml | 200 + src/libsystemd/sd_journal_add_match.xml | 216 + src/libsystemd/sd_journal_enumerate_fields.xml | 161 + src/libsystemd/sd_journal_get_catalog.xml | 137 + src/libsystemd/sd_journal_get_cursor.xml | 144 + .../sd_journal_get_cutoff_realtime_usec.xml | 145 + src/libsystemd/sd_journal_get_data.xml | 235 + src/libsystemd/sd_journal_get_fd.xml | 332 + src/libsystemd/sd_journal_get_realtime_usec.xml | 141 + src/libsystemd/sd_journal_get_usage.xml | 100 + src/libsystemd/sd_journal_has_runtime_files.xml | 95 + src/libsystemd/sd_journal_next.xml | 207 + src/libsystemd/sd_journal_open.xml | 228 + src/libsystemd/sd_journal_print.xml | 245 + src/libsystemd/sd_journal_query_unique.xml | 212 + src/libsystemd/sd_journal_seek_head.xml | 172 + src/libsystemd/sd_journal_stream_fd.xml | 163 + src/libsystemd/sd_listen_fds.xml | 257 + src/libsystemd/sd_login_monitor_new.xml | 287 + src/libsystemd/sd_machine_get_class.xml | 152 + src/libsystemd/sd_notify.xml | 408 + src/libsystemd/sd_pid_get_session.xml | 359 + src/libsystemd/sd_seat_get_active.xml | 212 + src/libsystemd/sd_session_is_active.xml | 359 + src/libsystemd/sd_uid_get_state.xml | 230 + src/libsystemd/sd_watchdog_enabled.xml | 169 + src/libsystemd/src/Makefile | 208 + src/libsystemd/src/sd-bus/DIFFERENCES | 25 + src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION | 110 + src/libsystemd/src/sd-bus/Makefile | 1 + src/libsystemd/src/sd-bus/PORTING-DBUS1 | 535 + src/libsystemd/src/sd-bus/bus-bloom.c | 157 + src/libsystemd/src/sd-bus/bus-bloom.h | 43 + src/libsystemd/src/sd-bus/bus-common-errors.c | 87 + src/libsystemd/src/sd-bus/bus-common-errors.h | 86 + src/libsystemd/src/sd-bus/bus-container.c | 278 + src/libsystemd/src/sd-bus/bus-container.h | 25 + src/libsystemd/src/sd-bus/bus-control.c | 1589 + src/libsystemd/src/sd-bus/bus-control.h | 32 + src/libsystemd/src/sd-bus/bus-convenience.c | 627 + src/libsystemd/src/sd-bus/bus-creds.c | 1351 + src/libsystemd/src/sd-bus/bus-creds.h | 90 + src/libsystemd/src/sd-bus/bus-dump.c | 603 + src/libsystemd/src/sd-bus/bus-dump.h | 37 + src/libsystemd/src/sd-bus/bus-error.c | 609 + src/libsystemd/src/sd-bus/bus-error.h | 64 + src/libsystemd/src/sd-bus/bus-gvariant.c | 311 + src/libsystemd/src/sd-bus/bus-gvariant.h | 30 + src/libsystemd/src/sd-bus/bus-internal.c | 375 + src/libsystemd/src/sd-bus/bus-internal.h | 400 + src/libsystemd/src/sd-bus/bus-introspect.c | 213 + src/libsystemd/src/sd-bus/bus-introspect.h | 40 + src/libsystemd/src/sd-bus/bus-kernel.c | 1783 + src/libsystemd/src/sd-bus/bus-kernel.h | 93 + src/libsystemd/src/sd-bus/bus-match.c | 1222 + src/libsystemd/src/sd-bus/bus-match.h | 100 + src/libsystemd/src/sd-bus/bus-message.c | 5940 ++ src/libsystemd/src/sd-bus/bus-message.h | 245 + src/libsystemd/src/sd-bus/bus-objects.c | 2807 + src/libsystemd/src/sd-bus/bus-objects.h | 25 + src/libsystemd/src/sd-bus/bus-protocol.h | 180 + src/libsystemd/src/sd-bus/bus-signature.c | 158 + src/libsystemd/src/sd-bus/bus-signature.h | 28 + src/libsystemd/src/sd-bus/bus-slot.c | 287 + src/libsystemd/src/sd-bus/bus-slot.h | 28 + src/libsystemd/src/sd-bus/bus-socket.c | 1065 + src/libsystemd/src/sd-bus/bus-socket.h | 37 + src/libsystemd/src/sd-bus/bus-track.c | 338 + src/libsystemd/src/sd-bus/bus-track.h | 22 + src/libsystemd/src/sd-bus/bus-type.c | 176 + src/libsystemd/src/sd-bus/bus-type.h | 37 + src/libsystemd/src/sd-bus/kdbus.h | 980 + src/libsystemd/src/sd-bus/sd-bus.c | 3792 + src/libsystemd/src/sd-bus/test-bus-benchmark.c | 372 + src/libsystemd/src/sd-bus/test-bus-chat.c | 561 + src/libsystemd/src/sd-bus/test-bus-cleanup.c | 96 + src/libsystemd/src/sd-bus/test-bus-creds.c | 51 + src/libsystemd/src/sd-bus/test-bus-error.c | 233 + src/libsystemd/src/sd-bus/test-bus-gvariant.c | 225 + src/libsystemd/src/sd-bus/test-bus-introspect.c | 64 + src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c | 142 + src/libsystemd/src/sd-bus/test-bus-kernel.c | 191 + src/libsystemd/src/sd-bus/test-bus-marshal.c | 433 + src/libsystemd/src/sd-bus/test-bus-match.c | 160 + src/libsystemd/src/sd-bus/test-bus-objects.c | 556 + src/libsystemd/src/sd-bus/test-bus-server.c | 217 + src/libsystemd/src/sd-bus/test-bus-signature.c | 165 + src/libsystemd/src/sd-bus/test-bus-zero-copy.c | 211 + src/libsystemd/src/sd-daemon/Makefile | 1 + src/libsystemd/src/sd-daemon/sd-daemon.c | 622 + src/libsystemd/src/sd-device/Makefile | 1 + .../src/sd-device/device-enumerator-private.h | 34 + src/libsystemd/src/sd-device/device-enumerator.c | 988 + src/libsystemd/src/sd-device/device-internal.h | 128 + src/libsystemd/src/sd-device/device-private.c | 1119 + src/libsystemd/src/sd-device/device-private.h | 68 + src/libsystemd/src/sd-device/device-util.h | 52 + src/libsystemd/src/sd-device/sd-device.c | 1935 + src/libsystemd/src/sd-device/sd-device.h | 101 + src/libsystemd/src/sd-event/Makefile | 1 + src/libsystemd/src/sd-event/sd-event.c | 2884 + src/libsystemd/src/sd-event/test-event.c | 361 + src/libsystemd/src/sd-hwdb/Makefile | 1 + src/libsystemd/src/sd-hwdb/hwdb-internal.h | 72 + src/libsystemd/src/sd-hwdb/hwdb-util.h | 26 + src/libsystemd/src/sd-hwdb/sd-hwdb.c | 470 + src/libsystemd/src/sd-hwdb/sd-hwdb.h | 49 + src/libsystemd/src/sd-id128/Makefile | 1 + src/libsystemd/src/sd-id128/id128-util.c | 195 + src/libsystemd/src/sd-id128/id128-util.h | 46 + src/libsystemd/src/sd-id128/sd-id128.c | 162 + src/libsystemd/src/sd-journal/Makefile | 42 + src/libsystemd/src/sd-journal/audit-type.c | 31 + src/libsystemd/src/sd-journal/audit-type.h | 37 + src/libsystemd/src/sd-journal/catalog.c | 768 + src/libsystemd/src/sd-journal/catalog.h | 36 + src/libsystemd/src/sd-journal/compress.c | 684 + src/libsystemd/src/sd-journal/compress.h | 85 + src/libsystemd/src/sd-journal/fsprg.c | 374 + src/libsystemd/src/sd-journal/fsprg.h | 65 + src/libsystemd/src/sd-journal/gcrypt-util.c | 1 + src/libsystemd/src/sd-journal/gcrypt-util.h | 1 + .../src/sd-journal/journal-authenticate.c | 552 + .../src/sd-journal/journal-authenticate.h | 41 + src/libsystemd/src/sd-journal/journal-def.h | 233 + src/libsystemd/src/sd-journal/journal-file.c | 3619 + src/libsystemd/src/sd-journal/journal-file.h | 266 + src/libsystemd/src/sd-journal/journal-internal.h | 144 + src/libsystemd/src/sd-journal/journal-send.c | 575 + src/libsystemd/src/sd-journal/journal-vacuum.c | 350 + src/libsystemd/src/sd-journal/journal-vacuum.h | 27 + src/libsystemd/src/sd-journal/journal-verify.c | 1309 + src/libsystemd/src/sd-journal/journal-verify.h | 24 + src/libsystemd/src/sd-journal/lookup3.c | 1009 + src/libsystemd/src/sd-journal/lookup3.h | 22 + src/libsystemd/src/sd-journal/mmap-cache.c | 726 + src/libsystemd/src/sd-journal/mmap-cache.h | 49 + src/libsystemd/src/sd-journal/sd-journal.c | 2987 + src/libsystemd/src/sd-login/Makefile | 1 + src/libsystemd/src/sd-login/sd-login.c | 1062 + src/libsystemd/src/sd-login/test-login.c | 264 + src/libsystemd/src/sd-netlink/Makefile | 1 + src/libsystemd/src/sd-netlink/local-addresses.c | 275 + src/libsystemd/src/sd-netlink/local-addresses.h | 36 + src/libsystemd/src/sd-netlink/netlink-internal.h | 137 + src/libsystemd/src/sd-netlink/netlink-message.c | 963 + src/libsystemd/src/sd-netlink/netlink-socket.c | 474 + src/libsystemd/src/sd-netlink/netlink-types.c | 694 + src/libsystemd/src/sd-netlink/netlink-types.h | 95 + src/libsystemd/src/sd-netlink/netlink-util.c | 169 + src/libsystemd/src/sd-netlink/netlink-util.h | 39 + src/libsystemd/src/sd-netlink/rtnl-message.c | 702 + src/libsystemd/src/sd-netlink/sd-netlink.c | 957 + src/libsystemd/src/sd-netlink/sd-netlink.h | 163 + .../src/sd-netlink/test-local-addresses.c | 57 + src/libsystemd/src/sd-netlink/test-netlink.c | 440 + src/libsystemd/src/sd-network/Makefile | 1 + src/libsystemd/src/sd-network/network-util.c | 38 + src/libsystemd/src/sd-network/network-util.h | 24 + src/libsystemd/src/sd-network/sd-network.c | 400 + src/libsystemd/src/sd-network/sd-network.h | 176 + src/libsystemd/src/sd-resolve/Makefile | 1 + src/libsystemd/src/sd-resolve/sd-resolve.c | 1243 + src/libsystemd/src/sd-resolve/sd-resolve.h | 116 + src/libsystemd/src/sd-resolve/test-resolve.c | 114 + src/libsystemd/src/sd-utf8/sd-utf8.c | 35 + src/libsystemd/src/subdir.mk | 29 + src/libsystemd/src/test.mk | 155 + src/libudev/Makefile | 65 +- src/libudev/include/libudev.h | 207 + src/libudev/libudev-device-internal.h | 58 - src/libudev/libudev-device-private.c | 411 - src/libudev/libudev-device.c | 958 - src/libudev/libudev-enumerate.c | 419 - src/libudev/libudev-hwdb.c | 146 - src/libudev/libudev-list.c | 352 - src/libudev/libudev-monitor.c | 845 - src/libudev/libudev-private.h | 150 - src/libudev/libudev-queue.c | 268 - src/libudev/libudev-util.c | 268 - src/libudev/libudev.c | 253 - src/libudev/libudev.h | 207 - src/libudev/libudev.xml | 125 + src/libudev/src/Makefile | 45 + src/libudev/src/libudev-device-internal.h | 59 + src/libudev/src/libudev-device-private.c | 412 + src/libudev/src/libudev-device.c | 960 + src/libudev/src/libudev-enumerate.c | 420 + src/libudev/src/libudev-hwdb.c | 146 + src/libudev/src/libudev-list.c | 353 + src/libudev/src/libudev-monitor.c | 847 + src/libudev/src/libudev-private.h | 150 + src/libudev/src/libudev-queue.c | 269 + src/libudev/src/libudev-util.c | 269 + src/libudev/src/libudev.c | 254 + src/libudev/src/udev.h | 217 + src/libudev/udev_device_get_syspath.xml | 207 + src/libudev/udev_device_has_tag.xml | 163 + src/libudev/udev_device_new_from_syspath.xml | 214 + src/libudev/udev_enumerate_add_match_subsystem.xml | 163 + src/libudev/udev_enumerate_new.xml | 111 + src/libudev/udev_enumerate_scan_devices.xml | 133 + src/libudev/udev_list_entry.xml | 123 + src/libudev/udev_monitor_filter_update.xml | 122 + src/libudev/udev_monitor_new_from_netlink.xml | 113 + src/libudev/udev_monitor_receive_device.xml | 137 + src/libudev/udev_new.xml | 110 + src/locale/.gitignore | 1 - src/locale/Makefile | 1 - src/locale/kbd-model-map | 68 - src/locale/keymap-util.c | 724 - src/locale/keymap-util.h | 46 - src/locale/language-fallback-map | 13 - src/locale/localectl.c | 683 - src/locale/localed.c | 710 - src/locale/org.freedesktop.locale1.conf | 27 - src/locale/org.freedesktop.locale1.policy.in | 40 - src/locale/org.freedesktop.locale1.service | 12 - src/locale/test-keymap-util.c | 220 - src/login/.gitignore | 6 - src/login/70-power-switch.rules | 18 - src/login/70-uaccess.rules | 81 - src/login/71-seat.rules.in | 54 - src/login/73-seat-late.rules.in | 17 - src/login/Makefile | 1 - src/login/inhibit.c | 292 - src/login/loginctl.c | 1587 - src/login/logind-acl.c | 292 - src/login/logind-acl.h | 56 - src/login/logind-action.c | 178 - src/login/logind-action.h | 49 - src/login/logind-button.c | 288 - src/login/logind-button.h | 44 - src/login/logind-core.c | 558 - src/login/logind-dbus.c | 3169 - src/login/logind-device.c | 124 - src/login/logind-device.h | 43 - src/login/logind-gperf.gperf | 39 - src/login/logind-inhibit.c | 485 - src/login/logind-inhibit.h | 89 - src/login/logind-seat-dbus.c | 474 - src/login/logind-seat.c | 695 - src/login/logind-seat.h | 95 - src/login/logind-session-dbus.c | 798 - src/login/logind-session-device.c | 480 - src/login/logind-session-device.h | 53 - src/login/logind-session.c | 1273 - src/login/logind-session.h | 185 - src/login/logind-user-dbus.c | 398 - src/login/logind-user.c | 916 - src/login/logind-user.h | 93 - src/login/logind-utmp.c | 183 - src/login/logind.c | 1208 - src/login/logind.conf.in | 37 - src/login/logind.h | 199 - src/login/org.freedesktop.login1.conf | 274 - src/login/org.freedesktop.login1.policy.in | 325 - src/login/org.freedesktop.login1.service | 12 - src/login/pam_systemd.c | 553 - src/login/pam_systemd.sym | 15 - src/login/sysfs-show.c | 189 - src/login/sysfs-show.h | 22 - src/login/systemd-user.m4 | 12 - src/login/test-inhibit.c | 112 - src/login/test-login-shared.c | 39 - src/login/test-login-tables.c | 34 - src/machine-id-setup/Makefile | 1 - src/machine-id-setup/machine-id-setup-main.c | 142 - src/machine/.gitignore | 1 - src/machine/Makefile | 1 - src/machine/image-dbus.c | 422 - src/machine/image-dbus.h | 35 - src/machine/machine-dbus.c | 1472 - src/machine/machine-dbus.h | 44 - src/machine/machine.c | 630 - src/machine/machine.h | 110 - src/machine/machinectl.c | 2875 - src/machine/machined-dbus.c | 1804 - src/machine/machined.c | 415 - src/machine/machined.h | 82 - src/machine/operation.c | 152 - src/machine/operation.h | 49 - src/machine/org.freedesktop.machine1.conf | 198 - src/machine/org.freedesktop.machine1.policy.in | 102 - src/machine/org.freedesktop.machine1.service | 12 - src/machine/test-machine-tables.c | 29 - src/manpages/daemon.xml | 763 + src/manpages/file-hierarchy.xml | 815 + src/manpages/hostname.xml | 98 + src/manpages/locale.conf.xml | 152 + src/manpages/localtime.xml | 103 + src/manpages/machine-id.xml | 146 + src/manpages/machine-info.xml | 185 + src/manpages/os-release.xml | 378 + src/modules-load/Makefile | 1 - src/modules-load/modules-load.c | 283 - src/network/.gitignore | 3 - src/network/Makefile | 1 - src/network/networkctl.c | 1142 - src/network/networkd-address-pool.c | 171 - src/network/networkd-address-pool.h | 44 - src/network/networkd-address.c | 863 - src/network/networkd-address.h | 79 - src/network/networkd-brvlan.c | 329 - src/network/networkd-brvlan.h | 29 - src/network/networkd-conf.c | 111 - src/network/networkd-conf.h | 49 - src/network/networkd-dhcp4.c | 657 - src/network/networkd-dhcp6.c | 265 - src/network/networkd-fdb.c | 253 - src/network/networkd-fdb.h | 47 - src/network/networkd-gperf.gperf | 18 - src/network/networkd-ipv4ll.c | 241 - src/network/networkd-link-bus.c | 140 - src/network/networkd-link.c | 3410 - src/network/networkd-link.h | 213 - src/network/networkd-lldp-tx.c | 416 - src/network/networkd-lldp-tx.h | 35 - src/network/networkd-manager-bus.c | 49 - src/network/networkd-manager.c | 1329 - src/network/networkd-ndisc.c | 664 - src/network/networkd-ndisc.h | 39 - src/network/networkd-netdev-bond.c | 444 - src/network/networkd-netdev-bond.h | 172 - src/network/networkd-netdev-bridge.c | 146 - src/network/networkd-netdev-bridge.h | 37 - src/network/networkd-netdev-dummy.c | 28 - src/network/networkd-netdev-dummy.h | 29 - src/network/networkd-netdev-gperf.gperf | 109 - src/network/networkd-netdev-ipvlan.c | 73 - src/network/networkd-netdev-ipvlan.h | 44 - src/network/networkd-netdev-macvlan.c | 89 - src/network/networkd-netdev-macvlan.h | 49 - src/network/networkd-netdev-tunnel.c | 725 - src/network/networkd-netdev-tunnel.h | 119 - src/network/networkd-netdev-tuntap.c | 183 - src/network/networkd-netdev-tuntap.h | 40 - src/network/networkd-netdev-veth.c | 111 - src/network/networkd-netdev-veth.h | 34 - src/network/networkd-netdev-vlan.c | 78 - src/network/networkd-netdev-vlan.h | 33 - src/network/networkd-netdev-vrf.c | 50 - src/network/networkd-netdev-vrf.h | 33 - src/network/networkd-netdev-vxlan.c | 296 - src/network/networkd-netdev-vxlan.h | 91 - src/network/networkd-netdev.c | 716 - src/network/networkd-netdev.h | 201 - src/network/networkd-network-bus.c | 153 - src/network/networkd-network-gperf.gperf | 127 - src/network/networkd-network.c | 1051 - src/network/networkd-network.h | 247 - src/network/networkd-route.c | 901 - src/network/networkd-route.h | 75 - src/network/networkd-util.c | 101 - src/network/networkd-util.h | 38 - src/network/networkd-wait-online-link.c | 131 - src/network/networkd-wait-online-link.h | 44 - src/network/networkd-wait-online-manager.c | 329 - src/network/networkd-wait-online.c | 166 - src/network/networkd-wait-online.h | 54 - src/network/networkd.c | 139 - src/network/networkd.h | 113 - src/network/org.freedesktop.network1.conf | 42 - src/network/org.freedesktop.network1.service | 12 - src/network/test-network-tables.c | 26 - src/network/test-network.c | 216 - src/network/test-networkd-conf.c | 142 - src/notify/Makefile | 1 - src/notify/notify.c | 203 - src/nspawn/.gitignore | 1 - src/nspawn/Makefile | 1 - src/nspawn/nspawn-cgroup.c | 162 - src/nspawn/nspawn-cgroup.h | 27 - src/nspawn/nspawn-expose-ports.c | 245 - src/nspawn/nspawn-expose-ports.h | 44 - src/nspawn/nspawn-gperf.gperf | 45 - src/nspawn/nspawn-mount.c | 944 - src/nspawn/nspawn-mount.h | 69 - src/nspawn/nspawn-network.c | 694 - src/nspawn/nspawn-network.h | 39 - src/nspawn/nspawn-patch-uid.c | 481 - src/nspawn/nspawn-patch-uid.h | 23 - src/nspawn/nspawn-register.c | 229 - src/nspawn/nspawn-register.h | 29 - src/nspawn/nspawn-seccomp.c | 197 - src/nspawn/nspawn-seccomp.h | 24 - src/nspawn/nspawn-settings.c | 516 - src/nspawn/nspawn-settings.h | 118 - src/nspawn/nspawn-setuid.c | 270 - src/nspawn/nspawn-setuid.h | 22 - src/nspawn/nspawn-stub-pid1.c | 170 - src/nspawn/nspawn-stub-pid1.h | 22 - src/nspawn/nspawn.c | 4188 - src/nspawn/test-patch-uid.c | 61 - src/nss-myhostname/Makefile | 48 +- src/nss-myhostname/nss-myhostname.c | 16 +- src/nss-myhostname/nss-myhostname.xml | 148 + src/nss-mymachines/Makefile | 1 - src/nss-mymachines/nss-mymachines.c | 738 - src/nss-mymachines/nss-mymachines.sym | 21 - src/nss-resolve/Makefile | 1 - src/nss-resolve/nss-resolve.c | 675 - src/nss-resolve/nss-resolve.sym | 19 - src/path/Makefile | 1 - src/path/path.c | 198 - src/quotacheck/Makefile | 1 - src/quotacheck/quotacheck.c | 124 - src/random-seed/Makefile | 1 - src/random-seed/random-seed.c | 176 - src/rc-local-generator/Makefile | 1 - src/rc-local-generator/rc-local-generator.c | 101 - src/remount-fs/Makefile | 1 - src/remount-fs/remount-fs.c | 155 - src/reply-password/Makefile | 1 - src/reply-password/reply-password.c | 96 - src/resolve/.gitignore | 6 - src/resolve/Makefile | 1 - src/resolve/RFCs | 59 - src/resolve/dns-type.c | 332 - src/resolve/dns-type.h | 162 - src/resolve/org.freedesktop.resolve1.conf | 27 - src/resolve/org.freedesktop.resolve1.service | 12 - src/resolve/resolv.conf | 11 - src/resolve/resolve-tool.c | 2007 - src/resolve/resolved-bus.c | 1689 - src/resolve/resolved-bus.h | 25 - src/resolve/resolved-conf.c | 240 - src/resolve/resolved-conf.h | 36 - src/resolve/resolved-def.h | 38 - src/resolve/resolved-dns-answer.c | 858 - src/resolve/resolved-dns-answer.h | 147 - src/resolve/resolved-dns-cache.c | 1064 - src/resolve/resolved-dns-cache.h | 52 - src/resolve/resolved-dns-dnssec.c | 2199 - src/resolve/resolved-dns-dnssec.h | 102 - src/resolve/resolved-dns-packet.c | 2300 - src/resolve/resolved-dns-packet.h | 302 - src/resolve/resolved-dns-query.c | 1121 - src/resolve/resolved-dns-query.h | 141 - src/resolve/resolved-dns-question.c | 468 - src/resolve/resolved-dns-question.h | 73 - src/resolve/resolved-dns-rr.c | 1839 - src/resolve/resolved-dns-rr.h | 353 - src/resolve/resolved-dns-scope.c | 1037 - src/resolve/resolved-dns-scope.h | 111 - src/resolve/resolved-dns-search-domain.c | 227 - src/resolve/resolved-dns-search-domain.h | 74 - src/resolve/resolved-dns-server.c | 777 - src/resolve/resolved-dns-server.h | 147 - src/resolve/resolved-dns-stream.c | 420 - src/resolve/resolved-dns-stream.h | 80 - src/resolve/resolved-dns-stub.c | 572 - src/resolve/resolved-dns-stub.h | 31 - src/resolve/resolved-dns-synthesize.c | 413 - src/resolve/resolved-dns-synthesize.h | 30 - src/resolve/resolved-dns-transaction.c | 3107 - src/resolve/resolved-dns-transaction.h | 179 - src/resolve/resolved-dns-trust-anchor.c | 743 - src/resolve/resolved-dns-trust-anchor.h | 43 - src/resolve/resolved-dns-zone.c | 664 - src/resolve/resolved-dns-zone.h | 81 - src/resolve/resolved-etc-hosts.c | 448 - src/resolve/resolved-etc-hosts.h | 28 - src/resolve/resolved-gperf.gperf | 22 - src/resolve/resolved-link-bus.c | 629 - src/resolve/resolved-link-bus.h | 38 - src/resolve/resolved-link.c | 1115 - src/resolve/resolved-link.h | 118 - src/resolve/resolved-llmnr.c | 471 - src/resolve/resolved-llmnr.h | 32 - src/resolve/resolved-manager.c | 1378 - src/resolve/resolved-manager.h | 183 - src/resolve/resolved-mdns.c | 287 - src/resolve/resolved-mdns.h | 30 - src/resolve/resolved-resolv-conf.c | 266 - src/resolve/resolved-resolv-conf.h | 27 - src/resolve/resolved.c | 120 - src/resolve/resolved.conf.in | 20 - .../test-data/_443._tcp.fedoraproject.org.pkts | Bin 169 -> 0 bytes .../test-data/_openpgpkey.fedoraproject.org.pkts | Bin 986 -> 0 bytes src/resolve/test-data/fake-caa.pkts | Bin 196 -> 0 bytes src/resolve/test-data/fedoraproject.org.pkts | Bin 1483 -> 0 bytes src/resolve/test-data/gandi.net.pkts | Bin 1010 -> 0 bytes src/resolve/test-data/google.com.pkts | Bin 747 -> 0 bytes src/resolve/test-data/kyhwana.org.pkts | Bin 1803 -> 0 bytes src/resolve/test-data/root.pkts | Bin 1061 -> 0 bytes ...sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts | Bin 330 -> 0 bytes src/resolve/test-data/teamits.com.pkts | Bin 1021 -> 0 bytes .../test-data/zbyszek@fedoraproject.org.pkts | Bin 2533 -> 0 bytes src/resolve/test-dns-packet.c | 132 - src/resolve/test-dnssec-complex.c | 236 - src/resolve/test-dnssec.c | 343 - src/resolve/test-resolve-tables.c | 64 - src/rfkill/Makefile | 1 - src/rfkill/rfkill.c | 426 - src/run/Makefile | 1 - src/run/run.c | 1261 - src/shared/Makefile | 1 - src/shared/acl-util.c | 429 - src/shared/acl-util.h | 48 - src/shared/acpi-fpdt.c | 164 - src/shared/acpi-fpdt.h | 24 - src/shared/apparmor-util.c | 39 - src/shared/apparmor-util.h | 24 - src/shared/ask-password-api.c | 734 - src/shared/ask-password-api.h | 38 - src/shared/base-filesystem.c | 129 - src/shared/base-filesystem.h | 24 - src/shared/boot-timestamps.c | 64 - src/shared/boot-timestamps.h | 25 - src/shared/bus-unit-util.c | 1324 - src/shared/bus-unit-util.h | 57 - src/shared/bus-util.c | 1550 - src/shared/bus-util.h | 160 - src/shared/cgroup-show.c | 312 - src/shared/cgroup-show.h | 32 - src/shared/clean-ipc.c | 365 - src/shared/clean-ipc.h | 24 - src/shared/condition.c | 542 - src/shared/condition.h | 94 - src/shared/conf-parser.c | 914 - src/shared/conf-parser.h | 229 - src/shared/dev-setup.c | 73 - src/shared/dev-setup.h | 24 - src/shared/dns-domain.c | 1322 - src/shared/dns-domain.h | 109 - src/shared/dropin.c | 251 - src/shared/dropin.h | 61 - src/shared/efivars.c | 715 - src/shared/efivars.h | 131 - src/shared/fdset.c | 273 - src/shared/fdset.h | 58 - src/shared/firewall-util.c | 357 - src/shared/firewall-util.h | 83 - src/shared/fstab-util.c | 263 - src/shared/fstab-util.h | 52 - src/shared/gcrypt-util.c | 71 - src/shared/gcrypt-util.h | 39 - src/shared/generator.c | 207 - src/shared/generator.h | 40 - src/shared/gpt.h | 66 - src/shared/ima-util.c | 32 - src/shared/ima-util.h | 24 - src/shared/import-util.c | 185 - src/shared/import-util.h | 43 - src/shared/initreq.h | 77 - src/shared/install-printf.c | 133 - src/shared/install-printf.h | 24 - src/shared/install.c | 2957 - src/shared/install.h | 256 - src/shared/linux/auto_dev-ioctl.h | 228 - src/shared/logs-show.c | 1310 - src/shared/logs-show.h | 70 - src/shared/machine-image.c | 818 - src/shared/machine-image.h | 103 - src/shared/machine-pool.c | 426 - src/shared/machine-pool.h | 30 - src/shared/output-mode.c | 37 - src/shared/output-mode.h | 57 - src/shared/pager.c | 226 - src/shared/pager.h | 30 - src/shared/path-lookup.c | 822 - src/shared/path-lookup.h | 76 - src/shared/ptyfwd.c | 484 - src/shared/ptyfwd.h | 48 - src/shared/resolve-util.c | 39 - src/shared/resolve-util.h | 60 - src/shared/seccomp-util.c | 323 - src/shared/seccomp-util.h | 35 - src/shared/sleep-config.c | 278 - src/shared/sleep-config.h | 26 - src/shared/spawn-ask-password-agent.c | 62 - src/shared/spawn-ask-password-agent.h | 23 - src/shared/spawn-polkit-agent.c | 102 - src/shared/spawn-polkit-agent.h | 23 - src/shared/specifier.c | 188 - src/shared/specifier.h | 37 - src/shared/switch-root.c | 156 - src/shared/switch-root.h | 23 - src/shared/sysctl-util.c | 73 - src/shared/sysctl-util.h | 25 - src/shared/test-tables.h | 60 - src/shared/tests.c | 33 - src/shared/tests.h | 22 - src/shared/udev-util.h | 44 - src/shared/uid-range.c | 208 - src/shared/uid-range.h | 33 - src/shared/utmp-wtmp.c | 445 - src/shared/utmp-wtmp.h | 74 - src/shared/vlan-util.c | 69 - src/shared/vlan-util.h | 35 - src/shared/watchdog.c | 164 - src/shared/watchdog.h | 29 - src/sleep/Makefile | 1 - src/sleep/sleep.c | 215 - src/socket-proxy/Makefile | 1 - src/socket-proxy/socket-proxyd.c | 679 - src/stdio-bridge/stdio-bridge.c | 302 - src/sysctl/Makefile | 1 - src/sysctl/sysctl.c | 287 - src/system-update-generator/Makefile | 1 - .../system-update-generator.c | 73 - src/systemctl/Makefile | 1 - src/systemctl/systemctl.c | 8075 -- src/systemctl/systemd-sysv-install.SKELETON | 47 - src/systemd-ask-password/Makefile | 33 + src/systemd-ask-password/ask-password.c | 188 + .../systemd-ask-password.completion.zsh | 12 + src/systemd-ask-password/systemd-ask-password.xml | 227 + src/systemd-cgls/Makefile | 33 + src/systemd-cgls/cgls.c | 288 + src/systemd-cgls/systemd-cgls.completion.bash | 56 + src/systemd-cgls/systemd-cgls.completion.zsh | 12 + src/systemd-cgls/systemd-cgls.xml | 139 + src/systemd-cgroups-agent/Makefile | 33 + src/systemd-cgroups-agent/cgroups-agent.c | 67 + src/systemd-cgtop/Makefile | 33 + src/systemd-cgtop/cgtop.c | 1133 + src/systemd-cgtop/systemd-cgtop.completion.bash | 62 + src/systemd-cgtop/systemd-cgtop.completion.zsh | 17 + src/systemd-cgtop/systemd-cgtop.xml | 377 + src/systemd-cryptsetup/Makefile | 58 + src/systemd-cryptsetup/cryptsetup-generator.c | 509 + src/systemd-cryptsetup/cryptsetup-pre.target | 11 + src/systemd-cryptsetup/cryptsetup.c | 756 + src/systemd-cryptsetup/cryptsetup.target | 10 + src/systemd-cryptsetup/crypttab.xml | 416 + .../systemd-cryptsetup-generator.xml | 193 + .../systemd-cryptsetup@.service.xml | 85 + src/systemd-debug-generator/Makefile | 34 + src/systemd-debug-generator/debug-generator.c | 202 + .../systemd-debug-generator.xml | 95 + src/systemd-getty-generator/Makefile | 33 + src/systemd-getty-generator/getty-generator.c | 233 + .../systemd-getty-generator.xml | 96 + src/systemd-gpt-auto-generator/Makefile | 42 + .../gpt-auto-generator.c | 1040 + .../systemd-gpt-auto-generator.xml | 186 + src/systemd-initctl/Makefile | 33 + src/systemd-initctl/initctl.c | 428 + src/systemd-initctl/systemd-initctl.service.in | 15 + src/systemd-initctl/systemd-initctl.service.xml | 76 + src/systemd-initctl/systemd-initctl.socket | 17 + src/systemd-machine-id-setup/Makefile | 38 + .../machine-id-setup-main.c | 143 + src/systemd-machine-id-setup/machine-id-setup.c | 1 + src/systemd-machine-id-setup/machine-id-setup.h | 1 + .../systemd-machine-id-commit.service.xml | 95 + .../systemd-machine-id-setup.completion.zsh | 8 + .../systemd-machine-id-setup.xml | 184 + src/systemd-nspawn/.gitignore | 1 + src/systemd-nspawn/Makefile | 93 + src/systemd-nspawn/loopback-setup.c | 1 + src/systemd-nspawn/loopback-setup.h | 1 + src/systemd-nspawn/machine-id-setup.c | 1 + src/systemd-nspawn/machine-id-setup.h | 1 + src/systemd-nspawn/mount-setup.c | 1 + src/systemd-nspawn/mount-setup.h | 1 + src/systemd-nspawn/nspawn-cgroup.c | 163 + src/systemd-nspawn/nspawn-cgroup.h | 27 + src/systemd-nspawn/nspawn-expose-ports.c | 245 + src/systemd-nspawn/nspawn-expose-ports.h | 44 + src/systemd-nspawn/nspawn-gperf.gperf | 47 + src/systemd-nspawn/nspawn-mount.c | 946 + src/systemd-nspawn/nspawn-mount.h | 69 + src/systemd-nspawn/nspawn-network.c | 696 + src/systemd-nspawn/nspawn-network.h | 39 + src/systemd-nspawn/nspawn-patch-uid.c | 483 + src/systemd-nspawn/nspawn-patch-uid.h | 23 + src/systemd-nspawn/nspawn-register.c | 230 + src/systemd-nspawn/nspawn-register.h | 29 + src/systemd-nspawn/nspawn-seccomp.c | 198 + src/systemd-nspawn/nspawn-seccomp.h | 24 + src/systemd-nspawn/nspawn-settings.c | 517 + src/systemd-nspawn/nspawn-settings.h | 119 + src/systemd-nspawn/nspawn-setuid.c | 271 + src/systemd-nspawn/nspawn-setuid.h | 22 + src/systemd-nspawn/nspawn-stub-pid1.c | 171 + src/systemd-nspawn/nspawn-stub-pid1.h | 22 + src/systemd-nspawn/nspawn.c | 4190 + src/systemd-nspawn/systemd-nspawn.completion.bash | 155 + src/systemd-nspawn/systemd-nspawn.completion.zsh | 50 + src/systemd-nspawn/systemd-nspawn.tmpfiles | 23 + src/systemd-nspawn/systemd-nspawn.xml | 1102 + src/systemd-nspawn/systemd-nspawn@.service.in | 39 + src/systemd-nspawn/test-patch-uid.c | 62 + src/systemd-rc-local-generator/Makefile | 32 + .../rc-local-generator.c | 101 + src/systemd-remount-fs/Makefile | 35 + src/systemd-remount-fs/mount-setup.c | 1 + src/systemd-remount-fs/mount-setup.h | 1 + src/systemd-remount-fs/remount-fs.c | 156 + .../systemd-remount-fs.service.in | 22 + .../systemd-remount-fs.service.xml | 88 + src/systemd-reply-password/Makefile | 33 + src/systemd-reply-password/reply-password.c | 96 + src/systemd-socket-proxyd/Makefile | 34 + src/systemd-socket-proxyd/socket-proxyd.c | 679 + .../systemd-socket-proxyd.xml | 190 + src/systemd-stdio-bridge/Makefile | 33 + src/systemd-stdio-bridge/stdio-bridge.c | 302 + src/systemd-system-update-generator/Makefile | 33 + .../system-update-generator.c | 73 + .../systemd-system-update-generator.xml | 75 + src/systemd-timesyncd/.gitignore | 2 + src/systemd-timesyncd/90-timesyncd.preset | 8 + src/systemd-timesyncd/Makefile | 65 + src/systemd-timesyncd/systemd-timesyncd.service.in | 35 + .../systemd-timesyncd.service.xml | 108 + src/systemd-timesyncd/systemd-timesyncd.sysusers | 8 + src/systemd-timesyncd/timesyncd-conf.c | 107 + src/systemd-timesyncd/timesyncd-conf.h | 32 + src/systemd-timesyncd/timesyncd-gperf.gperf | 21 + src/systemd-timesyncd/timesyncd-manager.c | 1157 + src/systemd-timesyncd/timesyncd-manager.h | 104 + src/systemd-timesyncd/timesyncd-server.c | 151 + src/systemd-timesyncd/timesyncd-server.h | 65 + src/systemd-timesyncd/timesyncd.c | 165 + src/systemd-timesyncd/timesyncd.conf.in | 16 + src/systemd-timesyncd/timesyncd.conf.xml | 112 + src/systemd-tty-ask-password-agent/Makefile | 33 + .../systemd-ask-password-console.service.xml | 93 + .../systemd-tty-ask-password-agent.completion.zsh | 14 + .../systemd-tty-ask-password-agent.xml | 149 + .../tty-ask-password-agent.c | 875 + src/systemd/Makefile | 1 - src/systemd/_sd-common.h | 83 - src/systemd/sd-bus-protocol.h | 102 - src/systemd/sd-bus-vtable.h | 141 - src/systemd/sd-bus.h | 456 - src/systemd/sd-daemon.h | 294 - src/systemd/sd-device.h | 101 - src/systemd/sd-dhcp-client.h | 158 - src/systemd/sd-dhcp-lease.h | 67 - src/systemd/sd-dhcp-server.h | 65 - src/systemd/sd-dhcp6-client.h | 135 - src/systemd/sd-dhcp6-lease.h | 52 - src/systemd/sd-event.h | 143 - src/systemd/sd-hwdb.h | 49 - src/systemd/sd-id128.h | 115 - src/systemd/sd-ipv4acd.h | 60 - src/systemd/sd-ipv4ll.h | 60 - src/systemd/sd-journal.h | 175 - src/systemd/sd-lldp.h | 182 - src/systemd/sd-login.h | 245 - src/systemd/sd-messages.h | 91 - src/systemd/sd-ndisc.h | 130 - src/systemd/sd-netlink.h | 163 - src/systemd/sd-network.h | 176 - src/systemd/sd-path.h | 91 - src/systemd/sd-resolve.h | 117 - src/systemd/sd-utf8.h | 32 - src/sysusers/Makefile | 1 - src/sysusers/sysusers.c | 1919 - src/sysv-generator/Makefile | 1 - src/sysv-generator/sysv-generator.c | 982 - src/test/Makefile | 36 +- src/test/test-acl-util.c | 10 +- src/test/test-af-list.c | 12 +- src/test/test-alloc-util.c | 6 +- src/test/test-architecture.c | 8 +- src/test/test-arphrd-list.c | 12 +- src/test/test-ask-password-api.c | 6 +- src/test/test-async.c | 8 +- src/test/test-barrier.c | 4 +- src/test/test-bitmap.c | 2 +- src/test/test-boot-timestamps.c | 10 +- src/test/test-btrfs.c | 14 +- src/test/test-calendarspec.c | 8 +- src/test/test-cap-list.c | 12 +- src/test/test-capability.c | 8 +- src/test/test-cgroup-mask.c | 9 +- src/test/test-cgroup-util.c | 21 +- src/test/test-cgroup.c | 8 +- src/test/test-clock.c | 12 +- src/test/test-condition.c | 28 +- src/test/test-conf-files.c | 20 +- src/test/test-conf-parser.c | 12 +- src/test/test-copy.c | 26 +- src/test/test-cpu-set-util.c | 6 +- src/test/test-daemon.c | 4 +- src/test/test-date.c | 6 +- src/test/test-device-nodes.c | 8 +- src/test/test-dns-domain.c | 8 +- src/test/test-ellipsize.c | 10 +- src/test/test-engine.c | 7 +- src/test/test-env-util.c | 8 +- src/test/test-escape.c | 6 +- src/test/test-execute.c | 17 +- src/test/test-extract-word.c | 6 +- src/test/test-fd-util.c | 8 +- src/test/test-fdset.c | 10 +- src/test/test-fileio.c | 24 +- src/test/test-firewall-util.c | 2 +- src/test/test-fs-util.c | 20 +- src/test/test-fstab-util.c | 10 +- src/test/test-glob-util.c | 8 +- src/test/test-hashmap-plain.c | 10 +- src/test/test-hashmap.c | 4 +- src/test/test-helper.h | 4 +- src/test/test-hexdecoct.c | 8 +- src/test/test-hostname-util.c | 10 +- src/test/test-hostname.c | 2 +- src/test/test-id128.c | 20 +- src/test/test-install-root.c | 12 +- src/test/test-install.c | 2 +- src/test/test-io-util.c | 8 +- src/test/test-ipcrm.c | 6 +- src/test/test-libudev.c | 16 +- src/test/test-list.c | 4 +- src/test/test-locale-util.c | 6 +- src/test/test-log.c | 6 +- src/test/test-loopback.c | 2 +- src/test/test-namespace.c | 10 +- src/test/test-netlink-manual.c | 10 +- src/test/test-ns.c | 2 +- src/test/test-nss.c | 28 +- src/test/test-parse-util.c | 4 +- src/test/test-path-lookup.c | 10 +- src/test/test-path-util.c | 18 +- src/test/test-path.c | 23 +- src/test/test-prioq.c | 10 +- src/test/test-proc-cmdline.c | 14 +- src/test/test-process-util.c | 24 +- src/test/test-ratelimit.c | 6 +- src/test/test-replace-var.c | 8 +- src/test/test-rlimit-util.c | 12 +- src/test/test-sched-prio.c | 7 +- src/test/test-selinux.c | 14 +- src/test/test-set.c | 2 +- src/test/test-sigbus.c | 6 +- src/test/test-signal-util.c | 4 +- src/test/test-siphash24.c | 4 +- src/test/test-sizeof.c | 4 +- src/test/test-sleep.c | 8 +- src/test/test-socket-util.c | 18 +- src/test/test-stat-util.c | 10 +- src/test/test-strbuf.c | 8 +- src/test/test-string-util.c | 8 +- src/test/test-strip-tab-ansi.c | 6 +- src/test/test-strv.c | 10 +- src/test/test-strxcpyx.c | 6 +- src/test/test-tables.c | 24 +- src/test/test-terminal-util.c | 12 +- src/test/test-time.c | 4 +- src/test/test-tmpfiles.c | 16 +- src/test/test-udev.c | 14 +- src/test/test-uid-range.c | 8 +- src/test/test-unaligned.c | 6 +- src/test/test-unit-file.c | 33 +- src/test/test-unit-name.c | 23 +- src/test/test-user-util.c | 10 +- src/test/test-utf8.c | 8 +- src/test/test-util.c | 16 +- src/test/test-verbs.c | 6 +- src/test/test-watchdog.c | 4 +- src/test/test-web-util.c | 4 +- src/test/test-xattr-util.c | 12 +- src/test/test-xml.c | 8 +- src/timedate/.gitignore | 1 - src/timedate/Makefile | 1 - src/timedate/org.freedesktop.timedate1.conf | 27 - src/timedate/org.freedesktop.timedate1.policy.in | 62 - src/timedate/org.freedesktop.timedate1.service | 12 - src/timedate/timedatectl.c | 507 - src/timedate/timedated.c | 747 - src/timesync/.gitignore | 2 - src/timesync/Makefile | 1 - src/timesync/timesyncd-conf.c | 106 - src/timesync/timesyncd-conf.h | 31 - src/timesync/timesyncd-gperf.gperf | 19 - src/timesync/timesyncd-manager.c | 1156 - src/timesync/timesyncd-manager.h | 104 - src/timesync/timesyncd-server.c | 150 - src/timesync/timesyncd-server.h | 65 - src/timesync/timesyncd.c | 164 - src/timesync/timesyncd.conf.in | 16 - src/tmpfiles/Makefile | 1 - src/tmpfiles/tmpfiles.c | 2342 - src/tty-ask-password-agent/Makefile | 1 - .../tty-ask-password-agent.c | 875 - src/udev/.gitignore | 4 - src/udev/.vimrc | 4 - src/udev/Makefile | 1 - src/udev/ata_id/Makefile | 1 - src/udev/ata_id/ata_id.c | 674 - src/udev/cdrom_id/Makefile | 1 - src/udev/cdrom_id/cdrom_id.c | 1085 - src/udev/collect/Makefile | 1 - src/udev/collect/collect.c | 491 - src/udev/mtd_probe/Makefile | 1 - src/udev/mtd_probe/mtd_probe.c | 56 - src/udev/mtd_probe/mtd_probe.h | 51 - src/udev/mtd_probe/probe_smartmedia.c | 96 - src/udev/net/.gitignore | 1 - src/udev/net/Makefile | 1 - src/udev/net/ethtool-util.c | 208 - src/udev/net/ethtool-util.h | 54 - src/udev/net/link-config-gperf.gperf | 37 - src/udev/net/link-config.c | 519 - src/udev/net/link-config.h | 98 - src/udev/scsi_id/.gitignore | 1 - src/udev/scsi_id/Makefile | 1 - src/udev/scsi_id/README | 4 - src/udev/scsi_id/scsi.h | 99 - src/udev/scsi_id/scsi_id.c | 625 - src/udev/scsi_id/scsi_id.h | 75 - src/udev/scsi_id/scsi_serial.c | 964 - src/udev/udev-builtin-blkid.c | 337 - src/udev/udev-builtin-btrfs.c | 58 - src/udev/udev-builtin-hwdb.c | 223 - src/udev/udev-builtin-input_id.c | 340 - src/udev/udev-builtin-keyboard.c | 277 - src/udev/udev-builtin-kmod.c | 123 - src/udev/udev-builtin-net_id.c | 623 - src/udev/udev-builtin-net_setup_link.c | 107 - src/udev/udev-builtin-path_id.c | 761 - src/udev/udev-builtin-uaccess.c | 88 - src/udev/udev-builtin-usb_id.c | 473 - src/udev/udev-builtin.c | 142 - src/udev/udev-ctrl.c | 462 - src/udev/udev-event.c | 943 - src/udev/udev-node.c | 375 - src/udev/udev-rules.c | 2577 - src/udev/udev-watch.c | 152 - src/udev/udev.conf | 3 - src/udev/udev.h | 216 - src/udev/udev.pc.in | 5 - src/udev/udevadm-control.c | 163 - src/udev/udevadm-hwdb.c | 698 - src/udev/udevadm-info.c | 480 - src/udev/udevadm-monitor.c | 281 - src/udev/udevadm-settle.c | 162 - src/udev/udevadm-test-builtin.c | 112 - src/udev/udevadm-test.c | 160 - src/udev/udevadm-trigger.c | 286 - src/udev/udevadm-util.c | 50 - src/udev/udevadm-util.h | 24 - src/udev/udevadm.c | 137 - src/udev/udevd.c | 1764 - src/udev/v4l_id/Makefile | 1 - src/udev/v4l_id/v4l_id.c | 86 - src/update-done/Makefile | 1 - src/update-done/update-done.c | 115 - src/update-utmp/Makefile | 1 - src/update-utmp/update-utmp.c | 283 - src/user-sessions/Makefile | 1 - src/user-sessions/user-sessions.c | 84 - src/vconsole/.gitignore | 1 - src/vconsole/90-vconsole.rules.in | 10 - src/vconsole/Makefile | 1 - src/vconsole/vconsole-setup.c | 332 - src/zsh-completion/_sd_hosts_or_user_at_host | 5 + src/zsh-completion/_sd_machines | 13 + src/zsh-completion/_sd_outputmodes | 5 + src/zsh-completion/_sd_unit_files | 9 + 3078 files changed, 694793 insertions(+), 386493 deletions(-) delete mode 120000 src/ac-power/Makefile delete mode 100644 src/ac-power/ac-power.c delete mode 120000 src/activate/Makefile delete mode 100644 src/activate/activate.c delete mode 100644 src/analyze/.gitignore delete mode 120000 src/analyze/Makefile delete mode 100644 src/analyze/analyze-verify.c delete mode 100644 src/analyze/analyze-verify.h delete mode 100644 src/analyze/analyze.c delete mode 120000 src/ask-password/Makefile delete mode 100644 src/ask-password/ask-password.c delete mode 120000 src/backlight/Makefile delete mode 100644 src/backlight/backlight.c delete mode 100644 src/basic/.gitignore delete mode 120000 src/basic/Makefile delete mode 100644 src/basic/MurmurHash2.c delete mode 100644 src/basic/MurmurHash2.h delete mode 100644 src/basic/af-list.c delete mode 100644 src/basic/af-list.h delete mode 100644 src/basic/alloc-util.c delete mode 100644 src/basic/alloc-util.h delete mode 100644 src/basic/architecture.c delete mode 100644 src/basic/architecture.h delete mode 100644 src/basic/arphrd-list.c delete mode 100644 src/basic/arphrd-list.h delete mode 100644 src/basic/async.c delete mode 100644 src/basic/async.h delete mode 100644 src/basic/audit-util.c delete mode 100644 src/basic/audit-util.h delete mode 100644 src/basic/barrier.c delete mode 100644 src/basic/barrier.h delete mode 100644 src/basic/bitmap.c delete mode 100644 src/basic/bitmap.h delete mode 100644 src/basic/blkid-util.h delete mode 100644 src/basic/btrfs-ctree.h delete mode 100644 src/basic/btrfs-util.c delete mode 100644 src/basic/btrfs-util.h delete mode 100644 src/basic/build.h delete mode 100644 src/basic/bus-label.c delete mode 100644 src/basic/bus-label.h delete mode 100644 src/basic/calendarspec.c delete mode 100644 src/basic/calendarspec.h delete mode 100644 src/basic/cap-list.c delete mode 100644 src/basic/cap-list.h delete mode 100644 src/basic/capability-util.c delete mode 100644 src/basic/capability-util.h delete mode 100644 src/basic/cgroup-util.c delete mode 100644 src/basic/cgroup-util.h delete mode 100644 src/basic/chattr-util.c delete mode 100644 src/basic/chattr-util.h delete mode 100644 src/basic/clock-util.c delete mode 100644 src/basic/clock-util.h delete mode 100644 src/basic/conf-files.c delete mode 100644 src/basic/conf-files.h delete mode 100644 src/basic/copy.c delete mode 100644 src/basic/copy.h delete mode 100644 src/basic/cpu-set-util.c delete mode 100644 src/basic/cpu-set-util.h delete mode 100644 src/basic/def.h delete mode 100644 src/basic/device-nodes.c delete mode 100644 src/basic/device-nodes.h delete mode 100644 src/basic/dirent-util.c delete mode 100644 src/basic/dirent-util.h delete mode 100644 src/basic/env-util.c delete mode 100644 src/basic/env-util.h delete mode 100644 src/basic/errno-list.c delete mode 100644 src/basic/errno-list.h delete mode 100644 src/basic/escape.c delete mode 100644 src/basic/escape.h delete mode 100644 src/basic/ether-addr-util.c delete mode 100644 src/basic/ether-addr-util.h delete mode 100644 src/basic/exit-status.c delete mode 100644 src/basic/exit-status.h delete mode 100644 src/basic/extract-word.c delete mode 100644 src/basic/extract-word.h delete mode 100644 src/basic/fd-util.c delete mode 100644 src/basic/fd-util.h delete mode 100644 src/basic/fileio-label.c delete mode 100644 src/basic/fileio-label.h delete mode 100644 src/basic/fileio.c delete mode 100644 src/basic/fileio.h delete mode 100644 src/basic/formats-util.h delete mode 100644 src/basic/fs-util.c delete mode 100644 src/basic/fs-util.h delete mode 100644 src/basic/glob-util.c delete mode 100644 src/basic/glob-util.h delete mode 100644 src/basic/gunicode.c delete mode 100644 src/basic/gunicode.h delete mode 100644 src/basic/hash-funcs.c delete mode 100644 src/basic/hash-funcs.h delete mode 100644 src/basic/hashmap.c delete mode 100644 src/basic/hashmap.h delete mode 100644 src/basic/hexdecoct.c delete mode 100644 src/basic/hexdecoct.h delete mode 100644 src/basic/hostname-util.c delete mode 100644 src/basic/hostname-util.h delete mode 100644 src/basic/in-addr-util.c delete mode 100644 src/basic/in-addr-util.h delete mode 100644 src/basic/io-util.c delete mode 100644 src/basic/io-util.h delete mode 100644 src/basic/ioprio.h delete mode 100644 src/basic/label.c delete mode 100644 src/basic/label.h delete mode 100644 src/basic/list.h delete mode 100644 src/basic/locale-util.c delete mode 100644 src/basic/locale-util.h delete mode 100644 src/basic/lockfile-util.c delete mode 100644 src/basic/lockfile-util.h delete mode 100644 src/basic/log.c delete mode 100644 src/basic/log.h delete mode 100644 src/basic/login-util.c delete mode 100644 src/basic/login-util.h delete mode 100644 src/basic/macro.h delete mode 100644 src/basic/memfd-util.c delete mode 100644 src/basic/memfd-util.h delete mode 100644 src/basic/mempool.c delete mode 100644 src/basic/mempool.h delete mode 100644 src/basic/missing.h delete mode 100644 src/basic/missing_syscall.h delete mode 100644 src/basic/mkdir-label.c delete mode 100644 src/basic/mkdir.c delete mode 100644 src/basic/mkdir.h delete mode 100644 src/basic/mount-util.c delete mode 100644 src/basic/mount-util.h delete mode 100644 src/basic/nss-util.h delete mode 100644 src/basic/ordered-set.c delete mode 100644 src/basic/ordered-set.h delete mode 100644 src/basic/parse-util.c delete mode 100644 src/basic/parse-util.h delete mode 100644 src/basic/path-util.c delete mode 100644 src/basic/path-util.h delete mode 100644 src/basic/prioq.c delete mode 100644 src/basic/prioq.h delete mode 100644 src/basic/proc-cmdline.c delete mode 100644 src/basic/proc-cmdline.h delete mode 100644 src/basic/process-util.c delete mode 100644 src/basic/process-util.h delete mode 100644 src/basic/random-util.c delete mode 100644 src/basic/random-util.h delete mode 100644 src/basic/ratelimit.c delete mode 100644 src/basic/ratelimit.h delete mode 100644 src/basic/raw-clone.h delete mode 100644 src/basic/refcnt.h delete mode 100644 src/basic/replace-var.c delete mode 100644 src/basic/replace-var.h delete mode 100644 src/basic/rlimit-util.c delete mode 100644 src/basic/rlimit-util.h delete mode 100644 src/basic/rm-rf.c delete mode 100644 src/basic/rm-rf.h delete mode 100644 src/basic/securebits.h delete mode 100644 src/basic/selinux-util.c delete mode 100644 src/basic/selinux-util.h delete mode 100644 src/basic/set.h delete mode 100644 src/basic/sigbus.c delete mode 100644 src/basic/sigbus.h delete mode 100644 src/basic/signal-util.c delete mode 100644 src/basic/signal-util.h delete mode 100644 src/basic/siphash24.c delete mode 100644 src/basic/siphash24.h delete mode 100644 src/basic/smack-util.c delete mode 100644 src/basic/smack-util.h delete mode 100644 src/basic/socket-label.c delete mode 100644 src/basic/socket-util.c delete mode 100644 src/basic/socket-util.h delete mode 100644 src/basic/sparse-endian.h delete mode 100644 src/basic/special.h delete mode 100644 src/basic/stat-util.c delete mode 100644 src/basic/stat-util.h delete mode 100644 src/basic/stdio-util.h delete mode 100644 src/basic/strbuf.c delete mode 100644 src/basic/strbuf.h delete mode 100644 src/basic/string-table.c delete mode 100644 src/basic/string-table.h delete mode 100644 src/basic/string-util.c delete mode 100644 src/basic/string-util.h delete mode 100644 src/basic/strv.c delete mode 100644 src/basic/strv.h delete mode 100644 src/basic/strxcpyx.c delete mode 100644 src/basic/strxcpyx.h delete mode 100644 src/basic/syslog-util.c delete mode 100644 src/basic/syslog-util.h delete mode 100644 src/basic/terminal-util.c delete mode 100644 src/basic/terminal-util.h delete mode 100644 src/basic/time-util.c delete mode 100644 src/basic/time-util.h delete mode 100644 src/basic/umask-util.h delete mode 100644 src/basic/unaligned.h delete mode 100644 src/basic/unit-name.c delete mode 100644 src/basic/unit-name.h delete mode 100644 src/basic/user-util.c delete mode 100644 src/basic/user-util.h delete mode 100644 src/basic/utf8.c delete mode 100644 src/basic/utf8.h delete mode 100644 src/basic/util.c delete mode 100644 src/basic/util.h delete mode 100644 src/basic/verbs.c delete mode 100644 src/basic/verbs.h delete mode 100644 src/basic/virt.c delete mode 100644 src/basic/virt.h delete mode 100644 src/basic/web-util.c delete mode 100644 src/basic/web-util.h delete mode 100644 src/basic/xattr-util.c delete mode 100644 src/basic/xattr-util.h delete mode 100644 src/basic/xml.c delete mode 100644 src/basic/xml.h delete mode 120000 src/binfmt/Makefile delete mode 100644 src/binfmt/binfmt.c delete mode 120000 src/boot/Makefile delete mode 100644 src/boot/bootctl.c delete mode 100644 src/boot/efi/.gitignore delete mode 100644 src/boot/efi/boot.c delete mode 100644 src/boot/efi/console.c delete mode 100644 src/boot/efi/console.h delete mode 100644 src/boot/efi/disk.c delete mode 100644 src/boot/efi/disk.h delete mode 100644 src/boot/efi/graphics.c delete mode 100644 src/boot/efi/graphics.h delete mode 100644 src/boot/efi/linux.c delete mode 100644 src/boot/efi/linux.h delete mode 100644 src/boot/efi/measure.c delete mode 100644 src/boot/efi/measure.h delete mode 100644 src/boot/efi/pefile.c delete mode 100644 src/boot/efi/pefile.h delete mode 100644 src/boot/efi/splash.c delete mode 100644 src/boot/efi/splash.h delete mode 100644 src/boot/efi/stub.c delete mode 100644 src/boot/efi/util.c delete mode 100644 src/boot/efi/util.h create mode 100644 src/busctl/Makefile create mode 100644 src/busctl/busctl-introspect.c create mode 100644 src/busctl/busctl-introspect.h create mode 100644 src/busctl/busctl.c create mode 100644 src/busctl/busctl.completion.bash create mode 100644 src/busctl/busctl.completion.zsh create mode 100644 src/busctl/busctl.xml delete mode 120000 src/cgls/Makefile delete mode 100644 src/cgls/cgls.c delete mode 120000 src/cgroups-agent/Makefile delete mode 100644 src/cgroups-agent/cgroups-agent.c delete mode 120000 src/cgtop/Makefile delete mode 100644 src/cgtop/cgtop.c delete mode 100644 src/core/.gitignore delete mode 120000 src/core/Makefile delete mode 100644 src/core/audit-fd.c delete mode 100644 src/core/audit-fd.h delete mode 100644 src/core/automount.c delete mode 100644 src/core/automount.h delete mode 100644 src/core/bus-policy.c delete mode 100644 src/core/bus-policy.h delete mode 100644 src/core/busname.c delete mode 100644 src/core/busname.h delete mode 100644 src/core/cgroup.c delete mode 100644 src/core/cgroup.h delete mode 100644 src/core/dbus-automount.c delete mode 100644 src/core/dbus-automount.h delete mode 100644 src/core/dbus-busname.c delete mode 100644 src/core/dbus-busname.h delete mode 100644 src/core/dbus-cgroup.c delete mode 100644 src/core/dbus-cgroup.h delete mode 100644 src/core/dbus-device.c delete mode 100644 src/core/dbus-device.h delete mode 100644 src/core/dbus-execute.c delete mode 100644 src/core/dbus-execute.h delete mode 100644 src/core/dbus-job.c delete mode 100644 src/core/dbus-job.h delete mode 100644 src/core/dbus-kill.c delete mode 100644 src/core/dbus-kill.h delete mode 100644 src/core/dbus-manager.c delete mode 100644 src/core/dbus-manager.h delete mode 100644 src/core/dbus-mount.c delete mode 100644 src/core/dbus-mount.h delete mode 100644 src/core/dbus-path.c delete mode 100644 src/core/dbus-path.h delete mode 100644 src/core/dbus-scope.c delete mode 100644 src/core/dbus-scope.h delete mode 100644 src/core/dbus-service.c delete mode 100644 src/core/dbus-service.h delete mode 100644 src/core/dbus-slice.c delete mode 100644 src/core/dbus-slice.h delete mode 100644 src/core/dbus-socket.c delete mode 100644 src/core/dbus-socket.h delete mode 100644 src/core/dbus-swap.c delete mode 100644 src/core/dbus-swap.h delete mode 100644 src/core/dbus-target.c delete mode 100644 src/core/dbus-target.h delete mode 100644 src/core/dbus-timer.c delete mode 100644 src/core/dbus-timer.h delete mode 100644 src/core/dbus-unit.c delete mode 100644 src/core/dbus-unit.h delete mode 100644 src/core/dbus.c delete mode 100644 src/core/dbus.h delete mode 100644 src/core/device.c delete mode 100644 src/core/device.h delete mode 100644 src/core/execute.c delete mode 100644 src/core/execute.h delete mode 100644 src/core/failure-action.c delete mode 100644 src/core/failure-action.h delete mode 100644 src/core/hostname-setup.c delete mode 100644 src/core/hostname-setup.h delete mode 100644 src/core/ima-setup.c delete mode 100644 src/core/ima-setup.h delete mode 100644 src/core/job.c delete mode 100644 src/core/job.h delete mode 100644 src/core/kill.c delete mode 100644 src/core/kill.h delete mode 100644 src/core/killall.c delete mode 100644 src/core/killall.h delete mode 100644 src/core/kmod-setup.c delete mode 100644 src/core/kmod-setup.h delete mode 100644 src/core/load-dropin.c delete mode 100644 src/core/load-dropin.h delete mode 100644 src/core/load-fragment-gperf.gperf.m4 delete mode 100644 src/core/load-fragment.c delete mode 100644 src/core/load-fragment.h delete mode 100644 src/core/locale-setup.c delete mode 100644 src/core/locale-setup.h delete mode 100644 src/core/loopback-setup.c delete mode 100644 src/core/loopback-setup.h delete mode 100644 src/core/machine-id-setup.c delete mode 100644 src/core/machine-id-setup.h delete mode 100644 src/core/macros.systemd.in delete mode 100644 src/core/main.c delete mode 100644 src/core/manager.c delete mode 100644 src/core/manager.h delete mode 100644 src/core/mount-setup.c delete mode 100644 src/core/mount-setup.h delete mode 100644 src/core/mount.c delete mode 100644 src/core/mount.h delete mode 100644 src/core/namespace.c delete mode 100644 src/core/namespace.h delete mode 100644 src/core/org.freedesktop.systemd1.conf delete mode 100644 src/core/org.freedesktop.systemd1.policy.in.in delete mode 100644 src/core/org.freedesktop.systemd1.service delete mode 100644 src/core/path.c delete mode 100644 src/core/path.h delete mode 100644 src/core/scope.c delete mode 100644 src/core/scope.h delete mode 100644 src/core/selinux-access.c delete mode 100644 src/core/selinux-access.h delete mode 100644 src/core/selinux-setup.c delete mode 100644 src/core/selinux-setup.h delete mode 100644 src/core/service.c delete mode 100644 src/core/service.h delete mode 100644 src/core/show-status.c delete mode 100644 src/core/show-status.h delete mode 100644 src/core/shutdown.c delete mode 100644 src/core/slice.c delete mode 100644 src/core/slice.h delete mode 100644 src/core/smack-setup.c delete mode 100644 src/core/smack-setup.h delete mode 100644 src/core/socket.c delete mode 100644 src/core/socket.h delete mode 100644 src/core/swap.c delete mode 100644 src/core/swap.h delete mode 100644 src/core/system.conf delete mode 100644 src/core/systemd.pc.in delete mode 100644 src/core/target.c delete mode 100644 src/core/target.h delete mode 100644 src/core/timer.c delete mode 100644 src/core/timer.h delete mode 100644 src/core/transaction.c delete mode 100644 src/core/transaction.h delete mode 100644 src/core/triggers.systemd.in delete mode 100644 src/core/umount.c delete mode 100644 src/core/umount.h delete mode 100644 src/core/unit-printf.c delete mode 100644 src/core/unit-printf.h delete mode 100644 src/core/unit.c delete mode 100644 src/core/unit.h delete mode 100644 src/core/user.conf delete mode 120000 src/coredump/Makefile delete mode 100644 src/coredump/coredump-vacuum.c delete mode 100644 src/coredump/coredump-vacuum.h delete mode 100644 src/coredump/coredump.c delete mode 100644 src/coredump/coredump.conf delete mode 100644 src/coredump/coredumpctl.c delete mode 100644 src/coredump/stacktrace.c delete mode 100644 src/coredump/stacktrace.h delete mode 100644 src/coredump/test-coredump-vacuum.c delete mode 120000 src/cryptsetup/Makefile delete mode 100644 src/cryptsetup/cryptsetup-generator.c delete mode 100644 src/cryptsetup/cryptsetup.c delete mode 120000 src/dbus1-generator/Makefile delete mode 120000 src/debug-generator/Makefile delete mode 100644 src/debug-generator/debug-generator.c delete mode 120000 src/delta/Makefile delete mode 100644 src/delta/delta.c delete mode 120000 src/detect-virt/Makefile delete mode 100644 src/detect-virt/detect-virt.c delete mode 120000 src/escape/Makefile delete mode 100644 src/escape/escape.c delete mode 120000 src/firstboot/Makefile delete mode 100644 src/firstboot/firstboot.c delete mode 120000 src/fsck/Makefile delete mode 100644 src/fsck/fsck.c delete mode 120000 src/fstab-generator/Makefile delete mode 100644 src/fstab-generator/fstab-generator.c delete mode 120000 src/getty-generator/Makefile delete mode 100644 src/getty-generator/getty-generator.c delete mode 120000 src/gpt-auto-generator/Makefile delete mode 100644 src/gpt-auto-generator/gpt-auto-generator.c create mode 100644 src/grp-boot/Makefile create mode 100644 src/grp-boot/bootctl/Makefile create mode 100644 src/grp-boot/bootctl/bootctl.c create mode 100644 src/grp-boot/bootctl/bootctl.completion.bash create mode 100644 src/grp-boot/bootctl/bootctl.completion.zsh create mode 100644 src/grp-boot/bootctl/bootctl.xml create mode 100644 src/grp-boot/kernel-install/50-depmod.install create mode 100644 src/grp-boot/kernel-install/90-loaderentry.install create mode 100644 src/grp-boot/kernel-install/Makefile create mode 100644 src/grp-boot/kernel-install/kernel-install create mode 100644 src/grp-boot/kernel-install/kernel-install.completion.bash create mode 100644 src/grp-boot/kernel-install/kernel-install.completion.zsh create mode 100644 src/grp-boot/kernel-install/kernel-install.xml create mode 100644 src/grp-boot/systemd-boot/.gitignore create mode 100644 src/grp-boot/systemd-boot/Makefile create mode 100644 src/grp-boot/systemd-boot/boot.c create mode 100644 src/grp-boot/systemd-boot/console.c create mode 100644 src/grp-boot/systemd-boot/console.h create mode 100644 src/grp-boot/systemd-boot/disk.c create mode 100644 src/grp-boot/systemd-boot/disk.h create mode 100644 src/grp-boot/systemd-boot/graphics.c create mode 100644 src/grp-boot/systemd-boot/graphics.h create mode 100644 src/grp-boot/systemd-boot/linux.c create mode 100644 src/grp-boot/systemd-boot/linux.h create mode 100644 src/grp-boot/systemd-boot/measure.c create mode 100644 src/grp-boot/systemd-boot/measure.h create mode 100644 src/grp-boot/systemd-boot/pefile.c create mode 100644 src/grp-boot/systemd-boot/pefile.h create mode 100644 src/grp-boot/systemd-boot/splash.c create mode 100644 src/grp-boot/systemd-boot/splash.h create mode 100644 src/grp-boot/systemd-boot/stub.c create mode 100755 src/grp-boot/systemd-boot/test-efi-create-disk.sh create mode 100644 src/grp-boot/systemd-boot/util.c create mode 100644 src/grp-boot/systemd-boot/util.h create mode 100644 src/grp-coredump/Makefile create mode 100644 src/grp-coredump/coredumpctl/Makefile create mode 100644 src/grp-coredump/coredumpctl/coredumpctl.c create mode 100644 src/grp-coredump/coredumpctl/coredumpctl.completion.bash create mode 100644 src/grp-coredump/coredumpctl/coredumpctl.completion.zsh create mode 100644 src/grp-coredump/coredumpctl/coredumpctl.xml create mode 100644 src/grp-coredump/systemd-coredump/50-coredump.sysctl.in create mode 100644 src/grp-coredump/systemd-coredump/Makefile create mode 100644 src/grp-coredump/systemd-coredump/coredump-vacuum.c create mode 100644 src/grp-coredump/systemd-coredump/coredump-vacuum.h create mode 100644 src/grp-coredump/systemd-coredump/coredump.c create mode 100644 src/grp-coredump/systemd-coredump/coredump.conf create mode 100644 src/grp-coredump/systemd-coredump/coredump.conf.xml create mode 100644 src/grp-coredump/systemd-coredump/stacktrace.c create mode 100644 src/grp-coredump/systemd-coredump/stacktrace.h create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.socket create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.sysusers create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump.xml create mode 100644 src/grp-coredump/systemd-coredump/systemd-coredump@.service.in create mode 100644 src/grp-coredump/systemd-coredump/test-coredump-vacuum.c create mode 100644 src/grp-hostname/Makefile create mode 100644 src/grp-hostname/hostnamectl/Makefile create mode 100644 src/grp-hostname/hostnamectl/hostnamectl.c create mode 100644 src/grp-hostname/hostnamectl/hostnamectl.completion.bash create mode 100644 src/grp-hostname/hostnamectl/hostnamectl.completion.zsh create mode 100644 src/grp-hostname/hostnamectl/hostnamectl.xml create mode 100644 src/grp-hostname/systemd-hostnamed/.gitignore create mode 100644 src/grp-hostname/systemd-hostnamed/Makefile create mode 100644 src/grp-hostname/systemd-hostnamed/hostnamed.c create mode 100644 src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.conf create mode 100644 src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.policy.in create mode 100644 src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.service create mode 100644 src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in create mode 100644 src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.xml create mode 100644 src/grp-initprogs/Makefile create mode 100644 src/grp-initprogs/grp-sleep/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/systemd-hibernate-resume-generator.xml create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.xml create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/Makefile create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/sleep.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.conf.xml create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.xml create mode 100644 src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in create mode 100644 src/grp-initprogs/systemd-backlight/Makefile create mode 100644 src/grp-initprogs/systemd-backlight/backlight.c create mode 100644 src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in create mode 100644 src/grp-initprogs/systemd-backlight/systemd-backlight@.service.xml create mode 100644 src/grp-initprogs/systemd-binfmt/Makefile create mode 100644 src/grp-initprogs/systemd-binfmt/binfmt.c create mode 100644 src/grp-initprogs/systemd-binfmt/binfmt.d.xml create mode 100644 src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount create mode 100644 src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount create mode 100644 src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in create mode 100644 src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.xml create mode 100644 src/grp-initprogs/systemd-detect-virt/Makefile create mode 100644 src/grp-initprogs/systemd-detect-virt/detect-virt.c create mode 100644 src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash create mode 100644 src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh create mode 100644 src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.xml create mode 100644 src/grp-initprogs/systemd-firstboot/Makefile create mode 100644 src/grp-initprogs/systemd-firstboot/firstboot.c create mode 100644 src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in create mode 100644 src/grp-initprogs/systemd-firstboot/systemd-firstboot.xml create mode 100644 src/grp-initprogs/systemd-fsck/Makefile create mode 100644 src/grp-initprogs/systemd-fsck/fsck.c create mode 100644 src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in create mode 100644 src/grp-initprogs/systemd-fsck/systemd-fsck@.service.xml create mode 100644 src/grp-initprogs/systemd-modules-load/Makefile create mode 100644 src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in create mode 100644 src/grp-initprogs/systemd-modules-load/modules-load.c create mode 100644 src/grp-initprogs/systemd-modules-load/modules-load.d.xml create mode 100644 src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in create mode 100644 src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.xml create mode 100644 src/grp-initprogs/systemd-quotacheck/Makefile create mode 100644 src/grp-initprogs/systemd-quotacheck/quotacheck.c create mode 100644 src/grp-initprogs/systemd-quotacheck/quotaon.service.in create mode 100644 src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in create mode 100644 src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.xml create mode 100644 src/grp-initprogs/systemd-random-seed/Makefile create mode 100644 src/grp-initprogs/systemd-random-seed/random-seed.c create mode 100644 src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in create mode 100644 src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.xml create mode 100644 src/grp-initprogs/systemd-rfkill/Makefile create mode 100644 src/grp-initprogs/systemd-rfkill/rfkill.c create mode 100644 src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in create mode 100644 src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.xml create mode 100644 src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket create mode 100644 src/grp-initprogs/systemd-sysctl/50-default.sysctl create mode 100644 src/grp-initprogs/systemd-sysctl/Makefile create mode 100644 src/grp-initprogs/systemd-sysctl/sysctl.c create mode 100644 src/grp-initprogs/systemd-sysctl/sysctl.d.xml create mode 100644 src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in create mode 100644 src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.xml create mode 100644 src/grp-initprogs/systemd-sysusers/.gitignore create mode 100644 src/grp-initprogs/systemd-sysusers/Makefile create mode 100644 src/grp-initprogs/systemd-sysusers/basic.sysusers.in create mode 100644 src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in create mode 100644 src/grp-initprogs/systemd-sysusers/systemd-sysusers.xml create mode 100644 src/grp-initprogs/systemd-sysusers/sysusers.c create mode 100644 src/grp-initprogs/systemd-sysusers/sysusers.d.xml create mode 100644 src/grp-initprogs/systemd-tmpfiles/Makefile create mode 100644 src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 create mode 100644 src/grp-initprogs/systemd-tmpfiles/home.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh create mode 100644 src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.xml create mode 100644 src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/tmpfiles.c create mode 100644 src/grp-initprogs/systemd-tmpfiles/tmpfiles.d.xml create mode 100644 src/grp-initprogs/systemd-tmpfiles/var.tmpfiles create mode 100644 src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles create mode 100644 src/grp-initprogs/systemd-update-done/Makefile create mode 100644 src/grp-initprogs/systemd-update-done/systemd-update-done.service.in create mode 100644 src/grp-initprogs/systemd-update-done/systemd-update-done.service.xml create mode 100644 src/grp-initprogs/systemd-update-done/update-done.c create mode 100644 src/grp-initprogs/systemd-update-utmp/Makefile create mode 100644 src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in create mode 100644 src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.xml create mode 100644 src/grp-initprogs/systemd-update-utmp/update-utmp.c create mode 100644 src/grp-initprogs/systemd-user-sessions/Makefile create mode 100644 src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in create mode 100644 src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.xml create mode 100644 src/grp-initprogs/systemd-user-sessions/user-sessions.c create mode 100644 src/grp-initprogs/systemd-vconsole-setup/.gitignore create mode 100644 src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in create mode 100644 src/grp-initprogs/systemd-vconsole-setup/Makefile create mode 100644 src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in create mode 100644 src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.xml create mode 100644 src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c create mode 100644 src/grp-initprogs/systemd-vconsole-setup/vconsole.conf.xml create mode 100644 src/grp-journal/.gitignore create mode 100644 src/grp-journal/90-journald.preset create mode 100644 src/grp-journal/Makefile create mode 100644 src/grp-journal/README.in create mode 100644 src/grp-journal/catalog/.gitignore create mode 100644 src/grp-journal/catalog/systemd.be.catalog.in create mode 100644 src/grp-journal/catalog/systemd.be@latin.catalog.in create mode 100644 src/grp-journal/catalog/systemd.bg.catalog.in create mode 100644 src/grp-journal/catalog/systemd.catalog.in create mode 100644 src/grp-journal/catalog/systemd.da.catalog.in create mode 100644 src/grp-journal/catalog/systemd.fr.catalog.in create mode 100644 src/grp-journal/catalog/systemd.hr.catalog.in create mode 100644 src/grp-journal/catalog/systemd.hu.catalog.in create mode 100644 src/grp-journal/catalog/systemd.it.catalog.in create mode 100644 src/grp-journal/catalog/systemd.ko.catalog.in create mode 100644 src/grp-journal/catalog/systemd.pl.catalog.in create mode 100644 src/grp-journal/catalog/systemd.pt_BR.catalog.in create mode 100644 src/grp-journal/catalog/systemd.ru.catalog.in create mode 100644 src/grp-journal/catalog/systemd.sr.catalog.in create mode 100644 src/grp-journal/catalog/systemd.zh_CN.catalog.in create mode 100644 src/grp-journal/catalog/systemd.zh_TW.catalog.in create mode 100644 src/grp-journal/grp-remote/.gitignore create mode 100644 src/grp-journal/grp-remote/Makefile create mode 100644 src/grp-journal/grp-remote/browse.html create mode 100755 src/grp-journal/grp-remote/log-generator.py create mode 100644 src/grp-journal/grp-remote/microhttpd-util.c create mode 100644 src/grp-journal/grp-remote/microhttpd-util.h create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/journal-gatewayd.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.xml create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket create mode 100644 src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.sysusers create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/Makefile create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.h create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.h create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.xml create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.h create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers create mode 100644 src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.xml create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/Makefile create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/journal-upload-journal.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.c create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.conf.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.h create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers create mode 100644 src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.xml create mode 100644 src/grp-journal/grp-remote/systemd-remote.tmpfiles create mode 100644 src/grp-journal/journal-nocow.tmpfiles create mode 100644 src/grp-journal/journalctl/Makefile create mode 100644 src/grp-journal/journalctl/journal-qrcode.c create mode 100644 src/grp-journal/journalctl/journal-qrcode.h create mode 100644 src/grp-journal/journalctl/journalctl.c create mode 100644 src/grp-journal/journalctl/journalctl.completion.bash create mode 100644 src/grp-journal/journalctl/journalctl.completion.zsh create mode 100644 src/grp-journal/journalctl/journalctl.xml create mode 100644 src/grp-journal/journalctl/systemd-journal-catalog-update.service.in create mode 100644 src/grp-journal/journalctl/systemd-journal-flush.service.in create mode 100644 src/grp-journal/libjournal-core/.gitignore create mode 100644 src/grp-journal/libjournal-core/Makefile create mode 100644 src/grp-journal/libjournal-core/journald-audit.c create mode 100644 src/grp-journal/libjournal-core/journald-audit.h create mode 100644 src/grp-journal/libjournal-core/journald-console.c create mode 100644 src/grp-journal/libjournal-core/journald-console.h create mode 100644 src/grp-journal/libjournal-core/journald-gperf.gperf create mode 100644 src/grp-journal/libjournal-core/journald-kmsg.c create mode 100644 src/grp-journal/libjournal-core/journald-kmsg.h create mode 100644 src/grp-journal/libjournal-core/journald-native.c create mode 100644 src/grp-journal/libjournal-core/journald-native.h create mode 100644 src/grp-journal/libjournal-core/journald-rate-limit.c create mode 100644 src/grp-journal/libjournal-core/journald-rate-limit.h create mode 100644 src/grp-journal/libjournal-core/journald-server.c create mode 100644 src/grp-journal/libjournal-core/journald-server.h create mode 100644 src/grp-journal/libjournal-core/journald-stream.c create mode 100644 src/grp-journal/libjournal-core/journald-stream.h create mode 100644 src/grp-journal/libjournal-core/journald-syslog.c create mode 100644 src/grp-journal/libjournal-core/journald-syslog.h create mode 100644 src/grp-journal/libjournal-core/journald-wall.c create mode 100644 src/grp-journal/libjournal-core/journald-wall.h create mode 100644 src/grp-journal/libjournal-core/test-audit-type.c create mode 100644 src/grp-journal/libjournal-core/test-catalog.c create mode 100644 src/grp-journal/libjournal-core/test-compress-benchmark.c create mode 100644 src/grp-journal/libjournal-core/test-compress.c create mode 100644 src/grp-journal/libjournal-core/test-journal-enum.c create mode 100644 src/grp-journal/libjournal-core/test-journal-flush.c create mode 100644 src/grp-journal/libjournal-core/test-journal-init.c create mode 100644 src/grp-journal/libjournal-core/test-journal-interleaving.c create mode 100644 src/grp-journal/libjournal-core/test-journal-match.c create mode 100644 src/grp-journal/libjournal-core/test-journal-send.c create mode 100644 src/grp-journal/libjournal-core/test-journal-stream.c create mode 100644 src/grp-journal/libjournal-core/test-journal-syslog.c create mode 100644 src/grp-journal/libjournal-core/test-journal-verify.c create mode 100644 src/grp-journal/libjournal-core/test-journal.c create mode 100644 src/grp-journal/libjournal-core/test-mmap-cache.c create mode 100644 src/grp-journal/systemd-cat/Makefile create mode 100644 src/grp-journal/systemd-cat/cat.c create mode 100644 src/grp-journal/systemd-cat/systemd-cat.completion.bash create mode 100644 src/grp-journal/systemd-cat/systemd-cat.completion.zsh create mode 100644 src/grp-journal/systemd-cat/systemd-cat.xml create mode 100644 src/grp-journal/systemd-journald/Makefile create mode 100644 src/grp-journal/systemd-journald/journald.c create mode 100644 src/grp-journal/systemd-journald/journald.conf create mode 100644 src/grp-journal/systemd-journald/journald.conf.xml create mode 100644 src/grp-journal/systemd-journald/systemd-journald-audit.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald-dev-log.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald.service.in create mode 100644 src/grp-journal/systemd-journald/systemd-journald.service.xml create mode 100644 src/grp-journal/systemd-journald/systemd-journald.socket create mode 100644 src/grp-journal/systemd-journald/systemd-journald.sysusers create mode 100644 src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 create mode 100644 src/grp-locale/Makefile create mode 100644 src/grp-locale/localectl/Makefile create mode 100644 src/grp-locale/localectl/localectl.c create mode 100644 src/grp-locale/localectl/localectl.completion.bash create mode 100644 src/grp-locale/localectl/localectl.completion.zsh create mode 100644 src/grp-locale/localectl/localectl.xml create mode 100644 src/grp-locale/systemd-localed/.gitignore create mode 100644 src/grp-locale/systemd-localed/Makefile create mode 100644 src/grp-locale/systemd-localed/kbd-model-map create mode 100644 src/grp-locale/systemd-localed/keymap-util.c create mode 100644 src/grp-locale/systemd-localed/keymap-util.h create mode 100644 src/grp-locale/systemd-localed/language-fallback-map create mode 100644 src/grp-locale/systemd-localed/localed.c create mode 100644 src/grp-locale/systemd-localed/org.freedesktop.locale1.conf create mode 100644 src/grp-locale/systemd-localed/org.freedesktop.locale1.policy.in create mode 100644 src/grp-locale/systemd-localed/org.freedesktop.locale1.service create mode 100644 src/grp-locale/systemd-localed/systemd-localed.service.in create mode 100644 src/grp-locale/systemd-localed/systemd-localed.service.xml create mode 100644 src/grp-locale/systemd-localed/test-keymap-util.c create mode 100644 src/grp-login/.gitignore create mode 100644 src/grp-login/Makefile create mode 100644 src/grp-login/loginctl/Makefile create mode 100644 src/grp-login/loginctl/loginctl.c create mode 100644 src/grp-login/loginctl/loginctl.completion.bash create mode 100644 src/grp-login/loginctl/loginctl.completion.zsh create mode 100644 src/grp-login/loginctl/loginctl.xml create mode 100644 src/grp-login/loginctl/sysfs-show.c create mode 100644 src/grp-login/loginctl/sysfs-show.h create mode 100644 src/grp-login/pam_systemd/Makefile create mode 100644 src/grp-login/pam_systemd/pam_systemd.c create mode 100644 src/grp-login/pam_systemd/pam_systemd.sym create mode 100644 src/grp-login/pam_systemd/pam_systemd.xml create mode 100644 src/grp-login/systemd-inhibit/Makefile create mode 100644 src/grp-login/systemd-inhibit/inhibit.c create mode 100644 src/grp-login/systemd-inhibit/systemd-inhibit.completion.zsh create mode 100644 src/grp-login/systemd-inhibit/systemd-inhibit.xml create mode 100644 src/grp-login/systemd-logind/70-power-switch.rules create mode 100644 src/grp-login/systemd-logind/70-uaccess.rules create mode 100644 src/grp-login/systemd-logind/71-seat.rules.in create mode 100644 src/grp-login/systemd-logind/73-seat-late.rules.in create mode 100644 src/grp-login/systemd-logind/Makefile create mode 100644 src/grp-login/systemd-logind/logind-acl.c create mode 100644 src/grp-login/systemd-logind/logind-acl.h create mode 100644 src/grp-login/systemd-logind/logind-action.c create mode 100644 src/grp-login/systemd-logind/logind-action.h create mode 100644 src/grp-login/systemd-logind/logind-button.c create mode 100644 src/grp-login/systemd-logind/logind-button.h create mode 100644 src/grp-login/systemd-logind/logind-core.c create mode 100644 src/grp-login/systemd-logind/logind-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-device.c create mode 100644 src/grp-login/systemd-logind/logind-device.h create mode 100644 src/grp-login/systemd-logind/logind-gperf.gperf create mode 100644 src/grp-login/systemd-logind/logind-inhibit.c create mode 100644 src/grp-login/systemd-logind/logind-inhibit.h create mode 100644 src/grp-login/systemd-logind/logind-seat-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-seat.c create mode 100644 src/grp-login/systemd-logind/logind-seat.h create mode 100644 src/grp-login/systemd-logind/logind-session-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-session-device.c create mode 100644 src/grp-login/systemd-logind/logind-session-device.h create mode 100644 src/grp-login/systemd-logind/logind-session.c create mode 100644 src/grp-login/systemd-logind/logind-session.h create mode 100644 src/grp-login/systemd-logind/logind-user-dbus.c create mode 100644 src/grp-login/systemd-logind/logind-user.c create mode 100644 src/grp-login/systemd-logind/logind-user.h create mode 100644 src/grp-login/systemd-logind/logind-utmp.c create mode 100644 src/grp-login/systemd-logind/logind.c create mode 100644 src/grp-login/systemd-logind/logind.conf.in create mode 100644 src/grp-login/systemd-logind/logind.conf.xml create mode 100644 src/grp-login/systemd-logind/logind.h create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.conf create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.policy.in create mode 100644 src/grp-login/systemd-logind/org.freedesktop.login1.service create mode 100644 src/grp-login/systemd-logind/systemd-logind.service.in create mode 100644 src/grp-login/systemd-logind/systemd-logind.service.xml create mode 100644 src/grp-login/systemd-logind/systemd-user.pam.m4 create mode 100644 src/grp-login/systemd-logind/user.slice create mode 100644 src/grp-login/test-inhibit.c create mode 100644 src/grp-login/test-login-shared.c create mode 100644 src/grp-login/test-login-tables.c create mode 100644 src/grp-machine/Makefile create mode 100644 src/grp-machine/grp-import/Makefile create mode 100644 src/grp-machine/grp-import/libimport/Makefile create mode 100644 src/grp-machine/grp-import/libimport/import-common.c create mode 100644 src/grp-machine/grp-import/libimport/import-common.h create mode 100644 src/grp-machine/grp-import/libimport/import-compress.c create mode 100644 src/grp-machine/grp-import/libimport/import-compress.h create mode 100644 src/grp-machine/grp-import/libimport/qcow2-util.c create mode 100644 src/grp-machine/grp-import/libimport/qcow2-util.h create mode 100644 src/grp-machine/grp-import/libimport/test-qcow2.c create mode 100644 src/grp-machine/grp-import/systemd-export/Makefile create mode 100644 src/grp-machine/grp-import/systemd-export/export-raw.c create mode 100644 src/grp-machine/grp-import/systemd-export/export-raw.h create mode 100644 src/grp-machine/grp-import/systemd-export/export-tar.c create mode 100644 src/grp-machine/grp-import/systemd-export/export-tar.h create mode 100644 src/grp-machine/grp-import/systemd-export/export.c create mode 100644 src/grp-machine/grp-import/systemd-import/Makefile create mode 100644 src/grp-machine/grp-import/systemd-import/import-pubring.gpg create mode 100644 src/grp-machine/grp-import/systemd-import/import-raw.c create mode 100644 src/grp-machine/grp-import/systemd-import/import-raw.h create mode 100644 src/grp-machine/grp-import/systemd-import/import-tar.c create mode 100644 src/grp-machine/grp-import/systemd-import/import-tar.h create mode 100644 src/grp-machine/grp-import/systemd-import/import.c create mode 100644 src/grp-machine/grp-import/systemd-importd/.gitignore create mode 100644 src/grp-machine/grp-import/systemd-importd/Makefile create mode 100644 src/grp-machine/grp-import/systemd-importd/importd.c create mode 100644 src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.conf create mode 100644 src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.policy.in create mode 100644 src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.service create mode 100644 src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in create mode 100644 src/grp-machine/grp-import/systemd-importd/systemd-importd.service.xml create mode 100644 src/grp-machine/grp-import/systemd-pull/Makefile create mode 100644 src/grp-machine/grp-import/systemd-pull/curl-util.c create mode 100644 src/grp-machine/grp-import/systemd-pull/curl-util.h create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-common.c create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-common.h create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-job.c create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-job.h create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-raw.c create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-raw.h create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-tar.c create mode 100644 src/grp-machine/grp-import/systemd-pull/pull-tar.h create mode 100644 src/grp-machine/grp-import/systemd-pull/pull.c create mode 100644 src/grp-machine/machinectl/Makefile create mode 100644 src/grp-machine/machinectl/machinectl.c create mode 100644 src/grp-machine/machinectl/machinectl.completion.bash create mode 100644 src/grp-machine/machinectl/machinectl.completion.zsh create mode 100644 src/grp-machine/machinectl/machinectl.xml create mode 100644 src/grp-machine/nss-mymachines/Makefile create mode 100644 src/grp-machine/nss-mymachines/nss-mymachines.c create mode 100644 src/grp-machine/nss-mymachines/nss-mymachines.sym create mode 100644 src/grp-machine/nss-mymachines/nss-mymachines.xml create mode 100644 src/grp-machine/systemd-machined/.gitignore create mode 100644 src/grp-machine/systemd-machined/Makefile create mode 100644 src/grp-machine/systemd-machined/image-dbus.c create mode 100644 src/grp-machine/systemd-machined/image-dbus.h create mode 100644 src/grp-machine/systemd-machined/machine-dbus.c create mode 100644 src/grp-machine/systemd-machined/machine-dbus.h create mode 100644 src/grp-machine/systemd-machined/machine.c create mode 100644 src/grp-machine/systemd-machined/machine.h create mode 100644 src/grp-machine/systemd-machined/machine.slice create mode 100644 src/grp-machine/systemd-machined/machined-dbus.c create mode 100644 src/grp-machine/systemd-machined/machined.c create mode 100644 src/grp-machine/systemd-machined/machined.h create mode 100644 src/grp-machine/systemd-machined/operation.c create mode 100644 src/grp-machine/systemd-machined/operation.h create mode 100644 src/grp-machine/systemd-machined/org.freedesktop.machine1.conf create mode 100644 src/grp-machine/systemd-machined/org.freedesktop.machine1.policy.in create mode 100644 src/grp-machine/systemd-machined/org.freedesktop.machine1.service create mode 100644 src/grp-machine/systemd-machined/systemd-machined.service.in create mode 100644 src/grp-machine/systemd-machined/systemd-machined.service.xml create mode 100644 src/grp-machine/systemd-machined/test-machine-tables.c create mode 100644 src/grp-network/90-networkd.preset create mode 100644 src/grp-network/Makefile create mode 100644 src/grp-network/libnetworkd-core/.gitignore create mode 100644 src/grp-network/libnetworkd-core/Makefile create mode 100644 src/grp-network/libnetworkd-core/networkd-address-pool.c create mode 100644 src/grp-network/libnetworkd-core/networkd-address-pool.h create mode 100644 src/grp-network/libnetworkd-core/networkd-address.c create mode 100644 src/grp-network/libnetworkd-core/networkd-address.h create mode 100644 src/grp-network/libnetworkd-core/networkd-brvlan.c create mode 100644 src/grp-network/libnetworkd-core/networkd-brvlan.h create mode 100644 src/grp-network/libnetworkd-core/networkd-conf.c create mode 100644 src/grp-network/libnetworkd-core/networkd-conf.h create mode 100644 src/grp-network/libnetworkd-core/networkd-dhcp4.c create mode 100644 src/grp-network/libnetworkd-core/networkd-dhcp6.c create mode 100644 src/grp-network/libnetworkd-core/networkd-fdb.c create mode 100644 src/grp-network/libnetworkd-core/networkd-fdb.h create mode 100644 src/grp-network/libnetworkd-core/networkd-gperf.gperf create mode 100644 src/grp-network/libnetworkd-core/networkd-ipv4ll.c create mode 100644 src/grp-network/libnetworkd-core/networkd-link-bus.c create mode 100644 src/grp-network/libnetworkd-core/networkd-link.c create mode 100644 src/grp-network/libnetworkd-core/networkd-link.h create mode 100644 src/grp-network/libnetworkd-core/networkd-lldp-tx.c create mode 100644 src/grp-network/libnetworkd-core/networkd-lldp-tx.h create mode 100644 src/grp-network/libnetworkd-core/networkd-manager-bus.c create mode 100644 src/grp-network/libnetworkd-core/networkd-manager.c create mode 100644 src/grp-network/libnetworkd-core/networkd-ndisc.c create mode 100644 src/grp-network/libnetworkd-core/networkd-ndisc.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-bond.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-bond.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-bridge.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-bridge.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-dummy.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-dummy.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-gperf.gperf create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-macvlan.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-macvlan.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-tunnel.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-tunnel.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-tuntap.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-tuntap.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-veth.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-veth.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vlan.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vlan.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vrf.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vrf.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vxlan.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev-vxlan.h create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev.c create mode 100644 src/grp-network/libnetworkd-core/networkd-netdev.h create mode 100644 src/grp-network/libnetworkd-core/networkd-network-bus.c create mode 100644 src/grp-network/libnetworkd-core/networkd-network-gperf.gperf create mode 100644 src/grp-network/libnetworkd-core/networkd-network.c create mode 100644 src/grp-network/libnetworkd-core/networkd-network.h create mode 100644 src/grp-network/libnetworkd-core/networkd-route.c create mode 100644 src/grp-network/libnetworkd-core/networkd-route.h create mode 100644 src/grp-network/libnetworkd-core/networkd-util.c create mode 100644 src/grp-network/libnetworkd-core/networkd-util.h create mode 100644 src/grp-network/libnetworkd-core/networkd.h create mode 100644 src/grp-network/network/80-container-host0.network create mode 100644 src/grp-network/network/80-container-ve.network create mode 100644 src/grp-network/network/80-container-vz.network create mode 100644 src/grp-network/network/99-default.link create mode 100644 src/grp-network/networkctl/Makefile create mode 100644 src/grp-network/networkctl/networkctl.c create mode 100644 src/grp-network/networkctl/networkctl.completion.bash create mode 100644 src/grp-network/networkctl/networkctl.completion.zsh create mode 100644 src/grp-network/networkctl/networkctl.xml create mode 100644 src/grp-network/systemd-networkd-wait-online/Makefile create mode 100644 src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.c create mode 100644 src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.h create mode 100644 src/grp-network/systemd-networkd-wait-online/networkd-wait-online-manager.c create mode 100644 src/grp-network/systemd-networkd-wait-online/networkd-wait-online.c create mode 100644 src/grp-network/systemd-networkd-wait-online/networkd-wait-online.h create mode 100644 src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in create mode 100644 src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.xml create mode 100644 src/grp-network/systemd-networkd/Makefile create mode 100644 src/grp-network/systemd-networkd/networkd.c create mode 100644 src/grp-network/systemd-networkd/networkd.conf.xml create mode 100644 src/grp-network/systemd-networkd/org.freedesktop.network1.conf create mode 100644 src/grp-network/systemd-networkd/org.freedesktop.network1.service create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.service.m4.in create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.service.xml create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.socket create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.sysusers create mode 100644 src/grp-network/systemd-networkd/systemd-networkd.tmpfiles create mode 100644 src/grp-network/test-network-tables.c create mode 100644 src/grp-network/test-network.c create mode 100644 src/grp-network/test-networkd-conf.c create mode 100644 src/grp-resolve/90-resolved.preset create mode 100644 src/grp-resolve/Makefile create mode 100644 src/grp-resolve/libbasic-dns/Makefile create mode 100644 src/grp-resolve/libbasic-dns/dns-type.c create mode 100644 src/grp-resolve/libbasic-dns/dns-type.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-def.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-answer.c create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-answer.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-dnssec.c create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-dnssec.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-packet.c create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-packet.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-question.c create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-question.h create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-rr.c create mode 100644 src/grp-resolve/libbasic-dns/resolved-dns-rr.h create mode 100644 src/grp-resolve/libbasic-dns/test-data/_443._tcp.fedoraproject.org.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/_openpgpkey.fedoraproject.org.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/fake-caa.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/fedoraproject.org.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/gandi.net.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/google.com.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/kyhwana.org.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/root.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/teamits.com.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-data/zbyszek@fedoraproject.org.pkts create mode 100644 src/grp-resolve/libbasic-dns/test-dns-packet.c create mode 100644 src/grp-resolve/libbasic-dns/test-dnssec-complex.c create mode 100644 src/grp-resolve/libbasic-dns/test-dnssec.c create mode 100644 src/grp-resolve/libbasic-dns/test-resolve-tables.c create mode 100644 src/grp-resolve/nss-resolve/Makefile create mode 100644 src/grp-resolve/nss-resolve/nss-resolve.c create mode 100644 src/grp-resolve/nss-resolve/nss-resolve.sym create mode 100644 src/grp-resolve/nss-resolve/nss-resolve.xml create mode 100644 src/grp-resolve/systemd-resolve/Makefile create mode 100644 src/grp-resolve/systemd-resolve/resolve-tool.c create mode 100644 src/grp-resolve/systemd-resolve/systemd-resolve.completion.bash create mode 100644 src/grp-resolve/systemd-resolve/systemd-resolve.completion.zsh create mode 100644 src/grp-resolve/systemd-resolve/systemd-resolve.xml create mode 100644 src/grp-resolve/systemd-resolved/.gitignore create mode 100644 src/grp-resolve/systemd-resolved/Makefile create mode 100644 src/grp-resolve/systemd-resolved/RFCs create mode 100644 src/grp-resolve/systemd-resolved/dnssec-trust-anchors.d.xml create mode 100644 src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.conf create mode 100644 src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.service create mode 100644 src/grp-resolve/systemd-resolved/resolv.conf create mode 100644 src/grp-resolve/systemd-resolved/resolved-bus.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-bus.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-conf.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-conf.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-cache.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-cache.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-query.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-query.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-scope.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-scope.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-search-domain.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-search-domain.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-server.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-server.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-stream.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-stream.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-stub.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-stub.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-synthesize.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-synthesize.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-transaction.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-transaction.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-zone.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-dns-zone.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-etc-hosts.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-etc-hosts.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-gperf.gperf create mode 100644 src/grp-resolve/systemd-resolved/resolved-link-bus.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-link-bus.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-link.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-link.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-llmnr.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-llmnr.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-manager.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-manager.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-mdns.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-mdns.h create mode 100644 src/grp-resolve/systemd-resolved/resolved-resolv-conf.c create mode 100644 src/grp-resolve/systemd-resolved/resolved-resolv-conf.h create mode 100644 src/grp-resolve/systemd-resolved/resolved.c create mode 100644 src/grp-resolve/systemd-resolved/resolved.conf.in create mode 100644 src/grp-resolve/systemd-resolved/resolved.conf.xml create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.service.xml create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.sysusers create mode 100644 src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles create mode 100644 src/grp-system/90-systemd.preset create mode 100644 src/grp-system/Makefile create mode 100644 src/grp-system/bootup.xml create mode 100644 src/grp-system/grp-utils/Makefile create mode 100644 src/grp-system/grp-utils/systemd-analyze/.gitignore create mode 100644 src/grp-system/grp-utils/systemd-analyze/Makefile create mode 100644 src/grp-system/grp-utils/systemd-analyze/analyze-verify.c create mode 100644 src/grp-system/grp-utils/systemd-analyze/analyze-verify.h create mode 100644 src/grp-system/grp-utils/systemd-analyze/analyze.c create mode 100644 src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.bash create mode 100644 src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.zsh create mode 100644 src/grp-system/grp-utils/systemd-analyze/systemd-analyze.xml create mode 100644 src/grp-system/grp-utils/systemd-delta/Makefile create mode 100644 src/grp-system/grp-utils/systemd-delta/delta.c create mode 100644 src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.bash create mode 100644 src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.zsh create mode 100644 src/grp-system/grp-utils/systemd-delta/systemd-delta.xml create mode 100644 src/grp-system/grp-utils/systemd-fstab-generator/Makefile create mode 100644 src/grp-system/grp-utils/systemd-fstab-generator/fstab-generator.c create mode 120000 src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.c create mode 120000 src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.h create mode 100644 src/grp-system/grp-utils/systemd-fstab-generator/systemd-fstab-generator.xml create mode 100644 src/grp-system/grp-utils/systemd-run/Makefile create mode 100644 src/grp-system/grp-utils/systemd-run/run.c create mode 100644 src/grp-system/grp-utils/systemd-run/systemd-run.completion.bash create mode 100644 src/grp-system/grp-utils/systemd-run/systemd-run.completion.zsh create mode 100644 src/grp-system/grp-utils/systemd-run/systemd-run.xml create mode 100644 src/grp-system/grp-utils/systemd-sysv-generator/.gitignore create mode 100644 src/grp-system/grp-utils/systemd-sysv-generator/Makefile create mode 100644 src/grp-system/grp-utils/systemd-sysv-generator/README.in create mode 100644 src/grp-system/grp-utils/systemd-sysv-generator/systemd-sysv-generator.xml create mode 100644 src/grp-system/grp-utils/systemd-sysv-generator/sysv-generator.c create mode 100644 src/grp-system/kernel-command-line.xml create mode 100644 src/grp-system/libcore/.gitignore create mode 100644 src/grp-system/libcore/Makefile create mode 100644 src/grp-system/libcore/audit-fd.c create mode 100644 src/grp-system/libcore/audit-fd.h create mode 100644 src/grp-system/libcore/automount.c create mode 100644 src/grp-system/libcore/automount.h create mode 100644 src/grp-system/libcore/bus-policy.c create mode 100644 src/grp-system/libcore/bus-policy.h create mode 100644 src/grp-system/libcore/busname.c create mode 100644 src/grp-system/libcore/busname.h create mode 100644 src/grp-system/libcore/cgroup.c create mode 100644 src/grp-system/libcore/cgroup.h create mode 100644 src/grp-system/libcore/dbus-automount.c create mode 100644 src/grp-system/libcore/dbus-automount.h create mode 100644 src/grp-system/libcore/dbus-busname.c create mode 100644 src/grp-system/libcore/dbus-busname.h create mode 100644 src/grp-system/libcore/dbus-cgroup.c create mode 100644 src/grp-system/libcore/dbus-cgroup.h create mode 100644 src/grp-system/libcore/dbus-device.c create mode 100644 src/grp-system/libcore/dbus-device.h create mode 100644 src/grp-system/libcore/dbus-execute.c create mode 100644 src/grp-system/libcore/dbus-execute.h create mode 100644 src/grp-system/libcore/dbus-job.c create mode 100644 src/grp-system/libcore/dbus-job.h create mode 100644 src/grp-system/libcore/dbus-kill.c create mode 100644 src/grp-system/libcore/dbus-kill.h create mode 100644 src/grp-system/libcore/dbus-manager.c create mode 100644 src/grp-system/libcore/dbus-manager.h create mode 100644 src/grp-system/libcore/dbus-mount.c create mode 100644 src/grp-system/libcore/dbus-mount.h create mode 100644 src/grp-system/libcore/dbus-path.c create mode 100644 src/grp-system/libcore/dbus-path.h create mode 100644 src/grp-system/libcore/dbus-scope.c create mode 100644 src/grp-system/libcore/dbus-scope.h create mode 100644 src/grp-system/libcore/dbus-service.c create mode 100644 src/grp-system/libcore/dbus-service.h create mode 100644 src/grp-system/libcore/dbus-slice.c create mode 100644 src/grp-system/libcore/dbus-slice.h create mode 100644 src/grp-system/libcore/dbus-socket.c create mode 100644 src/grp-system/libcore/dbus-socket.h create mode 100644 src/grp-system/libcore/dbus-swap.c create mode 100644 src/grp-system/libcore/dbus-swap.h create mode 100644 src/grp-system/libcore/dbus-target.c create mode 100644 src/grp-system/libcore/dbus-target.h create mode 100644 src/grp-system/libcore/dbus-timer.c create mode 100644 src/grp-system/libcore/dbus-timer.h create mode 100644 src/grp-system/libcore/dbus-unit.c create mode 100644 src/grp-system/libcore/dbus-unit.h create mode 100644 src/grp-system/libcore/dbus.c create mode 100644 src/grp-system/libcore/dbus.h create mode 100644 src/grp-system/libcore/device.c create mode 100644 src/grp-system/libcore/device.h create mode 100644 src/grp-system/libcore/execute.c create mode 100644 src/grp-system/libcore/execute.h create mode 100644 src/grp-system/libcore/failure-action.c create mode 100644 src/grp-system/libcore/failure-action.h create mode 100644 src/grp-system/libcore/hostname-setup.c create mode 100644 src/grp-system/libcore/hostname-setup.h create mode 100644 src/grp-system/libcore/ima-setup.c create mode 100644 src/grp-system/libcore/ima-setup.h create mode 100644 src/grp-system/libcore/job.c create mode 100644 src/grp-system/libcore/job.h create mode 100644 src/grp-system/libcore/kill.c create mode 100644 src/grp-system/libcore/kill.h create mode 100644 src/grp-system/libcore/killall.c create mode 100644 src/grp-system/libcore/killall.h create mode 100644 src/grp-system/libcore/kmod-setup.c create mode 100644 src/grp-system/libcore/kmod-setup.h create mode 100644 src/grp-system/libcore/linux/auto_dev-ioctl.h create mode 100644 src/grp-system/libcore/load-dropin.c create mode 100644 src/grp-system/libcore/load-dropin.h create mode 100644 src/grp-system/libcore/load-fragment-gperf.gperf.m4 create mode 100644 src/grp-system/libcore/load-fragment.c create mode 100644 src/grp-system/libcore/load-fragment.h create mode 100644 src/grp-system/libcore/locale-setup.c create mode 100644 src/grp-system/libcore/locale-setup.h create mode 100644 src/grp-system/libcore/loopback-setup.c create mode 100644 src/grp-system/libcore/loopback-setup.h create mode 100644 src/grp-system/libcore/machine-id-setup.c create mode 100644 src/grp-system/libcore/machine-id-setup.h create mode 100644 src/grp-system/libcore/manager.c create mode 100644 src/grp-system/libcore/manager.h create mode 100644 src/grp-system/libcore/mount-setup.c create mode 100644 src/grp-system/libcore/mount-setup.h create mode 100644 src/grp-system/libcore/mount.c create mode 100644 src/grp-system/libcore/mount.h create mode 100644 src/grp-system/libcore/namespace.c create mode 100644 src/grp-system/libcore/namespace.h create mode 100644 src/grp-system/libcore/path.c create mode 100644 src/grp-system/libcore/path.h create mode 100644 src/grp-system/libcore/scope.c create mode 100644 src/grp-system/libcore/scope.h create mode 100644 src/grp-system/libcore/selinux-access.c create mode 100644 src/grp-system/libcore/selinux-access.h create mode 100644 src/grp-system/libcore/selinux-setup.c create mode 100644 src/grp-system/libcore/selinux-setup.h create mode 100644 src/grp-system/libcore/service.c create mode 100644 src/grp-system/libcore/service.h create mode 100644 src/grp-system/libcore/show-status.c create mode 100644 src/grp-system/libcore/show-status.h create mode 100644 src/grp-system/libcore/slice.c create mode 100644 src/grp-system/libcore/slice.h create mode 100644 src/grp-system/libcore/smack-setup.c create mode 100644 src/grp-system/libcore/smack-setup.h create mode 100644 src/grp-system/libcore/socket.c create mode 100644 src/grp-system/libcore/socket.h create mode 100644 src/grp-system/libcore/swap.c create mode 100644 src/grp-system/libcore/swap.h create mode 100644 src/grp-system/libcore/target.c create mode 100644 src/grp-system/libcore/target.h create mode 100644 src/grp-system/libcore/timer.c create mode 100644 src/grp-system/libcore/timer.h create mode 100644 src/grp-system/libcore/transaction.c create mode 100644 src/grp-system/libcore/transaction.h create mode 100644 src/grp-system/libcore/unit-printf.c create mode 100644 src/grp-system/libcore/unit-printf.h create mode 100644 src/grp-system/libcore/unit.c create mode 100644 src/grp-system/libcore/unit.h create mode 100644 src/grp-system/systemctl/.gitignore create mode 100644 src/grp-system/systemctl/Makefile create mode 100644 src/grp-system/systemctl/halt.xml create mode 100644 src/grp-system/systemctl/runlevel.xml create mode 100644 src/grp-system/systemctl/shutdown.xml create mode 100644 src/grp-system/systemctl/systemctl.c create mode 100644 src/grp-system/systemctl/systemctl.completion.bash.in create mode 100644 src/grp-system/systemctl/systemctl.completion.zsh.in create mode 100644 src/grp-system/systemctl/systemctl.xml create mode 100755 src/grp-system/systemctl/systemd-sysv-install.SKELETON create mode 100644 src/grp-system/systemctl/systemd.preset.xml create mode 100644 src/grp-system/systemctl/telinit.xml create mode 100644 src/grp-system/systemd-shutdown/Makefile create mode 100644 src/grp-system/systemd-shutdown/halt.target create mode 100644 src/grp-system/systemd-shutdown/kexec.target create mode 120000 src/grp-system/systemd-shutdown/killall.c create mode 120000 src/grp-system/systemd-shutdown/killall.h create mode 120000 src/grp-system/systemd-shutdown/mount-setup.c create mode 120000 src/grp-system/systemd-shutdown/mount-setup.h create mode 100644 src/grp-system/systemd-shutdown/poweroff.target create mode 100644 src/grp-system/systemd-shutdown/reboot.target create mode 100644 src/grp-system/systemd-shutdown/shutdown.c create mode 100644 src/grp-system/systemd-shutdown/systemd-halt.service.in create mode 100644 src/grp-system/systemd-shutdown/systemd-kexec.service.in create mode 100644 src/grp-system/systemd-shutdown/systemd-poweroff.service.in create mode 100644 src/grp-system/systemd-shutdown/systemd-reboot.service.in create mode 100644 src/grp-system/systemd-shutdown/systemd-shutdown.xml create mode 100644 src/grp-system/systemd-shutdown/umount.c create mode 100644 src/grp-system/systemd-shutdown/umount.h create mode 100755 src/grp-system/systemd/50-systemd-user.xorg create mode 100644 src/grp-system/systemd/Makefile create mode 100644 src/grp-system/systemd/macros.systemd.in create mode 100644 src/grp-system/systemd/main.c create mode 100644 src/grp-system/systemd/org.freedesktop.systemd1.conf create mode 100644 src/grp-system/systemd/org.freedesktop.systemd1.policy.in.in create mode 100644 src/grp-system/systemd/org.freedesktop.systemd1.service create mode 100644 src/grp-system/systemd/system.conf create mode 100644 src/grp-system/systemd/systemd-system.conf.xml create mode 100644 src/grp-system/systemd/systemd-tmpfs.tmpfiles create mode 100644 src/grp-system/systemd/systemd.automount.xml create mode 100644 src/grp-system/systemd/systemd.device.xml create mode 100644 src/grp-system/systemd/systemd.exec.xml create mode 100644 src/grp-system/systemd/systemd.generator.xml create mode 100644 src/grp-system/systemd/systemd.journal-fields.xml create mode 100644 src/grp-system/systemd/systemd.kill.xml create mode 100644 src/grp-system/systemd/systemd.link.xml create mode 100644 src/grp-system/systemd/systemd.mount.xml create mode 100644 src/grp-system/systemd/systemd.netdev.xml create mode 100644 src/grp-system/systemd/systemd.network.xml create mode 100644 src/grp-system/systemd/systemd.nspawn.xml create mode 100644 src/grp-system/systemd/systemd.offline-updates.xml create mode 100644 src/grp-system/systemd/systemd.path.xml create mode 100644 src/grp-system/systemd/systemd.pc.in create mode 100644 src/grp-system/systemd/systemd.resource-control.xml create mode 100644 src/grp-system/systemd/systemd.scope.xml create mode 100644 src/grp-system/systemd/systemd.service.xml create mode 100644 src/grp-system/systemd/systemd.slice.xml create mode 100644 src/grp-system/systemd/systemd.socket.xml create mode 100644 src/grp-system/systemd/systemd.special.xml create mode 100644 src/grp-system/systemd/systemd.swap.xml create mode 100644 src/grp-system/systemd/systemd.target.xml create mode 100644 src/grp-system/systemd/systemd.time.xml create mode 100644 src/grp-system/systemd/systemd.timer.xml create mode 100644 src/grp-system/systemd/systemd.tmpfiles create mode 100644 src/grp-system/systemd/systemd.unit.xml create mode 100644 src/grp-system/systemd/systemd.xml create mode 100644 src/grp-system/systemd/triggers.systemd.in create mode 100644 src/grp-system/systemd/user.conf create mode 100644 src/grp-timedate/Makefile create mode 100644 src/grp-timedate/systemd-timedated/.gitignore create mode 100644 src/grp-timedate/systemd-timedated/Makefile create mode 100644 src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.conf create mode 100644 src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.policy.in create mode 100644 src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.service create mode 100644 src/grp-timedate/systemd-timedated/systemd-timedated.service.in create mode 100644 src/grp-timedate/systemd-timedated/systemd-timedated.service.xml create mode 100644 src/grp-timedate/systemd-timedated/timedated.c create mode 100644 src/grp-timedate/timedatectl/Makefile create mode 100644 src/grp-timedate/timedatectl/timedatectl.c create mode 100644 src/grp-timedate/timedatectl/timedatectl.completion.bash create mode 100644 src/grp-timedate/timedatectl/timedatectl.completion.zsh create mode 100644 src/grp-timedate/timedatectl/timedatectl.xml create mode 100644 src/grp-udev/.gitignore create mode 100644 src/grp-udev/Makefile create mode 100644 src/grp-udev/ata_id/Makefile create mode 100644 src/grp-udev/ata_id/ata_id.c create mode 100644 src/grp-udev/cdrom_id/60-cdrom_id.rules create mode 100644 src/grp-udev/cdrom_id/Makefile create mode 100644 src/grp-udev/cdrom_id/cdrom_id.c create mode 100644 src/grp-udev/collect/Makefile create mode 100644 src/grp-udev/collect/collect.c create mode 100644 src/grp-udev/hwdb/.gitignore create mode 100644 src/grp-udev/hwdb/20-OUI.hwdb create mode 100644 src/grp-udev/hwdb/20-acpi-vendor.hwdb create mode 100644 src/grp-udev/hwdb/20-acpi-vendor.hwdb.patch create mode 100644 src/grp-udev/hwdb/20-bluetooth-vendor-product.hwdb create mode 100644 src/grp-udev/hwdb/20-net-ifname.hwdb create mode 100644 src/grp-udev/hwdb/20-pci-classes.hwdb create mode 100644 src/grp-udev/hwdb/20-pci-vendor-model.hwdb create mode 100644 src/grp-udev/hwdb/20-sdio-classes.hwdb create mode 100644 src/grp-udev/hwdb/20-sdio-vendor-model.hwdb create mode 100644 src/grp-udev/hwdb/20-usb-classes.hwdb create mode 100644 src/grp-udev/hwdb/20-usb-vendor-model.hwdb create mode 100644 src/grp-udev/hwdb/60-evdev.hwdb create mode 100644 src/grp-udev/hwdb/60-keyboard.hwdb create mode 100644 src/grp-udev/hwdb/70-mouse.hwdb create mode 100644 src/grp-udev/hwdb/70-pointingstick.hwdb create mode 100644 src/grp-udev/hwdb/70-touchpad.hwdb create mode 100644 src/grp-udev/hwdb/Makefile create mode 100755 src/grp-udev/hwdb/acpi-update.py create mode 100755 src/grp-udev/hwdb/ids-update.pl create mode 100644 src/grp-udev/hwdb/sdio.ids create mode 100644 src/grp-udev/libudev-core/Makefile create mode 120000 src/grp-udev/libudev-core/logind-acl.c create mode 120000 src/grp-udev/libudev-core/logind-acl.h create mode 100644 src/grp-udev/libudev-core/net/.gitignore create mode 100644 src/grp-udev/libudev-core/net/Makefile create mode 100644 src/grp-udev/libudev-core/net/ethtool-util.c create mode 100644 src/grp-udev/libudev-core/net/ethtool-util.h create mode 100644 src/grp-udev/libudev-core/net/link-config-gperf.gperf create mode 100644 src/grp-udev/libudev-core/net/link-config.c create mode 100644 src/grp-udev/libudev-core/net/link-config.h create mode 120000 src/grp-udev/libudev-core/sd-login.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-blkid.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-btrfs.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-hwdb.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-input_id.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-keyboard.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-kmod.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-net_id.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-net_setup_link.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-path_id.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-uaccess.c create mode 100644 src/grp-udev/libudev-core/udev-builtin-usb_id.c create mode 100644 src/grp-udev/libudev-core/udev-builtin.c create mode 100644 src/grp-udev/libudev-core/udev-ctrl.c create mode 100644 src/grp-udev/libudev-core/udev-event.c create mode 100644 src/grp-udev/libudev-core/udev-node.c create mode 100644 src/grp-udev/libudev-core/udev-rules.c create mode 100644 src/grp-udev/libudev-core/udev-watch.c create mode 100644 src/grp-udev/mtd_probe/75-probe_mtd.rules create mode 100644 src/grp-udev/mtd_probe/Makefile create mode 100644 src/grp-udev/mtd_probe/mtd_probe.c create mode 100644 src/grp-udev/mtd_probe/mtd_probe.h create mode 100644 src/grp-udev/mtd_probe/probe_smartmedia.c create mode 100644 src/grp-udev/rules/.gitignore create mode 100644 src/grp-udev/rules/50-udev-default.rules create mode 100644 src/grp-udev/rules/60-block.rules create mode 100644 src/grp-udev/rules/60-drm.rules create mode 100644 src/grp-udev/rules/60-evdev.rules create mode 100644 src/grp-udev/rules/60-persistent-alsa.rules create mode 100644 src/grp-udev/rules/60-persistent-input.rules create mode 100644 src/grp-udev/rules/60-persistent-storage-tape.rules create mode 100644 src/grp-udev/rules/60-persistent-storage.rules create mode 100644 src/grp-udev/rules/60-serial.rules create mode 100644 src/grp-udev/rules/64-btrfs.rules create mode 100644 src/grp-udev/rules/70-mouse.rules create mode 100644 src/grp-udev/rules/70-touchpad.rules create mode 100644 src/grp-udev/rules/75-net-description.rules create mode 100644 src/grp-udev/rules/78-sound-card.rules create mode 100644 src/grp-udev/rules/80-drivers.rules create mode 100644 src/grp-udev/rules/80-net-setup-link.rules create mode 100644 src/grp-udev/rules/99-systemd.rules.in create mode 100644 src/grp-udev/scsi_id/.gitignore create mode 100644 src/grp-udev/scsi_id/Makefile create mode 100644 src/grp-udev/scsi_id/README create mode 100644 src/grp-udev/scsi_id/scsi.h create mode 100644 src/grp-udev/scsi_id/scsi_id.c create mode 100644 src/grp-udev/scsi_id/scsi_id.h create mode 100644 src/grp-udev/scsi_id/scsi_serial.c create mode 100644 src/grp-udev/systemd-hwdb/Makefile create mode 100644 src/grp-udev/systemd-hwdb/hwdb.c create mode 100644 src/grp-udev/systemd-hwdb/hwdb.xml create mode 100644 src/grp-udev/systemd-hwdb/systemd-hwdb.xml create mode 100644 src/grp-udev/systemd-udevd/Makefile create mode 100644 src/grp-udev/systemd-udevd/systemd-udevd.service.in create mode 100644 src/grp-udev/systemd-udevd/systemd-udevd.service.xml create mode 100644 src/grp-udev/systemd-udevd/udev.conf create mode 100644 src/grp-udev/systemd-udevd/udev.conf.xml create mode 100644 src/grp-udev/systemd-udevd/udevd.c create mode 100644 src/grp-udev/udev.pc.in create mode 100644 src/grp-udev/udev.xml create mode 100644 src/grp-udev/udevadm/Makefile create mode 100644 src/grp-udev/udevadm/udevadm-control.c create mode 100644 src/grp-udev/udevadm/udevadm-hwdb.c create mode 100644 src/grp-udev/udevadm/udevadm-info.c create mode 100644 src/grp-udev/udevadm/udevadm-monitor.c create mode 100644 src/grp-udev/udevadm/udevadm-settle.c create mode 100644 src/grp-udev/udevadm/udevadm-test-builtin.c create mode 100644 src/grp-udev/udevadm/udevadm-test.c create mode 100644 src/grp-udev/udevadm/udevadm-trigger.c create mode 100644 src/grp-udev/udevadm/udevadm-util.c create mode 100644 src/grp-udev/udevadm/udevadm-util.h create mode 100644 src/grp-udev/udevadm/udevadm.c create mode 100644 src/grp-udev/udevadm/udevadm.completion.bash create mode 100644 src/grp-udev/udevadm/udevadm.completion.zsh create mode 100644 src/grp-udev/udevadm/udevadm.xml create mode 100644 src/grp-udev/v4l_id/60-persistent-v4l.rules create mode 100644 src/grp-udev/v4l_id/Makefile create mode 100644 src/grp-udev/v4l_id/v4l_id.c create mode 100644 src/grp-utils/Makefile create mode 100644 src/grp-utils/systemd-ac-power/Makefile create mode 100644 src/grp-utils/systemd-ac-power/ac-power.c create mode 100644 src/grp-utils/systemd-escape/Makefile create mode 100644 src/grp-utils/systemd-escape/escape.c create mode 100644 src/grp-utils/systemd-escape/systemd-escape.xml create mode 100644 src/grp-utils/systemd-notify/Makefile create mode 100644 src/grp-utils/systemd-notify/notify.c create mode 100644 src/grp-utils/systemd-notify/systemd-notify.completion.zsh create mode 100644 src/grp-utils/systemd-notify/systemd-notify.xml create mode 100644 src/grp-utils/systemd-path/Makefile create mode 120000 src/grp-utils/systemd-path/_sd-common.h create mode 100644 src/grp-utils/systemd-path/path.c create mode 100644 src/grp-utils/systemd-path/sd-path.c create mode 100644 src/grp-utils/systemd-path/sd-path.h create mode 100644 src/grp-utils/systemd-path/systemd-path.completion.bash create mode 100644 src/grp-utils/systemd-path/systemd-path.xml create mode 100644 src/grp-utils/systemd-socket-activate/Makefile create mode 100644 src/grp-utils/systemd-socket-activate/activate.c create mode 100644 src/grp-utils/systemd-socket-activate/systemd-socket-activate.xml delete mode 120000 src/hibernate-resume/Makefile delete mode 100644 src/hibernate-resume/hibernate-resume-generator.c delete mode 100644 src/hibernate-resume/hibernate-resume.c delete mode 100644 src/hostname/.gitignore delete mode 120000 src/hostname/Makefile delete mode 100644 src/hostname/hostnamectl.c delete mode 100644 src/hostname/hostnamed.c delete mode 100644 src/hostname/org.freedesktop.hostname1.conf delete mode 100644 src/hostname/org.freedesktop.hostname1.policy.in delete mode 100644 src/hostname/org.freedesktop.hostname1.service delete mode 120000 src/hwdb/Makefile delete mode 100644 src/hwdb/hwdb.c delete mode 100644 src/import/.gitignore delete mode 120000 src/import/Makefile delete mode 100644 src/import/curl-util.c delete mode 100644 src/import/curl-util.h delete mode 100644 src/import/export-raw.c delete mode 100644 src/import/export-raw.h delete mode 100644 src/import/export-tar.c delete mode 100644 src/import/export-tar.h delete mode 100644 src/import/export.c delete mode 100644 src/import/import-common.c delete mode 100644 src/import/import-common.h delete mode 100644 src/import/import-compress.c delete mode 100644 src/import/import-compress.h delete mode 100644 src/import/import-pubring.gpg delete mode 100644 src/import/import-raw.c delete mode 100644 src/import/import-raw.h delete mode 100644 src/import/import-tar.c delete mode 100644 src/import/import-tar.h delete mode 100644 src/import/import.c delete mode 100644 src/import/importd.c delete mode 100644 src/import/org.freedesktop.import1.conf delete mode 100644 src/import/org.freedesktop.import1.policy.in delete mode 100644 src/import/org.freedesktop.import1.service delete mode 100644 src/import/pull-common.c delete mode 100644 src/import/pull-common.h delete mode 100644 src/import/pull-job.c delete mode 100644 src/import/pull-job.h delete mode 100644 src/import/pull-raw.c delete mode 100644 src/import/pull-raw.h delete mode 100644 src/import/pull-tar.c delete mode 100644 src/import/pull-tar.h delete mode 100644 src/import/pull.c delete mode 100644 src/import/qcow2-util.c delete mode 100644 src/import/qcow2-util.h delete mode 100644 src/import/test-qcow2.c delete mode 120000 src/initctl/Makefile delete mode 100644 src/initctl/initctl.c delete mode 100644 src/journal-remote/.gitignore delete mode 120000 src/journal-remote/Makefile delete mode 100644 src/journal-remote/browse.html delete mode 100644 src/journal-remote/journal-gatewayd.c delete mode 100644 src/journal-remote/journal-remote-parse.c delete mode 100644 src/journal-remote/journal-remote-parse.h delete mode 100644 src/journal-remote/journal-remote-write.c delete mode 100644 src/journal-remote/journal-remote-write.h delete mode 100644 src/journal-remote/journal-remote.c delete mode 100644 src/journal-remote/journal-remote.conf.in delete mode 100644 src/journal-remote/journal-remote.h delete mode 100644 src/journal-remote/journal-upload-journal.c delete mode 100644 src/journal-remote/journal-upload.c delete mode 100644 src/journal-remote/journal-upload.conf.in delete mode 100644 src/journal-remote/journal-upload.h delete mode 100755 src/journal-remote/log-generator.py delete mode 100644 src/journal-remote/microhttpd-util.c delete mode 100644 src/journal-remote/microhttpd-util.h delete mode 100644 src/journal/.gitignore delete mode 120000 src/journal/Makefile delete mode 100644 src/journal/audit-type.c delete mode 100644 src/journal/audit-type.h delete mode 100644 src/journal/cat.c delete mode 100644 src/journal/catalog.c delete mode 100644 src/journal/catalog.h delete mode 100644 src/journal/compress.c delete mode 100644 src/journal/compress.h delete mode 100644 src/journal/fsprg.c delete mode 100644 src/journal/fsprg.h delete mode 100644 src/journal/journal-authenticate.c delete mode 100644 src/journal/journal-authenticate.h delete mode 100644 src/journal/journal-def.h delete mode 100644 src/journal/journal-file.c delete mode 100644 src/journal/journal-file.h delete mode 100644 src/journal/journal-internal.h delete mode 100644 src/journal/journal-qrcode.c delete mode 100644 src/journal/journal-qrcode.h delete mode 100644 src/journal/journal-send.c delete mode 100644 src/journal/journal-vacuum.c delete mode 100644 src/journal/journal-vacuum.h delete mode 100644 src/journal/journal-verify.c delete mode 100644 src/journal/journal-verify.h delete mode 100644 src/journal/journalctl.c delete mode 100644 src/journal/journald-audit.c delete mode 100644 src/journal/journald-audit.h delete mode 100644 src/journal/journald-console.c delete mode 100644 src/journal/journald-console.h delete mode 100644 src/journal/journald-gperf.gperf delete mode 100644 src/journal/journald-kmsg.c delete mode 100644 src/journal/journald-kmsg.h delete mode 100644 src/journal/journald-native.c delete mode 100644 src/journal/journald-native.h delete mode 100644 src/journal/journald-rate-limit.c delete mode 100644 src/journal/journald-rate-limit.h delete mode 100644 src/journal/journald-server.c delete mode 100644 src/journal/journald-server.h delete mode 100644 src/journal/journald-stream.c delete mode 100644 src/journal/journald-stream.h delete mode 100644 src/journal/journald-syslog.c delete mode 100644 src/journal/journald-syslog.h delete mode 100644 src/journal/journald-wall.c delete mode 100644 src/journal/journald-wall.h delete mode 100644 src/journal/journald.c delete mode 100644 src/journal/journald.conf delete mode 100644 src/journal/lookup3.c delete mode 100644 src/journal/lookup3.h delete mode 100644 src/journal/mmap-cache.c delete mode 100644 src/journal/mmap-cache.h delete mode 100644 src/journal/sd-journal.c delete mode 100644 src/journal/test-audit-type.c delete mode 100644 src/journal/test-catalog.c delete mode 100644 src/journal/test-compress-benchmark.c delete mode 100644 src/journal/test-compress.c delete mode 100644 src/journal/test-journal-enum.c delete mode 100644 src/journal/test-journal-flush.c delete mode 100644 src/journal/test-journal-init.c delete mode 100644 src/journal/test-journal-interleaving.c delete mode 100644 src/journal/test-journal-match.c delete mode 100644 src/journal/test-journal-send.c delete mode 100644 src/journal/test-journal-stream.c delete mode 100644 src/journal/test-journal-syslog.c delete mode 100644 src/journal/test-journal-verify.c delete mode 100644 src/journal/test-journal.c delete mode 100644 src/journal/test-mmap-cache.c delete mode 100644 src/kernel-install/50-depmod.install delete mode 100644 src/kernel-install/90-loaderentry.install delete mode 120000 src/kernel-install/Makefile delete mode 100644 src/kernel-install/kernel-install create mode 100644 src/libbasic/Makefile create mode 100644 src/libbasic/include/Makefile create mode 100644 src/libbasic/include/basic/.gitignore create mode 100644 src/libbasic/include/basic/Makefile create mode 100644 src/libbasic/include/basic/MurmurHash2.h create mode 100644 src/libbasic/include/basic/af-list.h create mode 100644 src/libbasic/include/basic/alloc-util.h create mode 100644 src/libbasic/include/basic/architecture.h create mode 100644 src/libbasic/include/basic/arphrd-list.h create mode 100644 src/libbasic/include/basic/async.h create mode 100644 src/libbasic/include/basic/audit-util.h create mode 100644 src/libbasic/include/basic/barrier.h create mode 100644 src/libbasic/include/basic/bitmap.h create mode 100644 src/libbasic/include/basic/blkid-util.h create mode 100644 src/libbasic/include/basic/btrfs-ctree.h create mode 100644 src/libbasic/include/basic/btrfs-util.h create mode 100644 src/libbasic/include/basic/build.h create mode 100644 src/libbasic/include/basic/bus-label.h create mode 100644 src/libbasic/include/basic/calendarspec.h create mode 100644 src/libbasic/include/basic/cap-list.h create mode 100644 src/libbasic/include/basic/capability-util.h create mode 100644 src/libbasic/include/basic/cgroup-util.h create mode 100644 src/libbasic/include/basic/chattr-util.h create mode 100644 src/libbasic/include/basic/clock-util.h create mode 100644 src/libbasic/include/basic/conf-files.h create mode 100644 src/libbasic/include/basic/copy.h create mode 100644 src/libbasic/include/basic/cpu-set-util.h create mode 100644 src/libbasic/include/basic/def.h create mode 100644 src/libbasic/include/basic/device-nodes.h create mode 100644 src/libbasic/include/basic/dirent-util.h create mode 100644 src/libbasic/include/basic/env-util.h create mode 100644 src/libbasic/include/basic/errno-list.h create mode 100644 src/libbasic/include/basic/escape.h create mode 100644 src/libbasic/include/basic/ether-addr-util.h create mode 100644 src/libbasic/include/basic/exit-status.h create mode 100644 src/libbasic/include/basic/extract-word.h create mode 100644 src/libbasic/include/basic/fd-util.h create mode 100644 src/libbasic/include/basic/fileio-label.h create mode 100644 src/libbasic/include/basic/fileio.h create mode 100644 src/libbasic/include/basic/formats-util.h create mode 100644 src/libbasic/include/basic/fs-util.h create mode 100644 src/libbasic/include/basic/glob-util.h create mode 100644 src/libbasic/include/basic/gunicode.h create mode 100644 src/libbasic/include/basic/hash-funcs.h create mode 100644 src/libbasic/include/basic/hashmap.h create mode 100644 src/libbasic/include/basic/hexdecoct.h create mode 100644 src/libbasic/include/basic/hostname-util.h create mode 100644 src/libbasic/include/basic/in-addr-util.h create mode 100644 src/libbasic/include/basic/io-util.h create mode 100644 src/libbasic/include/basic/ioprio.h create mode 100644 src/libbasic/include/basic/label.h create mode 100644 src/libbasic/include/basic/list.h create mode 100644 src/libbasic/include/basic/locale-util.h create mode 100644 src/libbasic/include/basic/lockfile-util.h create mode 100644 src/libbasic/include/basic/log.h create mode 100644 src/libbasic/include/basic/login-util.h create mode 100644 src/libbasic/include/basic/macro.h create mode 100644 src/libbasic/include/basic/memfd-util.h create mode 100644 src/libbasic/include/basic/mempool.h create mode 100644 src/libbasic/include/basic/missing.h create mode 100644 src/libbasic/include/basic/missing_syscall.h create mode 100644 src/libbasic/include/basic/mkdir.h create mode 100644 src/libbasic/include/basic/mount-util.h create mode 100644 src/libbasic/include/basic/nss-util.h create mode 100644 src/libbasic/include/basic/ordered-set.h create mode 100644 src/libbasic/include/basic/parse-util.h create mode 100644 src/libbasic/include/basic/path-util.h create mode 100644 src/libbasic/include/basic/prioq.h create mode 100644 src/libbasic/include/basic/proc-cmdline.h create mode 100644 src/libbasic/include/basic/process-util.h create mode 100644 src/libbasic/include/basic/random-util.h create mode 100644 src/libbasic/include/basic/ratelimit.h create mode 100644 src/libbasic/include/basic/raw-clone.h create mode 100644 src/libbasic/include/basic/refcnt.h create mode 100644 src/libbasic/include/basic/replace-var.h create mode 100644 src/libbasic/include/basic/rlimit-util.h create mode 100644 src/libbasic/include/basic/rm-rf.h create mode 100644 src/libbasic/include/basic/securebits.h create mode 100644 src/libbasic/include/basic/selinux-util.h create mode 100644 src/libbasic/include/basic/set.h create mode 100644 src/libbasic/include/basic/sigbus.h create mode 100644 src/libbasic/include/basic/signal-util.h create mode 100644 src/libbasic/include/basic/siphash24.h create mode 100644 src/libbasic/include/basic/smack-util.h create mode 100644 src/libbasic/include/basic/socket-util.h create mode 100644 src/libbasic/include/basic/sparse-endian.h create mode 100644 src/libbasic/include/basic/special.h create mode 100644 src/libbasic/include/basic/stat-util.h create mode 100644 src/libbasic/include/basic/stdio-util.h create mode 100644 src/libbasic/include/basic/strbuf.h create mode 100644 src/libbasic/include/basic/string-table.h create mode 100644 src/libbasic/include/basic/string-util.h create mode 100644 src/libbasic/include/basic/strv.h create mode 100644 src/libbasic/include/basic/strxcpyx.h create mode 100644 src/libbasic/include/basic/syslog-util.h create mode 100644 src/libbasic/include/basic/terminal-util.h create mode 100644 src/libbasic/include/basic/time-util.h create mode 100644 src/libbasic/include/basic/umask-util.h create mode 100644 src/libbasic/include/basic/unaligned.h create mode 100644 src/libbasic/include/basic/unit-name.h create mode 100644 src/libbasic/include/basic/user-util.h create mode 100644 src/libbasic/include/basic/utf8.h create mode 100644 src/libbasic/include/basic/util.h create mode 100644 src/libbasic/include/basic/verbs.h create mode 100644 src/libbasic/include/basic/virt.h create mode 100644 src/libbasic/include/basic/web-util.h create mode 100644 src/libbasic/include/basic/xattr-util.h create mode 100644 src/libbasic/include/basic/xml.h create mode 100644 src/libbasic/src/Makefile create mode 100644 src/libbasic/src/MurmurHash2.c create mode 100644 src/libbasic/src/af-list.c create mode 100644 src/libbasic/src/alloc-util.c create mode 100644 src/libbasic/src/architecture.c create mode 100644 src/libbasic/src/arphrd-list.c create mode 100644 src/libbasic/src/async.c create mode 100644 src/libbasic/src/audit-util.c create mode 100644 src/libbasic/src/barrier.c create mode 100644 src/libbasic/src/bitmap.c create mode 100644 src/libbasic/src/btrfs-util.c create mode 100644 src/libbasic/src/bus-label.c create mode 100644 src/libbasic/src/calendarspec.c create mode 100644 src/libbasic/src/cap-list.c create mode 100644 src/libbasic/src/capability-util.c create mode 100644 src/libbasic/src/cgroup-util.c create mode 100644 src/libbasic/src/chattr-util.c create mode 100644 src/libbasic/src/clock-util.c create mode 100644 src/libbasic/src/conf-files.c create mode 100644 src/libbasic/src/copy.c create mode 100644 src/libbasic/src/cpu-set-util.c create mode 100644 src/libbasic/src/device-nodes.c create mode 100644 src/libbasic/src/dirent-util.c create mode 100644 src/libbasic/src/env-util.c create mode 100644 src/libbasic/src/errno-list.c create mode 100644 src/libbasic/src/escape.c create mode 100644 src/libbasic/src/ether-addr-util.c create mode 100644 src/libbasic/src/exit-status.c create mode 100644 src/libbasic/src/extract-word.c create mode 100644 src/libbasic/src/fd-util.c create mode 100644 src/libbasic/src/fileio-label.c create mode 100644 src/libbasic/src/fileio.c create mode 100644 src/libbasic/src/fs-util.c create mode 100644 src/libbasic/src/glob-util.c create mode 100644 src/libbasic/src/gunicode.c create mode 100644 src/libbasic/src/hash-funcs.c create mode 100644 src/libbasic/src/hashmap.c create mode 100644 src/libbasic/src/hexdecoct.c create mode 100644 src/libbasic/src/hostname-util.c create mode 100644 src/libbasic/src/in-addr-util.c create mode 100644 src/libbasic/src/io-util.c create mode 100644 src/libbasic/src/label.c create mode 100644 src/libbasic/src/locale-util.c create mode 100644 src/libbasic/src/lockfile-util.c create mode 100644 src/libbasic/src/log.c create mode 100644 src/libbasic/src/login-util.c create mode 100644 src/libbasic/src/memfd-util.c create mode 100644 src/libbasic/src/mempool.c create mode 100644 src/libbasic/src/mkdir-label.c create mode 100644 src/libbasic/src/mkdir.c create mode 100644 src/libbasic/src/mount-util.c create mode 100644 src/libbasic/src/ordered-set.c create mode 100644 src/libbasic/src/parse-util.c create mode 100644 src/libbasic/src/path-util.c create mode 100644 src/libbasic/src/prioq.c create mode 100644 src/libbasic/src/proc-cmdline.c create mode 100644 src/libbasic/src/process-util.c create mode 100644 src/libbasic/src/random-util.c create mode 100644 src/libbasic/src/ratelimit.c create mode 100644 src/libbasic/src/replace-var.c create mode 100644 src/libbasic/src/rlimit-util.c create mode 100644 src/libbasic/src/rm-rf.c create mode 100644 src/libbasic/src/selinux-util.c create mode 100644 src/libbasic/src/sigbus.c create mode 100644 src/libbasic/src/signal-util.c create mode 100644 src/libbasic/src/siphash24.c create mode 100644 src/libbasic/src/smack-util.c create mode 100644 src/libbasic/src/socket-label.c create mode 100644 src/libbasic/src/socket-util.c create mode 100644 src/libbasic/src/stat-util.c create mode 100644 src/libbasic/src/strbuf.c create mode 100644 src/libbasic/src/string-table.c create mode 100644 src/libbasic/src/string-util.c create mode 100644 src/libbasic/src/strv.c create mode 100644 src/libbasic/src/strxcpyx.c create mode 100644 src/libbasic/src/syslog-util.c create mode 100644 src/libbasic/src/terminal-util.c create mode 100644 src/libbasic/src/time-util.c create mode 100644 src/libbasic/src/unit-name.c create mode 100644 src/libbasic/src/user-util.c create mode 100644 src/libbasic/src/utf8.c create mode 100644 src/libbasic/src/util.c create mode 100644 src/libbasic/src/verbs.c create mode 100644 src/libbasic/src/virt.c create mode 100644 src/libbasic/src/web-util.c create mode 100644 src/libbasic/src/xattr-util.c create mode 100644 src/libbasic/src/xml.c create mode 100644 src/libfirewall/Makefile create mode 100644 src/libfirewall/firewall-util.c create mode 100644 src/libfirewall/firewall-util.h create mode 100644 src/libshared/Makefile create mode 100644 src/libshared/include/shared/acl-util.h create mode 100644 src/libshared/include/shared/acpi-fpdt.h create mode 100644 src/libshared/include/shared/apparmor-util.h create mode 100644 src/libshared/include/shared/ask-password-api.h create mode 100644 src/libshared/include/shared/base-filesystem.h create mode 100644 src/libshared/include/shared/boot-timestamps.h create mode 100644 src/libshared/include/shared/bus-unit-util.h create mode 100644 src/libshared/include/shared/bus-util.h create mode 100644 src/libshared/include/shared/cgroup-show.h create mode 100644 src/libshared/include/shared/clean-ipc.h create mode 100644 src/libshared/include/shared/condition.h create mode 100644 src/libshared/include/shared/conf-parser.h create mode 100644 src/libshared/include/shared/dev-setup.h create mode 100644 src/libshared/include/shared/dns-domain.h create mode 100644 src/libshared/include/shared/dropin.h create mode 100644 src/libshared/include/shared/efivars.h create mode 100644 src/libshared/include/shared/fdset.h create mode 100644 src/libshared/include/shared/fstab-util.h create mode 100644 src/libshared/include/shared/gcrypt-util.h create mode 100644 src/libshared/include/shared/generator.h create mode 100644 src/libshared/include/shared/gpt.h create mode 100644 src/libshared/include/shared/ima-util.h create mode 100644 src/libshared/include/shared/import-util.h create mode 100644 src/libshared/include/shared/initreq.h create mode 100644 src/libshared/include/shared/install-printf.h create mode 100644 src/libshared/include/shared/install.h create mode 100644 src/libshared/include/shared/logs-show.h create mode 100644 src/libshared/include/shared/machine-image.h create mode 100644 src/libshared/include/shared/machine-pool.h create mode 100644 src/libshared/include/shared/output-mode.h create mode 100644 src/libshared/include/shared/pager.h create mode 100644 src/libshared/include/shared/path-lookup.h create mode 100644 src/libshared/include/shared/ptyfwd.h create mode 100644 src/libshared/include/shared/resolve-util.h create mode 100644 src/libshared/include/shared/seccomp-util.h create mode 100644 src/libshared/include/shared/sleep-config.h create mode 100644 src/libshared/include/shared/spawn-ask-password-agent.h create mode 100644 src/libshared/include/shared/spawn-polkit-agent.h create mode 100644 src/libshared/include/shared/specifier.h create mode 100644 src/libshared/include/shared/switch-root.h create mode 100644 src/libshared/include/shared/sysctl-util.h create mode 100644 src/libshared/include/shared/test-tables.h create mode 100644 src/libshared/include/shared/tests.h create mode 100644 src/libshared/include/shared/udev-util.h create mode 100644 src/libshared/include/shared/uid-range.h create mode 100644 src/libshared/include/shared/utmp-wtmp.h create mode 100644 src/libshared/include/shared/vlan-util.h create mode 100644 src/libshared/include/shared/watchdog.h create mode 100644 src/libshared/src/Makefile create mode 100644 src/libshared/src/acl-util.c create mode 100644 src/libshared/src/acpi-fpdt.c create mode 100644 src/libshared/src/apparmor-util.c create mode 100644 src/libshared/src/ask-password-api.c create mode 100644 src/libshared/src/base-filesystem.c create mode 100644 src/libshared/src/boot-timestamps.c create mode 100644 src/libshared/src/bus-unit-util.c create mode 100644 src/libshared/src/bus-util.c create mode 100644 src/libshared/src/cgroup-show.c create mode 100644 src/libshared/src/clean-ipc.c create mode 100644 src/libshared/src/condition.c create mode 100644 src/libshared/src/conf-parser.c create mode 100644 src/libshared/src/dev-setup.c create mode 100644 src/libshared/src/dns-domain.c create mode 100644 src/libshared/src/dropin.c create mode 100644 src/libshared/src/efivars.c create mode 100644 src/libshared/src/fdset.c create mode 100644 src/libshared/src/fstab-util.c create mode 100644 src/libshared/src/gcrypt-util.c create mode 100644 src/libshared/src/generator.c create mode 100644 src/libshared/src/ima-util.c create mode 100644 src/libshared/src/import-util.c create mode 100644 src/libshared/src/install-printf.c create mode 100644 src/libshared/src/install.c create mode 100644 src/libshared/src/logs-show.c create mode 100644 src/libshared/src/machine-image.c create mode 100644 src/libshared/src/machine-pool.c create mode 100644 src/libshared/src/output-mode.c create mode 100644 src/libshared/src/pager.c create mode 100644 src/libshared/src/path-lookup.c create mode 100644 src/libshared/src/ptyfwd.c create mode 100644 src/libshared/src/resolve-util.c create mode 100644 src/libshared/src/seccomp-util.c create mode 100644 src/libshared/src/sleep-config.c create mode 100644 src/libshared/src/spawn-ask-password-agent.c create mode 100644 src/libshared/src/spawn-polkit-agent.c create mode 100644 src/libshared/src/specifier.c create mode 100644 src/libshared/src/switch-root.c create mode 100644 src/libshared/src/sysctl-util.c create mode 100644 src/libshared/src/tests.c create mode 100644 src/libshared/src/uid-range.c create mode 100644 src/libshared/src/utmp-wtmp.c create mode 100644 src/libshared/src/vlan-util.c create mode 100644 src/libshared/src/watchdog.c mode change 120000 => 100644 src/libsystemd-network/Makefile delete mode 100644 src/libsystemd-network/arp-util.c delete mode 100644 src/libsystemd-network/arp-util.h delete mode 100644 src/libsystemd-network/dhcp-identifier.c delete mode 100644 src/libsystemd-network/dhcp-identifier.h delete mode 100644 src/libsystemd-network/dhcp-internal.h delete mode 100644 src/libsystemd-network/dhcp-lease-internal.h delete mode 100644 src/libsystemd-network/dhcp-network.c delete mode 100644 src/libsystemd-network/dhcp-option.c delete mode 100644 src/libsystemd-network/dhcp-packet.c delete mode 100644 src/libsystemd-network/dhcp-protocol.h delete mode 100644 src/libsystemd-network/dhcp-server-internal.h delete mode 100644 src/libsystemd-network/dhcp6-internal.h delete mode 100644 src/libsystemd-network/dhcp6-lease-internal.h delete mode 100644 src/libsystemd-network/dhcp6-network.c delete mode 100644 src/libsystemd-network/dhcp6-option.c delete mode 100644 src/libsystemd-network/dhcp6-protocol.h delete mode 100644 src/libsystemd-network/icmp6-util.c delete mode 100644 src/libsystemd-network/icmp6-util.h create mode 120000 src/libsystemd-network/include/systemd-network/_sd-common.h create mode 100644 src/libsystemd-network/include/systemd-network/arp-util.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp-identifier.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp-lease-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp-protocol.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp-server-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp6-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp6-lease-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/dhcp6-protocol.h create mode 100644 src/libsystemd-network/include/systemd-network/icmp6-util.h create mode 100644 src/libsystemd-network/include/systemd-network/lldp-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/lldp-neighbor.h create mode 100644 src/libsystemd-network/include/systemd-network/lldp-network.h create mode 100644 src/libsystemd-network/include/systemd-network/ndisc-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/ndisc-router.h create mode 100644 src/libsystemd-network/include/systemd-network/network-internal.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-dhcp-client.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-dhcp-lease.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-dhcp-server.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-dhcp6-client.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-dhcp6-lease.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-ipv4acd.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-ipv4ll.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-lldp.h create mode 100644 src/libsystemd-network/include/systemd-network/sd-ndisc.h delete mode 100644 src/libsystemd-network/lldp-internal.h delete mode 100644 src/libsystemd-network/lldp-neighbor.c delete mode 100644 src/libsystemd-network/lldp-neighbor.h delete mode 100644 src/libsystemd-network/lldp-network.c delete mode 100644 src/libsystemd-network/lldp-network.h delete mode 100644 src/libsystemd-network/ndisc-internal.h delete mode 100644 src/libsystemd-network/ndisc-router.c delete mode 100644 src/libsystemd-network/ndisc-router.h delete mode 100644 src/libsystemd-network/network-internal.c delete mode 100644 src/libsystemd-network/network-internal.h delete mode 100644 src/libsystemd-network/sd-dhcp-client.c delete mode 100644 src/libsystemd-network/sd-dhcp-lease.c delete mode 100644 src/libsystemd-network/sd-dhcp-server.c delete mode 100644 src/libsystemd-network/sd-dhcp6-client.c delete mode 100644 src/libsystemd-network/sd-dhcp6-lease.c delete mode 100644 src/libsystemd-network/sd-ipv4acd.c delete mode 100644 src/libsystemd-network/sd-ipv4ll.c delete mode 100644 src/libsystemd-network/sd-lldp.c delete mode 100644 src/libsystemd-network/sd-ndisc.c create mode 100644 src/libsystemd-network/src/Makefile create mode 100644 src/libsystemd-network/src/arp-util.c create mode 100644 src/libsystemd-network/src/dhcp-identifier.c create mode 100644 src/libsystemd-network/src/dhcp-network.c create mode 100644 src/libsystemd-network/src/dhcp-option.c create mode 100644 src/libsystemd-network/src/dhcp-packet.c create mode 100644 src/libsystemd-network/src/dhcp6-network.c create mode 100644 src/libsystemd-network/src/dhcp6-option.c create mode 100644 src/libsystemd-network/src/icmp6-util.c create mode 100644 src/libsystemd-network/src/lldp-neighbor.c create mode 100644 src/libsystemd-network/src/lldp-network.c create mode 100644 src/libsystemd-network/src/ndisc-router.c create mode 100644 src/libsystemd-network/src/network-internal.c create mode 100644 src/libsystemd-network/src/sd-dhcp-client.c create mode 100644 src/libsystemd-network/src/sd-dhcp-lease.c create mode 100644 src/libsystemd-network/src/sd-dhcp-server.c create mode 100644 src/libsystemd-network/src/sd-dhcp6-client.c create mode 100644 src/libsystemd-network/src/sd-dhcp6-lease.c create mode 100644 src/libsystemd-network/src/sd-ipv4acd.c create mode 100644 src/libsystemd-network/src/sd-ipv4ll.c create mode 100644 src/libsystemd-network/src/sd-lldp.c create mode 100644 src/libsystemd-network/src/sd-ndisc.c delete mode 100644 src/libsystemd-network/test-acd.c delete mode 100644 src/libsystemd-network/test-dhcp-client.c delete mode 100644 src/libsystemd-network/test-dhcp-option.c delete mode 100644 src/libsystemd-network/test-dhcp-server.c delete mode 100644 src/libsystemd-network/test-dhcp6-client.c delete mode 100644 src/libsystemd-network/test-ipv4ll-manual.c delete mode 100644 src/libsystemd-network/test-ipv4ll.c delete mode 100644 src/libsystemd-network/test-lldp.c delete mode 100644 src/libsystemd-network/test-ndisc-rs.c create mode 100644 src/libsystemd-network/test/Makefile create mode 100644 src/libsystemd-network/test/test-acd.c create mode 100644 src/libsystemd-network/test/test-dhcp-client.c create mode 100644 src/libsystemd-network/test/test-dhcp-option.c create mode 100644 src/libsystemd-network/test/test-dhcp-server.c create mode 100644 src/libsystemd-network/test/test-dhcp6-client.c create mode 100644 src/libsystemd-network/test/test-ipv4ll-manual.c create mode 100644 src/libsystemd-network/test/test-ipv4ll.c create mode 100644 src/libsystemd-network/test/test-lldp.c create mode 100644 src/libsystemd-network/test/test-ndisc-rs.c mode change 120000 => 100644 src/libsystemd/Makefile create mode 100644 src/libsystemd/include/systemd/_sd-common.h create mode 100644 src/libsystemd/include/systemd/sd-bus-protocol.h create mode 100644 src/libsystemd/include/systemd/sd-bus-vtable.h create mode 100644 src/libsystemd/include/systemd/sd-bus.h create mode 100644 src/libsystemd/include/systemd/sd-daemon.h create mode 100644 src/libsystemd/include/systemd/sd-event.h create mode 100644 src/libsystemd/include/systemd/sd-id128.h create mode 100644 src/libsystemd/include/systemd/sd-journal.h create mode 100644 src/libsystemd/include/systemd/sd-login.h create mode 100644 src/libsystemd/include/systemd/sd-messages.h create mode 100644 src/libsystemd/include/systemd/sd-utf8.h create mode 100644 src/libsystemd/libsystemd-pkgconfig.xml create mode 100644 src/libsystemd/sd-bus-errors.xml create mode 100644 src/libsystemd/sd-bus.xml delete mode 100644 src/libsystemd/sd-bus/DIFFERENCES delete mode 100644 src/libsystemd/sd-bus/GVARIANT-SERIALIZATION delete mode 120000 src/libsystemd/sd-bus/Makefile delete mode 100644 src/libsystemd/sd-bus/PORTING-DBUS1 delete mode 100644 src/libsystemd/sd-bus/bus-bloom.c delete mode 100644 src/libsystemd/sd-bus/bus-bloom.h delete mode 100644 src/libsystemd/sd-bus/bus-common-errors.c delete mode 100644 src/libsystemd/sd-bus/bus-common-errors.h delete mode 100644 src/libsystemd/sd-bus/bus-container.c delete mode 100644 src/libsystemd/sd-bus/bus-container.h delete mode 100644 src/libsystemd/sd-bus/bus-control.c delete mode 100644 src/libsystemd/sd-bus/bus-control.h delete mode 100644 src/libsystemd/sd-bus/bus-convenience.c delete mode 100644 src/libsystemd/sd-bus/bus-creds.c delete mode 100644 src/libsystemd/sd-bus/bus-creds.h delete mode 100644 src/libsystemd/sd-bus/bus-dump.c delete mode 100644 src/libsystemd/sd-bus/bus-dump.h delete mode 100644 src/libsystemd/sd-bus/bus-error.c delete mode 100644 src/libsystemd/sd-bus/bus-error.h delete mode 100644 src/libsystemd/sd-bus/bus-gvariant.c delete mode 100644 src/libsystemd/sd-bus/bus-gvariant.h delete mode 100644 src/libsystemd/sd-bus/bus-internal.c delete mode 100644 src/libsystemd/sd-bus/bus-internal.h delete mode 100644 src/libsystemd/sd-bus/bus-introspect.c delete mode 100644 src/libsystemd/sd-bus/bus-introspect.h delete mode 100644 src/libsystemd/sd-bus/bus-kernel.c delete mode 100644 src/libsystemd/sd-bus/bus-kernel.h delete mode 100644 src/libsystemd/sd-bus/bus-match.c delete mode 100644 src/libsystemd/sd-bus/bus-match.h delete mode 100644 src/libsystemd/sd-bus/bus-message.c delete mode 100644 src/libsystemd/sd-bus/bus-message.h delete mode 100644 src/libsystemd/sd-bus/bus-objects.c delete mode 100644 src/libsystemd/sd-bus/bus-objects.h delete mode 100644 src/libsystemd/sd-bus/bus-protocol.h delete mode 100644 src/libsystemd/sd-bus/bus-signature.c delete mode 100644 src/libsystemd/sd-bus/bus-signature.h delete mode 100644 src/libsystemd/sd-bus/bus-slot.c delete mode 100644 src/libsystemd/sd-bus/bus-slot.h delete mode 100644 src/libsystemd/sd-bus/bus-socket.c delete mode 100644 src/libsystemd/sd-bus/bus-socket.h delete mode 100644 src/libsystemd/sd-bus/bus-track.c delete mode 100644 src/libsystemd/sd-bus/bus-track.h delete mode 100644 src/libsystemd/sd-bus/bus-type.c delete mode 100644 src/libsystemd/sd-bus/bus-type.h delete mode 100644 src/libsystemd/sd-bus/busctl-introspect.c delete mode 100644 src/libsystemd/sd-bus/busctl-introspect.h delete mode 100644 src/libsystemd/sd-bus/busctl.c delete mode 100644 src/libsystemd/sd-bus/kdbus.h delete mode 100644 src/libsystemd/sd-bus/sd-bus.c delete mode 100644 src/libsystemd/sd-bus/test-bus-benchmark.c delete mode 100644 src/libsystemd/sd-bus/test-bus-chat.c delete mode 100644 src/libsystemd/sd-bus/test-bus-cleanup.c delete mode 100644 src/libsystemd/sd-bus/test-bus-creds.c delete mode 100644 src/libsystemd/sd-bus/test-bus-error.c delete mode 100644 src/libsystemd/sd-bus/test-bus-gvariant.c delete mode 100644 src/libsystemd/sd-bus/test-bus-introspect.c delete mode 100644 src/libsystemd/sd-bus/test-bus-kernel-bloom.c delete mode 100644 src/libsystemd/sd-bus/test-bus-kernel.c delete mode 100644 src/libsystemd/sd-bus/test-bus-marshal.c delete mode 100644 src/libsystemd/sd-bus/test-bus-match.c delete mode 100644 src/libsystemd/sd-bus/test-bus-objects.c delete mode 100644 src/libsystemd/sd-bus/test-bus-server.c delete mode 100644 src/libsystemd/sd-bus/test-bus-signature.c delete mode 100644 src/libsystemd/sd-bus/test-bus-zero-copy.c create mode 100644 src/libsystemd/sd-daemon.xml delete mode 120000 src/libsystemd/sd-daemon/Makefile delete mode 100644 src/libsystemd/sd-daemon/sd-daemon.c delete mode 120000 src/libsystemd/sd-device/Makefile delete mode 100644 src/libsystemd/sd-device/device-enumerator-private.h delete mode 100644 src/libsystemd/sd-device/device-enumerator.c delete mode 100644 src/libsystemd/sd-device/device-internal.h delete mode 100644 src/libsystemd/sd-device/device-private.c delete mode 100644 src/libsystemd/sd-device/device-private.h delete mode 100644 src/libsystemd/sd-device/device-util.h delete mode 100644 src/libsystemd/sd-device/sd-device.c create mode 100644 src/libsystemd/sd-event.xml delete mode 120000 src/libsystemd/sd-event/Makefile delete mode 100644 src/libsystemd/sd-event/sd-event.c delete mode 100644 src/libsystemd/sd-event/test-event.c delete mode 120000 src/libsystemd/sd-hwdb/Makefile delete mode 100644 src/libsystemd/sd-hwdb/hwdb-internal.h delete mode 100644 src/libsystemd/sd-hwdb/hwdb-util.h delete mode 100644 src/libsystemd/sd-hwdb/sd-hwdb.c create mode 100644 src/libsystemd/sd-id128.xml delete mode 120000 src/libsystemd/sd-id128/Makefile delete mode 100644 src/libsystemd/sd-id128/id128-util.c delete mode 100644 src/libsystemd/sd-id128/id128-util.h delete mode 100644 src/libsystemd/sd-id128/sd-id128.c create mode 100644 src/libsystemd/sd-journal.xml create mode 100644 src/libsystemd/sd-login.xml delete mode 120000 src/libsystemd/sd-login/Makefile delete mode 100644 src/libsystemd/sd-login/sd-login.c delete mode 100644 src/libsystemd/sd-login/test-login.c delete mode 120000 src/libsystemd/sd-netlink/Makefile delete mode 100644 src/libsystemd/sd-netlink/local-addresses.c delete mode 100644 src/libsystemd/sd-netlink/local-addresses.h delete mode 100644 src/libsystemd/sd-netlink/netlink-internal.h delete mode 100644 src/libsystemd/sd-netlink/netlink-message.c delete mode 100644 src/libsystemd/sd-netlink/netlink-socket.c delete mode 100644 src/libsystemd/sd-netlink/netlink-types.c delete mode 100644 src/libsystemd/sd-netlink/netlink-types.h delete mode 100644 src/libsystemd/sd-netlink/netlink-util.c delete mode 100644 src/libsystemd/sd-netlink/netlink-util.h delete mode 100644 src/libsystemd/sd-netlink/rtnl-message.c delete mode 100644 src/libsystemd/sd-netlink/sd-netlink.c delete mode 100644 src/libsystemd/sd-netlink/test-local-addresses.c delete mode 100644 src/libsystemd/sd-netlink/test-netlink.c delete mode 120000 src/libsystemd/sd-network/Makefile delete mode 100644 src/libsystemd/sd-network/network-util.c delete mode 100644 src/libsystemd/sd-network/network-util.h delete mode 100644 src/libsystemd/sd-network/sd-network.c delete mode 120000 src/libsystemd/sd-path/Makefile delete mode 100644 src/libsystemd/sd-path/sd-path.c delete mode 120000 src/libsystemd/sd-resolve/Makefile delete mode 100644 src/libsystemd/sd-resolve/sd-resolve.c delete mode 100644 src/libsystemd/sd-resolve/test-resolve.c delete mode 120000 src/libsystemd/sd-utf8/Makefile delete mode 100644 src/libsystemd/sd-utf8/sd-utf8.c create mode 100644 src/libsystemd/sd_booted.xml create mode 100644 src/libsystemd/sd_bus_add_match.xml create mode 100644 src/libsystemd/sd_bus_creds_get_pid.xml create mode 100644 src/libsystemd/sd_bus_creds_new_from_pid.xml create mode 100644 src/libsystemd/sd_bus_default.xml create mode 100644 src/libsystemd/sd_bus_error.xml create mode 100644 src/libsystemd/sd_bus_error_add_map.xml create mode 100644 src/libsystemd/sd_bus_get_fd.xml create mode 100644 src/libsystemd/sd_bus_message_append.xml create mode 100644 src/libsystemd/sd_bus_message_append_array.xml create mode 100644 src/libsystemd/sd_bus_message_append_basic.xml create mode 100644 src/libsystemd/sd_bus_message_append_string_memfd.xml create mode 100644 src/libsystemd/sd_bus_message_append_strv.xml create mode 100644 src/libsystemd/sd_bus_message_get_cookie.xml create mode 100644 src/libsystemd/sd_bus_message_get_monotonic_usec.xml create mode 100644 src/libsystemd/sd_bus_message_read_basic.xml create mode 100644 src/libsystemd/sd_bus_negotiate_fds.xml create mode 100644 src/libsystemd/sd_bus_new.xml create mode 100644 src/libsystemd/sd_bus_path_encode.xml create mode 100644 src/libsystemd/sd_bus_process.xml create mode 100644 src/libsystemd/sd_bus_request_name.xml create mode 100644 src/libsystemd/sd_event_add_child.xml create mode 100644 src/libsystemd/sd_event_add_defer.xml create mode 100644 src/libsystemd/sd_event_add_io.xml create mode 100644 src/libsystemd/sd_event_add_signal.xml create mode 100644 src/libsystemd/sd_event_add_time.xml create mode 100644 src/libsystemd/sd_event_exit.xml create mode 100644 src/libsystemd/sd_event_get_fd-glib-example.c create mode 100644 src/libsystemd/sd_event_get_fd.xml create mode 100644 src/libsystemd/sd_event_new.xml create mode 100644 src/libsystemd/sd_event_now.xml create mode 100644 src/libsystemd/sd_event_run.xml create mode 100644 src/libsystemd/sd_event_set_watchdog.xml create mode 100644 src/libsystemd/sd_event_source_get_event.xml create mode 100644 src/libsystemd/sd_event_source_get_pending.xml create mode 100644 src/libsystemd/sd_event_source_set_description.xml create mode 100644 src/libsystemd/sd_event_source_set_enabled.xml create mode 100644 src/libsystemd/sd_event_source_set_prepare.xml create mode 100644 src/libsystemd/sd_event_source_set_priority.xml create mode 100644 src/libsystemd/sd_event_source_set_userdata.xml create mode 100644 src/libsystemd/sd_event_source_unref.xml create mode 100644 src/libsystemd/sd_event_wait.xml create mode 100644 src/libsystemd/sd_get_seats.xml create mode 100644 src/libsystemd/sd_id128_get_machine.xml create mode 100644 src/libsystemd/sd_id128_randomize.xml create mode 100644 src/libsystemd/sd_id128_to_string.xml create mode 100644 src/libsystemd/sd_is_fifo.xml create mode 100644 src/libsystemd/sd_journal_add_match.xml create mode 100644 src/libsystemd/sd_journal_enumerate_fields.xml create mode 100644 src/libsystemd/sd_journal_get_catalog.xml create mode 100644 src/libsystemd/sd_journal_get_cursor.xml create mode 100644 src/libsystemd/sd_journal_get_cutoff_realtime_usec.xml create mode 100644 src/libsystemd/sd_journal_get_data.xml create mode 100644 src/libsystemd/sd_journal_get_fd.xml create mode 100644 src/libsystemd/sd_journal_get_realtime_usec.xml create mode 100644 src/libsystemd/sd_journal_get_usage.xml create mode 100644 src/libsystemd/sd_journal_has_runtime_files.xml create mode 100644 src/libsystemd/sd_journal_next.xml create mode 100644 src/libsystemd/sd_journal_open.xml create mode 100644 src/libsystemd/sd_journal_print.xml create mode 100644 src/libsystemd/sd_journal_query_unique.xml create mode 100644 src/libsystemd/sd_journal_seek_head.xml create mode 100644 src/libsystemd/sd_journal_stream_fd.xml create mode 100644 src/libsystemd/sd_listen_fds.xml create mode 100644 src/libsystemd/sd_login_monitor_new.xml create mode 100644 src/libsystemd/sd_machine_get_class.xml create mode 100644 src/libsystemd/sd_notify.xml create mode 100644 src/libsystemd/sd_pid_get_session.xml create mode 100644 src/libsystemd/sd_seat_get_active.xml create mode 100644 src/libsystemd/sd_session_is_active.xml create mode 100644 src/libsystemd/sd_uid_get_state.xml create mode 100644 src/libsystemd/sd_watchdog_enabled.xml create mode 100644 src/libsystemd/src/Makefile create mode 100644 src/libsystemd/src/sd-bus/DIFFERENCES create mode 100644 src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION create mode 120000 src/libsystemd/src/sd-bus/Makefile create mode 100644 src/libsystemd/src/sd-bus/PORTING-DBUS1 create mode 100644 src/libsystemd/src/sd-bus/bus-bloom.c create mode 100644 src/libsystemd/src/sd-bus/bus-bloom.h create mode 100644 src/libsystemd/src/sd-bus/bus-common-errors.c create mode 100644 src/libsystemd/src/sd-bus/bus-common-errors.h create mode 100644 src/libsystemd/src/sd-bus/bus-container.c create mode 100644 src/libsystemd/src/sd-bus/bus-container.h create mode 100644 src/libsystemd/src/sd-bus/bus-control.c create mode 100644 src/libsystemd/src/sd-bus/bus-control.h create mode 100644 src/libsystemd/src/sd-bus/bus-convenience.c create mode 100644 src/libsystemd/src/sd-bus/bus-creds.c create mode 100644 src/libsystemd/src/sd-bus/bus-creds.h create mode 100644 src/libsystemd/src/sd-bus/bus-dump.c create mode 100644 src/libsystemd/src/sd-bus/bus-dump.h create mode 100644 src/libsystemd/src/sd-bus/bus-error.c create mode 100644 src/libsystemd/src/sd-bus/bus-error.h create mode 100644 src/libsystemd/src/sd-bus/bus-gvariant.c create mode 100644 src/libsystemd/src/sd-bus/bus-gvariant.h create mode 100644 src/libsystemd/src/sd-bus/bus-internal.c create mode 100644 src/libsystemd/src/sd-bus/bus-internal.h create mode 100644 src/libsystemd/src/sd-bus/bus-introspect.c create mode 100644 src/libsystemd/src/sd-bus/bus-introspect.h create mode 100644 src/libsystemd/src/sd-bus/bus-kernel.c create mode 100644 src/libsystemd/src/sd-bus/bus-kernel.h create mode 100644 src/libsystemd/src/sd-bus/bus-match.c create mode 100644 src/libsystemd/src/sd-bus/bus-match.h create mode 100644 src/libsystemd/src/sd-bus/bus-message.c create mode 100644 src/libsystemd/src/sd-bus/bus-message.h create mode 100644 src/libsystemd/src/sd-bus/bus-objects.c create mode 100644 src/libsystemd/src/sd-bus/bus-objects.h create mode 100644 src/libsystemd/src/sd-bus/bus-protocol.h create mode 100644 src/libsystemd/src/sd-bus/bus-signature.c create mode 100644 src/libsystemd/src/sd-bus/bus-signature.h create mode 100644 src/libsystemd/src/sd-bus/bus-slot.c create mode 100644 src/libsystemd/src/sd-bus/bus-slot.h create mode 100644 src/libsystemd/src/sd-bus/bus-socket.c create mode 100644 src/libsystemd/src/sd-bus/bus-socket.h create mode 100644 src/libsystemd/src/sd-bus/bus-track.c create mode 100644 src/libsystemd/src/sd-bus/bus-track.h create mode 100644 src/libsystemd/src/sd-bus/bus-type.c create mode 100644 src/libsystemd/src/sd-bus/bus-type.h create mode 100644 src/libsystemd/src/sd-bus/kdbus.h create mode 100644 src/libsystemd/src/sd-bus/sd-bus.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-benchmark.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-chat.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-cleanup.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-creds.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-error.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-gvariant.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-introspect.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-kernel.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-marshal.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-match.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-objects.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-server.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-signature.c create mode 100644 src/libsystemd/src/sd-bus/test-bus-zero-copy.c create mode 120000 src/libsystemd/src/sd-daemon/Makefile create mode 100644 src/libsystemd/src/sd-daemon/sd-daemon.c create mode 120000 src/libsystemd/src/sd-device/Makefile create mode 100644 src/libsystemd/src/sd-device/device-enumerator-private.h create mode 100644 src/libsystemd/src/sd-device/device-enumerator.c create mode 100644 src/libsystemd/src/sd-device/device-internal.h create mode 100644 src/libsystemd/src/sd-device/device-private.c create mode 100644 src/libsystemd/src/sd-device/device-private.h create mode 100644 src/libsystemd/src/sd-device/device-util.h create mode 100644 src/libsystemd/src/sd-device/sd-device.c create mode 100644 src/libsystemd/src/sd-device/sd-device.h create mode 120000 src/libsystemd/src/sd-event/Makefile create mode 100644 src/libsystemd/src/sd-event/sd-event.c create mode 100644 src/libsystemd/src/sd-event/test-event.c create mode 120000 src/libsystemd/src/sd-hwdb/Makefile create mode 100644 src/libsystemd/src/sd-hwdb/hwdb-internal.h create mode 100644 src/libsystemd/src/sd-hwdb/hwdb-util.h create mode 100644 src/libsystemd/src/sd-hwdb/sd-hwdb.c create mode 100644 src/libsystemd/src/sd-hwdb/sd-hwdb.h create mode 120000 src/libsystemd/src/sd-id128/Makefile create mode 100644 src/libsystemd/src/sd-id128/id128-util.c create mode 100644 src/libsystemd/src/sd-id128/id128-util.h create mode 100644 src/libsystemd/src/sd-id128/sd-id128.c create mode 100644 src/libsystemd/src/sd-journal/Makefile create mode 100644 src/libsystemd/src/sd-journal/audit-type.c create mode 100644 src/libsystemd/src/sd-journal/audit-type.h create mode 100644 src/libsystemd/src/sd-journal/catalog.c create mode 100644 src/libsystemd/src/sd-journal/catalog.h create mode 100644 src/libsystemd/src/sd-journal/compress.c create mode 100644 src/libsystemd/src/sd-journal/compress.h create mode 100644 src/libsystemd/src/sd-journal/fsprg.c create mode 100644 src/libsystemd/src/sd-journal/fsprg.h create mode 120000 src/libsystemd/src/sd-journal/gcrypt-util.c create mode 120000 src/libsystemd/src/sd-journal/gcrypt-util.h create mode 100644 src/libsystemd/src/sd-journal/journal-authenticate.c create mode 100644 src/libsystemd/src/sd-journal/journal-authenticate.h create mode 100644 src/libsystemd/src/sd-journal/journal-def.h create mode 100644 src/libsystemd/src/sd-journal/journal-file.c create mode 100644 src/libsystemd/src/sd-journal/journal-file.h create mode 100644 src/libsystemd/src/sd-journal/journal-internal.h create mode 100644 src/libsystemd/src/sd-journal/journal-send.c create mode 100644 src/libsystemd/src/sd-journal/journal-vacuum.c create mode 100644 src/libsystemd/src/sd-journal/journal-vacuum.h create mode 100644 src/libsystemd/src/sd-journal/journal-verify.c create mode 100644 src/libsystemd/src/sd-journal/journal-verify.h create mode 100644 src/libsystemd/src/sd-journal/lookup3.c create mode 100644 src/libsystemd/src/sd-journal/lookup3.h create mode 100644 src/libsystemd/src/sd-journal/mmap-cache.c create mode 100644 src/libsystemd/src/sd-journal/mmap-cache.h create mode 100644 src/libsystemd/src/sd-journal/sd-journal.c create mode 120000 src/libsystemd/src/sd-login/Makefile create mode 100644 src/libsystemd/src/sd-login/sd-login.c create mode 100644 src/libsystemd/src/sd-login/test-login.c create mode 120000 src/libsystemd/src/sd-netlink/Makefile create mode 100644 src/libsystemd/src/sd-netlink/local-addresses.c create mode 100644 src/libsystemd/src/sd-netlink/local-addresses.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-internal.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-message.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-socket.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-types.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-types.h create mode 100644 src/libsystemd/src/sd-netlink/netlink-util.c create mode 100644 src/libsystemd/src/sd-netlink/netlink-util.h create mode 100644 src/libsystemd/src/sd-netlink/rtnl-message.c create mode 100644 src/libsystemd/src/sd-netlink/sd-netlink.c create mode 100644 src/libsystemd/src/sd-netlink/sd-netlink.h create mode 100644 src/libsystemd/src/sd-netlink/test-local-addresses.c create mode 100644 src/libsystemd/src/sd-netlink/test-netlink.c create mode 120000 src/libsystemd/src/sd-network/Makefile create mode 100644 src/libsystemd/src/sd-network/network-util.c create mode 100644 src/libsystemd/src/sd-network/network-util.h create mode 100644 src/libsystemd/src/sd-network/sd-network.c create mode 100644 src/libsystemd/src/sd-network/sd-network.h create mode 120000 src/libsystemd/src/sd-resolve/Makefile create mode 100644 src/libsystemd/src/sd-resolve/sd-resolve.c create mode 100644 src/libsystemd/src/sd-resolve/sd-resolve.h create mode 100644 src/libsystemd/src/sd-resolve/test-resolve.c create mode 100644 src/libsystemd/src/sd-utf8/sd-utf8.c create mode 100644 src/libsystemd/src/subdir.mk create mode 100644 src/libsystemd/src/test.mk mode change 120000 => 100644 src/libudev/Makefile create mode 100644 src/libudev/include/libudev.h delete mode 100644 src/libudev/libudev-device-internal.h delete mode 100644 src/libudev/libudev-device-private.c delete mode 100644 src/libudev/libudev-device.c delete mode 100644 src/libudev/libudev-enumerate.c delete mode 100644 src/libudev/libudev-hwdb.c delete mode 100644 src/libudev/libudev-list.c delete mode 100644 src/libudev/libudev-monitor.c delete mode 100644 src/libudev/libudev-private.h delete mode 100644 src/libudev/libudev-queue.c delete mode 100644 src/libudev/libudev-util.c delete mode 100644 src/libudev/libudev.c delete mode 100644 src/libudev/libudev.h create mode 100644 src/libudev/libudev.xml create mode 100644 src/libudev/src/Makefile create mode 100644 src/libudev/src/libudev-device-internal.h create mode 100644 src/libudev/src/libudev-device-private.c create mode 100644 src/libudev/src/libudev-device.c create mode 100644 src/libudev/src/libudev-enumerate.c create mode 100644 src/libudev/src/libudev-hwdb.c create mode 100644 src/libudev/src/libudev-list.c create mode 100644 src/libudev/src/libudev-monitor.c create mode 100644 src/libudev/src/libudev-private.h create mode 100644 src/libudev/src/libudev-queue.c create mode 100644 src/libudev/src/libudev-util.c create mode 100644 src/libudev/src/libudev.c create mode 100644 src/libudev/src/udev.h create mode 100644 src/libudev/udev_device_get_syspath.xml create mode 100644 src/libudev/udev_device_has_tag.xml create mode 100644 src/libudev/udev_device_new_from_syspath.xml create mode 100644 src/libudev/udev_enumerate_add_match_subsystem.xml create mode 100644 src/libudev/udev_enumerate_new.xml create mode 100644 src/libudev/udev_enumerate_scan_devices.xml create mode 100644 src/libudev/udev_list_entry.xml create mode 100644 src/libudev/udev_monitor_filter_update.xml create mode 100644 src/libudev/udev_monitor_new_from_netlink.xml create mode 100644 src/libudev/udev_monitor_receive_device.xml create mode 100644 src/libudev/udev_new.xml delete mode 100644 src/locale/.gitignore delete mode 120000 src/locale/Makefile delete mode 100644 src/locale/kbd-model-map delete mode 100644 src/locale/keymap-util.c delete mode 100644 src/locale/keymap-util.h delete mode 100644 src/locale/language-fallback-map delete mode 100644 src/locale/localectl.c delete mode 100644 src/locale/localed.c delete mode 100644 src/locale/org.freedesktop.locale1.conf delete mode 100644 src/locale/org.freedesktop.locale1.policy.in delete mode 100644 src/locale/org.freedesktop.locale1.service delete mode 100644 src/locale/test-keymap-util.c delete mode 100644 src/login/.gitignore delete mode 100644 src/login/70-power-switch.rules delete mode 100644 src/login/70-uaccess.rules delete mode 100644 src/login/71-seat.rules.in delete mode 100644 src/login/73-seat-late.rules.in delete mode 120000 src/login/Makefile delete mode 100644 src/login/inhibit.c delete mode 100644 src/login/loginctl.c delete mode 100644 src/login/logind-acl.c delete mode 100644 src/login/logind-acl.h delete mode 100644 src/login/logind-action.c delete mode 100644 src/login/logind-action.h delete mode 100644 src/login/logind-button.c delete mode 100644 src/login/logind-button.h delete mode 100644 src/login/logind-core.c delete mode 100644 src/login/logind-dbus.c delete mode 100644 src/login/logind-device.c delete mode 100644 src/login/logind-device.h delete mode 100644 src/login/logind-gperf.gperf delete mode 100644 src/login/logind-inhibit.c delete mode 100644 src/login/logind-inhibit.h delete mode 100644 src/login/logind-seat-dbus.c delete mode 100644 src/login/logind-seat.c delete mode 100644 src/login/logind-seat.h delete mode 100644 src/login/logind-session-dbus.c delete mode 100644 src/login/logind-session-device.c delete mode 100644 src/login/logind-session-device.h delete mode 100644 src/login/logind-session.c delete mode 100644 src/login/logind-session.h delete mode 100644 src/login/logind-user-dbus.c delete mode 100644 src/login/logind-user.c delete mode 100644 src/login/logind-user.h delete mode 100644 src/login/logind-utmp.c delete mode 100644 src/login/logind.c delete mode 100644 src/login/logind.conf.in delete mode 100644 src/login/logind.h delete mode 100644 src/login/org.freedesktop.login1.conf delete mode 100644 src/login/org.freedesktop.login1.policy.in delete mode 100644 src/login/org.freedesktop.login1.service delete mode 100644 src/login/pam_systemd.c delete mode 100644 src/login/pam_systemd.sym delete mode 100644 src/login/sysfs-show.c delete mode 100644 src/login/sysfs-show.h delete mode 100644 src/login/systemd-user.m4 delete mode 100644 src/login/test-inhibit.c delete mode 100644 src/login/test-login-shared.c delete mode 100644 src/login/test-login-tables.c delete mode 120000 src/machine-id-setup/Makefile delete mode 100644 src/machine-id-setup/machine-id-setup-main.c delete mode 100644 src/machine/.gitignore delete mode 120000 src/machine/Makefile delete mode 100644 src/machine/image-dbus.c delete mode 100644 src/machine/image-dbus.h delete mode 100644 src/machine/machine-dbus.c delete mode 100644 src/machine/machine-dbus.h delete mode 100644 src/machine/machine.c delete mode 100644 src/machine/machine.h delete mode 100644 src/machine/machinectl.c delete mode 100644 src/machine/machined-dbus.c delete mode 100644 src/machine/machined.c delete mode 100644 src/machine/machined.h delete mode 100644 src/machine/operation.c delete mode 100644 src/machine/operation.h delete mode 100644 src/machine/org.freedesktop.machine1.conf delete mode 100644 src/machine/org.freedesktop.machine1.policy.in delete mode 100644 src/machine/org.freedesktop.machine1.service delete mode 100644 src/machine/test-machine-tables.c create mode 100644 src/manpages/daemon.xml create mode 100644 src/manpages/file-hierarchy.xml create mode 100644 src/manpages/hostname.xml create mode 100644 src/manpages/locale.conf.xml create mode 100644 src/manpages/localtime.xml create mode 100644 src/manpages/machine-id.xml create mode 100644 src/manpages/machine-info.xml create mode 100644 src/manpages/os-release.xml delete mode 120000 src/modules-load/Makefile delete mode 100644 src/modules-load/modules-load.c delete mode 100644 src/network/.gitignore delete mode 120000 src/network/Makefile delete mode 100644 src/network/networkctl.c delete mode 100644 src/network/networkd-address-pool.c delete mode 100644 src/network/networkd-address-pool.h delete mode 100644 src/network/networkd-address.c delete mode 100644 src/network/networkd-address.h delete mode 100644 src/network/networkd-brvlan.c delete mode 100644 src/network/networkd-brvlan.h delete mode 100644 src/network/networkd-conf.c delete mode 100644 src/network/networkd-conf.h delete mode 100644 src/network/networkd-dhcp4.c delete mode 100644 src/network/networkd-dhcp6.c delete mode 100644 src/network/networkd-fdb.c delete mode 100644 src/network/networkd-fdb.h delete mode 100644 src/network/networkd-gperf.gperf delete mode 100644 src/network/networkd-ipv4ll.c delete mode 100644 src/network/networkd-link-bus.c delete mode 100644 src/network/networkd-link.c delete mode 100644 src/network/networkd-link.h delete mode 100644 src/network/networkd-lldp-tx.c delete mode 100644 src/network/networkd-lldp-tx.h delete mode 100644 src/network/networkd-manager-bus.c delete mode 100644 src/network/networkd-manager.c delete mode 100644 src/network/networkd-ndisc.c delete mode 100644 src/network/networkd-ndisc.h delete mode 100644 src/network/networkd-netdev-bond.c delete mode 100644 src/network/networkd-netdev-bond.h delete mode 100644 src/network/networkd-netdev-bridge.c delete mode 100644 src/network/networkd-netdev-bridge.h delete mode 100644 src/network/networkd-netdev-dummy.c delete mode 100644 src/network/networkd-netdev-dummy.h delete mode 100644 src/network/networkd-netdev-gperf.gperf delete mode 100644 src/network/networkd-netdev-ipvlan.c delete mode 100644 src/network/networkd-netdev-ipvlan.h delete mode 100644 src/network/networkd-netdev-macvlan.c delete mode 100644 src/network/networkd-netdev-macvlan.h delete mode 100644 src/network/networkd-netdev-tunnel.c delete mode 100644 src/network/networkd-netdev-tunnel.h delete mode 100644 src/network/networkd-netdev-tuntap.c delete mode 100644 src/network/networkd-netdev-tuntap.h delete mode 100644 src/network/networkd-netdev-veth.c delete mode 100644 src/network/networkd-netdev-veth.h delete mode 100644 src/network/networkd-netdev-vlan.c delete mode 100644 src/network/networkd-netdev-vlan.h delete mode 100644 src/network/networkd-netdev-vrf.c delete mode 100644 src/network/networkd-netdev-vrf.h delete mode 100644 src/network/networkd-netdev-vxlan.c delete mode 100644 src/network/networkd-netdev-vxlan.h delete mode 100644 src/network/networkd-netdev.c delete mode 100644 src/network/networkd-netdev.h delete mode 100644 src/network/networkd-network-bus.c delete mode 100644 src/network/networkd-network-gperf.gperf delete mode 100644 src/network/networkd-network.c delete mode 100644 src/network/networkd-network.h delete mode 100644 src/network/networkd-route.c delete mode 100644 src/network/networkd-route.h delete mode 100644 src/network/networkd-util.c delete mode 100644 src/network/networkd-util.h delete mode 100644 src/network/networkd-wait-online-link.c delete mode 100644 src/network/networkd-wait-online-link.h delete mode 100644 src/network/networkd-wait-online-manager.c delete mode 100644 src/network/networkd-wait-online.c delete mode 100644 src/network/networkd-wait-online.h delete mode 100644 src/network/networkd.c delete mode 100644 src/network/networkd.h delete mode 100644 src/network/org.freedesktop.network1.conf delete mode 100644 src/network/org.freedesktop.network1.service delete mode 100644 src/network/test-network-tables.c delete mode 100644 src/network/test-network.c delete mode 100644 src/network/test-networkd-conf.c delete mode 120000 src/notify/Makefile delete mode 100644 src/notify/notify.c delete mode 100644 src/nspawn/.gitignore delete mode 120000 src/nspawn/Makefile delete mode 100644 src/nspawn/nspawn-cgroup.c delete mode 100644 src/nspawn/nspawn-cgroup.h delete mode 100644 src/nspawn/nspawn-expose-ports.c delete mode 100644 src/nspawn/nspawn-expose-ports.h delete mode 100644 src/nspawn/nspawn-gperf.gperf delete mode 100644 src/nspawn/nspawn-mount.c delete mode 100644 src/nspawn/nspawn-mount.h delete mode 100644 src/nspawn/nspawn-network.c delete mode 100644 src/nspawn/nspawn-network.h delete mode 100644 src/nspawn/nspawn-patch-uid.c delete mode 100644 src/nspawn/nspawn-patch-uid.h delete mode 100644 src/nspawn/nspawn-register.c delete mode 100644 src/nspawn/nspawn-register.h delete mode 100644 src/nspawn/nspawn-seccomp.c delete mode 100644 src/nspawn/nspawn-seccomp.h delete mode 100644 src/nspawn/nspawn-settings.c delete mode 100644 src/nspawn/nspawn-settings.h delete mode 100644 src/nspawn/nspawn-setuid.c delete mode 100644 src/nspawn/nspawn-setuid.h delete mode 100644 src/nspawn/nspawn-stub-pid1.c delete mode 100644 src/nspawn/nspawn-stub-pid1.h delete mode 100644 src/nspawn/nspawn.c delete mode 100644 src/nspawn/test-patch-uid.c mode change 120000 => 100644 src/nss-myhostname/Makefile create mode 100644 src/nss-myhostname/nss-myhostname.xml delete mode 120000 src/nss-mymachines/Makefile delete mode 100644 src/nss-mymachines/nss-mymachines.c delete mode 100644 src/nss-mymachines/nss-mymachines.sym delete mode 120000 src/nss-resolve/Makefile delete mode 100644 src/nss-resolve/nss-resolve.c delete mode 100644 src/nss-resolve/nss-resolve.sym delete mode 120000 src/path/Makefile delete mode 100644 src/path/path.c delete mode 120000 src/quotacheck/Makefile delete mode 100644 src/quotacheck/quotacheck.c delete mode 120000 src/random-seed/Makefile delete mode 100644 src/random-seed/random-seed.c delete mode 120000 src/rc-local-generator/Makefile delete mode 100644 src/rc-local-generator/rc-local-generator.c delete mode 120000 src/remount-fs/Makefile delete mode 100644 src/remount-fs/remount-fs.c delete mode 120000 src/reply-password/Makefile delete mode 100644 src/reply-password/reply-password.c delete mode 100644 src/resolve/.gitignore delete mode 120000 src/resolve/Makefile delete mode 100644 src/resolve/RFCs delete mode 100644 src/resolve/dns-type.c delete mode 100644 src/resolve/dns-type.h delete mode 100644 src/resolve/org.freedesktop.resolve1.conf delete mode 100644 src/resolve/org.freedesktop.resolve1.service delete mode 100644 src/resolve/resolv.conf delete mode 100644 src/resolve/resolve-tool.c delete mode 100644 src/resolve/resolved-bus.c delete mode 100644 src/resolve/resolved-bus.h delete mode 100644 src/resolve/resolved-conf.c delete mode 100644 src/resolve/resolved-conf.h delete mode 100644 src/resolve/resolved-def.h delete mode 100644 src/resolve/resolved-dns-answer.c delete mode 100644 src/resolve/resolved-dns-answer.h delete mode 100644 src/resolve/resolved-dns-cache.c delete mode 100644 src/resolve/resolved-dns-cache.h delete mode 100644 src/resolve/resolved-dns-dnssec.c delete mode 100644 src/resolve/resolved-dns-dnssec.h delete mode 100644 src/resolve/resolved-dns-packet.c delete mode 100644 src/resolve/resolved-dns-packet.h delete mode 100644 src/resolve/resolved-dns-query.c delete mode 100644 src/resolve/resolved-dns-query.h delete mode 100644 src/resolve/resolved-dns-question.c delete mode 100644 src/resolve/resolved-dns-question.h delete mode 100644 src/resolve/resolved-dns-rr.c delete mode 100644 src/resolve/resolved-dns-rr.h delete mode 100644 src/resolve/resolved-dns-scope.c delete mode 100644 src/resolve/resolved-dns-scope.h delete mode 100644 src/resolve/resolved-dns-search-domain.c delete mode 100644 src/resolve/resolved-dns-search-domain.h delete mode 100644 src/resolve/resolved-dns-server.c delete mode 100644 src/resolve/resolved-dns-server.h delete mode 100644 src/resolve/resolved-dns-stream.c delete mode 100644 src/resolve/resolved-dns-stream.h delete mode 100644 src/resolve/resolved-dns-stub.c delete mode 100644 src/resolve/resolved-dns-stub.h delete mode 100644 src/resolve/resolved-dns-synthesize.c delete mode 100644 src/resolve/resolved-dns-synthesize.h delete mode 100644 src/resolve/resolved-dns-transaction.c delete mode 100644 src/resolve/resolved-dns-transaction.h delete mode 100644 src/resolve/resolved-dns-trust-anchor.c delete mode 100644 src/resolve/resolved-dns-trust-anchor.h delete mode 100644 src/resolve/resolved-dns-zone.c delete mode 100644 src/resolve/resolved-dns-zone.h delete mode 100644 src/resolve/resolved-etc-hosts.c delete mode 100644 src/resolve/resolved-etc-hosts.h delete mode 100644 src/resolve/resolved-gperf.gperf delete mode 100644 src/resolve/resolved-link-bus.c delete mode 100644 src/resolve/resolved-link-bus.h delete mode 100644 src/resolve/resolved-link.c delete mode 100644 src/resolve/resolved-link.h delete mode 100644 src/resolve/resolved-llmnr.c delete mode 100644 src/resolve/resolved-llmnr.h delete mode 100644 src/resolve/resolved-manager.c delete mode 100644 src/resolve/resolved-manager.h delete mode 100644 src/resolve/resolved-mdns.c delete mode 100644 src/resolve/resolved-mdns.h delete mode 100644 src/resolve/resolved-resolv-conf.c delete mode 100644 src/resolve/resolved-resolv-conf.h delete mode 100644 src/resolve/resolved.c delete mode 100644 src/resolve/resolved.conf.in delete mode 100644 src/resolve/test-data/_443._tcp.fedoraproject.org.pkts delete mode 100644 src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts delete mode 100644 src/resolve/test-data/fake-caa.pkts delete mode 100644 src/resolve/test-data/fedoraproject.org.pkts delete mode 100644 src/resolve/test-data/gandi.net.pkts delete mode 100644 src/resolve/test-data/google.com.pkts delete mode 100644 src/resolve/test-data/kyhwana.org.pkts delete mode 100644 src/resolve/test-data/root.pkts delete mode 100644 src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts delete mode 100644 src/resolve/test-data/teamits.com.pkts delete mode 100644 src/resolve/test-data/zbyszek@fedoraproject.org.pkts delete mode 100644 src/resolve/test-dns-packet.c delete mode 100644 src/resolve/test-dnssec-complex.c delete mode 100644 src/resolve/test-dnssec.c delete mode 100644 src/resolve/test-resolve-tables.c delete mode 120000 src/rfkill/Makefile delete mode 100644 src/rfkill/rfkill.c delete mode 120000 src/run/Makefile delete mode 100644 src/run/run.c delete mode 120000 src/shared/Makefile delete mode 100644 src/shared/acl-util.c delete mode 100644 src/shared/acl-util.h delete mode 100644 src/shared/acpi-fpdt.c delete mode 100644 src/shared/acpi-fpdt.h delete mode 100644 src/shared/apparmor-util.c delete mode 100644 src/shared/apparmor-util.h delete mode 100644 src/shared/ask-password-api.c delete mode 100644 src/shared/ask-password-api.h delete mode 100644 src/shared/base-filesystem.c delete mode 100644 src/shared/base-filesystem.h delete mode 100644 src/shared/boot-timestamps.c delete mode 100644 src/shared/boot-timestamps.h delete mode 100644 src/shared/bus-unit-util.c delete mode 100644 src/shared/bus-unit-util.h delete mode 100644 src/shared/bus-util.c delete mode 100644 src/shared/bus-util.h delete mode 100644 src/shared/cgroup-show.c delete mode 100644 src/shared/cgroup-show.h delete mode 100644 src/shared/clean-ipc.c delete mode 100644 src/shared/clean-ipc.h delete mode 100644 src/shared/condition.c delete mode 100644 src/shared/condition.h delete mode 100644 src/shared/conf-parser.c delete mode 100644 src/shared/conf-parser.h delete mode 100644 src/shared/dev-setup.c delete mode 100644 src/shared/dev-setup.h delete mode 100644 src/shared/dns-domain.c delete mode 100644 src/shared/dns-domain.h delete mode 100644 src/shared/dropin.c delete mode 100644 src/shared/dropin.h delete mode 100644 src/shared/efivars.c delete mode 100644 src/shared/efivars.h delete mode 100644 src/shared/fdset.c delete mode 100644 src/shared/fdset.h delete mode 100644 src/shared/firewall-util.c delete mode 100644 src/shared/firewall-util.h delete mode 100644 src/shared/fstab-util.c delete mode 100644 src/shared/fstab-util.h delete mode 100644 src/shared/gcrypt-util.c delete mode 100644 src/shared/gcrypt-util.h delete mode 100644 src/shared/generator.c delete mode 100644 src/shared/generator.h delete mode 100644 src/shared/gpt.h delete mode 100644 src/shared/ima-util.c delete mode 100644 src/shared/ima-util.h delete mode 100644 src/shared/import-util.c delete mode 100644 src/shared/import-util.h delete mode 100644 src/shared/initreq.h delete mode 100644 src/shared/install-printf.c delete mode 100644 src/shared/install-printf.h delete mode 100644 src/shared/install.c delete mode 100644 src/shared/install.h delete mode 100644 src/shared/linux/auto_dev-ioctl.h delete mode 100644 src/shared/logs-show.c delete mode 100644 src/shared/logs-show.h delete mode 100644 src/shared/machine-image.c delete mode 100644 src/shared/machine-image.h delete mode 100644 src/shared/machine-pool.c delete mode 100644 src/shared/machine-pool.h delete mode 100644 src/shared/output-mode.c delete mode 100644 src/shared/output-mode.h delete mode 100644 src/shared/pager.c delete mode 100644 src/shared/pager.h delete mode 100644 src/shared/path-lookup.c delete mode 100644 src/shared/path-lookup.h delete mode 100644 src/shared/ptyfwd.c delete mode 100644 src/shared/ptyfwd.h delete mode 100644 src/shared/resolve-util.c delete mode 100644 src/shared/resolve-util.h delete mode 100644 src/shared/seccomp-util.c delete mode 100644 src/shared/seccomp-util.h delete mode 100644 src/shared/sleep-config.c delete mode 100644 src/shared/sleep-config.h delete mode 100644 src/shared/spawn-ask-password-agent.c delete mode 100644 src/shared/spawn-ask-password-agent.h delete mode 100644 src/shared/spawn-polkit-agent.c delete mode 100644 src/shared/spawn-polkit-agent.h delete mode 100644 src/shared/specifier.c delete mode 100644 src/shared/specifier.h delete mode 100644 src/shared/switch-root.c delete mode 100644 src/shared/switch-root.h delete mode 100644 src/shared/sysctl-util.c delete mode 100644 src/shared/sysctl-util.h delete mode 100644 src/shared/test-tables.h delete mode 100644 src/shared/tests.c delete mode 100644 src/shared/tests.h delete mode 100644 src/shared/udev-util.h delete mode 100644 src/shared/uid-range.c delete mode 100644 src/shared/uid-range.h delete mode 100644 src/shared/utmp-wtmp.c delete mode 100644 src/shared/utmp-wtmp.h delete mode 100644 src/shared/vlan-util.c delete mode 100644 src/shared/vlan-util.h delete mode 100644 src/shared/watchdog.c delete mode 100644 src/shared/watchdog.h delete mode 120000 src/sleep/Makefile delete mode 100644 src/sleep/sleep.c delete mode 120000 src/socket-proxy/Makefile delete mode 100644 src/socket-proxy/socket-proxyd.c delete mode 100644 src/stdio-bridge/stdio-bridge.c delete mode 120000 src/sysctl/Makefile delete mode 100644 src/sysctl/sysctl.c delete mode 120000 src/system-update-generator/Makefile delete mode 100644 src/system-update-generator/system-update-generator.c delete mode 120000 src/systemctl/Makefile delete mode 100644 src/systemctl/systemctl.c delete mode 100755 src/systemctl/systemd-sysv-install.SKELETON create mode 100644 src/systemd-ask-password/Makefile create mode 100644 src/systemd-ask-password/ask-password.c create mode 100644 src/systemd-ask-password/systemd-ask-password.completion.zsh create mode 100644 src/systemd-ask-password/systemd-ask-password.xml create mode 100644 src/systemd-cgls/Makefile create mode 100644 src/systemd-cgls/cgls.c create mode 100644 src/systemd-cgls/systemd-cgls.completion.bash create mode 100644 src/systemd-cgls/systemd-cgls.completion.zsh create mode 100644 src/systemd-cgls/systemd-cgls.xml create mode 100644 src/systemd-cgroups-agent/Makefile create mode 100644 src/systemd-cgroups-agent/cgroups-agent.c create mode 100644 src/systemd-cgtop/Makefile create mode 100644 src/systemd-cgtop/cgtop.c create mode 100644 src/systemd-cgtop/systemd-cgtop.completion.bash create mode 100644 src/systemd-cgtop/systemd-cgtop.completion.zsh create mode 100644 src/systemd-cgtop/systemd-cgtop.xml create mode 100644 src/systemd-cryptsetup/Makefile create mode 100644 src/systemd-cryptsetup/cryptsetup-generator.c create mode 100644 src/systemd-cryptsetup/cryptsetup-pre.target create mode 100644 src/systemd-cryptsetup/cryptsetup.c create mode 100644 src/systemd-cryptsetup/cryptsetup.target create mode 100644 src/systemd-cryptsetup/crypttab.xml create mode 100644 src/systemd-cryptsetup/systemd-cryptsetup-generator.xml create mode 100644 src/systemd-cryptsetup/systemd-cryptsetup@.service.xml create mode 100644 src/systemd-debug-generator/Makefile create mode 100644 src/systemd-debug-generator/debug-generator.c create mode 100644 src/systemd-debug-generator/systemd-debug-generator.xml create mode 100644 src/systemd-getty-generator/Makefile create mode 100644 src/systemd-getty-generator/getty-generator.c create mode 100644 src/systemd-getty-generator/systemd-getty-generator.xml create mode 100644 src/systemd-gpt-auto-generator/Makefile create mode 100644 src/systemd-gpt-auto-generator/gpt-auto-generator.c create mode 100644 src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml create mode 100644 src/systemd-initctl/Makefile create mode 100644 src/systemd-initctl/initctl.c create mode 100644 src/systemd-initctl/systemd-initctl.service.in create mode 100644 src/systemd-initctl/systemd-initctl.service.xml create mode 100644 src/systemd-initctl/systemd-initctl.socket create mode 100644 src/systemd-machine-id-setup/Makefile create mode 100644 src/systemd-machine-id-setup/machine-id-setup-main.c create mode 120000 src/systemd-machine-id-setup/machine-id-setup.c create mode 120000 src/systemd-machine-id-setup/machine-id-setup.h create mode 100644 src/systemd-machine-id-setup/systemd-machine-id-commit.service.xml create mode 100644 src/systemd-machine-id-setup/systemd-machine-id-setup.completion.zsh create mode 100644 src/systemd-machine-id-setup/systemd-machine-id-setup.xml create mode 100644 src/systemd-nspawn/.gitignore create mode 100644 src/systemd-nspawn/Makefile create mode 120000 src/systemd-nspawn/loopback-setup.c create mode 120000 src/systemd-nspawn/loopback-setup.h create mode 120000 src/systemd-nspawn/machine-id-setup.c create mode 120000 src/systemd-nspawn/machine-id-setup.h create mode 120000 src/systemd-nspawn/mount-setup.c create mode 120000 src/systemd-nspawn/mount-setup.h create mode 100644 src/systemd-nspawn/nspawn-cgroup.c create mode 100644 src/systemd-nspawn/nspawn-cgroup.h create mode 100644 src/systemd-nspawn/nspawn-expose-ports.c create mode 100644 src/systemd-nspawn/nspawn-expose-ports.h create mode 100644 src/systemd-nspawn/nspawn-gperf.gperf create mode 100644 src/systemd-nspawn/nspawn-mount.c create mode 100644 src/systemd-nspawn/nspawn-mount.h create mode 100644 src/systemd-nspawn/nspawn-network.c create mode 100644 src/systemd-nspawn/nspawn-network.h create mode 100644 src/systemd-nspawn/nspawn-patch-uid.c create mode 100644 src/systemd-nspawn/nspawn-patch-uid.h create mode 100644 src/systemd-nspawn/nspawn-register.c create mode 100644 src/systemd-nspawn/nspawn-register.h create mode 100644 src/systemd-nspawn/nspawn-seccomp.c create mode 100644 src/systemd-nspawn/nspawn-seccomp.h create mode 100644 src/systemd-nspawn/nspawn-settings.c create mode 100644 src/systemd-nspawn/nspawn-settings.h create mode 100644 src/systemd-nspawn/nspawn-setuid.c create mode 100644 src/systemd-nspawn/nspawn-setuid.h create mode 100644 src/systemd-nspawn/nspawn-stub-pid1.c create mode 100644 src/systemd-nspawn/nspawn-stub-pid1.h create mode 100644 src/systemd-nspawn/nspawn.c create mode 100644 src/systemd-nspawn/systemd-nspawn.completion.bash create mode 100644 src/systemd-nspawn/systemd-nspawn.completion.zsh create mode 100644 src/systemd-nspawn/systemd-nspawn.tmpfiles create mode 100644 src/systemd-nspawn/systemd-nspawn.xml create mode 100644 src/systemd-nspawn/systemd-nspawn@.service.in create mode 100644 src/systemd-nspawn/test-patch-uid.c create mode 100644 src/systemd-rc-local-generator/Makefile create mode 100644 src/systemd-rc-local-generator/rc-local-generator.c create mode 100644 src/systemd-remount-fs/Makefile create mode 120000 src/systemd-remount-fs/mount-setup.c create mode 120000 src/systemd-remount-fs/mount-setup.h create mode 100644 src/systemd-remount-fs/remount-fs.c create mode 100644 src/systemd-remount-fs/systemd-remount-fs.service.in create mode 100644 src/systemd-remount-fs/systemd-remount-fs.service.xml create mode 100644 src/systemd-reply-password/Makefile create mode 100644 src/systemd-reply-password/reply-password.c create mode 100644 src/systemd-socket-proxyd/Makefile create mode 100644 src/systemd-socket-proxyd/socket-proxyd.c create mode 100644 src/systemd-socket-proxyd/systemd-socket-proxyd.xml create mode 100644 src/systemd-stdio-bridge/Makefile create mode 100644 src/systemd-stdio-bridge/stdio-bridge.c create mode 100644 src/systemd-system-update-generator/Makefile create mode 100644 src/systemd-system-update-generator/system-update-generator.c create mode 100644 src/systemd-system-update-generator/systemd-system-update-generator.xml create mode 100644 src/systemd-timesyncd/.gitignore create mode 100644 src/systemd-timesyncd/90-timesyncd.preset create mode 100644 src/systemd-timesyncd/Makefile create mode 100644 src/systemd-timesyncd/systemd-timesyncd.service.in create mode 100644 src/systemd-timesyncd/systemd-timesyncd.service.xml create mode 100644 src/systemd-timesyncd/systemd-timesyncd.sysusers create mode 100644 src/systemd-timesyncd/timesyncd-conf.c create mode 100644 src/systemd-timesyncd/timesyncd-conf.h create mode 100644 src/systemd-timesyncd/timesyncd-gperf.gperf create mode 100644 src/systemd-timesyncd/timesyncd-manager.c create mode 100644 src/systemd-timesyncd/timesyncd-manager.h create mode 100644 src/systemd-timesyncd/timesyncd-server.c create mode 100644 src/systemd-timesyncd/timesyncd-server.h create mode 100644 src/systemd-timesyncd/timesyncd.c create mode 100644 src/systemd-timesyncd/timesyncd.conf.in create mode 100644 src/systemd-timesyncd/timesyncd.conf.xml create mode 100644 src/systemd-tty-ask-password-agent/Makefile create mode 100644 src/systemd-tty-ask-password-agent/systemd-ask-password-console.service.xml create mode 100644 src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.completion.zsh create mode 100644 src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.xml create mode 100644 src/systemd-tty-ask-password-agent/tty-ask-password-agent.c delete mode 120000 src/systemd/Makefile delete mode 100644 src/systemd/_sd-common.h delete mode 100644 src/systemd/sd-bus-protocol.h delete mode 100644 src/systemd/sd-bus-vtable.h delete mode 100644 src/systemd/sd-bus.h delete mode 100644 src/systemd/sd-daemon.h delete mode 100644 src/systemd/sd-device.h delete mode 100644 src/systemd/sd-dhcp-client.h delete mode 100644 src/systemd/sd-dhcp-lease.h delete mode 100644 src/systemd/sd-dhcp-server.h delete mode 100644 src/systemd/sd-dhcp6-client.h delete mode 100644 src/systemd/sd-dhcp6-lease.h delete mode 100644 src/systemd/sd-event.h delete mode 100644 src/systemd/sd-hwdb.h delete mode 100644 src/systemd/sd-id128.h delete mode 100644 src/systemd/sd-ipv4acd.h delete mode 100644 src/systemd/sd-ipv4ll.h delete mode 100644 src/systemd/sd-journal.h delete mode 100644 src/systemd/sd-lldp.h delete mode 100644 src/systemd/sd-login.h delete mode 100644 src/systemd/sd-messages.h delete mode 100644 src/systemd/sd-ndisc.h delete mode 100644 src/systemd/sd-netlink.h delete mode 100644 src/systemd/sd-network.h delete mode 100644 src/systemd/sd-path.h delete mode 100644 src/systemd/sd-resolve.h delete mode 100644 src/systemd/sd-utf8.h delete mode 120000 src/sysusers/Makefile delete mode 100644 src/sysusers/sysusers.c delete mode 120000 src/sysv-generator/Makefile delete mode 100644 src/sysv-generator/sysv-generator.c mode change 120000 => 100644 src/test/Makefile delete mode 100644 src/timedate/.gitignore delete mode 120000 src/timedate/Makefile delete mode 100644 src/timedate/org.freedesktop.timedate1.conf delete mode 100644 src/timedate/org.freedesktop.timedate1.policy.in delete mode 100644 src/timedate/org.freedesktop.timedate1.service delete mode 100644 src/timedate/timedatectl.c delete mode 100644 src/timedate/timedated.c delete mode 100644 src/timesync/.gitignore delete mode 120000 src/timesync/Makefile delete mode 100644 src/timesync/timesyncd-conf.c delete mode 100644 src/timesync/timesyncd-conf.h delete mode 100644 src/timesync/timesyncd-gperf.gperf delete mode 100644 src/timesync/timesyncd-manager.c delete mode 100644 src/timesync/timesyncd-manager.h delete mode 100644 src/timesync/timesyncd-server.c delete mode 100644 src/timesync/timesyncd-server.h delete mode 100644 src/timesync/timesyncd.c delete mode 100644 src/timesync/timesyncd.conf.in delete mode 120000 src/tmpfiles/Makefile delete mode 100644 src/tmpfiles/tmpfiles.c delete mode 120000 src/tty-ask-password-agent/Makefile delete mode 100644 src/tty-ask-password-agent/tty-ask-password-agent.c delete mode 100644 src/udev/.gitignore delete mode 100644 src/udev/.vimrc delete mode 120000 src/udev/Makefile delete mode 120000 src/udev/ata_id/Makefile delete mode 100644 src/udev/ata_id/ata_id.c delete mode 120000 src/udev/cdrom_id/Makefile delete mode 100644 src/udev/cdrom_id/cdrom_id.c delete mode 120000 src/udev/collect/Makefile delete mode 100644 src/udev/collect/collect.c delete mode 120000 src/udev/mtd_probe/Makefile delete mode 100644 src/udev/mtd_probe/mtd_probe.c delete mode 100644 src/udev/mtd_probe/mtd_probe.h delete mode 100644 src/udev/mtd_probe/probe_smartmedia.c delete mode 100644 src/udev/net/.gitignore delete mode 120000 src/udev/net/Makefile delete mode 100644 src/udev/net/ethtool-util.c delete mode 100644 src/udev/net/ethtool-util.h delete mode 100644 src/udev/net/link-config-gperf.gperf delete mode 100644 src/udev/net/link-config.c delete mode 100644 src/udev/net/link-config.h delete mode 100644 src/udev/scsi_id/.gitignore delete mode 120000 src/udev/scsi_id/Makefile delete mode 100644 src/udev/scsi_id/README delete mode 100644 src/udev/scsi_id/scsi.h delete mode 100644 src/udev/scsi_id/scsi_id.c delete mode 100644 src/udev/scsi_id/scsi_id.h delete mode 100644 src/udev/scsi_id/scsi_serial.c delete mode 100644 src/udev/udev-builtin-blkid.c delete mode 100644 src/udev/udev-builtin-btrfs.c delete mode 100644 src/udev/udev-builtin-hwdb.c delete mode 100644 src/udev/udev-builtin-input_id.c delete mode 100644 src/udev/udev-builtin-keyboard.c delete mode 100644 src/udev/udev-builtin-kmod.c delete mode 100644 src/udev/udev-builtin-net_id.c delete mode 100644 src/udev/udev-builtin-net_setup_link.c delete mode 100644 src/udev/udev-builtin-path_id.c delete mode 100644 src/udev/udev-builtin-uaccess.c delete mode 100644 src/udev/udev-builtin-usb_id.c delete mode 100644 src/udev/udev-builtin.c delete mode 100644 src/udev/udev-ctrl.c delete mode 100644 src/udev/udev-event.c delete mode 100644 src/udev/udev-node.c delete mode 100644 src/udev/udev-rules.c delete mode 100644 src/udev/udev-watch.c delete mode 100644 src/udev/udev.conf delete mode 100644 src/udev/udev.h delete mode 100644 src/udev/udev.pc.in delete mode 100644 src/udev/udevadm-control.c delete mode 100644 src/udev/udevadm-hwdb.c delete mode 100644 src/udev/udevadm-info.c delete mode 100644 src/udev/udevadm-monitor.c delete mode 100644 src/udev/udevadm-settle.c delete mode 100644 src/udev/udevadm-test-builtin.c delete mode 100644 src/udev/udevadm-test.c delete mode 100644 src/udev/udevadm-trigger.c delete mode 100644 src/udev/udevadm-util.c delete mode 100644 src/udev/udevadm-util.h delete mode 100644 src/udev/udevadm.c delete mode 100644 src/udev/udevd.c delete mode 120000 src/udev/v4l_id/Makefile delete mode 100644 src/udev/v4l_id/v4l_id.c delete mode 120000 src/update-done/Makefile delete mode 100644 src/update-done/update-done.c delete mode 120000 src/update-utmp/Makefile delete mode 100644 src/update-utmp/update-utmp.c delete mode 120000 src/user-sessions/Makefile delete mode 100644 src/user-sessions/user-sessions.c delete mode 100644 src/vconsole/.gitignore delete mode 100644 src/vconsole/90-vconsole.rules.in delete mode 120000 src/vconsole/Makefile delete mode 100644 src/vconsole/vconsole-setup.c create mode 100644 src/zsh-completion/_sd_hosts_or_user_at_host create mode 100644 src/zsh-completion/_sd_machines create mode 100644 src/zsh-completion/_sd_outputmodes create mode 100644 src/zsh-completion/_sd_unit_files (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 9d07505194..77e29b6c40 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,12 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# # This file is part of systemd. # -# Copyright 2010 Lennart Poettering +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by @@ -14,15 +20,50 @@ # # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk -# This file is a dirty trick to simplify compilation from within -# emacs. This file is not intended to be distributed. So, don't touch -# it, even better ignore it! - -all: - $(MAKE) -C .. - -clean: - $(MAKE) -C .. clean +nested.subdirs += busctl +nested.subdirs += grp-boot +nested.subdirs += grp-coredump +nested.subdirs += grp-hostname +nested.subdirs += grp-initprogs +nested.subdirs += grp-journal +nested.subdirs += grp-locale +nested.subdirs += grp-login +nested.subdirs += grp-machine +nested.subdirs += grp-network +nested.subdirs += grp-resolve +nested.subdirs += grp-system +nested.subdirs += grp-timedate +nested.subdirs += grp-udev +nested.subdirs += grp-utils +nested.subdirs += libbasic +nested.subdirs += libfirewall +nested.subdirs += libshared +nested.subdirs += libsystemd +nested.subdirs += libsystemd-network +nested.subdirs += libudev +nested.subdirs += nss-myhostname +nested.subdirs += systemd-ask-password +nested.subdirs += systemd-cgls +nested.subdirs += systemd-cgroups-agent +nested.subdirs += systemd-cgtop +nested.subdirs += systemd-cryptsetup +nested.subdirs += systemd-debug-generator +nested.subdirs += systemd-getty-generator +nested.subdirs += systemd-gpt-auto-generator +nested.subdirs += systemd-initctl +nested.subdirs += systemd-machine-id-setup +nested.subdirs += systemd-nspawn +nested.subdirs += systemd-rc-local-generator +nested.subdirs += systemd-remount-fs +nested.subdirs += systemd-reply-password +nested.subdirs += systemd-socket-proxyd +nested.subdirs += systemd-stdio-bridge +nested.subdirs += systemd-system-update-generator +nested.subdirs += systemd-timesyncd +nested.subdirs += systemd-tty-ask-password-agent +nested.subdirs += test -.PHONY: all clean +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/ac-power/Makefile b/src/ac-power/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/ac-power/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c deleted file mode 100644 index c5277884a8..0000000000 --- a/src/ac-power/ac-power.c +++ /dev/null @@ -1,35 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "util.h" - -int main(int argc, char *argv[]) { - int r; - - /* This is mostly intended to be used for scripts which want - * to detect whether AC power is plugged in or not. */ - - r = on_ac_power(); - if (r < 0) { - log_error_errno(r, "Failed to read AC status: %m"); - return EXIT_FAILURE; - } - - return r != 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/activate/Makefile b/src/activate/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/activate/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/activate/activate.c b/src/activate/activate.c deleted file mode 100644 index a0cfc22000..0000000000 --- a/src/activate/activate.c +++ /dev/null @@ -1,545 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "escape.h" -#include "fd-util.h" -#include "log.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 int arg_socket_type = SOCK_STREAM; -static char** arg_args = NULL; -static char** arg_setenv = NULL; -static char **arg_fdnames = NULL; -static bool arg_inetd = false; - -static int add_epoll(int epoll_fd, int fd) { - struct epoll_event ev = { - .events = EPOLLIN - }; - int r; - - assert(epoll_fd >= 0); - assert(fd >= 0); - - ev.data.fd = fd; - r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); - if (r < 0) - return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); - - return 0; -} - -static int open_sockets(int *epoll_fd, bool accept) { - char **address; - int n, fd, r; - int count = 0; - - n = sd_listen_fds(true); - if (n < 0) - return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); - if (n > 0) { - log_info("Received %i descriptors via the environment.", n); - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - r = fd_cloexec(fd, arg_accept); - if (r < 0) - return r; - - count++; - } - } - - /* Close logging and all other descriptors */ - if (arg_listen) { - int except[3 + n]; - - for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++) - except[fd] = fd; - - log_close(); - close_all_fds(except, 3 + n); - } - - /** Note: we leak some fd's on error here. I doesn't matter - * much, since the program will exit immediately anyway, but - * would be a pain to fix. - */ - - STRV_FOREACH(address, arg_listen) { - fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept*SOCK_CLOEXEC)); - if (fd < 0) { - log_open(); - return log_error_errno(fd, "Failed to open '%s': %m", *address); - } - - assert(fd == SD_LISTEN_FDS_START + count); - count++; - } - - if (arg_listen) - log_open(); - - *epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (*epoll_fd < 0) - return log_error_errno(errno, "Failed to create epoll object: %m"); - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) { - _cleanup_free_ char *name = NULL; - - getsockname_pretty(fd, &name); - log_info("Listening on %s as %i.", strna(name), fd); - - r = add_epoll(*epoll_fd, fd); - if (r < 0) - return r; - } - - return count; -} - -static int exec_process(const char* name, char **argv, char **env, int start_fd, int n_fds) { - - _cleanup_strv_free_ char **envp = NULL; - _cleanup_free_ char *joined = NULL; - unsigned n_env = 0, length; - const char *tocopy; - char **s; - int r; - - if (arg_inetd && n_fds != 1) { - log_error("--inetd only supported for single file descriptors."); - return -EINVAL; - } - - length = strv_length(arg_setenv); - - /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */ - envp = new0(char *, length + 8); - if (!envp) - return log_oom(); - - STRV_FOREACH(s, arg_setenv) { - - if (strchr(*s, '=')) { - char *k; - - k = strdup(*s); - if (!k) - return log_oom(); - - envp[n_env++] = k; - } else { - _cleanup_free_ char *p; - const char *n; - - p = strappend(*s, "="); - if (!p) - return log_oom(); - - n = strv_find_prefix(env, p); - if (!n) - continue; - - envp[n_env] = strdup(n); - if (!envp[n_env]) - return log_oom(); - - n_env++; - } - } - - FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") { - const char *n; - - n = strv_find_prefix(env, tocopy); - if (!n) - continue; - - envp[n_env] = strdup(n); - if (!envp[n_env]) - return log_oom(); - - n_env++; - } - - if (arg_inetd) { - assert(n_fds == 1); - - r = dup2(start_fd, STDIN_FILENO); - if (r < 0) - return log_error_errno(errno, "Failed to dup connection to stdin: %m"); - - r = dup2(start_fd, STDOUT_FILENO); - if (r < 0) - return log_error_errno(errno, "Failed to dup connection to stdout: %m"); - - start_fd = safe_close(start_fd); - } else { - if (start_fd != SD_LISTEN_FDS_START) { - assert(n_fds == 1); - - r = dup2(start_fd, SD_LISTEN_FDS_START); - if (r < 0) - return log_error_errno(errno, "Failed to dup connection: %m"); - - safe_close(start_fd); - start_fd = SD_LISTEN_FDS_START; - } - - if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0) - return log_oom(); - - if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0) - return log_oom(); - - if (arg_fdnames) { - _cleanup_free_ char *names = NULL; - size_t len; - char *e; - int i; - - len = strv_length(arg_fdnames); - if (len == 1) - for (i = 1; i < n_fds; i++) { - r = strv_extend(&arg_fdnames, arg_fdnames[0]); - if (r < 0) - return log_error_errno(r, "Failed to extend strv: %m"); - } - else if (len != (unsigned) n_fds) - log_warning("The number of fd names is different than number of fds: %zu vs %d", - len, n_fds); - - names = strv_join(arg_fdnames, ":"); - if (!names) - return log_oom(); - - e = strappend("LISTEN_FDNAMES=", names); - if (!e) - return log_oom(); - - envp[n_env++] = e; - } - } - - joined = strv_join(argv, " "); - if (!joined) - return log_oom(); - - log_info("Execing %s (%s)", name, joined); - execvpe(name, argv, envp); - - return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined); -} - -static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) { - _cleanup_free_ char *joined = NULL; - pid_t parent_pid, child_pid; - - joined = strv_join(argv, " "); - if (!joined) - return log_oom(); - - parent_pid = getpid(); - - child_pid = fork(); - if (child_pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - /* In the child */ - if (child_pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Make sure the child goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - exec_process(child, argv, env, fd, 1); - _exit(EXIT_FAILURE); - } - - log_info("Spawned %s (%s) as PID %d", child, joined, child_pid); - return 0; -} - -static int do_accept(const char* name, char **argv, char **envp, int fd) { - _cleanup_free_ char *local = NULL, *peer = NULL; - _cleanup_close_ int fd_accepted = -1; - - fd_accepted = accept4(fd, NULL, NULL, 0); - if (fd_accepted < 0) - return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd); - - getsockname_pretty(fd_accepted, &local); - getpeername_pretty(fd_accepted, true, &peer); - log_info("Connection from %s to %s", strna(peer), strna(local)); - - return fork_and_exec_process(name, argv, envp, fd_accepted); -} - -/* SIGCHLD handler. */ -static void sigchld_hdl(int sig) { - PROTECT_ERRNO; - - for (;;) { - siginfo_t si; - int r; - - si.si_pid = 0; - r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); - if (r < 0) { - if (errno != ECHILD) - log_error_errno(errno, "Failed to reap children: %m"); - return; - } - if (si.si_pid == 0) - return; - - log_info("Child %d died with code %d", si.si_pid, si.si_status); - } -} - -static int install_chld_handler(void) { - static const struct sigaction act = { - .sa_flags = SA_NOCLDSTOP, - .sa_handler = sigchld_hdl, - }; - - int r; - - r = sigaction(SIGCHLD, &act, 0); - if (r < 0) - return log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); - - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Listen on sockets and launch child on connection.\n\n" - "Options:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " -l --listen=ADDR Listen for raw connections at ADDR\n" - " -d --datagram Listen on datagram instead of stream socket\n" - " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n" - " -a --accept Spawn separate child for each connection\n" - " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" - " --fdname=NAME[:NAME...] Specify names for file descriptors\n" - " --inetd Enable inetd file descriptor passing protocol\n" - "\n" - "Note: file descriptors from sd_listen_fds() will be passed through.\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_FDNAME, - ARG_SEQPACKET, - ARG_INETD, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "datagram", no_argument, NULL, 'd' }, - { "seqpacket", no_argument, NULL, ARG_SEQPACKET }, - { "listen", required_argument, NULL, 'l' }, - { "accept", no_argument, NULL, 'a' }, - { "setenv", required_argument, NULL, 'E' }, - { "environment", required_argument, NULL, 'E' }, /* legacy alias */ - { "fdname", required_argument, NULL, ARG_FDNAME }, - { "inetd", no_argument, NULL, ARG_INETD }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'l': - r = strv_extend(&arg_listen, optarg); - if (r < 0) - return log_oom(); - - break; - - case 'd': - if (arg_socket_type == SOCK_SEQPACKET) { - log_error("--datagram may not be combined with --seqpacket."); - return -EINVAL; - } - - arg_socket_type = SOCK_DGRAM; - break; - - case ARG_SEQPACKET: - if (arg_socket_type == SOCK_DGRAM) { - log_error("--seqpacket may not be combined with --datagram."); - return -EINVAL; - } - - arg_socket_type = SOCK_SEQPACKET; - break; - - case 'a': - arg_accept = true; - break; - - case 'E': - r = strv_extend(&arg_setenv, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_FDNAME: { - _cleanup_strv_free_ char **names; - char **s; - - names = strv_split(optarg, ":"); - if (!names) - return log_oom(); - - STRV_FOREACH(s, names) - if (!fdname_is_valid(*s)) { - _cleanup_free_ char *esc; - - esc = cescape(*s); - log_warning("File descriptor name \"%s\" is not valid.", esc); - } - - /* Empty optargs means one empty name */ - r = strv_extend_strv(&arg_fdnames, - strv_isempty(names) ? STRV_MAKE("") : names, - false); - if (r < 0) - return log_error_errno(r, "strv_extend_strv: %m"); - break; - } - - case ARG_INETD: - arg_inetd = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind == argc) { - log_error("%s: command to execute is missing.", - program_invocation_short_name); - return -EINVAL; - } - - if (arg_socket_type == SOCK_DGRAM && arg_accept) { - log_error("Datagram sockets do not accept connections. " - "The --datagram and --accept options may not be combined."); - return -EINVAL; - } - - arg_args = argv + optind; - - return 1 /* work to do */; -} - -int main(int argc, char **argv, char **envp) { - int r, n; - int epoll_fd = -1; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; - - r = install_chld_handler(); - if (r < 0) - return EXIT_FAILURE; - - n = open_sockets(&epoll_fd, arg_accept); - if (n < 0) - return EXIT_FAILURE; - if (n == 0) { - log_error("No sockets to listen on specified or passed in."); - return EXIT_FAILURE; - } - - for (;;) { - struct epoll_event event; - - r = epoll_wait(epoll_fd, &event, 1, -1); - if (r < 0) { - if (errno == EINTR) - continue; - - log_error_errno(errno, "epoll_wait() failed: %m"); - return EXIT_FAILURE; - } - - log_info("Communication attempt on fd %i.", event.data.fd); - if (arg_accept) { - r = do_accept(argv[optind], argv + optind, envp, event.data.fd); - if (r < 0) - return EXIT_FAILURE; - } else - break; - } - - exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n); - - return EXIT_SUCCESS; -} diff --git a/src/analyze/.gitignore b/src/analyze/.gitignore deleted file mode 100644 index 752ea236c8..0000000000 --- a/src/analyze/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/systemd-analyze diff --git a/src/analyze/Makefile b/src/analyze/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/analyze/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c deleted file mode 100644 index 5fd3ee49eb..0000000000 --- a/src/analyze/analyze-verify.c +++ /dev/null @@ -1,304 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "alloc-util.h" -#include "analyze-verify.h" -#include "bus-error.h" -#include "bus-util.h" -#include "log.h" -#include "manager.h" -#include "pager.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; - - _cleanup_strv_free_ char **ans = NULL; - int r; - - STRV_FOREACH(filename, filenames) { - char *t; - - t = dirname_malloc(*filename); - if (!t) - return -ENOMEM; - - r = strv_consume(&ans, t); - if (r < 0) - return r; - } - - assert_se(strv_uniq(ans)); - - r = strv_extend(&ans, ""); - if (r < 0) - return r; - - *var = strv_join(ans, ":"); - if (!*var) - return -ENOMEM; - - return 0; -} - -static int verify_socket(Unit *u) { - int r; - - assert(u); - - if (u->type != UNIT_SOCKET) - return 0; - - /* Cannot run this without the service being around */ - - /* This makes sure instance is created if necessary. */ - r = socket_instantiate_service(SOCKET(u)); - if (r < 0) { - log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m"); - return r; - } - - /* This checks both type of sockets */ - if (UNIT_ISSET(SOCKET(u)->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); - log_unit_debug(u, "Using %s", UNIT(service)->id); - - if (UNIT(service)->load_state != UNIT_LOADED) { - log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id); - return -ENOENT; - } - } - - return 0; -} - -static int verify_executable(Unit *u, ExecCommand *exec) { - if (exec == NULL) - return 0; - - if (access(exec->path, X_OK) < 0) - return log_unit_error_errno(u, errno, "Command %s is not executable: %m", exec->path); - - return 0; -} - -static int verify_executables(Unit *u) { - ExecCommand *exec; - int r = 0, k; - unsigned i; - - assert(u); - - exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : - u->type == UNIT_MOUNT ? MOUNT(u)->control_command : - u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; - k = verify_executable(u, exec); - if (k < 0 && r == 0) - r = k; - - if (u->type == UNIT_SERVICE) - for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { - k = verify_executable(u, SERVICE(u)->exec_command[i]); - if (k < 0 && r == 0) - r = k; - } - - if (u->type == UNIT_SOCKET) - for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { - k = verify_executable(u, SOCKET(u)->exec_command[i]); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static int verify_documentation(Unit *u, bool check_man) { - char **p; - int r = 0, k; - - STRV_FOREACH(p, u->documentation) { - log_unit_debug(u, "Found documentation item: %s", *p); - - if (check_man && startswith(*p, "man:")) { - k = show_man_page(*p + 4, true); - if (k != 0) { - if (k < 0) - log_unit_error_errno(u, r, "Can't show %s: %m", *p); - else { - log_unit_error_errno(u, r, "man %s command failed with code %d", *p + 4, k); - k = -ENOEXEC; - } - if (r == 0) - r = k; - } - } - } - - /* Check remote URLs? */ - - return r; -} - -static int verify_unit(Unit *u, bool check_man) { - _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; - int r, k; - - assert(u); - - if (log_get_max_level() >= LOG_DEBUG) - 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, &err, NULL); - if (r < 0) - 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) - r = k; - - k = verify_executables(u); - if (k < 0 && r == 0) - r = k; - - k = verify_documentation(u, check_man); - if (k < 0 && r == 0) - r = k; - - return r; -} - -int verify_units(char **filenames, UnitFileScope scope, bool check_man) { - _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; - _cleanup_free_ char *var = NULL; - Manager *m = NULL; - FILE *serial = NULL; - FDSet *fdset = NULL; - char **filename; - int r = 0, k; - - Unit *units[strv_length(filenames)]; - int i, count = 0; - - if (strv_isempty(filenames)) - return 0; - - /* set the path */ - r = generate_path(&var, filenames); - if (r < 0) - return log_error_errno(r, "Failed to generate unit load path: %m"); - - assert_se(set_unit_path(var) >= 0); - - r = manager_new(scope, true, &m); - if (r < 0) - return log_error_errno(r, "Failed to initialize manager: %m"); - - log_debug("Starting manager..."); - - r = manager_startup(m, serial, fdset); - if (r < 0) { - log_error_errno(r, "Failed to start manager: %m"); - goto finish; - } - - manager_clear_jobs(m); - - log_debug("Loading remaining units from the command line..."); - - STRV_FOREACH(filename, filenames) { - _cleanup_free_ char *prepared = NULL; - - log_debug("Handling %s...", *filename); - - 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) - r = k; - } else - count++; - } - - for (i = 0; i < count; i++) { - k = verify_unit(units[i], check_man); - if (k < 0 && r == 0) - r = k; - } - -finish: - manager_free(m); - - return r; -} diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h deleted file mode 100644 index d8204dc69c..0000000000 --- a/src/analyze/analyze-verify.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include - -#include "path-lookup.h" - -int verify_units(char **filenames, UnitFileScope scope, bool check_man); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c deleted file mode 100644 index 66830695f3..0000000000 --- a/src/analyze/analyze.c +++ /dev/null @@ -1,1485 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2013 Lennart Poettering - Copyright 2013 Simon Peeters - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "analyze-verify.h" -#include "bus-error.h" -#include "bus-unit-util.h" -#include "bus-util.h" -#include "glob-util.h" -#include "hashmap.h" -#include "locale-util.h" -#include "log.h" -#include "pager.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) - -#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0)) - -#define svg(...) printf(__VA_ARGS__) - -#define svg_bar(class, x1, x2, y) \ - svg(" \n", \ - (class), \ - SCALE_X * (x1), SCALE_Y * (y), \ - SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0) - -#define svg_text(b, x, y, format, ...) \ - do { \ - svg(" ", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \ - svg(format, ## __VA_ARGS__); \ - svg("\n"); \ - } while (false) - -static enum dot { - DEP_ALL, - DEP_ORDER, - DEP_REQUIRE -} arg_dot = DEP_ALL; -static char** arg_dot_from_patterns = NULL; -static char** arg_dot_to_patterns = NULL; -static usec_t arg_fuzz = 0; -static bool arg_no_pager = false; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_user = false; -static bool arg_man = true; - -struct boot_times { - usec_t firmware_time; - usec_t loader_time; - usec_t kernel_time; - usec_t kernel_done_time; - usec_t initrd_time; - usec_t userspace_time; - usec_t finish_time; - usec_t security_start_time; - usec_t security_finish_time; - usec_t generators_start_time; - usec_t generators_finish_time; - usec_t unitsload_start_time; - usec_t unitsload_finish_time; - - /* - * If we're analyzing the user instance, all timestamps will be offset - * by its own start-up timestamp, which may be arbitrarily big. - * With "plot", this causes arbitrarily wide output SVG files which almost - * completely consist of empty space. Thus we cancel out this offset. - * - * This offset is subtracted from times above by acquire_boot_times(), - * but it still needs to be subtracted from unit-specific timestamps - * (so it is stored here for reference). - */ - usec_t reverse_offset; -}; - -struct unit_times { - char *name; - usec_t activating; - usec_t activated; - usec_t deactivated; - usec_t deactivating; - usec_t time; -}; - -struct host_info { - char *hostname; - char *kernel_name; - char *kernel_release; - char *kernel_version; - char *os_pretty_name; - char *virtualization; - char *architecture; -}; - -static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(property); - assert(val); - - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - path, - interface, - property, - &error, - 't', val); - - if (r < 0) { - log_error("Failed to parse reply: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(path); - assert(property); - assert(strv); - - r = sd_bus_get_property_strv( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - property, - &error, - strv); - if (r < 0) { - log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int compare_unit_time(const void *a, const void *b) { - return compare(((struct unit_times *)b)->time, - ((struct unit_times *)a)->time); -} - -static int compare_unit_start(const void *a, const void *b) { - return compare(((struct unit_times *)a)->activating, - ((struct unit_times *)b)->activating); -} - -static void free_unit_times(struct unit_times *t, unsigned n) { - struct unit_times *p; - - for (p = t; p < t + n; p++) - free(p->name); - - free(t); -} - -static void subtract_timestamp(usec_t *a, usec_t b) { - assert(a); - - if (*a > 0) { - assert(*a >= b); - *a -= b; - } -} - -static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { - static struct boot_times times; - static bool cached = false; - - if (cached) - goto finish; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - if (bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "FirmwareTimestampMonotonic", - ×.firmware_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LoaderTimestampMonotonic", - ×.loader_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KernelTimestamp", - ×.kernel_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "InitRDTimestampMonotonic", - ×.initrd_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UserspaceTimestampMonotonic", - ×.userspace_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "FinishTimestampMonotonic", - ×.finish_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SecurityStartTimestampMonotonic", - ×.security_start_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SecurityFinishTimestampMonotonic", - ×.security_finish_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GeneratorsStartTimestampMonotonic", - ×.generators_start_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GeneratorsFinishTimestampMonotonic", - ×.generators_finish_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitsLoadStartTimestampMonotonic", - ×.unitsload_start_time) < 0 || - bus_get_uint64_property(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitsLoadFinishTimestampMonotonic", - ×.unitsload_finish_time) < 0) - return -EIO; - - if (times.finish_time <= 0) { - log_error("Bootup is not yet finished. Please try again later."); - return -EINPROGRESS; - } - - if (arg_user) { - /* - * User-instance-specific timestamps processing - * (see comment to reverse_offset in struct boot_times). - */ - times.reverse_offset = times.userspace_time; - - times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0; - subtract_timestamp(×.finish_time, times.reverse_offset); - - subtract_timestamp(×.security_start_time, times.reverse_offset); - subtract_timestamp(×.security_finish_time, times.reverse_offset); - - subtract_timestamp(×.generators_start_time, times.reverse_offset); - subtract_timestamp(×.generators_finish_time, times.reverse_offset); - - subtract_timestamp(×.unitsload_start_time, times.reverse_offset); - subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); - } else { - if (times.initrd_time) - times.kernel_done_time = times.initrd_time; - else - times.kernel_done_time = times.userspace_time; - } - - cached = true; - -finish: - *bt = × - return 0; -} - -static void free_host_info(struct host_info *hi) { - - if (!hi) - return; - - free(hi->hostname); - free(hi->kernel_name); - free(hi->kernel_release); - free(hi->kernel_version); - free(hi->os_pretty_name); - free(hi->virtualization); - free(hi->architecture); - 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_(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; - size_t size = 0; - UnitInfo u; - - r = acquire_boot_times(bus, &boot_times); - if (r < 0) - goto fail; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, &reply, - NULL); - if (r < 0) { - log_error("Failed to list units: %s", bus_error_message(&error, -r)); - goto fail; - } - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) { - bus_log_parse_error(r); - goto fail; - } - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - struct unit_times *t; - - if (!GREEDY_REALLOC(unit_times, size, c+1)) { - r = log_oom(); - goto fail; - } - - t = unit_times+c; - t->name = NULL; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - if (bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveExitTimestampMonotonic", - &t->activating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveEnterTimestampMonotonic", - &t->activated) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveExitTimestampMonotonic", - &t->deactivating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveEnterTimestampMonotonic", - &t->deactivated) < 0) { - r = -EIO; - goto fail; - } - - subtract_timestamp(&t->activating, boot_times->reverse_offset); - subtract_timestamp(&t->activated, boot_times->reverse_offset); - subtract_timestamp(&t->deactivating, boot_times->reverse_offset); - subtract_timestamp(&t->deactivated, boot_times->reverse_offset); - - if (t->activated >= t->activating) - t->time = t->activated - t->activating; - else if (t->deactivated >= t->activating) - t->time = t->deactivated - t->activating; - else - t->time = 0; - - if (t->activating == 0) - continue; - - t->name = strdup(u.id); - if (t->name == NULL) { - r = log_oom(); - goto fail; - } - c++; - } - if (r < 0) { - bus_log_parse_error(r); - goto fail; - } - - *out = unit_times; - return c; - -fail: - if (unit_times) - free_unit_times(unit_times, (unsigned) c); - return r; -} - -static int acquire_host_info(sd_bus *bus, struct host_info **hi) { - 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) }, - { "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) }, - {} - }; - - _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(); - - r = bus_map_all_properties(bus, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - hostname_map, - host); - if (r < 0) - 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", - "/org/freedesktop/systemd1", - manager_map, - host); - if (r < 0) - return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r)); - - *hi = host; - host = NULL; - - return 0; -} - -static int pretty_boot_time(sd_bus *bus, char **_buf) { - char ts[FORMAT_TIMESPAN_MAX]; - struct boot_times *t; - static char buf[4096]; - size_t size; - char *ptr; - int r; - - r = acquire_boot_times(bus, &t); - if (r < 0) - return r; - - ptr = buf; - size = sizeof(buf); - - size = strpcpyf(&ptr, size, "Startup finished in "); - if (t->firmware_time) - size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC)); - if (t->loader_time) - size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC)); - if (t->kernel_time) - size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC)); - if (t->initrd_time > 0) - size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC)); - - size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC)); - strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); - - ptr = strdup(buf); - if (!ptr) - return log_oom(); - - *_buf = ptr; - return 0; -} - -static void svg_graph_box(double height, double begin, double end) { - long long i; - - /* outside box, fill */ - svg("\n", - SCALE_X * (end - begin), SCALE_Y * height); - - for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) { - /* lines for each second */ - if (i % 5000000 == 0) - svg(" \n" - " %.01fs\n", - SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i); - else if (i % 1000000 == 0) - svg(" \n" - " %.01fs\n", - SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i); - else - svg(" \n", - SCALE_X * i, SCALE_X * i, SCALE_Y * height); - } -} - -static int analyze_plot(sd_bus *bus) { - _cleanup_(free_host_infop) struct host_info *host = NULL; - struct unit_times *times; - struct boot_times *boot; - int n, m = 1, y=0; - double width; - _cleanup_free_ char *pretty_times = NULL; - struct unit_times *u; - - n = acquire_boot_times(bus, &boot); - if (n < 0) - return n; - - n = pretty_boot_time(bus, &pretty_times); - if (n < 0) - return n; - - n = acquire_host_info(bus, &host); - if (n < 0) - return n; - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - qsort(times, n, sizeof(struct unit_times), compare_unit_start); - - width = SCALE_X * (boot->firmware_time + boot->finish_time); - if (width < 800.0) - width = 800.0; - - if (boot->firmware_time > boot->loader_time) - m++; - if (boot->loader_time) { - m++; - if (width < 1000.0) - width = 1000.0; - } - if (boot->initrd_time) - m++; - if (boot->kernel_time) - m++; - - for (u = times; u < times + n; u++) { - double text_start, text_width; - - if (u->activating < boot->userspace_time || - u->activating > boot->finish_time) { - u->name = mfree(u->name); - continue; - } - - /* If the text cannot fit on the left side then - * increase the svg width so it fits on the right. - * TODO: calculate the text width more accurately */ - text_width = 8.0 * strlen(u->name); - text_start = (boot->firmware_time + u->activating) * SCALE_X; - if (text_width > text_start && text_width + text_start > width) - width = text_width + text_start; - - if (u->deactivated > u->activating && u->deactivated <= boot->finish_time - && u->activated == 0 && u->deactivating == 0) - u->activated = u->deactivating = u->deactivated; - if (u->activated < u->activating || u->activated > boot->finish_time) - u->activated = boot->finish_time; - if (u->deactivating < u->activated || u->activated > boot->finish_time) - u->deactivating = boot->finish_time; - if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time) - u->deactivated = boot->finish_time; - m++; - } - - svg("\n" - "\n"); - - svg("\n\n", - 80.0 + width, 150.0 + (m * SCALE_Y) + - 5 * SCALE_Y /* legend */); - - /* write some basic info as a comment, including some help */ - svg("\n" - "\n" - "\n" - "\n" - "\n\n" - "\n\n", VERSION); - - /* style sheet */ - svg("\n \n\n\n"); - - svg("\n"); - svg("%s", pretty_times); - svg("%s %s (%s %s %s) %s %s", - isempty(host->os_pretty_name) ? "GNU/Linux" : host->os_pretty_name, - strempty(host->hostname), - strempty(host->kernel_name), - strempty(host->kernel_release), - strempty(host->kernel_version), - strempty(host->architecture), - strempty(host->virtualization)); - - svg("\n", 20.0 + (SCALE_X * boot->firmware_time)); - svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); - - if (boot->firmware_time) { - svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y); - svg_text(true, -(double) boot->firmware_time, y, "firmware"); - y++; - } - if (boot->loader_time) { - svg_bar("loader", -(double) boot->loader_time, 0, y); - svg_text(true, -(double) boot->loader_time, y, "loader"); - y++; - } - if (boot->kernel_time) { - svg_bar("kernel", 0, boot->kernel_done_time, y); - svg_text(true, 0, y, "kernel"); - y++; - } - if (boot->initrd_time) { - svg_bar("initrd", boot->initrd_time, boot->userspace_time, y); - svg_text(true, boot->initrd_time, y, "initrd"); - y++; - } - svg_bar("active", boot->userspace_time, boot->finish_time, y); - svg_bar("security", boot->security_start_time, boot->security_finish_time, y); - svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y); - svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y); - svg_text(true, boot->userspace_time, y, "systemd"); - y++; - - for (u = times; u < times + n; u++) { - char ts[FORMAT_TIMESPAN_MAX]; - bool b; - - if (!u->name) - continue; - - svg_bar("activating", u->activating, u->activated, y); - svg_bar("active", u->activated, u->deactivating, y); - svg_bar("deactivating", u->deactivating, u->deactivated, y); - - /* place the text on the left if we have passed the half of the svg width */ - b = u->activating * SCALE_X < width / 2; - if (u->time) - svg_text(b, u->activating, y, "%s (%s)", - u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC)); - else - svg_text(b, u->activating, y, "%s", u->name); - y++; - } - - svg("\n"); - - /* Legend */ - svg("\n"); - y++; - svg_bar("activating", 0, 300000, y); - svg_text(true, 400000, y, "Activating"); - y++; - svg_bar("active", 0, 300000, y); - svg_text(true, 400000, y, "Active"); - y++; - svg_bar("deactivating", 0, 300000, y); - svg_text(true, 400000, y, "Deactivating"); - y++; - svg_bar("security", 0, 300000, y); - svg_text(true, 400000, y, "Setting up security module"); - y++; - svg_bar("generators", 0, 300000, y); - svg_text(true, 400000, y, "Generators"); - y++; - svg_bar("unitsload", 0, 300000, y); - svg_text(true, 400000, y, "Loading unit files"); - y++; - - svg("\n\n"); - - svg("\n"); - - free_unit_times(times, (unsigned) n); - - n = 0; - return n; -} - -static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches, - bool last, struct unit_times *times, struct boot_times *boot) { - unsigned int i; - char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX]; - - for (i = level; i != 0; i--) - printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE)); - - printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); - - if (times) { - if (times->time) - printf("%s%s @%s +%s%s", ansi_highlight_red(), name, - format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC), - format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC)); - else - printf("%s", name); - } else - printf("%s", name); - printf("\n"); - - return 0; -} - -static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { - _cleanup_free_ char *path = NULL; - - assert(bus); - assert(name); - assert(deps); - - path = unit_dbus_path_from_name(name); - if (path == NULL) - return -ENOMEM; - - return bus_get_unit_property_strv(bus, path, "After", deps); -} - -static Hashmap *unit_times_hashmap; - -static int list_dependencies_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - usec_t usa = 0, usb = 0; - struct unit_times *times; - - times = hashmap_get(unit_times_hashmap, *a); - if (times) - usa = times->activated; - times = hashmap_get(unit_times_hashmap, *b); - if (times) - usb = times->activated; - - return usb - usa; -} - -static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units, - unsigned int branches) { - _cleanup_strv_free_ char **deps = NULL; - char **c; - int r = 0; - usec_t service_longest = 0; - int to_print = 0; - struct unit_times *times; - struct boot_times *boot; - - if (strv_extend(units, name)) - return log_oom(); - - r = list_dependencies_get_dependencies(bus, name, &deps); - if (r < 0) - return r; - - qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); - - r = acquire_boot_times(bus, &boot); - if (r < 0) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); - if (times - && times->activated - && times->activated <= boot->finish_time - && (times->activated >= service_longest - || service_longest == 0)) { - service_longest = times->activated; - break; - } - } - - if (service_longest == 0 ) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); - if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz) - to_print++; - } - - if (!to_print) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); - if (!times - || !times->activated - || times->activated > boot->finish_time - || service_longest - times->activated > arg_fuzz) - continue; - - to_print--; - - r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot); - if (r < 0) - return r; - - if (strv_contains(*units, *c)) { - r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0), - true, NULL, boot); - if (r < 0) - return r; - continue; - } - - r = list_dependencies_one(bus, *c, level + 1, units, - (branches << 1) | (to_print ? 1 : 0)); - if (r < 0) - return r; - - if (!to_print) - break; - } - return 0; -} - -static int list_dependencies(sd_bus *bus, const char *name) { - _cleanup_strv_free_ char **units = NULL; - char ts[FORMAT_TIMESPAN_MAX]; - struct unit_times *times; - int r; - const char *id; - _cleanup_free_ char *path = 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); - - path = unit_dbus_path_from_name(name); - if (path == NULL) - return -ENOMEM; - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &reply, - "s"); - if (r < 0) { - log_error("Failed to get ID: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &id); - if (r < 0) - return bus_log_parse_error(r); - - times = hashmap_get(unit_times_hashmap, id); - - r = acquire_boot_times(bus, &boot); - if (r < 0) - return r; - - if (times) { - if (times->time) - printf("%s%s +%s%s\n", ansi_highlight_red(), id, - format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC)); - else - printf("%s\n", id); - } - - return list_dependencies_one(bus, name, 0, &units, 0); -} - -static int analyze_critical_chain(sd_bus *bus, char *names[]) { - struct unit_times *times; - unsigned int i; - Hashmap *h; - int n, r; - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - h = hashmap_new(&string_hash_ops); - if (!h) - return -ENOMEM; - - for (i = 0; i < (unsigned)n; i++) { - r = hashmap_put(h, times[i].name, ×[i]); - if (r < 0) - return r; - } - unit_times_hashmap = h; - - pager_open(arg_no_pager, false); - - puts("The time after the unit is active or started is printed after the \"@\" character.\n" - "The time the unit takes to start is printed after the \"+\" character.\n"); - - if (!strv_isempty(names)) { - char **name; - STRV_FOREACH(name, names) - list_dependencies(bus, *name); - } else - list_dependencies(bus, SPECIAL_DEFAULT_TARGET); - - hashmap_free(h); - free_unit_times(times, (unsigned) n); - return 0; -} - -static int analyze_blame(sd_bus *bus) { - struct unit_times *times; - unsigned i; - int n; - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - qsort(times, n, sizeof(struct unit_times), compare_unit_time); - - pager_open(arg_no_pager, false); - - for (i = 0; i < (unsigned) n; i++) { - char ts[FORMAT_TIMESPAN_MAX]; - - if (times[i].time > 0) - printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name); - } - - free_unit_times(times, (unsigned) n); - return 0; -} - -static int analyze_time(sd_bus *bus) { - _cleanup_free_ char *buf = NULL; - int r; - - r = pretty_boot_time(bus, &buf); - if (r < 0) - return r; - - puts(buf); - return 0; -} - -static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) { - _cleanup_strv_free_ char **units = NULL; - char **unit; - int r; - bool match_patterns; - - assert(u); - assert(prop); - assert(color); - - match_patterns = strv_fnmatch(patterns, u->id, 0); - - if (!strv_isempty(from_patterns) && - !match_patterns && - !strv_fnmatch(from_patterns, u->id, 0)) - return 0; - - r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); - if (r < 0) - return r; - - STRV_FOREACH(unit, units) { - bool match_patterns2; - - match_patterns2 = strv_fnmatch(patterns, *unit, 0); - - if (!strv_isempty(to_patterns) && - !match_patterns2 && - !strv_fnmatch(to_patterns, *unit, 0)) - continue; - - if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) - continue; - - printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); - } - - return 0; -} - -static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { - int r; - - assert(bus); - assert(u); - - 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 (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, "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); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - } - - return 0; -} - -static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { - _cleanup_strv_free_ char **expanded_patterns = NULL; - char **pattern; - int r; - - STRV_FOREACH(pattern, patterns) { - _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) - return log_oom(); - - if (string_is_glob(*pattern)) - continue; - - unit = unit_dbus_path_from_name(*pattern); - if (!unit) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - unit, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &unit_id); - if (r < 0) - return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - - if (!streq(*pattern, unit_id)) { - if (strv_extend(&expanded_patterns, unit_id) < 0) - return log_oom(); - } - } - - *ret = expanded_patterns; - expanded_patterns = NULL; /* do not free */ - - return 0; -} - -static int dot(sd_bus *bus, char* patterns[]) { - _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; - int r; - UnitInfo u; - - r = expand_patterns(bus, patterns, &expanded_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); - if (r < 0) - return r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, - &reply, - ""); - if (r < 0) { - log_error("Failed to list units: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) - return bus_log_parse_error(r); - - printf("digraph systemd {\n"); - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - - r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - printf("}\n"); - - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); - - if (on_tty()) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); - - return 0; -} - -static int dump(sd_bus *bus, char **args) { - _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; - - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Dump", - &error, - &reply, - ""); - 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) - return bus_log_parse_error(r); - - fputs(text, stdout); - return 0; -} - -static int set_log_level(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", - "LogLevel", - &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; -} - -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; -} - -static void help(void) { - - pager_open(arg_no_pager, false); - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Profile systemd, show unit dependencies, check unit files.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --system Operate on system systemd instance\n" - " --user Operate on user systemd instance\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --order Show only order in the graph\n" - " --require Show only requirement in the graph\n" - " --from-pattern=GLOB Show only origins in the graph\n" - " --to-pattern=GLOB Show only destinations in the graph\n" - " --fuzz=SECONDS Also print also services which finished SECONDS\n" - " earlier than the latest in the branch\n" - " --man[=BOOL] Do [not] check for existence of man pages\n\n" - "Commands:\n" - " time Print time spent in the kernel\n" - " blame Print list of running units ordered by time to init\n" - " critical-chain Print a tree of the time critical chain of units\n" - " plot Output SVG graphic showing service initialization\n" - " dot Output dependency graph in dot(1) format\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); - - /* When updating this list, including descriptions, apply - * changes to shell-completion/bash/systemd-analyze and - * shell-completion/zsh/_systemd-analyze too. */ -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_ORDER, - ARG_REQUIRE, - ARG_USER, - ARG_SYSTEM, - ARG_DOT_FROM_PATTERN, - ARG_DOT_TO_PATTERN, - ARG_FUZZ, - ARG_NO_PAGER, - ARG_MAN, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "order", no_argument, NULL, ARG_ORDER }, - { "require", no_argument, NULL, ARG_REQUIRE }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN }, - { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN }, - { "fuzz", required_argument, NULL, ARG_FUZZ }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "man", optional_argument, NULL, ARG_MAN }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - {} - }; - - int r, c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_USER: - arg_user = true; - break; - - case ARG_SYSTEM: - arg_user = false; - break; - - case ARG_ORDER: - arg_dot = DEP_ORDER; - break; - - case ARG_REQUIRE: - arg_dot = DEP_REQUIRE; - break; - - case ARG_DOT_FROM_PATTERN: - if (strv_extend(&arg_dot_from_patterns, optarg) < 0) - return log_oom(); - - break; - - case ARG_DOT_TO_PATTERN: - if (strv_extend(&arg_dot_to_patterns, optarg) < 0) - return log_oom(); - - break; - - case ARG_FUZZ: - r = parse_sec(optarg, &arg_fuzz); - if (r < 0) - return r; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_MAN: - if (optarg) { - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --man= argument."); - return -EINVAL; - } - - arg_man = !!r; - } else - arg_man = true; - - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option code."); - } - - return 1; /* work to do */ -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (streq_ptr(argv[optind], "verify")) - r = verify_units(argv+optind+1, - arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, - arg_man); - else { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - - 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; - } - - if (!argv[optind] || streq(argv[optind], "time")) - r = analyze_time(bus); - else if (streq(argv[optind], "blame")) - r = analyze_blame(bus); - else if (streq(argv[optind], "critical-chain")) - r = analyze_critical_chain(bus, argv+optind+1); - else if (streq(argv[optind], "plot")) - r = analyze_plot(bus); - else if (streq(argv[optind], "dot")) - r = dot(bus, argv+optind+1); - else if (streq(argv[optind], "dump")) - 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]); - } - -finish: - pager_close(); - - strv_free(arg_dot_from_patterns); - strv_free(arg_dot_to_patterns); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/ask-password/Makefile b/src/ask-password/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/ask-password/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c deleted file mode 100644 index 6d53dd982c..0000000000 --- a/src/ask-password/ask-password.c +++ /dev/null @@ -1,188 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "ask-password-api.h" -#include "def.h" -#include "log.h" -#include "macro.h" -#include "strv.h" - -static const char *arg_icon = NULL; -static const char *arg_id = NULL; -static const char *arg_keyname = NULL; -static char *arg_message = NULL; -static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; -static bool arg_multiple = false; -static bool arg_no_output = 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" - " --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" - " --no-output Do not print password to standard output\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_ICON = 0x100, - ARG_TIMEOUT, - ARG_ECHO, - ARG_NO_TTY, - ARG_ACCEPT_CACHED, - ARG_MULTIPLE, - ARG_ID, - ARG_KEYNAME, - ARG_NO_OUTPUT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "icon", required_argument, NULL, ARG_ICON }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { "echo", no_argument, NULL, ARG_ECHO }, - { "no-tty", no_argument, NULL, ARG_NO_TTY }, - { "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 }, - { "no-output", no_argument, NULL, ARG_NO_OUTPUT }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_ICON: - arg_icon = optarg; - break; - - case ARG_TIMEOUT: - if (parse_sec(optarg, &arg_timeout) < 0) { - log_error("Failed to parse --timeout parameter %s", optarg); - return -EINVAL; - } - break; - - case ARG_ECHO: - arg_flags |= ASK_PASSWORD_ECHO; - break; - - case ARG_NO_TTY: - arg_flags |= ASK_PASSWORD_NO_TTY; - break; - - case ARG_ACCEPT_CACHED: - arg_flags |= ASK_PASSWORD_ACCEPT_CACHED; - break; - - case ARG_MULTIPLE: - arg_multiple = true; - break; - - case ARG_ID: - arg_id = optarg; - break; - - case ARG_KEYNAME: - arg_keyname = optarg; - break; - - case ARG_NO_OUTPUT: - arg_no_output = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc > optind) { - arg_message = strv_join(argv + optind, " "); - if (!arg_message) - return log_oom(); - } - - return 1; -} - -int main(int argc, char *argv[]) { - _cleanup_strv_free_erase_ char **l = NULL; - usec_t timeout; - char **p; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_timeout > 0) - timeout = now(CLOCK_MONOTONIC) + arg_timeout; - else - timeout = 0; - - 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; - } - - STRV_FOREACH(p, l) { - if (!arg_no_output) - puts(*p); - - if (!arg_multiple) - break; - } - -finish: - free(arg_message); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/backlight/Makefile b/src/backlight/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/backlight/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c deleted file mode 100644 index 45be135a23..0000000000 --- a/src/backlight/backlight.c +++ /dev/null @@ -1,434 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "libudev.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; - const char *subsystem, *sysname; - - assert(device); - - parent = udev_device_get_parent(device); - if (!parent) - return NULL; - - subsystem = udev_device_get_subsystem(parent); - if (!subsystem) - return NULL; - - sysname = udev_device_get_sysname(parent); - if (!sysname) - return NULL; - - if (streq(subsystem, "drm")) { - const char *c; - - c = startswith(sysname, "card"); - if (!c) - return NULL; - - c += strspn(c, DIGITS); - if (*c == '-') { - /* A connector DRM device, let's ignore all but LVDS and eDP! */ - - if (!startswith(c, "-LVDS-") && - !startswith(c, "-Embedded DisplayPort-")) - return NULL; - } - - } else if (streq(subsystem, "pci")) { - const char *value; - - value = udev_device_get_sysattr_value(parent, "class"); - if (value) { - unsigned long class = 0; - - if (safe_atolu(value, &class) < 0) { - log_warning("Cannot parse PCI class %s of device %s:%s.", - value, subsystem, sysname); - return NULL; - } - - /* Graphics card */ - if (class == 0x30000) - return parent; - } - - } else if (streq(subsystem, "platform")) - return parent; - - return find_pci_or_platform_parent(parent); -} - -static bool same_device(struct udev_device *a, struct udev_device *b) { - assert(a); - assert(b); - - if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b))) - return false; - - if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b))) - return false; - - return true; -} - -static bool validate_device(struct udev *udev, struct udev_device *device) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - struct udev_device *parent; - const char *v, *subsystem; - int r; - - assert(udev); - assert(device); - - /* Verify whether we should actually care for a specific - * backlight device. For backlight devices there might be - * multiple ways to access the same control: "firmware" - * (i.e. ACPI), "platform" (i.e. via the machine's EC) and - * "raw" (via the graphics card). In general we should prefer - * "firmware" (i.e. ACPI) or "platform" access over "raw" - * access, in order not to confuse the BIOS/EC, and - * compatibility with possible low-level hotkey handling of - * screen brightness. The kernel will already make sure to - * expose only one of "firmware" and "platform" for the same - * device to userspace. However, we still need to make sure - * that we use "raw" only if no "firmware" or "platform" - * device for the same device exists. */ - - subsystem = udev_device_get_subsystem(device); - if (!streq_ptr(subsystem, "backlight")) - return true; - - v = udev_device_get_sysattr_value(device, "type"); - if (!streq_ptr(v, "raw")) - return true; - - parent = find_pci_or_platform_parent(device); - if (!parent) - return true; - - subsystem = udev_device_get_subsystem(parent); - if (!subsystem) - return true; - - enumerate = udev_enumerate_new(udev); - if (!enumerate) - return true; - - r = udev_enumerate_add_match_subsystem(enumerate, "backlight"); - if (r < 0) - return true; - - r = udev_enumerate_scan_devices(enumerate); - if (r < 0) - return true; - - first = udev_enumerate_get_list_entry(enumerate); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *other; - struct udev_device *other_parent; - const char *other_subsystem; - - other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!other) - return true; - - if (same_device(device, other)) - continue; - - v = udev_device_get_sysattr_value(other, "type"); - if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware")) - continue; - - /* OK, so there's another backlight device, and it's a - * platform or firmware device, so, let's see if we - * can verify it belongs to the same device as - * ours. */ - other_parent = find_pci_or_platform_parent(other); - if (!other_parent) - continue; - - if (same_device(parent, other_parent)) { - /* Both have the same PCI parent, that means - * we are out. */ - log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.", - udev_device_get_sysname(device), - udev_device_get_sysname(other)); - return false; - } - - other_subsystem = udev_device_get_subsystem(other_parent); - if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) { - /* The other is connected to the platform bus - * and we are a PCI device, that also means we - * are out. */ - log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.", - udev_device_get_sysname(device), - udev_device_get_sysname(other)); - return false; - } - } - - return true; -} - -static unsigned get_max_brightness(struct udev_device *device) { - int r; - const char *max_brightness_str; - unsigned max_brightness; - - max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness"); - if (!max_brightness_str) { - log_warning("Failed to read 'max_brightness' attribute."); - return 0; - } - - r = safe_atou(max_brightness_str, &max_brightness); - if (r < 0) { - log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str); - return 0; - } - - if (max_brightness <= 0) { - log_warning("Maximum brightness is 0, ignoring device."); - return 0; - } - - return max_brightness; -} - -/* Some systems turn the backlight all the way off at the lowest levels. - * clamp_brightness clamps the saved brightness to at least 1 or 5% of - * max_brightness in case of 'backlight' subsystem. This avoids preserving - * an unreadably dim screen, which would otherwise force the user to - * disable state restoration. */ -static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) { - int r; - unsigned brightness, new_brightness, min_brightness; - const char *subsystem; - - r = safe_atou(*value, &brightness); - if (r < 0) { - log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value); - return; - } - - subsystem = udev_device_get_subsystem(device); - if (streq_ptr(subsystem, "backlight")) - min_brightness = MAX(1U, max_brightness/20); - else - min_brightness = 0; - - new_brightness = CLAMP(brightness, min_brightness, max_brightness); - if (new_brightness != brightness) { - char *old_value = *value; - - r = asprintf(value, "%u", new_brightness); - if (r < 0) { - log_oom(); - return; - } - - log_info("Saved brightness %s %s to %s.", old_value, - new_brightness > brightness ? - "too low; increasing" : "too high; decreasing", - *value); - - free(old_value); - } -} - -int main(int argc, char *argv[]) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL; - const char *sysname, *path_id; - unsigned max_brightness; - int r; - - if (argc != 3) { - log_error("This program requires two arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = mkdir_p("/var/lib/systemd/backlight", 0755); - if (r < 0) { - log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m"); - return EXIT_FAILURE; - } - - udev = udev_new(); - if (!udev) { - log_oom(); - return EXIT_FAILURE; - } - - sysname = strchr(argv[2], ':'); - if (!sysname) { - log_error("Requires a subsystem and sysname pair specifying a backlight device."); - return EXIT_FAILURE; - } - - ss = strndup(argv[2], sysname - argv[2]); - if (!ss) { - log_oom(); - return EXIT_FAILURE; - } - - sysname++; - - if (!streq(ss, "backlight") && !streq(ss, "leds")) { - log_error("Not a backlight or LED device: '%s:%s'", ss, sysname); - return EXIT_FAILURE; - } - - errno = 0; - device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); - if (!device) { - if (errno > 0) - log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); - else - log_oom(); - - return EXIT_FAILURE; - } - - /* If max_brightness is 0, then there is no actual backlight - * device. This happens on desktops with Asus mainboards - * that load the eeepc-wmi module. - */ - max_brightness = get_max_brightness(device); - if (max_brightness == 0) - return EXIT_SUCCESS; - - escaped_ss = cescape(ss); - if (!escaped_ss) { - log_oom(); - return EXIT_FAILURE; - } - - escaped_sysname = cescape(sysname); - if (!escaped_sysname) { - log_oom(); - return EXIT_FAILURE; - } - - path_id = udev_device_get_property_value(device, "ID_PATH"); - if (path_id) { - escaped_path_id = cescape(path_id); - if (!escaped_path_id) { - log_oom(); - return EXIT_FAILURE; - } - - saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL); - } else - saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL); - - if (!saved) { - log_oom(); - return EXIT_FAILURE; - } - - /* If there are multiple conflicting backlight devices, then - * their probing at boot-time might happen in any order. This - * means the validity checking of the device then is not - * reliable, since it might not see other devices conflicting - * with a specific backlight. To deal with this, we will - * actively delete backlight state files at shutdown (where - * device probing should be complete), so that the validity - * check at boot time doesn't have to be reliable. */ - - if (streq(argv[1], "load")) { - _cleanup_free_ char *value = NULL; - const char *clamp; - - if (shall_restore_state() == 0) - return EXIT_SUCCESS; - - if (!validate_device(udev, device)) - return EXIT_SUCCESS; - - r = read_one_line_file(saved, &value); - if (r < 0) { - - if (r == -ENOENT) - return EXIT_SUCCESS; - - log_error_errno(r, "Failed to read %s: %m", saved); - return EXIT_FAILURE; - } - - clamp = udev_device_get_property_value(device, "ID_BACKLIGHT_CLAMP"); - if (!clamp || parse_boolean(clamp) != 0) /* default to clamping */ - clamp_brightness(device, &value, max_brightness); - - r = udev_device_set_sysattr_value(device, "brightness", value); - if (r < 0) { - log_error_errno(r, "Failed to write system 'brightness' attribute: %m"); - return EXIT_FAILURE; - } - - } else if (streq(argv[1], "save")) { - const char *value; - - if (!validate_device(udev, device)) { - unlink(saved); - return EXIT_SUCCESS; - } - - value = udev_device_get_sysattr_value(device, "brightness"); - if (!value) { - log_error("Failed to read system 'brightness' attribute"); - return EXIT_FAILURE; - } - - r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE); - if (r < 0) { - log_error_errno(r, "Failed to write %s: %m", saved); - return EXIT_FAILURE; - } - - } else { - log_error("Unknown verb %s.", argv[1]); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/basic/.gitignore b/src/basic/.gitignore deleted file mode 100644 index e22411e484..0000000000 --- a/src/basic/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -/cap-from-name.gperf -/cap-from-name.h -/cap-list.txt -/cap-to-name.h -/errno-from-name.gperf -/errno-from-name.h -/errno-list.txt -/errno-to-name.h -/af-from-name.gperf -/af-from-name.h -/af-list.txt -/af-to-name.h -/arphrd-from-name.gperf -/arphrd-from-name.h -/arphrd-list.txt -/arphrd-to-name.h diff --git a/src/basic/Makefile b/src/basic/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/basic/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c deleted file mode 100644 index 9020793930..0000000000 --- a/src/basic/MurmurHash2.c +++ /dev/null @@ -1,86 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -// Note - This code makes a few assumptions about how your machine behaves - - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 - -// And it has a few limitations - - -// 1. It will not work incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. - -#include "MurmurHash2.h" - -//----------------------------------------------------------------------------- -// Platform-specific functions and macros - -// Microsoft Visual Studio - -#if defined(_MSC_VER) - -#define BIG_CONSTANT(x) (x) - -// Other compilers - -#else // defined(_MSC_VER) - -#define BIG_CONSTANT(x) (x##LLU) - -#endif // !defined(_MSC_VER) - -//----------------------------------------------------------------------------- - -uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - - const uint32_t m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - - uint32_t h = seed ^ len; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data = (const unsigned char *)key; - - while (len >= 4) - { - uint32_t k = *(uint32_t*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - - switch(len) - { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} diff --git a/src/basic/MurmurHash2.h b/src/basic/MurmurHash2.h deleted file mode 100644 index 93362dd485..0000000000 --- a/src/basic/MurmurHash2.h +++ /dev/null @@ -1,33 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -#ifndef _MURMURHASH2_H_ -#define _MURMURHASH2_H_ - -//----------------------------------------------------------------------------- -// Platform-specific functions and macros - -// Microsoft Visual Studio - -#if defined(_MSC_VER) - -typedef unsigned char uint8_t; -typedef unsigned long uint32_t; -typedef unsigned __int64 uint64_t; - -// Other compilers - -#else // defined(_MSC_VER) - -#include - -#endif // !defined(_MSC_VER) - -//----------------------------------------------------------------------------- - -uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ); - -//----------------------------------------------------------------------------- - -#endif // _MURMURHASH2_H_ diff --git a/src/basic/af-list.c b/src/basic/af-list.c deleted file mode 100644 index 3fac9c508b..0000000000 --- a/src/basic/af-list.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "af-list.h" -#include "macro.h" - -static const struct af_name* lookup_af(register const char *str, register unsigned int len); - -#include "af-from-name.h" -#include "af-to-name.h" - -const char *af_to_name(int id) { - - if (id <= 0) - return NULL; - - if (id >= (int) ELEMENTSOF(af_names)) - return NULL; - - return af_names[id]; -} - -int af_from_name(const char *name) { - const struct af_name *sc; - - assert(name); - - sc = lookup_af(name, strlen(name)); - if (!sc) - return AF_UNSPEC; - - return sc->id; -} - -int af_max(void) { - return ELEMENTSOF(af_names); -} diff --git a/src/basic/af-list.h b/src/basic/af-list.h deleted file mode 100644 index 6a4cc03839..0000000000 --- a/src/basic/af-list.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "string-util.h" - -const char *af_to_name(int id); -int af_from_name(const char *name); - -static inline const char* af_to_name_short(int id) { - const char *f; - - if (id == AF_UNSPEC) - return "*"; - - f = af_to_name(id); - if (!f) - return "unknown"; - - assert(startswith(f, "AF_")); - return f + 3; -} - -int af_max(void); diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c deleted file mode 100644 index b540dcddf5..0000000000 --- a/src/basic/alloc-util.c +++ /dev/null @@ -1,83 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 deleted file mode 100644 index ceeee519b7..0000000000 --- a/src/basic/alloc-util.h +++ /dev/null @@ -1,111 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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) - -static inline bool size_multiply_overflow(size_t size, size_t need) { - return _unlikely_(need != 0 && size > (SIZE_MAX / need)); -} - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return malloc(size * need); -} - -_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return realloc(p, size * need); -} - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup(p, size * need); -} - -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/architecture.c b/src/basic/architecture.c deleted file mode 100644 index b1c8e91f50..0000000000 --- a/src/basic/architecture.c +++ /dev/null @@ -1,179 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "architecture.h" -#include "macro.h" -#include "string-table.h" -#include "string-util.h" - -int uname_architecture(void) { - - /* Return a sanitized enum identifying the architecture we are - * running on. This is based on uname(), and the user may - * hence control what this returns by using - * personality(). This puts the user in control on systems - * that can run binaries of multiple architectures. - * - * We do not translate the string returned by uname() - * 1:1. Instead we try to clean it up and break down the - * confusion on x86 and arm in particular. - * - * We do not try to distinguish CPUs not CPU features, but - * actual architectures, i.e. that have genuinely different - * code. */ - - static const struct { - const char *machine; - int arch; - } arch_map[] = { -#if defined(__x86_64__) || defined(__i386__) - { "x86_64", ARCHITECTURE_X86_64 }, - { "i686", ARCHITECTURE_X86 }, - { "i586", ARCHITECTURE_X86 }, - { "i486", ARCHITECTURE_X86 }, - { "i386", ARCHITECTURE_X86 }, -#elif defined(__powerpc__) || defined(__powerpc64__) - { "ppc64", ARCHITECTURE_PPC64 }, - { "ppc64le", ARCHITECTURE_PPC64_LE }, - { "ppc", ARCHITECTURE_PPC }, - { "ppcle", ARCHITECTURE_PPC_LE }, -#elif defined(__ia64__) - { "ia64", ARCHITECTURE_IA64 }, -#elif defined(__hppa__) || defined(__hppa64__) - { "parisc64", ARCHITECTURE_PARISC64 }, - { "parisc", ARCHITECTURE_PARISC }, -#elif defined(__s390__) || defined(__s390x__) - { "s390x", ARCHITECTURE_S390X }, - { "s390", ARCHITECTURE_S390 }, -#elif defined(__sparc__) - { "sparc64", ARCHITECTURE_SPARC64 }, - { "sparc", ARCHITECTURE_SPARC }, -#elif defined(__mips__) || defined(__mips64__) - { "mips64", ARCHITECTURE_MIPS64 }, - { "mips", ARCHITECTURE_MIPS }, -#elif defined(__alpha__) - { "alpha" , ARCHITECTURE_ALPHA }, -#elif defined(__arm__) || defined(__aarch64__) - { "aarch64", ARCHITECTURE_ARM64 }, - { "aarch64_be", ARCHITECTURE_ARM64_BE }, - { "armv4l", ARCHITECTURE_ARM }, - { "armv4b", ARCHITECTURE_ARM_BE }, - { "armv4tl", ARCHITECTURE_ARM }, - { "armv4tb", ARCHITECTURE_ARM_BE }, - { "armv5tl", ARCHITECTURE_ARM }, - { "armv5tb", ARCHITECTURE_ARM_BE }, - { "armv5tel", ARCHITECTURE_ARM }, - { "armv5teb" , ARCHITECTURE_ARM_BE }, - { "armv5tejl", ARCHITECTURE_ARM }, - { "armv5tejb", ARCHITECTURE_ARM_BE }, - { "armv6l", ARCHITECTURE_ARM }, - { "armv6b", ARCHITECTURE_ARM_BE }, - { "armv7l", ARCHITECTURE_ARM }, - { "armv7b", ARCHITECTURE_ARM_BE }, - { "armv7ml", ARCHITECTURE_ARM }, - { "armv7mb", ARCHITECTURE_ARM_BE }, - { "armv4l", ARCHITECTURE_ARM }, - { "armv4b", ARCHITECTURE_ARM_BE }, - { "armv4tl", ARCHITECTURE_ARM }, - { "armv4tb", ARCHITECTURE_ARM_BE }, - { "armv5tl", ARCHITECTURE_ARM }, - { "armv5tb", ARCHITECTURE_ARM_BE }, - { "armv5tel", ARCHITECTURE_ARM }, - { "armv5teb", ARCHITECTURE_ARM_BE }, - { "armv5tejl", ARCHITECTURE_ARM }, - { "armv5tejb", ARCHITECTURE_ARM_BE }, - { "armv6l", ARCHITECTURE_ARM }, - { "armv6b", ARCHITECTURE_ARM_BE }, - { "armv7l", ARCHITECTURE_ARM }, - { "armv7b", ARCHITECTURE_ARM_BE }, - { "armv7ml", ARCHITECTURE_ARM }, - { "armv7mb", ARCHITECTURE_ARM_BE }, - { "armv8l", ARCHITECTURE_ARM }, - { "armv8b", ARCHITECTURE_ARM_BE }, -#elif defined(__sh__) || defined(__sh64__) - { "sh5", ARCHITECTURE_SH64 }, - { "sh2", ARCHITECTURE_SH }, - { "sh2a", ARCHITECTURE_SH }, - { "sh3", ARCHITECTURE_SH }, - { "sh4", ARCHITECTURE_SH }, - { "sh4a", ARCHITECTURE_SH }, -#elif defined(__m68k__) - { "m68k", ARCHITECTURE_M68K }, -#elif defined(__tilegx__) - { "tilegx", ARCHITECTURE_TILEGX }, -#elif defined(__cris__) - { "crisv32", ARCHITECTURE_CRIS }, -#elif defined(__nios2__) - { "nios2", ARCHITECTURE_NIOS2 }, -#else -#error "Please register your architecture here!" -#endif - }; - - static int cached = _ARCHITECTURE_INVALID; - struct utsname u; - unsigned i; - - if (cached != _ARCHITECTURE_INVALID) - return cached; - - assert_se(uname(&u) >= 0); - - for (i = 0; i < ELEMENTSOF(arch_map); i++) - if (streq(arch_map[i].machine, u.machine)) - return cached = arch_map[i].arch; - - assert_not_reached("Couldn't identify architecture. You need to patch systemd."); - return _ARCHITECTURE_INVALID; -} - -static const char *const architecture_table[_ARCHITECTURE_MAX] = { - [ARCHITECTURE_X86] = "x86", - [ARCHITECTURE_X86_64] = "x86-64", - [ARCHITECTURE_PPC] = "ppc", - [ARCHITECTURE_PPC_LE] = "ppc-le", - [ARCHITECTURE_PPC64] = "ppc64", - [ARCHITECTURE_PPC64_LE] = "ppc64-le", - [ARCHITECTURE_IA64] = "ia64", - [ARCHITECTURE_PARISC] = "parisc", - [ARCHITECTURE_PARISC64] = "parisc64", - [ARCHITECTURE_S390] = "s390", - [ARCHITECTURE_S390X] = "s390x", - [ARCHITECTURE_SPARC] = "sparc", - [ARCHITECTURE_SPARC64] = "sparc64", - [ARCHITECTURE_MIPS] = "mips", - [ARCHITECTURE_MIPS_LE] = "mips-le", - [ARCHITECTURE_MIPS64] = "mips64", - [ARCHITECTURE_MIPS64_LE] = "mips64-le", - [ARCHITECTURE_ALPHA] = "alpha", - [ARCHITECTURE_ARM] = "arm", - [ARCHITECTURE_ARM_BE] = "arm-be", - [ARCHITECTURE_ARM64] = "arm64", - [ARCHITECTURE_ARM64_BE] = "arm64-be", - [ARCHITECTURE_SH] = "sh", - [ARCHITECTURE_SH64] = "sh64", - [ARCHITECTURE_M68K] = "m68k", - [ARCHITECTURE_TILEGX] = "tilegx", - [ARCHITECTURE_CRIS] = "cris", - [ARCHITECTURE_NIOS2] = "nios2", -}; - -DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h deleted file mode 100644 index b3e4d85906..0000000000 --- a/src/basic/architecture.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" -#include "util.h" - -/* A cleaned up architecture definition. We don't want to get lost in - * processor features, models, generations or even ABIs. Hence we - * focus on general family, and distinguish word width and - * endianness. */ - -enum { - ARCHITECTURE_X86 = 0, - ARCHITECTURE_X86_64, - ARCHITECTURE_PPC, - ARCHITECTURE_PPC_LE, - ARCHITECTURE_PPC64, - ARCHITECTURE_PPC64_LE, - ARCHITECTURE_IA64, - ARCHITECTURE_PARISC, - ARCHITECTURE_PARISC64, - ARCHITECTURE_S390, - ARCHITECTURE_S390X, - ARCHITECTURE_SPARC, - ARCHITECTURE_SPARC64, - ARCHITECTURE_MIPS, - ARCHITECTURE_MIPS_LE, - ARCHITECTURE_MIPS64, - ARCHITECTURE_MIPS64_LE, - ARCHITECTURE_ALPHA, - ARCHITECTURE_ARM, - ARCHITECTURE_ARM_BE, - ARCHITECTURE_ARM64, - ARCHITECTURE_ARM64_BE, - ARCHITECTURE_SH, - ARCHITECTURE_SH64, - ARCHITECTURE_M68K, - ARCHITECTURE_TILEGX, - ARCHITECTURE_CRIS, - ARCHITECTURE_NIOS2, - _ARCHITECTURE_MAX, - _ARCHITECTURE_INVALID = -1 -}; - -int uname_architecture(void); - -/* - * LIB_ARCH_TUPLE should resolve to the local library path - * architecture tuple systemd is built for, according to the Debian - * tuple list: - * - * https://wiki.debian.org/Multiarch/Tuples - * - * This is used in library search paths that should understand - * Debian's paths on all distributions. - */ - -#if defined(__x86_64__) -# define native_architecture() ARCHITECTURE_X86_64 -# define LIB_ARCH_TUPLE "x86_64-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_X86 -#elif defined(__i386__) -# define native_architecture() ARCHITECTURE_X86 -# define LIB_ARCH_TUPLE "i386-linux-gnu" -#elif defined(__powerpc64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_PPC64 -# define LIB_ARCH_TUPLE "ppc64-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC -# else -# define native_architecture() ARCHITECTURE_PPC64_LE -# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC_LE -# endif -#elif defined(__powerpc__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_PPC -# define LIB_ARCH_TUPLE "powerpc-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_PPC_LE -# error "Missing LIB_ARCH_TUPLE for PPCLE" -# endif -#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" -#elif defined(__hppa__) -# define native_architecture() ARCHITECTURE_PARISC -# define LIB_ARCH_TUPLE "hppa‑linux‑gnu" -#elif defined(__s390x__) -# define native_architecture() ARCHITECTURE_S390X -# define LIB_ARCH_TUPLE "s390x-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_S390 -#elif defined(__s390__) -# define native_architecture() ARCHITECTURE_S390 -# define LIB_ARCH_TUPLE "s390-linux-gnu" -#elif defined(__sparc__) && defined (__arch64__) -# define native_architecture() ARCHITECTURE_SPARC64 -# define LIB_ARCH_TUPLE "sparc64-linux-gnu" -#elif defined(__sparc__) -# define native_architecture() ARCHITECTURE_SPARC -# define LIB_ARCH_TUPLE "sparc-linux-gnu" -#elif defined(__mips64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_MIPS64 -# error "Missing LIB_ARCH_TUPLE for MIPS64" -# else -# define native_architecture() ARCHITECTURE_MIPS64_LE -# error "Missing LIB_ARCH_TUPLE for MIPS64_LE" -# endif -#elif defined(__mips__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_MIPS -# define LIB_ARCH_TUPLE "mips-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_MIPS_LE -# define LIB_ARCH_TUPLE "mipsel-linux-gnu" -# endif -#elif defined(__alpha__) -# define native_architecture() ARCHITECTURE_ALPHA -# define LIB_ARCH_TUPLE "alpha-linux-gnu" -#elif defined(__aarch64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_ARM64_BE -# define LIB_ARCH_TUPLE "aarch64_be-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_ARM64 -# define LIB_ARCH_TUPLE "aarch64-linux-gnu" -# endif -#elif defined(__arm__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_ARM_BE -# if defined(__ARM_EABI__) -# if defined(__ARM_PCS_VFP) -# define LIB_ARCH_TUPLE "armeb-linux-gnueabihf" -# else -# define LIB_ARCH_TUPLE "armeb-linux-gnueabi" -# endif -# else -# define LIB_ARCH_TUPLE "armeb-linux-gnu" -# endif -# else -# define native_architecture() ARCHITECTURE_ARM -# if defined(__ARM_EABI__) -# if defined(__ARM_PCS_VFP) -# define LIB_ARCH_TUPLE "arm-linux-gnueabihf" -# else -# define LIB_ARCH_TUPLE "arm-linux-gnueabi" -# endif -# else -# define LIB_ARCH_TUPLE "arm-linux-gnu" -# endif -# endif -#elif defined(__sh64__) -# define native_architecture() ARCHITECTURE_SH64 -# error "Missing LIB_ARCH_TUPLE for SH64" -#elif defined(__sh__) -# define native_architecture() ARCHITECTURE_SH -# define LIB_ARCH_TUPLE "sh4-linux-gnu" -#elif defined(__m68k__) -# define native_architecture() ARCHITECTURE_M68K -# define LIB_ARCH_TUPLE "m68k-linux-gnu" -#elif defined(__tilegx__) -# define native_architecture() ARCHITECTURE_TILEGX -# error "Missing LIB_ARCH_TUPLE for TILEGX" -#elif defined(__cris__) -# define native_architecture() ARCHITECTURE_CRIS -# error "Missing LIB_ARCH_TUPLE for CRIS" -#elif defined(__nios2__) -# define native_architecture() ARCHITECTURE_NIOS2 -# define LIB_ARCH_TUPLE "nios2-linux-gnu" -#else -# error "Please register your architecture here!" -#endif - -const char *architecture_to_string(int a) _const_; -int architecture_from_string(const char *s) _pure_; diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c deleted file mode 100644 index 6792d1ee3f..0000000000 --- a/src/basic/arphrd-list.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "arphrd-list.h" -#include "macro.h" - -static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len); - -#include "arphrd-from-name.h" -#include "arphrd-to-name.h" - -const char *arphrd_to_name(int id) { - - if (id <= 0) - return NULL; - - if (id >= (int) ELEMENTSOF(arphrd_names)) - return NULL; - - return arphrd_names[id]; -} - -int arphrd_from_name(const char *name) { - const struct arphrd_name *sc; - - assert(name); - - sc = lookup_arphrd(name, strlen(name)); - if (!sc) - return 0; - - return sc->id; -} - -int arphrd_max(void) { - return ELEMENTSOF(arphrd_names); -} diff --git a/src/basic/arphrd-list.h b/src/basic/arphrd-list.h deleted file mode 100644 index c0f8758dbe..0000000000 --- a/src/basic/arphrd-list.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -const char *arphrd_to_name(int id); -int arphrd_from_name(const char *name); - -int arphrd_max(void); diff --git a/src/basic/async.c b/src/basic/async.c deleted file mode 100644 index a1f163f27b..0000000000 --- a/src/basic/async.c +++ /dev/null @@ -1,94 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "async.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "util.h" - -int asynchronous_job(void* (*func)(void *p), void *arg) { - pthread_attr_t a; - pthread_t t; - int r; - - /* It kinda sucks that we have to resort to threads to - * implement an asynchronous sync(), but well, such is - * life. - * - * Note that issuing this command right before exiting a - * process will cause the process to wait for the sync() to - * complete. This function hence is nicely asynchronous really - * only in long running processes. */ - - r = pthread_attr_init(&a); - if (r > 0) - return -r; - - r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); - if (r > 0) - goto finish; - - r = pthread_create(&t, &a, func, arg); - -finish: - pthread_attr_destroy(&a); - return -r; -} - -static void *sync_thread(void *p) { - sync(); - return NULL; -} - -int asynchronous_sync(void) { - log_debug("Spawning new thread for sync"); - - return asynchronous_job(sync_thread, NULL); -} - -static void *close_thread(void *p) { - assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); - return NULL; -} - -int asynchronous_close(int fd) { - int r; - - /* This is supposed to behave similar to safe_close(), but - * actually invoke close() asynchronously, so that it will - * never block. Ideally the kernel would have an API for this, - * but it doesn't, so we work around it, and hide this as a - * far away as we can. */ - - if (fd >= 0) { - PROTECT_ERRNO; - - r = asynchronous_job(close_thread, FD_TO_PTR(fd)); - if (r < 0) - assert_se(close_nointr(fd) != -EBADF); - } - - return -1; -} diff --git a/src/basic/async.h b/src/basic/async.h deleted file mode 100644 index 9bd13ff6e0..0000000000 --- a/src/basic/async.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int asynchronous_job(void* (*func)(void *p), void *arg); - -int asynchronous_sync(void); -int asynchronous_close(int fd); diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c deleted file mode 100644 index 5741fecdd6..0000000000 --- a/src/basic/audit-util.c +++ /dev/null @@ -1,104 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "audit-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" -#include "parse-util.h" -#include "process-util.h" -#include "user-util.h" - -int audit_session_from_pid(pid_t pid, uint32_t *id) { - _cleanup_free_ char *s = NULL; - const char *p; - uint32_t u; - int r; - - assert(id); - - /* We don't convert ENOENT to ESRCH here, since we can't - * really distuingish between "audit is not available in the - * kernel" and "the process does not exist", both which will - * result in ENOENT. */ - - p = procfs_file_alloca(pid, "sessionid"); - - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = safe_atou32(s, &u); - if (r < 0) - return r; - - if (u == AUDIT_SESSION_INVALID || u <= 0) - return -ENODATA; - - *id = u; - return 0; -} - -int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { - _cleanup_free_ char *s = NULL; - const char *p; - uid_t u; - int r; - - assert(uid); - - p = procfs_file_alloca(pid, "loginuid"); - - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = parse_uid(s, &u); - if (r == -ENXIO) /* the UID was -1 */ - return -ENODATA; - if (r < 0) - return r; - - *uid = (uid_t) u; - return 0; -} - -bool use_audit(void) { - static int cached_use = -1; - - if (cached_use < 0) { - int fd; - - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); - if (fd < 0) - cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT; - else { - cached_use = true; - safe_close(fd); - } - } - - return cached_use; -} diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h deleted file mode 100644 index e048503991..0000000000 --- a/src/basic/audit-util.h +++ /dev/null @@ -1,31 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#define AUDIT_SESSION_INVALID ((uint32_t) -1) - -int audit_session_from_pid(pid_t pid, uint32_t *id); -int audit_loginuid_from_pid(pid_t pid, uid_t *uid); - -bool use_audit(void); diff --git a/src/basic/barrier.c b/src/basic/barrier.c deleted file mode 100644 index 2da633b311..0000000000 --- a/src/basic/barrier.c +++ /dev/null @@ -1,415 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "barrier.h" -#include "fd-util.h" -#include "macro.h" - -/** - * Barriers - * This barrier implementation provides a simple synchronization method based - * on file-descriptors that can safely be used between threads and processes. A - * barrier object contains 2 shared counters based on eventfd. Both processes - * can now place barriers and wait for the other end to reach a random or - * specific barrier. - * Barriers are numbered, so you can either wait for the other end to reach any - * barrier or the last barrier that you placed. This way, you can use barriers - * for one-way *and* full synchronization. Note that even-though barriers are - * numbered, these numbers are internal and recycled once both sides reached the - * same barrier (implemented as a simple signed counter). It is thus not - * possible to address barriers by their ID. - * - * Barrier-API: Both ends can place as many barriers via barrier_place() as - * they want and each pair of barriers on both sides will be implicitly linked. - * Each side can use the barrier_wait/sync_*() family of calls to wait for the - * other side to place a specific barrier. barrier_wait_next() waits until the - * other side calls barrier_place(). No links between the barriers are - * considered and this simply serves as most basic asynchronous barrier. - * barrier_sync_next() is like barrier_wait_next() and waits for the other side - * to place their next barrier via barrier_place(). However, it only waits for - * barriers that are linked to a barrier we already placed. If the other side - * already placed more barriers than we did, barrier_sync_next() returns - * immediately. - * barrier_sync() extends barrier_sync_next() and waits until the other end - * placed as many barriers via barrier_place() as we did. If they already placed - * as many as we did (or more), it returns immediately. - * - * Additionally to basic barriers, an abortion event is available. - * barrier_abort() places an abortion event that cannot be undone. An abortion - * immediately cancels all placed barriers and replaces them. Any running and - * following wait/sync call besides barrier_wait_abortion() will immediately - * return false on both sides (otherwise, they always return true). - * barrier_abort() can be called multiple times on both ends and will be a - * no-op if already called on this side. - * barrier_wait_abortion() can be used to wait for the other side to call - * barrier_abort() and is the only wait/sync call that does not return - * immediately if we aborted outself. It only returns once the other side - * called barrier_abort(). - * - * Barriers can be used for in-process and inter-process synchronization. - * However, for in-process synchronization you could just use mutexes. - * Therefore, main target is IPC and we require both sides to *not* share the FD - * table. If that's given, barriers provide target tracking: If the remote side - * exit()s, an abortion event is implicitly queued on the other side. This way, - * a sync/wait call will be woken up if the remote side crashed or exited - * unexpectedly. However, note that these abortion events are only queued if the - * barrier-queue has been drained. Therefore, it is safe to place a barrier and - * exit. The other side can safely wait on the barrier even though the exit - * queued an abortion event. Usually, the abortion event would overwrite the - * barrier, however, that's not true for exit-abortion events. Those are only - * queued if the barrier-queue is drained (thus, the receiving side has placed - * more barriers than the remote side). - */ - -/** - * barrier_create() - Initialize a barrier object - * @obj: barrier to initialize - * - * This initializes a barrier object. The caller is responsible of allocating - * the memory and keeping it valid. The memory does not have to be zeroed - * beforehand. - * Two eventfd objects are allocated for each barrier. If allocation fails, an - * error is returned. - * - * If this function fails, the barrier is reset to an invalid state so it is - * safe to call barrier_destroy() on the object regardless whether the - * initialization succeeded or not. - * - * The caller is responsible to destroy the object via barrier_destroy() before - * releasing the underlying memory. - * - * Returns: 0 on success, negative error code on failure. - */ -int barrier_create(Barrier *b) { - _cleanup_(barrier_destroyp) Barrier *staging = b; - int r; - - assert(b); - - b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (b->me < 0) - return -errno; - - b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (b->them < 0) - return -errno; - - r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK); - if (r < 0) - return -errno; - - staging = NULL; - return 0; -} - -/** - * barrier_destroy() - Destroy a barrier object - * @b: barrier to destroy or NULL - * - * This destroys a barrier object that has previously been passed to - * barrier_create(). The object is released and reset to invalid - * state. Therefore, it is safe to call barrier_destroy() multiple - * times or even if barrier_create() failed. However, barrier must be - * always initialized with BARRIER_NULL. - * - * If @b is NULL, this is a no-op. - */ -void barrier_destroy(Barrier *b) { - if (!b) - return; - - b->me = safe_close(b->me); - b->them = safe_close(b->them); - safe_close_pair(b->pipe); - b->barriers = 0; -} - -/** - * barrier_set_role() - Set the local role of the barrier - * @b: barrier to operate on - * @role: role to set on the barrier - * - * This sets the roles on a barrier object. This is needed to know - * which side of the barrier you're on. Usually, the parent creates - * the barrier via barrier_create() and then calls fork() or clone(). - * Therefore, the FDs are duplicated and the child retains the same - * barrier object. - * - * Both sides need to call barrier_set_role() after fork() or clone() - * are done. If this is not done, barriers will not work correctly. - * - * Note that barriers could be supported without fork() or clone(). However, - * this is currently not needed so it hasn't been implemented. - */ -void barrier_set_role(Barrier *b, unsigned int role) { - int fd; - - assert(b); - assert(role == BARRIER_PARENT || role == BARRIER_CHILD); - /* make sure this is only called once */ - assert(b->pipe[0] >= 0 && b->pipe[1] >= 0); - - if (role == BARRIER_PARENT) - b->pipe[1] = safe_close(b->pipe[1]); - else { - b->pipe[0] = safe_close(b->pipe[0]); - - /* swap me/them for children */ - fd = b->me; - b->me = b->them; - b->them = fd; - } -} - -/* places barrier; returns false if we aborted, otherwise true */ -static bool barrier_write(Barrier *b, uint64_t buf) { - ssize_t len; - - /* prevent new sync-points if we already aborted */ - 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)); - - if (len != sizeof(buf)) - goto error; - - /* lock if we aborted */ - if (buf >= (uint64_t)BARRIER_ABORTION) { - if (barrier_they_aborted(b)) - b->barriers = BARRIER_WE_ABORTED; - else - b->barriers = BARRIER_I_ABORTED; - } else if (!barrier_is_aborted(b)) - b->barriers += buf; - - return !barrier_i_aborted(b); - -error: - /* If there is an unexpected error, we have to make this fatal. There - * is no way we can recover from sync-errors. Therefore, we close the - * pipe-ends and treat this as abortion. The other end will notice the - * pipe-close and treat it as abortion, too. */ - - safe_close_pair(b->pipe); - b->barriers = BARRIER_WE_ABORTED; - return false; -} - -/* waits for barriers; returns false if they aborted, otherwise true */ -static bool barrier_read(Barrier *b, int64_t comp) { - if (barrier_they_aborted(b)) - return false; - - while (b->barriers > comp) { - struct pollfd pfd[2] = { - { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1], - .events = POLLHUP }, - { .fd = b->them, - .events = POLLIN }}; - uint64_t buf; - int r; - - r = poll(pfd, 2, -1); - if (r < 0 && IN_SET(errno, EAGAIN, EINTR)) - continue; - else if (r < 0) - goto error; - - if (pfd[1].revents) { - ssize_t len; - - /* events on @them signal new data for us */ - len = read(b->them, &buf, sizeof(buf)); - if (len < 0 && IN_SET(errno, EAGAIN, EINTR)) - continue; - - if (len != sizeof(buf)) - goto error; - } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL)) - /* POLLHUP on the pipe tells us the other side exited. - * We treat this as implicit abortion. But we only - * handle it if there's no event on the eventfd. This - * guarantees that exit-abortions do not overwrite real - * barriers. */ - buf = BARRIER_ABORTION; - else - continue; - - /* lock if they aborted */ - if (buf >= (uint64_t)BARRIER_ABORTION) { - if (barrier_i_aborted(b)) - b->barriers = BARRIER_WE_ABORTED; - else - b->barriers = BARRIER_THEY_ABORTED; - } else if (!barrier_is_aborted(b)) - b->barriers -= buf; - } - - return !barrier_they_aborted(b); - -error: - /* If there is an unexpected error, we have to make this fatal. There - * is no way we can recover from sync-errors. Therefore, we close the - * pipe-ends and treat this as abortion. The other end will notice the - * pipe-close and treat it as abortion, too. */ - - safe_close_pair(b->pipe); - b->barriers = BARRIER_WE_ABORTED; - return false; -} - -/** - * barrier_place() - Place a new barrier - * @b: barrier object - * - * This places a new barrier on the barrier object. If either side already - * aborted, this is a no-op and returns "false". Otherwise, the barrier is - * placed and this returns "true". - * - * Returns: true if barrier was placed, false if either side aborted. - */ -bool barrier_place(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_write(b, BARRIER_SINGLE); - return true; -} - -/** - * barrier_abort() - Abort the synchronization - * @b: barrier object to abort - * - * This aborts the barrier-synchronization. If barrier_abort() was already - * called on this side, this is a no-op. Otherwise, the barrier is put into the - * ABORT-state and will stay there. The other side is notified about the - * abortion. Any following attempt to place normal barriers or to wait on normal - * barriers will return immediately as "false". - * - * You can wait for the other side to call barrier_abort(), too. Use - * barrier_wait_abortion() for that. - * - * Returns: false if the other side already aborted, true otherwise. - */ -bool barrier_abort(Barrier *b) { - assert(b); - - barrier_write(b, BARRIER_ABORTION); - return !barrier_they_aborted(b); -} - -/** - * barrier_wait_next() - Wait for the next barrier of the other side - * @b: barrier to operate on - * - * This waits until the other side places its next barrier. This is independent - * of any barrier-links and just waits for any next barrier of the other side. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_wait_next(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, b->barriers - 1); - return !barrier_is_aborted(b); -} - -/** - * barrier_wait_abortion() - Wait for the other side to abort - * @b: barrier to operate on - * - * This waits until the other side called barrier_abort(). This can be called - * regardless whether the local side already called barrier_abort() or not. - * - * If the other side has already aborted, this returns immediately. - * - * Returns: false if the local side aborted, true otherwise. - */ -bool barrier_wait_abortion(Barrier *b) { - assert(b); - - barrier_read(b, BARRIER_THEY_ABORTED); - return !barrier_i_aborted(b); -} - -/** - * barrier_sync_next() - Wait for the other side to place a next linked barrier - * @b: barrier to operate on - * - * This is like barrier_wait_next() and waits for the other side to call - * barrier_place(). However, this only waits for linked barriers. That means, if - * the other side already placed more barriers than (or as much as) we did, this - * returns immediately instead of waiting. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_sync_next(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, MAX((int64_t)0, b->barriers - 1)); - return !barrier_is_aborted(b); -} - -/** - * barrier_sync() - Wait for the other side to place as many barriers as we did - * @b: barrier to operate on - * - * This is like barrier_sync_next() but waits for the other side to call - * barrier_place() as often as we did (in total). If they already placed as much - * as we did (or more), this returns immediately instead of waiting. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_sync(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, 0); - return !barrier_is_aborted(b); -} diff --git a/src/basic/barrier.h b/src/basic/barrier.h deleted file mode 100644 index 6347fddc4d..0000000000 --- a/src/basic/barrier.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "macro.h" - -/* See source file for an API description. */ - -typedef struct Barrier Barrier; - -enum { - BARRIER_SINGLE = 1LL, - BARRIER_ABORTION = INT64_MAX, - - /* bias values to store state; keep @WE < @THEY < @I */ - BARRIER_BIAS = INT64_MIN, - BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL, - BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL, - BARRIER_I_ABORTED = BARRIER_BIAS + 3LL, -}; - -enum { - BARRIER_PARENT, - BARRIER_CHILD, -}; - -struct Barrier { - int me; - int them; - int pipe[2]; - int64_t barriers; -}; - -#define BARRIER_NULL {-1, -1, {-1, -1}, 0} - -int barrier_create(Barrier *obj); -void barrier_destroy(Barrier *b); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy); - -void barrier_set_role(Barrier *b, unsigned int role); - -bool barrier_place(Barrier *b); -bool barrier_abort(Barrier *b); - -bool barrier_wait_next(Barrier *b); -bool barrier_wait_abortion(Barrier *b); -bool barrier_sync_next(Barrier *b); -bool barrier_sync(Barrier *b); - -static inline bool barrier_i_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_they_aborted(Barrier *b) { - return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_we_aborted(Barrier *b) { - return b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_is_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_place_and_sync(Barrier *b) { - (void) barrier_place(b); - return barrier_sync(b); -} diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c deleted file mode 100644 index f4b12fc261..0000000000 --- a/src/basic/bitmap.c +++ /dev/null @@ -1,237 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bitmap.h" -#include "hashmap.h" -#include "macro.h" - -struct Bitmap { - uint64_t *bitmaps; - size_t n_bitmaps; - size_t bitmaps_allocated; -}; - -/* Bitmaps are only meant to store relatively small numbers - * (corresponding to, say, an enum), so it is ok to limit - * the max entry. 64k should be plenty. */ -#define BITMAPS_MAX_ENTRY 0xffff - -/* This indicates that we reached the end of the bitmap */ -#define BITMAP_END ((unsigned) -1) - -#define BITMAP_NUM_TO_OFFSET(n) ((n) / (sizeof(uint64_t) * 8)) -#define BITMAP_NUM_TO_REM(n) ((n) % (sizeof(uint64_t) * 8)) -#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(uint64_t) * 8 + (rem)) - -Bitmap *bitmap_new(void) { - return new0(Bitmap, 1); -} - -Bitmap *bitmap_copy(Bitmap *b) { - Bitmap *ret; - - ret = bitmap_new(); - if (!ret) - return NULL; - - ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps); - if (!ret->bitmaps) { - free(ret); - return NULL; - } - - ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps; - return ret; -} - -void bitmap_free(Bitmap *b) { - if (!b) - return; - - free(b->bitmaps); - free(b); -} - -int bitmap_ensure_allocated(Bitmap **b) { - Bitmap *a; - - assert(b); - - if (*b) - return 0; - - a = bitmap_new(); - if (!a) - return -ENOMEM; - - *b = a; - - return 0; -} - -int bitmap_set(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - assert(b); - - /* we refuse to allocate huge bitmaps */ - if (n > BITMAPS_MAX_ENTRY) - return -ERANGE; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) { - if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1)) - return -ENOMEM; - - b->n_bitmaps = offset + 1; - } - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - b->bitmaps[offset] |= bitmask; - - return 0; -} - -void bitmap_unset(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - if (!b) - return; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) - return; - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - b->bitmaps[offset] &= ~bitmask; -} - -bool bitmap_isset(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - if (!b) - return false; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) - return false; - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - return !!(b->bitmaps[offset] & bitmask); -} - -bool bitmap_isclear(Bitmap *b) { - unsigned i; - - if (!b) - return true; - - for (i = 0; i < b->n_bitmaps; i++) - if (b->bitmaps[i] != 0) - return false; - - return true; -} - -void bitmap_clear(Bitmap *b) { - - if (!b) - return; - - b->bitmaps = mfree(b->bitmaps); - b->n_bitmaps = 0; - b->bitmaps_allocated = 0; -} - -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) { - uint64_t bitmask; - unsigned offset, rem; - - assert(i); - assert(n); - - if (!b || i->idx == BITMAP_END) - return false; - - offset = BITMAP_NUM_TO_OFFSET(i->idx); - rem = BITMAP_NUM_TO_REM(i->idx); - bitmask = UINT64_C(1) << rem; - - for (; offset < b->n_bitmaps; offset ++) { - if (b->bitmaps[offset]) { - for (; bitmask; bitmask <<= 1, rem ++) { - if (b->bitmaps[offset] & bitmask) { - *n = BITMAP_OFFSET_TO_NUM(offset, rem); - i->idx = *n + 1; - - return true; - } - } - } - - rem = 0; - bitmask = 1; - } - - i->idx = BITMAP_END; - - return false; -} - -bool bitmap_equal(Bitmap *a, Bitmap *b) { - size_t common_n_bitmaps; - Bitmap *c; - unsigned i; - - if (a == b) - return true; - - if (!a != !b) - return false; - - if (!a) - return true; - - common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps); - if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0) - return false; - - c = a->n_bitmaps > b->n_bitmaps ? a : b; - for (i = common_n_bitmaps; i < c->n_bitmaps; i++) - if (c->bitmaps[i] != 0) - return false; - - return true; -} diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h deleted file mode 100644 index 63fdbe8bea..0000000000 --- a/src/basic/bitmap.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include - -#include "hashmap.h" -#include "macro.h" - -typedef struct Bitmap Bitmap; - -Bitmap *bitmap_new(void); -Bitmap *bitmap_copy(Bitmap *b); -int bitmap_ensure_allocated(Bitmap **b); -void bitmap_free(Bitmap *b); - -int bitmap_set(Bitmap *b, unsigned n); -void bitmap_unset(Bitmap *b, unsigned n); -bool bitmap_isset(Bitmap *b, unsigned n); -bool bitmap_isclear(Bitmap *b); -void bitmap_clear(Bitmap *b); - -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n); - -bool bitmap_equal(Bitmap *a, Bitmap *b); - -#define BITMAP_FOREACH(n, b, i) \ - for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free); - -#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep) diff --git a/src/basic/blkid-util.h b/src/basic/blkid-util.h deleted file mode 100644 index 7aa75eb091..0000000000 --- a/src/basic/blkid-util.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#ifdef HAVE_BLKID -#include -#endif - -#include "util.h" - -#ifdef HAVE_BLKID -DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe); -#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep) -#endif diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h deleted file mode 100644 index 66bdf9736e..0000000000 --- a/src/basic/btrfs-ctree.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "macro.h" -#include "sparse-endian.h" - -/* Stolen from btrfs' ctree.h */ - -struct btrfs_timespec { - le64_t sec; - le32_t nsec; -} _packed_; - -struct btrfs_disk_key { - le64_t objectid; - uint8_t type; - le64_t offset; -} _packed_; - -struct btrfs_inode_item { - le64_t generation; - le64_t transid; - le64_t size; - le64_t nbytes; - le64_t block_group; - le32_t nlink; - le32_t uid; - le32_t gid; - le32_t mode; - le64_t rdev; - le64_t flags; - le64_t sequence; - le64_t reserved[4]; - struct btrfs_timespec atime; - struct btrfs_timespec ctime; - struct btrfs_timespec mtime; - struct btrfs_timespec otime; -} _packed_; - -struct btrfs_root_item { - struct btrfs_inode_item inode; - le64_t generation; - le64_t root_dirid; - le64_t bytenr; - le64_t byte_limit; - le64_t bytes_used; - le64_t last_snapshot; - le64_t flags; - le32_t refs; - struct btrfs_disk_key drop_progress; - uint8_t drop_level; - uint8_t level; - le64_t generation_v2; - uint8_t uuid[BTRFS_UUID_SIZE]; - uint8_t parent_uuid[BTRFS_UUID_SIZE]; - uint8_t received_uuid[BTRFS_UUID_SIZE]; - le64_t ctransid; - le64_t otransid; - le64_t stransid; - le64_t rtransid; - struct btrfs_timespec ctime; - struct btrfs_timespec otime; - struct btrfs_timespec stime; - struct btrfs_timespec rtime; - le64_t reserved[8]; -} _packed_; - -#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) - -struct btrfs_qgroup_info_item { - le64_t generation; - le64_t rfer; - le64_t rfer_cmpr; - le64_t excl; - le64_t excl_cmpr; -} _packed_; - -#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0) -#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1) -#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2) -#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3) -#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4) -#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5) - -struct btrfs_qgroup_limit_item { - le64_t flags; - le64_t max_rfer; - le64_t max_excl; - le64_t rsv_rfer; - le64_t rsv_excl; -} _packed_; - -struct btrfs_root_ref { - le64_t dirid; - le64_t sequence; - le16_t name_len; -} _packed_; diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c deleted file mode 100644 index 359d85f2e8..0000000000 --- a/src/basic/btrfs-util.c +++ /dev/null @@ -1,2075 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LINUX_BTRFS_H -#include -#endif - -#include "alloc-util.h" -#include "btrfs-ctree.h" -#include "btrfs-util.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" -#include "macro.h" -#include "missing.h" -#include "path-util.h" -#include "selinux-util.h" -#include "smack-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 - * directory, or that it is located on btrfs, before invoking any - * btrfs ioctls. The ioctl numbers are reused by some device drivers - * (such as DRM), and hence might have bad effects when invoked on - * device nodes (that reference drivers) rather than fds to normal - * files or directories. */ - -static int validate_subvolume_name(const char *name) { - - if (!filename_is_valid(name)) - return -EINVAL; - - if (strlen(name) > BTRFS_SUBVOL_NAME_MAX) - return -E2BIG; - - return 0; -} - -static int open_parent(const char *path, int flags) { - _cleanup_free_ char *parent = NULL; - int fd; - - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - fd = open(parent, flags); - if (fd < 0) - return -errno; - - return fd; -} - -static int extract_subvolume_name(const char *path, const char **subvolume) { - const char *fn; - int r; - - assert(path); - assert(subvolume); - - fn = basename(path); - - r = validate_subvolume_name(fn); - if (r < 0) - return r; - - *subvolume = fn; - return 0; -} - -int btrfs_is_filesystem(int fd) { - struct statfs sfs; - - assert(fd >= 0); - - if (fstatfs(fd, &sfs) < 0) - return -errno; - - return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); -} - -int btrfs_is_subvol_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - /* On btrfs subvolumes always have the inode 256 */ - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return 0; - - 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; - const char *subvolume; - int r; - - assert(path); - - r = extract_subvolume_name(path, &subvolume); - if (r < 0) - return r; - - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return fd; - - strncpy(args.name, subvolume, sizeof(args.name)-1); - - if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) - return -errno; - - return 0; -} - -int btrfs_subvol_make_label(const char *path) { - int r; - - assert(path); - - r = mac_selinux_create_file_prepare(path, S_IFDIR); - if (r < 0) - return r; - - r = btrfs_subvol_make(path); - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(path, false, false); -} - -int btrfs_subvol_set_read_only_fd(int fd, bool b) { - uint64_t flags, nflags; - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) - return -errno; - - if (b) - nflags = flags | BTRFS_SUBVOL_RDONLY; - else - nflags = flags & ~BTRFS_SUBVOL_RDONLY; - - if (flags == nflags) - return 0; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0) - return -errno; - - return 0; -} - -int btrfs_subvol_set_read_only(const char *path, bool b) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_subvol_set_read_only_fd(fd, b); -} - -int btrfs_subvol_get_read_only_fd(int fd) { - uint64_t flags; - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) - return -errno; - - return !!(flags & BTRFS_SUBVOL_RDONLY); -} - -int btrfs_reflink(int infd, int outfd) { - struct stat st; - int r; - - assert(infd >= 0); - assert(outfd >= 0); - - /* Make sure we invoke the ioctl on a regular file, so that no - * device driver accidentally gets it. */ - - if (fstat(outfd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - r = ioctl(outfd, BTRFS_IOC_CLONE, infd); - if (r < 0) - return -errno; - - return 0; -} - -int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) { - struct btrfs_ioctl_clone_range_args args = { - .src_fd = infd, - .src_offset = in_offset, - .src_length = sz, - .dest_offset = out_offset, - }; - struct stat st; - int r; - - assert(infd >= 0); - assert(outfd >= 0); - assert(sz > 0); - - if (fstat(outfd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args); - if (r < 0) - return -errno; - - return 0; -} - -int btrfs_get_block_device_fd(int fd, dev_t *dev) { - struct btrfs_ioctl_fs_info_args fsi = {}; - uint64_t id; - int r; - - assert(fd >= 0); - assert(dev); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0) - return -errno; - - /* We won't do this for btrfs RAID */ - if (fsi.num_devices != 1) - return 0; - - for (id = 1; id <= fsi.max_id; id++) { - struct btrfs_ioctl_dev_info_args di = { - .devid = id, - }; - struct stat st; - - if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) { - if (errno == ENODEV) - continue; - - return -errno; - } - - if (stat((char*) di.path, &st) < 0) - return -errno; - - if (!S_ISBLK(st.st_mode)) - return -ENODEV; - - if (major(st.st_rdev) == 0) - return -ENODEV; - - *dev = st.st_rdev; - return 1; - } - - return -ENODEV; -} - -int btrfs_get_block_device(const char *path, dev_t *dev) { - _cleanup_close_ int fd = -1; - - assert(path); - assert(dev); - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return btrfs_get_block_device_fd(fd, dev); -} - -int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { - struct btrfs_ioctl_ino_lookup_args args = { - .objectid = BTRFS_FIRST_FREE_OBJECTID - }; - int r; - - assert(fd >= 0); - assert(ret); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) - return -errno; - - *ret = args.treeid; - return 0; -} - -int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) { - _cleanup_close_ int subvol_fd = -1; - - assert(fd >= 0); - assert(ret); - - subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (subvol_fd < 0) - return -errno; - - return btrfs_subvol_get_id_fd(subvol_fd, ret); -} - -static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) { - assert(args); - - /* the objectid, type, offset together make up the btrfs key, - * which is considered a single 136byte integer when - * comparing. This call increases the counter by one, dealing - * with the overflow between the overflows */ - - if (args->key.min_offset < (uint64_t) -1) { - args->key.min_offset++; - return true; - } - - if (args->key.min_type < (uint8_t) -1) { - args->key.min_type++; - args->key.min_offset = 0; - return true; - } - - if (args->key.min_objectid < (uint64_t) -1) { - args->key.min_objectid++; - args->key.min_offset = 0; - args->key.min_type = 0; - return true; - } - - return 0; -} - -static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) { - assert(args); - assert(h); - - args->key.min_objectid = h->objectid; - args->key.min_type = h->type; - args->key.min_offset = h->offset; -} - -static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) { - assert(args); - - /* Compare min and max */ - - if (args->key.min_objectid < args->key.max_objectid) - return -1; - if (args->key.min_objectid > args->key.max_objectid) - return 1; - - if (args->key.min_type < args->key.max_type) - return -1; - if (args->key.min_type > args->key.max_type) - return 1; - - if (args->key.min_offset < args->key.max_offset) - return -1; - if (args->key.min_offset > args->key.max_offset) - return 1; - - return 0; -} - -#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \ - for ((i) = 0, \ - (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \ - (i) < (args).key.nr_items; \ - (i)++, \ - (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len)) - -#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, uint64_t subvol_id, BtrfsSubvolInfo *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_ITEM_KEY, - .key.max_type = BTRFS_ROOT_ITEM_KEY, - - .key.min_offset = 0, - .key.max_offset = (uint64_t) -1, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - bool found = false; - 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 -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - - const struct btrfs_root_item *ri; - - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->objectid != subvol_id) - continue; - if (sh->type != BTRFS_ROOT_ITEM_KEY) - continue; - - /* Older versions of the struct lacked the otime setting */ - if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) - continue; - - ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + - (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; - - ret->subvol_id = subvol_id; - ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); - - assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); - memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); - memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); - - found = true; - goto finish; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - -finish: - if (!found) - return -ENODATA; - - return 0; -} - -int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { - - 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_STATUS_KEY, - .key.max_type = BTRFS_QGROUP_LIMIT_KEY, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - bool found_info = false, found_limit = false; - 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_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 (errno == ENOENT) /* quota tree is missing: quota 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->objectid != 0) - continue; - if (sh->offset != qgroupid) - continue; - - if (sh->type == BTRFS_QGROUP_INFO_KEY) { - const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - ret->referenced = le64toh(qii->rfer); - ret->exclusive = le64toh(qii->excl); - - found_info = true; - - } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) { - const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER) - ret->referenced_max = le64toh(qli->max_rfer); - else - ret->referenced_max = (uint64_t) -1; - - 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; - } - - if (found_info && found_limit) - goto finish; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - -finish: - if (!found_limit && !found_info) - return -ENODATA; - - if (!found_info) { - ret->referenced = (uint64_t) -1; - ret->exclusive = (uint64_t) -1; - } - - if (!found_limit) { - ret->referenced_max = (uint64_t) -1; - ret->exclusive_max = (uint64_t) -1; - } - - 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; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) - return -errno; - - return 0; -} - -int btrfs_defrag(const char *p) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_defrag_fd(fd); -} - -int btrfs_quota_enable_fd(int fd, bool b) { - struct btrfs_ioctl_quota_ctl_args args = { - .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE, - }; - int r; - - assert(fd >= 0); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0) - return -errno; - - return 0; -} - -int btrfs_quota_enable(const char *path, bool b) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_quota_enable_fd(fd, b); -} - -int btrfs_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, - .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER, - }; - unsigned c; - int r; - - assert(fd >= 0); - - 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.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_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_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) { - struct btrfs_ioctl_vol_args args = {}; - _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL; - _cleanup_close_ int loop_fd = -1, backing_fd = -1; - struct stat st; - dev_t dev = 0; - int r; - - /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */ - if (!FILE_SIZE_VALID(new_size)) - return -EINVAL; - - /* btrfs cannot handle file systems < 16M, hence use this as minimum */ - if (new_size < 16*1024*1024) - new_size = 16*1024*1024; - - r = btrfs_get_block_device_fd(fd, &dev); - if (r < 0) - return r; - if (r == 0) - return -ENODEV; - - if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0) - return -ENOMEM; - r = read_one_line_file(p, &backing); - if (r == -ENOENT) - return -ENODEV; - if (r < 0) - return r; - if (isempty(backing) || !path_is_absolute(backing)) - return -ENODEV; - - backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY); - if (backing_fd < 0) - return -errno; - - if (fstat(backing_fd, &st) < 0) - return -errno; - if (!S_ISREG(st.st_mode)) - return -ENODEV; - - if (new_size == (uint64_t) st.st_size) - return 0; - - if (grow_only && new_size < (uint64_t) st.st_size) - return -EINVAL; - - if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0) - return -ENOMEM; - loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY); - if (loop_fd < 0) - return -errno; - - if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name)) - return -EINVAL; - - if (new_size < (uint64_t) st.st_size) { - /* Decrease size: first decrease btrfs size, then shorten loopback */ - if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) - return -errno; - } - - if (ftruncate(backing_fd, new_size) < 0) - return -errno; - - if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0) - return -errno; - - if (new_size > (uint64_t) st.st_size) { - /* Increase size: first enlarge loopback, then increase btrfs size */ - if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) - return -errno; - } - - /* Make sure the free disk space is correctly updated for both file systems */ - (void) fsync(fd); - (void) fsync(backing_fd); - - return 1; -} - -int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return btrfs_resize_loopback_fd(fd, new_size, grow_only); -} - -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, - - .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, - .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, - - .key.min_type = BTRFS_ROOT_BACKREF_KEY, - .key.max_type = BTRFS_ROOT_BACKREF_KEY, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - struct btrfs_ioctl_vol_args vol_args = {}; - _cleanup_close_ int subvol_fd = -1; - struct stat st; - bool made_writable = false; - int r; - - assert(fd >= 0); - assert(subvolume); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -EINVAL; - - subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (subvol_fd < 0) - return -errno; - - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id); - if (r < 0) - return r; - } - - /* 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) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - _cleanup_free_ char *p = NULL; - const struct btrfs_root_ref *ref; - struct btrfs_ioctl_ino_lookup_args ino_args; - - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->type != BTRFS_ROOT_BACKREF_KEY) - continue; - if (sh->offset != subvol_id) - continue; - - ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); - if (!p) - return -ENOMEM; - - zero(ino_args); - ino_args.treeid = subvol_id; - ino_args.objectid = htole64(ref->dirid); - - if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) - return -errno; - - if (!made_writable) { - r = btrfs_subvol_set_read_only_fd(subvol_fd, false); - if (r < 0) - return r; - - made_writable = true; - } - - if (isempty(ino_args.name)) - /* Subvolume is in the top-level - * directory of the subvolume. */ - r = subvol_remove_children(subvol_fd, p, sh->objectid, flags); - else { - _cleanup_close_ int child_fd = -1; - - /* Subvolume is somewhere further down, - * hence we need to open the - * containing directory first */ - - child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (child_fd < 0) - return -errno; - - r = subvol_remove_children(child_fd, p, sh->objectid, flags); - } - if (r < 0) - return r; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - /* OK, the child subvolumes should all be gone now, let's try - * again to remove the subvolume */ - if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0) - return -errno; - - (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); - return 0; -} - -int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { - _cleanup_close_ int fd = -1; - const char *subvolume; - int r; - - assert(path); - - r = extract_subvolume_name(path, &subvolume); - if (r < 0) - return r; - - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return fd; - - return subvol_remove_children(fd, subvolume, 0, flags); -} - -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) { - - struct btrfs_ioctl_search_args args = { - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, - .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, - - .key.min_type = BTRFS_ROOT_BACKREF_KEY, - .key.max_type = BTRFS_ROOT_BACKREF_KEY, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - struct btrfs_ioctl_vol_args_v2 vol_args = { - .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0, - .fd = old_fd, - }; - _cleanup_close_ int subvolume_fd = -1; - uint64_t new_subvol_id; - int r; - - assert(old_fd >= 0); - assert(new_fd >= 0); - assert(subvolume); - - strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); - - if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0) - return -errno; - - if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) && - !(flags & BTRFS_SNAPSHOT_QUOTA)) - return 0; - - if (old_subvol_id == 0) { - r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id); - if (r < 0) - return r; - } - - r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id); - 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) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL; - struct btrfs_ioctl_ino_lookup_args ino_args; - const struct btrfs_root_ref *ref; - _cleanup_close_ int old_child_fd = -1, new_child_fd = -1; - - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->type != BTRFS_ROOT_BACKREF_KEY) - continue; - - /* Avoid finding the source subvolume a second - * time */ - if (sh->offset != old_subvol_id) - continue; - - /* Avoid running into loops if the new - * subvolume is below the old one. */ - if (sh->objectid == new_subvol_id) - continue; - - ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); - if (!p) - return -ENOMEM; - - zero(ino_args); - ino_args.treeid = old_subvol_id; - ino_args.objectid = htole64(ref->dirid); - - if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) - return -errno; - - /* The kernel returns an empty name if the - * subvolume is in the top-level directory, - * and otherwise appends a slash, so that we - * can just concatenate easily here, without - * adding a slash. */ - c = strappend(ino_args.name, p); - if (!c) - return -ENOMEM; - - old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (old_child_fd < 0) - return -errno; - - np = strjoin(subvolume, "/", ino_args.name, NULL); - if (!np) - return -ENOMEM; - - new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (new_child_fd < 0) - return -errno; - - if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - /* If the snapshot is read-only we - * need to mark it writable - * temporarily, to put the subsnapshot - * into place. */ - - if (subvolume_fd < 0) { - subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (subvolume_fd < 0) - return -errno; - } - - r = btrfs_subvol_set_read_only_fd(subvolume_fd, false); - if (r < 0) - return r; - } - - /* When btrfs clones the subvolumes, child - * subvolumes appear as empty directories. Remove - * them, so that we can create a new snapshot - * in their place */ - if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) { - int k = -errno; - - if (flags & BTRFS_SNAPSHOT_READ_ONLY) - (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true); - - return k; - } - - r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY); - - /* Restore the readonly flag */ - if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - int k; - - k = btrfs_subvol_set_read_only_fd(subvolume_fd, true); - if (r >= 0 && k < 0) - return k; - } - - if (r < 0) - return r; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - if (flags & BTRFS_SNAPSHOT_QUOTA) - (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id); - - return 0; -} - -int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) { - _cleanup_close_ int new_fd = -1; - const char *subvolume; - int r; - - assert(old_fd >= 0); - assert(new_path); - - r = btrfs_is_subvol_fd(old_fd); - if (r < 0) - return r; - if (r == 0) { - if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) - return -EISDIR; - - r = btrfs_subvol_make(new_path); - if (r < 0) - return r; - - r = copy_directory_fd(old_fd, new_path, true); - if (r < 0) { - (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) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; - } - } - - return 0; - } - - r = extract_subvolume_name(new_path, &subvolume); - if (r < 0) - return r; - - new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (new_fd < 0) - return new_fd; - - return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags); -} - -int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) { - _cleanup_close_ int old_fd = -1; - - assert(old_path); - assert(new_path); - - old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (old_fd < 0) - return -errno; - - return btrfs_subvol_snapshot_fd(old_fd, new_path, flags); -} - -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 deleted file mode 100644 index 1d852d502c..0000000000 --- a/src/basic/btrfs-util.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-id128.h" - -#include "time-util.h" - -typedef struct BtrfsSubvolInfo { - uint64_t subvol_id; - usec_t otime; - - sd_id128_t uuid; - sd_id128_t parent_uuid; - - bool read_only; -} BtrfsSubvolInfo; - -typedef struct BtrfsQuotaInfo { - uint64_t referenced; - uint64_t exclusive; - uint64_t referenced_max; - uint64_t exclusive_max; -} BtrfsQuotaInfo; - -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_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); - -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_parent(int fd, uint64_t subvol_id, uint64_t *ret); - -int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info); - -int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret); - -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_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_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_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_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/build.h b/src/basic/build.h deleted file mode 100644 index 633c2aaccb..0000000000 --- a/src/basic/build.h +++ /dev/null @@ -1,155 +0,0 @@ -#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 . -***/ - -#ifdef HAVE_PAM -#define _PAM_FEATURE_ "+PAM" -#else -#define _PAM_FEATURE_ "-PAM" -#endif - -#ifdef HAVE_AUDIT -#define _AUDIT_FEATURE_ "+AUDIT" -#else -#define _AUDIT_FEATURE_ "-AUDIT" -#endif - -#ifdef HAVE_SELINUX -#define _SELINUX_FEATURE_ "+SELINUX" -#else -#define _SELINUX_FEATURE_ "-SELINUX" -#endif - -#ifdef HAVE_APPARMOR -#define _APPARMOR_FEATURE_ "+APPARMOR" -#else -#define _APPARMOR_FEATURE_ "-APPARMOR" -#endif - -#ifdef HAVE_IMA -#define _IMA_FEATURE_ "+IMA" -#else -#define _IMA_FEATURE_ "-IMA" -#endif - -#ifdef HAVE_SMACK -#define _SMACK_FEATURE_ "+SMACK" -#else -#define _SMACK_FEATURE_ "-SMACK" -#endif - -#ifdef HAVE_SYSV_COMPAT -#define _SYSVINIT_FEATURE_ "+SYSVINIT" -#else -#define _SYSVINIT_FEATURE_ "-SYSVINIT" -#endif - -#ifdef HAVE_UTMP -#define _UTMP_FEATURE_ "+UTMP" -#else -#define _UTMP_FEATURE_ "-UTMP" -#endif - -#ifdef HAVE_LIBCRYPTSETUP -#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" -#else -#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" -#endif - -#ifdef HAVE_GCRYPT -#define _GCRYPT_FEATURE_ "+GCRYPT" -#else -#define _GCRYPT_FEATURE_ "-GCRYPT" -#endif - -#ifdef HAVE_GNUTLS -#define _GNUTLS_FEATURE_ "+GNUTLS" -#else -#define _GNUTLS_FEATURE_ "-GNUTLS" -#endif - -#ifdef HAVE_ACL -#define _ACL_FEATURE_ "+ACL" -#else -#define _ACL_FEATURE_ "-ACL" -#endif - -#ifdef HAVE_XZ -#define _XZ_FEATURE_ "+XZ" -#else -#define _XZ_FEATURE_ "-XZ" -#endif - -#ifdef HAVE_LZ4 -#define _LZ4_FEATURE_ "+LZ4" -#else -#define _LZ4_FEATURE_ "-LZ4" -#endif - -#ifdef HAVE_SECCOMP -#define _SECCOMP_FEATURE_ "+SECCOMP" -#else -#define _SECCOMP_FEATURE_ "-SECCOMP" -#endif - -#ifdef HAVE_BLKID -#define _BLKID_FEATURE_ "+BLKID" -#else -#define _BLKID_FEATURE_ "-BLKID" -#endif - -#ifdef HAVE_ELFUTILS -#define _ELFUTILS_FEATURE_ "+ELFUTILS" -#else -#define _ELFUTILS_FEATURE_ "-ELFUTILS" -#endif - -#ifdef HAVE_KMOD -#define _KMOD_FEATURE_ "+KMOD" -#else -#define _KMOD_FEATURE_ "-KMOD" -#endif - -#ifdef HAVE_LIBIDN -#define _IDN_FEATURE_ "+IDN" -#else -#define _IDN_FEATURE_ "-IDN" -#endif - -#define SYSTEMD_FEATURES \ - _PAM_FEATURE_ " " \ - _AUDIT_FEATURE_ " " \ - _SELINUX_FEATURE_ " " \ - _IMA_FEATURE_ " " \ - _APPARMOR_FEATURE_ " " \ - _SMACK_FEATURE_ " " \ - _SYSVINIT_FEATURE_ " " \ - _UTMP_FEATURE_ " " \ - _LIBCRYPTSETUP_FEATURE_ " " \ - _GCRYPT_FEATURE_ " " \ - _GNUTLS_FEATURE_ " " \ - _ACL_FEATURE_ " " \ - _XZ_FEATURE_ " " \ - _LZ4_FEATURE_ " " \ - _SECCOMP_FEATURE_ " " \ - _BLKID_FEATURE_ " " \ - _ELFUTILS_FEATURE_ " " \ - _KMOD_FEATURE_ " " \ - _IDN_FEATURE_ diff --git a/src/basic/bus-label.c b/src/basic/bus-label.c deleted file mode 100644 index d4531c7947..0000000000 --- a/src/basic/bus-label.c +++ /dev/null @@ -1,98 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "bus-label.h" -#include "hexdecoct.h" -#include "macro.h" - -char *bus_label_escape(const char *s) { - char *r, *t; - const char *f; - - assert_return(s, NULL); - - /* Escapes all chars that D-Bus' object path cannot deal - * with. Can be reversed with bus_path_unescape(). We special - * case the empty string. */ - - if (*s == 0) - return strdup("_"); - - r = new(char, strlen(s)*3 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) { - - /* Escape everything that is not a-zA-Z0-9. We also - * escape 0-9 if it's the first character */ - - if (!(*f >= 'A' && *f <= 'Z') && - !(*f >= 'a' && *f <= 'z') && - !(f > s && *f >= '0' && *f <= '9')) { - *(t++) = '_'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -char *bus_label_unescape_n(const char *f, size_t l) { - char *r, *t; - size_t i; - - assert_return(f, NULL); - - /* Special case for the empty string */ - if (l == 1 && *f == '_') - return strdup(""); - - r = new(char, l + 1); - if (!r) - return NULL; - - for (i = 0, t = r; i < l; ++i) { - if (f[i] == '_') { - int a, b; - - if (l - i < 3 || - (a = unhexchar(f[i + 1])) < 0 || - (b = unhexchar(f[i + 2])) < 0) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '_'; - } else { - *(t++) = (char) ((a << 4) | b); - i += 2; - } - } else - *(t++) = f[i]; - } - - *t = 0; - - return r; -} diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h deleted file mode 100644 index 62fb2c450c..0000000000 --- a/src/basic/bus-label.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -char *bus_label_escape(const char *s); -char *bus_label_unescape_n(const char *f, size_t l); - -static inline char *bus_label_unescape(const char *f) { - return bus_label_unescape_n(f, f ? strlen(f) : 0); -} diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c deleted file mode 100644 index e4cfab364e..0000000000 --- a/src/basic/calendarspec.c +++ /dev/null @@ -1,1127 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "calendarspec.h" -#include "fileio.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -/* Longest valid date/time range is 1970..2199 */ -#define MAX_RANGE_LEN 230 -#define BITS_WEEKDAYS 127 - -static void free_chain(CalendarComponent *c) { - CalendarComponent *n; - - while (c) { - n = c->next; - free(c); - c = n; - } -} - -void calendar_spec_free(CalendarSpec *c) { - - if (!c) - return; - - free_chain(c->year); - free_chain(c->month); - free_chain(c->day); - free_chain(c->hour); - free_chain(c->minute); - free_chain(c->microsecond); - - free(c); -} - -static int component_compare(const void *_a, const void *_b) { - CalendarComponent * const *a = _a, * const *b = _b; - - if ((*a)->value < (*b)->value) - return -1; - if ((*a)->value > (*b)->value) - return 1; - - if ((*a)->repeat < (*b)->repeat) - return -1; - if ((*a)->repeat > (*b)->repeat) - return 1; - - return 0; -} - -static void sort_chain(CalendarComponent **c) { - unsigned n = 0, k; - CalendarComponent **b, *i, **j, *next; - - assert(c); - - for (i = *c; i; i = i->next) - n++; - - if (n <= 1) - return; - - j = b = alloca(sizeof(CalendarComponent*) * n); - for (i = *c; i; i = i->next) - *(j++) = i; - - qsort(b, n, sizeof(CalendarComponent*), component_compare); - - b[n-1]->next = NULL; - next = b[n-1]; - - /* Drop non-unique entries */ - for (k = n-1; k > 0; k--) { - if (b[k-1]->value == next->value && - b[k-1]->repeat == next->repeat) { - free(b[k-1]); - continue; - } - - b[k-1]->next = next; - next = b[k-1]; - } - - *c = next; -} - -static void fix_year(CalendarComponent *c) { - /* Turns 12 → 2012, 89 → 1989 */ - - while (c) { - CalendarComponent *n = c->next; - - if (c->value >= 0 && c->value < 70) - c->value += 2000; - - if (c->value >= 70 && c->value < 100) - c->value += 1900; - - c = n; - } -} - -int calendar_spec_normalize(CalendarSpec *c) { - assert(c); - - if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) - c->weekdays_bits = -1; - - fix_year(c->year); - - sort_chain(&c->year); - sort_chain(&c->month); - sort_chain(&c->day); - sort_chain(&c->hour); - sort_chain(&c->minute); - sort_chain(&c->microsecond); - - return 0; -} - -_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) { - if (!c) - return true; - - if (c->value < from || c->value > to) - return false; - - if (c->value + c->repeat > to) - return false; - - if (c->next) - return chain_valid(c->next, from, to); - - return true; -} - -_pure_ bool calendar_spec_valid(CalendarSpec *c) { - assert(c); - - if (c->weekdays_bits > BITS_WEEKDAYS) - return false; - - if (!chain_valid(c->year, 1970, 2199)) - return false; - - if (!chain_valid(c->month, 1, 12)) - return false; - - if (!chain_valid(c->day, 1, 31)) - return false; - - if (!chain_valid(c->hour, 0, 23)) - return false; - - if (!chain_valid(c->minute, 0, 59)) - return false; - - if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1)) - return false; - - return true; -} - -static void format_weekdays(FILE *f, const CalendarSpec *c) { - static const char *const days[] = { - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat", - "Sun" - }; - - int l, x; - bool need_comma = false; - - assert(f); - assert(c); - assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS); - - for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) { - - if (c->weekdays_bits & (1 << x)) { - - if (l < 0) { - if (need_comma) - fputc(',', f); - else - need_comma = true; - - fputs(days[x], f); - l = x; - } - - } else if (l >= 0) { - - if (x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); - } - - l = -1; - } - } - - if (l >= 0 && x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); - } -} - -static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) { - assert(f); - - if (!c) { - fputc('*', f); - return; - } - - assert(c->value >= 0); - 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, usec); - } -} - -int calendar_spec_to_string(const CalendarSpec *c, char **p) { - char *buf = NULL; - size_t sz = 0; - FILE *f; - int r; - - assert(c); - assert(p); - - f = open_memstream(&buf, &sz); - if (!f) - return -ENOMEM; - - if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { - format_weekdays(f, c); - fputc(' ', f); - } - - format_chain(f, 4, c->year, false); - fputc('-', f); - format_chain(f, 2, c->month, false); - fputc('-', f); - format_chain(f, 2, c->day, false); - fputc(' ', f); - format_chain(f, 2, c->hour, false); - fputc(':', f); - format_chain(f, 2, c->minute, false); - fputc(':', f); - format_chain(f, 2, c->microsecond, true); - - if (c->utc) - fputs(" UTC", f); - - r = fflush_and_check(f); - if (r < 0) { - free(buf); - fclose(f); - return r; - } - - fclose(f); - - *p = buf; - return 0; -} - -static int parse_weekdays(const char **p, CalendarSpec *c) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Monday", 0 }, - { "Mon", 0 }, - { "Tuesday", 1 }, - { "Tue", 1 }, - { "Wednesday", 2 }, - { "Wed", 2 }, - { "Thursday", 3 }, - { "Thu", 3 }, - { "Friday", 4 }, - { "Fri", 4 }, - { "Saturday", 5 }, - { "Sat", 5 }, - { "Sunday", 6 }, - { "Sun", 6 } - }; - - int l = -1; - bool first = true; - - assert(p); - assert(*p); - assert(c); - - for (;;) { - unsigned i; - - if (!first && **p == ' ') - return 0; - - for (i = 0; i < ELEMENTSOF(day_nr); i++) { - size_t skip; - - if (!startswith_no_case(*p, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - - if ((*p)[skip] != '-' && - (*p)[skip] != '.' && - (*p)[skip] != ',' && - (*p)[skip] != ' ' && - (*p)[skip] != 0) - return -EINVAL; - - c->weekdays_bits |= 1 << day_nr[i].nr; - - if (l >= 0) { - int j; - - if (l > day_nr[i].nr) - return -EINVAL; - - for (j = l + 1; j < day_nr[i].nr; j++) - c->weekdays_bits |= 1 << j; - } - - *p += skip; - break; - } - - /* Couldn't find this prefix, so let's assume the - weekday was not specified and let's continue with - the date */ - if (i >= ELEMENTSOF(day_nr)) - return first ? 0 : -EINVAL; - - /* We reached the end of the string */ - if (**p == 0) - return 0; - - /* We reached the end of the weekday spec part */ - if (**p == ' ') { - *p += strspn(*p, " "); - return 0; - } - - if (**p == '.') { - if (l >= 0) - return -EINVAL; - - if ((*p)[1] != '.') - return -EINVAL; - - l = day_nr[i].nr; - *p += 1; - - /* Support ranges with "-" for backwards compatibility */ - } else if (**p == '-') { - if (l >= 0) - return -EINVAL; - - l = day_nr[i].nr; - } else - l = -1; - - *p += 1; - first = false; - } -} - -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, &ee, 10); - if (errno > 0) - return -errno; - if (ee == *p) - return -EINVAL; - if ((unsigned long) (int) value != value) - return -ERANGE; - e = ee; - - if (usec) { - if (value * USEC_PER_SEC / USEC_PER_SEC != value) - return -ERANGE; - - 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 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 prepend_component(const char **p, bool usec, CalendarComponent **c) { - unsigned long i, value, range_end, range_inc, 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; - } else if (e[0] == '.' && e[1] == '.') { - e += 2; - r = parse_component_decimal(&e, usec, &range_end); - if (r < 0) - return r; - - if (value >= range_end) - return -EINVAL; - - range_inc = usec ? USEC_PER_SEC : 1; - - /* Don't allow impossibly large ranges... */ - if (range_end - value >= MAX_RANGE_LEN * range_inc) - return -EINVAL; - - /* ...or ranges with only a single element */ - if (range_end - value < range_inc) - return -EINVAL; - - for (i = value; i <= range_end; i += range_inc) { - r = const_chain(i, c); - if (r < 0) - return r; - } - } - - if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':') - return -EINVAL; - - cc = new0(CalendarComponent, 1); - if (!cc) - return -ENOMEM; - - cc->value = value; - cc->repeat = repeat; - cc->next = *c; - - *p = e; - *c = cc; - - if (*e ==',') { - *p += 1; - return prepend_component(p, usec, c); - } - - return 0; -} - -static int parse_chain(const char **p, bool usec, CalendarComponent **c) { - const char *t; - CalendarComponent *cc = NULL; - int r; - - assert(p); - assert(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; - return 0; - } - - r = prepend_component(&t, usec, &cc); - if (r < 0) { - free_chain(cc); - return r; - } - - *p = t; - *c = cc; - return 0; -} - -static int parse_date(const char **p, CalendarSpec *c) { - const char *t; - int r; - CalendarComponent *first, *second, *third; - - assert(p); - assert(*p); - assert(c); - - t = *p; - - if (*t == 0) - return 0; - - r = parse_chain(&t, false, &first); - if (r < 0) - return r; - - /* Already the end? A ':' as separator? In that case this was a time, not a date */ - if (*t == 0 || *t == ':') { - free_chain(first); - return 0; - } - - if (*t != '-') { - free_chain(first); - return -EINVAL; - } - - t++; - r = parse_chain(&t, false, &second); - if (r < 0) { - free_chain(first); - return r; - } - - /* Got two parts, hence it's month and day */ - if (*t == ' ' || *t == 0) { - *p = t + strspn(t, " "); - c->month = first; - c->day = second; - return 0; - } - - if (*t != '-') { - free_chain(first); - free_chain(second); - return -EINVAL; - } - - t++; - r = parse_chain(&t, false, &third); - if (r < 0) { - free_chain(first); - free_chain(second); - return r; - } - - /* Got tree parts, hence it is year, month and day */ - if (*t == ' ' || *t == 0) { - *p = t + strspn(t, " "); - c->year = first; - c->month = second; - c->day = third; - return 0; - } - - free_chain(first); - free_chain(second); - free_chain(third); - return -EINVAL; -} - -static int parse_calendar_time(const char **p, CalendarSpec *c) { - CalendarComponent *h = NULL, *m = NULL, *s = NULL; - const char *t; - int r; - - assert(p); - assert(*p); - assert(c); - - t = *p; - - if (*t == 0) { - /* If no time is specified at all, but a date of some - * kind, then this means 00:00:00 */ - if (c->day || c->weekdays_bits > 0) - goto null_hour; - - goto finish; - } - - r = parse_chain(&t, false, &h); - if (r < 0) - goto fail; - - if (*t != ':') { - r = -EINVAL; - goto fail; - } - - t++; - r = parse_chain(&t, false, &m); - if (r < 0) - goto fail; - - /* Already at the end? Then it's hours and minutes, and seconds are 0 */ - if (*t == 0) { - if (m != NULL) - goto null_second; - - goto finish; - } - - if (*t != ':') { - r = -EINVAL; - goto fail; - } - - t++; - r = parse_chain(&t, true, &s); - if (r < 0) - goto fail; - - /* At the end? Then it's hours, minutes and seconds */ - if (*t == 0) - goto finish; - - r = -EINVAL; - goto fail; - -null_hour: - r = const_chain(0, &h); - if (r < 0) - goto fail; - - r = const_chain(0, &m); - if (r < 0) - goto fail; - -null_second: - r = const_chain(0, &s); - if (r < 0) - goto fail; - -finish: - *p = t; - c->hour = h; - c->minute = m; - c->microsecond = s; - - return 0; - -fail: - free_chain(h); - free_chain(m); - free_chain(s); - return r; -} - -int calendar_spec_from_string(const char *p, CalendarSpec **spec) { - CalendarSpec *c; - int r; - const char *utc; - - assert(p); - assert(spec); - - if (isempty(p)) - return -EINVAL; - - c = new0(CalendarSpec, 1); - 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->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "hourly")) { - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "daily")) { - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "monthly")) { - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "annually") || - strcaseeq(p, "yearly") || - strcaseeq(p, "anually") /* backwards compatibility */ ) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "weekly")) { - - c->weekdays_bits = 1; - - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "quarterly")) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(4, &c->month); - if (r < 0) - goto fail; - r = const_chain(7, &c->month); - if (r < 0) - goto fail; - r = const_chain(10, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "biannually") || - strcaseeq(p, "bi-annually") || - strcaseeq(p, "semiannually") || - strcaseeq(p, "semi-annually")) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(7, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else { - r = parse_weekdays(&p, c); - if (r < 0) - goto fail; - - r = parse_date(&p, c); - if (r < 0) - goto fail; - - r = parse_calendar_time(&p, c); - if (r < 0) - goto fail; - - if (*p != 0) { - r = -EINVAL; - goto fail; - } - } - - r = calendar_spec_normalize(c); - if (r < 0) - goto fail; - - if (!calendar_spec_valid(c)) { - r = -EINVAL; - goto fail; - } - - *spec = c; - return 0; - -fail: - calendar_spec_free(c); - return r; -} - -static int find_matching_component(const CalendarComponent *c, int *val) { - const CalendarComponent *n; - int d = -1; - bool d_set = false; - int r; - - assert(val); - - if (!c) - return 0; - - while (c) { - n = c->next; - - if (c->value >= *val) { - - if (!d_set || c->value < d) { - d = c->value; - d_set = true; - } - - } else if (c->repeat > 0) { - int k; - - k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat); - - if (!d_set || k < d) { - d = k; - d_set = true; - } - } - - c = n; - } - - if (!d_set) - return -ENOENT; - - r = *val != d; - *val = d; - return r; -} - -static bool tm_out_of_bounds(const struct tm *tm, bool utc) { - struct tm t; - assert(tm); - - t = *tm; - - if (mktime_or_timegm(&t, utc) == (time_t) -1) - return true; - - /* Did any normalization take place? If so, it was out of bounds before */ - return - t.tm_year != tm->tm_year || - t.tm_mon != tm->tm_mon || - t.tm_mday != tm->tm_mday || - t.tm_hour != tm->tm_hour || - t.tm_min != tm->tm_min || - t.tm_sec != tm->tm_sec; -} - -static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { - struct tm t; - int k; - - if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS) - return true; - - t = *tm; - 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, 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 */ - (void) mktime_or_timegm(&c, spec->utc); - c.tm_isdst = -1; - - c.tm_year += 1900; - r = find_matching_component(spec->year, &c.tm_year); - c.tm_year -= 1900; - - if (r > 0) { - c.tm_mon = 0; - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - } - if (r < 0) - return r; - if (tm_out_of_bounds(&c, spec->utc)) - return -ENOENT; - - c.tm_mon += 1; - r = find_matching_component(spec->month, &c.tm_mon); - c.tm_mon -= 1; - - if (r > 0) { - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - } - 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 = 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 = 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 = tm_usec = 0; - continue; - } - - if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { - c.tm_mday++; - 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 = tm_usec = 0; - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_mday++; - 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 = tm_usec = 0; - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_hour++; - c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - 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 = tm_usec = 0; - continue; - } - - *tm = c; - *usec = tm_usec; - return 0; - } -} - -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); - - 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, &tm_usec); - if (r < 0) - return r; - - t = mktime_or_timegm(&tm, spec->utc); - if (t == (time_t) -1) - return -EINVAL; - - *next = (usec_t) t * USEC_PER_SEC + tm_usec; - return 0; -} diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h deleted file mode 100644 index f6472c1244..0000000000 --- a/src/basic/calendarspec.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* A structure for specifying (possibly repetitive) points in calendar - * time, a la cron */ - -#include - -#include "time-util.h" -#include "util.h" - -typedef struct CalendarComponent { - int value; - int repeat; - - struct CalendarComponent *next; -} CalendarComponent; - -typedef struct CalendarSpec { - int weekdays_bits; - bool utc; - - CalendarComponent *year; - CalendarComponent *month; - CalendarComponent *day; - - CalendarComponent *hour; - CalendarComponent *minute; - CalendarComponent *microsecond; -} CalendarSpec; - -void calendar_spec_free(CalendarSpec *c); - -int calendar_spec_normalize(CalendarSpec *spec); -bool calendar_spec_valid(CalendarSpec *spec); - -int calendar_spec_to_string(const CalendarSpec *spec, char **p); -int calendar_spec_from_string(const char *p, CalendarSpec **spec); - -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next); diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c deleted file mode 100644 index 3e773a06f5..0000000000 --- a/src/basic/cap-list.c +++ /dev/null @@ -1,66 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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-from-name.h" -#include "cap-to-name.h" - -const char *capability_to_name(int id) { - - if (id < 0) - return NULL; - - if (id >= (int) ELEMENTSOF(capability_names)) - return NULL; - - return capability_names[id]; -} - -int capability_from_name(const char *name) { - const struct capability_name *sc; - int r, i; - - assert(name); - - /* Try to parse numeric capability */ - r = safe_atoi(name, &i); - if (r >= 0 && i >= 0) - return i; - - /* Try to parse string capability */ - sc = lookup_capability(name, strlen(name)); - if (!sc) - return -EINVAL; - - return sc->id; -} - -int capability_list_length(void) { - return (int) ELEMENTSOF(capability_names); -} diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h deleted file mode 100644 index c1f6b94ad3..0000000000 --- a/src/basic/cap-list.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -const char *capability_to_name(int id); -int capability_from_name(const char *name); -int capability_list_length(void); diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c deleted file mode 100644 index d4c5bd6937..0000000000 --- a/src/basic/capability-util.c +++ /dev/null @@ -1,361 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "capability-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "util.h" - -int have_effective_cap(int value) { - _cleanup_cap_free_ cap_t cap; - cap_flag_value_t fv; - - cap = cap_get_proc(); - if (!cap) - return -errno; - - if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) - return -errno; - else - return fv == CAP_SET; -} - -unsigned long cap_last_cap(void) { - static thread_local unsigned long saved; - static thread_local bool valid = false; - _cleanup_free_ char *content = NULL; - unsigned long p = 0; - int r; - - if (valid) - return saved; - - /* available since linux-3.2 */ - r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); - if (r >= 0) { - r = safe_atolu(content, &p); - if (r >= 0) { - saved = p; - valid = true; - return p; - } - } - - /* fall back to syscall-probing for pre linux-3.2 */ - p = (unsigned long) CAP_LAST_CAP; - - if (prctl(PR_CAPBSET_READ, p) < 0) { - - /* Hmm, look downwards, until we find one that - * works */ - for (p--; p > 0; p --) - if (prctl(PR_CAPBSET_READ, p) >= 0) - break; - - } else { - - /* Hmm, look upwards, until we find one that doesn't - * work */ - for (;; p++) - if (prctl(PR_CAPBSET_READ, p+1) < 0) - break; - } - - saved = p; - valid = true; - - return p; -} - -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; - int r; - - /* If we are run as PID 1 we will lack CAP_SETPCAP by default - * in the effective set (yes, the kernel drops that when - * executing init!), so get it back temporarily so that we can - * call PR_CAPBSET_DROP. */ - - after_cap = cap_get_proc(); - if (!after_cap) - return -errno; - - if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) - return -errno; - - if (fv != CAP_SET) { - _cleanup_cap_free_ cap_t temp_cap = NULL; - static const cap_value_t v = CAP_SETPCAP; - - temp_cap = cap_dup(after_cap); - if (!temp_cap) { - r = -errno; - goto finish; - } - - if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { - r = -errno; - goto finish; - } - - if (cap_set_proc(temp_cap) < 0) { - r = -errno; - goto finish; - } - } - - for (i = 0; i <= cap_last_cap(); i++) { - - if (!(keep & (UINT64_C(1) << i))) { - cap_value_t v; - - /* Drop it from the bounding set */ - if (prctl(PR_CAPBSET_DROP, i) < 0) { - r = -errno; - goto finish; - } - v = (cap_value_t) i; - - /* Also drop it from the inheritable set, so - * that anything we exec() loses the - * capability for good. */ - if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - - /* If we shall apply this right now drop it - * also from our own capability sets. */ - if (right_now) { - if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || - cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - } - } - } - - r = 0; - -finish: - if (cap_set_proc(after_cap) < 0) - return -errno; - - return r; -} - -static int drop_from_file(const char *fn, uint64_t keep) { - int r, k; - uint32_t hi, lo; - uint64_t current, after; - char *p; - - r = read_one_line_file(fn, &p); - if (r < 0) - return r; - - assert_cc(sizeof(hi) == sizeof(unsigned)); - assert_cc(sizeof(lo) == sizeof(unsigned)); - - k = sscanf(p, "%u %u", &lo, &hi); - free(p); - - if (k != 2) - return -EIO; - - current = (uint64_t) lo | ((uint64_t) hi << 32ULL); - after = current & keep; - - if (current == after) - return 0; - - lo = (unsigned) (after & 0xFFFFFFFFULL); - hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); - - if (asprintf(&p, "%u %u", lo, hi) < 0) - return -ENOMEM; - - r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); - free(p); - - return r; -} - -int capability_bounding_set_drop_usermode(uint64_t keep) { - int r; - - r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); - if (r < 0) - return r; - - r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); - if (r < 0) - return r; - - return r; -} - -int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { - _cleanup_cap_free_ cap_t d = NULL; - unsigned i, j = 0; - int r; - - /* Unfortunately we cannot leave privilege dropping to PID 1 - * here, since we want to run as user but want to keep some - * capabilities. Since file capabilities have been introduced - * this cannot be done across exec() anymore, unless our - * binary has the capability configured in the file system, - * which we want to avoid. */ - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "Failed to change group ID: %m"); - - if (setgroups(0, NULL) < 0) - return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); - - /* Ensure we keep the permitted caps across the setresuid() */ - if (prctl(PR_SET_KEEPCAPS, 1) < 0) - return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); - - r = setresuid(uid, uid, uid); - if (r < 0) - return log_error_errno(errno, "Failed to change user ID: %m"); - - if (prctl(PR_SET_KEEPCAPS, 0) < 0) - 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); - if (r < 0) - return log_error_errno(r, "Failed to drop capabilities: %m"); - - /* Now upgrade the permitted caps we still kept to effective caps */ - d = cap_init(); - if (!d) - return log_oom(); - - if (keep_capabilities) { - cap_value_t bits[u64log2(keep_capabilities) + 1]; - - for (i = 0; i < ELEMENTSOF(bits); i++) - if (keep_capabilities & (1ULL << i)) - bits[j++] = i; - - /* use enough bits */ - assert(i == 64 || (keep_capabilities >> i) == 0); - /* don't use too many bits */ - 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) - 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"); - } - - return 0; -} - -int drop_capability(cap_value_t cv) { - _cleanup_cap_free_ cap_t tmp_cap = NULL; - - tmp_cap = cap_get_proc(); - if (!tmp_cap) - return -errno; - - if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || - (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || - (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) - return -errno; - - if (cap_set_proc(tmp_cap) < 0) - return -errno; - - return 0; -} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h deleted file mode 100644 index 35a896e229..0000000000 --- a/src/basic/capability-util.h +++ /dev/null @@ -1,57 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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 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); - -int drop_capability(cap_value_t cv); - -DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free); -#define _cleanup_cap_free_ _cleanup_(cap_freep) - -static inline void cap_free_charpp(char **p) { - if (*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 deleted file mode 100644 index 472e24b7a3..0000000000 --- a/src/basic/cgroup-util.c +++ /dev/null @@ -1,2365 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "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 "proc-cmdline.h" -#include "process-util.h" -#include "set.h" -#include "special.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; - FILE *f; - int r; - - assert(_f); - - r = cg_get_path(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - f = fopen(fs, "re"); - if (!f) - return -errno; - - *_f = f; - return 0; -} - -int cg_read_pid(FILE *f, pid_t *_pid) { - unsigned long ul; - - /* Note that the cgroup.procs might contain duplicates! See - * cgroups.txt for details. */ - - assert(f); - assert(_pid); - - errno = 0; - if (fscanf(f, "%lu", &ul) != 1) { - - if (feof(f)) - return 0; - - return errno > 0 ? -errno : -EIO; - } - - if (ul <= 0) - return -EIO; - - *_pid = (pid_t) ul; - return 1; -} - -int cg_read_event(const char *controller, const char *path, const char *event, - char **val) -{ - _cleanup_free_ char *events = NULL, *content = NULL; - char *p, *line; - int r; - - r = cg_get_path(controller, path, "cgroup.events", &events); - if (r < 0) - return r; - - r = read_full_file(events, &content, NULL); - if (r < 0) - return r; - - p = content; - while ((line = strsep(&p, "\n"))) { - char *key; - - key = strsep(&line, " "); - if (!key || !line) - return -EINVAL; - - if (strcmp(key, event)) - continue; - - *val = strdup(line); - return 0; - } - - return -ENOENT; -} - -int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { - _cleanup_free_ char *fs = NULL; - int r; - DIR *d; - - assert(_d); - - /* This is not recursive! */ - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - d = opendir(fs); - if (!d) - return -errno; - - *_d = d; - return 0; -} - -int cg_read_subgroup(DIR *d, char **fn) { - struct dirent *de; - - assert(d); - assert(fn); - - FOREACH_DIRENT_ALL(de, d, return -errno) { - char *b; - - if (de->d_type != DT_DIR) - continue; - - if (streq(de->d_name, ".") || - streq(de->d_name, "..")) - continue; - - b = strdup(de->d_name); - if (!b) - return -ENOMEM; - - *fn = b; - return 1; - } - - return 0; -} - -int cg_rmdir(const char *controller, const char *path) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, NULL, &p); - if (r < 0) - return r; - - r = rmdir(p); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; -} - -int cg_kill( - const char *controller, - const char *path, - int sig, - CGroupFlags flags, - Set *s, - cg_kill_log_func_t log_kill, - void *userdata) { - - _cleanup_set_free_ Set *allocated_set = NULL; - bool done = false; - int r, ret = 0; - pid_t my_pid; - - assert(sig >= 0); - - /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send - * SIGCONT on SIGKILL. */ - if (IN_SET(sig, SIGCONT, SIGKILL)) - flags &= ~CGROUP_SIGCONT; - - /* This goes through the tasks list and kills them all. This - * is repeated until no further processes are added to the - * tasks list, to properly handle forking processes */ - - if (!s) { - s = allocated_set = set_new(NULL); - if (!s) - return -ENOMEM; - } - - my_pid = getpid(); - - do { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0; - done = true; - - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) - continue; - - if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) - continue; - - if (log_kill) - log_kill(pid, sig, userdata); - - /* If we haven't killed this process yet, kill - * it */ - if (kill(pid, sig) < 0) { - if (ret >= 0 && errno != ESRCH) - ret = -errno; - } else { - if (flags & CGROUP_SIGCONT) - (void) kill(pid, SIGCONT); - - if (ret == 0) - ret = 1; - } - - done = false; - - r = set_put(s, PID_TO_PTR(pid)); - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } - - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - - /* To avoid racing against processes which fork - * quicker than we can kill them we repeat this until - * no new pids need to be killed. */ - - } while (!done); - - return ret; -} - -int cg_kill_recursive( - const char *controller, - const char *path, - int sig, - CGroupFlags flags, - Set *s, - cg_kill_log_func_t log_kill, - void *userdata) { - - _cleanup_set_free_ Set *allocated_set = NULL; - _cleanup_closedir_ DIR *d = NULL; - int r, ret; - char *fn; - - assert(path); - assert(sig >= 0); - - if (!s) { - s = allocated_set = set_new(NULL); - if (!s) - return -ENOMEM; - } - - ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata); - - r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata); - if (r != 0 && ret >= 0) - ret = r; - } - if (ret >= 0 && r < 0) - ret = r; - - if (flags & CGROUP_REMOVE) { - r = cg_rmdir(controller, path); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) - return r; - } - - return ret; -} - -int cg_migrate( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - bool done = false; - _cleanup_set_free_ Set *s = NULL; - int r, ret = 0; - pid_t my_pid; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - s = set_new(NULL); - if (!s) - return -ENOMEM; - - my_pid = getpid(); - - do { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0; - done = true; - - r = cg_enumerate_processes(cfrom, pfrom, &f); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_pid(f, &pid)) > 0) { - - /* This might do weird stuff if we aren't a - * single-threaded program. However, we - * luckily know we are not */ - if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) - continue; - - if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) - continue; - - /* Ignore kernel threads. Since they can only - * exist in the root cgroup, we only check for - * them there. */ - if (cfrom && - (isempty(pfrom) || path_equal(pfrom, "/")) && - is_kernel_thread(pid) > 0) - continue; - - r = cg_attach(cto, pto, pid); - if (r < 0) { - if (ret >= 0 && r != -ESRCH) - ret = r; - } else if (ret == 0) - ret = 1; - - done = false; - - r = set_put(s, PID_TO_PTR(pid)); - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } - - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } while (!done); - - return ret; -} - -int cg_migrate_recursive( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - _cleanup_closedir_ DIR *d = NULL; - int r, ret = 0; - char *fn; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - ret = cg_migrate(cfrom, pfrom, cto, pto, flags); - - r = cg_enumerate_subgroups(cfrom, pfrom, &d); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(pfrom, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_migrate_recursive(cfrom, p, cto, pto, flags); - if (r != 0 && ret >= 0) - ret = r; - } - - if (r < 0 && ret >= 0) - ret = r; - - if (flags & CGROUP_REMOVE) { - r = cg_rmdir(cfrom, pfrom); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) - return r; - } - - return ret; -} - -int cg_migrate_recursive_fallback( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - int r; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags); - if (r < 0) { - char prefix[strlen(pto) + 1]; - - /* This didn't work? Then let's try all prefixes of the destination */ - - PATH_FOREACH_PREFIX(prefix, pto) { - int q; - - q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags); - if (q >= 0) - return q; - } - } - - return r; -} - -static const char *controller_to_dirname(const char *controller) { - const char *e; - - assert(controller); - - /* Converts a controller name to the directory name below - * /sys/fs/cgroup/ we want to mount it to. Effectively, this - * just cuts off the name= prefixed used for named - * hierarchies, if it is specified. */ - - e = startswith(controller, "name="); - if (e) - return e; - - return controller; -} - -static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) { - const char *dn; - char *t = NULL; - - assert(fs); - assert(controller); - - dn = controller_to_dirname(controller); - - if (isempty(path) && isempty(suffix)) - t = strappend("/sys/fs/cgroup/", dn); - else if (isempty(path)) - t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL); - else if (isempty(suffix)) - t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL); - else - t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = t; - return 0; -} - -static int join_path_unified(const char *path, const char *suffix, char **fs) { - char *t; - - assert(fs); - - if (isempty(path) && isempty(suffix)) - t = strdup("/sys/fs/cgroup"); - else if (isempty(path)) - t = strappend("/sys/fs/cgroup/", suffix); - else if (isempty(suffix)) - t = strappend("/sys/fs/cgroup/", path); - else - t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = t; - return 0; -} - -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { - int unified, r; - - assert(fs); - - if (!controller) { - char *t; - - /* If no controller is specified, we return the path - * *below* the controllers, without any prefix. */ - - if (!path && !suffix) - return -EINVAL; - - if (!suffix) - t = strdup(path); - else if (!path) - t = strdup(suffix); - else - t = strjoin(path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = path_kill_slashes(t); - return 0; - } - - if (!cg_controller_is_valid(controller)) - return -EINVAL; - - unified = cg_unified(); - if (unified < 0) - return unified; - - if (unified > 0) - r = join_path_unified(path, suffix, fs); - else - r = join_path_legacy(controller, path, suffix, fs); - if (r < 0) - return r; - - path_kill_slashes(*fs); - return 0; -} - -static int controller_is_accessible(const char *controller) { - int unified; - - assert(controller); - - /* Checks whether a specific controller is accessible, - * i.e. its hierarchy mounted. In the unified hierarchy all - * controllers are considered accessible, except for the named - * hierarchies */ - - if (!cg_controller_is_valid(controller)) - return -EINVAL; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) { - /* We don't support named hierarchies if we are using - * the unified hierarchy. */ - - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) - return 0; - - if (startswith(controller, "name=")) - return -EOPNOTSUPP; - - } else { - const char *cc, *dn; - - dn = controller_to_dirname(controller); - cc = strjoina("/sys/fs/cgroup/", dn); - - if (laccess(cc, F_OK) < 0) - return -errno; - } - - return 0; -} - -int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) { - int r; - - assert(controller); - assert(fs); - - /* Check if the specified controller is actually accessible */ - r = controller_is_accessible(controller); - if (r < 0) - return r; - - return cg_get_path(controller, path, suffix, fs); -} - -static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { - assert(path); - assert(sb); - assert(ftwbuf); - - if (typeflag != FTW_DP) - return 0; - - if (ftwbuf->level < 1) - return 0; - - (void) rmdir(path); - return 0; -} - -int cg_trim(const char *controller, const char *path, bool delete_root) { - _cleanup_free_ char *fs = NULL; - int r = 0; - - assert(path); - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - errno = 0; - if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { - if (errno == ENOENT) - r = 0; - else if (errno > 0) - r = -errno; - else - r = -EIO; - } - - if (delete_root) { - if (rmdir(fs) < 0 && errno != ENOENT) - return -errno; - } - - return r; -} - -int cg_create(const char *controller, const char *path) { - _cleanup_free_ char *fs = NULL; - int r; - - r = cg_get_path_and_check(controller, path, NULL, &fs); - if (r < 0) - return r; - - r = mkdir_parents(fs, 0755); - if (r < 0) - return r; - - if (mkdir(fs, 0755) < 0) { - - if (errno == EEXIST) - return 0; - - return -errno; - } - - return 1; -} - -int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { - int r, q; - - assert(pid >= 0); - - r = cg_create(controller, path); - if (r < 0) - return r; - - q = cg_attach(controller, path, pid); - if (q < 0) - return q; - - /* This does not remove the cgroup on failure */ - return r; -} - -int cg_attach(const char *controller, const char *path, pid_t pid) { - _cleanup_free_ char *fs = NULL; - char c[DECIMAL_STR_MAX(pid_t) + 2]; - int r; - - assert(path); - assert(pid >= 0); - - r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - if (pid == 0) - pid = getpid(); - - xsprintf(c, PID_FMT "\n", pid); - - return write_string_file(fs, c, 0); -} - -int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { - int r; - - assert(controller); - assert(path); - assert(pid >= 0); - - r = cg_attach(controller, path, pid); - if (r < 0) { - char prefix[strlen(path) + 1]; - - /* This didn't work? Then let's try all prefixes of - * the destination */ - - PATH_FOREACH_PREFIX(prefix, path) { - int q; - - q = cg_attach(controller, prefix, pid); - if (q >= 0) - return q; - } - } - - return r; -} - -int cg_set_group_access( - const char *controller, - const char *path, - mode_t mode, - uid_t uid, - gid_t gid) { - - _cleanup_free_ char *fs = NULL; - int r; - - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (mode != MODE_INVALID) - mode &= 0777; - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - return chmod_and_chown(fs, mode, uid, gid); -} - -int cg_set_task_access( - const char *controller, - const char *path, - mode_t mode, - uid_t uid, - gid_t gid) { - - _cleanup_free_ char *fs = NULL, *procs = NULL; - int r, unified; - - assert(path); - - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (mode != MODE_INVALID) - mode &= 0666; - - r = cg_get_path(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - r = chmod_and_chown(fs, mode, uid, gid); - if (r < 0) - return r; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified) - return 0; - - /* Compatibility, Always keep values for "tasks" in sync with - * "cgroup.procs" */ - if (cg_get_path(controller, path, "tasks", &procs) >= 0) - (void) chmod_and_chown(procs, mode, uid, gid); - - return 0; -} - -int cg_pid_get_path(const char *controller, pid_t pid, char **path) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *fs; - size_t cs = 0; - int unified; - - assert(path); - assert(pid >= 0); - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified == 0) { - if (controller) { - if (!cg_controller_is_valid(controller)) - return -EINVAL; - } else - controller = SYSTEMD_CGROUP_CONTROLLER; - - cs = strlen(controller); - } - - fs = procfs_file_alloca(pid, "cgroup"); - f = fopen(fs, "re"); - if (!f) - return errno == ENOENT ? -ESRCH : -errno; - - FOREACH_LINE(line, f, return -errno) { - char *e, *p; - - truncate_nl(line); - - if (unified) { - e = startswith(line, "0:"); - if (!e) - continue; - - e = strchr(e, ':'); - if (!e) - continue; - } else { - char *l; - size_t k; - const char *word, *state; - bool found = false; - - l = strchr(line, ':'); - if (!l) - continue; - - l++; - e = strchr(l, ':'); - if (!e) - continue; - - *e = 0; - FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { - if (k == cs && memcmp(word, controller, cs) == 0) { - found = true; - break; - } - } - - if (!found) - continue; - } - - p = strdup(e + 1); - if (!p) - return -ENOMEM; - - *path = p; - return 0; - } - - return -ENODATA; -} - -int cg_install_release_agent(const char *controller, const char *agent) { - _cleanup_free_ char *fs = NULL, *contents = NULL; - const char *sc; - int r, unified; - - assert(agent); - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified) /* doesn't apply to unified hierarchy */ - return -EOPNOTSUPP; - - r = cg_get_path(controller, NULL, "release_agent", &fs); - if (r < 0) - return r; - - r = read_one_line_file(fs, &contents); - if (r < 0) - return r; - - sc = strstrip(contents); - if (isempty(sc)) { - r = write_string_file(fs, agent, 0); - if (r < 0) - return r; - } else if (!path_equal(sc, agent)) - return -EEXIST; - - fs = mfree(fs); - r = cg_get_path(controller, NULL, "notify_on_release", &fs); - if (r < 0) - return r; - - contents = mfree(contents); - r = read_one_line_file(fs, &contents); - if (r < 0) - return r; - - sc = strstrip(contents); - if (streq(sc, "0")) { - r = write_string_file(fs, "1", 0); - if (r < 0) - return r; - - return 1; - } - - if (!streq(sc, "1")) - return -EIO; - - return 0; -} - -int cg_uninstall_release_agent(const char *controller) { - _cleanup_free_ char *fs = NULL; - int r, unified; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified) /* Doesn't apply to unified hierarchy */ - return -EOPNOTSUPP; - - r = cg_get_path(controller, NULL, "notify_on_release", &fs); - if (r < 0) - return r; - - r = write_string_file(fs, "0", 0); - if (r < 0) - return r; - - fs = mfree(fs); - - r = cg_get_path(controller, NULL, "release_agent", &fs); - if (r < 0) - return r; - - r = write_string_file(fs, "", 0); - if (r < 0) - return r; - - return 0; -} - -int cg_is_empty(const char *controller, const char *path) { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid; - int r; - - assert(path); - - r = cg_enumerate_processes(controller, path, &f); - if (r == -ENOENT) - return 1; - if (r < 0) - return r; - - r = cg_read_pid(f, &pid); - if (r < 0) - return r; - - return r == 0; -} - -int cg_is_empty_recursive(const char *controller, const char *path) { - int unified, r; - - assert(path); - - /* The root cgroup is always populated */ - if (controller && (isempty(path) || path_equal(path, "/"))) - return false; - - unified = cg_unified(); - if (unified < 0) - return unified; - - if (unified > 0) { - _cleanup_free_ char *t = NULL; - - /* On the unified hierarchy we can check empty state - * via the "populated" attribute of "cgroup.events". */ - - r = cg_read_event(controller, path, "populated", &t); - if (r < 0) - return r; - - return streq(t, "0"); - } else { - _cleanup_closedir_ DIR *d = NULL; - char *fn; - - r = cg_is_empty(controller, path); - if (r <= 0) - return r; - - r = cg_enumerate_subgroups(controller, path, &d); - if (r == -ENOENT) - return 1; - if (r < 0) - return r; - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_is_empty_recursive(controller, p); - if (r <= 0) - return r; - } - if (r < 0) - return r; - - return true; - } -} - -int cg_split_spec(const char *spec, char **controller, char **path) { - char *t = NULL, *u = NULL; - const char *e; - - assert(spec); - - if (*spec == '/') { - if (!path_is_safe(spec)) - return -EINVAL; - - if (path) { - t = strdup(spec); - if (!t) - return -ENOMEM; - - *path = path_kill_slashes(t); - } - - if (controller) - *controller = NULL; - - return 0; - } - - e = strchr(spec, ':'); - if (!e) { - if (!cg_controller_is_valid(spec)) - return -EINVAL; - - if (controller) { - t = strdup(spec); - if (!t) - return -ENOMEM; - - *controller = t; - } - - if (path) - *path = NULL; - - return 0; - } - - t = strndup(spec, e-spec); - if (!t) - return -ENOMEM; - if (!cg_controller_is_valid(t)) { - free(t); - return -EINVAL; - } - - if (isempty(e+1)) - u = NULL; - else { - u = strdup(e+1); - if (!u) { - free(t); - return -ENOMEM; - } - - if (!path_is_safe(u) || - !path_is_absolute(u)) { - free(t); - free(u); - return -EINVAL; - } - - path_kill_slashes(u); - } - - if (controller) - *controller = t; - else - free(t); - - if (path) - *path = u; - else - free(u); - - return 0; -} - -int cg_mangle_path(const char *path, char **result) { - _cleanup_free_ char *c = NULL, *p = NULL; - char *t; - int r; - - assert(path); - assert(result); - - /* First, check if it already is a filesystem path */ - if (path_startswith(path, "/sys/fs/cgroup")) { - - t = strdup(path); - if (!t) - return -ENOMEM; - - *result = path_kill_slashes(t); - return 0; - } - - /* Otherwise, treat it as cg spec */ - r = cg_split_spec(path, &c, &p); - if (r < 0) - return r; - - return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result); -} - -int cg_get_root_path(char **path) { - char *p, *e; - int r; - - assert(path); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p); - if (r < 0) - return r; - - e = endswith(p, "/" SPECIAL_INIT_SCOPE); - if (!e) - e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */ - if (!e) - e = endswith(p, "/system"); /* even more legacy */ - if (e) - *e = 0; - - *path = p; - return 0; -} - -int cg_shift_path(const char *cgroup, const char *root, const char **shifted) { - _cleanup_free_ char *rt = NULL; - char *p; - int r; - - assert(cgroup); - assert(shifted); - - if (!root) { - /* If the root was specified let's use that, otherwise - * let's determine it from PID 1 */ - - r = cg_get_root_path(&rt); - if (r < 0) - return r; - - root = rt; - } - - p = path_startswith(cgroup, root); - if (p && p > cgroup) - *shifted = p - 1; - else - *shifted = cgroup; - - return 0; -} - -int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) { - _cleanup_free_ char *raw = NULL; - const char *c; - int r; - - assert(pid >= 0); - assert(cgroup); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw); - if (r < 0) - return r; - - r = cg_shift_path(raw, root, &c); - if (r < 0) - return r; - - if (c == raw) { - *cgroup = raw; - raw = NULL; - } else { - char *n; - - n = strdup(c); - if (!n) - return -ENOMEM; - - *cgroup = n; - } - - return 0; -} - -int cg_path_decode_unit(const char *cgroup, char **unit) { - char *c, *s; - size_t n; - - assert(cgroup); - assert(unit); - - n = strcspn(cgroup, "/"); - if (n < 3) - return -ENXIO; - - c = strndupa(cgroup, n); - c = cg_unescape(c); - - if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return -ENXIO; - - s = strdup(c); - if (!s) - return -ENOMEM; - - *unit = s; - return 0; -} - -static bool valid_slice_name(const char *p, size_t n) { - - if (!p) - return false; - - if (n < strlen("x.slice")) - return false; - - if (memcmp(p + n - 6, ".slice", 6) == 0) { - char buf[n+1], *c; - - memcpy(buf, p, n); - buf[n] = 0; - - c = cg_unescape(buf); - - return unit_name_is_valid(c, UNIT_NAME_PLAIN); - } - - return false; -} - -static const char *skip_slices(const char *p) { - assert(p); - - /* Skips over all slice assignments */ - - for (;;) { - size_t n; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (!valid_slice_name(p, n)) - return p; - - p += n; - } -} - -int cg_path_get_unit(const char *path, char **ret) { - const char *e; - char *unit; - int r; - - assert(path); - assert(ret); - - e = skip_slices(path); - - r = cg_path_decode_unit(e, &unit); - if (r < 0) - return r; - - /* We skipped over the slices, don't accept any now */ - if (endswith(unit, ".slice")) { - free(unit); - return -ENXIO; - } - - *ret = unit; - return 0; -} - -int cg_pid_get_unit(pid_t pid, char **unit) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(unit); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_unit(cgroup, unit); -} - -/** - * Skip session-*.scope, but require it to be there. - */ -static const char *skip_session(const char *p) { - size_t n; - - if (isempty(p)) - return NULL; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (n < strlen("session-x.scope")) - return NULL; - - if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) { - char buf[n - 8 - 6 + 1]; - - memcpy(buf, p + 8, n - 8 - 6); - buf[n - 8 - 6] = 0; - - /* Note that session scopes never need unescaping, - * since they cannot conflict with the kernel's own - * names, hence we don't need to call cg_unescape() - * here. */ - - if (!session_id_valid(buf)) - return false; - - p += n; - p += strspn(p, "/"); - return p; - } - - return NULL; -} - -/** - * Skip user@*.service, but require it to be there. - */ -static const char *skip_user_manager(const char *p) { - size_t n; - - if (isempty(p)) - return NULL; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (n < strlen("user@x.service")) - return NULL; - - if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) { - char buf[n - 5 - 8 + 1]; - - memcpy(buf, p + 5, n - 5 - 8); - buf[n - 5 - 8] = 0; - - /* Note that user manager services never need unescaping, - * since they cannot conflict with the kernel's own - * names, hence we don't need to call cg_unescape() - * here. */ - - if (parse_uid(buf, NULL) < 0) - return NULL; - - p += n; - p += strspn(p, "/"); - - return p; - } - - return NULL; -} - -static const char *skip_user_prefix(const char *path) { - const char *e, *t; - - assert(path); - - /* Skip slices, if there are any */ - e = skip_slices(path); - - /* Skip the user manager, if it's in the path now... */ - t = skip_user_manager(e); - if (t) - return t; - - /* Alternatively skip the user session if it is in the path... */ - return skip_session(e); -} - -int cg_path_get_user_unit(const char *path, char **ret) { - const char *t; - - assert(path); - assert(ret); - - t = skip_user_prefix(path); - if (!t) - return -ENXIO; - - /* And from here on it looks pretty much the same as for a - * system unit, hence let's use the same parser from here - * on. */ - return cg_path_get_unit(t, ret); -} - -int cg_pid_get_user_unit(pid_t pid, char **unit) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(unit); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_user_unit(cgroup, unit); -} - -int cg_path_get_machine_name(const char *path, char **machine) { - _cleanup_free_ char *u = NULL; - const char *sl; - int r; - - r = cg_path_get_unit(path, &u); - if (r < 0) - return r; - - sl = strjoina("/run/systemd/machines/unit:", u); - return readlink_malloc(sl, machine); -} - -int cg_pid_get_machine_name(pid_t pid, char **machine) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(machine); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_machine_name(cgroup, machine); -} - -int cg_path_get_session(const char *path, char **session) { - _cleanup_free_ char *unit = NULL; - char *start, *end; - int r; - - assert(path); - - r = cg_path_get_unit(path, &unit); - if (r < 0) - return r; - - start = startswith(unit, "session-"); - if (!start) - return -ENXIO; - end = endswith(start, ".scope"); - if (!end) - return -ENXIO; - - *end = 0; - if (!session_id_valid(start)) - return -ENXIO; - - if (session) { - char *rr; - - rr = strdup(start); - if (!rr) - return -ENOMEM; - - *session = rr; - } - - return 0; -} - -int cg_pid_get_session(pid_t pid, char **session) { - _cleanup_free_ char *cgroup = NULL; - int r; - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_session(cgroup, session); -} - -int cg_path_get_owner_uid(const char *path, uid_t *uid) { - _cleanup_free_ char *slice = NULL; - char *start, *end; - int r; - - assert(path); - - r = cg_path_get_slice(path, &slice); - if (r < 0) - return r; - - start = startswith(slice, "user-"); - if (!start) - return -ENXIO; - end = endswith(start, ".slice"); - if (!end) - return -ENXIO; - - *end = 0; - if (parse_uid(start, uid) < 0) - return -ENXIO; - - return 0; -} - -int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) { - _cleanup_free_ char *cgroup = NULL; - int r; - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_owner_uid(cgroup, uid); -} - -int cg_path_get_slice(const char *p, char **slice) { - const char *e = NULL; - - assert(p); - assert(slice); - - /* Finds the right-most slice unit from the beginning, but - * stops before we come to the first non-slice unit. */ - - for (;;) { - size_t n; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (!valid_slice_name(p, n)) { - - if (!e) { - char *s; - - s = strdup("-.slice"); - if (!s) - return -ENOMEM; - - *slice = s; - return 0; - } - - return cg_path_decode_unit(e, slice); - } - - e = p; - p += n; - } -} - -int cg_pid_get_slice(pid_t pid, char **slice) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(slice); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_slice(cgroup, slice); -} - -int cg_path_get_user_slice(const char *p, char **slice) { - const char *t; - assert(p); - assert(slice); - - t = skip_user_prefix(p); - if (!t) - return -ENXIO; - - /* And now it looks pretty much the same as for a system - * slice, so let's just use the same parser from here on. */ - return cg_path_get_slice(t, slice); -} - -int cg_pid_get_user_slice(pid_t pid, char **slice) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(slice); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_user_slice(cgroup, slice); -} - -char *cg_escape(const char *p) { - bool need_prefix = false; - - /* This implements very minimal escaping for names to be used - * as file names in the cgroup tree: any name which might - * conflict with a kernel name or is prefixed with '_' is - * prefixed with a '_'. That way, when reading cgroup names it - * is sufficient to remove a single prefixing underscore if - * there is one. */ - - /* The return value of this function (unlike cg_unescape()) - * needs free()! */ - - if (p[0] == 0 || - p[0] == '_' || - p[0] == '.' || - streq(p, "notify_on_release") || - streq(p, "release_agent") || - streq(p, "tasks") || - startswith(p, "cgroup.")) - need_prefix = true; - else { - const char *dot; - - dot = strrchr(p, '.'); - if (dot) { - CGroupController c; - size_t l = dot - p; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - const char *n; - - n = cgroup_controller_to_string(c); - - if (l != strlen(n)) - continue; - - if (memcmp(p, n, l) != 0) - continue; - - need_prefix = true; - break; - } - } - } - - if (need_prefix) - return strappend("_", p); - - return strdup(p); -} - -char *cg_unescape(const char *p) { - assert(p); - - /* The return value of this function (unlike cg_escape()) - * doesn't need free()! */ - - if (p[0] == '_') - return (char*) p+1; - - return (char*) p; -} - -#define CONTROLLER_VALID \ - DIGITS LETTERS \ - "_" - -bool cg_controller_is_valid(const char *p) { - const char *t, *s; - - if (!p) - return false; - - s = startswith(p, "name="); - if (s) - p = s; - - if (*p == 0 || *p == '_') - return false; - - for (t = p; *t; t++) - if (!strchr(CONTROLLER_VALID, *t)) - return false; - - if (t - p > FILENAME_MAX) - return false; - - return true; -} - -int cg_slice_to_path(const char *unit, char **ret) { - _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL; - const char *dash; - int r; - - assert(unit); - assert(ret); - - if (streq(unit, "-.slice")) { - char *x; - - x = strdup(""); - if (!x) - return -ENOMEM; - *ret = x; - return 0; - } - - if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN)) - return -EINVAL; - - if (!endswith(unit, ".slice")) - return -EINVAL; - - r = unit_name_to_prefix(unit, &p); - if (r < 0) - return r; - - dash = strchr(p, '-'); - - /* Don't allow initial dashes */ - if (dash == p) - return -EINVAL; - - while (dash) { - _cleanup_free_ char *escaped = NULL; - char n[dash - p + sizeof(".slice")]; - - /* Don't allow trailing or double dashes */ - if (dash[1] == 0 || dash[1] == '-') - return -EINVAL; - - strcpy(stpncpy(n, p, dash - p), ".slice"); - if (!unit_name_is_valid(n, UNIT_NAME_PLAIN)) - return -EINVAL; - - escaped = cg_escape(n); - if (!escaped) - return -ENOMEM; - - if (!strextend(&s, escaped, "/", NULL)) - return -ENOMEM; - - dash = strchr(dash+1, '-'); - } - - e = cg_escape(unit); - if (!e) - return -ENOMEM; - - if (!strextend(&s, e, NULL)) - return -ENOMEM; - - *ret = s; - s = NULL; - - return 0; -} - -int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, attribute, &p); - if (r < 0) - return r; - - return write_string_file(p, value, 0); -} - -int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, attribute, &p); - if (r < 0) - return r; - - return read_one_line_file(p, ret); -} - -int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) { - CGroupController c; - int r, unified; - - /* This one will create a cgroup in our private tree, but also - * duplicate it in the trees specified in mask, and remove it - * in all others */ - - /* First create the cgroup in our own hierarchy. */ - r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path); - if (r < 0) - return r; - - /* If we are in the unified hierarchy, we are done now */ - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return 0; - - /* Otherwise, do the same in the other hierarchies */ - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *n; - - n = cgroup_controller_to_string(c); - - if (mask & bit) - (void) cg_create(n, path); - else if (supported & bit) - (void) cg_trim(n, path, true); - } - - return 0; -} - -int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { - CGroupController c; - int r, unified; - - r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid); - if (r < 0) - return r; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return 0; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *p = NULL; - - if (!(supported & bit)) - continue; - - if (path_callback) - p = path_callback(bit, userdata); - - if (!p) - p = path; - - (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid); - } - - return 0; -} - -int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { - Iterator i; - void *pidp; - int r = 0; - - SET_FOREACH(pidp, pids, i) { - pid_t pid = PTR_TO_PID(pidp); - int q; - - q = cg_attach_everywhere(supported, path, pid, path_callback, userdata); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { - CGroupController c; - int r = 0, unified; - - if (!path_equal(from, to)) { - r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE); - if (r < 0) - return r; - } - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *p = NULL; - - if (!(supported & bit)) - continue; - - if (to_callback) - p = to_callback(bit, userdata); - - if (!p) - p = to; - - (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0); - } - - return 0; -} - -int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) { - CGroupController c; - int r, unified; - - r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root); - if (r < 0) - return r; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - - if (!(supported & bit)) - continue; - - (void) cg_trim(cgroup_controller_to_string(c), path, delete_root); - } - - return 0; -} - -int cg_mask_supported(CGroupMask *ret) { - CGroupMask mask = 0; - int r, unified; - - /* Determines the mask of supported cgroup controllers. Only - * includes controllers we can make sense of and that are - * actually accessible. */ - - unified = cg_unified(); - if (unified < 0) - return unified; - if (unified > 0) { - _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; - const char *c; - - /* In the unified hierarchy we can read the supported - * and accessible controllers from a the top-level - * cgroup attribute */ - - r = cg_get_root_path(&root); - if (r < 0) - return r; - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path); - if (r < 0) - return r; - - r = read_one_line_file(path, &controllers); - if (r < 0) - return r; - - c = controllers; - for (;;) { - _cleanup_free_ char *n = NULL; - CGroupController v; - - r = extract_first_word(&c, &n, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - v = cgroup_controller_from_string(n); - if (v < 0) - continue; - - mask |= CGROUP_CONTROLLER_TO_MASK(v); - } - - /* Currently, we only support the memory, io and pids - * controller in the unified hierarchy, mask - * everything else off. */ - mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS; - - } else { - CGroupController c; - - /* In the legacy hierarchy, we check whether which - * hierarchies are mounted. */ - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - const char *n; - - n = cgroup_controller_to_string(c); - if (controller_is_accessible(n) >= 0) - mask |= CGROUP_CONTROLLER_TO_MASK(c); - } - } - - *ret = mask; - return 0; -} - -int cg_kernel_controllers(Set *controllers) { - _cleanup_fclose_ FILE *f = NULL; - char buf[LINE_MAX]; - int r; - - assert(controllers); - - /* Determines the full list of kernel-known controllers. Might - * include controllers we don't actually support, arbitrary - * named hierarchies and controllers that aren't currently - * accessible (because not mounted). */ - - f = fopen("/proc/cgroups", "re"); - if (!f) { - if (errno == ENOENT) - return 0; - return -errno; - } - - /* Ignore the header line */ - (void) fgets(buf, sizeof(buf), f); - - for (;;) { - char *controller; - int enabled = 0; - - errno = 0; - if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { - - if (feof(f)) - break; - - if (ferror(f) && errno > 0) - return -errno; - - return -EBADMSG; - } - - if (!enabled) { - free(controller); - continue; - } - - if (!cg_controller_is_valid(controller)) { - free(controller); - return -EBADMSG; - } - - r = set_consume(controllers, controller); - if (r < 0) - return r; - } - - return 0; -} - -static thread_local int unified_cache = -1; - -int cg_unified(void) { - struct statfs fs; - - /* Checks if we support the unified hierarchy. Returns an - * error when the cgroup hierarchies aren't mounted yet or we - * have any other trouble determining if the unified hierarchy - * is supported. */ - - if (unified_cache >= 0) - return unified_cache; - - if (statfs("/sys/fs/cgroup/", &fs) < 0) - return -errno; - - if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) - unified_cache = true; - else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) - unified_cache = false; - else - return -ENOMEDIUM; - - return unified_cache; -} - -void cg_unified_flush(void) { - unified_cache = -1; -} - -int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { - _cleanup_free_ char *fs = NULL; - CGroupController c; - int r, unified; - - assert(p); - - if (supported == 0) - return 0; - - unified = cg_unified(); - if (unified < 0) - return unified; - if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */ - return 0; - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs); - if (r < 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *n; - - if (!(supported & bit)) - continue; - - n = cgroup_controller_to_string(c); - { - char s[1 + strlen(n) + 1]; - - s[0] = mask & bit ? '+' : '-'; - strcpy(s + 1, n); - - r = write_string_file(fs, s, 0); - if (r < 0) - log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); - } - } - - return 0; -} - -bool cg_is_unified_wanted(void) { - static thread_local int wanted = -1; - int r, unified; - - /* If the hierarchy is already mounted, then follow whatever - * was chosen for it. */ - unified = cg_unified(); - if (unified >= 0) - return unified; - - /* Otherwise, let's see what the kernel command line has to - * say. Since checking that is expensive, let's cache the - * result. */ - if (wanted >= 0) - return wanted; - - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL); - if (r > 0) - return (wanted = true); - else { - _cleanup_free_ char *value = NULL; - - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value); - if (r < 0) - return false; - if (r == 0) - return (wanted = false); - - return (wanted = parse_boolean(value) > 0); - } -} - -bool cg_is_legacy_wanted(void) { - return !cg_is_unified_wanted(); -} - -int cg_weight_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_WEIGHT_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, -}; - -static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", - [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", - [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", - [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", -}; - -DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); - -int cg_cpu_shares_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_CPU_SHARES_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -int cg_blkio_weight_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_BLKIO_WEIGHT_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { - [CGROUP_CONTROLLER_CPU] = "cpu", - [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", - [CGROUP_CONTROLLER_IO] = "io", - [CGROUP_CONTROLLER_BLKIO] = "blkio", - [CGROUP_CONTROLLER_MEMORY] = "memory", - [CGROUP_CONTROLLER_DEVICES] = "devices", - [CGROUP_CONTROLLER_PIDS] = "pids", -}; - -DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h deleted file mode 100644 index 14ebde5fc9..0000000000 --- a/src/basic/cgroup-util.h +++ /dev/null @@ -1,236 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#include "def.h" -#include "hashmap.h" -#include "macro.h" -#include "set.h" - -/* An enum of well known cgroup controllers */ -typedef enum CGroupController { - CGROUP_CONTROLLER_CPU, - CGROUP_CONTROLLER_CPUACCT, - CGROUP_CONTROLLER_IO, - CGROUP_CONTROLLER_BLKIO, - CGROUP_CONTROLLER_MEMORY, - CGROUP_CONTROLLER_DEVICES, - CGROUP_CONTROLLER_PIDS, - _CGROUP_CONTROLLER_MAX, - _CGROUP_CONTROLLER_INVALID = -1, -} CGroupController; - -#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c)) - -/* A bit mask of well known cgroup controllers */ -typedef enum CGroupMask { - CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), - CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), - CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), - CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), - CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), - CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), - CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), - _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 -} CGroupMask; - -/* Special values for all weight knobs on unified hierarchy */ -#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_WEIGHT_MIN UINT64_C(1) -#define CGROUP_WEIGHT_MAX UINT64_C(10000) -#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) - -#define CGROUP_LIMIT_MIN UINT64_C(0) -#define CGROUP_LIMIT_MAX ((uint64_t) -1) - -static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_WEIGHT_INVALID || - (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); -} - -/* IO limits on unified hierarchy */ -typedef enum CGroupIOLimitType { - CGROUP_IO_RBPS_MAX, - CGROUP_IO_WBPS_MAX, - CGROUP_IO_RIOPS_MAX, - CGROUP_IO_WIOPS_MAX, - - _CGROUP_IO_LIMIT_TYPE_MAX, - _CGROUP_IO_LIMIT_TYPE_INVALID = -1 -} CGroupIOLimitType; - -extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; - -const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; -CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; - -/* Special values for the cpu.shares attribute */ -#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) -#define CGROUP_CPU_SHARES_MIN UINT64_C(2) -#define CGROUP_CPU_SHARES_MAX UINT64_C(262144) -#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) - -static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { - return - x == CGROUP_CPU_SHARES_INVALID || - (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); -} - -/* Special values for the blkio.weight attribute */ -#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) -#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) -#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) - -static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_BLKIO_WEIGHT_INVALID || - (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); -} - -/* - * General rules: - * - * We accept named hierarchies in the syntax "foo" and "name=foo". - * - * We expect that named hierarchies do not conflict in name with a - * kernel hierarchy, modulo the "name=" prefix. - * - * We always generate "normalized" controller names, i.e. without the - * "name=" prefix. - * - * We require absolute cgroup paths. When returning, we will always - * generate paths with multiple adjacent / removed. - */ - -int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); -int cg_read_pid(FILE *f, pid_t *_pid); -int cg_read_event(const char *controller, const char *path, const char *event, - char **val); - -int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); -int cg_read_subgroup(DIR *d, char **fn); - -typedef enum CGroupFlags { - CGROUP_SIGCONT = 1, - CGROUP_IGNORE_SELF = 2, - CGROUP_REMOVE = 4, -} CGroupFlags; - -typedef void (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); - -int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); -int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); - -int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); -int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); -int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); - -int cg_split_spec(const char *spec, char **controller, char **path); -int cg_mangle_path(const char *path, char **result); - -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); -int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs); - -int cg_pid_get_path(const char *controller, pid_t pid, char **path); - -int cg_trim(const char *controller, const char *path, bool delete_root); - -int cg_rmdir(const char *controller, const char *path); - -int cg_create(const char *controller, const char *path); -int cg_attach(const char *controller, const char *path, pid_t pid); -int cg_attach_fallback(const char *controller, const char *path, pid_t pid); -int cg_create_and_attach(const char *controller, const char *path, pid_t pid); - -int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); -int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); - -int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); - -int cg_install_release_agent(const char *controller, const char *agent); -int cg_uninstall_release_agent(const char *controller); - -int cg_is_empty(const char *controller, const char *path); -int cg_is_empty_recursive(const char *controller, const char *path); - -int cg_get_root_path(char **path); - -int cg_path_get_session(const char *path, char **session); -int cg_path_get_owner_uid(const char *path, uid_t *uid); -int cg_path_get_unit(const char *path, char **unit); -int cg_path_get_user_unit(const char *path, char **unit); -int cg_path_get_machine_name(const char *path, char **machine); -int cg_path_get_slice(const char *path, char **slice); -int cg_path_get_user_slice(const char *path, char **slice); - -int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted); -int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup); - -int cg_pid_get_session(pid_t pid, char **session); -int cg_pid_get_owner_uid(pid_t pid, uid_t *uid); -int cg_pid_get_unit(pid_t pid, char **unit); -int cg_pid_get_user_unit(pid_t pid, char **unit); -int cg_pid_get_machine_name(pid_t pid, char **machine); -int cg_pid_get_slice(pid_t pid, char **slice); -int cg_pid_get_user_slice(pid_t pid, char **slice); - -int cg_path_decode_unit(const char *cgroup, char **unit); - -char *cg_escape(const char *p); -char *cg_unescape(const char *p) _pure_; - -bool cg_controller_is_valid(const char *p); - -int cg_slice_to_path(const char *unit, char **ret); - -typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); - -int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path); -int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); -int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); -int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); -int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root); -int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p); - -int cg_mask_supported(CGroupMask *ret); - -int cg_kernel_controllers(Set *controllers); - -int cg_unified(void); -void cg_unified_flush(void); - -bool cg_is_unified_wanted(void); -bool cg_is_legacy_wanted(void); - -const char* cgroup_controller_to_string(CGroupController c) _const_; -CGroupController cgroup_controller_from_string(const char *s) _pure_; - -int cg_weight_parse(const char *s, uint64_t *ret); -int cg_cpu_shares_parse(const char *s, uint64_t *ret); -int cg_blkio_weight_parse(const char *s, uint64_t *ret); diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c deleted file mode 100644 index 2896a729af..0000000000 --- a/src/basic/chattr-util.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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 deleted file mode 100644 index 960cf6d5b3..0000000000 --- a/src/basic/chattr-util.h +++ /dev/null @@ -1,26 +0,0 @@ -#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 . -***/ - -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 deleted file mode 100644 index 7fe8d35ea5..0000000000 --- a/src/basic/clock-util.c +++ /dev/null @@ -1,165 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "clock-util.h" -#include "fd-util.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" - -int clock_get_hwclock(struct tm *tm) { - _cleanup_close_ int fd = -1; - - assert(tm); - - fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - /* This leaves the timezone fields of struct tm - * uninitialized! */ - if (ioctl(fd, RTC_RD_TIME, tm) < 0) - return -errno; - - /* We don't know daylight saving, so we reset this in order not - * to confuse mktime(). */ - tm->tm_isdst = -1; - - return 0; -} - -int clock_set_hwclock(const struct tm *tm) { - _cleanup_close_ int fd = -1; - - assert(tm); - - fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (ioctl(fd, RTC_SET_TIME, tm) < 0) - return -errno; - - return 0; -} - -int clock_is_localtime(const char* adjtime_path) { - _cleanup_fclose_ FILE *f; - - if (adjtime_path == NULL) - adjtime_path = "/etc/adjtime"; - - /* - * The third line of adjtime is "UTC" or "LOCAL" or nothing. - * # /etc/adjtime - * 0.0 0 0 - * 0 - * UTC - */ - f = fopen(adjtime_path, "re"); - if (f) { - char line[LINE_MAX]; - bool b; - - b = fgets(line, sizeof(line), f) && - fgets(line, sizeof(line), f) && - fgets(line, sizeof(line), f); - if (!b) - /* less than three lines -> default to UTC */ - return 0; - - truncate_nl(line); - return streq(line, "LOCAL"); - - } else if (errno != ENOENT) - return -errno; - - /* adjtime not present -> default to UTC */ - return 0; -} - -int clock_set_timezone(int *min) { - const struct timeval *tv_null = NULL; - struct timespec ts; - struct tm *tm; - int minutesdelta; - struct timezone tz; - - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - assert_se(tm = localtime(&ts.tv_sec)); - minutesdelta = tm->tm_gmtoff / 60; - - tz.tz_minuteswest = -minutesdelta; - tz.tz_dsttime = 0; /* DST_NONE */ - - /* - * If the RTC does not run in UTC but in local time, the very first - * call to settimeofday() will set the kernel's timezone and will warp the - * system clock, so that it runs in UTC instead of the local time we - * have read from the RTC. - */ - if (settimeofday(tv_null, &tz) < 0) - return negative_errno(); - - if (min) - *min = minutesdelta; - return 0; -} - -int clock_reset_timewarp(void) { - const struct timeval *tv_null = NULL; - struct timezone tz; - - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; /* DST_NONE */ - - /* - * The very first call to settimeofday() does time warp magic. Do a - * dummy call here, so the time warping is sealed and all later calls - * behave as expected. - */ - if (settimeofday(tv_null, &tz) < 0) - return -errno; - - return 0; -} - -#define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC) - -int clock_apply_epoch(void) { - struct timespec ts; - - if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC) - return 0; - - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0) - return -errno; - - return 1; -} diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h deleted file mode 100644 index 8830cd2f38..0000000000 --- a/src/basic/clock-util.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . -***/ - -#include - -int clock_is_localtime(const char* adjtime_path); -int clock_set_timezone(int *min); -int clock_reset_timewarp(void); -int clock_get_hwclock(struct tm *tm); -int clock_set_hwclock(const struct tm *tm); -int clock_apply_epoch(void); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c deleted file mode 100644 index c781610e14..0000000000 --- a/src/basic/conf-files.c +++ /dev/null @@ -1,166 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "conf-files.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "path-util.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); - assert(suffix); - - dirpath = prefix_roota(root, path); - - dir = opendir(dirpath); - if (!dir) { - if (errno == ENOENT) - return 0; - return -errno; - } - - FOREACH_DIRENT(de, dir, return -errno) { - char *p; - - if (!dirent_is_file_with_suffix(de, suffix)) - continue; - - p = strjoin(dirpath, "/", de->d_name, NULL); - if (!p) - return -ENOMEM; - - r = hashmap_put(h, basename(p), p); - if (r == -EEXIST) { - log_debug("Skipping overridden file: %s.", p); - free(p); - } else if (r < 0) { - free(p); - return r; - } else if (r == 0) { - log_debug("Duplicate file %s", p); - free(p); - } - } - - return 0; -} - -static int base_cmp(const void *a, const void *b) { - const char *s1, *s2; - - s1 = *(char * const *)a; - s2 = *(char * const *)b; - return strcmp(basename(s1), basename(s2)); -} - -static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { - _cleanup_hashmap_free_ Hashmap *fh = NULL; - char **files, **p; - int r; - - assert(strv); - assert(suffix); - - /* This alters the dirs string array */ - if (!path_strv_resolve_uniq(dirs, root)) - return -ENOMEM; - - fh = hashmap_new(&string_hash_ops); - if (!fh) - return -ENOMEM; - - STRV_FOREACH(p, dirs) { - r = files_add(fh, root, *p, suffix); - if (r == -ENOMEM) - return r; - if (r < 0) - log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); - } - - files = hashmap_get_strv(fh); - if (!files) - return -ENOMEM; - - qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); - *strv = files; - - return 0; -} - -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { - _cleanup_strv_free_ char **copy = NULL; - - assert(strv); - assert(suffix); - - copy = strv_copy((char**) dirs); - if (!copy) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, copy); -} - -int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { - _cleanup_strv_free_ char **dirs = NULL; - va_list ap; - - assert(strv); - assert(suffix); - - va_start(ap, dir); - dirs = strv_new_ap(dir, ap); - va_end(ap); - - if (!dirs) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, dirs); -} - -int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { - _cleanup_strv_free_ char **dirs = NULL; - - assert(strv); - assert(suffix); - - dirs = strv_split_nulstr(d); - if (!dirs) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, dirs); -} diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h deleted file mode 100644 index e00e0e81fb..0000000000 --- a/src/basic/conf-files.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering - Copyright 2010-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); -int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); -int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); diff --git a/src/basic/copy.c b/src/basic/copy.c deleted file mode 100644 index 9883f5fa31..0000000000 --- a/src/basic/copy.c +++ /dev/null @@ -1,603 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "btrfs-util.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 "missing.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*1024u) - -static ssize_t try_copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { - static int have = -1; - ssize_t r; - - if (have == false) - return -ENOSYS; - - r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); - if (_unlikely_(have < 0)) - have = r >= 0 || errno != ENOSYS; - if (r >= 0) - return r; - else - return -errno; -} - -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { - bool try_cfr = true, try_sendfile = true, try_splice = true; - int r; - size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ - - assert(fdf >= 0); - assert(fdt >= 0); - - /* Try btrfs reflinks first. */ - if (try_reflink && - max_bytes == (uint64_t) -1 && - lseek(fdf, 0, SEEK_CUR) == 0 && - lseek(fdt, 0, SEEK_CUR) == 0) { - - r = btrfs_reflink(fdf, fdt); - if (r >= 0) - return 0; /* we copied the whole thing, hence hit EOF, return 0 */ - } - - for (;;) { - ssize_t n; - - if (max_bytes != (uint64_t) -1) { - if (max_bytes <= 0) - return 1; /* return > 0 if we hit the max_bytes limit */ - - if (m > max_bytes) - m = max_bytes; - } - - /* First try copy_file_range(), unless we already tried */ - if (try_cfr) { - n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); - if (n < 0) { - if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) - return n; - - try_cfr = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* First try sendfile(), unless we already tried */ - if (try_sendfile) { - n = sendfile(fdt, fdf, NULL, m); - if (n < 0) { - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - try_sendfile = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* Then try splice, unless we already tried */ - if (try_splice) { - n = splice(fdf, NULL, fdt, NULL, m, 0); - if (n < 0) { - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - try_splice = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* As a fallback just copy bits by hand */ - { - uint8_t buf[MIN(m, COPY_BUFFER_SIZE)]; - - n = read(fdf, buf, sizeof buf); - if (n < 0) - return -errno; - if (n == 0) /* EOF */ - break; - - r = loop_write(fdt, buf, (size_t) n, false); - if (r < 0) - return r; - } - - next: - if (max_bytes != (uint64_t) -1) { - assert(max_bytes >= (uint64_t) n); - max_bytes -= n; - } - /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, - * so reduce our maximum by the amount we already copied, - * but don't go below our copy buffer size, unless we are - * close the limit of bytes we are allowed to copy. */ - m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); - } - - return 0; /* return 0 if we hit EOF earlier than the size limit */ -} - -static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { - _cleanup_free_ char *target = NULL; - int r; - - assert(from); - assert(st); - assert(to); - - r = readlinkat_malloc(df, from, &target); - if (r < 0) - return r; - - if (symlinkat(target, dt, to) < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; - - return 0; -} - -static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) { - _cleanup_close_ int fdf = -1, fdt = -1; - struct timespec ts[2]; - int r, q; - - assert(from); - assert(st); - assert(to); - - fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fdf < 0) - return -errno; - - fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); - if (fdt < 0) - return -errno; - - r = copy_bytes(fdf, fdt, (uint64_t) -1, true); - if (r < 0) { - unlinkat(dt, to, 0); - return r; - } - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - ts[0] = st->st_atim; - ts[1] = st->st_mtim; - (void) futimens(fdt, ts); - - (void) copy_xattr(fdf, fdt); - - q = close(fdt); - fdt = -1; - - if (q < 0) { - r = -errno; - unlinkat(dt, to, 0); - } - - return r; -} - -static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) { - int r; - - assert(from); - assert(st); - assert(to); - - r = mkfifoat(dt, to, st->st_mode & 07777); - if (r < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - r = -errno; - - if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) - r = -errno; - - return r; -} - -static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { - int r; - - assert(from); - assert(st); - assert(to); - - r = mknodat(dt, to, st->st_mode, st->st_rdev); - if (r < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - r = -errno; - - if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) - r = -errno; - - return r; -} - -static int fd_copy_directory( - int df, - const char *from, - const struct stat *st, - int dt, - const char *to, - dev_t original_device, - bool merge) { - - _cleanup_close_ int fdf = -1, fdt = -1; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - bool created; - int r; - - assert(st); - assert(to); - - if (from) - fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - else - fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); - if (fdf < 0) - return -errno; - - d = fdopendir(fdf); - if (!d) - return -errno; - fdf = -1; - - r = mkdirat(dt, to, st->st_mode & 07777); - if (r >= 0) - created = true; - else if (errno == EEXIST && merge) - created = false; - else - return -errno; - - fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fdt < 0) - return -errno; - - r = 0; - - FOREACH_DIRENT_ALL(de, d, return -errno) { - struct stat buf; - int q; - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { - r = -errno; - continue; - } - - if (buf.st_dev != original_device) - continue; - - if (S_ISREG(buf.st_mode)) - q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISDIR(buf.st_mode)) - q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); - else if (S_ISLNK(buf.st_mode)) - q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISFIFO(buf.st_mode)) - q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) - q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else - q = -EOPNOTSUPP; - - if (q == -EEXIST && merge) - q = 0; - - if (q < 0) - r = q; - } - - if (created) { - struct timespec ut[2] = { - st->st_atim, - st->st_mtim - }; - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - (void) copy_xattr(dirfd(d), fdt); - (void) futimens(fdt, ut); - } - - return r; -} - -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) { - struct stat st; - - assert(from); - assert(to); - - if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; - - if (S_ISREG(st.st_mode)) - return fd_copy_regular(fdf, from, &st, fdt, to); - else if (S_ISDIR(st.st_mode)) - return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge); - else if (S_ISLNK(st.st_mode)) - return fd_copy_symlink(fdf, from, &st, fdt, to); - else if (S_ISFIFO(st.st_mode)) - return fd_copy_fifo(fdf, from, &st, fdt, to); - else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) - return fd_copy_node(fdf, from, &st, fdt, to); - else - return -EOPNOTSUPP; -} - -int copy_tree(const char *from, const char *to, bool merge) { - return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge); -} - -int copy_directory_fd(int dirfd, const char *to, bool merge) { - struct stat st; - - assert(dirfd >= 0); - assert(to); - - if (fstat(dirfd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -ENOTDIR; - - return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); -} - -int copy_directory(const char *from, const char *to, bool merge) { - struct stat st; - - assert(from); - assert(to); - - if (lstat(from, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -ENOTDIR; - - return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); -} - -int copy_file_fd(const char *from, int fdt, bool try_reflink) { - _cleanup_close_ int fdf = -1; - int r; - - assert(from); - assert(fdt >= 0); - - fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fdf < 0) - return -errno; - - r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); - - (void) copy_times(fdf, fdt); - (void) copy_xattr(fdf, fdt); - - return r; -} - -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) { - int fdt = -1, r; - - assert(from); - assert(to); - - RUN_WITH_UMASK(0000) { - fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); - if (fdt < 0) - return -errno; - } - - if (chattr_flags != 0) - (void) chattr_fd(fdt, chattr_flags, (unsigned) -1); - - r = copy_file_fd(from, fdt, true); - if (r < 0) { - close(fdt); - unlink(to); - return r; - } - - if (close(fdt) < 0) { - unlink_noerrno(to); - return -errno; - } - - return 0; -} - -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags); - if (r < 0) - return r; - - if (replace) { - r = renameat(AT_FDCWD, t, AT_FDCWD, to); - if (r < 0) - r = -errno; - } else - r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to); - if (r < 0) { - (void) unlink_noerrno(t); - return r; - } - - return 0; -} - -int copy_times(int fdf, int fdt) { - struct timespec ut[2]; - struct stat st; - usec_t crtime = 0; - - assert(fdf >= 0); - assert(fdt >= 0); - - if (fstat(fdf, &st) < 0) - return -errno; - - ut[0] = st.st_atim; - ut[1] = st.st_mtim; - - if (futimens(fdt, ut) < 0) - return -errno; - - if (fd_getcrtime(fdf, &crtime) >= 0) - (void) fd_setcrtime(fdt, crtime); - - return 0; -} - -int copy_xattr(int fdf, int fdt) { - _cleanup_free_ char *bufa = NULL, *bufb = NULL; - size_t sza = 100, szb = 100; - ssize_t n; - int ret = 0; - const char *p; - - for (;;) { - bufa = malloc(sza); - if (!bufa) - return -ENOMEM; - - n = flistxattr(fdf, bufa, sza); - if (n == 0) - return 0; - if (n > 0) - break; - if (errno != ERANGE) - return -errno; - - sza *= 2; - - bufa = mfree(bufa); - } - - p = bufa; - while (n > 0) { - size_t l; - - l = strlen(p); - assert(l < (size_t) n); - - if (startswith(p, "user.")) { - ssize_t m; - - if (!bufb) { - bufb = malloc(szb); - if (!bufb) - return -ENOMEM; - } - - m = fgetxattr(fdf, p, bufb, szb); - if (m < 0) { - if (errno == ERANGE) { - szb *= 2; - bufb = mfree(bufb); - continue; - } - - return -errno; - } - - if (fsetxattr(fdt, p, bufb, m, 0) < 0) - ret = -errno; - } - - p += l + 1; - n -= l + 1; - } - - return ret; -} diff --git a/src/basic/copy.h b/src/basic/copy.h deleted file mode 100644 index b5d08ebafe..0000000000 --- a/src/basic/copy.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -int copy_file_fd(const char *from, int to, bool try_reflink); -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags); -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags); -int copy_tree(const char *from, const char *to, bool merge); -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); -int copy_directory_fd(int dirfd, const char *to, bool merge); -int copy_directory(const char *from, const char *to, bool merge); -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); -int copy_times(int fdf, int fdt); -int copy_xattr(int fdf, int fdt); diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c deleted file mode 100644 index 95ed6928ff..0000000000 --- a/src/basic/cpu-set-util.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#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 deleted file mode 100644 index 6f49d9afb0..0000000000 --- a/src/basic/cpu-set-util.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 . -***/ - -#include - -#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 deleted file mode 100644 index 1a7a0f4928..0000000000 --- a/src/basic/def.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 . -***/ - -#include "util.h" - -#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC) -#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) -#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) - -#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) -#define DEFAULT_START_LIMIT_BURST 5 - -/* The default time after which exit-on-idle services exit. This - * should be kept lower than the watchdog timeout, because otherwise - * 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 - -#ifdef HAVE_SPLIT_USR -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" \ - "/lib/kbd/keymaps/\0" -#else -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" -#endif - -#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" -#define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS -#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" -#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" - -#define PLYMOUTH_SOCKET { \ - .un.sun_family = AF_UNIX, \ - .un.sun_path = "\0/org/freedesktop/plymouthd", \ - } - -#ifndef TTY_GID -#define TTY_GID 5 -#endif - -#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 deleted file mode 100644 index 38c0628a90..0000000000 --- a/src/basic/device-nodes.c +++ /dev/null @@ -1,80 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "device-nodes.h" -#include "utf8.h" - -int whitelisted_char_for_devnode(char c, const char *white) { - - if ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - strchr("#+-.:=@_", c) != NULL || - (white != NULL && strchr(white, c) != NULL)) - return 1; - - return 0; -} - -int encode_devnode_name(const char *str, char *str_enc, size_t len) { - size_t i, j; - - if (str == NULL || str_enc == NULL) - return -EINVAL; - - for (i = 0, j = 0; str[i] != '\0'; i++) { - int seqlen; - - seqlen = utf8_encoded_valid_unichar(&str[i]); - if (seqlen > 1) { - - if (len-j < (size_t)seqlen) - return -EINVAL; - - memcpy(&str_enc[j], &str[i], seqlen); - j += seqlen; - i += (seqlen-1); - - } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) { - - if (len-j < 4) - return -EINVAL; - - sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); - j += 4; - - } else { - if (len-j < 1) - return -EINVAL; - - str_enc[j] = str[i]; - j++; - } - } - - if (len-j < 1) - return -EINVAL; - - str_enc[j] = '\0'; - return 0; -} diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h deleted file mode 100644 index 94f385abcb..0000000000 --- a/src/basic/device-nodes.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -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 deleted file mode 100644 index 59067121b7..0000000000 --- a/src/basic/dirent-util.c +++ /dev/null @@ -1,74 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#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 (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) - return false; - - if (hidden_or_backup_file(de->d_name)) - return false; - - return true; -} - -bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { - assert(de); - - if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) - return false; - - if (de->d_name[0] == '.') - return false; - - return endswith(de->d_name, suffix); -} diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h deleted file mode 100644 index b91d04908f..0000000000 --- a/src/basic/dirent-util.h +++ /dev/null @@ -1,52 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#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_or_backup_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 deleted file mode 100644 index 7f5fddb700..0000000000 --- a/src/basic/env-util.c +++ /dev/null @@ -1,624 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "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" - -#define VALID_CHARS_ENV_NAME \ - DIGITS LETTERS \ - "_" - -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - -static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - - if (!e) - return false; - - if (n <= 0) - return false; - - if (e[0] >= '0' && e[0] <= '9') - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the equal sign and trailing NUL this - * hence leaves ARG_MAX-2 as longest possible variable - * name. */ - if (n > ARG_MAX - 2) - return false; - - for (p = e; p < e + n; p++) - if (!strchr(VALID_CHARS_ENV_NAME, *p)) - return false; - - return true; -} - -bool env_name_is_valid(const char *e) { - if (!e) - return false; - - return env_name_is_valid_n(e, strlen(e)); -} - -bool env_value_is_valid(const char *e) { - if (!e) - return false; - - if (!utf8_is_valid(e)) - return false; - - /* bash allows tabs in environment variables, and so should - * we */ - if (string_has_cc(e, "\t")) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > ARG_MAX - 3) - return false; - - return true; -} - -bool env_assignment_is_valid(const char *e) { - const char *eq; - - eq = strchr(e, '='); - if (!eq) - return false; - - if (!env_name_is_valid_n(e, eq - e)) - return false; - - if (!env_value_is_valid(eq + 1)) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ - if (strlen(e) > ARG_MAX - 1) - return false; - - return true; -} - -bool strv_env_is_valid(char **e) { - char **p, **q; - - STRV_FOREACH(p, e) { - size_t k; - - if (!env_assignment_is_valid(*p)) - return false; - - /* Check if there are duplicate assginments */ - k = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, k) && (*q)[k] == '=') - return false; - } - - return true; -} - -bool strv_env_name_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; - - STRV_FOREACH(p, l) { - if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -static int env_append(char **r, char ***k, char **a) { - assert(r); - assert(k); - - if (!a) - return 0; - - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ - - for (; *a; a++) { - char **j; - size_t n; - - n = strcspn(*a, "="); - - if ((*a)[n] == '=') - n++; - - for (j = r; j < *k; j++) - if (strneq(*j, *a, n)) - break; - - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) - return -ENOMEM; - } - - return 0; -} - -char **strv_env_merge(unsigned n_lists, ...) { - size_t n = 0; - char **l, **k, **r; - va_list ap; - unsigned i; - - /* Merges an arbitrary number of environment sets */ - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - n += strv_length(l); - } - va_end(ap); - - r = new(char*, n+1); - if (!r) - return NULL; - - k = r; - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; - } - va_end(ap); - - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; -} - -_pure_ static bool env_match(const char *t, const char *pattern) { - assert(t); - assert(pattern); - - /* pattern a matches string a - * a matches a= - * a matches a=b - * a= matches a= - * a=b matches a=b - * a= does not match a - * a=b does not match a= - * a=b does not match a - * a=b does not match a=c */ - - if (streq(t, pattern)) - return true; - - if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); - - return strneq(t, pattern, l) && t[l] == '='; - } - - return false; -} - -char **strv_env_delete(char **x, unsigned n_lists, ...) { - size_t n, i = 0; - char **k, **r; - va_list ap; - - /* Deletes every entry from x that is mentioned in the other - * string lists */ - - n = strv_length(x); - - r = new(char*, n+1); - if (!r) - return NULL; - - STRV_FOREACH(k, x) { - unsigned v; - - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { - char **l, **j; - - l = va_arg(ap, char**); - STRV_FOREACH(j, l) - if (env_match(*k, *j)) - goto skip; - } - va_end(ap); - - r[i] = strdup(*k); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - continue; - - skip: - va_end(ap); - } - - r[i] = NULL; - - assert(i <= n); - - return r; -} - -char **strv_env_unset(char **l, const char *p) { - - char **f, **t; - - if (!l) - return NULL; - - assert(p); - - /* Drops every occurrence of the env var setting p in the - * string list. Edits in-place. */ - - for (f = t = l; *f; f++) { - - if (env_match(*f, p)) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_unset_many(char **l, ...) { - - char **f, **t; - - if (!l) - return NULL; - - /* Like strv_env_unset() but applies many at once. Edits in-place. */ - - for (f = t = l; *f; f++) { - bool found = false; - const char *p; - va_list ap; - - va_start(ap, l); - - while ((p = va_arg(ap, const char*))) { - if (env_match(*f, p)) { - found = true; - break; - } - } - - va_end(ap); - - if (found) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_set(char **x, const char *p) { - - char **k, **r; - char* m[2] = { (char*) p, NULL }; - - /* Overrides the env var setting of p, returns a new copy */ - - r = new(char*, strv_length(x)+2); - if (!r) - return NULL; - - k = r; - if (env_append(r, &k, x) < 0) - goto fail; - - if (env_append(r, &k, m) < 0) - goto fail; - - *k = NULL; - - return r; - -fail: - strv_free(r); - return NULL; -} - -char *strv_env_get_n(char **l, const char *name, size_t k) { - char **i; - - assert(name); - - if (k <= 0) - return NULL; - - STRV_FOREACH(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; - - return NULL; -} - -char *strv_env_get(char **l, const char *name) { - assert(name); - - return strv_env_get_n(l, name, strlen(name)); -} - -char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; - int k = 0; - - STRV_FOREACH(p, e) { - size_t n; - bool duplicate = false; - - if (!env_assignment_is_valid(*p)) { - if (invalid_callback) - invalid_callback(*p, userdata); - free(*p); - continue; - } - - n = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, n) && (*q)[n] == '=') { - duplicate = true; - break; - } - - if (duplicate) { - free(*p); - continue; - } - - e[k++] = *p; - } - - if (e) - e[k] = NULL; - - return e; -} - -char *replace_env(const char *format, char **env) { - enum { - WORD, - CURLY, - VARIABLE - } state = WORD; - - const char *e, *word = format; - char *r = NULL, *k; - - assert(format); - - for (e = format; *e; e ++) { - - switch (state) { - - case WORD: - if (*e == '$') - state = CURLY; - break; - - case CURLY: - if (*e == '{') { - k = strnappend(r, word, e-word-1); - if (!k) - goto fail; - - free(r); - r = k; - - word = e-1; - state = VARIABLE; - - } else if (*e == '$') { - k = strnappend(r, word, e-word); - if (!k) - goto fail; - - free(r); - r = k; - - word = e+1; - state = WORD; - } else - state = WORD; - break; - - case VARIABLE: - if (*e == '}') { - const char *t; - - t = strempty(strv_env_get_n(env, word+2, e-word-2)); - - k = strappend(r, t); - if (!k) - goto fail; - - free(r); - r = k; - - word = e+1; - state = WORD; - } - break; - } - } - - k = strnappend(r, word, e-word); - if (!k) - goto fail; - - free(r); - return k; - -fail: - free(r); - return NULL; -} - -char **replace_env_argv(char **argv, char **env) { - char **ret, **i; - unsigned k = 0, l = 0; - - l = strv_length(argv); - - ret = new(char*, l+1); - if (!ret) - return NULL; - - STRV_FOREACH(i, argv) { - - /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') { - char *e; - char **w, **m = NULL; - unsigned q; - - e = strv_env_get(env, *i+1); - if (e) { - int r; - - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES); - if (r < 0) { - ret[k] = NULL; - strv_free(ret); - return NULL; - } - } else - m = NULL; - - q = strv_length(m); - l = l + q - 1; - - w = realloc(ret, sizeof(char*) * (l+1)); - if (!w) { - ret[k] = NULL; - strv_free(ret); - strv_free(m); - return NULL; - } - - ret = w; - if (m) { - memcpy(ret + k, m, q * sizeof(char*)); - free(m); - } - - k += q; - continue; - } - - /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env); - if (!ret[k]) { - strv_free(ret); - return NULL; - } - k++; - } - - ret[k] = NULL; - return ret; -} - -int 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 deleted file mode 100644 index b1fef704c2..0000000000 --- a/src/basic/env-util.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "macro.h" - -bool env_name_is_valid(const char *e); -bool env_value_is_valid(const char *e); -bool env_assignment_is_valid(const char *e); - -char *replace_env(const char *format, char **env); -char **replace_env_argv(char **argv, char **env); - -bool strv_env_is_valid(char **e); -#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) -char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); - -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, ...); -char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */ - -char **strv_env_set(char **x, const char *p); /* New copy ... */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_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 deleted file mode 100644 index 31b66bad5e..0000000000 --- a/src/basic/errno-list.c +++ /dev/null @@ -1,57 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "errno-list.h" -#include "macro.h" - -static const struct errno_name* lookup_errno(register const char *str, - register unsigned int len); - -#include "errno-from-name.h" -#include "errno-to-name.h" - -const char *errno_to_name(int id) { - - if (id < 0) - id = -id; - - if (id >= (int) ELEMENTSOF(errno_names)) - return NULL; - - return errno_names[id]; -} - -int errno_from_name(const char *name) { - const struct errno_name *sc; - - assert(name); - - sc = lookup_errno(name, strlen(name)); - if (!sc) - return -EINVAL; - - assert(sc->id > 0); - return sc->id; -} - -int errno_max(void) { - return ELEMENTSOF(errno_names); -} diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h deleted file mode 100644 index 4eec0cc786..0000000000 --- a/src/basic/errno-list.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -const char *errno_to_name(int id); -int errno_from_name(const char *name); - -int errno_max(void); diff --git a/src/basic/escape.c b/src/basic/escape.c deleted file mode 100644 index 01daf11ce7..0000000000 --- a/src/basic/escape.c +++ /dev/null @@ -1,502 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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, char32_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; - char32_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; - char32_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; - bool eight_bit = false; - char32_t u; - 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; -} - -char *octescape(const char *s, size_t len) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ - - r = new(char, len * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + len; f++) { - - if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { - *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); - } 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 deleted file mode 100644 index deaa4def28..0000000000 --- a/src/basic/escape.h +++ /dev/null @@ -1,54 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#include "string-util.h" -#include "missing.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, char32_t *ret, bool *eight_bit); - -char *xescape(const char *s, const char *bad); -char *octescape(const char *s, size_t len); - -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 deleted file mode 100644 index 5697e8d132..0000000000 --- a/src/basic/ether-addr-util.c +++ /dev/null @@ -1,125 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "ether-addr-util.h" -#include "macro.h" -#include "string-util.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; -} - -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { - assert(a); - assert(b); - - return a->ether_addr_octet[0] == b->ether_addr_octet[0] && - a->ether_addr_octet[1] == b->ether_addr_octet[1] && - a->ether_addr_octet[2] == b->ether_addr_octet[2] && - a->ether_addr_octet[3] == b->ether_addr_octet[3] && - a->ether_addr_octet[4] == b->ether_addr_octet[4] && - a->ether_addr_octet[5] == b->ether_addr_octet[5]; -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (hexoff == NULL) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } - - assert(s); - assert(ret); - - sep = s[strspn(s, hex)]; - if (sep == '\n') - return -EINVAL; - if (strchr(":.-", sep) == NULL) - return -EINVAL; - - if (sep == '.') { - uint16_t shorts[3] = { 0 }; - - parse_fields(shorts); - - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); - } - } else { - struct ether_addr out = { .ether_addr_octet = { 0 } }; - - parse_fields(out.ether_addr_octet); - - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - } - - if (offset) - *offset = pos; - return 0; -} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h deleted file mode 100644 index 74e125a95f..0000000000 --- a/src/basic/ether-addr-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include -#include - -#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]); - -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); - -#define ETHER_ADDR_NULL ((const struct ether_addr){}) - -static inline bool ether_addr_is_null(const struct ether_addr *addr) { - return ether_addr_equal(addr, ÐER_ADDR_NULL); -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c deleted file mode 100644 index d488cfc59f..0000000000 --- a/src/basic/exit-status.c +++ /dev/null @@ -1,234 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "exit-status.h" -#include "macro.h" -#include "set.h" - -const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { - - /* We cast to int here, so that -Wenum doesn't complain that - * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */ - - switch ((int) status) { - - case EXIT_SUCCESS: - return "SUCCESS"; - - case EXIT_FAILURE: - return "FAILURE"; - } - - if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) { - switch ((int) status) { - - case EXIT_CHDIR: - return "CHDIR"; - - case EXIT_NICE: - return "NICE"; - - case EXIT_FDS: - return "FDS"; - - case EXIT_EXEC: - return "EXEC"; - - case EXIT_MEMORY: - return "MEMORY"; - - case EXIT_LIMITS: - return "LIMITS"; - - case EXIT_OOM_ADJUST: - return "OOM_ADJUST"; - - case EXIT_SIGNAL_MASK: - return "SIGNAL_MASK"; - - case EXIT_STDIN: - return "STDIN"; - - case EXIT_STDOUT: - return "STDOUT"; - - case EXIT_CHROOT: - return "CHROOT"; - - case EXIT_IOPRIO: - return "IOPRIO"; - - case EXIT_TIMERSLACK: - return "TIMERSLACK"; - - case EXIT_SECUREBITS: - return "SECUREBITS"; - - case EXIT_SETSCHEDULER: - return "SETSCHEDULER"; - - case EXIT_CPUAFFINITY: - return "CPUAFFINITY"; - - case EXIT_GROUP: - return "GROUP"; - - case EXIT_USER: - return "USER"; - - case EXIT_CAPABILITIES: - return "CAPABILITIES"; - - case EXIT_CGROUP: - return "CGROUP"; - - case EXIT_SETSID: - return "SETSID"; - - case EXIT_CONFIRM: - return "CONFIRM"; - - case EXIT_STDERR: - return "STDERR"; - - case EXIT_PAM: - return "PAM"; - - case EXIT_NETWORK: - return "NETWORK"; - - case EXIT_NAMESPACE: - return "NAMESPACE"; - - case EXIT_NO_NEW_PRIVILEGES: - return "NO_NEW_PRIVILEGES"; - - case EXIT_SECCOMP: - return "SECCOMP"; - - case EXIT_SELINUX_CONTEXT: - return "SELINUX_CONTEXT"; - - case EXIT_PERSONALITY: - return "PERSONALITY"; - - case EXIT_APPARMOR_PROFILE: - return "APPARMOR"; - - case EXIT_ADDRESS_FAMILIES: - return "ADDRESS_FAMILIES"; - - case EXIT_RUNTIME_DIRECTORY: - return "RUNTIME_DIRECTORY"; - - case EXIT_CHOWN: - return "CHOWN"; - - case EXIT_MAKE_STARTER: - return "MAKE_STARTER"; - - case EXIT_SMACK_PROCESS_LABEL: - return "SMACK_PROCESS_LABEL"; - } - } - - if (level == EXIT_STATUS_LSB) { - switch ((int) status) { - - case EXIT_INVALIDARGUMENT: - return "INVALIDARGUMENT"; - - case EXIT_NOTIMPLEMENTED: - return "NOTIMPLEMENTED"; - - case EXIT_NOPERMISSION: - return "NOPERMISSION"; - - case EXIT_NOTINSTALLED: - return "NOTINSTALLED"; - - case EXIT_NOTCONFIGURED: - return "NOTCONFIGURED"; - - case EXIT_NOTRUNNING: - return "NOTRUNNING"; - } - } - - return NULL; -} - - -bool is_clean_exit(int code, int status, ExitStatusSet *success_status) { - - if (code == CLD_EXITED) - return status == 0 || - (success_status && - set_contains(success_status->status, INT_TO_PTR(status))); - - /* If a daemon does not implement handlers for some of the - * signals that's not considered an unclean shutdown */ - if (code == CLD_KILLED) - return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) || - (success_status && - set_contains(success_status->signal, INT_TO_PTR(status))); - - return false; -} - -bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) { - - if (is_clean_exit(code, status, success_status)) - return true; - - return - code == CLD_EXITED && - IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED); -} - -void exit_status_set_free(ExitStatusSet *x) { - assert(x); - - x->status = set_free(x->status); - x->signal = set_free(x->signal); -} - -bool exit_status_set_is_empty(ExitStatusSet *x) { - if (!x) - return true; - - return set_isempty(x->status) && set_isempty(x->signal); -} - -bool exit_status_set_test(ExitStatusSet *x, int code, int status) { - - if (exit_status_set_is_empty(x)) - return false; - - if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status))) - return true; - - if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status))) - return true; - - return false; -} diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h deleted file mode 100644 index 2309f68815..0000000000 --- a/src/basic/exit-status.h +++ /dev/null @@ -1,106 +0,0 @@ -#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 . -***/ - -#include - -#include "hashmap.h" -#include "macro.h" -#include "set.h" - -/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB - * 'status' verb exit codes which are defined very differently. For details see: - * - * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html - */ - -typedef enum ExitStatus { - /* EXIT_SUCCESS defined by libc */ - /* EXIT_FAILURE defined by libc */ - EXIT_INVALIDARGUMENT = 2, - EXIT_NOTIMPLEMENTED = 3, - EXIT_NOPERMISSION = 4, - EXIT_NOTINSTALLED = 5, - EXIT_NOTCONFIGURED = 6, - EXIT_NOTRUNNING = 7, - - /* The LSB suggests that error codes >= 200 are "reserved". We - * use them here under the assumption that they hence are - * unused by init scripts. */ - - EXIT_CHDIR = 200, - EXIT_NICE, - EXIT_FDS, - EXIT_EXEC, - EXIT_MEMORY, - EXIT_LIMITS, - EXIT_OOM_ADJUST, - EXIT_SIGNAL_MASK, - EXIT_STDIN, - EXIT_STDOUT, - EXIT_CHROOT, /* 210 */ - EXIT_IOPRIO, - EXIT_TIMERSLACK, - EXIT_SECUREBITS, - EXIT_SETSCHEDULER, - EXIT_CPUAFFINITY, - EXIT_GROUP, - EXIT_USER, - EXIT_CAPABILITIES, - EXIT_CGROUP, - EXIT_SETSID, /* 220 */ - EXIT_CONFIRM, - EXIT_STDERR, - _EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */ - EXIT_PAM, - EXIT_NETWORK, - EXIT_NAMESPACE, - EXIT_NO_NEW_PRIVILEGES, - EXIT_SECCOMP, - EXIT_SELINUX_CONTEXT, - EXIT_PERSONALITY, /* 230 */ - EXIT_APPARMOR_PROFILE, - EXIT_ADDRESS_FAMILIES, - EXIT_RUNTIME_DIRECTORY, - EXIT_MAKE_STARTER, - EXIT_CHOWN, - EXIT_SMACK_PROCESS_LABEL, -} ExitStatus; - -typedef enum ExitStatusLevel { - EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */ - EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */ - EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */ - EXIT_STATUS_FULL = EXIT_STATUS_LSB -} ExitStatusLevel; - -typedef struct ExitStatusSet { - Set *status; - Set *signal; -} ExitStatusSet; - -const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_; - -bool is_clean_exit(int code, int status, ExitStatusSet *success_status); -bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status); - -void exit_status_set_free(ExitStatusSet *x); -bool exit_status_set_is_empty(ExitStatusSet *x); -bool exit_status_set_test(ExitStatusSet *x, int code, int status); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c deleted file mode 100644 index d6c1228463..0000000000 --- a/src/basic/extract-word.c +++ /dev/null @@ -1,298 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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) { - bool eight_bit = false; - char32_t u; - - 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 deleted file mode 100644 index 21db5ef33f..0000000000 --- a/src/basic/extract-word.h +++ /dev/null @@ -1,35 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index 5c820332a5..0000000000 --- a/src/basic/fd-util.c +++ /dev/null @@ -1,380 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "stdio-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; -} - -void stdio_unset_cloexec(void) { - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); -} - -_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_or_backup_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; -} - -int fd_get_path(int fd, char **ret) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int r; - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - - r = readlink_malloc(procfs_path, ret); - - if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ - return -EBADF; - - return r; -} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h deleted file mode 100644 index 34b98d4aec..0000000000 --- a/src/basic/fd-util.h +++ /dev/null @@ -1,80 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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); -void stdio_unset_cloexec(void); - -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); - -int fd_get_path(int fd, char **ret); - -/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ -#define ERRNO_IS_DISCONNECT(r) \ - IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c deleted file mode 100644 index 66dbc0fe1e..0000000000 --- a/src/basic/fileio-label.c +++ /dev/null @@ -1,68 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Harald Hoyer - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#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; - - r = mac_selinux_create_file_prepare(fn, S_IFREG); - if (r < 0) - return r; - - r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - - mac_selinux_create_file_clear(); - - return r; -} - -int write_env_file_label(const char *fname, char **l) { - int r; - - r = mac_selinux_create_file_prepare(fname, S_IFREG); - if (r < 0) - return r; - - r = write_env_file(fname, l); - - mac_selinux_create_file_clear(); - - return r; -} - -int fopen_temporary_label(const char *target, - const char *path, FILE **f, char **temp_path) { - int r; - - r = mac_selinux_create_file_prepare(target, S_IFREG); - if (r < 0) - return r; - - r = fopen_temporary(path, f, temp_path); - - mac_selinux_create_file_clear(); - - return r; -} diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h deleted file mode 100644 index fe7543013d..0000000000 --- a/src/basic/fileio-label.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Harald Hoyer - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "fileio.h" - -int write_string_file_atomic_label(const char *fn, const char *line); -int write_env_file_label(const char *fname, char **l); -int fopen_temporary_label(const char *target, - const char *path, FILE **f, char **temp_path); diff --git a/src/basic/fileio.c b/src/basic/fileio.c deleted file mode 100644 index f183de4999..0000000000 --- a/src/basic/fileio.c +++ /dev/null @@ -1,1398 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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) { - - assert(f); - assert(line); - - fputs(line, f); - if (enforce_newline && !endswith(line, "\n")) - fputc('\n', f); - - return fflush_and_check(f); -} - -static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(fn); - assert(line); - - r = fopen_temporary(fn, &f, &p); - if (r < 0) - return r; - - (void) fchmod_umask(fileno(f), 0644); - - r = write_string_stream(f, line, enforce_newline); - if (r >= 0) { - if (rename(p, fn) < 0) - r = -errno; - } - - if (r < 0) - (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); - - if (flags & WRITE_STRING_FILE_ATOMIC) { - assert(flags & WRITE_STRING_FILE_CREATE); - - 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) { - 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) { - r = -errno; - goto fail; - } - - f = fdopen(fd, "we"); - if (!f) { - r = -errno; - safe_close(fd); - goto fail; - } - } - - 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) { - _cleanup_fclose_ FILE *f = NULL; - char t[LINE_MAX], *c; - - assert(fn); - assert(line); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - if (!fgets(t, sizeof(t), f)) { - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - t[0] = 0; - } - - c = strdup(t); - if (!c) - return -ENOMEM; - truncate_nl(c); - - *line = c; - return 0; -} - -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; - - assert(fn); - assert(blob); - - l = strlen(blob); - - 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) { - size_t n, l; - _cleanup_free_ char *buf = NULL; - struct stat st; - - assert(f); - assert(contents); - - if (fstat(fileno(f), &st) < 0) - return -errno; - - n = LINE_MAX; - - if (S_ISREG(st.st_mode)) { - - /* Safety check */ - if (st.st_size > 4*1024*1024) - return -E2BIG; - - /* Start with the right file size, but be prepared for - * files from /proc which generally report a file size - * of 0 */ - if (st.st_size > 0) - n = st.st_size; - } - - l = 0; - for (;;) { - char *t; - size_t k; - - t = realloc(buf, n+1); - if (!t) - return -ENOMEM; - - buf = t; - k = fread(buf + l, 1, n - l, f); - - if (k <= 0) { - if (ferror(f)) - return -errno; - - break; - } - - l += k; - n *= 2; - - /* Safety check */ - if (n > 4*1024*1024) - return -E2BIG; - } - - buf[l] = 0; - *contents = buf; - buf = NULL; /* do not free */ - - if (size) - *size = l; - - return 0; -} - -int read_full_file(const char *fn, char **contents, size_t *size) { - _cleanup_fclose_ FILE *f = NULL; - - assert(fn); - assert(contents); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - return read_full_stream(f, contents, size); -} - -static int parse_env_file_internal( - FILE *f, - const char *fname, - const char *newline, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - _cleanup_free_ char *contents = NULL, *key = NULL; - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - char *p, *value = NULL; - int r; - unsigned line = 1; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - assert(newline); - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(newline, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(newline, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (state == PRE_VALUE || - state == VALUE || - state == VALUE_ESCAPE || - state == SINGLE_QUOTE_VALUE || - state == SINGLE_QUOTE_VALUE_ESCAPE || - state == DOUBLE_QUOTE_VALUE || - state == DOUBLE_QUOTE_VALUE_ESCAPE) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - } - - return 0; - -fail: - free(value); - return r; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); - return -EINVAL; - } - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_file( - const char *fname, - const char *newline, ...) { - - va_list ap; - int r, n_pushed = 0; - - if (!newline) - newline = NEWLINE; - - va_start(ap, newline); - r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed); - va_end(ap); - - return r < 0 ? r : n_pushed; -} - -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } - - p = strjoin(key, "=", strempty(value), NULL); - if (!p) - return -ENOMEM; - - r = strv_consume(m, p); - if (r < 0) - return r; - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs(v, f); - fputc('\n', f); - return; - } - - p++; - fwrite(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc('\\', f); - - fputc(*p, f); - } - - fputc('\"', f); - } else - fputs(p, f); - - fputc('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - -int executable_is_script(const char *path, char **interpreter) { - int r; - _cleanup_free_ char *line = NULL; - int len; - char *ans; - - assert(path); - - r = read_one_line_file(path, &line); - if (r < 0) - return r; - - if (!startswith(line, "#!")) - return 0; - - ans = strstrip(line + 2); - len = strcspn(ans, " \t"); - - if (len == 0) - return 0; - - ans = strndup(ans, len); - if (!ans) - return -ENOMEM; - - *interpreter = ans; - return 1; -} - -/** - * Retrieve one field from a file like /proc/self/status. pattern - * 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_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); - - r = read_full_file(filename, &status, NULL); - if (r < 0) - return r; - - 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++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; - } - - len = strcspn(t, terminator); - - f = strndup(t, len); - if (!f) - return -ENOMEM; - - *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 mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern, int flags) { - _cleanup_umask_ mode_t u = 0; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, flags); - if (fd < 0) - return -errno; - - 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/.#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/.#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/.#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; -} - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - - assert(s); - - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ - - if (!f) - f = stdout; - - if (space) { - if (!separator) - separator = " "; - - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } - - *space = true; - } - - return fputs(s, f); -} - -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd; - - if (!directory) - directory = "/tmp"; - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - -#ifdef O_TMPFILE - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; -#endif - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p, flags); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - assert((flags & O_EXCL) == 0); - - /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - -#ifdef O_TMPFILE - { - _cleanup_free_ char *dn = NULL; - - dn = dirname_malloc(target); - if (!dn) - return -ENOMEM; - - fd = open(dn, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); - } -#endif - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = tmp; - tmp = NULL; - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) - return -errno; - } else { - char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int read_nul_string(FILE *f, char **ret) { - _cleanup_free_ char *x = NULL; - size_t allocated = 0, n = 0; - - assert(f); - assert(ret); - - /* Reads a NUL-terminated string from the specified file. */ - - for (;;) { - int c; - - if (!GREEDY_REALLOC(x, allocated, n+2)) - return -ENOMEM; - - c = fgetc(f); - if (c == 0) /* Terminate at NUL byte */ - break; - if (c == EOF) { - if (ferror(f)) - return -errno; - break; /* Terminate at EOF */ - } - - x[n++] = (char) c; - } - - if (x) - x[n] = 0; - else { - x = new0(char, 1); - if (!x) - return -ENOMEM; - } - - *ret = x; - x = NULL; - - return 0; -} diff --git a/src/basic/fileio.h b/src/basic/fileio.h deleted file mode 100644 index 9ac497d9eb..0000000000 --- a/src/basic/fileio.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#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); -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); - -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_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); -int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); - -int write_env_file(const char *fname, char **l); - -int executable_is_script(const char *path, char **interpreter); - -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 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); - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); - -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); - -int link_tmpfile(int fd, const char *path, const char *target); - -int read_nul_string(FILE *f, char **ret); diff --git a/src/basic/formats-util.h b/src/basic/formats-util.h deleted file mode 100644 index 39a185f59b..0000000000 --- a/src/basic/formats-util.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Ronny Chevalier - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#if SIZEOF_PID_T == 4 -# define PID_PRI PRIi32 -#elif SIZEOF_PID_T == 2 -# define PID_PRI PRIi16 -#else -# error Unknown pid_t size -#endif -#define PID_FMT "%" PID_PRI - -#if SIZEOF_UID_T == 4 -# define UID_FMT "%" PRIu32 -#elif SIZEOF_UID_T == 2 -# define UID_FMT "%" PRIu16 -#else -# error Unknown uid_t size -#endif - -#if SIZEOF_GID_T == 4 -# define GID_FMT "%" PRIu32 -#elif SIZEOF_GID_T == 2 -# define GID_FMT "%" PRIu16 -#else -# error Unknown gid_t size -#endif - -#if SIZEOF_TIME_T == 8 -# define PRI_TIME PRIi64 -#elif SIZEOF_TIME_T == 4 -# define PRI_TIME "li" -#else -# error Unknown time_t size -#endif - -#if SIZEOF_RLIM_T == 8 -# define RLIM_FMT "%" PRIu64 -#elif SIZEOF_RLIM_T == 4 -# define RLIM_FMT "%" PRIu32 -#else -# error Unknown rlim_t size -#endif - -#if SIZEOF_DEV_T == 8 -# define DEV_FMT "%" PRIu64 -#elif SIZEOF_DEV_T == 4 -# define DEV_FMT "%" PRIu32 -#else -# error Unknown dev_t size -#endif - -#if SIZEOF_INO_T == 8 -# define INO_FMT "%" PRIu64 -#elif SIZEOF_INO_T == 4 -# define INO_FMT "%" PRIu32 -#else -# error Unknown ino_t size -#endif diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c deleted file mode 100644 index f0c6f3265e..0000000000 --- a/src/basic/fs-util.c +++ /dev/null @@ -1,539 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "stat-util.h" -#include "stdio-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_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 == MODE_INVALID) ? 0644 : mode); - 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; -} - -int var_tmp(char **ret) { - const char *tmp_dir = NULL; - const char *env_tmp_dir = NULL; - char *c = NULL; - int r; - - assert(ret); - - env_tmp_dir = getenv("TMPDIR"); - if (env_tmp_dir != NULL) { - r = is_dir(env_tmp_dir, true); - if (r < 0 && r != -ENOENT) - return r; - if (r > 0) - tmp_dir = env_tmp_dir; - } - - if (!tmp_dir) - tmp_dir = "/var/tmp"; - - c = strdup(tmp_dir); - if (!c) - return -ENOMEM; - *ret = c; - - return 0; -} - -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - r = inotify_add_watch(fd, path, mask); - if (r < 0) - return -errno; - - return r; -} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h deleted file mode 100644 index 075e5942b1..0000000000 --- a/src/basic/fs-util.h +++ /dev/null @@ -1,78 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#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_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); - -int var_tmp(char **ret); - -#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]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c deleted file mode 100644 index 007198c269..0000000000 --- a/src/basic/glob-util.c +++ /dev/null @@ -1,70 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 deleted file mode 100644 index 5d8fb47a26..0000000000 --- a/src/basic/glob-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include -#include - -#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 deleted file mode 100644 index 542110503f..0000000000 --- a/src/basic/gunicode.c +++ /dev/null @@ -1,112 +0,0 @@ -/* gunicode.c - Unicode manipulation functions - * - * Copyright (C) 1999, 2000 Tom Tromey - * Copyright 2000, 2005 Red Hat, Inc. - */ - -#include - -#include "gunicode.h" - -#define unichar uint32_t - -/** - * g_utf8_prev_char: - * @p: a pointer to a position within a UTF-8 encoded string - * - * Finds the previous UTF-8 character in the string before @p. - * - * @p does not have to be at the beginning of a UTF-8 character. No check - * is made to see if the character found is actually valid other than - * it starts with an appropriate byte. If @p might be the first - * character of the string, you must use g_utf8_find_prev_char() instead. - * - * Return value: a pointer to the found character. - **/ -char * -utf8_prev_char (const char *p) -{ - while (1) - { - p--; - if ((*p & 0xc0) != 0x80) - return (char *)p; - } -} - -struct Interval -{ - unichar start, end; -}; - -static int -interval_compare (const void *key, const void *elt) -{ - unichar c = (unichar) (long) (key); - struct Interval *interval = (struct Interval *)elt; - - if (c < interval->start) - return -1; - if (c > interval->end) - return +1; - - return 0; -} - -/* - * NOTE: - * - * The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are - * generated from the Unicode Character Database's file - * extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py - * in this way: - * - * ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt - * - * Last update for Unicode 6.0. - */ - -/** - * g_unichar_iswide: - * @c: a Unicode character - * - * Determines if a character is typically rendered in a double-width - * cell. - * - * Return value: %TRUE if the character is wide - **/ -bool -unichar_iswide (unichar c) -{ - /* See NOTE earlier for how to update this table. */ - static const struct Interval wide[] = { - {0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, - {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096}, - {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, - {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE}, - {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, - {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, - {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, - {0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A}, - {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, - {0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */ - {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, - }; - - if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0], - interval_compare)) - return true; - - return false; -} - -const char utf8_skip_data[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 -}; diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h deleted file mode 100644 index 5975bc8fc9..0000000000 --- a/src/basic/gunicode.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/* gunicode.h - Unicode manipulation functions - * - * Copyright (C) 1999, 2000 Tom Tromey - * Copyright 2000, 2005 Red Hat, Inc. - */ - -#include -#include -#include - -char *utf8_prev_char (const char *p); - -extern const char utf8_skip_data[256]; - -/** - * g_utf8_next_char: - * @p: Pointer to the start of a valid UTF-8 character - * - * Skips to the next character in a UTF-8 string. The string must be - * valid; this macro is as fast as possible, and has no error-checking. - * You would use this macro to iterate over a string character by - * character. The macro returns the start of the next UTF-8 character. - * Before using this macro, use g_utf8_validate() to validate strings - * that may contain invalid UTF-8. - */ -#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)]) - -bool unichar_iswide (uint32_t c); diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c deleted file mode 100644 index c3a4a011b5..0000000000 --- a/src/basic/hash-funcs.c +++ /dev/null @@ -1,81 +0,0 @@ -/*** - 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 . -***/ - -#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 deleted file mode 100644 index 299189d143..0000000000 --- a/src/basic/hash-funcs.h +++ /dev/null @@ -1,65 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index 50fefb0b54..0000000000 --- a/src/basic/hashmap.c +++ /dev/null @@ -1,1828 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "hashmap.h" -#include "macro.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 -#include "list.h" -#endif - -/* - * Implementation of hashmaps. - * Addressing: open - * - uses less RAM compared to closed addressing (chaining), because - * our entries are small (especially in Sets, which tend to contain - * the majority of entries in systemd). - * Collision resolution: Robin Hood - * - tends to equalize displacement of entries from their optimal buckets. - * Probe sequence: linear - * - though theoretically worse than random probing/uniform hashing/double - * hashing, it is good for cache locality. - * - * References: - * Celis, P. 1986. Robin Hood Hashing. - * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. - * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf - * - The results are derived for random probing. Suggests deletion with - * tombstones and two mean-centered search methods. None of that works - * well for linear probing. - * - * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. - * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. - * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 - * http://www.math.uu.se/~svante/papers/sj157.pdf - * - Applies to Robin Hood with linear probing. Contains remarks on - * the unsuitability of mean-centered search with linear probing. - * - * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. - * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. - * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 - * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes - * in a successful search), and Janson writes about displacement. C = d + 1. - * - * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. - * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - * - Explanation of backward shift deletion with pictures. - * - * Khuong, P. 2013. The Other Robin Hood Hashing. - * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ - * - Short summary of random vs. linear probing, and tombstones vs. backward shift. - */ - -/* - * XXX Ideas for improvement: - * For unordered hashmaps, randomize iteration order, similarly to Perl: - * http://blog.booking.com/hardening-perls-hash-function.html - */ - -/* INV_KEEP_FREE = 1 / (1 - max_load_factor) - * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ -#define INV_KEEP_FREE 5U - -/* Fields common to entries of all hashmap/set types */ -struct hashmap_base_entry { - const void *key; -}; - -/* Entry types for specific hashmap/set types - * hashmap_base_entry must be at the beginning of each entry struct. */ - -struct plain_hashmap_entry { - struct hashmap_base_entry b; - void *value; -}; - -struct ordered_hashmap_entry { - struct plain_hashmap_entry p; - unsigned iterate_next, iterate_previous; -}; - -struct set_entry { - struct hashmap_base_entry b; -}; - -/* In several functions it is advantageous to have the hash table extended - * virtually by a couple of additional buckets. We reserve special index values - * for these "swap" buckets. */ -#define _IDX_SWAP_BEGIN (UINT_MAX - 3) -#define IDX_PUT (_IDX_SWAP_BEGIN + 0) -#define IDX_TMP (_IDX_SWAP_BEGIN + 1) -#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - -#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ -#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ - -assert_cc(IDX_FIRST == _IDX_SWAP_END); -assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); - -/* Storage space for the "swap" buckets. - * All entry types can fit into a ordered_hashmap_entry. */ -struct swap_entries { - struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; -}; - -/* Distance from Initial Bucket */ -typedef uint8_t dib_raw_t; -#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ -#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ -#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ -#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ - -#define DIB_FREE UINT_MAX - -#ifdef ENABLE_DEBUG_HASHMAP -struct hashmap_debug_info { - LIST_FIELDS(struct hashmap_debug_info, debug_list); - unsigned max_entries; /* high watermark of n_entries */ - - /* who allocated this hashmap */ - int line; - const char *file; - const char *func; - - /* fields to detect modification while iterating */ - unsigned put_count; /* counts puts into the hashmap */ - unsigned rem_count; /* counts removals from hashmap */ - unsigned last_rem_idx; /* remembers last removal index */ -}; - -/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ -static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ - -enum HashmapType { - HASHMAP_TYPE_PLAIN, - HASHMAP_TYPE_ORDERED, - HASHMAP_TYPE_SET, - _HASHMAP_TYPE_MAX -}; - -struct _packed_ indirect_storage { - void *storage; /* where buckets and DIBs are stored */ - uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ - - unsigned n_entries; /* number of stored entries */ - unsigned n_buckets; /* number of buckets */ - - unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops - O(n) instead of O(n^2) for unordered hashmaps. */ - uint8_t _pad[3]; /* padding for the whole HashmapBase */ - /* The bitfields in HashmapBase complete the alignment of the whole thing. */ -}; - -struct direct_storage { - /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. - * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, - * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - uint8_t storage[sizeof(struct indirect_storage)]; -}; - -#define DIRECT_BUCKETS(entry_t) \ - (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) - -/* We should be able to store at least one entry directly. */ -assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); - -/* We have 3 bits for n_direct_entries. */ -assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); - -/* Hashmaps with directly stored entries all use this shared hash key. - * It's no big deal if the key is guessed, because there can be only - * a handful of directly stored entries in a hashmap. When a hashmap - * outgrows direct storage, it gets its own key for indirect storage. */ -static uint8_t shared_hash_key[HASH_KEY_SIZE]; -static bool shared_hash_key_initialized; - -/* Fields that all hashmap/set types must have */ -struct HashmapBase { - const struct hash_ops *hash_ops; /* hash and compare ops to use */ - - union _packed_ { - struct indirect_storage indirect; /* if has_indirect */ - struct direct_storage direct; /* if !has_indirect */ - }; - - enum HashmapType type:2; /* HASHMAP_TYPE_* */ - bool has_indirect:1; /* whether indirect storage is used */ - unsigned n_direct_entries:3; /* Number of entries in direct storage. - * Only valid if !has_indirect. */ - bool from_pool:1; /* whether was allocated from mempool */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ -}; - -/* Specific hash types - * HashmapBase must be at the beginning of each hashmap struct. */ - -struct Hashmap { - struct HashmapBase b; -}; - -struct OrderedHashmap { - struct HashmapBase b; - unsigned iterate_list_head, iterate_list_tail; -}; - -struct Set { - struct HashmapBase b; -}; - -DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); -DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); -/* No need for a separate Set pool */ -assert_cc(sizeof(Hashmap) == sizeof(Set)); - -struct hashmap_type_info { - size_t head_size; - size_t entry_size; - struct mempool *mempool; - unsigned n_direct_buckets; -}; - -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { - [HASHMAP_TYPE_PLAIN] = { - .head_size = sizeof(Hashmap), - .entry_size = sizeof(struct plain_hashmap_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), - }, - [HASHMAP_TYPE_ORDERED] = { - .head_size = sizeof(OrderedHashmap), - .entry_size = sizeof(struct ordered_hashmap_entry), - .mempool = &ordered_hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), - }, - [HASHMAP_TYPE_SET] = { - .head_size = sizeof(Set), - .entry_size = sizeof(struct set_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), - }, -}; - -static unsigned n_buckets(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_buckets - : hashmap_type_info[h->type].n_direct_buckets; -} - -static unsigned n_entries(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_entries - : h->n_direct_entries; -} - -static void n_entries_inc(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries++; - else - h->n_direct_entries++; -} - -static void n_entries_dec(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries--; - else - h->n_direct_entries--; -} - -static void *storage_ptr(HashmapBase *h) { - return h->has_indirect ? h->indirect.storage - : h->direct.storage; -} - -static uint8_t *hash_key(HashmapBase *h) { - return h->has_indirect ? h->indirect.hash_key - : shared_hash_key; -} - -static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - 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) - -static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { - static uint8_t current[HASH_KEY_SIZE]; - static bool current_initialized = false; - - /* Returns a hash function key to use. In order to keep things - * fast we will not generate a new key each time we allocate a - * new hash table. Instead, we'll just reuse the most recently - * generated one, except if we never generated one or when we - * are rehashing an entire hash table because we reached a - * fill level */ - - if (!current_initialized || !reuse_is_ok) { - random_bytes(current, sizeof(current)); - current_initialized = true; - } - - memcpy(hash_key, current, sizeof(current)); -} - -static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); -} - -static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { - return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { - return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct set_entry *set_bucket_at(Set *h, unsigned idx) { - return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { - return &swap->e[idx - _IDX_SWAP_BEGIN]; -} - -/* Returns a pointer to the bucket at index idx. - * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, - unsigned idx) { - if (idx < _IDX_SWAP_BEGIN) - return bucket_at(h, idx); - - if (idx < _IDX_SWAP_END) - return &bucket_at_swap(swap, idx)->p.b; - - assert_not_reached("Invalid index"); -} - -static dib_raw_t *dib_raw_ptr(HashmapBase *h) { - return (dib_raw_t*) - ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); -} - -static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { - return idx >= from ? idx - from - : n_buckets(h) + idx - from; -} - -static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { - unsigned initial_bucket; - - if (raw_dib == DIB_RAW_FREE) - return DIB_FREE; - - if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) - return raw_dib; - - /* - * Having an overflow DIB value is very unlikely. The hash function - * would have to be bad. For example, in a table of size 2^24 filled - * to load factor 0.9 the maximum observed DIB is only about 60. - * In theory (assuming I used Maxima correctly), for an infinite size - * hash table with load factor 0.8 the probability of a given entry - * having DIB > 40 is 1.9e-8. - * This returns the correct DIB value by recomputing the hash value in - * the unlikely case. XXX Hitting this case could be a hint to rehash. - */ - initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); - return bucket_distance(h, idx, initial_bucket); -} - -static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { - dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; -} - -static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { - dib_raw_t *dibs; - - dibs = dib_raw_ptr(h); - - for ( ; idx < n_buckets(h); idx++) - if (dibs[idx] != DIB_RAW_FREE) - return idx; - - return IDX_NIL; -} - -static void bucket_mark_free(HashmapBase *h, unsigned idx) { - memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); - bucket_set_dib(h, idx, DIB_FREE); -} - -static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, - unsigned from, unsigned to) { - struct hashmap_base_entry *e_from, *e_to; - - assert(from != to); - - e_from = bucket_at_virtual(h, swap, from); - e_to = bucket_at_virtual(h, swap, to); - - memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le, *le_to; - - le_to = (struct ordered_hashmap_entry*) e_to; - - if (le_to->iterate_next != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_next); - le->iterate_previous = to; - } - - if (le_to->iterate_previous != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_previous); - le->iterate_next = to; - } - - if (lh->iterate_list_head == from) - lh->iterate_list_head = to; - if (lh->iterate_list_tail == from) - lh->iterate_list_tail = to; - } -} - -static unsigned next_idx(HashmapBase *h, unsigned idx) { - return (idx + 1U) % n_buckets(h); -} - -static unsigned prev_idx(HashmapBase *h, unsigned idx) { - return (n_buckets(h) + idx - 1U) % n_buckets(h); -} - -static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { - switch (h->type) { - - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - return ((struct plain_hashmap_entry*)e)->value; - - case HASHMAP_TYPE_SET: - return (void*) e->key; - - default: - assert_not_reached("Unknown hashmap type"); - } -} - -static void base_remove_entry(HashmapBase *h, unsigned idx) { - unsigned left, right, prev, dib; - dib_raw_t raw_dib, *dibs; - - dibs = dib_raw_ptr(h); - assert(dibs[idx] != DIB_RAW_FREE); - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.rem_count++; - h->debug.last_rem_idx = idx; -#endif - - left = idx; - /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ - for (right = next_idx(h, left); ; right = next_idx(h, right)) { - raw_dib = dibs[right]; - if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) - break; - - /* The buckets are not supposed to be all occupied and with DIB > 0. - * That would mean we could make everyone better off by shifting them - * backward. This scenario is impossible. */ - assert(left != right); - } - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); - - if (le->iterate_next != IDX_NIL) - ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; - else - lh->iterate_list_tail = le->iterate_previous; - - if (le->iterate_previous != IDX_NIL) - ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; - else - lh->iterate_list_head = le->iterate_next; - } - - /* Now shift all buckets in the interval (left, right) one step backwards */ - for (prev = left, left = next_idx(h, left); left != right; - prev = left, left = next_idx(h, left)) { - dib = bucket_calculate_dib(h, left, dibs[left]); - assert(dib != 0); - bucket_move_entry(h, NULL, left, prev); - bucket_set_dib(h, prev, dib - 1); - } - - bucket_mark_free(h, prev); - n_entries_dec(h); -} -#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) - -static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { - struct ordered_hashmap_entry *e; - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - idx = h->iterate_list_head; - e = ordered_bucket_at(h, idx); - } else { - idx = i->idx; - e = ordered_bucket_at(h, idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->p.b.key != i->next_key) { - idx = prev_idx(HASHMAP_BASE(h), idx); - e = ordered_bucket_at(h, idx); - } - assert(e->p.b.key == i->next_key); - } - -#ifdef ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - if (e->iterate_next != IDX_NIL) { - struct ordered_hashmap_entry *n; - i->idx = e->iterate_next; - n = ordered_bucket_at(h, i->idx); - i->next_key = n->p.b.key; - } else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - /* fast forward to the first occupied bucket */ - if (h->has_indirect) { - i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); - h->indirect.idx_lowest_entry = i->idx; - } else - i->idx = skip_free_buckets(h, 0); - - if (i->idx == IDX_NIL) - goto at_end; - } else { - struct hashmap_base_entry *e; - - assert(i->idx > 0); - - e = bucket_at(h, i->idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->key != i->next_key) - e = bucket_at(h, --i->idx); - - assert(e->key == i->next_key); - } - - idx = i->idx; -#ifdef ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - i->idx = skip_free_buckets(h, i->idx + 1); - if (i->idx != IDX_NIL) - i->next_key = bucket_at(h, i->idx)->key; - else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { - if (!h) { - i->idx = IDX_NIL; - return IDX_NIL; - } - -#ifdef ENABLE_DEBUG_HASHMAP - if (i->idx == IDX_FIRST) { - i->put_count = h->debug.put_count; - i->rem_count = h->debug.rem_count; - } else { - /* While iterating, must not add any new entries */ - assert(i->put_count == h->debug.put_count); - /* ... or remove entries other than the current one */ - assert(i->rem_count == h->debug.rem_count || - (i->rem_count == h->debug.rem_count - 1 && - i->prev_idx == h->debug.last_rem_idx)); - /* Reset our removals counter */ - i->rem_count = h->debug.rem_count; - } -#endif - - return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) - : hashmap_iterate_in_internal_order(h, i); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = hashmap_iterate_entry(h, i); - if (idx == IDX_NIL) { - if (value) - *value = NULL; - if (key) - *key = NULL; - - return false; - } - - e = bucket_at(h, idx); - data = entry_value(h, e); - if (value) - *value = data; - if (key) - *key = e->key; - - return true; -} - -bool set_iterate(Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); -} - -#define HASHMAP_FOREACH_IDX(idx, h, i) \ - for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ - (idx != IDX_NIL); \ - (idx) = hashmap_iterate_entry((h), &(i))) - -static void reset_direct_storage(HashmapBase *h) { - const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; - void *p; - - assert(!h->has_indirect); - - p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); - memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); -} - -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *h; - const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool use_pool; - - use_pool = is_main_thread(); - - h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); - - if (!h) - return NULL; - - h->type = type; - h->from_pool = use_pool; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; - - if (type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*)h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - reset_direct_storage(h); - - if (!shared_hash_key_initialized) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); - shared_hash_key_initialized= true; - } - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.func = func; - h->debug.file = file; - h->debug.line = line; - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - return h; -} - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *q; - - assert(h); - - if (*h) - return 0; - - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); - if (!q) - return -ENOMEM; - - *h = q; - return 0; -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static void hashmap_free_no_clear(HashmapBase *h) { - assert(!h->has_indirect); - assert(!h->n_direct_entries); - -#ifdef ENABLE_DEBUG_HASHMAP - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - if (h->from_pool) - mempool_free_tile(hashmap_type_info[h->type].mempool, h); - else - free(h); -} - -HashmapBase *internal_hashmap_free(HashmapBase *h) { - - /* Free the hashmap, but nothing in it */ - - if (h) { - internal_hashmap_clear(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h) { - - /* Free the hashmap and all data objects in it, but not the - * keys */ - - if (h) { - internal_hashmap_clear_free(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -Hashmap *hashmap_free_free_free(Hashmap *h) { - - /* Free the hashmap and all data and key objects in it */ - - if (h) { - hashmap_clear_free_free(h); - hashmap_free_no_clear(HASHMAP_BASE(h)); - } - - return NULL; -} - -void internal_hashmap_clear(HashmapBase *h) { - if (!h) - return; - - if (h->has_indirect) { - free(h->indirect.storage); - h->has_indirect = false; - } - - h->n_direct_entries = 0; - reset_direct_storage(h); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } -} - -void internal_hashmap_clear_free(HashmapBase *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; - idx = skip_free_buckets(h, idx + 1)) - free(entry_value(h, bucket_at(h, idx))); - - internal_hashmap_clear(h); -} - -void hashmap_clear_free_free(Hashmap *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; - idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { - struct plain_hashmap_entry *e = plain_bucket_at(h, idx); - free((void*)e->b.key); - free(e->value); - } - - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -static int resize_buckets(HashmapBase *h, unsigned entries_add); - -/* - * Finds an empty bucket to put an entry into, starting the scan at 'idx'. - * Performs Robin Hood swaps as it goes. The entry to put must be placed - * by the caller into swap slot IDX_PUT. - * If used for in-place resizing, may leave a displaced entry in swap slot - * IDX_PUT. Caller must rehash it next. - * Returns: true if it left a displaced entry to rehash next in IDX_PUT, - * false otherwise. - */ -static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, - struct swap_entries *swap) { - dib_raw_t raw_dib, *dibs; - unsigned dib, distance; - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.put_count++; -#endif - - dibs = dib_raw_ptr(h); - - for (distance = 0; ; distance++) { - raw_dib = dibs[idx]; - if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { - if (raw_dib == DIB_RAW_REHASH) - bucket_move_entry(h, swap, idx, IDX_TMP); - - if (h->has_indirect && h->indirect.idx_lowest_entry > idx) - h->indirect.idx_lowest_entry = idx; - - bucket_set_dib(h, idx, distance); - bucket_move_entry(h, swap, IDX_PUT, idx); - if (raw_dib == DIB_RAW_REHASH) { - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - return true; - } - - return false; - } - - dib = bucket_calculate_dib(h, idx, raw_dib); - - if (dib < distance) { - /* Found a wealthier entry. Go Robin Hood! */ - bucket_set_dib(h, idx, distance); - - /* swap the entries */ - bucket_move_entry(h, swap, idx, IDX_TMP); - bucket_move_entry(h, swap, IDX_PUT, idx); - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - - distance = dib; - } - - idx = next_idx(h, idx); - } -} - -/* - * Puts an entry into a hashmap, boldly - no check whether key already exists. - * The caller must place the entry (only its key and value, not link indexes) - * in swap slot IDX_PUT. - * Caller must ensure: the key does not exist yet in the hashmap. - * that resize is not needed if !may_resize. - * Returns: 1 if entry was put successfully. - * -ENOMEM if may_resize==true and resize failed with -ENOMEM. - * Cannot return -ENOMEM if !may_resize. - */ -static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, - struct swap_entries *swap, bool may_resize) { - struct ordered_hashmap_entry *new_entry; - int r; - - assert(idx < n_buckets(h)); - - new_entry = bucket_at_swap(swap, IDX_PUT); - - if (may_resize) { - r = resize_buckets(h, 1); - if (r < 0) - return r; - if (r > 0) - idx = bucket_hash(h, new_entry->p.b.key); - } - assert(n_entries(h) < n_buckets(h)); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - - new_entry->iterate_next = IDX_NIL; - new_entry->iterate_previous = lh->iterate_list_tail; - - if (lh->iterate_list_tail != IDX_NIL) { - struct ordered_hashmap_entry *old_tail; - - old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); - assert(old_tail->iterate_next == IDX_NIL); - old_tail->iterate_next = IDX_PUT; - } - - lh->iterate_list_tail = IDX_PUT; - if (lh->iterate_list_head == IDX_NIL) - lh->iterate_list_head = IDX_PUT; - } - - assert_se(hashmap_put_robin_hood(h, idx, swap) == false); - - n_entries_inc(h); -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); -#endif - - return 1; -} -#define hashmap_put_boldly(h, idx, swap, may_resize) \ - hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) - -/* - * Returns 0 if resize is not needed. - * 1 if successfully resized. - * -ENOMEM on allocation failure. - */ -static int resize_buckets(HashmapBase *h, unsigned entries_add) { - struct swap_entries swap; - void *new_storage; - dib_raw_t *old_dibs, *new_dibs; - const struct hashmap_type_info *hi; - unsigned idx, optimal_idx; - unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; - uint8_t new_shift; - bool rehash_next; - - assert(h); - - hi = &hashmap_type_info[h->type]; - new_n_entries = n_entries(h) + entries_add; - - /* overflow? */ - if (_unlikely_(new_n_entries < entries_add)) - return -ENOMEM; - - /* For direct storage we allow 100% load, because it's tiny. */ - if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) - return 0; - - /* - * Load factor = n/m = 1 - (1/INV_KEEP_FREE). - * From it follows: m = n + n/(INV_KEEP_FREE - 1) - */ - new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); - /* overflow? */ - if (_unlikely_(new_n_buckets < new_n_entries)) - return -ENOMEM; - - if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) - return -ENOMEM; - - old_n_buckets = n_buckets(h); - - if (_likely_(new_n_buckets <= old_n_buckets)) - return 0; - - new_shift = log2u_round_up(MAX( - new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), - 2 * sizeof(struct direct_storage))); - - /* Realloc storage (buckets and DIB array). */ - new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, - 1U << new_shift); - if (!new_storage) - return -ENOMEM; - - /* Must upgrade direct to indirect storage. */ - if (!h->has_indirect) { - memcpy(new_storage, h->direct.storage, - old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); - h->indirect.n_entries = h->n_direct_entries; - h->indirect.idx_lowest_entry = 0; - h->n_direct_entries = 0; - } - - /* Get a new hash key. If we've just upgraded to indirect storage, - * allow reusing a previously generated key. It's still a different key - * from the shared one that we used for direct storage. */ - get_hash_key(h->indirect.hash_key, !h->has_indirect); - - h->has_indirect = true; - h->indirect.storage = new_storage; - h->indirect.n_buckets = (1U << new_shift) / - (hi->entry_size + sizeof(dib_raw_t)); - - old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); - new_dibs = dib_raw_ptr(h); - - /* - * Move the DIB array to the new place, replacing valid DIB values with - * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. - * Note: Overlap is not possible, because we have at least doubled the - * number of buckets and dib_raw_t is smaller than any entry type. - */ - for (idx = 0; idx < old_n_buckets; idx++) { - assert(old_dibs[idx] != DIB_RAW_REHASH); - new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE - : DIB_RAW_REHASH; - } - - /* Zero the area of newly added entries (including the old DIB area) */ - memzero(bucket_at(h, old_n_buckets), - (n_buckets(h) - old_n_buckets) * hi->entry_size); - - /* The upper half of the new DIB array needs initialization */ - memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, - (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - - /* Rehash entries that need it */ - n_rehashed = 0; - for (idx = 0; idx < old_n_buckets; idx++) { - if (new_dibs[idx] != DIB_RAW_REHASH) - continue; - - optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); - - /* - * Not much to do if by luck the entry hashes to its current - * location. Just set its DIB. - */ - if (optimal_idx == idx) { - new_dibs[idx] = 0; - n_rehashed++; - continue; - } - - new_dibs[idx] = DIB_RAW_FREE; - bucket_move_entry(h, &swap, idx, IDX_PUT); - /* bucket_move_entry does not clear the source */ - memzero(bucket_at(h, idx), hi->entry_size); - - do { - /* - * Find the new bucket for the current entry. This may make - * another entry homeless and load it into IDX_PUT. - */ - rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); - n_rehashed++; - - /* Did the current entry displace another one? */ - if (rehash_next) - optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); - } while (rehash_next); - } - - assert(n_rehashed == n_entries(h)); - - return 1; -} - -/* - * Finds an entry with a matching key - * Returns: index of the found entry, or IDX_NIL if not found. - */ -static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { - struct hashmap_base_entry *e; - unsigned dib, distance; - dib_raw_t *dibs = dib_raw_ptr(h); - - assert(idx < n_buckets(h)); - - for (distance = 0; ; distance++) { - if (dibs[idx] == DIB_RAW_FREE) - return IDX_NIL; - - dib = bucket_calculate_dib(h, idx, dibs[idx]); - - if (dib < distance) - return IDX_NIL; - if (dib == distance) { - e = bucket_at(h, idx); - if (h->hash_ops->compare(e->key, key) == 0) - return idx; - } - - idx = next_idx(h, idx); - } -} -#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) - -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); - if (e->value == value) - return 0; - return -EEXIST; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int set_put(Set *s, const void *key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned hash, idx; - - assert(s); - - hash = bucket_hash(s, key); - idx = bucket_scan(s, hash, key); - if (idx != IDX_NIL) - return 0; - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = key; - return hashmap_put_boldly(s, hash, &swap, true); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); -#ifdef ENABLE_DEBUG_HASHMAP - /* Although the key is equal, the key pointer may have changed, - * and this would break our assumption for iterating. So count - * this operation as incompatible with iteration. */ - if (e->b.key != key) { - h->b.debug.put_count++; - h->b.debug.rem_count++; - h->b.debug.last_rem_idx = idx; - } -#endif - e->b.key = key; - e->value = value; - return 0; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int hashmap_update(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = plain_bucket_at(h, idx); - e->value = value; - return 0; -} - -void *internal_hashmap_get(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return entry_value(h, e); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **key2) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (key2) - *key2 = (void*) e->b.key; - - return e->value; -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key) { - unsigned hash; - - if (!h) - return false; - - hash = bucket_hash(h, key); - return bucket_scan(h, hash, key) != IDX_NIL; -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - void *data; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - void *data; - - if (!h) { - if (rkey) - *rkey = NULL; - return NULL; - } - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) { - if (rkey) - *rkey = NULL; - return NULL; - } - - e = plain_bucket_at(h, idx); - data = e->value; - if (rkey) - *rkey = (void*) e->b.key; - - remove_entry(h, idx); - - return data; -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx = bucket_scan(h, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(h, new_key); - if (bucket_scan(h, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(h, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned old_hash, new_hash, idx; - - if (!s) - return -ENOENT; - - old_hash = bucket_hash(s, old_key); - idx = bucket_scan(s, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(s, new_key); - if (bucket_scan(s, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(s, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = new_key; - assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); - - return 0; -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx_old, idx_new; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx_old = bucket_scan(h, old_hash, old_key); - if (idx_old == IDX_NIL) - return -ENOENT; - - old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - - new_hash = bucket_hash(h, new_key); - idx_new = bucket_scan(h, new_hash, new_key); - if (idx_new != IDX_NIL) - if (idx_old != idx_new) { - remove_entry(h, idx_new); - /* Compensate for a possible backward shift. */ - if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) - idx_old = prev_idx(HASHMAP_BASE(h), idx_old); - assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); - } - - remove_entry(h, idx_old); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (e->value != value) - return NULL; - - remove_entry(h, idx); - - return value; -} - -static unsigned find_first_entry(HashmapBase *h) { - Iterator i = ITERATOR_FIRST; - - if (!h || !n_entries(h)) - return IDX_NIL; - - return hashmap_iterate_entry(h, &i); -} - -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); - - return key; -} - -unsigned internal_hashmap_size(HashmapBase *h) { - - if (!h) - return 0; - - return n_entries(h); -} - -unsigned internal_hashmap_buckets(HashmapBase *h) { - - if (!h) - return 0; - - return n_buckets(h); -} - -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { - Iterator i; - unsigned idx; - - assert(h); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); - int r; - - r = hashmap_put(h, pe->b.key, pe->value); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int set_merge(Set *s, Set *other) { - Iterator i; - unsigned idx; - - assert(s); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct set_entry *se = set_bucket_at(other, idx); - int r; - - r = set_put(s, se->b.key); - if (r < 0) - return r; - } - - return 0; -} - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { - int r; - - assert(h); - - r = resize_buckets(h, entries_add); - if (r < 0) - return r; - - return 0; -} - -/* - * The same as hashmap_merge(), but every new item from other is moved to h. - * Keys already in h are skipped and stay in other. - * Returns: 0 on success. - * -ENOMEM on alloc failure, in which case no move has been done. - */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { - struct swap_entries swap; - struct hashmap_base_entry *e, *n; - Iterator i; - unsigned idx; - int r; - - assert(h); - - if (!other) - return 0; - - assert(other->type == h->type); - - /* - * This reserves buckets for the worst case, where none of other's - * entries are yet present in h. This is preferable to risking - * an allocation failure in the middle of the moving and having to - * rollback or return a partial result. - */ - r = resize_buckets(h, n_entries(other)); - if (r < 0) - return r; - - HASHMAP_FOREACH_IDX(idx, other, i) { - unsigned h_hash; - - e = bucket_at(other, idx); - h_hash = bucket_hash(h, e->key); - if (bucket_scan(h, h_hash, e->key) != IDX_NIL) - continue; - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); - - remove_entry(other, idx); - } - - return 0; -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { - struct swap_entries swap; - unsigned h_hash, other_hash, idx; - struct hashmap_base_entry *e, *n; - int r; - - assert(h); - - h_hash = bucket_hash(h, key); - if (bucket_scan(h, h_hash, key) != IDX_NIL) - return -EEXIST; - - if (!other) - return -ENOENT; - - assert(other->type == h->type); - - other_hash = bucket_hash(other, key); - idx = bucket_scan(other, other_hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = bucket_at(other, idx); - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - r = hashmap_put_boldly(h, h_hash, &swap, true); - if (r < 0) - return r; - - remove_entry(other, idx); - return 0; -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h) { - HashmapBase *copy; - int r; - - assert(h); - - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); - if (!copy) - return NULL; - - switch (h->type) { - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); - break; - case HASHMAP_TYPE_SET: - r = set_merge((Set*)copy, (Set*)h); - break; - default: - assert_not_reached("Unknown hashmap type"); - } - - if (r < 0) { - internal_hashmap_free(copy); - return NULL; - } - - return copy; -} - -char **internal_hashmap_get_strv(HashmapBase *h) { - char **sv; - Iterator i; - unsigned idx, n; - - sv = new(char*, n_entries(h)+1); - if (!sv) - return NULL; - - n = 0; - HASHMAP_FOREACH_IDX(idx, h, i) - sv[n++] = entry_value(h, bucket_at(h, idx)); - sv[n] = NULL; - - return sv; -} - -void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - struct ordered_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = ordered_bucket_at(h, idx); - if (e->iterate_next == IDX_NIL) - return NULL; - return ordered_bucket_at(h, e->iterate_next)->p.value; -} - -int set_consume(Set *s, void *value) { - int r; - - assert(s); - assert(value); - - r = set_put(s, value); - if (r <= 0) - free(value); - - return r; -} - -int set_put_strdup(Set *s, const char *p) { - char *c; - - assert(s); - assert(p); - - if (set_contains(s, (char*) p)) - return 0; - - c = strdup(p); - if (!c) - return -ENOMEM; - - return set_consume(s, c); -} - -int set_put_strdupv(Set *s, char **l) { - int n = 0, r; - char **i; - - assert(s); - - STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; - int r; - - assert(s); - assert(v); - - for (;;) { - char *word; - - r = extract_first_word(&p, &word, separators, flags); - if (r <= 0) - return r; - - r = set_consume(s, word); - if (r < 0) - return r; - } -} diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h deleted file mode 100644 index 6d1ae48b21..0000000000 --- a/src/basic/hashmap.h +++ /dev/null @@ -1,372 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -/* - * A hash table implementation. As a minor optimization a NULL hashmap object - * will be treated as empty hashmap for all read operations. That way it is not - * necessary to instantiate an object for each Hashmap use. - * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), - * the implemention will: - * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) - * - perform extra checks for invalid use of iterators - */ - -#define HASH_KEY_SIZE 16 - -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ -typedef struct HashmapBase HashmapBase; - -/* Specific hashmap/set types */ -typedef struct Hashmap Hashmap; /* Maps keys to values */ -typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ -typedef struct Set Set; /* Stores just keys */ - -/* Ideally the Iterator would be an opaque struct, but it is instantiated - * by hashmap users, so the definition has to be here. Do not use its fields - * directly. */ -typedef struct { - unsigned idx; /* index of an entry to be iterated next */ - const void *next_key; /* expected value of that entry's key pointer */ -#ifdef ENABLE_DEBUG_HASHMAP - unsigned put_count; /* hashmap's put_count recorded at start of iteration */ - unsigned rem_count; /* hashmap's rem_count in previous iteration */ - unsigned prev_idx; /* idx in previous iteration */ -#endif -} Iterator; - -#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) -#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) - -/* Macros for type checking */ -#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ - (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ - __builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ - __builtin_types_compatible_p(typeof(h), Set*)) - -#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ - (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ - -#define HASHMAP_BASE(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ - (HashmapBase*)(h), \ - (void)0) - -#define PLAIN_HASHMAP(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ - (Hashmap*)(h), \ - (void)0) - -#ifdef ENABLE_DEBUG_HASHMAP -# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line -# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ -# define HASHMAP_DEBUG_PASS_ARGS , func, file, line -#else -# define HASHMAP_DEBUG_PARAMS -# define HASHMAP_DEBUG_SRC_ARGS -# define HASHMAP_DEBUG_PASS_ARGS -#endif - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) - -HashmapBase *internal_hashmap_free(HashmapBase *h); -static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h); -static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} - -Hashmap *hashmap_free_free_free(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int hashmap_put(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_update(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace(PLAIN_HASHMAP(h), key, value); -} - -void *internal_hashmap_get(HashmapBase *h, const void *key); -static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key); -static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} -static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key); -static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value); -static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa - * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) -#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); -static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} -static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); -/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ -static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} -static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); -static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} -static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} - -unsigned internal_hashmap_size(HashmapBase *h) _pure_; -static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} - -static inline bool hashmap_isempty(Hashmap *h) { - return hashmap_size(h) == 0; -} -static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return ordered_hashmap_size(h) == 0; -} - -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; -static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); -static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} -static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} - -void internal_hashmap_clear(HashmapBase *h); -static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -void internal_hashmap_clear_free(HashmapBase *h); -static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} - -void hashmap_clear_free_free(Hashmap *h); -static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free(PLAIN_HASHMAP(h)); -} - -/* - * Note about all *_first*() functions - * - * For plain Hashmaps and Sets the order of entries is undefined. - * The functions find whatever entry is first in the implementation - * internal order. - * - * Only for OrderedHashmaps the order is well defined and finding - * the first entry is O(1). - */ - -void *internal_hashmap_steal_first(HashmapBase *h); -static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} - -void *internal_hashmap_steal_first_key(HashmapBase *h); -static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; -static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} - -/* no hashmap_next */ -void *ordered_hashmap_next(OrderedHashmap *h, const void *key); - -char **internal_hashmap_get_strv(HashmapBase *h); -static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} -static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} - -/* - * Hashmaps are iterated in unpredictable order. - * OrderedHashmaps are an exception to this. They are iterated in the order - * the entries were inserted. - * It is safe to remove the current entry. - */ -#define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define ORDERED_HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); - -#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) -#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) -#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) -#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) -#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) -#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c deleted file mode 100644 index c5bda6c4d6..0000000000 --- a/src/basic/hexdecoct.c +++ /dev/null @@ -1,754 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "util.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; -} - -ssize_t base64mem(const void *p, size_t l, char **out) { - 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 -ENOMEM; - - 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; - *out = r; - return z - r; -} - -static int base64_append_width(char **prefix, int plen, - const char *sep, int indent, - const void *p, size_t l, - int width) { - - _cleanup_free_ char *x = NULL; - char *t, *s; - ssize_t slen, len, avail; - int line, lines; - - len = base64mem(p, l, &x); - if (len <= 0) - return len; - - lines = (len + width - 1) / width; - - slen = sep ? strlen(sep) : 0; - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); - if (!t) - return -ENOMEM; - - memcpy_safe(t + plen, sep, slen); - - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { - int act = MIN(width, avail); - - if (line > 0 || sep) { - memset(s, ' ', indent); - s += indent; - } - - memcpy(s, x + width * line, act); - s += act; - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; - } - assert(avail == 0); - - *prefix = t; - return 0; -} - -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { - if (plen > width / 2 || plen + indent > width) - /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); - else - /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); -}; - - -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 deleted file mode 100644 index 1ba2f69ebd..0000000000 --- a/src/basic/hexdecoct.h +++ /dev/null @@ -1,56 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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); - -ssize_t base64mem(const void *p, size_t l, char **out); -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); -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 deleted file mode 100644 index 13c3bb6446..0000000000 --- a/src/basic/hostname-util.c +++ /dev/null @@ -1,252 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include - -#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; - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return false; - - /* This is the built-in kernel default host name */ - if (streq(u.nodename, "(none)")) - return false; - - return true; -} - -char* gethostname_malloc(void) { - struct utsname u; - - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(u.sysname); - - return strdup(u.nodename); -} - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) - return -ENOMEM; - - *ret = k; - return 0; -} - -static bool hostname_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '-' || - c == '_' || - c == '.'; -} - -/** - * Check if s looks like a valid host name or FQDN. This does not do - * full DNS validation, but only checks if the name is composed of - * allowed characters and the length is not above the maximum allowed - * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * 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_name_is_valid(). - */ -bool hostname_is_valid(const char *s, bool allow_trailing_dot) { - unsigned n_dots = 0; - const char *p; - bool dot; - - if (isempty(s)) - return false; - - /* Doesn't accept empty hostnames, hostnames with - * leading dots, and hostnames with multiple dots in a - * sequence. Also ensures that the length stays below - * HOST_NAME_MAX. */ - - for (p = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - return false; - - dot = true; - n_dots++; - } else { - if (!hostname_valid_char(*p)) - return false; - - dot = false; - } - } - - if (dot && (n_dots < 2 || !allow_trailing_dot)) - return false; - - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on - * Linux, but DNS allows domain names - * up to 255 characters */ - return false; - - return true; -} - -char* hostname_cleanup(char *s) { - char *p, *d; - bool dot; - - assert(s); - - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - continue; - - *(d++) = '.'; - dot = true; - } else if (hostname_valid_char(*p)) { - *(d++) = *p; - dot = false; - } - - } - - if (dot && d > s) - d[-1] = 0; - else - *d = 0; - - return s; -} - -bool is_localhost(const char *hostname) { - assert(hostname); - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain */ - - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); -} - -bool is_gateway_hostname(const char *hostname) { - assert(hostname); - - /* This tries to identify the valid syntaxes for the our - * synthetic "gateway" host. */ - - return - strcaseeq(hostname, "gateway") || - strcaseeq(hostname, "gateway."); -} - -int sethostname_idempotent(const char *s) { - char buf[HOST_NAME_MAX + 1] = {}; - - assert(s); - - if (gethostname(buf, sizeof(buf)) < 0) - return -errno; - - if (streq(buf, s)) - return 0; - - if (sethostname(s, strlen(s)) < 0) - return -errno; - - return 1; -} - -int read_hostname_config(const char *path, char **hostname) { - _cleanup_fclose_ FILE *f = NULL; - char l[LINE_MAX]; - char *name = NULL; - - assert(path); - assert(hostname); - - f = fopen(path, "re"); - if (!f) - return -errno; - - /* may have comments, ignore them */ - FOREACH_LINE(l, f, return -errno) { - truncate_nl(l); - if (l[0] != '\0' && l[0] != '#') { - /* found line with value */ - name = hostname_cleanup(l); - name = strdup(name); - if (!name) - return -ENOMEM; - break; - } - } - - if (!name) - /* no non-empty line found */ - return -ENOENT; - - *hostname = name; - return 0; -} diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h deleted file mode 100644 index 7af4e6c7ec..0000000000 --- a/src/basic/hostname-util.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" - -bool hostname_is_set(void); - -char* gethostname_malloc(void); -int gethostname_strict(char **ret); - -bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; -char* hostname_cleanup(char *s); - -#define machine_name_is_valid(s) hostname_is_valid(s, false) - -bool is_localhost(const char *hostname); -bool is_gateway_hostname(const char *hostname); - -int sethostname_idempotent(const char *s); - -int read_hostname_config(const char *path, char **hostname); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c deleted file mode 100644 index aa7ccd1afd..0000000000 --- a/src/basic/in-addr-util.c +++ /dev/null @@ -1,449 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "util.h" - -bool in4_addr_is_null(const struct in_addr *a) { - return a->s_addr == 0; -} - -bool in6_addr_is_null(const struct in6_addr *a) { - return - a->s6_addr32[0] == 0 && - a->s6_addr32[1] == 0 && - a->s6_addr32[2] == 0 && - a->s6_addr32[3] == 0; -} - -int in_addr_is_null(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_null(&u->in); - - if (family == AF_INET6) - return in6_addr_is_null(&u->in6); - - return -EAFNOSUPPORT; -} - -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) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); - - if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); - - 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); - - if (family == AF_INET) - return a->in.s_addr == b->in.s_addr; - - if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, - unsigned aprefixlen, - const union in_addr_union *b, - unsigned bprefixlen) { - - unsigned m; - - assert(a); - assert(b); - - /* Checks whether there are any addresses that are in both - * networks */ - - m = MIN(aprefixlen, bprefixlen); - - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { - assert(u); - - /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ - - if (prefixlen <= 0) - return 0; - - if (family == AF_INET) { - uint32_t c, n; - - if (prefixlen > 32) - prefixlen = 32; - - c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); - - u->in.s_addr = htobe32(n); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr add = {}, result; - uint8_t overflow = 0; - unsigned i; - - if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); - - for (i = 16; i > 0; i--) { - unsigned j = i - 1; - - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); - } - - if (overflow) - return 0; - - u->in6 = result; - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN; - else - return -EAFNOSUPPORT; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - *ret = x; - return 0; -} - -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - size_t l; - char *x; - int r; - - assert(u); - assert(ret); - - /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly - * handle IPv6 link-local addresses. */ - - if (family != AF_INET6) - goto fallback; - if (ifindex <= 0) - goto fallback; - - r = in_addr_is_link_local(family, u); - if (r < 0) - return r; - if (r == 0) - goto fallback; - - l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; - - return 0; - -fallback: - return in_addr_to_string(family, u, ret); -} - -int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - - assert(s); - assert(ret); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - errno = 0; - if (inet_pton(family, s, ret) <= 0) - return errno > 0 ? -errno : -EINVAL; - - return 0; -} - -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { - int r; - - assert(s); - assert(family); - assert(ret); - - r = in_addr_from_string(AF_INET, s, ret); - if (r >= 0) { - *family = AF_INET; - return 0; - } - - r = in_addr_from_string(AF_INET6, s, ret); - if (r >= 0) { - *family = AF_INET6; - return 0; - } - - return -EINVAL; -} - -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { - const char *suffix; - int r, ifi = 0; - - assert(s); - assert(family); - assert(ret); - - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ - - suffix = strchr(s, '%'); - if (suffix) { - - if (ifindex) { - /* If we shall return the interface index, try to parse it */ - r = parse_ifindex(suffix + 1, &ifi); - if (r < 0) { - unsigned u; - - u = if_nametoindex(suffix + 1); - if (u <= 0) - return -errno; - - ifi = (int) u; - } - } - - s = strndupa(s, suffix - s); - } - - r = in_addr_from_string_auto(s, family, ret); - if (r < 0) - return r; - - if (ifindex) - *ifindex = ifi; - - return r; -} - -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { - assert(addr); - - return 32 - u32ctz(be32toh(addr->s_addr)); -} - -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { - assert(addr); - assert(prefixlen <= 32); - - /* Shifting beyond 32 is not defined, handle this specially. */ - if (prefixlen == 0) - addr->s_addr = 0; - else - addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); - - return addr; -} - -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint8_t msb_octet = *(uint8_t*) addr; - - /* addr may not be aligned, so make sure we only access it byte-wise */ - - assert(addr); - assert(prefixlen); - - if (msb_octet < 128) - /* class A, leading bits: 0 */ - *prefixlen = 8; - else if (msb_octet < 192) - /* class B, leading bits 10 */ - *prefixlen = 16; - else if (msb_octet < 224) - /* class C, leading bits 110 */ - *prefixlen = 24; - else - /* class D or E, no default prefixlen */ - return -ERANGE; - - return 0; -} - -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { - unsigned char prefixlen; - int r; - - assert(addr); - assert(mask); - - r = in_addr_default_prefixlen(addr, &prefixlen); - if (r < 0) - return r; - - in_addr_prefixlen_to_netmask(mask, prefixlen); - return 0; -} - -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { - assert(addr); - - if (family == AF_INET) { - struct in_addr mask; - - if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) - return -EINVAL; - - addr->in.s_addr &= mask.s_addr; - return 0; - } - - if (family == AF_INET6) { - unsigned i; - - for (i = 0; i < 16; i++) { - uint8_t mask; - - if (prefixlen >= 8) { - mask = 0xFF; - prefixlen -= 8; - } else { - mask = 0xFF << (8 - prefixlen); - prefixlen = 0; - } - - addr->in6.s6_addr[i] &= mask; - } - - return 0; - } - - return -EAFNOSUPPORT; -} diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h deleted file mode 100644 index d60064aef8..0000000000 --- a/src/basic/in-addr-util.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "macro.h" -#include "util.h" - -union in_addr_union { - struct in_addr in; - struct in6_addr in6; -}; - -struct in_addr_data { - int family; - union in_addr_union address; -}; - -bool in4_addr_is_null(const struct in_addr *a); -bool in6_addr_is_null(const struct in6_addr *a); - -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); -int in_addr_to_string(int family, const union in_addr_union *u, char **ret); -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); -int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); - -static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(family == AF_INET || family == AF_INET6); - return family == AF_INET6 ? 16 : 4; -} - -#define IN_ADDR_NULL ((union in_addr_union) {}) diff --git a/src/basic/io-util.c b/src/basic/io-util.c deleted file mode 100644 index cc6dfa8c1b..0000000000 --- a/src/basic/io-util.c +++ /dev/null @@ -1,269 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "io-util.h" -#include "time-util.h" - -int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - - /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything - * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read - * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used - * was set to non-blocking too. */ - - 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 deleted file mode 100644 index 4684ed3bfc..0000000000 --- a/src/basic/io-util.h +++ /dev/null @@ -1,95 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#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; -} - -static inline bool FILE_SIZE_VALID(uint64_t l) { - /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than - * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ - - return (l >> 63) == 0; -} - -static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { - - /* Same as above, but allows one extra value: -1 as indication for infinity. */ - - if (l == (uint64_t) -1) - return true; - - return FILE_SIZE_VALID(l); - -} diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h deleted file mode 100644 index d8bb6eb497..0000000000 --- a/src/basic/ioprio.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef IOPRIO_H -#define IOPRIO_H - -/* This is minimal version of Linux' linux/ioprio.h header file, which - * is licensed GPL2 */ - -#include -#include - -/* - * Gives us 8 prio classes with 13-bits of data for each class - */ -#define IOPRIO_BITS (16) -#define IOPRIO_CLASS_SHIFT (13) -#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) - -#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) -#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) -#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) - -#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) - -/* - * These are the io priority groups as implemented by CFQ. RT is the realtime - * class, it always gets premium service. BE is the best-effort scheduling - * class, the default for any process. IDLE is the idle scheduling class, it - * is only served when no one else is using the disk. - */ -enum { - IOPRIO_CLASS_NONE, - IOPRIO_CLASS_RT, - IOPRIO_CLASS_BE, - IOPRIO_CLASS_IDLE, -}; - -/* - * 8 best effort priority levels are supported - */ -#define IOPRIO_BE_NR (8) - -enum { - IOPRIO_WHO_PROCESS = 1, - IOPRIO_WHO_PGRP, - IOPRIO_WHO_USER, -}; - -static inline int ioprio_set(int which, int who, int ioprio) { - return syscall(__NR_ioprio_set, which, who, ioprio); -} - -static inline int ioprio_get(int which, int who) { - return syscall(__NR_ioprio_get, which, who); -} - -#endif diff --git a/src/basic/label.c b/src/basic/label.c deleted file mode 100644 index f5ab855d32..0000000000 --- a/src/basic/label.c +++ /dev/null @@ -1,82 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "label.h" -#include "macro.h" -#include "selinux-util.h" -#include "smack-util.h" - -int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - int r, q; - - r = mac_selinux_fix(path, ignore_enoent, ignore_erofs); - q = mac_smack_fix(path, ignore_enoent, ignore_erofs); - - if (r < 0) - return r; - if (q < 0) - return q; - - return 0; -} - -int mkdir_label(const char *path, mode_t mode) { - int r; - - assert(path); - - r = mac_selinux_create_file_prepare(path, S_IFDIR); - if (r < 0) - return r; - - if (mkdir(path, mode) < 0) - r = -errno; - - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(path, false, false); -} - -int symlink_label(const char *old_path, const char *new_path) { - int r; - - assert(old_path); - assert(new_path); - - r = mac_selinux_create_file_prepare(new_path, S_IFLNK); - if (r < 0) - return r; - - if (symlink(old_path, new_path) < 0) - r = -errno; - - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(new_path, false, false); -} diff --git a/src/basic/label.h b/src/basic/label.h deleted file mode 100644 index 3e9251aa71..0000000000 --- a/src/basic/label.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 . -***/ - -#include -#include - -int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); - -int mkdir_label(const char *path, mode_t mode); -int symlink_label(const char *old_path, const char *new_path); diff --git a/src/basic/list.h b/src/basic/list.h deleted file mode 100644 index 5962aa4211..0000000000 --- a/src/basic/list.h +++ /dev/null @@ -1,182 +0,0 @@ -#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 . -***/ - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define LIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define LIST_FIELDS(t,name) \ - t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define LIST_HEAD_INIT(head) \ - do { \ - (head) = NULL; } \ - while (false) - -/* Initialize a list item */ -#define LIST_INIT(name,item) \ - do { \ - typeof(*(item)) *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while (false) - -/* Prepend an item to the list */ -#define LIST_PREPEND(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (false) - -/* Append an item to the list */ -#define LIST_APPEND(name,head,item) \ - do { \ - typeof(*(head)) *_tail; \ - LIST_FIND_TAIL(name,head,_tail); \ - LIST_INSERT_AFTER(name,head,_tail,item); \ - } while (false) - -/* Remove an item from the list */ -#define LIST_REMOVE(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else { \ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while (false) - -/* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) - -/* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) - -/* Insert an item after another one (a = where, b = what) */ -#define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if ((_b->name##_next = *_head)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->name##_next = _a->name##_next)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = _a; \ - _a->name##_next = _b; \ - } \ - } while (false) - -/* Insert an item before another one (a = where, b = what) */ -#define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if (!*_head) { \ - _b->name##_next = NULL; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - typeof(*(head)) *_tail = (head); \ - while (_tail->name##_next) \ - _tail = _tail->name##_next; \ - _b->name##_next = NULL; \ - _b->name##_prev = _tail; \ - _tail->name##_next = _b; \ - } \ - } else { \ - if ((_b->name##_prev = _a->name##_prev)) \ - _b->name##_prev->name##_next = _b; \ - _b->name##_next = _a; \ - _a->name##_prev = _b; \ - } \ - } while (false) - -#define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ - -#define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) - -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) - -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#define LIST_FOREACH_AFTER(name,i,p) \ - for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) - -/* Iterate through all the members of the list p is included in, but skip over p */ -#define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) - -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ -#define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c deleted file mode 100644 index ada0a28cd8..0000000000 --- a/src/basic/locale-util.c +++ /dev/null @@ -1,322 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dirent-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "locale-util.h" -#include "path-util.h" -#include "set.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -static int add_locales_from_archive(Set *locales) { - /* Stolen from glibc... */ - - struct locarhead { - uint32_t magic; - /* Serial number. */ - uint32_t serial; - /* Name hash table. */ - uint32_t namehash_offset; - uint32_t namehash_used; - uint32_t namehash_size; - /* String table. */ - uint32_t string_offset; - uint32_t string_used; - uint32_t string_size; - /* Table with locale records. */ - uint32_t locrectab_offset; - uint32_t locrectab_used; - uint32_t locrectab_size; - /* MD5 sum hash table. */ - uint32_t sumhash_offset; - uint32_t sumhash_used; - uint32_t sumhash_size; - }; - - struct namehashent { - /* Hash value of the name. */ - uint32_t hashval; - /* Offset of the name in the string table. */ - uint32_t name_offset; - /* Offset of the locale record. */ - uint32_t locrec_offset; - }; - - const struct locarhead *h; - const struct namehashent *e; - const void *p = MAP_FAILED; - _cleanup_close_ int fd = -1; - size_t sz = 0; - struct stat st; - unsigned i; - int r; - - fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return errno == ENOENT ? 0 : -errno; - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EBADMSG; - - if (st.st_size < (off_t) sizeof(struct locarhead)) - return -EBADMSG; - - p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) - return -errno; - - h = (const struct locarhead *) p; - if (h->magic != 0xde020109 || - h->namehash_offset + h->namehash_size > st.st_size || - h->string_offset + h->string_size > st.st_size || - h->locrectab_offset + h->locrectab_size > st.st_size || - h->sumhash_offset + h->sumhash_size > st.st_size) { - r = -EBADMSG; - goto finish; - } - - e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); - for (i = 0; i < h->namehash_size; i++) { - char *z; - - if (e[i].locrec_offset == 0) - continue; - - if (!utf8_is_valid((char*) p + e[i].name_offset)) - continue; - - z = strdup((char*) p + e[i].name_offset); - if (!z) { - r = -ENOMEM; - goto finish; - } - - r = set_consume(locales, z); - if (r < 0) - goto finish; - } - - r = 0; - - finish: - if (p != MAP_FAILED) - munmap((void*) p, sz); - - return r; -} - -static int add_locales_from_libdir (Set *locales) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *entry; - int r; - - dir = opendir("/usr/lib/locale"); - if (!dir) - return errno == ENOENT ? 0 : -errno; - - FOREACH_DIRENT(entry, dir, return -errno) { - char *z; - - dirent_ensure_type(dir, entry); - - if (entry->d_type != DT_DIR) - continue; - - z = strdup(entry->d_name); - if (!z) - return -ENOMEM; - - r = set_consume(locales, z); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int get_locales(char ***ret) { - _cleanup_set_free_ Set *locales = NULL; - _cleanup_strv_free_ char **l = NULL; - int r; - - locales = set_new(&string_hash_ops); - if (!locales) - return -ENOMEM; - - r = add_locales_from_archive(locales); - if (r < 0 && r != -ENOENT) - return r; - - r = add_locales_from_libdir(locales); - if (r < 0) - return r; - - l = set_get_strv(locales); - if (!l) - return -ENOMEM; - - strv_sort(l); - - *ret = l; - l = NULL; - - return 0; -} - -bool locale_is_valid(const char *name) { - - if (isempty(name)) - return false; - - if (strlen(name) >= 128) - return false; - - if (!utf8_is_valid(name)) - return false; - - if (!filename_is_valid(name)) - return false; - - if (!string_is_safe(name)) - return false; - - 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 *special_glyph(SpecialGlyph code) { - - static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { - /* ASCII fallback */ - [false] = { - [TREE_VERTICAL] = "| ", - [TREE_BRANCH] = "|-", - [TREE_RIGHT] = "`-", - [TREE_SPACE] = " ", - [TRIANGULAR_BULLET] = ">", - [BLACK_CIRCLE] = "*", - [ARROW] = "->", - [MDASH] = "-", - }, - - /* UTF-8 */ - [ true ] = { - [TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [TREE_SPACE] = " ", /* */ - [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [BLACK_CIRCLE] = "\342\227\217", /* ● */ - [ARROW] = "\342\206\222", /* → */ - [MDASH] = "\342\200\223", /* – */ - }, - }; - - return draw_table[is_locale_utf8()][code]; -} - -static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { - [VARIABLE_LANG] = "LANG", - [VARIABLE_LANGUAGE] = "LANGUAGE", - [VARIABLE_LC_CTYPE] = "LC_CTYPE", - [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", - [VARIABLE_LC_TIME] = "LC_TIME", - [VARIABLE_LC_COLLATE] = "LC_COLLATE", - [VARIABLE_LC_MONETARY] = "LC_MONETARY", - [VARIABLE_LC_MESSAGES] = "LC_MESSAGES", - [VARIABLE_LC_PAPER] = "LC_PAPER", - [VARIABLE_LC_NAME] = "LC_NAME", - [VARIABLE_LC_ADDRESS] = "LC_ADDRESS", - [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE", - [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT", - [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION" -}; - -DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable); diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h deleted file mode 100644 index 0630a034ab..0000000000 --- a/src/basic/locale-util.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "macro.h" - -typedef enum LocaleVariable { - /* We don't list LC_ALL here on purpose. People should be - * using LANG instead. */ - - VARIABLE_LANG, - VARIABLE_LANGUAGE, - VARIABLE_LC_CTYPE, - VARIABLE_LC_NUMERIC, - VARIABLE_LC_TIME, - VARIABLE_LC_COLLATE, - VARIABLE_LC_MONETARY, - VARIABLE_LC_MESSAGES, - VARIABLE_LC_PAPER, - VARIABLE_LC_NAME, - VARIABLE_LC_ADDRESS, - VARIABLE_LC_TELEPHONE, - VARIABLE_LC_MEASUREMENT, - VARIABLE_LC_IDENTIFICATION, - _VARIABLE_LC_MAX, - _VARIABLE_LC_INVALID = -1 -} 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 { - TREE_VERTICAL, - TREE_BRANCH, - TREE_RIGHT, - TREE_SPACE, - TRIANGULAR_BULLET, - BLACK_CIRCLE, - ARROW, - MDASH, - _SPECIAL_GLYPH_MAX -} SpecialGlyph; - -const char *special_glyph(SpecialGlyph code) _const_; - -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 deleted file mode 100644 index 3ee4191e4d..0000000000 --- a/src/basic/lockfile-util.c +++ /dev/null @@ -1,153 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "lockfile-util.h" -#include "macro.h" -#include "path-util.h" - -int make_lock_file(const char *p, int operation, LockFile *ret) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - /* - * We use UNPOSIX locks if they are available. They have nice - * semantics, and are mostly compatible with NFS. However, - * they are only available on new kernels. When we detect we - * are running on an older kernel, then we fall back to good - * old BSD locks. They also have nice semantics, but are - * slightly problematic on NFS, where they are upgraded to - * POSIX locks, even though locally they are orthogonal to - * POSIX locks. - */ - - t = strdup(p); - if (!t) - return -ENOMEM; - - for (;;) { - struct flock fl = { - .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, - .l_whence = SEEK_SET, - }; - struct stat st; - - fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) - return -errno; - - r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); - if (r < 0) { - - /* If the kernel is too old, use good old BSD locks */ - if (errno == EINVAL) - r = flock(fd, operation); - - if (r < 0) - return errno == EAGAIN ? -EBUSY : -errno; - } - - /* If we acquired the lock, let's check if the file - * still exists in the file system. If not, then the - * previous exclusive owner removed it and then closed - * it. In such a case our acquired lock is worthless, - * hence try again. */ - - r = fstat(fd, &st); - if (r < 0) - return -errno; - if (st.st_nlink > 0) - break; - - fd = safe_close(fd); - } - - ret->path = t; - ret->fd = fd; - ret->operation = operation; - - fd = -1; - t = NULL; - - return r; -} - -int make_lock_file_for(const char *p, int operation, LockFile *ret) { - const char *fn; - char *t; - - assert(p); - assert(ret); - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - t = newa(char, strlen(p) + 2 + 4 + 1); - stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); - - return make_lock_file(t, operation, ret); -} - -void release_lock_file(LockFile *f) { - int r; - - if (!f) - return; - - if (f->path) { - - /* If we are the exclusive owner we can safely delete - * the lock file itself. If we are not the exclusive - * owner, we can try becoming it. */ - - if (f->fd >= 0 && - (f->operation & ~LOCK_NB) == LOCK_SH) { - static const struct flock fl = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - }; - - r = fcntl(f->fd, F_OFD_SETLK, &fl); - if (r < 0 && errno == EINVAL) - r = flock(f->fd, LOCK_EX|LOCK_NB); - - if (r >= 0) - f->operation = LOCK_EX|LOCK_NB; - } - - if ((f->operation & ~LOCK_NB) == LOCK_EX) - unlink_noerrno(f->path); - - f->path = mfree(f->path); - } - - f->fd = safe_close(f->fd); - f->operation = 0; -} diff --git a/src/basic/lockfile-util.h b/src/basic/lockfile-util.h deleted file mode 100644 index 22491ee8e1..0000000000 --- a/src/basic/lockfile-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" -#include "missing.h" - -typedef struct LockFile { - char *path; - int fd; - int operation; -} LockFile; - -int make_lock_file(const char *p, int operation, LockFile *ret); -int make_lock_file_for(const char *p, int operation, LockFile *ret); -void release_lock_file(LockFile *f); - -#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) - -#define LOCK_FILE_INIT { .fd = -1, .path = NULL } diff --git a/src/basic/log.c b/src/basic/log.c deleted file mode 100644 index 49b4598b7c..0000000000 --- a/src/basic/log.c +++ /dev/null @@ -1,1170 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "proc-cmdline.h" -#include "process-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) - -static LogTarget log_target = LOG_TARGET_CONSOLE; -static int log_max_level = LOG_INFO; -static int log_facility = LOG_DAEMON; - -static int console_fd = STDERR_FILENO; -static int syslog_fd = -1; -static int kmsg_fd = -1; -static int journal_fd = -1; - -static bool syslog_is_stream = false; - -static bool show_color = false; -static bool show_location = false; - -static bool upgrade_syslog_to_journal = false; - -/* Akin to glibc's __abort_msg; which is private and we hence cannot - * use here. */ -static char *log_abort_msg = NULL; - -void log_close_console(void) { - - if (console_fd < 0) - return; - - if (getpid() == 1) { - if (console_fd >= 3) - safe_close(console_fd); - - console_fd = -1; - } -} - -static int log_open_console(void) { - - if (console_fd >= 0) - return 0; - - if (getpid() == 1) { - console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (console_fd < 0) - return console_fd; - } else - console_fd = STDERR_FILENO; - - return 0; -} - -void log_close_kmsg(void) { - kmsg_fd = safe_close(kmsg_fd); -} - -static int log_open_kmsg(void) { - - if (kmsg_fd >= 0) - return 0; - - kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (kmsg_fd < 0) - return -errno; - - return 0; -} - -void log_close_syslog(void) { - syslog_fd = safe_close(syslog_fd); -} - -static int create_log_socket(int type) { - struct timeval tv; - int fd; - - fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - fd_inc_sndbuf(fd, SNDBUF_SIZE); - - /* We need a blocking fd here since we'd otherwise lose - messages way too early. However, let's not hang forever in the - unlikely case of a deadlock. */ - if (getpid() == 1) - timeval_store(&tv, 10 * USEC_PER_MSEC); - else - timeval_store(&tv, 10 * USEC_PER_SEC); - (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - return fd; -} - -static int log_open_syslog(void) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/dev/log", - }; - - int r; - - if (syslog_fd >= 0) - return 0; - - syslog_fd = create_log_socket(SOCK_DGRAM); - if (syslog_fd < 0) { - r = syslog_fd; - goto fail; - } - - if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - safe_close(syslog_fd); - - /* Some legacy syslog systems still use stream - * sockets. They really shouldn't. But what can we - * do... */ - syslog_fd = create_log_socket(SOCK_STREAM); - if (syslog_fd < 0) { - r = syslog_fd; - goto fail; - } - - if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = -errno; - goto fail; - } - - syslog_is_stream = true; - } else - syslog_is_stream = false; - - return 0; - -fail: - log_close_syslog(); - return r; -} - -void log_close_journal(void) { - journal_fd = safe_close(journal_fd); -} - -static int log_open_journal(void) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - - int r; - - if (journal_fd >= 0) - return 0; - - journal_fd = create_log_socket(SOCK_DGRAM); - if (journal_fd < 0) { - r = journal_fd; - goto fail; - } - - if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - log_close_journal(); - return r; -} - -int log_open(void) { - int r; - - /* If we don't use the console we close it here, to not get - * killed by SAK. If we don't use syslog we close it here so - * that we are not confused by somebody deleting the socket in - * the fs. If we don't use /dev/kmsg we still keep it open, - * because there is no reason to close it. */ - - if (log_target == LOG_TARGET_NULL) { - log_close_journal(); - log_close_syslog(); - log_close_console(); - return 0; - } - - if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) || - getpid() == 1 || - isatty(STDERR_FILENO) <= 0) { - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) { - r = log_open_journal(); - if (r >= 0) { - log_close_syslog(); - log_close_console(); - return r; - } - } - - if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_SYSLOG) { - r = log_open_syslog(); - if (r >= 0) { - log_close_journal(); - log_close_console(); - return r; - } - } - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SAFE || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_KMSG) { - r = log_open_kmsg(); - if (r >= 0) { - log_close_journal(); - log_close_syslog(); - log_close_console(); - return r; - } - } - } - - log_close_journal(); - log_close_syslog(); - - return log_open_console(); -} - -void log_set_target(LogTarget target) { - assert(target >= 0); - assert(target < _LOG_TARGET_MAX); - - if (upgrade_syslog_to_journal) { - if (target == LOG_TARGET_SYSLOG) - target = LOG_TARGET_JOURNAL; - else if (target == LOG_TARGET_SYSLOG_OR_KMSG) - target = LOG_TARGET_JOURNAL_OR_KMSG; - } - - log_target = target; -} - -void log_close(void) { - log_close_journal(); - log_close_syslog(); - log_close_kmsg(); - log_close_console(); -} - -void log_forget_fds(void) { - console_fd = kmsg_fd = syslog_fd = journal_fd = -1; -} - -void log_set_max_level(int level) { - assert((level & LOG_PRIMASK) == level); - - log_max_level = level; -} - -void log_set_facility(int facility) { - log_facility = facility; -} - -static int write_to_console( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *buffer) { - - char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; - struct iovec iovec[6] = {}; - unsigned n = 0; - bool highlight; - - if (console_fd < 0) - return 0; - - if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { - sprintf(prefix, "<%i>", level); - IOVEC_SET_STRING(iovec[n++], prefix); - } - - highlight = LOG_PRI(level) <= LOG_ERR && show_color; - - if (show_location) { - snprintf(location, sizeof(location), "(%s:%i) ", file, line); - IOVEC_SET_STRING(iovec[n++], location); - } - - if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); - IOVEC_SET_STRING(iovec[n++], buffer); - if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); - IOVEC_SET_STRING(iovec[n++], "\n"); - - if (writev(console_fd, iovec, n) < 0) { - - if (errno == EIO && getpid() == 1) { - - /* If somebody tried to kick us from our - * console tty (via vhangup() or suchlike), - * try to reconnect */ - - log_close_console(); - log_open_console(); - - if (console_fd < 0) - return 0; - - if (writev(console_fd, iovec, n) < 0) - return -errno; - } else - return -errno; - } - - return 1; -} - -static int write_to_syslog( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *buffer) { - - char header_priority[2 + DECIMAL_STR_MAX(int) + 1], - header_time[64], - header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; - struct iovec iovec[5] = {}; - struct msghdr msghdr = { - .msg_iov = iovec, - .msg_iovlen = ELEMENTSOF(iovec), - }; - time_t t; - struct tm *tm; - - if (syslog_fd < 0) - return 0; - - xsprintf(header_priority, "<%i>", level); - - t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); - tm = localtime(&t); - if (!tm) - return -EINVAL; - - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return -EINVAL; - - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); - - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], header_time); - IOVEC_SET_STRING(iovec[2], program_invocation_short_name); - IOVEC_SET_STRING(iovec[3], header_pid); - IOVEC_SET_STRING(iovec[4], buffer); - - /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ - if (syslog_is_stream) - iovec[4].iov_len++; - - for (;;) { - ssize_t n; - - n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); - if (n < 0) - return -errno; - - if (!syslog_is_stream || - (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) - break; - - IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); - } - - return 1; -} - -static int write_to_kmsg( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *buffer) { - - char header_priority[2 + DECIMAL_STR_MAX(int) + 1], - header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; - struct iovec iovec[5] = {}; - - if (kmsg_fd < 0) - return 0; - - xsprintf(header_priority, "<%i>", level); - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); - - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], program_invocation_short_name); - IOVEC_SET_STRING(iovec[2], header_pid); - IOVEC_SET_STRING(iovec[3], buffer); - IOVEC_SET_STRING(iovec[4], "\n"); - - if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) - return -errno; - - return 1; -} - -static int log_do_header( - char *header, - size_t size, - int level, - int error, - const char *file, int line, const char *func, - const char *object_field, const char *object) { - - snprintf(header, size, - "PRIORITY=%i\n" - "SYSLOG_FACILITY=%i\n" - "%s%s%s" - "%s%.*i%s" - "%s%s%s" - "%s%.*i%s" - "%s%s%s" - "SYSLOG_IDENTIFIER=%s\n", - LOG_PRI(level), - LOG_FAC(level), - isempty(file) ? "" : "CODE_FILE=", - isempty(file) ? "" : file, - isempty(file) ? "" : "\n", - line ? "CODE_LINE=" : "", - line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */ - line ? "\n" : "", - isempty(func) ? "" : "CODE_FUNCTION=", - isempty(func) ? "" : func, - isempty(func) ? "" : "\n", - error ? "ERRNO=" : "", - error ? 1 : 0, error, - error ? "\n" : "", - isempty(object) ? "" : object_field, - isempty(object) ? "" : object, - isempty(object) ? "" : "\n", - program_invocation_short_name); - - return 0; -} - -static int write_to_journal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *buffer) { - - char header[LINE_MAX]; - struct iovec iovec[4] = {}; - struct msghdr mh = {}; - - if (journal_fd < 0) - return 0; - - log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object); - - IOVEC_SET_STRING(iovec[0], header); - IOVEC_SET_STRING(iovec[1], "MESSAGE="); - IOVEC_SET_STRING(iovec[2], buffer); - IOVEC_SET_STRING(iovec[3], "\n"); - - mh.msg_iov = iovec; - mh.msg_iovlen = ELEMENTSOF(iovec); - - if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 1; -} - -static int log_dispatch( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - char *buffer) { - - assert(buffer); - - if (log_target == LOG_TARGET_NULL) - return -error; - - /* Patch in LOG_DAEMON facility if necessary */ - if ((level & LOG_FACMASK) == 0) - level = log_facility | LOG_PRI(level); - - if (error < 0) - error = -error; - - do { - char *e; - int k = 0; - - buffer += strspn(buffer, NEWLINE); - - if (buffer[0] == 0) - break; - - if ((e = strpbrk(buffer, NEWLINE))) - *(e++) = 0; - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) { - - k = write_to_journal(level, error, file, line, func, object_field, object, buffer); - if (k < 0) { - if (k != -EAGAIN) - log_close_journal(); - log_open_kmsg(); - } - } - - if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_SYSLOG) { - - k = write_to_syslog(level, error, file, line, func, object_field, object, buffer); - if (k < 0) { - if (k != -EAGAIN) - log_close_syslog(); - log_open_kmsg(); - } - } - - if (k <= 0 && - (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SAFE || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_KMSG)) { - - k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer); - if (k < 0) { - log_close_kmsg(); - log_open_console(); - } - } - - if (k <= 0) - (void) write_to_console(level, error, file, line, func, object_field, object, buffer); - - buffer = e; - } while (buffer); - - return -error; -} - -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer) { - - PROTECT_ERRNO; - - /* This modifies the buffer... */ - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); -} - -int log_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) { - - PROTECT_ERRNO; - char buffer[LINE_MAX]; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; - - vsnprintf(buffer, sizeof(buffer), format, ap); - - return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); -} - -int log_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - va_list ap; - int r; - - va_start(ap, format); - r = log_internalv(level, error, file, line, func, format, ap); - va_end(ap); - - return r; -} - -int log_object_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *format, - va_list ap) { - - PROTECT_ERRNO; - char *buffer, *b; - size_t l; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; - - /* Prepend the object name before the message */ - if (object) { - size_t n; - - n = strlen(object); - l = n + 2 + LINE_MAX; - - buffer = newa(char, l); - b = stpcpy(stpcpy(buffer, object), ": "); - } else { - l = LINE_MAX; - b = buffer = newa(char, l); - } - - vsnprintf(b, l, format, ap); - - return log_dispatch(level, error, file, line, func, object_field, object, buffer); -} - -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *format, ...) { - - va_list ap; - int r; - - va_start(ap, format); - r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap); - va_end(ap); - - return r; -} - -static void log_assert( - int level, - const char *text, - const char *file, - int line, - const char *func, - const char *format) { - - static char buffer[LINE_MAX]; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return; - - DISABLE_WARNING_FORMAT_NONLITERAL; - xsprintf(buffer, format, text, file, line, func); - REENABLE_WARNING; - - log_abort_msg = buffer; - - log_dispatch(level, 0, file, line, func, NULL, NULL, buffer); -} - -noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); - abort(); -} - -noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); - abort(); -} - -void log_assert_failed_return(const char *text, const char *file, int line, const char *func) { - PROTECT_ERRNO; - log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); -} - -int log_oom_internal(const char *file, int line, const char *func) { - log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); - return -ENOMEM; -} - -int log_format_iovec( - struct iovec *iovec, - unsigned iovec_len, - unsigned *n, - bool newline_separator, - int error, - const char *format, - va_list ap) { - - static const char nl = '\n'; - - while (format && *n + 1 < iovec_len) { - va_list aq; - char *m; - int r; - - /* We need to copy the va_list structure, - * since vasprintf() leaves it afterwards at - * an undefined location */ - - if (error != 0) - errno = error; - - va_copy(aq, ap); - r = vasprintf(&m, format, aq); - va_end(aq); - if (r < 0) - return -EINVAL; - - /* Now, jump enough ahead, so that we point to - * the next format string */ - VA_FORMAT_ADVANCE(format, ap); - - IOVEC_SET_STRING(iovec[(*n)++], m); - - if (newline_separator) { - iovec[*n].iov_base = (char*) &nl; - iovec[*n].iov_len = 1; - (*n)++; - } - - format = va_arg(ap, char *); - } - return 0; -} - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - char buf[LINE_MAX]; - bool found = false; - PROTECT_ERRNO; - va_list ap; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - if (log_target == LOG_TARGET_NULL) - return -error; - - if ((level & LOG_FACMASK) == 0) - level = log_facility | LOG_PRI(level); - - if ((log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) && - journal_fd >= 0) { - char header[LINE_MAX]; - struct iovec iovec[17] = {}; - unsigned n = 0, i; - int r; - struct msghdr mh = { - .msg_iov = iovec, - }; - bool fallback = false; - - /* If the journal is available do structured logging */ - log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL); - IOVEC_SET_STRING(iovec[n++], header); - - va_start(ap, format); - r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); - if (r < 0) - fallback = true; - else { - mh.msg_iovlen = n; - (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); - } - - va_end(ap); - for (i = 1; i < n; i += 2) - free(iovec[i].iov_base); - - if (!fallback) - return -error; - } - - /* Fallback if journal logging is not available or didn't work. */ - - va_start(ap, format); - while (format) { - va_list aq; - - if (error != 0) - errno = error; - - va_copy(aq, ap); - vsnprintf(buf, sizeof(buf), format, aq); - va_end(aq); - - if (startswith(buf, "MESSAGE=")) { - found = true; - break; - } - - VA_FORMAT_ADVANCE(format, ap); - - format = va_arg(ap, char *); - } - va_end(ap); - - if (!found) - return -error; - - return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8); -} - -int log_set_target_from_string(const char *e) { - LogTarget t; - - t = log_target_from_string(e); - if (t < 0) - return -EINVAL; - - log_set_target(t); - return 0; -} - -int log_set_max_level_from_string(const char *e) { - int t; - - t = log_level_from_string(e); - if (t < 0) - return -EINVAL; - - log_set_max_level(t); - return 0; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - /* - * The systemd.log_xyz= settings are parsed by all tools, and - * so is "debug". - * - * However, "quiet" is only parsed by PID 1, and only turns of - * status output to /dev/console, but does not alter the log - * level. - */ - - if (streq(key, "debug") && !value) - log_set_max_level(LOG_DEBUG); - - else if (streq(key, "systemd.log_target") && value) { - - if (log_set_target_from_string(value) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_level") && value) { - - if (log_set_max_level_from_string(value) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_color") && value) { - - if (log_show_color_from_string(value) < 0) - log_warning("Failed to parse log color setting '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_location") && value) { - - if (log_show_location_from_string(value) < 0) - log_warning("Failed to parse log location setting '%s'. Ignoring.", value); - } - - return 0; -} - -void log_parse_environment(void) { - const char *e; - - if (get_ctty_devnr(0, NULL) < 0) - /* Only try to read the command line in daemons. - We assume that anything that has a controlling - tty is user stuff. */ - (void) parse_proc_cmdline(parse_proc_cmdline_item); - - e = secure_getenv("SYSTEMD_LOG_TARGET"); - if (e && log_set_target_from_string(e) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_LEVEL"); - if (e && log_set_max_level_from_string(e) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_COLOR"); - if (e && log_show_color_from_string(e) < 0) - log_warning("Failed to parse bool '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_LOCATION"); - if (e && log_show_location_from_string(e) < 0) - log_warning("Failed to parse bool '%s'. Ignoring.", e); -} - -LogTarget log_get_target(void) { - return log_target; -} - -int log_get_max_level(void) { - return log_max_level; -} - -void log_show_color(bool b) { - show_color = b; -} - -bool log_get_show_color(void) { - return show_color; -} - -void log_show_location(bool b) { - show_location = b; -} - -bool log_get_show_location(void) { - return show_location; -} - -int log_show_color_from_string(const char *e) { - int t; - - t = parse_boolean(e); - if (t < 0) - return t; - - log_show_color(t); - return 0; -} - -int log_show_location_from_string(const char *e) { - int t; - - t = parse_boolean(e); - if (t < 0) - return t; - - log_show_location(t); - return 0; -} - -bool log_on_console(void) { - if (log_target == LOG_TARGET_CONSOLE || - log_target == LOG_TARGET_CONSOLE_PREFIXED) - return true; - - return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0; -} - -static const char *const log_target_table[_LOG_TARGET_MAX] = { - [LOG_TARGET_CONSOLE] = "console", - [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed", - [LOG_TARGET_KMSG] = "kmsg", - [LOG_TARGET_JOURNAL] = "journal", - [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg", - [LOG_TARGET_SYSLOG] = "syslog", - [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", - [LOG_TARGET_AUTO] = "auto", - [LOG_TARGET_SAFE] = "safe", - [LOG_TARGET_NULL] = "null" -}; - -DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); - -void log_received_signal(int level, const struct signalfd_siginfo *si) { - if (si->ssi_pid > 0) { - _cleanup_free_ char *p = NULL; - - get_process_comm(si->ssi_pid, &p); - - log_full(level, - "Received SIG%s from PID %"PRIu32" (%s).", - signal_to_string(si->ssi_signo), - si->ssi_pid, strna(p)); - } else - log_full(level, - "Received SIG%s.", - signal_to_string(si->ssi_signo)); - -} - -void log_set_upgrade_syslog_to_journal(bool b) { - upgrade_syslog_to_journal = b; -} - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - PROTECT_ERRNO; - char buffer[LINE_MAX]; - int r; - va_list ap; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - if (log_target == LOG_TARGET_NULL) - return -error; - - if (error != 0) - errno = error; - - va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); - - if (unit) - r = log_struct_internal( - level, error, - file, line, func, - getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, - LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), - "CONFIG_FILE=%s", config_file, - "CONFIG_LINE=%u", config_line, - LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), - NULL); - else - r = log_struct_internal( - level, error, - file, line, func, - LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), - "CONFIG_FILE=%s", config_file, - "CONFIG_LINE=%u", config_line, - LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), - NULL); - - return r; -} diff --git a/src/basic/log.h b/src/basic/log.h deleted file mode 100644 index b6356228d9..0000000000 --- a/src/basic/log.h +++ /dev/null @@ -1,249 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-id128.h" - -#include "macro.h" - -typedef enum LogTarget{ - LOG_TARGET_CONSOLE, - LOG_TARGET_CONSOLE_PREFIXED, - LOG_TARGET_KMSG, - LOG_TARGET_JOURNAL, - LOG_TARGET_JOURNAL_OR_KMSG, - LOG_TARGET_SYSLOG, - LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ - LOG_TARGET_NULL, - _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 -} LogTarget; - -void log_set_target(LogTarget target); -void log_set_max_level(int level); -void log_set_facility(int facility); - -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string(const char *e); - -void log_show_color(bool b); -bool log_get_show_color(void) _pure_; -void log_show_location(bool b); -bool log_get_show_location(void) _pure_; - -int log_show_color_from_string(const char *e); -int log_show_location_from_string(const char *e); - -LogTarget log_get_target(void) _pure_; -int log_get_max_level(void) _pure_; - -int log_open(void); -void log_close(void); -void log_forget_fds(void); - -void log_close_syslog(void); -void log_close_journal(void); -void log_close_kmsg(void); -void log_close_console(void); - -void log_parse_environment(void); - -int log_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,7); - -int log_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) _printf_(6,0); - -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *format, ...) _printf_(8,9); - -int log_object_internalv( - int level, - int error, - const char*file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *format, - va_list ap) _printf_(8,0); - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,0) _sentinel_; - -int log_oom_internal( - const char *file, - int line, - const char *func); - -int log_format_iovec( - struct iovec *iovec, - unsigned iovec_len, - unsigned *n, - bool newline_separator, - int error, - const char *format, - va_list ap); - -/* This modifies the buffer passed! */ -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer); - -/* Logging for various assertions */ -noreturn void log_assert_failed( - const char *text, - const char *file, - int line, - const char *func); - -noreturn void log_assert_failed_unreachable( - const char *text, - const char *file, - int line, - const char *func); - -void log_assert_failed_return( - const char *text, - const char *file, - int line, - const char *func); - -/* Logging with level */ -#define log_full_errno(level, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) - -/* Normal logging */ -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) -#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) -#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) -#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) - -/* Logging triggered by an errno-like error */ -#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) -#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) -#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) -#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) -#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) - -#ifdef LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) -#else -# define log_trace(...) do {} while (0) -#endif - -/* Structured logging */ -#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) -#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) - -/* This modifies the buffer passed! */ -#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) - -#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) - -bool log_on_console(void) _pure_; - -const char *log_target_to_string(LogTarget target) _const_; -LogTarget log_target_from_string(const char *s) _pure_; - -/* Helpers to prepare various fields for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ -#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) - -void log_received_signal(int level, const struct signalfd_siginfo *si); - -void log_set_upgrade_syslog_to_journal(bool b); - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(9, 10); - -#define log_syntax(unit, level, config_file, config_line, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#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)); \ - } \ - }) diff --git a/src/basic/login-util.c b/src/basic/login-util.c deleted file mode 100644 index 339e94f12d..0000000000 --- a/src/basic/login-util.c +++ /dev/null @@ -1,31 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "login-util.h" -#include "string-util.h" - -bool session_id_valid(const char *id) { - - if (isempty(id)) - return false; - - return id[strspn(id, LETTERS DIGITS)] == '\0'; -} diff --git a/src/basic/login-util.h b/src/basic/login-util.h deleted file mode 100644 index b01ee25c88..0000000000 --- a/src/basic/login-util.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -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 deleted file mode 100644 index 6b2aeb933f..0000000000 --- a/src/basic/macro.h +++ /dev/null @@ -1,415 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include - -#define _printf_(a,b) __attribute__ ((format (printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__ ((sentinel)) -#define _unused_ __attribute__ ((unused)) -#define _destructor_ __attribute__ ((destructor)) -#define _pure_ __attribute__ ((pure)) -#define _const_ __attribute__ ((const)) -#define _deprecated_ __attribute__ ((deprecated)) -#define _packed_ __attribute__ ((packed)) -#define _malloc_ __attribute__ ((malloc)) -#define _weak_ __attribute__ ((weak)) -#define _likely_(x) (__builtin_expect(!!(x),1)) -#define _unlikely_(x) (__builtin_expect(!!(x),0)) -#define _public_ __attribute__ ((visibility("default"))) -#define _hidden_ __attribute__ ((visibility("hidden"))) -#define _weakref_(x) __attribute__((weakref(#x))) -#define _alignas_(x) __attribute__((aligned(__alignof(x)))) -#define _cleanup_(x) __attribute__((cleanup(x))) - -/* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - -#define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") - -#define DISABLE_WARNING_MISSING_PROTOTYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") - -#define DISABLE_WARNING_NONNULL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wnonnull\"") - -#define DISABLE_WARNING_SHADOW \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wshadow\"") - -#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") - -#define REENABLE_WARNING \ - _Pragma("GCC diagnostic pop") - -/* automake test harness */ -#define EXIT_TEST_SKIP 77 - -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - -/* builtins */ -#if __SIZEOF_INT__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffs(x); -#elif __SIZEOF_LONG__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); -#else -#error "neither int nor long are four bytes long?!?" -#endif - -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#error "Wut? Pointers are neither 4 nor 8 bytes long?" -#endif - -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) - -static inline size_t ALIGN_TO(size_t l, size_t ali) { - return ((l + ali - 1) & ~(ali - 1)); -} - -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) - -/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ -static inline unsigned long ALIGN_POWER2(unsigned long u) { - /* clz(0) is undefined */ - if (u == 1) - return 1; - - /* left-shift overflow is undefined */ - if (__builtin_clzl(u - 1UL) < 1) - return 0; - - return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); -} - -#define ELEMENTSOF(x) \ - __extension__ (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - (void)0)) -/* - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - */ -#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) -#define __container_of(uniq, ptr, type, member) \ - __extension__ ({ \ - const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ - (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \ - }) - -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MAX(_A, _B) \ - __extension__ (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) > (_B)) ? (_A) : (_B), \ - (void)0)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x,y,z) \ - __extension__ ({ \ - const typeof(x) _c = MAX(x,y); \ - MAX(_c, z); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ - }) - -#define MIN3(x,y,z) \ - __extension__ ({ \ - const typeof(x) _c = MIN(x,y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - __extension__ ({ \ - const typeof(x) UNIQ_T(X,xq) = (x); \ - const typeof(low) UNIQ_T(LOW,lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH,highq) = (high); \ - UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \ - UNIQ_T(HIGH,highq) : \ - UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \ - UNIQ_T(LOW,lowq) : \ - UNIQ_T(X,xq); \ - }) - -/* [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the - * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(_x, _y) \ - __extension__ ({ \ - const typeof(_x) __x = (_x); \ - const typeof(_y) __y = (_y); \ - (__x / __y + !!(__x % __y)); \ - }) - -#define assert_message_se(expr, message) \ - do { \ - if (_unlikely_(!(expr))) \ - log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#define assert_se(expr) assert_message_se(expr, #expr) - -/* We override the glibc assert() here. */ -#undef assert -#ifdef NDEBUG -#define assert(expr) do {} while (false) -#else -#define assert(expr) assert_message_se(expr, #expr) -#endif - -#define assert_not_reached(t) \ - do { \ - log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING -#else -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING -#endif - -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) - -#define assert_return(expr, r) \ - do { \ - if (!assert_log(expr, #expr)) \ - return (r); \ - } while (false) - -#define assert_return_errno(expr, r, err) \ - do { \ - if (!assert_log(expr, #expr)) { \ - errno = err; \ - return (r); \ - } \ - } while (false) - -#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) -#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) -#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) -#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) -#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) -#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) -#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) -#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) - -#define char_array_0(x) x[sizeof(x)-1] = 0; - -/* 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 - * types). Includes space for the trailing NUL. */ -#define DECIMAL_STR_MAX(type) \ - (2+(sizeof(type) <= 1 ? 3 : \ - sizeof(type) <= 2 ? 5 : \ - 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 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; \ - }) - -#define SWAP_TWO(x, y) do { \ - typeof(x) _t = (x); \ - (x) = (y); \ - (y) = (_t); \ - } while (false) - -/* Define C11 thread_local attribute even on older gcc compiler - * version */ -#ifndef thread_local -/* - * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ - * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 - */ -#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) -#define thread_local _Thread_local -#else -#define thread_local __thread -#endif -#endif - -/* Define C11 noreturn without and even on older gcc - * compiler versions */ -#ifndef noreturn -#if __STDC_VERSION__ >= 201112L -#define noreturn _Noreturn -#else -#define noreturn __attribute__((noreturn)) -#endif -#endif - -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#include "log.h" diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c deleted file mode 100644 index 8c8cc78ebf..0000000000 --- a/src/basic/memfd-util.c +++ /dev/null @@ -1,174 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#ifdef HAVE_LINUX_MEMFD_H -#include -#endif -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing.h" -#include "string-util.h" -#include "utf8.h" - -int memfd_new(const char *name) { - _cleanup_free_ char *g = NULL; - int fd; - - if (!name) { - char pr[17] = {}; - - /* If no name is specified we generate one. We include - * a hint indicating our library implementation, and - * add the thread name to it */ - - assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); - - if (isempty(pr)) - name = "sd"; - else { - _cleanup_free_ char *e = NULL; - - e = utf8_escape_invalid(pr); - if (!e) - return -ENOMEM; - - g = strappend("sd-", e); - if (!g) - return -ENOMEM; - - name = g; - } - } - - fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -int memfd_map(int fd, uint64_t offset, size_t size, void **p) { - void *q; - int sealed; - - assert(fd >= 0); - assert(size > 0); - assert(p); - - sealed = memfd_get_sealed(fd); - if (sealed < 0) - return sealed; - - if (sealed) - q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); - else - q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); - - if (q == MAP_FAILED) - return -errno; - - *p = q; - return 0; -} - -int memfd_set_sealed(int fd) { - int r; - - assert(fd >= 0); - - r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); - if (r < 0) - return -errno; - - return 0; -} - -int memfd_get_sealed(int fd) { - int r; - - assert(fd >= 0); - - r = fcntl(fd, F_GET_SEALS); - if (r < 0) - return -errno; - - return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); -} - -int memfd_get_size(int fd, uint64_t *sz) { - struct stat stat; - int r; - - assert(fd >= 0); - assert(sz); - - r = fstat(fd, &stat); - if (r < 0) - return -errno; - - *sz = stat.st_size; - return 0; -} - -int memfd_set_size(int fd, uint64_t sz) { - int r; - - assert(fd >= 0); - - r = ftruncate(fd, sz); - if (r < 0) - return -errno; - - return 0; -} - -int memfd_new_and_map(const char *name, size_t sz, void **p) { - _cleanup_close_ int fd = -1; - int r; - - assert(sz > 0); - assert(p); - - fd = memfd_new(name); - if (fd < 0) - return fd; - - r = memfd_set_size(fd, sz); - if (r < 0) - return r; - - r = memfd_map(fd, 0, sz, p); - if (r < 0) - return r; - - r = fd; - fd = -1; - - return r; -} diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h deleted file mode 100644 index 46d4989e4c..0000000000 --- a/src/basic/memfd-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -int memfd_new(const char *name); -int memfd_new_and_map(const char *name, size_t sz, void **p); - -int memfd_map(int fd, uint64_t offset, size_t size, void **p); - -int memfd_set_sealed(int fd); -int memfd_get_sealed(int fd); - -int memfd_get_size(int fd, uint64_t *sz); -int memfd_set_size(int fd, uint64_t sz); diff --git a/src/basic/mempool.c b/src/basic/mempool.c deleted file mode 100644 index f95e2beb0f..0000000000 --- a/src/basic/mempool.c +++ /dev/null @@ -1,104 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2014 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 . -***/ - -#include -#include - -#include "macro.h" -#include "mempool.h" -#include "util.h" - -struct pool { - struct pool *next; - unsigned n_tiles; - unsigned n_used; -}; - -void* mempool_alloc_tile(struct mempool *mp) { - unsigned i; - - /* When a tile is released we add it to the list and simply - * place the next pointer at its offset 0. */ - - assert(mp->tile_size >= sizeof(void*)); - assert(mp->at_least > 0); - - if (mp->freelist) { - void *r; - - r = mp->freelist; - mp->freelist = * (void**) mp->freelist; - return r; - } - - if (_unlikely_(!mp->first_pool) || - _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - unsigned n; - size_t size; - struct pool *p; - - n = mp->first_pool ? mp->first_pool->n_tiles : 0; - n = MAX(mp->at_least, n * 2); - size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); - n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; - - p = malloc(size); - if (!p) - return NULL; - - p->next = mp->first_pool; - p->n_tiles = n; - p->n_used = 0; - - mp->first_pool = p; - } - - i = mp->first_pool->n_used++; - - return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; -} - -void* mempool_alloc0_tile(struct mempool *mp) { - void *p; - - p = mempool_alloc_tile(mp); - if (p) - memzero(p, mp->tile_size); - return p; -} - -void mempool_free_tile(struct mempool *mp, void *p) { - * (void**) p = mp->freelist; - mp->freelist = p; -} - -#ifdef VALGRIND - -void mempool_drop(struct mempool *mp) { - struct pool *p = mp->first_pool; - while (p) { - struct pool *n; - n = p->next; - free(p); - p = n; - } -} - -#endif diff --git a/src/basic/mempool.h b/src/basic/mempool.h deleted file mode 100644 index 0618b8dd22..0000000000 --- a/src/basic/mempool.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011-2014 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 . -***/ - -#include - -struct pool; - -struct mempool { - struct pool *first_pool; - void *freelist; - size_t tile_size; - unsigned at_least; -}; - -void* mempool_alloc_tile(struct mempool *mp); -void* mempool_alloc0_tile(struct mempool *mp); -void mempool_free_tile(struct mempool *mp, void *p); - -#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -static struct mempool pool_name = { \ - .tile_size = sizeof(tile_type), \ - .at_least = alloc_at_least, \ -} - - -#ifdef VALGRIND -void mempool_drop(struct mempool *mp); -#endif diff --git a/src/basic/missing.h b/src/basic/missing.h deleted file mode 100644 index b1272f8799..0000000000 --- a/src/basic/missing.h +++ /dev/null @@ -1,1044 +0,0 @@ -#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 . -***/ - -/* Missing glibc definitions to access certain kernel APIs */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_AUDIT -#include -#endif - -#ifdef ARCH_MIPS -#include -#endif - -#ifdef HAVE_LINUX_BTRFS_H -#include -#endif - -#include "macro.h" - -#ifndef RLIMIT_RTTIME -#define RLIMIT_RTTIME 15 -#endif - -/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */ -#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS) - -#ifndef F_LINUX_SPECIFIC_BASE -#define F_LINUX_SPECIFIC_BASE 1024 -#endif - -#ifndef F_SETPIPE_SZ -#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) -#endif - -#ifndef F_GETPIPE_SZ -#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) -#endif - -#ifndef F_ADD_SEALS -#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) - -#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ -#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ -#define F_SEAL_GROW 0x0004 /* prevent file from growing */ -#define F_SEAL_WRITE 0x0008 /* prevent writes */ -#endif - -#ifndef F_OFD_GETLK -#define F_OFD_GETLK 36 -#define F_OFD_SETLK 37 -#define F_OFD_SETLKW 38 -#endif - -#ifndef MFD_ALLOW_SEALING -#define MFD_ALLOW_SEALING 0x0002U -#endif - -#ifndef MFD_CLOEXEC -#define MFD_CLOEXEC 0x0001U -#endif - -#ifndef IP_FREEBIND -#define IP_FREEBIND 15 -#endif - -#ifndef OOM_SCORE_ADJ_MIN -#define OOM_SCORE_ADJ_MIN (-1000) -#endif - -#ifndef OOM_SCORE_ADJ_MAX -#define OOM_SCORE_ADJ_MAX 1000 -#endif - -#ifndef AUDIT_SERVICE_START -#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ -#endif - -#ifndef AUDIT_SERVICE_STOP -#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ -#endif - -#ifndef TIOCVHANGUP -#define TIOCVHANGUP 0x5437 -#endif - -#ifndef IP_TRANSPARENT -#define IP_TRANSPARENT 19 -#endif - -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -#ifndef NETLINK_LIST_MEMBERSHIPS -#define NETLINK_LIST_MEMBERSHIPS 9 -#endif - -#ifndef SOL_SCTP -#define SOL_SCTP 132 -#endif - -#ifndef GRND_NONBLOCK -#define GRND_NONBLOCK 0x0001 -#endif - -#ifndef GRND_RANDOM -#define GRND_RANDOM 0x0002 -#endif - -#ifndef BTRFS_IOCTL_MAGIC -#define BTRFS_IOCTL_MAGIC 0x94 -#endif - -#ifndef BTRFS_PATH_NAME_MAX -#define BTRFS_PATH_NAME_MAX 4087 -#endif - -#ifndef BTRFS_DEVICE_PATH_NAME_MAX -#define BTRFS_DEVICE_PATH_NAME_MAX 1024 -#endif - -#ifndef BTRFS_FSID_SIZE -#define BTRFS_FSID_SIZE 16 -#endif - -#ifndef BTRFS_UUID_SIZE -#define BTRFS_UUID_SIZE 16 -#endif - -#ifndef BTRFS_SUBVOL_RDONLY -#define BTRFS_SUBVOL_RDONLY (1ULL << 1) -#endif - -#ifndef BTRFS_SUBVOL_NAME_MAX -#define BTRFS_SUBVOL_NAME_MAX 4039 -#endif - -#ifndef BTRFS_INO_LOOKUP_PATH_MAX -#define BTRFS_INO_LOOKUP_PATH_MAX 4080 -#endif - -#ifndef BTRFS_SEARCH_ARGS_BUFSIZE -#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) -#endif - -#ifndef BTRFS_QGROUP_LEVEL_SHIFT -#define BTRFS_QGROUP_LEVEL_SHIFT 48 -#endif - -#ifndef HAVE_LINUX_BTRFS_H -struct btrfs_ioctl_vol_args { - int64_t fd; - char name[BTRFS_PATH_NAME_MAX + 1]; -}; - -struct btrfs_qgroup_limit { - __u64 flags; - __u64 max_rfer; - __u64 max_excl; - __u64 rsv_rfer; - __u64 rsv_excl; -}; - -struct btrfs_qgroup_inherit { - __u64 flags; - __u64 num_qgroups; - __u64 num_ref_copies; - __u64 num_excl_copies; - struct btrfs_qgroup_limit lim; - __u64 qgroups[0]; -}; - -struct btrfs_ioctl_qgroup_limit_args { - __u64 qgroupid; - struct btrfs_qgroup_limit lim; -}; - -struct btrfs_ioctl_vol_args_v2 { - __s64 fd; - __u64 transid; - __u64 flags; - union { - struct { - __u64 size; - struct btrfs_qgroup_inherit *qgroup_inherit; - }; - __u64 unused[4]; - }; - char name[BTRFS_SUBVOL_NAME_MAX + 1]; -}; - -struct btrfs_ioctl_dev_info_args { - uint64_t devid; /* in/out */ - uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */ - uint64_t bytes_used; /* out */ - uint64_t total_bytes; /* out */ - uint64_t unused[379]; /* pad to 4k */ - char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ -}; - -struct btrfs_ioctl_fs_info_args { - uint64_t max_id; /* out */ - uint64_t num_devices; /* out */ - uint8_t fsid[BTRFS_FSID_SIZE]; /* out */ - uint64_t reserved[124]; /* pad to 1k */ -}; - -struct btrfs_ioctl_ino_lookup_args { - __u64 treeid; - __u64 objectid; - char name[BTRFS_INO_LOOKUP_PATH_MAX]; -}; - -struct btrfs_ioctl_search_key { - /* which root are we searching. 0 is the tree of tree roots */ - __u64 tree_id; - - /* keys returned will be >= min and <= max */ - __u64 min_objectid; - __u64 max_objectid; - - /* keys returned will be >= min and <= max */ - __u64 min_offset; - __u64 max_offset; - - /* max and min transids to search for */ - __u64 min_transid; - __u64 max_transid; - - /* keys returned will be >= min and <= max */ - __u32 min_type; - __u32 max_type; - - /* - * how many items did userland ask for, and how many are we - * returning - */ - __u32 nr_items; - - /* align to 64 bits */ - __u32 unused; - - /* some extra for later */ - __u64 unused1; - __u64 unused2; - __u64 unused3; - __u64 unused4; -}; - -struct btrfs_ioctl_search_header { - __u64 transid; - __u64 objectid; - __u64 offset; - __u32 type; - __u32 len; -}; - - -struct btrfs_ioctl_search_args { - struct btrfs_ioctl_search_key key; - char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; -}; - -struct btrfs_ioctl_clone_range_args { - __s64 src_fd; - __u64 src_offset, src_length; - __u64 dest_offset; -}; - -#define BTRFS_QUOTA_CTL_ENABLE 1 -#define BTRFS_QUOTA_CTL_DISABLE 2 -#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 -struct btrfs_ioctl_quota_ctl_args { - __u64 cmd; - __u64 status; -}; -#endif - -#ifndef BTRFS_IOC_DEFRAG -#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_RESIZE -#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_CLONE -#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) -#endif - -#ifndef BTRFS_IOC_CLONE_RANGE -#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \ - struct btrfs_ioctl_clone_range_args) -#endif - -#ifndef BTRFS_IOC_SUBVOL_CREATE -#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_SNAP_DESTROY -#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_TREE_SEARCH -#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ - struct btrfs_ioctl_search_args) -#endif - -#ifndef BTRFS_IOC_INO_LOOKUP -#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ - struct btrfs_ioctl_ino_lookup_args) -#endif - -#ifndef BTRFS_IOC_SNAP_CREATE_V2 -#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ - struct btrfs_ioctl_vol_args_v2) -#endif - -#ifndef BTRFS_IOC_SUBVOL_GETFLAGS -#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64) -#endif - -#ifndef BTRFS_IOC_SUBVOL_SETFLAGS -#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64) -#endif - -#ifndef BTRFS_IOC_DEV_INFO -#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \ - struct btrfs_ioctl_dev_info_args) -#endif - -#ifndef BTRFS_IOC_FS_INFO -#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ - struct btrfs_ioctl_fs_info_args) -#endif - -#ifndef BTRFS_IOC_DEVICES_READY -#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_QUOTA_CTL -#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \ - struct btrfs_ioctl_quota_ctl_args) -#endif - -#ifndef BTRFS_IOC_QGROUP_LIMIT -#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \ - struct btrfs_ioctl_qgroup_limit_args) -#endif - -#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT -#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46) -#endif - -#ifndef BTRFS_FIRST_FREE_OBJECTID -#define BTRFS_FIRST_FREE_OBJECTID 256 -#endif - -#ifndef BTRFS_LAST_FREE_OBJECTID -#define BTRFS_LAST_FREE_OBJECTID -256ULL -#endif - -#ifndef BTRFS_ROOT_TREE_OBJECTID -#define BTRFS_ROOT_TREE_OBJECTID 1 -#endif - -#ifndef BTRFS_QUOTA_TREE_OBJECTID -#define BTRFS_QUOTA_TREE_OBJECTID 8ULL -#endif - -#ifndef BTRFS_ROOT_ITEM_KEY -#define BTRFS_ROOT_ITEM_KEY 132 -#endif - -#ifndef BTRFS_QGROUP_STATUS_KEY -#define BTRFS_QGROUP_STATUS_KEY 240 -#endif - -#ifndef BTRFS_QGROUP_INFO_KEY -#define BTRFS_QGROUP_INFO_KEY 242 -#endif - -#ifndef BTRFS_QGROUP_LIMIT_KEY -#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 - -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -#ifndef CGROUP_SUPER_MAGIC -#define CGROUP_SUPER_MAGIC 0x27e0eb -#endif - -#ifndef CGROUP2_SUPER_MAGIC -#define CGROUP2_SUPER_MAGIC 0x63677270 -#endif - -#ifndef TMPFS_MAGIC -#define TMPFS_MAGIC 0x01021994 -#endif - -#ifndef MQUEUE_MAGIC -#define MQUEUE_MAGIC 0x19800202 -#endif - -#ifndef SECURITYFS_MAGIC -#define SECURITYFS_MAGIC 0x73636673 -#endif - -#ifndef TRACEFS_MAGIC -#define TRACEFS_MAGIC 0x74726163 -#endif - -#ifndef BPF_FS_MAGIC -#define BPF_FS_MAGIC 0xcafe4a11 -#endif - -#ifndef MS_MOVE -#define MS_MOVE 8192 -#endif - -#ifndef MS_PRIVATE -#define MS_PRIVATE (1 << 18) -#endif - -#ifndef SCM_SECURITY -#define SCM_SECURITY 0x03 -#endif - -#ifndef MS_STRICTATIME -#define MS_STRICTATIME (1<<24) -#endif - -#ifndef MS_REC -#define MS_REC 16384 -#endif - -#ifndef MS_SHARED -#define MS_SHARED (1<<20) -#endif - -#ifndef PR_SET_NO_NEW_PRIVS -#define PR_SET_NO_NEW_PRIVS 38 -#endif - -#ifndef PR_SET_CHILD_SUBREAPER -#define PR_SET_CHILD_SUBREAPER 36 -#endif - -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -#ifndef HAVE_SECURE_GETENV -# ifdef HAVE___SECURE_GETENV -# define secure_getenv __secure_getenv -# else -# error "neither secure_getenv nor __secure_getenv are available" -# endif -#endif - -#ifndef CIFS_MAGIC_NUMBER -# define CIFS_MAGIC_NUMBER 0xFF534D42 -#endif - -#ifndef TFD_TIMER_CANCEL_ON_SET -# define TFD_TIMER_CANCEL_ON_SET (1 << 1) -#endif - -#ifndef SO_REUSEPORT -# define SO_REUSEPORT 15 -#endif - -#ifndef EVIOCREVOKE -# define EVIOCREVOKE _IOW('E', 0x91, int) -#endif - -#ifndef DRM_IOCTL_SET_MASTER -# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) -#endif - -#ifndef DRM_IOCTL_DROP_MASTER -# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) -#endif - -#if defined(__i386__) || defined(__x86_64__) - -/* The precise definition of __O_TMPFILE is arch specific, so let's - * just define this on x86 where we know the value. */ - -#ifndef __O_TMPFILE -#define __O_TMPFILE 020000000 -#endif - -/* a horrid kludge trying to make sure that this will fail on old kernels */ -#ifndef O_TMPFILE -#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) -#endif - -#endif - -#if !HAVE_DECL_LO_FLAGS_PARTSCAN -#define LO_FLAGS_PARTSCAN 8 -#endif - -#ifndef LOOP_CTL_REMOVE -#define LOOP_CTL_REMOVE 0x4C81 -#endif - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - -#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE -#define IFLA_INET6_UNSPEC 0 -#define IFLA_INET6_FLAGS 1 -#define IFLA_INET6_CONF 2 -#define IFLA_INET6_STATS 3 -#define IFLA_INET6_MCAST 4 -#define IFLA_INET6_CACHEINFO 5 -#define IFLA_INET6_ICMP6STATS 6 -#define IFLA_INET6_TOKEN 7 -#define IFLA_INET6_ADDR_GEN_MODE 8 -#define __IFLA_INET6_MAX 9 - -#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) - -#define IN6_ADDR_GEN_MODE_EUI64 0 -#define IN6_ADDR_GEN_MODE_NONE 1 -#endif - -#if !HAVE_DECL_IN6_ADDR_GEN_MODE_STABLE_PRIVACY -#define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 -#endif - -#if !HAVE_DECL_IFLA_MACVLAN_FLAGS -#define IFLA_MACVLAN_UNSPEC 0 -#define IFLA_MACVLAN_MODE 1 -#define IFLA_MACVLAN_FLAGS 2 -#define __IFLA_MACVLAN_MAX 3 - -#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_IPVLAN_MODE -#define IFLA_IPVLAN_UNSPEC 0 -#define IFLA_IPVLAN_MODE 1 -#define __IFLA_IPVLAN_MAX 2 - -#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) - -#define IPVLAN_MODE_L2 0 -#define IPVLAN_MODE_L3 1 -#define IPVLAN_MAX 2 -#endif - -#if !HAVE_DECL_IFLA_VTI_REMOTE -#define IFLA_VTI_UNSPEC 0 -#define IFLA_VTI_LINK 1 -#define IFLA_VTI_IKEY 2 -#define IFLA_VTI_OKEY 3 -#define IFLA_VTI_LOCAL 4 -#define IFLA_VTI_REMOTE 5 -#define __IFLA_VTI_MAX 6 - -#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_PHYS_PORT_ID -#define IFLA_EXT_MASK 29 -#undef IFLA_PROMISCUITY -#define IFLA_PROMISCUITY 30 -#define IFLA_NUM_TX_QUEUES 31 -#define IFLA_NUM_RX_QUEUES 32 -#define IFLA_CARRIER 33 -#define IFLA_PHYS_PORT_ID 34 -#define __IFLA_MAX 35 - -#define IFLA_MAX (__IFLA_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BOND_AD_INFO -#define IFLA_BOND_UNSPEC 0 -#define IFLA_BOND_MODE 1 -#define IFLA_BOND_ACTIVE_SLAVE 2 -#define IFLA_BOND_MIIMON 3 -#define IFLA_BOND_UPDELAY 4 -#define IFLA_BOND_DOWNDELAY 5 -#define IFLA_BOND_USE_CARRIER 6 -#define IFLA_BOND_ARP_INTERVAL 7 -#define IFLA_BOND_ARP_IP_TARGET 8 -#define IFLA_BOND_ARP_VALIDATE 9 -#define IFLA_BOND_ARP_ALL_TARGETS 10 -#define IFLA_BOND_PRIMARY 11 -#define IFLA_BOND_PRIMARY_RESELECT 12 -#define IFLA_BOND_FAIL_OVER_MAC 13 -#define IFLA_BOND_XMIT_HASH_POLICY 14 -#define IFLA_BOND_RESEND_IGMP 15 -#define IFLA_BOND_NUM_PEER_NOTIF 16 -#define IFLA_BOND_ALL_SLAVES_ACTIVE 17 -#define IFLA_BOND_MIN_LINKS 18 -#define IFLA_BOND_LP_INTERVAL 19 -#define IFLA_BOND_PACKETS_PER_SLAVE 20 -#define IFLA_BOND_AD_LACP_RATE 21 -#define IFLA_BOND_AD_SELECT 22 -#define IFLA_BOND_AD_INFO 23 -#define __IFLA_BOND_MAX 24 - -#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_VLAN_PROTOCOL -#define IFLA_VLAN_UNSPEC 0 -#define IFLA_VLAN_ID 1 -#define IFLA_VLAN_FLAGS 2 -#define IFLA_VLAN_EGRESS_QOS 3 -#define IFLA_VLAN_INGRESS_QOS 4 -#define IFLA_VLAN_PROTOCOL 5 -#define __IFLA_VLAN_MAX 6 - -#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 -#define IFLA_VXLAN_PORT_RANGE 10 -#define IFLA_VXLAN_PROXY 11 -#define IFLA_VXLAN_RSC 12 -#define IFLA_VXLAN_L2MISS 13 -#define IFLA_VXLAN_L3MISS 14 -#define IFLA_VXLAN_PORT 15 -#define IFLA_VXLAN_GROUP6 16 -#define IFLA_VXLAN_LOCAL6 17 -#define IFLA_VXLAN_UDP_CSUM 18 -#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19 -#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20 -#define IFLA_VXLAN_REMCSUM_TX 21 -#define IFLA_VXLAN_REMCSUM_RX 22 -#define IFLA_VXLAN_GBP 23 -#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24 -#define __IFLA_VXLAN_MAX 25 - -#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT -#define IFLA_IPTUN_UNSPEC 0 -#define IFLA_IPTUN_LINK 1 -#define IFLA_IPTUN_LOCAL 2 -#define IFLA_IPTUN_REMOTE 3 -#define IFLA_IPTUN_TTL 4 -#define IFLA_IPTUN_TOS 5 -#define IFLA_IPTUN_ENCAP_LIMIT 6 -#define IFLA_IPTUN_FLOWINFO 7 -#define IFLA_IPTUN_FLAGS 8 -#define IFLA_IPTUN_PROTO 9 -#define IFLA_IPTUN_PMTUDISC 10 -#define IFLA_IPTUN_6RD_PREFIX 11 -#define IFLA_IPTUN_6RD_RELAY_PREFIX 12 -#define IFLA_IPTUN_6RD_PREFIXLEN 13 -#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14 -#define IFLA_IPTUN_ENCAP_TYPE 15 -#define IFLA_IPTUN_ENCAP_FLAGS 16 -#define IFLA_IPTUN_ENCAP_SPORT 17 -#define IFLA_IPTUN_ENCAP_DPORT 18 - -#define __IFLA_IPTUN_MAX 19 - -#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_GRE_ENCAP_DPORT -#define IFLA_GRE_UNSPEC 0 -#define IFLA_GRE_LINK 1 -#define IFLA_GRE_IFLAGS 2 -#define IFLA_GRE_OFLAGS 3 -#define IFLA_GRE_IKEY 4 -#define IFLA_GRE_OKEY 5 -#define IFLA_GRE_LOCAL 6 -#define IFLA_GRE_REMOTE 7 -#define IFLA_GRE_TTL 8 -#define IFLA_GRE_TOS 9 -#define IFLA_GRE_PMTUDISC 10 -#define IFLA_GRE_ENCAP_LIMIT 11 -#define IFLA_GRE_FLOWINFO 12 -#define IFLA_GRE_FLAGS 13 -#define IFLA_GRE_ENCAP_TYPE 14 -#define IFLA_GRE_ENCAP_FLAGS 15 -#define IFLA_GRE_ENCAP_SPORT 16 -#define IFLA_GRE_ENCAP_DPORT 17 - -#define __IFLA_GRE_MAX 18 - -#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO -#define IFLA_BRIDGE_FLAGS 0 -#define IFLA_BRIDGE_MODE 1 -#define IFLA_BRIDGE_VLAN_INFO 2 -#define __IFLA_BRIDGE_MAX 3 - -#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) -#endif - -#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN -#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ -#endif - -#ifndef BRIDGE_VLAN_INFO_RANGE_END -#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ -#endif - -#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID -#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_VLAN_FILTERING 7 -#define IFLA_BR_VLAN_PROTOCOL 8 -#define IFLA_BR_GROUP_FWD_MASK 9 -#define IFLA_BR_ROOT_ID 10 -#define IFLA_BR_BRIDGE_ID 11 -#define IFLA_BR_ROOT_PORT 12 -#define IFLA_BR_ROOT_PATH_COST 13 -#define IFLA_BR_TOPOLOGY_CHANGE 14 -#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15 -#define IFLA_BR_HELLO_TIMER 16 -#define IFLA_BR_TCN_TIMER 17 -#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18 -#define IFLA_BR_GC_TIMER 19 -#define IFLA_BR_GROUP_ADDR 20 -#define IFLA_BR_FDB_FLUSH 21 -#define IFLA_BR_MCAST_ROUTER 22 -#define IFLA_BR_MCAST_SNOOPING 23 -#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24 -#define IFLA_BR_MCAST_QUERIER 25 -#define IFLA_BR_MCAST_HASH_ELASTICITY 26 -#define IFLA_BR_MCAST_HASH_MAX 27 -#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28 -#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29 -#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30 -#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31 -#define IFLA_BR_MCAST_QUERIER_INTVL 32 -#define IFLA_BR_MCAST_QUERY_INTVL 33 -#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34 -#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35 -#define IFLA_BR_NF_CALL_IPTABLES 36 -#define IFLA_BR_NF_CALL_IP6TABLES 37 -#define IFLA_BR_NF_CALL_ARPTABLES 38 -#define IFLA_BR_VLAN_DEFAULT_PVID 39 -#define __IFLA_BR_MAX 40 - -#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 -#define IFLA_BRPORT_PRIORITY 2 -#define IFLA_BRPORT_COST 3 -#define IFLA_BRPORT_MODE 4 -#define IFLA_BRPORT_GUARD 5 -#define IFLA_BRPORT_PROTECT 6 -#define IFLA_BRPORT_FAST_LEAVE 7 -#define IFLA_BRPORT_LEARNING 8 -#define IFLA_BRPORT_UNICAST_FLOOD 9 -#define IFLA_BRPORT_LEARNING_SYNC 11 -#define __IFLA_BRPORT_MAX 12 - -#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BRPORT_PROXYARP -#define IFLA_BRPORT_PROXYARP 10 -#endif - -#if !HAVE_DECL_IFLA_VRF_TABLE -#define IFLA_VRF_TABLE 1 -#endif - -#if !HAVE_DECL_NDA_IFINDEX -#define NDA_UNSPEC 0 -#define NDA_DST 1 -#define NDA_LLADDR 2 -#define NDA_CACHEINFO 3 -#define NDA_PROBES 4 -#define NDA_VLAN 5 -#define NDA_PORT 6 -#define NDA_VNI 7 -#define NDA_IFINDEX 8 -#define __NDA_MAX 9 - -#define NDA_MAX (__NDA_MAX - 1) -#endif - -#ifndef RTA_PREF -#define RTA_PREF 20 -#endif - -#ifndef IPV6_UNICAST_IF -#define IPV6_UNICAST_IF 76 -#endif - -#ifndef IPV6_MIN_MTU -#define IPV6_MIN_MTU 1280 -#endif - -#ifndef IFF_MULTI_QUEUE -#define IFF_MULTI_QUEUE 0x100 -#endif - -#ifndef IFF_LOWER_UP -#define IFF_LOWER_UP 0x10000 -#endif - -#ifndef IFF_DORMANT -#define IFF_DORMANT 0x20000 -#endif - -#ifndef BOND_XMIT_POLICY_ENCAP23 -#define BOND_XMIT_POLICY_ENCAP23 3 -#endif - -#ifndef BOND_XMIT_POLICY_ENCAP34 -#define BOND_XMIT_POLICY_ENCAP34 4 -#endif - -#ifndef NET_ADDR_RANDOM -# define NET_ADDR_RANDOM 1 -#endif - -#ifndef NET_NAME_UNKNOWN -# define NET_NAME_UNKNOWN 0 -#endif - -#ifndef NET_NAME_ENUM -# define NET_NAME_ENUM 1 -#endif - -#ifndef NET_NAME_PREDICTABLE -# define NET_NAME_PREDICTABLE 2 -#endif - -#ifndef NET_NAME_USER -# define NET_NAME_USER 3 -#endif - -#ifndef NET_NAME_RENAMED -# define NET_NAME_RENAMED 4 -#endif - -#ifndef BPF_XOR -# define BPF_XOR 0xa0 -#endif - -/* Note that LOOPBACK_IFINDEX is currently not exported by the - * kernel/glibc, but hardcoded internally by the kernel. However, as - * it is exported to userspace indirectly via rtnetlink and the - * ioctls, and made use of widely we define it here too, in a way that - * is compatible with the kernel's internal definition. */ -#ifndef LOOPBACK_IFINDEX -#define LOOPBACK_IFINDEX 1 -#endif - -#if !HAVE_DECL_IFA_FLAGS -#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 - -#ifndef MAX_AUDIT_MESSAGE_LENGTH -#define MAX_AUDIT_MESSAGE_LENGTH 8970 -#endif - -#ifndef AUDIT_NLGRP_MAX -#define AUDIT_NLGRP_READLOG 1 -#endif - -#ifndef CAP_MAC_OVERRIDE -#define CAP_MAC_OVERRIDE 32 -#endif - -#ifndef CAP_MAC_ADMIN -#define CAP_MAC_ADMIN 33 -#endif - -#ifndef CAP_SYSLOG -#define CAP_SYSLOG 34 -#endif - -#ifndef CAP_WAKE_ALARM -#define CAP_WAKE_ALARM 35 -#endif - -#ifndef CAP_BLOCK_SUSPEND -#define CAP_BLOCK_SUSPEND 36 -#endif - -#ifndef CAP_AUDIT_READ -#define CAP_AUDIT_READ 37 -#endif - -#ifndef RENAME_NOREPLACE -#define RENAME_NOREPLACE (1 << 0) -#endif - -#ifndef KCMP_FILE -#define KCMP_FILE 0 -#endif - -#ifndef INPUT_PROP_POINTING_STICK -#define INPUT_PROP_POINTING_STICK 0x05 -#endif - -#ifndef INPUT_PROP_ACCELEROMETER -#define INPUT_PROP_ACCELEROMETER 0x06 -#endif - -#ifndef HAVE_KEY_SERIAL_T -typedef int32_t key_serial_t; -#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 - -/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway, - * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with - * linux/if.h. */ -#ifndef IF_OPER_UNKNOWN -#define IF_OPER_UNKNOWN 0 -#endif - -#ifndef IF_OPER_UP -#define IF_OPER_UP 6 - -#ifndef HAVE_CHAR32_T -#define char32_t uint32_t -#endif - -#ifndef HAVE_CHAR16_T -#define char16_t uint16_t -#endif - -#ifndef ETHERTYPE_LLDP -#define ETHERTYPE_LLDP 0x88cc -#endif - -#endif - -#include "missing_syscall.h" diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h deleted file mode 100644 index e6fd67cb9d..0000000000 --- a/src/basic/missing_syscall.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2016 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 . -***/ - -/* Missing glibc definitions to access certain kernel APIs */ - -#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); -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_MEMFD_CREATE -# ifndef __NR_memfd_create -# if defined __x86_64__ -# define __NR_memfd_create 319 -# elif defined __arm__ -# define __NR_memfd_create 385 -# elif defined __aarch64__ -# define __NR_memfd_create 279 -# elif defined __s390__ -# define __NR_memfd_create 350 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_memfd_create 4354 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_memfd_create 6318 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_memfd_create 5314 -# endif -# elif defined __i386__ -# define __NR_memfd_create 356 -# else -# warning "__NR_memfd_create unknown for your architecture" -# endif -# endif - -static inline int memfd_create(const char *name, unsigned int flags) { -# ifdef __NR_memfd_create - return syscall(__NR_memfd_create, name, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_GETRANDOM -# ifndef __NR_getrandom -# if defined __x86_64__ -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__arm__) -# define __NR_getrandom 384 -# elif defined(__aarch64__) -# define __NR_getrandom 278 -# elif defined(__ia64__) -# define __NR_getrandom 1339 -# elif defined(__m68k__) -# define __NR_getrandom 352 -# elif defined(__s390x__) -# define __NR_getrandom 349 -# elif defined(__powerpc__) -# define __NR_getrandom 359 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_getrandom 4353 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_getrandom 6317 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_getrandom 5313 -# endif -# else -# warning "__NR_getrandom unknown for your architecture" -# endif -# endif - -static inline int getrandom(void *buffer, size_t count, unsigned flags) { -# ifdef __NR_getrandom - return syscall(__NR_getrandom, buffer, count, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_GETTID -static inline pid_t gettid(void) { - return (pid_t) syscall(SYS_gettid); -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_NAME_TO_HANDLE_AT -# ifndef __NR_name_to_handle_at -# if defined(__x86_64__) -# define __NR_name_to_handle_at 303 -# elif defined(__i386__) -# define __NR_name_to_handle_at 341 -# elif defined(__arm__) -# define __NR_name_to_handle_at 370 -# elif defined(__powerpc__) -# define __NR_name_to_handle_at 345 -# else -# error "__NR_name_to_handle_at is not defined" -# endif -# endif - -struct file_handle { - unsigned int handle_bytes; - int handle_type; - unsigned char f_handle[0]; -}; - -static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { -# ifdef __NR_name_to_handle_at - return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_SETNS -# ifndef __NR_setns -# if defined(__x86_64__) -# define __NR_setns 308 -# elif defined(__i386__) -# define __NR_setns 346 -# else -# error "__NR_setns is not defined" -# endif -# endif - -static inline int setns(int fd, int nstype) { -# ifdef __NR_setns - return syscall(__NR_setns, fd, nstype); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -static inline pid_t raw_getpid(void) { -#if defined(__alpha__) - return (pid_t) syscall(__NR_getxpid); -#else - return (pid_t) syscall(__NR_getpid); -#endif -} - -/* ======================================================================= */ - -#if !HAVE_DECL_RENAMEAT2 -# ifndef __NR_renameat2 -# if defined __x86_64__ -# define __NR_renameat2 316 -# elif defined __arm__ -# define __NR_renameat2 382 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_renameat2 4351 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_renameat2 6315 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_renameat2 5311 -# endif -# elif defined __i386__ -# define __NR_renameat2 353 -# else -# warning "__NR_renameat2 unknown for your architecture" -# endif -# endif - -static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { -# ifdef __NR_renameat2 - return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_KCMP -static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { -# ifdef __NR_kcmp - return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_KEYCTL -static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { -# ifdef __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) { -# ifdef __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) { -# ifdef __NR_request_key - return syscall(__NR_request_key, type, description, callout_info, destringid); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_COPY_FILE_RANGE -# ifndef __NR_copy_file_range -# if defined(__x86_64__) -# define __NR_copy_file_range 326 -# elif defined(__i386__) -# define __NR_copy_file_range 377 -# elif defined __s390__ -# define __NR_copy_file_range 375 -# elif defined __arm__ -# define __NR_copy_file_range 391 -# elif defined __aarch64__ -# define __NR_copy_file_range 285 -# elif defined __powerpc__ -# define __NR_copy_file_range 379 -# else -# warning "__NR_copy_file_range not defined for your architecture" -# endif -# endif - -static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { -# ifdef __NR_copy_file_range - return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c deleted file mode 100644 index aa6878cdf0..0000000000 --- a/src/basic/mkdir-label.c +++ /dev/null @@ -1,38 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "label.h" -#include "mkdir.h" - -int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) { - return mkdir_safe_internal(path, mode, uid, gid, mkdir_label); -} - -int mkdir_parents_label(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir_label); -} - -int mkdir_p_label(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir_label); -} diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c deleted file mode 100644 index 6b1a98402c..0000000000 --- a/src/basic/mkdir.c +++ /dev/null @@ -1,128 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "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; - - if (_mkdir(path, mode) >= 0) - if (chmod_and_chown(path, mode, uid, gid) < 0) - return -errno; - - if (lstat(path, &st) < 0) - return -errno; - - if ((st.st_mode & 0007) > (mode & 0007) || - (st.st_mode & 0070) > (mode & 0070) || - (st.st_mode & 0700) > (mode & 0700) || - (uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid) || - !S_ISDIR(st.st_mode)) - return -EEXIST; - - return 0; -} - -int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) { - return mkdir_safe_internal(path, mode, uid, gid, mkdir); -} - -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { - const char *p, *e; - int r; - - assert(path); - - if (prefix && !path_startswith(path, prefix)) - return -ENOTDIR; - - /* return immediately if directory exists */ - e = strrchr(path, '/'); - if (!e) - return -EINVAL; - - if (e == path) - return 0; - - p = strndupa(path, e - path); - r = is_dir(p, true); - if (r > 0) - return 0; - if (r == 0) - return -ENOTDIR; - - /* create every parent directory in the path, except the last component */ - p = path + strspn(path, "/"); - for (;;) { - char t[strlen(path) + 1]; - - e = p + strcspn(p, "/"); - p = e + strspn(e, "/"); - - /* Is this the last component? If so, then we're - * done */ - if (*p == 0) - return 0; - - memcpy(t, path, e - path); - t[e-path] = 0; - - if (prefix && path_startswith(prefix, t)) - continue; - - r = _mkdir(t, mode); - if (r < 0 && errno != EEXIST) - return -errno; - } -} - -int mkdir_parents(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir); -} - -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { - int r; - - /* Like mkdir -p */ - - r = mkdir_parents_internal(prefix, path, mode, _mkdir); - if (r < 0) - return r; - - r = _mkdir(path, mode); - if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) - return -errno; - - return 0; -} - -int mkdir_p(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir); -} diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h deleted file mode 100644 index d564a3547f..0000000000 --- a/src/basic/mkdir.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid); -int mkdir_parents(const char *path, mode_t mode); -int mkdir_p(const char *path, mode_t mode); - -/* mandatory access control(MAC) versions */ -int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid); -int mkdir_parents_label(const char *path, mode_t mode); -int mkdir_p_label(const char *path, mode_t mode); - -/* internally used */ -typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); -int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir); -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c deleted file mode 100644 index 28dc778969..0000000000 --- a/src/basic/mount-util.c +++ /dev/null @@ -1,559 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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_CLOEXEC|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/. 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 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_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; - - /* Deal with mount points that are obstructed by a - * later mount */ - r = path_is_mount_point(x, 0); - if (r == -ENOENT || r == 0) - continue; - if (r < 0) - return r; - - /* Try to reuse the original flag set */ - 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) - 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" - "pvfs2\0" /* OrangeFS */ - "ocfs2\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; - } -} - -const char* mode_to_inaccessible_node(mode_t mode) { - /* This function maps a node type to the correspondent inaccessible node type. - * Character and block inaccessible devices may not be created (because major=0 and minor=0), - * in such case we map character and block devices to the inaccessible node type socket. */ - switch(mode & S_IFMT) { - case S_IFREG: - return "/run/systemd/inaccessible/reg"; - case S_IFDIR: - return "/run/systemd/inaccessible/dir"; - case S_IFCHR: - if (access("/run/systemd/inaccessible/chr", F_OK) == 0) - return "/run/systemd/inaccessible/chr"; - return "/run/systemd/inaccessible/sock"; - case S_IFBLK: - if (access("/run/systemd/inaccessible/blk", F_OK) == 0) - return "/run/systemd/inaccessible/blk"; - return "/run/systemd/inaccessible/sock"; - case S_IFIFO: - return "/run/systemd/inaccessible/fifo"; - case S_IFSOCK: - return "/run/systemd/inaccessible/sock"; - } - return NULL; -} diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h deleted file mode 100644 index f46989ebb3..0000000000 --- a/src/basic/mount-util.h +++ /dev/null @@ -1,54 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include - -#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]; -}; - -const char* mode_to_inaccessible_node(mode_t mode); - -#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ } diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h deleted file mode 100644 index e7844fff96..0000000000 --- a/src/basic/nss-util.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH - -#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ -enum nss_status _nss_##module##_gethostbyname4_r( \ - const char *name, \ - struct gaih_addrtuple **pat, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp) _public_; \ -enum nss_status _nss_##module##_gethostbyname3_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp, \ - char **canonp) _public_; \ -enum nss_status _nss_##module##_gethostbyname2_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_; \ -enum nss_status _nss_##module##_gethostbyname_r( \ - const char *name, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_ - -#define NSS_GETHOSTBYADDR_PROTOTYPES(module) \ -enum nss_status _nss_##module##_gethostbyaddr2_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp) _public_; \ -enum nss_status _nss_##module##_gethostbyaddr_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_ - -#define NSS_GETHOSTBYNAME_FALLBACKS(module) \ -enum nss_status _nss_##module##_gethostbyname2_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - return _nss_##module##_gethostbyname3_r( \ - name, \ - af, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ -} \ -enum nss_status _nss_##module##_gethostbyname_r( \ - const char *name, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - enum nss_status ret = NSS_STATUS_NOTFOUND; \ - \ - if (_res.options & RES_USE_INET6) \ - ret = _nss_##module##_gethostbyname3_r( \ - name, \ - AF_INET6, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ - if (ret == NSS_STATUS_NOTFOUND) \ - ret = _nss_##module##_gethostbyname3_r( \ - name, \ - AF_INET, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ - return ret; \ -} \ -struct __useless_struct_to_allow_trailing_semicolon__ - -#define NSS_GETHOSTBYADDR_FALLBACKS(module) \ -enum nss_status _nss_##module##_gethostbyaddr_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - return _nss_##module##_gethostbyaddr2_r( \ - addr, len, \ - af, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL); \ -} \ -struct __useless_struct_to_allow_trailing_semicolon__ - -#define NSS_GETPW_PROTOTYPES(module) \ -enum nss_status _nss_##module##_getpwnam_r( \ - const char *name, \ - struct passwd *pwd, \ - char *buffer, size_t buflen, \ - int *errnop) _public_; \ -enum nss_status _nss_##module##_getpwuid_r( \ - uid_t uid, \ - struct passwd *pwd, \ - char *buffer, size_t buflen, \ - int *errnop) _public_ - -#define NSS_GETGR_PROTOTYPES(module) \ -enum nss_status _nss_##module##_getgrnam_r( \ - const char *name, \ - struct group *gr, \ - char *buffer, size_t buflen, \ - int *errnop) _public_; \ -enum nss_status _nss_##module##_getgrgid_r( \ - gid_t gid, \ - struct group *gr, \ - char *buffer, size_t buflen, \ - int *errnop) _public_ - -typedef enum nss_status (*_nss_gethostbyname4_r_t)( - const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); - -typedef enum nss_status (*_nss_gethostbyname3_r_t)( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp); - -typedef enum nss_status (*_nss_gethostbyname2_r_t)( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); - -typedef enum nss_status (*_nss_gethostbyname_r_t)( - const char *name, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); - -typedef enum nss_status (*_nss_gethostbyaddr2_r_t)( - const void* addr, socklen_t len, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); -typedef enum nss_status (*_nss_gethostbyaddr_r_t)( - const void* addr, socklen_t len, - int af, - struct hostent *host, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c deleted file mode 100644 index 2e0bdf6488..0000000000 --- a/src/basic/ordered-set.c +++ /dev/null @@ -1,64 +0,0 @@ -/*** - 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 . -***/ - -#include "ordered-set.h" -#include "strv.h" - -int ordered_set_consume(OrderedSet *s, void *p) { - int r; - - r = ordered_set_put(s, p); - if (r <= 0) - free(p); - - return r; -} - -int ordered_set_put_strdup(OrderedSet *s, const char *p) { - char *c; - int r; - - assert(s); - assert(p); - - c = strdup(p); - if (!c) - return -ENOMEM; - - r = ordered_set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; -} - -int ordered_set_put_strdupv(OrderedSet *s, char **l) { - int n = 0, r; - char **i; - - STRV_FOREACH(i, l) { - r = ordered_set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h deleted file mode 100644 index e1dfc86380..0000000000 --- a/src/basic/ordered-set.h +++ /dev/null @@ -1,74 +0,0 @@ -#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 . -***/ - -#include "hashmap.h" - -typedef struct OrderedSet OrderedSet; - -static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) { - return (OrderedSet*) ordered_hashmap_new(ops); -} - -static inline 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; -} - -static inline OrderedSet* ordered_set_free_free(OrderedSet *s) { - ordered_hashmap_free_free((OrderedHashmap*) s); - return NULL; -} - -static inline int ordered_set_put(OrderedSet *s, void *p) { - return ordered_hashmap_put((OrderedHashmap*) s, p, p); -} - -static inline bool ordered_set_isempty(OrderedSet *s) { - return ordered_hashmap_isempty((OrderedHashmap*) s); -} - -static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value) { - return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL); -} - -int ordered_set_consume(OrderedSet *s, void *p); -int ordered_set_put_strdup(OrderedSet *s, const char *p); -int ordered_set_put_strdupv(OrderedSet *s, char **l); - -#define ORDERED_SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); - -#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep) -#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c deleted file mode 100644 index 503a895731..0000000000 --- a/src/basic/parse-util.c +++ /dev/null @@ -1,553 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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; -} - -int parse_percent(const char *p) { - const char *pc, *n; - unsigned v; - int r; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atou(n, &v); - if (r < 0) - return r; - if (v > 100) - return -ERANGE; - - return (int) v; -} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h deleted file mode 100644 index 73441bb6fd..0000000000 --- a/src/basic/parse-util.h +++ /dev/null @@ -1,109 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#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 - -#if SIZE_MAX == UINT_MAX -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned *) ret_u); -} -#else -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); - return safe_atolu(s, 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); - -int parse_percent(const char *p); diff --git a/src/basic/path-util.c b/src/basic/path-util.c deleted file mode 100644 index b2fa81a294..0000000000 --- a/src/basic/path-util.c +++ /dev/null @@ -1,816 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -/* 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 -#undef basename - -#include "alloc-util.h" -#include "extract-word.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "missing.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] == '/'; -} - -bool is_path(const char *p) { - return !!strchr(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 -ENOMEM; - - r = path_strv_make_absolute_cwd(l); - if (r < 0) { - strv_free(l); - return r; - } - - *ret = l; - return r; -} - -char *path_make_absolute(const char *p, const char *prefix) { - assert(p); - - /* Makes every item in the list an absolute path by prepending - * the prefix, if specified and necessary */ - - if (path_is_absolute(p) || !prefix) - return strdup(p); - - return strjoin(prefix, "/", p, 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)) - c = strdup(p); - else { - _cleanup_free_ char *cwd = NULL; - - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); - - c = strjoin(cwd, "/", p, NULL); - } - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *r, *p; - unsigned n_parents; - - assert(from_dir); - assert(to_path); - assert(_r); - - /* Strips the common part, and adds ".." elements as necessary. */ - - if (!path_is_absolute(from_dir)) - return -EINVAL; - - if (!path_is_absolute(to_path)) - return -EINVAL; - - /* Skip the common part. */ - for (;;) { - size_t a; - size_t b; - - from_dir += strspn(from_dir, "/"); - to_path += strspn(to_path, "/"); - - if (!*from_dir) { - if (!*to_path) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(to_path); - - if (!r) - return -ENOMEM; - - path_kill_slashes(r); - - *_r = r; - return 0; - } - - if (!*to_path) - break; - - a = strcspn(from_dir, "/"); - b = strcspn(to_path, "/"); - - if (a != b) - break; - - if (memcmp(from_dir, to_path, a) != 0) - break; - - from_dir += a; - to_path += b; - } - - /* If we're here, then "from_dir" has one or more elements that need to - * be replaced with "..". */ - - /* Count the number of necessary ".." elements. */ - for (n_parents = 0;;) { - from_dir += strspn(from_dir, "/"); - - if (!*from_dir) - break; - - from_dir += strcspn(from_dir, "/"); - n_parents++; - } - - r = malloc(n_parents * 3 + strlen(to_path) + 1); - if (!r) - return -ENOMEM; - - for (p = r; n_parents > 0; n_parents--, p += 3) - memcpy(p, "../", 3); - - strcpy(p, to_path); - path_kill_slashes(r); - - *_r = r; - return 0; -} - -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 - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t; - - r = path_make_absolute_cwd(*s, &t); - if (r < 0) - return r; - - free(*s); - *s = t; - } - - return 0; -} - -char **path_strv_resolve(char **l, const char *prefix) { - char **s; - unsigned k = 0; - bool enomem = false; - - if (strv_isempty(l)) - return l; - - /* Goes through every item in the string list and canonicalize - * the path. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t, *u; - _cleanup_free_ char *orig = NULL; - - if (!path_is_absolute(*s)) { - free(*s); - continue; - } - - if (prefix) { - orig = *s; - t = strappend(prefix, orig); - if (!t) { - enomem = true; - continue; - } - } else - t = *s; - - errno = 0; - u = canonicalize_file_name(t); - if (!u) { - if (errno == ENOENT) { - if (prefix) { - u = orig; - orig = NULL; - free(t); - } else - u = t; - } else { - free(t); - if (errno == ENOMEM || errno == 0) - enomem = true; - - continue; - } - } else if (prefix) { - char *x; - - free(t); - x = path_startswith(u, prefix); - if (x) { - /* restore the slash if it was lost */ - if (!startswith(x, "/")) - *(--x) = '/'; - - t = strdup(x); - free(u); - if (!t) { - enomem = true; - continue; - } - u = t; - } else { - /* canonicalized path goes outside of - * prefix, keep the original path instead */ - free(u); - u = orig; - orig = NULL; - } - } else - free(t); - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **path_strv_resolve_uniq(char **l, const char *prefix) { - - if (strv_isempty(l)) - return l; - - if (!path_strv_resolve(l, prefix)) - return NULL; - - return strv_uniq(l); -} - -char *path_kill_slashes(char *path) { - char *f, *t; - bool slash = false; - - /* Removes redundant inner and trailing slashes. Modifies the - * passed string in-place. - * - * ///foo///bar/ becomes /foo/bar - */ - - for (f = path, t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - slash = false; - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we are talking of the root directory, a - trailing slash is good */ - - if (t == path && slash) - *(t++) = '/'; - - *t = 0; - return path; -} - -char* path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - if ((path[0] == '/') != (prefix[0] == '/')) - return NULL; - - for (;;) { - size_t a, b; - - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return (char*) path; - - if (*path == 0) - return NULL; - - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); - - if (a != b) - return NULL; - - if (memcmp(path, prefix, a) != 0) - return NULL; - - path += a; - prefix += b; - } -} - -int path_compare(const char *a, const char *b) { - int d; - - assert(a); - assert(b); - - /* A relative path and an abolute path must not compare as equal. - * Which one is sorted before the other does not really matter. - * Here a relative path is ordered before an absolute path. */ - d = (a[0] == '/') - (b[0] == '/'); - if (d != 0) - return d; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return 0; - - /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) - return -1; - if (*b == 0) - return 1; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - 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 != 0) - return d; - - a += j; - b += k; - } -} - -bool path_equal(const char *a, const char *b) { - return path_compare(a, b) == 0; -} - -bool path_equal_or_files_same(const char *a, const char *b) { - return path_equal(a, b) || files_same(a, b) > 0; -} - -char* path_join(const char *root, const char *path, const char *rest) { - assert(path); - - if (!isempty(root)) - return strjoin(root, endswith(root, "/") ? "" : "/", - path[0] == '/' ? path+1 : path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest, - NULL); - else - return strjoin(path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest, - NULL); -} - -int find_binary(const char *name, char **ret) { - int last_error, r; - const char *p; - - assert(name); - - if (is_path(name)) { - if (access(name, X_OK) < 0) - return -errno; - - if (ret) { - r = path_make_absolute_cwd(name, ret); - if (r < 0) - return r; - } - - return 0; - } - - /** - * 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; - - last_error = -ENOENT; - - for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; - - r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (!path_is_absolute(element)) - continue; - - j = strjoin(element, "/", name, NULL); - if (!j) - return -ENOMEM; - - if (access(j, X_OK) >= 0) { - /* Found it! */ - - if (ret) { - *ret = path_kill_slashes(j); - j = NULL; - } - - return 0; - } - - last_error = -errno; - } - - return last_error; -} - -bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { - bool changed = false; - const char* const* i; - - assert(timestamp); - - if (paths == NULL) - return false; - - STRV_FOREACH(i, paths) { - struct stat stats; - usec_t u; - - if (stat(*i, &stats) < 0) - continue; - - u = timespec_load(&stats.st_mtim); - - /* first check */ - if (*timestamp >= u) - continue; - - log_debug("timestamp of '%s' changed", *i); - - /* update timestamp */ - if (update) { - *timestamp = u; - changed = true; - } else - return true; - } - - return changed; -} - -static int binary_is_good(const char *binary) { - _cleanup_free_ char *p = NULL, *d = NULL; - int r; - - r = find_binary(binary, &p); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - /* An fsck that is linked to /bin/true is a non-existent - * fsck */ - - r = readlink_malloc(p, &d); - if (r == -EINVAL) /* not a symlink */ - return 1; - if (r < 0) - return r; - - return !PATH_IN_SET(d, "true" - "/bin/true", - "/usr/bin/true", - "/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) { - char *n, *p; - size_t l; - - /* If root is passed, prefixes path with it. Otherwise returns - * it as is. */ - - assert(path); - - /* First, drop duplicate prefixing slashes from the path */ - while (path[0] == '/' && path[1] == '/') - path++; - - if (isempty(root) || path_equal(root, "/")) - return strdup(path); - - l = strlen(root) + 1 + strlen(path) + 1; - - n = new(char, l); - if (!n) - return NULL; - - p = stpcpy(n, root); - - while (p > n && p[-1] == '/') - p--; - - if (path[0] != '/') - *(p++) = '/'; - - strcpy(p, path); - return n; -} - -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_or_backup_file(const char *filename) { - const char *p; - - assert(filename); - - if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, "~")) - return true; - - p = strrchr(filename, '.'); - if (!p) - return false; - - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: - * - * The generic suffixes "~" and ".bak" for backup files - * The generic prefix "." for hidden files - * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. - */ - - return STR_IN_SET(p + 1, - "rpmnew", - "rpmsave", - "rpmorig", - "dpkg-old", - "dpkg-new", - "dpkg-tmp", - "dpkg-dist", - "dpkg-bak", - "dpkg-backup", - "dpkg-remove", - "ucf-new", - "ucf-old", - "ucf-dist", - "swp", - "bak", - "old", - "new"); -} - -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 deleted file mode 100644 index a27c13fcc3..0000000000 --- a/src/basic/path-util.h +++ /dev/null @@ -1,127 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "macro.h" -#include "time-util.h" - -#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" - -#ifdef HAVE_SPLIT_USR -# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR -#else -# define DEFAULT_PATH DEFAULT_PATH_NORMAL -#endif - -bool is_path(const char *p) _pure_; -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); -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_; -int path_compare(const char *a, const char *b) _pure_; -bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b); -char* path_join(const char *root, const char *path, const char *rest); - -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - -/* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) \ - ({ \ - char **s; \ - bool _found = false; \ - STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ - if (path_equal(p, *s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -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 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 - * directory. Excludes the specified directory itself */ -#define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ -#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -char *prefix_root(const char *root, const char *path); - -/* Similar to prefix_root(), but returns an alloca() buffer, or - * possibly a const pointer into the path parameter */ -#define prefix_roota(root, path) \ - ({ \ - const char* _path = (path), *_root = (root), *_ret; \ - char *_p, *_n; \ - size_t _l; \ - while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ - if (isempty(_root) || path_equal(_root, "/")) \ - _ret = _path; \ - else { \ - _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = alloca(_l); \ - _p = stpcpy(_n, _root); \ - while (_p > _n && _p[-1] == '/') \ - _p--; \ - if (_path[0] != '/') \ - *(_p++) = '/'; \ - strcpy(_p, _path); \ - _ret = _n; \ - } \ - _ret; \ - }) - -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_or_backup_file(const char *filename) _pure_; - -bool is_device_path(const char *path); diff --git a/src/basic/prioq.c b/src/basic/prioq.c deleted file mode 100644 index d2ec516d29..0000000000 --- a/src/basic/prioq.c +++ /dev/null @@ -1,320 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* - * 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 -#include - -#include "alloc-util.h" -#include "hashmap.h" -#include "prioq.h" - -struct prioq_item { - void *data; - unsigned *idx; -}; - -struct Prioq { - compare_func_t compare_func; - unsigned n_items, n_allocated; - - struct prioq_item *items; -}; - -Prioq *prioq_new(compare_func_t compare_func) { - Prioq *q; - - q = new0(Prioq, 1); - if (!q) - return q; - - q->compare_func = compare_func; - return q; -} - -Prioq* prioq_free(Prioq *q) { - if (!q) - return NULL; - - free(q->items); - free(q); - - return NULL; -} - -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { - assert(q); - - if (*q) - return 0; - - *q = prioq_new(compare_func); - if (!*q) - return -ENOMEM; - - return 0; -} - -static void swap(Prioq *q, unsigned j, unsigned k) { - void *saved_data; - unsigned *saved_idx; - - assert(q); - assert(j < q->n_items); - assert(k < q->n_items); - - assert(!q->items[j].idx || *(q->items[j].idx) == j); - assert(!q->items[k].idx || *(q->items[k].idx) == k); - - saved_data = q->items[j].data; - saved_idx = q->items[j].idx; - q->items[j].data = q->items[k].data; - q->items[j].idx = q->items[k].idx; - q->items[k].data = saved_data; - q->items[k].idx = saved_idx; - - if (q->items[j].idx) - *q->items[j].idx = j; - - if (q->items[k].idx) - *q->items[k].idx = k; -} - -static unsigned shuffle_up(Prioq *q, unsigned idx) { - assert(q); - - while (idx > 0) { - unsigned k; - - k = (idx-1)/2; - - if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) - break; - - swap(q, idx, k); - idx = k; - } - - return idx; -} - -static unsigned shuffle_down(Prioq *q, unsigned idx) { - assert(q); - - for (;;) { - unsigned j, k, s; - - k = (idx+1)*2; /* right child */ - j = k-1; /* left child */ - - if (j >= q->n_items) - break; - - if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) - - /* So our left child is smaller than we are, let's - * remember this fact */ - s = j; - else - s = idx; - - if (k < q->n_items && - q->compare_func(q->items[k].data, q->items[s].data) < 0) - - /* So our right child is smaller than we are, let's - * remember this fact */ - s = k; - - /* s now points to the smallest of the three items */ - - if (s == idx) - /* No swap necessary, we're done */ - break; - - swap(q, idx, s); - idx = s; - } - - return idx; -} - -int prioq_put(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - if (q->n_items >= q->n_allocated) { - unsigned n; - struct prioq_item *j; - - n = MAX((q->n_items+1) * 2, 16u); - j = realloc(q->items, sizeof(struct prioq_item) * n); - if (!j) - return -ENOMEM; - - q->items = j; - q->n_allocated = n; - } - - k = q->n_items++; - i = q->items + k; - i->data = data; - i->idx = idx; - - if (idx) - *idx = k; - - shuffle_up(q, k); - - return 0; -} - -static void remove_item(Prioq *q, struct prioq_item *i) { - struct prioq_item *l; - - assert(q); - assert(i); - - l = q->items + q->n_items - 1; - - if (i == l) - /* Last entry, let's just remove it */ - q->n_items--; - else { - unsigned k; - - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ - - k = i - q->items; - - i->data = l->data; - i->idx = l->idx; - if (i->idx) - *i->idx = k; - q->n_items--; - - k = shuffle_down(q, k); - shuffle_up(q, k); - } -} - -_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - assert(q); - - if (idx) { - if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) - return NULL; - - i = q->items + *idx; - if (i->data != data) - return NULL; - - return i; - } else { - for (i = q->items; i < q->items + q->n_items; i++) - if (i->data == data) - return i; - return NULL; - } -} - -int prioq_remove(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - if (!q) - return 0; - - i = find_item(q, data, idx); - if (!i) - return 0; - - remove_item(q, i); - return 1; -} - -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - i = find_item(q, data, idx); - if (!i) - return 0; - - k = i - q->items; - k = shuffle_down(q, k); - shuffle_up(q, k); - return 1; -} - -void *prioq_peek(Prioq *q) { - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - return q->items[0].data; -} - -void *prioq_pop(Prioq *q) { - void *data; - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - data = q->items[0].data; - remove_item(q, q->items); - return data; -} - -unsigned prioq_size(Prioq *q) { - - if (!q) - return 0; - - return q->n_items; -} - -bool prioq_isempty(Prioq *q) { - - if (!q) - return true; - - return q->n_items <= 0; -} diff --git a/src/basic/prioq.h b/src/basic/prioq.h deleted file mode 100644 index 113c73d040..0000000000 --- a/src/basic/prioq.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "hashmap.h" -#include "macro.h" - -typedef struct Prioq Prioq; - -#define PRIOQ_IDX_NULL ((unsigned) -1) - -Prioq *prioq_new(compare_func_t compare); -Prioq *prioq_free(Prioq *q); -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); - -int prioq_put(Prioq *q, void *data, unsigned *idx); -int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); - -void *prioq_peek(Prioq *q) _pure_; -void *prioq_pop(Prioq *q); - -unsigned prioq_size(Prioq *q) _pure_; -bool prioq_isempty(Prioq *q) _pure_; diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c deleted file mode 100644 index 0430beadaa..0000000000 --- a/src/basic/proc-cmdline.c +++ /dev/null @@ -1,188 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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, - NULL -}; - -static const char * const rlmap_initrd[] = { - "emergency", SPECIAL_EMERGENCY_TARGET, - "rescue", SPECIAL_RESCUE_TARGET, - NULL -}; - -const char* runlevel_to_target(const char *word) { - size_t i; - const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd - : rlmap; - - if (!word) - return NULL; - - if (in_initrd() && (word = startswith(word, "rd.")) == NULL) - return NULL; - - for (i = 0; rlmap_ptr[i] != NULL; i += 2) - if (streq(word, rlmap_ptr[i])) - return rlmap_ptr[i+1]; - - return NULL; -} diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h deleted file mode 100644 index 452642a2f5..0000000000 --- a/src/basic/proc-cmdline.h +++ /dev/null @@ -1,27 +0,0 @@ -#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 . -***/ - -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 deleted file mode 100644 index 54b644ad56..0000000000 --- a/src/basic/process-util.c +++ /dev/null @@ -1,860 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_VALGRIND_VALGRIND_H -#include -#endif - -#include "alloc-util.h" -#include "architecture.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "ioprio.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "process-util.h" -#include "raw-clone.h" -#include "signal-util.h" -#include "stat-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; - char state; - int r; - _cleanup_free_ char *line = NULL; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " %c", &state) != 1) - return -EIO; - - return (unsigned char) state; -} - -int get_process_comm(pid_t pid, char **name) { - const char *p; - int r; - - assert(name); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "comm"); - - r = read_one_line_file(p, name); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { - _cleanup_fclose_ FILE *f = NULL; - bool space = false; - char *r = NULL, *k; - const char *p; - int c; - - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing - * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most - * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If - * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a - * command line that resolves to the empty string will return the "comm" name of the process instead. - * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). */ - - p = procfs_file_alloca(pid, "cmdline"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - if (max_length == 1) { - - /* If there's only room for one byte, return the empty string */ - r = new0(char, 1); - if (!r) - return -ENOMEM; - - *line = r; - return 0; - - } else if (max_length == 0) { - size_t len = 0, allocated = 0; - - while ((c = getc(f)) != EOF) { - - if (!GREEDY_REALLOC(r, allocated, len+3)) { - free(r); - return -ENOMEM; - } - - if (isprint(c)) { - if (space) { - r[len++] = ' '; - space = false; - } - - r[len++] = c; - } else if (len > 0) - space = true; - } - - if (len > 0) - r[len] = 0; - else - r = mfree(r); - - } else { - bool dotdotdot = false; - size_t left; - - r = new(char, max_length); - if (!r) - return -ENOMEM; - - k = r; - left = max_length; - while ((c = getc(f)) != EOF) { - - if (isprint(c)) { - - if (space) { - if (left <= 2) { - dotdotdot = true; - break; - } - - *(k++) = ' '; - left--; - space = false; - } - - if (left <= 1) { - dotdotdot = true; - break; - } - - *(k++) = (char) c; - left--; - } else if (k > r) - space = true; - } - - if (dotdotdot) { - if (max_length <= 4) { - k = r; - left = max_length; - } else { - k = r + max_length - 4; - left = 4; - - /* Eat up final spaces */ - while (k > r && isspace(k[-1])) { - k--; - left++; - } - } - - strncpy(k, "...", left-1); - k[left-1] = 0; - } else - *k = 0; - } - - /* Kernel threads have no argv[] */ - if (isempty(r)) { - _cleanup_free_ char *t = NULL; - int h; - - free(r); - - if (!comm_fallback) - return -ENOENT; - - h = get_process_comm(pid, &t); - if (h < 0) - return h; - - if (max_length == 0) - r = strjoin("[", t, "]", NULL); - else { - size_t l; - - l = strlen(t); - - if (l + 3 <= max_length) - r = strjoin("[", t, "]", NULL); - else if (max_length <= 6) { - - r = new(char, max_length); - if (!r) - return -ENOMEM; - - memcpy(r, "[...]", max_length-1); - r[max_length-1] = 0; - } else { - char *e; - - t[max_length - 6] = 0; - - /* Chop off final spaces */ - e = strchr(t, 0); - while (e > t && isspace(e[-1])) - e--; - *e = 0; - - r = strjoin("[", t, "...]", NULL); - } - } - if (!r) - return -ENOMEM; - } - - *line = r; - 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 */ - - (void) 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; - char c; - bool eof; - FILE *f; - - if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ - return 0; - - assert(pid > 1); - - p = procfs_file_alloca(pid, "cmdline"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - count = fread(&c, 1, 1, f); - eof = feof(f); - fclose(f); - - /* Kernel threads have an empty cmdline */ - - if (count <= 0) - return eof ? 1 : -errno; - - return 0; -} - -int get_process_capeff(pid_t pid, char **capeff) { - const char *p; - int r; - - assert(capeff); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -static int get_process_link_contents(const char *proc_file, char **name) { - int r; - - assert(proc_file); - assert(name); - - r = readlink_malloc(proc_file, name); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - return 0; -} - -int get_process_exe(pid_t pid, char **name) { - const char *p; - char *d; - int r; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); - if (r < 0) - return r; - - d = endswith(*name, " (deleted)"); - if (d) - *d = '\0'; - - return 0; -} - -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *p; - - assert(field); - assert(uid); - - p = procfs_file_alloca(pid, "status"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - FOREACH_LINE(line, f, return -errno) { - char *l; - - l = strstrip(line); - - if (startswith(l, field)) { - l += strlen(field); - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, uid); - } - } - - return -EIO; -} - -int get_process_uid(pid_t pid, uid_t *uid) { - return get_process_id(pid, "Uid:", uid); -} - -int get_process_gid(pid_t pid, gid_t *gid) { - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", gid); -} - -int get_process_cwd(pid_t pid, char **cwd) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, cwd); -} - -int get_process_root(pid_t pid, char **root) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, root); -} - -int get_process_environ(pid_t pid, char **env) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *outcome = NULL; - int c; - const char *p; - size_t allocated = 0, sz = 0; - - assert(pid >= 0); - assert(env); - - p = procfs_file_alloca(pid, "environ"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - while ((c = fgetc(f)) != EOF) { - if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) - return -ENOMEM; - - if (c == '\0') - outcome[sz++] = '\n'; - else - sz += cescape_char(c, outcome + sz); - } - - if (!outcome) { - outcome = strdup(""); - if (!outcome) - return -ENOMEM; - } else - outcome[sz] = '\0'; - - *env = outcome; - outcome = NULL; - - return 0; -} - -int get_process_ppid(pid_t pid, pid_t *_ppid) { - int r; - _cleanup_free_ char *line = NULL; - long unsigned ppid; - const char *p; - - assert(pid >= 0); - assert(_ppid); - - if (pid == 0) { - *_ppid = getppid(); - return 0; - } - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%lu ", /* ppid */ - &ppid) != 1) - return -EIO; - - if ((long unsigned) (pid_t) ppid != ppid) - return -ERANGE; - - *_ppid = (pid_t) ppid; - - return 0; -} - -int wait_for_terminate(pid_t pid, siginfo_t *status) { - siginfo_t dummy; - - assert(pid >= 1); - - if (!status) - status = &dummy; - - for (;;) { - zero(*status); - - if (waitid(P_PID, pid, status, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - return negative_errno(); - } - - return 0; - } -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * process, the process was terminated by a signal, or - * failed for an unknown reason. - * >=0 : The process terminated normally, and its exit code is - * returned. - * - * That is, success is indicated by a return value of zero, and an - * error is indicated by a non-zero value. - * - * A warning is emitted if the process terminates abnormally, - * and also if it returns non-zero unless check_exit_code is true. - */ -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { - int r; - siginfo_t status; - - assert(name); - assert(pid > 1); - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_warning_errno(r, "Failed to wait for %s: %m", name); - - if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) - log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, - "%s failed with error code %i.", name, status.si_status); - else - log_debug("%s succeeded.", name); - - return status.si_status; - } else if (status.si_code == CLD_KILLED || - status.si_code == CLD_DUMPED) { - - log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); - return -EPROTO; - } - - log_warning("%s failed due to unknown reason.", name); - return -EPROTO; -} - -void sigkill_wait(pid_t pid) { - assert(pid > 1); - - if (kill(pid, SIGKILL) > 0) - (void) wait_for_terminate(pid, NULL); -} - -void sigkill_waitp(pid_t *pid) { - if (!pid) - return; - if (*pid <= 1) - return; - - sigkill_wait(*pid); -} - -int kill_and_sigcont(pid_t pid, int sig) { - int r; - - r = kill(pid, sig) < 0 ? -errno : 0; - - /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't - * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(SIGCONT, SIGKILL)) - (void) kill(pid, SIGCONT); - - return r; -} - -int getenv_for_pid(pid_t pid, const char *field, char **_value) { - _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; - int r; - bool done = false; - size_t l; - const char *path; - - assert(pid >= 0); - assert(field); - assert(_value); - - path = procfs_file_alloca(pid, "environ"); - - f = fopen(path, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - l = strlen(field); - r = 0; - - do { - char line[LINE_MAX]; - unsigned i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; - - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; - - line[i] = c; - } - line[i] = 0; - - if (memcmp(line, field, l) == 0 && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - r = 1; - break; - } - - } while (!done); - - *_value = value; - return r; -} - -bool pid_is_unwaited(pid_t pid) { - /* Checks whether a PID is still valid at all, including a zombie */ - - if (pid < 0) - return false; - - if (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; - - return errno != ESRCH; -} - -bool pid_is_alive(pid_t pid) { - int r; - - /* Checks whether a PID is still valid and not a zombie */ - - if (pid < 0) - return false; - - 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; -} - -int pid_from_same_root_fs(pid_t pid) { - const char *root; - - if (pid < 0) - return 0; - - root = procfs_file_alloca(pid, "root"); - - return files_same(root, "/proc/1/root"); -} - -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) { - - log_close(); - - /* 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) { - int architecture; - - if (!p) - return PERSONALITY_INVALID; - - /* Parse a personality specifier. We use 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. */ - - architecture = architecture_from_string(p); - if (architecture < 0) - return PERSONALITY_INVALID; - - if (architecture == native_architecture()) - return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) - return PER_LINUX32; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; - - if (p == PER_LINUX) - architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE - else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; -#endif - - if (architecture < 0) - return NULL; - - return architecture_to_string(architecture); -} - -void valgrind_summary_hack(void) { -#ifdef HAVE_VALGRIND_VALGRIND_H - if (getpid() == 1 && RUNNING_ON_VALGRIND) { - pid_t pid; - pid = raw_clone(SIGCHLD); - 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 -} - -int pid_compare_func(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - /* Suitable for usage in qsort() */ - - if (*p < *q) - return -1; - if (*p > *q) - return 1; - return 0; -} - -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 deleted file mode 100644 index 9f75088796..0000000000 --- a/src/basic/process-util.h +++ /dev/null @@ -1,105 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "formats-util.h" -#include "macro.h" - -#define procfs_file_alloca(pid, field) \ - ({ \ - pid_t _pid_ = (pid); \ - const char *_r_; \ - if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ - } else { \ - _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ - } \ - _r_; \ - }) - -int get_process_state(pid_t pid); -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); -int get_process_exe(pid_t pid, char **name); -int get_process_uid(pid_t pid, uid_t *uid); -int get_process_gid(pid_t pid, gid_t *gid); -int get_process_capeff(pid_t pid, char **capeff); -int get_process_cwd(pid_t pid, char **cwd); -int get_process_root(pid_t pid, char **root); -int get_process_environ(pid_t pid, char **environ); -int 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); -void sigkill_waitp(pid_t *pid); - -int kill_and_sigcont(pid_t pid, int sig); - -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); -int pid_from_same_root_fs(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); - -int pid_compare_func(const void *a, const void *b); diff --git a/src/basic/random-util.c b/src/basic/random-util.c deleted file mode 100644 index ad7b3eedf2..0000000000 --- a/src/basic/random-util.c +++ /dev/null @@ -1,133 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_AUXV_H -#include -#endif - -#include "fd-util.h" -#include "io-util.h" -#include "missing.h" -#include "random-util.h" -#include "time-util.h" - -int dev_urandom(void *p, size_t n) { - static int have_syscall = -1; - - _cleanup_close_ int fd = -1; - int r; - - /* Gathers some randomness from the kernel. This call will - * never block, and will always return some data from the - * kernel, regardless if the random pool is fully initialized - * or not. It thus makes no guarantee for the quality of the - * returned entropy, but is good enough for our usual usecases - * of seeding the hash functions for hashtable */ - - /* Use the getrandom() syscall unless we know we don't have - * it, or when the requested size is too large for it. */ - if (have_syscall != 0 || (size_t) (int) n != n) { - r = getrandom(p, n, GRND_NONBLOCK); - if (r == (int) n) { - have_syscall = true; - return 0; - } - - if (r < 0) { - if (errno == ENOSYS) - /* we lack the syscall, continue with - * reading from /dev/urandom */ - have_syscall = false; - else if (errno == EAGAIN) - /* not enough entropy for now. Let's - * remember to use the syscall the - * next time, again, but also read - * from /dev/urandom for now, which - * doesn't care about the current - * amount of entropy. */ - have_syscall = true; - else - return -errno; - } else - /* too short read? */ - return -ENODATA; - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, p, n, true); -} - -void initialize_srand(void) { - static bool srand_called = false; - unsigned x; -#ifdef HAVE_SYS_AUXV_H - void *auxv; -#endif - - if (srand_called) - return; - -#ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than nothing... */ - - auxv = (void*) getauxval(AT_RANDOM); - if (auxv) { - assert_cc(sizeof(x) < 16); - memcpy(&x, auxv, sizeof(x)); - } else -#endif - x = 0; - - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; -} - -void random_bytes(void *p, size_t n) { - uint8_t *q; - int r; - - r = dev_urandom(p, n); - if (r >= 0) - return; - - /* If some idiot made /dev/urandom unavailable to us, he'll - * get a PRNG instead. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q ++) - *q = rand(); -} diff --git a/src/basic/random-util.h b/src/basic/random-util.h deleted file mode 100644 index 3cee4c5014..0000000000 --- a/src/basic/random-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#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 . -***/ - -#include -#include - -int dev_urandom(void *p, size_t n); -void random_bytes(void *p, size_t n); -void initialize_srand(void); - -static inline uint64_t random_u64(void) { - uint64_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -static inline uint32_t random_u32(void) { - uint32_t u; - random_bytes(&u, sizeof(u)); - return u; -} diff --git a/src/basic/ratelimit.c b/src/basic/ratelimit.c deleted file mode 100644 index 3ca5625e4d..0000000000 --- a/src/basic/ratelimit.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include - -#include "macro.h" -#include "ratelimit.h" - -/* Modelled after Linux' lib/ratelimit.c by Dave Young - * , which is licensed GPLv2. */ - -bool ratelimit_test(RateLimit *r) { - usec_t ts; - - assert(r); - - if (r->interval <= 0 || r->burst <= 0) - return true; - - ts = now(CLOCK_MONOTONIC); - - if (r->begin <= 0 || - r->begin + r->interval < ts) { - r->begin = ts; - - /* Reset counter */ - r->num = 0; - goto good; - } - - if (r->num < r->burst) - goto good; - - return false; - -good: - r->num++; - return true; -} diff --git a/src/basic/ratelimit.h b/src/basic/ratelimit.h deleted file mode 100644 index 9c8dddf5ad..0000000000 --- a/src/basic/ratelimit.h +++ /dev/null @@ -1,58 +0,0 @@ -#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 . -***/ - -#include - -#include "time-util.h" -#include "util.h" - -typedef struct RateLimit { - usec_t interval; - usec_t begin; - unsigned burst; - unsigned num; -} RateLimit; - -#define RATELIMIT_DEFINE(_name, _interval, _burst) \ - RateLimit _name = { \ - .interval = (_interval), \ - .burst = (_burst), \ - .num = 0, \ - .begin = 0 \ - } - -#define RATELIMIT_INIT(v, _interval, _burst) \ - do { \ - RateLimit *_r = &(v); \ - _r->interval = (_interval); \ - _r->burst = (_burst); \ - _r->num = 0; \ - _r->begin = 0; \ - } while (false) - -#define RATELIMIT_RESET(v) \ - do { \ - RateLimit *_r = &(v); \ - _r->num = 0; \ - _r->begin = 0; \ - } while (false) - -bool ratelimit_test(RateLimit *r); diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h deleted file mode 100644 index d473828999..0000000000 --- a/src/basic/raw-clone.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2016 Michael Karcher - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "log.h" -#include "macro.h" - -/** - * raw_clone() - uses clone to create a new process with clone flags - * @flags: Flags to pass to the clone system call - * - * Uses the clone system call to create a new process with the cloning - * flags and termination signal passed in the flags parameter. Opposed - * to glibc's clone funtion, using this function does not set up a - * separate stack for the child, but relies on copy-on-write semantics - * on the one stack at a common virtual address, just as fork does. - * - * To obtain copy-on-write semantics, flags must not contain CLONE_VM, - * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are - * not usabale. - * Additionally, as this function does not pass the ptid, newtls and ctid - * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID, - * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. - * - * Returns: 0 in the child process and the child process id in the parent. - */ -static inline int raw_clone(unsigned long flags) { - assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| - CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); -#if defined(__s390__) || defined(__CRIS__) - /* On s390 and cris the order of the first and second arguments - * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, NULL, flags); -#elif defined(__sparc__) && defined(__arch64__) - { - /** - * sparc64 always returns the other process id in %o0, and - * a boolean flag whether this is the child or the parent in - * %o1. Inline assembly is needed to get the flag returned - * in %o1. - */ - int in_child; - int child_pid; - asm volatile("mov %2, %%g1\n\t" - "mov %3, %%o0\n\t" - "mov 0 , %%o1\n\t" - "t 0x6d\n\t" - "mov %%o1, %0\n\t" - "mov %%o0, %1" : - "=r"(in_child), "=r"(child_pid) : - "i"(__NR_clone), "r"(flags) : - "%o1", "%o0", "%g1" ); - if (in_child) - return 0; - else - return child_pid; - } -#else - return (int) syscall(__NR_clone, flags, NULL); -#endif -} diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h deleted file mode 100644 index 1d77a6445a..0000000000 --- a/src/basic/refcnt.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) - -#define REFCNT_INIT ((RefCount) { ._value = 1 }) diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c deleted file mode 100644 index 6a204b9ec3..0000000000 --- a/src/basic/replace-var.c +++ /dev/null @@ -1,112 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "macro.h" -#include "replace-var.h" -#include "string-util.h" - -/* - * Generic infrastructure for replacing @FOO@ style variables in - * strings. Will call a callback for each replacement. - */ - -static int get_variable(const char *b, char **r) { - size_t k; - char *t; - - assert(b); - assert(r); - - if (*b != '@') - return 0; - - k = strspn(b + 1, UPPERCASE_LETTERS "_"); - if (k <= 0 || b[k+1] != '@') - return 0; - - t = strndup(b + 1, k); - if (!t) - return -ENOMEM; - - *r = t; - return 1; -} - -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) { - char *r, *t; - const char *f; - size_t l; - - assert(text); - assert(lookup); - - l = strlen(text); - r = new(char, l+1); - if (!r) - return NULL; - - f = text; - t = r; - while (*f) { - _cleanup_free_ char *v = NULL, *n = NULL; - char *a; - int k; - size_t skip, d, nl; - - k = get_variable(f, &v); - if (k < 0) - goto oom; - if (k == 0) { - *(t++) = *(f++); - continue; - } - - n = lookup(v, userdata); - if (!n) - goto oom; - - skip = strlen(v) + 2; - - d = t - r; - nl = l - skip + strlen(n); - a = realloc(r, nl + 1); - if (!a) - goto oom; - - l = nl; - r = a; - t = r + d; - - t = stpcpy(t, n); - f += skip; - } - - *t = 0; - return r; - -oom: - free(r); - return NULL; -} diff --git a/src/basic/replace-var.h b/src/basic/replace-var.h deleted file mode 100644 index 78412910b2..0000000000 --- a/src/basic/replace-var.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c deleted file mode 100644 index ee063720ed..0000000000 --- a/src/basic/rlimit-util.c +++ /dev/null @@ -1,321 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "extract-word.h" -#include "formats-util.h" -#include "macro.h" -#include "missing.h" -#include "rlimit-util.h" -#include "string-table.h" -#include "time-util.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 int rlimit_parse_u64(const char *val, rlim_t *ret) { - uint64_t u; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */ - assert_cc(sizeof(rlim_t) == sizeof(uint64_t)); - - r = safe_atou64(val, &u); - if (r < 0) - return r; - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_size(const char *val, rlim_t *ret) { - uint64_t u; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_size(val, 1024, &u); - if (r < 0) - return r; - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_sec(const char *val, rlim_t *ret) { - uint64_t u; - usec_t t; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_sec(val, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) { - *ret = RLIM_INFINITY; - return 0; - } - - u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC); - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_usec(const char *val, rlim_t *ret) { - usec_t t; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_time(val, &t, 1); - if (r < 0) - return r; - if (t == USEC_INFINITY) { - *ret = RLIM_INFINITY; - return 0; - } - - *ret = (rlim_t) t; - return 0; -} - -static int rlimit_parse_nice(const char *val, rlim_t *ret) { - uint64_t rl; - int r; - - /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the - * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is - * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight - * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we - * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0. - * - * Yeah, Linux is quality engineering sometimes... */ - - if (val[0] == '+') { - - /* Prefixed with "+": Parse as positive user-friendly nice value */ - r = safe_atou64(val + 1, &rl); - if (r < 0) - return r; - - if (rl >= PRIO_MAX) - return -ERANGE; - - rl = 20 - rl; - - } else if (val[0] == '-') { - - /* Prefixed with "-": Parse as negative user-friendly nice value */ - r = safe_atou64(val + 1, &rl); - if (r < 0) - return r; - - if (rl > (uint64_t) (-PRIO_MIN)) - return -ERANGE; - - rl = 20 + rl; - } else { - - /* Not prefixed: parse as raw resource limit value */ - r = safe_atou64(val, &rl); - if (r < 0) - return r; - - if (rl > (uint64_t) (20 - PRIO_MIN)) - return -ERANGE; - } - - *ret = (rlim_t) rl; - return 0; -} - -static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = { - [RLIMIT_CPU] = rlimit_parse_sec, - [RLIMIT_FSIZE] = rlimit_parse_size, - [RLIMIT_DATA] = rlimit_parse_size, - [RLIMIT_STACK] = rlimit_parse_size, - [RLIMIT_CORE] = rlimit_parse_size, - [RLIMIT_RSS] = rlimit_parse_size, - [RLIMIT_NOFILE] = rlimit_parse_u64, - [RLIMIT_AS] = rlimit_parse_size, - [RLIMIT_NPROC] = rlimit_parse_u64, - [RLIMIT_MEMLOCK] = rlimit_parse_size, - [RLIMIT_LOCKS] = rlimit_parse_u64, - [RLIMIT_SIGPENDING] = rlimit_parse_u64, - [RLIMIT_MSGQUEUE] = rlimit_parse_size, - [RLIMIT_NICE] = rlimit_parse_nice, - [RLIMIT_RTPRIO] = rlimit_parse_u64, - [RLIMIT_RTTIME] = rlimit_parse_usec, -}; - -int rlimit_parse_one(int resource, const char *val, rlim_t *ret) { - assert(val); - assert(ret); - - if (resource < 0) - return -EINVAL; - if (resource >= _RLIMIT_MAX) - return -EINVAL; - - return rlimit_parse_table[resource](val, ret); -} - -int rlimit_parse(int resource, const char *val, struct rlimit *ret) { - _cleanup_free_ char *hard = NULL, *soft = NULL; - rlim_t hl, sl; - int r; - - assert(val); - assert(ret); - - r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = rlimit_parse_one(resource, soft, &sl); - if (r < 0) - return r; - - r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (!isempty(val)) - return -EINVAL; - if (r == 0) - hl = sl; - else { - r = rlimit_parse_one(resource, hard, &hl); - if (r < 0) - return r; - if (sl > hl) - return -EILSEQ; - } - - *ret = (struct rlimit) { - .rlim_cur = sl, - .rlim_max = hl, - }; - - return 0; -} - -int rlimit_format(const struct rlimit *rl, char **ret) { - char *s = NULL; - - assert(rl); - assert(ret); - - if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY) - s = strdup("infinity"); - else if (rl->rlim_cur >= RLIM_INFINITY) - (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max); - else if (rl->rlim_max >= RLIM_INFINITY) - (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur); - else if (rl->rlim_cur == rl->rlim_max) - (void) asprintf(&s, RLIM_FMT, rl->rlim_cur); - else - (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max); - - if (!s) - return -ENOMEM; - - *ret = s; - 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/basic/rlimit-util.h b/src/basic/rlimit-util.h deleted file mode 100644 index d4594eccd6..0000000000 --- a/src/basic/rlimit-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -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); - -int rlimit_parse_one(int resource, const char *val, rlim_t *ret); -int rlimit_parse(int resource, const char *val, struct rlimit *ret); - -int rlimit_format(const struct rlimit *rl, char **ret); - -#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c deleted file mode 100644 index 43816fd1bb..0000000000 --- a/src/basic/rm-rf.c +++ /dev/null @@ -1,236 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - int ret = 0, r; - - assert(fd >= 0); - - /* This returns the first error we run into, but nevertheless - * tries to go on. This closes the passed fd. */ - - if (!(flags & REMOVE_PHYSICAL)) { - - r = fd_is_temporary_fs(fd); - if (r < 0) { - safe_close(fd); - return r; - } - - if (!r) { - /* We refuse to clean physical file systems - * with this call, unless explicitly - * requested. This is extra paranoia just to - * be sure we never ever remove non-state - * data */ - - log_error("Attempted to remove disk file system, and we can't allow that."); - safe_close(fd); - return -EPERM; - } - } - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return errno == ENOENT ? 0 : -errno; - } - - for (;;) { - struct dirent *de; - bool is_dir; - struct stat st; - - errno = 0; - de = readdir(d); - if (!de) { - if (errno > 0 && ret == 0) - ret = -errno; - return ret; - } - - if (streq(de->d_name, ".") || streq(de->d_name, "..")) - continue; - - if (de->d_type == DT_UNKNOWN || - (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { - if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - continue; - } - - is_dir = S_ISDIR(st.st_mode); - } else - is_dir = de->d_type == DT_DIR; - - if (is_dir) { - int subdir_fd; - - /* if root_dev is set, remove subdirectories only if device is same */ - if (root_dev && st.st_dev != root_dev->st_dev) - continue; - - subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (subdir_fd < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - continue; - } - - /* Stop at mount points */ - r = fd_is_mount_point(fd, de->d_name, 0); - if (r < 0) { - if (ret == 0 && r != -ENOENT) - ret = r; - - safe_close(subdir_fd); - continue; - } - if (r) { - safe_close(subdir_fd); - continue; - } - - if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { - - /* This could be a subvolume, try to remove it */ - - r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r < 0) { - if (r != -ENOTTY && r != -EINVAL) { - if (ret == 0) - ret = r; - - safe_close(subdir_fd); - continue; - } - - /* ENOTTY, then it wasn't a - * btrfs subvolume, continue - * below. */ - } else { - /* It was a subvolume, continue. */ - safe_close(subdir_fd); - continue; - } - } - - /* We pass REMOVE_PHYSICAL here, to avoid - * doing the fstatfs() to check the file - * system type again for each directory */ - r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); - if (r < 0 && ret == 0) - ret = r; - - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - - } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { - - if (unlinkat(fd, de->d_name, 0) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - } - } -} - -int rm_rf(const char *path, RemoveFlags flags) { - int fd, r; - struct statfs s; - - assert(path); - - /* We refuse to clean the root file system with this - * call. This is extra paranoia to never cause a really - * seriously broken system. */ - if (path_equal(path, "/")) { - log_error("Attempted to remove entire root file system, and we can't allow that."); - return -EPERM; - } - - if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) { - /* Try to remove as subvolume first */ - r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r >= 0) - return r; - - if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR) - return r; - - /* Not btrfs or not a subvolume */ - } - - fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (fd < 0) { - - if (errno != ENOTDIR && errno != ELOOP) - return -errno; - - if (!(flags & REMOVE_PHYSICAL)) { - if (statfs(path, &s) < 0) - return -errno; - - if (!is_temporary_fs(&s)) { - log_error("Attempted to remove disk file system, and we can't allow that."); - return -EPERM; - } - } - - if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES)) - if (unlink(path) < 0 && errno != ENOENT) - return -errno; - - return 0; - } - - r = rm_rf_children(fd, flags, NULL); - - if (flags & REMOVE_ROOT) { - if (rmdir(path) < 0) { - if (r == 0 && errno != ENOENT) - r = -errno; - } - } - - return r; -} diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h deleted file mode 100644 index f693a5bb7c..0000000000 --- a/src/basic/rm-rf.h +++ /dev/null @@ -1,41 +0,0 @@ -#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 . -***/ - -#include - -typedef enum RemoveFlags { - REMOVE_ONLY_DIRECTORIES = 1, - REMOVE_ROOT = 2, - REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */ - REMOVE_SUBVOLUME = 8, -} RemoveFlags; - -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); -int rm_rf(const char *path, RemoveFlags flags); - -/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ -static inline void rm_rf_physical_and_free(char *p) { - if (!p) - return; - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free); diff --git a/src/basic/securebits.h b/src/basic/securebits.h deleted file mode 100644 index 98fbe0d433..0000000000 --- a/src/basic/securebits.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _LINUX_SECUREBITS_H -#define _LINUX_SECUREBITS_H 1 - -/* This is minimal version of Linux' linux/securebits.h header file, - * which is licensed GPL2 */ - -#define SECUREBITS_DEFAULT 0x00000000 - -/* When set UID 0 has no special privileges. When unset, we support - inheritance of root-permissions and suid-root executable under - compatibility mode. We raise the effective and inheritable bitmasks - *of the executable file* if the effective uid of the new process is - 0. If the real uid is 0, we raise the effective (legacy) bit of the - executable file. */ -#define SECURE_NOROOT 0 -#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ - -/* When set, setuid to/from uid 0 does not trigger capability-"fixup". - When unset, to provide compatibility with old programs relying on - set*uid to gain/lose privilege, transitions to/from uid 0 cause - capabilities to be gained/lost. */ -#define SECURE_NO_SETUID_FIXUP 2 -#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ - -/* When set, a process can retain its capabilities even after - transitioning to a non-root user (the set-uid fixup suppressed by - bit 2). Bit-4 is cleared when a process calls exec(); setting both - bit 4 and 5 will create a barrier through exec that no exec()'d - child can use this feature again. */ -#define SECURE_KEEP_CAPS 4 -#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ - -/* Each securesetting is implemented using two bits. One bit specifies - whether the setting is on or off. The other bit specify whether the - setting is locked or not. A setting which is locked cannot be - changed from user-level. */ -#define issecure_mask(X) (1 << (X)) -#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits)) - -#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ - issecure_mask(SECURE_NO_SETUID_FIXUP) | \ - issecure_mask(SECURE_KEEP_CAPS)) -#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) - -#endif /* !_LINUX_SECUREBITS_H */ diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c deleted file mode 100644 index bc07654668..0000000000 --- a/src/basic/selinux-util.c +++ /dev/null @@ -1,485 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SELINUX -#include -#include -#include -#endif - -#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(char*, freecon); -DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); - -#define _cleanup_freecon_ _cleanup_(freeconp) -#define _cleanup_context_free_ _cleanup_(context_freep) - -static int cached_use = -1; -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_have(void) { -#ifdef HAVE_SELINUX - if (cached_use < 0) - cached_use = is_selinux_enabled() > 0; - - return cached_use; -#else - return false; -#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; -#endif -} - -int mac_selinux_init(void) { - int r = 0; - -#ifdef HAVE_SELINUX - usec_t before_timestamp, after_timestamp; - struct mallinfo before_mallinfo, after_mallinfo; - - if (label_hnd) - return 0; - - if (!mac_selinux_use()) - return 0; - - before_mallinfo = mallinfo(); - before_timestamp = now(CLOCK_MONOTONIC); - - label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); - if (!label_hnd) { - log_enforcing("Failed to initialize SELinux context: %m"); - r = security_getenforce() == 1 ? -errno : 0; - } else { - char timespan[FORMAT_TIMESPAN_MAX]; - int l; - - after_timestamp = now(CLOCK_MONOTONIC); - after_mallinfo = mallinfo(); - - l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; - - log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.", - format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0), - (l+1023)/1024); - } -#endif - - return r; -} - -void mac_selinux_finish(void) { - -#ifdef HAVE_SELINUX - if (!label_hnd) - return; - - selabel_close(label_hnd); - label_hnd = NULL; -#endif -} - -int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - -#ifdef HAVE_SELINUX - struct stat st; - int r; - - assert(path); - - /* if mac_selinux_init() wasn't called before we are a NOOP */ - if (!label_hnd) - return 0; - - r = lstat(path, &st); - if (r >= 0) { - _cleanup_freecon_ char* fcon = NULL; - - r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode); - - /* If there's no label to set, then exit without warning */ - if (r < 0 && errno == ENOENT) - return 0; - - if (r >= 0) { - r = lsetfilecon_raw(path, fcon); - - /* If the FS doesn't support labels, then exit without warning */ - if (r < 0 && errno == EOPNOTSUPP) - return 0; - } - } - - if (r < 0) { - /* Ignore ENOENT in some cases */ - if (ignore_enoent && errno == ENOENT) - return 0; - - if (ignore_erofs && errno == EROFS) - return 0; - - log_enforcing("Unable to fix SELinux security context of %s: %m", path); - if (security_getenforce() == 1) - return -errno; - } -#endif - - return 0; -} - -int mac_selinux_apply(const char *path, const char *label) { - -#ifdef HAVE_SELINUX - if (!mac_selinux_use()) - return 0; - - assert(path); - assert(label); - - if (setfilecon(path, label) < 0) { - log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path); - if (security_getenforce() > 0) - return -errno; - } -#endif - return 0; -} - -int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { - int r = -EOPNOTSUPP; - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; - security_class_t sclass; - - assert(exe); - assert(label); - - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(&mycon); - if (r < 0) - return -errno; - - r = getfilecon_raw(exe, &fcon); - if (r < 0) - return -errno; - - sclass = string_to_security_class("process"); - r = security_compute_create_raw(mycon, fcon, sclass, label); - if (r < 0) - return -errno; -#endif - - return r; -} - -int mac_selinux_get_our_label(char **label) { - int r = -EOPNOTSUPP; - - assert(label); - -#ifdef HAVE_SELINUX - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(label); - if (r < 0) - return -errno; -#endif - - return r; -} - -int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { - int r = -EOPNOTSUPP; - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; - _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; - security_class_t sclass; - const char *range = NULL; - - assert(socket_fd >= 0); - assert(exe); - assert(label); - - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(&mycon); - if (r < 0) - return -errno; - - r = getpeercon_raw(socket_fd, &peercon); - if (r < 0) - return -errno; - - if (!exec_label) { - /* If there is no context set for next exec let's use context - of target executable */ - r = getfilecon_raw(exe, &fcon); - if (r < 0) - return -errno; - } - - bcon = context_new(mycon); - if (!bcon) - return -ENOMEM; - - pcon = context_new(peercon); - if (!pcon) - return -ENOMEM; - - range = context_range_get(pcon); - if (!range) - return -errno; - - r = context_range_set(bcon, range); - if (r) - return -errno; - - freecon(mycon); - mycon = strdup(context_str(bcon)); - if (!mycon) - return -ENOMEM; - - sclass = string_to_security_class("process"); - r = security_compute_create_raw(mycon, fcon, sclass, label); - if (r < 0) - return -errno; -#endif - - return r; -} - -char* mac_selinux_free(char *label) { - -#ifdef HAVE_SELINUX - if (!label) - return NULL; - - if (!mac_selinux_have()) - return NULL; - - - freecon(label); -#endif - - return NULL; -} - -int mac_selinux_create_file_prepare(const char *path, mode_t mode) { - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *filecon = NULL; - int r; - - assert(path); - - if (!label_hnd) - return 0; - - if (path_is_absolute(path)) - r = selabel_lookup_raw(label_hnd, &filecon, path, mode); - else { - _cleanup_free_ char *newpath = NULL; - - r = path_make_absolute_cwd(path, &newpath); - if (r < 0) - return r; - - r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); - } - - if (r < 0) { - /* No context specified by the policy? Proceed without setting it. */ - if (errno == ENOENT) - return 0; - - log_enforcing("Failed to determine SELinux security context for %s: %m", path); - } else { - if (setfscreatecon_raw(filecon) >= 0) - return 0; /* Success! */ - - log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); - } - - if (security_getenforce() > 0) - return -errno; - -#endif - return 0; -} - -void mac_selinux_create_file_clear(void) { - -#ifdef HAVE_SELINUX - PROTECT_ERRNO; - - if (!mac_selinux_use()) - return; - - setfscreatecon_raw(NULL); -#endif -} - -int mac_selinux_create_socket_prepare(const char *label) { - -#ifdef HAVE_SELINUX - if (!mac_selinux_use()) - return 0; - - assert(label); - - if (setsockcreatecon(label) < 0) { - log_enforcing("Failed to set SELinux security context %s for sockets: %m", label); - - if (security_getenforce() == 1) - return -errno; - } -#endif - - return 0; -} - -void mac_selinux_create_socket_clear(void) { - -#ifdef HAVE_SELINUX - PROTECT_ERRNO; - - if (!mac_selinux_use()) - return; - - setsockcreatecon_raw(NULL); -#endif -} - -int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { - - /* Binds a socket and label its file system object according to the SELinux policy */ - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *fcon = NULL; - const struct sockaddr_un *un; - bool context_changed = false; - char *path; - int r; - - assert(fd >= 0); - assert(addr); - assert(addrlen >= sizeof(sa_family_t)); - - if (!label_hnd) - goto skipped; - - /* Filter out non-local sockets */ - if (addr->sa_family != AF_UNIX) - goto skipped; - - /* Filter out anonymous sockets */ - if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1) - goto skipped; - - /* Filter out abstract namespace sockets */ - un = (const struct sockaddr_un*) addr; - if (un->sun_path[0] == 0) - goto skipped; - - path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); - - if (path_is_absolute(path)) - r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); - else { - _cleanup_free_ char *newpath = NULL; - - 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) { - /* No context specified by the policy? Proceed without setting it */ - if (errno == ENOENT) - goto skipped; - - log_enforcing("Failed to determine SELinux security context for %s: %m", path); - if (security_getenforce() > 0) - return -errno; - - } else { - if (setfscreatecon_raw(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) < 0 ? -errno : 0; - - if (context_changed) - setfscreatecon_raw(NULL); - - return r; - -skipped: -#endif - if (bind(fd, addr, addrlen) < 0) - return -errno; - - return 0; -} diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h deleted file mode 100644 index ce6bc8e44c..0000000000 --- a/src/basic/selinux-util.h +++ /dev/null @@ -1,51 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "macro.h" - -bool mac_selinux_use(void); -bool mac_selinux_have(void); -void mac_selinux_retest(void); - -int mac_selinux_init(void); -void mac_selinux_finish(void); - -int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs); -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); -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); - -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 deleted file mode 100644 index 12f64a8c57..0000000000 --- a/src/basic/set.h +++ /dev/null @@ -1,138 +0,0 @@ -#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 . -***/ - -#include "extract-word.h" -#include "hashmap.h" -#include "macro.h" - -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; -} - -static inline Set *set_free_free(Set *s) { - internal_hashmap_free_free(HASHMAP_BASE(s)); - return NULL; -} - -/* no set_free_free_free */ - -static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int set_put(Set *s, const void *key); -/* no set_update */ -/* no set_replace */ -static inline void *set_get(Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE(s), key); -} -/* no set_get2 */ - -static inline bool set_contains(Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(s), key); -} - -static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); -} - -/* no set_remove2 */ -/* no set_remove_value */ -int set_remove_and_put(Set *s, const void *old_key, const void *new_key); -/* no set_remove_and_replace */ -int set_merge(Set *s, Set *other); - -static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); -} - -static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); -} - -static inline unsigned set_size(Set *s) { - return internal_hashmap_size(HASHMAP_BASE(s)); -} - -static inline bool set_isempty(Set *s) { - return set_size(s) == 0; -} - -static inline unsigned set_buckets(Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE(s)); -} - -bool set_iterate(Set *s, Iterator *i, void **value); - -static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s)); -} - -static inline void set_clear_free(Set *s) { - internal_hashmap_clear_free(HASHMAP_BASE(s)); -} - -/* no set_clear_free_free */ - -static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); -} - -/* no set_steal_first_key */ -/* no set_first_key */ - -static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); -} - -/* no set_next */ - -static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); -} - -int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); - -#define SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) - -#define SET_FOREACH_MOVE(e, d, s) \ - for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); - -#define _cleanup_set_free_ _cleanup_(set_freep) -#define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c deleted file mode 100644 index 0ce4f75684..0000000000 --- a/src/basic/sigbus.c +++ /dev/null @@ -1,152 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "macro.h" -#include "sigbus.h" -#include "util.h" - -#define SIGBUS_QUEUE_MAX 64 - -static struct sigaction old_sigaction; -static unsigned n_installed = 0; - -/* We maintain a fixed size list of page addresses that triggered a - SIGBUS. We access with list with atomic operations, so that we - don't have to deal with locks between signal handler and main - programs in possibly multiple threads. */ - -static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX]; -static volatile sig_atomic_t n_sigbus_queue = 0; - -static void sigbus_push(void *addr) { - unsigned u; - - assert(addr); - - /* Find a free place, increase the number of entries and leave, if we can */ - for (u = 0; u < SIGBUS_QUEUE_MAX; u++) - if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) { - __sync_fetch_and_add(&n_sigbus_queue, 1); - return; - } - - /* If we can't, make sure the queue size is out of bounds, to - * mark it as overflow */ - for (;;) { - unsigned c; - - __sync_synchronize(); - c = n_sigbus_queue; - - if (c > SIGBUS_QUEUE_MAX) /* already overflow */ - return; - - if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX)) - return; - } -} - -int sigbus_pop(void **ret) { - assert(ret); - - for (;;) { - unsigned u, c; - - __sync_synchronize(); - c = n_sigbus_queue; - - if (_likely_(c == 0)) - return 0; - - if (_unlikely_(c >= SIGBUS_QUEUE_MAX)) - return -EOVERFLOW; - - for (u = 0; u < SIGBUS_QUEUE_MAX; u++) { - void *addr; - - addr = sigbus_queue[u]; - if (!addr) - continue; - - if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) { - __sync_fetch_and_sub(&n_sigbus_queue, 1); - *ret = addr; - return 1; - } - } - } -} - -static void sigbus_handler(int sn, siginfo_t *si, void *data) { - unsigned long ul; - void *aligned; - - assert(sn == SIGBUS); - assert(si); - - if (si->si_code != BUS_ADRERR || !si->si_addr) { - assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); - raise(SIGBUS); - return; - } - - ul = (unsigned long) si->si_addr; - ul = ul / page_size(); - ul = ul * page_size(); - aligned = (void*) ul; - - /* Let's remember which address failed */ - sigbus_push(aligned); - - /* Replace mapping with an anonymous page, so that the - * execution can continue, however with a zeroed out page */ - assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned); -} - -void sigbus_install(void) { - struct sigaction sa = { - .sa_sigaction = sigbus_handler, - .sa_flags = SA_SIGINFO, - }; - - n_installed++; - - if (n_installed == 1) - assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0); - - return; -} - -void sigbus_reset(void) { - - if (n_installed <= 0) - return; - - n_installed--; - - if (n_installed == 0) - assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); - - return; -} diff --git a/src/basic/sigbus.h b/src/basic/sigbus.h deleted file mode 100644 index 980243d9ce..0000000000 --- a/src/basic/sigbus.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -void sigbus_install(void); -void sigbus_reset(void); - -int sigbus_pop(void **ret); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c deleted file mode 100644 index 280b5c3251..0000000000 --- a/src/basic/signal-util.c +++ /dev/null @@ -1,278 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#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 = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - int sig, r = 0; - - for (sig = 1; sig < _NSIG; sig++) { - - /* These two cannot be caught... */ - if (sig == SIGKILL || sig == SIGSTOP) - continue; - - /* On Linux the first two RT signals are reserved by - * glibc, and sigaction() will return EINVAL for them. */ - if ((sigaction(sig, &sa, NULL) < 0)) - if (errno != EINVAL && r >= 0) - r = -errno; - } - - return r; -} - -int reset_signal_mask(void) { - sigset_t ss; - - if (sigemptyset(&ss) < 0) - return -errno; - - if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) - return -errno; - - return 0; -} - -static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { - int r = 0; - - /* negative signal ends the list. 0 signal is skipped. */ - - if (sig < 0) - return 0; - - if (sig > 0) { - if (sigaction(sig, sa, NULL) < 0) - r = -errno; - } - - while ((sig = va_arg(ap, int)) >= 0) { - - if (sig == 0) - continue; - - if (sigaction(sig, sa, NULL) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigaction_many(const struct sigaction *sa, ...) { - va_list ap; - int r; - - va_start(ap, sa); - r = sigaction_many_ap(sa, 0, ap); - va_end(ap); - - return r; -} - -int ignore_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -int default_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -static int sigset_add_many_ap(sigset_t *ss, va_list ap) { - int sig, r = 0; - - assert(ss); - - while ((sig = va_arg(ap, int)) >= 0) { - - if (sig == 0) - continue; - - if (sigaddset(ss, sig) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigset_add_many(sigset_t *ss, ...) { - va_list ap; - int r; - - va_start(ap, ss); - r = sigset_add_many_ap(ss, ap); - va_end(ap); - - return r; -} - -int sigprocmask_many(int how, sigset_t *old, ...) { - va_list ap; - sigset_t ss; - int r; - - if (sigemptyset(&ss) < 0) - return -errno; - - va_start(ap, old); - r = sigset_add_many_ap(&ss, ap); - va_end(ap); - - if (r < 0) - return r; - - if (sigprocmask(how, &ss, old) < 0) - return -errno; - - return 0; -} - -static const char *const __signal_table[] = { - [SIGHUP] = "HUP", - [SIGINT] = "INT", - [SIGQUIT] = "QUIT", - [SIGILL] = "ILL", - [SIGTRAP] = "TRAP", - [SIGABRT] = "ABRT", - [SIGBUS] = "BUS", - [SIGFPE] = "FPE", - [SIGKILL] = "KILL", - [SIGUSR1] = "USR1", - [SIGSEGV] = "SEGV", - [SIGUSR2] = "USR2", - [SIGPIPE] = "PIPE", - [SIGALRM] = "ALRM", - [SIGTERM] = "TERM", -#ifdef SIGSTKFLT - [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ -#endif - [SIGCHLD] = "CHLD", - [SIGCONT] = "CONT", - [SIGSTOP] = "STOP", - [SIGTSTP] = "TSTP", - [SIGTTIN] = "TTIN", - [SIGTTOU] = "TTOU", - [SIGURG] = "URG", - [SIGXCPU] = "XCPU", - [SIGXFSZ] = "XFSZ", - [SIGVTALRM] = "VTALRM", - [SIGPROF] = "PROF", - [SIGWINCH] = "WINCH", - [SIGIO] = "IO", - [SIGPWR] = "PWR", - [SIGSYS] = "SYS" -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); - -const char *signal_to_string(int signo) { - static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1]; - const char *name; - - name = __signal_to_string(signo); - if (name) - return name; - - if (signo >= SIGRTMIN && signo <= SIGRTMAX) - xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); - else - xsprintf(buf, "%d", signo); - - return buf; -} - -int signal_from_string(const char *s) { - int signo; - int offset = 0; - unsigned u; - - signo = __signal_from_string(s); - if (signo > 0) - return signo; - - if (startswith(s, "RTMIN+")) { - s += 6; - offset = SIGRTMIN; - } - if (safe_atou(s, &u) >= 0) { - signo = (int) u + offset; - if (SIGNAL_VALID(signo)) - return signo; - } - return -EINVAL; -} - -int signal_from_string_try_harder(const char *s) { - int signo; - assert(s); - - signo = signal_from_string(s); - if (signo <= 0) - if (startswith(s, "SIG")) - return signal_from_string(s+3); - - return signo; -} - -void nop_signal_handler(int sig) { - /* nothing here */ -} diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h deleted file mode 100644 index dfd6eb564d..0000000000 --- a/src/basic/signal-util.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" - -int reset_all_signal_handlers(void); -int reset_signal_mask(void); - -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); - -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); - -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); - -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); -} - -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ - t; \ - }) - -static inline bool SIGNAL_VALID(int signo) { - return signo > 0 && signo < _NSIG; -} diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c deleted file mode 100644 index 8c1cdc3db6..0000000000 --- a/src/basic/siphash24.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - SipHash reference C implementation - - Written in 2012 by - Jean-Philippe Aumasson - Daniel J. Bernstein - - To the extent possible under law, the author(s) have dedicated all copyright - and related and neighboring rights to this software to the public domain - worldwide. This software is distributed without any warranty. - - You should have received a copy of the CC0 Public Domain Dedication along with - this software. If not, see . - - (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 - -#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; - -#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 padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); -#endif - - state->v3 ^= state->padding; - sipround(state); - sipround(state); - state->v0 ^= state->padding; - - state->padding = 0; - } - - end -= (state->inlen % sizeof(uint64_t)); - - 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; - } -} - -uint64_t siphash24_finalize(struct siphash *state) { - uint64_t b; - - assert(state); - - b = state->padding | (((uint64_t) state->inlen) << 56); - -#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) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); -#endif - - state->v3 ^= b; - sipround(state); - sipround(state); - state->v0 ^= b; - -#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); -#endif - 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 deleted file mode 100644 index 54e2420cc6..0000000000 --- a/src/basic/siphash24.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -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 deleted file mode 100644 index 3a3df987df..0000000000 --- a/src/basic/smack-util.c +++ /dev/null @@ -1,241 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Intel Corporation - - Author: Auke Kok - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "path-util.h" -#include "process-util.h" -#include "smack-util.h" -#include "string-table.h" -#include "xattr-util.h" - -#ifdef HAVE_SMACK -bool mac_smack_use(void) { - static int cached_use = -1; - - if (cached_use < 0) - cached_use = access("/sys/fs/smackfs/", F_OK) >= 0; - - return cached_use; -} - -static const char* const smack_attr_table[_SMACK_ATTR_MAX] = { - [SMACK_ATTR_ACCESS] = "security.SMACK64", - [SMACK_ATTR_EXEC] = "security.SMACK64EXEC", - [SMACK_ATTR_MMAP] = "security.SMACK64MMAP", - [SMACK_ATTR_TRANSMUTE] = "security.SMACK64TRANSMUTE", - [SMACK_ATTR_IPIN] = "security.SMACK64IPIN", - [SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT", -}; - -DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr); - -int mac_smack_read(const char *path, SmackAttr attr, char **label) { - assert(path); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - assert(label); - - if (!mac_smack_use()) - return 0; - - return getxattr_malloc(path, smack_attr_to_string(attr), label, true); -} - -int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { - assert(fd >= 0); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - assert(label); - - if (!mac_smack_use()) - return 0; - - return fgetxattr_malloc(fd, smack_attr_to_string(attr), label); -} - -int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { - int r; - - assert(path); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - - if (!mac_smack_use()) - return 0; - - if (label) - r = lsetxattr(path, smack_attr_to_string(attr), label, strlen(label), 0); - else - r = lremovexattr(path, smack_attr_to_string(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { - int r; - - assert(fd >= 0); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - - if (!mac_smack_use()) - return 0; - - if (label) - r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0); - else - r = fremovexattr(fd, smack_attr_to_string(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int mac_smack_apply_pid(pid_t pid, const char *label) { - const char *p; - int r = 0; - - assert(label); - - if (!mac_smack_use()) - return 0; - - p = procfs_file_alloca(pid, "attr/current"); - r = write_string_file(p, label, 0); - if (r < 0) - return r; - - return r; -} - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - struct stat st; - int r = 0; - - assert(path); - - if (!mac_smack_use()) - return 0; - - /* - * Path must be in /dev and must exist - */ - if (!path_startswith(path, "/dev")) - return 0; - - r = lstat(path, &st); - if (r >= 0) { - const char *label; - - /* - * Label directories and character devices "*". - * Label symlinks "_". - * Don't change anything else. - */ - - if (S_ISDIR(st.st_mode)) - label = SMACK_STAR_LABEL; - else if (S_ISLNK(st.st_mode)) - label = SMACK_FLOOR_LABEL; - else if (S_ISCHR(st.st_mode)) - label = SMACK_STAR_LABEL; - else - return 0; - - r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0); - - /* If the FS doesn't support labels, then exit without warning */ - if (r < 0 && errno == EOPNOTSUPP) - return 0; - } - - if (r < 0) { - /* Ignore ENOENT in some cases */ - if (ignore_enoent && errno == ENOENT) - return 0; - - if (ignore_erofs && errno == EROFS) - return 0; - - r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); - } - - return r; -} - -int mac_smack_copy(const char *dest, const char *src) { - int r = 0; - _cleanup_free_ char *label = NULL; - - assert(dest); - assert(src); - - r = mac_smack_read(src, SMACK_ATTR_ACCESS, &label); - if (r < 0) - return r; - - r = mac_smack_apply(dest, SMACK_ATTR_ACCESS, label); - if (r < 0) - return r; - - return r; -} - -#else -bool mac_smack_use(void) { - return false; -} - -int mac_smack_read(const char *path, SmackAttr attr, char **label) { - return -EOPNOTSUPP; -} - -int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { - return -EOPNOTSUPP; -} - -int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { - return 0; -} - -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { - return 0; -} - -int mac_smack_apply_pid(pid_t pid, const char *label) { - return 0; -} - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - return 0; -} - -int mac_smack_copy(const char *dest, const char *src) { - return 0; -} -#endif diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h deleted file mode 100644 index f90ba0a027..0000000000 --- a/src/basic/smack-util.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Intel Corporation - - Author: Auke Kok - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "macro.h" - -#define SMACK_FLOOR_LABEL "_" -#define SMACK_STAR_LABEL "*" - -typedef enum SmackAttr { - SMACK_ATTR_ACCESS = 0, - SMACK_ATTR_EXEC = 1, - SMACK_ATTR_MMAP = 2, - SMACK_ATTR_TRANSMUTE = 3, - SMACK_ATTR_IPIN = 4, - SMACK_ATTR_IPOUT = 5, - _SMACK_ATTR_MAX, - _SMACK_ATTR_INVALID = -1, -} SmackAttr; - -bool mac_smack_use(void); - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); - -const char* smack_attr_to_string(SmackAttr i) _const_; -SmackAttr smack_attr_from_string(const char *s) _pure_; -int mac_smack_read(const char *path, SmackAttr attr, char **label); -int mac_smack_read_fd(int fd, SmackAttr attr, char **label); -int mac_smack_apply(const char *path, SmackAttr attr, const char *label); -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label); -int mac_smack_apply_pid(pid_t pid, const char *label); -int mac_smack_copy(const char *dest, const char *src); diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c deleted file mode 100644 index 6d1dc83874..0000000000 --- a/src/basic/socket-label.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "selinux-util.h" -#include "socket-util.h" -#include "umask-util.h" - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label) { - - _cleanup_close_ int fd = -1; - int r, one; - - assert(a); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported()) - return -EAFNOSUPPORT; - - if (label) { - r = mac_selinux_create_socket_prepare(label); - if (r < 0) - return r; - } - - fd = socket(socket_address_family(a), a->type | flags, a->protocol); - r = fd < 0 ? -errno : 0; - - if (label) - mac_selinux_create_socket_clear(); - - if (r < 0) - return r; - - if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { - int flag = only == SOCKET_ADDRESS_IPV6_ONLY; - - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) - return -errno; - } - - if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) { - if (bind_to_device) - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) - return -errno; - - if (reuse_port) { - one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0) - log_warning_errno(errno, "SO_REUSEPORT failed: %m"); - } - - if (free_bind) { - one = 1; - if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) - log_warning_errno(errno, "IP_FREEBIND failed: %m"); - } - - if (transparent) { - one = 1; - if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) - log_warning_errno(errno, "IP_TRANSPARENT failed: %m"); - } - } - - one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) - return -errno; - - if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { - /* Create parents */ - (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); - - /* Enforce the right access mode for the socket */ - RUN_WITH_UMASK(~socket_mode) { - r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); - if (r == -EADDRINUSE) { - /* Unlink and try again */ - unlink(a->sockaddr.un.sun_path); - if (bind(fd, &a->sockaddr.sa, a->size) < 0) - return -errno; - } else if (r < 0) - return r; - } - } else { - if (bind(fd, &a->sockaddr.sa, a->size) < 0) - return -errno; - } - - if (socket_address_can_accept(a)) - if (listen(fd, backlog) < 0) - return -errno; - - r = fd; - fd = -1; - - return r; -} - -int make_socket_fd(int log_level, const char* address, int type, int flags) { - SocketAddress a; - int fd, r; - - r = socket_address_parse(&a, address); - if (r < 0) - return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); - - a.type = type; - - fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, - NULL, false, false, false, 0755, 0644, NULL); - if (fd < 0 || log_get_max_level() >= log_level) { - _cleanup_free_ char *p = NULL; - - r = socket_address_print(&a, &p); - if (r < 0) - return log_error_errno(r, "socket_address_print(): %m"); - - if (fd < 0) - log_error_errno(fd, "Failed to listen on %s: %m", p); - else - log_full(log_level, "Listening on %s", p); - } - - return fd; -} diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c deleted file mode 100644 index 385c3e4df3..0000000000 --- a/src/basic/socket-util.c +++ /dev/null @@ -1,1048 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" -#include "util.h" - -int socket_address_parse(SocketAddress *a, const char *s) { - char *e, *n; - unsigned u; - int r; - - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_STREAM; - - if (*s == '[') { - /* IPv6 in [x:.....:z]:p notation */ - - e = strchr(s+1, ']'); - if (!e) - return -EINVAL; - - n = strndupa(s+1, e-s-1); - - errno = 0; - if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) - return errno > 0 ? -errno : -EINVAL; - - e++; - if (*e != ':') - return -EINVAL; - - e++; - r = safe_atou(e, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->size = sizeof(struct sockaddr_in6); - - } else if (*s == '/') { - /* AF_UNIX socket */ - - size_t l; - - l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path, s, l); - a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; - - } else if (*s == '@') { - /* Abstract AF_UNIX socket */ - size_t l; - - l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path+1, s+1, l); - a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - - } else { - e = strchr(s, ':'); - if (e) { - r = safe_atou(e+1, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - n = strndupa(s, e-s); - - /* IPv4 in w.x.y.z:p notation? */ - r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); - if (r < 0) - return -errno; - - if (r > 0) { - /* Gotcha, it's a traditional IPv4 address */ - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); - a->size = sizeof(struct sockaddr_in); - } else { - unsigned idx; - - if (strlen(n) > IF_NAMESIZE-1) - return -EINVAL; - - /* Uh, our last resort, an interface name */ - idx = if_nametoindex(n); - if (idx == 0) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->sockaddr.in6.sin6_scope_id = idx; - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } - } else { - - /* Just a port */ - r = safe_atou(s, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - if (socket_ipv6_is_supported()) { - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } else { - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); - a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; - a->size = sizeof(struct sockaddr_in); - } - } - } - - return 0; -} - -int socket_address_parse_and_warn(SocketAddress *a, const char *s) { - SocketAddress b; - int r; - - /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ - - r = socket_address_parse(&b, s); - if (r < 0) - return r; - - if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { - log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); - return -EAFNOSUPPORT; - } - - *a = b; - return 0; -} - -int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; - unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_RAW; - - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; - - family = netlink_family_from_string(sfamily); - if (family < 0) - return -EINVAL; - - a->sockaddr.nl.nl_family = AF_NETLINK; - a->sockaddr.nl.nl_groups = group; - - a->type = SOCK_RAW; - a->size = sizeof(struct sockaddr_nl); - a->protocol = family; - - return 0; -} - -int socket_address_verify(const SocketAddress *a) { - assert(a); - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->size != sizeof(struct sockaddr_in)) - return -EINVAL; - - if (a->sockaddr.in.sin_port == 0) - return -EINVAL; - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - case AF_INET6: - if (a->size != sizeof(struct sockaddr_in6)) - return -EINVAL; - - if (a->sockaddr.in6.sin6_port == 0) - return -EINVAL; - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - case AF_UNIX: - if (a->size < offsetof(struct sockaddr_un, sun_path)) - return -EINVAL; - - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; - - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; - - if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) - return -EINVAL; - } - } - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET) - return -EINVAL; - - return 0; - - case AF_NETLINK: - - if (a->size != sizeof(struct sockaddr_nl)) - return -EINVAL; - - if (a->type != SOCK_RAW && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_address_print(const SocketAddress *a, char **ret) { - int r; - - assert(a); - assert(ret); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_NETLINK) { - _cleanup_free_ char *sfamily = NULL; - - r = netlink_family_to_string_alloc(a->protocol, &sfamily); - if (r < 0) - return r; - - r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); - if (r < 0) - return -ENOMEM; - - return 0; - } - - return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); -} - -bool socket_address_can_accept(const SocketAddress *a) { - assert(a); - - return - a->type == SOCK_STREAM || - a->type == SOCK_SEQPACKET; -} - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { - assert(a); - assert(b); - - /* Invalid addresses are unequal to all */ - if (socket_address_verify(a) < 0 || - socket_address_verify(b) < 0) - return false; - - if (a->type != b->type) - return false; - - if (socket_address_family(a) != socket_address_family(b)) - return false; - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) - return false; - - if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) - return false; - - break; - - case AF_INET6: - if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) - return false; - - if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) - return false; - - break; - - case AF_UNIX: - if (a->size <= offsetof(struct sockaddr_un, sun_path) || - b->size <= offsetof(struct sockaddr_un, sun_path)) - return false; - - if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) - return false; - - if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path)) - return false; - } else { - if (a->size != b->size) - return false; - - if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) - return false; - } - - break; - - case AF_NETLINK: - if (a->protocol != b->protocol) - return false; - - if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) - return false; - - break; - - default: - /* Cannot compare, so we assume the addresses are different */ - return false; - } - - return true; -} - -bool socket_address_is(const SocketAddress *a, const char *s, int type) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse(&b, s) < 0) - return false; - - b.type = type; - - return socket_address_equal(a, &b); -} - -bool socket_address_is_netlink(const SocketAddress *a, const char *s) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse_netlink(&b, s) < 0) - return false; - - return socket_address_equal(a, &b); -} - -const char* socket_address_get_path(const SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return NULL; - - if (a->sockaddr.un.sun_path[0] == 0) - return NULL; - - return a->sockaddr.un.sun_path; -} - -bool socket_ipv6_is_supported(void) { - if (access("/proc/net/sockstat6", F_OK) != 0) - return false; - - return true; -} - -bool socket_address_matches_fd(const SocketAddress *a, int fd) { - SocketAddress b; - socklen_t solen; - - assert(a); - assert(fd >= 0); - - b.size = sizeof(b.sockaddr); - if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) - return false; - - if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) - return false; - - solen = sizeof(b.type); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) - return false; - - if (b.type != a->type) - return false; - - if (a->protocol != 0) { - solen = sizeof(b.protocol); - if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) - return false; - - if (b.protocol != a->protocol) - return false; - } - - return socket_address_equal(a, &b); -} - -int sockaddr_port(const struct sockaddr *_sa) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - - assert(sa); - - if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port); -} - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - char *p; - int r; - - assert(sa); - assert(salen >= sizeof(sa->sa.sa_family)); - - switch (sa->sa.sa_family) { - - case AF_INET: { - uint32_t a; - - a = be32toh(sa->in.sin_addr.s_addr); - - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(sa->in.sin_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); - if (r < 0) - return -ENOMEM; - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (translate_ipv6 && - memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a[0], a[1], a[2], a[3], - be16toh(sa->in6.sin6_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a[0], a[1], a[2], a[3]); - if (r < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - - if (include_port) { - r = asprintf(&p, - "[%s]:%u", - a, - be16toh(sa->in6.sin6_port)); - if (r < 0) - return -ENOMEM; - } else { - p = strdup(a); - if (!p) - return -ENOMEM; - } - } - - break; - } - - case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path)) { - p = strdup(""); - if (!p) - return -ENOMEM; - - } else if (sa->un.sun_path[0] == 0) { - /* abstract */ - - /* FIXME: We assume we can print the - * socket path here and that it hasn't - * more than one NUL byte. That is - * actually an invalid assumption */ - - p = new(char, sizeof(sa->un.sun_path)+1); - if (!p) - return -ENOMEM; - - p[0] = '@'; - memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); - p[sizeof(sa->un.sun_path)] = 0; - - } else { - p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); - if (!p) - return -ENOMEM; - } - - break; - - default: - return -EOPNOTSUPP; - } - - - *ret = p; - return 0; -} - -int getpeername_pretty(int fd, bool include_port, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - int r; - - assert(fd >= 0); - assert(ret); - - if (getpeername(fd, &sa.sa, &salen) < 0) - return -errno; - - if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; - - /* UNIX connection sockets are anonymous, so let's use - * PID/UID as pretty credentials instead */ - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - - return 0; - } - - /* For remote sockets we translate IPv6 addresses back to IPv4 - * if applicable, since that's nicer. */ - - return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); -} - -int getsockname_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - /* For local sockets we do not translate IPv6 addresses back - * to IPv6 if applicable, since this is usually used for - * listening sockets where the difference between IPv4 and - * IPv6 matters. */ - - return sockaddr_pretty(&sa.sa, salen, false, true, ret); -} - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { - int r; - char host[NI_MAXHOST], *ret; - - assert(_ret); - - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, - NI_IDN|NI_IDN_USE_STD3_ASCII_RULES); - if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; - - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; - } - - *_ret = ret; - return 0; -} - -int getnameinfo_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - return socknameinfo_pretty(&sa, salen, ret); -} - -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - -static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", - [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); - -static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { - [SOCKET_ADDRESS_DEFAULT] = "default", - [SOCKET_ADDRESS_BOTH] = "both", - [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { - assert(a); - assert(b); - - if (a->sa.sa_family != b->sa.sa_family) - return false; - - if (a->sa.sa_family == AF_INET) - return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; - - if (a->sa.sa_family == AF_INET6) - return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; - - return false; -} - -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); - -bool ifname_valid(const char *p) { - bool numeric = true; - - /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources - * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We - * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - if (STR_IN_SET(p, ".", "..")) - return false; - - while (*p) { - if ((unsigned char) *p >= 127U) - return false; - - if ((unsigned char) *p <= 32U) - return false; - - if (*p == ':' || *p == '/') - return false; - - numeric = numeric && (*p >= '0' && *p <= '9'); - p++; - } - - if (numeric) - return false; - - return true; -} - -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; -} - -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 msghdr mh = { - .msg_name = (struct sockaddr*) sa, - .msg_namelen = len, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - - 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 *(int*) CMSG_DATA(found); -} - -ssize_t next_datagram_size_fd(int fd) { - ssize_t l; - int k; - - /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will - * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't - * do. This difference is actually of major importance as we need to be sure that the size returned here - * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of - * the wrong size. */ - - l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); - if (l < 0) { - if (errno == EOPNOTSUPP || errno == EFAULT) - goto fallback; - - return -errno; - } - if (l == 0) - goto fallback; - - return l; - -fallback: - k = 0; - - /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD - * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ - - if (ioctl(fd, FIONREAD, &k) < 0) - return -errno; - - return (ssize_t) k; -} - -int flush_accept(int fd) { - - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int r; - - - /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ - - for (;;) { - int cfd; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return 0; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return 0; - - return -errno; - } - - close(cfd); - } -} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h deleted file mode 100644 index e9230e4a9f..0000000000 --- a/src/basic/socket-util.h +++ /dev/null @@ -1,154 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "macro.h" -#include "util.h" - -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_nl nl; - struct sockaddr_storage storage; - struct sockaddr_ll ll; -}; - -typedef struct SocketAddress { - union sockaddr_union sockaddr; - - /* We store the size here explicitly due to the weird - * sockaddr_un semantics for abstract sockets */ - socklen_t size; - - /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ - int type; - - /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ - int protocol; -} SocketAddress; - -typedef enum SocketAddressBindIPv6Only { - SOCKET_ADDRESS_DEFAULT, - SOCKET_ADDRESS_BOTH, - SOCKET_ADDRESS_IPV6_ONLY, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 -} SocketAddressBindIPv6Only; - -#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) - -int socket_address_parse(SocketAddress *a, const char *s); -int socket_address_parse_and_warn(SocketAddress *a, const char *s); -int socket_address_parse_netlink(SocketAddress *a, const char *s); -int socket_address_print(const SocketAddress *a, char **p); -int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); - -bool socket_address_can_accept(const SocketAddress *a) _pure_; - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label); -int make_socket_fd(int log_level, const char* address, int type, int flags); - -bool socket_address_is(const SocketAddress *a, const char *s, int type); -bool socket_address_is_netlink(const SocketAddress *a, const char *s); - -bool socket_address_matches_fd(const SocketAddress *a, int fd); - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; - -const char* socket_address_get_path(const SocketAddress *a); - -bool socket_ipv6_is_supported(void); - -int sockaddr_port(const struct sockaddr *_sa) _pure_; - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, bool include_port, char **ret); -int getsockname_pretty(int fd, char **ret); - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); -int getnameinfo_pretty(int fd, char **ret); - -const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; - -int netlink_family_to_string_alloc(int b, char **s); -int netlink_family_from_string(const char *s) _pure_; - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); - -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); - -bool ifname_valid(const char *p); - -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); - -ssize_t next_datagram_size_fd(int fd); - -int flush_accept(int fd); - -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - -/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ -#define SOCKADDR_UN_LEN(sa) \ - ({ \ - const struct sockaddr_un *_sa = &(sa); \ - assert(_sa->sun_family == AF_UNIX); \ - offsetof(struct sockaddr_un, sun_path) + \ - (_sa->sun_path[0] == 0 ? \ - 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ - }) diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h deleted file mode 100644 index c913fda8c5..0000000000 --- a/src/basic/sparse-endian.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (c) 2012 Josh Triplett - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef SPARSE_ENDIAN_H -#define SPARSE_ENDIAN_H - -#include -#include -#include - -#ifdef __CHECKER__ -#define __bitwise __attribute__((bitwise)) -#define __force __attribute__((force)) -#else -#define __bitwise -#define __force -#endif - -typedef uint16_t __bitwise le16_t; -typedef uint16_t __bitwise be16_t; -typedef uint32_t __bitwise le32_t; -typedef uint32_t __bitwise be32_t; -typedef uint64_t __bitwise le64_t; -typedef uint64_t __bitwise be64_t; - -#undef htobe16 -#undef htole16 -#undef be16toh -#undef le16toh -#undef htobe32 -#undef htole32 -#undef be32toh -#undef le32toh -#undef htobe64 -#undef htole64 -#undef be64toh -#undef le64toh - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) -#define bswap_16_on_be(x) (x) -#define bswap_32_on_be(x) (x) -#define bswap_64_on_be(x) (x) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16_on_le(x) (x) -#define bswap_32_on_le(x) (x) -#define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) -#endif - -static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); } -static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); } -static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); } - -static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); } -static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); } -static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); } - -static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); } -static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); } -static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); } - -static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); } -static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); } -static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); } - -#endif /* SPARSE_ENDIAN_H */ diff --git a/src/basic/special.h b/src/basic/special.h deleted file mode 100644 index 084d3dfa23..0000000000 --- a/src/basic/special.h +++ /dev/null @@ -1,119 +0,0 @@ -#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 . -***/ - -#define SPECIAL_DEFAULT_TARGET "default.target" - -/* Shutdown targets */ -#define SPECIAL_UMOUNT_TARGET "umount.target" -/* This is not really intended to be started by directly. This is - * mostly so that other targets (reboot/halt/poweroff) can depend on - * it to bring all services down that want to be brought down on - * system shutdown. */ -#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" -#define SPECIAL_HALT_TARGET "halt.target" -#define SPECIAL_POWEROFF_TARGET "poweroff.target" -#define SPECIAL_REBOOT_TARGET "reboot.target" -#define SPECIAL_KEXEC_TARGET "kexec.target" -#define SPECIAL_EXIT_TARGET "exit.target" -#define SPECIAL_SUSPEND_TARGET "suspend.target" -#define SPECIAL_HIBERNATE_TARGET "hibernate.target" -#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target" - -/* Special boot targets */ -#define SPECIAL_RESCUE_TARGET "rescue.target" -#define SPECIAL_EMERGENCY_TARGET "emergency.target" -#define SPECIAL_MULTI_USER_TARGET "multi-user.target" -#define SPECIAL_GRAPHICAL_TARGET "graphical.target" - -/* Early boot targets */ -#define SPECIAL_SYSINIT_TARGET "sysinit.target" -#define SPECIAL_SOCKETS_TARGET "sockets.target" -#define SPECIAL_BUSNAMES_TARGET "busnames.target" -#define SPECIAL_TIMERS_TARGET "timers.target" -#define SPECIAL_PATHS_TARGET "paths.target" -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" -#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" -#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" -#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target" -#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" -#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ -#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" -#define SPECIAL_SWAP_TARGET "swap.target" -#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target" -#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ -#define SPECIAL_BASIC_TARGET "basic.target" - -/* LSB compatibility */ -#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ -#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ -#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ - -/* - * Rules regarding adding further high level targets like the above: - * - * - Be conservative, only add more of these when we really need - * them. We need strong usecases for further additions. - * - * - When there can be multiple implementations running side-by-side, - * it needs to be a .target unit which can pull in all - * implementations. - * - * - If something can be implemented with socket activation, and - * without, it needs to be a .target unit, so that it can pull in - * the appropriate unit. - * - * - Otherwise, it should be a .service unit. - * - * - In some cases it is OK to have both a .service and a .target - * unit, i.e. if there can be multiple parallel implementations, but - * only one is the "system" one. Example: syslog. - * - * Or to put this in other words: .service symlinks can be used to - * arbitrate between multiple implementations if there can be only one - * of a kind. .target units can be used to support multiple - * implementations that can run side-by-side. - */ - -/* Magic early boot services */ -#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" -#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" -#define SPECIAL_QUOTAON_SERVICE "quotaon.service" -#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" - -/* Services systemd relies on */ -#define SPECIAL_DBUS_SERVICE "dbus.service" -#define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket" -#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service" - -/* Magic init signals */ -#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" -#define SPECIAL_SIGPWR_TARGET "sigpwr.target" -#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" - -/* Where we add all our system units, users and machines by default */ -#define SPECIAL_SYSTEM_SLICE "system.slice" -#define SPECIAL_USER_SLICE "user.slice" -#define SPECIAL_MACHINE_SLICE "machine.slice" -#define SPECIAL_ROOT_SLICE "-.slice" - -/* The scope unit systemd itself lives in. */ -#define SPECIAL_INIT_SCOPE "init.scope" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c deleted file mode 100644 index 309e84b93d..0000000000 --- a/src/basic/stat-util.c +++ /dev/null @@ -1,218 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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 deleted file mode 100644 index 56d28f791e..0000000000 --- a/src/basic/stat-util.h +++ /dev/null @@ -1,69 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include - -#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 deleted file mode 100644 index bd1144b4c9..0000000000 --- a/src/basic/stdio-util.h +++ /dev/null @@ -1,76 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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 deleted file mode 100644 index 4bef87d3c2..0000000000 --- a/src/basic/strbuf.c +++ /dev/null @@ -1,205 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "strbuf.h" - -/* - * Strbuf stores given strings in a single continuous allocated memory - * area. Identical strings are de-duplicated and return the same offset - * as the first string stored. If the tail of a string already exists - * in the buffer, the tail is returned. - * - * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the - * information about the stored strings. - * - * Example of udev rules: - * $ ./udevadm test . - * ... - * read rules file: /usr/lib/udev/rules.d/99-systemd.rules - * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings - * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used - * ... - */ - -struct strbuf *strbuf_new(void) { - struct strbuf *str; - - str = new0(struct strbuf, 1); - if (!str) - return NULL; - - str->buf = new0(char, 1); - if (!str->buf) - goto err; - str->len = 1; - - str->root = new0(struct strbuf_node, 1); - if (!str->root) - goto err; - str->nodes_count = 1; - return str; -err: - free(str->buf); - free(str->root); - free(str); - return NULL; -} - -static void strbuf_node_cleanup(struct strbuf_node *node) { - size_t i; - - for (i = 0; i < node->children_count; i++) - strbuf_node_cleanup(node->children[i].child); - free(node->children); - free(node); -} - -/* clean up trie data, leave only the string buffer */ -void strbuf_complete(struct strbuf *str) { - if (!str) - return; - if (str->root) - strbuf_node_cleanup(str->root); - str->root = NULL; -} - -/* clean up everything */ -void strbuf_cleanup(struct strbuf *str) { - if (!str) - return; - if (str->root) - strbuf_node_cleanup(str->root); - free(str->buf); - free(str); -} - -static int strbuf_children_cmp(const struct strbuf_child_entry *n1, - const struct strbuf_child_entry *n2) { - return n1->c - n2->c; -} - -static void bubbleinsert(struct strbuf_node *node, - uint8_t c, - struct strbuf_node *node_child) { - - struct strbuf_child_entry new = { - .c = c, - .child = node_child, - }; - int left = 0, right = node->children_count; - - while (right > left) { - int middle = (right + left) / 2 ; - if (strbuf_children_cmp(&node->children[middle], &new) <= 0) - left = middle + 1; - else - right = middle; - } - - memmove(node->children + left + 1, node->children + left, - sizeof(struct strbuf_child_entry) * (node->children_count - left)); - node->children[left] = new; - - node->children_count++; -} - -/* add string, return the index/offset into the buffer */ -ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) { - uint8_t c; - struct strbuf_node *node; - size_t depth; - char *buf_new; - struct strbuf_child_entry *child; - struct strbuf_node *node_child; - ssize_t off; - - if (!str->root) - return -EINVAL; - - /* search string; start from last character to find possibly matching tails */ - if (len == 0) - return 0; - str->in_count++; - str->in_len += len; - - node = str->root; - c = s[len-1]; - for (depth = 0; depth <= len; depth++) { - struct strbuf_child_entry search; - - /* match against current node */ - off = node->value_off + node->value_len - len; - if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) { - str->dedup_len += len; - str->dedup_count++; - return off; - } - - c = s[len - 1 - depth]; - - /* bsearch is not allowed on a NULL sequence */ - if (node->children_count == 0) - break; - - /* lookup child node */ - search.c = c; - child = bsearch(&search, node->children, node->children_count, - sizeof(struct strbuf_child_entry), - (__compar_fn_t) strbuf_children_cmp); - if (!child) - break; - node = child->child; - } - - /* add new string */ - buf_new = realloc(str->buf, str->len + len+1); - if (!buf_new) - return -ENOMEM; - str->buf = buf_new; - off = str->len; - memcpy(str->buf + off, s, len); - str->len += len; - str->buf[str->len++] = '\0'; - - /* new node */ - node_child = new0(struct strbuf_node, 1); - if (!node_child) - return -ENOMEM; - node_child->value_off = off; - node_child->value_len = len; - - /* extend array, add new entry, sort for bisection */ - child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry)); - if (!child) { - free(node_child); - return -ENOMEM; - } - - str->nodes_count++; - - node->children = child; - bubbleinsert(node, c, node_child); - - return off; -} diff --git a/src/basic/strbuf.h b/src/basic/strbuf.h deleted file mode 100644 index a1632da0e8..0000000000 --- a/src/basic/strbuf.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -struct strbuf { - char *buf; - size_t len; - struct strbuf_node *root; - - size_t nodes_count; - size_t in_count; - size_t in_len; - size_t dedup_len; - size_t dedup_count; -}; - -struct strbuf_node { - size_t value_off; - size_t value_len; - - struct strbuf_child_entry *children; - uint8_t children_count; -}; - -struct strbuf_child_entry { - uint8_t c; - struct strbuf_node *child; -}; - -struct strbuf *strbuf_new(void); -ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len); -void strbuf_complete(struct strbuf *str); -void strbuf_cleanup(struct strbuf *str); diff --git a/src/basic/string-table.c b/src/basic/string-table.c deleted file mode 100644 index a1499ab126..0000000000 --- a/src/basic/string-table.c +++ /dev/null @@ -1,34 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - - for (i = 0; i < len; ++i) - if (streq_ptr(table[i], key)) - return (ssize_t) i; - - return -1; -} diff --git a/src/basic/string-table.h b/src/basic/string-table.h deleted file mode 100644 index 369610efc8..0000000000 --- a/src/basic/string-table.h +++ /dev/null @@ -1,119 +0,0 @@ - -#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 . -***/ - -#include -#include -#include -#include -#include - -#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; \ - if (!s) \ - return -1; \ - 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_TO_STRING_FALLBACK(name,type,max,scope) \ - scope 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; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ - 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; \ - } \ - - -#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) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) diff --git a/src/basic/string-util.c b/src/basic/string-util.c deleted file mode 100644 index 293a15f9c0..0000000000 --- a/src/basic/string-util.c +++ /dev/null @@ -1,855 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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; - int r; - - 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)) { - char32_t c; - - r = utf8_encoded_to_unichar(i, &c); - if (r < 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; ) { - char32_t c; - - j = utf8_prev_char(j); - r = utf8_encoded_to_unichar(j, &c); - if (r < 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 deleted file mode 100644 index 1209e1e2e1..0000000000 --- a/src/basic/string-util.h +++ /dev/null @@ -1,194 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#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 HEXDIGITS DIGITS "abcdefABCDEF" - -#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 const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} - -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 deleted file mode 100644 index 34e464d253..0000000000 --- a/src/basic/strv.c +++ /dev/null @@ -1,943 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -char *strv_find(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (streq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_prefix(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (startswith(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_startswith(char **l, const char *name) { - char **i, *e; - - assert(name); - - /* Like strv_find_prefix, but actually returns only the - * suffix, not the whole item */ - - STRV_FOREACH(i, l) { - e = startswith(*i, name); - if (e) - return e; - } - - return NULL; -} - -void strv_clear(char **l) { - char **k; - - if (!l) - return; - - for (k = l; *k; k++) - free(*k); - - *l = NULL; -} - -char **strv_free(char **l) { - strv_clear(l); - free(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; - - k = r = new(char*, strv_length(l) + 1); - if (!r) - return NULL; - - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } - - *k = NULL; - return r; -} - -unsigned strv_length(char * const *l) { - unsigned n = 0; - - if (!l) - return 0; - - for (; *l; l++) - n++; - - return n; -} - -char **strv_new_ap(const char *x, va_list ap) { - const char *s; - char **a; - unsigned n = 0, i = 0; - va_list aq; - - /* As a special trick we ignore all listed strings that equal - * STRV_IGNORE. This is supposed to be used with the - * STRV_IFNOTNULL() macro to include possibly NULL strings in - * the string list. */ - - if (x) { - n = x == STRV_IGNORE ? 0 : 1; - - va_copy(aq, ap); - while ((s = va_arg(aq, const char*))) { - if (s == STRV_IGNORE) - continue; - - n++; - } - - va_end(aq); - } - - a = new(char*, n+1); - if (!a) - return NULL; - - if (x) { - if (x != STRV_IGNORE) { - a[i] = strdup(x); - if (!a[i]) - goto fail; - i++; - } - - while ((s = va_arg(ap, const char*))) { - - if (s == STRV_IGNORE) - continue; - - a[i] = strdup(s); - if (!a[i]) - goto fail; - - i++; - } - } - - a[i] = NULL; - - return a; - -fail: - strv_free(a); - return NULL; -} - -char **strv_new(const char *x, ...) { - char **r; - va_list ap; - - va_start(ap, x); - r = strv_new_ap(x, ap); - va_end(ap); - - return r; -} - -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) { - - if (filter_duplicates && strv_contains(t, *s)) - continue; - - t[p+i] = strdup(*s); - if (!t[p+i]) - goto rollback; - - i++; - t[p+i] = NULL; - } - - 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) { - int r; - char **s; - - STRV_FOREACH(s, b) { - char *v; - - v = strappend(*s, suffix); - if (!v) - return -ENOMEM; - - r = strv_push(a, v); - if (r < 0) { - free(v); - return r; - } - } - - return 0; -} - -char **strv_split(const char *s, const char *separator) { - const char *word, *state; - size_t l; - unsigned n, i; - char **r; - - assert(s); - - n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - -char **strv_split_newlines(const char *s) { - char **l; - unsigned n; - - assert(s); - - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ - - l = strv_split(s, NEWLINE); - if (!l) - return NULL; - - n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) - l[n - 1] = mfree(l[n - 1]); - - return l; -} - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&s, &word, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(l, allocated, n + 2)) - return -ENOMEM; - - l[n++] = word; - word = NULL; - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = l; - l = NULL; - - return (int) n; -} - -char *strv_join(char **l, const char *separator) { - char *r, *e; - char **s; - size_t n, k; - - if (!separator) - separator = " "; - - k = strlen(separator); - - n = 0; - STRV_FOREACH(s, l) { - if (s != l) - n += k; - n += strlen(*s); - } - - r = new(char, n+1); - if (!r) - return NULL; - - e = r; - STRV_FOREACH(s, l) { - if (s != l) - e = stpcpy(e, separator); - - e = stpcpy(e, *s); - } - - *e = 0; - - return r; -} - -char *strv_join_quoted(char **l) { - char *buf = NULL; - char **s; - size_t allocated = 0, len = 0; - - STRV_FOREACH(s, l) { - /* assuming here that escaped string cannot be more - * than twice as long, and reserving space for the - * separator and quotes. - */ - _cleanup_free_ char *esc = NULL; - size_t needed; - - if (!GREEDY_REALLOC(buf, allocated, - len + strlen(*s) * 2 + 3)) - goto oom; - - esc = cescape(*s); - if (!esc) - goto oom; - - needed = snprintf(buf + len, allocated - len, "%s\"%s\"", - len > 0 ? " " : "", esc); - assert(needed < allocated - len); - len += needed; - } - - if (!buf) - buf = malloc0(1); - - return buf; - - oom: - free(buf); - return NULL; -} - -int strv_push(char ***l, char *value) { - char **c; - unsigned n, m; - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) - return -ENOMEM; - - c[n] = value; - c[n+1] = NULL; - - *l = c; - return 0; -} - -int strv_push_pair(char ***l, char *a, char *b) { - char **c; - unsigned n, m; - - if (!a && !b) - return 0; - - n = strv_length(*l); - - /* increase and check for overflow */ - m = n + !!a + !!b + 1; - if (m < n) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) - return -ENOMEM; - - if (a) - c[n++] = a; - if (b) - c[n++] = b; - c[n] = NULL; - - *l = c; - return 0; -} - -int strv_push_prepend(char ***l, char *value) { - char **c; - unsigned n, m, i; - - if (!value) - return 0; - - n = strv_length(*l); - - /* increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = new(char*, m); - if (!c) - return -ENOMEM; - - for (i = 0; i < n; i++) - c[i+1] = (*l)[i]; - - c[0] = value; - c[n+1] = NULL; - - free(*l); - *l = c; - - return 0; -} - -int strv_consume(char ***l, char *value) { - int r; - - r = strv_push(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_consume_pair(char ***l, char *a, char *b) { - int r; - - r = strv_push_pair(l, a, b); - if (r < 0) { - free(a); - free(b); - } - - return r; -} - -int strv_consume_prepend(char ***l, char *value) { - int r; - - r = strv_push_prepend(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_extend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume(l, v); -} - -int strv_extend_front(char ***l, const char *value) { - size_t n, m; - char *v, **c; - - assert(l); - - /* Like strv_extend(), but prepends rather than appends the new entry */ - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and overflow check. */ - m = n + 2; - if (m < n) - return -ENOMEM; - - v = strdup(value); - if (!v) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) { - free(v); - return -ENOMEM; - } - - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; - return 0; -} - -char **strv_uniq(char **l) { - char **i; - - /* Drops duplicate entries. The first identical string will be - * kept, the others dropped */ - - STRV_FOREACH(i, l) - strv_remove(i+1, *i); - - return l; -} - -bool strv_is_uniq(char **l) { - char **i; - - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) - return false; - - return true; -} - -char **strv_remove(char **l, const char *s) { - char **f, **t; - - if (!l) - return NULL; - - assert(s); - - /* Drops every occurrence of s in the string list, edits - * in-place. */ - - for (f = t = l; *f; f++) - if (streq(*f, s)) - free(*f); - else - *(t++) = *f; - - *t = NULL; - return l; -} - -char **strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - const char *p; - unsigned c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - p = s; - while (p < s + l) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -char **strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL, NULL); - - return r; -} - -int strv_make_nulstr(char **l, char **p, size_t *q) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - 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 + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *p = m; - *q = n - 1; - - m = NULL; - - return 0; -} - -bool strv_overlap(char **a, char **b) { - char **i; - - STRV_FOREACH(i, a) - if (strv_contains(b, *i)) - return true; - - return false; -} - -static int str_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - - return strcmp(*a, *b); -} - -char **strv_sort(char **l) { - - if (strv_isempty(l)) - return l; - - qsort(l, strv_length(l), sizeof(char*), str_compare); - return l; -} - -bool strv_equal(char **a, char **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)) - return false; - - return true; -} - -void strv_print(char **l) { - char **s; - - STRV_FOREACH(s, l) - puts(*s); -} - -int strv_extendf(char ***l, const char *format, ...) { - va_list ap; - char *x; - int r; - - va_start(ap, format); - r = vasprintf(&x, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return strv_consume(l, x); -} - -char **strv_reverse(char **l) { - unsigned n, i; - - n = strv_length(l); - if (n <= 1) - return l; - - for (i = 0; i < n / 2; i++) - SWAP_TWO(l[i], l[n-1-i]); - - return l; -} - -char **strv_shell_escape(char **l, const char *bad) { - char **s; - - /* Escapes every character in every string in l that is in bad, - * edits in-place, does not roll-back on error. */ - - STRV_FOREACH(s, l) { - char *v; - - v = shell_escape(*s, bad); - if (!v) - return NULL; - - free(*s); - *s = v; - } - - return l; -} - -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) - return true; - - 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 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; -} - -int fputstrv(FILE *f, char **l, const char *separator, bool *space) { - bool b = false; - char **s; - int r; - - /* Like fputs(), but for strv, and with a less stupid argument order */ - - if (!space) - space = &b; - - STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/basic/strv.h b/src/basic/strv.h deleted file mode 100644 index 683ce83a2a..0000000000 --- a/src/basic/strv.h +++ /dev/null @@ -1,174 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "extract-word.h" -#include "macro.h" -#include "util.h" - -char *strv_find(char **l, const char *name) _pure_; -char *strv_find_prefix(char **l, const char *name) _pure_; -char *strv_find_startswith(char **l, const char *name) _pure_; - -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, 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); -int strv_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); -int strv_push_pair(char ***l, char *a, char *b); -int strv_push_prepend(char ***l, char *value); -int strv_consume(char ***l, char *value); -int strv_consume_pair(char ***l, char *a, char *b); -int strv_consume_prepend(char ***l, char *value); - -char **strv_remove(char **l, const char *s); -char **strv_uniq(char **l); -bool strv_is_uniq(char **l); - -bool strv_equal(char **a, char **b); - -#define strv_contains(l, s) (!!strv_find((l), (s))) - -char **strv_new(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); - -#define STRV_IGNORE ((const char *) -1) - -static inline const char* STRV_IFNOTNULL(const char *x) { - return x ? x : STRV_IGNORE; -} - -static inline bool strv_isempty(char * const *l) { - return !l || !*l; -} - -char **strv_split(const char *s, const char *separator); -char **strv_split_newlines(const char *s); - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); - -char *strv_join(char **l, const char *separator); -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_; - -#define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) - -#define STRV_FOREACH_BACKWARDS(s, l) \ - STRV_FOREACH(s, l) \ - ; \ - for ((s)--; (l) && ((s) >= (l)); (s)--) - -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) - -char **strv_sort(char **l); -void strv_print(char **l); - -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - -#define strv_from_stdarg_alloca(first) \ - ({ \ - char **_l; \ - \ - if (!first) \ - _l = (char**) &first; \ - else { \ - unsigned _n; \ - va_list _ap; \ - \ - _n = 1; \ - va_start(_ap, first); \ - while (va_arg(_ap, char*)) \ - _n++; \ - va_end(_ap); \ - \ - _l = newa(char*, _n+1); \ - _l[_n = 0] = (char*) first; \ - va_start(_ap, first); \ - for (;;) { \ - _l[++_n] = va_arg(_ap, char*); \ - if (!_l[_n]) \ - break; \ - } \ - va_end(_ap); \ - } \ - _l; \ - }) - -#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) - -#define FOREACH_STRING(x, ...) \ - for (char **_l = ({ \ - char **_ll = STRV_MAKE(__VA_ARGS__); \ - x = _ll ? _ll[0] : NULL; \ - _ll; \ - }); \ - _l && *_l; \ - x = ({ \ - _l ++; \ - _l[0]; \ - })) - -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); - -bool strv_fnmatch(char* const* patterns, const char *s, int flags); - -static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { - assert(s); - 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); - -int fputstrv(FILE *f, char **l, const char *separator, bool *space); diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c deleted file mode 100644 index aaf11d21f6..0000000000 --- a/src/basic/strxcpyx.c +++ /dev/null @@ -1,100 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* - * Concatenates/copies strings. In any case, terminates in all cases - * with '\0' * and moves the @dest pointer forward to the added '\0'. - * Returns the * remaining size, and 0 if the string was truncated. - */ - -#include -#include -#include - -#include "strxcpyx.h" - -size_t strpcpy(char **dest, size_t size, const char *src) { - size_t len; - - len = strlen(src); - if (len >= size) { - if (size > 1) - *dest = mempcpy(*dest, src, size-1); - size = 0; - } else { - if (len > 0) { - *dest = mempcpy(*dest, src, len); - size -= len; - } - } - *dest[0] = '\0'; - return size; -} - -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { - va_list va; - int i; - - va_start(va, src); - i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { - *dest += i; - size -= i; - } else { - *dest += size; - size = 0; - } - va_end(va); - *dest[0] = '\0'; - return size; -} - -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { - va_list va; - - va_start(va, src); - do { - size = strpcpy(dest, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - return size; -} - -size_t strscpy(char *dest, size_t size, const char *src) { - char *s; - - s = dest; - return strpcpy(&s, size, src); -} - -size_t strscpyl(char *dest, size_t size, const char *src, ...) { - va_list va; - char *s; - - va_start(va, src); - s = dest; - do { - size = strpcpy(&s, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h deleted file mode 100644 index 80ff58726b..0000000000 --- a/src/basic/strxcpyx.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include - -#include "macro.h" - -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c deleted file mode 100644 index db3405154e..0000000000 --- a/src/basic/syslog-util.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 deleted file mode 100644 index 5cb606a1bf..0000000000 --- a/src/basic/syslog-util.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 . -***/ - -#include - -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 deleted file mode 100644 index df56d85317..0000000000 --- a/src/basic/terminal-util.c +++ /dev/null @@ -1,1221 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "strv.h" -#include "terminal-util.h" -#include "time-util.h" -#include "util.h" - -static volatile unsigned cached_columns = 0; -static volatile unsigned cached_lines = 0; - -int chvt(int vt) { - _cleanup_close_ int fd; - - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - if (vt <= 0) { - int tiocl[2] = { - TIOCL_GETKMSGREDIRECT, - 0 - }; - - if (ioctl(fd, TIOCLINUX, tiocl) < 0) - return -errno; - - vt = tiocl[0] <= 0 ? 1 : tiocl[0]; - } - - if (ioctl(fd, VT_ACTIVATE, vt) < 0) - return -errno; - - return 0; -} - -int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { - struct termios old_termios, new_termios; - char c, line[LINE_MAX]; - - assert(f); - assert(ret); - - if (tcgetattr(fileno(f), &old_termios) >= 0) { - new_termios = old_termios; - - new_termios.c_lflag &= ~ICANON; - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; - - if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { - size_t k; - - if (t != USEC_INFINITY) { - if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { - tcsetattr(fileno(f), TCSADRAIN, &old_termios); - return -ETIMEDOUT; - } - } - - k = fread(&c, 1, 1, f); - - tcsetattr(fileno(f), TCSADRAIN, &old_termios); - - if (k <= 0) - return -EIO; - - if (need_nl) - *need_nl = c != '\n'; - - *ret = c; - return 0; - } - } - - if (t != USEC_INFINITY) { - if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) - return -ETIMEDOUT; - } - - errno = 0; - if (!fgets(line, sizeof(line), f)) - return errno > 0 ? -errno : -EIO; - - truncate_nl(line); - - if (strlen(line) != 1) - return -EBADMSG; - - if (need_nl) - *need_nl = false; - - *ret = line[0]; - return 0; -} - -int ask_char(char *ret, const char *replies, const char *text, ...) { - int r; - - assert(ret); - assert(replies); - assert(text); - - for (;;) { - va_list ap; - char c; - bool need_nl = true; - - if (colors_enabled()) - fputs(ANSI_HIGHLIGHT, stdout); - - va_start(ap, text); - vprintf(text, ap); - va_end(ap); - - if (colors_enabled()) - fputs(ANSI_NORMAL, stdout); - - fflush(stdout); - - r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl); - if (r < 0) { - - if (r == -EBADMSG) { - puts("Bad input, please try again."); - continue; - } - - putchar('\n'); - return r; - } - - if (need_nl) - putchar('\n'); - - if (strchr(replies, c)) { - *ret = c; - return 0; - } - - puts("Read unexpected character, please try again."); - } -} - -int ask_string(char **ret, const char *text, ...) { - assert(ret); - assert(text); - - for (;;) { - char line[LINE_MAX]; - va_list ap; - - if (colors_enabled()) - fputs(ANSI_HIGHLIGHT, stdout); - - va_start(ap, text); - vprintf(text, ap); - va_end(ap); - - if (colors_enabled()) - fputs(ANSI_NORMAL, stdout); - - fflush(stdout); - - errno = 0; - if (!fgets(line, sizeof(line), stdin)) - return errno > 0 ? -errno : -EIO; - - if (!endswith(line, "\n")) - putchar('\n'); - else { - char *s; - - if (isempty(line)) - continue; - - truncate_nl(line); - s = strdup(line); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; - } - } -} - -int reset_terminal_fd(int fd, bool switch_to_text) { - struct termios termios; - int r = 0; - - /* Set terminal to some sane defaults */ - - assert(fd >= 0); - - /* We leave locked terminal attributes untouched, so that - * Plymouth may set whatever it wants to set, and we don't - * interfere with that. */ - - /* Disable exclusive mode, just in case */ - (void) ioctl(fd, TIOCNXCL); - - /* Switch to text mode */ - if (switch_to_text) - (void) ioctl(fd, KDSETMODE, KD_TEXT); - - /* Enable console unicode mode */ - (void) ioctl(fd, KDSKBMODE, K_UNICODE); - - if (tcgetattr(fd, &termios) < 0) { - r = -errno; - goto finish; - } - - /* We only reset the stuff that matters to the software. How - * hardware is set up we don't touch assuming that somebody - * else will do that for us */ - - termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC); - termios.c_iflag |= ICRNL | IMAXBEL | IUTF8; - termios.c_oflag |= ONLCR; - termios.c_cflag |= CREAD; - termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE; - - termios.c_cc[VINTR] = 03; /* ^C */ - termios.c_cc[VQUIT] = 034; /* ^\ */ - termios.c_cc[VERASE] = 0177; - termios.c_cc[VKILL] = 025; /* ^X */ - termios.c_cc[VEOF] = 04; /* ^D */ - termios.c_cc[VSTART] = 021; /* ^Q */ - termios.c_cc[VSTOP] = 023; /* ^S */ - termios.c_cc[VSUSP] = 032; /* ^Z */ - termios.c_cc[VLNEXT] = 026; /* ^V */ - termios.c_cc[VWERASE] = 027; /* ^W */ - termios.c_cc[VREPRINT] = 022; /* ^R */ - termios.c_cc[VEOL] = 0; - termios.c_cc[VEOL2] = 0; - - termios.c_cc[VTIME] = 0; - termios.c_cc[VMIN] = 1; - - if (tcsetattr(fd, TCSANOW, &termios) < 0) - r = -errno; - -finish: - /* Just in case, flush all crap out */ - (void) tcflush(fd, TCIOFLUSH); - - return r; -} - -int reset_terminal(const char *name) { - _cleanup_close_ int fd = -1; - - /* We open the terminal with O_NONBLOCK here, to ensure we - * don't block on carrier if this is a terminal with carrier - * configured. */ - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - return reset_terminal_fd(fd, true); -} - -int open_terminal(const char *name, int mode) { - int fd, r; - unsigned c = 0; - - /* - * If a TTY is in the process of being closed opening it might - * cause EIO. This is horribly awful, but unlikely to be - * changed in the kernel. Hence we work around this problem by - * retrying a couple of times. - * - * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 - */ - - if (mode & O_CREAT) - return -EINVAL; - - for (;;) { - fd = open(name, mode, 0); - if (fd >= 0) - break; - - if (errno != EIO) - return -errno; - - /* Max 1s in total */ - if (c >= 20) - return -errno; - - usleep(50 * USEC_PER_MSEC); - c++; - } - - r = isatty(fd); - if (r < 0) { - safe_close(fd); - return -errno; - } - - if (!r) { - safe_close(fd); - return -ENOTTY; - } - - return fd; -} - -int acquire_terminal( - const char *name, - bool fail, - bool force, - bool ignore_tiocstty_eperm, - usec_t timeout) { - - int fd = -1, notify = -1, r = 0, wd = -1; - usec_t ts = 0; - - assert(name); - - /* We use inotify to be notified when the tty is closed. We - * create the watch before checking if we can actually acquire - * it, so that we don't lose any event. - * - * Note: strictly speaking this actually watches for the - * device being closed, it does *not* really watch whether a - * tty loses its controlling process. However, unless some - * rogue process uses TIOCNOTTY on /dev/tty *after* closing - * its tty otherwise this will not become a problem. As long - * as the administrator makes sure not configure any service - * on the same tty as an untrusted user this should not be a - * problem. (Which he probably should not do anyway.) */ - - if (timeout != USEC_INFINITY) - ts = now(CLOCK_MONOTONIC); - - if (!fail && !force) { - notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0)); - if (notify < 0) { - r = -errno; - goto fail; - } - - wd = inotify_add_watch(notify, name, IN_CLOSE); - if (wd < 0) { - r = -errno; - goto fail; - } - } - - for (;;) { - struct sigaction sa_old, sa_new = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - if (notify >= 0) { - r = flush_fd(notify); - if (r < 0) - goto fail; - } - - /* We pass here O_NOCTTY only so that we can check the return - * value TIOCSCTTY and have a reliable way to figure out if we - * successfully became the controlling process of the tty */ - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed - * if we already own the tty. */ - assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); - - /* First, try to get the tty */ - if (ioctl(fd, TIOCSCTTY, force) < 0) - r = -errno; - - assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); - - /* Sometimes, it makes sense to ignore TIOCSCTTY - * returning EPERM, i.e. when very likely we already - * are have this controlling terminal. */ - if (r < 0 && r == -EPERM && ignore_tiocstty_eperm) - r = 0; - - if (r < 0 && (force || fail || r != -EPERM)) - goto fail; - - if (r >= 0) - break; - - assert(!fail); - assert(!force); - assert(notify >= 0); - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - if (timeout != USEC_INFINITY) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - if (ts + timeout < n) { - r = -ETIMEDOUT; - goto fail; - } - - r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); - if (r < 0) - goto fail; - - if (r == 0) { - r = -ETIMEDOUT; - goto fail; - } - } - - l = read(notify, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - r = -errno; - goto fail; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if (e->wd != wd || !(e->mask & IN_CLOSE)) { - r = -EIO; - goto fail; - } - } - - break; - } - - /* We close the tty fd here since if the old session - * ended our handle will be dead. It's important that - * we do this after sleeping, so that we don't enter - * an endless loop. */ - fd = safe_close(fd); - } - - safe_close(notify); - - return fd; - -fail: - safe_close(fd); - safe_close(notify); - - return r; -} - -int release_terminal(void) { - static const struct sigaction sa_new = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - _cleanup_close_ int fd = -1; - struct sigaction sa_old; - int r = 0; - - fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed - * by our own TIOCNOTTY */ - assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); - - if (ioctl(fd, TIOCNOTTY) < 0) - r = -errno; - - assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); - - return r; -} - -int terminal_vhangup_fd(int fd) { - assert(fd >= 0); - - if (ioctl(fd, TIOCVHANGUP) < 0) - return -errno; - - return 0; -} - -int terminal_vhangup(const char *name) { - _cleanup_close_ int fd; - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - return terminal_vhangup_fd(fd); -} - -int vt_disallocate(const char *name) { - _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 - * entirely (including the scrollback buffer) */ - - if (!startswith(name, "/dev/")) - return -EINVAL; - - if (!tty_is_vc(name)) { - /* So this is not a VT. I guess we cannot deallocate - * it then. But let's at least clear the screen */ - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - loop_write(fd, - "\033[r" /* clear scrolling region */ - "\033[H" /* move home */ - "\033[2J", /* clear screen */ - 10, false); - return 0; - } - - if (!startswith(name, "/dev/tty")) - return -EINVAL; - - r = safe_atou(name+8, &u); - if (r < 0) - return r; - - if (u <= 0) - return -EINVAL; - - /* Try to deallocate */ - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - r = ioctl(fd, VT_DISALLOCATE, u); - fd = safe_close(fd); - - if (r >= 0) - return 0; - - if (errno != EBUSY) - return -errno; - - /* Couldn't deallocate, so let's clear it fully with - * scrollback */ - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - loop_write(fd, - "\033[r" /* clear scrolling region */ - "\033[H" /* move home */ - "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */ - 10, false); - return 0; -} - -int make_console_stdio(void) { - int fd, r; - - /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ - - fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY); - if (fd < 0) - return log_error_errno(fd, "Failed to acquire terminal: %m"); - - r = 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"); - - return 0; -} - -bool tty_is_vc(const char *tty) { - assert(tty); - - return vtnr_from_tty(tty) >= 0; -} - -bool tty_is_console(const char *tty) { - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - return streq(tty, "console"); -} - -int vtnr_from_tty(const char *tty) { - int i, r; - - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - if (!startswith(tty, "tty") ) - return -EINVAL; - - if (tty[3] < '0' || tty[3] > '9') - return -EINVAL; - - r = safe_atoi(tty+3, &i); - if (r < 0) - return r; - - if (i < 0 || i > 63) - return -EINVAL; - - return i; -} - -char *resolve_dev_console(char **active) { - char *tty; - - /* Resolve where /dev/console is pointing to, if /sys is actually ours - * (i.e. not read-only-mounted which is a sign for container setups) */ - - if (path_is_read_only_fs("/sys") > 0) - return NULL; - - if (read_one_line_file("/sys/class/tty/console/active", active) < 0) - return NULL; - - /* If multiple log outputs are configured the last one is what - * /dev/console points to */ - tty = strrchr(*active, ' '); - if (tty) - tty++; - else - tty = *active; - - if (streq(tty, "tty0")) { - char *tmp; - - /* Get the active VC (e.g. tty1) */ - if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) { - free(*active); - tty = *active = tmp; - } - } - - return tty; -} - -int get_kernel_consoles(char ***consoles) { - _cleanup_strv_free_ char **con = NULL; - _cleanup_free_ char *line = NULL; - const char *active; - int r; - - assert(consoles); - - r = read_one_line_file("/sys/class/tty/console/active", &line); - if (r < 0) - return r; - - active = line; - for (;;) { - _cleanup_free_ char *tty = NULL; - char *path; - - r = extract_first_word(&active, &tty, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - if (streq(tty, "tty0")) { - tty = mfree(tty); - r = read_one_line_file("/sys/class/tty/tty0/active", &tty); - if (r < 0) - return r; - } - - path = strappend("/dev/", tty); - if (!path) - return -ENOMEM; - - if (access(path, F_OK) < 0) { - log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path); - free(path); - continue; - } - - r = strv_consume(&con, path); - if (r < 0) - return r; - } - - if (strv_isempty(con)) { - log_debug("No devices found for system console"); - - r = strv_extend(&con, "/dev/console"); - if (r < 0) - return r; - } - - *consoles = con; - con = NULL; - return 0; -} - -bool tty_is_vc_resolve(const char *tty) { - _cleanup_free_ char *active = NULL; - - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - if (streq(tty, "console")) { - tty = resolve_dev_console(&active); - if (!tty) - return false; - } - - return tty_is_vc(tty); -} - -const char *default_term_for_tty(const char *tty) { - return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; -} - -int fd_columns(int fd) { - struct winsize ws = {}; - - if (ioctl(fd, TIOCGWINSZ, &ws) < 0) - return -errno; - - if (ws.ws_col <= 0) - return -EIO; - - return ws.ws_col; -} - -unsigned columns(void) { - const char *e; - int c; - - if (_likely_(cached_columns > 0)) - return cached_columns; - - c = 0; - e = getenv("COLUMNS"); - if (e) - (void) safe_atoi(e, &c); - - if (c <= 0) - c = fd_columns(STDOUT_FILENO); - - if (c <= 0) - c = 80; - - cached_columns = c; - return cached_columns; -} - -int fd_lines(int fd) { - struct winsize ws = {}; - - if (ioctl(fd, TIOCGWINSZ, &ws) < 0) - return -errno; - - if (ws.ws_row <= 0) - return -EIO; - - return ws.ws_row; -} - -unsigned lines(void) { - const char *e; - int l; - - if (_likely_(cached_lines > 0)) - return cached_lines; - - l = 0; - e = getenv("LINES"); - if (e) - (void) safe_atoi(e, &l); - - if (l <= 0) - l = fd_lines(STDOUT_FILENO); - - if (l <= 0) - l = 24; - - cached_lines = l; - return cached_lines; -} - -/* intended to be used as a SIGWINCH sighandler */ -void columns_lines_cache_reset(int signum) { - cached_columns = 0; - cached_lines = 0; -} - -bool on_tty(void) { - static int cached_on_tty = -1; - - if (_unlikely_(cached_on_tty < 0)) - cached_on_tty = isatty(STDOUT_FILENO) > 0; - - return cached_on_tty; -} - -int make_stdio(int fd) { - int r, s, t; - - assert(fd >= 0); - - r = dup2(fd, STDIN_FILENO); - s = dup2(fd, STDOUT_FILENO); - t = dup2(fd, STDERR_FILENO); - - if (fd >= 3) - safe_close(fd); - - if (r < 0 || s < 0 || t < 0) - return -errno; - - /* Explicitly unset O_CLOEXEC, since if fd was < 3, then - * dup2() was a NOP and the bit hence possibly set. */ - stdio_unset_cloexec(); - - return 0; -} - -int make_null_stdio(void) { - int null_fd; - - null_fd = open("/dev/null", O_RDWR|O_NOCTTY); - if (null_fd < 0) - return -errno; - - return make_stdio(null_fd); -} - -int getttyname_malloc(int fd, char **ret) { - size_t l = 100; - int r; - - assert(fd >= 0); - assert(ret); - - for (;;) { - char path[l]; - - r = ttyname_r(fd, path, sizeof(path)); - if (r == 0) { - const char *p; - char *c; - - p = startswith(path, "/dev/"); - c = strdup(p ?: path); - if (!c) - return -ENOMEM; - - *ret = c; - return 0; - } - - if (r != ERANGE) - return -r; - - l *= 2; - } - - return 0; -} - -int getttyname_harder(int fd, char **r) { - int k; - char *s = NULL; - - k = getttyname_malloc(fd, &s); - if (k < 0) - return k; - - if (streq(s, "tty")) { - free(s); - return get_ctty(0, NULL, r); - } - - *r = s; - return 0; -} - -int get_ctty_devnr(pid_t pid, dev_t *d) { - int r; - _cleanup_free_ char *line = NULL; - const char *p; - unsigned long ttynr; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%*d " /* ppid */ - "%*d " /* pgrp */ - "%*d " /* session */ - "%lu ", /* ttynr */ - &ttynr) != 1) - return -EIO; - - if (major(ttynr) == 0 && minor(ttynr) == 0) - return -ENXIO; - - if (d) - *d = (dev_t) ttynr; - - return 0; -} - -int get_ctty(pid_t pid, dev_t *_devnr, char **r) { - char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL; - _cleanup_free_ char *s = NULL; - const char *p; - dev_t devnr; - int k; - - assert(r); - - k = get_ctty_devnr(pid, &devnr); - if (k < 0) - return k; - - sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr)); - - k = readlink_malloc(fn, &s); - if (k < 0) { - - if (k != -ENOENT) - return k; - - /* This is an ugly hack */ - if (major(devnr) == 136) { - if (asprintf(&b, "pts/%u", minor(devnr)) < 0) - return -ENOMEM; - } else { - /* Probably something like the ptys which have no - * symlink in /dev/char. Let's return something - * vaguely useful. */ - - b = strdup(fn + 5); - if (!b) - return -ENOMEM; - } - } else { - if (startswith(s, "/dev/")) - p = s + 5; - else if (startswith(s, "../")) - p = s + 3; - else - p = s; - - b = strdup(p); - if (!b) - return -ENOMEM; - } - - *r = b; - if (_devnr) - *_devnr = devnr; - - return 0; -} - -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; - - /* Like ptsname(), but doesn't assume that the path is - * accessible in the local namespace. */ - - r = ioctl(pty, TIOCGPTN, &no); - if (r < 0) - return -errno; - - if (no < 0) - return -EIO; - - if (asprintf(ret, "/dev/pts/%i", no) < 0) - return -ENOMEM; - - 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 terminal_is_dumb(void) { - const char *e; - - if (!on_tty()) - return true; - - e = getenv("TERM"); - if (!e) - return true; - - return streq(e, "dumb"); -} - -bool colors_enabled(void) { - static int enabled = -1; - - if (_unlikely_(enabled < 0)) { - const char *colors; - - colors = getenv("SYSTEMD_COLORS"); - if (colors) - enabled = parse_boolean(colors) != 0; - else - enabled = !terminal_is_dumb(); - } - - return enabled; -} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h deleted file mode 100644 index 169ab772ff..0000000000 --- a/src/basic/terminal-util.h +++ /dev/null @@ -1,128 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#include "macro.h" -#include "time-util.h" - -#define ANSI_RED "\x1B[0;31m" -#define ANSI_GREEN "\x1B[0;32m" -#define ANSI_UNDERLINE "\x1B[0;4m" -#define ANSI_HIGHLIGHT "\x1B[0;1;39m" -#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m" -#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m" -#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" -#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m" -#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" -#define ANSI_NORMAL "\x1B[0m" - -#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" - -/* Set cursor to top left corner and clear screen */ -#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J" - -int reset_terminal_fd(int fd, bool switch_to_text); -int reset_terminal(const char *name); - -int open_terminal(const char *name, int mode); -int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout); -int release_terminal(void); - -int terminal_vhangup_fd(int fd); -int terminal_vhangup(const char *name); - -int chvt(int vt); - -int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); -int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4); -int ask_string(char **ret, const char *text, ...) _printf_(2, 3); - -int vt_disallocate(const char *name); - -char *resolve_dev_console(char **active); -int get_kernel_consoles(char ***consoles); -bool tty_is_vc(const char *tty); -bool tty_is_vc_resolve(const char *tty); -bool tty_is_console(const char *tty) _pure_; -int vtnr_from_tty(const char *tty); -const char *default_term_for_tty(const char *tty); - -int make_stdio(int fd); -int make_null_stdio(void); -int make_console_stdio(void); - -int fd_columns(int fd); -unsigned columns(void); -int fd_lines(int fd); -unsigned lines(void); -void columns_lines_cache_reset(int _unused_ signum); - -bool on_tty(void); -bool terminal_is_dumb(void); -bool colors_enabled(void); - -static inline const char *ansi_underline(void) { - return colors_enabled() ? ANSI_UNDERLINE : ""; -} - -static inline const char *ansi_highlight(void) { - return colors_enabled() ? ANSI_HIGHLIGHT : ""; -} - -static inline const char *ansi_highlight_underline(void) { - return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : ""; -} - -static inline const char *ansi_highlight_red(void) { - return colors_enabled() ? ANSI_HIGHLIGHT_RED : ""; -} - -static inline const char *ansi_highlight_green(void) { - return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : ""; -} - -static inline const char *ansi_highlight_yellow(void) { - return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : ""; -} - -static inline const char *ansi_highlight_blue(void) { - return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : ""; -} - -static inline const char *ansi_normal(void) { - return colors_enabled() ? ANSI_NORMAL : ""; -} - -int get_ctty_devnr(pid_t pid, dev_t *d); -int get_ctty(pid_t, dev_t *_devnr, char **r); - -int getttyname_malloc(int fd, char **r); -int getttyname_harder(int fd, char **r); - -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 deleted file mode 100644 index 24e681bf85..0000000000 --- a/src/basic/time-util.c +++ /dev/null @@ -1,1229 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" - -static nsec_t timespec_load_nsec(const struct timespec *ts); - -static clockid_t map_clock_id(clockid_t c) { - - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ - - switch (c) { - - case CLOCK_BOOTTIME_ALARM: - return CLOCK_BOOTTIME; - - case CLOCK_REALTIME_ALARM: - return CLOCK_REALTIME; - - default: - return c; - } -} - -usec_t now(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load(&ts); -} - -nsec_t now_nsec(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load_nsec(&ts); -} - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - - return ts; -} - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = u; - return ts; - } - - ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - - return ts; -} - -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; - - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = ts->boottime = u; - return ts; - } - - ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub(now(CLOCK_REALTIME), delta); - - return ts; -} - -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub(ts->realtime, delta); - ts->monotonic = usec_sub(ts->monotonic, delta); - - return ts; -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - return ts->realtime; - - case CLOCK_MONOTONIC: - return ts->monotonic; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - return ts->boottime; - - default: - return USEC_INFINITY; - } -} - -usec_t timespec_load(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) - return USEC_INFINITY; - - if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) ts->tv_sec * USEC_PER_SEC + - (usec_t) ts->tv_nsec / NSEC_PER_USEC; -} - -static nsec_t timespec_load_nsec(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) - return NSEC_INFINITY; - - if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) - return NSEC_INFINITY; - - return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; -} - -struct timespec *timespec_store(struct timespec *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = (long) -1; - return ts; - } - - ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); - - return ts; -} - -usec_t timeval_load(const struct timeval *tv) { - assert(tv); - - if (tv->tv_sec == (time_t) -1 && - tv->tv_usec == (suseconds_t) -1) - return USEC_INFINITY; - - if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) tv->tv_sec * USEC_PER_SEC + - (usec_t) tv->tv_usec; -} - -struct timeval *timeval_store(struct timeval *tv, usec_t u) { - assert(tv); - - if (u == USEC_INFINITY) { - tv->tv_sec = (time_t) -1; - tv->tv_usec = (suseconds_t) -1; - } else { - tv->tv_sec = (time_t) (u / USEC_PER_SEC); - tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); - } - - return tv; -} - -static char *format_timestamp_internal(char *buf, size_t l, usec_t t, - bool utc, bool us) { - struct tm tm; - time_t sec; - int k; - - assert(buf); - assert(l > 0); - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - sec = (time_t) (t / USEC_PER_SEC); - localtime_or_gmtime_r(&sec, &tm, utc); - - if (us) - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm); - else - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm); - - if (k <= 0) - return NULL; - if (us) { - snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); - if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0) - return NULL; - } - - return buf; -} - -char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, false); -} - -char *format_timestamp_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, false); -} - -char *format_timestamp_us(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, true); -} - -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, true); -} - -char *format_timestamp_relative(char *buf, size_t l, usec_t t) { - const char *s; - usec_t n, d; - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - n = now(CLOCK_REALTIME); - if (n > t) { - d = n - t; - s = "ago"; - } else { - d = t - n; - s = "left"; - } - - if (d >= USEC_PER_YEAR) - snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", - d / USEC_PER_YEAR, - (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); - else if (d >= USEC_PER_MONTH) - snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", - d / USEC_PER_MONTH, - (d % USEC_PER_MONTH) / USEC_PER_DAY, s); - else if (d >= USEC_PER_WEEK) - snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", - d / USEC_PER_WEEK, - (d % USEC_PER_WEEK) / USEC_PER_DAY, s); - else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); - else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); - else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); - else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); - else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); - else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); - else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); - else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); - else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); - else - snprintf(buf, l, "now"); - - buf[l-1] = 0; - return buf; -} - -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, - }; - - unsigned i; - char *p = buf; - bool something = false; - - assert(buf); - assert(l > 0); - - if (t == USEC_INFINITY) { - strncpy(p, "infinity", l-1); - p[l-1] = 0; - return p; - } - - if (t <= 0) { - strncpy(p, "0", l-1); - p[l-1] = 0; - return p; - } - - /* The result of this function can be parsed with parse_sec */ - - for (i = 0; i < ELEMENTSOF(table); i++) { - int k = 0; - size_t n; - bool done = false; - usec_t a, b; - - if (t <= 0) - break; - - if (t < accuracy && something) - break; - - if (t < table[i].usec) - continue; - - if (l <= 1) - break; - - a = t / table[i].usec; - b = t % table[i].usec; - - /* Let's see if we should shows this in dot notation */ - if (t < USEC_PER_MINUTE && b > 0) { - usec_t cc; - int j; - - j = 0; - for (cc = table[i].usec; cc > 1; cc /= 10) - j++; - - for (cc = accuracy; cc > 1; cc /= 10) { - b /= 10; - j--; - } - - if (j > 0) { - k = snprintf(p, l, - "%s"USEC_FMT".%0*llu%s", - p > buf ? " " : "", - a, - j, - (unsigned long long) b, - table[i].suffix); - - t = 0; - done = true; - } - } - - /* No? Then let's show it normally */ - if (!done) { - k = snprintf(p, l, - "%s"USEC_FMT"%s", - p > buf ? " " : "", - a, - table[i].suffix); - - t = b; - } - - n = MIN((size_t) k, l); - - l -= n; - p += n; - - something = true; - } - - *p = 0; - - return buf; -} - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - unsigned long long a, b; - - assert(value); - assert(t); - - if (sscanf(value, "%llu %llu", &a, &b) != 2) { - log_debug("Failed to parse dual timestamp value \"%s\": %m", value); - return -EINVAL; - } - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - -int parse_timestamp(const char *t, usec_t *usec) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Sunday", 0 }, - { "Sun", 0 }, - { "Monday", 1 }, - { "Mon", 1 }, - { "Tuesday", 2 }, - { "Tue", 2 }, - { "Wednesday", 3 }, - { "Wed", 3 }, - { "Thursday", 4 }, - { "Thu", 4 }, - { "Friday", 5 }, - { "Fri", 5 }, - { "Saturday", 6 }, - { "Sat", 6 }, - }; - - const char *k; - const char *utc; - struct tm tm, copy; - time_t x; - usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1; - unsigned i; - - /* - * Allowed syntaxes: - * - * 2012-09-22 16:34:22 - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) - * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) - * +5min - * -5days - * @2147483647 (seconds since epoch) - * - */ - - assert(t); - assert(usec); - - if (t[0] == '@') - return parse_sec(t + 1, usec); - - ret = now(CLOCK_REALTIME); - - if (streq(t, "now")) - goto finish; - - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; - - goto finish; - - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); - - 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; - - if (!startswith_no_case(t, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - if (t[skip] != ' ') - continue; - - weekday = day_nr[i].nr; - t += skip + 1; - break; - } - - copy = tm; - k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - 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) { - 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 from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - 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 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 from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M:%S", &tm); - 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 from_tm; - } - - return -EINVAL; - -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 + x_usec; - -finish: - ret += plus; - if (ret > minus) - ret -= minus; - else - ret = 0; - - *usec = ret; - - return 0; -} - -static char* extract_multiplier(char *p, usec_t *multiplier) { - 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 }, - { "minutes", USEC_PER_MINUTE }, - { "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 }, - }; - unsigned i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].usec; - return e; - } - } - - return p; -} - -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - const char *p, *s; - usec_t r = 0; - bool something = false; - - assert(t); - assert(usec); - assert(default_unit > 0); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *usec = USEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - char *e; - unsigned n = 0; - usec_t multiplier = default_unit, k; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - - if (z < 0) - return -ERANGE; - - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - p = extract_multiplier(e, &multiplier); - - something = true; - - k = (usec_t) z * multiplier; - - for (; n > 0; n--) - k /= 10; - - r += (usec_t) l * multiplier + k; - } - - *usec = r; - - 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; - nsec_t nsec; - } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, - { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ - }; - - const char *p, *s; - nsec_t r = 0; - bool something = false; - - assert(t); - assert(nsec); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *nsec = NSEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - char *e; - unsigned i, n = 0; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - errno = 0; - l = strtoll(p, &e, 10); - - if (errno > 0) - return -errno; - - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - - if (z < 0) - return -ERANGE; - - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; - - for (; n > 0; n--) - k /= 10; - - r += (nsec_t) l * table[i].nsec + k; - p = e + strlen(table[i].suffix); - - something = true; - break; - } - - if (i >= ELEMENTSOF(table)) - return -EINVAL; - - } - - *nsec = r; - - return 0; -} - -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - if (txc.status & STA_UNSYNC) - return false; - - return true; -} - -int get_timezones(char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; - - assert(ret); - - zones = strv_new("UTC", NULL); - if (!zones) - return -ENOMEM; - - n_allocated = 2; - n_zones = 1; - - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); - if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { - char *p, *w; - size_t k; - - p = strstrip(l); - - if (isempty(p) || *p == '#') - continue; - - /* Skip over country code */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Skip over coordinates */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Found timezone name */ - k = strcspn(p, WHITESPACE); - if (k <= 0) - continue; - - w = strndup(p, k); - if (!w) - return -ENOMEM; - - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); - return -ENOMEM; - } - - zones[n_zones++] = w; - zones[n_zones] = NULL; - } - - strv_sort(zones); - - } else if (errno != ENOENT) - return -errno; - - *ret = zones; - zones = NULL; - - return 0; -} - -bool timezone_is_valid(const char *name) { - bool slash = false; - const char *p, *t; - struct stat st; - - if (isempty(name)) - return false; - - if (name[0] == '/') - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - t = strjoina("/usr/share/zoneinfo/", name); - if (stat(t, &st) < 0) - return false; - - if (!S_ISREG(st.st_mode)) - return false; - - return true; -} - -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - -bool clock_supported(clockid_t clock) { - struct timespec ts; - - switch (clock) { - - case CLOCK_MONOTONIC: - case CLOCK_REALTIME: - return true; - - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - /* fall through, after checking the cached value for CLOCK_BOOTTIME. */ - - default: - /* For everything else, check properly */ - return clock_gettime(clock, &ts) >= 0; - } -} - -int get_timezone(char **tz) { - _cleanup_free_ char *t = NULL; - const char *e; - char *z; - int r; - - r = readlink_malloc("/etc/localtime", &t); - if (r < 0) - return r; /* returns EINVAL if not a symlink */ - - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); - if (!e) - return -EINVAL; - - if (!timezone_is_valid(e)) - return -EINVAL; - - z = strdup(e); - if (!z) - return -ENOMEM; - - *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 deleted file mode 100644 index 1b058f0e49..0000000000 --- a/src/basic/time-util.h +++ /dev/null @@ -1,178 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include - -typedef uint64_t usec_t; -typedef uint64_t nsec_t; - -#define NSEC_FMT "%" PRIu64 -#define USEC_FMT "%" PRIu64 - -#include "macro.h" - -typedef struct dual_timestamp { - usec_t realtime; - usec_t monotonic; -} dual_timestamp; - -typedef struct triple_timestamp { - usec_t realtime; - usec_t monotonic; - usec_t boottime; -} triple_timestamp; - -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) - -#define MSEC_PER_SEC 1000ULL -#define USEC_PER_SEC ((usec_t) 1000000ULL) -#define USEC_PER_MSEC ((usec_t) 1000ULL) -#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) -#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) -#define NSEC_PER_USEC ((nsec_t) 1000ULL) - -#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) -#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) -#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) -#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) -#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) -#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) -#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) -#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) -#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) -#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) -#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) -#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) - -#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */ -#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 -#define FORMAT_TIMESPAN_MAX 64 - -#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) - -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) - -usec_t now(clockid_t clock); -nsec_t now_nsec(clockid_t clock); - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts); -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts); -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); - -#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) - -#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) - -static inline bool dual_timestamp_is_set(dual_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); -} - -static inline bool triple_timestamp_is_set(triple_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || - (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); - -usec_t timespec_load(const struct timespec *ts) _pure_; -struct timespec *timespec_store(struct timespec *ts, usec_t u); - -usec_t timeval_load(const struct timeval *tv) _pure_; -struct timeval *timeval_store(struct timeval *tv, usec_t u); - -char *format_timestamp(char *buf, size_t l, usec_t t); -char *format_timestamp_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_us(char *buf, size_t l, usec_t t); -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_relative(char *buf, size_t l, usec_t t); -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - -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); - -int get_timezones(char ***l); -bool timezone_is_valid(const char *name); - -bool clock_boottime_supported(void); -bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); - -#define xstrftime(buf, fmt, tm) \ - assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ - "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); - -static inline usec_t usec_add(usec_t a, usec_t b) { - usec_t c; - - /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't - * overflow. */ - - c = a + b; - if (c < a || c < b) /* overflow check */ - return USEC_INFINITY; - - return c; -} - -static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); - - if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ - return USEC_INFINITY; - - if (timestamp < (usec_t) delta) - return 0; - - return timestamp - delta; -} diff --git a/src/basic/umask-util.h b/src/basic/umask-util.h deleted file mode 100644 index 359d87d27c..0000000000 --- a/src/basic/umask-util.h +++ /dev/null @@ -1,46 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#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 deleted file mode 100644 index 7c847a3ccb..0000000000 --- a/src/basic/unaligned.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include -#include - -/* BE */ - -static inline uint16_t unaligned_read_be16(const void *_u) { - const uint8_t *u = _u; - - return (((uint16_t) u[0]) << 8) | - ((uint16_t) u[1]); -} - -static inline uint32_t unaligned_read_be32(const void *_u) { - const uint8_t *u = _u; - - return (((uint32_t) unaligned_read_be16(u)) << 16) | - ((uint32_t) unaligned_read_be16(u + 2)); -} - -static inline uint64_t unaligned_read_be64(const void *_u) { - const uint8_t *u = _u; - - return (((uint64_t) unaligned_read_be32(u)) << 32) | - ((uint64_t) unaligned_read_be32(u + 4)); -} - -static inline void unaligned_write_be16(void *_u, uint16_t a) { - uint8_t *u = _u; - - u[0] = (uint8_t) (a >> 8); - u[1] = (uint8_t) a; -} - -static inline void unaligned_write_be32(void *_u, uint32_t a) { - uint8_t *u = _u; - - unaligned_write_be16(u, (uint16_t) (a >> 16)); - unaligned_write_be16(u + 2, (uint16_t) a); -} - -static inline void unaligned_write_be64(void *_u, uint64_t a) { - uint8_t *u = _u; - - 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)); -} - -#if __BYTE_ORDER == __BIG_ENDIAN -#define unaligned_read_ne16 unaligned_read_be16 -#define unaligned_read_ne32 unaligned_read_be32 -#define unaligned_read_ne64 unaligned_read_be64 - -#define unaligned_write_ne16 unaligned_write_be16 -#define unaligned_write_ne32 unaligned_write_be32 -#define unaligned_write_ne64 unaligned_write_be64 -#else -#define unaligned_read_ne16 unaligned_read_le16 -#define unaligned_read_ne32 unaligned_read_le32 -#define unaligned_read_ne64 unaligned_read_le64 - -#define unaligned_write_ne16 unaligned_write_le16 -#define unaligned_write_ne32 unaligned_write_le32 -#define unaligned_write_ne64 unaligned_write_le64 -#endif diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c deleted file mode 100644 index fe883b95c7..0000000000 --- a/src/basic/unit-name.c +++ /dev/null @@ -1,1049 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bus-label.h" -#include "glob-util.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" - -/* Characters valid in a unit name. */ -#define VALID_CHARS \ - DIGITS \ - LETTERS \ - ":-_.\\" - -/* The same, but also permits the single @ character that may appear */ -#define VALID_CHARS_WITH_AT \ - "@" \ - VALID_CHARS - -/* All chars valid in a unit name glob */ -#define VALID_CHARS_GLOB \ - VALID_CHARS_WITH_AT \ - "[]!-*?" - -bool unit_name_is_valid(const char *n, UnitNameFlags flags) { - const char *e, *i, *at; - - assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0); - - if (_unlikely_(flags == 0)) - return false; - - if (isempty(n)) - return false; - - if (strlen(n) >= UNIT_NAME_MAX) - return false; - - e = strrchr(n, '.'); - if (!e || e == n) - return false; - - if (unit_type_from_string(e + 1) < 0) - return false; - - for (i = n, at = NULL; i < e; i++) { - - if (*i == '@' && !at) - at = i; - - if (!strchr("@" VALID_CHARS, *i)) - return false; - } - - if (at == n) - return false; - - if (flags & UNIT_NAME_PLAIN) - if (!at) - return true; - - if (flags & UNIT_NAME_INSTANCE) - if (at && e > at + 1) - return true; - - if (flags & UNIT_NAME_TEMPLATE) - if (at && e == at + 1) - return true; - - return false; -} - -bool unit_prefix_is_valid(const char *p) { - - /* We don't allow additional @ in the prefix string */ - - if (isempty(p)) - return false; - - return in_charset(p, VALID_CHARS); -} - -bool unit_instance_is_valid(const char *i) { - - /* The max length depends on the length of the string, so we - * don't really check this here. */ - - if (isempty(i)) - return false; - - /* We allow additional @ in the instance string, we do not - * allow them in the prefix! */ - - return in_charset(i, "@" VALID_CHARS); -} - -bool unit_suffix_is_valid(const char *s) { - if (isempty(s)) - return false; - - if (s[0] != '.') - return false; - - if (unit_type_from_string(s + 1) < 0) - return false; - - return true; -} - -int unit_name_to_prefix(const char *n, char **ret) { - const char *p; - char *s; - - assert(n); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - p = strchr(n, '@'); - if (!p) - p = strrchr(n, '.'); - - assert_se(p); - - s = strndup(n, p - n); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_to_instance(const char *n, char **instance) { - const char *p, *d; - char *i; - - assert(n); - assert(instance); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - /* Everything past the first @ and before the last . is the instance */ - p = strchr(n, '@'); - if (!p) { - *instance = NULL; - return 0; - } - - p++; - - d = strrchr(p, '.'); - if (!d) - return -EINVAL; - - i = strndup(p, d-p); - if (!i) - return -ENOMEM; - - *instance = i; - return 1; -} - -int unit_name_to_prefix_and_instance(const char *n, char **ret) { - const char *d; - char *s; - - assert(n); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - d = strrchr(n, '.'); - if (!d) - return -EINVAL; - - s = strndup(n, d - n); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -UnitType unit_name_to_type(const char *n) { - const char *e; - - assert(n); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return _UNIT_TYPE_INVALID; - - assert_se(e = strrchr(n, '.')); - - return unit_type_from_string(e + 1); -} - -int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { - char *e, *s; - size_t a, b; - - assert(n); - assert(suffix); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - assert_se(e = strrchr(n, '.')); - - a = e - n; - b = strlen(suffix); - - s = new(char, a + b + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(s, n, a), suffix); - *ret = s; - - return 0; -} - -int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) { - char *s; - - assert(prefix); - assert(suffix); - assert(ret); - - if (!unit_prefix_is_valid(prefix)) - return -EINVAL; - - if (instance && !unit_instance_is_valid(instance)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - if (!instance) - s = strappend(prefix, suffix); - else - s = strjoin(prefix, "@", instance, suffix, NULL); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -static char *do_escape_char(char c, char *t) { - assert(t); - - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(c >> 4); - *(t++) = hexchar(c); - - return t; -} - -static char *do_escape(const char *f, char *t) { - assert(f); - assert(t); - - /* do not create units with a leading '.', like for "/.dotdir" mount points */ - if (*f == '.') { - t = do_escape_char(*f, t); - f++; - } - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - -char *unit_name_escape(const char *f) { - char *r, *t; - - assert(f); - - r = new(char, strlen(f)*4+1); - if (!r) - return NULL; - - t = do_escape(f, r); - *t = 0; - - return r; -} - -int unit_name_unescape(const char *f, char **ret) { - _cleanup_free_ char *r = NULL; - char *t; - - assert(f); - - r = strdup(f); - if (!r) - return -ENOMEM; - - for (t = r; *f; f++) { - if (*f == '-') - *(t++) = '/'; - else if (*f == '\\') { - int a, b; - - if (f[1] != 'x') - return -EINVAL; - - a = unhexchar(f[2]); - if (a < 0) - return -EINVAL; - - b = unhexchar(f[3]); - if (b < 0) - return -EINVAL; - - *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b); - f += 3; - } else - *(t++) = *f; - } - - *t = 0; - - *ret = r; - r = NULL; - - return 0; -} - -int unit_name_path_escape(const char *f, char **ret) { - char *p, *s; - - assert(f); - assert(ret); - - p = strdupa(f); - if (!p) - return -ENOMEM; - - path_kill_slashes(p); - - if (STR_IN_SET(p, "/", "")) - s = strdup("-"); - else { - char *e; - - if (!path_is_safe(p)) - return -EINVAL; - - /* Truncate trailing slashes */ - e = endswith(p, "/"); - if (e) - *e = 0; - - /* Truncate leading slashes */ - if (p[0] == '/') - p++; - - s = unit_name_escape(p); - } - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_path_unescape(const char *f, char **ret) { - char *s; - int r; - - assert(f); - - if (isempty(f)) - return -EINVAL; - - if (streq(f, "-")) { - s = strdup("/"); - if (!s) - return -ENOMEM; - } else { - char *w; - - r = unit_name_unescape(f, &w); - if (r < 0) - return r; - - /* Don't accept trailing or leading slashes */ - if (startswith(w, "/") || endswith(w, "/")) { - free(w); - return -EINVAL; - } - - /* Prefix a slash again */ - s = strappend("/", w); - free(w); - if (!s) - return -ENOMEM; - - if (!path_is_safe(s)) { - free(s); - return -EINVAL; - } - } - - if (ret) - *ret = s; - else - free(s); - - return 0; -} - -int unit_name_replace_instance(const char *f, const char *i, char **ret) { - const char *p, *e; - char *s; - size_t a, b; - - assert(f); - assert(i); - assert(ret); - - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - if (!unit_instance_is_valid(i)) - return -EINVAL; - - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); - - a = p - f; - b = strlen(i); - - s = new(char, a + 1 + b + strlen(e) + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); - - *ret = s; - return 0; -} - -int unit_name_template(const char *f, char **ret) { - const char *p, *e; - char *s; - size_t a; - - assert(f); - assert(ret); - - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); - - a = p - f; - - s = new(char, a + 1 + strlen(e) + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(s, f, a + 1), e); - - *ret = s; - return 0; -} - -int unit_name_from_path(const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s = NULL; - int r; - - assert(path); - assert(suffix); - assert(ret); - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - r = unit_name_path_escape(path, &p); - if (r < 0) - return r; - - s = strappend(p, suffix); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s; - int r; - - assert(prefix); - assert(path); - assert(suffix); - assert(ret); - - if (!unit_prefix_is_valid(prefix)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - r = unit_name_path_escape(path, &p); - if (r < 0) - return r; - - s = strjoin(prefix, "@", p, suffix, NULL); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_to_path(const char *name, char **ret) { - _cleanup_free_ char *prefix = NULL; - int r; - - assert(name); - - r = unit_name_to_prefix(name, &prefix); - if (r < 0) - return r; - - return unit_name_path_unescape(prefix, ret); -} - -char *unit_dbus_path_from_name(const char *name) { - _cleanup_free_ char *e = NULL; - - assert(name); - - e = bus_label_escape(name); - if (!e) - return NULL; - - return strappend("/org/freedesktop/systemd1/unit/", e); -} - -int unit_name_from_dbus_path(const char *path, char **name) { - const char *e; - char *n; - - e = startswith(path, "/org/freedesktop/systemd1/unit/"); - if (!e) - return -EINVAL; - - n = bus_label_unescape(e); - if (!n) - return -ENOMEM; - - *name = n; - return 0; -} - -const char* unit_dbus_interface_from_type(UnitType t) { - - static const char *const table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", - [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", - [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", - [UNIT_TARGET] = "org.freedesktop.systemd1.Target", - [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", - [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", - [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", - [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", - [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", - [UNIT_PATH] = "org.freedesktop.systemd1.Path", - [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", - [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", - }; - - if (t < 0) - return NULL; - if (t >= _UNIT_TYPE_MAX) - return NULL; - - return table[t]; -} - -const char *unit_dbus_interface_from_name(const char *name) { - UnitType t; - - t = unit_name_to_type(name); - if (t < 0) - return NULL; - - return unit_dbus_interface_from_type(t); -} - -static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { - const char *valid_chars; - - assert(f); - assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB)); - assert(t); - - /* We'll only escape the obvious characters here, to play - * safe. */ - - valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT; - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(valid_chars, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - -/** - * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, - * /blah/blah is converted to blah-blah.mount, anything else is left alone, - * except that @suffix is appended if a valid unit suffix is not present. - * - * 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; - int r; - - assert(name); - assert(suffix); - assert(ret); - - if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - /* Already a fully valid unit name? If so, no mangling is necessary... */ - if (unit_name_is_valid(name, UNIT_NAME_ANY)) - goto good; - - /* Already a fully valid globbing expression? If so, no mangling is necessary either... */ - if (allow_globs == UNIT_NAME_GLOB && - string_is_glob(name) && - in_charset(name, VALID_CHARS_GLOB)) - goto good; - - if (is_device_path(name)) { - r = unit_name_from_path(name, ".device", ret); - if (r >= 0) - return 1; - if (r != -EINVAL) - return r; - } - - if (path_is_absolute(name)) { - r = unit_name_from_path(name, ".mount", ret); - if (r >= 0) - return 1; - if (r != -EINVAL) - return r; - } - - s = new(char, strlen(name) * 4 + strlen(suffix) + 1); - if (!s) - return -ENOMEM; - - t = do_escape_mangle(name, allow_globs, s); - *t = 0; - - /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a - * valid glob. */ - if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0) - strcpy(t, suffix); - - *ret = s; - return 1; - -good: - s = strdup(name); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int slice_build_parent_slice(const char *slice, char **ret) { - char *s, *dash; - int r; - - assert(slice); - assert(ret); - - if (!slice_name_is_valid(slice)) - return -EINVAL; - - if (streq(slice, "-.slice")) { - *ret = NULL; - return 0; - } - - s = strdup(slice); - if (!s) - return -ENOMEM; - - dash = strrchr(s, '-'); - if (dash) - strcpy(dash, ".slice"); - else { - r = free_and_strdup(&s, "-.slice"); - if (r < 0) { - free(s); - return r; - } - } - - *ret = s; - return 1; -} - -int slice_build_subslice(const char *slice, const char*name, char **ret) { - char *subslice; - - assert(slice); - assert(name); - assert(ret); - - if (!slice_name_is_valid(slice)) - return -EINVAL; - - if (!unit_prefix_is_valid(name)) - return -EINVAL; - - if (streq(slice, "-.slice")) - subslice = strappend(name, ".slice"); - else { - char *e; - - assert_se(e = endswith(slice, ".slice")); - - subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); - if (!subslice) - return -ENOMEM; - - stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice"); - } - - *ret = subslice; - return 0; -} - -bool slice_name_is_valid(const char *name) { - const char *p, *e; - bool dash = false; - - if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) - return false; - - if (streq(name, "-.slice")) - return true; - - e = endswith(name, ".slice"); - if (!e) - return false; - - for (p = name; p < e; p++) { - - if (*p == '-') { - - /* Don't allow initial dash */ - if (p == name) - return false; - - /* Don't allow multiple dashes */ - if (dash) - return false; - - dash = true; - } else - dash = false; - } - - /* Don't allow trailing hash */ - if (dash) - return false; - - return true; -} - -static const char* const unit_type_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "service", - [UNIT_SOCKET] = "socket", - [UNIT_BUSNAME] = "busname", - [UNIT_TARGET] = "target", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SWAP] = "swap", - [UNIT_TIMER] = "timer", - [UNIT_PATH] = "path", - [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); - -static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { - [UNIT_STUB] = "stub", - [UNIT_LOADED] = "loaded", - [UNIT_NOT_FOUND] = "not-found", - [UNIT_ERROR] = "error", - [UNIT_MERGED] = "merged", - [UNIT_MASKED] = "masked" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); - -static const char* const unit_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_REQUISITE] = "Requisite", - [UNIT_WANTS] = "Wants", - [UNIT_BINDS_TO] = "BindsTo", - [UNIT_PART_OF] = "PartOf", - [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_REQUISITE_OF] = "RequisiteOf", - [UNIT_WANTED_BY] = "WantedBy", - [UNIT_BOUND_BY] = "BoundBy", - [UNIT_CONSISTS_OF] = "ConsistsOf", - [UNIT_CONFLICTS] = "Conflicts", - [UNIT_CONFLICTED_BY] = "ConflictedBy", - [UNIT_BEFORE] = "Before", - [UNIT_AFTER] = "After", - [UNIT_ON_FAILURE] = "OnFailure", - [UNIT_TRIGGERS] = "Triggers", - [UNIT_TRIGGERED_BY] = "TriggeredBy", - [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", - [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", - [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", - [UNIT_REFERENCES] = "References", - [UNIT_REFERENCED_BY] = "ReferencedBy", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h deleted file mode 100644 index 44eadf0347..0000000000 --- a/src/basic/unit-name.h +++ /dev/null @@ -1,367 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -#define UNIT_NAME_MAX 256 - -typedef enum UnitType { - UNIT_SERVICE = 0, - UNIT_SOCKET, - UNIT_BUSNAME, - UNIT_TARGET, - UNIT_DEVICE, - UNIT_MOUNT, - UNIT_AUTOMOUNT, - UNIT_SWAP, - UNIT_TIMER, - UNIT_PATH, - UNIT_SLICE, - UNIT_SCOPE, - _UNIT_TYPE_MAX, - _UNIT_TYPE_INVALID = -1 -} UnitType; - -typedef enum UnitLoadState { - UNIT_STUB = 0, - UNIT_LOADED, - UNIT_NOT_FOUND, - UNIT_ERROR, - UNIT_MERGED, - UNIT_MASKED, - _UNIT_LOAD_STATE_MAX, - _UNIT_LOAD_STATE_INVALID = -1 -} 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_REQUISITE, - UNIT_WANTS, - UNIT_BINDS_TO, - UNIT_PART_OF, - - /* Inverse of the above */ - UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */ - UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */ - UNIT_WANTED_BY, /* inverse of 'wants' */ - UNIT_BOUND_BY, /* inverse of 'binds_to' */ - UNIT_CONSISTS_OF, /* inverse of 'part_of' */ - - /* Negative dependencies */ - UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */ - UNIT_CONFLICTED_BY, - - /* Order */ - UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ - UNIT_AFTER, - - /* On Failure */ - UNIT_ON_FAILURE, - - /* Triggers (i.e. a socket triggers a service) */ - UNIT_TRIGGERS, - UNIT_TRIGGERED_BY, - - /* Propagate reloads */ - UNIT_PROPAGATES_RELOAD_TO, - UNIT_RELOAD_PROPAGATED_FROM, - - /* Joins namespace of */ - UNIT_JOINS_NAMESPACE_OF, - - /* Reference information for GC logic */ - UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ - UNIT_REFERENCED_BY, - - _UNIT_DEPENDENCY_MAX, - _UNIT_DEPENDENCY_INVALID = -1 -} UnitDependency; - -typedef enum UnitNameFlags { - UNIT_NAME_PLAIN = 1, /* Allow foo.service */ - UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */ - UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */ - UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE, -} UnitNameFlags; - -bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_; -bool unit_prefix_is_valid(const char *p) _pure_; -bool unit_instance_is_valid(const char *i) _pure_; -bool unit_suffix_is_valid(const char *s) _pure_; - -static inline int unit_prefix_and_instance_is_valid(const char *p) { - /* For prefix+instance and instance the same rules apply */ - return unit_instance_is_valid(p); -} - -int unit_name_to_prefix(const char *n, char **prefix); -int unit_name_to_instance(const char *n, char **instance); -int unit_name_to_prefix_and_instance(const char *n, char **ret); - -UnitType unit_name_to_type(const char *n) _pure_; - -int unit_name_change_suffix(const char *n, const char *suffix, char **ret); - -int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); - -char *unit_name_escape(const char *f); -int unit_name_unescape(const char *f, char **ret); -int unit_name_path_escape(const char *f, char **ret); -int unit_name_path_unescape(const char *f, char **ret); - -int unit_name_replace_instance(const char *f, const char *i, char **ret); - -int unit_name_template(const char *f, char **ret); - -int unit_name_from_path(const char *path, const char *suffix, char **ret); -int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret); -int unit_name_to_path(const char *name, char **ret); - -char *unit_dbus_path_from_name(const char *name); -int unit_name_from_dbus_path(const char *path, char **name); - -const char* unit_dbus_interface_from_type(UnitType t); -const char *unit_dbus_interface_from_name(const char *name); - -typedef enum UnitNameMangle { - UNIT_NAME_NOGLOB, - UNIT_NAME_GLOB, -} UnitNameMangle; - -int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret); - -static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) { - return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret); -} - -int slice_build_parent_slice(const char *slice, char **ret); -int slice_build_subslice(const char *slice, const char*name, char **subslice); -bool slice_name_is_valid(const char *name); - -const char *unit_type_to_string(UnitType i) _const_; -UnitType unit_type_from_string(const char *s) _pure_; - -const char *unit_load_state_to_string(UnitLoadState i) _const_; -UnitLoadState unit_load_state_from_string(const char *s) _pure_; - -const char *unit_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 deleted file mode 100644 index e9d668ddfc..0000000000 --- a/src/basic/user-util.c +++ /dev/null @@ -1,481 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "missing.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 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 deleted file mode 100644 index 8026eca3f4..0000000000 --- a/src/basic/user-util.h +++ /dev/null @@ -1,70 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -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)) - -static inline bool userns_supported(void) { - return access("/proc/self/uid_map", F_OK) >= 0; -} diff --git a/src/basic/utf8.c b/src/basic/utf8.c deleted file mode 100644 index 6eae2b983d..0000000000 --- a/src/basic/utf8.c +++ /dev/null @@ -1,409 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Kay Sievers - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -bool unichar_is_valid(char32_t ch) { - - if (ch >= 0x110000) /* End of unicode space */ - return false; - if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ - return false; - if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ - return false; - if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ - return false; - - return true; -} - -static bool unichar_is_control(char32_t ch) { - - /* - 0 to ' '-1 is the C0 range. - DEL=0x7F, and DEL+1 to 0x9F is C1 range. - '\t' is in C0 range, but more or less harmless and commonly used. - */ - - return (ch < ' ' && ch != '\t' && ch != '\n') || - (0x7F <= ch && ch <= 0x9F); -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) { - unsigned char c; - - assert(str); - - c = (unsigned char) str[0]; - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - - return 0; -} - -/* decode one unicode char */ -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { - char32_t unichar; - int len, i; - - assert(str); - - len = utf8_encoded_expected_len(str); - - switch (len) { - case 1: - *ret_unichar = (char32_t)str[0]; - return 0; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (char32_t)str[0] & 0x0f; - break; - case 4: - unichar = (char32_t)str[0] & 0x07; - break; - case 5: - unichar = (char32_t)str[0] & 0x03; - break; - case 6: - unichar = (char32_t)str[0] & 0x01; - break; - default: - return -EINVAL; - } - - for (i = 1; i < len; i++) { - if (((char32_t)str[i] & 0xc0) != 0x80) - return -EINVAL; - unichar <<= 6; - unichar |= (char32_t)str[i] & 0x3f; - } - - *ret_unichar = unichar; - - return 0; -} - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const char *p; - - assert(str); - - for (p = str; length;) { - int encoded_len, r; - char32_t val; - - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) - return false; - - r = utf8_encoded_to_unichar(p, &val); - if (r < 0 || - unichar_is_control(val) || - (!newline && val == '\n')) - return false; - - length -= encoded_len; - p += encoded_len; - } - - return true; -} - -const char *utf8_is_valid(const char *str) { - const uint8_t *p; - - assert(str); - - for (p = (const uint8_t*) str; *p; ) { - int len; - - len = utf8_encoded_valid_unichar((const char *)p); - if (len < 0) - return NULL; - - p += len; - } - - return str; -} - -char *utf8_escape_invalid(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - s = mempcpy(s, str, len); - str += len; - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *utf8_escape_non_printable(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - if (utf8_is_printable(str, len)) { - s = mempcpy(s, str, len); - str += len; - } else { - while (len > 0) { - *(s++) = '\\'; - *(s++) = 'x'; - *(s++) = hexchar((int) *str >> 4); - *(s++) = hexchar((int) *str); - - str += 1; - len--; - } - } - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *ascii_is_valid(const char *str) { - const char *p; - - assert(str); - - for (p = str; *p; p++) - if ((unsigned char) *p >= 128) - return NULL; - - return (char*) str; -} - -/** - * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 - * @out_utf8: output buffer of at least 4 bytes or NULL - * @g: UCS-4 character to encode - * - * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. - * The length of the character is returned. It is not zero-terminated! If the - * output buffer is NULL, only the length is returned. - * - * Returns: The length in bytes that the UTF-8 representation does or would - * occupy. - */ -size_t utf8_encode_unichar(char *out_utf8, char32_t g) { - - if (g < (1 << 7)) { - if (out_utf8) - out_utf8[0] = g & 0x7f; - return 1; - } else if (g < (1 << 11)) { - if (out_utf8) { - out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); - out_utf8[1] = 0x80 | (g & 0x3f); - } - return 2; - } else if (g < (1 << 16)) { - if (out_utf8) { - out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); - out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[2] = 0x80 | (g & 0x3f); - } - return 3; - } else if (g < (1 << 21)) { - if (out_utf8) { - out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); - out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); - out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[3] = 0x80 | (g & 0x3f); - } - return 4; - } - - return 0; -} - -char *utf16_to_utf8(const void *s, size_t length) { - const uint8_t *f; - char *r, *t; - - r = new(char, (length * 4 + 1) / 2 + 1); - if (!r) - return NULL; - - f = s; - t = r; - - while (f < (const uint8_t*) s + length) { - char16_t w1, w2; - - /* see RFC 2781 section 2.2 */ - - w1 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_surrogate(w1)) { - t += utf8_encode_unichar(t, w1); - - continue; - } - - if (utf16_is_trailing_surrogate(w1)) - continue; - else if (f >= (const uint8_t*) s + length) - break; - - w2 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_trailing_surrogate(w2)) { - f -= 2; - continue; - } - - t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); - } - - *t = 0; - return r; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(char32_t unichar) { - - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - - return 6; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) { - int len, i, r; - char32_t unichar; - - assert(str); - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -EINVAL; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -EINVAL; - - r = utf8_encoded_to_unichar(str, &unichar); - if (r < 0) - return r; - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -EINVAL; - - /* check if value has valid range */ - if (!unichar_is_valid(unichar)) - return -EINVAL; - - return len; -} diff --git a/src/basic/utf8.h b/src/basic/utf8.h deleted file mode 100644 index f9b9c9468b..0000000000 --- a/src/basic/utf8.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "macro.h" -#include "missing.h" - -#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" -#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" - -bool unichar_is_valid(char32_t c); - -const char *utf8_is_valid(const char *s) _pure_; -char *ascii_is_valid(const char *s) _pure_; - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) - -char *utf8_escape_invalid(const char *s); -char *utf8_escape_non_printable(const char *str); - -size_t utf8_encode_unichar(char *out_utf8, char32_t g); -char *utf16_to_utf8(const void *s, size_t length); - -int utf8_encoded_valid_unichar(const char *str); -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); - -static inline bool utf16_is_surrogate(char16_t c) { - return (0xd800 <= c && c <= 0xdfff); -} - -static inline bool utf16_is_trailing_surrogate(char16_t c) { - return (0xdc00 <= c && c <= 0xdfff); -} - -static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { - return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; -} diff --git a/src/basic/util.c b/src/basic/util.c deleted file mode 100644 index 9d66d28eb7..0000000000 --- a/src/basic/util.c +++ /dev/null @@ -1,876 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "build.h" -#include "cgroup-util.h" -#include "def.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "hashmap.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "set.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "user-util.h" -#include "util.h" - -/* Put this test here for a lack of better place */ -assert_cc(EAGAIN == EWOULDBLOCK); - -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -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; - char **directory; - - /* We fork this all off from a child process so that we can - * somewhat cleanly make use of SIGALRM to set a time limit */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pids = hashmap_new(NULL); - if (!pids) - return log_oom(); - - seen = set_new(&string_hash_ops); - if (!seen) - return log_oom(); - - STRV_FOREACH(directory, directories) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(*directory); - if (!d) { - if (errno == ENOENT) - continue; - - return log_error_errno(errno, "Failed to open directory %s: %m", *directory); - } - - FOREACH_DIRENT(de, d, break) { - _cleanup_free_ char *path = NULL; - pid_t pid; - int r; - - if (!dirent_is_file(de)) - continue; - - if (set_contains(seen, de->d_name)) { - log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); - continue; - } - - r = set_put_strdup(seen, de->d_name); - if (r < 0) - return log_oom(); - - path = strjoin(*directory, "/", de->d_name, NULL); - if (!path) - return log_oom(); - - if (null_or_empty_path(path)) { - log_debug("%s is empty (a mask).", path); - continue; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - continue; - } else if (pid == 0) { - char *_argv[2]; - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; - } else - argv[0] = path; - - execv(path, argv); - return log_error_errno(errno, "Failed to execute %s: %m", path); - } - - log_debug("Spawned %s as " PID_FMT ".", path, pid); - - r = hashmap_put(pids, PID_TO_PTR(pid), path); - if (r < 0) - return log_oom(); - path = NULL; - } - } - - /* Abort execution of this process after the timout. We simply - * rely on SIGALRM as default action terminating the process, - * and turn on alarm(). */ - - if (timeout != USEC_INFINITY) - alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - - while (!hashmap_isempty(pids)) { - _cleanup_free_ char *path = NULL; - pid_t pid; - - pid = PTR_TO_PID(hashmap_first_key(pids)); - assert(pid > 0); - - path = hashmap_remove(pids, PID_TO_PTR(pid)); - assert(path); - - wait_for_terminate_and_warn(path, pid, true); - } - - return 0; -} - -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { - pid_t executor_pid; - int r; - char *name; - char **dirs = (char**) directories; - - assert(!strv_isempty(dirs)); - - name = basename(dirs[0]); - assert(!isempty(name)); - - /* Executes all binaries in the directories in parallel and waits - * for them to finish. Optionally a timeout is applied. If a file - * with the same name exists in more than one directory, the - * earliest one wins. */ - - executor_pid = fork(); - if (executor_pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return; - - } else if (executor_pid == 0) { - r = do_execute(dirs, timeout, argv); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); - } - - wait_for_terminate_and_warn(name, executor_pid, true); -} - -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} - -int socket_from_display(const char *display, char **path) { - size_t k; - char *f, *c; - - assert(display); - assert(path); - - if (!display_is_local(display)) - return -EINVAL; - - k = strspn(display+1, "0123456789"); - - f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); - if (!f) - return -ENOMEM; - - c = stpcpy(f, "/tmp/.X11-unix/X"); - memcpy(c, display+1, k); - c[k] = 0; - - *path = f; - - return 0; -} - -int block_get_whole_disk(dev_t d, dev_t *ret) { - char *p, *s; - int r; - unsigned n, m; - - assert(ret); - - /* If it has a queue this is good enough for us */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = d; - return 0; - } - - /* If it is a partition find the originating device */ - if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r < 0) - return -ENOENT; - - /* Get parent dev_t */ - if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) - return -ENOMEM; - - r = read_one_line_file(p, &s); - free(p); - - if (r < 0) - return r; - - r = sscanf(s, "%u:%u", &m, &n); - free(s); - - if (r != 2) - return -EINVAL; - - /* Only return this if it is really good enough for us. */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = makedev(m, n); - return 0; - } - - return -ENOENT; -} - -bool kexec_loaded(void) { - bool loaded = false; - char *s; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { - if (s[0] == '1') - loaded = true; - free(s); - } - return loaded; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -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; - sigset_t ss, saved_ss; - unsigned n, i; - va_list ap; - char **l; - - assert(pid); - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when - * we go away */ - - parent_pid = getpid(); - - /* First we temporarily block all signals, so that the new - * child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the agent. */ - assert_se(sigfillset(&ss) >= 0); - assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); - - agent_pid = fork(); - if (agent_pid < 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - return -errno; - } - - if (agent_pid != 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - *pid = agent_pid; - return 0; - } - - /* In the child: - * - * Make sure the agent goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Make sure we actually can kill the agent, if we need to, in - * case somebody invoked us from a shell script that trapped - * SIGTERM or so... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Check whether our parent died before we were able - * to set the death signal and unblock the signals */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - /* Don't leak fds to the agent */ - close_all_fds(except, n_except); - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (fd > STDERR_FILENO) - close(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = alloca(sizeof(char *) * (n + 1)); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - -bool in_initrd(void) { - struct statfs s; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* We make two checks here: - * - * 1. the flag file /etc/initrd-release must exist - * 2. the root file system must be a memory file system - * - * The second check is extra paranoia, since misdetecting an - * initrd can have bad bad consequences due the initrd - * emptying when transititioning to the main systemd. - */ - - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); - - return saved_in_initrd; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -/* 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) { - size_t l, u, idx; - const void *p; - int comparison; - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (void *)(((const char *) base) + (idx * size)); - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - return NULL; -} - -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - for (;;) { - struct dirent *de; - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - errno = 0; - de = readdir(d); - if (!de && errno > 0) - return -errno; - - if (!de) - break; - - if (hidden_or_backup_file(de->d_name)) - continue; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (errno == ENOENT || errno == ENOTDIR) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (!machine_name_is_valid(machine)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user"); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - if (cg_get_root_path(&root) < 0) - return mem; - - if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value)) - return mem; - - if (safe_atou64(value, &lim) < 0) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - -#if SIZEOF_PID_T == 4 -#define TASKS_MAX ((uint64_t) (INT32_MAX-1)) -#elif SIZEOF_PID_T == 2 -#define TASKS_MAX ((uint64_t) (INT16_MAX-1)) -#else -#error "Unknown pid_t size" -#endif - - _cleanup_free_ char *value = NULL, *root = NULL; - uint64_t a = TASKS_MAX, b = TASKS_MAX; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum value for the pid_t type - * b) the cgroups pids_max attribute for the system - * c) the kernel's configure maximum PID value - * - * And then pick the smallest of the three */ - - if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0) - (void) safe_atou64(value, &a); - - if (cg_get_root_path(&root) >= 0) { - value = mfree(value); - - if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) - (void) safe_atou64(value, &b); - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - -int update_reboot_parameter_and_warn(const char *param) { - int r; - - if (isempty(param)) { - if (unlink("/run/systemd/reboot-param") < 0) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); - } - - return 0; - } - - RUN_WITH_UMASK(0022) { - r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_warning_errno(r, "Failed to write reboot parameter file: %m"); - } - - return 0; -} - -int version(void) { - puts(PACKAGE_STRING "\n" - SYSTEMD_FEATURES); - return 0; -} diff --git a/src/basic/util.h b/src/basic/util.h deleted file mode 100644 index 44497dcd78..0000000000 --- a/src/basic/util.h +++ /dev/null @@ -1,192 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "formats-util.h" -#include "macro.h" -#include "missing.h" -#include "time-util.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) - -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - -static inline const char* true_false(bool b) { - return b ? "true" : "false"; -} - -static inline const char* one_zero(bool b) { - return b ? "1" : "0"; -} - -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); - -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; -int socket_from_display(const char *display, char **path); - -int block_get_whole_disk(dev_t d, dev_t *ret); - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#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)) - -extern int saved_argc; -extern char **saved_argv; - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); - -bool in_initrd(void); -void in_initrd_force(bool value); - -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), - void *arg); - -/** - * 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 memcpy requires src to be nonnull. We do nothing if n is 0. - */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -int on_ac_power(void); - -#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; -} - -static inline void _reset_errno_(int *saved_errno) { - errno = *saved_errno; -} - -#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - -static inline unsigned u64log2(uint64_t n) { -#if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; -#else -#error "Wut?" -#endif -} - -static inline unsigned u32ctz(uint32_t n) { -#if __SIZEOF_INT__ == 4 - return __builtin_ctz(n); -#else -#error "Wut?" -#endif -} - -static inline unsigned log2i(int x) { - assert(x > 0); - - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) - return 0; - - return log2u(x - 1) + 1; -} - -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); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - -int update_reboot_parameter_and_warn(const char *param); - -int version(void); diff --git a/src/basic/verbs.c b/src/basic/verbs.c deleted file mode 100644 index d9cdb38d65..0000000000 --- a/src/basic/verbs.c +++ /dev/null @@ -1,101 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "verbs.h" -#include "virt.h" - -int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { - const Verb *verb; - const char *name; - unsigned i; - int left; - - assert(verbs); - assert(verbs[0].dispatch); - assert(argc >= 0); - assert(argv); - assert(argc >= optind); - - left = argc - optind; - name = argv[optind]; - - for (i = 0;; i++) { - bool found; - - /* At the end of the list? */ - if (!verbs[i].dispatch) { - if (name) - log_error("Unknown operation %s.", name); - else - log_error("Requires operation parameter."); - return -EINVAL; - } - - if (name) - found = streq(name, verbs[i].verb); - else - found = !!(verbs[i].flags & VERB_DEFAULT); - - if (found) { - verb = &verbs[i]; - break; - } - } - - assert(verb); - - if (!name) - left = 1; - - if (verb->min_args != VERB_ANY && - (unsigned) left < verb->min_args) { - log_error("Too few arguments."); - return -EINVAL; - } - - if (verb->max_args != VERB_ANY && - (unsigned) left > verb->max_args) { - log_error("Too many arguments."); - return -EINVAL; - } - - if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); - return 0; - } - - if (name) - return verb->dispatch(left, argv + optind, userdata); - else { - char* fake[2] = { - (char*) verb->verb, - NULL - }; - - return verb->dispatch(1, fake, userdata); - } -} diff --git a/src/basic/verbs.h b/src/basic/verbs.h deleted file mode 100644 index 7b5e18510f..0000000000 --- a/src/basic/verbs.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#define VERB_ANY ((unsigned) -1) -#define VERB_DEFAULT 1U -#define VERB_NOCHROOT 2U - -typedef struct { - const char *verb; - unsigned min_args, max_args; - unsigned flags; - int (* const dispatch)(int argc, char *argv[], void *userdata); -} Verb; - -int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/basic/virt.c b/src/basic/virt.c deleted file mode 100644 index dace1f4328..0000000000 --- a/src/basic/virt.c +++ /dev/null @@ -1,516 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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" - -static int detect_vm_cpuid(void) { - - /* CPUID is an x86 specific interface. */ -#if defined(__i386__) || defined(__x86_64__) - - static const struct { - const char *cpuid; - int id; - } cpuid_vendor_table[] = { - { "XenVMMXenVMM", VIRTUALIZATION_XEN }, - { "KVMKVMKVM", VIRTUALIZATION_KVM }, - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - { "VMwareVMware", VIRTUALIZATION_VMWARE }, - /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ - { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, - }; - - uint32_t eax, ecx; - bool hypervisor; - - /* http://lwn.net/Articles/301888/ */ - -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#define REG_b "rbx" -#endif - - /* First detect whether there is a hypervisor */ - eax = 1; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); - - hypervisor = !!(ecx & 0x80000000U); - - if (hypervisor) { - union { - uint32_t sig32[3]; - char text[13]; - } sig = {}; - unsigned j; - - /* There is a hypervisor, see what it is */ - eax = 0x40000000U; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " mov %%ebx, %1 \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) - : "0" (eax) - ); - - log_debug("Virtualization found, CPUID=%s", sig.text); - - for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++) - if (streq(sig.text, cpuid_vendor_table[j].cpuid)) - return cpuid_vendor_table[j].id; - - return VIRTUALIZATION_VM_OTHER; - } -#endif - log_debug("No virtualization found in CPUID"); - - return VIRTUALIZATION_NONE; -} - -static int detect_vm_device_tree(void) { -#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) - _cleanup_free_ char *hvtype = NULL; - int r; - - r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype); - if (r == -ENOENT) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - - dir = opendir("/proc/device-tree"); - if (!dir) { - if (errno == ENOENT) { - log_debug_errno(errno, "/proc/device-tree: %m"); - return VIRTUALIZATION_NONE; - } - return -errno; - } - - FOREACH_DIRENT(dent, dir, return -errno) - if (strstr(dent->d_name, "fw-cfg")) { - log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name); - return VIRTUALIZATION_QEMU; - } - - log_debug("No virtualization found in /proc/device-tree/*"); - return VIRTUALIZATION_NONE; - } else if (r < 0) - return r; - - log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); - if (streq(hvtype, "linux,kvm")) - return VIRTUALIZATION_KVM; - else if (strstr(hvtype, "xen")) - return VIRTUALIZATION_XEN; - else - return VIRTUALIZATION_VM_OTHER; -#else - log_debug("This platform does not support /proc/device-tree"); - return VIRTUALIZATION_NONE; -#endif -} - -static int detect_vm_dmi(void) { -#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" - }; - - static const struct { - 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 }, - { "VMW", VIRTUALIZATION_VMWARE }, - { "innotek GmbH", VIRTUALIZATION_ORACLE }, - { "Xen", VIRTUALIZATION_XEN }, - { "Bochs", VIRTUALIZATION_BOCHS }, - { "Parallels", VIRTUALIZATION_PARALLELS }, - }; - unsigned i; - int r; - - for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { - _cleanup_free_ char *s = NULL; - unsigned j; - - r = read_one_line_file(dmi_vendors[i], &s); - if (r < 0) { - if (r == -ENOENT) - continue; - - return r; - } - - - - for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++) - if (startswith(s, dmi_vendor_table[j].vendor)) { - log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]); - return dmi_vendor_table[j].id; - } - } -#endif - - log_debug("No virtualization found in DMI"); - - return VIRTUALIZATION_NONE; -} - -static int detect_vm_xen(void) { - /* Check for Dom0 will be executed later in detect_vm_xen_dom0 - Thats why we dont check the content of /proc/xen/capabilities here. */ - if (access("/proc/xen/capabilities", F_OK) < 0) { - log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); - return VIRTUALIZATION_NONE; - } - - log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); - return VIRTUALIZATION_XEN; - -} - -static bool detect_vm_xen_dom0(void) { - _cleanup_free_ char *domcap = NULL; - char *cap, *i; - int r; - - r = read_one_line_file("/proc/xen/capabilities", &domcap); - if (r == -ENOENT) { - log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); - return false; - } - if (r < 0) - return r; - - i = domcap; - while ((cap = strsep(&i, ","))) - if (streq(cap, "control_d")) - break; - if (!cap) { - log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); - return false; - } - - log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); - return true; -} - -static int detect_vm_hypervisor(void) { - _cleanup_free_ char *hvtype = NULL; - int r; - - r = read_one_line_file("/sys/hypervisor/type", &hvtype); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; - if (r < 0) - return r; - - log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); - - if (streq(hvtype, "xen")) - return VIRTUALIZATION_XEN; - else - return VIRTUALIZATION_VM_OTHER; -} - -static int detect_vm_uml(void) { - _cleanup_free_ char *cpuinfo_contents = NULL; - int r; - - /* Detect User-Mode Linux by reading /proc/cpuinfo */ - r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); - if (r < 0) - return r; - - if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { - log_debug("UML virtualization found in /proc/cpuinfo"); - return VIRTUALIZATION_UML; - } - - log_debug("No virtualization found in /proc/cpuinfo."); - return VIRTUALIZATION_NONE; -} - -static int detect_vm_zvm(void) { - -#if defined(__s390__) - _cleanup_free_ char *t = NULL; - int r; - - r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; - if (r < 0) - return r; - - log_debug("Virtualization %s found in /proc/sysinfo", t); - if (streq(t, "z/VM")) - return VIRTUALIZATION_ZVM; - else - return VIRTUALIZATION_KVM; -#else - log_debug("This platform does not support /proc/sysinfo"); - return VIRTUALIZATION_NONE; -#endif -} - -/* Returns a short identifier for the various VM implementations */ -int detect_vm(void) { - static thread_local int cached_found = _VIRTUALIZATION_INVALID; - int r; - - if (cached_found >= 0) - return cached_found; - - /* 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. - * - * 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_cpuid(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - /* 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) - goto finish; - - r = detect_vm_hypervisor(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_device_tree(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_uml(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_zvm(); - if (r < 0) - return r; - -finish: - /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others. - * In order to detect the Dom0 as not virtualization we need to - * double-check it */ - if (r == VIRTUALIZATION_XEN && detect_vm_xen_dom0()) - r = VIRTUALIZATION_NONE; - - cached_found = r; - log_debug("Found VM virtualization %s", virtualization_to_string(r)); - return r; -} - -int detect_container(void) { - - static const struct { - const char *value; - int id; - } value_table[] = { - { "lxc", VIRTUALIZATION_LXC }, - { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, - { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, - { "docker", VIRTUALIZATION_DOCKER }, - { "rkt", VIRTUALIZATION_RKT }, - }; - - static thread_local int cached_found = _VIRTUALIZATION_INVALID; - _cleanup_free_ char *m = NULL; - const char *e = NULL; - unsigned j; - int r; - - if (cached_found >= 0) - return cached_found; - - /* /proc/vz exists in container and outside of the container, - * /proc/bc only outside of the container. */ - if (access("/proc/vz", F_OK) >= 0 && - access("/proc/bc", F_OK) < 0) { - r = VIRTUALIZATION_OPENVZ; - goto finish; - } - - if (getpid() == 1) { - /* If we are PID 1 we can just check our own - * environment variable */ - - e = getenv("container"); - if (isempty(e)) { - r = VIRTUALIZATION_NONE; - goto finish; - } - } else { - - /* Otherwise, PID 1 dropped this information into a - * file in /run. This is better than accessing - * /proc/1/environ, since we don't need CAP_SYS_PTRACE - * for that. */ - - r = read_one_line_file("/run/systemd/container", &m); - if (r == -ENOENT) { - - /* Fallback for cases where PID 1 was not - * systemd (for example, cases where - * init=/bin/sh is used. */ - - r = getenv_for_pid(1, "container", &m); - if (r <= 0) { - - /* If that didn't work, give up, - * assume no container manager. - * - * Note: This means we still cannot - * detect containers if init=/bin/sh - * is passed but privileges dropped, - * as /proc/1/environ is only readable - * with privileges. */ - - r = VIRTUALIZATION_NONE; - goto finish; - } - } - if (r < 0) - return r; - - e = m; - } - - for (j = 0; j < ELEMENTSOF(value_table); j++) - if (streq(e, value_table[j].value)) { - r = value_table[j].id; - goto finish; - } - - r = VIRTUALIZATION_CONTAINER_OTHER; - -finish: - log_debug("Found container virtualization %s", virtualization_to_string(r)); - cached_found = r; - return r; -} - -int detect_virtualization(void) { - int r; - - r = detect_container(); - if (r == 0) - r = detect_vm(); - - return r; -} - -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", - [VIRTUALIZATION_QEMU] = "qemu", - [VIRTUALIZATION_BOCHS] = "bochs", - [VIRTUALIZATION_XEN] = "xen", - [VIRTUALIZATION_UML] = "uml", - [VIRTUALIZATION_VMWARE] = "vmware", - [VIRTUALIZATION_ORACLE] = "oracle", - [VIRTUALIZATION_MICROSOFT] = "microsoft", - [VIRTUALIZATION_ZVM] = "zvm", - [VIRTUALIZATION_PARALLELS] = "parallels", - [VIRTUALIZATION_VM_OTHER] = "vm-other", - - [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", - [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", - [VIRTUALIZATION_LXC] = "lxc", - [VIRTUALIZATION_OPENVZ] = "openvz", - [VIRTUALIZATION_DOCKER] = "docker", - [VIRTUALIZATION_RKT] = "rkt", - [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", -}; - -DEFINE_STRING_TABLE_LOOKUP(virtualization, int); diff --git a/src/basic/virt.h b/src/basic/virt.h deleted file mode 100644 index a538f07f6b..0000000000 --- a/src/basic/virt.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" - -enum { - VIRTUALIZATION_NONE = 0, - - VIRTUALIZATION_VM_FIRST, - VIRTUALIZATION_KVM = VIRTUALIZATION_VM_FIRST, - VIRTUALIZATION_QEMU, - VIRTUALIZATION_BOCHS, - VIRTUALIZATION_XEN, - VIRTUALIZATION_UML, - VIRTUALIZATION_VMWARE, - VIRTUALIZATION_ORACLE, - VIRTUALIZATION_MICROSOFT, - VIRTUALIZATION_ZVM, - VIRTUALIZATION_PARALLELS, - VIRTUALIZATION_VM_OTHER, - VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, - - VIRTUALIZATION_CONTAINER_FIRST, - VIRTUALIZATION_SYSTEMD_NSPAWN = VIRTUALIZATION_CONTAINER_FIRST, - VIRTUALIZATION_LXC_LIBVIRT, - VIRTUALIZATION_LXC, - VIRTUALIZATION_OPENVZ, - VIRTUALIZATION_DOCKER, - VIRTUALIZATION_RKT, - VIRTUALIZATION_CONTAINER_OTHER, - VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, - - _VIRTUALIZATION_MAX, - _VIRTUALIZATION_INVALID = -1 -}; - -static inline bool VIRTUALIZATION_IS_VM(int x) { - return x >= VIRTUALIZATION_VM_FIRST && x <= VIRTUALIZATION_VM_LAST; -} - -static inline bool VIRTUALIZATION_IS_CONTAINER(int x) { - return x >= VIRTUALIZATION_CONTAINER_FIRST && x <= VIRTUALIZATION_CONTAINER_LAST; -} - -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 deleted file mode 100644 index 595688ed93..0000000000 --- a/src/basic/web-util.c +++ /dev/null @@ -1,76 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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/basic/web-util.h b/src/basic/web-util.h deleted file mode 100644 index e6bb6b53f5..0000000000 --- a/src/basic/web-util.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -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 deleted file mode 100644 index 8256899eda..0000000000 --- a/src/basic/xattr-util.c +++ /dev/null @@ -1,200 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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_CLOEXEC|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/basic/xattr-util.h b/src/basic/xattr-util.h deleted file mode 100644 index 6fa097bf7e..0000000000 --- a/src/basic/xattr-util.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "time-util.h" - -int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink); -int fgetxattr_malloc(int fd, const char *name, char **value); - -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); diff --git a/src/basic/xml.c b/src/basic/xml.c deleted file mode 100644 index 1dbeac7324..0000000000 --- a/src/basic/xml.c +++ /dev/null @@ -1,255 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "macro.h" -#include "string-util.h" -#include "xml.h" - -enum { - STATE_NULL, - STATE_TEXT, - STATE_TAG, - STATE_ATTRIBUTE, -}; - -static void inc_lines(unsigned *line, const char *s, size_t n) { - const char *p = s; - - if (!line) - return; - - for (;;) { - const char *f; - - f = memchr(p, '\n', n); - if (!f) - return; - - n -= (f - p) + 1; - p = f + 1; - (*line)++; - } -} - -/* We don't actually do real XML here. We only read a simplistic - * subset, that is a bit less strict that XML and lacks all the more - * complex features, like entities, or namespaces. However, we do - * support some HTML5-like simplifications */ - -int xml_tokenize(const char **p, char **name, void **state, unsigned *line) { - const char *c, *e, *b; - char *ret; - int t; - - assert(p); - assert(*p); - assert(name); - assert(state); - - t = PTR_TO_INT(*state); - c = *p; - - if (t == STATE_NULL) { - if (line) - *line = 1; - t = STATE_TEXT; - } - - for (;;) { - if (*c == 0) - return XML_END; - - switch (t) { - - case STATE_TEXT: { - int x; - - e = strchrnul(c, '<'); - if (e > c) { - /* More text... */ - ret = strndup(c, e - c); - if (!ret) - return -ENOMEM; - - inc_lines(line, c, e - c); - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_TEXT); - - return XML_TEXT; - } - - assert(*e == '<'); - b = c + 1; - - if (startswith(b, "!--")) { - /* A comment */ - e = strstr(b + 3, "-->"); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 3 - b); - - c = e + 3; - continue; - } - - if (*b == '?') { - /* Processing instruction */ - - e = strstr(b + 1, "?>"); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 2 - b); - - c = e + 2; - continue; - } - - if (*b == '!') { - /* DTD */ - - e = strchr(b + 1, '>'); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 1 - b); - - c = e + 1; - continue; - } - - if (*b == '/') { - /* A closing tag */ - x = XML_TAG_CLOSE; - b++; - } else - x = XML_TAG_OPEN; - - e = strpbrk(b, WHITESPACE "/>"); - if (!e) - return -EINVAL; - - ret = strndup(b, e - b); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_TAG); - - return x; - } - - case STATE_TAG: - - b = c + strspn(c, WHITESPACE); - if (*b == 0) - return -EINVAL; - - inc_lines(line, c, b - c); - - e = b + strcspn(b, WHITESPACE "=/>"); - if (e > b) { - /* An attribute */ - - ret = strndup(b, e - b); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_ATTRIBUTE); - - return XML_ATTRIBUTE_NAME; - } - - if (startswith(b, "/>")) { - /* An empty tag */ - - *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */ - *p = b + 2; - *state = INT_TO_PTR(STATE_TEXT); - - return XML_TAG_CLOSE_EMPTY; - } - - if (*b != '>') - return -EINVAL; - - c = b + 1; - t = STATE_TEXT; - continue; - - case STATE_ATTRIBUTE: - - if (*c == '=') { - c++; - - if (*c == '\'' || *c == '\"') { - /* Tag with a quoted value */ - - e = strchr(c+1, *c); - if (!e) - return -EINVAL; - - inc_lines(line, c, e - c); - - ret = strndup(c+1, e - c - 1); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e + 1; - *state = INT_TO_PTR(STATE_TAG); - - return XML_ATTRIBUTE_VALUE; - - } - - /* Tag with a value without quotes */ - - b = strpbrk(c, WHITESPACE ">"); - if (!b) - b = c; - - ret = strndup(c, b - c); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = b; - *state = INT_TO_PTR(STATE_TAG); - return XML_ATTRIBUTE_VALUE; - } - - t = STATE_TAG; - continue; - } - - } - - assert_not_reached("Bad state"); -} diff --git a/src/basic/xml.h b/src/basic/xml.h deleted file mode 100644 index 41cb69f0dc..0000000000 --- a/src/basic/xml.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -enum { - XML_END, - XML_TEXT, - XML_TAG_OPEN, - XML_TAG_CLOSE, - XML_TAG_CLOSE_EMPTY, - XML_ATTRIBUTE_NAME, - XML_ATTRIBUTE_VALUE, -}; - -int xml_tokenize(const char **p, char **name, void **state, unsigned *line); diff --git a/src/binfmt/Makefile b/src/binfmt/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/binfmt/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c deleted file mode 100644 index eeef04fb1c..0000000000 --- a/src/binfmt/binfmt.c +++ /dev/null @@ -1,203 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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" - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d"); - -static int delete_rule(const char *rule) { - _cleanup_free_ char *x = NULL, *fn = NULL; - char *e; - - assert(rule[0]); - - x = strdup(rule); - if (!x) - return log_oom(); - - e = strchrnul(x+1, x[0]); - *e = 0; - - fn = strappend("/proc/sys/fs/binfmt_misc/", x+1); - if (!fn) - return log_oom(); - - return write_string_file(fn, "-1", 0); -} - -static int apply_rule(const char *rule) { - int r; - - delete_rule(rule); - - r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0); - if (r < 0) - return log_error_errno(r, "Failed to add binary format: %m"); - - return 0; -} - -static int apply_file(const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); - } - - log_debug("apply: %s", path); - for (;;) { - char l[LINE_MAX], *p; - int k; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - p = strstrip(l); - if (!*p) - continue; - if (strchr(COMMENTS "\n", *p)) - continue; - - k = apply_rule(p); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Registers binary formats.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, k; - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = apply_file(argv[i], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); - goto finish; - } - - /* Flush out all rules */ - write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0); - - STRV_FOREACH(f, files) { - k = apply_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/boot/Makefile b/src/boot/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/boot/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c deleted file mode 100644 index 056a0790bd..0000000000 --- a/src/boot/bootctl.c +++ /dev/null @@ -1,1143 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013-2015 Kay Sievers - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "blkid-util.h" -#include "dirent-util.h" -#include "efivars.h" -#include "fd-util.h" -#include "fileio.h" -#include "locale-util.h" -#include "rm-rf.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; - struct stat st, st2; - _cleanup_free_ char *t = NULL; - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - int r; - const char *v, *t2; - - if (statfs(p, &sfs) < 0) - return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p); - - if (sfs.f_type != 0x4d44) { - log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); - return -ENODEV; - } - - if (stat(p, &st) < 0) - return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p); - - if (major(st.st_dev) == 0) { - log_error("Block device node of %p is invalid.", p); - return -ENODEV; - } - - t2 = strjoina(p, "/.."); - r = stat(t2, &st2); - if (r < 0) - return log_error_errno(errno, "Failed to determine block device node of parent of \"%s\": %m", p); - - if (st.st_dev == st2.st_dev) { - log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); - return -ENODEV; - } - - r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); - if (r < 0) - return log_oom(); - - errno = 0; - b = blkid_new_probe_from_filename(t); - if (!b) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "Failed to open file system \"%s\": %m", p); - } - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) { - log_error("File system \"%s\" is ambiguous.", p); - return -ENODEV; - } else if (r == 1) { - log_error("File system \"%s\" does not contain a label.", p); - return -ENODEV; - } else if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe file system \"%s\": %m", p); - } - - errno = 0; - r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p); - } - - if (!streq(v, "vfat")) { - log_error("File system \"%s\" is not FAT.", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p); - } - - if (!streq(v, "gpt")) { - log_error("File system \"%s\" is not on a GPT partition table.", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p); - } - - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { - log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p); - } - - r = sd_id128_from_string(v, uuid); - if (r < 0) { - log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); - return -EIO; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition number \"%s\": m", p); - } - *part = strtoul(v, NULL, 10); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p); - } - *pstart = strtoul(v, NULL, 10); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p); - } - *psize = strtoul(v, NULL, 10); - - return 0; -} - -/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ -static int get_file_version(int fd, char **v) { - struct stat st; - char *buf; - const char *s, *e; - char *x = NULL; - int r = 0; - - assert(fd >= 0); - assert(v); - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_size < 27) - return 0; - - buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (buf == MAP_FAILED) - return -errno; - - s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17); - if (!s) - goto finish; - s += 17; - - e = memmem(s, st.st_size - (s - buf), " ####", 5); - if (!e || e - s < 3) { - log_error("Malformed version string."); - r = -EINVAL; - goto finish; - } - - x = strndup(s, e - s); - if (!x) { - r = log_oom(); - goto finish; - } - r = 1; - -finish: - munmap(buf, st.st_size); - *v = x; - return r; -} - -static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { - char *p; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0, c = 0; - - p = strjoina(esp_path, "/", path); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to read \"%s\": %m", p); - } - - FOREACH_DIRENT(de, d, break) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *v = NULL; - - if (!endswith_no_case(de->d_name, ".efi")) - continue; - - if (prefix && !startswith_no_case(de->d_name, prefix)) - continue; - - fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); - - r = get_file_version(fd, &v); - if (r < 0) - return r; - if (r > 0) - printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); - else - printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); - c++; - } - - return c; -} - -static int status_binaries(const char *esp_path, sd_id128_t partition) { - int r; - - printf("Boot Loader Binaries:\n"); - - printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); - - r = enumerate_binaries(esp_path, "EFI/systemd", NULL); - if (r == 0) - log_error("systemd-boot not installed in ESP."); - else if (r < 0) - return r; - - r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); - if (r == 0) - log_error("No default/fallback boot loader installed in ESP."); - else if (r < 0) - return r; - - printf("\n"); - - return 0; -} - -static int print_efi_option(uint16_t id, bool in_order) { - _cleanup_free_ char *title = NULL; - _cleanup_free_ char *path = NULL; - sd_id128_t partition; - bool active; - int r = 0; - - r = efi_get_boot_option(id, &title, &partition, &path, &active); - if (r < 0) - return r; - - /* print only configured entries with partition information */ - if (!path || sd_id128_is_null(partition)) - return 0; - - efi_tilt_backslashes(path); - - printf(" Title: %s\n", strna(title)); - printf(" ID: 0x%04X\n", id); - printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); - printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); - printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); - printf("\n"); - - return 0; -} - -static int status_variables(void) { - int n_options, n_order; - _cleanup_free_ uint16_t *options = NULL, *order = NULL; - int i; - - if (!is_efi_boot()) { - log_notice("Not booted with EFI, not showing EFI variables."); - return 0; - } - - n_options = efi_get_boot_options(&options); - if (n_options == -ENOENT) - return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs" - " needs to be available at /sys/firmware/efi/efivars/."); - else if (n_options < 0) - return log_error_errno(n_options, "Failed to read EFI boot entries: %m"); - - n_order = efi_get_boot_order(&order); - if (n_order == -ENOENT) - n_order = 0; - else if (n_order < 0) - return log_error_errno(n_order, "Failed to read EFI boot order."); - - /* print entries in BootOrder first */ - printf("Boot Loader Entries in EFI Variables:\n"); - for (i = 0; i < n_order; i++) - print_efi_option(order[i], true); - - /* print remaining entries */ - for (i = 0; i < n_options; i++) { - int j; - - for (j = 0; j < n_order; j++) - if (options[i] == order[j]) - goto next; - - print_efi_option(options[i], false); - next: - continue; - } - - return 0; -} - -static int compare_product(const char *a, const char *b) { - size_t x, y; - - assert(a); - assert(b); - - x = strcspn(a, " "); - y = strcspn(b, " "); - if (x != y) - return x < y ? -1 : x > y ? 1 : 0; - - return strncmp(a, b, x); -} - -static int compare_version(const char *a, const char *b) { - assert(a); - assert(b); - - a += strcspn(a, " "); - a += strspn(a, " "); - b += strcspn(b, " "); - b += strspn(b, " "); - - return strverscmp(a, b); -} - -static int version_check(int fd, const char *from, const char *to) { - _cleanup_free_ char *a = NULL, *b = NULL; - _cleanup_close_ int fd2 = -1; - int r; - - assert(fd >= 0); - assert(from); - assert(to); - - r = get_file_version(fd, &a); - if (r < 0) - return r; - if (r == 0) { - log_error("Source file \"%s\" does not carry version information!", from); - return -EINVAL; - } - - fd2 = open(to, O_RDONLY|O_CLOEXEC); - if (fd2 < 0) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to); - } - - r = get_file_version(fd2, &b); - if (r < 0) - return r; - if (r == 0 || compare_product(a, b) != 0) { - log_notice("Skipping \"%s\", since it's owned by another boot loader.", to); - return -EEXIST; - } - - if (compare_version(a, b) < 0) { - log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to); - return -ESTALE; - } - - return 0; -} - -static int copy_file(const char *from, const char *to, bool force) { - _cleanup_fclose_ FILE *f = NULL, *g = NULL; - char *p; - int r; - struct timespec t[2]; - struct stat st; - - assert(from); - assert(to); - - f = fopen(from, "re"); - if (!f) - return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from); - - if (!force) { - /* If this is an update, then let's compare versions first */ - r = version_check(fileno(f), from, to); - if (r < 0) - return r; - } - - p = strjoina(to, "~"); - g = fopen(p, "wxe"); - if (!g) { - /* Directory doesn't exist yet? Then let's skip this... */ - if (!force && errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to); - } - - rewind(f); - do { - size_t k; - uint8_t buf[32*1024]; - - k = fread(buf, 1, sizeof(buf), f); - if (ferror(f)) { - r = log_error_errno(EIO, "Failed to read \"%s\": %m", from); - goto error; - } - - if (k == 0) - break; - - fwrite(buf, 1, k, g); - if (ferror(g)) { - r = log_error_errno(EIO, "Failed to write \"%s\": %m", to); - goto error; - } - } while (!feof(f)); - - r = fflush_and_check(g); - if (r < 0) { - log_error_errno(r, "Failed to write \"%s\": %m", to); - goto error; - } - - r = fstat(fileno(f), &st); - if (r < 0) { - r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from); - goto error; - } - - t[0] = st.st_atim; - t[1] = st.st_mtim; - - r = futimens(fileno(g), t); - if (r < 0) { - r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p); - goto error; - } - - if (rename(p, to) < 0) { - r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to); - goto error; - } - - log_info("Copied \"%s\" to \"%s\".", from, to); - return 0; - -error: - (void) unlink(p); - return r; -} - -static char* strupper(char *s) { - char *p; - - for (p = s; *p; p++) - *p = toupper(*p); - - return s; -} - -static int mkdir_one(const char *prefix, const char *suffix) { - char *p; - - p = strjoina(prefix, "/", suffix); - if (mkdir(p, 0700) < 0) { - if (errno != EEXIST) - return log_error_errno(errno, "Failed to create \"%s\": %m", p); - } else - log_info("Created \"%s\".", p); - - return 0; -} - -static const char *efi_subdirs[] = { - "EFI", - "EFI/systemd", - "EFI/BOOT", - "loader", - "loader/entries" -}; - -static int create_dirs(const char *esp_path) { - int r; - unsigned i; - - for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) { - r = mkdir_one(esp_path, efi_subdirs[i]); - if (r < 0) - return r; - } - - return 0; -} - -static int copy_one_file(const char *esp_path, const char *name, bool force) { - char *p, *q; - int r; - - p = strjoina(BOOTLIBDIR "/", name); - q = strjoina(esp_path, "/EFI/systemd/", name); - r = copy_file(p, q, force); - - if (startswith(name, "systemd-boot")) { - int k; - char *v; - - /* Create the EFI default boot loader name (specified for removable devices) */ - v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot")); - strupper(strrchr(v, '/') + 1); - - k = copy_file(p, v, force); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static int install_binaries(const char *esp_path, bool force) { - struct dirent *de; - _cleanup_closedir_ DIR *d = NULL; - int r = 0; - - if (force) { - /* Don't create any of these directories when we are - * just updating. When we update we'll drop-in our - * files (unless there are newer ones already), but we - * won't create the directories for them in the first - * place. */ - r = create_dirs(esp_path); - if (r < 0) - return r; - } - - d = opendir(BOOTLIBDIR); - if (!d) - return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m"); - - FOREACH_DIRENT(de, d, break) { - int k; - - if (!endswith_no_case(de->d_name, ".efi")) - continue; - - k = copy_one_file(esp_path, de->d_name, force); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) { - _cleanup_free_ char *opath = NULL; - sd_id128_t ouuid; - int r; - - r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL); - if (r < 0) - return false; - if (!sd_id128_equal(uuid, ouuid)) - return false; - if (!streq_ptr(path, opath)) - return false; - - return true; -} - -static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) { - _cleanup_free_ uint16_t *options = NULL; - int n, i; - - n = efi_get_boot_options(&options); - if (n < 0) - return n; - - /* find already existing systemd-boot entry */ - for (i = 0; i < n; i++) - if (same_entry(options[i], uuid, path)) { - *id = options[i]; - return 1; - } - - /* find free slot in the sorted BootXXXX variable list */ - for (i = 0; i < n; i++) - if (i != options[i]) { - *id = i; - return 1; - } - - /* use the next one */ - if (i == 0xffff) - return -ENOSPC; - *id = i; - return 0; -} - -static int insert_into_order(uint16_t slot, bool first) { - _cleanup_free_ uint16_t *order = NULL; - uint16_t *t; - int n, i; - - n = efi_get_boot_order(&order); - if (n <= 0) - /* no entry, add us */ - return efi_set_boot_order(&slot, 1); - - /* are we the first and only one? */ - if (n == 1 && order[0] == slot) - return 0; - - /* are we already in the boot order? */ - for (i = 0; i < n; i++) { - if (order[i] != slot) - continue; - - /* we do not require to be the first one, all is fine */ - if (!first) - return 0; - - /* move us to the first slot */ - memmove(order + 1, order, i * sizeof(uint16_t)); - order[0] = slot; - return efi_set_boot_order(order, n); - } - - /* extend array */ - t = realloc(order, (n + 1) * sizeof(uint16_t)); - if (!t) - return -ENOMEM; - order = t; - - /* add us to the top or end of the list */ - if (first) { - memmove(order + 1, order, n * sizeof(uint16_t)); - order[0] = slot; - } else - order[n] = slot; - - return efi_set_boot_order(order, n + 1); -} - -static int remove_from_order(uint16_t slot) { - _cleanup_free_ uint16_t *order = NULL; - int n, i; - - n = efi_get_boot_order(&order); - if (n <= 0) - return n; - - for (i = 0; i < n; i++) { - if (order[i] != slot) - continue; - - if (i + 1 < n) - memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t)); - return efi_set_boot_order(order, n - 1); - } - - return 0; -} - -static int install_variables(const char *esp_path, - uint32_t part, uint64_t pstart, uint64_t psize, - sd_id128_t uuid, const char *path, - bool first) { - char *p; - uint16_t slot; - int r; - - if (!is_efi_boot()) { - log_warning("Not booted with EFI, skipping EFI variable setup."); - return 0; - } - - p = strjoina(esp_path, path); - if (access(p, F_OK) < 0) { - if (errno == ENOENT) - return 0; - else - return log_error_errno(errno, "Cannot access \"%s\": %m", p); - } - - r = find_slot(uuid, path, &slot); - if (r < 0) - return log_error_errno(r, - r == -ENOENT ? - "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : - "Failed to determine current boot order: %m"); - - if (first || r == false) { - r = efi_add_boot_option(slot, "Systemd Boot Manager", - part, pstart, psize, - uuid, path); - if (r < 0) - return log_error_errno(r, "Failed to create EFI Boot variable entry: %m"); - - log_info("Created EFI boot entry \"Systemd Boot Manager\"."); - } - - return insert_into_order(slot, first); -} - -static int remove_boot_efi(const char *esp_path) { - char *p; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r, c = 0; - - p = strjoina(esp_path, "/EFI/BOOT"); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open directory \"%s\": %m", p); - } - - FOREACH_DIRENT(de, d, break) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *v = NULL; - - if (!endswith_no_case(de->d_name, ".efi")) - continue; - - if (!startswith_no_case(de->d_name, "boot")) - continue; - - fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); - - r = get_file_version(fd, &v); - if (r < 0) - return r; - if (r > 0 && startswith(v, "systemd-boot ")) { - r = unlinkat(dirfd(d), de->d_name, 0); - if (r < 0) - return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name); - - log_info("Removed \"%s/%s\".", p, de->d_name); - } - - c++; - } - - return c; -} - -static int rmdir_one(const char *prefix, const char *suffix) { - char *p; - - p = strjoina(prefix, "/", suffix); - if (rmdir(p) < 0) { - if (!IN_SET(errno, ENOENT, ENOTEMPTY)) - return log_error_errno(errno, "Failed to remove \"%s\": %m", p); - } else - log_info("Removed \"%s\".", p); - - return 0; -} - -static int remove_binaries(const char *esp_path) { - char *p; - int r, q; - unsigned i; - - p = strjoina(esp_path, "/EFI/systemd"); - r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); - - q = remove_boot_efi(esp_path); - if (q < 0 && r == 0) - r = q; - - for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) { - q = rmdir_one(esp_path, efi_subdirs[i-1]); - if (q < 0 && r == 0) - r = q; - } - - return r; -} - -static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { - uint16_t slot; - int r; - - if (!is_efi_boot()) - return 0; - - r = find_slot(uuid, path, &slot); - if (r != 1) - return 0; - - r = efi_remove_boot_option(slot); - if (r < 0) - return r; - - if (in_order) - return remove_from_order(slot); - else - return 0; -} - -static int install_loader_config(const char *esp_path) { - char *p; - char line[64]; - char *machine = NULL; - _cleanup_fclose_ FILE *f = NULL, *g = NULL; - - f = fopen("/etc/machine-id", "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - if (fgets(line, sizeof(line), f) != NULL) { - char *s; - - s = strchr(line, '\n'); - if (s) - s[0] = '\0'; - if (strlen(line) == 32) - machine = line; - } - - if (!machine) - return -ESRCH; - - p = strjoina(esp_path, "/loader/loader.conf"); - g = fopen(p, "wxe"); - if (g) { - fprintf(g, "#timeout 3\n"); - fprintf(g, "default %s-*\n", machine); - if (ferror(g)) - return log_error_errno(EIO, "Failed to write \"%s\": %m", p); - } - - return 0; -} - -static int help(void) { - printf("%s [COMMAND] [OPTIONS...]\n" - "\n" - "Install, update or remove the systemd-boot EFI boot manager.\n\n" - " -h --help Show this help\n" - " --version Print version\n" - " --path=PATH Path to the EFI System Partition (ESP)\n" - " --no-variables Don't touch EFI variables\n" - "\n" - "Commands:\n" - " status Show status of installed systemd-boot and EFI variables\n" - " install Install systemd-boot to the ESP and EFI variables\n" - " update Update systemd-boot in the ESP and EFI variables\n" - " remove Remove systemd-boot from the ESP and EFI variables\n", - program_invocation_short_name); - - return 0; -} - -static const char *arg_path = "/boot"; -static bool arg_touch_variables = true; - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_PATH = 0x100, - ARG_VERSION, - ARG_NO_VARIABLES, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "path", required_argument, NULL, ARG_PATH }, - { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, - { NULL, 0, NULL, 0 } - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_PATH: - arg_path = optarg; - break; - - case ARG_NO_VARIABLES: - arg_touch_variables = false; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unknown option"); - } - - return 1; -} - -static void read_loader_efi_var(const char *name, char **var) { - int r; - - r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read EFI variable %s: %m", name); -} - -static int bootctl_main(int argc, char*argv[]) { - enum action { - ACTION_STATUS, - ACTION_INSTALL, - ACTION_UPDATE, - ACTION_REMOVE - } arg_action = ACTION_STATUS; - static const struct { - const char* verb; - enum action action; - } verbs[] = { - { "status", ACTION_STATUS }, - { "install", ACTION_INSTALL }, - { "update", ACTION_UPDATE }, - { "remove", ACTION_REMOVE }, - }; - - sd_id128_t uuid = {}; - uint32_t part = 0; - uint64_t pstart = 0, psize = 0; - int r, q; - - if (argv[optind]) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(verbs); i++) { - if (!streq(argv[optind], verbs[i].verb)) - continue; - arg_action = verbs[i].action; - break; - } - if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation \"%s\"", argv[optind]); - return -EINVAL; - } - } - - if (geteuid() != 0) - return log_error_errno(EPERM, "Need to be root."); - - r = verify_esp(arg_path, &part, &pstart, &psize, &uuid); - if (r == -ENODEV && !arg_path) - log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot."); - if (r < 0) - return r; - - switch (arg_action) { - case ACTION_STATUS: { - _cleanup_free_ char *fw_type = NULL; - _cleanup_free_ char *fw_info = NULL; - _cleanup_free_ char *loader = NULL; - _cleanup_free_ char *loader_path = NULL; - sd_id128_t loader_part_uuid = {}; - - if (is_efi_boot()) { - read_loader_efi_var("LoaderFirmwareType", &fw_type); - read_loader_efi_var("LoaderFirmwareInfo", &fw_info); - read_loader_efi_var("LoaderInfo", &loader); - read_loader_efi_var("LoaderImageIdentifier", &loader_path); - if (loader_path) - efi_tilt_backslashes(loader_path); - r = efi_loader_get_device_part_uuid(&loader_part_uuid); - if (r < 0 && r == -ENOENT) - log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); - - printf("System:\n"); - printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); - - r = is_efi_secure_boot(); - if (r < 0) - log_warning_errno(r, "Failed to query secure boot status: %m"); - else - printf(" Secure Boot: %s\n", r ? "enabled" : "disabled"); - - r = is_efi_secure_boot_setup_mode(); - if (r < 0) - log_warning_errno(r, "Failed to query secure boot mode: %m"); - else - printf(" Setup Mode: %s\n", r ? "setup" : "user"); - printf("\n"); - - printf("Loader:\n"); - printf(" Product: %s\n", strna(loader)); - if (!sd_id128_is_null(loader_part_uuid)) - printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", - SD_ID128_FORMAT_VAL(loader_part_uuid)); - else - printf(" Partition: n/a\n"); - printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); - printf("\n"); - } else - printf("System:\n Not booted with EFI\n"); - - r = status_binaries(arg_path, uuid); - if (r < 0) - return r; - - if (arg_touch_variables) - r = status_variables(); - break; - } - - case ACTION_INSTALL: - case ACTION_UPDATE: - umask(0002); - - r = install_binaries(arg_path, arg_action == ACTION_INSTALL); - if (r < 0) - return r; - - if (arg_action == ACTION_INSTALL) { - r = install_loader_config(arg_path); - if (r < 0) - return r; - } - - if (arg_touch_variables) - r = install_variables(arg_path, - part, pstart, psize, uuid, - "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", - arg_action == ACTION_INSTALL); - break; - - case ACTION_REMOVE: - r = remove_binaries(arg_path); - - if (arg_touch_variables) { - q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); - if (q < 0 && r == 0) - r = q; - } - break; - } - - return r; -} - -int main(int argc, char *argv[]) { - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = bootctl_main(argc, argv); - - finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/boot/efi/.gitignore b/src/boot/efi/.gitignore deleted file mode 100644 index e193acbe12..0000000000 --- a/src/boot/efi/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/systemd_boot.so -/stub.so diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c deleted file mode 100644 index 30c1ead1aa..0000000000 --- a/src/boot/efi/boot.c +++ /dev/null @@ -1,1857 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2015 Kay Sievers - * Copyright (C) 2012-2015 Harald Hoyer - */ - -#include -#include - -#include "console.h" -#include "disk.h" -#include "graphics.h" -#include "linux.h" -#include "pefile.h" -#include "util.h" -#include "measure.h" - -#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI -#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL -#endif - -/* magic string to find in the binary image */ -static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####"; - -static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; - -enum loader_type { - LOADER_UNDEFINED, - LOADER_EFI, - LOADER_LINUX -}; - -typedef struct { - CHAR16 *file; - CHAR16 *title_show; - CHAR16 *title; - CHAR16 *version; - CHAR16 *machine_id; - EFI_HANDLE *device; - enum loader_type type; - CHAR16 *loader; - CHAR16 *options; - CHAR16 key; - EFI_STATUS (*call)(VOID); - BOOLEAN no_autoselect; - BOOLEAN non_unique; -} ConfigEntry; - -typedef struct { - ConfigEntry **entries; - UINTN entry_count; - INTN idx_default; - INTN idx_default_efivar; - UINTN timeout_sec; - UINTN timeout_sec_config; - INTN timeout_sec_efivar; - CHAR16 *entry_default_pattern; - CHAR16 *entry_oneshot; - CHAR16 *options_edit; - BOOLEAN no_editor; -} Config; - -static VOID cursor_left(UINTN *cursor, UINTN *first) { - if ((*cursor) > 0) - (*cursor)--; - else if ((*first) > 0) - (*first)--; -} - -static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) { - if ((*cursor)+1 < x_max) - (*cursor)++; - else if ((*first) + (*cursor) < len) - (*first)++; -} - -static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) { - CHAR16 *line; - UINTN size; - UINTN len; - UINTN first; - CHAR16 *print; - UINTN cursor; - UINTN clear; - BOOLEAN exit; - BOOLEAN enter; - - if (!line_in) - line_in = L""; - size = StrLen(line_in) + 1024; - line = AllocatePool(size * sizeof(CHAR16)); - StrCpy(line, line_in); - len = StrLen(line); - print = AllocatePool((x_max+1) * sizeof(CHAR16)); - - uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE); - - first = 0; - cursor = 0; - clear = 0; - enter = FALSE; - exit = FALSE; - while (!exit) { - EFI_STATUS err; - UINT64 key; - UINTN i; - - i = len - first; - if (i >= x_max-1) - i = x_max-1; - CopyMem(print, line + first, i * sizeof(CHAR16)); - while (clear > 0 && i < x_max-1) { - clear--; - print[i++] = ' '; - } - print[i] = '\0'; - - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - - err = console_key_read(&key, TRUE); - if (EFI_ERROR(err)) - continue; - - switch (key) { - case KEYPRESS(0, SCAN_ESC, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')): - exit = TRUE; - break; - - case KEYPRESS(0, SCAN_HOME, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')): - /* beginning-of-line */ - cursor = 0; - first = 0; - continue; - - case KEYPRESS(0, SCAN_END, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')): - /* end-of-line */ - cursor = len - first; - if (cursor+1 >= x_max) { - cursor = x_max-1; - first = len - (x_max-1); - } - continue; - - case KEYPRESS(0, SCAN_DOWN, 0): - case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'): - case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0): - /* forward-word */ - while (line[first + cursor] && line[first + cursor] == ' ') - cursor_right(&cursor, &first, x_max, len); - while (line[first + cursor] && line[first + cursor] != ' ') - cursor_right(&cursor, &first, x_max, len); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - continue; - - case KEYPRESS(0, SCAN_UP, 0): - case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'): - case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0): - /* backward-word */ - if ((first + cursor) > 0 && line[first + cursor-1] == ' ') { - cursor_left(&cursor, &first); - while ((first + cursor) > 0 && line[first + cursor] == ' ') - cursor_left(&cursor, &first); - } - while ((first + cursor) > 0 && line[first + cursor-1] != ' ') - cursor_left(&cursor, &first); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - continue; - - case KEYPRESS(0, SCAN_RIGHT, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')): - /* forward-char */ - if (first + cursor == len) - continue; - cursor_right(&cursor, &first, x_max, len); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - continue; - - case KEYPRESS(0, SCAN_LEFT, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')): - /* backward-char */ - cursor_left(&cursor, &first); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - continue; - - case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'): - /* kill-word */ - clear = 0; - for (i = first + cursor; i < len && line[i] == ' '; i++) - clear++; - for (; i < len && line[i] != ' '; i++) - clear++; - - for (i = first + cursor; i + clear < len; i++) - line[i] = line[i + clear]; - len -= clear; - line[len] = '\0'; - continue; - - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')): - case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE): - /* backward-kill-word */ - clear = 0; - if ((first + cursor) > 0 && line[first + cursor-1] == ' ') { - cursor_left(&cursor, &first); - clear++; - while ((first + cursor) > 0 && line[first + cursor] == ' ') { - cursor_left(&cursor, &first); - clear++; - } - } - while ((first + cursor) > 0 && line[first + cursor-1] != ' ') { - cursor_left(&cursor, &first); - clear++; - } - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); - - for (i = first + cursor; i + clear < len; i++) - line[i] = line[i + clear]; - len -= clear; - line[len] = '\0'; - continue; - - case KEYPRESS(0, SCAN_DELETE, 0): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')): - if (len == 0) - continue; - if (first + cursor == len) - continue; - for (i = first + cursor; i < len; i++) - line[i] = line[i+1]; - clear = 1; - len--; - continue; - - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')): - /* kill-line */ - line[first + cursor] = '\0'; - clear = len - (first + cursor); - len = first + cursor; - continue; - - case KEYPRESS(0, 0, CHAR_LINEFEED): - case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): - if (StrCmp(line, line_in) != 0) { - *line_out = line; - line = NULL; - } - enter = TRUE; - exit = TRUE; - break; - - case KEYPRESS(0, 0, CHAR_BACKSPACE): - if (len == 0) - continue; - if (first == 0 && cursor == 0) - continue; - for (i = first + cursor-1; i < len; i++) - line[i] = line[i+1]; - clear = 1; - len--; - if (cursor > 0) - cursor--; - if (cursor > 0 || first == 0) - continue; - /* show full line if it fits */ - if (len < x_max) { - cursor = first; - first = 0; - continue; - } - /* jump left to see what we delete */ - if (first > 10) { - first -= 10; - cursor = 10; - } else { - cursor = first; - first = 0; - } - continue; - - case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'): - case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff): - if (len+1 == size) - continue; - for (i = len; i > first + cursor; i--) - line[i] = line[i-1]; - line[first + cursor] = KEYCHAR(key); - len++; - line[len] = '\0'; - if (cursor+1 < x_max) - cursor++; - else if (first + cursor < len) - first++; - continue; - } - } - - uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); - FreePool(print); - FreePool(line); - return enter; -} - -static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { - UINTN i; - - if (key == 0) - return -1; - - /* select entry by number key */ - if (key >= '1' && key <= '9') { - i = key - '0'; - if (i > config->entry_count) - i = config->entry_count; - return i-1; - } - - /* find matching key in config entries */ - for (i = start; i < config->entry_count; i++) - if (config->entries[i]->key == key) - return i; - - for (i = 0; i < start; i++) - if (config->entries[i]->key == key) - return i; - - return -1; -} - -static VOID print_status(Config *config, CHAR16 *loaded_image_path) { - UINT64 key; - UINTN i; - CHAR16 *s; - CHAR8 *b; - UINTN x; - UINTN y; - UINTN size; - - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - - Print(L"systemd-boot version: " VERSION "\n"); - Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n"); - Print(L"loaded image: %s\n", loaded_image_path); - Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - Print(L"firmware vendor: %s\n", ST->FirmwareVendor); - Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - - if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS) - Print(L"console size: %d x %d\n", x, y); - - if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { - Print(L"SecureBoot: %s\n", yes_no(*b > 0)); - FreePool(b); - } - - if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) { - Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user"); - FreePool(b); - } - - if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) { - Print(L"OsIndicationsSupported: %d\n", (UINT64)*b); - FreePool(b); - } - Print(L"\n"); - - Print(L"timeout: %d\n", config->timeout_sec); - if (config->timeout_sec_efivar >= 0) - Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar); - Print(L"timeout (config): %d\n", config->timeout_sec_config); - if (config->entry_default_pattern) - Print(L"default pattern: '%s'\n", config->entry_default_pattern); - Print(L"editor: %s\n", yes_no(!config->no_editor)); - Print(L"\n"); - - Print(L"config entry count: %d\n", config->entry_count); - Print(L"entry selected idx: %d\n", config->idx_default); - if (config->idx_default_efivar >= 0) - Print(L"entry EFI var idx: %d\n", config->idx_default_efivar); - Print(L"\n"); - - if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS) - Print(L"LoaderConfigTimeout: %d\n", i); - if (config->entry_oneshot) - Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot); - if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) { - Print(L"LoaderDevicePartUUID: %s\n", s); - FreePool(s); - } - if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) { - Print(L"LoaderEntryDefault: %s\n", s); - FreePool(s); - } - - Print(L"\n--- press key ---\n\n"); - console_key_read(&key, TRUE); - - for (i = 0; i < config->entry_count; i++) { - ConfigEntry *entry; - - if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q')) - break; - - entry = config->entries[i]; - Print(L"config entry: %d/%d\n", i+1, config->entry_count); - if (entry->file) - Print(L"file '%s'\n", entry->file); - Print(L"title show '%s'\n", entry->title_show); - if (entry->title) - Print(L"title '%s'\n", entry->title); - if (entry->version) - Print(L"version '%s'\n", entry->version); - if (entry->machine_id) - Print(L"machine-id '%s'\n", entry->machine_id); - if (entry->device) { - EFI_DEVICE_PATH *device_path; - CHAR16 *str; - - device_path = DevicePathFromHandle(entry->device); - if (device_path) { - str = DevicePathToStr(device_path); - Print(L"device handle '%s'\n", str); - FreePool(str); - } - } - if (entry->loader) - Print(L"loader '%s'\n", entry->loader); - if (entry->options) - Print(L"options '%s'\n", entry->options); - Print(L"auto-select %s\n", yes_no(!entry->no_autoselect)); - if (entry->call) - Print(L"internal call yes\n"); - - Print(L"\n--- press key ---\n\n"); - console_key_read(&key, TRUE); - } - - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); -} - -static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) { - EFI_STATUS err; - UINTN visible_max; - UINTN idx_highlight; - UINTN idx_highlight_prev; - UINTN idx_first; - UINTN idx_last; - BOOLEAN refresh; - BOOLEAN highlight; - UINTN i; - UINTN line_width; - CHAR16 **lines; - UINTN x_start; - UINTN y_start; - UINTN x_max; - UINTN y_max; - CHAR16 *status; - CHAR16 *clearline; - INTN timeout_remain; - INT16 idx; - BOOLEAN exit = FALSE; - BOOLEAN run = TRUE; - BOOLEAN wait = FALSE; - - graphics_mode(FALSE); - uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); - uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - - /* draw a single character to make ClearScreen work on some firmware */ - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" "); - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - - err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max); - if (EFI_ERROR(err)) { - x_max = 80; - y_max = 25; - } - - /* we check 10 times per second for a keystroke */ - if (config->timeout_sec > 0) - timeout_remain = config->timeout_sec * 10; - else - timeout_remain = -1; - - idx_highlight = config->idx_default; - idx_highlight_prev = 0; - - visible_max = y_max - 2; - - if ((UINTN)config->idx_default >= visible_max) - idx_first = config->idx_default-1; - else - idx_first = 0; - - idx_last = idx_first + visible_max-1; - - refresh = TRUE; - highlight = FALSE; - - /* length of the longest entry */ - line_width = 5; - for (i = 0; i < config->entry_count; i++) { - UINTN entry_len; - - entry_len = StrLen(config->entries[i]->title_show); - if (line_width < entry_len) - line_width = entry_len; - } - if (line_width > x_max-6) - line_width = x_max-6; - - /* offsets to center the entries on the screen */ - x_start = (x_max - (line_width)) / 2; - if (config->entry_count < visible_max) - y_start = ((visible_max - config->entry_count) / 2) + 1; - else - y_start = 0; - - /* menu entries title lines */ - lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count); - for (i = 0; i < config->entry_count; i++) { - UINTN j, k; - - lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16))); - for (j = 0; j < x_start; j++) - lines[i][j] = ' '; - - for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++) - lines[i][j] = config->entries[i]->title_show[k]; - - for (; j < x_max; j++) - lines[i][j] = ' '; - lines[i][x_max] = '\0'; - } - - status = NULL; - clearline = AllocatePool((x_max+1) * sizeof(CHAR16)); - for (i = 0; i < x_max; i++) - clearline[i] = ' '; - clearline[i] = 0; - - while (!exit) { - UINT64 key; - - if (refresh) { - for (i = 0; i < config->entry_count; i++) { - if (i < idx_first || i > idx_last) - continue; - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first); - if (i == idx_highlight) - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY); - else - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]); - if ((INTN)i == config->idx_default_efivar) { - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); - } - } - refresh = FALSE; - } else if (highlight) { - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first); - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]); - if ((INTN)idx_highlight_prev == config->idx_default_efivar) { - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); - } - - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first); - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]); - if ((INTN)idx_highlight == config->idx_default_efivar) { - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); - } - highlight = FALSE; - } - - if (timeout_remain > 0) { - FreePool(status); - status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10); - } - - /* print status at last line of screen */ - if (status) { - UINTN len; - UINTN x; - - /* center line */ - len = StrLen(status); - if (len < x_max) - x = (x_max - len) / 2; - else - x = 0; - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x)); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len); - } - - err = console_key_read(&key, wait); - if (EFI_ERROR(err)) { - /* timeout reached */ - if (timeout_remain == 0) { - exit = TRUE; - break; - } - - /* sleep and update status */ - if (timeout_remain > 0) { - uefi_call_wrapper(BS->Stall, 1, 100 * 1000); - timeout_remain--; - continue; - } - - /* timeout disabled, wait for next key */ - wait = TRUE; - continue; - } - - timeout_remain = -1; - - /* clear status after keystroke */ - if (status) { - FreePool(status); - status = NULL; - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); - } - - idx_highlight_prev = idx_highlight; - - switch (key) { - case KEYPRESS(0, SCAN_UP, 0): - case KEYPRESS(0, 0, 'k'): - if (idx_highlight > 0) - idx_highlight--; - break; - - case KEYPRESS(0, SCAN_DOWN, 0): - case KEYPRESS(0, 0, 'j'): - if (idx_highlight < config->entry_count-1) - idx_highlight++; - break; - - case KEYPRESS(0, SCAN_HOME, 0): - case KEYPRESS(EFI_ALT_PRESSED, 0, '<'): - if (idx_highlight > 0) { - refresh = TRUE; - idx_highlight = 0; - } - break; - - case KEYPRESS(0, SCAN_END, 0): - case KEYPRESS(EFI_ALT_PRESSED, 0, '>'): - if (idx_highlight < config->entry_count-1) { - refresh = TRUE; - idx_highlight = config->entry_count-1; - } - break; - - case KEYPRESS(0, SCAN_PAGE_UP, 0): - if (idx_highlight > visible_max) - idx_highlight -= visible_max; - else - idx_highlight = 0; - break; - - case KEYPRESS(0, SCAN_PAGE_DOWN, 0): - idx_highlight += visible_max; - if (idx_highlight > config->entry_count-1) - idx_highlight = config->entry_count-1; - break; - - case KEYPRESS(0, 0, CHAR_LINEFEED): - case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): - exit = TRUE; - break; - - case KEYPRESS(0, SCAN_F1, 0): - case KEYPRESS(0, 0, 'h'): - case KEYPRESS(0, 0, '?'): - status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp"); - break; - - case KEYPRESS(0, 0, 'Q'): - exit = TRUE; - run = FALSE; - break; - - case KEYPRESS(0, 0, 'd'): - if (config->idx_default_efivar != (INTN)idx_highlight) { - /* store the selected entry in a persistent EFI variable */ - efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE); - config->idx_default_efivar = idx_highlight; - status = StrDuplicate(L"Default boot entry selected."); - } else { - /* clear the default entry EFI variable */ - efivar_set(L"LoaderEntryDefault", NULL, TRUE); - config->idx_default_efivar = -1; - status = StrDuplicate(L"Default boot entry cleared."); - } - refresh = TRUE; - break; - - case KEYPRESS(0, 0, '-'): - case KEYPRESS(0, 0, 'T'): - if (config->timeout_sec_efivar > 0) { - config->timeout_sec_efivar--; - efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE); - if (config->timeout_sec_efivar > 0) - status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar); - else - status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); - } else if (config->timeout_sec_efivar <= 0){ - config->timeout_sec_efivar = -1; - efivar_set(L"LoaderConfigTimeout", NULL, TRUE); - if (config->timeout_sec_config > 0) - status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.", - config->timeout_sec_config); - else - status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); - } - break; - - case KEYPRESS(0, 0, '+'): - case KEYPRESS(0, 0, 't'): - if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0) - config->timeout_sec_efivar++; - config->timeout_sec_efivar++; - efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE); - if (config->timeout_sec_efivar > 0) - status = PoolPrint(L"Menu timeout set to %d sec.", - config->timeout_sec_efivar); - else - status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); - break; - - case KEYPRESS(0, 0, 'e'): - /* only the options of configured entries can be edited */ - if (config->no_editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED) - break; - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); - if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1)) - exit = TRUE; - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); - uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); - break; - - case KEYPRESS(0, 0, 'v'): - status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d", - ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff, - ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - break; - - case KEYPRESS(0, 0, 'P'): - print_status(config, loaded_image_path); - refresh = TRUE; - break; - - case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'): - case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')): - refresh = TRUE; - break; - - default: - /* jump with a hotkey directly to a matching entry */ - idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key)); - if (idx < 0) - break; - idx_highlight = idx; - refresh = TRUE; - } - - if (idx_highlight > idx_last) { - idx_last = idx_highlight; - idx_first = 1 + idx_highlight - visible_max; - refresh = TRUE; - } else if (idx_highlight < idx_first) { - idx_first = idx_highlight; - idx_last = idx_highlight + visible_max-1; - refresh = TRUE; - } - - if (!refresh && idx_highlight != idx_highlight_prev) - highlight = TRUE; - } - - *chosen_entry = config->entries[idx_highlight]; - - for (i = 0; i < config->entry_count; i++) - FreePool(lines[i]); - FreePool(lines); - FreePool(clearline); - - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK); - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - return run; -} - -static VOID config_add_entry(Config *config, ConfigEntry *entry) { - if ((config->entry_count & 15) == 0) { - UINTN i; - - i = config->entry_count + 16; - if (config->entry_count == 0) - config->entries = AllocatePool(sizeof(VOID *) * i); - else - config->entries = ReallocatePool(config->entries, - sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i); - } - config->entries[config->entry_count++] = entry; -} - -static VOID config_entry_free(ConfigEntry *entry) { - FreePool(entry->title_show); - FreePool(entry->title); - FreePool(entry->machine_id); - FreePool(entry->loader); - FreePool(entry->options); -} - -static BOOLEAN is_digit(CHAR16 c) { - return (c >= '0') && (c <= '9'); -} - -static UINTN c_order(CHAR16 c) { - if (c == '\0') - return 0; - if (is_digit(c)) - return 0; - else if ((c >= 'a') && (c <= 'z')) - return c; - else - return c + 0x10000; -} - -static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) { - CHAR16 *os1 = s1; - CHAR16 *os2 = s2; - - while (*s1 || *s2) { - INTN first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - INTN order; - - order = c_order(*s1) - c_order(*s2); - if (order) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first) - return first; - } - - return StrCmp(os1, os2); -} - -static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) { - CHAR8 *line; - UINTN linelen; - CHAR8 *value; - -skip: - line = content + *pos; - if (*line == '\0') - return NULL; - - linelen = 0; - while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen])) - linelen++; - - /* move pos to next line */ - *pos += linelen; - if (content[*pos]) - (*pos)++; - - /* empty line */ - if (linelen == 0) - goto skip; - - /* terminate line */ - line[linelen] = '\0'; - - /* remove leading whitespace */ - while (strchra((CHAR8 *)" \t", *line)) { - line++; - linelen--; - } - - /* remove trailing whitespace */ - while (linelen > 0 && strchra(sep, line[linelen-1])) - linelen--; - line[linelen] = '\0'; - - if (*line == '#') - goto skip; - - /* split key/value */ - value = line; - while (*value && !strchra(sep, *value)) - value++; - if (*value == '\0') - goto skip; - *value = '\0'; - value++; - while (*value && strchra(sep, *value)) - value++; - - /* unquote */ - if (value[0] == '\"' && line[linelen-1] == '\"') { - value++; - line[linelen-1] = '\0'; - } - - *key_ret = line; - *value_ret = value; - return line; -} - -static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) { - CHAR8 *line; - UINTN pos = 0; - CHAR8 *key, *value; - - line = content; - while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) { - if (strcmpa((CHAR8 *)"timeout", key) == 0) { - CHAR16 *s; - - s = stra_to_str(value); - config->timeout_sec_config = Atoi(s); - config->timeout_sec = config->timeout_sec_config; - FreePool(s); - continue; - } - - if (strcmpa((CHAR8 *)"default", key) == 0) { - FreePool(config->entry_default_pattern); - config->entry_default_pattern = stra_to_str(value); - StrLwr(config->entry_default_pattern); - continue; - } - - if (strcmpa((CHAR8 *)"editor", key) == 0) { - BOOLEAN on; - - if (EFI_ERROR(parse_boolean(value, &on))) - continue; - config->no_editor = !on; - } - } -} - -static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) { - ConfigEntry *entry; - CHAR8 *line; - UINTN pos = 0; - CHAR8 *key, *value; - UINTN len; - CHAR16 *initrd = NULL; - - entry = AllocateZeroPool(sizeof(ConfigEntry)); - - line = content; - while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) { - if (strcmpa((CHAR8 *)"title", key) == 0) { - FreePool(entry->title); - entry->title = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"version", key) == 0) { - FreePool(entry->version); - entry->version = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"machine-id", key) == 0) { - FreePool(entry->machine_id); - entry->machine_id = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"linux", key) == 0) { - FreePool(entry->loader); - entry->type = LOADER_LINUX; - entry->loader = stra_to_path(value); - entry->key = 'l'; - continue; - } - - if (strcmpa((CHAR8 *)"efi", key) == 0) { - entry->type = LOADER_EFI; - FreePool(entry->loader); - entry->loader = stra_to_path(value); - - /* do not add an entry for ourselves */ - if (StriCmp(entry->loader, loaded_image_path) == 0) { - entry->type = LOADER_UNDEFINED; - break; - } - continue; - } - - if (strcmpa((CHAR8 *)"architecture", key) == 0) { - /* do not add an entry for an EFI image of architecture not matching with that of the image */ - if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) { - entry->type = LOADER_UNDEFINED; - break; - } - continue; - } - - if (strcmpa((CHAR8 *)"initrd", key) == 0) { - CHAR16 *new; - - new = stra_to_path(value); - if (initrd) { - CHAR16 *s; - - s = PoolPrint(L"%s initrd=%s", initrd, new); - FreePool(initrd); - initrd = s; - } else - initrd = PoolPrint(L"initrd=%s", new); - FreePool(new); - continue; - } - - if (strcmpa((CHAR8 *)"options", key) == 0) { - CHAR16 *new; - - new = stra_to_str(value); - if (entry->options) { - CHAR16 *s; - - s = PoolPrint(L"%s %s", entry->options, new); - FreePool(entry->options); - entry->options = s; - } else { - entry->options = new; - new = NULL; - } - FreePool(new); - continue; - } - } - - if (entry->type == LOADER_UNDEFINED) { - config_entry_free(entry); - FreePool(initrd); - FreePool(entry); - return; - } - - /* add initrd= to options */ - if (entry->type == LOADER_LINUX && initrd) { - if (entry->options) { - CHAR16 *s; - - s = PoolPrint(L"%s %s", initrd, entry->options); - FreePool(entry->options); - entry->options = s; - } else { - entry->options = initrd; - initrd = NULL; - } - } - FreePool(initrd); - - entry->device = device; - entry->file = StrDuplicate(file); - len = StrLen(entry->file); - /* remove ".conf" */ - if (len > 5) - entry->file[len - 5] = '\0'; - StrLwr(entry->file); - - config_add_entry(config, entry); -} - -static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) { - CHAR8 *content = NULL; - UINTN sec; - UINTN len; - EFI_STATUS err; - - len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content); - if (len > 0) - config_defaults_load_from_file(config, content); - FreePool(content); - - err = efivar_get_int(L"LoaderConfigTimeout", &sec); - if (!EFI_ERROR(err)) { - config->timeout_sec_efivar = sec; - config->timeout_sec = sec; - } else - config->timeout_sec_efivar = -1; -} - -static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) { - EFI_FILE_HANDLE entries_dir; - EFI_STATUS err; - - err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL); - if (!EFI_ERROR(err)) { - for (;;) { - CHAR16 buf[256]; - UINTN bufsize; - EFI_FILE_INFO *f; - CHAR8 *content = NULL; - UINTN len; - - bufsize = sizeof(buf); - err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf); - if (bufsize == 0 || EFI_ERROR(err)) - break; - - f = (EFI_FILE_INFO *) buf; - if (f->FileName[0] == '.') - continue; - if (f->Attribute & EFI_FILE_DIRECTORY) - continue; - - len = StrLen(f->FileName); - if (len < 6) - continue; - if (StriCmp(f->FileName + len - 5, L".conf") != 0) - continue; - if (StrnCmp(f->FileName, L"auto-", 5) == 0) - continue; - - len = file_read(entries_dir, f->FileName, 0, 0, &content); - if (len > 0) - config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path); - FreePool(content); - } - uefi_call_wrapper(entries_dir->Close, 1, entries_dir); - } -} - -static VOID config_sort_entries(Config *config) { - UINTN i; - - for (i = 1; i < config->entry_count; i++) { - BOOLEAN more; - UINTN k; - - more = FALSE; - for (k = 0; k < config->entry_count - i; k++) { - ConfigEntry *entry; - - if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0) - continue; - entry = config->entries[k]; - config->entries[k] = config->entries[k+1]; - config->entries[k+1] = entry; - more = TRUE; - } - if (!more) - break; - } -} - -static VOID config_default_entry_select(Config *config) { - CHAR16 *var; - EFI_STATUS err; - UINTN i; - - /* - * The EFI variable to specify a boot entry for the next, and only the - * next reboot. The variable is always cleared directly after it is read. - */ - err = efivar_get(L"LoaderEntryOneShot", &var); - if (!EFI_ERROR(err)) { - BOOLEAN found = FALSE; - - for (i = 0; i < config->entry_count; i++) { - if (StrCmp(config->entries[i]->file, var) == 0) { - config->idx_default = i; - found = TRUE; - break; - } - } - - config->entry_oneshot = StrDuplicate(var); - efivar_set(L"LoaderEntryOneShot", NULL, TRUE); - FreePool(var); - if (found) - return; - } - - /* - * The EFI variable to select the default boot entry overrides the - * configured pattern. The variable can be set and cleared by pressing - * the 'd' key in the loader selection menu, the entry is marked with - * an '*'. - */ - err = efivar_get(L"LoaderEntryDefault", &var); - if (!EFI_ERROR(err)) { - BOOLEAN found = FALSE; - - for (i = 0; i < config->entry_count; i++) { - if (StrCmp(config->entries[i]->file, var) == 0) { - config->idx_default = i; - config->idx_default_efivar = i; - found = TRUE; - break; - } - } - FreePool(var); - if (found) - return; - } - config->idx_default_efivar = -1; - - if (config->entry_count == 0) - return; - - /* - * Match the pattern from the end of the list to the start, find last - * entry (largest number) matching the given pattern. - */ - if (config->entry_default_pattern) { - i = config->entry_count; - while (i--) { - if (config->entries[i]->no_autoselect) - continue; - if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) { - config->idx_default = i; - return; - } - } - } - - /* select the last suitable entry */ - i = config->entry_count; - while (i--) { - if (config->entries[i]->no_autoselect) - continue; - config->idx_default = i; - return; - } - - /* no entry found */ - config->idx_default = -1; -} - -/* generate a unique title, avoiding non-distinguishable menu entries */ -static VOID config_title_generate(Config *config) { - UINTN i, k; - BOOLEAN unique; - - /* set title */ - for (i = 0; i < config->entry_count; i++) { - CHAR16 *title; - - FreePool(config->entries[i]->title_show); - title = config->entries[i]->title; - if (!title) - title = config->entries[i]->file; - config->entries[i]->title_show = StrDuplicate(title); - } - - unique = TRUE; - for (i = 0; i < config->entry_count; i++) { - for (k = 0; k < config->entry_count; k++) { - if (i == k) - continue; - if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) - continue; - - unique = FALSE; - config->entries[i]->non_unique = TRUE; - config->entries[k]->non_unique = TRUE; - } - } - if (unique) - return; - - /* add version to non-unique titles */ - for (i = 0; i < config->entry_count; i++) { - CHAR16 *s; - - if (!config->entries[i]->non_unique) - continue; - if (!config->entries[i]->version) - continue; - - s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; - config->entries[i]->non_unique = FALSE; - } - - unique = TRUE; - for (i = 0; i < config->entry_count; i++) { - for (k = 0; k < config->entry_count; k++) { - if (i == k) - continue; - if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) - continue; - - unique = FALSE; - config->entries[i]->non_unique = TRUE; - config->entries[k]->non_unique = TRUE; - } - } - if (unique) - return; - - /* add machine-id to non-unique titles */ - for (i = 0; i < config->entry_count; i++) { - CHAR16 *s; - CHAR16 *m; - - if (!config->entries[i]->non_unique) - continue; - if (!config->entries[i]->machine_id) - continue; - - m = StrDuplicate(config->entries[i]->machine_id); - m[8] = '\0'; - s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; - config->entries[i]->non_unique = FALSE; - FreePool(m); - } - - unique = TRUE; - for (i = 0; i < config->entry_count; i++) { - for (k = 0; k < config->entry_count; k++) { - if (i == k) - continue; - if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) - continue; - - unique = FALSE; - config->entries[i]->non_unique = TRUE; - config->entries[k]->non_unique = TRUE; - } - } - if (unique) - return; - - /* add file name to non-unique titles */ - for (i = 0; i < config->entry_count; i++) { - CHAR16 *s; - - if (!config->entries[i]->non_unique) - continue; - s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; - config->entries[i]->non_unique = FALSE; - } -} - -static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) { - ConfigEntry *entry; - - entry = AllocateZeroPool(sizeof(ConfigEntry)); - entry->title = StrDuplicate(title); - entry->call = call; - entry->no_autoselect = TRUE; - config_add_entry(config, entry); - return TRUE; -} - -static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device, - enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) { - ConfigEntry *entry; - - entry = AllocateZeroPool(sizeof(ConfigEntry)); - entry->type = type; - entry->title = StrDuplicate(title); - entry->device = device; - entry->loader = StrDuplicate(loader); - entry->file = StrDuplicate(file); - StrLwr(entry->file); - entry->key = key; - config_add_entry(config, entry); - - return entry; -} - -static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path, - CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) { - EFI_FILE_HANDLE handle; - ConfigEntry *entry; - EFI_STATUS err; - - /* do not add an entry for ourselves */ - if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0) - return FALSE; - - /* check existence */ - err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL); - if (EFI_ERROR(err)) - return FALSE; - uefi_call_wrapper(handle->Close, 1, handle); - - entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader); - if (!entry) - return FALSE; - - /* do not boot right away into auto-detected entries */ - entry->no_autoselect = TRUE; - - return TRUE; -} - -static VOID config_entry_add_osx(Config *config) { - EFI_STATUS err; - UINTN handle_count = 0; - EFI_HANDLE *handles = NULL; - - err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles); - if (!EFI_ERROR(err)) { - UINTN i; - - for (i = 0; i < handle_count; i++) { - EFI_FILE *root; - BOOLEAN found; - - root = LibOpenRoot(handles[i]); - if (!root) - continue; - found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X", - L"\\System\\Library\\CoreServices\\boot.efi"); - uefi_call_wrapper(root->Close, 1, root); - if (found) - break; - } - - FreePool(handles); - } -} - -static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) { - EFI_FILE_HANDLE linux_dir; - EFI_STATUS err; - ConfigEntry *entry; - - err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL); - if (!EFI_ERROR(err)) { - for (;;) { - CHAR16 buf[256]; - UINTN bufsize; - EFI_FILE_INFO *f; - CHAR8 *sections[] = { - (UINT8 *)".osrel", - (UINT8 *)".cmdline", - NULL - }; - UINTN offs[ELEMENTSOF(sections)-1] = {}; - UINTN szs[ELEMENTSOF(sections)-1] = {}; - UINTN addrs[ELEMENTSOF(sections)-1] = {}; - CHAR8 *content = NULL; - UINTN len; - CHAR8 *line; - UINTN pos = 0; - CHAR8 *key, *value; - CHAR16 *os_name = NULL; - CHAR16 *os_id = NULL; - CHAR16 *os_version = NULL; - CHAR16 *os_build = NULL; - - bufsize = sizeof(buf); - err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf); - if (bufsize == 0 || EFI_ERROR(err)) - break; - - f = (EFI_FILE_INFO *) buf; - if (f->FileName[0] == '.') - continue; - if (f->Attribute & EFI_FILE_DIRECTORY) - continue; - len = StrLen(f->FileName); - if (len < 5) - continue; - if (StriCmp(f->FileName + len - 4, L".efi") != 0) - continue; - - /* look for .osrel and .cmdline sections in the .efi binary */ - err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); - if (EFI_ERROR(err)) - continue; - - len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content); - if (len <= 0) - continue; - - /* read properties from the embedded os-release file */ - line = content; - while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) { - if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) { - FreePool(os_name); - os_name = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"ID", key) == 0) { - FreePool(os_id); - os_id = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) { - FreePool(os_version); - os_version = stra_to_str(value); - continue; - } - - if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) { - FreePool(os_build); - os_build = stra_to_str(value); - continue; - } - } - - if (os_name && os_id && (os_version || os_build)) { - CHAR16 *conf; - CHAR16 *path; - CHAR16 *cmdline; - - conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build); - path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); - entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path); - - FreePool(content); - /* read the embedded cmdline file */ - len = file_read(linux_dir, f->FileName, offs[1], szs[1] - 1 , &content); - if (len > 0) { - cmdline = stra_to_str(content); - entry->options = cmdline; - cmdline = NULL; - } - FreePool(cmdline); - FreePool(conf); - FreePool(path); - } - - FreePool(os_name); - FreePool(os_id); - FreePool(os_version); - FreePool(os_build); - FreePool(content); - } - uefi_call_wrapper(linux_dir->Close, 1, linux_dir); - } -} - -static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) { - EFI_HANDLE image; - EFI_DEVICE_PATH *path; - CHAR16 *options; - EFI_STATUS err; - - path = FileDevicePath(entry->device, entry->loader); - if (!path) { - Print(L"Error getting device path."); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return EFI_INVALID_PARAMETER; - } - - err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image); - if (EFI_ERROR(err)) { - Print(L"Error loading %s: %r", entry->loader, err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - goto out; - } - - if (config->options_edit) - options = config->options_edit; - else if (entry->options) - options = entry->options; - else - options = NULL; - if (options) { - EFI_LOADED_IMAGE *loaded_image; - - err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, - parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(err)) { - Print(L"Error getting LoadedImageProtocol handle: %r", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - goto out_unload; - } - loaded_image->LoadOptions = options; - loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); - -#ifdef SD_BOOT_LOG_TPM - /* Try to log any options to the TPM, escpecially to catch manually edited options */ - err = tpm_log_event(SD_TPM_PCR, - (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, - loaded_image->LoadOptionsSize, loaded_image->LoadOptions); - if (EFI_ERROR(err)) { - Print(L"Unable to add image options measurement: %r", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; - } -#endif - } - - efivar_set_time_usec(L"LoaderTimeExecUSec", 0); - err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); -out_unload: - uefi_call_wrapper(BS->UnloadImage, 1, image); -out: - FreePool(path); - return err; -} - -static EFI_STATUS reboot_into_firmware(VOID) { - CHAR8 *b; - UINTN size; - UINT64 osind; - EFI_STATUS err; - - osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - - err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size); - if (!EFI_ERROR(err)) - osind |= (UINT64)*b; - FreePool(b); - - err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE); - if (EFI_ERROR(err)) - return err; - - err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL); - Print(L"Error calling ResetSystem: %r", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; -} - -static VOID config_free(Config *config) { - UINTN i; - - for (i = 0; i < config->entry_count; i++) - config_entry_free(config->entries[i]); - FreePool(config->entries); - FreePool(config->entry_default_pattern); - FreePool(config->options_edit); - FreePool(config->entry_oneshot); -} - -EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - CHAR16 *s; - CHAR8 *b; - UINTN size; - EFI_LOADED_IMAGE *loaded_image; - EFI_FILE *root_dir; - CHAR16 *loaded_image_path; - EFI_STATUS err; - Config config; - UINT64 init_usec; - BOOLEAN menu = FALSE; - CHAR16 uuid[37]; - - InitializeLib(image, sys_table); - init_usec = time_usec(); - efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec); - efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE); - s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - efivar_set(L"LoaderFirmwareInfo", s, FALSE); - FreePool(s); - s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - efivar_set(L"LoaderFirmwareType", s, FALSE); - FreePool(s); - - err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, - image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(err)) { - Print(L"Error getting a LoadedImageProtocol handle: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; - } - - /* export the device path this image is started from */ - if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) - efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); - - root_dir = LibOpenRoot(loaded_image->DeviceHandle); - if (!root_dir) { - Print(L"Unable to open root directory: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return EFI_LOAD_ERROR; - } - - - /* the filesystem path to this image, to prevent adding ourselves to the menu */ - loaded_image_path = DevicePathToStr(loaded_image->FilePath); - efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE); - - ZeroMem(&config, sizeof(Config)); - config_load_defaults(&config, root_dir); - - /* scan /EFI/Linux/ directory */ - config_entry_add_linux(&config, loaded_image, root_dir); - - /* scan /loader/entries/\*.conf files */ - config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); - - /* sort entries after version number */ - config_sort_entries(&config); - - /* if we find some well-known loaders, add them to the end of the list */ - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, - L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, - L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, - L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi"); - config_entry_add_osx(&config); - - if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) { - UINT64 osind = (UINT64)*b; - - if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) - config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware); - FreePool(b); - } - - if (config.entry_count == 0) { - Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed."); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - goto out; - } - - config_title_generate(&config); - - /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/ - config_default_entry_select(&config); - - /* if no configured entry to select from was found, enable the menu */ - if (config.idx_default == -1) { - config.idx_default = 0; - if (config.timeout_sec == 0) - config.timeout_sec = 10; - } - - /* select entry or show menu when key is pressed or timeout is set */ - if (config.timeout_sec == 0) { - UINT64 key; - - err = console_key_read(&key, FALSE); - if (!EFI_ERROR(err)) { - INT16 idx; - - /* find matching key in config entries */ - idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key)); - if (idx >= 0) - config.idx_default = idx; - else - menu = TRUE; - } - } else - menu = TRUE; - - for (;;) { - ConfigEntry *entry; - - entry = config.entries[config.idx_default]; - if (menu) { - efivar_set_time_usec(L"LoaderTimeMenuUSec", 0); - uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL); - if (!menu_run(&config, &entry, loaded_image_path)) - break; - - /* run special entry like "reboot" */ - if (entry->call) { - entry->call(); - continue; - } - } - - /* export the selected boot entry to the system */ - efivar_set(L"LoaderEntrySelected", entry->file, FALSE); - - uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL); - err = image_start(image, &config, entry); - if (EFI_ERROR(err)) { - graphics_mode(FALSE); - Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - goto out; - } - - menu = TRUE; - config.timeout_sec = 0; - } - err = EFI_SUCCESS; -out: - FreePool(loaded_image_path); - config_free(&config); - uefi_call_wrapper(root_dir->Close, 1, root_dir); - uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL); - return err; -} diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c deleted file mode 100644 index 2b797c9a5f..0000000000 --- a/src/boot/efi/console.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#include -#include - -#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 } } - -struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; - -typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)( - struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, - BOOLEAN ExtendedVerification -); - -typedef UINT8 EFI_KEY_TOGGLE_STATE; - -typedef struct { - UINT32 KeyShiftState; - EFI_KEY_TOGGLE_STATE KeyToggleState; -} EFI_KEY_STATE; - -typedef struct { - EFI_INPUT_KEY Key; - EFI_KEY_STATE KeyState; -} EFI_KEY_DATA; - -typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)( - struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, - EFI_KEY_DATA *KeyData -); - -typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)( - struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, - EFI_KEY_TOGGLE_STATE *KeyToggleState -); - -typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)( - EFI_KEY_DATA *KeyData -); - -typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)( - struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, - EFI_KEY_DATA KeyData, - EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, - VOID **NotifyHandle -); - -typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)( - struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, - VOID *NotificationHandle -); - -typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL { - EFI_INPUT_RESET_EX Reset; - EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx; - EFI_EVENT WaitForKeyEx; - EFI_SET_STATE SetState; - EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify; - EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify; -} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; - -EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { - EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; - static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; - static BOOLEAN checked; - UINTN index; - EFI_INPUT_KEY k; - EFI_STATUS err; - - if (!checked) { - err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx); - if (EFI_ERROR(err)) - TextInputEx = NULL; - - checked = TRUE; - } - - /* wait until key is pressed */ - if (wait) - uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index); - - if (TextInputEx) { - EFI_KEY_DATA keydata; - UINT64 keypress; - - err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata); - if (!EFI_ERROR(err)) { - UINT32 shift = 0; - - /* do not distinguish between left and right keys */ - if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) { - if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)) - shift |= EFI_CONTROL_PRESSED; - if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)) - shift |= EFI_ALT_PRESSED; - }; - - /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */ - keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar); - if (keypress > 0) { - *key = keypress; - return 0; - } - } - } - - /* fallback for firmware which does not support SimpleTextInputExProtocol - * - * This is also called in case ReadKeyStrokeEx did not return a key, because - * some broken firmwares offer SimpleTextInputExProtocol, but never acually - * handle any key. */ - err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k); - if (EFI_ERROR(err)) - return err; - - *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar); - return 0; -} diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h deleted file mode 100644 index 3fe0ce5ec4..0000000000 --- a/src/boot/efi/console.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#ifndef __SDBOOT_CONSOLE_H -#define __SDBOOT_CONSOLE_H - -#define EFI_SHIFT_STATE_VALID 0x80000000 -#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 -#define EFI_LEFT_CONTROL_PRESSED 0x00000008 -#define EFI_RIGHT_ALT_PRESSED 0x00000010 -#define EFI_LEFT_ALT_PRESSED 0x00000020 - -#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED) -#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED) -#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni)) -#define KEYCHAR(k) ((k) & 0xffff) -#define CHAR_CTRL(c) ((c) - 'a' + 1) - -EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait); -#endif diff --git a/src/boot/efi/disk.c b/src/boot/efi/disk.c deleted file mode 100644 index 3e3b5b224a..0000000000 --- a/src/boot/efi/disk.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#include -#include - -#include "util.h" - -EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]) { - EFI_DEVICE_PATH *device_path; - EFI_STATUS r = EFI_NOT_FOUND; - - /* export the device path this image is started from */ - device_path = DevicePathFromHandle(handle); - if (device_path) { - EFI_DEVICE_PATH *path, *paths; - - paths = UnpackDevicePath(device_path); - for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) { - HARDDRIVE_DEVICE_PATH *drive; - - if (DevicePathType(path) != MEDIA_DEVICE_PATH) - continue; - if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP) - continue; - drive = (HARDDRIVE_DEVICE_PATH *)path; - if (drive->SignatureType != SIGNATURE_TYPE_GUID) - continue; - - GuidToString(uuid, (EFI_GUID *)&drive->Signature); - r = EFI_SUCCESS; - break; - } - FreePool(paths); - } - - return r; -} diff --git a/src/boot/efi/disk.h b/src/boot/efi/disk.h deleted file mode 100644 index af91a9c674..0000000000 --- a/src/boot/efi/disk.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#ifndef __SDBOOT_DISK_H -#define __SDBOOT_DISK_H - -EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]); -#endif diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c deleted file mode 100644 index 4854baf874..0000000000 --- a/src/boot/efi/graphics.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - * Copyright (C) 2013 Intel Corporation - * Authored by Joonas Lahtinen - */ - -#include -#include - -#include "graphics.h" -#include "util.h" - -EFI_STATUS graphics_mode(BOOLEAN on) { - #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ - { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } }; - - struct _EFI_CONSOLE_CONTROL_PROTOCOL; - - typedef enum { - EfiConsoleControlScreenText, - EfiConsoleControlScreenGraphics, - EfiConsoleControlScreenMaxValue, - } EFI_CONSOLE_CONTROL_SCREEN_MODE; - - typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)( - struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, - EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, - BOOLEAN *UgaExists, - BOOLEAN *StdInLocked - ); - - typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)( - struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, - EFI_CONSOLE_CONTROL_SCREEN_MODE Mode - ); - - typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)( - struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, - CHAR16 *Password - ); - - typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL { - EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode; - EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; - EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn; - } EFI_CONSOLE_CONTROL_PROTOCOL; - - EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; - EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; - EFI_CONSOLE_CONTROL_SCREEN_MODE new; - EFI_CONSOLE_CONTROL_SCREEN_MODE current; - BOOLEAN uga_exists; - BOOLEAN stdin_locked; - EFI_STATUS err; - - err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl); - if (EFI_ERROR(err)) - /* console control protocol is nonstandard and might not exist. */ - return err == EFI_NOT_FOUND ? EFI_SUCCESS : err; - - /* check current mode */ - err = uefi_call_wrapper(ConsoleControl->GetMode, 4, ConsoleControl, ¤t, &uga_exists, &stdin_locked); - if (EFI_ERROR(err)) - return err; - - /* do not touch the mode */ - new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText; - if (new == current) - return EFI_SUCCESS; - - err = uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, new); - - /* some firmware enables the cursor when switching modes */ - uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); - - return err; -} diff --git a/src/boot/efi/graphics.h b/src/boot/efi/graphics.h deleted file mode 100644 index cf48e647e7..0000000000 --- a/src/boot/efi/graphics.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - * Copyright (C) 2013 Intel Corporation - * Authored by Joonas Lahtinen - */ - -#ifndef __SDBOOT_GRAPHICS_H -#define __SDBOOT_GRAPHICS_H - -EFI_STATUS graphics_mode(BOOLEAN on); -#endif diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c deleted file mode 100644 index 0dc99a6c53..0000000000 --- a/src/boot/efi/linux.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#include -#include - -#include "linux.h" -#include "util.h" - -#define SETUP_MAGIC 0x53726448 /* "HdrS" */ -struct SetupHeader { - UINT8 boot_sector[0x01f1]; - UINT8 setup_secs; - UINT16 root_flags; - UINT32 sys_size; - UINT16 ram_size; - UINT16 video_mode; - UINT16 root_dev; - UINT16 signature; - UINT16 jump; - UINT32 header; - UINT16 version; - UINT16 su_switch; - UINT16 setup_seg; - UINT16 start_sys; - UINT16 kernel_ver; - UINT8 loader_id; - UINT8 load_flags; - UINT16 movesize; - UINT32 code32_start; - UINT32 ramdisk_start; - UINT32 ramdisk_len; - UINT32 bootsect_kludge; - UINT16 heap_end; - UINT8 ext_loader_ver; - UINT8 ext_loader_type; - UINT32 cmd_line_ptr; - UINT32 ramdisk_max; - UINT32 kernel_alignment; - UINT8 relocatable_kernel; - UINT8 min_alignment; - UINT16 xloadflags; - UINT32 cmdline_size; - UINT32 hardware_subarch; - UINT64 hardware_subarch_data; - UINT32 payload_offset; - UINT32 payload_length; - UINT64 setup_data; - UINT64 pref_address; - UINT32 init_size; - UINT32 handover_offset; -} __attribute__((packed)); - -#ifdef __x86_64__ -typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup); -static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { - handover_f handover; - - asm volatile ("cli"); - handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset); - handover(image, ST, setup); -} -#else -typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0))); -static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { - handover_f handover; - - handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset); - handover(image, ST, setup); -} -#endif - -EFI_STATUS linux_exec(EFI_HANDLE *image, - CHAR8 *cmdline, UINTN cmdline_len, - UINTN linux_addr, - UINTN initrd_addr, UINTN initrd_size) { - struct SetupHeader *image_setup; - struct SetupHeader *boot_setup; - EFI_PHYSICAL_ADDRESS addr; - EFI_STATUS err; - - image_setup = (struct SetupHeader *)(linux_addr); - if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) - return EFI_LOAD_ERROR; - - if (image_setup->version < 0x20b || !image_setup->relocatable_kernel) - return EFI_LOAD_ERROR; - - addr = 0x3fffffff; - err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(0x4000), &addr); - if (EFI_ERROR(err)) - return err; - boot_setup = (struct SetupHeader *)(UINTN)addr; - ZeroMem(boot_setup, 0x4000); - CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader)); - boot_setup->loader_id = 0xff; - - boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512; - - if (cmdline) { - addr = 0xA0000; - err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); - if (EFI_ERROR(err)) - return err; - CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len); - ((CHAR8 *)addr)[cmdline_len] = 0; - boot_setup->cmd_line_ptr = (UINT32)addr; - } - - boot_setup->ramdisk_start = (UINT32)initrd_addr; - boot_setup->ramdisk_len = (UINT32)initrd_size; - - linux_efi_handover(image, boot_setup); - return EFI_LOAD_ERROR; -} diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h deleted file mode 100644 index d9e6ed7955..0000000000 --- a/src/boot/efi/linux.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#ifndef __SDBOOT_kernel_H -#define __SDBOOT_kernel_H - -EFI_STATUS linux_exec(EFI_HANDLE *image, - CHAR8 *cmdline, UINTN cmdline_size, - UINTN linux_addr, - UINTN initrd_addr, UINTN initrd_size); -#endif diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c deleted file mode 100644 index 7c016387c1..0000000000 --- a/src/boot/efi/measure.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - */ - -#ifdef SD_BOOT_LOG_TPM - -#include -#include -#include "measure.h" - -#define EFI_TCG_PROTOCOL_GUID { 0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd} } - -typedef struct _TCG_VERSION { - UINT8 Major; - UINT8 Minor; - UINT8 RevMajor; - UINT8 RevMinor; -} TCG_VERSION; - -typedef struct _TCG_BOOT_SERVICE_CAPABILITY { - UINT8 Size; - struct _TCG_VERSION StructureVersion; - struct _TCG_VERSION ProtocolSpecVersion; - UINT8 HashAlgorithmBitmap; - BOOLEAN TPMPresentFlag; - BOOLEAN TPMDeactivatedFlag; -} TCG_BOOT_SERVICE_CAPABILITY; - -typedef UINT32 TCG_ALGORITHM_ID; -#define TCG_ALG_SHA 0x00000004 // The SHA1 algorithm - -#define SHA1_DIGEST_SIZE 20 - -typedef struct _TCG_DIGEST { - UINT8 Digest[SHA1_DIGEST_SIZE]; -} TCG_DIGEST; - -#define EV_IPL 13 - -typedef struct _TCG_PCR_EVENT { - UINT32 PCRIndex; - UINT32 EventType; - struct _TCG_DIGEST digest; - UINT32 EventSize; - UINT8 Event[1]; -} TCG_PCR_EVENT; - -INTERFACE_DECL(_EFI_TCG); - -typedef EFI_STATUS(EFIAPI * EFI_TCG_STATUS_CHECK) (IN struct _EFI_TCG * This, - OUT struct _TCG_BOOT_SERVICE_CAPABILITY * ProtocolCapability, - OUT UINT32 * TCGFeatureFlags, - OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, - OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); - -typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_ALL) (IN struct _EFI_TCG * This, - IN UINT8 * HashData, - IN UINT64 HashDataLen, - IN TCG_ALGORITHM_ID AlgorithmId, - IN OUT UINT64 * HashedDataLen, IN OUT UINT8 ** HashedDataResult); - -typedef EFI_STATUS(EFIAPI * EFI_TCG_LOG_EVENT) (IN struct _EFI_TCG * This, - IN struct _TCG_PCR_EVENT * TCGLogData, - IN OUT UINT32 * EventNumber, IN UINT32 Flags); - -typedef EFI_STATUS(EFIAPI * EFI_TCG_PASS_THROUGH_TO_TPM) (IN struct _EFI_TCG * This, - IN UINT32 TpmInputParameterBlockSize, - IN UINT8 * TpmInputParameterBlock, - IN UINT32 TpmOutputParameterBlockSize, - IN UINT8 * TpmOutputParameterBlock); - -typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_LOG_EXTEND_EVENT) (IN struct _EFI_TCG * This, - IN EFI_PHYSICAL_ADDRESS HashData, - IN UINT64 HashDataLen, - IN TCG_ALGORITHM_ID AlgorithmId, - IN struct _TCG_PCR_EVENT * TCGLogData, - IN OUT UINT32 * EventNumber, - OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); - -typedef struct _EFI_TCG { - EFI_TCG_STATUS_CHECK StatusCheck; - EFI_TCG_HASH_ALL HashAll; - EFI_TCG_LOG_EVENT LogEvent; - EFI_TCG_PASS_THROUGH_TO_TPM PassThroughToTPM; - EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; -} EFI_TCG; - -#define EFI_TCG2_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} - -typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; - -typedef struct tdEFI_TCG2_VERSION { - UINT8 Major; - UINT8 Minor; -} EFI_TCG2_VERSION; - -typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; -typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; -typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; - -#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 -#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 - -typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { - UINT8 Size; - EFI_TCG2_VERSION StructureVersion; - EFI_TCG2_VERSION ProtocolVersion; - EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; - EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; - BOOLEAN TPMPresentFlag; - UINT16 MaxCommandSize; - UINT16 MaxResponseSize; - UINT32 ManufacturerID; - UINT32 NumberOfPCRBanks; - EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; -} EFI_TCG2_BOOT_SERVICE_CAPABILITY; - -#define EFI_TCG2_EVENT_HEADER_VERSION 1 - -typedef struct { - UINT32 HeaderSize; - UINT16 HeaderVersion; - UINT32 PCRIndex; - UINT32 EventType; -} EFI_TCG2_EVENT_HEADER; - -typedef struct tdEFI_TCG2_EVENT { - UINT32 Size; - EFI_TCG2_EVENT_HEADER Header; - UINT8 Event[1]; -} EFI_TCG2_EVENT; - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This, - IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_EVENT_LOG) (IN EFI_TCG2_PROTOCOL * This, - IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, - OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, - OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry, - OUT BOOLEAN * EventLogTruncated); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) (IN EFI_TCG2_PROTOCOL * This, - IN UINT64 Flags, - IN EFI_PHYSICAL_ADDRESS DataToHash, - IN UINT64 DataToHashLen, IN EFI_TCG2_EVENT * EfiTcgEvent); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_SUBMIT_COMMAND) (IN EFI_TCG2_PROTOCOL * This, - IN UINT32 InputParameterBlockSize, - IN UINT8 * InputParameterBlock, - IN UINT32 OutputParameterBlockSize, IN UINT8 * OutputParameterBlock); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, OUT UINT32 * ActivePcrBanks); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, IN UINT32 ActivePcrBanks); - -typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, - OUT UINT32 * OperationPresent, OUT UINT32 * Response); - -typedef struct tdEFI_TCG2_PROTOCOL { - EFI_TCG2_GET_CAPABILITY GetCapability; - EFI_TCG2_GET_EVENT_LOG GetEventLog; - EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; - EFI_TCG2_SUBMIT_COMMAND SubmitCommand; - EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; - EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; - EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; -} EFI_TCG2; - - -static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, - UINTN buffer_size, const CHAR16 *description) { - EFI_STATUS status; - TCG_PCR_EVENT *tcg_event; - UINT32 event_number; - EFI_PHYSICAL_ADDRESS event_log_last; - UINTN desc_len; - - desc_len = (StrLen(description) + 1) * sizeof(CHAR16); - - tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT)); - - if (tcg_event == NULL) - return EFI_OUT_OF_RESOURCES; - - tcg_event->EventSize = desc_len; - CopyMem((VOID *) & tcg_event->Event[0], (VOID *) description, desc_len); - - tcg_event->PCRIndex = pcrindex; - tcg_event->EventType = EV_IPL; - - event_number = 1; - status = uefi_call_wrapper(tcg->HashLogExtendEvent, 7, - tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); - - if (EFI_ERROR(status)) - return status; - - uefi_call_wrapper(BS->FreePool, 1, tcg_event); - - return EFI_SUCCESS; -} - - -static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, - UINT64 buffer_size, const CHAR16 *description) { - EFI_STATUS status; - EFI_TCG2_EVENT *tcg_event; - UINTN desc_len; - - desc_len = StrLen(description) * sizeof(CHAR16); - - tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1); - - if (tcg_event == NULL) - return EFI_OUT_OF_RESOURCES; - - tcg_event->Size = sizeof(EFI_TCG2_EVENT) - sizeof(tcg_event->Event) + desc_len + 1; - tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); - tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; - tcg_event->Header.PCRIndex = pcrindex; - tcg_event->Header.EventType = EV_IPL; - - CopyMem((VOID *) tcg_event->Event, (VOID *) description, desc_len); - - status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, tcg, 0, buffer, buffer_size, tcg_event); - - uefi_call_wrapper(BS->FreePool, 1, tcg_event); - - if (EFI_ERROR(status)) - return status; - - return EFI_SUCCESS; -} - -static EFI_TCG * tcg1_interface_check(void) { - EFI_GUID tpm_guid = EFI_TCG_PROTOCOL_GUID; - EFI_STATUS status; - EFI_TCG *tcg; - TCG_BOOT_SERVICE_CAPABILITY capability; - UINT32 features; - EFI_PHYSICAL_ADDRESS event_log_location; - EFI_PHYSICAL_ADDRESS event_log_last_entry; - - status = LibLocateProtocol(&tpm_guid, (void **) &tcg); - - if (EFI_ERROR(status)) - return NULL; - - capability.Size = (UINT8) sizeof(capability); - status = uefi_call_wrapper(tcg->StatusCheck, 5, tcg, &capability, &features, &event_log_location, &event_log_last_entry); - - if (EFI_ERROR(status)) - return NULL; - - if (capability.TPMDeactivatedFlag) - return NULL; - - if (!capability.TPMPresentFlag) - return NULL; - - return tcg; -} - -static EFI_TCG2 * tcg2_interface_check(void) { - EFI_GUID tpm2_guid = EFI_TCG2_PROTOCOL_GUID; - EFI_STATUS status; - EFI_TCG2 *tcg; - EFI_TCG2_BOOT_SERVICE_CAPABILITY capability; - - status = LibLocateProtocol(&tpm2_guid, (void **) &tcg); - - if (EFI_ERROR(status)) - return NULL; - - capability.Size = (UINT8) sizeof(capability); - status = uefi_call_wrapper(tcg->GetCapability, 2, tcg, &capability); - - if (EFI_ERROR(status)) - return NULL; - - if (!capability.TPMPresentFlag) - return NULL; - - return tcg; -} - -EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { - EFI_TCG *tpm1; - EFI_TCG2 *tpm2; - - tpm2 = tcg2_interface_check(); - if (tpm2) - return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); - - tpm1 = tcg1_interface_check(); - if (tpm1) - return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description); - - /* No active TPM found, so don't return an error */ - return EFI_SUCCESS; -} - -#endif diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h deleted file mode 100644 index a2cfe817d0..0000000000 --- a/src/boot/efi/measure.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - */ -#ifndef __SDBOOT_MEASURE_H -#define __SDBOOT_MEASURE_H - -#ifndef SD_TPM_PCR -#define SD_TPM_PCR 8 -#endif - -EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); -#endif diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pefile.c deleted file mode 100644 index 77fff77b69..0000000000 --- a/src/boot/efi/pefile.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#include -#include - -#include "pefile.h" -#include "util.h" - -struct DosFileHeader { - UINT8 Magic[2]; - UINT16 LastSize; - UINT16 nBlocks; - UINT16 nReloc; - UINT16 HdrSize; - UINT16 MinAlloc; - UINT16 MaxAlloc; - UINT16 ss; - UINT16 sp; - UINT16 Checksum; - UINT16 ip; - UINT16 cs; - UINT16 RelocPos; - UINT16 nOverlay; - UINT16 reserved[4]; - UINT16 OEMId; - UINT16 OEMInfo; - UINT16 reserved2[10]; - UINT32 ExeHeader; -} __attribute__((packed)); - -#define PE_HEADER_MACHINE_I386 0x014c -#define PE_HEADER_MACHINE_X64 0x8664 -struct PeFileHeader { - UINT16 Machine; - UINT16 NumberOfSections; - UINT32 TimeDateStamp; - UINT32 PointerToSymbolTable; - UINT32 NumberOfSymbols; - UINT16 SizeOfOptionalHeader; - UINT16 Characteristics; -} __attribute__((packed)); - -struct PeSectionHeader { - UINT8 Name[8]; - UINT32 VirtualSize; - UINT32 VirtualAddress; - UINT32 SizeOfRawData; - UINT32 PointerToRawData; - UINT32 PointerToRelocations; - UINT32 PointerToLinenumbers; - UINT16 NumberOfRelocations; - UINT16 NumberOfLinenumbers; - UINT32 Characteristics; -} __attribute__((packed)); - - -EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { - EFI_FILE_HANDLE handle; - struct DosFileHeader dos; - uint8_t magic[4]; - struct PeFileHeader pe; - UINTN len; - UINTN i; - EFI_STATUS err; - - err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); - if (EFI_ERROR(err)) - return err; - - /* MS-DOS stub */ - len = sizeof(dos); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(dos)) { - err = EFI_LOAD_ERROR; - goto out; - } - - if (CompareMem(dos.Magic, "MZ", 2) != 0) { - err = EFI_LOAD_ERROR; - goto out; - } - - err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader); - if (EFI_ERROR(err)) - goto out; - - /* PE header */ - len = sizeof(magic); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(magic)) { - err = EFI_LOAD_ERROR; - goto out; - } - - if (CompareMem(magic, "PE\0\0", 2) != 0) { - err = EFI_LOAD_ERROR; - goto out; - } - - len = sizeof(pe); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(pe)) { - err = EFI_LOAD_ERROR; - goto out; - } - - /* PE32+ Subsystem type */ - if (pe.Machine != PE_HEADER_MACHINE_X64 && - pe.Machine != PE_HEADER_MACHINE_I386) { - err = EFI_LOAD_ERROR; - goto out; - } - - if (pe.NumberOfSections > 96) { - err = EFI_LOAD_ERROR; - goto out; - } - - /* the sections start directly after the headers */ - err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader); - if (EFI_ERROR(err)) - goto out; - - for (i = 0; i < pe.NumberOfSections; i++) { - struct PeSectionHeader sect; - UINTN j; - - len = sizeof(sect); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, §); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(sect)) { - err = EFI_LOAD_ERROR; - goto out; - } - for (j = 0; sections[j]; j++) { - if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0) - continue; - - if (addrs) - addrs[j] = (UINTN)sect.VirtualAddress; - if (offsets) - offsets[j] = (UINTN)sect.PointerToRawData; - if (sizes) - sizes[j] = (UINTN)sect.VirtualSize; - } - } - -out: - uefi_call_wrapper(handle->Close, 1, handle); - return err; -} diff --git a/src/boot/efi/pefile.h b/src/boot/efi/pefile.h deleted file mode 100644 index 2e445ede17..0000000000 --- a/src/boot/efi/pefile.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#ifndef __SDBOOT_PEFILE_H -#define __SDBOOT_PEFILE_H - -EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, - CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); -#endif diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c deleted file mode 100644 index c0ef7f64fe..0000000000 --- a/src/boot/efi/splash.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#include -#include - -#include "graphics.h" -#include "splash.h" -#include "util.h" - -struct bmp_file { - CHAR8 signature[2]; - UINT32 size; - UINT16 reserved[2]; - UINT32 offset; -} __attribute__((packed)); - -/* we require at least BITMAPINFOHEADER, later versions are - accepted, but their features ignored */ -struct bmp_dib { - UINT32 size; - UINT32 x; - UINT32 y; - UINT16 planes; - UINT16 depth; - UINT32 compression; - UINT32 image_size; - INT32 x_pixel_meter; - INT32 y_pixel_meter; - UINT32 colors_used; - UINT32 colors_important; -} __attribute__((packed)); - -struct bmp_map { - UINT8 blue; - UINT8 green; - UINT8 red; - UINT8 reserved; -} __attribute__((packed)); - -EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib, - struct bmp_map **ret_map, UINT8 **pixmap) { - struct bmp_file *file; - struct bmp_dib *dib; - struct bmp_map *map; - UINTN row_size; - - if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib)) - return EFI_INVALID_PARAMETER; - - /* check file header */ - file = (struct bmp_file *)bmp; - if (file->signature[0] != 'B' || file->signature[1] != 'M') - return EFI_INVALID_PARAMETER; - if (file->size != size) - return EFI_INVALID_PARAMETER; - if (file->size < file->offset) - return EFI_INVALID_PARAMETER; - - /* check device-independent bitmap */ - dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file)); - if (dib->size < sizeof(struct bmp_dib)) - return EFI_UNSUPPORTED; - - switch (dib->depth) { - case 1: - case 4: - case 8: - case 24: - if (dib->compression != 0) - return EFI_UNSUPPORTED; - - break; - - case 16: - case 32: - if (dib->compression != 0 && dib->compression != 3) - return EFI_UNSUPPORTED; - - break; - - default: - return EFI_UNSUPPORTED; - } - - row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4; - if (file->size - file->offset < dib->y * row_size) - return EFI_INVALID_PARAMETER; - if (row_size * dib->y > 64 * 1024 * 1024) - return EFI_INVALID_PARAMETER; - - /* check color table */ - map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size); - if (file->offset < sizeof(struct bmp_file) + dib->size) - return EFI_INVALID_PARAMETER; - - if (file->offset > sizeof(struct bmp_file) + dib->size) { - UINT32 map_count; - UINTN map_size; - - if (dib->colors_used) - map_count = dib->colors_used; - else { - switch (dib->depth) { - case 1: - case 4: - case 8: - map_count = 1 << dib->depth; - break; - - default: - map_count = 0; - break; - } - } - - map_size = file->offset - (sizeof(struct bmp_file) + dib->size); - if (map_size != sizeof(struct bmp_map) * map_count) - return EFI_INVALID_PARAMETER; - } - - *ret_map = map; - *ret_dib = dib; - *pixmap = bmp + file->offset; - - return EFI_SUCCESS; -} - -static VOID pixel_blend(UINT32 *dst, const UINT32 source) { - UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g; - - alpha = (source & 0xff); - - /* convert src from RGBA to XRGB */ - src = source >> 8; - - /* decompose into RB and G components */ - src_rb = (src & 0xff00ff); - src_g = (src & 0x00ff00); - - dst_rb = (*dst & 0xff00ff); - dst_g = (*dst & 0x00ff00); - - /* blend */ - rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff; - g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00; - - *dst = (rb | g); -} - -EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, - struct bmp_dib *dib, struct bmp_map *map, - UINT8 *pixmap) { - UINT8 *in; - UINTN y; - - /* transform and copy pixels */ - in = pixmap; - for (y = 0; y < dib->y; y++) { - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out; - UINTN row_size; - UINTN x; - - out = &buf[(dib->y - y - 1) * dib->x]; - for (x = 0; x < dib->x; x++, in++, out++) { - switch (dib->depth) { - case 1: { - UINTN i; - - for (i = 0; i < 8 && x < dib->x; i++) { - out->Red = map[((*in) >> (7 - i)) & 1].red; - out->Green = map[((*in) >> (7 - i)) & 1].green; - out->Blue = map[((*in) >> (7 - i)) & 1].blue; - out++; - x++; - } - out--; - x--; - break; - } - - case 4: { - UINTN i; - - i = (*in) >> 4; - out->Red = map[i].red; - out->Green = map[i].green; - out->Blue = map[i].blue; - if (x < (dib->x - 1)) { - out++; - x++; - i = (*in) & 0x0f; - out->Red = map[i].red; - out->Green = map[i].green; - out->Blue = map[i].blue; - } - break; - } - - case 8: - out->Red = map[*in].red; - out->Green = map[*in].green; - out->Blue = map[*in].blue; - break; - - case 16: { - UINT16 i = *(UINT16 *) in; - - out->Red = (i & 0x7c00) >> 7; - out->Green = (i & 0x3e0) >> 2; - out->Blue = (i & 0x1f) << 3; - in += 1; - break; - } - - case 24: - out->Red = in[2]; - out->Green = in[1]; - out->Blue = in[0]; - in += 2; - break; - - case 32: { - UINT32 i = *(UINT32 *) in; - - pixel_blend((UINT32 *)out, i); - - in += 3; - break; - } - } - } - - /* add row padding; new lines always start at 32 bit boundary */ - row_size = in - pixmap; - in += ((row_size + 3) & ~3) - row_size; - } - - return EFI_SUCCESS; -} - -EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) { - EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {}; - EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; - EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; - struct bmp_dib *dib; - struct bmp_map *map; - UINT8 *pixmap; - UINT64 blt_size; - VOID *blt = NULL; - UINTN x_pos = 0; - UINTN y_pos = 0; - EFI_STATUS err; - - if (!background) { - if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) { - pixel.Red = 0xc0; - pixel.Green = 0xc0; - pixel.Blue = 0xc0; - } - background = &pixel; - } - - err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); - if (EFI_ERROR(err)) - return err; - - err = bmp_parse_header(content, len, &dib, &map, &pixmap); - if (EFI_ERROR(err)) - goto err; - - if (dib->x < GraphicsOutput->Mode->Info->HorizontalResolution) - x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2; - if (dib->y < GraphicsOutput->Mode->Info->VerticalResolution) - y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2; - - uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, - (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background, - EfiBltVideoFill, 0, 0, 0, 0, - GraphicsOutput->Mode->Info->HorizontalResolution, - GraphicsOutput->Mode->Info->VerticalResolution, 0); - - /* EFI buffer */ - blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); - blt = AllocatePool(blt_size); - if (!blt) - return EFI_OUT_OF_RESOURCES; - - err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, - blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0, - dib->x, dib->y, 0); - if (EFI_ERROR(err)) - goto err; - - err = bmp_to_blt(blt, dib, map, pixmap); - if (EFI_ERROR(err)) - goto err; - - err = graphics_mode(TRUE); - if (EFI_ERROR(err)) - goto err; - - err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, - blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos, - dib->x, dib->y, 0); -err: - FreePool(blt); - return err; -} diff --git a/src/boot/efi/splash.h b/src/boot/efi/splash.h deleted file mode 100644 index 09b543fb47..0000000000 --- a/src/boot/efi/splash.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#ifndef __SDBOOT_SPLASH_H -#define __SDBOOT_SPLASH_H - -EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background); -#endif diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c deleted file mode 100644 index 1e250f34f4..0000000000 --- a/src/boot/efi/stub.c +++ /dev/null @@ -1,130 +0,0 @@ -/* This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2015 Kay Sievers - */ - -#include -#include - -#include "disk.h" -#include "graphics.h" -#include "linux.h" -#include "pefile.h" -#include "splash.h" -#include "util.h" -#include "measure.h" - -/* magic string to find in the binary image */ -static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####"; - -static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; - -EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { - EFI_LOADED_IMAGE *loaded_image; - EFI_FILE *root_dir; - CHAR16 *loaded_image_path; - CHAR8 *b; - UINTN size; - BOOLEAN secure = FALSE; - CHAR8 *sections[] = { - (UINT8 *)".cmdline", - (UINT8 *)".linux", - (UINT8 *)".initrd", - (UINT8 *)".splash", - NULL - }; - UINTN addrs[ELEMENTSOF(sections)-1] = {}; - UINTN offs[ELEMENTSOF(sections)-1] = {}; - UINTN szs[ELEMENTSOF(sections)-1] = {}; - CHAR8 *cmdline = NULL; - UINTN cmdline_len; - CHAR16 uuid[37]; - EFI_STATUS err; - - InitializeLib(image, sys_table); - - err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, - image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(err)) { - Print(L"Error getting a LoadedImageProtocol handle: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; - } - - root_dir = LibOpenRoot(loaded_image->DeviceHandle); - if (!root_dir) { - Print(L"Unable to open root directory: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return EFI_LOAD_ERROR; - } - - loaded_image_path = DevicePathToStr(loaded_image->FilePath); - - if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { - if (*b > 0) - secure = TRUE; - FreePool(b); - } - - err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs); - if (EFI_ERROR(err)) { - Print(L"Unable to locate embedded .linux section: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; - } - - if (szs[0] > 0) - cmdline = (CHAR8 *)(loaded_image->ImageBase + addrs[0]); - - cmdline_len = szs[0]; - - /* if we are not in secure boot mode, accept a custom command line and replace the built-in one */ - if (!secure && loaded_image->LoadOptionsSize > 0) { - CHAR16 *options; - CHAR8 *line; - UINTN i; - - options = (CHAR16 *)loaded_image->LoadOptions; - cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8); - line = AllocatePool(cmdline_len); - for (i = 0; i < cmdline_len; i++) - line[i] = options[i]; - cmdline = line; - -#ifdef SD_BOOT_LOG_TPM - /* Try to log any options to the TPM, escpecially manually edited options */ - err = tpm_log_event(SD_TPM_PCR, - (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, - loaded_image->LoadOptionsSize, loaded_image->LoadOptions); - if (EFI_ERROR(err)) { - Print(L"Unable to add image options measurement: %r", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; - } -#endif - } - - /* export the device path this image is started from */ - if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) - efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); - - if (szs[3] > 0) - graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL); - - err = linux_exec(image, cmdline, cmdline_len, - (UINTN)loaded_image->ImageBase + addrs[1], - (UINTN)loaded_image->ImageBase + addrs[2], szs[2]); - - graphics_mode(FALSE); - Print(L"Execution of embedded linux image failed: %r\n", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return err; -} diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c deleted file mode 100644 index 98c5be74ce..0000000000 --- a/src/boot/efi/util.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#include -#include - -#include "util.h" - -/* - * Allocated random UUID, intended to be shared across tools that implement - * the (ESP)\loader\entries\-.conf convention and the - * associated EFI variables. - */ -static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; - -#ifdef __x86_64__ -UINT64 ticks_read(VOID) { - UINT64 a, d; - __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); - return (d << 32) | a; -} -#elif defined(__i386__) -UINT64 ticks_read(VOID) { - UINT64 val; - __asm__ volatile ("rdtsc" : "=A" (val)); - return val; -} -#else -UINT64 ticks_read(VOID) { - UINT64 val = 1; - return val; -} -#endif - -/* count TSC ticks during a millisecond delay */ -UINT64 ticks_freq(VOID) { - UINT64 ticks_start, ticks_end; - - ticks_start = ticks_read(); - uefi_call_wrapper(BS->Stall, 1, 1000); - ticks_end = ticks_read(); - - return (ticks_end - ticks_start) * 1000; -} - -UINT64 time_usec(VOID) { - UINT64 ticks; - static UINT64 freq; - - ticks = ticks_read(); - if (ticks == 0) - return 0; - - if (freq == 0) { - freq = ticks_freq(); - if (freq == 0) - return 0; - } - - return 1000 * 1000 * ticks / freq; -} - -EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) { - if (strcmpa(v, (CHAR8 *)"1") == 0 || - strcmpa(v, (CHAR8 *)"yes") == 0 || - strcmpa(v, (CHAR8 *)"y") == 0 || - strcmpa(v, (CHAR8 *)"true") == 0) { - *b = TRUE; - return EFI_SUCCESS; - } - - if (strcmpa(v, (CHAR8 *)"0") == 0 || - strcmpa(v, (CHAR8 *)"no") == 0 || - strcmpa(v, (CHAR8 *)"n") == 0 || - strcmpa(v, (CHAR8 *)"false") == 0) { - *b = FALSE; - return EFI_SUCCESS; - } - - return EFI_INVALID_PARAMETER; -} - -EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { - UINT32 flags; - - flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; - if (persistent) - flags |= EFI_VARIABLE_NON_VOLATILE; - - return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf); -} - -EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) { - return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent); -} - -EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) { - CHAR16 str[32]; - - SPrint(str, 32, L"%d", i); - return efivar_set(name, str, persistent); -} - -EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) { - CHAR8 *buf; - CHAR16 *val; - UINTN size; - EFI_STATUS err; - - err = efivar_get_raw(&loader_guid, name, &buf, &size); - if (EFI_ERROR(err)) - return err; - - val = StrDuplicate((CHAR16 *)buf); - if (!val) { - FreePool(buf); - return EFI_OUT_OF_RESOURCES; - } - - *value = val; - return EFI_SUCCESS; -} - -EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) { - CHAR16 *val; - EFI_STATUS err; - - err = efivar_get(name, &val); - if (!EFI_ERROR(err)) { - *i = Atoi(val); - FreePool(val); - } - return err; -} - -EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { - CHAR8 *buf; - UINTN l; - EFI_STATUS err; - - l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; - buf = AllocatePool(l); - if (!buf) - return EFI_OUT_OF_RESOURCES; - - err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf); - if (!EFI_ERROR(err)) { - *buffer = buf; - if (size) - *size = l; - } else - FreePool(buf); - return err; - -} - -VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) { - CHAR16 str[32]; - - if (usec == 0) - usec = time_usec(); - if (usec == 0) - return; - - SPrint(str, 32, L"%ld", usec); - efivar_set(name, str, FALSE); -} - -static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) { - CHAR16 unichar; - UINTN len; - UINTN i; - - if (stra[0] < 0x80) - len = 1; - else if ((stra[0] & 0xe0) == 0xc0) - len = 2; - else if ((stra[0] & 0xf0) == 0xe0) - len = 3; - else if ((stra[0] & 0xf8) == 0xf0) - len = 4; - else if ((stra[0] & 0xfc) == 0xf8) - len = 5; - else if ((stra[0] & 0xfe) == 0xfc) - len = 6; - else - return -1; - - switch (len) { - case 1: - unichar = stra[0]; - break; - case 2: - unichar = stra[0] & 0x1f; - break; - case 3: - unichar = stra[0] & 0x0f; - break; - case 4: - unichar = stra[0] & 0x07; - break; - case 5: - unichar = stra[0] & 0x03; - break; - case 6: - unichar = stra[0] & 0x01; - break; - } - - for (i = 1; i < len; i++) { - if ((stra[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= stra[i] & 0x3f; - } - - *c = unichar; - return len; -} - -CHAR16 *stra_to_str(CHAR8 *stra) { - UINTN strlen; - UINTN len; - UINTN i; - CHAR16 *str; - - len = strlena(stra); - str = AllocatePool((len + 1) * sizeof(CHAR16)); - - strlen = 0; - i = 0; - while (i < len) { - INTN utf8len; - - utf8len = utf8_to_16(stra + i, str + strlen); - if (utf8len <= 0) { - /* invalid utf8 sequence, skip the garbage */ - i++; - continue; - } - - strlen++; - i += utf8len; - } - str[strlen] = '\0'; - return str; -} - -CHAR16 *stra_to_path(CHAR8 *stra) { - CHAR16 *str; - UINTN strlen; - UINTN len; - UINTN i; - - len = strlena(stra); - str = AllocatePool((len + 2) * sizeof(CHAR16)); - - str[0] = '\\'; - strlen = 1; - i = 0; - while (i < len) { - INTN utf8len; - - utf8len = utf8_to_16(stra + i, str + strlen); - if (utf8len <= 0) { - /* invalid utf8 sequence, skip the garbage */ - i++; - continue; - } - - if (str[strlen] == '/') - str[strlen] = '\\'; - if (str[strlen] == '\\' && str[strlen-1] == '\\') { - /* skip double slashes */ - i += utf8len; - continue; - } - - strlen++; - i += utf8len; - } - str[strlen] = '\0'; - return str; -} - -CHAR8 *strchra(CHAR8 *s, CHAR8 c) { - do { - if (*s == c) - return s; - } while (*s++); - return NULL; -} - -INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) { - EFI_FILE_HANDLE handle; - CHAR8 *buf; - UINTN buflen; - EFI_STATUS err; - UINTN len; - - err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL); - if (EFI_ERROR(err)) - return err; - - if (size == 0) { - EFI_FILE_INFO *info; - - info = LibFileInfo(handle); - buflen = info->FileSize+1; - FreePool(info); - } else - buflen = size; - - if (off > 0) { - err = uefi_call_wrapper(handle->SetPosition, 2, handle, off); - if (EFI_ERROR(err)) - return err; - } - - buf = AllocatePool(buflen); - err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf); - if (!EFI_ERROR(err)) { - buf[buflen] = '\0'; - *content = buf; - len = buflen; - } else { - len = err; - FreePool(buf); - } - - uefi_call_wrapper(handle->Close, 1, handle); - return len; -} diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h deleted file mode 100644 index e673cdf9a0..0000000000 --- a/src/boot/efi/util.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * Copyright (C) 2012-2013 Kay Sievers - * Copyright (C) 2012 Harald Hoyer - */ - -#ifndef __SDBOOT_UTIL_H -#define __SDBOOT_UTIL_H - -#include -#include - -#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) - -static inline const CHAR16 *yes_no(BOOLEAN b) { - return b ? L"yes" : L"no"; -} - -EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b); - -UINT64 ticks_read(void); -UINT64 ticks_freq(void); -UINT64 time_usec(void); - -EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent); -EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent); -EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent); -VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec); - -EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value); -EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size); -EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i); - -CHAR8 *strchra(CHAR8 *s, CHAR8 c); -CHAR16 *stra_to_path(CHAR8 *stra); -CHAR16 *stra_to_str(CHAR8 *stra); - -INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content); -#endif diff --git a/src/busctl/Makefile b/src/busctl/Makefile new file mode 100644 index 0000000000..bb41d82b85 --- /dev/null +++ b/src/busctl/Makefile @@ -0,0 +1,37 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += \ + busctl + +busctl_SOURCES = \ + src/libsystemd/sd-bus/busctl.c \ + src/libsystemd/sd-bus/busctl-introspect.c \ + src/libsystemd/sd-bus/busctl-introspect.h + +busctl_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/busctl/busctl-introspect.c b/src/busctl/busctl-introspect.c new file mode 100644 index 0000000000..5c92c55483 --- /dev/null +++ b/src/busctl/busctl-introspect.c @@ -0,0 +1,791 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/xml.h" + +#include "busctl-introspect.h" + +#define NODE_DEPTH_MAX 16 + +typedef struct Context { + const XMLIntrospectOps *ops; + void *userdata; + + char *interface_name; + uint64_t interface_flags; + + char *member_name; + char *member_signature; + char *member_result; + uint64_t member_flags; + bool member_writable; + + const char *current; + void *xml_state; +} Context; + +static void context_reset_member(Context *c) { + free(c->member_name); + free(c->member_signature); + free(c->member_result); + + c->member_name = c->member_signature = c->member_result = NULL; + c->member_flags = 0; + c->member_writable = false; +} + +static void context_reset_interface(Context *c) { + c->interface_name = mfree(c->interface_name); + c->interface_flags = 0; + + context_reset_member(c); +} + +static int parse_xml_annotation(Context *context, uint64_t *flags) { + + enum { + STATE_ANNOTATION, + STATE_NAME, + STATE_VALUE + } state = STATE_ANNOTATION; + + _cleanup_free_ char *field = NULL, *value = NULL; + + assert(context); + + for (;;) { + _cleanup_free_ char *name = NULL; + + int t; + + t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); + if (t < 0) { + log_error("XML parse error."); + return t; + } + + if (t == XML_END) { + log_error("Premature end of XML data."); + return -EBADMSG; + } + + switch (state) { + + case STATE_ANNOTATION: + + if (t == XML_ATTRIBUTE_NAME) { + + if (streq_ptr(name, "name")) + state = STATE_NAME; + + else if (streq_ptr(name, "value")) + state = STATE_VALUE; + + else { + log_error("Unexpected attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) { + + if (flags) { + if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) { + + if (streq_ptr(value, "true")) + *flags |= SD_BUS_VTABLE_DEPRECATED; + + } else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) { + + if (streq_ptr(value, "true")) + *flags |= SD_BUS_VTABLE_METHOD_NO_REPLY; + + } else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) { + + if (streq_ptr(value, "const")) + *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST; + else if (streq_ptr(value, "invalidates")) + *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION; + else if (streq_ptr(value, "false")) + *flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION); + } + } + + return 0; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in . (1)"); + return -EINVAL; + } + + break; + + case STATE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + free(field); + field = name; + name = NULL; + + state = STATE_ANNOTATION; + } else { + log_error("Unexpected token in . (2)"); + return -EINVAL; + } + + break; + + case STATE_VALUE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(value); + value = name; + name = NULL; + + state = STATE_ANNOTATION; + } else { + log_error("Unexpected token in . (3)"); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Bad state"); + } + } +} + +static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) { + + enum { + STATE_NODE, + STATE_NODE_NAME, + STATE_INTERFACE, + STATE_INTERFACE_NAME, + STATE_METHOD, + STATE_METHOD_NAME, + STATE_METHOD_ARG, + STATE_METHOD_ARG_NAME, + STATE_METHOD_ARG_TYPE, + STATE_METHOD_ARG_DIRECTION, + STATE_SIGNAL, + STATE_SIGNAL_NAME, + STATE_SIGNAL_ARG, + STATE_SIGNAL_ARG_NAME, + STATE_SIGNAL_ARG_TYPE, + STATE_PROPERTY, + STATE_PROPERTY_NAME, + STATE_PROPERTY_TYPE, + STATE_PROPERTY_ACCESS, + } state = STATE_NODE; + + _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL; + const char *np = prefix; + int r; + + assert(context); + assert(prefix); + + if (n_depth > NODE_DEPTH_MAX) { + log_error(" depth too high."); + return -EINVAL; + } + + for (;;) { + _cleanup_free_ char *name = NULL; + int t; + + t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); + if (t < 0) { + log_error("XML parse error."); + return t; + } + + if (t == XML_END) { + log_error("Premature end of XML data."); + return -EBADMSG; + } + + switch (state) { + + case STATE_NODE: + if (t == XML_ATTRIBUTE_NAME) { + + if (streq_ptr(name, "name")) + state = STATE_NODE_NAME; + else { + log_error("Unexpected attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_OPEN) { + + if (streq_ptr(name, "interface")) + state = STATE_INTERFACE; + else if (streq_ptr(name, "node")) { + + r = parse_xml_node(context, np, n_depth+1); + if (r < 0) + return r; + } else { + log_error("Unexpected tag %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) { + + if (context->ops->on_path) { + r = context->ops->on_path(node_path ? node_path : np, context->userdata); + if (r < 0) + return r; + } + + return 0; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in . (1)"); + return -EINVAL; + } + + break; + + case STATE_NODE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + free(node_path); + + if (name[0] == '/') { + node_path = name; + name = NULL; + } else { + + if (endswith(prefix, "/")) + node_path = strappend(prefix, name); + else + node_path = strjoin(prefix, "/", name, NULL); + if (!node_path) + return log_oom(); + } + + np = node_path; + state = STATE_NODE; + } else { + log_error("Unexpected token in . (2)"); + return -EINVAL; + } + + break; + + case STATE_INTERFACE: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_INTERFACE_NAME; + else { + log_error("Unexpected attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "method")) + state = STATE_METHOD; + else if (streq_ptr(name, "signal")) + state = STATE_SIGNAL; + else if (streq_ptr(name, "property")) { + context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE; + state = STATE_PROPERTY; + } else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->interface_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) { + + if (n_depth == 0) { + if (context->ops->on_interface) { + r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_interface(context); + } + + state = STATE_NODE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in . (1)"); + return -EINVAL; + } + + break; + + case STATE_INTERFACE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + if (n_depth == 0) { + free(context->interface_name); + context->interface_name = name; + name = NULL; + } + + state = STATE_INTERFACE; + } else { + log_error("Unexpected token in . (2)"); + return -EINVAL; + } + + break; + + case STATE_METHOD: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_METHOD_NAME; + else { + log_error("Unexpected attribute %s", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "arg")) + state = STATE_METHOD_ARG; + else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) { + + if (n_depth == 0) { + if (context->ops->on_method) { + r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in (1)."); + return -EINVAL; + } + + break; + + case STATE_METHOD_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + + state = STATE_METHOD; + } else { + log_error("Unexpected token in (2)."); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_METHOD_ARG_NAME; + else if (streq_ptr(name, "type")) + state = STATE_METHOD_ARG_TYPE; + else if (streq_ptr(name, "direction")) + state = STATE_METHOD_ARG_DIRECTION; + else { + log_error("Unexpected method attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, NULL); + if (r < 0) + return r; + } else { + log_error("Unexpected method tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { + + if (n_depth == 0) { + + if (argument_type) { + if (!argument_direction || streq(argument_direction, "in")) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + } else if (streq(argument_direction, "out")) { + if (!strextend(&context->member_result, argument_type, NULL)) + return log_oom(); + } + } + + argument_type = mfree(argument_type); + argument_direction = mfree(argument_direction); + } + + state = STATE_METHOD; + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in method . (1)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_NAME: + + if (t == XML_ATTRIBUTE_VALUE) + state = STATE_METHOD_ARG; + else { + log_error("Unexpected token in method . (2)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_type); + argument_type = name; + name = NULL; + + state = STATE_METHOD_ARG; + } else { + log_error("Unexpected token in method . (3)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_DIRECTION: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_direction); + argument_direction = name; + name = NULL; + + state = STATE_METHOD_ARG; + } else { + log_error("Unexpected token in method . (4)"); + return -EINVAL; + } + + break; + + case STATE_SIGNAL: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_SIGNAL_NAME; + else { + log_error("Unexpected attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "arg")) + state = STATE_SIGNAL_ARG; + else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) { + + if (n_depth == 0) { + if (context->ops->on_signal) { + r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in . (1)"); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + + state = STATE_SIGNAL; + } else { + log_error("Unexpected token in . (2)"); + return -EINVAL; + } + + break; + + + case STATE_SIGNAL_ARG: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_SIGNAL_ARG_NAME; + else if (streq_ptr(name, "type")) + state = STATE_SIGNAL_ARG_TYPE; + else { + log_error("Unexpected signal attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, NULL); + if (r < 0) + return r; + } else { + log_error("Unexpected signal tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { + + if (argument_type) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + + argument_type = mfree(argument_type); + } + + state = STATE_SIGNAL; + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in signal (1)."); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_ARG_NAME: + + if (t == XML_ATTRIBUTE_VALUE) + state = STATE_SIGNAL_ARG; + else { + log_error("Unexpected token in signal (2)."); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_ARG_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_type); + argument_type = name; + name = NULL; + + state = STATE_SIGNAL_ARG; + } else { + log_error("Unexpected token in signal (3)."); + return -EINVAL; + } + + break; + + case STATE_PROPERTY: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_PROPERTY_NAME; + else if (streq_ptr(name, "type")) + state = STATE_PROPERTY_TYPE; + else if (streq_ptr(name, "access")) + state = STATE_PROPERTY_ACCESS; + else { + log_error("Unexpected attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected tag %s.", name); + return -EINVAL; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) { + + if (n_depth == 0) { + if (context->ops->on_property) { + r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in . (1)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in . (2)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_signature); + context->member_signature = name; + name = NULL; + } + + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in . (3)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_ACCESS: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (streq(name, "readwrite") || streq(name, "write")) + context->member_writable = true; + + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in . (4)"); + return -EINVAL; + } + + break; + } + } +} + +int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) { + Context context = { + .ops = ops, + .userdata = userdata, + .current = xml, + }; + + int r; + + assert(prefix); + assert(xml); + assert(ops); + + for (;;) { + _cleanup_free_ char *name = NULL; + + r = xml_tokenize(&context.current, &name, &context.xml_state, NULL); + if (r < 0) { + log_error("XML parse error"); + goto finish; + } + + if (r == XML_END) { + r = 0; + break; + } + + if (r == XML_TAG_OPEN) { + + if (streq(name, "node")) { + r = parse_xml_node(&context, prefix, 0); + if (r < 0) + goto finish; + } else { + log_error("Unexpected tag '%s' in introspection data.", name); + r = -EBADMSG; + goto finish; + } + } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token."); + r = -EBADMSG; + goto finish; + } + } + +finish: + context_reset_interface(&context); + + return r; +} diff --git a/src/busctl/busctl-introspect.h b/src/busctl/busctl-introspect.h new file mode 100644 index 0000000000..d922e352db --- /dev/null +++ b/src/busctl/busctl-introspect.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +typedef struct XMLIntrospectOps { + int (*on_path)(const char *path, void *userdata); + int (*on_interface)(const char *name, uint64_t flags, void *userdata); + int (*on_method)(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata); + int (*on_signal)(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata); + int (*on_property)(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata); +} XMLIntrospectOps; + +int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata); diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c new file mode 100644 index 0000000000..9e3644728d --- /dev/null +++ b/src/busctl/busctl.c @@ -0,0 +1,2088 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-dump.h" +#include "sd-bus/bus-internal.h" +#include "sd-bus/bus-signature.h" +#include "sd-bus/bus-type.h" +#include "shared/bus-util.h" +#include "shared/pager.h" + +#include "busctl-introspect.h" + +static bool arg_no_pager = false; +static bool arg_legend = true; +static char *arg_address = NULL; +static bool arg_unique = false; +static bool arg_acquired = false; +static bool arg_activatable = false; +static bool arg_show_machine = false; +static char **arg_matches = NULL; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_user = false; +static size_t arg_snaplen = 4096; +static bool arg_list = false; +static bool arg_quiet = false; +static bool arg_verbose = false; +static bool arg_expect_reply = true; +static bool arg_auto_start = true; +static bool arg_allow_interactive_authorization = true; +static bool arg_augment_creds = true; +static usec_t arg_timeout = 0; + +#define NAME_IS_ACQUIRED INT_TO_PTR(1) +#define NAME_IS_ACTIVATABLE INT_TO_PTR(2) + +static int list_bus_names(sd_bus *bus, char **argv) { + _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL; + _cleanup_free_ char **merged = NULL; + _cleanup_hashmap_free_ Hashmap *names = NULL; + char **i; + int r; + size_t max_i = 0; + unsigned n = 0; + void *v; + char *k; + Iterator iterator; + + assert(bus); + + if (!arg_unique && !arg_acquired && !arg_activatable) + arg_unique = arg_acquired = arg_activatable = true; + + r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); + if (r < 0) + return log_error_errno(r, "Failed to list names: %m"); + + pager_open(arg_no_pager, false); + + names = hashmap_new(&string_hash_ops); + if (!names) + return log_oom(); + + STRV_FOREACH(i, acquired) { + max_i = MAX(max_i, strlen(*i)); + + r = hashmap_put(names, *i, NAME_IS_ACQUIRED); + if (r < 0) + return log_error_errno(r, "Failed to add to hashmap: %m"); + } + + STRV_FOREACH(i, activatable) { + max_i = MAX(max_i, strlen(*i)); + + r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to add to hashmap: %m"); + } + + merged = new(char*, hashmap_size(names) + 1); + HASHMAP_FOREACH_KEY(v, k, names, iterator) + merged[n++] = k; + + merged[n] = NULL; + strv_sort(merged); + + if (arg_legend) { + printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s", + (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION"); + + if (arg_show_machine) + puts(" MACHINE"); + else + putchar('\n'); + } + + STRV_FOREACH(i, merged) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_id128_t mid; + + if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) { + /* Activatable */ + + printf("%-*s", (int) max_i, *i); + printf(" - - - (activatable) - - "); + if (arg_show_machine) + puts(" -"); + else + putchar('\n'); + continue; + + } + + if (!arg_unique && (*i)[0] == ':') + continue; + + if (!arg_acquired && (*i)[0] != ':') + continue; + + printf("%-*s", (int) max_i, *i); + + r = sd_bus_get_name_creds( + bus, *i, + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | + SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| + SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| + SD_BUS_CREDS_DESCRIPTION, &creds); + if (r >= 0) { + const char *unique, *session, *unit, *cn; + pid_t pid; + uid_t uid; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r >= 0) { + const char *comm = NULL; + + sd_bus_creds_get_comm(creds, &comm); + + printf(" %10lu %-15s", (unsigned long) pid, strna(comm)); + } else + fputs(" - - ", stdout); + + r = sd_bus_creds_get_euid(creds, &uid); + if (r >= 0) { + _cleanup_free_ char *u = NULL; + + u = uid_to_name(uid); + if (!u) + return log_oom(); + + if (strlen(u) > 16) + u[16] = 0; + + printf(" %-16s", u); + } else + fputs(" - ", stdout); + + r = sd_bus_creds_get_unique_name(creds, &unique); + if (r >= 0) + printf(" %-13s", unique); + else + fputs(" - ", stdout); + + r = sd_bus_creds_get_unit(creds, &unit); + if (r >= 0) { + _cleanup_free_ char *e; + + e = ellipsize(unit, 25, 100); + if (!e) + return log_oom(); + + printf(" %-25s", e); + } else + fputs(" - ", stdout); + + r = sd_bus_creds_get_session(creds, &session); + if (r >= 0) + printf(" %-10s", session); + else + fputs(" - ", stdout); + + r = sd_bus_creds_get_description(creds, &cn); + if (r >= 0) + printf(" %-19s", cn); + else + fputs(" - ", stdout); + + } else + printf(" - - - - - - - "); + + if (arg_show_machine) { + r = sd_bus_get_name_machine_id(bus, *i, &mid); + if (r >= 0) { + char m[SD_ID128_STRING_MAX]; + printf(" %s\n", sd_id128_to_string(mid, m)); + } else + puts(" -"); + } else + putchar('\n'); + } + + return 0; +} + +static void print_subtree(const char *prefix, const char *path, char **l) { + const char *vertical, *space; + char **n; + + /* We assume the list is sorted. Let's first skip over the + * entry we are looking at. */ + for (;;) { + if (!*l) + return; + + if (!streq(*l, path)) + break; + + l++; + } + + vertical = strjoina(prefix, special_glyph(TREE_VERTICAL)); + space = strjoina(prefix, special_glyph(TREE_SPACE)); + + for (;;) { + bool has_more = false; + + if (!*l || !path_startswith(*l, path)) + break; + + n = l + 1; + for (;;) { + if (!*n || !path_startswith(*n, path)) + break; + + if (!path_startswith(*n, *l)) { + has_more = true; + break; + } + + n++; + } + + printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l); + + print_subtree(has_more ? vertical : space, *l, l); + l = n; + } +} + +static void print_tree(const char *prefix, char **l) { + + pager_open(arg_no_pager, false); + + prefix = strempty(prefix); + + if (arg_list) { + char **i; + + STRV_FOREACH(i, l) + printf("%s%s\n", prefix, *i); + return; + } + + if (strv_isempty(l)) { + printf("No objects discovered.\n"); + return; + } + + if (streq(l[0], "/") && !l[1]) { + printf("Only root object discovered.\n"); + return; + } + + print_subtree(prefix, "/", l); +} + +static int on_path(const char *path, void *userdata) { + Set *paths = userdata; + int r; + + assert(paths); + + r = set_put_strdup(paths, path); + if (r < 0) + return log_oom(); + + return 0; +} + +static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) { + static const XMLIntrospectOps ops = { + .on_path = on_path, + }; + + _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; + + r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + if (r < 0) { + if (many) + printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r)); + else + log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &xml); + if (r < 0) + return bus_log_parse_error(r); + + return parse_xml_introspect(path, xml, &ops, paths); +} + +static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) { + _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL; + _cleanup_free_ char **l = NULL; + char *m; + int r; + + paths = set_new(&string_hash_ops); + if (!paths) + return log_oom(); + + done = set_new(&string_hash_ops); + if (!done) + return log_oom(); + + failed = set_new(&string_hash_ops); + if (!failed) + return log_oom(); + + m = strdup("/"); + if (!m) + return log_oom(); + + r = set_put(paths, m); + if (r < 0) { + free(m); + return log_oom(); + } + + for (;;) { + _cleanup_free_ char *p = NULL; + int q; + + p = set_steal_first(paths); + if (!p) + break; + + if (set_contains(done, p) || + set_contains(failed, p)) + continue; + + q = find_nodes(bus, service, p, paths, many); + if (q < 0) { + if (r >= 0) + r = q; + + q = set_put(failed, p); + } else + q = set_put(done, p); + + if (q < 0) + return log_oom(); + + assert(q != 0); + p = NULL; + } + + pager_open(arg_no_pager, false); + + l = set_get_strv(done); + if (!l) + return log_oom(); + + strv_sort(l); + print_tree(prefix, l); + + fflush(stdout); + + return r; +} + +static int tree(sd_bus *bus, char **argv) { + char **i; + int r = 0; + + if (!arg_unique && !arg_acquired) + arg_acquired = true; + + if (strv_length(argv) <= 1) { + _cleanup_strv_free_ char **names = NULL; + bool not_first = false; + + r = sd_bus_list_names(bus, &names, NULL); + if (r < 0) + return log_error_errno(r, "Failed to get name list: %m"); + + pager_open(arg_no_pager, false); + + STRV_FOREACH(i, names) { + int q; + + if (!arg_unique && (*i)[0] == ':') + continue; + + if (!arg_acquired && (*i)[0] == ':') + continue; + + if (not_first) + printf("\n"); + + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); + + q = tree_one(bus, *i, NULL, true); + if (q < 0 && r >= 0) + r = q; + + not_first = true; + } + } else { + STRV_FOREACH(i, argv+1) { + int q; + + if (i > argv+1) + printf("\n"); + + if (argv[2]) { + pager_open(arg_no_pager, false); + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); + } + + q = tree_one(bus, *i, NULL, !!argv[2]); + if (q < 0 && r >= 0) + r = q; + } + } + + return r; +} + +static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { + int r; + + for (;;) { + const char *contents = NULL; + char type; + union { + uint8_t u8; + uint16_t u16; + int16_t s16; + uint32_t u32; + int32_t s32; + uint64_t u64; + int64_t s64; + double d64; + const char *string; + int i; + } basic; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + if (r == 0) + return needs_space; + + if (bus_type_is_container(type) > 0) { + + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return r; + + if (type == SD_BUS_TYPE_ARRAY) { + unsigned n = 0; + + /* count array entries */ + for (;;) { + + r = sd_bus_message_skip(m, contents); + if (r < 0) + return r; + if (r == 0) + break; + + n++; + } + + r = sd_bus_message_rewind(m, false); + if (r < 0) + return r; + + if (needs_space) + fputc(' ', f); + + fprintf(f, "%u", n); + needs_space = true; + + } else if (type == SD_BUS_TYPE_VARIANT) { + + if (needs_space) + fputc(' ', f); + + fprintf(f, "%s", contents); + needs_space = true; + } + + r = format_cmdline(m, f, needs_space); + if (r < 0) + return r; + + needs_space = r > 0; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + continue; + } + + r = sd_bus_message_read_basic(m, type, &basic); + if (r < 0) + return r; + + if (needs_space) + fputc(' ', f); + + switch (type) { + case SD_BUS_TYPE_BYTE: + fprintf(f, "%u", basic.u8); + break; + + case SD_BUS_TYPE_BOOLEAN: + fputs(true_false(basic.i), f); + break; + + case SD_BUS_TYPE_INT16: + fprintf(f, "%i", basic.s16); + break; + + case SD_BUS_TYPE_UINT16: + fprintf(f, "%u", basic.u16); + break; + + case SD_BUS_TYPE_INT32: + fprintf(f, "%i", basic.s32); + break; + + case SD_BUS_TYPE_UINT32: + fprintf(f, "%u", basic.u32); + break; + + case SD_BUS_TYPE_INT64: + fprintf(f, "%" PRIi64, basic.s64); + break; + + case SD_BUS_TYPE_UINT64: + fprintf(f, "%" PRIu64, basic.u64); + break; + + case SD_BUS_TYPE_DOUBLE: + fprintf(f, "%g", basic.d64); + break; + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: { + _cleanup_free_ char *b = NULL; + + b = cescape(basic.string); + if (!b) + return -ENOMEM; + + fprintf(f, "\"%s\"", b); + break; + } + + case SD_BUS_TYPE_UNIX_FD: + fprintf(f, "%i", basic.i); + break; + + default: + assert_not_reached("Unknown basic type."); + } + + needs_space = true; + } +} + +typedef struct Member { + const char *type; + char *interface; + char *name; + char *signature; + char *result; + char *value; + bool writable; + uint64_t flags; +} Member; + +static void member_hash_func(const void *p, struct siphash *state) { + const Member *m = p; + uint64_t arity = 1; + + assert(m); + assert(m->type); + + string_hash_func(m->type, state); + + arity += !!m->name + !!m->interface; + + uint64_hash_func(&arity, state); + + if (m->name) + string_hash_func(m->name, state); + + if (m->interface) + string_hash_func(m->interface, state); +} + +static int member_compare_func(const void *a, const void *b) { + const Member *x = a, *y = b; + int d; + + assert(x); + assert(y); + assert(x->type); + assert(y->type); + + d = strcmp_ptr(x->interface, y->interface); + if (d != 0) + return d; + + d = strcmp(x->type, y->type); + if (d != 0) + return d; + + return strcmp_ptr(x->name, y->name); +} + +static int member_compare_funcp(const void *a, const void *b) { + const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b; + + return member_compare_func(*x, *y); +} + +static void member_free(Member *m) { + if (!m) + return; + + free(m->interface); + free(m->name); + free(m->signature); + free(m->result); + free(m->value); + free(m); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free); + +static void member_set_free(Set *s) { + Member *m; + + while ((m = set_steal_first(s))) + member_free(m); + + set_free(s); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free); + +static int on_interface(const char *interface, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(members); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "interface"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate interface"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "method"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->result, result); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate method"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "signal"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate signal"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "property"; + m->flags = flags; + m->writable = writable; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate property"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static const char *strdash(const char *x) { + return isempty(x) ? "-" : x; +} + +static int introspect(sd_bus *bus, char **argv) { + static const struct hash_ops member_hash_ops = { + .hash = member_hash_func, + .compare = member_compare_func, + }; + + static const XMLIntrospectOps ops = { + .on_interface = on_interface, + .on_method = on_method, + .on_signal = on_signal, + .on_property = on_property, + }; + + _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; + const char *xml; + int r; + unsigned name_width, type_width, signature_width, result_width; + Member **sorted = NULL; + unsigned k = 0, j, n_args; + + n_args = strv_length(argv); + if (n_args < 3) { + log_error("Requires service and object path argument."); + return -EINVAL; + } + + if (n_args > 4) { + log_error("Too many arguments."); + return -EINVAL; + } + + members = set_new(&member_hash_ops); + if (!members) + return log_oom(); + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + if (r < 0) { + log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &xml); + if (r < 0) + return bus_log_parse_error(r); + + /* First, get list of all properties */ + r = parse_xml_introspect(argv[2], xml, &ops, members); + if (r < 0) + return r; + + /* Second, find the current values for them */ + SET_FOREACH(m, members, i) { + + if (!streq(m->type, "property")) + continue; + + if (m->value) + continue; + + if (argv[3] && !streq(argv[3], m->interface)) + continue; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + Member *z; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *mf = NULL; + size_t sz = 0; + const char *name; + + r = sd_bus_message_enter_container(reply, 'e', "sv"); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) + break; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'v', NULL); + if (r < 0) + return bus_log_parse_error(r); + + mf = open_memstream(&buf, &sz); + if (!mf) + return log_oom(); + + r = format_cmdline(reply, mf, false); + if (r < 0) + return bus_log_parse_error(r); + + fclose(mf); + mf = NULL; + + z = set_get(members, &((Member) { + .type = "property", + .interface = m->interface, + .name = (char*) name })); + if (z) { + free(z->value); + z->value = buf; + buf = NULL; + } + + r = sd_bus_message_exit_container(reply); + 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_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + + pager_open(arg_no_pager, false); + + name_width = strlen("NAME"); + type_width = strlen("TYPE"); + signature_width = strlen("SIGNATURE"); + result_width = strlen("RESULT/VALUE"); + + sorted = newa(Member*, set_size(members)); + + SET_FOREACH(m, members, i) { + + if (argv[3] && !streq(argv[3], m->interface)) + continue; + + if (m->interface) + name_width = MAX(name_width, strlen(m->interface)); + if (m->name) + name_width = MAX(name_width, strlen(m->name) + 1); + if (m->type) + type_width = MAX(type_width, strlen(m->type)); + if (m->signature) + signature_width = MAX(signature_width, strlen(m->signature)); + if (m->result) + result_width = MAX(result_width, strlen(m->result)); + if (m->value) + result_width = MAX(result_width, strlen(m->value)); + + sorted[k++] = m; + } + + if (result_width > 40) + result_width = 40; + + qsort(sorted, k, sizeof(Member*), member_compare_funcp); + + if (arg_legend) { + printf("%-*s %-*s %-*s %-*s %s\n", + (int) name_width, "NAME", + (int) type_width, "TYPE", + (int) signature_width, "SIGNATURE", + (int) result_width, "RESULT/VALUE", + "FLAGS"); + } + + for (j = 0; j < k; j++) { + _cleanup_free_ char *ellipsized = NULL; + const char *rv; + bool is_interface; + + m = sorted[j]; + + if (argv[3] && !streq(argv[3], m->interface)) + continue; + + is_interface = streq(m->type, "interface"); + + if (argv[3] && is_interface) + continue; + + if (m->value) { + ellipsized = ellipsize(m->value, result_width, 100); + if (!ellipsized) + return log_oom(); + + rv = ellipsized; + } else + rv = strdash(m->result); + + printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", + is_interface ? ansi_highlight() : "", + is_interface ? "" : ".", + - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name), + is_interface ? ansi_normal() : "", + (int) type_width, strdash(m->type), + (int) signature_width, strdash(m->signature), + (int) result_width, rv, + (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"), + (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "", + m->writable ? " writable" : ""); + } + + return 0; +} + +static int message_dump(sd_bus_message *m, FILE *f) { + return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER); +} + +static int message_pcap(sd_bus_message *m, FILE *f) { + return bus_message_pcap_frame(m, arg_snaplen, f); +} + +static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char **i; + uint32_t flags = 0; + int r; + + /* upgrade connection; it's not used for anything else after this call */ + r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(message, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + STRV_FOREACH(i, argv+1) { + _cleanup_free_ char *m = NULL; + + if (!service_name_is_valid(*i)) { + log_error("Invalid service name '%s'", *i); + return -EINVAL; + } + + m = strjoin("sender='", *i, "'", NULL); + if (!m) + return log_oom(); + + r = sd_bus_message_append_basic(message, 's', m); + if (r < 0) + return bus_log_create_error(r); + + free(m); + m = strjoin("destination='", *i, "'", NULL); + if (!m) + return log_oom(); + + r = sd_bus_message_append_basic(message, 's', m); + if (r < 0) + return bus_log_create_error(r); + } + + STRV_FOREACH(i, arg_matches) { + r = sd_bus_message_append_basic(message, 's', *i); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(message); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(message, 'u', &flags); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, message, arg_timeout, &error, NULL); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + log_info("Monitoring bus message stream."); + + for (;;) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_process(bus, &m); + if (r < 0) + return log_error_errno(r, "Failed to process bus: %m"); + + if (m) { + dump(m, stdout); + fflush(stdout); + + if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) { + log_info("Connection terminated, exiting."); + return 0; + } + + continue; + } + + if (r > 0) + continue; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return log_error_errno(r, "Failed to wait for bus: %m"); + } +} + +static int capture(sd_bus *bus, char *argv[]) { + int r; + + if (isatty(fileno(stdout)) > 0) { + log_error("Refusing to write message data to console, please redirect output to a file."); + return -EINVAL; + } + + bus_pcap_header(arg_snaplen, stdout); + + r = monitor(bus, argv, message_pcap); + if (r < 0) + return r; + + if (ferror(stdout)) { + log_error("Couldn't write capture file."); + return -EIO; + } + + return r; +} + +static int status(sd_bus *bus, char *argv[]) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + pid_t pid; + int r; + + assert(bus); + + if (strv_length(argv) > 2) { + log_error("Expects no or one argument."); + return -EINVAL; + } + + if (argv[1]) { + r = parse_pid(argv[1], &pid); + if (r < 0) + r = sd_bus_get_name_creds( + bus, + argv[1], + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, + &creds); + else + r = sd_bus_creds_new_from_pid( + &creds, + pid, + _SD_BUS_CREDS_ALL); + } else { + const char *scope, *address; + sd_id128_t bus_id; + + r = sd_bus_get_address(bus, &address); + if (r >= 0) + printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal()); + + r = sd_bus_get_scope(bus, &scope); + if (r >= 0) + printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal()); + + r = sd_bus_get_bus_id(bus, &bus_id); + if (r >= 0) + printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal()); + + r = sd_bus_get_owner_creds( + bus, + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, + &creds); + } + + if (r < 0) + return log_error_errno(r, "Failed to get credentials: %m"); + + bus_creds_dump(creds, NULL, false); + return 0; +} + +static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) { + char **p; + int r; + + assert(m); + assert(signature); + assert(x); + + p = *x; + + for (;;) { + const char *v; + char t; + + t = *signature; + v = *p; + + if (t == 0) + break; + if (!v) { + log_error("Too few parameters for signature."); + return -EINVAL; + } + + signature++; + p++; + + switch (t) { + + case SD_BUS_TYPE_BOOLEAN: + + r = parse_boolean(v); + if (r < 0) { + log_error("Failed to parse as boolean: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &r); + break; + + case SD_BUS_TYPE_BYTE: { + uint8_t z; + + r = safe_atou8(v, &z); + if (r < 0) { + log_error("Failed to parse as byte (unsigned 8bit integer): %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT16: { + int16_t z; + + r = safe_atoi16(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT16: { + uint16_t z; + + r = safe_atou16(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT32: { + int32_t z; + + r = safe_atoi32(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t z; + + r = safe_atou32(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT64: { + int64_t z; + + r = safe_atoi64(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t z; + + r = safe_atou64(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + + case SD_BUS_TYPE_DOUBLE: { + double z; + + r = safe_atod(v, &z); + if (r < 0) { + log_error("Failed to parse as double precision floating point: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + + r = sd_bus_message_append_basic(m, t, v); + break; + + case SD_BUS_TYPE_ARRAY: { + uint32_t n; + size_t k; + + r = safe_atou32(v, &n); + if (r < 0) { + log_error("Failed to parse number of array entries: %s", v); + return r; + } + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid array signature."); + return r; + } + + { + unsigned i; + char s[k + 1]; + memcpy(s, signature, k); + s[k] = 0; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); + if (r < 0) + return bus_log_create_error(r); + + for (i = 0; i < n; i++) { + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_VARIANT: + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, v, &p); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + signature--; + p--; + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid struct/dict entry signature."); + return r; + } + + { + char s[k-1]; + memcpy(s, signature + 1, k - 2); + s[k - 2] = 0; + + r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_UNIX_FD: + log_error("UNIX file descriptor not supported as type."); + return -EINVAL; + + default: + log_error("Unknown signature type %c.", t); + return -EINVAL; + } + + if (r < 0) + return bus_log_create_error(r); + } + + *x = p; + return 0; +} + +static int call(sd_bus *bus, char *argv[]) { + _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); + + if (strv_length(argv) < 5) { + log_error("Expects at least four arguments."); + return -EINVAL; + } + + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_expect_reply(m, arg_expect_reply); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_auto_start(m, arg_auto_start); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(argv[5])) { + char **p; + + p = argv+6; + + r = message_append_cmdline(m, argv[5], &p); + if (r < 0) + return r; + + if (*p) { + log_error("Too many parameters for signature."); + return -EINVAL; + } + } + + if (!arg_expect_reply) { + r = sd_bus_send(bus, m, NULL); + if (r < 0) { + log_error("Failed to send message."); + return r; + } + + return 0; + } + + r = sd_bus_call(bus, m, arg_timeout, &error, &reply); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_is_empty(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0 && !arg_quiet) { + + if (arg_verbose) { + pager_open(arg_no_pager, false); + + r = bus_message_dump(reply, stdout, 0); + if (r < 0) + return r; + } else { + + fputs(sd_bus_message_get_signature(reply, true), stdout); + fputc(' ', stdout); + + r = format_cmdline(reply, stdout, false); + if (r < 0) + return bus_log_parse_error(r); + + fputc('\n', stdout); + } + } + + return 0; +} + +static int get_property(sd_bus *bus, char *argv[]) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + unsigned n; + char **i; + int r; + + assert(bus); + + n = strv_length(argv); + if (n < 5) { + log_error("Expects at least four arguments."); + return -EINVAL; + } + + STRV_FOREACH(i, argv + 4) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *contents = NULL; + char type; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_peek_type(reply, &type, &contents); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'v', contents); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_verbose) { + pager_open(arg_no_pager, false); + + r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); + if (r < 0) + return r; + } else { + fputs(contents, stdout); + fputc(' ', stdout); + + r = format_cmdline(reply, stdout, false); + if (r < 0) + return bus_log_parse_error(r); + + fputc('\n', stdout); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + + return 0; +} + +static int set_property(sd_bus *bus, char *argv[]) { + _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; + + assert(bus); + + n = strv_length(argv); + if (n < 6) { + log_error("Expects at least five arguments."); + return -EINVAL; + } + + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ss", argv[3], argv[4]); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', argv[5]); + if (r < 0) + return bus_log_create_error(r); + + p = argv+6; + r = message_append_cmdline(m, argv[5], &p); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + if (*p) { + log_error("Too many parameters for signature."); + return -EINVAL; + } + + r = sd_bus_call(bus, m, arg_timeout, &error, NULL); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + return 0; +} + +static int help(void) { + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Introspect the bus.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --system Connect to system bus\n" + " --user Connect to user bus\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --address=ADDRESS Connect to bus specified by address\n" + " --show-machine Show machine ID column in list\n" + " --unique Only show unique names\n" + " --acquired Only show acquired names\n" + " --activatable Only show activatable names\n" + " --match=MATCH Only show matching messages\n" + " --size=SIZE Maximum length of captured packet\n" + " --list Don't show tree, but simple object path list\n" + " --quiet Don't show method call reply\n" + " --verbose Show result values in long format\n" + " --expect-reply=BOOL Expect a method call reply\n" + " --auto-start=BOOL Auto-start destination service\n" + " --allow-interactive-authorization=BOOL\n" + " Allow interactive authorization for operation\n" + " --timeout=SECS Maximum time to wait for method call completion\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" + "Commands:\n" + " list List bus names\n" + " status [SERVICE] Show bus service, process or bus owner credentials\n" + " monitor [SERVICE...] Show bus traffic\n" + " capture [SERVICE...] Capture bus traffic as pcap\n" + " tree [SERVICE...] Show object tree of service\n" + " introspect SERVICE OBJECT [INTERFACE]\n" + " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" + " Call a method\n" + " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" + " Get property value\n" + " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" + " Set property value\n" + " help Show this help\n" + , program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, + ARG_SYSTEM, + ARG_USER, + ARG_ADDRESS, + ARG_MATCH, + ARG_SHOW_MACHINE, + ARG_UNIQUE, + ARG_ACQUIRED, + ARG_ACTIVATABLE, + ARG_SIZE, + ARG_LIST, + ARG_VERBOSE, + ARG_EXPECT_REPLY, + ARG_AUTO_START, + ARG_ALLOW_INTERACTIVE_AUTHORIZATION, + ARG_TIMEOUT, + ARG_AUGMENT_CREDS, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + { "address", required_argument, NULL, ARG_ADDRESS }, + { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE }, + { "unique", no_argument, NULL, ARG_UNIQUE }, + { "acquired", no_argument, NULL, ARG_ACQUIRED }, + { "activatable", no_argument, NULL, ARG_ACTIVATABLE }, + { "match", required_argument, NULL, ARG_MATCH }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "size", required_argument, NULL, ARG_SIZE }, + { "list", no_argument, NULL, ARG_LIST }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, ARG_VERBOSE }, + { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY }, + { "auto-start", required_argument, NULL, ARG_AUTO_START }, + { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, + {}, + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(); + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + + case ARG_USER: + arg_user = true; + break; + + case ARG_SYSTEM: + arg_user = false; + break; + + case ARG_ADDRESS: + arg_address = optarg; + break; + + case ARG_SHOW_MACHINE: + arg_show_machine = true; + break; + + case ARG_UNIQUE: + arg_unique = true; + break; + + case ARG_ACQUIRED: + arg_acquired = true; + break; + + case ARG_ACTIVATABLE: + arg_activatable = true; + break; + + case ARG_MATCH: + if (strv_extend(&arg_matches, optarg) < 0) + return log_oom(); + break; + + case ARG_SIZE: { + uint64_t sz; + + r = parse_size(optarg, 1024, &sz); + if (r < 0) { + log_error("Failed to parse size: %s", optarg); + return r; + } + + if ((uint64_t) (size_t) sz != sz) { + log_error("Size out of range."); + return -E2BIG; + } + + arg_snaplen = (size_t) sz; + break; + } + + case ARG_LIST: + arg_list = true; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case 'q': + arg_quiet = true; + break; + + case ARG_VERBOSE: + arg_verbose = true; + break; + + case ARG_EXPECT_REPLY: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --expect-reply= parameter."); + return r; + } + + arg_expect_reply = !!r; + break; + + + case ARG_AUTO_START: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --auto-start= parameter."); + return r; + } + + arg_auto_start = !!r; + break; + + + case ARG_ALLOW_INTERACTIVE_AUTHORIZATION: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --allow-interactive-authorization= parameter."); + return r; + } + + arg_allow_interactive_authorization = !!r; + break; + + case ARG_TIMEOUT: + r = parse_sec(optarg, &arg_timeout); + if (r < 0) { + log_error("Failed to parse --timeout= parameter."); + return r; + } + + break; + + case ARG_AUGMENT_CREDS: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --augment-creds= parameter."); + return r; + } + + arg_augment_creds = !!r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int busctl_main(sd_bus *bus, int argc, char *argv[]) { + assert(bus); + + if (optind >= argc || + streq(argv[optind], "list")) + return list_bus_names(bus, argv + optind); + + if (streq(argv[optind], "monitor")) + return monitor(bus, argv + optind, message_dump); + + if (streq(argv[optind], "capture")) + return capture(bus, argv + optind); + + if (streq(argv[optind], "status")) + return status(bus, argv + optind); + + if (streq(argv[optind], "tree")) + return tree(bus, argv + optind); + + if (streq(argv[optind], "introspect")) + return introspect(bus, argv + optind); + + if (streq(argv[optind], "call")) + return call(bus, argv + optind); + + if (streq(argv[optind], "get-property")) + return get_property(bus, argv + optind); + + if (streq(argv[optind], "set-property")) + return set_property(bus, argv + optind); + + if (streq(argv[optind], "help")) + return help(); + + log_error("Unknown command '%s'", argv[optind]); + return -EINVAL; +} + +int main(int argc, char *argv[]) { + sd_bus *bus = NULL; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_bus_new(&bus); + if (r < 0) { + log_error_errno(r, "Failed to allocate bus: %m"); + goto finish; + } + + if (streq_ptr(argv[optind], "monitor") || + streq_ptr(argv[optind], "capture")) { + + r = sd_bus_set_monitor(bus, true); + if (r < 0) { + log_error_errno(r, "Failed to set monitor mode: %m"); + goto finish; + } + + r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL); + if (r < 0) { + log_error_errno(r, "Failed to enable credentials: %m"); + goto finish; + } + + r = sd_bus_negotiate_timestamp(bus, true); + if (r < 0) { + log_error_errno(r, "Failed to enable timestamps: %m"); + goto finish; + } + + r = sd_bus_negotiate_fds(bus, true); + if (r < 0) { + log_error_errno(r, "Failed to enable fds: %m"); + goto finish; + } + } + + r = sd_bus_set_bus_client(bus, true); + if (r < 0) { + log_error_errno(r, "Failed to set bus client: %m"); + goto finish; + } + + if (arg_address) + r = sd_bus_set_address(bus, arg_address); + else { + switch (arg_transport) { + + case BUS_TRANSPORT_LOCAL: + if (arg_user) { + bus->is_user = true; + r = bus_set_address_user(bus); + } else { + bus->is_system = true; + r = bus_set_address_system(bus); + } + break; + + case BUS_TRANSPORT_REMOTE: + r = bus_set_address_system_remote(bus, arg_host); + break; + + case BUS_TRANSPORT_MACHINE: + r = bus_set_address_system_machine(bus, arg_host); + break; + + default: + assert_not_reached("Hmm, unknown transport type."); + } + } + if (r < 0) { + log_error_errno(r, "Failed to set address: %m"); + goto finish; + } + + r = sd_bus_start(bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to bus: %m"); + goto finish; + } + + r = busctl_main(bus, argc, argv); + +finish: + sd_bus_flush_close_unref(bus); + pager_close(); + + strv_free(arg_matches); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/busctl/busctl.completion.bash b/src/busctl/busctl.completion.bash new file mode 100644 index 0000000000..6a770b1b84 --- /dev/null +++ b/src/busctl/busctl.completion.bash @@ -0,0 +1,189 @@ +# busctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +__get_busnames() { + local mode=$1 + local a b + busctl $mode list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +__get_objects() { + local mode=$1 + local busname=$2 + local a b + busctl $mode tree --list --no-legend --no-pager $busname | { while read a b; do echo " $a"; done; }; +} + +__get_interfaces() { + local mode=$1 + local busname=$2 + local path=$3 + local a b + busctl $mode introspect --list --no-legend --no-pager $busname $path | { while read a b c; do [[ "$b" == "interface" ]] && echo " $a"; done; }; +} + +__get_members() { + local mode=$1 + local busname=$2 + local path=$3 + local interface=$4 + local type=$5 + local a b + busctl $mode introspect --list --no-legend --no-pager $busname $path $interface | sed -e 's/^\.//' | { while read a b c; do [[ "$b" == "$type" ]] && echo " $a"; done; }; +} + +__get_signature() { + local mode=$1 + local busname=$2 + local path=$3 + local interface=$4 + local member=$5 + local a b + busctl $mode introspect --list --no-legend --no-pager $busname $path $interface | sed -e 's/^\.//' | { while read a b c d; do [[ "$a" == "$member" ]] && echo " \"$c\""; done; }; +} + +_busctl() { + local i verb comps mode + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version --no-pager --no-legend --system --user + --show-machine --unique --acquired --activatable --list + --quiet --verbose --expect-reply=no --auto-start=no + --allow-interactive-authorization=yes --augment-creds=no' + [ARG]='-H --host -M --machine --address --match --timeout' + ) + + if __contains_word "--user" ${COMP_WORDS[*]}; then + mode=--user + else + mode=--system + fi + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --host|-H) + comps=$(compgen -A hostname) + ;; + --machine|-M) + comps=$( __get_machines ) + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [STANDALONE]='list help' + [BUSNAME]='status monitor capture tree' + [OBJECT]='introspect' + [METHOD]='call' + [PROPERTY_GET]='get-property' + [PROPERTY_SET]='set-property' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + n=$(($COMP_CWORD - $i)) + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + elif __contains_word "$verb" ${VERBS[BUSNAME]}; then + comps=$( __get_busnames $mode) + elif __contains_word "$verb" ${VERBS[OBJECT]}; then + if [[ $n -eq 1 ]] ; then + comps=$( __get_busnames $mode) + elif [[ $n -eq 2 ]] ; then + comps=$( __get_objects $mode ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 3 ]] ; then + comps=$( __get_interfaces $mode ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + else + comps='' + fi + elif __contains_word "$verb" ${VERBS[METHOD]}; then + if [[ $n -eq 1 ]] ; then + comps=$( __get_busnames $mode) + elif [[ $n -eq 2 ]] ; then + comps=$( __get_objects $mode ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 3 ]] ; then + comps=$( __get_interfaces $mode ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 4 ]] ; then + comps=$( __get_members $mode ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]} method) + elif [[ $n -eq 5 ]] ; then + comps=$( __get_signature $mode ${COMP_WORDS[COMP_CWORD-4]} ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + else + comps='' + fi + elif __contains_word "$verb" ${VERBS[PROPERTY_GET]}; then + if [[ $n -eq 1 ]] ; then + comps=$( __get_busnames $mode) + elif [[ $n -eq 2 ]] ; then + comps=$( __get_objects $mode ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 3 ]] ; then + comps=$( __get_interfaces $mode ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 4 ]] ; then + comps=$( __get_members $mode ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]} property) + else + comps='' + fi + elif __contains_word "$verb" ${VERBS[PROPERTY_SET]}; then + if [[ $n -eq 1 ]] ; then + comps=$( __get_busnames $mode) + elif [[ $n -eq 2 ]] ; then + comps=$( __get_objects $mode ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 3 ]] ; then + comps=$( __get_interfaces $mode ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + elif [[ $n -eq 4 ]] ; then + comps=$( __get_members $mode ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]} property) + elif [[ $n -eq 5 ]] ; then + comps=$( __get_signature $mode ${COMP_WORDS[COMP_CWORD-4]} ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]}) + else + comps='' + fi + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _busctl busctl diff --git a/src/busctl/busctl.completion.zsh b/src/busctl/busctl.completion.zsh new file mode 100644 index 0000000000..a425b8c700 --- /dev/null +++ b/src/busctl/busctl.completion.zsh @@ -0,0 +1,72 @@ +#compdef busctl + +# busctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +(( $+functions[_busctl_command] )) || _busctl_command() +{ + local -a _busctl_cmds + _busctl_cmds=( + "list:List bus names" + "status:Show bus service, process or bus owner credentials" + "monitor:Show bus traffic" + "capture:Capture bus traffix as pcap" + "tree:Show object tree of service" + "introspect:Introspect object" + "call:Call a method" + "get-property:Get property value" + "set-property:Set property value" + ) + if (( CURRENT == 1 )); then + _describe -t commands 'busctl command' _busctl_cmds || compadd "$@" + else + local curcontext="$curcontext" + cmd="${${_busctl_cmds[(r)$words[1]:*]%%:*}}" + if (( $+functions[_busctl_$cmd] )); then + _busctl_$cmd + else + _message "no more options" + fi + fi +} + +_arguments \ + {-h,--help}'[Prints a short help text and exits.]' \ + '--version[Prints a short version string and exits.]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-legend[Do not show the headers and footers]' \ + '--system[Connect to system manager]' \ + '--user[Connect to user service manager]' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \ + '--address=[Connect to the bus specified by address]:address' \ + '--show-machine[Show machine ID column in list]' \ + '--unique[Only show unique names]' \ + '--acquired[Only show acquired names]' \ + '--activatable[Only show activatable names]' \ + '--match=[Only show matching messages]:match' \ + '--list[Do not show tree, but simple object path list]' \ + '--quiet[Do not show method call reply]'\ + '--verbose[Show result values in long format]' \ + '--expect-reply=[Expect a method call reply]:boolean:(1 0)' \ + '--auto-start=[Auto-start destination service]:boolean:(1 0)' \ + '--allow-interactive-authorization=[Allow interactive authorization for operation]:boolean:(1 0)' \ + '--timeout=[Maximum time to wait for method call completion]:timeout (seconds)' \ + '--augment-creds=[Extend credential data with data read from /proc/$PID]:boolean:(1 0)' \ + '*::busctl command:_busctl_command' diff --git a/src/busctl/busctl.xml b/src/busctl/busctl.xml new file mode 100644 index 0000000000..052a33097f --- /dev/null +++ b/src/busctl/busctl.xml @@ -0,0 +1,482 @@ + + + + + + + + + busctl + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + busctl + 1 + + + + busctl + Introspect the bus + + + + + busctl + OPTIONS + COMMAND + NAME + + + + + Description + + busctl may be used to + introspect and monitor the D-Bus bus. + + + + Options + + The following options are understood: + + + + + + Connect to the bus specified by + ADDRESS instead of using suitable + defaults for either the system or user bus (see + and + options). + + + + + + When showing the list of peers, show a + column containing the names of containers they belong to. + See + systemd-machined.service8. + + + + + + + When showing the list of peers, show only + "unique" names (of the form + :number.number). + + + + + + + The opposite of — + only "well-known" names will be shown. + + + + + + When showing the list of peers, show only + peers which have actually not been activated yet, but may be + started automatically if accessed. + + + + + + + When showing messages being exchanged, show only the + subset matching MATCH. + See + sd_bus_add_match3. + + + + + + + + When used with the capture command, + specifies the maximum bus message size to capture + ("snaplen"). Defaults to 4096 bytes. + + + + + + + + When used with the tree command, shows a + flat list of object paths instead of a tree. + + + + + + + + When used with the call command, + suppresses display of the response message payload. Note that even + if this option is specified, errors returned will still be + printed and the tool will indicate success or failure with + the process exit code. + + + + + + + + When used with the call or + get-property command, shows output in a + more verbose format. + + + + + BOOL + + + When used with the call command, + specifies whether busctl shall wait for + completion of the method call, output the returned method + response data, and return success or failure via the process + exit code. If this is set to no, the + method call will be issued but no response is expected, the + tool terminates immediately, and thus no response can be + shown, and no success or failure is returned via the exit + code. To only suppress output of the reply message payload, + use above. Defaults to + yes. + + + + + BOOL + + + When used with the call command, specifies + whether the method call should implicitly activate the + called service, should it not be running yet but is + configured to be auto-started. Defaults to + yes. + + + + + BOOL + + + When used with the call command, + specifies whether the services may enforce interactive + authorization while executing the operation, if the security + policy is configured for this. Defaults to + yes. + + + + + SECS + + + When used with the call command, + specifies the maximum time to wait for method call + completion. If no time unit is specified, assumes + seconds. The usual other units are understood, too (ms, us, + s, min, h, d, w, month, y). Note that this timeout does not + apply if is used, as the + tool does not wait for any reply message then. When not + specified or when set to 0, the default of + 25s is assumed. + + + + + BOOL + + + Controls whether credential data reported by + list or status shall + be augmented with data from + /proc. When this is turned on, the data + shown is possibly inconsistent, as the data read from + /proc might be more recent than the rest of + the credential information. Defaults to yes. + + + + + + + + + + + + + + + + + Commands + + The following commands are understood: + + + + list + + Show all peers on the bus, by their service + names. By default, shows both unique and well-known names, but + this may be changed with the and + switches. This is the default + operation if no command is specified. + + + + status SERVICE + + Show process information and credentials of a + bus service (if one is specified by its unique or well-known + name), a process (if one is specified by its numeric PID), or + the owner of the bus (if no parameter is + specified). + + + + monitor SERVICE + + Dump messages being exchanged. If + SERVICE is specified, show messages + to or from this peer, identified by its well-known or unique + name. Otherwise, show all messages on the bus. Use Ctrl-C to + terminate the dump. + + + + capture SERVICE + + Similar to monitor but + writes the output in pcap format (for details, see the Libpcap + File Format description. Make sure to redirect the + output to STDOUT to a file. Tools like + wireshark1 + may be used to dissect and view the generated + files. + + + + tree SERVICE + + Shows an object tree of one or more + services. If SERVICE is specified, + show object tree of the specified services only. Otherwise, + show all object trees of all services on the bus that acquired + at least one well-known name. + + + + introspect SERVICE OBJECT INTERFACE + + Show interfaces, methods, properties and + signals of the specified object (identified by its path) on + the specified service. If the interface argument is passed, the + output is limited to members of the specified + interface. + + + + call SERVICE OBJECT INTERFACE METHOD SIGNATURE ARGUMENT + + Invoke a method and show the response. Takes a + service name, object path, interface name and method name. If + parameters shall be passed to the method call, a signature + string is required, followed by the arguments, individually + formatted as strings. For details on the formatting used, see + below. To suppress output of the returned data, use the + option. + + + + get-property SERVICE OBJECT INTERFACE PROPERTY + + Retrieve the current value of one or more + object properties. Takes a service name, object path, + interface name and property name. Multiple properties may be + specified at once, in which case their values will be shown one + after the other, separated by newlines. The output is, by + default, in terse format. Use for a + more elaborate output format. + + + + set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT + + Set the current value of an object + property. Takes a service name, object path, interface name, + property name, property signature, followed by a list of + parameters formatted as strings. + + + + help + + Show command syntax help. + + + + + + Parameter Formatting + + The call and + set-property commands take a signature string + followed by a list of parameters formatted as string (for details + on D-Bus signature strings, see the Type + system chapter of the D-Bus specification). For simple + types, each parameter following the signature should simply be the + parameter's value formatted as string. Positive boolean values may + be formatted as true, yes, + on, or 1; negative boolean + values may be specified as false, + no, off, or + 0. For arrays, a numeric argument for the + number of entries followed by the entries shall be specified. For + variants, the signature of the contents shall be specified, + followed by the contents. For dictionaries and structs, the + contents of them shall be directly specified. + + For example, + s jawoll is the formatting + of a single string jawoll. + + + as 3 hello world foobar + is the formatting of a string array with three entries, + hello, world and + foobar. + + + a{sv} 3 One s Eins Two u 2 Yes b true + is the formatting of a dictionary + array that maps strings to variants, consisting of three + entries. The string One is assigned the + string Eins. The string + Two is assigned the 32-bit unsigned + integer 2. The string Yes is assigned a + positive boolean. + + Note that the call, + get-property, introspect + commands will also generate output in this format for the returned + data. Since this format is sometimes too terse to be easily + understood, the call and + get-property commands may generate a more + verbose, multi-line output when passed the + option. + + + + Examples + + + Write and Read a Property + + The following two commands first write a property and then + read it back. The property is found on the + /org/freedesktop/systemd1 object of the + org.freedesktop.systemd1 service. The name of + the property is LogLevel on the + org.freedesktop.systemd1.Manager + interface. The property contains a single string: + + # busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel s debug +# busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel +s "debug" + + + + + Terse and Verbose Output + + The following two commands read a property that contains + an array of strings, and first show it in terse format, followed + by verbose format: + + $ busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Environment +as 2 "LANG=en_US.UTF-8" "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" +$ busctl get-property --verbose org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Environment +ARRAY "s" { + STRING "LANG=en_US.UTF-8"; + STRING "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"; +}; + + + + Invoking a Method + + The following command invokes the + StartUnit method on the + org.freedesktop.systemd1.Manager + interface of the + /org/freedesktop/systemd1 object + of the org.freedesktop.systemd1 + service, and passes it two strings + cups.service and + replace. As a result of the method + call, a single object path parameter is received and + shown: + + # busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager StartUnit ss "cups.service" "replace" +o "/org/freedesktop/systemd1/job/42684" + + + + + See Also + + + dbus-daemon1, + D-Bus, + sd-bus3, + systemd1, + machinectl1, + wireshark1 + + + diff --git a/src/cgls/Makefile b/src/cgls/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/cgls/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c deleted file mode 100644 index dcb5912b83..0000000000 --- a/src/cgls/cgls.c +++ /dev/null @@ -1,288 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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 "output-mode.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; -static bool arg_all = false; -static int arg_full = -1; -static char* arg_machine = NULL; - -static void help(void) { - printf("%s [OPTIONS...] [CGROUP...]\n\n" - "Recursively show control group contents.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " -a --all Show all groups, including empty\n" - " -l --full Do not ellipsize output\n" - " -k Include kernel threads in output\n" - " -M --machine= Show container\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_NO_PAGER = 0x100, - ARG_VERSION, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "all", no_argument, NULL, 'a' }, - { "full", no_argument, NULL, 'l' }, - { "machine", required_argument, NULL, 'M' }, - {} - }; - - int c; - - assert(argc >= 1); - assert(argv); - - while ((c = getopt_long(argc, argv, "hkalM:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case 'a': - arg_all = true; - break; - - case 'l': - arg_full = true; - break; - - case 'k': - arg_kernel_threads = true; - break; - - case 'M': - arg_machine = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int get_cgroup_root(char **ret) { - _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 == -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; - } - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); - if (r < 0) - return log_error_errno(r, "Failed to load machine data: %m"); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - 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"); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - ret); - if (r < 0) - return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); - - return 0; -} - -static void show_cg_info(const char *controller, const char *path) { - - if (cg_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) - printf("Controller %s; ", controller); - - printf("Control group %s:\n", isempty(path) ? "/" : path); - fflush(stdout); -} - -int main(int argc, char *argv[]) { - int r, output_flags; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (!arg_no_pager) { - r = pager_open(arg_no_pager, false); - if (r > 0 && arg_full < 0) - arg_full = true; - } - - output_flags = - arg_all * OUTPUT_SHOW_ALL | - (arg_full > 0) * OUTPUT_FULL_WIDTH | - arg_kernel_threads * OUTPUT_KERNEL_THREADS; - - if (optind < argc) { - _cleanup_free_ char *root = NULL; - int i; - - r = get_cgroup_root(&root); - if (r < 0) - goto finish; - - for (i = optind; i < argc; i++) { - int q; - - if (path_startswith(argv[i], "/sys/fs/cgroup")) { - - printf("Directory %s:\n", argv[i]); - fflush(stdout); - - q = show_cgroup_by_path(argv[i], NULL, 0, output_flags); - } else { - _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; - const char *controller, *path; - - r = cg_split_spec(argv[i], &c, &p); - if (r < 0) { - log_error_errno(r, "Failed to split argument %s: %m", argv[i]); - goto finish; - } - - controller = c ?: SYSTEMD_CGROUP_CONTROLLER; - if (p) { - j = strjoin(root, "/", p, NULL); - if (!j) { - r = log_oom(); - goto finish; - } - - path_kill_slashes(j); - path = j; - } else - path = root; - - show_cg_info(controller, path); - - q = show_cgroup(controller, path, NULL, 0, output_flags); - } - - if (q < 0) - r = q; - } - - } else { - bool done = false; - - if (!arg_machine) { - _cleanup_free_ char *cwd = NULL; - - cwd = get_current_dir_name(); - if (!cwd) { - r = log_error_errno(errno, "Cannot determine current working directory: %m"); - goto finish; - } - - if (path_startswith(cwd, "/sys/fs/cgroup")) { - printf("Working directory %s:\n", cwd); - fflush(stdout); - - r = show_cgroup_by_path(cwd, NULL, 0, output_flags); - done = true; - } - } - - if (!done) { - _cleanup_free_ char *root = NULL; - - r = get_cgroup_root(&root); - if (r < 0) - goto finish; - - show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); - - printf("-.slice\n"); - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); - } - } - - if (r < 0) - log_error_errno(r, "Failed to list cgroup tree: %m"); - -finish: - pager_close(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/cgroups-agent/Makefile b/src/cgroups-agent/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/cgroups-agent/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c deleted file mode 100644 index d7c722ac3d..0000000000 --- a/src/cgroups-agent/cgroups-agent.c +++ /dev/null @@ -1,67 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fd-util.h" -#include "log.h" -#include "socket-util.h" - -int main(int argc, char *argv[]) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/cgroups-agent", - }; - - _cleanup_close_ int fd = -1; - ssize_t n; - size_t l; - - if (argc != 2) { - log_error("Incorrect number of arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) { - log_debug_errno(errno, "Failed to allocate socket: %m"); - return EXIT_FAILURE; - } - - l = strlen(argv[1]); - - n = sendto(fd, argv[1], l, 0, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (n < 0) { - log_debug_errno(errno, "Failed to send cgroups agent message: %m"); - return EXIT_FAILURE; - } - - if ((size_t) n != l) { - log_debug("Datagram size mismatch"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/cgtop/Makefile b/src/cgtop/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/cgtop/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c deleted file mode 100644 index c67b328b38..0000000000 --- a/src/cgtop/cgtop.c +++ /dev/null @@ -1,1133 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.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; - - bool n_tasks_valid:1; - bool cpu_valid:1; - bool memory_valid:1; - bool io_valid:1; - - uint64_t n_tasks; - - unsigned cpu_iteration; - nsec_t cpu_usage; - nsec_t cpu_timestamp; - double cpu_fraction; - - uint64_t memory; - - unsigned io_iteration; - uint64_t io_input, io_output; - nsec_t io_timestamp; - uint64_t io_input_bps, io_output_bps; -} Group; - -static unsigned arg_depth = 3; -static unsigned arg_iterations = (unsigned) -1; -static bool arg_batch = false; -static bool arg_raw = false; -static usec_t arg_delay = 1*USEC_PER_SEC; -static char* arg_machine = NULL; -static char* arg_root = NULL; -static bool arg_recursive = true; - -static enum { - COUNT_PIDS, - COUNT_USERSPACE_PROCESSES, - COUNT_ALL_PROCESSES, -} arg_count = COUNT_PIDS; - -static enum { - ORDER_PATH, - ORDER_TASKS, - ORDER_CPU, - ORDER_MEMORY, - ORDER_IO, -} arg_order = ORDER_CPU; - -static enum { - CPU_PERCENT, - CPU_TIME, -} arg_cpu_type = CPU_PERCENT; - -static void group_free(Group *g) { - assert(g); - - free(g->path); - free(g); -} - -static void group_hashmap_clear(Hashmap *h) { - Group *g; - - while ((g = hashmap_steal_first(h))) - group_free(g); -} - -static void group_hashmap_free(Hashmap *h) { - group_hashmap_clear(h); - hashmap_free(h); -} - -static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) { - if (!is_valid) - return "-"; - if (arg_raw) { - snprintf(buf, l, "%jd", t); - return buf; - } - return format_bytes(buf, l, t); -} - -static int process( - const char *controller, - const char *path, - Hashmap *a, - Hashmap *b, - unsigned iteration, - Group **ret) { - - Group *g; - int r; - - assert(controller); - assert(path); - assert(a); - - g = hashmap_get(a, path); - if (!g) { - g = hashmap_get(b, path); - if (!g) { - g = new0(Group, 1); - if (!g) - return -ENOMEM; - - g->path = strdup(path); - if (!g->path) { - group_free(g); - return -ENOMEM; - } - - r = hashmap_put(a, g->path, g); - if (r < 0) { - group_free(g); - return r; - } - } else { - r = hashmap_move_one(a, b, path); - if (r < 0) - return r; - - g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; - } - } - - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid; - - r = cg_enumerate_processes(controller, path, &f); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - g->n_tasks = 0; - while (cg_read_pid(f, &pid) > 0) { - - if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) - continue; - - g->n_tasks++; - } - - if (g->n_tasks > 0) - g->n_tasks_valid = true; - - } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { - _cleanup_free_ char *p = NULL, *v = NULL; - - r = cg_get_path(controller, path, "pids.current", &p); - if (r < 0) - return r; - - r = read_one_line_file(p, &v); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - r = safe_atou64(v, &g->n_tasks); - if (r < 0) - return r; - - if (g->n_tasks > 0) - g->n_tasks_valid = true; - - } else if (streq(controller, "cpuacct") && cg_unified() <= 0) { - _cleanup_free_ char *p = NULL, *v = NULL; - uint64_t new_usage; - nsec_t timestamp; - - r = cg_get_path(controller, path, "cpuacct.usage", &p); - if (r < 0) - return r; - - r = read_one_line_file(p, &v); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - r = safe_atou64(v, &new_usage); - if (r < 0) - return r; - - timestamp = now_nsec(CLOCK_MONOTONIC); - - if (g->cpu_iteration == iteration - 1 && - (nsec_t) new_usage > g->cpu_usage) { - - nsec_t x, y; - - x = timestamp - g->cpu_timestamp; - if (x < 1) - x = 1; - - y = (nsec_t) new_usage - g->cpu_usage; - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } - - g->cpu_usage = (nsec_t) new_usage; - g->cpu_timestamp = timestamp; - g->cpu_iteration = iteration; - - } else if (streq(controller, "memory")) { - _cleanup_free_ char *p = NULL, *v = NULL; - - if (cg_unified() <= 0) - r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); - else - r = cg_get_path(controller, path, "memory.current", &p); - if (r < 0) - return r; - - r = read_one_line_file(p, &v); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - r = safe_atou64(v, &g->memory); - if (r < 0) - return r; - - if (g->memory > 0) - g->memory_valid = true; - - } else if ((streq(controller, "io") && cg_unified() > 0) || - (streq(controller, "blkio") && cg_unified() <= 0)) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - bool unified = cg_unified() > 0; - uint64_t wr = 0, rd = 0; - nsec_t timestamp; - - r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); - if (r < 0) - return r; - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - return -errno; - } - - for (;;) { - char line[LINE_MAX], *l; - uint64_t k, *q; - - if (!fgets(line, sizeof(line), f)) - break; - - /* Trim and skip the device */ - l = strstrip(line); - l += strcspn(l, WHITESPACE); - l += strspn(l, WHITESPACE); - - if (unified) { - while (!isempty(l)) { - if (sscanf(l, "rbytes=%" SCNu64, &k)) - rd += k; - else if (sscanf(l, "wbytes=%" SCNu64, &k)) - wr += k; - - l += strcspn(l, WHITESPACE); - l += strspn(l, WHITESPACE); - } - } else { - if (first_word(l, "Read")) { - l += 4; - q = &rd; - } else if (first_word(l, "Write")) { - l += 5; - q = ≀ - } else - continue; - - l += strspn(l, WHITESPACE); - r = safe_atou64(l, &k); - if (r < 0) - continue; - - *q += k; - } - } - - timestamp = now_nsec(CLOCK_MONOTONIC); - - if (g->io_iteration == iteration - 1) { - uint64_t x, yr, yw; - - x = (uint64_t) (timestamp - g->io_timestamp); - if (x < 1) - x = 1; - - if (rd > g->io_input) - yr = rd - g->io_input; - else - yr = 0; - - if (wr > g->io_output) - yw = wr - g->io_output; - else - yw = 0; - - if (yr > 0 || yw > 0) { - g->io_input_bps = (yr * 1000000000ULL) / x; - g->io_output_bps = (yw * 1000000000ULL) / x; - g->io_valid = true; - } - } - - g->io_input = rd; - g->io_output = wr; - g->io_timestamp = timestamp; - g->io_iteration = iteration; - } - - if (ret) - *ret = g; - - return 0; -} - -static int refresh_one( - const char *controller, - const char *path, - Hashmap *a, - Hashmap *b, - unsigned iteration, - unsigned depth, - Group **ret) { - - _cleanup_closedir_ DIR *d = NULL; - Group *ours = NULL; - int r; - - assert(controller); - assert(path); - assert(a); - - if (depth > arg_depth) - return 0; - - r = process(controller, path, a, b, iteration, &ours); - if (r < 0) - return r; - - r = cg_enumerate_subgroups(controller, path, &d); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *fn = NULL, *p = NULL; - Group *child = NULL; - - r = cg_read_subgroup(d, &fn); - if (r < 0) - return r; - if (r == 0) - break; - - p = strjoin(path, "/", fn, NULL); - if (!p) - return -ENOMEM; - - path_kill_slashes(p); - - r = refresh_one(controller, p, a, b, iteration, depth + 1, &child); - if (r < 0) - return r; - - if (arg_recursive && - IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) && - child && - child->n_tasks_valid && - streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - - /* Recursively sum up processes */ - - if (ours->n_tasks_valid) - ours->n_tasks += child->n_tasks; - else { - ours->n_tasks = child->n_tasks; - ours->n_tasks_valid = true; - } - } - } - - if (ret) - *ret = ours; - - return 1; -} - -static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) { - int r; - - assert(a); - - r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - r = refresh_one("memory", root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - r = refresh_one("io", root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - r = refresh_one("blkio", root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - r = refresh_one("pids", root, a, b, iteration, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -static int group_compare(const void*a, const void *b) { - const Group *x = *(Group**)a, *y = *(Group**)b; - - if (arg_order != ORDER_TASKS || arg_recursive) { - /* Let's make sure that the parent is always before - * the child. Except when ordering by tasks and - * recursive summing is off, since that is actually - * not accumulative for all children. */ - - if (path_startswith(y->path, x->path)) - return -1; - if (path_startswith(x->path, y->path)) - return 1; - } - - switch (arg_order) { - - case ORDER_PATH: - break; - - case ORDER_CPU: - if (arg_cpu_type == CPU_PERCENT) { - if (x->cpu_valid && y->cpu_valid) { - if (x->cpu_fraction > y->cpu_fraction) - return -1; - else if (x->cpu_fraction < y->cpu_fraction) - return 1; - } else if (x->cpu_valid) - return -1; - else if (y->cpu_valid) - return 1; - } else { - if (x->cpu_usage > y->cpu_usage) - return -1; - else if (x->cpu_usage < y->cpu_usage) - return 1; - } - - break; - - case ORDER_TASKS: - if (x->n_tasks_valid && y->n_tasks_valid) { - if (x->n_tasks > y->n_tasks) - return -1; - else if (x->n_tasks < y->n_tasks) - return 1; - } else if (x->n_tasks_valid) - return -1; - else if (y->n_tasks_valid) - return 1; - - break; - - case ORDER_MEMORY: - if (x->memory_valid && y->memory_valid) { - if (x->memory > y->memory) - return -1; - else if (x->memory < y->memory) - return 1; - } else if (x->memory_valid) - return -1; - else if (y->memory_valid) - return 1; - - break; - - case ORDER_IO: - if (x->io_valid && y->io_valid) { - if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) - return -1; - else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps) - return 1; - } else if (x->io_valid) - return -1; - else if (y->io_valid) - return 1; - } - - return path_compare(x->path, y->path); -} - -static void display(Hashmap *a) { - Iterator i; - Group *g; - Group **array; - signed path_columns; - unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */ - char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)]; - - assert(a); - - if (!terminal_is_dumb()) - fputs(ANSI_HOME_CLEAR, stdout); - - array = alloca(sizeof(Group*) * hashmap_size(a)); - - HASHMAP_FOREACH(g, a, i) - if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid) - array[n++] = g; - - qsort_safe(array, n, sizeof(Group*), group_compare); - - /* Find the longest names in one run */ - for (j = 0; j < n; j++) { - unsigned cputlen, pathtlen; - - format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); - cputlen = strlen(buffer); - maxtcpu = MAX(maxtcpu, cputlen); - - pathtlen = strlen(array[j]->path); - maxtpath = MAX(maxtpath, pathtlen); - } - - if (arg_cpu_type == CPU_PERCENT) - xsprintf(buffer, "%6s", "%CPU"); - else - xsprintf(buffer, "%*s", maxtcpu, "CPU Time"); - - rows = lines(); - if (rows <= 10) - rows = 10; - - if (on_tty()) { - const char *on, *off; - - path_columns = columns() - 36 - strlen(buffer); - if (path_columns < 10) - path_columns = 10; - - on = ansi_highlight_underline(); - off = ansi_underline(); - - printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n", - ansi_underline(), - arg_order == ORDER_PATH ? on : "", path_columns, "Control Group", - arg_order == ORDER_PATH ? off : "", - arg_order == ORDER_TASKS ? on : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+", - arg_order == ORDER_TASKS ? off : "", - arg_order == ORDER_CPU ? on : "", buffer, - arg_order == ORDER_CPU ? off : "", - arg_order == ORDER_MEMORY ? on : "", "Memory", - arg_order == ORDER_MEMORY ? off : "", - arg_order == ORDER_IO ? on : "", "Input/s", - arg_order == ORDER_IO ? off : "", - arg_order == ORDER_IO ? on : "", "Output/s", - arg_order == ORDER_IO ? off : "", - ansi_normal()); - } else - path_columns = maxtpath; - - for (j = 0; j < n; j++) { - _cleanup_free_ char *ellipsized = NULL; - const char *path; - - if (on_tty() && j + 6 > rows) - break; - - g = array[j]; - - path = isempty(g->path) ? "/" : g->path; - ellipsized = ellipsize(path, path_columns, 33); - printf("%-*s", path_columns, ellipsized ?: path); - - if (g->n_tasks_valid) - printf(" %7" PRIu64, g->n_tasks); - else - fputs(" -", stdout); - - if (arg_cpu_type == CPU_PERCENT) { - if (g->cpu_valid) - printf(" %6.1f", g->cpu_fraction*100); - else - fputs(" -", stdout); - } else - printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); - - printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); - printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); - printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps)); - - putchar('\n'); - } -} - -static void help(void) { - printf("%s [OPTIONS...] [CGROUP]\n\n" - "Show top control groups by their resource usage.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -p --order=path Order by path\n" - " -t --order=tasks Order by number of tasks/processes\n" - " -c --order=cpu Order by CPU load (default)\n" - " -m --order=memory Order by memory load\n" - " -i --order=io Order by IO load\n" - " -r --raw Provide raw (not human-readable) numbers\n" - " --cpu=percentage Show CPU usage as percentage (default)\n" - " --cpu=time Show CPU usage as time\n" - " -P Count userspace processes instead of tasks (excl. kernel)\n" - " -k Count all processes instead of tasks (incl. kernel)\n" - " --recursive=BOOL Sum up process count recursively\n" - " -d --delay=DELAY Delay between updates\n" - " -n --iterations=N Run for N iterations before exiting\n" - " -b --batch Run in batch mode, accepting no input\n" - " --depth=DEPTH Maximum traversal depth (default: %u)\n" - " -M --machine= Show container\n" - , program_invocation_short_name, arg_depth); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_DEPTH, - ARG_CPU_TYPE, - ARG_ORDER, - ARG_RECURSIVE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "delay", required_argument, NULL, 'd' }, - { "iterations", required_argument, NULL, 'n' }, - { "batch", no_argument, NULL, 'b' }, - { "raw", no_argument, NULL, 'r' }, - { "depth", required_argument, NULL, ARG_DEPTH }, - { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, - { "order", required_argument, NULL, ARG_ORDER }, - { "recursive", required_argument, NULL, ARG_RECURSIVE }, - { "machine", required_argument, NULL, 'M' }, - {} - }; - - bool recursive_unset = false; - int c, r; - - assert(argc >= 1); - assert(argv); - - while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_CPU_TYPE: - if (optarg) { - if (streq(optarg, "time")) - arg_cpu_type = CPU_TIME; - else if (streq(optarg, "percentage")) - arg_cpu_type = CPU_PERCENT; - else { - log_error("Unknown argument to --cpu=: %s", optarg); - return -EINVAL; - } - } else - arg_cpu_type = CPU_TIME; - - break; - - case ARG_DEPTH: - r = safe_atou(optarg, &arg_depth); - if (r < 0) { - log_error("Failed to parse depth parameter."); - return -EINVAL; - } - - break; - - case 'd': - r = parse_sec(optarg, &arg_delay); - if (r < 0 || arg_delay <= 0) { - log_error("Failed to parse delay parameter."); - return -EINVAL; - } - - break; - - case 'n': - r = safe_atou(optarg, &arg_iterations); - if (r < 0) { - log_error("Failed to parse iterations parameter."); - return -EINVAL; - } - - break; - - case 'b': - arg_batch = true; - break; - - case 'r': - arg_raw = true; - break; - - case 'p': - arg_order = ORDER_PATH; - break; - - case 't': - arg_order = ORDER_TASKS; - break; - - case 'c': - arg_order = ORDER_CPU; - break; - - case 'm': - arg_order = ORDER_MEMORY; - break; - - case 'i': - arg_order = ORDER_IO; - break; - - case ARG_ORDER: - if (streq(optarg, "path")) - arg_order = ORDER_PATH; - else if (streq(optarg, "tasks")) - arg_order = ORDER_TASKS; - else if (streq(optarg, "cpu")) - arg_order = ORDER_CPU; - else if (streq(optarg, "memory")) - arg_order = ORDER_MEMORY; - else if (streq(optarg, "io")) - arg_order = ORDER_IO; - else { - log_error("Invalid argument to --order=: %s", optarg); - return -EINVAL; - } - break; - - case 'k': - arg_count = COUNT_ALL_PROCESSES; - break; - - case 'P': - arg_count = COUNT_USERSPACE_PROCESSES; - break; - - case ARG_RECURSIVE: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --recursive= argument: %s", optarg); - return r; - } - - arg_recursive = r; - recursive_unset = r == 0; - break; - - case 'M': - arg_machine = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind == argc-1) { - if (arg_machine) { - log_error("Specifying a control group path together with the -M option is not allowed"); - return -EINVAL; - } - arg_root = argv[optind]; - } else if (optind < argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - if (recursive_unset && arg_count == COUNT_PIDS) { - log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); - return -EINVAL; - } - - return 1; -} - -static const char* counting_what(void) { - if (arg_count == COUNT_PIDS) - return "tasks"; - else if (arg_count == COUNT_ALL_PROCESSES) - return "all processes (incl. kernel)"; - else - return "userspace processes (excl. kernel)"; -} - -static int get_cgroup_root(char **ret) { - _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_root) { - char *aux; - - aux = strdup(arg_root); - if (!aux) - return log_oom(); - - *ret = aux; - return 0; - } - - if (!arg_machine) { - r = cg_get_root_path(ret); - if (r < 0) - return log_error_errno(r, "Failed to get root control group path: %m"); - - return 0; - } - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); - if (r < 0) - return log_error_errno(r, "Failed to load machine data: %m"); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - 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"); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - ret); - if (r < 0) - return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); - - return 0; -} - -int main(int argc, char *argv[]) { - int r; - Hashmap *a = NULL, *b = NULL; - unsigned iteration = 0; - usec_t last_refresh = 0; - bool quit = false, immediate_refresh = false; - _cleanup_free_ char *root = NULL; - CGroupMask mask; - - log_parse_environment(); - log_open(); - - r = cg_mask_supported(&mask); - if (r < 0) { - log_error_errno(r, "Failed to determine supported controllers: %m"); - goto finish; - } - - arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = get_cgroup_root(&root); - if (r < 0) { - log_error_errno(r, "Failed to get root control group path: %m"); - goto finish; - } - - a = hashmap_new(&string_hash_ops); - b = hashmap_new(&string_hash_ops); - if (!a || !b) { - r = log_oom(); - goto finish; - } - - signal(SIGWINCH, columns_lines_cache_reset); - - if (arg_iterations == (unsigned) -1) - arg_iterations = on_tty() ? 0 : 1; - - while (!quit) { - Hashmap *c; - usec_t t; - char key; - char h[FORMAT_TIMESPAN_MAX]; - - t = now(CLOCK_MONOTONIC); - - if (t >= last_refresh + arg_delay || immediate_refresh) { - - r = refresh(root, a, b, iteration++); - if (r < 0) { - log_error_errno(r, "Failed to refresh: %m"); - goto finish; - } - - group_hashmap_clear(b); - - c = a; - a = b; - b = c; - - last_refresh = t; - immediate_refresh = false; - } - - display(b); - - if (arg_iterations && iteration >= arg_iterations) - break; - - if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */ - fputs("\n", stdout); - fflush(stdout); - - if (arg_batch) - (void) usleep(last_refresh + arg_delay - t); - else { - r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); - if (r == -ETIMEDOUT) - continue; - if (r < 0) { - log_error_errno(r, "Couldn't read key: %m"); - goto finish; - } - } - - if (on_tty()) { /* TTY: Clear any user keystroke */ - fputs("\r \r", stdout); - fflush(stdout); - } - - if (arg_batch) - continue; - - switch (key) { - - case ' ': - immediate_refresh = true; - break; - - case 'q': - quit = true; - break; - - case 'p': - arg_order = ORDER_PATH; - break; - - case 't': - arg_order = ORDER_TASKS; - break; - - case 'c': - arg_order = ORDER_CPU; - break; - - case 'm': - arg_order = ORDER_MEMORY; - break; - - case 'i': - arg_order = ORDER_IO; - break; - - case '%': - arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; - break; - - case 'k': - arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS; - fprintf(stdout, "\nCounting: %s.", counting_what()); - fflush(stdout); - sleep(1); - break; - - case 'P': - arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS; - fprintf(stdout, "\nCounting: %s.", counting_what()); - fflush(stdout); - sleep(1); - break; - - case 'r': - if (arg_count == COUNT_PIDS) - fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode."); - else { - arg_recursive = !arg_recursive; - fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive)); - } - fflush(stdout); - sleep(1); - break; - - case '+': - if (arg_delay < USEC_PER_SEC) - arg_delay += USEC_PER_MSEC*250; - else - arg_delay += USEC_PER_SEC; - - fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); - fflush(stdout); - sleep(1); - break; - - case '-': - if (arg_delay <= USEC_PER_MSEC*500) - arg_delay = USEC_PER_MSEC*250; - else if (arg_delay < USEC_PER_MSEC*1250) - arg_delay -= USEC_PER_MSEC*250; - else - arg_delay -= USEC_PER_SEC; - - fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); - fflush(stdout); - sleep(1); - break; - - case '?': - case 'h': - -#define ON ANSI_HIGHLIGHT -#define OFF ANSI_NORMAL - - fprintf(stdout, - "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" - "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" - "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n" - "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit"); - fflush(stdout); - sleep(3); - break; - - default: - if (key < ' ') - fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); - else - fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); - fflush(stdout); - sleep(1); - break; - } - } - - r = 0; - -finish: - group_hashmap_free(a); - group_hashmap_free(b); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/core/.gitignore b/src/core/.gitignore deleted file mode 100644 index 465b4fcc20..0000000000 --- a/src/core/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/macros.systemd -/triggers.systemd -/systemd.pc diff --git a/src/core/Makefile b/src/core/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/core/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c deleted file mode 100644 index 76afe3fe15..0000000000 --- a/src/core/audit-fd.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include - -#include "audit-fd.h" - -#ifdef HAVE_AUDIT - -#include -#include - -#include "fd-util.h" -#include "log.h" -#include "util.h" - -static bool initialized = false; -static int audit_fd; - -int get_audit_fd(void) { - - if (!initialized) { - audit_fd = audit_open(); - - if (audit_fd < 0) { - if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error_errno(errno, "Failed to connect to audit log: %m"); - - audit_fd = errno ? -errno : -EINVAL; - } - - initialized = true; - } - - return audit_fd; -} - -void close_audit_fd(void) { - - if (initialized && audit_fd >= 0) - safe_close(audit_fd); - - initialized = true; - audit_fd = -ECONNRESET; -} - -#else - -int get_audit_fd(void) { - return -EAFNOSUPPORT; -} - -void close_audit_fd(void) { -} - -#endif diff --git a/src/core/audit-fd.h b/src/core/audit-fd.h deleted file mode 100644 index 0eccb59210..0000000000 --- a/src/core/audit-fd.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int get_audit_fd(void); -void close_audit_fd(void); diff --git a/src/core/automount.c b/src/core/automount.c deleted file mode 100644 index 4e9891569c..0000000000 --- a/src/core/automount.c +++ /dev/null @@ -1,1122 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "async.h" -#include "automount.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 "process-util.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, - [AUTOMOUNT_WAITING] = UNIT_ACTIVE, - [AUTOMOUNT_RUNNING] = UNIT_ACTIVE, - [AUTOMOUNT_FAILED] = UNIT_FAILED -}; - -struct expire_data { - int dev_autofs_fd; - int ioctl_fd; -}; - -static inline void expire_data_free(struct expire_data *data) { - if (!data) - return; - - safe_close(data->dev_autofs_fd); - safe_close(data->ioctl_fd); - free(data); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free); - -static int open_dev_autofs(Manager *m); -static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata); -static int automount_start_expire(Automount *a); -static void automount_stop_expire(Automount *a); -static int automount_send_ready(Automount *a, Set *tokens, int status); - -static void automount_init(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - a->pipe_fd = -1; - a->directory_mode = 0755; - UNIT(a)->ignore_on_isolate = true; -} - -static void unmount_autofs(Automount *a) { - int r; - - assert(a); - - if (a->pipe_fd < 0) - return; - - a->pipe_event_source = sd_event_source_unref(a->pipe_event_source); - a->pipe_fd = safe_close(a->pipe_fd); - - /* If we reload/reexecute things we keep the mount point - * around */ - if (a->where && - (UNIT(a)->manager->exit_code != MANAGER_RELOAD && - UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) { - automount_send_ready(a, a->tokens, -EHOSTDOWN); - automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); - - r = repeat_unmount(a->where, MNT_DETACH); - if (r < 0) - log_error_errno(r, "Failed to unmount: %m"); - } -} - -static void automount_done(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - unmount_autofs(a); - - a->where = mfree(a->where); - - a->tokens = set_free(a->tokens); - a->expire_tokens = set_free(a->expire_tokens); - - a->expire_event_source = sd_event_source_unref(a->expire_event_source); -} - -static int automount_add_mount_links(Automount *a) { - _cleanup_free_ char *parent = NULL; - - assert(a); - - parent = dirname_malloc(a->where); - if (!parent) - return -ENOMEM; - - return unit_require_mounts_for(UNIT(a), parent); -} - -static int automount_add_default_dependencies(Automount *a) { - int r; - - assert(a); - - if (!UNIT(a)->default_dependencies) - return 0; - - if (!MANAGER_IS_SYSTEM(UNIT(a)->manager)) - return 0; - - r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static int automount_verify(Automount *a) { - _cleanup_free_ char *e = NULL; - int r; - - assert(a); - - if (UNIT(a)->load_state != UNIT_LOADED) - return 0; - - if (path_equal(a->where, "/")) { - log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing."); - return -EINVAL; - } - - r = unit_name_from_path(a->where, ".automount", &e); - if (r < 0) - return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m"); - - if (!unit_has_name(UNIT(a), e)) { - log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int automount_load(Unit *u) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - /* Load a .automount file */ - r = unit_load_fragment_and_dropin_optional(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - Unit *x; - - if (!a->where) { - r = unit_name_to_path(u->id, &a->where); - if (r < 0) - return r; - } - - path_kill_slashes(a->where); - - r = unit_load_related_unit(u, ".mount", &x); - if (r < 0) - return r; - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); - if (r < 0) - return r; - - r = automount_add_mount_links(a); - if (r < 0) - return r; - - r = automount_add_default_dependencies(a); - if (r < 0) - return r; - } - - return automount_verify(a); -} - -static void automount_set_state(Automount *a, AutomountState state) { - AutomountState old_state; - assert(a); - - old_state = a->state; - a->state = state; - - if (state != AUTOMOUNT_RUNNING) - automount_stop_expire(a); - - if (state != AUTOMOUNT_WAITING && - state != AUTOMOUNT_RUNNING) - unmount_autofs(a); - - if (state != old_state) - log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state)); - - unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true); -} - -static int automount_coldplug(Unit *u) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(a); - assert(a->state == AUTOMOUNT_DEAD); - - if (a->deserialized_state != a->state) { - - r = open_dev_autofs(u->manager); - if (r < 0) - return r; - - if (a->deserialized_state == AUTOMOUNT_WAITING || - a->deserialized_state == AUTOMOUNT_RUNNING) { - assert(a->pipe_fd >= 0); - - r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); - if (r < 0) - return r; - - (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); - } - - automount_set_state(a, a->deserialized_state); - } - - return 0; -} - -static void automount_dump(Unit *u, FILE *f, const char *prefix) { - char time_string[FORMAT_TIMESPAN_MAX]; - Automount *a = AUTOMOUNT(u); - - assert(a); - - fprintf(f, - "%sAutomount State: %s\n" - "%sResult: %s\n" - "%sWhere: %s\n" - "%sDirectoryMode: %04o\n" - "%sTimeoutIdleUSec: %s\n", - prefix, automount_state_to_string(a->state), - prefix, automount_result_to_string(a->result), - prefix, a->where, - prefix, a->directory_mode, - prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, a->timeout_idle_usec, USEC_PER_SEC)); -} - -static void automount_enter_dead(Automount *a, AutomountResult f) { - assert(a); - - if (f != AUTOMOUNT_SUCCESS) - a->result = f; - - automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); -} - -static int open_dev_autofs(Manager *m) { - struct autofs_dev_ioctl param; - - assert(m); - - if (m->dev_autofs_fd >= 0) - return m->dev_autofs_fd; - - label_fix("/dev/autofs", false, false); - - m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY); - if (m->dev_autofs_fd < 0) - return log_error_errno(errno, "Failed to open /dev/autofs: %m"); - - init_autofs_dev_ioctl(¶m); - if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) { - m->dev_autofs_fd = safe_close(m->dev_autofs_fd); - return -errno; - } - - log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor); - - return m->dev_autofs_fd; -} - -static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) { - struct autofs_dev_ioctl *param; - size_t l; - - assert(dev_autofs_fd >= 0); - assert(where); - - l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1; - param = alloca(l); - - init_autofs_dev_ioctl(param); - param->size = l; - param->ioctlfd = -1; - param->openmount.devid = devid; - strcpy(param->path, where); - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) - return -errno; - - if (param->ioctlfd < 0) - return -EIO; - - (void) fd_cloexec(param->ioctlfd, true); - return param->ioctlfd; -} - -static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) { - uint32_t major, minor; - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0) - return -errno; - - major = param.protover.version; - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0) - return -errno; - - minor = param.protosubver.sub_version; - - log_debug("Autofs protocol version %i.%i", major, minor); - return 0; -} - -static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) { - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - /* Convert to seconds, rounding up. */ - param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC; - - if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0) - return -errno; - - return 0; -} - -static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) { - struct autofs_dev_ioctl param; - - assert(dev_autofs_fd >= 0); - assert(ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = ioctl_fd; - - if (status != 0) { - param.fail.token = token; - param.fail.status = status; - } else - param.ready.token = token; - - if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0) - return -errno; - - return 0; -} - -static int automount_send_ready(Automount *a, Set *tokens, int status) { - _cleanup_close_ int ioctl_fd = -1; - unsigned token; - int r; - - assert(a); - assert(status <= 0); - - if (set_isempty(tokens)) - return 0; - - ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); - if (ioctl_fd < 0) - return ioctl_fd; - - if (status != 0) - log_unit_debug_errno(UNIT(a), status, "Sending failure: %m"); - else - log_unit_debug(UNIT(a), "Sending success."); - - r = 0; - - /* Autofs thankfully does not hand out 0 as a token */ - while ((token = PTR_TO_UINT(set_steal_first(tokens)))) { - int k; - - /* Autofs fun fact II: - * - * if you pass a positive status code here, the kernel will - * freeze! Yay! */ - - k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, - ioctl_fd, - token, - status); - if (k < 0) - r = k; - } - - return r; -} - -static void automount_trigger_notify(Unit *u, Unit *other) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(a); - assert(other); - - /* Filter out invocations with bogus state */ - if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) - return; - - /* Don't propagate state changes from the mount if we are already down */ - if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) - return; - - /* Propagate start limit hit state */ - if (other->start_limit_hit) { - automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); - return; - } - - /* Don't propagate anything if there's still a job queued */ - if (other->job) - return; - - /* The mount is successfully established */ - if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { - (void) automount_send_ready(a, a->tokens, 0); - - r = automount_start_expire(a); - if (r < 0) - log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - - automount_set_state(a, AUTOMOUNT_RUNNING); - } - - if (IN_SET(MOUNT(other)->state, - MOUNT_MOUNTING, MOUNT_MOUNTING_DONE, - MOUNT_MOUNTED, MOUNT_REMOUNTING, - MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, - MOUNT_FAILED)) { - - (void) automount_send_ready(a, a->expire_tokens, -ENODEV); - } - - if (MOUNT(other)->state == MOUNT_DEAD) - (void) automount_send_ready(a, a->expire_tokens, 0); - - /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ - if (IN_SET(MOUNT(other)->state, - MOUNT_DEAD, MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, - MOUNT_FAILED)) { - - (void) automount_send_ready(a, a->tokens, -ENODEV); - - automount_set_state(a, AUTOMOUNT_WAITING); - } -} - -static void automount_enter_waiting(Automount *a) { - _cleanup_close_ int ioctl_fd = -1; - int p[2] = { -1, -1 }; - char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1]; - char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1 - + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1]; - bool mounted = false; - int r, dev_autofs_fd; - struct stat st; - - assert(a); - assert(a->pipe_fd < 0); - assert(a->where); - - set_clear(a->tokens); - - r = unit_fail_if_symlink(UNIT(a), a->where); - if (r < 0) - goto fail; - - (void) mkdir_p_label(a->where, 0555); - - unit_warn_if_dir_nonempty(UNIT(a), a->where); - - dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); - if (dev_autofs_fd < 0) { - r = dev_autofs_fd; - goto fail; - } - - if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { - r = -errno; - goto fail; - } - - xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp()); - xsprintf(name, "systemd-"PID_FMT, getpid()); - if (mount(name, a->where, "autofs", 0, options) < 0) { - r = -errno; - goto fail; - } - - mounted = true; - - p[1] = safe_close(p[1]); - - if (stat(a->where, &st) < 0) { - r = -errno; - goto fail; - } - - ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); - if (ioctl_fd < 0) { - r = ioctl_fd; - goto fail; - } - - r = autofs_protocol(dev_autofs_fd, ioctl_fd); - if (r < 0) - goto fail; - - r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec); - if (r < 0) - goto fail; - - /* Autofs fun fact: - * - * Unless we close the ioctl fd here, for some weird reason - * the direct mount will not receive events from the - * kernel. */ - - r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); - - a->pipe_fd = p[0]; - a->dev_id = st.st_dev; - - automount_set_state(a, AUTOMOUNT_WAITING); - - return; - -fail: - log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); - - safe_close_pair(p); - - if (mounted) { - r = repeat_unmount(a->where, MNT_DETACH); - if (r < 0) - log_error_errno(r, "Failed to unmount, ignoring: %m"); - } - - automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); -} - -static void *expire_thread(void *p) { - struct autofs_dev_ioctl param; - _cleanup_(expire_data_freep) struct expire_data *data = (struct expire_data*)p; - int r; - - assert(data->dev_autofs_fd >= 0); - assert(data->ioctl_fd >= 0); - - init_autofs_dev_ioctl(¶m); - param.ioctlfd = data->ioctl_fd; - - do { - r = ioctl(data->dev_autofs_fd, AUTOFS_DEV_IOCTL_EXPIRE, ¶m); - } while (r >= 0); - - if (errno != EAGAIN) - log_warning_errno(errno, "Failed to expire automount, ignoring: %m"); - - return NULL; -} - -static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) { - Automount *a = AUTOMOUNT(userdata); - _cleanup_(expire_data_freep) struct expire_data *data = NULL; - int r; - - assert(a); - assert(source == a->expire_event_source); - - data = new0(struct expire_data, 1); - if (!data) - return log_oom(); - - data->ioctl_fd = -1; - - data->dev_autofs_fd = fcntl(UNIT(a)->manager->dev_autofs_fd, F_DUPFD_CLOEXEC, 3); - if (data->dev_autofs_fd < 0) - return log_unit_error_errno(UNIT(a), errno, "Failed to duplicate autofs fd: %m"); - - data->ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); - if (data->ioctl_fd < 0) - return log_unit_error_errno(UNIT(a), data->ioctl_fd, "Couldn't open autofs ioctl fd: %m"); - - r = asynchronous_job(expire_thread, data); - if (r < 0) - return log_unit_error_errno(UNIT(a), r, "Failed to start expire job: %m"); - - data = NULL; - - return automount_start_expire(a); -} - -static int automount_start_expire(Automount *a) { - int r; - usec_t timeout; - - assert(a); - - if (a->timeout_idle_usec == 0) - return 0; - - timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/3, USEC_PER_SEC); - - if (a->expire_event_source) { - r = sd_event_source_set_time(a->expire_event_source, timeout); - if (r < 0) - return r; - - return sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_ONESHOT); - } - - r = sd_event_add_time( - UNIT(a)->manager->event, - &a->expire_event_source, - CLOCK_MONOTONIC, timeout, 0, - automount_dispatch_expire, a); - if (r < 0) - return r; - - (void) sd_event_source_set_description(a->expire_event_source, "automount-expire"); - - return 0; -} - -static void automount_stop_expire(Automount *a) { - assert(a); - - if (!a->expire_event_source) - return; - - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); -} - -static void automount_enter_runnning(Automount *a) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - struct stat st; - int r; - - assert(a); - - /* We don't take mount requests anymore if we are supposed to - * shut down anyway */ - if (unit_stop_pending(UNIT(a))) { - log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); - automount_send_ready(a, a->tokens, -EHOSTDOWN); - automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); - return; - } - - mkdir_p_label(a->where, a->directory_mode); - - /* Before we do anything, let's see if somebody is playing games with us? */ - if (lstat(a->where, &st) < 0) { - log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); - goto fail; - } - - if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) - log_unit_info(UNIT(a), "Automount point already active?"); - else { - Unit *trigger; - - trigger = UNIT_TRIGGER(UNIT(a)); - if (!trigger) { - log_unit_error(UNIT(a), "Unit to trigger vanished."); - goto fail; - } - - r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, 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; - } - } - - automount_set_state(a, AUTOMOUNT_RUNNING); - return; - -fail: - automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); -} - -static int automount_start(Unit *u) { - Automount *a = AUTOMOUNT(u); - Unit *trigger; - int r; - - assert(a); - assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); - - if (path_is_mount_point(a->where, 0) > 0) { - log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where); - return -EEXIST; - } - - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } - - r = unit_start_limit_test(u); - if (r < 0) { - automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); - return r; - } - - a->result = AUTOMOUNT_SUCCESS; - automount_enter_waiting(a); - return 1; -} - -static int automount_stop(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING); - - automount_enter_dead(a, AUTOMOUNT_SUCCESS); - return 1; -} - -static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { - Automount *a = AUTOMOUNT(u); - Iterator i; - void *p; - int r; - - assert(a); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", automount_state_to_string(a->state)); - unit_serialize_item(u, f, "result", automount_result_to_string(a->result)); - unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id); - - SET_FOREACH(p, a->tokens, i) - unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p)); - SET_FOREACH(p, a->expire_tokens, i) - unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p)); - - r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd); - if (r < 0) - return r; - - return 0; -} - -static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Automount *a = AUTOMOUNT(u); - int r; - - assert(a); - assert(fds); - - if (streq(key, "state")) { - AutomountState state; - - state = automount_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - a->deserialized_state = state; - } else if (streq(key, "result")) { - AutomountResult f; - - f = automount_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != AUTOMOUNT_SUCCESS) - a->result = f; - - } else if (streq(key, "dev-id")) { - unsigned d; - - if (safe_atou(value, &d) < 0) - log_unit_debug(u, "Failed to parse dev-id value: %s", value); - else - a->dev_id = (unsigned) d; - } else if (streq(key, "token")) { - unsigned token; - - if (safe_atou(value, &token) < 0) - log_unit_debug(u, "Failed to parse token value: %s", value); - else { - r = set_ensure_allocated(&a->tokens, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - r = set_put(a->tokens, UINT_TO_PTR(token)); - if (r < 0) - log_unit_error_errno(u, r, "Failed to add token to set: %m"); - } - } else if (streq(key, "expire-token")) { - unsigned token; - - if (safe_atou(value, &token) < 0) - log_unit_debug(u, "Failed to parse token value: %s", value); - else { - r = set_ensure_allocated(&a->expire_tokens, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - r = set_put(a->expire_tokens, UINT_TO_PTR(token)); - if (r < 0) - log_unit_error_errno(u, r, "Failed to add expire token to set: %m"); - } - } else if (streq(key, "pipe-fd")) { - int fd; - - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse pipe-fd value: %s", value); - else { - safe_close(a->pipe_fd); - a->pipe_fd = fdset_remove(fds, fd); - } - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -static UnitActiveState automount_active_state(Unit *u) { - assert(u); - - return state_translation_table[AUTOMOUNT(u)->state]; -} - -static const char *automount_sub_state_to_string(Unit *u) { - assert(u); - - return automount_state_to_string(AUTOMOUNT(u)->state); -} - -static bool automount_check_gc(Unit *u) { - assert(u); - - if (!UNIT_TRIGGER(u)) - return false; - - return UNIT_VTABLE(UNIT_TRIGGER(u))->check_gc(UNIT_TRIGGER(u)); -} - -static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) { - _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; - Unit *trigger; - int r; - - assert(a); - assert(fd == a->pipe_fd); - - if (events != EPOLLIN) { - log_unit_error(UNIT(a), "Got invalid poll event %"PRIu32" on pipe (fd=%d)", events, fd); - goto fail; - } - - r = loop_read_exact(a->pipe_fd, &packet, sizeof(packet), true); - if (r < 0) { - log_unit_error_errno(UNIT(a), r, "Invalid read from pipe: %m"); - goto fail; - } - - switch (packet.hdr.type) { - - case autofs_ptype_missing_direct: - - if (packet.v5_packet.pid > 0) { - _cleanup_free_ char *p = NULL; - - get_process_comm(packet.v5_packet.pid, &p); - log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p)); - } else - log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where); - - r = set_ensure_allocated(&a->tokens, NULL); - if (r < 0) { - log_unit_error(UNIT(a), "Failed to allocate token set."); - goto fail; - } - - r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); - if (r < 0) { - log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); - goto fail; - } - - automount_enter_runnning(a); - break; - - case autofs_ptype_expire_direct: - log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); - - automount_stop_expire(a); - - r = set_ensure_allocated(&a->expire_tokens, NULL); - if (r < 0) { - log_unit_error(UNIT(a), "Failed to allocate token set."); - goto fail; - } - - r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); - if (r < 0) { - log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); - goto fail; - } - - /* Before we do anything, let's see if somebody is playing games with us? */ - if (lstat(a->where, &st) < 0) { - log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); - goto fail; - } - - if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) { - log_unit_info(UNIT(a), "Automount point already unmounted?"); - automount_send_ready(a, a->expire_tokens, 0); - break; - } - - trigger = UNIT_TRIGGER(UNIT(a)); - if (!trigger) { - log_unit_error(UNIT(a), "Unit to trigger vanished."); - goto fail; - } - - r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, 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; - } - break; - - default: - log_unit_error(UNIT(a), "Received unknown automount request %i", packet.hdr.type); - break; - } - - return 0; - -fail: - automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); - return 0; -} - -static void automount_shutdown(Manager *m) { - assert(m); - - m->dev_autofs_fd = safe_close(m->dev_autofs_fd); -} - -static void automount_reset_failed(Unit *u) { - Automount *a = AUTOMOUNT(u); - - assert(a); - - if (a->state == AUTOMOUNT_FAILED) - automount_set_state(a, AUTOMOUNT_DEAD); - - a->result = AUTOMOUNT_SUCCESS; -} - -static bool automount_supported(void) { - static int supported = -1; - - if (supported < 0) - supported = access("/dev/autofs", F_OK) >= 0; - - return supported; -} - -static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { - [AUTOMOUNT_SUCCESS] = "success", - [AUTOMOUNT_FAILURE_RESOURCES] = "resources", - [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", - [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); - -const UnitVTable automount_vtable = { - .object_size = sizeof(Automount), - - .sections = - "Unit\0" - "Automount\0" - "Install\0", - - .init = automount_init, - .load = automount_load, - .done = automount_done, - - .coldplug = automount_coldplug, - - .dump = automount_dump, - - .start = automount_start, - .stop = automount_stop, - - .serialize = automount_serialize, - .deserialize_item = automount_deserialize_item, - - .active_state = automount_active_state, - .sub_state_to_string = automount_sub_state_to_string, - - .check_gc = automount_check_gc, - - .trigger_notify = automount_trigger_notify, - - .reset_failed = automount_reset_failed, - - .bus_vtable = bus_automount_vtable, - - .shutdown = automount_shutdown, - .supported = automount_supported, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Set up automount %s.", - [JOB_FAILED] = "Failed to set up automount %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Unset automount %s.", - [JOB_FAILED] = "Failed to unset automount %s.", - }, - }, -}; diff --git a/src/core/automount.h b/src/core/automount.h deleted file mode 100644 index 76a201178e..0000000000 --- a/src/core/automount.h +++ /dev/null @@ -1,59 +0,0 @@ -#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 . -***/ - -typedef struct Automount Automount; - -#include "unit.h" - -typedef enum AutomountResult { - AUTOMOUNT_SUCCESS, - AUTOMOUNT_FAILURE_RESOURCES, - AUTOMOUNT_FAILURE_START_LIMIT_HIT, - AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT, - _AUTOMOUNT_RESULT_MAX, - _AUTOMOUNT_RESULT_INVALID = -1 -} AutomountResult; - -struct Automount { - Unit meta; - - AutomountState state, deserialized_state; - - char *where; - usec_t timeout_idle_usec; - - int pipe_fd; - sd_event_source *pipe_event_source; - mode_t directory_mode; - dev_t dev_id; - - Set *tokens; - Set *expire_tokens; - - sd_event_source *expire_event_source; - - AutomountResult result; -}; - -extern const UnitVTable automount_vtable; - -const char* automount_result_to_string(AutomountResult i) _const_; -AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/bus-policy.c b/src/core/bus-policy.c deleted file mode 100644 index 4907c268e8..0000000000 --- a/src/core/bus-policy.c +++ /dev/null @@ -1,180 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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); - assert(access < _BUS_POLICY_ACCESS_MAX); - - switch (access) { - - case BUS_POLICY_ACCESS_SEE: - return KDBUS_POLICY_SEE; - - case BUS_POLICY_ACCESS_TALK: - return KDBUS_POLICY_TALK; - - case BUS_POLICY_ACCESS_OWN: - return KDBUS_POLICY_OWN; - - default: - assert_not_reached("Unknown policy access"); - } -} - -int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item) { - int r; - - assert(policy); - assert(item); - - switch (policy->type) { - - case BUSNAME_POLICY_TYPE_USER: { - const char *user = policy->name; - uid_t uid; - - r = get_user_creds(&user, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - item->policy_access.type = KDBUS_POLICY_ACCESS_USER; - item->policy_access.id = uid; - break; - } - - case BUSNAME_POLICY_TYPE_GROUP: { - const char *group = policy->name; - gid_t gid; - - r = get_group_creds(&group, &gid); - if (r < 0) - return r; - - item->policy_access.type = KDBUS_POLICY_ACCESS_GROUP; - item->policy_access.id = gid; - break; - } - - default: - assert_not_reached("Unknown policy type"); - } - - item->policy_access.access = bus_kernel_translate_access(policy->access); - - return 0; -} - -int bus_kernel_make_starter( - int fd, - const char *name, - bool activating, - bool accept_fd, - BusNamePolicy *policy, - BusPolicyAccess world_policy) { - - struct kdbus_cmd_free cmd_free = { .size = sizeof(cmd_free) }; - struct kdbus_cmd_hello *hello; - struct kdbus_item *n; - size_t policy_cnt = 0; - BusNamePolicy *po; - size_t size; - int r; - - assert(fd >= 0); - assert(name); - - LIST_FOREACH(policy, po, policy) - policy_cnt++; - - if (world_policy >= 0) - policy_cnt++; - - size = offsetof(struct kdbus_cmd_hello, items) + - ALIGN8(offsetof(struct kdbus_item, str) + strlen(name) + 1) + - policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); - - hello = alloca0_align(size, 8); - - n = hello->items; - strcpy(n->str, name); - n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; - n->type = KDBUS_ITEM_NAME; - n = KDBUS_ITEM_NEXT(n); - - LIST_FOREACH(policy, po, policy) { - n->type = KDBUS_ITEM_POLICY_ACCESS; - n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); - - r = bus_kernel_translate_policy(po, n); - if (r < 0) - return r; - - n = KDBUS_ITEM_NEXT(n); - } - - if (world_policy >= 0) { - n->type = KDBUS_ITEM_POLICY_ACCESS; - n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); - n->policy_access.type = KDBUS_POLICY_ACCESS_WORLD; - n->policy_access.access = bus_kernel_translate_access(world_policy); - } - - hello->size = size; - hello->flags = - (activating ? KDBUS_HELLO_ACTIVATOR : KDBUS_HELLO_POLICY_HOLDER) | - (accept_fd ? KDBUS_HELLO_ACCEPT_FD : 0); - hello->pool_size = KDBUS_POOL_SIZE; - hello->attach_flags_send = _KDBUS_ATTACH_ANY; - hello->attach_flags_recv = _KDBUS_ATTACH_ANY; - - if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) { - if (errno == ENOTTY) /* Major API change */ - return -ESOCKTNOSUPPORT; - return -errno; - } - - /* not interested in any output values */ - cmd_free.offset = hello->offset; - (void) ioctl(fd, KDBUS_CMD_FREE, &cmd_free); - - /* The higher 32bit of the bus_flags fields are considered - * 'incompatible flags'. Refuse them all for now. */ - if (hello->bus_flags > 0xFFFFFFFFULL) - return -ESOCKTNOSUPPORT; - - return fd; -} - -static const char* const bus_policy_access_table[_BUS_POLICY_ACCESS_MAX] = { - [BUS_POLICY_ACCESS_SEE] = "see", - [BUS_POLICY_ACCESS_TALK] = "talk", - [BUS_POLICY_ACCESS_OWN] = "own", -}; - -DEFINE_STRING_TABLE_LOOKUP(bus_policy_access, BusPolicyAccess); diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h deleted file mode 100644 index 5b2c4d5953..0000000000 --- a/src/core/bus-policy.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "kdbus.h" -#include "list.h" -#include "macro.h" - -typedef struct BusNamePolicy BusNamePolicy; - -typedef enum BusPolicyAccess { - BUS_POLICY_ACCESS_SEE, - BUS_POLICY_ACCESS_TALK, - BUS_POLICY_ACCESS_OWN, - _BUS_POLICY_ACCESS_MAX, - _BUS_POLICY_ACCESS_INVALID = -1 -} BusPolicyAccess; - -typedef enum BusNamePolicyType { - BUSNAME_POLICY_TYPE_USER, - BUSNAME_POLICY_TYPE_GROUP, - _BUSNAME_POLICY_TYPE_MAX, - _BUSNAME_POLICY_TYPE_INVALID = -1 -} BusNamePolicyType; - -struct BusNamePolicy { - BusNamePolicyType type; - BusPolicyAccess access; - - char *name; - - LIST_FIELDS(BusNamePolicy, policy); -}; - -int bus_kernel_translate_access(BusPolicyAccess access); -int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item); - -const char* bus_policy_access_to_string(BusPolicyAccess i) _const_; -BusPolicyAccess bus_policy_access_from_string(const char *s) _pure_; - -int bus_kernel_make_starter( - int fd, - const char *name, - bool activating, - bool accept_fd, - BusNamePolicy *policy, - BusPolicyAccess world_policy); diff --git a/src/core/busname.c b/src/core/busname.c deleted file mode 100644 index 730be2ee14..0000000000 --- a/src/core/busname.c +++ /dev/null @@ -1,1077 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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 "parse-util.h" -#include "process-util.h" -#include "service.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, - [BUSNAME_MAKING] = UNIT_ACTIVATING, - [BUSNAME_REGISTERED] = UNIT_ACTIVE, - [BUSNAME_LISTENING] = UNIT_ACTIVE, - [BUSNAME_RUNNING] = UNIT_ACTIVE, - [BUSNAME_SIGTERM] = UNIT_DEACTIVATING, - [BUSNAME_SIGKILL] = UNIT_DEACTIVATING, - [BUSNAME_FAILED] = UNIT_FAILED -}; - -static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); - -static void busname_init(Unit *u) { - BusName *n = BUSNAME(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - n->starter_fd = -1; - n->accept_fd = true; - n->activating = true; - - n->timeout_usec = u->manager->default_timeout_start_usec; -} - -static void busname_unwatch_control_pid(BusName *n) { - assert(n); - - if (n->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(n), n->control_pid); - n->control_pid = 0; -} - -static void busname_free_policy(BusName *n) { - BusNamePolicy *p; - - assert(n); - - while ((p = n->policy)) { - LIST_REMOVE(policy, n->policy, p); - - free(p->name); - free(p); - } -} - -static void busname_close_fd(BusName *n) { - assert(n); - - n->starter_event_source = sd_event_source_unref(n->starter_event_source); - n->starter_fd = safe_close(n->starter_fd); -} - -static void busname_done(Unit *u) { - BusName *n = BUSNAME(u); - - assert(n); - - n->name = mfree(n->name); - - busname_free_policy(n); - busname_unwatch_control_pid(n); - busname_close_fd(n); - - unit_ref_unset(&n->service); - - n->timer_event_source = sd_event_source_unref(n->timer_event_source); -} - -static int busname_arm_timer(BusName *n, usec_t usec) { - int r; - - assert(n); - - if (n->timer_event_source) { - r = sd_event_source_set_time(n->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(n)->manager->event, - &n->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - busname_dispatch_timer, n); - if (r < 0) - return r; - - (void) sd_event_source_set_description(n->timer_event_source, "busname-timer"); - - return 0; -} - -static int busname_add_default_default_dependencies(BusName *n) { - int r; - - assert(n); - - r = unit_add_dependency_by_name(UNIT(n), UNIT_BEFORE, SPECIAL_BUSNAMES_TARGET, NULL, true); - if (r < 0) - return r; - - if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); - if (r < 0) - return r; - } - - return unit_add_two_dependencies_by_name(UNIT(n), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static int busname_add_extras(BusName *n) { - Unit *u = UNIT(n); - int r; - - assert(n); - - if (!n->name) { - r = unit_name_to_prefix(u->id, &n->name); - if (r < 0) - return r; - } - - if (!u->description) { - r = unit_set_description(u, n->name); - if (r < 0) - return r; - } - - if (n->activating) { - if (!UNIT_DEREF(n->service)) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - unit_ref_set(&n->service, x); - } - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(n->service), true); - if (r < 0) - return r; - } - - if (u->default_dependencies) { - r = busname_add_default_default_dependencies(n); - if (r < 0) - return r; - } - - return 0; -} - -static int busname_verify(BusName *n) { - char *e; - - assert(n); - - if (UNIT(n)->load_state != UNIT_LOADED) - return 0; - - if (!service_name_is_valid(n->name)) { - log_unit_error(UNIT(n), "Name= setting is not a valid service name Refusing."); - return -EINVAL; - } - - e = strjoina(n->name, ".busname"); - if (!unit_has_name(UNIT(n), e)) { - log_unit_error(UNIT(n), "Name= setting doesn't match unit name. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int busname_load(Unit *u) { - BusName *n = BUSNAME(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - r = unit_load_fragment_and_dropin(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - /* This is a new unit? Then let's add in some extras */ - r = busname_add_extras(n); - if (r < 0) - return r; - } - - return busname_verify(n); -} - -static void busname_dump(Unit *u, FILE *f, const char *prefix) { - BusName *n = BUSNAME(u); - - assert(n); - assert(f); - - fprintf(f, - "%sBus Name State: %s\n" - "%sResult: %s\n" - "%sName: %s\n" - "%sActivating: %s\n" - "%sAccept FD: %s\n", - prefix, busname_state_to_string(n->state), - prefix, busname_result_to_string(n->result), - prefix, n->name, - prefix, yes_no(n->activating), - prefix, yes_no(n->accept_fd)); - - if (n->control_pid > 0) - fprintf(f, - "%sControl PID: "PID_FMT"\n", - prefix, n->control_pid); -} - -static void busname_unwatch_fd(BusName *n) { - int r; - - assert(n); - - if (!n->starter_event_source) - return; - - r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_OFF); - if (r < 0) - log_unit_debug_errno(UNIT(n), r, "Failed to disable event source: %m"); -} - -static int busname_watch_fd(BusName *n) { - int r; - - assert(n); - - if (n->starter_fd < 0) - return 0; - - if (n->starter_event_source) { - r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON); - if (r < 0) - goto fail; - } else { - r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(n->starter_event_source, "busname-starter"); - } - - return 0; - -fail: - log_unit_warning_errno(UNIT(n), r, "Failed to watch starter fd: %m"); - busname_unwatch_fd(n); - return r; -} - -static int busname_open_fd(BusName *n) { - _cleanup_free_ char *path = NULL; - const char *mode; - - assert(n); - - if (n->starter_fd >= 0) - return 0; - - mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user"; - n->starter_fd = bus_kernel_open_bus_fd(mode, &path); - if (n->starter_fd < 0) - return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus"); - - return 0; -} - -static void busname_set_state(BusName *n, BusNameState state) { - BusNameState old_state; - assert(n); - - old_state = n->state; - n->state = state; - - if (!IN_SET(state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { - n->timer_event_source = sd_event_source_unref(n->timer_event_source); - busname_unwatch_control_pid(n); - } - - if (state != BUSNAME_LISTENING) - busname_unwatch_fd(n); - - if (!IN_SET(state, BUSNAME_LISTENING, BUSNAME_MAKING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) - busname_close_fd(n); - - if (state != old_state) - log_unit_debug(UNIT(n), "Changed %s -> %s", busname_state_to_string(old_state), busname_state_to_string(state)); - - unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true); -} - -static int busname_coldplug(Unit *u) { - BusName *n = BUSNAME(u); - int r; - - assert(n); - assert(n->state == BUSNAME_DEAD); - - if (n->deserialized_state == n->state) - return 0; - - 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) - return r; - - r = busname_arm_timer(n, usec_add(u->state_change_timestamp.monotonic, n->timeout_usec)); - if (r < 0) - return r; - } - - if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_LISTENING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) { - r = busname_open_fd(n); - if (r < 0) - return r; - } - - if (n->deserialized_state == BUSNAME_LISTENING) { - r = busname_watch_fd(n); - if (r < 0) - return r; - } - - busname_set_state(n, n->deserialized_state); - return 0; -} - -static int busname_make_starter(BusName *n, pid_t *_pid) { - pid_t pid; - int r; - - r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec)); - if (r < 0) - goto fail; - - /* We have to resolve the user/group names out-of-process, - * hence let's fork here. It's messy, but well, what can we - * do? */ - - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { - int ret; - - (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); - (void) ignore_signals(SIGPIPE, -1); - log_forget_fds(); - - r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, n->policy, n->policy_world); - if (r < 0) { - ret = EXIT_MAKE_STARTER; - goto fail_child; - } - - _exit(0); - - fail_child: - log_open(); - log_error_errno(r, "Failed to create starter connection at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); - - _exit(ret); - } - - r = unit_watch_pid(UNIT(n), pid); - if (r < 0) - goto fail; - - *_pid = pid; - return 0; - -fail: - n->timer_event_source = sd_event_source_unref(n->timer_event_source); - return r; -} - -static void busname_enter_dead(BusName *n, BusNameResult f) { - assert(n); - - if (f != BUSNAME_SUCCESS) - n->result = f; - - busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); -} - -static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f) { - KillContext kill_context = {}; - int r; - - assert(n); - - if (f != BUSNAME_SUCCESS) - n->result = f; - - kill_context_init(&kill_context); - - r = unit_kill_context(UNIT(n), - &kill_context, - state != BUSNAME_SIGTERM ? KILL_KILL : KILL_TERMINATE, - -1, - n->control_pid, - false); - if (r < 0) { - log_unit_warning_errno(UNIT(n), r, "Failed to kill control process: %m"); - goto fail; - } - - if (r > 0) { - r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec)); - if (r < 0) { - log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m"); - goto fail; - } - - busname_set_state(n, state); - } else if (state == BUSNAME_SIGTERM) - busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_SUCCESS); - else - busname_enter_dead(n, BUSNAME_SUCCESS); - - return; - -fail: - busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); -} - -static void busname_enter_listening(BusName *n) { - int r; - - assert(n); - - if (n->activating) { - r = busname_watch_fd(n); - if (r < 0) { - log_unit_warning_errno(UNIT(n), r, "Failed to watch names: %m"); - goto fail; - } - - busname_set_state(n, BUSNAME_LISTENING); - } else - busname_set_state(n, BUSNAME_REGISTERED); - - return; - -fail: - busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_RESOURCES); -} - -static void busname_enter_making(BusName *n) { - int r; - - assert(n); - - r = busname_open_fd(n); - if (r < 0) - goto fail; - - if (n->policy) { - /* If there is a policy, we need to resolve user/group - * names, which we can't do from PID1, hence let's - * fork. */ - busname_unwatch_control_pid(n); - - r = busname_make_starter(n, &n->control_pid); - if (r < 0) { - log_unit_warning_errno(UNIT(n), r, "Failed to fork 'making' task: %m"); - goto fail; - } - - busname_set_state(n, BUSNAME_MAKING); - } else { - /* If there is no policy, we can do everything - * directly from PID 1, hence do so. */ - - r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world); - if (r < 0) { - log_unit_warning_errno(UNIT(n), r, "Failed to make starter: %m"); - goto fail; - } - - busname_enter_listening(n); - } - - return; - -fail: - busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); -} - -static void busname_enter_running(BusName *n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - bool pending = false; - Unit *other; - Iterator i; - int r; - - assert(n); - - if (!n->activating) - return; - - /* We don't take connections anymore if we are supposed to - * shut down anyway */ - - if (unit_stop_pending(UNIT(n))) { - log_unit_debug(UNIT(n), "Suppressing activation request since unit stop is scheduled."); - - /* Flush all queued activation reqeuest by closing and reopening the connection */ - bus_kernel_drop_one(n->starter_fd); - - busname_enter_listening(n); - return; - } - - /* If there's already a start pending don't bother to do - * anything */ - SET_FOREACH(other, UNIT(n)->dependencies[UNIT_TRIGGERS], i) - if (unit_active_or_pending(other)) { - pending = true; - break; - } - - if (!pending) { - 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; - } - - busname_set_state(n, BUSNAME_RUNNING); - return; - -fail: - log_unit_warning(UNIT(n), "Failed to queue service startup job: %s", bus_error_message(&error, r)); - busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); -} - -static int busname_start(Unit *u) { - BusName *n = BUSNAME(u); - int r; - - assert(n); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) - return -EAGAIN; - - /* Already on it! */ - if (n->state == BUSNAME_MAKING) - return 0; - - if (n->activating && UNIT_ISSET(n->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(n->service)); - - if (UNIT(service)->load_state != UNIT_LOADED) { - log_unit_error(u, "Bus service %s not loaded, refusing.", UNIT(service)->id); - return -ENOENT; - } - } - - assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); - - r = unit_start_limit_test(u); - if (r < 0) { - busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT); - return r; - } - - n->result = BUSNAME_SUCCESS; - busname_enter_making(n); - - return 1; -} - -static int busname_stop(Unit *u) { - BusName *n = BUSNAME(u); - - assert(n); - - /* Already on it */ - if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) - return 0; - - /* If there's already something running, we go directly into - * kill mode. */ - - if (n->state == BUSNAME_MAKING) { - busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_SUCCESS); - return -EAGAIN; - } - - assert(IN_SET(n->state, BUSNAME_REGISTERED, BUSNAME_LISTENING, BUSNAME_RUNNING)); - - busname_enter_dead(n, BUSNAME_SUCCESS); - return 1; -} - -static int busname_serialize(Unit *u, FILE *f, FDSet *fds) { - BusName *n = BUSNAME(u); - int r; - - assert(n); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", busname_state_to_string(n->state)); - unit_serialize_item(u, f, "result", busname_result_to_string(n->result)); - - if (n->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid); - - r = unit_serialize_item_fd(u, f, fds, "starter-fd", n->starter_fd); - if (r < 0) - return r; - - return 0; -} - -static int busname_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - BusName *n = BUSNAME(u); - - assert(n); - assert(key); - assert(value); - - if (streq(key, "state")) { - BusNameState state; - - state = busname_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - n->deserialized_state = state; - - } else if (streq(key, "result")) { - BusNameResult f; - - f = busname_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != BUSNAME_SUCCESS) - n->result = f; - - } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - n->control_pid = pid; - } else if (streq(key, "starter-fd")) { - int fd; - - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse starter fd value: %s", value); - else { - safe_close(n->starter_fd); - n->starter_fd = fdset_remove(fds, fd); - } - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState busname_active_state(Unit *u) { - assert(u); - - return state_translation_table[BUSNAME(u)->state]; -} - -_pure_ static const char *busname_sub_state_to_string(Unit *u) { - assert(u); - - return busname_state_to_string(BUSNAME(u)->state); -} - -static int busname_peek_message(BusName *n) { - struct kdbus_cmd_recv cmd_recv = { - .size = sizeof(cmd_recv), - .flags = KDBUS_RECV_PEEK, - }; - struct kdbus_cmd_free cmd_free = { - .size = sizeof(cmd_free), - }; - const char *comm = NULL; - struct kdbus_item *d; - struct kdbus_msg *k; - size_t start, ps, sz, delta; - void *p = NULL; - pid_t pid = 0; - int r; - - /* Generate a friendly debug log message about which process - * caused triggering of this bus name. This simply peeks the - * metadata of the first queued message and logs it. */ - - assert(n); - - /* Let's shortcut things a bit, if debug logging is turned off - * anyway. */ - - if (log_get_max_level() < LOG_DEBUG) - return 0; - - r = ioctl(n->starter_fd, KDBUS_CMD_RECV, &cmd_recv); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) - return 0; - - return log_unit_error_errno(UNIT(n), errno, "Failed to query activation message: %m"); - } - - /* We map as late as possible, and unmap imemdiately after - * use. On 32bit address space is scarce and we want to be - * able to handle a lot of activator connections at the same - * time, and hence shouldn't keep the mmap()s around for - * longer than necessary. */ - - ps = page_size(); - start = (cmd_recv.msg.offset / ps) * ps; - delta = cmd_recv.msg.offset - start; - sz = PAGE_ALIGN(delta + cmd_recv.msg.msg_size); - - p = mmap(NULL, sz, PROT_READ, MAP_SHARED, n->starter_fd, start); - if (p == MAP_FAILED) { - r = log_unit_error_errno(UNIT(n), errno, "Failed to map activation message: %m"); - goto finish; - } - - k = (struct kdbus_msg *) ((uint8_t *) p + delta); - KDBUS_ITEM_FOREACH(d, k, items) { - switch (d->type) { - - case KDBUS_ITEM_PIDS: - pid = d->pids.pid; - break; - - case KDBUS_ITEM_PID_COMM: - comm = d->str; - break; - } - } - - if (pid > 0) - log_unit_debug(UNIT(n), "Activation triggered by process " PID_FMT " (%s)", pid, strna(comm)); - - r = 0; - -finish: - if (p) - (void) munmap(p, sz); - - cmd_free.offset = cmd_recv.msg.offset; - if (ioctl(n->starter_fd, KDBUS_CMD_FREE, &cmd_free) < 0) - log_unit_warning(UNIT(n), "Failed to free peeked message, ignoring: %m"); - - return r; -} - -static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - BusName *n = userdata; - - assert(n); - assert(fd >= 0); - - if (n->state != BUSNAME_LISTENING) - return 0; - - log_unit_debug(UNIT(n), "Activation request"); - - if (revents != EPOLLIN) { - log_unit_error(UNIT(n), "Got unexpected poll event (0x%x) on starter fd.", revents); - goto fail; - } - - busname_peek_message(n); - busname_enter_running(n); - return 0; -fail: - - busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); - return 0; -} - -static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { - BusName *n = BUSNAME(u); - BusNameResult f; - - assert(n); - assert(pid >= 0); - - if (pid != n->control_pid) - return; - - n->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = BUSNAME_SUCCESS; - else if (code == CLD_EXITED) - f = BUSNAME_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = BUSNAME_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = BUSNAME_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown sigchld code"); - - log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, - "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - - if (f != BUSNAME_SUCCESS) - n->result = f; - - switch (n->state) { - - case BUSNAME_MAKING: - if (f == BUSNAME_SUCCESS) - busname_enter_listening(n); - else - busname_enter_signal(n, BUSNAME_SIGTERM, f); - break; - - case BUSNAME_SIGTERM: - case BUSNAME_SIGKILL: - busname_enter_dead(n, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - BusName *n = BUSNAME(userdata); - - assert(n); - assert(n->timer_event_source == source); - - switch (n->state) { - - case BUSNAME_MAKING: - log_unit_warning(UNIT(n), "Making timed out. Terminating."); - busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_TIMEOUT); - break; - - case BUSNAME_SIGTERM: - log_unit_warning(UNIT(n), "Stopping timed out. Killing."); - busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_FAILURE_TIMEOUT); - break; - - case BUSNAME_SIGKILL: - log_unit_warning(UNIT(n), "Processes still around after SIGKILL. Ignoring."); - busname_enter_dead(n, BUSNAME_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -static void busname_reset_failed(Unit *u) { - BusName *n = BUSNAME(u); - - assert(n); - - if (n->state == BUSNAME_FAILED) - busname_set_state(n, BUSNAME_DEAD); - - n->result = BUSNAME_SUCCESS; -} - -static void busname_trigger_notify(Unit *u, Unit *other) { - BusName *n = BUSNAME(u); - - assert(n); - assert(other); - - if (!IN_SET(n->state, BUSNAME_RUNNING, BUSNAME_LISTENING)) - return; - - if (other->start_limit_hit) { - busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT); - return; - } - - if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) - return; - - if (IN_SET(SERVICE(other)->state, - SERVICE_DEAD, SERVICE_FAILED, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, - SERVICE_AUTO_RESTART)) - busname_enter_listening(n); - - if (SERVICE(other)->state == SERVICE_RUNNING) - busname_set_state(n, BUSNAME_RUNNING); -} - -static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - return unit_kill_common(u, who, signo, -1, BUSNAME(u)->control_pid, error); -} - -static int busname_get_timeout(Unit *u, usec_t *timeout) { - BusName *n = BUSNAME(u); - usec_t t; - int r; - - if (!n->timer_event_source) - return 0; - - r = sd_event_source_get_time(n->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -static bool busname_supported(void) { - return false; -} - -static int busname_control_pid(Unit *u) { - BusName *n = BUSNAME(u); - - assert(n); - - return n->control_pid; -} - -static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { - [BUSNAME_SUCCESS] = "success", - [BUSNAME_FAILURE_RESOURCES] = "resources", - [BUSNAME_FAILURE_TIMEOUT] = "timeout", - [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", - [BUSNAME_FAILURE_SIGNAL] = "signal", - [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", - [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit", - [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(busname_result, BusNameResult); - -const UnitVTable busname_vtable = { - .object_size = sizeof(BusName), - - .sections = - "Unit\0" - "BusName\0" - "Install\0", - .private_section = "BusName", - - .init = busname_init, - .done = busname_done, - .load = busname_load, - - .coldplug = busname_coldplug, - - .dump = busname_dump, - - .start = busname_start, - .stop = busname_stop, - - .kill = busname_kill, - - .get_timeout = busname_get_timeout, - - .serialize = busname_serialize, - .deserialize_item = busname_deserialize_item, - - .active_state = busname_active_state, - .sub_state_to_string = busname_sub_state_to_string, - - .sigchld_event = busname_sigchld_event, - - .trigger_notify = busname_trigger_notify, - - .reset_failed = busname_reset_failed, - - .supported = busname_supported, - - .control_pid = busname_control_pid, - - .bus_vtable = bus_busname_vtable, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Listening on %s.", - [JOB_FAILED] = "Failed to listen on %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Closed %s.", - [JOB_FAILED] = "Failed stopping %s.", - }, - }, -}; diff --git a/src/core/busname.h b/src/core/busname.h deleted file mode 100644 index a8562db458..0000000000 --- a/src/core/busname.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct BusName BusName; -typedef struct BusNamePolicy BusNamePolicy; - -#include "unit.h" -#include "bus-policy.h" - -typedef enum BusNameResult { - BUSNAME_SUCCESS, - BUSNAME_FAILURE_RESOURCES, - BUSNAME_FAILURE_TIMEOUT, - BUSNAME_FAILURE_EXIT_CODE, - BUSNAME_FAILURE_SIGNAL, - BUSNAME_FAILURE_CORE_DUMP, - BUSNAME_FAILURE_START_LIMIT_HIT, - BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT, - _BUSNAME_RESULT_MAX, - _BUSNAME_RESULT_INVALID = -1 -} BusNameResult; - -struct BusName { - Unit meta; - - char *name; - int starter_fd; - - bool activating; - bool accept_fd; - - UnitRef service; - - BusNameState state, deserialized_state; - BusNameResult result; - - usec_t timeout_usec; - - sd_event_source *starter_event_source; - sd_event_source *timer_event_source; - - pid_t control_pid; - - LIST_HEAD(BusNamePolicy, policy); - BusPolicyAccess policy_world; -}; - -extern const UnitVTable busname_vtable; - -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 deleted file mode 100644 index c19e43f571..0000000000 --- a/src/core/cgroup.c +++ /dev/null @@ -1,1991 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 "string-table.h" -#include "string-util.h" -#include "stdio-util.h" - -#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) - -static void cgroup_compat_warn(void) { - static bool cgroup_compat_warned = false; - - if (cgroup_compat_warned) - return; - - log_warning("cgroup compatibility translation between legacy and unified hierarchy settings activated. See cgroup-compat debug messages for details."); - cgroup_compat_warned = true; -} - -#define log_cgroup_compat(unit, fmt, ...) do { \ - cgroup_compat_warn(); \ - log_unit_debug(unit, "cgroup-compat: " fmt, ##__VA_ARGS__); \ - } while (false) - -void cgroup_context_init(CGroupContext *c) { - assert(c); - - /* Initialize everything to the kernel defaults, assuming the - * structure is preinitialized to 0 */ - - c->cpu_shares = CGROUP_CPU_SHARES_INVALID; - c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; - c->cpu_quota_per_sec_usec = USEC_INFINITY; - - c->memory_high = CGROUP_LIMIT_MAX; - c->memory_max = CGROUP_LIMIT_MAX; - - c->memory_limit = CGROUP_LIMIT_MAX; - - c->io_weight = CGROUP_WEIGHT_INVALID; - c->startup_io_weight = CGROUP_WEIGHT_INVALID; - - c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; - c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; - - c->tasks_max = (uint64_t) -1; -} - -void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { - assert(c); - assert(a); - - LIST_REMOVE(device_allow, c->device_allow, a); - free(a->path); - free(a); -} - -void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w) { - assert(c); - assert(w); - - LIST_REMOVE(device_weights, c->io_device_weights, w); - free(w->path); - free(w); -} - -void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) { - assert(c); - assert(l); - - LIST_REMOVE(device_limits, c->io_device_limits, l); - free(l->path); - free(l); -} - -void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) { - assert(c); - assert(w); - - LIST_REMOVE(device_weights, c->blockio_device_weights, w); - free(w->path); - free(w); -} - -void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) { - assert(c); - assert(b); - - LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b); - free(b->path); - free(b); -} - -void cgroup_context_done(CGroupContext *c) { - assert(c); - - while (c->io_device_weights) - cgroup_context_free_io_device_weight(c, c->io_device_weights); - - while (c->io_device_limits) - cgroup_context_free_io_device_limit(c, c->io_device_limits); - - while (c->blockio_device_weights) - cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); - - while (c->blockio_device_bandwidths) - cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths); - - while (c->device_allow) - cgroup_context_free_device_allow(c, c->device_allow); -} - -void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { - CGroupIODeviceLimit *il; - CGroupIODeviceWeight *iw; - CGroupBlockIODeviceBandwidth *b; - CGroupBlockIODeviceWeight *w; - CGroupDeviceAllow *a; - char u[FORMAT_TIMESPAN_MAX]; - - assert(c); - assert(f); - - prefix = strempty(prefix); - - fprintf(f, - "%sCPUAccounting=%s\n" - "%sIOAccounting=%s\n" - "%sBlockIOAccounting=%s\n" - "%sMemoryAccounting=%s\n" - "%sTasksAccounting=%s\n" - "%sCPUShares=%" PRIu64 "\n" - "%sStartupCPUShares=%" PRIu64 "\n" - "%sCPUQuotaPerSecSec=%s\n" - "%sIOWeight=%" PRIu64 "\n" - "%sStartupIOWeight=%" PRIu64 "\n" - "%sBlockIOWeight=%" PRIu64 "\n" - "%sStartupBlockIOWeight=%" PRIu64 "\n" - "%sMemoryLow=%" PRIu64 "\n" - "%sMemoryHigh=%" PRIu64 "\n" - "%sMemoryMax=%" PRIu64 "\n" - "%sMemoryLimit=%" PRIu64 "\n" - "%sTasksMax=%" PRIu64 "\n" - "%sDevicePolicy=%s\n" - "%sDelegate=%s\n", - prefix, yes_no(c->cpu_accounting), - prefix, yes_no(c->io_accounting), - prefix, yes_no(c->blockio_accounting), - prefix, yes_no(c->memory_accounting), - prefix, yes_no(c->tasks_accounting), - prefix, c->cpu_shares, - prefix, c->startup_cpu_shares, - prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), - prefix, c->io_weight, - prefix, c->startup_io_weight, - prefix, c->blockio_weight, - prefix, c->startup_blockio_weight, - prefix, c->memory_low, - prefix, c->memory_high, - prefix, c->memory_max, - prefix, c->memory_limit, - prefix, c->tasks_max, - prefix, cgroup_device_policy_to_string(c->device_policy), - prefix, yes_no(c->delegate)); - - LIST_FOREACH(device_allow, a, c->device_allow) - fprintf(f, - "%sDeviceAllow=%s %s%s%s\n", - prefix, - a->path, - a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); - - LIST_FOREACH(device_weights, iw, c->io_device_weights) - fprintf(f, - "%sIODeviceWeight=%s %" PRIu64, - prefix, - iw->path, - iw->weight); - - LIST_FOREACH(device_limits, il, c->io_device_limits) { - char buf[FORMAT_BYTES_MAX]; - CGroupIOLimitType type; - - for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) - if (il->limits[type] != cgroup_io_limit_defaults[type]) - fprintf(f, - "%s%s=%s %s\n", - prefix, - cgroup_io_limit_type_to_string(type), - il->path, - format_bytes(buf, sizeof(buf), il->limits[type])); - } - - LIST_FOREACH(device_weights, w, c->blockio_device_weights) - fprintf(f, - "%sBlockIODeviceWeight=%s %" PRIu64, - prefix, - w->path, - w->weight); - - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - char buf[FORMAT_BYTES_MAX]; - - if (b->rbps != CGROUP_LIMIT_MAX) - fprintf(f, - "%sBlockIOReadBandwidth=%s %s\n", - prefix, - b->path, - format_bytes(buf, sizeof(buf), b->rbps)); - if (b->wbps != CGROUP_LIMIT_MAX) - fprintf(f, - "%sBlockIOWriteBandwidth=%s %s\n", - prefix, - b->path, - format_bytes(buf, sizeof(buf), b->wbps)); - } -} - -static int lookup_block_device(const char *p, dev_t *dev) { - struct stat st; - int r; - - assert(p); - assert(dev); - - r = stat(p, &st); - if (r < 0) - return log_warning_errno(errno, "Couldn't stat device %s: %m", p); - - if (S_ISBLK(st.st_mode)) - *dev = st.st_rdev; - else if (major(st.st_dev) != 0) { - /* If this is not a device node then find the block - * device this file is stored on */ - *dev = st.st_dev; - - /* If this is a partition, try to get the originating - * block device */ - block_get_whole_disk(*dev, dev); - } else { - log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p); - return -ENODEV; - } - - return 0; -} - -static int whitelist_device(const char *path, const char *node, const char *acc) { - char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4]; - struct stat st; - int r; - - assert(path); - assert(acc); - - if (stat(node, &st) < 0) { - log_warning("Couldn't stat device %s", node); - return -errno; - } - - if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { - log_warning("%s is not a device.", node); - return -ENODEV; - } - - sprintf(buf, - "%c %u:%u %s", - S_ISCHR(st.st_mode) ? 'c' : 'b', - major(st.st_rdev), minor(st.st_rdev), - acc); - - r = cg_set_attribute("devices", path, "devices.allow", buf); - if (r < 0) - 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; -} - -static int whitelist_major(const char *path, const char *name, char type, const char *acc) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - bool good = false; - int r; - - assert(path); - assert(acc); - assert(type == 'b' || type == 'c'); - - f = fopen("/proc/devices", "re"); - if (!f) - return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type); - - FOREACH_LINE(line, f, goto fail) { - char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w; - unsigned maj; - - truncate_nl(line); - - if (type == 'c' && streq(line, "Character devices:")) { - good = true; - continue; - } - - if (type == 'b' && streq(line, "Block devices:")) { - good = true; - continue; - } - - if (isempty(line)) { - good = false; - continue; - } - - if (!good) - continue; - - p = strstrip(line); - - w = strpbrk(p, WHITESPACE); - if (!w) - continue; - *w = 0; - - r = safe_atou(p, &maj); - if (r < 0) - continue; - if (maj <= 0) - continue; - - w++; - w += strspn(w, WHITESPACE); - - if (fnmatch(name, w, 0) != 0) - continue; - - sprintf(buf, - "%c %u:* %s", - type, - maj, - acc); - - r = cg_set_attribute("devices", path, "devices.allow", buf); - if (r < 0) - 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 0; - -fail: - log_warning_errno(errno, "Failed to read /proc/devices: %m"); - return -errno; -} - -static bool cgroup_context_has_io_config(CGroupContext *c) { - return c->io_accounting || - c->io_weight != CGROUP_WEIGHT_INVALID || - c->startup_io_weight != CGROUP_WEIGHT_INVALID || - c->io_device_weights || - c->io_device_limits; -} - -static bool cgroup_context_has_blockio_config(CGroupContext *c) { - return c->blockio_accounting || - c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || - c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || - c->blockio_device_weights || - c->blockio_device_bandwidths; -} - -static uint64_t cgroup_context_io_weight(CGroupContext *c, ManagerState state) { - if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && - c->startup_io_weight != CGROUP_WEIGHT_INVALID) - return c->startup_io_weight; - else if (c->io_weight != CGROUP_WEIGHT_INVALID) - return c->io_weight; - else - return CGROUP_WEIGHT_DEFAULT; -} - -static uint64_t cgroup_context_blkio_weight(CGroupContext *c, ManagerState state) { - if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && - c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) - return c->startup_blockio_weight; - else if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) - return c->blockio_weight; - else - return CGROUP_BLKIO_WEIGHT_DEFAULT; -} - -static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) { - return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT, - CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX); -} - -static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) { - return CLAMP(io_weight * CGROUP_BLKIO_WEIGHT_DEFAULT / CGROUP_WEIGHT_DEFAULT, - CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX); -} - -static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_t io_weight) { - char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; - dev_t dev; - int r; - - r = lookup_block_device(dev_path, &dev); - if (r < 0) - return; - - xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight); - r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set io.weight: %m"); -} - -static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) { - char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; - dev_t dev; - int r; - - r = lookup_block_device(dev_path, &dev); - if (r < 0) - return; - - xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight); - r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight_device: %m"); -} - -static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { - char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; - char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; - CGroupIOLimitType type; - dev_t dev; - unsigned n = 0; - int r; - - r = lookup_block_device(dev_path, &dev); - if (r < 0) - return 0; - - for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { - if (limits[type] != cgroup_io_limit_defaults[type]) { - xsprintf(limit_bufs[type], "%" PRIu64, limits[type]); - n++; - } else { - xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0"); - } - } - - xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev), - limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX], - limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]); - r = cg_set_attribute("io", u->cgroup_path, "io.max", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set io.max: %m"); - return n; -} - -static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) { - char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; - dev_t dev; - unsigned n = 0; - int r; - - r = lookup_block_device(dev_path, &dev); - if (r < 0) - return 0; - - if (rbps != CGROUP_LIMIT_MAX) - n++; - sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps); - r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.throttle.read_bps_device: %m"); - - if (wbps != CGROUP_LIMIT_MAX) - n++; - sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps); - r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.throttle.write_bps_device: %m"); - - return n; -} - -static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { - return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX; -} - -static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { - char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max"; - int r; - - if (v != CGROUP_LIMIT_MAX) - xsprintf(buf, "%" PRIu64 "\n", v); - - r = cg_set_attribute("memory", u->cgroup_path, file, buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set %s: %m", file); -} - -static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) { - const char *path; - CGroupContext *c; - bool is_root; - int r; - - assert(u); - - c = unit_get_cgroup_context(u); - path = u->cgroup_path; - - assert(c); - assert(path); - - if (mask == 0) - return; - - /* Some cgroup attributes are not supported on the root cgroup, - * hence silently ignore */ - is_root = isempty(path) || path_equal(path, "/"); - if (is_root) - /* Make sure we don't try to display messages with an empty path. */ - path = "/"; - - /* We generally ignore errors caused by read-only mounted - * cgroup trees (assuming we are running in a container then), - * and missing cgroups, i.e. EROFS and ENOENT. */ - - if ((mask & CGROUP_MASK_CPU) && !is_root) { - char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; - - sprintf(buf, "%" PRIu64 "\n", - IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares : - 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_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.shares: %m"); - - 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_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.cfs_period_us: %m"); - - if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { - sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC); - r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf); - } else - r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1"); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set cpu.cfs_quota_us: %m"); - } - - if (mask & CGROUP_MASK_IO) { - bool has_io = cgroup_context_has_io_config(c); - bool has_blockio = cgroup_context_has_blockio_config(c); - - if (!is_root) { - char buf[8+DECIMAL_STR_MAX(uint64_t)+1]; - uint64_t weight; - - if (has_io) - weight = cgroup_context_io_weight(c, state); - else if (has_blockio) { - uint64_t blkio_weight = cgroup_context_blkio_weight(c, state); - - weight = cgroup_weight_blkio_to_io(blkio_weight); - - log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64, - blkio_weight, weight); - } else - weight = CGROUP_WEIGHT_DEFAULT; - - xsprintf(buf, "default %" PRIu64 "\n", weight); - r = cg_set_attribute("io", path, "io.weight", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set io.weight: %m"); - - if (has_io) { - CGroupIODeviceWeight *w; - - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->io_device_weights) - cgroup_apply_io_device_weight(u, w->path, w->weight); - } else if (has_blockio) { - CGroupBlockIODeviceWeight *w; - - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->blockio_device_weights) { - weight = cgroup_weight_blkio_to_io(w->weight); - - log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s", - w->weight, weight, w->path); - - cgroup_apply_io_device_weight(u, w->path, weight); - } - } - } - - /* Apply limits and free ones without config. */ - if (has_io) { - CGroupIODeviceLimit *l, *next; - - LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { - if (!cgroup_apply_io_device_limit(u, l->path, l->limits)) - cgroup_context_free_io_device_limit(c, l); - } - } else if (has_blockio) { - CGroupBlockIODeviceBandwidth *b, *next; - - LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { - uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; - CGroupIOLimitType type; - - for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) - limits[type] = cgroup_io_limit_defaults[type]; - - limits[CGROUP_IO_RBPS_MAX] = b->rbps; - limits[CGROUP_IO_WBPS_MAX] = b->wbps; - - log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s", - b->rbps, b->wbps, b->path); - - if (!cgroup_apply_io_device_limit(u, b->path, limits)) - cgroup_context_free_blockio_device_bandwidth(c, b); - } - } - } - - if (mask & CGROUP_MASK_BLKIO) { - bool has_io = cgroup_context_has_io_config(c); - bool has_blockio = cgroup_context_has_blockio_config(c); - - if (!is_root) { - char buf[DECIMAL_STR_MAX(uint64_t)+1]; - uint64_t weight; - - if (has_blockio) - weight = cgroup_context_blkio_weight(c, state); - else if (has_io) { - uint64_t io_weight = cgroup_context_io_weight(c, state); - - weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state)); - - log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64, - io_weight, weight); - } else - weight = CGROUP_BLKIO_WEIGHT_DEFAULT; - - xsprintf(buf, "%" PRIu64 "\n", weight); - r = cg_set_attribute("blkio", path, "blkio.weight", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight: %m"); - - if (has_blockio) { - CGroupBlockIODeviceWeight *w; - - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->blockio_device_weights) - cgroup_apply_blkio_device_weight(u, w->path, w->weight); - } else if (has_io) { - CGroupIODeviceWeight *w; - - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->io_device_weights) { - weight = cgroup_weight_io_to_blkio(w->weight); - - log_cgroup_compat(u, "Applying IODeviceWeight %" PRIu64 " as BlockIODeviceWeight %" PRIu64 " for %s", - w->weight, weight, w->path); - - cgroup_apply_blkio_device_weight(u, w->path, weight); - } - } - } - - /* Apply limits and free ones without config. */ - if (has_blockio) { - CGroupBlockIODeviceBandwidth *b, *next; - - LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { - if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps)) - cgroup_context_free_blockio_device_bandwidth(c, b); - } - } else if (has_io) { - CGroupIODeviceLimit *l, *next; - - LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { - log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s", - l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path); - - if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX])) - cgroup_context_free_io_device_limit(c, l); - } - } - } - - if ((mask & CGROUP_MASK_MEMORY) && !is_root) { - if (cg_unified() > 0) { - uint64_t max = c->memory_max; - - if (cgroup_context_has_unified_memory_config(c)) - max = c->memory_max; - else { - max = c->memory_limit; - - if (max != CGROUP_LIMIT_MAX) - log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max); - } - - cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low); - cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); - cgroup_apply_unified_memory_limit(u, "memory.max", max); - } else { - char buf[DECIMAL_STR_MAX(uint64_t) + 1]; - uint64_t val = c->memory_limit; - - if (val == CGROUP_LIMIT_MAX) { - val = c->memory_max; - - if (val != CGROUP_LIMIT_MAX) - log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", c->memory_max); - } - - if (val == CGROUP_LIMIT_MAX) - strncpy(buf, "-1\n", sizeof(buf)); - else - xsprintf(buf, "%" PRIu64 "\n", val); - - r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set memory.limit_in_bytes: %m"); - } - } - - if ((mask & CGROUP_MASK_DEVICES) && !is_root) { - CGroupDeviceAllow *a; - - /* Changing the devices list of a populated cgroup - * might result in EINVAL, hence ignore EINVAL - * here. */ - - if (c->device_allow || c->device_policy != CGROUP_AUTO) - r = cg_set_attribute("devices", path, "devices.deny", "a"); - else - r = cg_set_attribute("devices", path, "devices.allow", "a"); - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to reset devices.list: %m"); - - if (c->device_policy == CGROUP_CLOSED || - (c->device_policy == CGROUP_AUTO && c->device_allow)) { - static const char auto_devices[] = - "/dev/null\0" "rwm\0" - "/dev/zero\0" "rwm\0" - "/dev/full\0" "rwm\0" - "/dev/random\0" "rwm\0" - "/dev/urandom\0" "rwm\0" - "/dev/tty\0" "rwm\0" - "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */ - /* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */ - "/run/systemd/inaccessible/chr\0" "rwm\0" - "/run/systemd/inaccessible/blk\0" "rwm\0"; - - const char *x, *y; - - NULSTR_FOREACH_PAIR(x, y, auto_devices) - whitelist_device(path, x, y); - - whitelist_major(path, "pts", 'c', "rw"); - whitelist_major(path, "kdbus", 'c', "rw"); - whitelist_major(path, "kdbus/*", 'c', "rw"); - } - - LIST_FOREACH(device_allow, a, c->device_allow) { - char acc[4]; - unsigned k = 0; - - if (a->r) - acc[k++] = 'r'; - if (a->w) - acc[k++] = 'w'; - if (a->m) - acc[k++] = 'm'; - - if (k == 0) - continue; - - acc[k++] = 0; - - if (startswith(a->path, "/dev/")) - whitelist_device(path, a->path, acc); - else if (startswith(a->path, "block-")) - whitelist_major(path, a->path + 6, 'b', acc); - else if (startswith(a->path, "char-")) - whitelist_major(path, a->path + 5, 'c', acc); - else - log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path); - } - } - - if ((mask & CGROUP_MASK_PIDS) && !is_root) { - - if (c->tasks_max != (uint64_t) -1) { - char buf[DECIMAL_STR_MAX(uint64_t) + 2]; - - sprintf(buf, "%" PRIu64 "\n", c->tasks_max); - r = cg_set_attribute("pids", path, "pids.max", buf); - } else - r = cg_set_attribute("pids", path, "pids.max", "max"); - - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set pids.max: %m"); - } -} - -CGroupMask cgroup_context_get_mask(CGroupContext *c) { - CGroupMask mask = 0; - - /* Figure out which controllers we need */ - - if (c->cpu_accounting || - c->cpu_shares != CGROUP_CPU_SHARES_INVALID || - c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID || - c->cpu_quota_per_sec_usec != USEC_INFINITY) - mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; - - if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c)) - mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; - - if (c->memory_accounting || - c->memory_limit != CGROUP_LIMIT_MAX || - cgroup_context_has_unified_memory_config(c)) - mask |= CGROUP_MASK_MEMORY; - - if (c->device_allow || - c->device_policy != CGROUP_AUTO) - mask |= CGROUP_MASK_DEVICES; - - if (c->tasks_accounting || - c->tasks_max != (uint64_t) -1) - mask |= CGROUP_MASK_PIDS; - - return mask; -} - -CGroupMask unit_get_own_mask(Unit *u) { - CGroupContext *c; - - /* Returns the mask of controllers the unit needs for itself */ - - c = unit_get_cgroup_context(u); - if (!c) - return 0; - - /* If delegation is turned on, then turn on all cgroups, - * unless we are on the legacy hierarchy and the process we - * fork into it is known to drop privileges, and hence - * shouldn't get access to the controllers. - * - * Note that on the unified hierarchy it is safe to delegate - * controllers to unprivileged services. */ - - if (c->delegate) { - ExecContext *e; - - e = unit_get_exec_context(u); - if (!e || - exec_context_maintains_privileges(e) || - cg_unified() > 0) - return _CGROUP_MASK_ALL; - } - - return cgroup_context_get_mask(c); -} - -CGroupMask unit_get_members_mask(Unit *u) { - assert(u); - - /* Returns the mask of controllers all of the unit's children - * require, merged */ - - if (u->cgroup_members_mask_valid) - return u->cgroup_members_mask; - - u->cgroup_members_mask = 0; - - if (u->type == UNIT_SLICE) { - Unit *member; - Iterator i; - - SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) { - - if (member == u) - continue; - - if (UNIT_DEREF(member->slice) != u) - continue; - - u->cgroup_members_mask |= - unit_get_own_mask(member) | - unit_get_members_mask(member); - } - } - - u->cgroup_members_mask_valid = true; - return u->cgroup_members_mask; -} - -CGroupMask unit_get_siblings_mask(Unit *u) { - assert(u); - - /* Returns the mask of controllers all of the unit's siblings - * require, i.e. the members mask of the unit's parent slice - * if there is one. */ - - if (UNIT_ISSET(u->slice)) - return unit_get_members_mask(UNIT_DEREF(u->slice)); - - return unit_get_own_mask(u) | unit_get_members_mask(u); -} - -CGroupMask unit_get_subtree_mask(Unit *u) { - - /* Returns the mask of this subtree, meaning of the group - * itself and its children. */ - - return unit_get_own_mask(u) | unit_get_members_mask(u); -} - -CGroupMask unit_get_target_mask(Unit *u) { - CGroupMask mask; - - /* This returns the cgroup mask of all controllers to enable - * for a specific cgroup, i.e. everything it needs itself, - * plus all that its children need, plus all that its siblings - * need. This is primarily useful on the legacy cgroup - * hierarchy, where we need to duplicate each cgroup in each - * hierarchy that shall be enabled for it. */ - - mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u); - mask &= u->manager->cgroup_supported; - - return mask; -} - -CGroupMask unit_get_enable_mask(Unit *u) { - CGroupMask mask; - - /* This returns the cgroup mask of all controllers to enable - * for the children of a specific cgroup. This is primarily - * useful for the unified cgroup hierarchy, where each cgroup - * controls which controllers are enabled for its children. */ - - mask = unit_get_members_mask(u); - mask &= u->manager->cgroup_supported; - - return mask; -} - -/* Recurse from a unit up through its containing slices, propagating - * mask bits upward. A unit is also member of itself. */ -void unit_update_cgroup_members_masks(Unit *u) { - CGroupMask m; - bool more; - - assert(u); - - /* Calculate subtree mask */ - m = unit_get_subtree_mask(u); - - /* See if anything changed from the previous invocation. If - * not, we're done. */ - if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask) - return; - - more = - u->cgroup_subtree_mask_valid && - ((m & ~u->cgroup_subtree_mask) != 0) && - ((~m & u->cgroup_subtree_mask) == 0); - - u->cgroup_subtree_mask = m; - u->cgroup_subtree_mask_valid = true; - - if (UNIT_ISSET(u->slice)) { - Unit *s = UNIT_DEREF(u->slice); - - if (more) - /* There's more set now than before. We - * propagate the new mask to the parent's mask - * (not caring if it actually was valid or - * not). */ - - s->cgroup_members_mask |= m; - - else - /* There's less set now than before (or we - * don't know), we need to recalculate - * everything, so let's invalidate the - * parent's members mask */ - - s->cgroup_members_mask_valid = false; - - /* And now make sure that this change also hits our - * grandparents */ - unit_update_cgroup_members_masks(s); - } -} - -static const char *migrate_callback(CGroupMask mask, void *userdata) { - Unit *u = userdata; - - assert(mask != 0); - assert(u); - - while (u) { - if (u->cgroup_path && - u->cgroup_realized && - (u->cgroup_realized_mask & mask) == mask) - return u->cgroup_path; - - u = UNIT_DEREF(u->slice); - } - - return NULL; -} - -char *unit_default_cgroup_path(Unit *u) { - _cleanup_free_ char *escaped = NULL, *slice = NULL; - int r; - - assert(u); - - if (unit_has_name(u, SPECIAL_ROOT_SLICE)) - return strdup(u->manager->cgroup_root); - - if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) { - r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); - if (r < 0) - return NULL; - } - - escaped = cg_escape(u->id); - if (!escaped) - return NULL; - - if (slice) - return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL); - else - return strjoin(u->manager->cgroup_root, "/", escaped, NULL); -} - -int unit_set_cgroup_path(Unit *u, const char *path) { - _cleanup_free_ char *p = NULL; - int r; - - assert(u); - - if (path) { - p = strdup(path); - if (!p) - return -ENOMEM; - } else - p = NULL; - - if (streq_ptr(u->cgroup_path, p)) - return 0; - - if (p) { - r = hashmap_put(u->manager->cgroup_unit, p, u); - if (r < 0) - return r; - } - - unit_release_cgroup(u); - - u->cgroup_path = p; - p = NULL; - - return 1; -} - -int unit_watch_cgroup(Unit *u) { - _cleanup_free_ char *events = NULL; - int r; - - assert(u); - - if (!u->cgroup_path) - return 0; - - if (u->cgroup_inotify_wd >= 0) - return 0; - - /* Only applies to the unified hierarchy */ - r = cg_unified(); - if (r < 0) - return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m"); - if (r == 0) - return 0; - - /* Don't watch the root slice, it's pointless. */ - if (unit_has_name(u, SPECIAL_ROOT_SLICE)) - return 0; - - r = hashmap_ensure_allocated(&u->manager->cgroup_inotify_wd_unit, &trivial_hash_ops); - if (r < 0) - return log_oom(); - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", &events); - if (r < 0) - return log_oom(); - - u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, events, IN_MODIFY); - if (u->cgroup_inotify_wd < 0) { - - if (errno == ENOENT) /* If the directory is already - * gone we don't need to track - * it, so this is not an error */ - return 0; - - return log_unit_error_errno(u, errno, "Failed to add inotify watch descriptor for control group %s: %m", u->cgroup_path); - } - - r = hashmap_put(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd), u); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to add inotify watch descriptor to hash map: %m"); - - return 0; -} - -static int unit_create_cgroup( - Unit *u, - CGroupMask target_mask, - CGroupMask enable_mask) { - - CGroupContext *c; - int r; - - assert(u); - - c = unit_get_cgroup_context(u); - if (!c) - return 0; - - if (!u->cgroup_path) { - _cleanup_free_ char *path = NULL; - - path = unit_default_cgroup_path(u); - if (!path) - return log_oom(); - - r = unit_set_cgroup_path(u, path); - if (r == -EEXIST) - return log_unit_error_errno(u, r, "Control group %s exists already.", path); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path); - } - - /* First, create our own group */ - r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path); - - /* Start watching it */ - (void) unit_watch_cgroup(u); - - /* Enable all controllers we need */ - r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path); - if (r < 0) - log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path); - - /* Keep track that this is now realized */ - u->cgroup_realized = true; - u->cgroup_realized_mask = target_mask; - u->cgroup_enabled_mask = enable_mask; - - if (u->type != UNIT_SLICE && !c->delegate) { - - /* Then, possibly move things over, but not if - * subgroups may contain processes, which is the case - * for slice and delegation units. */ - r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); - if (r < 0) - log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path); - } - - return 0; -} - -int unit_attach_pids_to_cgroup(Unit *u) { - int r; - assert(u); - - r = unit_realize_cgroup(u); - if (r < 0) - return r; - - r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->pids, migrate_callback, u); - if (r < 0) - return r; - - return 0; -} - -static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) { - assert(u); - - return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask; -} - -/* Check if necessary controllers and attributes for a unit are in place. - * - * If so, do nothing. - * If not, create paths, move processes over, and set attributes. - * - * Returns 0 on success and < 0 on failure. */ -static int unit_realize_cgroup_now(Unit *u, ManagerState state) { - CGroupMask target_mask, enable_mask; - int r; - - assert(u); - - if (u->in_cgroup_queue) { - LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); - u->in_cgroup_queue = false; - } - - target_mask = unit_get_target_mask(u); - enable_mask = unit_get_enable_mask(u); - - if (unit_has_mask_realized(u, target_mask, enable_mask)) - return 0; - - /* First, realize parents */ - if (UNIT_ISSET(u->slice)) { - r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state); - if (r < 0) - return r; - } - - /* And then do the real work */ - r = unit_create_cgroup(u, target_mask, enable_mask); - if (r < 0) - return r; - - /* Finally, apply the necessary attributes. */ - cgroup_context_apply(u, target_mask, state); - - return 0; -} - -static void unit_add_to_cgroup_queue(Unit *u) { - - if (u->in_cgroup_queue) - return; - - LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u); - u->in_cgroup_queue = true; -} - -unsigned manager_dispatch_cgroup_queue(Manager *m) { - ManagerState state; - unsigned n = 0; - Unit *i; - int r; - - state = manager_state(m); - - while ((i = m->cgroup_queue)) { - assert(i->in_cgroup_queue); - - r = unit_realize_cgroup_now(i, state); - if (r < 0) - log_warning_errno(r, "Failed to realize cgroups for queued unit %s, ignoring: %m", i->id); - - n++; - } - - return n; -} - -static void unit_queue_siblings(Unit *u) { - Unit *slice; - - /* This adds the siblings of the specified unit and the - * siblings of all parent units to the cgroup queue. (But - * neither the specified unit itself nor the parents.) */ - - while ((slice = UNIT_DEREF(u->slice))) { - Iterator i; - Unit *m; - - SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) { - if (m == u) - continue; - - /* Skip units that have a dependency on the slice - * but aren't actually in it. */ - if (UNIT_DEREF(m->slice) != slice) - continue; - - /* No point in doing cgroup application for units - * without active processes. */ - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m))) - continue; - - /* If the unit doesn't need any new controllers - * and has current ones realized, it doesn't need - * any changes. */ - if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m))) - continue; - - unit_add_to_cgroup_queue(m); - } - - u = slice; - } -} - -int unit_realize_cgroup(Unit *u) { - assert(u); - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return 0; - - /* So, here's the deal: when realizing the cgroups for this - * unit, we need to first create all parents, but there's more - * actually: for the weight-based controllers we also need to - * make sure that all our siblings (i.e. units that are in the - * same slice as we are) have cgroups, too. Otherwise, things - * would become very uneven as each of their processes would - * get as much resources as all our group together. This call - * will synchronously create the parent cgroups, but will - * defer work on the siblings to the next event loop - * iteration. */ - - /* Add all sibling slices to the cgroup queue. */ - unit_queue_siblings(u); - - /* And realize this one now (and apply the values) */ - return unit_realize_cgroup_now(u, manager_state(u->manager)); -} - -void unit_release_cgroup(Unit *u) { - assert(u); - - /* Forgets all cgroup details for this cgroup */ - - if (u->cgroup_path) { - (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); - u->cgroup_path = mfree(u->cgroup_path); - } - - if (u->cgroup_inotify_wd >= 0) { - if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0) - log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id); - - (void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd)); - u->cgroup_inotify_wd = -1; - } -} - -void unit_prune_cgroup(Unit *u) { - int r; - bool is_root_slice; - - assert(u); - - /* Removes the cgroup, if empty and possible, and stops watching it. */ - - if (!u->cgroup_path) - return; - - is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE); - - r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice); - if (r < 0) { - log_unit_debug_errno(u, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path); - return; - } - - if (is_root_slice) - return; - - unit_release_cgroup(u); - - u->cgroup_realized = false; - u->cgroup_realized_mask = 0; - u->cgroup_enabled_mask = 0; -} - -int unit_search_main_pid(Unit *u, pid_t *ret) { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0, npid, mypid; - int r; - - assert(u); - assert(ret); - - if (!u->cgroup_path) - return -ENXIO; - - r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f); - if (r < 0) - return r; - - mypid = getpid(); - while (cg_read_pid(f, &npid) > 0) { - pid_t ppid; - - if (npid == pid) - continue; - - /* Ignore processes that aren't our kids */ - if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid) - continue; - - if (pid != 0) - /* Dang, there's more than one daemonized PID - in this group, so we don't know what process - is the main process. */ - - return -ENODATA; - - pid = npid; - } - - *ret = pid; - return 0; -} - -static int unit_watch_pids_in_path(Unit *u, const char *path) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_fclose_ FILE *f = NULL; - int ret = 0, r; - - assert(u); - assert(path); - - r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); - if (r < 0) - ret = r; - else { - pid_t pid; - - while ((r = cg_read_pid(f, &pid)) > 0) { - r = unit_watch_pid(u, pid); - if (r < 0 && ret >= 0) - ret = r; - } - - if (r < 0 && ret >= 0) - ret = r; - } - - r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); - if (r < 0) { - if (ret >= 0) - ret = r; - } else { - char *fn; - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) - return -ENOMEM; - - r = unit_watch_pids_in_path(u, p); - if (r < 0 && ret >= 0) - ret = r; - } - - if (r < 0 && ret >= 0) - ret = r; - } - - return ret; -} - -int unit_watch_all_pids(Unit *u) { - assert(u); - - /* Adds all PIDs from our cgroup to the set of PIDs we - * watch. This is a fallback logic for cases where we do not - * get reliable cgroup empty notifications: we try to use - * SIGCHLD as replacement. */ - - if (!u->cgroup_path) - return -ENOENT; - - if (cg_unified() > 0) /* On unified we can use proper notifications */ - return 0; - - return unit_watch_pids_in_path(u, u->cgroup_path); -} - -int unit_notify_cgroup_empty(Unit *u) { - int r; - - assert(u); - - if (!u->cgroup_path) - return 0; - - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); - if (r <= 0) - return r; - - unit_add_to_gc_queue(u); - - if (UNIT_VTABLE(u)->notify_cgroup_empty) - UNIT_VTABLE(u)->notify_cgroup_empty(u); - - return 0; -} - -static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(fd >= 0); - assert(m); - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EINTR || errno == EAGAIN) - return 0; - - return log_error_errno(errno, "Failed to read control group inotify events: %m"); - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - Unit *u; - - if (e->wd < 0) - /* Queue overflow has no watch descriptor */ - continue; - - if (e->mask & IN_IGNORED) - /* The watch was just removed */ - continue; - - u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd)); - if (!u) /* Not that inotify might deliver - * events for a watch even after it - * was removed, because it was queued - * before the removal. Let's ignore - * this here safely. */ - continue; - - (void) unit_notify_cgroup_empty(u); - } - } -} - -int manager_setup_cgroup(Manager *m) { - _cleanup_free_ char *path = NULL; - CGroupController c; - int r, unified; - char *e; - - assert(m); - - /* 1. Determine hierarchy */ - m->cgroup_root = mfree(m->cgroup_root); - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); - if (r < 0) - return log_error_errno(r, "Cannot determine cgroup we are running in: %m"); - - /* Chop off the init scope, if we are already located in it */ - e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); - - /* LEGACY: Also chop off the system slice if we are in - * it. This is to support live upgrades from older systemd - * versions where PID 1 was moved there. Also see - * cg_get_root_path(). */ - if (!e && MANAGER_IS_SYSTEM(m)) { - e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); - if (!e) - e = endswith(m->cgroup_root, "/system"); /* even more legacy */ - } - if (e) - *e = 0; - - /* And make sure to store away the root value without trailing - * slash, even for the root dir, so that we can easily prepend - * it everywhere. */ - while ((e = endswith(m->cgroup_root, "/"))) - *e = 0; - - /* 2. Show data */ - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); - if (r < 0) - return log_error_errno(r, "Cannot find cgroup mount point: %m"); - - unified = cg_unified(); - if (unified < 0) - return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m"); - if (unified > 0) - log_debug("Unified cgroup hierarchy is located at %s.", path); - else - log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); - - if (!m->test_run) { - const char *scope_path; - - /* 3. Install agent */ - if (unified) { - - /* In the unified hierarchy we can get - * cgroup empty notifications via inotify. */ - - m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); - safe_close(m->cgroup_inotify_fd); - - m->cgroup_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->cgroup_inotify_fd < 0) - return log_error_errno(errno, "Failed to create control group inotify object: %m"); - - r = sd_event_add_io(m->event, &m->cgroup_inotify_event_source, m->cgroup_inotify_fd, EPOLLIN, on_cgroup_inotify_event, m); - if (r < 0) - return log_error_errno(r, "Failed to watch control group inotify object: %m"); - - /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also - * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ - r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); - if (r < 0) - return log_error_errno(r, "Failed to set priority of inotify event source: %m"); - - (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify"); - - } else if (MANAGER_IS_SYSTEM(m)) { - - /* On the legacy hierarchy we only get - * notifications via cgroup agents. (Which - * isn't really reliable, since it does not - * generate events when control groups with - * children run empty. */ - - r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); - if (r < 0) - log_warning_errno(r, "Failed to install release agent, ignoring: %m"); - else if (r > 0) - log_debug("Installed release agent."); - else if (r == 0) - log_debug("Release agent already installed."); - } - - /* 4. Make sure we are in the special "init.scope" unit in the root slice. */ - scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); - if (r < 0) - return log_error_errno(r, "Failed to create %s control group: %m", scope_path); - - /* also, move all other userspace processes remaining - * in the root cgroup into that scope. */ - r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); - if (r < 0) - log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); - - /* 5. And pin it, so that it cannot be unmounted */ - safe_close(m->pin_cgroupfs_fd); - m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); - if (m->pin_cgroupfs_fd < 0) - return log_error_errno(errno, "Failed to open pin file: %m"); - - /* 6. Always enable hierarchical support if it exists... */ - if (!unified) - (void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); - } - - /* 7. Figure out which controllers are supported */ - r = cg_mask_supported(&m->cgroup_supported); - if (r < 0) - return log_error_errno(r, "Failed to determine supported controllers: %m"); - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) - log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & CGROUP_CONTROLLER_TO_MASK(c))); - - return 0; -} - -void manager_shutdown_cgroup(Manager *m, bool delete) { - assert(m); - - /* We can't really delete the group, since we are in it. But - * let's trim it. */ - if (delete && m->cgroup_root) - (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); - - m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit); - - m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); - m->cgroup_inotify_fd = safe_close(m->cgroup_inotify_fd); - - m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd); - - m->cgroup_root = mfree(m->cgroup_root); -} - -Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { - char *p; - Unit *u; - - assert(m); - assert(cgroup); - - u = hashmap_get(m->cgroup_unit, cgroup); - if (u) - return u; - - p = strdupa(cgroup); - for (;;) { - char *e; - - e = strrchr(p, '/'); - if (!e || e == p) - return hashmap_get(m->cgroup_unit, SPECIAL_ROOT_SLICE); - - *e = 0; - - u = hashmap_get(m->cgroup_unit, p); - if (u) - return u; - } -} - -Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(m); - - if (pid <= 0) - return NULL; - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); - if (r < 0) - return NULL; - - return manager_get_unit_by_cgroup(m, cgroup); -} - -Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { - Unit *u; - - assert(m); - - if (pid <= 0) - return NULL; - - if (pid == 1) - return hashmap_get(m->units, SPECIAL_INIT_SCOPE); - - u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid)); - if (u) - return u; - - u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid)); - if (u) - return u; - - return manager_get_unit_by_pid_cgroup(m, pid); -} - -int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { - Unit *u; - - assert(m); - assert(cgroup); - - log_debug("Got cgroup empty notification for: %s", cgroup); - - u = manager_get_unit_by_cgroup(m, cgroup); - if (!u) - return 0; - - return unit_notify_cgroup_empty(u); -} - -int unit_get_memory_current(Unit *u, uint64_t *ret) { - _cleanup_free_ char *v = NULL; - int r; - - assert(u); - assert(ret); - - if (!u->cgroup_path) - return -ENODATA; - - if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) - return -ENODATA; - - if (cg_unified() <= 0) - r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); - else - r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - - return safe_atou64(v, ret); -} - -int unit_get_tasks_current(Unit *u, uint64_t *ret) { - _cleanup_free_ char *v = NULL; - int r; - - assert(u); - assert(ret); - - if (!u->cgroup_path) - return -ENODATA; - - if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) - return -ENODATA; - - r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - - return safe_atou64(v, ret); -} - -static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { - _cleanup_free_ char *v = NULL; - uint64_t ns; - int r; - - assert(u); - assert(ret); - - if (!u->cgroup_path) - return -ENODATA; - - if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0) - return -ENODATA; - - r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - - r = safe_atou64(v, &ns); - if (r < 0) - return r; - - *ret = ns; - return 0; -} - -int unit_get_cpu_usage(Unit *u, nsec_t *ret) { - nsec_t ns; - int r; - - r = unit_get_cpu_usage_raw(u, &ns); - if (r < 0) - return r; - - if (ns > u->cpuacct_usage_base) - ns -= u->cpuacct_usage_base; - else - ns = 0; - - *ret = ns; - return 0; -} - -int unit_reset_cpu_usage(Unit *u) { - nsec_t ns; - int r; - - assert(u); - - r = unit_get_cpu_usage_raw(u, &ns); - if (r < 0) { - u->cpuacct_usage_base = 0; - return r; - } - - u->cpuacct_usage_base = ns; - return 0; -} - -bool unit_cgroup_delegate(Unit *u) { - CGroupContext *c; - - assert(u); - - c = unit_get_cgroup_context(u); - if (!c) - return false; - - return c->delegate; -} - -void unit_invalidate_cgroup(Unit *u, CGroupMask m) { - assert(u); - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return; - - if (m == 0) - return; - - /* always invalidate compat pairs together */ - if (m & (CGROUP_MASK_IO | CGROUP_MASK_BLKIO)) - m |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; - - if ((u->cgroup_realized_mask & m) == 0) - return; - - u->cgroup_realized_mask &= ~m; - unit_add_to_cgroup_queue(u); -} - -void manager_invalidate_startup_units(Manager *m) { - Iterator i; - Unit *u; - - assert(m); - - SET_FOREACH(u, m->startup_units, i) - unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO); -} - -static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { - [CGROUP_AUTO] = "auto", - [CGROUP_CLOSED] = "closed", - [CGROUP_STRICT] = "strict", -}; - -DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy); diff --git a/src/core/cgroup.h b/src/core/cgroup.h deleted file mode 100644 index a57403e79f..0000000000 --- a/src/core/cgroup.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "list.h" -#include "time-util.h" -#include "cgroup-util.h" - -typedef struct CGroupContext CGroupContext; -typedef struct CGroupDeviceAllow CGroupDeviceAllow; -typedef struct CGroupIODeviceWeight CGroupIODeviceWeight; -typedef struct CGroupIODeviceLimit CGroupIODeviceLimit; -typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; -typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth; - -typedef enum CGroupDevicePolicy { - - /* When devices listed, will allow those, plus built-in ones, - if none are listed will allow everything. */ - CGROUP_AUTO, - - /* Everything forbidden, except built-in ones and listed ones. */ - CGROUP_CLOSED, - - /* Everythings forbidden, except for the listed devices */ - CGROUP_STRICT, - - _CGROUP_DEVICE_POLICY_MAX, - _CGROUP_DEVICE_POLICY_INVALID = -1 -} CGroupDevicePolicy; - -struct CGroupDeviceAllow { - LIST_FIELDS(CGroupDeviceAllow, device_allow); - char *path; - bool r:1; - bool w:1; - bool m:1; -}; - -struct CGroupIODeviceWeight { - LIST_FIELDS(CGroupIODeviceWeight, device_weights); - char *path; - uint64_t weight; -}; - -struct CGroupIODeviceLimit { - LIST_FIELDS(CGroupIODeviceLimit, device_limits); - char *path; - uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; -}; - -struct CGroupBlockIODeviceWeight { - LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); - char *path; - uint64_t weight; -}; - -struct CGroupBlockIODeviceBandwidth { - LIST_FIELDS(CGroupBlockIODeviceBandwidth, device_bandwidths); - char *path; - uint64_t rbps; - uint64_t wbps; -}; - -struct CGroupContext { - bool cpu_accounting; - bool io_accounting; - bool blockio_accounting; - bool memory_accounting; - bool tasks_accounting; - - /* For unified hierarchy */ - uint64_t io_weight; - uint64_t startup_io_weight; - LIST_HEAD(CGroupIODeviceWeight, io_device_weights); - LIST_HEAD(CGroupIODeviceLimit, io_device_limits); - - uint64_t memory_low; - uint64_t memory_high; - uint64_t memory_max; - - /* For legacy hierarchies */ - uint64_t cpu_shares; - uint64_t startup_cpu_shares; - usec_t cpu_quota_per_sec_usec; - - uint64_t blockio_weight; - uint64_t startup_blockio_weight; - LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights); - LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths); - - uint64_t memory_limit; - - CGroupDevicePolicy device_policy; - LIST_HEAD(CGroupDeviceAllow, device_allow); - - /* Common */ - uint64_t tasks_max; - - bool delegate; -}; - -#include "unit.h" - -void cgroup_context_init(CGroupContext *c); -void cgroup_context_done(CGroupContext *c); -void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); - -CGroupMask cgroup_context_get_mask(CGroupContext *c); - -void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a); -void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w); -void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l); -void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); -void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); - -CGroupMask unit_get_own_mask(Unit *u); -CGroupMask unit_get_siblings_mask(Unit *u); -CGroupMask unit_get_members_mask(Unit *u); -CGroupMask unit_get_subtree_mask(Unit *u); - -CGroupMask unit_get_target_mask(Unit *u); -CGroupMask unit_get_enable_mask(Unit *u); - -void unit_update_cgroup_members_masks(Unit *u); - -char *unit_default_cgroup_path(Unit *u); -int unit_set_cgroup_path(Unit *u, const char *path); - -int unit_realize_cgroup(Unit *u); -void unit_release_cgroup(Unit *u); -void unit_prune_cgroup(Unit *u); -int unit_watch_cgroup(Unit *u); - -int unit_attach_pids_to_cgroup(Unit *u); - -int manager_setup_cgroup(Manager *m); -void manager_shutdown_cgroup(Manager *m, bool delete); - -unsigned manager_dispatch_cgroup_queue(Manager *m); - -Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); -Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); -Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); - -int unit_search_main_pid(Unit *u, pid_t *ret); -int unit_watch_all_pids(Unit *u); - -int unit_get_memory_current(Unit *u, uint64_t *ret); -int unit_get_tasks_current(Unit *u, uint64_t *ret); -int unit_get_cpu_usage(Unit *u, nsec_t *ret); -int unit_reset_cpu_usage(Unit *u); - -bool unit_cgroup_delegate(Unit *u); - -int unit_notify_cgroup_empty(Unit *u); -int manager_notify_cgroup_empty(Manager *m, const char *group); - -void unit_invalidate_cgroup(Unit *u, CGroupMask m); - -void manager_invalidate_startup_units(Manager *m); - -const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; -CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c deleted file mode 100644 index b2806ad86f..0000000000 --- a/src/core/dbus-automount.c +++ /dev/null @@ -1,34 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - -const sd_bus_vtable bus_automount_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Automount, where), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Automount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Automount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h deleted file mode 100644 index 7b51eb973a..0000000000 --- a/src/core/dbus-automount.h +++ /dev/null @@ -1,23 +0,0 @@ -#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 . -***/ - - -extern const sd_bus_vtable bus_automount_vtable[]; diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c deleted file mode 100644 index cf816ba15b..0000000000 --- a/src/core/dbus-busname.c +++ /dev/null @@ -1,37 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-util.h" -#include "busname.h" -#include "dbus-busname.h" -#include "string-util.h" -#include "unit.h" - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult); - -const sd_bus_vtable bus_busname_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(BusName, name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(BusName, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(BusName, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(BusName, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Activating", "b", bus_property_get_bool, offsetof(BusName, activating), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("AcceptFileDescriptors", "b", bus_property_get_bool, offsetof(BusName, accept_fd), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-busname.h b/src/core/dbus-busname.h deleted file mode 100644 index 8643d1a404..0000000000 --- a/src/core/dbus-busname.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -extern const sd_bus_vtable bus_busname_vtable[]; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c deleted file mode 100644 index 85b0c86a2f..0000000000 --- a/src/core/dbus-cgroup.c +++ /dev/null @@ -1,1109 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-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); - -static int property_get_io_device_weight( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - CGroupContext *c = userdata; - CGroupIODeviceWeight *w; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; - - LIST_FOREACH(device_weights, w, c->io_device_weights) { - r = sd_bus_message_append(reply, "(st)", w->path, w->weight); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_io_device_limits( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - CGroupContext *c = userdata; - CGroupIODeviceLimit *l; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; - - LIST_FOREACH(device_limits, l, c->io_device_limits) { - CGroupIOLimitType type; - - type = cgroup_io_limit_type_from_string(property); - if (type < 0 || l->limits[type] == cgroup_io_limit_defaults[type]) - continue; - - r = sd_bus_message_append(reply, "(st)", l->path, l->limits[type]); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_blockio_device_weight( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - CGroupContext *c = userdata; - CGroupBlockIODeviceWeight *w; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; - - LIST_FOREACH(device_weights, w, c->blockio_device_weights) { - r = sd_bus_message_append(reply, "(st)", w->path, w->weight); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_blockio_device_bandwidths( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - CGroupContext *c = userdata; - CGroupBlockIODeviceBandwidth *b; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; - - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - uint64_t v; - - if (streq(property, "BlockIOReadBandwidth")) - v = b->rbps; - else - v = b->wbps; - - if (v == CGROUP_LIMIT_MAX) - continue; - - r = sd_bus_message_append(reply, "(st)", b->path, v); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_device_allow( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - CGroupContext *c = userdata; - CGroupDeviceAllow *a; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(ss)"); - if (r < 0) - return r; - - LIST_FOREACH(device_allow, a, c->device_allow) { - unsigned k = 0; - char rwm[4]; - - if (a->r) - rwm[k++] = 'r'; - if (a->w) - rwm[k++] = 'w'; - if (a->m) - rwm[k++] = 'm'; - - rwm[k] = 0; - - r = sd_bus_message_append(reply, "(ss)", a->path, rwm); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -const sd_bus_vtable bus_cgroup_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), - SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), - SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), - SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), - SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), - SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0), - SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0), - SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0), - SD_BUS_PROPERTY("IODeviceWeight", "a(st)", property_get_io_device_weight, 0, 0), - SD_BUS_PROPERTY("IOReadBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), - SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), - SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), - SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), - SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), - SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), - SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), - SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0), - SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), - SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), - SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), - SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), - SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), - SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0), - SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), - SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), - SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), - SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0), - SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0), - SD_BUS_VTABLE_END -}; - -static int bus_cgroup_set_transient_property( - Unit *u, - CGroupContext *c, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(u); - assert(c); - assert(name); - assert(message); - - if (streq(name, "Delegate")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->delegate = b; - unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no"); - } - - return 1; - } - - return 0; -} - -int bus_cgroup_set_property( - Unit *u, - CGroupContext *c, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - CGroupIOLimitType iol_type; - int r; - - assert(u); - assert(c); - assert(name); - assert(message); - - if (streq(name, "CPUAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->cpu_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU); - unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); - } - - return 1; - - } else if (streq(name, "CPUShares")) { - uint64_t shares; - - r = sd_bus_message_read(message, "t", &shares); - if (r < 0) - return r; - - if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); - - if (mode != UNIT_CHECK) { - c->cpu_shares = shares; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - - if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_drop_in_private(u, mode, name, "CPUShares="); - else - unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares); - } - - return 1; - - } else if (streq(name, "StartupCPUShares")) { - uint64_t shares; - - r = sd_bus_message_read(message, "t", &shares); - if (r < 0) - return r; - - if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); - - if (mode != UNIT_CHECK) { - c->startup_cpu_shares = shares; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - - if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_drop_in_private(u, mode, name, "StartupCPUShares="); - else - unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares); - } - - return 1; - - } else if (streq(name, "CPUQuotaPerSecUSec")) { - uint64_t u64; - - r = sd_bus_message_read(message, "t", &u64); - if (r < 0) - return r; - - if (u64 <= 0) - return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range"); - - if (mode != UNIT_CHECK) { - c->cpu_quota_per_sec_usec = u64; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); - } - - return 1; - - } else if (streq(name, "IOAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->io_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - unit_write_drop_in_private(u, mode, name, b ? "IOAccounting=yes" : "IOAccounting=no"); - } - - return 1; - - } else if (streq(name, "IOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_set_errnof(error, EINVAL, "IOWeight value out of range"); - - if (mode != UNIT_CHECK) { - c->io_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_drop_in_private(u, mode, name, "IOWeight="); - else - unit_write_drop_in_private_format(u, mode, name, "IOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "StartupIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_set_errnof(error, EINVAL, "StartupIOWeight value out of range"); - - if (mode != UNIT_CHECK) { - c->startup_io_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_drop_in_private(u, mode, name, "StartupIOWeight="); - else - unit_write_drop_in_private_format(u, mode, name, "StartupIOWeight=%" PRIu64, weight); - } - - return 1; - - } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) { - const char *path; - unsigned n = 0; - uint64_t u64; - - r = sd_bus_message_enter_container(message, 'a', "(st)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - - if (mode != UNIT_CHECK) { - CGroupIODeviceLimit *a = NULL, *b; - - LIST_FOREACH(device_limits, b, c->io_device_limits) { - if (path_equal(path, b->path)) { - a = b; - break; - } - } - - if (!a) { - CGroupIOLimitType type; - - a = new0(CGroupIODeviceLimit, 1); - if (!a) - return -ENOMEM; - - a->path = strdup(path); - if (!a->path) { - free(a); - return -ENOMEM; - } - - for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) - a->limits[type] = cgroup_io_limit_defaults[type]; - - LIST_PREPEND(device_limits, c->io_device_limits, a); - } - - a->limits[iol_type] = u64; - } - - n++; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - CGroupIODeviceLimit *a; - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - size_t size = 0; - - if (n == 0) { - LIST_FOREACH(device_limits, a, c->io_device_limits) - a->limits[iol_type] = cgroup_io_limit_defaults[iol_type]; - } - - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - fprintf(f, "%s=\n", name); - LIST_FOREACH(device_limits, a, c->io_device_limits) - if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type]) - fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]); - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(u, mode, name, buf); - } - - return 1; - - } else if (streq(name, "IODeviceWeight")) { - const char *path; - uint64_t weight; - unsigned n = 0; - - r = sd_bus_message_enter_container(message, 'a', "(st)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - - if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID) - return sd_bus_error_set_errnof(error, EINVAL, "IODeviceWeight out of range"); - - if (mode != UNIT_CHECK) { - CGroupIODeviceWeight *a = NULL, *b; - - LIST_FOREACH(device_weights, b, c->io_device_weights) { - if (path_equal(b->path, path)) { - a = b; - break; - } - } - - if (!a) { - a = new0(CGroupIODeviceWeight, 1); - if (!a) - return -ENOMEM; - - a->path = strdup(path); - if (!a->path) { - free(a); - return -ENOMEM; - } - LIST_PREPEND(device_weights,c->io_device_weights, a); - } - - a->weight = weight; - } - - n++; - } - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - CGroupIODeviceWeight *a; - size_t size = 0; - - if (n == 0) { - while (c->io_device_weights) - cgroup_context_free_io_device_weight(c, c->io_device_weights); - } - - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - fputs("IODeviceWeight=\n", f); - LIST_FOREACH(device_weights, a, c->io_device_weights) - fprintf(f, "IODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(u, mode, name, buf); - } - - return 1; - - } else if (streq(name, "BlockIOAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->blockio_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); - } - - return 1; - - } else if (streq(name, "BlockIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); - - if (mode != UNIT_CHECK) { - c->blockio_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_drop_in_private(u, mode, name, "BlockIOWeight="); - else - unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "StartupBlockIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); - - if (mode != UNIT_CHECK) { - c->startup_blockio_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight="); - else - unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - const char *path; - bool read = true; - unsigned n = 0; - uint64_t u64; - - if (streq(name, "BlockIOWriteBandwidth")) - read = false; - - r = sd_bus_message_enter_container(message, 'a', "(st)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - - if (mode != UNIT_CHECK) { - CGroupBlockIODeviceBandwidth *a = NULL, *b; - - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - if (path_equal(path, b->path)) { - a = b; - break; - } - } - - if (!a) { - a = new0(CGroupBlockIODeviceBandwidth, 1); - if (!a) - return -ENOMEM; - - a->rbps = CGROUP_LIMIT_MAX; - a->wbps = CGROUP_LIMIT_MAX; - a->path = strdup(path); - if (!a->path) { - free(a); - return -ENOMEM; - } - - LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); - } - - if (read) - a->rbps = u64; - else - a->wbps = u64; - } - - n++; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - CGroupBlockIODeviceBandwidth *a; - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - size_t size = 0; - - if (n == 0) { - LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) { - if (read) - a->rbps = CGROUP_LIMIT_MAX; - else - a->wbps = CGROUP_LIMIT_MAX; - } - } - - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - if (read) { - fputs("BlockIOReadBandwidth=\n", f); - LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) - if (a->rbps != CGROUP_LIMIT_MAX) - fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->rbps); - } else { - fputs("BlockIOWriteBandwidth=\n", f); - LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) - if (a->wbps != CGROUP_LIMIT_MAX) - fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->wbps); - } - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(u, mode, name, buf); - } - - return 1; - - } else if (streq(name, "BlockIODeviceWeight")) { - const char *path; - uint64_t weight; - unsigned n = 0; - - r = sd_bus_message_enter_container(message, 'a', "(st)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) - return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range"); - - if (mode != UNIT_CHECK) { - CGroupBlockIODeviceWeight *a = NULL, *b; - - LIST_FOREACH(device_weights, b, c->blockio_device_weights) { - if (path_equal(b->path, path)) { - a = b; - break; - } - } - - if (!a) { - a = new0(CGroupBlockIODeviceWeight, 1); - if (!a) - return -ENOMEM; - - a->path = strdup(path); - if (!a->path) { - free(a); - return -ENOMEM; - } - LIST_PREPEND(device_weights,c->blockio_device_weights, a); - } - - a->weight = weight; - } - - n++; - } - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - CGroupBlockIODeviceWeight *a; - size_t size = 0; - - if (n == 0) { - while (c->blockio_device_weights) - cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); - } - - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - fputs("BlockIODeviceWeight=\n", f); - LIST_FOREACH(device_weights, a, c->blockio_device_weights) - fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(u, mode, name, buf); - } - - return 1; - - } else if (streq(name, "MemoryAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->memory_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); - } - - return 1; - - } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax")) { - uint64_t v; - - r = sd_bus_message_read(message, "t", &v); - if (r < 0) - return r; - if (v <= 0) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); - - if (mode != UNIT_CHECK) { - if (streq(name, "MemoryLow")) - c->memory_low = v; - else if (streq(name, "MemoryHigh")) - c->memory_high = v; - else - c->memory_max = v; - - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - - if (v == CGROUP_LIMIT_MAX) - unit_write_drop_in_private_format(u, mode, name, "%s=infinity", name); - else - unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, v); - } - - return 1; - - } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale")) { - uint32_t raw; - uint64_t v; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - v = physical_memory_scale(raw, UINT32_MAX); - if (v <= 0 || v == UINT64_MAX) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); - - if (mode != UNIT_CHECK) { - const char *e; - - /* Chop off suffix */ - assert_se(e = endswith(name, "Scale")); - name = strndupa(name, e - name); - - if (streq(name, "MemoryLow")) - c->memory_low = v; - else if (streq(name, "MemoryHigh")) - c->memory_high = v; - else - c->memory_max = v; - - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu32 "%%", name, - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - - } else if (streq(name, "MemoryLimit")) { - uint64_t limit; - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (limit <= 0) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); - - if (mode != UNIT_CHECK) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - - if (limit == (uint64_t) -1) - unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity"); - else - unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit); - } - - return 1; - - } else if (streq(name, "MemoryLimitScale")) { - uint64_t limit; - uint32_t raw; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - limit = physical_memory_scale(raw, UINT32_MAX); - if (limit <= 0 || limit == UINT64_MAX) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); - - if (mode != UNIT_CHECK) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_drop_in_private_format(u, mode, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - - } else if (streq(name, "DevicePolicy")) { - const char *policy; - CGroupDevicePolicy p; - - r = sd_bus_message_read(message, "s", &policy); - if (r < 0) - return r; - - p = cgroup_device_policy_from_string(policy); - if (p < 0) - return -EINVAL; - - if (mode != UNIT_CHECK) { - c->device_policy = p; - unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); - unit_write_drop_in_private_format(u, mode, name, "DevicePolicy=%s", policy); - } - - return 1; - - } else if (streq(name, "DeviceAllow")) { - const char *path, *rwm; - unsigned n = 0; - - r = sd_bus_message_enter_container(message, 'a', "(ss)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { - - if ((!startswith(path, "/dev/") && - !startswith(path, "/run/systemd/inaccessible/") && - !startswith(path, "block-") && - !startswith(path, "char-")) || - strpbrk(path, WHITESPACE)) - return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node"); - - if (isempty(rwm)) - rwm = "rwm"; - - if (!in_charset(rwm, "rwm")) - return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags"); - - if (mode != UNIT_CHECK) { - CGroupDeviceAllow *a = NULL, *b; - - LIST_FOREACH(device_allow, b, c->device_allow) { - if (path_equal(b->path, path)) { - a = b; - break; - } - } - - if (!a) { - a = new0(CGroupDeviceAllow, 1); - if (!a) - return -ENOMEM; - - a->path = strdup(path); - if (!a->path) { - free(a); - return -ENOMEM; - } - - LIST_PREPEND(device_allow, c->device_allow, a); - } - - a->r = !!strchr(rwm, 'r'); - a->w = !!strchr(rwm, 'w'); - a->m = !!strchr(rwm, 'm'); - } - - n++; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - CGroupDeviceAllow *a; - size_t size = 0; - - if (n == 0) { - while (c->device_allow) - cgroup_context_free_device_allow(c, c->device_allow); - } - - unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - fputs("DeviceAllow=\n", f); - 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" : ""); - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(u, mode, name, buf); - } - - return 1; - - } else if (streq(name, "TasksAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->tasks_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no"); - } - - return 1; - - } else if (streq(name, "TasksMax")) { - uint64_t limit; - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (limit <= 0) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); - - if (mode != UNIT_CHECK) { - c->tasks_max = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - - if (limit == (uint64_t) -1) - unit_write_drop_in_private(u, mode, name, "TasksMax=infinity"); - else - unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit); - } - - return 1; - } else if (streq(name, "TasksMaxScale")) { - uint64_t limit; - uint32_t raw; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - limit = system_tasks_max_scale(raw, UINT32_MAX); - if (limit <= 0 || limit >= UINT64_MAX) - return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); - - if (mode != UNIT_CHECK) { - c->tasks_max = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu32 "%%", - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - } - - if (u->transient && u->load_state == UNIT_STUB) { - r = bus_cgroup_set_transient_property(u, c, name, message, mode, error); - if (r != 0) - return r; - - } - - return 0; -} diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h deleted file mode 100644 index b2212fe44e..0000000000 --- a/src/core/dbus-cgroup.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "cgroup.h" - -extern const sd_bus_vtable bus_cgroup_vtable[]; - -int bus_cgroup_set_property(Unit *u, CGroupContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c deleted file mode 100644 index e1a12224d3..0000000000 --- a/src/core/dbus-device.c +++ /dev/null @@ -1,28 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "dbus-device.h" -#include "device.h" -#include "unit.h" - -const sd_bus_vtable bus_device_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("SysFSPath", "s", NULL, offsetof(Device, sysfs), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-device.h b/src/core/dbus-device.h deleted file mode 100644 index eb1d8c3278..0000000000 --- a/src/core/dbus-device.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - -#include "unit.h" - -extern const sd_bus_vtable bus_device_vtable[]; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c deleted file mode 100644 index 307c3d8e7a..0000000000 --- a/src/core/dbus-execute.c +++ /dev/null @@ -1,1554 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#ifdef HAVE_SECCOMP -#include -#endif - -#include "af-list.h" -#include "alloc-util.h" -#include "bus-util.h" -#include "capability-util.h" -#include "dbus-execute.h" -#include "env-util.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 "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); - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); - -static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); -static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); - -static int property_get_environment_files( - 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; - char **j; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'a', "(sb)"); - if (r < 0) - return r; - - STRV_FOREACH(j, c->environment_files) { - const char *fn = *j; - - r = sd_bus_message_append(reply, "(sb)", fn[0] == '-' ? fn + 1 : fn, fn[0] == '-'); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_oom_score_adjust( - 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; - int32_t n; - - assert(bus); - assert(reply); - assert(c); - - if (c->oom_score_adjust_set) - n = c->oom_score_adjust; - else { - _cleanup_free_ char *t = NULL; - - n = 0; - if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) - safe_atoi32(t, &n); - } - - return sd_bus_message_append(reply, "i", n); -} - -static int property_get_nice( - 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; - int32_t n; - - assert(bus); - assert(reply); - assert(c); - - if (c->nice_set) - n = c->nice; - else { - errno = 0; - n = getpriority(PRIO_PROCESS, 0); - if (errno > 0) - n = 0; - } - - return sd_bus_message_append(reply, "i", n); -} - -static int property_get_ioprio( - 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; - int32_t n; - - assert(bus); - assert(reply); - assert(c); - - if (c->ioprio_set) - n = c->ioprio; - else { - n = ioprio_get(IOPRIO_WHO_PROCESS, 0); - if (n < 0) - n = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4); - } - - return sd_bus_message_append(reply, "i", n); -} - -static int property_get_cpu_sched_policy( - 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; - int32_t n; - - assert(bus); - assert(reply); - assert(c); - - if (c->cpu_sched_set) - n = c->cpu_sched_policy; - else { - n = sched_getscheduler(0); - if (n < 0) - n = SCHED_OTHER; - } - - return sd_bus_message_append(reply, "i", n); -} - -static int property_get_cpu_sched_priority( - 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; - int32_t n; - - assert(bus); - assert(reply); - assert(c); - - if (c->cpu_sched_set) - n = c->cpu_sched_priority; - else { - struct sched_param p = {}; - - if (sched_getparam(0, &p) >= 0) - n = p.sched_priority; - else - n = 0; - } - - return sd_bus_message_append(reply, "i", n); -} - -static int property_get_cpu_affinity( - 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); - - if (c->cpuset) - return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); - else - return sd_bus_message_append_array(reply, 'y', NULL, 0); -} - -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) { - - ExecContext *c = userdata; - uint64_t u; - - assert(bus); - assert(reply); - assert(c); - - if (c->timer_slack_nsec != NSEC_INFINITY) - u = (uint64_t) c->timer_slack_nsec; - else - u = (uint64_t) prctl(PR_GET_TIMERSLACK); - - return sd_bus_message_append(reply, "t", u); -} - -static int property_get_capability_bounding_set( - 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_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_empty_string( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", ""); -} - -static int property_get_syscall_filter( - 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; - _cleanup_strv_free_ char **l = NULL; - int r; - -#ifdef HAVE_SECCOMP - Iterator i; - void *id; -#endif - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'r', "bas"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "b", c->syscall_whitelist); - if (r < 0) - return r; - -#ifdef HAVE_SECCOMP - SET_FOREACH(id, c->syscall_filter, i) { - char *name; - - name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); - if (!name) - continue; - - r = strv_consume(&l, name); - if (r < 0) - return r; - } -#endif - - strv_sort(l); - - r = sd_bus_message_append_strv(reply, l); - if (r < 0) - return r; - - return sd_bus_message_close_container(reply); -} - -static int property_get_syscall_archs( - 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; - _cleanup_strv_free_ char **l = NULL; - int r; - -#ifdef HAVE_SECCOMP - Iterator i; - void *id; -#endif - - assert(bus); - assert(reply); - assert(c); - -#ifdef HAVE_SECCOMP - SET_FOREACH(id, c->syscall_archs, i) { - const char *name; - - name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1); - if (!name) - continue; - - r = strv_extend(&l, name); - if (r < 0) - return -ENOMEM; - } -#endif - - strv_sort(l); - - r = sd_bus_message_append_strv(reply, l); - if (r < 0) - return r; - - return 0; -} - -static int property_get_syscall_errno( - 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", (int32_t) c->syscall_errno); -} - -static int property_get_selinux_context( - 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, "(bs)", c->selinux_context_ignore, c->selinux_context); -} - -static int property_get_apparmor_profile( - 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, "(bs)", c->apparmor_profile_ignore, c->apparmor_profile); -} - -static int property_get_smack_process_label( - 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, "(bs)", c->smack_process_label_ignore, c->smack_process_label); -} - -static int property_get_personality( - 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, "s", personality_to_string(c->personality)); -} - -static int property_get_address_families( - 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; - _cleanup_strv_free_ char **l = NULL; - Iterator i; - void *af; - int r; - - assert(bus); - assert(reply); - assert(c); - - r = sd_bus_message_open_container(reply, 'r', "bas"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "b", c->address_families_whitelist); - if (r < 0) - return r; - - SET_FOREACH(af, c->address_families, i) { - const char *name; - - name = af_to_name(PTR_TO_INT(af)); - if (!name) - continue; - - r = strv_extend(&l, name); - if (r < 0) - return -ENOMEM; - } - - strv_sort(l); - - r = sd_bus_message_append_strv(reply, l); - if (r < 0) - return r; - - 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", 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), - SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTYVTDisallocate", "b", bus_property_get_bool, offsetof(ExecContext, tty_vt_disallocate), SD_BUS_VTABLE_PROPERTY_CONST), - 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_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - 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), - SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ReadOnlyPaths", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Personality", "s", property_get_personality, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, runtime_directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END -}; - -static int append_exec_command(sd_bus_message *reply, ExecCommand *c) { - int r; - - assert(reply); - assert(c); - - if (!c->path) - return 0; - - r = sd_bus_message_open_container(reply, 'r', "sasbttttuii"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "s", c->path); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(reply, c->argv); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "bttttuii", - c->ignore, - c->exec_status.start_timestamp.realtime, - c->exec_status.start_timestamp.monotonic, - c->exec_status.exit_timestamp.realtime, - c->exec_status.exit_timestamp.monotonic, - (uint32_t) c->exec_status.pid, - (int32_t) c->exec_status.code, - (int32_t) c->exec_status.status); - if (r < 0) - return r; - - return sd_bus_message_close_container(reply); -} - -int bus_property_get_exec_command( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) { - - ExecCommand *c = (ExecCommand*) userdata; - int r; - - assert(bus); - assert(reply); - - r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)"); - if (r < 0) - return r; - - r = append_exec_command(reply, c); - if (r < 0) - return r; - - return sd_bus_message_close_container(reply); -} - -int bus_property_get_exec_command_list( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *ret_error) { - - ExecCommand *c = *(ExecCommand**) userdata; - int r; - - assert(bus); - assert(reply); - - r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)"); - if (r < 0) - return r; - - LIST_FOREACH(command, c, c) { - r = append_exec_command(reply, c); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -int bus_exec_context_set_transient_property( - Unit *u, - ExecContext *c, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - const char *soft = NULL; - int r, ri; - - assert(u); - assert(c); - assert(name); - assert(message); - - if (streq(name, "User")) { - const char *uu; - - r = sd_bus_message_read(message, "s", &uu); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - - if (isempty(uu)) - c->user = mfree(c->user); - else if (free_and_strdup(&c->user, uu) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "User=%s", uu); - } - - return 1; - - } else if (streq(name, "Group")) { - const char *gg; - - r = sd_bus_message_read(message, "s", &gg); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - - if (isempty(gg)) - c->group = mfree(c->group); - else if (free_and_strdup(&c->group, gg) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "Group=%s", gg); - } - - 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 if (free_and_strdup(&c->syslog_identifier, id) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s", 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", 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", facility); - } - - return 1; - } else if (streq(name, "Nice")) { - int n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (n < PRIO_MIN || n >= PRIO_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); - - if (mode != UNIT_CHECK) { - c->nice = n; - unit_write_drop_in_private_format(u, mode, name, "Nice=%i", n); - } - - return 1; - - } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) { - const char *s; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - if (!path_is_absolute(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); - - if (mode != UNIT_CHECK) { - 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; - - unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s); - } - - 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; - } - - c->working_directory_missing_ok = missing_ok; - unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s); - } - - return 1; - - } else if (streq(name, "StandardInput")) { - const char *s; - ExecInput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_input_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard input name"); - - if (mode != UNIT_CHECK) { - c->std_input = p; - - unit_write_drop_in_private_format(u, mode, name, "StandardInput=%s", exec_input_to_string(p)); - } - - return 1; - - - } else if (streq(name, "StandardOutput")) { - const char *s; - ExecOutput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_output_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard output name"); - - if (mode != UNIT_CHECK) { - c->std_output = p; - - unit_write_drop_in_private_format(u, mode, name, "StandardOutput=%s", exec_output_to_string(p)); - } - - return 1; - - } else if (streq(name, "StandardError")) { - const char *s; - ExecOutput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_output_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard error name"); - - if (mode != UNIT_CHECK) { - c->std_error = p; - - unit_write_drop_in_private_format(u, mode, name, "StandardError=%s", exec_output_to_string(p)); - } - - return 1; - - } else if (STR_IN_SET(name, - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", - "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - if (streq(name, "IgnoreSIGPIPE")) - c->ignore_sigpipe = b; - else if (streq(name, "TTYVHangup")) - c->tty_vhangup = b; - else if (streq(name, "TTYReset")) - c->tty_reset = b; - else if (streq(name, "PrivateTmp")) - c->private_tmp = b; - else if (streq(name, "PrivateDevices")) - c->private_devices = b; - else if (streq(name, "PrivateNetwork")) - c->private_network = b; - else if (streq(name, "NoNewPrivileges")) - c->no_new_privileges = b; - else if (streq(name, "SyslogLevelPrefix")) - c->syslog_level_prefix = b; - else if (streq(name, "MemoryDenyWriteExecute")) - c->memory_deny_write_execute = b; - else if (streq(name, "RestrictRealtime")) - c->restrict_realtime = b; - - unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); - } - - return 1; - - } else if (streq(name, "UtmpIdentifier")) { - const char *id; - - r = sd_bus_message_read(message, "s", &id); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - if (isempty(id)) - c->utmp_id = mfree(c->utmp_id); - else if (free_and_strdup(&c->utmp_id, id) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s", strempty(id)); - } - - return 1; - - } else if (streq(name, "UtmpMode")) { - const char *s; - ExecUtmpMode m; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - m = exec_utmp_mode_from_string(s); - if (m < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode"); - - if (mode != UNIT_CHECK) { - c->utmp_mode = m; - - unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s", exec_utmp_mode_to_string(m)); - } - - return 1; - - } else if (streq(name, "PAMName")) { - const char *n; - - r = sd_bus_message_read(message, "s", &n); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - if (isempty(n)) - c->pam_name = mfree(c->pam_name); - else if (free_and_strdup(&c->pam_name, n) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "PAMName=%s", strempty(n)); - } - - return 1; - - } else if (streq(name, "Environment")) { - - _cleanup_strv_free_ char **l = NULL; - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - if (!strv_env_is_valid(l)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *joined = NULL; - char **e; - - if (strv_length(l) == 0) { - c->environment = strv_free(c->environment); - unit_write_drop_in_private_format(u, mode, name, "Environment="); - } else { - e = strv_env_merge(2, c->environment, l); - if (!e) - return -ENOMEM; - - strv_free(c->environment); - c->environment = e; - - joined = strv_join_quoted(c->environment); - if (!joined) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "Environment=%s", 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); - } - - 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", 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", *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", 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="); - } 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="); - } 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, "PassEnvironment=%s", joined); - } - } - - return 1; - - } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", - "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { - _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 (STR_IN_SET(name, "ReadWriteDirectories", "ReadWritePaths")) - dirs = &c->read_write_paths; - else if (STR_IN_SET(name, "ReadOnlyDirectories", "ReadOnlyPaths")) - dirs = &c->read_only_paths; - else /* "InaccessiblePaths" */ - dirs = &c->inaccessible_paths; - - if (strv_length(l) == 0) { - *dirs = strv_free(*dirs); - unit_write_drop_in_private_format(u, mode, name, "%s=", 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", 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", 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", 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=", 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", name, joined); - } - } - - return 1; - - } else if (streq(name, "SELinuxContext")) { - const char *s; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - if (isempty(s)) - c->selinux_context = mfree(c->selinux_context); - else if (free_and_strdup(&c->selinux_context, s) < 0) - return -ENOMEM; - - unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, strempty(s)); - } - - return 1; - - } - - ri = rlimit_from_string(name); - if (ri < 0) { - soft = endswith(name, "Soft"); - if (soft) { - const char *n; - - n = strndupa(name, soft - name); - ri = rlimit_from_string(n); - if (ri >= 0) - name = n; - - } - } - - if (ri >= 0) { - uint64_t rl; - rlim_t x; - - r = sd_bus_message_read(message, "t", &rl); - if (r < 0) - return r; - - if (rl == (uint64_t) -1) - x = RLIM_INFINITY; - else { - x = (rlim_t) rl; - - if ((uint64_t) x != rl) - return -ERANGE; - } - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *f = NULL; - struct rlimit nl; - - if (c->rlimit[ri]) { - nl = *c->rlimit[ri]; - - if (soft) - nl.rlim_cur = x; - else - nl.rlim_max = x; - } else - /* When the resource limit is not initialized yet, then assign the value to both fields */ - nl = (struct rlimit) { - .rlim_cur = x, - .rlim_max = x, - }; - - r = rlimit_format(&nl, &f); - if (r < 0) - return r; - - if (c->rlimit[ri]) - *c->rlimit[ri] = nl; - else { - c->rlimit[ri] = newdup(struct rlimit, &nl, 1); - if (!c->rlimit[ri]) - return -ENOMEM; - } - - unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, f); - } - - return 1; - } - - return 0; -} diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h deleted file mode 100644 index d0aa8e1dd5..0000000000 --- a/src/core/dbus-execute.h +++ /dev/null @@ -1,45 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "execute.h" - -#define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \ - BUS_PROPERTY_DUAL_TIMESTAMP(prefix "StartTimestamp", (offset) + offsetof(ExecStatus, start_timestamp), flags), \ - BUS_PROPERTY_DUAL_TIMESTAMP(prefix "ExitTimestamp", (offset) + offsetof(ExecStatus, exit_timestamp), flags), \ - SD_BUS_PROPERTY(prefix "PID", "u", bus_property_get_pid, (offset) + offsetof(ExecStatus, pid), flags), \ - SD_BUS_PROPERTY(prefix "Code", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, code), flags), \ - SD_BUS_PROPERTY(prefix "Status", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, status), flags) - -#define BUS_EXEC_COMMAND_VTABLE(name, offset, flags) \ - SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command, offset, flags) - -#define BUS_EXEC_COMMAND_LIST_VTABLE(name, offset, flags) \ - SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command_list, offset, flags) - -extern const sd_bus_vtable bus_exec_vtable[]; - -int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); -int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); -int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); - -int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c deleted file mode 100644 index ccf7453d47..0000000000 --- a/src/core/dbus-job.c +++ /dev/null @@ -1,193 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.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); - -static int property_get_unit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Job *j = userdata; - - assert(bus); - assert(reply); - assert(j); - - p = unit_dbus_path(j->unit); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", j->unit->id, p); -} - -int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Job *j = userdata; - int r; - - assert(message); - assert(j); - - r = mac_selinux_unit_access_check(j->unit, message, "stop", error); - if (r < 0) - return r; - - /* Access is granted to the job owner */ - if (!sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message))) { - - /* And for everybody else consult PolicyKit */ - r = bus_verify_manage_units_async(j->unit->manager, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - job_finish_and_invalidate(j, JOB_CANCELED, true, false); - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable bus_job_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("State", "s", property_get_state, offsetof(Job, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_VTABLE_END -}; - -static int send_new_signal(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *p = NULL; - Job *j = userdata; - int r; - - assert(bus); - assert(j); - - p = job_dbus_path(j); - if (!p) - return -ENOMEM; - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "JobNew"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "uos", j->id, p, j->unit->id); - if (r < 0) - return r; - - return sd_bus_send(bus, m, NULL); -} - -static int send_changed_signal(sd_bus *bus, void *userdata) { - _cleanup_free_ char *p = NULL; - Job *j = userdata; - - assert(bus); - assert(j); - - p = job_dbus_path(j); - if (!p) - return -ENOMEM; - - return sd_bus_emit_properties_changed(bus, p, "org.freedesktop.systemd1.Job", "State", NULL); -} - -void bus_job_send_change_signal(Job *j) { - int r; - - assert(j); - - if (j->in_dbus_queue) { - LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); - j->in_dbus_queue = false; - } - - r = bus_foreach_bus(j->manager, j->clients, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); - if (r < 0) - log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id); - - j->sent_dbus_new_signal = true; -} - -static int send_removed_signal(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *p = NULL; - Job *j = userdata; - int r; - - assert(bus); - assert(j); - - p = job_dbus_path(j); - if (!p) - return -ENOMEM; - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "JobRemoved"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "uoss", j->id, p, j->unit->id, job_result_to_string(j->result)); - if (r < 0) - return r; - - return sd_bus_send(bus, m, NULL); -} - -void bus_job_send_removed_signal(Job *j) { - int r; - - assert(j); - - if (!j->sent_dbus_new_signal) - bus_job_send_change_signal(j); - - r = bus_foreach_bus(j->manager, j->clients, send_removed_signal, j); - if (r < 0) - log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id); -} diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h deleted file mode 100644 index 024d06719e..0000000000 --- a/src/core/dbus-job.h +++ /dev/null @@ -1,31 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "job.h" - -extern const sd_bus_vtable bus_job_vtable[]; - -int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error); - -void bus_job_send_change_signal(Job *j); -void bus_job_send_removed_signal(Job *j); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c deleted file mode 100644 index 8c65be65fa..0000000000 --- a/src/core/dbus-kill.c +++ /dev/null @@ -1,122 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-util.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); - -const sd_bus_vtable bus_kill_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool, offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END -}; - -int bus_kill_context_set_transient_property( - Unit *u, - KillContext *c, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(u); - assert(c); - assert(name); - assert(message); - - if (streq(name, "KillMode")) { - const char *m; - KillMode k; - - r = sd_bus_message_read(message, "s", &m); - if (r < 0) - return r; - - k = kill_mode_from_string(m); - if (k < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m); - - if (mode != UNIT_CHECK) { - c->kill_mode = k; - - unit_write_drop_in_private_format(u, mode, name, "KillMode=%s", kill_mode_to_string(k)); - } - - return 1; - - } else if (streq(name, "KillSignal")) { - int sig; - - r = sd_bus_message_read(message, "i", &sig); - if (r < 0) - return r; - - if (!SIGNAL_VALID(sig)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig); - - if (mode != UNIT_CHECK) { - c->kill_signal = sig; - - unit_write_drop_in_private_format(u, mode, name, "KillSignal=%s", signal_to_string(sig)); - } - - return 1; - - } else if (streq(name, "SendSIGHUP")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->send_sighup = b; - - unit_write_drop_in_private_format(u, mode, name, "SendSIGHUP=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "SendSIGKILL")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->send_sigkill = b; - - unit_write_drop_in_private_format(u, mode, name, "SendSIGKILL=%s", yes_no(b)); - } - - return 1; - - } - - return 0; -} diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h deleted file mode 100644 index b9b18811e3..0000000000 --- a/src/core/dbus-kill.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "kill.h" -#include "unit.h" - -extern const sd_bus_vtable bus_kill_vtable[]; - -int bus_kill_context_set_transient_property(Unit *u, KillContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c deleted file mode 100644 index d05968bd65..0000000000 --- a/src/core/dbus-manager.c +++ /dev/null @@ -1,2306 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "architecture.h" -#include "build.h" -#include "bus-common-errors.h" -#include "clock-util.h" -#include "dbus-execute.h" -#include "dbus-job.h" -#include "dbus-manager.h" -#include "dbus-unit.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, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", PACKAGE_VERSION); -} - -static int property_get_features( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", SYSTEMD_FEATURES); -} - -static int property_get_virtualization( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - int v; - - assert(bus); - assert(reply); - - 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( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", architecture_to_string(uname_architecture())); -} - -static int property_get_tainted( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf; - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - if (m->taint_usr) - e = stpcpy(e, "split-usr:"); - - if (access("/proc/cgroups", F_OK) < 0) - e = stpcpy(e, "cgroups-missing:"); - - if (clock_is_localtime(NULL) > 0) - e = stpcpy(e, "local-hwclock:"); - - /* remove the last ':' */ - if (e != buf) - e[-1] = 0; - - return sd_bus_message_append(reply, "s", buf); -} - -static int property_get_log_target( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", log_target_to_string(log_get_target())); -} - -static int property_set_log_target( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *error) { - - const char *t; - int r; - - assert(bus); - assert(value); - - r = sd_bus_message_read(value, "s", &t); - if (r < 0) - return r; - - return log_set_target_from_string(t); -} - -static int property_get_log_level( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *t = NULL; - int r; - - assert(bus); - assert(reply); - - r = log_level_to_string_alloc(log_get_max_level(), &t); - if (r < 0) - return r; - - return sd_bus_message_append(reply, "s", t); -} - -static int property_set_log_level( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *error) { - - const char *t; - int r; - - assert(bus); - assert(value); - - r = sd_bus_message_read(value, "s", &t); - if (r < 0) - return r; - - 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( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->units)); -} - -static int property_get_n_failed_units( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "u", (uint32_t) set_size(m->failed_units)); -} - -static int property_get_n_jobs( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->jobs)); -} - -static int property_get_progress( - 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; - double d; - - assert(bus); - assert(reply); - assert(m); - - if (dual_timestamp_is_set(&m->finish_timestamp)) - d = 1.0; - else - d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs); - - return sd_bus_message_append(reply, "d", d); -} - -static int property_get_system_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "s", manager_state_to_string(manager_state(m))); -} - -static int property_set_runtime_watchdog( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *error) { - - usec_t *t = userdata; - int r; - - assert(bus); - assert(value); - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - r = sd_bus_message_read(value, "t", t); - if (r < 0) - return r; - - 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; - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (isempty(name)) { - _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); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - u = manager_get_unit_by_pid(m, pid); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); - } else { - u = manager_get_unit(m, name); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name); - } - - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Manager *m = userdata; - pid_t pid; - Unit *u; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - /* Anyone can call this method */ - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - if (pid < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pid); - - if (pid == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - } - - u = manager_get_unit_by_pid(m, pid); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid); - - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (isempty(name)) { - _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); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - u = manager_get_unit_by_pid(m, pid); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); - } else { - r = manager_load_unit(m, name, NULL, error, &u); - if (r < 0) - return r; - } - - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_start_unit_generic(sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) { - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_load_unit(m, name, NULL, error, &u); - if (r < 0) - return r; - - return bus_unit_method_start_generic(message, u, job_type, reload_if_possible, error); -} - -static int method_start_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_START, false, error); -} - -static int method_stop_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_STOP, false, error); -} - -static int method_reload_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_RELOAD, false, error); -} - -static int method_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_RESTART, false, error); -} - -static int method_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, false, error); -} - -static int method_reload_or_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_RESTART, true, error); -} - -static int method_reload_or_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error); -} - -static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *old_name; - Unit *u; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &old_name); - if (r < 0) - return r; - - u = manager_get_unit(m, old_name); - if (!u || !u->job || u->job->type != JOB_START) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name); - - return method_start_unit_generic(message, m, JOB_START, false, error); -} - -static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(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 is not loaded.", name); - - return bus_unit_method_kill(message, u, error); -} - -static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(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 is not loaded.", name); - - return bus_unit_method_reset_failed(message, u, error); -} - -static int method_set_unit_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - 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); -} - -static int reply_unit_info(sd_bus_message *reply, Unit *u) { - _cleanup_free_ char *unit_path = NULL, *job_path = NULL; - Unit *following; - - following = unit_following(u); - - unit_path = unit_dbus_path(u); - if (!unit_path) - return -ENOMEM; - - if (u->job) { - job_path = job_dbus_path(u->job); - if (!job_path) - return -ENOMEM; - } - - return sd_bus_message_append( - reply, "(ssssssouso)", - u->id, - unit_description(u), - unit_load_state_to_string(u->load_state), - unit_active_state_to_string(unit_active_state(u)), - unit_sub_state_to_string(u), - following ? following->id : "", - unit_path, - u->job ? u->job->id : 0, - u->job ? job_type_to_string(u->job->type) : "", - job_path ? job_path : "/"); -} - -static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - int r; - char **unit; - _cleanup_strv_free_ char **units = NULL; - - assert(message); - assert(m); - - r = sd_bus_message_read_strv(message, &units); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); - if (r < 0) - return r; - - STRV_FOREACH(unit, units) { - Unit *u; - - if (!unit_name_is_valid(*unit, UNIT_NAME_ANY)) - continue; - - r = manager_load_unit(m, *unit, NULL, error, &u); - if (r < 0) - return r; - - r = reply_unit_info(reply, u); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_get_unit_processes(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; - - 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_get_processes(message, u, error); -} - -static int transient_unit_from_message( - Manager *m, - sd_bus_message *message, - const char *name, - Unit **unit, - sd_bus_error *error) { - - UnitType t; - Unit *u; - int r; - - assert(m); - 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 (!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 - * fill in the transient data instead */ - r = unit_make_transient(u); - if (r < 0) - return r; - - /* Set our properties */ - r = bus_unit_set_properties(u, message, UNIT_RUNTIME, false, error); - if (r < 0) - return r; - - /* Now load the missing bits of the unit we just created */ - unit_add_to_load_queue(u); - manager_dispatch_load_queue(m); - - *unit = u; - - return 0; -} - -static int transient_aux_units_from_message( - Manager *m, - sd_bus_message *message, - sd_bus_error *error) { - - int r; - - assert(m); - assert(message); - - r = sd_bus_message_enter_container(message, 'a', "(sa(sv))"); - if (r < 0) - 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) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - return 0; -} - -static int method_start_transient_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *name, *smode; - Manager *m = userdata; - JobMode mode; - Unit *u; - 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, "ss", &name, &smode); - if (r < 0) - return r; - - 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); - - r = bus_verify_manage_units_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = transient_unit_from_message(m, message, name, &u, error); - if (r < 0) - return r; - - r = transient_aux_units_from_message(m, message, error); - if (r < 0) - return r; - - /* Finally, start it */ - return bus_unit_queue_job(message, u, JOB_START, mode, false, error); -} - -static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Manager *m = userdata; - uint32_t id; - Job *j; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = sd_bus_message_read(message, "u", &id); - if (r < 0) - return r; - - j = manager_get_job(m, id); - if (!j) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); - - r = mac_selinux_unit_access_check(j->unit, message, "status", error); - if (r < 0) - return r; - - path = job_dbus_path(j); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_cancel_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint32_t id; - Job *j; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &id); - if (r < 0) - return r; - - j = manager_get_job(m, id); - if (!j) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); - - return bus_job_method_cancel(message, j, error); -} - -static int method_clear_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = bus_verify_manage_units_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - manager_clear_jobs(m); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = bus_verify_manage_units_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - manager_reset_failed(m); - - return sd_bus_reply_method_return(message, NULL); -} - -static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - const char *k; - Iterator i; - Unit *u; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); - if (r < 0) - return r; - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - if (k != u->id) - continue; - - if (!strv_isempty(states) && - !strv_contains(states, unit_load_state_to_string(u->load_state)) && - !strv_contains(states, unit_active_state_to_string(unit_active_state(u))) && - !strv_contains(states, unit_sub_state_to_string(u))) - continue; - - if (!strv_isempty(patterns) && - !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) - continue; - - r = reply_unit_info(reply, u); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return list_units_filtered(message, userdata, error, NULL, NULL); -} - -static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **states = NULL; - int r; - - r = sd_bus_message_read_strv(message, &states); - if (r < 0) - return r; - - return list_units_filtered(message, userdata, error, states, NULL); -} - -static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **states = NULL; - _cleanup_strv_free_ char **patterns = NULL; - int r; - - r = sd_bus_message_read_strv(message, &states); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &patterns); - if (r < 0) - return r; - - return list_units_filtered(message, userdata, error, states, patterns); -} - -static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Iterator i; - Job *j; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(usssoo)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(j, m->jobs, i) { - _cleanup_free_ char *unit_path = NULL, *job_path = NULL; - - job_path = job_dbus_path(j); - if (!job_path) - return -ENOMEM; - - unit_path = unit_dbus_path(j->unit); - if (!unit_path) - return -ENOMEM; - - r = sd_bus_message_append( - reply, "(usssoo)", - j->id, - j->unit->id, - job_type_to_string(j->type), - job_state_to_string(j->state), - job_path, - unit_path); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_subscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - if (sd_bus_message_get_bus(message) == m->api_bus) { - - /* Note that direct bus connection subscribe by - * default, we only track peers on the API bus here */ - - if (!m->subscribed) { - r = sd_bus_track_new(sd_bus_message_get_bus(message), &m->subscribed, NULL, NULL); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(m->subscribed, message); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed."); - } - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_unsubscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - if (sd_bus_message_get_bus(message) == m->api_bus) { - r = sd_bus_track_remove_sender(m->subscribed, message); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed."); - } - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *dump = NULL; - _cleanup_fclose_ FILE *f = NULL; - Manager *m = userdata; - size_t size; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - f = open_memstream(&dump, &size); - if (!f) - return -ENOMEM; - - manager_dump_units(m, f, NULL); - manager_dump_jobs(m, f, NULL); - - r = fflush_and_check(f); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, "s", dump); -} - -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) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = bus_verify_reload_daemon_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - /* Instead of sending the reply back right away, we just - * remember that we need to and then send it after the reload - * is finished. That way the caller knows when the reload - * finished. */ - - assert(!m->queued_message); - r = sd_bus_message_new_method_return(message, &m->queued_message); - if (r < 0) - return r; - - m->exit_code = MANAGER_RELOAD; - - return 1; -} - -static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = bus_verify_reload_daemon_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - /* We don't send a reply back here, the client should - * just wait for us disconnecting. */ - - m->exit_code = MANAGER_REEXECUTE; - return 1; -} - -static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "halt", error); - if (r < 0) - return r; - - /* Exit() (in contrast to SetExitCode()) is actually allowed even if - * we are running on the host. It will fall back on reboot() in - * systemd-shutdown if it cannot do the exit() because it isn't a - * container. */ - - m->exit_code = MANAGER_EXIT; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reboot", error); - if (r < 0) - return r; - - if (!MANAGER_IS_SYSTEM(m)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers."); - - m->exit_code = MANAGER_REBOOT; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "halt", error); - if (r < 0) - return r; - - if (!MANAGER_IS_SYSTEM(m)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers."); - - m->exit_code = MANAGER_POWEROFF; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "halt", error); - if (r < 0) - return r; - - if (!MANAGER_IS_SYSTEM(m)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers."); - - m->exit_code = MANAGER_HALT; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reboot", error); - if (r < 0) - return r; - - if (!MANAGER_IS_SYSTEM(m)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers."); - - m->exit_code = MANAGER_KEXEC; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) { - char *ri = NULL, *rt = NULL; - const char *root, *init; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reboot", error); - if (r < 0) - return r; - - if (!MANAGER_IS_SYSTEM(m)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager."); - - r = sd_bus_message_read(message, "ss", &root, &init); - if (r < 0) - return r; - - if (path_equal(root, "/") || !path_is_absolute(root)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid switch root path %s", root); - - /* Safety check */ - if (isempty(init)) { - if (!path_is_os_tree(root)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path %s does not seem to be an OS tree. os-release file is missing.", root); - } else { - _cleanup_free_ char *p = NULL; - - if (!path_is_absolute(init)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid init path %s", init); - - p = strappend(root, init); - if (!p) - return -ENOMEM; - - if (access(p, X_OK) < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p); - } - - rt = strdup(root); - if (!rt) - return -ENOMEM; - - if (!isempty(init)) { - ri = strdup(init); - if (!ri) { - free(rt); - return -ENOMEM; - } - } - - free(m->switch_root); - m->switch_root = rt; - - free(m->switch_root_init); - m->switch_root_init = ri; - - m->exit_code = MANAGER_SWITCH_ROOT; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **plus = NULL; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &plus); - if (r < 0) - return r; - if (!strv_env_is_valid(plus)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); - - r = bus_verify_set_environment_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = manager_environment_add(m, NULL, plus); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_unset_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **minus = NULL; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &minus); - if (r < 0) - return r; - - if (!strv_env_name_or_assignment_is_valid(minus)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments"); - - r = bus_verify_set_environment_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = manager_environment_add(m, minus, NULL); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_unset_and_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **minus = NULL, **plus = NULL; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "reload", error); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &minus); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &plus); - if (r < 0) - return r; - - if (!strv_env_name_or_assignment_is_valid(minus)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments"); - if (!strv_env_is_valid(plus)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); - - r = bus_verify_set_environment_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = manager_environment_add(m, minus, plus); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) { - uint8_t code; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "exit", error); - if (r < 0) - return r; - - r = sd_bus_message_read_basic(message, 'y', &code); - if (r < 0) - return r; - - if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); - - m->return_value = code; - - return sd_bus_reply_method_return(message, NULL); -} - -static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - UnitFileList *item; - Hashmap *h; - Iterator i; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - h = hashmap_new(&string_hash_ops); - if (!h) - return -ENOMEM; - - r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns); - if (r < 0) - goto fail; - - r = sd_bus_message_open_container(reply, 'a', "(ss)"); - if (r < 0) - goto fail; - - HASHMAP_FOREACH(item, h, i) { - - r = sd_bus_message_append(reply, "(ss)", item->path, unit_file_state_to_string(item->state)); - if (r < 0) - goto fail; - } - - unit_file_list_free(h); - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); - -fail: - unit_file_list_free(h); - return r; -} - -static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return list_unit_files_by_patterns(message, userdata, error, NULL, NULL); -} - -static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **states = NULL; - _cleanup_strv_free_ char **patterns = NULL; - int r; - - r = sd_bus_message_read_strv(message, &states); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(message, &patterns); - if (r < 0) - return r; - - return list_unit_files_by_patterns(message, userdata, error, states, patterns); -} - -static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - UnitFileState state; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = unit_file_get_state(m->unit_file_scope, NULL, name, &state); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state)); -} - -static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *default_target = NULL; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - /* Anyone can call this method */ - - r = mac_selinux_access_check(message, "status", error); - if (r < 0) - return r; - - r = unit_file_get_default(m->unit_file_scope, NULL, &default_target); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, "s", default_target); -} - -static int send_unit_files_changed(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; - int r; - - assert(bus); - - r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged"); - if (r < 0) - return r; - - return sd_bus_send(bus, message, NULL); -} - -static int reply_unit_file_changes_and_free( - Manager *m, - sd_bus_message *message, - int carries_install_info, - UnitFileChange *changes, - unsigned n_changes) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned i; - int r; - - if (unit_file_changes_have_modification(changes, n_changes)) { - r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); - if (r < 0) - log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); - } - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - goto fail; - - if (carries_install_info >= 0) { - r = sd_bus_message_append(reply, "b", carries_install_info); - if (r < 0) - goto fail; - } - - r = sd_bus_message_open_container(reply, 'a', "(sss)"); - if (r < 0) - goto fail; - - for (i = 0; i < n_changes; i++) - if (changes[i].type >= 0) { - const char *change = unit_file_change_type_to_string(changes[i].type); - assert(change != NULL); - - r = sd_bus_message_append( - reply, "(sss)", - change, - changes[i].path, - changes[i].source); - if (r < 0) - goto fail; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto fail; - - unit_file_changes_free(changes, n_changes); - return sd_bus_send(NULL, reply, NULL); - -fail: - unit_file_changes_free(changes, n_changes); - return r; -} - -/* Create an error reply, using the error information from changes[] - * if possible, and fall back to generating an error from error code c. - * The error message only describes the first error. - * - * Coordinate with unit_file_dump_changes() in install.c. - */ -static int install_error( - sd_bus_error *error, - int c, - UnitFileChange *changes, - unsigned n_changes) { - int r; - unsigned i; - assert(c < 0); - - for (i = 0; i < n_changes; i++) - switch(changes[i].type) { - case 0 ... INT_MAX: - continue; - case -EEXIST: - if (changes[i].source) - r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, - "File %s already exists and is a symlink to %s.", - changes[i].path, changes[i].source); - else - r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, - "File %s already exists.", - changes[i].path); - goto found; - case -ERFKILL: - r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, - "Unit file %s is masked.", changes[i].path); - goto found; - case -EADDRNOTAVAIL: - r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, - "Unit %s is transient or generated.", changes[i].path); - goto found; - case -ELOOP: - r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, - "Refusing to operate on linked unit file %s", changes[i].path); - goto found; - default: - r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); - goto found; - } - - r = c; - found: - unit_file_changes_free(changes, n_changes); - return r; -} - -static int method_enable_unit_files_generic( - sd_bus_message *message, - Manager *m, - int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes), - bool carries_install_info, - sd_bus_error *error) { - - _cleanup_strv_free_ char **l = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int runtime, force, r; - - assert(message); - assert(m); - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "bb", &runtime, &force); - if (r < 0) - return r; - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); -} - -static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error); -} - -static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error); -} - -static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); -} - -static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) { - return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes); -} - -static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error); -} - -static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error); -} - -static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) { - - _cleanup_strv_free_ char **l = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - Manager *m = userdata; - UnitFilePresetMode mm; - int runtime, force, r; - const char *mode; - - assert(message); - assert(m); - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force); - if (r < 0) - return r; - - if (isempty(mode)) - mm = UNIT_FILE_PRESET_FULL; - else { - mm = unit_file_preset_mode_from_string(mode); - if (mm < 0) - return -EINVAL; - } - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); -} - -static int method_disable_unit_files_generic( - sd_bus_message *message, - Manager *m, - int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), - sd_bus_error *error) { - - _cleanup_strv_free_ char **l = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int r, runtime; - - assert(message); - assert(m); - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "b", &runtime); - if (r < 0) - return r; - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); -} - -static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, unit_file_disable, error); -} - -static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error); -} - -static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); -} - -static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - Manager *m = userdata; - const char *name; - int force, r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "enable", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "sb", &name, &force); - if (r < 0) - return r; - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); -} - -static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - Manager *m = userdata; - UnitFilePresetMode mm; - const char *mode; - int force, runtime, r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "enable", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force); - if (r < 0) - return r; - - if (isempty(mode)) - mm = UNIT_FILE_PRESET_FULL; - else { - mm = unit_file_preset_mode_from_string(mode); - if (mm < 0) - return -EINVAL; - } - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); -} - -static int method_add_dependency_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - Manager *m = userdata; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int runtime, force, r; - char *target, *type; - UnitDependency dep; - - assert(message); - assert(m); - - r = bus_verify_manage_unit_files_async(m, message, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = sd_bus_message_read_strv(message, &l); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "ssbb", &target, &type, &runtime, &force); - if (r < 0) - return r; - - dep = unit_dependency_from_string(type); - if (dep < 0) - return -EINVAL; - - r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); - if (r < 0) - return install_error(error, r, changes, n_changes); - - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); -} - -const sd_bus_vtable bus_manager_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Version", "s", property_get_version, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Features", "s", property_get_features, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Virtualization", "s", property_get_virtualization, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Architecture", "s", property_get_architecture, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Tainted", "s", property_get_tainted, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("FirmwareTimestamp", offsetof(Manager, firmware_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("LoaderTimestamp", offsetof(Manager, loader_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("KernelTimestamp", offsetof(Manager, kernel_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("InitRDTimestamp", offsetof(Manager, initrd_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("UserspaceTimestamp", offsetof(Manager, userspace_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("FinishTimestamp", offsetof(Manager, finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("SecurityStartTimestamp", offsetof(Manager, security_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("SecurityFinishTimestamp", offsetof(Manager, security_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsStartTimestamp", offsetof(Manager, generators_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsFinishTimestamp", offsetof(Manager, generators_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadStartTimestamp", offsetof(Manager, units_load_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadFinishTimestamp", offsetof(Manager, units_load_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0), - SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0), - SD_BUS_PROPERTY("NNames", "u", property_get_n_names, 0, 0), - SD_BUS_PROPERTY("NFailedUnits", "u", property_get_n_failed_units, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("NJobs", "u", property_get_n_jobs, 0, 0), - SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0), - SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0), - SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), - SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0), - SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), - SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), - 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("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), 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_VTABLE_HIDDEN), /* obsolete alias name */ - 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), - SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StopUnit", "ss", "o", method_stop_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadUnit", "ss", "o", method_reload_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RestartUnit", "ss", "o", method_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_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), - SD_BUS_METHOD("Reboot", NULL, NULL, method_reboot, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("PowerOff", NULL, NULL, method_poweroff, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("Halt", NULL, NULL, method_halt, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("KExec", NULL, NULL, method_kexec, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("SwitchRoot", "ss", NULL, method_switch_root, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), - SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("UnitNew", "so", 0), - SD_BUS_SIGNAL("UnitRemoved", "so", 0), - SD_BUS_SIGNAL("JobNew", "uos", 0), - SD_BUS_SIGNAL("JobRemoved", "uoss", 0), - SD_BUS_SIGNAL("StartupFinished", "tttttt", 0), - SD_BUS_SIGNAL("UnitFilesChanged", NULL, 0), - SD_BUS_SIGNAL("Reloading", "b", 0), - - SD_BUS_VTABLE_END -}; - -static int send_finished(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; - usec_t *times = userdata; - int r; - - assert(bus); - assert(times); - - r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished"); - if (r < 0) - return r; - - r = sd_bus_message_append(message, "tttttt", times[0], times[1], times[2], times[3], times[4], times[5]); - if (r < 0) - return r; - - return sd_bus_send(bus, message, NULL); -} - -void bus_manager_send_finished( - Manager *m, - usec_t firmware_usec, - usec_t loader_usec, - usec_t kernel_usec, - usec_t initrd_usec, - usec_t userspace_usec, - usec_t total_usec) { - - int r; - - assert(m); - - r = bus_foreach_bus( - m, - NULL, - send_finished, - (usec_t[6]) { - firmware_usec, - loader_usec, - kernel_usec, - initrd_usec, - userspace_usec, - total_usec - }); - if (r < 0) - log_debug_errno(r, "Failed to send finished signal: %m"); -} - -static int send_reloading(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; - int r; - - assert(bus); - - r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Reloading"); - if (r < 0) - return r; - - r = sd_bus_message_append(message, "b", PTR_TO_INT(userdata)); - if (r < 0) - return r; - - return sd_bus_send(bus, message, NULL); -} - -void bus_manager_send_reloading(Manager *m, bool active) { - int r; - - assert(m); - - r = bus_foreach_bus(m, NULL, send_reloading, INT_TO_PTR(active)); - if (r < 0) - log_debug_errno(r, "Failed to send reloading signal: %m"); -} - -static int send_changed_signal(sd_bus *bus, void *userdata) { - assert(bus); - - return sd_bus_emit_properties_changed_strv(bus, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - NULL); -} - -void bus_manager_send_change_signal(Manager *m) { - int r; - - assert(m); - - r = bus_foreach_bus(m, NULL, send_changed_signal, NULL); - if (r < 0) - log_debug_errno(r, "Failed to send manager change signal: %m"); -} diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h deleted file mode 100644 index 36a2e9481b..0000000000 --- a/src/core/dbus-manager.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 . -***/ - -#include "manager.h" - -extern const sd_bus_vtable bus_manager_vtable[]; - -void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); -void bus_manager_send_reloading(Manager *m, bool active); -void bus_manager_send_change_signal(Manager *m); diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c deleted file mode 100644 index 935db7c48b..0000000000 --- a/src/core/dbus-mount.c +++ /dev/null @@ -1,211 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-util.h" -#include "dbus-cgroup.h" -#include "dbus-execute.h" -#include "dbus-kill.h" -#include "dbus-mount.h" -#include "mount.h" -#include "string-util.h" -#include "unit.h" - -static int property_get_what( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Mount *m = userdata; - const char *d; - - assert(bus); - assert(reply); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what) - d = m->parameters_proc_self_mountinfo.what; - else if (m->from_fragment && m->parameters_fragment.what) - d = m->parameters_fragment.what; - else - d = ""; - - return sd_bus_message_append(reply, "s", d); -} - -static int property_get_options( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Mount *m = userdata; - const char *d; - - assert(bus); - assert(reply); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options) - d = m->parameters_proc_self_mountinfo.options; - else if (m->from_fragment && m->parameters_fragment.options) - d = m->parameters_fragment.options; - else - d = ""; - - return sd_bus_message_append(reply, "s", d); -} - -static int property_get_type( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Mount *m = userdata; - const char *d; - - assert(bus); - assert(reply); - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) - d = m->parameters_proc_self_mountinfo.fstype; - else if (m->from_fragment && m->parameters_fragment.fstype) - d = m->parameters_fragment.fstype; - else - d = ""; - - return sd_bus_message_append(reply, "s", d); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResult); - -const sd_bus_vtable bus_mount_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Mount, where), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("What", "s", property_get_what, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Mount, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -static int bus_mount_set_transient_property( - Mount *m, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - const char *new_property; - char **property; - char *p; - int r; - - assert(m); - assert(name); - assert(message); - - if (streq(name, "What")) - property = &m->parameters_fragment.what; - else if (streq(name, "Options")) - property = &m->parameters_fragment.options; - else if (streq(name, "Type")) - property = &m->parameters_fragment.fstype; - else - return 0; - - r = sd_bus_message_read(message, "s", &new_property); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - p = strdup(new_property); - if (!p) - return -ENOMEM; - - free(*property); - *property = p; - } - - return 1; -} - -int bus_mount_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Mount *m = MOUNT(u); - int r; - - assert(m); - assert(name); - assert(message); - - r = bus_cgroup_set_property(u, &m->cgroup_context, name, message, mode, error); - if (r != 0) - return r; - - if (u->transient && u->load_state == UNIT_STUB) { - /* This is a transient unit, let's load a little more */ - - r = bus_mount_set_transient_property(m, name, message, mode, error); - if (r != 0) - return r; - - r = bus_exec_context_set_transient_property(u, &m->exec_context, name, message, mode, error); - if (r != 0) - return r; - - r = bus_kill_context_set_transient_property(u, &m->kill_context, name, message, mode, error); - if (r != 0) - return r; - } - - return 0; -} - -int bus_mount_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h deleted file mode 100644 index ec16166d36..0000000000 --- a/src/core/dbus-mount.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_mount_vtable[]; - -int bus_mount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_mount_commit_properties(Unit *u); diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c deleted file mode 100644 index 1e153e503f..0000000000 --- a/src/core/dbus-path.c +++ /dev/null @@ -1,86 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - -static int property_get_paths( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Path *p = userdata; - PathSpec *k; - int r; - - assert(bus); - assert(reply); - assert(p); - - r = sd_bus_message_open_container(reply, 'a', "(ss)"); - if (r < 0) - return r; - - LIST_FOREACH(spec, k, p->specs) { - r = sd_bus_message_append(reply, "(ss)", path_type_to_string(k->type), k->path); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_unit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *p = userdata, *trigger; - - assert(bus); - assert(reply); - assert(p); - - trigger = UNIT_TRIGGER(p); - - return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); -} - -const sd_bus_vtable bus_path_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Paths", "a(ss)", property_get_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MakeDirectory", "b", bus_property_get_bool, offsetof(Path, make_directory), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Path, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h deleted file mode 100644 index d3c19e0c2b..0000000000 --- a/src/core/dbus-path.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - - - -extern const sd_bus_vtable bus_path_vtable[]; diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c deleted file mode 100644 index 1abaf9f658..0000000000 --- a/src/core/dbus-scope.c +++ /dev/null @@ -1,229 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-common-errors.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; - 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 */ - - r = scope_abandon(s); - if (r == -ESTALE) - return sd_bus_error_setf(error, BUS_ERROR_SCOPE_NOT_RUNNING, "Scope %s is not running, cannot abandon.", UNIT(s)->id); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, scope_result, ScopeResult); - -const sd_bus_vtable bus_scope_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Controller", "s", NULL, offsetof(Scope, controller), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Scope, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_SIGNAL("RequestStop", NULL, 0), - SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_abandon, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -static int bus_scope_set_transient_property( - Scope *s, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(s); - assert(name); - assert(message); - - if (streq(name, "PIDs")) { - unsigned n = 0; - uint32_t pid; - - r = sd_bus_message_enter_container(message, 'a', "u"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(message, "u", &pid)) > 0) { - - if (pid <= 1) - return -EINVAL; - - if (mode != UNIT_CHECK) { - r = unit_watch_pid(UNIT(s), pid); - if (r < 0 && r != -EEXIST) - return r; - } - - n++; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (n <= 0) - return -EINVAL; - - return 1; - - } else if (streq(name, "Controller")) { - const char *controller; - char *c; - - r = sd_bus_message_read(message, "s", &controller); - if (r < 0) - return r; - - if (!isempty(controller) && !service_name_is_valid(controller)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Controller '%s' is not a valid bus name.", controller); - - if (mode != UNIT_CHECK) { - if (isempty(controller)) - c = NULL; - else { - c = strdup(controller); - if (!c) - return -ENOMEM; - } - - free(s->controller); - s->controller = c; - } - - return 1; - - } else if (streq(name, "TimeoutStopUSec")) { - - if (mode != UNIT_CHECK) { - r = sd_bus_message_read(message, "t", &s->timeout_stop_usec); - if (r < 0) - return r; - - unit_write_drop_in_private_format(UNIT(s), mode, name, "TimeoutStopSec="USEC_FMT"us", s->timeout_stop_usec); - } else { - r = sd_bus_message_skip(message, "t"); - if (r < 0) - return r; - } - - return 1; - } - - return 0; -} - -int bus_scope_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Scope *s = SCOPE(u); - int r; - - assert(s); - assert(name); - assert(message); - - r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); - if (r != 0) - return r; - - if (u->load_state == UNIT_STUB) { - /* While we are created we still accept PIDs */ - - r = bus_scope_set_transient_property(s, name, message, mode, error); - if (r != 0) - return r; - - r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, mode, error); - if (r != 0) - return r; - } - - return 0; -} - -int bus_scope_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} - -int bus_scope_send_request_stop(Scope *s) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(s); - - if (!s->controller) - return 0; - - p = unit_dbus_path(UNIT(s)); - if (!p) - return -ENOMEM; - - r = sd_bus_message_new_signal( - UNIT(s)->manager->api_bus, - &m, - p, - "org.freedesktop.systemd1.Scope", - "RequestStop"); - if (r < 0) - return r; - - return sd_bus_send_to(UNIT(s)->manager->api_bus, m, s->controller, NULL); -} diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h deleted file mode 100644 index 270306f508..0000000000 --- a/src/core/dbus-scope.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_scope_vtable[]; - -int bus_scope_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_scope_commit_properties(Unit *u); - -int bus_scope_send_request_stop(Scope *s); diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c deleted file mode 100644 index fab3677a01..0000000000 --- a/src/core/dbus-service.c +++ /dev/null @@ -1,322 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "async.h" -#include "bus-util.h" -#include "dbus-cgroup.h" -#include "dbus-execute.h" -#include "dbus-kill.h" -#include "dbus-service.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); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); - -const sd_bus_vtable bus_service_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), - /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("GuessMainPID", "b", bus_property_get_bool, offsetof(Service, guess_main_pid), SD_BUS_VTABLE_PROPERTY_CONST), - 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", 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), - SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -static int bus_service_set_transient_property( - Service *s, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(s); - assert(name); - assert(message); - - if (streq(name, "RemainAfterExit")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - s->remain_after_exit = b; - unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "Type")) { - const char *t; - ServiceType k; - - r = sd_bus_message_read(message, "s", &t); - if (r < 0) - return r; - - k = service_type_from_string(t); - if (k < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service type %s", t); - - if (mode != UNIT_CHECK) { - s->type = k; - unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s", service_type_to_string(s->type)); - } - - return 1; - } else if (streq(name, "RuntimeMaxUSec")) { - usec_t u; - - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - s->runtime_max_usec = u; - unit_write_drop_in_private_format(UNIT(s), mode, name, "RuntimeMaxSec=" USEC_FMT "us", u); - } - - 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; - } - - s->exec_context.stdio_as_fds = true; - } - - return 1; - - } else if (streq(name, "ExecStart")) { - unsigned n = 0; - - r = sd_bus_message_enter_container(message, 'a', "(sasb)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { - _cleanup_strv_free_ char **argv = NULL; - const char *path; - int b; - - r = sd_bus_message_read(message, "s", &path); - if (r < 0) - return r; - - if (!path_is_absolute(path)) - return sd_bus_error_set_errnof(error, EINVAL, "Path %s is not absolute.", path); - - r = sd_bus_message_read_strv(message, &argv); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - ExecCommand *c; - - c = new0(ExecCommand, 1); - if (!c) - return -ENOMEM; - - c->path = strdup(path); - if (!c->path) { - free(c); - return -ENOMEM; - } - - c->argv = argv; - argv = NULL; - - c->ignore = b; - - path_kill_slashes(c->path); - exec_command_append_list(&s->exec_command[SERVICE_EXEC_START], c); - } - - n++; - } - - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - ExecCommand *c; - size_t size = 0; - - if (n == 0) - s->exec_command[SERVICE_EXEC_START] = exec_command_free_list(s->exec_command[SERVICE_EXEC_START]); - - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; - - fputs("ExecStart=\n", f); - - LIST_FOREACH(command, c, s->exec_command[SERVICE_EXEC_START]) { - _cleanup_free_ char *a; - - a = strv_join_quoted(c->argv); - if (!a) - return -ENOMEM; - - fprintf(f, "ExecStart=%s@%s %s\n", - c->ignore ? "-" : "", - c->path, - a); - } - - r = fflush_and_check(f); - if (r < 0) - return r; - unit_write_drop_in_private(UNIT(s), mode, name, buf); - } - - return 1; - } - - return 0; -} - -int bus_service_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Service *s = SERVICE(u); - int r; - - assert(s); - assert(name); - assert(message); - - r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); - if (r != 0) - return r; - - if (u->transient && u->load_state == UNIT_STUB) { - /* This is a transient unit, let's load a little more */ - - r = bus_service_set_transient_property(s, name, message, mode, error); - if (r != 0) - return r; - - r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, mode, error); - if (r != 0) - return r; - - r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, mode, error); - if (r != 0) - return r; - } - - return 0; -} - -int bus_service_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h deleted file mode 100644 index 769a53769e..0000000000 --- a/src/core/dbus-service.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_service_vtable[]; - -int bus_service_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_service_commit_properties(Unit *u); diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c deleted file mode 100644 index e37f50b283..0000000000 --- a/src/core/dbus-slice.c +++ /dev/null @@ -1,52 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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), - SD_BUS_VTABLE_END -}; - -int bus_slice_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Slice *s = SLICE(u); - - assert(name); - assert(u); - - return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); -} - -int bus_slice_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h deleted file mode 100644 index 52ceebb135..0000000000 --- a/src/core/dbus-slice.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_slice_vtable[]; - -int bus_slice_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_slice_commit_properties(Unit *u); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c deleted file mode 100644 index 961340608d..0000000000 --- a/src/core/dbus-socket.c +++ /dev/null @@ -1,184 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-util.h" -#include "dbus-cgroup.h" -#include "dbus-execute.h" -#include "dbus-socket.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); - -static int property_get_listen( - 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); - SocketPort *p; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = sd_bus_message_open_container(reply, 'a', "(ss)"); - if (r < 0) - return r; - - LIST_FOREACH(port, p, s->ports) { - _cleanup_free_ char *address = NULL; - const char *a; - - switch (p->type) { - case SOCKET_SOCKET: { - r = socket_address_print(&p->address, &address); - if (r) - return r; - - a = address; - break; - } - - case SOCKET_SPECIAL: - case SOCKET_MQUEUE: - case SOCKET_FIFO: - case SOCKET_USB_FUNCTION: - a = p->path; - break; - - default: - assert_not_reached("Unknown socket type"); - } - - r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), a); - if (r < 0) - return r; - } - - 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), - SD_BUS_PROPERTY("Backlog", "u", bus_property_get_unsigned, offsetof(Socket, backlog), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Socket, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("BindToDevice", "s", NULL, offsetof(Socket, bind_to_device), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SocketUser", "s", NULL, offsetof(Socket, user), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SocketGroup", "s", NULL, offsetof(Socket, group), SD_BUS_VTABLE_PROPERTY_CONST), - 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), - SD_BUS_PROPERTY("KeepAliveProbes", "u", bus_property_get_unsigned, offsetof(Socket, keep_alive_cnt), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DeferAcceptUSec" , "t", bus_property_get_usec, offsetof(Socket, defer_accept), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NoDelay", "b", bus_property_get_bool, offsetof(Socket, no_delay), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Priority", "i", bus_property_get_int, offsetof(Socket, priority), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ReceiveBuffer", "t", bus_property_get_size, offsetof(Socket, receive_buffer), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SendBuffer", "t", bus_property_get_size, offsetof(Socket, send_buffer), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IPTOS", "i", bus_property_get_int, offsetof(Socket, ip_tos), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IPTTL", "i", bus_property_get_int, offsetof(Socket, ip_ttl), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PipeSize", "t", bus_property_get_size, offsetof(Socket, pipe_size), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("FreeBind", "b", bus_property_get_bool, offsetof(Socket, free_bind), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Transparent", "b", bus_property_get_bool, offsetof(Socket, transparent), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SmackLabel", "s", NULL, offsetof(Socket, smack), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SmackLabelIPIn", "s", NULL, offsetof(Socket, smack_ip_in), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SmackLabelIPOut", "s", NULL, offsetof(Socket, smack_ip_out), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Socket, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - 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), - SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), 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), - BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -int bus_socket_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Socket *s = SOCKET(u); - - assert(s); - assert(name); - assert(message); - - return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); -} - -int bus_socket_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h deleted file mode 100644 index 7a792c7a89..0000000000 --- a/src/core/dbus-socket.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_socket_vtable[]; - -int bus_socket_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_socket_commit_properties(Unit *u); diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c deleted file mode 100644 index 292f8738c6..0000000000 --- a/src/core/dbus-swap.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-util.h" -#include "dbus-cgroup.h" -#include "dbus-execute.h" -#include "dbus-swap.h" -#include "string-util.h" -#include "swap.h" -#include "unit.h" - -static int property_get_priority( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Swap *s = SWAP(userdata); - int p; - - assert(bus); - assert(reply); - assert(s); - - if (s->from_proc_swaps) - p = s->parameters_proc_swaps.priority; - else if (s->from_fragment) - p = s->parameters_fragment.priority; - else - p = -1; - - return sd_bus_message_append(reply, "i", p); -} - -static int property_get_options( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Swap *s = SWAP(userdata); - const char *options = NULL; - - assert(bus); - assert(reply); - assert(s); - - if (s->from_fragment) - options = s->parameters_fragment.options; - - return sd_bus_message_append(reply, "s", options); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, swap_result, SwapResult); - -const sd_bus_vtable bus_swap_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("What", "s", NULL, offsetof(Swap, what), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Priority", "i", property_get_priority, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Options", "s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -int bus_swap_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Swap *s = SWAP(u); - - assert(s); - assert(name); - assert(message); - - return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); -} - -int bus_swap_commit_properties(Unit *u) { - assert(u); - - unit_update_cgroup_members_masks(u); - unit_realize_cgroup(u); - - return 0; -} diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h deleted file mode 100644 index 5238471f98..0000000000 --- a/src/core/dbus-swap.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_swap_vtable[]; - -int bus_swap_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); -int bus_swap_commit_properties(Unit *u); diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c deleted file mode 100644 index 6858b1ce72..0000000000 --- a/src/core/dbus-target.c +++ /dev/null @@ -1,26 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "dbus-target.h" -#include "unit.h" - -const sd_bus_vtable bus_target_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h deleted file mode 100644 index 9be5ce06b7..0000000000 --- a/src/core/dbus-target.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index efbb0e8915..0000000000 --- a/src/core/dbus-timer.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - -static int property_get_monotonic_timers( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Timer *t = userdata; - TimerValue *v; - int r; - - assert(bus); - assert(reply); - assert(t); - - r = sd_bus_message_open_container(reply, 'a', "(stt)"); - if (r < 0) - return r; - - LIST_FOREACH(value, v, t->values) { - _cleanup_free_ char *buf = NULL; - const char *s; - size_t l; - - if (v->base == TIMER_CALENDAR) - continue; - - s = timer_base_to_string(v->base); - assert(endswith(s, "Sec")); - - /* s/Sec/USec/ */ - l = strlen(s); - buf = new(char, l+2); - if (!buf) - return -ENOMEM; - - memcpy(buf, s, l-3); - memcpy(buf+l-3, "USec", 5); - - r = sd_bus_message_append(reply, "(stt)", buf, v->value, v->next_elapse); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_calendar_timers( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Timer *t = userdata; - TimerValue *v; - int r; - - assert(bus); - assert(reply); - assert(t); - - r = sd_bus_message_open_container(reply, 'a', "(sst)"); - if (r < 0) - return r; - - LIST_FOREACH(value, v, t->values) { - _cleanup_free_ char *buf = NULL; - - if (v->base != TIMER_CALENDAR) - continue; - - r = calendar_spec_to_string(v->calendar_spec, &buf); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "(sst)", timer_base_to_string(v->base), buf, v->next_elapse); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_unit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata, *trigger; - - assert(bus); - assert(reply); - assert(u); - - trigger = UNIT_TRIGGER(u); - - return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); -} - -static int property_get_next_elapse_monotonic( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Timer *t = userdata; - usec_t x; - - assert(bus); - assert(reply); - assert(t); - - if (t->next_elapse_monotonic_or_boottime <= 0) - x = 0; - else if (t->wake_system) { - usec_t a, b; - - a = now(CLOCK_MONOTONIC); - b = now(clock_boottime_or_monotonic()); - - if (t->next_elapse_monotonic_or_boottime + a > b) - x = t->next_elapse_monotonic_or_boottime + a - b; - else - x = 0; - } else - x = t->next_elapse_monotonic_or_boottime; - - return sd_bus_message_append(reply, "t", x); -} - -const sd_bus_vtable bus_timer_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - 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 -}; - -static int bus_timer_set_transient_property( - Timer *t, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(t); - assert(name); - assert(message); - - if (STR_IN_SET(name, - "OnActiveSec", - "OnBootSec", - "OnStartupSec", - "OnUnitActiveSec", - "OnUnitInactiveSec")) { - - TimerValue *v; - TimerBase b = _TIMER_BASE_INVALID; - usec_t u = 0; - - b = timer_base_from_string(name); - if (b < 0) - return -EINVAL; - - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - char time[FORMAT_TIMESPAN_MAX]; - - unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); - - v = new0(TimerValue, 1); - if (!v) - return -ENOMEM; - - v->base = b; - v->value = u; - - LIST_PREPEND(value, t->values, v); - } - - return 1; - - } else if (streq(name, "OnCalendar")) { - - TimerValue *v; - CalendarSpec *c = NULL; - const char *str; - - r = sd_bus_message_read(message, "s", &str); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - r = calendar_spec_from_string(str, &c); - if (r < 0) - return r; - - unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, str); - - v = new0(TimerValue, 1); - if (!v) { - calendar_spec_free(c); - return -ENOMEM; - } - - v->base = TIMER_CALENDAR; - v->calendar_spec = c; - - LIST_PREPEND(value, t->values, v); - } - - return 1; - - } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) { - usec_t u = 0; - - if (streq(name, "AccuracySec")) - log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); - - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - t->accuracy_usec = u; - unit_write_drop_in_private_format(UNIT(t), mode, name, "AccuracySec=" USEC_FMT "us", u); - } - - return 1; - - } 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) { - t->random_usec = u; - unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=" USEC_FMT "us", u); - } - - return 1; - - } else if (streq(name, "WakeSystem")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - t->wake_system = b; - unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", 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", name, yes_no(b)); - } - - return 1; - } - - return 0; -} - -int bus_timer_set_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - Timer *t = TIMER(u); - int r; - - assert(t); - assert(name); - assert(message); - - if (u->transient && u->load_state == UNIT_STUB) { - r = bus_timer_set_transient_property(t, name, message, mode, error); - if (r != 0) - return r; - } - - return 0; -} diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h deleted file mode 100644 index 39053dc4a2..0000000000 --- a/src/core/dbus-timer.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_timer_vtable[]; - -int bus_timer_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c deleted file mode 100644 index b55d2cf735..0000000000 --- a/src/core/dbus-unit.c +++ /dev/null @@ -1,1423 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "cgroup-util.h" -#include "dbus-unit.h" -#include "dbus.h" -#include "fd-util.h" -#include "locale-util.h" -#include "log.h" -#include "process-util.h" -#include "selinux-access.h" -#include "signal-util.h" -#include "special.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); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); - -static int property_get_names( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - Iterator i; - const char *t; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = sd_bus_message_open_container(reply, 'a', "s"); - if (r < 0) - return r; - - SET_FOREACH(t, u->names, i) { - r = sd_bus_message_append(reply, "s", t); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_following( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata, *f; - - assert(bus); - assert(reply); - assert(u); - - f = unit_following(u); - return sd_bus_message_append(reply, "s", f ? f->id : ""); -} - -static int property_get_dependencies( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Set *s = *(Set**) userdata; - Iterator j; - Unit *u; - int r; - - assert(bus); - assert(reply); - - r = sd_bus_message_open_container(reply, 'a', "s"); - if (r < 0) - return r; - - SET_FOREACH(u, s, j) { - r = sd_bus_message_append(reply, "s", u->id); - if (r < 0) - return r; - } - - 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, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_description(u)); -} - -static int property_get_active_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u))); -} - -static int property_get_sub_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u)); -} - -static int property_get_unit_file_preset( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = unit_get_unit_file_preset(u); - - return sd_bus_message_append(reply, "s", - r < 0 ? "": - r > 0 ? "enabled" : "disabled"); -} - -static int property_get_unit_file_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u))); -} - -static int property_get_can_start( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start); -} - -static int property_get_can_stop( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - /* On the lower levels we assume that every unit we can start - * we can also stop */ - - return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop); -} - -static int property_get_can_reload( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_reload(u)); -} - -static int property_get_can_isolate( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start); -} - -static int property_get_job( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - if (!u->job) - return sd_bus_message_append(reply, "(uo)", 0, "/"); - - p = job_dbus_path(u->job); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(uo)", u->job->id, p); -} - -static int property_get_need_daemon_reload( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u)); -} - -static int property_get_conditions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - const char *(*to_string)(ConditionType type) = NULL; - Condition **list = userdata, *c; - int r; - - assert(bus); - assert(reply); - assert(list); - - to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string; - - r = sd_bus_message_open_container(reply, 'a', "(sbbsi)"); - if (r < 0) - return r; - - LIST_FOREACH(conditions, c, *list) { - int tristate; - - tristate = - c->result == CONDITION_UNTESTED ? 0 : - c->result == CONDITION_SUCCEEDED ? 1 : -1; - - r = sd_bus_message_append(reply, "(sbbsi)", - to_string(c->type), - c->trigger, c->negate, - c->parameter, tristate); - if (r < 0) - return r; - - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_load_error( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - if (u->load_error != 0) - sd_bus_error_set_errno(&e, u->load_error); - - return sd_bus_message_append(reply, "(ss)", e.name, e.message); -} - -static int bus_verify_manage_units_async_full( - Unit *u, - const char *verb, - int capability, - const char *polkit_message, - sd_bus_message *call, - sd_bus_error *error) { - - const char *details[9] = { - "unit", u->id, - "verb", verb, - }; - - if (polkit_message) { - details[4] = "polkit.message"; - details[5] = polkit_message; - details[6] = "polkit.gettext_domain"; - details[7] = GETTEXT_PACKAGE; - } - - return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error); -} - -int bus_unit_method_start_generic( - sd_bus_message *message, - Unit *u, - JobType job_type, - bool reload_if_possible, - sd_bus_error *error) { - - const char *smode; - JobMode mode; - _cleanup_free_ char *verb = NULL; - static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { - [JOB_START] = N_("Authentication is required to start '$(unit)'."), - [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), - [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), - [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), - [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), - }; - int r; - - assert(message); - assert(u); - assert(job_type >= 0 && job_type < _JOB_TYPE_MAX); - - r = mac_selinux_unit_access_check( - u, message, - job_type_to_access_method(job_type), - error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "s", &smode); - if (r < 0) - return r; - - mode = job_mode_from_string(smode); - if (mode < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); - - if (reload_if_possible) - verb = strjoin("reload-or-", job_type_to_string(job_type), NULL); - else - verb = strdup(job_type_to_string(job_type)); - if (!verb) - return -ENOMEM; - - r = bus_verify_manage_units_async_full( - u, - verb, - CAP_SYS_ADMIN, - job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL, - message, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error); -} - -static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_START, false, error); -} - -static int method_stop(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_STOP, false, error); -} - -static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_RELOAD, false, error); -} - -static int method_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_RESTART, false, error); -} - -static int method_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, false, error); -} - -static int method_reload_or_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_RESTART, true, error); -} - -static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error); -} - -int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Unit *u = userdata; - const char *swho; - int32_t signo; - KillWho who; - int r; - - assert(message); - assert(u); - - r = mac_selinux_unit_access_check(u, message, "stop", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "si", &swho, &signo); - if (r < 0) - return r; - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho); - } - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range."); - - r = bus_verify_manage_units_async_full( - u, - "kill", - CAP_KILL, - N_("Authentication is required to kill '$(unit)'."), - message, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = unit_kill(u, who, signo, error); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Unit *u = userdata; - int r; - - assert(message); - assert(u); - - r = mac_selinux_unit_access_check(u, message, "reload", error); - if (r < 0) - return r; - - r = bus_verify_manage_units_async_full( - u, - "reset-failed", - CAP_SYS_ADMIN, - N_("Authentication is required to reset the \"failed\" state of '$(unit)'."), - message, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - unit_reset_failed(u); - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Unit *u = userdata; - int runtime, r; - - assert(message); - assert(u); - - r = mac_selinux_unit_access_check(u, message, "start", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "b", &runtime); - if (r < 0) - return r; - - r = bus_verify_manage_units_async_full( - u, - "set-property", - CAP_SYS_ADMIN, - N_("Authentication is required to set properties on '$(unit)'."), - message, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable bus_unit_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST), - 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("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), 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("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), 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), - SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), - 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), - SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0), - SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0), - BUS_PROPERTY_DUAL_TIMESTAMP("StateChangeTimestamp", offsetof(Unit, state_change_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("InactiveEnterTimestamp", offsetof(Unit, inactive_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanStart", "b", property_get_can_start, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST), - 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("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), - SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0), - 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("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ - SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), - - SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -static int property_get_slice( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", unit_slice_name(u)); -} - -static int property_get_current_memory( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t sz = (uint64_t) -1; - Unit *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = unit_get_memory_current(u, &sz); - if (r < 0 && r != -ENODATA) - log_unit_warning_errno(u, r, "Failed to get memory.usage_in_bytes attribute: %m"); - - return sd_bus_message_append(reply, "t", sz); -} - -static int property_get_current_tasks( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t cn = (uint64_t) -1; - Unit *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = unit_get_tasks_current(u, &cn); - if (r < 0 && r != -ENODATA) - log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m"); - - return sd_bus_message_append(reply, "t", cn); -} - -static int property_get_cpu_usage( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - nsec_t ns = (nsec_t) -1; - Unit *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = unit_get_cpu_usage(u, &ns); - if (r < 0 && r != -ENODATA) - log_unit_warning_errno(u, r, "Failed to get cpuacct.usage attribute: %m"); - - return sd_bus_message_append(reply, "t", ns); -} - -static int property_get_cgroup( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Unit *u = userdata; - const char *t; - - assert(bus); - assert(reply); - assert(u); - - /* Three cases: a) u->cgroup_path is NULL, in which case the - * unit has no control group, which we report as the empty - * string. b) u->cgroup_path is the empty string, which - * indicates the root cgroup, which we report as "/". c) all - * other cases we report as-is. */ - - if (u->cgroup_path) - t = isempty(u->cgroup_path) ? "/" : u->cgroup_path; - else - t = ""; - - return sd_bus_message_append(reply, "s", t); -} - -static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) { - _cleanup_free_ char *buf = NULL, *cmdline = NULL; - int r; - - assert(reply); - assert(pid > 0); - - r = set_put(pids, PID_TO_PTR(pid)); - if (r == -EEXIST || r == 0) - return 0; - if (r < 0) - return r; - - if (!p) { - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf); - if (r == -ESRCH) - return 0; - if (r < 0) - return r; - - p = buf; - } - - (void) get_process_cmdline(pid, 0, true, &cmdline); - - return sd_bus_message_append(reply, - "(sus)", - p, - (uint32_t) pid, - cmdline); -} - -static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(reply); - assert(p); - - r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); - if (r == ENOENT) - return 0; - if (r < 0) - return r; - - for (;;) { - pid_t pid; - - r = cg_read_pid(f, &pid); - if (r < 0) - return r; - if (r == 0) - break; - - if (is_kernel_thread(pid) > 0) - continue; - - r = append_process(reply, p, pid, pids); - if (r < 0) - return r; - } - - r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *g = NULL, *j = NULL; - - r = cg_read_subgroup(d, &g); - if (r < 0) - return r; - if (r == 0) - break; - - j = strjoin(p, "/", g, NULL); - if (!j) - return -ENOMEM; - - r = append_cgroup(reply, j, pids); - if (r < 0) - return r; - } - - return 0; -} - -int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(set_freep) Set *pids = NULL; - Unit *u = userdata; - pid_t pid; - int r; - - assert(message); - - pids = set_new(NULL); - if (!pids) - return -ENOMEM; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(sus)"); - if (r < 0) - return r; - - if (u->cgroup_path) { - r = append_cgroup(reply, u->cgroup_path, pids); - if (r < 0) - return r; - } - - /* The main and control pids might live outside of the cgroup, hence fetch them separately */ - pid = unit_main_pid(u); - if (pid > 0) { - r = append_process(reply, NULL, pid, pids); - if (r < 0) - return r; - } - - pid = unit_control_pid(u); - if (pid > 0) { - r = append_process(reply, NULL, pid, pids); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -const sd_bus_vtable bus_unit_cgroup_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), - SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), - SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), - SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), - SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), - SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -static int send_new_signal(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *p = NULL; - Unit *u = userdata; - int r; - - assert(bus); - assert(u); - - p = unit_dbus_path(u); - if (!p) - return -ENOMEM; - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitNew"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "so", u->id, p); - if (r < 0) - return r; - - return sd_bus_send(bus, m, NULL); -} - -static int send_changed_signal(sd_bus *bus, void *userdata) { - _cleanup_free_ char *p = NULL; - Unit *u = userdata; - int r; - - assert(bus); - assert(u); - - p = unit_dbus_path(u); - if (!p) - return -ENOMEM; - - /* Send a properties changed signal. First for the specific - * type, then for the generic unit. The clients may rely on - * this order to get atomic behavior if needed. */ - - r = sd_bus_emit_properties_changed_strv( - bus, p, - unit_dbus_interface_from_type(u->type), - NULL); - if (r < 0) - return r; - - return sd_bus_emit_properties_changed_strv( - bus, p, - "org.freedesktop.systemd1.Unit", - NULL); -} - -void bus_unit_send_change_signal(Unit *u) { - int r; - assert(u); - - if (u->in_dbus_queue) { - LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); - u->in_dbus_queue = false; - } - - if (!u->id) - return; - - r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id); - - u->sent_dbus_new_signal = true; -} - -static int send_removed_signal(sd_bus *bus, void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *p = NULL; - Unit *u = userdata; - int r; - - assert(bus); - assert(u); - - p = unit_dbus_path(u); - if (!p) - return -ENOMEM; - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnitRemoved"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "so", u->id, p); - if (r < 0) - return r; - - return sd_bus_send(bus, m, NULL); -} - -void bus_unit_send_removed_signal(Unit *u) { - int r; - assert(u); - - if (!u->sent_dbus_new_signal) - bus_unit_send_change_signal(u); - - if (!u->id) - return; - - r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id); -} - -int bus_unit_queue_job( - sd_bus_message *message, - Unit *u, - JobType type, - JobMode mode, - bool reload_if_possible, - sd_bus_error *error) { - - _cleanup_free_ char *path = NULL; - Job *j; - int r; - - assert(message); - assert(u); - assert(type >= 0 && type < _JOB_TYPE_MAX); - assert(mode >= 0 && mode < _JOB_MODE_MAX); - - r = mac_selinux_unit_access_check( - u, message, - job_type_to_access_method(type), - error); - if (r < 0) - return r; - - if (reload_if_possible && unit_can_reload(u)) { - if (type == JOB_RESTART) - type = JOB_RELOAD_OR_START; - else if (type == JOB_TRY_RESTART) - type = JOB_TRY_RELOAD; - } - - if (type == JOB_STOP && - (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && - unit_active_state(u) == UNIT_INACTIVE) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); - - 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_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, error, &j); - if (r < 0) - return r; - - if (sd_bus_message_get_bus(message) == u->manager->api_bus) { - if (!j->clients) { - r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(j->clients, message); - if (r < 0) - return r; - } - - path = job_dbus_path(j); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int bus_unit_set_transient_property( - Unit *u, - const char *name, - sd_bus_message *message, - UnitSetPropertiesMode mode, - sd_bus_error *error) { - - int r; - - assert(u); - assert(name); - assert(message); - - if (streq(name, "Description")) { - const char *d; - - r = sd_bus_message_read(message, "s", &d); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - r = unit_set_description(u, d); - if (r < 0) - return r; - - unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s", d); - } - - return 1; - - } else if (streq(name, "DefaultDependencies")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - u->default_dependencies = b; - unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "Slice")) { - Unit *slice; - const char *s; - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups."); - if (u->type == UNIT_SLICE) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units."); - if (unit_has_name(u, SPECIAL_INIT_SCOPE)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope"); - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s); - - /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be - * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare() - * instead of manager_load_unit() on purpose, here. */ - r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice); - if (r < 0) - return r; - - if (slice->type != UNIT_SLICE) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s); - - if (mode != UNIT_CHECK) { - r = unit_set_slice(u, slice); - if (r < 0) - return r; - - unit_write_drop_in_private_format(u, mode, name, "Slice=%s", s); - } - - return 1; - - } else if (STR_IN_SET(name, - "Requires", "RequiresOverridable", - "Requisite", "RequisiteOverridable", - "Wants", - "BindsTo", - "Conflicts", - "Before", "After", - "OnFailure", - "PropagatesReloadTo", "ReloadPropagatedFrom", - "PartOf")) { - - UnitDependency d; - const char *other; - - 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) - return r; - - while ((r = sd_bus_message_read(message, "s", &other)) > 0) { - if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other); - - if (mode != UNIT_CHECK) { - _cleanup_free_ char *label = NULL; - - r = unit_add_dependency_by_name(u, d, other, NULL, true); - if (r < 0) - return r; - - label = strjoin(name, "-", other, NULL); - if (!label) - return -ENOMEM; - - unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s", name, other); - } - - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - return 1; - } - - return 0; -} - -int bus_unit_set_properties( - Unit *u, - sd_bus_message *message, - UnitSetPropertiesMode mode, - bool commit, - sd_bus_error *error) { - - bool for_real = false; - unsigned n = 0; - int r; - - assert(u); - assert(message); - - /* We iterate through the array twice. First run we just check - * if all passed data is valid, second run actually applies - * it. This is to implement transaction-like behaviour without - * actually providing full transactions. */ - - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - return r; - - for (;;) { - const char *name; - - r = sd_bus_message_enter_container(message, 'r', "sv"); - if (r < 0) - return r; - if (r == 0) { - if (for_real || mode == UNIT_CHECK) - break; - - /* Reached EOF. Let's try again, and this time for realz... */ - r = sd_bus_message_rewind(message, false); - if (r < 0) - return r; - - for_real = true; - continue; - } - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (!UNIT_VTABLE(u)->bus_set_property) - return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties."); - - r = sd_bus_message_enter_container(message, 'v', NULL); - if (r < 0) - return r; - - r = UNIT_VTABLE(u)->bus_set_property(u, name, message, for_real ? mode : UNIT_CHECK, error); - if (r == 0 && u->transient && u->load_state == UNIT_STUB) - r = bus_unit_set_transient_property(u, name, message, for_real ? mode : UNIT_CHECK, error); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name); - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - n += for_real; - } - - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; - - if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties) - UNIT_VTABLE(u)->bus_commit_properties(u); - - return n; -} - -int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { - assert(u); - - 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 %s is masked.", u->id); - - if (u->load_state == UNIT_NOT_FOUND) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not found.", u->id); - - return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id); -} diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h deleted file mode 100644 index 4db88dbebc..0000000000 --- a/src/core/dbus-unit.h +++ /dev/null @@ -1,41 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "unit.h" - -extern const sd_bus_vtable bus_unit_vtable[]; -extern const sd_bus_vtable bus_unit_cgroup_vtable[]; - -void bus_unit_send_change_signal(Unit *u); -void bus_unit_send_removed_signal(Unit *u); - -int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); -int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error); - -int bus_unit_queue_job(sd_bus_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_method_get_processes(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 deleted file mode 100644 index 3422a02d68..0000000000 --- a/src/core/dbus.c +++ /dev/null @@ -1,1243 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-bus.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-manager.h" -#include "dbus-unit.h" -#include "dbus.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 - -static void destroy_bus(Manager *m, sd_bus **bus); - -int bus_send_queued_message(Manager *m) { - int r; - - assert(m); - - if (!m->queued_message) - return 0; - - /* If we cannot get rid of this message we won't dispatch any - * D-Bus messages, so that we won't end up wanting to queue - * another message. */ - - r = sd_bus_send(NULL, m->queued_message, NULL); - if (r < 0) - log_warning_errno(r, "Failed to send queued message: %m"); - - m->queued_message = sd_bus_message_unref(m->queued_message); - - return 0; -} - -int bus_forward_agent_released(Manager *m, const char *path) { - int r; - - assert(m); - assert(path); - - if (!MANAGER_IS_SYSTEM(m)) - return 0; - - if (!m->system_bus) - return 0; - - /* If we are running a system instance we forward the agent message on the system bus, so that the user - * instances get notified about this, too */ - - r = sd_bus_emit_signal(m->system_bus, - "/org/freedesktop/systemd1/agent", - "org.freedesktop.systemd1.Agent", - "Released", - "s", path); - if (r < 0) - return log_warning_errno(r, "Failed to propagate agent release message: %m"); - - return 1; -} - -static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Manager *m = userdata; - const char *cgroup; - uid_t sender_uid; - int r; - - assert(message); - assert(m); - - /* only accept org.freedesktop.systemd1.Agent from UID=0 */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &sender_uid); - if (r < 0 || sender_uid != 0) - return 0; - - /* parse 'cgroup-empty' notification */ - r = sd_bus_message_read(message, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - manager_notify_cgroup_empty(m, cgroup); - return 0; -} - -static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - sd_bus *bus; - - assert(message); - assert(m); - assert_se(bus = sd_bus_message_get_bus(message)); - - if (bus == m->api_bus) - destroy_bus(m, &m->api_bus); - if (bus == m->system_bus) - destroy_bus(m, &m->system_bus); - if (set_remove(m->private_buses, bus)) { - log_debug("Got disconnect on private connection."); - destroy_bus(m, &bus); - } - - return 0; -} - -static int signal_activation_request(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { - _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; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) || - manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) { - r = sd_bus_error_setf(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); - goto failed; - } - - r = manager_load_unit(m, name, NULL, &error, &u); - if (r < 0) - goto failed; - - if (u->refuse_manual_start) { - r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only.", u->id); - goto failed; - } - - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); - if (r < 0) - goto failed; - - /* Successfully queued, that's it for us */ - return 0; - -failed: - if (!sd_bus_error_is_set(&error)) - sd_bus_error_set_errno(&error, r); - - log_debug("D-Bus activation failed for %s: %s", name, bus_error_message(&error, r)); - - r = sd_bus_message_new_signal(sd_bus_message_get_bus(message), &reply, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure"); - if (r < 0) { - bus_log_create_error(r); - return 0; - } - - r = sd_bus_message_append(reply, "sss", name, error.name, error.message); - if (r < 0) { - bus_log_create_error(r); - return 0; - } - - r = sd_bus_send_to(NULL, reply, "org.freedesktop.DBus", NULL); - if (r < 0) - return log_error_errno(r, "Failed to respond with to bus activation request: %m"); - - return 0; -} - -#ifdef HAVE_SELINUX -static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *verb, *path; - Unit *u = NULL; - Job *j; - int r; - - assert(message); - - /* Our own method calls are all protected individually with - * selinux checks, but the built-in interfaces need to be - * protected too. */ - - if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set")) - verb = "reload"; - else if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", NULL) || - sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", NULL) || - sd_bus_message_is_method_call(message, "org.freedesktop.DBus.ObjectManager", NULL) || - sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Peer", NULL)) - verb = "status"; - else - return 0; - - path = sd_bus_message_get_path(message); - - if (object_path_startswith("/org/freedesktop/systemd1", path)) { - - r = mac_selinux_access_check(message, verb, error); - if (r < 0) - return r; - - return 0; - } - - if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) { - _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); - if (r < 0) - return 0; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return 0; - - u = manager_get_unit_by_pid(m, pid); - } else { - r = manager_get_job_from_dbus_path(m, path, &j); - if (r >= 0) - u = j->unit; - else - manager_load_unit_from_dbus_path(m, path, NULL, &u); - } - - if (!u) - return 0; - - r = mac_selinux_unit_access_check(u, message, verb, error); - if (r < 0) - return r; - - return 0; -} -#endif - -static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Job *j; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = manager_get_job_from_dbus_path(m, path, &j); - if (r < 0) - return 0; - - *found = j; - return 1; -} - -static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) { - Unit *u; - int r; - - assert(m); - assert(bus); - assert(path); - - if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - pid_t pid; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - u = manager_get_unit_by_pid(m, pid); - } else { - r = manager_load_unit_from_dbus_path(m, path, error, &u); - if (r < 0) - return 0; - } - - if (!u) - return 0; - - *unit = u; - return 1; -} - -static int bus_unit_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - return find_unit(m, bus, path, (Unit**) found, error); -} - -static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Unit *u; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = find_unit(m, bus, path, &u, error); - if (r <= 0) - return r; - - if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) - return 0; - - *found = u; - return 1; -} - -static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Unit *u; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = find_unit(m, bus, path, &u, error); - if (r <= 0) - return r; - - if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) - return 0; - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return 0; - - *found = u; - return 1; -} - -static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - CGroupContext *c; - Unit *u; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = find_unit(m, bus, path, &u, error); - if (r <= 0) - return r; - - if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) - return 0; - - c = unit_get_cgroup_context(u); - if (!c) - return 0; - - *found = c; - return 1; -} - -static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - ExecContext *c; - Unit *u; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = find_unit(m, bus, path, &u, error); - if (r <= 0) - return r; - - if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) - return 0; - - c = unit_get_exec_context(u); - if (!c) - return 0; - - *found = c; - return 1; -} - -static int bus_kill_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - KillContext *c; - Unit *u; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - r = find_unit(m, bus, path, &u, error); - if (r <= 0) - return r; - - if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) - return 0; - - c = unit_get_kill_context(u); - if (!c) - return 0; - - *found = c; - return 1; -} - -static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_free_ char **l = NULL; - Manager *m = userdata; - unsigned k = 0; - Iterator i; - Job *j; - - l = new0(char*, hashmap_size(m->jobs)+1); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(j, m->jobs, i) { - l[k] = job_dbus_path(j); - if (!l[k]) - return -ENOMEM; - - k++; - } - - assert(hashmap_size(m->jobs) == k); - - *nodes = l; - l = NULL; - - return k; -} - -static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_free_ char **l = NULL; - Manager *m = userdata; - unsigned k = 0; - Iterator i; - Unit *u; - - l = new0(char*, hashmap_size(m->units)+1); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(u, m->units, i) { - l[k] = unit_dbus_path(u); - if (!l[k]) - return -ENOMEM; - - k++; - } - - *nodes = l; - l = NULL; - - return k; -} - -static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { - UnitType t; - int r; - - assert(m); - assert(bus); - -#ifdef HAVE_SELINUX - r = sd_bus_add_filter(bus, NULL, mac_selinux_filter, m); - if (r < 0) - return log_error_errno(r, "Failed to add SELinux access filter: %m"); -#endif - - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to register Manager vtable: %m"); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/job", "org.freedesktop.systemd1.Job", bus_job_vtable, bus_job_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register Job vtable: %m"); - - r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/job", bus_job_enumerate, m); - if (r < 0) - return log_error_errno(r, "Failed to add job enumerator: %m"); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", "org.freedesktop.systemd1.Unit", bus_unit_vtable, bus_unit_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register Unit vtable: %m"); - - r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/unit", bus_unit_enumerate, m); - if (r < 0) - return log_error_errno(r, "Failed to add job enumerator: %m"); - - for (t = 0; t < _UNIT_TYPE_MAX; t++) { - const char *interface; - - assert_se(interface = unit_dbus_interface_from_type(t)); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); - - if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); - } - - if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); - } - - if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); - } - } - - return 0; -} - -static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { - int r; - - assert(m); - assert(bus); - - r = sd_bus_add_match( - bus, - NULL, - "sender='org.freedesktop.DBus.Local'," - "type='signal'," - "path='/org/freedesktop/DBus/Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - signal_disconnected, m); - - if (r < 0) - return log_error_errno(r, "Failed to register match for Disconnected message: %m"); - - return 0; -} - -static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - _cleanup_close_ int nfd = -1; - Manager *m = userdata; - sd_id128_t id; - int r; - - assert(s); - assert(m); - - nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (nfd < 0) { - log_warning_errno(errno, "Failed to accept private connection, ignoring: %m"); - return 0; - } - - if (set_size(m->private_buses) >= CONNECTIONS_MAX) { - log_warning("Too many concurrent connections, refusing"); - return 0; - } - - r = set_ensure_allocated(&m->private_buses, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - r = sd_bus_new(&bus); - if (r < 0) { - log_warning_errno(r, "Failed to allocate new private connection bus: %m"); - return 0; - } - - r = sd_bus_set_fd(bus, nfd, nfd); - if (r < 0) { - log_warning_errno(r, "Failed to set fd on new connection bus: %m"); - return 0; - } - - nfd = -1; - - r = bus_check_peercred(bus); - if (r < 0) { - log_warning_errno(r, "Incoming private connection from unprivileged client, refusing: %m"); - return 0; - } - - assert_se(sd_id128_randomize(&id) >= 0); - - r = sd_bus_set_server(bus, 1, id); - if (r < 0) { - log_warning_errno(r, "Failed to enable server support for new connection bus: %m"); - return 0; - } - - r = sd_bus_negotiate_creds(bus, 1, - SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| - SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT); - if (r < 0) { - log_warning_errno(r, "Failed to enable credentials for new connection: %m"); - return 0; - } - - r = sd_bus_start(bus); - if (r < 0) { - log_warning_errno(r, "Failed to start new connection bus: %m"); - return 0; - } - - r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) { - log_warning_errno(r, "Failed to attach new connection bus to event loop: %m"); - return 0; - } - - r = bus_setup_disconnected_match(m, bus); - if (r < 0) - return 0; - - r = bus_setup_api_vtables(m, bus); - if (r < 0) { - log_warning_errno(r, "Failed to set up API vtables on new connection bus: %m"); - return 0; - } - - r = set_put(m->private_buses, bus); - if (r < 0) { - log_warning_errno(r, "Failed to add new connection bus to set: %m"); - return 0; - } - - bus = NULL; - - log_debug("Accepted new private connection."); - - return 0; -} - -int manager_sync_bus_names(Manager *m, sd_bus *bus) { - _cleanup_strv_free_ char **names = NULL; - const char *name; - Iterator i; - Unit *u; - int r; - - assert(m); - assert(bus); - - r = sd_bus_list_names(bus, &names, NULL); - if (r < 0) - return log_error_errno(r, "Failed to get initial list of names: %m"); - - /* 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); - - 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; -} - -static int bus_setup_api(Manager *m, sd_bus *bus) { - Iterator i; - char *name; - Unit *u; - int r; - - assert(m); - assert(bus); - - /* Let's make sure we have enough credential bits so that we can make security and selinux decisions */ - r = sd_bus_negotiate_creds(bus, 1, - SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| - SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT); - if (r < 0) - log_warning_errno(r, "Failed to enable credential passing, ignoring: %m"); - - r = bus_setup_api_vtables(m, bus); - if (r < 0) - return r; - - HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) { - r = unit_install_bus_match(u, bus, name); - if (r < 0) - log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); - } - - r = sd_bus_add_match( - bus, - NULL, - "type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.systemd1.Activator'," - "member='ActivationRequest'", - signal_activation_request, m); - if (r < 0) - log_warning_errno(r, "Failed to subscribe to activation signal: %m"); - - /* Allow replacing of our name, to ease implementation of - * reexecution, where we keep the old connection open until - * after the new connection is set up and the name installed - * to allow clients to synchronously wait for reexecution to - * finish */ - r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - 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_(sd_bus_unrefp) sd_bus *bus = NULL; - int r; - - if (m->api_bus) - return 0; - - /* The API and system bus is the same if we are running in system mode */ - if (MANAGER_IS_SYSTEM(m) && m->system_bus) - bus = sd_bus_ref(m->system_bus); - else { - if (MANAGER_IS_SYSTEM(m)) - r = sd_bus_open_system(&bus); - else - r = sd_bus_open_user(&bus); - - if (r < 0) { - log_debug("Failed to connect to API bus, retrying later..."); - return 0; - } - - r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) { - log_error_errno(r, "Failed to attach API bus to event loop: %m"); - return 0; - } - - r = bus_setup_disconnected_match(m, bus); - if (r < 0) - return 0; - } - - r = bus_setup_api(m, bus); - if (r < 0) { - log_error_errno(r, "Failed to set up API bus: %m"); - return 0; - } - - m->api_bus = bus; - bus = NULL; - - return 0; -} - -static int bus_setup_system(Manager *m, sd_bus *bus) { - int r; - - assert(m); - assert(bus); - - /* if we are a user instance we get the Released message via the system bus */ - if (MANAGER_IS_USER(m)) { - r = sd_bus_add_match( - bus, - NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); - if (r < 0) - log_warning_errno(r, "Failed to register Released match on system bus: %m"); - } - - log_debug("Successfully connected to system bus."); - return 0; -} - -static int bus_init_system(Manager *m) { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - int r; - - if (m->system_bus) - return 0; - - /* The API and system bus is the same if we are running in system mode */ - if (MANAGER_IS_SYSTEM(m) && m->api_bus) { - m->system_bus = sd_bus_ref(m->api_bus); - return 0; - } - - r = sd_bus_open_system(&bus); - if (r < 0) { - log_debug("Failed to connect to system bus, retrying later..."); - return 0; - } - - r = bus_setup_disconnected_match(m, bus); - if (r < 0) - return 0; - - r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) { - log_error_errno(r, "Failed to attach system bus to event loop: %m"); - return 0; - } - - r = bus_setup_system(m, bus); - if (r < 0) { - log_error_errno(r, "Failed to set up system bus: %m"); - return 0; - } - - m->system_bus = bus; - bus = NULL; - - return 0; -} - -static int bus_init_private(Manager *m) { - _cleanup_close_ int fd = -1; - union sockaddr_union sa = { - .un.sun_family = AF_UNIX - }; - sd_event_source *s; - socklen_t salen; - int r; - - assert(m); - - if (m->private_listen_fd >= 0) - return 0; - - /* We don't need the private socket if we have kdbus */ - if (m->kdbus_fd >= 0) - return 0; - - if (MANAGER_IS_SYSTEM(m)) { - - /* We want the private bus only when running as init */ - if (getpid() != 1) - return 0; - - strcpy(sa.un.sun_path, "/run/systemd/private"); - salen = SOCKADDR_UN_LEN(sa.un); - } else { - size_t left = sizeof(sa.un.sun_path); - char *p = sa.un.sun_path; - const char *e; - - e = secure_getenv("XDG_RUNTIME_DIR"); - if (!e) { - log_error("Failed to determine XDG_RUNTIME_DIR"); - return -EHOSTDOWN; - } - - left = strpcpy(&p, left, e); - left = strpcpy(&p, left, "/systemd/private"); - - salen = sizeof(sa.un) - left; - } - - (void) mkdir_parents_label(sa.un.sun_path, 0755); - (void) unlink(sa.un.sun_path); - - fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate private socket: %m"); - - r = bind(fd, &sa.sa, salen); - if (r < 0) - return log_error_errno(errno, "Failed to bind private socket: %m"); - - r = listen(fd, SOMAXCONN); - if (r < 0) - return log_error_errno(errno, "Failed to make private socket listening: %m"); - - r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m); - if (r < 0) - return log_error_errno(r, "Failed to allocate event source: %m"); - - (void) sd_event_source_set_description(s, "bus-connection"); - - m->private_listen_fd = fd; - m->private_listen_event_source = s; - fd = -1; - - log_debug("Successfully created private D-Bus server."); - - return 0; -} - -int bus_init(Manager *m, bool try_bus_connect) { - int r; - - if (try_bus_connect) { - r = bus_init_system(m); - if (r < 0) - return r; - - r = bus_init_api(m); - if (r < 0) - return r; - } - - r = bus_init_private(m); - if (r < 0) - return r; - - return 0; -} - -static void destroy_bus(Manager *m, sd_bus **bus) { - Iterator i; - Job *j; - - assert(m); - assert(bus); - - if (!*bus) - return; - - /* Get rid of tracked clients on this bus */ - if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus) - m->subscribed = sd_bus_track_unref(m->subscribed); - - HASHMAP_FOREACH(j, m->jobs, i) - if (j->clients && sd_bus_track_get_bus(j->clients) == *bus) - j->clients = sd_bus_track_unref(j->clients); - - /* Get rid of queued message on this bus */ - if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus) - m->queued_message = sd_bus_message_unref(m->queued_message); - - /* Possibly flush unwritten data, but only if we are - * unprivileged, since we don't want to sync here */ - if (!MANAGER_IS_SYSTEM(m)) - sd_bus_flush(*bus); - - /* And destroy the object */ - sd_bus_close(*bus); - *bus = sd_bus_unref(*bus); -} - -void bus_done(Manager *m) { - sd_bus *b; - - assert(m); - - if (m->api_bus) - destroy_bus(m, &m->api_bus); - if (m->system_bus) - destroy_bus(m, &m->system_bus); - while ((b = set_steal_first(m->private_buses))) - destroy_bus(m, &b); - - m->private_buses = set_free(m->private_buses); - - m->subscribed = sd_bus_track_unref(m->subscribed); - m->deserialized_subscribed = strv_free(m->deserialized_subscribed); - - if (m->private_listen_event_source) - m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source); - - m->private_listen_fd = safe_close(m->private_listen_fd); - - bus_verify_polkit_async_registry_free(m->polkit_registry); -} - -int bus_fdset_add_all(Manager *m, FDSet *fds) { - Iterator i; - sd_bus *b; - int fd; - - assert(m); - assert(fds); - - /* When we are about to reexecute we add all D-Bus fds to the - * set to pass over to the newly executed systemd. They won't - * be used there however, except thatt they are closed at the - * very end of deserialization, those making it possible for - * clients to synchronously wait for systemd to reexec by - * simply waiting for disconnection */ - - if (m->api_bus) { - fd = sd_bus_get_fd(m->api_bus); - if (fd >= 0) { - fd = fdset_put_dup(fds, fd); - if (fd < 0) - return fd; - } - } - - SET_FOREACH(b, m->private_buses, i) { - fd = sd_bus_get_fd(b); - if (fd >= 0) { - fd = fdset_put_dup(fds, fd); - if (fd < 0) - return fd; - } - } - - /* We don't offer any APIs on the system bus (well, unless it - * is the same as the API bus) hence we don't bother with it - * here */ - - return 0; -} - -int bus_foreach_bus( - Manager *m, - sd_bus_track *subscribed2, - int (*send_message)(sd_bus *bus, void *userdata), - void *userdata) { - - Iterator i; - sd_bus *b; - int r, ret = 0; - - /* Send to all direct buses, unconditionally */ - SET_FOREACH(b, m->private_buses, i) { - r = send_message(b, userdata); - if (r < 0) - ret = r; - } - - /* Send to API bus, but only if somebody is subscribed */ - if (sd_bus_track_count(m->subscribed) > 0 || - sd_bus_track_count(subscribed2) > 0) { - r = send_message(m->api_bus, userdata); - if (r < 0) - ret = r; - } - - return ret; -} - -void bus_track_serialize(sd_bus_track *t, FILE *f) { - const char *n; - - assert(f); - - for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) - fprintf(f, "subscribed=%s\n", n); -} - -int bus_track_deserialize_item(char ***l, const char *line) { - const char *e; - int r; - - assert(l); - assert(line); - - e = startswith(line, "subscribed="); - if (!e) - return 0; - - r = strv_extend(l, e); - if (r < 0) - return r; - - return 1; -} - -int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { - int r = 0; - - assert(m); - assert(t); - assert(l); - - if (!strv_isempty(*l) && m->api_bus) { - char **i; - - if (!*t) { - r = sd_bus_track_new(m->api_bus, t, NULL, NULL); - if (r < 0) - return r; - } - - r = 0; - STRV_FOREACH(i, *l) { - int k; - - k = sd_bus_track_add_name(*t, *i); - if (k < 0) - r = k; - } - } - - *l = strv_free(*l); - - return r; -} - -int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error); -} - -int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error); -} - -int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error); -} - -int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error); -} diff --git a/src/core/dbus.h b/src/core/dbus.h deleted file mode 100644 index 6baaffbd75..0000000000 --- a/src/core/dbus.h +++ /dev/null @@ -1,44 +0,0 @@ -#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 . -***/ - -#include "manager.h" - -int bus_send_queued_message(Manager *m); - -int bus_init(Manager *m, bool try_bus_connect); -void bus_done(Manager *m); - -int bus_fdset_add_all(Manager *m, FDSet *fds); - -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); -int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error); -int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error); -int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error); - -int bus_forward_agent_released(Manager *m, const char *path); diff --git a/src/core/device.c b/src/core/device.c deleted file mode 100644 index 16e56efcc3..0000000000 --- a/src/core/device.c +++ /dev/null @@ -1,876 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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" - -static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { - [DEVICE_DEAD] = UNIT_INACTIVE, - [DEVICE_TENTATIVE] = UNIT_ACTIVATING, - [DEVICE_PLUGGED] = UNIT_ACTIVE, -}; - -static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); - -static void device_unset_sysfs(Device *d) { - Hashmap *devices; - Device *first; - - assert(d); - - if (!d->sysfs) - return; - - /* Remove this unit from the chain of devices which share the - * same sysfs path. */ - devices = UNIT(d)->manager->devices_by_sysfs; - first = hashmap_get(devices, d->sysfs); - LIST_REMOVE(same_sysfs, first, d); - - if (first) - hashmap_remove_and_replace(devices, d->sysfs, first->sysfs, first); - else - hashmap_remove(devices, d->sysfs); - - d->sysfs = mfree(d->sysfs); -} - -static int device_set_sysfs(Device *d, const char *sysfs) { - Device *first; - char *copy; - int r; - - assert(d); - - if (streq_ptr(d->sysfs, sysfs)) - return 0; - - r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops); - if (r < 0) - return r; - - copy = strdup(sysfs); - if (!copy) - return -ENOMEM; - - device_unset_sysfs(d); - - first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs); - LIST_PREPEND(same_sysfs, first, d); - - r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first); - if (r < 0) { - LIST_REMOVE(same_sysfs, first, d); - free(copy); - return r; - } - - d->sysfs = copy; - - return 0; -} - -static void device_init(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - assert(UNIT(d)->load_state == UNIT_STUB); - - /* In contrast to all other unit types we timeout jobs waiting - * for devices by default. This is because they otherwise wait - * indefinitely for plugged in devices, something which cannot - * happen for the other units since their operations time out - * anyway. */ - u->job_timeout = u->manager->default_timeout_start_usec; - - u->ignore_on_isolate = true; -} - -static void device_done(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - - device_unset_sysfs(d); -} - -static void device_set_state(Device *d, DeviceState state) { - DeviceState old_state; - assert(d); - - old_state = d->state; - d->state = state; - - if (state != old_state) - log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state)); - - unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true); -} - -static int device_coldplug(Unit *u) { - Device *d = DEVICE(u); - - assert(d); - assert(d->state == DEVICE_DEAD); - - if (d->found & DEVICE_FOUND_UDEV) - /* If udev says the device is around, it's around */ - device_set_state(d, DEVICE_PLUGGED); - else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED) - /* If a device is found in /proc/self/mountinfo or - * /proc/swaps, and was not yet announced via udev, - * it's "tentatively" around. */ - device_set_state(d, DEVICE_TENTATIVE); - - return 0; -} - -static int device_serialize(Unit *u, FILE *f, FDSet *fds) { - Device *d = DEVICE(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", device_state_to_string(d->state)); - - return 0; -} - -static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Device *d = DEVICE(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - DeviceState state; - - state = device_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - d->deserialized_state = state; - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -static void device_dump(Unit *u, FILE *f, const char *prefix) { - Device *d = DEVICE(u); - - assert(d); - - fprintf(f, - "%sDevice State: %s\n" - "%sSysfs Path: %s\n", - prefix, device_state_to_string(d->state), - prefix, strna(d->sysfs)); -} - -_pure_ static UnitActiveState device_active_state(Unit *u) { - assert(u); - - return state_translation_table[DEVICE(u)->state]; -} - -_pure_ static const char *device_sub_state_to_string(Unit *u) { - assert(u); - - return device_state_to_string(DEVICE(u)->state); -} - -static int device_update_description(Unit *u, struct udev_device *dev, const char *path) { - const char *model; - int r; - - assert(u); - assert(dev); - assert(path); - - model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); - if (!model) - model = udev_device_get_property_value(dev, "ID_MODEL"); - - if (model) { - const char *label; - - /* Try to concatenate the device model string with a label, if there is one */ - label = udev_device_get_property_value(dev, "ID_FS_LABEL"); - if (!label) - label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"); - if (!label) - label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER"); - - if (label) { - _cleanup_free_ char *j; - - j = strjoin(model, " ", label, NULL); - if (j) - r = unit_set_description(u, j); - else - r = -ENOMEM; - } else - r = unit_set_description(u, model); - } else - r = unit_set_description(u, path); - - if (r < 0) - log_unit_error_errno(u, r, "Failed to set device description: %m"); - - return r; -} - -static int device_add_udev_wants(Unit *u, struct udev_device *dev) { - const char *wants; - const char *word, *state; - size_t l; - int r; - const char *property; - - assert(u); - assert(dev); - - property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; - wants = udev_device_get_property_value(dev, property); - if (!wants) - return 0; - - FOREACH_WORD_QUOTED(word, l, wants, state) { - _cleanup_free_ char *n = NULL; - char e[l+1]; - - memcpy(e, word, l); - e[l] = 0; - - r = unit_name_mangle(e, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to mangle unit name: %m"); - - r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); - } - if (!isempty(state)) - log_unit_warning(u, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev))); - - return 0; -} - -static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) { - _cleanup_free_ char *e = NULL; - const char *sysfs = NULL; - Unit *u = NULL; - bool delete; - int r; - - assert(m); - assert(path); - - if (dev) { - sysfs = udev_device_get_syspath(dev); - if (!sysfs) - return 0; - } - - r = unit_name_from_path(path, ".device", &e); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name from device path: %m"); - - u = manager_get_unit(m, e); - - /* The device unit can still be present even if the device was - * unplugged: a mount unit can reference it hence preventing - * the GC to have garbaged it. That's desired since the device - * unit may have a dependency on the mount unit which was - * added during the loading of the later. */ - if (dev && u && DEVICE(u)->state == DEVICE_PLUGGED) { - /* This unit is in plugged state: we're sure it's - * attached to a device. */ - if (!path_equal(DEVICE(u)->sysfs, sysfs)) { - log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", - e, DEVICE(u)->sysfs, sysfs); - return -EEXIST; - } - } - - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Device)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); - if (r < 0) - goto fail; - - unit_add_to_load_queue(u); - } else - delete = false; - - /* If this was created via some dependency and has not - * actually been seen yet ->sysfs will not be - * initialized. Hence initialize it if necessary. */ - if (sysfs) { - r = device_set_sysfs(DEVICE(u), sysfs); - if (r < 0) - goto fail; - - (void) device_update_description(u, dev, path); - - /* The additional systemd udev properties we only interpret - * for the main object */ - if (main) - (void) device_add_udev_wants(u, dev); - } - - - /* Note that this won't dispatch the load queue, the caller - * has to do that if needed and appropriate */ - - unit_add_to_dbus_queue(u); - return 0; - -fail: - log_unit_warning_errno(u, r, "Failed to set up device unit: %m"); - - if (delete) - unit_free(u); - - return r; -} - -static int device_process_new(Manager *m, struct udev_device *dev) { - const char *sysfs, *dn, *alias; - struct udev_list_entry *item = NULL, *first = NULL; - int r; - - assert(m); - - sysfs = udev_device_get_syspath(dev); - if (!sysfs) - return 0; - - /* Add the main unit named after the sysfs path */ - r = device_setup_unit(m, dev, sysfs, true); - if (r < 0) - return r; - - /* Add an additional unit for the device node */ - dn = udev_device_get_devnode(dev); - if (dn) - (void) device_setup_unit(m, dev, dn, false); - - /* Add additional units for all symlinks */ - first = udev_device_get_devlinks_list_entry(dev); - udev_list_entry_foreach(item, first) { - const char *p; - struct stat st; - - /* Don't bother with the /dev/block links */ - p = udev_list_entry_get_name(item); - - if (path_startswith(p, "/dev/block/") || - path_startswith(p, "/dev/char/")) - continue; - - /* Verify that the symlink in the FS actually belongs - * to this device. This is useful to deal with - * conflicting devices, e.g. when two disks want the - * same /dev/disk/by-label/xxx link because they have - * the same label. We want to make sure that the same - * device that won the symlink wins in systemd, so we - * check the device node major/minor */ - if (stat(p, &st) >= 0) - if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) || - st.st_rdev != udev_device_get_devnum(dev)) - continue; - - (void) device_setup_unit(m, dev, p, false); - } - - /* Add additional units for all explicitly configured - * aliases */ - alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"); - if (alias) { - const char *word, *state; - size_t l; - - FOREACH_WORD_QUOTED(word, l, alias, state) { - char e[l+1]; - - memcpy(e, word, l); - e[l] = 0; - - if (path_is_absolute(e)) - (void) device_setup_unit(m, dev, e, false); - else - log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e); - } - if (!isempty(state)) - log_warning("SYSTEMD_ALIAS for %s has trailing garbage, ignoring.", sysfs); - } - - return 0; -} - -static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) { - DeviceFound n, previous; - - assert(d); - - n = add ? (d->found | found) : (d->found & ~found); - if (n == d->found) - return; - - previous = d->found; - d->found = n; - - if (!now) - return; - - if (d->found & DEVICE_FOUND_UDEV) - /* When the device is known to udev we consider it - * plugged. */ - device_set_state(d, DEVICE_PLUGGED); - else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0) - /* If the device has not been seen by udev yet, but is - * now referenced by the kernel, then we assume the - * kernel knows it now, and udev might soon too. */ - device_set_state(d, DEVICE_TENTATIVE); - else - /* If nobody sees the device, or if the device was - * previously seen by udev and now is only referenced - * from the kernel, then we consider the device is - * gone, the kernel just hasn't noticed it yet. */ - device_set_state(d, DEVICE_DEAD); -} - -static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) { - Device *d, *l; - - assert(m); - assert(sysfs); - - if (found == DEVICE_NOT_FOUND) - return 0; - - l = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_FOREACH(same_sysfs, d, l) - device_update_found_one(d, add, found, now); - - return 0; -} - -static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) { - _cleanup_free_ char *e = NULL; - Unit *u; - int r; - - assert(m); - assert(path); - - if (found == DEVICE_NOT_FOUND) - return 0; - - r = unit_name_from_path(path, ".device", &e); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name from device path: %m"); - - u = manager_get_unit(m, e); - if (!u) - return 0; - - device_update_found_one(DEVICE(u), add, found, now); - return 0; -} - -static bool device_is_ready(struct udev_device *dev) { - const char *ready; - - assert(dev); - - ready = udev_device_get_property_value(dev, "SYSTEMD_READY"); - if (!ready) - return true; - - return parse_boolean(ready) != 0; -} - -static Unit *device_following(Unit *u) { - Device *d = DEVICE(u); - Device *other, *first = NULL; - - assert(d); - - if (startswith(u->id, "sys-")) - return NULL; - - /* Make everybody follow the unit that's named after the sysfs path */ - for (other = d->same_sysfs_next; other; other = other->same_sysfs_next) - if (startswith(UNIT(other)->id, "sys-")) - return UNIT(other); - - for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) { - if (startswith(UNIT(other)->id, "sys-")) - return UNIT(other); - - first = other; - } - - return UNIT(first); -} - -static int device_following_set(Unit *u, Set **_set) { - Device *d = DEVICE(u), *other; - Set *set; - int r; - - assert(d); - assert(_set); - - if (LIST_JUST_US(same_sysfs, d)) { - *_set = NULL; - return 0; - } - - set = set_new(NULL); - if (!set) - return -ENOMEM; - - LIST_FOREACH_AFTER(same_sysfs, other, d) { - r = set_put(set, other); - if (r < 0) - goto fail; - } - - LIST_FOREACH_BEFORE(same_sysfs, other, d) { - r = set_put(set, other); - if (r < 0) - goto fail; - } - - *_set = set; - return 1; - -fail: - set_free(set); - return r; -} - -static void device_shutdown(Manager *m) { - assert(m); - - m->udev_event_source = sd_event_source_unref(m->udev_event_source); - - if (m->udev_monitor) { - udev_monitor_unref(m->udev_monitor); - m->udev_monitor = NULL; - } - - m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); -} - -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; - - assert(m); - - if (!m->udev_monitor) { - m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_monitor) { - log_oom(); - goto fail; - } - - /* This will fail if we are unprivileged, but that - * should not matter much, as user instances won't run - * during boot. */ - (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) { - log_error_errno(r, "Failed to add udev tag match: %m"); - goto fail; - } - - r = udev_monitor_enable_receiving(m->udev_monitor); - 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) { - 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) { - log_oom(); - goto fail; - } - - r = udev_enumerate_add_match_tag(e, "systemd"); - 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) { - log_error_errno(r, "Failed to install initialization match into enumeration: %m"); - goto fail; - } - - r = udev_enumerate_scan_devices(e); - 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) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - const char *sysfs; - - sysfs = udev_list_entry_get_name(item); - - dev = udev_device_new_from_syspath(m->udev, sysfs); - if (!dev) { - log_oom(); - continue; - } - - if (!device_is_ready(dev)) - continue; - - (void) device_process_new(m, dev); - - device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); - } - - return; - -fail: - device_shutdown(m); -} - -static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - Manager *m = userdata; - const char *action, *sysfs; - int r; - - assert(m); - - if (revents != EPOLLIN) { - static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); - - if (!ratelimit_test(&limit)) - log_error_errno(errno, "Failed to get udev event: %m"); - if (!(revents & EPOLLIN)) - return 0; - } - - /* - * libudev might filter-out devices which pass the bloom - * filter, so getting NULL here is not necessarily an error. - */ - dev = udev_monitor_receive_device(m->udev_monitor); - if (!dev) - return 0; - - sysfs = udev_device_get_syspath(dev); - if (!sysfs) { - log_error("Failed to get udev sys path."); - return 0; - } - - action = udev_device_get_action(dev); - if (!action) { - log_error("Failed to get udev action string."); - return 0; - } - - if (streq(action, "remove")) { - r = swap_process_device_remove(m, dev); - if (r < 0) - log_error_errno(r, "Failed to process swap device remove event: %m"); - - /* If we get notified that a device was removed by - * udev, then it's completely gone, hence unset all - * found bits */ - device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true); - - } else if (device_is_ready(dev)) { - - (void) device_process_new(m, dev); - - r = swap_process_device_new(m, dev); - if (r < 0) - log_error_errno(r, "Failed to process swap device new event: %m"); - - manager_dispatch_load_queue(m); - - /* The device is found now, set the udev found bit */ - device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true); - - } else { - /* The device is nominally around, but not ready for - * us. Hence unset the udev bit, but leave the rest - * around. */ - - device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true); - } - - return 0; -} - -static bool device_supported(void) { - static int read_only = -1; - - /* If /sys is read-only we don't support device units, and any - * attempts to start one should fail immediately. */ - - if (read_only < 0) - read_only = path_is_read_only_fs("/sys"); - - return read_only <= 0; -} - -int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - struct stat st; - - assert(m); - assert(node); - - if (!device_supported()) - return 0; - - /* This is called whenever we find a device referenced in - * /proc/swaps or /proc/self/mounts. Such a device might be - * mounted/enabled at a time where udev has not finished - * probing it yet, and we thus haven't learned about it - * yet. In this case we will set the device unit to - * "tentative" state. */ - - if (add) { - if (!path_startswith(node, "/dev")) - return 0; - - /* We make an extra check here, if the device node - * actually exists. If it's missing, then this is an - * indication that device was unplugged but is still - * referenced in /proc/swaps or - * /proc/self/mountinfo. Note that this check doesn't - * really cover all cases where a device might be gone - * away, since drives that can have a medium inserted - * will still have a device node even when the medium - * is not there... */ - - if (stat(node, &st) >= 0) { - if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) - return 0; - - dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev); - if (!dev && errno != ENOENT) - return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); - - } else if (errno != ENOENT) - return log_error_errno(errno, "Failed to stat device node file %s: %m", node); - - /* If the device is known in the kernel and newly - * appeared, then we'll create a device unit for it, - * under the name referenced in /proc/swaps or - * /proc/self/mountinfo. */ - - (void) device_setup_unit(m, dev, node, false); - } - - /* Update the device unit's state, should it exist */ - return device_update_found_by_name(m, node, add, found, now); -} - -const UnitVTable device_vtable = { - .object_size = sizeof(Device), - .sections = - "Unit\0" - "Device\0" - "Install\0", - - .init = device_init, - .done = device_done, - .load = unit_load_fragment_and_dropin_optional, - - .coldplug = device_coldplug, - - .serialize = device_serialize, - .deserialize_item = device_deserialize_item, - - .dump = device_dump, - - .active_state = device_active_state, - .sub_state_to_string = device_sub_state_to_string, - - .bus_vtable = bus_device_vtable, - - .following = device_following, - .following_set = device_following_set, - - .enumerate = device_enumerate, - .shutdown = device_shutdown, - .supported = device_supported, - - .status_message_formats = { - .starting_stopping = { - [0] = "Expecting device %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Found device %s.", - [JOB_TIMEOUT] = "Timed out waiting for device %s.", - }, - }, -}; diff --git a/src/core/device.h b/src/core/device.h deleted file mode 100644 index 184a1a349b..0000000000 --- a/src/core/device.h +++ /dev/null @@ -1,47 +0,0 @@ -#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 . -***/ - -typedef struct Device Device; - -typedef enum DeviceFound { - DEVICE_NOT_FOUND = 0, - DEVICE_FOUND_UDEV = 1, - DEVICE_FOUND_MOUNT = 2, - DEVICE_FOUND_SWAP = 4, -} DeviceFound; - -struct Device { - Unit meta; - - char *sysfs; - DeviceFound found; - - /* In order to be able to distinguish dependencies on - different device nodes we might end up creating multiple - devices for the same sysfs path. We chain them up here. */ - LIST_FIELDS(struct Device, same_sysfs); - - DeviceState state, deserialized_state; -}; - -extern const UnitVTable device_vtable; - -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 deleted file mode 100644 index 7c178b97c3..0000000000 --- a/src/core/execute.c +++ /dev/null @@ -1,3284 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_PAM -#include -#endif - -#ifdef HAVE_SELINUX -#include -#endif - -#ifdef HAVE_SECCOMP -#include -#endif - -#ifdef HAVE_APPARMOR -#include -#endif - -#include "sd-messages.h" - -#include "af-list.h" -#include "alloc-util.h" -#ifdef HAVE_APPARMOR -#include "apparmor-util.h" -#endif -#include "async.h" -#include "barrier.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 "rlimit-util.h" -#include "rm-rf.h" -#ifdef HAVE_SECCOMP -#include "seccomp-util.h" -#endif -#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) - -/* This assumes there is a 'tty' group */ -#define TTY_MODE 0620 - -#define SNDBUF_SIZE (8*1024*1024) - -static int shift_fds(int fds[], unsigned n_fds) { - int start, restart_from; - - if (n_fds <= 0) - return 0; - - /* Modifies the fds array! (sorts it) */ - - assert(fds); - - start = 0; - for (;;) { - int i; - - restart_from = -1; - - for (i = start; i < (int) n_fds; i++) { - int nfd; - - /* Already at right index? */ - if (fds[i] == i+3) - continue; - - nfd = fcntl(fds[i], F_DUPFD, i + 3); - if (nfd < 0) - return -errno; - - safe_close(fds[i]); - fds[i] = nfd; - - /* Hmm, the fd we wanted isn't free? Then - * let's remember that and try again from here */ - if (nfd != i+3 && restart_from < 0) - restart_from = i; - } - - if (restart_from < 0) - break; - - start = restart_from; - } - - return 0; -} - -static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { - unsigned i; - int r; - - if (n_fds <= 0) - return 0; - - assert(fds); - - /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */ - - for (i = 0; i < n_fds; i++) { - - r = fd_nonblock(fds[i], nonblock); - if (r < 0) - return r; - - /* We unconditionally drop FD_CLOEXEC from the fds, - * since after all we want to pass these fds to our - * children */ - - r = fd_cloexec(fds[i], false); - if (r < 0) - return r; - } - - return 0; -} - -static const char *exec_context_tty_path(const ExecContext *context) { - assert(context); - - if (context->stdio_as_fds) - return NULL; - - if (context->tty_path) - return context->tty_path; - - return "/dev/console"; -} - -static void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) { - const char *path; - - assert(context); - - path = exec_context_tty_path(context); - - if (context->tty_vhangup) { - if (p && p->stdin_fd >= 0) - (void) terminal_vhangup_fd(p->stdin_fd); - else if (path) - (void) terminal_vhangup(path); - } - - if (context->tty_reset) { - if (p && p->stdin_fd >= 0) - (void) reset_terminal_fd(p->stdin_fd, true); - else if (path) - (void) reset_terminal(path); - } - - if (context->tty_vt_disallocate && path) - (void) vt_disallocate(path); -} - -static bool is_terminal_output(ExecOutput o) { - return - o == EXEC_OUTPUT_TTY || - o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || - o == EXEC_OUTPUT_KMSG_AND_CONSOLE || - o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE; -} - -static int open_null_as(int flags, int nfd) { - int fd, r; - - assert(nfd >= 0); - - fd = open("/dev/null", flags|O_NOCTTY); - if (fd < 0) - return -errno; - - if (fd != nfd) { - r = dup2(fd, nfd) < 0 ? -errno : nfd; - safe_close(fd); - } else - r = nfd; - - return r; -} - -static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; - uid_t olduid = UID_INVALID; - gid_t oldgid = GID_INVALID; - int r; - - if (gid != GID_INVALID) { - oldgid = getgid(); - - r = setegid(gid); - if (r < 0) - return -errno; - } - - if (uid != UID_INVALID) { - olduid = getuid(); - - r = seteuid(uid); - if (r < 0) { - r = -errno; - goto restore_gid; - } - } - - r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - r = -errno; - - /* If we fail to restore the uid or gid, things will likely - fail later on. This should only happen if an LSM interferes. */ - - if (uid != UID_INVALID) - (void) seteuid(olduid); - - restore_gid: - if (gid != GID_INVALID) - (void) setegid(oldgid); - - return r; -} - -static int connect_logger_as( - Unit *unit, - const ExecContext *context, - ExecOutput output, - const char *ident, - int nfd, - uid_t uid, - gid_t gid) { - - int fd, r; - - assert(context); - assert(output < _EXEC_OUTPUT_MAX); - assert(ident); - assert(nfd >= 0); - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return -errno; - - r = connect_journal_socket(fd, uid, gid); - if (r < 0) - return r; - - if (shutdown(fd, SHUT_RD) < 0) { - safe_close(fd); - return -errno; - } - - (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); - - dprintf(fd, - "%s\n" - "%s\n" - "%i\n" - "%i\n" - "%i\n" - "%i\n" - "%i\n", - context->syslog_identifier ? context->syslog_identifier : ident, - unit->id, - context->syslog_priority, - !!context->syslog_level_prefix, - output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE, - output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE, - is_terminal_output(output)); - - if (fd == nfd) - return nfd; - - r = dup2(fd, nfd) < 0 ? -errno : nfd; - safe_close(fd); - - return r; -} -static int open_terminal_as(const char *path, mode_t mode, int nfd) { - int fd, r; - - assert(path); - assert(nfd >= 0); - - fd = open_terminal(path, mode | O_NOCTTY); - if (fd < 0) - return fd; - - if (fd != nfd) { - r = dup2(fd, nfd) < 0 ? -errno : nfd; - safe_close(fd); - } else - r = nfd; - - return r; -} - -static bool is_terminal_input(ExecInput i) { - return - i == EXEC_INPUT_TTY || - i == EXEC_INPUT_TTY_FORCE || - i == EXEC_INPUT_TTY_FAIL; -} - -static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { - - if (is_terminal_input(std_input) && !apply_tty_stdin) - return EXEC_INPUT_NULL; - - if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0) - return EXEC_INPUT_NULL; - - return std_input; -} - -static int fixup_output(ExecOutput std_output, int socket_fd) { - - if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0) - return EXEC_OUTPUT_INHERIT; - - return std_output; -} - -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, params->apply_tty_stdin); - - switch (i) { - - case EXEC_INPUT_NULL: - return open_null_as(O_RDONLY, STDIN_FILENO); - - case EXEC_INPUT_TTY: - case EXEC_INPUT_TTY_FORCE: - case EXEC_INPUT_TTY_FAIL: { - int fd, r; - - fd = acquire_terminal(exec_context_tty_path(context), - i == EXEC_INPUT_TTY_FAIL, - i == EXEC_INPUT_TTY_FORCE, - false, - USEC_INFINITY); - if (fd < 0) - return fd; - - if (fd != STDIN_FILENO) { - r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; - safe_close(fd); - } else - r = STDIN_FILENO; - - return r; - } - - case EXEC_INPUT_SOCKET: - return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; - - default: - assert_not_reached("Unknown input type"); - } -} - -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, - dev_t *journal_stream_dev, - ino_t *journal_stream_ino) { - - ExecOutput o; - ExecInput i; - int r; - - assert(unit); - assert(context); - assert(params); - assert(ident); - assert(journal_stream_dev); - assert(journal_stream_ino); - - 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) { - ExecOutput e; - e = fixup_output(context->std_error, socket_fd); - - /* This expects the input and output are already set up */ - - /* Don't change the stderr file descriptor if we inherit all - * the way and are not on a tty */ - if (e == EXEC_OUTPUT_INHERIT && - o == EXEC_OUTPUT_INHERIT && - i == EXEC_INPUT_NULL && - !is_terminal_input(context->std_input) && - getppid () != 1) - return fileno; - - /* Duplicate from stdout if possible */ - if (e == o || e == EXEC_OUTPUT_INHERIT) - return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno; - - o = e; - - } else if (o == EXEC_OUTPUT_INHERIT) { - /* If input got downgraded, inherit the original value */ - if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) - return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); - - /* If the input is connected to anything that's not a /dev/null, inherit that... */ - if (i != EXEC_INPUT_NULL) - return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; - - /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ - if (getppid() != 1) - return fileno; - - /* We need to open /dev/null here anew, to get the right access mode. */ - return open_null_as(O_WRONLY, fileno); - } - - switch (o) { - - case EXEC_OUTPUT_NULL: - return open_null_as(O_WRONLY, fileno); - - case EXEC_OUTPUT_TTY: - if (is_terminal_input(i)) - return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; - - /* We don't reset the terminal if this is just about output */ - return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); - - case EXEC_OUTPUT_SYSLOG: - case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: - case EXEC_OUTPUT_KMSG: - case EXEC_OUTPUT_KMSG_AND_CONSOLE: - case EXEC_OUTPUT_JOURNAL: - case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: - r = connect_logger_as(unit, context, o, ident, fileno, uid, gid); - if (r < 0) { - log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr"); - r = open_null_as(O_WRONLY, fileno); - } else { - struct stat st; - - /* If we connected this fd to the journal via a stream, patch the device/inode into the passed - * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits - * services to detect whether they are connected to the journal or not. */ - - if (fstat(fileno, &st) >= 0) { - *journal_stream_dev = st.st_dev; - *journal_stream_ino = st.st_ino; - } - } - return r; - - case EXEC_OUTPUT_SOCKET: - assert(socket_fd >= 0); - return dup2(socket_fd, fileno) < 0 ? -errno : fileno; - - default: - assert_not_reached("Unknown error type"); - } -} - -static int chown_terminal(int fd, uid_t uid) { - struct stat st; - - assert(fd >= 0); - - /* Before we chown/chmod the TTY, let's ensure this is actually a tty */ - if (isatty(fd) < 1) - return 0; - - /* This might fail. What matters are the results. */ - (void) fchown(fd, uid, -1); - (void) fchmod(fd, TTY_MODE); - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE) - return -EPERM; - - return 0; -} - -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); - - saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3); - if (saved_stdin < 0) - return -errno; - - saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3); - if (saved_stdout < 0) - return -errno; - - fd = acquire_terminal( - "/dev/console", - false, - false, - false, - DEFAULT_CONFIRM_USEC); - if (fd < 0) - return fd; - - r = chown_terminal(fd, getuid()); - if (r < 0) - return r; - - r = reset_terminal_fd(fd, true); - if (r < 0) - return r; - - 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; - - saved_stdin = saved_stdout = -1; - - return 0; -} - -_printf_(1, 2) static int write_confirm_message(const char *format, ...) { - _cleanup_close_ int fd = -1; - va_list ap; - - assert(format); - - fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - va_start(ap, format); - vdprintf(fd, format, ap); - va_end(ap); - - return 0; -} - -static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { - int r = 0; - - assert(saved_stdin); - assert(saved_stdout); - - release_terminal(); - - if (*saved_stdin >= 0) - if (dup2(*saved_stdin, STDIN_FILENO) < 0) - r = -errno; - - if (*saved_stdout >= 0) - if (dup2(*saved_stdout, STDOUT_FILENO) < 0) - r = -errno; - - *saved_stdin = safe_close(*saved_stdin); - *saved_stdout = safe_close(*saved_stdout); - - return r; -} - -static int ask_for_confirmation(char *response, char **argv) { - int saved_stdout = -1, saved_stdin = -1, r; - _cleanup_free_ char *line = NULL; - - r = setup_confirm_stdio(&saved_stdin, &saved_stdout); - if (r < 0) - return r; - - line = exec_command_line(argv); - if (!line) - return -ENOMEM; - - r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line); - - restore_confirm_stdio(&saved_stdin, &saved_stdout); - - return r; -} - -static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) { - bool keep_groups = false; - int r; - - assert(context); - - /* Lookup and set GID and supplementary group list. Here too - * we avoid NSS lookups for gid=0. */ - - if (context->group || username) { - /* First step, initialize groups from /etc/groups */ - if (username && gid != 0) { - if (initgroups(username, gid) < 0) - return -errno; - - keep_groups = true; - } - - /* Second step, set our gids */ - if (setresgid(gid, gid, gid) < 0) - return -errno; - } - - if (context->supplementary_groups) { - int ngroups_max, k; - gid_t *gids; - char **i; - - /* Final step, initialize any manually set supplementary groups */ - assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0); - - if (!(gids = new(gid_t, ngroups_max))) - return -ENOMEM; - - if (keep_groups) { - k = getgroups(ngroups_max, gids); - if (k < 0) { - free(gids); - return -errno; - } - } else - k = 0; - - STRV_FOREACH(i, context->supplementary_groups) { - const char *g; - - if (k >= ngroups_max) { - free(gids); - return -E2BIG; - } - - g = *i; - r = get_group_creds(&g, gids+k); - if (r < 0) { - free(gids); - return r; - } - - k++; - } - - if (setgroups(k, gids) < 0) { - free(gids); - return -errno; - } - - free(gids); - } - - return 0; -} - -static int enforce_user(const ExecContext *context, uid_t uid) { - assert(context); - - /* Sets (but doesn't look up) the uid and make sure we keep the - * capabilities while doing so. */ - - if (context->capability_ambient_set != 0) { - - /* First step: If we need to keep capabilities but - * drop privileges we need to make sure we keep our - * caps, while we drop privileges. */ - if (uid != 0) { - int sb = context->secure_bits | 1<= 0); - - parent_pid = getpid(); - - pam_pid = fork(); - if (pam_pid < 0) { - r = -errno; - goto fail; - } - - if (pam_pid == 0) { - int sig, ret = EXIT_PAM; - - /* The child's job is to reset the PAM session on - * termination */ - barrier_set_role(&barrier, BARRIER_CHILD); - - /* This string must fit in 10 chars (i.e. the length - * of "/sbin/init"), to look pretty in /bin/ps */ - rename_process("(sd-pam)"); - - /* Make sure we don't keep open the passed fds in this - child. We assume that otherwise only those fds are - open here that have been opened by PAM. */ - close_many(fds, n_fds); - - /* Drop privileges - we don't need any to pam_close_session - * and this will make PR_SET_PDEATHSIG work in most cases. - * If this fails, ignore the error - but expect sd-pam threads - * to fail to exit normally */ - if (setresuid(uid, uid, uid) < 0) - log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m"); - - (void) ignore_signals(SIGPIPE, -1); - - /* Wait until our parent died. This will only work if - * the above setresuid() succeeds, otherwise the kernel - * will not allow unprivileged parents kill their privileged - * children this way. We rely on the control groups kill logic - * to do the rest for us. */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - goto child_finish; - - /* Tell the parent that our setup is done. This is especially - * important regarding dropping privileges. Otherwise, unit - * setup might race against our setresuid(2) call. */ - barrier_place(&barrier); - - /* Check if our parent process might already have - * died? */ - if (getppid() == parent_pid) { - sigset_t ss; - - assert_se(sigemptyset(&ss) >= 0); - assert_se(sigaddset(&ss, SIGTERM) >= 0); - - for (;;) { - if (sigwait(&ss, &sig) < 0) { - if (errno == EINTR) - continue; - - goto child_finish; - } - - assert(sig == SIGTERM); - break; - } - } - - /* If our parent died we'll end the session */ - if (getppid() != parent_pid) { - pam_code = pam_close_session(handle, flags); - if (pam_code != PAM_SUCCESS) - goto child_finish; - } - - ret = 0; - - child_finish: - pam_end(handle, pam_code | flags); - _exit(ret); - } - - barrier_set_role(&barrier, BARRIER_PARENT); - - /* If the child was forked off successfully it will do all the - * cleanups, so forget about the handle here. */ - handle = NULL; - - /* Unblock SIGTERM again in the parent */ - assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0); - - /* We close the log explicitly here, since the PAM modules - * might have opened it, but we don't want this fd around. */ - closelog(); - - /* Synchronously wait for the child to initialize. We don't care for - * errors as we cannot recover. However, warn loudly if it happens. */ - if (!barrier_place_and_sync(&barrier)) - log_error("PAM initialization failed"); - - strv_free(*env); - *env = e; - - return 0; - -fail: - if (pam_code != PAM_SUCCESS) { - log_error("PAM failed: %s", pam_strerror(handle, pam_code)); - r = -EPERM; /* PAM errors do not map to errno */ - } else - log_error_errno(r, "PAM failed: %m"); - - if (handle) { - if (close_session) - pam_code = pam_close_session(handle, flags); - - pam_end(handle, pam_code | flags); - } - - strv_free(e); - closelog(); - - return r; -} -#endif - -static void rename_process_from_path(const char *path) { - char process_name[11]; - const char *p; - size_t l; - - /* This resulting string must fit in 10 chars (i.e. the length - * of "/sbin/init") to look pretty in /bin/ps */ - - p = basename(path); - if (isempty(p)) { - rename_process("(...)"); - return; - } - - l = strlen(p); - if (l > 8) { - /* The end of the process name is usually more - * interesting, since the first bit might just be - * "systemd-" */ - p = p + l - 8; - l = 8; - } - - process_name[0] = '('; - memcpy(process_name+1, p, l); - process_name[1+l] = ')'; - process_name[1+l+1] = 0; - - rename_process(process_name); -} - -#ifdef HAVE_SECCOMP - -static int apply_seccomp(const ExecContext *c) { - uint32_t negative_action, action; - scmp_filter_ctx *seccomp; - Iterator i; - void *id; - int r; - - assert(c); - - negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno); - - seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - if (c->syscall_archs) { - - SET_FOREACH(id, c->syscall_archs, i) { - r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); - if (r == -EEXIST) - continue; - if (r < 0) - goto finish; - } - - } else { - r = seccomp_add_secondary_archs(seccomp); - if (r < 0) - goto finish; - } - - action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action; - SET_FOREACH(id, c->syscall_filter, i) { - r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0); - if (r < 0) - goto finish; - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; -} - -static int apply_address_families(const ExecContext *c) { - scmp_filter_ctx *seccomp; - Iterator i; - int r; - - assert(c); - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); - if (r < 0) - goto finish; - - if (c->address_families_whitelist) { - int af, first = 0, last = 0; - void *afp; - - /* If this is a whitelist, we first block the address - * families that are out of range and then everything - * that is not in the set. First, we find the lowest - * and highest address family in the set. */ - - SET_FOREACH(afp, c->address_families, i) { - af = PTR_TO_INT(afp); - - if (af <= 0 || af >= af_max()) - continue; - - if (first == 0 || af < first) - first = af; - - if (last == 0 || af > last) - last = af; - } - - assert((first == 0) == (last == 0)); - - if (first == 0) { - - /* No entries in the valid range, block everything */ - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPROTONOSUPPORT), - SCMP_SYS(socket), - 0); - if (r < 0) - goto finish; - - } else { - - /* Block everything below the first entry */ - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPROTONOSUPPORT), - SCMP_SYS(socket), - 1, - SCMP_A0(SCMP_CMP_LT, first)); - if (r < 0) - goto finish; - - /* Block everything above the last entry */ - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPROTONOSUPPORT), - SCMP_SYS(socket), - 1, - SCMP_A0(SCMP_CMP_GT, last)); - if (r < 0) - goto finish; - - /* Block everything between the first and last - * entry */ - for (af = 1; af < af_max(); af++) { - - if (set_contains(c->address_families, INT_TO_PTR(af))) - continue; - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPROTONOSUPPORT), - SCMP_SYS(socket), - 1, - SCMP_A0(SCMP_CMP_EQ, af)); - if (r < 0) - goto finish; - } - } - - } else { - void *af; - - /* If this is a blacklist, then generate one rule for - * each address family that are then combined in OR - * checks. */ - - SET_FOREACH(af, c->address_families, i) { - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPROTONOSUPPORT), - SCMP_SYS(socket), - 1, - SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af))); - if (r < 0) - goto finish; - } - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; -} - -static int apply_memory_deny_write_execute(const ExecContext *c) { - scmp_filter_ctx *seccomp; - int r; - - assert(c); - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(mmap), - 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); - if (r < 0) - goto finish; - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(mprotect), - 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); - if (r < 0) - goto finish; - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; -} - -static int apply_restrict_realtime(const ExecContext *c) { - static const int permitted_policies[] = { - SCHED_OTHER, - SCHED_BATCH, - SCHED_IDLE, - }; - - scmp_filter_ctx *seccomp; - unsigned i; - int r, p, max_policy = 0; - - assert(c); - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - /* Determine the highest policy constant we want to allow */ - for (i = 0; i < ELEMENTSOF(permitted_policies); i++) - if (permitted_policies[i] > max_policy) - max_policy = permitted_policies[i]; - - /* Go through all policies with lower values than that, and block them -- unless they appear in the - * whitelist. */ - for (p = 0; p < max_policy; p++) { - bool good = false; - - /* Check if this is in the whitelist. */ - for (i = 0; i < ELEMENTSOF(permitted_policies); i++) - if (permitted_policies[i] == p) { - good = true; - break; - } - - if (good) - continue; - - /* Deny this policy */ - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(sched_setscheduler), - 1, - SCMP_A1(SCMP_CMP_EQ, p)); - if (r < 0) - goto finish; - } - - /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are unsigned here, - * hence no need no check for < 0 values. */ - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(sched_setscheduler), - 1, - SCMP_A1(SCMP_CMP_GT, max_policy)); - if (r < 0) - goto finish; - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; -} - -#endif - -static void do_idle_pipe_dance(int idle_pipe[4]) { - assert(idle_pipe); - - - idle_pipe[1] = safe_close(idle_pipe[1]); - idle_pipe[2] = safe_close(idle_pipe[2]); - - if (idle_pipe[0] >= 0) { - int r; - - r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); - - if (idle_pipe[3] >= 0 && r == 0 /* timeout */) { - ssize_t n; - - /* Signal systemd that we are bored and want to continue. */ - n = write(idle_pipe[3], "x", 1); - if (n > 0) - /* Wait for systemd to react to the signal above. */ - fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC); - } - - idle_pipe[0] = safe_close(idle_pipe[0]); - - } - - idle_pipe[3] = safe_close(idle_pipe[3]); -} - -static int build_environment( - const ExecContext *c, - const ExecParameters *p, - unsigned n_fds, - const char *home, - const char *username, - const char *shell, - dev_t journal_stream_dev, - ino_t journal_stream_ino, - char ***ret) { - - _cleanup_strv_free_ char **our_env = NULL; - unsigned n_env = 0; - char *x; - - assert(c); - assert(ret); - - our_env = new0(char*, 12); - 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; - - if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0) - return -ENOMEM; - our_env[n_env++] = x; - - joined = strv_join(p->fd_names, ":"); - if (!joined) - return -ENOMEM; - - x = strjoin("LISTEN_FDNAMES=", joined, NULL); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (p->watchdog_usec > 0) { - if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0) - return -ENOMEM; - our_env[n_env++] = x; - - if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, p->watchdog_usec) < 0) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (home) { - x = strappend("HOME=", home); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (username) { - x = strappend("LOGNAME=", username); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - - x = strappend("USER=", username); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (shell) { - x = strappend("SHELL=", shell); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (is_terminal_input(c->std_input) || - c->std_output == EXEC_OUTPUT_TTY || - c->std_error == EXEC_OUTPUT_TTY || - c->tty_path) { - - x = strdup(default_term_for_tty(exec_context_tty_path(c))); - if (!x) - return -ENOMEM; - our_env[n_env++] = x; - } - - if (journal_stream_dev != 0 && journal_stream_ino != 0) { - if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0) - return -ENOMEM; - - our_env[n_env++] = x; - } - - our_env[n_env++] = NULL; - assert(n_env <= 12); - - *ret = our_env; - our_env = NULL; - - 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, - ExecRuntime *runtime) { - - assert(context); - assert(params); - - if (!strv_isempty(context->read_write_paths) || - !strv_isempty(context->read_only_paths) || - !strv_isempty(context->inaccessible_paths)) - return true; - - if (context->mount_flags != 0) - return true; - - if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) - return true; - - if (context->private_devices || - context->protect_system != PROTECT_SYSTEM_NO || - context->protect_home != PROTECT_HOME_NO) - return true; - - 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 (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, - const ExecContext *context, - const ExecParameters *params, - ExecRuntime *runtime, - char **argv, - int socket_fd, - int *fds, unsigned n_fds, - char **files_env, - int *exit_status) { - - _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; - _cleanup_free_ char *mac_selinux_context_net = NULL; - const char *username = NULL, *home = NULL, *shell = NULL, *wd; - dev_t journal_stream_dev = 0; - ino_t journal_stream_ino = 0; - bool needs_mount_namespace; - uid_t uid = UID_INVALID; - gid_t gid = GID_INVALID; - int i, r; - - assert(unit); - assert(command); - assert(context); - assert(params); - assert(exit_status); - - rename_process_from_path(command->path); - - /* We reset exactly these signals, since they are the - * only ones we set to SIG_IGN in the main daemon. All - * others we leave untouched because we set them to - * SIG_DFL or a valid handler initially, both of which - * will be demoted to SIG_DFL. */ - (void) default_signals(SIGNALS_CRASH_HANDLER, - SIGNALS_IGNORE, -1); - - if (context->ignore_sigpipe) - (void) ignore_signals(SIGPIPE, -1); - - r = reset_signal_mask(); - if (r < 0) { - *exit_status = EXIT_SIGNAL_MASK; - return r; - } - - if (params->idle_pipe) - do_idle_pipe_dance(params->idle_pipe); - - /* Close sockets very early to make sure we don't - * block init reexecution because it cannot bind its - * sockets */ - - log_forget_fds(); - - r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds); - if (r < 0) { - *exit_status = EXIT_FDS; - return r; - } - - if (!context->same_pgrp) - if (setsid() < 0) { - *exit_status = EXIT_SETSID; - return -errno; - } - - exec_context_tty_reset(context, params); - - if (params->confirm_spawn) { - char response; - - r = ask_for_confirmation(&response, argv); - if (r == -ETIMEDOUT) - write_confirm_message("Confirmation question timed out, assuming positive response.\n"); - else if (r < 0) - write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r)); - else if (response == 's') { - write_confirm_message("Skipping execution.\n"); - *exit_status = EXIT_CONFIRM; - return -ECANCELED; - } else if (response == 'n') { - write_confirm_message("Failing execution.\n"); - *exit_status = 0; - return 0; - } - } - - if (context->user) { - username = context->user; - r = get_user_creds(&username, &uid, &gid, &home, &shell); - if (r < 0) { - *exit_status = EXIT_USER; - return r; - } - } - - if (context->group) { - const char *g = context->group; - - r = get_group_creds(&g, &gid); - if (r < 0) { - *exit_status = EXIT_GROUP; - return r; - } - } - - - /* If a socket is connected to STDIN/STDOUT/STDERR, we - * must sure to drop O_NONBLOCK */ - if (socket_fd >= 0) - (void) fd_nonblock(socket_fd, false); - - r = setup_input(context, params, socket_fd); - if (r < 0) { - *exit_status = EXIT_STDIN; - return r; - } - - r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); - if (r < 0) { - *exit_status = EXIT_STDOUT; - return r; - } - - r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); - if (r < 0) { - *exit_status = EXIT_STDERR; - return r; - } - - if (params->cgroup_path) { - r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL); - if (r < 0) { - *exit_status = EXIT_CGROUP; - return r; - } - } - - if (context->oom_score_adjust_set) { - char t[DECIMAL_STR_MAX(context->oom_score_adjust)]; - - /* When we can't make this change due to EPERM, then - * let's silently skip over it. User namespaces - * prohibit write access to this file, and we - * shouldn't trip up over that. */ - - sprintf(t, "%i", context->oom_score_adjust); - r = write_string_file("/proc/self/oom_score_adj", t, 0); - if (r == -EPERM || r == -EACCES) { - log_open(); - log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m"); - log_close(); - } else if (r < 0) { - *exit_status = EXIT_OOM_ADJUST; - return -errno; - } - } - - if (context->nice_set) - if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { - *exit_status = EXIT_NICE; - return -errno; - } - - if (context->cpu_sched_set) { - struct sched_param param = { - .sched_priority = context->cpu_sched_priority, - }; - - r = sched_setscheduler(0, - context->cpu_sched_policy | - (context->cpu_sched_reset_on_fork ? - SCHED_RESET_ON_FORK : 0), - ¶m); - if (r < 0) { - *exit_status = EXIT_SETSCHEDULER; - return -errno; - } - } - - if (context->cpuset) - if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { - *exit_status = EXIT_CPUAFFINITY; - return -errno; - } - - if (context->ioprio_set) - if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { - *exit_status = EXIT_IOPRIO; - return -errno; - } - - if (context->timer_slack_nsec != NSEC_INFINITY) - if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { - *exit_status = EXIT_TIMERSLACK; - return -errno; - } - - if (context->personality != PERSONALITY_INVALID) - if (personality(context->personality) < 0) { - *exit_status = EXIT_PERSONALITY; - return -errno; - } - - if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, - context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : - context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : - USER_PROCESS, - username ? "root" : context->user); - - if (context->user && is_terminal_input(context->std_input)) { - r = chown_terminal(STDIN_FILENO, uid); - if (r < 0) { - *exit_status = EXIT_STDIN; - return r; - } - } - - /* If delegation is enabled we'll pass ownership of the cgroup - * (but only in systemd's own controller hierarchy!) to the - * user of the new process. */ - if (params->cgroup_path && context->user && params->cgroup_delegate) { - r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid); - if (r < 0) { - *exit_status = EXIT_CGROUP; - return r; - } - - - r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0755, uid, gid); - if (r < 0) { - *exit_status = EXIT_CGROUP; - return r; - } - } - - if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) { - char **rt; - - STRV_FOREACH(rt, context->runtime_directory) { - _cleanup_free_ char *p; - - p = strjoin(params->runtime_prefix, "/", *rt, NULL); - if (!p) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return -ENOMEM; - } - - r = mkdir_p_label(p, context->runtime_directory_mode); - if (r < 0) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return r; - } - - r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid); - if (r < 0) { - *exit_status = EXIT_RUNTIME_DIRECTORY; - return r; - } - } - } - - r = build_environment( - context, - params, - n_fds, - home, - username, - shell, - journal_stream_dev, - journal_stream_ino, - &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; - } - - accum_env = strv_env_merge(5, - params->environment, - our_env, - pass_env, - context->environment, - files_env, - NULL); - if (!accum_env) { - *exit_status = EXIT_MEMORY; - return -ENOMEM; - } - accum_env = strv_env_clean(accum_env); - - umask(context->umask); - - if (params->apply_permissions && !command->privileged) { - r = enforce_groups(context, username, gid); - if (r < 0) { - *exit_status = EXIT_GROUP; - return r; - } -#ifdef HAVE_SMACK - if (context->smack_process_label) { - r = mac_smack_apply_pid(0, context->smack_process_label); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#ifdef SMACK_DEFAULT_PROCESS_LABEL - else { - _cleanup_free_ char *exec_label = NULL; - - r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); - if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - - r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - } -#endif -#endif -#ifdef HAVE_PAM - if (context->pam_name && username) { - r = setup_pam(context->pam_name, username, uid, context->tty_path, &accum_env, fds, n_fds); - if (r < 0) { - *exit_status = EXIT_PAM; - return r; - } - } -#endif - } - - if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { - r = setup_netns(runtime->netns_storage_socket); - if (r < 0) { - *exit_status = EXIT_NETWORK; - return r; - } - } - - needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); - - if (needs_mount_namespace) { - char *tmp = NULL, *var = NULL; - - /* The runtime struct only contains the parent - * of the private /tmp, which is - * non-accessible to world users. Inside of it - * there's a /tmp that is sticky, and that's - * the one we want to use here. */ - - if (context->private_tmp && runtime) { - if (runtime->tmp_dir) - tmp = strjoina(runtime->tmp_dir, "/tmp"); - if (runtime->var_tmp_dir) - var = strjoina(runtime->var_tmp_dir, "/tmp"); - } - - r = setup_namespace( - params->apply_chroot ? context->root_directory : NULL, - context->read_write_paths, - context->read_only_paths, - context->inaccessible_paths, - tmp, - var, - context->private_devices, - context->protect_home, - context->protect_system, - context->mount_flags); - - /* If we couldn't set up the namespace this is - * probably due to a missing capability. In this case, - * silently proceeed. */ - if (r == -EPERM || r == -EACCES) { - log_open(); - log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m"); - log_close(); - } else if (r < 0) { - *exit_status = EXIT_NAMESPACE; - return r; - } - } - - 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) { - *exit_status = EXIT_CHROOT; - return -errno; - } - - if (chdir(wd) < 0 && - !context->working_directory_missing_ok) { - *exit_status = EXIT_CHDIR; - return -errno; - } - } else { - const char *d; - - d = strjoina(strempty(context->root_directory), "/", strempty(wd)); - if (chdir(d) < 0 && - !context->working_directory_missing_ok) { - *exit_status = EXIT_CHDIR; - return -errno; - } - } - -#ifdef HAVE_SELINUX - if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) { - r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); - if (r < 0) { - *exit_status = EXIT_SELINUX_CONTEXT; - return r; - } - } -#endif - - /* We repeat the fd closing here, to make sure that - * nothing is leaked from the PAM modules. Note that - * we are more aggressive this time since socket_fd - * and the netns fds we don't need anymore. The custom - * endpoint fd was needed to upload the policy and can - * now be closed as well. */ - r = close_all_fds(fds, n_fds); - if (r >= 0) - r = shift_fds(fds, n_fds); - if (r >= 0) - r = flags_fds(fds, n_fds, context->non_blocking); - if (r < 0) { - *exit_status = EXIT_FDS; - return r; - } - - if (params->apply_permissions && !command->privileged) { - - bool use_address_families = context->address_families_whitelist || - !set_isempty(context->address_families); - bool use_syscall_filter = context->syscall_whitelist || - !set_isempty(context->syscall_filter) || - !set_isempty(context->syscall_archs); - int secure_bits = context->secure_bits; - - for (i = 0; i < _RLIMIT_MAX; i++) { - - if (!context->rlimit[i]) - continue; - - r = setrlimit_closest(i, context->rlimit[i]); - if (r < 0) { - *exit_status = EXIT_LIMITS; - return r; - } - } - - /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */ - if (context->restrict_realtime && !context->rlimit[RLIMIT_RTPRIO]) { - if (setrlimit(RLIMIT_RTPRIO, &RLIMIT_MAKE_CONST(0)) < 0) { - *exit_status = EXIT_LIMITS; - return -errno; - } - } - - 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->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<no_new_privileges || - (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || context->memory_deny_write_execute || context->restrict_realtime || use_syscall_filter))) - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { - *exit_status = EXIT_NO_NEW_PRIVILEGES; - return -errno; - } - -#ifdef HAVE_SECCOMP - if (use_address_families) { - r = apply_address_families(context); - if (r < 0) { - *exit_status = EXIT_ADDRESS_FAMILIES; - return r; - } - } - - if (context->memory_deny_write_execute) { - r = apply_memory_deny_write_execute(context); - if (r < 0) { - *exit_status = EXIT_SECCOMP; - return r; - } - } - - if (context->restrict_realtime) { - r = apply_restrict_realtime(context); - if (r < 0) { - *exit_status = EXIT_SECCOMP; - return r; - } - } - - if (use_syscall_filter) { - r = apply_seccomp(context); - if (r < 0) { - *exit_status = EXIT_SECCOMP; - return r; - } - } -#endif - -#ifdef HAVE_SELINUX - if (mac_selinux_use()) { - char *exec_context = mac_selinux_context_net ?: context->selinux_context; - - if (exec_context) { - r = setexeccon(exec_context); - if (r < 0) { - *exit_status = EXIT_SELINUX_CONTEXT; - return r; - } - } - } -#endif - -#ifdef HAVE_APPARMOR - if (context->apparmor_profile && mac_apparmor_use()) { - r = aa_change_onexec(context->apparmor_profile); - if (r < 0 && !context->apparmor_profile_ignore) { - *exit_status = EXIT_APPARMOR_PROFILE; - return -errno; - } - } -#endif - } - - final_argv = replace_env_argv(argv, accum_env); - if (!final_argv) { - *exit_status = EXIT_MEMORY; - return -ENOMEM; - } - - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { - _cleanup_free_ char *line; - - line = exec_command_line(final_argv); - if (line) { - log_open(); - log_struct(LOG_DEBUG, - LOG_UNIT_ID(unit), - "EXECUTABLE=%s", command->path, - LOG_UNIT_MESSAGE(unit, "Executing: %s", line), - NULL); - log_close(); - } - } - - execve(command->path, final_argv, accum_env); - *exit_status = EXIT_EXEC; - return -errno; -} - -int exec_spawn(Unit *unit, - ExecCommand *command, - const ExecContext *context, - const ExecParameters *params, - ExecRuntime *runtime, - pid_t *ret) { - - _cleanup_strv_free_ char **files_env = NULL; - int *fds = NULL; unsigned n_fds = 0; - _cleanup_free_ char *line = NULL; - int socket_fd, r; - char **argv; - pid_t pid; - - assert(unit); - assert(command); - assert(context); - assert(ret); - assert(params); - assert(params->fds || params->n_fds <= 0); - - if (context->std_input == EXEC_INPUT_SOCKET || - context->std_output == EXEC_OUTPUT_SOCKET || - context->std_error == EXEC_OUTPUT_SOCKET) { - - if (params->n_fds != 1) { - log_unit_error(unit, "Got more than one socket."); - return -EINVAL; - } - - socket_fd = params->fds[0]; - } else { - socket_fd = -1; - fds = params->fds; - n_fds = params->n_fds; - } - - r = exec_context_load_environment(unit, context, &files_env); - if (r < 0) - return log_unit_error_errno(unit, r, "Failed to load environment files: %m"); - - argv = params->argv ?: command->argv; - line = exec_command_line(argv); - if (!line) - return log_oom(); - - log_struct(LOG_DEBUG, - LOG_UNIT_ID(unit), - LOG_UNIT_MESSAGE(unit, "About to execute: %s", line), - "EXECUTABLE=%s", command->path, - NULL); - pid = fork(); - if (pid < 0) - return log_unit_error_errno(unit, errno, "Failed to fork: %m"); - - if (pid == 0) { - int exit_status; - - r = exec_child(unit, - command, - context, - params, - runtime, - argv, - socket_fd, - fds, n_fds, - files_env, - &exit_status); - if (r < 0) { - log_open(); - log_struct_errno(LOG_ERR, r, - LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), - LOG_UNIT_ID(unit), - LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", - exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD), - command->path), - "EXECUTABLE=%s", command->path, - NULL); - } - - _exit(exit_status); - } - - log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid); - - /* We add the new process to the cgroup both in the child (so - * that we can be sure that no user code is ever executed - * outside of the cgroup) and in the parent (so that we can be - * sure that when we kill the cgroup the process will be - * killed too). */ - if (params->cgroup_path) - (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid); - - exec_status_start(&command->exec_status, pid); - - *ret = pid; - return 0; -} - -void exec_context_init(ExecContext *c) { - assert(c); - - c->umask = 0022; - c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); - c->cpu_sched_policy = SCHED_OTHER; - c->syslog_priority = LOG_DAEMON|LOG_INFO; - c->syslog_level_prefix = true; - c->ignore_sigpipe = true; - 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) { - unsigned l; - - assert(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]); - - c->working_directory = mfree(c->working_directory); - c->root_directory = mfree(c->root_directory); - c->tty_path = mfree(c->tty_path); - c->syslog_identifier = mfree(c->syslog_identifier); - c->user = mfree(c->user); - c->group = mfree(c->group); - - c->supplementary_groups = strv_free(c->supplementary_groups); - - c->pam_name = mfree(c->pam_name); - - c->read_only_paths = strv_free(c->read_only_paths); - c->read_write_paths = strv_free(c->read_write_paths); - c->inaccessible_paths = strv_free(c->inaccessible_paths); - - if (c->cpuset) - CPU_FREE(c->cpuset); - - c->utmp_id = mfree(c->utmp_id); - c->selinux_context = mfree(c->selinux_context); - c->apparmor_profile = mfree(c->apparmor_profile); - - c->syscall_filter = set_free(c->syscall_filter); - c->syscall_archs = set_free(c->syscall_archs); - c->address_families = set_free(c->address_families); - - c->runtime_directory = strv_free(c->runtime_directory); -} - -int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) { - char **i; - - assert(c); - - if (!runtime_prefix) - return 0; - - STRV_FOREACH(i, c->runtime_directory) { - _cleanup_free_ char *p; - - p = strjoin(runtime_prefix, "/", *i, NULL); - if (!p) - return -ENOMEM; - - /* We execute this synchronously, since we need to be - * sure this is gone when we start the service - * next. */ - (void) rm_rf(p, REMOVE_ROOT); - } - - return 0; -} - -void exec_command_done(ExecCommand *c) { - assert(c); - - c->path = mfree(c->path); - - c->argv = strv_free(c->argv); -} - -void exec_command_done_array(ExecCommand *c, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) - exec_command_done(c+i); -} - -ExecCommand* exec_command_free_list(ExecCommand *c) { - ExecCommand *i; - - while ((i = c)) { - LIST_REMOVE(command, c, i); - exec_command_done(i); - free(i); - } - - return NULL; -} - -void exec_command_free_array(ExecCommand **c, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) - c[i] = exec_command_free_list(c[i]); -} - -typedef struct InvalidEnvInfo { - Unit *unit; - const char *path; -} InvalidEnvInfo; - -static void invalid_env(const char *p, void *userdata) { - InvalidEnvInfo *info = userdata; - - log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path); -} - -int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { - char **i, **r = NULL; - - assert(c); - assert(l); - - STRV_FOREACH(i, c->environment_files) { - char *fn; - int k; - bool ignore = false; - char **p; - _cleanup_globfree_ glob_t pglob = {}; - int count, n; - - fn = *i; - - if (fn[0] == '-') { - ignore = true; - fn++; - } - - if (!path_is_absolute(fn)) { - if (ignore) - continue; - - strv_free(r); - return -EINVAL; - } - - /* Filename supports globbing, take all matching files */ - errno = 0; - if (glob(fn, 0, NULL, &pglob) != 0) { - if (ignore) - continue; - - strv_free(r); - return errno > 0 ? -errno : -EINVAL; - } - count = pglob.gl_pathc; - if (count == 0) { - if (ignore) - continue; - - strv_free(r); - return -EINVAL; - } - for (n = 0; n < count; n++) { - k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p); - if (k < 0) { - if (ignore) - continue; - - strv_free(r); - return k; - } - /* Log invalid environment variables with filename */ - if (p) { - InvalidEnvInfo info = { - .unit = unit, - .path = pglob.gl_pathv[n] - }; - - p = strv_env_clean_with_callback(p, invalid_env, &info); - } - - if (r == NULL) - r = p; - else { - char **m; - - m = strv_env_merge(2, r, p); - strv_free(r); - strv_free(p); - if (!m) - return -ENOMEM; - - r = m; - } - } - } - - *l = r; - - return 0; -} - -static bool tty_may_match_dev_console(const char *tty) { - _cleanup_free_ char *active = NULL; - char *console; - - if (!tty) - return true; - - if (startswith(tty, "/dev/")) - tty += 5; - - /* trivial identity? */ - if (streq(tty, "console")) - return true; - - console = resolve_dev_console(&active); - /* if we could not resolve, assume it may */ - if (!console) - return true; - - /* "tty0" means the active VC, so it may be the same sometimes */ - return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty)); -} - -bool exec_context_may_touch_console(ExecContext *ec) { - - return (ec->tty_reset || - ec->tty_vhangup || - ec->tty_vt_disallocate || - is_terminal_input(ec->std_input) || - is_terminal_output(ec->std_output) || - is_terminal_output(ec->std_error)) && - tty_may_match_dev_console(exec_context_tty_path(ec)); -} - -static void strv_fprintf(FILE *f, char **l) { - char **g; - - assert(f); - - STRV_FOREACH(g, l) - fprintf(f, " %s", *g); -} - -void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { - char **e, **d; - unsigned i; - - assert(c); - assert(f); - - prefix = strempty(prefix); - - fprintf(f, - "%sUMask: %04o\n" - "%sWorkingDirectory: %s\n" - "%sRootDirectory: %s\n" - "%sNonBlocking: %s\n" - "%sPrivateTmp: %s\n" - "%sPrivateNetwork: %s\n" - "%sPrivateDevices: %s\n" - "%sProtectHome: %s\n" - "%sProtectSystem: %s\n" - "%sIgnoreSIGPIPE: %s\n" - "%sMemoryDenyWriteExecute: %s\n" - "%sRestrictRealtime: %s\n", - prefix, c->umask, - prefix, c->working_directory ? c->working_directory : "/", - prefix, c->root_directory ? c->root_directory : "/", - prefix, yes_no(c->non_blocking), - prefix, yes_no(c->private_tmp), - prefix, yes_no(c->private_network), - prefix, yes_no(c->private_devices), - prefix, protect_home_to_string(c->protect_home), - prefix, protect_system_to_string(c->protect_system), - prefix, yes_no(c->ignore_sigpipe), - prefix, yes_no(c->memory_deny_write_execute), - prefix, yes_no(c->restrict_realtime)); - - STRV_FOREACH(e, c->environment) - fprintf(f, "%sEnvironment: %s\n", prefix, *e); - - 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", - prefix, c->nice); - - if (c->oom_score_adjust_set) - fprintf(f, - "%sOOMScoreAdjust: %i\n", - prefix, c->oom_score_adjust); - - for (i = 0; i < RLIM_NLIMITS; i++) - 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; - - ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str); - fprintf(f, - "%sIOSchedulingClass: %s\n" - "%sIOPriority: %i\n", - prefix, strna(class_str), - prefix, (int) IOPRIO_PRIO_DATA(c->ioprio)); - } - - if (c->cpu_sched_set) { - _cleanup_free_ char *policy_str = NULL; - - sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str); - fprintf(f, - "%sCPUSchedulingPolicy: %s\n" - "%sCPUSchedulingPriority: %i\n" - "%sCPUSchedulingResetOnFork: %s\n", - prefix, strna(policy_str), - prefix, c->cpu_sched_priority, - prefix, yes_no(c->cpu_sched_reset_on_fork)); - } - - if (c->cpuset) { - fprintf(f, "%sCPUAffinity:", prefix); - for (i = 0; i < c->cpuset_ncpus; i++) - if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset)) - fprintf(f, " %u", i); - fputs("\n", f); - } - - if (c->timer_slack_nsec != NSEC_INFINITY) - fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec); - - fprintf(f, - "%sStandardInput: %s\n" - "%sStandardOutput: %s\n" - "%sStandardError: %s\n", - prefix, exec_input_to_string(c->std_input), - prefix, exec_output_to_string(c->std_output), - prefix, exec_output_to_string(c->std_error)); - - if (c->tty_path) - fprintf(f, - "%sTTYPath: %s\n" - "%sTTYReset: %s\n" - "%sTTYVHangup: %s\n" - "%sTTYVTDisallocate: %s\n", - prefix, c->tty_path, - prefix, yes_no(c->tty_reset), - prefix, yes_no(c->tty_vhangup), - prefix, yes_no(c->tty_vt_disallocate)); - - if (c->std_output == EXEC_OUTPUT_SYSLOG || - c->std_output == EXEC_OUTPUT_KMSG || - c->std_output == EXEC_OUTPUT_JOURNAL || - c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || - c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || - c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE || - c->std_error == EXEC_OUTPUT_SYSLOG || - c->std_error == EXEC_OUTPUT_KMSG || - c->std_error == EXEC_OUTPUT_JOURNAL || - c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || - c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || - c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) { - - _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL; - - log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str); - log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str); - - fprintf(f, - "%sSyslogFacility: %s\n" - "%sSyslogLevel: %s\n", - prefix, strna(fac_str), - prefix, strna(lvl_str)); - } - - if (c->secure_bits) - fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", - prefix, - (c->secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<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 & (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); - } - - if (c->user) - fprintf(f, "%sUser: %s\n", prefix, c->user); - if (c->group) - fprintf(f, "%sGroup: %s\n", prefix, c->group); - - if (strv_length(c->supplementary_groups) > 0) { - fprintf(f, "%sSupplementaryGroups:", prefix); - strv_fprintf(f, c->supplementary_groups); - fputs("\n", f); - } - - if (c->pam_name) - fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); - - if (strv_length(c->read_write_paths) > 0) { - fprintf(f, "%sReadWritePaths:", prefix); - strv_fprintf(f, c->read_write_paths); - fputs("\n", f); - } - - if (strv_length(c->read_only_paths) > 0) { - fprintf(f, "%sReadOnlyPaths:", prefix); - strv_fprintf(f, c->read_only_paths); - fputs("\n", f); - } - - if (strv_length(c->inaccessible_paths) > 0) { - fprintf(f, "%sInaccessiblePaths:", prefix); - strv_fprintf(f, c->inaccessible_paths); - fputs("\n", f); - } - - if (c->utmp_id) - fprintf(f, - "%sUtmpIdentifier: %s\n", - prefix, c->utmp_id); - - if (c->selinux_context) - fprintf(f, - "%sSELinuxContext: %s%s\n", - prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context); - - if (c->personality != PERSONALITY_INVALID) - fprintf(f, - "%sPersonality: %s\n", - prefix, strna(personality_to_string(c->personality))); - - if (c->syscall_filter) { -#ifdef HAVE_SECCOMP - Iterator j; - void *id; - bool first = true; -#endif - - fprintf(f, - "%sSystemCallFilter: ", - prefix); - - if (!c->syscall_whitelist) - fputc('~', f); - -#ifdef HAVE_SECCOMP - SET_FOREACH(id, c->syscall_filter, j) { - _cleanup_free_ char *name = NULL; - - if (first) - first = false; - else - fputc(' ', f); - - name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); - fputs(strna(name), f); - } -#endif - - fputc('\n', f); - } - - if (c->syscall_archs) { -#ifdef HAVE_SECCOMP - Iterator j; - void *id; -#endif - - fprintf(f, - "%sSystemCallArchitectures:", - prefix); - -#ifdef HAVE_SECCOMP - SET_FOREACH(id, c->syscall_archs, j) - fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1))); -#endif - fputc('\n', f); - } - - if (c->syscall_errno > 0) - fprintf(f, - "%sSystemCallErrorNumber: %s\n", - prefix, strna(errno_to_name(c->syscall_errno))); - - if (c->apparmor_profile) - fprintf(f, - "%sAppArmorProfile: %s%s\n", - prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); -} - -bool exec_context_maintains_privileges(ExecContext *c) { - assert(c); - - /* Returns true if the process forked off would run under - * an unchanged UID or as root. */ - - if (!c->user) - return true; - - if (streq(c->user, "root") || streq(c->user, "0")) - return true; - - return false; -} - -void exec_status_start(ExecStatus *s, pid_t pid) { - assert(s); - - zero(*s); - s->pid = pid; - dual_timestamp_get(&s->start_timestamp); -} - -void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { - assert(s); - - if (s->pid && s->pid != pid) - zero(*s); - - s->pid = pid; - dual_timestamp_get(&s->exit_timestamp); - - s->code = code; - s->status = status; - - if (context) { - if (context->utmp_id) - utmp_put_dead_process(context->utmp_id, pid, code, status); - - exec_context_tty_reset(context, NULL); - } -} - -void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { - char buf[FORMAT_TIMESTAMP_MAX]; - - assert(s); - assert(f); - - if (s->pid <= 0) - return; - - prefix = strempty(prefix); - - fprintf(f, - "%sPID: "PID_FMT"\n", - prefix, s->pid); - - if (s->start_timestamp.realtime > 0) - fprintf(f, - "%sStart Timestamp: %s\n", - prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime)); - - if (s->exit_timestamp.realtime > 0) - fprintf(f, - "%sExit Timestamp: %s\n" - "%sExit Code: %s\n" - "%sExit Status: %i\n", - prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime), - prefix, sigchld_code_to_string(s->code), - prefix, s->status); -} - -char *exec_command_line(char **argv) { - size_t k; - char *n, *p, **a; - bool first = true; - - assert(argv); - - k = 1; - STRV_FOREACH(a, argv) - k += strlen(*a)+3; - - if (!(n = new(char, k))) - return NULL; - - p = n; - STRV_FOREACH(a, argv) { - - if (!first) - *(p++) = ' '; - else - first = false; - - if (strpbrk(*a, WHITESPACE)) { - *(p++) = '\''; - p = stpcpy(p, *a); - *(p++) = '\''; - } else - p = stpcpy(p, *a); - - } - - *p = 0; - - /* FIXME: this doesn't really handle arguments that have - * spaces and ticks in them */ - - return n; -} - -void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { - _cleanup_free_ char *cmd = NULL; - const char *prefix2; - - assert(c); - assert(f); - - prefix = strempty(prefix); - prefix2 = strjoina(prefix, "\t"); - - cmd = exec_command_line(c->argv); - fprintf(f, - "%sCommand Line: %s\n", - prefix, cmd ? cmd : strerror(ENOMEM)); - - exec_status_dump(&c->exec_status, f, prefix2); -} - -void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { - assert(f); - - prefix = strempty(prefix); - - LIST_FOREACH(command, c, c) - exec_command_dump(c, f, prefix); -} - -void exec_command_append_list(ExecCommand **l, ExecCommand *e) { - ExecCommand *end; - - assert(l); - assert(e); - - if (*l) { - /* It's kind of important, that we keep the order here */ - LIST_FIND_TAIL(command, *l, end); - LIST_INSERT_AFTER(command, *l, end, e); - } else - *l = e; -} - -int exec_command_set(ExecCommand *c, const char *path, ...) { - va_list ap; - char **l, *p; - - assert(c); - assert(path); - - va_start(ap, path); - l = strv_new_ap(path, ap); - va_end(ap); - - if (!l) - return -ENOMEM; - - p = strdup(path); - if (!p) { - strv_free(l); - return -ENOMEM; - } - - free(c->path); - c->path = p; - - strv_free(c->argv); - c->argv = l; - - return 0; -} - -int exec_command_append(ExecCommand *c, const char *path, ...) { - _cleanup_strv_free_ char **l = NULL; - va_list ap; - int r; - - assert(c); - assert(path); - - va_start(ap, path); - l = strv_new_ap(path, ap); - va_end(ap); - - if (!l) - return -ENOMEM; - - r = strv_extend_strv(&c->argv, l, false); - if (r < 0) - return r; - - return 0; -} - - -static int exec_runtime_allocate(ExecRuntime **rt) { - - if (*rt) - return 0; - - *rt = new0(ExecRuntime, 1); - if (!*rt) - return -ENOMEM; - - (*rt)->n_ref = 1; - (*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1; - - return 0; -} - -int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) { - int r; - - assert(rt); - assert(c); - assert(id); - - if (*rt) - return 1; - - if (!c->private_network && !c->private_tmp) - return 0; - - r = exec_runtime_allocate(rt); - if (r < 0) - return r; - - if (c->private_network && (*rt)->netns_storage_socket[0] < 0) { - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, (*rt)->netns_storage_socket) < 0) - return -errno; - } - - if (c->private_tmp && !(*rt)->tmp_dir) { - r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir); - if (r < 0) - return r; - } - - return 1; -} - -ExecRuntime *exec_runtime_ref(ExecRuntime *r) { - assert(r); - assert(r->n_ref > 0); - - r->n_ref++; - return r; -} - -ExecRuntime *exec_runtime_unref(ExecRuntime *r) { - - if (!r) - return NULL; - - assert(r->n_ref > 0); - - r->n_ref--; - if (r->n_ref > 0) - return NULL; - - free(r->tmp_dir); - free(r->var_tmp_dir); - safe_close_pair(r->netns_storage_socket); - free(r); - - return NULL; -} - -int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) { - assert(u); - assert(f); - assert(fds); - - if (!rt) - return 0; - - if (rt->tmp_dir) - unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir); - - if (rt->var_tmp_dir) - unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir); - - if (rt->netns_storage_socket[0] >= 0) { - int copy; - - copy = fdset_put_dup(fds, rt->netns_storage_socket[0]); - if (copy < 0) - return copy; - - unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy); - } - - if (rt->netns_storage_socket[1] >= 0) { - int copy; - - copy = fdset_put_dup(fds, rt->netns_storage_socket[1]); - if (copy < 0) - return copy; - - unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy); - } - - return 0; -} - -int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) { - int r; - - assert(rt); - assert(key); - assert(value); - - if (streq(key, "tmp-dir")) { - char *copy; - - r = exec_runtime_allocate(rt); - if (r < 0) - return log_oom(); - - copy = strdup(value); - if (!copy) - return log_oom(); - - free((*rt)->tmp_dir); - (*rt)->tmp_dir = copy; - - } else if (streq(key, "var-tmp-dir")) { - char *copy; - - r = exec_runtime_allocate(rt); - if (r < 0) - return log_oom(); - - copy = strdup(value); - if (!copy) - return log_oom(); - - free((*rt)->var_tmp_dir); - (*rt)->var_tmp_dir = copy; - - } else if (streq(key, "netns-socket-0")) { - int fd; - - r = exec_runtime_allocate(rt); - if (r < 0) - return log_oom(); - - if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse netns socket value: %s", value); - else { - safe_close((*rt)->netns_storage_socket[0]); - (*rt)->netns_storage_socket[0] = fdset_remove(fds, fd); - } - } else if (streq(key, "netns-socket-1")) { - int fd; - - r = exec_runtime_allocate(rt); - if (r < 0) - return log_oom(); - - if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse netns socket value: %s", value); - else { - safe_close((*rt)->netns_storage_socket[1]); - (*rt)->netns_storage_socket[1] = fdset_remove(fds, fd); - } - } else - return 0; - - return 1; -} - -static void *remove_tmpdir_thread(void *p) { - _cleanup_free_ char *path = p; - - (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL); - return NULL; -} - -void exec_runtime_destroy(ExecRuntime *rt) { - int r; - - if (!rt) - return; - - /* If there are multiple users of this, let's leave the stuff around */ - if (rt->n_ref > 1) - return; - - if (rt->tmp_dir) { - log_debug("Spawning thread to nuke %s", rt->tmp_dir); - - r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir); - if (r < 0) { - log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir); - free(rt->tmp_dir); - } - - rt->tmp_dir = NULL; - } - - if (rt->var_tmp_dir) { - log_debug("Spawning thread to nuke %s", rt->var_tmp_dir); - - r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir); - if (r < 0) { - log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir); - free(rt->var_tmp_dir); - } - - rt->var_tmp_dir = NULL; - } - - safe_close_pair(rt->netns_storage_socket); -} - -static const char* const exec_input_table[_EXEC_INPUT_MAX] = { - [EXEC_INPUT_NULL] = "null", - [EXEC_INPUT_TTY] = "tty", - [EXEC_INPUT_TTY_FORCE] = "tty-force", - [EXEC_INPUT_TTY_FAIL] = "tty-fail", - [EXEC_INPUT_SOCKET] = "socket" -}; - -DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); - -static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { - [EXEC_OUTPUT_INHERIT] = "inherit", - [EXEC_OUTPUT_NULL] = "null", - [EXEC_OUTPUT_TTY] = "tty", - [EXEC_OUTPUT_SYSLOG] = "syslog", - [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console", - [EXEC_OUTPUT_KMSG] = "kmsg", - [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", - [EXEC_OUTPUT_JOURNAL] = "journal", - [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console", - [EXEC_OUTPUT_SOCKET] = "socket" -}; - -DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); - -static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { - [EXEC_UTMP_INIT] = "init", - [EXEC_UTMP_LOGIN] = "login", - [EXEC_UTMP_USER] = "user", -}; - -DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/core/execute.h b/src/core/execute.h deleted file mode 100644 index 189c4d0999..0000000000 --- a/src/core/execute.h +++ /dev/null @@ -1,291 +0,0 @@ -#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 . -***/ - -typedef struct ExecStatus ExecStatus; -typedef struct ExecCommand ExecCommand; -typedef struct ExecContext ExecContext; -typedef struct ExecRuntime ExecRuntime; -typedef struct ExecParameters ExecParameters; - -#include -#include -#include -#include - -#include "cgroup-util.h" -#include "fdset.h" -#include "list.h" -#include "missing.h" -#include "namespace.h" - -typedef enum ExecUtmpMode { - EXEC_UTMP_INIT, - EXEC_UTMP_LOGIN, - EXEC_UTMP_USER, - _EXEC_UTMP_MODE_MAX, - _EXEC_UTMP_MODE_INVALID = -1 -} ExecUtmpMode; - -typedef enum ExecInput { - EXEC_INPUT_NULL, - EXEC_INPUT_TTY, - EXEC_INPUT_TTY_FORCE, - EXEC_INPUT_TTY_FAIL, - EXEC_INPUT_SOCKET, - _EXEC_INPUT_MAX, - _EXEC_INPUT_INVALID = -1 -} ExecInput; - -typedef enum ExecOutput { - EXEC_OUTPUT_INHERIT, - EXEC_OUTPUT_NULL, - EXEC_OUTPUT_TTY, - EXEC_OUTPUT_SYSLOG, - EXEC_OUTPUT_SYSLOG_AND_CONSOLE, - EXEC_OUTPUT_KMSG, - EXEC_OUTPUT_KMSG_AND_CONSOLE, - EXEC_OUTPUT_JOURNAL, - EXEC_OUTPUT_JOURNAL_AND_CONSOLE, - EXEC_OUTPUT_SOCKET, - _EXEC_OUTPUT_MAX, - _EXEC_OUTPUT_INVALID = -1 -} ExecOutput; - -struct ExecStatus { - dual_timestamp start_timestamp; - dual_timestamp exit_timestamp; - pid_t pid; - int code; /* as in siginfo_t::si_code */ - int status; /* as in sigingo_t::si_status */ -}; - -struct ExecCommand { - char *path; - char **argv; - ExecStatus exec_status; - LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */ - bool ignore:1; - bool privileged:1; -}; - -struct ExecRuntime { - int n_ref; - - char *tmp_dir; - char *var_tmp_dir; - - int netns_storage_socket[2]; -}; - -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; - int nice; - int ioprio; - int cpu_sched_policy; - int cpu_sched_priority; - - cpu_set_t *cpuset; - unsigned cpuset_ncpus; - - ExecInput std_input; - ExecOutput std_output; - ExecOutput std_error; - - nsec_t timer_slack_nsec; - - bool stdio_as_fds; - - char *tty_path; - - bool tty_reset; - bool tty_vhangup; - bool tty_vt_disallocate; - - bool ignore_sigpipe; - - /* Since resolving these names might involve socket - * connections and we don't want to deadlock ourselves these - * names are resolved on execution only and in the child - * process. */ - char *user; - char *group; - char **supplementary_groups; - - char *pam_name; - - char *utmp_id; - ExecUtmpMode utmp_mode; - - bool selinux_context_ignore; - char *selinux_context; - - bool apparmor_profile_ignore; - char *apparmor_profile; - - bool smack_process_label_ignore; - char *smack_process_label; - - char **read_write_paths, **read_only_paths, **inaccessible_paths; - unsigned long mount_flags; - - uint64_t capability_bounding_set; - uint64_t capability_ambient_set; - int secure_bits; - - int syslog_priority; - char *syslog_identifier; - bool syslog_level_prefix; - - bool cpu_sched_reset_on_fork; - bool non_blocking; - bool private_tmp; - bool private_network; - bool private_devices; - ProtectSystem protect_system; - ProtectHome protect_home; - - bool no_new_privileges; - - /* This is not exposed to the user but available - * internally. We need it to make sure that whenever we spawn - * /usr/bin/mount it is run in the same process group as us so - * that the autofs logic detects that it belongs to us and we - * don't enter a trigger loop. */ - bool same_pgrp; - - unsigned long personality; - - Set *syscall_filter; - Set *syscall_archs; - int syscall_errno; - bool syscall_whitelist:1; - - Set *address_families; - bool address_families_whitelist:1; - - char **runtime_directory; - mode_t runtime_directory_mode; - - bool memory_deny_write_execute; - bool restrict_realtime; - - bool oom_score_adjust_set:1; - bool nice_set:1; - bool ioprio_set:1; - bool cpu_sched_set:1; - bool no_new_privileges_set:1; -}; - -struct ExecParameters { - char **argv; - char **environment; - - 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; - - const char *runtime_prefix; - - usec_t watchdog_usec; - - int *idle_pipe; - - int stdin_fd; - int stdout_fd; - int stderr_fd; -}; - -#include "unit.h" - -int exec_spawn(Unit *unit, - ExecCommand *command, - const ExecContext *context, - const ExecParameters *exec_params, - ExecRuntime *runtime, - pid_t *ret); - -void exec_command_done(ExecCommand *c); -void exec_command_done_array(ExecCommand *c, unsigned n); - -ExecCommand* exec_command_free_list(ExecCommand *c); -void exec_command_free_array(ExecCommand **c, unsigned n); - -char *exec_command_line(char **argv); - -void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix); -void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix); -void exec_command_append_list(ExecCommand **l, ExecCommand *e); -int exec_command_set(ExecCommand *c, const char *path, ...); -int exec_command_append(ExecCommand *c, const char *path, ...); - -void exec_context_init(ExecContext *c); -void exec_context_done(ExecContext *c); -void exec_context_dump(ExecContext *c, FILE* f, const char *prefix); - -int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root); - -int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l); - -bool exec_context_may_touch_console(ExecContext *c); -bool exec_context_maintains_privileges(ExecContext *c); - -void exec_status_start(ExecStatus *s, pid_t pid); -void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); -void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); - -int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id); -ExecRuntime *exec_runtime_ref(ExecRuntime *r); -ExecRuntime *exec_runtime_unref(ExecRuntime *r); - -int exec_runtime_serialize(Unit *unit, ExecRuntime *rt, FILE *f, FDSet *fds); -int exec_runtime_deserialize_item(Unit *unit, ExecRuntime **rt, const char *key, const char *value, FDSet *fds); - -void exec_runtime_destroy(ExecRuntime *rt); - -const char* exec_output_to_string(ExecOutput i) _const_; -ExecOutput exec_output_from_string(const char *s) _pure_; - -const char* exec_input_to_string(ExecInput i) _const_; -ExecInput exec_input_from_string(const char *s) _pure_; - -const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; -ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/core/failure-action.c b/src/core/failure-action.c deleted file mode 100644 index ddae46190f..0000000000 --- a/src/core/failure-action.c +++ /dev/null @@ -1,127 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - Copyright 2012 Michael Olbrich - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "bus-error.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) { - log_warning("%s", message); - manager_status_printf(m, STATUS_TYPE_EMERGENCY, - ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, - "%s", message); -} - -int failure_action( - Manager *m, - FailureAction action, - const char *reboot_arg) { - - assert(m); - assert(action >= 0); - assert(action < _FAILURE_ACTION_MAX); - - if (action == FAILURE_ACTION_NONE) - return -ECANCELED; - - if (!MANAGER_IS_SYSTEM(m)) { - /* Downgrade all options to simply exiting if we run - * in user mode */ - - log_warning("Exiting as result of failure."); - m->exit_code = MANAGER_EXIT; - return -ECANCELED; - } - - switch (action) { - - case FAILURE_ACTION_REBOOT: - log_and_status(m, "Rebooting as result of failure."); - - (void) update_reboot_parameter_and_warn(reboot_arg); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); - - break; - - case FAILURE_ACTION_REBOOT_FORCE: - log_and_status(m, "Forcibly rebooting as result of failure."); - - (void) update_reboot_parameter_and_warn(reboot_arg); - m->exit_code = MANAGER_REBOOT; - - break; - - case FAILURE_ACTION_REBOOT_IMMEDIATE: - log_and_status(m, "Rebooting immediately as result of failure."); - - sync(); - - if (!isempty(reboot_arg)) { - log_info("Rebooting with argument '%s'.", reboot_arg); - syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg); - log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); - } - - log_info("Rebooting."); - reboot(RB_AUTOBOOT); - break; - - case FAILURE_ACTION_POWEROFF: - log_and_status(m, "Powering off as result of failure."); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); - break; - - case FAILURE_ACTION_POWEROFF_FORCE: - log_and_status(m, "Forcibly powering off as result of failure."); - m->exit_code = MANAGER_POWEROFF; - break; - - case FAILURE_ACTION_POWEROFF_IMMEDIATE: - log_and_status(m, "Powering off immediately as result of failure."); - - sync(); - - log_info("Powering off."); - reboot(RB_POWER_OFF); - break; - - default: - assert_not_reached("Unknown failure action"); - } - - return -ECANCELED; -} - -static const char* const failure_action_table[_FAILURE_ACTION_MAX] = { - [FAILURE_ACTION_NONE] = "none", - [FAILURE_ACTION_REBOOT] = "reboot", - [FAILURE_ACTION_REBOOT_FORCE] = "reboot-force", - [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", - [FAILURE_ACTION_POWEROFF] = "poweroff", - [FAILURE_ACTION_POWEROFF_FORCE] = "poweroff-force", - [FAILURE_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate" -}; -DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction); diff --git a/src/core/failure-action.h b/src/core/failure-action.h deleted file mode 100644 index 1adac4ad5c..0000000000 --- a/src/core/failure-action.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - Copyright 2012 Michael Olbrich - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef enum FailureAction { - FAILURE_ACTION_NONE, - FAILURE_ACTION_REBOOT, - FAILURE_ACTION_REBOOT_FORCE, - FAILURE_ACTION_REBOOT_IMMEDIATE, - FAILURE_ACTION_POWEROFF, - FAILURE_ACTION_POWEROFF_FORCE, - FAILURE_ACTION_POWEROFF_IMMEDIATE, - _FAILURE_ACTION_MAX, - _FAILURE_ACTION_INVALID = -1 -} FailureAction; - -#include "macro.h" -#include "manager.h" - -int failure_action(Manager *m, FailureAction action, const char *reboot_arg); - -const char* failure_action_to_string(FailureAction i) _const_; -FailureAction failure_action_from_string(const char *s) _pure_; diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c deleted file mode 100644 index 68be52856b..0000000000 --- a/src/core/hostname-setup.c +++ /dev/null @@ -1,68 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fileio.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; - _cleanup_free_ char *b = NULL; - const char *hn; - bool enoent = false; - - r = read_hostname_config("/etc/hostname", &b); - if (r < 0) { - if (r == -ENOENT) - enoent = true; - else - log_warning_errno(r, "Failed to read configured hostname: %m"); - - hn = NULL; - } else - hn = b; - - if (isempty(hn)) { - /* Don't override the hostname if it is already set - * and not explicitly configured */ - if (hostname_is_set()) - return 0; - - if (enoent) - log_info("No hostname configured."); - - hn = "localhost"; - } - - 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/hostname-setup.h b/src/core/hostname-setup.h deleted file mode 100644 index 73e8c75c71..0000000000 --- a/src/core/hostname-setup.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int hostname_setup(void); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c deleted file mode 100644 index d1b0ce76ef..0000000000 --- a/src/core/ima-setup.c +++ /dev/null @@ -1,80 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group — http://security.polito.it - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fd-util.h" -#include "fileio.h" -#include "ima-setup.h" -#include "log.h" -#include "util.h" - -#define IMA_SECFS_DIR "/sys/kernel/security/ima" -#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" -#define IMA_POLICY_PATH "/etc/ima/ima-policy" - -int ima_setup(void) { -#ifdef HAVE_IMA - _cleanup_fclose_ FILE *input = NULL; - _cleanup_close_ int imafd = -1; - unsigned lineno = 0; - char line[page_size()]; - - if (access(IMA_SECFS_DIR, F_OK) < 0) { - log_debug("IMA support is disabled in the kernel, ignoring."); - return 0; - } - - input = fopen(IMA_POLICY_PATH, "re"); - if (!input) { - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m"); - return 0; - } - - if (access(IMA_SECFS_POLICY, F_OK) < 0) { - log_warning("Another IMA custom policy has already been loaded, ignoring."); - return 0; - } - - imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC); - if (imafd < 0) { - log_error_errno(errno, "Failed to open the IMA kernel interface "IMA_SECFS_POLICY", ignoring: %m"); - return 0; - } - - FOREACH_LINE(line, input, - return log_error_errno(errno, "Failed to read the IMA custom policy file "IMA_POLICY_PATH": %m")) { - size_t len; - - len = strlen(line); - lineno++; - - if (len > 0 && write(imafd, line, len) < 0) - return log_error_errno(errno, "Failed to load the IMA custom policy file "IMA_POLICY_PATH"%u: %m", - lineno); - } - - log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH"."); -#endif /* HAVE_IMA */ - return 0; -} diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h deleted file mode 100644 index 472b58cb00..0000000000 --- a/src/core/ima-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group — http://security.polito.it - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int ima_setup(void); diff --git a/src/core/job.c b/src/core/job.c deleted file mode 100644 index 7557874d4d..0000000000 --- a/src/core/job.c +++ /dev/null @@ -1,1259 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-id128.h" -#include "sd-messages.h" - -#include "alloc-util.h" -#include "async.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; - - /* used for deserialization */ - - assert(unit); - - j = new0(Job, 1); - if (!j) - return NULL; - - j->manager = unit->manager; - j->unit = unit; - j->type = _JOB_TYPE_INVALID; - - return j; -} - -Job* job_new(Unit *unit, JobType type) { - Job *j; - - assert(type < _JOB_TYPE_MAX); - - j = job_new_raw(unit); - if (!j) - return NULL; - - j->id = j->manager->current_job_id++; - j->type = type; - - /* We don't link it here, that's what job_dependency() is for */ - - return j; -} - -void job_free(Job *j) { - assert(j); - assert(!j->installed); - assert(!j->transaction_prev); - assert(!j->transaction_next); - assert(!j->subject_list); - assert(!j->object_list); - - if (j->in_run_queue) - LIST_REMOVE(run_queue, j->manager->run_queue, j); - - if (j->in_dbus_queue) - LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); - - sd_event_source_unref(j->timer_event_source); - - sd_bus_track_unref(j->clients); - strv_free(j->deserialized_clients); - - free(j); -} - -static void job_set_state(Job *j, JobState state) { - assert(j); - assert(state >= 0); - assert(state < _JOB_STATE_MAX); - - if (j->state == state) - return; - - j->state = state; - - if (!j->installed) - return; - - if (j->state == JOB_RUNNING) - j->unit->manager->n_running_jobs++; - else { - assert(j->state == JOB_WAITING); - assert(j->unit->manager->n_running_jobs > 0); - - j->unit->manager->n_running_jobs--; - - if (j->unit->manager->n_running_jobs <= 0) - j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source); - } -} - -void job_uninstall(Job *j) { - Job **pj; - - assert(j->installed); - - job_set_state(j, JOB_WAITING); - - pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - assert(*pj == j); - - /* Detach from next 'bigger' objects */ - - /* daemon-reload should be transparent to job observers */ - if (!MANAGER_IS_RELOADING(j->manager)) - bus_job_send_removed_signal(j); - - *pj = NULL; - - unit_add_to_gc_queue(j->unit); - - hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); - j->installed = false; -} - -static bool job_type_allows_late_merge(JobType t) { - /* Tells whether it is OK to merge a job of type 't' with an already - * running job. - * Reloads cannot be merged this way. Think of the sequence: - * 1. Reload of a daemon is in progress; the daemon has already loaded - * its config file, but hasn't completed the reload operation yet. - * 2. Edit foo's config file. - * 3. Trigger another reload to have the daemon use the new config. - * Should the second reload job be merged into the first one, the daemon - * would not know about the new config. - * JOB_RESTART jobs on the other hand can be merged, because they get - * patched into JOB_START after stopping the unit. So if we see a - * JOB_RESTART running, it means the unit hasn't stopped yet and at - * this time the merge is still allowed. */ - return t != JOB_RELOAD; -} - -static void job_merge_into_installed(Job *j, Job *other) { - assert(j->installed); - assert(j->unit == other->unit); - - if (j->type != JOB_NOP) - job_type_merge_and_collapse(&j->type, other->type, j->unit); - else - assert(other->type == JOB_NOP); - - j->irreversible = j->irreversible || other->irreversible; - j->ignore_order = j->ignore_order || other->ignore_order; -} - -Job* job_install(Job *j) { - Job **pj; - Job *uj; - - assert(!j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - assert(j->state == JOB_WAITING); - - pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - uj = *pj; - - if (uj) { - if (job_type_is_conflicting(uj->type, j->type)) - job_finish_and_invalidate(uj, JOB_CANCELED, false, false); - else { - /* not conflicting, i.e. mergeable */ - - if (uj->state == JOB_WAITING || - (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) { - job_merge_into_installed(uj, j); - log_unit_debug(uj->unit, - "Merged into installed job %s/%s as %u", - uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); - return uj; - } else { - /* already running and not safe to merge into */ - /* Patch uj to become a merged job and re-run it. */ - /* XXX It should be safer to queue j to run after uj finishes, but it is - * not currently possible to have more than one installed job per unit. */ - job_merge_into_installed(uj, j); - log_unit_debug(uj->unit, - "Merged into running job, re-running: %s/%s as %u", - uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); - - job_set_state(uj, JOB_WAITING); - return uj; - } - } - } - - /* Install the job */ - *pj = j; - j->installed = true; - - j->manager->n_installed_jobs++; - log_unit_debug(j->unit, - "Installed new job %s/%s as %u", - j->unit->id, job_type_to_string(j->type), (unsigned) j->id); - return j; -} - -int job_install_deserialized(Job *j) { - Job **pj; - - assert(!j->installed); - - if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) { - log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type))); - return -EINVAL; - } - - pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - if (*pj) { - log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job."); - return -EEXIST; - } - - *pj = j; - j->installed = true; - - if (j->state == JOB_RUNNING) - j->unit->manager->n_running_jobs++; - - log_unit_debug(j->unit, - "Reinstalled deserialized job %s/%s as %u", - j->unit->id, job_type_to_string(j->type), (unsigned) j->id); - return 0; -} - -JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) { - JobDependency *l; - - assert(object); - - /* Adds a new job link, which encodes that the 'subject' job - * needs the 'object' job in some way. If 'subject' is NULL - * this means the 'anchor' job (i.e. the one the user - * explicitly asked for) is the requester. */ - - if (!(l = new0(JobDependency, 1))) - return NULL; - - l->subject = subject; - l->object = object; - l->matters = matters; - l->conflicts = conflicts; - - if (subject) - LIST_PREPEND(subject, subject->subject_list, l); - - LIST_PREPEND(object, object->object_list, l); - - return l; -} - -void job_dependency_free(JobDependency *l) { - assert(l); - - if (l->subject) - LIST_REMOVE(subject, l->subject->subject_list, l); - - LIST_REMOVE(object, l->object->object_list, l); - - free(l); -} - -void job_dump(Job *j, FILE*f, const char *prefix) { - assert(j); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%s-> Job %u:\n" - "%s\tAction: %s -> %s\n" - "%s\tState: %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->irreversible)); -} - -/* - * Merging is commutative, so imagine the matrix as symmetric. We store only - * its lower triangle to avoid duplication. We don't store the main diagonal, - * because A merged with A is simply A. - * - * If the resulting type is collapsed immediately afterwards (to get rid of - * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain), - * the following properties hold: - * - * Merging is associative! A merged with B, and then merged with C is the same - * as A merged with the result of B merged with C. - * - * Mergeability is transitive! If A can be merged with B and B with C then - * A also with C. - * - * Also, if A merged with B cannot be merged with C, then either A or B cannot - * be merged with C either. - */ -static const JobType job_merging_table[] = { -/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD */ -/*********************************************************************************/ -/*JOB_START */ -/*JOB_VERIFY_ACTIVE */ JOB_START, -/*JOB_STOP */ -1, -1, -/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1, -/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, -}; - -JobType job_type_lookup_merge(JobType a, JobType b) { - assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2); - assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING); - assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING); - - if (a == b) - return a; - - if (a < b) { - JobType tmp = a; - a = b; - b = tmp; - } - - return job_merging_table[(a - 1) * a / 2 + b]; -} - -bool job_type_is_redundant(JobType a, UnitActiveState b) { - switch (a) { - - case JOB_START: - return - b == UNIT_ACTIVE || - b == UNIT_RELOADING; - - case JOB_STOP: - return - b == UNIT_INACTIVE || - b == UNIT_FAILED; - - case JOB_VERIFY_ACTIVE: - return - b == UNIT_ACTIVE || - b == UNIT_RELOADING; - - case JOB_RELOAD: - return - b == UNIT_RELOADING; - - case JOB_RESTART: - return - b == UNIT_ACTIVATING; - - case JOB_NOP: - return true; - - default: - assert_not_reached("Invalid job type"); - } -} - -JobType job_type_collapse(JobType t, Unit *u) { - UnitActiveState s; - - switch (t) { - - case JOB_TRY_RESTART: - s = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) - return JOB_NOP; - - return JOB_RESTART; - - case JOB_TRY_RELOAD: - s = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) - return JOB_NOP; - - return JOB_RELOAD; - - case JOB_RELOAD_OR_START: - s = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) - return JOB_START; - - return JOB_RELOAD; - - default: - return t; - } -} - -int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) { - JobType t; - - t = job_type_lookup_merge(*a, b); - if (t < 0) - return -EEXIST; - - *a = job_type_collapse(t, u); - return 0; -} - -static bool job_is_runnable(Job *j) { - Iterator i; - Unit *other; - - assert(j); - assert(j->installed); - - /* Checks whether there is any job running for the units this - * job needs to be running after (in the case of a 'positive' - * job type) or before (in the case of a 'negative' job - * type. */ - - /* Note that unit types have a say in what is runnable, - * too. For example, if they return -EAGAIN from - * unit_start() they can indicate they are not - * runnable yet. */ - - /* First check if there is an override */ - if (j->ignore_order) - return true; - - if (j->type == JOB_NOP) - return true; - - if (j->type == JOB_START || - j->type == JOB_VERIFY_ACTIVE || - j->type == JOB_RELOAD) { - - /* Immediate result is that the job is or might be - * started. In this case let's wait for the - * dependencies, regardless whether they are - * starting or stopping something. */ - - SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) - if (other->job) - return false; - } - - /* Also, if something else is being stopped and we should - * change state after it, then let's wait. */ - - SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) - if (other->job && - (other->job->type == JOB_STOP || - other->job->type == JOB_RESTART)) - return false; - - /* This means that for a service a and a service b where b - * shall be started after a: - * - * start a + start b → 1st step start a, 2nd step start b - * start a + stop b → 1st step stop b, 2nd step start a - * stop a + start b → 1st step stop a, 2nd step start b - * stop a + stop b → 1st step stop b, 2nd step stop a - * - * This has the side effect that restarts are properly - * synchronized too. */ - - return true; -} - -static void job_change_type(Job *j, JobType newtype) { - assert(j); - - log_unit_debug(j->unit, - "Converting job %s/%s -> %s/%s", - j->unit->id, job_type_to_string(j->type), - j->unit->id, job_type_to_string(newtype)); - - j->type = newtype; -} - -static int job_perform_on_unit(Job **j) { - 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); - break; - - case JOB_RESTART: - t = JOB_STOP; - /* fall through */ - case JOB_STOP: - r = unit_stop(u); - break; - - case JOB_RELOAD: - r = unit_reload(u); - break; - - default: - assert_not_reached("Invalid job type"); - } - - /* Log if the job still exists and the start/stop/reload function - * actually did something. */ - *j = manager_get_job(m, id); - if (*j && r > 0) - unit_status_emit_starting_stopping_reloading(u, t); - - return r; -} - -int job_run_and_invalidate(Job *j) { - int r; - - assert(j); - assert(j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - assert(j->in_run_queue); - - LIST_REMOVE(run_queue, j->manager->run_queue, j); - j->in_run_queue = false; - - if (j->state != JOB_WAITING) - return 0; - - if (!job_is_runnable(j)) - return -EAGAIN; - - job_set_state(j, JOB_RUNNING); - job_add_to_dbus_queue(j); - - - switch (j->type) { - - case JOB_VERIFY_ACTIVE: { - UnitActiveState t = unit_active_state(j->unit); - if (UNIT_IS_ACTIVE_OR_RELOADING(t)) - r = -EALREADY; - else if (t == UNIT_ACTIVATING) - r = -EAGAIN; - else - r = -EBADR; - break; - } - - case JOB_START: - case JOB_STOP: - case JOB_RESTART: - r = job_perform_on_unit(&j); - - /* If the unit type does not support starting/stopping, - * then simply wait. */ - if (r == -EBADR) - r = 0; - break; - - case JOB_RELOAD: - r = job_perform_on_unit(&j); - break; - - case JOB_NOP: - r = -EALREADY; - break; - - default: - assert_not_reached("Unknown job type"); - } - - if (j) { - if (r == -EALREADY) - r = job_finish_and_invalidate(j, JOB_DONE, true, true); - else if (r == -EBADR) - r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false); - else if (r == -ENOEXEC) - r = job_finish_and_invalidate(j, JOB_INVALID, true, false); - else if (r == -EPROTO) - r = job_finish_and_invalidate(j, JOB_ASSERT, true, false); - else if (r == -EOPNOTSUPP) - r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); - else if (r == -EAGAIN) - job_set_state(j, JOB_WAITING); - else if (r < 0) - r = job_finish_and_invalidate(j, JOB_FAILED, true, false); - } - - return r; -} - -_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { - - static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = { - [JOB_DONE] = "Started %s.", - [JOB_TIMEOUT] = "Timed out starting %s.", - [JOB_FAILED] = "Failed to start %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", - [JOB_ASSERT] = "Assertion failed for %s.", - [JOB_UNSUPPORTED] = "Starting of %s not supported.", - }; - static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { - [JOB_DONE] = "Stopped %s.", - [JOB_FAILED] = "Stopped (with error) %s.", - [JOB_TIMEOUT] = "Timed out stopping %s.", - }; - static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = { - [JOB_DONE] = "Reloaded %s.", - [JOB_FAILED] = "Reload failed for %s.", - [JOB_TIMEOUT] = "Timed out reloading %s.", - }; - /* When verify-active detects the unit is inactive, report it. - * Most likely a DEPEND warning from a requisiting unit will - * occur next and it's nice to see what was requisited. */ - static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = { - [JOB_SKIPPED] = "%s is not active.", - }; - - const UnitStatusMessageFormats *format_table; - const char *format; - - assert(u); - assert(t >= 0); - assert(t < _JOB_TYPE_MAX); - - 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] : - format_table->finished_stop_job[result]; - if (format) - return format; - } - } - - /* Return generic strings */ - if (t == JOB_START) - return generic_finished_start_job[result]; - else if (t == JOB_STOP || t == JOB_RESTART) - return generic_finished_stop_job[result]; - else if (t == JOB_RELOAD) - return generic_finished_reload_job[result]; - else if (t == JOB_VERIFY_ACTIVE) - return generic_finished_verify_active_job[result]; - - return NULL; -} - -static void job_print_status_message(Unit *u, JobType t, JobResult result) { - static struct { - const char *color, *word; - } const statuses[_JOB_RESULT_MAX] = { - [JOB_DONE] = {ANSI_GREEN, " OK "}, - [JOB_TIMEOUT] = {ANSI_HIGHLIGHT_RED, " TIME "}, - [JOB_FAILED] = {ANSI_HIGHLIGHT_RED, "FAILED"}, - [JOB_DEPENDENCY] = {ANSI_HIGHLIGHT_YELLOW, "DEPEND"}, - [JOB_SKIPPED] = {ANSI_HIGHLIGHT, " INFO "}, - [JOB_ASSERT] = {ANSI_HIGHLIGHT_YELLOW, "ASSERT"}, - [JOB_UNSUPPORTED] = {ANSI_HIGHLIGHT_YELLOW, "UNSUPP"}, - }; - - const char *format; - const char *status; - - 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; - - if (log_get_show_color()) - status = strjoina(statuses[result].color, statuses[result].word, ANSI_NORMAL); - else - status = statuses[result].word; - - if (result != JOB_DONE) - manager_flip_auto_status(u->manager, true); - - DISABLE_WARNING_FORMAT_NONLITERAL; - unit_status_printf(u, status, format); - REENABLE_WARNING; - - if (t == JOB_START && result == JOB_FAILED) { - _cleanup_free_ char *quoted; - - quoted = shell_maybe_quote(u->id); - manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted)); - } -} - -static void job_log_status_message(Unit *u, JobType t, JobResult result) { - const char *format; - char buf[LINE_MAX]; - sd_id128_t mid; - static const int job_result_log_level[_JOB_RESULT_MAX] = { - [JOB_DONE] = LOG_INFO, - [JOB_CANCELED] = LOG_INFO, - [JOB_TIMEOUT] = LOG_ERR, - [JOB_FAILED] = LOG_ERR, - [JOB_DEPENDENCY] = LOG_WARNING, - [JOB_SKIPPED] = LOG_NOTICE, - [JOB_INVALID] = LOG_INFO, - [JOB_ASSERT] = LOG_WARNING, - [JOB_UNSUPPORTED] = LOG_WARNING, - }; - - assert(u); - assert(t >= 0); - assert(t < _JOB_TYPE_MAX); - - /* Skip this if it goes to the console. since we already print - * to the console anyway... */ - - if (log_on_console()) - return; - - format = job_get_status_message_format(u, t, result); - if (!format) - return; - - DISABLE_WARNING_FORMAT_NONLITERAL; - xsprintf(buf, format, unit_description(u)); - REENABLE_WARNING; - - switch (t) { - - case JOB_START: - mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - break; - - case JOB_RELOAD: - mid = SD_MESSAGE_UNIT_RELOADED; - 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), - "RESULT=%s", job_result_to_string(result), - NULL); - return; - } - - log_struct(job_result_log_level[result], - LOG_MESSAGE_ID(mid), - LOG_UNIT_ID(u), - LOG_MESSAGE("%s", buf), - "RESULT=%s", job_result_to_string(result), - NULL); -} - -static void job_emit_status_message(Unit *u, JobType t, JobResult result) { - - /* No message if the job did not actually do anything due to failed condition. */ - if (t == JOB_START && result == JOB_DONE && !u->condition_result) - return; - - job_log_status_message(u, t, result); - job_print_status_message(u, t, result); -} - -static void job_fail_dependencies(Unit *u, UnitDependency d) { - Unit *other; - Iterator i; - - assert(u); - - SET_FOREACH(other, u->dependencies[d], i) { - Job *j = other->job; - - if (!j) - continue; - if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE)) - continue; - - job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); - } -} - -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { - Unit *u; - Unit *other; - JobType t; - Iterator i; - - assert(j); - assert(j->installed); - assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); - - u = j->unit; - t = j->type; - - j->result = result; - - log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); - - /* If this job did nothing to respective unit we don't log the status message */ - if (!already) - job_emit_status_message(u, t, result); - - job_add_to_dbus_queue(j); - - /* Patch restart jobs so that they become normal start jobs */ - if (result == JOB_DONE && t == JOB_RESTART) { - - job_change_type(j, JOB_START); - job_set_state(j, JOB_WAITING); - - job_add_to_run_queue(j); - - goto finish; - } - - if (result == JOB_FAILED || result == JOB_INVALID) - j->manager->n_failed_jobs++; - - job_uninstall(j); - job_free(j); - - /* Fail depending jobs on failure */ - if (result != JOB_DONE && recursive) { - if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) { - job_fail_dependencies(u, UNIT_REQUIRED_BY); - job_fail_dependencies(u, UNIT_REQUISITE_OF); - job_fail_dependencies(u, UNIT_BOUND_BY); - } else if (t == JOB_STOP) - job_fail_dependencies(u, UNIT_CONFLICTED_BY); - } - - /* Trigger OnFailure dependencies that are not generated by - * the unit itself. We don't treat JOB_CANCELED as failure in - * this context. And JOB_FAILURE is already handled by the - * unit itself. */ - if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { - log_struct(LOG_NOTICE, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - LOG_UNIT_ID(u), - LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.", - u->id, - job_type_to_string(t), - job_result_to_string(result)), - NULL); - - unit_start_on_failure(u); - } - - unit_trigger_notify(u); - -finish: - /* Try to start the next jobs that can be started */ - SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) - if (other->job) - job_add_to_run_queue(other->job); - SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i) - if (other->job) - job_add_to_run_queue(other->job); - - manager_check_finished(u->manager); - - return 0; -} - -static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) { - Job *j = userdata; - Unit *u; - - assert(j); - assert(s == j->timer_event_source); - - log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); - - u = j->unit; - job_finish_and_invalidate(j, JOB_TIMEOUT, true, false); - - failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); - - return 0; -} - -int job_start_timer(Job *j) { - int r; - - if (j->timer_event_source) - return 0; - - j->begin_usec = now(CLOCK_MONOTONIC); - - if (j->unit->job_timeout == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - j->manager->event, - &j->timer_event_source, - CLOCK_MONOTONIC, - usec_add(j->begin_usec, j->unit->job_timeout), 0, - job_dispatch_timer, j); - if (r < 0) - return r; - - (void) sd_event_source_set_description(j->timer_event_source, "job-start"); - - return 0; -} - -void job_add_to_run_queue(Job *j) { - assert(j); - assert(j->installed); - - if (j->in_run_queue) - return; - - if (!j->manager->run_queue) - sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT); - - LIST_PREPEND(run_queue, j->manager->run_queue, j); - j->in_run_queue = true; -} - -void job_add_to_dbus_queue(Job *j) { - assert(j); - assert(j->installed); - - if (j->in_dbus_queue) - return; - - /* We don't check if anybody is subscribed here, since this - * job might just have been created and not yet assigned to a - * connection/client. */ - - LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j); - j->in_dbus_queue = true; -} - -char *job_dbus_path(Job *j) { - char *p; - - assert(j); - - if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0) - return NULL; - - return p; -} - -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-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)); - - if (j->begin_usec > 0) - fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec); - - bus_track_serialize(j->clients, f); - - /* End marker */ - fputc('\n', f); - return 0; -} - -int job_deserialize(Job *j, FILE *f, FDSet *fds) { - assert(j); - - for (;;) { - char line[LINE_MAX], *l, *v; - size_t k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - return 0; - return -errno; - } - - char_array_0(line); - l = strstrip(line); - - /* End marker */ - if (l[0] == 0) - return 0; - - k = strcspn(l, "="); - - if (l[k] == '=') { - l[k] = 0; - v = l+k+1; - } else - v = l+k; - - if (streq(l, "job-id")) { - - if (safe_atou32(v, &j->id) < 0) - log_debug("Failed to parse job id value %s", v); - - } else if (streq(l, "job-type")) { - JobType t; - - t = job_type_from_string(v); - if (t < 0) - log_debug("Failed to parse job type %s", v); - else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION) - log_debug("Cannot deserialize job of type %s", v); - else - j->type = t; - - } else if (streq(l, "job-state")) { - JobState s; - - s = job_state_from_string(v); - if (s < 0) - log_debug("Failed to parse job state %s", v); - else - job_set_state(j, s); - - } else if (streq(l, "job-irreversible")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job irreversible flag %s", v); - else - j->irreversible = j->irreversible || b; - - } else if (streq(l, "job-sent-dbus-new-signal")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job sent_dbus_new_signal flag %s", v); - else - j->sent_dbus_new_signal = j->sent_dbus_new_signal || b; - - } else if (streq(l, "job-ignore-order")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job ignore_order flag %s", v); - else - j->ignore_order = j->ignore_order || b; - - } else if (streq(l, "job-begin")) { - unsigned long long ull; - - if (sscanf(v, "%llu", &ull) != 1) - log_debug("Failed to parse job-begin value %s", v); - else - j->begin_usec = ull; - - } else if (streq(l, "subscribed")) { - - if (strv_extend(&j->deserialized_clients, v) < 0) - return log_oom(); - } - } -} - -int job_coldplug(Job *j) { - int r; - - assert(j); - - /* After deserialization is complete and the bus connection - * set up again, let's start watching our subscribers again */ - r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients); - if (r < 0) - return r; - - if (j->state == JOB_WAITING) - job_add_to_run_queue(j); - - if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY) - return 0; - - j->timer_event_source = sd_event_source_unref(j->timer_event_source); - - r = sd_event_add_time( - j->manager->event, - &j->timer_event_source, - CLOCK_MONOTONIC, - usec_add(j->begin_usec, j->unit->job_timeout), 0, - job_dispatch_timer, j); - if (r < 0) - log_debug_errno(r, "Failed to restart timeout for job: %m"); - - (void) sd_event_source_set_description(j->timer_event_source, "job-timeout"); - - return r; -} - -void job_shutdown_magic(Job *j) { - assert(j); - - /* The shutdown target gets some special treatment here: we - * tell the kernel to begin with flushing its disk caches, to - * optimize shutdown time a bit. Ideally we wouldn't hardcode - * this magic into PID 1. However all other processes aren't - * options either since they'd exit much sooner than PID 1 and - * asynchronous sync() would cause their exit to be - * delayed. */ - - if (j->type != JOB_START) - return; - - if (!MANAGER_IS_SYSTEM(j->unit->manager)) - return; - - if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET)) - return; - - /* In case messages on console has been disabled on boot */ - j->unit->manager->no_console_output = false; - - if (detect_container() > 0) - return; - - asynchronous_sync(); -} - -int job_get_timeout(Job *j, usec_t *timeout) { - usec_t x = USEC_INFINITY, y = USEC_INFINITY; - Unit *u = j->unit; - int r; - - assert(u); - - if (j->timer_event_source) { - r = sd_event_source_get_time(j->timer_event_source, &x); - if (r < 0) - return r; - } - - if (UNIT_VTABLE(u)->get_timeout) { - r = UNIT_VTABLE(u)->get_timeout(u, &y); - if (r < 0) - return r; - } - - if (x == USEC_INFINITY && y == USEC_INFINITY) - return 0; - - *timeout = MIN(x, y); - return 1; -} - -static const char* const job_state_table[_JOB_STATE_MAX] = { - [JOB_WAITING] = "waiting", - [JOB_RUNNING] = "running" -}; - -DEFINE_STRING_TABLE_LOOKUP(job_state, JobState); - -static const char* const job_type_table[_JOB_TYPE_MAX] = { - [JOB_START] = "start", - [JOB_VERIFY_ACTIVE] = "verify-active", - [JOB_STOP] = "stop", - [JOB_RELOAD] = "reload", - [JOB_RELOAD_OR_START] = "reload-or-start", - [JOB_RESTART] = "restart", - [JOB_TRY_RESTART] = "try-restart", - [JOB_TRY_RELOAD] = "try-reload", - [JOB_NOP] = "nop", -}; - -DEFINE_STRING_TABLE_LOOKUP(job_type, JobType); - -static const char* const job_mode_table[_JOB_MODE_MAX] = { - [JOB_FAIL] = "fail", - [JOB_REPLACE] = "replace", - [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly", - [JOB_ISOLATE] = "isolate", - [JOB_FLUSH] = "flush", - [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies", - [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements", -}; - -DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode); - -static const char* const job_result_table[_JOB_RESULT_MAX] = { - [JOB_DONE] = "done", - [JOB_CANCELED] = "canceled", - [JOB_TIMEOUT] = "timeout", - [JOB_FAILED] = "failed", - [JOB_DEPENDENCY] = "dependency", - [JOB_SKIPPED] = "skipped", - [JOB_INVALID] = "invalid", - [JOB_ASSERT] = "assert", - [JOB_UNSUPPORTED] = "unsupported", -}; - -DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); - -const char* job_type_to_access_method(JobType t) { - assert(t >= 0); - assert(t < _JOB_TYPE_MAX); - - if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART)) - return "start"; - else if (t == JOB_STOP) - return "stop"; - else - return "reload"; -} diff --git a/src/core/job.h b/src/core/job.h deleted file mode 100644 index d359e8bb3e..0000000000 --- a/src/core/job.h +++ /dev/null @@ -1,242 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-event.h" - -#include "list.h" -#include "unit-name.h" - -typedef struct Job Job; -typedef struct JobDependency JobDependency; -typedef enum JobType JobType; -typedef enum JobState JobState; -typedef enum JobMode JobMode; -typedef enum JobResult JobResult; - -/* Be careful when changing the job types! Adjust job_merging_table[] accordingly! */ -enum JobType { - JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */ - JOB_VERIFY_ACTIVE, - - JOB_STOP, - - JOB_RELOAD, /* if running, reload */ - - /* Note that restarts are first treated like JOB_STOP, but - * then instead of finishing are patched to become - * JOB_START. */ - JOB_RESTART, /* If running, stop. Then start unconditionally. */ - - _JOB_TYPE_MAX_MERGING, - - /* JOB_NOP can enter into a transaction, but as it won't pull in - * any dependencies and it uses the special 'nop_job' slot in Unit, - * it won't have to merge with anything (except possibly into another - * JOB_NOP, previously installed). JOB_NOP is special-cased in - * job_type_is_*() functions so that the transaction can be - * activated. */ - JOB_NOP = _JOB_TYPE_MAX_MERGING, /* do nothing */ - - _JOB_TYPE_MAX_IN_TRANSACTION, - - /* JOB_TRY_RESTART can never appear in a transaction, because - * it always collapses into JOB_RESTART or JOB_NOP before entering. - * Thus we never need to merge it with anything. */ - JOB_TRY_RESTART = _JOB_TYPE_MAX_IN_TRANSACTION, /* if running, stop and then start */ - - /* Similar to JOB_TRY_RESTART but collapses to JOB_RELOAD or JOB_NOP */ - JOB_TRY_RELOAD, - - /* JOB_RELOAD_OR_START won't enter into a transaction and cannot result - * from transaction merging (there's no way for JOB_RELOAD and - * JOB_START to meet in one transaction). It can result from a merge - * during job installation, but then it will immediately collapse into - * one of the two simpler types. */ - JOB_RELOAD_OR_START, /* if running, reload, otherwise start */ - - _JOB_TYPE_MAX, - _JOB_TYPE_INVALID = -1 -}; - -enum JobState { - JOB_WAITING, - JOB_RUNNING, - _JOB_STATE_MAX, - _JOB_STATE_INVALID = -1 -}; - -enum JobMode { - JOB_FAIL, /* Fail if a conflicting job is already queued */ - JOB_REPLACE, /* Replace an existing conflicting job */ - JOB_REPLACE_IRREVERSIBLY,/* Like JOB_REPLACE + produce irreversible jobs */ - JOB_ISOLATE, /* Start a unit, and stop all others */ - JOB_FLUSH, /* Flush out all other queued jobs when queing this one */ - JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */ - JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */ - _JOB_MODE_MAX, - _JOB_MODE_INVALID = -1 -}; - -enum JobResult { - JOB_DONE, /* Job completed successfully */ - JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */ - JOB_TIMEOUT, /* Job timeout elapsed */ - JOB_FAILED, /* Job failed */ - JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */ - JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */ - JOB_INVALID, /* JOB_RELOAD of inactive unit */ - JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ - JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */ - _JOB_RESULT_MAX, - _JOB_RESULT_INVALID = -1 -}; - -#include "unit.h" - -struct JobDependency { - /* Encodes that the 'subject' job needs the 'object' job in - * some way. This structure is used only while building a transaction. */ - Job *subject; - Job *object; - - LIST_FIELDS(JobDependency, subject); - LIST_FIELDS(JobDependency, object); - - bool matters; - bool conflicts; -}; - -struct Job { - Manager *manager; - Unit *unit; - - LIST_FIELDS(Job, transaction); - LIST_FIELDS(Job, run_queue); - LIST_FIELDS(Job, dbus_queue); - - LIST_HEAD(JobDependency, subject_list); - LIST_HEAD(JobDependency, object_list); - - /* Used for graph algs as a "I have been here" marker */ - Job* marker; - unsigned generation; - - uint32_t id; - - JobType type; - JobState state; - - sd_event_source *timer_event_source; - usec_t begin_usec; - - /* - * This tracks where to send signals, and also which clients - * are allowed to call DBus methods on the job (other than - * root). - * - * There can be more than one client, because of job merging. - */ - sd_bus_track *clients; - char **deserialized_clients; - - JobResult result; - - bool installed:1; - bool in_run_queue:1; - bool matters_to_anchor:1; - bool in_dbus_queue:1; - bool sent_dbus_new_signal:1; - bool ignore_order:1; - bool irreversible:1; -}; - -Job* job_new(Unit *unit, JobType type); -Job* job_new_raw(Unit *unit); -void job_free(Job *job); -Job* job_install(Job *j); -int job_install_deserialized(Job *j); -void job_uninstall(Job *j); -void job_dump(Job *j, FILE*f, const char *prefix); -int job_serialize(Job *j, FILE *f, FDSet *fds); -int job_deserialize(Job *j, FILE *f, FDSet *fds); -int job_coldplug(Job *j); - -JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts); -void job_dependency_free(JobDependency *l); - -int job_merge(Job *j, Job *other); - -JobType job_type_lookup_merge(JobType a, JobType b) _pure_; - -_pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) { - return job_type_lookup_merge(a, b) >= 0; -} - -_pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) { - return a != JOB_NOP && b != JOB_NOP && !job_type_is_mergeable(a, b); -} - -_pure_ static inline bool job_type_is_superset(JobType a, JobType b) { - /* Checks whether operation a is a "superset" of b in its actions */ - if (b == JOB_NOP) - return true; - if (a == JOB_NOP) - return false; - return a == job_type_lookup_merge(a, b); -} - -bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_; - -/* Collapses a state-dependent job type into a simpler type by observing - * the state of the unit which it is going to be applied to. */ -JobType job_type_collapse(JobType t, Unit *u); - -int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u); - -void job_add_to_run_queue(Job *j); -void job_add_to_dbus_queue(Job *j); - -int job_start_timer(Job *j); - -int job_run_and_invalidate(Job *j); -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already); - -char *job_dbus_path(Job *j); - -void job_shutdown_magic(Job *j); - -int job_get_timeout(Job *j, usec_t *timeout) _pure_; - -const char* job_type_to_string(JobType t) _const_; -JobType job_type_from_string(const char *s) _pure_; - -const char* job_state_to_string(JobState t) _const_; -JobState job_state_from_string(const char *s) _pure_; - -const char* job_mode_to_string(JobMode t) _const_; -JobMode job_mode_from_string(const char *s) _pure_; - -const char* job_result_to_string(JobResult t) _const_; -JobResult job_result_from_string(const char *s) _pure_; - -const char* job_type_to_access_method(JobType t); diff --git a/src/core/kill.c b/src/core/kill.c deleted file mode 100644 index 6854587d54..0000000000 --- a/src/core/kill.c +++ /dev/null @@ -1,68 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "kill.h" -#include "signal-util.h" -#include "string-table.h" -#include "util.h" - -void kill_context_init(KillContext *c) { - assert(c); - - c->kill_signal = SIGTERM; - c->send_sigkill = true; - c->send_sighup = false; -} - -void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { - assert(c); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%sKillMode: %s\n" - "%sKillSignal: SIG%s\n" - "%sSendSIGKILL: %s\n" - "%sSendSIGHUP: %s\n", - prefix, kill_mode_to_string(c->kill_mode), - prefix, signal_to_string(c->kill_signal), - prefix, yes_no(c->send_sigkill), - prefix, yes_no(c->send_sighup)); -} - -static const char* const kill_mode_table[_KILL_MODE_MAX] = { - [KILL_CONTROL_GROUP] = "control-group", - [KILL_PROCESS] = "process", - [KILL_MIXED] = "mixed", - [KILL_NONE] = "none" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); - -static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_MAIN] = "main", - [KILL_CONTROL] = "control", - [KILL_ALL] = "all", - [KILL_MAIN_FAIL] = "main-fail", - [KILL_CONTROL_FAIL] = "control-fail", - [KILL_ALL_FAIL] = "all-fail" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/core/kill.h b/src/core/kill.h deleted file mode 100644 index b3d2056cb0..0000000000 --- a/src/core/kill.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct KillContext KillContext; - -#include -#include - -#include "macro.h" - -typedef enum KillMode { - /* The kill mode is a property of a unit. */ - KILL_CONTROL_GROUP = 0, - KILL_PROCESS, - KILL_MIXED, - KILL_NONE, - _KILL_MODE_MAX, - _KILL_MODE_INVALID = -1 -} KillMode; - -struct KillContext { - KillMode kill_mode; - int kill_signal; - bool send_sigkill; - bool send_sighup; -}; - -typedef enum KillWho { - /* Kill who is a property of an operation */ - KILL_MAIN, - KILL_CONTROL, - KILL_ALL, - KILL_MAIN_FAIL, - KILL_CONTROL_FAIL, - KILL_ALL_FAIL, - _KILL_WHO_MAX, - _KILL_WHO_INVALID = -1 -} KillWho; - -void kill_context_init(KillContext *c); -void kill_context_dump(KillContext *c, FILE *f, const char *prefix); - -const char *kill_mode_to_string(KillMode k) _const_; -KillMode kill_mode_from_string(const char *s) _pure_; - -const char *kill_who_to_string(KillWho k) _const_; -KillWho kill_who_from_string(const char *s) _pure_; diff --git a/src/core/killall.c b/src/core/killall.c deleted file mode 100644 index a8b814e868..0000000000 --- a/src/core/killall.c +++ /dev/null @@ -1,248 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "def.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" - -static bool ignore_proc(pid_t pid, bool warn_rootfs) { - _cleanup_fclose_ FILE *f = NULL; - char c; - const char *p; - size_t count; - uid_t uid; - int r; - - /* We are PID 1, let's not commit suicide */ - if (pid == 1) - return true; - - r = get_process_uid(pid, &uid); - if (r < 0) - return true; /* not really, but better safe than sorry */ - - /* Non-root processes otherwise are always subject to be killed */ - if (uid != 0) - return false; - - p = procfs_file_alloca(pid, "cmdline"); - f = fopen(p, "re"); - if (!f) - return true; /* not really, but has the desired effect */ - - count = fread(&c, 1, 1, f); - - /* Kernel threads have an empty cmdline */ - if (count <= 0) - return true; - - /* Processes with argv[0][0] = '@' we ignore from the killing - * spree. - * - * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ - if (c == '@' && warn_rootfs) { - _cleanup_free_ char *comm = NULL; - - r = pid_from_same_root_fs(pid); - if (r < 0) - return true; - - get_process_comm(pid, &comm); - - if (r) - log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is " - "running from the root file system, and thus likely to block re-mounting of the " - "root file system to read-only. Please consider moving it into an initrd file " - "system instead.", pid, strna(comm)); - return true; - } else if (c == '@') - return true; - - return false; -} - -static void wait_for_children(Set *pids, sigset_t *mask) { - usec_t until; - - assert(mask); - - if (set_isempty(pids)) - return; - - until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; - for (;;) { - struct timespec ts; - int k; - usec_t n; - void *p; - Iterator i; - - /* First, let the kernel inform us about killed - * children. Most processes will probably be our - * children, but some are not (might be our - * grandchildren instead...). */ - for (;;) { - pid_t pid; - - pid = waitpid(-1, NULL, WNOHANG); - if (pid == 0) - break; - if (pid < 0) { - if (errno == ECHILD) - break; - - log_error_errno(errno, "waitpid() failed: %m"); - return; - } - - (void) set_remove(pids, PID_TO_PTR(pid)); - } - - /* Now explicitly check who might be remaining, who - * might not be our child. */ - SET_FOREACH(p, pids, i) { - - /* We misuse getpgid as a check whether a - * process still exists. */ - if (getpgid(PTR_TO_PID(p)) >= 0) - continue; - - if (errno != ESRCH) - continue; - - set_remove(pids, p); - } - - if (set_isempty(pids)) - return; - - n = now(CLOCK_MONOTONIC); - if (n >= until) - return; - - timespec_store(&ts, until - n); - k = sigtimedwait(mask, NULL, &ts); - if (k != SIGCHLD) { - - if (k < 0 && errno != EAGAIN) { - log_error_errno(errno, "sigtimedwait() failed: %m"); - return; - } - - if (k >= 0) - log_warning("sigtimedwait() returned unexpected signal."); - } - } -} - -static int killall(int sig, Set *pids, bool send_sighup) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *d; - - dir = opendir("/proc"); - if (!dir) - return -errno; - - while ((d = readdir(dir))) { - pid_t pid; - int r; - - if (d->d_type != DT_DIR && - d->d_type != DT_UNKNOWN) - continue; - - if (parse_pid(d->d_name, &pid) < 0) - continue; - - if (ignore_proc(pid, sig == SIGKILL && !in_initrd())) - continue; - - if (sig == SIGKILL) { - _cleanup_free_ char *s = NULL; - - get_process_comm(pid, &s); - log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s)); - } - - if (kill(pid, sig) >= 0) { - if (pids) { - r = set_put(pids, PID_TO_PTR(pid)); - if (r < 0) - log_oom(); - } - } else if (errno != ENOENT) - log_warning_errno(errno, "Could not kill %d: %m", pid); - - if (send_sighup) { - /* Optionally, also send a SIGHUP signal, but - only if the process has a controlling - tty. This is useful to allow handling of - shells which ignore SIGTERM but react to - SIGHUP. We do not send this to processes that - have no controlling TTY since we don't want to - trigger reloads of daemon processes. Also we - make sure to only send this after SIGTERM so - that SIGTERM is always first in the queue. */ - - - if (get_ctty_devnr(pid, NULL) >= 0) - kill(pid, SIGHUP); - } - } - - return set_size(pids); -} - -void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { - sigset_t mask, oldmask; - _cleanup_set_free_ Set *pids = NULL; - - if (wait_for_exit) - pids = set_new(NULL); - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigaddset(&mask, SIGCHLD) == 0); - assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0); - - if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) - log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m"); - - killall(sig, pids, send_sighup); - - if (kill(-1, SIGCONT) < 0 && errno != ESRCH) - log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m"); - - if (wait_for_exit) - wait_for_children(pids, &mask); - - assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); -} diff --git a/src/core/killall.h b/src/core/killall.h deleted file mode 100644 index acc2439f00..0000000000 --- a/src/core/killall.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup); diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c deleted file mode 100644 index fd1021f706..0000000000 --- a/src/core/kmod-setup.c +++ /dev/null @@ -1,128 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#ifdef HAVE_KMOD -#include -#endif - -#include "bus-util.h" -#include "capability-util.h" -#include "kmod-setup.h" -#include "macro.h" - -#ifdef HAVE_KMOD -static void systemd_kmod_log( - void *data, - int priority, - const char *file, int line, - const char *fn, - const char *format, - va_list args) { - - /* library logging is enabled at debug only */ - DISABLE_WARNING_FORMAT_NONLITERAL; - log_internalv(LOG_DEBUG, 0, file, line, fn, format, args); - REENABLE_WARNING; -} -#endif - -int kmod_setup(void) { -#ifdef HAVE_KMOD - - static const struct { - const char *module; - const char *path; - bool warn_if_unavailable:1; - bool warn_if_module:1; - bool (*condition_fn)(void); - } kmod_table[] = { - /* auto-loading on use doesn't work before udev is up */ - { "autofs4", "/sys/class/misc/autofs", true, false, NULL }, - - /* early configure of ::1 on the loopback device */ - { "ipv6", "/sys/module/ipv6", false, true, NULL }, - - /* this should never be a module */ - { "unix", "/proc/net/unix", true, true, NULL }, - -#ifdef HAVE_LIBIPTC - /* netfilter is needed by networkd, nspawn among others, and cannot be autoloaded */ - { "ip_tables", "/proc/net/ip_tables_names", false, false, NULL }, -#endif - }; - struct kmod_ctx *ctx = NULL; - unsigned int i; - int r; - - if (have_effective_cap(CAP_SYS_MODULE) == 0) - return 0; - - for (i = 0; i < ELEMENTSOF(kmod_table); i++) { - struct kmod_module *mod; - - if (kmod_table[i].path && access(kmod_table[i].path, F_OK) >= 0) - continue; - - if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn()) - continue; - - if (kmod_table[i].warn_if_module) - log_debug("Your kernel apparently lacks built-in %s support. Might be " - "a good idea to compile it in. We'll now try to work around " - "this by loading the module...", kmod_table[i].module); - - if (!ctx) { - ctx = kmod_new(NULL, NULL); - if (!ctx) - return log_oom(); - - kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - kmod_load_resources(ctx); - } - - r = kmod_module_new_from_name(ctx, kmod_table[i].module, &mod); - if (r < 0) { - log_error("Failed to lookup module '%s'", kmod_table[i].module); - continue; - } - - r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (r == 0) - log_debug("Inserted module '%s'", kmod_module_get_name(mod)); - else if (r == KMOD_PROBE_APPLY_BLACKLIST) - log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); - else { - bool print_warning = kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT); - - log_full_errno(print_warning ? LOG_WARNING : LOG_DEBUG, r, - "Failed to insert module '%s': %m", kmod_module_get_name(mod)); - } - - kmod_module_unref(mod); - } - - if (ctx) - kmod_unref(ctx); - -#endif - return 0; -} diff --git a/src/core/kmod-setup.h b/src/core/kmod-setup.h deleted file mode 100644 index 685f4df301..0000000000 --- a/src/core/kmod-setup.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int kmod_setup(void); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c deleted file mode 100644 index f83fa09301..0000000000 --- a/src/core/load-dropin.c +++ /dev/null @@ -1,90 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include "conf-parser.h" -#include "load-dropin.h" -#include "load-fragment.h" -#include "log.h" -#include "strv.h" -#include "unit-name.h" -#include "unit.h" - -static int add_dependency_consumer( - UnitDependency dependency, - const char *entry, - const char* filepath, - void *arg) { - Unit *u = arg; - int r; - - assert(u); - - r = unit_add_dependency_by_name(u, dependency, entry, filepath, true); - if (r < 0) - log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", entry, u->id); - - return 0; -} - -int unit_load_dropin(Unit *u) { - _cleanup_strv_free_ char **l = NULL; - Iterator i; - char *t, **f; - int r; - - assert(u); - - /* Load dependencies from supplementary drop-in directories */ - - SET_FOREACH(t, u->names, i) { - char **p; - - STRV_FOREACH(p, u->manager->lookup_paths.search_path) { - unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS, - add_dependency_consumer, u, NULL); - unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES, - add_dependency_consumer, u, NULL); - } - } - - r = unit_find_dropin_paths(u, &l); - if (r <= 0) - return 0; - - if (!u->dropin_paths) { - u->dropin_paths = l; - l = NULL; - } else { - r = strv_extend_strv(&u->dropin_paths, l, true); - if (r < 0) - return log_oom(); - } - - STRV_FOREACH(f, u->dropin_paths) { - config_parse(u->id, *f, NULL, - UNIT_VTABLE(u)->sections, - config_item_perf_lookup, load_fragment_gperf_lookup, - false, false, false, u); - } - - u->dropin_mtime = now(CLOCK_REALTIME); - - return 0; -} diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h deleted file mode 100644 index 942d26724e..0000000000 --- a/src/core/load-dropin.h +++ /dev/null @@ -1,34 +0,0 @@ -#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 . -***/ - -#include "dropin.h" -#include "unit.h" - -/* Read service data supplementary drop-in directories */ - -static inline int unit_find_dropin_paths(Unit *u, char ***paths) { - return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path, - u->manager->unit_path_cache, - u->names, - paths); -} - -int unit_load_dropin(Unit *u); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 deleted file mode 100644 index 6a5c16a000..0000000000 --- a/src/core/load-fragment-gperf.gperf.m4 +++ /dev/null @@ -1,401 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "load-fragment.h" -#include "missing.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name load_fragment_gperf_hash -%define lookup-function-name load_fragment_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -m4_dnl Define the context options only once -m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', -`$1.WorkingDirectory, config_parse_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) -$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) -$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) -$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) -$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) -$1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context) -$1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context) -$1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context) -$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) -$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) -$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) -$1.Environment, config_parse_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) -$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path) -$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset) -$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup) -$1.TTYVTDisallocate, config_parse_bool, 0, offsetof($1, exec_context.tty_vt_disallocate) -$1.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.syslog_identifier) -$1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority) -$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority) -$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) -$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) -$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) -$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', -`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) -$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) -$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context) -$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute) -$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime) -$1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)', -`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -$1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -$1.RestrictRealtime, 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.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) -$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) -$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) -$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) -$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) -$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) -$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) -$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) -$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) -$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) -$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) -$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) -$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) -$1.ReadWritePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) -$1.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) -$1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) -$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) -$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) -$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices) -$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) -$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context) -$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) -$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality) -$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode) -$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory) -m4_ifdef(`HAVE_PAM', -`$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)', -`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) -$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) -$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) -m4_ifdef(`HAVE_SELINUX', -`$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', -`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -m4_ifdef(`HAVE_APPARMOR', -`$1.AppArmorProfile, config_parse_exec_apparmor_profile, 0, offsetof($1, exec_context)', -`$1.AppArmorProfile, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -m4_ifdef(`HAVE_SMACK', -`$1.SmackProcessLabel, config_parse_exec_smack_process_label, 0, offsetof($1, exec_context)', -`$1.SmackProcessLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')' -)m4_dnl -m4_define(`KILL_CONTEXT_CONFIG_ITEMS', -`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) -$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup) -$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) -$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)' -)m4_dnl -m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', -`$1.Slice, config_parse_unit_slice, 0, 0 -$1.CPUAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.cpu_accounting) -$1.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.cpu_shares) -$1.StartupCPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.startup_cpu_shares) -$1.CPUQuota, config_parse_cpu_quota, 0, offsetof($1, cgroup_context) -$1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting) -$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) -$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) -$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context) -$1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context) -$1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context) -$1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy) -$1.IOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.io_accounting) -$1.IOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.io_weight) -$1.StartupIOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.startup_io_weight) -$1.IODeviceWeight, config_parse_io_device_weight, 0, offsetof($1, cgroup_context) -$1.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) -$1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) -$1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) -$1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) -$1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting) -$1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight) -$1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight) -$1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) -$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.tasks_max) -$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) -$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0' -)m4_dnl -Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) -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.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 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 -Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0 -Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0 -Unit.After, config_parse_unit_deps, UNIT_AFTER, 0 -Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0 -Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 -Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 -Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 -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) -Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) -Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) -Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) -Unit.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_warn_compat, DISABLED_LEGACY, 0 -Unit.JobTimeoutSec, config_parse_sec_fix_0, 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) -Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) -m4_dnl The following is a legacy alias name for compatibility -Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) -Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) -Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) -Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) -Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions) -Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions) -Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions) -Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions) -Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions) -Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions) -Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions) -Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions) -Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions) -Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions) -Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions) -Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions) -Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions) -Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) -Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions) -Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions) -Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions) -Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) -Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions) -Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) -Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) -Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts) -Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts) -Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts) -Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts) -Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts) -Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts) -Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts) -Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts) -Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts) -Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts) -Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts) -Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) -Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts) -Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts) -Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts) -Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) -Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) -m4_dnl -Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) -Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) -Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) -Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) -Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) -Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) -Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) -Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec) -Service.TimeoutSec, config_parse_service_timeout, 0, 0 -Service.TimeoutStartSec, config_parse_service_timeout, 0, 0 -Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 -Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) -Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) -m4_dnl The following three only exist for compatibility, they moved into Unit, see above -Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) -Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) -Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) -Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) -Service.FailureAction, config_parse_failure_action, 0, offsetof(Service, failure_action) -Service.Type, config_parse_service_type, 0, offsetof(Service, type) -Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) -Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) -Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only) -Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit) -Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid) -Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_prevent_status) -Service.RestartForceExitStatus, config_parse_set_status, 0, offsetof(Service, restart_force_status) -Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status) -Service.SysVStartPriority, config_parse_warn_compat, DISABLED_LEGACY, 0 -Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) -Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name) -Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max) -Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) -Service.Sockets, config_parse_service_sockets, 0, 0 -Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0 -Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors) -Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings) -EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl -m4_dnl -Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0 -Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0 -Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0 -Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0 -Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0 -Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 -Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 -Socket.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 -Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command) -Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) -Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) -Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) -Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) -Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user) -Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group) -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) -Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval) -Socket.KeepAliveProbes, config_parse_unsigned, 0, offsetof(Socket, keep_alive_cnt) -Socket.DeferAcceptSec, config_parse_sec, 0, offsetof(Socket, defer_accept) -Socket.NoDelay, config_parse_bool, 0, offsetof(Socket, no_delay) -Socket.Priority, config_parse_int, 0, offsetof(Socket, priority) -Socket.ReceiveBuffer, config_parse_iec_size, 0, offsetof(Socket, receive_buffer) -Socket.SendBuffer, config_parse_iec_size, 0, offsetof(Socket, send_buffer) -Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos) -Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl) -Socket.Mark, config_parse_int, 0, offsetof(Socket, mark) -Socket.PipeSize, config_parse_iec_size, 0, offsetof(Socket, pipe_size) -Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind) -Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent) -Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) -Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) -Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) -Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) -Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) -Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) -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 -Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) -Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) -m4_ifdef(`HAVE_SMACK', -`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) -Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) -Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)', -`Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 -Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -m4_ifdef(`HAVE_SELINUX', -`Socket.SELinuxContextFromNet, config_parse_bool, 0, offsetof(Socket, selinux_context_from_net)', -`Socket.SELinuxContextFromNet, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl -m4_dnl -BusName.Name, config_parse_string, 0, offsetof(BusName, name) -BusName.Activating, config_parse_bool, 0, offsetof(BusName, activating) -BusName.Service, config_parse_busname_service, 0, 0 -BusName.AllowUser, config_parse_bus_policy, 0, 0 -BusName.AllowGroup, config_parse_bus_policy, 0, 0 -BusName.AllowWorld, config_parse_bus_policy_world, 0, offsetof(BusName, policy_world) -BusName.SELinuxContext, config_parse_exec_selinux_context, 0, 0 -BusName.AcceptFileDescriptors, config_parse_bool, 0, offsetof(BusName, accept_fd) -m4_dnl -Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what) -Mount.Where, config_parse_path, 0, offsetof(Mount, where) -Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options) -Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) -Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec) -Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) -Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options) -EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl -m4_dnl -Automount.Where, config_parse_path, 0, offsetof(Automount, where) -Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode) -Automount.TimeoutIdleSec, config_parse_sec, 0, offsetof(Automount, timeout_idle_usec) -m4_dnl -Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) -Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) -Swap.Options, config_parse_string, 0, offsetof(Swap, parameters_fragment.options) -Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec) -EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl -m4_dnl -Timer.OnCalendar, config_parse_timer, 0, 0 -Timer.OnActiveSec, config_parse_timer, 0, 0 -Timer.OnBootSec, config_parse_timer, 0, 0 -Timer.OnStartupSec, config_parse_timer, 0, 0 -Timer.OnUnitActiveSec, config_parse_timer, 0, 0 -Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 -Timer.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 -Path.PathExistsGlob, config_parse_path_spec, 0, 0 -Path.PathChanged, config_parse_path_spec, 0, 0 -Path.PathModified, config_parse_path_spec, 0, 0 -Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 -Path.Unit, config_parse_trigger_unit, 0, 0 -Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) -Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode) -m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Slice)m4_dnl -m4_dnl -CGROUP_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl -KILL_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl -Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec) -m4_dnl The [Install] section is ignored here. -Install.Alias, NULL, 0, 0 -Install.WantedBy, NULL, 0, 0 -Install.RequiredBy, NULL, 0, 0 -Install.Also, NULL, 0, 0 -Install.DefaultInstance, NULL, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c deleted file mode 100644 index a36953f766..0000000000 --- a/src/core/load-fragment.c +++ /dev/null @@ -1,4130 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2012 Holger Hans Peter Freyther - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#ifdef HAVE_SECCOMP -#include -#endif -#include -#include -#include -#include - -#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 "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "rlimit-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, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Disabled reason = ltype; - - switch(reason) { - case DISABLED_CONFIGURATION: - 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, 0, - "Support for option %s= has been removed and it is ignored", lvalue); - break; - case DISABLED_EXPERIMENTAL: - log_syntax(unit, LOG_INFO, filename, line, 0, - "Support for option %s= has not yet been enabled and it is ignored", lvalue); - break; - }; - - 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) { - - UnitDependency d = ltype; - Unit *u = userdata; - const char *p; - - assert(filename); - assert(lvalue); - assert(rvalue); - - p = rvalue; - for (;;) { - _cleanup_free_ char *word = NULL, *k = NULL; - int r; - - 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, word, &k); - if (r < 0) { - 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: %m", k); - } - - 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, - 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; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - 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: %m", rvalue); - return 0; - } - - 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) { - - Unit *u = userdata; - _cleanup_free_ char *k = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - 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: %m", rvalue); - return 0; - } - - 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) { - - _cleanup_free_ char *k = NULL; - Unit *u = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - 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: %m", rvalue); - return 0; - } - - return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, 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) { - - char ***x = data; - const char *word, *state; - Unit *u = userdata; - size_t l; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *k = NULL; - char t[l+1]; - - memcpy(t, word, l); - t[l] = 0; - - 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: %m", t); - return 0; - } - - 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, "Symlink path %s is not absolute, ignoring: %m", k); - return 0; - } - - path_kill_slashes(k); - - r = strv_push(x, k); - if (r < 0) - return log_oom(); - - k = NULL; - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring."); - - return 0; -} - -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) { - - _cleanup_free_ SocketPort *p = NULL; - SocketPort *tail; - Socket *s; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - if (isempty(rvalue)) { - /* An empty assignment removes all ports */ - socket_free_ports(s); - return 0; - } - - p = new0(SocketPort, 1); - if (!p) - return log_oom(); - - if (ltype != SOCKET_SOCKET) { - - p->type = ltype; - r = unit_full_printf(UNIT(s), rvalue, &p->path); - if (r < 0) { - 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); - - } else if (streq(lvalue, "ListenNetlink")) { - _cleanup_free_ char *k = NULL; - - 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: %m", rvalue); - return 0; - } - - 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); - return 0; - } - - } else { - _cleanup_free_ char *k = NULL; - - 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: %m", rvalue); - return 0; - } - - 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); - return 0; - } - - if (streq(lvalue, "ListenStream")) - p->address.type = SOCK_STREAM; - else if (streq(lvalue, "ListenDatagram")) - p->address.type = SOCK_DGRAM; - else { - assert(streq(lvalue, "ListenSequentialPacket")); - p->address.type = SOCK_SEQPACKET; - } - - if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue); - return 0; - } - } - - p->fd = -1; - p->auxiliary_fds = NULL; - p->n_auxiliary_fds = 0; - p->socket = s; - - if (s->ports) { - LIST_FIND_TAIL(port, s->ports, tail); - LIST_INSERT_AFTER(port, s->ports, tail, p); - } else - LIST_PREPEND(port, s->ports, p); - p = NULL; - - 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, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Socket *s; - SocketAddressBindIPv6Only b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = SOCKET(data); - - b = socket_address_bind_ipv6_only_from_string(rvalue); - if (b < 0) { - int r; - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); - return 0; - } - - s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; - } else - s->bind_ipv6_only = b; - - return 0; -} - -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) { - - ExecContext *c = data; - int priority, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &priority); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - return 0; - } - - if (priority < PRIO_MIN || priority >= PRIO_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue); - return 0; - } - - c->nice = priority; - c->nice_set = true; - - return 0; -} - -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) { - - ExecContext *c = data; - int oa, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &oa); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); - return 0; - } - - if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue); - return 0; - } - - c->oom_score_adjust = oa; - c->oom_score_adjust_set = true; - - return 0; -} - -int config_parse_exec( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ExecCommand **e = data; - const char *p; - bool semicolon; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(e); - - e += ltype; - rvalue += strspn(rvalue, WHITESPACE); - - if (isempty(rvalue)) { - /* An empty assignment resets the list */ - *e = exec_command_free_list(*e); - return 0; - } - - p = rvalue; - do { - _cleanup_free_ char *path = NULL, *firstword = NULL; - bool separate_argv0 = false, ignore = false, privileged = false; - _cleanup_free_ ExecCommand *nce = NULL; - _cleanup_strv_free_ char **n = NULL; - size_t nlen = 0, nbufsize = 0; - char *f; - int i; - - semicolon = false; - - r = extract_first_word_and_warn(&p, &firstword, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue); - if (r <= 0) - return 0; - - f = firstword; - for (i = 0; i < 3; i++) { - /* We accept an absolute path as first argument. - * If it's prefixed with - and the path doesn't exist, - * we ignore it instead of erroring out; - * if it's prefixed with @, we allow overriding of argv[0]; - * and if it's prefixed with !, it will be run with full privileges */ - if (*f == '-' && !ignore) - ignore = true; - else if (*f == '@' && !separate_argv0) - separate_argv0 = true; - else if (*f == '+' && !privileged) - privileged = true; - else - break; - f++; - } - - if (isempty(f)) { - /* First word is either "-" or "@" with no command. */ - 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, 0, "Executable path contains special characters, ignoring: %s", rvalue); - return 0; - } - if (!path_is_absolute(f)) { - 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, 0, "Executable path specifies a directory, ignoring: %s", rvalue); - return 0; - } - - if (f == firstword) { - path = firstword; - firstword = NULL; - } else { - path = strdup(f); - if (!path) - return log_oom(); - } - - if (!separate_argv0) { - if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) - return log_oom(); - f = strdup(path); - if (!f) - return log_oom(); - n[nlen++] = f; - n[nlen] = NULL; - } - - path_kill_slashes(path); - - while (!isempty(p)) { - _cleanup_free_ char *word = NULL; - - /* Check explicitly for an unquoted semicolon as - * command separator token. */ - if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) { - p++; - p += strspn(p, WHITESPACE); - semicolon = true; - break; - } - - /* Check for \; explicitly, to not confuse it with \\; - * or "\;" or "\\;" etc. extract_first_word would - * return the same for all of those. */ - if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) { - p += 2; - p += strspn(p, WHITESPACE); - if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) - return log_oom(); - f = strdup(";"); - if (!f) - return log_oom(); - n[nlen++] = f; - n[nlen] = NULL; - continue; - } - - r = extract_first_word_and_warn(&p, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue); - if (r == 0) - break; - else if (r < 0) - return 0; - - if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) - return log_oom(); - n[nlen++] = word; - n[nlen] = NULL; - word = NULL; - } - - if (!n || !n[0]) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); - return 0; - } - - nce = new0(ExecCommand, 1); - if (!nce) - return log_oom(); - - nce->argv = n; - nce->path = path; - nce->ignore = ignore; - nce->privileged = privileged; - - exec_command_append_list(e, nce); - - /* Do not _cleanup_free_ these. */ - n = NULL; - path = NULL; - nce = NULL; - - rvalue = p; - } while (semicolon); - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); - -int config_parse_socket_bindtodevice( - const char* 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 = data; - char *n; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (rvalue[0] && !streq(rvalue, "*")) { - if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue); - return 0; - } - - n = strdup(rvalue); - if (!n) - return log_oom(); - } else - n = NULL; - - free(s->bind_to_device); - s->bind_to_device = n; - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); - -int config_parse_exec_io_class(const char *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; - int x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = ioprio_class_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue); - return 0; - } - - c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio)); - c->ioprio_set = true; - - return 0; -} - -int config_parse_exec_io_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) { - - ExecContext *c = data; - int i, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &i); - if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue); - return 0; - } - - c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i); - c->ioprio_set = true; - - return 0; -} - -int config_parse_exec_cpu_sched_policy(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - - ExecContext *c = data; - int x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = sched_policy_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); - return 0; - } - - c->cpu_sched_policy = x; - /* Moving to or from real-time policy? We need to adjust the priority */ - c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x)); - c->cpu_sched_set = true; - - return 0; -} - -int config_parse_exec_cpu_sched_prio(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; - int i, min, max, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &i); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); - return 0; - } - - /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */ - min = sched_get_priority_min(c->cpu_sched_policy); - max = sched_get_priority_max(c->cpu_sched_policy); - - if (i < min || i > max) { - log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue); - return 0; - } - - c->cpu_sched_priority = i; - c->cpu_sched_set = true; - - return 0; -} - -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) { - - ExecContext *c = data; - _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; - int ncpus; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); - if (ncpus < 0) - return ncpus; - - if (c->cpuset) - CPU_FREE(c->cpuset); - - if (ncpus == 0) - /* An empty assignment resets the CPU list */ - c->cpuset = NULL; - else { - c->cpuset = cpuset; - cpuset = NULL; - } - c->cpuset_ncpus = ncpus; - - return 0; -} - -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) { - - ExecContext *c = data; - size_t l; - const char *word, *state; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* An empty assignment resets the field */ - c->secure_bits = 0; - return 0; - } - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - if (first_word(word, "keep-caps")) - c->secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1< replace */ - *capability_set = sum; - else - /* 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) { - - struct rlimit **rl = data, d = {}; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = rlimit_parse(ltype, rvalue, &d); - if (r == -EILSEQ) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); - return 0; - } - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } - - if (rl[ltype]) - *rl[ltype] = d; - else { - rl[ltype] = newdup(struct rlimit, &d, 1); - if (!rl[ltype]) - return log_oom(); - } - - return 0; -} - -#ifdef HAVE_SYSV_COMPAT -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 *priority = data; - int i, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atoi(rvalue, &i); - if (r < 0 || i < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue); - return 0; - } - - *priority = (int) i; - return 0; -} -#endif - -DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); - -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) { - - - unsigned long flags = 0; - ExecContext *c = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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; - } - - c->mount_flags = flags; - - return 0; -} - -int config_parse_exec_selinux_context( - 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 ignore; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - c->selinux_context = mfree(c->selinux_context); - c->selinux_context_ignore = false; - return 0; - } - - if (rvalue[0] == '-') { - ignore = true; - rvalue++; - } else - ignore = false; - - r = unit_name_printf(u, rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; - } - - free(c->selinux_context); - c->selinux_context = k; - c->selinux_context_ignore = ignore; - - return 0; -} - -int config_parse_exec_apparmor_profile( - 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 ignore; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - c->apparmor_profile = mfree(c->apparmor_profile); - c->apparmor_profile_ignore = false; - return 0; - } - - if (rvalue[0] == '-') { - ignore = true; - rvalue++; - } else - ignore = false; - - r = unit_name_printf(u, rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; - } - - free(c->apparmor_profile); - c->apparmor_profile = k; - c->apparmor_profile_ignore = ignore; - - return 0; -} - -int config_parse_exec_smack_process_label( - 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 ignore; - char *k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - c->smack_process_label = mfree(c->smack_process_label); - c->smack_process_label_ignore = false; - return 0; - } - - if (rvalue[0] == '-') { - ignore = true; - rvalue++; - } else - ignore = false; - - r = unit_name_printf(u, rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; - } - - free(c->smack_process_label); - c->smack_process_label = k; - c->smack_process_label_ignore = ignore; - - return 0; -} - -int config_parse_timer(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) { - - Timer *t = data; - usec_t u = 0; - TimerValue *v; - TimerBase b; - CalendarSpec *c = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets list */ - timer_free_values(t); - return 0; - } - - b = timer_base_from_string(lvalue); - if (b < 0) { - log_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, 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, 0, "Failed to parse timer value, ignoring: %s", rvalue); - return 0; - } - } - - v = new0(TimerValue, 1); - if (!v) { - calendar_spec_free(c); - return log_oom(); - } - - v->base = b; - v->value = u; - v->calendar_spec = c; - - LIST_PREPEND(value, t->values, v); - - return 0; -} - -int config_parse_trigger_unit( - 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; - Unit *u = data; - UnitType type; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) { - 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: %m"); - return 0; - } - - type = unit_name_to_type(p); - if (type < 0) { - 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, 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, NULL, true); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p); - return 0; - } - - return 0; -} - -int config_parse_path_spec(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) { - - Path *p = data; - PathSpec *s; - PathType b; - _cleanup_free_ char *k = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment clears list */ - path_free_specs(p); - return 0; - } - - b = path_type_from_string(lvalue); - if (b < 0) { - log_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) { - 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, 0, "Path is not absolute, ignoring: %s", k); - return 0; - } - - s = new0(PathSpec, 1); - if (!s) - return log_oom(); - - s->unit = UNIT(p); - s->path = path_kill_slashes(k); - k = NULL; - s->type = b; - s->inotify_fd = -1; - - LIST_PREPEND(spec, p->specs, s); - - return 0; -} - -int config_parse_socket_service( - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *p = NULL; - Socket *s = data; - Unit *x; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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 (!endswith(p, ".service")) { - 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)); - return 0; - } - - unit_ref_set(&s->service, x); - - 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, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Service *s = data; - const char *p; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - p = rvalue; - for (;;) { - _cleanup_free_ char *word = NULL, *k = NULL; - - 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), 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, 0, "Unit must be of type socket, ignoring: %s", k); - continue; - } - - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); - - r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); - } - - return 0; -} - -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) { - - _cleanup_free_ char *k = NULL; - Unit *u = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - 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: %m", rvalue); - return 0; - } - - if (!service_name_is_valid(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k); - return 0; - } - - return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); -} - -int config_parse_service_timeout( - 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) { - - Service *s = userdata; - usec_t usec; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(s); - - /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */ - - r = parse_sec(rvalue, &usec); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); - return 0; - } - - /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens - * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle - * all other timeouts. */ - if (usec <= 0) - usec = USEC_INFINITY; - - if (!streq(lvalue, "TimeoutStopSec")) { - s->start_timeout_defined = true; - s->timeout_start_usec = usec; - } - - if (!streq(lvalue, "TimeoutStartSec")) - s->timeout_stop_usec = usec; - - return 0; -} - -int config_parse_sec_fix_0( - 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) { - - usec_t *usec = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(usec); - - /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for - * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a - * timeout. */ - - r = parse_sec(rvalue, usec); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); - return 0; - } - - if (*usec <= 0) - *usec = USEC_INFINITY; - - return 0; -} - -int config_parse_busname_service( - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - BusName *n = data; - int r; - Unit *x; - _cleanup_free_ char *p = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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); - return 0; - } - - if (!endswith(p, ".service")) { - 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)); - return 0; - } - - unit_ref_set(&n->service, x); - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access"); - -int config_parse_bus_policy( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_free_ BusNamePolicy *p = NULL; - _cleanup_free_ char *id_str = NULL; - BusName *busname = data; - char *access_str; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - p = new0(BusNamePolicy, 1); - if (!p) - return log_oom(); - - if (streq(lvalue, "AllowUser")) - p->type = BUSNAME_POLICY_TYPE_USER; - else if (streq(lvalue, "AllowGroup")) - p->type = BUSNAME_POLICY_TYPE_GROUP; - else - assert_not_reached("Unknown lvalue"); - - id_str = strdup(rvalue); - if (!id_str) - return log_oom(); - - access_str = strpbrk(id_str, WHITESPACE); - if (!access_str) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue); - return 0; - } - - *access_str = '\0'; - access_str++; - access_str += strspn(access_str, WHITESPACE); - - p->access = bus_policy_access_from_string(access_str); - if (p->access < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str); - return 0; - } - - p->name = id_str; - id_str = NULL; - - LIST_PREPEND(policy, busname->policy, p); - p = NULL; - - return 0; -} - -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, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char ***env = data; - Unit *u = userdata; - _cleanup_free_ char *n = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment frees the list */ - *env = strv_free(*env); - return 0; - } - - 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); - return 0; - } - - 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, n); - if (r < 0) - return log_oom(); - - return 0; -} - -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) { - - Unit *u = userdata; - char*** env = data; - const char *word, *state; - size_t l; - _cleanup_free_ char *k = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *env = strv_free(*env); - return 0; - } - - 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); - return 0; - } - } - - if (!k) { - k = strdup(rvalue); - if (!k) - return log_oom(); - } - - FOREACH_WORD_QUOTED(word, l, k, state) { - _cleanup_free_ char *n = NULL; - char **x; - - r = cunescape_length(word, l, 0, &n); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue); - continue; - } - - if (!env_assignment_is_valid(n)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue); - continue; - } - - x = strv_env_set(*env, n); - if (!x) - return log_oom(); - - strv_free(*env); - *env = x; - } - if (!isempty(state)) - 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; -} - -int config_parse_ip_tos(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 *ip_tos = data, x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = ip_tos_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue); - return 0; - } - - *ip_tos = x; - return 0; -} - -int config_parse_unit_condition_path( - 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; - Condition **list = data, *c; - ConditionType t = ltype; - bool trigger, negate; - Unit *u = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *list = condition_free_list(*list); - return 0; - } - - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; - - negate = rvalue[0] == '!'; - if (negate) - rvalue++; - - 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); - return 0; - } - - if (!path_is_absolute(p)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p); - return 0; - } - - c = condition_new(t, p, trigger, negate); - if (!c) - return log_oom(); - - LIST_PREPEND(conditions, *list, c); - return 0; -} - -int config_parse_unit_condition_string( - 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 *s = NULL; - Condition **list = data, *c; - ConditionType t = ltype; - bool trigger, negate; - Unit *u = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *list = condition_free_list(*list); - return 0; - } - - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; - - negate = rvalue[0] == '!'; - if (negate) - rvalue++; - - 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); - return 0; - } - - c = condition_new(t, s, trigger, negate); - if (!c) - return log_oom(); - - LIST_PREPEND(conditions, *list, c); - return 0; -} - -int config_parse_unit_condition_null( - 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) { - - Condition **list = data, *c; - bool trigger, negate; - int b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *list = condition_free_list(*list); - return 0; - } - - trigger = rvalue[0] == '|'; - if (trigger) - rvalue++; - - negate = rvalue[0] == '!'; - if (negate) - rvalue++; - - b = parse_boolean(rvalue); - if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); - return 0; - } - - if (!b) - negate = !negate; - - c = condition_new(CONDITION_NULL, NULL, trigger, negate); - if (!c) - return log_oom(); - - LIST_PREPEND(conditions, *list, c); - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier"); - -int config_parse_unit_requires_mounts_for( - 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; - const char *word, *state; - size_t l; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - int r; - _cleanup_free_ char *n; - - n = strndup(word, l); - if (!n) - return log_oom(); - - if (!utf8_is_valid(n)) { - 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); - continue; - } - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; -} - -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) { - - Unit *u = userdata; - int r; - char **a, **b; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - u->documentation = strv_free(u->documentation); - return 0; - } - - r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype, - rvalue, data, userdata); - if (r < 0) - return r; - - for (a = b = u->documentation; a && *a; a++) { - - if (documentation_url_is_valid(*a)) - *(b++) = *a; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a); - free(*a); - } - } - if (b) - *b = NULL; - - return r; -} - -#ifdef HAVE_SECCOMP -static int syscall_filter_parse_one( - const char *unit, - const char *filename, - unsigned line, - ExecContext *c, - bool invert, - const char *t, - bool warn) { - int r; - - if (*t == '@') { - const SystemCallFilterSet *set; - - for (set = syscall_filter_sets; set->set_name; set++) - if (streq(set->set_name, t)) { - const char *sys; - - NULSTR_FOREACH(sys, set->value) { - r = syscall_filter_parse_one(unit, filename, line, c, invert, sys, false); - if (r < 0) - return r; - } - break; - } - } else { - int id; - - id = seccomp_syscall_resolve_name(t); - if (id == __NR_SCMP_ERROR) { - if (warn) - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t); - return 0; - } - - /* If we previously wanted to forbid a syscall and now - * we want to allow it, then remove it from the list - */ - if (!invert == c->syscall_whitelist) { - r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); - if (r == 0) - return 0; - if (r < 0) - return log_oom(); - } else - set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); - } - return 0; -} - -int config_parse_syscall_filter( - 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 invert = false; - const char *word, *state; - size_t l; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - c->syscall_filter = set_free(c->syscall_filter); - c->syscall_whitelist = false; - return 0; - } - - if (rvalue[0] == '~') { - invert = true; - rvalue++; - } - - if (!c->syscall_filter) { - c->syscall_filter = set_new(NULL); - if (!c->syscall_filter) - return log_oom(); - - if (invert) - /* Allow everything but the ones listed */ - c->syscall_whitelist = false; - else { - /* Allow nothing but the ones listed */ - c->syscall_whitelist = true; - - /* Accept default syscalls if we are on a whitelist */ - r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false); - if (r < 0) - return r; - } - } - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; - - t = strndup(word, l); - if (!t) - return log_oom(); - - r = syscall_filter_parse_one(unit, filename, line, c, invert, t, true); - if (r < 0) - return r; - } - if (!isempty(state)) - 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. */ - if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager)) - c->no_new_privileges = true; - - return 0; -} - -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) { - - Set **archs = data; - const char *word, *state; - size_t l; - int r; - - if (isempty(rvalue)) { - *archs = set_free(*archs); - return 0; - } - - r = set_ensure_allocated(archs, NULL); - if (r < 0) - return log_oom(); - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; - uint32_t a; - - t = strndup(word, l); - if (!t) - return log_oom(); - - r = seccomp_arch_from_string(t, &a); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t); - continue; - } - - r = set_put(*archs, UINT32_TO_PTR(a + 1)); - if (r == 0) - continue; - if (r < 0) - return log_oom(); - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; -} - -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) { - - ExecContext *c = data; - int e; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - /* Empty assignment resets to KILL */ - c->syscall_errno = 0; - return 0; - } - - e = errno_from_name(rvalue); - if (e < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue); - return 0; - } - - c->syscall_errno = e; - return 0; -} - -int config_parse_address_families( - 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; - bool invert = false; - const char *word, *state; - size_t l; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - c->address_families = set_free(c->address_families); - c->address_families_whitelist = false; - return 0; - } - - if (rvalue[0] == '~') { - invert = true; - rvalue++; - } - - if (!c->address_families) { - c->address_families = set_new(NULL); - if (!c->address_families) - return log_oom(); - - c->address_families_whitelist = !invert; - } - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; - int af; - - t = strndup(word, l); - if (!t) - return log_oom(); - - af = af_from_name(t); - if (af <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t); - continue; - } - - /* If we previously wanted to forbid an address family and now - * we want to allow it, then remove it from the list - */ - if (!invert == c->address_families_whitelist) { - r = set_put(c->address_families, INT_TO_PTR(af)); - if (r == 0) - continue; - if (r < 0) - return log_oom(); - } else - set_remove(c->address_families, INT_TO_PTR(af)); - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; -} -#endif - -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) { - - _cleanup_free_ char *k = NULL; - Unit *u = userdata, *slice = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(u); - - r = unit_name_printf(u, rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - return 0; - } - - r = manager_load_unit(u->manager, k, NULL, NULL, &slice); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k); - return 0; - } - - r = unit_set_slice(u, slice); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id); - return 0; - } - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); - -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) { - - uint64_t *shares = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_cpu_shares_parse(rvalue, shares); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - -int config_parse_cpu_quota( - 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) { - - CGroupContext *c = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - c->cpu_quota_per_sec_usec = USEC_INFINITY; - return 0; - } - - r = parse_percent(rvalue); - if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue); - return 0; - } - - c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U; - return 0; -} - -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) { - - CGroupContext *c = data; - uint64_t bytes = CGROUP_LIMIT_MAX; - int r; - - if (!isempty(rvalue) && !streq(rvalue, "infinity")) { - - r = parse_percent(rvalue); - if (r < 0) { - r = parse_size(rvalue, 1024, &bytes); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue); - return 0; - } - } else - bytes = physical_memory_scale(r, 100U); - - if (bytes <= 0 || bytes >= UINT64_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue); - return 0; - } - } - - if (streq(lvalue, "MemoryLow")) - c->memory_low = bytes; - else if (streq(lvalue, "MemoryHigh")) - c->memory_high = bytes; - else if (streq(lvalue, "MemoryMax")) - c->memory_max = bytes; - else - c->memory_limit = bytes; - - return 0; -} - -int config_parse_tasks_max( - 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 *tasks_max = data, u; - int r; - - if (isempty(rvalue) || streq(rvalue, "infinity")) { - *tasks_max = (uint64_t) -1; - return 0; - } - - r = parse_percent(rvalue); - if (r < 0) { - r = safe_atou64(rvalue, &u); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); - return 0; - } - } else - u = system_tasks_max_scale(r, 100U); - - if (u <= 0 || u >= UINT64_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue); - return 0; - } - - *tasks_max = u; - return 0; -} - -int config_parse_device_allow( - 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 *path = NULL, *t = NULL; - CGroupContext *c = data; - CGroupDeviceAllow *a; - const char *m = NULL; - size_t n; - int r; - - if (isempty(rvalue)) { - while (c->device_allow) - cgroup_context_free_device_allow(c, c->device_allow); - - return 0; - } - - r = unit_full_printf(userdata, rvalue, &t); - if(r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to resolve specifiers in %s, ignoring: %m", - rvalue); - } - - n = strcspn(t, WHITESPACE); - - path = strndup(t, n); - if (!path) - return log_oom(); - - if (!startswith(path, "/dev/") && - !startswith(path, "block-") && - !startswith(path, "char-")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); - return 0; - } - - m = t + n + strspn(t + n, WHITESPACE); - if (isempty(m)) - m = "rwm"; - - if (!in_charset(m, "rwm")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m); - return 0; - } - - a = new0(CGroupDeviceAllow, 1); - if (!a) - return log_oom(); - - a->path = path; - path = NULL; - a->r = !!strchr(m, 'r'); - a->w = !!strchr(m, 'w'); - a->m = !!strchr(m, 'm'); - - LIST_PREPEND(device_allow, c->device_allow, a); - return 0; -} - -int config_parse_io_weight( - 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 *weight = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_weight_parse(rvalue, weight); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - -int config_parse_io_device_weight( - 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 *path = NULL; - CGroupIODeviceWeight *w; - CGroupContext *c = data; - const char *weight; - uint64_t u; - size_t n; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - while (c->io_device_weights) - cgroup_context_free_io_device_weight(c, c->io_device_weights); - - return 0; - } - - n = strcspn(rvalue, WHITESPACE); - weight = rvalue + n; - weight += strspn(weight, WHITESPACE); - - if (isempty(weight)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); - return 0; - } - - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); - return 0; - } - - r = cg_weight_parse(weight, &u); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight); - return 0; - } - - assert(u != CGROUP_WEIGHT_INVALID); - - w = new0(CGroupIODeviceWeight, 1); - if (!w) - return log_oom(); - - w->path = path; - path = NULL; - - w->weight = u; - - LIST_PREPEND(device_weights, c->io_device_weights, w); - return 0; -} - -int config_parse_io_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) { - - _cleanup_free_ char *path = NULL; - CGroupIODeviceLimit *l = NULL, *t; - CGroupContext *c = data; - CGroupIOLimitType type; - const char *limit; - uint64_t num; - size_t n; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - type = cgroup_io_limit_type_from_string(lvalue); - assert(type >= 0); - - if (isempty(rvalue)) { - LIST_FOREACH(device_limits, l, c->io_device_limits) - l->limits[type] = cgroup_io_limit_defaults[type]; - return 0; - } - - n = strcspn(rvalue, WHITESPACE); - limit = rvalue + n; - limit += strspn(limit, WHITESPACE); - - if (!*limit) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); - return 0; - } - - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); - return 0; - } - - if (streq("infinity", limit)) { - num = CGROUP_LIMIT_MAX; - } else { - r = parse_size(limit, 1000, &num); - if (r < 0 || num <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue); - return 0; - } - } - - LIST_FOREACH(device_limits, t, c->io_device_limits) { - if (path_equal(path, t->path)) { - l = t; - break; - } - } - - if (!l) { - CGroupIOLimitType ttype; - - l = new0(CGroupIODeviceLimit, 1); - if (!l) - return log_oom(); - - l->path = path; - path = NULL; - for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++) - l->limits[ttype] = cgroup_io_limit_defaults[ttype]; - - LIST_PREPEND(device_limits, c->io_device_limits, l); - } - - l->limits[type] = num; - - return 0; -} - -int config_parse_blockio_weight( - 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 *weight = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = cg_blkio_weight_parse(rvalue, weight); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue); - return 0; - } - - return 0; -} - -int config_parse_blockio_device_weight( - 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 *path = NULL; - CGroupBlockIODeviceWeight *w; - CGroupContext *c = data; - const char *weight; - uint64_t u; - size_t n; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - while (c->blockio_device_weights) - cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); - - return 0; - } - - n = strcspn(rvalue, WHITESPACE); - weight = rvalue + n; - weight += strspn(weight, WHITESPACE); - - if (isempty(weight)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); - return 0; - } - - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); - return 0; - } - - r = cg_blkio_weight_parse(weight, &u); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight); - return 0; - } - - assert(u != CGROUP_BLKIO_WEIGHT_INVALID); - - w = new0(CGroupBlockIODeviceWeight, 1); - if (!w) - return log_oom(); - - w->path = path; - path = NULL; - - w->weight = u; - - LIST_PREPEND(device_weights, c->blockio_device_weights, w); - return 0; -} - -int config_parse_blockio_bandwidth( - 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 *path = NULL; - CGroupBlockIODeviceBandwidth *b = NULL, *t; - CGroupContext *c = data; - const char *bandwidth; - uint64_t bytes; - bool read; - size_t n; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - read = streq("BlockIOReadBandwidth", lvalue); - - if (isempty(rvalue)) { - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - b->rbps = CGROUP_LIMIT_MAX; - b->wbps = CGROUP_LIMIT_MAX; - } - return 0; - } - - n = strcspn(rvalue, WHITESPACE); - bandwidth = rvalue + n; - bandwidth += strspn(bandwidth, WHITESPACE); - - if (!*bandwidth) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); - return 0; - } - - path = strndup(rvalue, n); - if (!path) - return log_oom(); - - if (!path_startswith(path, "/dev")) { - 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, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue); - return 0; - } - - LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { - if (path_equal(path, t->path)) { - b = t; - break; - } - } - - if (!t) { - b = new0(CGroupBlockIODeviceBandwidth, 1); - if (!b) - return log_oom(); - - b->path = path; - path = NULL; - b->rbps = CGROUP_LIMIT_MAX; - b->wbps = CGROUP_LIMIT_MAX; - - LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); - } - - if (read) - b->rbps = bytes; - else - b->wbps = bytes; - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); - -int config_parse_job_mode_isolate( - 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) { - - JobMode *m = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue); - return 0; - } - - *m = r ? JOB_ISOLATE : JOB_REPLACE; - return 0; -} - -int config_parse_runtime_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) { - - char***rt = data; - Unit *u = userdata; - const char *word, *state; - size_t l; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *rt = strv_free(*rt); - return 0; - } - - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL, *n = NULL; - - t = strndup(word, l); - if (!t) - return log_oom(); - - r = unit_name_printf(u, t, &n); - if (r < 0) { - 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, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue); - continue; - } - - r = strv_push(rt, n); - if (r < 0) - return log_oom(); - - n = NULL; - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; -} - -int config_parse_set_status( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t l; - const char *word, *state; - int r; - ExitStatusSet *status_set = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* Empty assignment resets the list */ - if (isempty(rvalue)) { - exit_status_set_free(status_set); - return 0; - } - - FOREACH_WORD(word, l, rvalue, state) { - _cleanup_free_ char *temp; - int val; - Set **set; - - temp = strndup(word, l); - if (!temp) - return log_oom(); - - r = safe_atoi(temp, &val); - if (r < 0) { - val = signal_from_string_try_harder(temp); - - if (val <= 0) { - 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, 0, "Value %d is outside range 0-255, ignoring", val); - continue; - } - set = &status_set->status; - } - - r = set_ensure_allocated(set, NULL); - if (r < 0) - return log_oom(); - - r = set_put(*set, INT_TO_PTR(val)); - if (r < 0) { - 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, 0, "Trailing garbage, ignoring."); - - return 0; -} - -int config_parse_namespace_path_strv( - 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) { - - char*** sv = data; - const char *prev; - const char *cur; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - /* Empty assignment resets the list */ - *sv = strv_free(*sv); - return 0; - } - - prev = cur = rvalue; - for (;;) { - _cleanup_free_ char *word = NULL; - int offset; - - 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(word)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word); - prev = cur; - continue; - } - - 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(word + offset); - - r = strv_push(sv, word); - if (r < 0) - return log_oom(); - - prev = cur; - word = NULL; - } - - return 0; -} - -int config_parse_no_new_privileges( - 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; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); - return 0; - } - - c->no_new_privileges = !!k; - c->no_new_privileges_set = true; - - return 0; -} - -int config_parse_protect_home( - 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; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* 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) - c->protect_home = PROTECT_HOME_YES; - else if (k == 0) - c->protect_home = PROTECT_HOME_NO; - else { - ProtectHome h; - - h = protect_home_from_string(rvalue); - if (h < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); - return 0; - } - - c->protect_home = h; - } - - return 0; -} - -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) { - - ExecContext *c = data; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* 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) - c->protect_system = PROTECT_SYSTEM_YES; - else if (k == 0) - c->protect_system = PROTECT_SYSTEM_NO; - else { - ProtectSystem s; - - s = protect_system_from_string(rvalue); - if (s < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); - return 0; - } - - c->protect_system = s; - } - - return 0; -} - -#define FOLLOW_MAX 8 - -static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { - char *id = NULL; - unsigned c = 0; - int fd, r; - FILE *f; - - assert(filename); - assert(*filename); - assert(_f); - assert(names); - - /* This will update the filename pointer if the loaded file is - * reached by a symlink. The old string will be freed. */ - - for (;;) { - char *target, *name; - - if (c++ >= FOLLOW_MAX) - return -ELOOP; - - path_kill_slashes(*filename); - - /* Add the file name we are currently looking at to - * the names of this unit, but only if it is a valid - * unit name. */ - name = basename(*filename); - if (unit_name_is_valid(name, UNIT_NAME_ANY)) { - - id = set_get(names, name); - if (!id) { - id = strdup(name); - if (!id) - return -ENOMEM; - - r = set_consume(names, id); - if (r < 0) - return r; - } - } - - /* Try to open the file name, but don't if its a symlink */ - fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) - break; - - if (errno != ELOOP) - return -errno; - - /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ - r = readlink_and_make_absolute(*filename, &target); - if (r < 0) - return r; - - free(*filename); - *filename = target; - } - - f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -errno; - } - - *_f = f; - *_final = id; - - return 0; -} - -static int merge_by_names(Unit **u, Set *names, const char *id) { - char *k; - int r; - - assert(u); - assert(*u); - assert(names); - - /* Let's try to add in all symlink names we found */ - while ((k = set_steal_first(names))) { - - /* First try to merge in the other name into our - * unit */ - r = unit_merge_by_name(*u, k); - if (r < 0) { - Unit *other; - - /* Hmm, we couldn't merge the other unit into - * ours? Then let's try it the other way - * round */ - - /* If the symlink name we are looking at is unit template, then - we must search for instance of this template */ - if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) { - _cleanup_free_ char *instance = NULL; - - r = unit_name_replace_instance(k, (*u)->instance, &instance); - if (r < 0) - return r; - - other = manager_get_unit((*u)->manager, instance); - } else - other = manager_get_unit((*u)->manager, k); - - free(k); - - if (other) { - r = unit_merge(other, *u); - if (r >= 0) { - *u = other; - return merge_by_names(u, names, NULL); - } - } - - return r; - } - - if (id == k) - unit_choose_id(*u, id); - - free(k); - } - - return 0; -} - -static int load_from_path(Unit *u, const char *path) { - _cleanup_set_free_free_ Set *symlink_names = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *filename = NULL; - char *id = NULL; - Unit *merged; - struct stat st; - int r; - - assert(u); - assert(path); - - symlink_names = set_new(&string_hash_ops); - if (!symlink_names) - return -ENOMEM; - - if (path_is_absolute(path)) { - - filename = strdup(path); - if (!filename) - return -ENOMEM; - - r = open_follow(&filename, &f, symlink_names, &id); - if (r < 0) { - filename = mfree(filename); - if (r != -ENOENT) - return r; - } - - } else { - char **p; - - STRV_FOREACH(p, u->manager->lookup_paths.search_path) { - - /* Instead of opening the path right away, we manually - * follow all symlinks and add their name to our unit - * name set while doing so */ - filename = path_make_absolute(path, *p); - if (!filename) - return -ENOMEM; - - if (u->manager->unit_path_cache && - !set_get(u->manager->unit_path_cache, filename)) - r = -ENOENT; - else - r = open_follow(&filename, &f, symlink_names, &id); - if (r >= 0) - break; - filename = mfree(filename); - - /* ENOENT means that the file is missing or is a dangling symlink. - * ENOTDIR means that one of paths we expect to be is a directory - * is not a directory, we should just ignore that. - * EACCES means that the directory or file permissions are wrong. - */ - if (r == -EACCES) - log_debug_errno(r, "Cannot access \"%s\": %m", filename); - else if (!IN_SET(r, -ENOENT, -ENOTDIR)) - return r; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - } - } - - if (!filename) - /* Hmm, no suitable file found? */ - return 0; - - if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { - log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); - return -ELOOP; - } - - merged = u; - r = merge_by_names(&merged, symlink_names, id); - if (r < 0) - return r; - - if (merged != u) { - u->load_state = UNIT_MERGED; - return 0; - } - - if (fstat(fileno(f), &st) < 0) - return -errno; - - if (null_or_empty(&st)) { - u->load_state = UNIT_MASKED; - u->fragment_mtime = 0; - } else { - u->load_state = UNIT_LOADED; - u->fragment_mtime = timespec_load(&st.st_mtim); - - /* Now, parse the file contents */ - r = config_parse(u->id, filename, f, - UNIT_VTABLE(u)->sections, - config_item_perf_lookup, load_fragment_gperf_lookup, - false, true, false, u); - if (r < 0) - return r; - } - - free(u->fragment_path); - u->fragment_path = filename; - filename = NULL; - - if (u->source_path) { - if (stat(u->source_path, &st) >= 0) - u->source_mtime = timespec_load(&st.st_mtim); - else - u->source_mtime = 0; - } - - return 0; -} - -int unit_load_fragment(Unit *u) { - int r; - Iterator i; - const char *t; - - assert(u); - assert(u->load_state == UNIT_STUB); - assert(u->id); - - if (u->transient) { - u->load_state = UNIT_LOADED; - return 0; - } - - /* First, try to find the unit under its id. We always look - * for unit files in the default directories, to make it easy - * to override things by placing things in /etc/systemd/system */ - r = load_from_path(u, u->id); - if (r < 0) - return r; - - /* Try to find an alias we can load this with */ - if (u->load_state == UNIT_STUB) { - SET_FOREACH(t, u->names, i) { - - if (t == u->id) - continue; - - r = load_from_path(u, t); - if (r < 0) - return r; - - if (u->load_state != UNIT_STUB) - break; - } - } - - /* And now, try looking for it under the suggested (originally linked) path */ - if (u->load_state == UNIT_STUB && u->fragment_path) { - - r = load_from_path(u, u->fragment_path); - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) - /* Hmm, this didn't work? Then let's get rid - * of the fragment path stored for us, so that - * we don't point to an invalid location. */ - u->fragment_path = mfree(u->fragment_path); - } - - /* Look for a template */ - if (u->load_state == UNIT_STUB && u->instance) { - _cleanup_free_ char *k = NULL; - - r = unit_name_template(u->id, &k); - if (r < 0) - return r; - - r = load_from_path(u, k); - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) { - SET_FOREACH(t, u->names, i) { - _cleanup_free_ char *z = NULL; - - if (t == u->id) - continue; - - r = unit_name_template(t, &z); - if (r < 0) - return r; - - r = load_from_path(u, z); - if (r < 0) - return r; - - if (u->load_state != UNIT_STUB) - break; - } - } - } - - return 0; -} - -void unit_dump_config_items(FILE *f) { - static const struct { - const ConfigParserCallback callback; - const char *rvalue; - } table[] = { -#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR) - { config_parse_warn_compat, "NOTSUPPORTED" }, -#endif - { config_parse_int, "INTEGER" }, - { config_parse_unsigned, "UNSIGNED" }, - { config_parse_iec_size, "SIZE" }, - { config_parse_iec_uint64, "SIZE" }, - { config_parse_si_size, "SIZE" }, - { config_parse_bool, "BOOLEAN" }, - { config_parse_string, "STRING" }, - { config_parse_path, "PATH" }, - { config_parse_unit_path_printf, "PATH" }, - { config_parse_strv, "STRING [...]" }, - { config_parse_exec_nice, "NICE" }, - { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" }, - { config_parse_exec_io_class, "IOCLASS" }, - { config_parse_exec_io_priority, "IOPRIORITY" }, - { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" }, - { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" }, - { config_parse_exec_cpu_affinity, "CPUAFFINITY" }, - { config_parse_mode, "MODE" }, - { config_parse_unit_env_file, "FILE" }, - { config_parse_output, "OUTPUT" }, - { config_parse_input, "INPUT" }, - { config_parse_log_facility, "FACILITY" }, - { config_parse_log_level, "LEVEL" }, - { config_parse_exec_secure_bits, "SECUREBITS" }, - { config_parse_capability_set, "BOUNDINGSET" }, - { config_parse_limit, "LIMIT" }, - { config_parse_unit_deps, "UNIT [...]" }, - { config_parse_exec, "PATH [ARGUMENT [...]]" }, - { config_parse_service_type, "SERVICETYPE" }, - { config_parse_service_restart, "SERVICERESTART" }, -#ifdef HAVE_SYSV_COMPAT - { config_parse_sysv_priority, "SYSVPRIORITY" }, -#endif - { config_parse_kill_mode, "KILLMODE" }, - { config_parse_signal, "SIGNAL" }, - { config_parse_socket_listen, "SOCKET [...]" }, - { config_parse_socket_bind, "SOCKETBIND" }, - { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, - { config_parse_sec, "SECONDS" }, - { config_parse_nsec, "NANOSECONDS" }, - { config_parse_namespace_path_strv, "PATH [...]" }, - { config_parse_unit_requires_mounts_for, "PATH [...]" }, - { config_parse_exec_mount_flags, "MOUNTFLAG [...]" }, - { config_parse_unit_string_printf, "STRING" }, - { config_parse_trigger_unit, "UNIT" }, - { config_parse_timer, "TIMER" }, - { config_parse_path_spec, "PATH" }, - { config_parse_notify_access, "ACCESS" }, - { config_parse_ip_tos, "TOS" }, - { config_parse_unit_condition_path, "CONDITION" }, - { config_parse_unit_condition_string, "CONDITION" }, - { config_parse_unit_condition_null, "CONDITION" }, - { config_parse_unit_slice, "SLICE" }, - { config_parse_documentation, "URL" }, - { config_parse_service_timeout, "SECONDS" }, - { config_parse_failure_action, "ACTION" }, - { config_parse_set_status, "STATUS" }, - { config_parse_service_sockets, "SOCKETS" }, - { config_parse_environ, "ENVIRON" }, -#ifdef HAVE_SECCOMP - { config_parse_syscall_filter, "SYSCALLS" }, - { config_parse_syscall_archs, "ARCHS" }, - { config_parse_syscall_errno, "ERRNO" }, - { config_parse_address_families, "FAMILIES" }, -#endif - { config_parse_cpu_shares, "SHARES" }, - { config_parse_memory_limit, "LIMIT" }, - { config_parse_device_allow, "DEVICE" }, - { config_parse_device_policy, "POLICY" }, - { config_parse_io_limit, "LIMIT" }, - { config_parse_io_weight, "WEIGHT" }, - { config_parse_io_device_weight, "DEVICEWEIGHT" }, - { config_parse_blockio_bandwidth, "BANDWIDTH" }, - { config_parse_blockio_weight, "WEIGHT" }, - { config_parse_blockio_device_weight, "DEVICEWEIGHT" }, - { config_parse_long, "LONG" }, - { config_parse_socket_service, "SERVICE" }, -#ifdef HAVE_SELINUX - { config_parse_exec_selinux_context, "LABEL" }, -#endif - { config_parse_job_mode, "MODE" }, - { config_parse_job_mode_isolate, "BOOLEAN" }, - { config_parse_personality, "PERSONALITY" }, - }; - - const char *prev = NULL; - const char *i; - - assert(f); - - NULSTR_FOREACH(i, load_fragment_gperf_nulstr) { - const char *rvalue = "OTHER", *lvalue; - unsigned j; - size_t prefix_len; - const char *dot; - const ConfigPerfItem *p; - - assert_se(p = load_fragment_gperf_lookup(i, strlen(i))); - - dot = strchr(i, '.'); - lvalue = dot ? dot + 1 : i; - prefix_len = dot-i; - - if (dot) - if (!prev || !strneq(prev, i, prefix_len+1)) { - if (prev) - fputc('\n', f); - - fprintf(f, "[%.*s]\n", (int) prefix_len, i); - } - - for (j = 0; j < ELEMENTSOF(table); j++) - if (p->parse == table[j].callback) { - rvalue = table[j].rvalue; - break; - } - - fprintf(f, "%s=%s\n", lvalue, rvalue); - prev = i; - } -} diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h deleted file mode 100644 index b36a2e3a02..0000000000 --- a/src/core/load-fragment.h +++ /dev/null @@ -1,123 +0,0 @@ -#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 . -***/ - -#include "unit.h" - -/* Read service data from .desktop file style configuration fragments */ - -int unit_load_fragment(Unit *u); - -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); -int config_parse_exec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_service_timeout(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_service_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_service_restart(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_bindtodevice(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_output(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_input(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_io_class(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_io_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_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_exec_cpu_sched_prio(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_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_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_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_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); -int config_parse_timer(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_trigger_unit(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_path_spec(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_service(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_service_sockets(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_busname_service(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_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bus_policy_world(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_env_file(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_ip_tos(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_condition_path(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_condition_string(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_condition_null(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_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_notify_access(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_failure_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_unit_requires_mounts_for(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_filter(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_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); -int config_parse_tasks_max(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_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_device_allow(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_io_weight(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_io_device_weight(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_io_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_blockio_weight(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_blockio_device_weight(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_blockio_bandwidth(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_netclass(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_job_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_job_mode_isolate(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_selinux_context(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_apparmor_profile(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_smack_process_label(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_families(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_runtime_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_set_status(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_namespace_path_strv(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_no_new_privileges(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_quota(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_protect_home(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_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); -int config_parse_sec_fix_0(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); -extern const char load_fragment_gperf_nulstr[]; - -typedef enum Disabled { - DISABLED_CONFIGURATION, - DISABLED_LEGACY, - DISABLED_EXPERIMENTAL, -} Disabled; diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c deleted file mode 100644 index ccf61d29fb..0000000000 --- a/src/core/locale-setup.c +++ /dev/null @@ -1,124 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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" - -int locale_setup(char ***environment) { - char **add; - char *variables[_VARIABLE_LC_MAX] = {}; - int r = 0, i; - - if (detect_container() <= 0) { - r = parse_env_file("/proc/cmdline", WHITESPACE, - "locale.LANG", &variables[VARIABLE_LANG], - "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], - "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "locale.LC_TIME", &variables[VARIABLE_LC_TIME], - "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], - "locale.LC_NAME", &variables[VARIABLE_LC_NAME], - "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /proc/cmdline: %m"); - } - - /* Hmm, nothing set on the kernel cmd line? Then let's - * try /etc/locale.conf */ - if (r <= 0) { - r = parse_env_file("/etc/locale.conf", NEWLINE, - "LANG", &variables[VARIABLE_LANG], - "LANGUAGE", &variables[VARIABLE_LANGUAGE], - "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "LC_TIME", &variables[VARIABLE_LC_TIME], - "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "LC_PAPER", &variables[VARIABLE_LC_PAPER], - "LC_NAME", &variables[VARIABLE_LC_NAME], - "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /etc/locale.conf: %m"); - } - - add = NULL; - for (i = 0; i < _VARIABLE_LC_MAX; i++) { - char *s; - - if (!variables[i]) - continue; - - s = strjoin(locale_variable_to_string(i), "=", variables[i], NULL); - if (!s) { - r = -ENOMEM; - goto finish; - } - - if (strv_consume(&add, s) < 0) { - r = -ENOMEM; - goto finish; - } - } - - if (!strv_isempty(add)) { - char **e; - - e = strv_env_merge(2, *environment, add); - if (!e) { - r = -ENOMEM; - goto finish; - } - - strv_free(*environment); - *environment = e; - } - - r = 0; - -finish: - strv_free(add); - - for (i = 0; i < _VARIABLE_LC_MAX; i++) - free(variables[i]); - - return r; -} diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h deleted file mode 100644 index 3b97497afe..0000000000 --- a/src/core/locale-setup.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int locale_setup(char ***environment); diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c deleted file mode 100644 index 04062a7910..0000000000 --- a/src/core/loopback-setup.c +++ /dev/null @@ -1,90 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-netlink.h" - -#include "loopback-setup.h" -#include "missing.h" -#include "netlink-util.h" - -static int start_loopback(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); - if (r < 0) - return r; - - r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -static bool check_loopback(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - unsigned flags; - int r; - - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX); - if (r < 0) - return false; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return false; - - r = sd_rtnl_message_link_get_flags(reply, &flags); - if (r < 0) - return false; - - return flags & IFF_UP; -} - -int loopback_setup(void) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int r; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return r; - - r = start_loopback(rtnl); - if (r < 0) { - - /* If we lack the permissions to configure the - * loopback device, but we find it to be already - * configured, let's exit cleanly, in order to - * supported unprivileged containers. */ - if (r == -EPERM && check_loopback(rtnl)) - return 0; - - return log_warning_errno(r, "Failed to configure loopback device: %m"); - } - - return 0; -} diff --git a/src/core/loopback-setup.h b/src/core/loopback-setup.h deleted file mode 100644 index e7547b8a26..0000000000 --- a/src/core/loopback-setup.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int loopback_setup(void); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c deleted file mode 100644 index 76dfcfa6d7..0000000000 --- a/src/core/machine-id-setup.c +++ /dev/null @@ -1,258 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-id128.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "id128-util.h" -#include "log.h" -#include "machine-id-setup.h" -#include "macro.h" -#include "mkdir.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 generate_machine_id(const char *root, sd_id128_t *ret) { - const char *dbus_machine_id; - _cleanup_close_ int fd = -1; - int r; - - assert(ret); - - /* First, try reading the D-Bus machine id, unless it is a symlink */ - dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id"); - fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd >= 0) { - if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) { - log_info("Initializing machine ID from D-Bus machine ID."); - return 0; - } - - fd = safe_close(fd); - } - - if (isempty(root)) { - /* If that didn't work, see if we are running in a container, - * and a machine ID was passed in via $container_uuid the way - * libvirt/LXC does it */ - - if (detect_container() > 0) { - _cleanup_free_ char *e = NULL; - - if (getenv_for_pid(1, "container_uuid", &e) > 0 && - sd_id128_from_string(e, ret) >= 0) { - log_info("Initializing machine ID from container UUID."); - return 0; - } - - } else if (detect_vm() == VIRTUALIZATION_KVM) { - - /* If we are not running in a container, see if we are - * running in qemu/kvm and a machine ID was passed in - * via -uuid on the qemu/kvm command line */ - - if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) { - log_info("Initializing machine ID from KVM UUID."); - return 0; - } - } - } - - /* If that didn't work, generate a random machine id */ - r = sd_id128_randomize(ret); - if (r < 0) - return log_error_errno(r, "Failed to generate randomized : %m"); - - log_info("Initializing machine ID from random generator."); - return 0; -} - -int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) { - const char *etc_machine_id, *run_machine_id; - _cleanup_close_ int fd = -1; - bool writable; - int r; - - etc_machine_id = prefix_roota(root, "/etc/machine-id"); - - RUN_WITH_UMASK(0000) { - /* We create this 0444, to indicate that this isn't really - * something you should ever modify. Of course, since the file - * will be owned by root it doesn't matter much, but maybe - * people look. */ - - (void) mkdir_parents(etc_machine_id, 0755); - fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd < 0) { - int old_errno = errno; - - fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (old_errno == EROFS && errno == ENOENT) - log_error_errno(errno, - "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n" - "Booting up is supported only when:\n" - "1) /etc/machine-id exists and is populated.\n" - "2) /etc/machine-id exists and is empty.\n" - "3) /etc/machine-id is missing and /etc is writable.\n"); - else - log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); - - return -errno; - } - - writable = false; - } else - writable = true; - } - - /* A we got a valid machine ID argument, that's what counts */ - if (sd_id128_is_null(machine_id)) { - - /* Try to read any existing machine ID */ - if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) - return 0; - - /* Hmm, so, the id currently stored is not useful, then let's generate one */ - r = generate_machine_id(root, &machine_id); - if (r < 0) - return r; - - if (lseek(fd, 0, SEEK_SET) == (off_t) -1) - return log_error_errno(errno, "Failed to seek: %m"); - } - - if (writable) - if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0) - goto finish; - - fd = safe_close(fd); - - /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */ - - run_machine_id = prefix_roota(root, "/run/machine-id"); - - RUN_WITH_UMASK(0022) - r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false); - if (r < 0) { - (void) unlink(run_machine_id); - return log_error_errno(r, "Cannot write %s: %m", run_machine_id); - } - - /* And now, let's mount it over */ - if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) { - (void) unlink_noerrno(run_machine_id); - return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id); - } - - log_info("Installed transient %s file.", etc_machine_id); - - /* Mark the mount read-only */ - if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0) - log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id); - -finish: - if (ret) - *ret = machine_id; - - return 0; -} - -int machine_id_commit(const char *root) { - _cleanup_close_ int fd = -1, initial_mntns_fd = -1; - const char *etc_machine_id; - sd_id128_t id; - int r; - - /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed - * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the - * original mount namespace, thus revealing the file that was just created. */ - - etc_machine_id = prefix_roota(root, "/etc/machine-id"); - - r = path_is_mount_point(etc_machine_id, 0); - if (r < 0) - return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id); - if (r == 0) { - log_debug("%s is not a mount point. Nothing to do.", etc_machine_id); - return 0; - } - - /* Read existing machine-id */ - fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); - - r = fd_is_temporary_fs(fd); - if (r < 0) - return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id); - if (r == 0) { - log_error("%s is not on a temporary file system.", etc_machine_id); - return -EROFS; - } - - r = id128_read_fd(fd, ID128_PLAIN, &id); - if (r < 0) - return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id); - - fd = safe_close(fd); - - /* Store current mount namespace */ - r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Can't fetch current mount namespace: %m"); - - /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */ - if (unshare(CLONE_NEWNS) < 0) - return log_error_errno(errno, "Failed to enter new namespace: %m"); - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) - return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m"); - - if (umount(etc_machine_id) < 0) - return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id); - - /* Update a persistent version of etc_machine_id */ - r = id128_write(etc_machine_id, ID128_PLAIN, id, true); - if (r < 0) - return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id); - - /* Return to initial namespace and proceed a lazy tmpfs unmount */ - r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1); - if (r < 0) - return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id); - - if (umount2(etc_machine_id, MNT_DETACH) < 0) - return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id); - - return 0; -} diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h deleted file mode 100644 index 29f4620646..0000000000 --- a/src/core/machine-id-setup.h +++ /dev/null @@ -1,23 +0,0 @@ -#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 . -***/ - -int machine_id_commit(const char *root); -int machine_id_setup(const char *root, sd_id128_t requested, sd_id128_t *ret); diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in deleted file mode 100644 index 6e8a3b3e3d..0000000000 --- a/src/core/macros.systemd.in +++ /dev/null @@ -1,113 +0,0 @@ -# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */ -# -# This file is part of systemd. -# -# Copyright 2012 Lennart Poettering -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# systemd is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . - -# RPM macros for packages installing systemd unit files - -%_unitdir @systemunitdir@ -%_userunitdir @userunitdir@ -%_presetdir @systempresetdir@ -%_udevhwdbdir @udevhwdbdir@ -%_udevrulesdir @udevrulesdir@ -%_journalcatalogdir @catalogdir@ -%_tmpfilesdir @tmpfilesdir@ -%_sysusersdir @sysusersdir@ -%_sysctldir @sysctldir@ -%_binfmtdir @binfmtdir@ -%_systemdgeneratordir @systemgeneratordir@ -%_systemdusergeneratordir @usergeneratordir@ - -%systemd_requires \ -Requires(post): systemd \ -Requires(preun): systemd \ -Requires(postun): systemd \ -%{nil} - -%systemd_ordering \ -OrderWithRequires(post): systemd \ -OrderWithRequires(preun): systemd \ -OrderWithRequires(postun): systemd \ -%{nil} - -%systemd_post() \ -if [ $1 -eq 1 ] ; then \ - # Initial installation \ - systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}} - -%systemd_preun() \ -if [ $1 -eq 0 ] ; then \ - # Package removal, not upgrade \ - systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_user_preun() \ -if [ $1 -eq 0 ] ; then \ - # Package removal, not upgrade \ - systemctl --no-reload --user --global disable %{?*} > /dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_postun() %{nil} - -%systemd_user_postun() %{nil} - -%systemd_postun_with_restart() \ -if [ $1 -ge 1 ] ; then \ - # Package upgrade, not uninstall \ - systemctl try-restart %{?*} >/dev/null 2>&1 || : \ -fi \ -%{nil} - -%systemd_user_postun_with_restart() %{nil} - -%udev_hwdb_update() \ -udevadm hwdb --update >/dev/null 2>&1 || : \ -%{nil} - -%udev_rules_update() \ -udevadm control --reload >/dev/null 2>&1 || : \ -%{nil} - -%journal_catalog_update() \ -journalctl --update-catalog >/dev/null 2>&1 || : \ -%{nil} - -%tmpfiles_create() \ -systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \ -%{nil} - -%sysusers_create() \ -systemd-sysusers %{?*} >/dev/null 2>&1 || : \ -%{nil} - -%sysusers_create_inline() \ -echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : \ -%{nil} - -%sysctl_apply() \ -@rootlibexecdir@/systemd-sysctl %{?*} >/dev/null 2>&1 || : \ -%{nil} - -%binfmt_apply() \ -@rootlibexecdir@/systemd-binfmt %{?*} >/dev/null 2>&1 || : \ -%{nil} diff --git a/src/core/main.c b/src/core/main.c deleted file mode 100644 index 719bc49475..0000000000 --- a/src/core/main.c +++ /dev/null @@ -1,2195 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SECCOMP -#include -#endif -#ifdef HAVE_VALGRIND_VALGRIND_H -#include -#endif - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "architecture.h" -#include "build.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 "env-util.h" -#include "fd-util.h" -#include "fdset.h" -#include "fileio.h" -#include "formats-util.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 "log.h" -#include "loopback-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 "raw-clone.h" -#include "rlimit-util.h" -#include "selinux-setup.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "smack-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 "umask-util.h" -#include "user-util.h" -#include "virt.h" -#include "watchdog.h" - -static enum { - ACTION_RUN, - ACTION_HELP, - ACTION_VERSION, - ACTION_TEST, - ACTION_DUMP_CONFIGURATION_ITEMS, - ACTION_DONE -} arg_action = ACTION_RUN; -static char *arg_default_unit = NULL; -static bool arg_system = false; -static bool arg_dump_core = true; -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; -static bool arg_no_pager = false; -static char ***arg_join_controllers = NULL; -static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; -static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; -static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC; -static usec_t arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; -static usec_t arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; -static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL; -static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST; -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 = 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; -static FILE* arg_serialization = NULL; -static bool arg_default_cpu_accounting = false; -static bool arg_default_io_accounting = false; -static bool arg_default_blockio_accounting = false; -static bool arg_default_memory_accounting = false; -static bool arg_default_tasks_accounting = true; -static uint64_t arg_default_tasks_max = UINT64_MAX; -static sd_id128_t arg_machine_id = {}; - -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 */ - (void) raise(sig); - else if (!arg_dump_core) - log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); - else { - sa = (struct sigaction) { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP|SA_RESTART, - }; - - /* We want to wait for the core process, hence let's enable SIGCHLD */ - (void) sigaction(SIGCHLD, &sa, NULL); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); - else if (pid == 0) { - /* Enable default signal handler for core dump */ - - sa = (struct sigaction) { - .sa_handler = SIG_DFL, - }; - (void) sigaction(sig, &sa, NULL); - - /* Don't limit the coredump size */ - (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); - - /* Just to be sure... */ - (void) chdir("/"); - - /* Raise the signal again */ - pid = raw_getpid(); - (void) kill(pid, sig); /* raise() would kill the parent */ - - assert_not_reached("We shouldn't be here..."); - _exit(EXIT_FAILURE); - } else { - siginfo_t status; - int r; - - /* Order things nicely. */ - r = wait_for_terminate(pid, &status); - if (r < 0) - log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); - else if (status.si_code != CLD_DUMPED) - log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", - signal_to_string(sig), - pid, sigchld_code_to_string(status.si_code), - status.si_status, - strna(status.si_code == CLD_EXITED - ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL) - : signal_to_string(status.si_status))); - else - log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid); - } - } - - if (arg_crash_chvt >= 0) - (void) chvt(arg_crash_chvt); - - sa = (struct sigaction) { - .sa_handler = SIG_IGN, - .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, - }; - - /* Let the kernel reap children for us */ - (void) sigaction(SIGCHLD, &sa, NULL); - - if (arg_crash_shell) { - log_notice("Executing crash shell in 10s..."); - (void) sleep(10); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off crash shell: %m"); - else if (pid == 0) { - (void) setsid(); - (void) make_console_stdio(); - (void) execle("/bin/sh", "/bin/sh", NULL, environ); - - log_emergency_errno(errno, "execle() failed: %m"); - _exit(EXIT_FAILURE); - } else { - log_info("Spawned crash shell as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } - - freeze_or_reboot(); -} - -static void install_crash_handler(void) { - static const struct sigaction sa = { - .sa_handler = crash, - .sa_flags = SA_NODEFER, /* So that we can raise the signal again from the signal handler */ - }; - int r; - - /* We ignore the return value here, since, we don't mind if we - * cannot set up a crash handler */ - r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1); - if (r < 0) - log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); -} - -static int console_setup(void) { - _cleanup_close_ int tty_fd = -1; - int r; - - tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (tty_fd < 0) - return log_error_errno(tty_fd, "Failed to open /dev/console: %m"); - - /* We don't want to force text mode. plymouth may be showing - * pictures already from initrd. */ - r = reset_terminal_fd(tty_fd, false); - if (r < 0) - return log_error_errno(r, "Failed to reset /dev/console: %m"); - - return 0; -} - -static int parse_crash_chvt(const char *value) { - int b; - - if (safe_atoi(value, &arg_crash_chvt) >= 0) - return 0; - - 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; -} - -static int set_machine_id(const char *m) { - sd_id128_t t; - assert(m); - - if (sd_id128_from_string(m, &t) < 0) - return -EINVAL; - - if (sd_id128_is_null(t)) - return -EINVAL; - - arg_machine_id = t; - return 0; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - int r; - - assert(key); - - if (streq(key, "systemd.unit") && value) { - - if (!in_initrd()) - return free_and_strdup(&arg_default_unit, value); - - } else if (streq(key, "rd.systemd.unit") && value) { - - if (in_initrd()) - return free_and_strdup(&arg_default_unit, value); - - } else if (streq(key, "systemd.dump_core") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse dump core switch %s. Ignoring.", 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); - if (r < 0) - log_warning("Failed to parse crash shell switch %s. Ignoring.", value); - else - arg_crash_shell = r; - - } else if (streq(key, "systemd.crash_reboot") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse crash reboot switch %s. Ignoring.", value); - else - arg_crash_reboot = r; - - } else if (streq(key, "systemd.confirm_spawn") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse confirm spawn switch %s. Ignoring.", value); - else - arg_confirm_spawn = r; - - } else if (streq(key, "systemd.show_status") && value) { - - r = parse_show_status(value, &arg_show_status); - if (r < 0) - log_warning("Failed to parse show status switch %s. Ignoring.", value); - - } else if (streq(key, "systemd.default_standard_output") && value) { - - r = exec_output_from_string(value); - if (r < 0) - log_warning("Failed to parse default standard output switch %s. Ignoring.", value); - else - arg_default_std_output = r; - - } else if (streq(key, "systemd.default_standard_error") && value) { - - r = exec_output_from_string(value); - if (r < 0) - log_warning("Failed to parse default standard error switch %s. Ignoring.", value); - else - arg_default_std_error = r; - - } else if (streq(key, "systemd.setenv") && value) { - - if (env_assignment_is_valid(value)) { - char **env; - - env = strv_env_set(arg_default_environment, value); - if (env) - arg_default_environment = env; - else - log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", 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) - arg_show_status = SHOW_STATUS_AUTO; - - } else if (streq(key, "debug") && !value) { - - /* Note that log_parse_environment() handles 'debug' - * too, and sets the log level to LOG_DEBUG. */ - - if (detect_container() > 0) - log_set_target(LOG_TARGET_CONSOLE); - - } else if (!value) { - const char *target; - - /* SysV compatibility */ - target = runlevel_to_target(key); - if (target) - return free_and_strdup(&arg_default_unit, target); - - } else if (streq(key, "systemd.default_timeout_start_sec") && value) { - - r = parse_sec(value, &arg_default_timeout_start_usec); - if (r < 0) - log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); - - if (arg_default_timeout_start_usec <= 0) - arg_default_timeout_start_usec = USEC_INFINITY; - } - - return 0; -} - -#define DEFINE_SETTER(name, func, descr) \ - static int 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 r; \ - \ - assert(filename); \ - assert(lvalue); \ - assert(rvalue); \ - \ - r = func(rvalue); \ - if (r < 0) \ - log_syntax(unit, LOG_ERR, filename, line, r, \ - "Invalid " descr "'%s': %m", \ - rvalue); \ - \ - return 0; \ - } - -DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level") -DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target") -DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ) -DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location") - -static int config_parse_cpu_affinity2( - 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_cpu_free_ cpu_set_t *c = NULL; - int ncpus; - - ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue); - if (ncpus < 0) - return ncpus; - - if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning("Failed to set CPU affinity: %m"); - - return 0; -} - -static int config_parse_show_status( - 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 k; - ShowStatus *b = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - 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); - return 0; - } - - return 0; -} - -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) { - - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - 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; - } - - return 0; -} - -static int config_parse_join_controllers(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; - unsigned n = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - - arg_join_controllers = strv_free_free(arg_join_controllers); - - for (;;) { - _cleanup_free_ char *word = NULL; - char **l; - 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; - - l = strv_split(word, ","); - if (!l) - return log_oom(); - strv_uniq(l); - - if (strv_length(l) <= 1) { - strv_free(l); - continue; - } - - if (!arg_join_controllers) { - arg_join_controllers = new(char**, 2); - if (!arg_join_controllers) { - strv_free(l); - return log_oom(); - } - - arg_join_controllers[0] = l; - arg_join_controllers[1] = NULL; - - n = 1; - } else { - char ***a; - char ***t; - - t = new0(char**, n+2); - if (!t) { - strv_free(l); - return log_oom(); - } - - n = 0; - - for (a = arg_join_controllers; *a; a++) { - - if (strv_overlap(*a, l)) { - if (strv_extend_strv(&l, *a, false) < 0) { - strv_free(l); - strv_free_free(t); - return log_oom(); - } - - } else { - char **c; - - c = strv_copy(*a); - if (!c) { - strv_free(l); - strv_free_free(t); - return log_oom(); - } - - t[n++] = c; - } - } - - t[n++] = strv_uniq(l); - - strv_free_free(arg_join_controllers); - arg_join_controllers = t; - } - } - if (!isempty(rvalue)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; -} - -static int parse_config_file(void) { - - const ConfigTableItem items[] = { - { "Manager", "LogLevel", config_parse_level2, 0, NULL }, - { "Manager", "LogTarget", config_parse_target, 0, NULL }, - { "Manager", "LogColor", config_parse_color, 0, NULL }, - { "Manager", "LogLocation", config_parse_location, 0, NULL }, - { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, - { "Manager", "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", "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_capability_set, 0, &arg_capability_bounding_set }, -#ifdef HAVE_SECCOMP - { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, -#endif - { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, - { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec }, - { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, - { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, - { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, - { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, - { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, - { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ - { "Manager", "DefaultStartLimitIntervalSec",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, RLIMIT_CPU, arg_default_rlimit }, - { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit }, - { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit }, - { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit }, - { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit }, - { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit }, - { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit }, - { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit }, - { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit }, - { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit }, - { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit }, - { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit }, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit }, - { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit }, - { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit }, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit }, - { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, - { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_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_system ? - PKGSYSCONFDIR "/system.conf" : - PKGSYSCONFDIR "/user.conf"; - - conf_dirs_nulstr = arg_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); - - /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY - * like everywhere else. */ - if (arg_default_timeout_start_usec <= 0) - arg_default_timeout_start_usec = USEC_INFINITY; - if (arg_default_timeout_stop_usec <= 0) - arg_default_timeout_stop_usec = USEC_INFINITY; - - return 0; -} - -static void manager_set_defaults(Manager *m) { - - assert(m); - - m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec; - m->default_std_output = arg_default_std_output; - m->default_std_error = arg_default_std_error; - m->default_timeout_start_usec = arg_default_timeout_start_usec; - m->default_timeout_stop_usec = arg_default_timeout_stop_usec; - m->default_restart_usec = arg_default_restart_usec; - m->default_start_limit_interval = arg_default_start_limit_interval; - m->default_start_limit_burst = arg_default_start_limit_burst; - m->default_cpu_accounting = arg_default_cpu_accounting; - m->default_io_accounting = arg_default_io_accounting; - 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); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - ARG_UNIT, - ARG_SYSTEM, - ARG_USER, - ARG_TEST, - ARG_NO_PAGER, - 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_MACHINE_ID - }; - - static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "test", no_argument, NULL, ARG_TEST }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "help", no_argument, NULL, 'h' }, - { "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 }, - {} - }; - - int c, r; - - assert(argc >= 1); - assert(argv); - - if (getpid() == 1) - opterr = 0; - - while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0) - - switch (c) { - - case ARG_LOG_LEVEL: - r = log_set_max_level_from_string(optarg); - if (r < 0) { - log_error("Failed to parse log level %s.", optarg); - return r; - } - - break; - - case ARG_LOG_TARGET: - r = log_set_target_from_string(optarg); - if (r < 0) { - log_error("Failed to parse log target %s.", optarg); - return r; - } - - break; - - case ARG_LOG_COLOR: - - if (optarg) { - r = log_show_color_from_string(optarg); - if (r < 0) { - log_error("Failed to parse log color setting %s.", optarg); - return r; - } - } else - log_show_color(true); - - break; - - case ARG_LOG_LOCATION: - if (optarg) { - r = log_show_location_from_string(optarg); - if (r < 0) { - log_error("Failed to parse log location setting %s.", optarg); - return r; - } - } else - log_show_location(true); - - break; - - case ARG_DEFAULT_STD_OUTPUT: - r = exec_output_from_string(optarg); - if (r < 0) { - log_error("Failed to parse default standard output setting %s.", optarg); - return r; - } else - arg_default_std_output = r; - break; - - case ARG_DEFAULT_STD_ERROR: - r = exec_output_from_string(optarg); - if (r < 0) { - log_error("Failed to parse default standard error output setting %s.", optarg); - return r; - } else - arg_default_std_error = r; - break; - - case ARG_UNIT: - - r = free_and_strdup(&arg_default_unit, optarg); - if (r < 0) - return log_error_errno(r, "Failed to set default unit %s: %m", optarg); - - break; - - case ARG_SYSTEM: - arg_system = true; - break; - - case ARG_USER: - arg_system = false; - break; - - case ARG_TEST: - arg_action = ACTION_TEST; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_VERSION: - arg_action = ACTION_VERSION; - break; - - case ARG_DUMP_CONFIGURATION_ITEMS: - arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; - break; - - case ARG_DUMP_CORE: - 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; - } - 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: - 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; - } - break; - - case ARG_CONFIRM_SPAWN: - r = optarg ? parse_boolean(optarg) : 1; - if (r < 0) { - log_error("Failed to parse confirm spawn boolean %s.", optarg); - return r; - } - arg_confirm_spawn = r; - break; - - case ARG_SHOW_STATUS: - if (optarg) { - r = parse_show_status(optarg, &arg_show_status); - if (r < 0) { - log_error("Failed to parse show status boolean %s.", optarg); - return r; - } - } else - arg_show_status = SHOW_STATUS_YES; - break; - - case ARG_DESERIALIZE: { - int fd; - FILE *f; - - r = safe_atoi(optarg, &fd); - if (r < 0 || fd < 0) { - log_error("Failed to parse deserialize option %s.", optarg); - return -EINVAL; - } - - (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; - } - - case ARG_SWITCHED_ROOT: - 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; - break; - - case 'D': - log_set_max_level(LOG_DEBUG); - break; - - case 'b': - case 's': - case 'z': - /* Just to eat away the sysvinit kernel - * cmdline args without getopt() error - * messages that we'll parse in - * parse_proc_cmdline_word() or ignore. */ - - case '?': - if (getpid() != 1) - return -EINVAL; - else - return 0; - - default: - assert_not_reached("Unhandled option code."); - } - - if (optind < argc && getpid() != 1) { - /* Hmm, when we aren't run as init system - * let's complain about excess arguments */ - - log_error("Excess arguments."); - return -EINVAL; - } - - return 0; -} - -static int help(void) { - - printf("%s [OPTIONS...]\n\n" - "Starts up and maintains the system or user services.\n\n" - " -h --help Show this help\n" - " --test Determine startup sequence, dump it and exit\n" - " --no-pager Do not pipe output into a pager\n" - " --dump-configuration-items Dump understood unit configuration items\n" - " --unit=UNIT Set default unit\n" - " --system Run a system instance, even if PID != 1\n" - " --user Run a user instance\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[=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); - - return 0; -} - -static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) { - _cleanup_fdset_free_ FDSet *fds = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(m); - assert(_f); - assert(_fds); - - r = manager_open_serialization(m, &f); - 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) - return log_oom(); - - r = manager_serialize(m, f, fds, switching_root); - if (r < 0) - return log_error_errno(r, "Failed to serialize state: %m"); - - 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) - return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m"); - - r = fdset_cloexec(fds, false); - if (r < 0) - return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); - - *_f = f; - *_fds = fds; - - f = NULL; - fds = NULL; - - return 0; -} - -static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { - struct rlimit nl; - int r; - - assert(saved_rlimit); - - /* Save the original RLIMIT_NOFILE so that we can reset it - * later when transitioning from the initrd to the main - * systemd or suchlike. */ - if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) - return log_error_errno(errno, "Reading RLIMIT_NOFILE failed: %m"); - - /* Make sure forked processes get the default kernel setting */ - if (!arg_default_rlimit[RLIMIT_NOFILE]) { - struct rlimit *rl; - - rl = newdup(struct rlimit, saved_rlimit, 1); - if (!rl) - return log_oom(); - - arg_default_rlimit[RLIMIT_NOFILE] = rl; - } - - /* Bump up the resource limit for ourselves substantially */ - nl.rlim_cur = nl.rlim_max = 64*1024; - r = setrlimit_closest(RLIMIT_NOFILE, &nl); - if (r < 0) - return log_error_errno(r, "Setting RLIMIT_NOFILE failed: %m"); - - return 0; -} - -static void test_usr(void) { - - /* Check that /usr is not a separate fs */ - - if (dir_is_empty("/usr") <= 0) - return; - - log_warning("/usr appears to be on its own filesystem and is not already mounted. This is not a supported setup. " - "Some things will probably break (sometimes even silently) in mysterious ways. " - "Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information."); -} - -static int initialize_join_controllers(void) { - /* By default, mount "cpu" + "cpuacct" together, and "net_cls" - * + "net_prio". We'd like to add "cpuset" to the mix, but - * "cpuset" doesn't really work for groups with no initialized - * attributes. */ - - arg_join_controllers = new(char**, 3); - if (!arg_join_controllers) - return -ENOMEM; - - arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); - if (!arg_join_controllers[0]) - goto oom; - - 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) { -#ifdef HAVE_SECCOMP - scmp_filter_ctx *seccomp; - Iterator i; - void *id; - int r; - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return log_oom(); - - SET_FOREACH(id, arg_syscall_archs, i) { - r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); - if (r == -EEXIST) - continue; - if (r < 0) { - log_error_errno(r, "Failed to add architecture to seccomp: %m"); - goto finish; - } - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) { - log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); - goto finish; - } - - r = seccomp_load(seccomp); - if (r < 0) - log_error_errno(r, "Failed to add install architecture seccomp: %m"); - -finish: - seccomp_release(seccomp); - return r; -#else - return 0; -#endif -} - -static int status_welcome(void) { - _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; - int r; - - r = parse_env_file("/etc/os-release", NEWLINE, - "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color, - NULL); - if (r == -ENOENT) - r = parse_env_file("/usr/lib/os-release", NEWLINE, - "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file: %m"); - - if (log_get_show_color()) - return status_printf(NULL, false, false, - "\nWelcome to \x1B[%sm%s\x1B[0m!\n", - isempty(ansi_color) ? "1" : ansi_color, - isempty(pretty_name) ? "GNU/Linux" : pretty_name); - else - return status_printf(NULL, false, false, - "\nWelcome to %s!\n", - isempty(pretty_name) ? "GNU/Linux" : pretty_name); -} - -static int write_container_id(void) { - const char *c; - int r; - - c = getenv("container"); - if (isempty(c)) - return 0; - - RUN_WITH_UMASK(0022) - 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; -} - -static int fixup_environment(void) { - _cleanup_free_ char *term = NULL; - int r; - - /* We expect the environment to be set correctly - * if run inside a container. */ - if (detect_container() > 0) - return 0; - - /* When started as PID1, the kernel uses /dev/console - * for our stdios and uses TERM=linux whatever the - * backend device used by the console. We try to make - * a better guess here since some consoles might not - * have support for color mode for example. - * - * However if TERM was configured through the kernel - * command line then leave it alone. */ - - r = get_proc_cmdline_key("TERM=", &term); - if (r < 0) - return r; - - if (r == 0) { - term = strdup(default_term_for_tty("/dev/console") + 5); - if (!term) - return -ENOMEM; - } - - if (setenv("TERM", term, 1) < 0) - return -errno; - - return 0; -} - -int main(int argc, char *argv[]) { - Manager *m = NULL; - int r, retval = EXIT_FAILURE; - usec_t before_startup, after_startup; - char timespan[FORMAT_TIMESPAN_MAX]; - FDSet *fds = NULL; - bool reexecute = false; - const char *shutdown_verb = NULL; - dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp userspace_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp kernel_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp security_start_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp security_finish_timestamp = DUAL_TIMESTAMP_NULL; - static char systemd[] = "systemd"; - bool skip_setup = false; - unsigned j; - bool loaded_policy = false; - bool arm_reboot_watchdog = false; - bool queue_default_job = false; - bool empty_etc = false; - char *switch_root_dir = NULL, *switch_root_init = NULL; - struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0); - const char *error_message = NULL; - -#ifdef HAVE_SYSV_COMPAT - if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { - /* This is compatibility support for SysV, where - * calling init as a user is identical to telinit. */ - - execv(SYSTEMCTL_BINARY_PATH, argv); - log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); - return 1; - } -#endif - - dual_timestamp_from_monotonic(&kernel_timestamp, 0); - dual_timestamp_get(&userspace_timestamp); - - /* Determine if this is a reexecution or normal bootup. We do - * the full command line parsing much later, so let's just - * have a quick peek here. */ - if (strv_find(argv+1, "--deserialize")) - skip_setup = true; - - /* If we have switched root, do all the special setup - * things */ - if (strv_find(argv+1, "--switched-root")) - skip_setup = false; - - /* If we get started via the /sbin/init symlink then we are - called 'init'. After a subsequent reexecution we are then - called 'systemd'. That is confusing, hence let's call us - systemd right-away. */ - program_invocation_short_name = systemd; - prctl(PR_SET_NAME, systemd); - - saved_argv = argv; - saved_argc = argc; - - log_set_upgrade_syslog_to_journal(true); - - /* Disable the umask logic */ - if (getpid() == 1) - umask(0); - - if (getpid() == 1 && detect_container() <= 0) { - - /* Running outside of a container as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_KMSG); - log_open(); - - if (in_initrd()) - initrd_timestamp = userspace_timestamp; - - if (!skip_setup) { - r = mount_setup_early(); - if (r < 0) { - error_message = "Failed to early mount API filesystems"; - goto finish; - } - dual_timestamp_get(&security_start_timestamp); - if (mac_selinux_setup(&loaded_policy) < 0) { - error_message = "Failed to load SELinux policy"; - goto finish; - } else if (ima_setup() < 0) { - error_message = "Failed to load IMA policy"; - goto finish; - } else if (mac_smack_setup(&loaded_policy) < 0) { - error_message = "Failed to load SMACK policy"; - goto finish; - } - dual_timestamp_get(&security_finish_timestamp); - } - - if (mac_selinux_init() < 0) { - error_message = "Failed to initialize SELinux policy"; - goto finish; - } - - if (!skip_setup) { - if (clock_is_localtime(NULL) > 0) { - int min; - - /* - * The very first call of settimeofday() also does a time warp in the kernel. - * - * In the rtc-in-local time mode, we set the kernel's timezone, and rely on - * external tools to take care of maintaining the RTC and do all adjustments. - * This matches the behavior of Windows, which leaves the RTC alone if the - * registry tells that the RTC runs in UTC. - */ - r = clock_set_timezone(&min); - if (r < 0) - log_error_errno(r, "Failed to apply local time delta, ignoring: %m"); - else - log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); - } else if (!in_initrd()) { - /* - * Do a dummy very first call to seal the kernel's time warp magic. - * - * Do not call this from inside the initrd. The initrd might not - * carry /etc/adjtime with LOCAL, but the real system could be set up - * that way. In such case, we need to delay the time-warp or the sealing - * until we reach the real system. - * - * Do no set the kernel's timezone. The concept of local time cannot - * be supported reliably, the time will jump or be incorrect at every daylight - * saving time change. All kernel local time concepts will be treated - * as UTC that way. - */ - (void) clock_reset_timewarp(); - } - - r = clock_apply_epoch(); - if (r < 0) - log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); - else if (r > 0) - log_info("System time before build time, advancing clock."); - } - - /* Set the default for later on, but don't actually - * open the logs like this for now. Note that if we - * are transitioning from the initrd there might still - * be journal fd open, and we shouldn't attempt - * opening that before we parsed /proc/cmdline which - * might redirect output elsewhere. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - - } else if (getpid() == 1) { - /* Running inside a container, as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_CONSOLE); - log_close_console(); /* force reopen of /dev/console */ - log_open(); - - /* For the later on, see above... */ - log_set_target(LOG_TARGET_JOURNAL); - - /* clear the kernel timestamp, - * because we are in a container */ - kernel_timestamp = DUAL_TIMESTAMP_NULL; - } else { - /* Running as user instance */ - arg_system = false; - log_set_target(LOG_TARGET_AUTO); - log_open(); - - /* clear the kernel timestamp, - * because we are not PID 1 */ - kernel_timestamp = DUAL_TIMESTAMP_NULL; - } - - if (getpid() == 1) { - /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit) - * will process core dumps for system services by default. */ - (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); - - /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored - * until the systemd-coredump tool is enabled via sysctl. */ - if (!skip_setup) - (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); - } - - if (arg_system) { - if (fixup_environment() < 0) { - error_message = "Failed to fix up PID1 environment"; - goto finish; - } - - /* Try to figure out if we can use colors with the console. No - * need to do that for user instances since they never log - * into the console. */ - log_show_color(colors_enabled()); - make_null_stdio(); - } - - /* Initialize default unit */ - 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"; - goto finish; - } - - r = initialize_join_controllers(); - if (r < 0) { - error_message = "Failed to initialize cgroup controllers"; - goto finish; - } - - /* Mount /proc, /sys and friends, so that /proc/cmdline and - * /proc/$PID/fd is available. */ - if (getpid() == 1) { - - /* Load the kernel modules early, so that we kdbus.ko is loaded before kdbusfs shall be mounted */ - if (!skip_setup) - kmod_setup(); - - r = mount_setup(loaded_policy); - if (r < 0) { - error_message = "Failed to mount API filesystems"; - goto finish; - } - } - - /* Reset all signal handlers. */ - (void) reset_all_signal_handlers(); - (void) ignore_signals(SIGNALS_IGNORE, -1); - - arg_default_tasks_max = system_tasks_max_scale(15U, 100U); /* 15% the system PIDs equals 4915 by default. */ - - if (parse_config_file() < 0) { - error_message = "Failed to parse config file"; - goto finish; - } - - if (arg_system) { - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - } - - /* Note that this also parses bits from the kernel command - * line, including "debug". */ - log_parse_environment(); - - if (parse_argv(argc, argv) < 0) { - error_message = "Failed to parse commandline arguments"; - goto finish; - } - - if (arg_action == ACTION_TEST && - geteuid() == 0) { - log_error("Don't run test mode as root."); - goto finish; - } - - if (!arg_system && - arg_action == ACTION_RUN && - sd_booted() <= 0) { - log_error("Trying to run as user instance, but the system has not been booted with systemd."); - goto finish; - } - - if (arg_system && - arg_action == ACTION_RUN && - running_in_chroot() > 0) { - log_error("Cannot be run in a chroot() environment."); - goto finish; - } - - if (arg_action == ACTION_TEST) - skip_setup = true; - - if (arg_action == ACTION_TEST || arg_action == ACTION_HELP) - pager_open(arg_no_pager, false); - - if (arg_action == ACTION_HELP) { - retval = help(); - goto finish; - } else if (arg_action == ACTION_VERSION) { - retval = version(); - goto finish; - } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { - unit_dump_config_items(stdout); - retval = EXIT_SUCCESS; - goto finish; - } else if (arg_action == ACTION_DONE) { - retval = EXIT_SUCCESS; - goto finish; - } - - if (!arg_system && - !getenv("XDG_RUNTIME_DIR")) { - log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); - goto finish; - } - - assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST); - - /* Close logging fds, in order not to confuse fdset below */ - log_close(); - - /* Remember open file descriptors for later deserialization */ - r = fdset_new_fill(&fds); - if (r < 0) { - log_emergency_errno(r, "Failed to allocate fd set: %m"); - error_message = "Failed to allocate fd set"; - goto finish; - } else - fdset_cloexec(fds, true); - - if (arg_serialization) - assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); - - if (arg_system) - /* Become a session leader if we aren't one yet. */ - setsid(); - - /* Move out of the way, so that we won't block unmounts */ - assert_se(chdir("/") == 0); - - /* Reset the console, but only if this is really init and we - * are freshly booted */ - if (arg_system && arg_action == ACTION_RUN) { - - /* If we are init, we connect stdin/stdout/stderr to - * /dev/null and make sure we don't have a controlling - * tty. */ - release_terminal(); - - if (getpid() == 1 && !skip_setup) - console_setup(); - } - - /* Open the logging devices, if possible and necessary */ - log_open(); - - if (arg_show_status == _SHOW_STATUS_UNSET) - arg_show_status = SHOW_STATUS_YES; - - /* Make sure we leave a core dump without panicing the - * kernel. */ - if (getpid() == 1) { - install_crash_handler(); - - r = mount_cgroup_controllers(arg_join_controllers); - if (r < 0) - goto finish; - } - - if (arg_system) { - int v; - - log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")", - arg_action == ACTION_TEST ? "test " : "" ); - - v = detect_virtualization(); - if (v > 0) - log_info("Detected virtualization %s.", virtualization_to_string(v)); - - write_container_id(); - - log_info("Detected architecture %s.", architecture_to_string(uname_architecture())); - - if (in_initrd()) - log_info("Running in initial RAM disk."); - - /* Let's check whether /etc is already populated. We - * don't actually really check for that, but use - * /etc/machine-id as flag file. This allows container - * managers and installers to provision a couple of - * files already. If the container manager wants to - * provision the machine ID itself it should pass - * $container_uuid to PID 1. */ - - empty_etc = access("/etc/machine-id", F_OK) < 0; - if (empty_etc) - log_info("Running with unpopulated /etc."); - } else { - _cleanup_free_ char *t; - - t = uid_to_name(getuid()); - log_debug(PACKAGE_STRING " running in %suser mode for user "UID_FMT"/%s. (" SYSTEMD_FEATURES ")", - arg_action == ACTION_TEST ? " test" : "", getuid(), t); - } - - if (arg_system && !skip_setup) { - if (arg_show_status > 0) - status_welcome(); - - hostname_setup(); - machine_id_setup(NULL, arg_machine_id, NULL); - loopback_setup(); - bump_unix_max_dgram_qlen(); - - test_usr(); - } - - if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) - watchdog_set_timeout(&arg_runtime_watchdog); - - if (arg_timer_slack_nsec != NSEC_INFINITY) - if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_error_errno(errno, "Failed to adjust timer slack: %m"); - - 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, true); - if (r < 0) { - log_emergency_errno(r, "Failed to drop capability bounding set: %m"); - error_message = "Failed to drop capability bounding set"; - goto finish; - } - } - - if (arg_syscall_archs) { - r = enforce_syscall_archs(arg_syscall_archs); - if (r < 0) { - error_message = "Failed to set syscall architectures"; - goto finish; - } - } - - if (!arg_system) - /* Become reaper of our children */ - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) - log_warning_errno(errno, "Failed to make us a subreaper: %m"); - - if (arg_system) { - bump_rlimit_nofile(&saved_rlimit_nofile); - - 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_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."); - } - } - - r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m); - if (r < 0) { - log_emergency_errno(r, "Failed to allocate manager object: %m"); - error_message = "Failed to allocate manager object"; - goto finish; - } - - m->confirm_spawn = arg_confirm_spawn; - m->runtime_watchdog = arg_runtime_watchdog; - m->shutdown_watchdog = arg_shutdown_watchdog; - m->userspace_timestamp = userspace_timestamp; - m->kernel_timestamp = kernel_timestamp; - m->initrd_timestamp = initrd_timestamp; - m->security_start_timestamp = security_start_timestamp; - m->security_finish_timestamp = security_finish_timestamp; - - manager_set_defaults(m); - manager_set_show_status(m, arg_show_status); - manager_set_first_boot(m, empty_etc); - - /* Remember whether we should queue the default job */ - queue_default_job = !arg_serialization || arg_switched_root; - - before_startup = now(CLOCK_MONOTONIC); - - r = manager_startup(m, arg_serialization, fds); - if (r < 0) - log_error_errno(r, "Failed to fully start up daemon: %m"); - - /* This will close all file descriptors that were opened, but - * not claimed by any unit. */ - fds = fdset_free(fds); - - arg_serialization = safe_fclose(arg_serialization); - - if (queue_default_job) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Unit *target = NULL; - Job *default_unit_job; - - log_debug("Activating default unit: %s", arg_default_unit); - - r = manager_load_unit(m, arg_default_unit, NULL, &error, &target); - if (r < 0) - log_error("Failed to load default target: %s", bus_error_message(&error, r)); - else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) - log_error_errno(target->load_error, "Failed to load default target: %m"); - else if (target->load_state == UNIT_MASKED) - log_error("Default target masked."); - - if (!target || target->load_state != UNIT_LOADED) { - log_info("Trying to load rescue target..."); - - r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); - if (r < 0) { - log_emergency("Failed to load rescue target: %s", bus_error_message(&error, r)); - error_message = "Failed to load rescue target"; - goto finish; - } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) { - log_emergency_errno(target->load_error, "Failed to load rescue target: %m"); - error_message = "Failed to load rescue target"; - goto finish; - } else if (target->load_state == UNIT_MASKED) { - log_emergency("Rescue target masked."); - error_message = "Rescue target masked"; - goto finish; - } - } - - assert(target->load_state == UNIT_LOADED); - - if (arg_action == ACTION_TEST) { - printf("-> By units:\n"); - manager_dump_units(m, stdout, "\t"); - } - - 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)); - - 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"; - goto finish; - } - } else if (r < 0) { - log_emergency("Failed to isolate default target: %s", bus_error_message(&error, r)); - error_message = "Failed to isolate default target"; - goto finish; - } - - m->default_unit_job_id = default_unit_job->id; - - after_startup = now(CLOCK_MONOTONIC); - log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG, - "Loaded units and determined initial transaction in %s.", - format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC)); - - if (arg_action == ACTION_TEST) { - printf("-> By jobs:\n"); - manager_dump_jobs(m, stdout, "\t"); - retval = EXIT_SUCCESS; - goto finish; - } - } - - for (;;) { - r = manager_loop(m); - if (r < 0) { - log_emergency_errno(r, "Failed to run main loop: %m"); - error_message = "Failed to run main loop"; - goto finish; - } - - switch (m->exit_code) { - - case MANAGER_RELOAD: - log_info("Reloading."); - - r = parse_config_file(); - if (r < 0) - log_error("Failed to parse config file."); - - manager_set_defaults(m); - - r = manager_reload(m); - if (r < 0) - log_error_errno(r, "Failed to reload: %m"); - break; - - case MANAGER_REEXECUTE: - - if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) { - error_message = "Failed to prepare for reexecution"; - goto finish; - } - - reexecute = true; - log_notice("Reexecuting."); - goto finish; - - case MANAGER_SWITCH_ROOT: - /* Steal the switch root parameters */ - switch_root_dir = m->switch_root; - switch_root_init = m->switch_root_init; - m->switch_root = m->switch_root_init = NULL; - - if (!switch_root_init) - if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) { - error_message = "Failed to prepare for reexecution"; - goto finish; - } - - reexecute = true; - log_notice("Switching root."); - goto finish; - - case MANAGER_EXIT: - retval = m->return_value; - - if (MANAGER_IS_USER(m)) { - log_debug("Exit."); - goto finish; - } - - /* fallthrough */ - case MANAGER_REBOOT: - case MANAGER_POWEROFF: - case MANAGER_HALT: - case MANAGER_KEXEC: { - static const char * const table[_MANAGER_EXIT_CODE_MAX] = { - [MANAGER_EXIT] = "exit", - [MANAGER_REBOOT] = "reboot", - [MANAGER_POWEROFF] = "poweroff", - [MANAGER_HALT] = "halt", - [MANAGER_KEXEC] = "kexec" - }; - - assert_se(shutdown_verb = table[m->exit_code]); - arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT; - - log_notice("Shutting down."); - goto finish; - } - - default: - assert_not_reached("Unknown exit code."); - } - } - -finish: - pager_close(); - - if (m) - arg_shutdown_watchdog = m->shutdown_watchdog; - - 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); - 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(); - - if (reexecute) { - const char **args; - unsigned i, args_size; - - /* Close and disarm the watchdog, so that the new - * instance can reinitialize it, but doesn't get - * rebooted while we do that */ - watchdog_close(true); - - /* Reset the RLIMIT_NOFILE to the kernel default, so - * that the new systemd can pass the kernel default to - * its child processes */ - if (saved_rlimit_nofile.rlim_cur > 0) - (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile); - - if (switch_root_dir) { - /* Kill all remaining processes from the - * initrd, but don't wait for them, so that we - * can handle the SIGCHLD for them after - * deserializing. */ - broadcast_signal(SIGTERM, false, true); - - /* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */ - r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE); - if (r < 0) - log_error_errno(r, "Failed to switch root, trying to continue: %m"); - } - - /* Reopen the console */ - (void) make_console_stdio(); - - args_size = MAX(6, argc+1); - args = newa(const char*, args_size); - - if (!switch_root_init) { - char sfd[DECIMAL_STR_MAX(int) + 1]; - - /* First try to spawn ourselves with the right - * path, and with full serialization. We do - * this only if the user didn't specify an - * explicit init to spawn. */ - - assert(arg_serialization); - assert(fds); - - xsprintf(sfd, "%i", fileno(arg_serialization)); - - i = 0; - args[i++] = SYSTEMD_BINARY_PATH; - if (switch_root_dir) - args[i++] = "--switched-root"; - args[i++] = arg_system ? "--system" : "--user"; - args[i++] = "--deserialize"; - args[i++] = sfd; - args[i++] = NULL; - - assert(i <= args_size); - - /* - * 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 - * serialization. We pass the original argv[] and - * envp[]. (Well, modulo the ordering changes due to - * getopt() in argv[], and some cleanups in envp[], - * but let's hope that doesn't matter.) */ - - arg_serialization = safe_fclose(arg_serialization); - fds = fdset_free(fds); - - for (j = 1, i = 1; j < (unsigned) argc; j++) - args[i++] = argv[j]; - args[i++] = NULL; - assert(i <= args_size); - - /* Reenable any blocked signals, especially important - * if we switch from initial ramdisk to init=... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - if (switch_root_init) { - args[0] = switch_root_init; - (void) execv(args[0], (char* const*) args); - log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m"); - } - - args[0] = "/sbin/init"; - (void) execv(args[0], (char* const*) args); - - if (errno == ENOENT) { - log_warning("No /sbin/init, trying fallback"); - - args[0] = "/bin/sh"; - args[1] = NULL; - (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); - fds = fdset_free(fds); - -#ifdef HAVE_VALGRIND_VALGRIND_H - /* If we are PID 1 and running under valgrind, then let's exit - * here explicitly. valgrind will only generate nice output on - * exit(), not on exec(), hence let's do the former not the - * latter here. */ - if (getpid() == 1 && RUNNING_ON_VALGRIND) - return 0; -#endif - - if (shutdown_verb) { - char log_level[DECIMAL_STR_MAX(int) + 1]; - char exit_code[DECIMAL_STR_MAX(uint8_t) + 1]; - const char* command_line[11] = { - SYSTEMD_SHUTDOWN_BINARY_PATH, - shutdown_verb, - "--log-level", log_level, - "--log-target", - }; - unsigned pos = 5; - _cleanup_strv_free_ char **env_block = NULL; - - assert(command_line[pos] == NULL); - env_block = strv_copy(environ); - - 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: - command_line[pos++] = "kmsg"; - break; - - case LOG_TARGET_NULL: - command_line[pos++] = "null"; - break; - - case LOG_TARGET_CONSOLE: - default: - command_line[pos++] = "console"; - break; - }; - - if (log_get_show_color()) - command_line[pos++] = "--log-color"; - - if (log_get_show_location()) - command_line[pos++] = "--log-location"; - - if (streq(shutdown_verb, "exit")) { - command_line[pos++] = "--exit-code"; - command_line[pos++] = exit_code; - xsprintf(exit_code, "%d", retval); - } - - assert(pos < ELEMENTSOF(command_line)); - - if (arm_reboot_watchdog && arg_shutdown_watchdog > 0 && arg_shutdown_watchdog != USEC_INFINITY) { - char *e; - - /* If we reboot let's set the shutdown - * watchdog and tell the shutdown binary to - * repeatedly ping it */ - r = watchdog_set_timeout(&arg_shutdown_watchdog); - watchdog_close(r < 0); - - /* Tell the binary how often to ping, ignore failure */ - if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0) - (void) strv_push(&env_block, e); - } else - watchdog_close(true); - - /* Avoid the creation of new processes forked by the - * kernel; at this point, we will not listen to the - * signals anyway */ - if (detect_container() <= 0) - (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); - - execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); - log_error_errno(errno, "Failed to execute shutdown binary, %s: %m", - getpid() == 1 ? "freezing" : "quitting"); - } - - if (getpid() == 1) { - if (error_message) - manager_status_printf(NULL, STATUS_TYPE_EMERGENCY, - ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL, - "%s, freezing.", error_message); - freeze_or_reboot(); - } - - return retval; -} diff --git a/src/core/manager.c b/src/core/manager.c deleted file mode 100644 index 4d84a0b37e..0000000000 --- a/src/core/manager.c +++ /dev/null @@ -1,3141 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_AUDIT -#include -#endif - -#include "sd-daemon.h" -#include "sd-messages.h" - -#include "alloc-util.h" -#include "audit-fd.h" -#include "boot-timestamps.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "dbus-job.h" -#include "dbus-manager.h" -#include "dbus-unit.h" -#include "dbus.h" -#include "dirent-util.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) -#define CGROUPS_AGENT_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) -#define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3) -#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 - -static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); -static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); -static int manager_run_generators(Manager *m); - -static void manager_watch_jobs_in_progress(Manager *m) { - usec_t next; - int r; - - assert(m); - - if (m->jobs_in_progress_event_source) - return; - - next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC; - r = sd_event_add_time( - m->event, - &m->jobs_in_progress_event_source, - CLOCK_MONOTONIC, - next, 0, - manager_dispatch_jobs_in_progress, m); - if (r < 0) - return; - - (void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress"); -} - -#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1)) - -static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) { - char *p = buffer; - - assert(buflen >= CYLON_BUFFER_EXTRA + width + 1); - assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */ - - if (pos > 1) { - if (pos > 2) - p = mempset(p, ' ', pos-2); - if (log_get_show_color()) - p = stpcpy(p, ANSI_RED); - *p++ = '*'; - } - - if (pos > 0 && pos <= width) { - if (log_get_show_color()) - p = stpcpy(p, ANSI_HIGHLIGHT_RED); - *p++ = '*'; - } - - if (log_get_show_color()) - p = stpcpy(p, ANSI_NORMAL); - - if (pos < width) { - if (log_get_show_color()) - p = stpcpy(p, ANSI_RED); - *p++ = '*'; - if (pos < width-1) - p = mempset(p, ' ', width-1-pos); - if (log_get_show_color()) - strcpy(p, ANSI_NORMAL); - } -} - -void manager_flip_auto_status(Manager *m, bool enable) { - assert(m); - - if (enable) { - if (m->show_status == SHOW_STATUS_AUTO) - manager_set_show_status(m, SHOW_STATUS_TEMPORARY); - } else { - if (m->show_status == SHOW_STATUS_TEMPORARY) - manager_set_show_status(m, SHOW_STATUS_AUTO); - } -} - -static void manager_print_jobs_in_progress(Manager *m) { - _cleanup_free_ char *job_of_n = NULL; - Iterator i; - Job *j; - unsigned counter = 0, print_nr; - char cylon[6 + CYLON_BUFFER_EXTRA + 1]; - unsigned cylon_pos; - char time[FORMAT_TIMESPAN_MAX], limit[FORMAT_TIMESPAN_MAX] = "no limit"; - uint64_t x; - - assert(m); - assert(m->n_running_jobs > 0); - - manager_flip_auto_status(m, true); - - print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs; - - HASHMAP_FOREACH(j, m->jobs, i) - if (j->state == JOB_RUNNING && counter++ == print_nr) - break; - - /* m->n_running_jobs must be consistent with the contents of m->jobs, - * so the above loop must have succeeded in finding j. */ - assert(counter == print_nr + 1); - assert(j); - - cylon_pos = m->jobs_in_progress_iteration % 14; - if (cylon_pos >= 8) - cylon_pos = 14 - cylon_pos; - draw_cylon(cylon, sizeof(cylon), 6, cylon_pos); - - m->jobs_in_progress_iteration++; - - if (m->n_running_jobs > 1) { - if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0) - job_of_n = NULL; - } - - format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC); - if (job_get_timeout(j, &x) > 0) - format_timespan(limit, sizeof(limit), x - j->begin_usec, 1*USEC_PER_SEC); - - manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon, - "%sA %s job is running for %s (%s / %s)", - strempty(job_of_n), - job_type_to_string(j->type), - unit_description(j->unit), - time, limit); -} - -static int have_ask_password(void) { - _cleanup_closedir_ DIR *dir; - - dir = opendir("/run/systemd/ask-password"); - if (!dir) { - if (errno == ENOENT) - return false; - else - return -errno; - } - - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(dir); - if (!de && errno > 0) - return -errno; - if (!de) - return false; - - if (startswith(de->d_name, "ask.")) - return true; - } -} - -static int manager_dispatch_ask_password_fd(sd_event_source *source, - int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - - assert(m); - - flush_fd(fd); - - m->have_ask_password = have_ask_password(); - if (m->have_ask_password < 0) - /* Log error but continue. Negative have_ask_password - * is treated as unknown status. */ - log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m"); - - return 0; -} - -static void manager_close_ask_password(Manager *m) { - assert(m); - - m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source); - m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); - m->have_ask_password = -EINVAL; -} - -static int manager_check_ask_password(Manager *m) { - int r; - - assert(m); - - if (!m->ask_password_event_source) { - assert(m->ask_password_inotify_fd < 0); - - mkdir_p_label("/run/systemd/ask-password", 0755); - - m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->ask_password_inotify_fd < 0) - return log_error_errno(errno, "inotify_init1() failed: %m"); - - if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) { - log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m"); - manager_close_ask_password(m); - return -errno; - } - - r = sd_event_add_io(m->event, &m->ask_password_event_source, - m->ask_password_inotify_fd, EPOLLIN, - manager_dispatch_ask_password_fd, m); - if (r < 0) { - log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m"); - manager_close_ask_password(m); - return -errno; - } - - (void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password"); - - /* Queries might have been added meanwhile... */ - manager_dispatch_ask_password_fd(m->ask_password_event_source, - m->ask_password_inotify_fd, EPOLLIN, m); - } - - return m->have_ask_password; -} - -static int manager_watch_idle_pipe(Manager *m) { - int r; - - assert(m); - - if (m->idle_pipe_event_source) - return 0; - - if (m->idle_pipe[2] < 0) - return 0; - - r = sd_event_add_io(m->event, &m->idle_pipe_event_source, m->idle_pipe[2], EPOLLIN, manager_dispatch_idle_pipe_fd, m); - if (r < 0) - return log_error_errno(r, "Failed to watch idle pipe: %m"); - - (void) sd_event_source_set_description(m->idle_pipe_event_source, "manager-idle-pipe"); - - return 0; -} - -static void manager_close_idle_pipe(Manager *m) { - assert(m); - - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); - - safe_close_pair(m->idle_pipe); - safe_close_pair(m->idle_pipe + 2); -} - -static int manager_setup_time_change(Manager *m) { - int r; - - /* We only care for the cancellation event, hence we set the - * timeout to the latest possible value. */ - struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - - assert(m); - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - - if (m->test_run) - return 0; - - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever - * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ - - m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (m->time_change_fd < 0) - return log_error_errno(errno, "Failed to create timerfd: %m"); - - if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { - log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); - m->time_change_fd = safe_close(m->time_change_fd); - return 0; - } - - r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); - if (r < 0) - return log_error_errno(r, "Failed to create time change event source: %m"); - - (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change"); - - log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); - - return 0; -} - -static int enable_special_signals(Manager *m) { - _cleanup_close_ int fd = -1; - - 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. */ - if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL) - log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m"); - - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - /* Support systems without virtual console */ - if (fd != -ENOENT) - log_warning_errno(errno, "Failed to open /dev/tty0: %m"); - } else { - /* Enable that we get SIGWINCH on kbrequest */ - if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0) - log_warning_errno(errno, "Failed to enable kbrequest handling: %m"); - } - - return 0; -} - -static int manager_setup_signals(Manager *m) { - struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_NOCLDSTOP|SA_RESTART, - }; - sigset_t mask; - int r; - - assert(m); - - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); - - /* We make liberal use of realtime signals here. On - * Linux/glibc we have 30 of them (with the exception of Linux - * on hppa, see below), between SIGRTMIN+0 ... SIGRTMIN+30 - * (aka SIGRTMAX). */ - - assert_se(sigemptyset(&mask) == 0); - sigset_add_many(&mask, - SIGCHLD, /* Child died */ - SIGTERM, /* Reexecute daemon */ - SIGHUP, /* Reload configuration */ - SIGUSR1, /* systemd/upstart: reconnect to D-Bus */ - SIGUSR2, /* systemd: dump status */ - SIGINT, /* Kernel sends us this on control-alt-del */ - SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */ - SIGPWR, /* Some kernel drivers and upsd send us this on power failure */ - - SIGRTMIN+0, /* systemd: start default.target */ - SIGRTMIN+1, /* systemd: isolate rescue.target */ - SIGRTMIN+2, /* systemd: isolate emergency.target */ - SIGRTMIN+3, /* systemd: start halt.target */ - SIGRTMIN+4, /* systemd: start poweroff.target */ - SIGRTMIN+5, /* systemd: start reboot.target */ - SIGRTMIN+6, /* systemd: start kexec.target */ - - /* ... space for more special targets ... */ - - SIGRTMIN+13, /* systemd: Immediate halt */ - SIGRTMIN+14, /* systemd: Immediate poweroff */ - SIGRTMIN+15, /* systemd: Immediate reboot */ - SIGRTMIN+16, /* systemd: Immediate kexec */ - - /* ... space for more immediate system state changes ... */ - - SIGRTMIN+20, /* systemd: enable status messages */ - SIGRTMIN+21, /* systemd: disable status messages */ - SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ - SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ - SIGRTMIN+24, /* systemd: Immediate exit (--user only) */ - - /* .. one free signal here ... */ - -#if !defined(__hppa64__) && !defined(__hppa__) - /* Apparently Linux on hppa has fewer RT - * signals (SIGRTMAX is SIGRTMIN+25 there), - * hence let's not try to make use of them - * here. Since these commands are accessible - * by different means and only really a safety - * net, the missing functionality on hppa - * shouldn't matter. */ - - SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ - SIGRTMIN+27, /* systemd: set log target to console */ - SIGRTMIN+28, /* systemd: set log target to kmsg */ - SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */ - - /* ... one free signal here SIGRTMIN+30 ... */ -#endif - -1); - assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); - - m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (m->signal_fd < 0) - return -errno; - - r = sd_event_add_io(m->event, &m->signal_event_source, m->signal_fd, EPOLLIN, manager_dispatch_signal_fd, m); - if (r < 0) - return r; - - (void) sd_event_source_set_description(m->signal_event_source, "manager-signal"); - - /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the - * notify processing can still figure out to which process/service a message belongs, before we reap the - * process. Also, process this before handling cgroup notifications, so that we always collect child exit - * status information before detecting that there's no process in a cgroup. */ - r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); - if (r < 0) - return r; - - if (MANAGER_IS_SYSTEM(m)) - return enable_special_signals(m); - - return 0; -} - -static void manager_clean_environment(Manager *m) { - assert(m); - - /* Let's remove some environment variables that we - * need ourselves to communicate with our clients */ - strv_env_unset_many( - m->environment, - "NOTIFY_SOCKET", - "MAINPID", - "MANAGERPID", - "LISTEN_PID", - "LISTEN_FDS", - "LISTEN_FDNAMES", - "WATCHDOG_PID", - "WATCHDOG_USEC", - NULL); -} - -static int manager_default_environment(Manager *m) { - assert(m); - - if (MANAGER_IS_SYSTEM(m)) { - /* The system manager always starts with a clean - * environment for its children. It does not import - * the kernel or the parents exported variables. - * - * The initial passed environ is untouched to keep - * /proc/self/environ valid; it is used for tagging - * the init process inside containers. */ - m->environment = strv_new("PATH=" DEFAULT_PATH, - NULL); - - /* Import locale variables LC_*= from configuration */ - locale_setup(&m->environment); - } else { - /* The user manager passes its own environment - * along to its children. */ - m->environment = strv_copy(environ); - } - - if (!m->environment) - return -ENOMEM; - - manager_clean_environment(m); - strv_sort(m->environment); - - return 0; -} - - -int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { - Manager *m; - int r; - - assert(_m); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - m->unit_file_scope = scope; - 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_MAX; - -#ifdef ENABLE_EFI - if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) - boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); -#endif - - /* Prepare log fields we can use for structured logging */ - if (MANAGER_IS_SYSTEM(m)) { - m->unit_log_field = "UNIT="; - m->unit_log_format_string = "UNIT=%s"; - } else { - m->unit_log_field = "USER_UNIT="; - m->unit_log_format_string = "USER_UNIT=%s"; - } - - m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - - m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = - m->ask_password_inotify_fd = -1; - - m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ - - m->have_ask_password = -EINVAL; /* we don't know */ - m->first_boot = -1; - - m->test_run = test_run; - - /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ - RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7); - - r = manager_default_environment(m); - if (r < 0) - goto fail; - - r = hashmap_ensure_allocated(&m->units, &string_hash_ops); - if (r < 0) - goto fail; - - r = hashmap_ensure_allocated(&m->jobs, NULL); - if (r < 0) - goto fail; - - r = hashmap_ensure_allocated(&m->cgroup_unit, &string_hash_ops); - if (r < 0) - goto fail; - - r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops); - if (r < 0) - goto fail; - - r = sd_event_default(&m->event); - if (r < 0) - goto fail; - - r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE); - if (r < 0) - goto fail; - - r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue"); - - r = manager_setup_signals(m); - if (r < 0) - goto fail; - - r = manager_setup_cgroup(m); - if (r < 0) - goto fail; - - r = manager_setup_time_change(m); - if (r < 0) - goto fail; - - m->udev = udev_new(); - if (!m->udev) { - r = -ENOMEM; - goto fail; - } - - /* Note that we set up neither kdbus, nor the notify fd - * here. We do that after deserialization, since they might - * have gotten serialized across the reexec. */ - - m->taint_usr = dir_is_empty("/usr") > 0; - - *_m = m; - return 0; - -fail: - manager_free(m); - return r; -} - -static int manager_setup_notify(Manager *m) { - int r; - - if (m->test_run) - return 0; - - if (m->notify_fd < 0) { - _cleanup_close_ int fd = -1; - union sockaddr_union sa = { - .sa.sa_family = AF_UNIX, - }; - static const int one = 1; - const char *e; - - /* First free all secondary fields */ - m->notify_socket = mfree(m->notify_socket); - m->notify_event_source = sd_event_source_unref(m->notify_event_source); - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate notification socket: %m"); - - fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); - - e = manager_get_runtime_prefix(m); - if (!e) { - log_error("Failed to determine runtime prefix."); - return -EINVAL; - } - - m->notify_socket = strappend(e, "/systemd/notify"); - if (!m->notify_socket) - return log_oom(); - - (void) mkdir_parents_label(m->notify_socket, 0755); - (void) unlink(m->notify_socket); - - strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); - r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - - r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "SO_PASSCRED failed: %m"); - - m->notify_fd = fd; - fd = -1; - - log_debug("Using notification socket %s", m->notify_socket); - } - - if (!m->notify_event_source) { - r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_dispatch_notify_fd, m); - if (r < 0) - return log_error_errno(r, "Failed to allocate notify event source: %m"); - - /* Process notification messages 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, SD_EVENT_PRIORITY_NORMAL-7); - if (r < 0) - return log_error_errno(r, "Failed to set priority of notify event source: %m"); - - (void) sd_event_source_set_description(m->notify_event_source, "manager-notify"); - } - - return 0; -} - -static int manager_setup_cgroups_agent(Manager *m) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/cgroups-agent", - }; - int r; - - /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering - * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and - * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on - * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number - * of D-Bus connections may be queued until the kernel will start dropping further incoming connections, - * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX - * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and - * we thus won't lose messages. - * - * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen - * to it. The system instance hence listens on this special socket, but the user instances listen on the system - * bus for these messages. */ - - if (m->test_run) - return 0; - - if (!MANAGER_IS_SYSTEM(m)) - return 0; - - if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */ - return 0; - - if (m->cgroups_agent_fd < 0) { - _cleanup_close_ int fd = -1; - - /* First free all secondary fields */ - m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m"); - - fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE); - - (void) unlink(sa.un.sun_path); - - /* Only allow root to connect to this socket */ - RUN_WITH_UMASK(0077) - r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - - m->cgroups_agent_fd = fd; - fd = -1; - } - - if (!m->cgroups_agent_event_source) { - r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m); - if (r < 0) - return log_error_errno(r, "Failed to allocate cgroups agent event source: %m"); - - /* Process cgroups notifications early, but after having processed service notification messages or - * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification, - * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of - * cgroup inotify for the unified cgroup stuff. */ - r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5); - if (r < 0) - return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); - - (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent"); - } - - return 0; -} - -static int manager_connect_bus(Manager *m, bool reexecuting) { - bool try_bus_connect; - - assert(m); - - if (m->test_run) - return 0; - - try_bus_connect = - m->kdbus_fd >= 0 || - reexecuting || - (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")); - - /* Try to connect to the buses, if possible. */ - return bus_init(m, try_bus_connect); -} - -static unsigned manager_dispatch_cleanup_queue(Manager *m) { - Unit *u; - unsigned n = 0; - - assert(m); - - while ((u = m->cleanup_queue)) { - assert(u->in_cleanup_queue); - - unit_free(u); - n++; - } - - return n; -} - -enum { - GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */ - GC_OFFSET_UNSURE, /* No clue */ - GC_OFFSET_GOOD, /* We still need this unit */ - GC_OFFSET_BAD, /* We don't need this unit anymore */ - _GC_OFFSET_MAX -}; - -static void unit_gc_mark_good(Unit *u, unsigned gc_marker) -{ - Iterator i; - Unit *other; - - u->gc_marker = gc_marker + GC_OFFSET_GOOD; - - /* Recursively mark referenced units as GOOD as well */ - SET_FOREACH(other, u->dependencies[UNIT_REFERENCES], i) - if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE) - unit_gc_mark_good(other, gc_marker); -} - -static void unit_gc_sweep(Unit *u, unsigned gc_marker) { - Iterator i; - Unit *other; - bool is_bad; - - assert(u); - - if (u->gc_marker == gc_marker + GC_OFFSET_GOOD || - u->gc_marker == gc_marker + GC_OFFSET_BAD || - u->gc_marker == gc_marker + GC_OFFSET_UNSURE || - u->gc_marker == gc_marker + GC_OFFSET_IN_PATH) - return; - - if (u->in_cleanup_queue) - goto bad; - - if (unit_check_gc(u)) - goto good; - - u->gc_marker = gc_marker + GC_OFFSET_IN_PATH; - - is_bad = true; - - SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) { - unit_gc_sweep(other, gc_marker); - - if (other->gc_marker == gc_marker + GC_OFFSET_GOOD) - goto good; - - if (other->gc_marker != gc_marker + GC_OFFSET_BAD) - is_bad = false; - } - - if (is_bad) - goto bad; - - /* We were unable to find anything out about this entry, so - * let's investigate it later */ - u->gc_marker = gc_marker + GC_OFFSET_UNSURE; - unit_add_to_gc_queue(u); - return; - -bad: - /* We definitely know that this one is not useful anymore, so - * let's mark it for deletion */ - u->gc_marker = gc_marker + GC_OFFSET_BAD; - unit_add_to_cleanup_queue(u); - return; - -good: - unit_gc_mark_good(u, gc_marker); -} - -static unsigned manager_dispatch_gc_queue(Manager *m) { - Unit *u; - unsigned n = 0; - unsigned gc_marker; - - assert(m); - - /* log_debug("Running GC..."); */ - - m->gc_marker += _GC_OFFSET_MAX; - if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX) - m->gc_marker = 1; - - gc_marker = m->gc_marker; - - while ((u = m->gc_queue)) { - assert(u->in_gc_queue); - - unit_gc_sweep(u, gc_marker); - - LIST_REMOVE(gc_queue, m->gc_queue, u); - u->in_gc_queue = false; - - n++; - - if (u->gc_marker == gc_marker + GC_OFFSET_BAD || - u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - if (u->id) - log_unit_debug(u, "Collecting."); - u->gc_marker = gc_marker + GC_OFFSET_BAD; - unit_add_to_cleanup_queue(u); - } - } - - m->n_in_gc_queue = 0; - - return n; -} - -static void manager_clear_jobs_and_units(Manager *m) { - Unit *u; - - assert(m); - - while ((u = hashmap_first(m->units))) - unit_free(u); - - manager_dispatch_cleanup_queue(m); - - assert(!m->load_queue); - assert(!m->run_queue); - assert(!m->dbus_unit_queue); - assert(!m->dbus_job_queue); - assert(!m->cleanup_queue); - assert(!m->gc_queue); - - assert(hashmap_isempty(m->jobs)); - assert(hashmap_isempty(m->units)); - - m->n_on_console = 0; - m->n_running_jobs = 0; -} - -Manager* manager_free(Manager *m) { - UnitType c; - int i; - - if (!m) - return NULL; - - manager_clear_jobs_and_units(m); - - for (c = 0; c < _UNIT_TYPE_MAX; c++) - if (unit_vtable[c]->shutdown) - unit_vtable[c]->shutdown(m); - - /* If we reexecute ourselves, we keep the root cgroup - * around */ - manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); - - lookup_paths_flush_generator(&m->lookup_paths); - - bus_done(m); - - hashmap_free(m->units); - hashmap_free(m->jobs); - hashmap_free(m->watch_pids1); - hashmap_free(m->watch_pids2); - hashmap_free(m->watch_bus); - - set_free(m->startup_units); - set_free(m->failed_units); - - sd_event_source_unref(m->signal_event_source); - sd_event_source_unref(m->notify_event_source); - sd_event_source_unref(m->cgroups_agent_event_source); - sd_event_source_unref(m->time_change_event_source); - sd_event_source_unref(m->jobs_in_progress_event_source); - sd_event_source_unref(m->run_queue_event_source); - - safe_close(m->signal_fd); - safe_close(m->notify_fd); - safe_close(m->cgroups_agent_fd); - safe_close(m->time_change_fd); - safe_close(m->kdbus_fd); - - manager_close_ask_password(m); - - manager_close_idle_pipe(m); - - udev_unref(m->udev); - sd_event_unref(m->event); - - free(m->notify_socket); - - lookup_paths_free(&m->lookup_paths); - strv_free(m->environment); - - hashmap_free(m->cgroup_unit); - set_free_free(m->unit_path_cache); - - free(m->switch_root); - free(m->switch_root_init); - - for (i = 0; i < _RLIMIT_MAX; i++) - m->rlimit[i] = mfree(m->rlimit[i]); - - assert(hashmap_isempty(m->units_requiring_mounts_for)); - hashmap_free(m->units_requiring_mounts_for); - - free(m); - return NULL; -} - -void manager_enumerate(Manager *m) { - UnitType c; - - assert(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++) { - if (!unit_type_supported(c)) { - log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); - continue; - } - - if (!unit_vtable[c]->enumerate) - continue; - - unit_vtable[c]->enumerate(m); - } - - manager_dispatch_load_queue(m); -} - -static void manager_coldplug(Manager *m) { - Iterator i; - Unit *u; - char *k; - int r; - - assert(m); - - /* Then, let's set up their initial state. */ - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - - /* ignore aliases */ - if (u->id != k) - continue; - - r = unit_coldplug(u); - if (r < 0) - log_warning_errno(r, "We couldn't coldplug %s, proceeding anyway: %m", u->id); - } -} - -static void manager_build_unit_path_cache(Manager *m) { - char **i; - int r; - - assert(m); - - set_free_free(m->unit_path_cache); - - m->unit_path_cache = set_new(&string_hash_ops); - if (!m->unit_path_cache) { - r = -ENOMEM; - goto fail; - } - - /* This simply builds a list of files we know exist, so that - * we don't always have to go to disk */ - - STRV_FOREACH(i, m->lookup_paths.search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*i); - if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i); - continue; - } - - FOREACH_DIRENT(de, d, r = -errno; goto fail) { - char *p; - - p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); - if (!p) { - r = -ENOMEM; - goto fail; - } - - r = set_consume(m->unit_path_cache, p); - if (r < 0) - goto fail; - } - } - - return; - -fail: - log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m"); - m->unit_path_cache = set_free_free(m->unit_path_cache); -} - -static void manager_distribute_fds(Manager *m, FDSet *fds) { - Iterator i; - Unit *u; - - assert(m); - - HASHMAP_FOREACH(u, m->units, i) { - - if (fdset_size(fds) <= 0) - break; - - if (!UNIT_VTABLE(u)->distribute_fds) - continue; - - UNIT_VTABLE(u)->distribute_fds(u, fds); - } -} - -int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { - int r, q; - - assert(m); - - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); - if (r < 0) - return r; - - /* Make sure the transient directory always exists, so that it remains in the search path */ - r = mkdir_p_label(m->lookup_paths.transient, 0755); - if (r < 0) - return r; - - dual_timestamp_get(&m->generators_start_timestamp); - r = manager_run_generators(m); - dual_timestamp_get(&m->generators_finish_timestamp); - if (r < 0) - return r; - - lookup_paths_reduce(&m->lookup_paths); - manager_build_unit_path_cache(m); - - /* If we will deserialize make sure that during enumeration - * this is already known, so we increase the counter here - * already */ - if (serialization) - m->n_reloading++; - - /* First, enumerate what we can from all config files */ - dual_timestamp_get(&m->units_load_start_timestamp); - manager_enumerate(m); - dual_timestamp_get(&m->units_load_finish_timestamp); - - /* Second, deserialize if there is something to deserialize */ - if (serialization) - r = manager_deserialize(m, serialization, fds); - - /* Any fds left? Find some unit which wants them. This is - * useful to allow container managers to pass some file - * descriptors to us pre-initialized. This enables - * socket-based activation of entire containers. */ - manager_distribute_fds(m, fds); - - /* We might have deserialized the notify fd, but if we didn't - * then let's create the bus now */ - q = manager_setup_notify(m); - if (q < 0 && r == 0) - r = q; - - q = manager_setup_cgroups_agent(m); - if (q < 0 && r == 0) - r = q; - - /* We might have deserialized the kdbus control fd, but if we - * didn't, then let's create the bus now. */ - manager_connect_bus(m, !!serialization); - bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed); - - /* Third, fire things up! */ - manager_coldplug(m); - - if (serialization) { - assert(m->n_reloading > 0); - m->n_reloading--; - - /* Let's wait for the UnitNew/JobNew messages being - * sent, before we notify that the reload is - * finished */ - m->send_reloading_done = true; - } - - return r; -} - -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { - int r; - Transaction *tr; - - assert(m); - assert(type < _JOB_TYPE_MAX); - assert(unit); - assert(mode < _JOB_MODE_MAX); - - if (mode == JOB_ISOLATE && type != JOB_START) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); - - if (mode == JOB_ISOLATE && !unit->allow_isolate) - return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); - - log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); - - type = job_type_collapse(type, unit); - - tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY); - if (!tr) - return -ENOMEM; - - 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) - goto tr_abort; - - if (mode == JOB_ISOLATE) { - r = transaction_add_isolate_jobs(tr, m); - if (r < 0) - goto tr_abort; - } - - r = transaction_activate(tr, m, mode, e); - if (r < 0) - goto tr_abort; - - log_unit_debug(unit, - "Enqueued job %s/%s as %u", unit->id, - job_type_to_string(type), (unsigned) tr->anchor_job->id); - - if (_ret) - *_ret = tr->anchor_job; - - transaction_free(tr); - return 0; - -tr_abort: - transaction_abort(tr); - transaction_free(tr); - return r; -} - -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; - - assert(m); - assert(type < _JOB_TYPE_MAX); - assert(name); - assert(mode < _JOB_MODE_MAX); - - r = manager_load_unit(m, name, NULL, NULL, &unit); - if (r < 0) - return r; - - 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) { - assert(m); - - return hashmap_get(m->jobs, UINT32_TO_PTR(id)); -} - -Unit *manager_get_unit(Manager *m, const char *name) { - assert(m); - assert(name); - - return hashmap_get(m->units, name); -} - -unsigned manager_dispatch_load_queue(Manager *m) { - Unit *u; - unsigned n = 0; - - assert(m); - - /* Make sure we are not run recursively */ - if (m->dispatching_load_queue) - return 0; - - m->dispatching_load_queue = true; - - /* Dispatches the load queue. Takes a unit from the queue and - * tries to load its data until the queue is empty */ - - while ((u = m->load_queue)) { - assert(u->in_load_queue); - - unit_load(u); - n++; - } - - m->dispatching_load_queue = false; - return n; -} - -int manager_load_unit_prepare( - Manager *m, - const char *name, - const char *path, - sd_bus_error *e, - Unit **_ret) { - - Unit *ret; - UnitType t; - int r; - - assert(m); - assert(name || path); - - /* This will prepare the unit for loading, but not actually - * load anything from disk. */ - - if (path && !is_path(path)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); - - if (!name) - name = basename(path); - - t = unit_name_to_type(name); - - if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is missing the instance name.", name); - - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); - } - - ret = manager_get_unit(m, name); - if (ret) { - *_ret = ret; - return 1; - } - - ret = unit_new(m, unit_vtable[t]->object_size); - if (!ret) - return -ENOMEM; - - if (path) { - ret->fragment_path = strdup(path); - if (!ret->fragment_path) { - unit_free(ret); - return -ENOMEM; - } - } - - r = unit_add_name(ret, name); - if (r < 0) { - unit_free(ret); - return r; - } - - unit_add_to_load_queue(ret); - unit_add_to_dbus_queue(ret); - unit_add_to_gc_queue(ret); - - if (_ret) - *_ret = ret; - - return 0; -} - -int manager_load_unit( - Manager *m, - const char *name, - const char *path, - sd_bus_error *e, - Unit **_ret) { - - int r; - - assert(m); - - /* This will load the service information files, but not actually - * start any services or anything. */ - - r = manager_load_unit_prepare(m, name, path, e, _ret); - if (r != 0) - return r; - - manager_dispatch_load_queue(m); - - if (_ret) - *_ret = unit_follow_merge(*_ret); - - return 0; -} - -void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { - Iterator i; - Job *j; - - assert(s); - assert(f); - - HASHMAP_FOREACH(j, s->jobs, i) - job_dump(j, f, prefix); -} - -void manager_dump_units(Manager *s, FILE *f, const char *prefix) { - Iterator i; - Unit *u; - const char *t; - - assert(s); - assert(f); - - HASHMAP_FOREACH_KEY(u, t, s->units, i) - if (u->id == t) - unit_dump(u, f, prefix); -} - -void manager_clear_jobs(Manager *m) { - Job *j; - - assert(m); - - while ((j = hashmap_first(m->jobs))) - /* No need to recurse. We're cancelling all jobs. */ - job_finish_and_invalidate(j, JOB_CANCELED, false, false); -} - -static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { - Manager *m = userdata; - Job *j; - - assert(source); - assert(m); - - while ((j = m->run_queue)) { - assert(j->installed); - assert(j->in_run_queue); - - job_run_and_invalidate(j); - } - - if (m->n_running_jobs > 0) - manager_watch_jobs_in_progress(m); - - if (m->n_on_console > 0) - manager_watch_idle_pipe(m); - - return 1; -} - -static unsigned manager_dispatch_dbus_queue(Manager *m) { - Job *j; - Unit *u; - unsigned n = 0; - - assert(m); - - if (m->dispatching_dbus_queue) - return 0; - - m->dispatching_dbus_queue = true; - - while ((u = m->dbus_unit_queue)) { - assert(u->in_dbus_queue); - - bus_unit_send_change_signal(u); - n++; - } - - while ((j = m->dbus_job_queue)) { - assert(j->in_dbus_queue); - - bus_job_send_change_signal(j); - n++; - } - - m->dispatching_dbus_queue = false; - - if (m->send_reloading_done) { - m->send_reloading_done = false; - - bus_manager_send_reloading(m, false); - } - - if (m->queued_message) - bus_send_queued_message(m); - - return n; -} - -static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - char buf[PATH_MAX+1]; - ssize_t n; - - n = recv(fd, buf, sizeof(buf), 0); - if (n < 0) - return log_error_errno(errno, "Failed to read cgroups agent message: %m"); - if (n == 0) { - log_error("Got zero-length cgroups agent message, ignoring."); - return 0; - } - if ((size_t) n >= sizeof(buf)) { - log_error("Got overly long cgroups agent message, ignoring."); - return 0; - } - - if (memchr(buf, 0, n)) { - log_error("Got cgroups agent message with embedded NUL byte, ignoring."); - return 0; - } - buf[n] = 0; - - manager_notify_cgroup_empty(m, buf); - bus_forward_agent_released(m, buf); - - return 0; -} - -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); - assert(u); - assert(buf); - assert(n > 0); - - tags = strv_split(buf, "\n\r"); - if (!tags) { - log_oom(); - return; - } - - if (UNIT_VTABLE(u)->notify_message) - UNIT_VTABLE(u)->notify_message(u, pid, tags, fds); - else - log_unit_debug(u, "Got notification message for unit. Ignoring."); -} - -static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - - _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; - - assert(m); - assert(m->notify_fd == fd); - - if (revents != EPOLLIN) { - log_warning("Got unexpected poll event for notify fd."); - return 0; - } - - n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - 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); - - } 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); - } - } - - 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(); - } - } - - 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."); - return 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; - } - - 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; - } - - 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."); - - return 0; -} - -static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { - uint64_t iteration; - - assert(m); - assert(u); - assert(si); - - sd_event_get_iteration(m->event, &iteration); - - log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); - - unit_unwatch_pid(u, si->si_pid); - - if (UNIT_VTABLE(u)->sigchld_event) { - if (set_size(u->pids) <= 1 || - iteration != u->sigchldgen || - unit_main_pid(u) == si->si_pid || - unit_control_pid(u) == si->si_pid) { - UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); - u->sigchldgen = iteration; - } else - log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids)); - } -} - -static int manager_dispatch_sigchld(Manager *m) { - assert(m); - - for (;;) { - siginfo_t si = {}; - - /* First we call waitd() for a PID and do not reap the - * zombie. That way we can still access /proc/$PID for - * it while it is a zombie. */ - if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) { - - if (errno == ECHILD) - break; - - if (errno == EINTR) - continue; - - return -errno; - } - - if (si.si_pid <= 0) - break; - - if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { - _cleanup_free_ char *name = NULL; - Unit *u1, *u2, *u3; - - get_process_comm(si.si_pid, &name); - - log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)", - si.si_pid, strna(name), - sigchld_code_to_string(si.si_code), - si.si_status, - strna(si.si_code == CLD_EXITED - ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL) - : signal_to_string(si.si_status))); - - /* And now figure out the unit this belongs - * to, it might be multiple... */ - u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid); - if (u1) - invoke_sigchld_event(m, u1, &si); - u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid)); - if (u2 && u2 != u1) - invoke_sigchld_event(m, u2, &si); - u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid)); - if (u3 && u3 != u2 && u3 != u1) - invoke_sigchld_event(m, u3, &si); - } - - /* And now, we actually reap the zombie. */ - if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { - if (errno == EINTR) - continue; - - return -errno; - } - } - - return 0; -} - -static int manager_start_target(Manager *m, const char *name, JobMode mode) { - _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, &error, NULL); - if (r < 0) - log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); - - return r; -} - -static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - ssize_t n; - struct signalfd_siginfo sfsi; - bool sigchld = false; - int r; - - assert(m); - assert(m->signal_fd == fd); - - if (revents != EPOLLIN) { - log_warning("Got unexpected events from signal file descriptor."); - return 0; - } - - for (;;) { - n = read(m->signal_fd, &sfsi, sizeof(sfsi)); - if (n != sizeof(sfsi)) { - - if (n >= 0) - return -EIO; - - if (errno == EINTR || errno == EAGAIN) - break; - - return -errno; - } - - log_received_signal(sfsi.ssi_signo == SIGCHLD || - (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) - ? LOG_DEBUG : LOG_INFO, - &sfsi); - - switch (sfsi.ssi_signo) { - - case SIGCHLD: - sigchld = true; - break; - - case SIGTERM: - if (MANAGER_IS_SYSTEM(m)) { - /* This is for compatibility with the - * original sysvinit */ - m->exit_code = MANAGER_REEXECUTE; - break; - } - - /* Fall through */ - - case SIGINT: - if (MANAGER_IS_SYSTEM(m)) { - - /* If the user presses C-A-D more than - * 7 times within 2s, we reboot - * immediately. */ - - if (ratelimit_test(&m->ctrl_alt_del_ratelimit)) - manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); - else { - log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); - status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); - m->exit_code = MANAGER_REBOOT; - } - - break; - } - - /* Run the exit target if there is one, if not, just exit. */ - if (manager_start_target(m, SPECIAL_EXIT_TARGET, JOB_REPLACE) < 0) { - m->exit_code = MANAGER_EXIT; - return 0; - } - - break; - - case SIGWINCH: - if (MANAGER_IS_SYSTEM(m)) - manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); - - /* This is a nop on non-init */ - break; - - case SIGPWR: - if (MANAGER_IS_SYSTEM(m)) - manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); - - /* This is a nop on non-init */ - break; - - case SIGUSR1: { - Unit *u; - - u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); - - if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { - log_info("Trying to reconnect to bus..."); - bus_init(m, true); - } - - if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) { - log_info("Loading D-Bus service..."); - manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE); - } - - break; - } - - case SIGUSR2: { - _cleanup_free_ char *dump = NULL; - _cleanup_fclose_ FILE *f = NULL; - size_t size; - - f = open_memstream(&dump, &size); - if (!f) { - log_warning_errno(errno, "Failed to allocate memory stream: %m"); - break; - } - - manager_dump_units(m, f, "\t"); - manager_dump_jobs(m, f, "\t"); - - r = fflush_and_check(f); - if (r < 0) { - log_warning_errno(r, "Failed to write status stream: %m"); - break; - } - - log_dump(LOG_INFO, dump); - break; - } - - case SIGHUP: - m->exit_code = MANAGER_RELOAD; - break; - - default: { - - /* Starting SIGRTMIN+0 */ - static const char * const target_table[] = { - [0] = SPECIAL_DEFAULT_TARGET, - [1] = SPECIAL_RESCUE_TARGET, - [2] = SPECIAL_EMERGENCY_TARGET, - [3] = SPECIAL_HALT_TARGET, - [4] = SPECIAL_POWEROFF_TARGET, - [5] = SPECIAL_REBOOT_TARGET, - [6] = SPECIAL_KEXEC_TARGET - }; - - /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */ - static const ManagerExitCode code_table[] = { - [0] = MANAGER_HALT, - [1] = MANAGER_POWEROFF, - [2] = MANAGER_REBOOT, - [3] = MANAGER_KEXEC - }; - - if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && - (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { - int idx = (int) sfsi.ssi_signo - SIGRTMIN; - manager_start_target(m, target_table[idx], - (idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE); - break; - } - - if ((int) sfsi.ssi_signo >= SIGRTMIN+13 && - (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) { - m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13]; - break; - } - - switch (sfsi.ssi_signo - SIGRTMIN) { - - case 20: - manager_set_show_status(m, SHOW_STATUS_YES); - break; - - case 21: - manager_set_show_status(m, SHOW_STATUS_NO); - break; - - case 22: - log_set_max_level(LOG_DEBUG); - log_info("Setting log level to debug."); - break; - - case 23: - log_set_max_level(LOG_INFO); - log_info("Setting log level to info."); - break; - - case 24: - if (MANAGER_IS_USER(m)) { - m->exit_code = MANAGER_EXIT; - return 0; - } - - /* This is a nop on init */ - break; - - case 26: - case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_notice("Setting log target to journal-or-kmsg."); - break; - - case 27: - log_set_target(LOG_TARGET_CONSOLE); - log_notice("Setting log target to console."); - break; - - case 28: - log_set_target(LOG_TARGET_KMSG); - log_notice("Setting log target to kmsg."); - break; - - default: - log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); - } - } - } - } - - if (sigchld) - manager_dispatch_sigchld(m); - - return 0; -} - -static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - Iterator i; - Unit *u; - - assert(m); - assert(m->time_change_fd == fd); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), - LOG_MESSAGE("Time has been changed"), - NULL); - - /* Restart the watch */ - m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); - m->time_change_fd = safe_close(m->time_change_fd); - - manager_setup_time_change(m); - - HASHMAP_FOREACH(u, m->units, i) - if (UNIT_VTABLE(u)->time_change) - UNIT_VTABLE(u)->time_change(u); - - return 0; -} - -static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - - assert(m); - assert(m->idle_pipe[2] == fd); - - m->no_console_output = m->n_on_console > 0; - - manager_close_idle_pipe(m); - - return 0; -} - -static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata) { - Manager *m = userdata; - int r; - uint64_t next; - - assert(m); - assert(source); - - manager_print_jobs_in_progress(m); - - next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_PERIOD_USEC; - r = sd_event_source_set_time(source, next); - if (r < 0) - return r; - - return sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); -} - -int manager_loop(Manager *m) { - int r; - - RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000); - - assert(m); - m->exit_code = MANAGER_OK; - - /* Release the path cache */ - m->unit_path_cache = set_free_free(m->unit_path_cache); - - manager_check_finished(m); - - /* There might still be some zombies hanging around from - * before we were exec()'ed. Let's reap them. */ - r = manager_dispatch_sigchld(m); - if (r < 0) - return r; - - while (m->exit_code == MANAGER_OK) { - usec_t wait_usec; - - if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) - watchdog_ping(); - - if (!ratelimit_test(&rl)) { - /* Yay, something is going seriously wrong, pause a little */ - log_warning("Looping too fast. Throttling execution a little."); - sleep(1); - } - - if (manager_dispatch_load_queue(m) > 0) - continue; - - if (manager_dispatch_gc_queue(m) > 0) - continue; - - if (manager_dispatch_cleanup_queue(m) > 0) - continue; - - if (manager_dispatch_cgroup_queue(m) > 0) - continue; - - if (manager_dispatch_dbus_queue(m) > 0) - continue; - - /* Sleep for half the watchdog time */ - if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) { - wait_usec = m->runtime_watchdog / 2; - if (wait_usec <= 0) - wait_usec = 1; - } else - wait_usec = USEC_INFINITY; - - r = sd_event_run(m->event, wait_usec); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - } - - return m->exit_code; -} - -int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) { - _cleanup_free_ char *n = NULL; - Unit *u; - int r; - - assert(m); - assert(s); - assert(_u); - - r = unit_name_from_dbus_path(s, &n); - if (r < 0) - return r; - - r = manager_load_unit(m, n, NULL, e, &u); - if (r < 0) - return r; - - *_u = u; - - return 0; -} - -int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { - const char *p; - unsigned id; - Job *j; - int r; - - assert(m); - assert(s); - assert(_j); - - p = startswith(s, "/org/freedesktop/systemd1/job/"); - if (!p) - return -EINVAL; - - r = safe_atou(p, &id); - if (r < 0) - return r; - - j = manager_get_job(m, id); - if (!j) - return -ENOENT; - - *_j = j; - - return 0; -} - -void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { - -#ifdef HAVE_AUDIT - _cleanup_free_ char *p = NULL; - const char *msg; - int audit_fd, r; - - if (!MANAGER_IS_SYSTEM(m)) - return; - - audit_fd = get_audit_fd(); - if (audit_fd < 0) - return; - - /* Don't generate audit events if the service was already - * started and we're just deserializing */ - if (MANAGER_IS_RELOADING(m)) - return; - - if (u->type != UNIT_SERVICE) - return; - - r = unit_name_to_prefix_and_instance(u->id, &p); - if (r < 0) { - log_error_errno(r, "Failed to extract prefix and instance of unit name: %m"); - return; - } - - msg = strjoina("unit=", p); - if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) { - if (errno == EPERM) - /* We aren't allowed to send audit messages? - * Then let's not retry again. */ - close_audit_fd(); - else - log_warning_errno(errno, "Failed to send audit message: %m"); - } -#endif - -} - -void manager_send_unit_plymouth(Manager *m, Unit *u) { - static const union sockaddr_union sa = PLYMOUTH_SOCKET; - _cleanup_free_ char *message = NULL; - _cleanup_close_ int fd = -1; - int n = 0; - - /* Don't generate plymouth events if the service was already - * started and we're just deserializing */ - if (MANAGER_IS_RELOADING(m)) - return; - - if (!MANAGER_IS_SYSTEM(m)) - return; - - if (detect_container() > 0) - return; - - if (u->type != UNIT_SERVICE && - u->type != UNIT_MOUNT && - u->type != UNIT_SWAP) - return; - - /* We set SOCK_NONBLOCK here so that we rather drop the - * message then wait for plymouth */ - fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) { - log_error_errno(errno, "socket() failed: %m"); - return; - } - - if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - - if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) - log_error_errno(errno, "connect() failed: %m"); - return; - } - - if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) { - log_oom(); - return; - } - - errno = 0; - if (write(fd, message, n + 1) != n + 1) - if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) - log_error_errno(errno, "Failed to write Plymouth message: %m"); -} - -int manager_open_serialization(Manager *m, FILE **_f) { - const char *path; - int fd = -1; - FILE *f; - - assert(_f); - - path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; - - log_debug("Serializing state to %s", path); - - f = fdopen(fd, "w+"); - if (!f) { - safe_close(fd); - return -errno; - } - - *_f = f; - - return 0; -} - -int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { - Iterator i; - Unit *u; - const char *t; - char **e; - int r; - - assert(m); - assert(f); - assert(fds); - - m->n_reloading++; - - fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id); - fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); - fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs); - fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs); - - dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp); - dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp); - dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp); - dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp); - - if (!in_initrd()) { - dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp); - dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); - dual_timestamp_serialize(f, "security-start-timestamp", &m->security_start_timestamp); - dual_timestamp_serialize(f, "security-finish-timestamp", &m->security_finish_timestamp); - dual_timestamp_serialize(f, "generators-start-timestamp", &m->generators_start_timestamp); - dual_timestamp_serialize(f, "generators-finish-timestamp", &m->generators_finish_timestamp); - dual_timestamp_serialize(f, "units-load-start-timestamp", &m->units_load_start_timestamp); - dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp); - } - - if (!switching_root) { - STRV_FOREACH(e, m->environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", *e); - } - } - - if (m->notify_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, m->notify_fd); - if (copy < 0) - return copy; - - fprintf(f, "notify-fd=%i\n", copy); - fprintf(f, "notify-socket=%s\n", m->notify_socket); - } - - if (m->cgroups_agent_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, m->cgroups_agent_fd); - if (copy < 0) - return copy; - - fprintf(f, "cgroups-agent-fd=%i\n", copy); - } - - if (m->kdbus_fd >= 0) { - int copy; - - copy = fdset_put_dup(fds, m->kdbus_fd); - if (copy < 0) - return copy; - - fprintf(f, "kdbus-fd=%i\n", copy); - } - - bus_track_serialize(m->subscribed, f); - - fputc('\n', f); - - HASHMAP_FOREACH_KEY(u, t, m->units, i) { - if (u->id != t) - continue; - - /* Start marker */ - fputs(u->id, f); - fputc('\n', f); - - r = unit_serialize(u, f, fds, !switching_root); - if (r < 0) { - m->n_reloading--; - return r; - } - } - - assert(m->n_reloading > 0); - m->n_reloading--; - - if (ferror(f)) - return -EIO; - - r = bus_fdset_add_all(m, fds); - if (r < 0) - return r; - - return 0; -} - -int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { - int r = 0; - - assert(m); - assert(f); - - log_debug("Deserializing state..."); - - m->n_reloading++; - - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - r = 0; - else - r = -errno; - - goto finish; - } - - char_array_0(line); - l = strstrip(line); - - if (l[0] == 0) - break; - - if (startswith(l, "current-job-id=")) { - uint32_t id; - - if (safe_atou32(l+15, &id) < 0) - log_debug("Failed to parse current job id value %s", l+15); - else - m->current_job_id = MAX(m->current_job_id, id); - - } else if (startswith(l, "n-installed-jobs=")) { - uint32_t n; - - if (safe_atou32(l+17, &n) < 0) - log_debug("Failed to parse installed jobs counter %s", l+17); - else - m->n_installed_jobs += n; - - } else if (startswith(l, "n-failed-jobs=")) { - uint32_t n; - - if (safe_atou32(l+14, &n) < 0) - log_debug("Failed to parse failed jobs counter %s", l+14); - else - m->n_failed_jobs += n; - - } else if (startswith(l, "taint-usr=")) { - int b; - - b = parse_boolean(l+10); - if (b < 0) - log_debug("Failed to parse taint /usr flag %s", l+10); - else - m->taint_usr = m->taint_usr || b; - - } else if (startswith(l, "firmware-timestamp=")) - dual_timestamp_deserialize(l+19, &m->firmware_timestamp); - else if (startswith(l, "loader-timestamp=")) - dual_timestamp_deserialize(l+17, &m->loader_timestamp); - else if (startswith(l, "kernel-timestamp=")) - dual_timestamp_deserialize(l+17, &m->kernel_timestamp); - else if (startswith(l, "initrd-timestamp=")) - dual_timestamp_deserialize(l+17, &m->initrd_timestamp); - else if (startswith(l, "userspace-timestamp=")) - dual_timestamp_deserialize(l+20, &m->userspace_timestamp); - else if (startswith(l, "finish-timestamp=")) - dual_timestamp_deserialize(l+17, &m->finish_timestamp); - else if (startswith(l, "security-start-timestamp=")) - dual_timestamp_deserialize(l+25, &m->security_start_timestamp); - else if (startswith(l, "security-finish-timestamp=")) - dual_timestamp_deserialize(l+26, &m->security_finish_timestamp); - else if (startswith(l, "generators-start-timestamp=")) - dual_timestamp_deserialize(l+27, &m->generators_start_timestamp); - else if (startswith(l, "generators-finish-timestamp=")) - dual_timestamp_deserialize(l+28, &m->generators_finish_timestamp); - else if (startswith(l, "units-load-start-timestamp=")) - dual_timestamp_deserialize(l+27, &m->units_load_start_timestamp); - else if (startswith(l, "units-load-finish-timestamp=")) - dual_timestamp_deserialize(l+28, &m->units_load_finish_timestamp); - else if (startswith(l, "env=")) { - _cleanup_free_ char *uce = NULL; - char **e; - - r = cunescape(l + 4, UNESCAPE_RELAX, &uce); - if (r < 0) - goto finish; - - e = strv_env_set(m->environment, uce); - if (!e) { - r = -ENOMEM; - goto finish; - } - - strv_free(m->environment); - m->environment = e; - - } else if (startswith(l, "notify-fd=")) { - int fd; - - if (safe_atoi(l + 10, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse notify fd: %s", l + 10); - else { - m->notify_event_source = sd_event_source_unref(m->notify_event_source); - safe_close(m->notify_fd); - m->notify_fd = fdset_remove(fds, fd); - } - - } else if (startswith(l, "notify-socket=")) { - char *n; - - n = strdup(l+14); - if (!n) { - r = -ENOMEM; - goto finish; - } - - free(m->notify_socket); - m->notify_socket = n; - - } else if (startswith(l, "cgroups-agent-fd=")) { - int fd; - - if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse cgroups agent fd: %s", l + 10); - else { - m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); - safe_close(m->cgroups_agent_fd); - m->cgroups_agent_fd = fdset_remove(fds, fd); - } - - } else if (startswith(l, "kdbus-fd=")) { - int fd; - - if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse kdbus fd: %s", l + 9); - else { - safe_close(m->kdbus_fd); - m->kdbus_fd = fdset_remove(fds, fd); - } - - } else { - int k; - - k = bus_track_deserialize_item(&m->deserialized_subscribed, l); - if (k < 0) - log_debug_errno(k, "Failed to deserialize bus tracker object: %m"); - else if (k == 0) - log_debug("Unknown serialization item '%s'", l); - } - } - - for (;;) { - Unit *u; - char name[UNIT_NAME_MAX+2]; - - /* Start marker */ - if (!fgets(name, sizeof(name), f)) { - if (feof(f)) - r = 0; - else - r = -errno; - - goto finish; - } - - char_array_0(name); - - r = manager_load_unit(m, strstrip(name), NULL, NULL, &u); - if (r < 0) - goto finish; - - r = unit_deserialize(u, f, fds); - if (r < 0) - goto finish; - } - -finish: - if (ferror(f)) - r = -EIO; - - assert(m->n_reloading > 0); - m->n_reloading--; - - return r; -} - -int manager_reload(Manager *m) { - int r, q; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_fdset_free_ FDSet *fds = NULL; - - assert(m); - - r = manager_open_serialization(m, &f); - if (r < 0) - return r; - - m->n_reloading++; - bus_manager_send_reloading(m, true); - - fds = fdset_new(); - if (!fds) { - m->n_reloading--; - return -ENOMEM; - } - - r = manager_serialize(m, f, fds, false); - if (r < 0) { - m->n_reloading--; - return r; - } - - if (fseeko(f, 0, SEEK_SET) < 0) { - m->n_reloading--; - return -errno; - } - - /* From here on there is no way back. */ - manager_clear_jobs_and_units(m); - lookup_paths_flush_generator(&m->lookup_paths); - lookup_paths_free(&m->lookup_paths); - - q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); - if (q < 0 && r >= 0) - r = q; - - /* Find new unit paths */ - q = manager_run_generators(m); - if (q < 0 && r >= 0) - r = q; - - lookup_paths_reduce(&m->lookup_paths); - manager_build_unit_path_cache(m); - - /* First, enumerate what we can from all config files */ - manager_enumerate(m); - - /* Second, deserialize our stored data */ - q = manager_deserialize(m, f, fds); - if (q < 0 && r >= 0) - r = q; - - fclose(f); - f = NULL; - - /* Re-register notify_fd as event source */ - q = manager_setup_notify(m); - if (q < 0 && r >= 0) - r = q; - - q = manager_setup_cgroups_agent(m); - if (q < 0 && r >= 0) - r = q; - - /* 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--; - - m->send_reloading_done = true; - - return r; -} - -void manager_reset_failed(Manager *m) { - Unit *u; - Iterator i; - - assert(m); - - HASHMAP_FOREACH(u, m->units, i) - unit_reset_failed(u); -} - -bool manager_unit_inactive_or_pending(Manager *m, const char *name) { - Unit *u; - - assert(m); - assert(name); - - /* Returns true if the unit is inactive or going down */ - u = manager_get_unit(m, name); - if (!u) - return true; - - return unit_inactive_or_pending(u); -} - -static void manager_notify_finished(Manager *m) { - char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; - usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; - - if (m->test_run) - return; - - if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { - - /* Note that m->kernel_usec.monotonic is always at 0, - * and m->firmware_usec.monotonic and - * m->loader_usec.monotonic should be considered - * negative values. */ - - firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic; - loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic; - userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; - total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic; - - if (dual_timestamp_is_set(&m->initrd_timestamp)) { - - kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic; - initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), - "KERNEL_USEC="USEC_FMT, kernel_usec, - "INITRD_USEC="USEC_FMT, initrd_usec, - "USERSPACE_USEC="USEC_FMT, userspace_usec, - LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), - format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), - format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); - } else { - kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; - initrd_usec = 0; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), - "KERNEL_USEC="USEC_FMT, kernel_usec, - "USERSPACE_USEC="USEC_FMT, userspace_usec, - LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), - format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); - } - } else { - firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; - total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), - "USERSPACE_USEC="USEC_FMT, userspace_usec, - LOG_MESSAGE("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), - NULL); - } - - bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); - - sd_notifyf(false, - "READY=1\n" - "STATUS=Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)); -} - -void manager_check_finished(Manager *m) { - assert(m); - - if (MANAGER_IS_RELOADING(m)) - return; - - /* Verify that we are actually running currently. Initially - * the exit code is set to invalid, and during operation it is - * then set to MANAGER_OK */ - if (m->exit_code != MANAGER_OK) - return; - - if (hashmap_size(m->jobs) > 0) { - if (m->jobs_in_progress_event_source) - /* Ignore any failure, this is only for feedback */ - (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); - - return; - } - - manager_flip_auto_status(m, false); - - /* Notify Type=idle units that we are done now */ - manager_close_idle_pipe(m); - - /* Turn off confirm spawn now */ - m->confirm_spawn = false; - - /* No need to update ask password status when we're going non-interactive */ - manager_close_ask_password(m); - - /* This is no longer the first boot */ - manager_set_first_boot(m, false); - - if (dual_timestamp_is_set(&m->finish_timestamp)) - return; - - dual_timestamp_get(&m->finish_timestamp); - - manager_notify_finished(m); - - manager_invalidate_startup_units(m); -} - -static int manager_run_generators(Manager *m) { - _cleanup_strv_free_ char **paths = NULL; - const char *argv[5]; - char **path; - int r; - - assert(m); - - if (m->test_run) - return 0; - - paths = generator_binary_paths(m->unit_file_scope); - if (!paths) - return log_oom(); - - /* Optimize by skipping the whole process by not creating output directories - * if no generators are found. */ - STRV_FOREACH(path, paths) { - if (access(*path, F_OK) >= 0) - goto found; - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); - } - - return 0; - - found: - r = lookup_paths_mkdir_generator(&m->lookup_paths); - if (r < 0) - goto finish; - - argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */ - argv[1] = m->lookup_paths.generator; - argv[2] = m->lookup_paths.generator_early; - argv[3] = m->lookup_paths.generator_late; - argv[4] = NULL; - - RUN_WITH_UMASK(0022) - execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); - -finish: - lookup_paths_trim_generator(&m->lookup_paths); - return r; -} - -int manager_environment_add(Manager *m, char **minus, char **plus) { - char **a = NULL, **b = NULL, **l; - assert(m); - - l = m->environment; - - if (!strv_isempty(minus)) { - a = strv_env_delete(l, 1, minus); - if (!a) - return -ENOMEM; - - l = a; - } - - if (!strv_isempty(plus)) { - b = strv_env_merge(2, l, plus); - if (!b) { - strv_free(a); - return -ENOMEM; - } - - l = b; - } - - if (m->environment != l) - strv_free(m->environment); - if (a != l) - strv_free(a); - if (b != l) - strv_free(b); - - m->environment = l; - manager_clean_environment(m); - strv_sort(m->environment); - - return 0; -} - -int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) { - int i; - - assert(m); - - for (i = 0; i < _RLIMIT_MAX; i++) { - m->rlimit[i] = mfree(m->rlimit[i]); - - if (!default_rlimit[i]) - continue; - - m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1); - if (!m->rlimit[i]) - return -ENOMEM; - } - - return 0; -} - -void manager_recheck_journal(Manager *m) { - Unit *u; - - assert(m); - - if (!MANAGER_IS_SYSTEM(m)) - return; - - u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); - if (u && SOCKET(u)->state != SOCKET_RUNNING) { - log_close_journal(); - return; - } - - u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE); - if (u && SERVICE(u)->state != SERVICE_RUNNING) { - log_close_journal(); - return; - } - - /* Hmm, OK, so the socket is fully up and the service is up - * too, then let's make use of the thing. */ - log_open(); -} - -void manager_set_show_status(Manager *m, ShowStatus mode) { - assert(m); - assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY)); - - if (!MANAGER_IS_SYSTEM(m)) - 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) - (void) touch("/run/systemd/show-status"); - else - (void) unlink("/run/systemd/show-status"); -} - -static bool manager_get_show_status(Manager *m, StatusType type) { - assert(m); - - if (!MANAGER_IS_SYSTEM(m)) - return false; - - if (m->no_console_output) - return false; - - if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING)) - return false; - - /* If we cannot find out the status properly, just proceed. */ - if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) - return false; - - if (m->show_status > 0) - return true; - - return false; -} - -void manager_set_first_boot(Manager *m, bool b) { - assert(m); - - if (!MANAGER_IS_SYSTEM(m)) - return; - - if (m->first_boot != (int) b) { - if (b) - (void) touch("/run/systemd/first-boot"); - else - (void) unlink("/run/systemd/first-boot"); - } - - m->first_boot = b; -} - -void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { - va_list ap; - - /* If m is NULL, assume we're after shutdown and let the messages through. */ - - if (m && !manager_get_show_status(m, type)) - return; - - /* XXX We should totally drop the check for ephemeral here - * and thus effectively make 'Type=idle' pointless. */ - if (type == STATUS_TYPE_EPHEMERAL && m && m->n_on_console > 0) - return; - - va_start(ap, format); - status_vprintf(status, true, type == STATUS_TYPE_EPHEMERAL, format, ap); - va_end(ap); -} - -Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { - char p[strlen(path)+1]; - - assert(m); - assert(path); - - strcpy(p, path); - path_kill_slashes(p); - - return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p); -} - -const char *manager_get_runtime_prefix(Manager *m) { - assert(m); - - return MANAGER_IS_SYSTEM(m) ? - "/run" : - getenv("XDG_RUNTIME_DIR"); -} - -int manager_update_failed_units(Manager *m, Unit *u, bool failed) { - unsigned size; - int r; - - assert(m); - assert(u->manager == m); - - size = set_size(m->failed_units); - - if (failed) { - r = set_ensure_allocated(&m->failed_units, NULL); - if (r < 0) - return log_oom(); - - if (set_put(m->failed_units, u) < 0) - return log_oom(); - } else - (void) set_remove(m->failed_units, u); - - if (set_size(m->failed_units) != size) - bus_manager_send_change_signal(m); - - return 0; -} - -ManagerState manager_state(Manager *m) { - Unit *u; - - assert(m); - - /* Did we ever finish booting? If not then we are still starting up */ - if (!dual_timestamp_is_set(&m->finish_timestamp)) { - - u = manager_get_unit(m, SPECIAL_BASIC_TARGET); - if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) - return MANAGER_INITIALIZING; - - return MANAGER_STARTING; - } - - /* Is the special shutdown target queued? If so, we are in shutdown state */ - u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); - if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)) - return MANAGER_STOPPING; - - /* Are the rescue or emergency targets active or queued? If so we are in maintenance state */ - u = manager_get_unit(m, SPECIAL_RESCUE_TARGET); - if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) || - (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)))) - return MANAGER_MAINTENANCE; - - u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET); - if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) || - (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)))) - return MANAGER_MAINTENANCE; - - /* Are there any failed units? If so, we are in degraded mode */ - if (set_size(m->failed_units) > 0) - return MANAGER_DEGRADED; - - return MANAGER_RUNNING; -} - -static const char *const manager_state_table[_MANAGER_STATE_MAX] = { - [MANAGER_INITIALIZING] = "initializing", - [MANAGER_STARTING] = "starting", - [MANAGER_RUNNING] = "running", - [MANAGER_DEGRADED] = "degraded", - [MANAGER_MAINTENANCE] = "maintenance", - [MANAGER_STOPPING] = "stopping", -}; - -DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState); diff --git a/src/core/manager.h b/src/core/manager.h deleted file mode 100644 index 6ed15c1a41..0000000000 --- a/src/core/manager.h +++ /dev/null @@ -1,379 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "sd-bus.h" -#include "sd-event.h" - -#include "cgroup-util.h" -#include "fdset.h" -#include "hashmap.h" -#include "list.h" -#include "ratelimit.h" - -/* Enforce upper limit how many names we allow */ -#define MANAGER_MAX_NAMES 131072 /* 128K */ - -typedef struct Manager Manager; - -typedef enum ManagerState { - MANAGER_INITIALIZING, - MANAGER_STARTING, - MANAGER_RUNNING, - MANAGER_DEGRADED, - MANAGER_MAINTENANCE, - MANAGER_STOPPING, - _MANAGER_STATE_MAX, - _MANAGER_STATE_INVALID = -1 -} ManagerState; - -typedef enum ManagerExitCode { - MANAGER_OK, - MANAGER_EXIT, - MANAGER_RELOAD, - MANAGER_REEXECUTE, - MANAGER_REBOOT, - MANAGER_POWEROFF, - MANAGER_HALT, - MANAGER_KEXEC, - MANAGER_SWITCH_ROOT, - _MANAGER_EXIT_CODE_MAX, - _MANAGER_EXIT_CODE_INVALID = -1 -} ManagerExitCode; - -typedef enum StatusType { - STATUS_TYPE_EPHEMERAL, - STATUS_TYPE_NORMAL, - STATUS_TYPE_EMERGENCY, -} StatusType; - -#include "execute.h" -#include "job.h" -#include "path-lookup.h" -#include "show-status.h" -#include "unit-name.h" - -struct Manager { - /* Note that the set of units we know of is allowed to be - * inconsistent. However the subset of it that is loaded may - * not, and the list of jobs may neither. */ - - /* Active jobs and units */ - Hashmap *units; /* name string => Unit object n:1 */ - Hashmap *jobs; /* job id => Job object 1:1 */ - - /* To make it easy to iterate through the units of a specific - * type we maintain a per type linked list */ - LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]); - - /* Units that need to be loaded */ - LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */ - - /* Jobs that need to be run */ - LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */ - - /* Units and jobs that have not yet been announced via - * D-Bus. When something about a job changes it is added here - * if it is not in there yet. This allows easy coalescing of - * D-Bus change signals. */ - LIST_HEAD(Unit, dbus_unit_queue); - LIST_HEAD(Job, dbus_job_queue); - - /* Units to remove */ - LIST_HEAD(Unit, cleanup_queue); - - /* Units to check when doing GC */ - LIST_HEAD(Unit, gc_queue); - - /* Units that should be realized */ - LIST_HEAD(Unit, cgroup_queue); - - sd_event *event; - - /* We use two hash tables here, since the same PID might be - * watched by two different units: once the unit that forked - * it off, and possibly a different unit to which it was - * joined as cgroup member. Since we know that it is either - * one or two units for each PID we just use to hashmaps - * here. */ - Hashmap *watch_pids1; /* pid => Unit object n:1 */ - Hashmap *watch_pids2; /* pid => Unit object n:1 */ - - /* A set contains all units which cgroup should be refreshed after startup */ - Set *startup_units; - - /* A set which contains all currently failed units */ - Set *failed_units; - - sd_event_source *run_queue_event_source; - - char *notify_socket; - int notify_fd; - sd_event_source *notify_event_source; - - int cgroups_agent_fd; - sd_event_source *cgroups_agent_event_source; - - int signal_fd; - sd_event_source *signal_event_source; - - int time_change_fd; - sd_event_source *time_change_event_source; - - sd_event_source *jobs_in_progress_event_source; - - UnitFileScope unit_file_scope; - LookupPaths lookup_paths; - Set *unit_path_cache; - - char **environment; - - usec_t runtime_watchdog; - usec_t shutdown_watchdog; - - dual_timestamp firmware_timestamp; - dual_timestamp loader_timestamp; - dual_timestamp kernel_timestamp; - dual_timestamp initrd_timestamp; - dual_timestamp userspace_timestamp; - dual_timestamp finish_timestamp; - - dual_timestamp security_start_timestamp; - dual_timestamp security_finish_timestamp; - dual_timestamp generators_start_timestamp; - dual_timestamp generators_finish_timestamp; - dual_timestamp units_load_start_timestamp; - dual_timestamp units_load_finish_timestamp; - - struct udev* udev; - - /* Data specific to the device subsystem */ - struct udev_monitor* udev_monitor; - sd_event_source *udev_event_source; - Hashmap *devices_by_sysfs; - - /* Data specific to the mount subsystem */ - struct libmnt_monitor *mount_monitor; - sd_event_source *mount_event_source; - - /* Data specific to the swap filesystem */ - FILE *proc_swaps; - sd_event_source *swap_event_source; - Hashmap *swaps_by_devnode; - - /* Data specific to the D-Bus subsystem */ - sd_bus *api_bus, *system_bus; - Set *private_buses; - int private_listen_fd; - sd_event_source *private_listen_event_source; - - /* Contains all the clients that are subscribed to signals via - the API bus. Note that private bus connections are always - considered subscribes, since they last for very short only, - and it is much simpler that way. */ - sd_bus_track *subscribed; - char **deserialized_subscribed; - - /* This is used during reloading: before the reload we queue - * the reply message here, and afterwards we send it */ - sd_bus_message *queued_message; - - Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ - - bool send_reloading_done; - - uint32_t current_job_id; - uint32_t default_unit_job_id; - - /* Data specific to the Automount subsystem */ - int dev_autofs_fd; - - /* Data specific to the cgroup subsystem */ - Hashmap *cgroup_unit; - CGroupMask cgroup_supported; - char *cgroup_root; - - /* Notifications from cgroups, when the unified hierarchy is - * used is done via inotify. */ - int cgroup_inotify_fd; - sd_event_source *cgroup_inotify_event_source; - Hashmap *cgroup_inotify_wd_unit; - - /* Make sure the user cannot accidentally unmount our cgroup - * file system */ - int pin_cgroupfs_fd; - - int gc_marker; - unsigned n_in_gc_queue; - - /* Flags */ - ManagerExitCode exit_code:5; - - bool dispatching_load_queue:1; - bool dispatching_dbus_queue:1; - - bool taint_usr:1; - - bool test_run:1; - - /* If non-zero, exit with the following value when the systemd - * process terminate. Useful for containers: systemd-nspawn could get - * the return value. */ - uint8_t return_value; - - ShowStatus show_status; - bool confirm_spawn; - bool no_console_output; - - ExecOutput default_std_output, default_std_error; - - usec_t default_restart_usec, default_timeout_start_usec, default_timeout_stop_usec; - - usec_t default_start_limit_interval; - unsigned default_start_limit_burst; - - bool default_cpu_accounting; - bool default_memory_accounting; - bool default_io_accounting; - bool default_blockio_accounting; - bool default_tasks_accounting; - - uint64_t default_tasks_max; - usec_t default_timer_accuracy_usec; - - struct rlimit *rlimit[_RLIMIT_MAX]; - - /* non-zero if we are reloading or reexecuting, */ - int n_reloading; - - unsigned n_installed_jobs; - unsigned n_failed_jobs; - - /* Jobs in progress watching */ - unsigned n_running_jobs; - unsigned n_on_console; - unsigned jobs_in_progress_iteration; - - /* Do we have any outstanding password prompts? */ - int have_ask_password; - int ask_password_inotify_fd; - sd_event_source *ask_password_event_source; - - /* Type=idle pipes */ - int idle_pipe[4]; - sd_event_source *idle_pipe_event_source; - - char *switch_root; - char *switch_root_init; - - /* This maps all possible path prefixes to the units needing - * them. It's a hashmap with a path string as key and a Set as - * value where Unit objects are contained. */ - Hashmap *units_requiring_mounts_for; - - /* Reference to the kdbus bus control fd */ - int kdbus_fd; - - /* Used for processing polkit authorization responses */ - Hashmap *polkit_registry; - - /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */ - RateLimit ctrl_alt_del_ratelimit; - - const char *unit_log_field; - const char *unit_log_format_string; - - int first_boot; /* tri-state */ -}; - -#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) -#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) - -#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) - -int manager_new(UnitFileScope scope, bool test_run, Manager **m); -Manager* manager_free(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_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, 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); - -void manager_clear_jobs(Manager *m); - -unsigned manager_dispatch_load_queue(Manager *m); - -int manager_environment_add(Manager *m, char **minus, char **plus); -int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit); - -int manager_loop(Manager *m); - -int manager_open_serialization(Manager *m, FILE **_f); - -int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root); -int manager_deserialize(Manager *m, FILE *f, FDSet *fds); - -int manager_reload(Manager *m); - -void manager_reset_failed(Manager *m); - -void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); -void manager_send_unit_plymouth(Manager *m, Unit *u); - -bool manager_unit_inactive_or_pending(Manager *m, const char *name); - -void manager_check_finished(Manager *m); - -void manager_recheck_journal(Manager *m); - -void manager_set_show_status(Manager *m, ShowStatus mode); -void manager_set_first_boot(Manager *m, bool b); - -void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5); -void manager_flip_auto_status(Manager *m, bool enable); - -Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path); - -const char *manager_get_runtime_prefix(Manager *m); - -ManagerState manager_state(Manager *m); - -int manager_update_failed_units(Manager *m, Unit *u, bool failed); - -const char *manager_state_to_string(ManagerState m) _const_; -ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c deleted file mode 100644 index 5d8ab0ec70..0000000000 --- a/src/core/mount-setup.c +++ /dev/null @@ -1,419 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bus-util.h" -#include "cgroup-util.h" -#include "dev-setup.h" -#include "efivars.h" -#include "fs-util.h" -#include "label.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "mount-setup.h" -#include "mount-util.h" -#include "path-util.h" -#include "set.h" -#include "smack-util.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" -#include "virt.h" - -typedef enum MountMode { - MNT_NONE = 0, - MNT_FATAL = 1 << 0, - MNT_IN_CONTAINER = 1 << 1, -} MountMode; - -typedef struct MountPoint { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; - bool (*condition_fn)(void); - MountMode mode; -} MountPoint; - -/* The first three entries we might need before SELinux is up. The - * fourth (securityfs) is needed by IMA to load a custom policy. The - * other ones we can delay until SELinux and IMA are loaded. When - * SMACK is enabled we need smackfs, too, so it's a fifth one. */ -#ifdef HAVE_SMACK -#define N_EARLY_MOUNT 5 -#else -#define N_EARLY_MOUNT 4 -#endif - -static const MountPoint mount_table[] = { - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_NONE }, -#ifdef HAVE_SMACK - { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV, - mac_smack_use, MNT_FATAL }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - mac_smack_use, MNT_FATAL }, -#endif - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, - NULL, MNT_IN_CONTAINER }, -#ifdef HAVE_SMACK - { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - mac_smack_use, MNT_FATAL }, -#endif - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_wanted, MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_NONE }, -#ifdef ENABLE_EFI - { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - is_efi_boot, MNT_NONE }, -#endif -}; - -/* These are API file systems that might be mounted by other software, - * we just list them here so that we know that we should ignore them */ - -static const char ignore_paths[] = - /* SELinux file systems */ - "/sys/fs/selinux\0" - /* Container bind mounts */ - "/proc/sys\0" - "/dev/console\0" - "/proc/kmsg\0"; - -bool mount_point_is_api(const char *path) { - unsigned i; - - /* Checks if this mount point is considered "API", and hence - * should be ignored */ - - for (i = 0; i < ELEMENTSOF(mount_table); i ++) - if (path_equal(path, mount_table[i].where)) - return true; - - return path_startswith(path, "/sys/fs/cgroup/"); -} - -bool mount_point_ignore(const char *path) { - const char *i; - - NULSTR_FOREACH(i, ignore_paths) - if (path_equal(path, i)) - return true; - - return false; -} - -static int mount_one(const MountPoint *p, bool relabel) { - int r; - - assert(p); - - if (p->condition_fn && !p->condition_fn()) - return 0; - - /* Relabel first, just in case */ - if (relabel) - (void) label_fix(p->where, true, true); - - r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW); - if (r < 0 && r != -ENOENT) { - log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); - return (p->mode & MNT_FATAL) ? r : 0; - } - if (r > 0) - return 0; - - /* Skip securityfs in a container */ - if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0) - return 0; - - /* The access mode here doesn't really matter too much, since - * the mounted file system will take precedence anyway. */ - if (relabel) - (void) mkdir_p_label(p->where, 0755); - else - (void) mkdir_p(p->where, 0755); - - log_debug("Mounting %s to %s of type %s with options %s.", - p->what, - p->where, - p->type, - strna(p->options)); - - if (mount(p->what, - p->where, - p->type, - p->flags, - p->options) < 0) { - log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where); - return (p->mode & MNT_FATAL) ? -errno : 0; - } - - /* Relabel again, since we now mounted something fresh here */ - if (relabel) - (void) label_fix(p->where, false, false); - - return 1; -} - -static int mount_points_setup(unsigned n, bool loaded_policy) { - unsigned i; - int r = 0; - - for (i = 0; i < n; i ++) { - int j; - - j = mount_one(mount_table + i, loaded_policy); - if (j != 0 && r >= 0) - r = j; - } - - return r; -} - -int mount_setup_early(void) { - assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); - - /* Do a minimal mount of /proc and friends to enable the most - * basic stuff, such as SELinux */ - return mount_points_setup(N_EARLY_MOUNT, false); -} - -int mount_cgroup_controllers(char ***join_controllers) { - _cleanup_set_free_free_ Set *controllers = NULL; - int r; - - if (!cg_is_legacy_wanted()) - return 0; - - /* Mount all available cgroup controllers that are built into the kernel. */ - - controllers = set_new(&string_hash_ops); - if (!controllers) - return log_oom(); - - r = cg_kernel_controllers(controllers); - if (r < 0) - return log_error_errno(r, "Failed to enumerate cgroup controllers: %m"); - - for (;;) { - _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL; - MountPoint p = { - .what = "cgroup", - .type = "cgroup", - .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, - .mode = MNT_IN_CONTAINER, - }; - char ***k = NULL; - - controller = set_steal_first(controllers); - if (!controller) - break; - - if (join_controllers) - for (k = join_controllers; *k; k++) - if (strv_find(*k, controller)) - break; - - if (k && *k) { - char **i, **j; - - for (i = *k, j = *k; *i; i++) { - - if (!streq(*i, controller)) { - _cleanup_free_ char *t; - - t = set_remove(controllers, *i); - if (!t) { - free(*i); - continue; - } - } - - *(j++) = *i; - } - - *j = NULL; - - options = strv_join(*k, ","); - if (!options) - return log_oom(); - } else { - options = controller; - controller = NULL; - } - - where = strappend("/sys/fs/cgroup/", options); - if (!where) - return log_oom(); - - p.where = where; - p.options = options; - - r = mount_one(&p, true); - if (r < 0) - return r; - - if (r > 0 && k && *k) { - char **i; - - for (i = *k; *i; i++) { - _cleanup_free_ char *t = NULL; - - t = strappend("/sys/fs/cgroup/", *i); - if (!t) - return log_oom(); - - r = symlink(options, t); - if (r >= 0) { -#ifdef SMACK_RUN_LABEL - _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); - } - } - } - - /* Now that we mounted everything, let's make the tmpfs the - * cgroup file systems are mounted into read-only. */ - (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755"); - - return 0; -} - -#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int tflag, - struct FTW *ftwbuf) { - - /* No need to label /dev twice in a row... */ - if (_unlikely_(ftwbuf->level == 0)) - return FTW_CONTINUE; - - label_fix(fpath, false, false); - - /* /run/initramfs is static data and big, no need to - * dynamically relabel its contents at boot... */ - if (_unlikely_(ftwbuf->level == 1 && - tflag == FTW_D && - streq(fpath, "/run/initramfs"))) - return FTW_SKIP_SUBTREE; - - return FTW_CONTINUE; -}; -#endif - -int mount_setup(bool loaded_policy) { - int r = 0; - - r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy); - - if (r < 0) - return r; - -#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) - /* Nodes in devtmpfs and /run need to be manually updated for - * the appropriate labels, after mounting. The other virtual - * API file systems like /sys and /proc do not need that, they - * use the same label for all their files. */ - if (loaded_policy) { - usec_t before_relabel, after_relabel; - char timespan[FORMAT_TIMESPAN_MAX]; - - before_relabel = now(CLOCK_MONOTONIC); - - nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - - after_relabel = now(CLOCK_MONOTONIC); - - log_info("Relabelled /dev and /run in %s.", - format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0)); - } -#endif - - /* Create a few default symlinks, which are normally created - * by udevd, but some scripts might need them before we start - * udevd. */ - dev_setup(NULL, UID_INVALID, GID_INVALID); - - /* Mark the root directory as shared in regards to mount - * propagation. The kernel defaults to "private", but we think - * it makes more sense to have a default of "shared" so that - * nspawn and the container tools work out of the box. If - * specific setups need other settings they can reset the - * propagation mode to private if needed. */ - if (detect_container() <= 0) - if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) - log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m"); - - /* Create a few directories we always want around, Note that - * sd_booted() checks for /run/systemd/system, so this mkdir - * really needs to stay for good, otherwise software that - * copied sd-daemon.c into their sources will misdetect - * systemd. */ - (void) mkdir_label("/run/systemd", 0755); - (void) mkdir_label("/run/systemd/system", 0755); - (void) mkdir_label("/run/systemd/inaccessible", 0000); - /* Set up inaccessible items */ - (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0); - (void) mkdir_label("/run/systemd/inaccessible/dir", 0000); - (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0)); - (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0)); - (void) mkfifo("/run/systemd/inaccessible/fifo", 0000); - (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0); - - return 0; -} diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h deleted file mode 100644 index 647bd770ae..0000000000 --- a/src/core/mount-setup.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include - -int mount_setup_early(void); -int mount_setup(bool loaded_policy); - -int mount_cgroup_controllers(char ***join_controllers); - -bool mount_point_is_api(const char *path); -bool mount_point_ignore(const char *path); diff --git a/src/core/mount.c b/src/core/mount.c deleted file mode 100644 index fda4d65d6f..0000000000 --- a/src/core/mount.c +++ /dev/null @@ -1,1882 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "dbus-mount.h" -#include "escape.h" -#include "exit-status.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 - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); - -static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = UNIT_INACTIVE, - [MOUNT_MOUNTING] = UNIT_ACTIVATING, - [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE, - [MOUNT_MOUNTED] = UNIT_ACTIVE, - [MOUNT_REMOUNTING] = UNIT_RELOADING, - [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING, - [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING, - [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING, - [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING, - [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING, - [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING, - [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING, - [MOUNT_FAILED] = UNIT_FAILED -}; - -static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); - -static bool mount_needs_network(const char *options, const char *fstype) { - if (fstab_test_option(options, "_netdev\0")) - return true; - - if (fstype && fstype_is_network(fstype)) - return true; - - return false; -} - -static bool mount_is_network(const MountParameters *p) { - assert(p); - - return mount_needs_network(p->options, p->fstype); -} - -static bool mount_is_loop(const MountParameters *p) { - assert(p); - - if (fstab_test_option(p->options, "loop\0")) - return true; - - return false; -} - -static bool mount_is_bind(const MountParameters *p) { - assert(p); - - if (fstab_test_option(p->options, "bind\0" "rbind\0")) - return true; - - if (p->fstype && STR_IN_SET(p->fstype, "bind", "rbind")) - return true; - - return false; -} - -static bool mount_is_auto(const MountParameters *p) { - assert(p); - - return !fstab_test_option(p->options, "noauto\0"); -} - -static bool mount_is_automount(const MountParameters *p) { - assert(p); - - return fstab_test_option(p->options, - "comment=systemd.automount\0" - "x-systemd.automount\0"); -} - -static bool mount_state_active(MountState state) { - return IN_SET(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); -} - -static bool needs_quota(const MountParameters *p) { - assert(p); - - /* Quotas are not enabled on network filesystems, - * but we want them, for example, on storage connected via iscsi */ - if (p->fstype && fstype_is_network(p->fstype)) - return false; - - if (mount_is_bind(p)) - return false; - - return fstab_test_option(p->options, - "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0"); -} - -static void mount_init(Unit *u) { - Mount *m = MOUNT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - m->timeout_usec = u->manager->default_timeout_start_usec; - m->directory_mode = 0755; - - if (unit_has_name(u, "-.mount")) { - /* Don't allow start/stop for root directory */ - u->refuse_manual_start = true; - u->refuse_manual_stop = true; - } else { - /* The stdio/kmsg bridge socket is on /, in order to avoid a - * dep loop, don't use kmsg logging for -.mount */ - m->exec_context.std_output = u->manager->default_std_output; - m->exec_context.std_error = u->manager->default_std_error; - } - - /* We need to make sure that /usr/bin/mount is always called - * in the same process group as us, so that the autofs kernel - * side doesn't send us another mount request while we are - * already trying to comply its last one. */ - m->exec_context.same_pgrp = true; - - m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - - u->ignore_on_isolate = true; -} - -static int mount_arm_timer(Mount *m, usec_t usec) { - int r; - - assert(m); - - if (m->timer_event_source) { - r = sd_event_source_set_time(m->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(m)->manager->event, - &m->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - mount_dispatch_timer, m); - if (r < 0) - return r; - - (void) sd_event_source_set_description(m->timer_event_source, "mount-timer"); - - return 0; -} - -static void mount_unwatch_control_pid(Mount *m) { - assert(m); - - if (m->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(m), m->control_pid); - m->control_pid = 0; -} - -static void mount_parameters_done(MountParameters *p) { - assert(p); - - free(p->what); - free(p->options); - free(p->fstype); - - p->what = p->options = p->fstype = NULL; -} - -static void mount_done(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - m->where = mfree(m->where); - - mount_parameters_done(&m->parameters_proc_self_mountinfo); - mount_parameters_done(&m->parameters_fragment); - - m->exec_runtime = exec_runtime_unref(m->exec_runtime); - exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); - m->control_command = NULL; - - mount_unwatch_control_pid(m); - - m->timer_event_source = sd_event_source_unref(m->timer_event_source); -} - -_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) { - assert(m); - - if (m->from_fragment) - return &m->parameters_fragment; - - return NULL; -} - -_pure_ static MountParameters* get_mount_parameters(Mount *m) { - assert(m); - - if (m->from_proc_self_mountinfo) - return &m->parameters_proc_self_mountinfo; - - return get_mount_parameters_fragment(m); -} - -static int mount_add_mount_links(Mount *m) { - _cleanup_free_ char *parent = NULL; - MountParameters *pm; - Unit *other; - Iterator i; - Set *s; - int r; - - assert(m); - - if (!path_equal(m->where, "/")) { - /* Adds in links to other mount points that might lie further - * up in the hierarchy */ - - parent = dirname_malloc(m->where); - if (!parent) - return -ENOMEM; - - r = unit_require_mounts_for(UNIT(m), parent); - if (r < 0) - return r; - } - - /* Adds in links to other mount points that might be needed - * for the source path (if this is a bind mount or a loop mount) to be - * available. */ - pm = get_mount_parameters_fragment(m); - if (pm && pm->what && - path_is_absolute(pm->what) && - (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) { - - r = unit_require_mounts_for(UNIT(m), pm->what); - if (r < 0) - return r; - } - - /* Adds in links to other units that use this path or paths - * further down in the hierarchy */ - s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where); - SET_FOREACH(other, s, i) { - - if (other->load_state != UNIT_LOADED) - continue; - - if (other == UNIT(m)) - continue; - - r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true); - if (r < 0) - return r; - - if (UNIT(m)->fragment_path) { - /* If we have fragment configuration, then make this dependency required */ - r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true); - if (r < 0) - return r; - } - } - - return 0; -} - -static int mount_add_device_links(Mount *m) { - MountParameters *p; - bool device_wants_mount = false; - int r; - - assert(m); - - p = get_mount_parameters(m); - if (!p) - return 0; - - if (!p->what) - return 0; - - if (mount_is_bind(p)) - return 0; - - if (!is_device_path(p->what)) - return 0; - - /* /dev/root is a really weird thing, it's not a real device, - * but just a path the kernel exports for the root file system - * specified on the kernel command line. Ignore it here. */ - if (path_equal(p->what, "/dev/root")) - return 0; - - if (path_equal(m->where, "/")) - return 0; - - if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager)) - device_wants_mount = true; - - 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; - - return 0; -} - -static int mount_add_quota_links(Mount *m) { - int r; - MountParameters *p; - - assert(m); - - if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) - return 0; - - p = get_mount_parameters_fragment(m); - if (!p) - return 0; - - if (!needs_quota(p)) - return 0; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true); - if (r < 0) - return r; - - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static bool should_umount(Mount *m) { - MountParameters *p; - - if (PATH_IN_SET(m->where, "/", "/usr") || - path_startswith(m->where, "/run/initramfs")) - return false; - - p = get_mount_parameters(m); - if (p && fstab_test_option(p->options, "x-initrd.mount\0") && - !in_initrd()) - return false; - - return true; -} - -static int mount_add_default_dependencies(Mount *m) { - MountParameters *p; - const char *after; - int r; - - assert(m); - - if (!UNIT(m)->default_dependencies) - return 0; - - if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) - return 0; - - /* We do not add any default dependencies to /, /usr or - * /run/initramfs/, since they are guaranteed to stay - * mounted the whole time, since our system is on it. - * Also, don't bother with anything mounted below virtual - * file systems, it's also going to be virtual, and hence - * not worth the effort. */ - if (PATH_IN_SET(m->where, "/", "/usr") || - path_startswith(m->where, "/run/initramfs") || - path_startswith(m->where, "/proc") || - path_startswith(m->where, "/sys") || - path_startswith(m->where, "/dev")) - return 0; - - p = get_mount_parameters(m); - if (!p) - return 0; - - if (mount_is_network(p)) { - /* 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. */ - - r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true); - if (r < 0) - return r; - - /* 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); - if (r < 0) - return r; - } - - return 0; -} - -static int mount_verify(Mount *m) { - _cleanup_free_ char *e = NULL; - int r; - - assert(m); - - if (UNIT(m)->load_state != UNIT_LOADED) - return 0; - - if (!m->from_fragment && !m->from_proc_self_mountinfo) - return -ENOENT; - - r = unit_name_from_path(m->where, ".mount", &e); - if (r < 0) - return log_unit_error_errno(UNIT(m), r, "Failed to generate unit name from mount path: %m"); - - if (!unit_has_name(UNIT(m), e)) { - log_unit_error(UNIT(m), "Where= setting doesn't match unit name. Refusing."); - return -EINVAL; - } - - if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) { - log_unit_error(UNIT(m), "Cannot create mount unit for API file system %s. Refusing.", m->where); - return -EINVAL; - } - - if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { - log_unit_error(UNIT(m), "What= setting is missing. Refusing."); - return -EBADMSG; - } - - if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int mount_add_extras(Mount *m) { - Unit *u = UNIT(m); - int r; - - assert(m); - - if (u->fragment_path) - m->from_fragment = true; - - if (!m->where) { - r = unit_name_to_path(u->id, &m->where); - if (r < 0) - return r; - } - - path_kill_slashes(m->where); - - if (!u->description) { - r = unit_set_description(u, m->where); - if (r < 0) - return r; - } - - r = mount_add_device_links(m); - if (r < 0) - return r; - - r = mount_add_mount_links(m); - if (r < 0) - return r; - - r = mount_add_quota_links(m); - if (r < 0) - return r; - - r = unit_patch_contexts(u); - if (r < 0) - return r; - - r = unit_add_exec_dependencies(u, &m->exec_context); - if (r < 0) - return r; - - r = unit_set_default_slice(u); - if (r < 0) - return r; - - r = mount_add_default_dependencies(m); - if (r < 0) - return r; - - return 0; -} - -static int mount_load(Unit *u) { - Mount *m = MOUNT(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - if (m->from_proc_self_mountinfo) - r = unit_load_fragment_and_dropin_optional(u); - else - r = unit_load_fragment_and_dropin(u); - - if (r < 0) - return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - r = mount_add_extras(m); - if (r < 0) - return r; - } - - return mount_verify(m); -} - -static void mount_set_state(Mount *m, MountState state) { - MountState old_state; - assert(m); - - old_state = m->state; - m->state = state; - - if (!mount_state_active(state)) { - m->timer_event_source = sd_event_source_unref(m->timer_event_source); - mount_unwatch_control_pid(m); - m->control_command = NULL; - m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - } - - if (state != old_state) - log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); - - unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS); - m->reload_result = MOUNT_SUCCESS; -} - -static int mount_coldplug(Unit *u) { - Mount *m = MOUNT(u); - MountState new_state = MOUNT_DEAD; - int r; - - assert(m); - assert(m->state == MOUNT_DEAD); - - if (m->deserialized_state != m->state) - new_state = m->deserialized_state; - else if (m->from_proc_self_mountinfo) - new_state = MOUNT_MOUNTED; - - if (new_state == m->state) - return 0; - - if (m->control_pid > 0 && - pid_is_unwaited(m->control_pid) && - mount_state_active(new_state)) { - - r = unit_watch_pid(UNIT(m), m->control_pid); - if (r < 0) - return r; - - r = mount_arm_timer(m, usec_add(u->state_change_timestamp.monotonic, m->timeout_usec)); - if (r < 0) - return r; - } - - mount_set_state(m, new_state); - return 0; -} - -static void mount_dump(Unit *u, FILE *f, const char *prefix) { - Mount *m = MOUNT(u); - MountParameters *p; - - assert(m); - assert(f); - - p = get_mount_parameters(m); - - fprintf(f, - "%sMount State: %s\n" - "%sResult: %s\n" - "%sWhere: %s\n" - "%sWhat: %s\n" - "%sFile System Type: %s\n" - "%sOptions: %s\n" - "%sFrom /proc/self/mountinfo: %s\n" - "%sFrom fragment: %s\n" - "%sDirectoryMode: %04o\n", - prefix, mount_state_to_string(m->state), - prefix, mount_result_to_string(m->result), - prefix, m->where, - prefix, p ? strna(p->what) : "n/a", - prefix, p ? strna(p->fstype) : "n/a", - prefix, p ? strna(p->options) : "n/a", - prefix, yes_no(m->from_proc_self_mountinfo), - prefix, yes_no(m->from_fragment), - prefix, m->directory_mode); - - if (m->control_pid > 0) - fprintf(f, - "%sControl PID: "PID_FMT"\n", - prefix, m->control_pid); - - exec_context_dump(&m->exec_context, f, prefix); - kill_context_dump(&m->kill_context, f, prefix); -} - -static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; - ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, - }; - - assert(m); - assert(c); - assert(_pid); - - (void) unit_realize_cgroup(UNIT(m)); - if (m->reset_cpu_usage) { - (void) unit_reset_cpu_usage(UNIT(m)); - m->reset_cpu_usage = false; - } - - r = unit_setup_exec_runtime(UNIT(m)); - if (r < 0) - return r; - - r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec)); - if (r < 0) - return r; - - exec_params.environment = UNIT(m)->manager->environment; - exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; - exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; - exec_params.cgroup_path = UNIT(m)->cgroup_path; - exec_params.cgroup_delegate = m->cgroup_context.delegate; - exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(m)->manager); - - r = exec_spawn(UNIT(m), - c, - &m->exec_context, - &exec_params, - m->exec_runtime, - &pid); - if (r < 0) - return r; - - r = unit_watch_pid(UNIT(m), pid); - if (r < 0) - /* FIXME: we need to do something here */ - return r; - - *_pid = pid; - - return 0; -} - -static void mount_enter_dead(Mount *m, MountResult f) { - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - exec_runtime_destroy(m->exec_runtime); - m->exec_runtime = exec_runtime_unref(m->exec_runtime); - - exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager)); - - mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); -} - -static void mount_enter_mounted(Mount *m, MountResult f) { - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - mount_set_state(m, MOUNT_MOUNTED); -} - -static void mount_enter_signal(Mount *m, MountState state, MountResult f) { - int r; - - assert(m); - - if (f != MOUNT_SUCCESS) - m->result = f; - - r = unit_kill_context( - UNIT(m), - &m->kill_context, - (state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM) ? - KILL_KILL : KILL_TERMINATE, - -1, - m->control_pid, - false); - if (r < 0) - goto fail; - - if (r > 0) { - r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec)); - if (r < 0) - goto fail; - - mount_set_state(m, state); - } else if (state == MOUNT_REMOUNTING_SIGTERM) - mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS); - else if (state == MOUNT_REMOUNTING_SIGKILL) - mount_enter_mounted(m, MOUNT_SUCCESS); - else if (state == MOUNT_MOUNTING_SIGTERM) - mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_SUCCESS); - else if (state == MOUNT_UNMOUNTING_SIGTERM) - mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS); - else - mount_enter_dead(m, MOUNT_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m"); - - if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) - mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); - else - mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); -} - -static void mount_enter_unmounting(Mount *m) { - int r; - - assert(m); - - /* Start counting our attempts */ - if (!IN_SET(m->state, - MOUNT_UNMOUNTING, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL)) - m->n_retry_umount = 0; - - m->control_command_id = MOUNT_EXEC_UNMOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; - - r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL); - if (r < 0) - goto fail; - - mount_unwatch_control_pid(m); - - r = mount_spawn(m, m->control_command, &m->control_pid); - if (r < 0) - goto fail; - - mount_set_state(m, MOUNT_UNMOUNTING); - - return; - -fail: - log_unit_warning_errno(UNIT(m), r, "Failed to run 'umount' task: %m"); - 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; - - assert(m); - - m->control_command_id = MOUNT_EXEC_MOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; - - r = unit_fail_if_symlink(UNIT(m), m->where); - if (r < 0) - goto fail; - - (void) mkdir_p_label(m->where, m->directory_mode); - - unit_warn_if_dir_nonempty(UNIT(m), m->where); - - /* Create the source directory for bind-mounts if needed */ - p = get_mount_parameters_fragment(m); - if (p && mount_is_bind(p)) - (void) mkdir_p_label(p->what, m->directory_mode); - - if (m->from_fragment) { - _cleanup_free_ char *opts = NULL; - - r = mount_get_opts(m, &opts); - if (r < 0) - goto fail; - - r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, NULL); - if (r >= 0 && m->sloppy_options) - r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); - if (r >= 0 && !isempty(opts)) - r = exec_command_append(m->control_command, "-o", opts, NULL); - } else - r = -ENOENT; - - if (r < 0) - goto fail; - - mount_unwatch_control_pid(m); - - r = mount_spawn(m, m->control_command, &m->control_pid); - if (r < 0) - goto fail; - - mount_set_state(m, MOUNT_MOUNTING); - - return; - -fail: - log_unit_warning_errno(UNIT(m), r, "Failed to run 'mount' task: %m"); - mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); -} - -static void mount_enter_remounting(Mount *m) { - int r; - - assert(m); - - m->control_command_id = MOUNT_EXEC_REMOUNT; - m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; - - if (m->from_fragment) { - const char *o; - - if (m->parameters_fragment.options) - o = strjoina("remount,", m->parameters_fragment.options); - else - o = "remount"; - - r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, - "-o", o, NULL); - if (r >= 0 && m->sloppy_options) - r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); - } else - r = -ENOENT; - - if (r < 0) - goto fail; - - mount_unwatch_control_pid(m); - - r = mount_spawn(m, m->control_command, &m->control_pid); - if (r < 0) - goto fail; - - mount_set_state(m, MOUNT_REMOUNTING); - - return; - -fail: - log_unit_warning_errno(UNIT(m), r, "Failed to run 'remount' task: %m"); - m->reload_result = MOUNT_FAILURE_RESOURCES; - mount_enter_mounted(m, MOUNT_SUCCESS); -} - -static int mount_start(Unit *u) { - Mount *m = MOUNT(u); - int r; - - assert(m); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) - return -EAGAIN; - - /* Already on it! */ - if (m->state == MOUNT_MOUNTING) - return 0; - - assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); - - r = unit_start_limit_test(u); - if (r < 0) { - mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); - return r; - } - - m->result = MOUNT_SUCCESS; - m->reload_result = MOUNT_SUCCESS; - m->reset_cpu_usage = true; - - mount_enter_mounting(m); - return 1; -} - -static int mount_stop(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - /* Already on it */ - if (m->state == MOUNT_UNMOUNTING || - m->state == MOUNT_UNMOUNTING_SIGKILL || - m->state == MOUNT_UNMOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGTERM || - m->state == MOUNT_MOUNTING_SIGKILL) - return 0; - - assert(m->state == MOUNT_MOUNTING || - m->state == MOUNT_MOUNTING_DONE || - m->state == MOUNT_MOUNTED || - m->state == MOUNT_REMOUNTING || - m->state == MOUNT_REMOUNTING_SIGTERM || - m->state == MOUNT_REMOUNTING_SIGKILL); - - mount_enter_unmounting(m); - return 1; -} - -static int mount_reload(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - if (m->state == MOUNT_MOUNTING_DONE) - return -EAGAIN; - - assert(m->state == MOUNT_MOUNTED); - - mount_enter_remounting(m); - return 1; -} - -static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { - Mount *m = MOUNT(u); - - assert(m); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", mount_state_to_string(m->state)); - unit_serialize_item(u, f, "result", mount_result_to_string(m->result)); - unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result)); - - if (m->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", PID_FMT, m->control_pid); - - if (m->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id)); - - return 0; -} - -static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Mount *m = MOUNT(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - MountState state; - - if ((state = mount_state_from_string(value)) < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - m->deserialized_state = state; - } else if (streq(key, "result")) { - MountResult f; - - f = mount_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != MOUNT_SUCCESS) - m->result = f; - - } else if (streq(key, "reload-result")) { - MountResult f; - - f = mount_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse reload result value: %s", value); - else if (f != MOUNT_SUCCESS) - m->reload_result = f; - - } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - m->control_pid = pid; - } else if (streq(key, "control-command")) { - MountExecCommand id; - - id = mount_exec_command_from_string(value); - if (id < 0) - log_unit_debug(u, "Failed to parse exec-command value: %s", value); - else { - m->control_command_id = id; - m->control_command = m->exec_command + id; - } - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState mount_active_state(Unit *u) { - assert(u); - - return state_translation_table[MOUNT(u)->state]; -} - -_pure_ static const char *mount_sub_state_to_string(Unit *u) { - assert(u); - - return mount_state_to_string(MOUNT(u)->state); -} - -_pure_ static bool mount_check_gc(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - return m->from_proc_self_mountinfo; -} - -static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Mount *m = MOUNT(u); - MountResult f; - - assert(m); - assert(pid >= 0); - - if (pid != m->control_pid) - return; - - m->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = MOUNT_SUCCESS; - else if (code == CLD_EXITED) - f = MOUNT_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = MOUNT_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = MOUNT_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (f != MOUNT_SUCCESS) - m->result = f; - - if (m->control_command) { - exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status); - - m->control_command = NULL; - m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; - } - - log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, - "Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status); - - /* Note that mount(8) returning and the kernel sending us a - * mount table change event might happen out-of-order. If an - * operation succeed we assume the kernel will follow soon too - * and already change into the resulting state. If it fails - * we check if the kernel still knows about the mount. and - * change state accordingly. */ - - switch (m->state) { - - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_MOUNTING_SIGTERM: - - if (f == MOUNT_SUCCESS) - mount_enter_mounted(m, f); - else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, f); - else - mount_enter_dead(m, f); - break; - - case MOUNT_REMOUNTING: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - - m->reload_result = f; - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_SUCCESS); - else - mount_enter_dead(m, MOUNT_SUCCESS); - - break; - - case MOUNT_UNMOUNTING: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - - if (f == MOUNT_SUCCESS) { - - if (m->from_proc_self_mountinfo) { - - /* Still a mount point? If so, let's - * try again. Most likely there were - * multiple mount points stacked on - * top of each other. Note that due to - * the io event priority logic we can - * be sure the new mountinfo is loaded - * before we process the SIGCHLD for - * the mount command. */ - - if (m->n_retry_umount < RETRY_UMOUNT_MAX) { - log_unit_debug(u, "Mount still present, trying again."); - m->n_retry_umount++; - mount_enter_unmounting(m); - } else { - log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount); - mount_enter_mounted(m, f); - } - } else - mount_enter_dead(m, f); - - } else if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, f); - else - mount_enter_dead(m, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - Mount *m = MOUNT(userdata); - - assert(m); - assert(m->timer_event_source == source); - - switch (m->state) { - - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - log_unit_warning(UNIT(m), "Mounting timed out. Stopping."); - mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); - break; - - case MOUNT_REMOUNTING: - log_unit_warning(UNIT(m), "Remounting timed out. Stopping."); - m->reload_result = MOUNT_FAILURE_TIMEOUT; - mount_enter_mounted(m, MOUNT_SUCCESS); - break; - - case MOUNT_UNMOUNTING: - log_unit_warning(UNIT(m), "Unmounting timed out. Stopping."); - mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); - break; - - case MOUNT_MOUNTING_SIGTERM: - if (m->kill_context.send_sigkill) { - log_unit_warning(UNIT(m), "Mounting timed out. Killing."); - mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(m), "Mounting timed out. Skipping SIGKILL. Ignoring."); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_REMOUNTING_SIGTERM: - if (m->kill_context.send_sigkill) { - log_unit_warning(UNIT(m), "Remounting timed out. Killing."); - mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring."); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_UNMOUNTING_SIGTERM: - if (m->kill_context.send_sigkill) { - log_unit_warning(UNIT(m), "Unmounting timed out. Killing."); - mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(m), "Unmounting timed out. Skipping SIGKILL. Ignoring."); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - } - break; - - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGKILL: - log_unit_warning(UNIT(m),"Mount process still around after SIGKILL. Ignoring."); - - if (m->from_proc_self_mountinfo) - mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); - else - mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -static int mount_setup_unit( - Manager *m, - const char *what, - const char *where, - const char *options, - const char *fstype, - bool set_flags) { - - _cleanup_free_ char *e = NULL, *w = NULL, *o = NULL, *f = NULL; - bool load_extras = false; - MountParameters *p; - bool delete, changed = false; - Unit *u; - int r; - - assert(m); - assert(what); - assert(where); - assert(options); - assert(fstype); - - /* Ignore API mount points. They should never be referenced in - * dependencies ever. */ - if (mount_point_is_api(where) || mount_point_ignore(where)) - return 0; - - if (streq(fstype, "autofs")) - return 0; - - /* probably some kind of swap, ignore */ - if (!is_path(where)) - return 0; - - r = unit_name_from_path(where, ".mount", &e); - if (r < 0) - return r; - - u = manager_get_unit(m, e); - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Mount)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); - if (r < 0) - goto fail; - - MOUNT(u)->where = strdup(where); - if (!MOUNT(u)->where) { - r = -ENOMEM; - goto fail; - } - - u->source_path = strdup("/proc/self/mountinfo"); - if (!u->source_path) { - r = -ENOMEM; - goto fail; - } - - if (MANAGER_IS_SYSTEM(m)) { - const char* target; - - target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; - r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true); - if (r < 0) - goto fail; - - if (should_umount(MOUNT(u))) { - r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); - if (r < 0) - goto fail; - } - } - - unit_add_to_load_queue(u); - changed = true; - } else { - delete = false; - - if (!MOUNT(u)->where) { - MOUNT(u)->where = strdup(where); - if (!MOUNT(u)->where) { - r = -ENOMEM; - goto fail; - } - } - - if (MANAGER_IS_SYSTEM(m) && - mount_needs_network(options, fstype)) { - /* _netdev option may have shown up late, or on a - * remount. Add remote-fs dependencies, even though - * local-fs ones may already be there. */ - unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true); - load_extras = true; - } - - if (u->load_state == UNIT_NOT_FOUND) { - u->load_state = UNIT_LOADED; - u->load_error = 0; - - /* Load in the extras later on, after we - * finished initialization of the unit */ - load_extras = true; - changed = true; - } - } - - w = strdup(what); - o = strdup(options); - f = strdup(fstype); - if (!w || !o || !f) { - r = -ENOMEM; - goto fail; - } - - p = &MOUNT(u)->parameters_proc_self_mountinfo; - - changed = changed || - !streq_ptr(p->options, options) || - !streq_ptr(p->what, what) || - !streq_ptr(p->fstype, fstype); - - if (set_flags) { - MOUNT(u)->is_mounted = true; - MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; - MOUNT(u)->just_changed = changed; - } - - MOUNT(u)->from_proc_self_mountinfo = true; - - free(p->what); - p->what = w; - w = NULL; - - free(p->options); - p->options = o; - o = NULL; - - free(p->fstype); - p->fstype = f; - f = NULL; - - if (load_extras) { - r = mount_add_extras(MOUNT(u)); - if (r < 0) - goto fail; - } - - if (changed) - unit_add_to_dbus_queue(u); - - return 0; - -fail: - log_warning_errno(r, "Failed to set up mount unit: %m"); - - if (delete && u) - unit_free(u); - - return r; -} - -static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { - _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; - _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; - int r = 0; - - assert(m); - - t = mnt_new_table(); - if (!t) - return log_oom(); - - i = mnt_new_iter(MNT_ITER_FORWARD); - if (!i) - return log_oom(); - - r = mnt_table_parse_mtab(t, NULL); - if (r < 0) - return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); - - r = 0; - for (;;) { - const char *device, *path, *options, *fstype; - _cleanup_free_ char *d = NULL, *p = NULL; - struct libmnt_fs *fs; - int k; - - k = mnt_table_next_fs(t, i, &fs); - if (k == 1) - break; - if (k < 0) - return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m"); - - device = mnt_fs_get_source(fs); - path = mnt_fs_get_target(fs); - options = mnt_fs_get_options(fs); - fstype = mnt_fs_get_fstype(fs); - - if (!device || !path) - continue; - - if (cunescape(device, UNESCAPE_RELAX, &d) < 0) - return log_oom(); - - if (cunescape(path, UNESCAPE_RELAX, &p) < 0) - return log_oom(); - - (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags); - - k = mount_setup_unit(m, d, p, options, fstype, set_flags); - if (r == 0 && k < 0) - r = k; - } - - return r; -} - -static void mount_shutdown(Manager *m) { - - assert(m); - - m->mount_event_source = sd_event_source_unref(m->mount_event_source); - - mnt_unref_monitor(m->mount_monitor); - m->mount_monitor = NULL; -} - -static int mount_get_timeout(Unit *u, usec_t *timeout) { - Mount *m = MOUNT(u); - usec_t t; - int r; - - if (!m->timer_event_source) - return 0; - - r = sd_event_source_get_time(m->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -static void mount_enumerate(Manager *m) { - int r; - - assert(m); - - mnt_init_debug(0); - - if (!m->mount_monitor) { - int fd; - - m->mount_monitor = mnt_new_monitor(); - if (!m->mount_monitor) { - log_oom(); - goto fail; - } - - r = mnt_monitor_enable_kernel(m->mount_monitor, 1); - 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) { - 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) { - 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) { - 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) { - 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"); - } - - r = mount_load_proc_self_mountinfo(m, false); - if (r < 0) - goto fail; - - return; - -fail: - mount_shutdown(m); -} - -static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - _cleanup_set_free_ Set *around = NULL, *gone = NULL; - Manager *m = userdata; - const char *what; - Iterator i; - Unit *u; - int r; - - assert(m); - assert(revents & EPOLLIN); - - if (fd == mnt_monitor_get_fd(m->mount_monitor)) { - bool rescan = false; - - /* Drain all events and verify that the event is valid. - * - * Note that libmount also monitors /run/mount mkdir if the - * directory does not exist yet. The mkdir may generate event - * which is irrelevant for us. - * - * error: r < 0; valid: r == 0, false positive: rc == 1 */ - do { - r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); - if (r == 0) - rescan = true; - else if (r < 0) - return log_error_errno(r, "Failed to drain libmount events"); - } while (r == 0); - - log_debug("libmount event [rescan: %s]", yes_no(rescan)); - if (!rescan) - return 0; - } - - r = mount_load_proc_self_mountinfo(m, true); - if (r < 0) { - /* Reset flags, just in case, for later calls */ - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = MOUNT(u); - - mount->is_mounted = mount->just_mounted = mount->just_changed = false; - } - - return 0; - } - - manager_dispatch_load_queue(m); - - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { - Mount *mount = MOUNT(u); - - if (!mount->is_mounted) { - - /* A mount point is not around right now. It - * might be gone, or might never have - * existed. */ - - if (mount->from_proc_self_mountinfo && - mount->parameters_proc_self_mountinfo.what) { - - /* Remember that this device might just have disappeared */ - if (set_ensure_allocated(&gone, &string_hash_ops) < 0 || - set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0) - log_oom(); /* we don't care too much about OOM here... */ - } - - mount->from_proc_self_mountinfo = false; - - switch (mount->state) { - - case MOUNT_MOUNTED: - /* This has just been unmounted by - * somebody else, follow the state - * change. */ - mount->result = MOUNT_SUCCESS; /* make sure we forget any earlier umount failures */ - mount_enter_dead(mount, MOUNT_SUCCESS); - break; - - default: - break; - } - - } else if (mount->just_mounted || mount->just_changed) { - - /* A mount point was added or changed */ - - switch (mount->state) { - - case MOUNT_DEAD: - case MOUNT_FAILED: - /* This has just been mounted by - * somebody else, follow the state - * change. */ - mount_enter_mounted(mount, MOUNT_SUCCESS); - break; - - case MOUNT_MOUNTING: - mount_set_state(mount, MOUNT_MOUNTING_DONE); - break; - - default: - /* Nothing really changed, but let's - * issue an notification call - * nonetheless, in case somebody is - * waiting for this. (e.g. file system - * ro/rw remounts.) */ - mount_set_state(mount, mount->state); - break; - } - } - - if (mount->is_mounted && - mount->from_proc_self_mountinfo && - mount->parameters_proc_self_mountinfo.what) { - - if (set_ensure_allocated(&around, &string_hash_ops) < 0 || - set_put(around, mount->parameters_proc_self_mountinfo.what) < 0) - log_oom(); - } - - /* Reset the flags for later calls */ - mount->is_mounted = mount->just_mounted = mount->just_changed = false; - } - - SET_FOREACH(what, gone, i) { - if (set_contains(around, what)) - continue; - - /* Let the device units know that the device is no longer mounted */ - (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true); - } - - return 0; -} - -static void mount_reset_failed(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - if (m->state == MOUNT_FAILED) - mount_set_state(m, MOUNT_DEAD); - - m->result = MOUNT_SUCCESS; - m->reload_result = MOUNT_SUCCESS; -} - -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 int mount_control_pid(Unit *u) { - Mount *m = MOUNT(u); - - assert(m); - - return m->control_pid; -} - -static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { - [MOUNT_EXEC_MOUNT] = "ExecMount", - [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", - [MOUNT_EXEC_REMOUNT] = "ExecRemount", -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand); - -static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { - [MOUNT_SUCCESS] = "success", - [MOUNT_FAILURE_RESOURCES] = "resources", - [MOUNT_FAILURE_TIMEOUT] = "timeout", - [MOUNT_FAILURE_EXIT_CODE] = "exit-code", - [MOUNT_FAILURE_SIGNAL] = "signal", - [MOUNT_FAILURE_CORE_DUMP] = "core-dump", - [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); - -const UnitVTable mount_vtable = { - .object_size = sizeof(Mount), - .exec_context_offset = offsetof(Mount, exec_context), - .cgroup_context_offset = offsetof(Mount, cgroup_context), - .kill_context_offset = offsetof(Mount, kill_context), - .exec_runtime_offset = offsetof(Mount, exec_runtime), - - .sections = - "Unit\0" - "Mount\0" - "Install\0", - .private_section = "Mount", - - .init = mount_init, - .load = mount_load, - .done = mount_done, - - .coldplug = mount_coldplug, - - .dump = mount_dump, - - .start = mount_start, - .stop = mount_stop, - .reload = mount_reload, - - .kill = mount_kill, - - .serialize = mount_serialize, - .deserialize_item = mount_deserialize_item, - - .active_state = mount_active_state, - .sub_state_to_string = mount_sub_state_to_string, - - .check_gc = mount_check_gc, - - .sigchld_event = mount_sigchld_event, - - .reset_failed = mount_reset_failed, - - .control_pid = mount_control_pid, - - .bus_vtable = bus_mount_vtable, - .bus_set_property = bus_mount_set_property, - .bus_commit_properties = bus_mount_commit_properties, - - .get_timeout = mount_get_timeout, - - .can_transient = true, - - .enumerate = mount_enumerate, - .shutdown = mount_shutdown, - - .status_message_formats = { - .starting_stopping = { - [0] = "Mounting %s...", - [1] = "Unmounting %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Mounted %s.", - [JOB_FAILED] = "Failed to mount %s.", - [JOB_TIMEOUT] = "Timed out mounting %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Unmounted %s.", - [JOB_FAILED] = "Failed unmounting %s.", - [JOB_TIMEOUT] = "Timed out unmounting %s.", - }, - }, -}; diff --git a/src/core/mount.h b/src/core/mount.h deleted file mode 100644 index da529c44f4..0000000000 --- a/src/core/mount.h +++ /dev/null @@ -1,108 +0,0 @@ -#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 . -***/ - -typedef struct Mount Mount; - -#include "execute.h" -#include "kill.h" - -typedef enum MountExecCommand { - MOUNT_EXEC_MOUNT, - MOUNT_EXEC_UNMOUNT, - MOUNT_EXEC_REMOUNT, - _MOUNT_EXEC_COMMAND_MAX, - _MOUNT_EXEC_COMMAND_INVALID = -1 -} MountExecCommand; - -typedef enum MountResult { - MOUNT_SUCCESS, - MOUNT_FAILURE_RESOURCES, - MOUNT_FAILURE_TIMEOUT, - MOUNT_FAILURE_EXIT_CODE, - MOUNT_FAILURE_SIGNAL, - MOUNT_FAILURE_CORE_DUMP, - MOUNT_FAILURE_START_LIMIT_HIT, - _MOUNT_RESULT_MAX, - _MOUNT_RESULT_INVALID = -1 -} MountResult; - -typedef struct MountParameters { - char *what; - char *options; - char *fstype; -} MountParameters; - -struct Mount { - Unit meta; - - char *where; - - MountParameters parameters_proc_self_mountinfo; - MountParameters parameters_fragment; - - bool from_proc_self_mountinfo:1; - bool from_fragment:1; - - /* Used while looking for mount points that vanished or got - * added from/to /proc/self/mountinfo */ - bool is_mounted:1; - bool just_mounted:1; - bool just_changed:1; - - bool reset_cpu_usage:1; - - bool sloppy_options; - - MountResult result; - MountResult reload_result; - - mode_t directory_mode; - - usec_t timeout_usec; - - ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX]; - - ExecContext exec_context; - KillContext kill_context; - CGroupContext cgroup_context; - - ExecRuntime *exec_runtime; - - MountState state, deserialized_state; - - ExecCommand* control_command; - MountExecCommand control_command_id; - pid_t control_pid; - - sd_event_source *timer_event_source; - - unsigned n_retry_umount; -}; - -extern const UnitVTable mount_vtable; - -void mount_fd_event(Manager *m, int events); - -const char* mount_exec_command_to_string(MountExecCommand i) _const_; -MountExecCommand mount_exec_command_from_string(const char *s) _pure_; - -const char* mount_result_to_string(MountResult i) _const_; -MountResult mount_result_from_string(const char *s) _pure_; diff --git a/src/core/namespace.c b/src/core/namespace.c deleted file mode 100644 index 52a2505d94..0000000000 --- a/src/core/namespace.c +++ /dev/null @@ -1,663 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "dev-setup.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" - -#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC) - -typedef enum MountMode { - /* This is ordered by priority! */ - INACCESSIBLE, - READONLY, - PRIVATE_TMP, - PRIVATE_VAR_TMP, - PRIVATE_DEV, - READWRITE -} MountMode; - -typedef struct BindMount { - const char *path; - MountMode mode; - bool done; - bool ignore; -} BindMount; - -static int append_mounts(BindMount **p, char **strv, MountMode mode) { - char **i; - - assert(p); - - STRV_FOREACH(i, strv) { - - (*p)->ignore = false; - (*p)->done = false; - - if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') { - (*p)->ignore = true; - (*i)++; - } - - if (!path_is_absolute(*i)) - return -EINVAL; - - (*p)->path = *i; - (*p)->mode = mode; - (*p)++; - } - - return 0; -} - -static int mount_path_compare(const void *a, const void *b) { - const BindMount *p = a, *q = b; - int d; - - d = path_compare(p->path, q->path); - - if (d == 0) { - /* If the paths are equal, check the mode */ - if (p->mode < q->mode) - return -1; - - if (p->mode > q->mode) - return 1; - - return 0; - } - - /* If the paths are not equal, then order prefixes first */ - return d; -} - -static void drop_duplicates(BindMount *m, unsigned *n) { - BindMount *f, *t, *previous; - - assert(m); - assert(n); - - for (f = m, t = m, previous = NULL; f < m+*n; f++) { - - /* The first one wins */ - if (previous && path_equal(f->path, previous->path)) - continue; - - *t = *f; - - previous = t; - - t++; - } - - *n = t - m; -} - -static int mount_dev(BindMount *m) { - static const char devnodes[] = - "/dev/null\0" - "/dev/zero\0" - "/dev/full\0" - "/dev/random\0" - "/dev/urandom\0" - "/dev/tty\0"; - - char temporary_mount[] = "/tmp/namespace-dev-XXXXXX"; - const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL; - _cleanup_umask_ mode_t u; - int r; - - assert(m); - - u = umask(0000); - - if (!mkdtemp(temporary_mount)) - return -errno; - - dev = strjoina(temporary_mount, "/dev"); - (void) mkdir(dev, 0755); - if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) { - r = -errno; - goto fail; - } - - devpts = strjoina(temporary_mount, "/dev/pts"); - (void) mkdir(devpts, 0755); - if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) { - r = -errno; - goto fail; - } - - devptmx = strjoina(temporary_mount, "/dev/ptmx"); - if (symlink("pts/ptmx", devptmx) < 0) { - r = -errno; - goto fail; - } - - devshm = strjoina(temporary_mount, "/dev/shm"); - (void) mkdir(devshm, 01777); - r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL); - if (r < 0) { - r = -errno; - goto fail; - } - - devmqueue = strjoina(temporary_mount, "/dev/mqueue"); - (void) mkdir(devmqueue, 0755); - (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL); - - devhugepages = strjoina(temporary_mount, "/dev/hugepages"); - (void) mkdir(devhugepages, 0755); - (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL); - - devlog = strjoina(temporary_mount, "/dev/log"); - (void) symlink("/run/systemd/journal/dev-log", devlog); - - NULSTR_FOREACH(d, devnodes) { - _cleanup_free_ char *dn = NULL; - struct stat st; - - r = stat(d, &st); - if (r < 0) { - - if (errno == ENOENT) - continue; - - r = -errno; - goto fail; - } - - if (!S_ISBLK(st.st_mode) && - !S_ISCHR(st.st_mode)) { - r = -EINVAL; - goto fail; - } - - if (st.st_rdev == 0) - continue; - - dn = strappend(temporary_mount, d); - if (!dn) { - r = -ENOMEM; - goto fail; - } - - mac_selinux_create_file_prepare(d, st.st_mode); - r = mknod(dn, st.st_mode, st.st_rdev); - mac_selinux_create_file_clear(); - - if (r < 0) { - r = -errno; - goto fail; - } - } - - dev_setup(temporary_mount, UID_INVALID, GID_INVALID); - - /* Create the /dev directory if missing. It is more likely to be - * missing when the service is started with RootDirectory. This is - * consistent with mount units creating the mount points when missing. - */ - (void) mkdir_p_label(m->path, 0755); - - /* Unmount everything in old /dev */ - umount_recursive(m->path, 0); - if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) { - r = -errno; - goto fail; - } - - rmdir(dev); - rmdir(temporary_mount); - - return 0; - -fail: - if (devpts) - umount(devpts); - - if (devshm) - umount(devshm); - - if (devhugepages) - umount(devhugepages); - - if (devmqueue) - umount(devmqueue); - - umount(dev); - rmdir(dev); - rmdir(temporary_mount); - - return r; -} - -static int apply_mount( - BindMount *m, - const char *tmp_dir, - const char *var_tmp_dir) { - - const char *what; - int r; - struct stat target; - - assert(m); - - switch (m->mode) { - - case INACCESSIBLE: - - /* First, get rid of everything that is below if there - * is anything... Then, overmount it with an - * inaccessible path. */ - umount_recursive(m->path, 0); - - if (lstat(m->path, &target) < 0) { - if (m->ignore && errno == ENOENT) - return 0; - return -errno; - } - - what = mode_to_inaccessible_node(target.st_mode); - if (!what) { - log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed"); - return -ELOOP; - } - break; - case READONLY: - case READWRITE: - /* Nothing to mount here, we just later toggle the - * MS_RDONLY bit for the mount point */ - return 0; - - case PRIVATE_TMP: - what = tmp_dir; - break; - - case PRIVATE_VAR_TMP: - what = var_tmp_dir; - break; - - case PRIVATE_DEV: - return mount_dev(m); - - default: - assert_not_reached("Unknown mode"); - } - - assert(what); - - r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); - if (r >= 0) { - log_debug("Successfully mounted %s to %s", what, m->path); - return r; - } else { - if (m->ignore && errno == ENOENT) - return 0; - return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path); - } -} - -static int make_read_only(BindMount *m) { - int r; - - assert(m); - - if (IN_SET(m->mode, INACCESSIBLE, READONLY)) - r = bind_remount_recursive(m->path, true); - else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) { - r = bind_remount_recursive(m->path, false); - if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/ - if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) - r = -errno; - } else - r = 0; - - if (m->ignore && r == -ENOENT) - return 0; - - return r; -} - -int setup_namespace( - const char* root_directory, - char** read_write_paths, - char** read_only_paths, - char** inaccessible_paths, - const char* tmp_dir, - const char* var_tmp_dir, - bool private_dev, - ProtectHome protect_home, - ProtectSystem protect_system, - unsigned long mount_flags) { - - BindMount *m, *mounts = NULL; - unsigned n; - int r = 0; - - if (mount_flags == 0) - mount_flags = MS_SHARED; - - if (unshare(CLONE_NEWNS) < 0) - return -errno; - - n = !!tmp_dir + !!var_tmp_dir + - strv_length(read_write_paths) + - strv_length(read_only_paths) + - strv_length(inaccessible_paths) + - private_dev + - (protect_home != PROTECT_HOME_NO ? 3 : 0) + - (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) + - (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0); - - if (n > 0) { - m = mounts = (BindMount *) alloca0(n * sizeof(BindMount)); - r = append_mounts(&m, read_write_paths, READWRITE); - if (r < 0) - return r; - - r = append_mounts(&m, read_only_paths, READONLY); - if (r < 0) - return r; - - r = append_mounts(&m, inaccessible_paths, INACCESSIBLE); - if (r < 0) - return r; - - if (tmp_dir) { - m->path = prefix_roota(root_directory, "/tmp"); - m->mode = PRIVATE_TMP; - m++; - } - - if (var_tmp_dir) { - m->path = prefix_roota(root_directory, "/var/tmp"); - m->mode = PRIVATE_VAR_TMP; - m++; - } - - if (private_dev) { - m->path = prefix_roota(root_directory, "/dev"); - m->mode = PRIVATE_DEV; - m++; - } - - if (protect_home != PROTECT_HOME_NO) { - const char *home_dir, *run_user_dir, *root_dir; - - home_dir = prefix_roota(root_directory, "/home"); - home_dir = strjoina("-", home_dir); - run_user_dir = prefix_roota(root_directory, "/run/user"); - run_user_dir = strjoina("-", run_user_dir); - root_dir = prefix_roota(root_directory, "/root"); - root_dir = strjoina("-", root_dir); - - r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir), - protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE); - if (r < 0) - return r; - } - - if (protect_system != PROTECT_SYSTEM_NO) { - const char *usr_dir, *boot_dir, *etc_dir; - - usr_dir = prefix_roota(root_directory, "/usr"); - boot_dir = prefix_roota(root_directory, "/boot"); - boot_dir = strjoina("-", boot_dir); - etc_dir = prefix_roota(root_directory, "/etc"); - - r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL - ? STRV_MAKE(usr_dir, boot_dir, etc_dir) - : STRV_MAKE(usr_dir, boot_dir), READONLY); - if (r < 0) - return r; - } - - assert(mounts + n == m); - - qsort(mounts, n, sizeof(BindMount), mount_path_compare); - drop_duplicates(mounts, &n); - } - - if (n > 0 || root_directory) { - /* Remount / as SLAVE so that nothing now mounted in the namespace - shows up in the parent */ - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - return -errno; - } - - if (root_directory) { - /* Turn directory into bind mount */ - if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) - return -errno; - } - - if (n > 0) { - for (m = mounts; m < mounts + n; ++m) { - r = apply_mount(m, tmp_dir, var_tmp_dir); - if (r < 0) - goto fail; - } - - for (m = mounts; m < mounts + n; ++m) { - r = make_read_only(m); - if (r < 0) - goto fail; - } - } - - if (root_directory) { - /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ - r = mount_move_root(root_directory); - - /* at this point, we cannot rollback */ - if (r < 0) - return r; - } - - /* Remount / as the desired mode. Not that this will not - * reestablish propagation from our side to the host, since - * what's disconnected is disconnected. */ - if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) - /* at this point, we cannot rollback */ - return -errno; - - return 0; - -fail: - if (n > 0) { - for (m = mounts; m < mounts + n; ++m) - if (m->done) - (void) umount2(m->path, MNT_DETACH); - } - - return r; -} - -static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { - _cleanup_free_ char *x = NULL; - char bid[SD_ID128_STRING_MAX]; - sd_id128_t boot_id; - int r; - - assert(id); - assert(prefix); - assert(path); - - /* We include the boot id in the directory so that after a - * reboot we can easily identify obsolete directories. */ - - r = sd_id128_get_boot(&boot_id); - if (r < 0) - return r; - - x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL); - if (!x) - return -ENOMEM; - - RUN_WITH_UMASK(0077) - if (!mkdtemp(x)) - return -errno; - - RUN_WITH_UMASK(0000) { - char *y; - - y = strjoina(x, "/tmp"); - - if (mkdir(y, 0777 | S_ISVTX) < 0) - return -errno; - } - - *path = x; - x = NULL; - - return 0; -} - -int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { - char *a, *b; - int r; - - assert(id); - assert(tmp_dir); - assert(var_tmp_dir); - - r = setup_one_tmp_dir(id, "/tmp", &a); - if (r < 0) - return r; - - r = setup_one_tmp_dir(id, "/var/tmp", &b); - if (r < 0) { - char *t; - - t = strjoina(a, "/tmp"); - rmdir(t); - rmdir(a); - - free(a); - return r; - } - - *tmp_dir = a; - *var_tmp_dir = b; - - return 0; -} - -int setup_netns(int netns_storage_socket[2]) { - _cleanup_close_ int netns = -1; - int r, q; - - assert(netns_storage_socket); - assert(netns_storage_socket[0] >= 0); - assert(netns_storage_socket[1] >= 0); - - /* We use the passed socketpair as a storage buffer for our - * namespace reference fd. Whatever process runs this first - * shall create a new namespace, all others should just join - * it. To serialize that we use a file lock on the socket - * pair. - * - * It's a bit crazy, but hey, works great! */ - - if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0) - return -errno; - - 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) { - r = -errno; - goto fail; - } - - loopback_setup(); - - netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (netns < 0) { - r = -errno; - goto fail; - } - - r = 1; - - } 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; - } - - r = 0; - } - - q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT); - if (q < 0) { - r = q; - goto fail; - } - -fail: - (void) lockf(netns_storage_socket[0], F_ULOCK, 0); - return r; -} - -static const char *const protect_home_table[_PROTECT_HOME_MAX] = { - [PROTECT_HOME_NO] = "no", - [PROTECT_HOME_YES] = "yes", - [PROTECT_HOME_READ_ONLY] = "read-only", -}; - -DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome); - -static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { - [PROTECT_SYSTEM_NO] = "no", - [PROTECT_SYSTEM_YES] = "yes", - [PROTECT_SYSTEM_FULL] = "full", -}; - -DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem); diff --git a/src/core/namespace.h b/src/core/namespace.h deleted file mode 100644 index 1aedf5f208..0000000000 --- a/src/core/namespace.h +++ /dev/null @@ -1,63 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -typedef enum ProtectHome { - PROTECT_HOME_NO, - PROTECT_HOME_YES, - PROTECT_HOME_READ_ONLY, - _PROTECT_HOME_MAX, - _PROTECT_HOME_INVALID = -1 -} ProtectHome; - -typedef enum ProtectSystem { - PROTECT_SYSTEM_NO, - PROTECT_SYSTEM_YES, - PROTECT_SYSTEM_FULL, - _PROTECT_SYSTEM_MAX, - _PROTECT_SYSTEM_INVALID = -1 -} ProtectSystem; - -int setup_namespace(const char *chroot, - char **read_write_paths, - char **read_only_paths, - char **inaccessible_paths, - const char *tmp_dir, - const char *var_tmp_dir, - bool private_dev, - ProtectHome protect_home, - ProtectSystem protect_system, - unsigned long mount_flags); - -int setup_tmp_dirs(const char *id, - char **tmp_dir, - char **var_tmp_dir); - -int setup_netns(int netns_storage_socket[2]); - -const char* protect_home_to_string(ProtectHome p) _const_; -ProtectHome protect_home_from_string(const char *s) _pure_; - -const char* protect_system_to_string(ProtectSystem p) _const_; -ProtectSystem protect_system_from_string(const char *s) _pure_; diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf deleted file mode 100644 index 3c64f20872..0000000000 --- a/src/core/org.freedesktop.systemd1.conf +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/core/org.freedesktop.systemd1.policy.in.in b/src/core/org.freedesktop.systemd1.policy.in.in deleted file mode 100644 index cc39a9e1c3..0000000000 --- a/src/core/org.freedesktop.systemd1.policy.in.in +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Send passphrase back to system - <_message>Authentication is required to send the entered passphrase back to the system. - - no - no - auth_admin_keep - - @rootlibexecdir@/systemd-reply-password - - - - <_description>Manage system services or other units - <_message>Authentication is required to manage system services or other units. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Manage system service or unit files - <_message>Authentication is required to manage system service or unit files. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Set or unset system and service manager environment variables - <_message>Authentication is required to set or unset system and service manager environment variables. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Reload the systemd state - <_message>Authentication is required to reload the systemd state. - - auth_admin - auth_admin - auth_admin_keep - - - - diff --git a/src/core/org.freedesktop.systemd1.service b/src/core/org.freedesktop.systemd1.service deleted file mode 100644 index d4df3e93a2..0000000000 --- a/src/core/org.freedesktop.systemd1.service +++ /dev/null @@ -1,11 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.systemd1 -Exec=/bin/false -User=root diff --git a/src/core/path.c b/src/core/path.c deleted file mode 100644 index 0dd0d375d8..0000000000 --- a/src/core/path.c +++ /dev/null @@ -1,784 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "bus-error.h" -#include "bus-util.h" -#include "dbus-path.h" -#include "fd-util.h" -#include "fs-util.h" -#include "glob-util.h" -#include "macro.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, - [PATH_WAITING] = UNIT_ACTIVE, - [PATH_RUNNING] = UNIT_ACTIVE, - [PATH_FAILED] = UNIT_FAILED -}; - -static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); - -int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { - - static const int flags_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, - [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, - [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, - [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO - }; - - bool exists = false; - char *slash, *oldslash = NULL; - int r; - - assert(s); - assert(s->unit); - assert(handler); - - path_spec_unwatch(s); - - s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (s->inotify_fd < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(s->event_source, "path"); - - /* This assumes the path was passed through path_kill_slashes()! */ - - for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { - char *cut = NULL; - int flags; - char tmp; - - if (slash) { - cut = slash + (slash == s->path); - tmp = *cut; - *cut = '\0'; - - flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; - } else - flags = flags_table[s->type]; - - r = inotify_add_watch(s->inotify_fd, s->path, flags); - if (r < 0) { - if (errno == EACCES || errno == ENOENT) { - if (cut) - *cut = tmp; - break; - } - - r = log_warning_errno(errno, "Failed to add watch on %s: %s", s->path, errno == ENOSPC ? "too many watches" : strerror(-r)); - if (cut) - *cut = tmp; - goto fail; - } else { - exists = true; - - /* Path exists, we don't need to watch parent too closely. */ - if (oldslash) { - char *cut2 = oldslash + (oldslash == s->path); - char tmp2 = *cut2; - *cut2 = '\0'; - - (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); - /* Error is ignored, the worst can happen is we get spurious events. */ - - *cut2 = tmp2; - } - } - - if (cut) - *cut = tmp; - - if (slash) - oldslash = slash; - else { - /* whole path has been iterated over */ - s->primary_wd = r; - break; - } - } - - if (!exists) { - r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path); - /* either EACCESS or ENOENT */ - goto fail; - } - - return 0; - -fail: - path_spec_unwatch(s); - return r; -} - -void path_spec_unwatch(PathSpec *s) { - assert(s); - - s->event_source = sd_event_source_unref(s->event_source); - s->inotify_fd = safe_close(s->inotify_fd); -} - -int path_spec_fd_event(PathSpec *s, uint32_t revents) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - int r = 0; - - if (revents != EPOLLIN) { - log_error("Got invalid poll event on inotify."); - return -EINVAL; - } - - l = read(s->inotify_fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_error_errno(errno, "Failed to read inotify event: %m"); - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && - s->primary_wd == e->wd) - r = 1; - } - - return r; -} - -static bool path_spec_check_good(PathSpec *s, bool initial) { - bool good = false; - - switch (s->type) { - - case PATH_EXISTS: - good = access(s->path, F_OK) >= 0; - break; - - case PATH_EXISTS_GLOB: - good = glob_exists(s->path) > 0; - break; - - case PATH_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(s->path); - good = !(k == -ENOENT || k > 0); - break; - } - - case PATH_CHANGED: - case PATH_MODIFIED: { - bool b; - - b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; - s->previous_exists = b; - break; - } - - default: - ; - } - - return good; -} - -static void path_spec_mkdir(PathSpec *s, mode_t mode) { - int r; - - if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) - return; - - r = mkdir_p_label(s->path, mode); - if (r < 0) - log_warning_errno(r, "mkdir(%s) failed: %m", s->path); -} - -static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) { - fprintf(f, - "%s%s: %s\n", - prefix, - path_type_to_string(s->type), - s->path); -} - -void path_spec_done(PathSpec *s) { - assert(s); - assert(s->inotify_fd == -1); - - free(s->path); -} - -static void path_init(Unit *u) { - Path *p = PATH(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - p->directory_mode = 0755; -} - -void path_free_specs(Path *p) { - PathSpec *s; - - assert(p); - - while ((s = p->specs)) { - path_spec_unwatch(s); - LIST_REMOVE(spec, p->specs, s); - path_spec_done(s); - free(s); - } -} - -static void path_done(Unit *u) { - Path *p = PATH(u); - - assert(p); - - path_free_specs(p); -} - -static int path_add_mount_links(Path *p) { - PathSpec *s; - int r; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) { - r = unit_require_mounts_for(UNIT(p), s->path); - if (r < 0) - return r; - } - - return 0; -} - -static int path_verify(Path *p) { - assert(p); - - if (UNIT(p)->load_state != UNIT_LOADED) - return 0; - - if (!p->specs) { - log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int path_add_default_dependencies(Path *p) { - int r; - - assert(p); - - 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 (MANAGER_IS_SYSTEM(UNIT(p)->manager)) { - 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); -} - -static int path_load(Unit *u) { - Path *p = PATH(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - r = unit_load_fragment_and_dropin(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - - if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); - if (r < 0) - return r; - } - - r = path_add_mount_links(p); - if (r < 0) - return r; - - r = path_add_default_dependencies(p); - if (r < 0) - return r; - } - - return path_verify(p); -} - -static void path_dump(Unit *u, FILE *f, const char *prefix) { - Path *p = PATH(u); - Unit *trigger; - PathSpec *s; - - assert(p); - assert(f); - - trigger = UNIT_TRIGGER(u); - - fprintf(f, - "%sPath State: %s\n" - "%sResult: %s\n" - "%sUnit: %s\n" - "%sMakeDirectory: %s\n" - "%sDirectoryMode: %04o\n", - prefix, path_state_to_string(p->state), - prefix, path_result_to_string(p->result), - prefix, trigger ? trigger->id : "n/a", - prefix, yes_no(p->make_directory), - prefix, p->directory_mode); - - LIST_FOREACH(spec, s, p->specs) - path_spec_dump(s, f, prefix); -} - -static void path_unwatch(Path *p) { - PathSpec *s; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) - path_spec_unwatch(s); -} - -static int path_watch(Path *p) { - int r; - PathSpec *s; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) { - r = path_spec_watch(s, path_dispatch_io); - if (r < 0) - return r; - } - - return 0; -} - -static void path_set_state(Path *p, PathState state) { - PathState old_state; - assert(p); - - old_state = p->state; - p->state = state; - - if (state != PATH_WAITING && - (state != PATH_RUNNING || p->inotify_triggered)) - path_unwatch(p); - - if (state != old_state) - log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state)); - - unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true); -} - -static void path_enter_waiting(Path *p, bool initial, bool recheck); - -static int path_coldplug(Unit *u) { - Path *p = PATH(u); - - assert(p); - assert(p->state == PATH_DEAD); - - if (p->deserialized_state != p->state) { - - if (p->deserialized_state == PATH_WAITING || - p->deserialized_state == PATH_RUNNING) - path_enter_waiting(p, true, true); - else - path_set_state(p, p->deserialized_state); - } - - return 0; -} - -static void path_enter_dead(Path *p, PathResult f) { - assert(p); - - if (f != PATH_SUCCESS) - p->result = f; - - path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); -} - -static void path_enter_running(Path *p) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Unit *trigger; - int r; - - assert(p); - - /* Don't start job if we are supposed to go down */ - if (unit_stop_pending(UNIT(p))) - return; - - trigger = UNIT_TRIGGER(UNIT(p)); - if (!trigger) { - log_unit_error(UNIT(p), "Unit to trigger vanished."); - path_enter_dead(p, PATH_FAILURE_RESOURCES); - return; - } - - r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); - if (r < 0) - goto fail; - - p->inotify_triggered = false; - - r = path_watch(p); - if (r < 0) - goto fail; - - path_set_state(p, PATH_RUNNING); - return; - -fail: - log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r)); - path_enter_dead(p, PATH_FAILURE_RESOURCES); -} - -static bool path_check_good(Path *p, bool initial) { - PathSpec *s; - bool good = false; - - assert(p); - - LIST_FOREACH(spec, s, p->specs) { - good = path_spec_check_good(s, initial); - - if (good) - break; - } - - return good; -} - -static void path_enter_waiting(Path *p, bool initial, bool recheck) { - int r; - - if (recheck) - if (path_check_good(p, initial)) { - log_unit_debug(UNIT(p), "Got triggered."); - path_enter_running(p); - return; - } - - r = path_watch(p); - if (r < 0) - goto fail; - - /* Hmm, so now we have created inotify watches, but the file - * might have appeared/been removed by now, so we must - * recheck */ - - if (recheck) - if (path_check_good(p, false)) { - log_unit_debug(UNIT(p), "Got triggered."); - path_enter_running(p); - return; - } - - path_set_state(p, PATH_WAITING); - return; - -fail: - log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m"); - path_enter_dead(p, PATH_FAILURE_RESOURCES); -} - -static void path_mkdir(Path *p) { - PathSpec *s; - - assert(p); - - if (!p->make_directory) - return; - - LIST_FOREACH(spec, s, p->specs) - path_spec_mkdir(s, p->directory_mode); -} - -static int path_start(Unit *u) { - Path *p = PATH(u); - Unit *trigger; - int r; - - assert(p); - assert(p->state == PATH_DEAD || p->state == PATH_FAILED); - - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } - - r = unit_start_limit_test(u); - if (r < 0) { - path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); - return r; - } - - path_mkdir(p); - - p->result = PATH_SUCCESS; - path_enter_waiting(p, true, true); - - return 1; -} - -static int path_stop(Unit *u) { - Path *p = PATH(u); - - assert(p); - assert(p->state == PATH_WAITING || p->state == PATH_RUNNING); - - path_enter_dead(p, PATH_SUCCESS); - return 1; -} - -static int path_serialize(Unit *u, FILE *f, FDSet *fds) { - Path *p = PATH(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", path_state_to_string(p->state)); - unit_serialize_item(u, f, "result", path_result_to_string(p->result)); - - return 0; -} - -static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Path *p = PATH(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - PathState state; - - state = path_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - p->deserialized_state = state; - - } else if (streq(key, "result")) { - PathResult f; - - f = path_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != PATH_SUCCESS) - p->result = f; - - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState path_active_state(Unit *u) { - assert(u); - - return state_translation_table[PATH(u)->state]; -} - -_pure_ static const char *path_sub_state_to_string(Unit *u) { - assert(u); - - return path_state_to_string(PATH(u)->state); -} - -static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - PathSpec *s = userdata; - Path *p; - int changed; - - assert(s); - assert(s->unit); - assert(fd >= 0); - - p = PATH(s->unit); - - if (p->state != PATH_WAITING && - p->state != PATH_RUNNING) - return 0; - - /* log_debug("inotify wakeup on %s.", u->id); */ - - LIST_FOREACH(spec, s, p->specs) - if (path_spec_owns_inotify_fd(s, fd)) - break; - - if (!s) { - log_error("Got event on unknown fd."); - goto fail; - } - - changed = path_spec_fd_event(s, revents); - if (changed < 0) - goto fail; - - /* If we are already running, then remember that one event was - * dispatched so that we restart the service only if something - * actually changed on disk */ - p->inotify_triggered = true; - - if (changed) - path_enter_running(p); - else - path_enter_waiting(p, false, true); - - return 0; - -fail: - path_enter_dead(p, PATH_FAILURE_RESOURCES); - return 0; -} - -static void path_trigger_notify(Unit *u, Unit *other) { - Path *p = PATH(u); - - assert(u); - assert(other); - - /* Invoked whenever the unit we trigger changes state or gains - * or loses a job */ - - if (other->load_state != UNIT_LOADED) - return; - - if (p->state == PATH_RUNNING && - UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { - log_unit_debug(UNIT(p), "Got notified about unit deactivation."); - - /* Hmm, so inotify was triggered since the - * last activation, so I guess we need to - * recheck what is going on. */ - path_enter_waiting(p, false, p->inotify_triggered); - } -} - -static void path_reset_failed(Unit *u) { - Path *p = PATH(u); - - assert(p); - - if (p->state == PATH_FAILED) - path_set_state(p, PATH_DEAD); - - p->result = PATH_SUCCESS; -} - -static const char* const path_type_table[_PATH_TYPE_MAX] = { - [PATH_EXISTS] = "PathExists", - [PATH_EXISTS_GLOB] = "PathExistsGlob", - [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty", - [PATH_CHANGED] = "PathChanged", - [PATH_MODIFIED] = "PathModified", -}; - -DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); - -static const char* const path_result_table[_PATH_RESULT_MAX] = { - [PATH_SUCCESS] = "success", - [PATH_FAILURE_RESOURCES] = "resources", - [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); - -const UnitVTable path_vtable = { - .object_size = sizeof(Path), - - .sections = - "Unit\0" - "Path\0" - "Install\0", - - .init = path_init, - .done = path_done, - .load = path_load, - - .coldplug = path_coldplug, - - .dump = path_dump, - - .start = path_start, - .stop = path_stop, - - .serialize = path_serialize, - .deserialize_item = path_deserialize_item, - - .active_state = path_active_state, - .sub_state_to_string = path_sub_state_to_string, - - .trigger_notify = path_trigger_notify, - - .reset_failed = path_reset_failed, - - .bus_vtable = bus_path_vtable -}; diff --git a/src/core/path.h b/src/core/path.h deleted file mode 100644 index 4230c8fb99..0000000000 --- a/src/core/path.h +++ /dev/null @@ -1,93 +0,0 @@ -#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 . -***/ - -typedef struct Path Path; -typedef struct PathSpec PathSpec; - -#include "unit.h" - -typedef enum PathType { - PATH_EXISTS, - PATH_EXISTS_GLOB, - PATH_DIRECTORY_NOT_EMPTY, - PATH_CHANGED, - PATH_MODIFIED, - _PATH_TYPE_MAX, - _PATH_TYPE_INVALID = -1 -} PathType; - -typedef struct PathSpec { - Unit *unit; - - char *path; - - sd_event_source *event_source; - - LIST_FIELDS(struct PathSpec, spec); - - PathType type; - int inotify_fd; - int primary_wd; - - bool previous_exists; -} PathSpec; - -int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler); -void path_spec_unwatch(PathSpec *s); -int path_spec_fd_event(PathSpec *s, uint32_t events); -void path_spec_done(PathSpec *s); - -static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { - return s->inotify_fd == fd; -} - -typedef enum PathResult { - PATH_SUCCESS, - PATH_FAILURE_RESOURCES, - PATH_FAILURE_START_LIMIT_HIT, - _PATH_RESULT_MAX, - _PATH_RESULT_INVALID = -1 -} PathResult; - -struct Path { - Unit meta; - - LIST_HEAD(PathSpec, specs); - - PathState state, deserialized_state; - - bool inotify_triggered; - - bool make_directory; - mode_t directory_mode; - - PathResult result; -}; - -void path_free_specs(Path *p); - -extern const UnitVTable path_vtable; - -const char* path_type_to_string(PathType i) _const_; -PathType path_type_from_string(const char *s) _pure_; - -const char* path_result_to_string(PathResult i) _const_; -PathResult path_result_from_string(const char *s) _pure_; diff --git a/src/core/scope.c b/src/core/scope.c deleted file mode 100644 index b45e238974..0000000000 --- a/src/core/scope.c +++ /dev/null @@ -1,621 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "dbus-scope.h" -#include "load-dropin.h" -#include "log.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" - -static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = { - [SCOPE_DEAD] = UNIT_INACTIVE, - [SCOPE_RUNNING] = UNIT_ACTIVE, - [SCOPE_ABANDONED] = UNIT_ACTIVE, - [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING, - [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING, - [SCOPE_FAILED] = UNIT_FAILED -}; - -static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); - -static void scope_init(Unit *u) { - Scope *s = SCOPE(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - s->timeout_stop_usec = u->manager->default_timeout_stop_usec; - u->ignore_on_isolate = true; -} - -static void scope_done(Unit *u) { - Scope *s = SCOPE(u); - - assert(u); - - free(s->controller); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); -} - -static int scope_arm_timer(Scope *s, usec_t usec) { - int r; - - assert(s); - - if (s->timer_event_source) { - r = sd_event_source_set_time(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - scope_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "scope-timer"); - - return 0; -} - -static void scope_set_state(Scope *s, ScopeState state) { - ScopeState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) - unit_unwatch_all_pids(UNIT(s)); - - if (state != old_state) - log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int scope_add_default_dependencies(Scope *s) { - int r; - - 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), - UNIT_BEFORE, UNIT_CONFLICTS, - SPECIAL_SHUTDOWN_TARGET, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static int scope_verify(Scope *s) { - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (set_isempty(UNIT(s)->pids) && - !MANAGER_IS_RELOADING(UNIT(s)->manager) && - !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { - log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int scope_load(Unit *u) { - Scope *s = SCOPE(u); - int r; - - assert(s); - assert(u->load_state == UNIT_STUB); - - if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) - /* Refuse to load non-transient scope units, but allow them while reloading. */ - return -ENOENT; - - r = unit_load_fragment_and_dropin_optional(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - r = unit_patch_contexts(u); - if (r < 0) - return r; - - r = unit_set_default_slice(u); - if (r < 0) - return r; - - r = scope_add_default_dependencies(s); - if (r < 0) - return r; - } - - return scope_verify(s); -} - -static int scope_coldplug(Unit *u) { - Scope *s = SCOPE(u); - int r; - - assert(s); - assert(s->state == SCOPE_DEAD); - - if (s->deserialized_state == s->state) - return 0; - - if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) { - r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec)); - if (r < 0) - return r; - } - - if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED)) - unit_watch_all_pids(UNIT(s)); - - scope_set_state(s, s->deserialized_state); - return 0; -} - -static void scope_dump(Unit *u, FILE *f, const char *prefix) { - Scope *s = SCOPE(u); - - assert(s); - assert(f); - - fprintf(f, - "%sScope State: %s\n" - "%sResult: %s\n", - prefix, scope_state_to_string(s->state), - prefix, scope_result_to_string(s->result)); - - cgroup_context_dump(&s->cgroup_context, f, prefix); - kill_context_dump(&s->kill_context, f, prefix); -} - -static void scope_enter_dead(Scope *s, ScopeResult f) { - assert(s); - - if (f != SCOPE_SUCCESS) - s->result = f; - - scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); -} - -static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { - bool skip_signal = false; - int r; - - assert(s); - - if (f != SCOPE_SUCCESS) - s->result = f; - - unit_watch_all_pids(UNIT(s)); - - /* If we have a controller set let's ask the controller nicely - * to terminate the scope, instead of us going directly into - * SIGTERM berserk mode */ - if (state == SCOPE_STOP_SIGTERM) - skip_signal = bus_scope_send_request_stop(s) > 0; - - if (!skip_signal) { - r = unit_kill_context( - UNIT(s), - &s->kill_context, - state != SCOPE_STOP_SIGTERM ? KILL_KILL : - s->was_abandoned ? KILL_TERMINATE_AND_LOG : - KILL_TERMINATE, - -1, -1, false); - if (r < 0) - goto fail; - } else - r = 1; - - if (r > 0) { - r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); - if (r < 0) - goto fail; - - scope_set_state(s, state); - } else if (state == SCOPE_STOP_SIGTERM) - scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS); - else - scope_enter_dead(s, SCOPE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - - scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); -} - -static int scope_start(Unit *u) { - Scope *s = SCOPE(u); - int r; - - assert(s); - - if (unit_has_name(u, SPECIAL_INIT_SCOPE)) - return -EPERM; - - if (s->state == SCOPE_FAILED) - return -EPERM; - - /* We can't fulfill this right now, please try again later */ - if (s->state == SCOPE_STOP_SIGTERM || - s->state == SCOPE_STOP_SIGKILL) - return -EAGAIN; - - assert(s->state == SCOPE_DEAD); - - if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) - return -ENOENT; - - (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_usage(u); - - r = unit_attach_pids_to_cgroup(u); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); - scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); - return r; - } - - s->result = SCOPE_SUCCESS; - - scope_set_state(s, SCOPE_RUNNING); - return 1; -} - -static int scope_stop(Unit *u) { - Scope *s = SCOPE(u); - - assert(s); - - if (s->state == SCOPE_STOP_SIGTERM || - s->state == SCOPE_STOP_SIGKILL) - return 0; - - assert(s->state == SCOPE_RUNNING || - s->state == SCOPE_ABANDONED); - - scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS); - return 1; -} - -static void scope_reset_failed(Unit *u) { - Scope *s = SCOPE(u); - - assert(s); - - if (s->state == SCOPE_FAILED) - scope_set_state(s, SCOPE_DEAD); - - s->result = SCOPE_SUCCESS; -} - -static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - return unit_kill_common(u, who, signo, -1, -1, error); -} - -static int scope_get_timeout(Unit *u, usec_t *timeout) { - Scope *s = SCOPE(u); - usec_t t; - int r; - - if (!s->timer_event_source) - return 0; - - r = sd_event_source_get_time(s->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -static int scope_serialize(Unit *u, FILE *f, FDSet *fds) { - Scope *s = SCOPE(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", scope_state_to_string(s->state)); - unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned)); - return 0; -} - -static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Scope *s = SCOPE(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - ScopeState state; - - state = scope_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, "was-abandoned")) { - int k; - - k = parse_boolean(value); - if (k < 0) - log_unit_debug(u, "Failed to parse boolean value: %s", value); - else - s->was_abandoned = k; - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -static bool scope_check_gc(Unit *u) { - assert(u); - - /* Never clean up scopes that still have a process around, - * even if the scope is formally dead. */ - - if (!u->cgroup_path) - return false; - - return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0; -} - -static void scope_notify_cgroup_empty_event(Unit *u) { - Scope *s = SCOPE(u); - assert(u); - - log_unit_debug(u, "cgroup is empty"); - - if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) - scope_enter_dead(s, SCOPE_SUCCESS); -} - -static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { - - /* If we get a SIGCHLD event for one of the processes we were - interested in, then we look for others to watch, under the - assumption that we'll sooner or later get a SIGCHLD for - them, as the original process we watched was probably the - parent of them, and they are hence now our children. */ - - unit_tidy_watch_pids(u, 0, 0); - unit_watch_all_pids(u); - - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified() <= 0 && set_isempty(u->pids)) - scope_notify_cgroup_empty_event(u); -} - -static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - Scope *s = SCOPE(userdata); - - assert(s); - assert(s->timer_event_source == source); - - switch (s->state) { - - case SCOPE_STOP_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "Stopping timed out. Killing."); - scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL."); - scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); - } - - break; - - case SCOPE_STOP_SIGKILL: - log_unit_warning(UNIT(s), "Still around after SIGKILL. Ignoring."); - scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -int scope_abandon(Scope *s) { - assert(s); - - if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) - return -EPERM; - - if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED)) - return -ESTALE; - - s->was_abandoned = true; - s->controller = mfree(s->controller); - - /* The client is no longer watching the remaining processes, - * so let's step in here, under the assumption that the - * remaining processes will be sooner or later reassigned to - * us as parent. */ - - unit_tidy_watch_pids(UNIT(s), 0, 0); - unit_watch_all_pids(UNIT(s)); - - /* If the PID set is empty now, then let's finish this off */ - if (set_isempty(UNIT(s)->pids)) - scope_notify_cgroup_empty_event(UNIT(s)); - else - scope_set_state(s, SCOPE_ABANDONED); - - return 0; -} - -_pure_ static UnitActiveState scope_active_state(Unit *u) { - assert(u); - - return state_translation_table[SCOPE(u)->state]; -} - -_pure_ static const char *scope_sub_state_to_string(Unit *u) { - assert(u); - - return scope_state_to_string(SCOPE(u)->state); -} - -static void scope_enumerate(Manager *m) { - Unit *u; - int r; - - assert(m); - - /* Let's unconditionally add the "init.scope" special unit - * that encapsulates PID 1. Note that PID 1 already is in the - * cgroup for this, we hence just need to allocate the object - * for it and that's it. */ - - u = manager_get_unit(m, SPECIAL_INIT_SCOPE); - if (!u) { - u = unit_new(m, sizeof(Scope)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_INIT_SCOPE); - if (r < 0) { - unit_free(u); - 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; - - /* Prettify things, if we can. */ - if (!u->description) - u->description = strdup("System and Service Manager"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd(1)"); - - unit_add_to_load_queue(u); - unit_add_to_dbus_queue(u); -} - -static const char* const scope_result_table[_SCOPE_RESULT_MAX] = { - [SCOPE_SUCCESS] = "success", - [SCOPE_FAILURE_RESOURCES] = "resources", - [SCOPE_FAILURE_TIMEOUT] = "timeout", -}; - -DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult); - -const UnitVTable scope_vtable = { - .object_size = sizeof(Scope), - .cgroup_context_offset = offsetof(Scope, cgroup_context), - .kill_context_offset = offsetof(Scope, kill_context), - - .sections = - "Unit\0" - "Scope\0" - "Install\0", - .private_section = "Scope", - - .can_transient = true, - - .init = scope_init, - .load = scope_load, - .done = scope_done, - - .coldplug = scope_coldplug, - - .dump = scope_dump, - - .start = scope_start, - .stop = scope_stop, - - .kill = scope_kill, - - .get_timeout = scope_get_timeout, - - .serialize = scope_serialize, - .deserialize_item = scope_deserialize_item, - - .active_state = scope_active_state, - .sub_state_to_string = scope_sub_state_to_string, - - .check_gc = scope_check_gc, - - .sigchld_event = scope_sigchld_event, - - .reset_failed = scope_reset_failed, - - .notify_cgroup_empty = scope_notify_cgroup_empty_event, - - .bus_vtable = bus_scope_vtable, - .bus_set_property = bus_scope_set_property, - .bus_commit_properties = bus_scope_commit_properties, - - .enumerate = scope_enumerate, -}; diff --git a/src/core/scope.h b/src/core/scope.h deleted file mode 100644 index eaf8e8b447..0000000000 --- a/src/core/scope.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Scope Scope; - -#include "cgroup.h" -#include "kill.h" -#include "unit.h" - -typedef enum ScopeResult { - SCOPE_SUCCESS, - SCOPE_FAILURE_RESOURCES, - SCOPE_FAILURE_TIMEOUT, - _SCOPE_RESULT_MAX, - _SCOPE_RESULT_INVALID = -1 -} ScopeResult; - -struct Scope { - Unit meta; - - CGroupContext cgroup_context; - KillContext kill_context; - - ScopeState state, deserialized_state; - ScopeResult result; - - usec_t timeout_stop_usec; - - char *controller; - bool was_abandoned; - - sd_event_source *timer_event_source; -}; - -extern const UnitVTable scope_vtable; - -int scope_abandon(Scope *s); - -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 deleted file mode 100644 index 2b96a9551b..0000000000 --- a/src/core/selinux-access.c +++ /dev/null @@ -1,283 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Dan Walsh - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "selinux-access.h" - -#ifdef HAVE_SELINUX - -#include -#include -#include -#include -#ifdef HAVE_AUDIT -#include -#endif - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "audit-fd.h" -#include "bus-util.h" -#include "log.h" -#include "path-util.h" -#include "selinux-util.h" -#include "stdio-util.h" -#include "strv.h" -#include "util.h" - -static bool initialized = false; - -struct audit_info { - sd_bus_creds *creds; - const char *path; - const char *cmdline; -}; - -/* - Any time an access gets denied this callback will be called - with the audit data. We then need to just copy the audit data into the msgbuf. -*/ -static int audit_callback( - void *auditdata, - security_class_t cls, - char *msgbuf, - size_t msgbufsize) { - - const struct audit_info *audit = auditdata; - uid_t uid = 0, login_uid = 0; - gid_t gid = 0; - char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a"; - char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a"; - char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a"; - - if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0) - xsprintf(login_uid_buf, UID_FMT, login_uid); - if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0) - xsprintf(uid_buf, UID_FMT, uid); - if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0) - xsprintf(gid_buf, GID_FMT, gid); - - snprintf(msgbuf, msgbufsize, - "auid=%s uid=%s gid=%s%s%s%s%s%s%s", - login_uid_buf, uid_buf, gid_buf, - audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "", - audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : ""); - - return 0; -} - -static int callback_type_to_priority(int type) { - switch(type) { - - case SELINUX_ERROR: - return LOG_ERR; - - case SELINUX_WARNING: - return LOG_WARNING; - - case SELINUX_INFO: - return LOG_INFO; - - case SELINUX_AVC: - default: - return LOG_NOTICE; - } -} - -/* - libselinux uses this callback when access gets denied or other - events happen. If audit is turned on, messages will be reported - using audit netlink, otherwise they will be logged using the usual - channels. - - Code copied from dbus and modified. -*/ -_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { - va_list ap; - const char *fmt2; - -#ifdef HAVE_AUDIT - int fd; - - fd = get_audit_fd(); - - if (fd >= 0) { - _cleanup_free_ char *buf = NULL; - int r; - - va_start(ap, fmt); - r = vasprintf(&buf, fmt, ap); - va_end(ap); - - if (r >= 0) { - audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); - return 0; - } - } -#endif - - fmt2 = strjoina("selinux: ", fmt); - - va_start(ap, fmt); - log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap); - va_end(ap); - - return 0; -} - -static int access_init(sd_bus_error *error) { - - if (!mac_selinux_use()) - return 0; - - if (initialized) - return 1; - - if (avc_open(NULL, 0) != 0) { - int enforce, saved_errno = errno; - - enforce = security_getenforce(); - log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m"); - - /* 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; - - /* 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)); - } - - selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); - selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); - - initialized = true; - return 1; -} - -/* - This function communicates with the kernel to check whether or not it should - allow the access. - If the machine is in permissive mode it will return ok. Audit messages will - still be generated if the access would be denied in enforcing mode. -*/ -int mac_selinux_generic_access_check( - sd_bus_message *message, - const char *path, - const char *permission, - sd_bus_error *error) { - - _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; - char *fcon = NULL; - char **cmdline = NULL; - int r = 0; - - assert(message); - assert(permission); - assert(error); - - r = access_init(error); - if (r <= 0) - return r; - - r = sd_bus_query_sender_creds( - message, - SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID| - SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUGMENT /* get more bits from /proc */, - &creds); - if (r < 0) - goto finish; - - /* The SELinux context is something we really should have - * gotten directly from the message or sender, and not be an - * augmented field. If it was augmented we cannot use it for - * authorization, since this is racy and vulnerable. Let's add - * an extra check, just in case, even though this really - * shouldn't be possible. */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM); - - r = sd_bus_creds_get_selinux_context(creds, &scon); - if (r < 0) - goto finish; - - if (path) { - /* Get the file context of the unit file */ - - r = getfilecon_raw(path, &fcon); - if (r < 0) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); - goto finish; - } - - tclass = "service"; - } else { - r = getcon_raw(&fcon); - if (r < 0) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); - goto finish; - } - - tclass = "system"; - } - - sd_bus_creds_get_cmdline(creds, &cmdline); - cl = strv_join(cmdline, " "); - - audit_info.creds = creds; - audit_info.path = path; - audit_info.cmdline = cl; - - r = selinux_check_access(scon, fcon, tclass, permission, &audit_info); - if (r < 0) - r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); - - log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r); - -finish: - freecon(fcon); - - if (r < 0 && security_getenforce() != 1) { - sd_bus_error_free(error); - r = 0; - } - - 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 diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h deleted file mode 100644 index 8f1f058a32..0000000000 --- a/src/core/selinux-access.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Dan Walsh - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "bus-util.h" -#include "manager.h" - -int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error); - -#ifdef HAVE_SELINUX - -#define mac_selinux_access_check(message, permission, error) \ - mac_selinux_generic_access_check((message), NULL, (permission), (error)) - -#define mac_selinux_unit_access_check(unit, message, permission, error) \ - ({ \ - Unit *_unit = (unit); \ - mac_selinux_generic_access_check((message), _unit->source_path ?: _unit->fragment_path, (permission), (error)); \ - }) - -#else - -#define mac_selinux_access_check(message, permission, error) 0 -#define mac_selinux_unit_access_check(unit, message, permission, error) 0 - -#endif diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c deleted file mode 100644 index 527aa8add0..0000000000 --- a/src/core/selinux-setup.c +++ /dev/null @@ -1,121 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_SELINUX -#include -#endif - -#include "log.h" -#include "macro.h" -#include "selinux-setup.h" -#include "selinux-util.h" -#include "string-util.h" -#include "util.h" - -#ifdef HAVE_SELINUX -_printf_(2,3) -static int null_log(int type, const char *fmt, ...) { - return 0; -} -#endif - -int mac_selinux_setup(bool *loaded_policy) { - -#ifdef HAVE_SELINUX - int enforce = 0; - usec_t before_load, after_load; - char *con; - int r; - union selinux_callback cb; - bool initialized = false; - - assert(loaded_policy); - - /* Turn off all of SELinux' own logging, we want to do that */ - cb.func_log = null_log; - selinux_set_callback(SELINUX_CB_LOG, cb); - - /* Don't load policy in the initrd if we don't appear to have - * it. For the real root, we check below if we've already - * loaded policy, and return gracefully. - */ - if (in_initrd() && access(selinux_path(), F_OK) < 0) - return 0; - - /* Already initialized by somebody else? */ - r = getcon_raw(&con); - if (r == 0) { - initialized = !streq(con, "kernel"); - freecon(con); - } - - /* Make sure we have no fds open while loading the policy and - * transitioning */ - log_close(); - - /* Now load the 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]; - - mac_selinux_retest(); - - /* Transition to the new context */ - r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); - if (r < 0 || !label) { - log_open(); - log_error("Failed to compute init label, ignoring."); - } else { - r = setcon_raw(label); - - log_open(); - if (r < 0) - log_error("Failed to transition into init label '%s', ignoring.", label); - } - - after_load = now(CLOCK_MONOTONIC); - - log_info("Successfully loaded SELinux policy in %s.", - format_timespan(timespan, sizeof(timespan), after_load - before_load, 0)); - - *loaded_policy = true; - - } else { - log_open(); - - if (enforce > 0) { - if (!initialized) { - log_emergency("Failed to load SELinux policy."); - return -EIO; - } - - log_warning("Failed to load new SELinux policy. Continuing with old policy."); - } else - log_debug("Unable to load SELinux policy. Ignoring."); - } -#endif - - return 0; -} diff --git a/src/core/selinux-setup.h b/src/core/selinux-setup.h deleted file mode 100644 index 7b613249b0..0000000000 --- a/src/core/selinux-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - -#include - -int mac_selinux_setup(bool *loaded_policy); diff --git a/src/core/service.c b/src/core/service.c deleted file mode 100644 index afb198507b..0000000000 --- a/src/core/service.c +++ /dev/null @@ -1,3391 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "async.h" -#include "bus-error.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "dbus-service.h" -#include "def.h" -#include "env-util.h" -#include "escape.h" -#include "exit-status.h" -#include "fd-util.h" -#include "fileio.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, - [SERVICE_START_PRE] = UNIT_ACTIVATING, - [SERVICE_START] = UNIT_ACTIVATING, - [SERVICE_START_POST] = UNIT_ACTIVATING, - [SERVICE_RUNNING] = UNIT_ACTIVE, - [SERVICE_EXITED] = UNIT_ACTIVE, - [SERVICE_RELOAD] = UNIT_RELOADING, - [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_STOP_POST] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_FAILED] = UNIT_FAILED, - [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING -}; - -/* For Type=idle we never want to delay any other jobs, hence we - * consider idle jobs active as soon as we start working on them */ -static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = UNIT_INACTIVE, - [SERVICE_START_PRE] = UNIT_ACTIVE, - [SERVICE_START] = UNIT_ACTIVE, - [SERVICE_START_POST] = UNIT_ACTIVE, - [SERVICE_RUNNING] = UNIT_ACTIVE, - [SERVICE_EXITED] = UNIT_ACTIVE, - [SERVICE_RELOAD] = UNIT_RELOADING, - [SERVICE_STOP] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_STOP_POST] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SERVICE_FAILED] = UNIT_FAILED, - [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING -}; - -static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata); -static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata); - -static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); -static void service_enter_reload_by_notify(Service *s); - -static void service_init(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - s->timeout_start_usec = u->manager->default_timeout_start_usec; - s->timeout_stop_usec = u->manager->default_timeout_stop_usec; - s->restart_usec = u->manager->default_restart_usec; - s->runtime_max_usec = USEC_INFINITY; - s->type = _SERVICE_TYPE_INVALID; - s->socket_fd = -1; - s->stdin_fd = s->stdout_fd = s->stderr_fd = -1; - s->guess_main_pid = true; - - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; -} - -static void service_unwatch_control_pid(Service *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void service_unwatch_main_pid(Service *s) { - assert(s); - - if (s->main_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->main_pid); - s->main_pid = 0; -} - -static void service_unwatch_pid_file(Service *s) { - if (!s->pid_file_pathspec) - return; - - log_unit_debug(UNIT(s), "Stopping watch for PID file %s", s->pid_file_pathspec->path); - path_spec_unwatch(s->pid_file_pathspec); - path_spec_done(s->pid_file_pathspec); - s->pid_file_pathspec = mfree(s->pid_file_pathspec); -} - -static int service_set_main_pid(Service *s, pid_t pid) { - pid_t ppid; - - assert(s); - - if (pid <= 1) - return -EINVAL; - - if (pid == getpid()) - return -EINVAL; - - if (s->main_pid == pid && s->main_pid_known) - return 0; - - if (s->main_pid != pid) { - service_unwatch_main_pid(s); - exec_status_start(&s->main_exec_status, pid); - } - - s->main_pid = pid; - s->main_pid_known = true; - - 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 - s->main_pid_alien = false; - - return 0; -} - -void service_close_socket_fd(Service *s) { - assert(s); - - /* Undo the effect of service_set_socket_fd(). */ - - s->socket_fd = asynchronous_close(s->socket_fd); - - if (UNIT_ISSET(s->accept_socket)) { - socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); - unit_ref_unset(&s->accept_socket); - } -} - -static void service_stop_watchdog(Service *s) { - assert(s); - - s->watchdog_event_source = sd_event_source_unref(s->watchdog_event_source); - s->watchdog_timestamp = DUAL_TIMESTAMP_NULL; -} - -static usec_t service_get_watchdog_usec(Service *s) { - assert(s); - - if (s->watchdog_override_enable) - return s->watchdog_override_usec; - else - return s->watchdog_usec; -} - -static void service_start_watchdog(Service *s) { - int r; - usec_t watchdog_usec; - - assert(s); - - watchdog_usec = service_get_watchdog_usec(s); - if (watchdog_usec == 0 || watchdog_usec == USEC_INFINITY) - return; - - if (s->watchdog_event_source) { - r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, watchdog_usec)); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m"); - return; - } - - r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ONESHOT); - } else { - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->watchdog_event_source, - CLOCK_MONOTONIC, - usec_add(s->watchdog_timestamp.monotonic, watchdog_usec), 0, - service_dispatch_watchdog, s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m"); - return; - } - - (void) sd_event_source_set_description(s->watchdog_event_source, "service-watchdog"); - - /* Let's process everything else which might be a sign - * of living before we consider a service died. */ - r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE); - } - - if (r < 0) - log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m"); -} - -static void service_reset_watchdog(Service *s) { - assert(s); - - dual_timestamp_get(&s->watchdog_timestamp); - service_start_watchdog(s); -} - -static void service_reset_watchdog_timeout(Service *s, usec_t watchdog_override_usec) { - assert(s); - - s->watchdog_override_enable = true; - s->watchdog_override_usec = watchdog_override_usec; - service_reset_watchdog(s); - - log_unit_debug(UNIT(s), "watchdog_usec="USEC_FMT, s->watchdog_usec); - log_unit_debug(UNIT(s), "watchdog_override_usec="USEC_FMT, s->watchdog_override_usec); -} - -static void service_fd_store_unlink(ServiceFDStore *fs) { - - if (!fs) - return; - - if (fs->service) { - assert(fs->service->n_fd_store > 0); - LIST_REMOVE(fd_store, fs->service->fd_store, fs); - fs->service->n_fd_store--; - } - - if (fs->event_source) { - sd_event_source_set_enabled(fs->event_source, SD_EVENT_OFF); - sd_event_source_unref(fs->event_source); - } - - free(fs->fdname); - safe_close(fs->fd); - free(fs); -} - -static void service_release_resources(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - 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); - - assert(s->n_fd_store == 0); -} - -static void service_done(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - s->pid_file = mfree(s->pid_file); - s->status_text = mfree(s->status_text); - - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); - s->control_command = NULL; - s->main_command = NULL; - - exit_status_set_free(&s->restart_prevent_status); - exit_status_set_free(&s->restart_force_status); - exit_status_set_free(&s->success_status); - - /* This will leak a process, but at least no memory or any of - * our resources */ - service_unwatch_main_pid(s); - service_unwatch_control_pid(s); - service_unwatch_pid_file(s); - - if (s->bus_name) { - unit_unwatch_bus_name(u, s->bus_name); - s->bus_name = mfree(s->bus_name); - } - - s->bus_name_owner = mfree(s->bus_name_owner); - - service_close_socket_fd(s); - - unit_ref_unset(&s->accept_socket); - - service_stop_watchdog(s); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - service_release_resources(u); -} - -static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { - ServiceFDStore *fs = userdata; - - assert(e); - assert(fs); - - /* If we get either EPOLLHUP or EPOLLERR, it's time to remove this entry from the fd store */ - service_fd_store_unlink(fs); - return 0; -} - -static int service_add_fd_store(Service *s, int fd, const char *name) { - ServiceFDStore *fs; - int r; - - assert(s); - assert(fd >= 0); - - if (s->n_fd_store >= s->n_fd_store_max) - return 0; - - LIST_FOREACH(fd_store, fs, s->fd_store) { - r = same_fd(fs->fd, fd); - if (r < 0) - return r; - if (r > 0) { - /* Already included */ - safe_close(fd); - return 1; - } - } - - fs = new0(ServiceFDStore, 1); - if (!fs) - return -ENOMEM; - - 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; - } - - (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); - - LIST_PREPEND(fd_store, s->fd_store, fs); - s->n_fd_store++; - - return 1; -} - -static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { - int r; - - assert(s); - - if (fdset_size(fds) <= 0) - return 0; - - while (s->n_fd_store < s->n_fd_store_max) { - _cleanup_close_ int fd = -1; - - fd = fdset_steal_first(fds); - if (fd < 0) - break; - - 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) { - log_unit_debug(UNIT(s), "Added fd to fd store."); - fd = -1; - } - } - - if (fdset_size(fds) > 0) - log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max); - - return 0; -} - -static int service_arm_timer(Service *s, usec_t usec) { - int r; - - assert(s); - - if (s->timer_event_source) { - r = sd_event_source_set_time(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - service_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "service-timer"); - - return 0; -} - -static int service_verify(Service *s) { - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) { - log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing."); - return -EINVAL; - } - - if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) { - log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); - return -EINVAL; - } - - if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) { - log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing."); - return -EINVAL; - } - - if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { - log_unit_error(UNIT(s), "Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); - return -EINVAL; - } - - if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) { - log_unit_error(UNIT(s), "Service has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing."); - return -EINVAL; - } - - if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) { - log_unit_error(UNIT(s), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing."); - return -EINVAL; - } - - if (s->type == SERVICE_DBUS && !s->bus_name) { - log_unit_error(UNIT(s), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing."); - return -EINVAL; - } - - if (s->bus_name && s->type != SERVICE_DBUS) - log_unit_warning(UNIT(s), "Service has a D-Bus service name specified, but is not of type dbus. Ignoring."); - - if (s->exec_context.pam_name && !(s->kill_context.kill_mode == KILL_CONTROL_GROUP || s->kill_context.kill_mode == KILL_MIXED)) { - log_unit_error(UNIT(s), "Service has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing."); - return -EINVAL; - } - - if (s->usb_function_descriptors && !s->usb_function_strings) - log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring."); - - if (!s->usb_function_descriptors && s->usb_function_strings) - log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring."); - - if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT) - log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring."); - - return 0; -} - -static int service_add_default_dependencies(Service *s) { - int r; - - assert(s); - - if (!UNIT(s)->default_dependencies) - return 0; - - /* Add a number of automatic dependencies useful for the - * majority of services. */ - - if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { - /* 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; - - /* 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); -} - -static void service_fix_output(Service *s) { - assert(s); - - /* If nothing has been explicitly configured, patch default - * output in. If input is socket/tty we avoid this however, - * since in that case we want output to default to the same - * place as we read input from. */ - - if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && - s->exec_context.std_output == EXEC_OUTPUT_INHERIT && - s->exec_context.std_input == EXEC_INPUT_NULL) - s->exec_context.std_error = UNIT(s)->manager->default_std_error; - - if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && - s->exec_context.std_input == EXEC_INPUT_NULL) - s->exec_context.std_output = UNIT(s)->manager->default_std_output; -} - -static int service_setup_bus_name(Service *s) { - int r; - - assert(s); - - if (!s->bus_name) - return 0; - - 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; - - assert(s); - - if (s->type == _SERVICE_TYPE_INVALID) { - /* Figure out a type automatically */ - if (s->bus_name) - s->type = SERVICE_DBUS; - else if (s->exec_command[SERVICE_EXEC_START]) - s->type = SERVICE_SIMPLE; - else - s->type = SERVICE_ONESHOT; - } - - /* Oneshot services have disabled start timeout by default */ - if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined) - s->timeout_start_usec = USEC_INFINITY; - - service_fix_output(s); - - r = unit_patch_contexts(UNIT(s)); - if (r < 0) - return r; - - r = unit_add_exec_dependencies(UNIT(s), &s->exec_context); - if (r < 0) - return r; - - r = unit_set_default_slice(UNIT(s)); - if (r < 0) - return r; - - if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) - s->notify_access = NOTIFY_MAIN; - - if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) - s->notify_access = NOTIFY_MAIN; - - r = service_add_default_dependencies(s); - if (r < 0) - return r; - - r = service_setup_bus_name(s); - if (r < 0) - return r; - - return 0; -} - -static int service_load(Unit *u) { - Service *s = SERVICE(u); - int r; - - assert(s); - - /* Load a .service file */ - r = unit_load_fragment(u); - if (r < 0) - return r; - - /* Still nothing found? Then let's give up */ - if (u->load_state == UNIT_STUB) - return -ENOENT; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - - /* We were able to load something, then let's add in - * the dropin directories. */ - r = unit_load_dropin(u); - if (r < 0) - return r; - - /* This is a new unit? Then let's add in some - * extras */ - r = service_add_extras(s); - if (r < 0) - return r; - } - - return service_verify(s); -} - -static void service_dump(Unit *u, FILE *f, const char *prefix) { - ServiceExecCommand c; - Service *s = SERVICE(u); - const char *prefix2; - - assert(s); - - prefix = strempty(prefix); - prefix2 = strjoina(prefix, "\t"); - - fprintf(f, - "%sService State: %s\n" - "%sResult: %s\n" - "%sReload Result: %s\n" - "%sPermissionsStartOnly: %s\n" - "%sRootDirectoryStartOnly: %s\n" - "%sRemainAfterExit: %s\n" - "%sGuessMainPID: %s\n" - "%sType: %s\n" - "%sRestart: %s\n" - "%sNotifyAccess: %s\n" - "%sNotifyState: %s\n", - prefix, service_state_to_string(s->state), - prefix, service_result_to_string(s->result), - prefix, service_result_to_string(s->reload_result), - prefix, yes_no(s->permissions_start_only), - prefix, yes_no(s->root_directory_start_only), - prefix, yes_no(s->remain_after_exit), - prefix, yes_no(s->guess_main_pid), - prefix, service_type_to_string(s->type), - prefix, service_restart_to_string(s->restart), - prefix, notify_access_to_string(s->notify_access), - prefix, notify_state_to_string(s->notify_state)); - - if (s->control_pid > 0) - fprintf(f, - "%sControl PID: "PID_FMT"\n", - prefix, s->control_pid); - - if (s->main_pid > 0) - fprintf(f, - "%sMain PID: "PID_FMT"\n" - "%sMain PID Known: %s\n" - "%sMain PID Alien: %s\n", - prefix, s->main_pid, - prefix, yes_no(s->main_pid_known), - prefix, yes_no(s->main_pid_alien)); - - if (s->pid_file) - fprintf(f, - "%sPIDFile: %s\n", - prefix, s->pid_file); - - if (s->bus_name) - fprintf(f, - "%sBusName: %s\n" - "%sBus Name Good: %s\n", - prefix, s->bus_name, - prefix, yes_no(s->bus_name_good)); - - kill_context_dump(&s->kill_context, f, prefix); - exec_context_dump(&s->exec_context, f, prefix); - - for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) { - - if (!s->exec_command[c]) - continue; - - fprintf(f, "%s-> %s:\n", - prefix, service_exec_command_to_string(c)); - - exec_command_dump_list(s->exec_command[c], f, prefix2); - } - - if (s->status_text) - fprintf(f, "%sStatus Text: %s\n", - prefix, s->status_text); - - if (s->n_fd_store_max > 0) - fprintf(f, - "%sFile Descriptor Store Max: %u\n" - "%sFile Descriptor Store Current: %u\n", - prefix, s->n_fd_store_max, - prefix, s->n_fd_store); -} - -static int service_load_pid_file(Service *s, bool may_warn) { - _cleanup_free_ char *k = NULL; - int r; - pid_t pid; - - assert(s); - - if (!s->pid_file) - return -ENOENT; - - r = read_one_line_file(s->pid_file, &k); - if (r < 0) { - if (may_warn) - log_unit_info_errno(UNIT(s), r, "PID file %s not readable (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state)); - return r; - } - - r = parse_pid(k, &pid); - if (r < 0) { - if (may_warn) - log_unit_info_errno(UNIT(s), r, "Failed to read PID from file %s: %m", s->pid_file); - return r; - } - - if (!pid_is_alive(pid)) { - if (may_warn) - log_unit_info(UNIT(s), "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); - return -ESRCH; - } - - if (s->main_pid_known) { - if (pid == s->main_pid) - return 0; - - log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); - - service_unwatch_main_pid(s); - s->main_pid_known = false; - } else - log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pid); - - r = service_set_main_pid(s, pid); - if (r < 0) - return r; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) { - /* FIXME: we need to do something here */ - log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); - return r; - } - - return 0; -} - -static void service_search_main_pid(Service *s) { - pid_t pid = 0; - int r; - - assert(s); - - /* If we know it anyway, don't ever fallback to unreliable - * heuristics */ - if (s->main_pid_known) - return; - - if (!s->guess_main_pid) - return; - - assert(s->main_pid <= 0); - - if (unit_search_main_pid(UNIT(s), &pid) < 0) - return; - - log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); - if (service_set_main_pid(s, pid) < 0) - return; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ - log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); -} - -static void service_set_state(Service *s, ServiceState state) { - ServiceState old_state; - const UnitActiveState *table; - - assert(s); - - table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; - - old_state = s->state; - s->state = state; - - service_unwatch_pid_file(s); - - if (!IN_SET(state, - SERVICE_START_PRE, 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, - SERVICE_AUTO_RESTART)) - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (!IN_SET(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)) { - service_unwatch_main_pid(s); - s->main_command = NULL; - } - - if (!IN_SET(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)) { - service_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - } - - if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) - unit_unwatch_all_pids(UNIT(s)); - - if (!IN_SET(state, - SERVICE_START_PRE, 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) && - !(state == SERVICE_DEAD && UNIT(s)->job)) - service_close_socket_fd(s); - - if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) - service_stop_watchdog(s); - - /* For the inactive states unit_notify() will trim the cgroup, - * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager)) - unit_prune_cgroup(UNIT(s)); - - /* For remain_after_exit services, let's see if we can "release" the - * hold on the console, since unit_notify() only does that in case of - * change of state */ - if (state == SERVICE_EXITED && - s->remain_after_exit && - UNIT(s)->manager->n_on_console > 0) { - - ExecContext *ec; - - ec = unit_get_exec_context(UNIT(s)); - if (ec && exec_context_may_touch_console(ec)) { - Manager *m = UNIT(s)->manager; - - m->n_on_console--; - if (m->n_on_console == 0) - /* unset no_console_output flag, since the console is free */ - m->no_console_output = false; - } - } - - if (old_state != 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); -} - -static usec_t service_coldplug_timeout(Service *s) { - assert(s); - - switch (s->deserialized_state) { - - case SERVICE_START_PRE: - case SERVICE_START: - case SERVICE_START_POST: - case SERVICE_RELOAD: - return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec); - - case SERVICE_RUNNING: - return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec); - - case SERVICE_STOP: - case SERVICE_STOP_SIGABRT: - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec); - - case SERVICE_AUTO_RESTART: - return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec); - - default: - return USEC_INFINITY; - } -} - -static int service_coldplug(Unit *u) { - Service *s = SERVICE(u); - int r; - - assert(s); - assert(s->state == SERVICE_DEAD); - - if (s->deserialized_state == s->state) - return 0; - - r = service_arm_timer(s, service_coldplug_timeout(s)); - if (r < 0) - return r; - - 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; - } - - 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, char ***fd_names) { - _cleanup_strv_free_ char **rfd_names = NULL; - _cleanup_free_ int *rfds = NULL; - int rn_fds = 0, r; - - assert(s); - assert(fds); - assert(fd_names); - - if (s->socket_fd >= 0) { - - /* Pass the per-connection socket */ - - rfds = new(int, 1); - if (!rfds) - return -ENOMEM; - rfds[0] = s->socket_fd; - - rfd_names = strv_new("connection", NULL); - if (!rfd_names) - return -ENOMEM; - - rn_fds = 1; - } else { - Iterator i; - Unit *u; - - /* Pass all our configured sockets for singleton services */ - - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { - _cleanup_free_ int *cfds = NULL; - Socket *sock; - int cn_fds; - - if (u->type != UNIT_SOCKET) - continue; - - sock = SOCKET(u); - - 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)); - if (!t) - return -ENOMEM; - - rfds = t; - - 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; - *fd_names = rfd_names; - - rfds = NULL; - rfd_names = NULL; - - return rn_fds; -} - -static int service_spawn( - Service *s, - ExecCommand *c, - usec_t timeout, - bool pass_fds, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool is_control, - pid_t *_pid) { - - _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = 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, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, - }; - - int r; - - assert(s); - assert(c); - assert(_pid); - - (void) unit_realize_cgroup(UNIT(s)); - if (s->reset_cpu_usage) { - (void) unit_reset_cpu_usage(UNIT(s)); - s->reset_cpu_usage = false; - } - - r = unit_setup_exec_runtime(UNIT(s)); - if (r < 0) - return r; - - if (pass_fds || - s->exec_context.std_input == EXEC_INPUT_SOCKET || - s->exec_context.std_output == EXEC_OUTPUT_SOCKET || - s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - - r = service_collect_fds(s, &fds, &fd_names); - if (r < 0) - return r; - - n_fds = r; - } - - r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); - if (r < 0) - return r; - - r = unit_full_printf_strv(UNIT(s), c->argv, &argv); - if (r < 0) - return r; - - our_env = new0(char*, 6); - if (!our_env) - return -ENOMEM; - - if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) - if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) - return -ENOMEM; - - if (s->main_pid > 0) - if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) - return -ENOMEM; - - if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) - if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) - return -ENOMEM; - - if (s->socket_fd >= 0) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - r = getpeername(s->socket_fd, &sa.sa, &salen); - if (r < 0) - return -errno; - - if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) { - _cleanup_free_ char *addr = NULL; - char *t; - int port; - - r = sockaddr_pretty(&sa.sa, salen, true, false, &addr); - if (r < 0) - return r; - - t = strappend("REMOTE_ADDR=", addr); - if (!t) - return -ENOMEM; - our_env[n_env++] = t; - - port = sockaddr_port(&sa.sa); - if (port < 0) - return port; - - if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) - return -ENOMEM; - our_env[n_env++] = t; - } - } - - final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); - if (!final_env) - return -ENOMEM; - - if (is_control && UNIT(s)->cgroup_path) { - path = strjoina(UNIT(s)->cgroup_path, "/control"); - (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); - } else - path = UNIT(s)->cgroup_path; - - 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; - exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; - exec_params.cgroup_path = path; - exec_params.cgroup_delegate = s->cgroup_context.delegate; - exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); - exec_params.watchdog_usec = s->watchdog_usec; - 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, - &s->exec_context, - &exec_params, - s->exec_runtime, - &pid); - if (r < 0) - return r; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ - return r; - - *_pid = pid; - - return 0; -} - -static int main_pid_good(Service *s) { - assert(s); - - /* Returns 0 if the pid is dead, 1 if it is good, -1 if we - * don't know */ - - /* If we know the pid file, then let's just check if it is - * still valid */ - if (s->main_pid_known) { - - /* If it's an alien child let's check if it is still - * alive ... */ - if (s->main_pid_alien && s->main_pid > 0) - return pid_is_alive(s->main_pid); - - /* .. otherwise assume we'll get a SIGCHLD for it, - * which we really should wait for to collect exit - * status and code */ - return s->main_pid > 0; - } - - /* We don't know the pid */ - return -EAGAIN; -} - -_pure_ static int control_pid_good(Service *s) { - assert(s); - - return s->control_pid > 0; -} - -static int cgroup_good(Service *s) { - int r; - - assert(s); - - if (!UNIT(s)->cgroup_path) - return 0; - - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path); - if (r < 0) - return r; - - return !r; -} - -static bool service_shall_restart(Service *s) { - assert(s); - - /* Don't restart after manual stops */ - if (s->forbid_restart) - return false; - - /* Never restart if this is configured as special exception */ - if (exit_status_set_test(&s->restart_prevent_status, s->main_exec_status.code, s->main_exec_status.status)) - return false; - - /* Restart if the exit code/status are configured as restart triggers */ - if (exit_status_set_test(&s->restart_force_status, s->main_exec_status.code, s->main_exec_status.status)) - return true; - - switch (s->restart) { - - case SERVICE_RESTART_NO: - return false; - - case SERVICE_RESTART_ALWAYS: - return true; - - case SERVICE_RESTART_ON_SUCCESS: - return s->result == SERVICE_SUCCESS; - - case SERVICE_RESTART_ON_FAILURE: - return s->result != SERVICE_SUCCESS; - - case SERVICE_RESTART_ON_ABNORMAL: - return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE); - - case SERVICE_RESTART_ON_WATCHDOG: - return s->result == SERVICE_FAILURE_WATCHDOG; - - case SERVICE_RESTART_ON_ABORT: - return IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP); - - default: - assert_not_reached("unknown restart setting"); - } -} - -static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { - int r; - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); - - if (s->result != SERVICE_SUCCESS) { - log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result)); - failure_action(UNIT(s)->manager, s->failure_action, UNIT(s)->reboot_arg); - } - - if (allow_restart && service_shall_restart(s)) { - - r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_AUTO_RESTART); - } - - /* The next restart might not be a manual stop, hence reset the flag indicating manual stops */ - s->forbid_restart = false; - - /* We want fresh tmpdirs in case service is started again immediately */ - exec_runtime_destroy(s->exec_runtime); - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - - /* Also, remove the runtime directory in */ - exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - - /* Try to delete the pid file. At this point it will be - * out-of-date, and some software might be confused by it, so - * let's remove it. */ - if (s->pid_file) - (void) unlink(s->pid_file); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run install restart timer: %m"); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); -} - -static void service_enter_stop_post(Service *s, ServiceResult f) { - int r; - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_unwatch_control_pid(s); - unit_watch_all_pids(UNIT(s)); - - s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; - if (s->control_command) { - s->control_command_id = SERVICE_EXEC_STOP_POST; - - r = service_spawn(s, - s->control_command, - s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_STOP_POST); - } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static int state_to_kill_operation(ServiceState state) { - switch (state) { - - case SERVICE_STOP_SIGABRT: - return KILL_ABORT; - - case SERVICE_STOP_SIGTERM: - case SERVICE_FINAL_SIGTERM: - return KILL_TERMINATE; - - case SERVICE_STOP_SIGKILL: - case SERVICE_FINAL_SIGKILL: - return KILL_KILL; - - default: - return _KILL_OPERATION_INVALID; - } -} - -static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { - int r; - - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - unit_watch_all_pids(UNIT(s)); - - r = unit_kill_context( - UNIT(s), - &s->kill_context, - state_to_kill_operation(state), - s->main_pid, - s->control_pid, - s->main_pid_alien); - - if (r < 0) - goto fail; - - if (r > 0) { - r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); - if (r < 0) - goto fail; - - service_set_state(s, state); - } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill) - service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS); - else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) - service_enter_stop_post(s, SERVICE_SUCCESS); - else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill) - service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); - else - service_enter_dead(s, SERVICE_SUCCESS, true); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - - if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) - service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); - else - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); -} - -static void service_enter_stop_by_notify(Service *s) { - assert(s); - - unit_watch_all_pids(UNIT(s)); - - service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); - - /* The service told us it's stopping, so it's as if we SIGTERM'd it. */ - service_set_state(s, SERVICE_STOP_SIGTERM); -} - -static void service_enter_stop(Service *s, ServiceResult f) { - int r; - - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_unwatch_control_pid(s); - unit_watch_all_pids(UNIT(s)); - - s->control_command = s->exec_command[SERVICE_EXEC_STOP]; - if (s->control_command) { - s->control_command_id = SERVICE_EXEC_STOP; - - r = service_spawn(s, - s->control_command, - s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_STOP); - } else - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop' task: %m"); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static bool service_good(Service *s) { - int main_pid_ok; - assert(s); - - if (s->type == SERVICE_DBUS && !s->bus_name_good) - return false; - - main_pid_ok = main_pid_good(s); - if (main_pid_ok > 0) /* It's alive */ - return true; - if (main_pid_ok == 0) /* It's dead */ - return false; - - /* OK, we don't know anything about the main PID, maybe - * because there is none. Let's check the control group - * instead. */ - - return cgroup_good(s) != 0; -} - -static void service_enter_running(Service *s, ServiceResult f) { - assert(s); - - if (f != SERVICE_SUCCESS) - s->result = f; - - service_unwatch_control_pid(s); - - if (service_good(s)) { - - /* If there are any queued up sd_notify() - * notifications, process them now */ - if (s->notify_state == NOTIFY_RELOADING) - service_enter_reload_by_notify(s); - else if (s->notify_state == NOTIFY_STOPPING) - service_enter_stop_by_notify(s); - else { - service_set_state(s, SERVICE_RUNNING); - service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec)); - } - - } else if (s->remain_after_exit) - service_set_state(s, SERVICE_EXITED); - else - service_enter_stop(s, SERVICE_SUCCESS); -} - -static void service_enter_start_post(Service *s) { - int r; - assert(s); - - service_unwatch_control_pid(s); - service_reset_watchdog(s); - - s->control_command = s->exec_command[SERVICE_EXEC_START_POST]; - if (s->control_command) { - s->control_command_id = SERVICE_EXEC_START_POST; - - r = service_spawn(s, - s->control_command, - s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_START_POST); - } else - service_enter_running(s, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static void service_kill_control_processes(Service *s) { - char *p; - - if (!UNIT(s)->cgroup_path) - return; - - p = strjoina(UNIT(s)->cgroup_path, "/control"); - cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, CGROUP_SIGCONT|CGROUP_IGNORE_SELF|CGROUP_REMOVE, NULL, NULL, NULL); -} - -static void service_enter_start(Service *s) { - ExecCommand *c; - usec_t timeout; - pid_t pid; - int r; - - assert(s); - - service_unwatch_control_pid(s); - service_unwatch_main_pid(s); - - /* We want to ensure that nobody leaks processes from - * START_PRE here, so let's go on a killing spree, People - * should not spawn long running processes from START_PRE. */ - service_kill_control_processes(s); - - if (s->type == SERVICE_FORKING) { - s->control_command_id = SERVICE_EXEC_START; - c = s->control_command = s->exec_command[SERVICE_EXEC_START]; - - s->main_command = NULL; - } else { - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - s->control_command = NULL; - - c = s->main_command = s->exec_command[SERVICE_EXEC_START]; - } - - if (!c) { - assert(s->type == SERVICE_ONESHOT); - service_enter_start_post(s); - return; - } - - if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) - /* For simple + idle this is the main process. We don't apply any timeout here, but - * service_enter_running() will later apply the .runtime_max_usec timeout. */ - timeout = USEC_INFINITY; - else - timeout = s->timeout_start_usec; - - r = service_spawn(s, - c, - timeout, - true, - true, - true, - true, - false, - &pid); - if (r < 0) - goto fail; - - if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) { - /* For simple services we immediately start - * the START_POST binaries. */ - - service_set_main_pid(s, pid); - service_enter_start_post(s); - - } else if (s->type == SERVICE_FORKING) { - - /* For forking services we wait until the start - * process exited. */ - - s->control_pid = pid; - service_set_state(s, SERVICE_START); - - } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) { - - /* For oneshot services we wait until the start - * process exited, too, but it is our main process. */ - - /* For D-Bus services we know the main pid right away, - * but wait for the bus name to appear on the - * bus. Notify services are similar. */ - - service_set_main_pid(s, pid); - service_set_state(s, SERVICE_START); - } else - assert_not_reached("Unknown service type"); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m"); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); -} - -static void service_enter_start_pre(Service *s) { - int r; - - assert(s); - - service_unwatch_control_pid(s); - - s->control_command = s->exec_command[SERVICE_EXEC_START_PRE]; - if (s->control_command) { - /* Before we start anything, let's clear up what might - * be left from previous runs. */ - service_kill_control_processes(s); - - s->control_command_id = SERVICE_EXEC_START_PRE; - - r = service_spawn(s, - s->control_command, - s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_START_PRE); - } else - service_enter_start(s); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); -} - -static void service_enter_restart(Service *s) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(s); - - if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { - /* Don't restart things if we are going down anyway */ - log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart."); - - r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); - if (r < 0) - goto fail; - - return; - } - - /* Any units that are bound to this service must also be - * 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, &error, NULL); - if (r < 0) - goto fail; - - /* Note that we stay in the SERVICE_AUTO_RESTART state here, - * it will be canceled as part of the service_stop() call that - * is executed as part of JOB_RESTART. */ - - log_unit_debug(UNIT(s), "Scheduled restart job."); - return; - -fail: - log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, -r)); - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); -} - -static void service_enter_reload_by_notify(Service *s) { - assert(s); - - service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_start_usec)); - service_set_state(s, SERVICE_RELOAD); -} - -static void service_enter_reload(Service *s) { - int r; - - 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) { - s->control_command_id = SERVICE_EXEC_RELOAD; - - r = service_spawn(s, - s->control_command, - s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, - &s->control_pid); - if (r < 0) - goto fail; - - service_set_state(s, SERVICE_RELOAD); - } else - service_enter_running(s, SERVICE_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m"); - s->reload_result = SERVICE_FAILURE_RESOURCES; - service_enter_running(s, SERVICE_SUCCESS); -} - -static void service_run_next_control(Service *s) { - usec_t timeout; - int r; - - assert(s); - assert(s->control_command); - assert(s->control_command->command_next); - - assert(s->control_command_id != SERVICE_EXEC_START); - - s->control_command = s->control_command->command_next; - service_unwatch_control_pid(s); - - if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) - timeout = s->timeout_start_usec; - else - timeout = s->timeout_stop_usec; - - r = service_spawn(s, - s->control_command, - timeout, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - true, - &s->control_pid); - if (r < 0) - goto fail; - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m"); - - if (s->state == SERVICE_START_PRE) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - else if (s->state == SERVICE_STOP) - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); - else if (s->state == SERVICE_STOP_POST) - service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); - else if (s->state == SERVICE_RELOAD) { - s->reload_result = SERVICE_FAILURE_RESOURCES; - service_enter_running(s, SERVICE_SUCCESS); - } else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static void service_run_next_main(Service *s) { - pid_t pid; - int r; - - assert(s); - assert(s->main_command); - assert(s->main_command->command_next); - assert(s->type == SERVICE_ONESHOT); - - s->main_command = s->main_command->command_next; - service_unwatch_main_pid(s); - - r = service_spawn(s, - s->main_command, - s->timeout_start_usec, - true, - true, - true, - true, - false, - &pid); - if (r < 0) - goto fail; - - service_set_main_pid(s, pid); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next main task: %m"); - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); -} - -static int service_start(Unit *u) { - Service *s = SERVICE(u); - int r; - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (IN_SET(s->state, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) - return -EAGAIN; - - /* Already on it! */ - if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) - return 0; - - /* A service that will be restarted must be stopped first to - * trigger BindsTo and/or OnFailure dependencies. If a user - * does not want to wait for the holdoff time to elapse, the - * service should be manually restarted, not started. We - * simply return EAGAIN here, so that any start jobs stay - * queued, and assume that the auto restart timer will - * eventually trigger the restart. */ - if (s->state == SERVICE_AUTO_RESTART) - return -EAGAIN; - - assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); - - /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); - if (r < 0) { - service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); - return r; - } - - s->result = SERVICE_SUCCESS; - s->reload_result = SERVICE_SUCCESS; - s->main_pid_known = false; - s->main_pid_alien = false; - s->forbid_restart = false; - s->reset_cpu_usage = true; - - s->status_text = mfree(s->status_text); - s->status_errno = 0; - - s->notify_state = NOTIFY_UNKNOWN; - - s->watchdog_override_enable = false; - s->watchdog_override_usec = 0; - - service_enter_start_pre(s); - return 1; -} - -static int service_stop(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - /* Don't create restart jobs from manual stops. */ - s->forbid_restart = true; - - /* Already on it */ - if (IN_SET(s->state, - SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) - return 0; - - /* A restart will be scheduled or is in progress. */ - if (s->state == SERVICE_AUTO_RESTART) { - service_set_state(s, SERVICE_DEAD); - return 0; - } - - /* If there's already something running we go directly into - * kill mode. */ - if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) { - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); - return 0; - } - - assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED)); - - service_enter_stop(s, SERVICE_SUCCESS); - return 1; -} - -static int service_reload(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED); - - service_enter_reload(s); - return 1; -} - -_pure_ static bool service_can_reload(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return !!s->exec_command[SERVICE_EXEC_RELOAD]; -} - -static int service_serialize(Unit *u, FILE *f, FDSet *fds) { - Service *s = SERVICE(u); - ServiceFDStore *fs; - int r; - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", service_state_to_string(s->state)); - unit_serialize_item(u, f, "result", service_result_to_string(s->result)); - unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result)); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); - - if (s->main_pid_known && s->main_pid > 0) - unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid); - - unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known)); - 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); - - 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 - * first one again */ - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); - - 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; - - r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_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; - - 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) { - unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid); - dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp); - dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp); - - if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { - unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code); - unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status); - } - } - - dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); - - unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart)); - - if (s->watchdog_override_enable) - unit_serialize_item_format(u, f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec); - - return 0; -} - -static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Service *s = SERVICE(u); - int r; - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - ServiceState state; - - state = service_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, "result")) { - ServiceResult f; - - f = service_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != SERVICE_SUCCESS) - s->result = f; - - } else if (streq(key, "reload-result")) { - ServiceResult f; - - f = service_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse reload result value: %s", value); - else if (f != SERVICE_SUCCESS) - s->reload_result = f; - - } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - s->control_pid = pid; - } else if (streq(key, "main-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse main-pid value: %s", value); - else { - service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); - } - } else if (streq(key, "main-pid-known")) { - int b; - - b = parse_boolean(value); - if (b < 0) - log_unit_debug(u, "Failed to parse main-pid-known value: %s", value); - else - s->main_pid_known = b; - } else if (streq(key, "bus-name-good")) { - int b; - - b = parse_boolean(value); - if (b < 0) - 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; - - r = cunescape(value, 0, &t); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value); - else { - free(s->status_text); - s->status_text = t; - } - - } else if (streq(key, "control-command")) { - ServiceExecCommand id; - - id = service_exec_command_from_string(value); - if (id < 0) - log_unit_debug(u, "Failed to parse exec-command value: %s", value); - else { - s->control_command_id = id; - s->control_command = s->exec_command[id]; - } - } else if (streq(key, "socket-fd")) { - int fd; - - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse socket-fd value: %s", value); - else { - asynchronous_close(s->socket_fd); - s->socket_fd = fdset_remove(fds, fd); - } - } else if (streq(key, "fd-store-fd")) { - const char *fdv; - size_t pf; - int 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 { - _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) - fdset_remove(fds, fd); - } - - } else if (streq(key, "main-exec-status-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse main-exec-status-pid value: %s", value); - else - s->main_exec_status.pid = pid; - } else if (streq(key, "main-exec-status-code")) { - int i; - - if (safe_atoi(value, &i) < 0) - log_unit_debug(u, "Failed to parse main-exec-status-code value: %s", value); - else - s->main_exec_status.code = i; - } else if (streq(key, "main-exec-status-status")) { - int i; - - if (safe_atoi(value, &i) < 0) - log_unit_debug(u, "Failed to parse main-exec-status-status value: %s", value); - else - s->main_exec_status.status = i; - } else if (streq(key, "main-exec-status-start")) - dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp); - else if (streq(key, "main-exec-status-exit")) - dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp); - else if (streq(key, "watchdog-timestamp")) - dual_timestamp_deserialize(value, &s->watchdog_timestamp); - else if (streq(key, "forbid-restart")) { - int b; - - b = parse_boolean(value); - if (b < 0) - 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); - s->exec_context.stdio_as_fds = true; - } - } 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); - s->exec_context.stdio_as_fds = true; - } - } 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); - s->exec_context.stdio_as_fds = true; - } - } else if (streq(key, "watchdog-override-usec")) { - usec_t watchdog_override_usec; - if (timestamp_deserialize(value, &watchdog_override_usec) < 0) - log_unit_debug(u, "Failed to parse watchdog_override_usec value: %s", value); - else { - s->watchdog_override_enable = true; - s->watchdog_override_usec = watchdog_override_usec; - } - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState service_active_state(Unit *u) { - const UnitActiveState *table; - - assert(u); - - table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; - - return table[SERVICE(u)->state]; -} - -static const char *service_sub_state_to_string(Unit *u) { - assert(u); - - return service_state_to_string(SERVICE(u)->state); -} - -static bool service_check_gc(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - /* Never clean up services that still have a process around, - * even if the service is formally dead. */ - if (cgroup_good(s) > 0 || - main_pid_good(s) > 0 || - control_pid_good(s) > 0) - return true; - - return false; -} - -static int service_retry_pid_file(Service *s) { - int r; - - assert(s->pid_file); - assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); - - r = service_load_pid_file(s, false); - if (r < 0) - return r; - - service_unwatch_pid_file(s); - - service_enter_running(s, SERVICE_SUCCESS); - return 0; -} - -static int service_watch_pid_file(Service *s) { - int r; - - log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path); - - r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io); - if (r < 0) - goto fail; - - /* the pidfile might have appeared just before we set the watch */ - log_unit_debug(UNIT(s), "Trying to read PID file %s in case it changed", s->pid_file_pathspec->path); - service_retry_pid_file(s); - - return 0; -fail: - log_unit_error_errno(UNIT(s), r, "Failed to set a watch for PID file %s: %m", s->pid_file_pathspec->path); - service_unwatch_pid_file(s); - return r; -} - -static int service_demand_pid_file(Service *s) { - PathSpec *ps; - - assert(s->pid_file); - assert(!s->pid_file_pathspec); - - ps = new0(PathSpec, 1); - if (!ps) - return -ENOMEM; - - ps->unit = UNIT(s); - ps->path = strdup(s->pid_file); - if (!ps->path) { - free(ps); - return -ENOMEM; - } - - path_kill_slashes(ps->path); - - /* PATH_CHANGED would not be enough. There are daemons (sendmail) that - * keep their PID file open all the time. */ - ps->type = PATH_MODIFIED; - ps->inotify_fd = -1; - - s->pid_file_pathspec = ps; - - return service_watch_pid_file(s); -} - -static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata) { - PathSpec *p = userdata; - Service *s; - - assert(p); - - s = SERVICE(p->unit); - - assert(s); - assert(fd >= 0); - assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); - assert(s->pid_file_pathspec); - assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); - - log_unit_debug(UNIT(s), "inotify event"); - - if (path_spec_fd_event(p, events) < 0) - goto fail; - - if (service_retry_pid_file(s) == 0) - return 0; - - if (service_watch_pid_file(s) < 0) - goto fail; - - return 0; - -fail: - service_unwatch_pid_file(s); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); - return 0; -} - -static void service_notify_cgroup_empty_event(Unit *u) { - Service *s = SERVICE(u); - - assert(u); - - log_unit_debug(u, "cgroup is empty"); - - switch (s->state) { - - /* Waiting for SIGCHLD is usually more interesting, - * because it includes return codes/signals. Which is - * why we ignore the cgroup events for most cases, - * except when we don't know pid which to expect the - * SIGCHLD for. */ - - case SERVICE_START: - case SERVICE_START_POST: - /* If we were hoping for the daemon to write its PID file, - * we can give up now. */ - if (s->pid_file_pathspec) { - log_unit_warning(u, "Daemon never wrote its PID file. Failing."); - - service_unwatch_pid_file(s); - if (s->state == SERVICE_START) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - else - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); - } - break; - - case SERVICE_RUNNING: - /* service_enter_running() will figure out what to do */ - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP_SIGABRT: - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_stop_post(s, SERVICE_SUCCESS); - - break; - - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - if (main_pid_good(s) <= 0 && !control_pid_good(s)) - service_enter_dead(s, SERVICE_SUCCESS, true); - - break; - - default: - ; - } -} - -static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Service *s = SERVICE(u); - ServiceResult f; - - assert(s); - assert(pid >= 0); - - if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : - is_clean_exit_lsb(code, status, &s->success_status)) - f = SERVICE_SUCCESS; - else if (code == CLD_EXITED) - f = SERVICE_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = SERVICE_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SERVICE_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (s->main_pid == pid) { - /* Forking services may occasionally move to a new PID. - * As long as they update the PID file before exiting the old - * PID, they're fine. */ - if (service_load_pid_file(s, false) == 0) - return; - - s->main_pid = 0; - exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); - - if (s->main_command) { - /* If this is not a forking service than the - * main process got started and hence we copy - * the exit status so that it is recorded both - * as main and as control process exit - * status */ - - s->main_command->exec_status = s->main_exec_status; - - if (s->main_command->ignore) - f = SERVICE_SUCCESS; - } else if (s->exec_command[SERVICE_EXEC_START]) { - - /* If this is a forked process, then we should - * ignore the return value if this was - * configured for the starter process */ - - if (s->exec_command[SERVICE_EXEC_START]->ignore) - f = SERVICE_SUCCESS; - } - - log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - LOG_UNIT_ID(u), - LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s", - sigchld_code_to_string(code), status, - strna(code == CLD_EXITED - ? exit_status_to_string(status, EXIT_STATUS_FULL) - : signal_to_string(status))), - "EXIT_CODE=%s", sigchld_code_to_string(code), - "EXIT_STATUS=%i", status, - NULL); - - if (f != SERVICE_SUCCESS) - s->result = f; - - if (s->main_command && - s->main_command->command_next && - f == SERVICE_SUCCESS) { - - /* There is another command to * - * execute, so let's do that. */ - - log_unit_debug(u, "Running next main command for state %s.", service_state_to_string(s->state)); - service_run_next_main(s); - - } else { - - /* The service exited, so the service is officially - * gone. */ - s->main_command = NULL; - - switch (s->state) { - - case SERVICE_START_POST: - case SERVICE_RELOAD: - case SERVICE_STOP: - /* Need to wait until the operation is - * done */ - break; - - case SERVICE_START: - if (s->type == SERVICE_ONESHOT) { - /* This was our main goal, so let's go on */ - if (f == SERVICE_SUCCESS) - service_enter_start_post(s); - else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - break; - } - - /* Fall through */ - - case SERVICE_RUNNING: - service_enter_running(s, f); - break; - - case SERVICE_STOP_SIGABRT: - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - - if (!control_pid_good(s)) - service_enter_stop_post(s, f); - - /* If there is still a control process, wait for that first */ - break; - - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - - if (!control_pid_good(s)) - service_enter_dead(s, f, true); - break; - - default: - assert_not_reached("Uh, main process died at wrong time."); - } - } - - } else if (s->control_pid == pid) { - s->control_pid = 0; - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - if (s->control_command->ignore) - f = SERVICE_SUCCESS; - } - - log_unit_full(u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, - "Control process exited, code=%s status=%i", - sigchld_code_to_string(code), status); - - if (f != SERVICE_SUCCESS) - s->result = f; - - /* Immediately get rid of the cgroup, so that the - * kernel doesn't delay the cgroup empty messages for - * the service cgroup any longer than necessary */ - service_kill_control_processes(s); - - if (s->control_command && - s->control_command->command_next && - f == SERVICE_SUCCESS) { - - /* There is another command to * - * execute, so let's do that. */ - - log_unit_debug(u, "Running next control command for state %s.", service_state_to_string(s->state)); - service_run_next_control(s); - - } else { - /* No further commands for this step, so let's - * figure out what to do next */ - - s->control_command = NULL; - s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - - log_unit_debug(u, "Got final SIGCHLD for state %s.", service_state_to_string(s->state)); - - switch (s->state) { - - case SERVICE_START_PRE: - if (f == SERVICE_SUCCESS) - service_enter_start(s); - else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - break; - - case SERVICE_START: - if (s->type != SERVICE_FORKING) - /* Maybe spurious event due to a reload that changed the type? */ - break; - - if (f != SERVICE_SUCCESS) { - service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); - break; - } - - if (s->pid_file) { - bool has_start_post; - int r; - - /* Let's try to load the pid file here if we can. - * The PID file might actually be created by a START_POST - * script. In that case don't worry if the loading fails. */ - - has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; - r = service_load_pid_file(s, !has_start_post); - if (!has_start_post && r < 0) { - r = service_demand_pid_file(s); - if (r < 0 || !cgroup_good(s)) - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); - break; - } - } else - service_search_main_pid(s); - - service_enter_start_post(s); - break; - - case SERVICE_START_POST: - if (f != SERVICE_SUCCESS) { - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - break; - } - - if (s->pid_file) { - int r; - - r = service_load_pid_file(s, true); - if (r < 0) { - r = service_demand_pid_file(s); - if (r < 0 || !cgroup_good(s)) - service_enter_stop(s, SERVICE_FAILURE_RESOURCES); - break; - } - } else - service_search_main_pid(s); - - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_RELOAD: - if (f == SERVICE_SUCCESS) - if (service_load_pid_file(s, true) < 0) - service_search_main_pid(s); - - s->reload_result = f; - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP: - service_enter_signal(s, SERVICE_STOP_SIGTERM, f); - break; - - case SERVICE_STOP_SIGABRT: - case SERVICE_STOP_SIGTERM: - case SERVICE_STOP_SIGKILL: - if (main_pid_good(s) <= 0) - service_enter_stop_post(s, f); - - /* If there is still a service - * process around, wait until - * that one quit, too */ - break; - - case SERVICE_STOP_POST: - case SERVICE_FINAL_SIGTERM: - case SERVICE_FINAL_SIGKILL: - if (main_pid_good(s) <= 0) - service_enter_dead(s, f, true); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - } - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); - - /* We got one SIGCHLD for the service, let's watch all - * processes that are now running of the service, and watch - * that. Among the PIDs we then watch will be children - * reassigned to us, which hopefully allows us to identify - * when all children are gone */ - unit_tidy_watch_pids(u, s->main_pid, s->control_pid); - unit_watch_all_pids(u); - - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified() <= 0 && set_isempty(u->pids)) - service_notify_cgroup_empty_event(u); -} - -static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - Service *s = SERVICE(userdata); - - assert(s); - assert(source == s->timer_event_source); - - switch (s->state) { - - case SERVICE_START_PRE: - case SERVICE_START: - log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre"); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_START_POST: - log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_RUNNING: - log_unit_warning(UNIT(s), "Service reached runtime time limit. Stopping."); - service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_RELOAD: - log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process."); - service_kill_control_processes(s); - s->reload_result = SERVICE_FAILURE_TIMEOUT; - service_enter_running(s, SERVICE_SUCCESS); - break; - - case SERVICE_STOP: - log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_STOP_SIGABRT: - log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating."); - service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_STOP_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Killing."); - service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Skipping SIGKILL."); - service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); - } - - break; - - case SERVICE_STOP_SIGKILL: - /* Uh, we sent a SIGKILL and it is still not gone? - * Must be something we cannot kill, so let's just be - * weirded out and continue */ - - log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring."); - service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_STOP_POST: - log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating."); - service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); - break; - - case SERVICE_FINAL_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Killing."); - service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Skipping SIGKILL. Entering failed mode."); - service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); - } - - break; - - case SERVICE_FINAL_SIGKILL: - log_unit_warning(UNIT(s), "Processes still around after final SIGKILL. Entering failed mode."); - service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); - break; - - case SERVICE_AUTO_RESTART: - log_unit_info(UNIT(s), - s->restart_usec > 0 ? - "Service hold-off time over, scheduling restart." : - "Service has no hold-off time, scheduling restart."); - service_enter_restart(s); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata) { - Service *s = SERVICE(userdata); - char t[FORMAT_TIMESPAN_MAX]; - usec_t watchdog_usec; - - assert(s); - assert(source == s->watchdog_event_source); - - watchdog_usec = service_get_watchdog_usec(s); - - log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", - format_timespan(t, sizeof(t), watchdog_usec, 1)); - - service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); - - return 0; -} - -static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) { - Service *s = SERVICE(u); - _cleanup_free_ char *cc = NULL; - bool notify_dbus = false; - const char *e; - - assert(u); - - cc = strv_join(tags, ", "); - - if (s->notify_access == NOTIFY_NONE) { - log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception is disabled.", pid); - return; - } else if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { - if (s->main_pid != 0) - log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid); - else - log_unit_debug(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid); - return; - } else - log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc); - - /* Interpret MAINPID= */ - e = strv_find_startswith(tags, "MAINPID="); - if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) { - if (parse_pid(e, &pid) < 0) - log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e); - else { - service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); - notify_dbus = true; - } - } - - /* Interpret RELOADING= */ - if (strv_find(tags, "RELOADING=1")) { - - s->notify_state = NOTIFY_RELOADING; - - if (s->state == SERVICE_RUNNING) - service_enter_reload_by_notify(s); - - notify_dbus = true; - } - - /* Interpret READY= */ - if (strv_find(tags, "READY=1")) { - - s->notify_state = NOTIFY_READY; - - /* Type=notify services inform us about completed - * initialization with READY=1 */ - if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START) - service_enter_start_post(s); - - /* Sending READY=1 while we are reloading informs us - * that the reloading is complete */ - if (s->state == SERVICE_RELOAD && s->control_pid == 0) - service_enter_running(s, SERVICE_SUCCESS); - - notify_dbus = true; - } - - /* Interpret STOPPING= */ - if (strv_find(tags, "STOPPING=1")) { - - s->notify_state = NOTIFY_STOPPING; - - if (s->state == SERVICE_RUNNING) - service_enter_stop_by_notify(s); - - notify_dbus = true; - } - - /* Interpret STATUS= */ - e = strv_find_startswith(tags, "STATUS="); - if (e) { - _cleanup_free_ char *t = NULL; - - if (!isempty(e)) { - if (!utf8_is_valid(e)) - log_unit_warning(u, "Status message in notification message is not UTF-8 clean."); - else { - t = strdup(e); - if (!t) - log_oom(); - } - } - - if (!streq_ptr(s->status_text, t)) { - - free(s->status_text); - s->status_text = t; - t = NULL; - - notify_dbus = true; - } - } - - /* Interpret ERRNO= */ - e = strv_find_startswith(tags, "ERRNO="); - if (e) { - int status_errno; - - if (safe_atoi(e, &status_errno) < 0 || status_errno < 0) - log_unit_warning(u, "Failed to parse ERRNO= field in notification message: %s", e); - else { - if (s->status_errno != status_errno) { - s->status_errno = status_errno; - notify_dbus = true; - } - } - } - - /* Interpret WATCHDOG= */ - if (strv_find(tags, "WATCHDOG=1")) - service_reset_watchdog(s); - - 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); - } - - e = strv_find_startswith(tags, "WATCHDOG_USEC="); - if (e) { - usec_t watchdog_override_usec; - if (safe_atou64(e, &watchdog_override_usec) < 0) - log_unit_warning(u, "Failed to parse WATCHDOG_USEC=%s", e); - else - service_reset_watchdog_timeout(s, watchdog_override_usec); - } - - /* Notify clients about changed status or main pid */ - if (notify_dbus) - unit_add_to_dbus_queue(u); -} - -static int service_get_timeout(Unit *u, usec_t *timeout) { - Service *s = SERVICE(u); - uint64_t t; - int r; - - if (!s->timer_event_source) - return 0; - - r = sd_event_source_get_time(s->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -static void service_bus_name_owner_change( - Unit *u, - const char *name, - const char *old_owner, - const char *new_owner) { - - Service *s = SERVICE(u); - int r; - - assert(s); - assert(name); - - assert(streq(s->bus_name, name)); - assert(old_owner || new_owner); - - if (old_owner && new_owner) - log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", name, old_owner, new_owner); - else if (old_owner) - log_unit_debug(u, "D-Bus name %s no longer registered by %s", name, old_owner); - else - log_unit_debug(u, "D-Bus name %s now registered by %s", name, new_owner); - - 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 - * do */ - if (s->state == SERVICE_RUNNING) - service_enter_running(s, SERVICE_SUCCESS); - else if (s->state == SERVICE_START && new_owner) - service_enter_start_post(s); - - } else if (new_owner && - s->main_pid <= 0 && - (s->state == SERVICE_START || - s->state == SERVICE_START_POST || - s->state == SERVICE_RUNNING || - s->state == SERVICE_RELOAD)) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - pid_t pid; - - /* Try to acquire PID from bus service */ - - r = sd_bus_get_name_creds(u->manager->api_bus, name, SD_BUS_CREDS_PID, &creds); - if (r >= 0) - r = sd_bus_creds_get_pid(creds, &pid); - if (r >= 0) { - log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid); - - service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); - } - } -} - -int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context_net) { - _cleanup_free_ char *peer = NULL; - int r; - - assert(s); - assert(fd >= 0); - - /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs - * to be configured. We take ownership of the passed fd on success. */ - - if (UNIT(s)->load_state != UNIT_LOADED) - return -EINVAL; - - if (s->socket_fd >= 0) - return -EBUSY; - - if (s->state != SERVICE_DEAD) - return -EAGAIN; - - if (getpeername_pretty(fd, true, &peer) >= 0) { - - if (UNIT(s)->description) { - _cleanup_free_ char *a; - - a = strjoin(UNIT(s)->description, " (", peer, ")", NULL); - if (!a) - return -ENOMEM; - - r = unit_set_description(UNIT(s), a); - } else - r = unit_set_description(UNIT(s), peer); - - if (r < 0) - return r; - } - - r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); - if (r < 0) - return r; - - s->socket_fd = fd; - s->socket_fd_selinux_context_net = selinux_context_net; - - unit_ref_set(&s->accept_socket, UNIT(sock)); - return 0; -} - -static void service_reset_failed(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - if (s->state == SERVICE_FAILED) - service_set_state(s, SERVICE_DEAD); - - s->result = SERVICE_SUCCESS; - s->reload_result = SERVICE_SUCCESS; -} - -static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - Service *s = SERVICE(u); - - return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); -} - -static int service_main_pid(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->main_pid; -} - -static int service_control_pid(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->control_pid; -} - -static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { - [SERVICE_RESTART_NO] = "no", - [SERVICE_RESTART_ON_SUCCESS] = "on-success", - [SERVICE_RESTART_ON_FAILURE] = "on-failure", - [SERVICE_RESTART_ON_ABNORMAL] = "on-abnormal", - [SERVICE_RESTART_ON_WATCHDOG] = "on-watchdog", - [SERVICE_RESTART_ON_ABORT] = "on-abort", - [SERVICE_RESTART_ALWAYS] = "always", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart); - -static const char* const service_type_table[_SERVICE_TYPE_MAX] = { - [SERVICE_SIMPLE] = "simple", - [SERVICE_FORKING] = "forking", - [SERVICE_ONESHOT] = "oneshot", - [SERVICE_DBUS] = "dbus", - [SERVICE_NOTIFY] = "notify", - [SERVICE_IDLE] = "idle" -}; - -DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); - -static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { - [SERVICE_EXEC_START_PRE] = "ExecStartPre", - [SERVICE_EXEC_START] = "ExecStart", - [SERVICE_EXEC_START_POST] = "ExecStartPost", - [SERVICE_EXEC_RELOAD] = "ExecReload", - [SERVICE_EXEC_STOP] = "ExecStop", - [SERVICE_EXEC_STOP_POST] = "ExecStopPost", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand); - -static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { - [NOTIFY_NONE] = "none", - [NOTIFY_MAIN] = "main", - [NOTIFY_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); - -static const char* const notify_state_table[_NOTIFY_STATE_MAX] = { - [NOTIFY_UNKNOWN] = "unknown", - [NOTIFY_READY] = "ready", - [NOTIFY_RELOADING] = "reloading", - [NOTIFY_STOPPING] = "stopping", -}; - -DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState); - -static const char* const service_result_table[_SERVICE_RESULT_MAX] = { - [SERVICE_SUCCESS] = "success", - [SERVICE_FAILURE_RESOURCES] = "resources", - [SERVICE_FAILURE_TIMEOUT] = "timeout", - [SERVICE_FAILURE_EXIT_CODE] = "exit-code", - [SERVICE_FAILURE_SIGNAL] = "signal", - [SERVICE_FAILURE_CORE_DUMP] = "core-dump", - [SERVICE_FAILURE_WATCHDOG] = "watchdog", - [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); - -const UnitVTable service_vtable = { - .object_size = sizeof(Service), - .exec_context_offset = offsetof(Service, exec_context), - .cgroup_context_offset = offsetof(Service, cgroup_context), - .kill_context_offset = offsetof(Service, kill_context), - .exec_runtime_offset = offsetof(Service, exec_runtime), - - .sections = - "Unit\0" - "Service\0" - "Install\0", - .private_section = "Service", - - .init = service_init, - .done = service_done, - .load = service_load, - .release_resources = service_release_resources, - - .coldplug = service_coldplug, - - .dump = service_dump, - - .start = service_start, - .stop = service_stop, - .reload = service_reload, - - .can_reload = service_can_reload, - - .kill = service_kill, - - .serialize = service_serialize, - .deserialize_item = service_deserialize_item, - - .active_state = service_active_state, - .sub_state_to_string = service_sub_state_to_string, - - .check_gc = service_check_gc, - - .sigchld_event = service_sigchld_event, - - .reset_failed = service_reset_failed, - - .notify_cgroup_empty = service_notify_cgroup_empty_event, - .notify_message = service_notify_message, - - .main_pid = service_main_pid, - .control_pid = service_control_pid, - - .bus_name_owner_change = service_bus_name_owner_change, - - .bus_vtable = bus_service_vtable, - .bus_set_property = bus_service_set_property, - .bus_commit_properties = bus_service_commit_properties, - - .get_timeout = service_get_timeout, - .can_transient = true, - - .status_message_formats = { - .starting_stopping = { - [0] = "Starting %s...", - [1] = "Stopping %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Started %s.", - [JOB_FAILED] = "Failed to start %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Stopped %s.", - [JOB_FAILED] = "Stopped (with error) %s.", - }, - }, -}; diff --git a/src/core/service.h b/src/core/service.h deleted file mode 100644 index cfef375b03..0000000000 --- a/src/core/service.h +++ /dev/null @@ -1,222 +0,0 @@ -#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 . -***/ - -typedef struct Service Service; -typedef struct ServiceFDStore ServiceFDStore; - -#include "exit-status.h" -#include "kill.h" -#include "path.h" -#include "ratelimit.h" - -typedef enum ServiceRestart { - SERVICE_RESTART_NO, - SERVICE_RESTART_ON_SUCCESS, - SERVICE_RESTART_ON_FAILURE, - SERVICE_RESTART_ON_ABNORMAL, - SERVICE_RESTART_ON_WATCHDOG, - SERVICE_RESTART_ON_ABORT, - SERVICE_RESTART_ALWAYS, - _SERVICE_RESTART_MAX, - _SERVICE_RESTART_INVALID = -1 -} ServiceRestart; - -typedef enum ServiceType { - SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */ - SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */ - SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */ - SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */ - SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */ - SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */ - _SERVICE_TYPE_MAX, - _SERVICE_TYPE_INVALID = -1 -} ServiceType; - -typedef enum ServiceExecCommand { - SERVICE_EXEC_START_PRE, - SERVICE_EXEC_START, - SERVICE_EXEC_START_POST, - SERVICE_EXEC_RELOAD, - SERVICE_EXEC_STOP, - SERVICE_EXEC_STOP_POST, - _SERVICE_EXEC_COMMAND_MAX, - _SERVICE_EXEC_COMMAND_INVALID = -1 -} ServiceExecCommand; - -typedef enum NotifyAccess { - NOTIFY_NONE, - NOTIFY_ALL, - NOTIFY_MAIN, - _NOTIFY_ACCESS_MAX, - _NOTIFY_ACCESS_INVALID = -1 -} NotifyAccess; - -typedef enum NotifyState { - NOTIFY_UNKNOWN, - NOTIFY_READY, - NOTIFY_RELOADING, - NOTIFY_STOPPING, - _NOTIFY_STATE_MAX, - _NOTIFY_STATE_INVALID = -1 -} NotifyState; - -typedef enum ServiceResult { - SERVICE_SUCCESS, - SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ - SERVICE_FAILURE_TIMEOUT, - SERVICE_FAILURE_EXIT_CODE, - SERVICE_FAILURE_SIGNAL, - SERVICE_FAILURE_CORE_DUMP, - SERVICE_FAILURE_WATCHDOG, - SERVICE_FAILURE_START_LIMIT_HIT, - _SERVICE_RESULT_MAX, - _SERVICE_RESULT_INVALID = -1 -} ServiceResult; - -struct ServiceFDStore { - Service *service; - - int fd; - char *fdname; - sd_event_source *event_source; - - LIST_FIELDS(ServiceFDStore, fd_store); -}; - -struct Service { - Unit meta; - - ServiceType type; - ServiceRestart restart; - ExitStatusSet restart_prevent_status; - ExitStatusSet restart_force_status; - ExitStatusSet success_status; - - /* If set we'll read the main daemon PID from this file */ - char *pid_file; - - usec_t restart_usec; - usec_t timeout_start_usec; - usec_t timeout_stop_usec; - usec_t runtime_max_usec; - - dual_timestamp watchdog_timestamp; - usec_t watchdog_usec; - usec_t watchdog_override_usec; - bool watchdog_override_enable; - sd_event_source *watchdog_event_source; - - ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX]; - - ExecContext exec_context; - KillContext kill_context; - CGroupContext cgroup_context; - - ServiceState state, deserialized_state; - - /* The exit status of the real main process */ - ExecStatus main_exec_status; - - /* The currently executed control process */ - ExecCommand *control_command; - - /* The currently executed main process, which may be NULL if - * the main process got started via forking mode and not by - * us */ - ExecCommand *main_command; - - /* The ID of the control command currently being executed */ - ServiceExecCommand control_command_id; - - /* Runtime data of the execution context */ - ExecRuntime *exec_runtime; - - pid_t main_pid, control_pid; - int socket_fd; - bool socket_fd_selinux_context_net; - - bool permissions_start_only; - bool root_directory_start_only; - bool remain_after_exit; - bool guess_main_pid; - - /* If we shut down, remember why */ - ServiceResult result; - ServiceResult reload_result; - - bool main_pid_known:1; - bool main_pid_alien:1; - bool bus_name_good:1; - bool forbid_restart:1; - bool start_timeout_defined:1; - - bool reset_cpu_usage:1; - - char *bus_name; - char *bus_name_owner; /* unique name of the current owner */ - - char *status_text; - int status_errno; - - FailureAction failure_action; - - UnitRef accept_socket; - - sd_event_source *timer_event_source; - PathSpec *pid_file_pathspec; - - NotifyAccess notify_access; - NotifyState notify_state; - - ServiceFDStore *fd_store; - unsigned n_fd_store; - unsigned n_fd_store_max; - - 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); -void service_close_socket_fd(Service *s); - -const char* service_restart_to_string(ServiceRestart i) _const_; -ServiceRestart service_restart_from_string(const char *s) _pure_; - -const char* service_type_to_string(ServiceType i) _const_; -ServiceType service_type_from_string(const char *s) _pure_; - -const char* service_exec_command_to_string(ServiceExecCommand i) _const_; -ServiceExecCommand service_exec_command_from_string(const char *s) _pure_; - -const char* notify_access_to_string(NotifyAccess i) _const_; -NotifyAccess notify_access_from_string(const char *s) _pure_; - -const char* notify_state_to_string(NotifyState i) _const_; -NotifyState notify_state_from_string(const char *s) _pure_; - -const char* service_result_to_string(ServiceResult i) _const_; -ServiceResult service_result_from_string(const char *s) _pure_; diff --git a/src/core/show-status.c b/src/core/show-status.c deleted file mode 100644 index 59ebdc7219..0000000000 --- a/src/core/show-status.c +++ /dev/null @@ -1,124 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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) { - int r; - - assert(v); - assert(ret); - - if (streq(v, "auto")) { - *ret = SHOW_STATUS_AUTO; - return 0; - } - - r = parse_boolean(v); - if (r < 0) - return r; - - *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 deleted file mode 100644 index 9a29e72645..0000000000 --- a/src/core/show-status.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" - -/* Manager status */ - -typedef enum ShowStatus { - _SHOW_STATUS_UNSET = -2, - SHOW_STATUS_AUTO = -1, - SHOW_STATUS_NO = 0, - SHOW_STATUS_YES = 1, - SHOW_STATUS_TEMPORARY = 2, -} 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 deleted file mode 100644 index a795d875bb..0000000000 --- a/src/core/shutdown.c +++ /dev/null @@ -1,444 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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" - -#define FINALIZE_ATTEMPTS 50 - -static char* arg_verb; -static uint8_t arg_exit_code; - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - ARG_EXIT_CODE, - }; - - static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, - {} - }; - - int c, r; - - assert(argc >= 1); - assert(argv); - - /* "-" prevents getopt from permuting argv[] and moving the verb away - * from argv[1]. Our interface to initrd promises it'll be there. */ - while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0) - switch (c) { - - case ARG_LOG_LEVEL: - r = log_set_max_level_from_string(optarg); - if (r < 0) - log_error("Failed to parse log level %s, ignoring.", optarg); - - break; - - case ARG_LOG_TARGET: - r = log_set_target_from_string(optarg); - if (r < 0) - log_error("Failed to parse log target %s, ignoring", optarg); - - break; - - case ARG_LOG_COLOR: - - if (optarg) { - r = log_show_color_from_string(optarg); - if (r < 0) - log_error("Failed to parse log color setting %s, ignoring", optarg); - } else - log_show_color(true); - - break; - - case ARG_LOG_LOCATION: - if (optarg) { - r = log_show_location_from_string(optarg); - if (r < 0) - log_error("Failed to parse log location setting %s, ignoring", optarg); - } else - log_show_location(true); - - break; - - case ARG_EXIT_CODE: - r = safe_atou8(optarg, &arg_exit_code); - if (r < 0) - log_error("Failed to parse exit code %s, ignoring", optarg); - - break; - - case '\001': - if (!arg_verb) - arg_verb = optarg; - else - log_error("Excess arguments, ignoring"); - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option code."); - } - - if (!arg_verb) { - log_error("Verb argument missing."); - return -EINVAL; - } - - return 0; -} - -static int switch_root_initramfs(void) { - if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m"); - - if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) - return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m"); - - /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors. - * /run/initramfs/shutdown will take care of these. - * Also do not detach the old root, because /run/initramfs/shutdown needs to access it. - */ - return switch_root("/run/initramfs", "/oldroot", false, MS_BIND); -} - -int main(int argc, char *argv[]) { - bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; - bool in_container, use_watchdog = false; - _cleanup_free_ char *cgroup = NULL; - char *arguments[3]; - unsigned retries; - int cmd, r; - static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; - - log_parse_environment(); - r = parse_argv(argc, argv); - if (r < 0) - goto error; - - /* journald will die if not gone yet. The log target defaults - * to console, but may have been changed by command line options. */ - - log_close_console(); /* force reopen of /dev/console */ - log_open(); - - umask(0022); - - if (getpid() != 1) { - log_error("Not executed by init (PID 1)."); - r = -EPERM; - goto error; - } - - if (streq(arg_verb, "reboot")) - cmd = RB_AUTOBOOT; - else if (streq(arg_verb, "poweroff")) - cmd = RB_POWER_OFF; - else if (streq(arg_verb, "halt")) - cmd = RB_HALT_SYSTEM; - else if (streq(arg_verb, "kexec")) - cmd = LINUX_REBOOT_CMD_KEXEC; - else if (streq(arg_verb, "exit")) - cmd = 0; /* ignored, just checking that arg_verb is valid */ - else { - r = -EINVAL; - log_error("Unknown action '%s'.", arg_verb); - goto error; - } - - (void) cg_get_root_path(&cgroup); - in_container = detect_container() > 0; - - use_watchdog = !!getenv("WATCHDOG_USEC"); - - /* Lock us into memory */ - mlockall(MCL_CURRENT|MCL_FUTURE); - - /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that - * slow IO is processed here already and the final process killing spree is not impacted by processes - * desperately trying to sync IO to disk within their timeout. */ - if (!in_container) - sync(); - - log_info("Sending SIGTERM to remaining processes..."); - broadcast_signal(SIGTERM, true, true); - - log_info("Sending SIGKILL to remaining processes..."); - broadcast_signal(SIGKILL, true, false); - - need_umount = !in_container; - need_swapoff = !in_container; - need_loop_detach = !in_container; - need_dm_detach = !in_container; - - /* Unmount all mountpoints, swaps, and loopback devices */ - for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { - bool changed = false; - - if (use_watchdog) - watchdog_ping(); - - /* Let's trim the cgroup tree on each iteration so - that we leave an empty cgroup tree around, so that - container managers get a nice notify event when we - are down */ - if (cgroup) - cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); - - if (need_umount) { - log_info("Unmounting file systems."); - r = umount_all(&changed); - if (r == 0) { - need_umount = false; - log_info("All filesystems unmounted."); - } else if (r > 0) - log_info("Not all file systems unmounted, %d left.", r); - else - log_error_errno(r, "Failed to unmount file systems: %m"); - } - - if (need_swapoff) { - log_info("Deactivating swaps."); - r = swapoff_all(&changed); - if (r == 0) { - need_swapoff = false; - log_info("All swaps deactivated."); - } else if (r > 0) - log_info("Not all swaps deactivated, %d left.", r); - else - log_error_errno(r, "Failed to deactivate swaps: %m"); - } - - if (need_loop_detach) { - log_info("Detaching loop devices."); - r = loopback_detach_all(&changed); - if (r == 0) { - need_loop_detach = false; - log_info("All loop devices detached."); - } else if (r > 0) - log_info("Not all loop devices detached, %d left.", r); - else - log_error_errno(r, "Failed to detach loop devices: %m"); - } - - if (need_dm_detach) { - log_info("Detaching DM devices."); - r = dm_detach_all(&changed); - if (r == 0) { - need_dm_detach = false; - log_info("All DM devices detached."); - } else if (r > 0) - log_info("Not all DM devices detached, %d left.", r); - else - log_error_errno(r, "Failed to detach DM devices: %m"); - } - - if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { - if (retries > 0) - log_info("All filesystems, swaps, loop devices, DM devices detached."); - /* Yay, done */ - goto initrd_jump; - } - - /* If in this iteration we didn't manage to - * unmount/deactivate anything, we simply give up */ - if (!changed) { - log_info("Cannot finalize remaining%s%s%s%s continuing.", - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - goto initrd_jump; - } - - log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.", - retries + 1, - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - } - - log_error("Too many iterations, giving up."); - - initrd_jump: - - arguments[0] = NULL; - arguments[1] = arg_verb; - arguments[2] = NULL; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - if (!in_container && !in_initrd() && - access("/run/initramfs/shutdown", X_OK) == 0) { - r = switch_root_initramfs(); - if (r >= 0) { - argv[0] = (char*) "/shutdown"; - - setsid(); - make_console_stdio(); - - log_info("Successfully changed into root pivot.\n" - "Returning to initrd..."); - - execv("/shutdown", argv); - log_error_errno(errno, "Failed to execute shutdown binary: %m"); - } else - log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); - - } - - if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) - log_error("Failed to finalize %s%s%s%s ignoring", - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - - /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be - * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we - * sync'ed things already once above, but we did some more work since then which might have caused IO, hence - * let's doit once more. */ - if (!in_container) - sync(); - - if (streq(arg_verb, "exit")) { - if (in_container) - exit(arg_exit_code); - else { - /* We cannot exit() on the host, fallback on another - * method. */ - cmd = RB_POWER_OFF; - } - } - - switch (cmd) { - - case LINUX_REBOOT_CMD_KEXEC: - - if (!in_container) { - /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid; - - log_info("Rebooting with kexec."); - - pid = fork(); - if (pid < 0) - log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - const char * const args[] = { - KEXEC, "-e", NULL - }; - - /* Child */ - - execv(args[0], (char * const *) args); - _exit(EXIT_FAILURE); - } else - wait_for_terminate_and_warn("kexec", pid, true); - } - - cmd = RB_AUTOBOOT; - /* Fall through */ - - case RB_AUTOBOOT: - - if (!in_container) { - _cleanup_free_ char *param = NULL; - - r = read_one_line_file("/run/systemd/reboot-param", ¶m); - if (r < 0) - log_warning_errno(r, "Failed to read reboot parameter file: %m"); - - if (!isempty(param)) { - log_info("Rebooting with argument '%s'.", param); - syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); - log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); - } - } - - log_info("Rebooting."); - break; - - case RB_POWER_OFF: - log_info("Powering off."); - break; - - case RB_HALT_SYSTEM: - log_info("Halting system."); - break; - - default: - assert_not_reached("Unknown magic"); - } - - reboot(cmd); - if (errno == EPERM && in_container) { - /* If we are in a container, and we lacked - * CAP_SYS_BOOT just exit, this will kill our - * container for good. */ - log_info("Exiting container."); - exit(0); - } - - r = log_error_errno(errno, "Failed to invoke reboot(): %m"); - - 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 deleted file mode 100644 index c7700b8857..0000000000 --- a/src/core/slice.c +++ /dev/null @@ -1,346 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "dbus-slice.h" -#include "log.h" -#include "slice.h" -#include "special.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" -#include "unit.h" - -static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { - [SLICE_DEAD] = UNIT_INACTIVE, - [SLICE_ACTIVE] = UNIT_ACTIVE -}; - -static void slice_init(Unit *u) { - assert(u); - assert(u->load_state == UNIT_STUB); - - u->ignore_on_isolate = true; -} - -static void slice_set_state(Slice *t, SliceState state) { - SliceState old_state; - assert(t); - - old_state = t->state; - t->state = state; - - if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(t)->id, - slice_state_to_string(old_state), - slice_state_to_string(state)); - - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); -} - -static int slice_add_parent_slice(Slice *s) { - char *a, *dash; - Unit *parent; - int r; - - assert(s); - - if (UNIT_ISSET(UNIT(s)->slice)) - return 0; - - if (unit_has_name(UNIT(s), SPECIAL_ROOT_SLICE)) - return 0; - - a = strdupa(UNIT(s)->id); - dash = strrchr(a, '-'); - if (dash) - strcpy(dash, ".slice"); - else - a = (char*) SPECIAL_ROOT_SLICE; - - r = manager_load_unit(UNIT(s)->manager, a, NULL, NULL, &parent); - if (r < 0) - return r; - - unit_ref_set(&UNIT(s)->slice, parent); - return 0; -} - -static int slice_add_default_dependencies(Slice *s) { - int r; - - 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), - UNIT_BEFORE, UNIT_CONFLICTS, - SPECIAL_SHUTDOWN_TARGET, NULL, true); - if (r < 0) - return r; - - return 0; -} - -static int slice_verify(Slice *s) { - _cleanup_free_ char *parent = NULL; - int r; - - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (!slice_name_is_valid(UNIT(s)->id)) { - log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id); - return -EINVAL; - } - - r = slice_build_parent_slice(UNIT(s)->id, &parent); - if (r < 0) - return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m"); - - if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) { - log_unit_error(UNIT(s), "Located outside of parent slice. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int slice_load(Unit *u) { - Slice *s = SLICE(u); - int r; - - assert(s); - assert(u->load_state == UNIT_STUB); - - r = unit_load_fragment_and_dropin_optional(u); - if (r < 0) - return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED) { - - r = unit_patch_contexts(u); - if (r < 0) - return r; - - r = slice_add_parent_slice(s); - if (r < 0) - return r; - - r = slice_add_default_dependencies(s); - if (r < 0) - return r; - } - - return slice_verify(s); -} - -static int slice_coldplug(Unit *u) { - Slice *t = SLICE(u); - - assert(t); - assert(t->state == SLICE_DEAD); - - if (t->deserialized_state != t->state) - slice_set_state(t, t->deserialized_state); - - return 0; -} - -static void slice_dump(Unit *u, FILE *f, const char *prefix) { - Slice *t = SLICE(u); - - assert(t); - assert(f); - - fprintf(f, - "%sSlice State: %s\n", - prefix, slice_state_to_string(t->state)); - - cgroup_context_dump(&t->cgroup_context, f, prefix); -} - -static int slice_start(Unit *u) { - Slice *t = SLICE(u); - - assert(t); - assert(t->state == SLICE_DEAD); - - (void) unit_realize_cgroup(u); - (void) unit_reset_cpu_usage(u); - - slice_set_state(t, SLICE_ACTIVE); - return 1; -} - -static int slice_stop(Unit *u) { - Slice *t = SLICE(u); - - assert(t); - assert(t->state == SLICE_ACTIVE); - - /* We do not need to destroy the cgroup explicitly, - * unit_notify() will do that for us anyway. */ - - slice_set_state(t, SLICE_DEAD); - return 1; -} - -static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - return unit_kill_common(u, who, signo, -1, -1, error); -} - -static int slice_serialize(Unit *u, FILE *f, FDSet *fds) { - Slice *s = SLICE(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", slice_state_to_string(s->state)); - return 0; -} - -static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Slice *s = SLICE(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - SliceState state; - - state = slice_state_from_string(value); - if (state < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -_pure_ static UnitActiveState slice_active_state(Unit *u) { - assert(u); - - return state_translation_table[SLICE(u)->state]; -} - -_pure_ static const char *slice_sub_state_to_string(Unit *u) { - assert(u); - - return slice_state_to_string(SLICE(u)->state); -} - -static void slice_enumerate(Manager *m) { - Unit *u; - int r; - - assert(m); - - u = manager_get_unit(m, SPECIAL_ROOT_SLICE); - if (!u) { - u = unit_new(m, sizeof(Slice)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_ROOT_SLICE); - if (r < 0) { - unit_free(u); - 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) - u->description = strdup("Root Slice"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd.special(7)"); - - unit_add_to_load_queue(u); - unit_add_to_dbus_queue(u); -} - -const UnitVTable slice_vtable = { - .object_size = sizeof(Slice), - .cgroup_context_offset = offsetof(Slice, cgroup_context), - - .sections = - "Unit\0" - "Slice\0" - "Install\0", - .private_section = "Slice", - - .can_transient = true, - - .init = slice_init, - .load = slice_load, - - .coldplug = slice_coldplug, - - .dump = slice_dump, - - .start = slice_start, - .stop = slice_stop, - - .kill = slice_kill, - - .serialize = slice_serialize, - .deserialize_item = slice_deserialize_item, - - .active_state = slice_active_state, - .sub_state_to_string = slice_sub_state_to_string, - - .bus_vtable = bus_slice_vtable, - .bus_set_property = bus_slice_set_property, - .bus_commit_properties = bus_slice_commit_properties, - - .enumerate = slice_enumerate, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Created slice %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Removed slice %s.", - }, - }, -}; diff --git a/src/core/slice.h b/src/core/slice.h deleted file mode 100644 index c9f3f61067..0000000000 --- a/src/core/slice.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Slice Slice; - -struct Slice { - Unit meta; - - SliceState state, deserialized_state; - - CGroupContext cgroup_context; -}; - -extern const UnitVTable slice_vtable; diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c deleted file mode 100644 index 5a6d11cfa1..0000000000 --- a/src/core/smack-setup.c +++ /dev/null @@ -1,346 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation - Authors: - Nathaniel Chen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "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" - -#ifdef HAVE_SMACK - -static int write_access2_rules(const char* srcdir) { - _cleanup_close_ int load2_fd = -1, change_fd = -1; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *entry; - char buf[NAME_MAX]; - int dfd = -1; - int r = 0; - - load2_fd = open("/sys/fs/smackfs/load2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (load2_fd < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/load2': %m"); - return -errno; /* negative error */ - } - - change_fd = open("/sys/fs/smackfs/change-rule", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (change_fd < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/change-rule': %m"); - return -errno; /* negative error */ - } - - /* write rules to load2 or change-rule 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; - - if (!dirent_is_file(entry)) - continue; - - 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)) { - - _cleanup_free_ char *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL; - - if (isempty(truncate_nl(buf))) - continue; - - /* if 3 args -> load rule : subject object access1 */ - /* if 4 args -> change rule : subject object access1 access2 */ - if (sscanf(buf, "%ms %ms %ms %ms", &sbj, &obj, &acc1, &acc2) < 3) { - log_error_errno(errno, "Failed to parse rule '%s' in '%s', ignoring.", buf, entry->d_name); - continue; - } - - if (write(isempty(acc2) ? load2_fd : change_fd, buf, strlen(buf)) < 0) { - if (r == 0) - r = -errno; - log_error_errno(errno, "Failed to write '%s' to '%s' in '%s'", - buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name); - } - } - } - - return r; -} - -static int write_cipso2_rules(const char* srcdir) { - _cleanup_close_ int cipso2_fd = -1; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *entry; - char buf[NAME_MAX]; - int dfd = -1; - int r = 0; - - cipso2_fd = open("/sys/fs/smackfs/cipso2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (cipso2_fd < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/cipso2': %m"); - return -errno; /* negative error */ - } - - /* write rules to cipso2 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; - - if (!dirent_is_file(entry)) - continue; - - fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - if (r == 0) - r = -errno; - log_error_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; - } - - /* cipso2 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 (isempty(truncate_nl(buf))) - continue; - - if (write(cipso2_fd, buf, strlen(buf)) < 0) { - if (r == 0) - r = -errno; - log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'", - buf, entry->d_name); - break; - } - } - } - - 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) { - -#ifdef HAVE_SMACK - - int r; - - assert(loaded_policy); - - r = write_access2_rules("/etc/smack/accesses.d/"); - switch(r) { - case -ENOENT: - log_debug("Smack is not enabled in the kernel."); - return 0; - case ENOENT: - log_debug("Smack access rules directory '/etc/smack/accesses.d/' not found"); - return 0; - case 0: - log_info("Successfully loaded Smack policies."); - break; - default: - 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 < 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/"); - switch(r) { - case -ENOENT: - log_debug("Smack/CIPSO is not enabled in the kernel."); - return 0; - case ENOENT: - log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found"); - break; - case 0: - log_info("Successfully loaded Smack/CIPSO policies."); - break; - default: - 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; - -#endif - - return 0; -} diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h deleted file mode 100644 index 78164c85e6..0000000000 --- a/src/core/smack-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation - Authors: - Nathaniel Chen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int mac_smack_setup(bool *loaded_policy); diff --git a/src/core/socket.c b/src/core/socket.c deleted file mode 100644 index e098055885..0000000000 --- a/src/core/socket.c +++ /dev/null @@ -1,2992 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "io-util.h" -#include "label.h" -#include "log.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "smack-util.h" -#include "socket.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, - [SOCKET_START_PRE] = UNIT_ACTIVATING, - [SOCKET_START_CHOWN] = UNIT_ACTIVATING, - [SOCKET_START_POST] = UNIT_ACTIVATING, - [SOCKET_LISTENING] = UNIT_ACTIVE, - [SOCKET_RUNNING] = UNIT_ACTIVE, - [SOCKET_STOP_PRE] = UNIT_DEACTIVATING, - [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING, - [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING, - [SOCKET_STOP_POST] = UNIT_DEACTIVATING, - [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING, - [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING, - [SOCKET_FAILED] = UNIT_FAILED -}; - -static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); - -static void socket_init(Unit *u) { - Socket *s = SOCKET(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - s->backlog = SOMAXCONN; - s->timeout_usec = u->manager->default_timeout_start_usec; - s->directory_mode = 0755; - s->socket_mode = 0666; - - s->max_connections = 64; - - s->priority = -1; - s->ip_tos = -1; - s->ip_ttl = -1; - s->mark = -1; - - s->exec_context.std_output = u->manager->default_std_output; - s->exec_context.std_error = u->manager->default_std_error; - - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - - s->trigger_limit.interval = USEC_INFINITY; - s->trigger_limit.burst = (unsigned) -1; -} - -static void socket_unwatch_control_pid(Socket *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void socket_cleanup_fd_list(SocketPort *p) { - assert(p); - - close_many(p->auxiliary_fds, p->n_auxiliary_fds); - p->auxiliary_fds = mfree(p->auxiliary_fds); - p->n_auxiliary_fds = 0; -} - -void socket_free_ports(Socket *s) { - SocketPort *p; - - assert(s); - - while ((p = s->ports)) { - LIST_REMOVE(port, s->ports, p); - - sd_event_source_unref(p->event_source); - - socket_cleanup_fd_list(p); - safe_close(p->fd); - free(p->path); - free(p); - } -} - -static void socket_done(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - socket_free_ports(s); - - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); - s->control_command = NULL; - - socket_unwatch_control_pid(s); - - unit_ref_unset(&s->service); - - s->tcp_congestion = mfree(s->tcp_congestion); - s->bind_to_device = mfree(s->bind_to_device); - - 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); - - 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); -} - -static int socket_arm_timer(Socket *s, usec_t usec) { - int r; - - assert(s); - - if (s->timer_event_source) { - r = sd_event_source_set_time(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - socket_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "socket-timer"); - - return 0; -} - -int socket_instantiate_service(Socket *s) { - _cleanup_free_ char *prefix = NULL, *name = NULL; - int r; - Unit *u; - - assert(s); - - /* This fills in s->service if it isn't filled in yet. For - * Accept=yes sockets we create the next connection service - * here. For Accept=no this is mostly a NOP since the service - * is figured out at load time anyway. */ - - if (UNIT_DEREF(s->service)) - return 0; - - if (!s->accept) - return 0; - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); - if (r < 0) - return r; - - if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) - return -ENOMEM; - - r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); - if (r < 0) - return r; - - unit_ref_set(&s->service, u); - - return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); -} - -static bool have_non_accept_socket(Socket *s) { - SocketPort *p; - - assert(s); - - if (!s->accept) - return true; - - LIST_FOREACH(port, p, s->ports) { - - if (p->type != SOCKET_SOCKET) - return true; - - if (!socket_address_can_accept(&p->address)) - return true; - } - - return false; -} - -static int socket_add_mount_links(Socket *s) { - SocketPort *p; - int r; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - const char *path = NULL; - - if (p->type == SOCKET_SOCKET) - path = socket_address_get_path(&p->address); - else if (IN_SET(p->type, SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_USB_FUNCTION)) - path = p->path; - - if (!path) - continue; - - r = unit_require_mounts_for(UNIT(s), path); - if (r < 0) - return r; - } - - return 0; -} - -static int socket_add_device_link(Socket *s) { - char *t; - - assert(s); - - if (!s->bind_to_device || streq(s->bind_to_device, "lo")) - return 0; - - t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device); - 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; - - if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); - if (r < 0) - return r; - } - - return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -_pure_ static bool socket_has_exec(Socket *s) { - unsigned i; - assert(s); - - for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++) - if (s->exec_command[i]) - return true; - - return false; -} - -static int socket_add_extras(Socket *s) { - Unit *u = UNIT(s); - int r; - - assert(s); - - /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit - * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept() - * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly - * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic - * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to - * process whatever is queued in one go, and thus should normally never have to be started frequently. This is - * different for Accept=yes where each connection is processed by a new service instance, and thus frequent - * service starts are typical. */ - - if (s->trigger_limit.interval == USEC_INFINITY) - s->trigger_limit.interval = 2 * USEC_PER_SEC; - - if (s->trigger_limit.burst == (unsigned) -1) { - if (s->accept) - s->trigger_limit.burst = 200; - else - s->trigger_limit.burst = 20; - } - - if (have_non_accept_socket(s)) { - - if (!UNIT_DEREF(s->service)) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - unit_ref_set(&s->service, x); - } - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true); - if (r < 0) - return r; - } - - r = socket_add_mount_links(s); - if (r < 0) - return r; - - r = socket_add_device_link(s); - if (r < 0) - return r; - - r = unit_patch_contexts(u); - if (r < 0) - return r; - - if (socket_has_exec(s)) { - r = unit_add_exec_dependencies(u, &s->exec_context); - if (r < 0) - return r; - - r = unit_set_default_slice(u); - if (r < 0) - return r; - } - - r = socket_add_default_dependencies(s); - if (r < 0) - return r; - - return 0; -} - -static const char *socket_find_symlink_target(Socket *s) { - const char *found = NULL; - SocketPort *p; - - LIST_FOREACH(port, p, s->ports) { - const char *f = NULL; - - switch (p->type) { - - case SOCKET_FIFO: - f = p->path; - break; - - case SOCKET_SOCKET: - if (p->address.sockaddr.un.sun_path[0] != 0) - f = p->address.sockaddr.un.sun_path; - break; - - default: - break; - } - - if (f) { - if (found) - return NULL; - - found = f; - } - } - - return found; -} - -static int socket_verify(Socket *s) { - assert(s); - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - if (!s->ports) { - log_unit_error(UNIT(s), "Unit lacks Listen setting. Refusing."); - return -EINVAL; - } - - if (s->accept && have_non_accept_socket(s)) { - log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing."); - return -EINVAL; - } - - if (s->accept && s->max_connections <= 0) { - log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing."); - return -EINVAL; - } - - if (s->accept && UNIT_DEREF(s->service)) { - log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing."); - return -EINVAL; - } - - if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing."); - return -EINVAL; - } - - if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) { - log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int socket_load(Unit *u) { - Socket *s = SOCKET(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - r = unit_load_fragment_and_dropin(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - /* This is a new unit? Then let's add in some extras */ - r = socket_add_extras(s); - if (r < 0) - return r; - } - - return socket_verify(s); -} - -_const_ static const char* listen_lookup(int family, int type) { - - if (family == AF_NETLINK) - return "ListenNetlink"; - - if (type == SOCK_STREAM) - return "ListenStream"; - else if (type == SOCK_DGRAM) - return "ListenDatagram"; - else if (type == SOCK_SEQPACKET) - return "ListenSequentialPacket"; - - assert_not_reached("Unknown socket type"); - return NULL; -} - -static void socket_dump(Unit *u, FILE *f, const char *prefix) { - char time_string[FORMAT_TIMESPAN_MAX]; - SocketExecCommand c; - Socket *s = SOCKET(u); - SocketPort *p; - const char *prefix2; - - assert(s); - assert(f); - - prefix = strempty(prefix); - prefix2 = strjoina(prefix, "\t"); - - fprintf(f, - "%sSocket State: %s\n" - "%sResult: %s\n" - "%sBindIPv6Only: %s\n" - "%sBacklog: %u\n" - "%sSocketMode: %04o\n" - "%sDirectoryMode: %04o\n" - "%sKeepAlive: %s\n" - "%sNoDelay: %s\n" - "%sFreeBind: %s\n" - "%sTransparent: %s\n" - "%sBroadcast: %s\n" - "%sPassCredentials: %s\n" - "%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), - prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), - prefix, s->backlog, - prefix, s->socket_mode, - prefix, s->directory_mode, - prefix, yes_no(s->keep_alive), - prefix, yes_no(s->no_delay), - prefix, yes_no(s->free_bind), - prefix, yes_no(s->transparent), - prefix, yes_no(s->broadcast), - prefix, yes_no(s->pass_cred), - 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) - fprintf(f, - "%sControl PID: "PID_FMT"\n", - prefix, s->control_pid); - - if (s->bind_to_device) - fprintf(f, - "%sBindToDevice: %s\n", - prefix, s->bind_to_device); - - if (s->accept) - fprintf(f, - "%sAccepted: %u\n" - "%sNConnections: %u\n" - "%sMaxConnections: %u\n", - prefix, s->n_accepted, - prefix, s->n_connections, - prefix, s->max_connections); - - if (s->priority >= 0) - fprintf(f, - "%sPriority: %i\n", - prefix, s->priority); - - if (s->receive_buffer > 0) - fprintf(f, - "%sReceiveBuffer: %zu\n", - prefix, s->receive_buffer); - - if (s->send_buffer > 0) - fprintf(f, - "%sSendBuffer: %zu\n", - prefix, s->send_buffer); - - if (s->ip_tos >= 0) - fprintf(f, - "%sIPTOS: %i\n", - prefix, s->ip_tos); - - if (s->ip_ttl >= 0) - fprintf(f, - "%sIPTTL: %i\n", - prefix, s->ip_ttl); - - if (s->pipe_size > 0) - fprintf(f, - "%sPipeSize: %zu\n", - prefix, s->pipe_size); - - if (s->mark >= 0) - fprintf(f, - "%sMark: %i\n", - prefix, s->mark); - - if (s->mq_maxmsg > 0) - fprintf(f, - "%sMessageQueueMaxMessages: %li\n", - prefix, s->mq_maxmsg); - - if (s->mq_msgsize > 0) - fprintf(f, - "%sMessageQueueMessageSize: %li\n", - prefix, s->mq_msgsize); - - if (s->reuse_port) - fprintf(f, - "%sReusePort: %s\n", - prefix, yes_no(s->reuse_port)); - - if (s->smack) - fprintf(f, - "%sSmackLabel: %s\n", - prefix, s->smack); - - if (s->smack_ip_in) - fprintf(f, - "%sSmackLabelIPIn: %s\n", - prefix, s->smack_ip_in); - - if (s->smack_ip_out) - fprintf(f, - "%sSmackLabelIPOut: %s\n", - prefix, s->smack_ip_out); - - if (!isempty(s->user) || !isempty(s->group)) - fprintf(f, - "%sSocketUser: %s\n" - "%sSocketGroup: %s\n", - prefix, strna(s->user), - prefix, strna(s->group)); - - if (s->keep_alive_time > 0) - fprintf(f, - "%sKeepAliveTimeSec: %s\n", - prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_time, USEC_PER_SEC)); - - if (s->keep_alive_interval) - fprintf(f, - "%sKeepAliveIntervalSec: %s\n", - prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_interval, USEC_PER_SEC)); - - if (s->keep_alive_cnt) - fprintf(f, - "%sKeepAliveProbes: %u\n", - prefix, s->keep_alive_cnt); - - if (s->defer_accept) - fprintf(f, - "%sDeferAcceptSec: %s\n", - prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->defer_accept, USEC_PER_SEC)); - - LIST_FOREACH(port, p, s->ports) { - - if (p->type == SOCKET_SOCKET) { - const char *t; - int r; - char *k = NULL; - - r = socket_address_print(&p->address, &k); - if (r < 0) - t = strerror(-r); - else - t = k; - - fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t); - free(k); - } else if (p->type == SOCKET_SPECIAL) - fprintf(f, "%sListenSpecial: %s\n", prefix, p->path); - else if (p->type == SOCKET_USB_FUNCTION) - fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path); - else if (p->type == SOCKET_MQUEUE) - fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path); - else - fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); - } - - fprintf(f, - "%sTriggerLimitIntervalSec: %s\n" - "%sTriggerLimitBurst: %u\n", - prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), - prefix, s->trigger_limit.burst); - - exec_context_dump(&s->exec_context, f, prefix); - kill_context_dump(&s->kill_context, f, prefix); - - for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) { - if (!s->exec_command[c]) - continue; - - fprintf(f, "%s-> %s:\n", - prefix, socket_exec_command_to_string(c)); - - exec_command_dump_list(s->exec_command[c], f, prefix2); - } -} - -static int instance_from_socket(int fd, unsigned nr, char **instance) { - socklen_t l; - char *r; - union sockaddr_union local, remote; - - assert(fd >= 0); - assert(instance); - - l = sizeof(local); - if (getsockname(fd, &local.sa, &l) < 0) - return -errno; - - l = sizeof(remote); - if (getpeername(fd, &remote.sa, &l) < 0) - return -errno; - - switch (local.sa.sa_family) { - - case AF_INET: { - uint32_t - a = be32toh(local.in.sin_addr.s_addr), - b = be32toh(remote.in.sin_addr.s_addr); - - if (asprintf(&r, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", - nr, - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(local.in.sin_port), - b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, - be16toh(remote.in.sin_port)) < 0) - return -ENOMEM; - - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 && - memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t - *a = local.in6.sin6_addr.s6_addr+12, - *b = remote.in6.sin6_addr.s6_addr+12; - - if (asprintf(&r, - "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", - nr, - a[0], a[1], a[2], a[3], - be16toh(local.in6.sin6_port), - b[0], b[1], b[2], b[3], - be16toh(remote.in6.sin6_port)) < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN]; - - if (asprintf(&r, - "%u-%s:%u-%s:%u", - nr, - inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)), - be16toh(local.in6.sin6_port), - inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)), - be16toh(remote.in6.sin6_port)) < 0) - return -ENOMEM; - } - - break; - } - - case AF_UNIX: { - struct ucred ucred; - int k; - - k = getpeercred(fd, &ucred); - if (k >= 0) { - if (asprintf(&r, - "%u-"PID_FMT"-"UID_FMT, - nr, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - } else if (k == -ENODATA) { - /* This handles the case where somebody is - * connecting from another pid/uid namespace - * (e.g. from outside of our container). */ - if (asprintf(&r, - "%u-unknown", - nr) < 0) - return -ENOMEM; - } else - return k; - - break; - } - - default: - assert_not_reached("Unhandled socket type."); - } - - *instance = r; - return 0; -} - -static void socket_close_fds(Socket *s) { - SocketPort *p; - char **i; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - bool was_open; - - was_open = p->fd >= 0; - - p->event_source = sd_event_source_unref(p->event_source); - p->fd = safe_close(p->fd); - socket_cleanup_fd_list(p); - - /* One little note: we should normally not delete any sockets in the file system here! After all some - * other process we spawned might still have a reference of this fd and wants to continue to use - * it. Therefore we normally delete sockets in the file system before we create a new one, not after we - * stopped using one! That all said, if the user explicitly requested this, we'll delete them here - * anyway, but only then. */ - - if (!was_open || !s->remove_on_stop) - continue; - - switch (p->type) { - - case SOCKET_FIFO: - (void) unlink(p->path); - break; - - case SOCKET_MQUEUE: - (void) mq_unlink(p->path); - break; - - case SOCKET_SOCKET: - (void) socket_address_unlink(&p->address); - break; - - default: - break; - } - } - - if (s->remove_on_stop) - STRV_FOREACH(i, s->symlinks) - (void) unlink(*i); -} - -static void socket_apply_socket_options(Socket *s, int fd) { - int r; - - assert(s); - assert(fd >= 0); - - if (s->keep_alive) { - int b = s->keep_alive; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m"); - } - - if (s->keep_alive_time) { - int value = s->keep_alive_time / USEC_PER_SEC; - if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m"); - } - - if (s->keep_alive_interval) { - int value = s->keep_alive_interval / USEC_PER_SEC; - if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m"); - } - - if (s->keep_alive_cnt) { - int value = s->keep_alive_cnt; - if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m"); - } - - if (s->defer_accept) { - int value = s->defer_accept / USEC_PER_SEC; - if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m"); - } - - if (s->no_delay) { - int b = s->no_delay; - - 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) { - int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m"); - } - - if (s->pass_cred) { - int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m"); - } - - if (s->pass_sec) { - int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m"); - } - - if (s->priority >= 0) - if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m"); - - if (s->receive_buffer > 0) { - int value = (int) s->receive_buffer; - - /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ - - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m"); - } - - if (s->send_buffer > 0) { - int value = (int) s->send_buffer; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_SNDBUF failed: %m"); - } - - if (s->mark >= 0) - if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0) - log_unit_warning_errno(UNIT(s), errno, "SO_MARK failed: %m"); - - if (s->ip_tos >= 0) - if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0) - log_unit_warning_errno(UNIT(s), errno, "IP_TOS failed: %m"); - - if (s->ip_ttl >= 0) { - int x; - - r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl)); - - if (socket_ipv6_is_supported()) - x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl)); - else { - x = -1; - errno = EAFNOSUPPORT; - } - - if (r < 0 && x < 0) - log_unit_warning_errno(UNIT(s), errno, "IP_TTL/IPV6_UNICAST_HOPS failed: %m"); - } - - if (s->tcp_congestion) - if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0) - log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m"); - - if (s->smack_ip_in) { - r = mac_smack_apply_fd(fd, SMACK_ATTR_IPIN, s->smack_ip_in); - if (r < 0) - log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m"); - } - - if (s->smack_ip_out) { - r = mac_smack_apply_fd(fd, SMACK_ATTR_IPOUT, s->smack_ip_out); - if (r < 0) - log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m"); - } -} - -static void socket_apply_fifo_options(Socket *s, int fd) { - int r; - - assert(s); - assert(fd >= 0); - - if (s->pipe_size > 0) - if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) - 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, "SMACK relabelling failed, ignoring: %m"); - } -} - -static int fifo_address_create( - const char *path, - mode_t directory_mode, - mode_t socket_mode) { - - _cleanup_close_ int fd = -1; - mode_t old_mask; - struct stat st; - int r; - - assert(path); - - mkdir_parents_label(path, directory_mode); - - r = mac_selinux_create_file_prepare(path, S_IFIFO); - if (r < 0) - return r; - - /* Enforce the right access mode for the fifo */ - old_mask = umask(~ socket_mode); - - /* Include the original umask in our mask */ - (void) umask(~socket_mode | old_mask); - - r = mkfifo(path, socket_mode); - (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); - if (fd < 0) { - r = -errno; - goto fail; - } - - mac_selinux_create_file_clear(); - - if (fstat(fd, &st) < 0) { - r = -errno; - goto fail; - } - - if (!S_ISFIFO(st.st_mode) || - (st.st_mode & 0777) != (socket_mode & ~old_mask) || - st.st_uid != getuid() || - st.st_gid != getgid()) { - r = -EEXIST; - goto fail; - } - - r = fd; - fd = -1; - - return r; - -fail: - mac_selinux_create_file_clear(); - return r; -} - -static int special_address_create(const char *path, bool writable) { - _cleanup_close_ int fd = -1; - struct stat st; - int r; - - assert(path); - - 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) - 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)) - return -EEXIST; - - r = fd; - fd = -1; - - return r; -} - -static int usbffs_address_create(const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; - int r; - - assert(path); - - fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); - if (fd < 0) - return -errno; - - if (fstat(fd, &st) < 0) - return -errno; - - /* Check whether this is a regular file (ffs endpoint)*/ - if (!S_ISREG(st.st_mode)) - return -EEXIST; - - r = fd; - fd = -1; - - return r; -} - -static int mq_address_create( - const char *path, - mode_t mq_mode, - long maxmsg, - long msgsize) { - - _cleanup_close_ int fd = -1; - struct stat st; - mode_t old_mask; - struct mq_attr _attr, *attr = NULL; - int r; - - assert(path); - - if (maxmsg > 0 && msgsize > 0) { - _attr = (struct mq_attr) { - .mq_flags = O_NONBLOCK, - .mq_maxmsg = maxmsg, - .mq_msgsize = msgsize, - }; - attr = &_attr; - } - - /* Enforce the right access mode for the mq */ - old_mask = umask(~ mq_mode); - - /* Include the original umask in our mask */ - (void) umask(~mq_mode | old_mask); - fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); - (void) umask(old_mask); - - if (fd < 0) - return -errno; - - if (fstat(fd, &st) < 0) - return -errno; - - if ((st.st_mode & 0777) != (mq_mode & ~old_mask) || - st.st_uid != getuid() || - st.st_gid != getgid()) - return -EEXIST; - - r = fd; - fd = -1; - - return r; -} - -static int socket_symlink(Socket *s) { - const char *p; - char **i; - - assert(s); - - p = socket_find_symlink_target(s); - if (!p) - return 0; - - STRV_FOREACH(i, s->symlinks) - symlink_label(p, *i); - - return 0; -} - -static int usbffs_write_descs(int fd, Service *s) { - int r; - - if (!s->usb_function_descriptors || !s->usb_function_strings) - return -EINVAL; - - r = copy_file_fd(s->usb_function_descriptors, fd, false); - if (r < 0) - return r; - - return copy_file_fd(s->usb_function_strings, fd, false); -} - -static int usbffs_select_ep(const struct dirent *d) { - return d->d_name[0] != '.' && !streq(d->d_name, "ep0"); -} - -static int usbffs_dispatch_eps(SocketPort *p) { - _cleanup_free_ struct dirent **ent = NULL; - _cleanup_free_ char *path = NULL; - int r, i, n, k; - - path = dirname_malloc(p->path); - if (!path) - return -ENOMEM; - - r = scandir(path, &ent, usbffs_select_ep, alphasort); - if (r < 0) - return -errno; - - n = r; - p->auxiliary_fds = new(int, n); - if (!p->auxiliary_fds) - return -ENOMEM; - - p->n_auxiliary_fds = n; - - k = 0; - for (i = 0; i < n; ++i) { - _cleanup_free_ char *ep = NULL; - - ep = path_make_absolute(ent[i]->d_name, path); - if (!ep) - return -ENOMEM; - - path_kill_slashes(ep); - - r = usbffs_address_create(ep); - if (r < 0) - goto fail; - - p->auxiliary_fds[k] = r; - - ++k; - free(ent[i]); - } - - return r; - -fail: - close_many(p->auxiliary_fds, k); - p->auxiliary_fds = mfree(p->auxiliary_fds); - p->n_auxiliary_fds = 0; - - return r; -} - -static int socket_determine_selinux_label(Socket *s, char **ret) { - ExecCommand *c; - int r; - - assert(s); - assert(ret); - - if (s->selinux_context_from_net) { - /* If this is requested, get label from the network label */ - - r = mac_selinux_get_our_label(ret); - if (r == -EOPNOTSUPP) - goto no_label; - - } else { - /* Otherwise, get it from the executable we are about to start */ - r = socket_instantiate_service(s); - if (r < 0) - return r; - - if (!UNIT_ISSET(s->service)) - goto no_label; - - c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]; - if (!c) - goto no_label; - - r = mac_selinux_get_create_label_from_exe(c->path, ret); - if (r == -EPERM || r == -EOPNOTSUPP) - goto no_label; - } - - return r; - -no_label: - *ret = NULL; - return 0; -} - -static int socket_open_fds(Socket *s) { - _cleanup_(mac_selinux_freep) char *label = NULL; - bool know_label = false; - SocketPort *p; - int r; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - - if (p->fd >= 0) - continue; - - switch (p->type) { - - case SOCKET_SOCKET: - - if (!know_label) { - /* Figure out label, if we don't it know yet. We do it once, for the first socket where - * we need this and remember it for the rest. */ - - r = socket_determine_selinux_label(s, &label); - if (r < 0) - goto rollback; - - know_label = true; - } - - /* Apply the socket protocol */ - switch (p->address.type) { - - case SOCK_STREAM: - case SOCK_SEQPACKET: - if (s->socket_protocol == IPPROTO_SCTP) - p->address.protocol = s->socket_protocol; - break; - - case SOCK_DGRAM: - if (s->socket_protocol == IPPROTO_UDPLITE) - p->address.protocol = s->socket_protocol; - break; - } - - r = socket_address_listen( - &p->address, - SOCK_CLOEXEC|SOCK_NONBLOCK, - s->backlog, - s->bind_ipv6_only, - s->bind_to_device, - s->reuse_port, - s->free_bind, - s->transparent, - s->directory_mode, - s->socket_mode, - label); - if (r < 0) - goto rollback; - - p->fd = r; - socket_apply_socket_options(s, p->fd); - socket_symlink(s); - break; - - case SOCKET_SPECIAL: - - p->fd = special_address_create(p->path, s->writable); - if (p->fd < 0) { - r = p->fd; - goto rollback; - } - break; - - case SOCKET_FIFO: - - p->fd = fifo_address_create( - p->path, - s->directory_mode, - s->socket_mode); - if (p->fd < 0) { - r = p->fd; - goto rollback; - } - - socket_apply_fifo_options(s, p->fd); - socket_symlink(s); - break; - - case SOCKET_MQUEUE: - - p->fd = mq_address_create( - p->path, - s->socket_mode, - s->mq_maxmsg, - s->mq_msgsize); - if (p->fd < 0) { - r = p->fd; - goto rollback; - } - break; - - case SOCKET_USB_FUNCTION: { - _cleanup_free_ char *ep = NULL; - - ep = path_make_absolute("ep0", p->path); - - p->fd = usbffs_address_create(ep); - if (p->fd < 0) { - r = p->fd; - goto rollback; - } - - r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service))); - if (r < 0) - goto rollback; - - r = usbffs_dispatch_eps(p); - if (r < 0) - goto rollback; - - break; - } - default: - assert_not_reached("Unknown port type"); - } - } - - return 0; - -rollback: - socket_close_fds(s); - return r; -} - -static void socket_unwatch_fds(Socket *s) { - SocketPort *p; - int r; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - if (!p->event_source) - continue; - - r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); - if (r < 0) - log_unit_debug_errno(UNIT(s), r, "Failed to disable event source: %m"); - } -} - -static int socket_watch_fds(Socket *s) { - SocketPort *p; - int r; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - if (p->event_source) { - r = sd_event_source_set_enabled(p->event_source, SD_EVENT_ON); - if (r < 0) - goto fail; - } else { - r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(p->event_source, "socket-port-io"); - } - } - - return 0; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to watch listening fds: %m"); - socket_unwatch_fds(s); - return r; -} - -enum { - SOCKET_OPEN_NONE, - SOCKET_OPEN_SOME, - SOCKET_OPEN_ALL, -}; - -static int socket_check_open(Socket *s) { - bool have_open = false, have_closed = false; - SocketPort *p; - - assert(s); - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - have_closed = true; - else - have_open = true; - - if (have_open && have_closed) - return SOCKET_OPEN_SOME; - } - - if (have_open) - return SOCKET_OPEN_ALL; - - return SOCKET_OPEN_NONE; -} - -static void socket_set_state(Socket *s, SocketState state) { - SocketState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (!IN_SET(state, - SOCKET_START_PRE, - SOCKET_START_CHOWN, - SOCKET_START_POST, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL)) { - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - socket_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - } - - if (state != SOCKET_LISTENING) - socket_unwatch_fds(s); - - if (!IN_SET(state, - SOCKET_START_CHOWN, - SOCKET_START_POST, - SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL)) - socket_close_fds(s); - - if (state != old_state) - log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int socket_coldplug(Unit *u) { - Socket *s = SOCKET(u); - int r; - - assert(s); - assert(s->state == SOCKET_DEAD); - - if (s->deserialized_state == s->state) - return 0; - - 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, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL)) { - - r = unit_watch_pid(UNIT(s), s->control_pid); - if (r < 0) - return r; - - r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); - if (r < 0) - return r; - } - - if (IN_SET(s->deserialized_state, - SOCKET_START_CHOWN, - SOCKET_START_POST, - SOCKET_LISTENING, - SOCKET_RUNNING)) { - - /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors - * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for - * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open, - * and if there's a mismatch, warn loudly. */ - - r = socket_check_open(s); - if (r == SOCKET_OPEN_NONE) - log_unit_warning(UNIT(s), - "Socket unit configuration has changed while unit has been running, " - "no open socket file descriptor left. " - "The socket unit is not functional until restarted."); - else if (r == SOCKET_OPEN_SOME) - log_unit_warning(UNIT(s), - "Socket unit configuration has changed while unit has been running, " - "and some socket file descriptors have not been opened yet. " - "The socket unit is not fully functional until restarted."); - } - - if (s->deserialized_state == SOCKET_LISTENING) { - r = socket_watch_fds(s); - if (r < 0) - return r; - } - - socket_set_state(s, s->deserialized_state); - return 0; -} - -static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { - _cleanup_free_ char **argv = NULL; - pid_t pid; - int r; - ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, - }; - - assert(s); - assert(c); - assert(_pid); - - (void) unit_realize_cgroup(UNIT(s)); - if (s->reset_cpu_usage) { - (void) unit_reset_cpu_usage(UNIT(s)); - s->reset_cpu_usage = false; - } - - r = unit_setup_exec_runtime(UNIT(s)); - if (r < 0) - return r; - - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) - return r; - - r = unit_full_printf_strv(UNIT(s), c->argv, &argv); - if (r < 0) - return r; - - exec_params.argv = argv; - exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; - exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; - exec_params.cgroup_path = UNIT(s)->cgroup_path; - exec_params.cgroup_delegate = s->cgroup_context.delegate; - exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); - - r = exec_spawn(UNIT(s), - c, - &s->exec_context, - &exec_params, - s->exec_runtime, - &pid); - if (r < 0) - return r; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ - return r; - - *_pid = pid; - return 0; -} - -static int socket_chown(Socket *s, pid_t *_pid) { - pid_t pid; - int r; - - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) - goto fail; - - /* We have to resolve the user names out-of-process, hence - * let's fork here. It's messy, but well, what can we do? */ - - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { - SocketPort *p; - uid_t uid = UID_INVALID; - gid_t gid = GID_INVALID; - int ret; - - (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); - (void) ignore_signals(SIGPIPE, -1); - log_forget_fds(); - - if (!isempty(s->user)) { - const char *user = s->user; - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - ret = EXIT_USER; - goto fail_child; - } - } - - if (!isempty(s->group)) { - const char *group = s->group; - - r = get_group_creds(&group, &gid); - if (r < 0) { - ret = EXIT_GROUP; - goto fail_child; - } - } - - LIST_FOREACH(port, p, s->ports) { - const char *path = NULL; - - if (p->type == SOCKET_SOCKET) - path = socket_address_get_path(&p->address); - else if (p->type == SOCKET_FIFO) - path = p->path; - - if (!path) - continue; - - if (chown(path, uid, gid) < 0) { - r = -errno; - ret = EXIT_CHOWN; - goto fail_child; - } - } - - _exit(0); - - fail_child: - log_open(); - log_error_errno(r, "Failed to chown socket at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); - - _exit(ret); - } - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - goto fail; - - *_pid = pid; - return 0; - -fail: - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - return r; -} - -static void socket_enter_dead(Socket *s, SocketResult f) { - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - exec_runtime_destroy(s->exec_runtime); - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - - exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - - socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); -} - -static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); - -static void socket_enter_stop_post(Socket *s, SocketResult f) { - int r; - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_POST; - s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]; - - if (s->control_command) { - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - socket_set_state(s, SOCKET_STOP_POST); - } else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { - int r; - - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - r = unit_kill_context( - UNIT(s), - &s->kill_context, - (state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM) ? - KILL_KILL : KILL_TERMINATE, - -1, - s->control_pid, - false); - if (r < 0) - goto fail; - - if (r > 0) { - r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) - goto fail; - - socket_set_state(s, state); - } else if (state == SOCKET_STOP_PRE_SIGTERM) - socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_SUCCESS); - else if (state == SOCKET_STOP_PRE_SIGKILL) - socket_enter_stop_post(s, SOCKET_SUCCESS); - else if (state == SOCKET_FINAL_SIGTERM) - socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_SUCCESS); - else - socket_enter_dead(s, SOCKET_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - - if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) - socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); - else - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_stop_pre(Socket *s, SocketResult f) { - int r; - assert(s); - - if (f != SOCKET_SUCCESS) - s->result = f; - - socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_STOP_PRE; - s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]; - - if (s->control_command) { - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - socket_set_state(s, SOCKET_STOP_PRE); - } else - socket_enter_stop_post(s, SOCKET_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-pre' task: %m"); - socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_listening(Socket *s) { - int r; - assert(s); - - r = socket_watch_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); - goto fail; - } - - socket_set_state(s, SOCKET_LISTENING); - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_start_post(Socket *s) { - int r; - assert(s); - - socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_START_POST; - s->control_command = s->exec_command[SOCKET_EXEC_START_POST]; - - if (s->control_command) { - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); - goto fail; - } - - socket_set_state(s, SOCKET_START_POST); - } else - socket_enter_listening(s); - - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_start_chown(Socket *s) { - int r; - - assert(s); - - r = socket_open_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m"); - goto fail; - } - - if (!isempty(s->user) || !isempty(s->group)) { - - socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_START_CHOWN; - s->control_command = NULL; - - r = socket_chown(s, &s->control_pid); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to fork 'start-chown' task: %m"); - goto fail; - } - - socket_set_state(s, SOCKET_START_CHOWN); - } else - socket_enter_start_post(s); - - return; - -fail: - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); -} - -static void socket_enter_start_pre(Socket *s) { - int r; - assert(s); - - socket_unwatch_control_pid(s); - s->control_command_id = SOCKET_EXEC_START_PRE; - s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; - - if (s->control_command) { - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); - goto fail; - } - - socket_set_state(s, SOCKET_START_PRE); - } else - socket_enter_start_chown(s); - - return; - -fail: - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); -} - -static void flush_ports(Socket *s) { - SocketPort *p; - - /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy - * anymore */ - - LIST_FOREACH(port, p, s->ports) { - if (p->fd < 0) - continue; - - (void) flush_accept(p->fd); - (void) flush_fd(p->fd); - } -} - -static void socket_enter_running(Socket *s, int cfd) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or - * close it. */ - - assert(s); - - /* We don't take connections anymore if we are supposed to shut down anyway */ - if (unit_stop_pending(UNIT(s))) { - - log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); - - if (cfd >= 0) - cfd = safe_close(cfd); - else - flush_ports(s); - - return; - } - - if (!ratelimit_test(&s->trigger_limit)) { - safe_close(cfd); - log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); - socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); - return; - } - - if (cfd < 0) { - Iterator i; - Unit *other; - bool pending = false; - - /* If there's already a start pending don't bother to - * do anything */ - SET_FOREACH(other, UNIT(s)->dependencies[UNIT_TRIGGERS], i) - if (unit_active_or_pending(other)) { - pending = true; - break; - } - - if (!pending) { - if (!UNIT_ISSET(s->service)) { - log_unit_error(UNIT(s), "Service to activate vanished, refusing activation."); - r = -ENOENT; - goto fail; - } - - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); - if (r < 0) - goto fail; - } - - socket_set_state(s, SOCKET_RUNNING); - } else { - _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; - Service *service; - - if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); - safe_close(cfd); - return; - } - - r = socket_instantiate_service(s); - if (r < 0) - goto fail; - - r = instance_from_socket(cfd, s->n_accepted, &instance); - if (r < 0) { - if (r != -ENOTCONN) - goto fail; - - /* ENOTCONN is legitimate if TCP RST was received. - * This connection is over, but the socket unit lives on. */ - log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); - safe_close(cfd); - return; - } - - r = unit_name_to_prefix(UNIT(s)->id, &prefix); - if (r < 0) - goto fail; - - r = unit_name_build(prefix, instance, ".service", &name); - if (r < 0) - goto fail; - - r = unit_add_name(UNIT_DEREF(s->service), name); - if (r < 0) - goto fail; - - service = SERVICE(UNIT_DEREF(s->service)); - unit_ref_unset(&s->service); - - s->n_accepted++; - unit_choose_id(UNIT(service), name); - - r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); - if (r < 0) - goto fail; - - cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ - s->n_connections++; - - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); - if (r < 0) { - /* We failed to activate the new service, but it still exists. Let's make sure the service - * closes and forgets the connection fd again, immediately. */ - service_close_socket_fd(service); - goto fail; - } - - /* Notify clients about changed counters */ - unit_add_to_dbus_queue(UNIT(s)); - } - - return; - -fail: - log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", - cfd >= 0 ? "template" : "non-template", - bus_error_message(&error, r)); - - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - safe_close(cfd); -} - -static void socket_run_next(Socket *s) { - int r; - - assert(s); - assert(s->control_command); - assert(s->control_command->command_next); - - socket_unwatch_control_pid(s); - - s->control_command = s->control_command->command_next; - - r = socket_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run next task: %m"); - - if (s->state == SOCKET_START_POST) - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - else if (s->state == SOCKET_STOP_POST) - socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); - else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); -} - -static int socket_start(Unit *u) { - Socket *s = SOCKET(u); - int r; - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - if (IN_SET(s->state, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL)) - return -EAGAIN; - - /* Already on it! */ - if (IN_SET(s->state, - SOCKET_START_PRE, - SOCKET_START_CHOWN, - SOCKET_START_POST)) - return 0; - - /* Cannot run this without the service being around */ - if (UNIT_ISSET(s->service)) { - Service *service; - - service = SERVICE(UNIT_DEREF(s->service)); - - if (UNIT(service)->load_state != UNIT_LOADED) { - log_unit_error(u, "Socket service %s not loaded, refusing.", UNIT(service)->id); - return -ENOENT; - } - - /* If the service is already active we cannot start the - * socket */ - if (service->state != SERVICE_DEAD && - service->state != SERVICE_FAILED && - service->state != SERVICE_AUTO_RESTART) { - log_unit_error(u, "Socket service %s already active, refusing.", UNIT(service)->id); - return -EBUSY; - } - } - - assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); - - r = unit_start_limit_test(u); - if (r < 0) { - socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); - return r; - } - - s->result = SOCKET_SUCCESS; - s->reset_cpu_usage = true; - - socket_enter_start_pre(s); - - return 1; -} - -static int socket_stop(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - /* Already on it */ - if (IN_SET(s->state, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL)) - return 0; - - /* If there's already something running we go directly into - * kill mode. */ - if (IN_SET(s->state, - SOCKET_START_PRE, - SOCKET_START_CHOWN, - SOCKET_START_POST)) { - socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); - return -EAGAIN; - } - - assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); - - socket_enter_stop_pre(s, SOCKET_SUCCESS); - return 1; -} - -static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { - Socket *s = SOCKET(u); - SocketPort *p; - int r; - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); - unit_serialize_item(u, f, "result", socket_result_to_string(s->result)); - unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); - - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id)); - - LIST_FOREACH(port, p, s->ports) { - int copy; - - if (p->fd < 0) - continue; - - copy = fdset_put_dup(fds, p->fd); - if (copy < 0) - return copy; - - if (p->type == SOCKET_SOCKET) { - _cleanup_free_ char *t = NULL; - - r = socket_address_print(&p->address, &t); - if (r < 0) - return r; - - if (socket_address_family(&p->address) == AF_NETLINK) - unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t); - else - unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t); - - } else if (p->type == SOCKET_SPECIAL) - unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path); - else if (p->type == SOCKET_MQUEUE) - unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path); - else if (p->type == SOCKET_USB_FUNCTION) - unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path); - else { - assert(p->type == SOCKET_FIFO); - unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); - } - } - - return 0; -} - -static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Socket *s = SOCKET(u); - - assert(u); - assert(key); - assert(value); - - if (streq(key, "state")) { - SocketState state; - - state = socket_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, "result")) { - SocketResult f; - - f = socket_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != SOCKET_SUCCESS) - s->result = f; - - } else if (streq(key, "n-accepted")) { - unsigned k; - - if (safe_atou(value, &k) < 0) - log_unit_debug(u, "Failed to parse n-accepted value: %s", value); - else - s->n_accepted += k; - } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - s->control_pid = pid; - } else if (streq(key, "control-command")) { - SocketExecCommand id; - - id = socket_exec_command_from_string(value); - if (id < 0) - log_unit_debug(u, "Failed to parse exec-command value: %s", value); - else { - s->control_command_id = id; - s->control_command = s->exec_command[id]; - } - } else if (streq(key, "fifo")) { - int fd, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse fifo value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_FIFO && - path_equal_or_files_same(p->path, value+skip)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "special")) { - int fd, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse special value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_SPECIAL && - path_equal_or_files_same(p->path, value+skip)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "mqueue")) { - int fd, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse mqueue value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_MQUEUE && - streq(p->path, value+skip)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "socket")) { - int fd, type, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (socket_address_is(&p->address, value+skip, type)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "netlink")) { - int fd, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (socket_address_is_netlink(&p->address, value+skip)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "ffs")) { - int fd, skip = 0; - SocketPort *p; - - if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse ffs value: %s", value); - else { - - LIST_FOREACH(port, p, s->ports) - if (p->type == SOCKET_USB_FUNCTION && - path_equal_or_files_same(p->path, value+skip)) - break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else - log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); - - return 0; -} - -static void socket_distribute_fds(Unit *u, FDSet *fds) { - Socket *s = SOCKET(u); - SocketPort *p; - - assert(u); - - LIST_FOREACH(port, p, s->ports) { - Iterator i; - int fd; - - if (p->type != SOCKET_SOCKET) - continue; - - if (p->fd >= 0) - continue; - - FDSET_FOREACH(fd, fds, i) { - if (socket_address_matches_fd(&p->address, fd)) { - p->fd = fdset_remove(fds, fd); - s->deserialized_state = SOCKET_LISTENING; - break; - } - } - } -} - -_pure_ static UnitActiveState socket_active_state(Unit *u) { - assert(u); - - return state_translation_table[SOCKET(u)->state]; -} - -_pure_ static const char *socket_sub_state_to_string(Unit *u) { - assert(u); - - return socket_state_to_string(SOCKET(u)->state); -} - -const char* socket_port_type_to_string(SocketPort *p) { - - assert(p); - - switch (p->type) { - - case SOCKET_SOCKET: - - switch (p->address.type) { - - case SOCK_STREAM: - return "Stream"; - - case SOCK_DGRAM: - return "Datagram"; - - case SOCK_SEQPACKET: - return "SequentialPacket"; - - case SOCK_RAW: - if (socket_address_family(&p->address) == AF_NETLINK) - return "Netlink"; - - default: - return NULL; - } - - case SOCKET_SPECIAL: - return "Special"; - - case SOCKET_MQUEUE: - return "MessageQueue"; - - case SOCKET_FIFO: - return "FIFO"; - - case SOCKET_USB_FUNCTION: - return "USBFunction"; - - default: - return NULL; - } -} - -_pure_ static bool socket_check_gc(Unit *u) { - Socket *s = SOCKET(u); - - assert(u); - - return s->n_connections > 0; -} - -static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - SocketPort *p = userdata; - int cfd = -1; - - assert(p); - assert(fd >= 0); - - if (p->socket->state != SOCKET_LISTENING) - return 0; - - log_unit_debug(UNIT(p->socket), "Incoming traffic"); - - if (revents != EPOLLIN) { - - if (revents & EPOLLHUP) - log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); - else - log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents); - goto fail; - } - - if (p->socket->accept && - p->type == SOCKET_SOCKET && - socket_address_can_accept(&p->address)) { - - for (;;) { - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); - if (cfd < 0) { - - if (errno == EINTR) - continue; - - log_unit_error_errno(UNIT(p->socket), errno, "Failed to accept socket: %m"); - goto fail; - } - - break; - } - - socket_apply_socket_options(p->socket, cfd); - } - - socket_enter_running(p->socket, cfd); - return 0; - -fail: - socket_enter_stop_pre(p->socket, SOCKET_FAILURE_RESOURCES); - return 0; -} - -static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Socket *s = SOCKET(u); - SocketResult f; - - assert(s); - assert(pid >= 0); - - if (pid != s->control_pid) - return; - - s->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = SOCKET_SUCCESS; - else if (code == CLD_EXITED) - f = SOCKET_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = SOCKET_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SOCKET_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown sigchld code"); - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - if (s->control_command->ignore) - f = SOCKET_SUCCESS; - } - - log_unit_full(u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, - "Control process exited, code=%s status=%i", - sigchld_code_to_string(code), status); - - if (f != SOCKET_SUCCESS) - s->result = f; - - if (s->control_command && - s->control_command->command_next && - f == SOCKET_SUCCESS) { - - log_unit_debug(u, "Running next command for state %s", socket_state_to_string(s->state)); - socket_run_next(s); - } else { - s->control_command = NULL; - s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - - /* No further commands for this step, so let's figure - * out what to do next */ - - log_unit_debug(u, "Got final SIGCHLD for state %s", socket_state_to_string(s->state)); - - switch (s->state) { - - case SOCKET_START_PRE: - if (f == SOCKET_SUCCESS) - socket_enter_start_chown(s); - else - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); - break; - - case SOCKET_START_CHOWN: - if (f == SOCKET_SUCCESS) - socket_enter_start_post(s); - else - socket_enter_stop_pre(s, f); - break; - - case SOCKET_START_POST: - if (f == SOCKET_SUCCESS) - socket_enter_listening(s); - else - socket_enter_stop_pre(s, f); - break; - - case SOCKET_STOP_PRE: - case SOCKET_STOP_PRE_SIGTERM: - case SOCKET_STOP_PRE_SIGKILL: - socket_enter_stop_post(s, f); - break; - - case SOCKET_STOP_POST: - case SOCKET_FINAL_SIGTERM: - case SOCKET_FINAL_SIGKILL: - socket_enter_dead(s, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - Socket *s = SOCKET(userdata); - - assert(s); - assert(s->timer_event_source == source); - - switch (s->state) { - - case SOCKET_START_PRE: - log_unit_warning(UNIT(s), "Starting timed out. Terminating."); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_START_CHOWN: - case SOCKET_START_POST: - log_unit_warning(UNIT(s), "Starting timed out. Stopping."); - socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_STOP_PRE: - log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); - socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_STOP_PRE_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "Stopping timed out. Killing."); - socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL. Ignoring."); - socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); - } - break; - - case SOCKET_STOP_PRE_SIGKILL: - log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring."); - socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_STOP_POST: - log_unit_warning(UNIT(s), "Stopping timed out (2). Terminating."); - socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); - break; - - case SOCKET_FINAL_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "Stopping timed out (2). Killing."); - socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "Stopping timed out (2). Skipping SIGKILL. Ignoring."); - socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); - } - break; - - case SOCKET_FINAL_SIGKILL: - log_unit_warning(UNIT(s), "Still around after SIGKILL (2). Entering failed mode."); - socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -int socket_collect_fds(Socket *s, int **fds) { - int *rfds, k = 0, n = 0; - SocketPort *p; - - assert(s); - assert(fds); - - /* Called from the service code for requesting our fds */ - - LIST_FOREACH(port, p, s->ports) { - if (p->fd >= 0) - n++; - n += p->n_auxiliary_fds; - } - - if (n <= 0) { - *fds = NULL; - return 0; - } - - rfds = new(int, n); - if (!rfds) - return -ENOMEM; - - 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 == n); - - *fds = rfds; - return n; -} - -static void socket_reset_failed(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - if (s->state == SOCKET_FAILED) - socket_set_state(s, SOCKET_DEAD); - - s->result = SOCKET_SUCCESS; -} - -void socket_connection_unref(Socket *s) { - assert(s); - - /* The service is dead. Yay! - * - * This is strictly for one-instance-per-connection - * services. */ - - assert(s->n_connections > 0); - s->n_connections--; - - log_unit_debug(UNIT(s), "One connection closed, %u left.", s->n_connections); -} - -static void socket_trigger_notify(Unit *u, Unit *other) { - Socket *s = SOCKET(u); - - assert(u); - assert(other); - - /* Filter out invocations with bogus state */ - if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) - return; - - /* Don't propagate state changes from the service if we are already down */ - if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING)) - return; - - /* We don't care for the service state if we are in Accept=yes mode */ - if (s->accept) - return; - - /* Propagate start limit hit state */ - if (other->start_limit_hit) { - socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT); - return; - } - - /* Don't propagate anything if there's still a job queued */ - if (other->job) - return; - - if (IN_SET(SERVICE(other)->state, - SERVICE_DEAD, SERVICE_FAILED, - SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, - SERVICE_AUTO_RESTART)) - socket_enter_listening(s); - - if (SERVICE(other)->state == SERVICE_RUNNING) - socket_set_state(s, SOCKET_RUNNING); -} - -static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error); -} - -static int socket_get_timeout(Unit *u, usec_t *timeout) { - Socket *s = SOCKET(u); - usec_t t; - int r; - - if (!s->timer_event_source) - return 0; - - r = sd_event_source_get_time(s->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -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. */ - - if (s->fdname) - return s->fdname; - - return UNIT(s)->id; -} - -static int socket_control_pid(Unit *u) { - Socket *s = SOCKET(u); - - assert(s); - - return s->control_pid; -} - -static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { - [SOCKET_EXEC_START_PRE] = "StartPre", - [SOCKET_EXEC_START_CHOWN] = "StartChown", - [SOCKET_EXEC_START_POST] = "StartPost", - [SOCKET_EXEC_STOP_PRE] = "StopPre", - [SOCKET_EXEC_STOP_POST] = "StopPost" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); - -static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { - [SOCKET_SUCCESS] = "success", - [SOCKET_FAILURE_RESOURCES] = "resources", - [SOCKET_FAILURE_TIMEOUT] = "timeout", - [SOCKET_FAILURE_EXIT_CODE] = "exit-code", - [SOCKET_FAILURE_SIGNAL] = "signal", - [SOCKET_FAILURE_CORE_DUMP] = "core-dump", - [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit", - [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", - [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); - -const UnitVTable socket_vtable = { - .object_size = sizeof(Socket), - .exec_context_offset = offsetof(Socket, exec_context), - .cgroup_context_offset = offsetof(Socket, cgroup_context), - .kill_context_offset = offsetof(Socket, kill_context), - .exec_runtime_offset = offsetof(Socket, exec_runtime), - - .sections = - "Unit\0" - "Socket\0" - "Install\0", - .private_section = "Socket", - - .init = socket_init, - .done = socket_done, - .load = socket_load, - - .coldplug = socket_coldplug, - - .dump = socket_dump, - - .start = socket_start, - .stop = socket_stop, - - .kill = socket_kill, - - .get_timeout = socket_get_timeout, - - .serialize = socket_serialize, - .deserialize_item = socket_deserialize_item, - .distribute_fds = socket_distribute_fds, - - .active_state = socket_active_state, - .sub_state_to_string = socket_sub_state_to_string, - - .check_gc = socket_check_gc, - - .sigchld_event = socket_sigchld_event, - - .trigger_notify = socket_trigger_notify, - - .reset_failed = socket_reset_failed, - - .control_pid = socket_control_pid, - - .bus_vtable = bus_socket_vtable, - .bus_set_property = bus_socket_set_property, - .bus_commit_properties = bus_socket_commit_properties, - - .status_message_formats = { - /*.starting_stopping = { - [0] = "Starting socket %s...", - [1] = "Stopping socket %s...", - },*/ - .finished_start_job = { - [JOB_DONE] = "Listening on %s.", - [JOB_FAILED] = "Failed to listen on %s.", - [JOB_TIMEOUT] = "Timed out starting %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Closed %s.", - [JOB_FAILED] = "Failed stopping %s.", - [JOB_TIMEOUT] = "Timed out stopping %s.", - }, - }, -}; diff --git a/src/core/socket.h b/src/core/socket.h deleted file mode 100644 index 0f1ac69c6f..0000000000 --- a/src/core/socket.h +++ /dev/null @@ -1,185 +0,0 @@ -#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 . -***/ - -typedef struct Socket Socket; - -#include "mount.h" -#include "service.h" -#include "socket-util.h" - -typedef enum SocketExecCommand { - SOCKET_EXEC_START_PRE, - SOCKET_EXEC_START_CHOWN, - SOCKET_EXEC_START_POST, - SOCKET_EXEC_STOP_PRE, - SOCKET_EXEC_STOP_POST, - _SOCKET_EXEC_COMMAND_MAX, - _SOCKET_EXEC_COMMAND_INVALID = -1 -} SocketExecCommand; - -typedef enum SocketType { - SOCKET_SOCKET, - SOCKET_FIFO, - SOCKET_SPECIAL, - SOCKET_MQUEUE, - SOCKET_USB_FUNCTION, - _SOCKET_FIFO_MAX, - _SOCKET_FIFO_INVALID = -1 -} SocketType; - -typedef enum SocketResult { - SOCKET_SUCCESS, - SOCKET_FAILURE_RESOURCES, - SOCKET_FAILURE_TIMEOUT, - SOCKET_FAILURE_EXIT_CODE, - SOCKET_FAILURE_SIGNAL, - SOCKET_FAILURE_CORE_DUMP, - SOCKET_FAILURE_START_LIMIT_HIT, - SOCKET_FAILURE_TRIGGER_LIMIT_HIT, - SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, - _SOCKET_RESULT_MAX, - _SOCKET_RESULT_INVALID = -1 -} SocketResult; - -typedef struct SocketPort { - Socket *socket; - - SocketType type; - int fd; - int *auxiliary_fds; - int n_auxiliary_fds; - - SocketAddress address; - char *path; - sd_event_source *event_source; - - LIST_FIELDS(struct SocketPort, port); -} SocketPort; - -struct Socket { - Unit meta; - - LIST_HEAD(SocketPort, ports); - - unsigned n_accepted; - unsigned n_connections; - unsigned max_connections; - - unsigned backlog; - unsigned keep_alive_cnt; - usec_t timeout_usec; - usec_t keep_alive_time; - usec_t keep_alive_interval; - usec_t defer_accept; - - ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX]; - ExecContext exec_context; - KillContext kill_context; - CGroupContext cgroup_context; - ExecRuntime *exec_runtime; - - /* For Accept=no sockets refers to the one service we'll - activate. For Accept=yes sockets is either NULL, or filled - when the next service we spawn. */ - UnitRef service; - - SocketState state, deserialized_state; - - sd_event_source *timer_event_source; - - ExecCommand* control_command; - SocketExecCommand control_command_id; - pid_t control_pid; - - mode_t directory_mode; - mode_t socket_mode; - - SocketResult result; - - char **symlinks; - - bool accept; - bool remove_on_stop; - bool writable; - - int socket_protocol; - - /* Socket options */ - bool keep_alive; - bool no_delay; - bool free_bind; - bool transparent; - bool broadcast; - bool pass_cred; - bool pass_sec; - - /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ - SocketAddressBindIPv6Only bind_ipv6_only; - - int priority; - int mark; - size_t receive_buffer; - size_t send_buffer; - int ip_tos; - int ip_ttl; - size_t pipe_size; - char *bind_to_device; - char *tcp_congestion; - bool reuse_port; - long mq_maxmsg; - long mq_msgsize; - - char *smack; - char *smack_ip_in; - char *smack_ip_out; - - bool selinux_context_from_net; - - char *user, *group; - - bool reset_cpu_usage:1; - - char *fdname; - - RateLimit trigger_limit; -}; - -/* Called from the service code when collecting 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); - -int socket_instantiate_service(Socket *s); - -char *socket_fdname(Socket *s); - -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_; - -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_; diff --git a/src/core/swap.c b/src/core/swap.c deleted file mode 100644 index a532b15be8..0000000000 --- a/src/core/swap.c +++ /dev/null @@ -1,1532 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "libudev.h" - -#include "alloc-util.h" -#include "dbus-swap.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 "process-util.h" -#include "special.h" -#include "string-table.h" -#include "string-util.h" -#include "swap.h" -#include "udev-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, - [SWAP_ACTIVATING] = UNIT_ACTIVATING, - [SWAP_ACTIVATING_DONE] = UNIT_ACTIVE, - [SWAP_ACTIVE] = UNIT_ACTIVE, - [SWAP_DEACTIVATING] = UNIT_DEACTIVATING, - [SWAP_ACTIVATING_SIGTERM] = UNIT_DEACTIVATING, - [SWAP_ACTIVATING_SIGKILL] = UNIT_DEACTIVATING, - [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING, - [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING, - [SWAP_FAILED] = UNIT_FAILED -}; - -static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); - -static void swap_unset_proc_swaps(Swap *s) { - assert(s); - - if (!s->from_proc_swaps) - return; - - s->parameters_proc_swaps.what = mfree(s->parameters_proc_swaps.what); - - s->from_proc_swaps = false; -} - -static int swap_set_devnode(Swap *s, const char *devnode) { - Hashmap *swaps; - Swap *first; - int r; - - assert(s); - - r = hashmap_ensure_allocated(&UNIT(s)->manager->swaps_by_devnode, &string_hash_ops); - if (r < 0) - return r; - - swaps = UNIT(s)->manager->swaps_by_devnode; - - if (s->devnode) { - first = hashmap_get(swaps, s->devnode); - - LIST_REMOVE(same_devnode, first, s); - if (first) - hashmap_replace(swaps, first->devnode, first); - else - hashmap_remove(swaps, s->devnode); - - s->devnode = mfree(s->devnode); - } - - if (devnode) { - s->devnode = strdup(devnode); - if (!s->devnode) - return -ENOMEM; - - first = hashmap_get(swaps, s->devnode); - LIST_PREPEND(same_devnode, first, s); - - return hashmap_replace(swaps, first->devnode, first); - } - - return 0; -} - -static void swap_init(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - assert(UNIT(s)->load_state == UNIT_STUB); - - s->timeout_usec = u->manager->default_timeout_start_usec; - - s->exec_context.std_output = u->manager->default_std_output; - s->exec_context.std_error = u->manager->default_std_error; - - s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; - - s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - - u->ignore_on_isolate = true; -} - -static void swap_unwatch_control_pid(Swap *s) { - assert(s); - - if (s->control_pid <= 0) - return; - - unit_unwatch_pid(UNIT(s), s->control_pid); - s->control_pid = 0; -} - -static void swap_done(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - swap_unset_proc_swaps(s); - swap_set_devnode(s, NULL); - - s->what = mfree(s->what); - s->parameters_fragment.what = mfree(s->parameters_fragment.what); - s->parameters_fragment.options = mfree(s->parameters_fragment.options); - - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); - s->control_command = NULL; - - swap_unwatch_control_pid(s); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); -} - -static int swap_arm_timer(Swap *s, usec_t usec) { - int r; - - assert(s); - - if (s->timer_event_source) { - r = sd_event_source_set_time(s->timer_event_source, usec); - if (r < 0) - return r; - - return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); - } - - if (usec == USEC_INFINITY) - return 0; - - r = sd_event_add_time( - UNIT(s)->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - usec, 0, - swap_dispatch_timer, s); - if (r < 0) - return r; - - (void) sd_event_source_set_description(s->timer_event_source, "swap-timer"); - - return 0; -} - -static int swap_add_device_links(Swap *s) { - assert(s); - - if (!s->what) - return 0; - - if (!s->from_fragment) - return 0; - - if (is_device_path(s->what)) - return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO); - else - /* File based swap devices need to be ordered after - * systemd-remount-fs.service, since they might need a - * writable file system. */ - return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true); -} - -static int swap_add_default_dependencies(Swap *s) { - int r; - - assert(s); - - if (!UNIT(s)->default_dependencies) - return 0; - - if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) - 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); -} - -static int swap_verify(Swap *s) { - _cleanup_free_ char *e = NULL; - int r; - - if (UNIT(s)->load_state != UNIT_LOADED) - return 0; - - r = unit_name_from_path(s->what, ".swap", &e); - if (r < 0) - return log_unit_error_errno(UNIT(s), r, "Failed to generate unit name from path: %m"); - - if (!unit_has_name(UNIT(s), e)) { - log_unit_error(UNIT(s), "Value of What= and unit name do not match, not loading."); - return -EINVAL; - } - - if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load."); - return -EINVAL; - } - - return 0; -} - -static int swap_load_devnode(Swap *s) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - struct stat st; - const char *p; - - assert(s); - - if (stat(s->what, &st) < 0 || !S_ISBLK(st.st_mode)) - return 0; - - d = udev_device_new_from_devnum(UNIT(s)->manager->udev, 'b', st.st_rdev); - if (!d) - return 0; - - p = udev_device_get_devnode(d); - if (!p) - return 0; - - return swap_set_devnode(s, p); -} - -static int swap_load(Unit *u) { - int r; - Swap *s = SWAP(u); - - assert(s); - assert(u->load_state == UNIT_STUB); - - /* Load a .swap file */ - r = unit_load_fragment_and_dropin_optional(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - - if (UNIT(s)->fragment_path) - s->from_fragment = true; - - if (!s->what) { - if (s->parameters_fragment.what) - s->what = strdup(s->parameters_fragment.what); - else if (s->parameters_proc_swaps.what) - s->what = strdup(s->parameters_proc_swaps.what); - else { - r = unit_name_to_path(u->id, &s->what); - if (r < 0) - return r; - } - - if (!s->what) - return -ENOMEM; - } - - path_kill_slashes(s->what); - - if (!UNIT(s)->description) { - r = unit_set_description(u, s->what); - if (r < 0) - return r; - } - - r = unit_require_mounts_for(UNIT(s), s->what); - if (r < 0) - return r; - - r = swap_add_device_links(s); - if (r < 0) - return r; - - r = swap_load_devnode(s); - if (r < 0) - return r; - - r = unit_patch_contexts(u); - if (r < 0) - return r; - - r = unit_add_exec_dependencies(u, &s->exec_context); - if (r < 0) - return r; - - r = unit_set_default_slice(u); - if (r < 0) - return r; - - r = swap_add_default_dependencies(s); - if (r < 0) - return r; - } - - return swap_verify(s); -} - -static int swap_setup_unit( - Manager *m, - const char *what, - const char *what_proc_swaps, - int priority, - bool set_flags) { - - _cleanup_free_ char *e = NULL; - bool delete = false; - Unit *u = NULL; - int r; - SwapParameters *p; - - assert(m); - assert(what); - assert(what_proc_swaps); - - r = unit_name_from_path(what, ".swap", &e); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to generate unit name from path: %m"); - - u = manager_get_unit(m, e); - - if (u && - SWAP(u)->from_proc_swaps && - !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) { - log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps); - return -EEXIST; - } - - if (!u) { - delete = true; - - u = unit_new(m, sizeof(Swap)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); - if (r < 0) - goto fail; - - SWAP(u)->what = strdup(what); - if (!SWAP(u)->what) { - r = -ENOMEM; - goto fail; - } - - unit_add_to_load_queue(u); - } else - delete = false; - - p = &SWAP(u)->parameters_proc_swaps; - - if (!p->what) { - p->what = strdup(what_proc_swaps); - if (!p->what) { - r = -ENOMEM; - goto fail; - } - } - - if (set_flags) { - SWAP(u)->is_active = true; - SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; - } - - SWAP(u)->from_proc_swaps = true; - - p->priority = priority; - - unit_add_to_dbus_queue(u); - return 0; - -fail: - log_unit_warning_errno(u, r, "Failed to load swap unit: %m"); - - if (delete && u) - unit_free(u); - - return r; -} - -static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - const char *dn; - struct stat st; - int r; - - assert(m); - - r = swap_setup_unit(m, device, device, prio, set_flags); - if (r < 0) - return r; - - /* If this is a block device, then let's add duplicates for - * all other names of this block device */ - if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode)) - return 0; - - d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev); - if (!d) - return 0; - - /* Add the main device node */ - dn = udev_device_get_devnode(d); - if (dn && !streq(dn, device)) - swap_setup_unit(m, dn, device, prio, set_flags); - - /* Add additional units for all symlinks */ - first = udev_device_get_devlinks_list_entry(d); - udev_list_entry_foreach(item, first) { - const char *p; - - /* Don't bother with the /dev/block links */ - p = udev_list_entry_get_name(item); - - if (streq(p, device)) - continue; - - if (path_startswith(p, "/dev/block/")) - continue; - - if (stat(p, &st) >= 0) - if (!S_ISBLK(st.st_mode) || - st.st_rdev != udev_device_get_devnum(d)) - continue; - - swap_setup_unit(m, p, device, prio, set_flags); - } - - return r; -} - -static void swap_set_state(Swap *s, SwapState state) { - SwapState old_state; - Swap *other; - - assert(s); - - old_state = s->state; - s->state = state; - - if (state != SWAP_ACTIVATING && - state != SWAP_ACTIVATING_SIGTERM && - state != SWAP_ACTIVATING_SIGKILL && - state != SWAP_ACTIVATING_DONE && - state != SWAP_DEACTIVATING && - state != SWAP_DEACTIVATING_SIGTERM && - state != SWAP_DEACTIVATING_SIGKILL) { - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - swap_unwatch_control_pid(s); - s->control_command = NULL; - s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - } - - if (state != old_state) - log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); - - /* If there other units for the same device node have a job - queued it might be worth checking again if it is runnable - now. This is necessary, since swap_start() refuses - operation with EAGAIN if there's already another job for - the same device node queued. */ - LIST_FOREACH_OTHERS(same_devnode, other, s) - if (UNIT(other)->job) - job_add_to_run_queue(UNIT(other)->job); -} - -static int swap_coldplug(Unit *u) { - Swap *s = SWAP(u); - SwapState new_state = SWAP_DEAD; - int r; - - assert(s); - assert(s->state == SWAP_DEAD); - - if (s->deserialized_state != s->state) - new_state = s->deserialized_state; - else if (s->from_proc_swaps) - new_state = SWAP_ACTIVE; - - if (new_state == s->state) - return 0; - - 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) - return r; - - r = swap_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); - if (r < 0) - return r; - } - - swap_set_state(s, new_state); - return 0; -} - -static void swap_dump(Unit *u, FILE *f, const char *prefix) { - Swap *s = SWAP(u); - SwapParameters *p; - - assert(s); - assert(f); - - if (s->from_proc_swaps) - p = &s->parameters_proc_swaps; - else if (s->from_fragment) - p = &s->parameters_fragment; - else - p = NULL; - - fprintf(f, - "%sSwap State: %s\n" - "%sResult: %s\n" - "%sWhat: %s\n" - "%sFrom /proc/swaps: %s\n" - "%sFrom fragment: %s\n", - prefix, swap_state_to_string(s->state), - prefix, swap_result_to_string(s->result), - prefix, s->what, - prefix, yes_no(s->from_proc_swaps), - prefix, yes_no(s->from_fragment)); - - if (s->devnode) - fprintf(f, "%sDevice Node: %s\n", prefix, s->devnode); - - if (p) - fprintf(f, - "%sPriority: %i\n" - "%sOptions: %s\n", - prefix, p->priority, - prefix, strempty(p->options)); - - if (s->control_pid > 0) - fprintf(f, - "%sControl PID: "PID_FMT"\n", - prefix, s->control_pid); - - exec_context_dump(&s->exec_context, f, prefix); - kill_context_dump(&s->kill_context, f, prefix); -} - -static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { - pid_t pid; - int r; - ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, - }; - - assert(s); - assert(c); - assert(_pid); - - (void) unit_realize_cgroup(UNIT(s)); - if (s->reset_cpu_usage) { - (void) unit_reset_cpu_usage(UNIT(s)); - s->reset_cpu_usage = false; - } - - r = unit_setup_exec_runtime(UNIT(s)); - if (r < 0) - goto fail; - - r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) - goto fail; - - exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; - exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; - exec_params.cgroup_path = UNIT(s)->cgroup_path; - exec_params.cgroup_delegate = s->cgroup_context.delegate; - exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); - - r = exec_spawn(UNIT(s), - c, - &s->exec_context, - &exec_params, - s->exec_runtime, - &pid); - if (r < 0) - goto fail; - - r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ - goto fail; - - *_pid = pid; - - return 0; - -fail: - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - return r; -} - -static void swap_enter_dead(Swap *s, SwapResult f) { - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - exec_runtime_destroy(s->exec_runtime); - s->exec_runtime = exec_runtime_unref(s->exec_runtime); - - exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); - - swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); -} - -static void swap_enter_active(Swap *s, SwapResult f) { - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - swap_set_state(s, SWAP_ACTIVE); -} - -static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { - int r; - - assert(s); - - if (f != SWAP_SUCCESS) - s->result = f; - - r = unit_kill_context( - UNIT(s), - &s->kill_context, - (state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM) ? - KILL_KILL : KILL_TERMINATE, - -1, - s->control_pid, - false); - if (r < 0) - goto fail; - - if (r > 0) { - r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); - if (r < 0) - goto fail; - - swap_set_state(s, state); - } else if (state == SWAP_ACTIVATING_SIGTERM) - swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_SUCCESS); - else if (state == SWAP_DEACTIVATING_SIGTERM) - swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_SUCCESS); - else - swap_enter_dead(s, SWAP_SUCCESS); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); - swap_enter_dead(s, SWAP_FAILURE_RESOURCES); -} - -static void swap_enter_activating(Swap *s) { - _cleanup_free_ char *opts = NULL; - int r; - - assert(s); - - s->control_command_id = SWAP_EXEC_ACTIVATE; - s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE; - - if (s->from_fragment) { - int priority = -1; - - r = fstab_find_pri(s->parameters_fragment.options, &priority); - if (r < 0) - log_warning_errno(r, "Failed to parse swap priority \"%s\", ignoring: %m", s->parameters_fragment.options); - else if (r == 1 && s->parameters_fragment.priority >= 0) - log_warning("Duplicate swap priority configuration by Priority and Options fields."); - - if (r <= 0 && s->parameters_fragment.priority >= 0) { - if (s->parameters_fragment.options) - r = asprintf(&opts, "%s,pri=%i", s->parameters_fragment.options, s->parameters_fragment.priority); - else - r = asprintf(&opts, "pri=%i", s->parameters_fragment.priority); - if (r < 0) - goto fail; - } - } - - r = exec_command_set(s->control_command, "/sbin/swapon", NULL); - if (r < 0) - goto fail; - - if (s->parameters_fragment.options || opts) { - r = exec_command_append(s->control_command, "-o", - opts ? : s->parameters_fragment.options, NULL); - if (r < 0) - goto fail; - } - - r = exec_command_append(s->control_command, s->what, NULL); - if (r < 0) - goto fail; - - swap_unwatch_control_pid(s); - - r = swap_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - swap_set_state(s, SWAP_ACTIVATING); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapon' task: %m"); - swap_enter_dead(s, SWAP_FAILURE_RESOURCES); -} - -static void swap_enter_deactivating(Swap *s) { - int r; - - assert(s); - - s->control_command_id = SWAP_EXEC_DEACTIVATE; - s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE; - - r = exec_command_set(s->control_command, - "/sbin/swapoff", - s->what, - NULL); - if (r < 0) - goto fail; - - swap_unwatch_control_pid(s); - - r = swap_spawn(s, s->control_command, &s->control_pid); - if (r < 0) - goto fail; - - swap_set_state(s, SWAP_DEACTIVATING); - - return; - -fail: - log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapoff' task: %m"); - swap_enter_active(s, SWAP_FAILURE_RESOURCES); -} - -static int swap_start(Unit *u) { - Swap *s = SWAP(u), *other; - int r; - - assert(s); - - /* We cannot fulfill this request right now, try again later - * please! */ - - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) - return -EAGAIN; - - if (s->state == SWAP_ACTIVATING) - return 0; - - assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED); - - if (detect_container() > 0) - return -EPERM; - - /* If there's a job for another swap unit for the same node - * running, then let's not dispatch this one for now, and wait - * until that other job has finished. */ - LIST_FOREACH_OTHERS(same_devnode, other, s) - if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) - return -EAGAIN; - - r = unit_start_limit_test(u); - if (r < 0) { - swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); - return r; - } - - s->result = SWAP_SUCCESS; - s->reset_cpu_usage = true; - - swap_enter_activating(s); - return 1; -} - -static int swap_stop(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) - return 0; - - assert(s->state == SWAP_ACTIVATING || - s->state == SWAP_ACTIVATING_DONE || - s->state == SWAP_ACTIVE); - - if (detect_container() > 0) - return -EPERM; - - swap_enter_deactivating(s); - return 1; -} - -static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { - Swap *s = SWAP(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); - unit_serialize_item(u, f, "result", swap_result_to_string(s->result)); - - if (s->control_pid > 0) - unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); - - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id)); - - return 0; -} - -static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Swap *s = SWAP(u); - - assert(s); - assert(fds); - - if (streq(key, "state")) { - SwapState state; - - state = swap_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, "result")) { - SwapResult f; - - f = swap_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != SWAP_SUCCESS) - s->result = f; - } else if (streq(key, "control-pid")) { - pid_t pid; - - if (parse_pid(value, &pid) < 0) - log_unit_debug(u, "Failed to parse control-pid value: %s", value); - else - s->control_pid = pid; - - } else if (streq(key, "control-command")) { - SwapExecCommand id; - - id = swap_exec_command_from_string(value); - if (id < 0) - log_unit_debug(u, "Failed to parse exec-command value: %s", value); - else { - s->control_command_id = id; - s->control_command = s->exec_command + id; - } - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState swap_active_state(Unit *u) { - assert(u); - - return state_translation_table[SWAP(u)->state]; -} - -_pure_ static const char *swap_sub_state_to_string(Unit *u) { - assert(u); - - return swap_state_to_string(SWAP(u)->state); -} - -_pure_ static bool swap_check_gc(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - return s->from_proc_swaps; -} - -static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { - Swap *s = SWAP(u); - SwapResult f; - - assert(s); - assert(pid >= 0); - - if (pid != s->control_pid) - return; - - s->control_pid = 0; - - if (is_clean_exit(code, status, NULL)) - f = SWAP_SUCCESS; - else if (code == CLD_EXITED) - f = SWAP_FAILURE_EXIT_CODE; - else if (code == CLD_KILLED) - f = SWAP_FAILURE_SIGNAL; - else if (code == CLD_DUMPED) - f = SWAP_FAILURE_CORE_DUMP; - else - assert_not_reached("Unknown code"); - - if (f != SWAP_SUCCESS) - s->result = f; - - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - - s->control_command = NULL; - s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; - } - - log_unit_full(u, f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, - "Swap process exited, code=%s status=%i", sigchld_code_to_string(code), status); - - switch (s->state) { - - case SWAP_ACTIVATING: - case SWAP_ACTIVATING_DONE: - case SWAP_ACTIVATING_SIGTERM: - case SWAP_ACTIVATING_SIGKILL: - - if (f == SWAP_SUCCESS) - swap_enter_active(s, f); - else - swap_enter_dead(s, f); - break; - - case SWAP_DEACTIVATING: - case SWAP_DEACTIVATING_SIGKILL: - case SWAP_DEACTIVATING_SIGTERM: - - swap_enter_dead(s, f); - break; - - default: - assert_not_reached("Uh, control process died at wrong time."); - } - - /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); -} - -static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { - Swap *s = SWAP(userdata); - - assert(s); - assert(s->timer_event_source == source); - - switch (s->state) { - - case SWAP_ACTIVATING: - case SWAP_ACTIVATING_DONE: - log_unit_warning(UNIT(s), "Activation timed out. Stopping."); - swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); - break; - - case SWAP_DEACTIVATING: - log_unit_warning(UNIT(s), "Deactivation timed out. Stopping."); - swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); - break; - - case SWAP_ACTIVATING_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "Activation timed out. Killing."); - swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "Activation timed out. Skipping SIGKILL. Ignoring."); - swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); - } - break; - - case SWAP_DEACTIVATING_SIGTERM: - if (s->kill_context.send_sigkill) { - log_unit_warning(UNIT(s), "Deactivation timed out. Killing."); - swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); - } else { - log_unit_warning(UNIT(s), "Deactivation timed out. Skipping SIGKILL. Ignoring."); - swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); - } - break; - - case SWAP_ACTIVATING_SIGKILL: - case SWAP_DEACTIVATING_SIGKILL: - log_unit_warning(UNIT(s), "Swap process still around after SIGKILL. Ignoring."); - swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); - break; - - default: - assert_not_reached("Timeout at wrong time."); - } - - return 0; -} - -static int swap_load_proc_swaps(Manager *m, bool set_flags) { - unsigned i; - int r = 0; - - assert(m); - - rewind(m->proc_swaps); - - (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n"); - - for (i = 1;; i++) { - _cleanup_free_ char *dev = NULL, *d = NULL; - int prio = 0, k; - - k = fscanf(m->proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%i\n", /* priority */ - &dev, &prio); - if (k != 2) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u.", i); - continue; - } - - if (cunescape(dev, UNESCAPE_RELAX, &d) < 0) - return log_oom(); - - device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags); - - k = swap_process_new(m, d, prio, set_flags); - if (k < 0) - r = k; - } - - return r; -} - -static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - Unit *u; - int r; - - assert(m); - assert(revents & EPOLLPRI); - - r = swap_load_proc_swaps(m, true); - if (r < 0) { - log_error_errno(r, "Failed to reread /proc/swaps: %m"); - - /* Reset flags, just in case, for late calls */ - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { - Swap *swap = SWAP(u); - - swap->is_active = swap->just_activated = false; - } - - return 0; - } - - manager_dispatch_load_queue(m); - - LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { - Swap *swap = SWAP(u); - - if (!swap->is_active) { - /* This has just been deactivated */ - - swap_unset_proc_swaps(swap); - - switch (swap->state) { - - case SWAP_ACTIVE: - swap_enter_dead(swap, SWAP_SUCCESS); - break; - - default: - /* Fire again */ - swap_set_state(swap, swap->state); - break; - } - - if (swap->what) - device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true); - - } else if (swap->just_activated) { - - /* New swap entry */ - - switch (swap->state) { - - case SWAP_DEAD: - case SWAP_FAILED: - swap_enter_active(swap, SWAP_SUCCESS); - break; - - case SWAP_ACTIVATING: - swap_set_state(swap, SWAP_ACTIVATING_DONE); - break; - - default: - /* Nothing really changed, but let's - * issue an notification call - * nonetheless, in case somebody is - * waiting for this. */ - swap_set_state(swap, swap->state); - break; - } - } - - /* Reset the flags for later calls */ - swap->is_active = swap->just_activated = false; - } - - return 1; -} - -static Unit *swap_following(Unit *u) { - Swap *s = SWAP(u); - Swap *other, *first = NULL; - - assert(s); - - /* If the user configured the swap through /etc/fstab or - * a device unit, follow that. */ - - if (s->from_fragment) - return NULL; - - LIST_FOREACH_OTHERS(same_devnode, other, s) - if (other->from_fragment) - return UNIT(other); - - /* Otherwise, make everybody follow the unit that's named after - * the swap device in the kernel */ - - if (streq_ptr(s->what, s->devnode)) - return NULL; - - LIST_FOREACH_AFTER(same_devnode, other, s) - if (streq_ptr(other->what, other->devnode)) - return UNIT(other); - - LIST_FOREACH_BEFORE(same_devnode, other, s) { - if (streq_ptr(other->what, other->devnode)) - return UNIT(other); - - first = other; - } - - /* Fall back to the first on the list */ - return UNIT(first); -} - -static int swap_following_set(Unit *u, Set **_set) { - Swap *s = SWAP(u), *other; - Set *set; - int r; - - assert(s); - assert(_set); - - if (LIST_JUST_US(same_devnode, s)) { - *_set = NULL; - return 0; - } - - set = set_new(NULL); - if (!set) - return -ENOMEM; - - LIST_FOREACH_OTHERS(same_devnode, other, s) { - r = set_put(set, other); - if (r < 0) - goto fail; - } - - *_set = set; - return 1; - -fail: - set_free(set); - return r; -} - -static void swap_shutdown(Manager *m) { - assert(m); - - m->swap_event_source = sd_event_source_unref(m->swap_event_source); - - m->proc_swaps = safe_fclose(m->proc_swaps); - - m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); -} - -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) { - 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) { - 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) { - 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"); - } - - r = swap_load_proc_swaps(m, false); - if (r < 0) - goto fail; - - return; - -fail: - swap_shutdown(m); -} - -int swap_process_device_new(Manager *m, struct udev_device *dev) { - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_free_ char *e = NULL; - const char *dn; - Swap *s; - int r = 0; - - assert(m); - assert(dev); - - dn = udev_device_get_devnode(dev); - if (!dn) - return 0; - - r = unit_name_from_path(dn, ".swap", &e); - if (r < 0) - return r; - - s = hashmap_get(m->units, e); - if (s) - r = swap_set_devnode(s, dn); - - first = udev_device_get_devlinks_list_entry(dev); - udev_list_entry_foreach(item, first) { - _cleanup_free_ char *n = NULL; - int q; - - q = unit_name_from_path(udev_list_entry_get_name(item), ".swap", &n); - if (q < 0) - return q; - - s = hashmap_get(m->units, n); - if (s) { - q = swap_set_devnode(s, dn); - if (q < 0) - r = q; - } - } - - return r; -} - -int swap_process_device_remove(Manager *m, struct udev_device *dev) { - const char *dn; - int r = 0; - Swap *s; - - dn = udev_device_get_devnode(dev); - if (!dn) - return 0; - - while ((s = hashmap_get(m->swaps_by_devnode, dn))) { - int q; - - q = swap_set_devnode(s, NULL); - if (q < 0) - r = q; - } - - return r; -} - -static void swap_reset_failed(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - if (s->state == SWAP_FAILED) - swap_set_state(s, SWAP_DEAD); - - s->result = SWAP_SUCCESS; -} - -static int swap_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { - return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error); -} - -static int swap_get_timeout(Unit *u, usec_t *timeout) { - Swap *s = SWAP(u); - usec_t t; - int r; - - if (!s->timer_event_source) - return 0; - - r = sd_event_source_get_time(s->timer_event_source, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) - return 0; - - *timeout = t; - return 1; -} - -static bool swap_supported(void) { - static int supported = -1; - - /* If swap support is not available in the kernel, or we are - * running in a container we don't support swap units, and any - * attempts to starting one should fail immediately. */ - - if (supported < 0) - supported = - access("/proc/swaps", F_OK) >= 0 && - detect_container() <= 0; - - return supported; -} - -static int swap_control_pid(Unit *u) { - Swap *s = SWAP(u); - - assert(s); - - return s->control_pid; -} - -static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { - [SWAP_EXEC_ACTIVATE] = "ExecActivate", - [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand); - -static const char* const swap_result_table[_SWAP_RESULT_MAX] = { - [SWAP_SUCCESS] = "success", - [SWAP_FAILURE_RESOURCES] = "resources", - [SWAP_FAILURE_TIMEOUT] = "timeout", - [SWAP_FAILURE_EXIT_CODE] = "exit-code", - [SWAP_FAILURE_SIGNAL] = "signal", - [SWAP_FAILURE_CORE_DUMP] = "core-dump", - [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); - -const UnitVTable swap_vtable = { - .object_size = sizeof(Swap), - .exec_context_offset = offsetof(Swap, exec_context), - .cgroup_context_offset = offsetof(Swap, cgroup_context), - .kill_context_offset = offsetof(Swap, kill_context), - .exec_runtime_offset = offsetof(Swap, exec_runtime), - - .sections = - "Unit\0" - "Swap\0" - "Install\0", - .private_section = "Swap", - - .init = swap_init, - .load = swap_load, - .done = swap_done, - - .coldplug = swap_coldplug, - - .dump = swap_dump, - - .start = swap_start, - .stop = swap_stop, - - .kill = swap_kill, - - .get_timeout = swap_get_timeout, - - .serialize = swap_serialize, - .deserialize_item = swap_deserialize_item, - - .active_state = swap_active_state, - .sub_state_to_string = swap_sub_state_to_string, - - .check_gc = swap_check_gc, - - .sigchld_event = swap_sigchld_event, - - .reset_failed = swap_reset_failed, - - .control_pid = swap_control_pid, - - .bus_vtable = bus_swap_vtable, - .bus_set_property = bus_swap_set_property, - .bus_commit_properties = bus_swap_commit_properties, - - .following = swap_following, - .following_set = swap_following_set, - - .enumerate = swap_enumerate, - .shutdown = swap_shutdown, - .supported = swap_supported, - - .status_message_formats = { - .starting_stopping = { - [0] = "Activating swap %s...", - [1] = "Deactivating swap %s...", - }, - .finished_start_job = { - [JOB_DONE] = "Activated swap %s.", - [JOB_FAILED] = "Failed to activate swap %s.", - [JOB_TIMEOUT] = "Timed out activating swap %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Deactivated swap %s.", - [JOB_FAILED] = "Failed deactivating swap %s.", - [JOB_TIMEOUT] = "Timed out deactivating swap %s.", - }, - }, -}; diff --git a/src/core/swap.h b/src/core/swap.h deleted file mode 100644 index fbf66debdc..0000000000 --- a/src/core/swap.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Maarten Lankhorst - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "libudev.h" - -typedef struct Swap Swap; - -typedef enum SwapExecCommand { - SWAP_EXEC_ACTIVATE, - SWAP_EXEC_DEACTIVATE, - _SWAP_EXEC_COMMAND_MAX, - _SWAP_EXEC_COMMAND_INVALID = -1 -} SwapExecCommand; - -typedef enum SwapResult { - SWAP_SUCCESS, - SWAP_FAILURE_RESOURCES, - SWAP_FAILURE_TIMEOUT, - SWAP_FAILURE_EXIT_CODE, - SWAP_FAILURE_SIGNAL, - SWAP_FAILURE_CORE_DUMP, - SWAP_FAILURE_START_LIMIT_HIT, - _SWAP_RESULT_MAX, - _SWAP_RESULT_INVALID = -1 -} SwapResult; - -typedef struct SwapParameters { - char *what; - char *options; - int priority; -} SwapParameters; - -struct Swap { - Unit meta; - - char *what; - - /* If the device has already shown up, this is the device - * node, which might be different from what, due to - * symlinks */ - char *devnode; - - SwapParameters parameters_proc_swaps; - SwapParameters parameters_fragment; - - bool from_proc_swaps:1; - bool from_fragment:1; - - /* Used while looking for swaps that vanished or got added - * from/to /proc/swaps */ - bool is_active:1; - bool just_activated:1; - - bool reset_cpu_usage:1; - - SwapResult result; - - usec_t timeout_usec; - - ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX]; - ExecContext exec_context; - KillContext kill_context; - CGroupContext cgroup_context; - - ExecRuntime *exec_runtime; - - SwapState state, deserialized_state; - - ExecCommand* control_command; - SwapExecCommand control_command_id; - pid_t control_pid; - - sd_event_source *timer_event_source; - - /* In order to be able to distinguish dependencies on - different device nodes we might end up creating multiple - devices for the same swap. We chain them up here. */ - - LIST_FIELDS(struct Swap, same_devnode); -}; - -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_exec_command_to_string(SwapExecCommand i) _const_; -SwapExecCommand swap_exec_command_from_string(const char *s) _pure_; - -const char* swap_result_to_string(SwapResult i) _const_; -SwapResult swap_result_from_string(const char *s) _pure_; diff --git a/src/core/system.conf b/src/core/system.conf deleted file mode 100644 index c6bb050aac..0000000000 --- a/src/core/system.conf +++ /dev/null @@ -1,61 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See systemd-system.conf(5) for details. - -[Manager] -#LogLevel=info -#LogTarget=journal-or-kmsg -#LogColor=yes -#LogLocation=no -#DumpCore=yes -#ShowStatus=yes -#CrashChangeVT=no -#CrashShell=no -#CrashReboot=no -#CPUAffinity=1 2 -#JoinControllers=cpu,cpuacct net_cls,net_prio -#RuntimeWatchdogSec=0 -#ShutdownWatchdogSec=10min -#CapabilityBoundingSet= -#SystemCallArchitectures= -#TimerSlackNSec= -#DefaultTimerAccuracySec=1min -#DefaultStandardOutput=journal -#DefaultStandardError=inherit -#DefaultTimeoutStartSec=90s -#DefaultTimeoutStopSec=90s -#DefaultRestartSec=100ms -#DefaultStartLimitIntervalSec=10s -#DefaultStartLimitBurst=5 -#DefaultEnvironment= -#DefaultCPUAccounting=no -#DefaultIOAccounting=no -#DefaultBlockIOAccounting=no -#DefaultMemoryAccounting=no -#DefaultTasksAccounting=yes -#DefaultTasksMax=15% -#DefaultLimitCPU= -#DefaultLimitFSIZE= -#DefaultLimitDATA= -#DefaultLimitSTACK= -#DefaultLimitCORE= -#DefaultLimitRSS= -#DefaultLimitNOFILE= -#DefaultLimitAS= -#DefaultLimitNPROC= -#DefaultLimitMEMLOCK= -#DefaultLimitLOCKS= -#DefaultLimitSIGPENDING= -#DefaultLimitMSGQUEUE= -#DefaultLimitNICE= -#DefaultLimitRTPRIO= -#DefaultLimitRTTIME= diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in deleted file mode 100644 index ac52b30dd3..0000000000 --- a/src/core/systemd.pc.in +++ /dev/null @@ -1,34 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -prefix=@prefix@ -systemdutildir=@rootlibexecdir@ -systemdsystemunitdir=@systemunitdir@ -systemdsystempresetdir=@systempresetdir@ -systemduserunitdir=@userunitdir@ -systemduserpresetdir=@userpresetdir@ -systemdsystemconfdir=@pkgsysconfdir@/system -systemduserconfdir=@pkgsysconfdir@/user -systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system -systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user -systemdsystemgeneratordir=@systemgeneratordir@ -systemdusergeneratordir=@usergeneratordir@ -systemdsleepdir=@systemsleepdir@ -systemdshutdowndir=@systemshutdowndir@ -tmpfilesdir=@tmpfilesdir@ -sysusersdir=@sysusersdir@ -sysctldir=@sysctldir@ -binfmtdir=@binfmtdir@ -modulesloaddir=@modulesloaddir@ -catalogdir=@catalogdir@ -systemuidmax=@systemuidmax@ -systemgidmax=@systemgidmax@ - -Name: systemd -Description: systemd System and Service Manager -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ diff --git a/src/core/target.c b/src/core/target.c deleted file mode 100644 index 61a91aad07..0000000000 --- a/src/core/target.c +++ /dev/null @@ -1,223 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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, - [TARGET_ACTIVE] = UNIT_ACTIVE -}; - -static void target_set_state(Target *t, TargetState state) { - TargetState old_state; - assert(t); - - old_state = t->state; - t->state = state; - - if (state != old_state) - log_debug("%s changed %s -> %s", - UNIT(t)->id, - target_state_to_string(old_state), - target_state_to_string(state)); - - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); -} - -static int target_add_default_dependencies(Target *t) { - - static const UnitDependency deps[] = { - UNIT_REQUIRES, - UNIT_REQUISITE, - UNIT_WANTS, - UNIT_BINDS_TO, - UNIT_PART_OF - }; - - Iterator i; - Unit *other; - int r; - unsigned k; - - assert(t); - - /* Imply ordering for requirement dependencies on target - * units. Note that when the user created a contradicting - * ordering manually we won't add anything in here to make - * sure we don't create a loop. */ - - for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) { - r = unit_add_default_target_dependency(other, UNIT(t)); - if (r < 0) - return r; - } - - /* Make sure targets are unloaded on shutdown */ - return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static int target_load(Unit *u) { - Target *t = TARGET(u); - int r; - - assert(t); - - r = unit_load_fragment_and_dropin(u); - if (r < 0) - return r; - - /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED && u->default_dependencies) { - r = target_add_default_dependencies(t); - if (r < 0) - return r; - } - - return 0; -} - -static int target_coldplug(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_DEAD); - - if (t->deserialized_state != t->state) - target_set_state(t, t->deserialized_state); - - return 0; -} - -static void target_dump(Unit *u, FILE *f, const char *prefix) { - Target *t = TARGET(u); - - assert(t); - assert(f); - - fprintf(f, - "%sTarget State: %s\n", - prefix, target_state_to_string(t->state)); -} - -static int target_start(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_DEAD); - - target_set_state(t, TARGET_ACTIVE); - return 1; -} - -static int target_stop(Unit *u) { - Target *t = TARGET(u); - - assert(t); - assert(t->state == TARGET_ACTIVE); - - target_set_state(t, TARGET_DEAD); - return 1; -} - -static int target_serialize(Unit *u, FILE *f, FDSet *fds) { - Target *s = TARGET(u); - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", target_state_to_string(s->state)); - return 0; -} - -static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Target *s = TARGET(u); - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - TargetState state; - - state = target_state_from_string(value); - if (state < 0) - log_debug("Failed to parse state value %s", value); - else - s->deserialized_state = state; - - } else - log_debug("Unknown serialization key '%s'", key); - - return 0; -} - -_pure_ static UnitActiveState target_active_state(Unit *u) { - assert(u); - - return state_translation_table[TARGET(u)->state]; -} - -_pure_ static const char *target_sub_state_to_string(Unit *u) { - assert(u); - - return target_state_to_string(TARGET(u)->state); -} - -const UnitVTable target_vtable = { - .object_size = sizeof(Target), - - .sections = - "Unit\0" - "Target\0" - "Install\0", - - .load = target_load, - .coldplug = target_coldplug, - - .dump = target_dump, - - .start = target_start, - .stop = target_stop, - - .serialize = target_serialize, - .deserialize_item = target_deserialize_item, - - .active_state = target_active_state, - .sub_state_to_string = target_sub_state_to_string, - - .bus_vtable = bus_target_vtable, - - .status_message_formats = { - .finished_start_job = { - [JOB_DONE] = "Reached target %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Stopped target %s.", - }, - }, -}; diff --git a/src/core/target.h b/src/core/target.h deleted file mode 100644 index 339aea154e..0000000000 --- a/src/core/target.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -typedef struct Target Target; - -struct Target { - Unit meta; - - TargetState state, deserialized_state; -}; - -extern const UnitVTable target_vtable; diff --git a/src/core/timer.c b/src/core/timer.c deleted file mode 100644 index 3206296f09..0000000000 --- a/src/core/timer.c +++ /dev/null @@ -1,859 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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 "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, - [TIMER_WAITING] = UNIT_ACTIVE, - [TIMER_RUNNING] = UNIT_ACTIVE, - [TIMER_ELAPSED] = UNIT_ACTIVE, - [TIMER_FAILED] = UNIT_FAILED -}; - -static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata); - -static void timer_init(Unit *u) { - Timer *t = TIMER(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - 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) { - TimerValue *v; - - assert(t); - - while ((v = t->values)) { - LIST_REMOVE(value, t->values, v); - calendar_spec_free(v->calendar_spec); - free(v); - } -} - -static void timer_done(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - - timer_free_values(t); - - t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source); - t->realtime_event_source = sd_event_source_unref(t->realtime_event_source); - - free(t->stamp_path); -} - -static int timer_verify(Timer *t) { - assert(t); - - if (UNIT(t)->load_state != UNIT_LOADED) - return 0; - - if (!t->values) { - log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); - return -EINVAL; - } - - return 0; -} - -static int timer_add_default_dependencies(Timer *t) { - int r; - TimerValue *v; - - 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; - - if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { - r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); - if (r < 0) - return r; - - LIST_FOREACH(value, v, t->values) { - if (v->base == TIMER_CALENDAR) { - r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true); - if (r < 0) - return r; - break; - } - } - } - - return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); -} - -static int timer_setup_persistent(Timer *t) { - int r; - - assert(t); - - if (!t->persistent) - return 0; - - if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { - - r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers"); - if (r < 0) - return r; - - t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id); - } else { - const char *e; - - e = getenv("XDG_DATA_HOME"); - if (e) - t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL); - else { - - _cleanup_free_ char *h = NULL; - - r = get_home_dir(&h); - if (r < 0) - return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m"); - - t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL); - } - } - - if (!t->stamp_path) - return log_oom(); - - return 0; -} - -static int timer_load(Unit *u) { - Timer *t = TIMER(u); - int r; - - assert(u); - assert(u->load_state == UNIT_STUB); - - r = unit_load_fragment_and_dropin(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_LOADED) { - - if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { - Unit *x; - - r = unit_load_related_unit(u, ".service", &x); - if (r < 0) - return r; - - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); - if (r < 0) - return r; - } - - r = timer_setup_persistent(t); - if (r < 0) - return r; - - r = timer_add_default_dependencies(t); - if (r < 0) - return r; - } - - return timer_verify(t); -} - -static void timer_dump(Unit *u, FILE *f, const char *prefix) { - char buf[FORMAT_TIMESPAN_MAX]; - Timer *t = TIMER(u); - Unit *trigger; - TimerValue *v; - - trigger = UNIT_TRIGGER(u); - - fprintf(f, - "%sTimer State: %s\n" - "%sResult: %s\n" - "%sUnit: %s\n" - "%sPersistent: %s\n" - "%sWakeSystem: %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, yes_no(t->remain_after_elapse)); - - LIST_FOREACH(value, v, t->values) { - - if (v->base == TIMER_CALENDAR) { - _cleanup_free_ char *p = NULL; - - calendar_spec_to_string(v->calendar_spec, &p); - - fprintf(f, - "%s%s: %s\n", - prefix, - timer_base_to_string(v->base), - strna(p)); - } else { - char timespan1[FORMAT_TIMESPAN_MAX]; - - fprintf(f, - "%s%s: %s\n", - prefix, - timer_base_to_string(v->base), - format_timespan(timespan1, sizeof(timespan1), v->value, 0)); - } - } -} - -static void timer_set_state(Timer *t, TimerState state) { - TimerState old_state; - assert(t); - - old_state = t->state; - t->state = state; - - if (state != TIMER_WAITING) { - t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source); - t->realtime_event_source = sd_event_source_unref(t->realtime_event_source); - } - - if (state != old_state) - log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state)); - - unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); -} - -static void timer_enter_waiting(Timer *t, bool initial); - -static int timer_coldplug(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - assert(t->state == TIMER_DEAD); - - 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); - - return 0; -} - -static void timer_enter_dead(Timer *t, TimerResult f) { - assert(t); - - if (f != TIMER_SUCCESS) - t->result = 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; - - if (t <= 0) - return 0; - - a = now(clock_boottime_or_monotonic()); - b = now(CLOCK_MONOTONIC); - - if (t + a > b) - return t + a - b; - else - 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; - Unit *trigger; - int r; - - assert(t); - - trigger = UNIT_TRIGGER(UNIT(t)); - if (!trigger) { - log_unit_error(UNIT(t), "Unit to trigger vanished."); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); - return; - } - - /* If we shall wake the system we use the boottime clock - * rather than the monotonic clock. */ - - ts_realtime = now(CLOCK_REALTIME); - ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC); - t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0; - - LIST_FOREACH(value, v, t->values) { - - if (v->disabled) - continue; - - if (v->base == TIMER_CALENDAR) { - usec_t b; - - /* If we know the last time this was - * triggered, schedule the job based relative - * to that. If we don't just start from - * now. */ - - b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime; - - r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse); - if (r < 0) - continue; - - if (!found_realtime) - t->next_elapse_realtime = v->next_elapse; - else - t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse); - - found_realtime = true; - - } else { - switch (v->base) { - - case TIMER_ACTIVE: - if (state_translation_table[t->state] == UNIT_ACTIVE) - base = UNIT(t)->inactive_exit_timestamp.monotonic; - else - base = ts_monotonic; - break; - - case TIMER_BOOT: - 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 = trigger->inactive_exit_timestamp.monotonic; - - if (base <= 0) - base = t->last_trigger.monotonic; - - if (base <= 0) - continue; - - break; - - case TIMER_UNIT_INACTIVE: - leave_around = true; - base = trigger->inactive_enter_timestamp.monotonic; - - if (base <= 0) - base = t->last_trigger.monotonic; - - if (base <= 0) - continue; - - break; - - default: - assert_not_reached("Unknown timer base"); - } - - if (t->wake_system) - base = monotonic_to_boottime(base); - - v->next_elapse = base + v->value; - - if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) { - /* This is a one time trigger, disable it now */ - v->disabled = true; - continue; - } - - if (!found_monotonic) - t->next_elapse_monotonic_or_boottime = v->next_elapse; - else - t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse); - - found_monotonic = true; - } - } - - if (!found_monotonic && !found_realtime) { - log_unit_debug(UNIT(t), "Timer is elapsed."); - timer_enter_elapsed(t, leave_around); - return; - } - - if (found_monotonic) { - char buf[FORMAT_TIMESPAN_MAX]; - usec_t left; - - 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); - if (r < 0) - goto fail; - - r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT); - if (r < 0) - goto fail; - } else { - - r = sd_event_add_time( - UNIT(t)->manager->event, - &t->monotonic_event_source, - t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC, - t->next_elapse_monotonic_or_boottime, t->accuracy_usec, - timer_dispatch, t); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic"); - } - - } else if (t->monotonic_event_source) { - - r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF); - if (r < 0) - goto fail; - } - - 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) { - r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime); - if (r < 0) - goto fail; - - r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT); - if (r < 0) - goto fail; - } else { - r = sd_event_add_time( - UNIT(t)->manager->event, - &t->realtime_event_source, - t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME, - t->next_elapse_realtime, t->accuracy_usec, - timer_dispatch, t); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime"); - } - - } else if (t->realtime_event_source) { - - r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF); - if (r < 0) - goto fail; - } - - timer_set_state(t, TIMER_WAITING); - return; - -fail: - log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m"); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); -} - -static void timer_enter_running(Timer *t) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Unit *trigger; - int r; - - assert(t); - - /* Don't start job if we are supposed to go down */ - if (unit_stop_pending(UNIT(t))) - return; - - trigger = UNIT_TRIGGER(UNIT(t)); - if (!trigger) { - log_unit_error(UNIT(t), "Unit to trigger vanished."); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); - return; - } - - r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, 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, MODE_INVALID); - - timer_set_state(t, TIMER_RUNNING); - return; - -fail: - log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r)); - timer_enter_dead(t, TIMER_FAILURE_RESOURCES); -} - -static int timer_start(Unit *u) { - Timer *t = TIMER(u); - TimerValue *v; - Unit *trigger; - int r; - - assert(t); - assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); - - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } - - r = unit_start_limit_test(u); - if (r < 0) { - timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); - return r; - } - - t->last_trigger = DUAL_TIMESTAMP_NULL; - - /* Reenable all timers that depend on unit activation time */ - LIST_FOREACH(value, v, t->values) - if (v->base == TIMER_ACTIVE) - v->disabled = false; - - if (t->stamp_path) { - struct stat st; - - if (stat(t->stamp_path, &st) >= 0) - t->last_trigger.realtime = timespec_load(&st.st_atim); - else if (errno == ENOENT) - /* The timer has never run before, - * make sure a stamp file exists. - */ - touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); - } - - t->result = TIMER_SUCCESS; - timer_enter_waiting(t, true); - return 1; -} - -static int timer_stop(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED); - - timer_enter_dead(t, TIMER_SUCCESS); - return 1; -} - -static int timer_serialize(Unit *u, FILE *f, FDSet *fds) { - Timer *t = TIMER(u); - - assert(u); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", timer_state_to_string(t->state)); - unit_serialize_item(u, f, "result", timer_result_to_string(t->result)); - - if (t->last_trigger.realtime > 0) - unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime); - - if (t->last_trigger.monotonic > 0) - unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic); - - return 0; -} - -static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Timer *t = TIMER(u); - int r; - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - TimerState state; - - state = timer_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - t->deserialized_state = state; - } else if (streq(key, "result")) { - TimerResult f; - - f = timer_result_from_string(value); - if (f < 0) - log_unit_debug(u, "Failed to parse result value: %s", value); - else if (f != TIMER_SUCCESS) - t->result = f; - } else if (streq(key, "last-trigger-realtime")) { - - r = safe_atou64(value, &t->last_trigger.realtime); - if (r < 0) - log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value); - - } else if (streq(key, "last-trigger-monotonic")) { - - r = safe_atou64(value, &t->last_trigger.monotonic); - if (r < 0) - log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value); - - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState timer_active_state(Unit *u) { - assert(u); - - return state_translation_table[TIMER(u)->state]; -} - -_pure_ static const char *timer_sub_state_to_string(Unit *u) { - assert(u); - - return timer_state_to_string(TIMER(u)->state); -} - -static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) { - Timer *t = TIMER(userdata); - - assert(t); - - if (t->state != TIMER_WAITING) - return 0; - - log_unit_debug(UNIT(t), "Timer elapsed."); - timer_enter_running(t); - return 0; -} - -static void timer_trigger_notify(Unit *u, Unit *other) { - Timer *t = TIMER(u); - TimerValue *v; - - assert(u); - assert(other); - - if (other->load_state != UNIT_LOADED) - return; - - /* Reenable all timers that depend on unit state */ - LIST_FOREACH(value, v, t->values) - if (v->base == TIMER_UNIT_ACTIVE || - v->base == TIMER_UNIT_INACTIVE) - v->disabled = false; - - switch (t->state) { - - case TIMER_WAITING: - case TIMER_ELAPSED: - - /* Recalculate sleep time */ - timer_enter_waiting(t, false); - break; - - case TIMER_RUNNING: - - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { - log_unit_debug(UNIT(t), "Got notified about unit deactivation."); - timer_enter_waiting(t, false); - } - break; - - case TIMER_DEAD: - case TIMER_FAILED: - break; - - default: - assert_not_reached("Unknown timer state"); - } -} - -static void timer_reset_failed(Unit *u) { - Timer *t = TIMER(u); - - assert(t); - - if (t->state == TIMER_FAILED) - timer_set_state(t, TIMER_DEAD); - - t->result = TIMER_SUCCESS; -} - -static void timer_time_change(Unit *u) { - Timer *t = TIMER(u); - - assert(u); - - if (t->state != TIMER_WAITING) - return; - - log_unit_debug(u, "Time change, recalculating next elapse."); - timer_enter_waiting(t, false); -} - -static const char* const timer_base_table[_TIMER_BASE_MAX] = { - [TIMER_ACTIVE] = "OnActiveSec", - [TIMER_BOOT] = "OnBootSec", - [TIMER_STARTUP] = "OnStartupSec", - [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec", - [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec", - [TIMER_CALENDAR] = "OnCalendar" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); - -static const char* const timer_result_table[_TIMER_RESULT_MAX] = { - [TIMER_SUCCESS] = "success", - [TIMER_FAILURE_RESOURCES] = "resources", - [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit", -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); - -const UnitVTable timer_vtable = { - .object_size = sizeof(Timer), - - .sections = - "Unit\0" - "Timer\0" - "Install\0", - .private_section = "Timer", - - .init = timer_init, - .done = timer_done, - .load = timer_load, - - .coldplug = timer_coldplug, - - .dump = timer_dump, - - .start = timer_start, - .stop = timer_stop, - - .serialize = timer_serialize, - .deserialize_item = timer_deserialize_item, - - .active_state = timer_active_state, - .sub_state_to_string = timer_sub_state_to_string, - - .trigger_notify = timer_trigger_notify, - - .reset_failed = timer_reset_failed, - .time_change = timer_time_change, - - .bus_vtable = bus_timer_vtable, - .bus_set_property = bus_timer_set_property, - - .can_transient = true, -}; diff --git a/src/core/timer.h b/src/core/timer.h deleted file mode 100644 index 9c4b64f898..0000000000 --- a/src/core/timer.h +++ /dev/null @@ -1,89 +0,0 @@ -#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 . -***/ - -typedef struct Timer Timer; - -#include "calendarspec.h" - -typedef enum TimerBase { - TIMER_ACTIVE, - TIMER_BOOT, - TIMER_STARTUP, - TIMER_UNIT_ACTIVE, - TIMER_UNIT_INACTIVE, - TIMER_CALENDAR, - _TIMER_BASE_MAX, - _TIMER_BASE_INVALID = -1 -} TimerBase; - -typedef struct TimerValue { - TimerBase base; - bool disabled; - - usec_t value; /* only for monotonic events */ - CalendarSpec *calendar_spec; /* only for calendar events */ - usec_t next_elapse; - - LIST_FIELDS(struct TimerValue, value); -} TimerValue; - -typedef enum TimerResult { - TIMER_SUCCESS, - TIMER_FAILURE_RESOURCES, - TIMER_FAILURE_START_LIMIT_HIT, - _TIMER_RESULT_MAX, - _TIMER_RESULT_INVALID = -1 -} TimerResult; - -struct Timer { - Unit meta; - - usec_t accuracy_usec; - usec_t random_usec; - - LIST_HEAD(TimerValue, values); - usec_t next_elapse_realtime; - usec_t next_elapse_monotonic_or_boottime; - dual_timestamp last_trigger; - - TimerState state, deserialized_state; - - sd_event_source *monotonic_event_source; - sd_event_source *realtime_event_source; - - TimerResult result; - - bool persistent; - bool wake_system; - bool remain_after_elapse; - - char *stamp_path; -}; - -void timer_free_values(Timer *t); - -extern const UnitVTable timer_vtable; - -const char *timer_base_to_string(TimerBase i) _const_; -TimerBase timer_base_from_string(const char *s) _pure_; - -const char* timer_result_to_string(TimerResult i) _const_; -TimerResult timer_result_from_string(const char *s) _pure_; diff --git a/src/core/transaction.c b/src/core/transaction.c deleted file mode 100644 index 8370b864fb..0000000000 --- a/src/core/transaction.c +++ /dev/null @@ -1,1102 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "terminal-util.h" -#include "transaction.h" -#include "dbus-unit.h" - -static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); - -static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) { - assert(tr); - assert(j); - - /* Deletes one job from the transaction */ - - transaction_unlink_job(tr, j, delete_dependencies); - - job_free(j); -} - -static void transaction_delete_unit(Transaction *tr, Unit *u) { - Job *j; - - /* Deletes all jobs associated with a certain unit from the - * transaction */ - - while ((j = hashmap_get(tr->jobs, u))) - transaction_delete_job(tr, j, true); -} - -void transaction_abort(Transaction *tr) { - Job *j; - - assert(tr); - - while ((j = hashmap_first(tr->jobs))) - transaction_delete_job(tr, j, false); - - assert(hashmap_isempty(tr->jobs)); -} - -static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) { - JobDependency *l; - - /* A recursive sweep through the graph that marks all units - * that matter to the anchor job, i.e. are directly or - * indirectly a dependency of the anchor job via paths that - * are fully marked as mattering. */ - - j->matters_to_anchor = true; - j->generation = generation; - - LIST_FOREACH(subject, l, j->subject_list) { - - /* This link does not matter */ - if (!l->matters) - continue; - - /* This unit has already been marked */ - if (l->object->generation == generation) - continue; - - transaction_find_jobs_that_matter_to_anchor(l->object, generation); - } -} - -static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) { - JobDependency *l, *last; - - assert(j); - assert(other); - assert(j->unit == other->unit); - assert(!j->installed); - - /* Merges 'other' into 'j' and then deletes 'other'. */ - - j->type = t; - j->state = JOB_WAITING; - 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 */ - last = NULL; - LIST_FOREACH(subject, l, other->subject_list) { - assert(l->subject == other); - l->subject = j; - last = l; - } - - /* Merge both lists */ - if (last) { - last->subject_next = j->subject_list; - if (j->subject_list) - j->subject_list->subject_prev = last; - j->subject_list = other->subject_list; - } - - /* Patch us in as new owner of the JobDependency objects */ - last = NULL; - LIST_FOREACH(object, l, other->object_list) { - assert(l->object == other); - l->object = j; - last = l; - } - - /* Merge both lists */ - if (last) { - last->object_next = j->object_list; - if (j->object_list) - j->object_list->object_prev = last; - j->object_list = other->object_list; - } - - /* Kill the other job */ - other->subject_list = NULL; - other->object_list = NULL; - transaction_delete_job(tr, other, true); -} - -_pure_ static bool job_is_conflicted_by(Job *j) { - JobDependency *l; - - assert(j); - - /* Returns true if this job is pulled in by a least one - * ConflictedBy dependency. */ - - LIST_FOREACH(object, l, j->object_list) - if (l->conflicts) - return true; - - return false; -} - -static int delete_one_unmergeable_job(Transaction *tr, Job *j) { - Job *k; - - assert(j); - - /* Tries to delete one item in the linked list - * j->transaction_next->transaction_next->... that conflicts - * with another one, in an attempt to make an inconsistent - * transaction work. */ - - /* We rely here on the fact that if a merged with b does not - * merge with c, either a or b merge with c neither */ - LIST_FOREACH(transaction, j, j) - LIST_FOREACH(transaction, k, j->transaction_next) { - Job *d; - - /* Is this one mergeable? Then skip it */ - if (job_type_is_mergeable(j->type, k->type)) - continue; - - /* Ok, we found two that conflict, let's see if we can - * drop one of them */ - if (!j->matters_to_anchor && !k->matters_to_anchor) { - - /* Both jobs don't matter, so let's - * find the one that is smarter to - * remove. Let's think positive and - * rather remove stops then starts -- - * except if something is being - * stopped because it is conflicted by - * another unit in which case we - * rather remove the start. */ - - log_unit_debug(j->unit, - "Looking at job %s/%s conflicted_by=%s", - j->unit->id, job_type_to_string(j->type), - yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); - log_unit_debug(k->unit, - "Looking at job %s/%s conflicted_by=%s", - k->unit->id, job_type_to_string(k->type), - yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); - - if (j->type == JOB_STOP) { - - if (job_is_conflicted_by(j)) - d = k; - else - d = j; - - } else if (k->type == JOB_STOP) { - - if (job_is_conflicted_by(k)) - d = j; - else - d = k; - } else - d = j; - - } else if (!j->matters_to_anchor) - d = j; - else if (!k->matters_to_anchor) - d = k; - else - return -ENOEXEC; - - /* Ok, we can drop one, so let's do so. */ - log_unit_debug(d->unit, - "Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s", - j->unit->id, job_type_to_string(j->type), - k->unit->id, job_type_to_string(k->type), - d->unit->id, job_type_to_string(d->type)); - transaction_delete_job(tr, d, true); - return 0; - } - - return -EINVAL; -} - -static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { - Job *j; - Iterator i; - int r; - - assert(tr); - - /* First step, check whether any of the jobs for one specific - * task conflict. If so, try to drop one of them. */ - HASHMAP_FOREACH(j, tr->jobs, i) { - JobType t; - Job *k; - - t = j->type; - LIST_FOREACH(transaction, k, j->transaction_next) { - if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0) - continue; - - /* OK, we could not merge all jobs for this - * action. Let's see if we can get rid of one - * of them */ - - r = delete_one_unmergeable_job(tr, j); - if (r >= 0) - /* Ok, we managed to drop one, now - * let's ask our callers to call us - * again after garbage collecting */ - return -EAGAIN; - - /* We couldn't merge anything. Failure */ - return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, - "Transaction contains conflicting jobs '%s' and '%s' for %s. " - "Probably contradicting requirement dependencies configured.", - job_type_to_string(t), - job_type_to_string(k->type), - k->unit->id); - } - } - - /* Second step, merge the jobs. */ - HASHMAP_FOREACH(j, tr->jobs, i) { - JobType t = j->type; - Job *k; - - /* Merge all transaction jobs for j->unit */ - LIST_FOREACH(transaction, k, j->transaction_next) - assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0); - - while ((k = j->transaction_next)) { - if (tr->anchor_job == k) { - transaction_merge_and_delete_job(tr, k, j, t); - j = k; - } else - transaction_merge_and_delete_job(tr, j, k, t); - } - - assert(!j->transaction_next); - assert(!j->transaction_prev); - } - - return 0; -} - -static void transaction_drop_redundant(Transaction *tr) { - Job *j; - Iterator i; - - /* Goes through the transaction and removes all jobs of the units - * whose jobs are all noops. If not all of a unit's jobs are - * redundant, they are kept. */ - - assert(tr); - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - Job *k; - - LIST_FOREACH(transaction, k, j) { - - if (tr->anchor_job == k || - !job_type_is_redundant(k->type, unit_active_state(k->unit)) || - (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) - goto next_unit; - } - - /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */ - transaction_delete_job(tr, j, false); - goto rescan; - next_unit:; - } -} - -_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { - assert(u); - assert(!j->transaction_prev); - - /* Checks whether at least one of the jobs for this unit - * matters to the anchor. */ - - LIST_FOREACH(transaction, j, j) - if (j->matters_to_anchor) - return true; - - return false; -} - -static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) { - Iterator i; - Unit *u; - int r; - - assert(tr); - assert(j); - assert(!j->transaction_prev); - - /* Does a recursive sweep through the ordering graph, looking - * for a cycle. If we find a cycle we try to break it. */ - - /* Have we seen this before? */ - if (j->generation == generation) { - Job *k, *delete; - - /* If the marker is NULL we have been here already and - * decided the job was loop-free from here. Hence - * shortcut things and return right-away. */ - if (!j->marker) - return 0; - - /* So, the marker is not NULL and we already have been - * here. We have a cycle. Let's try to break it. We go - * backwards in our path and try to find a suitable - * job to remove. We use the marker to find our way - * back, since smart how we are we stored our way back - * in there. */ - log_unit_warning(j->unit, - "Found ordering cycle on %s/%s", - j->unit->id, job_type_to_string(j->type)); - - delete = NULL; - for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) { - - /* logging for j not k here to provide consistent narrative */ - log_unit_warning(j->unit, - "Found dependency on %s/%s", - k->unit->id, job_type_to_string(k->type)); - - if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k)) - /* Ok, we can drop this one, so let's - * do so. */ - delete = k; - - /* Check if this in fact was the beginning of - * the cycle */ - if (k == j) - break; - } - - - if (delete) { - const char *status; - /* logging for j not k here to provide consistent narrative */ - log_unit_warning(j->unit, - "Breaking ordering cycle by deleting job %s/%s", - delete->unit->id, job_type_to_string(delete->type)); - log_unit_error(delete->unit, - "Job %s/%s deleted to break ordering cycle starting with %s/%s", - delete->unit->id, job_type_to_string(delete->type), - j->unit->id, job_type_to_string(j->type)); - - if (log_get_show_color()) - status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL; - else - status = " SKIP "; - - unit_status_printf(delete->unit, status, - "Ordering cycle found, skipping %s"); - transaction_delete_unit(tr, delete->unit); - return -EAGAIN; - } - - log_error("Unable to break cycle"); - - return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, - "Transaction order is cyclic. See system logs for details."); - } - - /* Make the marker point to where we come from, so that we can - * find our way backwards if we want to break a cycle. We use - * a special marker for the beginning: we point to - * ourselves. */ - j->marker = from ? from : j; - j->generation = generation; - - /* We assume that the dependencies are bidirectional, and - * hence can ignore UNIT_AFTER */ - SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) { - Job *o; - - /* Is there a job for this unit? */ - o = hashmap_get(tr->jobs, u); - if (!o) { - /* Ok, there is no job for this in the - * transaction, but maybe there is already one - * running? */ - o = u->job; - if (!o) - continue; - } - - r = transaction_verify_order_one(tr, o, j, generation, e); - if (r < 0) - return r; - } - - /* Ok, let's backtrack, and remember that this entry is not on - * our path anymore. */ - j->marker = NULL; - - return 0; -} - -static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) { - Job *j; - int r; - Iterator i; - unsigned g; - - assert(tr); - assert(generation); - - /* Check if the ordering graph is cyclic. If it is, try to fix - * that up by dropping one of the jobs. */ - - g = (*generation)++; - - HASHMAP_FOREACH(j, tr->jobs, i) { - r = transaction_verify_order_one(tr, j, NULL, g, e); - if (r < 0) - return r; - } - - return 0; -} - -static void transaction_collect_garbage(Transaction *tr) { - Iterator i; - Job *j; - - assert(tr); - - /* Drop jobs that are not required by any other job */ - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - if (tr->anchor_job == j || j->object_list) { - /* log_debug("Keeping job %s/%s because of %s/%s", */ - /* j->unit->id, job_type_to_string(j->type), */ - /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */ - /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */ - continue; - } - - /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */ - transaction_delete_job(tr, j, true); - goto rescan; - } -} - -static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) { - Iterator i; - Job *j; - - assert(tr); - - /* Checks whether applying this transaction means that - * existing jobs would be replaced */ - - HASHMAP_FOREACH(j, tr->jobs, i) { - - /* Assume merged */ - assert(!j->transaction_prev); - assert(!j->transaction_next); - - if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) && - job_type_is_conflicting(j->unit->job->type, j->type)) - return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, - "Transaction is destructive."); - } - - return 0; -} - -static void transaction_minimize_impact(Transaction *tr) { - Job *j; - Iterator i; - - assert(tr); - - /* Drops all unnecessary jobs that reverse already active jobs - * or that stop a running service. */ - -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - LIST_FOREACH(transaction, j, j) { - bool stops_running_service, changes_existing_job; - - /* If it matters, we shouldn't drop it */ - if (j->matters_to_anchor) - continue; - - /* Would this stop a running service? - * Would this change an existing job? - * If so, let's drop this entry */ - - stops_running_service = - j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit)); - - changes_existing_job = - j->unit->job && - job_type_is_conflicting(j->type, j->unit->job->type); - - if (!stops_running_service && !changes_existing_job) - continue; - - if (stops_running_service) - log_unit_debug(j->unit, - "%s/%s would stop a running service.", - j->unit->id, job_type_to_string(j->type)); - - if (changes_existing_job) - log_unit_debug(j->unit, - "%s/%s would change existing job.", - j->unit->id, job_type_to_string(j->type)); - - /* Ok, let's get rid of this */ - log_unit_debug(j->unit, - "Deleting %s/%s to minimize impact.", - j->unit->id, job_type_to_string(j->type)); - - transaction_delete_job(tr, j, true); - goto rescan; - } - } -} - -static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { - Iterator i; - Job *j; - int r; - - /* Moves the transaction jobs to the set of active jobs */ - - if (mode == JOB_ISOLATE || mode == JOB_FLUSH) { - - /* When isolating first kill all installed jobs which - * aren't part of the new transaction */ - HASHMAP_FOREACH(j, m->jobs, i) { - assert(j->installed); - - if (j->unit->ignore_on_isolate) - continue; - - if (hashmap_get(tr->jobs, j->unit)) - continue; - - /* Not invalidating recursively. Avoids triggering - * OnFailure= actions of dependent jobs. Also avoids - * invalidating our iterator. */ - job_finish_and_invalidate(j, JOB_CANCELED, false, false); - } - } - - HASHMAP_FOREACH(j, tr->jobs, i) { - /* Assume merged */ - assert(!j->transaction_prev); - assert(!j->transaction_next); - - r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j); - if (r < 0) - goto rollback; - } - - while ((j = hashmap_steal_first(tr->jobs))) { - Job *installed_job; - - /* Clean the job dependencies */ - transaction_unlink_job(tr, j, false); - - installed_job = job_install(j); - if (installed_job != j) { - /* j has been merged into a previously installed job */ - if (tr->anchor_job == j) - tr->anchor_job = installed_job; - hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); - job_free(j); - j = installed_job; - } - - job_add_to_run_queue(j); - job_add_to_dbus_queue(j); - job_start_timer(j); - job_shutdown_magic(j); - } - - return 0; - -rollback: - - HASHMAP_FOREACH(j, tr->jobs, i) - hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); - - return r; -} - -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e) { - Iterator i; - Job *j; - int r; - unsigned generation = 1; - - assert(tr); - - /* This applies the changes recorded in tr->jobs to - * the actual list of jobs, if possible. */ - - /* Reset the generation counter of all installed jobs. The detection of cycles - * looks at installed jobs. If they had a non-zero generation from some previous - * walk of the graph, the algorithm would break. */ - HASHMAP_FOREACH(j, m->jobs, i) - j->generation = 0; - - /* First step: figure out which jobs matter */ - transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++); - - /* Second step: Try not to stop any running services if - * we don't have to. Don't try to reverse running - * jobs if we don't have to. */ - if (mode == JOB_FAIL) - transaction_minimize_impact(tr); - - /* Third step: Drop redundant jobs */ - transaction_drop_redundant(tr); - - for (;;) { - /* Fourth step: Let's remove unneeded jobs that might - * be lurking. */ - if (mode != JOB_ISOLATE) - transaction_collect_garbage(tr); - - /* Fifth step: verify order makes sense and correct - * cycles if necessary and possible */ - r = transaction_verify_order(tr, &generation, e); - if (r >= 0) - break; - - if (r != -EAGAIN) { - log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r)); - return r; - } - - /* Let's see if the resulting transaction ordering - * graph is still cyclic... */ - } - - for (;;) { - /* Sixth step: let's drop unmergeable entries if - * necessary and possible, merge entries we can - * merge */ - r = transaction_merge_jobs(tr, e); - if (r >= 0) - break; - - if (r != -EAGAIN) { - log_warning("Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r)); - return r; - } - - /* Seventh step: an entry got dropped, let's garbage - * collect its dependencies. */ - if (mode != JOB_ISOLATE) - transaction_collect_garbage(tr); - - /* Let's see if the resulting transaction still has - * unmergeable entries ... */ - } - - /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */ - transaction_drop_redundant(tr); - - /* Ninth step: check whether we can actually apply this */ - r = transaction_is_destructive(tr, mode, e); - if (r < 0) { - log_notice("Requested transaction contradicts existing jobs: %s", bus_error_message(e, r)); - return r; - } - - /* Tenth step: apply changes */ - r = transaction_apply(tr, m, mode); - if (r < 0) - return log_warning_errno(r, "Failed to apply transaction: %m"); - - assert(hashmap_isempty(tr->jobs)); - - if (!hashmap_isempty(m->jobs)) { - /* Are there any jobs now? Then make sure we have the - * idle pipe around. We don't really care too much - * whether this works or not, as the idle pipe is a - * feature for cosmetics, not actually useful for - * anything beyond that. */ - - if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 && - m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) { - (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); - (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); - } - } - - return 0; -} - -static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) { - Job *j, *f; - - assert(tr); - assert(unit); - - /* Looks for an existing prospective job and returns that. If - * it doesn't exist it is created and added to the prospective - * jobs list. */ - - f = hashmap_get(tr->jobs, unit); - - LIST_FOREACH(transaction, j, f) { - assert(j->unit == unit); - - if (j->type == type) { - if (is_new) - *is_new = false; - return j; - } - } - - j = job_new(unit, type); - if (!j) - return NULL; - - j->generation = 0; - j->marker = NULL; - j->matters_to_anchor = false; - j->irreversible = tr->irreversible; - - LIST_PREPEND(transaction, f, j); - - if (hashmap_replace(tr->jobs, unit, f) < 0) { - LIST_REMOVE(transaction, f, j); - job_free(j); - return NULL; - } - - if (is_new) - *is_new = true; - - /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */ - - return j; -} - -static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) { - assert(tr); - assert(j); - - if (j->transaction_prev) - j->transaction_prev->transaction_next = j->transaction_next; - else if (j->transaction_next) - hashmap_replace(tr->jobs, j->unit, j->transaction_next); - else - hashmap_remove_value(tr->jobs, j->unit, j); - - if (j->transaction_next) - j->transaction_next->transaction_prev = j->transaction_prev; - - j->transaction_prev = j->transaction_next = NULL; - - while (j->subject_list) - job_dependency_free(j->subject_list); - - while (j->object_list) { - Job *other = j->object_list->matters ? j->object_list->subject : NULL; - - job_dependency_free(j->object_list); - - if (other && delete_dependencies) { - log_unit_debug(other->unit, - "Deleting job %s/%s as dependency of job %s/%s", - other->unit->id, job_type_to_string(other->type), - j->unit->id, job_type_to_string(j->type)); - transaction_delete_job(tr, other, delete_dependencies); - } - } -} - -int transaction_add_job_and_dependencies( - Transaction *tr, - JobType type, - Unit *unit, - Job *by, - bool matters, - bool conflicts, - bool ignore_requirements, - bool ignore_order, - sd_bus_error *e) { - Job *ret; - Iterator i; - Unit *dep; - int r; - bool is_new; - - assert(tr); - assert(type < _JOB_TYPE_MAX); - assert(type < _JOB_TYPE_MAX_IN_TRANSACTION); - assert(unit); - - /* Before adding jobs for this unit, let's ensure that its state has been loaded - * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). - * This way, we "recursively" coldplug units, ensuring that we do not look at state of - * not-yet-coldplugged units. */ - if (MANAGER_IS_RELOADING(unit->manager)) - unit_coldplug(unit); - - /* log_debug("Pulling in %s/%s from %s/%s", */ - /* unit->id, job_type_to_string(type), */ - /* by ? by->unit->id : "NA", */ - /* by ? job_type_to_string(by->type) : "NA"); */ - - if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED)) - return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); - - if (type != JOB_STOP) { - r = bus_unit_check_load_state(unit, e); - if (r < 0) - return r; - } - - 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.", - job_type_to_string(type), unit->id); - - - /* First add the job. */ - ret = transaction_add_one_job(tr, type, unit, &is_new); - if (!ret) - return -ENOMEM; - - ret->ignore_order = ret->ignore_order || ignore_order; - - /* Then, add a link to the job. */ - if (by) { - if (!job_dependency_new(by, ret, matters, conflicts)) - return -ENOMEM; - } else { - /* If the job has no parent job, it is the anchor job. */ - assert(!tr->anchor_job); - tr->anchor_job = ret; - } - - if (is_new && !ignore_requirements && type != JOB_NOP) { - Set *following; - - /* If we are following some other unit, make sure we - * 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, 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); - } - } - - set_free(following); - } - - /* 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, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) /* job type not applicable */ - goto fail; - - sd_bus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) /* job type not applicable */ - goto fail; - - 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, ignore_order, e); - if (r < 0) { - /* unit masked, job type not applicable and unit not found are not considered as errors. */ - log_unit_full(dep, - IN_SET(r, -ERFKILL, -EBADR, -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, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) /* job type not applicable */ - goto fail; - - 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, true, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) /* job type not applicable */ - goto fail; - - sd_bus_error_free(e); - } - } - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e); - if (r < 0) { - log_unit_warning(dep, - "Cannot add dependency job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - - } - - if (type == JOB_STOP || type == JOB_RESTART) { - static const UnitDependency propagate_deps[] = { - UNIT_REQUIRED_BY, - UNIT_REQUISITE_OF, - UNIT_BOUND_BY, - UNIT_CONSISTS_OF, - }; - - JobType ptype; - unsigned j; - - /* We propagate STOP as STOP, but RESTART only - * as TRY_RESTART, in order not to start - * dependencies that are not around. */ - ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type; - - for (j = 0; j < ELEMENTSOF(propagate_deps); j++) - SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) { - JobType nt; - - nt = job_type_collapse(ptype, dep); - if (nt == JOB_NOP) - continue; - - r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e); - if (r < 0) { - if (r != -EBADR) /* job type not applicable */ - goto fail; - - sd_bus_error_free(e); - } - } - } - - if (type == JOB_RELOAD) { - - SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { - JobType nt; - - nt = job_type_collapse(JOB_TRY_RELOAD, dep); - if (nt == JOB_NOP) - continue; - - r = transaction_add_job_and_dependencies(tr, nt, dep, ret, false, false, false, ignore_order, e); - if (r < 0) { - log_unit_warning(dep, - "Cannot add dependency reload job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - } - - /* JOB_VERIFY_STARTED require no dependency handling */ - } - - return 0; - -fail: - return r; -} - -int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { - Iterator i; - Unit *u; - char *k; - int r; - - assert(tr); - assert(m); - - HASHMAP_FOREACH_KEY(u, k, m->units, i) { - - /* ignore aliases */ - if (u->id != k) - continue; - - if (u->ignore_on_isolate) - continue; - - /* No need to stop inactive jobs */ - if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job) - continue; - - /* Is there already something listed for this? */ - if (hashmap_get(tr->jobs, u)) - continue; - - 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"); - } - - return 0; -} - -Transaction *transaction_new(bool irreversible) { - Transaction *tr; - - tr = new0(Transaction, 1); - if (!tr) - return NULL; - - tr->jobs = hashmap_new(NULL); - if (!tr->jobs) { - free(tr); - return NULL; - } - - tr->irreversible = irreversible; - - return tr; -} - -void transaction_free(Transaction *tr) { - assert(hashmap_isempty(tr->jobs)); - hashmap_free(tr->jobs); - free(tr); -} diff --git a/src/core/transaction.h b/src/core/transaction.h deleted file mode 100644 index 6a3f927b0f..0000000000 --- a/src/core/transaction.h +++ /dev/null @@ -1,51 +0,0 @@ -#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 . -***/ - -typedef struct Transaction Transaction; - -#include "hashmap.h" -#include "job.h" -#include "manager.h" -#include "unit.h" - -struct Transaction { - /* Jobs to be added */ - Hashmap *jobs; /* Unit object => Job object list 1:1 */ - Job *anchor_job; /* the job the user asked for */ - bool irreversible; -}; - -Transaction *transaction_new(bool irreversible); -void transaction_free(Transaction *tr); - -int transaction_add_job_and_dependencies( - Transaction *tr, - JobType type, - Unit *unit, - Job *by, - bool matters, - bool conflicts, - bool ignore_requirements, - bool ignore_order, - sd_bus_error *e); -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e); -int transaction_add_isolate_jobs(Transaction *tr, Manager *m); -void transaction_abort(Transaction *tr); diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in deleted file mode 100644 index 0d8c303136..0000000000 --- a/src/core/triggers.systemd.in +++ /dev/null @@ -1,66 +0,0 @@ -# -*- 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 . - -# The contents of this are an example to be copied into systemd.spec. -# -# Minimum rpm version supported: 4.13.0 - -%transfiletriggerin -P 900900 -p -- @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 -- @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 -- @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 deleted file mode 100644 index c21a2be54e..0000000000 --- a/src/core/umount.c +++ /dev/null @@ -1,614 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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 "path-util.h" -#include "string-util.h" -#include "udev-util.h" -#include "umount.h" -#include "util.h" -#include "virt.h" - -typedef struct MountPoint { - char *path; - char *options; - dev_t devnum; - LIST_FIELDS(struct MountPoint, mount_point); -} MountPoint; - -static void mount_point_free(MountPoint **head, MountPoint *m) { - assert(head); - assert(m); - - LIST_REMOVE(mount_point, *head, m); - - free(m->path); - free(m); -} - -static void mount_points_list_free(MountPoint **head) { - assert(head); - - while (*head) - mount_point_free(head, *head); -} - -static int mount_points_list_get(MountPoint **head) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - unsigned int i; - int r; - - assert(head); - - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; - - for (i = 1;; i++) { - _cleanup_free_ char *path = NULL, *options = NULL; - char *p = NULL; - MountPoint *m; - 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 flags */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%*s " /* (9) file system type */ - "%*s" /* (10) mount source */ - "%ms" /* (11) mount options */ - "%*[^\n]", /* some rubbish at the end */ - &path, &options); - if (k != 2) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - continue; - } - - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; - - /* Ignore mount points we can't unmount because they - * are API or because we are keeping them open (like - * /dev/console). Also, ignore all mounts below API - * file systems, since they are likely virtual too, - * and hence not worth spending time on. Also, in - * unprivileged containers we might lack the rights to - * unmount these things, hence don't bother. */ - if (mount_point_is_api(p) || - mount_point_ignore(p) || - path_startswith(p, "/dev") || - path_startswith(p, "/sys") || - path_startswith(p, "/proc")) { - free(p); - continue; - } - - m = new0(MountPoint, 1); - if (!m) { - free(p); - return -ENOMEM; - } - - m->path = p; - m->options = options; - options = NULL; - - LIST_PREPEND(mount_point, *head, m); - } - - return 0; -} - -static int swap_list_get(MountPoint **head) { - _cleanup_fclose_ FILE *proc_swaps = NULL; - unsigned int i; - int r; - - assert(head); - - proc_swaps = fopen("/proc/swaps", "re"); - if (!proc_swaps) - return (errno == ENOENT) ? 0 : -errno; - - (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); - - for (i = 2;; i++) { - MountPoint *swap; - char *dev = NULL, *d; - int k; - - k = fscanf(proc_swaps, - "%ms " /* device/file */ - "%*s " /* type of swap */ - "%*s " /* swap size */ - "%*s " /* used */ - "%*s\n", /* priority */ - &dev); - - if (k != 1) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u.", i); - free(dev); - continue; - } - - if (endswith(dev, " (deleted)")) { - free(dev); - continue; - } - - r = cunescape(dev, UNESCAPE_RELAX, &d); - free(dev); - if (r < 0) - return r; - - swap = new0(MountPoint, 1); - if (!swap) { - free(d); - return -ENOMEM; - } - - swap->path = d; - LIST_PREPEND(mount_point, *head, swap); - } - - return 0; -} - -static int loopback_list_get(MountPoint **head) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; - int r; - - assert(head); - - udev = udev_new(); - if (!udev) - return -ENOMEM; - - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_sysname(e, "loop*"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - MountPoint *lb; - _cleanup_udev_device_unref_ struct udev_device *d; - char *loop; - const char *dn; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - dn = udev_device_get_devnode(d); - if (!dn) - continue; - - loop = strdup(dn); - if (!loop) - return -ENOMEM; - - lb = new0(MountPoint, 1); - if (!lb) { - free(loop); - return -ENOMEM; - } - - lb->path = loop; - LIST_PREPEND(mount_point, *head, lb); - } - - return 0; -} - -static int dm_list_get(MountPoint **head) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; - int r; - - assert(head); - - udev = udev_new(); - if (!udev) - return -ENOMEM; - - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_sysname(e, "dm-*"); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - MountPoint *m; - _cleanup_udev_device_unref_ struct udev_device *d; - dev_t devnum; - char *node; - const char *dn; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - devnum = udev_device_get_devnum(d); - dn = udev_device_get_devnode(d); - if (major(devnum) == 0 || !dn) - continue; - - node = strdup(dn); - if (!node) - return -ENOMEM; - - m = new(MountPoint, 1); - if (!m) { - free(node); - return -ENOMEM; - } - - m->path = node; - m->devnum = devnum; - LIST_PREPEND(mount_point, *head, m); - } - - return 0; -} - -static int delete_loopback(const char *device) { - _cleanup_close_ int fd = -1; - int r; - - fd = open(device, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return errno == ENOENT ? 0 : -errno; - - r = ioctl(fd, LOOP_CLR_FD, 0); - if (r >= 0) - return 1; - - /* ENXIO: not bound, so no error */ - if (errno == ENXIO) - return 0; - - return -errno; -} - -static int delete_dm(dev_t devnum) { - _cleanup_close_ int fd = -1; - int r; - struct dm_ioctl dm = { - .version = {DM_VERSION_MAJOR, - DM_VERSION_MINOR, - DM_VERSION_PATCHLEVEL}, - .data_size = sizeof(dm), - .dev = devnum, - }; - - assert(major(devnum) != 0); - - fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; - - r = ioctl(fd, DM_DEV_REMOVE, &dm); - return r >= 0 ? 0 : -errno; -} - -static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - - /* If we are in a container, don't attempt to - read-only mount anything as that brings no real - 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. - * - * Mount points can be stacked. If a mount - * point is stacked below / or /usr, we - * cannot umount or remount it directly, - * since there is no way to refer to the - * underlying mount. There's nothing we can do - * about it for the general case, but we can - * do something about it if it is aliased - * somehwere else via a bind mount. If we - * explicitly remount the super block of that - * alias read-only we hence should be - * relatively safe regarding keeping the fs we - * can otherwise not see dirty. */ - 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 - * anyway, since we are running from it. They have - * already been remounted ro. */ - if (path_equal(m->path, "/") -#ifndef HAVE_SPLIT_USR - || path_equal(m->path, "/usr") -#endif - || path_startswith(m->path, "/run/initramfs") - ) - continue; - - /* Trying to umount. We don't force here since we rely - * on busy NFS and FUSE file systems to return EBUSY - * until we closed everything on top of them. */ - log_info("Unmounting %s.", m->path); - if (umount2(m->path, 0) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else if (log_error) { - log_warning_errno(errno, "Could not unmount %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int swap_points_list_off(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0; - - assert(head); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - log_info("Deactivating swap %s.", m->path); - if (swapoff(m->path) == 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int loopback_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - struct stat loopback_st; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - lstat(m->path, &loopback_st) >= 0 && - root_st.st_dev == loopback_st.st_rdev) { - n_failed++; - continue; - } - - log_info("Detaching loopback %s.", m->path); - r = delete_loopback(m->path); - if (r >= 0) { - if (r > 0 && changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning_errno(errno, "Could not detach loopback %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -static int dm_points_list_detach(MountPoint **head, bool *changed) { - MountPoint *m, *n; - int n_failed = 0, k; - struct stat root_st; - - assert(head); - - k = lstat("/", &root_st); - - LIST_FOREACH_SAFE(mount_point, m, n, *head) { - int r; - - if (k >= 0 && - major(root_st.st_dev) != 0 && - root_st.st_dev == m->devnum) { - n_failed++; - continue; - } - - log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); - r = delete_dm(m->devnum); - if (r >= 0) { - if (changed) - *changed = true; - - mount_point_free(head, m); - } else { - log_warning_errno(errno, "Could not detach DM %s: %m", m->path); - n_failed++; - } - } - - return n_failed; -} - -int umount_all(bool *changed) { - int r; - bool umount_changed; - LIST_HEAD(MountPoint, mp_list_head); - - LIST_HEAD_INIT(mp_list_head); - r = mount_points_list_get(&mp_list_head); - if (r < 0) - goto end; - - /* retry umount, until nothing can be umounted anymore */ - do { - umount_changed = false; - - mount_points_list_umount(&mp_list_head, &umount_changed, false); - if (umount_changed) - *changed = true; - - } while (umount_changed); - - /* umount one more time with logging enabled */ - r = mount_points_list_umount(&mp_list_head, &umount_changed, true); - if (r <= 0) - goto end; - - end: - mount_points_list_free(&mp_list_head); - - return r; -} - -int swapoff_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, swap_list_head); - - LIST_HEAD_INIT(swap_list_head); - - r = swap_list_get(&swap_list_head); - if (r < 0) - goto end; - - r = swap_points_list_off(&swap_list_head, changed); - - end: - mount_points_list_free(&swap_list_head); - - return r; -} - -int loopback_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, loopback_list_head); - - LIST_HEAD_INIT(loopback_list_head); - - r = loopback_list_get(&loopback_list_head); - if (r < 0) - goto end; - - r = loopback_points_list_detach(&loopback_list_head, changed); - - end: - mount_points_list_free(&loopback_list_head); - - return r; -} - -int dm_detach_all(bool *changed) { - int r; - LIST_HEAD(MountPoint, dm_list_head); - - LIST_HEAD_INIT(dm_list_head); - - r = dm_list_get(&dm_list_head); - if (r < 0) - goto end; - - r = dm_points_list_detach(&dm_list_head, changed); - - end: - mount_points_list_free(&dm_list_head); - - return r; -} diff --git a/src/core/umount.h b/src/core/umount.h deleted file mode 100644 index 4e2215a47d..0000000000 --- a/src/core/umount.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int umount_all(bool *changed); - -int swapoff_all(bool *changed); - -int loopback_detach_all(bool *changed); - -int dm_detach_all(bool *changed); diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c deleted file mode 100644 index f11df42af3..0000000000 --- a/src/core/unit-printf.c +++ /dev/null @@ -1,304 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 "unit.h" -#include "user-util.h" - -static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - - assert(u); - - return unit_name_to_prefix_and_instance(u->id, ret); -} - -static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - - assert(u); - - return unit_name_to_prefix(u->id, ret); -} - -static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) { - _cleanup_free_ char *p = NULL; - Unit *u = userdata; - int r; - - assert(u); - - r = unit_name_to_prefix(u->id, &p); - if (r < 0) - return r; - - return unit_name_unescape(p, ret); -} - -static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - - assert(u); - - return unit_name_unescape(strempty(u->instance), ret); -} - -static int specifier_filename(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - - assert(u); - - if (u->instance) - return unit_name_path_unescape(u->instance, ret); - else - return unit_name_to_path(u->id, ret); -} - -static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - char *n; - - assert(u); - - if (u->cgroup_path) - n = strdup(u->cgroup_path); - else - n = unit_default_cgroup_path(u); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - char *n; - - assert(u); - - n = strdup(u->manager->cgroup_root); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - char *n; - - assert(u); - - if (UNIT_ISSET(u->slice)) { - Unit *slice; - - slice = UNIT_DEREF(u->slice); - - if (slice->cgroup_path) - n = strdup(slice->cgroup_path); - else - n = unit_default_cgroup_path(slice); - } else - n = strdup(u->manager->cgroup_root); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -static int specifier_runtime(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - const char *e; - char *n = NULL; - - assert(u); - - e = manager_get_runtime_prefix(u->manager); - if (!e) - return -EOPNOTSUPP; - n = strdup(e); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *t; - - /* 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 = 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; -} - -static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - - /* On PID 1 (which runs as root) this will not result in NSS, - * which is good. See above */ - - return get_home_dir(ret); -} - -static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - - /* On PID 1 (which runs as root) this will not result in NSS, - * which is good. See above */ - - return get_shell(ret); -} - -int unit_name_printf(Unit *u, const char* format, char **ret) { - - /* - * This will use the passed string as format string and - * replace the following specifiers: - * - * %n: the full id of the unit (foo@bar.waldo) - * %N: the id of the unit without the suffix (foo@bar) - * %p: the prefix (foo) - * %i: the instance (bar) - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'i', specifier_string, u->instance }, - { 0, NULL, NULL } - }; - - assert(u); - assert(format); - assert(ret); - - return specifier_printf(format, table, u, ret); -} - -int unit_full_printf(Unit *u, const char *format, char **ret) { - - /* This is similar to unit_name_printf() but also supports - * unescaping. Also, adds a couple of additional codes: - * - * %f the instance if set, otherwise the id - * %c cgroup path of unit - * %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 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 - * %v `uname -r` of the running system - */ - - const Specifier table[] = { - { 'n', specifier_string, u->id }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'P', specifier_prefix_unescaped, NULL }, - { 'i', specifier_string, u->instance }, - { 'I', specifier_instance_unescaped, NULL }, - - { 'f', specifier_filename, NULL }, - { 'c', specifier_cgroup, NULL }, - { 'r', specifier_cgroup_slice, NULL }, - { 'R', specifier_cgroup_root, NULL }, - { 't', specifier_runtime, NULL }, - - { 'U', specifier_user_id, NULL }, - { 'u', specifier_user_name, NULL }, - { 'h', specifier_user_home, NULL }, - { 's', specifier_user_shell, NULL }, - - { 'm', specifier_machine_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} - }; - - assert(u); - assert(format); - assert(ret); - - return specifier_printf(format, table, u, ret); -} - -int unit_full_printf_strv(Unit *u, char **l, char ***ret) { - size_t n; - char **r, **i, **j; - int q; - - /* Applies unit_full_printf to every entry in l */ - - assert(u); - - n = strv_length(l); - r = new(char*, n+1); - if (!r) - return -ENOMEM; - - for (i = l, j = r; *i; i++, j++) { - q = unit_full_printf(u, *i, j); - if (q < 0) - goto fail; - } - - *j = NULL; - *ret = r; - return 0; - -fail: - for (j--; j >= r; j--) - free(*j); - - free(r); - return q; -} diff --git a/src/core/unit-printf.h b/src/core/unit-printf.h deleted file mode 100644 index 4fc8531228..0000000000 --- a/src/core/unit-printf.h +++ /dev/null @@ -1,26 +0,0 @@ -#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 . -***/ - -#include "unit.h" - -int unit_name_printf(Unit *u, const char* text, char **ret); -int unit_full_printf(Unit *u, const char *text, char **ret); -int unit_full_printf_strv(Unit *u, char **l, char ***ret); diff --git a/src/core/unit.c b/src/core/unit.c deleted file mode 100644 index 4934a0e56f..0000000000 --- a/src/core/unit.c +++ /dev/null @@ -1,3871 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-id128.h" -#include "sd-messages.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "cgroup-util.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 "set.h" -#include "signal-util.h" -#include "special.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "umask-util.h" -#include "unit-name.h" -#include "unit.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_DEVICE] = &device_vtable, - [UNIT_MOUNT] = &mount_vtable, - [UNIT_AUTOMOUNT] = &automount_vtable, - [UNIT_SWAP] = &swap_vtable, - [UNIT_TIMER] = &timer_vtable, - [UNIT_PATH] = &path_vtable, - [UNIT_SLICE] = &slice_vtable, - [UNIT_SCOPE] = &scope_vtable -}; - -static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency); - -Unit *unit_new(Manager *m, size_t size) { - Unit *u; - - assert(m); - assert(size >= sizeof(Unit)); - - u = malloc0(size); - if (!u) - return NULL; - - u->names = set_new(&string_hash_ops); - if (!u->names) { - free(u); - return NULL; - } - - u->manager = m; - u->type = _UNIT_TYPE_INVALID; - u->default_dependencies = true; - u->unit_file_state = _UNIT_FILE_STATE_INVALID; - u->unit_file_preset = -1; - u->on_failure_job_mode = JOB_REPLACE; - u->cgroup_inotify_wd = -1; - u->job_timeout = USEC_INFINITY; - u->sigchldgen = 0; - - RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst); - RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); - - return u; -} - -bool unit_has_name(Unit *u, const char *name) { - assert(u); - assert(name); - - return !!set_get(u->names, (char*) name); -} - -static void unit_init(Unit *u) { - CGroupContext *cc; - ExecContext *ec; - KillContext *kc; - - assert(u); - assert(u->manager); - assert(u->type >= 0); - - cc = unit_get_cgroup_context(u); - if (cc) { - cgroup_context_init(cc); - - /* Copy in the manager defaults into the cgroup - * context, _before_ the rest of the settings have - * been initialized */ - - cc->cpu_accounting = u->manager->default_cpu_accounting; - cc->io_accounting = u->manager->default_io_accounting; - 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); - if (ec) - exec_context_init(ec); - - kc = unit_get_kill_context(u); - if (kc) - kill_context_init(kc); - - if (UNIT_VTABLE(u)->init) - UNIT_VTABLE(u)->init(u); -} - -int unit_add_name(Unit *u, const char *text) { - _cleanup_free_ char *s = NULL, *i = NULL; - UnitType t; - int r; - - assert(u); - assert(text); - - if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) { - - if (!u->instance) - return -EINVAL; - - r = unit_name_replace_instance(text, u->instance, &s); - if (r < 0) - return r; - } else { - s = strdup(text); - if (!s) - return -ENOMEM; - } - - if (set_contains(u->names, s)) - return 0; - if (hashmap_contains(u->manager->units, s)) - return -EEXIST; - - if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return -EINVAL; - - t = unit_name_to_type(s); - if (t < 0) - return -EINVAL; - - if (u->type != _UNIT_TYPE_INVALID && t != u->type) - return -EINVAL; - - r = unit_name_to_instance(s, &i); - if (r < 0) - return r; - - if (i && !unit_type_may_template(t)) - return -EINVAL; - - /* Ensure that this unit is either instanced or not instanced, - * but not both. Note that we do allow names with different - * instance names however! */ - if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) - return -EINVAL; - - if (!unit_type_may_alias(t) && !set_isempty(u->names)) - return -EEXIST; - - if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) - return -E2BIG; - - r = set_put(u->names, s); - if (r < 0) - return r; - assert(r > 0); - - r = hashmap_put(u->manager->units, s, u); - if (r < 0) { - (void) set_remove(u->names, s); - return r; - } - - if (u->type == _UNIT_TYPE_INVALID) { - u->type = t; - u->id = s; - u->instance = i; - - LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); - - unit_init(u); - - i = NULL; - } - - s = NULL; - - unit_add_to_dbus_queue(u); - return 0; -} - -int unit_choose_id(Unit *u, const char *name) { - _cleanup_free_ char *t = NULL; - char *s, *i; - int r; - - assert(u); - assert(name); - - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - - if (!u->instance) - return -EINVAL; - - r = unit_name_replace_instance(name, u->instance, &t); - if (r < 0) - return r; - - name = t; - } - - /* Selects one of the names of this unit as the id */ - s = set_get(u->names, (char*) name); - if (!s) - return -ENOENT; - - /* Determine the new instance from the new id */ - r = unit_name_to_instance(s, &i); - if (r < 0) - return r; - - u->id = s; - - free(u->instance); - u->instance = i; - - unit_add_to_dbus_queue(u); - - return 0; -} - -int unit_set_description(Unit *u, const char *description) { - char *s; - - assert(u); - - if (isempty(description)) - s = NULL; - else { - s = strdup(description); - if (!s) - return -ENOMEM; - } - - free(u->description); - u->description = s; - - unit_add_to_dbus_queue(u); - return 0; -} - -bool unit_check_gc(Unit *u) { - UnitActiveState state; - assert(u); - - if (u->job) - return true; - - if (u->nop_job) - return true; - - state = unit_active_state(u); - - /* If the unit is inactive and failed and no job is queued for - * it, then release its runtime resources */ - if (UNIT_IS_INACTIVE_OR_FAILED(state) && - UNIT_VTABLE(u)->release_resources) - UNIT_VTABLE(u)->release_resources(u); - - /* But we keep the unit object around for longer when it is - * referenced or configured to not be gc'ed */ - if (state != UNIT_INACTIVE) - return true; - - if (u->no_gc) - return true; - - if (u->refs) - return true; - - if (UNIT_VTABLE(u)->check_gc) - if (UNIT_VTABLE(u)->check_gc(u)) - return true; - - return false; -} - -void unit_add_to_load_queue(Unit *u) { - assert(u); - assert(u->type != _UNIT_TYPE_INVALID); - - if (u->load_state != UNIT_STUB || u->in_load_queue) - return; - - LIST_PREPEND(load_queue, u->manager->load_queue, u); - u->in_load_queue = true; -} - -void unit_add_to_cleanup_queue(Unit *u) { - assert(u); - - if (u->in_cleanup_queue) - return; - - LIST_PREPEND(cleanup_queue, u->manager->cleanup_queue, u); - u->in_cleanup_queue = true; -} - -void unit_add_to_gc_queue(Unit *u) { - assert(u); - - if (u->in_gc_queue || u->in_cleanup_queue) - return; - - if (unit_check_gc(u)) - return; - - LIST_PREPEND(gc_queue, u->manager->gc_queue, u); - u->in_gc_queue = true; - - u->manager->n_in_gc_queue++; -} - -void unit_add_to_dbus_queue(Unit *u) { - assert(u); - assert(u->type != _UNIT_TYPE_INVALID); - - if (u->load_state == UNIT_STUB || u->in_dbus_queue) - return; - - /* Shortcut things if nobody cares */ - if (sd_bus_track_count(u->manager->subscribed) <= 0 && - set_isempty(u->manager->private_buses)) { - u->sent_dbus_new_signal = true; - return; - } - - LIST_PREPEND(dbus_queue, u->manager->dbus_unit_queue, u); - u->in_dbus_queue = true; -} - -static void bidi_set_free(Unit *u, Set *s) { - Iterator i; - Unit *other; - - assert(u); - - /* Frees the set and makes sure we are dropped from the - * inverse pointers */ - - SET_FOREACH(other, s, i) { - UnitDependency d; - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - set_remove(other->dependencies[d], u); - - unit_add_to_gc_queue(other); - } - - set_free(s); -} - -static void unit_remove_transient(Unit *u) { - char **i; - - assert(u); - - if (!u->transient) - return; - - if (u->fragment_path) - (void) unlink(u->fragment_path); - - STRV_FOREACH(i, u->dropin_paths) { - _cleanup_free_ char *p = NULL, *pp = NULL; - - p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */ - if (!p) - continue; - - pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */ - if (!pp) - continue; - - /* Only drop transient drop-ins */ - if (!path_equal(u->manager->lookup_paths.transient, pp)) - continue; - - (void) unlink(*i); - (void) rmdir(p); - } -} - -static void unit_free_requires_mounts_for(Unit *u) { - char **j; - - STRV_FOREACH(j, u->requires_mounts_for) { - char s[strlen(*j) + 1]; - - PATH_FOREACH_PREFIX_MORE(s, *j) { - char *y; - Set *x; - - x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y); - if (!x) - continue; - - set_remove(x, u); - - if (set_isempty(x)) { - hashmap_remove(u->manager->units_requiring_mounts_for, y); - free(y); - set_free(x); - } - } - } - - u->requires_mounts_for = strv_free(u->requires_mounts_for); -} - -static void unit_done(Unit *u) { - ExecContext *ec; - CGroupContext *cc; - - assert(u); - - if (u->type < 0) - return; - - if (UNIT_VTABLE(u)->done) - UNIT_VTABLE(u)->done(u); - - ec = unit_get_exec_context(u); - if (ec) - exec_context_done(ec); - - cc = unit_get_cgroup_context(u); - if (cc) - cgroup_context_done(cc); -} - -void unit_free(Unit *u) { - UnitDependency d; - Iterator i; - char *t; - - assert(u); - - if (u->transient_file) - fclose(u->transient_file); - - if (!MANAGER_IS_RELOADING(u->manager)) - unit_remove_transient(u); - - bus_unit_send_removed_signal(u); - - unit_done(u); - - sd_bus_slot_unref(u->match_bus_slot); - - unit_free_requires_mounts_for(u); - - SET_FOREACH(t, u->names, i) - hashmap_remove_value(u->manager->units, t, u); - - if (u->job) { - Job *j = u->job; - job_uninstall(j); - job_free(j); - } - - if (u->nop_job) { - Job *j = u->nop_job; - job_uninstall(j); - job_free(j); - } - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - bidi_set_free(u, u->dependencies[d]); - - if (u->type != _UNIT_TYPE_INVALID) - LIST_REMOVE(units_by_type, u->manager->units_by_type[u->type], u); - - if (u->in_load_queue) - LIST_REMOVE(load_queue, u->manager->load_queue, u); - - if (u->in_dbus_queue) - LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); - - if (u->in_cleanup_queue) - LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u); - - if (u->in_gc_queue) { - LIST_REMOVE(gc_queue, u->manager->gc_queue, u); - u->manager->n_in_gc_queue--; - } - - if (u->in_cgroup_queue) - LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); - - unit_release_cgroup(u); - - (void) manager_update_failed_units(u->manager, u, false); - set_remove(u->manager->startup_units, u); - - free(u->description); - strv_free(u->documentation); - free(u->fragment_path); - free(u->source_path); - strv_free(u->dropin_paths); - free(u->instance); - - free(u->job_timeout_reboot_arg); - - set_free_free(u->names); - - unit_unwatch_all_pids(u); - - condition_free_list(u->conditions); - condition_free_list(u->asserts); - - free(u->reboot_arg); - - unit_ref_unset(&u->slice); - - while (u->refs) - unit_ref_unset(u->refs); - - free(u); -} - -UnitActiveState unit_active_state(Unit *u) { - assert(u); - - if (u->load_state == UNIT_MERGED) - return unit_active_state(unit_follow_merge(u)); - - /* After a reload it might happen that a unit is not correctly - * loaded but still has a process around. That's why we won't - * shortcut failed loading to UNIT_INACTIVE_FAILED. */ - - return UNIT_VTABLE(u)->active_state(u); -} - -const char* unit_sub_state_to_string(Unit *u) { - assert(u); - - return UNIT_VTABLE(u)->sub_state_to_string(u); -} - -static int complete_move(Set **s, Set **other) { - int r; - - assert(s); - assert(other); - - if (!*other) - return 0; - - if (*s) { - r = set_move(*s, *other); - if (r < 0) - return r; - } else { - *s = *other; - *other = NULL; - } - - return 0; -} - -static int merge_names(Unit *u, Unit *other) { - char *t; - Iterator i; - int r; - - assert(u); - assert(other); - - r = complete_move(&u->names, &other->names); - if (r < 0) - return r; - - set_free_free(other->names); - other->names = NULL; - other->id = NULL; - - SET_FOREACH(t, u->names, i) - assert_se(hashmap_replace(u->manager->units, t, u) == 0); - - return 0; -} - -static int reserve_dependencies(Unit *u, Unit *other, UnitDependency d) { - unsigned n_reserve; - - assert(u); - assert(other); - assert(d < _UNIT_DEPENDENCY_MAX); - - /* - * If u does not have this dependency set allocated, there is no need - * to reserve anything. In that case other's set will be transferred - * as a whole to u by complete_move(). - */ - if (!u->dependencies[d]) - return 0; - - /* merge_dependencies() will skip a u-on-u dependency */ - n_reserve = set_size(other->dependencies[d]) - !!set_get(other->dependencies[d], u); - - return set_reserve(u->dependencies[d], n_reserve); -} - -static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitDependency d) { - Iterator i; - Unit *back; - int r; - - assert(u); - assert(other); - assert(d < _UNIT_DEPENDENCY_MAX); - - /* Fix backwards pointers */ - SET_FOREACH(back, other->dependencies[d], i) { - UnitDependency k; - - for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) { - /* Do not add dependencies between u and itself */ - if (back == u) { - if (set_remove(back->dependencies[k], other)) - maybe_warn_about_dependency(u, other_id, k); - } else { - r = set_remove_and_put(back->dependencies[k], other, u); - if (r == -EEXIST) - set_remove(back->dependencies[k], other); - else - assert(r >= 0 || r == -ENOENT); - } - } - } - - /* Also do not move dependencies on u to itself */ - back = set_remove(other->dependencies[d], u); - if (back) - maybe_warn_about_dependency(u, other_id, d); - - /* The move cannot fail. The caller must have performed a reservation. */ - assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0); - - other->dependencies[d] = set_free(other->dependencies[d]); -} - -int unit_merge(Unit *u, Unit *other) { - UnitDependency d; - const char *other_id = NULL; - int r; - - assert(u); - assert(other); - assert(u->manager == other->manager); - assert(u->type != _UNIT_TYPE_INVALID); - - other = unit_follow_merge(other); - - if (other == u) - return 0; - - if (u->type != other->type) - return -EINVAL; - - if (!u->instance != !other->instance) - return -EINVAL; - - if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ - return -EEXIST; - - if (other->load_state != UNIT_STUB && - other->load_state != UNIT_NOT_FOUND) - return -EEXIST; - - if (other->job) - return -EEXIST; - - if (other->nop_job) - return -EEXIST; - - if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) - return -EEXIST; - - if (other->id) - other_id = strdupa(other->id); - - /* Make reservations to ensure merge_dependencies() won't fail */ - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { - r = reserve_dependencies(u, other, d); - /* - * We don't rollback reservations if we fail. We don't have - * a way to undo reservations. A reservation is not a leak. - */ - if (r < 0) - return r; - } - - /* Merge names */ - r = merge_names(u, other); - if (r < 0) - return r; - - /* Redirect all references */ - while (other->refs) - unit_ref_set(other->refs, u); - - /* Merge dependencies */ - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) - merge_dependencies(u, other, other_id, d); - - other->load_state = UNIT_MERGED; - other->merged_into = u; - - /* If there is still some data attached to the other node, we - * don't need it anymore, and can free it. */ - if (other->load_state != UNIT_STUB) - if (UNIT_VTABLE(other)->done) - UNIT_VTABLE(other)->done(other); - - unit_add_to_dbus_queue(u); - unit_add_to_cleanup_queue(other); - - return 0; -} - -int unit_merge_by_name(Unit *u, const char *name) { - _cleanup_free_ char *s = NULL; - Unit *other; - int r; - - assert(u); - assert(name); - - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - if (!u->instance) - return -EINVAL; - - r = unit_name_replace_instance(name, u->instance, &s); - if (r < 0) - return r; - - name = s; - } - - other = manager_get_unit(u->manager, name); - if (other) - return unit_merge(u, other); - - return unit_add_name(u, name); -} - -Unit* unit_follow_merge(Unit *u) { - assert(u); - - while (u->load_state == UNIT_MERGED) - assert_se(u = u->merged_into); - - return u; -} - -int unit_add_exec_dependencies(Unit *u, ExecContext *c) { - int r; - - assert(u); - assert(c); - - if (c->working_directory) { - r = unit_require_mounts_for(u, c->working_directory); - if (r < 0) - return r; - } - - if (c->root_directory) { - r = unit_require_mounts_for(u, c->root_directory); - if (r < 0) - return r; - } - - if (!MANAGER_IS_SYSTEM(u->manager)) - return 0; - - if (c->private_tmp) { - r = unit_require_mounts_for(u, "/tmp"); - if (r < 0) - return r; - - r = unit_require_mounts_for(u, "/var/tmp"); - if (r < 0) - return r; - } - - if (c->std_output != EXEC_OUTPUT_KMSG && - c->std_output != EXEC_OUTPUT_SYSLOG && - c->std_output != EXEC_OUTPUT_JOURNAL && - c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE && - c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && - c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_KMSG && - c->std_error != EXEC_OUTPUT_SYSLOG && - c->std_error != EXEC_OUTPUT_JOURNAL && - c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && - c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE) - return 0; - - /* If syslog or kernel logging is requested, make sure our own - * logging daemon is run first. */ - - r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); - if (r < 0) - return r; - - return 0; -} - -const char *unit_description(Unit *u) { - assert(u); - - if (u->description) - return u->description; - - return strna(u->id); -} - -void unit_dump(Unit *u, FILE *f, const char *prefix) { - char *t, **j; - UnitDependency d; - Iterator i; - const char *prefix2; - char - timestamp0[FORMAT_TIMESTAMP_MAX], - timestamp1[FORMAT_TIMESTAMP_MAX], - timestamp2[FORMAT_TIMESTAMP_MAX], - timestamp3[FORMAT_TIMESTAMP_MAX], - timestamp4[FORMAT_TIMESTAMP_MAX], - timespan[FORMAT_TIMESPAN_MAX]; - Unit *following; - _cleanup_set_free_ Set *following_set = NULL; - int r; - - assert(u); - assert(u->type >= 0); - - prefix = strempty(prefix); - prefix2 = strjoina(prefix, "\t"); - - fprintf(f, - "%s-> Unit %s:\n" - "%s\tDescription: %s\n" - "%s\tInstance: %s\n" - "%s\tUnit Load State: %s\n" - "%s\tUnit Active State: %s\n" - "%s\tState Change Timestamp: %s\n" - "%s\tInactive Exit Timestamp: %s\n" - "%s\tActive Enter Timestamp: %s\n" - "%s\tActive Exit Timestamp: %s\n" - "%s\tInactive Enter Timestamp: %s\n" - "%s\tGC Check Good: %s\n" - "%s\tNeed Daemon Reload: %s\n" - "%s\tTransient: %s\n" - "%s\tSlice: %s\n" - "%s\tCGroup: %s\n" - "%s\tCGroup realized: %s\n" - "%s\tCGroup mask: 0x%x\n" - "%s\tCGroup members mask: 0x%x\n", - prefix, u->id, - prefix, unit_description(u), - prefix, strna(u->instance), - prefix, unit_load_state_to_string(u->load_state), - prefix, unit_active_state_to_string(unit_active_state(u)), - prefix, strna(format_timestamp(timestamp0, sizeof(timestamp0), u->state_change_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), - prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), - prefix, yes_no(unit_check_gc(u)), - prefix, yes_no(unit_need_daemon_reload(u)), - prefix, yes_no(u->transient), - prefix, strna(unit_slice_name(u)), - prefix, strna(u->cgroup_path), - prefix, yes_no(u->cgroup_realized), - prefix, u->cgroup_realized_mask, - prefix, u->cgroup_members_mask); - - SET_FOREACH(t, u->names, i) - fprintf(f, "%s\tName: %s\n", prefix, t); - - STRV_FOREACH(j, u->documentation) - fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); - - following = unit_following(u); - if (following) - fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); - - r = unit_following_set(u, &following_set); - if (r >= 0) { - Unit *other; - - SET_FOREACH(other, following_set, i) - fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id); - } - - if (u->fragment_path) - fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); - - if (u->source_path) - fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path); - - STRV_FOREACH(j, u->dropin_paths) - fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j); - - if (u->job_timeout != USEC_INFINITY) - fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); - - if (u->job_timeout_action != FAILURE_ACTION_NONE) - fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, failure_action_to_string(u->job_timeout_action)); - - if (u->job_timeout_reboot_arg) - fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg); - - condition_dump_list(u->conditions, f, prefix, condition_type_to_string); - condition_dump_list(u->asserts, f, prefix, assert_type_to_string); - - if (dual_timestamp_is_set(&u->condition_timestamp)) - fprintf(f, - "%s\tCondition Timestamp: %s\n" - "%s\tCondition Result: %s\n", - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)), - prefix, yes_no(u->condition_result)); - - if (dual_timestamp_is_set(&u->assert_timestamp)) - fprintf(f, - "%s\tAssert Timestamp: %s\n" - "%s\tAssert Result: %s\n", - prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->assert_timestamp.realtime)), - prefix, yes_no(u->assert_result)); - - for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { - Unit *other; - - SET_FOREACH(other, u->dependencies[d], i) - fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id); - } - - if (!strv_isempty(u->requires_mounts_for)) { - fprintf(f, - "%s\tRequiresMountsFor:", prefix); - - STRV_FOREACH(j, u->requires_mounts_for) - fprintf(f, " %s", *j); - - fputs("\n", f); - } - - if (u->load_state == UNIT_LOADED) { - - fprintf(f, - "%s\tStopWhenUnneeded: %s\n" - "%s\tRefuseManualStart: %s\n" - "%s\tRefuseManualStop: %s\n" - "%s\tDefaultDependencies: %s\n" - "%s\tOnFailureJobMode: %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)); - - if (UNIT_VTABLE(u)->dump) - UNIT_VTABLE(u)->dump(u, f, prefix2); - - } else if (u->load_state == UNIT_MERGED) - fprintf(f, - "%s\tMerged into: %s\n", - prefix, u->merged_into->id); - else if (u->load_state == UNIT_ERROR) - fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error)); - - - if (u->job) - job_dump(u->job, f, prefix2); - - if (u->nop_job) - job_dump(u->nop_job, f, prefix2); - -} - -/* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin(Unit *u) { - int r; - - assert(u); - - /* Load a .{service,socket,...} file */ - r = unit_load_fragment(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) - return -ENOENT; - - /* Load drop-in directory data */ - r = unit_load_dropin(unit_follow_merge(u)); - if (r < 0) - return r; - - return 0; -} - -/* Common implementation for multiple backends */ -int unit_load_fragment_and_dropin_optional(Unit *u) { - int r; - - assert(u); - - /* Same as unit_load_fragment_and_dropin(), but whether - * something can be loaded or not doesn't matter. */ - - /* Load a .service file */ - r = unit_load_fragment(u); - if (r < 0) - return r; - - if (u->load_state == UNIT_STUB) - u->load_state = UNIT_LOADED; - - /* Load drop-in directory data */ - r = unit_load_dropin(unit_follow_merge(u)); - if (r < 0) - return r; - - return 0; -} - -int unit_add_default_target_dependency(Unit *u, Unit *target) { - assert(u); - assert(target); - - if (target->type != UNIT_TARGET) - return 0; - - /* Only add the dependency if both units are loaded, so that - * that loop check below is reliable */ - if (u->load_state != UNIT_LOADED || - target->load_state != UNIT_LOADED) - return 0; - - /* If either side wants no automatic dependencies, then let's - * skip this */ - if (!u->default_dependencies || - !target->default_dependencies) - return 0; - - /* Don't create loops */ - if (set_get(target->dependencies[UNIT_BEFORE], u)) - return 0; - - return unit_add_dependency(target, UNIT_AFTER, u, true); -} - -static int unit_add_target_dependencies(Unit *u) { - - static const UnitDependency deps[] = { - UNIT_REQUIRED_BY, - UNIT_REQUISITE_OF, - UNIT_WANTED_BY, - UNIT_BOUND_BY - }; - - Unit *target; - Iterator i; - unsigned k; - int r = 0; - - assert(u); - - for (k = 0; k < ELEMENTSOF(deps); k++) - SET_FOREACH(target, u->dependencies[deps[k]], i) { - r = unit_add_default_target_dependency(u, target); - if (r < 0) - return r; - } - - return r; -} - -static int unit_add_slice_dependencies(Unit *u) { - assert(u); - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return 0; - - if (UNIT_ISSET(u->slice)) - return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true); - - if (unit_has_name(u, SPECIAL_ROOT_SLICE)) - return 0; - - 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) { - char **i; - int r; - - assert(u); - - STRV_FOREACH(i, u->requires_mounts_for) { - char prefix[strlen(*i) + 1]; - - PATH_FOREACH_PREFIX_MORE(prefix, *i) { - _cleanup_free_ char *p = NULL; - Unit *m; - - r = unit_name_from_path(prefix, ".mount", &p); - if (r < 0) - return r; - - 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; - - if (m->load_state != UNIT_LOADED) - continue; - - r = unit_add_dependency(u, UNIT_AFTER, m, true); - if (r < 0) - return r; - - if (m->fragment_path) { - r = unit_add_dependency(u, UNIT_REQUIRES, m, true); - if (r < 0) - return r; - } - } - } - - return 0; -} - -static int unit_add_startup_units(Unit *u) { - CGroupContext *c; - int r; - - c = unit_get_cgroup_context(u); - if (!c) - return 0; - - if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID && - c->startup_io_weight == CGROUP_WEIGHT_INVALID && - c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) - return 0; - - r = set_ensure_allocated(&u->manager->startup_units, NULL); - if (r < 0) - return r; - - return set_put(u->manager->startup_units, u); -} - -int unit_load(Unit *u) { - int r; - - assert(u); - - if (u->in_load_queue) { - LIST_REMOVE(load_queue, u->manager->load_queue, u); - u->in_load_queue = false; - } - - if (u->type == _UNIT_TYPE_INVALID) - return -EINVAL; - - if (u->load_state != UNIT_STUB) - return 0; - - if (u->transient_file) { - r = fflush_and_check(u->transient_file); - if (r < 0) - goto fail; - - fclose(u->transient_file); - u->transient_file = NULL; - - u->fragment_mtime = now(CLOCK_REALTIME); - } - - if (UNIT_VTABLE(u)->load) { - r = UNIT_VTABLE(u)->load(u); - if (r < 0) - goto fail; - } - - if (u->load_state == UNIT_STUB) { - r = -ENOENT; - goto fail; - } - - if (u->load_state == UNIT_LOADED) { - - r = unit_add_target_dependencies(u); - if (r < 0) - goto fail; - - r = unit_add_slice_dependencies(u); - if (r < 0) - goto fail; - - r = unit_add_mount_dependencies(u); - if (r < 0) - goto fail; - - r = unit_add_startup_units(u); - if (r < 0) - goto fail; - - if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing."); - r = -EINVAL; - goto fail; - } - - unit_update_cgroup_members_masks(u); - } - - assert((u->load_state != UNIT_MERGED) == !u->merged_into); - - unit_add_to_dbus_queue(unit_follow_merge(u)); - unit_add_to_gc_queue(u); - - return 0; - -fail: - u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : UNIT_ERROR; - u->load_error = r; - unit_add_to_dbus_queue(u); - unit_add_to_gc_queue(u); - - log_unit_debug_errno(u, r, "Failed to load configuration: %m"); - - return r; -} - -static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) { - Condition *c; - int triggered = -1; - - assert(u); - assert(to_string); - - /* If the condition list is empty, then it is true */ - if (!first) - return true; - - /* Otherwise, if all of the non-trigger conditions apply and - * if any of the trigger conditions apply (unless there are - * none) we return true */ - LIST_FOREACH(conditions, c, first) { - int r; - - r = condition_test(c); - if (r < 0) - log_unit_warning(u, - "Couldn't determine result for %s=%s%s%s, assuming failed: %m", - to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter); - else - log_unit_debug(u, - "%s=%s%s%s %s.", - to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter, - condition_result_to_string(c->result)); - - if (!c->trigger && r <= 0) - return false; - - if (c->trigger && triggered <= 0) - triggered = r > 0; - } - - return triggered != 0; -} - -static bool unit_condition_test(Unit *u) { - assert(u); - - dual_timestamp_get(&u->condition_timestamp); - u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string); - - return u->condition_result; -} - -static bool unit_assert_test(Unit *u) { - assert(u); - - dual_timestamp_get(&u->assert_timestamp); - u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string); - - 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(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); - - if (t != JOB_RELOAD) { - format_table = &UNIT_VTABLE(u)->status_message_formats; - if (format_table) { - format = format_table->starting_stopping[t == JOB_STOP]; - if (format) - return format; - } - } - - /* Return generic strings */ - if (t == JOB_START) - return "Starting %s."; - else if (t == JOB_STOP) - return "Stopping %s."; - else - return "Reloading %s."; -} - -static void unit_status_print_starting_stopping(Unit *u, JobType t) { - const char *format; - - 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; - unit_status_printf(u, "", format); - REENABLE_WARNING; -} - -static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { - const char *format; - char buf[LINE_MAX]; - sd_id128_t mid; - - assert(u); - - if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) - return; - - if (log_on_console()) - return; - - /* We log status messages for all units and all operations. */ - - format = unit_get_status_message_format(u, t); - - DISABLE_WARNING_FORMAT_NONLITERAL; - xsprintf(buf, format, unit_description(u)); - REENABLE_WARNING; - - mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : - t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : - SD_MESSAGE_UNIT_RELOADING; - - /* Note that we deliberately use LOG_MESSAGE() instead of - * LOG_UNIT_MESSAGE() here, since this is supposed to mimic - * closely what is written to screen using the status output, - * which is supposed the highest level, friendliest output - * possible, which means we should avoid the low-level unit - * name. */ - log_struct(LOG_INFO, - LOG_MESSAGE_ID(mid), - LOG_UNIT_ID(u), - LOG_MESSAGE("%s", buf), - NULL); -} - -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); - unit_status_print_starting_stopping(u, t); -} - -int unit_start_limit_test(Unit *u) { - assert(u); - - if (ratelimit_test(&u->start_limit)) { - u->start_limit_hit = false; - return 0; - } - - log_unit_warning(u, "Start request repeated too quickly."); - u->start_limit_hit = true; - - return failure_action(u->manager, u->start_limit_action, u->reboot_arg); -} - -/* Errors: - * -EBADR: This unit type does not support starting. - * -EALREADY: Unit is already started. - * -EAGAIN: An operation is already in progress. Retry later. - * -ECANCELED: Too many requests for now. - * -EPROTO: Assert failed - * -EINVAL: Unit not loaded - * -EOPNOTSUPP: Unit type not supported - */ -int unit_start(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - /* If this is already started, then this will succeed. Note - * that this will even succeed if this unit is not startable - * by the user. This is relied on to detect when we need to - * wait for units and when waiting is finished. */ - state = unit_active_state(u); - if (UNIT_IS_ACTIVE_OR_RELOADING(state)) - return -EALREADY; - - /* Units that aren't loaded cannot be started */ - if (u->load_state != UNIT_LOADED) - return -EINVAL; - - /* If the conditions failed, don't do anything at all. If we - * already are activating this call might still be useful to - * speed up activation in case there is some hold-off time, - * but we don't want to recheck the condition in that case. */ - if (state != UNIT_ACTIVATING && - !unit_condition_test(u)) { - log_unit_debug(u, "Starting requested but condition failed. Not starting unit."); - return -EALREADY; - } - - /* If the asserts failed, fail the entire job */ - if (state != UNIT_ACTIVATING && - !unit_assert_test(u)) { - log_unit_notice(u, "Starting requested but asserts failed."); - return -EPROTO; - } - - /* Units of types that aren't supported cannot be - * started. Note that we do this test only after the condition - * checks, so that we rather return condition check errors - * (which are usually not considered a true failure) than "not - * supported" errors (which are considered a failure). - */ - if (!unit_supported(u)) - return -EOPNOTSUPP; - - /* Forward to the main object, if we aren't it. */ - following = unit_following(u); - if (following) { - log_unit_debug(u, "Redirecting start request from %s to %s.", u->id, following->id); - return unit_start(following); - } - - /* If it is stopped, but we cannot start it, then fail */ - if (!UNIT_VTABLE(u)->start) - return -EBADR; - - /* We don't suppress calls to ->start() here when we are - * already starting, to allow this request to be used as a - * "hurry up" call, for example when the unit is in some "auto - * restart" state where it waits for a holdoff timer to elapse - * before it will start again. */ - - unit_add_to_dbus_queue(u); - - return UNIT_VTABLE(u)->start(u); -} - -bool unit_can_start(Unit *u) { - assert(u); - - if (u->load_state != UNIT_LOADED) - return false; - - if (!unit_supported(u)) - return false; - - return !!UNIT_VTABLE(u)->start; -} - -bool unit_can_isolate(Unit *u) { - assert(u); - - return unit_can_start(u) && - u->allow_isolate; -} - -/* Errors: - * -EBADR: This unit type does not support stopping. - * -EALREADY: Unit is already stopped. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int unit_stop(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - state = unit_active_state(u); - if (UNIT_IS_INACTIVE_OR_FAILED(state)) - return -EALREADY; - - following = unit_following(u); - if (following) { - log_unit_debug(u, "Redirecting stop request from %s to %s.", u->id, following->id); - return unit_stop(following); - } - - if (!UNIT_VTABLE(u)->stop) - return -EBADR; - - unit_add_to_dbus_queue(u); - - return UNIT_VTABLE(u)->stop(u); -} - -/* Errors: - * -EBADR: This unit type does not support reloading. - * -ENOEXEC: Unit is not started. - * -EAGAIN: An operation is already in progress. Retry later. - */ -int unit_reload(Unit *u) { - UnitActiveState state; - Unit *following; - - assert(u); - - if (u->load_state != UNIT_LOADED) - return -EINVAL; - - if (!unit_can_reload(u)) - return -EBADR; - - state = unit_active_state(u); - if (state == UNIT_RELOADING) - return -EALREADY; - - if (state != UNIT_ACTIVE) { - log_unit_warning(u, "Unit cannot be reloaded because it is inactive."); - return -ENOEXEC; - } - - following = unit_following(u); - if (following) { - log_unit_debug(u, "Redirecting reload request from %s to %s.", u->id, following->id); - return unit_reload(following); - } - - unit_add_to_dbus_queue(u); - - return UNIT_VTABLE(u)->reload(u); -} - -bool unit_can_reload(Unit *u) { - assert(u); - - if (!UNIT_VTABLE(u)->reload) - return false; - - if (!UNIT_VTABLE(u)->can_reload) - return true; - - return UNIT_VTABLE(u)->can_reload(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_REQUISITE_OF, - UNIT_WANTED_BY, - UNIT_BOUND_BY, - }; - - Unit *other; - Iterator i; - unsigned j; - int r; - - assert(u); - - /* If this service shall be shut down when unneeded then do - * so. */ - - if (!u->stop_when_unneeded) - return; - - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) - return; - - for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) - SET_FOREACH(other, u->dependencies[needed_dependencies[j]], i) - if (unit_active_or_pending(other)) - return; - - /* If stopping a unit fails continuously we might enter a stop - * loop here, hence stop acting on the service being - * unnecessary after a while. */ - if (!ratelimit_test(&u->auto_stop_ratelimit)) { - log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); - return; - } - - log_unit_info(u, "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, &error, NULL); - if (r < 0) - 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; - int r; - - assert(u); - - if (u->job) - return; - - if (unit_active_state(u) != UNIT_ACTIVE) - return; - - SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) { - if (other->job) - continue; - - if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) - continue; - - stop = true; - break; - } - - if (!stop) - return; - - /* If stopping a unit fails continuously we might enter a stop - * loop here, hence stop acting on the service being - * unnecessary after a while. */ - if (!ratelimit_test(&u->auto_stop_ratelimit)) { - log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id); - return; - } - - assert(other); - log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); - - /* A unit we need to run is gone. Sniff. Let's stop this. */ - 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: %s", bus_error_message(&error, r)); -} - -static void retroactively_start_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(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, 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, 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, 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, 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, NULL, NULL); -} - -static void retroactively_stop_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); - - /* Pull down units which are bound to us recursively if enabled */ - SET_FOREACH(other, u->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, NULL, NULL); -} - -static void check_unneeded_dependencies(Unit *u) { - Iterator i; - Unit *other; - - assert(u); - assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); - - /* Garbage collect services that might not be needed anymore, if enabled */ - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], 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_BINDS_TO], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); -} - -void unit_start_on_failure(Unit *u) { - Unit *other; - Iterator i; - - assert(u); - - if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) - return; - - log_unit_info(u, "Triggering OnFailure= dependencies."); - - SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { - int r; - - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL); - if (r < 0) - log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m"); - } -} - -void unit_trigger_notify(Unit *u) { - Unit *other; - Iterator i; - - assert(u); - - SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i) - if (UNIT_VTABLE(other)->trigger_notify) - UNIT_VTABLE(other)->trigger_notify(other, u); -} - -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { - Manager *m; - bool unexpected; - - assert(u); - assert(os < _UNIT_ACTIVE_STATE_MAX); - assert(ns < _UNIT_ACTIVE_STATE_MAX); - - /* Note that this is called for all low-level state changes, - * even if they might map to the same high-level - * UnitActiveState! That means that ns == os is an expected - * behavior here. For example: if a mount point is remounted - * this function will be called too! */ - - m = u->manager; - - /* Update timestamps for state changes */ - if (!MANAGER_IS_RELOADING(m)) { - dual_timestamp_get(&u->state_change_timestamp); - - if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->inactive_exit_timestamp = u->state_change_timestamp; - else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns)) - u->inactive_enter_timestamp = u->state_change_timestamp; - - if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->active_enter_timestamp = u->state_change_timestamp; - else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) - u->active_exit_timestamp = u->state_change_timestamp; - } - - /* Keep track of failed units */ - (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); - - /* Make sure the cgroup is always removed when we become inactive */ - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - unit_prune_cgroup(u); - - /* Note that this doesn't apply to RemainAfterExit services exiting - * successfully, since there's no change of state in that case. Which is - * why it is handled in service_set_state() */ - if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { - ExecContext *ec; - - ec = unit_get_exec_context(u); - if (ec && exec_context_may_touch_console(ec)) { - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { - m->n_on_console--; - - if (m->n_on_console == 0) - /* unset no_console_output flag, since the console is free */ - m->no_console_output = false; - } else - m->n_on_console++; - } - } - - if (u->job) { - unexpected = false; - - if (u->job->state == JOB_WAITING) - - /* So we reached a different state for this - * job. Let's see if we can run it now if it - * failed previously due to EAGAIN. */ - job_add_to_run_queue(u->job); - - /* Let's check whether this state change constitutes a - * finished job, or maybe contradicts a running job and - * hence needs to invalidate jobs. */ - - switch (u->job->type) { - - case JOB_START: - case JOB_VERIFY_ACTIVE: - - if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true, false); - else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { - unexpected = true; - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); - } - - break; - - case JOB_RELOAD: - case JOB_RELOAD_OR_START: - case JOB_TRY_RELOAD: - - if (u->job->state == JOB_RUNNING) { - if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); - else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { - unexpected = true; - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); - } - } - - break; - - case JOB_STOP: - case JOB_RESTART: - case JOB_TRY_RESTART: - - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true, false); - else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { - unexpected = true; - job_finish_and_invalidate(u->job, JOB_FAILED, true, false); - } - - break; - - default: - assert_not_reached("Job type unknown"); - } - - } else - unexpected = true; - - if (!MANAGER_IS_RELOADING(m)) { - - /* If this state change happened without being - * requested by a job, then let's retroactively start - * or stop dependencies. We skip that step when - * deserializing, since we don't want to create any - * additional jobs just because something is already - * activated. */ - - if (unexpected) { - if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) - retroactively_start_dependencies(u); - else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) - retroactively_stop_dependencies(u); - } - - /* stop unneeded units regardless if going down was expected or not */ - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) - check_unneeded_dependencies(u); - - if (ns != os && ns == UNIT_FAILED) { - log_unit_notice(u, "Unit entered failed state."); - unit_start_on_failure(u); - } - } - - /* Some names are special */ - if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { - - if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) - /* The bus might have just become available, - * hence try to connect to it, if we aren't - * yet connected. */ - bus_init(m, true); - - if (u->type == UNIT_SERVICE && - !UNIT_IS_ACTIVE_OR_RELOADING(os) && - !MANAGER_IS_RELOADING(m)) { - /* Write audit record if we have just finished starting up */ - manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); - u->in_audit = true; - } - - if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) - manager_send_unit_plymouth(m, u); - - } else { - - /* We don't care about D-Bus here, since we'll get an - * asynchronous notification for it anyway. */ - - if (u->type == UNIT_SERVICE && - UNIT_IS_INACTIVE_OR_FAILED(ns) && - !UNIT_IS_INACTIVE_OR_FAILED(os) && - !MANAGER_IS_RELOADING(m)) { - - /* Hmm, if there was no start record written - * write it now, so that we always have a nice - * pair */ - if (!u->in_audit) { - manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); - - if (ns == UNIT_INACTIVE) - manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true); - } else - /* Write audit record if we have just finished shutting down */ - manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); - - u->in_audit = false; - } - } - - manager_recheck_journal(m); - unit_trigger_notify(u); - - if (!MANAGER_IS_RELOADING(u->manager)) { - /* Maybe we finished startup and are now ready for - * being stopped because unneeded? */ - unit_check_unneeded(u); - - /* Maybe we finished startup, but something we needed - * has vanished? Let's die then. (This happens when - * something BindsTo= to a Type=oneshot unit, as these - * units go directly from starting to inactive, - * without ever entering started.) */ - unit_check_binds_to(u); - } - - unit_add_to_dbus_queue(u); - unit_add_to_gc_queue(u); -} - -int unit_watch_pid(Unit *u, pid_t pid) { - int q, r; - - assert(u); - assert(pid >= 1); - - /* Watch a specific PID. We only support one or two units - * watching each PID for now, not more. */ - - r = set_ensure_allocated(&u->pids, NULL); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&u->manager->watch_pids1, NULL); - if (r < 0) - return r; - - r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u); - if (r == -EEXIST) { - r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL); - if (r < 0) - return r; - - r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u); - } - - q = set_put(u->pids, PID_TO_PTR(pid)); - if (q < 0) - return q; - - return r; -} - -void unit_unwatch_pid(Unit *u, pid_t pid) { - assert(u); - assert(pid >= 1); - - (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u); - (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u); - (void) set_remove(u->pids, PID_TO_PTR(pid)); -} - -void unit_unwatch_all_pids(Unit *u) { - assert(u); - - while (!set_isempty(u->pids)) - unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids))); - - u->pids = set_free(u->pids); -} - -void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { - Iterator i; - void *e; - - assert(u); - - /* Cleans dead PIDs from our list */ - - SET_FOREACH(e, u->pids, i) { - pid_t pid = PTR_TO_PID(e); - - if (pid == except1 || pid == except2) - continue; - - if (!pid_is_unwaited(pid)) - unit_unwatch_pid(u, pid); - } -} - -bool unit_job_is_applicable(Unit *u, JobType j) { - assert(u); - assert(j >= 0 && j < _JOB_TYPE_MAX); - - switch (j) { - - case JOB_VERIFY_ACTIVE: - case JOB_START: - case JOB_STOP: - case JOB_NOP: - return true; - - case JOB_RESTART: - case JOB_TRY_RESTART: - return unit_can_start(u); - - case JOB_RELOAD: - case JOB_TRY_RELOAD: - return unit_can_reload(u); - - case JOB_RELOAD_OR_START: - return unit_can_reload(u) && unit_can_start(u); - - default: - assert_not_reached("Invalid job type"); - } -} - -static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency) { - assert(u); - - /* Only warn about some unit types */ - if (!IN_SET(dependency, UNIT_CONFLICTS, UNIT_CONFLICTED_BY, UNIT_BEFORE, UNIT_AFTER, UNIT_ON_FAILURE, UNIT_TRIGGERS, UNIT_TRIGGERED_BY)) - return; - - if (streq_ptr(u->id, other)) - log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id); - else - log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other), u->id); -} - -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) { - - static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_WANTS] = UNIT_WANTED_BY, - [UNIT_REQUISITE] = UNIT_REQUISITE_OF, - [UNIT_BINDS_TO] = UNIT_BOUND_BY, - [UNIT_PART_OF] = UNIT_CONSISTS_OF, - [UNIT_REQUIRED_BY] = UNIT_REQUIRES, - [UNIT_REQUISITE_OF] = UNIT_REQUISITE, - [UNIT_WANTED_BY] = UNIT_WANTS, - [UNIT_BOUND_BY] = UNIT_BINDS_TO, - [UNIT_CONSISTS_OF] = UNIT_PART_OF, - [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, - [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, - [UNIT_BEFORE] = UNIT_AFTER, - [UNIT_AFTER] = UNIT_BEFORE, - [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID, - [UNIT_REFERENCES] = UNIT_REFERENCED_BY, - [UNIT_REFERENCED_BY] = UNIT_REFERENCES, - [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, - [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, - [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, - [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, - [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, - }; - int r, q = 0, v = 0, w = 0; - Unit *orig_u = u, *orig_other = other; - - assert(u); - assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); - assert(other); - - u = unit_follow_merge(u); - other = unit_follow_merge(other); - - /* We won't allow dependencies on ourselves. We will not - * consider them an error however. */ - if (u == other) { - maybe_warn_about_dependency(orig_u, orig_other->id, d); - return 0; - } - - r = set_ensure_allocated(&u->dependencies[d], NULL); - if (r < 0) - return r; - - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) { - r = set_ensure_allocated(&other->dependencies[inverse_table[d]], NULL); - if (r < 0) - return r; - } - - if (add_reference) { - r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], NULL); - if (r < 0) - return r; - - r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], NULL); - if (r < 0) - return r; - } - - q = set_put(u->dependencies[d], other); - if (q < 0) - return q; - - if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) { - v = set_put(other->dependencies[inverse_table[d]], u); - if (v < 0) { - r = v; - goto fail; - } - } - - if (add_reference) { - w = set_put(u->dependencies[UNIT_REFERENCES], other); - if (w < 0) { - r = w; - goto fail; - } - - r = set_put(other->dependencies[UNIT_REFERENCED_BY], u); - if (r < 0) - goto fail; - } - - unit_add_to_dbus_queue(u); - return 0; - -fail: - if (q > 0) - set_remove(u->dependencies[d], other); - - if (v > 0) - set_remove(other->dependencies[inverse_table[d]], u); - - if (w > 0) - set_remove(u->dependencies[UNIT_REFERENCES], other); - - return r; -} - -int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) { - int r; - - assert(u); - - r = unit_add_dependency(u, d, other, add_reference); - if (r < 0) - return r; - - return unit_add_dependency(u, e, other, add_reference); -} - -static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) { - int r; - - assert(u); - assert(name || path); - assert(buf); - assert(ret); - - if (!name) - name = basename(path); - - if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - *buf = NULL; - *ret = name; - return 0; - } - - if (u->instance) - r = unit_name_replace_instance(name, u->instance, buf); - else { - _cleanup_free_ char *i = NULL; - - r = unit_name_to_prefix(u->id, &i); - if (r < 0) - return r; - - r = unit_name_replace_instance(name, i, buf); - } - if (r < 0) - return r; - - *ret = *buf; - return 0; -} - -int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { - _cleanup_free_ char *buf = NULL; - Unit *other; - int r; - - 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(u, d, other, add_reference); -} - -int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { - _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(u, d, e, other, add_reference); -} - -int set_unit_path(const char *p) { - /* This is mostly for debug purposes */ - if (setenv("SYSTEMD_UNIT_PATH", p, 1) < 0) - return -errno; - - return 0; -} - -char *unit_dbus_path(Unit *u) { - assert(u); - - if (!u->id) - return NULL; - - return unit_dbus_path_from_name(u->id); -} - -int unit_set_slice(Unit *u, Unit *slice) { - assert(u); - assert(slice); - - /* Sets the unit slice if it has not been set before. Is extra - * careful, to only allow this for units that actually have a - * cgroup context. Also, we don't allow to set this for slices - * (since the parent slice is derived from the name). Make - * sure the unit we set is actually a slice. */ - - if (!UNIT_HAS_CGROUP_CONTEXT(u)) - return -EOPNOTSUPP; - - if (u->type == UNIT_SLICE) - return -EINVAL; - - if (unit_active_state(u) != UNIT_INACTIVE) - return -EBUSY; - - if (slice->type != UNIT_SLICE) - return -EINVAL; - - if (unit_has_name(u, SPECIAL_INIT_SCOPE) && - !unit_has_name(slice, SPECIAL_ROOT_SLICE)) - return -EPERM; - - if (UNIT_DEREF(u->slice) == slice) - return 0; - - /* Disallow slice changes if @u is already bound to cgroups */ - if (UNIT_ISSET(u->slice) && u->cgroup_realized) - return -EBUSY; - - unit_ref_unset(&u->slice); - unit_ref_set(&u->slice, slice); - return 1; -} - -int unit_set_default_slice(Unit *u) { - _cleanup_free_ char *b = NULL; - const char *slice_name; - Unit *slice; - int r; - - assert(u); - - if (UNIT_ISSET(u->slice)) - return 0; - - if (u->instance) { - _cleanup_free_ char *prefix = NULL, *escaped = NULL; - - /* Implicitly place all instantiated units in their - * own per-template slice */ - - r = unit_name_to_prefix(u->id, &prefix); - if (r < 0) - return r; - - /* The prefix is already escaped, but it might include - * "-" which has a special meaning for slice units, - * hence escape it here extra. */ - escaped = unit_name_escape(prefix); - if (!escaped) - return -ENOMEM; - - if (MANAGER_IS_SYSTEM(u->manager)) - b = strjoin("system-", escaped, ".slice", NULL); - else - b = strappend(escaped, ".slice"); - if (!b) - return -ENOMEM; - - slice_name = b; - } else - slice_name = - MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE) - ? SPECIAL_SYSTEM_SLICE - : SPECIAL_ROOT_SLICE; - - r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice); - if (r < 0) - return r; - - return unit_set_slice(u, slice); -} - -const char *unit_slice_name(Unit *u) { - assert(u); - - if (!UNIT_ISSET(u->slice)) - return NULL; - - return UNIT_DEREF(u->slice)->id; -} - -int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { - _cleanup_free_ char *t = NULL; - int r; - - assert(u); - assert(type); - assert(_found); - - r = unit_name_change_suffix(u->id, type, &t); - if (r < 0) - return r; - if (unit_has_name(u, t)) - return -EINVAL; - - r = manager_load_unit(u->manager, t, NULL, NULL, _found); - assert(r < 0 || *_found != u); - return r; -} - -static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *name, *old_owner, *new_owner; - Unit *u = userdata; - int r; - - assert(message); - assert(u); - - r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (UNIT_VTABLE(u)->bus_name_owner_change) - UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner); - - return 0; -} - -int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { - const char *match; - - assert(u); - assert(bus); - assert(name); - - if (u->match_bus_slot) - return -EBUSY; - - match = strjoina("type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "arg0='", name, "'"); - - return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); -} - -int unit_watch_bus_name(Unit *u, const char *name) { - int r; - - assert(u); - assert(name); - - /* Watch a specific name on the bus. We only support one unit - * watching each name for now. */ - - 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, u->manager->api_bus, name); - if (r < 0) - return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); - } - - r = hashmap_put(u->manager->watch_bus, name, u); - if (r < 0) { - u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); - return log_warning_errno(r, "Failed to put bus name to hashmap: %m"); - } - - return 0; -} - -void unit_unwatch_bus_name(Unit *u, const char *name) { - assert(u); - assert(name); - - hashmap_remove_value(u->manager->watch_bus, name, u); - u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); -} - -bool unit_can_serialize(Unit *u) { - assert(u); - - return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; -} - -int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { - int r; - - assert(u); - assert(f); - assert(fds); - - if (unit_can_serialize(u)) { - ExecRuntime *rt; - - r = UNIT_VTABLE(u)->serialize(u, f, fds); - if (r < 0) - return r; - - rt = unit_get_exec_runtime(u); - if (rt) { - r = exec_runtime_serialize(u, rt, f, fds); - if (r < 0) - return r; - } - } - - dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp); - - dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); - dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp); - dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); - dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); - - dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); - dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp); - - if (dual_timestamp_is_set(&u->condition_timestamp)) - unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); - - if (dual_timestamp_is_set(&u->assert_timestamp)) - unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result)); - - unit_serialize_item(u, f, "transient", yes_no(u->transient)); - unit_serialize_item_format(u, f, "cpuacct-usage-base", "%" PRIu64, u->cpuacct_usage_base); - - if (u->cgroup_path) - unit_serialize_item(u, f, "cgroup", u->cgroup_path); - unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); - - if (serialize_jobs) { - if (u->job) { - fprintf(f, "job\n"); - job_serialize(u->job, f, fds); - } - - if (u->nop_job) { - fprintf(f, "job\n"); - job_serialize(u->nop_job, f, fds); - } - } - - /* End marker */ - fputc('\n', f); - 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; - - assert(u); - assert(f); - assert(key); - assert(format); - - fputs(key, f); - fputc('=', f); - - va_start(ap, format); - vfprintf(f, format, ap); - va_end(ap); - - fputc('\n', f); -} - -int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { - ExecRuntime **rt = NULL; - size_t offset; - int r; - - assert(u); - assert(f); - assert(fds); - - offset = UNIT_VTABLE(u)->exec_runtime_offset; - if (offset > 0) - rt = (ExecRuntime**) ((uint8_t*) u + offset); - - for (;;) { - char line[LINE_MAX], *l, *v; - size_t k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - return 0; - return -errno; - } - - char_array_0(line); - l = strstrip(line); - - /* End marker */ - if (isempty(l)) - break; - - k = strcspn(l, "="); - - if (l[k] == '=') { - l[k] = 0; - v = l+k+1; - } else - v = l+k; - - if (streq(l, "job")) { - if (v[0] == '\0') { - /* new-style serialized job */ - Job *j; - - j = job_new_raw(u); - if (!j) - return log_oom(); - - r = job_deserialize(j, f, fds); - if (r < 0) { - job_free(j); - return r; - } - - r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j); - if (r < 0) { - job_free(j); - return r; - } - - r = job_install_deserialized(j); - if (r < 0) { - hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id)); - job_free(j); - return r; - } - } else /* legacy for pre-44 */ - log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v); - continue; - } else if (streq(l, "state-change-timestamp")) { - dual_timestamp_deserialize(v, &u->state_change_timestamp); - continue; - } else if (streq(l, "inactive-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->inactive_exit_timestamp); - continue; - } else if (streq(l, "active-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->active_enter_timestamp); - continue; - } else if (streq(l, "active-exit-timestamp")) { - dual_timestamp_deserialize(v, &u->active_exit_timestamp); - continue; - } else if (streq(l, "inactive-enter-timestamp")) { - dual_timestamp_deserialize(v, &u->inactive_enter_timestamp); - continue; - } else if (streq(l, "condition-timestamp")) { - dual_timestamp_deserialize(v, &u->condition_timestamp); - continue; - } else if (streq(l, "assert-timestamp")) { - dual_timestamp_deserialize(v, &u->assert_timestamp); - continue; - } else if (streq(l, "condition-result")) { - - r = parse_boolean(v); - if (r < 0) - log_unit_debug(u, "Failed to parse condition result value %s, ignoring.", v); - else - u->condition_result = r; - - continue; - - } else if (streq(l, "assert-result")) { - - r = parse_boolean(v); - if (r < 0) - log_unit_debug(u, "Failed to parse assert result value %s, ignoring.", v); - else - u->assert_result = r; - - continue; - - } else if (streq(l, "transient")) { - - r = parse_boolean(v); - if (r < 0) - log_unit_debug(u, "Failed to parse transient bool %s, ignoring.", v); - else - u->transient = r; - - continue; - - } else if (streq(l, "cpuacct-usage-base")) { - - r = safe_atou64(v, &u->cpuacct_usage_base); - if (r < 0) - log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v); - - continue; - - } else if (streq(l, "cgroup")) { - - r = unit_set_cgroup_path(u, v); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v); - - (void) unit_watch_cgroup(u); - - continue; - } else if (streq(l, "cgroup-realized")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_unit_debug(u, "Failed to parse cgroup-realized bool %s, ignoring.", v); - else - u->cgroup_realized = b; - - continue; - } - - if (unit_can_serialize(u)) { - if (rt) { - r = exec_runtime_deserialize_item(u, rt, l, v, fds); - if (r < 0) { - log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l); - continue; - } - - /* Returns positive if key was handled by the call */ - if (r > 0) - continue; - } - - r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds); - if (r < 0) - log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l); - } - } - - /* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is - * useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from - * before 228 where the base for timeouts was not persistent across reboots. */ - - if (!dual_timestamp_is_set(&u->state_change_timestamp)) - dual_timestamp_get(&u->state_change_timestamp); - - return 0; -} - -int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) { - Unit *device; - _cleanup_free_ char *e = NULL; - int r; - - assert(u); - - /* Adds in links to the device node that this unit is based on */ - if (isempty(what)) - return 0; - - if (!is_device_path(what)) - return 0; - - /* When device units aren't supported (such as in a - * container), don't create dependencies on them. */ - if (!unit_type_supported(UNIT_DEVICE)) - return 0; - - r = unit_name_from_path(what, ".device", &e); - if (r < 0) - return r; - - r = manager_load_unit(u->manager, e, NULL, NULL, &device); - if (r < 0) - return r; - - r = unit_add_two_dependencies(u, UNIT_AFTER, - MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS, - device, true); - if (r < 0) - return r; - - if (wants) { - r = unit_add_dependency(device, UNIT_WANTS, u, false); - if (r < 0) - return r; - } - - return 0; -} - -int unit_coldplug(Unit *u) { - int r = 0, q = 0; - - assert(u); - - /* Make sure we don't enter a loop, when coldplugging - * recursively. */ - if (u->coldplugged) - return 0; - - u->coldplugged = true; - - if (UNIT_VTABLE(u)->coldplug) - r = UNIT_VTABLE(u)->coldplug(u); - - if (u->job) - q = job_coldplug(u->job); - - if (r < 0) - return r; - if (q < 0) - return q; - - return 0; -} - -static bool fragment_mtime_newer(const char *path, usec_t mtime) { - struct stat st; - - if (!path) - return false; - - if (stat(path, &st) < 0) - /* What, cannot access this anymore? */ - return true; - - if (mtime > 0) - /* For non-empty files check the mtime */ - return timespec_load(&st.st_mtim) > mtime; - else if (!null_or_empty(&st)) - /* For masked files check if they are still so */ - return true; - - return false; -} - -bool unit_need_daemon_reload(Unit *u) { - _cleanup_strv_free_ char **t = NULL; - char **path; - - assert(u); - - if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime)) - return true; - - if (fragment_mtime_newer(u->source_path, u->source_mtime)) - return true; - - (void) unit_find_dropin_paths(u, &t); - if (!strv_equal(u->dropin_paths, t)) - return true; - - STRV_FOREACH(path, u->dropin_paths) - if (fragment_mtime_newer(*path, u->dropin_mtime)) - return true; - - return false; -} - -void unit_reset_failed(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->reset_failed) - UNIT_VTABLE(u)->reset_failed(u); - - RATELIMIT_RESET(u->start_limit); - u->start_limit_hit = false; -} - -Unit *unit_following(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->following) - return UNIT_VTABLE(u)->following(u); - - return NULL; -} - -bool unit_stop_pending(Unit *u) { - assert(u); - - /* This call does check the current state of the unit. It's - * hence useful to be called from state change calls of the - * unit itself, where the state isn't updated yet. This is - * different from unit_inactive_or_pending() which checks both - * the current state and for a queued job. */ - - return u->job && u->job->type == JOB_STOP; -} - -bool unit_inactive_or_pending(Unit *u) { - assert(u); - - /* Returns true if the unit is inactive or going down */ - - if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) - return true; - - if (unit_stop_pending(u)) - return true; - - return false; -} - -bool unit_active_or_pending(Unit *u) { - assert(u); - - /* Returns true if the unit is active or going up */ - - if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) - return true; - - if (u->job && - (u->job->type == JOB_START || - u->job->type == JOB_RELOAD_OR_START || - u->job->type == JOB_RESTART)) - return true; - - return false; -} - -int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { - assert(u); - assert(w >= 0 && w < _KILL_WHO_MAX); - assert(SIGNAL_VALID(signo)); - - if (!UNIT_VTABLE(u)->kill) - return -EOPNOTSUPP; - - return UNIT_VTABLE(u)->kill(u, w, signo, error); -} - -static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { - Set *pid_set; - int r; - - pid_set = set_new(NULL); - if (!pid_set) - return NULL; - - /* Exclude the main/control pids from being killed via the cgroup */ - if (main_pid > 0) { - r = set_put(pid_set, PID_TO_PTR(main_pid)); - if (r < 0) - goto fail; - } - - if (control_pid > 0) { - r = set_put(pid_set, PID_TO_PTR(control_pid)); - if (r < 0) - goto fail; - } - - return pid_set; - -fail: - set_free(pid_set); - return NULL; -} - -int unit_kill_common( - Unit *u, - KillWho who, - int signo, - pid_t main_pid, - pid_t control_pid, - sd_bus_error *error) { - - int r = 0; - bool killed = false; - - if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) { - if (main_pid < 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); - else if (main_pid == 0) - return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - } - - if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) { - if (control_pid < 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); - else if (control_pid == 0) - return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - } - - if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) - if (control_pid > 0) { - if (kill(control_pid, signo) < 0) - r = -errno; - else - killed = true; - } - - if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) - if (main_pid > 0) { - if (kill(main_pid, signo) < 0) - r = -errno; - else - killed = true; - } - - if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) { - _cleanup_set_free_ Set *pid_set = NULL; - int q; - - /* Exclude the main/control pids from being killed via the cgroup */ - pid_set = unit_pid_set(main_pid, control_pid); - if (!pid_set) - return -ENOMEM; - - q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, NULL, NULL); - if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) - r = q; - else - killed = true; - } - - if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL)) - return -ESRCH; - - return r; -} - -int unit_following_set(Unit *u, Set **s) { - assert(u); - assert(s); - - if (UNIT_VTABLE(u)->following_set) - return UNIT_VTABLE(u)->following_set(u, s); - - *s = NULL; - return 0; -} - -UnitFileState unit_get_unit_file_state(Unit *u) { - int r; - - assert(u); - - if (u->unit_file_state < 0 && u->fragment_path) { - r = unit_file_get_state( - u->manager->unit_file_scope, - NULL, - basename(u->fragment_path), - &u->unit_file_state); - if (r < 0) - u->unit_file_state = UNIT_FILE_BAD; - } - - return u->unit_file_state; -} - -int unit_get_unit_file_preset(Unit *u) { - assert(u); - - if (u->unit_file_preset < 0 && u->fragment_path) - u->unit_file_preset = unit_file_query_preset( - u->manager->unit_file_scope, - NULL, - basename(u->fragment_path)); - - return u->unit_file_preset; -} - -Unit* unit_ref_set(UnitRef *ref, Unit *u) { - assert(ref); - assert(u); - - if (ref->unit) - unit_ref_unset(ref); - - ref->unit = u; - LIST_PREPEND(refs, u->refs, ref); - return u; -} - -void unit_ref_unset(UnitRef *ref) { - assert(ref); - - if (!ref->unit) - return; - - /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might - * be unreferenced now. */ - unit_add_to_gc_queue(ref->unit); - - LIST_REMOVE(refs, ref->unit->refs, ref); - ref->unit = NULL; -} - -int unit_patch_contexts(Unit *u) { - CGroupContext *cc; - ExecContext *ec; - unsigned i; - int r; - - assert(u); - - /* Patch in the manager defaults into the exec and cgroup - * contexts, _after_ the rest of the settings have been - * initialized */ - - ec = unit_get_exec_context(u); - if (ec) { - /* This only copies in the ones that need memory */ - for (i = 0; i < _RLIMIT_MAX; i++) - if (u->manager->rlimit[i] && !ec->rlimit[i]) { - ec->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); - if (!ec->rlimit[i]) - return -ENOMEM; - } - - if (MANAGER_IS_USER(u->manager) && - !ec->working_directory) { - - r = get_home_dir(&ec->working_directory); - if (r < 0) - return r; - - /* Allow user services to run, even if the - * home directory is missing */ - ec->working_directory_missing_ok = true; - } - - if (MANAGER_IS_USER(u->manager) && - (ec->syscall_whitelist || - !set_isempty(ec->syscall_filter) || - !set_isempty(ec->syscall_archs) || - ec->address_families_whitelist || - !set_isempty(ec->address_families))) - ec->no_new_privileges = true; - - if (ec->private_devices) - ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD); - } - - cc = unit_get_cgroup_context(u); - if (cc) { - - if (ec && - ec->private_devices && - cc->device_policy == CGROUP_AUTO) - cc->device_policy = CGROUP_CLOSED; - } - - return 0; -} - -ExecContext *unit_get_exec_context(Unit *u) { - size_t offset; - assert(u); - - if (u->type < 0) - return NULL; - - offset = UNIT_VTABLE(u)->exec_context_offset; - if (offset <= 0) - return NULL; - - return (ExecContext*) ((uint8_t*) u + offset); -} - -KillContext *unit_get_kill_context(Unit *u) { - size_t offset; - assert(u); - - if (u->type < 0) - return NULL; - - offset = UNIT_VTABLE(u)->kill_context_offset; - if (offset <= 0) - return NULL; - - return (KillContext*) ((uint8_t*) u + offset); -} - -CGroupContext *unit_get_cgroup_context(Unit *u) { - size_t offset; - - if (u->type < 0) - return NULL; - - offset = UNIT_VTABLE(u)->cgroup_context_offset; - if (offset <= 0) - return NULL; - - return (CGroupContext*) ((uint8_t*) u + offset); -} - -ExecRuntime *unit_get_exec_runtime(Unit *u) { - size_t offset; - - if (u->type < 0) - return NULL; - - offset = UNIT_VTABLE(u)->exec_runtime_offset; - if (offset <= 0) - return NULL; - - return *(ExecRuntime**) ((uint8_t*) u + offset); -} - -static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) { - assert(u); - - if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT)) - return NULL; - - if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */ - return u->manager->lookup_paths.transient; - - if (mode == UNIT_RUNTIME) - return u->manager->lookup_paths.runtime_control; - - if (mode == UNIT_PERSISTENT) - return u->manager->lookup_paths.persistent_control; - - return NULL; -} - -int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - _cleanup_free_ char *p = NULL, *q = NULL; - const char *dir, *wrapped; - int r; - - assert(u); - - if (u->transient_file) { - /* When this is a transient unit file in creation, then let's not create a new drop-in but instead - * write to the transient unit file. */ - fputs(data, u->transient_file); - fputc('\n', u->transient_file); - return 0; - } - - if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) - return 0; - - dir = unit_drop_in_dir(u, mode); - if (!dir) - return -EINVAL; - - wrapped = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\"\n" - "# or an equivalent operation. Do not edit.\n", - data, - "\n"); - - r = drop_in_file(dir, u->id, 50, name, &p, &q); - if (r < 0) - return r; - - (void) mkdir_p(p, 0755); - r = write_string_file_atomic_label(q, wrapped); - if (r < 0) - return r; - - r = strv_push(&u->dropin_paths, q); - if (r < 0) - return r; - q = NULL; - - strv_uniq(u->dropin_paths); - - u->dropin_mtime = now(CLOCK_REALTIME); - - return 0; -} - -int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) { - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - assert(u); - assert(name); - assert(format); - - if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) - return 0; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return unit_write_drop_in(u, mode, name, p); -} - -int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - const char *ndata; - - assert(u); - assert(name); - assert(data); - - if (!UNIT_VTABLE(u)->private_section) - return -EINVAL; - - if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) - return 0; - - ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data); - - return unit_write_drop_in(u, mode, name, ndata); -} - -int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) { - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - assert(u); - assert(name); - assert(format); - - if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) - return 0; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return unit_write_drop_in_private(u, mode, name, p); -} - -int unit_make_transient(Unit *u) { - FILE *f; - char *path; - - assert(u); - - if (!UNIT_VTABLE(u)->can_transient) - return -EOPNOTSUPP; - - path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL); - if (!path) - return -ENOMEM; - - /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are - * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */ - - RUN_WITH_UMASK(0022) { - f = fopen(path, "we"); - if (!f) { - free(path); - return -errno; - } - } - - if (u->transient_file) - fclose(u->transient_file); - u->transient_file = f; - - free(u->fragment_path); - u->fragment_path = 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; - - u->load_state = UNIT_STUB; - u->load_error = 0; - u->transient = true; - - unit_add_to_dbus_queue(u); - unit_add_to_gc_queue(u); - - fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n", - u->transient_file); - - return 0; -} - -static void log_kill(pid_t pid, int sig, void *userdata) { - _cleanup_free_ char *comm = NULL; - - (void) get_process_comm(pid, &comm); - - /* Don't log about processes marked with brackets, under the assumption that these are temporary processes - only, like for example systemd's own PAM stub process. */ - if (comm && comm[0] == '(') - return; - - log_unit_notice(userdata, - "Killing process " PID_FMT " (%s) with signal SIG%s.", - pid, - strna(comm), - signal_to_string(sig)); -} - -static int operation_to_signal(KillContext *c, KillOperation k) { - assert(c); - - switch (k) { - - case KILL_TERMINATE: - case KILL_TERMINATE_AND_LOG: - return c->kill_signal; - - case KILL_KILL: - return SIGKILL; - - case KILL_ABORT: - return SIGABRT; - - default: - assert_not_reached("KillOperation unknown"); - } -} - -int unit_kill_context( - Unit *u, - KillContext *c, - KillOperation k, - pid_t main_pid, - pid_t control_pid, - bool main_pid_alien) { - - bool wait_for_exit = false, send_sighup; - cg_kill_log_func_t log_func; - int sig, r; - - assert(u); - assert(c); - - /* Kill the processes belonging to this unit, in preparation for shutting the unit down. Returns > 0 if we - * killed something worth waiting for, 0 otherwise. */ - - if (c->kill_mode == KILL_NONE) - return 0; - - sig = operation_to_signal(c, k); - - send_sighup = - c->send_sighup && - IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) && - sig != SIGHUP; - - log_func = - k != KILL_TERMINATE || - IN_SET(sig, SIGKILL, SIGABRT) ? log_kill : NULL; - - if (main_pid > 0) { - if (log_func) - log_func(main_pid, sig, u); - - r = kill_and_sigcont(main_pid, sig); - if (r < 0 && r != -ESRCH) { - _cleanup_free_ char *comm = NULL; - (void) get_process_comm(main_pid, &comm); - - log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm)); - } else { - if (!main_pid_alien) - wait_for_exit = true; - - if (r != -ESRCH && send_sighup) - (void) kill(main_pid, SIGHUP); - } - } - - if (control_pid > 0) { - if (log_func) - log_func(control_pid, sig, u); - - r = kill_and_sigcont(control_pid, sig); - if (r < 0 && r != -ESRCH) { - _cleanup_free_ char *comm = NULL; - (void) get_process_comm(control_pid, &comm); - - log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm)); - } else { - wait_for_exit = true; - - if (r != -ESRCH && send_sighup) - (void) kill(control_pid, SIGHUP); - } - } - - if (u->cgroup_path && - (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) { - _cleanup_set_free_ Set *pid_set = NULL; - - /* Exclude the main/control pids from being killed via the cgroup */ - pid_set = unit_pid_set(main_pid, control_pid); - if (!pid_set) - return -ENOMEM; - - r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, - sig, - CGROUP_SIGCONT|CGROUP_IGNORE_SELF, - pid_set, - log_func, u); - if (r < 0) { - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path); - - } else if (r > 0) { - - /* FIXME: For now, on the legacy hierarchy, we - * will not wait for the cgroup members to die - * if we are running in a container or if this - * is a delegation unit, simply because cgroup - * notification is unreliable in these - * cases. It doesn't work at all in - * containers, and outside of containers it - * can be confused easily by left-over - * directories in the cgroup — which however - * should not exist in non-delegated units. On - * the unified hierarchy that's different, - * there we get proper events. Hence rely on - * them.*/ - - if (cg_unified() > 0 || - (detect_container() == 0 && !unit_cgroup_delegate(u))) - wait_for_exit = true; - - if (send_sighup) { - set_free(pid_set); - - pid_set = unit_pid_set(main_pid, control_pid); - if (!pid_set) - return -ENOMEM; - - cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, - SIGHUP, - CGROUP_IGNORE_SELF, - pid_set, - NULL, NULL); - } - } - } - - return wait_for_exit; -} - -int unit_require_mounts_for(Unit *u, const char *path) { - char prefix[strlen(path) + 1], *p; - int r; - - assert(u); - assert(path); - - /* Registers a unit for requiring a certain path and all its - * prefixes. We keep a simple array of these paths in the - * unit, since its usually short. However, we build a prefix - * table for all possible prefixes so that new appearing mount - * units can easily determine which units to make themselves a - * dependency of. */ - - if (!path_is_absolute(path)) - return -EINVAL; - - p = strdup(path); - if (!p) - return -ENOMEM; - - path_kill_slashes(p); - - if (!path_is_safe(p)) { - free(p); - return -EPERM; - } - - if (strv_contains(u->requires_mounts_for, p)) { - free(p); - return 0; - } - - r = strv_consume(&u->requires_mounts_for, p); - if (r < 0) - return r; - - PATH_FOREACH_PREFIX_MORE(prefix, p) { - Set *x; - - x = hashmap_get(u->manager->units_requiring_mounts_for, prefix); - if (!x) { - char *q; - - r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &string_hash_ops); - if (r < 0) - return r; - - q = strdup(prefix); - if (!q) - return -ENOMEM; - - x = set_new(NULL); - if (!x) { - free(q); - return -ENOMEM; - } - - r = hashmap_put(u->manager->units_requiring_mounts_for, q, x); - if (r < 0) { - free(q); - set_free(x); - return r; - } - } - - r = set_put(x, u); - if (r < 0) - return r; - } - - return 0; -} - -int unit_setup_exec_runtime(Unit *u) { - ExecRuntime **rt; - size_t offset; - Iterator i; - Unit *other; - - offset = UNIT_VTABLE(u)->exec_runtime_offset; - assert(offset > 0); - - /* Check if there already is an ExecRuntime for this unit? */ - rt = (ExecRuntime**) ((uint8_t*) u + offset); - if (*rt) - return 0; - - /* Try to get it from somebody else */ - SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) { - - *rt = unit_get_exec_runtime(other); - if (*rt) { - exec_runtime_ref(*rt); - return 0; - } - } - - return exec_runtime_make(rt, unit_get_exec_context(u), u->id); -} - -bool unit_type_supported(UnitType t) { - if (_unlikely_(t < 0)) - return false; - if (_unlikely_(t >= _UNIT_TYPE_MAX)) - return false; - - if (!unit_vtable[t]->supported) - return true; - - return unit_vtable[t]->supported(); -} - -void unit_warn_if_dir_nonempty(Unit *u, const char* where) { - int r; - - assert(u); - assert(where); - - r = dir_is_empty(where); - if (r > 0) - return; - if (r < 0) { - log_unit_warning_errno(u, r, "Failed to check directory %s: %m", where); - return; - } - - log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), - LOG_UNIT_ID(u), - LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), - "WHERE=%s", where, - NULL); -} - -int unit_fail_if_symlink(Unit *u, const char* where) { - int r; - - assert(u); - assert(where); - - r = is_symlink(where); - if (r < 0) { - log_unit_debug_errno(u, r, "Failed to check symlink %s, ignoring: %m", where); - return 0; - } - if (r == 0) - return 0; - - log_struct(LOG_ERR, - LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), - LOG_UNIT_ID(u), - LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where), - "WHERE=%s", where, - NULL); - - return -ELOOP; -} - -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 though nothing was - * actually loaded, as those unit types don't require a file - * on disk to validly load. */ - - 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); -} - -pid_t unit_control_pid(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->control_pid) - return UNIT_VTABLE(u)->control_pid(u); - - return 0; -} - -pid_t unit_main_pid(Unit *u) { - assert(u); - - if (UNIT_VTABLE(u)->main_pid) - return UNIT_VTABLE(u)->main_pid(u); - - return 0; -} diff --git a/src/core/unit.h b/src/core/unit.h deleted file mode 100644 index 1eabfa51e2..0000000000 --- a/src/core/unit.h +++ /dev/null @@ -1,643 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -typedef struct Unit Unit; -typedef struct UnitVTable UnitVTable; -typedef struct UnitRef UnitRef; -typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; - -#include "condition.h" -#include "failure-action.h" -#include "install.h" -#include "list.h" -#include "unit-name.h" - -typedef enum KillOperation { - KILL_TERMINATE, - KILL_TERMINATE_AND_LOG, - KILL_KILL, - KILL_ABORT, - _KILL_OPERATION_MAX, - _KILL_OPERATION_INVALID = -1 -} KillOperation; - -static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) { - return t == UNIT_ACTIVE || t == UNIT_RELOADING; -} - -static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) { - return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_RELOADING; -} - -static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) { - return t == UNIT_INACTIVE || t == UNIT_FAILED || t == UNIT_DEACTIVATING; -} - -static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) { - return t == UNIT_INACTIVE || t == UNIT_FAILED; -} - -#include "job.h" - -struct UnitRef { - /* Keeps tracks of references to a unit. This is useful so - * that we can merge two units if necessary and correct all - * references to them */ - - Unit* unit; - LIST_FIELDS(UnitRef, refs); -}; - -struct Unit { - Manager *manager; - - UnitType type; - UnitLoadState load_state; - Unit *merged_into; - - char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ - char *instance; - - Set *names; - Set *dependencies[_UNIT_DEPENDENCY_MAX]; - - char **requires_mounts_for; - - char *description; - char **documentation; - - char *fragment_path; /* if loaded from a config file this is the primary path to it */ - char *source_path; /* if converted, the source file */ - char **dropin_paths; - - usec_t fragment_mtime; - usec_t source_mtime; - usec_t dropin_mtime; - - /* If this is a transient unit we are currently writing, this is where we are writing it to */ - FILE *transient_file; - - /* If there is something to do with this unit, then this is the installed job for it */ - Job *job; - - /* JOB_NOP jobs are special and can be installed without disturbing the real job. */ - Job *nop_job; - - /* The slot used for watching NameOwnerChanged signals */ - sd_bus_slot *match_bus_slot; - - /* Job timeout and action to take */ - usec_t job_timeout; - FailureAction job_timeout_action; - char *job_timeout_reboot_arg; - - /* References to this */ - LIST_HEAD(UnitRef, refs); - - /* Conditions to check */ - LIST_HEAD(Condition, conditions); - LIST_HEAD(Condition, asserts); - - dual_timestamp condition_timestamp; - dual_timestamp assert_timestamp; - - /* Updated whenever the low-level state changes */ - dual_timestamp state_change_timestamp; - - /* Updated whenever the (high-level) active state enters or leaves the active or inactive states */ - dual_timestamp inactive_exit_timestamp; - dual_timestamp active_enter_timestamp; - dual_timestamp active_exit_timestamp; - dual_timestamp inactive_enter_timestamp; - - UnitRef slice; - - /* Per type list */ - LIST_FIELDS(Unit, units_by_type); - - /* All units which have requires_mounts_for set */ - LIST_FIELDS(Unit, has_requires_mounts_for); - - /* Load queue */ - LIST_FIELDS(Unit, load_queue); - - /* D-Bus queue */ - LIST_FIELDS(Unit, dbus_queue); - - /* Cleanup queue */ - LIST_FIELDS(Unit, cleanup_queue); - - /* GC queue */ - LIST_FIELDS(Unit, gc_queue); - - /* CGroup realize members queue */ - LIST_FIELDS(Unit, cgroup_queue); - - /* Units with the same CGroup netclass */ - LIST_FIELDS(Unit, cgroup_netclass); - - /* PIDs we keep an eye on. Note that a unit might have many - * more, but these are the ones we care enough about to - * process SIGCHLD for */ - Set *pids; - - /* Used in sigchld event invocation to avoid repeat events being invoked */ - uint64_t sigchldgen; - - /* Used during GC sweeps */ - unsigned gc_marker; - - /* Error code when we didn't manage to load the unit (negative) */ - int load_error; - - /* Put a ratelimit on unit starting */ - RateLimit start_limit; - FailureAction start_limit_action; - char *reboot_arg; - - /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ - RateLimit auto_stop_ratelimit; - - /* Cached unit file state and preset */ - UnitFileState unit_file_state; - int unit_file_preset; - - /* Where the cpuacct.usage cgroup counter was at the time the unit was started */ - nsec_t cpuacct_usage_base; - - /* Counterparts in the cgroup filesystem */ - char *cgroup_path; - CGroupMask cgroup_realized_mask; - CGroupMask cgroup_enabled_mask; - CGroupMask cgroup_subtree_mask; - CGroupMask cgroup_members_mask; - int cgroup_inotify_wd; - - uint32_t cgroup_netclass_id; - - /* How to start OnFailure units */ - JobMode on_failure_job_mode; - - /* Garbage collect us we nobody wants or requires us anymore */ - bool stop_when_unneeded; - - /* Create default dependencies */ - bool default_dependencies; - - /* Refuse manual starting, allow starting only indirectly via dependency. */ - bool refuse_manual_start; - - /* Don't allow the user to stop this unit manually, allow stopping only indirectly via dependency. */ - bool refuse_manual_stop; - - /* Allow isolation requests */ - bool allow_isolate; - - /* Ignore this unit when isolating */ - bool ignore_on_isolate; - - /* Did the last condition check succeed? */ - bool condition_result; - bool assert_result; - - /* Is this a transient unit? */ - bool transient; - - bool in_load_queue:1; - bool in_dbus_queue:1; - bool in_cleanup_queue:1; - bool in_gc_queue:1; - bool in_cgroup_queue:1; - - bool sent_dbus_new_signal:1; - - bool no_gc:1; - - bool in_audit:1; - - bool cgroup_realized:1; - bool cgroup_members_mask_valid:1; - bool cgroup_subtree_mask_valid:1; - - bool start_limit_hit:1; - - /* Did we already invoke unit_coldplug() for this unit? */ - bool coldplugged:1; -}; - -struct UnitStatusMessageFormats { - const char *starting_stopping[2]; - const char *finished_start_job[_JOB_RESULT_MAX]; - const char *finished_stop_job[_JOB_RESULT_MAX]; -}; - -typedef enum UnitSetPropertiesMode { - UNIT_CHECK = 0, - UNIT_RUNTIME = 1, - UNIT_PERSISTENT = 2, -} UnitSetPropertiesMode; - -#include "automount.h" -#include "busname.h" -#include "device.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 */ - size_t object_size; - - /* If greater than 0, the offset into the object where - * ExecContext is found, if the unit type has that */ - size_t exec_context_offset; - - /* If greater than 0, the offset into the object where - * CGroupContext is found, if the unit type has that */ - size_t cgroup_context_offset; - - /* If greater than 0, the offset into the object where - * KillContext is found, if the unit type has that */ - size_t kill_context_offset; - - /* If greater than 0, the offset into the object where the - * pointer to ExecRuntime is found, if the unit type has - * that */ - size_t exec_runtime_offset; - - /* The name of the configuration file section with the private settings of this unit */ - const char *private_section; - - /* Config file sections this unit type understands, separated - * by NUL chars */ - const char *sections; - - /* This should reset all type-specific variables. This should - * not allocate memory, and is called with zero-initialized - * data. It should hence only initialize variables that need - * to be set != 0. */ - void (*init)(Unit *u); - - /* This should free all type-specific variables. It should be - * idempotent. */ - void (*done)(Unit *u); - - /* Actually load data from disk. This may fail, and should set - * load_state to UNIT_LOADED, UNIT_MERGED or leave it at - * UNIT_STUB if no configuration could be found. */ - int (*load)(Unit *u); - - /* If a lot of units got created via enumerate(), this is - * where to actually set the state and call unit_notify(). */ - int (*coldplug)(Unit *u); - - void (*dump)(Unit *u, FILE *f, const char *prefix); - - int (*start)(Unit *u); - int (*stop)(Unit *u); - int (*reload)(Unit *u); - - int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error); - - bool (*can_reload)(Unit *u); - - /* Write all data that cannot be restored from other sources - * away using unit_serialize_item() */ - int (*serialize)(Unit *u, FILE *f, FDSet *fds); - - /* Restore one item from the serialization */ - 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 */ - 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 */ - UnitActiveState (*active_state)(Unit *u); - - /* Returns the substate specific to this unit type as - * string. This is purely information so that we can give the - * user a more fine grained explanation in which actual state a - * unit is in. */ - const char* (*sub_state_to_string)(Unit *u); - - /* Return true when there is reason to keep this entry around - * even nothing references it and it isn't active in any - * way */ - bool (*check_gc)(Unit *u); - - /* When the unit is not running and no job for it queued we - * shall release its runtime resources */ - void (*release_resources)(Unit *u); - - /* Invoked on every child that died */ - void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); - - /* Reset failed state if we are in failed state */ - void (*reset_failed)(Unit *u); - - /* Called whenever any of the cgroups this unit watches for - * ran empty */ - void (*notify_cgroup_empty)(Unit *u); - - /* Called whenever a process of this unit sends us a message */ - void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds); - - /* Called whenever a name this Unit registered for comes or - * goes away. */ - void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); - - /* Called for each property that is being set */ - int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); - - /* Called after at least one property got changed to apply the necessary change */ - int (*bus_commit_properties)(Unit *u); - - /* Return the unit this unit is following */ - Unit *(*following)(Unit *u); - - /* Return the set of units that are following each other */ - int (*following_set)(Unit *u, Set **s); - - /* Invoked each time a unit this unit is triggering changes - * state or gains/loses a job */ - void (*trigger_notify)(Unit *u, Unit *trigger); - - /* Called whenever CLOCK_REALTIME made a jump */ - void (*time_change)(Unit *u); - - /* Returns the next timeout of a unit */ - int (*get_timeout)(Unit *u, usec_t *timeout); - - /* Returns the main PID if there is any defined, or 0. */ - pid_t (*main_pid)(Unit *u); - - /* Returns the main PID if there is any defined, or 0. */ - pid_t (*control_pid)(Unit *u); - - /* This is called for each unit type and should be used to - * enumerate existing devices and load them. However, - * 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. */ - void (*enumerate)(Manager *m); - - /* Type specific cleanups. */ - void (*shutdown)(Manager *m); - - /* If this function is set and return false all jobs for units - * of this type will immediately fail. */ - bool (*supported)(void); - - /* The bus vtable */ - const sd_bus_vtable *bus_vtable; - - /* The strings to print in status messages */ - UnitStatusMessageFormats status_message_formats; - - /* True if transient units of this type are OK */ - bool can_transient:1; -}; - -extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; - -#define UNIT_VTABLE(u) unit_vtable[(u)->type] - -/* For casting a unit into the various unit types */ -#define DEFINE_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* UPPERCASE(Unit *u) { \ - if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \ - return NULL; \ - \ - return (MixedCase*) u; \ - } - -/* For casting the various unit types into a unit */ -#define UNIT(u) (&(u)->meta) - -#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) -#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) -#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0) - -#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS])) - -DEFINE_CAST(SERVICE, Service); -DEFINE_CAST(SOCKET, Socket); -DEFINE_CAST(BUSNAME, BusName); -DEFINE_CAST(TARGET, Target); -DEFINE_CAST(DEVICE, Device); -DEFINE_CAST(MOUNT, Mount); -DEFINE_CAST(AUTOMOUNT, Automount); -DEFINE_CAST(SWAP, Swap); -DEFINE_CAST(TIMER, Timer); -DEFINE_CAST(PATH, Path); -DEFINE_CAST(SLICE, Slice); -DEFINE_CAST(SCOPE, Scope); - -Unit *unit_new(Manager *m, size_t size); -void unit_free(Unit *u); - -int unit_add_name(Unit *u, const char *name); - -int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); -int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference); - -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_exec_dependencies(Unit *u, ExecContext *c); - -int unit_choose_id(Unit *u, const char *name); -int unit_set_description(Unit *u, const char *description); - -bool unit_check_gc(Unit *u); - -void unit_add_to_load_queue(Unit *u); -void unit_add_to_dbus_queue(Unit *u); -void unit_add_to_cleanup_queue(Unit *u); -void unit_add_to_gc_queue(Unit *u); - -int unit_merge(Unit *u, Unit *other); -int unit_merge_by_name(Unit *u, const char *other); - -Unit *unit_follow_merge(Unit *u) _pure_; - -int unit_load_fragment_and_dropin(Unit *u); -int unit_load_fragment_and_dropin_optional(Unit *u); -int unit_load(Unit *unit); - -int unit_set_slice(Unit *u, Unit *slice); -int unit_set_default_slice(Unit *u); - -const char *unit_description(Unit *u) _pure_; - -bool unit_has_name(Unit *u, const char *name); - -UnitActiveState unit_active_state(Unit *u); - -const char* unit_sub_state_to_string(Unit *u); - -void unit_dump(Unit *u, FILE *f, const char *prefix); - -bool unit_can_reload(Unit *u) _pure_; -bool unit_can_start(Unit *u) _pure_; -bool unit_can_isolate(Unit *u) _pure_; - -int unit_start(Unit *u); -int unit_stop(Unit *u); -int unit_reload(Unit *u); - -int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error); -int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error); - -void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success); - -int unit_watch_pid(Unit *u, pid_t pid); -void unit_unwatch_pid(Unit *u, pid_t pid); -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(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); - -bool unit_job_is_applicable(Unit *u, JobType j); - -int set_unit_path(const char *p); - -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); -int unit_deserialize(Unit *u, FILE *f, FDSet *fds); - -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); - -void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0); -void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t); - -bool unit_need_daemon_reload(Unit *u); - -void unit_reset_failed(Unit *u); - -Unit *unit_following(Unit *u); -int unit_following_set(Unit *u, Set **s); - -const char *unit_slice_name(Unit *u); - -bool unit_stop_pending(Unit *u) _pure_; -bool unit_inactive_or_pending(Unit *u) _pure_; -bool unit_active_or_pending(Unit *u); - -int unit_add_default_target_dependency(Unit *u, Unit *target); - -void unit_start_on_failure(Unit *u); -void unit_trigger_notify(Unit *u); - -UnitFileState unit_get_unit_file_state(Unit *u); -int unit_get_unit_file_preset(Unit *u); - -Unit* unit_ref_set(UnitRef *ref, Unit *u); -void unit_ref_unset(UnitRef *ref); - -#define UNIT_DEREF(ref) ((ref).unit) -#define UNIT_ISSET(ref) (!!(ref).unit) - -int unit_patch_contexts(Unit *u); - -ExecContext *unit_get_exec_context(Unit *u) _pure_; -KillContext *unit_get_kill_context(Unit *u) _pure_; -CGroupContext *unit_get_cgroup_context(Unit *u) _pure_; - -ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_; - -int unit_setup_exec_runtime(Unit *u); - -int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data); -int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5); - -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_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); - -int unit_require_mounts_for(Unit *u, const char *path); - -bool unit_type_supported(UnitType t); - -bool unit_is_pristine(Unit *u); - -pid_t unit_control_pid(Unit *u); -pid_t unit_main_pid(Unit *u); - -static inline bool unit_supported(Unit *u) { - return unit_type_supported(u->type); -} - -void unit_warn_if_dir_nonempty(Unit *u, const char* where); -int unit_fail_if_symlink(Unit *u, const char* where); - -int unit_start_limit_test(Unit *u); - -/* Macros which append UNIT= or USER_UNIT= to the message */ - -#define log_unit_full(unit, level, error, ...) \ - ({ \ - Unit *_u = (unit); \ - _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \ - log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ - }) - -#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__) -#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__) -#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__) -#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__) -#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__) - -#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__) -#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__) -#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__) -#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__) -#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__) - -#define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__ -#define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id diff --git a/src/core/user.conf b/src/core/user.conf deleted file mode 100644 index b427f1ef6d..0000000000 --- a/src/core/user.conf +++ /dev/null @@ -1,44 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# You can override the directives in this file by creating files in -# /etc/systemd/user.conf.d/*.conf. -# -# See systemd-user.conf(5) for details - -[Manager] -#LogLevel=info -#LogTarget=console -#LogColor=yes -#LogLocation=no -#SystemCallArchitectures= -#TimerSlackNSec= -#DefaultTimerAccuracySec=1min -#DefaultStandardOutput=inherit -#DefaultStandardError=inherit -#DefaultTimeoutStartSec=90s -#DefaultTimeoutStopSec=90s -#DefaultRestartSec=100ms -#DefaultStartLimitIntervalSec=10s -#DefaultStartLimitBurst=5 -#DefaultEnvironment= -#DefaultLimitCPU= -#DefaultLimitFSIZE= -#DefaultLimitDATA= -#DefaultLimitSTACK= -#DefaultLimitCORE= -#DefaultLimitRSS= -#DefaultLimitNOFILE= -#DefaultLimitAS= -#DefaultLimitNPROC= -#DefaultLimitMEMLOCK= -#DefaultLimitLOCKS= -#DefaultLimitSIGPENDING= -#DefaultLimitMSGQUEUE= -#DefaultLimitNICE= -#DefaultLimitRTPRIO= -#DefaultLimitRTTIME= diff --git a/src/coredump/Makefile b/src/coredump/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/coredump/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/coredump/coredump-vacuum.c b/src/coredump/coredump-vacuum.c deleted file mode 100644 index f02b6dbd87..0000000000 --- a/src/coredump/coredump-vacuum.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "coredump-vacuum.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "macro.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 */ -#define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ -#define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */ - -struct vacuum_candidate { - unsigned n_files; - char *oldest_file; - usec_t oldest_mtime; -}; - -static void vacuum_candidate_free(struct vacuum_candidate *c) { - if (!c) - return; - - free(c->oldest_file); - free(c); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free); - -static void vacuum_candidate_hasmap_free(Hashmap *h) { - struct vacuum_candidate *c; - - while ((c = hashmap_steal_first(h))) - vacuum_candidate_free(c); - - hashmap_free(h); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hasmap_free); - -static int uid_from_file_name(const char *filename, uid_t *uid) { - const char *p, *e, *u; - - p = startswith(filename, "core."); - if (!p) - return -EINVAL; - - /* Skip the comm field */ - p = strchr(p, '.'); - if (!p) - return -EINVAL; - p++; - - /* Find end up UID */ - e = strchr(p, '.'); - if (!e) - return -EINVAL; - - u = strndupa(p, e-p); - return parse_uid(u, uid); -} - -static bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) { - uint64_t fs_size = 0, fs_free = (uint64_t) -1; - struct statvfs sv; - - assert(fd >= 0); - - if (fstatvfs(fd, &sv) >= 0) { - fs_size = sv.f_frsize * sv.f_blocks; - fs_free = sv.f_frsize * sv.f_bfree; - } - - if (max_use == (uint64_t) -1) { - - if (fs_size > 0) { - max_use = PAGE_ALIGN(fs_size / 10); /* 10% */ - - if (max_use > DEFAULT_MAX_USE_UPPER) - max_use = DEFAULT_MAX_USE_UPPER; - - if (max_use < DEFAULT_MAX_USE_LOWER) - max_use = DEFAULT_MAX_USE_LOWER; - } else - max_use = DEFAULT_MAX_USE_LOWER; - } else - max_use = PAGE_ALIGN(max_use); - - if (max_use > 0 && sum > max_use) - return true; - - if (keep_free == (uint64_t) -1) { - - if (fs_size > 0) { - keep_free = PAGE_ALIGN((fs_size * 3) / 20); /* 15% */ - - if (keep_free > DEFAULT_KEEP_FREE_UPPER) - keep_free = DEFAULT_KEEP_FREE_UPPER; - } else - keep_free = DEFAULT_KEEP_FREE; - } else - keep_free = PAGE_ALIGN(keep_free); - - if (keep_free > 0 && fs_free < keep_free) - return true; - - return false; -} - -int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { - _cleanup_closedir_ DIR *d = NULL; - struct stat exclude_st; - int r; - - if (keep_free == 0 && max_use == 0) - return 0; - - if (exclude_fd >= 0) { - if (fstat(exclude_fd, &exclude_st) < 0) - return log_error_errno(errno, "Failed to fstat(): %m"); - } - - /* This algorithm will keep deleting the oldest file of the - * user with the most coredumps until we are back in the size - * limits. Note that vacuuming for journal files is different, - * because we rely on rate-limiting of the messages there, - * to avoid being flooded. */ - - d = opendir("/var/lib/systemd/coredump"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Can't open coredump directory: %m"); - } - - for (;;) { - _cleanup_(vacuum_candidate_hasmap_freep) Hashmap *h = NULL; - struct vacuum_candidate *worst = NULL; - struct dirent *de; - uint64_t sum = 0; - - rewinddir(d); - - FOREACH_DIRENT(de, d, goto fail) { - struct vacuum_candidate *c; - struct stat st; - uid_t uid; - usec_t t; - - r = uid_from_file_name(de->d_name, &uid); - if (r < 0) - continue; - - if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == ENOENT) - continue; - - log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name); - continue; - } - - if (!S_ISREG(st.st_mode)) - continue; - - if (exclude_fd >= 0 && - exclude_st.st_dev == st.st_dev && - exclude_st.st_ino == st.st_ino) - continue; - - r = hashmap_ensure_allocated(&h, NULL); - if (r < 0) - return log_oom(); - - t = timespec_load(&st.st_mtim); - - c = hashmap_get(h, UID_TO_PTR(uid)); - if (c) { - - if (t < c->oldest_mtime) { - char *n; - - n = strdup(de->d_name); - if (!n) - return log_oom(); - - free(c->oldest_file); - c->oldest_file = n; - c->oldest_mtime = t; - } - - } else { - _cleanup_(vacuum_candidate_freep) struct vacuum_candidate *n = NULL; - - n = new0(struct vacuum_candidate, 1); - if (!n) - return log_oom(); - - n->oldest_file = strdup(de->d_name); - if (!n->oldest_file) - return log_oom(); - - n->oldest_mtime = t; - - r = hashmap_put(h, UID_TO_PTR(uid), n); - if (r < 0) - return log_oom(); - - c = n; - n = NULL; - } - - c->n_files++; - - if (!worst || - worst->n_files < c->n_files || - (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime)) - worst = c; - - sum += st.st_blocks * 512; - } - - if (!worst) - break; - - r = vacuum_necessary(dirfd(d), sum, keep_free, max_use); - if (r <= 0) - return r; - - if (unlinkat(dirfd(d), worst->oldest_file, 0) < 0) { - - if (errno == ENOENT) - continue; - - return log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file); - } else - log_info("Removed old coredump %s.", worst->oldest_file); - } - - return 0; - -fail: - return log_error_errno(errno, "Failed to read directory: %m"); -} diff --git a/src/coredump/coredump-vacuum.h b/src/coredump/coredump-vacuum.h deleted file mode 100644 index 4b7b9f2d98..0000000000 --- a/src/coredump/coredump-vacuum.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c deleted file mode 100644 index dcc09fcc6d..0000000000 --- a/src/coredump/coredump.c +++ /dev/null @@ -1,1185 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#ifdef HAVE_ELFUTILS -#include -#include -#endif - -#include "sd-journal.h" -#include "sd-login.h" -#include "sd-daemon.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 "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 "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "process-util.h" -#include "socket-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)) - -/* The maximum size up to which we leave the coredump around on disk */ -#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX - -/* The maximum size up to which we store the coredump in the journal */ -#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU)) - -/* Make sure to not make this larger than the maximum journal entry - * size. See DATA_SIZE_MAX in journald-native.c. */ -assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX); - -enum { - /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and - * attaching xattrs */ - CONTEXT_PID, - CONTEXT_UID, - CONTEXT_GID, - CONTEXT_SIGNAL, - CONTEXT_TIMESTAMP, - CONTEXT_RLIMIT, - CONTEXT_COMM, - CONTEXT_EXE, - _CONTEXT_MAX -}; - -typedef enum CoredumpStorage { - COREDUMP_STORAGE_NONE, - COREDUMP_STORAGE_EXTERNAL, - COREDUMP_STORAGE_JOURNAL, - COREDUMP_STORAGE_BOTH, - _COREDUMP_STORAGE_MAX, - _COREDUMP_STORAGE_INVALID = -1 -} CoredumpStorage; - -static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = { - [COREDUMP_STORAGE_NONE] = "none", - [COREDUMP_STORAGE_EXTERNAL] = "external", - [COREDUMP_STORAGE_JOURNAL] = "journal", - [COREDUMP_STORAGE_BOTH] = "both", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage); -static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting"); - -static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL; -static bool arg_compress = true; -static uint64_t arg_process_size_max = PROCESS_SIZE_MAX; -static uint64_t arg_external_size_max = EXTERNAL_SIZE_MAX; -static size_t arg_journal_size_max = JOURNAL_SIZE_MAX; -static uint64_t arg_keep_free = (uint64_t) -1; -static uint64_t arg_max_use = (uint64_t) -1; - -static int parse_config(void) { - static const ConfigTableItem items[] = { - { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, - { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, - { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max }, - { "Coredump", "ExternalSizeMax", config_parse_iec_uint64, 0, &arg_external_size_max }, - { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, - { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free }, - { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use }, - {} - }; - - return config_parse_many(PKGSYSCONFDIR "/coredump.conf", - CONF_PATHS_NULSTR("systemd/coredump.conf.d"), - "Coredump\0", - config_item_table_lookup, items, - false, NULL); -} - -static int fix_acl(int fd, uid_t uid) { - -#ifdef HAVE_ACL - _cleanup_(acl_freep) acl_t acl = NULL; - acl_entry_t entry; - acl_permset_t permset; - int r; - - assert(fd >= 0); - - if (uid <= SYSTEM_UID_MAX) - return 0; - - /* Make sure normal users can read (but not write or delete) - * their own coredumps */ - - acl = acl_get_fd(fd); - if (!acl) - return log_error_errno(errno, "Failed to get ACL: %m"); - - if (acl_create_entry(&acl, &entry) < 0 || - acl_set_tag_type(entry, ACL_USER) < 0 || - acl_set_qualifier(entry, &uid) < 0) - return log_error_errno(errno, "Failed to patch ACL: %m"); - - if (acl_get_permset(entry, &permset) < 0 || - 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"); -#endif - - return 0; -} - -static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) { - - static const char * const xattrs[_CONTEXT_MAX] = { - [CONTEXT_PID] = "user.coredump.pid", - [CONTEXT_UID] = "user.coredump.uid", - [CONTEXT_GID] = "user.coredump.gid", - [CONTEXT_SIGNAL] = "user.coredump.signal", - [CONTEXT_TIMESTAMP] = "user.coredump.timestamp", - [CONTEXT_COMM] = "user.coredump.comm", - [CONTEXT_EXE] = "user.coredump.exe", - }; - - int r = 0; - unsigned i; - - assert(fd >= 0); - - /* Attach some metadata to coredumps via extended - * attributes. Just because we can. */ - - for (i = 0; i < _CONTEXT_MAX; i++) { - int k; - - if (isempty(context[i]) || !xattrs[i]) - continue; - - k = fsetxattr(fd, xattrs[i], context[i], strlen(context[i]), XATTR_CREATE); - if (k < 0 && r == 0) - r = -errno; - } - - return r; -} - -#define filename_escape(s) xescape((s), "./ ") - -static inline const char *coredump_tmpfile_name(const char *s) { - return s ? s : "(unnamed temporary file)"; -} - -static int fix_permissions( - int fd, - const char *filename, - const char *target, - const char *context[_CONTEXT_MAX], - uid_t uid) { - - int r; - - assert(fd >= 0); - assert(target); - assert(context); - - /* Ignore errors on these */ - (void) fchmod(fd, 0640); - (void) fix_acl(fd, uid); - (void) fix_xattr(fd, context); - - if (fsync(fd) < 0) - return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); - - r = link_tmpfile(fd, filename, target); - if (r < 0) - return log_error_errno(r, "Failed to move coredump %s into place: %m", target); - - return 0; -} - -static int maybe_remove_external_coredump(const char *filename, uint64_t size) { - - /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */ - - if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) && - size <= arg_external_size_max) - return 0; - - if (!filename) - return 1; - - if (unlink(filename) < 0 && errno != ENOENT) - return log_error_errno(errno, "Failed to unlink %s: %m", filename); - - return 1; -} - -static int make_filename(const char *context[_CONTEXT_MAX], char **ret) { - _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL; - sd_id128_t boot = {}; - int r; - - assert(context); - - c = filename_escape(context[CONTEXT_COMM]); - if (!c) - return -ENOMEM; - - u = filename_escape(context[CONTEXT_UID]); - if (!u) - return -ENOMEM; - - r = sd_id128_get_boot(&boot); - if (r < 0) - return r; - - p = filename_escape(context[CONTEXT_PID]); - if (!p) - return -ENOMEM; - - t = filename_escape(context[CONTEXT_TIMESTAMP]); - if (!t) - return -ENOMEM; - - if (asprintf(ret, - "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000", - c, - u, - SD_ID128_FORMAT_VAL(boot), - p, - t) < 0) - return -ENOMEM; - - return 0; -} - -static int save_external_coredump( - const char *context[_CONTEXT_MAX], - int input_fd, - char **ret_filename, - int *ret_node_fd, - int *ret_data_fd, - uint64_t *ret_size) { - - _cleanup_free_ char *fn = NULL, *tmp = NULL; - _cleanup_close_ int fd = -1; - uint64_t rlimit, max_size; - struct stat st; - uid_t uid; - int r; - - assert(context); - assert(ret_filename); - assert(ret_node_fd); - assert(ret_data_fd); - assert(ret_size); - - r = parse_uid(context[CONTEXT_UID], &uid); - if (r < 0) - return log_error_errno(r, "Failed to parse UID: %m"); - - r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); - if (rlimit <= 0) { - /* Is coredumping disabled? Then don't bother saving/processing the coredump */ - log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); - return -EBADSLT; - } - - /* Never store more than the process configured, or than we actually shall keep or process */ - max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max)); - - r = make_filename(context, &fn); - if (r < 0) - return log_error_errno(r, "Failed to determine coredump file name: %m"); - - mkdir_p_label("/var/lib/systemd/coredump", 0755); - - fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); - if (fd < 0) - return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); - - r = copy_bytes(input_fd, fd, max_size, false); - if (r == -EFBIG) { - log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); - goto fail; - } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { - log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); - goto fail; - } else if (r < 0) { - log_error_errno(r, "Failed to dump coredump to file: %m"); - goto fail; - } - - if (fstat(fd, &st) < 0) { - log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); - goto fail; - } - - if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { - log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); - goto fail; - } - -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - /* If we will remove the coredump anyway, do not compress. */ - if (maybe_remove_external_coredump(NULL, st.st_size) == 0 - && arg_compress) { - - _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; - _cleanup_close_ int fd_compressed = -1; - - fn_compressed = strappend(fn, COMPRESSED_EXT); - if (!fn_compressed) { - log_oom(); - goto uncompressed; - } - - fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); - if (fd_compressed < 0) { - log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); - goto uncompressed; - } - - r = compress_stream(fd, fd_compressed, -1); - if (r < 0) { - log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); - goto fail_compressed; - } - - r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); - if (r < 0) - goto fail_compressed; - - /* OK, this worked, we can get rid of the uncompressed version now */ - if (tmp) - unlink_noerrno(tmp); - - *ret_filename = fn_compressed; /* compressed */ - *ret_node_fd = fd_compressed; /* compressed */ - *ret_data_fd = fd; /* uncompressed */ - *ret_size = (uint64_t) st.st_size; /* uncompressed */ - - fn_compressed = NULL; - fd = fd_compressed = -1; - - return 0; - - fail_compressed: - if (tmp_compressed) - (void) unlink(tmp_compressed); - } - -uncompressed: -#endif - - r = fix_permissions(fd, tmp, fn, context, uid); - if (r < 0) - goto fail; - - *ret_filename = fn; - *ret_data_fd = fd; - *ret_node_fd = -1; - *ret_size = (uint64_t) st.st_size; - - fn = NULL; - fd = -1; - - return 0; - -fail: - if (tmp) - (void) unlink(tmp); - return r; -} - -static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) { - _cleanup_free_ char *field = NULL; - ssize_t n; - - assert(fd >= 0); - assert(ret); - assert(ret_size); - - if (lseek(fd, 0, SEEK_SET) == (off_t) -1) - return log_warning_errno(errno, "Failed to seek: %m"); - - field = malloc(9 + size); - if (!field) { - log_warning("Failed to allocate memory for coredump, coredump will not be stored."); - return -ENOMEM; - } - - memcpy(field, "COREDUMP=", 9); - - n = read(fd, field + 9, size); - if (n < 0) - return log_error_errno((int) n, "Failed to read core data: %m"); - if ((size_t) n < size) { - log_error("Core data too short."); - return -EIO; - } - - *ret = field; - *ret_size = size + 9; - - field = NULL; - - return 0; -} - -/* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines: - * 0:/dev/pts/23 - * pos: 0 - * flags: 0100002 - * - * 1:/dev/pts/23 - * pos: 0 - * flags: 0100002 - * - * 2:/dev/pts/23 - * pos: 0 - * flags: 0100002 - * EOF - */ -static int compose_open_fds(pid_t pid, char **open_fds) { - _cleanup_closedir_ DIR *proc_fd_dir = NULL; - _cleanup_close_ int proc_fdinfo_fd = -1; - _cleanup_free_ char *buffer = NULL; - _cleanup_fclose_ FILE *stream = NULL; - const char *fddelim = "", *path; - struct dirent *dent = NULL; - size_t size = 0; - int r = 0; - - assert(pid >= 0); - assert(open_fds != NULL); - - path = procfs_file_alloca(pid, "fd"); - proc_fd_dir = opendir(path); - if (!proc_fd_dir) - return -errno; - - proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH); - if (proc_fdinfo_fd < 0) - return -errno; - - stream = open_memstream(&buffer, &size); - if (!stream) - return -ENOMEM; - - FOREACH_DIRENT(dent, proc_fd_dir, return -errno) { - _cleanup_fclose_ FILE *fdinfo = NULL; - _cleanup_free_ char *fdname = NULL; - char line[LINE_MAX]; - int fd; - - r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname); - if (r < 0) - return r; - - fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname); - fddelim = "\n"; - - /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */ - fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY); - if (fd < 0) - continue; - - fdinfo = fdopen(fd, "re"); - if (fdinfo == NULL) { - close(fd); - continue; - } - - FOREACH_LINE(line, fdinfo, break) { - fputs(line, stream); - if (!endswith(line, "\n")) - fputc('\n', stream); - } - } - - errno = 0; - stream = safe_fclose(stream); - - if (errno > 0) - return -errno; - - *open_fds = buffer; - buffer = NULL; - - return 0; -} - -static int change_uid_gid(const char *context[]) { - uid_t uid; - gid_t gid; - int r; - - r = parse_uid(context[CONTEXT_UID], &uid); - if (r < 0) - return r; - - if (uid <= SYSTEM_UID_MAX) { - const char *user = "systemd-coredump"; - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user); - uid = gid = 0; - } - } else { - r = parse_gid(context[CONTEXT_GID], &gid); - if (r < 0) - return r; - } - - return drop_privileges(uid, gid, 0); -} - -static int submit_coredump( - const char *context[_CONTEXT_MAX], - struct iovec *iovec, - size_t n_iovec_allocated, - size_t n_iovec, - int input_fd) { - - _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; - _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; - uint64_t coredump_size; - int r; - - assert(context); - assert(iovec); - assert(n_iovec_allocated >= n_iovec + 3); - assert(input_fd >= 0); - - /* Vacuum before we write anything again */ - (void) coredump_vacuum(-1, arg_keep_free, arg_max_use); - - /* Always stream the coredump to disk, if that's possible */ - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); - if (r < 0) - /* Skip whole core dumping part */ - goto log; - - /* If we don't want to keep the coredump on disk, remove it now, as later on we will lack the privileges for - * it. However, we keep the fd to it, so that we can still process it and log it. */ - r = maybe_remove_external_coredump(filename, coredump_size); - if (r < 0) - return r; - if (r == 0) { - const char *coredump_filename; - - coredump_filename = strjoina("COREDUMP_FILENAME=", filename); - IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename); - } - - /* Vacuum again, but exclude the coredump we just created */ - (void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use); - - /* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the coredump - * memory under the user's uid. This also ensures that the credentials journald will see are the ones of the - * coredumping user, thus making sure the user gets access to the core dump. Let's also get rid of all - * capabilities, if we run as root, we won't need them anymore. */ - r = change_uid_gid(context); - if (r < 0) - return log_error_errno(r, "Failed to drop privileges: %m"); - -#ifdef HAVE_ELFUTILS - /* Try to get a strack trace if we can */ - if (coredump_size <= arg_process_size_max) { - _cleanup_free_ char *stacktrace = NULL; - - r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace); - if (r >= 0) - core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.\n\n", stacktrace, NULL); - else if (r == -EINVAL) - log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); - else - log_warning_errno(r, "Failed to generate stack trace: %m"); - } - - if (!core_message) -#endif -log: - core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.", NULL); - if (core_message) - IOVEC_SET_STRING(iovec[n_iovec++], core_message); - - /* Optionally store the entire coredump in the journal */ - if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && - coredump_size <= arg_journal_size_max) { - size_t sz = 0; - - /* Store the coredump itself in the journal */ - - r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz); - if (r >= 0) { - iovec[n_iovec].iov_base = coredump_data; - iovec[n_iovec].iov_len = sz; - n_iovec++; - } - } - - assert(n_iovec <= n_iovec_allocated); - - r = sd_journal_sendv(iovec, n_iovec); - if (r < 0) - return log_error_errno(r, "Failed to log coredump: %m"); - - return 0; -} - -static void map_context_fields(const struct iovec *iovec, const char *context[]) { - - static const char * const context_field_names[_CONTEXT_MAX] = { - [CONTEXT_PID] = "COREDUMP_PID=", - [CONTEXT_UID] = "COREDUMP_UID=", - [CONTEXT_GID] = "COREDUMP_GID=", - [CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=", - [CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=", - [CONTEXT_COMM] = "COREDUMP_COMM=", - [CONTEXT_EXE] = "COREDUMP_EXE=", - [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", - }; - - unsigned i; - - assert(iovec); - assert(context); - - for (i = 0; i < _CONTEXT_MAX; i++) { - size_t l; - - l = strlen(context_field_names[i]); - if (iovec->iov_len < l) - continue; - - if (memcmp(iovec->iov_base, context_field_names[i], l) != 0) - continue; - - /* Note that these strings are NUL terminated, because we made sure that a trailing NUL byte is in the - * buffer, though not included in the iov_len count. (see below) */ - context[i] = (char*) iovec->iov_base + l; - break; - } -} - -static int process_socket(int fd) { - _cleanup_close_ int coredump_fd = -1; - struct iovec *iovec = NULL; - size_t n_iovec = 0, n_iovec_allocated = 0, i; - const char *context[_CONTEXT_MAX] = {}; - int r; - - assert(fd >= 0); - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - for (;;) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iovlen = 1, - }; - ssize_t n; - ssize_t l; - - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { - r = log_oom(); - goto finish; - } - - l = next_datagram_size_fd(fd); - if (l < 0) { - r = log_error_errno(l, "Failed to determine datagram size to read: %m"); - goto finish; - } - - assert(l >= 0); - - iovec[n_iovec].iov_len = l; - iovec[n_iovec].iov_base = malloc(l + 1); - if (!iovec[n_iovec].iov_base) { - r = log_oom(); - goto finish; - } - - mh.msg_iov = iovec + n_iovec; - - n = recvmsg(fd, &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); - if (n < 0) { - free(iovec[n_iovec].iov_base); - r = log_error_errno(errno, "Failed to receive datagram: %m"); - goto finish; - } - - if (n == 0) { - struct cmsghdr *cmsg, *found = NULL; - /* The final zero-length datagram carries the file descriptor and tells us that we're done. */ - - free(iovec[n_iovec].iov_base); - - 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; - } - } - - if (!found) { - log_error("Coredump file descriptor missing."); - r = -EBADMSG; - goto finish; - } - - assert(coredump_fd < 0); - coredump_fd = *(int*) CMSG_DATA(found); - break; - } - - /* Add trailing NUL byte, in case these are strings */ - ((char*) iovec[n_iovec].iov_base)[n] = 0; - iovec[n_iovec].iov_len = (size_t) n; - - cmsg_close_all(&mh); - map_context_fields(iovec + n_iovec, context); - n_iovec++; - } - - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { - r = log_oom(); - goto finish; - } - - /* Make sure we got all data we really need */ - assert(context[CONTEXT_PID]); - assert(context[CONTEXT_UID]); - assert(context[CONTEXT_GID]); - assert(context[CONTEXT_SIGNAL]); - assert(context[CONTEXT_TIMESTAMP]); - assert(context[CONTEXT_RLIMIT]); - assert(context[CONTEXT_COMM]); - assert(coredump_fd >= 0); - - r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd); - -finish: - for (i = 0; i < n_iovec; i++) - free(iovec[i].iov_base); - free(iovec); - - return r; -} - -static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/coredump", - }; - _cleanup_close_ int fd = -1; - size_t i; - int r; - - assert(iovec || n_iovec <= 0); - assert(input_fd >= 0); - - fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to create coredump socket: %m"); - - if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return log_error_errno(errno, "Failed to connect to coredump service: %m"); - - for (i = 0; i < n_iovec; i++) { - struct msghdr mh = { - .msg_iov = (struct iovec*) iovec + i, - .msg_iovlen = 1, - }; - struct iovec copy[2]; - - for (;;) { - if (sendmsg(fd, &mh, MSG_NOSIGNAL) >= 0) - break; - - if (errno == EMSGSIZE && mh.msg_iov[0].iov_len > 0) { - /* This field didn't fit? That's a pity. Given that this is just metadata, - * let's truncate the field at half, and try again. We append three dots, in - * order to show that this is truncated. */ - - if (mh.msg_iov != copy) { - /* We don't want to modify the caller's iovec, hence let's create our - * own array, consisting of two new iovecs, where the first is a - * (truncated) copy of what we want to send, and the second one - * contains the trailing dots. */ - copy[0] = iovec[i]; - copy[1] = (struct iovec) { - .iov_base = (char[]) { '.', '.', '.' }, - .iov_len = 3, - }; - - mh.msg_iov = copy; - mh.msg_iovlen = 2; - } - - copy[0].iov_len /= 2; /* halve it, and try again */ - continue; - } - - return log_error_errno(errno, "Failed to send coredump datagram: %m"); - } - } - - r = send_one_fd(fd, input_fd, 0); - if (r < 0) - return log_error_errno(r, "Failed to send coredump fd: %m"); - - return 0; -} - -static int process_special_crash(const char *context[], int input_fd) { - _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; - _cleanup_free_ char *filename = NULL; - uint64_t coredump_size; - int r; - - assert(context); - assert(input_fd >= 0); - - /* If we are pid1 or journald, we cut things short, don't write to the journal, but still create a coredump. */ - - if (arg_storage != COREDUMP_STORAGE_NONE) - arg_storage = COREDUMP_STORAGE_EXTERNAL; - - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); - if (r < 0) - return r; - - r = maybe_remove_external_coredump(filename, coredump_size); - if (r < 0) - return r; - - log_notice("Detected coredump of the journal daemon or PID 1, diverted to %s.", filename); - - return 0; -} - -static int process_kernel(int argc, char* argv[]) { - - /* The small core field we allocate on the stack, to keep things simple */ - char - *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, - *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL, - *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL, - *core_user_unit = NULL, *core_slice = NULL, *core_timestamp = NULL, *core_rlimit = NULL; - - /* The larger ones we allocate on the heap */ - _cleanup_free_ char - *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, - *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL; - - _cleanup_free_ char *exe = NULL, *comm = NULL; - const char *context[_CONTEXT_MAX]; - struct iovec iovec[25]; - size_t n_iovec = 0; - uid_t owner_uid; - const char *p; - pid_t pid; - char *t; - int r; - - if (argc < CONTEXT_COMM + 1) { - log_error("Not enough arguments passed from kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1); - return -EINVAL; - } - - r = parse_pid(argv[CONTEXT_PID + 1], &pid); - if (r < 0) - return log_error_errno(r, "Failed to parse PID."); - - r = get_process_comm(pid, &comm); - if (r < 0) { - log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m"); - comm = strv_join(argv + CONTEXT_COMM + 1, " "); - if (!comm) - return log_oom(); - } - - r = get_process_exe(pid, &exe); - if (r < 0) - log_warning_errno(r, "Failed to get EXE, ignoring: %m"); - - context[CONTEXT_PID] = argv[CONTEXT_PID + 1]; - context[CONTEXT_UID] = argv[CONTEXT_UID + 1]; - context[CONTEXT_GID] = argv[CONTEXT_GID + 1]; - context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1]; - context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1]; - context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1]; - context[CONTEXT_COMM] = comm; - context[CONTEXT_EXE] = exe; - - if (cg_pid_get_unit(pid, &t) >= 0) { - - /* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */ - if (streq(t, SPECIAL_INIT_SCOPE)) { - log_notice("Due to PID 1 having crashed coredump collection will now be turned off."); - (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); - } - - /* Let's avoid dead-locks when processing journald and init crashes, as socket activation and logging - * are unlikely to work then. */ - if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) { - free(t); - return process_special_crash(context, STDIN_FILENO); - } - - core_unit = strjoina("COREDUMP_UNIT=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_unit); - } - - /* OK, now we know it's not the journal, hence we can make use of it now. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_open(); - - if (cg_pid_get_user_unit(pid, &t) >= 0) { - core_user_unit = strjoina("COREDUMP_USER_UNIT=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_user_unit); - } - - core_pid = strjoina("COREDUMP_PID=", context[CONTEXT_PID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_pid); - - core_uid = strjoina("COREDUMP_UID=", context[CONTEXT_UID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_uid); - - core_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_gid); - - core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]); - IOVEC_SET_STRING(iovec[n_iovec++], core_signal); - - core_rlimit = strjoina("COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]); - IOVEC_SET_STRING(iovec[n_iovec++], core_rlimit); - - if (sd_pid_get_session(pid, &t) >= 0) { - core_session = strjoina("COREDUMP_SESSION=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_session); - } - - if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) { - r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid); - if (r > 0) - IOVEC_SET_STRING(iovec[n_iovec++], core_owner_uid); - } - - if (sd_pid_get_slice(pid, &t) >= 0) { - core_slice = strjoina("COREDUMP_SLICE=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_slice); - } - - if (comm) { - core_comm = strjoina("COREDUMP_COMM=", comm); - IOVEC_SET_STRING(iovec[n_iovec++], core_comm); - } - - if (exe) { - core_exe = strjoina("COREDUMP_EXE=", exe); - IOVEC_SET_STRING(iovec[n_iovec++], core_exe); - } - - if (get_process_cmdline(pid, 0, false, &t) >= 0) { - core_cmdline = strjoina("COREDUMP_CMDLINE=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_cmdline); - } - - if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) { - core_cgroup = strjoina("COREDUMP_CGROUP=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_cgroup); - } - - if (compose_open_fds(pid, &t) >= 0) { - core_open_fds = strappend("COREDUMP_OPEN_FDS=", t); - free(t); - - if (core_open_fds) - IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds); - } - - p = procfs_file_alloca(pid, "status"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_status = strappend("COREDUMP_PROC_STATUS=", t); - free(t); - - if (core_proc_status) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status); - } - - p = procfs_file_alloca(pid, "maps"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t); - free(t); - - if (core_proc_maps) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps); - } - - p = procfs_file_alloca(pid, "limits"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t); - free(t); - - if (core_proc_limits) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits); - } - - p = procfs_file_alloca(pid, "cgroup"); - if (read_full_file(p, &t, NULL) >=0) { - core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t); - free(t); - - if (core_proc_cgroup) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup); - } - - if (get_process_cwd(pid, &t) >= 0) { - core_cwd = strjoina("COREDUMP_CWD=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_cwd); - } - - if (get_process_root(pid, &t) >= 0) { - core_root = strjoina("COREDUMP_ROOT=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_root); - } - - if (get_process_environ(pid, &t) >= 0) { - core_environ = strappend("COREDUMP_ENVIRON=", t); - free(t); - - if (core_environ) - IOVEC_SET_STRING(iovec[n_iovec++], core_environ); - } - - core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000"); - IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp); - - IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); - - assert_cc(2 == LOG_CRIT); - IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2"); - - assert(n_iovec <= ELEMENTSOF(iovec)); - - return send_iovec(iovec, n_iovec, STDIN_FILENO); -} - -int main(int argc, char *argv[]) { - int r; - - /* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not - * log to then. */ - - log_set_target(LOG_TARGET_KMSG); - log_open(); - - /* Make sure we never enter a loop */ - (void) prctl(PR_SET_DUMPABLE, 0); - - /* Ignore all parse errors */ - (void) parse_config(); - - log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage)); - log_debug("Selected compression %s.", yes_no(arg_compress)); - - r = sd_listen_fds(false); - if (r < 0) { - log_error_errno(r, "Failed to determine number of file descriptor: %m"); - goto finish; - } - - /* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as - * coredump handler */ - if (r == 0) - r = process_kernel(argc, argv); - else if (r == 1) - r = process_socket(SD_LISTEN_FDS_START); - else { - log_error("Received unexpected number of file descriptors."); - r = -EINVAL; - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/coredump/coredump.conf b/src/coredump/coredump.conf deleted file mode 100644 index c2f0643e03..0000000000 --- a/src/coredump/coredump.conf +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See coredump.conf(5) for details. - -[Coredump] -#Storage=external -#Compress=yes -#ProcessSizeMax=2G -#ExternalSizeMax=2G -#JournalSizeMax=767M -#MaxUse= -#KeepFree= diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c deleted file mode 100644 index 27b1e0fb3f..0000000000 --- a/src/coredump/coredumpctl.c +++ /dev/null @@ -1,878 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-journal.h" - -#include "alloc-util.h" -#include "compress.h" -#include "fd-util.h" -#include "fileio.h" -#include "journal-internal.h" -#include "log.h" -#include "macro.h" -#include "pager.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-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, - ACTION_INFO, - ACTION_LIST, - ACTION_DUMP, - ACTION_GDB, -} arg_action = ACTION_LIST; -static const char* arg_field = NULL; -static const char *arg_directory = NULL; -static bool arg_no_pager = false; -static int arg_no_legend = false; -static int arg_one = false; -static FILE* arg_output = NULL; - -static Set *new_matches(void) { - Set *set; - char *tmp; - int r; - - set = set_new(NULL); - if (!set) { - log_oom(); - return NULL; - } - - tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); - if (!tmp) { - log_oom(); - set_free(set); - return NULL; - } - - r = set_consume(set, tmp); - if (r < 0) { - log_error_errno(r, "failed to add to set: %m"); - set_free(set); - return NULL; - } - - return set; -} - -static int add_match(Set *set, const char *match) { - _cleanup_free_ char *p = NULL; - char *pattern = NULL; - const char* prefix; - pid_t pid; - int r; - - if (strchr(match, '=')) - prefix = ""; - else if (strchr(match, '/')) { - r = path_make_absolute_cwd(match, &p); - if (r < 0) - goto fail; - match = p; - prefix = "COREDUMP_EXE="; - } else if (parse_pid(match, &pid) >= 0) - prefix = "COREDUMP_PID="; - else - prefix = "COREDUMP_COMM="; - - pattern = strjoin(prefix, match, NULL); - if (!pattern) { - r = -ENOMEM; - goto fail; - } - - log_debug("Adding pattern: %s", pattern); - r = set_consume(set, pattern); - if (r < 0) - goto fail; - - return 0; -fail: - return log_error_errno(r, "Failed to add match: %m"); -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "List or retrieve coredumps from the journal.\n\n" - "Flags:\n" - " -h --help Show this help\n" - " --version Print version string\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not print the column headers.\n" - " -1 Show information about most recent entry only\n" - " -F --field=FIELD List all values a certain field takes\n" - " -o --output=FILE Write output to FILE\n\n" - " -D --directory=DIR Use journal files from directory\n\n" - - "Commands:\n" - " list [MATCHES...] List available coredumps (default)\n" - " info [MATCHES...] Show detailed information about one or more coredumps\n" - " dump [MATCHES...] Print first matching coredump to stdout\n" - " gdb [MATCHES...] Start gdb for the first matching coredump\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[], Set *matches) { - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_LEGEND, - }; - - int r, c; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version" , no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "output", required_argument, NULL, 'o' }, - { "field", required_argument, NULL, 'F' }, - { "directory", required_argument, NULL, 'D' }, - {} - }; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0) - switch(c) { - - case 'h': - arg_action = ACTION_NONE; - help(); - return 0; - - case ARG_VERSION: - arg_action = ACTION_NONE; - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_no_legend = true; - break; - - case 'o': - if (arg_output) { - log_error("cannot set output more than once"); - return -EINVAL; - } - - arg_output = fopen(optarg, "we"); - if (!arg_output) - return log_error_errno(errno, "writing to '%s': %m", optarg); - - break; - - case 'F': - if (arg_field) { - log_error("cannot use --field/-F more than once"); - return -EINVAL; - } - arg_field = optarg; - break; - - case '1': - arg_one = true; - break; - - case 'D': - arg_directory = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind < argc) { - const char *cmd = argv[optind++]; - if (streq(cmd, "list")) - arg_action = ACTION_LIST; - else if (streq(cmd, "dump")) - arg_action = ACTION_DUMP; - else if (streq(cmd, "gdb")) - arg_action = ACTION_GDB; - else if (streq(cmd, "info")) - arg_action = ACTION_INFO; - else { - log_error("Unknown action '%s'", cmd); - return -EINVAL; - } - } - - if (arg_field && arg_action != ACTION_LIST) { - log_error("Option --field/-F only makes sense with list"); - return -EINVAL; - } - - while (optind < argc) { - r = add_match(matches, argv[optind]); - if (r != 0) - return r; - optind++; - } - - return 0; -} - -static int retrieve(const void *data, - size_t len, - const char *name, - char **var) { - - size_t ident; - char *v; - - ident = strlen(name) + 1; /* name + "=" */ - - if (len < ident) - return 0; - - if (memcmp(data, name, ident - 1) != 0) - return 0; - - if (((const char*) data)[ident - 1] != '=') - return 0; - - v = strndup((const char*)data + ident, len - ident); - if (!v) - return log_oom(); - - free(*var); - *var = v; - - return 0; -} - -static void print_field(FILE* file, sd_journal *j) { - _cleanup_free_ char *value = NULL; - const void *d; - size_t l; - - assert(file); - assert(j); - - assert(arg_field); - - SD_JOURNAL_FOREACH_DATA(j, d, l) - retrieve(d, l, arg_field, &value); - - if (value) - fprintf(file, "%s\n", value); -} - -static int print_list(FILE* file, sd_journal *j, int had_legend) { - _cleanup_free_ char - *pid = NULL, *uid = NULL, *gid = NULL, - *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, - *filename = NULL; - const void *d; - size_t l; - usec_t t; - char buf[FORMAT_TIMESTAMP_MAX]; - int r; - bool present; - - assert(file); - assert(j); - - SD_JOURNAL_FOREACH_DATA(j, d, l) { - retrieve(d, l, "COREDUMP_PID", &pid); - retrieve(d, l, "COREDUMP_UID", &uid); - retrieve(d, l, "COREDUMP_GID", &gid); - retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); - retrieve(d, l, "COREDUMP_EXE", &exe); - retrieve(d, l, "COREDUMP_COMM", &comm); - retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); - retrieve(d, l, "COREDUMP_FILENAME", &filename); - } - - if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) { - log_warning("Empty coredump log entry"); - return -EINVAL; - } - - r = sd_journal_get_realtime_usec(j, &t); - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - format_timestamp(buf, sizeof(buf), t); - present = filename && access(filename, F_OK) == 0; - - if (!had_legend && !arg_no_legend) - fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", - FORMAT_TIMESTAMP_WIDTH, "TIME", - 6, "PID", - 5, "UID", - 5, "GID", - 3, "SIG", - 1, "PRESENT", - "EXE"); - - fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", - FORMAT_TIMESTAMP_WIDTH, buf, - 6, strna(pid), - 5, strna(uid), - 5, strna(gid), - 3, strna(sgnl), - 1, present ? "*" : "", - strna(exe ?: (comm ?: cmdline))); - - return 0; -} - -static int print_info(FILE *file, sd_journal *j, bool need_space) { - _cleanup_free_ char - *pid = NULL, *uid = NULL, *gid = NULL, - *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, - *unit = NULL, *user_unit = NULL, *session = NULL, - *boot_id = NULL, *machine_id = NULL, *hostname = NULL, - *slice = NULL, *cgroup = NULL, *owner_uid = NULL, - *message = NULL, *timestamp = NULL, *filename = NULL; - const void *d; - size_t l; - int r; - - assert(file); - assert(j); - - SD_JOURNAL_FOREACH_DATA(j, d, l) { - retrieve(d, l, "COREDUMP_PID", &pid); - retrieve(d, l, "COREDUMP_UID", &uid); - retrieve(d, l, "COREDUMP_GID", &gid); - retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); - retrieve(d, l, "COREDUMP_EXE", &exe); - retrieve(d, l, "COREDUMP_COMM", &comm); - retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); - retrieve(d, l, "COREDUMP_UNIT", &unit); - retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit); - retrieve(d, l, "COREDUMP_SESSION", &session); - retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid); - retrieve(d, l, "COREDUMP_SLICE", &slice); - retrieve(d, l, "COREDUMP_CGROUP", &cgroup); - retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp); - retrieve(d, l, "COREDUMP_FILENAME", &filename); - retrieve(d, l, "_BOOT_ID", &boot_id); - retrieve(d, l, "_MACHINE_ID", &machine_id); - retrieve(d, l, "_HOSTNAME", &hostname); - retrieve(d, l, "MESSAGE", &message); - } - - if (need_space) - fputs("\n", file); - - if (comm) - fprintf(file, - " PID: %s%s%s (%s)\n", - ansi_highlight(), strna(pid), ansi_normal(), comm); - else - fprintf(file, - " PID: %s%s%s\n", - ansi_highlight(), strna(pid), ansi_normal()); - - if (uid) { - uid_t n; - - if (parse_uid(uid, &n) >= 0) { - _cleanup_free_ char *u = NULL; - - u = uid_to_name(n); - fprintf(file, - " UID: %s (%s)\n", - uid, u); - } else { - fprintf(file, - " UID: %s\n", - uid); - } - } - - if (gid) { - gid_t n; - - if (parse_gid(gid, &n) >= 0) { - _cleanup_free_ char *g = NULL; - - g = gid_to_name(n); - fprintf(file, - " GID: %s (%s)\n", - gid, g); - } else { - fprintf(file, - " GID: %s\n", - gid); - } - } - - if (sgnl) { - int sig; - - if (safe_atoi(sgnl, &sig) >= 0) - fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig)); - else - fprintf(file, " Signal: %s\n", sgnl); - } - - if (timestamp) { - usec_t u; - - r = safe_atou64(timestamp, &u); - if (r >= 0) { - char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX]; - - fprintf(file, - " Timestamp: %s (%s)\n", - format_timestamp(absolute, sizeof(absolute), u), - format_timestamp_relative(relative, sizeof(relative), u)); - - } else - fprintf(file, " Timestamp: %s\n", timestamp); - } - - if (cmdline) - fprintf(file, " Command Line: %s\n", cmdline); - if (exe) - fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal()); - if (cgroup) - fprintf(file, " Control Group: %s\n", cgroup); - if (unit) - fprintf(file, " Unit: %s\n", unit); - if (user_unit) - fprintf(file, " User Unit: %s\n", unit); - if (slice) - fprintf(file, " Slice: %s\n", slice); - if (session) - fprintf(file, " Session: %s\n", session); - if (owner_uid) { - uid_t n; - - if (parse_uid(owner_uid, &n) >= 0) { - _cleanup_free_ char *u = NULL; - - u = uid_to_name(n); - fprintf(file, - " Owner UID: %s (%s)\n", - owner_uid, u); - } else { - fprintf(file, - " Owner UID: %s\n", - owner_uid); - } - } - if (boot_id) - fprintf(file, " Boot ID: %s\n", boot_id); - if (machine_id) - fprintf(file, " Machine ID: %s\n", machine_id); - if (hostname) - fprintf(file, " Hostname: %s\n", hostname); - - if (filename && access(filename, F_OK) == 0) - fprintf(file, " Coredump: %s\n", filename); - - if (message) { - _cleanup_free_ char *m = NULL; - - m = strreplace(message, "\n", "\n "); - - fprintf(file, " Message: %s\n", strstrip(m ?: message)); - } - - return 0; -} - -static int focus(sd_journal *j) { - int r; - - r = sd_journal_seek_tail(j); - if (r == 0) - r = sd_journal_previous(j); - if (r < 0) - return log_error_errno(r, "Failed to search journal: %m"); - if (r == 0) { - log_error("No match found."); - return -ESRCH; - } - return r; -} - -static void print_entry(sd_journal *j, unsigned n_found) { - assert(j); - - if (arg_action == ACTION_INFO) - print_info(stdout, j, n_found); - else if (arg_field) - print_field(stdout, j); - else - print_list(stdout, j, n_found); -} - -static int dump_list(sd_journal *j) { - unsigned n_found = 0; - int r; - - assert(j); - - /* The coredumps are likely to compressed, and for just - * listing them we don't need to decompress them, so let's - * pick a fairly low data threshold here */ - sd_journal_set_data_threshold(j, 4096); - - if (arg_one) { - r = focus(j); - if (r < 0) - return r; - - print_entry(j, 0); - } else { - SD_JOURNAL_FOREACH(j) - print_entry(j, n_found++); - - if (!arg_field && n_found <= 0) { - log_notice("No coredumps found."); - return -ESRCH; - } - } - - return 0; -} - -static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { - const char *data; - _cleanup_free_ char *filename = NULL; - size_t len; - int r; - - assert((fd >= 0) != !!path); - assert(!!path == !!unlink_temp); - - /* Prefer uncompressed file to journal (probably cached) to - * compressed file (probably uncached). */ - r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len); - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m"); - else if (r == 0) - retrieve(data, len, "COREDUMP_FILENAME", &filename); - - if (filename && access(filename, R_OK) < 0) { - log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "File %s is not readable: %m", filename); - filename = mfree(filename); - } - - if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { - if (path) { - *path = filename; - filename = NULL; - } - - return 0; - } else { - _cleanup_close_ int fdt = -1; - char *temp = NULL; - - if (fd < 0) { - temp = strdup("/var/tmp/coredump-XXXXXX"); - if (!temp) - return log_oom(); - - fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); - if (fdt < 0) - return log_error_errno(fdt, "Failed to create temporary file: %m"); - log_debug("Created temporary file %s", temp); - - fd = fdt; - } - - r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); - if (r == 0) { - ssize_t sz; - - assert(len >= 9); - data += 9; - len -= 9; - - sz = write(fdt, data, len); - if (sz < 0) { - r = log_error_errno(errno, - "Failed to write temporary file: %m"); - goto error; - } - if (sz != (ssize_t) len) { - log_error("Short write to temporary file."); - r = -EIO; - goto error; - } - } else if (filename) { -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - _cleanup_close_ int fdf; - - fdf = open(filename, O_RDONLY | O_CLOEXEC); - if (fdf < 0) { - r = log_error_errno(errno, - "Failed to open %s: %m", - filename); - goto error; - } - - r = decompress_stream(filename, fdf, fd, -1); - if (r < 0) { - log_error_errno(r, "Failed to decompress %s: %m", filename); - goto error; - } -#else - log_error("Cannot decompress file. Compiled without compression support."); - r = -EOPNOTSUPP; - goto error; -#endif - } else { - if (r == -ENOENT) - log_error("Cannot retrieve coredump from journal or disk."); - else - log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); - goto error; - } - - if (temp) { - *path = temp; - *unlink_temp = true; - } - - return 0; - -error: - if (temp) { - unlink(temp); - log_debug("Removed temporary file %s", temp); - } - return r; - } -} - -static int dump_core(sd_journal* j) { - int r; - - assert(j); - - r = focus(j); - if (r < 0) - return r; - - print_info(arg_output ? stdout : stderr, j, false); - - if (on_tty() && !arg_output) { - log_error("Refusing to dump core to tty."); - return -ENOTTY; - } - - r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Coredump retrieval failed: %m"); - - r = sd_journal_previous(j); - if (r >= 0) - log_warning("More than one entry matches, ignoring rest."); - - return 0; -} - -static int run_gdb(sd_journal *j) { - _cleanup_free_ char *exe = NULL, *path = NULL; - bool unlink_path = false; - const char *data; - siginfo_t st; - size_t len; - pid_t pid; - int r; - - assert(j); - - r = focus(j); - if (r < 0) - return r; - - print_info(stdout, j, false); - fputs("\n", stdout); - - r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len); - if (r < 0) - return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m"); - - assert(len > strlen("COREDUMP_EXE=")); - data += strlen("COREDUMP_EXE="); - len -= strlen("COREDUMP_EXE="); - - exe = strndup(data, len); - if (!exe) - return log_oom(); - - if (endswith(exe, " (deleted)")) { - log_error("Binary already deleted."); - return -ENOENT; - } - - if (!path_is_absolute(exe)) { - log_error("Binary is not an absolute path."); - return -ENOENT; - } - - r = save_core(j, -1, &path, &unlink_path); - if (r < 0) - return log_error_errno(r, "Failed to retrieve core: %m"); - - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "Failed to fork(): %m"); - goto finish; - } - if (pid == 0) { - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execlp("gdb", "gdb", exe, path, NULL); - - log_error_errno(errno, "Failed to invoke gdb: %m"); - _exit(1); - } - - r = wait_for_terminate(pid, &st); - if (r < 0) { - log_error_errno(r, "Failed to wait for gdb: %m"); - goto finish; - } - - r = st.si_code == CLD_EXITED ? st.si_status : 255; - -finish: - if (unlink_path) { - log_debug("Removed temporary file %s", path); - unlink(path); - } - - return r; -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_journal_closep) sd_journal*j = NULL; - const char* match; - Iterator it; - int r = 0; - _cleanup_set_free_free_ Set *matches = NULL; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - matches = new_matches(); - if (!matches) { - r = -ENOMEM; - goto end; - } - - r = parse_argv(argc, argv, matches); - if (r < 0) - goto end; - - if (arg_action == ACTION_NONE) - goto end; - - sigbus_install(); - - if (arg_directory) { - r = sd_journal_open_directory(&j, arg_directory, 0); - if (r < 0) { - log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); - goto end; - } - } else { - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); - if (r < 0) { - log_error_errno(r, "Failed to open journal: %m"); - goto end; - } - } - - /* We want full data, nothing truncated. */ - sd_journal_set_data_threshold(j, 0); - - SET_FOREACH(match, matches, it) { - r = sd_journal_add_match(j, match, strlen(match)); - if (r != 0) { - log_error_errno(r, "Failed to add match '%s': %m", - match); - goto end; - } - } - - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { - _cleanup_free_ char *filter; - - filter = journal_make_match_string(j); - log_debug("Journal filter: %s", filter); - } - - switch(arg_action) { - - case ACTION_LIST: - case ACTION_INFO: - pager_open(arg_no_pager, false); - r = dump_list(j); - break; - - case ACTION_DUMP: - r = dump_core(j); - break; - - case ACTION_GDB: - r = run_gdb(j); - break; - - default: - assert_not_reached("Shouldn't be here"); - } - -end: - pager_close(); - - if (arg_output) - fclose(arg_output); - - return r >= 0 ? r : EXIT_FAILURE; -} diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c deleted file mode 100644 index cc4dad9465..0000000000 --- a/src/coredump/stacktrace.c +++ /dev/null @@ -1,200 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "macro.h" -#include "stacktrace.h" -#include "string-util.h" -#include "util.h" - -#define FRAMES_MAX 64 -#define THREADS_MAX 64 - -struct stack_context { - FILE *f; - Dwfl *dwfl; - Elf *elf; - unsigned n_thread; - unsigned n_frame; -}; - -static int frame_callback(Dwfl_Frame *frame, void *userdata) { - struct stack_context *c = userdata; - Dwarf_Addr pc, pc_adjusted, bias = 0; - _cleanup_free_ Dwarf_Die *scopes = NULL; - const char *fname = NULL, *symbol = NULL; - Dwfl_Module *module; - bool is_activation; - - assert(frame); - assert(c); - - if (c->n_frame >= FRAMES_MAX) - return DWARF_CB_ABORT; - - if (!dwfl_frame_pc(frame, &pc, &is_activation)) - return DWARF_CB_ABORT; - - pc_adjusted = pc - (is_activation ? 0 : 1); - - module = dwfl_addrmodule(c->dwfl, pc_adjusted); - if (module) { - Dwarf_Die *s, *cudie; - int n; - - cudie = dwfl_module_addrdie(module, pc_adjusted, &bias); - if (cudie) { - n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes); - for (s = scopes; s < scopes + n; s++) { - if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) { - Dwarf_Attribute *a, space; - - a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space); - if (!a) - a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space); - if (a) - symbol = dwarf_formstring(a); - if (!symbol) - symbol = dwarf_diename(s); - - if (symbol) - break; - } - } - } - - if (!symbol) - symbol = dwfl_module_addrname(module, pc_adjusted); - - fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - } - - fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname)); - c->n_frame++; - - return DWARF_CB_OK; -} - -static int thread_callback(Dwfl_Thread *thread, void *userdata) { - struct stack_context *c = userdata; - pid_t tid; - - assert(thread); - assert(c); - - if (c->n_thread >= THREADS_MAX) - return DWARF_CB_ABORT; - - if (c->n_thread != 0) - fputc('\n', c->f); - - c->n_frame = 0; - - tid = dwfl_thread_tid(thread); - fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid); - - if (dwfl_thread_getframes(thread, frame_callback, c) < 0) - return DWARF_CB_ABORT; - - c->n_thread++; - - return DWARF_CB_OK; -} - -int coredump_make_stack_trace(int fd, const char *executable, char **ret) { - - static const Dwfl_Callbacks callbacks = { - .find_elf = dwfl_build_id_find_elf, - .find_debuginfo = dwfl_standard_find_debuginfo, - }; - - struct stack_context c = {}; - char *buf = NULL; - size_t sz = 0; - int r; - - assert(fd >= 0); - assert(ret); - - if (lseek(fd, 0, SEEK_SET) == (off_t) -1) - return -errno; - - c.f = open_memstream(&buf, &sz); - if (!c.f) - return -ENOMEM; - - elf_version(EV_CURRENT); - - c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); - if (!c.elf) { - r = -EINVAL; - goto finish; - } - - c.dwfl = dwfl_begin(&callbacks); - if (!c.dwfl) { - r = -EINVAL; - goto finish; - } - - if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) { - r = -EINVAL; - goto finish; - } - - if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) { - r = -EINVAL; - goto finish; - } - - if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) { - r = -EINVAL; - goto finish; - } - - if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) { - r = -EINVAL; - goto finish; - } - - c.f = safe_fclose(c.f); - - *ret = buf; - buf = NULL; - - r = 0; - -finish: - if (c.dwfl) - dwfl_end(c.dwfl); - - if (c.elf) - elf_end(c.elf); - - safe_fclose(c.f); - - free(buf); - - return r; -} diff --git a/src/coredump/stacktrace.h b/src/coredump/stacktrace.h deleted file mode 100644 index 15e9c04465..0000000000 --- a/src/coredump/stacktrace.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -int coredump_make_stack_trace(int fd, const char *executable, char **ret); diff --git a/src/coredump/test-coredump-vacuum.c b/src/coredump/test-coredump-vacuum.c deleted file mode 100644 index 70a57f183f..0000000000 --- a/src/coredump/test-coredump-vacuum.c +++ /dev/null @@ -1,30 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "coredump-vacuum.h" - -int main(int argc, char *argv[]) { - - if (coredump_vacuum(-1, (uint64_t) -1, 70 * 1024) < 0) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} diff --git a/src/cryptsetup/Makefile b/src/cryptsetup/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/cryptsetup/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c deleted file mode 100644 index 8ac5ab730a..0000000000 --- a/src/cryptsetup/cryptsetup-generator.c +++ /dev/null @@ -1,509 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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 "proc-cmdline.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" -#include "util.h" - -typedef struct crypto_device { - char *uuid; - char *keyfile; - char *name; - char *options; - bool create; -} crypto_device; - -static const char *arg_dest = "/tmp"; -static bool arg_enabled = true; -static bool arg_read_crypttab = true; -static bool arg_whitelist = false; -static Hashmap *arg_disks = NULL; -static char *arg_default_options = NULL; -static char *arg_default_keyfile = NULL; - -static int create_disk( - const char *name, - const char *device, - const char *password, - const char *options) { - - _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL, - *filtered = NULL; - _cleanup_fclose_ FILE *f = NULL; - bool noauto, nofail, tmp, swap; - char *from; - int r; - - assert(name); - assert(device); - - noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); - nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); - tmp = fstab_test_option(options, "tmp\0"); - swap = fstab_test_option(options, "swap\0"); - - if (tmp && swap) { - log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); - return -EINVAL; - } - - e = unit_name_escape(name); - if (!e) - return log_oom(); - - r = unit_name_build("systemd-cryptsetup", e, ".service", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - p = strjoin(arg_dest, "/", n, NULL); - if (!p) - return log_oom(); - - u = fstab_node_to_udev_node(device); - if (!u) - return log_oom(); - - r = unit_name_from_path(u, ".device", &d); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", p); - - fputs( - "# Automatically generated by systemd-cryptsetup-generator\n\n" - "[Unit]\n" - "Description=Cryptography Setup for %I\n" - "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n" - "SourcePath=/etc/crypttab\n" - "DefaultDependencies=no\n" - "Conflicts=umount.target\n" - "BindsTo=dev-mapper-%i.device\n" - "IgnoreOnIsolate=true\n" - "After=cryptsetup-pre.target\n", - f); - - if (!nofail) - fprintf(f, - "Before=cryptsetup.target\n"); - - if (password) { - if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) - fputs("After=systemd-random-seed.service\n", f); - else if (!streq(password, "-") && !streq(password, "none")) { - _cleanup_free_ char *uu; - - uu = fstab_node_to_udev_node(password); - if (!uu) - return log_oom(); - - if (!path_equal(uu, "/dev/null")) { - - if (is_device_path(uu)) { - _cleanup_free_ char *dd = NULL; - - r = unit_name_from_path(uu, ".device", &dd); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); - } else - fprintf(f, "RequiresMountsFor=%s\n", password); - } - } - } - - if (is_device_path(u)) - fprintf(f, - "BindsTo=%s\n" - "After=%s\n" - "Before=umount.target\n", - d, d); - else - fprintf(f, - "RequiresMountsFor=%s\n", - u); - - r = generator_write_timeouts(arg_dest, device, name, options, &filtered); - if (r < 0) - return r; - - fprintf(f, - "\n[Service]\n" - "Type=oneshot\n" - "RemainAfterExit=yes\n" - "TimeoutSec=0\n" /* the binary handles timeouts anyway */ - "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" - "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", - name, u, strempty(password), strempty(filtered), - name); - - if (tmp) - fprintf(f, - "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", - name); - - if (swap) - fprintf(f, - "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", - name); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write file %s: %m", p); - - from = strjoina("../", n); - - if (!noauto) { - - to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - - free(to); - if (!nofail) - to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); - else - to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - } - - free(to); - to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - - if (!noauto && !nofail) { - _cleanup_free_ char *dmname; - dmname = strjoin("dev-mapper-", e, ".device", NULL); - if (!dmname) - return log_oom(); - - r = write_drop_in(arg_dest, dmname, 90, "device-timeout", - "# Automatically generated by systemd-cryptsetup-generator \n\n" - "[Unit]\nJobTimeoutSec=0"); - if (r < 0) - return log_error_errno(r, "Failed to write device drop-in: %m"); - } - - return 0; -} - -static void free_arg_disks(void) { - crypto_device *d; - - while ((d = hashmap_steal_first(arg_disks))) { - free(d->uuid); - free(d->keyfile); - free(d->name); - free(d->options); - free(d); - } - - hashmap_free(arg_disks); -} - -static crypto_device *get_crypto_device(const char *uuid) { - int r; - crypto_device *d; - - assert(uuid); - - d = hashmap_get(arg_disks, uuid); - if (!d) { - d = new0(struct crypto_device, 1); - if (!d) - return NULL; - - d->create = false; - d->keyfile = d->options = d->name = NULL; - - d->uuid = strdup(uuid); - if (!d->uuid) { - free(d); - return NULL; - } - - r = hashmap_put(arg_disks, d->uuid, d); - if (r < 0) { - free(d->uuid); - free(d); - return NULL; - } - } - - return d; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - crypto_device *d; - _cleanup_free_ char *uuid = NULL, *uuid_value = NULL; - - if (STR_IN_SET(key, "luks", "rd.luks") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse luks switch %s. Ignoring.", value); - else - arg_enabled = r; - - } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value); - else - arg_read_crypttab = r; - - } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) { - - d = get_crypto_device(startswith(value, "luks-") ? value+5 : value); - if (!d) - return log_oom(); - - d->create = arg_whitelist = true; - - } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) { - - r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); - if (r == 2) { - d = get_crypto_device(uuid); - if (!d) - return log_oom(); - - free(d->options); - d->options = uuid_value; - uuid_value = NULL; - } else if (free_and_strdup(&arg_default_options, value) < 0) - return log_oom(); - - } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) { - - r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); - if (r == 2) { - d = get_crypto_device(uuid); - if (!d) - return log_oom(); - - free(d->keyfile); - d->keyfile = uuid_value; - uuid_value = NULL; - } else if (free_and_strdup(&arg_default_keyfile, value) < 0) - return log_oom(); - - } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) { - - r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); - if (r == 2) { - d = get_crypto_device(uuid); - if (!d) - return log_oom(); - - d->create = arg_whitelist = true; - - free(d->name); - d->name = uuid_value; - uuid_value = NULL; - } else - log_warning("Failed to parse luks name switch %s. Ignoring.", value); - - } - - return 0; -} - -static int add_crypttab_devices(void) { - struct stat st; - unsigned crypttab_line = 0; - _cleanup_fclose_ FILE *f = NULL; - - if (!arg_read_crypttab) - return 0; - - f = fopen("/etc/crypttab", "re"); - if (!f) { - if (errno != ENOENT) - log_error_errno(errno, "Failed to open /etc/crypttab: %m"); - return 0; - } - - if (fstat(fileno(f), &st) < 0) { - log_error_errno(errno, "Failed to stat /etc/crypttab: %m"); - return 0; - } - - for (;;) { - int r, k; - char line[LINE_MAX], *l, *uuid; - crypto_device *d = NULL; - _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL; - - if (!fgets(line, sizeof(line), f)) - break; - - crypttab_line++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options); - if (k < 2 || k > 4) { - log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line); - continue; - } - - uuid = startswith(device, "UUID="); - if (!uuid) - uuid = path_startswith(device, "/dev/disk/by-uuid/"); - if (!uuid) - uuid = startswith(name, "luks-"); - if (uuid) - d = hashmap_get(arg_disks, uuid); - - if (arg_whitelist && !d) { - log_info("Not creating device '%s' because it was not specified on the kernel command line.", name); - continue; - } - - r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options); - if (r < 0) - return r; - - if (d) - d->create = false; - } - - return 0; -} - -static int add_proc_cmdline_devices(void) { - int r; - Iterator i; - crypto_device *d; - - HASHMAP_FOREACH(d, arg_disks, i) { - const char *options; - _cleanup_free_ char *device = NULL; - - if (!d->create) - continue; - - if (!d->name) { - d->name = strappend("luks-", d->uuid); - if (!d->name) - return log_oom(); - } - - device = strappend("UUID=", d->uuid); - if (!device) - return log_oom(); - - if (d->options) - options = d->options; - else if (arg_default_options) - options = arg_default_options; - else - options = "timeout=0"; - - r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options); - if (r < 0) - return r; - } - - return 0; -} - -int main(int argc, char *argv[]) { - int r = EXIT_FAILURE; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - arg_disks = hashmap_new(&string_hash_ops); - if (!arg_disks) - goto cleanup; - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) { - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - r = EXIT_FAILURE; - } - - if (!arg_enabled) { - r = EXIT_SUCCESS; - goto cleanup; - } - - if (add_crypttab_devices() < 0) - goto cleanup; - - if (add_proc_cmdline_devices() < 0) - goto cleanup; - - r = EXIT_SUCCESS; - -cleanup: - free_arg_disks(); - free(arg_default_options); - free(arg_default_keyfile); - - return r; -} diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c deleted file mode 100644 index 9927621ea0..0000000000 --- a/src/cryptsetup/cryptsetup.c +++ /dev/null @@ -1,757 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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 "mount-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */ -static char *arg_cipher = NULL; -static unsigned arg_key_size = 0; -static int arg_key_slot = CRYPT_ANY_SLOT; -static unsigned arg_keyfile_size = 0; -static unsigned arg_keyfile_offset = 0; -static char *arg_hash = NULL; -static char *arg_header = NULL; -static unsigned arg_tries = 3; -static bool arg_readonly = false; -static bool arg_verify = false; -static bool arg_discards = false; -static bool arg_tcrypt_hidden = false; -static bool arg_tcrypt_system = false; -static char **arg_tcrypt_keyfiles = NULL; -static uint64_t arg_offset = 0; -static uint64_t arg_skip = 0; -static usec_t arg_timeout = 0; - -/* Options Debian's crypttab knows we don't: - - precheck= - check= - checkargs= - noearly= - loud= - keyscript= -*/ - -static int parse_one_option(const char *option) { - assert(option); - - /* Handled outside of this tool */ - if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail")) - return 0; - - if (startswith(option, "cipher=")) { - char *t; - - t = strdup(option+7); - if (!t) - return log_oom(); - - free(arg_cipher); - arg_cipher = t; - - } else if (startswith(option, "size=")) { - - if (safe_atou(option+5, &arg_key_size) < 0) { - log_error("size= parse failure, ignoring."); - return 0; - } - - if (arg_key_size % 8) { - log_error("size= not a multiple of 8, ignoring."); - return 0; - } - - arg_key_size /= 8; - - } else if (startswith(option, "key-slot=")) { - - arg_type = CRYPT_LUKS1; - if (safe_atoi(option+9, &arg_key_slot) < 0) { - log_error("key-slot= parse failure, ignoring."); - return 0; - } - - } else if (startswith(option, "tcrypt-keyfile=")) { - - arg_type = CRYPT_TCRYPT; - if (path_is_absolute(option+15)) { - if (strv_extend(&arg_tcrypt_keyfiles, option + 15) < 0) - return log_oom(); - } else - log_error("Key file path '%s' is not absolute. Ignoring.", option+15); - - } else if (startswith(option, "keyfile-size=")) { - - if (safe_atou(option+13, &arg_keyfile_size) < 0) { - log_error("keyfile-size= parse failure, ignoring."); - return 0; - } - - } else if (startswith(option, "keyfile-offset=")) { - - if (safe_atou(option+15, &arg_keyfile_offset) < 0) { - log_error("keyfile-offset= parse failure, ignoring."); - return 0; - } - - } else if (startswith(option, "hash=")) { - char *t; - - t = strdup(option+5); - if (!t) - return log_oom(); - - free(arg_hash); - arg_hash = t; - - } else if (startswith(option, "header=")) { - arg_type = CRYPT_LUKS1; - - if (!path_is_absolute(option+7)) { - log_error("Header path '%s' is not absolute, refusing.", option+7); - return -EINVAL; - } - - if (arg_header) { - log_error("Duplicate header= options, refusing."); - return -EINVAL; - } - - arg_header = strdup(option+7); - if (!arg_header) - return log_oom(); - - } else if (startswith(option, "tries=")) { - - if (safe_atou(option+6, &arg_tries) < 0) { - log_error("tries= parse failure, ignoring."); - return 0; - } - - } else if (STR_IN_SET(option, "readonly", "read-only")) - arg_readonly = true; - else if (streq(option, "verify")) - arg_verify = true; - else if (STR_IN_SET(option, "allow-discards", "discard")) - arg_discards = true; - else if (streq(option, "luks")) - arg_type = CRYPT_LUKS1; - else if (streq(option, "tcrypt")) - arg_type = CRYPT_TCRYPT; - else if (streq(option, "tcrypt-hidden")) { - arg_type = CRYPT_TCRYPT; - arg_tcrypt_hidden = true; - } else if (streq(option, "tcrypt-system")) { - arg_type = CRYPT_TCRYPT; - arg_tcrypt_system = true; - } else if (STR_IN_SET(option, "plain", "swap", "tmp")) - arg_type = CRYPT_PLAIN; - else if (startswith(option, "timeout=")) { - - if (parse_sec(option+8, &arg_timeout) < 0) { - log_error("timeout= parse failure, ignoring."); - return 0; - } - - } else if (startswith(option, "offset=")) { - - if (safe_atou64(option+7, &arg_offset) < 0) { - log_error("offset= parse failure, refusing."); - return -EINVAL; - } - - } else if (startswith(option, "skip=")) { - - if (safe_atou64(option+5, &arg_skip) < 0) { - log_error("skip= parse failure, refusing."); - return -EINVAL; - } - - } else if (!streq(option, "none")) - log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option); - - return 0; -} - -static int parse_options(const char *options) { - const char *word, *state; - size_t l; - int r; - - assert(options); - - FOREACH_WORD_SEPARATOR(word, l, options, ",", state) { - _cleanup_free_ char *o; - - o = strndup(word, l); - if (!o) - return -ENOMEM; - r = parse_one_option(o); - if (r < 0) - return r; - } - - /* sanity-check options */ - if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) { - if (arg_offset) - log_warning("offset= ignored with type %s", arg_type); - if (arg_skip) - log_warning("skip= ignored with type %s", arg_type); - } - - return 0; -} - -static void log_glue(int level, const char *msg, void *usrptr) { - log_debug("%s", msg); -} - -static int disk_major_minor(const char *path, char **ret) { - struct stat st; - - assert(path); - - if (stat(path, &st) < 0) - return -errno; - - if (!S_ISBLK(st.st_mode)) - return -EINVAL; - - if (asprintf(ret, "/dev/block/%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) - return -errno; - - return 0; -} - -static char* disk_description(const char *path) { - - static const char name_fields[] = - "ID_PART_ENTRY_NAME\0" - "DM_NAME\0" - "ID_MODEL_FROM_DATABASE\0" - "ID_MODEL\0"; - - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - struct stat st; - const char *i; - int r; - - assert(path); - - if (stat(path, &st) < 0) - return NULL; - - if (!S_ISBLK(st.st_mode)) - return NULL; - - r = sd_device_new_from_devnum(&device, 'b', st.st_rdev); - if (r < 0) - return NULL; - - NULSTR_FOREACH(i, name_fields) { - const char *name; - - r = sd_device_get_property_value(device, i, &name); - if (r >= 0 && !isempty(name)) - return strdup(name); - } - - return NULL; -} - -static char *disk_mount_point(const char *label) { - _cleanup_free_ char *device = NULL; - _cleanup_endmntent_ FILE *f = NULL; - struct mntent *m; - - /* Yeah, we don't support native systemd unit files here for now */ - - if (asprintf(&device, "/dev/mapper/%s", label) < 0) - return NULL; - - f = setmntent("/etc/fstab", "r"); - if (!f) - return NULL; - - while ((m = getmntent(f))) - if (path_equal(m->mnt_fsname, device)) - return strdup(m->mnt_dir); - - return NULL; -} - -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; - char **p, *id; - int r = 0; - - assert(vol); - assert(src); - assert(ret); - - description = disk_description(src); - mount_point = disk_mount_point(vol); - - if (description && streq(vol, description)) - /* If the description string is simply the - * volume name, then let's not show this - * twice */ - description = mfree(description); - - if (mount_point && description) - r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point); - else if (mount_point) - r = asprintf(&name_buffer, "%s on %s", vol, mount_point); - else if (description) - r = asprintf(&name_buffer, "%s (%s)", description, vol); - - if (r < 0) - return log_oom(); - - name = name_buffer ? name_buffer : vol; - - if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) - return log_oom(); - - if (src) - (void) disk_major_minor(src, &maj_min); - - if (maj_min) { - escaped_name = maj_min; - maj_min = NULL; - } else - escaped_name = cescape(name); - - if (!escaped_name) - return log_oom(); - - id = strjoina("cryptsetup:", escaped_name); - - 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_erase_ char **passwords2 = NULL; - - 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, "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])) { - log_warning("Passwords did not match, retrying."); - return -EAGAIN; - } - } - - strv_uniq(passwords); - - STRV_FOREACH(p, passwords) { - char *c; - - if (strlen(*p)+1 >= arg_key_size) - continue; - - /* Pad password if necessary */ - c = new(char, arg_key_size); - if (!c) - return log_oom(); - - strncpy(c, *p, arg_key_size); - free(*p); - *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) { - - int r = 0; - _cleanup_free_ char *passphrase = NULL; - struct crypt_params_tcrypt params = { - .flags = CRYPT_TCRYPT_LEGACY_MODES, - .keyfiles = (const char **)arg_tcrypt_keyfiles, - .keyfiles_count = strv_length(arg_tcrypt_keyfiles) - }; - - assert(cd); - assert(name); - assert(key_file || (passwords && passwords[0])); - - if (arg_tcrypt_hidden) - params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER; - - if (arg_tcrypt_system) - params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER; - - if (key_file) { - r = read_one_line_file(key_file, &passphrase); - if (r < 0) { - log_error_errno(r, "Failed to read password file '%s': %m", key_file); - return -EAGAIN; - } - - params.passphrase = passphrase; - } else - params.passphrase = passwords[0]; - params.passphrase_size = strlen(params.passphrase); - - r = crypt_load(cd, CRYPT_TCRYPT, ¶ms); - if (r < 0) { - if (key_file && r == -EPERM) { - log_error("Failed to activate using password file '%s'.", key_file); - return -EAGAIN; - } - return r; - } - - return crypt_activate_by_volume_key(cd, name, NULL, 0, flags); -} - -static int attach_luks_or_plain(struct crypt_device *cd, - const char *name, - const char *key_file, - const char *data_device, - char **passwords, - uint32_t flags) { - int r = 0; - bool pass_volume_key = false; - - assert(cd); - assert(name); - assert(key_file || passwords); - - if (!arg_type || streq(arg_type, CRYPT_LUKS1)) { - r = crypt_load(cd, CRYPT_LUKS1, NULL); - if (r < 0) { - log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd)); - return r; - } - - if (data_device) - r = crypt_set_data_device(cd, data_device); - } - - if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) { - struct crypt_params_plain params = { - .offset = arg_offset, - .skip = arg_skip, - }; - const char *cipher, *cipher_mode; - _cleanup_free_ char *truncated_cipher = NULL; - - if (arg_hash) { - /* plain isn't a real hash type. it just means "use no hash" */ - if (!streq(arg_hash, "plain")) - params.hash = arg_hash; - } else if (!key_file) - /* for CRYPT_PLAIN, the behaviour of cryptsetup - * package is to not hash when a key file is provided */ - params.hash = "ripemd160"; - - if (arg_cipher) { - size_t l; - - l = strcspn(arg_cipher, "-"); - truncated_cipher = strndup(arg_cipher, l); - if (!truncated_cipher) - return log_oom(); - - cipher = truncated_cipher; - cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain"; - } else { - cipher = "aes"; - cipher_mode = "cbc-essiv:sha256"; - } - - /* for CRYPT_PLAIN limit reads - * from keyfile to key length, and - * ignore keyfile-size */ - arg_keyfile_size = arg_key_size; - - /* In contrast to what the name - * crypt_setup() might suggest this - * doesn't actually format anything, - * it just configures encryption - * parameters when used for plain - * mode. */ - r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, ¶ms); - - /* hash == NULL implies the user passed "plain" */ - pass_volume_key = (params.hash == NULL); - } - - if (r < 0) - return log_error_errno(r, "Loading of cryptographic parameters failed: %m"); - - log_info("Set cipher %s, mode %s, key size %i bits for device %s.", - crypt_get_cipher(cd), - crypt_get_cipher_mode(cd), - crypt_get_volume_key_size(cd)*8, - 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); - if (r < 0) { - log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); - return -EAGAIN; - } - } else { - char **p; - - STRV_FOREACH(p, passwords) { - if (pass_volume_key) - r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags); - else - r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags); - - if (r >= 0) - break; - } - } - - return r; -} - -static int help(void) { - - printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n" - "%s detach VOLUME\n\n" - "Attaches or detaches an encrypted block device.\n", - program_invocation_short_name, - program_invocation_short_name); - - return 0; -} - -int main(int argc, char *argv[]) { - int r = EXIT_FAILURE; - struct crypt_device *cd = NULL; - - if (argc <= 1) { - help(); - return EXIT_SUCCESS; - } - - if (argc < 3) { - log_error("This program requires at least two arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (streq(argv[1], "attach")) { - uint32_t flags = 0; - int k; - unsigned tries; - usec_t until; - crypt_status_info status; - const char *key_file = NULL; - - /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ - - if (argc < 4) { - log_error("attach requires at least two arguments."); - goto finish; - } - - if (argc >= 5 && - argv[4][0] && - !streq(argv[4], "-") && - !streq(argv[4], "none")) { - - if (!path_is_absolute(argv[4])) - log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]); - else - key_file = argv[4]; - } - - if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) { - if (parse_options(argv[5]) < 0) - goto finish; - } - - /* A delicious drop of snake oil */ - mlockall(MCL_FUTURE); - - if (arg_header) { - log_debug("LUKS header: %s", arg_header); - 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; - } - - crypt_set_log_callback(cd, log_glue, NULL); - - status = crypt_status(cd, argv[2]); - if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) { - log_info("Volume %s already active.", argv[2]); - r = EXIT_SUCCESS; - goto finish; - } - - if (arg_readonly) - flags |= CRYPT_ACTIVATE_READONLY; - - if (arg_discards) - flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; - - if (arg_timeout > 0) - until = now(CLOCK_MONOTONIC) + arg_timeout; - else - until = 0; - - arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8)); - - if (key_file) { - struct stat st; - - /* Ideally we'd do this on the open fd, but since this is just a - * warning it's OK to do this in two steps. */ - if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005)) - log_warning("Key file %s is world-readable. This is not a good idea!", key_file); - } - - for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { - _cleanup_strv_free_erase_ char **passwords = NULL; - - if (!key_file) { - k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); - if (k == -EAGAIN) - continue; - else if (k < 0) - goto finish; - } - - if (streq_ptr(arg_type, CRYPT_TCRYPT)) - k = attach_tcrypt(cd, argv[2], key_file, passwords, flags); - else - k = attach_luks_or_plain(cd, - argv[2], - key_file, - arg_header ? argv[3] : NULL, - passwords, - flags); - if (k >= 0) - break; - else if (k == -EAGAIN) { - key_file = NULL; - continue; - } else if (k != -EPERM) { - log_error_errno(k, "Failed to activate: %m"); - goto finish; - } - - log_warning("Invalid passphrase."); - } - - if (arg_tries != 0 && tries >= arg_tries) { - log_error("Too many attempts; giving up."); - r = EXIT_FAILURE; - goto finish; - } - - } else if (streq(argv[1], "detach")) { - int k; - - k = crypt_init_by_name(&cd, argv[2]); - if (k == -ENODEV) { - log_info("Volume %s already inactive.", argv[2]); - r = EXIT_SUCCESS; - goto finish; - } else if (k) { - log_error_errno(k, "crypt_init_by_name() failed: %m"); - goto finish; - } - - crypt_set_log_callback(cd, log_glue, NULL); - - k = crypt_deactivate(cd, argv[2]); - if (k < 0) { - log_error_errno(k, "Failed to deactivate: %m"); - goto finish; - } - - } else { - log_error("Unknown verb %s.", argv[1]); - goto finish; - } - - r = EXIT_SUCCESS; - -finish: - - if (cd) - crypt_free(cd); - - free(arg_cipher); - free(arg_hash); - free(arg_header); - strv_free(arg_tcrypt_keyfiles); - - return r; -} diff --git a/src/dbus1-generator/Makefile b/src/dbus1-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/dbus1-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/debug-generator/Makefile b/src/debug-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/debug-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c deleted file mode 100644 index 7e80af78e7..0000000000 --- a/src/debug-generator/debug-generator.c +++ /dev/null @@ -1,202 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 "util.h" - -static char *arg_default_unit = NULL; -static const char *arg_dest = "/tmp"; -static char **arg_mask = NULL; -static char **arg_wants = NULL; -static bool arg_debug_shell = false; - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - assert(key); - - if (streq(key, "systemd.mask")) { - - if (!value) - log_error("Missing argument for systemd.mask= kernel command line parameter."); - else { - char *n; - - r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to glob unit name: %m"); - - r = strv_consume(&arg_mask, n); - if (r < 0) - return log_oom(); - } - - } else if (streq(key, "systemd.wants")) { - - if (!value) - log_error("Missing argument for systemd.want= kernel command line parameter."); - else { - char *n; - - r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to glob unit name: %m"); - - r = strv_consume(&arg_wants, n); - if (r < 0) - return log_oom(); - } - - } else if (streq(key, "systemd.debug-shell")) { - - if (value) { - r = parse_boolean(value); - if (r < 0) - log_error("Failed to parse systemd.debug-shell= argument '%s', ignoring.", value); - else - 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; -} - -static int generate_mask_symlinks(void) { - char **u; - int r = 0; - - if (strv_isempty(arg_mask)) - return 0; - - STRV_FOREACH(u, arg_mask) { - _cleanup_free_ char *p = NULL; - - p = strjoin(arg_dest, "/", *u, NULL); - if (!p) - return log_oom(); - - if (symlink("/dev/null", p) < 0) - r = log_error_errno(errno, - "Failed to create mask symlink %s: %m", - p); - } - - return r; -} - -static int generate_wants_symlinks(void) { - char **u; - int r = 0; - - if (strv_isempty(arg_wants)) - return 0; - - STRV_FOREACH(u, arg_wants) { - _cleanup_free_ char *p = NULL, *f = NULL; - - p = strjoin(arg_dest, "/", arg_default_unit, ".wants/", *u, NULL); - if (!p) - return log_oom(); - - f = strappend(SYSTEM_DATA_UNIT_PATH "/", *u); - if (!f) - return log_oom(); - - mkdir_parents_label(p, 0755); - - if (symlink(f, p) < 0) - r = log_error_errno(errno, - "Failed to create wants symlink %s: %m", - p); - } - - return r; -} - -int main(int argc, char *argv[]) { - int r, q; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[2]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - 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"); - - if (arg_debug_shell) { - r = strv_extend(&arg_wants, "debug-shell.service"); - if (r < 0) { - r = log_oom(); - goto finish; - } - } - - r = generate_mask_symlinks(); - - q = generate_wants_symlinks(); - if (q < 0) - r = q; - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - -} diff --git a/src/delta/Makefile b/src/delta/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/delta/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/delta/delta.c b/src/delta/delta.c deleted file mode 100644 index f32744def2..0000000000 --- a/src/delta/delta.c +++ /dev/null @@ -1,635 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "hashmap.h" -#include "locale-util.h" -#include "log.h" -#include "pager.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-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" - "/run\0" - "/usr/local/lib\0" - "/usr/local/share\0" - "/usr/lib\0" - "/usr/share\0" -#ifdef HAVE_SPLIT_USR - "/lib\0" -#endif - ; - -static const char suffixes[] = - "sysctl.d\0" - "tmpfiles.d\0" - "modules-load.d\0" - "binfmt.d\0" - "systemd/system\0" - "systemd/user\0" - "systemd/system-preset\0" - "systemd/user-preset\0" - "udev/rules.d\0" - "modprobe.d\0"; - -static const char have_dropins[] = - "systemd/system\0" - "systemd/user\0"; - -static bool arg_no_pager = false; -static int arg_diff = -1; - -static enum { - SHOW_MASKED = 1 << 0, - SHOW_EQUIVALENT = 1 << 1, - SHOW_REDIRECTED = 1 << 2, - SHOW_OVERRIDDEN = 1 << 3, - SHOW_UNCHANGED = 1 << 4, - SHOW_EXTENDED = 1 << 5, - - SHOW_DEFAULTS = - (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED) -} arg_flags = 0; - -static int equivalent(const char *a, const char *b) { - _cleanup_free_ char *x = NULL, *y = NULL; - - x = canonicalize_file_name(a); - if (!x) - return -errno; - - y = canonicalize_file_name(b); - if (!y) - return -errno; - - return path_equal(x, y); -} - -static int notify_override_masked(const char *top, const char *bottom) { - if (!(arg_flags & SHOW_MASKED)) - return 0; - - printf("%s%s%s %s %s %s\n", - ansi_highlight_red(), "[MASKED]", ansi_normal(), - top, special_glyph(ARROW), bottom); - return 1; -} - -static int notify_override_equivalent(const char *top, const char *bottom) { - if (!(arg_flags & SHOW_EQUIVALENT)) - return 0; - - printf("%s%s%s %s %s %s\n", - ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(), - top, special_glyph(ARROW), bottom); - return 1; -} - -static int notify_override_redirected(const char *top, const char *bottom) { - if (!(arg_flags & SHOW_REDIRECTED)) - return 0; - - printf("%s%s%s %s %s %s\n", - ansi_highlight(), "[REDIRECTED]", ansi_normal(), - top, special_glyph(ARROW), bottom); - return 1; -} - -static int notify_override_overridden(const char *top, const char *bottom) { - if (!(arg_flags & SHOW_OVERRIDDEN)) - return 0; - - printf("%s%s%s %s %s %s\n", - ansi_highlight(), "[OVERRIDDEN]", ansi_normal(), - top, special_glyph(ARROW), bottom); - return 1; -} - -static int notify_override_extended(const char *top, const char *bottom) { - if (!(arg_flags & SHOW_EXTENDED)) - return 0; - - printf("%s%s%s %s %s %s\n", - ansi_highlight(), "[EXTENDED]", ansi_normal(), - top, special_glyph(ARROW), bottom); - return 1; -} - -static int notify_override_unchanged(const char *f) { - if (!(arg_flags & SHOW_UNCHANGED)) - return 0; - - printf("[UNCHANGED] %s\n", f); - return 1; -} - -static int found_override(const char *top, const char *bottom) { - _cleanup_free_ char *dest = NULL; - int k; - pid_t pid; - - assert(top); - assert(bottom); - - if (null_or_empty_path(top) > 0) - return notify_override_masked(top, bottom); - - k = readlink_malloc(top, &dest); - if (k >= 0) { - if (equivalent(dest, bottom) > 0) - return notify_override_equivalent(top, bottom); - else - return notify_override_redirected(top, bottom); - } - - k = notify_override_overridden(top, bottom); - if (!arg_diff) - return k; - - putchar('\n'); - - fflush(stdout); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off diff: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - execlp("diff", "diff", "-us", "--", bottom, top, NULL); - log_error_errno(errno, "Failed to execute diff: %m"); - _exit(EXIT_FAILURE); - } - - wait_for_terminate_and_warn("diff", pid, false); - putchar('\n'); - - return k; -} - -static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) { - _cleanup_free_ char *unit = NULL; - _cleanup_free_ char *path = NULL; - _cleanup_strv_free_ char **list = NULL; - char **file; - char *c; - int r; - - assert(!endswith(drop, "/")); - - path = strjoin(toppath, "/", drop, NULL); - if (!path) - return -ENOMEM; - - log_debug("Looking at %s", path); - - unit = strdup(drop); - if (!unit) - return -ENOMEM; - - c = strrchr(unit, '.'); - if (!c) - return -EINVAL; - *c = 0; - - r = get_files_in_directory(path, &list); - if (r < 0) - return log_error_errno(r, "Failed to enumerate %s: %m", path); - - STRV_FOREACH(file, list) { - Hashmap *h; - int k; - char *p; - char *d; - - if (!endswith(*file, ".conf")) - continue; - - p = strjoin(path, "/", *file, NULL); - if (!p) - return -ENOMEM; - d = p + strlen(toppath) + 1; - - log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); - k = hashmap_put(top, d, p); - if (k >= 0) { - p = strdup(p); - if (!p) - return -ENOMEM; - d = p + strlen(toppath) + 1; - } else if (k != -EEXIST) { - free(p); - return k; - } - - log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); - free(hashmap_remove(bottom, d)); - k = hashmap_put(bottom, d, p); - if (k < 0) { - free(p); - return k; - } - - h = hashmap_get(drops, unit); - if (!h) { - h = hashmap_new(&string_hash_ops); - if (!h) - return -ENOMEM; - hashmap_put(drops, unit, h); - unit = strdup(unit); - if (!unit) - return -ENOMEM; - } - - p = strdup(p); - if (!p) - return -ENOMEM; - - log_debug("Adding to drops: %s %s %s %s %s", - unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); - k = hashmap_put(h, basename(p), p); - if (k < 0) { - free(p); - if (k != -EEXIST) - return k; - } - } - return 0; -} - -static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) { - _cleanup_closedir_ DIR *d; - - assert(top); - assert(bottom); - assert(drops); - assert(path); - - log_debug("Looking at %s", path); - - d = opendir(path); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open %s: %m", path); - } - - for (;;) { - struct dirent *de; - int k; - char *p; - - errno = 0; - de = readdir(d); - if (!de) - return -errno; - - dirent_ensure_type(d, de); - - if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) - enumerate_dir_d(top, bottom, drops, path, de->d_name); - - if (!dirent_is_file(de)) - continue; - - p = strjoin(path, "/", de->d_name, NULL); - if (!p) - return -ENOMEM; - - log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p); - k = hashmap_put(top, basename(p), p); - if (k >= 0) { - p = strdup(p); - if (!p) - return -ENOMEM; - } else if (k != -EEXIST) { - free(p); - return k; - } - - log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p); - free(hashmap_remove(bottom, basename(p))); - k = hashmap_put(bottom, basename(p), p); - if (k < 0) { - free(p); - return k; - } - } -} - -static int process_suffix(const char *suffix, const char *onlyprefix) { - const char *p; - char *f; - Hashmap *top, *bottom, *drops; - Hashmap *h; - char *key; - int r = 0, k; - Iterator i, j; - int n_found = 0; - bool dropins; - - assert(suffix); - assert(!startswith(suffix, "/")); - assert(!strstr(suffix, "//")); - - dropins = nulstr_contains(have_dropins, suffix); - - top = hashmap_new(&string_hash_ops); - bottom = hashmap_new(&string_hash_ops); - drops = hashmap_new(&string_hash_ops); - if (!top || !bottom || !drops) { - r = -ENOMEM; - goto finish; - } - - NULSTR_FOREACH(p, prefixes) { - _cleanup_free_ char *t = NULL; - - t = strjoin(p, "/", suffix, NULL); - if (!t) { - r = -ENOMEM; - goto finish; - } - - k = enumerate_dir(top, bottom, drops, t, dropins); - if (r == 0) - r = k; - } - - HASHMAP_FOREACH_KEY(f, key, top, i) { - char *o; - - o = hashmap_get(bottom, key); - assert(o); - - if (!onlyprefix || startswith(o, onlyprefix)) { - if (path_equal(o, f)) { - notify_override_unchanged(f); - } else { - k = found_override(f, o); - if (k < 0) - r = k; - else - n_found += k; - } - } - - h = hashmap_get(drops, key); - if (h) - HASHMAP_FOREACH(o, h, j) - if (!onlyprefix || startswith(o, onlyprefix)) - n_found += notify_override_extended(f, o); - } - -finish: - hashmap_free_free(top); - hashmap_free_free(bottom); - - HASHMAP_FOREACH_KEY(h, key, drops, i) { - hashmap_free_free(hashmap_remove(drops, key)); - hashmap_remove(drops, key); - free(key); - } - hashmap_free(drops); - - return r < 0 ? r : n_found; -} - -static int process_suffixes(const char *onlyprefix) { - const char *n; - int n_found = 0, r; - - NULSTR_FOREACH(n, suffixes) { - r = process_suffix(n, onlyprefix); - if (r < 0) - return r; - - n_found += r; - } - - return n_found; -} - -static int process_suffix_chop(const char *arg) { - const char *p; - - assert(arg); - - if (!path_is_absolute(arg)) - return process_suffix(arg, NULL); - - /* Strip prefix from the suffix */ - NULSTR_FOREACH(p, prefixes) { - const char *suffix; - - suffix = startswith(arg, p); - if (suffix) { - suffix += strspn(suffix, "/"); - if (*suffix) - return process_suffix(suffix, NULL); - else - return process_suffixes(arg); - } - } - - log_error("Invalid suffix specification %s.", arg); - return -EINVAL; -} - -static void help(void) { - printf("%s [OPTIONS...] [SUFFIX...]\n\n" - "Find overridden configuration files.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --diff[=1|0] Show a diff when overridden files differ\n" - " -t --type=LIST... Only display a selected set of override types\n" - , program_invocation_short_name); -} - -static int parse_flags(const char *flag_str, int flags) { - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) { - if (strneq("masked", word, l)) - flags |= SHOW_MASKED; - else if (strneq ("equivalent", word, l)) - flags |= SHOW_EQUIVALENT; - else if (strneq("redirected", word, l)) - flags |= SHOW_REDIRECTED; - else if (strneq("overridden", word, l)) - flags |= SHOW_OVERRIDDEN; - else if (strneq("unchanged", word, l)) - flags |= SHOW_UNCHANGED; - else if (strneq("extended", word, l)) - flags |= SHOW_EXTENDED; - else if (strneq("default", word, l)) - flags |= SHOW_DEFAULTS; - else - return -EINVAL; - } - return flags; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_NO_PAGER = 0x100, - ARG_DIFF, - ARG_VERSION - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "diff", optional_argument, NULL, ARG_DIFF }, - { "type", required_argument, NULL, 't' }, - {} - }; - - int c; - - assert(argc >= 1); - assert(argv); - - while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case 't': { - int f; - f = parse_flags(optarg, arg_flags); - if (f < 0) { - log_error("Failed to parse flags field."); - return -EINVAL; - } - arg_flags = f; - break; - } - - case ARG_DIFF: - if (!optarg) - arg_diff = 1; - else { - int b; - - b = parse_boolean(optarg); - if (b < 0) { - log_error("Failed to parse diff boolean."); - return -EINVAL; - } - - arg_diff = b; - } - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, k, n_found = 0; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_flags == 0) - arg_flags = SHOW_DEFAULTS; - - if (arg_diff < 0) - arg_diff = !!(arg_flags & SHOW_OVERRIDDEN); - else if (arg_diff) - arg_flags |= SHOW_OVERRIDDEN; - - pager_open(arg_no_pager, false); - - if (optind < argc) { - int i; - - for (i = optind; i < argc; i++) { - path_kill_slashes(argv[i]); - - k = process_suffix_chop(argv[i]); - if (k < 0) - r = k; - else - n_found += k; - } - - } else { - k = process_suffixes(NULL); - if (k < 0) - r = k; - else - n_found += k; - } - - if (r >= 0) - printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found); - -finish: - pager_close(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/detect-virt/Makefile b/src/detect-virt/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/detect-virt/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c deleted file mode 100644 index 5d51589a31..0000000000 --- a/src/detect-virt/detect-virt.c +++ /dev/null @@ -1,169 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "util.h" -#include "virt.h" - -static bool arg_quiet = false; -static enum { - ANY_VIRTUALIZATION, - ONLY_VM, - ONLY_CONTAINER, - ONLY_CHROOT, -} arg_mode = ANY_VIRTUALIZATION; - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Detect execution in a virtualized environment.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -c --container Only detect whether we are run in a container\n" - " -v --vm Only detect whether we are run in a VM\n" - " -r --chroot Detect whether we are run in a chroot() environment\n" - " -q --quiet Don't output anything, just set return value\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100 - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "container", no_argument, NULL, 'c' }, - { "vm", no_argument, NULL, 'v' }, - { "chroot", no_argument, NULL, 'r' }, - { "quiet", no_argument, NULL, 'q' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'q': - arg_quiet = true; - break; - - case 'c': - arg_mode = ONLY_CONTAINER; - break; - - case 'v': - arg_mode = ONLY_VM; - break; - - case 'r': - arg_mode = ONLY_CHROOT; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind < argc) { - log_error("%s takes no arguments.", program_invocation_short_name); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r; - - /* This is mostly intended to be used for scripts which want - * to detect whether we are being run in a virtualized - * environment or not */ - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - switch (arg_mode) { - - case ONLY_VM: - r = detect_vm(); - if (r < 0) { - log_error_errno(r, "Failed to check for VM: %m"); - return EXIT_FAILURE; - } - - break; - - case ONLY_CONTAINER: - r = detect_container(); - if (r < 0) { - log_error_errno(r, "Failed to check for container: %m"); - return EXIT_FAILURE; - } - - break; - - case ONLY_CHROOT: - r = running_in_chroot(); - if (r < 0) { - log_error_errno(r, "Failed to check for chroot() environment: %m"); - return EXIT_FAILURE; - } - - return r ? EXIT_SUCCESS : EXIT_FAILURE; - - case ANY_VIRTUALIZATION: - default: - r = detect_virtualization(); - if (r < 0) { - log_error_errno(r, "Failed to check for virtualization: %m"); - return EXIT_FAILURE; - } - - break; - } - - if (!arg_quiet) - puts(virtualization_to_string(r)); - - return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/escape/Makefile b/src/escape/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/escape/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/escape/escape.c b/src/escape/escape.c deleted file mode 100644 index 9f39049577..0000000000 --- a/src/escape/escape.c +++ /dev/null @@ -1,237 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Michael Biebl - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "log.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" - -static enum { - ACTION_ESCAPE, - ACTION_UNESCAPE, - ACTION_MANGLE -} arg_action = ACTION_ESCAPE; -static const char *arg_suffix = NULL; -static const char *arg_template = NULL; -static bool arg_path = false; - -static void help(void) { - printf("%s [OPTIONS...] [NAME...]\n\n" - "Show system and user paths.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --suffix=SUFFIX Unit suffix to append to escaped strings\n" - " --template=TEMPLATE Insert strings as instance into template\n" - " -u --unescape Unescape strings\n" - " -m --mangle Mangle strings\n" - " -p --path When escaping/unescaping assume the string is a path\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_SUFFIX, - ARG_TEMPLATE - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "suffix", required_argument, NULL, ARG_SUFFIX }, - { "template", required_argument, NULL, ARG_TEMPLATE }, - { "unescape", no_argument, NULL, 'u' }, - { "mangle", no_argument, NULL, 'm' }, - { "path", no_argument, NULL, 'p' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hump", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_SUFFIX: - - if (unit_type_from_string(optarg) < 0) { - log_error("Invalid unit suffix type %s.", optarg); - return -EINVAL; - } - - arg_suffix = optarg; - break; - - case ARG_TEMPLATE: - - if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE)) { - log_error("Template name %s is not valid.", optarg); - return -EINVAL; - } - - arg_template = optarg; - break; - - case 'u': - arg_action = ACTION_UNESCAPE; - break; - - case 'm': - arg_action = ACTION_MANGLE; - break; - - case 'p': - arg_path = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind >= argc) { - log_error("Not enough arguments."); - return -EINVAL; - } - - if (arg_template && arg_suffix) { - log_error("--suffix= and --template= may not be combined."); - return -EINVAL; - } - - if ((arg_template || arg_suffix) && arg_action != ACTION_ESCAPE) { - log_error("--suffix= and --template= are not compatible with --unescape or --mangle."); - return -EINVAL; - } - - if (arg_path && !IN_SET(arg_action, ACTION_ESCAPE, ACTION_UNESCAPE)) { - log_error("--path may not be combined with --mangle."); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - char **i; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - STRV_FOREACH(i, argv + optind) { - _cleanup_free_ char *e = NULL; - - switch (arg_action) { - - case ACTION_ESCAPE: - if (arg_path) { - r = unit_name_path_escape(*i, &e); - if (r < 0) { - log_error_errno(r, "Failed to escape string: %m"); - goto finish; - } - } else { - e = unit_name_escape(*i); - if (!e) { - r = log_oom(); - goto finish; - } - } - - if (arg_template) { - char *x; - - r = unit_name_replace_instance(arg_template, e, &x); - if (r < 0) { - log_error_errno(r, "Failed to replace instance: %m"); - goto finish; - } - - free(e); - e = x; - } else if (arg_suffix) { - char *x; - - x = strjoin(e, ".", arg_suffix, NULL); - if (!x) { - r = log_oom(); - goto finish; - } - - free(e); - e = x; - } - - break; - - case ACTION_UNESCAPE: - if (arg_path) - r = unit_name_path_unescape(*i, &e); - else - r = unit_name_unescape(*i, &e); - - if (r < 0) { - log_error_errno(r, "Failed to unescape string: %m"); - goto finish; - } - break; - - case ACTION_MANGLE: - r = unit_name_mangle(*i, UNIT_NAME_NOGLOB, &e); - if (r < 0) { - log_error_errno(r, "Failed to mangle name: %m"); - goto finish; - } - break; - } - - if (i != argv+optind) - fputc(' ', stdout); - - fputs(e, stdout); - } - - fputc('\n', stdout); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/firstboot/Makefile b/src/firstboot/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/firstboot/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c deleted file mode 100644 index 83a21eaf0e..0000000000 --- a/src/firstboot/firstboot.c +++ /dev/null @@ -1,870 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "ask-password-api.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "locale-util.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "random-util.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-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 */ -static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ -static char *arg_timezone = NULL; -static char *arg_hostname = NULL; -static sd_id128_t arg_machine_id = {}; -static char *arg_root_password = NULL; -static bool arg_prompt_locale = false; -static bool arg_prompt_timezone = false; -static bool arg_prompt_hostname = false; -static bool arg_prompt_root_password = false; -static bool arg_copy_locale = false; -static bool arg_copy_timezone = false; -static bool arg_copy_root_password = false; - -static bool press_any_key(void) { - char k = 0; - bool need_nl = true; - - printf("-- Press any key to proceed --"); - fflush(stdout); - - (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl); - - if (need_nl) - putchar('\n'); - - return k != 'q'; -} - -static void print_welcome(void) { - _cleanup_free_ char *pretty_name = NULL; - const char *os_release = NULL; - static bool done = false; - int r; - - if (done) - return; - - os_release = prefix_roota(arg_root, "/etc/os-release"); - r = parse_env_file(os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - if (r == -ENOENT) { - - os_release = prefix_roota(arg_root, "/usr/lib/os-release"); - r = parse_env_file(os_release, NEWLINE, - "PRETTY_NAME", &pretty_name, - NULL); - } - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file: %m"); - - printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", - isempty(pretty_name) ? "GNU/Linux" : pretty_name); - - press_any_key(); - - done = true; -} - -static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) { - unsigned n, per_column, i, j; - unsigned break_lines, break_modulo; - - assert(n_columns > 0); - - n = strv_length(x); - per_column = (n + n_columns - 1) / n_columns; - - break_lines = lines(); - if (break_lines > 2) - break_lines--; - - /* The first page gets two extra lines, since we want to show - * a title */ - break_modulo = break_lines; - if (break_modulo > 3) - break_modulo -= 3; - - for (i = 0; i < per_column; i++) { - - for (j = 0; j < n_columns; j ++) { - _cleanup_free_ char *e = NULL; - - if (j * per_column + i >= n) - break; - - e = ellipsize(x[j * per_column + i], width, percentage); - if (!e) - return log_oom(); - - printf("%4u) %-*s", j * per_column + i + 1, width, e); - } - - putchar('\n'); - - /* on the first screen we reserve 2 extra lines for the title */ - if (i % break_lines == break_modulo) { - if (!press_any_key()) - return 0; - } - } - - return 0; -} - -static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) { - int r; - - assert(text); - assert(is_valid); - assert(ret); - - for (;;) { - _cleanup_free_ char *p = NULL; - unsigned u; - - r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); - if (r < 0) - return log_error_errno(r, "Failed to query user: %m"); - - if (isempty(p)) { - log_warning("No data entered, skipping."); - return 0; - } - - r = safe_atou(p, &u); - if (r >= 0) { - char *c; - - if (u <= 0 || u > strv_length(l)) { - log_error("Specified entry number out of range."); - continue; - } - - log_info("Selected '%s'.", l[u-1]); - - c = strdup(l[u-1]); - if (!c) - return log_oom(); - - free(*ret); - *ret = c; - return 0; - } - - if (!is_valid(p)) { - log_error("Entered data invalid."); - continue; - } - - free(*ret); - *ret = p; - p = 0; - return 0; - } -} - -static int prompt_locale(void) { - _cleanup_strv_free_ char **locales = NULL; - int r; - - if (arg_locale || arg_locale_messages) - return 0; - - if (!arg_prompt_locale) - return 0; - - r = get_locales(&locales); - if (r < 0) - return log_error_errno(r, "Cannot query locales list: %m"); - - print_welcome(); - - printf("\nAvailable Locales:\n\n"); - r = show_menu(locales, 3, 22, 60); - if (r < 0) - return r; - - putchar('\n'); - - r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); - if (r < 0) - return r; - - if (isempty(arg_locale)) - return 0; - - r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); - if (r < 0) - return r; - - return 0; -} - -static int process_locale(void) { - const char *etc_localeconf; - char* locales[3]; - unsigned i = 0; - int r; - - etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); - if (laccess(etc_localeconf, F_OK) >= 0) - return 0; - - if (arg_copy_locale && arg_root) { - - mkdir_parents(etc_localeconf, 0755); - r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0); - if (r != -ENOENT) { - if (r < 0) - return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); - - log_info("%s copied.", etc_localeconf); - return 0; - } - } - - r = prompt_locale(); - if (r < 0) - return r; - - if (!isempty(arg_locale)) - locales[i++] = strjoina("LANG=", arg_locale); - if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) - locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages); - - if (i == 0) - return 0; - - locales[i] = NULL; - - mkdir_parents(etc_localeconf, 0755); - r = write_env_file(etc_localeconf, locales); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); - - log_info("%s written.", etc_localeconf); - return 0; -} - -static int prompt_timezone(void) { - _cleanup_strv_free_ char **zones = NULL; - int r; - - if (arg_timezone) - return 0; - - if (!arg_prompt_timezone) - return 0; - - r = get_timezones(&zones); - if (r < 0) - return log_error_errno(r, "Cannot query timezone list: %m"); - - print_welcome(); - - printf("\nAvailable Time Zones:\n\n"); - r = show_menu(zones, 3, 22, 30); - if (r < 0) - return r; - - putchar('\n'); - - r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone); - if (r < 0) - return r; - - return 0; -} - -static int process_timezone(void) { - const char *etc_localtime, *e; - int r; - - etc_localtime = prefix_roota(arg_root, "/etc/localtime"); - if (laccess(etc_localtime, F_OK) >= 0) - return 0; - - if (arg_copy_timezone && arg_root) { - _cleanup_free_ char *p = NULL; - - r = readlink_malloc("/etc/localtime", &p); - if (r != -ENOENT) { - if (r < 0) - return log_error_errno(r, "Failed to read host timezone: %m"); - - mkdir_parents(etc_localtime, 0755); - if (symlink(p, etc_localtime) < 0) - return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); - - log_info("%s copied.", etc_localtime); - return 0; - } - } - - r = prompt_timezone(); - if (r < 0) - return r; - - if (isempty(arg_timezone)) - return 0; - - e = strjoina("../usr/share/zoneinfo/", arg_timezone); - - mkdir_parents(etc_localtime, 0755); - if (symlink(e, etc_localtime) < 0) - return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); - - log_info("%s written", etc_localtime); - return 0; -} - -static int prompt_hostname(void) { - int r; - - if (arg_hostname) - return 0; - - if (!arg_prompt_hostname) - return 0; - - print_welcome(); - putchar('\n'); - - for (;;) { - _cleanup_free_ char *h = NULL; - - r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); - if (r < 0) - return log_error_errno(r, "Failed to query hostname: %m"); - - if (isempty(h)) { - log_warning("No hostname entered, skipping."); - break; - } - - if (!hostname_is_valid(h, true)) { - log_error("Specified hostname invalid."); - continue; - } - - /* Get rid of the trailing dot that we allow, but don't want to see */ - arg_hostname = hostname_cleanup(h); - h = NULL; - break; - } - - return 0; -} - -static int process_hostname(void) { - const char *etc_hostname; - int r; - - etc_hostname = prefix_roota(arg_root, "/etc/hostname"); - if (laccess(etc_hostname, F_OK) >= 0) - return 0; - - r = prompt_hostname(); - if (r < 0) - return r; - - if (isempty(arg_hostname)) - return 0; - - mkdir_parents(etc_hostname, 0755); - r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_hostname); - - log_info("%s written.", etc_hostname); - return 0; -} - -static int process_machine_id(void) { - const char *etc_machine_id; - char id[SD_ID128_STRING_MAX]; - int r; - - etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - if (laccess(etc_machine_id, F_OK) >= 0) - return 0; - - if (sd_id128_is_null(arg_machine_id)) - return 0; - - mkdir_parents(etc_machine_id, 0755); - r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_error_errno(r, "Failed to write machine id: %m"); - - log_info("%s written.", etc_machine_id); - return 0; -} - -static int prompt_root_password(void) { - const char *msg1, *msg2, *etc_shadow; - int r; - - if (arg_root_password) - return 0; - - if (!arg_prompt_root_password) - return 0; - - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - - print_welcome(); - putchar('\n'); - - msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); - msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); - - for (;;) { - _cleanup_string_free_erase_ char *a = NULL, *b = NULL; - - r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); - if (r < 0) - return log_error_errno(r, "Failed to query root password: %m"); - - if (isempty(a)) { - log_warning("No password entered, skipping."); - break; - } - - r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); - if (r < 0) - return log_error_errno(r, "Failed to query root password: %m"); - - if (!streq(a, b)) { - log_error("Entered passwords did not match, please try again."); - continue; - } - - arg_root_password = a; - a = NULL; - break; - } - - return 0; -} - -static int write_root_shadow(const char *path, const struct spwd *p) { - _cleanup_fclose_ FILE *f = NULL; - assert(path); - assert(p); - - RUN_WITH_UMASK(0777) - f = fopen(path, "wex"); - if (!f) - return -errno; - - errno = 0; - if (putspent(p, f) != 0) - return errno > 0 ? -errno : -EIO; - - return fflush_and_check(f); -} - -static int process_root_password(void) { - - static const char table[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "./"; - - struct spwd item = { - .sp_namp = (char*) "root", - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; - - _cleanup_close_ int lock = -1; - char salt[3+16+1+1]; - uint8_t raw[16]; - unsigned i; - char *j; - - const char *etc_shadow; - int r; - - etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (laccess(etc_shadow, F_OK) >= 0) - return 0; - - mkdir_parents(etc_shadow, 0755); - - lock = take_etc_passwd_lock(arg_root); - if (lock < 0) - return log_error_errno(lock, "Failed to take a lock: %m"); - - if (arg_copy_root_password && arg_root) { - struct spwd *p; - - errno = 0; - p = getspnam("root"); - if (p || errno != ENOENT) { - if (!p) { - if (!errno) - errno = EIO; - - return log_error_errno(errno, "Failed to find shadow entry for root: %m"); - } - - r = write_root_shadow(etc_shadow, p); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_shadow); - - log_info("%s copied.", etc_shadow); - return 0; - } - } - - r = prompt_root_password(); - if (r < 0) - return r; - - if (!arg_root_password) - return 0; - - r = dev_urandom(raw, 16); - if (r < 0) - return log_error_errno(r, "Failed to get salt: %m"); - - /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ - assert_cc(sizeof(table) == 64 + 1); - j = stpcpy(salt, "$6$"); - for (i = 0; i < 16; i++) - j[i] = table[raw[i] & 63]; - j[i++] = '$'; - j[i] = 0; - - errno = 0; - item.sp_pwdp = crypt(arg_root_password, salt); - if (!item.sp_pwdp) { - if (!errno) - errno = EINVAL; - - return log_error_errno(errno, "Failed to encrypt password: %m"); - } - - item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - r = write_root_shadow(etc_shadow, &item); - if (r < 0) - return log_error_errno(r, "Failed to write %s: %m", etc_shadow); - - log_info("%s written.", etc_shadow); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Configures basic settings of the system.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - " --locale=LOCALE Set primary locale (LANG=)\n" - " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" - " --timezone=TIMEZONE Set timezone\n" - " --hostname=NAME Set host name\n" - " --machine-ID=ID Set machine ID\n" - " --root-password=PASSWORD Set root password\n" - " --root-password-file=FILE Set root password from file\n" - " --prompt-locale Prompt the user for locale settings\n" - " --prompt-timezone Prompt the user for timezone\n" - " --prompt-hostname Prompt the user for hostname\n" - " --prompt-root-password Prompt the user for root password\n" - " --prompt Prompt for all of the above\n" - " --copy-locale Copy locale from host\n" - " --copy-timezone Copy timezone from host\n" - " --copy-root-password Copy root password from host\n" - " --copy Copy locale, timezone, root password\n" - " --setup-machine-id Generate a new random machine ID\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ROOT, - ARG_LOCALE, - ARG_LOCALE_MESSAGES, - ARG_TIMEZONE, - ARG_HOSTNAME, - ARG_MACHINE_ID, - ARG_ROOT_PASSWORD, - ARG_ROOT_PASSWORD_FILE, - ARG_PROMPT, - ARG_PROMPT_LOCALE, - ARG_PROMPT_TIMEZONE, - ARG_PROMPT_HOSTNAME, - ARG_PROMPT_ROOT_PASSWORD, - ARG_COPY, - ARG_COPY_LOCALE, - ARG_COPY_TIMEZONE, - ARG_COPY_ROOT_PASSWORD, - ARG_SETUP_MACHINE_ID, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "locale", required_argument, NULL, ARG_LOCALE }, - { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, - { "timezone", required_argument, NULL, ARG_TIMEZONE }, - { "hostname", required_argument, NULL, ARG_HOSTNAME }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, - { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, - { "prompt", no_argument, NULL, ARG_PROMPT }, - { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, - { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, - { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, - { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, - { "copy", no_argument, NULL, ARG_COPY }, - { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, - { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, - { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, - { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, - {} - }; - - int r, c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case ARG_LOCALE: - if (!locale_is_valid(optarg)) { - log_error("Locale %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_locale, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_LOCALE_MESSAGES: - if (!locale_is_valid(optarg)) { - log_error("Locale %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_locale_messages, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_TIMEZONE: - if (!timezone_is_valid(optarg)) { - log_error("Timezone %s is not valid.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_timezone, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_ROOT_PASSWORD: - r = free_and_strdup(&arg_root_password, optarg); - if (r < 0) - return log_oom(); - break; - - case ARG_ROOT_PASSWORD_FILE: - arg_root_password = mfree(arg_root_password); - - r = read_one_line_file(optarg, &arg_root_password); - if (r < 0) - return log_error_errno(r, "Failed to read %s: %m", optarg); - - break; - - case ARG_HOSTNAME: - if (!hostname_is_valid(optarg, true)) { - log_error("Host name %s is not valid.", optarg); - return -EINVAL; - } - - hostname_cleanup(optarg); - r = free_and_strdup(&arg_hostname, optarg); - if (r < 0) - return log_oom(); - - break; - - case ARG_MACHINE_ID: - if (sd_id128_from_string(optarg, &arg_machine_id) < 0) { - log_error("Failed to parse machine id %s.", optarg); - return -EINVAL; - } - - break; - - case ARG_PROMPT: - arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; - break; - - case ARG_PROMPT_LOCALE: - arg_prompt_locale = true; - break; - - case ARG_PROMPT_TIMEZONE: - arg_prompt_timezone = true; - break; - - case ARG_PROMPT_HOSTNAME: - arg_prompt_hostname = true; - break; - - case ARG_PROMPT_ROOT_PASSWORD: - arg_prompt_root_password = true; - break; - - case ARG_COPY: - arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; - break; - - case ARG_COPY_LOCALE: - arg_copy_locale = true; - break; - - case ARG_COPY_TIMEZONE: - arg_copy_timezone = true; - break; - - case ARG_COPY_ROOT_PASSWORD: - arg_copy_root_password = true; - break; - - case ARG_SETUP_MACHINE_ID: - - r = sd_id128_randomize(&arg_machine_id); - if (r < 0) - return log_error_errno(r, "Failed to generate randomized machine ID: %m"); - - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = process_locale(); - if (r < 0) - goto finish; - - r = process_timezone(); - if (r < 0) - goto finish; - - r = process_hostname(); - if (r < 0) - goto finish; - - r = process_machine_id(); - if (r < 0) - goto finish; - - r = process_root_password(); - if (r < 0) - goto finish; - -finish: - free(arg_root); - free(arg_locale); - free(arg_locale_messages); - free(arg_timezone); - free(arg_hostname); - string_erase(arg_root_password); - free(arg_root_password); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/fsck/Makefile b/src/fsck/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/fsck/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c deleted file mode 100644 index d32e1d923e..0000000000 --- a/src/fsck/fsck.c +++ /dev/null @@ -1,487 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Holger Hans Peter Freyther - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-device.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 { - FSCK_SUCCESS = 0, - FSCK_ERROR_CORRECTED = 1, - FSCK_SYSTEM_SHOULD_REBOOT = 2, - FSCK_ERRORS_LEFT_UNCORRECTED = 4, - FSCK_OPERATIONAL_ERROR = 8, - FSCK_USAGE_OR_SYNTAX_ERROR = 16, - FSCK_USER_CANCELLED = 32, - FSCK_SHARED_LIB_ERROR = 128, -}; - -static bool arg_skip = false; -static bool arg_force = false; -static bool arg_show_progress = false; -static const char *arg_repair = "-a"; - -static void start_target(const char *target, const char *mode) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(target); - - r = bus_connect_system_systemd(&bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - return; - } - - log_info("Running request %s/start/replace", target); - - /* Start these units only if we can replace base.target with it */ - r = sd_bus_call_method(bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnitReplace", - &error, - NULL, - "sss", "basic.target", target, mode); - - /* Don't print a warning if we aren't called during startup */ - if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) - log_error("Failed to start unit: %s", bus_error_message(&error, r)); -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - assert(key); - - if (streq(key, "fsck.mode") && value) { - - if (streq(value, "auto")) - arg_force = arg_skip = false; - else if (streq(value, "force")) - arg_force = true; - else if (streq(value, "skip")) - arg_skip = true; - else - log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value); - - } else if (streq(key, "fsck.repair") && value) { - - if (streq(value, "preen")) - arg_repair = "-a"; - else { - r = parse_boolean(value); - if (r > 0) - arg_repair = "-y"; - else if (r == 0) - arg_repair = "-n"; - else - log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value); - } - } - -#ifdef HAVE_SYSV_COMPAT - else if (streq(key, "fastboot") && !value) { - log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line."); - arg_skip = true; - - } else if (streq(key, "forcefsck") && !value) { - log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line."); - arg_force = true; - } -#endif - - return 0; -} - -static void test_files(void) { - -#ifdef HAVE_SYSV_COMPAT - if (access("/fastboot", F_OK) >= 0) { - log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system."); - arg_skip = true; - } - - if (access("/forcefsck", F_OK) >= 0) { - log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system."); - arg_force = true; - } -#endif - - arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0; -} - -static double percent(int pass, unsigned long cur, unsigned long max) { - /* Values stolen from e2fsck */ - - static const int pass_table[] = { - 0, 70, 90, 92, 95, 100 - }; - - if (pass <= 0) - return 0.0; - - if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0) - return 100.0; - - return (double) pass_table[pass-1] + - ((double) pass_table[pass] - (double) pass_table[pass-1]) * - (double) cur / (double) max; -} - -static int process_progress(int fd) { - _cleanup_fclose_ FILE *console = NULL, *f = NULL; - usec_t last = 0; - bool locked = false; - int clear = 0, r; - - /* No progress pipe to process? Then we are a NOP. */ - if (fd < 0) - return 0; - - f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -errno; - } - - console = fopen("/dev/console", "we"); - if (!console) - return -ENOMEM; - - for (;;) { - int pass, m; - unsigned long cur, max; - _cleanup_free_ char *device = NULL; - double p; - usec_t t; - - if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) { - - if (ferror(f)) - r = log_warning_errno(errno, "Failed to read from progress pipe: %m"); - else if (feof(f)) - r = 0; - else { - log_warning("Failed to parse progress pipe data"); - r = -EBADMSG; - } - break; - } - - /* Only show one progress counter at max */ - if (!locked) { - if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) - continue; - - locked = true; - } - - /* Only update once every 50ms */ - t = now(CLOCK_MONOTONIC); - if (last + 50 * USEC_PER_MSEC > t) - continue; - - last = t; - - p = percent(pass, cur, max); - fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m); - fflush(console); - - if (m > clear) - clear = m; - } - - if (clear > 0) { - unsigned j; - - fputc('\r', console); - for (j = 0; j < (unsigned) clear; j++) - fputc(' ', console); - fputc('\r', console); - fflush(console); - } - - return r; -} - -static int fsck_progress_socket(void) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/fsck.progress", - }; - - int fd, r; - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - return log_warning_errno(errno, "socket(): %m"); - - if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); - safe_close(fd); - return r; - } - - return fd; -} - -int main(int argc, char *argv[]) { - _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; - _cleanup_(sd_device_unrefp) sd_device *dev = NULL; - const char *device, *type; - bool root_directory; - siginfo_t status; - struct stat st; - int r; - pid_t pid; - - if (argc > 2) { - log_error("This program expects one or no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - test_files(); - - if (!arg_force && arg_skip) { - r = 0; - goto finish; - } - - if (argc > 1) { - device = argv[1]; - - if (stat(device, &st) < 0) { - r = log_error_errno(errno, "Failed to stat %s: %m", device); - goto finish; - } - - if (!S_ISBLK(st.st_mode)) { - log_error("%s is not a block device.", device); - r = -EINVAL; - goto finish; - } - - r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); - if (r < 0) { - log_error_errno(r, "Failed to detect device %s: %m", device); - goto finish; - } - - root_directory = false; - } else { - struct timespec times[2]; - - /* Find root device */ - - if (stat("/", &st) < 0) { - r = log_error_errno(errno, "Failed to stat() the root directory: %m"); - goto finish; - } - - /* Virtual root devices don't need an fsck */ - if (major(st.st_dev) == 0) { - log_debug("Root directory is virtual or btrfs, skipping check."); - r = 0; - goto finish; - } - - /* check if we are already writable */ - times[0] = st.st_atim; - times[1] = st.st_mtim; - - if (utimensat(AT_FDCWD, "/", times, 0) == 0) { - log_info("Root directory is writable, skipping check."); - r = 0; - goto finish; - } - - r = sd_device_new_from_devnum(&dev, 'b', st.st_dev); - if (r < 0) { - log_error_errno(r, "Failed to detect root device: %m"); - goto finish; - } - - r = sd_device_get_devname(dev, &device); - if (r < 0) { - log_error_errno(r, "Failed to detect device node of root directory: %m"); - goto finish; - } - - root_directory = true; - } - - r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type); - if (r >= 0) { - r = fsck_exists(type); - if (r < 0) - log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device); - else if (r == 0) { - log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device); - goto finish; - } - } - - if (arg_show_progress) { - if (pipe(progress_pipe) < 0) { - r = log_error_errno(errno, "pipe(): %m"); - goto finish; - } - } - - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "fork(): %m"); - goto finish; - } - if (pid == 0) { - char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1]; - int progress_socket = -1; - const char *cmdline[9]; - int i = 0; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - /* Close the reading side of the progress pipe */ - progress_pipe[0] = safe_close(progress_pipe[0]); - - /* Try to connect to a progress management daemon, if there is one */ - progress_socket = fsck_progress_socket(); - if (progress_socket >= 0) { - /* If this worked we close the progress pipe early, and just use the socket */ - progress_pipe[1] = safe_close(progress_pipe[1]); - xsprintf(dash_c, "-C%i", progress_socket); - } else if (progress_pipe[1] >= 0) { - /* Otherwise if we have the progress pipe to our own local handle, we use it */ - xsprintf(dash_c, "-C%i", progress_pipe[1]); - } else - dash_c[0] = 0; - - cmdline[i++] = "/sbin/fsck"; - cmdline[i++] = arg_repair; - cmdline[i++] = "-T"; - - /* - * Since util-linux v2.25 fsck uses /run/fsck/.lock files. - * The previous versions use flock for the device and conflict with - * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5 - */ - cmdline[i++] = "-l"; - - if (!root_directory) - cmdline[i++] = "-M"; - - if (arg_force) - cmdline[i++] = "-f"; - - if (!isempty(dash_c)) - cmdline[i++] = dash_c; - - cmdline[i++] = device; - cmdline[i++] = NULL; - - execv(cmdline[0], (char**) cmdline); - _exit(FSCK_OPERATIONAL_ERROR); - } - - progress_pipe[1] = safe_close(progress_pipe[1]); - (void) process_progress(progress_pipe[0]); - progress_pipe[0] = -1; - - r = wait_for_terminate(pid, &status); - if (r < 0) { - log_error_errno(r, "waitid(): %m"); - goto finish; - } - - if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { - - if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) - log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); - else if (status.si_code == CLD_EXITED) - log_error("fsck failed with error code %i.", status.si_status); - else - log_error("fsck failed due to unknown reason."); - - r = -EINVAL; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) - /* System should be rebooted. */ - start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); - else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) - /* Some other problem */ - start_target(SPECIAL_EMERGENCY_TARGET, "replace"); - else { - log_warning("Ignoring error."); - r = 0; - } - - } else - r = 0; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) - (void) touch("/run/systemd/quotacheck"); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/fstab-generator/Makefile b/src/fstab-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/fstab-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c deleted file mode 100644 index 33af553d0d..0000000000 --- a/src/fstab-generator/fstab-generator.c +++ /dev/null @@ -1,723 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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 "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"; -static bool arg_fstab_enabled = true; -static char *arg_root_what = NULL; -static char *arg_root_fstype = NULL; -static char *arg_root_options = NULL; -static int arg_root_rw = -1; -static char *arg_usr_what = NULL; -static char *arg_usr_fstype = NULL; -static char *arg_usr_options = NULL; - -static int add_swap( - const char *what, - struct mntent *me, - bool noauto, - bool nofail) { - - _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(what); - assert(me); - - if (access("/proc/swaps", F_OK) < 0) { - log_info("Swap not supported, ignoring fstab swap entry for %s.", what); - return 0; - } - - if (detect_container() > 0) { - log_info("Running in a container, ignoring fstab swap entry for %s.", what); - return 0; - } - - r = unit_name_from_path(what, ".swap", &name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - unit = strjoin(arg_dest, "/", name, NULL); - if (!unit) - return log_oom(); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, - errno == EEXIST ? - "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : - "Failed to create unit file %s: %m", - unit); - - fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" - "[Unit]\n" - "SourcePath=/etc/fstab\n" - "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" - "[Swap]\n" - "What=%s\n", - what); - - if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults")) - fprintf(f, "Options=%s\n", me->mnt_opts); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); - - /* use what as where, to have a nicer error message */ - r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); - if (r < 0) - return r; - - if (!noauto) { - lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET, - nofail ? ".wants/" : ".requires/", name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - } - - return 0; -} - -static bool mount_is_network(struct mntent *me) { - assert(me); - - return fstab_test_option(me->mnt_opts, "_netdev\0") || - fstype_is_network(me->mnt_type); -} - -static bool mount_in_initrd(struct mntent *me) { - assert(me); - - return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") || - streq(me->mnt_dir, "/usr"); -} - -static int write_idle_timeout(FILE *f, const char *where, const char *opts) { - _cleanup_free_ char *timeout = NULL; - char timespan[FORMAT_TIMESPAN_MAX]; - usec_t u; - int r; - - r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) - return 0; - - r = parse_sec(timeout, &u); - if (r < 0) { - log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); - return 0; - } - - fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0)); - - return 0; -} - -static int write_requires_after(FILE *f, const char *opts) { - _cleanup_strv_free_ char **names = NULL, **units = NULL; - _cleanup_free_ char *res = NULL; - char **s; - int r; - - assert(f); - assert(opts); - - r = fstab_extract_values(opts, "x-systemd.requires", &names); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) - return 0; - - STRV_FOREACH(s, names) { - char *x; - - r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".mount", &x); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - r = strv_consume(&units, x); - if (r < 0) - return log_oom(); - } - - if (units) { - res = strv_join(units, " "); - if (!res) - return log_oom(); - fprintf(f, "After=%1$s\nRequires=%1$s\n", res); - } - - return 0; -} - -static int write_requires_mounts_for(FILE *f, const char *opts) { - _cleanup_strv_free_ char **paths = NULL; - _cleanup_free_ char *res = NULL; - int r; - - assert(f); - assert(opts); - - r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) - return 0; - - res = strv_join(paths, " "); - if (!res) - return log_oom(); - - fprintf(f, "RequiresMountsFor=%s\n", res); - - return 0; -} - -static int add_mount( - const char *what, - const char *where, - const char *fstype, - const char *opts, - int passno, - bool noauto, - bool nofail, - bool automount, - const char *post, - const char *source) { - - _cleanup_free_ char - *name = NULL, *unit = NULL, *lnk = NULL, - *automount_name = NULL, *automount_unit = NULL, - *filtered = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(what); - assert(where); - assert(opts); - assert(post); - assert(source); - - if (streq_ptr(fstype, "autofs")) - return 0; - - if (!is_path(where)) { - log_warning("Mount point %s is not a valid path, ignoring.", where); - return 0; - } - - if (mount_point_is_api(where) || - mount_point_ignore(where)) - return 0; - - if (path_equal(where, "/")) { - if (noauto) - log_warning("Ignoring \"noauto\" for root device"); - if (nofail) - log_warning("Ignoring \"nofail\" for root device"); - if (automount) - log_warning("Ignoring automount option for root device"); - - noauto = nofail = automount = false; - } - - r = unit_name_from_path(where, ".mount", &name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - unit = strjoin(arg_dest, "/", name, NULL); - if (!unit) - return log_oom(); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, - errno == EEXIST ? - "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : - "Failed to create unit file %s: %m", - unit); - - fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" - "[Unit]\n" - "SourcePath=%s\n" - "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", - source); - - if (!noauto && !nofail && !automount) - fprintf(f, "Before=%s\n", post); - - if (!automount && opts) { - r = write_requires_after(f, opts); - if (r < 0) - return r; - r = write_requires_mounts_for(f, opts); - if (r < 0) - return r; - } - - if (passno != 0) { - r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); - if (r < 0) - return r; - } - - fprintf(f, - "\n" - "[Mount]\n" - "What=%s\n" - "Where=%s\n", - what, - where); - - if (!isempty(fstype) && !streq(fstype, "auto")) - fprintf(f, "Type=%s\n", fstype); - - r = generator_write_timeouts(arg_dest, what, where, opts, &filtered); - if (r < 0) - return r; - - if (!isempty(filtered) && !streq(filtered, "defaults")) - fprintf(f, "Options=%s\n", filtered); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); - - if (!noauto && !automount) { - lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - } - - if (automount) { - r = unit_name_from_path(where, ".automount", &automount_name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - automount_unit = strjoin(arg_dest, "/", automount_name, NULL); - if (!automount_unit) - return log_oom(); - - fclose(f); - f = fopen(automount_unit, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit); - - fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" - "[Unit]\n" - "SourcePath=%s\n" - "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", - source); - - fprintf(f, "Before=%s\n", post); - - if (opts) { - r = write_requires_after(f, opts); - if (r < 0) - return r; - r = write_requires_mounts_for(f, opts); - if (r < 0) - return r; - } - - fprintf(f, - "\n" - "[Automount]\n" - "Where=%s\n", - where); - - r = write_idle_timeout(f, where, opts); - if (r < 0) - return r; - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); - - free(lnk); - lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(automount_unit, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - } - - return 0; -} - -static int parse_fstab(bool initrd) { - _cleanup_endmntent_ FILE *f = NULL; - const char *fstab_path; - struct mntent *me; - int r = 0; - - fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab"; - f = setmntent(fstab_path, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open %s: %m", fstab_path); - } - - while ((me = getmntent(f))) { - _cleanup_free_ char *where = NULL, *what = NULL; - bool noauto, nofail; - int k; - - if (initrd && !mount_in_initrd(me)) - continue; - - what = fstab_node_to_udev_node(me->mnt_fsname); - if (!what) - return log_oom(); - - if (is_device_path(what) && path_is_read_only_fs("sys") > 0) { - log_info("Running in a container, ignoring fstab device entry for %s.", what); - continue; - } - - where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir); - if (!where) - return log_oom(); - - if (is_path(where)) - path_kill_slashes(where); - - noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); - nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); - log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s", - what, where, me->mnt_type, - yes_no(noauto), yes_no(nofail)); - - if (streq(me->mnt_type, "swap")) - k = add_swap(what, me, noauto, nofail); - else { - bool automount; - const char *post; - - automount = fstab_test_option(me->mnt_opts, - "comment=systemd.automount\0" - "x-systemd.automount\0"); - if (initrd) - post = SPECIAL_INITRD_FS_TARGET; - else if (mount_is_network(me)) - post = SPECIAL_REMOTE_FS_TARGET; - else - post = SPECIAL_LOCAL_FS_TARGET; - - k = add_mount(what, - where, - me->mnt_type, - me->mnt_opts, - me->mnt_passno, - noauto, - nofail, - automount, - post, - fstab_path); - } - - if (k < 0) - r = k; - } - - return r; -} - -static int add_sysroot_mount(void) { - _cleanup_free_ char *what = NULL; - const char *opts; - int r; - - if (isempty(arg_root_what)) { - log_debug("Could not find a root= entry on the kernel command line."); - return 0; - } - - if (streq(arg_root_what, "gpt-auto")) { - /* This is handled by the gpt-auto generator */ - log_debug("Skipping root directory handling, as gpt-auto was requested."); - return 0; - } - - if (path_equal(arg_root_what, "/dev/nfs")) { - /* This is handled by the kernel or the initrd */ - log_debug("Skipping root directory handling, as /dev/nfs was requested."); - return 0; - } - - what = fstab_node_to_udev_node(arg_root_what); - if (!what) - return log_oom(); - - if (!arg_root_options) - opts = arg_root_rw > 0 ? "rw" : "ro"; - else if (arg_root_rw >= 0 || - !fstab_test_option(arg_root_options, "ro\0" "rw\0")) - opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro"); - else - opts = arg_root_options; - - log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); - - if (is_device_path(what)) { - r = generator_write_initrd_root_device_deps(arg_dest, what); - if (r < 0) - return r; - } - - return add_mount(what, - "/sysroot", - arg_root_fstype, - opts, - is_device_path(what) ? 1 : 0, /* passno */ - false, /* noauto off */ - false, /* nofail off */ - false, /* automount off */ - SPECIAL_INITRD_ROOT_FS_TARGET, - "/proc/cmdline"); -} - -static int add_sysroot_usr_mount(void) { - _cleanup_free_ char *what = NULL; - const char *opts; - - if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options) - return 0; - - if (arg_root_what && !arg_usr_what) { - /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */ - arg_usr_what = strdup(arg_root_what); - if (!arg_usr_what) - return log_oom(); - } - - if (arg_root_fstype && !arg_usr_fstype) { - arg_usr_fstype = strdup(arg_root_fstype); - if (!arg_usr_fstype) - return log_oom(); - } - - if (arg_root_options && !arg_usr_options) { - arg_usr_options = strdup(arg_root_options); - if (!arg_usr_options) - return log_oom(); - } - - if (!arg_usr_what) - return 0; - - what = fstab_node_to_udev_node(arg_usr_what); - if (!what) - return log_oom(); - - if (!arg_usr_options) - opts = arg_root_rw > 0 ? "rw" : "ro"; - else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0")) - opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro"); - else - opts = arg_usr_options; - - log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype)); - return add_mount(what, - "/sysroot/usr", - arg_usr_fstype, - opts, - is_device_path(what) ? 1 : 0, /* passno */ - false, /* noauto off */ - false, /* nofail off */ - false, /* automount off */ - SPECIAL_INITRD_FS_TARGET, - "/proc/cmdline"); -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last - * instance should take precedence. In the case of multiple rootflags= - * or usrflags= the arguments should be concatenated */ - - if (STR_IN_SET(key, "fstab", "rd.fstab") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse fstab switch %s. Ignoring.", value); - else - arg_fstab_enabled = r; - - } else if (streq(key, "root") && value) { - - if (free_and_strdup(&arg_root_what, value) < 0) - return log_oom(); - - } else if (streq(key, "rootfstype") && value) { - - if (free_and_strdup(&arg_root_fstype, value) < 0) - return log_oom(); - - } else if (streq(key, "rootflags") && value) { - char *o; - - o = arg_root_options ? - strjoin(arg_root_options, ",", value, NULL) : - strdup(value); - if (!o) - return log_oom(); - - free(arg_root_options); - arg_root_options = o; - - } else if (streq(key, "mount.usr") && value) { - - if (free_and_strdup(&arg_usr_what, value) < 0) - return log_oom(); - - } else if (streq(key, "mount.usrfstype") && value) { - - if (free_and_strdup(&arg_usr_fstype, value) < 0) - return log_oom(); - - } else if (streq(key, "mount.usrflags") && value) { - char *o; - - o = arg_usr_options ? - strjoin(arg_usr_options, ",", value, NULL) : - strdup(value); - if (!o) - return log_oom(); - - free(arg_usr_options); - arg_usr_options = o; - - } else if (streq(key, "rw") && !value) - arg_root_rw = true; - else if (streq(key, "ro") && !value) - arg_root_rw = false; - - return 0; -} - -int main(int argc, char *argv[]) { - int r = 0; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - /* Always honour root= and usr= in the kernel command line if we are in an initrd */ - if (in_initrd()) { - int k; - - r = add_sysroot_mount(); - - k = add_sysroot_usr_mount(); - if (k < 0) - r = k; - } else - r = 0; - - /* Honour /etc/fstab only when that's enabled */ - if (arg_fstab_enabled) { - int k; - - log_debug("Parsing /etc/fstab"); - - /* Parse the local /etc/fstab, possibly from the initrd */ - k = parse_fstab(false); - if (k < 0) - r = k; - - /* If running in the initrd also parse the /etc/fstab from the host */ - if (in_initrd()) { - log_debug("Parsing /sysroot/etc/fstab"); - - k = parse_fstab(true); - if (k < 0) - r = k; - } - } - - free(arg_root_what); - free(arg_root_fstype); - free(arg_root_options); - - free(arg_usr_what); - free(arg_usr_fstype); - free(arg_usr_options); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/getty-generator/Makefile b/src/getty-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/getty-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c deleted file mode 100644 index b15c76b5b8..0000000000 --- a/src/getty-generator/getty-generator.c +++ /dev/null @@ -1,233 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "mkdir.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"; - -static int add_symlink(const char *fservice, const char *tservice) { - char *from, *to; - int r; - - assert(fservice); - assert(tservice); - - from = strjoina(SYSTEM_DATA_UNIT_PATH "/", fservice); - to = strjoina(arg_dest, "/getty.target.wants/", tservice); - - mkdir_parents_label(to, 0755); - - r = symlink(from, to); - if (r < 0) { - /* In case console=hvc0 is passed this will very likely result in EEXIST */ - if (errno == EEXIST) - return 0; - - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - } - - return 0; -} - -static int add_serial_getty(const char *tty) { - _cleanup_free_ char *n = NULL; - int r; - - assert(tty); - - log_debug("Automatically adding serial getty for /dev/%s.", tty); - - r = unit_name_from_path_instance("serial-getty", tty, ".service", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate service name: %m"); - - return add_symlink("serial-getty@.service", n); -} - -static int add_container_getty(const char *tty) { - _cleanup_free_ char *n = NULL; - int r; - - assert(tty); - - log_debug("Automatically adding container getty for /dev/pts/%s.", tty); - - r = unit_name_from_path_instance("container-getty", tty, ".service", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate service name: %m"); - - return add_symlink("container-getty@.service", n); -} - -static int verify_tty(const char *name) { - _cleanup_close_ int fd = -1; - const char *p; - - /* Some TTYs are weird and have been enumerated but don't work - * when you try to use them, such as classic ttyS0 and - * friends. Let's check that and open the device and run - * isatty() on it. */ - - p = strjoina("/dev/", name); - - /* O_NONBLOCK is essential here, to make sure we don't wait - * for DCD */ - fd = open(p, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) - return -errno; - - errno = 0; - if (isatty(fd) <= 0) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -int main(int argc, char *argv[]) { - - static const char virtualization_consoles[] = - "hvc0\0" - "xvc0\0" - "hvsi0\0" - "sclp_line0\0" - "ttysclp0\0" - "3270!tty1\0"; - - _cleanup_free_ char *active = NULL; - const char *j; - int r; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - if (detect_container() > 0) { - _cleanup_free_ char *container_ttys = NULL; - - log_debug("Automatically adding console shell."); - - if (add_symlink("console-getty.service", "console-getty.service") < 0) - return EXIT_FAILURE; - - /* When $container_ttys is set for PID 1, spawn - * gettys on all ptys named therein. Note that despite - * the variable name we only support ptys here. */ - - r = getenv_for_pid(1, "container_ttys", &container_ttys); - if (r > 0) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, container_ttys, state) { - const char *t; - char tty[l + 1]; - - memcpy(tty, word, l); - tty[l] = 0; - - /* First strip off /dev/ if it is specified */ - t = path_startswith(tty, "/dev/"); - if (!t) - t = tty; - - /* Then, make sure it's actually a pty */ - t = path_startswith(t, "pts/"); - if (!t) - continue; - - if (add_container_getty(t) < 0) - return EXIT_FAILURE; - } - } - - /* Don't add any further magic if we are in a container */ - return EXIT_SUCCESS; - } - - if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { - const char *word, *state; - size_t l; - - /* Automatically add in a serial getty on all active - * kernel consoles */ - FOREACH_WORD(word, l, active, state) { - _cleanup_free_ char *tty = NULL; - - tty = strndup(word, l); - if (!tty) { - log_oom(); - return EXIT_FAILURE; - } - - if (isempty(tty) || tty_is_vc(tty)) - continue; - - if (verify_tty(tty) < 0) - continue; - - /* We assume that gettys on virtual terminals are - * started via manual configuration and do this magic - * only for non-VC terminals. */ - - if (add_serial_getty(tty) < 0) - return EXIT_FAILURE; - } - } - - /* Automatically add in a serial getty on the first - * virtualizer console */ - NULSTR_FOREACH(j, virtualization_consoles) { - char *p; - - p = strjoina("/sys/class/tty/", j); - if (access(p, F_OK) < 0) - continue; - - if (add_serial_getty(j) < 0) - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/gpt-auto-generator/Makefile b/src/gpt-auto-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/gpt-auto-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c deleted file mode 100644 index 39355de953..0000000000 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ /dev/null @@ -1,1040 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "libudev.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 "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" - -static const char *arg_dest = "/tmp"; -static bool arg_enabled = true; -static bool arg_root_enabled = true; -static bool arg_root_rw = false; - -static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) { - _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL; - _cleanup_fclose_ FILE *f = NULL; - char *from, *ret; - int r; - - assert(id); - assert(what); - assert(device); - - r = unit_name_from_path(what, ".device", &d); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - e = unit_name_escape(id); - if (!e) - return log_oom(); - - r = unit_name_build("systemd-cryptsetup", e, ".service", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - p = strjoin(arg_dest, "/", n, NULL); - if (!p) - return log_oom(); - - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", p); - - fprintf(f, - "# Automatically generated by systemd-gpt-auto-generator\n\n" - "[Unit]\n" - "Description=Cryptography Setup for %%I\n" - "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n" - "DefaultDependencies=no\n" - "Conflicts=umount.target\n" - "BindsTo=dev-mapper-%%i.device %s\n" - "Before=umount.target cryptsetup.target\n" - "After=%s\n" - "IgnoreOnIsolate=true\n" - "[Service]\n" - "Type=oneshot\n" - "RemainAfterExit=yes\n" - "TimeoutSec=0\n" /* the binary handles timeouts anyway */ - "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n" - "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", - d, d, - id, what, rw ? "" : "read-only", - id); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write file %s: %m", p); - - from = strjoina("../", n); - - to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - - free(to); - to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - - free(to); - to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - - free(p); - p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); - if (!p) - return log_oom(); - - mkdir_parents_label(p, 0755); - r = write_string_file(p, - "# Automatically generated by systemd-gpt-auto-generator\n\n" - "[Unit]\n" - "JobTimeoutSec=0\n", - WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */ - if (r < 0) - return log_error_errno(r, "Failed to write device drop-in: %m"); - - ret = strappend("/dev/mapper/", id); - if (!ret) - return log_oom(); - - *device = ret; - return 0; -} - -static int add_mount( - const char *id, - const char *what, - const char *where, - const char *fstype, - bool rw, - const char *options, - const char *description, - const char *post) { - - _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(id); - assert(what); - assert(where); - assert(description); - - log_debug("Adding %s: %s %s", where, what, strna(fstype)); - - if (streq_ptr(fstype, "crypto_LUKS")) { - - r = add_cryptsetup(id, what, rw, &crypto_what); - if (r < 0) - return r; - - what = crypto_what; - fstype = NULL; - } - - r = unit_name_from_path(where, ".mount", &unit); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - p = strjoin(arg_dest, "/", unit, NULL); - if (!p) - return log_oom(); - - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); - - fprintf(f, - "# Automatically generated by systemd-gpt-auto-generator\n\n" - "[Unit]\n" - "Description=%s\n" - "Documentation=man:systemd-gpt-auto-generator(8)\n", - description); - - if (post) - fprintf(f, "Before=%s\n", post); - - r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); - if (r < 0) - return r; - - fprintf(f, - "\n" - "[Mount]\n" - "What=%s\n" - "Where=%s\n", - what, where); - - if (fstype) - fprintf(f, "Type=%s\n", fstype); - - if (options) - fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); - else - fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", p); - - if (post) { - lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(p, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - } - - return 0; -} - -static bool path_is_busy(const char *where) { - int r; - - /* already a mountpoint; generators run during reload */ - r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); - if (r > 0) - return false; - - /* the directory might not exist on a stateless system */ - if (r == -ENOENT) - return false; - - if (r < 0) - return true; - - /* not a mountpoint but it contains files */ - if (dir_is_empty(where) <= 0) - return true; - - return false; -} - -static int probe_and_add_mount( - const char *id, - const char *what, - const char *where, - bool rw, - const char *description, - const char *post) { - - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype = NULL; - int r; - - assert(id); - assert(what); - assert(where); - assert(description); - - if (path_is_busy(where)) { - log_debug("%s already populated, ignoring.", where); - return 0; - } - - /* Let's check the partition type here, so that we know - * whether to do LUKS magic. */ - - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober: %m"); - } - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ - return 0; - else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); - - /* add_mount is OK with fstype being NULL. */ - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - - return add_mount( - id, - what, - where, - fstype, - rw, - NULL, - description, - post); -} - -static int add_swap(const char *path) { - _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(path); - - log_debug("Adding swap: %s", path); - - r = unit_name_from_path(path, ".swap", &name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - unit = strjoin(arg_dest, "/", name, NULL); - if (!unit) - return log_oom(); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); - - fprintf(f, - "# Automatically generated by systemd-gpt-auto-generator\n\n" - "[Unit]\n" - "Description=Swap Partition\n" - "Documentation=man:systemd-gpt-auto-generator(8)\n\n" - "[Swap]\n" - "What=%s\n", - path); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); - - lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - - return 0; -} - -#ifdef ENABLE_EFI -static int add_automount( - const char *id, - const char *what, - const char *where, - const char *fstype, - bool rw, - const char *options, - const char *description, - usec_t timeout) { - - _cleanup_free_ char *unit = NULL, *lnk = NULL; - _cleanup_free_ char *opt, *p = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(id); - assert(where); - assert(description); - - if (options) - opt = strjoin(options, ",noauto", NULL); - else - opt = strdup("noauto"); - if (!opt) - return log_oom(); - - r = add_mount(id, - what, - where, - fstype, - rw, - opt, - description, - NULL); - if (r < 0) - return r; - - r = unit_name_from_path(where, ".automount", &unit); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - p = strjoin(arg_dest, "/", unit, NULL); - if (!p) - return log_oom(); - - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); - - fprintf(f, - "# Automatically generated by systemd-gpt-auto-generator\n\n" - "[Unit]\n" - "Description=%s\n" - "Documentation=man:systemd-gpt-auto-generator(8)\n" - "[Automount]\n" - "Where=%s\n" - "TimeoutIdleSec=%lld\n", - description, - where, - (unsigned long long)timeout / USEC_PER_SEC); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", p); - - lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL); - if (!lnk) - return log_oom(); - mkdir_parents_label(lnk, 0755); - - if (symlink(p, lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - - return 0; -} - -static int add_boot(const char *what) { - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype = NULL, *uuid = NULL; - sd_id128_t id, type_id; - int r; - - assert(what); - - if (!is_efi_boot()) { - log_debug("Not an EFI boot, ignoring /boot."); - return 0; - } - - if (in_initrd()) { - log_debug("In initrd, ignoring /boot."); - return 0; - } - - if (detect_container() > 0) { - log_debug("In a container, ignoring /boot."); - 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; - } - - r = efi_loader_get_device_part_uuid(&id); - if (r == -ENOENT) { - log_debug("EFI loader partition unknown."); - return 0; - } - - if (r < 0) - return log_error_errno(r, "Failed to read ESP partition UUID: %m"); - - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober: %m"); - } - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ - return 0; - else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); - - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - if (!streq_ptr(fstype, "vfat")) { - log_debug("Partition for /boot is not a FAT filesystem, ignoring."); - return 0; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); - if (r != 0) { - log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring."); - return 0; - } - - if (sd_id128_from_string(uuid, &type_id) < 0) { - log_debug("Partition for /boot does not have a valid UUID, ignoring."); - return 0; - } - - if (!sd_id128_equal(type_id, id)) { - log_debug("Partition for /boot does not appear to be the partition we are booted from."); - return 0; - } - - r = add_automount("boot", - what, - "/boot", - "vfat", - true, - "umask=0077", - "EFI System Partition Automount", - 120 * USEC_PER_SEC); - - return r; -} -#else -static int add_boot(const char *what) { - return 0; -} -#endif - -static int enumerate_partitions(dev_t devnum) { - - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL; - struct udev_list_entry *first, *item; - struct udev_device *parent = NULL; - const char *name, *node, *pttype, *devtype; - int boot_nr = -1, home_nr = -1, srv_nr = -1; - bool home_rw = true, srv_rw = true; - blkid_partlist pl; - int r, k; - dev_t pn; - - udev = udev_new(); - if (!udev) - return log_oom(); - - d = udev_device_new_from_devnum(udev, 'b', devnum); - if (!d) - return log_oom(); - - name = udev_device_get_devnode(d); - if (!name) - name = udev_device_get_syspath(d); - if (!name) { - log_debug("Device %u:%u does not have a name, ignoring.", - major(devnum), minor(devnum)); - return 0; - } - - parent = udev_device_get_parent(d); - if (!parent) { - log_debug("%s: not a partitioned device, ignoring.", name); - return 0; - } - - /* Does it have a devtype? */ - devtype = udev_device_get_devtype(parent); - if (!devtype) { - log_debug("%s: parent doesn't have a device type, ignoring.", name); - return 0; - } - - /* Is this a disk or a partition? We only care for disks... */ - if (!streq(devtype, "disk")) { - log_debug("%s: parent isn't a raw disk, ignoring.", name); - return 0; - } - - /* Does it have a device node? */ - node = udev_device_get_devnode(parent); - if (!node) { - log_debug("%s: parent device does not have device node, ignoring.", name); - return 0; - } - - log_debug("%s: root device %s.", name, node); - - pn = udev_device_get_devnum(parent); - if (major(pn) == 0) - return 0; - - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "%s: failed to allocate prober: %m", node); - } - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == 1) - return 0; /* no results */ - else if (r == -2) { - log_warning("%s: probe gave ambiguous results, ignoring.", node); - return 0; - } else if (r != 0) - return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node); - - errno = 0; - r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); - if (r != 0) { - if (errno == 0) - return 0; /* No partition table found. */ - - return log_error_errno(errno, "%s: failed to determine partition table type: %m", node); - } - - /* We only do this all for GPT... */ - if (!streq_ptr(pttype, "gpt")) { - log_debug("%s: not a GPT partition table, ignoring.", node); - return 0; - } - - errno = 0; - pl = blkid_probe_get_partitions(b); - if (!pl) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "%s: failed to list partitions: %m", node); - } - - e = udev_enumerate_new(udev); - if (!e) - return log_oom(); - - r = udev_enumerate_add_match_parent(e, parent); - if (r < 0) - return log_oom(); - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return log_oom(); - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return log_error_errno(r, "%s: failed to enumerate partitions: %m", node); - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *q; - unsigned long long flags; - const char *stype, *subnode; - sd_id128_t type_id; - blkid_partition pp; - dev_t qn; - int nr; - - q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!q) - continue; - - qn = udev_device_get_devnum(q); - if (major(qn) == 0) - continue; - - if (qn == devnum) - continue; - - if (qn == pn) - continue; - - subnode = udev_device_get_devnode(q); - if (!subnode) - continue; - - pp = blkid_partlist_devno_to_partition(pl, qn); - if (!pp) - continue; - - nr = blkid_partition_get_partno(pp); - if (nr < 0) - continue; - - stype = blkid_partition_get_type_string(pp); - if (!stype) - continue; - - if (sd_id128_from_string(stype, &type_id) < 0) - continue; - - flags = blkid_partition_get_flags(pp); - - if (sd_id128_equal(type_id, GPT_SWAP)) { - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - if (flags & GPT_FLAG_READ_ONLY) { - log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode); - continue; - } - - k = add_swap(subnode); - if (k < 0) - r = k; - - } else if (sd_id128_equal(type_id, GPT_ESP)) { - - /* We only care for the first /boot partition */ - if (boot && nr >= boot_nr) - continue; - - /* Note that we do not honour the "no-auto" - * flag for the ESP, as it is often unset, to - * hide it from Windows. */ - - boot_nr = nr; - - r = free_and_strdup(&boot, subnode); - if (r < 0) - return log_oom(); - - } else if (sd_id128_equal(type_id, GPT_HOME)) { - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We only care for the first /home partition */ - if (home && nr >= home_nr) - continue; - - home_nr = nr; - home_rw = !(flags & GPT_FLAG_READ_ONLY), - - r = free_and_strdup(&home, subnode); - if (r < 0) - return log_oom(); - - } else if (sd_id128_equal(type_id, GPT_SRV)) { - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We only care for the first /srv partition */ - if (srv && nr >= srv_nr) - continue; - - srv_nr = nr; - srv_rw = !(flags & GPT_FLAG_READ_ONLY), - - r = free_and_strdup(&srv, subnode); - if (r < 0) - return log_oom(); - } - } - - if (boot) { - k = add_boot(boot); - if (k < 0) - r = k; - } - - if (home) { - k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET); - if (k < 0) - r = k; - } - - if (srv) { - k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET); - if (k < 0) - r = k; - } - - return r; -} - -static int get_block_device(const char *path, dev_t *dev) { - struct stat st; - struct statfs sfs; - - assert(path); - assert(dev); - - /* Get's the block device directly backing a file system. If - * the block device is encrypted, returns the device mapper - * block device. */ - - if (lstat(path, &st)) - return -errno; - - if (major(st.st_dev) != 0) { - *dev = st.st_dev; - return 1; - } - - if (statfs(path, &sfs) < 0) - return -errno; - - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) - return btrfs_get_block_device(path, dev); - - return 0; -} - -static int get_block_device_harder(const char *path, dev_t *dev) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *p = NULL, *t = NULL; - struct dirent *de, *found = NULL; - const char *q; - unsigned maj, min; - dev_t dt; - int r; - - assert(path); - assert(dev); - - /* Gets the backing block device for a file system, and - * handles LUKS encrypted file systems, looking for its - * immediate parent, if there is one. */ - - r = get_block_device(path, &dt); - if (r <= 0) - return r; - - if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) - return -ENOMEM; - - d = opendir(p); - if (!d) { - if (errno == ENOENT) - goto fallback; - - return -errno; - } - - FOREACH_DIRENT_ALL(de, d, return -errno) { - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) - continue; - - if (found) /* Don't try to support multiple backing block devices */ - goto fallback; - - found = de; - } - - if (!found) - goto fallback; - - q = strjoina(p, "/", found->d_name, "/dev"); - - r = read_one_line_file(q, &t); - if (r == -ENOENT) - goto fallback; - if (r < 0) - return r; - - if (sscanf(t, "%u:%u", &maj, &min) != 2) - return -EINVAL; - - if (maj == 0) - goto fallback; - - *dev = makedev(maj, min); - return 1; - -fallback: - *dev = dt; - return 1; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - assert(key); - - if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) { - - r = parse_boolean(value); - if (r < 0) - log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value); - else - arg_enabled = r; - - } else if (streq(key, "root") && value) { - - /* Disable root disk logic if there's a root= value - * specified (unless it happens to be "gpt-auto") */ - - arg_root_enabled = streq(value, "gpt-auto"); - - } else if (streq(key, "rw") && !value) - arg_root_rw = true; - else if (streq(key, "ro") && !value) - arg_root_rw = false; - - return 0; -} - -static int add_root_mount(void) { - -#ifdef ENABLE_EFI - int r; - - if (!is_efi_boot()) { - log_debug("Not a EFI boot, not creating root mount."); - return 0; - } - - r = efi_loader_get_device_part_uuid(NULL); - if (r == -ENOENT) { - log_debug("EFI loader partition unknown, exiting."); - return 0; - } else if (r < 0) - return log_error_errno(r, "Failed to read ESP partition UUID: %m"); - - /* OK, we have an ESP partition, this is fantastic, so let's - * wait for a root device to show up. A udev rule will create - * the link for us under the right name. */ - - if (in_initrd()) { - r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); - if (r < 0) - return 0; - } - - return add_mount( - "root", - "/dev/gpt-auto-root", - in_initrd() ? "/sysroot" : "/", - NULL, - arg_root_rw, - NULL, - "Root Partition", - in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); -#else - return 0; -#endif -} - -static int add_mounts(void) { - dev_t devno; - int r; - - r = get_block_device_harder("/", &devno); - if (r < 0) - return log_error_errno(r, "Failed to determine block device of root file system: %m"); - else if (r == 0) { - r = get_block_device_harder("/usr", &devno); - if (r < 0) - return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); - else if (r == 0) { - log_debug("Neither root nor /usr file system are on a (single) block device."); - return 0; - } - } - - return enumerate_partitions(devno); -} - -int main(int argc, char *argv[]) { - int r = 0; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[3]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - if (detect_container() > 0) { - log_debug("In a container, exiting."); - return EXIT_SUCCESS; - } - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - if (!arg_enabled) { - log_debug("Disabled, exiting."); - return EXIT_SUCCESS; - } - - if (arg_root_enabled) - r = add_root_mount(); - - if (!in_initrd()) { - int k; - - k = add_mounts(); - if (k < 0) - r = k; - } - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/grp-boot/Makefile b/src/grp-boot/Makefile new file mode 100644 index 0000000000..df1febe823 --- /dev/null +++ b/src/grp-boot/Makefile @@ -0,0 +1,30 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += bootctl +nested.subdirs += kernel-install +nested.subdirs += systemd-boot + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-boot/bootctl/Makefile b/src/grp-boot/bootctl/Makefile new file mode 100644 index 0000000000..90bbed9fad --- /dev/null +++ b/src/grp-boot/bootctl/Makefile @@ -0,0 +1,53 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_EFI),) +ifneq ($(HAVE_BLKID),) +bootctl_SOURCES = \ + src/boot/bootctl.c + +bootctl_CPPFLAGS = \ + -DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\" \ + -DBOOTLIBDIR=\"$(bootlibdir)\" + +bootctl_CFLAGS = \ + $(BLKID_CFLAGS) + +bootctl_LDADD = \ + libsystemd-shared.la \ + $(BLKID_LIBS) + +bin_PROGRAMS += \ + bootctl + +dist_bashcompletion_data += \ + shell-completion/bash/bootctl + +dist_zshcompletion_data += \ + shell-completion/zsh/_bootctl +endif # HAVE_BLKID +endif # ENABLE_EFI + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-boot/bootctl/bootctl.c b/src/grp-boot/bootctl/bootctl.c new file mode 100644 index 0000000000..19c5d1417f --- /dev/null +++ b/src/grp-boot/bootctl/bootctl.c @@ -0,0 +1,1143 @@ +/*** + This file is part of systemd. + + Copyright 2013-2015 Kay Sievers + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/blkid-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/locale-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/efivars.h" + +static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) { + struct statfs sfs; + struct stat st, st2; + _cleanup_free_ char *t = NULL; + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + int r; + const char *v, *t2; + + if (statfs(p, &sfs) < 0) + return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p); + + if (sfs.f_type != 0x4d44) { + log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); + return -ENODEV; + } + + if (stat(p, &st) < 0) + return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p); + + if (major(st.st_dev) == 0) { + log_error("Block device node of %p is invalid.", p); + return -ENODEV; + } + + t2 = strjoina(p, "/.."); + r = stat(t2, &st2); + if (r < 0) + return log_error_errno(errno, "Failed to determine block device node of parent of \"%s\": %m", p); + + if (st.st_dev == st2.st_dev) { + log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); + return -ENODEV; + } + + r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); + if (r < 0) + return log_oom(); + + errno = 0; + b = blkid_new_probe_from_filename(t); + if (!b) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "Failed to open file system \"%s\": %m", p); + } + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) { + log_error("File system \"%s\" is ambiguous.", p); + return -ENODEV; + } else if (r == 1) { + log_error("File system \"%s\" does not contain a label.", p); + return -ENODEV; + } else if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe file system \"%s\": %m", p); + } + + errno = 0; + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p); + } + + if (!streq(v, "vfat")) { + log_error("File system \"%s\" is not FAT.", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p); + } + + if (!streq(v, "gpt")) { + log_error("File system \"%s\" is not on a GPT partition table.", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p); + } + + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { + log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p); + } + + r = sd_id128_from_string(v, uuid); + if (r < 0) { + log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); + return -EIO; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition number \"%s\": m", p); + } + *part = strtoul(v, NULL, 10); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p); + } + *pstart = strtoul(v, NULL, 10); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) { + r = errno ? -errno : -EIO; + return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p); + } + *psize = strtoul(v, NULL, 10); + + return 0; +} + +/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ +static int get_file_version(int fd, char **v) { + struct stat st; + char *buf; + const char *s, *e; + char *x = NULL; + int r = 0; + + assert(fd >= 0); + assert(v); + + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_size < 27) + return 0; + + buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) + return -errno; + + s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17); + if (!s) + goto finish; + s += 17; + + e = memmem(s, st.st_size - (s - buf), " ####", 5); + if (!e || e - s < 3) { + log_error("Malformed version string."); + r = -EINVAL; + goto finish; + } + + x = strndup(s, e - s); + if (!x) { + r = log_oom(); + goto finish; + } + r = 1; + +finish: + munmap(buf, st.st_size); + *v = x; + return r; +} + +static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { + char *p; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0, c = 0; + + p = strjoina(esp_path, "/", path); + d = opendir(p); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to read \"%s\": %m", p); + } + + FOREACH_DIRENT(de, d, break) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *v = NULL; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + if (prefix && !startswith_no_case(de->d_name, prefix)) + continue; + + fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); + + r = get_file_version(fd, &v); + if (r < 0) + return r; + if (r > 0) + printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); + else + printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); + c++; + } + + return c; +} + +static int status_binaries(const char *esp_path, sd_id128_t partition) { + int r; + + printf("Boot Loader Binaries:\n"); + + printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); + + r = enumerate_binaries(esp_path, "EFI/systemd", NULL); + if (r == 0) + log_error("systemd-boot not installed in ESP."); + else if (r < 0) + return r; + + r = enumerate_binaries(esp_path, "EFI/BOOT", "boot"); + if (r == 0) + log_error("No default/fallback boot loader installed in ESP."); + else if (r < 0) + return r; + + printf("\n"); + + return 0; +} + +static int print_efi_option(uint16_t id, bool in_order) { + _cleanup_free_ char *title = NULL; + _cleanup_free_ char *path = NULL; + sd_id128_t partition; + bool active; + int r = 0; + + r = efi_get_boot_option(id, &title, &partition, &path, &active); + if (r < 0) + return r; + + /* print only configured entries with partition information */ + if (!path || sd_id128_is_null(partition)) + return 0; + + efi_tilt_backslashes(path); + + printf(" Title: %s\n", strna(title)); + printf(" ID: 0x%04X\n", id); + printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); + printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); + printf("\n"); + + return 0; +} + +static int status_variables(void) { + int n_options, n_order; + _cleanup_free_ uint16_t *options = NULL, *order = NULL; + int i; + + if (!is_efi_boot()) { + log_notice("Not booted with EFI, not showing EFI variables."); + return 0; + } + + n_options = efi_get_boot_options(&options); + if (n_options == -ENOENT) + return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs" + " needs to be available at /sys/firmware/efi/efivars/."); + else if (n_options < 0) + return log_error_errno(n_options, "Failed to read EFI boot entries: %m"); + + n_order = efi_get_boot_order(&order); + if (n_order == -ENOENT) + n_order = 0; + else if (n_order < 0) + return log_error_errno(n_order, "Failed to read EFI boot order."); + + /* print entries in BootOrder first */ + printf("Boot Loader Entries in EFI Variables:\n"); + for (i = 0; i < n_order; i++) + print_efi_option(order[i], true); + + /* print remaining entries */ + for (i = 0; i < n_options; i++) { + int j; + + for (j = 0; j < n_order; j++) + if (options[i] == order[j]) + goto next; + + print_efi_option(options[i], false); + next: + continue; + } + + return 0; +} + +static int compare_product(const char *a, const char *b) { + size_t x, y; + + assert(a); + assert(b); + + x = strcspn(a, " "); + y = strcspn(b, " "); + if (x != y) + return x < y ? -1 : x > y ? 1 : 0; + + return strncmp(a, b, x); +} + +static int compare_version(const char *a, const char *b) { + assert(a); + assert(b); + + a += strcspn(a, " "); + a += strspn(a, " "); + b += strcspn(b, " "); + b += strspn(b, " "); + + return strverscmp(a, b); +} + +static int version_check(int fd, const char *from, const char *to) { + _cleanup_free_ char *a = NULL, *b = NULL; + _cleanup_close_ int fd2 = -1; + int r; + + assert(fd >= 0); + assert(from); + assert(to); + + r = get_file_version(fd, &a); + if (r < 0) + return r; + if (r == 0) { + log_error("Source file \"%s\" does not carry version information!", from); + return -EINVAL; + } + + fd2 = open(to, O_RDONLY|O_CLOEXEC); + if (fd2 < 0) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to); + } + + r = get_file_version(fd2, &b); + if (r < 0) + return r; + if (r == 0 || compare_product(a, b) != 0) { + log_notice("Skipping \"%s\", since it's owned by another boot loader.", to); + return -EEXIST; + } + + if (compare_version(a, b) < 0) { + log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to); + return -ESTALE; + } + + return 0; +} + +static int copy_file(const char *from, const char *to, bool force) { + _cleanup_fclose_ FILE *f = NULL, *g = NULL; + char *p; + int r; + struct timespec t[2]; + struct stat st; + + assert(from); + assert(to); + + f = fopen(from, "re"); + if (!f) + return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from); + + if (!force) { + /* If this is an update, then let's compare versions first */ + r = version_check(fileno(f), from, to); + if (r < 0) + return r; + } + + p = strjoina(to, "~"); + g = fopen(p, "wxe"); + if (!g) { + /* Directory doesn't exist yet? Then let's skip this... */ + if (!force && errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to); + } + + rewind(f); + do { + size_t k; + uint8_t buf[32*1024]; + + k = fread(buf, 1, sizeof(buf), f); + if (ferror(f)) { + r = log_error_errno(EIO, "Failed to read \"%s\": %m", from); + goto error; + } + + if (k == 0) + break; + + fwrite(buf, 1, k, g); + if (ferror(g)) { + r = log_error_errno(EIO, "Failed to write \"%s\": %m", to); + goto error; + } + } while (!feof(f)); + + r = fflush_and_check(g); + if (r < 0) { + log_error_errno(r, "Failed to write \"%s\": %m", to); + goto error; + } + + r = fstat(fileno(f), &st); + if (r < 0) { + r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from); + goto error; + } + + t[0] = st.st_atim; + t[1] = st.st_mtim; + + r = futimens(fileno(g), t); + if (r < 0) { + r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p); + goto error; + } + + if (rename(p, to) < 0) { + r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to); + goto error; + } + + log_info("Copied \"%s\" to \"%s\".", from, to); + return 0; + +error: + (void) unlink(p); + return r; +} + +static char* strupper(char *s) { + char *p; + + for (p = s; *p; p++) + *p = toupper(*p); + + return s; +} + +static int mkdir_one(const char *prefix, const char *suffix) { + char *p; + + p = strjoina(prefix, "/", suffix); + if (mkdir(p, 0700) < 0) { + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create \"%s\": %m", p); + } else + log_info("Created \"%s\".", p); + + return 0; +} + +static const char *efi_subdirs[] = { + "EFI", + "EFI/systemd", + "EFI/BOOT", + "loader", + "loader/entries" +}; + +static int create_dirs(const char *esp_path) { + int r; + unsigned i; + + for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) { + r = mkdir_one(esp_path, efi_subdirs[i]); + if (r < 0) + return r; + } + + return 0; +} + +static int copy_one_file(const char *esp_path, const char *name, bool force) { + char *p, *q; + int r; + + p = strjoina(BOOTLIBDIR "/", name); + q = strjoina(esp_path, "/EFI/systemd/", name); + r = copy_file(p, q, force); + + if (startswith(name, "systemd-boot")) { + int k; + char *v; + + /* Create the EFI default boot loader name (specified for removable devices) */ + v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot")); + strupper(strrchr(v, '/') + 1); + + k = copy_file(p, v, force); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static int install_binaries(const char *esp_path, bool force) { + struct dirent *de; + _cleanup_closedir_ DIR *d = NULL; + int r = 0; + + if (force) { + /* Don't create any of these directories when we are + * just updating. When we update we'll drop-in our + * files (unless there are newer ones already), but we + * won't create the directories for them in the first + * place. */ + r = create_dirs(esp_path); + if (r < 0) + return r; + } + + d = opendir(BOOTLIBDIR); + if (!d) + return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m"); + + FOREACH_DIRENT(de, d, break) { + int k; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + k = copy_one_file(esp_path, de->d_name, force); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) { + _cleanup_free_ char *opath = NULL; + sd_id128_t ouuid; + int r; + + r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL); + if (r < 0) + return false; + if (!sd_id128_equal(uuid, ouuid)) + return false; + if (!streq_ptr(path, opath)) + return false; + + return true; +} + +static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) { + _cleanup_free_ uint16_t *options = NULL; + int n, i; + + n = efi_get_boot_options(&options); + if (n < 0) + return n; + + /* find already existing systemd-boot entry */ + for (i = 0; i < n; i++) + if (same_entry(options[i], uuid, path)) { + *id = options[i]; + return 1; + } + + /* find free slot in the sorted BootXXXX variable list */ + for (i = 0; i < n; i++) + if (i != options[i]) { + *id = i; + return 1; + } + + /* use the next one */ + if (i == 0xffff) + return -ENOSPC; + *id = i; + return 0; +} + +static int insert_into_order(uint16_t slot, bool first) { + _cleanup_free_ uint16_t *order = NULL; + uint16_t *t; + int n, i; + + n = efi_get_boot_order(&order); + if (n <= 0) + /* no entry, add us */ + return efi_set_boot_order(&slot, 1); + + /* are we the first and only one? */ + if (n == 1 && order[0] == slot) + return 0; + + /* are we already in the boot order? */ + for (i = 0; i < n; i++) { + if (order[i] != slot) + continue; + + /* we do not require to be the first one, all is fine */ + if (!first) + return 0; + + /* move us to the first slot */ + memmove(order + 1, order, i * sizeof(uint16_t)); + order[0] = slot; + return efi_set_boot_order(order, n); + } + + /* extend array */ + t = realloc(order, (n + 1) * sizeof(uint16_t)); + if (!t) + return -ENOMEM; + order = t; + + /* add us to the top or end of the list */ + if (first) { + memmove(order + 1, order, n * sizeof(uint16_t)); + order[0] = slot; + } else + order[n] = slot; + + return efi_set_boot_order(order, n + 1); +} + +static int remove_from_order(uint16_t slot) { + _cleanup_free_ uint16_t *order = NULL; + int n, i; + + n = efi_get_boot_order(&order); + if (n <= 0) + return n; + + for (i = 0; i < n; i++) { + if (order[i] != slot) + continue; + + if (i + 1 < n) + memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t)); + return efi_set_boot_order(order, n - 1); + } + + return 0; +} + +static int install_variables(const char *esp_path, + uint32_t part, uint64_t pstart, uint64_t psize, + sd_id128_t uuid, const char *path, + bool first) { + char *p; + uint16_t slot; + int r; + + if (!is_efi_boot()) { + log_warning("Not booted with EFI, skipping EFI variable setup."); + return 0; + } + + p = strjoina(esp_path, path); + if (access(p, F_OK) < 0) { + if (errno == ENOENT) + return 0; + else + return log_error_errno(errno, "Cannot access \"%s\": %m", p); + } + + r = find_slot(uuid, path, &slot); + if (r < 0) + return log_error_errno(r, + r == -ENOENT ? + "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" : + "Failed to determine current boot order: %m"); + + if (first || r == false) { + r = efi_add_boot_option(slot, "Systemd Boot Manager", + part, pstart, psize, + uuid, path); + if (r < 0) + return log_error_errno(r, "Failed to create EFI Boot variable entry: %m"); + + log_info("Created EFI boot entry \"Systemd Boot Manager\"."); + } + + return insert_into_order(slot, first); +} + +static int remove_boot_efi(const char *esp_path) { + char *p; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r, c = 0; + + p = strjoina(esp_path, "/EFI/BOOT"); + d = opendir(p); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open directory \"%s\": %m", p); + } + + FOREACH_DIRENT(de, d, break) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *v = NULL; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + if (!startswith_no_case(de->d_name, "boot")) + continue; + + fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name); + + r = get_file_version(fd, &v); + if (r < 0) + return r; + if (r > 0 && startswith(v, "systemd-boot ")) { + r = unlinkat(dirfd(d), de->d_name, 0); + if (r < 0) + return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name); + + log_info("Removed \"%s/%s\".", p, de->d_name); + } + + c++; + } + + return c; +} + +static int rmdir_one(const char *prefix, const char *suffix) { + char *p; + + p = strjoina(prefix, "/", suffix); + if (rmdir(p) < 0) { + if (!IN_SET(errno, ENOENT, ENOTEMPTY)) + return log_error_errno(errno, "Failed to remove \"%s\": %m", p); + } else + log_info("Removed \"%s\".", p); + + return 0; +} + +static int remove_binaries(const char *esp_path) { + char *p; + int r, q; + unsigned i; + + p = strjoina(esp_path, "/EFI/systemd"); + r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); + + q = remove_boot_efi(esp_path); + if (q < 0 && r == 0) + r = q; + + for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) { + q = rmdir_one(esp_path, efi_subdirs[i-1]); + if (q < 0 && r == 0) + r = q; + } + + return r; +} + +static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { + uint16_t slot; + int r; + + if (!is_efi_boot()) + return 0; + + r = find_slot(uuid, path, &slot); + if (r != 1) + return 0; + + r = efi_remove_boot_option(slot); + if (r < 0) + return r; + + if (in_order) + return remove_from_order(slot); + else + return 0; +} + +static int install_loader_config(const char *esp_path) { + char *p; + char line[64]; + char *machine = NULL; + _cleanup_fclose_ FILE *f = NULL, *g = NULL; + + f = fopen("/etc/machine-id", "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + if (fgets(line, sizeof(line), f) != NULL) { + char *s; + + s = strchr(line, '\n'); + if (s) + s[0] = '\0'; + if (strlen(line) == 32) + machine = line; + } + + if (!machine) + return -ESRCH; + + p = strjoina(esp_path, "/loader/loader.conf"); + g = fopen(p, "wxe"); + if (g) { + fprintf(g, "#timeout 3\n"); + fprintf(g, "default %s-*\n", machine); + if (ferror(g)) + return log_error_errno(EIO, "Failed to write \"%s\": %m", p); + } + + return 0; +} + +static int help(void) { + printf("%s [COMMAND] [OPTIONS...]\n" + "\n" + "Install, update or remove the systemd-boot EFI boot manager.\n\n" + " -h --help Show this help\n" + " --version Print version\n" + " --path=PATH Path to the EFI System Partition (ESP)\n" + " --no-variables Don't touch EFI variables\n" + "\n" + "Commands:\n" + " status Show status of installed systemd-boot and EFI variables\n" + " install Install systemd-boot to the ESP and EFI variables\n" + " update Update systemd-boot in the ESP and EFI variables\n" + " remove Remove systemd-boot from the ESP and EFI variables\n", + program_invocation_short_name); + + return 0; +} + +static const char *arg_path = "/boot"; +static bool arg_touch_variables = true; + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_PATH = 0x100, + ARG_VERSION, + ARG_NO_VARIABLES, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "path", required_argument, NULL, ARG_PATH }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_PATH: + arg_path = optarg; + break; + + case ARG_NO_VARIABLES: + arg_touch_variables = false; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unknown option"); + } + + return 1; +} + +static void read_loader_efi_var(const char *name, char **var) { + int r; + + r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read EFI variable %s: %m", name); +} + +static int bootctl_main(int argc, char*argv[]) { + enum action { + ACTION_STATUS, + ACTION_INSTALL, + ACTION_UPDATE, + ACTION_REMOVE + } arg_action = ACTION_STATUS; + static const struct { + const char* verb; + enum action action; + } verbs[] = { + { "status", ACTION_STATUS }, + { "install", ACTION_INSTALL }, + { "update", ACTION_UPDATE }, + { "remove", ACTION_REMOVE }, + }; + + sd_id128_t uuid = {}; + uint32_t part = 0; + uint64_t pstart = 0, psize = 0; + int r, q; + + if (argv[optind]) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(verbs); i++) { + if (!streq(argv[optind], verbs[i].verb)) + continue; + arg_action = verbs[i].action; + break; + } + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation \"%s\"", argv[optind]); + return -EINVAL; + } + } + + if (geteuid() != 0) + return log_error_errno(EPERM, "Need to be root."); + + r = verify_esp(arg_path, &part, &pstart, &psize, &uuid); + if (r == -ENODEV && !arg_path) + log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot."); + if (r < 0) + return r; + + switch (arg_action) { + case ACTION_STATUS: { + _cleanup_free_ char *fw_type = NULL; + _cleanup_free_ char *fw_info = NULL; + _cleanup_free_ char *loader = NULL; + _cleanup_free_ char *loader_path = NULL; + sd_id128_t loader_part_uuid = {}; + + if (is_efi_boot()) { + read_loader_efi_var("LoaderFirmwareType", &fw_type); + read_loader_efi_var("LoaderFirmwareInfo", &fw_info); + read_loader_efi_var("LoaderInfo", &loader); + read_loader_efi_var("LoaderImageIdentifier", &loader_path); + if (loader_path) + efi_tilt_backslashes(loader_path); + r = efi_loader_get_device_part_uuid(&loader_part_uuid); + if (r < 0 && r == -ENOENT) + log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); + + printf("System:\n"); + printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); + + r = is_efi_secure_boot(); + if (r < 0) + log_warning_errno(r, "Failed to query secure boot status: %m"); + else + printf(" Secure Boot: %s\n", r ? "enabled" : "disabled"); + + r = is_efi_secure_boot_setup_mode(); + if (r < 0) + log_warning_errno(r, "Failed to query secure boot mode: %m"); + else + printf(" Setup Mode: %s\n", r ? "setup" : "user"); + printf("\n"); + + printf("Loader:\n"); + printf(" Product: %s\n", strna(loader)); + if (!sd_id128_is_null(loader_part_uuid)) + printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + SD_ID128_FORMAT_VAL(loader_part_uuid)); + else + printf(" Partition: n/a\n"); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); + printf("\n"); + } else + printf("System:\n Not booted with EFI\n"); + + r = status_binaries(arg_path, uuid); + if (r < 0) + return r; + + if (arg_touch_variables) + r = status_variables(); + break; + } + + case ACTION_INSTALL: + case ACTION_UPDATE: + umask(0002); + + r = install_binaries(arg_path, arg_action == ACTION_INSTALL); + if (r < 0) + return r; + + if (arg_action == ACTION_INSTALL) { + r = install_loader_config(arg_path); + if (r < 0) + return r; + } + + if (arg_touch_variables) + r = install_variables(arg_path, + part, pstart, psize, uuid, + "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", + arg_action == ACTION_INSTALL); + break; + + case ACTION_REMOVE: + r = remove_binaries(arg_path); + + if (arg_touch_variables) { + q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); + if (q < 0 && r == 0) + r = q; + } + break; + } + + return r; +} + +int main(int argc, char *argv[]) { + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = bootctl_main(argc, argv); + + finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-boot/bootctl/bootctl.completion.bash b/src/grp-boot/bootctl/bootctl.completion.bash new file mode 100644 index 0000000000..c86ec7edc9 --- /dev/null +++ b/src/grp-boot/bootctl/bootctl.completion.bash @@ -0,0 +1,60 @@ +# bootctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_bootctl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version' + ) + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [STANDALONE]='status' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _bootctl bootctl diff --git a/src/grp-boot/bootctl/bootctl.completion.zsh b/src/grp-boot/bootctl/bootctl.completion.zsh new file mode 100644 index 0000000000..0e1b0a5562 --- /dev/null +++ b/src/grp-boot/bootctl/bootctl.completion.zsh @@ -0,0 +1,30 @@ +#compdef bootctl + +(( $+functions[_bootctl_command] )) || _bootctl_command() +{ + local -a _bootctl_cmds + _bootctl_cmds=( + "status:Show status of installed systemd-boot and EFI variables" + "install:Install systemd-boot to the ESP and EFI variables" + "update:Update systemd-boot in the ESP and EFI variables" + "remove:Remove systemd-boot from the ESP and EFI variables" + ) + if (( CURRENT == 1 )); then + _describe -t commands 'bootctl command' _bootctl_cmds || compadd "$@" + else + local curcontext="$curcontext" + cmd="${${_bootctl_cmds[(r)$words[1]:*]%%:*}}" + if (( $+functions[_bootctl_$cmd] )); then + _bootctl_$cmd + else + _message "no more options" + fi + fi +} + +_arguments \ + {-h,--help}'[Prints a short help text and exits.]' \ + '--version[Prints a short version string and exits.]' \ + '--path=[Path to the EFI System Partition (ESP)]:path:_directories' \ + '--no-variables[Do not touch EFI variables]' \ + '*::bootctl command:_bootctl_command' diff --git a/src/grp-boot/bootctl/bootctl.xml b/src/grp-boot/bootctl/bootctl.xml new file mode 100644 index 0000000000..6e835c037f --- /dev/null +++ b/src/grp-boot/bootctl/bootctl.xml @@ -0,0 +1,128 @@ + + + + + + + + bootctl + systemd + + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + bootctl + 1 + + + + bootctl + Control the firmware and boot manager settings + + + + + bootctl OPTIONSstatus + + + bootctl OPTIONSupdate + + + bootctl OPTIONSinstall + + + bootctl OPTIONSremove + + + + + Description + + bootctl checks, updates, + installs or removes the boot loader from the current + system. + + bootctl status checks and prints the + currently installed versions of the boot loader binaries and + all current EFI boot variables. + + bootctl update updates all installed + versions of systemd-boot, if the current version is newer than the + version installed in the EFI system partition. This also includes + the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A + systemd-boot entry in the EFI boot variables is created if there + is no current entry. The created entry will be added to the end of + the boot order list. + + bootctl install installs systemd-boot into + the EFI system partition. A copy of systemd-boot will be stored as + the EFI default/fallback loader at /EFI/BOOT/BOOT*.EFI. A systemd-boot + entry in the EFI boot variables is created and added to the top + of the boot order list. + + bootctl remove removes all installed + versions of systemd-boot from the EFI system partition, and removes + systemd-boot from the EFI boot variables. + + If no command is passed, status is + implied. + + + + Options + The following options are understood: + + + + + + + Path to the EFI system partition. The default is /boot. + + + + + Do not touch the EFI boot variables. + + + + + + Exit status + On success, 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + Boot loader specification + Systemd boot loader interface + + + diff --git a/src/grp-boot/kernel-install/50-depmod.install b/src/grp-boot/kernel-install/50-depmod.install new file mode 100644 index 0000000000..68c24bed7a --- /dev/null +++ b/src/grp-boot/kernel-install/50-depmod.install @@ -0,0 +1,8 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +[[ $1 == "add" ]] || exit 0 +[[ $2 ]] || exit 1 + +exec depmod -a "$2" diff --git a/src/grp-boot/kernel-install/90-loaderentry.install b/src/grp-boot/kernel-install/90-loaderentry.install new file mode 100644 index 0000000000..af9f0f9ccd --- /dev/null +++ b/src/grp-boot/kernel-install/90-loaderentry.install @@ -0,0 +1,89 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +if [[ -f /etc/machine-id ]]; then + read MACHINE_ID < /etc/machine-id +fi + +if ! [[ $MACHINE_ID ]]; then + exit 1 +fi + +BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION" +BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR} +LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" + +if [[ $COMMAND == remove ]]; then + exec rm -f "$LOADER_ENTRY" +fi + +if ! [[ $COMMAND == add ]]; then + exit 1 +fi + +if ! [[ $KERNEL_IMAGE ]]; then + exit 1 +fi + +if [[ -f /etc/os-release ]]; then + . /etc/os-release +elif [[ -f /usr/lib/os-release ]]; then + . /usr/lib/os-release +fi + +if ! [[ $PRETTY_NAME ]]; then + PRETTY_NAME="GNU/Linux $KERNEL_VERSION" +fi + +declare -a BOOT_OPTIONS + +if [[ -f /etc/kernel/cmdline ]]; then + read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline +fi + +if ! [[ ${BOOT_OPTIONS[*]} ]]; then + read -r -d '' -a line < /proc/cmdline + for i in "${line[@]}"; do + [[ "${i#initrd=*}" != "$i" ]] && continue + BOOT_OPTIONS+=("$i") + done +fi + +if ! [[ ${BOOT_OPTIONS[*]} ]]; then + echo "Could not determine the kernel command line parameters." >&2 + echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2 + exit 1 +fi + +cp "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" && + chown root:root "$BOOT_DIR_ABS/linux" && + chmod 0644 "$BOOT_DIR_ABS/linux" || { + echo "Could not copy '$KERNEL_IMAGE to '$BOOT_DIR_ABS/linux'." >&2 + exit 1 +} + +mkdir -p "${LOADER_ENTRY%/*}" || { + echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2 + exit 1 +} + +{ + echo "title $PRETTY_NAME" + echo "version $KERNEL_VERSION" + echo "machine-id $MACHINE_ID" + echo "options ${BOOT_OPTIONS[*]}" + echo "linux $BOOT_DIR/linux" + [[ -f $BOOT_DIR_ABS/initrd ]] && \ + echo "initrd $BOOT_DIR/initrd" + : +} > "$LOADER_ENTRY" || { + echo "Could not create loader entry '$LOADER_ENTRY'." >&2 + exit 1 +} +exit 0 diff --git a/src/grp-boot/kernel-install/Makefile b/src/grp-boot/kernel-install/Makefile new file mode 100644 index 0000000000..6a937f516c --- /dev/null +++ b/src/grp-boot/kernel-install/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +dist_bin_SCRIPTS = \ + src/kernel-install/kernel-install + +dist_kernelinstall_SCRIPTS = \ + src/kernel-install/50-depmod.install \ + src/kernel-install/90-loaderentry.install + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-boot/kernel-install/kernel-install b/src/grp-boot/kernel-install/kernel-install new file mode 100644 index 0000000000..1159dc384d --- /dev/null +++ b/src/grp-boot/kernel-install/kernel-install @@ -0,0 +1,144 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +# +# This file is part of systemd. +# +# Copyright 2013 Harald Hoyer +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +usage() +{ + echo "Usage:" + echo " $0 add KERNEL-VERSION KERNEL-IMAGE" + echo " $0 remove KERNEL-VERSION" +} + +dropindirs_sort() +{ + local suffix=$1; shift + local -a files + local f d i + + readarray -t files < <( + for d in "$@"; do + for i in "$d/"*"$suffix"; do + if [[ -e "$i" ]]; then + echo "${i##*/}" + fi + done + done | sort -Vu + ) + + for f in "${files[@]}"; do + for d in "$@"; do + if [[ -e "$d/$f" ]]; then + echo "$d/$f" + continue 2 + fi + done + done +} + +export LC_COLLATE=C + +for i in "$@"; do + if [ "$i" == "--help" -o "$i" == "-h" ]; then + usage + exit 0 + fi +done + +if [[ "${0##*/}" == 'installkernel' ]]; then + COMMAND='add' +else + COMMAND="$1" + shift +fi + +KERNEL_VERSION="$1" +KERNEL_IMAGE="$2" + +if [[ -f /etc/machine-id ]]; then + read MACHINE_ID < /etc/machine-id +fi + +if ! [[ $MACHINE_ID ]]; then + echo "Could not determine your machine ID from /etc/machine-id." >&2 + echo "Please run 'systemd-machine-id-setup' as root. See man:machine-id(5)" >&2 + exit 1 +fi + +if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then + echo "Not enough arguments" >&2 + exit 1 +fi + +if [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then + BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" +elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]] \ + || mountpoint -q /boot/efi; then + BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" +else + BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" +fi + +ret=0 + +readarray -t PLUGINS < <( + dropindirs_sort ".install" \ + "/etc/kernel/install.d" \ + "/usr/lib/kernel/install.d" +) + +case $COMMAND in + add) + if [[ ! "$KERNEL_IMAGE" ]]; then + echo "Command 'add' requires an argument" >&2 + exit 1 + fi + + mkdir -p "$BOOT_DIR_ABS" || { + echo "Could not create boot directory '$BOOT_DIR_ABS'." >&2 + exit 1 + } + + for f in "${PLUGINS[@]}"; do + if [[ -x $f ]]; then + "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE" + ((ret+=$?)) + fi + done + ;; + + remove) + for f in "${PLUGINS[@]}"; do + if [[ -x $f ]]; then + "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS" + ((ret+=$?)) + fi + done + + rm -rf "$BOOT_DIR_ABS" + ((ret+=$?)) + ;; + + *) + echo "Unknown command '$COMMAND'" >&2 + exit 1 + ;; +esac + +exit $ret diff --git a/src/grp-boot/kernel-install/kernel-install.completion.bash b/src/grp-boot/kernel-install/kernel-install.completion.bash new file mode 100644 index 0000000000..7cd2494cf7 --- /dev/null +++ b/src/grp-boot/kernel-install/kernel-install.completion.bash @@ -0,0 +1,50 @@ +# kernel-install(8) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2013 Kay Sievers +# Copyright 2013 Harald Hoyer +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +_kernel_install() { + local comps + local MACHINE_ID + local cur=${COMP_WORDS[COMP_CWORD]} + + case $COMP_CWORD in + 1) + comps="add remove" + ;; + 2) + comps=$(cd /lib/modules; echo [0-9]*) + if [[ ${COMP_WORDS[1]} == "remove" ]] && [[ -f /etc/machine-id ]]; then + read MACHINE_ID < /etc/machine-id + if [[ $MACHINE_ID ]] && ( [[ -d /boot/$MACHINE_ID ]] || [[ -L /boot/$MACHINE_ID ]] ); then + comps=$(cd "/boot/$MACHINE_ID"; echo [0-9]*) + fi + fi + ;; + 3) + [[ "$cur" ]] || cur=/boot/vmlinuz-${COMP_WORDS[2]} + comps=$(compgen -f -- "$cur") + compopt -o filenames + ;; + esac + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _kernel_install kernel-install diff --git a/src/grp-boot/kernel-install/kernel-install.completion.zsh b/src/grp-boot/kernel-install/kernel-install.completion.zsh new file mode 100644 index 0000000000..4fdd3a4ae7 --- /dev/null +++ b/src/grp-boot/kernel-install/kernel-install.completion.zsh @@ -0,0 +1,26 @@ +#compdef kernel-install + +_images(){ + if [[ "$words[2]" == "remove" ]]; then + _message 'No more options' + else + _path_files -W /boot/ -P /boot/ -g "vmlinuz-*" + fi +} + +_kernels(){ + read _MACHINE_ID < /etc/machine-id + _kernel=( /lib/modules/[0-9]* ) + if [[ "$cmd" == "remove" && -n "$_MACHINE_ID" ]]; then + _kernel=( "/boot/$_MACHINE_ID"/[0-9]* ) + fi + _kernel=( ${_kernel##*/} ) + _describe "installed kernels" _kernel +} + +_arguments \ + '1::add or remove:(add remove)' \ + '2::kernel versions:_kernels' \ + '3::kernel images:_images' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-boot/kernel-install/kernel-install.xml b/src/grp-boot/kernel-install/kernel-install.xml new file mode 100644 index 0000000000..eb519188a6 --- /dev/null +++ b/src/grp-boot/kernel-install/kernel-install.xml @@ -0,0 +1,192 @@ + + + + + + + + + kernel-install + systemd + + + + Developer + Harald + Hoyer + harald@redhat.com + + + + + + kernel-install + 8 + + + + kernel-install + Add and remove kernel and initramfs images to and from /boot + + + + + kernel-install + COMMAND + KERNEL-VERSION + KERNEL-IMAGE + + + + + Description + + kernel-install is used to install and remove kernel and + initramfs images to and from /boot. + + + kernel-install will execute the files + located in the directory /usr/lib/kernel/install.d/ + and the local administration directory /etc/kernel/install.d/. + All files are collectively sorted and executed in lexical order, regardless of the directory in + which they live. However, files with identical filenames replace each other. + Files in /etc/kernel/install.d/ take precedence over files with the same name + in /usr/lib/kernel/install.d/. This can be used to override a system-supplied + executables with a local file if needed; a symbolic link in /etc/kernel/install.d/ + with the same name as an executable in /usr/lib/kernel/install.d/, + pointing to /dev/null, disables the executable entirely. Executables must have the + extension .install; other extensions are ignored. + + + + + Commands + The following commands are understood: + + + add KERNEL-VERSION KERNEL-IMAGE + + kernel-install creates the directory + /boot/MACHINE-ID/KERNEL-VERSION/ + and calls every executable + /usr/lib/kernel/install.d/*.install and + /etc/kernel/install.d/*.install with + the arguments + add KERNEL-VERSION /boot/MACHINE-ID/KERNEL-VERSION/ + + + The kernel-install plugin 50-depmod.install runs depmod for the KERNEL-VERSION. + + The kernel-install plugin + 90-loaderentry.install copies + KERNEL-IMAGE to + /boot/MACHINE-ID/KERNEL-VERSION/linux. + It also creates a boot loader entry according to the boot + loader specification in + /boot/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + The title of the entry is the + PRETTY_NAME parameter specified + in /etc/os-release or + /usr/lib/os-release (if the former is + missing), or "GNU/Linux + KERNEL-VERSION", if unset. If + the file initrd is found next to the + linux file, the initrd will be added to + the configuration. + + + + remove KERNEL-VERSION + + Calls every executable /usr/lib/kernel/install.d/*.install + and /etc/kernel/install.d/*.install with the arguments + remove KERNEL-VERSION /boot/MACHINE-ID/KERNEL-VERSION/ + + + kernel-install removes the entire directory + /boot/MACHINE-ID/KERNEL-VERSION/ afterwards. + + The kernel-install plugin 90-loaderentry.install removes the file + /boot/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + + + + + + + + + Exit status + If every executable returns with 0, 0 is returned, a non-zero failure code otherwise. + + + + Files + + + + /usr/lib/kernel/install.d/*.install + /etc/kernel/install.d/*.install + + + Drop-in files which are executed by kernel-install. + + + + + /etc/kernel/cmdline + /proc/cmdline + + + The content of the file /etc/kernel/cmdline specifies the kernel command line to use. + If that file does not exist, /proc/cmdline is used. + + + + + /etc/machine-id + + + The content of the file specifies the machine identification MACHINE-ID. + + + + + /etc/os-release + /usr/lib/os-release + + + The content of the file specifies the operating system title PRETTY_NAME. + + + + + + + See Also + + machine-id5, + os-release5, + Boot loader specification + + + + diff --git a/src/grp-boot/systemd-boot/.gitignore b/src/grp-boot/systemd-boot/.gitignore new file mode 100644 index 0000000000..e193acbe12 --- /dev/null +++ b/src/grp-boot/systemd-boot/.gitignore @@ -0,0 +1,2 @@ +/systemd_boot.so +/stub.so diff --git a/src/grp-boot/systemd-boot/Makefile b/src/grp-boot/systemd-boot/Makefile new file mode 100644 index 0000000000..3b4d4ddda1 --- /dev/null +++ b/src/grp-boot/systemd-boot/Makefile @@ -0,0 +1,193 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_EFI),) +ifneq ($(HAVE_GNUEFI),) +efi_cppflags = \ + $(EFI_CPPFLAGS) \ + -I$(top_builddir) -include config.h \ + -I$(EFI_INC_DIR)/efi \ + -I$(EFI_INC_DIR)/efi/$(EFI_ARCH) \ + -DEFI_MACHINE_TYPE_NAME=\"$(EFI_MACHINE_TYPE_NAME)\" + +efi_cflags = \ + $(EFI_CFLAGS) \ + -Wall \ + -Wextra \ + -std=gnu90 \ + -nostdinc \ + -ggdb -O0 \ + -fpic \ + -fshort-wchar \ + -nostdinc \ + -ffreestanding \ + -fno-strict-aliasing \ + -fno-stack-protector \ + -Wsign-compare \ + -Wno-missing-field-initializers + +ifneq ($(ARCH_X86_64),) +efi_cflags += \ + -mno-red-zone \ + -mno-sse \ + -mno-mmx \ + -DEFI_FUNCTION_WRAPPER \ + -DGNU_EFI_USE_MS_ABI +endif # ARCH_X86_64 + +ifneq ($(ARCH_IA32),) +efi_cflags += \ + -mno-sse \ + -mno-mmx +endif # ARCH_IA32 + +efi_ldflags = \ + $(EFI_LDFLAGS) \ + -T $(EFI_LDS_DIR)/elf_$(EFI_ARCH)_efi.lds \ + -shared \ + -Bsymbolic \ + -nostdlib \ + -znocombreloc \ + -L $(EFI_LIB_DIR) \ + $(EFI_LDS_DIR)/crt0-efi-$(EFI_ARCH).o + +# Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary' instead, +# and add required symbols manually. +ifneq ($(ARCH_AARCH64),) +efi_ldflags += --defsym=EFI_SUBSYSTEM=0xa +EFI_FORMAT = -O binary +else +EFI_FORMAT = --target=efi-app-$(EFI_ARCH) +endif # ARCH_AARCH64 +endif # HAVE_GNUEFI +endif # ENABLE_EFI + +# ------------------------------------------------------------------------------ +systemd_boot_headers = \ + src/boot/efi/util.h \ + src/boot/efi/console.h \ + src/boot/efi/graphics.h \ + src/boot/efi/pefile.h \ + src/boot/efi/measure.h \ + src/boot/efi/disk.h + +systemd_boot_sources = \ + src/boot/efi/util.c \ + src/boot/efi/console.c \ + src/boot/efi/graphics.c \ + src/boot/efi/pefile.c \ + src/boot/efi/disk.c \ + src/boot/efi/measure.c \ + src/boot/efi/boot.c + +EXTRA_DIST += $(systemd_boot_sources) $(systemd_boot_headers) + +systemd_boot_objects = $(addprefix $(top_builddir)/,$(systemd_boot_sources:.c=.o)) +systemd_boot_solib = $(top_builddir)/src/boot/efi/systemd_boot.so +systemd_boot = systemd-boot$(EFI_MACHINE_TYPE_NAME).efi + +ifneq ($(ENABLE_EFI),) +ifneq ($(HAVE_GNUEFI),) +bootlib_DATA = $(systemd_boot) + +$(outdir)/%.o: $(top_srcdir)/src/boot/efi/%.c $(addprefix $(top_srcdir)/,$(systemd_boot_headers)) + @$(MKDIR_P) $(top_builddir)/src/boot/efi/ + $(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@ + +$(systemd_boot_solib): $(systemd_boot_objects) + $(AM_V_CCLD)$(LD) $(efi_ldflags) $(systemd_boot_objects) \ + -o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \ + nm -D -u $@ | grep ' U ' && exit 1 || : + +$(systemd_boot): $(systemd_boot_solib) + $(AM_V_GEN)$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \ + -j .dynsym -j .rel -j .rela -j .reloc $(EFI_FORMAT) $< $@ +endif # HAVE_GNUEFI +endif # ENABLE_EFI + +CLEANFILES += $(systemd_boot_objects) $(systemd_boot_solib) $(systemd_boot) + +# ------------------------------------------------------------------------------ +stub_headers = \ + src/boot/efi/util.h \ + src/boot/efi/pefile.h \ + src/boot/efi/disk.h \ + src/boot/efi/graphics.h \ + src/boot/efi/splash.h \ + src/boot/efi/measure.h \ + src/boot/efi/linux.h + +stub_sources = \ + src/boot/efi/util.c \ + src/boot/efi/pefile.c \ + src/boot/efi/disk.c \ + src/boot/efi/graphics.c \ + src/boot/efi/splash.c \ + src/boot/efi/linux.c \ + src/boot/efi/measure.c \ + src/boot/efi/stub.c + +EXTRA_DIST += \ + $(stub_sources) \ + $(stub_headers) \ + test/splash.bmp + +stub_objects = $(addprefix $(top_builddir)/,$(stub_sources:.c=.o)) +stub_solib = $(top_builddir)/src/boot/efi/stub.so +stub = linux$(EFI_MACHINE_TYPE_NAME).efi.stub + +ifneq ($(ENABLE_EFI),) +ifneq ($(HAVE_GNUEFI),) +bootlib_DATA += $(stub) + +$(outdir)/%.o: $(top_srcdir)/src/boot/efi/%.c $(addprefix $(top_srcdir)/,$(stub_headers)) + @$(MKDIR_P) $(top_builddir)/src/boot/efi/ + $(AM_V_CC)$(EFI_CC) $(efi_cppflags) $(efi_cflags) -c $< -o $@ + +$(stub_solib): $(stub_objects) + $(AM_V_CCLD)$(LD) $(efi_ldflags) $(stub_objects) \ + -o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \ + nm -D -u $@ | grep ' U ' && exit 1 || : + +$(stub): $(stub_solib) + $(AM_V_GEN)$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \ + -j .dynsym -j .rel -j .rela -j .reloc $(EFI_FORMAT) $< $@ +endif # HAVE_GNUEFI +endif # ENABLE_EFI + +CLEANFILES += $(stub_objects) $(stub_solib) $(stub) + +# ------------------------------------------------------------------------------ +CLEANFILES += test-efi-disk.img + +test-efi-disk.img: $(systemd_boot) $(stub) test/test-efi-create-disk.sh + $(AM_V_GEN)test/test-efi-create-disk.sh + +test-efi: test-efi-disk.img + $(QEMU) -machine accel=kvm -m 1024 -bios $(QEMU_BIOS) -snapshot test-efi-disk.img + +EXTRA_DIST += test/test-efi-create-disk.sh + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-boot/systemd-boot/boot.c b/src/grp-boot/systemd-boot/boot.c new file mode 100644 index 0000000000..9dfaed6d46 --- /dev/null +++ b/src/grp-boot/systemd-boot/boot.c @@ -0,0 +1,1857 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2015 Kay Sievers + * Copyright (C) 2012-2015 Harald Hoyer + */ + +#include +#include + +#include "console.h" +#include "disk.h" +#include "graphics.h" +#include "linux.h" +#include "measure.h" +#include "pefile.h" +#include "util.h" + +#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL +#endif + +/* magic string to find in the binary image */ +static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####"; + +static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + +enum loader_type { + LOADER_UNDEFINED, + LOADER_EFI, + LOADER_LINUX +}; + +typedef struct { + CHAR16 *file; + CHAR16 *title_show; + CHAR16 *title; + CHAR16 *version; + CHAR16 *machine_id; + EFI_HANDLE *device; + enum loader_type type; + CHAR16 *loader; + CHAR16 *options; + CHAR16 key; + EFI_STATUS (*call)(VOID); + BOOLEAN no_autoselect; + BOOLEAN non_unique; +} ConfigEntry; + +typedef struct { + ConfigEntry **entries; + UINTN entry_count; + INTN idx_default; + INTN idx_default_efivar; + UINTN timeout_sec; + UINTN timeout_sec_config; + INTN timeout_sec_efivar; + CHAR16 *entry_default_pattern; + CHAR16 *entry_oneshot; + CHAR16 *options_edit; + BOOLEAN no_editor; +} Config; + +static VOID cursor_left(UINTN *cursor, UINTN *first) { + if ((*cursor) > 0) + (*cursor)--; + else if ((*first) > 0) + (*first)--; +} + +static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) { + if ((*cursor)+1 < x_max) + (*cursor)++; + else if ((*first) + (*cursor) < len) + (*first)++; +} + +static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN y_pos) { + CHAR16 *line; + UINTN size; + UINTN len; + UINTN first; + CHAR16 *print; + UINTN cursor; + UINTN clear; + BOOLEAN exit; + BOOLEAN enter; + + if (!line_in) + line_in = L""; + size = StrLen(line_in) + 1024; + line = AllocatePool(size * sizeof(CHAR16)); + StrCpy(line, line_in); + len = StrLen(line); + print = AllocatePool((x_max+1) * sizeof(CHAR16)); + + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE); + + first = 0; + cursor = 0; + clear = 0; + enter = FALSE; + exit = FALSE; + while (!exit) { + EFI_STATUS err; + UINT64 key; + UINTN i; + + i = len - first; + if (i >= x_max-1) + i = x_max-1; + CopyMem(print, line + first, i * sizeof(CHAR16)); + while (clear > 0 && i < x_max-1) { + clear--; + print[i++] = ' '; + } + print[i] = '\0'; + + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + + err = console_key_read(&key, TRUE); + if (EFI_ERROR(err)) + continue; + + switch (key) { + case KEYPRESS(0, SCAN_ESC, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')): + exit = TRUE; + break; + + case KEYPRESS(0, SCAN_HOME, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')): + /* beginning-of-line */ + cursor = 0; + first = 0; + continue; + + case KEYPRESS(0, SCAN_END, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')): + /* end-of-line */ + cursor = len - first; + if (cursor+1 >= x_max) { + cursor = x_max-1; + first = len - (x_max-1); + } + continue; + + case KEYPRESS(0, SCAN_DOWN, 0): + case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'): + case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0): + /* forward-word */ + while (line[first + cursor] && line[first + cursor] == ' ') + cursor_right(&cursor, &first, x_max, len); + while (line[first + cursor] && line[first + cursor] != ' ') + cursor_right(&cursor, &first, x_max, len); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + continue; + + case KEYPRESS(0, SCAN_UP, 0): + case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'): + case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0): + /* backward-word */ + if ((first + cursor) > 0 && line[first + cursor-1] == ' ') { + cursor_left(&cursor, &first); + while ((first + cursor) > 0 && line[first + cursor] == ' ') + cursor_left(&cursor, &first); + } + while ((first + cursor) > 0 && line[first + cursor-1] != ' ') + cursor_left(&cursor, &first); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + continue; + + case KEYPRESS(0, SCAN_RIGHT, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')): + /* forward-char */ + if (first + cursor == len) + continue; + cursor_right(&cursor, &first, x_max, len); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + continue; + + case KEYPRESS(0, SCAN_LEFT, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')): + /* backward-char */ + cursor_left(&cursor, &first); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + continue; + + case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'): + /* kill-word */ + clear = 0; + for (i = first + cursor; i < len && line[i] == ' '; i++) + clear++; + for (; i < len && line[i] != ' '; i++) + clear++; + + for (i = first + cursor; i + clear < len; i++) + line[i] = line[i + clear]; + len -= clear; + line[len] = '\0'; + continue; + + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')): + case KEYPRESS(EFI_ALT_PRESSED, 0, CHAR_BACKSPACE): + /* backward-kill-word */ + clear = 0; + if ((first + cursor) > 0 && line[first + cursor-1] == ' ') { + cursor_left(&cursor, &first); + clear++; + while ((first + cursor) > 0 && line[first + cursor] == ' ') { + cursor_left(&cursor, &first); + clear++; + } + } + while ((first + cursor) > 0 && line[first + cursor-1] != ' ') { + cursor_left(&cursor, &first); + clear++; + } + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); + + for (i = first + cursor; i + clear < len; i++) + line[i] = line[i + clear]; + len -= clear; + line[len] = '\0'; + continue; + + case KEYPRESS(0, SCAN_DELETE, 0): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')): + if (len == 0) + continue; + if (first + cursor == len) + continue; + for (i = first + cursor; i < len; i++) + line[i] = line[i+1]; + clear = 1; + len--; + continue; + + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')): + /* kill-line */ + line[first + cursor] = '\0'; + clear = len - (first + cursor); + len = first + cursor; + continue; + + case KEYPRESS(0, 0, CHAR_LINEFEED): + case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): + if (StrCmp(line, line_in) != 0) { + *line_out = line; + line = NULL; + } + enter = TRUE; + exit = TRUE; + break; + + case KEYPRESS(0, 0, CHAR_BACKSPACE): + if (len == 0) + continue; + if (first == 0 && cursor == 0) + continue; + for (i = first + cursor-1; i < len; i++) + line[i] = line[i+1]; + clear = 1; + len--; + if (cursor > 0) + cursor--; + if (cursor > 0 || first == 0) + continue; + /* show full line if it fits */ + if (len < x_max) { + cursor = first; + first = 0; + continue; + } + /* jump left to see what we delete */ + if (first > 10) { + first -= 10; + cursor = 10; + } else { + cursor = first; + first = 0; + } + continue; + + case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'): + case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff): + if (len+1 == size) + continue; + for (i = len; i > first + cursor; i--) + line[i] = line[i-1]; + line[first + cursor] = KEYCHAR(key); + len++; + line[len] = '\0'; + if (cursor+1 < x_max) + cursor++; + else if (first + cursor < len) + first++; + continue; + } + } + + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); + FreePool(print); + FreePool(line); + return enter; +} + +static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { + UINTN i; + + if (key == 0) + return -1; + + /* select entry by number key */ + if (key >= '1' && key <= '9') { + i = key - '0'; + if (i > config->entry_count) + i = config->entry_count; + return i-1; + } + + /* find matching key in config entries */ + for (i = start; i < config->entry_count; i++) + if (config->entries[i]->key == key) + return i; + + for (i = 0; i < start; i++) + if (config->entries[i]->key == key) + return i; + + return -1; +} + +static VOID print_status(Config *config, CHAR16 *loaded_image_path) { + UINT64 key; + UINTN i; + CHAR16 *s; + CHAR8 *b; + UINTN x; + UINTN y; + UINTN size; + + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + Print(L"systemd-boot version: " VERSION "\n"); + Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n"); + Print(L"loaded image: %s\n", loaded_image_path); + Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); + Print(L"firmware vendor: %s\n", ST->FirmwareVendor); + Print(L"firmware version: %d.%02d\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + + if (uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x, &y) == EFI_SUCCESS) + Print(L"console size: %d x %d\n", x, y); + + if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { + Print(L"SecureBoot: %s\n", yes_no(*b > 0)); + FreePool(b); + } + + if (efivar_get_raw(&global_guid, L"SetupMode", &b, &size) == EFI_SUCCESS) { + Print(L"SetupMode: %s\n", *b > 0 ? L"setup" : L"user"); + FreePool(b); + } + + if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) { + Print(L"OsIndicationsSupported: %d\n", (UINT64)*b); + FreePool(b); + } + Print(L"\n"); + + Print(L"timeout: %d\n", config->timeout_sec); + if (config->timeout_sec_efivar >= 0) + Print(L"timeout (EFI var): %d\n", config->timeout_sec_efivar); + Print(L"timeout (config): %d\n", config->timeout_sec_config); + if (config->entry_default_pattern) + Print(L"default pattern: '%s'\n", config->entry_default_pattern); + Print(L"editor: %s\n", yes_no(!config->no_editor)); + Print(L"\n"); + + Print(L"config entry count: %d\n", config->entry_count); + Print(L"entry selected idx: %d\n", config->idx_default); + if (config->idx_default_efivar >= 0) + Print(L"entry EFI var idx: %d\n", config->idx_default_efivar); + Print(L"\n"); + + if (efivar_get_int(L"LoaderConfigTimeout", &i) == EFI_SUCCESS) + Print(L"LoaderConfigTimeout: %d\n", i); + if (config->entry_oneshot) + Print(L"LoaderEntryOneShot: %s\n", config->entry_oneshot); + if (efivar_get(L"LoaderDevicePartUUID", &s) == EFI_SUCCESS) { + Print(L"LoaderDevicePartUUID: %s\n", s); + FreePool(s); + } + if (efivar_get(L"LoaderEntryDefault", &s) == EFI_SUCCESS) { + Print(L"LoaderEntryDefault: %s\n", s); + FreePool(s); + } + + Print(L"\n--- press key ---\n\n"); + console_key_read(&key, TRUE); + + for (i = 0; i < config->entry_count; i++) { + ConfigEntry *entry; + + if (key == KEYPRESS(0, SCAN_ESC, 0) || key == KEYPRESS(0, 0, 'q')) + break; + + entry = config->entries[i]; + Print(L"config entry: %d/%d\n", i+1, config->entry_count); + if (entry->file) + Print(L"file '%s'\n", entry->file); + Print(L"title show '%s'\n", entry->title_show); + if (entry->title) + Print(L"title '%s'\n", entry->title); + if (entry->version) + Print(L"version '%s'\n", entry->version); + if (entry->machine_id) + Print(L"machine-id '%s'\n", entry->machine_id); + if (entry->device) { + EFI_DEVICE_PATH *device_path; + CHAR16 *str; + + device_path = DevicePathFromHandle(entry->device); + if (device_path) { + str = DevicePathToStr(device_path); + Print(L"device handle '%s'\n", str); + FreePool(str); + } + } + if (entry->loader) + Print(L"loader '%s'\n", entry->loader); + if (entry->options) + Print(L"options '%s'\n", entry->options); + Print(L"auto-select %s\n", yes_no(!entry->no_autoselect)); + if (entry->call) + Print(L"internal call yes\n"); + + Print(L"\n--- press key ---\n\n"); + console_key_read(&key, TRUE); + } + + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); +} + +static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *loaded_image_path) { + EFI_STATUS err; + UINTN visible_max; + UINTN idx_highlight; + UINTN idx_highlight_prev; + UINTN idx_first; + UINTN idx_last; + BOOLEAN refresh; + BOOLEAN highlight; + UINTN i; + UINTN line_width; + CHAR16 **lines; + UINTN x_start; + UINTN y_start; + UINTN x_max; + UINTN y_max; + CHAR16 *status; + CHAR16 *clearline; + INTN timeout_remain; + INT16 idx; + BOOLEAN exit = FALSE; + BOOLEAN run = TRUE; + BOOLEAN wait = FALSE; + + graphics_mode(FALSE); + uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + + /* draw a single character to make ClearScreen work on some firmware */ + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L" "); + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + err = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &x_max, &y_max); + if (EFI_ERROR(err)) { + x_max = 80; + y_max = 25; + } + + /* we check 10 times per second for a keystroke */ + if (config->timeout_sec > 0) + timeout_remain = config->timeout_sec * 10; + else + timeout_remain = -1; + + idx_highlight = config->idx_default; + idx_highlight_prev = 0; + + visible_max = y_max - 2; + + if ((UINTN)config->idx_default >= visible_max) + idx_first = config->idx_default-1; + else + idx_first = 0; + + idx_last = idx_first + visible_max-1; + + refresh = TRUE; + highlight = FALSE; + + /* length of the longest entry */ + line_width = 5; + for (i = 0; i < config->entry_count; i++) { + UINTN entry_len; + + entry_len = StrLen(config->entries[i]->title_show); + if (line_width < entry_len) + line_width = entry_len; + } + if (line_width > x_max-6) + line_width = x_max-6; + + /* offsets to center the entries on the screen */ + x_start = (x_max - (line_width)) / 2; + if (config->entry_count < visible_max) + y_start = ((visible_max - config->entry_count) / 2) + 1; + else + y_start = 0; + + /* menu entries title lines */ + lines = AllocatePool(sizeof(CHAR16 *) * config->entry_count); + for (i = 0; i < config->entry_count; i++) { + UINTN j, k; + + lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16))); + for (j = 0; j < x_start; j++) + lines[i][j] = ' '; + + for (k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++) + lines[i][j] = config->entries[i]->title_show[k]; + + for (; j < x_max; j++) + lines[i][j] = ' '; + lines[i][x_max] = '\0'; + } + + status = NULL; + clearline = AllocatePool((x_max+1) * sizeof(CHAR16)); + for (i = 0; i < x_max; i++) + clearline[i] = ' '; + clearline[i] = 0; + + while (!exit) { + UINT64 key; + + if (refresh) { + for (i = 0; i < config->entry_count; i++) { + if (i < idx_first || i > idx_last) + continue; + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first); + if (i == idx_highlight) + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY); + else + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]); + if ((INTN)i == config->idx_default_efivar) { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); + } + } + refresh = FALSE; + } else if (highlight) { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]); + if ((INTN)idx_highlight_prev == config->idx_default_efivar) { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); + } + + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]); + if ((INTN)idx_highlight == config->idx_default_efivar) { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, L"=>"); + } + highlight = FALSE; + } + + if (timeout_remain > 0) { + FreePool(status); + status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10); + } + + /* print status at last line of screen */ + if (status) { + UINTN len; + UINTN x; + + /* center line */ + len = StrLen(status); + if (len < x_max) + x = (x_max - len) / 2; + else + x = 0; + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x)); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len); + } + + err = console_key_read(&key, wait); + if (EFI_ERROR(err)) { + /* timeout reached */ + if (timeout_remain == 0) { + exit = TRUE; + break; + } + + /* sleep and update status */ + if (timeout_remain > 0) { + uefi_call_wrapper(BS->Stall, 1, 100 * 1000); + timeout_remain--; + continue; + } + + /* timeout disabled, wait for next key */ + wait = TRUE; + continue; + } + + timeout_remain = -1; + + /* clear status after keystroke */ + if (status) { + FreePool(status); + status = NULL; + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); + } + + idx_highlight_prev = idx_highlight; + + switch (key) { + case KEYPRESS(0, SCAN_UP, 0): + case KEYPRESS(0, 0, 'k'): + if (idx_highlight > 0) + idx_highlight--; + break; + + case KEYPRESS(0, SCAN_DOWN, 0): + case KEYPRESS(0, 0, 'j'): + if (idx_highlight < config->entry_count-1) + idx_highlight++; + break; + + case KEYPRESS(0, SCAN_HOME, 0): + case KEYPRESS(EFI_ALT_PRESSED, 0, '<'): + if (idx_highlight > 0) { + refresh = TRUE; + idx_highlight = 0; + } + break; + + case KEYPRESS(0, SCAN_END, 0): + case KEYPRESS(EFI_ALT_PRESSED, 0, '>'): + if (idx_highlight < config->entry_count-1) { + refresh = TRUE; + idx_highlight = config->entry_count-1; + } + break; + + case KEYPRESS(0, SCAN_PAGE_UP, 0): + if (idx_highlight > visible_max) + idx_highlight -= visible_max; + else + idx_highlight = 0; + break; + + case KEYPRESS(0, SCAN_PAGE_DOWN, 0): + idx_highlight += visible_max; + if (idx_highlight > config->entry_count-1) + idx_highlight = config->entry_count-1; + break; + + case KEYPRESS(0, 0, CHAR_LINEFEED): + case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): + exit = TRUE; + break; + + case KEYPRESS(0, SCAN_F1, 0): + case KEYPRESS(0, 0, 'h'): + case KEYPRESS(0, 0, '?'): + status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp"); + break; + + case KEYPRESS(0, 0, 'Q'): + exit = TRUE; + run = FALSE; + break; + + case KEYPRESS(0, 0, 'd'): + if (config->idx_default_efivar != (INTN)idx_highlight) { + /* store the selected entry in a persistent EFI variable */ + efivar_set(L"LoaderEntryDefault", config->entries[idx_highlight]->file, TRUE); + config->idx_default_efivar = idx_highlight; + status = StrDuplicate(L"Default boot entry selected."); + } else { + /* clear the default entry EFI variable */ + efivar_set(L"LoaderEntryDefault", NULL, TRUE); + config->idx_default_efivar = -1; + status = StrDuplicate(L"Default boot entry cleared."); + } + refresh = TRUE; + break; + + case KEYPRESS(0, 0, '-'): + case KEYPRESS(0, 0, 'T'): + if (config->timeout_sec_efivar > 0) { + config->timeout_sec_efivar--; + efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE); + if (config->timeout_sec_efivar > 0) + status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar); + else + status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); + } else if (config->timeout_sec_efivar <= 0){ + config->timeout_sec_efivar = -1; + efivar_set(L"LoaderConfigTimeout", NULL, TRUE); + if (config->timeout_sec_config > 0) + status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.", + config->timeout_sec_config); + else + status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); + } + break; + + case KEYPRESS(0, 0, '+'): + case KEYPRESS(0, 0, 't'): + if (config->timeout_sec_efivar == -1 && config->timeout_sec_config == 0) + config->timeout_sec_efivar++; + config->timeout_sec_efivar++; + efivar_set_int(L"LoaderConfigTimeout", config->timeout_sec_efivar, TRUE); + if (config->timeout_sec_efivar > 0) + status = PoolPrint(L"Menu timeout set to %d sec.", + config->timeout_sec_efivar); + else + status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); + break; + + case KEYPRESS(0, 0, 'e'): + /* only the options of configured entries can be edited */ + if (config->no_editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED) + break; + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); + if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1)) + exit = TRUE; + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1); + break; + + case KEYPRESS(0, 0, 'v'): + status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d", + ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff, + ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + break; + + case KEYPRESS(0, 0, 'P'): + print_status(config, loaded_image_path); + refresh = TRUE; + break; + + case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'l'): + case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('l')): + refresh = TRUE; + break; + + default: + /* jump with a hotkey directly to a matching entry */ + idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key)); + if (idx < 0) + break; + idx_highlight = idx; + refresh = TRUE; + } + + if (idx_highlight > idx_last) { + idx_last = idx_highlight; + idx_first = 1 + idx_highlight - visible_max; + refresh = TRUE; + } else if (idx_highlight < idx_first) { + idx_first = idx_highlight; + idx_last = idx_highlight + visible_max-1; + refresh = TRUE; + } + + if (!refresh && idx_highlight != idx_highlight_prev) + highlight = TRUE; + } + + *chosen_entry = config->entries[idx_highlight]; + + for (i = 0; i < config->entry_count; i++) + FreePool(lines[i]); + FreePool(lines); + FreePool(clearline); + + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK); + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + return run; +} + +static VOID config_add_entry(Config *config, ConfigEntry *entry) { + if ((config->entry_count & 15) == 0) { + UINTN i; + + i = config->entry_count + 16; + if (config->entry_count == 0) + config->entries = AllocatePool(sizeof(VOID *) * i); + else + config->entries = ReallocatePool(config->entries, + sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i); + } + config->entries[config->entry_count++] = entry; +} + +static VOID config_entry_free(ConfigEntry *entry) { + FreePool(entry->title_show); + FreePool(entry->title); + FreePool(entry->machine_id); + FreePool(entry->loader); + FreePool(entry->options); +} + +static BOOLEAN is_digit(CHAR16 c) { + return (c >= '0') && (c <= '9'); +} + +static UINTN c_order(CHAR16 c) { + if (c == '\0') + return 0; + if (is_digit(c)) + return 0; + else if ((c >= 'a') && (c <= 'z')) + return c; + else + return c + 0x10000; +} + +static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) { + CHAR16 *os1 = s1; + CHAR16 *os2 = s2; + + while (*s1 || *s2) { + INTN first; + + while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { + INTN order; + + order = c_order(*s1) - c_order(*s2); + if (order) + return order; + s1++; + s2++; + } + + while (*s1 == '0') + s1++; + while (*s2 == '0') + s2++; + + first = 0; + while (is_digit(*s1) && is_digit(*s2)) { + if (first == 0) + first = *s1 - *s2; + s1++; + s2++; + } + + if (is_digit(*s1)) + return 1; + if (is_digit(*s2)) + return -1; + + if (first) + return first; + } + + return StrCmp(os1, os2); +} + +static CHAR8 *line_get_key_value(CHAR8 *content, CHAR8 *sep, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) { + CHAR8 *line; + UINTN linelen; + CHAR8 *value; + +skip: + line = content + *pos; + if (*line == '\0') + return NULL; + + linelen = 0; + while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen])) + linelen++; + + /* move pos to next line */ + *pos += linelen; + if (content[*pos]) + (*pos)++; + + /* empty line */ + if (linelen == 0) + goto skip; + + /* terminate line */ + line[linelen] = '\0'; + + /* remove leading whitespace */ + while (strchra((CHAR8 *)" \t", *line)) { + line++; + linelen--; + } + + /* remove trailing whitespace */ + while (linelen > 0 && strchra(sep, line[linelen-1])) + linelen--; + line[linelen] = '\0'; + + if (*line == '#') + goto skip; + + /* split key/value */ + value = line; + while (*value && !strchra(sep, *value)) + value++; + if (*value == '\0') + goto skip; + *value = '\0'; + value++; + while (*value && strchra(sep, *value)) + value++; + + /* unquote */ + if (value[0] == '\"' && line[linelen-1] == '\"') { + value++; + line[linelen-1] = '\0'; + } + + *key_ret = line; + *value_ret = value; + return line; +} + +static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) { + CHAR8 *line; + UINTN pos = 0; + CHAR8 *key, *value; + + line = content; + while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) { + if (strcmpa((CHAR8 *)"timeout", key) == 0) { + CHAR16 *s; + + s = stra_to_str(value); + config->timeout_sec_config = Atoi(s); + config->timeout_sec = config->timeout_sec_config; + FreePool(s); + continue; + } + + if (strcmpa((CHAR8 *)"default", key) == 0) { + FreePool(config->entry_default_pattern); + config->entry_default_pattern = stra_to_str(value); + StrLwr(config->entry_default_pattern); + continue; + } + + if (strcmpa((CHAR8 *)"editor", key) == 0) { + BOOLEAN on; + + if (EFI_ERROR(parse_boolean(value, &on))) + continue; + config->no_editor = !on; + } + } +} + +static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR16 *file, CHAR8 *content, CHAR16 *loaded_image_path) { + ConfigEntry *entry; + CHAR8 *line; + UINTN pos = 0; + CHAR8 *key, *value; + UINTN len; + CHAR16 *initrd = NULL; + + entry = AllocateZeroPool(sizeof(ConfigEntry)); + + line = content; + while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) { + if (strcmpa((CHAR8 *)"title", key) == 0) { + FreePool(entry->title); + entry->title = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"version", key) == 0) { + FreePool(entry->version); + entry->version = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"machine-id", key) == 0) { + FreePool(entry->machine_id); + entry->machine_id = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"linux", key) == 0) { + FreePool(entry->loader); + entry->type = LOADER_LINUX; + entry->loader = stra_to_path(value); + entry->key = 'l'; + continue; + } + + if (strcmpa((CHAR8 *)"efi", key) == 0) { + entry->type = LOADER_EFI; + FreePool(entry->loader); + entry->loader = stra_to_path(value); + + /* do not add an entry for ourselves */ + if (StriCmp(entry->loader, loaded_image_path) == 0) { + entry->type = LOADER_UNDEFINED; + break; + } + continue; + } + + if (strcmpa((CHAR8 *)"architecture", key) == 0) { + /* do not add an entry for an EFI image of architecture not matching with that of the image */ + if (strcmpa((CHAR8 *)EFI_MACHINE_TYPE_NAME, value) != 0) { + entry->type = LOADER_UNDEFINED; + break; + } + continue; + } + + if (strcmpa((CHAR8 *)"initrd", key) == 0) { + CHAR16 *new; + + new = stra_to_path(value); + if (initrd) { + CHAR16 *s; + + s = PoolPrint(L"%s initrd=%s", initrd, new); + FreePool(initrd); + initrd = s; + } else + initrd = PoolPrint(L"initrd=%s", new); + FreePool(new); + continue; + } + + if (strcmpa((CHAR8 *)"options", key) == 0) { + CHAR16 *new; + + new = stra_to_str(value); + if (entry->options) { + CHAR16 *s; + + s = PoolPrint(L"%s %s", entry->options, new); + FreePool(entry->options); + entry->options = s; + } else { + entry->options = new; + new = NULL; + } + FreePool(new); + continue; + } + } + + if (entry->type == LOADER_UNDEFINED) { + config_entry_free(entry); + FreePool(initrd); + FreePool(entry); + return; + } + + /* add initrd= to options */ + if (entry->type == LOADER_LINUX && initrd) { + if (entry->options) { + CHAR16 *s; + + s = PoolPrint(L"%s %s", initrd, entry->options); + FreePool(entry->options); + entry->options = s; + } else { + entry->options = initrd; + initrd = NULL; + } + } + FreePool(initrd); + + entry->device = device; + entry->file = StrDuplicate(file); + len = StrLen(entry->file); + /* remove ".conf" */ + if (len > 5) + entry->file[len - 5] = '\0'; + StrLwr(entry->file); + + config_add_entry(config, entry); +} + +static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) { + CHAR8 *content = NULL; + UINTN sec; + UINTN len; + EFI_STATUS err; + + len = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content); + if (len > 0) + config_defaults_load_from_file(config, content); + FreePool(content); + + err = efivar_get_int(L"LoaderConfigTimeout", &sec); + if (!EFI_ERROR(err)) { + config->timeout_sec_efivar = sec; + config->timeout_sec = sec; + } else + config->timeout_sec_efivar = -1; +} + +static VOID config_load_entries(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path) { + EFI_FILE_HANDLE entries_dir; + EFI_STATUS err; + + err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL); + if (!EFI_ERROR(err)) { + for (;;) { + CHAR16 buf[256]; + UINTN bufsize; + EFI_FILE_INFO *f; + CHAR8 *content = NULL; + UINTN len; + + bufsize = sizeof(buf); + err = uefi_call_wrapper(entries_dir->Read, 3, entries_dir, &bufsize, buf); + if (bufsize == 0 || EFI_ERROR(err)) + break; + + f = (EFI_FILE_INFO *) buf; + if (f->FileName[0] == '.') + continue; + if (f->Attribute & EFI_FILE_DIRECTORY) + continue; + + len = StrLen(f->FileName); + if (len < 6) + continue; + if (StriCmp(f->FileName + len - 5, L".conf") != 0) + continue; + if (StrnCmp(f->FileName, L"auto-", 5) == 0) + continue; + + len = file_read(entries_dir, f->FileName, 0, 0, &content); + if (len > 0) + config_entry_add_from_file(config, device, f->FileName, content, loaded_image_path); + FreePool(content); + } + uefi_call_wrapper(entries_dir->Close, 1, entries_dir); + } +} + +static VOID config_sort_entries(Config *config) { + UINTN i; + + for (i = 1; i < config->entry_count; i++) { + BOOLEAN more; + UINTN k; + + more = FALSE; + for (k = 0; k < config->entry_count - i; k++) { + ConfigEntry *entry; + + if (str_verscmp(config->entries[k]->file, config->entries[k+1]->file) <= 0) + continue; + entry = config->entries[k]; + config->entries[k] = config->entries[k+1]; + config->entries[k+1] = entry; + more = TRUE; + } + if (!more) + break; + } +} + +static VOID config_default_entry_select(Config *config) { + CHAR16 *var; + EFI_STATUS err; + UINTN i; + + /* + * The EFI variable to specify a boot entry for the next, and only the + * next reboot. The variable is always cleared directly after it is read. + */ + err = efivar_get(L"LoaderEntryOneShot", &var); + if (!EFI_ERROR(err)) { + BOOLEAN found = FALSE; + + for (i = 0; i < config->entry_count; i++) { + if (StrCmp(config->entries[i]->file, var) == 0) { + config->idx_default = i; + found = TRUE; + break; + } + } + + config->entry_oneshot = StrDuplicate(var); + efivar_set(L"LoaderEntryOneShot", NULL, TRUE); + FreePool(var); + if (found) + return; + } + + /* + * The EFI variable to select the default boot entry overrides the + * configured pattern. The variable can be set and cleared by pressing + * the 'd' key in the loader selection menu, the entry is marked with + * an '*'. + */ + err = efivar_get(L"LoaderEntryDefault", &var); + if (!EFI_ERROR(err)) { + BOOLEAN found = FALSE; + + for (i = 0; i < config->entry_count; i++) { + if (StrCmp(config->entries[i]->file, var) == 0) { + config->idx_default = i; + config->idx_default_efivar = i; + found = TRUE; + break; + } + } + FreePool(var); + if (found) + return; + } + config->idx_default_efivar = -1; + + if (config->entry_count == 0) + return; + + /* + * Match the pattern from the end of the list to the start, find last + * entry (largest number) matching the given pattern. + */ + if (config->entry_default_pattern) { + i = config->entry_count; + while (i--) { + if (config->entries[i]->no_autoselect) + continue; + if (MetaiMatch(config->entries[i]->file, config->entry_default_pattern)) { + config->idx_default = i; + return; + } + } + } + + /* select the last suitable entry */ + i = config->entry_count; + while (i--) { + if (config->entries[i]->no_autoselect) + continue; + config->idx_default = i; + return; + } + + /* no entry found */ + config->idx_default = -1; +} + +/* generate a unique title, avoiding non-distinguishable menu entries */ +static VOID config_title_generate(Config *config) { + UINTN i, k; + BOOLEAN unique; + + /* set title */ + for (i = 0; i < config->entry_count; i++) { + CHAR16 *title; + + FreePool(config->entries[i]->title_show); + title = config->entries[i]->title; + if (!title) + title = config->entries[i]->file; + config->entries[i]->title_show = StrDuplicate(title); + } + + unique = TRUE; + for (i = 0; i < config->entry_count; i++) { + for (k = 0; k < config->entry_count; k++) { + if (i == k) + continue; + if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) + continue; + + unique = FALSE; + config->entries[i]->non_unique = TRUE; + config->entries[k]->non_unique = TRUE; + } + } + if (unique) + return; + + /* add version to non-unique titles */ + for (i = 0; i < config->entry_count; i++) { + CHAR16 *s; + + if (!config->entries[i]->non_unique) + continue; + if (!config->entries[i]->version) + continue; + + s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version); + FreePool(config->entries[i]->title_show); + config->entries[i]->title_show = s; + config->entries[i]->non_unique = FALSE; + } + + unique = TRUE; + for (i = 0; i < config->entry_count; i++) { + for (k = 0; k < config->entry_count; k++) { + if (i == k) + continue; + if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) + continue; + + unique = FALSE; + config->entries[i]->non_unique = TRUE; + config->entries[k]->non_unique = TRUE; + } + } + if (unique) + return; + + /* add machine-id to non-unique titles */ + for (i = 0; i < config->entry_count; i++) { + CHAR16 *s; + CHAR16 *m; + + if (!config->entries[i]->non_unique) + continue; + if (!config->entries[i]->machine_id) + continue; + + m = StrDuplicate(config->entries[i]->machine_id); + m[8] = '\0'; + s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, m); + FreePool(config->entries[i]->title_show); + config->entries[i]->title_show = s; + config->entries[i]->non_unique = FALSE; + FreePool(m); + } + + unique = TRUE; + for (i = 0; i < config->entry_count; i++) { + for (k = 0; k < config->entry_count; k++) { + if (i == k) + continue; + if (StrCmp(config->entries[i]->title_show, config->entries[k]->title_show) != 0) + continue; + + unique = FALSE; + config->entries[i]->non_unique = TRUE; + config->entries[k]->non_unique = TRUE; + } + } + if (unique) + return; + + /* add file name to non-unique titles */ + for (i = 0; i < config->entry_count; i++) { + CHAR16 *s; + + if (!config->entries[i]->non_unique) + continue; + s = PoolPrint(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->file); + FreePool(config->entries[i]->title_show); + config->entries[i]->title_show = s; + config->entries[i]->non_unique = FALSE; + } +} + +static BOOLEAN config_entry_add_call(Config *config, CHAR16 *title, EFI_STATUS (*call)(VOID)) { + ConfigEntry *entry; + + entry = AllocateZeroPool(sizeof(ConfigEntry)); + entry->title = StrDuplicate(title); + entry->call = call; + entry->no_autoselect = TRUE; + config_add_entry(config, entry); + return TRUE; +} + +static ConfigEntry *config_entry_add_loader(Config *config, EFI_HANDLE *device, + enum loader_type type,CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) { + ConfigEntry *entry; + + entry = AllocateZeroPool(sizeof(ConfigEntry)); + entry->type = type; + entry->title = StrDuplicate(title); + entry->device = device; + entry->loader = StrDuplicate(loader); + entry->file = StrDuplicate(file); + StrLwr(entry->file); + entry->key = key; + config_add_entry(config, entry); + + return entry; +} + +static BOOLEAN config_entry_add_loader_auto(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, CHAR16 *loaded_image_path, + CHAR16 *file, CHAR16 key, CHAR16 *title, CHAR16 *loader) { + EFI_FILE_HANDLE handle; + ConfigEntry *entry; + EFI_STATUS err; + + /* do not add an entry for ourselves */ + if (loaded_image_path && StriCmp(loader, loaded_image_path) == 0) + return FALSE; + + /* check existence */ + err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, loader, EFI_FILE_MODE_READ, 0ULL); + if (EFI_ERROR(err)) + return FALSE; + uefi_call_wrapper(handle->Close, 1, handle); + + entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, file, key, title, loader); + if (!entry) + return FALSE; + + /* do not boot right away into auto-detected entries */ + entry->no_autoselect = TRUE; + + return TRUE; +} + +static VOID config_entry_add_osx(Config *config) { + EFI_STATUS err; + UINTN handle_count = 0; + EFI_HANDLE *handles = NULL; + + err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles); + if (!EFI_ERROR(err)) { + UINTN i; + + for (i = 0; i < handle_count; i++) { + EFI_FILE *root; + BOOLEAN found; + + root = LibOpenRoot(handles[i]); + if (!root) + continue; + found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"OS X", + L"\\System\\Library\\CoreServices\\boot.efi"); + uefi_call_wrapper(root->Close, 1, root); + if (found) + break; + } + + FreePool(handles); + } +} + +static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_image, EFI_FILE *root_dir) { + EFI_FILE_HANDLE linux_dir; + EFI_STATUS err; + ConfigEntry *entry; + + err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL); + if (!EFI_ERROR(err)) { + for (;;) { + CHAR16 buf[256]; + UINTN bufsize; + EFI_FILE_INFO *f; + CHAR8 *sections[] = { + (UINT8 *)".osrel", + (UINT8 *)".cmdline", + NULL + }; + UINTN offs[ELEMENTSOF(sections)-1] = {}; + UINTN szs[ELEMENTSOF(sections)-1] = {}; + UINTN addrs[ELEMENTSOF(sections)-1] = {}; + CHAR8 *content = NULL; + UINTN len; + CHAR8 *line; + UINTN pos = 0; + CHAR8 *key, *value; + CHAR16 *os_name = NULL; + CHAR16 *os_id = NULL; + CHAR16 *os_version = NULL; + CHAR16 *os_build = NULL; + + bufsize = sizeof(buf); + err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf); + if (bufsize == 0 || EFI_ERROR(err)) + break; + + f = (EFI_FILE_INFO *) buf; + if (f->FileName[0] == '.') + continue; + if (f->Attribute & EFI_FILE_DIRECTORY) + continue; + len = StrLen(f->FileName); + if (len < 5) + continue; + if (StriCmp(f->FileName + len - 4, L".efi") != 0) + continue; + + /* look for .osrel and .cmdline sections in the .efi binary */ + err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); + if (EFI_ERROR(err)) + continue; + + len = file_read(linux_dir, f->FileName, offs[0], szs[0], &content); + if (len <= 0) + continue; + + /* read properties from the embedded os-release file */ + line = content; + while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) { + if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) { + FreePool(os_name); + os_name = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"ID", key) == 0) { + FreePool(os_id); + os_id = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) { + FreePool(os_version); + os_version = stra_to_str(value); + continue; + } + + if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) { + FreePool(os_build); + os_build = stra_to_str(value); + continue; + } + } + + if (os_name && os_id && (os_version || os_build)) { + CHAR16 *conf; + CHAR16 *path; + CHAR16 *cmdline; + + conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build); + path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); + entry = config_entry_add_loader(config, loaded_image->DeviceHandle, LOADER_LINUX, conf, 'l', os_name, path); + + FreePool(content); + /* read the embedded cmdline file */ + len = file_read(linux_dir, f->FileName, offs[1], szs[1] - 1 , &content); + if (len > 0) { + cmdline = stra_to_str(content); + entry->options = cmdline; + cmdline = NULL; + } + FreePool(cmdline); + FreePool(conf); + FreePool(path); + } + + FreePool(os_name); + FreePool(os_id); + FreePool(os_version); + FreePool(os_build); + FreePool(content); + } + uefi_call_wrapper(linux_dir->Close, 1, linux_dir); + } +} + +static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) { + EFI_HANDLE image; + EFI_DEVICE_PATH *path; + CHAR16 *options; + EFI_STATUS err; + + path = FileDevicePath(entry->device, entry->loader); + if (!path) { + Print(L"Error getting device path."); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return EFI_INVALID_PARAMETER; + } + + err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image); + if (EFI_ERROR(err)) { + Print(L"Error loading %s: %r", entry->loader, err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + goto out; + } + + if (config->options_edit) + options = config->options_edit; + else if (entry->options) + options = entry->options; + else + options = NULL; + if (options) { + EFI_LOADED_IMAGE *loaded_image; + + err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, + parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(err)) { + Print(L"Error getting LoadedImageProtocol handle: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + goto out_unload; + } + loaded_image->LoadOptions = options; + loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially to catch manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif + } + + efivar_set_time_usec(L"LoaderTimeExecUSec", 0); + err = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); +out_unload: + uefi_call_wrapper(BS->UnloadImage, 1, image); +out: + FreePool(path); + return err; +} + +static EFI_STATUS reboot_into_firmware(VOID) { + CHAR8 *b; + UINTN size; + UINT64 osind; + EFI_STATUS err; + + osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + + err = efivar_get_raw(&global_guid, L"OsIndications", &b, &size); + if (!EFI_ERROR(err)) + osind |= (UINT64)*b; + FreePool(b); + + err = efivar_set_raw(&global_guid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE); + if (EFI_ERROR(err)) + return err; + + err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL); + Print(L"Error calling ResetSystem: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; +} + +static VOID config_free(Config *config) { + UINTN i; + + for (i = 0; i < config->entry_count; i++) + config_entry_free(config->entries[i]); + FreePool(config->entries); + FreePool(config->entry_default_pattern); + FreePool(config->options_edit); + FreePool(config->entry_oneshot); +} + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { + CHAR16 *s; + CHAR8 *b; + UINTN size; + EFI_LOADED_IMAGE *loaded_image; + EFI_FILE *root_dir; + CHAR16 *loaded_image_path; + EFI_STATUS err; + Config config; + UINT64 init_usec; + BOOLEAN menu = FALSE; + CHAR16 uuid[37]; + + InitializeLib(image, sys_table); + init_usec = time_usec(); + efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec); + efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE); + s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + efivar_set(L"LoaderFirmwareInfo", s, FALSE); + FreePool(s); + s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); + efivar_set(L"LoaderFirmwareType", s, FALSE); + FreePool(s); + + err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(err)) { + Print(L"Error getting a LoadedImageProtocol handle: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + + /* export the device path this image is started from */ + if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) + efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); + + root_dir = LibOpenRoot(loaded_image->DeviceHandle); + if (!root_dir) { + Print(L"Unable to open root directory: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return EFI_LOAD_ERROR; + } + + + /* the filesystem path to this image, to prevent adding ourselves to the menu */ + loaded_image_path = DevicePathToStr(loaded_image->FilePath); + efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE); + + ZeroMem(&config, sizeof(Config)); + config_load_defaults(&config, root_dir); + + /* scan /EFI/Linux/ directory */ + config_entry_add_linux(&config, loaded_image, root_dir); + + /* scan /loader/entries/\*.conf files */ + config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); + + /* sort entries after version number */ + config_sort_entries(&config); + + /* if we find some well-known loaders, add them to the end of the list */ + config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, + L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, + L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); + config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, + L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi"); + config_entry_add_osx(&config); + + if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) { + UINT64 osind = (UINT64)*b; + + if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) + config_entry_add_call(&config, L"Reboot Into Firmware Interface", reboot_into_firmware); + FreePool(b); + } + + if (config.entry_count == 0) { + Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed."); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + goto out; + } + + config_title_generate(&config); + + /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/ + config_default_entry_select(&config); + + /* if no configured entry to select from was found, enable the menu */ + if (config.idx_default == -1) { + config.idx_default = 0; + if (config.timeout_sec == 0) + config.timeout_sec = 10; + } + + /* select entry or show menu when key is pressed or timeout is set */ + if (config.timeout_sec == 0) { + UINT64 key; + + err = console_key_read(&key, FALSE); + if (!EFI_ERROR(err)) { + INT16 idx; + + /* find matching key in config entries */ + idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key)); + if (idx >= 0) + config.idx_default = idx; + else + menu = TRUE; + } + } else + menu = TRUE; + + for (;;) { + ConfigEntry *entry; + + entry = config.entries[config.idx_default]; + if (menu) { + efivar_set_time_usec(L"LoaderTimeMenuUSec", 0); + uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x10000, 0, NULL); + if (!menu_run(&config, &entry, loaded_image_path)) + break; + + /* run special entry like "reboot" */ + if (entry->call) { + entry->call(); + continue; + } + } + + /* export the selected boot entry to the system */ + efivar_set(L"LoaderEntrySelected", entry->file, FALSE); + + uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL); + err = image_start(image, &config, entry); + if (EFI_ERROR(err)) { + graphics_mode(FALSE); + Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + goto out; + } + + menu = TRUE; + config.timeout_sec = 0; + } + err = EFI_SUCCESS; +out: + FreePool(loaded_image_path); + config_free(&config); + uefi_call_wrapper(root_dir->Close, 1, root_dir); + uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL); + return err; +} diff --git a/src/grp-boot/systemd-boot/console.c b/src/grp-boot/systemd-boot/console.c new file mode 100644 index 0000000000..2b797c9a5f --- /dev/null +++ b/src/grp-boot/systemd-boot/console.c @@ -0,0 +1,135 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#include +#include + +#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 } } + +struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; + +typedef EFI_STATUS (EFIAPI *EFI_INPUT_RESET_EX)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + BOOLEAN ExtendedVerification +); + +typedef UINT8 EFI_KEY_TOGGLE_STATE; + +typedef struct { + UINT32 KeyShiftState; + EFI_KEY_TOGGLE_STATE KeyToggleState; +} EFI_KEY_STATE; + +typedef struct { + EFI_INPUT_KEY Key; + EFI_KEY_STATE KeyState; +} EFI_KEY_DATA; + +typedef EFI_STATUS (EFIAPI *EFI_INPUT_READ_KEY_EX)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + EFI_KEY_DATA *KeyData +); + +typedef EFI_STATUS (EFIAPI *EFI_SET_STATE)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + EFI_KEY_TOGGLE_STATE *KeyToggleState +); + +typedef EFI_STATUS (EFIAPI *EFI_KEY_NOTIFY_FUNCTION)( + EFI_KEY_DATA *KeyData +); + +typedef EFI_STATUS (EFIAPI *EFI_REGISTER_KEYSTROKE_NOTIFY)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + EFI_KEY_DATA KeyData, + EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction, + VOID **NotifyHandle +); + +typedef EFI_STATUS (EFIAPI *EFI_UNREGISTER_KEYSTROKE_NOTIFY)( + struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This, + VOID *NotificationHandle +); + +typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL { + EFI_INPUT_RESET_EX Reset; + EFI_INPUT_READ_KEY_EX ReadKeyStrokeEx; + EFI_EVENT WaitForKeyEx; + EFI_SET_STATE SetState; + EFI_REGISTER_KEYSTROKE_NOTIFY RegisterKeyNotify; + EFI_UNREGISTER_KEYSTROKE_NOTIFY UnregisterKeyNotify; +} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; + +EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { + EFI_GUID EfiSimpleTextInputExProtocolGuid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; + static BOOLEAN checked; + UINTN index; + EFI_INPUT_KEY k; + EFI_STATUS err; + + if (!checked) { + err = LibLocateProtocol(&EfiSimpleTextInputExProtocolGuid, (VOID **)&TextInputEx); + if (EFI_ERROR(err)) + TextInputEx = NULL; + + checked = TRUE; + } + + /* wait until key is pressed */ + if (wait) + uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index); + + if (TextInputEx) { + EFI_KEY_DATA keydata; + UINT64 keypress; + + err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata); + if (!EFI_ERROR(err)) { + UINT32 shift = 0; + + /* do not distinguish between left and right keys */ + if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) { + if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED)) + shift |= EFI_CONTROL_PRESSED; + if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)) + shift |= EFI_ALT_PRESSED; + }; + + /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */ + keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar); + if (keypress > 0) { + *key = keypress; + return 0; + } + } + } + + /* fallback for firmware which does not support SimpleTextInputExProtocol + * + * This is also called in case ReadKeyStrokeEx did not return a key, because + * some broken firmwares offer SimpleTextInputExProtocol, but never acually + * handle any key. */ + err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k); + if (EFI_ERROR(err)) + return err; + + *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar); + return 0; +} diff --git a/src/grp-boot/systemd-boot/console.h b/src/grp-boot/systemd-boot/console.h new file mode 100644 index 0000000000..3fe0ce5ec4 --- /dev/null +++ b/src/grp-boot/systemd-boot/console.h @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#ifndef __SDBOOT_CONSOLE_H +#define __SDBOOT_CONSOLE_H + +#define EFI_SHIFT_STATE_VALID 0x80000000 +#define EFI_RIGHT_CONTROL_PRESSED 0x00000004 +#define EFI_LEFT_CONTROL_PRESSED 0x00000008 +#define EFI_RIGHT_ALT_PRESSED 0x00000010 +#define EFI_LEFT_ALT_PRESSED 0x00000020 + +#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED) +#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED) +#define KEYPRESS(keys, scan, uni) ((((UINT64)keys) << 32) | ((scan) << 16) | (uni)) +#define KEYCHAR(k) ((k) & 0xffff) +#define CHAR_CTRL(c) ((c) - 'a' + 1) + +EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait); +#endif diff --git a/src/grp-boot/systemd-boot/disk.c b/src/grp-boot/systemd-boot/disk.c new file mode 100644 index 0000000000..3e3b5b224a --- /dev/null +++ b/src/grp-boot/systemd-boot/disk.c @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "util.h" + +EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]) { + EFI_DEVICE_PATH *device_path; + EFI_STATUS r = EFI_NOT_FOUND; + + /* export the device path this image is started from */ + device_path = DevicePathFromHandle(handle); + if (device_path) { + EFI_DEVICE_PATH *path, *paths; + + paths = UnpackDevicePath(device_path); + for (path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) { + HARDDRIVE_DEVICE_PATH *drive; + + if (DevicePathType(path) != MEDIA_DEVICE_PATH) + continue; + if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP) + continue; + drive = (HARDDRIVE_DEVICE_PATH *)path; + if (drive->SignatureType != SIGNATURE_TYPE_GUID) + continue; + + GuidToString(uuid, (EFI_GUID *)&drive->Signature); + r = EFI_SUCCESS; + break; + } + FreePool(paths); + } + + return r; +} diff --git a/src/grp-boot/systemd-boot/disk.h b/src/grp-boot/systemd-boot/disk.h new file mode 100644 index 0000000000..af91a9c674 --- /dev/null +++ b/src/grp-boot/systemd-boot/disk.h @@ -0,0 +1,19 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#ifndef __SDBOOT_DISK_H +#define __SDBOOT_DISK_H + +EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[37]); +#endif diff --git a/src/grp-boot/systemd-boot/graphics.c b/src/grp-boot/systemd-boot/graphics.c new file mode 100644 index 0000000000..4854baf874 --- /dev/null +++ b/src/grp-boot/systemd-boot/graphics.c @@ -0,0 +1,88 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + * Copyright (C) 2013 Intel Corporation + * Authored by Joonas Lahtinen + */ + +#include +#include + +#include "graphics.h" +#include "util.h" + +EFI_STATUS graphics_mode(BOOLEAN on) { + #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ + { 0xf42f7782, 0x12e, 0x4c12, { 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21 } }; + + struct _EFI_CONSOLE_CONTROL_PROTOCOL; + + typedef enum { + EfiConsoleControlScreenText, + EfiConsoleControlScreenGraphics, + EfiConsoleControlScreenMaxValue, + } EFI_CONSOLE_CONTROL_SCREEN_MODE; + + typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE)( + struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, + EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, + BOOLEAN *UgaExists, + BOOLEAN *StdInLocked + ); + + typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE)( + struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, + EFI_CONSOLE_CONTROL_SCREEN_MODE Mode + ); + + typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN)( + struct _EFI_CONSOLE_CONTROL_PROTOCOL *This, + CHAR16 *Password + ); + + typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL { + EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn; + } EFI_CONSOLE_CONTROL_PROTOCOL; + + EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; + EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; + EFI_CONSOLE_CONTROL_SCREEN_MODE new; + EFI_CONSOLE_CONTROL_SCREEN_MODE current; + BOOLEAN uga_exists; + BOOLEAN stdin_locked; + EFI_STATUS err; + + err = LibLocateProtocol(&ConsoleControlProtocolGuid, (VOID **)&ConsoleControl); + if (EFI_ERROR(err)) + /* console control protocol is nonstandard and might not exist. */ + return err == EFI_NOT_FOUND ? EFI_SUCCESS : err; + + /* check current mode */ + err = uefi_call_wrapper(ConsoleControl->GetMode, 4, ConsoleControl, ¤t, &uga_exists, &stdin_locked); + if (EFI_ERROR(err)) + return err; + + /* do not touch the mode */ + new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText; + if (new == current) + return EFI_SUCCESS; + + err = uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, new); + + /* some firmware enables the cursor when switching modes */ + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); + + return err; +} diff --git a/src/grp-boot/systemd-boot/graphics.h b/src/grp-boot/systemd-boot/graphics.h new file mode 100644 index 0000000000..cf48e647e7 --- /dev/null +++ b/src/grp-boot/systemd-boot/graphics.h @@ -0,0 +1,22 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + * Copyright (C) 2013 Intel Corporation + * Authored by Joonas Lahtinen + */ + +#ifndef __SDBOOT_GRAPHICS_H +#define __SDBOOT_GRAPHICS_H + +EFI_STATUS graphics_mode(BOOLEAN on); +#endif diff --git a/src/grp-boot/systemd-boot/linux.c b/src/grp-boot/systemd-boot/linux.c new file mode 100644 index 0000000000..0dc99a6c53 --- /dev/null +++ b/src/grp-boot/systemd-boot/linux.c @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "linux.h" +#include "util.h" + +#define SETUP_MAGIC 0x53726448 /* "HdrS" */ +struct SetupHeader { + UINT8 boot_sector[0x01f1]; + UINT8 setup_secs; + UINT16 root_flags; + UINT32 sys_size; + UINT16 ram_size; + UINT16 video_mode; + UINT16 root_dev; + UINT16 signature; + UINT16 jump; + UINT32 header; + UINT16 version; + UINT16 su_switch; + UINT16 setup_seg; + UINT16 start_sys; + UINT16 kernel_ver; + UINT8 loader_id; + UINT8 load_flags; + UINT16 movesize; + UINT32 code32_start; + UINT32 ramdisk_start; + UINT32 ramdisk_len; + UINT32 bootsect_kludge; + UINT16 heap_end; + UINT8 ext_loader_ver; + UINT8 ext_loader_type; + UINT32 cmd_line_ptr; + UINT32 ramdisk_max; + UINT32 kernel_alignment; + UINT8 relocatable_kernel; + UINT8 min_alignment; + UINT16 xloadflags; + UINT32 cmdline_size; + UINT32 hardware_subarch; + UINT64 hardware_subarch_data; + UINT32 payload_offset; + UINT32 payload_length; + UINT64 setup_data; + UINT64 pref_address; + UINT32 init_size; + UINT32 handover_offset; +} __attribute__((packed)); + +#ifdef __x86_64__ +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup); +static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { + handover_f handover; + + asm volatile ("cli"); + handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset); + handover(image, ST, setup); +} +#else +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0))); +static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { + handover_f handover; + + handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset); + handover(image, ST, setup); +} +#endif + +EFI_STATUS linux_exec(EFI_HANDLE *image, + CHAR8 *cmdline, UINTN cmdline_len, + UINTN linux_addr, + UINTN initrd_addr, UINTN initrd_size) { + struct SetupHeader *image_setup; + struct SetupHeader *boot_setup; + EFI_PHYSICAL_ADDRESS addr; + EFI_STATUS err; + + image_setup = (struct SetupHeader *)(linux_addr); + if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) + return EFI_LOAD_ERROR; + + if (image_setup->version < 0x20b || !image_setup->relocatable_kernel) + return EFI_LOAD_ERROR; + + addr = 0x3fffffff; + err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(0x4000), &addr); + if (EFI_ERROR(err)) + return err; + boot_setup = (struct SetupHeader *)(UINTN)addr; + ZeroMem(boot_setup, 0x4000); + CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader)); + boot_setup->loader_id = 0xff; + + boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512; + + if (cmdline) { + addr = 0xA0000; + err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, + EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr); + if (EFI_ERROR(err)) + return err; + CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len); + ((CHAR8 *)addr)[cmdline_len] = 0; + boot_setup->cmd_line_ptr = (UINT32)addr; + } + + boot_setup->ramdisk_start = (UINT32)initrd_addr; + boot_setup->ramdisk_len = (UINT32)initrd_size; + + linux_efi_handover(image, boot_setup); + return EFI_LOAD_ERROR; +} diff --git a/src/grp-boot/systemd-boot/linux.h b/src/grp-boot/systemd-boot/linux.h new file mode 100644 index 0000000000..d9e6ed7955 --- /dev/null +++ b/src/grp-boot/systemd-boot/linux.h @@ -0,0 +1,22 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#ifndef __SDBOOT_kernel_H +#define __SDBOOT_kernel_H + +EFI_STATUS linux_exec(EFI_HANDLE *image, + CHAR8 *cmdline, UINTN cmdline_size, + UINTN linux_addr, + UINTN initrd_addr, UINTN initrd_size); +#endif diff --git a/src/grp-boot/systemd-boot/measure.c b/src/grp-boot/systemd-boot/measure.c new file mode 100644 index 0000000000..05adf41778 --- /dev/null +++ b/src/grp-boot/systemd-boot/measure.c @@ -0,0 +1,312 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef SD_BOOT_LOG_TPM + +#include +#include + +#include "measure.h" + +#define EFI_TCG_PROTOCOL_GUID { 0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd} } + +typedef struct _TCG_VERSION { + UINT8 Major; + UINT8 Minor; + UINT8 RevMajor; + UINT8 RevMinor; +} TCG_VERSION; + +typedef struct _TCG_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + struct _TCG_VERSION StructureVersion; + struct _TCG_VERSION ProtocolSpecVersion; + UINT8 HashAlgorithmBitmap; + BOOLEAN TPMPresentFlag; + BOOLEAN TPMDeactivatedFlag; +} TCG_BOOT_SERVICE_CAPABILITY; + +typedef UINT32 TCG_ALGORITHM_ID; +#define TCG_ALG_SHA 0x00000004 // The SHA1 algorithm + +#define SHA1_DIGEST_SIZE 20 + +typedef struct _TCG_DIGEST { + UINT8 Digest[SHA1_DIGEST_SIZE]; +} TCG_DIGEST; + +#define EV_IPL 13 + +typedef struct _TCG_PCR_EVENT { + UINT32 PCRIndex; + UINT32 EventType; + struct _TCG_DIGEST digest; + UINT32 EventSize; + UINT8 Event[1]; +} TCG_PCR_EVENT; + +INTERFACE_DECL(_EFI_TCG); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_STATUS_CHECK) (IN struct _EFI_TCG * This, + OUT struct _TCG_BOOT_SERVICE_CAPABILITY * ProtocolCapability, + OUT UINT32 * TCGFeatureFlags, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_ALL) (IN struct _EFI_TCG * This, + IN UINT8 * HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN OUT UINT64 * HashedDataLen, IN OUT UINT8 ** HashedDataResult); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_LOG_EVENT) (IN struct _EFI_TCG * This, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, IN UINT32 Flags); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_PASS_THROUGH_TO_TPM) (IN struct _EFI_TCG * This, + IN UINT32 TpmInputParameterBlockSize, + IN UINT8 * TpmInputParameterBlock, + IN UINT32 TpmOutputParameterBlockSize, + IN UINT8 * TpmOutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_LOG_EXTEND_EVENT) (IN struct _EFI_TCG * This, + IN EFI_PHYSICAL_ADDRESS HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef struct _EFI_TCG { + EFI_TCG_STATUS_CHECK StatusCheck; + EFI_TCG_HASH_ALL HashAll; + EFI_TCG_LOG_EVENT LogEvent; + EFI_TCG_PASS_THROUGH_TO_TPM PassThroughToTPM; + EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; +} EFI_TCG; + +#define EFI_TCG2_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} + +typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; + +typedef struct tdEFI_TCG2_VERSION { + UINT8 Major; + UINT8 Minor; +} EFI_TCG2_VERSION; + +typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; +typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; +typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + BOOLEAN TPMPresentFlag; + UINT16 MaxCommandSize; + UINT16 MaxResponseSize; + UINT32 ManufacturerID; + UINT32 NumberOfPCRBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +#define EFI_TCG2_EVENT_HEADER_VERSION 1 + +typedef struct { + UINT32 HeaderSize; + UINT16 HeaderVersion; + UINT32 PCRIndex; + UINT32 EventType; +} EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + UINT32 Size; + EFI_TCG2_EVENT_HEADER Header; + UINT8 Event[1]; +} EFI_TCG2_EVENT; + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_EVENT_LOG) (IN EFI_TCG2_PROTOCOL * This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry, + OUT BOOLEAN * EventLogTruncated); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) (IN EFI_TCG2_PROTOCOL * This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, IN EFI_TCG2_EVENT * EfiTcgEvent); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SUBMIT_COMMAND) (IN EFI_TCG2_PROTOCOL * This, + IN UINT32 InputParameterBlockSize, + IN UINT8 * InputParameterBlock, + IN UINT32 OutputParameterBlockSize, IN UINT8 * OutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, OUT UINT32 * ActivePcrBanks); +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, IN UINT32 ActivePcrBanks); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, + OUT UINT32 * OperationPresent, OUT UINT32 * Response); + +typedef struct tdEFI_TCG2_PROTOCOL { + EFI_TCG2_GET_CAPABILITY GetCapability; + EFI_TCG2_GET_EVENT_LOG GetEventLog; + EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; + EFI_TCG2_SUBMIT_COMMAND SubmitCommand; + EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; + EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; + EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; +} EFI_TCG2; + + +static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINTN buffer_size, const CHAR16 *description) { + EFI_STATUS status; + TCG_PCR_EVENT *tcg_event; + UINT32 event_number; + EFI_PHYSICAL_ADDRESS event_log_last; + UINTN desc_len; + + desc_len = (StrLen(description) + 1) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT)); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->EventSize = desc_len; + CopyMem((VOID *) & tcg_event->Event[0], (VOID *) description, desc_len); + + tcg_event->PCRIndex = pcrindex; + tcg_event->EventType = EV_IPL; + + event_number = 1; + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 7, + tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); + + if (EFI_ERROR(status)) + return status; + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + return EFI_SUCCESS; +} + + +static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINT64 buffer_size, const CHAR16 *description) { + EFI_STATUS status; + EFI_TCG2_EVENT *tcg_event; + UINTN desc_len; + + desc_len = StrLen(description) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->Size = sizeof(EFI_TCG2_EVENT) - sizeof(tcg_event->Event) + desc_len + 1; + tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); + tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + tcg_event->Header.PCRIndex = pcrindex; + tcg_event->Header.EventType = EV_IPL; + + CopyMem((VOID *) tcg_event->Event, (VOID *) description, desc_len); + + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, tcg, 0, buffer, buffer_size, tcg_event); + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + if (EFI_ERROR(status)) + return status; + + return EFI_SUCCESS; +} + +static EFI_TCG * tcg1_interface_check(void) { + EFI_GUID tpm_guid = EFI_TCG_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG *tcg; + TCG_BOOT_SERVICE_CAPABILITY capability; + UINT32 features; + EFI_PHYSICAL_ADDRESS event_log_location; + EFI_PHYSICAL_ADDRESS event_log_last_entry; + + status = LibLocateProtocol(&tpm_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->StatusCheck, 5, tcg, &capability, &features, &event_log_location, &event_log_last_entry); + + if (EFI_ERROR(status)) + return NULL; + + if (capability.TPMDeactivatedFlag) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +static EFI_TCG2 * tcg2_interface_check(void) { + EFI_GUID tpm2_guid = EFI_TCG2_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG2 *tcg; + EFI_TCG2_BOOT_SERVICE_CAPABILITY capability; + + status = LibLocateProtocol(&tpm2_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->GetCapability, 2, tcg, &capability); + + if (EFI_ERROR(status)) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { + EFI_TCG *tpm1; + EFI_TCG2 *tpm2; + + tpm2 = tcg2_interface_check(); + if (tpm2) + return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); + + tpm1 = tcg1_interface_check(); + if (tpm1) + return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description); + + /* No active TPM found, so don't return an error */ + return EFI_SUCCESS; +} + +#endif diff --git a/src/grp-boot/systemd-boot/measure.h b/src/grp-boot/systemd-boot/measure.h new file mode 100644 index 0000000000..a2cfe817d0 --- /dev/null +++ b/src/grp-boot/systemd-boot/measure.h @@ -0,0 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ +#ifndef __SDBOOT_MEASURE_H +#define __SDBOOT_MEASURE_H + +#ifndef SD_TPM_PCR +#define SD_TPM_PCR 8 +#endif + +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); +#endif diff --git a/src/grp-boot/systemd-boot/pefile.c b/src/grp-boot/systemd-boot/pefile.c new file mode 100644 index 0000000000..77fff77b69 --- /dev/null +++ b/src/grp-boot/systemd-boot/pefile.c @@ -0,0 +1,170 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "pefile.h" +#include "util.h" + +struct DosFileHeader { + UINT8 Magic[2]; + UINT16 LastSize; + UINT16 nBlocks; + UINT16 nReloc; + UINT16 HdrSize; + UINT16 MinAlloc; + UINT16 MaxAlloc; + UINT16 ss; + UINT16 sp; + UINT16 Checksum; + UINT16 ip; + UINT16 cs; + UINT16 RelocPos; + UINT16 nOverlay; + UINT16 reserved[4]; + UINT16 OEMId; + UINT16 OEMInfo; + UINT16 reserved2[10]; + UINT32 ExeHeader; +} __attribute__((packed)); + +#define PE_HEADER_MACHINE_I386 0x014c +#define PE_HEADER_MACHINE_X64 0x8664 +struct PeFileHeader { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; +} __attribute__((packed)); + +struct PeSectionHeader { + UINT8 Name[8]; + UINT32 VirtualSize; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} __attribute__((packed)); + + +EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { + EFI_FILE_HANDLE handle; + struct DosFileHeader dos; + uint8_t magic[4]; + struct PeFileHeader pe; + UINTN len; + UINTN i; + EFI_STATUS err; + + err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); + if (EFI_ERROR(err)) + return err; + + /* MS-DOS stub */ + len = sizeof(dos); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(dos)) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (CompareMem(dos.Magic, "MZ", 2) != 0) { + err = EFI_LOAD_ERROR; + goto out; + } + + err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader); + if (EFI_ERROR(err)) + goto out; + + /* PE header */ + len = sizeof(magic); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(magic)) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (CompareMem(magic, "PE\0\0", 2) != 0) { + err = EFI_LOAD_ERROR; + goto out; + } + + len = sizeof(pe); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(pe)) { + err = EFI_LOAD_ERROR; + goto out; + } + + /* PE32+ Subsystem type */ + if (pe.Machine != PE_HEADER_MACHINE_X64 && + pe.Machine != PE_HEADER_MACHINE_I386) { + err = EFI_LOAD_ERROR; + goto out; + } + + if (pe.NumberOfSections > 96) { + err = EFI_LOAD_ERROR; + goto out; + } + + /* the sections start directly after the headers */ + err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader); + if (EFI_ERROR(err)) + goto out; + + for (i = 0; i < pe.NumberOfSections; i++) { + struct PeSectionHeader sect; + UINTN j; + + len = sizeof(sect); + err = uefi_call_wrapper(handle->Read, 3, handle, &len, §); + if (EFI_ERROR(err)) + goto out; + if (len != sizeof(sect)) { + err = EFI_LOAD_ERROR; + goto out; + } + for (j = 0; sections[j]; j++) { + if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0) + continue; + + if (addrs) + addrs[j] = (UINTN)sect.VirtualAddress; + if (offsets) + offsets[j] = (UINTN)sect.PointerToRawData; + if (sizes) + sizes[j] = (UINTN)sect.VirtualSize; + } + } + +out: + uefi_call_wrapper(handle->Close, 1, handle); + return err; +} diff --git a/src/grp-boot/systemd-boot/pefile.h b/src/grp-boot/systemd-boot/pefile.h new file mode 100644 index 0000000000..2e445ede17 --- /dev/null +++ b/src/grp-boot/systemd-boot/pefile.h @@ -0,0 +1,20 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#ifndef __SDBOOT_PEFILE_H +#define __SDBOOT_PEFILE_H + +EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, + CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); +#endif diff --git a/src/grp-boot/systemd-boot/splash.c b/src/grp-boot/systemd-boot/splash.c new file mode 100644 index 0000000000..c0ef7f64fe --- /dev/null +++ b/src/grp-boot/systemd-boot/splash.c @@ -0,0 +1,321 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#include +#include + +#include "graphics.h" +#include "splash.h" +#include "util.h" + +struct bmp_file { + CHAR8 signature[2]; + UINT32 size; + UINT16 reserved[2]; + UINT32 offset; +} __attribute__((packed)); + +/* we require at least BITMAPINFOHEADER, later versions are + accepted, but their features ignored */ +struct bmp_dib { + UINT32 size; + UINT32 x; + UINT32 y; + UINT16 planes; + UINT16 depth; + UINT32 compression; + UINT32 image_size; + INT32 x_pixel_meter; + INT32 y_pixel_meter; + UINT32 colors_used; + UINT32 colors_important; +} __attribute__((packed)); + +struct bmp_map { + UINT8 blue; + UINT8 green; + UINT8 red; + UINT8 reserved; +} __attribute__((packed)); + +EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib, + struct bmp_map **ret_map, UINT8 **pixmap) { + struct bmp_file *file; + struct bmp_dib *dib; + struct bmp_map *map; + UINTN row_size; + + if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib)) + return EFI_INVALID_PARAMETER; + + /* check file header */ + file = (struct bmp_file *)bmp; + if (file->signature[0] != 'B' || file->signature[1] != 'M') + return EFI_INVALID_PARAMETER; + if (file->size != size) + return EFI_INVALID_PARAMETER; + if (file->size < file->offset) + return EFI_INVALID_PARAMETER; + + /* check device-independent bitmap */ + dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file)); + if (dib->size < sizeof(struct bmp_dib)) + return EFI_UNSUPPORTED; + + switch (dib->depth) { + case 1: + case 4: + case 8: + case 24: + if (dib->compression != 0) + return EFI_UNSUPPORTED; + + break; + + case 16: + case 32: + if (dib->compression != 0 && dib->compression != 3) + return EFI_UNSUPPORTED; + + break; + + default: + return EFI_UNSUPPORTED; + } + + row_size = ((UINTN) dib->depth * dib->x + 31) / 32 * 4; + if (file->size - file->offset < dib->y * row_size) + return EFI_INVALID_PARAMETER; + if (row_size * dib->y > 64 * 1024 * 1024) + return EFI_INVALID_PARAMETER; + + /* check color table */ + map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + dib->size); + if (file->offset < sizeof(struct bmp_file) + dib->size) + return EFI_INVALID_PARAMETER; + + if (file->offset > sizeof(struct bmp_file) + dib->size) { + UINT32 map_count; + UINTN map_size; + + if (dib->colors_used) + map_count = dib->colors_used; + else { + switch (dib->depth) { + case 1: + case 4: + case 8: + map_count = 1 << dib->depth; + break; + + default: + map_count = 0; + break; + } + } + + map_size = file->offset - (sizeof(struct bmp_file) + dib->size); + if (map_size != sizeof(struct bmp_map) * map_count) + return EFI_INVALID_PARAMETER; + } + + *ret_map = map; + *ret_dib = dib; + *pixmap = bmp + file->offset; + + return EFI_SUCCESS; +} + +static VOID pixel_blend(UINT32 *dst, const UINT32 source) { + UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g; + + alpha = (source & 0xff); + + /* convert src from RGBA to XRGB */ + src = source >> 8; + + /* decompose into RB and G components */ + src_rb = (src & 0xff00ff); + src_g = (src & 0x00ff00); + + dst_rb = (*dst & 0xff00ff); + dst_g = (*dst & 0x00ff00); + + /* blend */ + rb = ((((src_rb - dst_rb) * alpha + 0x800080) >> 8) + dst_rb) & 0xff00ff; + g = ((((src_g - dst_g) * alpha + 0x008000) >> 8) + dst_g) & 0x00ff00; + + *dst = (rb | g); +} + +EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, + struct bmp_dib *dib, struct bmp_map *map, + UINT8 *pixmap) { + UINT8 *in; + UINTN y; + + /* transform and copy pixels */ + in = pixmap; + for (y = 0; y < dib->y; y++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out; + UINTN row_size; + UINTN x; + + out = &buf[(dib->y - y - 1) * dib->x]; + for (x = 0; x < dib->x; x++, in++, out++) { + switch (dib->depth) { + case 1: { + UINTN i; + + for (i = 0; i < 8 && x < dib->x; i++) { + out->Red = map[((*in) >> (7 - i)) & 1].red; + out->Green = map[((*in) >> (7 - i)) & 1].green; + out->Blue = map[((*in) >> (7 - i)) & 1].blue; + out++; + x++; + } + out--; + x--; + break; + } + + case 4: { + UINTN i; + + i = (*in) >> 4; + out->Red = map[i].red; + out->Green = map[i].green; + out->Blue = map[i].blue; + if (x < (dib->x - 1)) { + out++; + x++; + i = (*in) & 0x0f; + out->Red = map[i].red; + out->Green = map[i].green; + out->Blue = map[i].blue; + } + break; + } + + case 8: + out->Red = map[*in].red; + out->Green = map[*in].green; + out->Blue = map[*in].blue; + break; + + case 16: { + UINT16 i = *(UINT16 *) in; + + out->Red = (i & 0x7c00) >> 7; + out->Green = (i & 0x3e0) >> 2; + out->Blue = (i & 0x1f) << 3; + in += 1; + break; + } + + case 24: + out->Red = in[2]; + out->Green = in[1]; + out->Blue = in[0]; + in += 2; + break; + + case 32: { + UINT32 i = *(UINT32 *) in; + + pixel_blend((UINT32 *)out, i); + + in += 3; + break; + } + } + } + + /* add row padding; new lines always start at 32 bit boundary */ + row_size = in - pixmap; + in += ((row_size + 3) & ~3) - row_size; + } + + return EFI_SUCCESS; +} + +EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL pixel = {}; + EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; + struct bmp_dib *dib; + struct bmp_map *map; + UINT8 *pixmap; + UINT64 blt_size; + VOID *blt = NULL; + UINTN x_pos = 0; + UINTN y_pos = 0; + EFI_STATUS err; + + if (!background) { + if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) { + pixel.Red = 0xc0; + pixel.Green = 0xc0; + pixel.Blue = 0xc0; + } + background = &pixel; + } + + err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); + if (EFI_ERROR(err)) + return err; + + err = bmp_parse_header(content, len, &dib, &map, &pixmap); + if (EFI_ERROR(err)) + goto err; + + if (dib->x < GraphicsOutput->Mode->Info->HorizontalResolution) + x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2; + if (dib->y < GraphicsOutput->Mode->Info->VerticalResolution) + y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2; + + uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)background, + EfiBltVideoFill, 0, 0, 0, 0, + GraphicsOutput->Mode->Info->HorizontalResolution, + GraphicsOutput->Mode->Info->VerticalResolution, 0); + + /* EFI buffer */ + blt_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + blt = AllocatePool(blt_size); + if (!blt) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, + blt, EfiBltVideoToBltBuffer, x_pos, y_pos, 0, 0, + dib->x, dib->y, 0); + if (EFI_ERROR(err)) + goto err; + + err = bmp_to_blt(blt, dib, map, pixmap); + if (EFI_ERROR(err)) + goto err; + + err = graphics_mode(TRUE); + if (EFI_ERROR(err)) + goto err; + + err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, + blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos, + dib->x, dib->y, 0); +err: + FreePool(blt); + return err; +} diff --git a/src/grp-boot/systemd-boot/splash.h b/src/grp-boot/systemd-boot/splash.h new file mode 100644 index 0000000000..09b543fb47 --- /dev/null +++ b/src/grp-boot/systemd-boot/splash.h @@ -0,0 +1,20 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#ifndef __SDBOOT_SPLASH_H +#define __SDBOOT_SPLASH_H + +EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_BLT_PIXEL *background); +#endif diff --git a/src/grp-boot/systemd-boot/stub.c b/src/grp-boot/systemd-boot/stub.c new file mode 100644 index 0000000000..9fae0c1372 --- /dev/null +++ b/src/grp-boot/systemd-boot/stub.c @@ -0,0 +1,130 @@ +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2015 Kay Sievers + */ + +#include +#include + +#include "disk.h" +#include "graphics.h" +#include "linux.h" +#include "measure.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 " ####"; + +static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + +EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { + EFI_LOADED_IMAGE *loaded_image; + EFI_FILE *root_dir; + CHAR16 *loaded_image_path; + CHAR8 *b; + UINTN size; + BOOLEAN secure = FALSE; + CHAR8 *sections[] = { + (UINT8 *)".cmdline", + (UINT8 *)".linux", + (UINT8 *)".initrd", + (UINT8 *)".splash", + NULL + }; + UINTN addrs[ELEMENTSOF(sections)-1] = {}; + UINTN offs[ELEMENTSOF(sections)-1] = {}; + UINTN szs[ELEMENTSOF(sections)-1] = {}; + CHAR8 *cmdline = NULL; + UINTN cmdline_len; + CHAR16 uuid[37]; + EFI_STATUS err; + + InitializeLib(image, sys_table); + + err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(err)) { + Print(L"Error getting a LoadedImageProtocol handle: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + + root_dir = LibOpenRoot(loaded_image->DeviceHandle); + if (!root_dir) { + Print(L"Unable to open root directory: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return EFI_LOAD_ERROR; + } + + loaded_image_path = DevicePathToStr(loaded_image->FilePath); + + if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { + if (*b > 0) + secure = TRUE; + FreePool(b); + } + + err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs); + if (EFI_ERROR(err)) { + Print(L"Unable to locate embedded .linux section: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + + if (szs[0] > 0) + cmdline = (CHAR8 *)(loaded_image->ImageBase + addrs[0]); + + cmdline_len = szs[0]; + + /* if we are not in secure boot mode, accept a custom command line and replace the built-in one */ + if (!secure && loaded_image->LoadOptionsSize > 0) { + CHAR16 *options; + CHAR8 *line; + UINTN i; + + options = (CHAR16 *)loaded_image->LoadOptions; + cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8); + line = AllocatePool(cmdline_len); + for (i = 0; i < cmdline_len; i++) + line[i] = options[i]; + cmdline = line; + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif + } + + /* export the device path this image is started from */ + if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) + efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); + + if (szs[3] > 0) + graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL); + + err = linux_exec(image, cmdline, cmdline_len, + (UINTN)loaded_image->ImageBase + addrs[1], + (UINTN)loaded_image->ImageBase + addrs[2], szs[2]); + + graphics_mode(FALSE); + Print(L"Execution of embedded linux image failed: %r\n", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; +} diff --git a/src/grp-boot/systemd-boot/test-efi-create-disk.sh b/src/grp-boot/systemd-boot/test-efi-create-disk.sh new file mode 100755 index 0000000000..cd4699dc18 --- /dev/null +++ b/src/grp-boot/systemd-boot/test-efi-create-disk.sh @@ -0,0 +1,42 @@ +#!/bin/bash -e + +# create GPT table with EFI System Partition +rm -f test-efi-disk.img +dd if=/dev/null of=test-efi-disk.img bs=1M seek=512 count=1 +parted --script test-efi-disk.img "mklabel gpt" "mkpart ESP fat32 1MiB 511MiB" "set 1 boot on" + +# create FAT32 file system +LOOP=$(losetup --show -f -P test-efi-disk.img) +mkfs.vfat -F32 ${LOOP}p1 +mkdir -p mnt +mount ${LOOP}p1 mnt + +mkdir -p mnt/EFI/{BOOT,systemd} +cp systemd-bootx64.efi mnt/EFI/BOOT/BOOTX64.efi + +[ -e /boot/shellx64.efi ] && cp /boot/shellx64.efi mnt/ + +mkdir mnt/EFI/Linux +echo -n "foo=yes bar=no root=/dev/fakeroot debug rd.break=initqueue" > mnt/cmdline.txt +objcopy \ + --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \ + --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \ + --add-section .splash=test/splash.bmp --change-section-vma .splash=0x40000 \ + --add-section .linux=/boot/$(cat /etc/machine-id)/$(uname -r)/linux --change-section-vma .linux=0x2000000 \ + --add-section .initrd=/boot/$(cat /etc/machine-id)/$(uname -r)/initrd --change-section-vma .initrd=0x3000000 \ + linuxx64.efi.stub mnt/EFI/Linux/linux-test.efi + +# install entries +mkdir -p mnt/loader/entries +echo -e "timeout 3\n" > mnt/loader/loader.conf +echo -e "title Test\nefi /test\n" > mnt/loader/entries/test.conf +echo -e "title Test2\nlinux /test2\noptions option=yes word number=1000 more\n" > mnt/loader/entries/test2.conf +echo -e "title Test3\nlinux /test3\n" > mnt/loader/entries/test3.conf +echo -e "title Test4\nlinux /test4\n" > mnt/loader/entries/test4.conf +echo -e "title Test5\nefi /test5\n" > mnt/loader/entries/test5.conf +echo -e "title Test6\nlinux /test6\n" > mnt/loader/entries/test6.conf + +sync +umount mnt +rmdir mnt +losetup -d $LOOP diff --git a/src/grp-boot/systemd-boot/util.c b/src/grp-boot/systemd-boot/util.c new file mode 100644 index 0000000000..98c5be74ce --- /dev/null +++ b/src/grp-boot/systemd-boot/util.c @@ -0,0 +1,345 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#include +#include + +#include "util.h" + +/* + * Allocated random UUID, intended to be shared across tools that implement + * the (ESP)\loader\entries\-.conf convention and the + * associated EFI variables. + */ +static const EFI_GUID loader_guid = { 0x4a67b082, 0x0a4c, 0x41cf, {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f} }; + +#ifdef __x86_64__ +UINT64 ticks_read(VOID) { + UINT64 a, d; + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d << 32) | a; +} +#elif defined(__i386__) +UINT64 ticks_read(VOID) { + UINT64 val; + __asm__ volatile ("rdtsc" : "=A" (val)); + return val; +} +#else +UINT64 ticks_read(VOID) { + UINT64 val = 1; + return val; +} +#endif + +/* count TSC ticks during a millisecond delay */ +UINT64 ticks_freq(VOID) { + UINT64 ticks_start, ticks_end; + + ticks_start = ticks_read(); + uefi_call_wrapper(BS->Stall, 1, 1000); + ticks_end = ticks_read(); + + return (ticks_end - ticks_start) * 1000; +} + +UINT64 time_usec(VOID) { + UINT64 ticks; + static UINT64 freq; + + ticks = ticks_read(); + if (ticks == 0) + return 0; + + if (freq == 0) { + freq = ticks_freq(); + if (freq == 0) + return 0; + } + + return 1000 * 1000 * ticks / freq; +} + +EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b) { + if (strcmpa(v, (CHAR8 *)"1") == 0 || + strcmpa(v, (CHAR8 *)"yes") == 0 || + strcmpa(v, (CHAR8 *)"y") == 0 || + strcmpa(v, (CHAR8 *)"true") == 0) { + *b = TRUE; + return EFI_SUCCESS; + } + + if (strcmpa(v, (CHAR8 *)"0") == 0 || + strcmpa(v, (CHAR8 *)"no") == 0 || + strcmpa(v, (CHAR8 *)"n") == 0 || + strcmpa(v, (CHAR8 *)"false") == 0) { + *b = FALSE; + return EFI_SUCCESS; + } + + return EFI_INVALID_PARAMETER; +} + +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { + UINT32 flags; + + flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + if (persistent) + flags |= EFI_VARIABLE_NON_VOLATILE; + + return uefi_call_wrapper(RT->SetVariable, 5, name, (EFI_GUID *)vendor, flags, size, buf); +} + +EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent) { + return efivar_set_raw(&loader_guid, name, (CHAR8 *)value, value ? (StrLen(value)+1) * sizeof(CHAR16) : 0, persistent); +} + +EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent) { + CHAR16 str[32]; + + SPrint(str, 32, L"%d", i); + return efivar_set(name, str, persistent); +} + +EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value) { + CHAR8 *buf; + CHAR16 *val; + UINTN size; + EFI_STATUS err; + + err = efivar_get_raw(&loader_guid, name, &buf, &size); + if (EFI_ERROR(err)) + return err; + + val = StrDuplicate((CHAR16 *)buf); + if (!val) { + FreePool(buf); + return EFI_OUT_OF_RESOURCES; + } + + *value = val; + return EFI_SUCCESS; +} + +EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i) { + CHAR16 *val; + EFI_STATUS err; + + err = efivar_get(name, &val); + if (!EFI_ERROR(err)) { + *i = Atoi(val); + FreePool(val); + } + return err; +} + +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { + CHAR8 *buf; + UINTN l; + EFI_STATUS err; + + l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; + buf = AllocatePool(l); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(RT->GetVariable, 5, name, (EFI_GUID *)vendor, NULL, &l, buf); + if (!EFI_ERROR(err)) { + *buffer = buf; + if (size) + *size = l; + } else + FreePool(buf); + return err; + +} + +VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec) { + CHAR16 str[32]; + + if (usec == 0) + usec = time_usec(); + if (usec == 0) + return; + + SPrint(str, 32, L"%ld", usec); + efivar_set(name, str, FALSE); +} + +static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) { + CHAR16 unichar; + UINTN len; + UINTN i; + + if (stra[0] < 0x80) + len = 1; + else if ((stra[0] & 0xe0) == 0xc0) + len = 2; + else if ((stra[0] & 0xf0) == 0xe0) + len = 3; + else if ((stra[0] & 0xf8) == 0xf0) + len = 4; + else if ((stra[0] & 0xfc) == 0xf8) + len = 5; + else if ((stra[0] & 0xfe) == 0xfc) + len = 6; + else + return -1; + + switch (len) { + case 1: + unichar = stra[0]; + break; + case 2: + unichar = stra[0] & 0x1f; + break; + case 3: + unichar = stra[0] & 0x0f; + break; + case 4: + unichar = stra[0] & 0x07; + break; + case 5: + unichar = stra[0] & 0x03; + break; + case 6: + unichar = stra[0] & 0x01; + break; + } + + for (i = 1; i < len; i++) { + if ((stra[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= stra[i] & 0x3f; + } + + *c = unichar; + return len; +} + +CHAR16 *stra_to_str(CHAR8 *stra) { + UINTN strlen; + UINTN len; + UINTN i; + CHAR16 *str; + + len = strlena(stra); + str = AllocatePool((len + 1) * sizeof(CHAR16)); + + strlen = 0; + i = 0; + while (i < len) { + INTN utf8len; + + utf8len = utf8_to_16(stra + i, str + strlen); + if (utf8len <= 0) { + /* invalid utf8 sequence, skip the garbage */ + i++; + continue; + } + + strlen++; + i += utf8len; + } + str[strlen] = '\0'; + return str; +} + +CHAR16 *stra_to_path(CHAR8 *stra) { + CHAR16 *str; + UINTN strlen; + UINTN len; + UINTN i; + + len = strlena(stra); + str = AllocatePool((len + 2) * sizeof(CHAR16)); + + str[0] = '\\'; + strlen = 1; + i = 0; + while (i < len) { + INTN utf8len; + + utf8len = utf8_to_16(stra + i, str + strlen); + if (utf8len <= 0) { + /* invalid utf8 sequence, skip the garbage */ + i++; + continue; + } + + if (str[strlen] == '/') + str[strlen] = '\\'; + if (str[strlen] == '\\' && str[strlen-1] == '\\') { + /* skip double slashes */ + i += utf8len; + continue; + } + + strlen++; + i += utf8len; + } + str[strlen] = '\0'; + return str; +} + +CHAR8 *strchra(CHAR8 *s, CHAR8 c) { + do { + if (*s == c) + return s; + } while (*s++); + return NULL; +} + +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content) { + EFI_FILE_HANDLE handle; + CHAR8 *buf; + UINTN buflen; + EFI_STATUS err; + UINTN len; + + err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL); + if (EFI_ERROR(err)) + return err; + + if (size == 0) { + EFI_FILE_INFO *info; + + info = LibFileInfo(handle); + buflen = info->FileSize+1; + FreePool(info); + } else + buflen = size; + + if (off > 0) { + err = uefi_call_wrapper(handle->SetPosition, 2, handle, off); + if (EFI_ERROR(err)) + return err; + } + + buf = AllocatePool(buflen); + err = uefi_call_wrapper(handle->Read, 3, handle, &buflen, buf); + if (!EFI_ERROR(err)) { + buf[buflen] = '\0'; + *content = buf; + len = buflen; + } else { + len = err; + FreePool(buf); + } + + uefi_call_wrapper(handle->Close, 1, handle); + return len; +} diff --git a/src/grp-boot/systemd-boot/util.h b/src/grp-boot/systemd-boot/util.h new file mode 100644 index 0000000000..e673cdf9a0 --- /dev/null +++ b/src/grp-boot/systemd-boot/util.h @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Copyright (C) 2012-2013 Kay Sievers + * Copyright (C) 2012 Harald Hoyer + */ + +#ifndef __SDBOOT_UTIL_H +#define __SDBOOT_UTIL_H + +#include +#include + +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +static inline const CHAR16 *yes_no(BOOLEAN b) { + return b ? L"yes" : L"no"; +} + +EFI_STATUS parse_boolean(CHAR8 *v, BOOLEAN *b); + +UINT64 ticks_read(void); +UINT64 ticks_freq(void); +UINT64 time_usec(void); + +EFI_STATUS efivar_set(CHAR16 *name, CHAR16 *value, BOOLEAN persistent); +EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent); +EFI_STATUS efivar_set_int(CHAR16 *name, UINTN i, BOOLEAN persistent); +VOID efivar_set_time_usec(CHAR16 *name, UINT64 usec); + +EFI_STATUS efivar_get(CHAR16 *name, CHAR16 **value); +EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size); +EFI_STATUS efivar_get_int(CHAR16 *name, UINTN *i); + +CHAR8 *strchra(CHAR8 *s, CHAR8 c); +CHAR16 *stra_to_path(CHAR8 *stra); +CHAR16 *stra_to_str(CHAR8 *stra); + +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, UINTN off, UINTN size, CHAR8 **content); +#endif diff --git a/src/grp-coredump/Makefile b/src/grp-coredump/Makefile new file mode 100644 index 0000000000..c2bbf948e9 --- /dev/null +++ b/src/grp-coredump/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += coredumpctl +nested.subdirs += systemd-coredump + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-coredump/coredumpctl/Makefile b/src/grp-coredump/coredumpctl/Makefile new file mode 100644 index 0000000000..25a0ee29f2 --- /dev/null +++ b/src/grp-coredump/coredumpctl/Makefile @@ -0,0 +1,41 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +coredumpctl_SOURCES = \ + src/coredump/coredumpctl.c + +coredumpctl_LDADD = \ + libsystemd-shared.la + +bin_PROGRAMS += \ + coredumpctl + +dist_bashcompletion_data += \ + shell-completion/bash/coredumpctl + +dist_zshcompletion_data += \ + shell-completion/zsh/_coredumpctl + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-coredump/coredumpctl/coredumpctl.c b/src/grp-coredump/coredumpctl/coredumpctl.c new file mode 100644 index 0000000000..19d28f744e --- /dev/null +++ b/src/grp-coredump/coredumpctl/coredumpctl.c @@ -0,0 +1,878 @@ +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/sigbus.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-journal/compress.h" +#include "sd-journal/journal-internal.h" +#include "shared/pager.h" + +static enum { + ACTION_NONE, + ACTION_INFO, + ACTION_LIST, + ACTION_DUMP, + ACTION_GDB, +} arg_action = ACTION_LIST; +static const char* arg_field = NULL; +static const char *arg_directory = NULL; +static bool arg_no_pager = false; +static int arg_no_legend = false; +static int arg_one = false; +static FILE* arg_output = NULL; + +static Set *new_matches(void) { + Set *set; + char *tmp; + int r; + + set = set_new(NULL); + if (!set) { + log_oom(); + return NULL; + } + + tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); + if (!tmp) { + log_oom(); + set_free(set); + return NULL; + } + + r = set_consume(set, tmp); + if (r < 0) { + log_error_errno(r, "failed to add to set: %m"); + set_free(set); + return NULL; + } + + return set; +} + +static int add_match(Set *set, const char *match) { + _cleanup_free_ char *p = NULL; + char *pattern = NULL; + const char* prefix; + pid_t pid; + int r; + + if (strchr(match, '=')) + prefix = ""; + else if (strchr(match, '/')) { + r = path_make_absolute_cwd(match, &p); + if (r < 0) + goto fail; + match = p; + prefix = "COREDUMP_EXE="; + } else if (parse_pid(match, &pid) >= 0) + prefix = "COREDUMP_PID="; + else + prefix = "COREDUMP_COMM="; + + pattern = strjoin(prefix, match, NULL); + if (!pattern) { + r = -ENOMEM; + goto fail; + } + + log_debug("Adding pattern: %s", pattern); + r = set_consume(set, pattern); + if (r < 0) + goto fail; + + return 0; +fail: + return log_error_errno(r, "Failed to add match: %m"); +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "List or retrieve coredumps from the journal.\n\n" + "Flags:\n" + " -h --help Show this help\n" + " --version Print version string\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not print the column headers.\n" + " -1 Show information about most recent entry only\n" + " -F --field=FIELD List all values a certain field takes\n" + " -o --output=FILE Write output to FILE\n\n" + " -D --directory=DIR Use journal files from directory\n\n" + + "Commands:\n" + " list [MATCHES...] List available coredumps (default)\n" + " info [MATCHES...] Show detailed information about one or more coredumps\n" + " dump [MATCHES...] Print first matching coredump to stdout\n" + " gdb [MATCHES...] Start gdb for the first matching coredump\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[], Set *matches) { + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, + }; + + int r, c; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "output", required_argument, NULL, 'o' }, + { "field", required_argument, NULL, 'F' }, + { "directory", required_argument, NULL, 'D' }, + {} + }; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0) + switch(c) { + + case 'h': + arg_action = ACTION_NONE; + help(); + return 0; + + case ARG_VERSION: + arg_action = ACTION_NONE; + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_LEGEND: + arg_no_legend = true; + break; + + case 'o': + if (arg_output) { + log_error("cannot set output more than once"); + return -EINVAL; + } + + arg_output = fopen(optarg, "we"); + if (!arg_output) + return log_error_errno(errno, "writing to '%s': %m", optarg); + + break; + + case 'F': + if (arg_field) { + log_error("cannot use --field/-F more than once"); + return -EINVAL; + } + arg_field = optarg; + break; + + case '1': + arg_one = true; + break; + + case 'D': + arg_directory = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + const char *cmd = argv[optind++]; + if (streq(cmd, "list")) + arg_action = ACTION_LIST; + else if (streq(cmd, "dump")) + arg_action = ACTION_DUMP; + else if (streq(cmd, "gdb")) + arg_action = ACTION_GDB; + else if (streq(cmd, "info")) + arg_action = ACTION_INFO; + else { + log_error("Unknown action '%s'", cmd); + return -EINVAL; + } + } + + if (arg_field && arg_action != ACTION_LIST) { + log_error("Option --field/-F only makes sense with list"); + return -EINVAL; + } + + while (optind < argc) { + r = add_match(matches, argv[optind]); + if (r != 0) + return r; + optind++; + } + + return 0; +} + +static int retrieve(const void *data, + size_t len, + const char *name, + char **var) { + + size_t ident; + char *v; + + ident = strlen(name) + 1; /* name + "=" */ + + if (len < ident) + return 0; + + if (memcmp(data, name, ident - 1) != 0) + return 0; + + if (((const char*) data)[ident - 1] != '=') + return 0; + + v = strndup((const char*)data + ident, len - ident); + if (!v) + return log_oom(); + + free(*var); + *var = v; + + return 0; +} + +static void print_field(FILE* file, sd_journal *j) { + _cleanup_free_ char *value = NULL; + const void *d; + size_t l; + + assert(file); + assert(j); + + assert(arg_field); + + SD_JOURNAL_FOREACH_DATA(j, d, l) + retrieve(d, l, arg_field, &value); + + if (value) + fprintf(file, "%s\n", value); +} + +static int print_list(FILE* file, sd_journal *j, int had_legend) { + _cleanup_free_ char + *pid = NULL, *uid = NULL, *gid = NULL, + *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, + *filename = NULL; + const void *d; + size_t l; + usec_t t; + char buf[FORMAT_TIMESTAMP_MAX]; + int r; + bool present; + + assert(file); + assert(j); + + SD_JOURNAL_FOREACH_DATA(j, d, l) { + retrieve(d, l, "COREDUMP_PID", &pid); + retrieve(d, l, "COREDUMP_UID", &uid); + retrieve(d, l, "COREDUMP_GID", &gid); + retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); + retrieve(d, l, "COREDUMP_EXE", &exe); + retrieve(d, l, "COREDUMP_COMM", &comm); + retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); + retrieve(d, l, "COREDUMP_FILENAME", &filename); + } + + if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) { + log_warning("Empty coredump log entry"); + return -EINVAL; + } + + r = sd_journal_get_realtime_usec(j, &t); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + format_timestamp(buf, sizeof(buf), t); + present = filename && access(filename, F_OK) == 0; + + if (!had_legend && !arg_no_legend) + fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", + FORMAT_TIMESTAMP_WIDTH, "TIME", + 6, "PID", + 5, "UID", + 5, "GID", + 3, "SIG", + 1, "PRESENT", + "EXE"); + + fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", + FORMAT_TIMESTAMP_WIDTH, buf, + 6, strna(pid), + 5, strna(uid), + 5, strna(gid), + 3, strna(sgnl), + 1, present ? "*" : "", + strna(exe ?: (comm ?: cmdline))); + + return 0; +} + +static int print_info(FILE *file, sd_journal *j, bool need_space) { + _cleanup_free_ char + *pid = NULL, *uid = NULL, *gid = NULL, + *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, + *unit = NULL, *user_unit = NULL, *session = NULL, + *boot_id = NULL, *machine_id = NULL, *hostname = NULL, + *slice = NULL, *cgroup = NULL, *owner_uid = NULL, + *message = NULL, *timestamp = NULL, *filename = NULL; + const void *d; + size_t l; + int r; + + assert(file); + assert(j); + + SD_JOURNAL_FOREACH_DATA(j, d, l) { + retrieve(d, l, "COREDUMP_PID", &pid); + retrieve(d, l, "COREDUMP_UID", &uid); + retrieve(d, l, "COREDUMP_GID", &gid); + retrieve(d, l, "COREDUMP_SIGNAL", &sgnl); + retrieve(d, l, "COREDUMP_EXE", &exe); + retrieve(d, l, "COREDUMP_COMM", &comm); + retrieve(d, l, "COREDUMP_CMDLINE", &cmdline); + retrieve(d, l, "COREDUMP_UNIT", &unit); + retrieve(d, l, "COREDUMP_USER_UNIT", &user_unit); + retrieve(d, l, "COREDUMP_SESSION", &session); + retrieve(d, l, "COREDUMP_OWNER_UID", &owner_uid); + retrieve(d, l, "COREDUMP_SLICE", &slice); + retrieve(d, l, "COREDUMP_CGROUP", &cgroup); + retrieve(d, l, "COREDUMP_TIMESTAMP", ×tamp); + retrieve(d, l, "COREDUMP_FILENAME", &filename); + retrieve(d, l, "_BOOT_ID", &boot_id); + retrieve(d, l, "_MACHINE_ID", &machine_id); + retrieve(d, l, "_HOSTNAME", &hostname); + retrieve(d, l, "MESSAGE", &message); + } + + if (need_space) + fputs("\n", file); + + if (comm) + fprintf(file, + " PID: %s%s%s (%s)\n", + ansi_highlight(), strna(pid), ansi_normal(), comm); + else + fprintf(file, + " PID: %s%s%s\n", + ansi_highlight(), strna(pid), ansi_normal()); + + if (uid) { + uid_t n; + + if (parse_uid(uid, &n) >= 0) { + _cleanup_free_ char *u = NULL; + + u = uid_to_name(n); + fprintf(file, + " UID: %s (%s)\n", + uid, u); + } else { + fprintf(file, + " UID: %s\n", + uid); + } + } + + if (gid) { + gid_t n; + + if (parse_gid(gid, &n) >= 0) { + _cleanup_free_ char *g = NULL; + + g = gid_to_name(n); + fprintf(file, + " GID: %s (%s)\n", + gid, g); + } else { + fprintf(file, + " GID: %s\n", + gid); + } + } + + if (sgnl) { + int sig; + + if (safe_atoi(sgnl, &sig) >= 0) + fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig)); + else + fprintf(file, " Signal: %s\n", sgnl); + } + + if (timestamp) { + usec_t u; + + r = safe_atou64(timestamp, &u); + if (r >= 0) { + char absolute[FORMAT_TIMESTAMP_MAX], relative[FORMAT_TIMESPAN_MAX]; + + fprintf(file, + " Timestamp: %s (%s)\n", + format_timestamp(absolute, sizeof(absolute), u), + format_timestamp_relative(relative, sizeof(relative), u)); + + } else + fprintf(file, " Timestamp: %s\n", timestamp); + } + + if (cmdline) + fprintf(file, " Command Line: %s\n", cmdline); + if (exe) + fprintf(file, " Executable: %s%s%s\n", ansi_highlight(), exe, ansi_normal()); + if (cgroup) + fprintf(file, " Control Group: %s\n", cgroup); + if (unit) + fprintf(file, " Unit: %s\n", unit); + if (user_unit) + fprintf(file, " User Unit: %s\n", unit); + if (slice) + fprintf(file, " Slice: %s\n", slice); + if (session) + fprintf(file, " Session: %s\n", session); + if (owner_uid) { + uid_t n; + + if (parse_uid(owner_uid, &n) >= 0) { + _cleanup_free_ char *u = NULL; + + u = uid_to_name(n); + fprintf(file, + " Owner UID: %s (%s)\n", + owner_uid, u); + } else { + fprintf(file, + " Owner UID: %s\n", + owner_uid); + } + } + if (boot_id) + fprintf(file, " Boot ID: %s\n", boot_id); + if (machine_id) + fprintf(file, " Machine ID: %s\n", machine_id); + if (hostname) + fprintf(file, " Hostname: %s\n", hostname); + + if (filename && access(filename, F_OK) == 0) + fprintf(file, " Coredump: %s\n", filename); + + if (message) { + _cleanup_free_ char *m = NULL; + + m = strreplace(message, "\n", "\n "); + + fprintf(file, " Message: %s\n", strstrip(m ?: message)); + } + + return 0; +} + +static int focus(sd_journal *j) { + int r; + + r = sd_journal_seek_tail(j); + if (r == 0) + r = sd_journal_previous(j); + if (r < 0) + return log_error_errno(r, "Failed to search journal: %m"); + if (r == 0) { + log_error("No match found."); + return -ESRCH; + } + return r; +} + +static void print_entry(sd_journal *j, unsigned n_found) { + assert(j); + + if (arg_action == ACTION_INFO) + print_info(stdout, j, n_found); + else if (arg_field) + print_field(stdout, j); + else + print_list(stdout, j, n_found); +} + +static int dump_list(sd_journal *j) { + unsigned n_found = 0; + int r; + + assert(j); + + /* The coredumps are likely to compressed, and for just + * listing them we don't need to decompress them, so let's + * pick a fairly low data threshold here */ + sd_journal_set_data_threshold(j, 4096); + + if (arg_one) { + r = focus(j); + if (r < 0) + return r; + + print_entry(j, 0); + } else { + SD_JOURNAL_FOREACH(j) + print_entry(j, n_found++); + + if (!arg_field && n_found <= 0) { + log_notice("No coredumps found."); + return -ESRCH; + } + } + + return 0; +} + +static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { + const char *data; + _cleanup_free_ char *filename = NULL; + size_t len; + int r; + + assert((fd >= 0) != !!path); + assert(!!path == !!unlink_temp); + + /* Prefer uncompressed file to journal (probably cached) to + * compressed file (probably uncached). */ + r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m"); + else if (r == 0) + retrieve(data, len, "COREDUMP_FILENAME", &filename); + + if (filename && access(filename, R_OK) < 0) { + log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + "File %s is not readable: %m", filename); + filename = mfree(filename); + } + + if (filename && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) { + if (path) { + *path = filename; + filename = NULL; + } + + return 0; + } else { + _cleanup_close_ int fdt = -1; + char *temp = NULL; + + if (fd < 0) { + temp = strdup("/var/tmp/coredump-XXXXXX"); + if (!temp) + return log_oom(); + + fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); + if (fdt < 0) + return log_error_errno(fdt, "Failed to create temporary file: %m"); + log_debug("Created temporary file %s", temp); + + fd = fdt; + } + + r = sd_journal_get_data(j, "COREDUMP", (const void**) &data, &len); + if (r == 0) { + ssize_t sz; + + assert(len >= 9); + data += 9; + len -= 9; + + sz = write(fdt, data, len); + if (sz < 0) { + r = log_error_errno(errno, + "Failed to write temporary file: %m"); + goto error; + } + if (sz != (ssize_t) len) { + log_error("Short write to temporary file."); + r = -EIO; + goto error; + } + } else if (filename) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + _cleanup_close_ int fdf; + + fdf = open(filename, O_RDONLY | O_CLOEXEC); + if (fdf < 0) { + r = log_error_errno(errno, + "Failed to open %s: %m", + filename); + goto error; + } + + r = decompress_stream(filename, fdf, fd, -1); + if (r < 0) { + log_error_errno(r, "Failed to decompress %s: %m", filename); + goto error; + } +#else + log_error("Cannot decompress file. Compiled without compression support."); + r = -EOPNOTSUPP; + goto error; +#endif + } else { + if (r == -ENOENT) + log_error("Cannot retrieve coredump from journal or disk."); + else + log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); + goto error; + } + + if (temp) { + *path = temp; + *unlink_temp = true; + } + + return 0; + +error: + if (temp) { + unlink(temp); + log_debug("Removed temporary file %s", temp); + } + return r; + } +} + +static int dump_core(sd_journal* j) { + int r; + + assert(j); + + r = focus(j); + if (r < 0) + return r; + + print_info(arg_output ? stdout : stderr, j, false); + + if (on_tty() && !arg_output) { + log_error("Refusing to dump core to tty."); + return -ENOTTY; + } + + r = save_core(j, arg_output ? fileno(arg_output) : STDOUT_FILENO, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Coredump retrieval failed: %m"); + + r = sd_journal_previous(j); + if (r >= 0) + log_warning("More than one entry matches, ignoring rest."); + + return 0; +} + +static int run_gdb(sd_journal *j) { + _cleanup_free_ char *exe = NULL, *path = NULL; + bool unlink_path = false; + const char *data; + siginfo_t st; + size_t len; + pid_t pid; + int r; + + assert(j); + + r = focus(j); + if (r < 0) + return r; + + print_info(stdout, j, false); + fputs("\n", stdout); + + r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len); + if (r < 0) + return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m"); + + assert(len > strlen("COREDUMP_EXE=")); + data += strlen("COREDUMP_EXE="); + len -= strlen("COREDUMP_EXE="); + + exe = strndup(data, len); + if (!exe) + return log_oom(); + + if (endswith(exe, " (deleted)")) { + log_error("Binary already deleted."); + return -ENOENT; + } + + if (!path_is_absolute(exe)) { + log_error("Binary is not an absolute path."); + return -ENOENT; + } + + r = save_core(j, -1, &path, &unlink_path); + if (r < 0) + return log_error_errno(r, "Failed to retrieve core: %m"); + + pid = fork(); + if (pid < 0) { + r = log_error_errno(errno, "Failed to fork(): %m"); + goto finish; + } + if (pid == 0) { + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execlp("gdb", "gdb", exe, path, NULL); + + log_error_errno(errno, "Failed to invoke gdb: %m"); + _exit(1); + } + + r = wait_for_terminate(pid, &st); + if (r < 0) { + log_error_errno(r, "Failed to wait for gdb: %m"); + goto finish; + } + + r = st.si_code == CLD_EXITED ? st.si_status : 255; + +finish: + if (unlink_path) { + log_debug("Removed temporary file %s", path); + unlink(path); + } + + return r; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_journal_closep) sd_journal*j = NULL; + const char* match; + Iterator it; + int r = 0; + _cleanup_set_free_free_ Set *matches = NULL; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + matches = new_matches(); + if (!matches) { + r = -ENOMEM; + goto end; + } + + r = parse_argv(argc, argv, matches); + if (r < 0) + goto end; + + if (arg_action == ACTION_NONE) + goto end; + + sigbus_install(); + + if (arg_directory) { + r = sd_journal_open_directory(&j, arg_directory, 0); + if (r < 0) { + log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); + goto end; + } + } else { + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + log_error_errno(r, "Failed to open journal: %m"); + goto end; + } + } + + /* We want full data, nothing truncated. */ + sd_journal_set_data_threshold(j, 0); + + SET_FOREACH(match, matches, it) { + r = sd_journal_add_match(j, match, strlen(match)); + if (r != 0) { + log_error_errno(r, "Failed to add match '%s': %m", + match); + goto end; + } + } + + if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + _cleanup_free_ char *filter; + + filter = journal_make_match_string(j); + log_debug("Journal filter: %s", filter); + } + + switch(arg_action) { + + case ACTION_LIST: + case ACTION_INFO: + pager_open(arg_no_pager, false); + r = dump_list(j); + break; + + case ACTION_DUMP: + r = dump_core(j); + break; + + case ACTION_GDB: + r = run_gdb(j); + break; + + default: + assert_not_reached("Shouldn't be here"); + } + +end: + pager_close(); + + if (arg_output) + fclose(arg_output); + + return r >= 0 ? r : EXIT_FAILURE; +} diff --git a/src/grp-coredump/coredumpctl/coredumpctl.completion.bash b/src/grp-coredump/coredumpctl/coredumpctl.completion.bash new file mode 100644 index 0000000000..6091677506 --- /dev/null +++ b/src/grp-coredump/coredumpctl/coredumpctl.completion.bash @@ -0,0 +1,85 @@ +# coredumpctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__journal_fields=(MESSAGE{,_ID} PRIORITY CODE_{FILE,LINE,FUNC} + ERRNO SYSLOG_{FACILITY,IDENTIFIER,PID} COREDUMP_EXE + _{P,U,G}ID _COMM _EXE _CMDLINE + _AUDIT_{SESSION,LOGINUID} + _SYSTEMD_{CGROUP,SESSION,UNIT,OWNER_UID} + _SELINUX_CONTEXT _SOURCE_REALTIME_TIMESTAMP + _{BOOT,MACHINE}_ID _HOSTNAME _TRANSPORT + _KERNEL_{DEVICE,SUBSYSTEM} + _UDEV_{SYSNAME,DEVNODE,DEVLINK} + __CURSOR __{REALTIME,MONOTONIC}_TIMESTAMP) +_coredumpctl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --no-pager --no-legend -o --output -F --field -1' + + local -A VERBS=( + [LIST]='list' + [DUMP]='dump gdb' + ) + + if __contains_word "$prev" '--output -o'; then + comps=$( compgen -A file -- "$cur" ) + compopt -o filenames + elif __contains_word "$prev" '--FIELD -F'; then + comps=$( compgen -W '${__journal_fields[*]}' -- "$cur" ) + elif [[ $cur = -* ]]; then + comps=${OPTS} + elif __contains_word "$prev" ${VERBS[*]} && + ! __contains_word ${COMP_WORDS[COMP_CWORD-2]} '--output -o -F --field'; then + compopt -o nospace + COMPREPLY=( $(compgen -W '${__journal_fields[*]}' -S= -- "$cur") ) + return 0 + elif [[ $cur = *=* ]]; then + mapfile -t field_vals < <(coredumpctl -F "${prev%=}" 2>/dev/null) + COMPREPLY=( $(compgen -W '${field_vals[*]}' -- "${cur#=}") ) + return 0 + elif [[ $prev = '=' ]]; then + mapfile -t field_vals < <(coredumpctl -F "${COMP_WORDS[COMP_CWORD-2]}" 2>/dev/null) + comps=${field_vals[*]} + else + for ((i=0; i <= COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[LIST]} ${VERBS[DUMP]}; then + comps='' + fi + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _coredumpctl coredumpctl diff --git a/src/grp-coredump/coredumpctl/coredumpctl.completion.zsh b/src/grp-coredump/coredumpctl/coredumpctl.completion.zsh new file mode 100644 index 0000000000..e4c04a697f --- /dev/null +++ b/src/grp-coredump/coredumpctl/coredumpctl.completion.zsh @@ -0,0 +1,39 @@ +#compdef coredumpctl + +_coredumpctl_command(){ + local -a _coredumpctl_cmds + _coredumpctl_cmds=( + 'list:List available coredumps' + 'info:Show detailed information about one or more coredumps' + 'dump:Print coredump to stdout' + 'gdb:Start gdb on a coredump' + ) + if (( CURRENT == 1 )); then + _describe -t commands 'coredumpctl command' _coredumpctl_cmds + else + local curcontext="$curcontext" + local -a _dumps + cmd="${${_coredumpctl_cmds[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + # user can set zstyle ':completion:*:*:coredumpctl:*' sort no for coredumps to be ordered by date, otherwise they get ordered by pid + _dumps=( "${(foa)$(coredumpctl list --no-legend | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $5,$0}' 2>/dev/null)}" ) + if [[ -n "$_dumps" ]]; then + _describe -t pids 'coredumps' _dumps + else + _message "no coredumps" + fi + else + _message "no more options" + fi + fi +} + +_arguments \ + {-o+,--output=}'[Write output to FILE]:output file:_files' \ + {-F+,--field=}'[Show field in list output]:field' \ + '-1[Show information about most recent entry only]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-legend[Do not print the column headers]' \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '*::coredumpctl commands:_coredumpctl_command' diff --git a/src/grp-coredump/coredumpctl/coredumpctl.xml b/src/grp-coredump/coredumpctl/coredumpctl.xml new file mode 100644 index 0000000000..abc245be5e --- /dev/null +++ b/src/grp-coredump/coredumpctl/coredumpctl.xml @@ -0,0 +1,259 @@ + + + + + + + + + coredumpctl + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + coredumpctl + 1 + + + + coredumpctl + Retrieve and process saved core dumps and metadata + + + + + coredumpctl + OPTIONS + COMMAND + PID|COMM|EXE|MATCH + + + + + Description + + coredumpctl is a tool that can be used to retrieve and process core + dumps and metadata which were saved by + systemd-coredump8. + + + + + Options + + The following options are understood: + + + + + + + + + + Do not print column headers. + + + + + + + + Show information of a single core dump only, instead of listing + all known core dumps. + + + + FIELD + FIELD + + Print all possible data values the specified + field takes in matching core dump entries of the + journal. + + + + FILE + FILE + + Write the core to . + + + + + DIR + DIR + + Use the journal files in the specified . + + + + + + + + Commands + + The following commands are understood: + + + + list + + List core dumps captured in the journal + matching specified characteristics. If no command is + specified, this is the implied default. + + It's worth noting that different restrictions apply to + data saved in the journal and core dump files saved in + /var/lib/systemd/coredump, see overview in + systemd-coredump8. + Thus it may very well happen that a particular core dump is still listed + in the journal while its corresponding core dump file has already been + removed. + + + + info + + Show detailed information about core dumps + captured in the journal. + + + + dump + + Extract the last core dump matching specified + characteristics. The core dump will be written on standard + output, unless an output file is specified with + . + + + + gdb + + Invoke the GNU debugger on the last core dump + matching specified characteristics. + + + + + + + + Matching + + A match can be: + + + + PID + + Process ID of the + process that dumped + core. An integer. + + + + COMM + + Name of the executable (matches + ). Must not contain slashes. + + + + + EXE + + Path to the executable (matches + ). Must contain at least one + slash. + + + + MATCH + + General journalctl predicates (see + journalctl1). + Must contain an equal sign. + + + + + + Exit status + On success, 0 is returned; otherwise, a non-zero failure + code is returned. Not finding any matching core dumps is treated as + failure. + + + + + Examples + + + List all the core dumps of a program named foo + + # coredumpctl list foo + + + + Invoke gdb on the last core dump + + # coredumpctl gdb + + + + Show information about a process that dumped core, + matching by its PID 6654 + + # coredumpctl info 6654 + + + + Extract the last core dump of /usr/bin/bar to a file named + <filename noindex="true">bar.coredump</filename> + + # coredumpctl -o bar.coredump dump /usr/bin/bar + + + + + See Also + + systemd-coredump8, + coredump.conf5, + systemd-journald.service8, + gdb1 + + + + diff --git a/src/grp-coredump/systemd-coredump/50-coredump.sysctl.in b/src/grp-coredump/systemd-coredump/50-coredump.sysctl.in new file mode 100644 index 0000000000..5a25de4512 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/50-coredump.sysctl.in @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See sysctl.d(5) for the description of the files in this directory, +# and systemd-coredump(8) and core(5) for the explanation of the +# setting below. + +kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %e diff --git a/src/grp-coredump/systemd-coredump/Makefile b/src/grp-coredump/systemd-coredump/Makefile new file mode 100644 index 0000000000..08fc6d44df --- /dev/null +++ b/src/grp-coredump/systemd-coredump/Makefile @@ -0,0 +1,85 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_COREDUMP),) +systemd_coredump_SOURCES = \ + src/coredump/coredump.c \ + src/coredump/coredump-vacuum.c \ + src/coredump/coredump-vacuum.h + +systemd_coredump_CFLAGS = \ + $(ACL_CFLAGS) + +systemd_coredump_LDADD = \ + libsystemd-shared.la \ + $(ACL_LIBS) + +ifneq ($(HAVE_ELFUTILS),) +systemd_coredump_SOURCES += \ + src/coredump/stacktrace.c \ + src/coredump/stacktrace.h + +systemd_coredump_LDADD += \ + $(ELFUTILS_LIBS) +endif # HAVE_ELFUTILS + +nodist_systemunit_DATA += \ + units/systemd-coredump@.service + +dist_systemunit_DATA += \ + units/systemd-coredump.socket + +SOCKETS_TARGET_WANTS += \ + systemd-coredump.socket + +rootlibexec_PROGRAMS += \ + systemd-coredump + +dist_pkgsysconf_DATA += \ + src/coredump/coredump.conf + +manual_tests += \ + test-coredump-vacuum + +test_coredump_vacuum_SOURCES = \ + src/coredump/test-coredump-vacuum.c \ + src/coredump/coredump-vacuum.c \ + src/coredump/coredump-vacuum.h + +test_coredump_vacuum_LDADD = \ + libsystemd-shared.la + +nodist_sysctl_DATA = \ + sysctl.d/50-coredump.conf + +CLEANFILES += \ + sysctl.d/50-coredump.conf +endif # ENABLE_COREDUMP + +EXTRA_DIST += \ + sysctl.d/50-coredump.conf.in \ + units/systemd-coredump@.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-coredump/systemd-coredump/coredump-vacuum.c b/src/grp-coredump/systemd-coredump/coredump-vacuum.c new file mode 100644 index 0000000000..7fc6e85436 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/coredump-vacuum.c @@ -0,0 +1,269 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "coredump-vacuum.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 */ +#define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ +#define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */ + +struct vacuum_candidate { + unsigned n_files; + char *oldest_file; + usec_t oldest_mtime; +}; + +static void vacuum_candidate_free(struct vacuum_candidate *c) { + if (!c) + return; + + free(c->oldest_file); + free(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free); + +static void vacuum_candidate_hasmap_free(Hashmap *h) { + struct vacuum_candidate *c; + + while ((c = hashmap_steal_first(h))) + vacuum_candidate_free(c); + + hashmap_free(h); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hasmap_free); + +static int uid_from_file_name(const char *filename, uid_t *uid) { + const char *p, *e, *u; + + p = startswith(filename, "core."); + if (!p) + return -EINVAL; + + /* Skip the comm field */ + p = strchr(p, '.'); + if (!p) + return -EINVAL; + p++; + + /* Find end up UID */ + e = strchr(p, '.'); + if (!e) + return -EINVAL; + + u = strndupa(p, e-p); + return parse_uid(u, uid); +} + +static bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) { + uint64_t fs_size = 0, fs_free = (uint64_t) -1; + struct statvfs sv; + + assert(fd >= 0); + + if (fstatvfs(fd, &sv) >= 0) { + fs_size = sv.f_frsize * sv.f_blocks; + fs_free = sv.f_frsize * sv.f_bfree; + } + + if (max_use == (uint64_t) -1) { + + if (fs_size > 0) { + max_use = PAGE_ALIGN(fs_size / 10); /* 10% */ + + if (max_use > DEFAULT_MAX_USE_UPPER) + max_use = DEFAULT_MAX_USE_UPPER; + + if (max_use < DEFAULT_MAX_USE_LOWER) + max_use = DEFAULT_MAX_USE_LOWER; + } else + max_use = DEFAULT_MAX_USE_LOWER; + } else + max_use = PAGE_ALIGN(max_use); + + if (max_use > 0 && sum > max_use) + return true; + + if (keep_free == (uint64_t) -1) { + + if (fs_size > 0) { + keep_free = PAGE_ALIGN((fs_size * 3) / 20); /* 15% */ + + if (keep_free > DEFAULT_KEEP_FREE_UPPER) + keep_free = DEFAULT_KEEP_FREE_UPPER; + } else + keep_free = DEFAULT_KEEP_FREE; + } else + keep_free = PAGE_ALIGN(keep_free); + + if (keep_free > 0 && fs_free < keep_free) + return true; + + return false; +} + +int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { + _cleanup_closedir_ DIR *d = NULL; + struct stat exclude_st; + int r; + + if (keep_free == 0 && max_use == 0) + return 0; + + if (exclude_fd >= 0) { + if (fstat(exclude_fd, &exclude_st) < 0) + return log_error_errno(errno, "Failed to fstat(): %m"); + } + + /* This algorithm will keep deleting the oldest file of the + * user with the most coredumps until we are back in the size + * limits. Note that vacuuming for journal files is different, + * because we rely on rate-limiting of the messages there, + * to avoid being flooded. */ + + d = opendir("/var/lib/systemd/coredump"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Can't open coredump directory: %m"); + } + + for (;;) { + _cleanup_(vacuum_candidate_hasmap_freep) Hashmap *h = NULL; + struct vacuum_candidate *worst = NULL; + struct dirent *de; + uint64_t sum = 0; + + rewinddir(d); + + FOREACH_DIRENT(de, d, goto fail) { + struct vacuum_candidate *c; + struct stat st; + uid_t uid; + usec_t t; + + r = uid_from_file_name(de->d_name, &uid); + if (r < 0) + continue; + + if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW) < 0) { + if (errno == ENOENT) + continue; + + log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name); + continue; + } + + if (!S_ISREG(st.st_mode)) + continue; + + if (exclude_fd >= 0 && + exclude_st.st_dev == st.st_dev && + exclude_st.st_ino == st.st_ino) + continue; + + r = hashmap_ensure_allocated(&h, NULL); + if (r < 0) + return log_oom(); + + t = timespec_load(&st.st_mtim); + + c = hashmap_get(h, UID_TO_PTR(uid)); + if (c) { + + if (t < c->oldest_mtime) { + char *n; + + n = strdup(de->d_name); + if (!n) + return log_oom(); + + free(c->oldest_file); + c->oldest_file = n; + c->oldest_mtime = t; + } + + } else { + _cleanup_(vacuum_candidate_freep) struct vacuum_candidate *n = NULL; + + n = new0(struct vacuum_candidate, 1); + if (!n) + return log_oom(); + + n->oldest_file = strdup(de->d_name); + if (!n->oldest_file) + return log_oom(); + + n->oldest_mtime = t; + + r = hashmap_put(h, UID_TO_PTR(uid), n); + if (r < 0) + return log_oom(); + + c = n; + n = NULL; + } + + c->n_files++; + + if (!worst || + worst->n_files < c->n_files || + (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime)) + worst = c; + + sum += st.st_blocks * 512; + } + + if (!worst) + break; + + r = vacuum_necessary(dirfd(d), sum, keep_free, max_use); + if (r <= 0) + return r; + + if (unlinkat(dirfd(d), worst->oldest_file, 0) < 0) { + + if (errno == ENOENT) + continue; + + return log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file); + } else + log_info("Removed old coredump %s.", worst->oldest_file); + } + + return 0; + +fail: + return log_error_errno(errno, "Failed to read directory: %m"); +} diff --git a/src/grp-coredump/systemd-coredump/coredump-vacuum.h b/src/grp-coredump/systemd-coredump/coredump-vacuum.h new file mode 100644 index 0000000000..4b7b9f2d98 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/coredump-vacuum.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use); diff --git a/src/grp-coredump/systemd-coredump/coredump.c b/src/grp-coredump/systemd-coredump/coredump.c new file mode 100644 index 0000000000..dd05326ce0 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/coredump.c @@ -0,0 +1,1186 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_ELFUTILS +#include +#include +#endif + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/cgroup-util.h" +#include "basic/copy.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/socket-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "journald-native.h" +#include "sd-journal/compress.h" +#include "shared/acl-util.h" +#include "shared/conf-parser.h" + +#include "coredump-vacuum.h" +#include "stacktrace.h" + +/* The maximum size up to which we process coredumps */ +#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU)) + +/* The maximum size up to which we leave the coredump around on disk */ +#define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX + +/* The maximum size up to which we store the coredump in the journal */ +#define JOURNAL_SIZE_MAX ((size_t) (767LU*1024LU*1024LU)) + +/* Make sure to not make this larger than the maximum journal entry + * size. See DATA_SIZE_MAX in journald-native.c. */ +assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX); + +enum { + /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and + * attaching xattrs */ + CONTEXT_PID, + CONTEXT_UID, + CONTEXT_GID, + CONTEXT_SIGNAL, + CONTEXT_TIMESTAMP, + CONTEXT_RLIMIT, + CONTEXT_COMM, + CONTEXT_EXE, + _CONTEXT_MAX +}; + +typedef enum CoredumpStorage { + COREDUMP_STORAGE_NONE, + COREDUMP_STORAGE_EXTERNAL, + COREDUMP_STORAGE_JOURNAL, + COREDUMP_STORAGE_BOTH, + _COREDUMP_STORAGE_MAX, + _COREDUMP_STORAGE_INVALID = -1 +} CoredumpStorage; + +static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = { + [COREDUMP_STORAGE_NONE] = "none", + [COREDUMP_STORAGE_EXTERNAL] = "external", + [COREDUMP_STORAGE_JOURNAL] = "journal", + [COREDUMP_STORAGE_BOTH] = "both", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage); +static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, CoredumpStorage, "Failed to parse storage setting"); + +static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL; +static bool arg_compress = true; +static uint64_t arg_process_size_max = PROCESS_SIZE_MAX; +static uint64_t arg_external_size_max = EXTERNAL_SIZE_MAX; +static size_t arg_journal_size_max = JOURNAL_SIZE_MAX; +static uint64_t arg_keep_free = (uint64_t) -1; +static uint64_t arg_max_use = (uint64_t) -1; + +static int parse_config(void) { + static const ConfigTableItem items[] = { + { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, + { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, + { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max }, + { "Coredump", "ExternalSizeMax", config_parse_iec_uint64, 0, &arg_external_size_max }, + { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, + { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free }, + { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use }, + {} + }; + + return config_parse_many(PKGSYSCONFDIR "/coredump.conf", + CONF_PATHS_NULSTR("systemd/coredump.conf.d"), + "Coredump\0", + config_item_table_lookup, items, + false, NULL); +} + +static int fix_acl(int fd, uid_t uid) { + +#ifdef HAVE_ACL + _cleanup_(acl_freep) acl_t acl = NULL; + acl_entry_t entry; + acl_permset_t permset; + int r; + + assert(fd >= 0); + + if (uid <= SYSTEM_UID_MAX) + return 0; + + /* Make sure normal users can read (but not write or delete) + * their own coredumps */ + + acl = acl_get_fd(fd); + if (!acl) + return log_error_errno(errno, "Failed to get ACL: %m"); + + if (acl_create_entry(&acl, &entry) < 0 || + acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &uid) < 0) + return log_error_errno(errno, "Failed to patch ACL: %m"); + + if (acl_get_permset(entry, &permset) < 0 || + 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"); +#endif + + return 0; +} + +static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) { + + static const char * const xattrs[_CONTEXT_MAX] = { + [CONTEXT_PID] = "user.coredump.pid", + [CONTEXT_UID] = "user.coredump.uid", + [CONTEXT_GID] = "user.coredump.gid", + [CONTEXT_SIGNAL] = "user.coredump.signal", + [CONTEXT_TIMESTAMP] = "user.coredump.timestamp", + [CONTEXT_COMM] = "user.coredump.comm", + [CONTEXT_EXE] = "user.coredump.exe", + }; + + int r = 0; + unsigned i; + + assert(fd >= 0); + + /* Attach some metadata to coredumps via extended + * attributes. Just because we can. */ + + for (i = 0; i < _CONTEXT_MAX; i++) { + int k; + + if (isempty(context[i]) || !xattrs[i]) + continue; + + k = fsetxattr(fd, xattrs[i], context[i], strlen(context[i]), XATTR_CREATE); + if (k < 0 && r == 0) + r = -errno; + } + + return r; +} + +#define filename_escape(s) xescape((s), "./ ") + +static inline const char *coredump_tmpfile_name(const char *s) { + return s ? s : "(unnamed temporary file)"; +} + +static int fix_permissions( + int fd, + const char *filename, + const char *target, + const char *context[_CONTEXT_MAX], + uid_t uid) { + + int r; + + assert(fd >= 0); + assert(target); + assert(context); + + /* Ignore errors on these */ + (void) fchmod(fd, 0640); + (void) fix_acl(fd, uid); + (void) fix_xattr(fd, context); + + if (fsync(fd) < 0) + return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); + + r = link_tmpfile(fd, filename, target); + if (r < 0) + return log_error_errno(r, "Failed to move coredump %s into place: %m", target); + + return 0; +} + +static int maybe_remove_external_coredump(const char *filename, uint64_t size) { + + /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */ + + if (IN_SET(arg_storage, COREDUMP_STORAGE_EXTERNAL, COREDUMP_STORAGE_BOTH) && + size <= arg_external_size_max) + return 0; + + if (!filename) + return 1; + + if (unlink(filename) < 0 && errno != ENOENT) + return log_error_errno(errno, "Failed to unlink %s: %m", filename); + + return 1; +} + +static int make_filename(const char *context[_CONTEXT_MAX], char **ret) { + _cleanup_free_ char *c = NULL, *u = NULL, *p = NULL, *t = NULL; + sd_id128_t boot = {}; + int r; + + assert(context); + + c = filename_escape(context[CONTEXT_COMM]); + if (!c) + return -ENOMEM; + + u = filename_escape(context[CONTEXT_UID]); + if (!u) + return -ENOMEM; + + r = sd_id128_get_boot(&boot); + if (r < 0) + return r; + + p = filename_escape(context[CONTEXT_PID]); + if (!p) + return -ENOMEM; + + t = filename_escape(context[CONTEXT_TIMESTAMP]); + if (!t) + return -ENOMEM; + + if (asprintf(ret, + "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000", + c, + u, + SD_ID128_FORMAT_VAL(boot), + p, + t) < 0) + return -ENOMEM; + + return 0; +} + +static int save_external_coredump( + const char *context[_CONTEXT_MAX], + int input_fd, + char **ret_filename, + int *ret_node_fd, + int *ret_data_fd, + uint64_t *ret_size) { + + _cleanup_free_ char *fn = NULL, *tmp = NULL; + _cleanup_close_ int fd = -1; + uint64_t rlimit, max_size; + struct stat st; + uid_t uid; + int r; + + assert(context); + assert(ret_filename); + assert(ret_node_fd); + assert(ret_data_fd); + assert(ret_size); + + r = parse_uid(context[CONTEXT_UID], &uid); + if (r < 0) + return log_error_errno(r, "Failed to parse UID: %m"); + + r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]); + if (rlimit <= 0) { + /* Is coredumping disabled? Then don't bother saving/processing the coredump */ + log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]); + return -EBADSLT; + } + + /* Never store more than the process configured, or than we actually shall keep or process */ + max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max)); + + r = make_filename(context, &fn); + if (r < 0) + return log_error_errno(r, "Failed to determine coredump file name: %m"); + + mkdir_p_label("/var/lib/systemd/coredump", 0755); + + fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); + if (fd < 0) + return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); + + r = copy_bytes(input_fd, fd, max_size, false); + if (r == -EFBIG) { + log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); + goto fail; + } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { + log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]); + goto fail; + } else if (r < 0) { + log_error_errno(r, "Failed to dump coredump to file: %m"); + goto fail; + } + + if (fstat(fd, &st) < 0) { + log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); + goto fail; + } + + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { + log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); + goto fail; + } + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + /* If we will remove the coredump anyway, do not compress. */ + if (maybe_remove_external_coredump(NULL, st.st_size) == 0 + && arg_compress) { + + _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL; + _cleanup_close_ int fd_compressed = -1; + + fn_compressed = strappend(fn, COMPRESSED_EXT); + if (!fn_compressed) { + log_oom(); + goto uncompressed; + } + + fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); + if (fd_compressed < 0) { + log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); + goto uncompressed; + } + + r = compress_stream(fd, fd_compressed, -1); + if (r < 0) { + log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); + goto fail_compressed; + } + + r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); + if (r < 0) + goto fail_compressed; + + /* OK, this worked, we can get rid of the uncompressed version now */ + if (tmp) + unlink_noerrno(tmp); + + *ret_filename = fn_compressed; /* compressed */ + *ret_node_fd = fd_compressed; /* compressed */ + *ret_data_fd = fd; /* uncompressed */ + *ret_size = (uint64_t) st.st_size; /* uncompressed */ + + fn_compressed = NULL; + fd = fd_compressed = -1; + + return 0; + + fail_compressed: + if (tmp_compressed) + (void) unlink(tmp_compressed); + } + +uncompressed: +#endif + + r = fix_permissions(fd, tmp, fn, context, uid); + if (r < 0) + goto fail; + + *ret_filename = fn; + *ret_data_fd = fd; + *ret_node_fd = -1; + *ret_size = (uint64_t) st.st_size; + + fn = NULL; + fd = -1; + + return 0; + +fail: + if (tmp) + (void) unlink(tmp); + return r; +} + +static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) { + _cleanup_free_ char *field = NULL; + ssize_t n; + + assert(fd >= 0); + assert(ret); + assert(ret_size); + + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) + return log_warning_errno(errno, "Failed to seek: %m"); + + field = malloc(9 + size); + if (!field) { + log_warning("Failed to allocate memory for coredump, coredump will not be stored."); + return -ENOMEM; + } + + memcpy(field, "COREDUMP=", 9); + + n = read(fd, field + 9, size); + if (n < 0) + return log_error_errno((int) n, "Failed to read core data: %m"); + if ((size_t) n < size) { + log_error("Core data too short."); + return -EIO; + } + + *ret = field; + *ret_size = size + 9; + + field = NULL; + + return 0; +} + +/* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines: + * 0:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * + * 1:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * + * 2:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * EOF + */ +static int compose_open_fds(pid_t pid, char **open_fds) { + _cleanup_closedir_ DIR *proc_fd_dir = NULL; + _cleanup_close_ int proc_fdinfo_fd = -1; + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *stream = NULL; + const char *fddelim = "", *path; + struct dirent *dent = NULL; + size_t size = 0; + int r = 0; + + assert(pid >= 0); + assert(open_fds != NULL); + + path = procfs_file_alloca(pid, "fd"); + proc_fd_dir = opendir(path); + if (!proc_fd_dir) + return -errno; + + proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (proc_fdinfo_fd < 0) + return -errno; + + stream = open_memstream(&buffer, &size); + if (!stream) + return -ENOMEM; + + FOREACH_DIRENT(dent, proc_fd_dir, return -errno) { + _cleanup_fclose_ FILE *fdinfo = NULL; + _cleanup_free_ char *fdname = NULL; + char line[LINE_MAX]; + int fd; + + r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname); + if (r < 0) + return r; + + fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname); + fddelim = "\n"; + + /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */ + fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY); + if (fd < 0) + continue; + + fdinfo = fdopen(fd, "re"); + if (fdinfo == NULL) { + close(fd); + continue; + } + + FOREACH_LINE(line, fdinfo, break) { + fputs(line, stream); + if (!endswith(line, "\n")) + fputc('\n', stream); + } + } + + errno = 0; + stream = safe_fclose(stream); + + if (errno > 0) + return -errno; + + *open_fds = buffer; + buffer = NULL; + + return 0; +} + +static int change_uid_gid(const char *context[]) { + uid_t uid; + gid_t gid; + int r; + + r = parse_uid(context[CONTEXT_UID], &uid); + if (r < 0) + return r; + + if (uid <= SYSTEM_UID_MAX) { + const char *user = "systemd-coredump"; + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user); + uid = gid = 0; + } + } else { + r = parse_gid(context[CONTEXT_GID], &gid); + if (r < 0) + return r; + } + + return drop_privileges(uid, gid, 0); +} + +static int submit_coredump( + const char *context[_CONTEXT_MAX], + struct iovec *iovec, + size_t n_iovec_allocated, + size_t n_iovec, + int input_fd) { + + _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; + _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; + uint64_t coredump_size; + int r; + + assert(context); + assert(iovec); + assert(n_iovec_allocated >= n_iovec + 3); + assert(input_fd >= 0); + + /* Vacuum before we write anything again */ + (void) coredump_vacuum(-1, arg_keep_free, arg_max_use); + + /* Always stream the coredump to disk, if that's possible */ + r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); + if (r < 0) + /* Skip whole core dumping part */ + goto log; + + /* If we don't want to keep the coredump on disk, remove it now, as later on we will lack the privileges for + * it. However, we keep the fd to it, so that we can still process it and log it. */ + r = maybe_remove_external_coredump(filename, coredump_size); + if (r < 0) + return r; + if (r == 0) { + const char *coredump_filename; + + coredump_filename = strjoina("COREDUMP_FILENAME=", filename); + IOVEC_SET_STRING(iovec[n_iovec++], coredump_filename); + } + + /* Vacuum again, but exclude the coredump we just created */ + (void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use); + + /* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the coredump + * memory under the user's uid. This also ensures that the credentials journald will see are the ones of the + * coredumping user, thus making sure the user gets access to the core dump. Let's also get rid of all + * capabilities, if we run as root, we won't need them anymore. */ + r = change_uid_gid(context); + if (r < 0) + return log_error_errno(r, "Failed to drop privileges: %m"); + +#ifdef HAVE_ELFUTILS + /* Try to get a strack trace if we can */ + if (coredump_size <= arg_process_size_max) { + _cleanup_free_ char *stacktrace = NULL; + + r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace); + if (r >= 0) + core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.\n\n", stacktrace, NULL); + else if (r == -EINVAL) + log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); + else + log_warning_errno(r, "Failed to generate stack trace: %m"); + } + + if (!core_message) +#endif +log: + core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.", NULL); + if (core_message) + IOVEC_SET_STRING(iovec[n_iovec++], core_message); + + /* Optionally store the entire coredump in the journal */ + if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && + coredump_size <= arg_journal_size_max) { + size_t sz = 0; + + /* Store the coredump itself in the journal */ + + r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz); + if (r >= 0) { + iovec[n_iovec].iov_base = coredump_data; + iovec[n_iovec].iov_len = sz; + n_iovec++; + } + } + + assert(n_iovec <= n_iovec_allocated); + + r = sd_journal_sendv(iovec, n_iovec); + if (r < 0) + return log_error_errno(r, "Failed to log coredump: %m"); + + return 0; +} + +static void map_context_fields(const struct iovec *iovec, const char *context[]) { + + static const char * const context_field_names[_CONTEXT_MAX] = { + [CONTEXT_PID] = "COREDUMP_PID=", + [CONTEXT_UID] = "COREDUMP_UID=", + [CONTEXT_GID] = "COREDUMP_GID=", + [CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=", + [CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=", + [CONTEXT_COMM] = "COREDUMP_COMM=", + [CONTEXT_EXE] = "COREDUMP_EXE=", + [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", + }; + + unsigned i; + + assert(iovec); + assert(context); + + for (i = 0; i < _CONTEXT_MAX; i++) { + size_t l; + + l = strlen(context_field_names[i]); + if (iovec->iov_len < l) + continue; + + if (memcmp(iovec->iov_base, context_field_names[i], l) != 0) + continue; + + /* Note that these strings are NUL terminated, because we made sure that a trailing NUL byte is in the + * buffer, though not included in the iov_len count. (see below) */ + context[i] = (char*) iovec->iov_base + l; + break; + } +} + +static int process_socket(int fd) { + _cleanup_close_ int coredump_fd = -1; + struct iovec *iovec = NULL; + size_t n_iovec = 0, n_iovec_allocated = 0, i; + const char *context[_CONTEXT_MAX] = {}; + int r; + + assert(fd >= 0); + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + for (;;) { + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iovlen = 1, + }; + ssize_t n; + ssize_t l; + + if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + r = log_oom(); + goto finish; + } + + l = next_datagram_size_fd(fd); + if (l < 0) { + r = log_error_errno(l, "Failed to determine datagram size to read: %m"); + goto finish; + } + + assert(l >= 0); + + iovec[n_iovec].iov_len = l; + iovec[n_iovec].iov_base = malloc(l + 1); + if (!iovec[n_iovec].iov_base) { + r = log_oom(); + goto finish; + } + + mh.msg_iov = iovec + n_iovec; + + n = recvmsg(fd, &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (n < 0) { + free(iovec[n_iovec].iov_base); + r = log_error_errno(errno, "Failed to receive datagram: %m"); + goto finish; + } + + if (n == 0) { + struct cmsghdr *cmsg, *found = NULL; + /* The final zero-length datagram carries the file descriptor and tells us that we're done. */ + + free(iovec[n_iovec].iov_base); + + 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; + } + } + + if (!found) { + log_error("Coredump file descriptor missing."); + r = -EBADMSG; + goto finish; + } + + assert(coredump_fd < 0); + coredump_fd = *(int*) CMSG_DATA(found); + break; + } + + /* Add trailing NUL byte, in case these are strings */ + ((char*) iovec[n_iovec].iov_base)[n] = 0; + iovec[n_iovec].iov_len = (size_t) n; + + cmsg_close_all(&mh); + map_context_fields(iovec + n_iovec, context); + n_iovec++; + } + + if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + r = log_oom(); + goto finish; + } + + /* Make sure we got all data we really need */ + assert(context[CONTEXT_PID]); + assert(context[CONTEXT_UID]); + assert(context[CONTEXT_GID]); + assert(context[CONTEXT_SIGNAL]); + assert(context[CONTEXT_TIMESTAMP]); + assert(context[CONTEXT_RLIMIT]); + assert(context[CONTEXT_COMM]); + assert(coredump_fd >= 0); + + r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd); + +finish: + for (i = 0; i < n_iovec; i++) + free(iovec[i].iov_base); + free(iovec); + + return r; +} + +static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/coredump", + }; + _cleanup_close_ int fd = -1; + size_t i; + int r; + + assert(iovec || n_iovec <= 0); + assert(input_fd >= 0); + + fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to create coredump socket: %m"); + + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return log_error_errno(errno, "Failed to connect to coredump service: %m"); + + for (i = 0; i < n_iovec; i++) { + struct msghdr mh = { + .msg_iov = (struct iovec*) iovec + i, + .msg_iovlen = 1, + }; + struct iovec copy[2]; + + for (;;) { + if (sendmsg(fd, &mh, MSG_NOSIGNAL) >= 0) + break; + + if (errno == EMSGSIZE && mh.msg_iov[0].iov_len > 0) { + /* This field didn't fit? That's a pity. Given that this is just metadata, + * let's truncate the field at half, and try again. We append three dots, in + * order to show that this is truncated. */ + + if (mh.msg_iov != copy) { + /* We don't want to modify the caller's iovec, hence let's create our + * own array, consisting of two new iovecs, where the first is a + * (truncated) copy of what we want to send, and the second one + * contains the trailing dots. */ + copy[0] = iovec[i]; + copy[1] = (struct iovec) { + .iov_base = (char[]) { '.', '.', '.' }, + .iov_len = 3, + }; + + mh.msg_iov = copy; + mh.msg_iovlen = 2; + } + + copy[0].iov_len /= 2; /* halve it, and try again */ + continue; + } + + return log_error_errno(errno, "Failed to send coredump datagram: %m"); + } + } + + r = send_one_fd(fd, input_fd, 0); + if (r < 0) + return log_error_errno(r, "Failed to send coredump fd: %m"); + + return 0; +} + +static int process_special_crash(const char *context[], int input_fd) { + _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; + _cleanup_free_ char *filename = NULL; + uint64_t coredump_size; + int r; + + assert(context); + assert(input_fd >= 0); + + /* If we are pid1 or journald, we cut things short, don't write to the journal, but still create a coredump. */ + + if (arg_storage != COREDUMP_STORAGE_NONE) + arg_storage = COREDUMP_STORAGE_EXTERNAL; + + r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); + if (r < 0) + return r; + + r = maybe_remove_external_coredump(filename, coredump_size); + if (r < 0) + return r; + + log_notice("Detected coredump of the journal daemon or PID 1, diverted to %s.", filename); + + return 0; +} + +static int process_kernel(int argc, char* argv[]) { + + /* The small core field we allocate on the stack, to keep things simple */ + char + *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL, + *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL, + *core_user_unit = NULL, *core_slice = NULL, *core_timestamp = NULL, *core_rlimit = NULL; + + /* The larger ones we allocate on the heap */ + _cleanup_free_ char + *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, + *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL; + + _cleanup_free_ char *exe = NULL, *comm = NULL; + const char *context[_CONTEXT_MAX]; + struct iovec iovec[25]; + size_t n_iovec = 0; + uid_t owner_uid; + const char *p; + pid_t pid; + char *t; + int r; + + if (argc < CONTEXT_COMM + 1) { + log_error("Not enough arguments passed from kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1); + return -EINVAL; + } + + r = parse_pid(argv[CONTEXT_PID + 1], &pid); + if (r < 0) + return log_error_errno(r, "Failed to parse PID."); + + r = get_process_comm(pid, &comm); + if (r < 0) { + log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m"); + comm = strv_join(argv + CONTEXT_COMM + 1, " "); + if (!comm) + return log_oom(); + } + + r = get_process_exe(pid, &exe); + if (r < 0) + log_warning_errno(r, "Failed to get EXE, ignoring: %m"); + + context[CONTEXT_PID] = argv[CONTEXT_PID + 1]; + context[CONTEXT_UID] = argv[CONTEXT_UID + 1]; + context[CONTEXT_GID] = argv[CONTEXT_GID + 1]; + context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1]; + context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1]; + context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1]; + context[CONTEXT_COMM] = comm; + context[CONTEXT_EXE] = exe; + + if (cg_pid_get_unit(pid, &t) >= 0) { + + /* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */ + if (streq(t, SPECIAL_INIT_SCOPE)) { + log_notice("Due to PID 1 having crashed coredump collection will now be turned off."); + (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + } + + /* Let's avoid dead-locks when processing journald and init crashes, as socket activation and logging + * are unlikely to work then. */ + if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) { + free(t); + return process_special_crash(context, STDIN_FILENO); + } + + core_unit = strjoina("COREDUMP_UNIT=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_unit); + } + + /* OK, now we know it's not the journal, hence we can make use of it now. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + + if (cg_pid_get_user_unit(pid, &t) >= 0) { + core_user_unit = strjoina("COREDUMP_USER_UNIT=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_user_unit); + } + + core_pid = strjoina("COREDUMP_PID=", context[CONTEXT_PID]); + IOVEC_SET_STRING(iovec[n_iovec++], core_pid); + + core_uid = strjoina("COREDUMP_UID=", context[CONTEXT_UID]); + IOVEC_SET_STRING(iovec[n_iovec++], core_uid); + + core_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]); + IOVEC_SET_STRING(iovec[n_iovec++], core_gid); + + core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]); + IOVEC_SET_STRING(iovec[n_iovec++], core_signal); + + core_rlimit = strjoina("COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]); + IOVEC_SET_STRING(iovec[n_iovec++], core_rlimit); + + if (sd_pid_get_session(pid, &t) >= 0) { + core_session = strjoina("COREDUMP_SESSION=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_session); + } + + if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) { + r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid); + if (r > 0) + IOVEC_SET_STRING(iovec[n_iovec++], core_owner_uid); + } + + if (sd_pid_get_slice(pid, &t) >= 0) { + core_slice = strjoina("COREDUMP_SLICE=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_slice); + } + + if (comm) { + core_comm = strjoina("COREDUMP_COMM=", comm); + IOVEC_SET_STRING(iovec[n_iovec++], core_comm); + } + + if (exe) { + core_exe = strjoina("COREDUMP_EXE=", exe); + IOVEC_SET_STRING(iovec[n_iovec++], core_exe); + } + + if (get_process_cmdline(pid, 0, false, &t) >= 0) { + core_cmdline = strjoina("COREDUMP_CMDLINE=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_cmdline); + } + + if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) { + core_cgroup = strjoina("COREDUMP_CGROUP=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_cgroup); + } + + if (compose_open_fds(pid, &t) >= 0) { + core_open_fds = strappend("COREDUMP_OPEN_FDS=", t); + free(t); + + if (core_open_fds) + IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds); + } + + p = procfs_file_alloca(pid, "status"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_status = strappend("COREDUMP_PROC_STATUS=", t); + free(t); + + if (core_proc_status) + IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status); + } + + p = procfs_file_alloca(pid, "maps"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t); + free(t); + + if (core_proc_maps) + IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps); + } + + p = procfs_file_alloca(pid, "limits"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t); + free(t); + + if (core_proc_limits) + IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits); + } + + p = procfs_file_alloca(pid, "cgroup"); + if (read_full_file(p, &t, NULL) >=0) { + core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t); + free(t); + + if (core_proc_cgroup) + IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup); + } + + if (get_process_cwd(pid, &t) >= 0) { + core_cwd = strjoina("COREDUMP_CWD=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_cwd); + } + + if (get_process_root(pid, &t) >= 0) { + core_root = strjoina("COREDUMP_ROOT=", t); + free(t); + + IOVEC_SET_STRING(iovec[n_iovec++], core_root); + } + + if (get_process_environ(pid, &t) >= 0) { + core_environ = strappend("COREDUMP_ENVIRON=", t); + free(t); + + if (core_environ) + IOVEC_SET_STRING(iovec[n_iovec++], core_environ); + } + + core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000"); + IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp); + + IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); + + assert_cc(2 == LOG_CRIT); + IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2"); + + assert(n_iovec <= ELEMENTSOF(iovec)); + + return send_iovec(iovec, n_iovec, STDIN_FILENO); +} + +int main(int argc, char *argv[]) { + int r; + + /* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not + * log to then. */ + + log_set_target(LOG_TARGET_KMSG); + log_open(); + + /* Make sure we never enter a loop */ + (void) prctl(PR_SET_DUMPABLE, 0); + + /* Ignore all parse errors */ + (void) parse_config(); + + log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage)); + log_debug("Selected compression %s.", yes_no(arg_compress)); + + r = sd_listen_fds(false); + if (r < 0) { + log_error_errno(r, "Failed to determine number of file descriptor: %m"); + goto finish; + } + + /* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as + * coredump handler */ + if (r == 0) + r = process_kernel(argc, argv); + else if (r == 1) + r = process_socket(SD_LISTEN_FDS_START); + else { + log_error("Received unexpected number of file descriptors."); + r = -EINVAL; + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-coredump/systemd-coredump/coredump.conf b/src/grp-coredump/systemd-coredump/coredump.conf new file mode 100644 index 0000000000..c2f0643e03 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/coredump.conf @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See coredump.conf(5) for details. + +[Coredump] +#Storage=external +#Compress=yes +#ProcessSizeMax=2G +#ExternalSizeMax=2G +#JournalSizeMax=767M +#MaxUse= +#KeepFree= diff --git a/src/grp-coredump/systemd-coredump/coredump.conf.xml b/src/grp-coredump/systemd-coredump/coredump.conf.xml new file mode 100644 index 0000000000..4f95680a3a --- /dev/null +++ b/src/grp-coredump/systemd-coredump/coredump.conf.xml @@ -0,0 +1,161 @@ + + + + + + + + coredump.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + coredump.conf + 5 + + + + coredump.conf + coredump.conf.d + Core dump storage configuration files + + + + /etc/systemd/coredump.conf + /etc/systemd/coredump.conf.d/*.conf + /run/systemd/coredump.conf.d/*.conf + /usr/lib/systemd/coredump.conf.d/*.conf + + + + Description + + These files configure the behavior of + systemd-coredump8, + a handler for core dumps invoked by the kernel. Whether systemd-coredump is used + is determined by the kernel's + kernel.core_pattern sysctl8 + setting. See + systemd-coredump8 + and + core5 + pages for the details. + + + + + + Options + + All options are configured in the + [Coredump] section: + + + + + Storage= + + Controls where to store cores. One of + none, external, + journal, and both. When + none, the core dumps will be logged but not + stored permanently. When external (the + default), cores will be stored in /var/lib/systemd/coredump. + When journal, cores will be stored in + the journal and rotated following normal journal + rotation patterns. When both, cores + will be stored in both locations. + + When cores are stored in the journal, they might be + compressed following journal compression settings, see + journald.conf5. + When cores are stored externally, they will be compressed + by default, see below. + + + + Compress= + + Controls compression for external + storage. Takes a boolean argument, which defaults to + yes. + + + + + ProcessSizeMax= + + The maximum size in bytes of a core + which will be processed. Core dumps exceeding this size + will be logged, but the backtrace will not be generated + and the core will not be stored. + + + + ExternalSizeMax= + JournalSizeMax= + + The maximum (uncompressed) size in bytes of a + core to be saved. + + + + MaxUse= + KeepFree= + + Enforce limits on the disk space taken up by + externally stored core dumps. makes + sure that old core dumps are removed as soon as the total disk + space taken up by core dumps grows beyond this limit (defaults + to 10% of the total disk size). + controls how much disk space to keep free at least (defaults + to 15% of the total disk size). Note that the disk space used + by core dumps might temporarily exceed these limits while + core dumps are processed. Note that old core dumps are also + removed based on time via + systemd-tmpfiles8. Set + either value to 0 to turn off size-based + clean-up. + + + + + + + See Also + + systemd-journald.service8, + coredumpctl1, + systemd-tmpfiles8 + + + + diff --git a/src/grp-coredump/systemd-coredump/stacktrace.c b/src/grp-coredump/systemd-coredump/stacktrace.c new file mode 100644 index 0000000000..e51b2986e6 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/stacktrace.c @@ -0,0 +1,201 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "stacktrace.h" + +#define FRAMES_MAX 64 +#define THREADS_MAX 64 + +struct stack_context { + FILE *f; + Dwfl *dwfl; + Elf *elf; + unsigned n_thread; + unsigned n_frame; +}; + +static int frame_callback(Dwfl_Frame *frame, void *userdata) { + struct stack_context *c = userdata; + Dwarf_Addr pc, pc_adjusted, bias = 0; + _cleanup_free_ Dwarf_Die *scopes = NULL; + const char *fname = NULL, *symbol = NULL; + Dwfl_Module *module; + bool is_activation; + + assert(frame); + assert(c); + + if (c->n_frame >= FRAMES_MAX) + return DWARF_CB_ABORT; + + if (!dwfl_frame_pc(frame, &pc, &is_activation)) + return DWARF_CB_ABORT; + + pc_adjusted = pc - (is_activation ? 0 : 1); + + module = dwfl_addrmodule(c->dwfl, pc_adjusted); + if (module) { + Dwarf_Die *s, *cudie; + int n; + + cudie = dwfl_module_addrdie(module, pc_adjusted, &bias); + if (cudie) { + n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes); + for (s = scopes; s < scopes + n; s++) { + if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) { + Dwarf_Attribute *a, space; + + a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space); + if (!a) + a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space); + if (a) + symbol = dwarf_formstring(a); + if (!symbol) + symbol = dwarf_diename(s); + + if (symbol) + break; + } + } + } + + if (!symbol) + symbol = dwfl_module_addrname(module, pc_adjusted); + + fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + + fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname)); + c->n_frame++; + + return DWARF_CB_OK; +} + +static int thread_callback(Dwfl_Thread *thread, void *userdata) { + struct stack_context *c = userdata; + pid_t tid; + + assert(thread); + assert(c); + + if (c->n_thread >= THREADS_MAX) + return DWARF_CB_ABORT; + + if (c->n_thread != 0) + fputc('\n', c->f); + + c->n_frame = 0; + + tid = dwfl_thread_tid(thread); + fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid); + + if (dwfl_thread_getframes(thread, frame_callback, c) < 0) + return DWARF_CB_ABORT; + + c->n_thread++; + + return DWARF_CB_OK; +} + +int coredump_make_stack_trace(int fd, const char *executable, char **ret) { + + static const Dwfl_Callbacks callbacks = { + .find_elf = dwfl_build_id_find_elf, + .find_debuginfo = dwfl_standard_find_debuginfo, + }; + + struct stack_context c = {}; + char *buf = NULL; + size_t sz = 0; + int r; + + assert(fd >= 0); + assert(ret); + + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) + return -errno; + + c.f = open_memstream(&buf, &sz); + if (!c.f) + return -ENOMEM; + + elf_version(EV_CURRENT); + + c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (!c.elf) { + r = -EINVAL; + goto finish; + } + + c.dwfl = dwfl_begin(&callbacks); + if (!c.dwfl) { + r = -EINVAL; + goto finish; + } + + if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) { + r = -EINVAL; + goto finish; + } + + if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) { + r = -EINVAL; + goto finish; + } + + if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) { + r = -EINVAL; + goto finish; + } + + if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) { + r = -EINVAL; + goto finish; + } + + c.f = safe_fclose(c.f); + + *ret = buf; + buf = NULL; + + r = 0; + +finish: + if (c.dwfl) + dwfl_end(c.dwfl); + + if (c.elf) + elf_end(c.elf); + + safe_fclose(c.f); + + free(buf); + + return r; +} diff --git a/src/grp-coredump/systemd-coredump/stacktrace.h b/src/grp-coredump/systemd-coredump/stacktrace.h new file mode 100644 index 0000000000..15e9c04465 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/stacktrace.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + 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 . +***/ + +int coredump_make_stack_trace(int fd, const char *executable, char **ret); diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.socket b/src/grp-coredump/systemd-coredump/systemd-coredump.socket new file mode 100644 index 0000000000..4cb2460471 --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.socket @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Process Core Dump Socket +Documentation=man:systemd-coredump(8) +DefaultDependencies=no + +[Socket] +ListenSequentialPacket=/run/systemd/coredump +SocketMode=0600 +Accept=yes +MaxConnections=16 diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.sysusers b/src/grp-coredump/systemd-coredump/systemd-coredump.sysusers new file mode 100644 index 0000000000..bc0816ca5e --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-coredump - "systemd Core Dumper" diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles b/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles new file mode 100644 index 0000000000..02b052583d --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.tmpfiles @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /var/lib/systemd/coredump 0755 root root 3d diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump.xml b/src/grp-coredump/systemd-coredump/systemd-coredump.xml new file mode 100644 index 0000000000..a28dc62e5a --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump.xml @@ -0,0 +1,145 @@ + + + + + + + + + systemd-coredump + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-coredump + 8 + + + + systemd-coredump + systemd-coredump.socket + systemd-coredump@.service + Acquire, save and process core dumps + + + + /usr/lib/systemd/systemd-coredump + systemd-coredump@.service + systemd-coredump.socket + + + + Description + systemd-coredump is a system service that can acquire core dumps + from the kernel and handle them in various ways. + + Core dumps can be written to the journal or saved as a file. Once saved they can be retrieved + for further processing, for example in + gdb1. + + + By default, systemd-coredump will log the core dump including a backtrace + if possible to the journal and store the core dump itself in an external file in + /var/lib/systemd/coredump. + + When the kernel invokes systemd-coredump to handle a core dump, + it will connect to the socket created by the systemd-coredump.socket + unit, which in turn will spawn a systemd-coredump@.service instance + to process the core dump. Hence systemd-coredump.socket + and systemd-coredump@.service are helper units which do the actual + processing of core dumps and are subject to normal service management. + + The behavior of a specific program upon reception of a signal is governed by a few + factors which are described in detail in + core5. + In particular, the core dump will only be processed when the related resource limits are sufficient. + + + + + Configuration + For programs started by systemd process resource limits can be set by directive + LimitCore=, see + systemd.exec5. + + + In order to be used systemd-coredump must be configured in + sysctl8 + parameter kernel.core_pattern. The syntax of this parameter is explained in + core5. + Systemd installs the file /usr/lib/sysctl.d/50-coredump.conf which configures + kernel.core_pattern accordingly. This file may be masked or overridden to use a different + setting following normal + sysctl.d5 + rules. + If the sysctl configuration is modified, it must be updated in the kernel before + it takes effect, see + sysctl8 + and + systemd-sysctl8. + + + The behaviour of systemd-coredump itself is configured through the configuration file + /etc/systemd/coredump.conf and corresponding snippets + /etc/systemd/coredump.conf.d/*.conf, see + coredump.conf5. A new + instance of systemd-coredump is invoked upon receiving every core dump. Therefore, changes + in these files will take effect the next time a core dump is received. + + Resources used by core dump files are restricted in two ways. Parameters like maximum size of acquired + core dumps and files can be set in files /etc/systemd/coredump.conf and snippets mentioned + above. In addition the storage time of core dump files is restricted by systemd-tmpfiles, + corresponding settings are by default in /usr/lib/tmpfiles.d/systemd.conf. + + + + Usage + Data stored in the journal can be viewed with + journalctl1 + as usual. + coredumpctl1 + can be used to retrieve saved core dumps independent of their location, to display information and to process + them e.g. by passing to the GNU debugger (gdb). + + + + See Also + + coredump.conf5, + coredumpctl1, + systemd-journald.service8, + systemd-tmpfiles8, + core5, + sysctl.d5, + systemd-sysctl.service8. + + + diff --git a/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in b/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in new file mode 100644 index 0000000000..588c8d629c --- /dev/null +++ b/src/grp-coredump/systemd-coredump/systemd-coredump@.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Process Core Dump +Documentation=man:systemd-coredump(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/coredump +Conflicts=shutdown.target +After=systemd-remount-fs.service systemd-journald.socket +Requires=systemd-journald.socket +Before=shutdown.target + +[Service] +ExecStart=-@rootlibexecdir@/systemd-coredump +Nice=9 +OOMScoreAdjust=500 +PrivateNetwork=yes +ProtectSystem=full +RuntimeMaxSec=5min diff --git a/src/grp-coredump/systemd-coredump/test-coredump-vacuum.c b/src/grp-coredump/systemd-coredump/test-coredump-vacuum.c new file mode 100644 index 0000000000..70a57f183f --- /dev/null +++ b/src/grp-coredump/systemd-coredump/test-coredump-vacuum.c @@ -0,0 +1,30 @@ +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "coredump-vacuum.h" + +int main(int argc, char *argv[]) { + + if (coredump_vacuum(-1, (uint64_t) -1, 70 * 1024) < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/src/grp-hostname/Makefile b/src/grp-hostname/Makefile new file mode 100644 index 0000000000..939c268c10 --- /dev/null +++ b/src/grp-hostname/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += hostnamectl +nested.subdirs += systemd-hostnamed + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-hostname/hostnamectl/Makefile b/src/grp-hostname/hostnamectl/Makefile new file mode 100644 index 0000000000..c728acce7b --- /dev/null +++ b/src/grp-hostname/hostnamectl/Makefile @@ -0,0 +1,44 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HOSTNAMED),) + +hostnamectl_SOURCES = \ + src/hostname/hostnamectl.c + +hostnamectl_LDADD = \ + libsystemd-shared.la + +bin_PROGRAMS += \ + hostnamectl + +dist_bashcompletion_data += \ + shell-completion/bash/hostnamectl + +dist_zshcompletion_data += \ + shell-completion/zsh/_hostnamectl +endif # ENABLE_HOSTNAMED + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-hostname/hostnamectl/hostnamectl.c b/src/grp-hostname/hostnamectl/hostnamectl.c new file mode 100644 index 0000000000..8a24813934 --- /dev/null +++ b/src/grp-hostname/hostnamectl/hostnamectl.c @@ -0,0 +1,529 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/hostname-util.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/spawn-polkit-agent.h" + +static bool arg_ask_password = true; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_transient = false; +static bool arg_pretty = false; +static bool arg_static = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + char *hostname; + char *static_hostname; + char *pretty_hostname; + char *icon_name; + char *chassis; + char *deployment; + char *location; + char *kernel_name; + char *kernel_release; + char *os_pretty_name; + char *os_cpe_name; + char *virtualization; + char *architecture; +} StatusInfo; + +static void print_status_info(StatusInfo *i) { + sd_id128_t mid = {}, bid = {}; + int r; + + assert(i); + + printf(" Static hostname: %s\n", strna(i->static_hostname)); + + if (!isempty(i->pretty_hostname) && + !streq_ptr(i->pretty_hostname, i->static_hostname)) + printf(" Pretty hostname: %s\n", i->pretty_hostname); + + if (!isempty(i->hostname) && + !streq_ptr(i->hostname, i->static_hostname)) + printf("Transient hostname: %s\n", i->hostname); + + if (!isempty(i->icon_name)) + printf(" Icon name: %s\n", + strna(i->icon_name)); + + if (!isempty(i->chassis)) + printf(" Chassis: %s\n", + strna(i->chassis)); + + if (!isempty(i->deployment)) + printf(" Deployment: %s\n", i->deployment); + + if (!isempty(i->location)) + printf(" Location: %s\n", i->location); + + r = sd_id128_get_machine(&mid); + if (r >= 0) + printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid)); + + r = sd_id128_get_boot(&bid); + if (r >= 0) + printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid)); + + if (!isempty(i->virtualization)) + printf(" Virtualization: %s\n", i->virtualization); + + if (!isempty(i->os_pretty_name)) + printf(" Operating System: %s\n", i->os_pretty_name); + + if (!isempty(i->os_cpe_name)) + printf(" CPE OS Name: %s\n", i->os_cpe_name); + + if (!isempty(i->kernel_name) && !isempty(i->kernel_release)) + printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release); + + if (!isempty(i->architecture)) + printf(" Architecture: %s\n", i->architecture); + +} + +static int show_one_name(sd_bus *bus, const char* attr) { + _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; + + r = sd_bus_get_property( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + attr, + &error, &reply, "s"); + if (r < 0) { + log_error("Could not get property: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &s); + if (r < 0) + return bus_log_parse_error(r); + + printf("%s\n", s); + + return 0; +} + +static int show_all_names(sd_bus *bus) { + StatusInfo info = {}; + + static const struct bus_properties_map hostname_map[] = { + { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) }, + { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) }, + { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) }, + { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) }, + { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) }, + { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) }, + { "Location", "s", NULL, offsetof(StatusInfo, location) }, + { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) }, + { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) }, + { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) }, + { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) }, + {} + }; + + static const struct bus_properties_map manager_map[] = { + { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) }, + { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) }, + {} + }; + + int r; + + r = bus_map_all_properties(bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + hostname_map, + &info); + if (r < 0) + goto fail; + + bus_map_all_properties(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + manager_map, + &info); + + print_status_info(&info); + +fail: + free(info.hostname); + free(info.static_hostname); + free(info.pretty_hostname); + free(info.icon_name); + free(info.chassis); + free(info.deployment); + free(info.location); + free(info.kernel_name); + free(info.kernel_release); + free(info.os_pretty_name); + free(info.os_cpe_name); + free(info.virtualization); + free(info.architecture); + + return r; +} + +static int show_status(sd_bus *bus, char **args, unsigned n) { + assert(args); + + if (arg_pretty || arg_static || arg_transient) { + const char *attr; + + if (!!arg_static + !!arg_pretty + !!arg_transient > 1) { + log_error("Cannot query more than one name type at a time"); + return -EINVAL; + } + + attr = arg_pretty ? "PrettyHostname" : + arg_static ? "StaticHostname" : "Hostname"; + + return show_one_name(bus, attr); + } else + return show_all_names(bus); +} + +static int set_simple_string(sd_bus *bus, const char *method, const char *value) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r = 0; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + method, + &error, NULL, + "sb", value, arg_ask_password); + if (r < 0) + log_error("Could not set property: %s", bus_error_message(&error, -r)); + return r; +} + +static int set_hostname(sd_bus *bus, char **args, unsigned n) { + _cleanup_free_ char *h = NULL; + char *hostname = args[1]; + int r; + + assert(args); + assert(n == 2); + + if (!arg_pretty && !arg_static && !arg_transient) + arg_pretty = arg_static = arg_transient = true; + + if (arg_pretty) { + const char *p; + + /* If the passed hostname is already valid, then + * assume the user doesn't know anything about pretty + * hostnames, so let's unset the pretty hostname, and + * just set the passed hostname as static/dynamic + * hostname. */ + + if (arg_static && hostname_is_valid(hostname, true)) { + p = ""; + /* maybe get rid of trailing dot */ + hostname = hostname_cleanup(hostname); + } else { + p = h = strdup(hostname); + if (!p) + return log_oom(); + + hostname_cleanup(hostname); + } + + r = set_simple_string(bus, "SetPrettyHostname", p); + if (r < 0) + return r; + } + + if (arg_static) { + r = set_simple_string(bus, "SetStaticHostname", hostname); + if (r < 0) + return r; + } + + if (arg_transient) { + r = set_simple_string(bus, "SetHostname", hostname); + if (r < 0) + return r; + } + + return 0; +} + +static int set_icon_name(sd_bus *bus, char **args, unsigned n) { + assert(args); + assert(n == 2); + + return set_simple_string(bus, "SetIconName", args[1]); +} + +static int set_chassis(sd_bus *bus, char **args, unsigned n) { + assert(args); + assert(n == 2); + + return set_simple_string(bus, "SetChassis", args[1]); +} + +static int set_deployment(sd_bus *bus, char **args, unsigned n) { + assert(args); + assert(n == 2); + + return set_simple_string(bus, "SetDeployment", args[1]); +} + +static int set_location(sd_bus *bus, char **args, unsigned n) { + assert(args); + assert(n == 2); + + return set_simple_string(bus, "SetLocation", args[1]); +} + +static void help(void) { + printf("%s [OPTIONS...] COMMAND ...\n\n" + "Query or change system hostname.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --transient Only set transient hostname\n" + " --static Only set static hostname\n" + " --pretty Only set pretty hostname\n\n" + "Commands:\n" + " status Show current hostname settings\n" + " set-hostname NAME Set system hostname\n" + " set-icon-name NAME Set icon name for host\n" + " set-chassis NAME Set chassis type for host\n" + " set-deployment NAME Set deployment environment for host\n" + " set-location NAME Set location for host\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, + ARG_TRANSIENT, + ARG_STATIC, + ARG_PRETTY + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "transient", no_argument, NULL, ARG_TRANSIENT }, + { "static", no_argument, NULL, ARG_STATIC }, + { "pretty", no_argument, NULL, ARG_PRETTY }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_TRANSIENT: + arg_transient = true; + break; + + case ARG_PRETTY: + arg_pretty = true; + break; + + case ARG_STATIC: + arg_static = true; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(sd_bus *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-hostname", EQUAL, 2, set_hostname }, + { "set-icon-name", EQUAL, 2, set_icon_name }, + { "set-chassis", EQUAL, 2, set_chassis }, + { "set-deployment", EQUAL, 2, set_deployment }, + { "set-location", EQUAL, 2, set_location }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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; + } + + r = hostnamectl_main(bus, argc, argv); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-hostname/hostnamectl/hostnamectl.completion.bash b/src/grp-hostname/hostnamectl/hostnamectl.completion.bash new file mode 100644 index 0000000000..6a252188ea --- /dev/null +++ b/src/grp-hostname/hostnamectl/hostnamectl.completion.bash @@ -0,0 +1,64 @@ +# hostnamectl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_hostnamectl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --transient --static --pretty + --no-ask-password -H --host --machine' + + if [[ $cur = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [STANDALONE]='status' + [ICONS]='set-icon-name' + [NAME]='set-hostname set-deployment set-location' + [CHASSIS]='set-chassis' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[CHASSIS]}; then + comps='desktop laptop server tablet handset watch embedded vm container' + elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[ICONS]} ${VERBS[NAME]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _hostnamectl hostnamectl diff --git a/src/grp-hostname/hostnamectl/hostnamectl.completion.zsh b/src/grp-hostname/hostnamectl/hostnamectl.completion.zsh new file mode 100644 index 0000000000..7528e0649d --- /dev/null +++ b/src/grp-hostname/hostnamectl/hostnamectl.completion.zsh @@ -0,0 +1,80 @@ +#compdef hostnamectl + +_hostnamectl_set-hostname() { + if (( CURRENT <= 3 )); then + _message "new hostname" + else + _message "no more options" + fi +} + +_hostnamectl_set-icon-name() { + if (( CURRENT <= 3 )); then + _message "new icon name" + else + _message "no more options" + fi +} + +_hostnamectl_set-chassis() { + if (( CURRENT <= 3 )); then + _chassis=( desktop laptop server tablet handset watch embedded vm container ) + _describe chassis _chassis + else + _message "no more options" + fi +} + +_hostnamectl_set-deployment() { + if (( CURRENT <= 3 )); then + _message "new environment" + else + _message "no more options" + fi +} + +_hostnamectl_set-location() { + if (( CURRENT <= 3 )); then + _message "new location" + else + _message "no more options" + fi +} + +_hostnamectl_command() { + local -a _hostnamectl_cmds + _hostnamectl_cmds=( + "status:Show current hostname settings" + "set-hostname:Set system hostname" + "set-icon-name:Set icon name for host" + "set-chassis:Set chassis type for host" + "set-deployment:Set deployment environment for host" + "set-location:Set location for host" + ) + if (( CURRENT == 1 )); then + _describe -t commands 'hostnamectl commands' _hostnamectl_cmds || compadd "$@" + else + local curcontext="$curcontext" + cmd="${${_hostnamectl_cmds[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + if [[ $cmd == status ]]; then + _message "no options" + else + _hostnamectl_$cmd + fi + else + _message "unknown hostnamectl command: $words[1]" + fi + fi +} + +_arguments -s \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--transient[Only set transient hostname]' \ + '--static[Only set static hostname]' \ + '--pretty[Only set pretty hostname]' \ + '--no-ask-password[Do not prompt for password]' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \ + '*::hostnamectl commands:_hostnamectl_command' diff --git a/src/grp-hostname/hostnamectl/hostnamectl.xml b/src/grp-hostname/hostnamectl/hostnamectl.xml new file mode 100644 index 0000000000..60004e9d04 --- /dev/null +++ b/src/grp-hostname/hostnamectl/hostnamectl.xml @@ -0,0 +1,260 @@ + + + + + + + + + hostnamectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + hostnamectl + 1 + + + + hostnamectl + Control the system hostname + + + + + hostnamectl + OPTIONS + COMMAND + + + + + Description + + hostnamectl may be used to query and + change the system hostname and related settings. + + This tool distinguishes three different hostnames: the + high-level "pretty" hostname which might include all kinds of + special characters (e.g. "Lennart's Laptop"), the static hostname + which is used to initialize the kernel hostname at boot (e.g. + "lennarts-laptop"), and the transient hostname which is a fallback + value received from network configuration. If a static hostname is + set, and is valid (something other than localhost), then the + transient hostname is not used. + + Note that the pretty hostname has little restrictions on the + characters used, while the static and transient hostnames are + limited to the usually accepted characters of Internet domain + names. + + The static hostname is stored in + /etc/hostname, see + hostname5 + for more information. The pretty hostname, chassis type, and icon + name are stored in /etc/machine-info, see + machine-info5. + + Use + systemd-firstboot1 + to initialize the system host name for mounted (but not booted) + system images. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + + + If status is used (or no + explicit command is given) and one of those fields is given, + hostnamectl will print out just this + selected hostname. + + If used with set-hostname, only the + selected hostname(s) will be updated. When more than one of + those options is used, all the specified hostnames will be + updated. + + + + + + + + + + The following commands are understood: + + + + status + + Show current system + hostname and related + information. + + + + set-hostname NAME + + Set the system hostname to + NAME. By default, this will alter + the pretty, the static, and the transient hostname alike; + however, if one or more of , + , are + used, only the selected hostnames are changed. If the pretty + hostname is being set, and static or transient are being set + as well, the specified hostname will be simplified in regards + to the character set used before the latter are updated. This + is done by replacing spaces with - and + removing special characters. This ensures that the pretty and + the static hostname are always closely related while still + following the validity rules of the specific name. This + simplification of the hostname string is not done if only the + transient and/or static host names are set, and the pretty + host name is left untouched. + + Pass the empty string as the + hostname to reset the selected hostnames to their default + (usually localhost). + + + + set-icon-name NAME + + Set the system icon name to + NAME. The icon name is used by some + graphical applications to visualize this host. The icon name + should follow the Icon + Naming Specification. + + Pass an empty string to reset the icon name to the + default value, which is determined from chassis type (see + below) and possibly other parameters. + + + + set-chassis TYPE + + Set the chassis type to + TYPE. The chassis type is used by + some graphical applications to visualize the host or alter + user interaction. Currently, the following chassis types are + defined: + desktop, + laptop, + server, + tablet, + handset, + watch, + embedded, + as well as the special chassis types + vm and + container for virtualized systems that lack + an immediate physical chassis. + + Pass an empty string to reset the chassis type to the + default value which is determined from the firmware and + possibly other parameters. + + + + + set-deployment ENVIRONMENT + + Set the deployment environment description. + ENVIRONMENT must be a single word + without any control characters. One of the following is + suggested: + development, + integration, + staging, + production. + + + Pass an empty string to reset to the default empty + value. + + + + + set-location LOCATION + + Set the location string for the system, if it + is known. LOCATION should be a + human-friendly, free-form string describing the physical + location of the system, if it is known and applicable. This + may be as generic as Berlin, Germany or as + specific as Left Rack, 2nd Shelf. + + Pass an empty string to reset to the default empty + value. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + hostname1, + hostname5, + machine-info5, + systemctl1, + systemd-hostnamed.service8, + systemd-firstboot1 + + + + diff --git a/src/grp-hostname/systemd-hostnamed/.gitignore b/src/grp-hostname/systemd-hostnamed/.gitignore new file mode 100644 index 0000000000..1ff281b231 --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/.gitignore @@ -0,0 +1 @@ +org.freedesktop.hostname1.policy diff --git a/src/grp-hostname/systemd-hostnamed/Makefile b/src/grp-hostname/systemd-hostnamed/Makefile new file mode 100644 index 0000000000..0c8f0b5ae6 --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/Makefile @@ -0,0 +1,64 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HOSTNAMED),) +systemd_hostnamed_SOURCES = \ + src/hostname/hostnamed.c + +systemd_hostnamed_LDADD = \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-hostnamed + +nodist_systemunit_DATA += \ + units/systemd-hostnamed.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.hostname1.busname + +dist_dbuspolicy_DATA += \ + src/hostname/org.freedesktop.hostname1.conf + +dist_dbussystemservice_DATA += \ + src/hostname/org.freedesktop.hostname1.service + +polkitpolicy_files += \ + src/hostname/org.freedesktop.hostname1.policy + +SYSTEM_UNIT_ALIASES += \ + systemd-hostnamed.service dbus-org.freedesktop.hostname1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.hostname1.busname + +endif # ENABLE_HOSTNAMED +polkitpolicy_in_files += \ + src/hostname/org.freedesktop.hostname1.policy.in + +EXTRA_DIST += \ + units/systemd-hostnamed.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-hostname/systemd-hostnamed/hostnamed.c b/src/grp-hostname/systemd-hostnamed/hostnamed.c new file mode 100644 index 0000000000..366adbb282 --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/hostnamed.c @@ -0,0 +1,743 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/fileio-label.h" +#include "basic/hostname-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/bus-util.h" + +#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:") + +enum { + PROP_HOSTNAME, + PROP_STATIC_HOSTNAME, + PROP_PRETTY_HOSTNAME, + PROP_ICON_NAME, + PROP_CHASSIS, + PROP_DEPLOYMENT, + PROP_LOCATION, + PROP_KERNEL_NAME, + PROP_KERNEL_RELEASE, + PROP_KERNEL_VERSION, + PROP_OS_PRETTY_NAME, + PROP_OS_CPE_NAME, + _PROP_MAX +}; + +typedef struct Context { + char *data[_PROP_MAX]; + Hashmap *polkit_registry; +} Context; + +static void context_reset(Context *c) { + int p; + + assert(c); + + for (p = 0; p < _PROP_MAX; p++) + c->data[p] = mfree(c->data[p]); +} + +static void context_free(Context *c) { + assert(c); + + context_reset(c); + bus_verify_polkit_async_registry_free(c->polkit_registry); +} + +static int context_read_data(Context *c) { + int r; + struct utsname u; + + assert(c); + + context_reset(c); + + assert_se(uname(&u) >= 0); + c->data[PROP_KERNEL_NAME] = strdup(u.sysname); + c->data[PROP_KERNEL_RELEASE] = strdup(u.release); + c->data[PROP_KERNEL_VERSION] = strdup(u.version); + if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] || + !c->data[PROP_KERNEL_VERSION]) + return -ENOMEM; + + c->data[PROP_HOSTNAME] = gethostname_malloc(); + if (!c->data[PROP_HOSTNAME]) + return -ENOMEM; + + r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]); + if (r < 0 && r != -ENOENT) + return r; + + r = parse_env_file("/etc/machine-info", NEWLINE, + "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], + "ICON_NAME", &c->data[PROP_ICON_NAME], + "CHASSIS", &c->data[PROP_CHASSIS], + "DEPLOYMENT", &c->data[PROP_DEPLOYMENT], + "LOCATION", &c->data[PROP_LOCATION], + NULL); + if (r < 0 && r != -ENOENT) + return r; + + r = parse_env_file("/etc/os-release", NEWLINE, + "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], + "CPE_NAME", &c->data[PROP_OS_CPE_NAME], + NULL); + if (r == -ENOENT) + r = parse_env_file("/usr/lib/os-release", NEWLINE, + "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], + "CPE_NAME", &c->data[PROP_OS_CPE_NAME], + NULL); + + if (r < 0 && r != -ENOENT) + return r; + + return 0; +} + +static bool valid_chassis(const char *chassis) { + assert(chassis); + + return nulstr_contains( + "vm\0" + "container\0" + "desktop\0" + "laptop\0" + "server\0" + "tablet\0" + "handset\0" + "watch\0" + "embedded\0", + chassis); +} + +static bool valid_deployment(const char *deployment) { + assert(deployment); + + return in_charset(deployment, VALID_DEPLOYMENT_CHARS); +} + +static const char* fallback_chassis(void) { + int r; + char *type; + unsigned t; + int v; + + v = detect_virtualization(); + + if (VIRTUALIZATION_IS_VM(v)) + return "vm"; + if (VIRTUALIZATION_IS_CONTAINER(v)) + return "container"; + + r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type); + if (r < 0) + goto try_dmi; + + r = safe_atou(type, &t); + free(type); + if (r < 0) + goto try_dmi; + + /* We only list the really obvious cases here as the ACPI data + * is not really super reliable. + * + * See the ACPI 5.0 Spec Section 5.2.9.1 for details: + * + * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf + */ + + switch(t) { + + case 1: + case 3: + case 6: + return "desktop"; + + case 2: + return "laptop"; + + case 4: + case 5: + case 7: + return "server"; + + case 8: + return "tablet"; + } + +try_dmi: + r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type); + if (r < 0) + return NULL; + + r = safe_atou(type, &t); + free(type); + if (r < 0) + return NULL; + + /* We only list the really obvious cases here. The DMI data is + unreliable enough, so let's not do any additional guesswork + on top of that. + + See the SMBIOS Specification 3.0 section 7.4.1 for + details about the values listed here: + + https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf + */ + + switch (t) { + + case 0x3: + case 0x4: + case 0x6: + case 0x7: + return "desktop"; + + case 0x8: + case 0x9: + case 0xA: + case 0xE: + return "laptop"; + + case 0xB: + return "handset"; + + case 0x11: + case 0x1C: + case 0x1D: + return "server"; + + case 0x1E: + return "tablet"; + } + + return NULL; +} + +static char* context_fallback_icon_name(Context *c) { + const char *chassis; + + assert(c); + + if (!isempty(c->data[PROP_CHASSIS])) + return strappend("computer-", c->data[PROP_CHASSIS]); + + chassis = fallback_chassis(); + if (chassis) + return strappend("computer-", chassis); + + return strdup("computer"); +} + + +static bool hostname_is_useful(const char *hn) { + return !isempty(hn) && !is_localhost(hn); +} + +static int context_update_kernel_hostname(Context *c) { + const char *static_hn; + const char *hn; + + assert(c); + + static_hn = c->data[PROP_STATIC_HOSTNAME]; + + /* /etc/hostname with something other than "localhost" + * has the highest preference ... */ + if (hostname_is_useful(static_hn)) + hn = static_hn; + + /* ... the transient host name, (ie: DHCP) comes next ... */ + else if (!isempty(c->data[PROP_HOSTNAME])) + hn = c->data[PROP_HOSTNAME]; + + /* ... fallback to static "localhost.*" ignored above ... */ + else if (!isempty(static_hn)) + hn = static_hn; + + /* ... and the ultimate fallback */ + else + hn = "localhost"; + + if (sethostname_idempotent(hn) < 0) + return -errno; + + return 0; +} + +static int context_write_data_static_hostname(Context *c) { + + assert(c); + + if (isempty(c->data[PROP_STATIC_HOSTNAME])) { + + if (unlink("/etc/hostname") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]); +} + +static int context_write_data_machine_info(Context *c) { + + static const char * const name[_PROP_MAX] = { + [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME", + [PROP_ICON_NAME] = "ICON_NAME", + [PROP_CHASSIS] = "CHASSIS", + [PROP_DEPLOYMENT] = "DEPLOYMENT", + [PROP_LOCATION] = "LOCATION", + }; + + _cleanup_strv_free_ char **l = NULL; + int r, p; + + assert(c); + + r = load_env_file(NULL, "/etc/machine-info", NULL, &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) { + _cleanup_free_ char *t = NULL; + char **u; + + assert(name[p]); + + if (isempty(c->data[p])) { + strv_env_unset(l, name[p]); + continue; + } + + t = strjoin(name[p], "=", c->data[p], NULL); + if (!t) + return -ENOMEM; + + u = strv_env_set(l, t); + if (!u) + return -ENOMEM; + + strv_free(l); + l = u; + } + + if (strv_isempty(l)) { + if (unlink("/etc/machine-info") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + return write_env_file_label("/etc/machine-info", l); +} + +static int property_get_icon_name( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *n = NULL; + Context *c = userdata; + const char *name; + + if (isempty(c->data[PROP_ICON_NAME])) + name = n = context_fallback_icon_name(c); + else + name = c->data[PROP_ICON_NAME]; + + if (!name) + return -ENOMEM; + + return sd_bus_message_append(reply, "s", name); +} + +static int property_get_chassis( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Context *c = userdata; + const char *name; + + if (isempty(c->data[PROP_CHASSIS])) + name = fallback_chassis(); + else + name = c->data[PROP_CHASSIS]; + + return sd_bus_message_append(reply, "s", name); +} + +static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *name; + int interactive; + char *h; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; + + if (isempty(name)) + name = c->data[PROP_STATIC_HOSTNAME]; + + if (isempty(name)) + name = "localhost"; + + if (!hostname_is_valid(name, false)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); + + if (streq_ptr(name, c->data[PROP_HOSTNAME])) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.hostname1.set-hostname", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + h = strdup(name); + if (!h) + return -ENOMEM; + + free(c->data[PROP_HOSTNAME]); + c->data[PROP_HOSTNAME] = h; + + r = context_update_kernel_hostname(c); + if (r < 0) { + log_error_errno(r, "Failed to set host name: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + } + + log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME])); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *name; + int interactive; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; + + name = empty_to_null(name); + + if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.hostname1.set-static-hostname", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (isempty(name)) + c->data[PROP_STATIC_HOSTNAME] = mfree(c->data[PROP_STATIC_HOSTNAME]); + else { + char *h; + + if (!hostname_is_valid(name, false)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); + + h = strdup(name); + if (!h) + return -ENOMEM; + + free(c->data[PROP_STATIC_HOSTNAME]); + c->data[PROP_STATIC_HOSTNAME] = h; + } + + r = context_update_kernel_hostname(c); + if (r < 0) { + log_error_errno(r, "Failed to set host name: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); + } + + r = context_write_data_static_hostname(c); + if (r < 0) { + log_error_errno(r, "Failed to write static host name: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r)); + } + + log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) { + int interactive; + const char *name; + int r; + + assert(c); + assert(m); + + r = sd_bus_message_read(m, "sb", &name, &interactive); + if (r < 0) + return r; + + name = empty_to_null(name); + + if (streq_ptr(name, c->data[prop])) + return sd_bus_reply_method_return(m, NULL); + + /* Since the pretty hostname should always be changed at the + * same time as the static one, use the same policy action for + * both... */ + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (isempty(name)) + c->data[prop] = mfree(c->data[prop]); + else { + char *h; + + /* The icon name might ultimately be used as file + * name, so better be safe than sorry */ + + if (prop == PROP_ICON_NAME && !filename_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name); + if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name); + if (prop == PROP_CHASSIS && !valid_chassis(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name); + if (prop == PROP_DEPLOYMENT && !valid_deployment(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name); + if (prop == PROP_LOCATION && string_has_cc(name, NULL)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name); + + h = strdup(name); + if (!h) + return -ENOMEM; + + free(c->data[prop]); + c->data[prop] = h; + } + + r = context_write_data_machine_info(c); + if (r < 0) { + log_error_errno(r, "Failed to write machine info: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r)); + } + + log_info("Changed %s to '%s'", + prop == PROP_PRETTY_HOSTNAME ? "pretty host name" : + prop == PROP_DEPLOYMENT ? "deployment" : + prop == PROP_LOCATION ? "location" : + prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop])); + + (void) sd_bus_emit_properties_changed( + sd_bus_message_get_bus(m), + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" : + prop == PROP_DEPLOYMENT ? "Deployment" : + prop == PROP_LOCATION ? "Location" : + prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error); +} + +static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error); +} + +static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error); +} + +static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error); +} + +static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error); +} + +static const sd_bus_vtable hostname_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(c); + assert(event); + assert(_bus); + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); + + r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c); + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); + + r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + *_bus = bus; + bus = NULL; + + return 0; +} + +int main(int argc, char *argv[]) { + Context context = {}; + _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); + log_parse_environment(); + log_open(); + + umask(0022); + mac_selinux_init(); + + 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"); + goto finish; + } + + sd_event_set_watchdog(event, true); + + r = connect_bus(&context, event, &bus); + if (r < 0) + goto finish; + + r = context_read_data(&context); + if (r < 0) { + log_error_errno(r, "Failed to read hostname and machine information: %m"); + goto finish; + } + + r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + +finish: + context_free(&context); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.conf b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.conf new file mode 100644 index 0000000000..46b4aadc83 --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.policy.in b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.policy.in new file mode 100644 index 0000000000..c32c1d4fda --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.policy.in @@ -0,0 +1,50 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Set host name + <_message>Authentication is required to set the local host name. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set static host name + <_message>Authentication is required to set the statically configured local host name, as well as the pretty host name. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.hostname1.set-hostname org.freedesktop.hostname1.set-machine-info + + + + <_description>Set machine information + <_message>Authentication is required to set local machine information. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.service b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.service new file mode 100644 index 0000000000..6041ed60ca --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/org.freedesktop.hostname1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.hostname1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.hostname1.service diff --git a/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in new file mode 100644 index 0000000000..0b03a589ea --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hostname Service +Documentation=man:systemd-hostnamed.service(8) man:hostname(5) man:machine-info(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/hostnamed + +[Service] +ExecStart=@rootlibexecdir@/systemd-hostnamed +BusName=org.freedesktop.hostname1 +CapabilityBoundingSet=CAP_SYS_ADMIN +WatchdogSec=3min +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=yes +ProtectHome=yes +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.xml b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.xml new file mode 100644 index 0000000000..6990d41b02 --- /dev/null +++ b/src/grp-hostname/systemd-hostnamed/systemd-hostnamed.service.xml @@ -0,0 +1,85 @@ + + + + + + + + + systemd-hostnamed.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-hostnamed.service + 8 + + + + systemd-hostnamed.service + systemd-hostnamed + Host name bus mechanism + + + + systemd-hostnamed.service + /usr/lib/systemd/systemd-hostnamed + + + + Description + + systemd-hostnamed is a system service + that may be used as a mechanism to change the system's hostname. + systemd-hostnamed is automatically activated + on request and terminates itself when it is unused. + + The tool + hostnamectl1 + is a command line client to this service. + + See the + developer documentation for information about the APIs + systemd-hostnamed provides. + + + + See Also + + systemd1, + hostname5, + machine-info5, + hostnamectl1, + sethostname2 + + + + diff --git a/src/grp-initprogs/Makefile b/src/grp-initprogs/Makefile new file mode 100644 index 0000000000..2cf2214e2a --- /dev/null +++ b/src/grp-initprogs/Makefile @@ -0,0 +1,44 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += grp-sleep +nested.subdirs += systemd-backlight +nested.subdirs += systemd-binfmt +nested.subdirs += systemd-detect-virt +nested.subdirs += systemd-firstboot +nested.subdirs += systemd-fsck +nested.subdirs += systemd-modules-load +nested.subdirs += systemd-quotacheck +nested.subdirs += systemd-random-seed +nested.subdirs += systemd-rfkill +nested.subdirs += systemd-sysctl +nested.subdirs += systemd-sysusers +nested.subdirs += systemd-tmpfiles +nested.subdirs += systemd-update-done +nested.subdirs += systemd-update-utmp +nested.subdirs += systemd-user-sessions +nested.subdirs += systemd-vconsole-setup + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/Makefile b/src/grp-initprogs/grp-sleep/Makefile new file mode 100644 index 0000000000..5a3a87d2bf --- /dev/null +++ b/src/grp-initprogs/grp-sleep/Makefile @@ -0,0 +1,30 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-hibernate-resume +nested.subdirs += systemd-hibernate-resume-generator +nested.subdirs += systemd-sleep + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile new file mode 100644 index 0000000000..835f2a37b2 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/Makefile @@ -0,0 +1,38 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HIBERNATE),) +systemgenerator_PROGRAMS += \ + systemd-hibernate-resume-generator + +systemd_hibernate_resume_generator_SOURCES = \ + src/hibernate-resume/hibernate-resume-generator.c + +systemd_hibernate_resume_generator_LDADD = \ + libsystemd-shared.la + +endif # ENABLE_HIBERNATE + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c new file mode 100644 index 0000000000..6380c297c2 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/hibernate-resume-generator.c @@ -0,0 +1,99 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ivan Shapovalov + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/fstab-util.h" + +static const char *arg_dest = "/tmp"; +static char *arg_resume_dev = NULL; + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + if (streq(key, "resume") && value) { + free(arg_resume_dev); + arg_resume_dev = fstab_node_to_udev_node(value); + if (!arg_resume_dev) + return log_oom(); + } + + return 0; +} + +static int process_resume(void) { + _cleanup_free_ char *name = NULL, *lnk = NULL; + int r; + + if (!arg_resume_dev) + return 0; + + r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Don't even consider resuming outside of initramfs. */ + if (!in_initrd()) + return EXIT_SUCCESS; + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + r = process_resume(); + free(arg_resume_dev); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/systemd-hibernate-resume-generator.xml b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/systemd-hibernate-resume-generator.xml new file mode 100644 index 0000000000..d811b9b551 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume-generator/systemd-hibernate-resume-generator.xml @@ -0,0 +1,93 @@ + + + + + + + + systemd-hibernate-resume-generator + systemd + + + + Developer + Ivan + Shapovalov + intelfx100@gmail.com + + + + + + systemd-hibernate-resume-generator + 8 + + + + systemd-hibernate-resume-generator + Unit generator for resume= kernel parameter + + + + /usr/lib/systemd/system-generators/systemd-hibernate-resume-generator + + + + Description + + systemd-hibernate-resume-generator is a + generator that instantiates + systemd-hibernate-resume@.service8 + unit according to the value of parameter + specified on the kernel command line. + + + + Kernel Command Line + + systemd-hibernate-resume-generator + understands the following kernel command line parameters: + + + + + resume= + + Takes a path to the resume device. Both + persistent block device paths like + /dev/disk/by-foo/bar and + fstab5-style + specifiers like FOO=bar are + supported. + + + + + + + See Also + + systemd1, + systemd-hibernate-resume@.service8, + kernel-command-line7 + + + + diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile new file mode 100644 index 0000000000..95f44744a2 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/Makefile @@ -0,0 +1,45 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HIBERNATE),) + +rootlibexec_PROGRAMS += \ + systemd-hibernate-resume + +systemd_hibernate_resume_SOURCES = \ + src/hibernate-resume/hibernate-resume.c + +systemd_hibernate_resume_LDADD = \ + libsystemd-shared.la + +nodist_systemunit_DATA += \ + units/systemd-hibernate-resume@.service + +endif # ENABLE_HIBERNATE + +EXTRA_DIST += \ + units/systemd-hibernate-resume@.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c new file mode 100644 index 0000000000..64d14bf64b --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/hibernate-resume.c @@ -0,0 +1,82 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ivan Shapovalov + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/util.h" + +int main(int argc, char *argv[]) { + struct stat st; + const char *device; + _cleanup_free_ char *major_minor = NULL; + int r; + + if (argc != 2) { + log_error("This program expects one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Refuse to run unless we are in an initrd() */ + if (!in_initrd()) + return EXIT_SUCCESS; + + device = argv[1]; + + if (stat(device, &st) < 0) { + log_error_errno(errno, "Failed to stat '%s': %m", device); + return EXIT_FAILURE; + } + + if (!S_ISBLK(st.st_mode)) { + log_error("Resume device '%s' is not a block device.", device); + return EXIT_FAILURE; + } + + if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) { + log_oom(); + return EXIT_FAILURE; + } + + r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE); + if (r < 0) { + log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor); + return EXIT_FAILURE; + } + + /* + * The write above shall not return. + * + * However, failed resume is a normal condition (may mean that there is + * no hibernation image). + */ + + log_info("Could not resume from '%s' (%s).", device, major_minor); + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in new file mode 100644 index 0000000000..65e8eb83f1 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Resume from hibernation using device %f +Documentation=man:systemd-hibernate-resume@.service(8) +DefaultDependencies=no +BindsTo=%i.device +Wants=local-fs-pre.target +After=%i.device +Before=local-fs-pre.target +ConditionPathExists=/etc/initrd-release + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-hibernate-resume %f diff --git a/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.xml b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.xml new file mode 100644 index 0000000000..7d00827447 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-hibernate-resume/systemd-hibernate-resume@.service.xml @@ -0,0 +1,81 @@ + + + + + + + + systemd-hibernate-resume@.service + systemd + + + + Developer + Ivan + Shapovalov + intelfx100@gmail.com + + + + + + systemd-hibernate-resume@.service + 8 + + + + systemd-hibernate-resume@.service + systemd-hibernate-resume + Resume from hibernation + + + + systemd-hibernate-resume@.service + /usr/lib/systemd/systemd-hibernate-resume + + + + Description + + systemd-hibernate-resume@.service + initiates the resume from hibernation. It is instantiated with the + device to resume from as the template argument. + + systemd-hibernate-resume only supports + the in-kernel hibernation implementation, known as + swsusp. + Internally, it works by writing the major:minor of specified + device node to /sys/power/resume. + + Failing to initiate a resume is not an error condition. It + may mean that there was no resume image (e. g. if the system has + been simply powered off and not hibernated). In such case, the + boot is ordinarily continued. + + + + See Also + + systemd1, + systemd-hibernate-resume-generator8 + + + + diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile b/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile new file mode 100644 index 0000000000..45c0beaf9d --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/Makefile @@ -0,0 +1,49 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-sleep +systemd_sleep_SOURCES = \ + src/sleep/sleep.c + +systemd_sleep_LDADD = \ + libsystemd-shared.la + +ifneq ($(ENABLE_HIBERNATE),) + +dist_systemunit_DATA += \ + units/hibernate.target \ + units/hybrid-sleep.target + +nodist_systemunit_DATA += \ + units/systemd-hibernate.service \ + units/systemd-hybrid-sleep.service + +endif # ENABLE_HIBERNATE + +EXTRA_DIST += \ + units/systemd-hibernate.service.in \ + units/systemd-hybrid-sleep.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target b/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target new file mode 100644 index 0000000000..143eb59230 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/hibernate.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hibernate +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-hibernate.service +After=systemd-hibernate.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target b/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target new file mode 100644 index 0000000000..d2d3409225 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/hybrid-sleep.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-hybrid-sleep.service +After=systemd-hybrid-sleep.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c new file mode 100644 index 0000000000..e9e84bc893 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.c @@ -0,0 +1,215 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/sleep-config.h" + +static char* arg_verb = NULL; + +static int write_mode(char **modes) { + int r = 0; + char **mode; + + STRV_FOREACH(mode, modes) { + int k; + + k = write_string_file("/sys/power/disk", *mode, 0); + if (k == 0) + return 0; + + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", + *mode); + if (r == 0) + r = k; + } + + if (r < 0) + log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); + + return r; +} + +static int write_state(FILE **f, char **states) { + char **state; + int r = 0; + + STRV_FOREACH(state, states) { + int k; + + k = write_string_stream(*f, *state, true); + if (k == 0) + return 0; + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", + *state); + if (r == 0) + r = k; + + fclose(*f); + *f = fopen("/sys/power/state", "we"); + if (!*f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + } + + return r; +} + +static int execute(char **modes, char **states) { + + char *arguments[] = { + NULL, + (char*) "pre", + arg_verb, + NULL + }; + static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; + + int r; + _cleanup_fclose_ FILE *f = NULL; + + /* This file is opened first, so that if we hit an error, + * we can abort before modifying any state. */ + f = fopen("/sys/power/state", "we"); + if (!f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + + /* Configure the hibernation mode */ + r = write_mode(modes); + if (r < 0) + return r; + + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + LOG_MESSAGE("Suspending system..."), + "SLEEP=%s", arg_verb, + NULL); + + r = write_state(&f, states); + if (r < 0) + return r; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb, + NULL); + + arguments[1] = (char*) "post"; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + return r; +} + +static void help(void) { + printf("%s COMMAND\n\n" + "Suspend the system, hibernate the system, or both.\n\n" + "Commands:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Both hibernate and suspend the system\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; /* done */ + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc - optind != 1) { + log_error("Usage: %s COMMAND", + program_invocation_short_name); + return -EINVAL; + } + + arg_verb = argv[optind]; + + if (!streq(arg_verb, "suspend") && + !streq(arg_verb, "hibernate") && + !streq(arg_verb, "hybrid-sleep")) { + log_error("Unknown command '%s'.", arg_verb); + return -EINVAL; + } + + return 1 /* work to do */; +} + +int main(int argc, char *argv[]) { + _cleanup_strv_free_ char **modes = NULL, **states = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = parse_sleep_config(arg_verb, &modes, &states); + if (r < 0) + goto finish; + + r = execute(modes, states); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.target b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.target new file mode 100644 index 0000000000..10c7c8d594 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/sleep.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Sleep +Documentation=man:systemd.special(7) +DefaultDependencies=no +RefuseManualStart=yes +StopWhenUnneeded=yes diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target b/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target new file mode 100644 index 0000000000..f50cb2264f --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/suspend.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Suspend +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-suspend.service +After=systemd-suspend.service diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in new file mode 100644 index 0000000000..29d9b696a8 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hibernate.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hibernate +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hibernate diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in new file mode 100644 index 0000000000..914b686c36 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-hybrid-sleep.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hybrid-sleep diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.conf.xml b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.conf.xml new file mode 100644 index 0000000000..9a379ecb94 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.conf.xml @@ -0,0 +1,186 @@ + + + + + + + + systemd-sleep.conf + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-sleep.conf + 5 + + + + systemd-sleep.conf + sleep.conf.d + Suspend and hibernation configuration file + + + + /etc/systemd/sleep.conf + /etc/systemd/sleep.conf.d/*.conf + /run/systemd/sleep.conf.d/*.conf + /usr/lib/systemd/sleep.conf.d/*.conf + + + + Description + + systemd supports three general + power-saving modes: + + + + suspend + + a low-power state + where execution of the OS is paused, + and complete power loss might result + in lost data, and which is fast to + enter and exit. This corresponds to + suspend, standby, or freeze states as + understood by the kernel. + + + + + hibernate + + a low-power state + where execution of the OS is paused, + and complete power loss does not + result in lost data, and which might + be slow to enter and exit. This + corresponds to the hibernation as + understood by the kernel. + + + + + hybrid-sleep + + a low-power state + where execution of the OS is paused, + which might be slow to enter, and on + complete power loss does not result in + lost data but might be slower to exit + in that case. This mode is called + suspend-to-both by the kernel. + + + + + Settings in these files determine what strings + will be written to + /sys/power/disk and + /sys/power/state by + systemd-sleep8 + when + systemd1 + attempts to suspend or hibernate the machine. + + + + + + Options + + The following options can be configured in the + [Sleep] section of + /etc/systemd/sleep.conf or a + sleep.conf.d file: + + + + SuspendMode= + HibernateMode= + HybridSleepMode= + + The string to be written to + /sys/power/disk by, + respectively, + systemd-suspend.service8, + systemd-hibernate.service8, or + systemd-hybrid-sleep.service8. + More than one value can be specified by separating + multiple values with whitespace. They will be tried + in turn, until one is written without error. If + neither succeeds, the operation will be aborted. + + + + + SuspendState= + HibernateState= + HybridSleepState= + + The string to be written to + /sys/power/state by, + respectively, + systemd-suspend.service8, + systemd-hibernate.service8, or + systemd-hybrid-sleep.service8. + More than one value can be specified by separating + multiple values with whitespace. They will be tried + in turn, until one is written without error. If + neither succeeds, the operation will be aborted. + + + + + + + Example: freeze + + Example: to exploit the freeze mode added + in Linux 3.9, one can use systemctl suspend + with + [Sleep] +SuspendState=freeze + + + + See Also + + systemd-sleep8, + systemd-suspend.service8, + systemd-hibernate.service8, + systemd-hybrid-sleep.service8, + systemd1, + systemd.directives7 + + + + diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.xml b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.xml new file mode 100644 index 0000000000..a8beb86f4d --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-sleep.xml @@ -0,0 +1,146 @@ + + + + + + + + + systemd-suspend.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-suspend.service + 8 + + + + systemd-suspend.service + systemd-hibernate.service + systemd-hybrid-sleep.service + systemd-sleep + System sleep state logic + + + + systemd-suspend.service + systemd-hibernate.service + systemd-hybrid-sleep.service + /usr/lib/systemd/system-sleep + + + + Description + + systemd-suspend.service is a system + service that is pulled in by suspend.target + and is responsible for the actual system suspend. Similarly, + systemd-hibernate.service is pulled in by + hibernate.target to execute the actual + hibernation. Finally, + systemd-hybrid-sleep.service is pulled in by + hybrid-sleep.target to execute hybrid + hibernation with system suspend. + + Immediately before entering system suspend and/or + hibernation systemd-suspend.service (and the + other mentioned units, respectively) will run all executables in + /usr/lib/systemd/system-sleep/ and pass two + arguments to them. The first argument will be + pre, the second either + suspend, hibernate, or + hybrid-sleep depending on the chosen action. + Immediately after leaving system suspend and/or hibernation the + same executables are run, but the first argument is now + post. All executables in this directory are + executed in parallel, and execution of the action is not continued + until all executables have finished. + + Note that scripts or binaries dropped in + /usr/lib/systemd/system-sleep/ are intended + for local use only and should be considered hacks. If applications + want to be notified of system suspend/hibernation and resume, + there are much nicer interfaces available. + + Note that + systemd-suspend.service, + systemd-hibernate.service, and + systemd-hybrid-sleep.service + should never be executed directly. Instead, trigger system sleep + states with a command such as systemctl suspend + or similar. + + Internally, this service will echo a string like + mem into /sys/power/state, + to trigger the actual system suspend. What exactly is written + where can be configured in the [Sleep] section + of /etc/systemd/sleep.conf or a + sleep.conf.d file. See + systemd-sleep.conf5. + + + + + Options + + systemd-sleep understands the + following commands: + + + + + + + + + + + Suspend, hibernate, or put the system to + hybrid sleep. + + + + + + + See Also + + systemd-sleep.conf5, + systemd1, + systemctl1, + systemd.special7, + systemd-halt.service8 + + + + diff --git a/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in new file mode 100644 index 0000000000..3a702d2e22 --- /dev/null +++ b/src/grp-initprogs/grp-sleep/systemd-sleep/systemd-suspend.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Suspend +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep suspend diff --git a/src/grp-initprogs/systemd-backlight/Makefile b/src/grp-initprogs/systemd-backlight/Makefile new file mode 100644 index 0000000000..4a79889681 --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/Makefile @@ -0,0 +1,43 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_BACKLIGHT),) +rootlibexec_PROGRAMS += \ + systemd-backlight + +nodist_systemunit_DATA += \ + units/systemd-backlight@.service + +systemd_backlight_SOURCES = \ + src/backlight/backlight.c + +systemd_backlight_LDADD = \ + libsystemd-shared.la +endif # ENABLE_BACKLIGHT + +EXTRA_DIST += \ + units/systemd-backlight@.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-backlight/backlight.c b/src/grp-initprogs/systemd-backlight/backlight.c new file mode 100644 index 0000000000..aa8d852024 --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/backlight.c @@ -0,0 +1,434 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fileio.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" + +static struct udev_device *find_pci_or_platform_parent(struct udev_device *device) { + struct udev_device *parent; + const char *subsystem, *sysname; + + assert(device); + + parent = udev_device_get_parent(device); + if (!parent) + return NULL; + + subsystem = udev_device_get_subsystem(parent); + if (!subsystem) + return NULL; + + sysname = udev_device_get_sysname(parent); + if (!sysname) + return NULL; + + if (streq(subsystem, "drm")) { + const char *c; + + c = startswith(sysname, "card"); + if (!c) + return NULL; + + c += strspn(c, DIGITS); + if (*c == '-') { + /* A connector DRM device, let's ignore all but LVDS and eDP! */ + + if (!startswith(c, "-LVDS-") && + !startswith(c, "-Embedded DisplayPort-")) + return NULL; + } + + } else if (streq(subsystem, "pci")) { + const char *value; + + value = udev_device_get_sysattr_value(parent, "class"); + if (value) { + unsigned long class = 0; + + if (safe_atolu(value, &class) < 0) { + log_warning("Cannot parse PCI class %s of device %s:%s.", + value, subsystem, sysname); + return NULL; + } + + /* Graphics card */ + if (class == 0x30000) + return parent; + } + + } else if (streq(subsystem, "platform")) + return parent; + + return find_pci_or_platform_parent(parent); +} + +static bool same_device(struct udev_device *a, struct udev_device *b) { + assert(a); + assert(b); + + if (!streq_ptr(udev_device_get_subsystem(a), udev_device_get_subsystem(b))) + return false; + + if (!streq_ptr(udev_device_get_sysname(a), udev_device_get_sysname(b))) + return false; + + return true; +} + +static bool validate_device(struct udev *udev, struct udev_device *device) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *enumerate = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + struct udev_device *parent; + const char *v, *subsystem; + int r; + + assert(udev); + assert(device); + + /* Verify whether we should actually care for a specific + * backlight device. For backlight devices there might be + * multiple ways to access the same control: "firmware" + * (i.e. ACPI), "platform" (i.e. via the machine's EC) and + * "raw" (via the graphics card). In general we should prefer + * "firmware" (i.e. ACPI) or "platform" access over "raw" + * access, in order not to confuse the BIOS/EC, and + * compatibility with possible low-level hotkey handling of + * screen brightness. The kernel will already make sure to + * expose only one of "firmware" and "platform" for the same + * device to userspace. However, we still need to make sure + * that we use "raw" only if no "firmware" or "platform" + * device for the same device exists. */ + + subsystem = udev_device_get_subsystem(device); + if (!streq_ptr(subsystem, "backlight")) + return true; + + v = udev_device_get_sysattr_value(device, "type"); + if (!streq_ptr(v, "raw")) + return true; + + parent = find_pci_or_platform_parent(device); + if (!parent) + return true; + + subsystem = udev_device_get_subsystem(parent); + if (!subsystem) + return true; + + enumerate = udev_enumerate_new(udev); + if (!enumerate) + return true; + + r = udev_enumerate_add_match_subsystem(enumerate, "backlight"); + if (r < 0) + return true; + + r = udev_enumerate_scan_devices(enumerate); + if (r < 0) + return true; + + first = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *other; + struct udev_device *other_parent; + const char *other_subsystem; + + other = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!other) + return true; + + if (same_device(device, other)) + continue; + + v = udev_device_get_sysattr_value(other, "type"); + if (!streq_ptr(v, "platform") && !streq_ptr(v, "firmware")) + continue; + + /* OK, so there's another backlight device, and it's a + * platform or firmware device, so, let's see if we + * can verify it belongs to the same device as + * ours. */ + other_parent = find_pci_or_platform_parent(other); + if (!other_parent) + continue; + + if (same_device(parent, other_parent)) { + /* Both have the same PCI parent, that means + * we are out. */ + log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.", + udev_device_get_sysname(device), + udev_device_get_sysname(other)); + return false; + } + + other_subsystem = udev_device_get_subsystem(other_parent); + if (streq_ptr(other_subsystem, "platform") && streq_ptr(subsystem, "pci")) { + /* The other is connected to the platform bus + * and we are a PCI device, that also means we + * are out. */ + log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.", + udev_device_get_sysname(device), + udev_device_get_sysname(other)); + return false; + } + } + + return true; +} + +static unsigned get_max_brightness(struct udev_device *device) { + int r; + const char *max_brightness_str; + unsigned max_brightness; + + max_brightness_str = udev_device_get_sysattr_value(device, "max_brightness"); + if (!max_brightness_str) { + log_warning("Failed to read 'max_brightness' attribute."); + return 0; + } + + r = safe_atou(max_brightness_str, &max_brightness); + if (r < 0) { + log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str); + return 0; + } + + if (max_brightness <= 0) { + log_warning("Maximum brightness is 0, ignoring device."); + return 0; + } + + return max_brightness; +} + +/* Some systems turn the backlight all the way off at the lowest levels. + * clamp_brightness clamps the saved brightness to at least 1 or 5% of + * max_brightness in case of 'backlight' subsystem. This avoids preserving + * an unreadably dim screen, which would otherwise force the user to + * disable state restoration. */ +static void clamp_brightness(struct udev_device *device, char **value, unsigned max_brightness) { + int r; + unsigned brightness, new_brightness, min_brightness; + const char *subsystem; + + r = safe_atou(*value, &brightness); + if (r < 0) { + log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value); + return; + } + + subsystem = udev_device_get_subsystem(device); + if (streq_ptr(subsystem, "backlight")) + min_brightness = MAX(1U, max_brightness/20); + else + min_brightness = 0; + + new_brightness = CLAMP(brightness, min_brightness, max_brightness); + if (new_brightness != brightness) { + char *old_value = *value; + + r = asprintf(value, "%u", new_brightness); + if (r < 0) { + log_oom(); + return; + } + + log_info("Saved brightness %s %s to %s.", old_value, + new_brightness > brightness ? + "too low; increasing" : "too high; decreasing", + *value); + + free(old_value); + } +} + +int main(int argc, char *argv[]) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *saved = NULL, *ss = NULL, *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL; + const char *sysname, *path_id; + unsigned max_brightness; + int r; + + if (argc != 3) { + log_error("This program requires two arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = mkdir_p("/var/lib/systemd/backlight", 0755); + if (r < 0) { + log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m"); + return EXIT_FAILURE; + } + + udev = udev_new(); + if (!udev) { + log_oom(); + return EXIT_FAILURE; + } + + sysname = strchr(argv[2], ':'); + if (!sysname) { + log_error("Requires a subsystem and sysname pair specifying a backlight device."); + return EXIT_FAILURE; + } + + ss = strndup(argv[2], sysname - argv[2]); + if (!ss) { + log_oom(); + return EXIT_FAILURE; + } + + sysname++; + + if (!streq(ss, "backlight") && !streq(ss, "leds")) { + log_error("Not a backlight or LED device: '%s:%s'", ss, sysname); + return EXIT_FAILURE; + } + + errno = 0; + device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); + if (!device) { + if (errno > 0) + log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); + else + log_oom(); + + return EXIT_FAILURE; + } + + /* If max_brightness is 0, then there is no actual backlight + * device. This happens on desktops with Asus mainboards + * that load the eeepc-wmi module. + */ + max_brightness = get_max_brightness(device); + if (max_brightness == 0) + return EXIT_SUCCESS; + + escaped_ss = cescape(ss); + if (!escaped_ss) { + log_oom(); + return EXIT_FAILURE; + } + + escaped_sysname = cescape(sysname); + if (!escaped_sysname) { + log_oom(); + return EXIT_FAILURE; + } + + path_id = udev_device_get_property_value(device, "ID_PATH"); + if (path_id) { + escaped_path_id = cescape(path_id); + if (!escaped_path_id) { + log_oom(); + return EXIT_FAILURE; + } + + saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL); + } else + saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL); + + if (!saved) { + log_oom(); + return EXIT_FAILURE; + } + + /* If there are multiple conflicting backlight devices, then + * their probing at boot-time might happen in any order. This + * means the validity checking of the device then is not + * reliable, since it might not see other devices conflicting + * with a specific backlight. To deal with this, we will + * actively delete backlight state files at shutdown (where + * device probing should be complete), so that the validity + * check at boot time doesn't have to be reliable. */ + + if (streq(argv[1], "load")) { + _cleanup_free_ char *value = NULL; + const char *clamp; + + if (shall_restore_state() == 0) + return EXIT_SUCCESS; + + if (!validate_device(udev, device)) + return EXIT_SUCCESS; + + r = read_one_line_file(saved, &value); + if (r < 0) { + + if (r == -ENOENT) + return EXIT_SUCCESS; + + log_error_errno(r, "Failed to read %s: %m", saved); + return EXIT_FAILURE; + } + + clamp = udev_device_get_property_value(device, "ID_BACKLIGHT_CLAMP"); + if (!clamp || parse_boolean(clamp) != 0) /* default to clamping */ + clamp_brightness(device, &value, max_brightness); + + r = udev_device_set_sysattr_value(device, "brightness", value); + if (r < 0) { + log_error_errno(r, "Failed to write system 'brightness' attribute: %m"); + return EXIT_FAILURE; + } + + } else if (streq(argv[1], "save")) { + const char *value; + + if (!validate_device(udev, device)) { + unlink(saved); + return EXIT_SUCCESS; + } + + value = udev_device_get_sysattr_value(device, "brightness"); + if (!value) { + log_error("Failed to read system 'brightness' attribute"); + return EXIT_FAILURE; + } + + r = write_string_file(saved, value, WRITE_STRING_FILE_CREATE); + if (r < 0) { + log_error_errno(r, "Failed to write %s: %m", saved); + return EXIT_FAILURE; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in new file mode 100644 index 0000000000..5e6706c11c --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save Screen Backlight Brightness of %i +Documentation=man:systemd-backlight@.service(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/backlight +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-backlight load %i +ExecStop=@rootlibexecdir@/systemd-backlight save %i +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.xml b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.xml new file mode 100644 index 0000000000..3459ed8851 --- /dev/null +++ b/src/grp-initprogs/systemd-backlight/systemd-backlight@.service.xml @@ -0,0 +1,94 @@ + + + + + + + + systemd-backlight@.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-backlight@.service + 8 + + + + systemd-backlight@.service + systemd-backlight + Load and save the display backlight brightness at boot and shutdown + + + + systemd-backlight@.service + /usr/lib/systemd/systemd-backlight + + + + Description + + systemd-backlight@.service is a service + that restores the display backlight brightness at early boot and + saves it at shutdown. On disk, the backlight brightness is stored + in /var/lib/systemd/backlight/. During + loading, if the udev property is + not set to false, the brightness is clamped to a value of at + least 1 or 5% of maximum brightness, whichever is greater. This + restriction will be removed when the kernel allows user space to + reliably set a brightness value which does not turn off the + display. + + + + Kernel Command Line + + systemd-backlight understands the + following kernel command line parameter: + + + + systemd.restore_state= + + Takes a boolean argument. Defaults to + 1. If 0, does not + restore the backlight settings on boot. However, settings will + still be stored on shutdown. + + + + + + See Also + + systemd1 + + + + diff --git a/src/grp-initprogs/systemd-binfmt/Makefile b/src/grp-initprogs/systemd-binfmt/Makefile new file mode 100644 index 0000000000..d9e032d16d --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/Makefile @@ -0,0 +1,56 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_BINFMT),) +systemd_binfmt_SOURCES = \ + src/binfmt/binfmt.c + +systemd_binfmt_LDADD = \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-binfmt + +dist_systemunit_DATA += \ + units/proc-sys-fs-binfmt_misc.automount \ + units/proc-sys-fs-binfmt_misc.mount + +nodist_systemunit_DATA += \ + units/systemd-binfmt.service + +INSTALL_DIRS += \ + $(prefix)/lib/binfmt.d \ + $(sysconfdir)/binfmt.d + +SYSINIT_TARGET_WANTS += \ + systemd-binfmt.service \ + proc-sys-fs-binfmt_misc.automount + +endif # ENABLE_BINFMT + +EXTRA_DIST += \ + units/systemd-binfmt.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-binfmt/binfmt.c b/src/grp-initprogs/systemd-binfmt/binfmt.c new file mode 100644 index 0000000000..39bbdb5335 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/binfmt.c @@ -0,0 +1,203 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d"); + +static int delete_rule(const char *rule) { + _cleanup_free_ char *x = NULL, *fn = NULL; + char *e; + + assert(rule[0]); + + x = strdup(rule); + if (!x) + return log_oom(); + + e = strchrnul(x+1, x[0]); + *e = 0; + + fn = strappend("/proc/sys/fs/binfmt_misc/", x+1); + if (!fn) + return log_oom(); + + return write_string_file(fn, "-1", 0); +} + +static int apply_rule(const char *rule) { + int r; + + delete_rule(rule); + + r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule, 0); + if (r < 0) + return log_error_errno(r, "Failed to add binary format: %m"); + + return 0; +} + +static int apply_file(const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); + } + + log_debug("apply: %s", path); + for (;;) { + char l[LINE_MAX], *p; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + p = strstrip(l); + if (!*p) + continue; + if (strchr(COMMENTS "\n", *p)) + continue; + + k = apply_rule(p); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Registers binary formats.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, k; + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = apply_file(argv[i], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); + goto finish; + } + + /* Flush out all rules */ + write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0); + + STRV_FOREACH(f, files) { + k = apply_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-binfmt/binfmt.d.xml b/src/grp-initprogs/systemd-binfmt/binfmt.d.xml new file mode 100644 index 0000000000..5b63cfb4c3 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/binfmt.d.xml @@ -0,0 +1,101 @@ + + + + + + + + binfmt.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + binfmt.d + 5 + + + + binfmt.d + Configure additional binary formats for + executables at boot + + + + /etc/binfmt.d/*.conf + /run/binfmt.d/*.conf + /usr/lib/binfmt.d/*.conf + + + + Description + + At boot, + systemd-binfmt.service8 + reads configuration files from the above directories to register + in the kernel additional binary formats for executables. + + + + Configuration Format + + Each file contains a list of binfmt_misc kernel binary + format rules. Consult binfmt_misc.txt + for more information on registration of additional binary formats + and how to write rules. + + Empty lines and lines beginning with ; and # are ignored. + Note that this means you may not use ; and # as delimiter in + binary format rules. + + + + + + Example + + /etc/binfmt.d/wine.conf example: + + # Start WINE on Windows executables +:DOSWin:M::MZ::/usr/bin/wine: + + + + + See Also + + systemd1, + systemd-binfmt.service8, + systemd-delta1, + wine8 + + + + diff --git a/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount new file mode 100644 index 0000000000..6be38937b1 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.automount @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Arbitrary Executable File Formats File System Automount Point +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no +Before=sysinit.target +ConditionPathExists=/proc/sys/fs/binfmt_misc/ +ConditionPathIsReadWrite=/proc/sys/ + +[Automount] +Where=/proc/sys/fs/binfmt_misc diff --git a/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount new file mode 100644 index 0000000000..8c7c386318 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/proc-sys-fs-binfmt_misc.mount @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Arbitrary Executable File Formats File System +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no + +[Mount] +What=binfmt_misc +Where=/proc/sys/fs/binfmt_misc +Type=binfmt_misc diff --git a/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in new file mode 100644 index 0000000000..d53073ee61 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Set Up Additional Binary Formats +Documentation=man:systemd-binfmt.service(8) man:binfmt.d(5) +Documentation=https://www.kernel.org/doc/Documentation/binfmt_misc.txt +DefaultDependencies=no +Conflicts=shutdown.target +After=proc-sys-fs-binfmt_misc.automount +Before=sysinit.target shutdown.target +ConditionPathIsReadWrite=/proc/sys/ +ConditionDirectoryNotEmpty=|/lib/binfmt.d +ConditionDirectoryNotEmpty=|/usr/lib/binfmt.d +ConditionDirectoryNotEmpty=|/usr/local/lib/binfmt.d +ConditionDirectoryNotEmpty=|/etc/binfmt.d +ConditionDirectoryNotEmpty=|/run/binfmt.d + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-binfmt +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.xml b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.xml new file mode 100644 index 0000000000..cccfb49ca9 --- /dev/null +++ b/src/grp-initprogs/systemd-binfmt/systemd-binfmt.service.xml @@ -0,0 +1,75 @@ + + + + + + + + systemd-binfmt.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-binfmt.service + 8 + + + + systemd-binfmt.service + systemd-binfmt + Configure additional binary formats for executables at boot + + + + systemd-binfmt.service + /usr/lib/systemd/systemd-binfmt + + + + Description + + systemd-binfmt.service is an early boot + service that registers additional binary formats for executables + in the kernel. + + See + binfmt.d5 + for information about the configuration of this service. + + + + See Also + + systemd1, + binfmt.d5, + wine8 + + + + diff --git a/src/grp-initprogs/systemd-detect-virt/Makefile b/src/grp-initprogs/systemd-detect-virt/Makefile new file mode 100644 index 0000000000..7158be148a --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/Makefile @@ -0,0 +1,36 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-detect-virt +systemd_detect_virt_SOURCES = \ + src/detect-virt/detect-virt.c + +systemd_detect_virt_LDADD = \ + libsystemd-shared.la + +INSTALL_EXEC_HOOKS += \ + systemd-detect-virt-install-hook + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-detect-virt/detect-virt.c b/src/grp-initprogs/systemd-detect-virt/detect-virt.c new file mode 100644 index 0000000000..06cdab5297 --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/detect-virt.c @@ -0,0 +1,169 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/util.h" +#include "basic/virt.h" + +static bool arg_quiet = false; +static enum { + ANY_VIRTUALIZATION, + ONLY_VM, + ONLY_CONTAINER, + ONLY_CHROOT, +} arg_mode = ANY_VIRTUALIZATION; + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Detect execution in a virtualized environment.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -c --container Only detect whether we are run in a container\n" + " -v --vm Only detect whether we are run in a VM\n" + " -r --chroot Detect whether we are run in a chroot() environment\n" + " -q --quiet Don't output anything, just set return value\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "container", no_argument, NULL, 'c' }, + { "vm", no_argument, NULL, 'v' }, + { "chroot", no_argument, NULL, 'r' }, + { "quiet", no_argument, NULL, 'q' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'q': + arg_quiet = true; + break; + + case 'c': + arg_mode = ONLY_CONTAINER; + break; + + case 'v': + arg_mode = ONLY_VM; + break; + + case 'r': + arg_mode = ONLY_CHROOT; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + log_error("%s takes no arguments.", program_invocation_short_name); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a virtualized + * environment or not */ + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + switch (arg_mode) { + + case ONLY_VM: + r = detect_vm(); + if (r < 0) { + log_error_errno(r, "Failed to check for VM: %m"); + return EXIT_FAILURE; + } + + break; + + case ONLY_CONTAINER: + r = detect_container(); + if (r < 0) { + log_error_errno(r, "Failed to check for container: %m"); + return EXIT_FAILURE; + } + + break; + + case ONLY_CHROOT: + r = running_in_chroot(); + if (r < 0) { + log_error_errno(r, "Failed to check for chroot() environment: %m"); + return EXIT_FAILURE; + } + + return r ? EXIT_SUCCESS : EXIT_FAILURE; + + case ANY_VIRTUALIZATION: + default: + r = detect_virtualization(); + if (r < 0) { + log_error_errno(r, "Failed to check for virtualization: %m"); + return EXIT_FAILURE; + } + + break; + } + + if (!arg_quiet) + puts(virtualization_to_string(r)); + + return r != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash new file mode 100644 index 0000000000..df06c29841 --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.bash @@ -0,0 +1,40 @@ +# systemd-detect-virt(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd_detect_virt() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version -c --container -v --vm -q --quiet' + ) + + _init_completion || return + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_detect_virt systemd-detect-virt diff --git a/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh new file mode 100644 index 0000000000..a0c7df727c --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.completion.zsh @@ -0,0 +1,11 @@ +#compdef systemd-detect-virt + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + {-c,--container}'[Only detect whether we are run in a container]' \ + {-v,--vm}'[Only detect whether we are run in a VM]' \ + {-q,--quiet}"[Don't output anything, just set return value]" + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.xml b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.xml new file mode 100644 index 0000000000..2b7f4e69ab --- /dev/null +++ b/src/grp-initprogs/systemd-detect-virt/systemd-detect-virt.xml @@ -0,0 +1,245 @@ + + + + + + + + + systemd-detect-virt + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-detect-virt + 1 + + + + systemd-detect-virt + Detect execution in a virtualized environment + + + + + systemd-detect-virt OPTIONS + + + + + Description + + systemd-detect-virt detects execution in + a virtualized environment. It identifies the virtualization + technology and can distinguish full machine virtualization from + container virtualization. systemd-detect-virt + exits with a return value of 0 (success) if a virtualization + technology is detected, and non-zero (error) otherwise. By default, + any type of virtualization is detected, and the options + and can be used + to limit what types of virtualization are detected. + + When executed without will print a + short identifier for the detected virtualization technology. The + following technologies are currently identified: + + + Known virtualization technologies (both + VM, i.e. full hardware virtualization, + and container, i.e. shared kernel virtualization) + + + + + + + Type + ID + Product + + + + + VM + qemu + QEMU software virtualization + + + + kvm + Linux KVM kernel virtual machine + + + + zvm + s390 z/VM + + + + vmware + VMware Workstation or Server, and related products + + + + microsoft + Hyper-V, also known as Viridian or Windows Server Virtualization + + + + oracle + Oracle VM VirtualBox (historically marketed by innotek and Sun Microsystems) + + + + xen + Xen hypervisor (only domU, not dom0) + + + + bochs + Bochs Emulator + + + + uml + User-mode Linux + + + + parallels + Parallels Desktop, Parallels Server + + + + Container + openvz + OpenVZ/Virtuozzo + + + + lxc + Linux container implementation by LXC + + + + lxc-libvirt + Linux container implementation by libvirt + + + + systemd-nspawn + systemd's minimal container implementation, see systemd-nspawn1 + + + + docker + Docker container manager + + + + rkt + rkt app container runtime + + + +
+ + If multiple virtualization solutions are used, only the + "innermost" is detected and identified. That means if both + machine and container virtualization are used in + conjunction, only the latter will be identified (unless + is passed). +
+ + + Options + + The following options are understood: + + + + + + + Only detects container virtualization (i.e. + shared kernel virtualization). + + + + + + + Only detects hardware virtualization). + + + + + + + Detect whether invoked in a + chroot2 + environment. In this mode, no output is written, but the return + value indicates whether the process was invoked in a + chroot() + environment or not. + + + + + + + Suppress output of the virtualization + technology identifier. + + + + + + + + + + Exit status + + If a virtualization technology is detected, 0 is returned, a + non-zero code otherwise. + + + + See Also + + systemd1, + systemd-nspawn1, + chroot2 + + + +
diff --git a/src/grp-initprogs/systemd-firstboot/Makefile b/src/grp-initprogs/systemd-firstboot/Makefile new file mode 100644 index 0000000000..20ea125a52 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/Makefile @@ -0,0 +1,47 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_FIRSTBOOT),) +systemd_firstboot_SOURCES = \ + src/firstboot/firstboot.c + +systemd_firstboot_LDADD = \ + libsystemd-shared.la \ + -lcrypt + +rootbin_PROGRAMS += \ + systemd-firstboot + +nodist_systemunit_DATA += \ + units/systemd-firstboot.service + +SYSINIT_TARGET_WANTS += \ + systemd-firstboot.service +endif # ENABLE_FIRSTBOOT + +EXTRA_DIST += \ + units/systemd-firstboot.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-firstboot/firstboot.c b/src/grp-initprogs/systemd-firstboot/firstboot.c new file mode 100644 index 0000000000..7308e108e8 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/firstboot.c @@ -0,0 +1,870 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/locale-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "shared/ask-password-api.h" + +static char *arg_root = NULL; +static char *arg_locale = NULL; /* $LANG */ +static char *arg_locale_messages = NULL; /* $LC_MESSAGES */ +static char *arg_timezone = NULL; +static char *arg_hostname = NULL; +static sd_id128_t arg_machine_id = {}; +static char *arg_root_password = NULL; +static bool arg_prompt_locale = false; +static bool arg_prompt_timezone = false; +static bool arg_prompt_hostname = false; +static bool arg_prompt_root_password = false; +static bool arg_copy_locale = false; +static bool arg_copy_timezone = false; +static bool arg_copy_root_password = false; + +static bool press_any_key(void) { + char k = 0; + bool need_nl = true; + + printf("-- Press any key to proceed --"); + fflush(stdout); + + (void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl); + + if (need_nl) + putchar('\n'); + + return k != 'q'; +} + +static void print_welcome(void) { + _cleanup_free_ char *pretty_name = NULL; + const char *os_release = NULL; + static bool done = false; + int r; + + if (done) + return; + + os_release = prefix_roota(arg_root, "/etc/os-release"); + r = parse_env_file(os_release, NEWLINE, + "PRETTY_NAME", &pretty_name, + NULL); + if (r == -ENOENT) { + + os_release = prefix_roota(arg_root, "/usr/lib/os-release"); + r = parse_env_file(os_release, NEWLINE, + "PRETTY_NAME", &pretty_name, + NULL); + } + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read os-release file: %m"); + + printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", + isempty(pretty_name) ? "GNU/Linux" : pretty_name); + + press_any_key(); + + done = true; +} + +static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage) { + unsigned n, per_column, i, j; + unsigned break_lines, break_modulo; + + assert(n_columns > 0); + + n = strv_length(x); + per_column = (n + n_columns - 1) / n_columns; + + break_lines = lines(); + if (break_lines > 2) + break_lines--; + + /* The first page gets two extra lines, since we want to show + * a title */ + break_modulo = break_lines; + if (break_modulo > 3) + break_modulo -= 3; + + for (i = 0; i < per_column; i++) { + + for (j = 0; j < n_columns; j ++) { + _cleanup_free_ char *e = NULL; + + if (j * per_column + i >= n) + break; + + e = ellipsize(x[j * per_column + i], width, percentage); + if (!e) + return log_oom(); + + printf("%4u) %-*s", j * per_column + i + 1, width, e); + } + + putchar('\n'); + + /* on the first screen we reserve 2 extra lines for the title */ + if (i % break_lines == break_modulo) { + if (!press_any_key()) + return 0; + } + } + + return 0; +} + +static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *name), char **ret) { + int r; + + assert(text); + assert(is_valid); + assert(ret); + + for (;;) { + _cleanup_free_ char *p = NULL; + unsigned u; + + r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); + if (r < 0) + return log_error_errno(r, "Failed to query user: %m"); + + if (isempty(p)) { + log_warning("No data entered, skipping."); + return 0; + } + + r = safe_atou(p, &u); + if (r >= 0) { + char *c; + + if (u <= 0 || u > strv_length(l)) { + log_error("Specified entry number out of range."); + continue; + } + + log_info("Selected '%s'.", l[u-1]); + + c = strdup(l[u-1]); + if (!c) + return log_oom(); + + free(*ret); + *ret = c; + return 0; + } + + if (!is_valid(p)) { + log_error("Entered data invalid."); + continue; + } + + free(*ret); + *ret = p; + p = 0; + return 0; + } +} + +static int prompt_locale(void) { + _cleanup_strv_free_ char **locales = NULL; + int r; + + if (arg_locale || arg_locale_messages) + return 0; + + if (!arg_prompt_locale) + return 0; + + r = get_locales(&locales); + if (r < 0) + return log_error_errno(r, "Cannot query locales list: %m"); + + print_welcome(); + + printf("\nAvailable Locales:\n\n"); + r = show_menu(locales, 3, 22, 60); + if (r < 0) + return r; + + putchar('\n'); + + r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); + if (r < 0) + return r; + + if (isempty(arg_locale)) + return 0; + + r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); + if (r < 0) + return r; + + return 0; +} + +static int process_locale(void) { + const char *etc_localeconf; + char* locales[3]; + unsigned i = 0; + int r; + + etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); + if (laccess(etc_localeconf, F_OK) >= 0) + return 0; + + if (arg_copy_locale && arg_root) { + + mkdir_parents(etc_localeconf, 0755); + r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0); + if (r != -ENOENT) { + if (r < 0) + return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); + + log_info("%s copied.", etc_localeconf); + return 0; + } + } + + r = prompt_locale(); + if (r < 0) + return r; + + if (!isempty(arg_locale)) + locales[i++] = strjoina("LANG=", arg_locale); + if (!isempty(arg_locale_messages) && !streq(arg_locale_messages, arg_locale)) + locales[i++] = strjoina("LC_MESSAGES=", arg_locale_messages); + + if (i == 0) + return 0; + + locales[i] = NULL; + + mkdir_parents(etc_localeconf, 0755); + r = write_env_file(etc_localeconf, locales); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); + + log_info("%s written.", etc_localeconf); + return 0; +} + +static int prompt_timezone(void) { + _cleanup_strv_free_ char **zones = NULL; + int r; + + if (arg_timezone) + return 0; + + if (!arg_prompt_timezone) + return 0; + + r = get_timezones(&zones); + if (r < 0) + return log_error_errno(r, "Cannot query timezone list: %m"); + + print_welcome(); + + printf("\nAvailable Time Zones:\n\n"); + r = show_menu(zones, 3, 22, 30); + if (r < 0) + return r; + + putchar('\n'); + + r = prompt_loop("Please enter timezone name or number", zones, timezone_is_valid, &arg_timezone); + if (r < 0) + return r; + + return 0; +} + +static int process_timezone(void) { + const char *etc_localtime, *e; + int r; + + etc_localtime = prefix_roota(arg_root, "/etc/localtime"); + if (laccess(etc_localtime, F_OK) >= 0) + return 0; + + if (arg_copy_timezone && arg_root) { + _cleanup_free_ char *p = NULL; + + r = readlink_malloc("/etc/localtime", &p); + if (r != -ENOENT) { + if (r < 0) + return log_error_errno(r, "Failed to read host timezone: %m"); + + mkdir_parents(etc_localtime, 0755); + if (symlink(p, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); + + log_info("%s copied.", etc_localtime); + return 0; + } + } + + r = prompt_timezone(); + if (r < 0) + return r; + + if (isempty(arg_timezone)) + return 0; + + e = strjoina("../usr/share/zoneinfo/", arg_timezone); + + mkdir_parents(etc_localtime, 0755); + if (symlink(e, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); + + log_info("%s written", etc_localtime); + return 0; +} + +static int prompt_hostname(void) { + int r; + + if (arg_hostname) + return 0; + + if (!arg_prompt_hostname) + return 0; + + print_welcome(); + putchar('\n'); + + for (;;) { + _cleanup_free_ char *h = NULL; + + r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); + if (r < 0) + return log_error_errno(r, "Failed to query hostname: %m"); + + if (isempty(h)) { + log_warning("No hostname entered, skipping."); + break; + } + + if (!hostname_is_valid(h, true)) { + log_error("Specified hostname invalid."); + continue; + } + + /* Get rid of the trailing dot that we allow, but don't want to see */ + arg_hostname = hostname_cleanup(h); + h = NULL; + break; + } + + return 0; +} + +static int process_hostname(void) { + const char *etc_hostname; + int r; + + etc_hostname = prefix_roota(arg_root, "/etc/hostname"); + if (laccess(etc_hostname, F_OK) >= 0) + return 0; + + r = prompt_hostname(); + if (r < 0) + return r; + + if (isempty(arg_hostname)) + return 0; + + mkdir_parents(etc_hostname, 0755); + r = write_string_file(etc_hostname, arg_hostname, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_hostname); + + log_info("%s written.", etc_hostname); + return 0; +} + +static int process_machine_id(void) { + const char *etc_machine_id; + char id[SD_ID128_STRING_MAX]; + int r; + + etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); + if (laccess(etc_machine_id, F_OK) >= 0) + return 0; + + if (sd_id128_is_null(arg_machine_id)) + return 0; + + mkdir_parents(etc_machine_id, 0755); + r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id), WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_error_errno(r, "Failed to write machine id: %m"); + + log_info("%s written.", etc_machine_id); + return 0; +} + +static int prompt_root_password(void) { + const char *msg1, *msg2, *etc_shadow; + int r; + + if (arg_root_password) + return 0; + + if (!arg_prompt_root_password) + return 0; + + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); + if (laccess(etc_shadow, F_OK) >= 0) + return 0; + + print_welcome(); + putchar('\n'); + + msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); + msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); + + for (;;) { + _cleanup_string_free_erase_ char *a = NULL, *b = NULL; + + r = ask_password_tty(msg1, NULL, 0, 0, NULL, &a); + if (r < 0) + return log_error_errno(r, "Failed to query root password: %m"); + + if (isempty(a)) { + log_warning("No password entered, skipping."); + break; + } + + r = ask_password_tty(msg2, NULL, 0, 0, NULL, &b); + if (r < 0) + return log_error_errno(r, "Failed to query root password: %m"); + + if (!streq(a, b)) { + log_error("Entered passwords did not match, please try again."); + continue; + } + + arg_root_password = a; + a = NULL; + break; + } + + return 0; +} + +static int write_root_shadow(const char *path, const struct spwd *p) { + _cleanup_fclose_ FILE *f = NULL; + assert(path); + assert(p); + + RUN_WITH_UMASK(0777) + f = fopen(path, "wex"); + if (!f) + return -errno; + + errno = 0; + if (putspent(p, f) != 0) + return errno > 0 ? -errno : -EIO; + + return fflush_and_check(f); +} + +static int process_root_password(void) { + + static const char table[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "./"; + + struct spwd item = { + .sp_namp = (char*) "root", + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + _cleanup_close_ int lock = -1; + char salt[3+16+1+1]; + uint8_t raw[16]; + unsigned i; + char *j; + + const char *etc_shadow; + int r; + + etc_shadow = prefix_roota(arg_root, "/etc/shadow"); + if (laccess(etc_shadow, F_OK) >= 0) + return 0; + + mkdir_parents(etc_shadow, 0755); + + lock = take_etc_passwd_lock(arg_root); + if (lock < 0) + return log_error_errno(lock, "Failed to take a lock: %m"); + + if (arg_copy_root_password && arg_root) { + struct spwd *p; + + errno = 0; + p = getspnam("root"); + if (p || errno != ENOENT) { + if (!p) { + if (!errno) + errno = EIO; + + return log_error_errno(errno, "Failed to find shadow entry for root: %m"); + } + + r = write_root_shadow(etc_shadow, p); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); + + log_info("%s copied.", etc_shadow); + return 0; + } + } + + r = prompt_root_password(); + if (r < 0) + return r; + + if (!arg_root_password) + return 0; + + r = dev_urandom(raw, 16); + if (r < 0) + return log_error_errno(r, "Failed to get salt: %m"); + + /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ + assert_cc(sizeof(table) == 64 + 1); + j = stpcpy(salt, "$6$"); + for (i = 0; i < 16; i++) + j[i] = table[raw[i] & 63]; + j[i++] = '$'; + j[i] = 0; + + errno = 0; + item.sp_pwdp = crypt(arg_root_password, salt); + if (!item.sp_pwdp) { + if (!errno) + errno = EINVAL; + + return log_error_errno(errno, "Failed to encrypt password: %m"); + } + + item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + + r = write_root_shadow(etc_shadow, &item); + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); + + log_info("%s written.", etc_shadow); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Configures basic settings of the system.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + " --locale=LOCALE Set primary locale (LANG=)\n" + " --locale-messages=LOCALE Set message locale (LC_MESSAGES=)\n" + " --timezone=TIMEZONE Set timezone\n" + " --hostname=NAME Set host name\n" + " --machine-ID=ID Set machine ID\n" + " --root-password=PASSWORD Set root password\n" + " --root-password-file=FILE Set root password from file\n" + " --prompt-locale Prompt the user for locale settings\n" + " --prompt-timezone Prompt the user for timezone\n" + " --prompt-hostname Prompt the user for hostname\n" + " --prompt-root-password Prompt the user for root password\n" + " --prompt Prompt for all of the above\n" + " --copy-locale Copy locale from host\n" + " --copy-timezone Copy timezone from host\n" + " --copy-root-password Copy root password from host\n" + " --copy Copy locale, timezone, root password\n" + " --setup-machine-id Generate a new random machine ID\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + ARG_LOCALE, + ARG_LOCALE_MESSAGES, + ARG_TIMEZONE, + ARG_HOSTNAME, + ARG_MACHINE_ID, + ARG_ROOT_PASSWORD, + ARG_ROOT_PASSWORD_FILE, + ARG_PROMPT, + ARG_PROMPT_LOCALE, + ARG_PROMPT_TIMEZONE, + ARG_PROMPT_HOSTNAME, + ARG_PROMPT_ROOT_PASSWORD, + ARG_COPY, + ARG_COPY_LOCALE, + ARG_COPY_TIMEZONE, + ARG_COPY_ROOT_PASSWORD, + ARG_SETUP_MACHINE_ID, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + { "locale", required_argument, NULL, ARG_LOCALE }, + { "locale-messages", required_argument, NULL, ARG_LOCALE_MESSAGES }, + { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "root-password", required_argument, NULL, ARG_ROOT_PASSWORD }, + { "root-password-file", required_argument, NULL, ARG_ROOT_PASSWORD_FILE }, + { "prompt", no_argument, NULL, ARG_PROMPT }, + { "prompt-locale", no_argument, NULL, ARG_PROMPT_LOCALE }, + { "prompt-timezone", no_argument, NULL, ARG_PROMPT_TIMEZONE }, + { "prompt-hostname", no_argument, NULL, ARG_PROMPT_HOSTNAME }, + { "prompt-root-password", no_argument, NULL, ARG_PROMPT_ROOT_PASSWORD }, + { "copy", no_argument, NULL, ARG_COPY }, + { "copy-locale", no_argument, NULL, ARG_COPY_LOCALE }, + { "copy-timezone", no_argument, NULL, ARG_COPY_TIMEZONE }, + { "copy-root-password", no_argument, NULL, ARG_COPY_ROOT_PASSWORD }, + { "setup-machine-id", no_argument, NULL, ARG_SETUP_MACHINE_ID }, + {} + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case ARG_LOCALE: + if (!locale_is_valid(optarg)) { + log_error("Locale %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_locale, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_LOCALE_MESSAGES: + if (!locale_is_valid(optarg)) { + log_error("Locale %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_locale_messages, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_TIMEZONE: + if (!timezone_is_valid(optarg)) { + log_error("Timezone %s is not valid.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_timezone, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_ROOT_PASSWORD: + r = free_and_strdup(&arg_root_password, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_ROOT_PASSWORD_FILE: + arg_root_password = mfree(arg_root_password); + + r = read_one_line_file(optarg, &arg_root_password); + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", optarg); + + break; + + case ARG_HOSTNAME: + if (!hostname_is_valid(optarg, true)) { + log_error("Host name %s is not valid.", optarg); + return -EINVAL; + } + + hostname_cleanup(optarg); + r = free_and_strdup(&arg_hostname, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_MACHINE_ID: + if (sd_id128_from_string(optarg, &arg_machine_id) < 0) { + log_error("Failed to parse machine id %s.", optarg); + return -EINVAL; + } + + break; + + case ARG_PROMPT: + arg_prompt_locale = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true; + break; + + case ARG_PROMPT_LOCALE: + arg_prompt_locale = true; + break; + + case ARG_PROMPT_TIMEZONE: + arg_prompt_timezone = true; + break; + + case ARG_PROMPT_HOSTNAME: + arg_prompt_hostname = true; + break; + + case ARG_PROMPT_ROOT_PASSWORD: + arg_prompt_root_password = true; + break; + + case ARG_COPY: + arg_copy_locale = arg_copy_timezone = arg_copy_root_password = true; + break; + + case ARG_COPY_LOCALE: + arg_copy_locale = true; + break; + + case ARG_COPY_TIMEZONE: + arg_copy_timezone = true; + break; + + case ARG_COPY_ROOT_PASSWORD: + arg_copy_root_password = true; + break; + + case ARG_SETUP_MACHINE_ID: + + r = sd_id128_randomize(&arg_machine_id); + if (r < 0) + return log_error_errno(r, "Failed to generate randomized machine ID: %m"); + + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = process_locale(); + if (r < 0) + goto finish; + + r = process_timezone(); + if (r < 0) + goto finish; + + r = process_hostname(); + if (r < 0) + goto finish; + + r = process_machine_id(); + if (r < 0) + goto finish; + + r = process_root_password(); + if (r < 0) + goto finish; + +finish: + free(arg_root); + free(arg_locale); + free(arg_locale_messages); + free(arg_timezone); + free(arg_hostname); + string_erase(arg_root_password); + free(arg_root_password); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in new file mode 100644 index 0000000000..405c6f3fd2 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=First Boot Wizard +Documentation=man:systemd-firstboot(1) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=systemd-sysusers.service sysinit.target shutdown.target +ConditionPathIsReadWrite=/etc +ConditionFirstBoot=yes + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password +StandardOutput=tty +StandardInput=tty +StandardError=tty diff --git a/src/grp-initprogs/systemd-firstboot/systemd-firstboot.xml b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.xml new file mode 100644 index 0000000000..b269e48113 --- /dev/null +++ b/src/grp-initprogs/systemd-firstboot/systemd-firstboot.xml @@ -0,0 +1,259 @@ + + + + + + + + + systemd-firstboot + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-firstboot + 1 + + + + systemd-firstboot + systemd-firstboot.service + Initialize basic system settings on or before the first boot-up of a system + + + + + systemd-firstboot + OPTIONS + + + systemd-firstboot.service + + + + Description + + systemd-firstboot initializes the most + basic system settings interactively on the first boot, or + optionally non-interactively when a system image is created. The + following settings may be set up: + + + The system locale, more specifically the two + locale variables LANG= and + LC_MESSAGES + + The system time zone + + The system host name + + The machine ID of the system + + The root user's password + + + Each of the fields may either be queried interactively by + users, set non-interactively on the tool's command line, or be + copied from a host system that is used to set up the system + image. + + If a setting is already initialized, it will not be + overwritten and the user will not be prompted for the + setting. + + Note that this tool operates directly on the file system and + does not involve any running system services, unlike + localectl1, + timedatectl1 + or + hostnamectl1. + This allows systemd-firstboot to operate on + mounted but not booted disk images and in early boot. It is not + recommended to use systemd-firstboot on the + running system while it is up. + + + + Options + + The following options are understood: + + + + + Takes a directory path as an argument. All + paths will be prefixed with the given alternate + root path, including config search + paths. This is useful to operate on a system image mounted to + the specified directory instead of the host system itself. + + + + + + + + Sets the system locale, more specifically the + LANG= and LC_MESSAGES + settings. The argument should be a valid locale identifier, + such as de_DE.UTF-8. This controls the + locale.conf5 + configuration file. + + + + + + Sets the system time zone. The argument should + be a valid time zone identifier, such as + Europe/Berlin. This controls the + localtime5 + symlink. + + + + + + Sets the system hostname. The argument should + be a host name, compatible with DNS. This controls the + hostname5 + configuration file. + + + + + + Sets the system's machine ID. This controls + the + machine-id5 + file. + + + + + + + Sets the password of the system's root user. + This creates a + shadow5 + file. This setting exists in two forms: + accepts the password to set + directly on the command line, and + reads it from a file. + Note that it is not recommended to specify passwords on the + command line, as other users might be able to see them simply + by invoking + ps1. + + + + + + + + + Prompt the user interactively for a specific + basic setting. Note that any explicit configuration settings + specified on the command line take precedence, and the user is + not prompted for it. + + + + + + Query the user for locale, timezone, hostname + and root password. This is equivalent to specifying + , + , + , + in combination. + + + + + + + + + Copy a specific basic setting from the host. + This only works in combination with + (see above). + + + + + + Copy locale, time zone and root password from + the host. This is equivalent to specifying + , + , + in combination. + + + + + + + Initialize the system's machine ID to a random + ID. This only works in combination with + . + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + locale.conf5, + localtime5, + hostname5, + machine-id5, + shadow5, + systemd-machine-id-setup1, + localectl1, + timedatectl1, + hostnamectl1 + + + + diff --git a/src/grp-initprogs/systemd-fsck/Makefile b/src/grp-initprogs/systemd-fsck/Makefile new file mode 100644 index 0000000000..8a223a7f70 --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-fsck +systemd_fsck_SOURCES = \ + src/fsck/fsck.c + +systemd_fsck_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-fsck/fsck.c b/src/grp-initprogs/systemd-fsck/fsck.c new file mode 100644 index 0000000000..4ca018763f --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/fsck.c @@ -0,0 +1,487 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2014 Holger Hans Peter Freyther + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" +#include "shared/bus-util.h" + +/* exit codes as defined in fsck(8) */ +enum { + FSCK_SUCCESS = 0, + FSCK_ERROR_CORRECTED = 1, + FSCK_SYSTEM_SHOULD_REBOOT = 2, + FSCK_ERRORS_LEFT_UNCORRECTED = 4, + FSCK_OPERATIONAL_ERROR = 8, + FSCK_USAGE_OR_SYNTAX_ERROR = 16, + FSCK_USER_CANCELLED = 32, + FSCK_SHARED_LIB_ERROR = 128, +}; + +static bool arg_skip = false; +static bool arg_force = false; +static bool arg_show_progress = false; +static const char *arg_repair = "-a"; + +static void start_target(const char *target, const char *mode) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(target); + + r = bus_connect_system_systemd(&bus); + if (r < 0) { + log_error_errno(r, "Failed to get D-Bus connection: %m"); + return; + } + + log_info("Running request %s/start/replace", target); + + /* Start these units only if we can replace base.target with it */ + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnitReplace", + &error, + NULL, + "sss", "basic.target", target, mode); + + /* Don't print a warning if we aren't called during startup */ + if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB)) + log_error("Failed to start unit: %s", bus_error_message(&error, r)); +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + assert(key); + + if (streq(key, "fsck.mode") && value) { + + if (streq(value, "auto")) + arg_force = arg_skip = false; + else if (streq(value, "force")) + arg_force = true; + else if (streq(value, "skip")) + arg_skip = true; + else + log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value); + + } else if (streq(key, "fsck.repair") && value) { + + if (streq(value, "preen")) + arg_repair = "-a"; + else { + r = parse_boolean(value); + if (r > 0) + arg_repair = "-y"; + else if (r == 0) + arg_repair = "-n"; + else + log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value); + } + } + +#ifdef HAVE_SYSV_COMPAT + else if (streq(key, "fastboot") && !value) { + log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line."); + arg_skip = true; + + } else if (streq(key, "forcefsck") && !value) { + log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line."); + arg_force = true; + } +#endif + + return 0; +} + +static void test_files(void) { + +#ifdef HAVE_SYSV_COMPAT + if (access("/fastboot", F_OK) >= 0) { + log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system."); + arg_skip = true; + } + + if (access("/forcefsck", F_OK) >= 0) { + log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system."); + arg_force = true; + } +#endif + + arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0; +} + +static double percent(int pass, unsigned long cur, unsigned long max) { + /* Values stolen from e2fsck */ + + static const int pass_table[] = { + 0, 70, 90, 92, 95, 100 + }; + + if (pass <= 0) + return 0.0; + + if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0) + return 100.0; + + return (double) pass_table[pass-1] + + ((double) pass_table[pass] - (double) pass_table[pass-1]) * + (double) cur / (double) max; +} + +static int process_progress(int fd) { + _cleanup_fclose_ FILE *console = NULL, *f = NULL; + usec_t last = 0; + bool locked = false; + int clear = 0, r; + + /* No progress pipe to process? Then we are a NOP. */ + if (fd < 0) + return 0; + + f = fdopen(fd, "re"); + if (!f) { + safe_close(fd); + return -errno; + } + + console = fopen("/dev/console", "we"); + if (!console) + return -ENOMEM; + + for (;;) { + int pass, m; + unsigned long cur, max; + _cleanup_free_ char *device = NULL; + double p; + usec_t t; + + if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) { + + if (ferror(f)) + r = log_warning_errno(errno, "Failed to read from progress pipe: %m"); + else if (feof(f)) + r = 0; + else { + log_warning("Failed to parse progress pipe data"); + r = -EBADMSG; + } + break; + } + + /* Only show one progress counter at max */ + if (!locked) { + if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) + continue; + + locked = true; + } + + /* Only update once every 50ms */ + t = now(CLOCK_MONOTONIC); + if (last + 50 * USEC_PER_MSEC > t) + continue; + + last = t; + + p = percent(pass, cur, max); + fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m); + fflush(console); + + if (m > clear) + clear = m; + } + + if (clear > 0) { + unsigned j; + + fputc('\r', console); + for (j = 0; j < (unsigned) clear; j++) + fputc(' ', console); + fputc('\r', console); + fflush(console); + } + + return r; +} + +static int fsck_progress_socket(void) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/fsck.progress", + }; + + int fd, r; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return log_warning_errno(errno, "socket(): %m"); + + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); + safe_close(fd); + return r; + } + + return fd; +} + +int main(int argc, char *argv[]) { + _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *device, *type; + bool root_directory; + siginfo_t status; + struct stat st; + int r; + pid_t pid; + + if (argc > 2) { + log_error("This program expects one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + test_files(); + + if (!arg_force && arg_skip) { + r = 0; + goto finish; + } + + if (argc > 1) { + device = argv[1]; + + if (stat(device, &st) < 0) { + r = log_error_errno(errno, "Failed to stat %s: %m", device); + goto finish; + } + + if (!S_ISBLK(st.st_mode)) { + log_error("%s is not a block device.", device); + r = -EINVAL; + goto finish; + } + + r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); + if (r < 0) { + log_error_errno(r, "Failed to detect device %s: %m", device); + goto finish; + } + + root_directory = false; + } else { + struct timespec times[2]; + + /* Find root device */ + + if (stat("/", &st) < 0) { + r = log_error_errno(errno, "Failed to stat() the root directory: %m"); + goto finish; + } + + /* Virtual root devices don't need an fsck */ + if (major(st.st_dev) == 0) { + log_debug("Root directory is virtual or btrfs, skipping check."); + r = 0; + goto finish; + } + + /* check if we are already writable */ + times[0] = st.st_atim; + times[1] = st.st_mtim; + + if (utimensat(AT_FDCWD, "/", times, 0) == 0) { + log_info("Root directory is writable, skipping check."); + r = 0; + goto finish; + } + + r = sd_device_new_from_devnum(&dev, 'b', st.st_dev); + if (r < 0) { + log_error_errno(r, "Failed to detect root device: %m"); + goto finish; + } + + r = sd_device_get_devname(dev, &device); + if (r < 0) { + log_error_errno(r, "Failed to detect device node of root directory: %m"); + goto finish; + } + + root_directory = true; + } + + r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type); + if (r >= 0) { + r = fsck_exists(type); + if (r < 0) + log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device); + else if (r == 0) { + log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device); + goto finish; + } + } + + if (arg_show_progress) { + if (pipe(progress_pipe) < 0) { + r = log_error_errno(errno, "pipe(): %m"); + goto finish; + } + } + + pid = fork(); + if (pid < 0) { + r = log_error_errno(errno, "fork(): %m"); + goto finish; + } + if (pid == 0) { + char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1]; + int progress_socket = -1; + const char *cmdline[9]; + int i = 0; + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + /* Close the reading side of the progress pipe */ + progress_pipe[0] = safe_close(progress_pipe[0]); + + /* Try to connect to a progress management daemon, if there is one */ + progress_socket = fsck_progress_socket(); + if (progress_socket >= 0) { + /* If this worked we close the progress pipe early, and just use the socket */ + progress_pipe[1] = safe_close(progress_pipe[1]); + xsprintf(dash_c, "-C%i", progress_socket); + } else if (progress_pipe[1] >= 0) { + /* Otherwise if we have the progress pipe to our own local handle, we use it */ + xsprintf(dash_c, "-C%i", progress_pipe[1]); + } else + dash_c[0] = 0; + + cmdline[i++] = "/sbin/fsck"; + cmdline[i++] = arg_repair; + cmdline[i++] = "-T"; + + /* + * Since util-linux v2.25 fsck uses /run/fsck/.lock files. + * The previous versions use flock for the device and conflict with + * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5 + */ + cmdline[i++] = "-l"; + + if (!root_directory) + cmdline[i++] = "-M"; + + if (arg_force) + cmdline[i++] = "-f"; + + if (!isempty(dash_c)) + cmdline[i++] = dash_c; + + cmdline[i++] = device; + cmdline[i++] = NULL; + + execv(cmdline[0], (char**) cmdline); + _exit(FSCK_OPERATIONAL_ERROR); + } + + progress_pipe[1] = safe_close(progress_pipe[1]); + (void) process_progress(progress_pipe[0]); + progress_pipe[0] = -1; + + r = wait_for_terminate(pid, &status); + if (r < 0) { + log_error_errno(r, "waitid(): %m"); + goto finish; + } + + if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { + + if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) + log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); + else if (status.si_code == CLD_EXITED) + log_error("fsck failed with error code %i.", status.si_status); + else + log_error("fsck failed due to unknown reason."); + + r = -EINVAL; + + if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) + /* System should be rebooted. */ + start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); + else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) + /* Some other problem */ + start_target(SPECIAL_EMERGENCY_TARGET, "replace"); + else { + log_warning("Ignoring error."); + r = 0; + } + + } else + r = 0; + + if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) + (void) touch("/run/systemd/quotacheck"); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in new file mode 100644 index 0000000000..6ca6b07e9e --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=File System Check on %f +Documentation=man:systemd-fsck@.service(8) +DefaultDependencies=no +BindsTo=%i.device +After=%i.device systemd-fsck-root.service local-fs-pre.target +Before=systemd-quotacheck.service shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-fsck %f +TimeoutSec=0 diff --git a/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.xml b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.xml new file mode 100644 index 0000000000..933c3247ad --- /dev/null +++ b/src/grp-initprogs/systemd-fsck/systemd-fsck@.service.xml @@ -0,0 +1,139 @@ + + + + + + + + systemd-fsck@.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-fsck@.service + 8 + + + + systemd-fsck@.service + systemd-fsck-root.service + systemd-fsck + File system checker logic + + + + systemd-fsck@.service + systemd-fsck-root.service + /usr/lib/systemd/systemd-fsck + + + + Description + + systemd-fsck@.service and + systemd-fsck-root.service are services + responsible for file system checks. They are instantiated for each + device that is configured for file system checking. + systemd-fsck-root.service is responsible for + file system checks on the root file system, but only if the + root filesystem was not checked in the initramfs. + systemd-fsck@.service is used for all other + file systems and for the root file system in the initramfs. + + These services are started at boot if + in /etc/fstab for the + file system is set to a value greater than zero. The file system + check for root is performed before the other file systems. Other + file systems may be checked in parallel, except when they are on + the same rotating disk. + + systemd-fsck does not know any details + about specific filesystems, and simply executes file system + checkers specific to each filesystem type + (/sbin/fsck.*). This helper will decide if + the filesystem should actually be checked based on the time since + last check, number of mounts, unclean unmount, etc. + + If a file system check fails for a service without + , emergency mode is activated, by isolating + to emergency.target. + + + + Kernel Command Line + + systemd-fsck understands one kernel + command line parameter: + + + + fsck.mode= + + One of auto, + force, skip. Controls + the mode of operation. The default is auto, + and ensures that file system checks are done when the file + system checker deems them necessary. force + unconditionally results in full file system checks. + skip skips any file system + checks. + + + + fsck.repair= + + One of preen, + yes, no. Controls the + mode of operation. The default is preen, + and will automatically repair problems that can be safely + fixed. yes will answer yes to all + questions by fsck and no will answer no to + all questions. + + + + + + See Also + + systemd1, + fsck8, + systemd-quotacheck.service8, + fsck.btrfs8, + fsck.cramfs8, + fsck.ext48, + fsck.fat8, + fsck.hfsplus8, + fsck.minix8, + fsck.ntfs8, + fsck.xfs8 + + + + diff --git a/src/grp-initprogs/systemd-modules-load/Makefile b/src/grp-initprogs/systemd-modules-load/Makefile new file mode 100644 index 0000000000..01ee557164 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/Makefile @@ -0,0 +1,59 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_KMOD),) +systemd_modules_load_SOURCES = \ + src/modules-load/modules-load.c + +systemd_modules_load_CFLAGS = \ + $(KMOD_CFLAGS) + +systemd_modules_load_LDADD = \ + libsystemd-shared.la \ + $(KMOD_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-modules-load + +nodist_systemunit_DATA += \ + units/systemd-modules-load.service + +SYSINIT_TARGET_WANTS += \ + systemd-modules-load.service + +ifneq ($(ENABLE_TMPFILES),) +nodist_systemunit_DATA += \ + units/kmod-static-nodes.service + +SYSINIT_TARGET_WANTS += \ + kmod-static-nodes.service +endif # ENABLE_TMPFILES +endif # HAVE_KMOD + +EXTRA_DIST += \ + units/systemd-modules-load.service.in \ + units/kmod-static-nodes.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in b/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in new file mode 100644 index 0000000000..a9c8df1184 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/kmod-static-nodes.service.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create list of required static device nodes for the current kernel +DefaultDependencies=no +Before=sysinit.target systemd-tmpfiles-setup-dev.service +ConditionCapability=CAP_SYS_MODULE +ConditionFileNotEmpty=/lib/modules/%v/modules.devname + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@KMOD@ static-nodes --format=tmpfiles --output=/run/tmpfiles.d/kmod.conf diff --git a/src/grp-initprogs/systemd-modules-load/modules-load.c b/src/grp-initprogs/systemd-modules-load/modules-load.c new file mode 100644 index 0000000000..d7ea7886c6 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/modules-load.c @@ -0,0 +1,283 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/proc-cmdline.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static char **arg_proc_cmdline_modules = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d"); + +static void systemd_kmod_log(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args) { + + DISABLE_WARNING_FORMAT_NONLITERAL; + log_internalv(priority, 0, file, line, fn, format, args); + REENABLE_WARNING; +} + +static int add_modules(const char *p) { + _cleanup_strv_free_ char **k = NULL; + + k = strv_split(p, ","); + if (!k) + return log_oom(); + + if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) + return log_oom(); + + return 0; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + if (STR_IN_SET(key, "modules-load", "rd.modules-load") && value) { + r = add_modules(value); + if (r < 0) + return r; + } + + return 0; +} + +static int load_module(struct kmod_ctx *ctx, const char *m) { + const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST; + struct kmod_list *itr, *modlist = NULL; + int r = 0; + + log_debug("load: %s", m); + + r = kmod_module_new_from_lookup(ctx, m, &modlist); + if (r < 0) + return log_error_errno(r, "Failed to lookup alias '%s': %m", m); + + if (!modlist) { + log_error("Failed to find module '%s'", m); + return -ENOENT; + } + + kmod_list_foreach(itr, modlist) { + struct kmod_module *mod; + int state, err; + + mod = kmod_module_get_module(itr); + state = kmod_module_get_initstate(mod); + + switch (state) { + case KMOD_MODULE_BUILTIN: + log_info("Module '%s' is builtin", kmod_module_get_name(mod)); + break; + + case KMOD_MODULE_LIVE: + log_debug("Module '%s' is already loaded", kmod_module_get_name(mod)); + break; + + default: + err = kmod_module_probe_insert_module(mod, probe_flags, + NULL, NULL, NULL, NULL); + + if (err == 0) + log_info("Inserted module '%s'", kmod_module_get_name(mod)); + else if (err == KMOD_PROBE_APPLY_BLACKLIST) + log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); + else { + log_error_errno(err, "Failed to insert '%s': %m", kmod_module_get_name(mod)); + r = err; + } + } + + kmod_module_unref(mod); + } + + kmod_module_unref_list(modlist); + + return r; +} + +static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(ctx); + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open %s, ignoring: %m", path); + } + + log_debug("apply: %s", path); + for (;;) { + char line[LINE_MAX], *l; + int k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + l = strstrip(line); + if (!*l) + continue; + if (strchr(COMMENTS "\n", *l)) + continue; + + k = load_module(ctx, l); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Loads statically configured kernel modules.\n\n" + " -h --help Show this help\n" + " --version Show package version\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, k; + struct kmod_ctx *ctx; + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + ctx = kmod_new(NULL, NULL); + if (!ctx) { + log_error("Failed to allocate memory for kmod."); + goto finish; + } + + kmod_load_resources(ctx); + kmod_set_log_fn(ctx, systemd_kmod_log, NULL); + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = apply_file(ctx, argv[i], false); + if (k < 0 && r == 0) + r = k; + } + + } else { + _cleanup_strv_free_ char **files = NULL; + char **fn, **i; + + STRV_FOREACH(i, arg_proc_cmdline_modules) { + k = load_module(ctx, *i); + if (k < 0 && r == 0) + r = k; + } + + k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (k < 0) { + log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); + if (r == 0) + r = k; + goto finish; + } + + STRV_FOREACH(fn, files) { + k = apply_file(ctx, *fn, true); + if (k < 0 && r == 0) + r = k; + } + } + +finish: + kmod_unref(ctx); + strv_free(arg_proc_cmdline_modules); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-modules-load/modules-load.d.xml b/src/grp-initprogs/systemd-modules-load/modules-load.d.xml new file mode 100644 index 0000000000..4b722aa128 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/modules-load.d.xml @@ -0,0 +1,101 @@ + + + + + + + + modules-load.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + modules-load.d + 5 + + + + modules-load.d + Configure kernel modules to load at boot + + + + /etc/modules-load.d/*.conf + /run/modules-load.d/*.conf + /usr/lib/modules-load.d/*.conf + + + + Description + + systemd-modules-load.service8 + reads files from the above directories which contain kernel + modules to load during boot in a static list. Each configuration + file is named in the style of + /etc/modules-load.d/program.conf. + Note that it is usually a better idea to rely on the automatic + module loading by PCI IDs, USB IDs, DMI IDs or similar triggers + encoded in the kernel modules themselves instead of static + configuration like this. In fact, most modern kernel modules are + prepared for automatic loading already. + + + + Configuration Format + + The configuration files should simply contain a list of + kernel module names to load, separated by newlines. Empty lines + and lines whose first non-whitespace character is # or ; are + ignored. + + + + + + Example + + /etc/modules-load.d/virtio-net.conf example: + + # Load virtio-net.ko at boot +virtio-net + + + + + See Also + + systemd1, + systemd-modules-load.service8, + systemd-delta1, + modprobe8 + + + + diff --git a/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in new file mode 100644 index 0000000000..9de6d31349 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load Kernel Modules +Documentation=man:systemd-modules-load.service(8) man:modules-load.d(5) +DefaultDependencies=no +Conflicts=shutdown.target +Before=sysinit.target shutdown.target +ConditionCapability=CAP_SYS_MODULE +ConditionDirectoryNotEmpty=|/lib/modules-load.d +ConditionDirectoryNotEmpty=|/usr/lib/modules-load.d +ConditionDirectoryNotEmpty=|/usr/local/lib/modules-load.d +ConditionDirectoryNotEmpty=|/etc/modules-load.d +ConditionDirectoryNotEmpty=|/run/modules-load.d +ConditionKernelCommandLine=|modules-load +ConditionKernelCommandLine=|rd.modules-load + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-modules-load +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.xml b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.xml new file mode 100644 index 0000000000..b25929b2e4 --- /dev/null +++ b/src/grp-initprogs/systemd-modules-load/systemd-modules-load.service.xml @@ -0,0 +1,96 @@ + + + + + + + + systemd-modules-load.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-modules-load.service + 8 + + + + systemd-modules-load.service + systemd-modules-load + Load kernel modules at boot + + + + systemd-modules-load.service + /usr/lib/systemd/systemd-modules-load + + + + Description + + systemd-modules-load.service is an + early boot service that loads kernel modules based on static + configuration. + + See + modules-load.d5 + for information about the configuration of this service. + + + + + Kernel Command Line + + systemd-modules-load.service + understands the following kernel command line parameters: + + + + + modules-load= + rd.modules-load= + + Takes a comma-separated list of kernel modules + to statically load during early boot. The option prefixed with + rd. is read by the initial RAM disk + only. + + + + + + + See Also + + systemd1, + modules-load.d5, + + + + diff --git a/src/grp-initprogs/systemd-quotacheck/Makefile b/src/grp-initprogs/systemd-quotacheck/Makefile new file mode 100644 index 0000000000..84c7204d38 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_QUOTACHECK),) +rootlibexec_PROGRAMS += \ + systemd-quotacheck + +nodist_systemunit_DATA += \ + units/systemd-quotacheck.service + +systemd_quotacheck_SOURCES = \ + src/quotacheck/quotacheck.c + +systemd_quotacheck_LDADD = \ + libsystemd-shared.la +endif # ENABLE_QUOTACHECK + +EXTRA_DIST += \ + units/systemd-quotacheck.service.in + +nodist_systemunit_DATA += \ + units/quotaon.service + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-quotacheck/quotacheck.c b/src/grp-initprogs/systemd-quotacheck/quotacheck.c new file mode 100644 index 0000000000..3c2292b0c4 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/quotacheck.c @@ -0,0 +1,124 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +static bool arg_skip = false; +static bool arg_force = false; + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + if (streq(key, "quotacheck.mode") && value) { + + if (streq(value, "auto")) + arg_force = arg_skip = false; + else if (streq(value, "force")) + arg_force = true; + else if (streq(value, "skip")) + arg_skip = true; + else + log_warning("Invalid quotacheck.mode= parameter '%s'. Ignoring.", value); + } + +#ifdef HAVE_SYSV_COMPAT + else if (streq(key, "forcequotacheck") && !value) { + log_warning("Please use 'quotacheck.mode=force' rather than 'forcequotacheck' on the kernel command line."); + arg_force = true; + } +#endif + + return 0; +} + +static void test_files(void) { + +#ifdef HAVE_SYSV_COMPAT + if (access("/forcequotacheck", F_OK) >= 0) { + log_error("Please pass 'quotacheck.mode=force' on the kernel command line rather than creating /forcequotacheck on the root file system."); + arg_force = true; + } +#endif +} + +int main(int argc, char *argv[]) { + + static const char * const cmdline[] = { + QUOTACHECK, + "-anug", + NULL + }; + + pid_t pid; + int r; + + if (argc > 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + test_files(); + + if (!arg_force) { + if (arg_skip) + return EXIT_SUCCESS; + + if (access("/run/systemd/quotacheck", F_OK) < 0) + return EXIT_SUCCESS; + } + + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "fork(): %m"); + return EXIT_FAILURE; + } else if (pid == 0) { + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + execv(cmdline[0], (char**) cmdline); + _exit(1); /* Operational error */ + } + + r = wait_for_terminate_and_warn("quotacheck", pid, true); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-quotacheck/quotaon.service.in b/src/grp-initprogs/systemd-quotacheck/quotaon.service.in new file mode 100644 index 0000000000..7d59a40195 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/quotaon.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Enable File System Quotas +Documentation=man:quotaon(8) +DefaultDependencies=no +After=systemd-quotacheck.service +Before=local-fs.target shutdown.target +ConditionPathExists=@QUOTAON@ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@QUOTAON@ -aug diff --git a/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in new file mode 100644 index 0000000000..5cb9bc3bc9 --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=File System Quota Check +Documentation=man:systemd-quotacheck.service(8) +DefaultDependencies=no +After=systemd-remount-fs.service +Before=local-fs.target shutdown.target +ConditionPathExists=@QUOTACHECK@ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-quotacheck +TimeoutSec=0 diff --git a/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.xml b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.xml new file mode 100644 index 0000000000..9d4976274e --- /dev/null +++ b/src/grp-initprogs/systemd-quotacheck/systemd-quotacheck.service.xml @@ -0,0 +1,94 @@ + + + + + + + + systemd-quotacheck.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-quotacheck.service + 8 + + + + systemd-quotacheck.service + systemd-quotacheck + File system quota checker logic + + + + systemd-quotacheck.service + /usr/lib/systemd/systemd-quotacheck + + + + Description + + systemd-quotacheck.service is a service + responsible for file system quota checks. It is run once at boot + after all necessary file systems are mounted. It is pulled in only + if at least one file system has quotas enabled. + + + + Kernel Command Line + + systemd-quotacheck understands one + kernel command line parameter: + + + + quotacheck.mode= + + One of auto, + force, skip. Controls + the mode of operation. The default is auto, + and ensures that file system quota checks are done when the + file system quota checker deems them necessary. + force unconditionally results in full file + system quota checks. skip skips any file + system quota checks. + + + + + + See Also + + systemd1, + quotacheck8, + systemd-fsck@.service8 + + + + diff --git a/src/grp-initprogs/systemd-random-seed/Makefile b/src/grp-initprogs/systemd-random-seed/Makefile new file mode 100644 index 0000000000..8d8fce955c --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/Makefile @@ -0,0 +1,47 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_RANDOMSEED),) +rootlibexec_PROGRAMS += \ + systemd-random-seed + +nodist_systemunit_DATA += \ + units/systemd-random-seed.service + +systemd_random_seed_SOURCES = \ + src/random-seed/random-seed.c + +systemd_random_seed_LDADD = \ + libsystemd-shared.la + +SYSINIT_TARGET_WANTS += \ + systemd-random-seed.service + +endif # ENABLE_RANDOMSEED + +EXTRA_DIST += \ + units/systemd-random-seed.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-random-seed/random-seed.c b/src/grp-initprogs/systemd-random-seed/random-seed.c new file mode 100644 index 0000000000..7fea6069f3 --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/random-seed.c @@ -0,0 +1,176 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#define POOL_SIZE_MIN 512 + +int main(int argc, char *argv[]) { + _cleanup_close_ int seed_fd = -1, random_fd = -1; + _cleanup_free_ void* buf = NULL; + size_t buf_size = 0; + ssize_t k; + int r, open_rw_error; + FILE *f; + bool refresh_seed_file = true; + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + /* Read pool size, if possible */ + f = fopen("/proc/sys/kernel/random/poolsize", "re"); + if (f) { + if (fscanf(f, "%zu", &buf_size) > 0) + /* poolsize is in bits on 2.6, but we want bytes */ + buf_size /= 8; + + fclose(f); + } + + if (buf_size <= POOL_SIZE_MIN) + buf_size = POOL_SIZE_MIN; + + buf = malloc(buf_size); + if (!buf) { + r = log_oom(); + goto finish; + } + + r = mkdir_parents_label(RANDOM_SEED, 0755); + if (r < 0) { + log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); + goto finish; + } + + /* When we load the seed we read it and write it to the device + * and then immediately update the saved seed with new data, + * to make sure the next boot gets seeded differently. */ + + if (streq(argv[1], "load")) { + + seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); + open_rw_error = -errno; + if (seed_fd < 0) { + refresh_seed_file = false; + + seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (seed_fd < 0) { + bool missing = errno == ENOENT; + + log_full_errno(missing ? LOG_DEBUG : LOG_ERR, + open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m"); + r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR, + errno, "Failed to open " RANDOM_SEED " for reading: %m"); + if (missing) + r = 0; + + goto finish; + } + } + + random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (random_fd < 0) { + random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600); + if (random_fd < 0) { + r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); + goto finish; + } + } + + k = loop_read(seed_fd, buf, buf_size, false); + if (k < 0) + r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m"); + else if (k == 0) { + r = 0; + log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding."); + } else { + (void) lseek(seed_fd, 0, SEEK_SET); + + r = loop_write(random_fd, buf, (size_t) k, false); + if (r < 0) + log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); + } + + } else if (streq(argv[1], "save")) { + + seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); + if (seed_fd < 0) { + r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); + goto finish; + } + + random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (random_fd < 0) { + r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); + goto finish; + } + + } else { + log_error("Unknown verb '%s'.", argv[1]); + r = -EINVAL; + goto finish; + } + + if (refresh_seed_file) { + + /* This is just a safety measure. Given that we are root and + * most likely created the file ourselves the mode and owner + * should be correct anyway. */ + (void) fchmod(seed_fd, 0600); + (void) fchown(seed_fd, 0, 0); + + k = loop_read(random_fd, buf, buf_size, false); + if (k < 0) { + r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m"); + goto finish; + } + if (k == 0) { + log_error("Got EOF while reading from /dev/urandom."); + r = -EIO; + goto finish; + } + + r = loop_write(seed_fd, buf, (size_t) k, false); + if (r < 0) + log_error_errno(r, "Failed to write new random seed file: %m"); + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in new file mode 100644 index 0000000000..115233268d --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save Random Seed +Documentation=man:systemd-random-seed.service(8) man:random(4) +DefaultDependencies=no +RequiresMountsFor=@RANDOM_SEED@ +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-random-seed load +ExecStop=@rootlibexecdir@/systemd-random-seed save +TimeoutSec=30s diff --git a/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.xml b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.xml new file mode 100644 index 0000000000..f3b5a947da --- /dev/null +++ b/src/grp-initprogs/systemd-random-seed/systemd-random-seed.service.xml @@ -0,0 +1,75 @@ + + + + + + + + systemd-random-seed.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-random-seed.service + 8 + + + + systemd-random-seed.service + systemd-random-seed + Load and save the system random seed at boot and shutdown + + + + systemd-random-seed.service + /usr/lib/systemd/systemd-random-seed + + + + Description + + systemd-random-seed.service is a + service that restores the random seed of the system at early boot + and saves it at shutdown. See + random4 + for details. Saving/restoring the random seed across boots + increases the amount of available entropy early at boot. On disk + the random seed is stored in + /var/lib/systemd/random-seed. + + + + See Also + + systemd1, + random4 + + + + diff --git a/src/grp-initprogs/systemd-rfkill/Makefile b/src/grp-initprogs/systemd-rfkill/Makefile new file mode 100644 index 0000000000..32fb516b52 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_RFKILL),) +rootlibexec_PROGRAMS += \ + systemd-rfkill + +nodist_systemunit_DATA += \ + units/systemd-rfkill.service + +dist_systemunit_DATA += \ + units/systemd-rfkill.socket + +systemd_rfkill_SOURCES = \ + src/rfkill/rfkill.c + +systemd_rfkill_LDADD = \ + libsystemd-shared.la +endif # ENABLE_RFKILL + +EXTRA_DIST += \ + units/systemd-rfkill.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-rfkill/rfkill.c b/src/grp-initprogs/systemd-rfkill/rfkill.c new file mode 100644 index 0000000000..86779e9ac8 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/rfkill.c @@ -0,0 +1,427 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" + +#define EXIT_USEC (5 * USEC_PER_SEC) + +static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { + [RFKILL_TYPE_ALL] = "all", + [RFKILL_TYPE_WLAN] = "wlan", + [RFKILL_TYPE_BLUETOOTH] = "bluetooth", + [RFKILL_TYPE_UWB] = "uwb", + [RFKILL_TYPE_WIMAX] = "wimax", + [RFKILL_TYPE_WWAN] = "wwan", + [RFKILL_TYPE_GPS] = "gps", + [RFKILL_TYPE_FM] = "fm", + [RFKILL_TYPE_NFC] = "nfc", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int); + +static int find_device( + struct udev *udev, + const struct rfkill_event *event, + struct udev_device **ret) { + + _cleanup_free_ char *sysname = NULL; + struct udev_device *device; + const char *name; + + assert(udev); + assert(event); + assert(ret); + + if (asprintf(&sysname, "rfkill%i", event->idx) < 0) + return log_oom(); + + device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); + if (!device) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); + + name = udev_device_get_sysattr_value(device, "name"); + if (!name) { + log_debug("Device has no name, ignoring."); + udev_device_unref(device); + return -ENOENT; + } + + log_debug("Operating on rfkill device '%s'.", name); + + *ret = device; + return 0; +} + +static int wait_for_initialized( + struct udev *udev, + struct udev_device *device, + struct udev_device **ret) { + + _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; + struct udev_device *d; + const char *sysname; + int watch_fd, r; + + assert(udev); + assert(device); + assert(ret); + + if (udev_device_get_is_initialized(device) != 0) { + *ret = udev_device_ref(device); + return 0; + } + + assert_se(sysname = udev_device_get_sysname(device)); + + /* Wait until the device is initialized, so that we can get + * access to the ID_PATH property */ + + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!monitor) + return log_error_errno(errno, "Failed to acquire monitor: %m"); + + r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL); + if (r < 0) + return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m"); + + r = udev_monitor_enable_receiving(monitor); + if (r < 0) + return log_error_errno(r, "Failed to enable udev receiving: %m"); + + watch_fd = udev_monitor_get_fd(monitor); + if (watch_fd < 0) + return log_error_errno(watch_fd, "Failed to get watch fd: %m"); + + /* Check again, maybe things changed */ + d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); + if (!d) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); + + if (udev_device_get_is_initialized(d) != 0) { + *ret = d; + return 0; + } + + for (;;) { + _cleanup_udev_device_unref_ struct udev_device *t = NULL; + + r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); + if (r == -EINTR) + continue; + if (r < 0) + return log_error_errno(r, "Failed to watch udev monitor: %m"); + + t = udev_monitor_receive_device(monitor); + if (!t) + continue; + + if (streq_ptr(udev_device_get_sysname(device), sysname)) { + *ret = udev_device_ref(t); + return 0; + } + } +} + +static int determine_state_file( + struct udev *udev, + const struct rfkill_event *event, + struct udev_device *d, + char **ret) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + const char *path_id, *type; + char *state_file; + int r; + + assert(event); + assert(d); + assert(ret); + + r = wait_for_initialized(udev, d, &device); + if (r < 0) + return r; + + assert_se(type = rfkill_type_to_string(event->type)); + + path_id = udev_device_get_property_value(device, "ID_PATH"); + if (path_id) { + _cleanup_free_ char *escaped_path_id = NULL; + + escaped_path_id = cescape(path_id); + if (!escaped_path_id) + return log_oom(); + + state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL); + } else + state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL); + + if (!state_file) + return log_oom(); + + *ret = state_file; + return 0; +} + +static int load_state( + int rfkill_fd, + struct udev *udev, + const struct rfkill_event *event) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *state_file = NULL, *value = NULL; + struct rfkill_event we; + ssize_t l; + int b, r; + + assert(rfkill_fd >= 0); + assert(udev); + assert(event); + + if (shall_restore_state() == 0) + return 0; + + r = find_device(udev, event, &device); + if (r < 0) + return r; + + r = determine_state_file(udev, event, device, &state_file); + if (r < 0) + return r; + + r = read_one_line_file(state_file, &value); + if (r == -ENOENT) { + /* No state file? Then save the current state */ + + r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) + return log_error_errno(r, "Failed to write state file %s: %m", state_file); + + log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to read state file %s: %m", state_file); + + b = parse_boolean(value); + if (b < 0) + return log_error_errno(b, "Failed to parse state file %s: %m", state_file); + + we = (struct rfkill_event) { + .op = RFKILL_OP_CHANGE, + .idx = event->idx, + .soft = b, + }; + + l = write(rfkill_fd, &we, sizeof(we)); + if (l < 0) + return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx); + if (l != sizeof(we)) { + log_error("Couldn't write rfkill event structure, too short."); + return -EIO; + } + + log_debug("Loaded state '%s' from %s.", one_zero(b), state_file); + return 0; +} + +static int save_state( + int rfkill_fd, + struct udev *udev, + const struct rfkill_event *event) { + + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + _cleanup_free_ char *state_file = NULL; + int r; + + assert(rfkill_fd >= 0); + assert(udev); + assert(event); + + r = find_device(udev, event, &device); + if (r < 0) + return r; + + r = determine_state_file(udev, event, device, &state_file); + if (r < 0) + return r; + + r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + if (r < 0) + return log_error_errno(r, "Failed to write state file %s: %m", state_file); + + log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_close_ int rfkill_fd = -1; + bool ready = false; + int r, n; + + if (argc > 1) { + log_error("This program requires no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + udev = udev_new(); + if (!udev) { + r = log_oom(); + goto finish; + } + + r = mkdir_p("/var/lib/systemd/rfkill", 0755); + if (r < 0) { + log_error_errno(r, "Failed to create rfkill directory: %m"); + goto finish; + } + + n = sd_listen_fds(false); + if (n < 0) { + r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m"); + goto finish; + } + if (n > 1) { + log_error("Got too many file descriptors."); + r = -EINVAL; + goto finish; + } + + if (n == 0) { + rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (rfkill_fd < 0) { + if (errno == ENOENT) { + log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting."); + r = 0; + goto finish; + } + + r = log_error_errno(errno, "Failed to open /dev/rfkill: %m"); + goto finish; + } + } else { + rfkill_fd = SD_LISTEN_FDS_START; + + r = fd_nonblock(rfkill_fd, 1); + if (r < 0) { + log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m"); + goto finish; + } + } + + for (;;) { + struct rfkill_event event; + const char *type; + ssize_t l; + + l = read(rfkill_fd, &event, sizeof(event)); + if (l < 0) { + if (errno == EAGAIN) { + + if (!ready) { + /* Notify manager that we are + * now finished with + * processing whatever was + * queued */ + (void) sd_notify(false, "READY=1"); + ready = true; + } + + /* Hang around for a bit, maybe there's more coming */ + + r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC); + if (r == -EINTR) + continue; + if (r < 0) { + log_error_errno(r, "Failed to poll() on device: %m"); + goto finish; + } + if (r > 0) + continue; + + log_debug("All events read and idle, exiting."); + break; + } + + log_error_errno(errno, "Failed to read from /dev/rfkill: %m"); + } + + if (l != RFKILL_EVENT_SIZE_V1) { + log_error("Read event structure of invalid size."); + r = -EIO; + goto finish; + } + + type = rfkill_type_to_string(event.type); + if (!type) { + log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type); + continue; + } + + switch (event.op) { + + case RFKILL_OP_ADD: + log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type); + (void) load_state(rfkill_fd, udev, &event); + break; + + case RFKILL_OP_DEL: + log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type); + break; + + case RFKILL_OP_CHANGE: + log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type); + (void) save_state(rfkill_fd, udev, &event); + break; + + default: + log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type); + break; + } + } + + r = 0; + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in new file mode 100644 index 0000000000..780a19b996 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save RF Kill Switch Status +Documentation=man:systemd-rfkill.service(8) +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/rfkill +BindsTo=sys-devices-virtual-misc-rfkill.device +Conflicts=shutdown.target +After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service +Before=shutdown.target + +[Service] +Type=notify +ExecStart=@rootlibexecdir@/systemd-rfkill +TimeoutSec=30s diff --git a/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.xml b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.xml new file mode 100644 index 0000000000..f464842700 --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.service.xml @@ -0,0 +1,90 @@ + + + + + + + + systemd-rfkill.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-rfkill.service + 8 + + + + systemd-rfkill.service + systemd-rfkill.socket + systemd-rfkill + Load and save the RF kill switch state at boot and change + + + + systemd-rfkill.service + systemd-rfkill.socket + /usr/lib/systemd/systemd-rfkill + + + + Description + + systemd-rfkill.service is a service + that restores the RF kill switch state at early boot and saves it + on each change. On disk, the RF kill switch state is stored in + /var/lib/systemd/rfkill/. + + + + Kernel Command Line + + systemd-rfkill understands the + following kernel command line parameter: + + + + systemd.restore_state= + + Takes a boolean argument. Defaults to + 1. If 0, does not + restore the rfkill settings on boot. However, settings will + still be stored on shutdown. + + + + + + See Also + + systemd1 + + + + diff --git a/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket new file mode 100644 index 0000000000..20ae2f8adb --- /dev/null +++ b/src/grp-initprogs/systemd-rfkill/systemd-rfkill.socket @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load/Save RF Kill Switch Status /dev/rfkill Watch +Documentation=man:systemd-rfkill.socket(8) +DefaultDependencies=no +BindsTo=sys-devices-virtual-misc-rfkill.device +After=sys-devices-virtual-misc-rfkill.device +Conflicts=shutdown.target +Before=shutdown.target + +[Socket] +ListenSpecial=/dev/rfkill +Writable=yes diff --git a/src/grp-initprogs/systemd-sysctl/50-default.sysctl b/src/grp-initprogs/systemd-sysctl/50-default.sysctl new file mode 100644 index 0000000000..f08f32e849 --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/50-default.sysctl @@ -0,0 +1,40 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See sysctl.d(5) and core(5) for documentation. + +# To override settings in this file, create a local file in /etc +# (e.g. /etc/sysctl.d/90-override.conf), and put any assignments +# there. + +# System Request functionality of the kernel (SYNC) +# +# Use kernel.sysrq = 1 to allow all keys. +# See http://fedoraproject.org/wiki/QA/Sysrq for a list of values and keys. +kernel.sysrq = 16 + +# Append the PID to the core filename +kernel.core_uses_pid = 1 + +# Source route verification +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.all.rp_filter = 1 + +# Do not accept source routing +net.ipv4.conf.default.accept_source_route = 0 +net.ipv4.conf.all.accept_source_route = 0 + +# Promote secondary addresses when the primary address is removed +net.ipv4.conf.default.promote_secondaries = 1 +net.ipv4.conf.all.promote_secondaries = 1 + +# Fair Queue CoDel packet scheduler to fight bufferbloat +net.core.default_qdisc = fq_codel + +# Enable hard and soft link protection +fs.protected_hardlinks = 1 +fs.protected_symlinks = 1 diff --git a/src/grp-initprogs/systemd-sysctl/Makefile b/src/grp-initprogs/systemd-sysctl/Makefile new file mode 100644 index 0000000000..3fe12fd460 --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-sysctl +systemd_sysctl_SOURCES = \ + src/sysctl/sysctl.c + +systemd_sysctl_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-sysctl/sysctl.c b/src/grp-initprogs/systemd-sysctl/sysctl.c new file mode 100644 index 0000000000..c44aa0aabb --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/sysctl.c @@ -0,0 +1,287 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/sysctl-util.h" + +static char **arg_prefixes = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d"); + +static int apply_all(Hashmap *sysctl_options) { + char *property, *value; + Iterator i; + int r = 0; + + HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { + int k; + + k = sysctl_write(property, value); + if (k < 0) { + log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k, + "Couldn't write '%s' to '%s', ignoring: %m", value, property); + + if (r == 0 && k != -ENOENT) + r = k; + } + } + + return r; +} + +static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); + } + + log_debug("Parsing %s", path); + while (!feof(f)) { + char l[LINE_MAX], *p, *value, *new_value, *property, *existing; + void *v; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); + } + + p = strstrip(l); + if (!*p) + continue; + + if (strchr(COMMENTS "\n", *p)) + continue; + + value = strchr(p, '='); + if (!value) { + log_error("Line is not an assignment in file '%s': %s", path, value); + + if (r == 0) + r = -EINVAL; + continue; + } + + *value = 0; + value++; + + p = sysctl_normalize(strstrip(p)); + value = strstrip(value); + + if (!strv_isempty(arg_prefixes)) { + char **i, *t; + STRV_FOREACH(i, arg_prefixes) { + t = path_startswith(*i, "/proc/sys/"); + if (t == NULL) + t = *i; + if (path_startswith(p, t)) + goto found; + } + /* not found */ + continue; + } + +found: + existing = hashmap_get2(sysctl_options, p, &v); + if (existing) { + if (streq(value, existing)) + continue; + + log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path); + free(hashmap_remove(sysctl_options, p)); + free(v); + } + + property = strdup(p); + if (!property) + return log_oom(); + + new_value = strdup(value); + if (!new_value) { + free(property); + return log_oom(); + } + + k = hashmap_put(sysctl_options, property, new_value); + if (k < 0) { + log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); + free(property); + free(new_value); + return k; + } + } + + return r; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Applies kernel sysctl settings.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --prefix=PATH Only apply rules with the specified prefix\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_PREFIX + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "prefix", required_argument, NULL, ARG_PREFIX }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_PREFIX: { + char *p; + + /* We used to require people to specify absolute paths + * in /proc/sys in the past. This is kinda useless, but + * we need to keep compatibility. We now support any + * sysctl name available. */ + sysctl_normalize(optarg); + + if (startswith(optarg, "/proc/sys")) + p = strdup(optarg); + else + p = strappend("/proc/sys/", optarg); + if (!p) + return log_oom(); + + if (strv_consume(&arg_prefixes, p) < 0) + return log_oom(); + + break; + } + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r = 0, k; + Hashmap *sysctl_options; + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + sysctl_options = hashmap_new(&string_hash_ops); + if (!sysctl_options) { + r = log_oom(); + goto finish; + } + + r = 0; + + if (argc > optind) { + int i; + + for (i = optind; i < argc; i++) { + k = parse_file(sysctl_options, argv[i], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = parse_file(sysctl_options, *f, true); + if (k < 0 && r == 0) + r = k; + } + } + + k = apply_all(sysctl_options); + if (k < 0 && r == 0) + r = k; + +finish: + hashmap_free_free_free(sysctl_options); + strv_free(arg_prefixes); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-sysctl/sysctl.d.xml b/src/grp-initprogs/systemd-sysctl/sysctl.d.xml new file mode 100644 index 0000000000..ccf6c8e39f --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/sysctl.d.xml @@ -0,0 +1,184 @@ + + + + + + + sysctl.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sysctl.d + 5 + + + + sysctl.d + Configure kernel parameters at boot + + + + /etc/sysctl.d/*.conf + /run/sysctl.d/*.conf + /usr/lib/sysctl.d/*.conf + + + + Description + + At boot, + systemd-sysctl.service8 + reads configuration files from the above directories to configure + sysctl8 + kernel parameters. + + + + Configuration Format + + The configuration files contain a list of variable + assignments, separated by newlines. Empty lines and lines whose + first non-whitespace character is # or + ; are ignored. + + Note that either / or + . may be used as separators within sysctl + variable names. If the first separator is a slash, remaining + slashes and dots are left intact. If the first separator is a dot, + dots and slashes are interchanged. + kernel.domainname=foo and + kernel/domainname=foo are equivalent and will + cause foo to be written to + /proc/sys/kernel/domainname. Either + net.ipv4.conf.enp3s0/200.forwarding or + net/ipv4/conf/enp3s0.200/forwarding may be used + to refer to + /proc/sys/net/ipv4/conf/enp3s0.200/forwarding. + + + The settings configured with sysctl.d + files will be applied early on boot. The network + interface-specific options will also be applied individually for + each network interface as it shows up in the system. (More + specifically, net.ipv4.conf.*, + net.ipv6.conf.*, + net.ipv4.neigh.* and + net.ipv6.neigh.*). + + Many sysctl parameters only become available when certain + kernel modules are loaded. Modules are usually loaded on demand, + e.g. when certain hardware is plugged in or network brought up. + This means that + systemd-sysctl.service8 + which runs during early boot will not configure such parameters if + they become available after it has run. To set such parameters, it + is recommended to add an + udev7 + rule to set those parameters when they become available. + Alternatively, a slightly simpler and less efficient option is to + add the module to + modules-load.d5, + causing it to be loaded statically before sysctl settings are + applied (see example below). + + + + + + Examples + + Set kernel YP domain name + /etc/sysctl.d/domain-name.conf: + + + kernel.domainname=example.com + + + + Apply settings available only when a certain module is loaded (method one) + /etc/udev/rules.d/99-bridge.rules: + + + ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", \ + RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/net/bridge" + + + /etc/sysctl.d/bridge.conf: + + + net.bridge.bridge-nf-call-ip6tables = 0 +net.bridge.bridge-nf-call-iptables = 0 +net.bridge.bridge-nf-call-arptables = 0 + + + This method applies settings when the module is + loaded. Please note that, unless the br_netfilter + module is loaded, bridged packets will not be filtered by + Netfilter (starting with kernel 3.18), so simply not loading the + module is sufficient to avoid filtering. + + + + Apply settings available only when a certain module is loaded (method two) + /etc/modules-load.d/bridge.conf: + + + br_netfilter + + /etc/sysctl.d/bridge.conf: + + + net.bridge.bridge-nf-call-ip6tables = 0 +net.bridge.bridge-nf-call-iptables = 0 +net.bridge.bridge-nf-call-arptables = 0 + + + This method forces the module to be always loaded. Please + note that, unless the br_netfilter module is + loaded, bridged packets will not be filtered with Netfilter + (starting with kernel 3.18), so simply not loading the module is + sufficient to avoid filtering. + + + + + See Also + + systemd1, + systemd-sysctl.service8, + systemd-delta1, + sysctl8, + sysctl.conf5, + modprobe8 + + + + diff --git a/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in new file mode 100644 index 0000000000..d784c6426d --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Apply Kernel Variables +Documentation=man:systemd-sysctl.service(8) man:sysctl.d(5) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-modules-load.service +Before=sysinit.target shutdown.target +ConditionPathIsReadWrite=/proc/sys/ + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-sysctl +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.xml b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.xml new file mode 100644 index 0000000000..686b2cdef4 --- /dev/null +++ b/src/grp-initprogs/systemd-sysctl/systemd-sysctl.service.xml @@ -0,0 +1,152 @@ + + + + + + + + systemd-sysctl.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-sysctl.service + 8 + + + + systemd-sysctl.service + systemd-sysctl + Configure kernel parameters at boot + + + + + /usr/lib/systemd/systemd-sysctl + OPTIONS + CONFIGFILE + + systemd-sysctl.service + + + + Description + + systemd-sysctl.service is an early boot + service that configures + sysctl8 + kernel parameters by invoking /usr/lib/systemd/systemd-sysctl. + + When invoked with no arguments, /usr/lib/systemd/systemd-sysctl applies + all directives from configuration files listed in + sysctl.d5. + If one or more filenames are passed on the command line, only the directives in these files are + applied. + + In addition, option may be used to limit which sysctl + settings are applied. + + See + sysctl.d5 + for information about the configuration of sysctl settings. After sysctl configuration is + changed on disk, it must be written to the files in /proc/sys before it + takes effect. It is possible to update specific settings, or simply to reload all configuration, + see Examples below. + + + Options + + + + + Only apply rules with the specified prefix. + + + + + + + + + + + Examples + + + Reset all sysctl settings + + systemctl restart systemd-sysctl + + + + View coredump handler configuration + + # sysctl kernel.core_pattern +kernel.core_pattern = |/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t %P %I + + + + + Update coredump handler configuration + + # /usr/lib/systemd/systemd-sysctl --prefix kernel.core_pattern + + This searches all the directories listed in + sysctl.d5 + for configuration files and writes /proc/sys/kernel/core_pattern. + + + + Update coredump handler configuration according to a specific file + + # /usr/lib/systemd/systemd-sysctl 50-coredump.conf + + This applies all the settings found in 50-coredump.conf. + Either /etc/sysctl.d/50-coredump.conf, or + /run/sysctl.d/50-coredump.conf, or + /usr/lib/sysctl.d/50-coredump.conf will be used, in the order + of preference. + + + See + sysctl8 + for various ways to directly apply sysctl settings. + + + + See Also + + systemd1, + sysctl.d5, + sysctl8, + + + + diff --git a/src/grp-initprogs/systemd-sysusers/.gitignore b/src/grp-initprogs/systemd-sysusers/.gitignore new file mode 100644 index 0000000000..c065034d29 --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/.gitignore @@ -0,0 +1,3 @@ +/basic.conf +/systemd.conf +/systemd-remote.conf diff --git a/src/grp-initprogs/systemd-sysusers/Makefile b/src/grp-initprogs/systemd-sysusers/Makefile new file mode 100644 index 0000000000..66af87f02c --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/Makefile @@ -0,0 +1,56 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_SYSUSERS),) +systemd_sysusers_SOURCES = \ + src/sysusers/sysusers.c + +systemd_sysusers_LDADD = \ + libsystemd-shared.la + +rootbin_PROGRAMS += \ + systemd-sysusers + +nodist_systemunit_DATA += \ + units/systemd-sysusers.service + +SYSINIT_TARGET_WANTS += \ + systemd-sysusers.service + +nodist_sysusers_DATA = \ + sysusers.d/systemd.conf \ + sysusers.d/basic.conf + +INSTALL_DIRS += \ + $(sysusersdir) +endif # ENABLE_SYSUSERS + +EXTRA_DIST += \ + units/systemd-sysusers.service.in \ + sysusers.d/systemd.conf.m4 \ + sysusers.d/systemd-remote.conf.m4 \ + sysusers.d/basic.conf.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-sysusers/basic.sysusers.in b/src/grp-initprogs/systemd-sysusers/basic.sysusers.in new file mode 100644 index 0000000000..b2dc5ebd4f --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/basic.sysusers.in @@ -0,0 +1,36 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# The superuser +u root 0 "Super User" /root + +# The nobody user for NFS file systems +u nobody 65534 "Nobody" - + +# Administrator group: can *see* more than normal users +g adm - - - + +# Administrator group: can *do* more than normal users +g wheel - - - + +# Access to certain kernel and userspace facilities +g kmem - - - +g tty @TTY_GID@ - - +g utmp - - - + +# Hardware access groups +g audio - - - +g cdrom - - - +g dialout - - - +g disk - - - +g input - - - +g lp - - - +g tape - - - +g video - - - + +# Default group for normal users +g users - - - diff --git a/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in new file mode 100644 index 0000000000..4d8309ab6b --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create System Users +Documentation=man:sysusers.d(5) man:systemd-sysusers.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-remount-fs.service +Before=sysinit.target shutdown.target systemd-update-done.service +ConditionNeedsUpdate=/etc + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-sysusers +TimeoutSec=90s diff --git a/src/grp-initprogs/systemd-sysusers/systemd-sysusers.xml b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.xml new file mode 100644 index 0000000000..4892caad12 --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/systemd-sysusers.xml @@ -0,0 +1,116 @@ + + + + + + + + + systemd-sysusers + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-sysusers + 8 + + + + systemd-sysusers + systemd-sysusers.service + Allocate system users and groups + + + + + systemd-sysusers + OPTIONS + CONFIGFILE + + + systemd-sysusers.service + + + + Description + + systemd-sysusers creates system users and + groups, based on the file format and location specified in + sysusers.d5. + + + If invoked with no arguments, it applies all directives from + all files found. If one or more filenames are passed on the + command line, only the directives in these files are applied. If + only the basename of a file is specified, all directories as + specified in + sysusers.d5 + are searched for a matching file. If the string + - is specified as filename, entries from the + standard input of the process are read. + + + + Options + + The following options are understood: + + + + + Takes a directory path as an argument. All + paths will be prefixed with the given alternate + root path, including config search + paths. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + sysusers.d5 + + + + diff --git a/src/grp-initprogs/systemd-sysusers/sysusers.c b/src/grp-initprogs/systemd-sysusers/sysusers.c new file mode 100644 index 0000000000..55ff36410d --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/sysusers.c @@ -0,0 +1,1919 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/copy.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/formats-util.h" +#include "basic/hashmap.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/specifier.h" +#include "shared/uid-range.h" + +typedef enum ItemType { + ADD_USER = 'u', + ADD_GROUP = 'g', + ADD_MEMBER = 'm', + ADD_RANGE = 'r', +} ItemType; +typedef struct Item { + ItemType type; + + char *name; + char *uid_path; + char *gid_path; + char *description; + char *home; + + gid_t gid; + uid_t uid; + + bool gid_set:1; + bool uid_set:1; + + bool todo_user:1; + bool todo_group:1; +} Item; + +static char *arg_root = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); + +static Hashmap *users = NULL, *groups = NULL; +static Hashmap *todo_uids = NULL, *todo_gids = NULL; +static Hashmap *members = NULL; + +static Hashmap *database_uid = NULL, *database_user = NULL; +static Hashmap *database_gid = NULL, *database_group = NULL; + +static uid_t search_uid = UID_INVALID; +static UidRange *uid_range = NULL; +static unsigned n_uid_range = 0; + +static int load_user_database(void) { + _cleanup_fclose_ FILE *f = NULL; + const char *passwd_path; + struct passwd *pw; + int r; + + passwd_path = prefix_roota(arg_root, "/etc/passwd"); + f = fopen(passwd_path, "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + r = hashmap_ensure_allocated(&database_user, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_uid, NULL); + if (r < 0) + return r; + + errno = 0; + while ((pw = fgetpwent(f))) { + char *n; + int k, q; + + n = strdup(pw->pw_name); + if (!n) + return -ENOMEM; + + k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid)); + if (k < 0 && k != -EEXIST) { + free(n); + return k; + } + + q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n); + if (q < 0 && q != -EEXIST) { + if (k < 0) + free(n); + return q; + } + + if (q < 0 && k < 0) + free(n); + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + return 0; +} + +static int load_group_database(void) { + _cleanup_fclose_ FILE *f = NULL; + const char *group_path; + struct group *gr; + int r; + + group_path = prefix_roota(arg_root, "/etc/group"); + f = fopen(group_path, "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + r = hashmap_ensure_allocated(&database_group, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&database_gid, NULL); + if (r < 0) + return r; + + errno = 0; + while ((gr = fgetgrent(f))) { + char *n; + int k, q; + + n = strdup(gr->gr_name); + if (!n) + return -ENOMEM; + + k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid)); + if (k < 0 && k != -EEXIST) { + free(n); + return k; + } + + q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n); + if (q < 0 && q != -EEXIST) { + if (k < 0) + free(n); + return q; + } + + if (q < 0 && k < 0) + free(n); + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + return 0; +} + +static int make_backup(const char *target, const char *x) { + _cleanup_close_ int src = -1; + _cleanup_fclose_ FILE *dst = NULL; + char *backup, *temp; + struct timespec ts[2]; + struct stat st; + int r; + + src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (src < 0) { + if (errno == ENOENT) /* No backup necessary... */ + return 0; + + return -errno; + } + + if (fstat(src, &st) < 0) + return -errno; + + r = fopen_temporary_label(target, x, &dst, &temp); + if (r < 0) + return r; + + r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); + if (r < 0) + goto fail; + + /* Don't fail on chmod() or chown(). If it stays owned by us + * and/or unreadable by others, then it isn't too bad... */ + + backup = strjoina(x, "-"); + + /* Copy over the access mask */ + if (fchmod(fileno(dst), st.st_mode & 07777) < 0) + log_warning_errno(errno, "Failed to change mode on %s: %m", backup); + + if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0) + log_warning_errno(errno, "Failed to change ownership of %s: %m", backup); + + ts[0] = st.st_atim; + ts[1] = st.st_mtim; + if (futimens(fileno(dst), ts) < 0) + log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup); + + if (rename(temp, backup) < 0) + goto fail; + + return 0; + +fail: + unlink(temp); + return r; +} + +static int putgrent_with_members(const struct group *gr, FILE *group) { + char **a; + + assert(gr); + assert(group); + + a = hashmap_get(members, gr->gr_name); + if (a) { + _cleanup_strv_free_ char **l = NULL; + bool added = false; + char **i; + + l = strv_copy(gr->gr_mem); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, a) { + if (strv_find(l, *i)) + continue; + + if (strv_extend(&l, *i) < 0) + return -ENOMEM; + + added = true; + } + + if (added) { + struct group t; + + strv_uniq(l); + strv_sort(l); + + t = *gr; + t.gr_mem = l; + + errno = 0; + if (putgrent(&t, group) != 0) + return errno > 0 ? -errno : -EIO; + + return 1; + } + } + + errno = 0; + if (putgrent(gr, group) != 0) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { + char **a; + + assert(sg); + assert(gshadow); + + a = hashmap_get(members, sg->sg_namp); + if (a) { + _cleanup_strv_free_ char **l = NULL; + bool added = false; + char **i; + + l = strv_copy(sg->sg_mem); + if (!l) + return -ENOMEM; + + STRV_FOREACH(i, a) { + if (strv_find(l, *i)) + continue; + + if (strv_extend(&l, *i) < 0) + return -ENOMEM; + + added = true; + } + + if (added) { + struct sgrp t; + + strv_uniq(l); + strv_sort(l); + + t = *sg; + t.sg_mem = l; + + errno = 0; + if (putsgent(&t, gshadow) != 0) + return errno > 0 ? -errno : -EIO; + + return 1; + } + } + + errno = 0; + if (putsgent(sg, gshadow) != 0) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int sync_rights(FILE *from, FILE *to) { + struct stat st; + + if (fstat(fileno(from), &st) < 0) + return -errno; + + if (fchmod(fileno(to), st.st_mode & 07777) < 0) + return -errno; + + if (fchown(fileno(to), st.st_uid, st.st_gid) < 0) + return -errno; + + return 0; +} + +static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { + int r = 0; + if (rename(temp_path, dest_path) < 0) + return -errno; + +#ifdef SMACK_RUN_LABEL + r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); + if (r < 0) + return r; +#endif + return r; +} + +static int write_files(void) { + + _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; + _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; + const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; + bool group_changed = false; + Iterator iterator; + Item *i; + int r; + + if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) { + _cleanup_fclose_ FILE *original = NULL; + + /* First we update the actual group list file */ + group_path = prefix_roota(arg_root, "/etc/group"); + r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); + if (r < 0) + goto finish; + + original = fopen(group_path, "re"); + if (original) { + struct group *gr; + + r = sync_rights(original, group); + if (r < 0) + goto finish; + + errno = 0; + while ((gr = fgetgrent(original))) { + /* Safety checks against name and GID + * collisions. Normally, this should + * be unnecessary, but given that we + * look at the entries anyway here, + * let's make an extra verification + * step that we don't generate + * duplicate entries. */ + + i = hashmap_get(groups, gr->gr_name); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); + r = -EEXIST; + goto finish; + } + + if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { + log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); + r = -EEXIST; + goto finish; + } + + r = putgrent_with_members(gr, group); + if (r < 0) + goto finish; + if (r > 0) + group_changed = true; + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(group), 0644) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct group n = { + .gr_name = i->name, + .gr_gid = i->gid, + .gr_passwd = (char*) "x", + }; + + r = putgrent_with_members(&n, group); + if (r < 0) + goto finish; + + group_changed = true; + } + + r = fflush_and_check(group); + if (r < 0) + goto finish; + + if (original) { + fclose(original); + original = NULL; + } + + /* OK, now also update the shadow file for the group list */ + gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); + r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); + if (r < 0) + goto finish; + + original = fopen(gshadow_path, "re"); + if (original) { + struct sgrp *sg; + + r = sync_rights(original, gshadow); + if (r < 0) + goto finish; + + errno = 0; + while ((sg = fgetsgent(original))) { + + i = hashmap_get(groups, sg->sg_namp); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); + r = -EEXIST; + goto finish; + } + + r = putsgent_with_members(sg, gshadow); + if (r < 0) + goto finish; + if (r > 0) + group_changed = true; + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(gshadow), 0000) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct sgrp n = { + .sg_namp = i->name, + .sg_passwd = (char*) "!!", + }; + + r = putsgent_with_members(&n, gshadow); + if (r < 0) + goto finish; + + group_changed = true; + } + + r = fflush_and_check(gshadow); + if (r < 0) + goto finish; + } + + if (hashmap_size(todo_uids) > 0) { + _cleanup_fclose_ FILE *original = NULL; + long lstchg; + + /* First we update the user database itself */ + passwd_path = prefix_roota(arg_root, "/etc/passwd"); + r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); + if (r < 0) + goto finish; + + original = fopen(passwd_path, "re"); + if (original) { + struct passwd *pw; + + r = sync_rights(original, passwd); + if (r < 0) + goto finish; + + errno = 0; + while ((pw = fgetpwent(original))) { + + i = hashmap_get(users, pw->pw_name); + if (i && i->todo_user) { + log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); + r = -EEXIST; + goto finish; + } + + if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { + log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); + r = -EEXIST; + goto finish; + } + + errno = 0; + if (putpwent(pw, passwd) < 0) { + r = errno ? -errno : -EIO; + goto finish; + } + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(passwd), 0644) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct passwd n = { + .pw_name = i->name, + .pw_uid = i->uid, + .pw_gid = i->gid, + .pw_gecos = i->description, + + /* "x" means the password is stored in + * the shadow file */ + .pw_passwd = (char*) "x", + + /* We default to the root directory as home */ + .pw_dir = i->home ? i->home : (char*) "/", + + /* Initialize the shell to nologin, + * with one exception: for root we + * patch in something special */ + .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", + }; + + errno = 0; + if (putpwent(&n, passwd) != 0) { + r = errno ? -errno : -EIO; + goto finish; + } + } + + r = fflush_and_check(passwd); + if (r < 0) + goto finish; + + if (original) { + fclose(original); + original = NULL; + } + + /* The we update the shadow database */ + shadow_path = prefix_roota(arg_root, "/etc/shadow"); + r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + if (r < 0) + goto finish; + + lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + + original = fopen(shadow_path, "re"); + if (original) { + struct spwd *sp; + + r = sync_rights(original, shadow); + if (r < 0) + goto finish; + + errno = 0; + while ((sp = fgetspent(original))) { + + i = hashmap_get(users, sp->sp_namp); + if (i && i->todo_user) { + /* we will update the existing entry */ + sp->sp_lstchg = lstchg; + + /* only the /etc/shadow stage is left, so we can + * safely remove the item from the todo set */ + i->todo_user = false; + hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); + } + + errno = 0; + if (putspent(sp, shadow) < 0) { + r = errno ? -errno : -EIO; + goto finish; + } + + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) { + r = -errno; + goto finish; + } + } else if (errno != ENOENT) { + r = -errno; + goto finish; + } else if (fchmod(fileno(shadow), 0000) < 0) { + r = -errno; + goto finish; + } + + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct spwd n = { + .sp_namp = i->name, + .sp_pwdp = (char*) "!!", + .sp_lstchg = lstchg, + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; + + errno = 0; + if (putspent(&n, shadow) != 0) { + r = errno ? -errno : -EIO; + goto finish; + } + } + + r = fflush_and_check(shadow); + if (r < 0) + goto finish; + } + + /* Make a backup of the old files */ + if (group_changed) { + if (group) { + r = make_backup("/etc/group", group_path); + if (r < 0) + goto finish; + } + if (gshadow) { + r = make_backup("/etc/gshadow", gshadow_path); + if (r < 0) + goto finish; + } + } + + if (passwd) { + r = make_backup("/etc/passwd", passwd_path); + if (r < 0) + goto finish; + } + if (shadow) { + r = make_backup("/etc/shadow", shadow_path); + if (r < 0) + goto finish; + } + + /* And make the new files count */ + if (group_changed) { + if (group) { + r = rename_and_apply_smack(group_tmp, group_path); + if (r < 0) + goto finish; + + group_tmp = mfree(group_tmp); + } + if (gshadow) { + r = rename_and_apply_smack(gshadow_tmp, gshadow_path); + if (r < 0) + goto finish; + + gshadow_tmp = mfree(gshadow_tmp); + } + } + + if (passwd) { + r = rename_and_apply_smack(passwd_tmp, passwd_path); + if (r < 0) + goto finish; + + passwd_tmp = mfree(passwd_tmp); + } + if (shadow) { + r = rename_and_apply_smack(shadow_tmp, shadow_path); + if (r < 0) + goto finish; + + shadow_tmp = mfree(shadow_tmp); + } + + r = 0; + +finish: + if (passwd_tmp) + unlink(passwd_tmp); + if (shadow_tmp) + unlink(shadow_tmp); + if (group_tmp) + unlink(group_tmp); + if (gshadow_tmp) + unlink(gshadow_tmp); + + return r; +} + +static int uid_is_ok(uid_t uid, const char *name) { + struct passwd *p; + struct group *g; + const char *n; + Item *i; + + /* Let's see if we already have assigned the UID a second time */ + if (hashmap_get(todo_uids, UID_TO_PTR(uid))) + return 0; + + /* Try to avoid using uids that are already used by a group + * that doesn't have the same name as our new user. */ + i = hashmap_get(todo_gids, GID_TO_PTR(uid)); + if (i && !streq(i->name, name)) + return 0; + + /* Let's check the files directly */ + if (hashmap_contains(database_uid, UID_TO_PTR(uid))) + return 0; + + n = hashmap_get(database_gid, GID_TO_PTR(uid)); + if (n && !streq(n, name)) + return 0; + + /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */ + if (!arg_root) { + errno = 0; + p = getpwuid(uid); + if (p) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + errno = 0; + g = getgrgid((gid_t) uid); + if (g) { + if (!streq(g->gr_name, name)) + return 0; + } else if (!IN_SET(errno, 0, ENOENT)) + return -errno; + } + + return 1; +} + +static int root_stat(const char *p, struct stat *st) { + const char *fix; + + fix = prefix_roota(arg_root, p); + if (stat(fix, st) < 0) + return -errno; + + return 0; +} + +static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) { + struct stat st; + bool found_uid = false, found_gid = false; + uid_t uid = 0; + gid_t gid = 0; + + assert(i); + + /* First, try to get the gid directly */ + if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) { + gid = st.st_gid; + found_gid = true; + } + + /* Then, try to get the uid directly */ + if ((_uid || (_gid && !found_gid)) + && i->uid_path + && root_stat(i->uid_path, &st) >= 0) { + + uid = st.st_uid; + found_uid = true; + + /* If we need the gid, but had no success yet, also derive it from the uid path */ + if (_gid && !found_gid) { + gid = st.st_gid; + found_gid = true; + } + } + + /* If that didn't work yet, then let's reuse the gid as uid */ + if (_uid && !found_uid && i->gid_path) { + + if (found_gid) { + uid = (uid_t) gid; + found_uid = true; + } else if (root_stat(i->gid_path, &st) >= 0) { + uid = (uid_t) st.st_gid; + found_uid = true; + } + } + + if (_uid) { + if (!found_uid) + return 0; + + *_uid = uid; + } + + if (_gid) { + if (!found_gid) + return 0; + + *_gid = gid; + } + + return 1; +} + +static int add_user(Item *i) { + void *z; + int r; + + assert(i); + + /* Check the database directly */ + z = hashmap_get(database_user, i->name); + if (z) { + log_debug("User %s already exists.", i->name); + i->uid = PTR_TO_UID(z); + i->uid_set = true; + return 0; + } + + if (!arg_root) { + struct passwd *p; + + /* Also check NSS */ + errno = 0; + p = getpwnam(i->name); + if (p) { + log_debug("User %s already exists.", i->name); + i->uid = p->pw_uid; + i->uid_set = true; + + r = free_and_strdup(&i->description, p->pw_gecos); + if (r < 0) + return log_oom(); + + return 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name); + } + + /* Try to use the suggested numeric uid */ + if (i->uid_set) { + r = uid_is_ok(i->uid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + if (r == 0) { + log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name); + i->uid_set = false; + } + } + + /* If that didn't work, try to read it from the specified path */ + if (!i->uid_set) { + uid_t c; + + if (read_id_from_file(i, &c, NULL) > 0) { + + if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); + else { + r = uid_is_ok(c, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) { + i->uid = c; + i->uid_set = true; + } else + log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name); + } + } + } + + /* Otherwise, try to reuse the group ID */ + if (!i->uid_set && i->gid_set) { + r = uid_is_ok((uid_t) i->gid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + if (r > 0) { + i->uid = (uid_t) i->gid; + i->uid_set = true; + } + } + + /* And if that didn't work either, let's try to find a free one */ + if (!i->uid_set) { + for (;;) { + r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + if (r < 0) { + log_error("No free user ID available for %s.", i->name); + return r; + } + + r = uid_is_ok(search_uid, i->name); + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) + break; + } + + i->uid_set = true; + i->uid = search_uid; + } + + r = hashmap_ensure_allocated(&todo_uids, NULL); + if (r < 0) + return log_oom(); + + r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); + if (r < 0) + return log_oom(); + + i->todo_user = true; + log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid); + + return 0; +} + +static int gid_is_ok(gid_t gid) { + struct group *g; + struct passwd *p; + + if (hashmap_get(todo_gids, GID_TO_PTR(gid))) + return 0; + + /* Avoid reusing gids that are already used by a different user */ + if (hashmap_get(todo_uids, UID_TO_PTR(gid))) + return 0; + + if (hashmap_contains(database_gid, GID_TO_PTR(gid))) + return 0; + + if (hashmap_contains(database_uid, UID_TO_PTR(gid))) + return 0; + + if (!arg_root) { + errno = 0; + g = getgrgid(gid); + if (g) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + errno = 0; + p = getpwuid((uid_t) gid); + if (p) + return 0; + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + } + + return 1; +} + +static int add_group(Item *i) { + void *z; + int r; + + assert(i); + + /* Check the database directly */ + z = hashmap_get(database_group, i->name); + if (z) { + log_debug("Group %s already exists.", i->name); + i->gid = PTR_TO_GID(z); + i->gid_set = true; + return 0; + } + + /* Also check NSS */ + if (!arg_root) { + struct group *g; + + errno = 0; + g = getgrnam(i->name); + if (g) { + log_debug("Group %s already exists.", i->name); + i->gid = g->gr_gid; + i->gid_set = true; + return 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); + } + + /* Try to use the suggested numeric gid */ + if (i->gid_set) { + r = gid_is_ok(i->gid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + if (r == 0) { + log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); + i->gid_set = false; + } + } + + /* Try to reuse the numeric uid, if there's one */ + if (!i->gid_set && i->uid_set) { + r = gid_is_ok((gid_t) i->uid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + if (r > 0) { + i->gid = (gid_t) i->uid; + i->gid_set = true; + } + } + + /* If that didn't work, try to read it from the specified path */ + if (!i->gid_set) { + gid_t c; + + if (read_id_from_file(i, NULL, &c) > 0) { + + if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); + else { + r = gid_is_ok(c); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) { + i->gid = c; + i->gid_set = true; + } else + log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name); + } + } + } + + /* And if that didn't work either, let's try to find a free one */ + if (!i->gid_set) { + for (;;) { + /* We look for new GIDs in the UID pool! */ + r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + if (r < 0) { + log_error("No free group ID available for %s.", i->name); + return r; + } + + r = gid_is_ok(search_uid); + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) + break; + } + + i->gid_set = true; + i->gid = search_uid; + } + + r = hashmap_ensure_allocated(&todo_gids, NULL); + if (r < 0) + return log_oom(); + + r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); + if (r < 0) + return log_oom(); + + i->todo_group = true; + log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid); + + return 0; +} + +static int process_item(Item *i) { + int r; + + assert(i); + + switch (i->type) { + + case ADD_USER: + r = add_group(i); + if (r < 0) + return r; + + return add_user(i); + + case ADD_GROUP: { + Item *j; + + j = hashmap_get(users, i->name); + if (j) { + /* There's already user to be created for this + * name, let's process that in one step */ + + if (i->gid_set) { + j->gid = i->gid; + j->gid_set = true; + } + + if (i->gid_path) { + r = free_and_strdup(&j->gid_path, i->gid_path); + if (r < 0) + return log_oom(); + } + + return 0; + } + + return add_group(i); + } + + default: + assert_not_reached("Unknown item type"); + } +} + +static void item_free(Item *i) { + + if (!i) + return; + + free(i->name); + free(i->uid_path); + free(i->gid_path); + free(i->description); + free(i); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); + +static int add_implicit(void) { + char *g, **l; + Iterator iterator; + int r; + + /* Implicitly create additional users and groups, if they were listed in "m" lines */ + + HASHMAP_FOREACH_KEY(l, g, members, iterator) { + Item *i; + char **m; + + i = hashmap_get(groups, g); + if (!i) { + _cleanup_(item_freep) Item *j = NULL; + + r = hashmap_ensure_allocated(&groups, &string_hash_ops); + if (r < 0) + return log_oom(); + + j = new0(Item, 1); + if (!j) + return log_oom(); + + j->type = ADD_GROUP; + j->name = strdup(g); + if (!j->name) + return log_oom(); + + r = hashmap_put(groups, j->name, j); + if (r < 0) + return log_oom(); + + log_debug("Adding implicit group '%s' due to m line", j->name); + j = NULL; + } + + STRV_FOREACH(m, l) { + + i = hashmap_get(users, *m); + if (!i) { + _cleanup_(item_freep) Item *j = NULL; + + r = hashmap_ensure_allocated(&users, &string_hash_ops); + if (r < 0) + return log_oom(); + + j = new0(Item, 1); + if (!j) + return log_oom(); + + j->type = ADD_USER; + j->name = strdup(*m); + if (!j->name) + return log_oom(); + + r = hashmap_put(users, j->name, j); + if (r < 0) + return log_oom(); + + log_debug("Adding implicit user '%s' due to m line", j->name); + j = NULL; + } + } + } + + return 0; +} + +static bool item_equal(Item *a, Item *b) { + assert(a); + assert(b); + + if (a->type != b->type) + return false; + + if (!streq_ptr(a->name, b->name)) + return false; + + if (!streq_ptr(a->uid_path, b->uid_path)) + return false; + + if (!streq_ptr(a->gid_path, b->gid_path)) + return false; + + if (!streq_ptr(a->description, b->description)) + return false; + + if (a->uid_set != b->uid_set) + return false; + + if (a->uid_set && a->uid != b->uid) + return false; + + if (a->gid_set != b->gid_set) + return false; + + if (a->gid_set && a->gid != b->gid) + return false; + + if (!streq_ptr(a->home, b->home)) + return false; + + return true; +} + +static bool valid_user_group_name(const char *u) { + const char *i; + long sz; + + if (isempty(u)) + return false; + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && + u[0] != '_') + return false; + + for (i = u+1; *i; i++) { + if (!(*i >= 'a' && *i <= 'z') && + !(*i >= 'A' && *i <= 'Z') && + !(*i >= '0' && *i <= '9') && + *i != '_' && + *i != '-') + return false; + } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); + + if ((size_t) (i-u) > (size_t) sz) + return false; + + if ((size_t) (i-u) > UT_NAMESIZE - 1) + return false; + + return true; +} + +static bool valid_gecos(const char *d) { + + if (!d) + return false; + + if (!utf8_is_valid(d)) + return false; + + if (string_has_cc(d, NULL)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(d, ':')) + return false; + + return true; +} + +static bool valid_home(const char *p) { + + if (isempty(p)) + return false; + + if (!utf8_is_valid(p)) + return false; + + if (string_has_cc(p, NULL)) + return false; + + if (!path_is_absolute(p)) + return false; + + if (!path_is_safe(p)) + return false; + + /* Colons are used as field separators, and hence not OK */ + if (strchr(p, ':')) + return false; + + return true; +} + +static int parse_line(const char *fname, unsigned line, const char *buffer) { + + static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} + }; + + _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL; + _cleanup_(item_freep) Item *i = NULL; + Item *existing; + Hashmap *h; + int r; + const char *p; + + assert(fname); + assert(line >= 1); + assert(buffer); + + /* Parse columns */ + p = buffer; + r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL); + if (r < 0) { + log_error("[%s:%u] Syntax error.", fname, line); + return r; + } + if (r < 2) { + log_error("[%s:%u] Missing action and name columns.", fname, line); + return -EINVAL; + } + if (!isempty(p)) { + log_error("[%s:%u] Trailing garbage.", fname, line); + return -EINVAL; + } + + /* Verify action */ + if (strlen(action) != 1) { + log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); + return -EINVAL; + } + + if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { + log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]); + return -EBADMSG; + } + + /* Verify name */ + if (isempty(name) || streq(name, "-")) + name = mfree(name); + + if (name) { + r = specifier_printf(name, specifier_table, NULL, &resolved_name); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); + return r; + } + + if (!valid_user_group_name(resolved_name)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); + return -EINVAL; + } + } + + /* Verify id */ + if (isempty(id) || streq(id, "-")) + id = mfree(id); + + if (id) { + r = specifier_printf(id, specifier_table, NULL, &resolved_id); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); + return r; + } + } + + /* Verify description */ + if (isempty(description) || streq(description, "-")) + description = mfree(description); + + if (description) { + if (!valid_gecos(description)) { + log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description); + return -EINVAL; + } + } + + /* Verify home */ + if (isempty(home) || streq(home, "-")) + home = mfree(home); + + if (home) { + if (!valid_home(home)) { + log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home); + return -EINVAL; + } + } + + switch (action[0]) { + + case ADD_RANGE: + if (resolved_name) { + log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); + return -EINVAL; + } + + if (!resolved_id) { + log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); + if (r < 0) { + log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); + return -EINVAL; + } + + return 0; + + case ADD_MEMBER: { + char **l; + + /* Try to extend an existing member or group item */ + if (!name) { + log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); + return -EINVAL; + } + + if (!resolved_id) { + log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); + return -EINVAL; + } + + if (!valid_user_group_name(resolved_id)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&members, &string_hash_ops); + if (r < 0) + return log_oom(); + + l = hashmap_get(members, resolved_id); + if (l) { + /* A list for this group name already exists, let's append to it */ + r = strv_push(&l, resolved_name); + if (r < 0) + return log_oom(); + + resolved_name = NULL; + + assert_se(hashmap_update(members, resolved_id, l) >= 0); + } else { + /* No list for this group name exists yet, create one */ + + l = new0(char *, 2); + if (!l) + return -ENOMEM; + + l[0] = resolved_name; + l[1] = NULL; + + r = hashmap_put(members, resolved_id, l); + if (r < 0) { + free(l); + return log_oom(); + } + + resolved_id = resolved_name = NULL; + } + + return 0; + } + + case ADD_USER: + if (!name) { + log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&users, &string_hash_ops); + if (r < 0) + return log_oom(); + + i = new0(Item, 1); + if (!i) + return log_oom(); + + if (resolved_id) { + if (path_is_absolute(resolved_id)) { + i->uid_path = resolved_id; + resolved_id = NULL; + + path_kill_slashes(i->uid_path); + } else { + r = parse_uid(resolved_id, &i->uid); + if (r < 0) { + log_error("Failed to parse UID: %s", id); + return -EBADMSG; + } + + i->uid_set = true; + } + } + + i->description = description; + description = NULL; + + i->home = home; + home = NULL; + + h = users; + break; + + case ADD_GROUP: + if (!name) { + log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); + return -EINVAL; + } + + if (description) { + log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line); + return -EINVAL; + } + + if (home) { + log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line); + return -EINVAL; + } + + r = hashmap_ensure_allocated(&groups, &string_hash_ops); + if (r < 0) + return log_oom(); + + i = new0(Item, 1); + if (!i) + return log_oom(); + + if (resolved_id) { + if (path_is_absolute(resolved_id)) { + i->gid_path = resolved_id; + resolved_id = NULL; + + path_kill_slashes(i->gid_path); + } else { + r = parse_gid(resolved_id, &i->gid); + if (r < 0) { + log_error("Failed to parse GID: %s", id); + return -EBADMSG; + } + + i->gid_set = true; + } + } + + h = groups; + break; + + default: + return -EBADMSG; + } + + i->type = action[0]; + i->name = resolved_name; + resolved_name = NULL; + + existing = hashmap_get(h, i->name); + if (existing) { + + /* Two identical items are fine */ + if (!item_equal(existing, i)) + log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); + + return 0; + } + + r = hashmap_put(h, i->name, i); + if (r < 0) + return log_oom(); + + i = NULL; + return 0; +} + +static int read_config_file(const char *fn, bool ignore_enoent) { + _cleanup_fclose_ FILE *rf = NULL; + FILE *f = NULL; + char line[LINE_MAX]; + unsigned v = 0; + int r = 0; + + assert(fn); + + if (streq(fn, "-")) + f = stdin; + else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); + } + + f = rf; + } + + FOREACH_LINE(line, f, break) { + char *l; + int k; + + v++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + k = parse_line(fn, v, l); + if (k < 0 && r == 0) + r = k; + } + + if (ferror(f)) { + log_error_errno(errno, "Failed to read from file %s: %m", fn); + if (r == 0) + r = -EIO; + } + + return r; +} + +static void free_database(Hashmap *by_name, Hashmap *by_id) { + char *name; + + for (;;) { + name = hashmap_first(by_id); + if (!name) + break; + + hashmap_remove(by_name, name); + + hashmap_steal_first_key(by_id); + free(name); + } + + while ((name = hashmap_steal_first_key(by_name))) + free(name); + + hashmap_free(by_name); + hashmap_free(by_id); +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Creates system user accounts.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=PATH Operate on an alternate filesystem root\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + + _cleanup_close_ int lock = -1; + Iterator iterator; + int r, k; + Item *i; + char *n; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "SELinux setup failed: %m"); + goto finish; + } + + if (optind < argc) { + int j; + + for (j = optind; j < argc; j++) { + k = read_config_file(argv[j], false); + if (k < 0 && r == 0) + r = k; + } + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = read_config_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + + if (!uid_range) { + /* Default to default range of 1..SYSTEMD_UID_MAX */ + r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); + if (r < 0) { + log_oom(); + goto finish; + } + } + + r = add_implicit(); + if (r < 0) + goto finish; + + lock = take_etc_passwd_lock(arg_root); + if (lock < 0) { + log_error_errno(lock, "Failed to take lock: %m"); + goto finish; + } + + r = load_user_database(); + if (r < 0) { + log_error_errno(r, "Failed to load user database: %m"); + goto finish; + } + + r = load_group_database(); + if (r < 0) { + log_error_errno(r, "Failed to read group database: %m"); + goto finish; + } + + HASHMAP_FOREACH(i, groups, iterator) + process_item(i); + + HASHMAP_FOREACH(i, users, iterator) + process_item(i); + + r = write_files(); + if (r < 0) + log_error_errno(r, "Failed to write files: %m"); + +finish: + while ((i = hashmap_steal_first(groups))) + item_free(i); + + while ((i = hashmap_steal_first(users))) + item_free(i); + + while ((n = hashmap_first_key(members))) { + strv_free(hashmap_steal_first(members)); + free(n); + } + + hashmap_free(groups); + hashmap_free(users); + hashmap_free(members); + hashmap_free(todo_uids); + hashmap_free(todo_gids); + + free_database(database_user, database_uid); + free_database(database_group, database_gid); + + free(arg_root); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-sysusers/sysusers.d.xml b/src/grp-initprogs/systemd-sysusers/sysusers.d.xml new file mode 100644 index 0000000000..18ee3800d6 --- /dev/null +++ b/src/grp-initprogs/systemd-sysusers/sysusers.d.xml @@ -0,0 +1,223 @@ + + + + + + + + sysusers.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sysusers.d + 5 + + + + sysusers.d + Declarative allocation of system users and groups + + + + /usr/lib/sysusers.d/*.conf + + + + Description + + systemd-sysusers uses the files from + sysusers.d directory to create system users + and groups at package installation or boot time. This tool may be + used to allocate system users and groups only, it is not useful + for creating non-system users and groups, as it accesses + /etc/passwd and + /etc/group directly, bypassing any more + complex user databases, for example any database involving NIS or + LDAP. + + + + Configuration Format + + Each configuration file shall be named in the style of + package.conf or + package-part.conf. + The second variant should be used when it is desirable to make it + easy to override just this part of configuration. + + The file format is one line per user or group containing + name, ID, GECOS field description and home directory: + + # Type Name ID GECOS +u httpd 440 "HTTP User" +u authd /usr/bin/authd "Authorization user" +g input - - +m authd input +u root 0 "Superuser" /root + + + Type + + The type consists of a single letter. The following line + types are understood: + + + + u + Create a system user and group of the + specified name should they not exist yet. The user's primary + group will be set to the group bearing the same name. The + user's shell will be set to + /sbin/nologin, the home directory to + the specified home directory, or / if + none is given. The account will be created disabled, so that + logins are not allowed. + + + + g + Create a system group of the specified name + should it not exist yet. Note that u + implicitly create a matching group. The group will be + created with no password set. + + + + m + Add a user to a group. If the user or group + do not exist yet, they will be implicitly + created. + + + + r + Add a range of numeric UIDs/GIDs to the pool + to allocate new UIDs and GIDs from. If no line of this type + is specified, the range of UIDs/GIDs is set to some + compiled-in default. Note that both UIDs and GIDs are + allocated from the same pool, in order to ensure that users + and groups of the same name are likely to carry the same + numeric UID and GID. + + + + + + + Name + + The name field specifies the user or group name. It should + be shorter than 31 characters and avoid any non-ASCII + characters, and not begin with a numeric character. It is + strongly recommended to pick user and group names that are + unlikely to clash with normal users created by the + administrator. A good scheme to guarantee this is by prefixing + all system and group names with the underscore, and avoiding too + generic names. + + For m lines, this field should contain + the user name to add to a group. + + For lines of type r, this field should + be set to -. + + + + ID + + For u and g, the + numeric 32-bit UID or GID of the user/group. Do not use IDs 65535 + or 4294967295, as they have special placeholder meanings. + Specify - for automatic UID/GID allocation + for the user or group. Alternatively, specify an absolute path + in the file system. In this case, the UID/GID is read from the + path's owner/group. This is useful to create users whose UID/GID + match the owners of pre-existing files (such as SUID or SGID + binaries). + + For m lines, this field should contain + the group name to add to a user to. + + For lines of type r, this field should + be set to a UID/GID range in the format + FROM-TO, where both values are formatted as + decimal ASCII numbers. Alternatively, a single UID/GID may be + specified formatted as decimal ASCII numbers. + + + + GECOS + + A short, descriptive string for users to be created, + enclosed in quotation marks. Note that this field may not + contain colons. + + Only applies to lines of type u and + should otherwise be left unset, or be set to + -. + + + + Home Directory + + The home directory for a new system user. If omitted, + defaults to the root directory. It is recommended to not + unnecessarily specify home directories for system users, unless + software strictly requires one to be set. + + Only applies to lines of type u and + should otherwise be left unset, or be set to + -. + + + + + + + + Idempotence + + Note that systemd-sysusers will do + nothing if the specified users or groups already exist, so + normally, there is no reason to override + sysusers.d vendor configuration, except to + block certain users or groups from being created. + + + + See Also + + systemd1, + systemd-sysusers8 + + + + diff --git a/src/grp-initprogs/systemd-tmpfiles/Makefile b/src/grp-initprogs/systemd-tmpfiles/Makefile new file mode 100644 index 0000000000..14da180947 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/Makefile @@ -0,0 +1,88 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_TMPFILES),) +systemd_tmpfiles_SOURCES = \ + src/tmpfiles/tmpfiles.c + +systemd_tmpfiles_CFLAGS = \ + $(ACL_CFLAGS) + +systemd_tmpfiles_LDADD = \ + libsystemd-shared.la \ + $(ACL_LIBS) + +rootbin_PROGRAMS += \ + systemd-tmpfiles + +dist_systemunit_DATA += \ + units/systemd-tmpfiles-clean.timer + +nodist_systemunit_DATA += \ + units/systemd-tmpfiles-setup-dev.service \ + units/systemd-tmpfiles-setup.service \ + units/systemd-tmpfiles-clean.service + +nodist_tmpfiles_DATA = \ + tmpfiles.d/systemd.conf \ + tmpfiles.d/etc.conf + +dist_tmpfiles_DATA = \ + tmpfiles.d/systemd-nologin.conf \ + tmpfiles.d/tmp.conf \ + tmpfiles.d/x11.conf \ + tmpfiles.d/var.conf \ + tmpfiles.d/home.conf \ + tmpfiles.d/systemd-nspawn.conf \ + tmpfiles.d/journal-nocow.conf + +ifneq ($(HAVE_SYSV_COMPAT),) +dist_tmpfiles_DATA += \ + tmpfiles.d/legacy.conf +endif # HAVE_SYSV_COMPAT + +SYSINIT_TARGET_WANTS += \ + systemd-tmpfiles-setup-dev.service \ + systemd-tmpfiles-setup.service + +dist_zshcompletion_data += \ + shell-completion/zsh/_systemd-tmpfiles + +TIMERS_TARGET_WANTS += \ + systemd-tmpfiles-clean.timer + +INSTALL_DIRS += \ + $(tmpfilesdir) \ + $(sysconfdir)/tmpfiles.d +endif # ENABLE_TMPFILES + +EXTRA_DIST += \ + tmpfiles.d/systemd.conf.m4 \ + tmpfiles.d/etc.conf.m4 \ + units/systemd-tmpfiles-setup-dev.service.in \ + units/systemd-tmpfiles-setup.service.in \ + units/systemd-tmpfiles-clean.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 b/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 new file mode 100644 index 0000000000..928105ea8d --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/etc.tmpfiles.m4 @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +L /etc/os-release - - - - ../usr/lib/os-release +L /etc/localtime - - - - ../usr/share/zoneinfo/UTC +L+ /etc/mtab - - - - ../proc/self/mounts +m4_ifdef(`HAVE_SMACK_RUN_LABEL', +t /etc/mtab - - - - security.SMACK64=_ +)m4_dnl +C /etc/nsswitch.conf - - - - +m4_ifdef(`HAVE_PAM', +C /etc/pam.d - - - - +)m4_dnl diff --git a/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles new file mode 100644 index 0000000000..9f25b83392 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/home.tmpfiles @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +Q /home 0755 - - - +q /srv 0755 - - - diff --git a/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles new file mode 100644 index 0000000000..62e2ae0986 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/legacy.tmpfiles @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# These files are considered legacy and are unnecessary on legacy-free +# systems. + +d /run/lock 0755 root root - +L /var/lock - - - - ../run/lock + +# /run/lock/subsys is used for serializing SysV service execution, and +# hence without use on SysV-less systems. + +d /run/lock/subsys 0755 root root - + +# /forcefsck, /fastboot and /forcequotacheck are deprecated in favor of the +# kernel command line options 'fsck.mode=force', 'fsck.mode=skip' and +# 'quotacheck.mode=force' + +r! /forcefsck +r! /fastboot +r! /forcequotacheck diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles new file mode 100644 index 0000000000..a30a8da604 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-nologin.tmpfiles @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5), systemd-user-session.service(5) and pam_nologin(8). +# This file has special suffix so it is not run by mistake. + +F! /run/nologin 0644 - - - "System is booting up. See pam_nologin(8)" diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in new file mode 100644 index 0000000000..133c8c94c4 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Cleanup of Temporary Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target time-sync.target +Before=shutdown.target + +[Service] +Type=oneshot +ExecStart=@rootbindir@/systemd-tmpfiles --clean +IOSchedulingClass=idle diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer new file mode 100644 index 0000000000..9975dcfaca --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-clean.timer @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Daily Cleanup of Temporary Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) + +[Timer] +OnBootSec=15min +OnUnitActiveSec=1d diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in new file mode 100644 index 0000000000..0123a030e4 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup-dev.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create Static Device Nodes in /dev +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-sysusers.service +Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target +ConditionCapability=CAP_SYS_MODULE + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in new file mode 100644 index 0000000000..e895cda0e6 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles-setup.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Create Volatile Files and Directories +Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target systemd-sysusers.service +Before=sysinit.target shutdown.target +RefuseManualStop=yes + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh new file mode 100644 index 0000000000..6ff02e5d98 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.completion.zsh @@ -0,0 +1,13 @@ +#compdef systemd-tmpfiles + +_arguments \ + {-h,--help}'[Show help]' \ + '--version[Show package version]' \ + '--create[Create, set ownership/permissions based on the config files.]' \ + '--clean[Clean up all files and directories with an age parameter configured.]' \ + '--remove[All files and directories marked with r, R in the configuration files are removed.]' \ + '--boot[Execute actions only safe at boot]' \ + '--prefix=[Only apply rules that apply to paths with the specified prefix.]' \ + '--exclude-prefix=[Ignore rules that apply to paths with the specified prefix.]' \ + '--root=[Operate on an alternate filesystem root]:directory:_directories' \ + '*::files:_files' diff --git a/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.xml b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.xml new file mode 100644 index 0000000000..c1aab51551 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/systemd-tmpfiles.xml @@ -0,0 +1,200 @@ + + + + + + + + + systemd-tmpfiles + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-tmpfiles + 8 + + + + systemd-tmpfiles + systemd-tmpfiles-setup.service + systemd-tmpfiles-setup-dev.service + systemd-tmpfiles-clean.service + systemd-tmpfiles-clean.timer + Creates, deletes and cleans up volatile + and temporary files and directories + + + + + systemd-tmpfiles + OPTIONS + CONFIGFILE + + + systemd-tmpfiles-setup.service + systemd-tmpfiles-setup-dev.service + systemd-tmpfiles-clean.service + systemd-tmpfiles-clean.timer + + + + Description + + systemd-tmpfiles creates, deletes, and + cleans up volatile and temporary files and directories, based on + the configuration file format and location specified in + tmpfiles.d5. + + + If invoked with no arguments, it applies all directives from all configuration + files. If one or more absolute filenames are passed on the command line, only the + directives in these files are applied. If - is specified instead + of a filename, directives are read from standard input. If only the basename of a + configuration file is specified, all configuration directories as specified in + tmpfiles.d5 + are searched for a matching file. + + + + Options + + The following options are understood: + + + + + If this option is passed, all files and + directories marked with + f, + F, + w, + d, + D, + v, + p, + L, + c, + b, + m + in the configuration files are created or written to. Files + and directories marked with + z, + Z, + t, + T, + a, and + A have their ownership, access mode and + security labels set. + + + + + If this option is passed, all files and + directories with an age parameter configured will be cleaned + up. + + + + + If this option is passed, the contents of + directories marked with D or + R, and files or directories themselves + marked with r or R are + removed. + + + + Also execute lines with an exclamation mark. + + + + + Only apply rules with paths that start with + the specified prefix. This option can be specified multiple + times. + + + + Ignore rules with paths that start with the + specified prefix. This option can be specified multiple + times. + + + + Takes a directory path as an argument. All + paths will be prefixed with the given alternate + root path, including config search + paths. + + + + + + + It is possible to combine , + , and in one + invocation. For example, during boot the following command line is + executed to ensure that all temporary and volatile directories are + removed and created according to the configuration file: + + systemd-tmpfiles --remove --create + + + + + Unprivileged --cleanup operation + + systemd-tmpfiles tries to avoid changing + the access and modification times on the directories it accesses, + which requires CAP_ADMIN privileges. When + running as non-root, directories which are checked for files to + clean up will have their access time bumped, which might prevent + their cleanup. + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + tmpfiles.d5 + + + + diff --git a/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles new file mode 100644 index 0000000000..fe5225d751 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/tmp.tmpfiles @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Clear tmp directories separately, to make them easier to override +q /tmp 1777 root root 10d +q /var/tmp 1777 root root 30d diff --git a/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c new file mode 100644 index 0000000000..73f578572f --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.c @@ -0,0 +1,2343 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering, Kay Sievers + Copyright 2015 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/capability-util.h" +#include "basic/chattr-util.h" +#include "basic/conf-files.h" +#include "basic/copy.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/io-util.h" +#include "basic/label.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/selinux-util.h" +#include "basic/set.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/acl-util.h" +#include "shared/specifier.h" + +/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates + * them in the file system. This is intended to be used to create + * properly owned directories beneath /tmp, /var/tmp, /run, which are + * volatile and hence need to be recreated on bootup. */ + +typedef enum ItemType { + /* These ones take file names */ + CREATE_FILE = 'f', + TRUNCATE_FILE = 'F', + CREATE_DIRECTORY = 'd', + TRUNCATE_DIRECTORY = 'D', + CREATE_SUBVOLUME = 'v', + CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', + CREATE_SUBVOLUME_NEW_QUOTA = 'Q', + CREATE_FIFO = 'p', + CREATE_SYMLINK = 'L', + CREATE_CHAR_DEVICE = 'c', + CREATE_BLOCK_DEVICE = 'b', + COPY_FILES = 'C', + + /* These ones take globs */ + WRITE_FILE = 'w', + EMPTY_DIRECTORY = 'e', + SET_XATTR = 't', + RECURSIVE_SET_XATTR = 'T', + SET_ACL = 'a', + RECURSIVE_SET_ACL = 'A', + SET_ATTRIBUTE = 'h', + RECURSIVE_SET_ATTRIBUTE = 'H', + IGNORE_PATH = 'x', + IGNORE_DIRECTORY_PATH = 'X', + REMOVE_PATH = 'r', + RECURSIVE_REMOVE_PATH = 'R', + RELABEL_PATH = 'z', + RECURSIVE_RELABEL_PATH = 'Z', + ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */ +} ItemType; + +typedef struct Item { + ItemType type; + + char *path; + char *argument; + char **xattrs; +#ifdef HAVE_ACL + acl_t acl_access; + acl_t acl_default; +#endif + uid_t uid; + gid_t gid; + mode_t mode; + usec_t age; + + dev_t major_minor; + unsigned attribute_value; + unsigned attribute_mask; + + bool uid_set:1; + bool gid_set:1; + bool mode_set:1; + bool age_set:1; + bool mask_perms:1; + bool attribute_set:1; + + bool keep_first_level:1; + + bool force:1; + + bool done:1; +} Item; + +typedef struct ItemArray { + Item *items; + size_t count; + size_t size; +} ItemArray; + +static bool arg_create = false; +static bool arg_clean = false; +static bool arg_remove = false; +static bool arg_boot = false; + +static char **arg_include_prefixes = NULL; +static char **arg_exclude_prefixes = NULL; +static char *arg_root = NULL; + +static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d"); + +#define MAX_DEPTH 256 + +static OrderedHashmap *items = NULL, *globs = NULL; +static Set *unix_sockets = NULL; + +static const Specifier specifier_table[] = { + { 'm', specifier_machine_id, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} +}; + +static bool needs_glob(ItemType t) { + return IN_SET(t, + WRITE_FILE, + IGNORE_PATH, + IGNORE_DIRECTORY_PATH, + REMOVE_PATH, + RECURSIVE_REMOVE_PATH, + EMPTY_DIRECTORY, + ADJUST_MODE, + RELABEL_PATH, + RECURSIVE_RELABEL_PATH, + SET_XATTR, + RECURSIVE_SET_XATTR, + SET_ACL, + RECURSIVE_SET_ACL, + SET_ATTRIBUTE, + RECURSIVE_SET_ATTRIBUTE); +} + +static bool takes_ownership(ItemType t) { + return IN_SET(t, + CREATE_FILE, + TRUNCATE_FILE, + CREATE_DIRECTORY, + EMPTY_DIRECTORY, + TRUNCATE_DIRECTORY, + CREATE_SUBVOLUME, + CREATE_SUBVOLUME_INHERIT_QUOTA, + CREATE_SUBVOLUME_NEW_QUOTA, + CREATE_FIFO, + CREATE_SYMLINK, + CREATE_CHAR_DEVICE, + CREATE_BLOCK_DEVICE, + COPY_FILES, + WRITE_FILE, + IGNORE_PATH, + IGNORE_DIRECTORY_PATH, + REMOVE_PATH, + RECURSIVE_REMOVE_PATH); +} + +static struct Item* find_glob(OrderedHashmap *h, const char *match) { + ItemArray *j; + Iterator i; + + ORDERED_HASHMAP_FOREACH(j, h, i) { + unsigned n; + + for (n = 0; n < j->count; n++) { + Item *item = j->items + n; + + if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) + return item; + } + } + + return NULL; +} + +static void load_unix_sockets(void) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + + if (unix_sockets) + return; + + /* We maintain a cache of the sockets we found in + * /proc/net/unix to speed things up a little. */ + + unix_sockets = set_new(&string_hash_ops); + if (!unix_sockets) + return; + + f = fopen("/proc/net/unix", "re"); + if (!f) + return; + + /* Skip header */ + if (!fgets(line, sizeof(line), f)) + goto fail; + + for (;;) { + char *p, *s; + int k; + + if (!fgets(line, sizeof(line), f)) + break; + + truncate_nl(line); + + p = strchr(line, ':'); + if (!p) + continue; + + if (strlen(p) < 37) + continue; + + p += 37; + p += strspn(p, WHITESPACE); + p += strcspn(p, WHITESPACE); /* skip one more word */ + p += strspn(p, WHITESPACE); + + if (*p != '/') + continue; + + s = strdup(p); + if (!s) + goto fail; + + path_kill_slashes(s); + + k = set_consume(unix_sockets, s); + if (k < 0 && k != -EEXIST) + goto fail; + } + + return; + +fail: + set_free_free(unix_sockets); + unix_sockets = NULL; +} + +static bool unix_socket_alive(const char *fn) { + assert(fn); + + load_unix_sockets(); + + if (unix_sockets) + return !!set_get(unix_sockets, (char*) fn); + + /* We don't know, so assume yes */ + return true; +} + +static int dir_is_mount_point(DIR *d, const char *subdir) { + + union file_handle_union h = FILE_HANDLE_INIT; + int mount_id_parent, mount_id; + int r_p, r; + + r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0); + if (r_p < 0) + r_p = -errno; + + h.handle.handle_bytes = MAX_HANDLE_SZ; + r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0); + if (r < 0) + r = -errno; + + /* got no handle; make no assumptions, return error */ + if (r_p < 0 && r < 0) + return r_p; + + /* got both handles; if they differ, it is a mount point */ + if (r_p >= 0 && r >= 0) + return mount_id_parent != mount_id; + + /* got only one handle; assume different mount points if one + * of both queries was not supported by the filesystem */ + if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP) + return true; + + /* return error */ + if (r_p < 0) + return r_p; + return r; +} + +static DIR* xopendirat_nomod(int dirfd, const char *path) { + DIR *dir; + + dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME); + if (dir) + return dir; + + log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); + if (errno != EPERM) + return NULL; + + dir = xopendirat(dirfd, path, O_NOFOLLOW); + if (!dir) + log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); + + return dir; +} + +static DIR* opendir_nomod(const char *path) { + return xopendirat_nomod(AT_FDCWD, path); +} + +static int dir_cleanup( + Item *i, + const char *p, + DIR *d, + const struct stat *ds, + usec_t cutoff, + dev_t rootdev, + bool mountpoint, + int maxdepth, + bool keep_this_level) { + + struct dirent *dent; + struct timespec times[2]; + bool deleted = false; + int r = 0; + + while ((dent = readdir(d))) { + struct stat s; + usec_t age; + _cleanup_free_ char *sub_path = NULL; + + if (STR_IN_SET(dent->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { + if (errno == ENOENT) + continue; + + /* FUSE, NFS mounts, SELinux might return EACCES */ + if (errno == EACCES) + log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); + else + log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); + r = -errno; + continue; + } + + /* Stay on the same filesystem */ + if (s.st_dev != rootdev) { + log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name); + continue; + } + + /* Try to detect bind mounts of the same filesystem instance; they + * do not differ in device major/minors. This type of query is not + * supported on all kernels or filesystem types though. */ + if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) { + log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", + p, dent->d_name); + continue; + } + + /* Do not delete read-only files owned by root */ + if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) { + log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name); + continue; + } + + sub_path = strjoin(p, "/", dent->d_name, NULL); + if (!sub_path) { + r = log_oom(); + goto finish; + } + + /* Is there an item configured for this path? */ + if (ordered_hashmap_get(items, sub_path)) { + log_debug("Ignoring \"%s\": a separate entry exists.", sub_path); + continue; + } + + if (find_glob(globs, sub_path)) { + log_debug("Ignoring \"%s\": a separate glob exists.", sub_path); + continue; + } + + if (S_ISDIR(s.st_mode)) { + + if (mountpoint && + streq(dent->d_name, "lost+found") && + s.st_uid == 0) { + log_debug("Ignoring \"%s\".", sub_path); + continue; + } + + if (maxdepth <= 0) + log_warning("Reached max depth on \"%s\".", sub_path); + else { + _cleanup_closedir_ DIR *sub_dir; + int q; + + sub_dir = xopendirat_nomod(dirfd(d), dent->d_name); + if (!sub_dir) { + if (errno != ENOENT) + r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path); + + continue; + } + + q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); + if (q < 0) + r = q; + } + + /* Note: if you are wondering why we don't + * support the sticky bit for excluding + * directories from cleaning like we do it for + * other file system objects: well, the sticky + * bit already has a meaning for directories, + * so we don't want to overload that. */ + + if (keep_this_level) { + log_debug("Keeping \"%s\".", sub_path); + continue; + } + + /* Ignore ctime, we change it when deleting */ + age = timespec_load(&s.st_mtim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + /* Follows spelling in stat(1). */ + log_debug("Directory \"%s\": modify time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_atim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("Directory \"%s\": access time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + log_debug("Removing directory \"%s\".", sub_path); + if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) + if (errno != ENOENT && errno != ENOTEMPTY) { + log_error_errno(errno, "rmdir(%s): %m", sub_path); + r = -errno; + } + + } else { + /* Skip files for which the sticky bit is + * set. These are semantics we define, and are + * unknown elsewhere. See XDG_RUNTIME_DIR + * specification for details. */ + if (s.st_mode & S_ISVTX) { + log_debug("Skipping \"%s\": sticky bit set.", sub_path); + continue; + } + + if (mountpoint && S_ISREG(s.st_mode)) + if (s.st_uid == 0 && STR_IN_SET(dent->d_name, + ".journal", + "aquota.user", + "aquota.group")) { + log_debug("Skipping \"%s\".", sub_path); + continue; + } + + /* Ignore sockets that are listed in /proc/net/unix */ + if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) { + log_debug("Skipping \"%s\": live socket.", sub_path); + continue; + } + + /* Ignore device nodes */ + if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) { + log_debug("Skipping \"%s\": a device.", sub_path); + continue; + } + + /* Keep files on this level around if this is + * requested */ + if (keep_this_level) { + log_debug("Keeping \"%s\".", sub_path); + continue; + } + + age = timespec_load(&s.st_mtim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + /* Follows spelling in stat(1). */ + log_debug("File \"%s\": modify time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_atim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("File \"%s\": access time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + age = timespec_load(&s.st_ctim); + if (age >= cutoff) { + char a[FORMAT_TIMESTAMP_MAX]; + log_debug("File \"%s\": change time %s is too new.", + sub_path, + format_timestamp_us(a, sizeof(a), age)); + continue; + } + + log_debug("unlink \"%s\"", sub_path); + + if (unlinkat(dirfd(d), dent->d_name, 0) < 0) + if (errno != ENOENT) + r = log_error_errno(errno, "unlink(%s): %m", sub_path); + + deleted = true; + } + } + +finish: + if (deleted) { + usec_t age1, age2; + char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; + + /* Restore original directory timestamps */ + times[0] = ds->st_atim; + times[1] = ds->st_mtim; + + age1 = timespec_load(&ds->st_atim); + age2 = timespec_load(&ds->st_mtim); + log_debug("Restoring access and modification time on \"%s\": %s, %s", + p, + format_timestamp_us(a, sizeof(a), age1), + format_timestamp_us(b, sizeof(b), age2)); + if (futimens(dirfd(d), times) < 0) + log_error_errno(errno, "utimensat(%s): %m", p); + } + + return r; +} + +static int path_set_perms(Item *i, const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + + assert(i); + assert(path); + + /* We open the file with O_PATH here, to make the operation + * somewhat atomic. Also there's unfortunately no fchmodat() + * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via + * O_PATH. */ + + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); + + if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + + if (S_ISLNK(st.st_mode)) + log_debug("Skipping mode an owner fix for symlink %s.", path); + else { + char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + xsprintf(fn, "/proc/self/fd/%i", fd); + + /* not using i->path directly because it may be a glob */ + if (i->mode_set) { + mode_t m = i->mode; + + if (i->mask_perms) { + if (!(st.st_mode & 0111)) + m &= ~0111; + if (!(st.st_mode & 0222)) + m &= ~0222; + if (!(st.st_mode & 0444)) + m &= ~0444; + if (!S_ISDIR(st.st_mode)) + m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ + } + + if (m == (st.st_mode & 07777)) + log_debug("\"%s\" has right mode %o", path, st.st_mode); + else { + log_debug("chmod \"%s\" to mode %o", path, m); + if (chmod(fn, m) < 0) + return log_error_errno(errno, "chmod(%s) failed: %m", path); + } + } + + if ((i->uid != st.st_uid || i->gid != st.st_gid) && + (i->uid_set || i->gid_set)) { + log_debug("chown \"%s\" to "UID_FMT"."GID_FMT, + path, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID); + if (chown(fn, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID) < 0) + return log_error_errno(errno, "chown(%s) failed: %m", path); + } + } + + fd = safe_close(fd); + + return label_fix(path, false, false); +} + +static int parse_xattrs_from_arg(Item *i) { + const char *p; + int r; + + assert(i); + assert(i->argument); + + p = i->argument; + + for (;;) { + _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL; + + r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p); + if (r <= 0) + break; + + r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced); + if (r < 0) + return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr); + + r = split_pair(xattr_replaced, "=", &name, &value); + if (r < 0) { + log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr); + continue; + } + + if (isempty(name) || isempty(value)) { + log_warning("Malformed extended attribute found, ignoring: %s", xattr); + continue; + } + + if (strv_push_pair(&i->xattrs, name, value) < 0) + return log_oom(); + + name = value = NULL; + } + + return 0; +} + +static int path_set_xattrs(Item *i, const char *path) { + char **name, **value; + + assert(i); + assert(path); + + STRV_FOREACH_PAIR(name, value, i->xattrs) { + int n; + + n = strlen(*value); + log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); + if (lsetxattr(path, *name, *value, n, 0) < 0) { + log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); + return -errno; + } + } + return 0; +} + +static int parse_acls_from_arg(Item *item) { +#ifdef HAVE_ACL + int r; + + assert(item); + + /* If force (= modify) is set, we will not modify the acl + * afterwards, so the mask can be added now if necessary. */ + + r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force); + if (r < 0) + log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument); +#else + log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring"); +#endif + + return 0; +} + +#ifdef HAVE_ACL +static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) { + _cleanup_(acl_free_charpp) char *t = NULL; + _cleanup_(acl_freep) acl_t dup = NULL; + int r; + + /* Returns 0 for success, positive error if already warned, + * negative error otherwise. */ + + if (modify) { + r = acls_for_file(path, type, acl, &dup); + if (r < 0) + return r; + + r = calc_acl_mask_if_needed(&dup); + if (r < 0) + return r; + } else { + dup = acl_dup(acl); + if (!dup) + return -errno; + + /* the mask was already added earlier if needed */ + } + + r = add_base_acls_if_needed(&dup, path); + if (r < 0) + return r; + + t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE); + log_debug("Setting %s ACL %s on %s.", + type == ACL_TYPE_ACCESS ? "access" : "default", + strna(t), pretty); + + r = acl_set_file(path, type, dup); + if (r < 0) + /* Return positive to indicate we already warned */ + return -log_error_errno(errno, + "Setting %s ACL \"%s\" on %s failed: %m", + type == ACL_TYPE_ACCESS ? "access" : "default", + strna(t), pretty); + + return 0; +} +#endif + +static int path_set_acls(Item *item, const char *path) { + int r = 0; +#ifdef HAVE_ACL + char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd = -1; + struct stat st; + + assert(item); + assert(path); + + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (fd < 0) + return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); + + if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) + return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + + if (S_ISLNK(st.st_mode)) { + log_debug("Skipping ACL fix for symlink %s.", path); + return 0; + } + + xsprintf(fn, "/proc/self/fd/%i", fd); + + if (item->acl_access) + r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); + + if (r == 0 && item->acl_default) + r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); + + if (r > 0) + return -r; /* already warned */ + else if (r == -EOPNOTSUPP) { + log_debug_errno(r, "ACLs not supported by file system at %s", path); + return 0; + } else if (r < 0) + log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); +#endif + return r; +} + +#define ATTRIBUTES_ALL \ + (FS_NOATIME_FL | \ + FS_SYNC_FL | \ + FS_DIRSYNC_FL | \ + FS_APPEND_FL | \ + FS_COMPR_FL | \ + FS_NODUMP_FL | \ + FS_EXTENT_FL | \ + FS_IMMUTABLE_FL | \ + FS_JOURNAL_DATA_FL | \ + FS_SECRM_FL | \ + FS_UNRM_FL | \ + FS_NOTAIL_FL | \ + FS_TOPDIR_FL | \ + FS_NOCOW_FL) + +static int parse_attribute_from_arg(Item *item) { + + static const struct { + char character; + unsigned value; + } attributes[] = { + { 'A', FS_NOATIME_FL }, /* do not update atime */ + { 'S', FS_SYNC_FL }, /* Synchronous updates */ + { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */ + { 'a', FS_APPEND_FL }, /* writes to file may only append */ + { 'c', FS_COMPR_FL }, /* Compress file */ + { 'd', FS_NODUMP_FL }, /* do not dump file */ + { 'e', FS_EXTENT_FL }, /* Extents */ + { 'i', FS_IMMUTABLE_FL }, /* Immutable file */ + { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */ + { 's', FS_SECRM_FL }, /* Secure deletion */ + { 'u', FS_UNRM_FL }, /* Undelete */ + { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ + { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/ + { 'C', FS_NOCOW_FL }, /* Do not cow file */ + }; + + enum { + MODE_ADD, + MODE_DEL, + MODE_SET + } mode = MODE_ADD; + + unsigned value = 0, mask = 0; + const char *p; + + assert(item); + + p = item->argument; + if (p) { + if (*p == '+') { + mode = MODE_ADD; + p++; + } else if (*p == '-') { + mode = MODE_DEL; + p++; + } else if (*p == '=') { + mode = MODE_SET; + p++; + } + } + + if (isempty(p) && mode != MODE_SET) { + log_error("Setting file attribute on '%s' needs an attribute specification.", item->path); + return -EINVAL; + } + + for (; p && *p ; p++) { + unsigned i, v; + + for (i = 0; i < ELEMENTSOF(attributes); i++) + if (*p == attributes[i].character) + break; + + if (i >= ELEMENTSOF(attributes)) { + log_error("Unknown file attribute '%c' on '%s'.", *p, item->path); + return -EINVAL; + } + + v = attributes[i].value; + + SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); + + mask |= v; + } + + if (mode == MODE_SET) + mask |= ATTRIBUTES_ALL; + + assert(mask != 0); + + item->attribute_mask = mask; + item->attribute_value = value; + item->attribute_set = true; + + return 0; +} + +static int path_set_attribute(Item *item, const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + unsigned f; + int r; + + if (!item->attribute_set || item->attribute_mask == 0) + return 0; + + fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); + if (fd < 0) { + if (errno == ELOOP) + return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); + + return log_error_errno(errno, "Cannot open '%s': %m", path); + } + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Cannot stat '%s': %m", path); + + /* Issuing the file attribute ioctls on device nodes is not + * safe, as that will be delivered to the drivers, not the + * file system containing the device node. */ + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { + log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); + return -EINVAL; + } + + f = item->attribute_value & item->attribute_mask; + + /* Mask away directory-specific flags */ + if (!S_ISDIR(st.st_mode)) + f &= ~FS_DIRSYNC_FL; + + r = chattr_fd(fd, f, item->attribute_mask); + if (r < 0) + log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, + r, + "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", + path, item->attribute_value, item->attribute_mask); + + return 0; +} + +static int write_one_file(Item *i, const char *path) { + _cleanup_close_ int fd = -1; + int flags, r = 0; + struct stat st; + + assert(i); + assert(path); + + flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : + i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(path, S_IFREG); + fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); + mac_selinux_create_file_clear(); + } + + if (fd < 0) { + if (i->type == WRITE_FILE && errno == ENOENT) { + log_debug_errno(errno, "Not writing \"%s\": %m", path); + return 0; + } + + r = -errno; + if (!i->argument && errno == EROFS && stat(path, &st) == 0 && + (i->type == CREATE_FILE || st.st_size == 0)) + goto check_mode; + + return log_error_errno(r, "Failed to create file %s: %m", path); + } + + if (i->argument) { + _cleanup_free_ char *unescaped = NULL, *replaced = NULL; + + log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); + + r = cunescape(i->argument, 0, &unescaped); + if (r < 0) + return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument); + + r = specifier_printf(unescaped, specifier_table, NULL, &replaced); + if (r < 0) + return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped); + + r = loop_write(fd, replaced, strlen(replaced), false); + if (r < 0) + return log_error_errno(r, "Failed to write file \"%s\": %m", path); + } else + log_debug("\"%s\" has been created.", path); + + fd = safe_close(fd); + + if (stat(path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", path); + + check_mode: + if (!S_ISREG(st.st_mode)) { + log_error("%s is not a file.", path); + return -EEXIST; + } + + r = path_set_perms(i, path); + if (r < 0) + return r; + + return 0; +} + +typedef int (*action_t)(Item *, const char *); + +static int item_do_children(Item *i, const char *path, action_t action) { + _cleanup_closedir_ DIR *d; + int r = 0; + + assert(i); + assert(path); + + /* This returns the first error we run into, but nevertheless + * tries to go on */ + + d = opendir_nomod(path); + if (!d) + return errno == ENOENT || errno == ENOTDIR ? 0 : -errno; + + for (;;) { + _cleanup_free_ char *p = NULL; + struct dirent *de; + int q; + + errno = 0; + de = readdir(d); + if (!de) { + if (errno > 0 && r == 0) + r = -errno; + + break; + } + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + p = strjoin(path, "/", de->d_name, NULL); + if (!p) + return -ENOMEM; + + q = action(i, p); + if (q < 0 && q != -ENOENT && r == 0) + r = q; + + if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { + q = item_do_children(i, p, action); + if (q < 0 && r == 0) + r = q; + } + } + + return r; +} + +static int glob_item(Item *i, action_t action, bool recursive) { + _cleanup_globfree_ glob_t g = { + .gl_closedir = (void (*)(void *)) closedir, + .gl_readdir = (struct dirent *(*)(void *)) readdir, + .gl_opendir = (void *(*)(const char *)) opendir_nomod, + .gl_lstat = lstat, + .gl_stat = stat, + }; + int r = 0, k; + char **fn; + + errno = 0; + k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); + if (k != 0 && k != GLOB_NOMATCH) + return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); + + STRV_FOREACH(fn, g.gl_pathv) { + k = action(i, *fn); + if (k < 0 && r == 0) + r = k; + + if (recursive) { + k = item_do_children(i, *fn, action); + if (k < 0 && r == 0) + r = k; + } + } + + return r; +} + +typedef enum { + CREATION_NORMAL, + CREATION_EXISTING, + CREATION_FORCE, + _CREATION_MODE_MAX, + _CREATION_MODE_INVALID = -1 +} CreationMode; + +static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { + [CREATION_NORMAL] = "Created", + [CREATION_EXISTING] = "Found existing", + [CREATION_FORCE] = "Created replacement", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); + +static int create_item(Item *i) { + _cleanup_free_ char *resolved = NULL; + struct stat st; + int r = 0; + int q = 0; + CreationMode creation; + + assert(i); + + log_debug("Running create action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + + case IGNORE_PATH: + case IGNORE_DIRECTORY_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + return 0; + + case CREATE_FILE: + case TRUNCATE_FILE: + r = write_one_file(i, i->path); + if (r < 0) + return r; + break; + + case COPY_FILES: { + r = specifier_printf(i->argument, specifier_table, NULL, &resolved); + if (r < 0) + return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument); + + log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path); + r = copy_tree(resolved, i->path, false); + + if (r == -EROFS && stat(i->path, &st) == 0) + r = -EEXIST; + + if (r < 0) { + struct stat a, b; + + if (r != -EEXIST) + return log_error_errno(r, "Failed to copy files to %s: %m", i->path); + + if (stat(resolved, &a) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", resolved); + + if (stat(i->path, &b) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if ((a.st_mode ^ b.st_mode) & S_IFMT) { + log_debug("Can't copy to %s, file exists already and is of different type", i->path); + return 0; + } + } + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + + case WRITE_FILE: + r = glob_item(i, write_one_file, false); + if (r < 0) + return r; + + break; + + case CREATE_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + RUN_WITH_UMASK(0000) + mkdir_parents_label(i->path, 0755); + + if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { + + if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) + + /* Don't create a subvolume unless the + * root directory is one, too. We do + * this under the assumption that if + * the root directory is just a plain + * directory (i.e. very light-weight), + * we shouldn't try to split it up + * into subvolumes (i.e. more + * heavy-weight). Thus, chroot() + * environments and suchlike will get + * a full brtfs subvolume set up below + * their tree only if they + * specifically set up a btrfs + * subvolume for the root dir too. */ + + r = -ENOTTY; + else { + RUN_WITH_UMASK((~i->mode) & 0777) + r = btrfs_subvol_make(i->path); + } + } else + r = 0; + + if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) + RUN_WITH_UMASK(0000) + r = mkdir_label(i->path, i->mode); + + if (r < 0) { + int k; + + if (r != -EEXIST && r != -EROFS) + return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); + + k = is_dir(i->path, false); + if (k == -ENOENT && r == -EROFS) + return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); + if (k < 0) + return log_error_errno(k, "Failed to check if %s exists: %m", i->path); + if (!k) { + log_warning("\"%s\" already exists and is not a directory.", i->path); + return 0; + } + + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + + log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); + + if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { + r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); + if (r == -ENOTTY) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); + else if (r == -EROFS) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); + else if (r == -ENOPROTOOPT) + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); + else if (r < 0) + q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); + else if (r > 0) + log_debug("Adjusted quota for subvolume \"%s\".", i->path); + else if (r == 0) + log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); + } + + /* fall through */ + + case EMPTY_DIRECTORY: + r = path_set_perms(i, i->path); + if (q < 0) + return q; + if (r < 0) + return r; + + break; + + case CREATE_FIFO: + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, S_IFIFO); + r = mkfifo(i->path, i->mode); + mac_selinux_create_file_clear(); + } + + if (r < 0) { + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); + + if (lstat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if (!S_ISFIFO(st.st_mode)) { + + if (i->force) { + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, S_IFIFO); + r = mkfifo_atomic(i->path, i->mode); + mac_selinux_create_file_clear(); + } + + if (r < 0) + return log_error_errno(r, "Failed to create fifo %s: %m", i->path); + creation = CREATION_FORCE; + } else { + log_warning("\"%s\" already exists and is not a fifo.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + } + + case CREATE_SYMLINK: { + r = specifier_printf(i->argument, specifier_table, NULL, &resolved); + if (r < 0) + return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument); + + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = symlink(resolved, i->path); + mac_selinux_create_file_clear(); + + if (r < 0) { + _cleanup_free_ char *x = NULL; + + if (errno != EEXIST) + return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path); + + r = readlink_malloc(i->path, &x); + if (r < 0 || !streq(resolved, x)) { + + if (i->force) { + mac_selinux_create_file_prepare(i->path, S_IFLNK); + r = symlink_atomic(resolved, i->path); + mac_selinux_create_file_clear(); + + if (r < 0) + return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path); + + creation = CREATION_FORCE; + } else { + log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + + creation = CREATION_NORMAL; + log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); + break; + } + + case CREATE_BLOCK_DEVICE: + case CREATE_CHAR_DEVICE: { + mode_t file_type; + + if (have_effective_cap(CAP_MKNOD) == 0) { + /* In a container we lack CAP_MKNOD. We + shouldn't attempt to create the device node in + that case to avoid noise, and we don't support + virtualized devices in containers anyway. */ + + log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); + return 0; + } + + file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, file_type); + r = mknod(i->path, i->mode | file_type, i->major_minor); + mac_selinux_create_file_clear(); + } + + if (r < 0) { + if (errno == EPERM) { + log_debug("We lack permissions, possibly because of cgroup configuration; " + "skipping creation of device node %s.", i->path); + return 0; + } + + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create device node %s: %m", i->path); + + if (lstat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if ((st.st_mode & S_IFMT) != file_type) { + + if (i->force) { + + RUN_WITH_UMASK(0000) { + mac_selinux_create_file_prepare(i->path, file_type); + r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); + mac_selinux_create_file_clear(); + } + + if (r < 0) + return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); + creation = CREATION_FORCE; + } else { + log_debug("%s is not a device node.", i->path); + return 0; + } + } else + creation = CREATION_EXISTING; + } else + creation = CREATION_NORMAL; + + log_debug("%s %s device node \"%s\" %u:%u.", + creation_mode_verb_to_string(creation), + i->type == CREATE_BLOCK_DEVICE ? "block" : "char", + i->path, major(i->mode), minor(i->mode)); + + r = path_set_perms(i, i->path); + if (r < 0) + return r; + + break; + } + + case ADJUST_MODE: + case RELABEL_PATH: + r = glob_item(i, path_set_perms, false); + if (r < 0) + return r; + break; + + case RECURSIVE_RELABEL_PATH: + r = glob_item(i, path_set_perms, true); + if (r < 0) + return r; + break; + + case SET_XATTR: + r = glob_item(i, path_set_xattrs, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_XATTR: + r = glob_item(i, path_set_xattrs, true); + if (r < 0) + return r; + break; + + case SET_ACL: + r = glob_item(i, path_set_acls, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_ACL: + r = glob_item(i, path_set_acls, true); + if (r < 0) + return r; + break; + + case SET_ATTRIBUTE: + r = glob_item(i, path_set_attribute, false); + if (r < 0) + return r; + break; + + case RECURSIVE_SET_ATTRIBUTE: + r = glob_item(i, path_set_attribute, true); + if (r < 0) + return r; + break; + } + + return 0; +} + +static int remove_item_instance(Item *i, const char *instance) { + int r; + + assert(i); + + switch (i->type) { + + case REMOVE_PATH: + if (remove(instance) < 0 && errno != ENOENT) + return log_error_errno(errno, "rm(%s): %m", instance); + + break; + + case TRUNCATE_DIRECTORY: + case RECURSIVE_REMOVE_PATH: + /* FIXME: we probably should use dir_cleanup() here + * instead of rm_rf() so that 'x' is honoured. */ + log_debug("rm -rf \"%s\"", instance); + r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "rm_rf(%s): %m", instance); + + break; + + default: + assert_not_reached("wut?"); + } + + return 0; +} + +static int remove_item(Item *i) { + assert(i); + + log_debug("Running remove action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + + case REMOVE_PATH: + case TRUNCATE_DIRECTORY: + case RECURSIVE_REMOVE_PATH: + return glob_item(i, remove_item_instance, false); + + default: + return 0; + } +} + +static int clean_item_instance(Item *i, const char* instance) { + _cleanup_closedir_ DIR *d = NULL; + struct stat s, ps; + bool mountpoint; + usec_t cutoff, n; + char timestamp[FORMAT_TIMESTAMP_MAX]; + + assert(i); + + if (!i->age_set) + return 0; + + n = now(CLOCK_REALTIME); + if (n < i->age) + return 0; + + cutoff = n - i->age; + + d = opendir_nomod(instance); + if (!d) { + if (IN_SET(errno, ENOENT, ENOTDIR)) { + log_debug_errno(errno, "Directory \"%s\": %m", instance); + return 0; + } + + return log_error_errno(errno, "Failed to open directory %s: %m", instance); + } + + if (fstat(dirfd(d), &s) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); + + if (!S_ISDIR(s.st_mode)) { + log_error("%s is not a directory.", i->path); + return -ENOTDIR; + } + + if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) + return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); + + mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino; + + log_debug("Cleanup threshold for %s \"%s\" is %s", + mountpoint ? "mount point" : "directory", + instance, + format_timestamp_us(timestamp, sizeof(timestamp), cutoff)); + + return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, + MAX_DEPTH, i->keep_first_level); +} + +static int clean_item(Item *i) { + assert(i); + + log_debug("Running clean action for entry %c %s", (char) i->type, i->path); + + switch (i->type) { + case CREATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: + case TRUNCATE_DIRECTORY: + case IGNORE_PATH: + case COPY_FILES: + clean_item_instance(i, i->path); + return 0; + case IGNORE_DIRECTORY_PATH: + return glob_item(i, clean_item_instance, false); + default: + return 0; + } +} + +static int process_item_array(ItemArray *array); + +static int process_item(Item *i) { + int r, q, p, t = 0; + _cleanup_free_ char *prefix = NULL; + + assert(i); + + if (i->done) + return 0; + + i->done = true; + + prefix = malloc(strlen(i->path) + 1); + if (!prefix) + return log_oom(); + + PATH_FOREACH_PREFIX(prefix, i->path) { + ItemArray *j; + + j = ordered_hashmap_get(items, prefix); + if (j) { + int s; + + s = process_item_array(j); + if (s < 0 && t == 0) + t = s; + } + } + + r = arg_create ? create_item(i) : 0; + q = arg_remove ? remove_item(i) : 0; + p = arg_clean ? clean_item(i) : 0; + + return t < 0 ? t : + r < 0 ? r : + q < 0 ? q : + p; +} + +static int process_item_array(ItemArray *array) { + unsigned n; + int r = 0, k; + + assert(array); + + for (n = 0; n < array->count; n++) { + k = process_item(array->items + n); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static void item_free_contents(Item *i) { + assert(i); + free(i->path); + free(i->argument); + strv_free(i->xattrs); + +#ifdef HAVE_ACL + acl_free(i->acl_access); + acl_free(i->acl_default); +#endif +} + +static void item_array_free(ItemArray *a) { + unsigned n; + + if (!a) + return; + + for (n = 0; n < a->count; n++) + item_free_contents(a->items + n); + free(a->items); + free(a); +} + +static int item_compare(const void *a, const void *b) { + const Item *x = a, *y = b; + + /* Make sure that the ownership taking item is put first, so + * that we first create the node, and then can adjust it */ + + if (takes_ownership(x->type) && !takes_ownership(y->type)) + return -1; + if (!takes_ownership(x->type) && takes_ownership(y->type)) + return 1; + + return (int) x->type - (int) y->type; +} + +static bool item_compatible(Item *a, Item *b) { + assert(a); + assert(b); + assert(streq(a->path, b->path)); + + if (takes_ownership(a->type) && takes_ownership(b->type)) + /* check if the items are the same */ + return streq_ptr(a->argument, b->argument) && + + a->uid_set == b->uid_set && + a->uid == b->uid && + + a->gid_set == b->gid_set && + a->gid == b->gid && + + a->mode_set == b->mode_set && + a->mode == b->mode && + + a->age_set == b->age_set && + a->age == b->age && + + a->mask_perms == b->mask_perms && + + a->keep_first_level == b->keep_first_level && + + a->major_minor == b->major_minor; + + return true; +} + +static bool should_include_path(const char *path) { + char **prefix; + + STRV_FOREACH(prefix, arg_exclude_prefixes) + if (path_startswith(path, *prefix)) { + log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.", + path, *prefix); + return false; + } + + STRV_FOREACH(prefix, arg_include_prefixes) + if (path_startswith(path, *prefix)) { + log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix); + return true; + } + + /* no matches, so we should include this path only if we + * have no whitelist at all */ + if (strv_length(arg_include_prefixes) == 0) + return true; + + log_debug("Entry \"%s\" does not match any include prefix, skipping.", path); + return false; +} + +static int parse_line(const char *fname, unsigned line, const char *buffer) { + + _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; + _cleanup_(item_free_contents) Item i = {}; + ItemArray *existing; + OrderedHashmap *h; + int r, pos; + bool force = false, boot = false; + + assert(fname); + assert(line >= 1); + assert(buffer); + + r = extract_many_words( + &buffer, + NULL, + EXTRACT_QUOTES, + &action, + &path, + &mode, + &user, + &group, + &age, + NULL); + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line); + else if (r < 2) { + log_error("[%s:%u] Syntax error.", fname, line); + return -EIO; + } + + if (!isempty(buffer) && !streq(buffer, "-")) { + i.argument = strdup(buffer); + if (!i.argument) + return log_oom(); + } + + if (isempty(action)) { + log_error("[%s:%u] Command too short '%s'.", fname, line, action); + return -EINVAL; + } + + for (pos = 1; action[pos]; pos++) { + if (action[pos] == '!' && !boot) + boot = true; + else if (action[pos] == '+' && !force) + force = true; + else { + log_error("[%s:%u] Unknown modifiers in command '%s'", + fname, line, action); + return -EINVAL; + } + } + + if (boot && !arg_boot) { + log_debug("Ignoring entry %s \"%s\" because --boot is not specified.", + action, path); + return 0; + } + + i.type = action[0]; + i.force = force; + + r = specifier_printf(path, specifier_table, NULL, &i.path); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); + return r; + } + + switch (i.type) { + + case CREATE_DIRECTORY: + case CREATE_SUBVOLUME: + case CREATE_SUBVOLUME_INHERIT_QUOTA: + case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: + case TRUNCATE_DIRECTORY: + case CREATE_FIFO: + case IGNORE_PATH: + case IGNORE_DIRECTORY_PATH: + case REMOVE_PATH: + case RECURSIVE_REMOVE_PATH: + case ADJUST_MODE: + case RELABEL_PATH: + case RECURSIVE_RELABEL_PATH: + if (i.argument) + log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type); + + break; + + case CREATE_FILE: + case TRUNCATE_FILE: + break; + + case CREATE_SYMLINK: + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) + return log_oom(); + } + break; + + case WRITE_FILE: + if (!i.argument) { + log_error("[%s:%u] Write file requires argument.", fname, line); + return -EBADMSG; + } + break; + + case COPY_FILES: + if (!i.argument) { + i.argument = strappend("/usr/share/factory/", i.path); + if (!i.argument) + return log_oom(); + } else if (!path_is_absolute(i.argument)) { + log_error("[%s:%u] Source path is not absolute.", fname, line); + return -EBADMSG; + } + + path_kill_slashes(i.argument); + break; + + case CREATE_CHAR_DEVICE: + case CREATE_BLOCK_DEVICE: { + unsigned major, minor; + + if (!i.argument) { + log_error("[%s:%u] Device file requires argument.", fname, line); + return -EBADMSG; + } + + if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) { + log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument); + return -EBADMSG; + } + + i.major_minor = makedev(major, minor); + break; + } + + case SET_XATTR: + case RECURSIVE_SET_XATTR: + if (!i.argument) { + log_error("[%s:%u] Set extended attribute requires argument.", fname, line); + return -EBADMSG; + } + r = parse_xattrs_from_arg(&i); + if (r < 0) + return r; + break; + + case SET_ACL: + case RECURSIVE_SET_ACL: + if (!i.argument) { + log_error("[%s:%u] Set ACLs requires argument.", fname, line); + return -EBADMSG; + } + r = parse_acls_from_arg(&i); + if (r < 0) + return r; + break; + + case SET_ATTRIBUTE: + case RECURSIVE_SET_ATTRIBUTE: + if (!i.argument) { + log_error("[%s:%u] Set file attribute requires argument.", fname, line); + return -EBADMSG; + } + r = parse_attribute_from_arg(&i); + if (r < 0) + return r; + break; + + default: + log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); + return -EBADMSG; + } + + if (!path_is_absolute(i.path)) { + log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); + return -EBADMSG; + } + + path_kill_slashes(i.path); + + if (!should_include_path(i.path)) + return 0; + + if (arg_root) { + char *p; + + p = prefix_root(arg_root, i.path); + if (!p) + return log_oom(); + + free(i.path); + i.path = p; + } + + if (!isempty(user) && !streq(user, "-")) { + const char *u = user; + + r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); + if (r < 0) { + log_error("[%s:%u] Unknown user '%s'.", fname, line, user); + return r; + } + + i.uid_set = true; + } + + if (!isempty(group) && !streq(group, "-")) { + const char *g = group; + + r = get_group_creds(&g, &i.gid); + if (r < 0) { + log_error("[%s:%u] Unknown group '%s'.", fname, line, group); + return r; + } + + i.gid_set = true; + } + + if (!isempty(mode) && !streq(mode, "-")) { + const char *mm = mode; + unsigned m; + + if (*mm == '~') { + i.mask_perms = true; + mm++; + } + + if (parse_mode(mm, &m) < 0) { + log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); + return -EBADMSG; + } + + i.mode = m; + i.mode_set = true; + } else + i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; + + if (!isempty(age) && !streq(age, "-")) { + const char *a = age; + + if (*a == '~') { + i.keep_first_level = true; + a++; + } + + if (parse_sec(a, &i.age) < 0) { + log_error("[%s:%u] Invalid age '%s'.", fname, line, age); + return -EBADMSG; + } + + i.age_set = true; + } + + h = needs_glob(i.type) ? globs : items; + + existing = ordered_hashmap_get(h, i.path); + if (existing) { + unsigned n; + + for (n = 0; n < existing->count; n++) { + if (!item_compatible(existing->items + n, &i)) { + log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.", + fname, line, i.path); + return 0; + } + } + } else { + existing = new0(ItemArray, 1); + r = ordered_hashmap_put(h, i.path, existing); + if (r < 0) + return log_oom(); + } + + if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1)) + return log_oom(); + + memcpy(existing->items + existing->count++, &i, sizeof(i)); + + /* Sort item array, to enforce stable ordering of application */ + qsort_safe(existing->items, existing->count, sizeof(Item), item_compare); + + zero(i); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --create Create marked files/directories\n" + " --clean Clean up marked directories\n" + " --remove Remove marked files/directories\n" + " --boot Execute actions only safe at boot\n" + " --prefix=PATH Only apply rules with the specified prefix\n" + " --exclude-prefix=PATH Ignore rules with the specified prefix\n" + " --root=PATH Operate on an alternate filesystem root\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_CREATE, + ARG_CLEAN, + ARG_REMOVE, + ARG_BOOT, + ARG_PREFIX, + ARG_EXCLUDE_PREFIX, + ARG_ROOT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "create", no_argument, NULL, ARG_CREATE }, + { "clean", no_argument, NULL, ARG_CLEAN }, + { "remove", no_argument, NULL, ARG_REMOVE }, + { "boot", no_argument, NULL, ARG_BOOT }, + { "prefix", required_argument, NULL, ARG_PREFIX }, + { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX }, + { "root", required_argument, NULL, ARG_ROOT }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_CREATE: + arg_create = true; + break; + + case ARG_CLEAN: + arg_clean = true; + break; + + case ARG_REMOVE: + arg_remove = true; + break; + + case ARG_BOOT: + arg_boot = true; + break; + + case ARG_PREFIX: + if (strv_push(&arg_include_prefixes, optarg) < 0) + return log_oom(); + break; + + case ARG_EXCLUDE_PREFIX: + if (strv_push(&arg_exclude_prefixes, optarg) < 0) + return log_oom(); + break; + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (!arg_clean && !arg_create && !arg_remove) { + log_error("You need to specify at least one of --clean, --create or --remove."); + return -EINVAL; + } + + return 1; +} + +static int read_config_file(const char *fn, bool ignore_enoent) { + _cleanup_fclose_ FILE *_f = NULL; + FILE *f; + char line[LINE_MAX]; + Iterator iterator; + unsigned v = 0; + Item *i; + int r = 0; + + assert(fn); + + if (streq(fn, "-")) { + log_debug("Reading config from stdin."); + fn = ""; + f = stdin; + } else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; + } + + return log_error_errno(r, "Failed to open '%s': %m", fn); + } + log_debug("Reading config file \"%s\".", fn); + f = _f; + } + + FOREACH_LINE(line, f, break) { + char *l; + int k; + + v++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + k = parse_line(fn, v, l); + if (k < 0 && r == 0) + r = k; + } + + /* we have to determine age parameter for each entry of type X */ + ORDERED_HASHMAP_FOREACH(i, globs, iterator) { + Iterator iter; + Item *j, *candidate_item = NULL; + + if (i->type != IGNORE_DIRECTORY_PATH) + continue; + + ORDERED_HASHMAP_FOREACH(j, items, iter) { + if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) + continue; + + if (path_equal(j->path, i->path)) { + candidate_item = j; + break; + } + + if ((!candidate_item && path_startswith(i->path, j->path)) || + (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0))) + candidate_item = j; + } + + if (candidate_item && candidate_item->age_set) { + i->age = candidate_item->age; + i->age_set = true; + } + } + + if (ferror(f)) { + log_error_errno(errno, "Failed to read from file %s: %m", fn); + if (r == 0) + r = -EIO; + } + + return r; +} + +int main(int argc, char *argv[]) { + int r, k; + ItemArray *a; + Iterator iterator; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + mac_selinux_init(); + + items = ordered_hashmap_new(&string_hash_ops); + globs = ordered_hashmap_new(&string_hash_ops); + + if (!items || !globs) { + r = log_oom(); + goto finish; + } + + r = 0; + + if (optind < argc) { + int j; + + for (j = optind; j < argc; j++) { + k = read_config_file(argv[j], false); + if (k < 0 && r == 0) + r = k; + } + + } else { + _cleanup_strv_free_ char **files = NULL; + char **f; + + r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); + if (r < 0) { + log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + k = read_config_file(*f, true); + if (k < 0 && r == 0) + r = k; + } + } + + /* The non-globbing ones usually create things, hence we apply + * them first */ + ORDERED_HASHMAP_FOREACH(a, items, iterator) { + k = process_item_array(a); + if (k < 0 && r == 0) + r = k; + } + + /* The globbing ones usually alter things, hence we apply them + * second. */ + ORDERED_HASHMAP_FOREACH(a, globs, iterator) { + k = process_item_array(a); + if (k < 0 && r == 0) + r = k; + } + +finish: + while ((a = ordered_hashmap_steal_first(items))) + item_array_free(a); + + while ((a = ordered_hashmap_steal_first(globs))) + item_array_free(a); + + ordered_hashmap_free(items); + ordered_hashmap_free(globs); + + free(arg_include_prefixes); + free(arg_exclude_prefixes); + free(arg_root); + + set_free_free(unix_sockets); + + mac_selinux_finish(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-tmpfiles/tmpfiles.d.xml b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.d.xml new file mode 100644 index 0000000000..957475d2bd --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/tmpfiles.d.xml @@ -0,0 +1,703 @@ + + + + + + + tmpfiles.d + systemd + + + + Documentation + Brandon + Philips + brandon@ifup.org + + + + + + tmpfiles.d + 5 + + + + tmpfiles.d + Configuration for creation, deletion and cleaning of + volatile and temporary files + + + + /etc/tmpfiles.d/*.conf + /run/tmpfiles.d/*.conf + /usr/lib/tmpfiles.d/*.conf + + + + Description + + systemd-tmpfiles uses the configuration + files from the above directories to describe the creation, + cleaning and removal of volatile and temporary files and + directories which usually reside in directories such as + /run or /tmp. + + Volatile and temporary files and directories are those + located in /run (and its alias + /var/run), /tmp, + /var/tmp, the API file systems such as + /sys or /proc, as well + as some other directories below /var. + + System daemons frequently require private runtime + directories below /run to place communication + sockets and similar in. For these, consider declaring them in + their unit files using RuntimeDirectory= (see + systemd.exec5 + for details), if this is feasible. + + + + Configuration Format + + Each configuration file shall be named in the style of + package.conf or + package-part.conf. + The second variant should be used when it is desirable to make it + easy to override just this part of configuration. + + Files in /etc/tmpfiles.d override files + with the same name in /usr/lib/tmpfiles.d and + /run/tmpfiles.d. Files in + /run/tmpfiles.d override files with the same + name in /usr/lib/tmpfiles.d. Packages should + install their configuration files in + /usr/lib/tmpfiles.d. Files in + /etc/tmpfiles.d are reserved for the local + administrator, who may use this logic to override the + configuration files installed by vendor packages. All + configuration files are sorted by their filename in lexicographic + order, regardless of which of the directories they reside in. If + multiple files specify the same path, the entry in the file with + the lexicographically earliest name will be applied. All other + conflicting entries will be logged as errors. When two lines are + prefix and suffix of each other, then the prefix is always + processed first, the suffix later. Lines that take globs are + applied after those accepting no globs. If multiple operations + shall be applied on the same file, (such as ACL, xattr, file + attribute adjustments), these are always done in the same fixed + order. Otherwise, the files/directories are processed in the order + they are listed. + + If the administrator wants to disable a configuration file + supplied by the vendor, the recommended way is to place a symlink + to /dev/null in + /etc/tmpfiles.d/ bearing the same filename. + + + The configuration format is one line per path containing + type, path, mode, ownership, age, and argument fields: + + #Type Path Mode UID GID Age Argument + d /run/user 0755 root root 10d - + L /tmp/foobar - - - - /dev/null + + Fields may be enclosed within quotes and contain C-style escapes. + + + Type + + The type consists of a single letter and optionally an + exclamation mark. + + The following line types are understood: + + + + f + Create a file if it does not exist yet. If + the argument parameter is given, it will be written to the + file. Does not follow symlinks. + + + + F + Create or truncate a file. If the argument + parameter is given, it will be written to the file. Does not follow symlinks. + + + + + w + Write the argument parameter to a file, if + the file exists. Lines of this type accept shell-style + globs in place of normal path names. The argument parameter + will be written without a trailing newline. C-style + backslash escapes are interpreted. Follows + symlinks. + + + + d + Create a directory. The mode and ownership will be adjusted if + specified and the directory already exists. Contents of this directory are subject + to time based cleanup if the time argument is specified. + + + + D + Similar to d, but in addition the contents + of the directory will be removed when is used. + + + + + e + Similar to d, but the directory will not be + created if it does not exist. Lines of this type accept shell-style globs in + place of normal path names. + + + + v + Create a subvolume if the path does not + exist yet, the file system supports subvolumes (btrfs), and + the system itself is installed into a subvolume + (specifically: the root directory / is + itself a subvolume). Otherwise, create a normal directory, in + the same way as d. A subvolume created + with this line type is not assigned to any higher-level + quota group. For that, use q or + Q, which allow creating simple quota + group hierarchies, see below. + + + + q + Similar to v. However, + makes sure that the subvolume will be assigned to the same + higher-level quota groups as the subvolume it has been + created in. This ensures that higher-level limits and + accounting applied to the parent subvolume also include the + specified subvolume. On non-btrfs file systems, this line + type is identical to d. If the subvolume + already exists and is already assigned to one or more higher + level quota groups, no change to the quota hierarchy is + made. Also see Q below. See btrfs-qgroup8 + for details about the btrfs quota group + concept. + + + + Q + Similar to q. However, + instead of copying the higher-level quota group assignments + from the parent as-is, the lowest quota group of the parent + subvolume is determined that is not the leaf quota + group. Then, an "intermediary" quota group is inserted that + is one level below this level, and shares the same ID part + as the specified subvolume. If no higher-level quota group + exists for the parent subvolume, a new quota group at level + 255 sharing the same ID as the specified subvolume is + inserted instead. This new intermediary quota group is then + assigned to the parent subvolume's higher-level quota + groups, and the specified subvolume's leaf quota group is + assigned to it. + + Effectively, this has a similar effect as + q, however introduces a new higher-level + quota group for the specified subvolume that may be used to + enforce limits and accounting to the specified subvolume and + children subvolume created within it. Thus, by creating + subvolumes only via q and + Q, a concept of "subtree quotas" is + implemented. Each subvolume for which Q + is set will get a "subtree" quota group created, and all + child subvolumes created within it will be assigned to + it. Each subvolume for which q is set + will not get such a "subtree" quota group, but it is ensured + that they are added to the same "subtree" quota group as their + immediate parents. + + It is recommended to use + Q for subvolumes that typically contain + further subvolumes, and where it is desirable to have + accounting and quota limits on all child subvolumes + together. Examples for Q are typically + /home or + /var/lib/machines. In contrast, + q should be used for subvolumes that + either usually do not include further subvolumes or where no + accounting and quota limits are needed that apply to all + child subvolumes together. Examples for q + are typically /var or + /var/tmp. As with Q, + q has no effect on the quota group + hierarchy if the subvolume exists and already has at least + one higher-level quota group assigned. + + + + p + p+ + Create a named pipe (FIFO) if it does not + exist yet. If suffixed with + and a file + already exists where the pipe is to be created, it will be + removed and be replaced by the pipe. + + + + L + L+ + Create a symlink if it does not exist + yet. If suffixed with + and a file + already exists where the symlink is to be created, it will + be removed and be replaced by the symlink. If the argument + is omitted, symlinks to files with the same name residing in + the directory /usr/share/factory/ are + created. Note that permissions and ownership on symlinks + are ignored. + + + + c + c+ + Create a character device node if it does + not exist yet. If suffixed with + and a + file already exists where the device node is to be created, + it will be removed and be replaced by the device node. It is + recommended to suffix this entry with an exclamation mark to + only create static device nodes at boot, as udev will not + manage static device nodes that are created at runtime. + + + + + b + b+ + Create a block device node if it does not + exist yet. If suffixed with + and a file + already exists where the device node is to be created, it + will be removed and be replaced by the device node. It is + recommended to suffix this entry with an exclamation mark to + only create static device nodes at boot, as udev will not + manage static device nodes that are created at runtime. + + + + + C + Recursively copy a file or directory, if the + destination files or directories do not exist yet. Note that + this command will not descend into subdirectories if the + destination directory already exists. Instead, the entire + copy operation is skipped. If the argument is omitted, files + from the source directory + /usr/share/factory/ with the same name + are copied. Does not follow symlinks. + + + + x + Ignore a path during cleaning. Use this type + to exclude paths from clean-up as controlled with the Age + parameter. Note that lines of this type do not influence the + effect of r or R + lines. Lines of this type accept shell-style globs in place + of normal path names. + + + + X + Ignore a path during cleaning. Use this type + to exclude paths from clean-up as controlled with the Age + parameter. Unlike x, this parameter will + not exclude the content if path is a directory, but only + directory itself. Note that lines of this type do not + influence the effect of r or + R lines. Lines of this type accept + shell-style globs in place of normal path names. + + + + + r + Remove a file or directory if it exists. + This may not be used to remove non-empty directories, use + R for that. Lines of this type accept + shell-style globs in place of normal path + names. Does not follow symlinks. + + + + R + Recursively remove a path and all its + subdirectories (if it is a directory). Lines of this type + accept shell-style globs in place of normal path + names. Does not follow symlinks. + + + + z + Adjust the access mode, group and user, and + restore the SELinux security context of a file or directory, + if it exists. Lines of this type accept shell-style globs in + place of normal path names. Does not follow symlinks. + + + + Z + Recursively set the access mode, group and + user, and restore the SELinux security context of a file or + directory if it exists, as well as of its subdirectories and + the files contained therein (if applicable). Lines of this + type accept shell-style globs in place of normal path + names. Does not follow symlinks. + + + + t + Set extended attributes. Lines of this type + accept shell-style globs in place of normal path names. + This can be useful for setting SMACK labels. Does not follow + symlinks. + + + + T + Recursively set extended attributes. Lines + of this type accept shell-style globs in place of normal + path names. This can be useful for setting SMACK + labels. Does not follow symlinks. + + + + h + Set file/directory attributes. Lines of this type + accept shell-style globs in place of normal path names. + + The format of the argument field is + [+-=][aAcCdDeijsStTu] . The prefix + + (the default one) causes the + attribute(s) to be added; - causes the + attribute(s) to be removed; = causes the + attributes to be set exactly as the following letters. The + letters aAcCdDeijsStTu select the new + attributes for the files, see + chattr + 1 for further information. + + Passing only = as argument resets + all the file attributes listed above. It has to be pointed + out that the = prefix limits itself to + the attributes corresponding to the letters listed here. All + other attributes will be left untouched. Does not follow + symlinks. + + + + + H + Recursively set file/directory attributes. Lines + of this type accept shell-style globs in place of normal + path names. Does not follow symlinks. + + + + + a + a+ + Set POSIX ACLs (access control lists). If + suffixed with +, the specified entries will + be added to the existing set. + systemd-tmpfiles will automatically add + the required base entries for user and group based on the + access mode of the file, unless base entries already exist + or are explicitly specified. The mask will be added if not + specified explicitly or already present. Lines of this type + accept shell-style globs in place of normal path names. This + can be useful for allowing additional access to certain + files. Does not follow symlinks. + + + + A + A+ + Same as a and + a+, but recursive. Does not follow + symlinks. + + + + If the exclamation mark is used, this line is only safe of + execute during boot, and can break a running system. Lines + without the exclamation mark are presumed to be safe to execute + at any time, e.g. on package upgrades. + systemd-tmpfiles will execute line with an + exclamation mark only if option is + given. + + For example: + # Make sure these are created by default so that nobody else can + d /tmp/.X11-unix 1777 root root 10d + + # Unlink the X11 lock files + r! /tmp/.X[0-9]*-lock + The second line in contrast to the first one would break a + running system, and will only be executed with + . + + + + Path + + The file system path specification supports simple + specifier expansion. The following expansions are + understood: + + + Specifiers available + + + + + + + Specifier + Meaning + Details + + + + + %m + Machine ID + The machine ID of the running system, formatted as string. See machine-id5 for more information. + + + %b + Boot ID + The boot ID of the running system, formatted as string. See random4 for more information. + + + %H + Host name + The hostname of the running system. + + + %v + Kernel release + Identical to uname -r output. + + + %% + Escaped % + Single percent sign. + + + +
+
+ + + Mode + + The file access mode to use when creating this file or + directory. If omitted or when set to -, the + default is used: 0755 for directories, 0644 for all other file + objects. For z, Z lines, + if omitted or when set to -, the file access + mode will not be modified. This parameter is ignored for + x, r, + R, L, t, + and a lines. + + Optionally, if prefixed with ~, the + access mode is masked based on the already set access bits for + existing file or directories: if the existing file has all + executable bits unset, all executable bits are removed from the + new access mode, too. Similarly, if all read bits are removed + from the old access mode, they will be removed from the new + access mode too, and if all write bits are removed, they will be + removed from the new access mode too. In addition, the + sticky/SUID/SGID bit is removed unless applied to a + directory. This functionality is particularly useful in + conjunction with Z. + + + + UID, GID + + The user and group to use for this file or directory. This + may either be a numeric user/group ID or a user or group + name. If omitted or when set to -, the + default 0 (root) is used. For z and + Z lines, when omitted or when set to + -, the file ownership will not be + modified. These parameters are ignored for x, + r, R, + L, t, and + a lines. + + + + Age + The date field, when set, is used to decide what files to + delete when cleaning. If a file or directory is older than the + current time minus the age field, it is deleted. The field + format is a series of integers each followed by one of the + following suffixes for the respective time units: + s, + m or min, + h, + d, + w, + ms, and + us, + meaning seconds, minutes, hours, days, weeks, + milliseconds, and microseconds, respectively. Full names of the time units can + be used too. + + + If multiple integers and units are specified, the time + values are summed. If an integer is given without a unit, + s is assumed. + + + When the age is set to zero, the files are cleaned + unconditionally. + + The age field only applies to lines starting with + d, D, e, + v, q, + Q, C, x + and X. If omitted or set to + -, no automatic clean-up is done. + + If the age field starts with a tilde character + ~, the clean-up is only applied to files and + directories one level inside the directory specified, but not + the files and directories immediately inside it. + + + + Argument + + For L lines determines the destination + path of the symlink. For c and + b, determines the major/minor of the device + node, with major and minor formatted as integers, separated by + :, e.g. 1:3. For + f, F, and + w, the argument may be used to specify a short string that + is written to the file, suffixed by a newline. For + C, specifies the source file or + directory. For t and T, + determines extended attributes to be set. For + a and A, determines ACL + attributes to be set. For h and + H, determines the file attributes to + set. Ignored for all other lines. + + +
+ + + Examples + + Create directories with specific mode and ownership + + screen1, + needs two directories created at boot with specific modes and ownership: + + # /usr/lib/tmpfiles.d/screen.conf +d /run/screens 1777 root screen 10d +d /run/uscreens 0755 root screen 10d12h + + + Contents of /run/screens and /run/uscreens will + cleaned up after 10 and 10½ days, respectively. + + + + Create a directory with a SMACK attribute + D /run/cups - - - - +t /run/cups - - - - security.SMACK64=printing user.attr-with-spaces="foo bar" + + + The direcory will be owned by root and have default mode. It's contents are + not subject to time based cleanup, but will be obliterated when + systemd-tmpfiles --remove runs. + + + + Create a directory and prevent its contents from cleanup + + abrt1, + needs a directory created at boot with specific mode and ownership and its content + should be preserved from the automatic cleanup applied to the contents of + /var/tmp: + + # /usr/lib/tmpfiles.d/tmp.conf +d /var/tmp 1777 root root 30d + + + # /usr/lib/tmpfiles.d/abrt.conf +d /var/tmp/abrt 0755 abrt abrt - + + + + + Apply clean up during boot and based on time + + # /usr/lib/tmpfiles.d/dnf.conf +r! /var/cache/dnf/*/*/download_lock.pid +r! /var/cache/dnf/*/*/metadata_lock.pid +r! /var/lib/dnf/rpmdb_lock.pid +e /var/chache/dnf/ - - - 30d + + + The lock files will be removed during boot. Any files and directories in + /var/chache/dnf/ will be removed after they have not been + accessed in 30 days. + + + + + See Also + + systemd1, + systemd-tmpfiles8, + systemd-delta1, + systemd.exec5, + attr5, + getfattr1, + setfattr1, + setfacl1, + getfacl1, + chattr1, + btrfs-subvolume8, + btrfs-qgroup8 + + + +
diff --git a/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles new file mode 100644 index 0000000000..ae7952e77a --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/var.tmpfiles @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +q /var 0755 - - - + +L /var/run - - - - ../run + +d /var/log 0755 - - - +f /var/log/wtmp 0664 root utmp - +f /var/log/btmp 0600 root utmp - + +d /var/cache 0755 - - - + +d /var/lib 0755 - - - + +d /var/spool 0755 - - - diff --git a/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles b/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles new file mode 100644 index 0000000000..4c96a54a13 --- /dev/null +++ b/src/grp-initprogs/systemd-tmpfiles/x11.tmpfiles @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Make sure these are created by default so that nobody else can +d /tmp/.X11-unix 1777 root root 10d +d /tmp/.ICE-unix 1777 root root 10d +d /tmp/.XIM-unix 1777 root root 10d +d /tmp/.font-unix 1777 root root 10d +d /tmp/.Test-unix 1777 root root 10d + +# Unlink the X11 lock files +r! /tmp/.X[0-9]*-lock diff --git a/src/grp-initprogs/systemd-update-done/Makefile b/src/grp-initprogs/systemd-update-done/Makefile new file mode 100644 index 0000000000..258828924a --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-update-done + +systemd_update_done_SOURCES = \ + src/update-done/update-done.c + +systemd_update_done_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in new file mode 100644 index 0000000000..ec7d906392 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Update is Completed +Documentation=man:systemd-update-done.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target +ConditionNeedsUpdate=|/etc +ConditionNeedsUpdate=|/var + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-update-done diff --git a/src/grp-initprogs/systemd-update-done/systemd-update-done.service.xml b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.xml new file mode 100644 index 0000000000..a2dad39f01 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/systemd-update-done.service.xml @@ -0,0 +1,97 @@ + + + + + + + + systemd-update-done.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-update-done.service + 8 + + + + systemd-update-done.service + systemd-update-done + Mark /etc and /var fully updated + + + + systemd-update-done.service + /usr/lib/systemd/systemd-update-done + + + + Description + + systemd-update-done.service is a + service that is invoked as part of the first boot after the vendor + operating system resources in /usr have been + updated. This is useful to implement offline updates of + /usr which might require updates to + /etc or /var on the + following boot. + + systemd-update-done.service updates the + file modification time (mtime) of the stamp files + /etc/.updated and + /var/.updated to the modification time of the + /usr directory, unless the stamp files are + already newer. + + Services that shall run after offline upgrades of + /usr should order themselves before + systemd-update-done.service, and use the + ConditionNeedsUpdate= (see + systemd.unit5) + condition to make sure to run when /etc or + /var are older than /usr + according to the modification times of the files described above. + This requires that updates to /usr are always + followed by an update of the modification time of + /usr, for example by invoking + touch1 + on it. + + + + + See Also + + systemd1, + systemd.unit5, + touch1 + + + + diff --git a/src/grp-initprogs/systemd-update-done/update-done.c b/src/grp-initprogs/systemd-update-done/update-done.c new file mode 100644 index 0000000000..39d19ec048 --- /dev/null +++ b/src/grp-initprogs/systemd-update-done/update-done.c @@ -0,0 +1,115 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/selinux-util.h" +#include "basic/util.h" + +#define MESSAGE \ + "This file was created by systemd-update-done. Its only \n" \ + "purpose is to hold a timestamp of the time this directory\n" \ + "was updated. See systemd-update-done.service(8).\n" + +static int apply_timestamp(const char *path, struct timespec *ts) { + struct timespec twice[2] = { + *ts, + *ts + }; + struct stat st; + + assert(path); + assert(ts); + + if (stat(path, &st) >= 0) { + /* Is the timestamp file already newer than the OS? If + * so, there's nothing to do. We ignore the nanosecond + * component of the timestamp, since some file systems + * do not support any better accuracy than 1s and we + * have no way to identify the accuracy + * available. Most notably ext4 on small disks (where + * 128 byte inodes are used) does not support better + * accuracy than 1s. */ + if (st.st_mtim.tv_sec > ts->tv_sec) + return 0; + + /* It is older? Then let's update it */ + if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { + + if (errno == EROFS) + return log_debug("Can't update timestamp file %s, file system is read-only.", path); + + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); + } + + } else if (errno == ENOENT) { + _cleanup_close_ int fd = -1; + int r; + + /* The timestamp file doesn't exist yet? Then let's create it. */ + + r = mac_selinux_create_file_prepare(path, S_IFREG); + if (r < 0) + return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); + + fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); + mac_selinux_create_file_clear(); + + if (fd < 0) { + if (errno == EROFS) + return log_debug("Can't create timestamp file %s, file system is read-only.", path); + + return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); + } + + (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); + + if (futimens(fd, twice) < 0) + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); + } else + log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); + + return 0; +} + +int main(int argc, char *argv[]) { + struct stat st; + int r, q = 0; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (stat("/usr", &st) < 0) { + log_error_errno(errno, "Failed to stat /usr: %m"); + return EXIT_FAILURE; + } + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "SELinux setup failed: %m"); + goto finish; + } + + r = apply_timestamp("/etc/.updated", &st.st_mtim); + q = apply_timestamp("/var/.updated", &st.st_mtim); + +finish: + return r < 0 || q < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-update-utmp/Makefile b/src/grp-initprogs/systemd-update-utmp/Makefile new file mode 100644 index 0000000000..506e4840a5 --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/Makefile @@ -0,0 +1,41 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_UTMP),) +rootlibexec_PROGRAMS += \ + systemd-update-utmp +endif # HAVE_UTMP + +systemd_update_utmp_SOURCES = \ + src/update-utmp/update-utmp.c + +systemd_update_utmp_CFLAGS = \ + $(AUDIT_CFLAGS) + +systemd_update_utmp_LDADD = \ + libsystemd-shared.la \ + $(AUDIT_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in new file mode 100644 index 0000000000..163eccd91f --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Update UTMP about System Boot/Shutdown +Documentation=man:systemd-update-utmp.service(8) man:utmp(5) +DefaultDependencies=no +RequiresMountsFor=/var/log/wtmp +Conflicts=shutdown.target +After=systemd-remount-fs.service systemd-tmpfiles-setup.service auditd.service +Before=sysinit.target shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-update-utmp reboot +ExecStop=@rootlibexecdir@/systemd-update-utmp shutdown diff --git a/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.xml b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.xml new file mode 100644 index 0000000000..c8a9cb7c90 --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/systemd-update-utmp.service.xml @@ -0,0 +1,76 @@ + + + + + + + + systemd-update-utmp.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-update-utmp.service + 8 + + + + systemd-update-utmp.service + systemd-update-utmp-runlevel.service + systemd-update-utmp + Write audit and utmp updates at bootup, runlevel + changes and shutdown + + + + systemd-update-utmp.service + systemd-update-utmp-runlevel.service + /usr/lib/systemd/systemd-update-utmp + + + + Description + + systemd-update-utmp-runlevel.service is + a service that writes SysV runlevel changes to utmp and wtmp, as + well as the audit logs, as they occur. + systemd-update-utmp.service does the same for + system reboots and shutdown requests. + + + + See Also + + systemd1, + utmp5, + auditd8 + + + + diff --git a/src/grp-initprogs/systemd-update-utmp/update-utmp.c b/src/grp-initprogs/systemd-update-utmp/update-utmp.c new file mode 100644 index 0000000000..cceb34f60d --- /dev/null +++ b/src/grp-initprogs/systemd-update-utmp/update-utmp.c @@ -0,0 +1,283 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_AUDIT +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/special.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/utmp-wtmp.h" + +typedef struct Context { + sd_bus *bus; +#ifdef HAVE_AUDIT + int audit_fd; +#endif +} Context; + +static usec_t get_startup_time(Context *c) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + usec_t t = 0; + int r; + + assert(c); + + r = sd_bus_get_property_trivial( + c->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UserspaceTimestamp", + &error, + 't', &t); + if (r < 0) { + log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r)); + return 0; + } + + return t; +} + +static int get_current_runlevel(Context *c) { + static const struct { + const int runlevel; + const char *special; + } table[] = { + /* The first target of this list that is active or has + * a job scheduled wins. We prefer runlevels 5 and 3 + * here over the others, since these are the main + * runlevels used on Fedora. It might make sense to + * change the order on some distributions. */ + { '5', SPECIAL_GRAPHICAL_TARGET }, + { '3', SPECIAL_MULTI_USER_TARGET }, + { '1', SPECIAL_RESCUE_TARGET }, + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + unsigned i; + + assert(c); + + for (i = 0; i < ELEMENTSOF(table); i++) { + _cleanup_free_ char *state = NULL, *path = NULL; + + path = unit_dbus_path_from_name(table[i].special); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( + c->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &state); + if (r < 0) + return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r)); + + if (streq(state, "active") || streq(state, "reloading")) + return table[i].runlevel; + } + + return 0; +} + +static int on_reboot(Context *c) { + int r = 0, q; + usec_t t; + + assert(c); + + /* We finished start-up, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && + errno != EPERM) { + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + /* If this call fails it will return 0, which + * utmp_put_reboot() will then fix to the current time */ + t = get_startup_time(c); + + q = utmp_put_reboot(t); + if (q < 0) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +static int on_shutdown(Context *c) { + int r = 0, q; + + assert(c); + + /* We started shut-down, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && + errno != EPERM) { + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + q = utmp_put_shutdown(); + if (q < 0) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +static int on_runlevel(Context *c) { + int r = 0, q, previous, runlevel; + + assert(c); + + /* We finished changing runlevel, so let's write the + * utmp record and send the audit msg */ + + /* First, get last runlevel */ + q = utmp_get_runlevel(&previous, NULL); + + if (q < 0) { + if (q != -ESRCH && q != -ENOENT) + return log_error_errno(q, "Failed to get current runlevel: %m"); + + previous = 0; + } + + /* Secondly, get new runlevel */ + runlevel = get_current_runlevel(c); + + if (runlevel < 0) + return runlevel; + + if (previous == runlevel) + return 0; + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) { + _cleanup_free_ char *s = NULL; + + if (asprintf(&s, "old-level=%c new-level=%c", + previous > 0 ? previous : 'N', + runlevel > 0 ? runlevel : 'N') < 0) + return log_oom(); + + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) + r = log_error_errno(errno, "Failed to send audit message: %m"); + } +#endif + + q = utmp_put_runlevel(runlevel, previous); + if (q < 0 && q != -ESRCH && q != -ENOENT) { + log_error_errno(q, "Failed to write utmp record: %m"); + r = q; + } + + return r; +} + +int main(int argc, char *argv[]) { + Context c = { +#ifdef HAVE_AUDIT + .audit_fd = -1 +#endif + }; + int r; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return EXIT_FAILURE; + } + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + +#ifdef HAVE_AUDIT + /* If the kernel lacks netlink or audit support, + * don't worry about it. */ + c.audit_fd = audit_open(); + if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) + log_error_errno(errno, "Failed to connect to audit log: %m"); +#endif + r = bus_connect_system_systemd(&c.bus); + if (r < 0) { + log_error_errno(r, "Failed to get D-Bus connection: %m"); + r = -EIO; + goto finish; + } + + log_debug("systemd-update-utmp running as pid "PID_FMT, getpid()); + + if (streq(argv[1], "reboot")) + r = on_reboot(&c); + else if (streq(argv[1], "shutdown")) + r = on_shutdown(&c); + else if (streq(argv[1], "runlevel")) + r = on_runlevel(&c); + else { + log_error("Unknown command %s", argv[1]); + r = -EINVAL; + } + + log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid()); + +finish: +#ifdef HAVE_AUDIT + if (c.audit_fd >= 0) + audit_close(c.audit_fd); +#endif + + sd_bus_flush_close_unref(c.bus); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-user-sessions/Makefile b/src/grp-initprogs/systemd-user-sessions/Makefile new file mode 100644 index 0000000000..91c84e46fe --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/Makefile @@ -0,0 +1,48 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_PAM),) + +systemd_user_sessions_SOURCES = \ + src/user-sessions/user-sessions.c + +systemd_user_sessions_LDADD = \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-user-sessions + +nodist_systemunit_DATA += \ + units/systemd-user-sessions.service + +MULTI_USER_TARGET_WANTS += \ + systemd-user-sessions.service + +endif # HAVE_PAM + +EXTRA_DIST += \ + units/systemd-user-sessions.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in new file mode 100644 index 0000000000..b4ea5a134b --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Permit User Sessions +Documentation=man:systemd-user-sessions.service(8) +After=remote-fs.target nss-user-lookup.target network.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-user-sessions start +ExecStop=@rootlibexecdir@/systemd-user-sessions stop diff --git a/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.xml b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.xml new file mode 100644 index 0000000000..67aba54119 --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/systemd-user-sessions.service.xml @@ -0,0 +1,75 @@ + + + + + + + + systemd-user-sessions.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-user-sessions.service + 8 + + + + systemd-user-sessions.service + systemd-user-sessions + Permit user logins after boot, prohibit user logins at shutdown + + + + systemd-user-sessions.service + /usr/lib/systemd/systemd-user-sessions + + + + Description + + systemd-user-sessions.service is a + service that controls user logins through + pam_nologin8. + After basic system initialization is complete, it removes + /run/nologin, thus permitting logins. Before + system shutdown, it creates /run/nologin, thus + prohibiting further logins. + + + + See Also + + systemd1, + systemd-logind.service8, + pam_nologin8 + + + + diff --git a/src/grp-initprogs/systemd-user-sessions/user-sessions.c b/src/grp-initprogs/systemd-user-sessions/user-sessions.c new file mode 100644 index 0000000000..639c0200e6 --- /dev/null +++ b/src/grp-initprogs/systemd-user-sessions/user-sessions.c @@ -0,0 +1,84 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fileio-label.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +int main(int argc, char*argv[]) { + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + mac_selinux_init(); + + if (streq(argv[1], "start")) { + int r = 0; + + if (unlink("/run/nologin") < 0 && errno != ENOENT) + r = log_error_errno(errno, + "Failed to remove /run/nologin file: %m"); + + if (unlink("/etc/nologin") < 0 && errno != ENOENT) { + /* If the file doesn't exist and /etc simply + * was read-only (in which case unlink() + * returns EROFS even if the file doesn't + * exist), don't complain */ + + if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) { + log_error_errno(errno, "Failed to remove /etc/nologin file: %m"); + return EXIT_FAILURE; + } + } + + if (r < 0) + return EXIT_FAILURE; + + } else if (streq(argv[1], "stop")) { + int r; + + r = write_string_file_atomic_label("/run/nologin", "System is going down."); + if (r < 0) { + log_error_errno(r, "Failed to create /run/nologin: %m"); + return EXIT_FAILURE; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + return EXIT_FAILURE; + } + + mac_selinux_finish(); + + return EXIT_SUCCESS; +} diff --git a/src/grp-initprogs/systemd-vconsole-setup/.gitignore b/src/grp-initprogs/systemd-vconsole-setup/.gitignore new file mode 100644 index 0000000000..82741b2fb3 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/.gitignore @@ -0,0 +1 @@ +/90-vconsole.rules diff --git a/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in b/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in new file mode 100644 index 0000000000..35b9ad5151 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/90-vconsole.rules.in @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# Each vtcon keeps its own state of fonts. +# +ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup" diff --git a/src/grp-initprogs/systemd-vconsole-setup/Makefile b/src/grp-initprogs/systemd-vconsole-setup/Makefile new file mode 100644 index 0000000000..5a93ae780b --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/Makefile @@ -0,0 +1,50 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_VCONSOLE),) +systemd_vconsole_setup_SOURCES = \ + src/vconsole/vconsole-setup.c + +systemd_vconsole_setup_LDADD = \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-vconsole-setup + +nodist_udevrules_DATA += \ + src/vconsole/90-vconsole.rules + +nodist_systemunit_DATA += \ + units/systemd-vconsole-setup.service + +SYSINIT_TARGET_WANTS += \ + systemd-vconsole-setup.service +endif # ENABLE_VCONSOLE + +EXTRA_DIST += \ + src/vconsole/90-vconsole.rules.in \ + units/systemd-vconsole-setup.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in new file mode 100644 index 0000000000..6160361871 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Setup Virtual Console +Documentation=man:systemd-vconsole-setup.service(8) man:vconsole.conf(5) +DefaultDependencies=no +Conflicts=shutdown.target +Before=sysinit.target shutdown.target +ConditionPathExists=/dev/tty0 + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-vconsole-setup diff --git a/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.xml b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.xml new file mode 100644 index 0000000000..ff079761c1 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/systemd-vconsole-setup.service.xml @@ -0,0 +1,114 @@ + + + + + + + + systemd-vconsole-setup.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-vconsole-setup.service + 8 + + + + systemd-vconsole-setup.service + systemd-vconsole-setup + Configure the virtual console at boot + + + + systemd-vconsole-setup.service + /usr/lib/systemd/systemd-vconsole-setup + + + + Description + + systemd-vconsole-setup.service is an + early boot service that configures the virtual console font and + console keymap. Internally it calls + loadkeys1 + and + setfont8. + + See + vconsole.conf5 + for information about the configuration files understood by this + service. + + + + + + Kernel Command Line + + A few configuration parameters from + vconsole.conf may be overridden on the kernel + command line: + + + + vconsole.keymap= + vconsole.keymap.toggle= + + Overrides the key mapping table for the + keyboard and the second toggle keymap. + + + + vconsole.font= + vconsole.font.map= + vconsole.font.unimap= + + Configures the console font, the console map, + and the unicode font map. + + + + See + vconsole.conf5 + for information about these settings. + + + + See Also + + systemd1, + vconsole.conf5, + loadkeys1, + setfont8, + systemd-localed.service8 + + + + diff --git a/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c b/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c new file mode 100644 index 0000000000..87c0c2f066 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/vconsole-setup.c @@ -0,0 +1,333 @@ +/*** + This file is part of systemd. + + Copyright 2010 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "basic/virt.h" + +static bool is_vconsole(int fd) { + unsigned char data[1]; + + data[0] = TIOCL_GETFGCONSOLE; + return ioctl(fd, TIOCLINUX, data) >= 0; +} + +static int disable_utf8(int fd) { + int r = 0, k; + + if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) + r = -errno; + + k = loop_write(fd, "\033%@", 3, false); + if (k < 0) + r = k; + + k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); + if (k < 0) + r = k; + + if (r < 0) + log_warning_errno(r, "Failed to disable UTF-8: %m"); + + return r; +} + +static int enable_utf8(int fd) { + int r = 0, k; + long current = 0; + + if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { + /* + * Change the current keyboard to unicode, unless it + * is currently in raw or off mode anyway. We + * shouldn't interfere with X11's processing of the + * key events. + * + * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html + * + */ + + if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) + r = -errno; + } + + k = loop_write(fd, "\033%G", 3, false); + if (k < 0) + r = k; + + k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); + if (k < 0) + r = k; + + if (r < 0) + log_warning_errno(r, "Failed to enable UTF-8: %m"); + + return r; +} + +static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { + const char *args[8]; + int i = 0, r; + pid_t pid; + + /* An empty map means kernel map */ + if (isempty(map)) + return 1; + + args[i++] = KBD_LOADKEYS; + args[i++] = "-q"; + args[i++] = "-C"; + args[i++] = vc; + if (utf8) + args[i++] = "-u"; + args[i++] = map; + if (map_toggle) + args[i++] = map_toggle; + args[i++] = NULL; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); + if (r < 0) + return r; + + return r == 0; +} + +static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { + const char *args[9]; + int i = 0, r; + pid_t pid; + + /* An empty font means kernel font */ + if (isempty(font)) + return 1; + + args[i++] = KBD_SETFONT; + args[i++] = "-C"; + args[i++] = vc; + args[i++] = font; + if (map) { + args[i++] = "-m"; + args[i++] = map; + } + if (unimap) { + args[i++] = "-u"; + args[i++] = unimap; + } + args[i++] = NULL; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); + if (r < 0) + return r; + + return r == 0; +} + +/* + * A newly allocated VT uses the font from the active VT. Here + * we update all possibly already allocated VTs with the configured + * font. It also allows to restart systemd-vconsole-setup.service, + * to apply a new font to all VTs. + */ +static void font_copy_to_all_vcs(int fd) { + struct vt_stat vcs = {}; + unsigned char map8[E_TABSZ]; + unsigned short map16[E_TABSZ]; + struct unimapdesc unimapd; + _cleanup_free_ struct unipair* unipairs = NULL; + int i, r; + + unipairs = new(struct unipair, USHRT_MAX); + if (!unipairs) { + log_oom(); + return; + } + + /* get active, and 16 bit mask of used VT numbers */ + r = ioctl(fd, VT_GETSTATE, &vcs); + if (r < 0) { + log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); + return; + } + + for (i = 1; i <= 15; i++) { + char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int vcfd = -1; + struct console_font_op cfo = {}; + + if (i == vcs.v_active) + continue; + + /* skip non-allocated ttys */ + xsprintf(vcname, "/dev/vcs%i", i); + if (access(vcname, F_OK) < 0) + continue; + + xsprintf(vcname, "/dev/tty%i", i); + vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); + if (vcfd < 0) + continue; + + /* copy font from active VT, where the font was uploaded to */ + cfo.op = KD_FONT_OP_COPY; + cfo.height = vcs.v_active-1; /* tty1 == index 0 */ + (void) ioctl(vcfd, KDFONTOP, &cfo); + + /* copy map of 8bit chars */ + if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) + (void) ioctl(vcfd, PIO_SCRNMAP, map8); + + /* copy map of 8bit chars -> 16bit Unicode values */ + if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) + (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); + + /* copy unicode translation table */ + /* unimapd is a ushort count and a pointer to an + array of struct unipair { ushort, ushort } */ + unimapd.entries = unipairs; + unimapd.entry_ct = USHRT_MAX; + if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { + struct unimapinit adv = { 0, 0, 0 }; + + (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); + (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); + } + } +} + +int main(int argc, char **argv) { + const char *vc; + _cleanup_free_ char + *vc_keymap = NULL, *vc_keymap_toggle = NULL, + *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; + _cleanup_close_ int fd = -1; + bool utf8, font_copy = false, font_ok, keyboard_ok; + int r = EXIT_FAILURE; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argv[1]) + vc = argv[1]; + else { + vc = "/dev/tty0"; + font_copy = true; + } + + fd = open_terminal(vc, O_RDWR|O_CLOEXEC); + if (fd < 0) { + log_error_errno(fd, "Failed to open %s: %m", vc); + return EXIT_FAILURE; + } + + if (!is_vconsole(fd)) { + log_error("Device %s is not a virtual console.", vc); + return EXIT_FAILURE; + } + + utf8 = is_locale_utf8(); + + r = parse_env_file("/etc/vconsole.conf", NEWLINE, + "KEYMAP", &vc_keymap, + "KEYMAP_TOGGLE", &vc_keymap_toggle, + "FONT", &vc_font, + "FONT_MAP", &vc_font_map, + "FONT_UNIMAP", &vc_font_unimap, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); + + /* Let the kernel command line override /etc/vconsole.conf */ + if (detect_container() <= 0) { + r = parse_env_file("/proc/cmdline", WHITESPACE, + "vconsole.keymap", &vc_keymap, + "vconsole.keymap.toggle", &vc_keymap_toggle, + "vconsole.font", &vc_font, + "vconsole.font.map", &vc_font_map, + "vconsole.font.unimap", &vc_font_unimap, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); + } + + if (utf8) + (void) enable_utf8(fd); + else + (void) disable_utf8(fd); + + font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; + keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; + + /* Only copy the font when we executed setfont successfully */ + if (font_copy && font_ok) + (void) font_copy_to_all_vcs(fd); + + return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-initprogs/systemd-vconsole-setup/vconsole.conf.xml b/src/grp-initprogs/systemd-vconsole-setup/vconsole.conf.xml new file mode 100644 index 0000000000..27196d44e9 --- /dev/null +++ b/src/grp-initprogs/systemd-vconsole-setup/vconsole.conf.xml @@ -0,0 +1,139 @@ + + + + + + + + vconsole.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + vconsole.conf + 5 + + + + vconsole.conf + Configuration file for the virtual console + + + + /etc/vconsole.conf + + + + Description + + The /etc/vconsole.conf file configures + the virtual console, i.e. keyboard mapping and console font. It is + applied at boot by + systemd-vconsole-setup.service8. + + The basic file format of the + vconsole.conf is a newline-separated list of + environment-like shell-compatible variable assignments. It is + possible to source the configuration from shell scripts, however, + beyond mere variable assignments no shell features are supported, + allowing applications to read the file without implementing a + shell compatible execution engine. + + Note that the kernel command line options + vconsole.keymap=, + vconsole.keymap.toggle=, + vconsole.font=, + vconsole.font.map=, + vconsole.font.unimap= may be used + to override the console settings at boot. + + Depending on the operating system other configuration files + might be checked for configuration of the virtual console as well, + however only as fallback. + + + + Options + + The following options are understood: + + + + + KEYMAP= + KEYMAP_TOGGLE= + + Configures the key mapping table for the + keyboard. KEYMAP= defaults to + us if not set. The + KEYMAP_TOGGLE= can be used to configure a + second toggle keymap and is by default + unset. + + + + FONT= + FONT_MAP= + FONT_UNIMAP= + + Configures the console font, the console map + and the unicode font map. + + + + + + + Example + + + German keyboard and console + + /etc/vconsole.conf: + + KEYMAP=de-latin1 +FONT=eurlatgr + + + + + + See Also + + systemd1, + systemd-vconsole-setup.service8, + loadkeys1, + setfont8, + locale.conf5, + systemd-localed.service8 + + + + diff --git a/src/grp-journal/.gitignore b/src/grp-journal/.gitignore new file mode 100644 index 0000000000..c3fea7424f --- /dev/null +++ b/src/grp-journal/.gitignore @@ -0,0 +1 @@ +/README diff --git a/src/grp-journal/90-journald.preset b/src/grp-journal/90-journald.preset new file mode 100644 index 0000000000..6a8c17b1fa --- /dev/null +++ b/src/grp-journal/90-journald.preset @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +disable syslog.socket + +disable systemd-journal-gatewayd.* diff --git a/src/grp-journal/Makefile b/src/grp-journal/Makefile new file mode 100644 index 0000000000..01802eff17 --- /dev/null +++ b/src/grp-journal/Makefile @@ -0,0 +1,197 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +test_journal_SOURCES = \ + src/journal/test-journal.c + +test_journal_LDADD = \ + libjournal-core.la + +test_journal_send_SOURCES = \ + src/journal/test-journal-send.c + +test_journal_send_LDADD = \ + libjournal-core.la + +test_journal_syslog_SOURCES = \ + src/journal/test-journal-syslog.c + +test_journal_syslog_LDADD = \ + libjournal-core.la + +test_journal_match_SOURCES = \ + src/journal/test-journal-match.c + +test_journal_match_LDADD = \ + libjournal-core.la + +test_journal_enum_SOURCES = \ + src/journal/test-journal-enum.c + +test_journal_enum_LDADD = \ + libjournal-core.la + +test_journal_stream_SOURCES = \ + src/journal/test-journal-stream.c + +test_journal_stream_LDADD = \ + libjournal-core.la + +test_journal_flush_SOURCES = \ + src/journal/test-journal-flush.c + +test_journal_flush_LDADD = \ + libjournal-core.la + +test_journal_init_SOURCES = \ + src/journal/test-journal-init.c + +test_journal_init_LDADD = \ + libjournal-core.la + +test_journal_verify_SOURCES = \ + src/journal/test-journal-verify.c + +test_journal_verify_LDADD = \ + libjournal-core.la + +test_journal_interleaving_SOURCES = \ + src/journal/test-journal-interleaving.c + +test_journal_interleaving_LDADD = \ + libjournal-core.la + +test_mmap_cache_SOURCES = \ + src/journal/test-mmap-cache.c + +test_mmap_cache_LDADD = \ + libjournal-core.la + +test_catalog_SOURCES = \ + src/journal/test-catalog.c + +test_catalog_CPPFLAGS = \ + -DCATALOG_DIR=\"$(abs_top_builddir)/catalog\" + +test_catalog_LDADD = \ + libjournal-core.la + +test_compress_SOURCES = \ + src/journal/test-compress.c + +test_compress_LDADD = \ + libsystemd-shared.la + +ifneq ($(HAVE_LZ4),) +test_compress_CFLAGS += \ + $(LZ4_CFLAGA) +test_compress_LDADD += \ + $(LZ4_LIBS) +endif + +test_compress_benchmark_SOURCES = \ + src/journal/test-compress-benchmark.c + +test_compress_benchmark_LDADD = \ + libsystemd-shared.la + +test_audit_type_SOURCES = \ + src/journal/test-audit-type.c + +test_audit_type_LDADD = \ + libjournal-core.la + +journal-install-hook: + -$(MKDIR_P) $(DESTDIR)/var/log/journal + -chown 0:0 $(DESTDIR)/var/log/journal + -chmod 755 $(DESTDIR)/var/log/journal + -setfacl -nm g:adm:rx,d:g:adm:rx $(DESTDIR)/var/log/journal/ + -setfacl -nm g:wheel:rx,d:g:wheel:rx $(DESTDIR)/var/log/journal/ + +journal-uninstall-hook: + -rmdir $(DESTDIR)/var/log/journal/remote + -rmdir $(DESTDIR)/var/log/journal/ + +INSTALL_EXEC_HOOKS += journal-install-hook +UNINSTALL_EXEC_HOOKS += journal-uninstall-hook + +# ------------------------------------------------------------------------------ +# Update catalog on installation. Do not bother if installing +# in DESTDIR, since this is likely for packaging purposes. +catalog-update-hook: + -test -n "$(DESTDIR)" || $(rootbindir)/journalctl --update-catalog + +INSTALL_DATA_HOOKS += \ + catalog-update-hook + +catalog-remove-hook: + -test -n "$(DESTDIR)" || rm -f $(catalogstatedir)/database + +UNINSTALL_DATA_HOOKS += \ + catalog-remove-hook + +tests += \ + test-journal \ + test-journal-enum \ + test-journal-send \ + test-journal-syslog \ + test-journal-match \ + test-journal-stream \ + test-journal-init \ + test-journal-verify \ + test-journal-interleaving \ + test-journal-flush \ + test-mmap-cache \ + test-catalog \ + test-audit-type + +ifneq ($(HAVE_COMPRESSION),) +tests += \ + test-compress \ + test-compress-benchmark +endif # HAVE_COMPRESSION + +ifneq ($(HAVE_SYSV_COMPAT),) + +varlog_DATA = \ + docs/var-log/README + +$(outdir)/README: docs/var-log/README.in + $(SED_PROCESS) + +CLEANFILES += \ + docs/var-log/README +endif # HAVE_SYSV_COMPAT + +EXTRA_DIST += \ + docs/var-log/README.in + +nested.subdirs += grp-remote +nested.subdirs += journalctl +nested.subdirs += libjournal-core +nested.subdirs += systemd-cat +nested.subdirs += systemd-journald + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/README.in b/src/grp-journal/README.in new file mode 100644 index 0000000000..2e64fb196a --- /dev/null +++ b/src/grp-journal/README.in @@ -0,0 +1,26 @@ +You are looking for the traditional text log files in @VARLOGDIR@, and +they are gone? + +Here's an explanation on what's going on: + +You are running a systemd-based OS where traditional syslog has been +replaced with the Journal. The journal stores the same (and more) +information as classic syslog. To make use of the journal and access +the collected log data simply invoke "journalctl", which will output +the logs in the identical text-based format the syslog files in +@VARLOGDIR@ used to be. For further details, please refer to +journalctl(1). + +Alternatively, consider installing one of the traditional syslog +implementations available for your distribution, which will generate +the classic log files for you. Syslog implementations such as +syslog-ng or rsyslog may be installed side-by-side with the journal +and will continue to function the way they always did. + +Thank you! + +Further reading: + man:journalctl(1) + man:systemd-journald.service(8) + man:journald.conf(5) + http://0pointer.de/blog/projects/the-journal.html diff --git a/src/grp-journal/catalog/.gitignore b/src/grp-journal/catalog/.gitignore new file mode 100644 index 0000000000..ff695342e3 --- /dev/null +++ b/src/grp-journal/catalog/.gitignore @@ -0,0 +1 @@ +*.catalog diff --git a/src/grp-journal/catalog/systemd.be.catalog.in b/src/grp-journal/catalog/systemd.be.catalog.in new file mode 100644 index 0000000000..5b237f0558 --- /dev/null +++ b/src/grp-journal/catalog/systemd.be.catalog.in @@ -0,0 +1,313 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2015, 2016 Viktar Vaŭčkievič +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Belarusian translation + +# Фармат каталога апісаны на старонцы +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Сэрвіс журналявання запусціўся +Defined-By: systemd +Support: %SUPPORT_URL% + +Працэс сістэмнага журналявання запусціўся, адкрыў файлы для +запісу і гатовы апрацоўваць запыты. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Сэрвіс журналявання спыніўся +Defined-By: systemd +Support: %SUPPORT_URL% + +Працэс сістэмнага журналявання спыніўся і закрыў усе файлы. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Дыскавае месца, занятае часопісам +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) цяпер займае @CURRENT_USE_PRETTY@. +Максімальна дазволены памер складае @MAX_USE_PRETTY@. +Пакідаем вольнымі не меньш за @DISK_KEEP_FREE_PRETTY@ (даступна на дыску +@DISK_AVAILABLE_PRETTY@). +Такім чынам, ліміт складае @LIMIT_PRETTY@, з якіх @AVAILABLE_PRETTY@ +даступна. + +Ліміты на памер наладжваецца з дапамогай SystemMaxUse=, SystemKeepFree=, +SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= у +файле /etc/systemd/journald.conf. Глядзіце journald.conf(5) для дэталей. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Паведамленні з сэрвісу адкінуты +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Сэрвіс адправіў занадта штат паведамленняў за кароткі прамежак часу. +Частка паведамленняў была адкінута. + +Майце на ўвазе, што былі адкінуты паведамлення толькі гэтага сэрвісу. +Паведамленні іншых сэрвісаў засталіся. + +Мяжа, пасля якой паведамленні будуць адкінуты, наладжваецца з дапамогай +RateLimitIntervalSec= і RateLimitBurst= у файле /etc/systemd/journald.conf. +Глядзіце journald.conf(5) для дэталей. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Паведамленні страчаны +Defined-By: systemd +Support: %SUPPORT_URL% + +Паведамленні ядра былі страчаны, так як сістэма журналявання не паспела +іх апрацаваць. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Працэс @COREDUMP_PID@ (@COREDUMP_COMM@) скінуў дамп памяці +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Працэс @COREDUMP_PID@ (@COREDUMP_COMM@) разбіўся і скінуў дамп памяці. + +Звычайна гэта сведчыць аб памылцы ў праграмным кодзе. +Рэкамендуецца паведаміць аб гэтым распрацоўнікам. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Новая сесія № @SESSION_ID@ створана для карыстальніка @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Новая сесія з № @SESSION_ID@ створана для карыстальніка @USER_ID@. + +Лідар гэтай сесіі пад № @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Сесія № @SESSION_ID@ спынена +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Сесія № @SESSION_ID@ спынена. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Даступна новае працоўнае месца № @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Новае працоўнае месца № @SEAT_ID@ наладжана і даступна для выкарыстання. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Працоўнае месца № @SEAT_ID@ выдалена +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Працоўнае месца № @SEAT_ID@ выдалена і больш не даступна. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Час зменены +Defined-By: systemd +Support: %SUPPORT_URL% + +Сістэмны гадзіннік зменены на @REALTIME@ мікрасекунд ад 1 студзеня 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Часавы пояс зменены на @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Сістэмны часавы пояс зменены на @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Запуск сістэмы завяршыўся +Defined-By: systemd +Support: %SUPPORT_URL% + +Усе сістэмныя сэрвісы, неабходныя для загрузкі сістэмы, паспяхова +запусціліся. Майце на ўвазе, што гэта не значыць, што машына нічога не +робіць. Магчыма, некаторыя сэрвісы яшчэ ініцыялізіруюцца. + +На запуск ядра спатрэбілася @KERNEL_USEC@ мікрасекунд. + +На запуск пачатковага RAM-дыска спатрэбілася @INITRD_USEC@ мікрасекунд. + +На запуск сістэмных сэрвісаў спатрэбілася @USERSPACE_USEC@ мікрасекунд. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Сістэма перайшла ў стан сну @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Цяпер сістэма перайшла у стан сну @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Сістэма выйшла са стана сну @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Цяпер сістэма выйшла са стана сну @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Сістэма завяршае работу +Defined-By: systemd +Support: %SUPPORT_URL% + +Пачаўся працэс выключэння сістэмы. +Спыняюцца ўсе сістэмныя сэрвісы і дэмантуюцца файлавыя сістэмы. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Юніт @UNIT@ запускаецца +Defined-By: systemd +Support: %SUPPORT_URL% + +Пачаўся працэс запуску юніта @UNIT@. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Юніт @UNIT@ запусціўся +Defined-By: systemd +Support: %SUPPORT_URL% + +Працэс запуску юніта @UNIT@ завершаны. + +Вынік: @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Юніт @UNIT@ спыняецца +Defined-By: systemd +Support: %SUPPORT_URL% + +Пачаўся працэс спынення юніта @UNIT@. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Юніт @UNIT@ спынены +Defined-By: systemd +Support: %SUPPORT_URL% + +Працэс спынення юніта @UNIT@ завершаны. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Збой юніта @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Збой юніта @UNIT@. + +Вынік: @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Юніт @UNIT@ перачытвае сваю канфігурацыю +Defined-By: systemd +Support: %SUPPORT_URL% + +Юніт @UNIT@ пачаў перачытваць сваю канфігурацыю. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Юніт @UNIT@ перачытаў сваю канфігурацыю +Defined-By: systemd +Support: %SUPPORT_URL% + +Юніт @UNIT@ перачытаў сваю канфігурацыю. + +Вынік: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Працэс @EXECUTABLE@ не можа быць выкананы +Defined-By: systemd +Support: %SUPPORT_URL% + +Працэс @EXECUTABLE@ не можа быць выкананы ў выніку збою. + +Ён вярнуў памылку нумар @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Sibject: Адно ці больш паведамленняў не былі накіраваны ў syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Адно ці больш паведамленняў не былі накіраваны ў syslog сэрвіс, які +выконваецца паралельна з journald. Звычайна гэта значыць, што +рэалізацыя syslog не паспявае апрацаваць паведамленні з неабходнай +хуткасцю. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Кропка мантавання не пустая +Defined-By: systemd +Support: %SUPPORT_URL% + +Каталог @WHERE@ указаны як кропка мантавання (другое поле ў /etc/fstab ці +Where= поле ў файле юніта systemd) і не пусты. Гэта не перашкаджае +мантаванню, але існуючыя ў ім файлы будуць недаступны. Для доступу да іх, +калі ласка, змантуйце гэтую файлавую сістэму ў іншае месца. + +-- 24d8d4452573402496068381a6312df2 +Subject: Віртуальная машына або кантэйнер запусціўся +Defined-By: systemd +Support: %SUPPORT_URL% + +Віртуальная машына @NAME@ з лідарам № @LEADER@ запусцілася і +гатова для выкарыстання. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Віртуальная машына або кантэйнер спынены +Defined-By: systemd +Support: %SUPPORT_URL% + +Віртуальная машына @NAME@ з лідарам № @LEADER@ спынена. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Механізм DNSSEC адключаны, бо сервер не падтымлівае яго +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Сэрвіс вызначэння імён (systemd-resolved.service) вызначыў, што DNS-сервер +не падтрымлівае механізм DNSSEC. У выніку праверка DNSSEC была адключана. + +Гэтая падзея ўзнікае калі наладжаны DNSSEC=allow-downgrade +у файле resolved.conf і DNS-сервер не падтрымлівае механізм DNSSEC. +Звярніце ўвагу, што рэжым allow-downgrade дазваляе правесці атаку +«DNSSEC downgrade», у ходзе якой зламыснік можа адключыць праверку DNSSEC +шляхам падстаноўкі падробленых DNSSEC-адказаў у камунікацыйны канал. + +Гэта падзея можа быць прыкметай таго, што DNS-сервер сапраўды несумяшчальны +з DNSSEC або што зламысніку паспяхова атрымалася правесці атаку па +адключэнню DNSSEC. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: Збой пры праверцы DNSSEC +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +DNS-запыт або рэсурсны запіс не прайшоў праверку DNSSEC. +Як правіла, гэта паказвае на знешняе ўздзеянне на канал сувязі. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Давераны ключ DNSSEC быў ануляваны +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Давераны ключ DNSSEC быў ануляваны. Неабходна наладзіць новы давераны ключ +або абнавіць аперацыйную сістэму, каб атрымаць абноўлены давераны ключ +DNSSEC. diff --git a/src/grp-journal/catalog/systemd.be@latin.catalog.in b/src/grp-journal/catalog/systemd.be@latin.catalog.in new file mode 100644 index 0000000000..fc9f7cad16 --- /dev/null +++ b/src/grp-journal/catalog/systemd.be@latin.catalog.in @@ -0,0 +1,318 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2015, 2016 Viktar Vaŭčkievič +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Belarusian Latin translation + +# Farmat kataloha apisany na staroncy +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Servis žurnaliavannia zapusciŭsia +Defined-By: systemd +Support: %SUPPORT_URL% + +Praces sistemnaha žurnaliavannia zapusciŭsia, adkryŭ fajly dlia +zapisu i hatovy apracoŭvać zapyty. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Servis žurnaliavannia spyniŭsia +Defined-By: systemd +Support: %SUPPORT_URL% + +Praces sistemnaha žurnaliavannia spyniŭsia i zakryŭ usie fajly. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: dyskavaje miesca, zaniataje časopisam +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) ciapier zajmaje @CURRENT_USE_PRETTY@. +Maksimaĺna dazvolieny pamier skladaje @MAX_USE_PRETTY@. +Pakidajem voĺnymi nie mieńš za @DISK_KEEP_FREE_PRETTY@ (dastupna na dysku +@DISK_AVAILABLE_PRETTY@). +Takim čynam, limit skladaje @LIMIT_PRETTY@, z jakich @AVAILABLE_PRETTY@ +dastupna. + +Limity na pamier naladžvaiecca z dapamohaj SystemMaxUse=, SystemKeepFree=, +SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= u +fajlie /etc/systemd/journald.conf. Hliadzicie journald.conf(5) dlia +detaliej. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Paviedamlienni z servisu adkinuty +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Servis adpraviŭ zanadta štat paviedamlienniaŭ za karotki pramiežak času. +Častka paviedamlienniaŭ byla adkinuta. + +Majcie na ŭvazie, što byli adkinuty paviedamliennia toĺki hetaha servisu. +Paviedamlienni inšych servisaŭ zastalisia. + +Miaža, paslia jakoj paviedamlienni buduć adkinuty, naladžvajecca z dapamohaj +RateLimitIntervalSec= i RateLimitBurst= u fajlie /etc/systemd/journald.conf. +Hliadzicie journald.conf(5) dlia detaliej. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Paviedamlienni stračany +Defined-By: systemd +Support: %SUPPORT_URL% + +Paviedamlienni jadra byli stračany, tak jak sistema žurnaliavannia nie +paspiela ich apracavać. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Praces @COREDUMP_PID@ (@COREDUMP_COMM@) skinuŭ damp pamiaci +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Praces @COREDUMP_PID@ (@COREDUMP_COMM@) razbiŭsia i skinuŭ damp pamiaci. + +Zvyčajna heta sviedčyć ab pamylcy ŭ prahramnym kodzie. +Rekamiendujecca paviedamić ab hetym raspracoŭnikam. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Novaja siesija № @SESSION_ID@ stvorana dlia karystaĺnika @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Novaja siesija z № @SESSION_ID@ stvorana dlia karystaĺnika @USER_ID@. + +Lidar hetaj siesii pad № @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Siesija № @SESSION_ID@ spyniena +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Siesija № @SESSION_ID@ spyniena. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Dastupna novaje pracoŭnaje miesca № @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Novaje pracoŭnaje miesca № @SEAT_ID@ naladžana i dastupna dlia +vykarystannia. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Pracoŭnaje miesca № @SEAT_ID@ vydaliena +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Pracoŭnaje miesca № @SEAT_ID@ vydaliena i boĺš nie dastupna. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Čas zmienieny +Defined-By: systemd +Support: %SUPPORT_URL% + +Sistemny hadzinnik zmienieny na @REALTIME@ mikrasiekund ad 1 studzienia +1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Časavy pojas zmienieny na @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Sistemny časavy pojas zmienieny na @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Zapusk sistemy zaviaršyŭsia +Defined-By: systemd +Support: %SUPPORT_URL% + +Usie sistemnyja servisy, nieabchodnyja dlia zahruzki sistemy, paspiachova +zapuscilisia. Majcie na ŭvazie, što heta nie značyć, što mašyna ničoha nie +robić. Mahčyma, niekatoryja servisy jašče inicyjalizirujucca. + +Na zapusk jadra spatrebilasia @KERNEL_USEC@ mikrasiekund. + +Na zapusk pačatkovaha RAM-dyska spatrebilasia @INITRD_USEC@ mikrasiekund. + +Na zapusk sistemnych servisaŭ spatrebilasia @USERSPACE_USEC@ mikrasiekund. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Sistema pierajšla ŭ stan snu @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Ciapier sistema pierajšla u stan snu @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Sistema vyjšla sa stana snu @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Ciapier sistema vyjšla sa stana snu @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Sistema zaviaršaje rabotu +Defined-By: systemd +Support: %SUPPORT_URL% + +Pačaŭsia praces vykliučennia sistemy. +Spyniajucca ŭsie sistemnyja servisy i demantujucca fajlavyja sistemy. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Junit @UNIT@ zapuskajecca +Defined-By: systemd +Support: %SUPPORT_URL% + +Pačaŭsia praces zapusku junita @UNIT@. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Junit @UNIT@ zapusciŭsia +Defined-By: systemd +Support: %SUPPORT_URL% + +Praces zapusku junita @UNIT@ zavieršany. + +Vynik: @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Junit @UNIT@ spyniajecca +Defined-By: systemd +Support: %SUPPORT_URL% + +Pačaŭsia praces spyniennia junita @UNIT@. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Junit @UNIT@ spynieny +Defined-By: systemd +Support: %SUPPORT_URL% + +Praces spyniennia junita @UNIT@ zavieršany. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Zboj junita @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Zboj junita @UNIT@. + +Vynik: @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Junit @UNIT@ pieračytvaje svaju kanfihuracyju +Defined-By: systemd +Support: %SUPPORT_URL% + +Junit @UNIT@ pačaŭ pieračytvać svaju kanfihuracyju. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Junit @UNIT@ pieračytaŭ svaju kanfihuracyju +Defined-By: systemd +Support: %SUPPORT_URL% + +Junit @UNIT@ pieračytaŭ svaju kanfihuracyju. + +Vynik: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Praces @EXECUTABLE@ nie moža być vykanany +Defined-By: systemd +Support: %SUPPORT_URL% + +Praces @EXECUTABLE@ nie moža być vykanany ŭ vyniku zboju. + +Jon viarnuŭ pamylku numar @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Sibject: Adno ci boĺš paviedamlienniaŭ nie byli nakiravany ŭ syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Adno ci boĺš paviedamlienniaŭ nie byli nakiravany ŭ syslog servis, jaki +vykonvajecca paralieĺna z journald. Zvyčajna heta značyć, što +realizacyja syslog nie paspiavaje apracavać paviedamlienni z nieabchodnaj +chutkasciu. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Kropka mantavannia nie pustaja +Defined-By: systemd +Support: %SUPPORT_URL% + +Kataloh @WHERE@ ukazany jak kropka mantavannia (druhoje polie ŭ /etc/fstab +ci Where= polie ŭ fajlie junita systemd) i nie pusty. Heta nie pieraškadžaje +mantavanniu, alie isnujučyja ŭ im fajly buduć niedastupny. Dlia dostupu da +ich, kali laska, zmantujcie hetuju fajlavuju sistemu ŭ inšaje miesca. + +-- 24d8d4452573402496068381a6312df2 +Subject: Virtuaĺnaja mašyna abo kantejnier zapusciŭsia +Defined-By: systemd +Support: %SUPPORT_URL% + +Virtuaĺnaja mašyna @NAME@ z lidaram № @LEADER@ zapuscilasia i +hatova dlia vykarystannia. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Virtuaĺnaja mašyna abo kantejnier spynieny +Defined-By: systemd +Support: %SUPPORT_URL% + +Virtuaĺnaja mašyna @NAME@ z lidaram № @LEADER@ spyniena. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Miechanizm DNSSEC adkliučany, bo siervier nie padtrymlivaje jaho +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Servis vyznačennia imion (systemd-resolved.service) vyznačyŭ, što +DNS-siervier nie padtrymlivaje miechanizm DNSSEC. U vyniku pravierka DNSSEC +byla adkliučana. + +Hetaja padzieja ŭznikaje kali naladžany DNSSEC=allow-downgrade +u fajlie resolved.conf i DNS-siervier nie padtrymlivaje miechanizm DNSSEC. +Zviarnicie ŭvahu, što režym allow-downgrade dazvaliaje praviesci ataku +«DNSSEC downgrade», u chodzie jakoj zlamysnik moža adkliučyć pravierku +DNSSEC šliacham padstanoŭki padroblienych DNSSEC-adkazaŭ u kamunikacyjny +kanal. + +Heta padzieja moža być prykmietaj taho, što DNS-siervier sapraŭdy +niesumiaščaĺny z DNSSEC abo što zlamysniku paspiachova atrymalasia praviesci +ataku pa adkliučenniu DNSSEC. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: Zboj pry praviercy DNSSEC +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +DNS-zapyt abo resursny zapis nie prajšoŭ pravierku DNSSEC. +Jak pravila, heta pakazvaje na zniešniaje ŭzdziejannie na kanal suviazi. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Davierany kliuč DNSSEC byŭ anuliavany +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Davierany kliuč DNSSEC byŭ anuliavany. Nieabchodna naladzić novy davierany +kliuč abo abnavić apieracyjnuju sistemu, kab atrymać abnoŭlieny davierany +kliuč DNSSEC. diff --git a/src/grp-journal/catalog/systemd.bg.catalog.in b/src/grp-journal/catalog/systemd.bg.catalog.in new file mode 100644 index 0000000000..76b0ce8f17 --- /dev/null +++ b/src/grp-journal/catalog/systemd.bg.catalog.in @@ -0,0 +1,324 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2016 Alexander Shopov +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Журналният процес е пуснат +Defined-By: systemd +Support: %SUPPORT_URL% + +Журналният процес на системата е стартирал, отворил е журналните файлове +за запис и може да приема заявки. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Журналният процес е спрян +Defined-By: systemd +Support: %SUPPORT_URL% + +Журналният процес на системата е спрян, затворени са всички отворени +журнални файлове. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Пространството върху диска заето от журналните файлове +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) в момента заема @CURRENT_USE_PRETTY@. +Максималният зададен размер е @MAX_USE_PRETTY@. +Свободни се оставят поне @DISK_KEEP_FREE_PRETTY@ (от текущо наличните @DISK_AVAILABLE_PRETTY@). +Максималният наложен размер е @LIMIT_PRETTY@, от който @AVAILABLE_PRETTY@ са свободни. + +Настройките за максималния размер на журнала върху диска се +управляват чрез директивите „SystemMaxUse=“, „SystemKeepFree=“, +„SystemMaxFileSize=“, „RuntimeMaxUse=“, „RuntimeKeepFree=“ и +„RuntimeMaxFileSize=“ във файла „/etc/systemd/journald.conf“. +За повече информация прегледайте „journald.conf(5)“ от ръководството. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Съобщенията от някоя услуга не са допуснати +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Някоя услуга генерира прекалено много съобщения за кратък период. +Част само от нейните съобщения са отхвърляни. + +Съобщенията от другите услуги не са засегнати. + +Настройките за максималния брой съобщения, които ще се обработят, се +управляват чрез директивите „RateLimitInterval=“ и „RateLimitBurst=“ във +файла „/etc/systemd/journald.conf“. За повече информация прегледайте +„journald.conf(5)“ от ръководството. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Пропуснати журнални съобщения +Defined-By: systemd +Support: %SUPPORT_URL% + +Някои от съобщенията на ядрото може и да са пропуснати, защото системата не +смогваше да ги обработи достатъчно бързо. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Процес № @COREDUMP_PID@ (@COREDUMP_COMM@) запази освободената памет +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Процес № @COREDUMP_PID@ (@COREDUMP_COMM@) заби, представянето му в паметта +бе запазено. + +Най-често това се дължи на грешка в забилата програма и следва да я +докладвате на създателите на програмата. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Създадена е нова сесия № @SESSION_ID@ за потребителя „@USER_ID@“ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +За потребителя „@USER_ID@“ е създадена нова сесия № @SESSION_ID@. + +Водещият процес на сесията е: @LEADER@ + +-- 3354939424b4456d9802ca8333ed424a +Subject: Сесия № @SESSION_ID@ приключи +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Сесия № @SESSION_ID@ приключи работа. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Налично е ново работно място № @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Новото работно място № @SEAT_ID@ е настроено и готово за работа. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Работното място № @SEAT_ID@ е премахнато +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Работното място № @SEAT_ID@ вече не е налично. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Смяна на системното време +Defined-By: systemd +Support: %SUPPORT_URL% + +Часовникът на системата е сверен да сочи @REALTIME@ микросекунди след +1 януари 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Смяна на часовия пояс да е „@TIMEZONE@“ +Defined-By: systemd +Support: %SUPPORT_URL% + +Часовият пояс на системата е сменен на „@TIMEZONE@“. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Стартирането на системата завърши +Defined-By: systemd +Support: %SUPPORT_URL% + +Успешно са стартирали всички услуги, които са посочени за задействане при +стартиране на системата. Това не означава, че системата бездейства, защото +някои от услугите може да извършват специфични действия при стартиране. + +Стартирането на ядрото отне @KERNEL_USEC@ микросекунди. + +Стартирането на RAM диска за първоначално зареждане отне @INITRD_USEC@ +микросекунди. + +Стартирането на потребителските програми отне @USERSPACE_USEC@ микросекунди. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Системата е приспана на ниво „@SLEEP@“ +Defined-By: systemd +Support: %SUPPORT_URL% + +Системата премина в състояние на приспиване „@SLEEP@“. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Системата се събуди след приспиване на ниво„@SLEEP@“ +Defined-By: systemd +Support: %SUPPORT_URL% + +Системата се събуди от състояние на приспиване „@SLEEP@“. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Започна процедура на спиране на системата +Defined-By: systemd +Support: %SUPPORT_URL% + +Започна процедурата на Systemd за спиране на системата. Всички процеси и +услуги се спират, всички файлови системи се демонтират. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Модул „@UNIT@“ се стартира +Defined-By: systemd +Support: %SUPPORT_URL% + +Модулът „@UNIT@“ се стартира в момента + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Модул „@UNIT@“ вече е стартиран +Defined-By: systemd +Support: %SUPPORT_URL% + +Стартирането на модул „@UNIT@“ завърши. + +Резултатът е: @RESULT@ + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Модул „@UNIT@“ се спира +Defined-By: systemd +Support: %SUPPORT_URL% + +Модулът „@UNIT@“ се спира в момента. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Модул „@UNIT@“ вече е спрян +Defined-By: systemd +Support: %SUPPORT_URL% + +Спирането на модул „@UNIT@“ завърши. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Модулът „@UNIT@“ не успя да стартира +Defined-By: systemd +Support: %SUPPORT_URL% + +Модулът „@UNIT@“ не успя да стартира. + +Резултатът е: @RESULT@ + +-- d34d037fff1847e6ae669a370e694725 +Subject: Модулът „@UNIT@“ започна презареждане на настройките си +Defined-By: systemd +Support: %SUPPORT_URL% + +Модулът „@UNIT@“ започна презареждане на настройките си. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Модулът „@UNIT@“ завърши презареждането на настройките си +Defined-By: systemd +Support: %SUPPORT_URL% + +Модулът „@UNIT@“ завърши презареждането на настройките си. + +Резултатът e: @RESULT@ + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Програмата „@EXECUTABLE@“ не успя да се стартира +Defined-By: systemd +Support: %SUPPORT_URL% + +Програмата „@EXECUTABLE@“ не успя да се стартира. + +Върнатият номер на грешка е: @ERRNO@ + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Поне едно съобщение не бе препратено към syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Поне едно съобщение не бе препратено към журналната услуга syslog, която +работи успоредно с journald. + +Най-често това указва, че тази реализация на syslog не може да поеме текущия +обем съобщения. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Точката за монтиране не е празна +Defined-By: systemd +Support: %SUPPORT_URL% + +Директорията „@WHERE@“ не е празна. + +Тя е указана като точка за монтиране — или като второ поле във файла +„/etc/fstab“, или чрез директивата „Where=“ в някой от файловете за +модул на Systemd. + +Това не пречи на самото монтиране, но вече съществуващите там файлове и +директории няма да се виждат повече, освен ако ръчно не монтирате тази +непразна директория някъде другаде. + +-- 24d8d4452573402496068381a6312df2 +Subject: Стартирана е виртуална машина или контейнер +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуалната машина „@NAME@“ с идентификатор на водещия процес @LEADER@ +е стартирана и готова за работа. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Спряна е виртуална машина или контейнер +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуалната машина „@NAME@“ с идентификатор на водещия процес @LEADER@ +е спряна. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Режимът DNSSEC е изключен, защото сървърът не го поддържа +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Локалната услуга за имена (systemd-resolved.service) установи, че +настроения сървър за DNS не поддържа DNSSEC, затова този режим е изключен. + +Това се случва, когато директивата „DNSSEC=allow-downgrade“ е включена във +файла „resolved.conf“ и зададеният сървър за DNS не е съвместим с DNSSEC. + +Внимавайте, защото това може да позволи атака, при която трета страна ви +връща отговори, които да предизвикат понижаването на сигурността от DNSSEC +до DNS. + +Такова събитие означава, че или сървърът за DNS не е съвместим с DNSSEC, +или някой успешно ви е атакувал за понижаване на сигурността на имената. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: Неуспешна проверка на DNSSEC +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Заявка или запис в DNS не издържа проверка с DNSSEC. + +Това обикновено показва вмешателство на трета страна в канала ви за връзка. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Анулирана доверена котва в DNSSEC +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Анулирана е доверена котва за DNSSEC и трябва да настроите нова. + +Понякога новата идва с обновяване на системата. diff --git a/src/grp-journal/catalog/systemd.catalog.in b/src/grp-journal/catalog/systemd.catalog.in new file mode 100644 index 0000000000..8de8597fe9 --- /dev/null +++ b/src/grp-journal/catalog/systemd.catalog.in @@ -0,0 +1,334 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: The journal has been started +Defined-By: systemd +Support: %SUPPORT_URL% + +The system journal process has started up, opened the journal +files for writing and is now ready to process requests. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: The journal has been stopped +Defined-By: systemd +Support: %SUPPORT_URL% + +The system journal process has shut down and closed all currently +active journal files. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Disk space used by the journal +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) is currently using @CURRENT_USE_PRETTY@. +Maximum allowed usage is set to @MAX_USE_PRETTY@. +Leaving at least @DISK_KEEP_FREE_PRETTY@ free (of currently available @DISK_AVAILABLE_PRETTY@ of disk space). +Enforced usage limit is thus @LIMIT_PRETTY@, of which @AVAILABLE_PRETTY@ are still available. + +The limits controlling how much disk space is used by the journal may +be configured with SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, +RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= settings in +/etc/systemd/journald.conf. See journald.conf(5) for details. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Messages from a service have been suppressed +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +A service has logged too many messages within a time period. Messages +from the service have been dropped. + +Note that only messages from the service in question have been +dropped, other services' messages are unaffected. + +The limits controlling when messages are dropped may be configured +with RateLimitIntervalSec= and RateLimitBurst= in +/etc/systemd/journald.conf. See journald.conf(5) for details. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Journal messages have been missed +Defined-By: systemd +Support: %SUPPORT_URL% + +Kernel messages have been lost as the journal system has been unable +to process them quickly enough. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Process @COREDUMP_PID@ (@COREDUMP_COMM@) dumped core +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Process @COREDUMP_PID@ (@COREDUMP_COMM@) crashed and dumped core. + +This usually indicates a programming error in the crashing program and +should be reported to its vendor as a bug. + +-- fc2e22bc6ee647b6b90729ab34a250b1 de +Subject: Speicherabbild für Prozess @COREDUMP_PID@ (@COREDUMP_COMM) generiert +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Prozess @COREDUMP_PID@ (@COREDUMP_COMM@) ist abgebrochen worden und +ein Speicherabbild wurde generiert. + +Üblicherweise ist dies ein Hinweis auf einen Programmfehler und sollte +als Fehler dem jeweiligen Hersteller gemeldet werden. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: A new session @SESSION_ID@ has been created for user @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A new session with the ID @SESSION_ID@ has been created for the user @USER_ID@. + +The leading process of the session is @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Session @SESSION_ID@ has been terminated +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A session with the ID @SESSION_ID@ has been terminated. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: A new seat @SEAT_ID@ is now available +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A new seat @SEAT_ID@ has been configured and is now available. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Seat @SEAT_ID@ has now been removed +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A seat @SEAT_ID@ has been removed and is no longer available. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Time change +Defined-By: systemd +Support: %SUPPORT_URL% + +The system clock has been changed to @REALTIME@ microseconds after January 1st, 1970. + +-- c7a787079b354eaaa9e77b371893cd27 de +Subject: Zeitänderung +Defined-By: systemd +Support: %SUPPORT_URL% + +Die System-Zeit wurde geändert auf @REALTIME@ Mikrosekunden nach dem 1. Januar 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Time zone change to @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +The system timezone has been changed to @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: System start-up is now complete +Defined-By: systemd +Support: %SUPPORT_URL% + +All system services necessary queued for starting at boot have been +successfully started. Note that this does not mean that the machine is +now idle as services might still be busy with completing start-up. + +Kernel start-up required @KERNEL_USEC@ microseconds. + +Initial RAM disk start-up required @INITRD_USEC@ microseconds. + +Userspace start-up required @USERSPACE_USEC@ microseconds. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: System sleep state @SLEEP@ entered +Defined-By: systemd +Support: %SUPPORT_URL% + +The system has now entered the @SLEEP@ sleep state. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: System sleep state @SLEEP@ left +Defined-By: systemd +Support: %SUPPORT_URL% + +The system has now left the @SLEEP@ sleep state. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: System shutdown initiated +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemd shutdown has been initiated. The shutdown has now begun and +all system services are terminated and all file systems unmounted. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Unit @UNIT@ has begun start-up +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has begun starting up. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Unit @UNIT@ has finished start-up +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has finished starting up. + +The start-up result is @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Unit @UNIT@ has begun shutting down +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has begun shutting down. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Unit @UNIT@ has finished shutting down +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has finished shutting down. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Unit @UNIT@ has failed +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has failed. + +The result is @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Unit @UNIT@ has begun reloading its configuration +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has begun reloading its configuration + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Unit @UNIT@ has finished reloading its configuration +Defined-By: systemd +Support: %SUPPORT_URL% + +Unit @UNIT@ has finished reloading its configuration + +The result is @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Process @EXECUTABLE@ could not be executed +Defined-By: systemd +Support: %SUPPORT_URL% + +The process @EXECUTABLE@ could not be executed and failed. + +The error number returned by this process is @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: One or more messages could not be forwarded to syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +One or more messages could not be forwarded to the syslog service +running side-by-side with journald. This usually indicates that the +syslog implementation has not been able to keep up with the speed of +messages queued. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Mount point is not empty +Defined-By: systemd +Support: %SUPPORT_URL% + +The directory @WHERE@ is specified as the mount point (second field in +/etc/fstab or Where= field in systemd unit file) and is not empty. +This does not interfere with mounting, but the pre-exisiting files in +this directory become inaccessible. To see those over-mounted files, +please manually mount the underlying file system to a secondary +location. + +-- 24d8d4452573402496068381a6312df2 +Subject: A virtual machine or container has been started +Defined-By: systemd +Support: %SUPPORT_URL% + +The virtual machine @NAME@ with its leader PID @LEADER@ has been +started is now ready to use. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: A virtual machine or container has been terminated +Defined-By: systemd +Support: %SUPPORT_URL% + +The virtual machine @NAME@ with its leader PID @LEADER@ has been +shut down. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: DNSSEC mode has been turned off, as server doesn't support it +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +The resolver service (systemd-resolved.service) has detected that the +configured DNS server does not support DNSSEC, and DNSSEC validation has been +turned off as result. + +This event will take place if DNSSEC=allow-downgrade is configured in +resolved.conf and the configured DNS server is incompatible with DNSSEC. Note +that using this mode permits DNSSEC downgrade attacks, as an attacker might be +able turn off DNSSEC validation on the system by inserting DNS replies in the +communication channel that result in a downgrade like this. + +This event might be indication that the DNS server is indeed incompatible with +DNSSEC or that an attacker has successfully managed to stage such a downgrade +attack. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: DNSSEC validation failed +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +A DNS query or resource record set failed DNSSEC validation. This is usually +indication that the communication channel used was tampered with. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: A DNSSEC trust anchor has been revoked +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +A DNSSEC trust anchor has been revoked. A new trust anchor has to be +configured, or the operating system needs to be updated, to provide an updated +DNSSEC trust anchor. diff --git a/src/grp-journal/catalog/systemd.da.catalog.in b/src/grp-journal/catalog/systemd.da.catalog.in new file mode 100644 index 0000000000..bc7d94476f --- /dev/null +++ b/src/grp-journal/catalog/systemd.da.catalog.in @@ -0,0 +1,261 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Danish translation + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Journalen er blevet startet +Defined-By: systemd +Support: %SUPPORT_URL% + +System-journal processen har startet op, åbnet journal filerne for +tilskrivning og er nu klar til at modtage anmodninger. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Journalen er blevet stoppet +Defined-By: systemd +Support: %SUPPORT_URL% + +System-journal processen er stoppet og har lukket alle aktive journal +filer. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Beskeder fra en service er blevet undertrykt +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +En service har logget for mange beskeder inden for en given tidsperiode. +Beskeder fra omtalte service er blevet smidt væk. + +Kun beskeder fra omtalte service er smidt væk. Beskeder fra andre +services er ikke påvirket. + +Grænsen for hvornår beskeder bliver smidt væk kan konfigureres +med RateLimitIntervalSec= og RateLimitBurst= i +/etc/systemd/journald.conf. Se journald.conf(5) for detaljer herom. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Journal beskeder er gået tabt +Defined-By: systemd +Support: %SUPPORT_URL% + +Kernel beskeder er gået tabt da journal systemet ikke har været i stand +til at håndtere dem hurtigt nok. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Fejl-fil genereret for process @COREDUMP_PID@ (@COREDUMP_COMM@) +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Process @COREDUMP_PID@ (@COREDUMP_COMM@) har lukket ned og genereret en +fejl-fil. + +Dette indikerer som regel en programmeringsfejl i det nedlukkede program +og burde blive reporteret som en bug til folkene bag + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: En ny session @SESSION_ID@ er blevet lavet for bruger @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En ny session med ID @SESSION_ID@ er blevet lavet for brugeren @USER_ID@. + +Den ledende process for sessionen er @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Session @SESSION_ID@ er blevet lukket ned +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En session med ID @SESSION_ID@ er blevet lukket ned. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: En ny arbejdsstation $SEAT_ID@ er nu tilgængelig +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En ny arbejdsstation @SEAT_ID@ er blevet konfigureret og er nu tilgængelig. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Arbejdsstation @SEAT_ID@ er nu blevet fjernet +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +En arbejdsstation @SEAT_ID@ er blevet fjernet og er ikke længere tilgængelig. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Tidsændring +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemtiden er blevet ændret til @REALTIME@ mikrosekunder efter d. 1. Januar 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Tidszoneændring til @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Tidszonen for systemet er blevet ændret til @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Opstart af systemet er nu fuldført +Defined-By: systemd +Support: %SUPPORT_URL% + +Alle system services i kø til at køre ved opstart, er blevet startet +med success. Bemærk at dette ikke betyder at maskinen er i dvale, da +services stadig kan være i gang med at færdiggøre deres opstart. + +Opstart af kernel tog @KERNEL_USEC@ mikrosekunder. + +Opstart af initrd tog @INITRD_USEC@ mikrosekunder. + +Opstart af userspace tog @USERSPACE_USEC@ mikrosekunder. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: System slumretilstand @SLEEP@ trådt i kraft +Defined-By: systemd +Support: %SUPPORT_URL% + +System er nu gået i @SLEEP@ slumretilstand. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: System slumretilstand @SLEEP@ forladt +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemet har nu forladt @SLEEP@ slumretilstand. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Systemnedlukning påbegyndt +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemnedlukning er blevet påbegyndt. Nedlukningen er nu begyndt og +alle system services er blevet afbrudt og alle filsystemer afmonteret. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Enhed @UNIT@ har påbegyndt opstart +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ er begyndt at starte op. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Enhed @UNIT har færdiggjort opstart +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ er færdig med at starte op. + +Resultat for opstart er @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Enhed @UNIT@ har påbegyndt nedlukning +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ har påbegyndt nedlukning. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Enhed @UNIT@ har færdiggjort nedlukning +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ har færdiggjort nedlukning. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Enhed @UNIT@ har fejlet +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ har fejlet. + +Resultatet er @RESULT@ + +-- d34d037fff1847e6ae669a370e694725 +Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ er begyndt at genindlæse sin konfiguration + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Enhed @UNIT@ har færdiggjort genindlæsning af sin konfiguration +Defined-By: systemd +Support: %SUPPORT_URL% + +Enhed @UNIT@ er færdig med at genindlæse sin konfiguration + +Resultatet er: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Process @EXECUTABLE@ kunne ikke eksekveres +Defined-By: systemd +Support: %SUPPORT_URL% + +Processen @EXECUTABLE@ kunne ikke eksekveres og fejlede. + +Processens returnerede fejlkode er @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Èn eller flere beskeder kunne ikke videresendes til syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Èn eller flere beskeder kunne ikke videresendes til syslog servicen +der kører side-om-side med journald. Dette indikerer typisk at syslog +implementationen ikke har kunnet følge med mængden af ventende beskeder. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Monteringspunkt er ikke tomt +Defined-By: systemd +Support: %SUPPORT_URL% + +Folderen @WHERE@ er specificeret som monteringspunkt (andet felt i +/etc/fstab eller Where= feltet i systemd enhedsfil) men er ikke tom. +Dette forstyrrer ikke monteringen, men de pre-eksisterende filer i folderen +bliver utilgængelige. For at se de over-monterede filer; montér det +underlæggende filsystem til en anden lokation. + +-- 24d8d4452573402496068381a6312df2 +Subject: En virtuel maskine eller container er blevet startet +Defined-By: systemd +Support: %SUPPORT_URL% + +Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet +startet og er klar til brug. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: En virtuel maskine eller container er blevet afbrudt +Defined-By: systemd +Support: %SUPPORT_URL% + +Den virtuelle maskine @NAME@ med dens leder PID @LEADER@ er blevet +nedlukket. diff --git a/src/grp-journal/catalog/systemd.fr.catalog.in b/src/grp-journal/catalog/systemd.fr.catalog.in new file mode 100644 index 0000000000..573b288e74 --- /dev/null +++ b/src/grp-journal/catalog/systemd.fr.catalog.in @@ -0,0 +1,320 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2013-2015 Sylvain Plantefève +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# French translation + +# Le format du catalogue de messages est décrit (en anglais) içi : +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +-- f77379a8490b408bbe5f6940505a777b +Subject: Le journal a été démarré +Defined-By: systemd +Support: %SUPPORT_URL% + +Le processus du journal système a démarré, ouvert ses fichiers en écriture +et est prêt à traiter les requêtes. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Le journal a été arrêté +Defined-By: systemd +Support: %SUPPORT_URL% + +Le processus du journal système a été arrêté et tous ses fichiers actifs +ont été fermés. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Espace disque utilisé par le journal +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +@JOURNAL_NAME@ (@JOURNAL_PATH@) utilise actuellement @CURRENT_USE_PRETTY@. +Le maximum autorisé est défini à @MAX_USE_PRETTY@. +Au moins @DISK_KEEP_FREE_PRETTY@ doivent être laissés libres +(sur @DISK_AVAILABLE_PRETTY@ d'espace disque actuellement libre). +La limite appliquée est donc @LIMIT_PRETTY@, dont @AVAILABLE_PRETTY@ +sont toujours disponibles. + +Les limites définissant la quantité d'espace disque que peut utiliser le +journal peuvent être configurées avec les paramètres SystemMaxUse=, +SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, +RuntimeMaxFileSize= dans le fichier /etc/systemd/journald.conf. +Voir journald.conf(5) pour plus de détails. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Des messages d'un service ont été supprimés +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Un service a essayé d'enregistrer un trop grand nombre de messages sur un +intervalle de temps donné. Des messages de ce service ont été évincés. + +Notez que seuls des messages de ce service ont été évincés, les messages des +autres services ne sont pas affectés. + +Les limites définissant ce comportement peuvent être configurées avec les +paramètres RateLimitIntervalSec= et RateLimitBurst= dans le fichier +/etc/systemd/journald.conf. Voir journald.conf(5) pour plus de détails. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Des messages du journal ont été manqués +Defined-By: systemd +Support: %SUPPORT_URL% + +Des messages du noyau ont été manqués car le journal système n'a pas été +capable de les traiter suffisamment vite. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Le processus @COREDUMP_PID@ (@COREDUMP_COMM@) a généré un fichier « core » +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Le processus @COREDUMP_PID@ (@COREDUMP_COMM@) a planté et généré un fichier « core ». + +Cela indique généralement une erreur de programmation dans le programme +incriminé, et cela devrait être notifié à son concepteur comme un défaut (bug). + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Une nouvelle session @SESSION_ID@ a été créée pour l'utilisateur @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Une nouvelle session a été créée pour l'utilisateur @USER_ID@ avec +l'identifiant (ID) @SESSION_ID@. + +Le processus maître de la session est @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: La session @SESSION_ID@ s'est terminée +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +La session d'identifiant (ID) @SESSION_ID@ s'est terminée. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Un nouveau poste (seat) @SEAT_ID@ est disponible +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Un nouveau poste (seat) @SEAT_ID@ a été configuré et est maintenant +disponible. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Le poste (seat) @SEAT_ID@ a été retiré +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Le poste (seat) @SEAT_ID@ a été retiré et n'est plus disponible. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Changement d'heure +Defined-By: systemd +Support: %SUPPORT_URL% + +L'horloge système a été modifiée et positionnée à @REALTIME@ microsecondes +après le 1er janvier 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Fuseau horaire modifié en @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Le fuseau horaire du système a été modifié et positionné à @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Le démarrage du système est terminé +Defined-By: systemd +Support: %SUPPORT_URL% + +Tous les services nécessaires au démarrage du système ont été lancés avec +succès. Notez que cela ne signifie pas que le système est maintenant au +repos, car des services peuvent encore être en train de terminer leur +démarrage. + +Le chargement du noyau a nécessité @KERNEL_USEC@ microsecondes. + +Le chargement du « RAM disk » initial a nécessité @INITRD_USEC@ microsecondes. + +Le chargement de l'espace utilisateur a nécessité @USERSPACE_USEC@ microsecondes. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Le système entre dans l'état de repos (sleep state) @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Le système est maintenant à l'état de repos (sleep state) @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Le système sorti de l'état de repos (sleep state) @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Le système est maintenant sorti de l'état de repos (sleep state) @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Arrêt du système amorcé +Defined-By: systemd +Support: %SUPPORT_URL% + +L'arrêt du système a été amorcé. L'arrêt a maintenant commencé, tous les +services du système sont terminés et tous les systèmes de fichiers sont +démontés. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: L'unité (unit) @UNIT@ a commencé à démarrer +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a commencé à démarrer. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: L'unité (unit) @UNIT@ a terminé son démarrage +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: L'unité (unit) @UNIT@ a commencé à s'arrêter +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a commencé à s'arrêter. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: L'unité (unit) @UNIT@ a terminé son arrêt +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a terminé son arrêt. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: L'unité (unit) @UNIT@ a échoué +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a échoué, avec le résultat @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: L'unité (unit) @UNIT@ a commencé à recharger sa configuration +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a commencé à recharger sa configuration. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: L'unité (unit) @UNIT@ a terminé de recharger configuration +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unité (unit) @UNIT@ a terminé de recharger configuration, +avec le résultat @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Le processus @EXECUTABLE@ n'a pas pu être exécuté +Defined-By: systemd +Support: %SUPPORT_URL% + +Le processus @EXECUTABLE@ n'a pas pu être exécuté, et a donc échoué. + +Le code d'erreur renvoyé est @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Un ou plusieurs messages n'ont pas pu être transmis à syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Un ou plusieurs messages n'ont pas pu être transmis au service syslog +s'exécutant conjointement avec journald. Cela indique généralement que +l'implémentation de syslog utilisée n'a pas été capable de suivre +la cadence du flux de messages. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Le point de montage n'est pas vide +Defined-By: systemd +Support: %SUPPORT_URL% + +Le répertoire @WHERE@ est spécifié comme point de montage (second champ du +fichier /etc/fstab, ou champ Where= dans une unité (unit) systemd) et n'est +pas vide. +Cela ne perturbe pas le montage du système de fichiers, mais les fichiers +préalablement présents dans ce répertoire sont devenus inaccessibles. +Pour atteindre ces fichiers, veuillez monter manuellement le système de +fichiers sous-jacent à un autre emplacement. + +-- 24d8d4452573402496068381a6312df2 +Subject: Une machine virtuelle ou un conteneur (container) a été démarré +Defined-By: systemd +Support: %SUPPORT_URL% + +La machine virtuelle @NAME@ a été démarrée avec le PID maître @LEADER@, +et est maintenant prête à l'emploi. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Une machine virtuelle ou un conteneur (container) a été arrêté +Defined-By: systemd +Support: %SUPPORT_URL% + +La machine virtuelle @NAME@ avec le PID maître @LEADER@ a été arrêtée. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Le mode DNSSEC a été désactivé, car il n'est pas supporté par le serveur +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Le service de résolution (systemd-resolved.service) a détecté que le serveur +DNS configuré ne supporte pas DNSSEC, et la validation DNSSEC a donc été +désactivée. + +Cet évènement se produit si DNSSEC=allow-downgrade est configuré dans +resolved.conf et que le serveur DNS configuré n'est pas compatible avec +DNSSEC. +Veuillez noter que ce mode permet des attaques de rétrogradation DNSSEC, +car un attaquant peut être capable de désactiver la validation DNSSEC sur +le système en injectant des réponses DNS dans le canal de communication. + +Cet évènement indique que le serveur DNS est effectivement incompatible avec +DNSSEC, ou qu'un attaquant a peut-être conduit une telle attaque avec succès. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: La validation DNSSEC a échoué +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Une requête ou une ressource DNS n'a pas passé la validation DNSSEC. +Ceci est généralement une indication que le canal de communication a été +altéré. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Une ancre de confiance DNSSEC a été révoquée +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Une ancre de confiance DNSSEC a été révoquée. Une nouvelle ancre de +confiance doit être configurée, ou le système d'exploitation a besoin +d'être mis à jour, pour fournir une version à jour de l'ancre de confiance. diff --git a/src/grp-journal/catalog/systemd.hr.catalog.in b/src/grp-journal/catalog/systemd.hr.catalog.in new file mode 100644 index 0000000000..7502aed741 --- /dev/null +++ b/src/grp-journal/catalog/systemd.hr.catalog.in @@ -0,0 +1,314 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Croatian translation + +# Format kataloga je dokumentiran na +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# Za pojašnjenje zašto ovo radimo, posjetite https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: journal je pokrenut +Defined-By: systemd +Support: %SUPPORT_URL% + +Journal proces sustava se pokrenuo, otvorio je journal + datoteke za upis i spreman je za obradu zahtjeva. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: journal je zaustavljen +Defined-By: systemd +Support: %SUPPORT_URL% + +Journal proces sustava je isključio i zatvorio sve trenutno +aktivne journal datoteke. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Diskovni prostor koji koristi journal +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) trenutno koristi @CURRENT_USE_PRETTY@. +Najveća dopuštena upotreba je postavljena na @MAX_USE_PRETTY@. +Ostavljam najmanje @DISK_KEEP_FREE_PRETTY@ slobodno (trenutno dostupno @DISK_AVAILABLE_PRETTY@ diskovnog prostora). +Prisilno ograničenje upotrebe je @LIMIT_PRETTY@, od kojeg je @AVAILABLE_PRETTY@ još dostupno. + +Ograničenja kontroliraju koliko diskovnog prostora koristi journal mogu +se podesiti sa SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, +RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= settings u +/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Poruka iz usluge je potisnuta +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Usluga je prijavila previše poruka u određenom vremenskom razdoblju. Poruke +iz usluge su odbačene. + +Zapamtite da samo poruke iz usluge u upitu su +odbačene, ostale poruke usluga nisu zahvaćene. + +Ograničenja koja kontroliraju kada je poruka odbačena mogu se podesiti +sa RateLimitIntervalSec= i RateLimitBurst= u +/etc/systemd/journald.conf. Pogledajte journald.conf(5) za više pojedinosti. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Journal poruka je propuštena +Defined-By: systemd +Support: %SUPPORT_URL% + +Kernel poruka je izgubljena zato jer ih journal sustav nije mogao +dovoljno brzo obraditi. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Proces @COREDUMP_PID@ (@COREDUMP_COMM@) je izbacio jezgru +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Proces @COREDUMP_PID@ (@COREDUMP_COMM@) se srušio i izbacio jezgru. + +Rušenje programa je uobičajeno uzrokovano greškom u programiranju i +trebalo bi se prijaviti razvijatelju kao greška. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Nova sesija @SESSION_ID@ je stvorena za korisnika @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Nova sesija sa ID @SESSION_ID@ je stvorena za korisnika @USER_ID@. + +Glavni proces sesije je @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Sesija @SESSION_ID@ je prekinuta +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Sesija sa ID @SESSION_ID@ je prekinuta. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Novo sjedište @SEAT_ID@ je sada dostupno +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Novo sjedište @SEAT_ID@ je podešeno i sada je dostupno. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Sjedište @SEAT_ID@ je sada uklonjeno +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Sjedište @SEAT_ID@ je uklonjeno i više nije dostupno. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Vrijeme promjene +Defined-By: systemd +Support: %SUPPORT_URL% + +Sat sustava je promijenjen na @REALTIME@ microsekundi nakon 1. Siječnja, 1970 godine. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Vremenska zona je promijenjena u @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Vremenska zona je promijenjena u @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Pokretanje sustava je sada završeno +Defined-By: systemd +Support: %SUPPORT_URL% + +Sve usluge sustava koje su zadane za pokretanje pri pokretanju sustava +su uspješno pokrenute. Zapamtite da ovo ne znači da sada računalo +miruje zato jer se neke usluge još uvijek mogu pokretati. + +Pokretanje kernela zahtijeva @KERNEL_USEC@ mikrosekundi. + +Pokretanje početnog RAM diska zahtijeva @INITRD_USEC@ mikrosekundi. + +Pokretanje prostora korisnika zahtijeva @USERSPACE_USEC@ mikrosekundi. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Pokrenuto je stanje spavanja @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Sustav je sada pokrenuo stanje spavanja @SLEEP@ + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Završeno je stanje spavanja @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Sustav je sada završio stanje spavanja @SLEEP@ + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Pokrenuto je isključivanje sustava +Defined-By: systemd +Support: %SUPPORT_URL% + +Pokrenuto je isključivanje sustava. Isključivanje je sada pokrenuto, +sve usluge sustava su prekinute i svi datotečni sustavi su odmontirani. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Jedinica @UNIT@ je započela pokretanje +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je započela pokretanje. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Jedinica @UNIT@ je završila pokretanje +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je završila pokretanje. + +Rezultat pokretanja je @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Jedinica @UNIT@ je započela isključivanje +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je započela isključivanje. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Jedinica @UNIT@ je završila isključivanje +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je završila isključivanje. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Jedinica @UNIT@ nije uspjela +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ nije uspjela. + +Rezultat je @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je započela ponovno učitavati podešavanja + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Jedinica @UNIT@ je završila ponovno učitavati podešavanja +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedinica @UNIT@ je završila ponovno učitavati podešavanja + +Rezultat je @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Proces @EXECUTABLE@ se ne može pokrenuti +Defined-By: systemd +Support: %SUPPORT_URL% + +Proces @EXECUTABLE@ se ne može pokrenuti i nije uspio. + +Broj greške vraćen ovim procesom je @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava +Defined-By: systemd +Support: %SUPPORT_URL% + +Jedna ili više poruka se ne mogu proslijediti u dnevnik sustava, usluge +su pokrenute istovremeno s journalom. Ovo uobičajeno označava da +implementacija dnevnika sustava ne može slijediti brzinu +zahtjeva poruka. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Točka montiranja nije prazna +Defined-By: systemd +Support: %SUPPORT_URL% + +Direktorij @WHERE@ je određen za točku montiranja (drugi redak u +/etc/fstab ili Where= redak u datoteci systemd jedinice) i nije prazan. +To ne utječe na montiranje, ali postojeće datoteke u ovom direktoriju +postaju nedostupne. Kako bi vidjeli datoteke preko kojih je montirano, +ručno montirajte osnovni datotečni sustav na drugu lokaciju. + +-- 24d8d4452573402496068381a6312df2 +Subject: Virtualni stroj ili spremnik su pokrenuti +Defined-By: systemd +Support: %SUPPORT_URL% + +Virtualni stroj @NAME@ sa vodećim @LEADER@ PID-om je +pokrenut i spreman je za korištenje. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Virtualni stroj ili spremnik su isključeni +Defined-By: systemd +Support: %SUPPORT_URL% + +Virtualni stroj @NAME@ sa vodećim PID-om @LEADER@ je +isključen. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: DNSSEC način je isključen, jer ga poslužitelj ne podržava +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Usluga razrješavanja (systemd-resolved.service) je otkrila da +podešeni DNS poslužitelj ne podržava DNSSEC, i DNSSEC, kao rezultat +provjera je isključena. + +Ovaj događaj će zauzeti mjesto ako je DNSSEC=allow-downgrade podešen u +resolved.conf i podešeni DNS poslužitelj je nekompatibilan s DNSSEC. Zapamtite +da korištenje ovog načina dopušta povećanje DNSSEC napada, napadač bi mogao +isključiti DNSSEC provjeru na sustavu umetanjem DNS odgovora u +komunikacijski kanal što rezultira povećanjem napada poput ovog. + +Ovaj događaj bi mogao označavati da je DNS poslužitelj uistinu nekompatibilan s +DNSSEC ili da je napadač uspješno izvršio takav napad. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: DNSSEC provjera neuspješna +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +DNS zahtjev ili snimak resursa nije prošao DNSSEC provjeru. To uobičajeno +označava da je komunikacijski kanal mijenjan. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: DNSSEC pouzdano sidro je opozvano +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +A DNSSEC trust anchor has been revoked. A new trust anchor has to be +configured, or the operating system needs to be updated, to provide an updated +DNSSEC trust anchor. diff --git a/src/grp-journal/catalog/systemd.hu.catalog.in b/src/grp-journal/catalog/systemd.hu.catalog.in new file mode 100644 index 0000000000..f538b7f958 --- /dev/null +++ b/src/grp-journal/catalog/systemd.hu.catalog.in @@ -0,0 +1,262 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2016 Gabor Kelemen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: A napló elindult +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszernapló folyamat elindult, megnyitotta írásra a naplófájlokat, +és most készen áll kérések feldolgozására. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: A napló leállt +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszernapló folyamat leállt, és bezárt minden jelenleg aktív naplófájlt. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Egy szolgáltatás üzenetei elnémítva +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Egy szolgáltatás túl sok üzenetet naplózott adott idő alatt. A +szolgáltatástól származó üzenetek eldobásra kerültek. + +Ne feledje, hogy csak a kérdéses szolgáltatás üzenetei kerültek eldobásra, + más szolgáltatások üzeneteit ez nem befolyásolja. + +Az üzenetek eldobását vezérlő korlátok az /etc/systemd/journald.conf +RateLimitIntervalSec= és RateLimitBurst= beállításaival adhatók meg. +Részletekért lásd a journald.conf(5) man oldalt. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Naplóüzenetek vesztek el +Defined-By: systemd +Support: %SUPPORT_URL% + +Kernelüzenetek vesztek el, mert a naplózó rendszer nem tudta elég gyorsan +feldolgozni azokat. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Egy folyamat összeomlott: @COREDUMP_PID@ (@COREDUMP_COMM@) +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Ez a folyamat: @COREDUMP_PID@ (@COREDUMP_COMM@) összeomlott, és core fájlt + írt ki. + +Ez általában programozási hibát jelez az összeomló programban, és +a szállítója felé kell bejelenteni. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Új munkamenet (@SESSION_ID@) létrehozva, felhasználója: @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Létrejött egy új munkamenet @SESSION_ID@ azonosítóval ezen felhasználóhoz: +@USER_ID@. + +A munkamenet vezető folyamata: @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Munkamenet (@SESSION_ID@) befejezve +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A következő azonosítójú munkamenet befejeződött: @SESSION_ID@. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Elérhető egy új munkaállomás: @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Beállításra kerül és használható egy új munkaállomás: @SEAT_ID@. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: A munkaállomás eltávolítva: @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +A munkaállomás el lett távolítva, és már nem érhető el: @SEAT_ID@ + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Időmódosítás +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszeróra beállítva @REALTIME@ ezredmásodpercre 1970. január 1. után. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Időzóna-módosítás erre: @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszer időzónája módosítva lett erre: @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: A rendszer indítása kész +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszerindításkor szükséges indításhoz sorba állított összes +rendszerszolgáltatás elindult. Ne feledje, hogy ez nem jelenti, hogy a +gép üresjáratban van, mivel egyes szolgáltatások még az indítás +befejezésével lehetnek elfoglalva. + +A kernel indítása @KERNEL_USEC@ ezredmásodpercet igényelt. + +A kiinduló RAM lemez indítása @INITRD_USEC@ ezredmásodpercet igényelt. + +A felhasználói programok indítása @USERSPACE_USEC@ ezredmásodpercet igényelt. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: A rendszer „@SLEEP@” alvási állapotba lépett +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszer belépett ebbe az alvási állapotba: @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: A rendszer „@SLEEP@” alvási állapotból kilépett +Defined-By: systemd +Support: %SUPPORT_URL% + +A rendszer kilépett ebből az alvási állapotból: @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Rendszer leállítása kezdeményezve +Defined-By: systemd +Support: %SUPPORT_URL% + +A systemd leállítása kezdeményezve. A leállítás megkezdődött, minden +rendszerszolgáltatás befejeződik, minden fájlrendszer leválasztásra kerül. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: A(z) @UNIT@ egység indítása megkezdődött +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység megkezdte az indulást. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: A(z) @UNIT@ egység befejezte az indulást +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység befejezte az indulást + +Az indítás eredménye: @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: A(z) @UNIT@ egység megkezdte a leállást +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység megkezdte a leállást. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: A(z) @UNIT@ egység befejezte a leállást +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység befejezte a leállást. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: A(z) @UNIT@ egység hibát jelzett +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység hibát jelzett. + +Az eredmény: @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység megkezdte a beállításainak újratöltését. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: A(z) @UNIT@ egység befejezte a beállításainak újratöltését +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @UNIT@ egység befejezte a beállításainak újratöltését. + +Az eredmény: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +A folyamat végrehajtása sikertelen volt, és hibát jelzett: @EXECUTABLE@. + +A folyamat által visszaadott hibaszám: @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Legalább egy üzenet nem továbbítható a rendszernaplónak +Defined-By: systemd +Support: %SUPPORT_URL% + +Legalább egy üzenet nem volt továbbítható a journald-vel párhuzamosan futó +syslog szolgáltatásnak. Ez általában azt jelenti, hogy a syslog +megvalósítás nem volt képes lépést tartani a sorba állított +üzenetek sebességével. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: A csatolási pont nem üres +Defined-By: systemd +Support: %SUPPORT_URL% + +A csatolási pontként megadott @WHERE@ könyvtár (második mező az /etc/fstab +fájlban, vagy a Where= sor a systemd egységfájlban) nem üres. Ez nem +akadályozza meg a csatolást, de a könyvtárban már meglévő fájlok +elérhetetlenné válnak. A fájlok láthatóvá tételéhez csatolja +az azokat tartalmazó fájlrendszert egy másodlagos helyre. + +-- 24d8d4452573402496068381a6312df2 +Subject: Egy virtuális gép vagy konténer elindult +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) elindult, és +használatra kész. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Egy virtuális gép vagy konténer befejeződött +Defined-By: systemd +Support: %SUPPORT_URL% + +A(z) @NAME@ nevű virtuális gép (vezető PID: @LEADER@) leállt. diff --git a/src/grp-journal/catalog/systemd.it.catalog.in b/src/grp-journal/catalog/systemd.it.catalog.in new file mode 100644 index 0000000000..86e44a604d --- /dev/null +++ b/src/grp-journal/catalog/systemd.it.catalog.in @@ -0,0 +1,254 @@ +# This file is part of systemd. +# +# Copyright 2013 Daniele Medri +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages + +-- f77379a8490b408bbe5f6940505a777b +Subject: Il registro è stato avviato +Defined-By: systemd +Support: %SUPPORT_URL% + +Il processo relativo al registro di sistema è stato avviato, ha aperto i +file in scrittura ed è ora pronto a gestire richieste. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Il registro è stato terminato +Defined-By: systemd +Support: %SUPPORT_URL% + +Il processo relativo al registro di sistema è stato terminato e ha chiuso +tutti i file attivi. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: I messaggi di un servizio sono stati soppressi +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Un servizio ha registrato troppi messaggi in un dato periodo di tempo. +I messaggi del servizio sono stati eliminati. + +Solo i messaggi del servizio indicato sono stati +eliminati, i messaggi degli altri servizi rimangono invariati. + +I limiti oltre i quali i messaggi si eliminano si configurano +con RateLimitIntervalSec= e RateLimitBurst= in +/etc/systemd/journald.conf. Vedi journald.conf(5) per maggiori informazioni. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: I messaggi di un servizio sono stati perduti +Defined-By: systemd +Support: %SUPPORT_URL% + +I messaggi del kernel sono stati perduti perché, il registro di sistema +non è stato in grado di gestirli abbastanza velocemente. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Il processo @COREDUMP_PID@ (@COREDUMP_COMM@) ha generato un dump. +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Il processo @COREDUMP_PID@ (@COREDUMP_COMM@) si è bloccato generando un dump. + +Questo di solito capita per un errore di programmazione nell'applicazione e +dovrebbe essere segnalato al vendor come un bug. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: La nuova sessione @SESSION_ID@ è stata creata per l'utente @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Una nuova sessione con ID @SESSION_ID@ è stata creata per l'utente @USER_ID@. + +Il processo primario della sessione è @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: La sessione @SESSION_ID@ è terminata +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +La sessione con ID @SESSION_ID@ è terminata. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: La nuova postazione @SEAT_ID@ è ora disponibile +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +La nuova postazione @SEAT_ID@ è stata configurata ed è ora disponibile. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: La postazione @SEAT_ID@ è stata rimossa +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +La postazione @SEAT_ID@ è stata rimossa e non è più disponibile. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Cambio d'orario +Defined-By: systemd +Support: %SUPPORT_URL% + +L'orologio di sistema è cambiato in @REALTIME@ microsecondi dal 1 gennaio, 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Il fuso orario è cambiato in @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Il fuso orario di sistema è cambiato in @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Avvio del sistema completato. +Defined-By: systemd +Support: %SUPPORT_URL% + +Tutti i servizi di sistema richiesti per la fase di avvio sono stati eseguiti +con successo. Nota che la macchina potrebbe non essere ancora pronta in quanto +i servizi attivati sono in fase di completamento. + +L'avvio del kernel ha richiesto @KERNEL_USEC@ microsecondi. + +L'avvio del disco RAM ha richiesto @INITRD_USEC@ microsecondi. + +L'avvio dello userspace ha richiesto @USERSPACE_USEC@ microsecondi. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Il sistema è entrato in fase di pausa @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Il sistema è entrato nello stato di pausa @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Il sistema è uscito dalla fase di pausa @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Il sistema è uscito dallo stato di pausa @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Il sistema è in fase di spegnimento +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemd è in fase di spegnimento. Tutti i servizi di sistema +saranno terminati e tutti i file systems smontati. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: L'unità @UNIT@ inizia la fase di avvio +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ ha iniziato la fase di avvio. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: L'unità @UNIT@ termina la fase di avvio +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ ha terminato la fase di avvio. + +La fase di avvio è @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: L'unità @UNIT@ inizia la fase di spegnimento +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ ha iniziato la fase di spegnimento. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: L'unità @UNIT@ termina la fase di spegnimento +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ ha terminato la fase di spegnimento. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: L'unità @UNIT@ è fallita +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ è fallita. + +Il risultato è @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: L'unità @UNIT@ inizia a caricare la propria configurazione +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ è iniziata ricaricando la propria configurazione + +-- 7b05ebc668384222baa8881179cfda54 +Subject: L'unità @UNIT@ termina il caricamento della propria configurazione +Defined-By: systemd +Support: %SUPPORT_URL% + +L'unità @UNIT@ è terminata ricaricando la propria configurazione + +Il risultato è @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Il processo @EXECUTABLE@ non può essere eseguito +Defined-By: systemd +Support: %SUPPORT_URL% + +Il processo @EXECUTABLE@ non può essere eseguito e termina. + +Il numero di errore restituito durante l'esecuzione del processo è @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Uno o più messaggi non possono essere inoltrati a syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Uno o più messaggi non possono essere inviati al servizio syslog +eseguito in parallelo a journald. Questo di solito capita perché, +l'implementazione di syslog non sta al passo con la +velocità dei messaggi accodati. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Il punto di montaggio non è vuoto +Defined-By: systemd +Support: %SUPPORT_URL% + +La directory @WHERE@ è specificata come punto di montaggio (secondo campo +in /etc/fstab o nel campo Where= del file unità di systemd) e non è vuoto. +Questo non interferisce con il montaggio, ma i file pre-esistenti in questa +directory diventano inaccessibili. Per visualizzare i file, si suggerisce +di montare manualmente il file system indicato in una posizione secondaria. + +-- 24d8d4452573402496068381a6312df2 +Subject: Avviata macchina virtuale o container +Defined-By: systemd +Support: %SUPPORT_URL% + +La macchina virtuale @NAME@ con PID primario @LEADER@ è stata +avviata ed è pronta all'uso. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Terminata macchina virtuale o container +Defined-By: systemd +Support: %SUPPORT_URL% + +La macchina virtuale @NAME@ con PID primario @LEADER@ è stata spenta. diff --git a/src/grp-journal/catalog/systemd.ko.catalog.in b/src/grp-journal/catalog/systemd.ko.catalog.in new file mode 100644 index 0000000000..8a053254ee --- /dev/null +++ b/src/grp-journal/catalog/systemd.ko.catalog.in @@ -0,0 +1,264 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Korean translation + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ +# +# Translator : +# Seong-ho Cho , 2015. + +-- f77379a8490b408bbe5f6940505a777b +Subject: 저널 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +시스템 저널 프로세스를 시작했고 기록목적으로 저널 파일을 열었으며, +프로세스 요청을 기다리고 있습니다. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: 저널 멈춤 +Defined-By: systemd +Support: %SUPPORT_URL% + +시스템 저널 프로세스를 껐고 현재 활성화 중인 저널 파일을 모두 +닫았습니다. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: 서비스의 메시지를 거절함 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +일정 시간동안 서비스에서 너무 많은 메시지를 기록했습니다. +서비스에서 오는 메시지를 거절했습니다. + +의문점이 있는 서비스로부터 오는 메시지만 거절했음을 참고하십시오 +다른 서비스의 메시지에는 영향을 주지 않습니다. + +메시지 거절 제어 제한 값은 /etc/systemd/journald.conf 의 +RateLimitIntervalSec= 변수와 RateLimitBurst= 변수로 설정합니다. +자세한 내용은 ournald.conf(5)를 살펴보십시오. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: 저널 메시지 놓침 +Defined-By: systemd +Support: %SUPPORT_URL% + +저널 시스템에서 커널 메시지를 충분히 빠르게 처리할 수 없어 커널 + 메시지를 잃었습니다. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: 프로세스 @COREDUMP_PID@번 코어 덤프(@COREDUMP_COMM@) 생성함 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +프로세스 @COREDUMP_PID@번 (@COREDUMP_COMM@)이 비정상적으로 끝나 +코어 덤프를 생성했습니다. + +보통 비정상 종료 관리 프로그램에서 프로그래밍 오류를 나타내며, +제작자에게 버그로 보고해야합니다. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: @USER_ID@ 사용자의 새 @SESSION_ID@ 세션 만듦 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +@USER_ID@ 사용자의 새 @SESSION_ID@ 세션을 만들었습니다. + +이 세션의 관리 프로세스는 @LEADER@ 입니다. + +-- 3354939424b4456d9802ca8333ed424a +Subject: @SESSION_ID@ 세션 마침 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +@SESSION_ID@ 세션을 끝냈습니다. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: 새 @SEAT_ID@ 시트 사용할 수 있음 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +새 @SEAT_ID@ 시트를 설정했고 사용할 수 있습니다. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: @SEAT_ID@ 시트 제거함 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +@SEAT_ID@ 시트를 제거했으며 더이상 사용할 수 없습니다. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: 시간 바꿈 +Defined-By: systemd +Support: %SUPPORT_URL% + +시스템 시계를 1970년 1월 1일 이후로 @REALTIME@ 마이크로초 지난 값으로 +설정했습니다. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: @TIMEZONE@ 시간대로 시간대 바꿈 +Defined-By: systemd +Support: %SUPPORT_URL% + +시스템 시간대를 @TIMEZONE@ 시간대로 바꾸었습니다. + +-- b07a249cd024414a82dd00cd181378ff +Subject: 시스템 시동 마침 +Defined-By: systemd +Support: %SUPPORT_URL% + +부팅 과정에 시작하려고 준비한 모든 시스템 서비스를 성공적으로 + 시작했습니다. 머신이 서비스처럼 대기중이라는 의미는 아니며 +지동을 완전히 마칠 때까지 사용중일 수도 있는 점 참고하십시오. + +커널 시동에 @KERNEL_USEC@ 마이크로초가 걸립니다. + +초기 램 디스크 시동에 @INITRD_USEC@ 마이크로초가 걸립니다. + +사용자 영역 시동에 @USERSPACE_USEC@ 마이크로초가 걸립니다. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: @SLEEP@ 대기 상태 진입 +Defined-By: systemd +Support: %SUPPORT_URL% + +@SLEEP@ 대기 상태로 진입했습니다. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: @SLEEP@ 대기 상태 마침 +Defined-By: systemd +Support: %SUPPORT_URL% + +@SLEEP@ 대기 상태를 마쳤습니다. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: 컴퓨터 끄기 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +컴퓨터 끄기 동작을 시작했습니다. 모든 시스템 동작을 멈추고 +모든 파일 시스템의 마운트를 해제합니다. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: @UNIT@ 유닛 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛을 시작했습니다. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: @UNIT@ 유닛 시동 마침 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛 시동을 마쳤습니다. + +시동 결과는 @RESULT@ 입니다. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: @UNIT@ 유닛 끝내기 동작 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛 끝내기 동작을 시작했습니다. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: @UNIT@ 유닛 끝내기 동작 마침 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛 끝내기 동작을 마쳤습니다. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: @UNIT@ 유닛 동작 실패 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛 동작에 실패했습니다. + +결과는 @RESULT@ 입니다. + +-- d34d037fff1847e6ae669a370e694725 +Subject: @UNIT@ 유닛 설정 다시 읽기 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛의 설정 다시 읽기를 시작했습니다 + +-- 7b05ebc668384222baa8881179cfda54 +Subject: @UNIT@ 유닛 설정 다시 읽기 완료 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 유닛의 설정 다시 읽기 동작을 끝냈습니다. + +결과는 @RESULT@ 입니다. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: @EXECUTABLE@ 프로세스 시작할 수 없음 +Defined-By: systemd +Support: %SUPPORT_URL% + +@EXECUTABLE@ 프로세스를 시작할 수 없어 실행에 실패했습니다. + +이 프로세스에서 반환한 오류 번호는 @ERRNO@번 입니다. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: 하나 이상의 메시지를 syslog에 전달할 수 없음 +Defined-By: systemd +Support: %SUPPORT_URL% + +journald 서비스와 동시에 실행중인 syslog 서비스에 하나 이상의 메시지를 +전달할 수 없습니다. 보통 순차적으로 오는 메시지의 속도를 syslog 구현체가 +따라가지 못함을 의미합니다. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: 마운트 지점 비어있지 않음 +Defined-By: systemd +Support: %SUPPORT_URL% + +@WHERE@ 디렉터리를 마운트 지점으로 지정했으며 (/etc/fstab 파일의 + 두번째 필드 또는 systemd 유닛 파일의 Where= 필드) 비어있지 않습니다. +마운트 과정에 방해가 되진 않지만 이전에 이 디렉터리에 존재하는 파일에 + 접근할 수 없게 됩니다. 중복으로 마운트한 파일을 보려면, 근본 파일 +시스템의 다음 위치에 직접 마운트하십시오. + +-- 24d8d4452573402496068381a6312df2 +Subject: 가상 머신 또는 컨테이너 시작 +Defined-By: systemd +Support: %SUPPORT_URL% + +@LEADER@ 프로세스 ID로 동작하는 @NAME@ 가상 머신을 시작했으며, +이제부터 사용할 수 있습니다. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: 가상 머신 또는 컨테이너 마침 +Defined-By: systemd +Support: %SUPPORT_URL% + +@LEADER@ 프로세스 ID로 동작하는 @NAME@ 가상 머신을 껐습니다. diff --git a/src/grp-journal/catalog/systemd.pl.catalog.in b/src/grp-journal/catalog/systemd.pl.catalog.in new file mode 100644 index 0000000000..33c2122974 --- /dev/null +++ b/src/grp-journal/catalog/systemd.pl.catalog.in @@ -0,0 +1,315 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2014, 2015, 2016 Piotr Drąg +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Polish translation + +# The catalog format is documented on +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Uruchomiono dziennik +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemowy proces dziennika został uruchomiony, otworzył pliki dziennika do +zapisu i jest gotowy do przetwarzania żądań. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Zatrzymano dziennik +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemowy proces dziennika został wyłączony i zamknął wszystkie obecnie +aktywne pliki dziennika. + +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Miejsce na dysku używane przez dziennik +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) obecnie używa @CURRENT_USE_PRETTY@. +Maksymalnie może używać @MAX_USE_PRETTY@. +Zostawianie co najmniej @DISK_KEEP_FREE_PRETTY@ wolnego (z obecnie dostępnego @DISK_AVAILABLE_PRETTY@ miejsca na dysku). +Wymuszone ograniczenie użycia wynosi więc @LIMIT_PRETTY@, z czego @AVAILABLE_PRETTY@ jest nadal dostępne. + +Ograniczenia kontrolujące ilość miejsca na dysku używanego przez dziennik +można konfigurować za pomocą ustawień SystemMaxUse=, SystemKeepFree=, +SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= +w pliku /etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej +informacji. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Ograniczono komunikaty z usługi +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Usługa zapisała za dużo komunikatów w określonym czasie. Komunikaty z usługi +zostały pominięte. + +Proszę zauważyć, że tylko komunikaty z danej usługi zostały pominięte. Nie ma +to wpływu na komunikaty innych usług. + +Ograniczenia kontrolujące pomijanie komunikatów mogą być konfigurowane +za pomocą opcji RateLimitIntervalSec= i RateLimitBurst= w pliku +/etc/systemd/journald.conf. Strona journald.conf(5) zawiera więcej informacji. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Utracono komunikaty dziennika +Defined-By: systemd +Support: %SUPPORT_URL% + +Komunikaty jądra zostały utracone, ponieważ system dziennika nie mógł +przetworzyć ich odpowiednio szybko. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Proces @COREDUMP_PID@ (@COREDUMP_COMM@) zrzucił plik core +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Proces @COREDUMP_PID@ (@COREDUMP_COMM@) uległ awarii i zrzucił plik core. + +Zwykle wskazuje to na błąd programistyczny w danym programie i powinno zostać +zgłoszone jego producentowi jako błąd. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Utworzono nową sesję @SESSION_ID@ dla użytkownika @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Nowa sesja o identyfikatorze @SESSION_ID@ została utworzona dla użytkownika +@USER_ID@. + +Proces prowadzący sesji: @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Zakończono sesję @SESSION_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Sesja o identyfikatorze @SESSION_ID@ została zakończona. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Dostępne jest nowe stanowisko @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Nowe stanowisko @SEAT_ID@ zostało skonfigurowane i jest teraz dostępne. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Usunięto stanowisko @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Stanowisko @SEAT_ID@ zostało usunięte i nie jest już dostępne. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Zmiana czasu +Defined-By: systemd +Support: %SUPPORT_URL% + +Zegar systemowy został zmieniony na @REALTIME@ μs po 1 stycznia 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Zmiana strefy czasowej na @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemowa strefa czasowa została zmieniona na @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Ukończono uruchamianie systemu +Defined-By: systemd +Support: %SUPPORT_URL% + +Wszystkie usługi systemowe obowiązkowo zakolejkowane do włączenia podczas +uruchamiania systemu zostały pomyślnie uruchomione. Proszę zauważyć, że nie +oznacza to, że komputer jest bezczynny, jako że usługi mogą wciąż kończyć +proces uruchamiania. + +Uruchamianie jądra zajęło @KERNEL_USEC@ μs. + +Uruchamianie początkowego dysku RAM zajęło @INITRD_USEC@ μs. + +Uruchamianie przestrzeni użytkownika zajęło @USERSPACE_USEC@ μs. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Przejście do stanu uśpienia @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +System przeszedł do stanu uśpienia @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Wyjście ze stanu uśpienia @SLEEP@ +Defined-By: systemd +Support: %SUPPORT_URL% + +System wyszedł ze stanu uśpienia @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Zainicjowano wyłączenie systemu +Defined-By: systemd +Support: %SUPPORT_URL% + +Zainicjowano wyłączenie systemd. Wyłączenie zostało rozpoczęte i wszystkie +usługi systemowe zostały zakończone, a wszystkie systemy plików odmontowane. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Rozpoczęto uruchamianie jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ rozpoczęła uruchamianie. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Ukończono uruchamianie jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ ukończyła uruchamianie. + +Wynik uruchamiania: @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Rozpoczęto wyłączanie jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ rozpoczęła wyłączanie. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Ukończono wyłączanie jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ ukończyła wyłączanie. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Jednostka @UNIT@ się nie powiodła +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ się nie powiodła. + +Wynik: @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Rozpoczęto ponowne wczytywanie konfiguracji jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ rozpoczęła ponowne wczytywanie swojej konfiguracji. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Ukończono ponowne wczytywanie konfiguracji jednostki @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Jednostka @UNIT@ ukończyła ponowne wczytywanie swojej konfiguracji. + +Wynik: @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Nie można wykonać procesu @EXECUTABLE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Proces @EXECUTABLE@ nie mógł zostać wykonany i się nie powiódł. + +Numer błędu zwrócony przez ten proces: @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Nie można przekazać jednego lub więcej komunikatów do syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Jeden lub więcej komunikatów nie może zostać przekazanych do usługi syslog +uruchomionej obok journald. Zwykle oznacza to, że implementacja syslog nie +jest w stanie nadążyć za prędkością kolejki komunikatów. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Punkt montowania nie jest pusty +Defined-By: systemd +Support: %SUPPORT_URL% + +Katalog @WHERE@ został podany jako punkt montowania (drugie pole w pliku +/etc/fstab lub pole Where= w pliku jednostki systemd) i nie jest pusty. Nie +wpływa to na montowanie, ale wcześniej istniejące pliki w tym katalogu stają +się niedostępne. Aby zobaczyć te pliki, proszę ręcznie zamontować system +plików w innym położeniu. + +-- 24d8d4452573402496068381a6312df2 +Subject: Uruchomiono maszynę wirtualną lub kontener +Defined-By: systemd +Support: %SUPPORT_URL% + +Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została uruchomiona i jest +gotowa do użycia. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Zakończono maszynę wirtualną lub kontener +Defined-By: systemd +Support: %SUPPORT_URL% + +Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona. + +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer +DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona. + +To zdarzenie będzie miało miejsce, jeśli skonfigurowano DNSSEC=allow-downgrade +w pliku resolved.conf, a skonfigurowany serwer DNS jest niezgodny z DNSSEC. +Proszę zauważyć, że używanie tego trybu umożliwia ataki wyłączające DNSSEC, +ponieważ atakujący będzie mógł wyłączyć walidację DNSSEC na komputerze przez +umieszczenie odpowiednich odpowiedzi DNS w kanale komunikacji. + +To zdarzenie może wskazywać, że serwer DNS jest faktycznie niezgodny z DNSSEC, +albo że atakującemu udało się upozorować atak tego typu. + +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: Walidacja DNSSEC się nie powiodła +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Zapytanie DNS lub ustawiony wpis zasobu nie przeszedł walidacji DNSSEC. +Zwykle wskazuje to, że ktoś manipulował używanym kanałem komunikacji. + +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Unieważniono kotwicę zaufania DNSSEC +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Kotwica zaufania DNSSEC została unieważniona. Należy skonfigurować nową, albo +system operacyjny musi zostać zaktualizowany, aby dostarczyć zaktualizowaną +kotwicę zaufania DNSSEC. diff --git a/src/grp-journal/catalog/systemd.pt_BR.catalog.in b/src/grp-journal/catalog/systemd.pt_BR.catalog.in new file mode 100644 index 0000000000..e461c2b2ba --- /dev/null +++ b/src/grp-journal/catalog/systemd.pt_BR.catalog.in @@ -0,0 +1,264 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2015 Rafael Ferreira (translation) +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Catálogo de mensagens para as mensagens do próprio systemd + +# O formato do catálogo está documentado em +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# Para uma explicação do porquê de fazermos tudo isso, veja +# https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: O jornal foi inciado +Defined-By: systemd +Support: %SUPPORT_URL% + +O processo jornal do sistema foi iniciado, arquivos foram abertos e está +pronto para processar requisições. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: O jornal foi interrompido +Defined-By: systemd +Support: %SUPPORT_URL% + +O processo do jornal do sistema foi desligado e todos os arquivos de jornal +do sistema foram fechados. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Mensagens de um serviço foram suprimidas +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Um serviço registrou no log um número excessivo de mensagens dentro de um +período de tempo. Mensagens do serviço foram descartadas. + +Note que apenas mensagens de um serviço em questão foram descartadas; outras +mensagens dos serviços não foram afetadas. + +Os controles de limites de quando as mensagens são descartadas pode ser +configurado com RateLimitIntervalSec= e RateLimitBurst= no +/etc/systemd/journald.conf. Veja journald.conf(5) para detalhes. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Mensagens do jornal foram perdidas +Defined-By: systemd +Support: %SUPPORT_URL% + +Mensagens do kernel foram perdidas pois o sistema do jornal não pôde +processá-las em velocidade suficiente para a demanda. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Processo @COREDUMP_PID@ (@COREDUMP_COMM@) despejou núcleo +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Processo @COREDUMP_PID@ (@COREDUMP_COMM@) travou e despejou o núcleo. + +Isso normalmente indica um erro de programação no programa que travou e +deveria ser relatado para seu fabricante como um erro. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: A nova sessão @SESSION_ID@ foi criada para usuário o @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Uma nova sessão com o ID @SESSION_ID@ foi criada para o usuário @USER_ID@. + +O processo originador da sessão é @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Sessão @SESSION_ID@ foi terminada +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Um sessão com o ID @SESSION_ID@ foi terminada. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Um novo seat @SEAT_ID@ está disponível +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Um novo seat @SEAT_ID@ foi configurado e está disponível. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Seat @SEAT_ID@ foi removido agora +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Um seat @SEAT_ID@ foi removido e não está mais disponível. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Time change +Defined-By: systemd +Support: %SUPPORT_URL% + +O relógio do sistema foi alterado para @REALTIME@ microssegundos após 1º de +janeiro de 1970. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Fuso horário alterado para @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +O fuso horário do sistema foi alterado para @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Inicialização do sistema foi concluída +Defined-By: systemd +Support: %SUPPORT_URL% + +Todos os serviços do sistema necessários que estão enfileirados para +executar na inicialização do sistema, foram iniciados com sucesso. Note +que isso não significa que a máquina está ociosa, pois os serviços podem +ainda estar ocupados com a inicialização completa. + +Inicialização do kernel precisou @KERNEL_USEC@ microssegundos. + +Disco de RAM inicial precisou de @INITRD_USEC@ microssegundos. + +Inicialização do espaço do usuário precisou de @USERSPACE_USEC@ microssegundos. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Estado de suspensão do sistema @SLEEP@ iniciado +Defined-By: systemd +Support: %SUPPORT_URL% + +O sistema entrou agora no estado de suspensão @SLEEP@. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Estado de suspensão do sistema @SLEEP@ finalizado +Defined-By: systemd +Support: %SUPPORT_URL% + +O sistema saiu agora do estado de suspensão @SLEEP@. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Desligamento do sistema iniciado +Defined-By: systemd +Support: %SUPPORT_URL% + +Desligamento do sistema foi inicializado. O desligamento se iniciou e todos +os serviços do sistema foram terminados e todos os sistemas desmontados. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Unidade @UNIT@ sendo iniciado +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ está sendo iniciada. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Unidade @UNIT@ concluiu a inicialização +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ concluiu a inicialização. + +The start-up result is @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Unidade @UNIT@ sendo desligado +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ está sendo desligada. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: A unidade @UNIT@ concluiu o desligamento +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ concluiu o desligamento. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: A unidade @UNIT@ falhou +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ falhou. + +O resultado é @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ iniciou o recarregamento de sua configuração. + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Unidade @UNIT@ concluiu recarregamento de sua configuração +Defined-By: systemd +Support: %SUPPORT_URL% + +A unidade @UNIT@ concluiu o recarregamento de sua configuração. + +O resultado é @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Processo @EXECUTABLE@ não pôde ser executado +Defined-By: systemd +Support: %SUPPORT_URL% + +O processo @EXECUTABLE@ não pôde ser executado e falhou. + +O número de erro retornado por este processo é @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Uma ou mais mensagens não puderam ser encaminhadas para o syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Uma ou mais mensagens não puderam ser encaminhadas para o serviço do syslog +em execução paralela ao journald. Isso normalmente indica que a implementação +do syslog não foi capaz de se manter com a velocidade das mensagens +enfileiradas. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Ponto de montagem não está vazio +Defined-By: systemd +Support: %SUPPORT_URL% + +O diretório @WHERE@ está especificado como ponto de montagem (o segundo campo +no /etc/fstab ou campo Where= no arquivo de unidade do systemd) e não está +vazio. Isso não interfere com a montagem, mas os arquivos pré-existentes +neste diretório se tornaram inacessívels. Para ver aqueles arquivos, sobre os +quais foi realizada a montagem, por favor monte manualmente o sistema de +arquivos subjacente para uma localização secundária. + +-- 24d8d4452573402496068381a6312df2 +Subject: Uma máquina virtual ou contêiner foi iniciado +Defined-By: systemd +Support: %SUPPORT_URL% + +A máquina virtual @NAME@ com seu PID @LEADER@ incial foi iniciada e está +pronto para ser usad. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Uma máquina virtual ou contêiner foi terminado +Defined-By: systemd +Support: %SUPPORT_URL% + +A máquina virtual @NAME@ com seu PID @LEADER@ incial foi desligada. diff --git a/src/grp-journal/catalog/systemd.ru.catalog.in b/src/grp-journal/catalog/systemd.ru.catalog.in new file mode 100644 index 0000000000..df55478592 --- /dev/null +++ b/src/grp-journal/catalog/systemd.ru.catalog.in @@ -0,0 +1,354 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2013-2016 Sergey Ptashnick +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Russian translation + +# Формат каталога сообщений описан по ссылке +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# Перед каждым элементом в комментарии указан Subject исходного +# сообщения (на английском). + +# Subject: The Journal has been started +-- f77379a8490b408bbe5f6940505a777b +Subject: Запущена служба журналирования +Defined-By: systemd +Support: %SUPPORT_URL% + +Процесс, отвечающий за журналирование системных событий, успешно запустился, +открыл для записи файлы журнала, и готов обрабатывать запросы. + +# Subject: The Journal has been stopped +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Служба журналирования остановлена +Defined-By: systemd +Support: %SUPPORT_URL% + +Процесс, отвечающий за журналирование системных событий, завершил работу и +закрыл все свои файлы. + +# Subject: Disk space used by the journal +-- ec387f577b844b8fa948f33cad9a75e6 +Subject: Место на диске, занятое журналом +Defined-By: systemd +Support: %SUPPORT_URL% + +@JOURNAL_NAME@ (@JOURNAL_PATH@) сейчас занимает @CURRENT_USE_PRETTY@. +Максимальный разрешенный размер составляет @MAX_USE_PRETTY@. +Оставляем свободными как минимум @DISK_KEEP_FREE_PRETTY@ (сейчас на диске +свободно @DISK_AVAILABLE_PRETTY@). +Таким образом, предел использования составляет @LIMIT_PRETTY@, из которых +@AVAILABLE_PRETTY@ пока свободно. + +Ограничения на размер журнала настраиваются при помощи параметров +SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, +RuntimeKeepFree=, RuntimeMaxFileSize= в файле /etc/systemd/journald.conf. +Более подробные сведения вы можете получить на справочной странице +journald.conf(5). + +# Subject: Messages from a service have been suppressed +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Часть сообщений от службы пропущена +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Служба отправила слишком много сообщений за короткий промежуток времени. +Часть сообщений была пропущена. + +Обратите внимание, что были пропущены сообщения только от этой службы, +сообщения других служб не затронуты. + +Предел, после которого служба журнала начинает игнорировать сообщения, +настраивается параметрами RateLimitIntervalSec= и RateLimitBurst= в файле +/etc/systemd/journald.conf. Подробности смотрите на странице руководства +journald.conf(5). + +# Subject: Journal messages have been missed +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Часть сообщений ядра пропущена +Defined-By: systemd +Support: %SUPPORT_URL% + +Часть сообщений, поступивших от ядра, была потеряна, так как служба +журналирования не успела их обработать. + +# Subject: Process @COREDUMP_PID@ (@COREDUMP_COMM@) dumped core +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Процесс @COREDUMP_PID@ (@COREDUMP_COMM@) сбросил дамп памяти +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Процесс @COREDUMP_PID@ (@COREDUMP_COMM@) завершился из-за критической ошибки. +Записан дамп памяти. + +Вероятно, это произошло из-за ошибки, допущенной в коде программы. +Рекомендуется сообщить её разработчикам о возникшей проблеме. + +# Subject: A new session @SESSION_ID@ has been created for user @USER_ID@ +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Для пользователя @USER_ID@ создан новый сеанс @SESSION_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Для пользователя @USER_ID@ создан новый сеанс с идентификатором @SESSION_ID@. + +Главный процесс нового сеанса имеет индентификатор @LEADER@. + +# Subject: A session @SESSION_ID@ has been terminated +-- 3354939424b4456d9802ca8333ed424a +Subject: Сеанс @SESSION_ID@ завершен +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Сеанс с идентификатором @SESSION_ID@ завершился. + +# Subject: A new seat @SEAT_ID@ is now available +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Добавлено новое рабочее место @SEAT_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Новое рабочее место (seat) @SEAT_ID@ полностью настроено и готово к +использованию. + +# Subject: A seat @SEAT_ID@ has now been removed +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Рабочее место @SEAT_ID@ отключено +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Рабочее место (seat) @SEAT_ID@ было отключено. + +# Subject: Time change +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Переведены системные часы +Defined-By: systemd +Support: %SUPPORT_URL% + +Системные часы были переведены. Сейчас они показывают @REALTIME@ микросекунд +с момента 00:00:00 1 января 1970 года. + +# Subject: Time zone change to @TIMEZONE@ +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Часовой пояс изменен на @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Системный часовой пояс был изменен. Новое значение: @TIMEZONE@. + +# Subject: System start-up is now complete +-- b07a249cd024414a82dd00cd181378ff +Subject: Запуск системы завершен +Defined-By: systemd +Support: %SUPPORT_URL% + +Все системные службы, запуск которых предписан настройками, были запущены. +Впрочем, это ещё не означает, что система в данный момент ничем не занята, +так как некоторые службы могут продолжать инициализацию даже после того, как +отчитались о своем запуске. + +Запуск ядра занял @KERNEL_USEC@ микросекунд. + +Процессы начального RAM-диска (initrd) отработали за @INITRD_USEC@ микросекунд. + +Запуск системных служб занял @USERSPACE_USEC@ микросекунд. + +# Subject: System sleep state @SLEEP@ entered +-- 6bbd95ee977941e497c48be27c254128 +Subject: Система перешла в состояние сна (@SLEEP@) +Defined-By: systemd +Support: %SUPPORT_URL% + +Система была переведена в состояние сна (@SLEEP@). + +# Subject: System sleep state @SLEEP@ left +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Система вышла из состояния сна (@SLEEP@) +Defined-By: systemd +Support: %SUPPORT_URL% + +Система была выведена из состояния сна (@SLEEP@). + +# Subject: System shutdown initiated +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Подготовка системы к выключению +Defined-By: systemd +Support: %SUPPORT_URL% + +Начат процесс подготовки к выключению компьютера. Останавливаются все системные +службы, отмонтируются все файловые системы. + +# Subject: Unit @UNIT@ has begun with start-up +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Начинается запуск юнита @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Начат процесс запуска юнита @UNIT@. + +# Subject: Unit @UNIT@ has finished start-up +-- 39f53479d3a045ac8e11786248231fbf +Subject: Запуск юнита @UNIT@ завершен +Defined-By: systemd +Support: %SUPPORT_URL% + +Процесс запуска юнита @UNIT@ был завершен. + +Результат: @RESULT@. + +# Subject: Unit @UNIT@ has begun shutting down +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Начинается остановка юнита @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Начат процесс остановки юнита @UNIT@. + +# Subject: Unit @UNIT@ has finished shutting down +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Завершена остановка юнита @UNIT@. +Defined-By: systemd +Support: %SUPPORT_URL% + +Процесс остановки юнита @UNIT@ был завершен. + +# Subject: Unit @UNIT@ has failed +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Ошибка юнита @UNIT@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Произошел сбой юнита @UNIT@. + +Результат: @RESULT@. + +# Subject: Unit @UNIT@ has begun with reloading its configuration +-- d34d037fff1847e6ae669a370e694725 +Subject: Юнит @UNIT@ начал перечитывать свои настройки +Defined-By: systemd +Support: %SUPPORT_URL% + +Юнит @UNIT@ начал процесс перечитывания своей конфигурации. + +# Subject: Unit @UNIT@ has finished reloading its configuration +-- 7b05ebc668384222baa8881179cfda54 +Subject: Юнит @UNIT@ завершил перечитывание своих настроек +Defined-By: systemd +Support: %SUPPORT_URL% + +Юнит @UNIT@ завершил процесс перечитывания своей конфигурации. + +Результат: @RESULT@. + +# Subject: Process @EXECUTABLE@ could not be executed +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Не удалось запустить процесс @EXECUTABLE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Сбой: не удалось запустить процесс @EXECUTABLE@. + +Код ошибки: @ERRNO@. + +# Subject: One or more messages could not be forwarded to syslog +-- 0027229ca0644181a76c4e92458afa2e +Subject: Часть сообщений не удалось передать процессу syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +Не удалось передать некоторые сообщения демону системного лога (syslog), +дублирующему работу службы системного журнала. Скорее всего, причина в том, что +используемая реализация syslog не успевает обрабатывать сообщения с достаточной +скоростью. + +# Subject: Mount point is not empty +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Каталог, являющийся точкой монтирования, не пуст +Defined-By: systemd +Support: %SUPPORT_URL% + +Каталог @WHERE@, который был указан в качестве точки монтирования (во втором +столбце файла /etc/fstab, либо в параметре Where= файла конфигурации юнита), +не является пустым. Это никак не мешает монтированию, однако ранее находившиеся +в нем файлы будут недоступны. Чтобы получить к ним доступ, вы можете вручную +перемонтировать эту файловую систему в другую точку. + +# Subject: A virtual machine or container has been started +-- 24d8d4452573402496068381a6312df2 +Subject: Запущена виртуальная машина/контейнер +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуальная машина @NAME@ (идентификатор главного процесса: @LEADER@) запущена и +готова к работе. + +# Subject: A virtual machine or container has been terminated +-- 58432bd3bace477cb514b56381b8a758 +Subject: Остановлена виртуальная машина/контейнер +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуальная машина @NAME@ (идентификатор главного процесса: @LEADER@) выключена. + +# Subject: DNSSEC mode has been turned off, as server doesn't support it +-- 36db2dfa5a9045e1bd4af5f93e1cf057 +Subject: Механизм DNSSEC был отключен, так как DNS-сервер его не поддерживает +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) resolved.conf(5) + +Служба разрешения имен хостов (systemd-resolved.service) определила, что +указанный в настойках DNS-сервер не поддерживает технологию DNSSEC, и +автоматически отключила DNSSEC-проверки. + +Данное событие возникает, если в файле resolved.conf указан параметр +DNSSEC=allow-downgrade, и вышестоящий DNS-сервер не поддерживает DNSSEC. +Обратите внимание, что режим allow-downgrade допускает возможность атаки +"DNSSEC downgrade", в ходе которой атакующий хакер блокирует проверки DNSSEC +путем отправки ложных сообщений от имени DNS-сервера. + +Возникновение данного события может свидетельствовать как о том, что ваш +DNS-сервер не поддерживает DNSSEC, так и о том, что некий хакер успешно провел +против вас атаку, направленную на блокировку DNSSEC-проверок. + +# Subject: DNSSEC validation failed +-- 1675d7f172174098b1108bf8c7dc8f5d +Subject: Проверка DNSSEC провалена +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +DNS-запрос или отдельная ресурсная запись не прошла проверку DNSSEC. +Как правило, это свидетельствует о постороннем вмешательстве в канал связи. + +# Subject: A DNSSEC trust anchor has been revoked +-- 4d4408cfd0d144859184d1e65d7c8a65 +Subject: Открытый ключ DNSSEC был отозван +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-resolved.service(8) + +Открытый ключ (trust ahcnor) DNSSEC был отозван. Необходимо настроить новый +открытый ключ, либо обновить систему, чтобы получить обновленный открытый ключ. diff --git a/src/grp-journal/catalog/systemd.sr.catalog.in b/src/grp-journal/catalog/systemd.sr.catalog.in new file mode 100644 index 0000000000..06a0ff648c --- /dev/null +++ b/src/grp-journal/catalog/systemd.sr.catalog.in @@ -0,0 +1,262 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Serbian translation + +# Формат каталога је документован на +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# Да бисте видели зашто ово радимо, погледајте https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: Журнал је покренут +Defined-By: systemd +Support: %SUPPORT_URL% + +Системски журналски процес се покренуо, отворио журналске +датотеке за упис и спреман је за обраду захтева. + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: Журнал је заустављен +Defined-By: systemd +Support: %SUPPORT_URL% + +Системски журналски процес се зауставио и затворио све тренутно +отворене журналске датотеке. + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: Поруке од услуге су утишане +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +Услуга је уписала сувише порука за једно време. Поруке +од услуге су одбачене. + +Знајте да су само поруке од ове услуге одбачене, друге +услуге нису захваћене овим. + +Ограничења која подешавају начин на који се поруке одбацују се могу подесити +помоћу „RateLimitIntervalSec=“ и „RateLimitBurst=“ параметара унутар датотеке +/etc/systemd/journald.conf. Погледајте journald.conf(5) за појединости. + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: Журналске поруке су изгубљене +Defined-By: systemd +Support: %SUPPORT_URL% + +Поруке кернела су изгубљене јер журналски систем није могао да их +обради довољно брзо. + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: Процес @COREDUMP_PID@ (@COREDUMP_COMM@) је избацио своје језгро +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +Процес @COREDUMP_PID@ (@COREDUMP_COMM@) је пао и избацио своје језгро. + +Ово обично значи да постоји грешка у програму који је пао и ова +грешка треба да се пријави продавцу. + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: Нова сесија @SESSION_ID@ је направљена за корисника @USER_ID@ +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Нова сесија са ИБ-ом @SESSION_ID@ је направљена за корисника @USER_ID@. + +Водећи процес сесије је @LEADER@. + +-- 3354939424b4456d9802ca8333ed424a +Subject: Сесија @SESSION_ID@ је окончана +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Сесија са ИБ-ом @SESSION_ID@ је окончана. + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: Ново седиште @SEAT_ID@ је сада доступно +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Ново седиште @SEAT_ID@ је исподешавано и сада је доступно. + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: Седиште @SEAT_ID@ је сада уклоњено +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +Седиште @SEAT_ID@ је сада уклоњено и више није доступно. + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: Време је промењено +Defined-By: systemd +Support: %SUPPORT_URL% + +Системски сат је сада подешен на @REALTIME@ микросекунде након 1. јануара 1970. године. + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: Временска зона је промењена на @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +Временска зона је промењена на @TIMEZONE@. + +-- b07a249cd024414a82dd00cd181378ff +Subject: Подизање система је сада готово +Defined-By: systemd +Support: %SUPPORT_URL% + +Све системске услуге које су заказане за подизање су успешно покренуте. +Знајте да ово не значи да је машина сада беспослена јер услуге могу +и даље бити заузете завршавањем покретања система. + +Подизање кернела је трајало @KERNEL_USEC@ микросекунде. + +Подизање почетног РАМ диска је трајало @INITRD_USEC@ микросекунде. + +Подизање корисничких програма је трајало @USERSPACE_USEC@ микросекунде. + +-- 6bbd95ee977941e497c48be27c254128 +Subject: Системско стање спавања @SLEEP@ започето +Defined-By: systemd +Support: %SUPPORT_URL% + +Систем је сада ушао у @SLEEP@ стање спавања. + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: Системско стање спавања @SLEEP@ напуштено +Defined-By: systemd +Support: %SUPPORT_URL% + +Систем је изашао из @SLEEP@ стања спавања. + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: Гашење система започето +Defined-By: systemd +Support: %SUPPORT_URL% + +Систем-де гашење је започето. Гашење је сада почело и све +системске услуге су окончане и сви системи датотека откачени. + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: Јединица @UNIT@ је почела са покретањем +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је почела са покретањем. + +-- 39f53479d3a045ac8e11786248231fbf +Subject: Јединица @UNIT@ је завршила са покретањем +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је завршила са покретањем. + +Исход покретања је @RESULT@. + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: Јединица @UNIT@ је почела са гашењем +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је почела са гашењем. + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: Јединица @UNIT@ је завршила са гашењем +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је завршила са гашењем. + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: Јединица @UNIT@ је пукла +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је пукла. + +Исход је @RESULT@. + +-- d34d037fff1847e6ae669a370e694725 +Subject: Јединица @UNIT@ је почела са поновним учитавањем свог подешавања +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је почела са поновним учитавањем свог подешавања + +-- 7b05ebc668384222baa8881179cfda54 +Subject: Јединица @UNIT@ је завршила са поновним учитавањем свог подешавања +Defined-By: systemd +Support: %SUPPORT_URL% + +Јединица @UNIT@ је завршила са поновним учитавањем свог подешавања + +Исход је @RESULT@. + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: Процес @EXECUTABLE@ није могао бити извршен +Defined-By: systemd +Support: %SUPPORT_URL% + +Процес @EXECUTABLE@ није могао бити извршен и пукао је. + +Овај процес је вратио број грешке @ERRNO@. + +-- 0027229ca0644181a76c4e92458afa2e +Subject: Једна или више порука није могло бити прослеђено системском записнику +Defined-By: systemd +Support: %SUPPORT_URL% + +Једна или више порука није могло бити прослеђено „syslog“ услузи +која ради упоредно са журнал-деом. Ово обично значи да спроведена +„syslog“ услуга није могла да издржи брзину свих надолазећих +порука у реду. + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: Тачка качења није празна +Defined-By: systemd +Support: %SUPPORT_URL% + +Директоријум @WHERE@ је наведен као тачка качења (друго поље у +/etc/fstab датотеци или у „Where=“ пољу систем-де јединичне датотеке) +и он није празан. Ово не утиче на качење али ће већ постојеће датотеке у +овом директоријуму постати недоступне. Да бисте видели ове недоступне +датотеке, ручно прикачите основни систем датотека у другу +путању. + +-- 24d8d4452573402496068381a6312df2 +Subject: Виртуелна машина или контејнер је покренут(а) +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуелна машина @NAME@ са водећим ПИБ-ом @LEADER@ је +покренута и сада је спремна за коришћење. + +-- 58432bd3bace477cb514b56381b8a758 +Subject: Виртуелна машина или контејнер је окончан(а) +Defined-By: systemd +Support: %SUPPORT_URL% + +Виртуелна машина @NAME@ са водећим ПИБ-ом @LEADER@ је +угашена. diff --git a/src/grp-journal/catalog/systemd.zh_CN.catalog.in b/src/grp-journal/catalog/systemd.zh_CN.catalog.in new file mode 100644 index 0000000000..ba7c697c16 --- /dev/null +++ b/src/grp-journal/catalog/systemd.zh_CN.catalog.in @@ -0,0 +1,253 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2015 Boyuan Yang +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Simplified Chinese translation + +# 本 catalog 文档格式被记载在 +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# 如需了解我们为什么做这些工作,请见 https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: 日志已开始 +Defined-By: systemd +Support: %SUPPORT_URL% + +系统日志进程已启动,已打开供写入的日志文件并准备好处理请求。 + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: 日志已停止 +Defined-By: systemd +Support: %SUPPORT_URL% + +系统日志进程已终止,并已关闭所有当前活动的日志文件。 + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: 由某个服务而来的消息已被抑制 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +某个服务在一个时间周期内记录了太多消息。 +从该服务而来的消息已被丢弃。 + +请注意只有由有问题的服务传来的消息被丢弃, +其它服务的消息不受影响。 + +可以在 /etc/systemd/journald.conf 中设定 RateLimitIntervalSec= +以及 RateLimitBurst = 的值以控制丢弃信息的限制。 +请参见 journald.conf(5) 以了解详情。 + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: 日志消息已遗失 +Defined-By: systemd +Support: %SUPPORT_URL% + +因日志系统对内核消息的处理速度不够快, +部分信息已经遗失。 + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: 进程 @COREDUMP_PID@ (@COREDUMP_COMM@) 核心已转储 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +进程 @COREDUMP_PID@ (@COREDUMP_COMM@) 已崩溃并进行核心转储。 + +这通常意味着崩溃程序中存在编程错误,并应当将此错误向其开发者报告。 + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: 一个新会话 @SESSION_ID@ 已为用户 @USER_ID@ 建立 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一个 ID 为 @SESSION_ID@ 的新会话已为用户 @USER_ID@ 建立。 + +该会话的首进程为 @LEADER@。 + +-- 3354939424b4456d9802ca8333ed424a +Subject: 会话 @SESSION_ID@ 已终止 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一个 ID 为 @SESSION_ID@ 的会话已终止。 + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: 一个新的座位 @SEAT_ID@ 可用 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一个新的座位 @SEAT_ID@ 已被配置并已可用。 + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: 座位 @SEAT_ID@ 已被移除 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +座位 @SEAT_ID@ 已被移除并不再可用。 + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: 时间已变更 +Defined-By: systemd +Support: %SUPPORT_URL% + +系统时钟已变更为1970年1月1日后 @REALTIME@ 微秒。 + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: 时区变更为 @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +系统时区已变更为 @TIMEZONE@。 + +-- b07a249cd024414a82dd00cd181378ff +Subject: 系统启动已完成 +Defined-By: systemd +Support: %SUPPORT_URL% + +所有系统启动时需要的系统服务均已成功启动。 +请注意这并不代表现在机器已经空闲,因为某些服务可能仍处于完成启动的过程中。 + +内核启动使用了 @KERNEL_USEC@ 毫秒。 + +初始内存盘启动使用了 @INITRD_USEC@ 毫秒。 + +用户空间启动使用了 @USERSPACE_USEC@ 毫秒。 + +-- 6bbd95ee977941e497c48be27c254128 +Subject: 系统已进入 @SLEEP@ 睡眠状态 +Defined-By: systemd +Support: http://lists.freedesktop.org/mailman/listinfo/systemd-deve + +系统现已进入 @SLEEP@ 睡眠状态。 + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: 系统已离开 @SLEEP@ 睡眠状态 +Defined-By: systemd +Support: %SUPPORT_URL% + +系统现已离开 @SLEEP@ 睡眠状态。 + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: 系统关机已开始 +Defined-By: systemd +Support: %SUPPORT_URL% + +系统关机操作已初始化。 +关机已开始,所有系统服务均已结束,所有文件系统已卸载。 + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: @UNIT@ 单元已开始启动 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已开始启动。 + +-- 39f53479d3a045ac8e11786248231fbf +Subject: @UNIT@ 单元已结束启动 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已结束启动。 + +启动结果为“@RESULT@”。 + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: @UNIT@ 单元已开始停止操作 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已开始停止操作。 + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: @UNIT@ 单元已结束停止操作 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已结束停止操作。 + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: @UNIT@ 单元已失败 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已失败。 + +结果为“@RESULT@”。 + +-- d34d037fff1847e6ae669a370e694725 +Subject: @UNIT@ 单元已开始重新载入其配置 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已开始重新载入其配置。 + +-- 7b05ebc668384222baa8881179cfda54 +Subject: @UNIT@ 单元已结束配置重载入 +Defined-By: systemd +Support: %SUPPORT_URL% + +@UNIT@ 单元已结束配置重载入操作。 + +结果为“@RESULT@”。 + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: 进程 @EXECUTABLE@ 无法执行 +Defined-By: systemd +Support: %SUPPORT_URL% + +进程 @EXECUTABLE@ 无法被执行并已失败。 + +该进程返回的错误代码为 @ERRNO@。 + +-- 0027229ca0644181a76c4e92458afa2e +Subject: 一个或更多消息无法被转发至 syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +有一条或更多的消息无法被转发至与 journald 同时运行的 syslog 服务。 +这通常意味着 syslog 实现无法跟上队列中消息进入的速度。 + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: 挂载点不为空 +Defined-By: systemd +Support: %SUPPORT_URL% + +目录 @WHERE@ 被指定为挂载点(即 /etc/fstab 文件的第二栏,或 systemd 单元 +文件的 Where= 字段),且该目录非空。 +这并不会影响挂载行为,但该目录中先前已存在的文件将无法被访问。 +如需查看这些文件,请手动将其下的文件系统挂载到另一个位置。 + +-- 24d8d4452573402496068381a6312df2 +Subject: 一个虚拟机或容器已启动 +Defined-By: systemd +Support: %SUPPORT_URL% + +虚拟机 @NAME@,以及其首进程 PID @LEADER@,已被启动并可被使用。 + +-- 58432bd3bace477cb514b56381b8a758 +Subject: 一个虚拟机或容器已被终止 +Defined-By: systemd +Support: %SUPPORT_URL% + +虚拟机 @NAME@,以及其首进程 PID @LEADER@,已被关闭并停止。 diff --git a/src/grp-journal/catalog/systemd.zh_TW.catalog.in b/src/grp-journal/catalog/systemd.zh_TW.catalog.in new file mode 100644 index 0000000000..f7b42fa1c7 --- /dev/null +++ b/src/grp-journal/catalog/systemd.zh_TW.catalog.in @@ -0,0 +1,263 @@ +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# Copyright 2015 Jeff Huang +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# Message catalog for systemd's own messages +# Traditional Chinese translation + +# Catalog 的格式記錄於 +# http://www.freedesktop.org/wiki/Software/systemd/catalog + +# For an explanation why we do all this, see https://xkcd.com/1024/ + +-- f77379a8490b408bbe5f6940505a777b +Subject: 日誌已開始 +Defined-By: systemd +Support: %SUPPORT_URL% + +系統日誌行程已啟動,已開啟日誌 +檔案供寫入並準備好對行程的要求做出回應。 + +-- d93fb3c9c24d451a97cea615ce59c00b +Subject: 日誌已停止 +Defined-By: systemd +Support: %SUPPORT_URL% + +系統日誌行程已關閉,且關閉所有目前 +活躍的日誌檔案。 + +-- a596d6fe7bfa4994828e72309e95d61e +Subject: 從服務而來的訊息已被抑制 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:journald.conf(5) + +有一個服務在一個時間週期內記錄了太多訊息。 +從該服務而來的訊息已被丟棄。 + +注意,只有有問題的服務之訊息被丟棄, +其他服務的訊息則不受影響。 + +可以在 /etc/systemd/journald.conf 中設定 +RateLimitIntervalSec= 以及 RateLimitBurst= +來控制當訊息要開始被丟棄時的限制。參見 journald.conf(5) 以獲得更多資訊。 + +-- e9bf28e6e834481bb6f48f548ad13606 +Subject: 日誌訊息已遺失 +Defined-By: systemd +Support: %SUPPORT_URL% + +因日誌系統對核心訊息的處理不夠快速, +部份訊息已遺失。 + +-- fc2e22bc6ee647b6b90729ab34a250b1 +Subject: 行程 @COREDUMP_PID@ (@COREDUMP_COMM@) 核心傾印 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:core(5) + +行程 @COREDUMP_PID@ (@COREDUMP_COMM@) 當掉並核心傾印。 + +這通常代表了在當掉的程式中的一個程式錯誤 +並需要回報錯誤給其開發者。 + +-- 8d45620c1a4348dbb17410da57c60c66 +Subject: 新的工作階段 @SESSION_ID@ 已為使用者 @USER_ID@ 建立 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一個新的工作階段,ID @SESSION_ID@ 已為使用者 @USER_ID@ 建立。 + +這個工作階段的領導行程為 @LEADER@。 + +-- 3354939424b4456d9802ca8333ed424a +Subject: 工作階段 @SESSION_ID@ 已結束 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一個工作階段,ID @SESSION_ID@ 已結束。 + +-- fcbefc5da23d428093f97c82a9290f7b +Subject: 新的座位 @SEAT_ID@ 可用 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +一個新的座位 @SEAT_ID@ 已被設定且現在可用。 + +-- e7852bfe46784ed0accde04bc864c2d5 +Subject: 座位 @SEAT_ID@ 已被移除 +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat + +座位 @SEAT_ID@ 已被移除且不再可用。 + +-- c7a787079b354eaaa9e77b371893cd27 +Subject: 時間變更 +Defined-By: systemd +Support: %SUPPORT_URL% + +系統時間已變更為1970年1月1日後 @REALTIME@ 微秒。 + +-- 45f82f4aef7a4bbf942ce861d1f20990 +Subject: 時區變更為 @TIMEZONE@ +Defined-By: systemd +Support: %SUPPORT_URL% + +系統時區已變更為 @TIMEZONE@。 + +-- b07a249cd024414a82dd00cd181378ff +Subject: 系統啟動已完成 +Defined-By: systemd +Support: %SUPPORT_URL% + +所有開機所必要的系統服務都已成功啟動。 +注意這並不代表這臺機器有空閒的時間 +可以服務,可能仍忙於完成啟動。 + +核心啟動需要 @KERNEL_USEC@ 微秒。 + +初始 RAM 磁碟啟動需要 @INITRD_USEC@ 微秒。 + +使用者空間啟動需要 @USERSPACE_USEC@ 微秒。 + +-- 6bbd95ee977941e497c48be27c254128 +Subject: 系統進入 @SLEEP@ 睡眠狀態 +Defined-By: systemd +Support: %SUPPORT_URL% + +系統現在已進入 @SLEEP@ 睡眠狀態。 + +-- 8811e6df2a8e40f58a94cea26f8ebf14 +Subject: 系統離開 @SLEEP@ 睡眠狀態 +Defined-By: systemd +Support: %SUPPORT_URL% + +系統現在已離開 @SLEEP@ 睡眠狀態。 + +-- 98268866d1d54a499c4e98921d93bc40 +Subject: 系統關機開始 +Defined-By: systemd +Support: %SUPPORT_URL% + +Systemd 關閉已經開始。關閉已開始且所有系統服務 +都已結束,所有的檔案系統也都已被卸載。 + +-- 7d4958e842da4a758f6c1cdc7b36dcc5 +Subject: 單位 @UNIT@ 已開始啟動 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已開始啟動。 + +-- 39f53479d3a045ac8e11786248231fbf +Subject: 單位 @UNIT@ 啟動已結束 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 啟動已結束。 + +啟動結果為 @RESULT@。 + +-- de5b426a63be47a7b6ac3eaac82e2f6f +Subject: 單位 @UNIT@ 已開始關閉 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已開始關閉。 + +-- 9d1aaa27d60140bd96365438aad20286 +Subject: 單位 @UNIT@ 已關閉結束 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已關閉結束。 + +-- be02cf6855d2428ba40df7e9d022f03d +Subject: 單位 @UNIT@ 已失敗 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已失敗。 + +結果為 @RESULT@。 + +-- d34d037fff1847e6ae669a370e694725 +Subject: 單位 @UNIT@ 已開始重新載入其設定 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已開始重新載入其設定 + +-- 7b05ebc668384222baa8881179cfda54 +Subject: 單位 @UNIT@ 已結束重新載入其設定 +Defined-By: systemd +Support: %SUPPORT_URL% + +單位 @UNIT@ 已結束重新載入其設定 + +結果為 @RESULT@。 + +-- 641257651c1b4ec9a8624d7a40a9e1e7 +Subject: 行程 @EXECUTABLE@ 無法執行 +Defined-By: systemd +Support: %SUPPORT_URL% + +行程 @EXECUTABLE@ 無法執行且失敗。 + +由該行程所回傳的錯誤碼為 @ERRNO@。 + +-- 0027229ca0644181a76c4e92458afa2e +Subject: 一個或更多訊息無法被轉發到 syslog +Defined-By: systemd +Support: %SUPPORT_URL% + +一個或更多訊息無法被轉發到 syslog 服務 +以及並行執行的 journald。這通常代表著 +syslog 實作並無未跟上佇列中訊息 +的速度。 + +-- 1dee0369c7fc4736b7099b38ecb46ee7 +Subject: 掛載點不為空 +Defined-By: systemd +Support: %SUPPORT_URL% + +目錄 @WHERE@ 被指定為掛載點(在 /etc/fstab 中的 +第二欄或是在 systemd 單位檔案中的 Where= 欄位)且其不為空。 +這並不會干擾掛載,但在此目錄中已存在的檔案 +會變成無法存取的狀態。要檢視這些 over-mounted 的檔案, +請手動掛載下面的檔案系統到次要 +位置。 + +-- 24d8d4452573402496068381a6312df2 +Subject: 虛擬機器或容器已啟動 +Defined-By: systemd +Support: %SUPPORT_URL% + +虛擬機器 @NAME@ 包含它的領導 PID @LEADER@ 現在 +已經開始並已經可以使用。 + +-- 58432bd3bace477cb514b56381b8a758 +Subject: 虛擬機器或容器已結束 +Defined-By: systemd +Support: %SUPPORT_URL% + +虛擬機器 @NAME@ 包含它的領導 PID @LEADER@ 已經 +關閉。 diff --git a/src/grp-journal/grp-remote/.gitignore b/src/grp-journal/grp-remote/.gitignore new file mode 100644 index 0000000000..06847b65d4 --- /dev/null +++ b/src/grp-journal/grp-remote/.gitignore @@ -0,0 +1,2 @@ +/journal-remote.conf +/journal-upload.conf diff --git a/src/grp-journal/grp-remote/Makefile b/src/grp-journal/grp-remote/Makefile new file mode 100644 index 0000000000..66e419a37e --- /dev/null +++ b/src/grp-journal/grp-remote/Makefile @@ -0,0 +1,30 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-journal-gatewayd +nested.subdirs += systemd-journal-remote +nested.subdirs += systemd-journal-upload + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/grp-remote/browse.html b/src/grp-journal/grp-remote/browse.html new file mode 100644 index 0000000000..32848c7673 --- /dev/null +++ b/src/grp-journal/grp-remote/browse.html @@ -0,0 +1,544 @@ + + + + Journal + + + + + + + +

+ +
+
+
+
+
+
+ +
+ +      + Only current boot +
+ +
+ +
+ +
+ + + + +      + + +
+ +
+ g: First Page      + ←, k, BACKSPACE: Previous Page      + →, j, SPACE: Next Page      + G: Last Page      + +: More entries      + -: Fewer entries +
+ + + + diff --git a/src/grp-journal/grp-remote/log-generator.py b/src/grp-journal/grp-remote/log-generator.py new file mode 100755 index 0000000000..fd6964e758 --- /dev/null +++ b/src/grp-journal/grp-remote/log-generator.py @@ -0,0 +1,76 @@ +#!/usr/bin/python +from __future__ import print_function +import sys +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 = """\ +__CURSOR=s=6863c726210b4560b7048889d8ada5c5;i=3e931;b=f446871715504074bf7049ef0718fa93;m={m:x};t=4fd05c +__REALTIME_TIMESTAMP={realtime_ts} +__MONOTONIC_TIMESTAMP={monotonic_ts} +_BOOT_ID=f446871715504074bf7049ef0718fa93 +_TRANSPORT=syslog +PRIORITY={priority} +SYSLOG_FACILITY={facility} +SYSLOG_IDENTIFIER=/USR/SBIN/CRON +MESSAGE={message} +_UID=0 +_GID=0 +_MACHINE_ID=69121ca41d12c1b69a7960174c27b618 +_HOSTNAME=hostname +SYSLOG_PID=25721 +_PID=25721 +_SOURCE_REALTIME_TIMESTAMP={source_realtime_ts} +DATA={data} +""" + +m = 0x198603b12d7 +realtime_ts = 1404101101501873 +monotonic_ts = 1753961140951 +source_realtime_ts = 1404101101483516 +priority = 3 +facility = 6 + +src = open('/dev/urandom', 'rb') + +bytes = 0 +counter = 0 + +for i in range(OPTIONS.n): + message = repr(src.read(2000)) + 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, + monotonic_ts=monotonic_ts, + source_realtime_ts=source_realtime_ts, + priority=priority, + facility=facility, + message=message, + data=data) + m += 1 + realtime_ts += 1 + monotonic_ts += 1 + source_realtime_ts += 1 + + bytes += len(entry) + + print(entry) + + if OPTIONS.dots: + print('.', file=sys.stderr, end='', flush=True) + +if OPTIONS.dots: + print(file=sys.stderr) +print('Wrote {} bytes'.format(bytes), file=sys.stderr) diff --git a/src/grp-journal/grp-remote/microhttpd-util.c b/src/grp-journal/grp-remote/microhttpd-util.c new file mode 100644 index 0000000000..3fc5e13b71 --- /dev/null +++ b/src/grp-journal/grp-remote/microhttpd-util.c @@ -0,0 +1,328 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_GNUTLS +#include +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "microhttpd-util.h" + +void microhttpd_logger(void *arg, const char *fmt, va_list ap) { + char *f; + + f = strjoina("microhttpd: ", fmt); + + DISABLE_WARNING_FORMAT_NONLITERAL; + log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap); + REENABLE_WARNING; +} + + +static int mhd_respond_internal(struct MHD_Connection *connection, + enum MHD_RequestTerminationCode code, + char *buffer, + size_t size, + enum MHD_ResponseMemoryMode mode) { + struct MHD_Response *response; + int r; + + assert(connection); + + response = MHD_create_response_from_buffer(size, buffer, mode); + if (!response) + return MHD_NO; + + log_debug("Queueing response %u: %s", code, buffer); + MHD_add_response_header(response, "Content-Type", "text/plain"); + r = MHD_queue_response(connection, code, response); + MHD_destroy_response(response); + + return r; +} + +int mhd_respond(struct MHD_Connection *connection, + enum MHD_RequestTerminationCode code, + const char *message) { + + return mhd_respond_internal(connection, code, + (char*) message, strlen(message), + MHD_RESPMEM_PERSISTENT); +} + +int mhd_respond_oom(struct MHD_Connection *connection) { + return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n"); +} + +int mhd_respondf(struct MHD_Connection *connection, + enum MHD_RequestTerminationCode code, + const char *format, ...) { + + char *m; + int r; + va_list ap; + + assert(connection); + assert(format); + + va_start(ap, format); + r = vasprintf(&m, format, ap); + va_end(ap); + + if (r < 0) + return respond_oom(connection); + + return mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE); +} + +#ifdef HAVE_GNUTLS + +static struct { + const char *const names[4]; + int level; + bool enabled; +} gnutls_log_map[] = { + { {"0"}, LOG_DEBUG }, + { {"1", "audit"}, LOG_WARNING, true}, /* gnutls session audit */ + { {"2", "assert"}, LOG_DEBUG }, /* gnutls assert log */ + { {"3", "hsk", "ext"}, LOG_DEBUG }, /* gnutls handshake log */ + { {"4", "rec"}, LOG_DEBUG }, /* gnutls record log */ + { {"5", "dtls"}, LOG_DEBUG }, /* gnutls DTLS log */ + { {"6", "buf"}, LOG_DEBUG }, + { {"7", "write", "read"}, LOG_DEBUG }, + { {"8"}, LOG_DEBUG }, + { {"9", "enc", "int"}, LOG_DEBUG }, +}; + +static void log_func_gnutls(int level, const char *message) { + assert_se(message); + + if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) { + if (gnutls_log_map[level].enabled) + log_internal(gnutls_log_map[level].level, 0, NULL, 0, NULL, "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message); + } else { + log_debug("Received GNUTLS message with unknown level %d.", level); + log_internal(LOG_DEBUG, 0, NULL, 0, NULL, "gnutls: %s", message); + } +} + +static void log_reset_gnutls_level(void) { + int i; + + for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--) + if (gnutls_log_map[i].enabled) { + log_debug("Setting gnutls log level to %d", i); + gnutls_global_set_log_level(i); + break; + } +} + +static int log_enable_gnutls_category(const char *cat) { + unsigned i; + + if (streq(cat, "all")) { + for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++) + gnutls_log_map[i].enabled = true; + log_reset_gnutls_level(); + return 0; + } else + for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++) + if (strv_contains((char**)gnutls_log_map[i].names, cat)) { + gnutls_log_map[i].enabled = true; + log_reset_gnutls_level(); + return 0; + } + log_error("No such log category: %s", cat); + return -EINVAL; +} + +int setup_gnutls_logger(char **categories) { + char **cat; + int r; + + gnutls_global_set_log_function(log_func_gnutls); + + if (categories) { + STRV_FOREACH(cat, categories) { + r = log_enable_gnutls_category(*cat); + if (r < 0) + return r; + } + } else + log_reset_gnutls_level(); + + return 0; +} + +static int verify_cert_authorized(gnutls_session_t session) { + unsigned status; + gnutls_certificate_type_t type; + gnutls_datum_t out; + int r; + + r = gnutls_certificate_verify_peers2(session, &status); + if (r < 0) + return log_error_errno(r, "gnutls_certificate_verify_peers2 failed: %m"); + + type = gnutls_certificate_type_get(session); + r = gnutls_certificate_verification_status_print(status, type, &out, 0); + if (r < 0) + return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m"); + + log_debug("Certificate status: %s", out.data); + gnutls_free(out.data); + + return status == 0 ? 0 : -EPERM; +} + +static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) { + const gnutls_datum_t *pcert; + unsigned listsize; + gnutls_x509_crt_t cert; + int r; + + assert(session); + assert(client_cert); + + pcert = gnutls_certificate_get_peers(session, &listsize); + if (!pcert || !listsize) { + log_error("Failed to retrieve certificate chain"); + return -EINVAL; + } + + r = gnutls_x509_crt_init(&cert); + if (r < 0) { + log_error("Failed to initialize client certificate"); + return r; + } + + /* Note that by passing values between 0 and listsize here, you + can get access to the CA's certs */ + r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER); + if (r < 0) { + log_error("Failed to import client certificate"); + gnutls_x509_crt_deinit(cert); + return r; + } + + *client_cert = cert; + return 0; +} + +static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) { + size_t len = 0; + int r; + + assert(buf); + assert(*buf == NULL); + + r = gnutls_x509_crt_get_dn(client_cert, NULL, &len); + if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) { + log_error("gnutls_x509_crt_get_dn failed"); + return r; + } + + *buf = malloc(len); + if (!*buf) + return log_oom(); + + gnutls_x509_crt_get_dn(client_cert, *buf, &len); + return 0; +} + +static inline void gnutls_x509_crt_deinitp(gnutls_x509_crt_t *p) { + gnutls_x509_crt_deinit(*p); +} + +int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { + const union MHD_ConnectionInfo *ci; + gnutls_session_t session; + _cleanup_(gnutls_x509_crt_deinitp) gnutls_x509_crt_t client_cert = NULL; + _cleanup_free_ char *buf = NULL; + int r; + + assert(connection); + assert(code); + + *code = 0; + + ci = MHD_get_connection_info(connection, + MHD_CONNECTION_INFO_GNUTLS_SESSION); + if (!ci) { + log_error("MHD_get_connection_info failed: session is unencrypted"); + *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN, + "Encrypted connection is required"); + return -EPERM; + } + session = ci->tls_session; + assert(session); + + r = get_client_cert(session, &client_cert); + if (r < 0) { + *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, + "Authorization through certificate is required"); + return -EPERM; + } + + r = get_auth_dn(client_cert, &buf); + if (r < 0) { + *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, + "Failed to determine distinguished name from certificate"); + return -EPERM; + } + + log_debug("Connection from %s", buf); + + if (hostname) { + *hostname = buf; + buf = NULL; + } + + r = verify_cert_authorized(session); + if (r < 0) { + log_warning("Client is not authorized"); + *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, + "Client certificate not signed by recognized authority"); + } + return r; +} + +#else +int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { + return -EPERM; +} + +int setup_gnutls_logger(char **categories) { + if (categories) + log_notice("Ignoring specified gnutls logging categories — gnutls not available."); + return 0; +} +#endif diff --git a/src/grp-journal/grp-remote/microhttpd-util.h b/src/grp-journal/grp-remote/microhttpd-util.h new file mode 100644 index 0000000000..178e78f892 --- /dev/null +++ b/src/grp-journal/grp-remote/microhttpd-util.h @@ -0,0 +1,60 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/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. */ +#define respond_oom(connection) log_oom(), mhd_respond_oom(connection) + +int mhd_respondf(struct MHD_Connection *connection, + unsigned code, + const char *format, ...) _printf_(3,4); + +int mhd_respond(struct MHD_Connection *connection, + unsigned code, + const char *message); + +int mhd_respond_oom(struct MHD_Connection *connection); + +int check_permissions(struct MHD_Connection *connection, int *code, char **hostname); + +/* Set gnutls internal logging function to a callback which uses our + * own logging framework. + * + * gnutls categories are additionally filtered by our internal log + * level, so it should be set fairly high to capture all potentially + * interesting events without overwhelming detail. + */ +int setup_gnutls_logger(char **categories); diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile b/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile new file mode 100644 index 0000000000..0ae96978af --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/Makefile @@ -0,0 +1,66 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_MICROHTTPD),) +gatewayddocumentrootdir=$(pkgdatadir)/gatewayd + +rootlibexec_PROGRAMS += \ + systemd-journal-gatewayd + +systemd_journal_gatewayd_SOURCES = \ + src/journal-remote/journal-gatewayd.c \ + src/journal-remote/microhttpd-util.h \ + src/journal-remote/microhttpd-util.c + +systemd_journal_gatewayd_LDADD = \ + libsystemd-shared.la \ + $(MICROHTTPD_LIBS) + +ifneq ($(HAVE_GNUTLS),) +systemd_journal_gatewayd_LDADD += \ + $(GNUTLS_LIBS) +endif # HAVE_GNUTLS + +systemd_journal_gatewayd_CFLAGS = \ + $(MICROHTTPD_CFLAGS) + +systemd_journal_gatewayd_CPPFLAGS = \ + -DDOCUMENT_ROOT=\"$(gatewayddocumentrootdir)\" + +dist_systemunit_DATA += \ + units/systemd-journal-gatewayd.socket + +nodist_systemunit_DATA += \ + units/systemd-journal-gatewayd.service + +dist_gatewayddocumentroot_DATA = \ + src/journal-remote/browse.html + +endif # HAVE_MICROHTTPD + +EXTRA_DIST += \ + units/systemd-journal-gatewayd.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/journal-gatewayd.c b/src/grp-journal/grp-remote/systemd-journal-gatewayd/journal-gatewayd.c new file mode 100644 index 0000000000..76b241a14b --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/journal-gatewayd.c @@ -0,0 +1,1077 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#ifdef HAVE_GNUTLS +#include +#endif +#include +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/sigbus.h" +#include "basic/util.h" +#include "microhttpd-util.h" +#include "shared/bus-util.h" +#include "shared/logs-show.h" + +#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC) + +static char *arg_key_pem = NULL; +static char *arg_cert_pem = NULL; +static char *arg_trust_pem = NULL; + +typedef struct RequestMeta { + sd_journal *journal; + + OutputMode mode; + + char *cursor; + int64_t n_skip; + uint64_t n_entries; + bool n_entries_set; + + FILE *tmp; + uint64_t delta, size; + + int argument_parse_error; + + bool follow; + bool discrete; + + uint64_t n_fields; + bool n_fields_set; +} RequestMeta; + +static const char* const mime_types[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "text/plain", + [OUTPUT_JSON] = "application/json", + [OUTPUT_JSON_SSE] = "text/event-stream", + [OUTPUT_EXPORT] = "application/vnd.fdo.journal", +}; + +static RequestMeta *request_meta(void **connection_cls) { + RequestMeta *m; + + assert(connection_cls); + if (*connection_cls) + return *connection_cls; + + m = new0(RequestMeta, 1); + if (!m) + return NULL; + + *connection_cls = m; + return m; +} + +static void request_meta_free( + void *cls, + struct MHD_Connection *connection, + void **connection_cls, + enum MHD_RequestTerminationCode toe) { + + RequestMeta *m = *connection_cls; + + if (!m) + return; + + sd_journal_close(m->journal); + + safe_fclose(m->tmp); + + free(m->cursor); + free(m); +} + +static int open_journal(RequestMeta *m) { + assert(m); + + if (m->journal) + return 0; + + return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); +} + +static int request_meta_ensure_tmp(RequestMeta *m) { + assert(m); + + if (m->tmp) + rewind(m->tmp); + else { + int fd; + + fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC); + if (fd < 0) + return fd; + + m->tmp = fdopen(fd, "w+"); + if (!m->tmp) { + safe_close(fd); + return -errno; + } + } + + return 0; +} + +static ssize_t request_reader_entries( + void *cls, + uint64_t pos, + char *buf, + size_t max) { + + RequestMeta *m = cls; + int r; + size_t n, k; + + assert(m); + assert(buf); + assert(max > 0); + assert(pos >= m->delta); + + pos -= m->delta; + + while (pos >= m->size) { + off_t sz; + + /* End of this entry, so let's serialize the next + * one */ + + if (m->n_entries_set && + m->n_entries <= 0) + return MHD_CONTENT_READER_END_OF_STREAM; + + if (m->n_skip < 0) + r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip + 1); + else if (m->n_skip > 0) + r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1); + else + r = sd_journal_next(m->journal); + + if (r < 0) { + log_error_errno(r, "Failed to advance journal pointer: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } else if (r == 0) { + + if (m->follow) { + 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; + } + + return MHD_CONTENT_READER_END_OF_STREAM; + } + + if (m->discrete) { + assert(m->cursor); + + r = sd_journal_test_cursor(m->journal, m->cursor); + if (r < 0) { + log_error_errno(r, "Failed to test cursor: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + if (r == 0) + return MHD_CONTENT_READER_END_OF_STREAM; + } + + pos -= m->size; + m->delta += m->size; + + if (m->n_entries_set) + m->n_entries -= 1; + + m->n_skip = 0; + + r = request_meta_ensure_tmp(m); + if (r < 0) { + log_error_errno(r, "Failed to create temporary file: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL); + if (r < 0) { + log_error_errno(r, "Failed to serialize item: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + sz = ftello(m->tmp); + if (sz == (off_t) -1) { + log_error_errno(errno, "Failed to retrieve file position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + m->size = (uint64_t) sz; + } + + if (fseeko(m->tmp, pos, SEEK_SET) < 0) { + log_error_errno(errno, "Failed to seek to position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + n = m->size - pos; + if (n < 1) + return 0; + if (n > max) + n = max; + + errno = 0; + k = fread(buf, 1, n, m->tmp); + if (k != n) { + log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + return (ssize_t) k; +} + +static int request_parse_accept( + RequestMeta *m, + struct MHD_Connection *connection) { + + const char *header; + + assert(m); + assert(connection); + + header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept"); + if (!header) + return 0; + + if (streq(header, mime_types[OUTPUT_JSON])) + m->mode = OUTPUT_JSON; + else if (streq(header, mime_types[OUTPUT_JSON_SSE])) + m->mode = OUTPUT_JSON_SSE; + else if (streq(header, mime_types[OUTPUT_EXPORT])) + m->mode = OUTPUT_EXPORT; + else + m->mode = OUTPUT_SHORT; + + return 0; +} + +static int request_parse_range( + RequestMeta *m, + struct MHD_Connection *connection) { + + const char *range, *colon, *colon2; + int r; + + assert(m); + assert(connection); + + range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); + if (!range) + return 0; + + if (!startswith(range, "entries=")) + return 0; + + range += 8; + range += strspn(range, WHITESPACE); + + colon = strchr(range, ':'); + if (!colon) + m->cursor = strdup(range); + else { + const char *p; + + colon2 = strchr(colon + 1, ':'); + if (colon2) { + _cleanup_free_ char *t; + + t = strndup(colon + 1, colon2 - colon - 1); + if (!t) + return -ENOMEM; + + r = safe_atoi64(t, &m->n_skip); + if (r < 0) + return r; + } + + p = (colon2 ? colon2 : colon) + 1; + if (*p) { + r = safe_atou64(p, &m->n_entries); + if (r < 0) + return r; + + if (m->n_entries <= 0) + return -EINVAL; + + m->n_entries_set = true; + } + + m->cursor = strndup(range, colon - range); + } + + if (!m->cursor) + return -ENOMEM; + + m->cursor[strcspn(m->cursor, WHITESPACE)] = 0; + if (isempty(m->cursor)) + m->cursor = mfree(m->cursor); + + return 0; +} + +static int request_parse_arguments_iterator( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value) { + + RequestMeta *m = cls; + _cleanup_free_ char *p = NULL; + int r; + + assert(m); + + if (isempty(key)) { + m->argument_parse_error = -EINVAL; + return MHD_NO; + } + + if (streq(key, "follow")) { + if (isempty(value)) { + m->follow = true; + return MHD_YES; + } + + r = parse_boolean(value); + if (r < 0) { + m->argument_parse_error = r; + return MHD_NO; + } + + m->follow = r; + return MHD_YES; + } + + if (streq(key, "discrete")) { + if (isempty(value)) { + m->discrete = true; + return MHD_YES; + } + + r = parse_boolean(value); + if (r < 0) { + m->argument_parse_error = r; + return MHD_NO; + } + + m->discrete = r; + return MHD_YES; + } + + if (streq(key, "boot")) { + if (isempty(value)) + r = true; + else { + r = parse_boolean(value); + if (r < 0) { + m->argument_parse_error = r; + return MHD_NO; + } + } + + if (r) { + char match[9 + 32 + 1] = "_BOOT_ID="; + sd_id128_t bid; + + r = sd_id128_get_boot(&bid); + if (r < 0) { + log_error_errno(r, "Failed to get boot ID: %m"); + return MHD_NO; + } + + sd_id128_to_string(bid, match + 9); + r = sd_journal_add_match(m->journal, match, sizeof(match)-1); + if (r < 0) { + m->argument_parse_error = r; + return MHD_NO; + } + } + + return MHD_YES; + } + + p = strjoin(key, "=", strempty(value), NULL); + if (!p) { + m->argument_parse_error = log_oom(); + return MHD_NO; + } + + r = sd_journal_add_match(m->journal, p, 0); + if (r < 0) { + m->argument_parse_error = r; + return MHD_NO; + } + + return MHD_YES; +} + +static int request_parse_arguments( + RequestMeta *m, + struct MHD_Connection *connection) { + + assert(m); + assert(connection); + + m->argument_parse_error = 0; + MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m); + + return m->argument_parse_error; +} + +static int request_handler_entries( + struct MHD_Connection *connection, + void *connection_cls) { + + struct MHD_Response *response; + RequestMeta *m = connection_cls; + int r; + + assert(connection); + assert(m); + + r = open_journal(m); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + + if (request_parse_accept(m, connection) < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); + + if (request_parse_range(m, connection) < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n"); + + if (request_parse_arguments(m, connection) < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n"); + + if (m->discrete) { + if (!m->cursor) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n"); + + m->n_entries = 1; + m->n_entries_set = true; + } + + if (m->cursor) + r = sd_journal_seek_cursor(m->journal, m->cursor); + else if (m->n_skip >= 0) + r = sd_journal_seek_head(m->journal); + else if (m->n_skip < 0) + r = sd_journal_seek_tail(m->journal); + if (r < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n"); + + response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); + if (!response) + return respond_oom(connection); + + MHD_add_response_header(response, "Content-Type", mime_types[m->mode]); + + r = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return r; +} + +static int output_field(FILE *f, OutputMode m, const char *d, size_t l) { + const char *eq; + size_t j; + + eq = memchr(d, '=', l); + if (!eq) + return -EINVAL; + + j = l - (eq - d + 1); + + if (m == OUTPUT_JSON) { + fprintf(f, "{ \"%.*s\" : ", (int) (eq - d), d); + json_escape(f, eq+1, j, OUTPUT_FULL_WIDTH); + fputs(" }\n", f); + } else { + fwrite(eq+1, 1, j, f); + fputc('\n', f); + } + + return 0; +} + +static ssize_t request_reader_fields( + void *cls, + uint64_t pos, + char *buf, + size_t max) { + + RequestMeta *m = cls; + int r; + size_t n, k; + + assert(m); + assert(buf); + assert(max > 0); + assert(pos >= m->delta); + + pos -= m->delta; + + while (pos >= m->size) { + off_t sz; + const void *d; + size_t l; + + /* End of this field, so let's serialize the next + * one */ + + if (m->n_fields_set && + m->n_fields <= 0) + return MHD_CONTENT_READER_END_OF_STREAM; + + r = sd_journal_enumerate_unique(m->journal, &d, &l); + if (r < 0) { + log_error_errno(r, "Failed to advance field index: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } else if (r == 0) + return MHD_CONTENT_READER_END_OF_STREAM; + + pos -= m->size; + m->delta += m->size; + + if (m->n_fields_set) + m->n_fields -= 1; + + r = request_meta_ensure_tmp(m); + if (r < 0) { + log_error_errno(r, "Failed to create temporary file: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + r = output_field(m->tmp, m->mode, d, l); + if (r < 0) { + log_error_errno(r, "Failed to serialize item: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + sz = ftello(m->tmp); + if (sz == (off_t) -1) { + log_error_errno(errno, "Failed to retrieve file position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + m->size = (uint64_t) sz; + } + + if (fseeko(m->tmp, pos, SEEK_SET) < 0) { + log_error_errno(errno, "Failed to seek to position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + n = m->size - pos; + if (n > max) + n = max; + + errno = 0; + k = fread(buf, 1, n, m->tmp); + if (k != n) { + log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + return (ssize_t) k; +} + +static int request_handler_fields( + struct MHD_Connection *connection, + const char *field, + void *connection_cls) { + + struct MHD_Response *response; + RequestMeta *m = connection_cls; + int r; + + assert(connection); + assert(m); + + r = open_journal(m); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + + if (request_parse_accept(m, connection) < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); + + r = sd_journal_query_unique(m->journal, field); + if (r < 0) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n"); + + response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL); + if (!response) + return respond_oom(connection); + + MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]); + + r = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return r; +} + +static int request_handler_redirect( + struct MHD_Connection *connection, + const char *target) { + + char *page; + struct MHD_Response *response; + int ret; + + assert(connection); + assert(target); + + if (asprintf(&page, "Please continue to the journal browser.", target) < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE); + if (!response) { + free(page); + return respond_oom(connection); + } + + MHD_add_response_header(response, "Content-Type", "text/html"); + MHD_add_response_header(response, "Location", target); + + ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); + MHD_destroy_response(response); + + return ret; +} + +static int request_handler_file( + struct MHD_Connection *connection, + const char *path, + const char *mime_type) { + + struct MHD_Response *response; + int ret; + _cleanup_close_ int fd = -1; + struct stat st; + + assert(connection); + assert(path); + assert(mime_type); + + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return mhd_respondf(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path); + + 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_offset64(st.st_size, fd, 0); + if (!response) + return respond_oom(connection); + + fd = -1; + + MHD_add_response_header(response, "Content-Type", mime_type); + + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return ret; +} + +static int get_virtualization(char **v) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + char *b = NULL; + int r; + + r = sd_bus_default_system(&bus); + if (r < 0) + return r; + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Virtualization", + NULL, + &b); + if (r < 0) + return r; + + if (isempty(b)) { + free(b); + *v = NULL; + return 0; + } + + *v = b; + return 1; +} + +static int request_handler_machine( + struct MHD_Connection *connection, + void *connection_cls) { + + struct MHD_Response *response; + RequestMeta *m = connection_cls; + int r; + _cleanup_free_ char* hostname = NULL, *os_name = NULL; + uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0; + char *json; + sd_id128_t mid, bid; + _cleanup_free_ char *v = NULL; + + assert(connection); + assert(m); + + r = open_journal(m); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + + r = sd_id128_get_machine(&mid); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r)); + + r = sd_id128_get_boot(&bid); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r)); + + hostname = gethostname_malloc(); + if (!hostname) + return respond_oom(connection); + + r = sd_journal_get_usage(m->journal, &usage); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + + r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to); + if (r < 0) + return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + + if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT) + (void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); + + get_virtualization(&v); + + r = asprintf(&json, + "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\"," + "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\"," + "\"hostname\" : \"%s\"," + "\"os_pretty_name\" : \"%s\"," + "\"virtualization\" : \"%s\"," + "\"usage\" : \"%"PRIu64"\"," + "\"cutoff_from_realtime\" : \"%"PRIu64"\"," + "\"cutoff_to_realtime\" : \"%"PRIu64"\" }\n", + SD_ID128_FORMAT_VAL(mid), + SD_ID128_FORMAT_VAL(bid), + hostname_cleanup(hostname), + os_name ? os_name : "GNU/Linux", + v ? v : "bare", + usage, + cutoff_from, + cutoff_to); + + if (r < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); + if (!response) { + free(json); + return respond_oom(connection); + } + + MHD_add_response_header(response, "Content-Type", "application/json"); + r = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return r; +} + +static int request_handler( + void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **connection_cls) { + int r, code; + + assert(connection); + assert(connection_cls); + assert(url); + assert(method); + + if (!streq(method, "GET")) + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, + "Unsupported method.\n"); + + + if (!*connection_cls) { + if (!request_meta(connection_cls)) + return respond_oom(connection); + return MHD_YES; + } + + if (arg_trust_pem) { + r = check_permissions(connection, &code, NULL); + if (r < 0) + return code; + } + + if (streq(url, "/")) + return request_handler_redirect(connection, "/browse"); + + if (streq(url, "/entries")) + return request_handler_entries(connection, *connection_cls); + + if (startswith(url, "/fields/")) + return request_handler_fields(connection, url + 8, *connection_cls); + + if (streq(url, "/browse")) + return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html"); + + if (streq(url, "/machine")) + return request_handler_machine(connection, *connection_cls); + + return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.\n"); +} + +static void help(void) { + printf("%s [OPTIONS...] ...\n\n" + "HTTP server for journal events.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --cert=CERT.PEM Server certificate in PEM format\n" + " --key=KEY.PEM Server key in PEM format\n" + " --trust=CERT.PEM Certificat authority certificate in PEM format\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_KEY, + ARG_CERT, + ARG_TRUST, + }; + + int r, c; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { "trust", required_argument, NULL, ARG_TRUST }, + {} + }; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch(c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_KEY: + if (arg_key_pem) { + log_error("Key file specified twice"); + return -EINVAL; + } + r = read_full_file(optarg, &arg_key_pem, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read key file: %m"); + assert(arg_key_pem); + break; + + case ARG_CERT: + if (arg_cert_pem) { + log_error("Certificate file specified twice"); + return -EINVAL; + } + r = read_full_file(optarg, &arg_cert_pem, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read certificate file: %m"); + assert(arg_cert_pem); + break; + + case ARG_TRUST: +#ifdef HAVE_GNUTLS + if (arg_trust_pem) { + log_error("CA certificate file specified twice"); + return -EINVAL; + } + r = read_full_file(optarg, &arg_trust_pem, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read CA certificate file: %m"); + assert(arg_trust_pem); + break; +#else + log_error("Option --trust is not available."); +#endif + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + log_error("This program does not take arguments."); + return -EINVAL; + } + + if (!!arg_key_pem != !!arg_cert_pem) { + log_error("Certificate and key files must be specified together"); + return -EINVAL; + } + + if (arg_trust_pem && !arg_key_pem) { + log_error("CA certificate can only be used with certificate file"); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + struct MHD_Daemon *d = NULL; + int r, n; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + return EXIT_FAILURE; + if (r == 0) + return EXIT_SUCCESS; + + sigbus_install(); + + r = setup_gnutls_logger(NULL); + if (r < 0) + return EXIT_FAILURE; + + n = sd_listen_fds(1); + if (n < 0) { + log_error_errno(n, "Failed to determine passed sockets: %m"); + goto finish; + } else if (n > 1) { + log_error("Can't listen on more than one socket."); + goto finish; + } else { + struct MHD_OptionItem opts[] = { + { MHD_OPTION_NOTIFY_COMPLETED, + (intptr_t) request_meta_free, NULL }, + { MHD_OPTION_EXTERNAL_LOGGER, + (intptr_t) microhttpd_logger, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }}; + int opts_pos = 2; + + /* 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) + {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START}; + if (arg_key_pem) { + assert(arg_cert_pem); + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem}; + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem}; + flags |= MHD_USE_SSL; + } + if (arg_trust_pem) { + assert(flags & MHD_USE_SSL); + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem}; + } + + d = MHD_start_daemon(flags, 19531, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_ARRAY, opts, + MHD_OPTION_END); + } + + if (!d) { + log_error("Failed to start daemon!"); + goto finish; + } + + pause(); + + r = EXIT_SUCCESS; + +finish: + if (d) + MHD_stop_daemon(d); + + return r; +} diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in new file mode 100644 index 0000000000..f4f845841d --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.in @@ -0,0 +1,29 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service +Documentation=man:systemd-journal-gatewayd(8) +Requires=systemd-journal-gatewayd.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-gatewayd +User=systemd-journal-gateway +Group=systemd-journal-gateway +SupplementaryGroups=systemd-journal +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=full +ProtectHome=yes + +# If there are many split upjournal files we need a lot of fds to +# access them all and combine +LimitNOFILE=16384 + +[Install] +Also=systemd-journal-gatewayd.socket diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.xml b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.xml new file mode 100644 index 0000000000..9ed85c3950 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.service.xml @@ -0,0 +1,302 @@ + + + + + + + + + systemd-journal-gatewayd.service + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-journal-gatewayd.service + 8 + + + + systemd-journal-gatewayd.service + systemd-journal-gatewayd.socket + systemd-journal-gatewayd + HTTP server for journal events + + + + systemd-journal-gatewayd.service + systemd-journal-gatewayd.socket + + /usr/lib/systemd/systemd-journal-gatewayd + OPTIONS + + + + + Description + + systemd-journal-gatewayd serves journal + events over the network. Clients must connect using + HTTP. The server listens on port 19531 by default. + If is specified, the server expects + HTTPS connections. + + The program is started by + systemd1 + and expects to receive a single socket. Use + systemctl start systemd-journal-gatewayd.socket to start + the service, and systemctl enable systemd-journal-gatewayd.socket + to have it started on boot. + + + + Options + + The following options are understood: + + + + + + Specify the path to a file containing a server + certificate in PEM format. This option switches + systemd-journal-gatewayd into HTTPS mode + and must be used together with + . + + + + + + Specify the path to a file containing a server + key in PEM format corresponding to the certificate specified + with . + + + + + + + + + Supported URLs + + The following URLs are recognized: + + + + /browse + + Interactive browsing. + + + + /entries[?option1&option2=value...] + + Retrieval of events in various formats. + + The part of the HTTP header + determines the format. Supported values are described below. + + + The part of the HTTP header + determines the range of events returned. Supported values are + described below. + + + GET parameters can be used to modify what events are + returned. Supported parameters are described below. + + + + + /machine + + Return a JSON structure describing the machine. + + Example: + { "machine_id" : "8cf7ed9d451ea194b77a9f118f3dc446", + "boot_id" : "3d3c9efaf556496a9b04259ee35df7f7", + "hostname" : "fedora", + "os_pretty_name" : "Fedora 19 (Rawhide)", + "virtualization" : "kvm", + ...} + + + + + + /fields/FIELD_NAME + + Return a list of values of this field present in the logs. + + + + + + + Accept header + + + + + + Recognized formats: + + + + text/plain + + The default. Plaintext syslog-like output, + one line per journal entry + (like journalctl --output short). + + + + + application/json + + Entries are formatted as JSON data structures, + one per line + (like journalctl --output json). + See Journal + JSON Format for more information. + + + + + text/event-stream + + Entries are formatted as JSON data structures, + wrapped in a format suitable for + Server-Sent Events + (like journalctl --output json-sse). + + + + + + application/vnd.fdo.journal + + Entries are serialized into a binary (but + mostly text-based) stream suitable for backups and network + transfer + (like journalctl --output export). + See Journal + Export Format for more information. + + + + + + + Range header + + + + + + where + is a cursor string, + is an integer, + is an unsigned integer. + + + Range defaults to all available events. + + + + URL GET parameters + + Following parameters can be used as part of the URL: + + + + follow + + wait for new events + (like journalctl --follow, except that + the number of events returned is not limited). + + + + + discrete + + Test that the specified cursor refers to an + entry in the journal. Returns just this entry. + + + + + boot + + Limit events to the current boot of the system + (like journalctl --this-boot). + + + + KEY=match + + Match journal fields. See + systemd.journal-fields7. + + + + + + + Examples + Retrieve events from this boot from local journal + in Journal + Export Format: + curl --silent -H'Accept: application/vnd.fdo.journal' \ + 'http://localhost:19531/entries?boot' + + + Listen for core dumps: + curl 'http://localhost:19531/entries?follow&MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1' + + + + See Also + + systemd1, + journalctl1, + systemd-journald.service8, + systemd.journal-fields7, + + + + diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket new file mode 100644 index 0000000000..79d9b04210 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.socket @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service Socket +Documentation=man:systemd-journal-gatewayd(8) + +[Socket] +ListenStream=19531 + +[Install] +WantedBy=sockets.target diff --git a/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.sysusers b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.sysusers new file mode 100644 index 0000000000..379be0852e --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-gatewayd/systemd-journal-gatewayd.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-journal-gateway - "systemd Journal Gateway" diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/Makefile b/src/grp-journal/grp-remote/systemd-journal-remote/Makefile new file mode 100644 index 0000000000..8bb2c3871d --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/Makefile @@ -0,0 +1,84 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_MICROHTTPD),) +rootlibexec_PROGRAMS += \ + systemd-journal-remote + +systemd_journal_remote_SOURCES = \ + src/journal-remote/journal-remote-parse.h \ + src/journal-remote/journal-remote-parse.c \ + src/journal-remote/journal-remote-write.h \ + src/journal-remote/journal-remote-write.c \ + src/journal-remote/journal-remote.h \ + src/journal-remote/journal-remote.c + +systemd_journal_remote_LDADD = \ + libjournal-core.la + +systemd_journal_remote_SOURCES += \ + src/journal-remote/microhttpd-util.h \ + src/journal-remote/microhttpd-util.c + +systemd_journal_remote_CFLAGS = \ + $(MICROHTTPD_CFLAGS) + +systemd_journal_remote_LDADD += \ + $(MICROHTTPD_LIBS) + +ifneq ($(ENABLE_TMPFILES),) +dist_tmpfiles_DATA += \ + tmpfiles.d/systemd-remote.conf +endif # ENABLE_TMPFILES + +ifneq ($(HAVE_GNUTLS),) +systemd_journal_remote_LDADD += \ + $(GNUTLS_LIBS) +endif # HAVE_GNUTLS + +# systemd-journal-remote make sense mostly with full crypto stack +dist_systemunit_DATA += \ + units/systemd-journal-remote.socket + +nodist_systemunit_DATA += \ + units/systemd-journal-remote.service + +journal-remote-install-hook: journal-install-hook + -$(MKDIR_P) $(DESTDIR)/var/log/journal/remote + -chown 0:0 $(DESTDIR)/var/log/journal/remote + -chmod 755 $(DESTDIR)/var/log/journal/remote + +INSTALL_EXEC_HOOKS += journal-remote-install-hook + +nodist_pkgsysconf_DATA += \ + src/journal-remote/journal-remote.conf + +EXTRA_DIST += \ + units/systemd-journal-remote.service.in \ + src/journal-remote/journal-remote.conf.in \ + src/journal-remote/log-generator.py +endif # HAVE_MICROHTTPD + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.c b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.c new file mode 100644 index 0000000000..c71fedd816 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.c @@ -0,0 +1,507 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "journald-native.h" + +#include "journal-remote-parse.h" + +#define LINE_CHUNK 8*1024u + +void source_free(RemoteSource *source) { + if (!source) + return; + + if (source->fd >= 0 && !source->passive_fd) { + log_debug("Closing fd:%d (%s)", source->fd, source->name); + safe_close(source->fd); + } + + free(source->name); + free(source->buf); + iovw_free_contents(&source->iovw); + + log_debug("Writer ref count %i", source->writer->n_ref); + writer_unref(source->writer); + + sd_event_source_unref(source->event); + sd_event_source_unref(source->buffer_event); + + free(source); +} + +/** + * Initialize zero-filled source with given values. On success, takes + * ownerhship of fd and writer, otherwise does not touch them. + */ +RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) { + + RemoteSource *source; + + log_debug("Creating source for %sfd:%d (%s)", + passive_fd ? "passive " : "", fd, name); + + assert(fd >= 0); + + source = new0(RemoteSource, 1); + if (!source) + return NULL; + + source->fd = fd; + source->passive_fd = passive_fd; + source->name = name; + source->writer = writer; + + return source; +} + +static char* realloc_buffer(RemoteSource *source, size_t size) { + char *b, *old = source->buf; + + b = GREEDY_REALLOC(source->buf, source->size, size); + if (!b) + return NULL; + + iovw_rebase(&source->iovw, old, source->buf); + + return b; +} + +static int get_line(RemoteSource *source, char **line, size_t *size) { + ssize_t n; + char *c = NULL; + + assert(source); + assert(source->state == STATE_LINE); + assert(source->offset <= source->filled); + assert(source->filled <= source->size); + assert(source->buf == NULL || source->size > 0); + assert(source->fd >= 0); + + for (;;) { + if (source->buf) { + size_t start = MAX(source->scanned, source->offset); + + c = memchr(source->buf + start, '\n', + source->filled - start); + if (c != NULL) + break; + } + + source->scanned = source->filled; + if (source->scanned >= DATA_SIZE_MAX) { + log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX); + return -E2BIG; + } + + if (source->passive_fd) + /* we have to wait for some data to come to us */ + return -EAGAIN; + + /* We know that source->filled is at most DATA_SIZE_MAX, so if + we reallocate it, we'll increase the size at least a bit. */ + assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX); + if (source->size - source->filled < LINE_CHUNK && + !realloc_buffer(source, MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX))) + return log_oom(); + + assert(source->buf); + assert(source->size - source->filled >= LINE_CHUNK || + source->size == ENTRY_SIZE_MAX); + + n = read(source->fd, + source->buf + source->filled, + source->size - source->filled); + if (n < 0) { + if (errno != EAGAIN) + log_error_errno(errno, "read(%d, ..., %zu): %m", + source->fd, + source->size - source->filled); + return -errno; + } else if (n == 0) + return 0; + + source->filled += n; + } + + *line = source->buf + source->offset; + *size = c + 1 - source->buf - source->offset; + source->offset += *size; + + return 1; +} + +int push_data(RemoteSource *source, const char *data, size_t size) { + assert(source); + assert(source->state != STATE_EOF); + + if (!realloc_buffer(source, source->filled + size)) { + log_error("Failed to store received data of size %zu " + "(in addition to existing %zu bytes with %zu filled): %s", + size, source->size, source->filled, strerror(ENOMEM)); + return -ENOMEM; + } + + memcpy(source->buf + source->filled, data, size); + source->filled += size; + + return 0; +} + +static int fill_fixed_size(RemoteSource *source, void **data, size_t size) { + + assert(source); + assert(source->state == STATE_DATA_START || + source->state == STATE_DATA || + source->state == STATE_DATA_FINISH); + assert(size <= DATA_SIZE_MAX); + assert(source->offset <= source->filled); + assert(source->filled <= source->size); + assert(source->buf != NULL || source->size == 0); + assert(source->buf == NULL || source->size > 0); + assert(source->fd >= 0); + assert(data); + + while (source->filled - source->offset < size) { + int n; + + if (source->passive_fd) + /* we have to wait for some data to come to us */ + return -EAGAIN; + + if (!realloc_buffer(source, source->offset + size)) + return log_oom(); + + n = read(source->fd, source->buf + source->filled, + source->size - source->filled); + if (n < 0) { + if (errno != EAGAIN) + log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd, + source->size - source->filled); + return -errno; + } else if (n == 0) + return 0; + + source->filled += n; + } + + *data = source->buf + source->offset; + source->offset += size; + + return 1; +} + +static int get_data_size(RemoteSource *source) { + int r; + void *data; + + assert(source); + assert(source->state == STATE_DATA_START); + assert(source->data_size == 0); + + r = fill_fixed_size(source, &data, sizeof(uint64_t)); + if (r <= 0) + return r; + + source->data_size = le64toh( *(uint64_t *) data ); + if (source->data_size > DATA_SIZE_MAX) { + log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u", + source->data_size, DATA_SIZE_MAX); + return -EINVAL; + } + if (source->data_size == 0) + log_warning("Binary field with zero length"); + + return 1; +} + +static int get_data_data(RemoteSource *source, void **data) { + int r; + + assert(source); + assert(data); + assert(source->state == STATE_DATA); + + r = fill_fixed_size(source, data, source->data_size); + if (r <= 0) + return r; + + return 1; +} + +static int get_data_newline(RemoteSource *source) { + int r; + char *data; + + assert(source); + assert(source->state == STATE_DATA_FINISH); + + r = fill_fixed_size(source, (void**) &data, 1); + if (r <= 0) + return r; + + assert(data); + if (*data != '\n') { + log_error("expected newline, got '%c'", *data); + return -EINVAL; + } + + return 1; +} + +static int process_dunder(RemoteSource *source, char *line, size_t n) { + const char *timestamp; + int r; + + assert(line); + assert(n > 0); + assert(line[n-1] == '\n'); + + /* XXX: is it worth to support timestamps in extended format? + * We don't produce them, but who knows... */ + + timestamp = startswith(line, "__CURSOR="); + if (timestamp) + /* ignore __CURSOR */ + return 1; + + timestamp = startswith(line, "__REALTIME_TIMESTAMP="); + if (timestamp) { + long long unsigned x; + line[n-1] = '\0'; + r = safe_atollu(timestamp, &x); + if (r < 0) + log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp); + else + source->ts.realtime = x; + return r < 0 ? r : 1; + } + + timestamp = startswith(line, "__MONOTONIC_TIMESTAMP="); + if (timestamp) { + long long unsigned x; + line[n-1] = '\0'; + r = safe_atollu(timestamp, &x); + if (r < 0) + log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp); + else + source->ts.monotonic = x; + return r < 0 ? r : 1; + } + + timestamp = startswith(line, "__"); + if (timestamp) { + log_notice("Unknown dunder line %s", line); + return 1; + } + + /* no dunder */ + return 0; +} + +static int process_data(RemoteSource *source) { + int r; + + switch(source->state) { + case STATE_LINE: { + char *line, *sep; + size_t n = 0; + + assert(source->data_size == 0); + + r = get_line(source, &line, &n); + if (r < 0) + return r; + if (r == 0) { + source->state = STATE_EOF; + return r; + } + assert(n > 0); + assert(line[n-1] == '\n'); + + if (n == 1) { + log_trace("Received empty line, event is ready"); + return 1; + } + + r = process_dunder(source, line, n); + if (r != 0) + return r < 0 ? r : 0; + + /* MESSAGE=xxx\n + or + COREDUMP\n + LLLLLLLL0011223344...\n + */ + sep = memchr(line, '=', n); + if (sep) { + /* chomp newline */ + n--; + + r = iovw_put(&source->iovw, line, n); + if (r < 0) + return r; + } else { + /* replace \n with = */ + line[n-1] = '='; + + source->field_len = n; + source->state = STATE_DATA_START; + + /* we cannot put the field in iovec until we have all data */ + } + + log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary"); + + return 0; /* continue */ + } + + case STATE_DATA_START: + assert(source->data_size == 0); + + r = get_data_size(source); + // log_debug("get_data_size() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + source->state = STATE_EOF; + return 0; + } + + source->state = source->data_size > 0 ? + STATE_DATA : STATE_DATA_FINISH; + + return 0; /* continue */ + + case STATE_DATA: { + void *data; + char *field; + + assert(source->data_size > 0); + + r = get_data_data(source, &data); + // log_debug("get_data_data() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + source->state = STATE_EOF; + return 0; + } + + assert(data); + + field = (char*) data - sizeof(uint64_t) - source->field_len; + memmove(field + sizeof(uint64_t), field, source->field_len); + + r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size); + if (r < 0) + return r; + + source->state = STATE_DATA_FINISH; + + return 0; /* continue */ + } + + case STATE_DATA_FINISH: + r = get_data_newline(source); + // log_debug("get_data_newline() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + source->state = STATE_EOF; + return 0; + } + + source->data_size = 0; + source->state = STATE_LINE; + + return 0; /* continue */ + default: + assert_not_reached("wtf?"); + } +} + +int process_source(RemoteSource *source, bool compress, bool seal) { + size_t remain, target; + int r; + + assert(source); + assert(source->writer); + + r = process_data(source); + if (r <= 0) + return r; + + /* We have a full event */ + log_trace("Received full event from source@%p fd:%d (%s)", + source, source->fd, source->name); + + if (!source->iovw.count) { + log_warning("Entry with no payload, skipping"); + goto freeing; + } + + assert(source->iovw.iovec); + assert(source->iovw.count); + + r = writer_write(source->writer, &source->iovw, &source->ts, compress, seal); + if (r < 0) + log_error_errno(r, "Failed to write entry of %zu bytes: %m", + iovw_size(&source->iovw)); + else + r = 1; + + freeing: + iovw_free_contents(&source->iovw); + + /* possibly reset buffer position */ + remain = source->filled - source->offset; + + if (remain == 0) /* no brainer */ + source->offset = source->scanned = source->filled = 0; + else if (source->offset > source->size - source->filled && + source->offset > remain) { + memcpy(source->buf, source->buf + source->offset, remain); + source->offset = source->scanned = 0; + source->filled = remain; + } + + target = source->size; + while (target > 16 * LINE_CHUNK && source->filled < target / 2) + target /= 2; + if (target < source->size) { + char *tmp; + + tmp = realloc(source->buf, target); + if (!tmp) + log_warning("Failed to reallocate buffer to (smaller) size %zu", + target); + else { + log_debug("Reallocated buffer from %zu to %zu bytes", + source->size, target); + source->buf = tmp; + source->size = target; + } + } + + return r; +} diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.h b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.h new file mode 100644 index 0000000000..4f47ea89d6 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-parse.h @@ -0,0 +1,69 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "journal-remote-write.h" + +typedef enum { + STATE_LINE = 0, /* waiting to read, or reading line */ + STATE_DATA_START, /* reading binary data header */ + STATE_DATA, /* reading binary data */ + STATE_DATA_FINISH, /* expecting newline */ + STATE_EOF, /* done */ +} source_state; + +typedef struct RemoteSource { + char *name; + int fd; + bool passive_fd; + + char *buf; + size_t size; /* total size of the buffer */ + size_t offset; /* offset to the beginning of live data in the buffer */ + size_t scanned; /* number of bytes since the beginning of data without a newline */ + size_t filled; /* total number of bytes in the buffer */ + + size_t field_len; /* used for binary fields: the field name length */ + size_t data_size; /* and the size of the binary data chunk being processed */ + + struct iovec_wrapper iovw; + + source_state state; + dual_timestamp ts; + + Writer *writer; + + sd_event_source *event; + sd_event_source *buffer_event; +} RemoteSource; + +RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer); + +static inline size_t source_non_empty(RemoteSource *source) { + assert(source); + + return source->filled; +} + +void source_free(RemoteSource *source); +int push_data(RemoteSource *source, const char *data, size_t size); +int process_source(RemoteSource *source, bool compress, bool seal); diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.c b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.c new file mode 100644 index 0000000000..4e05eda103 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.c @@ -0,0 +1,169 @@ +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" + +#include "journal-remote.h" + +int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) { + if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) + return log_oom(); + + iovw->iovec[iovw->count++] = (struct iovec) {data, len}; + return 0; +} + +void iovw_free_contents(struct iovec_wrapper *iovw) { + iovw->iovec = mfree(iovw->iovec); + iovw->size_bytes = iovw->count = 0; +} + +size_t iovw_size(struct iovec_wrapper *iovw) { + size_t n = 0, i; + + for (i = 0; i < iovw->count; i++) + n += iovw->iovec[i].iov_len; + + return n; +} + +void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { + size_t i; + + for (i = 0; i < iovw->count; i++) + iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new; +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int do_rotate(JournalFile **f, bool compress, bool seal) { + int r = journal_file_rotate(f, compress, seal, NULL); + if (r < 0) { + if (*f) + log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); + else + log_error_errno(r, "Failed to create rotated journal: %m"); + } + + return r; +} + +Writer* writer_new(RemoteServer *server) { + Writer *w; + + w = new0(Writer, 1); + if (!w) + return NULL; + + memset(&w->metrics, 0xFF, sizeof(w->metrics)); + + w->mmap = mmap_cache_new(); + if (!w->mmap) { + free(w); + return NULL; + } + + w->n_ref = 1; + w->server = server; + + return w; +} + +Writer* writer_free(Writer *w) { + if (!w) + return NULL; + + if (w->journal) { + log_debug("Closing journal file %s.", w->journal->path); + journal_file_close(w->journal); + } + + if (w->server && w->hashmap_key) + hashmap_remove(w->server->writers, w->hashmap_key); + + free(w->hashmap_key); + + if (w->mmap) + mmap_cache_unref(w->mmap); + + free(w); + + return NULL; +} + +Writer* writer_unref(Writer *w) { + if (w && (-- w->n_ref <= 0)) + writer_free(w); + + return NULL; +} + +Writer* writer_ref(Writer *w) { + if (w) + assert_se(++ w->n_ref >= 2); + + return w; +} + +int writer_write(Writer *w, + struct iovec_wrapper *iovw, + dual_timestamp *ts, + bool compress, + bool seal) { + int r; + + assert(w); + assert(iovw); + assert(iovw->count > 0); + + if (journal_file_rotate_suggested(w->journal, 0)) { + log_info("%s: Journal header limits reached or header out-of-date, rotating", + w->journal->path); + r = do_rotate(&w->journal, compress, seal); + if (r < 0) + return r; + } + + r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count, + &w->seqnum, NULL, NULL); + if (r >= 0) { + if (w->server) + w->server->event_count += 1; + return 1; + } + + log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->path); + r = do_rotate(&w->journal, compress, seal); + if (r < 0) + return r; + else + log_debug("%s: Successfully rotated journal", w->journal->path); + + log_debug("Retrying write."); + r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count, + &w->seqnum, NULL, NULL); + if (r < 0) + return r; + + if (w->server) + w->server->event_count += 1; + return 1; +} diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.h b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.h new file mode 100644 index 0000000000..a61434ca75 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote-write.h @@ -0,0 +1,70 @@ +#pragma once + +/*** + 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 . +***/ + +#include "sd-journal/journal-file.h" + +typedef struct RemoteServer RemoteServer; + +struct iovec_wrapper { + struct iovec *iovec; + size_t size_bytes; + size_t count; +}; + +int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len); +void iovw_free_contents(struct iovec_wrapper *iovw); +size_t iovw_size(struct iovec_wrapper *iovw); +void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); + +typedef struct Writer { + JournalFile *journal; + JournalMetrics metrics; + + MMapCache *mmap; + RemoteServer *server; + char *hashmap_key; + + uint64_t seqnum; + + int n_ref; +} Writer; + +Writer* writer_new(RemoteServer* server); +Writer* writer_free(Writer *w); + +Writer* writer_ref(Writer *w); +Writer* writer_unref(Writer *w); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Writer*, writer_unref); +#define _cleanup_writer_unref_ _cleanup_(writer_unrefp) + +int writer_write(Writer *s, + struct iovec_wrapper *iovw, + dual_timestamp *ts, + bool compress, + bool seal); + +typedef enum JournalWriteSplitMode { + JOURNAL_WRITE_SPLIT_NONE, + JOURNAL_WRITE_SPLIT_HOST, + _JOURNAL_WRITE_SPLIT_MAX, + _JOURNAL_WRITE_SPLIT_INVALID = -1 +} JournalWriteSplitMode; diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.c b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.c new file mode 100644 index 0000000000..ac1ba30310 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.c @@ -0,0 +1,1602 @@ +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GNUTLS +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "journald-native.h" +#include "sd-journal/journal-file.h" +#include "shared/conf-parser.h" + +#include "journal-remote-write.h" +#include "journal-remote.h" + +#define REMOTE_JOURNAL_PATH "/var/log/journal/remote" + +#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem" +#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem" +#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem" + +static char* arg_url = NULL; +static char* arg_getter = NULL; +static char* arg_listen_raw = NULL; +static char* arg_listen_http = NULL; +static char* arg_listen_https = NULL; +static char** arg_files = NULL; +static int arg_compress = true; +static int arg_seal = false; +static int http_socket = -1, https_socket = -1; +static char** arg_gnutls_log = NULL; + +static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST; +static char* arg_output = NULL; + +static char *arg_key = NULL; +static char *arg_cert = NULL; +static char *arg_trust = NULL; +static bool arg_trust_all = false; + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int spawn_child(const char* child, char** argv) { + int fd[2]; + pid_t parent_pid, child_pid; + int r; + + if (pipe(fd) < 0) + return log_error_errno(errno, "Failed to create pager pipe: %m"); + + parent_pid = getpid(); + + child_pid = fork(); + if (child_pid < 0) { + r = log_error_errno(errno, "Failed to fork: %m"); + safe_close_pair(fd); + return r; + } + + /* In the child */ + if (child_pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + r = dup2(fd[1], STDOUT_FILENO); + if (r < 0) { + log_error_errno(errno, "Failed to dup pipe to stdout: %m"); + _exit(EXIT_FAILURE); + } + + safe_close_pair(fd); + + /* Make sure the child goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + execvp(child, argv); + log_error_errno(errno, "Failed to exec child %s: %m", child); + _exit(EXIT_FAILURE); + } + + r = close(fd[1]); + if (r < 0) + log_warning_errno(errno, "Failed to close write end of pipe: %m"); + + return fd[0]; +} + +static int spawn_curl(const char* url) { + char **argv = STRV_MAKE("curl", + "-HAccept: application/vnd.fdo.journal", + "--silent", + "--show-error", + url); + int r; + + r = spawn_child("curl", argv); + if (r < 0) + log_error_errno(r, "Failed to spawn curl: %m"); + return r; +} + +static int spawn_getter(const char *getter) { + int r; + _cleanup_strv_free_ char **words = NULL; + + assert(getter); + r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to split getter option: %m"); + + r = spawn_child(words[0], words); + if (r < 0) + log_error_errno(r, "Failed to spawn getter %s: %m", getter); + + return r; +} + +#define filename_escape(s) xescape((s), "/ ") + +static int open_output(Writer *w, const char* host) { + _cleanup_free_ char *_output = NULL; + const char *output; + int r; + + switch (arg_split_mode) { + case JOURNAL_WRITE_SPLIT_NONE: + output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal"; + break; + + case JOURNAL_WRITE_SPLIT_HOST: { + _cleanup_free_ char *name; + + assert(host); + + name = filename_escape(host); + if (!name) + return log_oom(); + + r = asprintf(&_output, "%s/remote-%s.journal", + arg_output ?: REMOTE_JOURNAL_PATH, + name); + if (r < 0) + return log_oom(); + + output = _output; + break; + } + + default: + assert_not_reached("what?"); + } + + r = journal_file_open_reliably(output, + O_RDWR|O_CREAT, 0640, + arg_compress, arg_seal, + &w->metrics, + w->mmap, NULL, + NULL, &w->journal); + if (r < 0) + log_error_errno(r, "Failed to open output journal %s: %m", + output); + else + log_debug("Opened output file %s", w->journal->path); + return r; +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int init_writer_hashmap(RemoteServer *s) { + static const struct hash_ops *hash_ops[] = { + [JOURNAL_WRITE_SPLIT_NONE] = NULL, + [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops, + }; + + assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops)); + + s->writers = hashmap_new(hash_ops[arg_split_mode]); + if (!s->writers) + return log_oom(); + + return 0; +} + +static int get_writer(RemoteServer *s, const char *host, + Writer **writer) { + const void *key; + _cleanup_writer_unref_ Writer *w = NULL; + int r; + + switch(arg_split_mode) { + case JOURNAL_WRITE_SPLIT_NONE: + key = "one and only"; + break; + + case JOURNAL_WRITE_SPLIT_HOST: + assert(host); + key = host; + break; + + default: + assert_not_reached("what split mode?"); + } + + w = hashmap_get(s->writers, key); + if (w) + writer_ref(w); + else { + w = writer_new(s); + if (!w) + return log_oom(); + + if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) { + w->hashmap_key = strdup(key); + if (!w->hashmap_key) + return log_oom(); + } + + r = open_output(w, host); + if (r < 0) + return r; + + r = hashmap_put(s->writers, w->hashmap_key ?: key, w); + if (r < 0) + return r; + } + + *writer = w; + w = NULL; + return 0; +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +/* This should go away as soon as µhttpd allows state to be passed around. */ +static RemoteServer *server; + +static int dispatch_raw_source_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata); +static int dispatch_raw_source_until_block(sd_event_source *event, + void *userdata); +static int dispatch_blocking_source_event(sd_event_source *event, + void *userdata); +static int dispatch_raw_connection_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata); +static int dispatch_http_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata); + +static int get_source_for_fd(RemoteServer *s, + int fd, char *name, RemoteSource **source) { + Writer *writer; + int r; + + /* This takes ownership of name, but only on success. */ + + assert(fd >= 0); + assert(source); + + if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1)) + return log_oom(); + + r = get_writer(s, name, &writer); + if (r < 0) + return log_warning_errno(r, "Failed to get writer for source %s: %m", + name); + + if (s->sources[fd] == NULL) { + s->sources[fd] = source_new(fd, false, name, writer); + if (!s->sources[fd]) { + writer_unref(writer); + return log_oom(); + } + + s->active++; + } + + *source = s->sources[fd]; + return 0; +} + +static int remove_source(RemoteServer *s, int fd) { + RemoteSource *source; + + assert(s); + assert(fd >= 0 && fd < (ssize_t) s->sources_size); + + source = s->sources[fd]; + if (source) { + /* this closes fd too */ + source_free(source); + s->sources[fd] = NULL; + s->active--; + } + + return 0; +} + +static int add_source(RemoteServer *s, int fd, char* name, bool own_name) { + + RemoteSource *source = NULL; + int r; + + /* This takes ownership of name, even on failure, if own_name is true. */ + + assert(s); + assert(fd >= 0); + assert(name); + + if (!own_name) { + name = strdup(name); + if (!name) + return log_oom(); + } + + r = get_source_for_fd(s, fd, name, &source); + if (r < 0) { + log_error_errno(r, "Failed to create source for fd:%d (%s): %m", + fd, name); + free(name); + return r; + } + + r = sd_event_add_io(s->events, &source->event, + fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI, + dispatch_raw_source_event, source); + if (r == 0) { + /* Add additional source for buffer processing. It will be + * enabled later. */ + r = sd_event_add_defer(s->events, &source->buffer_event, + dispatch_raw_source_until_block, source); + if (r == 0) + sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF); + } else if (r == -EPERM) { + log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name); + r = sd_event_add_defer(s->events, &source->event, + dispatch_blocking_source_event, source); + if (r == 0) + sd_event_source_set_enabled(source->event, SD_EVENT_ON); + } + if (r < 0) { + log_error_errno(r, "Failed to register event source for fd:%d: %m", + fd); + goto error; + } + + r = sd_event_source_set_description(source->event, name); + if (r < 0) { + log_error_errno(r, "Failed to set source name for fd:%d: %m", fd); + goto error; + } + + return 1; /* work to do */ + + error: + remove_source(s, fd); + return r; +} + +static int add_raw_socket(RemoteServer *s, int fd) { + int r; + _cleanup_close_ int fd_ = fd; + char name[sizeof("raw-socket-")-1 + DECIMAL_STR_MAX(int) + 1]; + + assert(fd >= 0); + + r = sd_event_add_io(s->events, &s->listen_event, + fd, EPOLLIN, + dispatch_raw_connection_event, s); + if (r < 0) + return r; + + xsprintf(name, "raw-socket-%d", fd); + + r = sd_event_source_set_description(s->listen_event, name); + if (r < 0) + return r; + + fd_ = -1; + s->active++; + return 0; +} + +static int setup_raw_socket(RemoteServer *s, const char *address) { + int fd; + + fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC); + if (fd < 0) + return fd; + + return add_raw_socket(s, fd); +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int request_meta(void **connection_cls, int fd, char *hostname) { + RemoteSource *source; + Writer *writer; + int r; + + assert(connection_cls); + if (*connection_cls) + return 0; + + r = get_writer(server, hostname, &writer); + if (r < 0) + return log_warning_errno(r, "Failed to get writer for source %s: %m", + hostname); + + source = source_new(fd, true, hostname, writer); + if (!source) { + writer_unref(writer); + return log_oom(); + } + + log_debug("Added RemoteSource as connection metadata %p", source); + + *connection_cls = source; + return 0; +} + +static void request_meta_free(void *cls, + struct MHD_Connection *connection, + void **connection_cls, + enum MHD_RequestTerminationCode toe) { + RemoteSource *s; + + assert(connection_cls); + s = *connection_cls; + + if (s) { + log_debug("Cleaning up connection metadata %p", s); + source_free(s); + *connection_cls = NULL; + } +} + +static int process_http_upload( + struct MHD_Connection *connection, + const char *upload_data, + size_t *upload_data_size, + RemoteSource *source) { + + bool finished = false; + size_t remaining; + int r; + + assert(source); + + log_trace("%s: connection %p, %zu bytes", + __func__, connection, *upload_data_size); + + if (*upload_data_size) { + log_trace("Received %zu bytes", *upload_data_size); + + r = push_data(source, upload_data, *upload_data_size); + if (r < 0) + return mhd_respond_oom(connection); + + *upload_data_size = 0; + } else + finished = true; + + for (;;) { + r = process_source(source, arg_compress, arg_seal); + if (r == -EAGAIN) + break; + else if (r < 0) { + log_warning("Failed to process data for connection %p", connection); + if (r == -E2BIG) + return mhd_respondf(connection, + MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, + "Entry is too large, maximum is %u bytes.\n", + DATA_SIZE_MAX); + else + return mhd_respondf(connection, + MHD_HTTP_UNPROCESSABLE_ENTITY, + "Processing failed: %s.", strerror(-r)); + } + } + + if (!finished) + return MHD_YES; + + /* The upload is finished */ + + remaining = source_non_empty(source); + if (remaining > 0) { + log_warning("Premature EOFbyte. %zu bytes lost.", remaining); + return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED, + "Premature EOF. %zu bytes of trailing data not processed.", + remaining); + } + + return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n"); +}; + +static int request_handler( + void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **connection_cls) { + + const char *header; + int r, code, fd; + _cleanup_free_ char *hostname = NULL; + + assert(connection); + assert(connection_cls); + assert(url); + assert(method); + + log_trace("Handling a connection %s %s %s", method, url, version); + + if (*connection_cls) + return process_http_upload(connection, + upload_data, upload_data_size, + *connection_cls); + + if (!streq(method, "POST")) + return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, + "Unsupported method.\n"); + + if (!streq(url, "/upload")) + return mhd_respond(connection, MHD_HTTP_NOT_FOUND, + "Not found.\n"); + + header = MHD_lookup_connection_value(connection, + MHD_HEADER_KIND, "Content-Type"); + if (!header || !streq(header, "application/vnd.fdo.journal")) + return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, + "Content-Type: application/vnd.fdo.journal" + " is required.\n"); + + { + const union MHD_ConnectionInfo *ci; + + ci = MHD_get_connection_info(connection, + MHD_CONNECTION_INFO_CONNECTION_FD); + if (!ci) { + log_error("MHD_get_connection_info failed: cannot get remote fd"); + return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + "Cannot check remote address"); + } + + fd = ci->connect_fd; + assert(fd >= 0); + } + + if (server->check_trust) { + r = check_permissions(connection, &code, &hostname); + if (r < 0) + return code; + } else { + r = getpeername_pretty(fd, false, &hostname); + if (r < 0) + return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + "Cannot check remote hostname"); + } + + assert(hostname); + + r = request_meta(connection_cls, fd, hostname); + if (r == -ENOMEM) + return respond_oom(connection); + else if (r < 0) + return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, + strerror(-r)); + + hostname = NULL; + return MHD_YES; +} + +static int setup_microhttpd_server(RemoteServer *s, + int fd, + const char *key, + const char *cert, + const char *trust) { + struct MHD_OptionItem opts[] = { + { 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, 128*1024}, + { MHD_OPTION_END}, + { MHD_OPTION_END}, + { MHD_OPTION_END}, + { MHD_OPTION_END}}; + int opts_pos = 4; + int flags = + MHD_USE_DEBUG | + MHD_USE_DUAL_STACK | + MHD_USE_EPOLL_LINUX_ONLY | + MHD_USE_PEDANTIC_CHECKS | + MHD_USE_PIPE_FOR_SHUTDOWN; + + const union MHD_DaemonInfo *info; + int r, epoll_fd; + MHDDaemonWrapper *d; + + assert(fd >= 0); + + r = fd_nonblock(fd, true); + if (r < 0) + return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd); + + if (key) { + assert(cert); + + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key}; + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert}; + + flags |= MHD_USE_SSL; + + if (trust) + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust}; + } + + d = new(MHDDaemonWrapper, 1); + if (!d) + return log_oom(); + + d->fd = (uint64_t) fd; + + d->daemon = MHD_start_daemon(flags, 0, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_ARRAY, opts, + MHD_OPTION_END); + if (!d->daemon) { + log_error("Failed to start µhttp daemon"); + r = -EINVAL; + goto error; + } + + log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)", + key ? "HTTPS" : "HTTP", fd, d); + + + info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); + if (!info) { + log_error("µhttp returned NULL daemon info"); + r = -EOPNOTSUPP; + goto error; + } + + epoll_fd = info->listen_fd; + if (epoll_fd < 0) { + log_error("µhttp epoll fd is invalid"); + r = -EUCLEAN; + goto error; + } + + r = sd_event_add_io(s->events, &d->event, + epoll_fd, EPOLLIN, + dispatch_http_event, d); + if (r < 0) { + log_error_errno(r, "Failed to add event callback: %m"); + goto error; + } + + r = sd_event_source_set_description(d->event, "epoll-fd"); + if (r < 0) { + log_error_errno(r, "Failed to set source name: %m"); + goto error; + } + + r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops); + if (r < 0) { + log_oom(); + goto error; + } + + r = hashmap_put(s->daemons, &d->fd, d); + if (r < 0) { + log_error_errno(r, "Failed to add daemon to hashmap: %m"); + goto error; + } + + s->active++; + return 0; + +error: + MHD_stop_daemon(d->daemon); + free(d->daemon); + free(d); + return r; +} + +static int setup_microhttpd_socket(RemoteServer *s, + const char *address, + const char *key, + const char *cert, + const char *trust) { + int fd; + + fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC); + if (fd < 0) + return fd; + + return setup_microhttpd_server(s, fd, key, cert, trust); +} + +static int dispatch_http_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata) { + MHDDaemonWrapper *d = userdata; + int r; + + assert(d); + + r = MHD_run(d->daemon); + if (r == MHD_NO) { + log_error("MHD_run failed!"); + // XXX: unregister daemon + return -EINVAL; + } + + return 1; /* work to do */ +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int setup_signals(RemoteServer *s) { + int r; + + assert(s); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0); + + r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s); + if (r < 0) + return r; + + r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s); + if (r < 0) + return r; + + return 0; +} + +static int negative_fd(const char *spec) { + /* Return a non-positive number as its inverse, -EINVAL otherwise. */ + + int fd, r; + + r = safe_atoi(spec, &fd); + if (r < 0) + return r; + + if (fd > 0) + return -EINVAL; + else + return -fd; +} + +static int remoteserver_init(RemoteServer *s, + const char* key, + const char* cert, + const char* trust) { + int r, n, fd; + char **file; + + assert(s); + + if ((arg_listen_raw || arg_listen_http) && trust) { + log_error("Option --trust makes all non-HTTPS connections untrusted."); + return -EINVAL; + } + + r = sd_event_default(&s->events); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + setup_signals(s); + + assert(server == NULL); + server = s; + + r = init_writer_hashmap(s); + if (r < 0) + return r; + + n = sd_listen_fds(true); + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + else + log_debug("Received %d descriptors", n); + + if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) { + log_error("Received fewer sockets than expected"); + return -EBADFD; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + if (sd_is_socket(fd, AF_UNSPEC, 0, true)) { + log_debug("Received a listening socket (fd:%d)", fd); + + if (fd == http_socket) + r = setup_microhttpd_server(s, fd, NULL, NULL, NULL); + else if (fd == https_socket) + r = setup_microhttpd_server(s, fd, key, cert, trust); + else + r = add_raw_socket(s, fd); + } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) { + char *hostname; + + r = getpeername_pretty(fd, false, &hostname); + if (r < 0) + return log_error_errno(r, "Failed to retrieve remote name: %m"); + + log_debug("Received a connection socket (fd:%d) from %s", fd, hostname); + + r = add_source(s, fd, hostname, true); + } else { + log_error("Unknown socket passed on fd:%d", fd); + + return -EINVAL; + } + + if (r < 0) + return log_error_errno(r, "Failed to register socket (fd:%d): %m", + fd); + } + + if (arg_getter) { + log_info("Spawning getter %s...", arg_getter); + fd = spawn_getter(arg_getter); + if (fd < 0) + return fd; + + r = add_source(s, fd, (char*) arg_output, false); + if (r < 0) + return r; + } + + if (arg_url) { + const char *url; + char *hostname, *p; + + if (!strstr(arg_url, "/entries")) { + if (endswith(arg_url, "/")) + url = strjoina(arg_url, "entries"); + else + url = strjoina(arg_url, "/entries"); + } + else + url = strdupa(arg_url); + + log_info("Spawning curl %s...", url); + fd = spawn_curl(url); + if (fd < 0) + return fd; + + hostname = + startswith(arg_url, "https://") ?: + startswith(arg_url, "http://") ?: + arg_url; + + hostname = strdupa(hostname); + if ((p = strchr(hostname, '/'))) + *p = '\0'; + if ((p = strchr(hostname, ':'))) + *p = '\0'; + + r = add_source(s, fd, hostname, false); + if (r < 0) + return r; + } + + if (arg_listen_raw) { + log_debug("Listening on a socket..."); + r = setup_raw_socket(s, arg_listen_raw); + if (r < 0) + return r; + } + + if (arg_listen_http) { + r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL); + if (r < 0) + return r; + } + + if (arg_listen_https) { + r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust); + if (r < 0) + return r; + } + + STRV_FOREACH(file, arg_files) { + const char *output_name; + + if (streq(*file, "-")) { + log_debug("Using standard input as source."); + + fd = STDIN_FILENO; + output_name = "stdin"; + } else { + log_debug("Reading file %s...", *file); + + fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", *file); + output_name = *file; + } + + r = add_source(s, fd, (char*) output_name, false); + if (r < 0) + return r; + } + + if (s->active == 0) { + log_error("Zero sources specified"); + return -EINVAL; + } + + if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) { + /* In this case we know what the writer will be + called, so we can create it and verify that we can + create output as expected. */ + r = get_writer(s, NULL, &s->_single_writer); + if (r < 0) + return r; + } + + return 0; +} + +static void server_destroy(RemoteServer *s) { + size_t i; + MHDDaemonWrapper *d; + + while ((d = hashmap_steal_first(s->daemons))) { + MHD_stop_daemon(d->daemon); + sd_event_source_unref(d->event); + free(d); + } + + hashmap_free(s->daemons); + + assert(s->sources_size == 0 || s->sources); + for (i = 0; i < s->sources_size; i++) + remove_source(s, i); + free(s->sources); + + writer_unref(s->_single_writer); + hashmap_free(s->writers); + + sd_event_source_unref(s->sigterm_event); + sd_event_source_unref(s->sigint_event); + sd_event_source_unref(s->listen_event); + sd_event_unref(s->events); + + /* fds that we're listening on remain open... */ +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static int handle_raw_source(sd_event_source *event, + int fd, + uint32_t revents, + RemoteServer *s) { + + RemoteSource *source; + int r; + + /* Returns 1 if there might be more data pending, + * 0 if data is currently exhausted, negative on error. + */ + + assert(fd >= 0 && fd < (ssize_t) s->sources_size); + source = s->sources[fd]; + assert(source->fd == fd); + + r = process_source(source, arg_compress, arg_seal); + if (source->state == STATE_EOF) { + size_t remaining; + + log_debug("EOF reached with source fd:%d (%s)", + source->fd, source->name); + + remaining = source_non_empty(source); + if (remaining > 0) + log_notice("Premature EOF. %zu bytes lost.", remaining); + remove_source(s, source->fd); + log_debug("%zu active sources remaining", s->active); + return 0; + } else if (r == -E2BIG) { + log_notice_errno(E2BIG, "Entry too big, skipped"); + return 1; + } else if (r == -EAGAIN) { + return 0; + } else if (r < 0) { + log_debug_errno(r, "Closing connection: %m"); + remove_source(server, fd); + return 0; + } else + return 1; +} + +static int dispatch_raw_source_until_block(sd_event_source *event, + void *userdata) { + RemoteSource *source = userdata; + int r; + + /* Make sure event stays around even if source is destroyed */ + sd_event_source_ref(event); + + r = handle_raw_source(event, source->fd, EPOLLIN, server); + if (r != 1) + /* No more data for now */ + sd_event_source_set_enabled(event, SD_EVENT_OFF); + + sd_event_source_unref(event); + + return r; +} + +static int dispatch_raw_source_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata) { + RemoteSource *source = userdata; + int r; + + assert(source->event); + assert(source->buffer_event); + + r = handle_raw_source(event, fd, EPOLLIN, server); + if (r == 1) + /* Might have more data. We need to rerun the handler + * until we are sure the buffer is exhausted. */ + sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON); + + return r; +} + +static int dispatch_blocking_source_event(sd_event_source *event, + void *userdata) { + RemoteSource *source = userdata; + + return handle_raw_source(event, source->fd, EPOLLIN, server); +} + +static int accept_connection(const char* type, int fd, + SocketAddress *addr, char **hostname) { + int fd2, r; + + log_debug("Accepting new %s connection on fd:%d", type, fd); + fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (fd2 < 0) + return log_error_errno(errno, "accept() on fd:%d failed: %m", fd); + + switch(socket_address_family(addr)) { + case AF_INET: + case AF_INET6: { + _cleanup_free_ char *a = NULL; + char *b; + + r = socket_address_print(addr, &a); + if (r < 0) { + log_error_errno(r, "socket_address_print(): %m"); + close(fd2); + return r; + } + + r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b); + if (r < 0) { + log_error_errno(r, "Resolving hostname failed: %m"); + close(fd2); + return r; + } + + log_debug("Accepted %s %s connection from %s", + type, + socket_address_family(addr) == AF_INET ? "IP" : "IPv6", + a); + + *hostname = b; + + return fd2; + }; + default: + log_error("Rejected %s connection with unsupported family %d", + type, socket_address_family(addr)); + close(fd2); + + return -EINVAL; + } +} + +static int dispatch_raw_connection_event(sd_event_source *event, + int fd, + uint32_t revents, + void *userdata) { + RemoteServer *s = userdata; + int fd2; + SocketAddress addr = { + .size = sizeof(union sockaddr_union), + .type = SOCK_STREAM, + }; + char *hostname = NULL; + + fd2 = accept_connection("raw", fd, &addr, &hostname); + if (fd2 < 0) + return fd2; + + return add_source(s, fd2, hostname, true); +} + +/********************************************************************** + ********************************************************************** + **********************************************************************/ + +static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = { + [JOURNAL_WRITE_SPLIT_NONE] = "none", + [JOURNAL_WRITE_SPLIT_HOST] = "host", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode); +static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode, + journal_write_split_mode, + JournalWriteSplitMode, + "Failed to parse split mode setting"); + +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 }, + { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, + {}}; + + return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf", + CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), + "Remote\0", config_item_table_lookup, items, + false, NULL); +} + +static void help(void) { + printf("%s [OPTIONS...] {FILE|-}...\n\n" + "Write external journal events to journal file(s).\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --url=URL Read events from systemd-journal-gatewayd at URL\n" + " --getter=COMMAND Read events from the output of COMMAND\n" + " --listen-raw=ADDR Listen for connections at ADDR\n" + " --listen-http=ADDR Listen for HTTP connections at ADDR\n" + " --listen-https=ADDR Listen for HTTPS connections at ADDR\n" + " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n" + " --compress[=BOOL] XZ-compress the output journal (default: yes)\n" + " --seal[=BOOL] Use event sealing (default: no)\n" + " --key=FILENAME SSL key in PEM format (default:\n" + " \"" PRIV_KEY_FILE "\")\n" + " --cert=FILENAME SSL certificate in PEM format (default:\n" + " \"" CERT_FILE "\")\n" + " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n" + " \"" TRUST_FILE "\")\n" + " --gnutls-log=CATEGORY...\n" + " Specify a list of gnutls logging categories\n" + " --split-mode=none|host How many output files to create\n" + "\n" + "Note: file descriptors from sd_listen_fds() will be consumed, too.\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_URL, + ARG_LISTEN_RAW, + ARG_LISTEN_HTTP, + ARG_LISTEN_HTTPS, + ARG_GETTER, + ARG_SPLIT_MODE, + ARG_COMPRESS, + ARG_SEAL, + ARG_KEY, + ARG_CERT, + ARG_TRUST, + ARG_GNUTLS_LOG, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "url", required_argument, NULL, ARG_URL }, + { "getter", required_argument, NULL, ARG_GETTER }, + { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW }, + { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP }, + { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS }, + { "output", required_argument, NULL, 'o' }, + { "split-mode", required_argument, NULL, ARG_SPLIT_MODE }, + { "compress", optional_argument, NULL, ARG_COMPRESS }, + { "seal", optional_argument, NULL, ARG_SEAL }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { "trust", required_argument, NULL, ARG_TRUST }, + { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG }, + {} + }; + + int c, r; + bool type_a, type_b; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0 /* done */; + + case ARG_VERSION: + return version(); + + case ARG_URL: + if (arg_url) { + log_error("cannot currently set more than one --url"); + return -EINVAL; + } + + arg_url = optarg; + break; + + case ARG_GETTER: + if (arg_getter) { + log_error("cannot currently use --getter more than once"); + return -EINVAL; + } + + arg_getter = optarg; + break; + + case ARG_LISTEN_RAW: + if (arg_listen_raw) { + log_error("cannot currently use --listen-raw more than once"); + return -EINVAL; + } + + arg_listen_raw = optarg; + break; + + case ARG_LISTEN_HTTP: + if (arg_listen_http || http_socket >= 0) { + log_error("cannot currently use --listen-http more than once"); + return -EINVAL; + } + + r = negative_fd(optarg); + if (r >= 0) + http_socket = r; + else + arg_listen_http = optarg; + break; + + case ARG_LISTEN_HTTPS: + if (arg_listen_https || https_socket >= 0) { + log_error("cannot currently use --listen-https more than once"); + return -EINVAL; + } + + r = negative_fd(optarg); + if (r >= 0) + https_socket = r; + else + arg_listen_https = optarg; + + break; + + case ARG_KEY: + if (arg_key) { + log_error("Key file specified twice"); + return -EINVAL; + } + + arg_key = strdup(optarg); + if (!arg_key) + return log_oom(); + + break; + + case ARG_CERT: + if (arg_cert) { + log_error("Certificate file specified twice"); + return -EINVAL; + } + + arg_cert = strdup(optarg); + if (!arg_cert) + return log_oom(); + + break; + + case ARG_TRUST: + if (arg_trust || arg_trust_all) { + log_error("Confusing trusted CA configuration"); + return -EINVAL; + } + + if (streq(optarg, "all")) + arg_trust_all = true; + else { +#ifdef HAVE_GNUTLS + arg_trust = strdup(optarg); + if (!arg_trust) + return log_oom(); +#else + log_error("Option --trust is not available."); + return -EINVAL; +#endif + } + + break; + + case 'o': + if (arg_output) { + log_error("cannot use --output/-o more than once"); + return -EINVAL; + } + + arg_output = optarg; + break; + + case ARG_SPLIT_MODE: + arg_split_mode = journal_write_split_mode_from_string(optarg); + if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) { + log_error("Invalid split mode: %s", optarg); + return -EINVAL; + } + break; + + case ARG_COMPRESS: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --compress= parameter."); + return -EINVAL; + } + + arg_compress = !!r; + } else + arg_compress = true; + + break; + + case ARG_SEAL: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --seal= parameter."); + return -EINVAL; + } + + arg_seal = !!r; + } else + arg_seal = true; + + break; + + case ARG_GNUTLS_LOG: { +#ifdef HAVE_GNUTLS + const char* p = optarg; + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m"); + + if (r == 0) + break; + + if (strv_push(&arg_gnutls_log, word) < 0) + return log_oom(); + + word = NULL; + } + break; +#else + log_error("Option --gnutls-log is not available."); + return -EINVAL; +#endif + } + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unknown option code."); + } + + if (optind < argc) + arg_files = argv + optind; + + type_a = arg_getter || !strv_isempty(arg_files); + type_b = arg_url + || arg_listen_raw + || arg_listen_http || arg_listen_https + || sd_listen_fds(false) > 0; + if (type_a && type_b) { + log_error("Cannot use file input or --getter with " + "--arg-listen-... or socket activation."); + return -EINVAL; + } + if (type_a) { + if (!arg_output) { + log_error("Option --output must be specified with file input or --getter."); + return -EINVAL; + } + + arg_split_mode = JOURNAL_WRITE_SPLIT_NONE; + } + + if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE + && arg_output && is_dir(arg_output, true) > 0) { + log_error("For SplitMode=none, output must be a file."); + return -EINVAL; + } + + if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST + && arg_output && is_dir(arg_output, true) <= 0) { + log_error("For SplitMode=host, output must be a directory."); + return -EINVAL; + } + + log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s", + journal_write_split_mode_to_string(arg_split_mode), + strna(arg_key), + strna(arg_cert), + strna(arg_trust)); + + return 1 /* work to do */; +} + +static int load_certificates(char **key, char **cert, char **trust) { + int r; + + r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read key from file '%s': %m", + arg_key ?: PRIV_KEY_FILE); + + r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read certificate from file '%s': %m", + arg_cert ?: CERT_FILE); + + if (arg_trust_all) + log_info("Certificate checking disabled."); + else { + r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL); + if (r < 0) + return log_error_errno(r, "Failed to read CA certificate file '%s': %m", + arg_trust ?: TRUST_FILE); + } + + return 0; +} + +int main(int argc, char **argv) { + RemoteServer s = {}; + int r; + _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; + + log_show_color(true); + log_parse_environment(); + + r = parse_config(); + if (r < 0) + return EXIT_FAILURE; + + r = parse_argv(argc, argv); + if (r <= 0) + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + + + if (arg_listen_http || arg_listen_https) { + r = setup_gnutls_logger(arg_gnutls_log); + if (r < 0) + return EXIT_FAILURE; + } + + if (arg_listen_https || https_socket >= 0) + if (load_certificates(&key, &cert, &trust) < 0) + return EXIT_FAILURE; + + if (remoteserver_init(&s, key, cert, trust) < 0) + return EXIT_FAILURE; + + r = sd_event_set_watchdog(s.events, true); + if (r < 0) + log_error_errno(r, "Failed to enable watchdog: %m"); + else + log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled"); + + log_debug("%s running as pid "PID_FMT, + program_invocation_short_name, getpid()); + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + while (s.active) { + r = sd_event_get_state(s.events); + if (r < 0) + break; + if (r == SD_EVENT_FINISHED) + break; + + r = sd_event_run(s.events, -1); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + break; + } + } + + sd_notifyf(false, + "STOPPING=1\n" + "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count); + log_info("Finishing after writing %" PRIu64 " entries", s.event_count); + + server_destroy(&s); + + free(arg_key); + free(arg_cert); + free(arg_trust); + + return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.in b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.in new file mode 100644 index 0000000000..7122d63362 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.in @@ -0,0 +1,6 @@ +[Remote] +# Seal=false +# SplitMode=host +# ServerKeyFile=@CERTIFICATEROOT@/private/journal-remote.pem +# ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-remote.pem +# TrustedCertificateFile=@CERTIFICATEROOT@/ca/trusted.pem diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.xml b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.xml new file mode 100644 index 0000000000..2d345963d9 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.conf.xml @@ -0,0 +1,121 @@ + + + + + + + + journal-remote.conf + systemd + + + + Developer + Chris + Morgan + chmorgan@gmail.com + + + + + + journal-remote.conf + 5 + + + + journal-remote.conf + journal-remote.conf.d + Journal remote service configuration files + + + + /etc/systemd/journal-remote.conf + /etc/systemd/journald.conf.d/*.conf + /run/systemd/journald.conf.d/*.conf + /usr/lib/systemd/journald.conf.d/*.conf + + + + Description + + These files configure various parameters of the systemd-remote-journal + application, + systemd-journal-remote8. + + + + + + Options + + All options are configured in the + [Remote] section: + + + + Seal= + + Periodically sign the data in the journal using Forward Secure Sealing. + + + + + + SplitMode= + + One of host or none. + + + + + ServerKeyFile= + + SSL key in PEM format. + + + + ServerCertificateFile= + + SSL CA certificate in PEM format. + + + + TrustedCertificateFile= + + SSL CA certificate. + + + + + + + + See Also + + systemd-journal-remote8, + systemd1, + systemd-journald.service8 + + + + diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.h b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.h new file mode 100644 index 0000000000..6abfc1019d --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/journal-remote.h @@ -0,0 +1,53 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "microhttpd-util.h" + +#include "journal-remote-parse.h" +#include "journal-remote-write.h" + +typedef struct MHDDaemonWrapper MHDDaemonWrapper; + +struct MHDDaemonWrapper { + uint64_t fd; + struct MHD_Daemon *daemon; + + sd_event_source *event; +}; + +struct RemoteServer { + RemoteSource **sources; + size_t sources_size; + size_t active; + + sd_event *events; + sd_event_source *sigterm_event, *sigint_event, *listen_event; + + Hashmap *writers; + Writer *_single_writer; + uint64_t event_count; + + bool check_trust; + Hashmap *daemons; +}; diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in new file mode 100644 index 0000000000..fdf3da4b64 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.service.in @@ -0,0 +1,25 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Sink Service +Documentation=man:systemd-journal-remote(8) man:journal-remote.conf(5) +Requires=systemd-journal-remote.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-remote \ + --listen-https=-3 \ + --output=/var/log/journal/remote/ +User=systemd-journal-remote +Group=systemd-journal-remote +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +WatchdogSec=3min + +[Install] +Also=systemd-journal-remote.socket diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket new file mode 100644 index 0000000000..076dcae8a3 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.socket @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Sink Socket + +[Socket] +ListenStream=19532 + +[Install] +WantedBy=sockets.target diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers new file mode 100644 index 0000000000..ca20c24896 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-journal-remote - "systemd Journal Remote" diff --git a/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.xml b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.xml new file mode 100644 index 0000000000..3899f175d4 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-remote/systemd-journal-remote.xml @@ -0,0 +1,325 @@ + + + + + + + + + systemd-journal-remote + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-journal-remote + 8 + + + + systemd-journal-remote + Receive journal messages over the network + + + + + systemd-journal-remote + OPTIONS + -o/--output=DIR|FILE + SOURCES + + + + + Description + + + systemd-journal-remote is a command to + receive serialized journal events and store them to the journal. + Input streams are in the + + Journal Export Format + , + i.e. like the output from + journalctl --output=export. For transport over + the network, this serialized stream is usually carried over an + HTTPS connection. + + + + + Sources + + + Sources can be either "active" + (systemd-journal-remote requests and pulls + the data), or "passive" + (systemd-journal-remote waits for a + connection and then receives events pushed by the other side). + + + + systemd-journal-remote can read more than one + event stream at a time. They will be interleaved in the output + file. In case of "active" connections, each "source" is one + stream, and in case of "passive" connections, each connection can + result in a separate stream. Sockets can be configured in + "accept" mode (i.e. only one connection), or "listen" mode (i.e. + multiple connections, each resulting in a stream). + + + + When there are no more connections, and no more can be created + (there are no listening sockets), then + systemd-journal-remote will exit. + + + Active sources can be specified in the following + ways: + + + + When is given as a + positional argument, events will be read from standard input. + Other positional arguments will be treated as filenames + to open and read from. + + + + + + With the + option, + events will be retrieved using HTTP from + ADDRESS. This URL should refer to the + root of a remote + systemd-journal-gatewayd8 + instance (e.g. http://some.host:19531/ or + https://some.host:19531/). + + + + Passive sources can be specified in the following + ways: + + + + + + ADDRESS must be an + address suitable for (cf. + systemd.socket5). + systemd-journal-remote will listen on this + socket for connections. Each connection is expected to be a + stream of journal events. + + + + + + + + ADDRESS must be + either a negative integer, in which case it will be + interpreted as the (negated) file descriptor number, or an + address suitable for (c.f. + systemd.socket5). + In the first case, matching file descriptor must be inherited + through + $LISTEN_FDS/$LISTEN_PID. + In the second case, an HTTP or HTTPS server will be spawned on + this port, respectively for and + . Currently, only POST requests + to /upload with Content-Type: + application/vnd.fdo.journal are supported. + + + + + $LISTEN_FDS + + systemd-journal-remote + supports the + $LISTEN_FDS/$LISTEN_PID + protocol. Open sockets inherited through socket activation + behave like those opened with + described above, unless they are specified as an argument in + + or + + above. In the latter case, an HTTP or HTTPS server will be + spawned using this descriptor and connections must be made + over the HTTP protocol. + + + + + + + + Sinks + + The location of the output journal can be specified + with or . For "active" + sources, this option is required. + + + + + + + Will write to this journal file. The filename + must end with .journal. The file will be + created if it does not exist. If necessary (journal file full, + or corrupted), the file will be renamed following normal + journald rules and a new journal file will be created in its + stead. + + + + + + Will create journal files underneath directory + DIR. The directory must exist. If + necessary (journal files over size, or corrupted), journal + files will be rotated following normal journald rules. Names + of files underneath DIR will be + generated using the rules described below. + + + + If is not used, the output + directory /var/log/journal/remote/ will be + used. In case the output file is not specified, journal files + will be created underneath the selected directory. Files will be + called + remote-hostname.journal, + where the hostname part is the + escaped hostname of the source endpoint of the connection, or the + numerical address if the hostname cannot be determined. + + In case of "active" sources, the output file name must + always be given explicitly. + + + + Options + + The following options are understood: + + + + + + One of none or + host. For the first, only one output + journal file is used. For the latter, a separate output file + is used, based on the hostname of the other endpoint of a + connection. + + In case of "active" sources, the output file name must + always be given explicitly and only none + is allowed. + + + + + + + Compress or not, respectively, the data in the + journal using XZ. + + + + + + + Periodically sign or not, respectively, the + data in the journal using Forward Secure Sealing. + + + + + + + Program to invoke to retrieve data. The journal + event stream must be generated on standard output. + + Examples: + + --getter='curl "-HAccept: application/vnd.fdo.journal" https://some.host:19531/' + + --getter='wget --header="Accept: application/vnd.fdo.journal" -O- https://some.host:19531/' + + + + + + + + + + Examples + Copy local journal events to a different journal directory: + +journalctl -o export | systemd-journal-remote -o /tmp/dir - + + + + Retrieve all available events from a remote + systemd-journal-gatewayd8 + instance and store them in + /var/log/journal/remote/remote-some.host.journal: + +systemd-journal-remote --url http://some.host:19531/ + + + + Retrieve current boot events and wait for new events from a remote + systemd-journal-gatewayd8 + instance, and store them in + /var/log/journal/remote/remote-some.host.journal: + +systemd-journal-remote --url http://some.host:19531/entries?boot&follow + + + + + + See Also + + systemd-journal-upload8, + journalctl1, + systemd-journald.service8, + systemd-journal-gatewayd.service8 + journal-remote.conf5 + + + diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/Makefile b/src/grp-journal/grp-remote/systemd-journal-upload/Makefile new file mode 100644 index 0000000000..f6966df0f6 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/Makefile @@ -0,0 +1,54 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_LIBCURL),) +rootlibexec_PROGRAMS += \ + systemd-journal-upload + +systemd_journal_upload_SOURCES = \ + src/journal-remote/journal-upload.h \ + src/journal-remote/journal-upload.c \ + src/journal-remote/journal-upload-journal.c + +systemd_journal_upload_CFLAGS = \ + $(LIBCURL_CFLAGS) + +systemd_journal_upload_LDADD = \ + libsystemd-shared.la \ + $(LIBCURL_LIBS) + +nodist_systemunit_DATA += \ + units/systemd-journal-upload.service + +nodist_pkgsysconf_DATA += \ + src/journal-remote/journal-upload.conf + +endif # HAVE_LIBCURL + +EXTRA_DIST += \ + units/systemd-journal-upload.service.in \ + src/journal-remote/journal-upload.conf.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload-journal.c b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload-journal.c new file mode 100644 index 0000000000..61d287da97 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload-journal.c @@ -0,0 +1,424 @@ +/*** + 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/utf8.h" +#include "basic/util.h" + +#include "journal-upload.h" + +/** + * Write up to size bytes to buf. Return negative on error, and number of + * bytes written otherwise. The last case is a kind of an error too. + */ +static ssize_t write_entry(char *buf, size_t size, Uploader *u) { + int r; + size_t pos = 0; + + assert(size <= SSIZE_MAX); + + for (;;) { + + switch(u->entry_state) { + case ENTRY_CURSOR: { + u->current_cursor = mfree(u->current_cursor); + + r = sd_journal_get_cursor(u->journal, &u->current_cursor); + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); + + r = snprintf(buf + pos, size - pos, + "__CURSOR=%s\n", u->current_cursor); + if (pos + r > size) + /* not enough space */ + return pos; + + u->entry_state++; + + if (pos + r == size) { + /* exactly one character short, but we don't need it */ + buf[size - 1] = '\n'; + return size; + } + + pos += r; + } /* fall through */ + + case ENTRY_REALTIME: { + usec_t realtime; + + r = sd_journal_get_realtime_usec(u->journal, &realtime); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + r = snprintf(buf + pos, size - pos, + "__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime); + if (r + pos > size) + /* not enough space */ + return pos; + + u->entry_state++; + + if (r + pos == size) { + /* exactly one character short, but we don't need it */ + buf[size - 1] = '\n'; + return size; + } + + pos += r; + } /* fall through */ + + case ENTRY_MONOTONIC: { + usec_t monotonic; + sd_id128_t boot_id; + + r = sd_journal_get_monotonic_usec(u->journal, &monotonic, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + r = snprintf(buf + pos, size - pos, + "__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic); + if (r + pos > size) + /* not enough space */ + return pos; + + u->entry_state++; + + if (r + pos == size) { + /* exactly one character short, but we don't need it */ + buf[size - 1] = '\n'; + return size; + } + + pos += r; + } /* fall through */ + + case ENTRY_BOOT_ID: { + sd_id128_t boot_id; + char sid[33]; + + r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + r = snprintf(buf + pos, size - pos, + "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid)); + if (r + pos > size) + /* not enough space */ + return pos; + + u->entry_state++; + + if (r + pos == size) { + /* exactly one character short, but we don't need it */ + buf[size - 1] = '\n'; + return size; + } + + pos += r; + } /* fall through */ + + case ENTRY_NEW_FIELD: { + u->field_pos = 0; + + r = sd_journal_enumerate_data(u->journal, + &u->field_data, + &u->field_length); + if (r < 0) + return log_error_errno(r, "Failed to move to next field in entry: %m"); + else if (r == 0) { + u->entry_state = ENTRY_OUTRO; + continue; + } + + if (!utf8_is_printable_newline(u->field_data, + u->field_length, false)) { + u->entry_state = ENTRY_BINARY_FIELD_START; + continue; + } + + u->entry_state++; + } /* fall through */ + + case ENTRY_TEXT_FIELD: + case ENTRY_BINARY_FIELD: { + bool done; + size_t tocopy; + + done = size - pos > u->field_length - u->field_pos; + if (done) + tocopy = u->field_length - u->field_pos; + else + tocopy = size - pos; + + memcpy(buf + pos, + (char*) u->field_data + u->field_pos, + tocopy); + + if (done) { + buf[pos + tocopy] = '\n'; + pos += tocopy + 1; + u->entry_state = ENTRY_NEW_FIELD; + continue; + } else { + u->field_pos += tocopy; + return size; + } + } + + case ENTRY_BINARY_FIELD_START: { + const char *c; + size_t len; + + c = memchr(u->field_data, '=', u->field_length); + if (!c || c == u->field_data) { + log_error("Invalid field."); + return -EINVAL; + } + + len = c - (const char*)u->field_data; + + /* need space for label + '\n' */ + if (size - pos < len + 1) + return pos; + + memcpy(buf + pos, u->field_data, len); + buf[pos + len] = '\n'; + pos += len + 1; + + u->field_pos = len + 1; + u->entry_state++; + } /* fall through */ + + case ENTRY_BINARY_FIELD_SIZE: { + uint64_t le64; + + /* need space for uint64_t */ + if (size - pos < 8) + return pos; + + le64 = htole64(u->field_length - u->field_pos); + memcpy(buf + pos, &le64, 8); + pos += 8; + + u->entry_state++; + continue; + } + + case ENTRY_OUTRO: + /* need space for '\n' */ + if (size - pos < 1) + return pos; + + buf[pos++] = '\n'; + u->entry_state++; + u->entries_sent++; + + return pos; + + default: + assert_not_reached("WTF?"); + } + } + assert_not_reached("WTF?"); +} + +static inline void check_update_watchdog(Uploader *u) { + usec_t after; + usec_t elapsed_time; + + if (u->watchdog_usec <= 0) + return; + + after = now(CLOCK_MONOTONIC); + elapsed_time = usec_sub(after, u->watchdog_timestamp); + if (elapsed_time > u->watchdog_usec / 2) { + log_debug("Update watchdog timer"); + sd_notify(false, "WATCHDOG=1"); + u->watchdog_timestamp = after; + } +} + +static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void *userp) { + Uploader *u = userp; + int r; + sd_journal *j; + size_t filled = 0; + ssize_t w; + + assert(u); + assert(nmemb <= SSIZE_MAX / size); + + check_update_watchdog(u); + + j = u->journal; + + while (j && filled < size * nmemb) { + if (u->entry_state == ENTRY_DONE) { + r = sd_journal_next(j); + if (r < 0) { + log_error_errno(r, "Failed to move to next entry in journal: %m"); + return CURL_READFUNC_ABORT; + } else if (r == 0) { + if (u->input_event) + log_debug("No more entries, waiting for journal."); + else { + log_info("No more entries, closing journal."); + close_journal_input(u); + } + + u->uploading = false; + + break; + } + + u->entry_state = ENTRY_CURSOR; + } + + w = write_entry((char*)buf + filled, size * nmemb - filled, u); + if (w < 0) + return CURL_READFUNC_ABORT; + filled += w; + + if (filled == 0) { + log_error("Buffer space is too small to write entry."); + return CURL_READFUNC_ABORT; + } else if (u->entry_state != ENTRY_DONE) + /* This means that all available space was used up */ + break; + + log_debug("Entry %zu (%s) has been uploaded.", + u->entries_sent, u->current_cursor); + } + + return filled; +} + +void close_journal_input(Uploader *u) { + assert(u); + + if (u->journal) { + log_debug("Closing journal input."); + + sd_journal_close(u->journal); + u->journal = NULL; + } + u->timeout = 0; +} + +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"); + else if (r < skip) + return 0; + + /* have data */ + u->entry_state = ENTRY_CURSOR; + return start_upload(u, journal_input_callback, u); +} + +int check_journal_input(Uploader *u) { + if (u->input_event) { + int r; + + r = sd_journal_process(u->journal); + if (r < 0) { + log_error_errno(r, "Failed to process journal: %m"); + close_journal_input(u); + return r; + } + + if (r == SD_JOURNAL_NOP) + return 0; + } + + return process_journal_input(u, 1); +} + +static int dispatch_journal_input(sd_event_source *event, + int fd, + uint32_t revents, + void *userp) { + Uploader *u = userp; + + assert(u); + + if (u->uploading) + return 0; + + log_debug("Detected journal input, checking for new data."); + return check_journal_input(u); +} + +int open_journal_for_upload(Uploader *u, + sd_journal *j, + const char *cursor, + bool after_cursor, + bool follow) { + int fd, r, events; + + u->journal = j; + + sd_journal_set_data_threshold(j, 0); + + if (follow) { + fd = sd_journal_get_fd(j); + if (fd < 0) + return log_error_errno(fd, "sd_journal_get_fd failed: %m"); + + events = sd_journal_get_events(j); + + r = sd_journal_reliable_fd(j); + assert(r >= 0); + if (r > 0) + u->timeout = -1; + else + u->timeout = JOURNAL_UPLOAD_POLL_TIMEOUT; + + r = sd_event_add_io(u->events, &u->input_event, + fd, events, dispatch_journal_input, u); + if (r < 0) + return log_error_errno(r, "Failed to register input event: %m"); + + log_debug("Listening for journal events on fd:%d, timeout %d", + fd, u->timeout == (uint64_t) -1 ? -1 : (int) u->timeout); + } else + log_debug("Not listening for journal events."); + + if (cursor) { + r = sd_journal_seek_cursor(j, cursor); + if (r < 0) + return log_error_errno(r, "Failed to seek to cursor %s: %m", + cursor); + } + + return process_journal_input(u, 1 + !!after_cursor); +} diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.c b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.c new file mode 100644 index 0000000000..82d8331e76 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.c @@ -0,0 +1,881 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/glob-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/sigbus.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/conf-parser.h" + +#include "journal-upload.h" + +#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem" +#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem" +#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem" +#define DEFAULT_PORT 19532 + +static const char* arg_url = NULL; +static const char *arg_key = NULL; +static const char *arg_cert = NULL; +static const char *arg_trust = NULL; +static const char *arg_directory = NULL; +static char **arg_file = NULL; +static const char *arg_cursor = NULL; +static bool arg_after_cursor = false; +static int arg_journal_type = 0; +static const char *arg_machine = NULL; +static bool arg_merge = false; +static int arg_follow = -1; +static const char *arg_save_state = NULL; + +static void close_fd_input(Uploader *u); + +#define SERVER_ANSWER_KEEP 2048 + +#define STATE_FILE "/var/lib/systemd/journal-upload/state" + +#define easy_setopt(curl, opt, value, level, cmd) \ + do { \ + code = curl_easy_setopt(curl, opt, value); \ + if (code) { \ + log_full(level, \ + "curl_easy_setopt " #opt " failed: %s", \ + curl_easy_strerror(code)); \ + cmd; \ + } \ + } while (0) + +static size_t output_callback(char *buf, + size_t size, + size_t nmemb, + void *userp) { + Uploader *u = userp; + + assert(u); + + log_debug("The server answers (%zu bytes): %.*s", + size*nmemb, (int)(size*nmemb), buf); + + if (nmemb && !u->answer) { + u->answer = strndup(buf, size*nmemb); + if (!u->answer) + log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m", + size*nmemb); + } + + return size * nmemb; +} + +static int check_cursor_updating(Uploader *u) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + if (!u->state_file) + return 0; + + r = mkdir_parents(u->state_file, 0755); + if (r < 0) + return log_error_errno(r, "Cannot create parent directory of state file %s: %m", + u->state_file); + + r = fopen_temporary(u->state_file, &f, &temp_path); + if (r < 0) + return log_error_errno(r, "Cannot save state to %s: %m", + u->state_file); + unlink(temp_path); + + return 0; +} + +static int update_cursor_state(Uploader *u) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + if (!u->state_file || !u->last_cursor) + return 0; + + r = fopen_temporary(u->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fprintf(f, + "# This is private data. Do not parse.\n" + "LAST_CURSOR=%s\n", + u->last_cursor); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, u->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + if (temp_path) + (void) unlink(temp_path); + + (void) unlink(u->state_file); + + return log_error_errno(r, "Failed to save state %s: %m", u->state_file); +} + +static int load_cursor_state(Uploader *u) { + int r; + + if (!u->state_file) + return 0; + + r = parse_env_file(u->state_file, NEWLINE, + "LAST_CURSOR", &u->last_cursor, + NULL); + + if (r == -ENOENT) + log_debug("State file %s is not present.", u->state_file); + else if (r < 0) + return log_error_errno(r, "Failed to read state file %s: %m", + u->state_file); + else + log_debug("Last cursor was %s", u->last_cursor); + + return 0; +} + + + +int start_upload(Uploader *u, + size_t (*input_callback)(void *ptr, + size_t size, + size_t nmemb, + void *userdata), + void *data) { + CURLcode code; + + assert(u); + assert(input_callback); + + if (!u->header) { + struct curl_slist *h; + + h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal"); + if (!h) + return log_oom(); + + h = curl_slist_append(h, "Transfer-Encoding: chunked"); + if (!h) { + curl_slist_free_all(h); + return log_oom(); + } + + h = curl_slist_append(h, "Accept: text/plain"); + if (!h) { + curl_slist_free_all(h); + return log_oom(); + } + + u->header = h; + } + + if (!u->easy) { + CURL *curl; + + curl = curl_easy_init(); + if (!curl) { + log_error("Call to curl_easy_init failed."); + return -ENOSR; + } + + /* tell it to POST to the URL */ + easy_setopt(curl, CURLOPT_POST, 1L, + LOG_ERR, return -EXFULL); + + easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error, + LOG_ERR, return -EXFULL); + + /* set where to write to */ + easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback, + LOG_ERR, return -EXFULL); + + easy_setopt(curl, CURLOPT_WRITEDATA, data, + LOG_ERR, return -EXFULL); + + /* set where to read from */ + easy_setopt(curl, CURLOPT_READFUNCTION, input_callback, + LOG_ERR, return -EXFULL); + + easy_setopt(curl, CURLOPT_READDATA, data, + LOG_ERR, return -EXFULL); + + /* use our special own mime type and chunked transfer */ + easy_setopt(curl, CURLOPT_HTTPHEADER, u->header, + LOG_ERR, return -EXFULL); + + if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) + /* enable verbose for easier tracing */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, ); + + easy_setopt(curl, CURLOPT_USERAGENT, + "systemd-journal-upload " PACKAGE_STRING, + LOG_WARNING, ); + + if (arg_key || startswith(u->url, "https://")) { + easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE, + LOG_ERR, return -EXFULL); + easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE, + LOG_ERR, return -EXFULL); + } + + if (streq_ptr(arg_trust, "all")) + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0, + LOG_ERR, return -EUCLEAN); + else if (arg_trust || startswith(u->url, "https://")) + easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE, + LOG_ERR, return -EXFULL); + + if (arg_key || arg_trust) + easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1, + LOG_WARNING, ); + + u->easy = curl; + } else { + /* truncate the potential old error message */ + u->error[0] = '\0'; + + free(u->answer); + u->answer = 0; + } + + /* upload to this place */ + code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url); + if (code) { + log_error("curl_easy_setopt CURLOPT_URL failed: %s", + curl_easy_strerror(code)); + return -EXFULL; + } + + u->uploading = true; + + return 0; +} + +static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) { + Uploader *u = userp; + + ssize_t r; + + assert(u); + assert(nmemb <= SSIZE_MAX / size); + + if (u->input < 0) + return 0; + + r = read(u->input, buf, size * nmemb); + log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, r); + + if (r > 0) + return r; + + u->uploading = false; + if (r == 0) { + log_debug("Reached EOF"); + close_fd_input(u); + return 0; + } else { + log_error_errno(errno, "Aborting transfer after read error on input: %m."); + return CURL_READFUNC_ABORT; + } +} + +static void close_fd_input(Uploader *u) { + assert(u); + + if (u->input >= 0) + close_nointr(u->input); + u->input = -1; + u->timeout = 0; +} + +static int dispatch_fd_input(sd_event_source *event, + int fd, + uint32_t revents, + void *userp) { + Uploader *u = userp; + + assert(u); + assert(fd >= 0); + + if (revents & EPOLLHUP) { + log_debug("Received HUP"); + close_fd_input(u); + return 0; + } + + if (!(revents & EPOLLIN)) { + log_warning("Unexpected poll event %"PRIu32".", revents); + return -EINVAL; + } + + if (u->uploading) { + log_warning("dispatch_fd_input called when uploading, ignoring."); + return 0; + } + + return start_upload(u, fd_input_callback, u); +} + +static int open_file_for_upload(Uploader *u, const char *filename) { + int fd, r = 0; + + if (streq(filename, "-")) + fd = STDIN_FILENO; + else { + fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", filename); + } + + u->input = fd; + + if (arg_follow) { + r = sd_event_add_io(u->events, &u->input_event, + fd, EPOLLIN, dispatch_fd_input, u); + if (r < 0) { + if (r != -EPERM || arg_follow > 0) + return log_error_errno(r, "Failed to register input event: %m"); + + /* Normal files should just be consumed without polling. */ + r = start_upload(u, fd_input_callback, u); + } + } + + return r; +} + +static int dispatch_sigterm(sd_event_source *event, + const struct signalfd_siginfo *si, + void *userdata) { + Uploader *u = userdata; + + assert(u); + + log_received_signal(LOG_INFO, si); + + close_fd_input(u); + close_journal_input(u); + + sd_event_exit(u->events, 0); + return 0; +} + +static int setup_signals(Uploader *u) { + int r; + + assert(u); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0); + + r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u); + if (r < 0) + return r; + + r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u); + if (r < 0) + return r; + + return 0; +} + +static int setup_uploader(Uploader *u, const char *url, const char *state_file) { + int r; + const char *host, *proto = ""; + + assert(u); + assert(url); + + memzero(u, sizeof(Uploader)); + u->input = -1; + + if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) { + host = url; + proto = "https://"; + } + + if (strchr(host, ':')) + u->url = strjoin(proto, url, "/upload", NULL); + else { + char *t; + size_t x; + + t = strdupa(url); + x = strlen(t); + while (x > 0 && t[x - 1] == '/') + t[x - 1] = '\0'; + + u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL); + } + if (!u->url) + return log_oom(); + + u->state_file = state_file; + + r = sd_event_default(&u->events); + if (r < 0) + return log_error_errno(r, "sd_event_default failed: %m"); + + r = setup_signals(u); + if (r < 0) + return log_error_errno(r, "Failed to set up signals: %m"); + + (void) sd_watchdog_enabled(false, &u->watchdog_usec); + + return load_cursor_state(u); +} + +static void destroy_uploader(Uploader *u) { + assert(u); + + curl_easy_cleanup(u->easy); + curl_slist_free_all(u->header); + free(u->answer); + + free(u->last_cursor); + free(u->current_cursor); + + free(u->url); + + u->input_event = sd_event_source_unref(u->input_event); + + close_fd_input(u); + close_journal_input(u); + + sd_event_source_unref(u->sigterm_event); + sd_event_source_unref(u->sigint_event); + sd_event_unref(u->events); +} + +static int perform_upload(Uploader *u) { + CURLcode code; + long status; + + assert(u); + + u->watchdog_timestamp = now(CLOCK_MONOTONIC); + code = curl_easy_perform(u->easy); + if (code) { + if (u->error[0]) + log_error("Upload to %s failed: %.*s", + u->url, (int) sizeof(u->error), u->error); + else + log_error("Upload to %s failed: %s", + u->url, curl_easy_strerror(code)); + return -EIO; + } + + code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status); + if (code) { + log_error("Failed to retrieve response code: %s", + curl_easy_strerror(code)); + return -EUCLEAN; + } + + if (status >= 300) { + log_error("Upload to %s failed with code %ld: %s", + u->url, status, strna(u->answer)); + return -EIO; + } else if (status < 200) { + log_error("Upload to %s finished with unexpected code %ld: %s", + u->url, status, strna(u->answer)); + return -EIO; + } else + log_debug("Upload finished successfully with code %ld: %s", + status, strna(u->answer)); + + free(u->last_cursor); + u->last_cursor = u->current_cursor; + u->current_cursor = NULL; + + return update_cursor_state(u); +} + +static int parse_config(void) { + const ConfigTableItem items[] = { + { "Upload", "URL", config_parse_string, 0, &arg_url }, + { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key }, + { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert }, + { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, + {}}; + + return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf", + CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), + "Upload\0", config_item_table_lookup, items, + false, NULL); +} + +static void help(void) { + printf("%s -u URL {FILE|-}...\n\n" + "Upload journal events to a remote server.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -u --url=URL Upload to this address (default port " + STRINGIFY(DEFAULT_PORT) ")\n" + " --key=FILENAME Specify key in PEM format (default:\n" + " \"" PRIV_KEY_FILE "\")\n" + " --cert=FILENAME Specify certificate in PEM format (default:\n" + " \"" CERT_FILE "\")\n" + " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n" + " \"" TRUST_FILE "\")\n" + " --system Use the system journal\n" + " --user Use the user journal for the current user\n" + " -m --merge Use all available journals\n" + " -M --machine=CONTAINER Operate on local container\n" + " -D --directory=PATH Use journal files from directory\n" + " --file=PATH Use this journal file\n" + " --cursor=CURSOR Start at the specified cursor\n" + " --after-cursor=CURSOR Start after the specified cursor\n" + " --follow[=BOOL] Do [not] wait for input\n" + " --save-state[=FILE] Save uploaded cursors (default \n" + " " STATE_FILE ")\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_KEY, + ARG_CERT, + ARG_TRUST, + ARG_USER, + ARG_SYSTEM, + ARG_FILE, + ARG_CURSOR, + ARG_AFTER_CURSOR, + ARG_FOLLOW, + ARG_SAVE_STATE, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "url", required_argument, NULL, 'u' }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { "trust", required_argument, NULL, ARG_TRUST }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + { "merge", no_argument, NULL, 'm' }, + { "machine", required_argument, NULL, 'M' }, + { "directory", required_argument, NULL, 'D' }, + { "file", required_argument, NULL, ARG_FILE }, + { "cursor", required_argument, NULL, ARG_CURSOR }, + { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, + { "follow", optional_argument, NULL, ARG_FOLLOW }, + { "save-state", optional_argument, NULL, ARG_SAVE_STATE }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + opterr = 0; + + while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0 /* done */; + + case ARG_VERSION: + return version(); + + case 'u': + if (arg_url) { + log_error("cannot use more than one --url"); + return -EINVAL; + } + + arg_url = optarg; + break; + + case ARG_KEY: + if (arg_key) { + log_error("cannot use more than one --key"); + return -EINVAL; + } + + arg_key = optarg; + break; + + case ARG_CERT: + if (arg_cert) { + log_error("cannot use more than one --cert"); + return -EINVAL; + } + + arg_cert = optarg; + break; + + case ARG_TRUST: + if (arg_trust) { + log_error("cannot use more than one --trust"); + return -EINVAL; + } + + arg_trust = optarg; + break; + + case ARG_SYSTEM: + arg_journal_type |= SD_JOURNAL_SYSTEM; + break; + + case ARG_USER: + arg_journal_type |= SD_JOURNAL_CURRENT_USER; + break; + + case 'm': + arg_merge = true; + break; + + case 'M': + if (arg_machine) { + log_error("cannot use more than one --machine/-M"); + return -EINVAL; + } + + arg_machine = optarg; + break; + + case 'D': + if (arg_directory) { + log_error("cannot use more than one --directory/-D"); + return -EINVAL; + } + + arg_directory = optarg; + break; + + case ARG_FILE: + r = glob_extend(&arg_file, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); + break; + + case ARG_CURSOR: + if (arg_cursor) { + log_error("cannot use more than one --cursor/--after-cursor"); + return -EINVAL; + } + + arg_cursor = optarg; + break; + + case ARG_AFTER_CURSOR: + if (arg_cursor) { + log_error("cannot use more than one --cursor/--after-cursor"); + return -EINVAL; + } + + arg_cursor = optarg; + arg_after_cursor = true; + break; + + case ARG_FOLLOW: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --follow= parameter."); + return -EINVAL; + } + + arg_follow = !!r; + } else + arg_follow = true; + + break; + + case ARG_SAVE_STATE: + arg_save_state = optarg ?: STATE_FILE; + break; + + case '?': + log_error("Unknown option %s.", argv[optind-1]); + return -EINVAL; + + case ':': + log_error("Missing argument to %s.", argv[optind-1]); + return -EINVAL; + + default: + assert_not_reached("Unhandled option code."); + } + + if (!arg_url) { + log_error("Required --url/-u option missing."); + return -EINVAL; + } + + if (!!arg_key != !!arg_cert) { + log_error("Options --key and --cert must be used together."); + return -EINVAL; + } + + if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) { + log_error("Input arguments make no sense with journal input."); + return -EINVAL; + } + + return 1; +} + +static int open_journal(sd_journal **j) { + int r; + + if (arg_directory) + r = sd_journal_open_directory(j, arg_directory, arg_journal_type); + else if (arg_file) + r = sd_journal_open_files(j, (const char**) arg_file, 0); + else if (arg_machine) + r = sd_journal_open_container(j, arg_machine, 0); + 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"); + return r; +} + +int main(int argc, char **argv) { + Uploader u; + int r; + bool use_journal; + + log_show_color(true); + log_parse_environment(); + + r = parse_config(); + if (r < 0) + goto finish; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + sigbus_install(); + + r = setup_uploader(&u, arg_url, arg_save_state); + if (r < 0) + goto cleanup; + + sd_event_set_watchdog(u.events, true); + + r = check_cursor_updating(&u); + if (r < 0) + goto cleanup; + + log_debug("%s running as pid "PID_FMT, + program_invocation_short_name, getpid()); + + use_journal = optind >= argc; + if (use_journal) { + sd_journal *j; + r = open_journal(&j); + if (r < 0) + goto finish; + r = open_journal_for_upload(&u, j, + arg_cursor ?: u.last_cursor, + arg_cursor ? arg_after_cursor : true, + !!arg_follow); + if (r < 0) + goto finish; + } + + sd_notify(false, + "READY=1\n" + "STATUS=Processing input..."); + + for (;;) { + r = sd_event_get_state(u.events); + if (r < 0) + break; + if (r == SD_EVENT_FINISHED) + break; + + if (use_journal) { + if (!u.journal) + break; + + r = check_journal_input(&u); + } else if (u.input < 0 && !use_journal) { + if (optind >= argc) + break; + + log_debug("Using %s as input.", argv[optind]); + r = open_file_for_upload(&u, argv[optind++]); + } + if (r < 0) + goto cleanup; + + if (u.uploading) { + r = perform_upload(&u); + if (r < 0) + break; + } + + r = sd_event_run(u.events, u.timeout); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + break; + } + } + +cleanup: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + destroy_uploader(&u); + +finish: + return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.conf.in b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.conf.in new file mode 100644 index 0000000000..c5670682e8 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.conf.in @@ -0,0 +1,5 @@ +[Upload] +# URL= +# ServerKeyFile=@CERTIFICATEROOT@/private/journal-upload.pem +# ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-upload.pem +# TrustedCertificateFile=@CERTIFICATEROOT@/ca/trusted.pem diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.h b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.h new file mode 100644 index 0000000000..2decc7b22e --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/journal-upload.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include + +#include "basic/time-util.h" + +typedef enum { + ENTRY_CURSOR = 0, /* Nothing actually written yet. */ + ENTRY_REALTIME, + ENTRY_MONOTONIC, + ENTRY_BOOT_ID, + ENTRY_NEW_FIELD, /* In between fields. */ + ENTRY_TEXT_FIELD, /* In the middle of a text field. */ + ENTRY_BINARY_FIELD_START, /* Writing the name of a binary field. */ + ENTRY_BINARY_FIELD_SIZE, /* Writing the size of a binary field. */ + ENTRY_BINARY_FIELD, /* In the middle of a binary field. */ + ENTRY_OUTRO, /* Writing '\n' */ + ENTRY_DONE, /* Need to move to a new field. */ +} entry_state; + +typedef struct Uploader { + sd_event *events; + sd_event_source *sigint_event, *sigterm_event; + + char *url; + CURL *easy; + bool uploading; + char error[CURL_ERROR_SIZE]; + struct curl_slist *header; + char *answer; + + sd_event_source *input_event; + uint64_t timeout; + + /* fd stuff */ + int input; + + /* journal stuff */ + sd_journal* journal; + + entry_state entry_state; + const void *field_data; + size_t field_pos, field_length; + + /* general metrics */ + const char *state_file; + + size_t entries_sent; + char *last_cursor, *current_cursor; + usec_t watchdog_timestamp; + usec_t watchdog_usec; +} Uploader; + +#define JOURNAL_UPLOAD_POLL_TIMEOUT (10 * USEC_PER_SEC) + +int start_upload(Uploader *u, + size_t (*input_callback)(void *ptr, + size_t size, + size_t nmemb, + void *userdata), + void *data); + +int open_journal_for_upload(Uploader *u, + sd_journal *j, + const char *cursor, + bool after_cursor, + bool follow); +void close_journal_input(Uploader *u); +int check_journal_input(Uploader *u); diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in new file mode 100644 index 0000000000..1f488ff425 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Remote Upload Service +Documentation=man:systemd-journal-upload(8) +After=network.target + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-upload \ + --save-state +User=systemd-journal-upload +SupplementaryGroups=systemd-journal +PrivateTmp=yes +PrivateDevices=yes +WatchdogSec=3min + +# If there are many split up journal files we need a lot of fds to +# access them all and combine +LimitNOFILE=16384 + +[Install] +WantedBy=multi-user.target diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers new file mode 100644 index 0000000000..927d400279 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-journal-upload - "systemd Journal Upload" diff --git a/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.xml b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.xml new file mode 100644 index 0000000000..f9723dea89 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-journal-upload/systemd-journal-upload.xml @@ -0,0 +1,263 @@ + + + + + + + + + systemd-journal-upload + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-journal-upload + 8 + + + + systemd-journal-upload + Send journal messages over the network + + + + + systemd-journal-upload + OPTIONS + -u/--url=URL + SOURCES + + + + + Description + + + systemd-journal-upload will upload journal + entries to the URL specified with . Unless + limited by one of the options specified below, all journal + entries accessible to the user the program is running as will be + uploaded, and then the program will wait and send new entries + as they become available. + + + + + Options + + + + + + + + Upload to the specified + address. URL may specify either + just the hostname or both the protocol and + hostname. https is the default. + + + + + + + + Limit uploaded entries to entries from system + services and the kernel, or to entries from services of + current user. This has the same meaning as + and options + for + journalctl1. If + neither is specified, all accessible entries are uploaded. + + + + + + + + Upload entries interleaved from all available + journals, including other machines. This has the same meaning + as option for + journalctl1. + + + + + + + Takes a directory path as argument. Upload + entries from the specified journal directory + DIR instead of the default runtime + and system journal paths. This has the same meaning as + option for + journalctl1. + + + + + + + Takes a file glob as an argument. Upload + entries from the specified journal files matching + GLOB instead of the default runtime + and system journal paths. May be specified multiple times, in + which case files will be suitably interleaved. This has the same meaning as + option for + journalctl1. + + + + + + + Upload entries from the location in the + journal specified by the passed cursor. This has the same + meaning as option for + journalctl1. + + + + + + Upload entries from the location in the + journal after the location specified by + the this cursor. This has the same meaning as + option for + journalctl1. + + + + + + =PATH + + Upload entries from the location in the + journal after the location specified by + the cursor saved in file at PATH + (/var/lib/systemd/journal-upload/state by default). + After an entry is successfully uploaded, update this file + with the cursor of that entry. + + + + + + + + + + Exit status + + On success, 0 is returned; otherwise, a non-zero + failure code is returned. + + + + Examples + + Setting up certificates for authentication + + Certificates signed by a trusted authority are used to + verify that the server to which messages are uploaded is + legitimate, and vice versa, that the client is trusted. + + A suitable set of certificates can be generated with + openssl: + + openssl req -newkey rsa:2048 -days 3650 -x509 -nodes \ + -out ca.pem -keyout ca.key -subj '/CN=Certificate authority/' + +cat >ca.conf <<EOF +[ ca ] +default_ca = this + +[ this ] +new_certs_dir = . +certificate = ca.pem +database = ./index +private_key = ca.key +serial = ./serial +default_days = 3650 +default_md = default +policy = policy_anything + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional +EOF + +touch index +echo 0001 >serial + +SERVER=server +CLIENT=client + +openssl req -newkey rsa:1024 -nodes -out $SERVER.csr -keyout $SERVER.key -subj "/CN=$SERVER/" +openssl ca -batch -config ca.conf -notext -in $SERVER.csr -out $SERVER.pem + +openssl req -newkey rsa:1024 -nodes -out $CLIENT.csr -keyout $CLIENT.key -subj "/CN=$CLIENT/" +openssl ca -batch -config ca.conf -notext -in $CLIENT.csr -out $CLIENT.pem + + + Generated files ca.pem, + server.pem, and + server.key should be installed on server, + and ca.pem, + client.pem, and + client.key on the client. The location of + those files can be specified using + TrustedCertificateFile=, + ServerCertificateFile=, + ServerKeyFile=, in + /etc/systemd/journal-remote.conf and + /etc/systemd/journal-upload.conf, + respectively. The default locations can be queried by using + systemd-journal-remote --help and + systemd-journal-upload --help. + + + + + See Also + + systemd-journal-remote8, + journalctl1, + systemd-journald.service8, + systemd-journal-gatewayd.service8 + + + diff --git a/src/grp-journal/grp-remote/systemd-remote.tmpfiles b/src/grp-journal/grp-remote/systemd-remote.tmpfiles new file mode 100644 index 0000000000..e19230f648 --- /dev/null +++ b/src/grp-journal/grp-remote/systemd-remote.tmpfiles @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /var/lib/systemd/journal-upload 0755 systemd-journal-upload systemd-journal-upload - - + +z /var/log/journal/remote 2755 systemd-journal-remote systemd-journal-remote - - +z /run/log/journal/remote 2755 systemd-journal-remote systemd-journal-remote - - diff --git a/src/grp-journal/journal-nocow.tmpfiles b/src/grp-journal/journal-nocow.tmpfiles new file mode 100644 index 0000000000..e7938c8911 --- /dev/null +++ b/src/grp-journal/journal-nocow.tmpfiles @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Set the NOCOW attribute for directories of journal files. This flag +# is inheredited by their new files and sub-directories. Matters only +# for btrfs filesystems. +# +# WARNING: Enabling the NOCOW attribute improves journal performance +# substantially, but also disables the btrfs checksum logic. In +# btrfs RAID filesystems the checksums are needed for rebuilding +# corrupted files. Without checksums such rebuilds are not +# possible. +# +# In a single-disk filesystem (or a filesystem without redundancy) +# enabling the NOCOW attribute for journal files is safe, because +# they have their own checksums and a rebuilding wouldn't be possible +# in any case. + +h /var/log/journal - - - - +C +h /var/log/journal/%m - - - - +C +h /var/log/journal/remote - - - - +C diff --git a/src/grp-journal/journalctl/Makefile b/src/grp-journal/journalctl/Makefile new file mode 100644 index 0000000000..f8519405d2 --- /dev/null +++ b/src/grp-journal/journalctl/Makefile @@ -0,0 +1,61 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +# using _CFLAGS = in the conditional below would suppress AM_CFLAGS +journalctl_CFLAGS = \ + +journalctl_SOURCES = \ + src/journal/journalctl.c + +journalctl_LDADD = \ + libsystemd-shared.la \ + libudev-core.la + +ifneq ($(HAVE_QRENCODE),) +journalctl_SOURCES += \ + src/journal/journal-qrcode.c \ + src/journal/journal-qrcode.h + +journalctl_CFLAGS += \ + $(QRENCODE_CFLAGS) + +journalctl_LDADD += \ + $(QRENCODE_LIBS) +endif # HAVE_QRENCODE + +rootbin_PROGRAMS += \ + journalctl + +nodist_systemunit_DATA += \ + units/systemd-journal-flush.service \ + units/systemd-journal-catalog-update.service +SYSINIT_TARGET_WANTS += \ + systemd-journal-flush.service \ + systemd-journal-catalog-update.service +EXTRA_DIST += \ + units/systemd-journal-flush.service.in \ + units/systemd-journal-catalog-update.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/journalctl/journal-qrcode.c b/src/grp-journal/journalctl/journal-qrcode.c new file mode 100644 index 0000000000..e38730d65c --- /dev/null +++ b/src/grp-journal/journalctl/journal-qrcode.c @@ -0,0 +1,135 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "journal-qrcode.h" + +#define WHITE_ON_BLACK "\033[40;37;1m" +#define NORMAL "\033[0m" + +static void print_border(FILE *output, unsigned width) { + unsigned x, y; + + /* Four rows of border */ + for (y = 0; y < 4; y += 2) { + fputs(WHITE_ON_BLACK, output); + + for (x = 0; x < 4 + width + 4; x++) + fputs("\342\226\210", output); + + fputs(NORMAL "\n", output); + } +} + +int print_qr_code( + FILE *output, + const void *seed, + size_t seed_size, + uint64_t start, + uint64_t interval, + const char *hn, + sd_id128_t machine) { + + FILE *f; + char *url = NULL; + size_t url_size = 0, i; + QRcode* qr; + unsigned x, y; + + assert(seed); + assert(seed_size > 0); + + f = open_memstream(&url, &url_size); + if (!f) + return -ENOMEM; + + fputs("fss://", f); + + for (i = 0; i < seed_size; i++) { + if (i > 0 && i % 3 == 0) + fputc('-', f); + fprintf(f, "%02x", ((uint8_t*) seed)[i]); + } + + fprintf(f, "/%"PRIx64"-%"PRIx64"?machine=" SD_ID128_FORMAT_STR, + start, + interval, + SD_ID128_FORMAT_VAL(machine)); + + if (hn) + fprintf(f, ";hostname=%s", hn); + + if (ferror(f)) { + fclose(f); + free(url); + return -ENOMEM; + } + + fclose(f); + + qr = QRcode_encodeString(url, 0, QR_ECLEVEL_L, QR_MODE_8, 1); + free(url); + + if (!qr) + return -ENOMEM; + + print_border(output, qr->width); + + for (y = 0; y < (unsigned) qr->width; y += 2) { + const uint8_t *row1, *row2; + + row1 = qr->data + qr->width * y; + row2 = row1 + qr->width; + + fputs(WHITE_ON_BLACK, output); + for (x = 0; x < 4; x++) + fputs("\342\226\210", output); + + for (x = 0; x < (unsigned) qr->width; x ++) { + bool a, b; + + a = row1[x] & 1; + b = (y+1) < (unsigned) qr->width ? (row2[x] & 1) : false; + + if (a && b) + fputc(' ', output); + else if (a) + fputs("\342\226\204", output); + else if (b) + fputs("\342\226\200", output); + else + fputs("\342\226\210", output); + } + + for (x = 0; x < 4; x++) + fputs("\342\226\210", output); + fputs(NORMAL "\n", output); + } + + print_border(output, qr->width); + + QRcode_free(qr); + return 0; +} diff --git a/src/grp-journal/journalctl/journal-qrcode.h b/src/grp-journal/journalctl/journal-qrcode.h new file mode 100644 index 0000000000..34a779d5be --- /dev/null +++ b/src/grp-journal/journalctl/journal-qrcode.h @@ -0,0 +1,27 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +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/grp-journal/journalctl/journalctl.c b/src/grp-journal/journalctl/journalctl.c new file mode 100644 index 0000000000..e50c21d681 --- /dev/null +++ b/src/grp-journal/journalctl/journalctl.c @@ -0,0 +1,2617 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/chattr-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rlimit-util.h" +#include "basic/set.h" +#include "basic/sigbus.h" +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "sd-journal/catalog.h" +#include "sd-journal/fsprg.h" +#include "sd-journal/journal-def.h" +#include "sd-journal/journal-internal.h" +#include "sd-journal/journal-vacuum.h" +#include "sd-journal/journal-verify.h" +#include "shared/acl-util.h" +#include "shared/bus-util.h" +#include "shared/logs-show.h" +#include "shared/pager.h" +#include "shared/udev-util.h" +#include "udev.h" + +#include "journal-qrcode.h" + +#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) + +enum { + /* Special values for arg_lines */ + ARG_LINES_DEFAULT = -2, + ARG_LINES_ALL = -1, +}; + +static OutputMode arg_output = OUTPUT_SHORT; +static bool arg_utc = false; +static bool arg_pager_end = false; +static bool arg_follow = false; +static bool arg_full = true; +static bool arg_all = false; +static bool arg_no_pager = false; +static int arg_lines = ARG_LINES_DEFAULT; +static bool arg_no_tail = false; +static bool arg_quiet = false; +static bool arg_merge = false; +static bool arg_boot = false; +static sd_id128_t arg_boot_id = {}; +static int arg_boot_offset = 0; +static bool arg_dmesg = false; +static bool arg_no_hostname = false; +static const char *arg_cursor = NULL; +static const char *arg_after_cursor = NULL; +static bool arg_show_cursor = false; +static const char *arg_directory = NULL; +static char **arg_file = NULL; +static bool arg_file_stdin = false; +static int arg_priorities = 0xFF; +static const char *arg_verify_key = NULL; +#ifdef HAVE_GCRYPT +static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; +static bool arg_force = false; +#endif +static usec_t arg_since, arg_until; +static bool arg_since_set = false, arg_until_set = false; +static char **arg_syslog_identifier = NULL; +static char **arg_system_units = NULL; +static char **arg_user_units = NULL; +static const char *arg_field = NULL; +static bool arg_catalog = false; +static bool arg_reverse = false; +static int arg_journal_type = 0; +static char *arg_root = NULL; +static const char *arg_machine = NULL; +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, + ACTION_NEW_ID128, + ACTION_PRINT_HEADER, + ACTION_SETUP_KEYS, + ACTION_VERIFY, + ACTION_DISK_USAGE, + ACTION_LIST_CATALOG, + ACTION_DUMP_CATALOG, + ACTION_UPDATE_CATALOG, + ACTION_LIST_BOOTS, + ACTION_FLUSH, + ACTION_SYNC, + ACTION_ROTATE, + ACTION_VACUUM, + ACTION_LIST_FIELDS, + ACTION_LIST_FIELD_NAMES, +} arg_action = ACTION_SHOW; + +typedef struct BootId { + sd_id128_t id; + uint64_t first; + uint64_t last; + LIST_FIELDS(struct BootId, boot_list); +} BootId; + +static int add_matches_for_device(sd_journal *j, const char *devpath) { + int r; + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + struct udev_device *d = NULL; + struct stat st; + + assert(j); + assert(devpath); + + if (!path_startswith(devpath, "/dev/")) { + log_error("Devpath does not start with /dev/"); + return -EINVAL; + } + + udev = udev_new(); + if (!udev) + return log_oom(); + + r = stat(devpath, &st); + if (r < 0) + log_error_errno(errno, "Couldn't stat file: %m"); + + d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev); + if (!device) + return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); + + while (d) { + _cleanup_free_ char *match = NULL; + const char *subsys, *sysname, *devnode; + + subsys = udev_device_get_subsystem(d); + if (!subsys) { + d = udev_device_get_parent(d); + continue; + } + + sysname = udev_device_get_sysname(d); + if (!sysname) { + d = udev_device_get_parent(d); + continue; + } + + match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname, NULL); + if (!match) + return log_oom(); + + r = sd_journal_add_match(j, match, 0); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + + devnode = udev_device_get_devnode(d); + if (devnode) { + _cleanup_free_ char *match1 = NULL; + + r = stat(devnode, &st); + if (r < 0) + return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode); + + r = asprintf(&match1, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st.st_mode) ? 'b' : 'c', major(st.st_rdev), minor(st.st_rdev)); + if (r < 0) + return log_oom(); + + r = sd_journal_add_match(j, match1, 0); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + } + + d = udev_device_get_parent(d); + } + + r = add_match_this_boot(j, arg_machine); + if (r < 0) + return log_error_errno(r, "Failed to add match for the current boot: %m"); + + return 0; +} + +static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) { + + if (arg_utc) + return format_timestamp_utc(buf, l, t); + + return format_timestamp(buf, l, t); +} + +static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) { + sd_id128_t id = SD_ID128_NULL; + int off = 0, r; + + if (strlen(x) >= 32) { + char *t; + + t = strndupa(x, 32); + r = sd_id128_from_string(t, &id); + if (r >= 0) + x += 32; + + if (*x != '-' && *x != '+' && *x != 0) + return -EINVAL; + + if (*x != 0) { + r = safe_atoi(x, &off); + if (r < 0) + return r; + } + } else { + r = safe_atoi(x, &off); + if (r < 0) + return r; + } + + if (boot_id) + *boot_id = id; + + if (offset) + *offset = off; + + return 0; +} + +static void help(void) { + + pager_open(arg_no_pager, arg_pager_end); + + printf("%s [OPTIONS...] [MATCHES...]\n\n" + "Query the journal.\n\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" + " -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" + " -b --boot[=ID] Show current boot or the specified boot\n" + " --list-boots Show terse information about recorded boots\n" + " -k --dmesg Show kernel message log from the current boot\n" + " -u --unit=UNIT Show logs from the specified unit\n" + " --user-unit=UNIT Show logs from the specified user unit\n" + " -t --identifier=STRING Show entries with the specified syslog identifier\n" + " -p --priority=RANGE Show entries with the specified priority\n" + " -e --pager-end Immediately jump to the end in the pager\n" + " -f --follow Follow the journal\n" + " -n --lines[=INTEGER] Number of journal entries to show\n" + " --no-tail Show all lines, even in follow mode\n" + " -r --reverse Show the newest entries first\n" + " -o --output=STRING Change journal output mode (short, short-iso,\n" + " short-precise, short-monotonic, verbose,\n" + " export, json, json-pretty, json-sse, cat)\n" + " --utc Express time in Coordinated Universal Time (UTC)\n" + " -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 info messages and privilege warning\n" + " --no-pager Do not pipe output into a pager\n" + " --no-hostname Suppress output of hostname field\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 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" + " --force Override of the FSS key pair with --setup-keys\n" +#endif + "\nCommands:\n" + " -h --help Show this help text\n" + " --version Show package version\n" + " -N --fields List all field names currently used\n" + " -F --field=FIELD List all values that a specified field takes\n" + " --disk-usage Show total disk usage of all journal files\n" + " --vacuum-size=BYTES Reduce disk usage below specified size\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" +#endif + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_FULL, + ARG_NO_TAIL, + ARG_NEW_ID128, + ARG_THIS_BOOT, + ARG_LIST_BOOTS, + ARG_USER, + ARG_SYSTEM, + ARG_ROOT, + ARG_HEADER, + ARG_SETUP_KEYS, + ARG_FILE, + ARG_INTERVAL, + ARG_VERIFY, + ARG_VERIFY_KEY, + ARG_DISK_USAGE, + ARG_AFTER_CURSOR, + ARG_SHOW_CURSOR, + ARG_USER_UNIT, + ARG_LIST_CATALOG, + ARG_DUMP_CATALOG, + ARG_UPDATE_CATALOG, + ARG_FORCE, + ARG_UTC, + ARG_SYNC, + ARG_FLUSH, + ARG_ROTATE, + ARG_VACUUM_SIZE, + ARG_VACUUM_FILES, + ARG_VACUUM_TIME, + ARG_NO_HOSTNAME, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "pager-end", no_argument, NULL, 'e' }, + { "follow", no_argument, NULL, 'f' }, + { "force", no_argument, NULL, ARG_FORCE }, + { "output", required_argument, NULL, 'o' }, + { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, 'l' }, + { "no-full", no_argument, NULL, ARG_NO_FULL }, + { "lines", optional_argument, NULL, 'n' }, + { "no-tail", no_argument, NULL, ARG_NO_TAIL }, + { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, + { "quiet", no_argument, NULL, 'q' }, + { "merge", no_argument, NULL, 'm' }, + { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ + { "boot", optional_argument, NULL, 'b' }, + { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, + { "dmesg", no_argument, NULL, 'k' }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + { "directory", required_argument, NULL, 'D' }, + { "file", required_argument, NULL, ARG_FILE }, + { "root", required_argument, NULL, ARG_ROOT }, + { "header", no_argument, NULL, ARG_HEADER }, + { "identifier", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, + { "interval", required_argument, NULL, ARG_INTERVAL }, + { "verify", no_argument, NULL, ARG_VERIFY }, + { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, + { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, + { "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, 'S' }, + { "until", required_argument, NULL, 'U' }, + { "unit", required_argument, NULL, 'u' }, + { "user-unit", required_argument, NULL, ARG_USER_UNIT }, + { "field", required_argument, NULL, 'F' }, + { "fields", no_argument, NULL, 'N' }, + { "catalog", no_argument, NULL, 'x' }, + { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, + { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, + { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, + { "reverse", no_argument, NULL, 'r' }, + { "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 }, + { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'e': + arg_pager_end = true; + + if (arg_lines == ARG_LINES_DEFAULT) + arg_lines = 1000; + + break; + + case 'f': + arg_follow = true; + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output format '%s'.", optarg); + return -EINVAL; + } + + if (arg_output == OUTPUT_EXPORT || + arg_output == OUTPUT_JSON || + arg_output == OUTPUT_JSON_PRETTY || + arg_output == OUTPUT_JSON_SSE || + arg_output == OUTPUT_CAT) + arg_quiet = true; + + break; + + case 'l': + arg_full = true; + break; + + case ARG_NO_FULL: + arg_full = false; + break; + + case 'a': + arg_all = true; + break; + + case 'n': + if (optarg) { + if (streq(optarg, "all")) + arg_lines = ARG_LINES_ALL; + else { + r = safe_atoi(optarg, &arg_lines); + if (r < 0 || arg_lines < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + } + } else { + arg_lines = 10; + + /* Hmm, no argument? Maybe the next + * word on the command line is + * supposed to be the argument? Let's + * see if there is one, and is + * parsable. */ + if (optind < argc) { + int n; + if (streq(argv[optind], "all")) { + arg_lines = ARG_LINES_ALL; + optind++; + } else if (safe_atoi(argv[optind], &n) >= 0 && n >= 0) { + arg_lines = n; + optind++; + } + } + } + + break; + + case ARG_NO_TAIL: + arg_no_tail = true; + break; + + case ARG_NEW_ID128: + arg_action = ACTION_NEW_ID128; + break; + + case 'q': + arg_quiet = true; + break; + + case 'm': + arg_merge = true; + break; + + case ARG_THIS_BOOT: + arg_boot = true; + break; + + case 'b': + arg_boot = true; + + if (optarg) { + r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset); + if (r < 0) { + log_error("Failed to parse boot descriptor '%s'", optarg); + return -EINVAL; + } + } else { + + /* Hmm, no argument? Maybe the next + * word on the command line is + * supposed to be the argument? Let's + * see if there is one and is parsable + * as a boot descriptor... */ + + if (optind < argc && + parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset) >= 0) + optind++; + } + + break; + + case ARG_LIST_BOOTS: + arg_action = ACTION_LIST_BOOTS; + break; + + case 'k': + arg_boot = arg_dmesg = true; + break; + + case ARG_SYSTEM: + arg_journal_type |= SD_JOURNAL_SYSTEM; + break; + + case ARG_USER: + arg_journal_type |= SD_JOURNAL_CURRENT_USER; + break; + + case 'M': + arg_machine = optarg; + break; + + case 'D': + arg_directory = optarg; + break; + + case ARG_FILE: + if (streq(optarg, "-")) + /* An undocumented feature: we can read journal files from STDIN. We don't document + * this though, since after all we only support this for mmap-able, seekable files, and + * not for example pipes which are probably the primary usecase for reading things from + * STDIN. To avoid confusion we hence don't document this feature. */ + arg_file_stdin = true; + else { + r = glob_extend(&arg_file, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); + } + break; + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case 'c': + arg_cursor = optarg; + break; + + case ARG_AFTER_CURSOR: + arg_after_cursor = optarg; + break; + + case ARG_SHOW_CURSOR: + arg_show_cursor = true; + break; + + case ARG_HEADER: + arg_action = ACTION_PRINT_HEADER; + break; + + case ARG_VERIFY: + arg_action = ACTION_VERIFY; + break; + + case ARG_DISK_USAGE: + arg_action = ACTION_DISK_USAGE; + break; + + case ARG_VACUUM_SIZE: + r = parse_size(optarg, 1024, &arg_vacuum_size); + if (r < 0) { + log_error("Failed to parse vacuum size: %s", optarg); + return r; + } + + 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) { + log_error("Failed to parse vacuum time: %s", optarg); + return r; + } + + arg_action = ACTION_VACUUM; + break; + +#ifdef HAVE_GCRYPT + case ARG_FORCE: + arg_force = true; + break; + + case ARG_SETUP_KEYS: + arg_action = ACTION_SETUP_KEYS; + break; + + + case ARG_VERIFY_KEY: + arg_action = ACTION_VERIFY; + arg_verify_key = optarg; + arg_merge = false; + break; + + case ARG_INTERVAL: + r = parse_sec(optarg, &arg_interval); + if (r < 0 || arg_interval <= 0) { + log_error("Failed to parse sealing key change interval: %s", optarg); + return -EINVAL; + } + break; +#else + case ARG_SETUP_KEYS: + case ARG_VERIFY_KEY: + case ARG_INTERVAL: + case ARG_FORCE: + log_error("Forward-secure sealing not available."); + return -EOPNOTSUPP; +#endif + + case 'p': { + const char *dots; + + dots = strstr(optarg, ".."); + if (dots) { + char *a; + int from, to, i; + + /* a range */ + a = strndup(optarg, dots - optarg); + if (!a) + return log_oom(); + + from = log_level_from_string(a); + to = log_level_from_string(dots + 2); + free(a); + + if (from < 0 || to < 0) { + log_error("Failed to parse log level range %s", optarg); + return -EINVAL; + } + + arg_priorities = 0; + + if (from < to) { + for (i = from; i <= to; i++) + arg_priorities |= 1 << i; + } else { + for (i = to; i <= from; i++) + arg_priorities |= 1 << i; + } + + } else { + int p, i; + + p = log_level_from_string(optarg); + if (p < 0) { + log_error("Unknown log level %s", optarg); + return -EINVAL; + } + + arg_priorities = 0; + + for (i = 0; i <= p; i++) + arg_priorities |= 1 << i; + } + + break; + } + + case 'S': + r = parse_timestamp(optarg, &arg_since); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_since_set = true; + break; + + case 'U': + r = parse_timestamp(optarg, &arg_until); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_until_set = true; + break; + + case 't': + r = strv_extend(&arg_syslog_identifier, optarg); + if (r < 0) + return log_oom(); + break; + + case 'u': + r = strv_extend(&arg_system_units, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_USER_UNIT: + r = strv_extend(&arg_user_units, optarg); + if (r < 0) + return log_oom(); + break; + + case 'F': + arg_action = ACTION_LIST_FIELDS; + arg_field = optarg; + break; + + case 'N': + arg_action = ACTION_LIST_FIELD_NAMES; + break; + + case ARG_NO_HOSTNAME: + arg_no_hostname = true; + break; + + case 'x': + arg_catalog = true; + break; + + case ARG_LIST_CATALOG: + arg_action = ACTION_LIST_CATALOG; + break; + + case ARG_DUMP_CATALOG: + arg_action = ACTION_DUMP_CATALOG; + break; + + case ARG_UPDATE_CATALOG: + arg_action = ACTION_UPDATE_CATALOG; + break; + + case 'r': + arg_reverse = true; + break; + + case ARG_UTC: + arg_utc = true; + break; + + case ARG_FLUSH: + arg_action = ACTION_FLUSH; + break; + + case ARG_ROTATE: + arg_action = ACTION_ROTATE; + break; + + case ARG_SYNC: + arg_action = ACTION_SYNC; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT) + arg_lines = 10; + + if (!!arg_directory + !!arg_file + !!arg_machine > 1) { + log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one."); + return -EINVAL; + } + + if (arg_since_set && arg_until_set && arg_since > arg_until) { + log_error("--since= must be before --until=."); + return -EINVAL; + } + + if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) { + log_error("Please specify only one of --since=, --cursor=, and --after-cursor."); + return -EINVAL; + } + + if (arg_follow && arg_reverse) { + log_error("Please specify either --reverse= or --follow=, not both."); + return -EINVAL; + } + + 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; + } + + if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && arg_merge) { + log_error("Using --boot or --list-boots with --merge is not supported."); + return -EINVAL; + } + + if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) { + + /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user + * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice + * to users, and automatically turn --unit= into --user-unit= if combined with --user. */ + r = strv_extend_strv(&arg_user_units, arg_system_units, true); + if (r < 0) + return -ENOMEM; + + arg_system_units = strv_free(arg_system_units); + } + + return 1; +} + +static int generate_new_id128(void) { + sd_id128_t id; + int r; + unsigned i; + + r = sd_id128_randomize(&id); + if (r < 0) + return log_error_errno(r, "Failed to generate ID: %m"); + + printf("As string:\n" + SD_ID128_FORMAT_STR "\n\n" + "As UUID:\n" + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" + "As macro:\n" + "#define MESSAGE_XYZ SD_ID128_MAKE(", + SD_ID128_FORMAT_VAL(id), + SD_ID128_FORMAT_VAL(id)); + for (i = 0; i < 16; i++) + printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); + fputs(")\n\n", stdout); + + printf("As Python constant:\n" + ">>> import uuid\n" + ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n", + SD_ID128_FORMAT_VAL(id)); + + return 0; +} + +static int add_matches(sd_journal *j, char **args) { + char **i; + bool have_term = false; + + assert(j); + + STRV_FOREACH(i, args) { + int r; + + if (streq(*i, "+")) { + if (!have_term) + break; + r = sd_journal_add_disjunction(j); + have_term = false; + + } else if (path_is_absolute(*i)) { + _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL; + const char *path; + struct stat st; + + p = canonicalize_file_name(*i); + path = p ?: *i; + + if (lstat(path, &st) < 0) + return log_error_errno(errno, "Couldn't stat file: %m"); + + if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) { + if (executable_is_script(path, &interpreter) > 0) { + _cleanup_free_ char *comm; + + comm = strndup(basename(path), 15); + if (!comm) + return log_oom(); + + t = strappend("_COMM=", comm); + if (!t) + return log_oom(); + + /* Append _EXE only if the interpreter is not a link. + Otherwise, it might be outdated often. */ + if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) { + t2 = strappend("_EXE=", interpreter); + if (!t2) + return log_oom(); + } + } else { + t = strappend("_EXE=", path); + if (!t) + return log_oom(); + } + + r = sd_journal_add_match(j, t, 0); + + if (r >=0 && t2) + r = sd_journal_add_match(j, t2, 0); + + } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + r = add_matches_for_device(j, path); + if (r < 0) + return r; + } else { + log_error("File is neither a device node, nor regular file, nor executable: %s", *i); + return -EINVAL; + } + + have_term = true; + } else { + r = sd_journal_add_match(j, *i, 0); + have_term = true; + } + + if (r < 0) + return log_error_errno(r, "Failed to add match '%s': %m", *i); + } + + if (!strv_isempty(args) && !have_term) { + log_error("\"+\" can only be used between terms"); + return -EINVAL; + } + + return 0; +} + +static void boot_id_free_all(BootId *l) { + + while (l) { + BootId *i = l; + LIST_REMOVE(boot_list, l, i); + free(i); + } +} + +static int discover_next_boot(sd_journal *j, + sd_id128_t previous_boot_id, + bool advance_older, + BootId **ret) { + + _cleanup_free_ BootId *next_boot = NULL; + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; + + assert(j); + assert(ret); + + /* We expect the journal to be on the last position of a boot + * (in relation to the direction we are going), so that the next + * invocation of sd_journal_next/previous will be from a different + * boot. We then collect any information we desire and then jump + * to the last location of the new boot by using a _BOOT_ID match + * coming from the other journal direction. */ + + /* Make sure we aren't restricted by any _BOOT_ID matches, so that + * we can actually advance to a *different* boot. */ + sd_journal_flush_matches(j); + + do { + if (advance_older) + r = sd_journal_previous(j); + else + r = sd_journal_next(j); + if (r < 0) + return r; + else if (r == 0) + return 0; /* End of journal, yay. */ + + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r < 0) + return r; + + /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that + * normally, this will only require a single iteration, as we seeked to the last entry of the previous + * boot entry already. However, it might happen that the per-journal-field entry arrays are less + * complete than the main entry array, and hence might reference an entry that's not actually the last + * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to + * speed things up, but let's not trust that it is complete, and hence, manually advance as + * necessary. */ + + } while (sd_id128_equal(boot_id, previous_boot_id)); + + next_boot = new0(BootId, 1); + if (!next_boot) + return -ENOMEM; + + next_boot->id = boot_id; + + r = sd_journal_get_realtime_usec(j, &next_boot->first); + if (r < 0) + return r; + + /* Now seek to the last occurrence of this boot ID. */ + sd_id128_to_string(next_boot->id, match + 9); + r = sd_journal_add_match(j, match, sizeof(match) - 1); + if (r < 0) + return r; + + if (advance_older) + r = sd_journal_seek_head(j); + else + r = sd_journal_seek_tail(j); + if (r < 0) + return r; + + if (advance_older) + r = sd_journal_next(j); + else + r = sd_journal_previous(j); + if (r < 0) + return r; + else if (r == 0) + return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ + + r = sd_journal_get_realtime_usec(j, &next_boot->last); + if (r < 0) + return r; + + *ret = next_boot; + next_boot = NULL; + + return 0; +} + +static int get_boots( + sd_journal *j, + BootId **boots, + sd_id128_t *boot_id, + int offset) { + + bool skip_once; + int r, count = 0; + BootId *head = NULL, *tail = NULL; + const bool advance_older = boot_id && offset <= 0; + sd_id128_t previous_boot_id; + + assert(j); + + /* Adjust for the asymmetry that offset 0 is + * the last (and current) boot, while 1 is considered the + * (chronological) first boot in the journal. */ + skip_once = boot_id && sd_id128_is_null(*boot_id) && offset <= 0; + + /* Advance to the earliest/latest occurrence of our reference + * boot ID (taking our lookup direction into account), so that + * discover_next_boot() can do its job. + * If no reference is given, the journal head/tail will do, + * they're "virtual" boots after all. */ + if (boot_id && !sd_id128_is_null(*boot_id)) { + char match[9+32+1] = "_BOOT_ID="; + + sd_journal_flush_matches(j); + + sd_id128_to_string(*boot_id, match + 9); + r = sd_journal_add_match(j, match, sizeof(match) - 1); + if (r < 0) + return r; + + if (advance_older) + r = sd_journal_seek_head(j); /* seek to oldest */ + else + r = sd_journal_seek_tail(j); /* seek to newest */ + if (r < 0) + return r; + + if (advance_older) + r = sd_journal_next(j); /* read the oldest entry */ + else + r = sd_journal_previous(j); /* read the most recently added entry */ + if (r < 0) + return r; + else if (r == 0) + goto finish; + else if (offset == 0) { + count = 1; + goto finish; + } + + /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot + * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at + * the following entry, which must then have an older/newer boot ID */ + } else { + + if (advance_older) + r = sd_journal_seek_tail(j); /* seek to newest */ + else + r = sd_journal_seek_head(j); /* seek to oldest */ + if (r < 0) + return r; + + /* No sd_journal_next()/_previous() here. + * + * At this point the read pointer is positioned after the newest/before the oldest entry in the whole + * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest + * entry we have. */ + } + + previous_boot_id = SD_ID128_NULL; + for (;;) { + _cleanup_free_ BootId *current = NULL; + + r = discover_next_boot(j, previous_boot_id, advance_older, ¤t); + if (r < 0) { + boot_id_free_all(head); + return r; + } + + if (!current) + break; + + previous_boot_id = current->id; + + if (boot_id) { + if (!skip_once) + offset += advance_older ? 1 : -1; + skip_once = false; + + if (offset == 0) { + count = 1; + *boot_id = current->id; + break; + } + } else { + LIST_INSERT_AFTER(boot_list, head, tail, current); + tail = current; + current = NULL; + count++; + } + } + +finish: + if (boots) + *boots = head; + + sd_journal_flush_matches(j); + + return count; +} + +static int list_boots(sd_journal *j) { + int w, i, count; + BootId *id, *all_ids; + + assert(j); + + count = get_boots(j, &all_ids, NULL, 0); + if (count < 0) + return log_error_errno(count, "Failed to determine boots: %m"); + if (count == 0) + return count; + + pager_open(arg_no_pager, arg_pager_end); + + /* numbers are one less, but we need an extra char for the sign */ + w = DECIMAL_STR_WIDTH(count - 1) + 1; + + i = 0; + LIST_FOREACH(boot_list, id, all_ids) { + char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; + + printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n", + w, i - count + 1, + SD_ID128_FORMAT_VAL(id->id), + format_timestamp_maybe_utc(a, sizeof(a), id->first), + format_timestamp_maybe_utc(b, sizeof(b), id->last)); + i++; + } + + boot_id_free_all(all_ids); + + return 0; +} + +static int add_boot(sd_journal *j) { + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; + + assert(j); + + if (!arg_boot) + return 0; + + /* Take a shortcut and use the current boot_id, which we can do very quickly. + * We can do this only when we logs are coming from the current machine, + * so take the slow path if log location is specified. */ + if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) && + !arg_directory && !arg_file) + + return add_match_this_boot(j, arg_machine); + + boot_id = arg_boot_id; + r = get_boots(j, NULL, &boot_id, arg_boot_offset); + assert(r <= 1); + if (r <= 0) { + const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r); + + if (sd_id128_is_null(arg_boot_id)) + log_error("Data from the specified boot (%+i) is not available: %s", + arg_boot_offset, reason); + else + log_error("Data from the specified boot ("SD_ID128_FORMAT_STR") is not available: %s", + SD_ID128_FORMAT_VAL(arg_boot_id), reason); + + return r == 0 ? -ENODATA : r; + } + + sd_id128_to_string(boot_id, match + 9); + + r = sd_journal_add_match(j, match, sizeof(match) - 1); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + + r = sd_journal_add_conjunction(j); + if (r < 0) + return log_error_errno(r, "Failed to add conjunction: %m"); + + return 0; +} + +static int add_dmesg(sd_journal *j) { + int r; + assert(j); + + if (!arg_dmesg) + return 0; + + r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel")); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + + r = sd_journal_add_conjunction(j); + if (r < 0) + return log_error_errno(r, "Failed to add conjunction: %m"); + + return 0; +} + +static int get_possible_units( + sd_journal *j, + const char *fields, + char **patterns, + Set **units) { + + _cleanup_set_free_free_ Set *found; + const char *field; + int r; + + found = set_new(&string_hash_ops); + if (!found) + return -ENOMEM; + + NULSTR_FOREACH(field, fields) { + const void *data; + size_t size; + + r = sd_journal_query_unique(j, field); + if (r < 0) + return r; + + SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { + char **pattern, *eq; + size_t prefix; + _cleanup_free_ char *u = NULL; + + eq = memchr(data, '=', size); + if (eq) + prefix = eq - (char*) data + 1; + else + prefix = 0; + + u = strndup((char*) data + prefix, size - prefix); + if (!u) + return -ENOMEM; + + STRV_FOREACH(pattern, patterns) + if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) { + log_debug("Matched %s with pattern %s=%s", u, field, *pattern); + + r = set_consume(found, u); + u = NULL; + if (r < 0 && r != -EEXIST) + return r; + + break; + } + } + } + + *units = found; + found = NULL; + return 0; +} + +/* This list is supposed to return the superset of unit names + * possibly matched by rules added with add_matches_for_unit... */ +#define SYSTEM_UNITS \ + "_SYSTEMD_UNIT\0" \ + "COREDUMP_UNIT\0" \ + "UNIT\0" \ + "OBJECT_SYSTEMD_UNIT\0" \ + "_SYSTEMD_SLICE\0" + +/* ... and add_matches_for_user_unit */ +#define USER_UNITS \ + "_SYSTEMD_USER_UNIT\0" \ + "USER_UNIT\0" \ + "COREDUMP_USER_UNIT\0" \ + "OBJECT_SYSTEMD_USER_UNIT\0" + +static int add_units(sd_journal *j) { + _cleanup_strv_free_ char **patterns = NULL; + int r, count = 0; + char **i; + + assert(j); + + STRV_FOREACH(i, arg_system_units) { + _cleanup_free_ char *u = NULL; + + r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); + if (r < 0) + return r; + + if (string_is_glob(u)) { + r = strv_push(&patterns, u); + if (r < 0) + return r; + u = NULL; + } else { + r = add_matches_for_unit(j, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + count++; + } + } + + if (!strv_isempty(patterns)) { + _cleanup_set_free_free_ Set *units = NULL; + Iterator it; + char *u; + + r = get_possible_units(j, SYSTEM_UNITS, patterns, &units); + if (r < 0) + return r; + + SET_FOREACH(u, units, it) { + r = add_matches_for_unit(j, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + count++; + } + } + + patterns = strv_free(patterns); + + STRV_FOREACH(i, arg_user_units) { + _cleanup_free_ char *u = NULL; + + r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); + if (r < 0) + return r; + + if (string_is_glob(u)) { + r = strv_push(&patterns, u); + if (r < 0) + return r; + u = NULL; + } else { + r = add_matches_for_user_unit(j, u, getuid()); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + count++; + } + } + + if (!strv_isempty(patterns)) { + _cleanup_set_free_free_ Set *units = NULL; + Iterator it; + char *u; + + r = get_possible_units(j, USER_UNITS, patterns, &units); + if (r < 0) + return r; + + SET_FOREACH(u, units, it) { + r = add_matches_for_user_unit(j, u, getuid()); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + count++; + } + } + + /* Complain if the user request matches but nothing whatsoever was + * found, since otherwise everything would be matched. */ + if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0) + return -ENODATA; + + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + + return 0; +} + +static int add_priorities(sd_journal *j) { + char match[] = "PRIORITY=0"; + int i, r; + assert(j); + + if (arg_priorities == 0xFF) + return 0; + + for (i = LOG_EMERG; i <= LOG_DEBUG; i++) + if (arg_priorities & (1 << i)) { + match[sizeof(match)-2] = '0' + i; + + r = sd_journal_add_match(j, match, strlen(match)); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + } + + r = sd_journal_add_conjunction(j); + if (r < 0) + return log_error_errno(r, "Failed to add conjunction: %m"); + + return 0; +} + + +static int add_syslog_identifier(sd_journal *j) { + int r; + char **i; + + assert(j); + + STRV_FOREACH(i, arg_syslog_identifier) { + char *u; + + u = strjoina("SYSLOG_IDENTIFIER=", *i); + r = sd_journal_add_match(j, u, 0); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + } + + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + + return 0; +} + +static int setup_keys(void) { +#ifdef HAVE_GCRYPT + size_t mpk_size, seed_size, state_size, i; + uint8_t *mpk, *seed, *state; + int fd = -1, r; + sd_id128_t machine, boot; + char *p = NULL, *k = NULL; + struct FSSHeader h; + uint64_t n; + struct stat st; + + r = stat("/var/log/journal", &st); + if (r < 0 && errno != ENOENT && errno != ENOTDIR) + return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal"); + + if (r < 0 || !S_ISDIR(st.st_mode)) { + log_error("%s is not a directory, must be using persistent logging for FSS.", + "/var/log/journal"); + return r < 0 ? -errno : -ENOTDIR; + } + + r = sd_id128_get_machine(&machine); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + r = sd_id128_get_boot(&boot); + if (r < 0) + return log_error_errno(r, "Failed to get boot ID: %m"); + + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", + SD_ID128_FORMAT_VAL(machine)) < 0) + return log_oom(); + + if (arg_force) { + r = unlink(p); + if (r < 0 && errno != ENOENT) { + r = log_error_errno(errno, "unlink(\"%s\") failed: %m", p); + goto finish; + } + } else if (access(p, F_OK) >= 0) { + log_error("Sealing key file %s exists already. Use --force to recreate.", p); + r = -EEXIST; + goto finish; + } + + if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX", + SD_ID128_FORMAT_VAL(machine)) < 0) { + r = log_oom(); + goto finish; + } + + mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR); + mpk = alloca(mpk_size); + + seed_size = FSPRG_RECOMMENDED_SEEDLEN; + seed = alloca(seed_size); + + state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); + state = alloca(state_size); + + fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + r = log_error_errno(errno, "Failed to open /dev/random: %m"); + goto finish; + } + + log_info("Generating seed..."); + r = loop_read_exact(fd, seed, seed_size, true); + if (r < 0) { + log_error_errno(r, "Failed to read random seed: %m"); + goto finish; + } + + log_info("Generating key pair..."); + FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR); + + log_info("Generating sealing key..."); + FSPRG_GenState0(state, mpk, seed, seed_size); + + assert(arg_interval > 0); + + n = now(CLOCK_REALTIME); + n /= arg_interval; + + safe_close(fd); + fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + r = log_error_errno(fd, "Failed to open %s: %m", k); + goto finish; + } + + /* Enable secure remove, exclusion from dump, synchronous + * 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(r, "Failed to set file attributes: %m"); + + zero(h); + memcpy(h.signature, "KSHHRHLP", 8); + h.machine_id = machine; + h.boot_id = boot; + h.header_size = htole64(sizeof(h)); + h.start_usec = htole64(n * arg_interval); + h.interval_usec = htole64(arg_interval); + h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR); + h.fsprg_state_size = htole64(state_size); + + r = loop_write(fd, &h, sizeof(h), false); + if (r < 0) { + log_error_errno(r, "Failed to write header: %m"); + goto finish; + } + + r = loop_write(fd, state, state_size, false); + if (r < 0) { + log_error_errno(r, "Failed to write state: %m"); + goto finish; + } + + if (link(k, p) < 0) { + r = log_error_errno(errno, "Failed to link file: %m"); + goto finish; + } + + if (on_tty()) { + fprintf(stderr, + "\n" + "The new key pair has been generated. The %ssecret sealing key%s has been written to\n" + "the following local file. This key file is automatically updated when the\n" + "sealing key is advanced. It should not be used on multiple hosts.\n" + "\n" + "\t%s\n" + "\n" + "Please write down the following %ssecret verification key%s. It should be stored\n" + "at a safe location and should not be saved locally on disk.\n" + "\n\t%s", + ansi_highlight(), ansi_normal(), + ansi_highlight(), ansi_normal(), + ansi_highlight_red(), + p); + fflush(stderr); + } + for (i = 0; i < seed_size; i++) { + if (i > 0 && i % 3 == 0) + putchar('-'); + printf("%02x", ((uint8_t*) seed)[i]); + } + + printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval); + + if (on_tty()) { + char tsb[FORMAT_TIMESPAN_MAX], *hn; + + fprintf(stderr, + "%s\n" + "The sealing key is automatically changed every %s.\n", + ansi_normal(), + format_timespan(tsb, sizeof(tsb), arg_interval, 0)); + + hn = gethostname_malloc(); + + if (hn) { + hostname_cleanup(hn); + fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine)); + } else + fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine)); + +#ifdef HAVE_QRENCODE + /* If this is not an UTF-8 system don't print any QR codes */ + if (is_locale_utf8()) { + fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr); + print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine); + } +#endif + free(hn); + } + + r = 0; + +finish: + safe_close(fd); + + if (k) { + unlink(k); + free(k); + } + + free(p); + + return r; +#else + log_error("Forward-secure sealing not available."); + return -EOPNOTSUPP; +#endif +} + +static int verify(sd_journal *j) { + int r = 0; + Iterator i; + JournalFile *f; + + assert(j); + + log_show_color(true); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + int k; + usec_t first = 0, validated = 0, last = 0; + +#ifdef HAVE_GCRYPT + if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) + log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path); +#endif + + k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true); + if (k == -EINVAL) { + /* If the key was invalid give up right-away. */ + return k; + } else if (k < 0) { + 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]; + log_info("PASS: %s", f->path); + + if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) { + if (validated > 0) { + log_info("=> Validated from %s to %s, final %s entries not sealed.", + format_timestamp_maybe_utc(a, sizeof(a), first), + format_timestamp_maybe_utc(b, sizeof(b), validated), + format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0)); + } else if (last > 0) + log_info("=> No sealing yet, %s of entries not sealed.", + format_timespan(c, sizeof(c), last - first, 0)); + else + log_info("=> No sealing yet, no entries in file."); + } + } + } + + return r; +} + +static int access_check_var_log_journal(sd_journal *j) { +#ifdef HAVE_ACL + _cleanup_strv_free_ char **g = NULL; + const char* dir; +#endif + int r; + + assert(j); + + if (arg_quiet) + return 0; + + /* If we are root, we should have access, don't warn. */ + if (getuid() == 0) + return 0; + + /* If we are in the 'systemd-journal' group, we should have + * access too. */ + r = in_group("systemd-journal"); + if (r < 0) + return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); + if (r > 0) + return 0; + +#ifdef HAVE_ACL + if (laccess("/run/log/journal", F_OK) >= 0) + dir = "/run/log/journal"; + else + dir = "/var/log/journal"; + + /* If we are in any of the groups listed in the journal ACLs, + * then all is good, too. Let's enumerate all groups from the + * default ACL of the directory, which generally should allow + * access to most journal files too. */ + r = acl_search_groups(dir, &g); + if (r < 0) + return log_error_errno(r, "Failed to search journal ACL: %m"); + if (r > 0) + return 0; + + /* Print a pretty list, if there were ACLs set. */ + if (!strv_isempty(g)) { + _cleanup_free_ char *s = NULL; + + /* Thre are groups in the ACL, let's list them */ + r = strv_extend(&g, "systemd-journal"); + if (r < 0) + return log_oom(); + + strv_sort(g); + strv_uniq(g); + + s = strv_join(g, "', '"); + if (!s) + return log_oom(); + + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in groups '%s' can see all messages.\n" + " Pass -q to turn off this notice.", s); + return 1; + } +#endif + + /* If no ACLs were found, print a short version of the message. */ + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" + " turn off this notice."); + + return 1; +} + +static int access_check(sd_journal *j) { + Iterator it; + void *code; + char *path; + int r = 0; + + assert(j); + + if (hashmap_isempty(j->errors)) { + if (ordered_hashmap_isempty(j->files)) + log_notice("No journal files were found."); + + return 0; + } + + 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."); + } + + HASHMAP_FOREACH_KEY(path, code, j->errors, it) { + int err; + + err = abs(PTR_TO_INT(code)); + + switch (err) { + case EACCES: + continue; + + 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 or directory %s, ignoring file: %m", path); + break; + } + } + + return r; +} + +static int flush_to_var(void) { + _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_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", SIGUSR1); + 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); + + 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_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); + if (r < 0) + return log_error_errno(errno, "Failed to watch journal directory: %m"); + + for (;;) { + if (access("/run/systemd/journal/flushed", F_OK) >= 0) + break; + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to check for existence of /run/systemd/journal/flushed: %m"); + + 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 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_(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; + int n_shown = 0; + bool ellipsized = false; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + signal(SIGWINCH, columns_lines_cache_reset); + sigbus_install(); + + /* Increase max number of open files to 16K if we can, we + * might needs this when browsing journal files, which might + * be split up into many files. */ + setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); + + switch (arg_action) { + + case ACTION_NEW_ID128: + r = generate_new_id128(); + goto finish; + + case ACTION_SETUP_KEYS: + r = setup_keys(); + goto finish; + + case ACTION_LIST_CATALOG: + case ACTION_DUMP_CATALOG: + case ACTION_UPDATE_CATALOG: { + _cleanup_free_ char *database; + + database = path_join(arg_root, CATALOG_DATABASE, NULL); + if (!database) { + r = log_oom(); + goto finish; + } + + if (arg_action == ACTION_UPDATE_CATALOG) { + r = catalog_update(database, arg_root, catalog_file_dirs); + if (r < 0) + log_error_errno(r, "Failed to list catalog: %m"); + } else { + bool oneline = arg_action == ACTION_LIST_CATALOG; + + pager_open(arg_no_pager, arg_pager_end); + + if (optind < argc) + r = catalog_list_items(stdout, database, oneline, argv + optind); + else + r = catalog_list(stdout, database, oneline); + if (r < 0) + log_error_errno(r, "Failed to list catalog: %m"); + } + + 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: + case ACTION_LIST_FIELDS: + case ACTION_LIST_FIELD_NAMES: + /* 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_stdin) { + int ifd = STDIN_FILENO; + r = sd_journal_open_files_fd(&j, &ifd, 1, 0); + } else if (arg_file) + r = sd_journal_open_files(&j, (const char**) arg_file, 0); + else if (arg_machine) { + _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; + int fd; + + if (geteuid() != 0) { + /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of + * the container, thus we need root privileges to override them. */ + log_error("Using the --machine= switch requires root privileges."); + r = -EPERM; + goto finish; + } + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error_errno(r, "Failed to open system bus: %m"); + goto finish; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineRootDirectory", + &error, + &reply, + "s", arg_machine); + if (r < 0) { + log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r)); + goto finish; + } + + r = sd_bus_message_read(reply, "h", &fd); + if (r < 0) { + bus_log_parse_error(r); + goto finish; + } + + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) { + r = log_error_errno(errno, "Failed to duplicate file descriptor: %m"); + goto finish; + } + + r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT); + if (r < 0) + safe_close(fd); + } 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_file ? "files" : "journal"); + goto finish; + } + + r = access_check(j); + if (r < 0) + goto finish; + + switch (arg_action) { + + 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; + + case ACTION_VERIFY: + r = verify(j); + goto finish; + + case ACTION_DISK_USAGE: { + uint64_t bytes = 0; + char sbytes[FORMAT_BYTES_MAX]; + + r = sd_journal_get_usage(j, &bytes); + if (r < 0) + goto finish; + + printf("Archived and active journals take up %s on disk.\n", + format_bytes(sbytes, sizeof(sbytes), bytes)); + goto finish; + } + + case ACTION_LIST_BOOTS: + r = list_boots(j); + goto finish; + + case ACTION_VACUUM: { + Directory *d; + Iterator i; + + HASHMAP_FOREACH(d, j->directories_by_path, i) { + int q; + + if (d->is_root) + continue; + + 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 %s: %m", d->path); + r = q; + } + } + + goto finish; + } + + case ACTION_LIST_FIELD_NAMES: { + const char *field; + + SD_JOURNAL_FOREACH_FIELD(j, field) { + printf("%s\n", field); + n_shown++; + } + + r = 0; + goto finish; + } + + case ACTION_SHOW: + case ACTION_LIST_FIELDS: + break; + + default: + assert_not_reached("Unknown action"); + } + + if (arg_boot_offset != 0 && + sd_journal_has_runtime_files(j) > 0 && + sd_journal_has_persistent_files(j) == 0) { + log_info("Specifying boot ID has no effect, no persistent journal was found"); + r = 0; + goto finish; + } + /* add_boot() must be called first! + * It may need to seek the journal to find parent boot IDs. */ + r = add_boot(j); + if (r < 0) + goto finish; + + r = add_dmesg(j); + if (r < 0) + goto finish; + + r = add_units(j); + if (r < 0) { + log_error_errno(r, "Failed to add filter for units: %m"); + goto finish; + } + + r = add_syslog_identifier(j); + if (r < 0) { + log_error_errno(r, "Failed to add filter for syslog identifiers: %m"); + goto finish; + } + + r = add_priorities(j); + if (r < 0) + goto finish; + + r = add_matches(j, argv + optind); + if (r < 0) + goto finish; + + if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + _cleanup_free_ char *filter; + + filter = journal_make_match_string(j); + if (!filter) + return log_oom(); + + log_debug("Journal filter: %s", filter); + } + + if (arg_action == ACTION_LIST_FIELDS) { + const void *data; + size_t size; + + assert(arg_field); + + r = sd_journal_set_data_threshold(j, 0); + if (r < 0) { + log_error_errno(r, "Failed to unset data size threshold: %m"); + goto finish; + } + + r = sd_journal_query_unique(j, arg_field); + if (r < 0) { + log_error_errno(r, "Failed to query unique data objects: %m"); + goto finish; + } + + SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { + const void *eq; + + if (arg_lines >= 0 && n_shown >= arg_lines) + break; + + eq = memchr(data, '=', size); + if (eq) + printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1); + else + printf("%.*s\n", (int) size, (const char*) data); + + n_shown++; + } + + r = 0; + goto finish; + } + + /* Opening the fd now means the first sd_journal_wait() will actually wait */ + if (arg_follow) { + r = sd_journal_get_fd(j); + if (r == -EMEDIUMTYPE) { + log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); + goto finish; + } + if (r < 0) { + log_error_errno(r, "Failed to get journal fd: %m"); + goto finish; + } + } + + if (arg_cursor || arg_after_cursor) { + r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor); + if (r < 0) { + log_error_errno(r, "Failed to seek to cursor: %m"); + goto finish; + } + + if (!arg_reverse) + r = sd_journal_next_skip(j, 1 + !!arg_after_cursor); + else + r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor); + + if (arg_after_cursor && r < 2) { + /* We couldn't find the next entry after the cursor. */ + if (arg_follow) + need_seek = true; + else + arg_lines = 0; + } + + } else if (arg_since_set && !arg_reverse) { + r = sd_journal_seek_realtime_usec(j, arg_since); + if (r < 0) { + log_error_errno(r, "Failed to seek to date: %m"); + goto finish; + } + r = sd_journal_next(j); + + } else if (arg_until_set && arg_reverse) { + r = sd_journal_seek_realtime_usec(j, arg_until); + if (r < 0) { + log_error_errno(r, "Failed to seek to date: %m"); + goto finish; + } + r = sd_journal_previous(j); + + } else if (arg_lines >= 0) { + r = sd_journal_seek_tail(j); + if (r < 0) { + log_error_errno(r, "Failed to seek to tail: %m"); + goto finish; + } + + r = sd_journal_previous_skip(j, arg_lines); + + } else if (arg_reverse) { + r = sd_journal_seek_tail(j); + if (r < 0) { + log_error_errno(r, "Failed to seek to tail: %m"); + goto finish; + } + + r = sd_journal_previous(j); + + } else { + r = sd_journal_seek_head(j); + if (r < 0) { + log_error_errno(r, "Failed to seek to head: %m"); + goto finish; + } + + r = sd_journal_next(j); + } + + if (r < 0) { + log_error_errno(r, "Failed to iterate through journal: %m"); + goto finish; + } + if (r == 0) { + if (arg_follow) + need_seek = true; + else { + if (!arg_quiet) + printf("-- No entries --\n"); + goto finish; + } + } + + if (!arg_follow) + pager_open(arg_no_pager, arg_pager_end); + + if (!arg_quiet) { + usec_t start, end; + char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; + + r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); + if (r < 0) { + log_error_errno(r, "Failed to get cutoff: %m"); + goto finish; + } + + if (r > 0) { + if (arg_follow) + printf("-- Logs begin at %s. --\n", + format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start)); + else + printf("-- Logs begin at %s, end at %s. --\n", + format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start), + format_timestamp_maybe_utc(end_buf, sizeof(end_buf), end)); + } + } + + for (;;) { + while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) { + int flags; + + if (need_seek) { + if (!arg_reverse) + r = sd_journal_next(j); + else + r = sd_journal_previous(j); + if (r < 0) { + log_error_errno(r, "Failed to iterate through journal: %m"); + goto finish; + } + if (r == 0) + break; + } + + if (arg_until_set && !arg_reverse) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) { + log_error_errno(r, "Failed to determine timestamp: %m"); + goto finish; + } + if (usec > arg_until) + goto finish; + } + + if (arg_since_set && arg_reverse) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) { + log_error_errno(r, "Failed to determine timestamp: %m"); + goto finish; + } + if (usec < arg_since) + goto finish; + } + + if (!arg_merge && !arg_quiet) { + sd_id128_t boot_id; + + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r >= 0) { + if (previous_boot_id_valid && + !sd_id128_equal(boot_id, previous_boot_id)) + printf("%s-- Reboot --%s\n", + ansi_highlight(), ansi_normal()); + + previous_boot_id = boot_id; + previous_boot_id_valid = true; + } + } + + flags = + arg_all * OUTPUT_SHOW_ALL | + arg_full * OUTPUT_FULL_WIDTH | + colors_enabled() * OUTPUT_COLOR | + arg_catalog * OUTPUT_CATALOG | + arg_utc * OUTPUT_UTC | + arg_no_hostname * OUTPUT_NO_HOSTNAME; + + r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized); + need_seek = true; + if (r == -EADDRNOTAVAIL) + break; + else if (r < 0 || ferror(stdout)) + goto finish; + + n_shown++; + } + + if (!arg_follow) { + if (arg_show_cursor) { + _cleanup_free_ char *cursor = NULL; + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0 && r != -EADDRNOTAVAIL) + log_error_errno(r, "Failed to get cursor: %m"); + else if (r >= 0) + printf("-- cursor: %s\n", cursor); + } + + break; + } + + r = sd_journal_wait(j, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Couldn't wait for journal event: %m"); + goto finish; + } + + first_line = false; + } + +finish: + pager_close(); + + strv_free(arg_file); + + strv_free(arg_syslog_identifier); + strv_free(arg_system_units); + strv_free(arg_user_units); + + free(arg_root); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-journal/journalctl/journalctl.completion.bash b/src/grp-journal/journalctl/journalctl.completion.bash new file mode 100644 index 0000000000..53bedcd92e --- /dev/null +++ b/src/grp-journal/journalctl/journalctl.completion.bash @@ -0,0 +1,123 @@ +# journalctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + (machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager; echo ".host") | \ + { while read a b; do echo " $a"; done; } | sort -u; +} + +__syslog_priorities=(emerg alert crit err warning notice info debug) + +_journalctl() { + local field_vals= cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-a --all --full --system --user + --disk-usage -f --follow --header + -h --help -l --local --new-id128 -m --merge --no-pager + --no-tail -q --quiet --setup-keys --this-boot --verify + --version --list-catalog --update-catalog --list-boots + --show-cursor --dmesg -k --pager-end -e -r --reverse + --utc -x --catalog --no-full --force --dump-catalog + --flush --rotate --sync' + [ARG]='-b --boot --this-boot -D --directory --file -F --field + -M --machine -o --output -u --unit --user-unit -p --priority + --vacuum-size --vacuum-time' + [ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until + --after-cursor --verify-key -t --identifier + --root' + ) + + if __contains_word "$prev" ${OPTS[ARG]} ${OPTS[ARGUNKNOWN]}; then + case $prev in + --boot|--this-boot|-b) + comps=$(journalctl -F '_BOOT_ID' 2>/dev/null) + ;; + --directory|-D) + comps=$(compgen -d -- "$cur") + compopt -o filenames + ;; + --file) + comps=$(compgen -f -- "$cur") + compopt -o filenames + ;; + --output|-o) + comps='short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat' + ;; + --field|-F) + comps=$(journalctl --fields | sort 2>/dev/null) + ;; + --machine|-M) + comps=$( __get_machines ) + ;; + --priority|-p) + comps=${__syslog_priorities[*]} + ;; + --unit|-u) + comps=$(journalctl -F '_SYSTEMD_UNIT' 2>/dev/null) + ;; + --user-unit) + comps=$(journalctl -F '_SYSTEMD_USER_UNIT' 2>/dev/null) + ;; + *) + return 0 + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ $cur = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + elif [[ $cur = *=* ]]; then + mapfile -t field_vals < <(journalctl -F "${prev%=}" 2>/dev/null) + COMPREPLY=( $(compgen -W '${field_vals[*]}' -- "${cur#=}") ) + elif [[ $cur = /dev* ]]; then + compopt -o filenames + COMPREPLY=( $(compgen -f -- "${cur}") ) + elif [[ $cur = /* ]]; then + # Append /dev/ to the list of completions, so that + # after typing / the user sees /dev/ as one + # of the alternatives. Later on the rule above will + # take care of showing device files in /dev/. + mapfile -t field_vals < <(journalctl -F "_EXE" 2>/dev/null; echo '/dev/') + COMPREPLY=( $(compgen -W '${field_vals[*]}' -- "${cur}") ) + if [[ "${COMPREPLY[@]}" = '/dev/' ]]; then + compopt -o filenames + COMPREPLY=( $(compgen -f -- "${cur}") ) + fi + elif [[ $prev = '=' ]]; then + mapfile -t field_vals < <(journalctl -F "${COMP_WORDS[COMP_CWORD-2]}" 2>/dev/null) + COMPREPLY=( $(compgen -W '${field_vals[*]}' -- "$cur") ) + else + mapfile -t field_vals < <(journalctl --fields 2>/dev/null) + compopt -o nospace + COMPREPLY=( $(compgen -W '${field_vals[*]}' -S= -- "$cur") ) + fi +} + +complete -F _journalctl journalctl diff --git a/src/grp-journal/journalctl/journalctl.completion.zsh b/src/grp-journal/journalctl/journalctl.completion.zsh new file mode 100644 index 0000000000..2bee23b6d3 --- /dev/null +++ b/src/grp-journal/journalctl/journalctl.completion.zsh @@ -0,0 +1,98 @@ +#compdef journalctl + +_list_fields() { + local -a journal_fields + journal_fields=(MESSAGE{,_ID} PRIORITY CODE_{FILE,LINE,FUNC} + ERRNO SYSLOG_{FACILITY,IDENTIFIER,PID} + _{P,U,G}ID _COMM _EXE _CMDLINE + _AUDIT_{SESSION,LOGINUID} + _SYSTEMD_{CGROUP,SESSION,UNIT,OWNER_UID} + _SYSTEMD_USER_UNIT USER_UNIT + _SELINUX_CONTEXT _SOURCE_REALTIME_TIMESTAMP + _{BOOT,MACHINE}_ID _HOSTNAME _TRANSPORT + _KERNEL_{DEVICE,SUBSYSTEM} + _UDEV_{SYSNAME,DEVNODE,DEVLINK} + __CURSOR __{REALTIME,MONOTONIC}_TIMESTAMP) + case $_jrnl_none in + yes) _values -s '=' 'possible fields' \ + "${journal_fields[@]}:value:_journal_fields ${words[CURRENT]%%=*}" ;; + *) _describe 'possible fields' journal_fields ;; + esac +} + +_journal_none() { + local -a _commands _files _jrnl_none + # Setting use-cache will slow this down considerably + _commands=( ${"$(_call_program commands "$service" -F _EXE 2>/dev/null)"} ) + _jrnl_none='yes' + _alternative : \ + 'files:/dev files:_files -W /dev -P /dev/' \ + "commands:commands:($_commands[@])" \ + 'fields:fields:_list_fields' +} + +_journal_fields() { + local -a _fields cmd + cmd=("journalctl" "-F ${@[-1]}" "2>/dev/null" ) + _fields=$(_call_program fields $cmd[@]) + _fields=${_fields//'\'/'\\'} + _fields=${_fields//':'/'\:'} + _fields=( ${(f)_fields} ) + typeset -U _fields + _describe 'possible values' _fields +} + +_journal_boots() { + local -a _bootid _previousboots + _bootid=( ${(f)"$(_call_program bootid "$service -F _BOOT_ID")"} ) + _previousboots=( -{1..${#_bootid}} ) + _alternative : \ + "offsets:boot offsets:compadd -a '_previousboots[1,-2]'" \ + "bootid:boot ids:compadd -a _bootid" +} + +_arguments -s \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--no-pager[Do not pipe output into a pager]' \ + {-l,--full}'[Show long fields in full]' \ + {-a,--all}'[Show all fields, including long and unprintable]' \ + {-f,--follow}'[Follow journal]' \ + {-e,--pager-end}'[Jump to the end of the journal in the pager]' \ + {-n+,--lines=}'[Number of journal entries to show]:integer' \ + '--no-tail[Show all lines, even in follow mode]' \ + {-r,--reverse}'[Reverse output]' \ + {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \ + {-x,--catalog}'[Show explanatory texts with each log line]' \ + {-q,--quiet}"[Don't show privilege warning]" \ + {-m,--merge}'[Show entries from all available journals]' \ + {-b+,--boot=}'[Show data only from the specified boot or offset]::boot id or offset:_journal_boots' \ + '--list-boots[List boots ordered by time]' \ + {-k,--dmesg}'[Show only kernel messages from the current boot]' \ + {-u+,--unit=}'[Show data only from the specified unit]:units:_journal_fields _SYSTEMD_UNIT' \ + '--user-unit=[Show data only from the specified user session unit]:units:_journal_fields USER_UNIT' \ + {-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journal_fields PRIORITY' \ + {-t+,--identifier=}'[Show only messages with the specified syslog identifier]:identifier:_journal_fields SYSLOG_IDENTIFIER' \ + {-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journal_fields __CURSORS' \ + '--after-cursor=[Start showing entries from after the specified cursor]:cursors:_journal_fields __CURSORS' \ + '--since=[Start showing entries on or newer than the specified date]:YYYY-MM-DD HH\:MM\:SS' \ + '--until=[Stop showing entries on or older than the specified date]:YYYY-MM-DD HH\:MM\:SS' \ + {-F,--field=}'[List all values a certain field takes]:Fields:_list_fields' \ + '--system[Show system and kernel messages]' \ + '--user[Show messages from user services]' \ + {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \ + {-D+,--directory=}'[Show journal files from directory]:directories:_directories' \ + '--file=[Operate on specified journal files]:file:_files' \ + '--root=[Operate on catalog hierarchy under specified directory]:directories:_directories' \ + '--new-id128[Generate a new 128 Bit ID]' \ + '--header[Show journal header information]' \ + '--disk-usage[Show total disk usage]' \ + '--list-catalog[List messages in catalog]' \ + '--dump-catalog[Dump messages in catalog]' \ + '--update-catalog[Update binary catalog database]' \ + '--setup-keys[Generate a new FSS key pair]' \ + '--force[Force recreation of the FSS keys]' \ + '--interval=[Time interval for changing the FSS sealing key]:time interval' \ + '--verify[Verify journal file consistency]' \ + '--verify-key=[Specify FSS verification key]:FSS key' \ + '*::default: _journal_none' diff --git a/src/grp-journal/journalctl/journalctl.xml b/src/grp-journal/journalctl/journalctl.xml new file mode 100644 index 0000000000..e77621d7b3 --- /dev/null +++ b/src/grp-journal/journalctl/journalctl.xml @@ -0,0 +1,922 @@ + + + + + + + + + journalctl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + journalctl + 1 + + + + journalctl + Query the systemd journal + + + + + journalctl + OPTIONS + MATCHES + + + + + Description + + journalctl may be used to query the + contents of the + systemd1 + journal as written by + systemd-journald.service8. + + If called without parameters, it will show the full + contents of the journal, starting with the oldest entry + collected. + + If one or more match arguments are passed, the output is + filtered accordingly. A match is in the format + FIELD=VALUE, + e.g. _SYSTEMD_UNIT=httpd.service, referring + to the components of a structured journal entry. See + systemd.journal-fields7 + for a list of well-known fields. If multiple matches are + specified matching different fields, the log entries are + filtered by both, i.e. the resulting output will show only + entries matching all the specified matches of this kind. If two + matches apply to the same field, then they are automatically + matched as alternatives, i.e. the resulting output will show + entries matching any of the specified matches for the same + field. Finally, the character + may appear + as a separate word between other terms on the command line. This + causes all matches before and after to be combined in a + disjunction (i.e. logical OR). + + It is also possible to filter the entries by specifying an + absolute file path as an argument. The file path may be a file or + a symbolic link and the file must exist at the time of the query. If a + file path refers to an executable binary, an _EXE= + match for the canonicalized binary path is added to the query. If a + file path refers to an executable script, a _COMM= + match for the script name is added to the query. If a file path + refers to a device node, _KERNEL_DEVICE= matches for + the kernel name of the device and for each of its ancestor devices is + added to the query. Symbolic links are dereferenced, kernel names are + synthesized, and parent devices are identified from the environment at + the time of the query. In general, a device node is the best proxy for + an actual device, as log entries do not usually contain fields that + identify an actual device. For the resulting log entries to be correct + for the actual device, the relevant parts of the environment at the time + the entry was logged, in particular the actual device corresponding to + the device node, must have been the same as those at the time of the + query. Because device nodes generally change their corresponding devices + across reboots, specifying a device node path causes the resulting + entries to be restricted to those from the current boot. + + Additional constraints may be added using options + , , etc., to + further limit what entries will be shown (logical AND). + + Output is interleaved from all accessible journal files, + whether they are rotated or currently being written, and + regardless of whether they belong to the system itself or are + accessible user journals. + + The set of journal files which will be used can be + modified using the , + , , and + options, see below. + + All users are granted access to their private per-user + journals. However, by default, only root and users who are + members of a few special groups are granted access to the system + journal and the journals of other users. Members of the groups + systemd-journal, adm, and + wheel can read all journal files. Note + that the two latter groups traditionally have additional + privileges specified by the distribution. Members of the + wheel group can often perform administrative + tasks. + + The output is paged through less by + default, and long lines are "truncated" to screen width. The + hidden part can be viewed by using the left-arrow and + right-arrow keys. Paging can be disabled; see the + option and the "Environment" section + below. + + When outputting to a tty, lines are colored according to + priority: lines of level ERROR and higher are colored red; lines + of level NOTICE and higher are highlighted; other lines are + displayed normally. + + + + Options + + The following options are understood: + + + + + + + + Ellipsize fields when they do not fit in + available columns. The default is to show full fields, + allowing them to wrap or be truncated by the pager, if one + is used. + + The old options + / are not useful + anymore, except to undo . + + + + + + + + Show all fields in full, even if they + include unprintable characters or are very + long. + + + + + + + Show only the most recent journal entries, + and continuously print new entries as they are appended to + the journal. + + + + + + + Immediately jump to the end of the journal + inside the implied pager tool. This implies + to guarantee that the pager will not + buffer logs of unbounded size. This may be overridden with + an explicit with some other numeric + value, while will disable this cap. + Note that this option is only supported for the + less1 + pager. + + + + + + + Show the most recent journal events and + limit the number of events shown. If + is used, this option is + implied. The argument is a positive integer or + all to disable line limiting. The default + value is 10 if no argument is given. + + + + + + Show all stored output lines, even in follow + mode. Undoes the effect of . + + + + + + + + Reverse output so that the newest entries + are displayed first. + + + + + + + Controls the formatting of the journal + entries that are shown. Takes one of the following + options: + + + + + + + is the default and generates an output that is + mostly identical to the formatting of classic syslog + files, showing one line per journal entry. + + + + + + + + + is very similar, but shows ISO 8601 wallclock + timestamps. + + + + + + + + + is very similar, but shows timestamps with full + microsecond precision. + + + + + + + + + is very similar, but shows monotonic timestamps + instead of wallclock timestamps. + + + + + + + + + is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock + timestamps ("UNIX time"). The time is shown with microsecond accuracy. + + + + + + + + + shows the full-structured entry items with all + fields. + + + + + + + + + serializes the journal into a binary (but mostly + text-based) stream suitable for backups and network + transfer (see + Journal Export Format + for more information). + + + + + + + + + formats entries as JSON data structures, one per + line (see + Journal JSON Format + for more information). + + + + + + + + + formats entries as JSON data structures, but + formats them in multiple lines in order to make them + more readable by humans. + + + + + + + + + formats entries as JSON data structures, but wraps + them in a format suitable for + Server-Sent Events. + + + + + + + + + + generates a very terse output, only showing the + actual message of each journal entry with no metadata, + not even a timestamp. + + + + + + + + + + Express time in Coordinated Universal Time + (UTC). + + + + + + Don't show the hostname field of log messages originating from the local host. This switch only + has an effect on the family of output modes (see above). + + + + + + + Augment log lines with explanation texts from + the message catalog. This will add explanatory help texts to + log messages in the output where this is available. These + short help texts will explain the context of an error or log + event, possible solutions, as well as pointers to support + forums, developer documentation, and any other relevant + manuals. Note that help texts are not available for all + messages, but only for selected ones. For more information on + the message catalog, please refer to the + Message Catalog Developer Documentation. + + Note: when attaching journalctl + output to bug reports, please do not use + . + + + + + + + + Suppresses all info messages + (i.e. "-- Logs begin at ...", "-- Reboot --"), + any warning messages regarding + inaccessible system journals when run as a normal + user. + + + + + + + Show entries interleaved from all available + journals, including remote ones. + + + + + + + Show messages from a specific boot. This will + add a match for _BOOT_ID=. + + The argument may be empty, in which case logs for the + current boot will be shown. + + If the boot ID is omitted, a positive + offset will look up the boots + starting from the beginning of the journal, and an + equal-or-less-than zero offset will + look up boots starting from the end of the journal. Thus, + 1 means the first boot found in the + journal in chronological order, 2 the + second and so on; while -0 is the last + boot, -1 the boot before last, and so + on. An empty offset is equivalent + to specifying -0, except when the current + boot is not the last boot (e.g. because + was specified to look at logs + from a different machine). + + If the 32-character ID is + specified, it may optionally be followed by + offset which identifies the boot + relative to the one given by boot + ID. Negative values mean earlier + boots and positive values mean later boots. If + offset is not specified, a value of + zero is assumed, and the logs for the boot given by + ID are shown. + + + + + + + Show a tabular list of boot numbers (relative to + the current boot), their IDs, and the timestamps of the first + and last message pertaining to the boot. + + + + + + + Show only kernel messages. This implies + and adds the match + _TRANSPORT=kernel. + + + + + + + Show messages for the specified syslog + identifier + SYSLOG_IDENTIFIER. + + This parameter can be specified multiple + times. + + + + + + + Show messages for the specified systemd unit + UNIT (such as a service unit), or + for any of the units matched by + PATTERN. If a pattern is + specified, a list of unit names found in the journal is + compared with the specified pattern and all that match are + used. For each unit name, a match is added for messages from + the unit + (_SYSTEMD_UNIT=UNIT), + along with additional matches for messages from systemd and + messages about coredumps for the specified unit. + + This parameter can be specified multiple times. + + + + + + + Show messages for the specified user session + unit. This will add a match for messages from the unit + (_SYSTEMD_USER_UNIT= and + _UID=) and additional matches for messages + from session systemd and messages about coredumps for the + specified unit. + + This parameter can be specified multiple times. + + + + + + + + Filter output by message priorities or + priority ranges. Takes either a single numeric or textual log + level (i.e. between 0/emerg and + 7/debug), or a range of numeric/text log + levels in the form FROM..TO. The log levels are the usual + syslog log levels as documented in + syslog3, + i.e. emerg (0), + alert (1), crit (2), + err (3), warning (4), + notice (5), info (6), + debug (7). If a single log level is + specified, all messages with this log level or a lower (hence + more important) log level are shown. If a range is specified, + all messages within the range are shown, including both the + start and the end value of the range. This will add + PRIORITY= matches for the specified + priorities. + + + + + + + Start showing entries from the location in the + journal specified by the passed cursor. + + + + + + Start showing entries from the location in the + journal after the location specified by + the passed cursor. The cursor is shown when the + option is used. + + + + + + + The cursor is shown after the last entry after + two dashes: + -- cursor: s=0639... + The format of the cursor is private + and subject to change. + + + + + + + + + Start showing entries on or newer than the + specified date, or on or older than the specified date, + respectively. Date specifications should be of the format + 2012-10-30 18:17:16. If the time part is + omitted, 00:00:00 is assumed. If only the + seconds component is omitted, :00 is + assumed. If the date component is omitted, the current day is + assumed. Alternatively the strings + yesterday, today, + tomorrow are understood, which refer to + 00:00:00 of the day before the current day, the current day, + or the day after the current day, + respectively. now refers to the current + time. Finally, relative times may be specified, prefixed with + - or +, referring to + times before or after the current time, respectively. For complete + time and date specification, see + systemd.time7. + + + + + + + + + Print all possible data values the specified + field can take in all entries of the journal. + + + + + + + Print all field names currently used in all entries of the journal. + + + + + + + Show messages from system services and the + kernel (with ). Show messages from + service of current user (with ). If + neither is specified, show all messages that the user can see. + + + + + + + + Show messages from a running, local + container. Specify a container name to connect to. + + + + + + + + Takes a directory path as argument. If + specified, journalctl will operate on the specified journal + directory DIR instead of the + default runtime and system journal paths. + + + + + + Takes a file glob as an argument. If + specified, journalctl will operate on the specified journal + files matching GLOB instead of the + default runtime and system journal paths. May be specified + multiple times, in which case files will be suitably + interleaved. + + + + + + Takes a directory path as an argument. If + specified, journalctl will operate on catalog file hierarchy + underneath the specified directory instead of the root + directory (e.g. will create + ROOT/var/lib/systemd/catalog/database). + + + + + + + Instead of showing journal contents, generate + a new 128-bit ID suitable for identifying messages. This is + intended for usage by developers who need a new identifier for + a new message they introduce and want to make + recognizable. This will print the new ID in three different + formats which can be copied into source code or similar. + + + + + + + Instead of showing journal contents, show + internal header information of the journal fields + accessed. + + + + + + Shows the current disk usage of all journal + files. This shows the sum of the disk usage of all archived + and active journal files. + + + + + + + + Removes archived journal files until the disk + space they use falls below the specified size (specified with + the usual K, M, + G and T suffixes), or all + archived journal files contain no data older than the specified + timespan (specified with the usual s, + m, h, + days, months, + weeks and years suffixes), + or no more than the specified number of separate journal files + remain. Note that running has + only an indirect effect on the output shown by + , as the latter includes active + journal files, while the vacuuming operation only operates + on archived journal files. Similarly, + might not actually reduce the + number of journal files to below the specified number, as it + will not remove active journal + files. , + and + may be combined in a single + invocation to enforce any combination of a size, a time and a + number of files limit on the archived journal + files. Specifying any of these three parameters as zero is + equivalent to not enforcing the specific limit, and is thus + redundant. + + + + + + List the contents of the message catalog as a + table of message IDs, plus their short description strings. + + + If any 128-bit-IDs are + specified, only those entries are shown. + + + + + + + Show the contents of the message catalog, with + entries separated by a line consisting of two dashes and the + ID (the format is the same as .catalog + files). + + If any 128-bit-IDs are + specified, only those entries are shown. + + + + + + + Update the message catalog index. This command + needs to be executed each time new catalog files are + installed, removed, or updated to rebuild the binary catalog + index. + + + + + + Instead of showing journal contents, generate + a new key pair for Forward Secure Sealing (FSS). This will + generate a sealing key and a verification key. The sealing key + is stored in the journal data directory and shall remain on + the host. The verification key should be stored + externally. Refer to the option in + journald.conf5 + for information on Forward Secure Sealing and for a link to a + refereed scholarly paper detailing the cryptographic theory it + is based on. + + + + + + When is passed + and Forward Secure Sealing (FSS) has already been configured, + recreate FSS keys. + + + + + + Specifies the change interval for the sealing + key when generating an FSS key pair with + . Shorter intervals increase CPU + consumption but shorten the time range of undetectable journal + alterations. Defaults to 15min. + + + + + + Check the journal file for internal + consistency. If the file has been generated with FSS enabled and + the FSS verification key has been specified with + , authenticity of the journal file + is verified. + + + + + + Specifies the FSS verification key to use for + the operation. + + + + + + Asks the journal daemon to write all yet + unwritten journal data to the backing file system and + synchronize all journals. This call does not return until the + synchronization operation is complete. This command guarantees + that any log messages written before its invocation are safely + stored on disk at the time it returns. + + + + + + Asks the journal daemon to flush any log data + stored in /run/log/journal into + /var/log/journal, if persistent storage + is enabled. This call does not return until the operation is + complete. Note that this call is idempotent: the data is only + flushed from /run/log/journal into + /var/log/journal once during system + runtime, and this command exits cleanly without executing any + operation if this has already happened. This command + effectively guarantees that all data is flushed to + /var/log/journal at the time it + returns. + + + + + + Asks the journal daemon to rotate journal + files. This call does not return until the rotation operation + is complete. + + + + + + + + + + Exit status + + On success, 0 is returned; otherwise, a non-zero failure + code is returned. + + + + + + Examples + + Without arguments, all collected logs are shown + unfiltered: + + journalctl + + With one match specified, all entries with a field matching + the expression are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service + + If two different fields are matched, only entries matching + both expressions at the same time are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + + If two matches refer to the same field, all entries matching + either expression are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _SYSTEMD_UNIT=dbus.service + + If the separator + is used, two + expressions may be combined in a logical OR. The following will + show all messages from the Avahi service process with the PID + 28097 plus all messages from the D-Bus service (from any of its + processes): + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + _SYSTEMD_UNIT=dbus.service + + Show all logs generated by the D-Bus executable: + + journalctl /usr/bin/dbus-daemon + + Show all kernel logs from previous boot: + + journalctl -k -b -1 + + Show a live log display from a system service + apache.service: + + journalctl -f -u apache + + + + + See Also + + systemd1, + systemd-journald.service8, + systemctl1, + coredumpctl1, + systemd.journal-fields7, + journald.conf5, + systemd.time7 + + + diff --git a/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in b/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in new file mode 100644 index 0000000000..6370dd478f --- /dev/null +++ b/src/grp-journal/journalctl/systemd-journal-catalog-update.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Rebuild Journal Catalog +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target systemd-update-done.service +ConditionNeedsUpdate=/etc + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootbindir@/journalctl --update-catalog +TimeoutSec=90s diff --git a/src/grp-journal/journalctl/systemd-journal-flush.service.in b/src/grp-journal/journalctl/systemd-journal-flush.service.in new file mode 100644 index 0000000000..a0a2e3fdb4 --- /dev/null +++ b/src/grp-journal/journalctl/systemd-journal-flush.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Flush Journal to Persistent Storage +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Requires=systemd-journald.service +After=systemd-journald.service +After=systemd-remount-fs.service +Before=systemd-user-sessions.service systemd-tmpfiles-setup.service +RequiresMountsFor=/var/log/journal + +[Service] +ExecStart=@rootbindir@/journalctl --flush +Type=oneshot +RemainAfterExit=yes +TimeoutSec=90s diff --git a/src/grp-journal/libjournal-core/.gitignore b/src/grp-journal/libjournal-core/.gitignore new file mode 100644 index 0000000000..04d5852547 --- /dev/null +++ b/src/grp-journal/libjournal-core/.gitignore @@ -0,0 +1,4 @@ +/journald-gperf.c +/libsystemd-journal.pc +/audit_type-list.txt +/audit_type-*-name.* diff --git a/src/grp-journal/libjournal-core/Makefile b/src/grp-journal/libjournal-core/Makefile new file mode 100644 index 0000000000..daa6458376 --- /dev/null +++ b/src/grp-journal/libjournal-core/Makefile @@ -0,0 +1,56 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libjournal_core_la_SOURCES = \ + src/journal/journald-kmsg.c \ + src/journal/journald-kmsg.h \ + src/journal/journald-syslog.c \ + src/journal/journald-syslog.h \ + src/journal/journald-stream.c \ + src/journal/journald-stream.h \ + src/journal/journald-server.c \ + src/journal/journald-server.h \ + src/journal/journald-console.c \ + src/journal/journald-console.h \ + src/journal/journald-wall.c \ + src/journal/journald-wall.h \ + src/journal/journald-native.c \ + src/journal/journald-native.h \ + src/journal/journald-audit.c \ + src/journal/journald-audit.h \ + src/journal/journald-rate-limit.c \ + src/journal/journald-rate-limit.h \ + src/journal/journal-internal.h + +nodist_libjournal_core_la_SOURCES = \ + src/journal/journald-gperf.c + +libjournal_core_la_LIBADD = \ + libsystemd-shared.la + +noinst_LTLIBRARIES += \ + libjournal-core.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/libjournal-core/journald-audit.c b/src/grp-journal/libjournal-core/journald-audit.c new file mode 100644 index 0000000000..74ad0c017e --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-audit.c @@ -0,0 +1,565 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/io-util.h" +#include "basic/missing.h" +#include "basic/string-util.h" +#include "sd-journal/audit-type.h" + +#include "journald-audit.h" + +typedef struct MapField { + const char *audit_field; + const char *journal_field; + int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov); +} MapField; + +static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + _cleanup_free_ char *c = NULL; + size_t l = 0, allocated = 0; + const char *e; + + assert(field); + assert(p); + assert(iov); + assert(n_iov); + + l = strlen(field); + allocated = l + 1; + c = malloc(allocated); + if (!c) + return -ENOMEM; + + memcpy(c, field, l); + for (e = *p; *e != ' ' && *e != 0; e++) { + if (!GREEDY_REALLOC(c, allocated, l+2)) + return -ENOMEM; + + c[l++] = *e; + } + + c[l] = 0; + + if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) + return -ENOMEM; + + (*iov)[*n_iov].iov_base = c; + (*iov)[*n_iov].iov_len = l; + (*n_iov)++; + + *p = e; + c = NULL; + + return 1; +} + +static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) { + _cleanup_free_ char *c = NULL; + const char *s, *e; + size_t l; + + assert(field); + assert(p); + assert(iov); + assert(n_iov); + + /* The kernel formats string fields in one of two formats. */ + + if (**p == '"') { + /* Normal quoted syntax */ + s = *p + 1; + e = strchr(s, '"'); + if (!e) + return 0; + + l = strlen(field) + (e - s); + c = malloc(l+1); + if (!c) + return -ENOMEM; + + *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0; + + e += 1; + + } else if (unhexchar(**p) >= 0) { + /* Hexadecimal escaping */ + size_t allocated = 0; + + l = strlen(field); + allocated = l + 2; + c = malloc(allocated); + if (!c) + return -ENOMEM; + + memcpy(c, field, l); + for (e = *p; *e != ' ' && *e != 0; e += 2) { + int a, b; + uint8_t x; + + a = unhexchar(e[0]); + if (a < 0) + return 0; + + b = unhexchar(e[1]); + if (b < 0) + return 0; + + x = ((uint8_t) a << 4 | (uint8_t) b); + + if (filter_printable && x < (uint8_t) ' ') + x = (uint8_t) ' '; + + if (!GREEDY_REALLOC(c, allocated, l+2)) + return -ENOMEM; + + c[l++] = (char) x; + } + + c[l] = 0; + } else + return 0; + + if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) + return -ENOMEM; + + (*iov)[*n_iov].iov_base = c; + (*iov)[*n_iov].iov_len = l; + (*n_iov)++; + + *p = e; + c = NULL; + + return 1; +} + +static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false); +} + +static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true); +} + +static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + const char *e, *f; + char *c, *t; + int r; + + /* Implements fallback mappings for all fields we don't know */ + + for (e = *p; e < *p + 16; e++) { + + if (*e == 0 || *e == ' ') + return 0; + + if (*e == '=') + break; + + if (!((*e >= 'a' && *e <= 'z') || + (*e >= 'A' && *e <= 'Z') || + (*e >= '0' && *e <= '9') || + *e == '_' || *e == '-')) + return 0; + } + + if (e <= *p || e >= *p + 16) + return 0; + + c = alloca(strlen(prefix) + (e - *p) + 2); + + t = stpcpy(c, prefix); + for (f = *p; f < e; f++) { + char x; + + if (*f >= 'a' && *f <= 'z') + x = (*f - 'a') + 'A'; /* uppercase */ + else if (*f == '-') + x = '_'; /* dashes → underscores */ + else + x = *f; + + *(t++) = x; + } + strcpy(t, "="); + + e++; + + r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov); + if (r < 0) + return r; + + *p = e; + return r; +} + +/* Kernel fields are those occurring in the audit string before + * msg='. All of these fields are trusted, hence carry the "_" prefix. + * We try to translate the fields we know into our native names. The + * other's are generically mapped to _AUDIT_FIELD_XYZ= */ +static const MapField map_fields_kernel[] = { + + /* First, we map certain well-known audit fields into native + * well-known fields */ + { "pid=", "_PID=", map_simple_field }, + { "ppid=", "_PPID=", map_simple_field }, + { "uid=", "_UID=", map_simple_field }, + { "euid=", "_EUID=", map_simple_field }, + { "fsuid=", "_FSUID=", map_simple_field }, + { "gid=", "_GID=", map_simple_field }, + { "egid=", "_EGID=", map_simple_field }, + { "fsgid=", "_FSGID=", map_simple_field }, + { "tty=", "_TTY=", map_simple_field }, + { "ses=", "_AUDIT_SESSION=", map_simple_field }, + { "auid=", "_AUDIT_LOGINUID=", map_simple_field }, + { "subj=", "_SELINUX_CONTEXT=", map_simple_field }, + { "comm=", "_COMM=", map_string_field }, + { "exe=", "_EXE=", map_string_field }, + { "proctitle=", "_CMDLINE=", map_string_field_printable }, + + /* Some fields don't map to native well-known fields. However, + * we know that they are string fields, hence let's undo + * string field escaping for them, though we stick to the + * generic field names. */ + { "path=", "_AUDIT_FIELD_PATH=", map_string_field }, + { "dev=", "_AUDIT_FIELD_DEV=", map_string_field }, + { "name=", "_AUDIT_FIELD_NAME=", map_string_field }, + {} +}; + +/* Userspace fields are those occurring in the audit string after + * msg='. All of these fields are untrusted, hence carry no "_" + * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */ +static const MapField map_fields_userspace[] = { + { "cwd=", "AUDIT_FIELD_CWD=", map_string_field }, + { "cmd=", "AUDIT_FIELD_CMD=", map_string_field }, + { "acct=", "AUDIT_FIELD_ACCT=", map_string_field }, + { "exe=", "AUDIT_FIELD_EXE=", map_string_field }, + { "comm=", "AUDIT_FIELD_COMM=", map_string_field }, + {} +}; + +static int map_all_fields( + const char *p, + const MapField map_fields[], + const char *prefix, + bool handle_msg, + struct iovec **iov, + size_t *n_iov_allocated, + unsigned *n_iov) { + + int r; + + assert(p); + assert(iov); + assert(n_iov_allocated); + assert(n_iov); + + for (;;) { + bool mapped = false; + const MapField *m; + const char *v; + + p += strspn(p, WHITESPACE); + + if (*p == 0) + return 0; + + if (handle_msg) { + v = startswith(p, "msg='"); + if (v) { + const char *e; + char *c; + + /* Userspace message. It's enclosed in + simple quotation marks, is not + escaped, but the last field in the + line, hence let's remove the + quotation mark, and apply the + userspace mapping instead of the + kernel mapping. */ + + e = endswith(v, "'"); + if (!e) + return 0; /* don't continue splitting up if the final quotation mark is missing */ + + c = strndupa(v, e - v); + return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov); + } + } + + /* Try to map the kernel fields to our own names */ + for (m = map_fields; m->audit_field; m++) { + v = startswith(p, m->audit_field); + if (!v) + continue; + + r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov); + if (r < 0) + return log_debug_errno(r, "Failed to parse audit array: %m"); + + if (r > 0) { + mapped = true; + p = v; + break; + } + } + + if (!mapped) { + r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov); + if (r < 0) + return log_debug_errno(r, "Failed to parse audit array: %m"); + + if (r == 0) + /* Couldn't process as generic field, let's just skip over it */ + p += strcspn(p, WHITESPACE); + } + } +} + +static void process_audit_string(Server *s, int type, const char *data, size_t size) { + _cleanup_free_ struct iovec *iov = NULL; + size_t n_iov_allocated = 0; + unsigned n_iov = 0, k; + uint64_t seconds, msec, id; + const char *p, *type_name; + unsigned z; + char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)], + type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)], + source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)]; + char *m; + + assert(s); + + if (size <= 0) + return; + + if (!data) + return; + + /* Note that the input buffer is NUL terminated, but let's + * check whether there is a spurious NUL byte */ + if (memchr(data, 0, size)) + return; + + p = startswith(data, "audit"); + if (!p) + return; + + if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n", + &seconds, + &msec, + &id, + &k) != 3) + return; + + p += k; + p += strspn(p, WHITESPACE); + + if (isempty(p)) + return; + + n_iov_allocated = N_IOVEC_META_FIELDS + 7; + iov = new(struct iovec, n_iov_allocated); + if (!iov) { + log_oom(); + return; + } + + IOVEC_SET_STRING(iov[n_iov++], "_TRANSPORT=audit"); + + sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64, + (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC); + IOVEC_SET_STRING(iov[n_iov++], source_time_field); + + sprintf(type_field, "_AUDIT_TYPE=%i", type); + IOVEC_SET_STRING(iov[n_iov++], type_field); + + sprintf(id_field, "_AUDIT_ID=%" PRIu64, id); + IOVEC_SET_STRING(iov[n_iov++], id_field); + + assert_cc(4 == LOG_FAC(LOG_AUTH)); + IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=4"); + IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit"); + + type_name = audit_type_name_alloca(type); + + m = strjoina("MESSAGE=", type_name, " ", p); + IOVEC_SET_STRING(iov[n_iov++], m); + + z = n_iov; + + map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov); + + if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) { + log_oom(); + goto finish; + } + + server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0); + +finish: + /* free() all entries that map_all_fields() added. All others + * are allocated on the stack or are constant. */ + + for (; z < n_iov; z++) + free(iov[z].iov_base); +} + +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) { + + const struct nlmsghdr *nl = buffer; + + assert(s); + + if (buffer_size < ALIGN(sizeof(struct nlmsghdr))) + return; + + assert(buffer); + + /* Filter out fake data */ + if (!sa || + salen != sizeof(struct sockaddr_nl) || + sa->nl.nl_family != AF_NETLINK || + sa->nl.nl_pid != 0) { + log_debug("Audit netlink message from invalid sender."); + return; + } + + if (!ucred || ucred->pid != 0) { + log_debug("Audit netlink message with invalid credentials."); + return; + } + + if (!NLMSG_OK(nl, buffer_size)) { + log_error("Audit netlink message truncated."); + return; + } + + /* Ignore special Netlink messages */ + if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR)) + return; + + /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */ + if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG) + return; + + process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr))); +} + +static int enable_audit(int fd, bool b) { + struct { + union { + struct nlmsghdr header; + uint8_t header_space[NLMSG_HDRLEN]; + }; + struct audit_status body; + } _packed_ request = { + .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)), + .header.nlmsg_type = AUDIT_SET, + .header.nlmsg_flags = NLM_F_REQUEST, + .header.nlmsg_seq = 1, + .header.nlmsg_pid = 0, + .body.mask = AUDIT_STATUS_ENABLED, + .body.enabled = b, + }; + union sockaddr_union sa = { + .nl.nl_family = AF_NETLINK, + .nl.nl_pid = 0, + }; + struct iovec iovec = { + .iov_base = &request, + .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)), + }; + struct msghdr mh = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &sa.sa, + .msg_namelen = sizeof(sa.nl), + }; + + ssize_t n; + + n = sendmsg(fd, &mh, MSG_NOSIGNAL); + if (n < 0) + return -errno; + if (n != NLMSG_LENGTH(sizeof(struct audit_status))) + return -EIO; + + /* We don't wait for the result here, we can't do anything + * about it anyway */ + + return 0; +} + +int server_open_audit(Server *s) { + static const int one = 1; + int r; + + if (s->audit_fd < 0) { + static const union sockaddr_union sa = { + .nl.nl_family = AF_NETLINK, + .nl.nl_pid = 0, + .nl.nl_groups = AUDIT_NLGRP_READLOG, + }; + + s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); + if (s->audit_fd < 0) { + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + log_debug("Audit not supported in the kernel."); + else + log_warning_errno(errno, "Failed to create audit socket, ignoring: %m"); + + return 0; + } + + if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) { + log_warning_errno(errno, + "Failed to join audit multicast group. " + "The kernel is probably too old or multicast reading is not supported. " + "Ignoring: %m"); + s->audit_fd = safe_close(s->audit_fd); + return 0; + } + } else + fd_nonblock(s->audit_fd, 1); + + r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m"); + + r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s); + if (r < 0) + return log_error_errno(r, "Failed to add audit fd to event loop: %m"); + + /* We are listening now, try to enable audit */ + r = enable_audit(s->audit_fd, true); + if (r < 0) + log_warning_errno(r, "Failed to issue audit enable call: %m"); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/journald-audit.h b/src/grp-journal/libjournal-core/journald-audit.h new file mode 100644 index 0000000000..98c07e95e4 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-audit.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/socket-util.h" + +#include "journald-server.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); + +int server_open_audit(Server*s); diff --git a/src/grp-journal/libjournal-core/journald-console.c b/src/grp-journal/libjournal-core/journald-console.c new file mode 100644 index 0000000000..b1e6828dc2 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-console.c @@ -0,0 +1,116 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/terminal-util.h" + +#include "journald-console.h" +#include "journald-server.h" + +static bool prefix_timestamp(void) { + + static int cached_printk_time = -1; + + if (_unlikely_(cached_printk_time < 0)) { + _cleanup_free_ char *p = NULL; + + cached_printk_time = + read_one_line_file("/sys/module/printk/parameters/time", &p) >= 0 + && parse_boolean(p) > 0; + } + + return cached_printk_time; +} + +void server_forward_console( + Server *s, + int priority, + const char *identifier, + const char *message, + const struct ucred *ucred) { + + struct iovec iovec[5]; + struct timespec ts; + char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1]; + char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)]; + int n = 0, fd; + _cleanup_free_ char *ident_buf = NULL; + const char *tty; + + assert(s); + assert(message); + + if (LOG_PRI(priority) > s->max_level_console) + return; + + /* First: timestamp */ + if (prefix_timestamp()) { + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ", + ts.tv_sec, + ts.tv_nsec / 1000); + IOVEC_SET_STRING(iovec[n++], tbuf); + } + + /* Second: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Fourth: message */ + IOVEC_SET_STRING(iovec[n++], message); + IOVEC_SET_STRING(iovec[n++], "\n"); + + tty = s->tty_path ? s->tty_path : "/dev/console"; + + fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + log_debug_errno(fd, "Failed to open %s for logging: %m", tty); + return; + } + + if (writev(fd, iovec, n) < 0) + log_debug_errno(errno, "Failed to write to %s for logging: %m", tty); + + safe_close(fd); +} diff --git a/src/grp-journal/libjournal-core/journald-console.h b/src/grp-journal/libjournal-core/journald-console.h new file mode 100644 index 0000000000..dda07e2c28 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-console.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-server.h" + +void server_forward_console(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/grp-journal/libjournal-core/journald-gperf.gperf b/src/grp-journal/libjournal-core/journald-gperf.gperf new file mode 100644 index 0000000000..7c5c68862a --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-gperf.gperf @@ -0,0 +1,48 @@ +%{ +#include +#include + +#include "shared/conf-parser.h" + +#include "journald-server.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name journald_gperf_hash +%define lookup-function-name journald_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Journal.Storage, config_parse_storage, 0, offsetof(Server, storage) +Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) +Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) +Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) +# The following is a legacy name for compatibility +Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) +Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval) +Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) +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) +Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Server, forward_to_kmsg) +Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Server, forward_to_console) +Journal.ForwardToWall, config_parse_bool, 0, offsetof(Server, forward_to_wall) +Journal.TTYPath, config_parse_path, 0, offsetof(Server, tty_path) +Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(Server, max_level_store) +Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Server, max_level_syslog) +Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Server, max_level_kmsg) +Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Server, max_level_console) +Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Server, max_level_wall) +Journal.SplitMode, config_parse_split_mode, 0, offsetof(Server, split_mode) diff --git a/src/grp-journal/libjournal-core/journald-kmsg.c b/src/grp-journal/libjournal-core/journald-kmsg.c new file mode 100644 index 0000000000..946318c184 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-kmsg.c @@ -0,0 +1,474 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" + +#include "journald-kmsg.h" +#include "journald-server.h" +#include "journald-syslog.h" + +void server_forward_kmsg( + Server *s, + int priority, + const char *identifier, + const char *message, + const struct ucred *ucred) { + + struct iovec iovec[5]; + char header_priority[DECIMAL_STR_MAX(priority) + 3], + header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1]; + int n = 0; + char *ident_buf = NULL; + + assert(s); + assert(priority >= 0); + assert(priority <= 999); + assert(message); + + if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg)) + return; + + if (_unlikely_(s->dev_kmsg_fd < 0)) + return; + + /* Never allow messages with kernel facility to be written to + * kmsg, regardless where the data comes from. */ + priority = syslog_fixup_facility(priority); + + /* First: priority field */ + xsprintf(header_priority, "<%i>", priority); + IOVEC_SET_STRING(iovec[n++], header_priority); + + /* Second: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Fourth: message */ + IOVEC_SET_STRING(iovec[n++], message); + IOVEC_SET_STRING(iovec[n++], "\n"); + + if (writev(s->dev_kmsg_fd, iovec, n) < 0) + log_debug_errno(errno, "Failed to write to /dev/kmsg for logging: %m"); + + free(ident_buf); +} + +static bool is_us(const char *pid) { + pid_t t; + + assert(pid); + + if (parse_pid(pid, &t) < 0) + return false; + + return t == getpid(); +} + +static void dev_kmsg_record(Server *s, const char *p, size_t l) { + struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; + char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL; + int priority, r; + unsigned n = 0, z = 0, j; + unsigned long long usec; + char *identifier = NULL, *pid = NULL, *e, *f, *k; + uint64_t serial; + size_t pl; + char *kernel_device = NULL; + + assert(s); + assert(p); + + if (l <= 0) + return; + + e = memchr(p, ',', l); + if (!e) + return; + *e = 0; + + r = safe_atoi(p, &priority); + if (r < 0 || priority < 0 || priority > 999) + return; + + if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) + return; + + l -= (e - p) + 1; + p = e + 1; + e = memchr(p, ',', l); + if (!e) + return; + *e = 0; + + r = safe_atou64(p, &serial); + if (r < 0) + return; + + if (s->kernel_seqnum) { + /* We already read this one? */ + if (serial < *s->kernel_seqnum) + return; + + /* Did we lose any? */ + if (serial > *s->kernel_seqnum) + server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, + LOG_MESSAGE("Missed %"PRIu64" kernel messages", + serial - *s->kernel_seqnum), + NULL); + + /* Make sure we never read this one again. Note that + * we always store the next message serial we expect + * here, simply because this makes handling the first + * message with serial 0 easy. */ + *s->kernel_seqnum = serial + 1; + } + + l -= (e - p) + 1; + p = e + 1; + f = memchr(p, ';', l); + if (!f) + return; + /* Kernel 3.6 has the flags field, kernel 3.5 lacks that */ + e = memchr(p, ',', l); + if (!e || f < e) + e = f; + *e = 0; + + r = safe_atollu(p, &usec); + if (r < 0) + return; + + l -= (f - p) + 1; + p = f + 1; + e = memchr(p, '\n', l); + if (!e) + return; + *e = 0; + + pl = e - p; + l -= (e - p) + 1; + k = e + 1; + + for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) { + char *m; + /* Metadata fields attached */ + + if (*k != ' ') + break; + + k++, l--; + + e = memchr(k, '\n', l); + if (!e) + return; + + *e = 0; + + if (cunescape_length_with_prefix(k, e - k, "_KERNEL_", UNESCAPE_RELAX, &m) < 0) + break; + + if (startswith(m, "_KERNEL_DEVICE=")) + kernel_device = m + 15; + + IOVEC_SET_STRING(iovec[n++], m); + z++; + + l -= (e - k) + 1; + k = e + 1; + } + + if (kernel_device) { + struct udev_device *ud; + + ud = udev_device_new_from_device_id(s->udev, kernel_device); + if (ud) { + const char *g; + struct udev_list_entry *ll; + char *b; + + g = udev_device_get_devnode(ud); + if (g) { + b = strappend("_UDEV_DEVNODE=", g); + if (b) { + IOVEC_SET_STRING(iovec[n++], b); + z++; + } + } + + g = udev_device_get_sysname(ud); + if (g) { + b = strappend("_UDEV_SYSNAME=", g); + if (b) { + IOVEC_SET_STRING(iovec[n++], b); + z++; + } + } + + j = 0; + ll = udev_device_get_devlinks_list_entry(ud); + udev_list_entry_foreach(ll, ll) { + + if (j > N_IOVEC_UDEV_FIELDS) + break; + + g = udev_list_entry_get_name(ll); + if (g) { + b = strappend("_UDEV_DEVLINK=", g); + if (b) { + IOVEC_SET_STRING(iovec[n++], b); + z++; + } + } + + j++; + } + + udev_device_unref(ud); + } + } + + if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0) + IOVEC_SET_STRING(iovec[n++], source_time); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel"); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + + if ((priority & LOG_FACMASK) == LOG_KERN) + IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel"); + else { + pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid); + + /* Avoid any messages we generated ourselves via + * log_info() and friends. */ + if (pid && is_us(pid)) + goto finish; + + if (identifier) { + syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); + if (syslog_identifier) + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + if (pid) { + syslog_pid = strappend("SYSLOG_PID=", pid); + if (syslog_pid) + IOVEC_SET_STRING(iovec[n++], syslog_pid); + } + } + + if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0) + IOVEC_SET_STRING(iovec[n++], message); + + server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0); + +finish: + for (j = 0; j < z; j++) + free(iovec[j].iov_base); + + free(message); + free(syslog_priority); + free(syslog_identifier); + free(syslog_pid); + free(syslog_facility); + free(source_time); + free(identifier); + free(pid); +} + +static int server_read_dev_kmsg(Server *s) { + char buffer[8192+1]; /* the kernel-side limit per record is 8K currently */ + ssize_t l; + + assert(s); + assert(s->dev_kmsg_fd >= 0); + + l = read(s->dev_kmsg_fd, buffer, sizeof(buffer) - 1); + if (l == 0) + return 0; + if (l < 0) { + /* Old kernels who don't allow reading from /dev/kmsg + * return EINVAL when we try. So handle this cleanly, + * but don' try to ever read from it again. */ + if (errno == EINVAL) { + s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source); + return 0; + } + + if (errno == EAGAIN || errno == EINTR || errno == EPIPE) + return 0; + + return log_error_errno(errno, "Failed to read from kernel: %m"); + } + + dev_kmsg_record(s, buffer, l); + return 1; +} + +int server_flush_dev_kmsg(Server *s) { + int r; + + assert(s); + + if (s->dev_kmsg_fd < 0) + return 0; + + if (!s->dev_kmsg_readable) + return 0; + + log_debug("Flushing /dev/kmsg..."); + + for (;;) { + r = server_read_dev_kmsg(s); + if (r < 0) + return r; + + if (r == 0) + break; + } + + return 0; +} + +static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Server *s = userdata; + + assert(es); + assert(fd == s->dev_kmsg_fd); + assert(s); + + if (revents & EPOLLERR) + log_warning("/dev/kmsg buffer overrun, some messages lost."); + + if (!(revents & EPOLLIN)) + log_error("Got invalid event from epoll for /dev/kmsg: %"PRIx32, revents); + + return server_read_dev_kmsg(s); +} + +int server_open_dev_kmsg(Server *s) { + int r; + + assert(s); + + s->dev_kmsg_fd = open("/dev/kmsg", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (s->dev_kmsg_fd < 0) { + log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + "Failed to open /dev/kmsg, ignoring: %m"); + return 0; + } + + r = sd_event_add_io(s->event, &s->dev_kmsg_event_source, s->dev_kmsg_fd, EPOLLIN, dispatch_dev_kmsg, s); + if (r < 0) { + + /* This will fail with EPERM on older kernels where + * /dev/kmsg is not readable. */ + if (r == -EPERM) { + r = 0; + goto fail; + } + + log_error_errno(r, "Failed to add /dev/kmsg fd to event loop: %m"); + goto fail; + } + + r = sd_event_source_set_priority(s->dev_kmsg_event_source, SD_EVENT_PRIORITY_IMPORTANT+10); + if (r < 0) { + log_error_errno(r, "Failed to adjust priority of kmsg event source: %m"); + goto fail; + } + + s->dev_kmsg_readable = true; + + return 0; + +fail: + s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source); + s->dev_kmsg_fd = safe_close(s->dev_kmsg_fd); + + return r; +} + +int server_open_kernel_seqnum(Server *s) { + _cleanup_close_ int fd; + uint64_t *p; + int r; + + assert(s); + + /* We store the seqnum we last read in an mmaped file. That + * way we can just use it like a variable, but it is + * persistent and automatically flushed at reboot. */ + + fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); + if (fd < 0) { + log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m"); + return 0; + } + + 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; + } + + p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) { + log_error_errno(errno, "Failed to map sequential number file, ignoring: %m"); + return 0; + } + + s->kernel_seqnum = p; + + return 0; +} diff --git a/src/grp-journal/libjournal-core/journald-kmsg.h b/src/grp-journal/libjournal-core/journald-kmsg.h new file mode 100644 index 0000000000..dab49f1e8c --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-kmsg.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-server.h" + +int server_open_dev_kmsg(Server *s); +int server_flush_dev_kmsg(Server *s); + +void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); + +int server_open_kernel_seqnum(Server *s); diff --git a/src/grp-journal/libjournal-core/journald-native.c b/src/grp-journal/libjournal-core/journald-native.c new file mode 100644 index 0000000000..9bfea935ad --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-native.c @@ -0,0 +1,502 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/io-util.h" +#include "basic/memfd-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/socket-util.h" +#include "basic/string-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" + +bool valid_user_field(const char *p, size_t l, bool allow_protected) { + const char *a; + + /* We kinda enforce POSIX syntax recommendations for + environment variables here, but make a couple of additional + requirements. + + http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */ + + /* No empty field names */ + if (l <= 0) + return false; + + /* Don't allow names longer than 64 chars */ + if (l > 64) + return false; + + /* Variables starting with an underscore are protected */ + if (!allow_protected && p[0] == '_') + return false; + + /* Don't allow digits as first character */ + if (p[0] >= '0' && p[0] <= '9') + return false; + + /* Only allow A-Z0-9 and '_' */ + for (a = p; a < p + l; a++) + if ((*a < 'A' || *a > 'Z') && + (*a < '0' || *a > '9') && + *a != '_') + return false; + + return true; +} + +static bool allow_object_pid(const struct ucred *ucred) { + return ucred && ucred->uid == 0; +} + +void server_process_native_message( + Server *s, + const void *buffer, size_t buffer_size, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len) { + + struct iovec *iovec = NULL; + unsigned n = 0, j, tn = (unsigned) -1; + const char *p; + size_t remaining, m = 0, entry_size = 0; + int priority = LOG_INFO; + char *identifier = NULL, *message = NULL; + pid_t object_pid = 0; + + assert(s); + assert(buffer || buffer_size == 0); + + p = buffer; + remaining = buffer_size; + + while (remaining > 0) { + const char *e, *q; + + e = memchr(p, '\n', remaining); + + if (!e) { + /* Trailing noise, let's ignore it, and flush what we collected */ + log_debug("Received message with trailing noise, ignoring."); + break; + } + + if (e == p) { + /* Entry separator */ + + if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ + log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); + continue; + } + + server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); + n = 0; + priority = LOG_INFO; + entry_size = 0; + + p++; + remaining--; + continue; + } + + if (*p == '.' || *p == '#') { + /* Ignore control commands for now, and + * comments too. */ + remaining -= (e - p) + 1; + p = e + 1; + continue; + } + + /* A property follows */ + + /* n existing properties, 1 new, +1 for _TRANSPORT */ + if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { + log_oom(); + break; + } + + q = memchr(p, '=', e - p); + if (q) { + if (valid_user_field(p, q - p, false)) { + size_t l; + + l = e - p; + + /* If the field name starts with an + * underscore, skip the variable, + * since that indidates a trusted + * field */ + iovec[n].iov_base = (char*) p; + iovec[n].iov_len = l; + entry_size += iovec[n].iov_len; + n++; + + /* We need to determine the priority + * of this entry for the rate limiting + * logic */ + if (l == 10 && + startswith(p, "PRIORITY=") && + p[9] >= '0' && p[9] <= '9') + priority = (priority & LOG_FACMASK) | (p[9] - '0'); + + else if (l == 17 && + startswith(p, "SYSLOG_FACILITY=") && + p[16] >= '0' && p[16] <= '9') + priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3); + + else if (l == 18 && + startswith(p, "SYSLOG_FACILITY=") && + p[16] >= '0' && p[16] <= '9' && + p[17] >= '0' && p[17] <= '9') + priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); + + else if (l >= 19 && + startswith(p, "SYSLOG_IDENTIFIER=")) { + char *t; + + t = strndup(p + 18, l - 18); + if (t) { + free(identifier); + identifier = t; + } + + } else if (l >= 8 && + startswith(p, "MESSAGE=")) { + char *t; + + t = strndup(p + 8, l - 8); + if (t) { + free(message); + message = t; + } + + } else if (l > strlen("OBJECT_PID=") && + l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) && + startswith(p, "OBJECT_PID=") && + allow_object_pid(ucred)) { + char buf[DECIMAL_STR_MAX(pid_t)]; + memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); + buf[l-strlen("OBJECT_PID=")] = '\0'; + + /* ignore error */ + parse_pid(buf, &object_pid); + } + } + + remaining -= (e - p) + 1; + p = e + 1; + continue; + } else { + le64_t l_le; + uint64_t l; + char *k; + + if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { + log_debug("Failed to parse message, ignoring."); + break; + } + + memcpy(&l_le, e + 1, sizeof(uint64_t)); + l = le64toh(l_le); + + if (l > DATA_SIZE_MAX) { + log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); + break; + } + + if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || + e[1+sizeof(uint64_t)+l] != '\n') { + log_debug("Failed to parse message, ignoring."); + break; + } + + k = malloc((e - p) + 1 + l); + if (!k) { + log_oom(); + break; + } + + memcpy(k, p, e - p); + k[e - p] = '='; + memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); + + if (valid_user_field(p, e - p, false)) { + iovec[n].iov_base = k; + iovec[n].iov_len = (e - p) + 1 + l; + entry_size += iovec[n].iov_len; + n++; + } else + free(k); + + remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; + p = e + 1 + sizeof(uint64_t) + l + 1; + } + } + + if (n <= 0) + goto finish; + + tn = n++; + IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); + entry_size += strlen("_TRANSPORT=journal"); + + if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ + log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", + n, entry_size); + goto finish; + } + + if (message) { + if (s->forward_to_syslog) + server_forward_syslog(s, priority, identifier, message, ucred, tv); + + if (s->forward_to_kmsg) + server_forward_kmsg(s, priority, identifier, message, ucred); + + if (s->forward_to_console) + server_forward_console(s, priority, identifier, message, ucred); + + if (s->forward_to_wall) + server_forward_wall(s, priority, identifier, message, ucred); + } + + server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); + +finish: + for (j = 0; j < n; j++) { + if (j == tn) + continue; + + if (iovec[j].iov_base < buffer || + (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) + free(iovec[j].iov_base); + } + + free(iovec); + free(identifier); + free(message); +} + +void server_process_native_file( + Server *s, + int fd, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len) { + + struct stat st; + bool sealed; + int r; + + /* Data is in the passed fd, since it didn't fit in a + * datagram. */ + + assert(s); + assert(fd >= 0); + + /* If it's a memfd, check if it is sealed. If so, we can just + * use map it and use it, and do not need to copy the data + * out. */ + sealed = memfd_get_sealed(fd) > 0; + + if (!sealed && (!ucred || ucred->uid != 0)) { + _cleanup_free_ char *sl = NULL, *k = NULL; + const char *e; + + /* If this is not a sealed memfd, and the peer is unknown or + * unprivileged, then verify the path. */ + + if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) { + log_oom(); + return; + } + + r = readlink_malloc(sl, &k); + if (r < 0) { + log_error_errno(r, "readlink(%s) failed: %m", sl); + return; + } + + e = path_startswith(k, "/dev/shm/"); + if (!e) + e = path_startswith(k, "/tmp/"); + if (!e) + e = path_startswith(k, "/var/tmp/"); + if (!e) { + log_error("Received file outside of allowed directories. Refusing."); + return; + } + + if (!filename_is_valid(e)) { + log_error("Received file in subdirectory of allowed directories. Refusing."); + return; + } + } + + if (fstat(fd, &st) < 0) { + log_error_errno(errno, "Failed to stat passed file, ignoring: %m"); + return; + } + + if (!S_ISREG(st.st_mode)) { + log_error("File passed is not regular. Ignoring."); + return; + } + + if (st.st_size <= 0) + return; + + if (st.st_size > ENTRY_SIZE_MAX) { + log_error("File passed too large. Ignoring."); + return; + } + + if (sealed) { + void *p; + size_t ps; + + /* The file is sealed, we can just map it and use it. */ + + ps = PAGE_ALIGN(st.st_size); + p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) { + log_error_errno(errno, "Failed to map memfd, ignoring: %m"); + return; + } + + server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len); + 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 */ + + p = malloc(st.st_size); + if (!p) { + log_oom(); + return; + } + + n = pread(fd, p, st.st_size, 0); + if (n < 0) + 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); + } +} + +int server_open_native_socket(Server*s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; + static const int one = 1; + int r; + + assert(s); + + if (s->native_fd < 0) { + s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->native_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); + + (void) unlink(sa.un.sun_path); + + r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + (void) chmod(sa.un.sun_path, 0666); + } else + fd_nonblock(s->native_fd, 1); + + r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); + +#ifdef HAVE_SELINUX + 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"); + } +#endif + + r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); + + r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, 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/grp-journal/libjournal-core/journald-native.h b/src/grp-journal/libjournal-core/journald-native.h new file mode 100644 index 0000000000..c13b80aa4f --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-native.h @@ -0,0 +1,35 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-server.h" + +/* Make sure not to make this smaller than the maximum coredump + * size. See COREDUMP_MAX in coredump.c */ +#define ENTRY_SIZE_MAX (1024*1024*770u) +#define DATA_SIZE_MAX (1024*1024*768u) + +bool valid_user_field(const char *p, size_t l, bool allow_protected); + +void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); + +void server_process_native_file(Server *s, int fd, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); + +int server_open_native_socket(Server*s); diff --git a/src/grp-journal/libjournal-core/journald-rate-limit.c b/src/grp-journal/libjournal-core/journald-rate-limit.c new file mode 100644 index 0000000000..22603bf8e6 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-rate-limit.c @@ -0,0 +1,272 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "journald-rate-limit.h" + +#define POOLS_MAX 5 +#define BUCKETS_MAX 127 +#define GROUPS_MAX 2047 + +static const int priority_map[] = { + [LOG_EMERG] = 0, + [LOG_ALERT] = 0, + [LOG_CRIT] = 0, + [LOG_ERR] = 1, + [LOG_WARNING] = 2, + [LOG_NOTICE] = 3, + [LOG_INFO] = 3, + [LOG_DEBUG] = 4 +}; + +typedef struct JournalRateLimitPool JournalRateLimitPool; +typedef struct JournalRateLimitGroup JournalRateLimitGroup; + +struct JournalRateLimitPool { + usec_t begin; + unsigned num; + unsigned suppressed; +}; + +struct JournalRateLimitGroup { + JournalRateLimit *parent; + + char *id; + JournalRateLimitPool pools[POOLS_MAX]; + uint64_t hash; + + LIST_FIELDS(JournalRateLimitGroup, bucket); + LIST_FIELDS(JournalRateLimitGroup, lru); +}; + +struct JournalRateLimit { + usec_t interval; + unsigned burst; + + JournalRateLimitGroup* buckets[BUCKETS_MAX]; + JournalRateLimitGroup *lru, *lru_tail; + + unsigned n_groups; + + uint8_t hash_key[16]; +}; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { + JournalRateLimit *r; + + assert(interval > 0 || burst == 0); + + r = new0(JournalRateLimit, 1); + if (!r) + return NULL; + + r->interval = interval; + r->burst = burst; + + random_bytes(r->hash_key, sizeof(r->hash_key)); + + return r; +} + +static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { + assert(g); + + if (g->parent) { + assert(g->parent->n_groups > 0); + + if (g->parent->lru_tail == g) + g->parent->lru_tail = g->lru_prev; + + LIST_REMOVE(lru, g->parent->lru, g); + LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g); + + g->parent->n_groups--; + } + + free(g->id); + free(g); +} + +void journal_rate_limit_free(JournalRateLimit *r) { + assert(r); + + while (r->lru) + journal_rate_limit_group_free(r->lru); + + free(r); +} + +_pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { + unsigned i; + + assert(g); + + for (i = 0; i < POOLS_MAX; i++) + if (g->pools[i].begin + g->parent->interval >= ts) + return false; + + return true; +} + +static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { + assert(r); + + /* Makes room for at least one new item, but drop all + * expored items too. */ + + while (r->n_groups >= GROUPS_MAX || + (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts))) + journal_rate_limit_group_free(r->lru_tail); +} + +static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { + JournalRateLimitGroup *g; + struct siphash state; + + assert(r); + assert(id); + + g = new0(JournalRateLimitGroup, 1); + if (!g) + return NULL; + + g->id = strdup(id); + if (!g->id) + goto fail; + + siphash24_init(&state, r->hash_key); + string_hash_func(g->id, &state); + g->hash = siphash24_finalize(&state); + + journal_rate_limit_vacuum(r, ts); + + LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g); + LIST_PREPEND(lru, r->lru, g); + if (!g->lru_next) + r->lru_tail = g; + r->n_groups++; + + g->parent = r; + return g; + +fail: + journal_rate_limit_group_free(g); + return NULL; +} + +static unsigned burst_modulate(unsigned burst, uint64_t available) { + unsigned k; + + /* Modulates the burst rate a bit with the amount of available + * disk space */ + + k = u64log2(available); + + /* 1MB */ + if (k <= 20) + return burst; + + burst = (burst * (k-20)) / 4; + + /* + * Example: + * + * <= 1MB = rate * 1 + * 16MB = rate * 2 + * 256MB = rate * 3 + * 4GB = rate * 4 + * 64GB = rate * 5 + * 1TB = rate * 6 + */ + + return burst; +} + +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { + uint64_t h; + JournalRateLimitGroup *g; + JournalRateLimitPool *p; + struct siphash state; + unsigned burst; + usec_t ts; + + assert(id); + + if (!r) + return 1; + + if (r->interval == 0 || r->burst == 0) + return 1; + + burst = burst_modulate(r->burst, available); + + ts = now(CLOCK_MONOTONIC); + + 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) + if (streq(g->id, id)) + break; + + if (!g) { + g = journal_rate_limit_group_new(r, id, ts); + if (!g) + return -ENOMEM; + } + + p = &g->pools[priority_map[priority]]; + + if (p->begin <= 0) { + p->suppressed = 0; + p->num = 1; + p->begin = ts; + return 1; + } + + if (p->begin + r->interval < ts) { + unsigned s; + + s = p->suppressed; + p->suppressed = 0; + p->num = 1; + p->begin = ts; + + return 1 + s; + } + + if (p->num <= burst) { + p->num++; + return 1; + } + + p->suppressed++; + return 0; +} diff --git a/src/grp-journal/libjournal-core/journald-rate-limit.h b/src/grp-journal/libjournal-core/journald-rate-limit.h new file mode 100644 index 0000000000..533dd0f013 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-rate-limit.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/util.h" + +typedef struct JournalRateLimit JournalRateLimit; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); +void journal_rate_limit_free(JournalRateLimit *r); +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); diff --git a/src/grp-journal/libjournal-core/journald-server.c b/src/grp-journal/libjournal-core/journald-server.c new file mode 100644 index 0000000000..c314b74ac2 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-server.c @@ -0,0 +1,2009 @@ +/*** + 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 . +***/ + +#ifdef HAVE_SELINUX +#include +#endif +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/cgroup-util.h" +#include "basic/dirent-util.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/rm-rf.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "sd-journal/journal-authenticate.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-internal.h" +#include "sd-journal/journal-vacuum.h" +#include "shared/acl-util.h" +#include "shared/conf-parser.h" + +#include "journald-audit.h" +#include "journald-kmsg.h" +#include "journald-native.h" +#include "journald-rate-limit.h" +#include "journald-server.h" +#include "journald-stream.h" +#include "journald-syslog.h" + +#define USER_JOURNALS_MAX 1024 + +#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE) +#define DEFAULT_RATE_LIMIT_INTERVAL (30*USEC_PER_SEC) +#define DEFAULT_RATE_LIMIT_BURST 1000 +#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH + +#define RECHECK_SPACE_USEC (30*USEC_PER_SEC) + +#define NOTIFY_SNDBUF_SIZE (8*1024*1024) + +/* The period to insert between posting changes for coalescing */ +#define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC) + +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; + + assert(s); + assert(metrics); + assert(path); + assert(name); + + ts = now(CLOCK_MONOTONIC); + + if (!verbose && s->cached_space_timestamp + RECHECK_SPACE_USEC > ts) { + + if (available) + *available = s->cached_space_available; + if (limit) + *limit = s->cached_space_limit; + + return 0; + } + + p = strjoina(path, SERVER_MACHINE_ID(s)); + d = opendir(p); + if (!d) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open %s: %m", p); + + if (fstatvfs(dirfd(d), &ss) < 0) + return log_error_errno(errno, "Failed to fstatvfs(%s): %m", p); + + FOREACH_DIRENT_ALL(de, d, break) { + struct stat st; + + if (!endswith(de->d_name, ".journal") && + !endswith(de->d_name, ".journal~")) + continue; + + 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; + + sum += (uint64_t) st.st_blocks * 512UL; + } + + /* If requested, 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 (patch_min_use) + metrics->min_use = MAX(metrics->min_use, sum); + + ss_avail = ss.f_bsize * ss.f_bavail; + avail = LESS_BY(ss_avail, metrics->keep_free); + + 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], fb6[FORMAT_BYTES_MAX]; + format_bytes(fb1, sizeof(fb1), sum); + 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_space_limit); + format_bytes(fb6, sizeof(fb6), s->cached_space_available); + + server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE, + LOG_MESSAGE("%s (%s) is %s, max %s, %s free.", + name, path, fb1, fb5, fb6), + "JOURNAL_NAME=%s", name, + "JOURNAL_PATH=%s", path, + "CURRENT_USE=%"PRIu64, sum, + "CURRENT_USE_PRETTY=%s", fb1, + "MAX_USE=%"PRIu64, metrics->max_use, + "MAX_USE_PRETTY=%s", fb2, + "DISK_KEEP_FREE=%"PRIu64, metrics->keep_free, + "DISK_KEEP_FREE_PRETTY=%s", fb3, + "DISK_AVAILABLE=%"PRIu64, ss_avail, + "DISK_AVAILABLE_PRETTY=%s", fb4, + "LIMIT=%"PRIu64, s->cached_space_limit, + "LIMIT_PRETTY=%s", fb5, + "AVAILABLE=%"PRIu64, s->cached_space_available, + "AVAILABLE_PRETTY=%s", fb6, + NULL); + } + + if (available) + *available = s->cached_space_available; + if (limit) + *limit = s->cached_space_limit; + + return 1; +} + +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 + int r; +#endif + assert(f); + +#ifdef HAVE_ACL + if (uid <= SYSTEM_UID_MAX) + return; + + 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 +} + +static int open_journal( + Server *s, + bool reliably, + const char *fname, + int flags, + bool seal, + JournalMetrics *metrics, + JournalFile **ret) { + int r; + JournalFile *f; + + assert(s); + assert(fname); + assert(ret); + + if (reliably) + r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); + else + r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); + if (r < 0) + return r; + + r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC); + if (r < 0) { + (void) journal_file_close(f); + return r; + } + + *ret = f; + return r; +} + +static JournalFile* find_journal(Server *s, uid_t uid) { + _cleanup_free_ char *p = NULL; + int r; + JournalFile *f; + sd_id128_t machine; + + assert(s); + + /* We split up user logs only on /var, not on /run. If the + * runtime file is open, we write to it exclusively, in order + * to guarantee proper order as soon as we flush /run to + * /var and close the runtime file. */ + + if (s->runtime_journal) + return s->runtime_journal; + + if (uid <= SYSTEM_UID_MAX) + return s->system_journal; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return s->system_journal; + + f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid)); + if (f) + return f; + + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal", + SD_ID128_FORMAT_VAL(machine), uid) < 0) + return s->system_journal; + + while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { + /* Too many open? Then let's close one */ + f = ordered_hashmap_steal_first(s->user_journals); + assert(f); + (void) journal_file_close(f); + } + + r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &f); + if (r < 0) + return s->system_journal; + + server_add_acls(f, uid); + + r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); + if (r < 0) { + (void) journal_file_close(f); + return s->system_journal; + } + + return f; +} + +static int do_rotate( + Server *s, + JournalFile **f, + const char* name, + bool seal, + uint32_t uid) { + + int r; + assert(s); + + if (!*f) + return -EINVAL; + + r = journal_file_rotate(f, s->compress, seal, s->deferred_closes); + if (r < 0) + if (*f) + log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); + else + log_error_errno(r, "Failed to create new %s journal: %m", name); + else + server_add_acls(*f, uid); + + return r; +} + +void server_rotate(Server *s) { + JournalFile *f; + void *k; + Iterator i; + int r; + + log_debug("Rotating..."); + + (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_UID(k)); + if (r >= 0) + ordered_hashmap_replace(s->user_journals, k, f); + else if (!f) + /* Old file has been closed and deallocated */ + ordered_hashmap_remove(s->user_journals, k); + } + + /* Perform any deferred closes which aren't still offlining. */ + SET_FOREACH(f, s->deferred_closes, i) + if (!journal_file_is_offlining(f)) { + (void) set_remove(s->deferred_closes, f); + (void) journal_file_close(f); + } +} + +void server_sync(Server *s) { + JournalFile *f; + Iterator i; + int r; + + if (s->system_journal) { + r = journal_file_set_offline(s->system_journal, false); + if (r < 0) + log_warning_errno(r, "Failed to sync system journal, ignoring: %m"); + } + + ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) { + r = journal_file_set_offline(f, false); + if (r < 0) + log_warning_errno(r, "Failed to sync user journal, ignoring: %m"); + } + + if (s->sync_event_source) { + r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF); + if (r < 0) + log_error_errno(r, "Failed to disable sync timer source: %m"); + } + + s->sync_scheduled = false; +} + +static void do_vacuum( + Server *s, + JournalFile *f, + 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, 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_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p); +} + +int server_vacuum(Server *s, bool verbose, bool patch_min_use) { + assert(s); + + log_debug("Vacuuming..."); + + s->oldest_file_usec = 0; + + 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); + + s->cached_space_limit = 0; + s->cached_space_available = 0; + s->cached_space_timestamp = 0; + + return 0; +} + +static void server_cache_machine_id(Server *s) { + sd_id128_t id; + int r; + + assert(s); + + r = sd_id128_get_machine(&id); + if (r < 0) + return; + + sd_id128_to_string(id, stpcpy(s->machine_id_field, "_MACHINE_ID=")); +} + +static void server_cache_boot_id(Server *s) { + sd_id128_t id; + int r; + + assert(s); + + r = sd_id128_get_boot(&id); + if (r < 0) + return; + + sd_id128_to_string(id, stpcpy(s->boot_id_field, "_BOOT_ID=")); +} + +static void server_cache_hostname(Server *s) { + _cleanup_free_ char *t = NULL; + char *x; + + assert(s); + + t = gethostname_malloc(); + if (!t) + return; + + x = strappend("_HOSTNAME=", t); + if (!x) + return; + + free(s->hostname_field); + s->hostname_field = x; +} + +static bool shall_try_append_again(JournalFile *f, int r) { + switch(r) { + case -E2BIG: /* Hit configured limit */ + case -EFBIG: /* Hit fs limit */ + case -EDQUOT: /* Quota limit hit */ + case -ENOSPC: /* Disk full */ + log_debug("%s: Allocation limit reached, rotating.", f->path); + return true; + case -EIO: /* I/O error of some kind (mmap) */ + log_warning("%s: IO error, rotating.", f->path); + return true; + case -EHOSTDOWN: /* Other machine */ + log_info("%s: Journal file from other machine, rotating.", f->path); + return true; + case -EBUSY: /* Unclean shutdown */ + log_info("%s: Unclean shutdown, rotating.", f->path); + return true; + case -EPROTONOSUPPORT: /* Unsupported feature */ + log_info("%s: Unsupported feature, rotating.", f->path); + return true; + case -EBADMSG: /* Corrupted */ + case -ENODATA: /* Truncated */ + case -ESHUTDOWN: /* Already archived */ + log_warning("%s: Journal file corrupted, rotating.", f->path); + return true; + case -EIDRM: /* Journal file has been deleted */ + log_warning("%s: Journal file has been deleted, rotating.", f->path); + return true; + default: + return false; + } +} + +static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n, int priority) { + JournalFile *f; + bool vacuumed = false; + int r; + + assert(s); + assert(iovec); + assert(n > 0); + + f = find_journal(s, uid); + if (!f) + return; + + 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, false, false); + vacuumed = true; + + f = find_journal(s, uid); + if (!f) + return; + } + + r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); + if (r >= 0) { + server_schedule_sync(s, priority); + return; + } + + if (vacuumed || !shall_try_append_again(f, r)) { + log_error_errno(r, "Failed to write entry (%d items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n)); + return; + } + + server_rotate(s); + server_vacuum(s, false, false); + + f = find_journal(s, uid); + if (!f) + return; + + log_debug("Retrying write."); + r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); + if (r < 0) + log_error_errno(r, "Failed to write entry (%d items, %zu bytes) despite vacuuming, ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n)); + else + server_schedule_sync(s, priority); +} + +static void dispatch_message_real( + Server *s, + struct iovec *iovec, unsigned n, unsigned m, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len, + const char *unit_id, + int priority, + pid_t object_pid) { + + char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)], + uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)], + gid[sizeof("_GID=") + DECIMAL_STR_MAX(gid_t)], + owner_uid[sizeof("_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)], + source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)], + o_uid[sizeof("OBJECT_UID=") + DECIMAL_STR_MAX(uid_t)], + o_gid[sizeof("OBJECT_GID=") + DECIMAL_STR_MAX(gid_t)], + o_owner_uid[sizeof("OBJECT_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)]; + uid_t object_uid; + gid_t object_gid; + char *x; + int r; + char *t, *c; + uid_t realuid = 0, owner = 0, journal_uid; + bool owner_valid = false; +#ifdef HAVE_AUDIT + char audit_session[sizeof("_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)], + audit_loginuid[sizeof("_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)], + o_audit_session[sizeof("OBJECT_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)], + o_audit_loginuid[sizeof("OBJECT_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)]; + + uint32_t audit; + uid_t loginuid; +#endif + + assert(s); + assert(iovec); + assert(n > 0); + assert(n + N_IOVEC_META_FIELDS + (object_pid ? N_IOVEC_OBJECT_FIELDS : 0) <= m); + + if (ucred) { + realuid = ucred->uid; + + sprintf(pid, "_PID="PID_FMT, ucred->pid); + IOVEC_SET_STRING(iovec[n++], pid); + + sprintf(uid, "_UID="UID_FMT, ucred->uid); + IOVEC_SET_STRING(iovec[n++], uid); + + sprintf(gid, "_GID="GID_FMT, ucred->gid); + IOVEC_SET_STRING(iovec[n++], gid); + + r = get_process_comm(ucred->pid, &t); + if (r >= 0) { + x = strjoina("_COMM=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + r = get_process_exe(ucred->pid, &t); + if (r >= 0) { + x = strjoina("_EXE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + r = get_process_cmdline(ucred->pid, 0, false, &t); + if (r >= 0) { + x = strjoina("_CMDLINE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + r = get_process_capeff(ucred->pid, &t); + if (r >= 0) { + x = strjoina("_CAP_EFFECTIVE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + +#ifdef HAVE_AUDIT + r = audit_session_from_pid(ucred->pid, &audit); + if (r >= 0) { + sprintf(audit_session, "_AUDIT_SESSION=%"PRIu32, audit); + IOVEC_SET_STRING(iovec[n++], audit_session); + } + + r = audit_loginuid_from_pid(ucred->pid, &loginuid); + if (r >= 0) { + sprintf(audit_loginuid, "_AUDIT_LOGINUID="UID_FMT, loginuid); + IOVEC_SET_STRING(iovec[n++], audit_loginuid); + } +#endif + + r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c); + if (r >= 0) { + char *session = NULL; + + x = strjoina("_SYSTEMD_CGROUP=", c); + IOVEC_SET_STRING(iovec[n++], x); + + r = cg_path_get_session(c, &t); + if (r >= 0) { + session = strjoina("_SYSTEMD_SESSION=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], session); + } + + if (cg_path_get_owner_uid(c, &owner) >= 0) { + owner_valid = true; + + sprintf(owner_uid, "_SYSTEMD_OWNER_UID="UID_FMT, owner); + IOVEC_SET_STRING(iovec[n++], owner_uid); + } + + if (cg_path_get_unit(c, &t) >= 0) { + x = strjoina("_SYSTEMD_UNIT=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } else if (unit_id && !session) { + x = strjoina("_SYSTEMD_UNIT=", unit_id); + IOVEC_SET_STRING(iovec[n++], x); + } + + if (cg_path_get_user_unit(c, &t) >= 0) { + x = strjoina("_SYSTEMD_USER_UNIT=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } else if (unit_id && session) { + x = strjoina("_SYSTEMD_USER_UNIT=", unit_id); + IOVEC_SET_STRING(iovec[n++], x); + } + + if (cg_path_get_slice(c, &t) >= 0) { + x = strjoina("_SYSTEMD_SLICE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + free(c); + } else if (unit_id) { + x = strjoina("_SYSTEMD_UNIT=", unit_id); + IOVEC_SET_STRING(iovec[n++], x); + } + +#ifdef HAVE_SELINUX + if (mac_selinux_have()) { + if (label) { + x = alloca(strlen("_SELINUX_CONTEXT=") + label_len + 1); + + *((char*) mempcpy(stpcpy(x, "_SELINUX_CONTEXT="), label, label_len)) = 0; + IOVEC_SET_STRING(iovec[n++], x); + } else { + char *con; + + if (getpidcon(ucred->pid, &con) >= 0) { + x = strjoina("_SELINUX_CONTEXT=", con); + + freecon(con); + IOVEC_SET_STRING(iovec[n++], x); + } + } + } +#endif + } + assert(n <= m); + + if (object_pid) { + r = get_process_uid(object_pid, &object_uid); + if (r >= 0) { + sprintf(o_uid, "OBJECT_UID="UID_FMT, object_uid); + IOVEC_SET_STRING(iovec[n++], o_uid); + } + + r = get_process_gid(object_pid, &object_gid); + if (r >= 0) { + sprintf(o_gid, "OBJECT_GID="GID_FMT, object_gid); + IOVEC_SET_STRING(iovec[n++], o_gid); + } + + r = get_process_comm(object_pid, &t); + if (r >= 0) { + x = strjoina("OBJECT_COMM=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + r = get_process_exe(object_pid, &t); + if (r >= 0) { + x = strjoina("OBJECT_EXE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + r = get_process_cmdline(object_pid, 0, false, &t); + if (r >= 0) { + x = strjoina("OBJECT_CMDLINE=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + +#ifdef HAVE_AUDIT + r = audit_session_from_pid(object_pid, &audit); + if (r >= 0) { + sprintf(o_audit_session, "OBJECT_AUDIT_SESSION=%"PRIu32, audit); + IOVEC_SET_STRING(iovec[n++], o_audit_session); + } + + r = audit_loginuid_from_pid(object_pid, &loginuid); + if (r >= 0) { + sprintf(o_audit_loginuid, "OBJECT_AUDIT_LOGINUID="UID_FMT, loginuid); + IOVEC_SET_STRING(iovec[n++], o_audit_loginuid); + } +#endif + + r = cg_pid_get_path_shifted(object_pid, s->cgroup_root, &c); + if (r >= 0) { + x = strjoina("OBJECT_SYSTEMD_CGROUP=", c); + IOVEC_SET_STRING(iovec[n++], x); + + r = cg_path_get_session(c, &t); + if (r >= 0) { + x = strjoina("OBJECT_SYSTEMD_SESSION=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + if (cg_path_get_owner_uid(c, &owner) >= 0) { + sprintf(o_owner_uid, "OBJECT_SYSTEMD_OWNER_UID="UID_FMT, owner); + IOVEC_SET_STRING(iovec[n++], o_owner_uid); + } + + if (cg_path_get_unit(c, &t) >= 0) { + x = strjoina("OBJECT_SYSTEMD_UNIT=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + if (cg_path_get_user_unit(c, &t) >= 0) { + x = strjoina("OBJECT_SYSTEMD_USER_UNIT=", t); + free(t); + IOVEC_SET_STRING(iovec[n++], x); + } + + free(c); + } + } + assert(n <= m); + + if (tv) { + sprintf(source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv)); + IOVEC_SET_STRING(iovec[n++], source_time); + } + + /* Note that strictly speaking storing the boot id here is + * redundant since the entry includes this in-line + * anyway. However, we need this indexed, too. */ + if (!isempty(s->boot_id_field)) + IOVEC_SET_STRING(iovec[n++], s->boot_id_field); + + if (!isempty(s->machine_id_field)) + IOVEC_SET_STRING(iovec[n++], s->machine_id_field); + + if (!isempty(s->hostname_field)) + IOVEC_SET_STRING(iovec[n++], s->hostname_field); + + assert(n <= m); + + if (s->split_mode == SPLIT_UID && realuid > 0) + /* Split up strictly by any UID */ + journal_uid = realuid; + else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0) + /* Split up by login UIDs. We do this only if the + * realuid is not root, in order not to accidentally + * leak privileged information to the user that is + * logged by a privileged process that is part of an + * unprivileged session. */ + journal_uid = owner; + else + journal_uid = 0; + + write_to_journal(s, journal_uid, iovec, n, priority); +} + +void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) { + char mid[11 + 32 + 1]; + struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS]; + unsigned n = 0, m; + int r; + va_list ap; + struct ucred ucred = {}; + + assert(s); + assert(format); + + assert_cc(3 == LOG_FAC(LOG_DAEMON)); + IOVEC_SET_STRING(iovec[n++], "SYSLOG_FACILITY=3"); + IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=systemd-journald"); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver"); + assert_cc(6 == LOG_INFO); + IOVEC_SET_STRING(iovec[n++], "PRIORITY=6"); + + if (!sd_id128_is_null(message_id)) { + snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id)); + IOVEC_SET_STRING(iovec[n++], mid); + } + + m = n; + + va_start(ap, format); + r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, false, 0, format, ap); + /* Error handling below */ + va_end(ap); + + ucred.pid = getpid(); + ucred.uid = getuid(); + ucred.gid = getgid(); + + if (r >= 0) + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); + + while (m < n) + free(iovec[m++].iov_base); + + if (r < 0) { + /* We failed to format the message. Emit a warning instead. */ + char buf[LINE_MAX]; + + xsprintf(buf, "MESSAGE=Entry printing failed: %s", strerror(-r)); + + n = 3; + IOVEC_SET_STRING(iovec[n++], "PRIORITY=4"); + IOVEC_SET_STRING(iovec[n++], buf); + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); + } +} + +void server_dispatch_message( + Server *s, + struct iovec *iovec, unsigned n, unsigned m, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len, + const char *unit_id, + int priority, + pid_t object_pid) { + + int rl, r; + _cleanup_free_ char *path = NULL; + uint64_t available = 0; + char *c; + + assert(s); + assert(iovec || n == 0); + + if (n == 0) + return; + + if (LOG_PRI(priority) > s->max_level_store) + return; + + /* Stop early in case the information will not be stored + * in a journal. */ + if (s->storage == STORAGE_NONE) + return; + + if (!ucred) + goto finish; + + r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &path); + if (r < 0) + goto finish; + + /* example: /user/lennart/3/foobar + * /system/dbus.service/foobar + * + * So let's cut of everything past the third /, since that is + * where user directories start */ + + c = strchr(path, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) + *c = 0; + } + } + + (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; + + /* Write a suppression message if we suppressed something */ + if (rl > 1) + server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, + LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path), + NULL); + +finish: + dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); +} + + +static int system_journal_open(Server *s, bool flush_requested) { + const char *fn; + int r = 0; + + if (!s->system_journal && + (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && + (flush_requested + || access("/run/systemd/journal/flushed", F_OK) >= 0)) { + + /* If in auto mode: first try to create the machine + * path, but not the prefix. + * + * If in persistent mode: create /var/log/journal and + * the machine path */ + + if (s->storage == STORAGE_PERSISTENT) + (void) mkdir_p("/var/log/journal/", 0755); + + fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s)); + (void) mkdir(fn, 0755); + + fn = strjoina(fn, "/system.journal"); + r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal); + 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"); + + r = 0; + } + } + + if (!s->runtime_journal && + (s->storage != STORAGE_NONE)) { + + fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal"); + + if (s->system_journal) { + + /* Try to open the runtime journal, but only + * if it already exists, so that we can flush + * it into the system journal */ + + r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to open runtime journal: %m"); + + r = 0; + } + + } else { + + /* OK, we really need the runtime journal, so create + * it if necessary. */ + + (void) mkdir("/run/log", 0755); + (void) mkdir("/run/log/journal", 0755); + (void) mkdir_parents(fn, 0750); + + r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal); + if (r < 0) + return log_error_errno(r, "Failed to open runtime journal: %m"); + } + + 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); + } + } + + return r; +} + +int server_flush_to_var(Server *s) { + sd_id128_t machine; + sd_journal *j = NULL; + char ts[FORMAT_TIMESPAN_MAX]; + usec_t start; + unsigned n = 0; + int r; + + assert(s); + + if (s->storage != STORAGE_AUTO && + s->storage != STORAGE_PERSISTENT) + return 0; + + if (!s->runtime_journal) + return 0; + + (void) system_journal_open(s, true); + + if (!s->system_journal) + return 0; + + log_debug("Flushing to /var..."); + + start = now(CLOCK_MONOTONIC); + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY); + if (r < 0) + return log_error_errno(r, "Failed to read runtime journal: %m"); + + sd_journal_set_data_threshold(j, 0); + + SD_JOURNAL_FOREACH(j) { + Object *o = NULL; + JournalFile *f; + + f = j->current_file; + assert(f && f->current_offset > 0); + + n++; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) { + log_error_errno(r, "Can't read entry: %m"); + goto finish; + } + + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + if (r >= 0) + continue; + + if (!shall_try_append_again(s->system_journal, r)) { + log_error_errno(r, "Can't write entry: %m"); + goto finish; + } + + server_rotate(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."); + r = -EIO; + goto finish; + } + + log_debug("Retrying write."); + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Can't write entry: %m"); + goto finish; + } + } + + r = 0; + +finish: + journal_file_post_change(s->system_journal); + + s->runtime_journal = journal_file_close(s->runtime_journal); + + if (r >= 0) + (void) rm_rf("/run/log/journal", REMOVE_ROOT); + + sd_journal_close(j); + + server_driver_message(s, SD_ID128_NULL, + LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.", + format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), + n), + NULL); + + return r; +} + +int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Server *s = userdata; + struct ucred *ucred = NULL; + struct timeval *tv = NULL; + struct cmsghdr *cmsg; + char *label = NULL; + size_t label_len = 0, m; + struct iovec iovec; + ssize_t n; + int *fds = NULL, v = 0; + unsigned n_fds = 0; + + union { + struct cmsghdr cmsghdr; + + /* We use NAME_MAX space for the SELinux label + * here. The kernel currently enforces no + * limit, but according to suggestions from + * the SELinux people this will change and it + * will probably be identical to NAME_MAX. For + * now we use that, but this should be updated + * one day when the final limit is known. */ + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct timeval)) + + CMSG_SPACE(sizeof(int)) + /* fd */ + CMSG_SPACE(NAME_MAX)]; /* selinux label */ + } control = {}; + + union sockaddr_union sa = {}; + + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_name = &sa, + .msg_namelen = sizeof(sa), + }; + + assert(s); + assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd); + + if (revents != EPOLLIN) { + log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents); + return -EIO; + } + + /* Try to get the right size, if we can. (Not all + * sockets support SIOCINQ, hence we just try, but + * don't rely on it. */ + (void) ioctl(fd, SIOCINQ, &v); + + /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */ + m = PAGE_ALIGN(MAX3((size_t) v + 1, + (size_t) LINE_MAX, + ALIGN(sizeof(struct nlmsghdr)) + ALIGN((size_t) MAX_AUDIT_MESSAGE_LENGTH)) + 1); + + if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m)) + return log_oom(); + + iovec.iov_base = s->buffer; + iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */ + + n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return log_error_errno(errno, "recvmsg() failed: %m"); + } + + 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); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_SECURITY) { + label = (char*) CMSG_DATA(cmsg); + label_len = cmsg->cmsg_len - CMSG_LEN(0); + } else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + tv = (struct timeval*) CMSG_DATA(cmsg); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } + } + + /* And a trailing NUL, just in case */ + s->buffer[n] = 0; + + if (fd == s->syslog_fd) { + if (n > 0 && n_fds == 0) + server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len); + else if (n_fds > 0) + log_warning("Got file descriptors via syslog socket. Ignoring."); + + } else if (fd == s->native_fd) { + if (n > 0 && n_fds == 0) + server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len); + else if (n == 0 && n_fds == 1) + server_process_native_file(s, fds[0], ucred, tv, label, label_len); + else if (n_fds > 0) + log_warning("Got too many file descriptors via native socket. Ignoring."); + + } else { + assert(fd == s->audit_fd); + + if (n > 0 && n_fds == 0) + server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen); + else if (n_fds > 0) + log_warning("Got file descriptors via audit socket. Ignoring."); + } + + close_many(fds, n_fds); + return 0; +} + +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 " PID_FMT, si->ssi_pid); + + server_flush_to_var(s); + server_sync(s); + server_vacuum(s, false, false); + + 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 " PID_FMT, si->ssi_pid); + server_rotate(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; +} + +static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + Server *s = userdata; + + assert(s); + + log_received_signal(LOG_INFO, si); + + sd_event_exit(s->event, 0); + 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, SIGRTMIN+1, -1) >= 0); + + r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s); + if (r < 0) + return r; + + r = sd_event_add_signal(s->event, &s->sigusr2_event_source, SIGUSR2, dispatch_sigusr2, s); + if (r < 0) + return r; + + r = sd_event_add_signal(s->event, &s->sigterm_event_source, SIGTERM, dispatch_sigterm, 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 *p; + int r; + + r = proc_cmdline(&line); + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); + return 0; + } + + p = line; + for (;;) { + _cleanup_free_ char *word = NULL; + + 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); + if (r < 0) + log_warning("Failed to parse forward to syslog switch %s. Ignoring.", word + 35); + else + s->forward_to_syslog = r; + } else if (startswith(word, "systemd.journald.forward_to_kmsg=")) { + r = parse_boolean(word + 33); + if (r < 0) + log_warning("Failed to parse forward to kmsg switch %s. Ignoring.", word + 33); + else + s->forward_to_kmsg = r; + } else if (startswith(word, "systemd.journald.forward_to_console=")) { + r = parse_boolean(word + 36); + if (r < 0) + log_warning("Failed to parse forward to console switch %s. Ignoring.", word + 36); + else + s->forward_to_console = r; + } else if (startswith(word, "systemd.journald.forward_to_wall=")) { + r = parse_boolean(word + 33); + if (r < 0) + log_warning("Failed to parse forward to wall switch %s. Ignoring.", word + 33); + else + s->forward_to_wall = r; + } else if (startswith(word, "systemd.journald")) + log_warning("Invalid systemd.journald parameter. Ignoring."); + } + + /* 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(PKGSYSCONFDIR "/journald.conf", + CONF_PATHS_NULSTR("systemd/journald.conf.d"), + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + false, s); +} + +static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { + Server *s = userdata; + + assert(s); + + server_sync(s); + return 0; +} + +int server_schedule_sync(Server *s, int priority) { + int r; + + assert(s); + + if (priority <= LOG_CRIT) { + /* Immediately sync to disk when this is of priority CRIT, ALERT, EMERG */ + server_sync(s); + return 0; + } + + if (s->sync_scheduled) + return 0; + + if (s->sync_interval_usec > 0) { + usec_t when; + + r = sd_event_now(s->event, CLOCK_MONOTONIC, &when); + if (r < 0) + return r; + + when += s->sync_interval_usec; + + if (!s->sync_event_source) { + r = sd_event_add_time( + s->event, + &s->sync_event_source, + CLOCK_MONOTONIC, + when, 0, + server_dispatch_sync, s); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s->sync_event_source, SD_EVENT_PRIORITY_IMPORTANT); + } else { + r = sd_event_source_set_time(s->sync_event_source, when); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_ONESHOT); + } + if (r < 0) + return r; + + s->sync_scheduled = true; + } + + return 0; +} + +static int dispatch_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Server *s = userdata; + + assert(s); + + server_cache_hostname(s); + return 0; +} + +static int server_open_hostname(Server *s) { + int r; + + assert(s); + + s->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); + if (s->hostname_fd < 0) + return log_error_errno(errno, "Failed to open /proc/sys/kernel/hostname: %m"); + + r = sd_event_add_io(s->event, &s->hostname_event_source, s->hostname_fd, 0, dispatch_hostname_change, s); + if (r < 0) { + /* kernels prior to 3.2 don't support polling this file. Ignore + * the failure. */ + if (r == -EPERM) { + log_warning_errno(r, "Failed to register hostname fd in event loop, ignoring: %m"); + s->hostname_fd = safe_close(s->hostname_fd); + return 0; + } + + return log_error_errno(r, "Failed to register hostname fd in event loop: %m"); + } + + r = sd_event_source_set_priority(s->hostname_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); + if (r < 0) + return log_error_errno(r, "Failed to adjust priority of host name event source: %m"); + + 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 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, SOCKADDR_UN_LEN(sa.un)); + 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 = 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; + + s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL; + s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST; + + s->forward_to_wall = true; + + s->max_file_usec = DEFAULT_MAX_FILE_USEC; + + s->max_level_store = LOG_DEBUG; + s->max_level_syslog = LOG_DEBUG; + s->max_level_kmsg = LOG_NOTICE; + s->max_level_console = LOG_INFO; + s->max_level_wall = LOG_EMERG; + + 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; + } + + (void) mkdir_p("/run/systemd/journal", 0755); + + s->user_journals = ordered_hashmap_new(NULL); + if (!s->user_journals) + return log_oom(); + + s->mmap = mmap_cache_new(); + if (!s->mmap) + return log_oom(); + + s->deferred_closes = set_new(NULL); + if (!s->deferred_closes) + return log_oom(); + + r = sd_event_default(&s->event); + if (r < 0) + return log_error_errno(r, "Failed to create event loop: %m"); + + n = sd_listen_fds(true); + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + + if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) { + + if (s->native_fd >= 0) { + log_error("Too many native sockets passed."); + return -EINVAL; + } + + s->native_fd = fd; + + } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) { + + if (s->stdout_fd >= 0) { + log_error("Too many stdout sockets passed."); + return -EINVAL; + } + + s->stdout_fd = fd; + + } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 || + sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) { + + if (s->syslog_fd >= 0) { + log_error("Too many /dev/log sockets passed."); + return -EINVAL; + } + + s->syslog_fd = fd; + + } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { + + if (s->audit_fd >= 0) { + log_error("Too many audit sockets passed."); + return -EINVAL; + } + + s->audit_fd = fd; + + } else { + + if (!fds) { + fds = fdset_new(); + if (!fds) + return log_oom(); + } + + r = fdset_put(fds, fd); + if (r < 0) + return log_oom(); + } + } + + /* 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; + + /* 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) + return r; + + r = server_open_hostname(s); + if (r < 0) + return r; + + r = setup_signals(s); + if (r < 0) + return r; + + s->udev = udev_new(); + if (!s->udev) + return -ENOMEM; + + s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); + if (!s->rate_limit) + return -ENOMEM; + + r = cg_get_root_path(&s->cgroup_root); + if (r < 0) + return r; + + server_cache_hostname(s); + server_cache_boot_id(s); + server_cache_machine_id(s); + + (void) server_connect_notify(s); + + return system_journal_open(s, false); +} + +void server_maybe_append_tags(Server *s) { +#ifdef HAVE_GCRYPT + JournalFile *f; + Iterator i; + usec_t n; + + n = now(CLOCK_REALTIME); + + if (s->system_journal) + journal_file_maybe_append_tag(s->system_journal, n); + + ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) + journal_file_maybe_append_tag(f, n); +#endif +} + +void server_done(Server *s) { + JournalFile *f; + assert(s); + + if (s->deferred_closes) { + journal_file_close_set(s->deferred_closes); + set_free(s->deferred_closes); + } + + while (s->stdout_streams) + stdout_stream_free(s->stdout_streams); + + if (s->system_journal) + (void) journal_file_close(s->system_journal); + + if (s->runtime_journal) + (void) journal_file_close(s->runtime_journal); + + while ((f = ordered_hashmap_steal_first(s->user_journals))) + (void) journal_file_close(f); + + ordered_hashmap_free(s->user_journals); + + sd_event_source_unref(s->syslog_event_source); + sd_event_source_unref(s->native_event_source); + sd_event_source_unref(s->stdout_event_source); + sd_event_source_unref(s->dev_kmsg_event_source); + sd_event_source_unref(s->audit_event_source); + sd_event_source_unref(s->sync_event_source); + sd_event_source_unref(s->sigusr1_event_source); + 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); + safe_close(s->native_fd); + safe_close(s->stdout_fd); + 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); + + if (s->kernel_seqnum) + munmap(s->kernel_seqnum, sizeof(uint64_t)); + + free(s->buffer); + free(s->tty_path); + free(s->cgroup_root); + free(s->hostname_field); + + if (s->mmap) + mmap_cache_unref(s->mmap); + + 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/grp-journal/libjournal-core/journald-server.h b/src/grp-journal/libjournal-core/journald-server.h new file mode 100644 index 0000000000..5f1af1e50b --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-server.h @@ -0,0 +1,187 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "sd-journal/journal-file.h" + +typedef struct Server Server; + +#include "journald-rate-limit.h" +#include "journald-stream.h" + +typedef enum Storage { + STORAGE_AUTO, + STORAGE_VOLATILE, + STORAGE_PERSISTENT, + STORAGE_NONE, + _STORAGE_MAX, + _STORAGE_INVALID = -1 +} Storage; + +typedef enum SplitMode { + SPLIT_UID, + SPLIT_LOGIN, + SPLIT_NONE, + _SPLIT_MAX, + _SPLIT_INVALID = -1 +} SplitMode; + +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; + + sd_event_source *syslog_event_source; + sd_event_source *native_event_source; + sd_event_source *stdout_event_source; + sd_event_source *dev_kmsg_event_source; + sd_event_source *audit_event_source; + sd_event_source *sync_event_source; + sd_event_source *sigusr1_event_source; + 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; + OrderedHashmap *user_journals; + + uint64_t seqnum; + + char *buffer; + size_t buffer_size; + + JournalRateLimit *rate_limit; + usec_t sync_interval_usec; + usec_t rate_limit_interval; + unsigned rate_limit_burst; + + JournalMetrics runtime_metrics; + JournalMetrics system_metrics; + + bool compress; + bool seal; + + bool forward_to_kmsg; + bool forward_to_syslog; + bool forward_to_console; + bool forward_to_wall; + + unsigned n_forward_syslog_missed; + usec_t last_warn_forward_syslog_missed; + + uint64_t cached_space_available; + uint64_t cached_space_limit; + usec_t cached_space_timestamp; + + uint64_t var_available_timestamp; + + usec_t max_retention_usec; + usec_t max_file_usec; + usec_t oldest_file_usec; + + LIST_HEAD(StdoutStream, stdout_streams); + LIST_HEAD(StdoutStream, stdout_streams_notify_queue); + unsigned n_stdout_streams; + + char *tty_path; + + int max_level_store; + int max_level_syslog; + int max_level_kmsg; + int max_level_console; + int max_level_wall; + + Storage storage; + SplitMode split_mode; + + MMapCache *mmap; + + Set *deferred_closes; + + struct udev *udev; + + uint64_t *kernel_seqnum; + bool dev_kmsg_readable:1; + + 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]; + char *hostname_field; + + /* Cached cgroup root, so that we don't have to query that all the time */ + char *cgroup_root; + + 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 +#define N_IOVEC_UDEV_FIELDS 32 +#define N_IOVEC_OBJECT_FIELDS 12 +#define N_IOVEC_PAYLOAD_FIELDS 15 + +void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid); +void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_; + +/* gperf lookup function */ +const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length); + +int config_parse_storage(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 *storage_to_string(Storage s) _const_; +Storage storage_from_string(const char *s) _pure_; + +int config_parse_split_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 *split_mode_to_string(SplitMode s) _const_; +SplitMode split_mode_from_string(const char *s) _pure_; + +int server_init(Server *s); +void server_done(Server *s); +void server_sync(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); +void server_maybe_append_tags(Server *s); +int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); diff --git a/src/grp-journal/libjournal-core/journald-stream.c b/src/grp-journal/libjournal-core/journald-stream.c new file mode 100644 index 0000000000..e90dfac1cd --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-stream.c @@ -0,0 +1,786 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#ifdef HAVE_SELINUX +#include +#endif + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/selinux-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/syslog-util.h" + +#include "journald-console.h" +#include "journald-kmsg.h" +#include "journald-server.h" +#include "journald-stream.h" +#include "journald-syslog.h" +#include "journald-wall.h" + +#define STDOUT_STREAMS_MAX 4096 + +typedef enum StdoutStreamState { + STDOUT_STREAM_IDENTIFIER, + STDOUT_STREAM_UNIT_ID, + STDOUT_STREAM_PRIORITY, + STDOUT_STREAM_LEVEL_PREFIX, + STDOUT_STREAM_FORWARD_TO_SYSLOG, + STDOUT_STREAM_FORWARD_TO_KMSG, + STDOUT_STREAM_FORWARD_TO_CONSOLE, + STDOUT_STREAM_RUNNING +} StdoutStreamState; + +struct StdoutStream { + Server *server; + StdoutStreamState state; + + int fd; + + struct ucred ucred; + char *label; + char *identifier; + char *unit_id; + int priority; + bool level_prefix:1; + bool forward_to_syslog:1; + bool forward_to_kmsg:1; + bool forward_to_console:1; + + bool fdstore:1; + bool in_notify_queue:1; + + char buffer[LINE_MAX+1]; + size_t length; + + sd_event_source *event_source; + + char *state_file; + + LIST_FIELDS(StdoutStream, stdout_stream); + LIST_FIELDS(StdoutStream, stdout_stream_notify_queue); +}; + +void stdout_stream_free(StdoutStream *s) { + if (!s) + return; + + if (s->server) { + 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) { + sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF); + s->event_source = sd_event_source_unref(s->event_source); + } + + safe_close(s->fd); + free(s->label); + free(s->identifier); + free(s->unit_id); + free(s->state_file); + + free(s); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free); + +static void stdout_stream_destroy(StdoutStream *s) { + if (!s) + return; + + if (s->state_file) + (void) unlink(s->state_file); + + stdout_stream_free(s); +} + +static int stdout_stream_save(StdoutStream *s) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(s); + + if (s->state != STDOUT_STREAM_RUNNING) + return 0; + + if (!s->state_file) { + struct stat st; + + r = fstat(s->fd, &st); + if (r < 0) + return log_warning_errno(errno, "Failed to stat connected stream: %m"); + + /* We use device and inode numbers as identifier for the stream */ + if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) + return log_oom(); + } + + mkdir_p("/run/systemd/journal/streams", 0755); + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fprintf(f, + "# This is private data. Do not parse\n" + "PRIORITY=%i\n" + "LEVEL_PREFIX=%i\n" + "FORWARD_TO_SYSLOG=%i\n" + "FORWARD_TO_KMSG=%i\n" + "FORWARD_TO_CONSOLE=%i\n", + s->priority, + s->level_prefix, + s->forward_to_syslog, + s->forward_to_kmsg, + s->forward_to_console); + + if (!isempty(s->identifier)) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->identifier); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "IDENTIFIER=%s\n", escaped); + } + + if (!isempty(s->unit_id)) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->unit_id); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "UNIT=%s\n", escaped); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, s->state_file) < 0) { + r = -errno; + goto fail; + } + + 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; + +fail: + (void) unlink(s->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); +} + +static int stdout_stream_log(StdoutStream *s, const char *p) { + struct iovec iovec[N_IOVEC_META_FIELDS + 5]; + int priority; + char syslog_priority[] = "PRIORITY=\0"; + char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1]; + _cleanup_free_ char *message = NULL, *syslog_identifier = NULL; + unsigned n = 0; + size_t label_len; + + assert(s); + assert(p); + + 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); + + if (s->forward_to_kmsg || s->server->forward_to_kmsg) + server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred); + + if (s->forward_to_console || s->server->forward_to_console) + server_forward_console(s->server, priority, s->identifier, p, &s->ucred); + + if (s->server->forward_to_wall) + server_forward_wall(s->server, priority, s->identifier, p, &s->ucred); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout"); + + syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority); + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (priority & LOG_FACMASK) { + xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)); + IOVEC_SET_STRING(iovec[n++], syslog_facility); + } + + if (s->identifier) { + syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier); + if (syslog_identifier) + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + message = strappend("MESSAGE=", p); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + label_len = s->label ? strlen(s->label) : 0; + server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, s->label, label_len, s->unit_id, priority, 0); + return 0; +} + +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) { + + case STDOUT_STREAM_IDENTIFIER: + if (isempty(p)) + s->identifier = NULL; + else { + s->identifier = strdup(p); + if (!s->identifier) + return log_oom(); + } + + s->state = STDOUT_STREAM_UNIT_ID; + return 0; + + case STDOUT_STREAM_UNIT_ID: + if (s->ucred.uid == 0) { + if (isempty(p)) + s->unit_id = NULL; + else { + s->unit_id = strdup(p); + if (!s->unit_id) + return log_oom(); + } + } + + s->state = STDOUT_STREAM_PRIORITY; + return 0; + + case STDOUT_STREAM_PRIORITY: + r = safe_atoi(p, &s->priority); + if (r < 0 || s->priority < 0 || s->priority > 999) { + log_warning("Failed to parse log priority line."); + return -EINVAL; + } + + s->state = STDOUT_STREAM_LEVEL_PREFIX; + return 0; + + case STDOUT_STREAM_LEVEL_PREFIX: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse level prefix line."); + return -EINVAL; + } + + s->level_prefix = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG; + return 0; + + case STDOUT_STREAM_FORWARD_TO_SYSLOG: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse forward to syslog line."); + return -EINVAL; + } + + s->forward_to_syslog = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_KMSG; + return 0; + + case STDOUT_STREAM_FORWARD_TO_KMSG: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse copy to kmsg line."); + return -EINVAL; + } + + s->forward_to_kmsg = !!r; + s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE; + return 0; + + case STDOUT_STREAM_FORWARD_TO_CONSOLE: + r = parse_boolean(p); + if (r < 0) { + log_warning("Failed to parse copy to console line."); + return -EINVAL; + } + + s->forward_to_console = !!r; + s->state = STDOUT_STREAM_RUNNING; + + /* Try to save the stream, so that journald can be restarted and we can recover */ + (void) stdout_stream_save(s); + return 0; + + case STDOUT_STREAM_RUNNING: + return stdout_stream_log(s, orig); + } + + assert_not_reached("Unknown stream state"); +} + +static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + char *p; + size_t remaining; + int r; + + assert(s); + + p = s->buffer; + remaining = s->length; + for (;;) { + char *end; + size_t skip; + + end = memchr(p, '\n', remaining); + if (end) + skip = end - p + 1; + else if (remaining >= sizeof(s->buffer) - 1) { + end = p + sizeof(s->buffer) - 1; + skip = remaining; + } else + break; + + *end = 0; + + r = stdout_stream_line(s, p); + if (r < 0) + return r; + + remaining -= skip; + p += skip; + } + + if (force_flush && remaining > 0) { + p[remaining] = 0; + r = stdout_stream_line(s, p); + if (r < 0) + return r; + + p += remaining; + remaining = 0; + } + + if (p > s->buffer) { + memmove(s->buffer, p, remaining); + s->length = remaining; + } + + return 0; +} + +static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + StdoutStream *s = userdata; + ssize_t l; + int r; + + assert(s); + + if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) { + log_error("Got invalid event from epoll for stdout stream: %"PRIx32, revents); + goto terminate; + } + + l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length); + if (l < 0) { + + if (errno == EAGAIN) + return 0; + + log_warning_errno(errno, "Failed to read from stream: %m"); + goto terminate; + } + + if (l == 0) { + stdout_stream_scan(s, true); + goto terminate; + } + + s->length += l; + r = stdout_stream_scan(s, false); + if (r < 0) + goto terminate; + + return 1; + +terminate: + stdout_stream_destroy(s); + return 0; +} + +static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { + _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL; + int r; + + assert(s); + assert(fd >= 0); + + stream = new0(StdoutStream, 1); + if (!stream) + return log_oom(); + + stream->fd = -1; + stream->priority = LOG_INFO; + + r = getpeercred(fd, &stream->ucred); + if (r < 0) + return log_error_errno(r, "Failed to determine peer credentials: %m"); + + 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"); + } + + (void) shutdown(fd, SHUT_WR); + + r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream); + if (r < 0) + return log_error_errno(r, "Failed to add stream to event loop: %m"); + + r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5); + if (r < 0) + return log_error_errno(r, "Failed to adjust stdout event source priority: %m"); + + stream->fd = fd; + + stream->server = s; + LIST_PREPEND(stdout_stream, s->stdout_streams, stream); + s->n_stdout_streams++; + + if (ret) + *ret = stream; + + stream = NULL; + + return 0; +} + +static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) { + _cleanup_close_ int fd = -1; + Server *s = userdata; + int r; + + assert(s); + + if (revents != EPOLLIN) { + log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents); + return -EIO; + } + + fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (fd < 0) { + if (errno == EAGAIN) + return 0; + + return log_error_errno(errno, "Failed to accept stdout connection: %m"); + } + + if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { + log_warning("Too many stdout streams, refusing connection."); + return 0; + } + + r = stdout_stream_install(s, fd, NULL); + if (r < 0) + return r; + + fd = -1; + return 0; +} + +static int stdout_stream_load(StdoutStream *stream, const char *fname) { + _cleanup_free_ char + *priority = NULL, + *level_prefix = NULL, + *forward_to_syslog = NULL, + *forward_to_kmsg = NULL, + *forward_to_console = NULL; + int r; + + assert(stream); + assert(fname); + + if (!stream->state_file) { + stream->state_file = strappend("/run/systemd/journal/streams/", fname); + if (!stream->state_file) + return log_oom(); + } + + r = parse_env_file(stream->state_file, NEWLINE, + "PRIORITY", &priority, + "LEVEL_PREFIX", &level_prefix, + "FORWARD_TO_SYSLOG", &forward_to_syslog, + "FORWARD_TO_KMSG", &forward_to_kmsg, + "FORWARD_TO_CONSOLE", &forward_to_console, + "IDENTIFIER", &stream->identifier, + "UNIT", &stream->unit_id, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to read: %s", stream->state_file); + + if (priority) { + int p; + + p = log_level_from_string(priority); + if (p >= 0) + stream->priority = p; + } + + if (level_prefix) { + r = parse_boolean(level_prefix); + if (r >= 0) + stream->level_prefix = r; + } + + if (forward_to_syslog) { + r = parse_boolean(forward_to_syslog); + if (r >= 0) + stream->forward_to_syslog = r; + } + + if (forward_to_kmsg) { + r = parse_boolean(forward_to_kmsg); + if (r >= 0) + stream->forward_to_kmsg = r; + } + + if (forward_to_console) { + r = parse_boolean(forward_to_console); + if (r >= 0) + stream->forward_to_console = r; + } + + return 0; +} + +static int stdout_stream_restore(Server *s, const char *fname, int fd) { + StdoutStream *stream; + int r; + + assert(s); + assert(fname); + assert(fd >= 0); + + if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { + log_warning("Too many stdout streams, refusing restoring of stream."); + return -ENOBUFS; + } + + r = stdout_stream_install(s, fd, &stream); + if (r < 0) + return r; + + stream->state = STDOUT_STREAM_RUNNING; + stream->fdstore = true; + + /* Ignore all parsing errors */ + (void) stdout_stream_load(stream, fname); + + return 0; +} + +int server_restore_streams(Server *s, FDSet *fds) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r; + + d = opendir("/run/systemd/journal/streams"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m"); + } + + FOREACH_DIRENT(de, d, goto fail) { + unsigned long st_dev, st_ino; + bool found = false; + Iterator i; + int fd; + + if (sscanf(de->d_name, "%lu:%lu", &st_dev, &st_ino) != 2) + continue; + + FDSET_FOREACH(fd, fds, i) { + struct stat st; + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", de->d_name); + + if (S_ISSOCK(st.st_mode) && st.st_dev == st_dev && st.st_ino == st_ino) { + found = true; + break; + } + } + + if (!found) { + /* No file descriptor? Then let's delete the state file */ + log_debug("Cannot restore stream file %s", de->d_name); + unlinkat(dirfd(d), de->d_name, 0); + continue; + } + + fdset_remove(fds, fd); + + r = stdout_stream_restore(s, de->d_name, fd); + if (r < 0) + safe_close(fd); + } + + return 0; + +fail: + return log_error_errno(errno, "Failed to read streams directory: %m"); +} + +int server_open_stdout_socket(Server *s) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; + int r; + + assert(s); + + if (s->stdout_fd < 0) { + s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->stdout_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); + + (void) unlink(sa.un.sun_path); + + r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + (void) chmod(sa.un.sun_path, 0666); + + if (listen(s->stdout_fd, SOMAXCONN) < 0) + return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path); + } else + fd_nonblock(s->stdout_fd, 1); + + r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s); + 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+5); + if (r < 0) + return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m"); + + 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/grp-journal/libjournal-core/journald-stream.h b/src/grp-journal/libjournal-core/journald-stream.h new file mode 100644 index 0000000000..fe88cc34f5 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-stream.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "shared/fdset.h" + +typedef struct StdoutStream StdoutStream; + +#include "journald-server.h" + +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/grp-journal/libjournal-core/journald-syslog.c b/src/grp-journal/libjournal-core/journald-syslog.c new file mode 100644 index 0000000000..bc793235d4 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-syslog.c @@ -0,0 +1,455 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/process-util.h" +#include "basic/selinux-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/syslog-util.h" + +#include "journald-console.h" +#include "journald-kmsg.h" +#include "journald-server.h" +#include "journald-syslog.h" +#include "journald-wall.h" + +/* Warn once every 30s if we missed syslog message */ +#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC) + +static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/syslog", + }; + struct msghdr msghdr = { + .msg_iov = (struct iovec *) iovec, + .msg_iovlen = n_iovec, + .msg_name = (struct sockaddr*) &sa.sa, + .msg_namelen = SOCKADDR_UN_LEN(sa.un), + }; + struct cmsghdr *cmsg; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + + assert(s); + assert(iovec); + assert(n_iovec > 0); + + if (ucred) { + zero(control); + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred)); + msghdr.msg_controllen = cmsg->cmsg_len; + } + + /* Forward the syslog message we received via /dev/log to + * /run/systemd/syslog. Unfortunately we currently can't set + * the SO_TIMESTAMP auxiliary data, and hence we don't. */ + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + + /* The socket is full? I guess the syslog implementation is + * too slow, and we shouldn't wait for that... */ + if (errno == EAGAIN) { + s->n_forward_syslog_missed++; + return; + } + + if (ucred && (errno == ESRCH || errno == EPERM)) { + struct ucred u; + + /* Hmm, presumably the sender process vanished + * by now, or we don't have CAP_SYS_AMDIN, so + * let's fix it as good as we can, and retry */ + + u = *ucred; + u.pid = getpid(); + memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred)); + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + + if (errno == EAGAIN) { + s->n_forward_syslog_missed++; + return; + } + } + + if (errno != ENOENT) + log_debug_errno(errno, "Failed to forward syslog message: %m"); +} + +static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) { + struct iovec iovec; + + assert(s); + assert(buffer); + + if (LOG_PRI(priority) > s->max_level_syslog) + return; + + IOVEC_SET_STRING(iovec, buffer); + forward_syslog_iovec(s, &iovec, 1, ucred, tv); +} + +void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) { + struct iovec iovec[5]; + char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64], + header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1]; + int n = 0; + time_t t; + struct tm *tm; + char *ident_buf = NULL; + + assert(s); + assert(priority >= 0); + assert(priority <= 999); + assert(message); + + if (LOG_PRI(priority) > s->max_level_syslog) + return; + + /* First: priority field */ + xsprintf(header_priority, "<%i>", priority); + IOVEC_SET_STRING(iovec[n++], header_priority); + + /* Second: timestamp */ + t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); + tm = localtime(&t); + if (!tm) + return; + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return; + IOVEC_SET_STRING(iovec[n++], header_time); + + /* Third: identifier and PID */ + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); + + if (identifier) + IOVEC_SET_STRING(iovec[n++], identifier); + + IOVEC_SET_STRING(iovec[n++], header_pid); + } else if (identifier) { + IOVEC_SET_STRING(iovec[n++], identifier); + IOVEC_SET_STRING(iovec[n++], ": "); + } + + /* Fourth: message */ + IOVEC_SET_STRING(iovec[n++], message); + + forward_syslog_iovec(s, iovec, n, ucred, tv); + + free(ident_buf); +} + +int syslog_fixup_facility(int priority) { + + if ((priority & LOG_FACMASK) == 0) + return (priority & LOG_PRIMASK) | LOG_USER; + + return priority; +} + +size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) { + const char *p; + char *t; + size_t l, e; + + assert(buf); + assert(identifier); + assert(pid); + + p = *buf; + + p += strspn(p, WHITESPACE); + l = strcspn(p, WHITESPACE); + + if (l <= 0 || + p[l-1] != ':') + return 0; + + e = l; + l--; + + if (p[l-1] == ']') { + size_t k = l-1; + + for (;;) { + + if (p[k] == '[') { + t = strndup(p+k+1, l-k-2); + if (t) + *pid = t; + + l = k; + break; + } + + if (k == 0) + break; + + k--; + } + } + + t = strndup(p, l); + if (t) + *identifier = t; + + if (strchr(WHITESPACE, p[e])) + e++; + *buf = p + e; + return e; +} + +static void syslog_skip_date(char **buf) { + enum { + LETTER, + SPACE, + NUMBER, + SPACE_OR_NUMBER, + COLON + } sequence[] = { + LETTER, LETTER, LETTER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + SPACE + }; + + char *p; + unsigned i; + + assert(buf); + assert(*buf); + + p = *buf; + + for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { + + if (!*p) + return; + + switch (sequence[i]) { + + case SPACE: + if (*p != ' ') + return; + break; + + case SPACE_OR_NUMBER: + if (*p == ' ') + break; + + /* fall through */ + + case NUMBER: + if (*p < '0' || *p > '9') + return; + + break; + + case LETTER: + if (!(*p >= 'A' && *p <= 'Z') && + !(*p >= 'a' && *p <= 'z')) + return; + + break; + + case COLON: + if (*p != ':') + return; + break; + + } + } + + *buf = p; +} + +void server_process_syslog_message( + Server *s, + const char *buf, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, + size_t label_len) { + + char syslog_priority[sizeof("PRIORITY=") + 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; + int priority = LOG_USER | LOG_INFO; + _cleanup_free_ char *identifier = NULL, *pid = NULL; + const char *orig; + + assert(s); + assert(buf); + + orig = buf; + syslog_parse_priority(&buf, &priority, true); + + if (s->forward_to_syslog) + forward_syslog_raw(s, priority, orig, ucred, tv); + + syslog_skip_date((char**) &buf); + syslog_parse_identifier(&buf, &identifier, &pid); + + if (s->forward_to_kmsg) + server_forward_kmsg(s, priority, identifier, buf, ucred); + + if (s->forward_to_console) + server_forward_console(s, priority, identifier, buf, ucred); + + if (s->forward_to_wall) + server_forward_wall(s, priority, identifier, buf, ucred); + + IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog"); + + xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK); + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (priority & LOG_FACMASK) { + xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)); + IOVEC_SET_STRING(iovec[n++], syslog_facility); + } + + if (identifier) { + syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier); + IOVEC_SET_STRING(iovec[n++], syslog_identifier); + } + + if (pid) { + syslog_pid = strjoina("SYSLOG_PID=", pid); + IOVEC_SET_STRING(iovec[n++], syslog_pid); + } + + message = strjoina("MESSAGE=", buf); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0); +} + +int server_open_syslog_socket(Server *s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/dev-log", + }; + static const int one = 1; + int r; + + assert(s); + + if (s->syslog_fd < 0) { + s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->syslog_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); + + (void) unlink(sa.un.sun_path); + + r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + (void) chmod(sa.un.sun_path, 0666); + } else + fd_nonblock(s->syslog_fd, 1); + + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); + +#ifdef HAVE_SELINUX + 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"); + } +#endif + + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); + + r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, 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; +} + +void server_maybe_warn_forward_syslog_missed(Server *s) { + usec_t n; + + assert(s); + + if (s->n_forward_syslog_missed <= 0) + return; + + n = now(CLOCK_MONOTONIC); + if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n) + return; + + server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, + LOG_MESSAGE("Forwarding to syslog missed %u messages.", + s->n_forward_syslog_missed), + NULL); + + s->n_forward_syslog_missed = 0; + s->last_warn_forward_syslog_missed = n; +} diff --git a/src/grp-journal/libjournal-core/journald-syslog.h b/src/grp-journal/libjournal-core/journald-syslog.h new file mode 100644 index 0000000000..46ad715314 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-syslog.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-server.h" + +int syslog_fixup_facility(int priority) _const_; + +size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); + +void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); + +void server_process_syslog_message(Server *s, const char *buf, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); +int server_open_syslog_socket(Server *s); + +void server_maybe_warn_forward_syslog_missed(Server *s); diff --git a/src/grp-journal/libjournal-core/journald-wall.c b/src/grp-journal/libjournal-core/journald-wall.c new file mode 100644 index 0000000000..0c816de55f --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-wall.c @@ -0,0 +1,72 @@ +/*** + This file is part of systemd. + + Copyright 2014 Sebastian Thorarensen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "shared/utmp-wtmp.h" + +#include "journald-server.h" +#include "journald-wall.h" + +void server_forward_wall( + Server *s, + int priority, + const char *identifier, + const char *message, + const struct ucred *ucred) { + + _cleanup_free_ char *ident_buf = NULL, *l_buf = NULL; + const char *l; + int r; + + assert(s); + assert(message); + + if (LOG_PRI(priority) > s->max_level_wall) + return; + + if (ucred) { + if (!identifier) { + get_process_comm(ucred->pid, &ident_buf); + identifier = ident_buf; + } + + if (asprintf(&l_buf, "%s["PID_FMT"]: %s", strempty(identifier), ucred->pid, message) < 0) { + log_oom(); + return; + } + + l = l_buf; + + } else if (identifier) { + + l = l_buf = strjoin(identifier, ": ", message, NULL); + if (!l_buf) { + log_oom(); + return; + } + } else + l = message; + + r = utmp_wall(l, "systemd-journald", NULL, NULL, NULL); + if (r < 0) + log_debug_errno(r, "Failed to send wall message: %m"); +} diff --git a/src/grp-journal/libjournal-core/journald-wall.h b/src/grp-journal/libjournal-core/journald-wall.h new file mode 100644 index 0000000000..ebc2b89fa8 --- /dev/null +++ b/src/grp-journal/libjournal-core/journald-wall.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Sebastian Thorarensen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-server.h" + +void server_forward_wall(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/grp-journal/libjournal-core/test-audit-type.c b/src/grp-journal/libjournal-core/test-audit-type.c new file mode 100644 index 0000000000..812a3953ea --- /dev/null +++ b/src/grp-journal/libjournal-core/test-audit-type.c @@ -0,0 +1,43 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "sd-journal/audit-type.h" + +static void print_audit_label(int i) { + const char *name; + + name = audit_type_name_alloca(i); + /* This is a separate function only because of alloca */ + printf("%i → %s → %s\n", i, audit_type_to_string(i), name); +} + +static void test_audit_type(void) { + int i; + + for (i = 0; i <= AUDIT_KERNEL; i++) + print_audit_label(i); +} + +int main(int argc, char **argv) { + test_audit_type(); +} diff --git a/src/grp-journal/libjournal-core/test-catalog.c b/src/grp-journal/libjournal-core/test-catalog.c new file mode 100644 index 0000000000..73ffb16104 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-catalog.c @@ -0,0 +1,264 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-journal/catalog.h" + +static const char *catalog_dirs[] = { + CATALOG_DIR, + NULL, +}; + +static const char *no_catalog_dirs[] = { + "/bin/hopefully/with/no/catalog", + NULL +}; + +static Hashmap * test_import(const char* contents, ssize_t size, int code) { + int r; + char name[] = "/tmp/test-catalog.XXXXXX"; + _cleanup_close_ int fd; + Hashmap *h; + + if (size < 0) + size = strlen(contents); + + assert_se(h = hashmap_new(&catalog_hash_ops)); + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(write(fd, contents, size) == size); + + r = catalog_import_file(h, name); + assert_se(r == code); + + unlink(name); + + return h; +} + +static void test_catalog_import_invalid(void) { + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + + h = test_import("xxx", -1, -EINVAL); + assert_se(hashmap_isempty(h)); +} + +static void test_catalog_import_badid(void) { + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + const char *input = +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededede\n" \ +"Subject: message\n" \ +"\n" \ +"payload\n"; + h = test_import(input, -1, -EINVAL); +} + +static void test_catalog_import_one(void) { + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + char *payload; + Iterator j; + + const char *input = +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: message\n" \ +"\n" \ +"payload\n"; + const char *expect = +"Subject: message\n" \ +"\n" \ +"payload\n"; + + h = test_import(input, -1, 0); + assert_se(hashmap_size(h) == 1); + + HASHMAP_FOREACH(payload, h, j) { + printf("expect: %s\n", expect); + printf("actual: %s\n", payload); + assert_se(streq(expect, payload)); + } +} + +static void test_catalog_import_merge(void) { + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + char *payload; + Iterator j; + + const char *input = +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: message\n" \ +"Defined-By: me\n" \ +"\n" \ +"payload\n" \ +"\n" \ +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: override subject\n" \ +"X-Header: hello\n" \ +"\n" \ +"override payload\n"; + + const char *combined = +"Subject: override subject\n" \ +"X-Header: hello\n" \ +"Subject: message\n" \ +"Defined-By: me\n" \ +"\n" \ +"override payload\n"; + + h = test_import(input, -1, 0); + assert_se(hashmap_size(h) == 1); + + HASHMAP_FOREACH(payload, h, j) { + assert_se(streq(combined, payload)); + } +} + +static void test_catalog_import_merge_no_body(void) { + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + char *payload; + Iterator j; + + const char *input = +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: message\n" \ +"Defined-By: me\n" \ +"\n" \ +"payload\n" \ +"\n" \ +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: override subject\n" \ +"X-Header: hello\n" \ +"\n"; + + const char *combined = +"Subject: override subject\n" \ +"X-Header: hello\n" \ +"Subject: message\n" \ +"Defined-By: me\n" \ +"\n" \ +"payload\n"; + + h = test_import(input, -1, 0); + assert_se(hashmap_size(h) == 1); + + HASHMAP_FOREACH(payload, h, j) { + assert_se(streq(combined, payload)); + } +} + +static const char* database = NULL; + +static void test_catalog_update(void) { + static char name[] = "/tmp/test-catalog.XXXXXX"; + int r; + + r = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(r >= 0); + + database = name; + + /* Test what happens if there are no files. */ + r = catalog_update(database, NULL, NULL); + assert_se(r >= 0); + + /* Test what happens if there are no files in the directory. */ + r = catalog_update(database, NULL, no_catalog_dirs); + assert_se(r >= 0); + + /* Make sure that we at least have some files loaded or the + catalog_list below will fail. */ + r = catalog_update(database, NULL, catalog_dirs); + assert_se(r >= 0); +} + +static void test_catalog_file_lang(void) { + _cleanup_free_ char *lang = NULL, *lang2 = NULL, *lang3 = NULL, *lang4 = NULL; + + assert_se(catalog_file_lang("systemd.de_DE.catalog", &lang) == 1); + assert_se(streq(lang, "de_DE")); + + assert_se(catalog_file_lang("systemd..catalog", &lang2) == 0); + assert_se(lang2 == NULL); + + assert_se(catalog_file_lang("systemd.fr.catalog", &lang2) == 1); + assert_se(streq(lang2, "fr")); + + assert_se(catalog_file_lang("systemd.fr.catalog.gz", &lang3) == 0); + assert_se(lang3 == NULL); + + assert_se(catalog_file_lang("systemd.01234567890123456789012345678901.catalog", &lang3) == 0); + assert_se(lang3 == NULL); + + assert_se(catalog_file_lang("systemd.0123456789012345678901234567890.catalog", &lang3) == 1); + assert_se(streq(lang3, "0123456789012345678901234567890")); + + assert_se(catalog_file_lang("/x/y/systemd.catalog", &lang4) == 0); + assert_se(lang4 == NULL); + + assert_se(catalog_file_lang("/x/y/systemd.ru_RU.catalog", &lang4) == 1); + assert_se(streq(lang4, "ru_RU")); +} + +int main(int argc, char *argv[]) { + _cleanup_free_ char *text = NULL; + int r; + + setlocale(LC_ALL, "de_DE.UTF-8"); + + log_parse_environment(); + log_open(); + + test_catalog_file_lang(); + + test_catalog_import_invalid(); + test_catalog_import_badid(); + test_catalog_import_one(); + test_catalog_import_merge(); + test_catalog_import_merge_no_body(); + + test_catalog_update(); + + r = catalog_list(stdout, database, true); + assert_se(r >= 0); + + r = catalog_list(stdout, database, false); + assert_se(r >= 0); + + assert_se(catalog_get(database, SD_MESSAGE_COREDUMP, &text) >= 0); + printf(">>>%s<<<\n", text); + + if (database) + unlink(database); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-compress-benchmark.c b/src/grp-journal/libjournal-core/test-compress-benchmark.c new file mode 100644 index 0000000000..4292e4f8cb --- /dev/null +++ b/src/grp-journal/libjournal-core/test-compress-benchmark.c @@ -0,0 +1,180 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-journal/compress.h" + +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, const char *type) { + char *buf; + size_t i; + + buf = malloc(count); + assert_se(buf); + + 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, const char* type, + compress_t compress, decompress_t decompress) { + usec_t n, n2 = 0; + float dt; + + _cleanup_free_ char *text, *buf; + _cleanup_free_ void *buf2 = NULL; + size_t buf2_allocated = 0; + size_t skipped = 0, compressed = 0, total = 0; + + text = make_buf(MAX_SIZE, type); + buf = calloc(MAX_SIZE + 1, 1); + assert_se(text && buf); + + n = now(CLOCK_MONOTONIC); + + for (size_t i = 0; i <= MAX_SIZE; i++) { + size_t j = 0, k = 0, size; + int r; + + size = permute(i); + if (size == 0) + continue; + + 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[size] == 0); + if (r != 0) { + skipped += size; + continue; + } + + assert_se(j > 0); + 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 == size); + + assert_se(memcmp(text, buf2, size) == 0); + + total += size; + compressed += j; + + n2 = now(CLOCK_MONOTONIC); + if (n2 - n > arg_duration) + break; + } + + dt = (n2-n) / 1e6; + + log_info("%s/%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), " + "mean compresion %.2f%%, skipped %zu bytes", + 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_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_atozu(argv[2], &arg_start); + else + arg_start = getpid(); + + NULSTR_FOREACH(i, "zeros\0simple\0random\0") { +#ifdef HAVE_XZ + test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz); +#endif +#ifdef HAVE_LZ4 + test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4); +#endif + } + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-compress.c b/src/grp-journal/libjournal-core/test-compress.c new file mode 100644 index 0000000000..5682c28e8c --- /dev/null +++ b/src/grp-journal/libjournal-core/test-compress.c @@ -0,0 +1,308 @@ +/*** + This file is part of systemd + + Copyright 2014 Ronny Chevalier + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_LZ4 +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/random-util.h" +#include "basic/util.h" +#include "sd-journal/compress.h" + +#ifdef HAVE_XZ +# define XZ_OK 0 +#else +# define XZ_OK -EPROTONOSUPPORT +#endif + +#ifdef HAVE_LZ4 +# define LZ4_OK 0 +#else +# define LZ4_OK -EPROTONOSUPPORT +#endif + +typedef int (compress_blob_t)(const void *src, uint64_t src_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); +typedef int (decompress_sw_t)(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); + +typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes); +typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); + +static void test_compress_decompress(int compression, + compress_blob_t compress, + decompress_blob_t decompress, + const char *data, + size_t data_len, + bool may_fail) { + char compressed[512]; + 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, sizeof(compressed), &csize); + if (r == -ENOBUFS) { + log_info_errno(r, "compression failed: %m"); + assert_se(may_fail); + } else { + assert_se(r == 0); + r = decompress(compressed, csize, + (void **) &decompressed, &usize, &csize, 0); + assert_se(r == 0); + assert_se(decompressed); + assert_se(memcmp(decompressed, data, data_len) == 0); + } + + r = decompress("garbage", 7, + (void **) &decompressed, &usize, &csize, 0); + assert_se(r < 0); + + /* make sure to have the minimal lz4 compressed size */ + r = decompress("00000000\1g", 9, + (void **) &decompressed, &usize, &csize, 0); + assert_se(r < 0); + + r = decompress("\100000000g", 9, + (void **) &decompressed, &usize, &csize, 0); + assert_se(r < 0); + + memzero(decompressed, usize); +} + +static void test_decompress_startswith(int compression, + compress_blob_t compress, + decompress_sw_t decompress_sw, + const char *data, + size_t data_len, + bool may_fail) { + + 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 %.20s text*/", + object_compressed_to_string(compression), data); + +#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); + + compressed = compressed2 = malloc(BUFSIZE_2); + assert_se(compressed2); + r = compress(data, data_len, compressed, BUFSIZE_2, &csize); + assert(r == 0); + } + assert_se(r == 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, + const char* cat, + compress_stream_t compress, + decompress_stream_t decompress, + const char *srcfile) { + + _cleanup_close_ int src = -1, dst = -1, dst2 = -1; + char pattern[] = "/tmp/systemd-test.compressed.XXXXXX", + pattern2[] = "/tmp/systemd-test.compressed.XXXXXX"; + int r; + _cleanup_free_ char *cmd = NULL, *cmd2; + struct stat st = {}; + + log_debug("/* testing %s compression */", + object_compressed_to_string(compression)); + + log_debug("/* create source from %s */", srcfile); + + assert_se((src = open(srcfile, O_RDONLY|O_CLOEXEC)) >= 0); + + log_debug("/* test compression */"); + + assert_se((dst = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC)) >= 0); + + assert_se(compress(src, dst, -1) == 0); + + if (cat) { + assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0); + assert_se(system(cmd) == 0); + } + + log_debug("/* test decompression */"); + + assert_se((dst2 = mkostemp_safe(pattern2, O_RDWR|O_CLOEXEC)) >= 0); + + assert_se(stat(srcfile, &st) == 0); + + assert_se(lseek(dst, 0, SEEK_SET) == 0); + r = decompress(dst, dst2, st.st_size); + assert_se(r == 0); + + assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0); + assert_se(system(cmd2) == 0); + + log_debug("/* test faulty decompression */"); + + assert_se(lseek(dst, 1, SEEK_SET) == 1); + r = decompress(dst, dst2, st.st_size); + assert_se(r == -EBADMSG || r == 0); + + assert_se(lseek(dst, 0, SEEK_SET) == 0); + assert_se(lseek(dst2, 0, SEEK_SET) == 0); + r = decompress(dst, dst2, st.st_size - 1); + assert_se(r == -EFBIG); + + assert_se(unlink(pattern) == 0); + 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" + "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"; + + 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); + +#ifdef HAVE_XZ + test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz, + 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 + log_info("/* XZ test skipped */"); +#endif + +#ifdef HAVE_LZ4 + test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4, + 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); + + 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 + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-enum.c b/src/grp-journal/libjournal-core/test-journal-enum.c new file mode 100644 index 0000000000..588e12c800 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-enum.c @@ -0,0 +1,53 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/log.h" +#include "basic/macro.h" +#include "sd-journal/journal-internal.h" + +int main(int argc, char *argv[]) { + unsigned n = 0; + _cleanup_(sd_journal_closep) sd_journal*j = NULL; + + log_set_max_level(LOG_DEBUG); + + assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0); + + assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0); + assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0); + + SD_JOURNAL_FOREACH_BACKWARDS(j) { + const void *d; + size_t l; + + assert_se(sd_journal_get_data(j, "MESSAGE", &d, &l) >= 0); + + printf("%.*s\n", (int) l, (char*) d); + + n++; + if (n >= 10) + break; + } + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-flush.c b/src/grp-journal/libjournal-core/test-journal-flush.c new file mode 100644 index 0000000000..66ae8f83c4 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-flush.c @@ -0,0 +1,75 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-internal.h" + +int main(int argc, char *argv[]) { + _cleanup_free_ char *fn = NULL; + char dn[] = "/var/tmp/test-journal-flush.XXXXXX"; + JournalFile *new_journal = NULL; + sd_journal *j = NULL; + unsigned n = 0; + int r; + + assert_se(mkdtemp(dn)); + fn = strappend(dn, "/test.journal"); + + r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); + assert_se(r >= 0); + + r = sd_journal_open(&j, 0); + assert_se(r >= 0); + + sd_journal_set_data_threshold(j, 0); + + SD_JOURNAL_FOREACH(j) { + Object *o; + JournalFile *f; + + f = j->current_file; + assert_se(f && f->current_offset > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + assert_se(r >= 0); + + r = journal_file_copy_entry(f, new_journal, o, f->current_offset, NULL, NULL, NULL); + assert_se(r >= 0); + + n++; + if (n > 10000) + break; + } + + sd_journal_close(j); + + (void) journal_file_close(new_journal); + + unlink(fn); + assert_se(rmdir(dn) == 0); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-init.c b/src/grp-journal/libjournal-core/test-journal-init.c new file mode 100644 index 0000000000..c5f1d345e9 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-init.c @@ -0,0 +1,64 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/rm-rf.h" +#include "basic/util.h" + +int main(int argc, char *argv[]) { + sd_journal *j; + int r, i, I = 100; + char t[] = "/tmp/journal-stream-XXXXXX"; + + log_set_max_level(LOG_DEBUG); + + if (argc >= 2) { + r = safe_atoi(argv[1], &I); + if (r < 0) + log_info("Could not parse loop count argument. Using default."); + } + + log_info("Running %d loops", I); + + assert_se(mkdtemp(t)); + + for (i = 0; i < I; i++) { + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + assert_se(r == 0); + + sd_journal_close(j); + + r = sd_journal_open_directory(&j, t, 0); + assert_se(r == 0); + + sd_journal_close(j); + + j = NULL; + r = sd_journal_open_directory(&j, t, SD_JOURNAL_LOCAL_ONLY); + assert_se(r == -EINVAL); + assert_se(j == NULL); + } + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-interleaving.c b/src/grp-journal/libjournal-core/test-journal-interleaving.c new file mode 100644 index 0000000000..004394d18a --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-interleaving.c @@ -0,0 +1,307 @@ +/*** + This file is part of systemd. + + Copyright 2013 Marius Vollmer + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/rm-rf.h" +#include "basic/util.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-vacuum.h" + +/* This program tests skipping around in a multi-file journal. + */ + +static bool arg_keep = false; + +noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) { + log_internal(LOG_CRIT, 0, file, line, func, + "'%s' failed at %s:%u (%s): %s.", + text, file, line, func, strerror(eno)); + abort(); +} + +#define assert_ret(expr) \ + do { \ + int _r_ = (expr); \ + if (_unlikely_(_r_ < 0)) \ + log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) + +static JournalFile *test_open(const char *name) { + JournalFile *f; + assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); + return f; +} + +static void test_close(JournalFile *f) { + (void) journal_file_close (f); +} + +static void append_number(JournalFile *f, int n, uint64_t *seqnum) { + char *p; + dual_timestamp ts; + static dual_timestamp previous_ts = {}; + struct iovec iovec[1]; + + dual_timestamp_get(&ts); + + if (ts.monotonic <= previous_ts.monotonic) + ts.monotonic = previous_ts.monotonic + 1; + + if (ts.realtime <= previous_ts.realtime) + ts.realtime = previous_ts.realtime + 1; + + previous_ts = ts; + + assert_se(asprintf(&p, "NUMBER=%d", n) >= 0); + iovec[0].iov_base = p; + iovec[0].iov_len = strlen(p); + assert_ret(journal_file_append_entry(f, &ts, iovec, 1, seqnum, NULL, NULL)); + free(p); +} + +static void test_check_number (sd_journal *j, int n) { + const void *d; + _cleanup_free_ char *k; + size_t l; + int x; + + assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l)); + assert_se(k = strndup(d, l)); + printf("%s\n", k); + + assert_se(safe_atoi(k + 7, &x) >= 0); + assert_se(n == x); +} + +static void test_check_numbers_down (sd_journal *j, int count) { + int i; + + for (i = 1; i <= count; i++) { + int r; + test_check_number(j, i); + assert_ret(r = sd_journal_next(j)); + if (i == count) + assert_se(r == 0); + else + assert_se(r == 1); + } + +} + +static void test_check_numbers_up (sd_journal *j, int count) { + for (int i = count; i >= 1; i--) { + int r; + test_check_number(j, i); + assert_ret(r = sd_journal_previous(j)); + if (i == 1) + assert_se(r == 0); + else + assert_se(r == 1); + } + +} + +static void setup_sequential(void) { + JournalFile *one, *two; + one = test_open("one.journal"); + two = test_open("two.journal"); + append_number(one, 1, NULL); + append_number(one, 2, NULL); + append_number(two, 3, NULL); + append_number(two, 4, NULL); + test_close(one); + test_close(two); +} + +static void setup_interleaved(void) { + JournalFile *one, *two; + one = test_open("one.journal"); + two = test_open("two.journal"); + append_number(one, 1, NULL); + append_number(two, 2, NULL); + append_number(one, 3, NULL); + append_number(two, 4, NULL); + test_close(one); + test_close(two); +} + +static void test_skip(void (*setup)(void)) { + char t[] = "/tmp/journal-skip-XXXXXX"; + sd_journal *j; + int r; + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + setup(); + + /* Seek to head, iterate down. + */ + assert_ret(sd_journal_open_directory(&j, t, 0)); + assert_ret(sd_journal_seek_head(j)); + assert_ret(sd_journal_next(j)); + test_check_numbers_down(j, 4); + sd_journal_close(j); + + /* Seek to tail, iterate up. + */ + assert_ret(sd_journal_open_directory(&j, t, 0)); + assert_ret(sd_journal_seek_tail(j)); + assert_ret(sd_journal_previous(j)); + test_check_numbers_up(j, 4); + sd_journal_close(j); + + /* Seek to tail, skip to head, iterate down. + */ + assert_ret(sd_journal_open_directory(&j, t, 0)); + assert_ret(sd_journal_seek_tail(j)); + assert_ret(r = sd_journal_previous_skip(j, 4)); + assert_se(r == 4); + test_check_numbers_down(j, 4); + sd_journal_close(j); + + /* Seek to head, skip to tail, iterate up. + */ + assert_ret(sd_journal_open_directory(&j, t, 0)); + assert_ret(sd_journal_seek_head(j)); + assert_ret(r = sd_journal_next_skip(j, 4)); + assert_se(r == 4); + test_check_numbers_up(j, 4); + sd_journal_close(j); + + log_info("Done..."); + + if (arg_keep) + log_info("Not removing %s", t); + else { + journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + } + + puts("------------------------------------------------------------"); +} + +static void test_sequence_numbers(void) { + + char t[] = "/tmp/journal-seq-XXXXXX"; + JournalFile *one, *two; + uint64_t seqnum = 0; + sd_id128_t seqnum_id; + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, + true, false, NULL, NULL, NULL, NULL, &one) == 0); + + append_number(one, 1, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 1); + append_number(one, 2, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 2); + + assert_se(one->header->state == STATE_ONLINE); + assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id)); + assert_se(!sd_id128_equal(one->header->file_id, one->header->boot_id)); + assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id)); + + memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t)); + + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, + true, false, NULL, NULL, NULL, one, &two) == 0); + + assert_se(two->header->state == STATE_ONLINE); + assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id)); + assert_se(sd_id128_equal(one->header->machine_id, one->header->machine_id)); + assert_se(sd_id128_equal(one->header->boot_id, one->header->boot_id)); + assert_se(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id)); + + append_number(two, 3, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 3); + append_number(two, 4, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 4); + + test_close(two); + + append_number(one, 5, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 5); + + append_number(one, 6, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 6); + + test_close(one); + + /* restart server */ + seqnum = 0; + + assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0, + true, false, NULL, NULL, NULL, NULL, &two) == 0); + + assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id)); + + append_number(two, 7, &seqnum); + printf("seqnum=%"PRIu64"\n", seqnum); + assert_se(seqnum == 5); + + /* So..., here we have the same seqnum in two files with the + * same seqnum_id. */ + + test_close(two); + + log_info("Done..."); + + if (arg_keep) + log_info("Not removing %s", t); + else { + journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + } +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + + /* journal_file_open requires a valid machine id */ + if (access("/etc/machine-id", F_OK) != 0) + return EXIT_TEST_SKIP; + + arg_keep = argc > 1; + + test_skip(setup_sequential); + test_skip(setup_interleaved); + + test_sequence_numbers(); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-match.c b/src/grp-journal/libjournal-core/test-journal-match.c new file mode 100644 index 0000000000..967e30c46a --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-match.c @@ -0,0 +1,76 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-journal/journal-internal.h" + +int main(int argc, char *argv[]) { + _cleanup_(sd_journal_closep) sd_journal*j = NULL; + _cleanup_free_ char *t; + + log_set_max_level(LOG_DEBUG); + + assert_se(sd_journal_open(&j, 0) >= 0); + + assert_se(sd_journal_add_match(j, "foobar", 0) < 0); + assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0); + assert_se(sd_journal_add_match(j, "", 0) < 0); + assert_se(sd_journal_add_match(j, "=", 0) < 0); + assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0); + assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); + assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0); + assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0); + + assert_se(sd_journal_add_disjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0); + assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0); + assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0); + + assert_se(sd_journal_add_conjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0); + + assert_se(sd_journal_add_disjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0); + + assert_se(t = journal_make_match_string(j)); + + printf("resulting match expression is: %s\n", t); + + assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))")); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-send.c b/src/grp-journal/libjournal-core/test-journal-send.c new file mode 100644 index 0000000000..192e12c887 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-send.c @@ -0,0 +1,102 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/macro.h" + +int main(int argc, char *argv[]) { + char huge[4096*1024]; + + /* 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; + assert_se(sd_journal_perror("Foobar") == 0); + + assert_se(sd_journal_perror("") == 0); + + memset(huge, 'x', sizeof(huge)); + memcpy(huge, "HUGE=", 5); + char_array_0(huge); + + assert_se(sd_journal_send("MESSAGE=Huge field attached", + huge, + NULL) == 0); + + 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!"); + + 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); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-stream.c b/src/grp-journal/libjournal-core/test-journal-stream.c new file mode 100644 index 0000000000..b71c44ef56 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-stream.c @@ -0,0 +1,196 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/rm-rf.h" +#include "basic/util.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-internal.h" + +#define N_ENTRIES 200 + +static void verify_contents(sd_journal *j, unsigned skip) { + unsigned i; + + assert_se(j); + + i = 0; + SD_JOURNAL_FOREACH(j) { + const void *d; + char *k, *c; + size_t l; + unsigned u = 0; + + assert_se(sd_journal_get_cursor(j, &k) >= 0); + printf("cursor: %s\n", k); + free(k); + + assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) d); + + assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); + assert_se(k = strndup(d, l)); + printf("\t%s\n", k); + + if (skip > 0) { + assert_se(safe_atou(k + 7, &u) >= 0); + assert_se(i == u); + i += skip; + } + + free(k); + + assert_se(sd_journal_get_cursor(j, &c) >= 0); + assert_se(sd_journal_test_cursor(j, c) > 0); + free(c); + } + + if (skip > 0) + assert_se(i == N_ENTRIES); +} + +int main(int argc, char *argv[]) { + JournalFile *one, *two, *three; + char t[] = "/tmp/journal-stream-XXXXXX"; + unsigned i; + _cleanup_(sd_journal_closep) sd_journal *j = NULL; + char *z; + const void *data; + size_t l; + dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL; + + /* journal_file_open requires a valid machine id */ + if (access("/etc/machine-id", F_OK) != 0) + return EXIT_TEST_SKIP; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); + assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); + + for (i = 0; i < N_ENTRIES; i++) { + char *p, *q; + dual_timestamp ts; + struct iovec iovec[2]; + + dual_timestamp_get(&ts); + + if (ts.monotonic <= previous_ts.monotonic) + ts.monotonic = previous_ts.monotonic + 1; + + if (ts.realtime <= previous_ts.realtime) + ts.realtime = previous_ts.realtime + 1; + + previous_ts = ts; + + assert_se(asprintf(&p, "NUMBER=%u", i) >= 0); + iovec[0].iov_base = p; + iovec[0].iov_len = strlen(p); + + assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0); + + iovec[1].iov_base = q; + iovec[1].iov_len = strlen(q); + + if (i % 10 == 0) + assert_se(journal_file_append_entry(three, &ts, iovec, 2, NULL, NULL, NULL) == 0); + else { + if (i % 3 == 0) + assert_se(journal_file_append_entry(two, &ts, iovec, 2, NULL, NULL, NULL) == 0); + + assert_se(journal_file_append_entry(one, &ts, iovec, 2, NULL, NULL, NULL) == 0); + } + + free(p); + free(q); + } + + (void) journal_file_close(one); + (void) journal_file_close(two); + (void) journal_file_close(three); + + assert_se(sd_journal_open_directory(&j, t, 0) >= 0); + + assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); + SD_JOURNAL_FOREACH_BACKWARDS(j) { + _cleanup_free_ char *c; + + assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) data); + + assert_se(sd_journal_get_cursor(j, &c) >= 0); + assert_se(sd_journal_test_cursor(j, c) > 0); + } + + SD_JOURNAL_FOREACH(j) { + _cleanup_free_ char *c; + + assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) data); + + assert_se(sd_journal_get_cursor(j, &c) >= 0); + assert_se(sd_journal_test_cursor(j, c) > 0); + } + + sd_journal_flush_matches(j); + + verify_contents(j, 1); + + printf("NEXT TEST\n"); + assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); + + assert_se(z = journal_make_match_string(j)); + printf("resulting match expression is: %s\n", z); + free(z); + + verify_contents(j, 5); + + printf("NEXT TEST\n"); + sd_journal_flush_matches(j); + assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0); + + assert_se(z = journal_make_match_string(j)); + printf("resulting match expression is: %s\n", z); + free(z); + + verify_contents(j, 0); + + assert_se(sd_journal_query_unique(j, "NUMBER") >= 0); + SD_JOURNAL_FOREACH_UNIQUE(j, data, l) + printf("%.*s\n", (int) l, (const char*) data); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-syslog.c b/src/grp-journal/libjournal-core/test-journal-syslog.c new file mode 100644 index 0000000000..4756126478 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-syslog.c @@ -0,0 +1,45 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" + +#include "journald-syslog.h" + +static void test_syslog_parse_identifier(const char* str, + const char *ident, const char*pid, int ret) { + const char *buf = str; + _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; + int ret2; + + ret2 = syslog_parse_identifier(&buf, &ident2, &pid2); + + assert_se(ret == ret2); + assert_se(ident == ident2 || streq_ptr(ident, ident2)); + assert_se(pid == pid2 || streq_ptr(pid, pid2)); +} + +int main(void) { + test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); + test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal-verify.c b/src/grp-journal/libjournal-core/test-journal-verify.c new file mode 100644 index 0000000000..485315c118 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal-verify.c @@ -0,0 +1,150 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/rm-rf.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-verify.h" + +#define N_ENTRIES 6000 +#define RANDOM_RANGE 77 + +static void bit_toggle(const char *fn, uint64_t p) { + uint8_t b; + ssize_t r; + int fd; + + fd = open(fn, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + + r = pread(fd, &b, 1, p/8); + assert_se(r == 1); + + b ^= 1 << (p % 8); + + r = pwrite(fd, &b, 1, p/8); + assert_se(r == 1); + + safe_close(fd); +} + +static int raw_verify(const char *fn, const char *verification_key) { + JournalFile *f; + int r; + + r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); + if (r < 0) + return r; + + r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false); + (void) journal_file_close(f); + + return r; +} + +int main(int argc, char *argv[]) { + char t[] = "/tmp/journal-XXXXXX"; + unsigned n; + JournalFile *f; + const char *verification_key = argv[1]; + usec_t from = 0, to = 0, total = 0; + char a[FORMAT_TIMESTAMP_MAX]; + char b[FORMAT_TIMESTAMP_MAX]; + char c[FORMAT_TIMESPAN_MAX]; + struct stat st; + uint64_t p; + + /* journal_file_open requires a valid machine id */ + if (access("/etc/machine-id", F_OK) != 0) + return EXIT_TEST_SKIP; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + log_info("Generating..."); + + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + + for (n = 0; n < N_ENTRIES; n++) { + struct iovec iovec; + struct dual_timestamp ts; + char *test; + + dual_timestamp_get(&ts); + + assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE)); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + free(test); + } + + (void) journal_file_close(f); + + log_info("Verifying..."); + + assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + /* journal_file_print_header(f); */ + journal_file_dump(f); + + assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0); + + if (verification_key && JOURNAL_HEADER_SEALED(f->header)) + log_info("=> Validated from %s to %s, %s missing", + format_timestamp(a, sizeof(a), from), + format_timestamp(b, sizeof(b), to), + format_timespan(c, sizeof(c), total > to ? total - to : 0, 0)); + + (void) journal_file_close(f); + + if (verification_key) { + log_info("Toggling bits..."); + + assert_se(stat("test.journal", &st) >= 0); + + for (p = 38448*8+0; p < ((uint64_t) st.st_size * 8); p ++) { + bit_toggle("test.journal", p); + + log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8); + + if (raw_verify("test.journal", verification_key) >= 0) + log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8); + + bit_toggle("test.journal", p); + } + } + + log_info("Exiting..."); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-journal.c b/src/grp-journal/libjournal-core/test-journal.c new file mode 100644 index 0000000000..05de15f0c0 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-journal.c @@ -0,0 +1,178 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/log.h" +#include "basic/rm-rf.h" +#include "sd-journal/journal-authenticate.h" +#include "sd-journal/journal-file.h" +#include "sd-journal/journal-vacuum.h" + +static bool arg_keep = false; + +static void test_non_empty(void) { + dual_timestamp ts; + JournalFile *f; + struct iovec iovec; + static const char test[] = "TEST1=1", test2[] = "TEST2=2"; + Object *o; + uint64_t p; + char t[] = "/tmp/journal-XXXXXX"; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); + + dual_timestamp_get(&ts); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + iovec.iov_base = (void*) test2; + iovec.iov_len = strlen(test2); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + +#ifdef HAVE_GCRYPT + journal_file_append_tag(f); +#endif + journal_file_dump(f); + + assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); + + assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); + + assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); + + assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 0); + + assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); + + assert_se(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); + + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); + + assert_se(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); + + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); + + assert_se(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); + + assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); + + assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); + + assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); + + assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); + + journal_file_rotate(&f, true, true, NULL); + journal_file_rotate(&f, true, true, NULL); + + (void) journal_file_close(f); + + log_info("Done..."); + + if (arg_keep) + log_info("Not removing %s", t); + else { + journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + } + + puts("------------------------------------------------------------"); +} + +static void test_empty(void) { + JournalFile *f1, *f2, *f3, *f4; + char t[] = "/tmp/journal-XXXXXX"; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); + + assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); + + assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); + + assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); + + journal_file_print_header(f1); + puts(""); + journal_file_print_header(f2); + puts(""); + journal_file_print_header(f3); + puts(""); + journal_file_print_header(f4); + puts(""); + + log_info("Done..."); + + if (arg_keep) + log_info("Not removing %s", t); + else { + journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); + + assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + } + + (void) journal_file_close(f1); + (void) journal_file_close(f2); + (void) journal_file_close(f3); + (void) journal_file_close(f4); +} + +int main(int argc, char *argv[]) { + arg_keep = argc > 1; + + /* journal_file_open requires a valid machine id */ + if (access("/etc/machine-id", F_OK) != 0) + return EXIT_TEST_SKIP; + + test_non_empty(); + test_empty(); + + return 0; +} diff --git a/src/grp-journal/libjournal-core/test-mmap-cache.c b/src/grp-journal/libjournal-core/test-mmap-cache.c new file mode 100644 index 0000000000..3eb5444480 --- /dev/null +++ b/src/grp-journal/libjournal-core/test-mmap-cache.c @@ -0,0 +1,79 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "sd-journal/mmap-cache.h" + +int main(int argc, char *argv[]) { + int x, y, z, r; + char px[] = "/tmp/testmmapXXXXXXX", py[] = "/tmp/testmmapYXXXXXX", pz[] = "/tmp/testmmapZXXXXXX"; + MMapCache *m; + void *p, *q; + + assert_se(m = mmap_cache_new()); + + x = mkostemp_safe(px, O_RDWR|O_CLOEXEC); + assert_se(x >= 0); + unlink(px); + + y = mkostemp_safe(py, O_RDWR|O_CLOEXEC); + assert_se(y >= 0); + unlink(py); + + z = mkostemp_safe(pz, O_RDWR|O_CLOEXEC); + assert_se(z >= 0); + unlink(pz); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 1, 2, NULL, &p); + assert_se(r >= 0); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 2, 2, NULL, &q); + assert_se(r >= 0); + + assert_se((uint8_t*) p + 1 == (uint8_t*) q); + + r = mmap_cache_get(m, x, PROT_READ, 1, false, 3, 2, NULL, &q); + assert_se(r >= 0); + + assert_se((uint8_t*) p + 2 == (uint8_t*) q); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p); + assert_se(r >= 0); + + r = mmap_cache_get(m, x, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q); + assert_se(r >= 0); + + assert_se((uint8_t*) p + 1 == (uint8_t*) q); + + mmap_cache_unref(m); + + safe_close(x); + safe_close(y); + safe_close(z); + + return 0; +} diff --git a/src/grp-journal/systemd-cat/Makefile b/src/grp-journal/systemd-cat/Makefile new file mode 100644 index 0000000000..77c0861f2c --- /dev/null +++ b/src/grp-journal/systemd-cat/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_cat_SOURCES = \ + src/journal/cat.c + +systemd_cat_LDADD = \ + libjournal-core.la + +bin_PROGRAMS += \ + systemd-cat + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/systemd-cat/cat.c b/src/grp-journal/systemd-cat/cat.c new file mode 100644 index 0000000000..8ab4febd53 --- /dev/null +++ b/src/grp-journal/systemd-cat/cat.c @@ -0,0 +1,161 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/syslog-util.h" +#include "basic/util.h" + +static const char *arg_identifier = NULL; +static int arg_priority = LOG_INFO; +static bool arg_level_prefix = true; + +static void help(void) { + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Execute process with stdout/stderr connected to the journal.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -t --identifier=STRING Set syslog identifier\n" + " -p --priority=PRIORITY Set priority value (0..7)\n" + " --level-prefix=BOOL Control whether level prefix shall be parsed\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_LEVEL_PREFIX + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "identifier", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 't': + if (isempty(optarg)) + arg_identifier = NULL; + else + arg_identifier = optarg; + break; + + case 'p': + arg_priority = log_level_from_string(optarg); + if (arg_priority < 0) { + log_error("Failed to parse priority value."); + return -EINVAL; + } + break; + + case ARG_LEVEL_PREFIX: { + int k; + + k = parse_boolean(optarg); + if (k < 0) + return log_error_errno(k, "Failed to parse level prefix value."); + + arg_level_prefix = k; + break; + } + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + _cleanup_close_ int fd = -1, saved_stderr = -1; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); + if (fd < 0) { + r = log_error_errno(fd, "Failed to create stream fd: %m"); + goto finish; + } + + saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); + + if (dup3(fd, STDOUT_FILENO, 0) < 0 || + dup3(fd, STDERR_FILENO, 0) < 0) { + r = log_error_errno(errno, "Failed to duplicate fd: %m"); + goto finish; + } + + if (fd >= 3) + safe_close(fd); + fd = -1; + + if (argc <= optind) + (void) execl("/bin/cat", "/bin/cat", NULL); + else + (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) + (void) dup3(saved_stderr, STDERR_FILENO, 0); + + log_error_errno(r, "Failed to execute process: %m"); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-journal/systemd-cat/systemd-cat.completion.bash b/src/grp-journal/systemd-cat/systemd-cat.completion.bash new file mode 100644 index 0000000000..8d84042af1 --- /dev/null +++ b/src/grp-journal/systemd-cat/systemd-cat.completion.bash @@ -0,0 +1,57 @@ +# systemd-cat(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd_cat() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version' + [ARG]='-t --identifier -p --priority --level-prefix' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --identifier|-t) + comps='' + ;; + --priority|-p) + comps='emerg alert crit err warning notice info debug' + ;; + --level-prefix) + comps='yes no' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_cat systemd-cat diff --git a/src/grp-journal/systemd-cat/systemd-cat.completion.zsh b/src/grp-journal/systemd-cat/systemd-cat.completion.zsh new file mode 100644 index 0000000000..7487b00ee8 --- /dev/null +++ b/src/grp-journal/systemd-cat/systemd-cat.completion.zsh @@ -0,0 +1,12 @@ +#compdef systemd-cat + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version.]' \ + {-t+,--identifier=}'[Set syslog identifier.]:syslog identifier:' \ + {-p+,--priority=}'[Set priority value.]:value:({0..7})' \ + '--level-prefix=[Control whether level prefix shall be parsed.]:boolean:(1 0)' \ + ':Message' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-journal/systemd-cat/systemd-cat.xml b/src/grp-journal/systemd-cat/systemd-cat.xml new file mode 100644 index 0000000000..160db9fb5c --- /dev/null +++ b/src/grp-journal/systemd-cat/systemd-cat.xml @@ -0,0 +1,178 @@ + + + + + + + + + systemd-cat + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cat + 1 + + + + systemd-cat + Connect a pipeline or program's output with the journal + + + + + systemd-cat OPTIONS COMMAND ARGUMENTS + + + systemd-cat OPTIONS + + + + + Description + + systemd-cat may be used to connect the + standard input and output of a process to the journal, or as a + filter tool in a shell pipeline to pass the output the previous + pipeline element generates to the journal. + + If no parameter is passed, systemd-cat + will write everything it reads from standard input (stdin) to the + journal. + + If parameters are passed, they are executed as command line + with standard output (stdout) and standard error output (stderr) + connected to the journal, so that all it writes is stored in the + journal. + + + + Options + + The following options are understood: + + + + + + + + + + Specify a short string that is used to + identify the logging tool. If not specified, no identification + string is written to the journal. + + + + + + + Specify the default priority level for the + logged messages. Pass one of + emerg, + alert, + crit, + err, + warning, + notice, + info, + debug, or a + value between 0 and 7 (corresponding to the same named + levels). These priority values are the same as defined by + syslog3. + Defaults to info. Note that this simply + controls the default, individual lines may be logged with + different levels if they are prefixed accordingly. For details, + see below. + + + + + + Controls whether lines read are parsed for + syslog priority level prefixes. If enabled (the default), a + line prefixed with a priority prefix such as + <5> is logged at priority 5 + (notice), and similar for the other + priority levels. Takes a boolean argument. + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + Examples + + + Invoke a program + + This calls /bin/ls + with standard output and error connected to the journal: + + # systemd-cat ls + + + + Usage in a shell pipeline + + This builds a shell pipeline also invoking + /bin/ls and writes the output it generates + to the journal: + + # ls | systemd-cat + + + Even though the two examples have very similar effects the + first is preferable since only one process is running at a time, + and both stdout and stderr are captured while in the second + example, only stdout is captured. + + + + See Also + + systemd1, + systemctl1, + logger1 + + + + diff --git a/src/grp-journal/systemd-journald/Makefile b/src/grp-journal/systemd-journald/Makefile new file mode 100644 index 0000000000..4f05218d88 --- /dev/null +++ b/src/grp-journal/systemd-journald/Makefile @@ -0,0 +1,85 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_journald_SOURCES = \ + src/journal/journald.c \ + src/journal/journald-server.h + +systemd_journald_LDADD = \ + libjournal-core.la \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-journald + +dist_systemunit_DATA += \ + units/systemd-journald.socket \ + units/systemd-journald-dev-log.socket \ + units/systemd-journald-audit.socket + +nodist_systemunit_DATA += \ + units/systemd-journald.service + +dist_pkgsysconf_DATA += \ + src/journal/journald.conf + +nodist_catalog_DATA = \ + catalog/systemd.bg.catalog \ + catalog/systemd.be.catalog \ + catalog/systemd.be@latin.catalog \ + catalog/systemd.fr.catalog \ + catalog/systemd.it.catalog \ + catalog/systemd.pl.catalog \ + catalog/systemd.pt_BR.catalog \ + catalog/systemd.ru.catalog \ + catalog/systemd.zh_CN.catalog \ + catalog/systemd.zh_TW.catalog \ + catalog/systemd.catalog + +EXTRA_DIST += \ + $(nodist_catalog_DATA:.catalog=.catalog.in) + +# Note that we don't use @@ for replacement markers here, but %%. This is +# because the catalog uses @@ already for its runtime replacement handling and +# we don't want to conflict with that. +$(outdir)/%.catalog: catalog/%.catalog.in + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ + $(SED) -e 's~%SUPPORT_URL%~$(SUPPORT_URL)~' < $< > $@ + +SOCKETS_TARGET_WANTS += \ + systemd-journald.socket \ + systemd-journald-dev-log.socket \ + systemd-journald-audit.socket + +SYSINIT_TARGET_WANTS += \ + systemd-journald.service + +EXTRA_DIST += \ + units/systemd-journald.service.in + +gperf_gperf_sources += \ + src/journal/journald-gperf.gperf + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-journal/systemd-journald/journald.c b/src/grp-journal/systemd-journald/journald.c new file mode 100644 index 0000000000..cd887feed8 --- /dev/null +++ b/src/grp-journal/systemd-journald/journald.c @@ -0,0 +1,120 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include + +#include "basic/formats-util.h" +#include "basic/sigbus.h" +#include "journald-kmsg.h" +#include "journald-server.h" +#include "journald-syslog.h" +#include "sd-journal/journal-authenticate.h" + +int main(int argc, char *argv[]) { + Server server; + int r; + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SAFE); + log_set_facility(LOG_SYSLOG); + log_parse_environment(); + log_open(); + + umask(0022); + + sigbus_install(); + + r = server_init(&server); + if (r < 0) + goto finish; + + 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, + LOG_MESSAGE("Journal started"), + NULL); + + for (;;) { + usec_t t = USEC_INFINITY, n; + + r = sd_event_get_state(server.event); + if (r < 0) + goto finish; + if (r == SD_EVENT_FINISHED) + break; + + n = now(CLOCK_REALTIME); + + if (server.max_retention_usec > 0 && server.oldest_file_usec > 0) { + + /* The retention time is reached, so let's vacuum! */ + if (server.oldest_file_usec + server.max_retention_usec < n) { + log_info("Retention time reached."); + server_rotate(&server); + server_vacuum(&server, false, false); + continue; + } + + /* Calculate when to rotate the next time */ + t = server.oldest_file_usec + server.max_retention_usec - n; + } + +#ifdef HAVE_GCRYPT + if (server.system_journal) { + usec_t u; + + if (journal_file_next_evolve_usec(server.system_journal, &u)) { + if (n >= u) + t = 0; + else + t = MIN(t, u - n); + } + } +#endif + + r = sd_event_run(server.event, t); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + + server_maybe_append_tags(&server); + server_maybe_warn_forward_syslog_missed(&server); + } + + log_debug("systemd-journald stopped as pid "PID_FMT, getpid()); + server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, + LOG_MESSAGE("Journal stopped"), + NULL); + +finish: + server_done(&server); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-journal/systemd-journald/journald.conf b/src/grp-journal/systemd-journald/journald.conf new file mode 100644 index 0000000000..2541b949be --- /dev/null +++ b/src/grp-journal/systemd-journald/journald.conf @@ -0,0 +1,41 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See journald.conf(5) for details. + +[Journal] +#Storage=auto +#Compress=yes +#Seal=yes +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitIntervalSec=30s +#RateLimitBurst=1000 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#SystemMaxFiles=100 +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#RuntimeMaxFiles=100 +#MaxRetentionSec= +#MaxFileSec=1month +#ForwardToSyslog=no +#ForwardToKMsg=no +#ForwardToConsole=no +#ForwardToWall=yes +#TTYPath=/dev/console +#MaxLevelStore=debug +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +#MaxLevelConsole=info +#MaxLevelWall=emerg diff --git a/src/grp-journal/systemd-journald/journald.conf.xml b/src/grp-journal/systemd-journald/journald.conf.xml new file mode 100644 index 0000000000..fef4fde898 --- /dev/null +++ b/src/grp-journal/systemd-journald/journald.conf.xml @@ -0,0 +1,411 @@ + + + + + + + + journald.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + journald.conf + 5 + + + + journald.conf + journald.conf.d + Journal service configuration files + + + + /etc/systemd/journald.conf + /etc/systemd/journald.conf.d/*.conf + /run/systemd/journald.conf.d/*.conf + /usr/lib/systemd/journald.conf.d/*.conf + + + + Description + + These files configure various parameters of the systemd + journal service, + systemd-journald.service8. + + + + + + + Options + + All options are configured in the + [Journal] section: + + + + + Storage= + + Controls where to store journal data. One of + volatile, + persistent, + auto and + none. If + volatile, journal + log data will be stored only in memory, i.e. below the + /run/log/journal hierarchy (which is + created if needed). If persistent, data + will be stored preferably on disk, i.e. below the + /var/log/journal hierarchy (which is + created if needed), with a fallback to + /run/log/journal (which is created if + needed), during early boot and if the disk is not writable. + auto is similar to + persistent but the directory + /var/log/journal is not created if + needed, so that its existence controls where log data goes. + none turns off all storage, all log data + received will be dropped. Forwarding to other targets, such as + the console, the kernel log buffer, or a syslog socket will + still work however. Defaults to + auto. + + + + Compress= + + Takes a boolean value. If enabled (the + default), data objects that shall be stored in the journal and + are larger than a certain threshold are compressed before they + are written to the file system. + + + + Seal= + + Takes a boolean value. If enabled (the + default), and a sealing key is available (as created by + journalctl1's + command), Forward Secure Sealing + (FSS) for all persistent journal files is enabled. FSS is + based on Seekable Sequential Key + Generators by G. A. Marson and B. Poettering + (doi:10.1007/978-3-642-40203-6_7) and may be used to protect + journal files from unnoticed alteration. + + + + SplitMode= + + Controls whether to split up journal files per user. Split-up journal files are primarily + useful for access control: on UNIX/Linux access control is managed per file, and the journal daemon will assign + users read access to their journal files. This setting takes one of uid, + login or none. If uid, all regular users will get each + their own journal files regardless of whether their processes possess login sessions or not, however system + users will log into the system journal. If login, actually logged-in users will get each + their own journal files, but users without login session and system users will log into the system + journal. Note that in this mode, user code running outside of any login session will log into the system log + instead of the split-out user logs. Most importantly, this means that information about core dumps of user + processes collected via the + systemd-coredump8 subsystem + will end up in the system logs instead of the user logs, and thus not be accessible to the owning users. If + none, journal files are not split up by user and all messages are instead stored in the + single system journal. In this mode unprivileged users generally do not have access to their own log data. Note + that splitting up journal files by user is only available for journals stored persistently. If journals are + stored on volatile storage (see above), only a single journal file for all user IDs is kept. Defaults to + uid. + + + + RateLimitIntervalSec= + RateLimitBurst= + + Configures the rate limiting that is applied + to all messages generated on the system. If, in the time + interval defined by RateLimitIntervalSec=, + more messages than specified in + RateLimitBurst= are logged by a service, + all further messages within the interval are dropped until the + interval is over. A message about the number of dropped + messages is generated. This rate limiting is applied + per-service, so that two services which log do not interfere + with each other's limits. Defaults to 1000 messages in 30s. + The time specification for + RateLimitIntervalSec= may be specified in the + following units: s, min, + h, ms, + us. To turn off any kind of rate limiting, + set either value to 0. + + + + SystemMaxUse= + SystemKeepFree= + SystemMaxFileSize= + SystemMaxFiles= + RuntimeMaxUse= + RuntimeKeepFree= + RuntimeMaxFileSize= + RuntimeMaxFiles= + + Enforce size limits on the journal files + stored. The options prefixed with System + apply to the journal files when stored on a persistent file + system, more specifically + /var/log/journal. The options prefixed + with Runtime apply to the journal files + when stored on a volatile in-memory file system, more + specifically /run/log/journal. The former + is used only when /var is mounted, + writable, and the directory + /var/log/journal exists. Otherwise, only + the latter applies. Note that this means that during early + boot and if the administrator disabled persistent logging, + only the latter options apply, while the former apply if + persistent logging is enabled and the system is fully booted + up. journalctl and + systemd-journald ignore all files with + names not ending with .journal or + .journal~, so only such files, located in + the appropriate directories, are taken into account when + calculating current disk usage. + + SystemMaxUse= and + RuntimeMaxUse= control how much disk space + the journal may use up at most. + SystemKeepFree= and + RuntimeKeepFree= control how much disk + space systemd-journald shall leave free for other uses. + systemd-journald will respect both limits + and use the smaller of the two values. + + The first pair defaults to 10% and the second to 15% of + the size of the respective file system, but each value is + capped to 4G. If the file system is nearly full and either + SystemKeepFree= or + RuntimeKeepFree= are violated when + systemd-journald is started, the limit will be raised to the + percentage that is actually free. This means that if there was + enough free space before and journal files were created, and + subsequently something else causes the file system to fill up, + journald will stop using more space, but it will not be + removing existing files to reduce the footprint again, + either. + + SystemMaxFileSize= and + RuntimeMaxFileSize= control how large + individual journal files may grow at most. This influences + the granularity in which disk space is made available through + rotation, i.e. deletion of historic data. Defaults to one + eighth of the values configured with + SystemMaxUse= and + RuntimeMaxUse=, so that usually seven + rotated journal files are kept as history. + + Specify values in bytes or use K, M, G, T, P, E as + units for the specified sizes (equal to 1024, 1024², ... bytes). + Note that size limits are enforced synchronously when journal + files are extended, and no explicit rotation step triggered by + time is needed. + + SystemMaxFiles= and + RuntimeMaxFiles= control how many + individual journal files to keep at most. Note that only + archived files are deleted to reduce the number of files until + this limit is reached; active files will stay around. This + means that, in effect, there might still be more journal files + around in total than this limit after a vacuuming operation is + complete. This setting defaults to 100. + + + + MaxFileSec= + + The maximum time to store entries in a single + journal file before rotating to the next one. Normally, + time-based rotation should not be required as size-based + rotation with options such as + SystemMaxFileSize= should be sufficient to + ensure that journal files do not grow without bounds. However, + to ensure that not too much data is lost at once when old + journal files are deleted, it might make sense to change this + value from the default of one month. Set to 0 to turn off this + feature. This setting takes time values which may be suffixed + with the units year, + month, week, + day, h or + m to override the default time unit of + seconds. + + + + MaxRetentionSec= + + The maximum time to store journal entries. + This controls whether journal files containing entries older + then the specified time span are deleted. Normally, time-based + deletion of old journal files should not be required as + size-based deletion with options such as + SystemMaxUse= should be sufficient to + ensure that journal files do not grow without bounds. However, + to enforce data retention policies, it might make sense to + change this value from the default of 0 (which turns off this + feature). This setting also takes time values which may be + suffixed with the units year, + month, week, + day, h or + m to override the default time unit of + seconds. + + + + + SyncIntervalSec= + + The timeout before synchronizing journal files + to disk. After syncing, journal files are placed in the + OFFLINE state. Note that syncing is unconditionally done + immediately after a log message of priority CRIT, ALERT or + EMERG has been logged. This setting hence applies only to + messages of the levels ERR, WARNING, NOTICE, INFO, DEBUG. The + default timeout is 5 minutes. + + + + ForwardToSyslog= + ForwardToKMsg= + ForwardToConsole= + ForwardToWall= + + Control whether log messages received by the + journal daemon shall be forwarded to a traditional syslog + daemon, to the kernel log buffer (kmsg), to the system + console, or sent as wall messages to all logged-in users. + These options take boolean arguments. If forwarding to syslog + is enabled but nothing reads messages from the socket, + forwarding to syslog has no effect. By default, only + forwarding to wall is enabled. These settings may be + overridden at boot time with the kernel command line options + systemd.journald.forward_to_syslog=, + systemd.journald.forward_to_kmsg=, + systemd.journald.forward_to_console=, and + systemd.journald.forward_to_wall=. When + forwarding to the console, the TTY to log to can be changed + with TTYPath=, described + below. + + + + MaxLevelStore= + MaxLevelSyslog= + MaxLevelKMsg= + MaxLevelConsole= + MaxLevelWall= + + Controls the maximum log level of messages + that are stored on disk, forwarded to syslog, kmsg, the + console or wall (if that is enabled, see above). As argument, + takes one of + emerg, + alert, + crit, + err, + warning, + notice, + info, + debug, + or integer values in the range of 0–7 (corresponding to the + same levels). Messages equal or below the log level specified + are stored/forwarded, messages above are dropped. Defaults to + debug for MaxLevelStore= + and MaxLevelSyslog=, to ensure that the all + messages are written to disk and forwarded to syslog. Defaults + to + notice for MaxLevelKMsg=, + info for MaxLevelConsole=, + and emerg for + MaxLevelWall=. + + + + TTYPath= + + Change the console TTY to use if + ForwardToConsole=yes is used. Defaults to + /dev/console. + + + + + + + + Forwarding to traditional syslog daemons + + + Journal events can be transferred to a different logging daemon + in two different ways. With the first method, messages are + immediately forwarded to a socket + (/run/systemd/journal/syslog), where the + traditional syslog daemon can read them. This method is + controlled by the ForwardToSyslog= option. With a + second method, a syslog daemon behaves like a normal journal + client, and reads messages from the journal files, similarly to + journalctl1. + With this, messages do not have to be read immediately, + which allows a logging daemon which is only started late in boot + to access all messages since the start of the system. In + addition, full structured meta-data is available to it. This + method of course is available only if the messages are stored in + a journal file at all. So it will not work if + Storage=none is set. It should be noted that + usually the second method is used by syslog + daemons, so the Storage= option, and not the + ForwardToSyslog= option, is relevant for them. + + + + + See Also + + systemd1, + systemd-journald.service8, + journalctl1, + systemd.journal-fields7, + systemd-system.conf5 + + + + diff --git a/src/grp-journal/systemd-journald/systemd-journald-audit.socket b/src/grp-journal/systemd-journald/systemd-journald-audit.socket new file mode 100644 index 0000000000..541f2cf38d --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald-audit.socket @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Audit Socket +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target +ConditionSecurity=audit +ConditionCapability=CAP_AUDIT_READ + +[Socket] +Service=systemd-journald.service +ReceiveBuffer=128M +ListenNetlink=audit 1 +PassCredentials=yes diff --git a/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket b/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket new file mode 100644 index 0000000000..ffd44bb507 --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald-dev-log.socket @@ -0,0 +1,32 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Socket (/dev/log) +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +Service=systemd-journald.service +ListenDatagram=/run/systemd/journal/dev-log +Symlinks=/dev/log +SocketMode=0666 +PassCredentials=yes +PassSecurity=yes + +# Increase both the send and receive buffer, so that things don't +# block early. Note that journald internally uses the this socket both +# for receiving syslog messages, and for forwarding them to any other +# syslog, hence we bump both values. +ReceiveBuffer=8M +SendBuffer=8M diff --git a/src/grp-journal/systemd-journald/systemd-journald.service.in b/src/grp-journal/systemd-journald/systemd-journald.service.in new file mode 100644 index 0000000000..08ace8ae44 --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.service.in @@ -0,0 +1,34 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Service +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Requires=systemd-journald.socket +After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket syslog.socket +Before=sysinit.target + +[Service] +Type=notify +Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket +ExecStart=@rootlibexecdir@/systemd-journald +Restart=always +RestartSec=0 +NotifyAccess=all +StandardOutput=null +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE +WatchdogSec=3min +FileDescriptorStoreMax=1024 +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io + +# Increase the default a bit in order to allow many simultaneous +# services being run since we keep one fd open per service. Also, when +# flushing journal files to disk, we might need a lot of fds when many +# journal files are combined. +LimitNOFILE=16384 diff --git a/src/grp-journal/systemd-journald/systemd-journald.service.xml b/src/grp-journal/systemd-journald/systemd-journald.service.xml new file mode 100644 index 0000000000..2810638bc2 --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.service.xml @@ -0,0 +1,276 @@ + + + + + + + + + systemd-journald.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-journald.service + 8 + + + + systemd-journald.service + systemd-journald.socket + systemd-journald-dev-log.socket + systemd-journald-audit.socket + systemd-journald + Journal service + + + + systemd-journald.service + systemd-journald.socket + systemd-journald-dev-log.socket + systemd-journald-audit.socket + /usr/lib/systemd/systemd-journald + + + + Description + + systemd-journald is a system service + that collects and stores logging data. It creates and maintains + structured, indexed journals based on logging information that is + received from a variety of sources: + + + Kernel log messages, via kmsg + + Simple system log messages, via the libc + syslog3 + call + + Structured system log messages via the native + Journal API, see + sd_journal_print4 + + Standard output and standard error of system + services + + Audit records, via the audit + subsystem + + + The daemon will implicitly collect numerous metadata fields + for each log messages in a secure and unfakeable way. See + systemd.journal-fields7 + for more information about the collected metadata. + + + Log data collected by the journal is primarily text-based + but can also include binary data where necessary. All objects + stored in the journal can be up to 2^64-1 bytes in size. + + By default, the journal stores log data in + /run/log/journal/. Since + /run/ is volatile, log data is lost at + reboot. To make the data persistent, it is sufficient to create + /var/log/journal/ where + systemd-journald will then store the + data: + + mkdir -p /var/log/journal +systemd-tmpfiles --create --prefix /var/log/journal + + See + journald.conf5 + for information about the configuration of this service. + + + + Signals + + + + SIGUSR1 + + Request that journal data from + /run/ is flushed to + /var/ in order to make it persistent (if + this is enabled). This must be used after + /var/ is mounted, as otherwise log data + from /run is never flushed to + /var regardless of the configuration. The + journalctl --flush command uses this signal + to request flushing of the journal files, and then waits for + the operation to complete. See + journalctl1 + for details. + + + + SIGUSR2 + + Request immediate rotation of the journal + files. The journalctl --rotate command uses + this signal to request journal file + rotation. + + + + SIGRTMIN+1 + + Request that all unwritten log data is written + to disk. The journalctl --sync command uses + this signal to trigger journal synchronization, and then waits + for the operation to complete. + + + + + + Kernel Command Line + + A few configuration parameters from + journald.conf may be overridden on the kernel + command line: + + + + systemd.journald.forward_to_syslog= + systemd.journald.forward_to_kmsg= + systemd.journald.forward_to_console= + systemd.journald.forward_to_wall= + + Enables/disables forwarding of collected log + messages to syslog, the kernel log buffer, the system console + or wall. + + + See + journald.conf5 + for information about these settings. + + + + + + + + Access Control + + Journal files are, by default, owned and readable by the + systemd-journal system group but are not + writable. Adding a user to this group thus enables her/him to read + the journal files. + + By default, each logged in user will get her/his own set of + journal files in /var/log/journal/. These + files will not be owned by the user, however, in order to avoid + that the user can write to them directly. Instead, file system + ACLs are used to ensure the user gets read access only. + + Additional users and groups may be granted access to journal + files via file system access control lists (ACL). Distributions + and administrators may choose to grant read access to all members + of the wheel and adm system + groups with a command such as the following: + + # setfacl -Rnm g:wheel:rx,d:g:wheel:rx,g:adm:rx,d:g:adm:rx /var/log/journal/ + + Note that this command will update the ACLs both for + existing journal files and for future journal files created in the + /var/log/journal/ directory. + + + + Files + + + + /etc/systemd/journald.conf + + Configure + systemd-journald + behavior. See + journald.conf5. + + + + + /run/log/journal/machine-id/*.journal + /run/log/journal/machine-id/*.journal~ + /var/log/journal/machine-id/*.journal + /var/log/journal/machine-id/*.journal~ + + systemd-journald writes + entries to files in + /run/log/journal/machine-id/ + or + /var/log/journal/machine-id/ + with the .journal suffix. If the daemon is + stopped uncleanly, or if the files are found to be corrupted, + they are renamed using the .journal~ + suffix, and systemd-journald starts writing + to a new file. /run is used when + /var/log/journal is not available, or + when is set in the + journald.conf5 + configuration file. + + + + /dev/kmsg + /dev/log + /run/systemd/journal/dev-log + /run/systemd/journal/socket + /run/systemd/journal/stdout + + Sockets and other paths that + systemd-journald will listen on that are + visible in the file system. In addition to these, journald can + listen for audit events using netlink. + + + + + + See Also + + systemd1, + journalctl1, + journald.conf5, + systemd.journal-fields7, + sd-journal3, + systemd-coredump8, + setfacl1, + sd_journal_print4, + pydoc systemd.journal + + + + diff --git a/src/grp-journal/systemd-journald/systemd-journald.socket b/src/grp-journal/systemd-journald/systemd-journald.socket new file mode 100644 index 0000000000..71737014ca --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.socket @@ -0,0 +1,26 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Socket +Documentation=man:systemd-journald.service(8) man:journald.conf(5) +DefaultDependencies=no +Before=sockets.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +ListenStream=/run/systemd/journal/stdout +ListenDatagram=/run/systemd/journal/socket +SocketMode=0666 +PassCredentials=yes +PassSecurity=yes +ReceiveBuffer=8M +Service=systemd-journald.service diff --git a/src/grp-journal/systemd-journald/systemd-journald.sysusers b/src/grp-journal/systemd-journald/systemd-journald.sysusers new file mode 100644 index 0000000000..dcb01f606a --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +g systemd-journal - - diff --git a/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 b/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 new file mode 100644 index 0000000000..2e8bd8cbef --- /dev/null +++ b/src/grp-journal/systemd-journald/systemd-journald.tmpfiles.m4 @@ -0,0 +1,55 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/log 0755 root root - + +z /run/log/journal 2755 root systemd-journal - - +Z /run/log/journal/%m ~2750 root systemd-journal - - +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r-- +'',`` +a+ /run/log/journal/%m - - - - d:group:adm:r-x +a+ /run/log/journal/%m - - - - group:adm:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:adm:r-- +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /run/log/journal/%m - - - - d:group:wheel:r-x +a+ /run/log/journal/%m - - - - group:wheel:r-x +a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r-- +'')')')m4_dnl + +z /var/log/journal 2755 root systemd-journal - - +z /var/log/journal/%m 2755 root systemd-journal - - +z /var/log/journal/%m/system.journal 0640 root systemd-journal - - +m4_ifdef(`HAVE_ACL',`m4_dnl +m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /var/log/journal - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /var/log/journal - - - - group:adm:r-x,group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x +a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x +a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r-- +'', `` +a+ /var/log/journal - - - - d:group:adm:r-x +a+ /var/log/journal - - - - group:adm:r-x +a+ /var/log/journal/%m - - - - d:group:adm:r-x +a+ /var/log/journal/%m - - - - group:adm:r-x +a+ /var/log/journal/%m/system.journal - - - - group:adm:r-- +'')',`m4_dnl +m4_ifdef(`ENABLE_WHEEL_GROUP',`` +a+ /var/log/journal - - - - d:group:wheel:r-x +a+ /var/log/journal - - - - group:wheel:r-x +a+ /var/log/journal/%m - - - - d:group:wheel:r-x +a+ /var/log/journal/%m - - - - group:wheel:r-x +a+ /var/log/journal/%m/system.journal - - - - group:wheel:r-- +'')')')m4_dnl diff --git a/src/grp-locale/Makefile b/src/grp-locale/Makefile new file mode 100644 index 0000000000..082d1f8eba --- /dev/null +++ b/src/grp-locale/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += localectl +nested.subdirs += systemd-localed + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-locale/localectl/Makefile b/src/grp-locale/localectl/Makefile new file mode 100644 index 0000000000..2544d16309 --- /dev/null +++ b/src/grp-locale/localectl/Makefile @@ -0,0 +1,44 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_LOCALED),) + +localectl_SOURCES = \ + src/locale/localectl.c + +localectl_LDADD = \ + libsystemd-shared.la + +bin_PROGRAMS += \ + localectl + +dist_bashcompletion_data += \ + shell-completion/bash/localectl + +dist_zshcompletion_data += \ + shell-completion/zsh/_localectl +endif # ENABLE_LOCALED + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-locale/localectl/localectl.c b/src/grp-locale/localectl/localectl.c new file mode 100644 index 0000000000..b33ebc1640 --- /dev/null +++ b/src/grp-locale/localectl/localectl.c @@ -0,0 +1,683 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/locale-util.h" +#include "basic/set.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/pager.h" +#include "shared/spawn-polkit-agent.h" + +static bool arg_no_pager = false; +static bool arg_ask_password = true; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_convert = true; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + char **locale; + char *vconsole_keymap; + char *vconsole_keymap_toggle; + char *x11_layout; + char *x11_model; + char *x11_variant; + char *x11_options; +} StatusInfo; + +static void status_info_clear(StatusInfo *info) { + if (info) { + strv_free(info->locale); + free(info->vconsole_keymap); + free(info->vconsole_keymap_toggle); + free(info->x11_layout); + free(info->x11_model); + free(info->x11_variant); + free(info->x11_options); + zero(*info); + } +} + +static void print_overridden_variables(void) { + int r; + char *variables[_VARIABLE_LC_MAX] = {}; + LocaleVariable j; + bool print_warning = true; + + if (detect_container() > 0 || arg_host) + return; + + r = parse_env_file("/proc/cmdline", WHITESPACE, + "locale.LANG", &variables[VARIABLE_LANG], + "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], + "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], + "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], + "locale.LC_TIME", &variables[VARIABLE_LC_TIME], + "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], + "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], + "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], + "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], + "locale.LC_NAME", &variables[VARIABLE_LC_NAME], + "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], + "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], + "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], + "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r < 0 && r != -ENOENT) { + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); + goto finish; + } + + for (j = 0; j < _VARIABLE_LC_MAX; j++) + if (variables[j]) { + if (print_warning) { + log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" + " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); + + print_warning = false; + } else + log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); + } + finish: + for (j = 0; j < _VARIABLE_LC_MAX; j++) + free(variables[j]); +} + +static void print_status_info(StatusInfo *i) { + assert(i); + + if (strv_isempty(i->locale)) + puts(" System Locale: n/a"); + else { + char **j; + + printf(" System Locale: %s\n", i->locale[0]); + STRV_FOREACH(j, i->locale + 1) + printf(" %s\n", *j); + } + + printf(" VC Keymap: %s\n", strna(i->vconsole_keymap)); + if (!isempty(i->vconsole_keymap_toggle)) + printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle); + + printf(" X11 Layout: %s\n", strna(i->x11_layout)); + if (!isempty(i->x11_model)) + printf(" X11 Model: %s\n", i->x11_model); + if (!isempty(i->x11_variant)) + printf(" X11 Variant: %s\n", i->x11_variant); + if (!isempty(i->x11_options)) + printf(" X11 Options: %s\n", i->x11_options); +} + +static int show_status(sd_bus *bus, char **args, unsigned n) { + _cleanup_(status_info_clear) StatusInfo info = {}; + static const struct bus_properties_map map[] = { + { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) }, + { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) }, + { "VConsoleKeymapToggle", "s", NULL, offsetof(StatusInfo, vconsole_keymap_toggle) }, + { "X11Layout", "s", NULL, offsetof(StatusInfo, x11_layout) }, + { "X11Model", "s", NULL, offsetof(StatusInfo, x11_model) }, + { "X11Variant", "s", NULL, offsetof(StatusInfo, x11_variant) }, + { "X11Options", "s", NULL, offsetof(StatusInfo, x11_options) }, + { "Locale", "as", NULL, offsetof(StatusInfo, locale) }, + {} + }; + int r; + + assert(bus); + + r = bus_map_all_properties(bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + map, + &info); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + print_overridden_variables(); + print_status_info(&info); + + return r; +} + +static int set_locale(sd_bus *bus, char **args, unsigned n) { + _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); + assert(args); + + polkit_agent_open_if_enabled(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetLocale"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, args + 1); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "b", arg_ask_password); + 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 issue method call: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int list_locales(sd_bus *bus, char **args, unsigned n) { + _cleanup_strv_free_ char **l = NULL; + int r; + + assert(args); + + r = get_locales(&l); + if (r < 0) + return log_error_errno(r, "Failed to read list of locales: %m"); + + pager_open(arg_no_pager, false); + strv_print(l); + + return 0; +} + +static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *map, *toggle_map; + int r; + + assert(bus); + assert(args); + + if (n > 3) { + log_error("Too many arguments."); + return -EINVAL; + } + + polkit_agent_open_if_enabled(); + + map = args[1]; + toggle_map = n > 2 ? args[2] : ""; + + r = sd_bus_call_method( + bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetVConsoleKeyboard", + &error, + NULL, + "ssbb", map, toggle_map, arg_convert, arg_ask_password); + if (r < 0) + log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); + + return r; +} + +static Set *keymaps = NULL; + +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + char *p, *e; + int r; + + if (tflag != FTW_F) + return 0; + + if (!endswith(fpath, ".map") && + !endswith(fpath, ".map.gz")) + return 0; + + p = strdup(basename(fpath)); + if (!p) + return log_oom(); + + e = endswith(p, ".map"); + if (e) + *e = 0; + + e = endswith(p, ".map.gz"); + if (e) + *e = 0; + + r = set_consume(keymaps, p); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Can't add keymap: %m"); + + return 0; +} + +static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) { + _cleanup_strv_free_ char **l = NULL; + const char *dir; + + keymaps = set_new(&string_hash_ops); + if (!keymaps) + return log_oom(); + + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) + nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS); + + l = set_get_strv(keymaps); + if (!l) { + set_free_free(keymaps); + return log_oom(); + } + + set_free(keymaps); + + if (strv_isempty(l)) { + log_error("Couldn't find any console keymaps."); + return -ENOENT; + } + + strv_sort(l); + + pager_open(arg_no_pager, false); + + strv_print(l); + + return 0; +} + +static int set_x11_keymap(sd_bus *bus, char **args, unsigned n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *layout, *model, *variant, *options; + int r; + + assert(bus); + assert(args); + + if (n > 5) { + log_error("Too many arguments."); + return -EINVAL; + } + + polkit_agent_open_if_enabled(); + + layout = args[1]; + model = n > 2 ? args[2] : ""; + variant = n > 3 ? args[3] : ""; + options = n > 4 ? args[4] : ""; + + r = sd_bus_call_method( + bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetX11Keyboard", + &error, + NULL, + "ssssbb", layout, model, variant, options, + arg_convert, arg_ask_password); + if (r < 0) + log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); + + return r; +} + +static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **list = NULL; + char line[LINE_MAX]; + enum { + NONE, + MODELS, + LAYOUTS, + VARIANTS, + OPTIONS + } state = NONE, look_for; + int r; + + if (n > 2) { + log_error("Too many arguments."); + return -EINVAL; + } + + f = fopen("/usr/share/X11/xkb/rules/base.lst", "re"); + if (!f) + return log_error_errno(errno, "Failed to open keyboard mapping list. %m"); + + if (streq(args[0], "list-x11-keymap-models")) + look_for = MODELS; + else if (streq(args[0], "list-x11-keymap-layouts")) + look_for = LAYOUTS; + else if (streq(args[0], "list-x11-keymap-variants")) + look_for = VARIANTS; + else if (streq(args[0], "list-x11-keymap-options")) + look_for = OPTIONS; + else + assert_not_reached("Wrong parameter"); + + FOREACH_LINE(line, f, break) { + char *l, *w; + + l = strstrip(line); + + if (isempty(l)) + continue; + + if (l[0] == '!') { + if (startswith(l, "! model")) + state = MODELS; + else if (startswith(l, "! layout")) + state = LAYOUTS; + else if (startswith(l, "! variant")) + state = VARIANTS; + else if (startswith(l, "! option")) + state = OPTIONS; + else + state = NONE; + + continue; + } + + if (state != look_for) + continue; + + w = l + strcspn(l, WHITESPACE); + + if (n > 1) { + char *e; + + if (*w == 0) + continue; + + *w = 0; + w++; + w += strspn(w, WHITESPACE); + + e = strchr(w, ':'); + if (!e) + continue; + + *e = 0; + + if (!streq(w, args[1])) + continue; + } else + *w = 0; + + r = strv_extend(&list, l); + if (r < 0) + return log_oom(); + } + + if (strv_isempty(list)) { + log_error("Couldn't find any entries."); + return -ENOENT; + } + + strv_sort(list); + strv_uniq(list); + + pager_open(arg_no_pager, false); + + strv_print(list); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...] COMMAND ...\n\n" + "Query or change system locale and keyboard settings.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --no-convert Don't convert keyboard mappings\n\n" + "Commands:\n" + " status Show current locale settings\n" + " set-locale LOCALE... Set system locale\n" + " list-locales Show known locales\n" + " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n" + " list-keymaps Show known virtual console keyboard mappings\n" + " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n" + " Set X11 and console keyboard mappings\n" + " list-x11-keymap-models Show known X11 keyboard mapping models\n" + " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n" + " list-x11-keymap-variants [LAYOUT]\n" + " Show known X11 keyboard mapping variants\n" + " list-x11-keymap-options Show known X11 keyboard mapping options\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_CONVERT, + ARG_NO_ASK_PASSWORD + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "no-convert", no_argument, NULL, ARG_NO_CONVERT }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_CONVERT: + arg_convert = false; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int localectl_main(sd_bus *bus, int argc, char *argv[]) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(sd_bus *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-locale", MORE, 2, set_locale }, + { "list-locales", EQUAL, 1, list_locales }, + { "set-keymap", MORE, 2, set_vconsole_keymap }, + { "list-keymaps", EQUAL, 1, list_vconsole_keymaps }, + { "set-x11-keymap", MORE, 2, set_x11_keymap }, + { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps }, + { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps }, + { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps }, + { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char*argv[]) { + sd_bus *bus = NULL; + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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; + } + + r = localectl_main(bus, argc, argv); + +finish: + sd_bus_flush_close_unref(bus); + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-locale/localectl/localectl.completion.bash b/src/grp-locale/localectl/localectl.completion.bash new file mode 100644 index 0000000000..e0c06a794e --- /dev/null +++ b/src/grp-locale/localectl/localectl.completion.bash @@ -0,0 +1,92 @@ +# localectl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__locale_fields=( LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME \ + LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER \ + LC_NAME LC_ADDRESS LC_TELEPHONE \ + LC_MEASUREMENT LC_IDENTIFICATION ) +# LC_ALL is omitted on purpose + +_localectl() { + local i verb comps locale_vals + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --no-convert --no-pager --no-ask-password + -H --host --machine' + + if __contains_word "$prev" $OPTS; then + case $prev in + --host|-H) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ $cur = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [STANDALONE]='status list-locales list-keymaps' + [LOCALES]='set-locale' + [KEYMAPS]='set-keymap' + [X11]='set-x11-keymap' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[LOCALES]}; then + if [[ $cur = *=* ]]; then + mapfile -t locale_vals < <(command localectl list-locales 2>/dev/null) + COMPREPLY=( $(compgen -W '${locale_vals[*]}' -- "${cur#=}") ) + elif [[ $prev = "=" ]]; then + mapfile -t locale_vals < <(command localectl list-locales 2>/dev/null) + COMPREPLY=( $(compgen -W '${locale_vals[*]}' -- "$cur") ) + else + compopt -o nospace + COMPREPLY=( $(compgen -W '${__locale_fields[*]}' -S= -- "$cur") ) + fi + return 0 + elif __contains_word "$verb" ${VERBS[KEYMAPS]}; then + comps=$(command localectl list-keymaps) + elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[X11]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _localectl localectl diff --git a/src/grp-locale/localectl/localectl.completion.zsh b/src/grp-locale/localectl/localectl.completion.zsh new file mode 100644 index 0000000000..54c2d456e4 --- /dev/null +++ b/src/grp-locale/localectl/localectl.completion.zsh @@ -0,0 +1,93 @@ +#compdef localectl + +_localectl_set-locale() { + local -a _locales locale_fields + locale_fields=(LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME \ + LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER \ + LC_NAME LC_ADDRESS LC_TELEPHONE \ + LC_MEASUREMENT LC_IDENTIFICATION) + # LC_ALL is omitted on purpose + + local expl suf + _locales=( ${(f)"$(_call_program locales "$service" list-locales)"} ) + compset -P1 '*=' + if [[ -prefix 1 *\= ]]; then + local conf=${PREFIX%%\=*} + _wanted locales expl "locales configs" \ + _combination localeconfs confs=$conf locales "$@" - + else + compadd -S '=' $locale_fields + fi +} + +_localectl_set-keymap() { + local -a _keymaps + if (( CURRENT <= 3 )); then + _keymaps=( ${(f)"$(_call_program locales "$service" list-keymaps)"} ) + _describe keymaps _keymaps + else + _message "no more options" + fi +} + +_localectl_set-x11-keymap() { + if (( $+commands[pkg-config] )); then + local -a _file _layout _model _variant _options + local _xorg_lst + _xorg_lst=${"$($commands[pkg-config] xkeyboard-config --variable=xkb_base)"} + _file=( ${(ps:\n\!:)"$(<$_xorg_lst/rules/xorg.lst)"} ) + _layout=( ${${${(M)${(f)_file[2]}:# *}# }%% *} ) + _model=( ${${${(M)${(f)_file[1]}:# *}# }%% *} ) + _variant=( ${${${(M)${(f)_file[3]}:# *}# }%% *} ) + _options=( ${${${(M)${(f)_file[4]}:# *}# }%% *} ) + #_layout=( ${(f)"$( echo $_file[1] | awk '/^ / {print $1}' )"} ) + #_model=( ${(f)"$(echo $_file[2] | awk '/^ / {print $1}')"} ) + #_variant=( ${(f)"$(echo $_file[3] | awk '/^ / {print $1}')"} ) + #_options=( ${(f)"$(echo ${_file[4]//:/\\:} | awk '/^ / {print $1}')"} ) + + case $CURRENT in + 2) _describe layouts _layout ;; + 3) _describe models _model;; + 4) _describe variants _variant;; + 5) _describe options _options;; + *) _message "no more options" + esac + fi +} + +_localectl_command() { + local -a _localectl_cmds + _localectl_cmds=( + 'status:Show current locale settings' + 'set-locale:Set system locale' + 'list-locales:Show known locales' + 'set-keymap:Set virtual console keyboard mapping' + 'list-keymaps:Show known virtual console keyboard mappings' + 'set-x11-keymap:Set X11 keyboard mapping' + 'list-x11-keymap-models:Show known X11 keyboard mapping models' + 'list-x11-keymap-layouts:Show known X11 keyboard mapping layouts' + 'list-x11-keymap-variants:Show known X11 keyboard mapping variants' + 'list-x11-keymap-options:Show known X11 keyboard mapping options' + ) + if (( CURRENT == 1 )); then + _describe -t commands 'localectl command' _localectl_cmds + else + local curcontext="$curcontext" + cmd="${${_localectl_cmds[(r)$words[1]:*]%%:*}}" + if (( $+functions[_localectl_$cmd] )); then + _localectl_$cmd + else + _message "unknown localectl command: $words[1]" + fi + fi +} + +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + "--no-convert[Don't convert keyboard mappings]" \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-ask-password[Do not prompt for password]' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machine' \ + '*::localectl commands:_localectl_command' diff --git a/src/grp-locale/localectl/localectl.xml b/src/grp-locale/localectl/localectl.xml new file mode 100644 index 0000000000..8d2becb5d9 --- /dev/null +++ b/src/grp-locale/localectl/localectl.xml @@ -0,0 +1,230 @@ + + + + + + + + + localectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + localectl + 1 + + + + localectl + Control the system locale and keyboard layout settings + + + + + localectl + OPTIONS + COMMAND + + + + + Description + + localectl may be used to query and change + the system locale and keyboard layout settings. It communicates with + systemd-localed8 + to modify files such as /etc/locale.conf and + /etc/vconsole.conf. + + The system locale controls the language settings of system + services and of the UI before the user logs in, such as the + display manager, as well as the default for users after + login. + + The keyboard settings control the keyboard layout used on + the text console and of the graphical UI before the user logs in, + such as the display manager, as well as the default for users + after login. + + Note that the changes performed using this tool might require + the initramfs to be rebuilt to take effect during early system boot. + The initramfs is not rebuilt automatically by localectl. + + + Note that + systemd-firstboot1 + may be used to initialize the system locale for mounted (but not booted) + system images. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + If set-keymap or + set-x11-keymap is invoked and this option + is passed, then the keymap will not be converted from the + console to X11, or X11 to console, + respectively. + + + + + + + + + + The following commands are understood: + + + + status + + Show current settings of the system locale and + keyboard mapping. + + + + set-locale LOCALE... + + Set the system locale. This takes one or more + assignments such as "LANG=de_DE.utf8", + "LC_MESSAGES=en_GB.utf8", and so on. See + locale7 + for details on the available settings and their meanings. Use + list-locales for a list of available + locales (see below). + + + + list-locales + + List available locales useful for + configuration with + set-locale. + + + + set-keymap MAP [TOGGLEMAP] + + Set the system keyboard mapping for the + console and X11. This takes a mapping name (such as "de" or + "us"), and possibly a second one to define a toggle keyboard + mapping. Unless is passed, the + selected setting is also applied as the default system + keyboard mapping of X11, after converting it to the closest + matching X11 keyboard mapping. Use + list-keymaps for a list of available + keyboard mappings (see below). + + + + list-keymaps + + List available keyboard mappings for the + console, useful for configuration with + set-keymap. + + + + set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]] + + Set the system default keyboard mapping for + X11 and the virtual console. This takes a keyboard mapping + name (such as de or us), + and possibly a model, variant, and options, see + kbd4 + for details. Unless is passed, + the selected setting is also applied as the system console + keyboard mapping, after converting it to the closest matching + console keyboard mapping. + + + + list-x11-keymap-models + list-x11-keymap-layouts + list-x11-keymap-variants [LAYOUT] + list-x11-keymap-options + + List available X11 keymap models, layouts, + variants and options, useful for configuration with + set-keymap. The command + list-x11-keymap-variants optionally takes a + layout parameter to limit the output to the variants suitable + for the specific layout. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + + + See Also + + systemd1, + locale7, + locale.conf5, + vconsole.conf5, + loadkeys1, + kbd4, + + The XKB Configuration Guide + , + systemctl1, + systemd-localed.service8, + systemd-firstboot1, + mkinitrd8 + + + + diff --git a/src/grp-locale/systemd-localed/.gitignore b/src/grp-locale/systemd-localed/.gitignore new file mode 100644 index 0000000000..b1e0ba755e --- /dev/null +++ b/src/grp-locale/systemd-localed/.gitignore @@ -0,0 +1 @@ +org.freedesktop.locale1.policy diff --git a/src/grp-locale/systemd-localed/Makefile b/src/grp-locale/systemd-localed/Makefile new file mode 100644 index 0000000000..1a12959a61 --- /dev/null +++ b/src/grp-locale/systemd-localed/Makefile @@ -0,0 +1,89 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_LOCALED),) +systemd_localed_SOURCES = \ + src/locale/localed.c \ + src/locale/keymap-util.c \ + src/locale/keymap-util.h + +systemd_localed_LDADD = \ + libsystemd-shared.la \ + -ldl + +systemd_localed_CFLAGS = \ + $(XKBCOMMON_CFLAGS) + +nodist_systemunit_DATA += \ + units/systemd-localed.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.locale1.busname + +rootlibexec_PROGRAMS += \ + systemd-localed + +dist_dbuspolicy_DATA += \ + src/locale/org.freedesktop.locale1.conf + +dist_dbussystemservice_DATA += \ + src/locale/org.freedesktop.locale1.service + +polkitpolicy_files += \ + src/locale/org.freedesktop.locale1.policy + +SYSTEM_UNIT_ALIASES += \ + systemd-localed.service dbus-org.freedesktop.locale1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.locale1.busname + +dist_pkgdata_DATA = \ + src/locale/kbd-model-map \ + src/locale/language-fallback-map + +test_keymap_util_SOURCES = \ + src/locale/test-keymap-util.c \ + src/locale/keymap-util.c \ + src/locale/keymap-util.h + +test_keymap_util_LDADD = \ + libsystemd-shared.la \ + -ldl + +tests += \ + test-keymap-util + +endif # ENABLE_LOCALED + +.PHONY: update-kbd-model-map + +polkitpolicy_in_files += \ + src/locale/org.freedesktop.locale1.policy.in + +EXTRA_DIST += \ + units/systemd-localed.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-locale/systemd-localed/kbd-model-map b/src/grp-locale/systemd-localed/kbd-model-map new file mode 100644 index 0000000000..8fa984f83b --- /dev/null +++ b/src/grp-locale/systemd-localed/kbd-model-map @@ -0,0 +1,68 @@ +# Generated from system-config-keyboard's model list +# consolelayout xlayout xmodel xvariant xoptions +sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +nl nl pc105 - terminate:ctrl_alt_bksp +mk-utf mk,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +trq tr pc105 - terminate:ctrl_alt_bksp +uk gb pc105 - terminate:ctrl_alt_bksp +is-latin1 is pc105 - terminate:ctrl_alt_bksp +de de pc105 - terminate:ctrl_alt_bksp +la-latin1 latam pc105 - terminate:ctrl_alt_bksp +us us pc105+inet - terminate:ctrl_alt_bksp +ko kr pc105 - terminate:ctrl_alt_bksp +ro-std ro pc105 std terminate:ctrl_alt_bksp +de-latin1 de pc105 - terminate:ctrl_alt_bksp +slovene si pc105 - terminate:ctrl_alt_bksp +hu101 hu pc105 qwerty terminate:ctrl_alt_bksp +jp106 jp jp106 - terminate:ctrl_alt_bksp +croat hr pc105 - terminate:ctrl_alt_bksp +it2 it pc105 - terminate:ctrl_alt_bksp +hu hu pc105 - terminate:ctrl_alt_bksp +sr-latin rs pc105 latin terminate:ctrl_alt_bksp +fi fi pc105 - terminate:ctrl_alt_bksp +fr_CH ch pc105 fr terminate:ctrl_alt_bksp +dk-latin1 dk pc105 - terminate:ctrl_alt_bksp +fr fr pc105 - terminate:ctrl_alt_bksp +it it pc105 - terminate:ctrl_alt_bksp +ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +fr-latin1 fr pc105 - terminate:ctrl_alt_bksp +sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp +be-latin1 be pc105 - terminate:ctrl_alt_bksp +dk dk pc105 - terminate:ctrl_alt_bksp +fr-pc fr pc105 - terminate:ctrl_alt_bksp +bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +it-ibm it pc105 - terminate:ctrl_alt_bksp +cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +br-abnt2 br abnt2 - terminate:ctrl_alt_bksp +ro ro pc105 - terminate:ctrl_alt_bksp +us-acentos us pc105 intl terminate:ctrl_alt_bksp +pt-latin1 pt pc105 - terminate:ctrl_alt_bksp +ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp +tj_alt-UTF8 tj pc105 - terminate:ctrl_alt_bksp +de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp +no no pc105 - terminate:ctrl_alt_bksp +bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +dvorak us pc105 dvorak terminate:ctrl_alt_bksp +dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp +ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp +pl2 pl pc105 - terminate:ctrl_alt_bksp +es es pc105 - terminate:ctrl_alt_bksp +ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp +ie ie pc105 - terminate:ctrl_alt_bksp +et ee pc105 - terminate:ctrl_alt_bksp +sk-qwerty sk pc105 - terminate:ctrl_alt_bksp,qwerty +sk-qwertz sk pc105 - terminate:ctrl_alt_bksp +fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp +fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp +cf ca pc105 - terminate:ctrl_alt_bksp +sv-latin1 se pc105 - terminate:ctrl_alt_bksp +sr-cy rs pc105 - terminate:ctrl_alt_bksp +gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +by by,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +il il pc105 - terminate:ctrl_alt_bksp +kazakh kz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll +lt.baltic lt pc105 - terminate:ctrl_alt_bksp +lt.l4 lt pc105 - terminate:ctrl_alt_bksp +lt lt pc105 - terminate:ctrl_alt_bksp +khmer kh,us pc105 - terminate:ctrl_alt_bksp diff --git a/src/grp-locale/systemd-localed/keymap-util.c b/src/grp-locale/systemd-localed/keymap-util.c new file mode 100644 index 0000000000..1a99cef5e5 --- /dev/null +++ b/src/grp-locale/systemd-localed/keymap-util.c @@ -0,0 +1,725 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/fileio.h" +#include "basic/locale-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/string-util.h" +#include "basic/strv.h" + +#include "keymap-util.h" + +static bool startswith_comma(const char *s, const char *prefix) { + s = startswith(s, prefix); + if (!s) + return false; + + return *s == ',' || *s == '\0'; +} + +static const char* strnulldash(const char *s) { + return isempty(s) || streq(s, "-") ? NULL : s; +} + +static const char* systemd_kbd_model_map(void) { + const char* s; + + s = getenv("SYSTEMD_KBD_MODEL_MAP"); + if (s) + return s; + + return SYSTEMD_KBD_MODEL_MAP; +} + +static const char* systemd_language_fallback_map(void) { + const char* s; + + s = getenv("SYSTEMD_LANGUAGE_FALLBACK_MAP"); + if (s) + return s; + + return SYSTEMD_LANGUAGE_FALLBACK_MAP; +} + +static void context_free_x11(Context *c) { + c->x11_layout = mfree(c->x11_layout); + c->x11_options = mfree(c->x11_options); + c->x11_model = mfree(c->x11_model); + c->x11_variant = mfree(c->x11_variant); +} + +static void context_free_vconsole(Context *c) { + c->vc_keymap = mfree(c->vc_keymap); + c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); +} + +static void context_free_locale(Context *c) { + int p; + + for (p = 0; p < _VARIABLE_LC_MAX; p++) + c->locale[p] = mfree(c->locale[p]); +} + +void context_free(Context *c) { + context_free_locale(c); + context_free_x11(c); + context_free_vconsole(c); +}; + +void locale_simplify(Context *c) { + int p; + + for (p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++) + if (isempty(c->locale[p]) || streq_ptr(c->locale[VARIABLE_LANG], c->locale[p])) + c->locale[p] = mfree(c->locale[p]); +} + +static int locale_read_data(Context *c) { + int r; + + context_free_locale(c); + + r = parse_env_file("/etc/locale.conf", NEWLINE, + "LANG", &c->locale[VARIABLE_LANG], + "LANGUAGE", &c->locale[VARIABLE_LANGUAGE], + "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], + "LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC], + "LC_TIME", &c->locale[VARIABLE_LC_TIME], + "LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE], + "LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY], + "LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES], + "LC_PAPER", &c->locale[VARIABLE_LC_PAPER], + "LC_NAME", &c->locale[VARIABLE_LC_NAME], + "LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS], + "LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE], + "LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT], + "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r == -ENOENT) { + int p; + + /* Fill in what we got passed from systemd. */ + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name))); + if (r < 0) + return r; + } + + r = 0; + } + + locale_simplify(c); + return r; +} + +static int vconsole_read_data(Context *c) { + int r; + + context_free_vconsole(c); + + r = parse_env_file("/etc/vconsole.conf", NEWLINE, + "KEYMAP", &c->vc_keymap, + "KEYMAP_TOGGLE", &c->vc_keymap_toggle, + NULL); + + if (r < 0 && r != -ENOENT) + return r; + + return 0; +} + +static int x11_read_data(Context *c) { + _cleanup_fclose_ FILE *f; + char line[LINE_MAX]; + bool in_section = false; + int r; + + context_free_x11(c); + + f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); + if (!f) + return errno == ENOENT ? 0 : -errno; + + while (fgets(line, sizeof(line), f)) { + char *l; + + char_array_0(line); + l = strstrip(line); + + if (l[0] == 0 || l[0] == '#') + continue; + + if (in_section && first_word(l, "Option")) { + _cleanup_strv_free_ char **a = NULL; + + r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) + return r; + + if (strv_length(a) == 3) { + char **p = NULL; + + if (streq(a[1], "XkbLayout")) + p = &c->x11_layout; + else if (streq(a[1], "XkbModel")) + p = &c->x11_model; + else if (streq(a[1], "XkbVariant")) + p = &c->x11_variant; + else if (streq(a[1], "XkbOptions")) + p = &c->x11_options; + + if (p) { + free(*p); + *p = a[2]; + a[2] = NULL; + } + } + + } else if (!in_section && first_word(l, "Section")) { + _cleanup_strv_free_ char **a = NULL; + + r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) + return -ENOMEM; + + if (strv_length(a) == 2 && streq(a[1], "InputClass")) + in_section = true; + + } else if (in_section && first_word(l, "EndSection")) + in_section = false; + } + + return 0; +} + +int context_read_data(Context *c) { + int r, q, p; + + r = locale_read_data(c); + q = vconsole_read_data(c); + p = x11_read_data(c); + + return r < 0 ? r : q < 0 ? q : p; +} + +int locale_write_data(Context *c, char ***settings) { + int r, p; + _cleanup_strv_free_ char **l = NULL; + + /* Set values will be returned as strv in *settings on success. */ + + r = load_env_file(NULL, "/etc/locale.conf", NULL, &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + _cleanup_free_ char *t = NULL; + char **u; + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + if (isempty(c->locale[p])) { + l = strv_env_unset(l, name); + continue; + } + + if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) + return -ENOMEM; + + u = strv_env_set(l, t); + if (!u) + return -ENOMEM; + + strv_free(l); + l = u; + } + + if (strv_isempty(l)) { + if (unlink("/etc/locale.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file_label("/etc/locale.conf", l); + if (r < 0) + return r; + + *settings = l; + l = NULL; + return 0; +} + +int vconsole_write_data(Context *c) { + int r; + _cleanup_strv_free_ char **l = NULL; + + r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l); + if (r < 0 && r != -ENOENT) + return r; + + if (isempty(c->vc_keymap)) + l = strv_env_unset(l, "KEYMAP"); + else { + _cleanup_free_ char *s = NULL; + char **u; + + s = strappend("KEYMAP=", c->vc_keymap); + if (!s) + return -ENOMEM; + + u = strv_env_set(l, s); + if (!u) + return -ENOMEM; + + strv_free(l); + l = u; + } + + if (isempty(c->vc_keymap_toggle)) + l = strv_env_unset(l, "KEYMAP_TOGGLE"); + else { + _cleanup_free_ char *s = NULL; + char **u; + + s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle); + if (!s) + return -ENOMEM; + + u = strv_env_set(l, s); + if (!u) + return -ENOMEM; + + strv_free(l); + l = u; + } + + if (strv_isempty(l)) { + if (unlink("/etc/vconsole.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + return write_env_file_label("/etc/vconsole.conf", l); +} + +int x11_write_data(Context *c) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *temp_path = NULL; + int r; + + if (isempty(c->x11_layout) && + isempty(c->x11_model) && + isempty(c->x11_variant) && + isempty(c->x11_options)) { + + if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + mkdir_p_label("/etc/X11/xorg.conf.d", 0755); + + r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); + if (r < 0) + return r; + + fchmod(fileno(f), 0644); + + fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" + "# manually too freely.\n" + "Section \"InputClass\"\n" + " Identifier \"system-keyboard\"\n" + " MatchIsKeyboard \"on\"\n", f); + + if (!isempty(c->x11_layout)) + fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); + + if (!isempty(c->x11_model)) + fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); + + if (!isempty(c->x11_variant)) + fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); + + if (!isempty(c->x11_options)) + fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); + + fputs("EndSection\n", f); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); + + if (temp_path) + (void) unlink(temp_path); + + return r; +} + +static int read_next_mapping(const char* filename, + unsigned min_fields, unsigned max_fields, + FILE *f, unsigned *n, char ***a) { + assert(f); + assert(n); + assert(a); + + for (;;) { + char line[LINE_MAX]; + char *l, **b; + int r; + size_t length; + + errno = 0; + if (!fgets(line, sizeof(line), f)) { + + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + return 0; + } + + (*n)++; + + l = strstrip(line); + if (l[0] == 0 || l[0] == '#') + continue; + + r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) + return r; + + length = strv_length(b); + if (length < min_fields || length > max_fields) { + log_error("Invalid line %s:%u, ignoring.", filename, *n); + strv_free(b); + continue; + + } + + *a = b; + return 1; + } +} + +int vconsole_convert_to_x11(Context *c) { + const char *map; + int modified = -1; + + map = systemd_kbd_model_map(); + + if (isempty(c->vc_keymap)) { + modified = + !isempty(c->x11_layout) || + !isempty(c->x11_model) || + !isempty(c->x11_variant) || + !isempty(c->x11_options); + + context_free_x11(c); + } else { + _cleanup_fclose_ FILE *f = NULL; + unsigned n = 0; + + f = fopen(map, "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_strv_free_ char **a = NULL; + int r; + + r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); + if (r < 0) + return r; + if (r == 0) + break; + + if (!streq(c->vc_keymap, a[0])) + continue; + + if (!streq_ptr(c->x11_layout, strnulldash(a[1])) || + !streq_ptr(c->x11_model, strnulldash(a[2])) || + !streq_ptr(c->x11_variant, strnulldash(a[3])) || + !streq_ptr(c->x11_options, strnulldash(a[4]))) { + + if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 || + free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 || + free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 || + free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0) + return -ENOMEM; + + modified = true; + } + + break; + } + } + + if (modified > 0) + log_info("Changing X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", + strempty(c->x11_layout), + strempty(c->x11_model), + strempty(c->x11_variant), + strempty(c->x11_options)); + else if (modified < 0) + log_notice("X11 keyboard layout was not modified: no conversion found for \"%s\".", + c->vc_keymap); + else + log_debug("X11 keyboard layout did not need to be modified."); + + return modified > 0; +} + +int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) { + const char *dir; + _cleanup_free_ char *n; + + if (x11_variant) + n = strjoin(x11_layout, "-", x11_variant, NULL); + else + n = strdup(x11_layout); + if (!n) + return -ENOMEM; + + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + _cleanup_free_ char *p = NULL, *pz = NULL; + bool uncompressed; + + p = strjoin(dir, "xkb/", n, ".map", NULL); + pz = strjoin(dir, "xkb/", n, ".map.gz", NULL); + if (!p || !pz) + return -ENOMEM; + + uncompressed = access(p, F_OK) == 0; + if (uncompressed || access(pz, F_OK) == 0) { + log_debug("Found converted keymap %s at %s", + n, uncompressed ? p : pz); + + *new_keymap = n; + n = NULL; + return 1; + } + } + + return 0; +} + +int find_legacy_keymap(Context *c, char **new_keymap) { + const char *map; + _cleanup_fclose_ FILE *f = NULL; + unsigned n = 0; + unsigned best_matching = 0; + int r; + + assert(!isempty(c->x11_layout)); + + map = systemd_kbd_model_map(); + + f = fopen(map, "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_strv_free_ char **a = NULL; + unsigned matching = 0; + + r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); + if (r < 0) + return r; + if (r == 0) + break; + + /* Determine how well matching this entry is */ + if (streq(c->x11_layout, a[1])) + /* If we got an exact match, this is best */ + matching = 10; + else { + /* We have multiple X layouts, look for an + * entry that matches our key with everything + * but the first layout stripped off. */ + if (startswith_comma(c->x11_layout, a[1])) + matching = 5; + else { + char *x; + + /* If that didn't work, strip off the + * other layouts from the entry, too */ + x = strndupa(a[1], strcspn(a[1], ",")); + if (startswith_comma(c->x11_layout, x)) + matching = 1; + } + } + + if (matching > 0) { + if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) { + matching++; + + if (streq_ptr(c->x11_variant, a[3])) { + matching++; + + if (streq_ptr(c->x11_options, a[4])) + matching++; + } + } + } + + /* The best matching entry so far, then let's save that */ + if (matching >= MAX(best_matching, 1u)) { + log_debug("Found legacy keymap %s with score %u", + a[0], matching); + + if (matching > best_matching) { + best_matching = matching; + + r = free_and_strdup(new_keymap, a[0]); + if (r < 0) + return r; + } + } + } + + if (best_matching < 10 && c->x11_layout) { + /* The best match is only the first part of the X11 + * keymap. Check if we have a converted map which + * matches just the first layout. + */ + char *l, *v = NULL, *converted; + + l = strndupa(c->x11_layout, strcspn(c->x11_layout, ",")); + if (c->x11_variant) + v = strndupa(c->x11_variant, strcspn(c->x11_variant, ",")); + r = find_converted_keymap(l, v, &converted); + if (r < 0) + return r; + if (r > 0) { + free(*new_keymap); + *new_keymap = converted; + } + } + + return (bool) *new_keymap; +} + +int find_language_fallback(const char *lang, char **language) { + const char *map; + _cleanup_fclose_ FILE *f = NULL; + unsigned n = 0; + + assert(lang); + assert(language); + + map = systemd_language_fallback_map(); + + f = fopen(map, "re"); + if (!f) + return -errno; + + for (;;) { + _cleanup_strv_free_ char **a = NULL; + int r; + + r = read_next_mapping(map, 2, 2, f, &n, &a); + if (r <= 0) + return r; + + if (streq(lang, a[0])) { + assert(strv_length(a) == 2); + *language = a[1]; + a[1] = NULL; + return 1; + } + } + + assert_not_reached("should not be here"); +} + +int x11_convert_to_vconsole(Context *c) { + bool modified = false; + + if (isempty(c->x11_layout)) { + modified = + !isempty(c->vc_keymap) || + !isempty(c->vc_keymap_toggle); + + context_free_vconsole(c); + } else { + char *new_keymap = NULL; + int r; + + r = find_converted_keymap(c->x11_layout, c->x11_variant, &new_keymap); + if (r < 0) + return r; + else if (r == 0) { + r = find_legacy_keymap(c, &new_keymap); + if (r < 0) + return r; + } + if (r == 0) + /* We search for layout-variant match first, but then we also look + * for anything which matches just the layout. So it's accurate to say + * that we couldn't find anything which matches the layout. */ + log_notice("No conversion to virtual console map found for \"%s\".", + c->x11_layout); + + if (!streq_ptr(c->vc_keymap, new_keymap)) { + free(c->vc_keymap); + c->vc_keymap = new_keymap; + c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); + modified = true; + } else + free(new_keymap); + } + + if (modified) + log_info("Changing virtual console keymap to '%s' toggle '%s'", + strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); + else + log_debug("Virtual console keymap was not modified."); + + return modified; +} diff --git a/src/grp-locale/systemd-localed/keymap-util.h b/src/grp-locale/systemd-localed/keymap-util.h new file mode 100644 index 0000000000..58b6ec4d8f --- /dev/null +++ b/src/grp-locale/systemd-localed/keymap-util.h @@ -0,0 +1,46 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/locale-util.h" + +typedef struct Context { + char *locale[_VARIABLE_LC_MAX]; + + char *x11_layout; + char *x11_model; + char *x11_variant; + char *x11_options; + + char *vc_keymap; + char *vc_keymap_toggle; +} Context; + +int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap); +int find_legacy_keymap(Context *c, char **new_keymap); +int find_language_fallback(const char *lang, char **language); + +int context_read_data(Context *c); +void context_free(Context *c); +int vconsole_convert_to_x11(Context *c); +int vconsole_write_data(Context *c); +int x11_convert_to_vconsole(Context *c); +int x11_write_data(Context *c); +void locale_simplify(Context *c); +int locale_write_data(Context *c, char ***settings); diff --git a/src/grp-locale/systemd-localed/language-fallback-map b/src/grp-locale/systemd-localed/language-fallback-map new file mode 100644 index 0000000000..d0b02a6b98 --- /dev/null +++ b/src/grp-locale/systemd-localed/language-fallback-map @@ -0,0 +1,13 @@ +csb_PL csb:pl +en_AU en_AU:en_GB +en_IE en_IE:en_GB +en_NZ en_NZ:en_GB +en_ZA en_ZA:en_GB +fr_BE fr_BE:fr_FR +fr_CA fr_CA:fr_FR +fr_CH fr_CH:fr_FR +fr_LU fr_LU:fr_FR +it_CH it_CH:it_IT +mai_IN mai:hi +nds_DE nds:de +szl_PL szl:pl diff --git a/src/grp-locale/systemd-localed/localed.c b/src/grp-locale/systemd-localed/localed.c new file mode 100644 index 0000000000..55fadd78df --- /dev/null +++ b/src/grp-locale/systemd-localed/localed.c @@ -0,0 +1,711 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_XKBCOMMON +#include +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/locale-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-message.h" +#include "shared/bus-util.h" + +#include "keymap-util.h" + +static Hashmap *polkit_registry = NULL; + +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_(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; + + assert(bus); + + l_unset = new0(char*, _VARIABLE_LC_MAX); + if (!l_unset) + return -ENOMEM; + + l_set = new0(char*, _VARIABLE_LC_MAX); + if (!l_set) + return -ENOMEM; + + for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) { + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + if (isempty(c->locale[p])) + l_unset[c_set++] = (char*) name; + else { + char *s; + + if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0) + return -ENOMEM; + + l_set[c_unset++] = s; + } + } + + assert(c_set + c_unset == _VARIABLE_LC_MAX); + r = sd_bus_message_new_method_call(bus, &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnsetAndSetEnvironment"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, l_unset); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, l_set); + if (r < 0) + return r; + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) + log_error_errno(r, "Failed to update the manager environment: %m"); + + return 0; +} + +static int vconsole_reload(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "RestartUnit", + &error, + NULL, + "ss", "systemd-vconsole-setup.service", "replace"); + + if (r < 0) + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + return r; +} + +static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus *bus) { + int r; + + assert(bus); + + r = vconsole_convert_to_x11(c); + if (r <= 0) + return r; + + /* modified */ + r = x11_write_data(c); + if (r < 0) + return log_error_errno(r, "Failed to write X11 keyboard layout: %m"); + + sd_bus_emit_properties_changed(bus, + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); + + return 1; +} + +static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus *bus) { + int r; + + assert(bus); + + r = x11_convert_to_vconsole(c); + if (r <= 0) + return r; + + /* modified */ + r = vconsole_write_data(c); + if (r < 0) + log_error_errno(r, "Failed to save virtual console keymap: %m"); + + sd_bus_emit_properties_changed(bus, + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "VConsoleKeymap", "VConsoleKeymapToggle", NULL); + + return vconsole_reload(bus); +} + +static int property_get_locale( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Context *c = userdata; + _cleanup_strv_free_ char **l = NULL; + int p, q; + + l = new0(char*, _VARIABLE_LC_MAX+1); + if (!l) + return -ENOMEM; + + for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) { + char *t; + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + if (isempty(c->locale[p])) + continue; + + if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) + return -ENOMEM; + + l[q++] = t; + } + + return sd_bus_message_append_strv(reply, l); +} + +static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + _cleanup_strv_free_ char **l = NULL; + char **i; + const char *lang = NULL; + int interactive; + bool modified = false; + bool have[_VARIABLE_LC_MAX] = {}; + int p; + int r; + + assert(m); + assert(c); + + r = bus_message_read_strv_extend(m, &l); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(m, 'b', &interactive); + if (r < 0) + return r; + + /* Check whether a variable changed and if it is valid */ + STRV_FOREACH(i, l) { + bool valid = false; + + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + size_t k; + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + k = strlen(name); + if (startswith(*i, name) && + (*i)[k] == '=' && + locale_is_valid((*i) + k + 1)) { + valid = true; + have[p] = true; + + if (p == VARIABLE_LANG) + lang = (*i) + k + 1; + + if (!streq_ptr(*i + k + 1, c->locale[p])) + modified = true; + + break; + } + } + + if (!valid) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); + } + + /* If LANG was specified, but not LANGUAGE, check if we should + * set it based on the language fallback table. */ + if (have[VARIABLE_LANG] && !have[VARIABLE_LANGUAGE]) { + _cleanup_free_ char *language = NULL; + + assert(lang); + + (void) find_language_fallback(lang, &language); + if (language) { + log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language); + if (!streq_ptr(language, c->locale[VARIABLE_LANGUAGE])) { + r = strv_extendf(&l, "LANGUAGE=%s", language); + if (r < 0) + return r; + + have[VARIABLE_LANGUAGE] = true; + modified = true; + } + } + } + + /* Check whether a variable is unset */ + if (!modified) + for (p = 0; p < _VARIABLE_LC_MAX; p++) + if (!isempty(c->locale[p]) && !have[p]) { + modified = true; + break; + } + + if (modified) { + _cleanup_strv_free_ char **settings = NULL; + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.locale1.set-locale", + NULL, + interactive, + UID_INVALID, + &polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + STRV_FOREACH(i, l) + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + size_t k; + const char *name; + + name = locale_variable_to_string(p); + assert(name); + + k = strlen(name); + if (startswith(*i, name) && (*i)[k] == '=') { + r = free_and_strdup(&c->locale[p], *i + k + 1); + if (r < 0) + return r; + break; + } + } + + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + if (have[p]) + continue; + + c->locale[p] = mfree(c->locale[p]); + } + + locale_simplify(c); + + r = locale_write_data(c, &settings); + if (r < 0) { + log_error_errno(r, "Failed to set locale: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r)); + } + + locale_update_system_manager(c, sd_bus_message_get_bus(m)); + + if (settings) { + _cleanup_free_ char *line; + + line = strv_join(settings, ", "); + log_info("Changed locale to %s.", strnull(line)); + } else + log_info("Changed locale to unset."); + + (void) sd_bus_emit_properties_changed( + sd_bus_message_get_bus(m), + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "Locale", NULL); + } else + log_debug("Locale settings were not modified."); + + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *keymap, *keymap_toggle; + int convert, interactive; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive); + if (r < 0) + return r; + + keymap = empty_to_null(keymap); + keymap_toggle = empty_to_null(keymap_toggle); + + if (!streq_ptr(keymap, c->vc_keymap) || + !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) { + + if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) || + (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle)))) + return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data"); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.locale1.set-keyboard", + NULL, + interactive, + UID_INVALID, + &polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + if (free_and_strdup(&c->vc_keymap, keymap) < 0 || + free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0) + return -ENOMEM; + + r = vconsole_write_data(c); + if (r < 0) { + log_error_errno(r, "Failed to set virtual console keymap: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r)); + } + + log_info("Changed virtual console keymap to '%s' toggle '%s'", + strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); + + r = vconsole_reload(sd_bus_message_get_bus(m)); + if (r < 0) + log_error_errno(r, "Failed to request keymap reload: %m"); + + (void) sd_bus_emit_properties_changed( + sd_bus_message_get_bus(m), + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "VConsoleKeymap", "VConsoleKeymapToggle", NULL); + + if (convert) { + r = vconsole_convert_to_x11_and_emit(c, sd_bus_message_get_bus(m)); + if (r < 0) + log_error_errno(r, "Failed to convert keymap data: %m"); + } + } + + return sd_bus_reply_method_return(m, NULL); +} + +#ifdef HAVE_XKBCOMMON + +_printf_(3, 0) +static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { + const char *fmt; + + fmt = strjoina("libxkbcommon: ", format); + log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); +} + +#define LOAD_SYMBOL(symbol, dl, name) \ + ({ \ + (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \ + (symbol) ? 0 : -EOPNOTSUPP; \ + }) + +static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + + /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge + * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function + * pointers to the shared library are below: */ + + struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL; + void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL; + void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL; + struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL; + void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL; + + const struct xkb_rule_names rmlvo = { + .model = model, + .layout = layout, + .variant = variant, + .options = options, + }; + struct xkb_context *ctx = NULL; + struct xkb_keymap *km = NULL; + void *dl; + int r; + + /* Compile keymap from RMLVO information to check out its validity */ + + dl = dlopen("libxkbcommon.so.0", RTLD_LAZY); + if (!dl) + return -EOPNOTSUPP; + + r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref"); + if (r < 0) + goto finish; + + ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + if (!ctx) { + r = -ENOMEM; + goto finish; + } + + symbol_xkb_context_set_log_fn(ctx, log_xkb); + + km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!km) { + r = -EINVAL; + goto finish; + } + + r = 0; + +finish: + if (symbol_xkb_keymap_unref && km) + symbol_xkb_keymap_unref(km); + + if (symbol_xkb_context_unref && ctx) + symbol_xkb_context_unref(ctx); + + (void) dlclose(dl); + return r; +} + +#else + +static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + return 0; +} + +#endif + +static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *layout, *model, *variant, *options; + int convert, interactive; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive); + if (r < 0) + return r; + + layout = empty_to_null(layout); + model = empty_to_null(model); + variant = empty_to_null(variant); + options = empty_to_null(options); + + if (!streq_ptr(layout, c->x11_layout) || + !streq_ptr(model, c->x11_model) || + !streq_ptr(variant, c->x11_variant) || + !streq_ptr(options, c->x11_options)) { + + if ((layout && !string_is_safe(layout)) || + (model && !string_is_safe(model)) || + (variant && !string_is_safe(variant)) || + (options && !string_is_safe(options))) + return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data"); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.locale1.set-keyboard", + NULL, + interactive, + UID_INVALID, + &polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = verify_xkb_rmlvo(model, layout, variant, options); + if (r < 0) { + log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", + strempty(model), strempty(layout), strempty(variant), strempty(options)); + + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system."); + + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); + } + + if (free_and_strdup(&c->x11_layout, layout) < 0 || + free_and_strdup(&c->x11_model, model) < 0 || + free_and_strdup(&c->x11_variant, variant) < 0 || + free_and_strdup(&c->x11_options, options) < 0) + return -ENOMEM; + + r = x11_write_data(c); + if (r < 0) { + log_error_errno(r, "Failed to set X11 keyboard layout: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r)); + } + + log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", + strempty(c->x11_layout), + strempty(c->x11_model), + strempty(c->x11_variant), + strempty(c->x11_options)); + + (void) sd_bus_emit_properties_changed( + sd_bus_message_get_bus(m), + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); + + if (convert) { + r = x11_convert_to_vconsole_and_emit(c, sd_bus_message_get_bus(m)); + if (r < 0) + log_error_errno(r, "Failed to convert keymap data: %m"); + } + } + + return sd_bus_reply_method_return(m, NULL); +} + +static const sd_bus_vtable locale_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(c); + assert(event); + assert(_bus); + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); + + r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c); + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); + + r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + *_bus = bus; + bus = NULL; + + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(context_free) Context context = {}; + _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); + log_parse_environment(); + log_open(); + + umask(0022); + mac_selinux_init(); + + 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"); + goto finish; + } + + sd_event_set_watchdog(event, true); + + r = connect_bus(&context, event, &bus); + if (r < 0) + goto finish; + + r = context_read_data(&context); + if (r < 0) { + log_error_errno(r, "Failed to read locale data: %m"); + goto finish; + } + + r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); + if (r < 0) + log_error_errno(r, "Failed to run event loop: %m"); + +finish: + bus_verify_polkit_async_registry_free(polkit_registry); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-locale/systemd-localed/org.freedesktop.locale1.conf b/src/grp-locale/systemd-localed/org.freedesktop.locale1.conf new file mode 100644 index 0000000000..79d0ecd2bb --- /dev/null +++ b/src/grp-locale/systemd-localed/org.freedesktop.locale1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-locale/systemd-localed/org.freedesktop.locale1.policy.in b/src/grp-locale/systemd-localed/org.freedesktop.locale1.policy.in new file mode 100644 index 0000000000..df63845e9b --- /dev/null +++ b/src/grp-locale/systemd-localed/org.freedesktop.locale1.policy.in @@ -0,0 +1,40 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Set system locale + <_message>Authentication is required to set the system locale. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.locale1.set-keyboard + + + + <_description>Set system keyboard settings + <_message>Authentication is required to set the system keyboard settings. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/grp-locale/systemd-localed/org.freedesktop.locale1.service b/src/grp-locale/systemd-localed/org.freedesktop.locale1.service new file mode 100644 index 0000000000..025f9a0fc2 --- /dev/null +++ b/src/grp-locale/systemd-localed/org.freedesktop.locale1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.locale1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.locale1.service diff --git a/src/grp-locale/systemd-localed/systemd-localed.service.in b/src/grp-locale/systemd-localed/systemd-localed.service.in new file mode 100644 index 0000000000..1f3151c2b5 --- /dev/null +++ b/src/grp-locale/systemd-localed/systemd-localed.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Locale Service +Documentation=man:systemd-localed.service(8) man:locale.conf(5) man:vconsole.conf(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/localed + +[Service] +ExecStart=@rootlibexecdir@/systemd-localed +BusName=org.freedesktop.locale1 +CapabilityBoundingSet= +WatchdogSec=3min +PrivateTmp=yes +PrivateDevices=yes +PrivateNetwork=yes +ProtectSystem=yes +ProtectHome=yes +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/src/grp-locale/systemd-localed/systemd-localed.service.xml b/src/grp-locale/systemd-localed/systemd-localed.service.xml new file mode 100644 index 0000000000..06aa78c0e4 --- /dev/null +++ b/src/grp-locale/systemd-localed/systemd-localed.service.xml @@ -0,0 +1,87 @@ + + + + + + + + + systemd-localed.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-localed.service + 8 + + + + systemd-localed.service + systemd-localed + Locale bus mechanism + + + + systemd-localed.service + /usr/lib/systemd/systemd-localed + + + + Description + + systemd-localed is a system service + that may be used as mechanism to change the system locale + settings, as well as the console key mapping and default X11 key + mapping. systemd-localed is automatically + activated on request and terminates itself when it is + unused. + + The tool + localectl1 + is a command line client to this service. + + See the + developer documentation for information about the APIs + systemd-localed provides. + + + + See Also + + systemd1, + locale.conf5, + vconsole.conf5, + localectl1, + loadkeys1 + + + + diff --git a/src/grp-locale/systemd-localed/test-keymap-util.c b/src/grp-locale/systemd-localed/test-keymap-util.c new file mode 100644 index 0000000000..6c001b2f50 --- /dev/null +++ b/src/grp-locale/systemd-localed/test-keymap-util.c @@ -0,0 +1,221 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/string-util.h" + +#include "keymap-util.h" + +static void test_find_language_fallback(void) { + _cleanup_free_ char *ans = NULL, *ans2 = NULL; + + log_info("/*** %s ***/", __func__); + + assert_se(find_language_fallback("foobar", &ans) == 0); + assert_se(ans == NULL); + + assert_se(find_language_fallback("csb", &ans) == 0); + assert_se(ans == NULL); + + assert_se(find_language_fallback("csb_PL", &ans) == 1); + assert_se(streq(ans, "csb:pl")); + + assert_se(find_language_fallback("szl_PL", &ans2) == 1); + assert_se(streq(ans2, "szl:pl")); +} + +static void test_find_converted_keymap(void) { + _cleanup_free_ char *ans = NULL, *ans2 = NULL; + int r; + + log_info("/*** %s ***/", __func__); + + assert_se(find_converted_keymap("pl", "foobar", &ans) == 0); + assert_se(ans == NULL); + + r = find_converted_keymap("pl", NULL, &ans); + if (r == 0) { + log_info("Skipping rest of %s: keymaps are not installed", __func__); + return; + } + + assert_se(r == 1); + assert_se(streq(ans, "pl")); + + assert_se(find_converted_keymap("pl", "dvorak", &ans2) == 1); + assert_se(streq(ans2, "pl-dvorak")); +} + +static void test_find_legacy_keymap(void) { + Context c = {}; + _cleanup_free_ char *ans = NULL, *ans2 = NULL; + + log_info("/*** %s ***/", __func__); + + c.x11_layout = (char*) "foobar"; + assert_se(find_legacy_keymap(&c, &ans) == 0); + assert_se(ans == NULL); + + c.x11_layout = (char*) "pl"; + assert_se(find_legacy_keymap(&c, &ans) == 1); + assert_se(streq(ans, "pl2")); + + c.x11_layout = (char*) "pl,ru"; + assert_se(find_legacy_keymap(&c, &ans2) == 1); + assert_se(streq(ans, "pl2")); +} + +static void test_vconsole_convert_to_x11(void) { + _cleanup_(context_free) Context c = {}; + + log_info("/*** %s ***/", __func__); + + log_info("/* test emptying first (:) */"); + assert_se(free_and_strdup(&c.x11_layout, "foo") >= 0); + assert_se(free_and_strdup(&c.x11_variant, "bar") >= 0); + assert_se(vconsole_convert_to_x11(&c) == 1); + assert_se(c.x11_layout == NULL); + assert_se(c.x11_variant == NULL); + + log_info("/* test emptying second (:) */"); + + assert_se(vconsole_convert_to_x11(&c) == 0); + assert_se(c.x11_layout == NULL); + assert_se(c.x11_variant == NULL); + + log_info("/* test without variant, new mapping (es:) */"); + assert_se(free_and_strdup(&c.vc_keymap, "es") >= 0); + + assert_se(vconsole_convert_to_x11(&c) == 1); + assert_se(streq(c.x11_layout, "es")); + assert_se(c.x11_variant == NULL); + + log_info("/* test with known variant, new mapping (es:dvorak) */"); + assert_se(free_and_strdup(&c.vc_keymap, "es-dvorak") >= 0); + + assert_se(vconsole_convert_to_x11(&c) == 0); // FIXME + assert_se(streq(c.x11_layout, "es")); + assert_se(c.x11_variant == NULL); // FIXME: "dvorak" + + log_info("/* test with old mapping (fr:latin9) */"); + assert_se(free_and_strdup(&c.vc_keymap, "fr-latin9") >= 0); + + assert_se(vconsole_convert_to_x11(&c) == 1); + assert_se(streq(c.x11_layout, "fr")); + assert_se(streq(c.x11_variant, "latin9")); + + log_info("/* test with a compound mapping (ru,us) */"); + assert_se(free_and_strdup(&c.vc_keymap, "ru") >= 0); + + assert_se(vconsole_convert_to_x11(&c) == 1); + assert_se(streq(c.x11_layout, "ru,us")); + assert_se(c.x11_variant == NULL); + + log_info("/* test with a simple mapping (us) */"); + assert_se(free_and_strdup(&c.vc_keymap, "us") >= 0); + + assert_se(vconsole_convert_to_x11(&c) == 1); + assert_se(streq(c.x11_layout, "us")); + assert_se(c.x11_variant == NULL); +} + +static void test_x11_convert_to_vconsole(void) { + _cleanup_(context_free) Context c = {}; + int r; + + log_info("/*** %s ***/", __func__); + + log_info("/* test emptying first (:) */"); + assert_se(free_and_strdup(&c.vc_keymap, "foobar") >= 0); + assert_se(x11_convert_to_vconsole(&c) == 1); + assert_se(c.vc_keymap == NULL); + + log_info("/* test emptying second (:) */"); + + assert_se(x11_convert_to_vconsole(&c) == 0); + assert_se(c.vc_keymap == NULL); + + log_info("/* test without variant, new mapping (es:) */"); + assert_se(free_and_strdup(&c.x11_layout, "es") >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 1); + assert_se(streq(c.vc_keymap, "es")); + + log_info("/* test with unknown variant, new mapping (es:foobar) */"); + assert_se(free_and_strdup(&c.x11_variant, "foobar") >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 0); + assert_se(streq(c.vc_keymap, "es")); + + log_info("/* test with known variant, new mapping (es:dvorak) */"); + assert_se(free_and_strdup(&c.x11_variant, "dvorak") >= 0); + + r = x11_convert_to_vconsole(&c); + if (r == 0) { + log_info("Skipping rest of %s: keymaps are not installed", __func__); + return; + } + + assert_se(r == 1); + assert_se(streq(c.vc_keymap, "es-dvorak")); + + log_info("/* test with old mapping (fr:latin9) */"); + assert_se(free_and_strdup(&c.x11_layout, "fr") >= 0); + assert_se(free_and_strdup(&c.x11_variant, "latin9") >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 1); + assert_se(streq(c.vc_keymap, "fr-latin9")); + + log_info("/* test with a compound mapping (us,ru:) */"); + assert_se(free_and_strdup(&c.x11_layout, "us,ru") >= 0); + assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 1); + assert_se(streq(c.vc_keymap, "us")); + + log_info("/* test with a compound mapping (ru,us:) */"); + assert_se(free_and_strdup(&c.x11_layout, "ru,us") >= 0); + assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 1); + assert_se(streq(c.vc_keymap, "ru")); + + /* https://bugzilla.redhat.com/show_bug.cgi?id=1333998 */ + log_info("/* test with a simple new mapping (ru:) */"); + assert_se(free_and_strdup(&c.x11_layout, "ru") >= 0); + assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); + + assert_se(x11_convert_to_vconsole(&c) == 0); + assert_se(streq(c.vc_keymap, "ru")); +} + +int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + test_find_language_fallback(); + test_find_converted_keymap(); + test_find_legacy_keymap(); + + test_vconsole_convert_to_x11(); + test_x11_convert_to_vconsole(); + + return 0; +} diff --git a/src/grp-login/.gitignore b/src/grp-login/.gitignore new file mode 100644 index 0000000000..3a8ba497c1 --- /dev/null +++ b/src/grp-login/.gitignore @@ -0,0 +1,6 @@ +/logind-gperf.c +/logind.conf +/org.freedesktop.login1.policy +/71-seat.rules +/73-seat-late.rules +/systemd-user diff --git a/src/grp-login/Makefile b/src/grp-login/Makefile new file mode 100644 index 0000000000..48a15db591 --- /dev/null +++ b/src/grp-login/Makefile @@ -0,0 +1,63 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +test_login_SOURCES = \ + src/libsystemd/sd-login/test-login.c + +test_login_LDADD = \ + libsystemd-shared.la + +test_login_shared_SOURCES = \ + src/login/test-login-shared.c + +test_login_shared_LDADD = \ + libsystemd-shared.la + +test_inhibit_SOURCES = \ + src/login/test-inhibit.c + +test_inhibit_LDADD = \ + libsystemd-shared.la + +test_login_tables_SOURCES = \ + src/login/test-login-tables.c + +test_login_tables_LDADD = \ + liblogind-core.la + +manual_tests += \ + test-login \ + test-inhibit + +tests += \ + test-login-tables \ + test-login-shared + +nested.subdirs += loginctl +nested.subdirs += pam_systemd +nested.subdirs += systemd-inhibit +nested.subdirs += systemd-logind + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/loginctl/Makefile b/src/grp-login/loginctl/Makefile new file mode 100644 index 0000000000..f1a474e1cc --- /dev/null +++ b/src/grp-login/loginctl/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +loginctl_SOURCES = \ + src/login/loginctl.c \ + src/login/sysfs-show.h \ + src/login/sysfs-show.c + +loginctl_LDADD = \ + libsystemd-shared.la + +rootbin_PROGRAMS += \ + loginctl + +dist_bashcompletion_data += \ + shell-completion/bash/loginctl + +dist_zshcompletion_data += shell-completion/zsh/_loginctl + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/loginctl/loginctl.c b/src/grp-login/loginctl/loginctl.c new file mode 100644 index 0000000000..469bec582f --- /dev/null +++ b/src/grp-login/loginctl/loginctl.c @@ -0,0 +1,1588 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "basic/verbs.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" +#include "shared/cgroup-show.h" +#include "shared/logs-show.h" +#include "shared/pager.h" +#include "shared/spawn-polkit-agent.h" + +#include "sysfs-show.h" + +static char **arg_property = NULL; +static bool arg_all = false; +static bool arg_value = false; +static bool arg_full = false; +static bool arg_no_pager = false; +static bool arg_legend = true; +static const char *arg_kill_who = NULL; +static int arg_signal = SIGTERM; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_ask_password = true; +static unsigned arg_lines = 10; +static OutputMode arg_output = OUTPUT_SHORT; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +static OutputFlags get_output_flags(void) { + + return + arg_all * OUTPUT_SHOW_ALL | + arg_full * OUTPUT_FULL_WIDTH | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + colors_enabled() * OUTPUT_COLOR; +} + +static int list_sessions(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 *id, *user, *seat, *object; + sd_bus *bus = userdata; + unsigned k = 0; + uint32_t uid; + int r; + + assert(bus); + assert(argv); + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSessions", + &error, &reply, + ""); + if (r < 0) { + log_error("Failed to list sessions: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "(susso)"); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT"); + + while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) { + printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat); + k++; + } + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("\n%u sessions listed.\n", k); + + return 0; +} + +static int list_users(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 *user, *object; + sd_bus *bus = userdata; + unsigned k = 0; + uint32_t uid; + int r; + + assert(bus); + assert(argv); + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListUsers", + &error, &reply, + ""); + if (r < 0) { + log_error("Failed to list users: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "(uso)"); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("%10s %-16s\n", "UID", "USER"); + + while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) { + printf("%10u %-16s\n", (unsigned) uid, user); + k++; + } + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("\n%u users listed.\n", k); + + return 0; +} + +static int list_seats(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 *seat, *object; + sd_bus *bus = userdata; + unsigned k = 0; + int r; + + assert(bus); + assert(argv); + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats", + &error, &reply, + ""); + if (r < 0) { + log_error("Failed to list seats: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "(so)"); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("%-16s\n", "SEAT"); + + while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) { + printf("%-16s\n", seat); + k++; + } + if (r < 0) + return bus_log_parse_error(r); + + if (arg_legend) + printf("\n%u seats listed.\n", k); + + return 0; +} + +static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) { + _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; + unsigned c; + int r; + + assert(bus); + assert(unit); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + path, + interface, + "ControlGroup", + &error, + &reply, + "s"); + if (r < 0) + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &cgroup); + if (r < 0) + return bus_log_parse_error(r); + + if (isempty(cgroup)) + return 0; + + c = columns(); + if (c > 18) + c -= 18; + else + c = 0; + + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + + return 0; +} + +typedef struct SessionStatusInfo { + char *id; + uid_t uid; + char *name; + struct dual_timestamp timestamp; + unsigned int vtnr; + char *seat; + char *tty; + char *display; + int remote; + char *remote_host; + char *remote_user; + char *service; + pid_t leader; + char *type; + char *class; + char *state; + char *scope; + char *desktop; +} SessionStatusInfo; + +typedef struct UserStatusInfo { + uid_t uid; + int linger; + char *name; + struct dual_timestamp timestamp; + char *state; + char **sessions; + char *display; + char *slice; +} UserStatusInfo; + +typedef struct SeatStatusInfo { + char *id; + char *active_session; + char **sessions; +} SeatStatusInfo; + +static void session_status_info_clear(SessionStatusInfo *info) { + if (info) { + free(info->id); + free(info->name); + free(info->seat); + free(info->tty); + free(info->display); + free(info->remote_host); + free(info->remote_user); + free(info->service); + free(info->type); + free(info->class); + free(info->state); + free(info->scope); + free(info->desktop); + zero(*info); + } +} + +static void user_status_info_clear(UserStatusInfo *info) { + if (info) { + free(info->name); + free(info->state); + strv_free(info->sessions); + free(info->display); + free(info->slice); + zero(*info); + } +} + +static void seat_status_info_clear(SeatStatusInfo *info) { + if (info) { + free(info->id); + free(info->active_session); + strv_free(info->sessions); + zero(*info); + } +} + +static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + const char *contents; + int r; + + r = sd_bus_message_peek_type(m, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents); + if (r < 0) + return r; + + if (contents[0] == 's' || contents[0] == 'o') { + const char *s; + char **p = (char **) userdata; + + r = sd_bus_message_read_basic(m, contents[0], &s); + if (r < 0) + return r; + + r = free_and_strdup(p, s); + if (r < 0) + return r; + } else { + r = sd_bus_message_read_basic(m, contents[0], userdata); + if (r < 0) + return r; + } + + r = sd_bus_message_skip(m, contents+1); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + const char *name; + int r; + + assert(bus); + assert(m); + + r = sd_bus_message_enter_container(m, 'a', "(so)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) { + r = strv_extend(userdata, name); + if (r < 0) + return r; + } + if (r < 0) + return r; + + return sd_bus_message_exit_container(m); +} + +static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) { + + static const struct bus_properties_map map[] = { + { "Id", "s", NULL, offsetof(SessionStatusInfo, id) }, + { "Name", "s", NULL, offsetof(SessionStatusInfo, name) }, + { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, + { "Display", "s", NULL, offsetof(SessionStatusInfo, display) }, + { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) }, + { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) }, + { "Service", "s", NULL, offsetof(SessionStatusInfo, service) }, + { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) }, + { "Type", "s", NULL, offsetof(SessionStatusInfo, type) }, + { "Class", "s", NULL, offsetof(SessionStatusInfo, class) }, + { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) }, + { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, + { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) }, + { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) }, + { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) }, + { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) }, + { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) }, + { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) }, + { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) }, + {} + }; + + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + _cleanup_(session_status_info_clear) SessionStatusInfo i = {}; + int r; + + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + if (*new_line) + printf("\n"); + + *new_line = true; + + printf("%s - ", strna(i.id)); + + if (i.name) + printf("%s (%u)\n", i.name, (unsigned) i.uid); + else + printf("%u\n", (unsigned) i.uid); + + s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime); + s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime); + + if (s1) + printf("\t Since: %s; %s\n", s2, s1); + else if (s2) + printf("\t Since: %s\n", s2); + + if (i.leader > 0) { + _cleanup_free_ char *t = NULL; + + printf("\t Leader: %u", (unsigned) i.leader); + + get_process_comm(i.leader, &t); + if (t) + printf(" (%s)", t); + + printf("\n"); + } + + if (!isempty(i.seat)) { + printf("\t Seat: %s", i.seat); + + if (i.vtnr > 0) + printf("; vc%u", i.vtnr); + + printf("\n"); + } + + if (i.tty) + printf("\t TTY: %s\n", i.tty); + else if (i.display) + printf("\t Display: %s\n", i.display); + + if (i.remote_host && i.remote_user) + printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host); + else if (i.remote_host) + printf("\t Remote: %s\n", i.remote_host); + else if (i.remote_user) + printf("\t Remote: user %s\n", i.remote_user); + else if (i.remote) + printf("\t Remote: Yes\n"); + + if (i.service) { + printf("\t Service: %s", i.service); + + if (i.type) + printf("; type %s", i.type); + + if (i.class) + printf("; class %s", i.class); + + printf("\n"); + } else if (i.type) { + printf("\t Type: %s", i.type); + + if (i.class) + printf("; class %s", i.class); + + printf("\n"); + } else if (i.class) + printf("\t Class: %s\n", i.class); + + if (!isempty(i.desktop)) + printf("\t Desktop: %s\n", i.desktop); + + if (i.state) + printf("\t State: %s\n", i.state); + + if (i.scope) { + printf("\t Unit: %s\n", i.scope); + show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader); + + if (arg_transport == BUS_TRANSPORT_LOCAL) { + + show_journal_by_unit( + stdout, + i.scope, + arg_output, + 0, + i.timestamp.monotonic, + arg_lines, + 0, + get_output_flags() | OUTPUT_BEGIN_NEWLINE, + SD_JOURNAL_LOCAL_ONLY, + true, + NULL); + } + } + + return 0; +} + +static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) { + + static const struct bus_properties_map map[] = { + { "Name", "s", NULL, offsetof(UserStatusInfo, name) }, + { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) }, + { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) }, + { "State", "s", NULL, offsetof(UserStatusInfo, state) }, + { "UID", "u", NULL, offsetof(UserStatusInfo, uid) }, + { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) }, + { "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) }, + { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) }, + { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) }, + {} + }; + + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + _cleanup_(user_status_info_clear) UserStatusInfo i = {}; + int r; + + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + if (*new_line) + printf("\n"); + + *new_line = true; + + if (i.name) + printf("%s (%u)\n", i.name, (unsigned) i.uid); + else + printf("%u\n", (unsigned) i.uid); + + s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime); + s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime); + + if (s1) + printf("\t Since: %s; %s\n", s2, s1); + else if (s2) + printf("\t Since: %s\n", s2); + + if (!isempty(i.state)) + printf("\t State: %s\n", i.state); + + if (!strv_isempty(i.sessions)) { + char **l; + printf("\tSessions:"); + + STRV_FOREACH(l, i.sessions) + printf(" %s%s", + streq_ptr(*l, i.display) ? "*" : "", + *l); + + printf("\n"); + } + + printf("\t Linger: %s\n", yes_no(i.linger)); + + if (i.slice) { + printf("\t Unit: %s\n", i.slice); + show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0); + + show_journal_by_unit( + stdout, + i.slice, + arg_output, + 0, + i.timestamp.monotonic, + arg_lines, + 0, + get_output_flags() | OUTPUT_BEGIN_NEWLINE, + SD_JOURNAL_LOCAL_ONLY, + true, + NULL); + } + + return 0; +} + +static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) { + + static const struct bus_properties_map map[] = { + { "Id", "s", NULL, offsetof(SeatStatusInfo, id) }, + { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) }, + { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) }, + {} + }; + + _cleanup_(seat_status_info_clear) SeatStatusInfo i = {}; + int r; + + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + if (*new_line) + printf("\n"); + + *new_line = true; + + printf("%s\n", strna(i.id)); + + if (!strv_isempty(i.sessions)) { + char **l; + printf("\tSessions:"); + + STRV_FOREACH(l, i.sessions) { + if (streq_ptr(*l, i.active_session)) + printf(" *%s", *l); + else + printf(" %s", *l); + } + + printf("\n"); + } + + if (arg_transport == BUS_TRANSPORT_LOCAL) { + unsigned c; + + c = columns(); + if (c > 21) + c -= 21; + else + c = 0; + + printf("\t Devices:\n"); + + show_sysfs(i.id, "\t\t ", c); + } + + return 0; +} + +#define property(name, fmt, ...) \ + do { \ + if (arg_value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + +static int print_property(const char *name, sd_bus_message *m, const char *contents) { + int r; + + assert(name); + assert(m); + assert(contents); + + if (arg_property && !strv_find(arg_property, name)) + /* skip what we didn't read */ + return sd_bus_message_skip(m, contents); + + switch (contents[0]) { + + case SD_BUS_TYPE_STRUCT_BEGIN: + + if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) { + const char *s; + + r = sd_bus_message_read(m, "(so)", &s, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || !isempty(s)) + property(name, "%s", s); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) { + uint32_t uid; + + r = sd_bus_message_read(m, "(uo)", &uid, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (!uid_is_valid(uid)) { + log_error("Invalid user ID: " UID_FMT, uid); + return -EINVAL; + } + + property(name, UID_FMT, uid); + return 0; + } + + break; + + case SD_BUS_TYPE_ARRAY: + + if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) { + const char *s; + bool space = false; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)"); + if (r < 0) + return bus_log_parse_error(r); + + if (!arg_value) + printf("%s=", name); + + while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) { + printf("%s%s", space ? " " : "", s); + space = true; + } + + if (space || !arg_value) + printf("\n"); + + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + } + + break; + } + + r = bus_print_property(name, m, arg_value, arg_all); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) { + r = sd_bus_message_skip(m, contents); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all) + printf("%s=[unprintable]\n", name); + } + + return 0; +} + +static int show_properties(sd_bus *bus, const char *path, bool *new_line) { + _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); + assert(path); + assert(new_line); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + if (*new_line) + printf("\n"); + + *new_line = true; + + while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *name, *contents; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_peek_type(reply, NULL, &contents); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return bus_log_parse_error(r); + + r = print_property(name, reply, contents); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + 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 (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + return 0; +} + +static int show_session(int argc, char *argv[], void *userdata) { + bool properties, new_line = false; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + properties = !strstr(argv[0], "status"); + + pager_open(arg_no_pager, false); + + if (argc <= 1) { + /* If not argument is specified inspect the manager + * itself */ + if (properties) + return show_properties(bus, "/org/freedesktop/login1", &new_line); + + /* And in the pretty case, show data of the calling session */ + return print_session_status_info(bus, "/org/freedesktop/login1/session/self", &new_line); + } + + for (i = 1; i < argc; i++) { + _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( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetSession", + &error, &reply, + "s", argv[i]); + if (r < 0) { + log_error("Failed to get session: %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); + + if (properties) + r = show_properties(bus, path, &new_line); + else + r = print_session_status_info(bus, path, &new_line); + + if (r < 0) + return r; + } + + return 0; +} + +static int show_user(int argc, char *argv[], void *userdata) { + bool properties, new_line = false; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + properties = !strstr(argv[0], "status"); + + pager_open(arg_no_pager, false); + + if (argc <= 1) { + /* If not argument is specified inspect the manager + * itself */ + if (properties) + return show_properties(bus, "/org/freedesktop/login1", &new_line); + + return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line); + } + + for (i = 1; i < argc; i++) { + _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; + + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetUser", + &error, &reply, + "u", (uint32_t) uid); + if (r < 0) { + log_error("Failed to get user: %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); + + if (properties) + r = show_properties(bus, path, &new_line); + else + r = print_user_status_info(bus, path, &new_line); + + if (r < 0) + return r; + } + + return 0; +} + +static int show_seat(int argc, char *argv[], void *userdata) { + bool properties, new_line = false; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + properties = !strstr(argv[0], "status"); + + pager_open(arg_no_pager, false); + + if (argc <= 1) { + /* If not argument is specified inspect the manager + * itself */ + if (properties) + return show_properties(bus, "/org/freedesktop/login1", &new_line); + + return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line); + } + + for (i = 1; i < argc; i++) { + _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( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetSeat", + &error, &reply, + "s", argv[i]); + if (r < 0) { + log_error("Failed to get seat: %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); + + if (properties) + r = show_properties(bus, path, &new_line); + else + r = print_seat_status_info(bus, path, &new_line); + + if (r < 0) + return r; + } + + return 0; +} + +static int activate(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + char *short_argv[3]; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + if (argc < 2) { + /* No argument? Let's convert this into the empty + * session name, which the calls will then resolve to + * the caller's session. */ + + short_argv[0] = argv[0]; + short_argv[1] = (char*) ""; + short_argv[2] = NULL; + + argv = short_argv; + argc = 2; + } + + for (i = 1; i < argc; i++) { + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + streq(argv[0], "lock-session") ? "LockSession" : + streq(argv[0], "unlock-session") ? "UnlockSession" : + streq(argv[0], "terminate-session") ? "TerminateSession" : + "ActivateSession", + &error, NULL, + "s", argv[i]); + if (r < 0) { + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int kill_session(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + if (!arg_kill_who) + arg_kill_who = "all"; + + for (i = 1; i < argc; i++) { + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "KillSession", + &error, NULL, + "ssi", argv[i], arg_kill_who, arg_signal); + if (r < 0) { + log_error("Could not kill session: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int enable_linger(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + char* short_argv[3]; + bool b; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + b = streq(argv[0], "enable-linger"); + + if (argc < 2) { + short_argv[0] = argv[0]; + short_argv[1] = (char*) ""; + short_argv[2] = NULL; + argv = short_argv; + argc = 2; + } + + for (i = 1; i < argc; i++) { + uid_t uid; + + if (isempty(argv[i])) + uid = UID_INVALID; + else { + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetUserLinger", + &error, NULL, + "ubb", (uint32_t) uid, b, true); + if (r < 0) { + log_error("Could not enable linger: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int terminate_user(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + for (i = 1; i < argc; i++) { + uid_t uid; + + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "TerminateUser", + &error, NULL, + "u", (uint32_t) uid); + if (r < 0) { + log_error("Could not terminate user: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int kill_user(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + if (!arg_kill_who) + arg_kill_who = "all"; + + for (i = 1; i < argc; i++) { + uid_t uid; + + r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "KillUser", + &error, NULL, + "ui", (uint32_t) uid, arg_signal); + if (r < 0) { + log_error("Could not kill user: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int attach(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + for (i = 2; i < argc; i++) { + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "AttachDevice", + &error, NULL, + "ssb", argv[1], argv[i], true); + + if (r < 0) { + log_error("Could not attach device: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int flush_devices(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "FlushDevices", + &error, NULL, + "b", true); + if (r < 0) + log_error("Could not flush devices: %s", bus_error_message(&error, -r)); + + return r; +} + +static int lock_sessions(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions", + &error, NULL, + NULL); + if (r < 0) + log_error("Could not lock sessions: %s", bus_error_message(&error, -r)); + + return r; +} + +static int terminate_seat(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + assert(argv); + + polkit_agent_open_if_enabled(); + + for (i = 1; i < argc; i++) { + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "TerminateSeat", + &error, NULL, + "s", argv[i]); + if (r < 0) { + log_error("Could not terminate seat: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int help(int argc, char *argv[], void *userdata) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the login manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --no-ask-password Don't prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " -p --property=NAME Show only properties by this name\n" + " -a --all Show all properties, including empty ones\n" + " --value When showing properties, only print the value\n" + " -l --full Do not ellipsize output\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " -n --lines=INTEGER Number of journal entries to show\n" + " -o --output=STRING Change journal output mode (short, short-monotonic,\n" + " verbose, export, json, json-pretty, json-sse, cat)\n\n" + "Session Commands:\n" + " list-sessions List sessions\n" + " session-status [ID...] Show session status\n" + " show-session [ID...] Show properties of sessions or the manager\n" + " activate [ID] Activate a session\n" + " lock-session [ID...] Screen lock one or more sessions\n" + " unlock-session [ID...] Screen unlock one or more sessions\n" + " lock-sessions Screen lock all current sessions\n" + " unlock-sessions Screen unlock all current sessions\n" + " terminate-session ID... Terminate one or more sessions\n" + " kill-session ID... Send signal to processes of a session\n\n" + "User Commands:\n" + " list-users List users\n" + " user-status [USER...] Show user status\n" + " show-user [USER...] Show properties of users or the manager\n" + " enable-linger [USER...] Enable linger state of one or more users\n" + " disable-linger [USER...] Disable linger state of one or more users\n" + " terminate-user USER... Terminate all sessions of one or more users\n" + " kill-user USER... Send signal to processes of a user\n\n" + "Seat Commands:\n" + " list-seats List seats\n" + " seat-status [NAME...] Show seat status\n" + " show-seat [NAME...] Show properties of seats or the manager\n" + " attach NAME DEVICE... Attach one or more devices to a seat\n" + " flush-devices Flush all device associations\n" + " terminate-seat NAME... Terminate all sessions on one or more seats\n" + , program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_VALUE, + ARG_NO_PAGER, + ARG_NO_LEGEND, + ARG_KILL_WHO, + ARG_NO_ASK_PASSWORD, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "value", no_argument, NULL, ARG_VALUE }, + { "full", no_argument, NULL, 'l' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "lines", required_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(0, NULL, NULL); + return 0; + + case ARG_VERSION: + return version(); + + case 'p': { + r = strv_extend(&arg_property, optarg); + if (r < 0) + return log_oom(); + + /* If the user asked for a particular + * property, show it to him, even if it is + * empty. */ + arg_all = true; + break; + } + + case 'a': + arg_all = true; + break; + + case ARG_VALUE: + arg_value = true; + break; + + case 'l': + arg_full = true; + break; + + case 'n': + if (safe_atou(optarg, &arg_lines) < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case ARG_KILL_WHO: + arg_kill_who = optarg; + break; + + case 's': + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { + log_error("Failed to parse signal string %s.", optarg); + return -EINVAL; + } + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int loginctl_main(int argc, char *argv[], sd_bus *bus) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions }, + { "session-status", VERB_ANY, VERB_ANY, 0, show_session }, + { "show-session", VERB_ANY, VERB_ANY, 0, show_session }, + { "activate", VERB_ANY, 2, 0, activate }, + { "lock-session", VERB_ANY, VERB_ANY, 0, activate }, + { "unlock-session", VERB_ANY, VERB_ANY, 0, activate }, + { "lock-sessions", VERB_ANY, 1, 0, lock_sessions }, + { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions }, + { "terminate-session", 2, VERB_ANY, 0, activate }, + { "kill-session", 2, VERB_ANY, 0, kill_session }, + { "list-users", VERB_ANY, 1, 0, list_users }, + { "user-status", VERB_ANY, VERB_ANY, 0, show_user }, + { "show-user", VERB_ANY, VERB_ANY, 0, show_user }, + { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, + { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, + { "terminate-user", 2, VERB_ANY, 0, terminate_user }, + { "kill-user", 2, VERB_ANY, 0, kill_user }, + { "list-seats", VERB_ANY, 1, 0, list_seats }, + { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat }, + { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat }, + { "attach", 3, VERB_ANY, 0, attach }, + { "flush-devices", VERB_ANY, 1, 0, flush_devices }, + { "terminate-seat", 2, VERB_ANY, 0, terminate_seat }, + {} + }; + + return dispatch_verb(argc, argv, verbs, bus); +} + +int main(int argc, char *argv[]) { + sd_bus *bus = NULL; + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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; + } + + sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); + + r = loginctl_main(argc, argv, bus); + +finish: + sd_bus_flush_close_unref(bus); + + pager_close(); + polkit_agent_close(); + + strv_free(arg_property); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-login/loginctl/loginctl.completion.bash b/src/grp-login/loginctl/loginctl.completion.bash new file mode 100644 index 0000000000..776eca4e62 --- /dev/null +++ b/src/grp-login/loginctl/loginctl.completion.bash @@ -0,0 +1,111 @@ +# loginctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_all_sessions () { loginctl --no-legend list-sessions | { while read -r a b; do printf "%s\n" "$a"; done; } ; } +__get_all_users () { loginctl --no-legend list-users | { while read -r a b; do printf "%s\n" "$b"; done; } ; } +__get_all_seats () { loginctl --no-legend list-seats | { while read -r a b; do printf "%s\n" "$a"; done; } ; } + +_loginctl () { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='--all -a --help -h --no-pager --privileged -P --version + --no-legend --no-ask-password -l --full' + [ARG]='--host -H --kill-who --property -p --signal -s --machine' + ) + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --signal|-s) + _signals + return + ;; + --kill-who) + comps='all leader' + ;; + --host|-H) + comps=$(compgen -A hostname) + ;; + --property|-p) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [SESSIONS]='session-status show-session activate lock-session unlock-session terminate-session kill-session' + [USERS]='user-status show-user enable-linger disable-linger terminate-user kill-user' + [SEATS]='seat-status show-seat terminate-seat' + [STANDALONE]='list-sessions list-users list-seats flush-devices' + [ATTACH]='attach' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps="${VERBS[*]}" + + elif __contains_word "$verb" ${VERBS[SESSIONS]}; then + comps=$( __get_all_sessions ) + + elif __contains_word "$verb" ${VERBS[USERS]}; then + comps=$( __get_all_users ) + + elif __contains_word "$verb" ${VERBS[SEATS]}; then + comps=$( __get_all_seats ) + + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + + elif __contains_word "$verb" ${VERBS[ATTACH]}; then + if [[ $prev = $verb ]]; then + comps=$( __get_all_seats ) + else + comps=$(compgen -A file -- "$cur" ) + compopt -o filenames + fi + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _loginctl loginctl diff --git a/src/grp-login/loginctl/loginctl.completion.zsh b/src/grp-login/loginctl/loginctl.completion.zsh new file mode 100644 index 0000000000..6f6ff6e314 --- /dev/null +++ b/src/grp-login/loginctl/loginctl.completion.zsh @@ -0,0 +1,172 @@ +#compdef loginctl + +_loginctl_all_sessions() { + local session description + loginctl --no-legend list-sessions | while read -r session description; do + _sys_all_sessions+=( "$session" ) + _sys_all_sessions_descr+=( "${session}:$description" ) + done +} + +_loginctl_all_users() { + local uid description + loginctl --no-legend list-users | while read -r uid description; do + _sys_all_users+=( "$uid" ) + _sys_all_users_descr+=( "${uid}:$description" ) + done +} + +_loginctl_all_seats() { + local seat description + loginctl --no-legend list-seats | while read -r seat description; do + _sys_all_seats+=( "$seat" ) + _sys_all_seats_descr+=( "${seat}:$description" ) + done +} + +local fun +# Completion functions for SESSIONS +for fun in session-status show-session activate lock-session unlock-session terminate-session kill-session ; do + (( $+functions[_loginctl_$fun] )) || _loginctl_$fun() + { + local -a _sys_all_sessions{,_descr} + + _loginctl_all_sessions + for _ignore in $words[2,-1]; do + _sys_all_sessions[(i)$_ignore]=() + _sys_all_sessions_descr[(i)$_ignore:*]=() + done + + if zstyle -T ":completion:${curcontext}:systemd-sessions" verbose; then + _describe -t systemd-sessions session _sys_all_sessions_descr _sys_all_sessions "$@" + else + local expl + _wanted systemd-sessions expl session compadd "$@" -a _sys_all_sessions + fi + } +done + +# Completion functions for USERS +for fun in user-status show-user enable-linger disable-linger terminate-user kill-user ; do + (( $+functions[_loginctl_$fun] )) || _loginctl_$fun() + { + local -a _sys_all_users{,_descr} + zstyle -a ":completion:${curcontext}:users" users _sys_all_users + + if ! (( $#_sys_all_users )); then + _loginctl_all_users + fi + + for _ignore in $words[2,-1]; do + _sys_all_users[(i)$_ignore]=() + _sys_all_users_descr[(i)$_ignore:*]=() + done + + # using the common tag `users' here, not rolling our own `systemd-users' tag + if zstyle -T ":completion:${curcontext}:users" verbose; then + _describe -t users user ${_sys_all_users_descr:+_sys_all_users_descr} _sys_all_users "$@" + else + local expl + _wanted users expl user compadd "$@" -a _sys_all_users + fi + } +done + +# Completion functions for SEATS +(( $+functions[_loginctl_seats] )) || _loginctl_seats() +{ + local -a _sys_all_seats{,_descr} + + _loginctl_all_seats + for _ignore in $words[2,-1]; do + _sys_all_seats[(i)$_ignore]=() + _sys_all_seats_descr[(i)$_ignore:*]=() + done + + if zstyle -T ":completion:${curcontext}:systemd-seats" verbose; then + _describe -t systemd-seats seat _sys_all_seats_descr _sys_all_seats "$@" + else + local expl + _wanted systemd-seats expl seat compadd "$@" -a _sys_all_seats + fi +} +for fun in seat-status show-seat terminate-seat ; do + (( $+functions[_loginctl_$fun] )) || _loginctl_$fun() + { _loginctl_seats } +done + +# Completion functions for ATTACH +(( $+functions[_loginctl_attach] )) || _loginctl_attach() +{ + _arguments -w -C -S -s \ + ':seat:_loginctl_seats' \ + '*:device:_files' +} + +# no loginctl completion for: +# [STANDALONE]='list-sessions list-users list-seats flush-devices' + +(( $+functions[_loginctl_command] )) || _loginctl_command() +{ + local -a _loginctl_cmds + _loginctl_cmds=( + "list-sessions:List sessions" + "session-status:Show session status" + "show-session:Show properties of one or more sessions" + "activate:Activate a session" + "lock-session:Screen lock one or more sessions" + "unlock-session:Screen unlock one or more sessions" + "lock-sessions:Screen lock all current sessions" + "unlock-sessions:Screen unlock all current sessions" + "terminate-session:Terminate one or more sessions" + "kill-session:Send signal to processes of a session" + "list-users:List users" + "user-status:Show user status" + "show-user:Show properties of one or more users" + "enable-linger:Enable linger state of one or more users" + "disable-linger:Disable linger state of one or more users" + "terminate-user:Terminate all sessions of one or more users" + "kill-user:Send signal to processes of a user" + "list-seats:List seats" + "seat-status:Show seat status" + "show-seat:Show properties of one or more seats" + "attach:Attach one or more devices to a seat" + "flush-devices:Flush all device associations" + "terminate-seat:Terminate all sessions on one or more seats" + ) + + if (( CURRENT == 1 )); then + _describe -t commands 'loginctl command' _loginctl_cmds || compadd "$@" + else + local curcontext="$curcontext" _ignore + + cmd="${${_loginctl_cmds[(r)$words[1]:*]%%:*}}" + + if (( $#cmd )); then + curcontext="${curcontext%:*:*}:loginctl-${cmd}:" + + _call_function ret _loginctl_$cmd || _message 'no more arguments' + else + _message "unknown loginctl command: $words[1]" + fi + return ret + fi +} + + +_arguments -s \ + {-h,--help}'[Show help]' \ + '--version[Show package version]' \ + \*{-p+,--property=}'[Show only properties by this name]:unit property' \ + {-a,--all}'[Show all properties, including empty ones]' \ + '--kill-who=[Who to send signal to]:killwho:(main control all)' \ + {-s+,--signal=}'[Which signal to send]:signal:_signals' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \ + {-l,--full}'[Do not ellipsize output]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-legend[Do not show the headers and footers]' \ + '--no-ask-password[Do not ask for system passwords]' \ + {-n+,--lines=}'[Number of journal entries to show]' \ + {-o+,--output=}'[Change journal output mode]:output modes:_sd_outputmodes' \ + '*::loginctl command:_loginctl_command' diff --git a/src/grp-login/loginctl/loginctl.xml b/src/grp-login/loginctl/loginctl.xml new file mode 100644 index 0000000000..fb51740503 --- /dev/null +++ b/src/grp-login/loginctl/loginctl.xml @@ -0,0 +1,459 @@ + + + + + + + + + loginctl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + loginctl + 1 + + + + loginctl + Control the systemd login manager + + + + + loginctl + OPTIONS + COMMAND + NAME + + + + + Description + + loginctl may be used to introspect and + control the state of the + systemd1 + login manager + systemd-logind.service8. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + + When showing session/user/seat properties, + limit display to certain properties as specified as argument. + If not specified, all set properties are shown. The argument + should be a property name, such as + Sessions. If specified more than once, all + properties with the specified names are + shown. + + + + + + + When printing properties with show, + only print the value, and skip the property name and + =. + + + + + + + + When showing session/user/seat properties, + show all properties regardless of whether they are set or + not. + + + + + + + Do not ellipsize process tree entries. + + + + + + + When used with + kill-session, choose which processes to + kill. Must be one of , or + to select whether to kill only the leader + process of the session or all processes of the session. If + omitted, defaults to . + + + + + + + When used with kill-session + or kill-user, choose which signal to send + to selected processes. Must be one of the well known signal + specifiers, such as SIGTERM, + SIGINT or SIGSTOP. + If omitted, defaults to + SIGTERM. + + + + + + + When used with user-status + and session-status, controls the number of + journal lines to show, counting from the most recent ones. + Takes a positive integer argument. Defaults to 10. + + + + + + + + When used with user-status + and session-status, controls the formatting + of the journal entries that are shown. For the available + choices, see + journalctl1. + Defaults to short. + + + + + + + + + + + + + + Commands + + The following commands are understood: + + Session Commands + + + list-sessions + + List current sessions. + + + + session-status ID... + + Show terse runtime status information about + one or more sessions, followed by the most recent log data + from the journal. Takes one or more session identifiers as + parameters. If no session identifiers are passed, the status of + the caller's session is shown. This function is intended to + generate human-readable output. If you are looking for + computer-parsable output, use show-session + instead. + + + + show-session ID... + + Show properties of one or more sessions or the + manager itself. If no argument is specified, properties of the + manager will be shown. If a session ID is specified, + properties of the session are shown. By default, empty + properties are suppressed. Use to show + those too. To select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required. Use + session-status if you are looking for + formatted human-readable output. + + + + activate ID + + Activate a session. This brings a session into + the foreground if another session is currently in the + foreground on the respective seat. Takes a session identifier + as argument. If no argument is specified, the session of the + caller is put into foreground. + + + + lock-session ID... + unlock-session ID... + + Activates/deactivates the screen lock on one + or more sessions, if the session supports it. Takes one or + more session identifiers as arguments. If no argument is + specified, the session of the caller is locked/unlocked. + + + + + lock-sessions + unlock-sessions + + Activates/deactivates the screen lock on all + current sessions supporting it. + + + + terminate-session ID... + + Terminates a session. This kills all processes + of the session and deallocates all resources attached to the + session. + + + + kill-session ID... + + Send a signal to one or more processes of the + session. Use to select which + process to kill. Use to select the + signal to send. + + + + User Commands + + list-users + + List currently logged in users. + + + + + user-status USER... + + Show terse runtime status information about + one or more logged in users, followed by the most recent log + data from the journal. Takes one or more user names or numeric + user IDs as parameters. If no parameters are passed, the status + of the caller's user is shown. This function is intended to + generate human-readable output. If you are looking for + computer-parsable output, use show-user + instead. Users may be specified by their usernames or numeric + user IDs. + + + + show-user USER... + + Show properties of one or more users or the + manager itself. If no argument is specified, properties of the + manager will be shown. If a user is specified, properties of + the user are shown. By default, empty properties are + suppressed. Use to show those too. To + select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required. Use + user-status if you are looking for + formatted human-readable output. + + + + enable-linger USER... + disable-linger USER... + + Enable/disable user lingering for one or more + users. If enabled for a specific user, a user manager is + spawned for the user at boot and kept around after logouts. + This allows users who are not logged in to run long-running + services. Takes one or more user names or numeric UIDs as + argument. If no argument is specified, enables/disables + lingering for the user of the session of the caller. + + See also KillUserProcesses= setting in + logind.conf5. + + + + + terminate-user USER... + + Terminates all sessions of a user. This kills + all processes of all sessions of the user and deallocates all + runtime resources attached to the user. + + + + kill-user USER... + + Send a signal to all processes of a user. Use + to select the signal to send. + + + + + Seat Commands + + list-seats + + List currently available seats on the local + system. + + + + seat-status NAME... + + Show terse runtime status information about + one or more seats. Takes one or more seat names as parameters. + If no seat names are passed the status of the caller's + session's seat is shown. This function is intended to generate + human-readable output. If you are looking for + computer-parsable output, use show-seat + instead. + + + + show-seat NAME... + + Show properties of one or more seats or the + manager itself. If no argument is specified, properties of the + manager will be shown. If a seat is specified, properties of + the seat are shown. By default, empty properties are + suppressed. Use to show those too. To + select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required. Use + seat-status if you are looking for + formatted human-readable output. + + + + attach NAME DEVICE... + + Persistently attach one or more devices to a + seat. The devices should be specified via device paths in the + /sys file system. To create a new seat, + attach at least one graphics card to a previously unused seat + name. Seat names may consist only of a–z, A–Z, 0–9, + - and _ and must be + prefixed with seat. To drop assignment of a + device to a specific seat, just reassign it to a different + seat, or use flush-devices. + + + + + flush-devices + + Removes all device assignments previously + created with attach. After this call, only + automatically generated seats will remain, and all seat + hardware is assigned to them. + + + + terminate-seat NAME... + + Terminates all sessions on a seat. This kills + all processes of all sessions on the seat and deallocates all + runtime resources attached to them. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + Examples + + + Querying user status + + $ loginctl user-status +fatima (1005) + Since: Sat 2016-04-09 14:23:31 EDT; 54min ago + State: active + Sessions: 5 *3 + Unit: user-1005.slice + ├─user@1005.service + ... + ├─session-3.scope + ... + └─session-5.scope + ├─3473 login -- fatima + └─3515 -zsh + +Apr 09 14:40:30 laptop login[2325]: pam_unix(login:session): + session opened for user fatima by LOGIN(uid=0) +Apr 09 14:40:30 laptop login[2325]: LOGIN ON tty3 BY fatima + + + There are two sessions, 3 and 5. Session 3 is a graphical session, + marked with a star. The tree of processing including the two corresponding + scope units and the user manager unit are shown. + + + + + + + See Also + + systemd1, + systemctl1, + systemd-logind.service8, + logind.conf5 + + + + diff --git a/src/grp-login/loginctl/sysfs-show.c b/src/grp-login/loginctl/sysfs-show.c new file mode 100644 index 0000000000..12808c3336 --- /dev/null +++ b/src/grp-login/loginctl/sysfs-show.c @@ -0,0 +1,190 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/locale-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" + +#include "sysfs-show.h" + +static int show_sysfs_one( + struct udev *udev, + const char *seat, + struct udev_list_entry **item, + const char *sub, + const char *prefix, + unsigned n_columns) { + + assert(udev); + assert(seat); + assert(item); + assert(prefix); + + while (*item) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct udev_list_entry *next, *lookahead; + const char *sn, *name, *sysfs, *subsystem, *sysname; + _cleanup_free_ char *k = NULL, *l = NULL; + bool is_master; + + sysfs = udev_list_entry_get_name(*item); + if (!path_startswith(sysfs, sub)) + return 0; + + d = udev_device_new_from_syspath(udev, sysfs); + if (!d) { + *item = udev_list_entry_get_next(*item); + continue; + } + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + /* Explicitly also check for tag 'seat' here */ + if (!streq(seat, sn) || !udev_device_has_tag(d, "seat")) { + *item = udev_list_entry_get_next(*item); + continue; + } + + is_master = udev_device_has_tag(d, "master-of-seat"); + + name = udev_device_get_sysattr_value(d, "name"); + if (!name) + name = udev_device_get_sysattr_value(d, "id"); + subsystem = udev_device_get_subsystem(d); + sysname = udev_device_get_sysname(d); + + /* Look if there's more coming after this */ + lookahead = next = udev_list_entry_get_next(*item); + while (lookahead) { + const char *lookahead_sysfs; + + lookahead_sysfs = udev_list_entry_get_name(lookahead); + + if (path_startswith(lookahead_sysfs, sub) && + !path_startswith(lookahead_sysfs, sysfs)) { + _cleanup_udev_device_unref_ struct udev_device *lookahead_d = NULL; + + lookahead_d = udev_device_new_from_syspath(udev, lookahead_sysfs); + if (lookahead_d) { + const char *lookahead_sn; + + lookahead_sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(lookahead_sn)) + lookahead_sn = "seat0"; + + if (streq(seat, lookahead_sn) && udev_device_has_tag(lookahead_d, "seat")) + break; + } + } + + lookahead = udev_list_entry_get_next(lookahead); + } + + k = ellipsize(sysfs, n_columns, 20); + if (!k) + return -ENOMEM; + + printf("%s%s%s\n", prefix, special_glyph(lookahead ? TREE_BRANCH : TREE_RIGHT), k); + + if (asprintf(&l, + "%s%s:%s%s%s%s", + is_master ? "[MASTER] " : "", + subsystem, sysname, + name ? " \"" : "", strempty(name), name ? "\"" : "") < 0) + return -ENOMEM; + + free(k); + k = ellipsize(l, n_columns, 70); + if (!k) + return -ENOMEM; + + printf("%s%s%s\n", prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ", k); + + *item = next; + if (*item) { + _cleanup_free_ char *p = NULL; + + p = strappend(prefix, lookahead ? special_glyph(TREE_VERTICAL) : " "); + if (!p) + return -ENOMEM; + + show_sysfs_one(udev, seat, item, sysfs, p, n_columns - 2); + } + } + + return 0; +} + +int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + struct udev_list_entry *first = NULL; + int r; + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if (isempty(seat)) + seat = "seat0"; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + if (!streq(seat, "seat0")) + r = udev_enumerate_add_match_tag(e, seat); + else + r = udev_enumerate_add_match_tag(e, "seat"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_is_initialized(e); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + if (first) + show_sysfs_one(udev, seat, &first, "/", prefix, n_columns); + else + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), "(none)"); + + return r; +} diff --git a/src/grp-login/loginctl/sysfs-show.h b/src/grp-login/loginctl/sysfs-show.h new file mode 100644 index 0000000000..3e94bc3ed5 --- /dev/null +++ b/src/grp-login/loginctl/sysfs-show.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int show_sysfs(const char *seat, const char *prefix, unsigned columns); diff --git a/src/grp-login/pam_systemd/Makefile b/src/grp-login/pam_systemd/Makefile new file mode 100644 index 0000000000..8de05d9b7b --- /dev/null +++ b/src/grp-login/pam_systemd/Makefile @@ -0,0 +1,55 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_PAM),) +pam_systemd_la_SOURCES = \ + src/login/pam_systemd.sym \ + src/login/pam_systemd.c + +pam_systemd_la_CFLAGS = \ + $(PAM_CFLAGS) + +pam_systemd_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -Wl,--version-script=$(srcdir)/pam_systemd.sym + +pam_systemd_la_LIBADD = \ + libshared.la \ + $(PAM_LIBS) + +pamlib_LTLIBRARIES = \ + pam_systemd.la + +dist_pamconf_DATA = \ + src/login/systemd-user + +EXTRA_DIST += \ + src/login/systemd-user.m4 +endif # HAVE_PAM + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/pam_systemd/pam_systemd.c b/src/grp-login/pam_systemd/pam_systemd.c new file mode 100644 index 0000000000..11f527cda0 --- /dev/null +++ b/src/grp-login/pam_systemd/pam_systemd.c @@ -0,0 +1,553 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/hostname-util.h" +#include "basic/login-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/socket-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +static int parse_argv( + pam_handle_t *handle, + int argc, const char **argv, + const char **class, + const char **type, + bool *debug) { + + unsigned i; + + assert(argc >= 0); + assert(argc == 0 || argv); + + for (i = 0; i < (unsigned) argc; i++) { + if (startswith(argv[i], "class=")) { + if (class) + *class = argv[i] + 6; + + } else if (startswith(argv[i], "type=")) { + if (type) + *type = argv[i] + 5; + + } else if (streq(argv[i], "debug")) { + if (debug) + *debug = true; + + } else if (startswith(argv[i], "debug=")) { + int k; + + k = parse_boolean(argv[i] + 6); + if (k < 0) + pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring."); + else if (debug) + *debug = k; + + } else + pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]); + } + + return 0; +} + +static int get_user_data( + pam_handle_t *handle, + const char **ret_username, + struct passwd **ret_pw) { + + const char *username = NULL; + struct passwd *pw = NULL; + int r; + + assert(handle); + assert(ret_username); + assert(ret_pw); + + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user name."); + return r; + } + + if (isempty(username)) { + pam_syslog(handle, LOG_ERR, "User name not valid."); + return PAM_AUTH_ERR; + } + + pw = pam_modutil_getpwnam(handle, username); + if (!pw) { + pam_syslog(handle, LOG_ERR, "Failed to get user data."); + return PAM_USER_UNKNOWN; + } + + *ret_pw = pw; + *ret_username = username; + + return PAM_SUCCESS; +} + +static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + _cleanup_free_ char *p = NULL, *tty = NULL; + _cleanup_close_ int fd = -1; + struct ucred ucred; + int v, r; + + assert(display); + assert(vtnr); + + /* We deduce the X11 socket from the display name, then use + * SO_PEERCRED to determine the X11 server process, ask for + * the controlling tty of that and if it's a VC then we know + * the seat and the virtual terminal. Sounds ugly, is only + * semi-ugly. */ + + r = socket_from_display(display, &p); + if (r < 0) + return r; + strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return -errno; + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + r = get_ctty(ucred.pid, NULL, &tty); + if (r < 0) + return r; + + v = vtnr_from_tty(tty); + if (v < 0) + return v; + else if (v == 0) + return -ENOENT; + + if (seat) + *seat = "seat0"; + *vtnr = (uint32_t) v; + + return 0; +} + +static int export_legacy_dbus_address( + pam_handle_t *handle, + uid_t uid, + const char *runtime) { + + _cleanup_free_ char *s = NULL; + int r = PAM_BUF_ERR; + + /* FIXME: We *really* should move the access() check into the + * daemons that spawn dbus-daemon, instead of forcing + * DBUS_SESSION_BUS_ADDRESS= here. */ + + s = strjoin(runtime, "/bus", NULL); + if (!s) + goto error; + + if (access(s, F_OK) < 0) + return PAM_SUCCESS; + + s = mfree(s); + if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) + goto error; + + r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); + if (r != PAM_SUCCESS) + goto error; + + return PAM_SUCCESS; + +error: + pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); + return r; +} + +_public_ PAM_EXTERN int pam_sm_open_session( + pam_handle_t *handle, + int flags, + int argc, const char **argv) { + + _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, + *tty = NULL, *display = NULL, + *remote_user = NULL, *remote_host = NULL, + *seat = NULL, + *type = NULL, *class = NULL, + *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int session_fd = -1, existing, r; + bool debug = false, remote; + struct passwd *pw; + uint32_t vtnr = 0; + uid_t original_uid; + + assert(handle); + + /* Make this a NOP on non-logind systems */ + if (!logind_running()) + return PAM_SUCCESS; + + if (parse_argv(handle, + argc, argv, + &class_pam, + &type_pam, + &debug) < 0) + return PAM_SESSION_ERR; + + if (debug) + pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); + + r = get_user_data(handle, &username, &pw); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user data."); + return r; + } + + /* Make sure we don't enter a loop by talking to + * systemd-logind when it is actually waiting for the + * background to finish start-up. If the service is + * "systemd-user" we simply set XDG_RUNTIME_DIR and + * leave. */ + + pam_get_item(handle, PAM_SERVICE, (const void**) &service); + if (streq_ptr(service, "systemd-user")) { + _cleanup_free_ char *rt = NULL; + + if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0) + return PAM_BUF_ERR; + + 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; + } + + /* Otherwise, we ask logind to create a session for us */ + + pam_get_item(handle, PAM_XDISPLAY, (const void**) &display); + pam_get_item(handle, PAM_TTY, (const void**) &tty); + pam_get_item(handle, PAM_RUSER, (const void**) &remote_user); + pam_get_item(handle, PAM_RHOST, (const void**) &remote_host); + + seat = pam_getenv(handle, "XDG_SEAT"); + if (isempty(seat)) + seat = getenv("XDG_SEAT"); + + cvtnr = pam_getenv(handle, "XDG_VTNR"); + if (isempty(cvtnr)) + cvtnr = getenv("XDG_VTNR"); + + type = pam_getenv(handle, "XDG_SESSION_TYPE"); + if (isempty(type)) + type = getenv("XDG_SESSION_TYPE"); + if (isempty(type)) + type = type_pam; + + class = pam_getenv(handle, "XDG_SESSION_CLASS"); + if (isempty(class)) + class = getenv("XDG_SESSION_CLASS"); + if (isempty(class)) + class = class_pam; + + desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP"); + if (isempty(desktop)) + desktop = getenv("XDG_SESSION_DESKTOP"); + + tty = strempty(tty); + + if (strchr(tty, ':')) { + /* A tty with a colon is usually an X11 display, + * placed there to show up in utmp. We rearrange + * things and don't pretend that an X display was a + * tty. */ + + if (isempty(display)) + display = tty; + tty = NULL; + } else if (streq(tty, "cron")) { + /* cron has been setting PAM_TTY to "cron" for a very + * long time and it probably shouldn't stop doing that + * for compatibility reasons. */ + type = "unspecified"; + class = "background"; + tty = NULL; + } else if (streq(tty, "ssh")) { + /* ssh has been setting PAM_TTY to "ssh" for a very + * long time and probably shouldn't stop doing that + * for compatibility reasons. */ + type ="tty"; + class = "user"; + tty = NULL; + } + + /* If this fails vtnr will be 0, that's intended */ + if (!isempty(cvtnr)) + (void) safe_atou32(cvtnr, &vtnr); + + if (!isempty(display) && !vtnr) { + if (isempty(seat)) + get_seat_from_display(display, &seat, &vtnr); + else if (streq(seat, "seat0")) + get_seat_from_display(display, NULL, &vtnr); + } + + if (seat && !streq(seat, "seat0") && vtnr != 0) { + pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat); + vtnr = 0; + } + + if (isempty(type)) + type = !isempty(display) ? "x11" : + !isempty(tty) ? "tty" : "unspecified"; + + if (isempty(class)) + class = streq(type, "unspecified") ? "background" : "user"; + + remote = !isempty(remote_host) && !is_localhost(remote_host); + + /* Talk to logind over the message bus */ + + r = sd_bus_open_system(&bus); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + if (debug) + pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " + "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s", + pw->pw_uid, getpid(), + strempty(service), + type, class, strempty(desktop), + strempty(seat), vtnr, strempty(tty), strempty(display), + yes_no(remote), strempty(remote_user), strempty(remote_host)); + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CreateSession", + &error, + &reply, + "uusssssussbssa(sv)", + (uint32_t) pw->pw_uid, + (uint32_t) getpid(), + service, + type, + class, + desktop, + seat, + vtnr, + tty, + display, + remote, + remote_user, + remote_host, + 0); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) { + pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r)); + return PAM_SUCCESS; + } else { + pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); + return PAM_SYSTEM_ERR; + } + } + + r = sd_bus_message_read(reply, + "soshusub", + &id, + &object_path, + &runtime_path, + &session_fd, + &original_uid, + &seat, + &vtnr, + &existing); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + if (debug) + pam_syslog(handle, LOG_DEBUG, "Reply from logind: " + "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", + id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); + + r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set session id."); + return r; + } + + if (original_uid == pw->pw_uid) { + /* Don't set $XDG_RUNTIME_DIR if the user we now + * authenticated for does not match the original user + * of the session. We do this in order not to result + * in privileged apps clobbering the runtime directory + * unnecessarily. */ + + r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 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, runtime_path); + if (r != PAM_SUCCESS) + return r; + } + + if (!isempty(seat)) { + r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set seat."); + return r; + } + } + + if (vtnr > 0) { + char buf[DECIMAL_STR_MAX(vtnr)]; + sprintf(buf, "%u", vtnr); + + r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number."); + return r; + } + } + + r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to install existing flag."); + return r; + } + + if (session_fd >= 0) { + session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3); + if (session_fd < 0) { + pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m"); + return PAM_SESSION_ERR; + } + + 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); + return r; + } + } + + return PAM_SUCCESS; +} + +_public_ PAM_EXTERN int pam_sm_close_session( + pam_handle_t *handle, + int flags, + int argc, const char **argv) { + + _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; + + assert(handle); + + /* Only release session if it wasn't pre-existing when we + * tried to create it */ + pam_get_data(handle, "systemd.existing", &existing); + + id = pam_getenv(handle, "XDG_SESSION_ID"); + if (id && !existing) { + + /* Before we go and close the FIFO we need to tell + * logind that this is a clean session shutdown, so + * that it doesn't just go and slaughter us + * immediately after closing the fd */ + + r = sd_bus_open_system(&bus); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ReleaseSession", + &error, + NULL, + "s", + id); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r)); + return PAM_SESSION_ERR; + } + } + + /* Note that we are knowingly leaking the FIFO fd here. This + * way, logind can watch us die. If we closed it here it would + * not have any clue when that is completed. Given that one + * cannot really have multiple PAM sessions open from the same + * process this means we will leak one FD at max. */ + + return PAM_SUCCESS; +} diff --git a/src/grp-login/pam_systemd/pam_systemd.sym b/src/grp-login/pam_systemd/pam_systemd.sym new file mode 100644 index 0000000000..23ff75f688 --- /dev/null +++ b/src/grp-login/pam_systemd/pam_systemd.sym @@ -0,0 +1,15 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. +***/ + +{ +global: + pam_sm_close_session; + pam_sm_open_session; +local: *; +}; diff --git a/src/grp-login/pam_systemd/pam_systemd.xml b/src/grp-login/pam_systemd/pam_systemd.xml new file mode 100644 index 0000000000..ddda81bc90 --- /dev/null +++ b/src/grp-login/pam_systemd/pam_systemd.xml @@ -0,0 +1,296 @@ + + + + + + + + + pam_systemd + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + pam_systemd + 8 + + + + pam_systemd + Register user sessions in the systemd login manager + + + + pam_systemd.so + + + + Description + + pam_systemd registers user sessions with + the systemd login manager + systemd-logind.service8, + and hence the systemd control group hierarchy. + + On login, this module ensures the following: + + + If it does not exist yet, the user runtime + directory /run/user/$USER is created and + its ownership changed to the user that is logging + in. + + The $XDG_SESSION_ID + environment variable is initialized. If auditing is available + and pam_loginuid.so was run before this + module (which is highly recommended), the variable is + initialized from the auditing session id + (/proc/self/sessionid). Otherwise, an + independent session counter is used. + + A new systemd scope unit is created for the + session. If this is the first concurrent session of the user, an + implicit slice below user.slice is + automatically created and the scope placed into it. An instance + of the system service user@.service, which + runs the systemd user manager instance, is started. + + + + On logout, this module ensures the following: + + + If enabled in + logind.conf + 5, all processes of the + session are terminated. If the last concurrent session of a user + ends, the user's systemd instance will be terminated too, and so + will the user's slice unit. + + If the last concurrent session of a user ends, + the $XDG_RUNTIME_DIR directory and all its + contents are removed, too. + + + If the system was not booted up with systemd as init system, + this module does nothing and immediately returns + PAM_SUCCESS. + + + + + Options + + The following options are understood: + + + + + + + Takes a string argument which sets the session + class. The XDG_SESSION_CLASS environmental variable takes + precedence. One of + user, + greeter, + lock-screen or + background. See + sd_session_get_class3 + for details about the session class. + + + + + + Takes a string argument which sets the session + type. The XDG_SESSION_TYPE environmental variable takes + precedence. One of + unspecified, + tty, + x11, + wayland or + mir. See + sd_session_get_type3 + for details about the session type. + + + + + + Takes an optional + boolean argument. If yes or without + the argument, the module will log + debugging information as it + operates. + + + + + + Module Types Provided + + Only is provided. + + + + Environment + + The following environment variables are set for the + processes of the user's session: + + + + $XDG_SESSION_ID + + A session identifier, suitable to be used in + filenames. The string itself should be considered opaque, + although often it is just the audit session ID as reported by + /proc/self/sessionid. Each ID will be + assigned only once during machine uptime. It may hence be used + to uniquely label files or other resources of this + session. + + + + $XDG_RUNTIME_DIR + + Path to a user-private user-writable directory + that is bound to the user login time on the machine. It is + automatically created the first time a user logs in and + removed on the user's final logout. If a user logs in twice at + the same time, both sessions will see the same + $XDG_RUNTIME_DIR and the same contents. If + a user logs in once, then logs out again, and logs in again, + the directory contents will have been lost in between, but + applications should not rely on this behavior and must be able + to deal with stale files. To store session-private data in + this directory, the user should include the value of + $XDG_SESSION_ID in the filename. This + directory shall be used for runtime file system objects such + as AF_UNIX sockets, FIFOs, PID files and + similar. It is guaranteed that this directory is local and + offers the greatest possible file system feature set the + operating system provides. For further details, see the XDG + Base Directory Specification. + + + + + The following environment variables are read by the module + and may be used by the PAM service to pass metadata to the + module: + + + + $XDG_SESSION_TYPE + + The session type. This may be used instead of + on the module parameter line, and is + usually preferred. + + + + $XDG_SESSION_CLASS + + The session class. This may be used instead of + on the module parameter line, and is + usually preferred. + + + + $XDG_SESSION_DESKTOP + + A single, short identifier string for the + desktop environment. This may be used to indicate the session + desktop used, where this applies and if this information is + available. For example: GNOME, or + KDE. It is recommended to use the same + identifiers and capitalization as for + $XDG_CURRENT_DESKTOP, as defined by the + Desktop + Entry Specification. (However, note that + $XDG_SESSION_DESKTOP only takes a single + item, and not a colon-separated list like + $XDG_CURRENT_DESKTOP.) See + sd_session_get_desktop3 + for more details. + + + + $XDG_SEAT + + The seat name the session shall be registered + for, if any. + + + + $XDG_VTNR + + The VT number the session shall be registered + for, if any. (Only applies to seats with a VT available, such + as seat0) + + + + + + + Example + + #%PAM-1.0 +auth required pam_unix.so +auth required pam_nologin.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so +session required pam_loginuid.so +session required pam_systemd.so + + + + See Also + + systemd1, + systemd-logind.service8, + logind.conf5, + loginctl1, + pam.conf5, + pam.d5, + pam8, + pam_loginuid8, + systemd.scope5, + systemd.slice5, + systemd.service5 + + + + diff --git a/src/grp-login/systemd-inhibit/Makefile b/src/grp-login/systemd-inhibit/Makefile new file mode 100644 index 0000000000..8b3b7995d9 --- /dev/null +++ b/src/grp-login/systemd-inhibit/Makefile @@ -0,0 +1,37 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +dist_zshcompletion_data += shell-completion/zsh/_systemd-inhibit + +systemd_inhibit_SOURCES = \ + src/login/inhibit.c + +systemd_inhibit_LDADD = \ + libsystemd-shared.la + +rootbin_PROGRAMS += \ + systemd-inhibit + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/systemd-inhibit/inhibit.c b/src/grp-login/systemd-inhibit/inhibit.c new file mode 100644 index 0000000000..543bdb9e80 --- /dev/null +++ b/src/grp-login/systemd-inhibit/inhibit.c @@ -0,0 +1,292 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +static const char* arg_what = "idle:sleep:shutdown"; +static const char* arg_who = NULL; +static const char* arg_why = "Unknown reason"; +static const char* arg_mode = NULL; + +static enum { + ACTION_INHIBIT, + ACTION_LIST +} arg_action = ACTION_INHIBIT; + +static int inhibit(sd_bus *bus, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + int fd; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + error, + &reply, + "ssss", arg_what, arg_who, arg_why, arg_mode); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); + if (r < 0) + return r; + + r = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (r < 0) + return -errno; + + return r; +} + +static int print_inhibitors(sd_bus *bus, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *what, *who, *why, *mode; + unsigned int uid, pid; + unsigned n = 0; + int r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors", + error, + &reply, + ""); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { + _cleanup_free_ char *comm = NULL, *u = NULL; + + if (arg_mode && !streq(mode, arg_mode)) + continue; + + get_process_comm(pid, &comm); + u = uid_to_name(uid); + + printf(" Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n" + " What: %s\n" + " Why: %s\n" + " Mode: %s\n\n", + who, uid, strna(u), pid, strna(comm), + what, + why, + mode); + + n++; + } + 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); + + printf("%u inhibitors listed.\n", n); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Execute a process while inhibiting shutdown/sleep/idle.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --what=WHAT Operations to inhibit, colon separated list of:\n" + " shutdown, sleep, idle, handle-power-key,\n" + " handle-suspend-key, handle-hibernate-key,\n" + " handle-lid-switch\n" + " --who=STRING A descriptive string who is inhibiting\n" + " --why=STRING A descriptive string why is being inhibited\n" + " --mode=MODE One of block or delay\n" + " --list List active inhibitors\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_WHAT, + ARG_WHO, + ARG_WHY, + ARG_MODE, + ARG_LIST, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "what", required_argument, NULL, ARG_WHAT }, + { "who", required_argument, NULL, ARG_WHO }, + { "why", required_argument, NULL, ARG_WHY }, + { "mode", required_argument, NULL, ARG_MODE }, + { "list", no_argument, NULL, ARG_LIST }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_WHAT: + arg_what = optarg; + break; + + case ARG_WHO: + arg_who = optarg; + break; + + case ARG_WHY: + arg_why = optarg; + break; + + case ARG_MODE: + arg_mode = optarg; + break; + + case ARG_LIST: + arg_action = ACTION_LIST; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_action == ACTION_INHIBIT && optind == argc) + arg_action = ACTION_LIST; + + else if (arg_action == ACTION_INHIBIT && optind >= argc) { + log_error("Missing command line to execute."); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + _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(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + return EXIT_FAILURE; + if (r == 0) + return EXIT_SUCCESS; + + r = sd_bus_default_system(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to bus: %m"); + return EXIT_FAILURE; + } + + if (arg_action == ACTION_LIST) { + + r = print_inhibitors(bus, &error); + if (r < 0) { + log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r)); + return EXIT_FAILURE; + } + + } else { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *w = NULL; + pid_t pid; + + if (!arg_who) + arg_who = w = strv_join(argv + optind, " "); + + if (!arg_mode) + arg_mode = "block"; + + fd = inhibit(bus, &error); + if (fd < 0) { + log_error("Failed to inhibit: %s", bus_error_message(&error, fd)); + return EXIT_FAILURE; + } + + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + return EXIT_FAILURE; + } + + if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + close_all_fds(NULL, 0); + + execvp(argv[optind], argv + optind); + log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(argv[optind], pid, true); + return r < 0 ? EXIT_FAILURE : r; + } + + return 0; +} diff --git a/src/grp-login/systemd-inhibit/systemd-inhibit.completion.zsh b/src/grp-login/systemd-inhibit/systemd-inhibit.completion.zsh new file mode 100644 index 0000000000..1b3247b2cd --- /dev/null +++ b/src/grp-login/systemd-inhibit/systemd-inhibit.completion.zsh @@ -0,0 +1,33 @@ +#compdef systemd-inhibit + +_systemd_inhibit_command(){ + if (( CURRENT == 1 )); then + compset -q + _normal + else + local n=${words[(b:2:i)[^-]*]} + if (( n <= CURRENT )); then + compset -n $n + _alternative \ + 'files:file:_files' \ + 'commands:command:_normal' && return 0 + fi + _default + fi +} + +_inhibit_what() { + local _inhibit + _inhibit=(shutdown sleep idle handle-power-key handle-suspend-key handle-hibernate-key handle-lid-switch) + _values -s : "${_inhibit[@]}" +} + +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--what=[Operations to inhibit]:options:_inhibit_what' \ + '--who=[A descriptive string who is inhibiting]:who is inhibiting:' \ + '--why=[A descriptive string why is being inhibited]:reason for the lock:' \ + '--mode=[One of block or delay]:lock mode:( block delay )' \ + '--list[List active inhibitors]' \ + '*:commands:_systemd_inhibit_command' diff --git a/src/grp-login/systemd-inhibit/systemd-inhibit.xml b/src/grp-login/systemd-inhibit/systemd-inhibit.xml new file mode 100644 index 0000000000..9d85908f97 --- /dev/null +++ b/src/grp-login/systemd-inhibit/systemd-inhibit.xml @@ -0,0 +1,177 @@ + + + + + + + + + systemd-inhibit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-inhibit + 1 + + + + systemd-inhibit + Execute a program with an inhibition lock taken + + + + + systemd-inhibit OPTIONS COMMAND ARGUMENTS + + + systemd-inhibit OPTIONS --list + + + + + Description + + systemd-inhibit may be used to execute a + program with a shutdown, sleep or idle inhibitor lock taken. The + lock will be acquired before the specified command line is + executed and released afterwards. + + Inhibitor locks may be used to block or delay system sleep + and shutdown requests from the user, as well as automatic idle + handling of the OS. This is useful to avoid system suspends while + an optical disc is being recorded, or similar operations that + should not be interrupted. + + For more information see the Inhibitor + Lock Developer Documentation. + + + + Options + + The following options are understood: + + + + + + Takes a colon-separated list of one or more + operations to inhibit: + shutdown, + sleep, + idle, + handle-power-key, + handle-suspend-key, + handle-hibernate-key, + handle-lid-switch, + for inhibiting reboot/power-off/halt/kexec, + suspending/hibernating, the automatic idle detection, or the + low-level handling of the power/sleep key and the lid switch, + respectively. If omitted, defaults to + idle:sleep:shutdown. + + + + + + Takes a short, human-readable descriptive + string for the program taking the lock. If not passed, + defaults to the command line string. + + + + + + Takes a short, human-readable descriptive + string for the reason for taking the lock. Defaults to + "Unknown reason". + + + + + + Takes either block or + delay and describes how the lock is + applied. If block is used (the default), + the lock prohibits any of the requested operations without + time limit, and only privileged users may override it. If + delay is used, the lock can only delay the + requested operations for a limited time. If the time elapses, + the lock is ignored and the operation executed. The time limit + may be specified in + logind.conf5. + Note that delay is only available for + sleep and + shutdown. + + + + + + Lists all active inhibition locks instead of + acquiring one. + + + + + + + + + + Exit status + + Returns the exit status of the executed program. + + + + Example + + # systemd-inhibit wodim foobar.iso + + This burns the ISO image + foobar.iso on a CD using + wodim1, + and inhibits system sleeping, shutdown and idle while + doing so. + + + + See Also + + systemd1, + logind.conf5 + + + + diff --git a/src/grp-login/systemd-logind/70-power-switch.rules b/src/grp-login/systemd-logind/70-power-switch.rules new file mode 100644 index 0000000000..e2855b50f7 --- /dev/null +++ b/src/grp-login/systemd-logind/70-power-switch.rules @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="power_switch_end" + +SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", \ + SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="*,116|116,*|116|*,116,*", TAG+="power-switch" + +LABEL="power_switch_end" diff --git a/src/grp-login/systemd-logind/70-uaccess.rules b/src/grp-login/systemd-logind/70-uaccess.rules new file mode 100644 index 0000000000..50dcd2e275 --- /dev/null +++ b/src/grp-login/systemd-logind/70-uaccess.rules @@ -0,0 +1,81 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="uaccess_end" +ENV{MAJOR}=="", GOTO="uaccess_end" + +# PTP/MTP protocol devices, cameras, portable media players +SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess" + +# Digicams with proprietary protocol +ENV{ID_GPHOTO2}=="?*", TAG+="uaccess" + +# SCSI and USB scanners +ENV{libsane_matched}=="yes", TAG+="uaccess" + +# HPLIP devices (necessary for ink level check and HP tool maintenance) +ENV{ID_HPLIP}=="1", TAG+="uaccess" + +# optical drives +SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess" + +# Sound devices +SUBSYSTEM=="sound", TAG+="uaccess" \ + OPTIONS+="static_node=snd/timer", OPTIONS+="static_node=snd/seq" + +# ffado is an userspace driver for firewire sound cards +SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess" + +# Webcams, frame grabber, TV cards +SUBSYSTEM=="video4linux", TAG+="uaccess" +SUBSYSTEM=="dvb", TAG+="uaccess" + +# IIDC devices: industrial cameras and some webcams +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" +# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" + +# DRI video devices +SUBSYSTEM=="drm", KERNEL=="card*|renderD*", TAG+="uaccess" + +# KVM +SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" + +# smart-card readers +ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess" + +# (USB) authentication devices +ENV{ID_SECURITY_TOKEN}=="?*", TAG+="uaccess" + +# PDA devices +ENV{ID_PDA}=="?*", TAG+="uaccess" + +# Programmable remote control +ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" + +# joysticks +SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" + +# color measurement devices +ENV{COLOR_MEASUREMENT_DEVICE}=="?*", TAG+="uaccess" + +# DDC/CI device, usually high-end monitors such as the DreamColor +ENV{DDC_DEVICE}=="?*", TAG+="uaccess" + +# media player raw devices (for user-mode drivers, Android SDK, etc.) +SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" + +# software-defined radio communication devices +ENV{ID_SOFTWARE_RADIO}=="?*", TAG+="uaccess" + +# 3D printers, CNC machines, laser cutters, 3D scanners, etc. +ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess" + +LABEL="uaccess_end" diff --git a/src/grp-login/systemd-logind/71-seat.rules.in b/src/grp-login/systemd-logind/71-seat.rules.in new file mode 100644 index 0000000000..de55c9a4ec --- /dev/null +++ b/src/grp-login/systemd-logind/71-seat.rules.in @@ -0,0 +1,54 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="seat_end" + +TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat" +SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat" +SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat" +SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat" +SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" +SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" + +# 'Plugable' USB hub, sound, network, graphics adapter +SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1" + +# qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group +# devices belonging to one seat. See: +# http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt +SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", TAG+="seat", ENV{ID_AUTOSEAT}="1" + +# Mimo 720, with integrated USB hub, displaylink graphics, and e2i +# touchscreen. This device carries no proper VID/PID in the USB hub, +# but it does carry good ID data in the graphics component, hence we +# check it from the parent. There's a bit of a race here however, +# given that the child devices might not exist yet at the time this +# rule is executed. To work around this we'll trigger the parent from +# the child if we notice that the parent wasn't recognized yet. + +# Match parent +SUBSYSTEM=="usb", ATTR{idVendor}=="058f", ATTR{idProduct}=="6254", \ + ATTR{%k.2/idVendor}=="17e9", ATTR{%k.2/idProduct}=="401a", ATTR{%k.2/product}=="mimo inc", \ + ENV{ID_AUTOSEAT}="1", ENV{ID_AVOID_LOOP}="1" + +# Match child, look for parent's ID_AVOID_LOOP +SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ + ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ + IMPORT{parent}="ID_AVOID_LOOP" + +# Match child, retrigger parent +SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ + ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ + ENV{ID_AVOID_LOOP}=="", \ + RUN+="@rootbindir@/udevadm trigger --parent-match=%p/.." + +TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id" +TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}" + +SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@rootbindir@/loginctl lock-sessions" + +LABEL="seat_end" diff --git a/src/grp-login/systemd-logind/73-seat-late.rules.in b/src/grp-login/systemd-logind/73-seat-late.rules.in new file mode 100644 index 0000000000..901df750fd --- /dev/null +++ b/src/grp-login/systemd-logind/73-seat-late.rules.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="seat_late_end" + +ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="seat-$env{ID_FOR_SEAT}" +ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" + +ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" + +TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" + +LABEL="seat_late_end" diff --git a/src/grp-login/systemd-logind/Makefile b/src/grp-login/systemd-logind/Makefile new file mode 100644 index 0000000000..24de945818 --- /dev/null +++ b/src/grp-login/systemd-logind/Makefile @@ -0,0 +1,132 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_LOGIND),) + +systemd_logind_SOURCES = \ + src/login/logind.c \ + src/login/logind.h + +nodist_systemd_logind_SOURCES = \ + src/login/logind-gperf.c + +systemd_logind_LDADD = \ + liblogind-core.la + +liblogind_core_la_SOURCES = \ + src/login/logind-core.c \ + src/login/logind-device.c \ + src/login/logind-device.h \ + src/login/logind-button.c \ + src/login/logind-button.h \ + src/login/logind-action.c \ + src/login/logind-action.h \ + src/login/logind-seat.c \ + src/login/logind-seat.h \ + src/login/logind-session.c \ + src/login/logind-session.h \ + src/login/logind-session-device.c \ + src/login/logind-session-device.h \ + src/login/logind-user.c \ + src/login/logind-user.h \ + src/login/logind-inhibit.c \ + src/login/logind-inhibit.h \ + src/login/logind-dbus.c \ + src/login/logind-session-dbus.c \ + src/login/logind-seat-dbus.c \ + src/login/logind-user-dbus.c \ + src/login/logind-utmp.c \ + src/login/logind-acl.h + +liblogind_core_la_LIBADD = \ + libsystemd-shared.la + +ifneq ($(HAVE_ACL),) +liblogind_core_la_SOURCES += \ + src/login/logind-acl.c +endif # HAVE_ACL + +noinst_LTLIBRARIES += \ + liblogind-core.la + +rootlibexec_PROGRAMS += \ + systemd-logind + +nodist_systemunit_DATA += \ + units/systemd-logind.service + +dist_systemunit_DATA += \ + units/user.slice + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.login1.busname + +dist_dbussystemservice_DATA += \ + src/login/org.freedesktop.login1.service + +dist_dbuspolicy_DATA += \ + src/login/org.freedesktop.login1.conf + +nodist_pkgsysconf_DATA += \ + src/login/logind.conf + +polkitpolicy_files += \ + src/login/org.freedesktop.login1.policy + +INSTALL_DIRS += \ + $(systemdstatedir) + +MULTI_USER_TARGET_WANTS += \ + systemd-logind.service + +SYSTEM_UNIT_ALIASES += \ + systemd-logind.service dbus-org.freedesktop.login1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.login1.busname + +dist_udevrules_DATA += \ + src/login/70-uaccess.rules \ + src/login/70-power-switch.rules + +nodist_udevrules_DATA += \ + src/login/71-seat.rules \ + src/login/73-seat-late.rules + +endif # ENABLE_LOGIND + +polkitpolicy_in_files += \ + src/login/org.freedesktop.login1.policy.in + +gperf_gperf_sources += \ + src/login/logind-gperf.gperf + +EXTRA_DIST += \ + src/login/71-seat.rules.in \ + src/login/73-seat-late.rules.in \ + units/systemd-logind.service.in \ + src/login/logind.conf.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-login/systemd-logind/logind-acl.c b/src/grp-login/systemd-logind/logind-acl.c new file mode 100644 index 0000000000..44e49c1dad --- /dev/null +++ b/src/grp-login/systemd-logind/logind-acl.c @@ -0,0 +1,293 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/acl-util.h" +#include "shared/udev-util.h" + +#include "logind-acl.h" + +static int flush_acl(acl_t acl) { + acl_entry_t i; + int found; + bool changed = false; + + assert(acl); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + if (acl_delete_entry(acl, i) < 0) + return -errno; + + changed = true; + } + + if (found < 0) + return -errno; + + return changed; +} + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + acl_t acl; + int r = 0; + bool changed = false; + + assert(path); + + acl = acl_get_file(path, ACL_TYPE_ACCESS); + if (!acl) + return -errno; + + if (flush) { + + r = flush_acl(acl); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else if (del && old_uid > 0) { + acl_entry_t entry; + + r = acl_find_uid(acl, old_uid, &entry); + if (r < 0) + goto finish; + + if (r > 0) { + if (acl_delete_entry(acl, entry) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (add && new_uid > 0) { + acl_entry_t entry; + acl_permset_t permset; + int rd, wt; + + r = acl_find_uid(acl, new_uid, &entry); + if (r < 0) + goto finish; + + if (r == 0) { + if (acl_create_entry(&acl, &entry) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &new_uid) < 0) { + r = -errno; + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0) { + r = -errno; + goto finish; + } + + rd = acl_get_perm(permset, ACL_READ); + if (rd < 0) { + r = -errno; + goto finish; + } + + wt = acl_get_perm(permset, ACL_WRITE); + if (wt < 0) { + r = -errno; + goto finish; + } + + if (!rd || !wt) { + + if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (!changed) + goto finish; + + if (acl_calc_mask(&acl) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) { + r = -errno; + goto finish; + } + + r = 0; + +finish: + acl_free(acl); + + return r; +} + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_set_free_free_ Set *nodes = NULL; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + Iterator i; + char *n; + int r; + + assert(udev); + + nodes = set_new(&string_hash_ops); + if (!nodes) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + if (isempty(seat)) + seat = "seat0"; + + /* We can only match by one tag in libudev. We choose + * "uaccess" for that. If we could match for two tags here we + * could add the seat name as second match tag, but this would + * be hardly optimizable in libudev, and hence checking the + * second tag manually in our loop is a good solution. */ + r = udev_enumerate_add_match_tag(e, "uaccess"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_is_initialized(e); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + const char *node, *sn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + if (!streq(seat, sn)) + continue; + + node = udev_device_get_devnode(d); + /* In case people mistag devices with nodes, we need to ignore this */ + if (!node) + continue; + + n = strdup(node); + if (!n) + return -ENOMEM; + + log_debug("Found udev node %s for seat %s", n, seat); + r = set_consume(nodes, n); + if (r < 0) + return r; + } + + /* udev exports "dead" device nodes to allow module on-demand loading, + * these devices are not known to the kernel at this moment */ + dir = opendir("/run/udev/static_node-tags/uaccess"); + if (dir) { + FOREACH_DIRENT(dent, dir, return -errno) { + _cleanup_free_ char *unescaped_devname = NULL; + + if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0) + return -ENOMEM; + + n = strappend("/dev/", unescaped_devname); + if (!n) + return -ENOMEM; + + log_debug("Found static node %s for seat %s", n, seat); + r = set_consume(nodes, n); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } + } + + r = 0; + SET_FOREACH(n, nodes, i) { + int k; + + log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)", + n, seat, old_uid, new_uid, + del ? " del" : "", add ? " add" : ""); + + k = devnode_acl(n, flush, del, old_uid, add, new_uid); + if (k == -ENOENT) + log_debug("Device %s disappeared while setting ACLs", n); + else if (k < 0 && r == 0) + r = k; + } + + return r; +} diff --git a/src/grp-login/systemd-logind/logind-acl.h b/src/grp-login/systemd-logind/logind-acl.h new file mode 100644 index 0000000000..7d324ea90a --- /dev/null +++ b/src/grp-login/systemd-logind/logind-acl.h @@ -0,0 +1,56 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#ifdef HAVE_ACL + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); +#else + +static inline int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +static inline int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +#endif diff --git a/src/grp-login/systemd-logind/logind-action.c b/src/grp-login/systemd-logind/logind-action.c new file mode 100644 index 0000000000..a7c88e7e65 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-action.c @@ -0,0 +1,179 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" +#include "shared/sleep-config.h" + +#include "logind-action.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge) { + + static const char * const message_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = "Powering Off...", + [HANDLE_REBOOT] = "Rebooting...", + [HANDLE_HALT] = "Halting...", + [HANDLE_KEXEC] = "Rebooting via kexec...", + [HANDLE_SUSPEND] = "Suspending...", + [HANDLE_HIBERNATE] = "Hibernating...", + [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." + }; + + static const char * const target_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, + [HANDLE_HALT] = SPECIAL_HALT_TARGET, + [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, + [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + InhibitWhat inhibit_operation; + Inhibitor *offending = NULL; + bool supported; + int r; + + assert(m); + + /* If the key handling is turned off, don't do anything */ + if (handle == HANDLE_IGNORE) { + log_debug("Refusing operation, as it is turned off."); + return 0; + } + + if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) { + /* If the last system suspend or startup is too close, + * let's not suspend for now, to give USB docking + * stations some time to settle so that we can + * properly watch its displays. */ + if (m->lid_switch_ignore_event_source) { + log_debug("Ignoring lid switch request, system startup or resume too close."); + return 0; + } + } + + /* If the key handling is inhibited, don't do anything */ + if (!ignore_inhibited && inhibit_key > 0) { + if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { + log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); + return 0; + } + } + + /* Locking is handled differently from the rest. */ + if (handle == HANDLE_LOCK) { + + if (!is_edge) + return 0; + + log_info("Locking sessions..."); + session_send_lock_all(m, true); + return 1; + } + + if (handle == HANDLE_SUSPEND) + supported = can_sleep("suspend") > 0; + else if (handle == HANDLE_HIBERNATE) + supported = can_sleep("hibernate") > 0; + else if (handle == HANDLE_HYBRID_SLEEP) + supported = can_sleep("hybrid-sleep") > 0; + else if (handle == HANDLE_KEXEC) + supported = access(KEXEC, X_OK) >= 0; + else + supported = true; + + if (!supported) { + log_warning("Requested operation not supported, ignoring."); + return -EOPNOTSUPP; + } + + if (m->action_what) { + log_debug("Action already in progress, ignoring."); + return -EALREADY; + } + + inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; + + /* If the actual operation is inhibited, warn and fail */ + if (!ignore_inhibited && + manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) { + _cleanup_free_ char *comm = NULL, *u = NULL; + + get_process_comm(offending->pid, &comm); + u = uid_to_name(offending->uid); + + /* If this is just a recheck of the lid switch then don't warn about anything */ + if (!is_edge) { + log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", + inhibit_what_to_string(inhibit_operation), + offending->uid, strna(u), + offending->pid, strna(comm)); + return 0; + } + + log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", + inhibit_what_to_string(inhibit_operation), + offending->uid, strna(u), + offending->pid, strna(comm)); + + return -EPERM; + } + + log_info("%s", message_table[handle]); + + r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); + if (r < 0) { + log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + return r; + } + + return 1; +} + +static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { + [HANDLE_IGNORE] = "ignore", + [HANDLE_POWEROFF] = "poweroff", + [HANDLE_REBOOT] = "reboot", + [HANDLE_HALT] = "halt", + [HANDLE_KEXEC] = "kexec", + [HANDLE_SUSPEND] = "suspend", + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", + [HANDLE_LOCK] = "lock" +}; + +DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); +DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); diff --git a/src/grp-login/systemd-logind/logind-action.h b/src/grp-login/systemd-logind/logind-action.h new file mode 100644 index 0000000000..fb40ae48d2 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-action.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef enum HandleAction { + HANDLE_IGNORE, + HANDLE_POWEROFF, + HANDLE_REBOOT, + HANDLE_HALT, + HANDLE_KEXEC, + HANDLE_SUSPEND, + HANDLE_HIBERNATE, + HANDLE_HYBRID_SLEEP, + HANDLE_LOCK, + _HANDLE_ACTION_MAX, + _HANDLE_ACTION_INVALID = -1 +} HandleAction; + +#include "logind-inhibit.h" +#include "logind.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge); + +const char* handle_action_to_string(HandleAction h) _const_; +HandleAction handle_action_from_string(const char *s) _pure_; + +int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-login/systemd-logind/logind-button.c b/src/grp-login/systemd-logind/logind-button.c new file mode 100644 index 0000000000..81c82117fc --- /dev/null +++ b/src/grp-login/systemd-logind/logind-button.c @@ -0,0 +1,290 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "logind-button.h" + +Button* button_new(Manager *m, const char *name) { + Button *b; + + assert(m); + assert(name); + + b = new0(Button, 1); + if (!b) + return NULL; + + b->name = strdup(name); + if (!b->name) { + free(b); + return NULL; + } + + if (hashmap_put(m->buttons, b->name, b) < 0) { + free(b->name); + free(b); + return NULL; + } + + b->manager = m; + b->fd = -1; + + return b; +} + +void button_free(Button *b) { + assert(b); + + hashmap_remove(b->manager->buttons, b->name); + + sd_event_source_unref(b->io_event_source); + sd_event_source_unref(b->check_event_source); + + if (b->fd >= 0) + /* If the device has been unplugged close() returns + * ENODEV, let's ignore this, hence we don't use + * safe_close() */ + (void) close(b->fd); + + free(b->name); + free(b->seat); + free(b); +} + +int button_set_seat(Button *b, const char *sn) { + char *s; + + assert(b); + assert(sn); + + s = strdup(sn); + if (!s) + return -ENOMEM; + + free(b->seat); + b->seat = s; + + return 0; +} + +static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { + HandleAction handle_action; + + assert(manager); + + /* If we are docked, handle the lid switch differently */ + if (manager_is_docked_or_external_displays(manager)) + handle_action = manager->handle_lid_switch_docked; + else + handle_action = manager->handle_lid_switch; + + manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge); +} + +static int button_recheck(sd_event_source *e, void *userdata) { + Button *b = userdata; + + assert(b); + assert(b->lid_closed); + + button_lid_switch_handle_action(b->manager, false); + return 1; +} + +static int button_install_check_event_source(Button *b) { + int r; + assert(b); + + /* Install a post handler, so that we keep rechecking as long as the lid is closed. */ + + if (b->check_event_source) + return 0; + + r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b); + if (r < 0) + return r; + + return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1); +} + +static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Button *b = userdata; + struct input_event ev; + ssize_t l; + + assert(s); + assert(fd == b->fd); + assert(b); + + l = read(b->fd, &ev, sizeof(ev)); + if (l < 0) + return errno != EAGAIN ? -errno : 0; + if ((size_t) l < sizeof(ev)) + return -EIO; + + if (ev.type == EV_KEY && ev.value > 0) { + + switch (ev.code) { + + case KEY_POWER: + case KEY_POWER2: + log_struct(LOG_INFO, + LOG_MESSAGE("Power key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); + break; + + /* The kernel is a bit confused here: + + KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend" + KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate" + */ + + case KEY_SLEEP: + log_struct(LOG_INFO, + LOG_MESSAGE("Suspend key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); + break; + + case KEY_SUSPEND: + log_struct(LOG_INFO, + LOG_MESSAGE("Hibernate key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), + NULL); + + manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); + break; + } + + } else if (ev.type == EV_SW && ev.value > 0) { + + if (ev.code == SW_LID) { + log_struct(LOG_INFO, + LOG_MESSAGE("Lid closed."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), + NULL); + + b->lid_closed = true; + button_lid_switch_handle_action(b->manager, true); + button_install_check_event_source(b); + + } else if (ev.code == SW_DOCK) { + log_struct(LOG_INFO, + LOG_MESSAGE("System docked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), + NULL); + + b->docked = true; + } + + } else if (ev.type == EV_SW && ev.value == 0) { + + if (ev.code == SW_LID) { + log_struct(LOG_INFO, + LOG_MESSAGE("Lid opened."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), + NULL); + + b->lid_closed = false; + b->check_event_source = sd_event_source_unref(b->check_event_source); + + } else if (ev.code == SW_DOCK) { + log_struct(LOG_INFO, + LOG_MESSAGE("System undocked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), + NULL); + + b->docked = false; + } + } + + return 0; +} + +int button_open(Button *b) { + char *p, name[256]; + int r; + + assert(b); + + b->fd = safe_close(b->fd); + + p = strjoina("/dev/input/", b->name); + + b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (b->fd < 0) + return log_warning_errno(errno, "Failed to open %s: %m", b->name); + + if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { + r = log_error_errno(errno, "Failed to get input name: %m"); + goto fail; + } + + r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); + if (r < 0) { + log_error_errno(r, "Failed to add button event: %m"); + goto fail; + } + + log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name); + + return 0; + +fail: + b->fd = safe_close(b->fd); + return r; +} + +int button_check_switches(Button *b) { + uint8_t switches[SW_MAX/8+1] = {}; + assert(b); + + if (b->fd < 0) + return -EINVAL; + + if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0) + return -errno; + + b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1; + b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1; + + if (b->lid_closed) + button_install_check_event_source(b); + + return 0; +} diff --git a/src/grp-login/systemd-logind/logind-button.h b/src/grp-login/systemd-logind/logind-button.h new file mode 100644 index 0000000000..f30cba2959 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-button.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Button Button; + +#include "logind.h" + +struct Button { + Manager *manager; + + sd_event_source *io_event_source; + sd_event_source *check_event_source; + + char *name; + char *seat; + int fd; + + bool lid_closed; + bool docked; +}; + +Button* button_new(Manager *m, const char *name); +void button_free(Button*b); +int button_open(Button *b); +int button_set_seat(Button *b, const char *sn); +int button_check_switches(Button *b); diff --git a/src/grp-login/systemd-logind/logind-core.c b/src/grp-login/systemd-logind/logind-core.c new file mode 100644 index 0000000000..b2ae7c2e1a --- /dev/null +++ b/src/grp-login/systemd-logind/logind-core.c @@ -0,0 +1,560 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/udev-util.h" + +#include "logind.h" + +int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) { + Device *d; + + assert(m); + assert(sysfs); + + d = hashmap_get(m->devices, sysfs); + if (d) + /* we support adding master-flags, but not removing them */ + d->master = d->master || master; + else { + d = device_new(m, sysfs, master); + if (!d) + return -ENOMEM; + } + + if (_device) + *_device = d; + + return 0; +} + +int manager_add_seat(Manager *m, const char *id, Seat **_seat) { + Seat *s; + + assert(m); + assert(id); + + s = hashmap_get(m->seats, id); + if (!s) { + s = seat_new(m, id); + if (!s) + return -ENOMEM; + } + + if (_seat) + *_seat = s; + + return 0; +} + +int manager_add_session(Manager *m, const char *id, Session **_session) { + Session *s; + + assert(m); + assert(id); + + s = hashmap_get(m->sessions, id); + if (!s) { + s = session_new(m, id); + if (!s) + return -ENOMEM; + } + + if (_session) + *_session = s; + + return 0; +} + +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { + User *u; + int r; + + assert(m); + assert(name); + + u = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!u) { + r = user_new(&u, m, uid, gid, name); + if (r < 0) + return r; + } + + if (_user) + *_user = u; + + return 0; +} + +int manager_add_user_by_name(Manager *m, const char *name, User **_user) { + uid_t uid; + gid_t gid; + int r; + + assert(m); + assert(name); + + r = get_user_creds(&name, &uid, &gid, NULL, NULL); + if (r < 0) + return r; + + return manager_add_user(m, uid, gid, name, _user); +} + +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { + struct passwd *p; + + assert(m); + + errno = 0; + p = getpwuid(uid); + if (!p) + return errno > 0 ? -errno : -ENOENT; + + return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); +} + +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { + Inhibitor *i; + + assert(m); + assert(id); + + i = hashmap_get(m->inhibitors, id); + if (i) { + if (_inhibitor) + *_inhibitor = i; + + return 0; + } + + i = inhibitor_new(m, id); + if (!i) + return -ENOMEM; + + if (_inhibitor) + *_inhibitor = i; + + return 0; +} + +int manager_add_button(Manager *m, const char *name, Button **_button) { + Button *b; + + assert(m); + assert(name); + + b = hashmap_get(m->buttons, name); + if (!b) { + b = button_new(m, name); + if (!b) + return -ENOMEM; + } + + if (_button) + *_button = b; + + return 0; +} + +int manager_process_seat_device(Manager *m, struct udev_device *d) { + Device *device; + int r; + + assert(m); + + if (streq_ptr(udev_device_get_action(d), "remove")) { + + device = hashmap_get(m->devices, udev_device_get_syspath(d)); + if (!device) + return 0; + + seat_add_to_gc_queue(device->seat); + device_free(device); + + } else { + const char *sn; + Seat *seat = NULL; + bool master; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + if (!seat_name_is_valid(sn)) { + log_warning("Device with invalid seat name %s found, ignoring.", sn); + return 0; + } + + seat = hashmap_get(m->seats, sn); + master = udev_device_has_tag(d, "master-of-seat"); + + /* Ignore non-master devices for unknown seats */ + if (!master && !seat) + return 0; + + r = manager_add_device(m, udev_device_get_syspath(d), master, &device); + if (r < 0) + return r; + + if (!seat) { + r = manager_add_seat(m, sn, &seat); + if (r < 0) { + if (!device->seat) + device_free(device); + + return r; + } + } + + device_attach(device, seat); + seat_start(seat); + } + + return 0; +} + +int manager_process_button_device(Manager *m, struct udev_device *d) { + Button *b; + + int r; + + assert(m); + + if (streq_ptr(udev_device_get_action(d), "remove")) { + + b = hashmap_get(m->buttons, udev_device_get_sysname(d)); + if (!b) + return 0; + + button_free(b); + + } else { + const char *sn; + + r = manager_add_button(m, udev_device_get_sysname(d), &b); + if (r < 0) + return r; + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + button_set_seat(b, sn); + button_open(b); + } + + return 0; +} + +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) { + _cleanup_free_ char *unit = NULL; + Session *s; + int r; + + assert(m); + + if (pid < 1) + return -EINVAL; + + r = cg_pid_get_unit(pid, &unit); + if (r < 0) + return 0; + + s = hashmap_get(m->session_units, unit); + if (!s) + return 0; + + if (session) + *session = s; + return 1; +} + +int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) { + _cleanup_free_ char *unit = NULL; + User *u; + int r; + + assert(m); + assert(user); + + if (pid < 1) + return -EINVAL; + + r = cg_pid_get_slice(pid, &unit); + if (r < 0) + return 0; + + u = hashmap_get(m->user_units, unit); + if (!u) + return 0; + + *user = u; + return 1; +} + +int manager_get_idle_hint(Manager *m, dual_timestamp *t) { + Session *s; + bool idle_hint; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + Iterator i; + + assert(m); + + idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL); + + HASHMAP_FOREACH(s, m->sessions, i) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(s, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic < ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +bool manager_shall_kill(Manager *m, const char *user) { + assert(m); + assert(user); + + if (!m->kill_exclude_users && streq(user, "root")) + return false; + + if (strv_contains(m->kill_exclude_users, user)) + return false; + + if (!strv_isempty(m->kill_only_users)) + return strv_contains(m->kill_only_users, user); + + return m->kill_user_processes; +} + +static int vt_is_busy(unsigned int vtnr) { + struct vt_stat vt_stat; + int r = 0; + _cleanup_close_ int fd; + + assert(vtnr >= 1); + + /* We explicitly open /dev/tty1 here instead of /dev/tty0. If + * we'd open the latter we'd open the foreground tty which + * hence would be unconditionally busy. By opening /dev/tty1 + * we avoid this. Since tty1 is special and needs to be an + * explicitly loaded getty or DM this is safe. */ + + fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) + r = -errno; + else + r = !!(vt_stat.v_state & (1 << vtnr)); + + return r; +} + +int manager_spawn_autovt(Manager *m, unsigned int vtnr) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)]; + int r; + + assert(m); + assert(vtnr >= 1); + + if (vtnr > m->n_autovts && + vtnr != m->reserve_vt) + return 0; + + if (vtnr != m->reserve_vt) { + /* If this is the reserved TTY, we'll start the getty + * on it in any case, but otherwise only if it is not + * busy. */ + + r = vt_is_busy(vtnr); + if (r < 0) + return r; + else if (r > 0) + return -EBUSY; + } + + snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr); + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + &error, + NULL, + "ss", name, "fail"); + if (r < 0) + log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); + + return r; +} + +static bool manager_is_docked(Manager *m) { + Iterator i; + Button *b; + + HASHMAP_FOREACH(b, m->buttons, i) + if (b->docked) + return true; + + return false; +} + +static int manager_count_external_displays(Manager *m) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + int r; + int n = 0; + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_subsystem(e, "drm"); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct udev_device *p; + const char *status, *enabled, *dash, *nn, *i; + bool external = false; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + p = udev_device_get_parent(d); + if (!p) + continue; + + /* If the parent shares the same subsystem as the + * device we are looking at then it is a connector, + * which is what we are interested in. */ + if (!streq_ptr(udev_device_get_subsystem(p), "drm")) + continue; + + nn = udev_device_get_sysname(d); + if (!nn) + continue; + + /* Ignore internal displays: the type is encoded in + * the sysfs name, as the second dash separated item + * (the first is the card name, the last the connector + * number). We implement a whitelist of external + * displays here, rather than a whitelist, to ensure + * we don't block suspends too eagerly. */ + dash = strchr(nn, '-'); + if (!dash) + continue; + + dash++; + FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" + "Composite-", "SVIDEO-", "Component-", + "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") { + + if (startswith(dash, i)) { + external = true; + break; + } + } + if (!external) + continue; + + /* Ignore ports that are not enabled */ + enabled = udev_device_get_sysattr_value(d, "enabled"); + if (!enabled) + continue; + if (!streq_ptr(enabled, "enabled")) + continue; + + /* We count any connector which is not explicitly + * "disconnected" as connected. */ + status = udev_device_get_sysattr_value(d, "status"); + if (!streq_ptr(status, "disconnected")) + n++; + } + + return n; +} + +bool manager_is_docked_or_external_displays(Manager *m) { + int n; + + /* If we are docked don't react to lid closing */ + if (manager_is_docked(m)) { + log_debug("System is docked."); + return true; + } + + /* If we have more than one display connected, + * assume that we are docked. */ + n = manager_count_external_displays(m); + if (n < 0) + log_warning_errno(n, "Display counting failed: %m"); + else if (n >= 1) { + log_debug("External (%i) displays connected.", n); + return true; + } + + return false; +} diff --git a/src/grp-login/systemd-logind/logind-dbus.c b/src/grp-login/systemd-logind/logind-dbus.c new file mode 100644 index 0000000000..282ba64a3d --- /dev/null +++ b/src/grp-login/systemd-logind/logind-dbus.c @@ -0,0 +1,3170 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/selinux-util.h" +#include "basic/special.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/efivars.h" +#include "shared/sleep-config.h" +#include "shared/udev-util.h" +#include "shared/utmp-wtmp.h" + +#include "logind.h" + +int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *session; + int r; + + assert(m); + assert(message); + assert(ret); + + if (isempty(name)) { + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + } + + session = hashmap_get(m->sessions, name); + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); + + *ret = session; + return 0; +} + +int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) { + User *user; + int r; + + assert(m); + assert(message); + assert(ret); + + if (uid == UID_INVALID) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + /* Note that we get the owner UID of the session, not the actual client UID here! */ + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r < 0) + return r; + } + + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid); + + *ret = user; + return 0; +} + +int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) { + Seat *seat; + int r; + + assert(m); + assert(message); + assert(ret); + + if (isempty(name)) { + Session *session; + + r = manager_get_session_from_creds(m, message, NULL, error, &session); + if (r < 0) + return r; + + seat = session->seat; + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); + } else { + seat = hashmap_get(m->seats, name); + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", name); + } + + *ret = seat; + return 0; +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "b", manager_get_idle_hint(m, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + + assert(bus); + assert(reply); + assert(m); + + manager_get_idle_hint(m, &t); + + return sd_bus_message_append(reply, "t", streq(property, "IdleSinceHint") ? t.realtime : t.monotonic); +} + +static int property_get_inhibited( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + InhibitWhat w; + + assert(bus); + assert(reply); + assert(m); + + w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY); + + return sd_bus_message_append(reply, "s", inhibit_what_to_string(w)); +} + +static int property_get_preparing( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool b; + + assert(bus); + assert(reply); + assert(m); + + if (streq(property, "PreparingForShutdown")) + b = !!(m->action_what & INHIBIT_SHUTDOWN); + else + b = !!(m->action_what & INHIBIT_SLEEP); + + return sd_bus_message_append(reply, "b", b); +} + +static int property_get_scheduled_shutdown( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(bus); + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'r', "st"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); + +static int property_get_docked( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); +} + +static int property_get_current_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); +} + +static int property_get_current_inhibitors( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); +} + +static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Session *session = NULL; + Manager *m = userdata; + pid_t pid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + r = manager_get_session_from_creds(m, message, NULL, error, &session); + if (r < 0) + return r; + } else { + r = manager_get_session_by_pid(m, pid, &session); + if (r < 0) + return r; + + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid); + } + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + User *user = NULL; + pid_t pid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); + if (r < 0) + return r; + } else { + r = manager_get_user_by_pid(m, pid, &user); + if (r < 0) + return r; + if (!user) + return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "PID "PID_FMT" does not belong to any known or logged in user", pid); + } + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + Seat *seat; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, name, error, &seat); + if (r < 0) + return r; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Session *session; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(susso)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(session, m->sessions, i) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(susso)", + session->id, + (uint32_t) session->user->uid, + session->user->name, + session->seat ? session->seat->id : "", + p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + User *user; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(uso)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(user, m->users, i) { + _cleanup_free_ char *p = NULL; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(uso)", + (uint32_t) user->uid, + user->name, + p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Seat *seat; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(seat, m->seats, i) { + _cleanup_free_ char *p = NULL; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", seat->id, p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Inhibitor *inhibitor; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssuu)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { + + r = sd_bus_message_append(reply, "(ssssuu)", + strempty(inhibit_what_to_string(inhibitor->what)), + strempty(inhibitor->who), + strempty(inhibitor->why), + strempty(inhibit_mode_to_string(inhibitor->mode)), + (uint32_t) inhibitor->uid, + (uint32_t) inhibitor->pid); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; + uint32_t audit_id = 0; + _cleanup_free_ char *id = NULL; + Session *session = NULL; + Manager *m = userdata; + User *user = NULL; + Seat *seat = NULL; + pid_t leader; + uid_t uid; + int remote; + uint32_t vtnr = 0; + SessionType t; + SessionClass c; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); + if (r < 0) + return r; + + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); + if (leader < 0 || leader == 1) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); + + if (isempty(type)) + t = _SESSION_TYPE_INVALID; + else { + t = session_type_from_string(type); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type); + } + + if (isempty(class)) + c = _SESSION_CLASS_INVALID; + else { + c = session_class_from_string(class); + if (c < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class); + } + + if (isempty(desktop)) + desktop = NULL; + else { + if (!string_is_safe(desktop)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop); + } + + if (isempty(cseat)) + seat = NULL; + else { + seat = hashmap_get(m->seats, cseat); + if (!seat) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat); + } + + if (tty_is_vc(tty)) { + int v; + + if (!seat) + seat = m->seat0; + else if (seat != m->seat0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); + + v = vtnr_from_tty(tty); + if (v <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty); + + if (!vtnr) + vtnr = (uint32_t) v; + else if (vtnr != (uint32_t) v) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match"); + + } else if (tty_is_console(tty)) { + + if (!seat) + seat = m->seat0; + else if (seat != m->seat0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0"); + + if (vtnr != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0"); + } + + if (seat) { + if (seat_has_vts(seat)) { + if (!vtnr || vtnr > 63) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range"); + } else { + if (vtnr != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0"); + } + } + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + return r; + + if (t == _SESSION_TYPE_INVALID) { + if (!isempty(display)) + t = SESSION_X11; + else if (!isempty(tty)) + t = SESSION_TTY; + else + t = SESSION_UNSPECIFIED; + } + + if (c == _SESSION_CLASS_INVALID) { + if (t == SESSION_UNSPECIFIED) + c = SESSION_BACKGROUND; + else + c = SESSION_USER; + } + + if (leader == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); + if (r < 0) + return r; + } + + r = manager_get_session_by_pid(m, leader, NULL); + if (r > 0) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session"); + + /* + * Old gdm and lightdm start the user-session on the same VT as + * the greeter session. But they destroy the greeter session + * after the user-session and want the user-session to take + * over the VT. We need to support this for + * backwards-compatibility, so make sure we allow new sessions + * on a VT that a greeter is running on. Furthermore, to allow + * re-logins, we have to allow a greeter to take over a used VT for + * the exact same reasons. + */ + if (c != SESSION_GREETER && + vtnr > 0 && + vtnr < m->seat0->position_count && + m->seat0->positions[vtnr] && + m->seat0->positions[vtnr]->class != SESSION_GREETER) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); + + if (hashmap_size(m->sessions) >= m->sessions_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); + + audit_session_from_pid(leader, &audit_id); + if (audit_id > 0) { + /* Keep our session IDs and the audit session IDs in sync */ + + if (asprintf(&id, "%"PRIu32, audit_id) < 0) + return -ENOMEM; + + /* Wut? There's already a session by this name and we + * didn't find it above? Weird, then let's not trust + * the audit data and let's better register a new + * ID */ + if (hashmap_get(m->sessions, id)) { + log_warning("Existing logind session ID %s used by new audit session, ignoring", id); + audit_id = 0; + + id = mfree(id); + } + } + + if (!id) { + do { + id = mfree(id); + + if (asprintf(&id, "c%lu", ++m->session_counter) < 0) + return -ENOMEM; + + } while (hashmap_get(m->sessions, id)); + } + + r = manager_add_user_by_uid(m, uid, &user); + if (r < 0) + goto fail; + + r = manager_add_session(m, id, &session); + if (r < 0) + goto fail; + + session_set_user(session, user); + + session->leader = leader; + session->audit_id = audit_id; + session->type = t; + session->class = c; + session->remote = remote; + session->vtnr = vtnr; + + if (!isempty(tty)) { + session->tty = strdup(tty); + if (!session->tty) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(display)) { + session->display = strdup(display); + if (!session->display) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(remote_user)) { + session->remote_user = strdup(remote_user); + if (!session->remote_user) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(remote_host)) { + session->remote_host = strdup(remote_host); + if (!session->remote_host) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(service)) { + session->service = strdup(service); + if (!session->service) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(desktop)) { + session->desktop = strdup(desktop); + if (!session->desktop) { + r = -ENOMEM; + goto fail; + } + } + + if (seat) { + r = seat_attach_session(seat, session); + if (r < 0) + goto fail; + } + + r = session_start(session); + if (r < 0) + goto fail; + + session->create_message = sd_bus_message_ref(message); + + /* Now, let's wait until the slice unit and stuff got + * created. We send the reply back from + * session_send_create_reply(). */ + + return 1; + +fail: + if (session) + session_add_to_gc_queue(session); + + if (user) + user_add_to_gc_queue(user); + + return r; +} + +static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + r = session_release(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_activate(message, session, error); +} + +static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *session_name, *seat_name; + Manager *m = userdata; + Session *session; + Seat *seat; + int r; + + assert(message); + assert(m); + + /* Same as ActivateSession() but refuses to work if + * the seat doesn't match */ + + r = sd_bus_message_read(message, "ss", &session_name, &seat_name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, session_name, error, &session); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, seat_name, error, &seat); + if (r < 0) + return r; + + if (session->seat != seat) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name); + + r = session_activate(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_lock(message, session, error); +} + +static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.lock-sessions", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions")); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *name; + Manager *m = userdata; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_kill(message, session, error); +} + +static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + return bus_user_method_kill(message, user, error); +} + +static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_session_from_creds(m, message, name, error, &session); + if (r < 0) + return r; + + return bus_session_method_terminate(message, session, error); +} + +static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t uid; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + + r = manager_get_user_from_creds(m, message, uid, error, &user); + if (r < 0) + return r; + + return bus_user_method_terminate(message, user, error); +} + +static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Seat *seat; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_get_seat_from_creds(m, message, name, error, &seat); + if (r < 0) + return r; + + return bus_seat_method_terminate(message, seat, error); +} + +static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *cc = NULL; + Manager *m = userdata; + int r, b, interactive; + struct passwd *pw; + const char *path; + uint32_t uid; + bool self = false; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ubb", &uid, &b, &interactive); + if (r < 0) + return r; + + if (uid == UID_INVALID) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + /* Note that we get the owner UID of the session, not the actual client UID here! */ + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r < 0) + return r; + + self = true; + + } else if (!uid_is_valid(uid)) + return -EINVAL; + + errno = 0; + pw = getpwuid(uid); + if (!pw) + return errno > 0 ? -errno : -ENOENT; + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + mkdir_p_label("/var/lib/systemd", 0755); + + r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0); + if (r < 0) + return r; + + cc = cescape(pw->pw_name); + if (!cc) + return -ENOMEM; + + path = strjoina("/var/lib/systemd/linger/", cc); + if (b) { + User *u; + + r = touch(path); + if (r < 0) + return r; + + if (manager_add_user_by_uid(m, uid, &u) >= 0) + user_start(u); + + } else { + User *u; + + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + u = hashmap_get(m->users, UID_TO_PTR(uid)); + if (u) + user_add_to_gc_queue(u); + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int trigger_device(Manager *m, struct udev_device *d) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *first, *item; + int r; + + assert(m); + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + if (d) { + r = udev_enumerate_add_match_parent(e, d); + if (r < 0) + return r; + } + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_free_ char *t = NULL; + const char *p; + + p = udev_list_entry_get_name(item); + + t = strappend(p, "/uevent"); + if (!t) + return -ENOMEM; + + write_string_file(t, "change", WRITE_STRING_FILE_CREATE); + } + + return 0; +} + +static int attach_device(Manager *m, const char *seat, const char *sysfs) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_free_ char *rule = NULL, *file = NULL; + const char *id_for_seat; + int r; + + assert(m); + assert(seat); + assert(sysfs); + + d = udev_device_new_from_syspath(m->udev, sysfs); + if (!d) + return -ENODEV; + + if (!udev_device_has_tag(d, "seat")) + return -ENODEV; + + id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT"); + if (!id_for_seat) + return -ENODEV; + + if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) + return -ENOMEM; + + if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) + return -ENOMEM; + + mkdir_p_label("/etc/udev/rules.d", 0755); + r = write_string_file_atomic_label(file, rule); + if (r < 0) + return r; + + return trigger_device(m, d); +} + +static int flush_devices(Manager *m) { + _cleanup_closedir_ DIR *d; + + assert(m); + + d = opendir("/etc/udev/rules.d"); + if (!d) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open /etc/udev/rules.d: %m"); + } else { + struct dirent *de; + + while ((de = readdir(d))) { + + if (!dirent_is_file(de)) + continue; + + if (!startswith(de->d_name, "72-seat-")) + continue; + + if (!endswith(de->d_name, ".rules")) + continue; + + if (unlinkat(dirfd(d), de->d_name, 0) < 0) + log_warning_errno(errno, "Failed to unlink %s: %m", de->d_name); + } + } + + return trigger_device(m, NULL); +} + +static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *sysfs, *seat; + Manager *m = userdata; + int interactive, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ssb", &seat, &sysfs, &interactive); + if (r < 0) + return r; + + if (!path_startswith(sysfs, "/sys")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs); + + if (!seat_name_is_valid(seat)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.attach-device", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = attach_device(m, seat, sysfs); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int interactive, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &interactive); + if (r < 0) + return r; + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.flush-devices", + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = flush_devices(m); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int have_multiple_sessions( + Manager *m, + uid_t uid) { + + Session *session; + Iterator i; + + assert(m); + + /* Check for other users' sessions. Greeter sessions do not + * count, and non-login sessions do not count either. */ + HASHMAP_FOREACH(session, m->sessions, i) + if (session->class == SESSION_USER && + session->user->uid != uid) + return true; + + return false; +} + +static int bus_manager_log_shutdown( + Manager *m, + InhibitWhat w, + const char *unit_name) { + + const char *p, *q; + + assert(m); + assert(unit_name); + + if (w != INHIBIT_SHUTDOWN) + return 0; + + if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { + p = "MESSAGE=System is powering down"; + q = "SHUTDOWN=power-off"; + } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { + p = "MESSAGE=System is halting"; + q = "SHUTDOWN=halt"; + } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { + p = "MESSAGE=System is rebooting"; + q = "SHUTDOWN=reboot"; + } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { + p = "MESSAGE=System is rebooting with kexec"; + q = "SHUTDOWN=kexec"; + } else { + p = "MESSAGE=System is shutting down"; + q = NULL; + } + + if (isempty(m->wall_message)) + p = strjoina(p, "."); + else + p = strjoina(p, " (", m->wall_message, ")."); + + return log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), + p, + q, + NULL); +} + +static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->lid_switch_ignore_event_source = sd_event_source_unref(m->lid_switch_ignore_event_source); + return 0; +} + +int manager_set_lid_switch_ignore(Manager *m, usec_t until) { + int r; + + assert(m); + + if (until <= now(CLOCK_MONOTONIC)) + return 0; + + /* We want to ignore the lid switch for a while after each + * suspend, and after boot-up. Hence let's install a timer for + * this. As long as the event source exists we ignore the lid + * switch. */ + + if (m->lid_switch_ignore_event_source) { + usec_t u; + + r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u); + if (r < 0) + return r; + + if (until <= u) + return 0; + + r = sd_event_source_set_time(m->lid_switch_ignore_event_source, until); + } else + r = sd_event_add_time( + m->event, + &m->lid_switch_ignore_event_source, + CLOCK_MONOTONIC, + until, 0, + lid_switch_ignore_handler, m); + + return r; +} + +static void reset_scheduled_shutdown(Manager *m) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); + m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); + m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); + m->scheduled_shutdown_timeout = 0; + m->shutdown_dry_run = false; + + if (m->unlink_nologin) { + (void) unlink("/run/nologin"); + m->unlink_nologin = false; + } +} + +static int execute_shutdown_or_sleep( + Manager *m, + InhibitWhat w, + const char *unit_name, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + char *c = NULL; + const char *p; + int r; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(unit_name); + + bus_manager_log_shutdown(m, w, unit_name); + + if (m->shutdown_dry_run) { + log_info("Running in dry run, suppressing action."); + reset_scheduled_shutdown(m); + } else { + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, + &reply, + "ss", unit_name, "replace-irreversibly"); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "o", &p); + if (r < 0) + return r; + + c = strdup(p); + if (!c) + return -ENOMEM; + } + + m->action_unit = unit_name; + free(m->action_job); + m->action_job = c; + m->action_what = w; + + /* Make sure the lid switch is ignored for a while */ + manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); + + return 0; +} + +int manager_dispatch_delayed(Manager *manager, bool timeout) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Inhibitor *offending = NULL; + int r; + + assert(manager); + + if (manager->action_what == 0 || manager->action_job) + return 0; + + if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { + _cleanup_free_ char *comm = NULL, *u = NULL; + + if (!timeout) + return 0; + + (void) get_process_comm(offending->pid, &comm); + u = uid_to_name(offending->uid); + + log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.", + offending->uid, strna(u), + offending->pid, strna(comm)); + } + + /* Actually do the operation */ + r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error); + if (r < 0) { + log_warning("Failed to send delayed message: %s", bus_error_message(&error, r)); + + manager->action_unit = NULL; + manager->action_what = 0; + return r; + } + + return 1; +} + +static int manager_inhibit_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *manager = userdata; + int r; + + assert(manager); + assert(manager->inhibit_timeout_source == s); + + r = manager_dispatch_delayed(manager, true); + return (r < 0) ? r : 0; +} + +static int delay_shutdown_or_sleep( + Manager *m, + InhibitWhat w, + const char *unit_name) { + + int r; + usec_t timeout_val; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(unit_name); + + timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max; + + if (m->inhibit_timeout_source) { + r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC, + timeout_val, 0, manager_inhibit_timeout_handler, m); + if (r < 0) + return r; + } + + m->action_unit = unit_name; + m->action_what = w; + + return 0; +} + +static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { + + static const char * const signal_name[_INHIBIT_WHAT_MAX] = { + [INHIBIT_SHUTDOWN] = "PrepareForShutdown", + [INHIBIT_SLEEP] = "PrepareForSleep" + }; + + int active = _active; + + assert(m); + assert(w >= 0); + assert(w < _INHIBIT_WHAT_MAX); + assert(signal_name[w]); + + return sd_bus_emit_signal(m->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + signal_name[w], + "b", + active); +} + +int bus_manager_shutdown_or_sleep_now_or_later( + Manager *m, + const char *unit_name, + InhibitWhat w, + sd_bus_error *error) { + + bool delayed; + int r; + + assert(m); + assert(unit_name); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + assert(!m->action_job); + + /* Tell everybody to prepare for shutdown/sleep */ + send_prepare_for(m, w, true); + + delayed = + m->inhibit_delay_max > 0 && + manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL); + + if (delayed) + /* Shutdown is delayed, keep in mind what we + * want to do, and start a timeout */ + r = delay_shutdown_or_sleep(m, w, unit_name); + else + /* Shutdown is not delayed, execute it + * immediately */ + r = execute_shutdown_or_sleep(m, w, unit_name, error); + + return r; +} + +static int verify_shutdown_creds( + Manager *m, + sd_bus_message *message, + InhibitWhat w, + bool interactive, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + bool multiple_sessions, blocked; + uid_t uid; + int r; + + assert(m); + assert(message); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = have_multiple_sessions(m, uid); + if (r < 0) + return r; + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + + if (multiple_sessions && action_multiple_sessions) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (blocked && action_ignore_inhibit) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + if (!multiple_sessions && !blocked && action) { + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + return 0; +} + +static int method_do_shutdown_or_sleep( + Manager *m, + sd_bus_message *message, + const char *unit_name, + InhibitWhat w, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, + sd_bus_error *error) { + + int interactive, r; + + assert(m); + assert(message); + assert(unit_name); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + + r = sd_bus_message_read(message, "b", &interactive); + if (r < 0) + return r; + + /* Don't allow multiple jobs being executed at the same time */ + if (m->action_what) + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); + + if (sleep_verb) { + r = can_sleep(sleep_verb); + if (r < 0) + return r; + + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); + } + + r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, + action_ignore_inhibit, error); + if (r != 0) + return r; + + r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_POWEROFF_TARGET, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.power-off", + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.power-off-ignore-inhibit", + NULL, + error); +} + +static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_REBOOT_TARGET, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.reboot", + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.reboot-ignore-inhibit", + NULL, + error); +} + +static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_SUSPEND_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.suspend", + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", + error); +} + +static int nologin_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *m = userdata; + int r; + + log_info("Creating /run/nologin, blocking further logins..."); + + r = write_string_file_atomic_label("/run/nologin", "System is going down."); + if (r < 0) + log_error_errno(r, "Failed to create /run/nologin: %m"); + else + m->unlink_nologin = true; + + return 0; +} + +static int update_schedule_file(Manager *m) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + + r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); + + r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); + if (r < 0) + return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); + + (void) fchmod(fileno(f), 0644); + + fprintf(f, + "USEC="USEC_FMT"\n" + "WARN_WALL=%i\n" + "MODE=%s\n", + m->scheduled_shutdown_timeout, + m->enable_wall_messages, + m->scheduled_shutdown_type); + + if (!isempty(m->wall_message)) { + _cleanup_free_ char *t; + + t = cescape(m->wall_message); + if (!t) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WALL_MESSAGE=%s\n", t); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(temp_path); + (void) unlink("/run/systemd/shutdown/scheduled"); + + return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); +} + +static int manager_scheduled_shutdown_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Manager *m = userdata; + const char *target; + int r; + + assert(m); + + if (isempty(m->scheduled_shutdown_type)) + return 0; + + if (streq(m->scheduled_shutdown_type, "halt")) + target = SPECIAL_HALT_TARGET; + else if (streq(m->scheduled_shutdown_type, "poweroff")) + target = SPECIAL_POWEROFF_TARGET; + else + target = SPECIAL_REBOOT_TARGET; + + r = execute_shutdown_or_sleep(m, 0, target, &error); + if (r < 0) + return log_error_errno(r, "Unable to execute transition to %s: %m", target); + + return 0; +} + +static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *action_multiple_sessions = NULL; + const char *action_ignore_inhibit = NULL; + const char *action = NULL; + uint64_t elapse; + char *type; + int r; + + assert(m); + assert(message); + + r = sd_bus_message_read(message, "st", &type, &elapse); + if (r < 0) + return r; + + if (startswith(type, "dry-")) { + type += 4; + m->shutdown_dry_run = true; + } + + if (streq(type, "reboot")) { + action = "org.freedesktop.login1.reboot"; + action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; + } else if (streq(type, "halt")) { + action = "org.freedesktop.login1.halt"; + action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; + } else if (streq(type, "poweroff")) { + action = "org.freedesktop.login1.power-off"; + action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; + action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; + } else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); + + r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false, + action, action_multiple_sessions, action_ignore_inhibit, error); + if (r != 0) + return r; + + if (m->scheduled_shutdown_timeout_source) { + r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, + CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed: %m"); + } + + r = free_and_strdup(&m->scheduled_shutdown_type, type); + if (r < 0) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + return log_oom(); + } + + if (m->nologin_timeout_source) { + r = sd_event_source_set_time(m->nologin_timeout_source, elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed: %m"); + + r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); + } else { + r = sd_event_add_time(m->event, &m->nologin_timeout_source, + CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed: %m"); + } + + m->scheduled_shutdown_timeout = elapse; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); + if (r >= 0) { + const char *tty = NULL; + + (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); + (void) sd_bus_creds_get_tty(creds, &tty); + + r = free_and_strdup(&m->scheduled_shutdown_tty, tty); + if (r < 0) { + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + return log_oom(); + } + } + + r = manager_setup_wall_message_timer(m); + if (r < 0) + return r; + + if (!isempty(type)) { + r = update_schedule_file(m); + if (r < 0) + return r; + } else + (void) unlink("/run/systemd/shutdown/scheduled"); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + bool cancelled; + + assert(m); + assert(message); + + cancelled = m->scheduled_shutdown_type != NULL; + reset_scheduled_shutdown(m); + + if (cancelled) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *tty = NULL; + uid_t uid = 0; + int r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); + if (r >= 0) { + (void) sd_bus_creds_get_uid(creds, &uid); + (void) sd_bus_creds_get_tty(creds, &tty); + } + + utmp_wall("The system shutdown has been cancelled", + uid_to_name(uid), tty, logind_wall_tty_filter, m); + } + + return sd_bus_reply_method_return(message, "b", cancelled); +} + +static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_HIBERNATE_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", + error); +} + +static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + SPECIAL_HYBRID_SLEEP_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", + error); +} + +static int method_can_shutdown_or_sleep( + Manager *m, + sd_bus_message *message, + InhibitWhat w, + const char *action, + const char *action_multiple_sessions, + const char *action_ignore_inhibit, + const char *sleep_verb, + sd_bus_error *error) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + bool multiple_sessions, challenge, blocked; + const char *result = NULL; + uid_t uid; + int r; + + assert(m); + assert(message); + assert(w >= 0); + assert(w <= _INHIBIT_WHAT_MAX); + assert(action); + assert(action_multiple_sessions); + assert(action_ignore_inhibit); + + if (sleep_verb) { + r = can_sleep(sleep_verb); + if (r < 0) + return r; + if (r == 0) + return sd_bus_reply_method_return(message, "s", "na"); + } + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = have_multiple_sessions(m, uid); + if (r < 0) + return r; + + multiple_sessions = r > 0; + blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + + if (multiple_sessions) { + r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + } + + if (blocked) { + r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0 && !result) + result = "yes"; + else if (challenge && (!result || streq(result, "yes"))) + result = "challenge"; + else + result = "no"; + } + + if (!multiple_sessions && !blocked) { + /* If neither inhibit nor multiple sessions + * apply then just check the normal policy */ + + r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + } + + return sd_bus_reply_method_return(message, "s", result); +} + +static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.power-off", + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.power-off-ignore-inhibit", + NULL, + error); +} + +static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SHUTDOWN, + "org.freedesktop.login1.reboot", + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.reboot-ignore-inhibit", + NULL, + error); +} + +static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.suspend", + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.suspend-ignore-inhibit", + "suspend", + error); +} + +static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hibernate", + error); +} + +static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep( + m, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "hybrid-sleep", + error); +} + +static int property_get_reboot_to_firmware_setup( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = efi_get_reboot_to_firmware(); + if (r < 0 && r != -EOPNOTSUPP) + return r; + + return sd_bus_message_append(reply, "b", r > 0); +} + +static int method_set_reboot_to_firmware_setup( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int b, r; + Manager *m = userdata; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = efi_set_reboot_to_firmware(b); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_firmware_setup( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + bool challenge; + const char *result; + Manager *m = userdata; + + assert(message); + assert(m); + + r = efi_reboot_to_firmware_supported(); + if (r == -EOPNOTSUPP) + return sd_bus_reply_method_return(message, "s", "na"); + else if (r < 0) + return r; + + r = bus_test_polkit(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + +static int method_set_wall_message( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + Manager *m = userdata; + char *wall_message; + int enable_wall_messages; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-wall-message", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + if (isempty(wall_message)) + m->wall_message = mfree(m->wall_message); + else { + r = free_and_strdup(&m->wall_message, wall_message); + if (r < 0) + return log_oom(); + } + + m->enable_wall_messages = enable_wall_messages; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *who, *why, *what, *mode; + _cleanup_free_ char *id = NULL; + _cleanup_close_ int fifo_fd = -1; + Manager *m = userdata; + Inhibitor *i = NULL; + InhibitMode mm; + InhibitWhat w; + pid_t pid; + uid_t uid; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ssss", &what, &who, &why, &mode); + if (r < 0) + return r; + + w = inhibit_what_from_string(what); + if (w <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what); + + mm = inhibit_mode_from_string(mode); + if (mm < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode); + + /* Delay is only supported for shutdown/sleep */ + if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep"); + + /* Don't allow taking delay locks while we are already + * executing the operation. We shouldn't create the impression + * that the lock was successful if the machine is about to go + * down/suspend any moment. */ + if (m->action_what & w) + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_BOOT, + w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : + w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : + w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : + w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : + w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : + w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : + "org.freedesktop.login1.inhibit-handle-lid-switch", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + if (hashmap_size(m->inhibitors) >= m->inhibitors_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + + do { + id = mfree(id); + + if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) + return -ENOMEM; + + } while (hashmap_get(m->inhibitors, id)); + + r = manager_add_inhibitor(m, id, &i); + if (r < 0) + return r; + + i->what = w; + i->mode = mm; + i->pid = pid; + i->uid = uid; + i->why = strdup(why); + i->who = strdup(who); + + if (!i->why || !i->who) { + r = -ENOMEM; + goto fail; + } + + fifo_fd = inhibitor_create_fifo(i); + if (fifo_fd < 0) { + r = fifo_fd; + goto fail; + } + + inhibitor_start(i); + + return sd_bus_reply_method_return(message, "h", fifo_fd); + +fail: + if (i) + inhibitor_free(i); + + return r; +} + +const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), + SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), + + SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0), + SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), + SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), + SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), + SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), + SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), + SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), + SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), + SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("SessionNew", "so", 0), + SD_BUS_SIGNAL("SessionRemoved", "so", 0), + SD_BUS_SIGNAL("UserNew", "uo", 0), + SD_BUS_SIGNAL("UserRemoved", "uo", 0), + SD_BUS_SIGNAL("SeatNew", "so", 0), + SD_BUS_SIGNAL("SeatRemoved", "so", 0), + SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), + SD_BUS_SIGNAL("PrepareForSleep", "b", 0), + + SD_BUS_VTABLE_END +}; + +static int session_jobs_reply(Session *s, const char *unit, const char *result) { + int r = 0; + + assert(s); + assert(unit); + + if (!s->started) + return r; + + if (streq(result, "done")) + r = session_send_create_reply(s, NULL); + else { + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + + sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); + r = session_send_create_reply(s, &e); + } + + return r; +} + +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *result, *unit; + Manager *m = userdata; + Session *session; + uint32_t id; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (m->action_job && streq(m->action_job, path)) { + log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); + + /* Tell people that they now may take a lock again */ + send_prepare_for(m, m->action_what, false); + + m->action_job = mfree(m->action_job); + m->action_unit = NULL; + m->action_what = 0; + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session && streq_ptr(path, session->scope_job)) { + session->scope_job = mfree(session->scope_job); + session_jobs_reply(session, unit, result); + + session_save(session); + user_save(session->user); + session_add_to_gc_queue(session); + } + + user = hashmap_get(m->user_units, unit); + if (user && + (streq_ptr(path, user->service_job) || + streq_ptr(path, user->slice_job))) { + + if (streq_ptr(path, user->service_job)) + user->service_job = mfree(user->service_job); + + if (streq_ptr(path, user->slice_job)) + user->slice_job = mfree(user->slice_job); + + LIST_FOREACH(sessions_by_user, session, user->sessions) + session_jobs_reply(session, unit, result); + + user_save(user); + user_add_to_gc_queue(user); + } + + return 0; +} + +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *unit; + Manager *m = userdata; + Session *session; + User *user; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "so", &unit, &path); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session) + session_add_to_gc_queue(session); + + user = hashmap_get(m->user_units, unit); + if (user) + user_add_to_gc_queue(user); + + return 0; +} + +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *unit = NULL; + Manager *m = userdata; + const char *path; + Session *session; + User *user; + int r; + + assert(message); + assert(m); + + path = sd_bus_message_get_path(message); + if (!path) + return 0; + + r = unit_name_from_dbus_path(path, &unit); + if (r == -EINVAL) /* not a unit */ + return 0; + if (r < 0) { + log_oom(); + return 0; + } + + session = hashmap_get(m->session_units, unit); + if (session) + session_add_to_gc_queue(session); + + user = hashmap_get(m->user_units, unit); + if (user) + user_add_to_gc_queue(user); + + return 0; +} + +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + Iterator i; + int b, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (b) + return 0; + + /* systemd finished reloading, let's recheck all our sessions */ + log_debug("System manager has been reloaded, rechecking sessions..."); + + HASHMAP_FOREACH(session, m->sessions, i) + session_add_to_gc_queue(session); + + return 0; +} + +int manager_send_changed(Manager *manager, const char *property, ...) { + char **l; + + assert(manager); + + l = strv_from_stdarg_alloca(property); + + return sd_bus_emit_properties_changed_strv( + manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + l); +} + +static int strdup_job(sd_bus_message *reply, char **job) { + const char *j; + char *copy; + int r; + + r = sd_bus_message_read(reply, "o", &j); + if (r < 0) + return r; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + return 1; +} + +int manager_start_slice( + Manager *manager, + const char *slice, + const char *description, + const char *after, + const char *after2, + uint64_t tasks_max, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(slice); + assert(job); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_start_scope( + Manager *manager, + const char *scope, + pid_t pid, + const char *slice, + const char *description, + const char *after, + const char *after2, + uint64_t tasks_max, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(scope); + assert(pid > 1); + assert(job); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(slice)) { + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return r; + } + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + /* cgroup empty notification is not available in containers + * currently. To make this less problematic, let's shorten the + * stop timeout for sessions, so that we don't wait + * forever. */ + + /* Make sure that the session shells are terminated with + * SIGHUP since bash and friends tend to ignore SIGTERM */ + r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(unit); + assert(job); + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, + &reply, + "ss", unit, "replace"); + if (r < 0) + return r; + + return strdup_job(reply, job); +} + +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(unit); + assert(job); + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + error, + &reply, + "ss", unit, "fail"); + if (r < 0) { + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { + + *job = NULL; + sd_bus_error_free(error); + return 0; + } + + return r; + } + + return strdup_job(reply, job); +} + +int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + int r; + + assert(manager); + assert(scope); + + path = unit_dbus_path_from_name(scope); + if (!path) + return -ENOMEM; + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Scope", + "Abandon", + error, + NULL, + NULL); + if (r < 0) { + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) || + sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) { + sd_bus_error_free(error); + return 0; + } + + return r; + } + + return 1; +} + +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) { + assert(manager); + assert(unit); + + return sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + error, + NULL, + "ssi", unit, who == KILL_LEADER ? "main" : "all", signo); +} + +int manager_unit_is_active(Manager *manager, const char *unit) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *path = NULL; + const char *state; + int r; + + assert(manager); + assert(unit); + + path = unit_dbus_path_from_name(unit); + if (!path) + return -ENOMEM; + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &reply, + "s"); + if (r < 0) { + /* systemd might have droppped off momentarily, let's + * not make this an error */ + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + /* If the unit is already unloaded then it's not + * active */ + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) + return false; + + return r; + } + + r = sd_bus_message_read(reply, "s", &state); + if (r < 0) + return -EINVAL; + + return !streq(state, "inactive") && !streq(state, "failed"); +} + +int manager_job_is_active(Manager *manager, const char *path) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(path); + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Job", + "State", + &error, + &reply, + "s"); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) + return false; + + return r; + } + + /* We don't actually care about the state really. The fact + * that we could read the job state is enough for us */ + + return true; +} diff --git a/src/grp-login/systemd-logind/logind-device.c b/src/grp-login/systemd-logind/logind-device.c new file mode 100644 index 0000000000..6165a9ea6e --- /dev/null +++ b/src/grp-login/systemd-logind/logind-device.c @@ -0,0 +1,125 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/util.h" + +#include "logind-device.h" + +Device* device_new(Manager *m, const char *sysfs, bool master) { + Device *d; + + assert(m); + assert(sysfs); + + d = new0(Device, 1); + if (!d) + return NULL; + + d->sysfs = strdup(sysfs); + if (!d->sysfs) { + free(d); + return NULL; + } + + if (hashmap_put(m->devices, d->sysfs, d) < 0) { + free(d->sysfs); + free(d); + return NULL; + } + + d->manager = m; + d->master = master; + dual_timestamp_get(&d->timestamp); + + return d; +} + +static void device_detach(Device *d) { + Seat *s; + SessionDevice *sd; + + assert(d); + + if (!d->seat) + return; + + while ((sd = d->session_devices)) + session_device_free(sd); + + s = d->seat; + LIST_REMOVE(devices, d->seat->devices, d); + d->seat = NULL; + + if (!seat_has_master_device(s)) { + seat_add_to_gc_queue(s); + seat_send_changed(s, "CanGraphical", NULL); + } +} + +void device_free(Device *d) { + assert(d); + + device_detach(d); + + hashmap_remove(d->manager->devices, d->sysfs); + + free(d->sysfs); + free(d); +} + +void device_attach(Device *d, Seat *s) { + Device *i; + bool had_master; + + assert(d); + assert(s); + + if (d->seat == s) + return; + + if (d->seat) + device_detach(d); + + d->seat = s; + had_master = seat_has_master_device(s); + + /* We keep the device list sorted by the "master" flag. That is, master + * devices are at the front, other devices at the tail. As there is no + * way to easily add devices at the list-tail, we need to iterate the + * list to find the first non-master device when adding non-master + * devices. We assume there is only a few (normally 1) master devices + * per seat, so we iterate only a few times. */ + + if (d->master || !s->devices) + LIST_PREPEND(devices, s->devices, d); + else { + LIST_FOREACH(devices, i, s->devices) { + if (!i->devices_next || !i->master) { + LIST_INSERT_AFTER(devices, s->devices, i, d); + break; + } + } + } + + if (!had_master && d->master) + seat_send_changed(s, "CanGraphical", NULL); +} diff --git a/src/grp-login/systemd-logind/logind-device.h b/src/grp-login/systemd-logind/logind-device.h new file mode 100644 index 0000000000..9e2678d801 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-device.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct Device Device; + +#include "logind-seat.h" +#include "logind-session-device.h" + +struct Device { + Manager *manager; + + char *sysfs; + Seat *seat; + bool master; + + dual_timestamp timestamp; + + LIST_FIELDS(struct Device, devices); + LIST_HEAD(SessionDevice, session_devices); +}; + +Device* device_new(Manager *m, const char *sysfs, bool master); +void device_free(Device *d); +void device_attach(Device *d, Seat *s); diff --git a/src/grp-login/systemd-logind/logind-gperf.gperf b/src/grp-login/systemd-logind/logind-gperf.gperf new file mode 100644 index 0000000000..1a39c3bf96 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-gperf.gperf @@ -0,0 +1,41 @@ +%{ +#include + +#include "shared/conf-parser.h" + +#include "logind.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name logind_gperf_hash +%define lookup-function-name logind_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Login.NAutoVTs, config_parse_unsigned, 0, offsetof(Manager, n_autovts) +Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt) +Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes) +Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users) +Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) +Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) +Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) +Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) +Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) +Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) +Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked) +Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited) +Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited) +Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited) +Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) +Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec) +Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) +Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) +Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) +Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) +Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) +Login.UserTasksMax, config_parse_user_tasks_max,0, offsetof(Manager, user_tasks_max) diff --git a/src/grp-login/systemd-logind/logind-inhibit.c b/src/grp-login/systemd-logind/logind-inhibit.c new file mode 100644 index 0000000000..8a35db0dc8 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-inhibit.c @@ -0,0 +1,486 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "logind-inhibit.h" + +Inhibitor* inhibitor_new(Manager *m, const char* id) { + Inhibitor *i; + + assert(m); + + i = new0(Inhibitor, 1); + if (!i) + return NULL; + + i->state_file = strappend("/run/systemd/inhibit/", id); + if (!i->state_file) { + free(i); + return NULL; + } + + i->id = basename(i->state_file); + + if (hashmap_put(m->inhibitors, i->id, i) < 0) { + free(i->state_file); + free(i); + return NULL; + } + + i->manager = m; + i->fifo_fd = -1; + + return i; +} + +void inhibitor_free(Inhibitor *i) { + assert(i); + + hashmap_remove(i->manager->inhibitors, i->id); + + inhibitor_remove_fifo(i); + + free(i->who); + free(i->why); + + if (i->state_file) { + unlink(i->state_file); + free(i->state_file); + } + + free(i); +} + +int inhibitor_save(Inhibitor *i) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(i); + + r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(i->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "WHAT=%s\n" + "MODE=%s\n" + "UID="UID_FMT"\n" + "PID="PID_FMT"\n", + inhibit_what_to_string(i->what), + inhibit_mode_to_string(i->mode), + i->uid, + i->pid); + + if (i->who) { + _cleanup_free_ char *cc = NULL; + + cc = cescape(i->who); + if (!cc) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WHO=%s\n", cc); + } + + if (i->why) { + _cleanup_free_ char *cc = NULL; + + cc = cescape(i->why); + if (!cc) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "WHY=%s\n", cc); + } + + if (i->fifo_path) + fprintf(f, "FIFO=%s\n", i->fifo_path); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, i->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(i->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file); +} + +int inhibitor_start(Inhibitor *i) { + assert(i); + + if (i->started) + return 0; + + dual_timestamp_get(&i->since); + + log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.", + strna(i->who), strna(i->why), + i->pid, i->uid, + inhibit_mode_to_string(i->mode)); + + inhibitor_save(i); + + i->started = true; + + manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); + + return 0; +} + +int inhibitor_stop(Inhibitor *i) { + assert(i); + + if (i->started) + log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.", + strna(i->who), strna(i->why), + i->pid, i->uid, + inhibit_mode_to_string(i->mode)); + + if (i->state_file) + unlink(i->state_file); + + i->started = false; + + manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); + + return 0; +} + +int inhibitor_load(Inhibitor *i) { + + _cleanup_free_ char + *what = NULL, + *uid = NULL, + *pid = NULL, + *who = NULL, + *why = NULL, + *mode = NULL; + + InhibitWhat w; + InhibitMode mm; + char *cc; + int r; + + r = parse_env_file(i->state_file, NEWLINE, + "WHAT", &what, + "UID", &uid, + "PID", &pid, + "WHO", &who, + "WHY", &why, + "MODE", &mode, + "FIFO", &i->fifo_path, + NULL); + if (r < 0) + return r; + + w = what ? inhibit_what_from_string(what) : 0; + if (w >= 0) + i->what = w; + + mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK; + if (mm >= 0) + i->mode = mm; + + if (uid) { + r = parse_uid(uid, &i->uid); + if (r < 0) + return r; + } + + if (pid) { + r = parse_pid(pid, &i->pid); + if (r < 0) + return r; + } + + if (who) { + r = cunescape(who, 0, &cc); + if (r < 0) + return r; + + free(i->who); + i->who = cc; + } + + if (why) { + r = cunescape(why, 0, &cc); + if (r < 0) + return r; + + free(i->why); + i->why = cc; + } + + if (i->fifo_path) { + int fd; + + fd = inhibitor_create_fifo(i); + safe_close(fd); + } + + return 0; +} + +static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Inhibitor *i = userdata; + + assert(s); + assert(fd == i->fifo_fd); + assert(i); + + inhibitor_stop(i); + inhibitor_free(i); + + return 0; +} + +int inhibitor_create_fifo(Inhibitor *i) { + int r; + + assert(i); + + /* Create FIFO */ + if (!i->fifo_path) { + r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + return r; + + i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL); + if (!i->fifo_path) + return -ENOMEM; + + if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST) + return -errno; + } + + /* Open reading side */ + if (i->fifo_fd < 0) { + i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + if (i->fifo_fd < 0) + return -errno; + } + + if (!i->event_source) { + r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i); + if (r < 0) + return r; + + r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); + if (r < 0) + return r; + } + + /* Open writing side */ + r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + if (r < 0) + return -errno; + + return r; +} + +void inhibitor_remove_fifo(Inhibitor *i) { + assert(i); + + i->event_source = sd_event_source_unref(i->event_source); + i->fifo_fd = safe_close(i->fifo_fd); + + if (i->fifo_path) { + unlink(i->fifo_path); + i->fifo_path = mfree(i->fifo_path); + } +} + +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { + Inhibitor *i; + Iterator j; + InhibitWhat what = 0; + + assert(m); + + HASHMAP_FOREACH(i, m->inhibitors, j) + if (i->mode == mm) + what |= i->what; + + return what; +} + +static int pid_is_active(Manager *m, pid_t pid) { + Session *s; + int r; + + r = manager_get_session_by_pid(m, pid, &s); + if (r < 0) + return r; + + /* If there's no session assigned to it, then it's globally + * active on all ttys */ + if (r == 0) + return 1; + + return session_is_active(s); +} + +bool manager_is_inhibited( + Manager *m, + InhibitWhat w, + InhibitMode mm, + dual_timestamp *since, + bool ignore_inactive, + bool ignore_uid, + uid_t uid, + Inhibitor **offending) { + + Inhibitor *i; + Iterator j; + struct dual_timestamp ts = DUAL_TIMESTAMP_NULL; + bool inhibited = false; + + assert(m); + assert(w > 0 && w < _INHIBIT_WHAT_MAX); + + HASHMAP_FOREACH(i, m->inhibitors, j) { + if (!(i->what & w)) + continue; + + if (i->mode != mm) + continue; + + if (ignore_inactive && pid_is_active(m, i->pid) <= 0) + continue; + + if (ignore_uid && i->uid == uid) + continue; + + if (!inhibited || + i->since.monotonic < ts.monotonic) + ts = i->since; + + inhibited = true; + + if (offending) + *offending = i; + } + + if (since) + *since = ts; + + return inhibited; +} + +const char *inhibit_what_to_string(InhibitWhat w) { + static thread_local char buffer[97]; + char *p; + + if (w < 0 || w >= _INHIBIT_WHAT_MAX) + return NULL; + + p = buffer; + if (w & INHIBIT_SHUTDOWN) + p = stpcpy(p, "shutdown:"); + if (w & INHIBIT_SLEEP) + p = stpcpy(p, "sleep:"); + if (w & INHIBIT_IDLE) + p = stpcpy(p, "idle:"); + if (w & INHIBIT_HANDLE_POWER_KEY) + p = stpcpy(p, "handle-power-key:"); + if (w & INHIBIT_HANDLE_SUSPEND_KEY) + p = stpcpy(p, "handle-suspend-key:"); + if (w & INHIBIT_HANDLE_HIBERNATE_KEY) + p = stpcpy(p, "handle-hibernate-key:"); + if (w & INHIBIT_HANDLE_LID_SWITCH) + p = stpcpy(p, "handle-lid-switch:"); + + if (p > buffer) + *(p-1) = 0; + else + *p = 0; + + return buffer; +} + +InhibitWhat inhibit_what_from_string(const char *s) { + InhibitWhat what = 0; + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, s, ":", state) { + if (l == 8 && strneq(word, "shutdown", l)) + what |= INHIBIT_SHUTDOWN; + else if (l == 5 && strneq(word, "sleep", l)) + what |= INHIBIT_SLEEP; + else if (l == 4 && strneq(word, "idle", l)) + what |= INHIBIT_IDLE; + else if (l == 16 && strneq(word, "handle-power-key", l)) + what |= INHIBIT_HANDLE_POWER_KEY; + else if (l == 18 && strneq(word, "handle-suspend-key", l)) + what |= INHIBIT_HANDLE_SUSPEND_KEY; + else if (l == 20 && strneq(word, "handle-hibernate-key", l)) + what |= INHIBIT_HANDLE_HIBERNATE_KEY; + else if (l == 17 && strneq(word, "handle-lid-switch", l)) + what |= INHIBIT_HANDLE_LID_SWITCH; + else + return _INHIBIT_WHAT_INVALID; + } + + return what; +} + +static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { + [INHIBIT_BLOCK] = "block", + [INHIBIT_DELAY] = "delay" +}; + +DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode); diff --git a/src/grp-login/systemd-logind/logind-inhibit.h b/src/grp-login/systemd-logind/logind-inhibit.h new file mode 100644 index 0000000000..70de199c60 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-inhibit.h @@ -0,0 +1,89 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Inhibitor Inhibitor; + + +typedef enum InhibitWhat { + INHIBIT_SHUTDOWN = 1, + INHIBIT_SLEEP = 2, + INHIBIT_IDLE = 4, + INHIBIT_HANDLE_POWER_KEY = 8, + INHIBIT_HANDLE_SUSPEND_KEY = 16, + INHIBIT_HANDLE_HIBERNATE_KEY = 32, + INHIBIT_HANDLE_LID_SWITCH = 64, + _INHIBIT_WHAT_MAX = 128, + _INHIBIT_WHAT_INVALID = -1 +} InhibitWhat; + +typedef enum InhibitMode { + INHIBIT_BLOCK, + INHIBIT_DELAY, + _INHIBIT_MODE_MAX, + _INHIBIT_MODE_INVALID = -1 +} InhibitMode; + +#include "logind.h" + +struct Inhibitor { + Manager *manager; + + sd_event_source *event_source; + + char *id; + char *state_file; + + bool started; + + InhibitWhat what; + char *who; + char *why; + InhibitMode mode; + + pid_t pid; + uid_t uid; + + dual_timestamp since; + + char *fifo_path; + int fifo_fd; +}; + +Inhibitor* inhibitor_new(Manager *m, const char *id); +void inhibitor_free(Inhibitor *i); + +int inhibitor_save(Inhibitor *i); +int inhibitor_load(Inhibitor *i); + +int inhibitor_start(Inhibitor *i); +int inhibitor_stop(Inhibitor *i); + +int inhibitor_create_fifo(Inhibitor *i); +void inhibitor_remove_fifo(Inhibitor *i); + +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); +bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending); + +const char *inhibit_what_to_string(InhibitWhat k); +InhibitWhat inhibit_what_from_string(const char *s); + +const char *inhibit_mode_to_string(InhibitMode k); +InhibitMode inhibit_mode_from_string(const char *s); diff --git a/src/grp-login/systemd-logind/logind-seat-dbus.c b/src/grp-login/systemd-logind/logind-seat-dbus.c new file mode 100644 index 0000000000..e4febe1ab5 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat-dbus.c @@ -0,0 +1,475 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" + +#include "logind-seat.h" +#include "logind.h" + +static int property_get_active_session( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = s->active ? session_bus_path(s->active) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p); +} + +static int property_get_can_multi_session( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_multi_session(s)); +} + +static int property_get_can_tty( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_tty(s)); +} + +static int property_get_can_graphical( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_can_graphical(s)); +} + +static int property_get_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + Session *session; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", session->id, p); + if (r < 0) + return r; + + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 1; +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Seat *s = userdata; + dual_timestamp t; + uint64_t u; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = seat_get_idle_hint(s, &t); + if (r < 0) + return r; + + u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", u); +} + +int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + UID_INVALID, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = seat_stop_sessions(s, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + const char *name; + Session *session; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + session = hashmap_get(s->manager->sessions, name); + if (!session) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); + + if (session->seat != s) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); + + r = session_activate(session); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + unsigned int to; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "u", &to); + if (r < 0) + return r; + + if (to <= 0) + return -EINVAL; + + r = seat_switch_to(s, to); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = seat_switch_to_next(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Seat *s = userdata; + int r; + + assert(message); + assert(s); + + r = seat_switch_to_previous(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable seat_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Seat *seat; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/seat/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + Session *session; + const char *name; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + + session = hashmap_get(m->sessions, name); + if (!session) + return 0; + + seat = session->seat; + } else { + _cleanup_free_ char *e = NULL; + const char *p; + + p = startswith(path, "/org/freedesktop/login1/seat/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + seat = hashmap_get(m->seats, e); + } + + if (!seat) + return 0; + + *found = seat; + return 1; +} + +char *seat_bus_path(Seat *s) { + _cleanup_free_ char *t = NULL; + + assert(s); + + t = bus_label_escape(s->id); + if (!t) + return NULL; + + return strappend("/org/freedesktop/login1/seat/", t); +} + +int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + Seat *seat; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(seat, m->seats, i) { + char *p; + + p = seat_bus_path(seat); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *name; + Session *session; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_session(creds, &name); + if (r >= 0) { + session = hashmap_get(m->sessions, name); + if (session && session->seat) { + r = strv_extend(&l, "/org/freedesktop/login1/seat/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int seat_send_signal(Seat *s, bool new_seat) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = seat_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_seat ? "SeatNew" : "SeatRemoved", + "so", s->id, p); +} + +int seat_send_changed(Seat *s, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(s); + + if (!s->started) + return 0; + + p = seat_bus_path(s); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); +} diff --git a/src/grp-login/systemd-logind/logind-seat.c b/src/grp-login/systemd-logind/logind-seat.c new file mode 100644 index 0000000000..6525ad5d7b --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat.c @@ -0,0 +1,696 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "logind-acl.h" +#include "logind-seat.h" + +Seat *seat_new(Manager *m, const char *id) { + Seat *s; + + assert(m); + assert(id); + + s = new0(Seat, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/seats/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = basename(s->state_file); + s->manager = m; + + if (hashmap_put(m->seats, s->id, s) < 0) { + free(s->state_file); + free(s); + return NULL; + } + + return s; +} + +void seat_free(Seat *s) { + assert(s); + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); + + while (s->sessions) + session_free(s->sessions); + + assert(!s->active); + + while (s->devices) + device_free(s->devices); + + hashmap_remove(s->manager->seats, s->id); + + free(s->positions); + free(s->state_file); + free(s); +} + +int seat_save(Seat *s) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(s); + + if (!s->started) + return 0; + + r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "IS_SEAT0=%i\n" + "CAN_MULTI_SESSION=%i\n" + "CAN_TTY=%i\n" + "CAN_GRAPHICAL=%i\n", + seat_is_seat0(s), + seat_can_multi_session(s), + seat_can_tty(s), + seat_can_graphical(s)); + + if (s->active) { + assert(s->active->user); + + fprintf(f, + "ACTIVE=%s\n" + "ACTIVE_UID="UID_FMT"\n", + s->active->id, + s->active->user->uid); + } + + if (s->sessions) { + Session *i; + + fputs("SESSIONS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) { + fprintf(f, + "%s%c", + i->id, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + fputs("UIDS=", f); + LIST_FOREACH(sessions_by_seat, i, s->sessions) + fprintf(f, + UID_FMT"%c", + i->user->uid, + i->sessions_by_seat_next ? ' ' : '\n'); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, s->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(s->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file); +} + +int seat_load(Seat *s) { + assert(s); + + /* There isn't actually anything to read here ... */ + + return 0; +} + +static int vt_allocate(unsigned int vtnr) { + char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)]; + _cleanup_close_ int fd = -1; + + assert(vtnr >= 1); + + xsprintf(p, "/dev/tty%u", vtnr); + fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return 0; +} + +int seat_preallocate_vts(Seat *s) { + int r = 0; + unsigned i; + + assert(s); + assert(s->manager); + + log_debug("Preallocating VTs..."); + + if (s->manager->n_autovts <= 0) + return 0; + + if (!seat_has_vts(s)) + return 0; + + for (i = 1; i <= s->manager->n_autovts; i++) { + int q; + + q = vt_allocate(i); + if (q < 0) { + log_error_errno(q, "Failed to preallocate VT %u: %m", i); + r = q; + } + } + + return r; +} + +int seat_apply_acls(Seat *s, Session *old_active) { + int r; + + assert(s); + + r = devnode_acl_all(s->manager->udev, + s->id, + false, + !!old_active, old_active ? old_active->user->uid : 0, + !!s->active, s->active ? s->active->user->uid : 0); + + if (r < 0) + log_error_errno(r, "Failed to apply ACLs: %m"); + + return r; +} + +int seat_set_active(Seat *s, Session *session) { + Session *old_active; + + assert(s); + assert(!session || session->seat == s); + + if (session == s->active) + return 0; + + old_active = s->active; + s->active = session; + + if (old_active) { + session_device_pause_all(old_active); + session_send_changed(old_active, "Active", NULL); + } + + seat_apply_acls(s, old_active); + + if (session && session->started) { + session_send_changed(session, "Active", NULL); + session_device_resume_all(session); + } + + if (!session || session->started) + seat_send_changed(s, "ActiveSession", NULL); + + seat_save(s); + + if (session) { + session_save(session); + user_save(session->user); + } + + if (old_active) { + session_save(old_active); + if (!session || session->user != old_active->user) + user_save(old_active->user); + } + + return 0; +} + +int seat_switch_to(Seat *s, unsigned int num) { + /* Public session positions skip 0 (there is only F1-F12). Maybe it + * will get reassigned in the future, so return error for now. */ + if (num == 0) + return -EINVAL; + + if (num >= s->position_count || !s->positions[num]) { + /* allow switching to unused VTs to trigger auto-activate */ + if (seat_has_vts(s) && num < 64) + return chvt(num); + + return -EINVAL; + } + + return session_activate(s->positions[num]); +} + +int seat_switch_to_next(Seat *s) { + unsigned int start, i; + + if (s->position_count == 0) + return -EINVAL; + + start = 1; + if (s->active && s->active->position > 0) + start = s->active->position; + + for (i = start + 1; i < s->position_count; ++i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + for (i = 1; i < start; ++i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + return -EINVAL; +} + +int seat_switch_to_previous(Seat *s) { + unsigned int start, i; + + if (s->position_count == 0) + return -EINVAL; + + start = 1; + if (s->active && s->active->position > 0) + start = s->active->position; + + for (i = start - 1; i > 0; --i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + for (i = s->position_count - 1; i > start; --i) + if (s->positions[i]) + return session_activate(s->positions[i]); + + return -EINVAL; +} + +int seat_active_vt_changed(Seat *s, unsigned int vtnr) { + Session *i, *new_active = NULL; + int r; + + assert(s); + assert(vtnr >= 1); + + if (!seat_has_vts(s)) + return -EINVAL; + + log_debug("VT changed to %u", vtnr); + + /* we might have earlier closing sessions on the same VT, so try to + * find a running one first */ + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr && !i->stopping) { + new_active = i; + break; + } + + if (!new_active) { + /* no running one? then we can't decide which one is the + * active one, let the first one win */ + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr) { + new_active = i; + break; + } + } + + r = seat_set_active(s, new_active); + manager_spawn_autovt(s->manager, vtnr); + + return r; +} + +int seat_read_active_vt(Seat *s) { + char t[64]; + ssize_t k; + unsigned int vtnr; + int r; + + assert(s); + + if (!seat_has_vts(s)) + return 0; + + lseek(s->manager->console_active_fd, SEEK_SET, 0); + + k = read(s->manager->console_active_fd, t, sizeof(t)-1); + if (k <= 0) { + log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); + return k < 0 ? -errno : -EIO; + } + + t[k] = 0; + truncate_nl(t); + + if (!startswith(t, "tty")) { + log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); + return -EIO; + } + + r = safe_atou(t+3, &vtnr); + if (r < 0) { + log_error("Failed to parse VT number %s", t+3); + return r; + } + + if (!vtnr) { + log_error("VT number invalid: %s", t+3); + return -EIO; + } + + return seat_active_vt_changed(s, vtnr); +} + +int seat_start(Seat *s) { + assert(s); + + if (s->started) + return 0; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), + "SEAT_ID=%s", s->id, + LOG_MESSAGE("New seat %s.", s->id), + NULL); + + /* Initialize VT magic stuff */ + seat_preallocate_vts(s); + + /* Read current VT */ + seat_read_active_vt(s); + + s->started = true; + + /* Save seat data */ + seat_save(s); + + seat_send_signal(s, true); + + return 0; +} + +int seat_stop(Seat *s, bool force) { + int r = 0; + + assert(s); + + if (s->started) + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), + "SEAT_ID=%s", s->id, + LOG_MESSAGE("Removed seat %s.", s->id), + NULL); + + seat_stop_sessions(s, force); + + unlink(s->state_file); + seat_add_to_gc_queue(s); + + if (s->started) + seat_send_signal(s, false); + + s->started = false; + + return r; +} + +int seat_stop_sessions(Seat *s, bool force) { + Session *session; + int r = 0, k; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + k = session_stop(session, force); + if (k < 0) + r = k; + } + + return r; +} + +void seat_evict_position(Seat *s, Session *session) { + Session *iter; + unsigned int pos = session->position; + + session->position = 0; + + if (pos == 0) + return; + + if (pos < s->position_count && s->positions[pos] == session) { + s->positions[pos] = NULL; + + /* There might be another session claiming the same + * position (eg., during gdm->session transition), so let's look + * for it and set it on the free slot. */ + LIST_FOREACH(sessions_by_seat, iter, s->sessions) { + if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) { + s->positions[pos] = iter; + break; + } + } + } +} + +void seat_claim_position(Seat *s, Session *session, unsigned int pos) { + /* with VTs, the position is always the same as the VTnr */ + if (seat_has_vts(s)) + pos = session->vtnr; + + if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1)) + return; + + seat_evict_position(s, session); + + session->position = pos; + if (pos > 0) + s->positions[pos] = session; +} + +static void seat_assign_position(Seat *s, Session *session) { + unsigned int pos; + + if (session->position > 0) + return; + + for (pos = 1; pos < s->position_count; ++pos) + if (!s->positions[pos]) + break; + + seat_claim_position(s, session, pos); +} + +int seat_attach_session(Seat *s, Session *session) { + assert(s); + assert(session); + assert(!session->seat); + + if (!seat_has_vts(s) != !session->vtnr) + return -EINVAL; + + session->seat = s; + LIST_PREPEND(sessions_by_seat, s->sessions, session); + seat_assign_position(s, session); + + seat_send_changed(s, "Sessions", NULL); + + /* On seats with VTs, the VT logic defines which session is active. On + * seats without VTs, we automatically activate new sessions. */ + if (!seat_has_vts(s)) + seat_set_active(s, session); + + return 0; +} + +void seat_complete_switch(Seat *s) { + Session *session; + + assert(s); + + /* if no session-switch is pending or if it got canceled, do nothing */ + if (!s->pending_switch) + return; + + session = s->pending_switch; + s->pending_switch = NULL; + + seat_set_active(s, session); +} + +bool seat_has_vts(Seat *s) { + assert(s); + + return seat_is_seat0(s) && s->manager->console_active_fd >= 0; +} + +bool seat_is_seat0(Seat *s) { + assert(s); + + return s->manager->seat0 == s; +} + +bool seat_can_multi_session(Seat *s) { + assert(s); + + return seat_has_vts(s); +} + +bool seat_can_tty(Seat *s) { + assert(s); + + return seat_has_vts(s); +} + +bool seat_has_master_device(Seat *s) { + assert(s); + + /* device list is ordered by "master" flag */ + return !!s->devices && s->devices->master; +} + +bool seat_can_graphical(Seat *s) { + assert(s); + + return seat_has_master_device(s); +} + +int seat_get_idle_hint(Seat *s, dual_timestamp *t) { + Session *session; + bool idle_hint = true; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(session, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic > ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +bool seat_check_gc(Seat *s, bool drop_not_started) { + assert(s); + + if (drop_not_started && !s->started) + return false; + + if (seat_is_seat0(s)) + return true; + + return seat_has_master_device(s); +} + +void seat_add_to_gc_queue(Seat *s) { + assert(s); + + if (s->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s); + s->in_gc_queue = true; +} + +static bool seat_name_valid_char(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || + c == '_'; +} + +bool seat_name_is_valid(const char *name) { + const char *p; + + assert(name); + + if (!startswith(name, "seat")) + return false; + + if (!name[4]) + return false; + + for (p = name; *p; p++) + if (!seat_name_valid_char(*p)) + return false; + + if (strlen(name) > 255) + return false; + + return true; +} diff --git a/src/grp-login/systemd-logind/logind-seat.h b/src/grp-login/systemd-logind/logind-seat.h new file mode 100644 index 0000000000..7fbeb5a727 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-seat.h @@ -0,0 +1,96 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct Seat Seat; + +#include "logind-session.h" + +struct Seat { + Manager *manager; + char *id; + + char *state_file; + + LIST_HEAD(Device, devices); + + Session *active; + Session *pending_switch; + LIST_HEAD(Session, sessions); + + Session **positions; + size_t position_count; + + bool in_gc_queue:1; + bool started:1; + + LIST_FIELDS(Seat, gc_queue); +}; + +Seat *seat_new(Manager *m, const char *id); +void seat_free(Seat *s); + +int seat_save(Seat *s); +int seat_load(Seat *s); + +int seat_apply_acls(Seat *s, Session *old_active); +int seat_set_active(Seat *s, Session *session); +int seat_switch_to(Seat *s, unsigned int num); +int seat_switch_to_next(Seat *s); +int seat_switch_to_previous(Seat *s); +int seat_active_vt_changed(Seat *s, unsigned int vtnr); +int seat_read_active_vt(Seat *s); +int seat_preallocate_vts(Seat *s); + +int seat_attach_session(Seat *s, Session *session); +void seat_complete_switch(Seat *s); +void seat_evict_position(Seat *s, Session *session); +void seat_claim_position(Seat *s, Session *session, unsigned int pos); + +bool seat_has_vts(Seat *s); +bool seat_is_seat0(Seat *s); +bool seat_can_multi_session(Seat *s); +bool seat_can_tty(Seat *s); +bool seat_has_master_device(Seat *s); +bool seat_can_graphical(Seat *s); + +int seat_get_idle_hint(Seat *s, dual_timestamp *t); + +int seat_start(Seat *s); +int seat_stop(Seat *s, bool force); +int seat_stop_sessions(Seat *s, bool force); + +bool seat_check_gc(Seat *s, bool drop_not_started); +void seat_add_to_gc_queue(Seat *s); + +bool seat_name_is_valid(const char *name); + +extern const sd_bus_vtable seat_vtable[]; + +int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *seat_bus_path(Seat *s); + +int seat_send_signal(Seat *s, bool new_seat); +int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_; + +int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-session-dbus.c b/src/grp-login/systemd-logind/logind-session-dbus.c new file mode 100644 index 0000000000..f79f9b5003 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-dbus.c @@ -0,0 +1,799 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/fd-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" + +#include "logind-session-device.h" +#include "logind-session.h" +#include "logind.h" + +static int property_get_user( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = user_bus_path(s->user); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p); +} + +static int property_get_name( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", s->user->name); +} + +static int property_get_seat( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + p = s->seat ? seat_bus_path(s->seat) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass); + +static int property_get_active( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_is_active(s)); +} + +static int property_get_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s))); +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + uint64_t u; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = session_get_idle_hint(s, &t); + if (r < 0) + return r; + + u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", u); +} + +static int property_get_locked_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); +} + +int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_stop(s, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = session_activate(s); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + int r; + + assert(message); + assert(s); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.lock-sessions", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock")); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint"); + + session_set_idle_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); + + session_set_locked_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + const char *swho; + int32_t signo; + KillWho who; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "si", &swho, &signo); + if (r < 0) + return r; + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); + } + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + s->user->uid, + &s->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = session_kill(s, who, signo); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + int r, force; + uid_t uid; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &force); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && (force || uid != s->user->uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control"); + + r = session_set_controller(s, sd_bus_message_get_sender(message), force); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_release_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + + assert(message); + assert(s); + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + session_drop_controller(s); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (sd) + /* We don't allow retrieving a device multiple times. + * The related ReleaseDevice call is not ref-counted. + * The caller should use dup() if it requires more + * than one fd (it would be functionally + * equivalent). */ + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken"); + + r = session_device_new(s, dev, &sd); + if (r < 0) + return r; + + r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active); + if (r < 0) + session_device_free(sd); + + return r; +} + +static int method_release_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (!sd) + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); + + session_device_free(sd); + return sd_bus_reply_method_return(message, NULL); +} + +static int method_pause_device_complete(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Session *s = userdata; + uint32_t major, minor; + SessionDevice *sd; + dev_t dev; + int r; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "uu", &major, &minor); + if (r < 0) + return r; + + if (!session_is_controller(s, sd_bus_message_get_sender(message))) + return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); + + dev = makedev(major, minor); + sd = hashmap_get(s->devices, &dev); + if (!sd) + return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); + + session_device_complete_pause(sd); + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable session_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("PauseDevice", "uus", 0), + SD_BUS_SIGNAL("ResumeDevice", "uuh", 0), + SD_BUS_SIGNAL("Lock", NULL, 0), + SD_BUS_SIGNAL("Unlock", NULL, 0), + + SD_BUS_VTABLE_END +}; + +int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Session *session; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/session/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + const char *name; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_session(creds, &name); + if (r < 0) + return r; + + session = hashmap_get(m->sessions, name); + } else { + _cleanup_free_ char *e = NULL; + const char *p; + + p = startswith(path, "/org/freedesktop/login1/session/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + session = hashmap_get(m->sessions, e); + } + + if (!session) + return 0; + + *found = session; + return 1; +} + +char *session_bus_path(Session *s) { + _cleanup_free_ char *t = NULL; + + assert(s); + + t = bus_label_escape(s->id); + if (!t) + return NULL; + + return strappend("/org/freedesktop/login1/session/", t); +} + +int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + Session *session; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(session, m->sessions, i) { + char *p; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + const char *name; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_session(creds, &name); + if (r >= 0) { + session = hashmap_get(m->sessions, name); + if (session) { + r = strv_extend(&l, "/org/freedesktop/login1/session/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int session_send_signal(Session *s, bool new_session) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_session ? "SessionNew" : "SessionRemoved", + "so", s->id, p); +} + +int session_send_changed(Session *s, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(s); + + if (!s->started) + return 0; + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l); +} + +int session_send_lock(Session *s, bool lock) { + _cleanup_free_ char *p = NULL; + + assert(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + s->manager->bus, + p, + "org.freedesktop.login1.Session", + lock ? "Lock" : "Unlock", + NULL); +} + +int session_send_lock_all(Manager *m, bool lock) { + Session *session; + Iterator i; + int r = 0; + + assert(m); + + HASHMAP_FOREACH(session, m->sessions, i) { + int k; + + k = session_send_lock(session, lock); + if (k < 0) + r = k; + } + + return r; +} + +int session_send_create_reply(Session *s, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; + _cleanup_close_ int fifo_fd = -1; + _cleanup_free_ char *p = NULL; + + assert(s); + + /* This is called after the session scope and the user service + * were successfully created, and finishes where + * bus_manager_create_session() left off. */ + + if (!s->create_message) + return 0; + + if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) + return 0; + + c = s->create_message; + s->create_message = NULL; + + if (error) + return sd_bus_reply_method_error(c, error); + + fifo_fd = session_create_fifo(s); + if (fifo_fd < 0) + return fifo_fd; + + /* Update the session state file before we notify the client + * about the result. */ + session_save(s); + + p = session_bus_path(s); + if (!p) + return -ENOMEM; + + log_debug("Sending reply about created session: " + "id=%s object_path=%s uid=%u runtime_path=%s " + "session_fd=%d seat=%s vtnr=%u", + s->id, + p, + (uint32_t) s->user->uid, + s->user->runtime_path, + fifo_fd, + s->seat ? s->seat->id : "", + (uint32_t) s->vtnr); + + return sd_bus_reply_method_return( + c, "soshusub", + s->id, + p, + s->user->runtime_path, + fifo_fd, + (uint32_t) s->user->uid, + s->seat ? s->seat->id : "", + (uint32_t) s->vtnr, + false); +} diff --git a/src/grp-login/systemd-logind/logind-session-device.c b/src/grp-login/systemd-logind/logind-session-device.c new file mode 100644 index 0000000000..99528488c0 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-device.c @@ -0,0 +1,482 @@ +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "logind-session-device.h" + +enum SessionDeviceNotifications { + SESSION_DEVICE_RESUME, + SESSION_DEVICE_TRY_PAUSE, + SESSION_DEVICE_PAUSE, + SESSION_DEVICE_RELEASE, +}; + +static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *path = NULL; + const char *t = NULL; + uint32_t major, minor; + int r; + + assert(sd); + + major = major(sd->dev); + minor = minor(sd->dev); + + if (!sd->session->controller) + return 0; + + path = session_bus_path(sd->session); + if (!path) + return -ENOMEM; + + r = sd_bus_message_new_signal( + sd->session->manager->bus, + &m, path, + "org.freedesktop.login1.Session", + (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice"); + if (!m) + return r; + + r = sd_bus_message_set_destination(m, sd->session->controller); + if (r < 0) + return r; + + switch (type) { + case SESSION_DEVICE_RESUME: + r = sd_bus_message_append(m, "uuh", major, minor, sd->fd); + if (r < 0) + return r; + break; + case SESSION_DEVICE_TRY_PAUSE: + t = "pause"; + break; + case SESSION_DEVICE_PAUSE: + t = "force"; + break; + case SESSION_DEVICE_RELEASE: + t = "gone"; + break; + default: + return -EINVAL; + } + + if (t) { + r = sd_bus_message_append(m, "uus", major, minor, t); + if (r < 0) + return r; + } + + return sd_bus_send(sd->session->manager->bus, m, NULL); +} + +static int sd_eviocrevoke(int fd) { + static bool warned; + int r; + + assert(fd >= 0); + + r = ioctl(fd, EVIOCREVOKE, NULL); + if (r < 0) { + r = -errno; + if (r == -EINVAL && !warned) { + warned = true; + log_warning("kernel does not support evdev-revocation"); + } + } + + return 0; +} + +static int sd_drmsetmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int sd_drmdropmaster(int fd) { + int r; + + assert(fd >= 0); + + r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); + if (r < 0) + return -errno; + + return 0; +} + +static int session_device_open(SessionDevice *sd, bool active) { + int fd, r; + + assert(sd->type != DEVICE_TYPE_UNKNOWN); + + /* open device and try to get an udev_device from it */ + fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (fd < 0) + return -errno; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + if (active) { + /* Weird legacy DRM semantics might return an error + * even though we're master. No way to detect that so + * fail at all times and let caller retry in inactive + * state. */ + r = sd_drmsetmaster(fd); + if (r < 0) { + close_nointr(fd); + return r; + } + } else { + /* DRM-Master is granted to the first user who opens a + * device automatically (ughh, racy!). Hence, we just + * drop DRM-Master in case we were the first. */ + sd_drmdropmaster(fd); + } + break; + case DEVICE_TYPE_EVDEV: + if (!active) + sd_eviocrevoke(fd); + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + return fd; +} + +static int session_device_start(SessionDevice *sd) { + int r; + + assert(sd); + assert(session_is_active(sd->session)); + + if (sd->active) + return 0; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* Device is kept open. Simply call drmSetMaster() and hope + * there is no-one else. In case it fails, we keep the device + * paused. Maybe at some point we have a drmStealMaster(). */ + r = sd_drmsetmaster(sd->fd); + if (r < 0) + return r; + break; + case DEVICE_TYPE_EVDEV: + /* Evdev devices are revoked while inactive. Reopen it and we + * are fine. */ + r = session_device_open(sd, true); + if (r < 0) + return r; + close_nointr(sd->fd); + sd->fd = r; + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices wihout synchronizations */ + break; + } + + sd->active = true; + return 0; +} + +static void session_device_stop(SessionDevice *sd) { + assert(sd); + + if (!sd->active) + return; + + switch (sd->type) { + case DEVICE_TYPE_DRM: + /* On DRM devices we simply drop DRM-Master but keep it open. + * This allows the user to keep resources allocated. The + * CAP_SYS_ADMIN restriction to DRM-Master prevents users from + * circumventing this. */ + sd_drmdropmaster(sd->fd); + break; + case DEVICE_TYPE_EVDEV: + /* Revoke access on evdev file-descriptors during deactivation. + * This will basically prevent any operations on the fd and + * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN + * protection this way. */ + sd_eviocrevoke(sd->fd); + break; + case DEVICE_TYPE_UNKNOWN: + default: + /* fallback for devices without synchronization */ + break; + } + + sd->active = false; +} + +static DeviceType detect_device_type(struct udev_device *dev) { + const char *sysname, *subsystem; + DeviceType type; + + sysname = udev_device_get_sysname(dev); + subsystem = udev_device_get_subsystem(dev); + type = DEVICE_TYPE_UNKNOWN; + + if (streq_ptr(subsystem, "drm")) { + if (startswith(sysname, "card")) + type = DEVICE_TYPE_DRM; + } else if (streq_ptr(subsystem, "input")) { + if (startswith(sysname, "event")) + type = DEVICE_TYPE_EVDEV; + } + + return type; +} + +static int session_device_verify(SessionDevice *sd) { + struct udev_device *dev, *p = NULL; + const char *sp, *node; + int r; + + dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev); + if (!dev) + return -ENODEV; + + sp = udev_device_get_syspath(dev); + node = udev_device_get_devnode(dev); + if (!node) { + r = -EINVAL; + goto err_dev; + } + + /* detect device type so we can find the correct sysfs parent */ + sd->type = detect_device_type(dev); + if (sd->type == DEVICE_TYPE_UNKNOWN) { + r = -ENODEV; + goto err_dev; + } else if (sd->type == DEVICE_TYPE_EVDEV) { + /* for evdev devices we need the parent node as device */ + p = dev; + dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL); + if (!dev) { + r = -ENODEV; + goto err_dev; + } + sp = udev_device_get_syspath(dev); + } else if (sd->type != DEVICE_TYPE_DRM) { + /* Prevent opening unsupported devices. Especially devices of + * subsystem "input" must be opened via the evdev node as + * we require EVIOCREVOKE. */ + r = -ENODEV; + goto err_dev; + } + + /* search for an existing seat device and return it if available */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + /* The caller might have gotten the udev event before we were + * able to process it. Hence, fake the "add" event and let the + * logind-manager handle the new device. */ + r = manager_process_seat_device(sd->session->manager, dev); + if (r < 0) + goto err_dev; + + /* if it's still not available, then the device is invalid */ + sd->device = hashmap_get(sd->session->manager->devices, sp); + if (!sd->device) { + r = -ENODEV; + goto err_dev; + } + } + + if (sd->device->seat != sd->session->seat) { + r = -EPERM; + goto err_dev; + } + + sd->node = strdup(node); + if (!sd->node) { + r = -ENOMEM; + goto err_dev; + } + + r = 0; +err_dev: + udev_device_unref(p ? : dev); + return r; +} + +int session_device_new(Session *s, dev_t dev, SessionDevice **out) { + SessionDevice *sd; + int r; + + assert(s); + assert(out); + + if (!s->seat) + return -EPERM; + + sd = new0(SessionDevice, 1); + if (!sd) + return -ENOMEM; + + sd->session = s; + sd->dev = dev; + sd->fd = -1; + sd->type = DEVICE_TYPE_UNKNOWN; + + r = session_device_verify(sd); + if (r < 0) + goto error; + + r = hashmap_put(s->devices, &sd->dev, sd); + if (r < 0) { + r = -ENOMEM; + goto error; + } + + /* Open the device for the first time. We need a valid fd to pass back + * to the caller. If the session is not active, this _might_ immediately + * revoke access and thus invalidate the fd. But this is still needed + * to pass a valid fd back. */ + sd->active = session_is_active(s); + r = session_device_open(sd, sd->active); + if (r < 0) { + /* EINVAL _may_ mean a master is active; retry inactive */ + if (sd->active && r == -EINVAL) { + sd->active = false; + r = session_device_open(sd, false); + } + if (r < 0) + goto error; + } + sd->fd = r; + + LIST_PREPEND(sd_by_device, sd->device->session_devices, sd); + + *out = sd; + return 0; + +error: + hashmap_remove(s->devices, &sd->dev); + free(sd->node); + free(sd); + return r; +} + +void session_device_free(SessionDevice *sd) { + assert(sd); + + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_RELEASE); + close_nointr(sd->fd); + + LIST_REMOVE(sd_by_device, sd->device->session_devices, sd); + + hashmap_remove(sd->session->devices, &sd->dev); + + free(sd->node); + free(sd); +} + +void session_device_complete_pause(SessionDevice *sd) { + SessionDevice *iter; + Iterator i; + + if (!sd->active) + return; + + session_device_stop(sd); + + /* if not all devices are paused, wait for further completion events */ + HASHMAP_FOREACH(iter, sd->session->devices, i) + if (iter->active) + return; + + /* complete any pending session switch */ + seat_complete_switch(sd->session->seat); +} + +void session_device_resume_all(Session *s) { + SessionDevice *sd; + Iterator i; + int r; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (!sd->active) { + r = session_device_start(sd); + if (!r) + session_device_notify(sd, SESSION_DEVICE_RESUME); + } + } +} + +void session_device_pause_all(Session *s) { + SessionDevice *sd; + Iterator i; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (sd->active) { + session_device_stop(sd); + session_device_notify(sd, SESSION_DEVICE_PAUSE); + } + } +} + +unsigned int session_device_try_pause_all(Session *s) { + SessionDevice *sd; + Iterator i; + unsigned int num_pending = 0; + + assert(s); + + HASHMAP_FOREACH(sd, s->devices, i) { + if (sd->active) { + session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE); + ++num_pending; + } + } + + return num_pending; +} diff --git a/src/grp-login/systemd-logind/logind-session-device.h b/src/grp-login/systemd-logind/logind-session-device.h new file mode 100644 index 0000000000..4a22decb1d --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session-device.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef enum DeviceType DeviceType; +typedef struct SessionDevice SessionDevice; + +#include "logind.h" + +enum DeviceType { + DEVICE_TYPE_UNKNOWN, + DEVICE_TYPE_DRM, + DEVICE_TYPE_EVDEV, +}; + +struct SessionDevice { + Session *session; + Device *device; + + dev_t dev; + char *node; + int fd; + bool active; + DeviceType type; + + LIST_FIELDS(struct SessionDevice, sd_by_device); +}; + +int session_device_new(Session *s, dev_t dev, SessionDevice **out); +void session_device_free(SessionDevice *sd); +void session_device_complete_pause(SessionDevice *sd); + +void session_device_resume_all(Session *s); +void session_device_pause_all(Session *s); +unsigned int session_device_try_pause_all(Session *s); diff --git a/src/grp-login/systemd-logind/logind-session.c b/src/grp-login/systemd-logind/logind-session.c new file mode 100644 index 0000000000..b08db54068 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session.c @@ -0,0 +1,1275 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "logind-session.h" + +#define RELEASE_USEC (20*USEC_PER_SEC) + +static void session_remove_fifo(Session *s); + +Session* session_new(Manager *m, const char *id) { + Session *s; + + assert(m); + assert(id); + assert(session_id_valid(id)); + + s = new0(Session, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/sessions/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->devices = hashmap_new(&devt_hash_ops); + if (!s->devices) { + free(s->state_file); + free(s); + return NULL; + } + + s->id = basename(s->state_file); + + if (hashmap_put(m->sessions, s->id, s) < 0) { + hashmap_free(s->devices); + free(s->state_file); + free(s); + return NULL; + } + + s->manager = m; + s->fifo_fd = -1; + s->vtfd = -1; + + return s; +} + +void session_free(Session *s) { + SessionDevice *sd; + + assert(s); + + if (s->in_gc_queue) + LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + session_remove_fifo(s); + + session_drop_controller(s); + + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + hashmap_free(s->devices); + + if (s->user) { + LIST_REMOVE(sessions_by_user, s->user->sessions, s); + + if (s->user->display == s) + s->user->display = NULL; + } + + if (s->seat) { + if (s->seat->active == s) + s->seat->active = NULL; + if (s->seat->pending_switch == s) + s->seat->pending_switch = NULL; + + seat_evict_position(s->seat, s); + LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); + } + + if (s->scope) { + hashmap_remove(s->manager->session_units, s->scope); + free(s->scope); + } + + free(s->scope_job); + + sd_bus_message_unref(s->create_message); + + free(s->tty); + free(s->display); + free(s->remote_host); + free(s->remote_user); + free(s->service); + free(s->desktop); + + hashmap_remove(s->manager->sessions, s->id); + + free(s->state_file); + free(s); +} + +void session_set_user(Session *s, User *u) { + assert(s); + assert(!s->user); + + s->user = u; + LIST_PREPEND(sessions_by_user, u->sessions, s); +} + +int session_save(Session *s) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r = 0; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (!s->started) + return 0; + + r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(s->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + assert(s->user); + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "UID="UID_FMT"\n" + "USER=%s\n" + "ACTIVE=%i\n" + "STATE=%s\n" + "REMOTE=%i\n", + s->user->uid, + s->user->name, + session_is_active(s), + session_state_to_string(session_get_state(s)), + s->remote); + + if (s->type >= 0) + fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); + + if (s->class >= 0) + fprintf(f, "CLASS=%s\n", session_class_to_string(s->class)); + + if (s->scope) + fprintf(f, "SCOPE=%s\n", s->scope); + if (s->scope_job) + fprintf(f, "SCOPE_JOB=%s\n", s->scope_job); + + if (s->fifo_path) + fprintf(f, "FIFO=%s\n", s->fifo_path); + + if (s->seat) + fprintf(f, "SEAT=%s\n", s->seat->id); + + if (s->tty) + fprintf(f, "TTY=%s\n", s->tty); + + if (s->display) + fprintf(f, "DISPLAY=%s\n", s->display); + + if (s->remote_host) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->remote_host); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "REMOTE_HOST=%s\n", escaped); + } + + if (s->remote_user) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->remote_user); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "REMOTE_USER=%s\n", escaped); + } + + if (s->service) { + _cleanup_free_ char *escaped; + + escaped = cescape(s->service); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "SERVICE=%s\n", escaped); + } + + if (s->desktop) { + _cleanup_free_ char *escaped; + + + escaped = cescape(s->desktop); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "DESKTOP=%s\n", escaped); + } + + if (s->seat && seat_has_vts(s->seat)) + fprintf(f, "VTNR=%u\n", s->vtnr); + + if (!s->vtnr) + fprintf(f, "POSITION=%u\n", s->position); + + if (s->leader > 0) + fprintf(f, "LEADER="PID_FMT"\n", s->leader); + + if (s->audit_id > 0) + fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); + + if (dual_timestamp_is_set(&s->timestamp)) + fprintf(f, + "REALTIME="USEC_FMT"\n" + "MONOTONIC="USEC_FMT"\n", + s->timestamp.realtime, + s->timestamp.monotonic); + + if (s->controller) + fprintf(f, "CONTROLLER=%s\n", s->controller); + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, s->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(s->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save session data %s: %m", s->state_file); +} + + +int session_load(Session *s) { + _cleanup_free_ char *remote = NULL, + *seat = NULL, + *vtnr = NULL, + *state = NULL, + *position = NULL, + *leader = NULL, + *type = NULL, + *class = NULL, + *uid = NULL, + *realtime = NULL, + *monotonic = NULL, + *controller = NULL; + + int k, r; + + assert(s); + + r = parse_env_file(s->state_file, NEWLINE, + "REMOTE", &remote, + "SCOPE", &s->scope, + "SCOPE_JOB", &s->scope_job, + "FIFO", &s->fifo_path, + "SEAT", &seat, + "TTY", &s->tty, + "DISPLAY", &s->display, + "REMOTE_HOST", &s->remote_host, + "REMOTE_USER", &s->remote_user, + "SERVICE", &s->service, + "DESKTOP", &s->desktop, + "VTNR", &vtnr, + "STATE", &state, + "POSITION", &position, + "LEADER", &leader, + "TYPE", &type, + "CLASS", &class, + "UID", &uid, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + "CONTROLLER", &controller, + NULL); + + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", s->state_file); + + if (!s->user) { + uid_t u; + User *user; + + if (!uid) { + log_error("UID not specified for session %s", s->id); + return -ENOENT; + } + + r = parse_uid(uid, &u); + if (r < 0) { + log_error("Failed to parse UID value %s for session %s.", uid, s->id); + return r; + } + + user = hashmap_get(s->manager->users, UID_TO_PTR(u)); + if (!user) { + log_error("User of session %s not known.", s->id); + return -ENOENT; + } + + session_set_user(s, user); + } + + if (remote) { + k = parse_boolean(remote); + if (k >= 0) + s->remote = k; + } + + if (vtnr) + safe_atou(vtnr, &s->vtnr); + + if (seat && !s->seat) { + Seat *o; + + o = hashmap_get(s->manager->seats, seat); + if (o) + r = seat_attach_session(o, s); + if (!o || r < 0) + log_error("Cannot attach session %s to seat %s", s->id, seat); + } + + if (!s->seat || !seat_has_vts(s->seat)) + s->vtnr = 0; + + if (position && s->seat) { + unsigned int npos; + + safe_atou(position, &npos); + seat_claim_position(s->seat, s, npos); + } + + if (leader) { + k = parse_pid(leader, &s->leader); + if (k >= 0) + audit_session_from_pid(s->leader, &s->audit_id); + } + + if (type) { + SessionType t; + + t = session_type_from_string(type); + if (t >= 0) + s->type = t; + } + + if (class) { + SessionClass c; + + c = session_class_from_string(class); + if (c >= 0) + s->class = c; + } + + if (state && streq(state, "closing")) + s->stopping = true; + + if (s->fifo_path) { + int fd; + + /* If we open an unopened pipe for reading we will not + get an EOF. to trigger an EOF we hence open it for + writing, but close it right away which then will + trigger the EOF. This will happen immediately if no + other process has the FIFO open for writing, i. e. + when the session died before logind (re)started. */ + + fd = session_create_fifo(s); + safe_close(fd); + } + + if (realtime) + timestamp_deserialize(realtime, &s->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &s->timestamp.monotonic); + + if (controller) { + if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) + session_set_controller(s, controller, false); + else + session_restore_vt(s); + } + + return r; +} + +int session_activate(Session *s) { + unsigned int num_pending; + + assert(s); + assert(s->user); + + if (!s->seat) + return -EOPNOTSUPP; + + if (s->seat->active == s) + return 0; + + /* on seats with VTs, we let VTs manage session-switching */ + if (seat_has_vts(s->seat)) { + if (!s->vtnr) + return -EOPNOTSUPP; + + return chvt(s->vtnr); + } + + /* On seats without VTs, we implement session-switching in logind. We + * try to pause all session-devices and wait until the session + * controller acknowledged them. Once all devices are asleep, we simply + * switch the active session and be done. + * We save the session we want to switch to in seat->pending_switch and + * seat_complete_switch() will perform the final switch. */ + + s->seat->pending_switch = s; + + /* if no devices are running, immediately perform the session switch */ + num_pending = session_device_try_pause_all(s); + if (!num_pending) + seat_complete_switch(s->seat); + + return 0; +} + +static int session_start_scope(Session *s) { + int r; + + assert(s); + assert(s->user); + + if (!s->scope) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *scope, *job = NULL; + const char *description; + + scope = strjoin("session-", s->id, ".scope", NULL); + if (!scope) + return log_oom(); + + description = strjoina("Session ", s->id, " of user ", s->user->name); + + r = manager_start_scope( + s->manager, + scope, + s->leader, + s->user->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ + &error, + &job); + if (r < 0) { + log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); + free(scope); + return r; + } else { + s->scope = scope; + + free(s->scope_job); + s->scope_job = job; + } + } + + if (s->scope) + (void) hashmap_put(s->manager->session_units, s->scope, s); + + return 0; +} + +int session_start(Session *s) { + int r; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (s->started) + return 0; + + r = user_start(s->user); + if (r < 0) + return r; + + /* Create cgroup */ + r = session_start_scope(s); + if (r < 0) + return r; + + log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER="PID_FMT, s->leader, + LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), + NULL); + + if (!dual_timestamp_is_set(&s->timestamp)) + dual_timestamp_get(&s->timestamp); + + if (s->seat) + seat_read_active_vt(s->seat); + + s->started = true; + + user_elect_display(s->user); + + /* Save data */ + session_save(s); + user_save(s->user); + if (s->seat) + seat_save(s->seat); + + /* Send signals */ + session_send_signal(s, true); + user_send_changed(s->user, "Sessions", "Display", NULL); + if (s->seat) { + if (s->seat->active == s) + seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL); + else + seat_send_changed(s->seat, "Sessions", NULL); + } + + return 0; +} + +static int session_stop_scope(Session *s, bool force) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(s); + + if (!s->scope) + return 0; + + /* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything + * that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log + * when killing any processes left after this point. */ + r = manager_abandon_scope(s->manager, s->scope, &error); + if (r < 0) + log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r)); + + /* Optionally, let's kill everything that's left now. */ + if (force || manager_shall_kill(s->manager, s->user->name)) { + char *job = NULL; + + r = manager_stop_unit(s->manager, s->scope, &error, &job); + if (r < 0) + return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); + + free(s->scope_job); + s->scope_job = job; + } else + s->scope_job = mfree(s->scope_job); + + return 0; +} + +int session_stop(Session *s, bool force) { + int r; + + assert(s); + + if (!s->user) + return -ESTALE; + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (s->seat) + seat_evict_position(s->seat, s); + + /* We are going down, don't care about FIFOs anymore */ + session_remove_fifo(s); + + /* Kill cgroup */ + r = session_stop_scope(s, force); + + s->stopping = true; + + user_elect_display(s->user); + + session_save(s); + user_save(s->user); + + return r; +} + +int session_finalize(Session *s) { + SessionDevice *sd; + + assert(s); + + if (!s->user) + return -ESTALE; + + if (s->started) + log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER="PID_FMT, s->leader, + LOG_MESSAGE("Removed session %s.", s->id), + NULL); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (s->seat) + seat_evict_position(s->seat, s); + + /* Kill session devices */ + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + (void) unlink(s->state_file); + session_add_to_gc_queue(s); + user_add_to_gc_queue(s->user); + + if (s->started) { + session_send_signal(s, false); + s->started = false; + } + + if (s->seat) { + if (s->seat->active == s) + seat_set_active(s->seat, NULL); + + seat_save(s->seat); + seat_send_changed(s->seat, "Sessions", NULL); + } + + user_save(s->user); + user_send_changed(s->user, "Sessions", "Display", NULL); + + return 0; +} + +static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { + Session *s = userdata; + + assert(es); + assert(s); + + session_stop(s, false); + return 0; +} + +int session_release(Session *s) { + assert(s); + + if (!s->started || s->stopping) + return 0; + + if (s->timer_event_source) + return 0; + + return sd_event_add_time(s->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + RELEASE_USEC, 0, + release_timeout_callback, s); +} + +bool session_is_active(Session *s) { + assert(s); + + if (!s->seat) + return true; + + return s->seat->active == s; +} + +static int get_tty_atime(const char *tty, usec_t *atime) { + _cleanup_free_ char *p = NULL; + struct stat st; + + assert(tty); + assert(atime); + + if (!path_is_absolute(tty)) { + p = strappend("/dev/", tty); + if (!p) + return -ENOMEM; + + tty = p; + } else if (!path_startswith(tty, "/dev/")) + return -ENOENT; + + if (lstat(tty, &st) < 0) + return -errno; + + *atime = timespec_load(&st.st_atim); + return 0; +} + +static int get_process_ctty_atime(pid_t pid, usec_t *atime) { + _cleanup_free_ char *p = NULL; + int r; + + assert(pid > 0); + assert(atime); + + r = get_ctty(pid, NULL, &p); + if (r < 0) + return r; + + return get_tty_atime(p, atime); +} + +int session_get_idle_hint(Session *s, dual_timestamp *t) { + usec_t atime = 0, n; + int r; + + assert(s); + + /* Explicit idle hint is set */ + if (s->idle_hint) { + if (t) + *t = s->idle_hint_timestamp; + + return s->idle_hint; + } + + /* Graphical sessions should really implement a real + * idle hint logic */ + if (SESSION_TYPE_IS_GRAPHICAL(s->type)) + goto dont_know; + + /* For sessions with an explicitly configured tty, let's check + * its atime */ + if (s->tty) { + r = get_tty_atime(s->tty, &atime); + if (r >= 0) + goto found_atime; + } + + /* For sessions with a leader but no explicitly configured + * tty, let's check the controlling tty of the leader */ + if (s->leader > 0) { + r = get_process_ctty_atime(s->leader, &atime); + if (r >= 0) + goto found_atime; + } + +dont_know: + if (t) + *t = s->idle_hint_timestamp; + + return 0; + +found_atime: + if (t) + dual_timestamp_from_realtime(t, atime); + + n = now(CLOCK_REALTIME); + + if (s->manager->idle_action_usec <= 0) + return 0; + + return atime + s->manager->idle_action_usec <= n; +} + +void session_set_idle_hint(Session *s, bool b) { + assert(s); + + if (s->idle_hint == b) + return; + + s->idle_hint = b; + dual_timestamp_get(&s->idle_hint_timestamp); + + session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + + if (s->seat) + seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + + user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); + manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); +} + +int session_get_locked_hint(Session *s) { + assert(s); + + return s->locked_hint; +} + +void session_set_locked_hint(Session *s, bool b) { + assert(s); + + if (s->locked_hint == b) + return; + + s->locked_hint = b; + + session_send_changed(s, "LockedHint", NULL); +} + +static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Session *s = userdata; + + assert(s); + assert(s->fifo_fd == fd); + + /* EOF on the FIFO means the session died abnormally. */ + + session_remove_fifo(s); + session_stop(s, false); + + return 1; +} + +int session_create_fifo(Session *s) { + int r; + + assert(s); + + /* Create FIFO */ + if (!s->fifo_path) { + r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); + if (r < 0) + return r; + + if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) + return -ENOMEM; + + if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) + return -errno; + } + + /* Open reading side */ + if (s->fifo_fd < 0) { + s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + if (s->fifo_fd < 0) + return -errno; + + } + + if (!s->fifo_event_source) { + r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s); + if (r < 0) + return r; + + /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new + * sessions). */ + r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); + if (r < 0) + return r; + } + + /* Open writing side */ + r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + if (r < 0) + return -errno; + + return r; +} + +static void session_remove_fifo(Session *s) { + assert(s); + + s->fifo_event_source = sd_event_source_unref(s->fifo_event_source); + s->fifo_fd = safe_close(s->fifo_fd); + + if (s->fifo_path) { + unlink(s->fifo_path); + s->fifo_path = mfree(s->fifo_path); + } +} + +bool session_check_gc(Session *s, bool drop_not_started) { + assert(s); + + if (drop_not_started && !s->started) + return false; + + if (!s->user) + return false; + + if (s->fifo_fd >= 0) { + if (pipe_eof(s->fifo_fd) <= 0) + return true; + } + + if (s->scope_job && manager_job_is_active(s->manager, s->scope_job)) + return true; + + if (s->scope && manager_unit_is_active(s->manager, s->scope)) + return true; + + return false; +} + +void session_add_to_gc_queue(Session *s) { + assert(s); + + if (s->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s); + s->in_gc_queue = true; +} + +SessionState session_get_state(Session *s) { + assert(s); + + /* always check closing first */ + if (s->stopping || s->timer_event_source) + return SESSION_CLOSING; + + if (s->scope_job || s->fifo_fd < 0) + return SESSION_OPENING; + + if (session_is_active(s)) + return SESSION_ACTIVE; + + return SESSION_ONLINE; +} + +int session_kill(Session *s, KillWho who, int signo) { + assert(s); + + if (!s->scope) + return -ESRCH; + + return manager_kill_unit(s->manager, s->scope, who, signo, NULL); +} + +static int session_open_vt(Session *s) { + char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; + + if (s->vtnr < 1) + return -ENODEV; + + if (s->vtfd >= 0) + return s->vtfd; + + sprintf(path, "/dev/tty%u", s->vtnr); + s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); + if (s->vtfd < 0) + return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id); + + return s->vtfd; +} + +int session_prepare_vt(Session *s) { + int vt, r; + struct vt_mode mode = { 0 }; + + if (s->vtnr < 1) + return 0; + + vt = session_open_vt(s); + if (vt < 0) + return vt; + + r = fchown(vt, s->user->uid, -1); + if (r < 0) { + r = log_error_errno(errno, + "Cannot change owner of /dev/tty%u: %m", + s->vtnr); + goto error; + } + + r = ioctl(vt, KDSKBMODE, K_OFF); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set K_OFF on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + r = ioctl(vt, KDSETMODE, KD_GRAPHICS); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set KD_GRAPHICS on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. + * So we need a dummy handler here which just acknowledges *all* VT + * switch requests. */ + mode.mode = VT_PROCESS; + mode.relsig = SIGRTMIN; + mode.acqsig = SIGRTMIN + 1; + r = ioctl(vt, VT_SETMODE, &mode); + if (r < 0) { + r = log_error_errno(errno, + "Cannot set VT_PROCESS on /dev/tty%u: %m", + s->vtnr); + goto error; + } + + return 0; + +error: + session_restore_vt(s); + return r; +} + +void session_restore_vt(Session *s) { + + static const struct vt_mode mode = { + .mode = VT_AUTO, + }; + + _cleanup_free_ char *utf8 = NULL; + int vt, kb, old_fd; + + /* We need to get a fresh handle to the virtual terminal, + * since the old file-descriptor is potentially in a hung-up + * state after the controlling process exited; we do a + * little dance to avoid having the terminal be available + * for reuse before we've cleaned it up. + */ + old_fd = s->vtfd; + s->vtfd = -1; + + vt = session_open_vt(s); + safe_close(old_fd); + + if (vt < 0) + return; + + (void) ioctl(vt, KDSETMODE, KD_TEXT); + + if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1') + kb = K_UNICODE; + else + kb = K_XLATE; + + (void) ioctl(vt, KDSKBMODE, kb); + + (void) ioctl(vt, VT_SETMODE, &mode); + (void) fchown(vt, 0, (gid_t) -1); + + s->vtfd = safe_close(s->vtfd); +} + +void session_leave_vt(Session *s) { + int r; + + assert(s); + + /* This is called whenever we get a VT-switch signal from the kernel. + * We acknowledge all of them unconditionally. Note that session are + * free to overwrite those handlers and we only register them for + * sessions with controllers. Legacy sessions are not affected. + * However, if we switch from a non-legacy to a legacy session, we must + * make sure to pause all device before acknowledging the switch. We + * process the real switch only after we are notified via sysfs, so the + * legacy session might have already started using the devices. If we + * don't pause the devices before the switch, we might confuse the + * session we switch to. */ + + if (s->vtfd < 0) + return; + + session_device_pause_all(s); + r = ioctl(s->vtfd, VT_RELDISP, 1); + if (r < 0) + log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id); +} + +bool session_is_controller(Session *s, const char *sender) { + assert(s); + + return streq_ptr(s->controller, sender); +} + +static void session_release_controller(Session *s, bool notify) { + _cleanup_free_ char *name = NULL; + SessionDevice *sd; + + if (!s->controller) + return; + + name = s->controller; + + /* By resetting the controller before releasing the devices, we won't + * send notification signals. This avoids sending useless notifications + * if the controller is released on disconnects. */ + if (!notify) + s->controller = NULL; + + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + s->controller = NULL; + s->track = sd_bus_track_unref(s->track); +} + +static int on_bus_track(sd_bus_track *track, void *userdata) { + Session *s = userdata; + + assert(track); + assert(s); + + session_drop_controller(s); + + return 0; +} + +int session_set_controller(Session *s, const char *sender, bool force) { + _cleanup_free_ char *name = NULL; + int r; + + assert(s); + assert(sender); + + if (session_is_controller(s, sender)) + return 0; + if (s->controller && !force) + return -EBUSY; + + name = strdup(sender); + if (!name) + return -ENOMEM; + + s->track = sd_bus_track_unref(s->track); + r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s); + if (r < 0) + return r; + + r = sd_bus_track_add_name(s->track, name); + if (r < 0) + return r; + + /* When setting a session controller, we forcibly mute the VT and set + * it into graphics-mode. Applications can override that by changing + * VT state after calling TakeControl(). However, this serves as a good + * default and well-behaving controllers can now ignore VTs entirely. + * Note that we reset the VT on ReleaseControl() and if the controller + * exits. + * If logind crashes/restarts, we restore the controller during restart + * or reset the VT in case it crashed/exited, too. */ + r = session_prepare_vt(s); + if (r < 0) { + s->track = sd_bus_track_unref(s->track); + return r; + } + + session_release_controller(s, true); + s->controller = name; + name = NULL; + session_save(s); + + return 0; +} + +void session_drop_controller(Session *s) { + assert(s); + + if (!s->controller) + return; + + s->track = sd_bus_track_unref(s->track); + session_release_controller(s, false); + session_save(s); + session_restore_vt(s); +} + +static const char* const session_state_table[_SESSION_STATE_MAX] = { + [SESSION_OPENING] = "opening", + [SESSION_ONLINE] = "online", + [SESSION_ACTIVE] = "active", + [SESSION_CLOSING] = "closing" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); + +static const char* const session_type_table[_SESSION_TYPE_MAX] = { + [SESSION_UNSPECIFIED] = "unspecified", + [SESSION_TTY] = "tty", + [SESSION_X11] = "x11", + [SESSION_WAYLAND] = "wayland", + [SESSION_MIR] = "mir", + [SESSION_WEB] = "web", +}; + +DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); + +static const char* const session_class_table[_SESSION_CLASS_MAX] = { + [SESSION_USER] = "user", + [SESSION_GREETER] = "greeter", + [SESSION_LOCK_SCREEN] = "lock-screen", + [SESSION_BACKGROUND] = "background" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_LEADER] = "leader", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/grp-login/systemd-logind/logind-session.h b/src/grp-login/systemd-logind/logind-session.h new file mode 100644 index 0000000000..976046d847 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-session.h @@ -0,0 +1,186 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" +#include "basic/login-util.h" + +typedef enum KillWho KillWho; +typedef struct Session Session; + +#include "logind-user.h" + +typedef enum SessionState { + SESSION_OPENING, /* Session scope is being created */ + SESSION_ONLINE, /* Logged in */ + SESSION_ACTIVE, /* Logged in and in the fg */ + SESSION_CLOSING, /* Logged out, but scope is still there */ + _SESSION_STATE_MAX, + _SESSION_STATE_INVALID = -1 +} SessionState; + +typedef enum SessionClass { + SESSION_USER, + SESSION_GREETER, + SESSION_LOCK_SCREEN, + SESSION_BACKGROUND, + _SESSION_CLASS_MAX, + _SESSION_CLASS_INVALID = -1 +} SessionClass; + +typedef enum SessionType { + SESSION_UNSPECIFIED, + SESSION_TTY, + SESSION_X11, + SESSION_WAYLAND, + SESSION_MIR, + SESSION_WEB, + _SESSION_TYPE_MAX, + _SESSION_TYPE_INVALID = -1 +} SessionType; + +#define SESSION_TYPE_IS_GRAPHICAL(type) IN_SET(type, SESSION_X11, SESSION_WAYLAND, SESSION_MIR) + +enum KillWho { + KILL_LEADER, + KILL_ALL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +}; + +struct Session { + Manager *manager; + + const char *id; + unsigned int position; + SessionType type; + SessionClass class; + + char *state_file; + + User *user; + + dual_timestamp timestamp; + + char *tty; + char *display; + + bool remote; + char *remote_user; + char *remote_host; + char *service; + char *desktop; + + char *scope; + char *scope_job; + + Seat *seat; + unsigned int vtnr; + int vtfd; + + pid_t leader; + uint32_t audit_id; + + int fifo_fd; + char *fifo_path; + + sd_event_source *fifo_event_source; + + bool idle_hint; + dual_timestamp idle_hint_timestamp; + + bool locked_hint; + + bool in_gc_queue:1; + bool started:1; + bool stopping:1; + + sd_bus_message *create_message; + + sd_event_source *timer_event_source; + + char *controller; + Hashmap *devices; + sd_bus_track *track; + + LIST_FIELDS(Session, sessions_by_user); + LIST_FIELDS(Session, sessions_by_seat); + + LIST_FIELDS(Session, gc_queue); +}; + +Session *session_new(Manager *m, const char *id); +void session_free(Session *s); +void session_set_user(Session *s, User *u); +bool session_check_gc(Session *s, bool drop_not_started); +void session_add_to_gc_queue(Session *s); +int session_activate(Session *s); +bool session_is_active(Session *s); +int session_get_idle_hint(Session *s, dual_timestamp *t); +void session_set_idle_hint(Session *s, bool b); +int session_get_locked_hint(Session *s); +void session_set_locked_hint(Session *s, bool b); +int session_create_fifo(Session *s); +int session_start(Session *s); +int session_stop(Session *s, bool force); +int session_finalize(Session *s); +int session_release(Session *s); +int session_save(Session *s); +int session_load(Session *s); +int session_kill(Session *s, KillWho who, int signo); + +SessionState session_get_state(Session *u); + +extern const sd_bus_vtable session_vtable[]; +int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error); +int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *session_bus_path(Session *s); + +int session_send_signal(Session *s, bool new_session); +int session_send_changed(Session *s, const char *properties, ...) _sentinel_; +int session_send_lock(Session *s, bool lock); +int session_send_lock_all(Manager *m, bool lock); + +int session_send_create_reply(Session *s, sd_bus_error *error); + +const char* session_state_to_string(SessionState t) _const_; +SessionState session_state_from_string(const char *s) _pure_; + +const char* session_type_to_string(SessionType t) _const_; +SessionType session_type_from_string(const char *s) _pure_; + +const char* session_class_to_string(SessionClass t) _const_; +SessionClass session_class_from_string(const char *s) _pure_; + +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; + +int session_prepare_vt(Session *s); +void session_restore_vt(Session *s); +void session_leave_vt(Session *s); + +bool session_is_controller(Session *s, const char *sender); +int session_set_controller(Session *s, const char *sender, bool force); +void session_drop_controller(Session *s); + +int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-user-dbus.c b/src/grp-login/systemd-logind/logind-user-dbus.c new file mode 100644 index 0000000000..57d1c857e6 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user-dbus.c @@ -0,0 +1,399 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "shared/bus-util.h" + +#include "logind-user.h" +#include "logind.h" + +static int property_get_display( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + p = u->display ? session_bus_path(u->display) : strdup("/"); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p); +} + +static int property_get_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", user_state_to_string(user_get_state(u))); +} + +static int property_get_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + Session *session; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = sd_bus_message_open_container(reply, 'a', "(so)"); + if (r < 0) + return r; + + LIST_FOREACH(sessions_by_user, session, u->sessions) { + _cleanup_free_ char *p = NULL; + + p = session_bus_path(session); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(so)", session->id, p); + if (r < 0) + return r; + + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_idle_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0); +} + +static int property_get_idle_since_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + dual_timestamp t = DUAL_TIMESTAMP_NULL; + uint64_t k; + + assert(bus); + assert(reply); + assert(u); + + user_get_idle_hint(u, &t); + k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; + + return sd_bus_message_append(reply, "t", k); +} + +static int property_get_linger( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + User *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = user_check_linger_file(u); + + return sd_bus_message_append(reply, "b", r > 0); +} + +int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + User *u = userdata; + int r; + + assert(message); + assert(u); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + u->uid, + &u->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = user_stop(u, true); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + User *u = userdata; + int32_t signo; + int r; + + assert(message); + assert(u); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.login1.manage", + NULL, + false, + u->uid, + &u->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = sd_bus_message_read(message, "i", &signo); + if (r < 0) + return r; + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + + r = user_kill(u, signo); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable user_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), + SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), + + SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + uid_t uid; + User *user; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/login1/user/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_owner_uid(creds, &uid); + } else { + const char *p; + + p = startswith(path, "/org/freedesktop/login1/user/_"); + if (!p) + return 0; + + r = parse_uid(p, &uid); + } + if (r < 0) + return 0; + + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return 0; + + *found = user; + return 1; +} + +char *user_bus_path(User *u) { + char *s; + + assert(u); + + if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0) + return NULL; + + return s; +} + +int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + sd_bus_message *message; + Manager *m = userdata; + User *user; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(user, m->users, i) { + char *p; + + p = user_bus_path(user); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + message = sd_bus_get_current_message(bus); + if (message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t uid; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); + if (r >= 0) { + r = sd_bus_creds_get_owner_uid(creds, &uid); + if (r >= 0) { + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (user) { + r = strv_extend(&l, "/org/freedesktop/login1/user/self"); + if (r < 0) + return r; + } + } + } + } + + *nodes = l; + l = NULL; + + return 1; +} + +int user_send_signal(User *u, bool new_user) { + _cleanup_free_ char *p = NULL; + + assert(u); + + p = user_bus_path(u); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + u->manager->bus, + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + new_user ? "UserNew" : "UserRemoved", + "uo", (uint32_t) u->uid, p); +} + +int user_send_changed(User *u, const char *properties, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(u); + + if (!u->started) + return 0; + + p = user_bus_path(u); + if (!p) + return -ENOMEM; + + l = strv_from_stdarg_alloca(properties); + + return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l); +} diff --git a/src/grp-login/systemd-logind/logind-user.c b/src/grp-login/systemd-logind/logind-user.c new file mode 100644 index 0000000000..5a2750b72b --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user.c @@ -0,0 +1,917 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/smack-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/clean-ipc.h" +#include "shared/conf-parser.h" + +#include "logind-user.h" + +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { + _cleanup_(user_freep) User *u = NULL; + char lu[DECIMAL_STR_MAX(uid_t) + 1]; + int r; + + assert(out); + assert(m); + assert(name); + + u = new0(User, 1); + if (!u) + return -ENOMEM; + + u->manager = m; + u->uid = uid; + u->gid = gid; + xsprintf(lu, UID_FMT, uid); + + u->name = strdup(name); + if (!u->name) + return -ENOMEM; + + if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) + return -ENOMEM; + + if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) + return -ENOMEM; + + r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); + if (r < 0) + return r; + + r = unit_name_build("user", lu, ".service", &u->service); + if (r < 0) + return r; + + r = hashmap_put(m->users, UID_TO_PTR(uid), u); + if (r < 0) + return r; + + r = hashmap_put(m->user_units, u->slice, u); + if (r < 0) + return r; + + r = hashmap_put(m->user_units, u->service, u); + if (r < 0) + return r; + + *out = u; + u = NULL; + return 0; +} + +User *user_free(User *u) { + if (!u) + return NULL; + + if (u->in_gc_queue) + LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u); + + while (u->sessions) + session_free(u->sessions); + + if (u->service) + hashmap_remove_value(u->manager->user_units, u->service, u); + + if (u->slice) + hashmap_remove_value(u->manager->user_units, u->slice, u); + + hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); + + u->slice_job = mfree(u->slice_job); + u->service_job = mfree(u->service_job); + + u->service = mfree(u->service); + u->slice = mfree(u->slice); + u->runtime_path = mfree(u->runtime_path); + u->state_file = mfree(u->state_file); + u->name = mfree(u->name); + + return mfree(u); +} + +static int user_save_internal(User *u) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(u); + assert(u->state_file); + + r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(u->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "NAME=%s\n" + "STATE=%s\n", + u->name, + user_state_to_string(user_get_state(u))); + + /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ + if (u->runtime_path) + fprintf(f, "RUNTIME=%s\n", u->runtime_path); + + if (u->service_job) + fprintf(f, "SERVICE_JOB=%s\n", u->service_job); + + if (u->slice_job) + fprintf(f, "SLICE_JOB=%s\n", u->slice_job); + + if (u->display) + fprintf(f, "DISPLAY=%s\n", u->display->id); + + if (dual_timestamp_is_set(&u->timestamp)) + fprintf(f, + "REALTIME="USEC_FMT"\n" + "MONOTONIC="USEC_FMT"\n", + u->timestamp.realtime, + u->timestamp.monotonic); + + if (u->sessions) { + Session *i; + bool first; + + fputs("SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nSEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + + fputs("\nACTIVE_SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!session_is_active(i)) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nONLINE_SESSIONS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (session_get_state(i) == SESSION_CLOSING) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->id, f); + } + + fputs("\nACTIVE_SEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (!session_is_active(i) || !i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + + fputs("\nONLINE_SEATS=", f); + first = true; + LIST_FOREACH(sessions_by_user, i, u->sessions) { + if (session_get_state(i) == SESSION_CLOSING || !i->seat) + continue; + + if (first) + first = false; + else + fputc(' ', f); + + fputs(i->seat->id, f); + } + fputc('\n', f); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, u->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(u->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save user data %s: %m", u->state_file); +} + +int user_save(User *u) { + assert(u); + + if (!u->started) + return 0; + + return user_save_internal (u); +} + +int user_load(User *u) { + _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; + Session *s = NULL; + int r; + + assert(u); + + r = parse_env_file(u->state_file, NEWLINE, + "SERVICE_JOB", &u->service_job, + "SLICE_JOB", &u->slice_job, + "DISPLAY", &display, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + NULL); + if (r < 0) { + if (r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to read %s: %m", u->state_file); + } + + if (display) + s = hashmap_get(u->manager->sessions, display); + + if (s && s->display && display_is_local(s->display)) + u->display = s; + + if (realtime) + timestamp_deserialize(realtime, &u->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &u->timestamp.monotonic); + + return r; +} + +static int user_mkdir_runtime_path(User *u) { + int r; + + assert(u); + + r = mkdir_safe_label("/run/user", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /run/user: %m"); + + if (path_is_mount_point(u->runtime_path, 0) <= 0) { + _cleanup_free_ char *t = NULL; + + (void) mkdir_label(u->runtime_path, 0700); + + if (mac_smack_use()) + r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); + else + r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); + if (r < 0) { + r = log_oom(); + goto fail; + } + + r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); + if (r < 0) { + if (errno != EPERM) { + r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); + goto fail; + } + + /* Lacking permissions, maybe + * CAP_SYS_ADMIN-less container? In this case, + * just use a normal directory. */ + + r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); + if (r < 0) { + log_error_errno(r, "Failed to change runtime directory ownership and mode: %m"); + goto fail; + } + } + + r = label_fix(u->runtime_path, false, false); + if (r < 0) + log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path); + } + + return 0; + +fail: + /* Try to clean up, but ignore errors */ + (void) rmdir(u->runtime_path); + return r; +} + +static int user_start_slice(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *description; + char *job; + int r; + + assert(u); + + u->slice_job = mfree(u->slice_job); + description = strjoina("User Slice of ", u->name); + + r = manager_start_slice( + u->manager, + u->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + u->manager->user_tasks_max, + &error, + &job); + if (r >= 0) + u->slice_job = job; + else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", + u->slice, bus_error_message(&error, r), error.name); + + return 0; +} + +static int user_start_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + u->service_job = mfree(u->service_job); + + r = manager_start_unit( + u->manager, + u->service, + &error, + &job); + if (r < 0) { + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); + } else { + u->service_job = job; + } + + return 0; +} + +int user_start(User *u) { + int r; + + assert(u); + + if (u->started && !u->stopping) + return 0; + + /* + * If u->stopping is set, the user is marked for removal and the slice + * and service stop-jobs are queued. We have to clear that flag before + * queing the start-jobs again. If they succeed, the user object can be + * re-used just fine (pid1 takes care of job-ordering and proper + * restart), but if they fail, we want to force another user_stop() so + * possibly pending units are stopped. + * Note that we don't clear u->started, as we have no clue what state + * the user is in on failure here. Hence, we pretend the user is + * running so it will be properly taken down by GC. However, we clearly + * return an error from user_start() in that case, so no further + * reference to the user is taken. + */ + u->stopping = false; + + if (!u->started) { + log_debug("New user %s logged in.", u->name); + + /* Make XDG_RUNTIME_DIR */ + r = user_mkdir_runtime_path(u); + if (r < 0) + return r; + } + + /* Create cgroup */ + r = user_start_slice(u); + if (r < 0) + return r; + + /* Save the user data so far, because pam_systemd will read the + * XDG_RUNTIME_DIR out of it while starting up systemd --user. + * We need to do user_save_internal() because we have not + * "officially" started yet. */ + user_save_internal(u); + + /* Spawn user systemd */ + r = user_start_service(u); + if (r < 0) + return r; + + if (!u->started) { + if (!dual_timestamp_is_set(&u->timestamp)) + dual_timestamp_get(&u->timestamp); + user_send_signal(u, true); + u->started = true; + } + + /* Save new user data */ + user_save(u); + + return 0; +} + +static int user_stop_slice(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + r = manager_stop_unit(u->manager, u->slice, &error, &job); + if (r < 0) { + log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); + return r; + } + + free(u->slice_job); + u->slice_job = job; + + return r; +} + +static int user_stop_service(User *u) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job; + int r; + + assert(u); + + r = manager_stop_unit(u->manager, u->service, &error, &job); + if (r < 0) { + log_error("Failed to stop user service: %s", bus_error_message(&error, r)); + return r; + } + + free(u->service_job); + u->service_job = job; + + return r; +} + +static int user_remove_runtime_path(User *u) { + int r; + + assert(u); + + r = rm_rf(u->runtime_path, 0); + if (r < 0) + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); + + /* Ignore cases where the directory isn't mounted, as that's + * quite possible, if we lacked the permissions to mount + * something */ + r = umount2(u->runtime_path, MNT_DETACH); + if (r < 0 && errno != EINVAL && errno != ENOENT) + log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); + + r = rm_rf(u->runtime_path, REMOVE_ROOT); + if (r < 0) + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); + + return r; +} + +int user_stop(User *u, bool force) { + Session *s; + int r = 0, k; + assert(u); + + /* Stop jobs have already been queued */ + if (u->stopping) { + user_save(u); + return r; + } + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + k = session_stop(s, force); + if (k < 0) + r = k; + } + + /* Kill systemd */ + k = user_stop_service(u); + if (k < 0) + r = k; + + /* Kill cgroup */ + k = user_stop_slice(u); + if (k < 0) + r = k; + + u->stopping = true; + + user_save(u); + + return r; +} + +int user_finalize(User *u) { + Session *s; + int r = 0, k; + + assert(u); + + if (u->started) + log_debug("User %s logged out.", u->name); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + k = session_finalize(s); + if (k < 0) + r = k; + } + + /* Kill XDG_RUNTIME_DIR */ + k = user_remove_runtime_path(u); + if (k < 0) + r = k; + + /* Clean SysV + POSIX IPC objects */ + if (u->manager->remove_ipc) { + k = clean_ipc(u->uid); + if (k < 0) + r = k; + } + + unlink(u->state_file); + user_add_to_gc_queue(u); + + if (u->started) { + user_send_signal(u, false); + u->started = false; + } + + return r; +} + +int user_get_idle_hint(User *u, dual_timestamp *t) { + Session *s; + bool idle_hint = true; + dual_timestamp ts = DUAL_TIMESTAMP_NULL; + + assert(u); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + dual_timestamp k; + int ih; + + ih = session_get_idle_hint(s, &k); + if (ih < 0) + return ih; + + if (!ih) { + if (!idle_hint) { + if (k.monotonic < ts.monotonic) + ts = k; + } else { + idle_hint = false; + ts = k; + } + } else if (idle_hint) { + + if (k.monotonic > ts.monotonic) + ts = k; + } + } + + if (t) + *t = ts; + + return idle_hint; +} + +int user_check_linger_file(User *u) { + _cleanup_free_ char *cc = NULL; + char *p = NULL; + + cc = cescape(u->name); + if (!cc) + return -ENOMEM; + + p = strjoina("/var/lib/systemd/linger/", cc); + + return access(p, F_OK) >= 0; +} + +bool user_check_gc(User *u, bool drop_not_started) { + assert(u); + + if (drop_not_started && !u->started) + return false; + + if (u->sessions) + return true; + + if (user_check_linger_file(u) > 0) + return true; + + if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) + return true; + + if (u->service_job && manager_job_is_active(u->manager, u->service_job)) + return true; + + return false; +} + +void user_add_to_gc_queue(User *u) { + assert(u); + + if (u->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u); + u->in_gc_queue = true; +} + +UserState user_get_state(User *u) { + Session *i; + + assert(u); + + if (u->stopping) + return USER_CLOSING; + + if (!u->started || u->slice_job || u->service_job) + return USER_OPENING; + + if (u->sessions) { + bool all_closing = true; + + LIST_FOREACH(sessions_by_user, i, u->sessions) { + SessionState state; + + state = session_get_state(i); + if (state == SESSION_ACTIVE) + return USER_ACTIVE; + if (state != SESSION_CLOSING) + all_closing = false; + } + + return all_closing ? USER_CLOSING : USER_ONLINE; + } + + if (user_check_linger_file(u) > 0) + return USER_LINGERING; + + return USER_CLOSING; +} + +int user_kill(User *u, int signo) { + assert(u); + + return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL); +} + +static bool elect_display_filter(Session *s) { + /* Return true if the session is a candidate for the user’s ‘primary + * session’ or ‘display’. */ + assert(s); + + return (s->class == SESSION_USER && !s->stopping); +} + +static int elect_display_compare(Session *s1, Session *s2) { + /* Indexed by SessionType. Lower numbers mean more preferred. */ + const int type_ranks[_SESSION_TYPE_MAX] = { + [SESSION_UNSPECIFIED] = 0, + [SESSION_TTY] = -2, + [SESSION_X11] = -3, + [SESSION_WAYLAND] = -3, + [SESSION_MIR] = -3, + [SESSION_WEB] = -1, + }; + + /* Calculate the partial order relationship between s1 and s2, + * returning < 0 if s1 is preferred as the user’s ‘primary session’, + * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2 + * is preferred. + * + * s1 or s2 may be NULL. */ + if (!s1 && !s2) + return 0; + + if ((s1 == NULL) != (s2 == NULL)) + return (s1 == NULL) - (s2 == NULL); + + if (s1->stopping != s2->stopping) + return s1->stopping - s2->stopping; + + if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER)) + return (s1->class != SESSION_USER) - (s2->class != SESSION_USER); + + if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID)) + return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID); + + if (s1->type != s2->type) + return type_ranks[s1->type] - type_ranks[s2->type]; + + return 0; +} + +void user_elect_display(User *u) { + Session *s; + + assert(u); + + /* This elects a primary session for each user, which we call + * the "display". We try to keep the assignment stable, but we + * "upgrade" to better choices. */ + log_debug("Electing new display for user %s", u->name); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + if (!elect_display_filter(s)) { + log_debug("Ignoring session %s", s->id); + continue; + } + + if (elect_display_compare(s, u->display) < 0) { + log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-"); + u->display = s; + } + } +} + +static const char* const user_state_table[_USER_STATE_MAX] = { + [USER_OFFLINE] = "offline", + [USER_OPENING] = "opening", + [USER_LINGERING] = "lingering", + [USER_ONLINE] = "online", + [USER_ACTIVE] = "active", + [USER_CLOSING] = "closing" +}; + +DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); + +int config_parse_tmpfs_size( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* First, try to parse as percentage */ + r = parse_percent(rvalue); + if (r > 0 && r < 100) + *sz = physical_memory_scale(r, 100U); + else { + uint64_t k; + + /* If the passed argument was not a percentage, or out of range, parse as byte size */ + + r = parse_size(rvalue, 1024, &k); + if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + return 0; + } + + *sz = PAGE_ALIGN((size_t) k); + } + + return 0; +} + +int config_parse_user_tasks_max( + 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 *m = data; + uint64_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* First, try to parse as percentage */ + r = parse_percent(rvalue); + if (r > 0 && r < 100) + k = system_tasks_max_scale(r, 100U); + else { + + /* If the passed argument was not a percentage, or out of range, parse as byte size */ + + r = safe_atou64(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue); + return 0; + } + } + + if (k <= 0 || k >= UINT64_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue); + return 0; + } + + *m = k; + return 0; +} diff --git a/src/grp-login/systemd-logind/logind-user.h b/src/grp-login/systemd-logind/logind-user.h new file mode 100644 index 0000000000..b23c59fa1d --- /dev/null +++ b/src/grp-login/systemd-logind/logind-user.h @@ -0,0 +1,94 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef struct User User; + +#include "logind.h" + +typedef enum UserState { + USER_OFFLINE, /* Not logged in at all */ + USER_OPENING, /* Is logging in */ + USER_LINGERING, /* Lingering has been enabled by the admin for this user */ + USER_ONLINE, /* User logged in */ + USER_ACTIVE, /* User logged in and has a session in the fg */ + USER_CLOSING, /* User logged out, but processes still remain and lingering is not enabled */ + _USER_STATE_MAX, + _USER_STATE_INVALID = -1 +} UserState; + +struct User { + Manager *manager; + uid_t uid; + gid_t gid; + char *name; + char *state_file; + char *runtime_path; + char *slice; + char *service; + + char *service_job; + char *slice_job; + + Session *display; + + dual_timestamp timestamp; + + bool in_gc_queue:1; + bool started:1; + bool stopping:1; + + LIST_HEAD(Session, sessions); + LIST_FIELDS(User, gc_queue); +}; + +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); +User *user_free(User *u); + +DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); + +bool user_check_gc(User *u, bool drop_not_started); +void user_add_to_gc_queue(User *u); +int user_start(User *u); +int user_stop(User *u, bool force); +int user_finalize(User *u); +UserState user_get_state(User *u); +int user_get_idle_hint(User *u, dual_timestamp *t); +int user_save(User *u); +int user_load(User *u); +int user_kill(User *u, int signo); +int user_check_linger_file(User *u); +void user_elect_display(User *u); + +extern const sd_bus_vtable user_vtable[]; +int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +char *user_bus_path(User *s); + +int user_send_signal(User *u, bool new_user); +int user_send_changed(User *u, const char *properties, ...) _sentinel_; + +const char* user_state_to_string(UserState s) _const_; +UserState user_state_from_string(const char *s) _pure_; + +int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-login/systemd-logind/logind-utmp.c b/src/grp-login/systemd-logind/logind-utmp.c new file mode 100644 index 0000000000..1e97083f78 --- /dev/null +++ b/src/grp-login/systemd-logind/logind-utmp.c @@ -0,0 +1,184 @@ +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/formats-util.h" +#include "basic/special.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/utmp-wtmp.h" + +#include "logind.h" + +_const_ static usec_t when_wall(usec_t n, usec_t elapse) { + + usec_t left; + unsigned int i; + static const int wall_timers[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 25, 40, 55, 70, 100, 130, 150, 180, + }; + + /* If the time is already passed, then don't announce */ + if (n >= elapse) + return 0; + + left = elapse - n; + + for (i = 1; i < ELEMENTSOF(wall_timers); i++) + if (wall_timers[i] * USEC_PER_MINUTE >= left) + return left - wall_timers[i-1] * USEC_PER_MINUTE; + + return left % USEC_PER_HOUR; +} + +bool logind_wall_tty_filter(const char *tty, void *userdata) { + + Manager *m = userdata; + + assert(m); + + if (!startswith(tty, "/dev/") || !m->scheduled_shutdown_tty) + return true; + + return !streq(tty + 5, m->scheduled_shutdown_tty); +} + +static int warn_wall(Manager *m, usec_t n) { + char date[FORMAT_TIMESTAMP_MAX] = {}; + _cleanup_free_ char *l = NULL; + usec_t left; + int r; + + assert(m); + + if (!m->enable_wall_messages) + return 0; + + left = m->scheduled_shutdown_timeout > n; + + r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", + strempty(m->wall_message), + isempty(m->wall_message) ? "" : "\n", + m->scheduled_shutdown_type, + left ? "at " : "NOW", + left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : ""); + if (r < 0) { + log_oom(); + return 0; + } + + utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), + m->scheduled_shutdown_tty, logind_wall_tty_filter, m); + + return 1; +} + +static int wall_message_timeout_handler( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + Manager *m = userdata; + usec_t n, next; + int r; + + assert(m); + assert(s == m->wall_message_timeout_source); + + n = now(CLOCK_REALTIME); + + r = warn_wall(m, n); + if (r == 0) + return 0; + + next = when_wall(n, m->scheduled_shutdown_timeout); + if (next > 0) { + r = sd_event_source_set_time(s, n + next); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); + } + + return 0; +} + +int manager_setup_wall_message_timer(Manager *m) { + + usec_t n, elapse; + int r; + + assert(m); + + n = now(CLOCK_REALTIME); + elapse = m->scheduled_shutdown_timeout; + + /* wall message handling */ + + if (isempty(m->scheduled_shutdown_type)) { + warn_wall(m, n); + return 0; + } + + if (elapse < n) + return 0; + + /* Warn immediately if less than 15 minutes are left */ + if (elapse - n < 15 * USEC_PER_MINUTE) { + r = warn_wall(m, n); + if (r == 0) + return 0; + } + + elapse = when_wall(n, elapse); + if (elapse == 0) + return 0; + + if (m->wall_message_timeout_source) { + r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + + r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); + } else { + r = sd_event_add_time(m->event, &m->wall_message_timeout_source, + CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); + if (r < 0) + return log_error_errno(r, "sd_event_add_time() failed. %m"); + } + + return 0; +} diff --git a/src/grp-login/systemd-logind/logind.c b/src/grp-login/systemd-logind/logind.c new file mode 100644 index 0000000000..84e23beef0 --- /dev/null +++ b/src/grp-login/systemd-logind/logind.c @@ -0,0 +1,1209 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" +#include "shared/udev-util.h" + +#include "logind.h" + +static void manager_free(Manager *m); + +static void manager_reset_config(Manager *m) { + m->n_autovts = 6; + m->reserve_vt = 6; + m->remove_ipc = true; + m->inhibit_delay_max = 5 * USEC_PER_SEC; + m->handle_power_key = HANDLE_POWEROFF; + m->handle_suspend_key = HANDLE_SUSPEND; + m->handle_hibernate_key = HANDLE_HIBERNATE; + m->handle_lid_switch = HANDLE_SUSPEND; + m->handle_lid_switch_docked = HANDLE_IGNORE; + m->power_key_ignore_inhibited = false; + m->suspend_key_ignore_inhibited = false; + m->hibernate_key_ignore_inhibited = false; + m->lid_switch_ignore_inhibited = true; + + m->holdoff_timeout_usec = 30 * USEC_PER_SEC; + + m->idle_action_usec = 30 * USEC_PER_MINUTE; + m->idle_action = HANDLE_IGNORE; + + m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */ + m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */ + m->sessions_max = 8192; + m->inhibitors_max = 8192; + + m->kill_user_processes = KILL_USER_PROCESSES; + + m->kill_only_users = strv_free(m->kill_only_users); + m->kill_exclude_users = strv_free(m->kill_exclude_users); +} + +static Manager *manager_new(void) { + Manager *m; + int r; + + m = new0(Manager, 1); + if (!m) + return NULL; + + m->console_active_fd = -1; + m->reserve_vt_fd = -1; + + m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); + + m->devices = hashmap_new(&string_hash_ops); + m->seats = hashmap_new(&string_hash_ops); + m->sessions = hashmap_new(&string_hash_ops); + m->users = hashmap_new(NULL); + m->inhibitors = hashmap_new(&string_hash_ops); + m->buttons = hashmap_new(&string_hash_ops); + + m->user_units = hashmap_new(&string_hash_ops); + m->session_units = hashmap_new(&string_hash_ops); + + if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) + goto fail; + + m->udev = udev_new(); + if (!m->udev) + goto fail; + + r = sd_event_default(&m->event); + if (r < 0) + goto fail; + + sd_event_set_watchdog(m->event, true); + + manager_reset_config(m); + + return m; + +fail: + manager_free(m); + return NULL; +} + +static void manager_free(Manager *m) { + Session *session; + User *u; + Device *d; + Seat *s; + Inhibitor *i; + Button *b; + + assert(m); + + while ((session = hashmap_first(m->sessions))) + session_free(session); + + while ((u = hashmap_first(m->users))) + user_free(u); + + while ((d = hashmap_first(m->devices))) + device_free(d); + + while ((s = hashmap_first(m->seats))) + seat_free(s); + + while ((i = hashmap_first(m->inhibitors))) + inhibitor_free(i); + + while ((b = hashmap_first(m->buttons))) + button_free(b); + + hashmap_free(m->devices); + hashmap_free(m->seats); + hashmap_free(m->sessions); + hashmap_free(m->users); + hashmap_free(m->inhibitors); + hashmap_free(m->buttons); + + hashmap_free(m->user_units); + hashmap_free(m->session_units); + + sd_event_source_unref(m->idle_action_event_source); + sd_event_source_unref(m->inhibit_timeout_source); + sd_event_source_unref(m->scheduled_shutdown_timeout_source); + sd_event_source_unref(m->nologin_timeout_source); + sd_event_source_unref(m->wall_message_timeout_source); + + sd_event_source_unref(m->console_active_event_source); + sd_event_source_unref(m->udev_seat_event_source); + sd_event_source_unref(m->udev_device_event_source); + sd_event_source_unref(m->udev_vcsa_event_source); + sd_event_source_unref(m->udev_button_event_source); + sd_event_source_unref(m->lid_switch_ignore_event_source); + + safe_close(m->console_active_fd); + + udev_monitor_unref(m->udev_seat_monitor); + udev_monitor_unref(m->udev_device_monitor); + udev_monitor_unref(m->udev_vcsa_monitor); + udev_monitor_unref(m->udev_button_monitor); + + udev_unref(m->udev); + + if (m->unlink_nologin) + (void) unlink("/run/nologin"); + + bus_verify_polkit_async_registry_free(m->polkit_registry); + + sd_bus_unref(m->bus); + sd_event_unref(m->event); + + safe_close(m->reserve_vt_fd); + + strv_free(m->kill_only_users); + strv_free(m->kill_exclude_users); + + free(m->scheduled_shutdown_type); + free(m->scheduled_shutdown_tty); + free(m->wall_message); + free(m->action_job); + free(m); +} + +static int manager_enumerate_devices(Manager *m) { + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + int r; + + assert(m); + + /* Loads devices from udev and creates seats for them as + * necessary */ + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_tag(e, "master-of-seat"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_is_initialized(e); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + int k; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + k = manager_process_seat_device(m, d); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_enumerate_buttons(Manager *m) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + int r; + + assert(m); + + /* Loads buttons from udev */ + + if (m->handle_power_key == HANDLE_IGNORE && + m->handle_suspend_key == HANDLE_IGNORE && + m->handle_hibernate_key == HANDLE_IGNORE && + m->handle_lid_switch == HANDLE_IGNORE && + m->handle_lid_switch_docked == HANDLE_IGNORE) + return 0; + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_subsystem(e, "input"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_tag(e, "power-switch"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_is_initialized(e); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + int k; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + k = manager_process_button_device(m, d); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_enumerate_seats(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + /* This loads data about seats stored on disk, but does not + * actually create any seats. Removes data of seats that no + * longer exist. */ + + d = opendir("/run/systemd/seats"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/seats: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + Seat *s; + int k; + + if (!dirent_is_file(de)) + continue; + + s = hashmap_get(m->seats, de->d_name); + if (!s) { + unlinkat(dirfd(d), de->d_name, 0); + continue; + } + + k = seat_load(s); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_enumerate_linger_users(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + d = opendir("/var/lib/systemd/linger"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /var/lib/systemd/linger/: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + int k; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_user_by_name(m, de->d_name, NULL); + if (k < 0) { + log_notice_errno(k, "Couldn't add lingering user %s: %m", de->d_name); + r = k; + } + } + + return r; +} + +static int manager_enumerate_users(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r, k; + + assert(m); + + /* Add lingering users */ + r = manager_enumerate_linger_users(m); + + /* Read in user data stored on disk */ + d = opendir("/run/systemd/users"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/users: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + User *u; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_user_by_name(m, de->d_name, &u); + if (k < 0) { + log_error_errno(k, "Failed to add user by file name %s: %m", de->d_name); + + r = k; + continue; + } + + user_add_to_gc_queue(u); + + k = user_load(u); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_enumerate_sessions(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + /* Read in session data stored on disk */ + d = opendir("/run/systemd/sessions"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/sessions: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + struct Session *s; + int k; + + if (!dirent_is_file(de)) + continue; + + if (!session_id_valid(de->d_name)) { + log_warning("Invalid session file name '%s', ignoring.", de->d_name); + r = -EINVAL; + continue; + } + + k = manager_add_session(m, de->d_name, &s); + if (k < 0) { + log_error_errno(k, "Failed to add session by file name %s: %m", de->d_name); + + r = k; + continue; + } + + session_add_to_gc_queue(s); + + k = session_load(s); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_enumerate_inhibitors(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + d = opendir("/run/systemd/inhibit"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + int k; + Inhibitor *i; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_inhibitor(m, de->d_name, &i); + if (k < 0) { + log_notice_errno(k, "Couldn't add inhibitor %s: %m", de->d_name); + r = k; + continue; + } + + k = inhibitor_load(i); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_dispatch_seat_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + Manager *m = userdata; + + assert(m); + + d = udev_monitor_receive_device(m->udev_seat_monitor); + if (!d) + return -ENOMEM; + + manager_process_seat_device(m, d); + return 0; +} + +static int manager_dispatch_device_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + Manager *m = userdata; + + assert(m); + + d = udev_monitor_receive_device(m->udev_device_monitor); + if (!d) + return -ENOMEM; + + manager_process_seat_device(m, d); + return 0; +} + +static int manager_dispatch_vcsa_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + Manager *m = userdata; + const char *name; + + assert(m); + + d = udev_monitor_receive_device(m->udev_vcsa_monitor); + if (!d) + return -ENOMEM; + + name = udev_device_get_sysname(d); + + /* Whenever a VCSA device is removed try to reallocate our + * VTs, to make sure our auto VTs never go away. */ + + if (name && startswith(name, "vcsa") && streq_ptr(udev_device_get_action(d), "remove")) + seat_preallocate_vts(m->seat0); + + return 0; +} + +static int manager_dispatch_button_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + Manager *m = userdata; + + assert(m); + + d = udev_monitor_receive_device(m->udev_button_monitor); + if (!d) + return -ENOMEM; + + manager_process_button_device(m, d); + return 0; +} + +static int manager_dispatch_console(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(m); + assert(m->seat0); + assert(m->console_active_fd == fd); + + seat_read_active_vt(m->seat0); + return 0; +} + +static int manager_reserve_vt(Manager *m) { + _cleanup_free_ char *p = NULL; + + assert(m); + + if (m->reserve_vt <= 0) + return 0; + + if (asprintf(&p, "/dev/tty%u", m->reserve_vt) < 0) + return log_oom(); + + m->reserve_vt_fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (m->reserve_vt_fd < 0) { + + /* Don't complain on VT-less systems */ + if (errno != ENOENT) + log_warning_errno(errno, "Failed to pin reserved VT: %m"); + return -errno; + } + + return 0; +} + +static int manager_connect_bus(Manager *m) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(m); + assert(!m->bus); + + r = sd_bus_default_system(&m->bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); + + r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/login1", "org.freedesktop.login1.Manager", manager_vtable, m); + if (r < 0) + return log_error_errno(r, "Failed to add manager object vtable: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add seat object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/seat", seat_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add seat enumerator: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/session", "org.freedesktop.login1.Session", session_vtable, session_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add session object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/session", session_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add session enumerator: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/user", "org.freedesktop.login1.User", user_vtable, user_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add user object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/user", user_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add user enumerator: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + match_job_removed, m); + if (r < 0) + return log_error_errno(r, "Failed to add match for JobRemoved: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='UnitRemoved'," + "path='/org/freedesktop/systemd1'", + match_unit_removed, m); + if (r < 0) + return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'", + match_properties_changed, m); + if (r < 0) + return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='Reloading'," + "path='/org/freedesktop/systemd1'", + match_reloading, m); + if (r < 0) + return log_error_errno(r, "Failed to add match for Reloading: %m"); + + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Subscribe", + &error, + NULL, NULL); + if (r < 0) { + log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + return 0; +} + +static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo *si, void *data) { + Manager *m = data; + Session *active, *iter; + + /* + * We got a VT-switch signal and we have to acknowledge it immediately. + * Preferably, we'd just use m->seat0->active->vtfd, but unfortunately, + * old user-space might run multiple sessions on a single VT, *sigh*. + * Therefore, we have to iterate all sessions and find one with a vtfd + * on the requested VT. + * As only VTs with active controllers have VT_PROCESS set, our current + * notion of the active VT might be wrong (for instance if the switch + * happens while we setup VT_PROCESS). Therefore, read the current VT + * first and then use s->active->vtnr as reference. Note that this is + * not racy, as no further VT-switch can happen as long as we're in + * synchronous VT_PROCESS mode. + */ + + assert(m->seat0); + seat_read_active_vt(m->seat0); + + active = m->seat0->active; + if (!active || active->vtnr < 1) { + log_warning("Received VT_PROCESS signal without a registered session on that VT."); + return 0; + } + + if (active->vtfd >= 0) { + session_leave_vt(active); + } else { + LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) { + if (iter->vtnr == active->vtnr && iter->vtfd >= 0) { + session_leave_vt(iter); + break; + } + } + } + + return 0; +} + +static int manager_connect_console(Manager *m) { + int r; + + assert(m); + assert(m->console_active_fd < 0); + + /* On certain architectures (S390 and Xen, and containers), + /dev/tty0 does not exist, so don't fail if we can't open + it. */ + if (access("/dev/tty0", F_OK) < 0) + return 0; + + m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (m->console_active_fd < 0) { + + /* On some systems the device node /dev/tty0 may exist + * even though /sys/class/tty/tty0 does not. */ + if (errno == ENOENT) + return 0; + + 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); + if (r < 0) { + log_error("Failed to watch foreground console"); + return r; + } + + /* + * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used + * as VT-acquire signal. We ignore any acquire-events (yes, we still + * have to provide a valid signal-number for it!) and acknowledge all + * release events immediately. + */ + + if (SIGRTMIN + 1 > SIGRTMAX) { + log_error("Not enough real-time signals available: %u-%u", SIGRTMIN, SIGRTMAX); + return -EINVAL; + } + + assert_se(ignore_signals(SIGRTMIN + 1, -1) >= 0); + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN, -1) >= 0); + + r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m); + if (r < 0) + return r; + + return 0; +} + +static int manager_connect_udev(Manager *m) { + int r; + + assert(m); + assert(!m->udev_seat_monitor); + assert(!m->udev_device_monitor); + assert(!m->udev_vcsa_monitor); + assert(!m->udev_button_monitor); + + m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_seat_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "master-of-seat"); + if (r < 0) + return r; + + r = udev_monitor_enable_receiving(m->udev_seat_monitor); + if (r < 0) + return r; + + r = sd_event_add_io(m->event, &m->udev_seat_event_source, udev_monitor_get_fd(m->udev_seat_monitor), EPOLLIN, manager_dispatch_seat_udev, m); + if (r < 0) + return r; + + m->udev_device_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_device_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "input", NULL); + if (r < 0) + return r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "graphics", NULL); + if (r < 0) + return r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "drm", NULL); + if (r < 0) + return r; + + r = udev_monitor_enable_receiving(m->udev_device_monitor); + if (r < 0) + return r; + + r = sd_event_add_io(m->event, &m->udev_device_event_source, udev_monitor_get_fd(m->udev_device_monitor), EPOLLIN, manager_dispatch_device_udev, m); + if (r < 0) + return r; + + /* Don't watch keys if nobody cares */ + if (m->handle_power_key != HANDLE_IGNORE || + m->handle_suspend_key != HANDLE_IGNORE || + m->handle_hibernate_key != HANDLE_IGNORE || + m->handle_lid_switch != HANDLE_IGNORE || + m->handle_lid_switch_docked != HANDLE_IGNORE) { + + m->udev_button_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_button_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_tag(m->udev_button_monitor, "power-switch"); + if (r < 0) + return r; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_button_monitor, "input", NULL); + if (r < 0) + return r; + + r = udev_monitor_enable_receiving(m->udev_button_monitor); + if (r < 0) + return r; + + r = sd_event_add_io(m->event, &m->udev_button_event_source, udev_monitor_get_fd(m->udev_button_monitor), EPOLLIN, manager_dispatch_button_udev, m); + if (r < 0) + return r; + } + + /* Don't bother watching VCSA devices, if nobody cares */ + if (m->n_autovts > 0 && m->console_active_fd >= 0) { + + m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_vcsa_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_vcsa_monitor, "vc", NULL); + if (r < 0) + return r; + + r = udev_monitor_enable_receiving(m->udev_vcsa_monitor); + if (r < 0) + return r; + + r = sd_event_add_io(m->event, &m->udev_vcsa_event_source, udev_monitor_get_fd(m->udev_vcsa_monitor), EPOLLIN, manager_dispatch_vcsa_udev, m); + if (r < 0) + return r; + } + + return 0; +} + +static void manager_gc(Manager *m, bool drop_not_started) { + Seat *seat; + Session *session; + User *user; + + assert(m); + + while ((seat = m->seat_gc_queue)) { + LIST_REMOVE(gc_queue, m->seat_gc_queue, seat); + seat->in_gc_queue = false; + + if (!seat_check_gc(seat, drop_not_started)) { + seat_stop(seat, false); + seat_free(seat); + } + } + + while ((session = m->session_gc_queue)) { + LIST_REMOVE(gc_queue, m->session_gc_queue, session); + session->in_gc_queue = false; + + /* First, if we are not closing yet, initiate stopping */ + if (!session_check_gc(session, drop_not_started) && + session_get_state(session) != SESSION_CLOSING) + session_stop(session, false); + + /* Normally, this should make the session referenced + * again, if it doesn't then let's get rid of it + * immediately */ + if (!session_check_gc(session, drop_not_started)) { + session_finalize(session); + session_free(session); + } + } + + while ((user = m->user_gc_queue)) { + LIST_REMOVE(gc_queue, m->user_gc_queue, user); + user->in_gc_queue = false; + + /* First step: queue stop jobs */ + if (!user_check_gc(user, drop_not_started)) + user_stop(user, false); + + /* Second step: finalize user */ + if (!user_check_gc(user, drop_not_started)) { + user_finalize(user); + user_free(user); + } + } +} + +static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *userdata) { + Manager *m = userdata; + struct dual_timestamp since; + usec_t n, elapse; + int r; + + assert(m); + + if (m->idle_action == HANDLE_IGNORE || + m->idle_action_usec <= 0) + return 0; + + n = now(CLOCK_MONOTONIC); + + r = manager_get_idle_hint(m, &since); + if (r <= 0) + /* Not idle. Let's check if after a timeout it might be idle then. */ + elapse = n + m->idle_action_usec; + else { + /* Idle! Let's see if it's time to do something, or if + * we shall sleep for longer. */ + + if (n >= since.monotonic + m->idle_action_usec && + (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) { + log_info("System idle. Taking action."); + + manager_handle_action(m, 0, m->idle_action, false, false); + m->idle_action_not_before_usec = n; + } + + elapse = MAX(since.monotonic, m->idle_action_not_before_usec) + m->idle_action_usec; + } + + if (!m->idle_action_event_source) { + + r = sd_event_add_time( + m->event, + &m->idle_action_event_source, + CLOCK_MONOTONIC, + elapse, USEC_PER_SEC*30, + manager_dispatch_idle_action, m); + if (r < 0) + return log_error_errno(r, "Failed to add idle event source: %m"); + + r = sd_event_source_set_priority(m->idle_action_event_source, SD_EVENT_PRIORITY_IDLE+10); + if (r < 0) + return log_error_errno(r, "Failed to set idle event source priority: %m"); + } else { + r = sd_event_source_set_time(m->idle_action_event_source, elapse); + if (r < 0) + return log_error_errno(r, "Failed to set idle event timer: %m"); + + r = sd_event_source_set_enabled(m->idle_action_event_source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "Failed to enable idle event timer: %m"); + } + + return 0; +} + +static int manager_parse_config_file(Manager *m) { + assert(m); + + 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); +} + +static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = userdata; + int r; + + manager_reset_config(m); + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse config file, using defaults: %m"); + else + log_info("Config file reloaded."); + + return 0; +} + +static int manager_startup(Manager *m) { + int r; + Seat *seat; + Session *session; + User *user; + Button *button; + Inhibitor *inhibitor; + Iterator i; + + assert(m); + + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGHUP, -1) >= 0); + + r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m); + if (r < 0) + return log_error_errno(r, "Failed to register SIGHUP handler: %m"); + + /* Connect to console */ + r = manager_connect_console(m); + if (r < 0) + return r; + + /* Connect to udev */ + r = manager_connect_udev(m); + if (r < 0) + return log_error_errno(r, "Failed to create udev watchers: %m"); + + /* Connect to the bus */ + r = manager_connect_bus(m); + if (r < 0) + return r; + + /* Instantiate magic seat 0 */ + r = manager_add_seat(m, "seat0", &m->seat0); + if (r < 0) + return log_error_errno(r, "Failed to add seat0: %m"); + + r = manager_set_lid_switch_ignore(m, 0 + m->holdoff_timeout_usec); + if (r < 0) + log_warning_errno(r, "Failed to set up lid switch ignore event source: %m"); + + /* Deserialize state */ + r = manager_enumerate_devices(m); + if (r < 0) + log_warning_errno(r, "Device enumeration failed: %m"); + + r = manager_enumerate_seats(m); + if (r < 0) + log_warning_errno(r, "Seat enumeration failed: %m"); + + r = manager_enumerate_users(m); + if (r < 0) + log_warning_errno(r, "User enumeration failed: %m"); + + r = manager_enumerate_sessions(m); + if (r < 0) + log_warning_errno(r, "Session enumeration failed: %m"); + + r = manager_enumerate_inhibitors(m); + if (r < 0) + log_warning_errno(r, "Inhibitor enumeration failed: %m"); + + r = manager_enumerate_buttons(m); + if (r < 0) + log_warning_errno(r, "Button enumeration failed: %m"); + + /* Remove stale objects before we start them */ + manager_gc(m, false); + + /* Reserve the special reserved VT */ + manager_reserve_vt(m); + + /* And start everything */ + HASHMAP_FOREACH(seat, m->seats, i) + seat_start(seat); + + HASHMAP_FOREACH(user, m->users, i) + user_start(user); + + HASHMAP_FOREACH(session, m->sessions, i) + session_start(session); + + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) + inhibitor_start(inhibitor); + + HASHMAP_FOREACH(button, m->buttons, i) + button_check_switches(button); + + manager_dispatch_idle_action(NULL, 0, m); + + return 0; +} + +static int manager_run(Manager *m) { + int r; + + assert(m); + + for (;;) { + r = sd_event_get_state(m->event); + if (r < 0) + return r; + if (r == SD_EVENT_FINISHED) + return 0; + + manager_gc(m, true); + + r = manager_dispatch_delayed(m, false); + if (r < 0) + return r; + if (r > 0) + continue; + + r = sd_event_run(m->event, (uint64_t) -1); + if (r < 0) + return r; + } +} + +int main(int argc, char *argv[]) { + Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_set_facility(LOG_AUTH); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "Could not initialize labelling: %m"); + goto finish; + } + + /* Always create the directories people can create inotify + * watches in. Note that some applications might check for the + * existence of /run/systemd/seats/ to determine whether + * logind is available, so please always make sure this check + * stays in. */ + mkdir_label("/run/systemd/seats", 0755); + mkdir_label("/run/systemd/users", 0755); + mkdir_label("/run/systemd/sessions", 0755); + + m = manager_new(); + if (!m) { + r = log_oom(); + goto finish; + } + + manager_parse_config_file(m); + + r = manager_startup(m); + if (r < 0) { + log_error_errno(r, "Failed to fully start up daemon: %m"); + goto finish; + } + + log_debug("systemd-logind running as pid "PID_FMT, getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + r = manager_run(m); + + log_debug("systemd-logind stopped as pid "PID_FMT, getpid()); + +finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + manager_free(m); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-login/systemd-logind/logind.conf.in b/src/grp-login/systemd-logind/logind.conf.in new file mode 100644 index 0000000000..6f720b7708 --- /dev/null +++ b/src/grp-login/systemd-logind/logind.conf.in @@ -0,0 +1,37 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See logind.conf(5) for details. + +[Login] +#NAutoVTs=6 +#ReserveVT=6 +#KillUserProcesses=@KILL_USER_PROCESSES@ +#KillOnlyUsers= +#KillExcludeUsers=root +#InhibitDelayMaxSec=5 +#HandlePowerKey=poweroff +#HandleSuspendKey=suspend +#HandleHibernateKey=hibernate +#HandleLidSwitch=suspend +#HandleLidSwitchDocked=ignore +#PowerKeyIgnoreInhibited=no +#SuspendKeyIgnoreInhibited=no +#HibernateKeyIgnoreInhibited=no +#LidSwitchIgnoreInhibited=yes +#HoldoffTimeoutSec=30s +#IdleAction=ignore +#IdleActionSec=30min +#RuntimeDirectorySize=10% +#RemoveIPC=yes +#InhibitorsMax=8192 +#SessionsMax=8192 +#UserTasksMax=33% diff --git a/src/grp-login/systemd-logind/logind.conf.xml b/src/grp-login/systemd-logind/logind.conf.xml new file mode 100644 index 0000000000..adba5a4131 --- /dev/null +++ b/src/grp-login/systemd-logind/logind.conf.xml @@ -0,0 +1,348 @@ + + + + + + + + logind.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + logind.conf + 5 + + + + logind.conf + logind.conf.d + Login manager configuration files + + + + /etc/systemd/logind.conf + /etc/systemd/logind.conf.d/*.conf + /run/systemd/logind.conf.d/*.conf + /usr/lib/systemd/logind.conf.d/*.conf + + + + Description + + These files configure various parameters of the systemd + login manager, + systemd-logind.service8. + + + + + + + Options + + All options are configured in the + [Login] section: + + + + + NAutoVTs= + + Takes a positive integer. Configures how many + virtual terminals (VTs) to allocate by default that, when + switched to and are previously unused, + autovt services are automatically spawned + on. These services are instantiated from the template unit + autovt@.service for the respective VT TTY + name, for example, autovt@tty4.service. + By default, autovt@.service is linked to + getty@.service. In other words, login + prompts are started dynamically as the user switches to unused + virtual terminals. Hence, this parameter controls how many + login gettys are available on the VTs. If a + VT is already used by some other subsystem (for example, a + graphical login), this kind of activation will not be + attempted. Note that the VT configured in + ReserveVT= is always subject to this kind + of activation, even if it is not one of the VTs configured + with the NAutoVTs= directive. Defaults to + 6. When set to 0, automatic spawning of + autovt services is + disabled. + + + + ReserveVT= + + Takes a positive integer. Identifies one + virtual terminal that shall unconditionally be reserved for + autovt@.service activation (see above). + The VT selected with this option will be marked busy + unconditionally, so that no other subsystem will allocate it. + This functionality is useful to ensure that, regardless of how + many VTs are allocated by other subsystems, one login + getty is always available. Defaults to 6 + (in other words, there will always be a + getty available on Alt-F6.). When set to 0, + VT reservation is disabled. + + + + KillUserProcesses= + + Takes a boolean argument. Configures whether the processes of a + user should be killed when the user logs out. If true, the scope unit + corresponding to the session and all processes inside that scope will be + terminated. If false, the scope is "abandoned", see + systemd.scope5, + and processes are not killed. Defaults to yes, + but see the options KillOnlyUsers= and + KillExcludeUsers= below. + + In addition to session processes, user process may run under the user + manager unit user@.service. Depending on the linger + settings, this may allow users to run processes independent of their login + sessions. See the description of enable-linger in + loginctl1. + + + Note that setting KillUserProcesses=yes + will break tools like + screen1 + and + tmux1, + unless they are moved out of the session scope. See example in + systemd-run1. + + + + + KillOnlyUsers= + KillExcludeUsers= + + These settings take space-separated lists of usernames that override + the KillUserProcesses= setting. A user name may be added to + KillExcludeUsers= to exclude the processes in the session scopes of + that user from being killed even if KillUserProcesses=yes is set. If + KillExcludeUsers= is not set, the root user is + excluded by default. KillExcludeUsers= may be set to an empty value + to override this default. If a user is not excluded, KillOnlyUsers= + is checked next. If this setting is specified, only the session scopes of those users + will be killed. Otherwise, users are subject to the + KillUserProcesses=yes setting. + + + + IdleAction= + + Configures the action to take when the system + is idle. Takes one of + ignore, + poweroff, + reboot, + halt, + kexec, + suspend, + hibernate, + hybrid-sleep, and + lock. + Defaults to ignore. + + Note that this requires that user sessions correctly + report the idle status to the system. The system will execute + the action after all sessions report that they are idle, no + idle inhibitor lock is active, and subsequently, the time + configured with IdleActionSec= (see below) + has expired. + + + + + IdleActionSec= + + Configures the delay after which the action + configured in IdleAction= (see above) is + taken after the system is idle. + + + + InhibitDelayMaxSec= + + Specifies the maximum time a system shutdown + or sleep request is delayed due to an inhibitor lock of type + delay being active before the inhibitor is + ignored and the operation executes anyway. Defaults to + 5. + + + + HandlePowerKey= + HandleSuspendKey= + HandleHibernateKey= + HandleLidSwitch= + HandleLidSwitchDocked= + + Controls whether logind shall handle the + system power and sleep keys and the lid switch to trigger + actions such as system power-off or suspend. Can be one of + ignore, + poweroff, + reboot, + halt, + kexec, + suspend, + hibernate, + hybrid-sleep, and + lock. + If ignore, logind will never handle these + keys. If lock, all running sessions will be + screen-locked; otherwise, the specified action will be taken + in the respective event. Only input devices with the + power-switch udev tag will be watched for + key/lid switch events. HandlePowerKey= + defaults to poweroff. + HandleSuspendKey= and + HandleLidSwitch= default to + suspend. + HandleLidSwitchDocked= defaults to + ignore. + HandleHibernateKey= defaults to + hibernate. If the system is inserted in a + docking station, or if more than one display is connected, the + action specified by HandleLidSwitchDocked= + occurs; otherwise the HandleLidSwitch= + action occurs. + + + + PowerKeyIgnoreInhibited= + SuspendKeyIgnoreInhibited= + HibernateKeyIgnoreInhibited= + LidSwitchIgnoreInhibited= + + Controls whether actions triggered by the + power and sleep keys and the lid switch are subject to + inhibitor locks. These settings take boolean arguments. If + no, the inhibitor locks taken by + applications in order to block the requested operation are + respected. If yes, the requested operation + is executed in any case. + PowerKeyIgnoreInhibited=, + SuspendKeyIgnoreInhibited= and + HibernateKeyIgnoreInhibited= default to + no. + LidSwitchIgnoreInhibited= defaults to + yes. This means that the lid switch does + not respect suspend blockers by default, but the power and + sleep keys do. + + + + HoldoffTimeoutSec= + + Specifies the timeout after system startup or + system resume in which systemd will hold off on reacting to + lid events. This is required for the system to properly + detect any hotplugged devices so systemd can ignore lid events + if external monitors, or docks, are connected. If set to 0, + systemd will always react immediately, possibly before the + kernel fully probed all hotplugged devices. This is safe, as + long as you do not care for systemd to account for devices + that have been plugged or unplugged while the system was off. + Defaults to 30s. + + + + RuntimeDirectorySize= + + Sets the size limit on the + $XDG_RUNTIME_DIR runtime directory for each + user who logs in. Takes a size in bytes, optionally suffixed + with the usual K, G, M, and T suffixes, to the base 1024 + (IEC). Alternatively, a numerical percentage suffixed by + % may be specified, which sets the size + limit relative to the amount of physical RAM. Defaults to 10%. + Note that this size is a safety limit only. As each runtime + directory is a tmpfs file system, it will only consume as much + memory as is needed. + + + + InhibitorsMax= + + Controls the maximum number of concurrent inhibitors to permit. Defaults to 8192 + (8K). + + + + SessionsMax= + + Controls the maximum number of concurrent user sessions to manage. Defaults to 8192 + (8K). Depending on how the pam_systemd.so module is included in the PAM stack + configuration, further login sessions will either be refused, or permitted but not tracked by + systemd-logind. + + + + UserTasksMax= + + Sets the maximum number of OS tasks each user may run concurrently. This controls the + TasksMax= setting of the per-user slice unit, see + systemd.resource-control5 + for details. Defaults to 33%, which equals 10813 with the kernel's defaults on the host, but might be smaller + in OS containers. + + + + RemoveIPC= + + Controls whether System V and POSIX IPC objects belonging to the user shall be removed when the + user fully logs out. Takes a boolean argument. If enabled, the user may not consume IPC resources after the + last of the user's sessions terminated. This covers System V semaphores, shared memory and message queues, as + well as POSIX shared memory and message queues. Note that IPC objects of the root user and other system users + are excluded from the effect of this setting. Defaults to yes. + + + + + + + See Also + + systemd1, + systemd-logind.service8, + loginctl1, + systemd-system.conf5 + + + + diff --git a/src/grp-login/systemd-logind/logind.h b/src/grp-login/systemd-logind/logind.h new file mode 100644 index 0000000000..c637cdfcac --- /dev/null +++ b/src/grp-login/systemd-logind/logind.h @@ -0,0 +1,199 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/set.h" + +typedef struct Manager Manager; + +#include "logind-action.h" +#include "logind-button.h" +#include "logind-device.h" +#include "logind-inhibit.h" + +struct Manager { + sd_event *event; + sd_bus *bus; + + Hashmap *devices; + Hashmap *seats; + Hashmap *sessions; + Hashmap *users; + Hashmap *inhibitors; + Hashmap *buttons; + + LIST_HEAD(Seat, seat_gc_queue); + LIST_HEAD(Session, session_gc_queue); + LIST_HEAD(User, user_gc_queue); + + struct udev *udev; + struct udev_monitor *udev_seat_monitor, *udev_device_monitor, *udev_vcsa_monitor, *udev_button_monitor; + + sd_event_source *console_active_event_source; + sd_event_source *udev_seat_event_source; + sd_event_source *udev_device_event_source; + sd_event_source *udev_vcsa_event_source; + sd_event_source *udev_button_event_source; + + int console_active_fd; + + unsigned n_autovts; + + unsigned reserve_vt; + int reserve_vt_fd; + + Seat *seat0; + + char **kill_only_users, **kill_exclude_users; + bool kill_user_processes; + + unsigned long session_counter; + unsigned long inhibit_counter; + + Hashmap *session_units; + Hashmap *user_units; + + usec_t inhibit_delay_max; + + /* If an action is currently being executed or is delayed, + * this is != 0 and encodes what is being done */ + InhibitWhat action_what; + + /* If a shutdown/suspend was delayed due to a inhibitor this + contains the unit name we are supposed to start after the + delay is over */ + const char *action_unit; + + /* If a shutdown/suspend is currently executed, then this is + * the job of it */ + char *action_job; + sd_event_source *inhibit_timeout_source; + + char *scheduled_shutdown_type; + usec_t scheduled_shutdown_timeout; + sd_event_source *scheduled_shutdown_timeout_source; + uid_t scheduled_shutdown_uid; + char *scheduled_shutdown_tty; + sd_event_source *nologin_timeout_source; + bool unlink_nologin; + + char *wall_message; + unsigned enable_wall_messages; + sd_event_source *wall_message_timeout_source; + + bool shutdown_dry_run; + + sd_event_source *idle_action_event_source; + usec_t idle_action_usec; + usec_t idle_action_not_before_usec; + HandleAction idle_action; + + HandleAction handle_power_key; + HandleAction handle_suspend_key; + HandleAction handle_hibernate_key; + HandleAction handle_lid_switch; + HandleAction handle_lid_switch_docked; + + bool power_key_ignore_inhibited; + bool suspend_key_ignore_inhibited; + bool hibernate_key_ignore_inhibited; + bool lid_switch_ignore_inhibited; + + bool remove_ipc; + + Hashmap *polkit_registry; + + usec_t holdoff_timeout_usec; + sd_event_source *lid_switch_ignore_event_source; + + size_t runtime_dir_size; + uint64_t user_tasks_max; + uint64_t sessions_max; + uint64_t inhibitors_max; +}; + +int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); +int manager_add_button(Manager *m, const char *name, Button **_button); +int manager_add_seat(Manager *m, const char *id, Seat **_seat); +int manager_add_session(Manager *m, const char *id, Session **_session); +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); +int manager_add_user_by_name(Manager *m, const char *name, User **_user); +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); + +int manager_process_seat_device(Manager *m, struct udev_device *d); +int manager_process_button_device(Manager *m, struct udev_device *d); + +int manager_spawn_autovt(Manager *m, unsigned int vtnr); + +bool manager_shall_kill(Manager *m, const char *user); + +int manager_get_idle_hint(Manager *m, dual_timestamp *t); + +int manager_get_user_by_pid(Manager *m, pid_t pid, User **user); +int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); + +bool manager_is_docked_or_external_displays(Manager *m); + +extern const sd_bus_vtable manager_vtable[]; + +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); + +int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error); + +int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; + +int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); +int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); +int manager_unit_is_active(Manager *manager, const char *unit); +int manager_job_is_active(Manager *manager, const char *path); + +/* gperf lookup function */ +const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); + +int manager_set_lid_switch_ignore(Manager *m, usec_t until); + +int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_user_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret); +int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret); +int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret); + +int manager_setup_wall_message_timer(Manager *m); +bool logind_wall_tty_filter(const char *tty, void *userdata); + +int manager_dispatch_delayed(Manager *manager, bool timeout); diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.conf b/src/grp-login/systemd-logind/org.freedesktop.login1.conf new file mode 100644 index 0000000000..c89e40457e --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.conf @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in b/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in new file mode 100644 index 0000000000..66cbce393c --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.policy.in @@ -0,0 +1,325 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Allow applications to inhibit system shutdown + <_message>Authentication is required for an application to inhibit system shutdown. + + no + yes + yes + + org.freedesktop.login1.inhibit-delay-shutdown org.freedesktop.login1.inhibit-block-sleep org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle + + + + <_description>Allow applications to delay system shutdown + <_message>Authentication is required for an application to delay system shutdown. + + yes + yes + yes + + org.freedesktop.login1.inhibit-delay-sleep + + + + <_description>Allow applications to inhibit system sleep + <_message>Authentication is required for an application to inhibit system sleep. + + no + yes + yes + + org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle + + + + <_description>Allow applications to delay system sleep + <_message>Authentication is required for an application to delay system sleep. + + yes + yes + yes + + + + + <_description>Allow applications to inhibit automatic system suspend + <_message>Authentication is required for an application to inhibit automatic system suspend. + + yes + yes + yes + + + + + <_description>Allow applications to inhibit system handling of the power key + <_message>Authentication is required for an application to inhibit system handling of the power key. + + no + yes + yes + + org.freedesktop.login1.inhibit-handle-suspend-key org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch + + + + <_description>Allow applications to inhibit system handling of the suspend key + <_message>Authentication is required for an application to inhibit system handling of the suspend key. + + no + yes + yes + + org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch + + + + <_description>Allow applications to inhibit system handling of the hibernate key + <_message>Authentication is required for an application to inhibit system handling of the hibernate key. + + no + yes + yes + + + + + <_description>Allow applications to inhibit system handling of the lid switch + <_message>Authentication is required for an application to inhibit system handling of the lid switch. + + no + yes + yes + + + + + <_description>Allow non-logged-in user to run programs + <_message>Explicit request is required to run programs as a non-logged-in user. + + yes + yes + yes + + + + + <_description>Allow non-logged-in users to run programs + <_message>Authentication is required to run programs as a non-logged-in user. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Allow attaching devices to seats + <_message>Authentication is required for attaching a device to a seat. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.flush-devices + + + + <_description>Flush device to seat attachments + <_message>Authentication is required for resetting how devices are attached to seats. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Power off the system + <_message>Authentication is required for powering off the system. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.set-wall-message + + + + <_description>Power off the system while other users are logged in + <_message>Authentication is required for powering off the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.power-off + + + + <_description>Power off the system while an application asked to inhibit it + <_message>Authentication is required for powering off the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.power-off + + + + <_description>Reboot the system + <_message>Authentication is required for rebooting the system. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.set-wall-message + + + + <_description>Reboot the system while other users are logged in + <_message>Authentication is required for rebooting the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + + + <_description>Reboot the system while an application asked to inhibit it + <_message>Authentication is required for rebooting the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.reboot + + + + <_description>Suspend the system + <_message>Authentication is required for suspending the system. + + auth_admin_keep + auth_admin_keep + yes + + + + + <_description>Suspend the system while other users are logged in + <_message>Authentication is required for suspending the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.suspend + + + + <_description>Suspend the system while an application asked to inhibit it + <_message>Authentication is required for suspending the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.suspend + + + + <_description>Hibernate the system + <_message>Authentication is required for hibernating the system. + + auth_admin_keep + auth_admin_keep + yes + + + + + <_description>Hibernate the system while other users are logged in + <_message>Authentication is required for hibernating the system while other users are logged in. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.hibernate + + + + <_description>Hibernate the system while an application asked to inhibit it + <_message>Authentication is required for hibernating the system while an application asked to inhibit it. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.login1.hibernate + + + + <_description>Manage active sessions, users and seats + <_message>Authentication is required for managing active sessions, users and seats. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Lock or unlock active sessions + <_message>Authentication is required to lock or unlock active sessions. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Allow indication to the firmware to boot to setup interface + <_message>Authentication is required to indicate to the firmware to boot to setup interface. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set a wall message + <_message>Authentication is required to set a wall message + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/grp-login/systemd-logind/org.freedesktop.login1.service b/src/grp-login/systemd-logind/org.freedesktop.login1.service new file mode 100644 index 0000000000..762dae2bb3 --- /dev/null +++ b/src/grp-login/systemd-logind/org.freedesktop.login1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.login1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.login1.service diff --git a/src/grp-login/systemd-logind/systemd-logind.service.in b/src/grp-login/systemd-logind/systemd-logind.service.in new file mode 100644 index 0000000000..bee08d011f --- /dev/null +++ b/src/grp-login/systemd-logind/systemd-logind.service.in @@ -0,0 +1,33 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Login Service +Documentation=man:systemd-logind.service(8) man:logind.conf(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/logind +Documentation=http://www.freedesktop.org/wiki/Software/systemd/multiseat +Wants=user.slice +After=nss-user-lookup.target user.slice + +# Ask for the dbus socket. If running over kdbus, the socket will +# not be actually used. +Wants=dbus.socket +After=dbus.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-logind +Restart=always +RestartSec=0 +BusName=org.freedesktop.login1 +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG +WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io + +# Increase the default a bit in order to allow many simultaneous +# logins since we keep one fd open per session. +LimitNOFILE=16384 diff --git a/src/grp-login/systemd-logind/systemd-logind.service.xml b/src/grp-login/systemd-logind/systemd-logind.service.xml new file mode 100644 index 0000000000..5733e42cd1 --- /dev/null +++ b/src/grp-login/systemd-logind/systemd-logind.service.xml @@ -0,0 +1,121 @@ + + + + + + + + + systemd-logind.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-logind.service + 8 + + + + systemd-logind.service + systemd-logind + Login manager + + + + systemd-logind.service + /usr/lib/systemd/systemd-logind + + + + Description + + systemd-logind is a system service that + manages user logins. It is responsible for: + + + Keeping track of users and sessions, their + processes and their idle state + + Providing PolicyKit-based access for users to + operations such as system shutdown or sleep + + Implementing a shutdown/sleep inhibition logic + for applications + + Handling of power/sleep hardware + keys + + Multi-seat management + + Session switch management + + Device access management for + users + + Automatic spawning of text logins (gettys) on + virtual console activation and user runtime directory + management + + + User sessions are registered in logind via the + pam_systemd8 + PAM module. + + See + logind.conf5 + for information about the configuration of this service. + + See Multi-Seat + on Linux for an introduction into basic concepts of logind + such as users, sessions and seats. + + See the + logind D-Bus API Documentation for information about the + APIs systemd-logind provides. + + For more information on the inhibition logic see the Inhibitor + Lock Developer Documentation. + + + + See Also + + systemd1, + systemd-user-sessions.service8, + loginctl1, + logind.conf5, + pam_systemd8 + + + + diff --git a/src/grp-login/systemd-logind/systemd-user.pam.m4 b/src/grp-login/systemd-logind/systemd-user.pam.m4 new file mode 100644 index 0000000000..f188a8e548 --- /dev/null +++ b/src/grp-login/systemd-logind/systemd-user.pam.m4 @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# Used by systemd --user instances. + +account include system-auth + +m4_ifdef(`HAVE_SELINUX', +session required pam_selinux.so close +session required pam_selinux.so nottys open +)m4_dnl +session required pam_loginuid.so +session include system-auth diff --git a/src/grp-login/systemd-logind/user.slice b/src/grp-login/systemd-logind/user.slice new file mode 100644 index 0000000000..9fa6284c12 --- /dev/null +++ b/src/grp-login/systemd-logind/user.slice @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User and Session Slice +Documentation=man:systemd.special(7) +Before=slices.target diff --git a/src/grp-login/test-inhibit.c b/src/grp-login/test-inhibit.c new file mode 100644 index 0000000000..2451d56446 --- /dev/null +++ b/src/grp-login/test-inhibit.c @@ -0,0 +1,112 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +static int inhibit(sd_bus *bus, const char *what) { + _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; + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit", + &error, + &reply, + "ssss", what, who, reason, mode); + assert_se(r >= 0); + + r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); + assert_se(r >= 0); + assert_se(fd >= 0); + + return dup(fd); +} + +static void print_inhibitors(sd_bus *bus) { + _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; + int r; + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors", + &error, + &reply, + ""); + assert_se(r >= 0); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); + assert_se(r >= 0); + + while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { + printf("what=<%s> who=<%s> why=<%s> mode=<%s> uid=<%"PRIu32"> pid=<%"PRIu32">\n", + what, who, why, mode, uid, pid); + + n++; + } + assert_se(r >= 0); + + printf("%u inhibitors\n", n); +} + +int main(int argc, char*argv[]) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + int fd1, fd2; + int r; + + r = sd_bus_open_system(&bus); + assert_se(r >= 0); + + print_inhibitors(bus); + + fd1 = inhibit(bus, "sleep"); + assert_se(fd1 >= 0); + print_inhibitors(bus); + + fd2 = inhibit(bus, "idle:shutdown"); + assert_se(fd2 >= 0); + print_inhibitors(bus); + + safe_close(fd1); + sleep(1); + print_inhibitors(bus); + + safe_close(fd2); + sleep(1); + print_inhibitors(bus); + + return 0; +} diff --git a/src/grp-login/test-login-shared.c b/src/grp-login/test-login-shared.c new file mode 100644 index 0000000000..5a9329f7ee --- /dev/null +++ b/src/grp-login/test-login-shared.c @@ -0,0 +1,39 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/login-util.h" +#include "basic/macro.h" + +static void test_session_id_valid(void) { + assert_se(session_id_valid("c1")); + assert_se(session_id_valid("1234")); + + assert_se(!session_id_valid("1-2")); + assert_se(!session_id_valid("")); + assert_se(!session_id_valid("\tid")); +} + +int main(int argc, char* argv[]) { + log_parse_environment(); + log_open(); + + test_session_id_valid(); + + return 0; +} diff --git a/src/grp-login/test-login-tables.c b/src/grp-login/test-login-tables.c new file mode 100644 index 0000000000..3f2f4f3054 --- /dev/null +++ b/src/grp-login/test-login-tables.c @@ -0,0 +1,34 @@ +/*** + This file is part of systemd + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "logind-action.h" +#include "logind-session.h" +#include "shared/test-tables.h" + +int main(int argc, char **argv) { + test_table(handle_action, HANDLE_ACTION); + test_table(inhibit_mode, INHIBIT_MODE); + test_table(kill_who, KILL_WHO); + test_table(session_class, SESSION_CLASS); + test_table(session_state, SESSION_STATE); + test_table(session_type, SESSION_TYPE); + test_table(user_state, USER_STATE); + + return EXIT_SUCCESS; +} diff --git a/src/grp-machine/Makefile b/src/grp-machine/Makefile new file mode 100644 index 0000000000..9679410b7b --- /dev/null +++ b/src/grp-machine/Makefile @@ -0,0 +1,31 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += grp-import +nested.subdirs += machinectl +nested.subdirs += nss-mymachines +nested.subdirs += systemd-machined + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/Makefile b/src/grp-machine/grp-import/Makefile new file mode 100644 index 0000000000..dfa189b317 --- /dev/null +++ b/src/grp-machine/grp-import/Makefile @@ -0,0 +1,32 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += libimport +nested.subdirs += systemd-export +nested.subdirs += systemd-import +nested.subdirs += systemd-importd +nested.subdirs += systemd-pull + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/libimport/Makefile b/src/grp-machine/grp-import/libimport/Makefile new file mode 100644 index 0000000000..585001b2fa --- /dev/null +++ b/src/grp-machine/grp-import/libimport/Makefile @@ -0,0 +1,41 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +manual_tests += \ + test-qcow2 + +test_qcow2_SOURCES = \ + src/import/test-qcow2.c \ + src/import/qcow2-util.c \ + src/import/qcow2-util.h + +test_qcow2_CFLAGS = \ + $(ZLIB_CFLAGS) + +test_qcow2_LDADD = \ + libsystemd-shared.la \ + $(ZLIB_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/libimport/import-common.c b/src/grp-machine/grp-import/libimport/import-common.c new file mode 100644 index 0000000000..9061cd4da1 --- /dev/null +++ b/src/grp-machine/grp-import/libimport/import-common.c @@ -0,0 +1,222 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include "basic/btrfs-util.h" +#include "basic/capability-util.h" +#include "basic/fd-util.h" +#include "basic/signal-util.h" +#include "basic/util.h" + +#include "import-common.h" + +int import_make_read_only_fd(int fd) { + int r; + + assert(fd >= 0); + + /* First, let's make this a read-only subvolume if it refers + * to a subvolume */ + r = btrfs_subvol_set_read_only_fd(fd, true); + if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) { + struct stat st; + + /* This doesn't refer to a subvolume, or the file + * system isn't even btrfs. In that, case fall back to + * chmod()ing */ + + r = fstat(fd, &st); + if (r < 0) + return log_error_errno(errno, "Failed to stat temporary image: %m"); + + /* Drop "w" flag */ + if (fchmod(fd, st.st_mode & 07555) < 0) + return log_error_errno(errno, "Failed to chmod() final image: %m"); + + return 0; + + } else if (r < 0) + return log_error_errno(r, "Failed to make subvolume read-only: %m"); + + return 0; +} + +int import_make_read_only(const char *path) { + _cleanup_close_ int fd = 1; + + fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", path); + + return import_make_read_only_fd(fd); +} + +int import_fork_tar_x(const char *path, pid_t *ret) { + _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; + pid_t pid; + int r; + + assert(path); + assert(ret); + + if (pipe2(pipefd, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to create pipe for tar: %m"); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork off tar: %m"); + + if (pid == 0) { + int null_fd; + uint64_t retain = + (1ULL << CAP_CHOWN) | + (1ULL << CAP_FOWNER) | + (1ULL << CAP_FSETID) | + (1ULL << CAP_MKNOD) | + (1ULL << CAP_SETFCAP) | + (1ULL << CAP_DAC_OVERRIDE); + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + pipefd[1] = safe_close(pipefd[1]); + + if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (pipefd[0] != STDIN_FILENO) + pipefd[0] = safe_close(pipefd[0]); + + null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); + if (null_fd < 0) { + log_error_errno(errno, "Failed to open /dev/null: %m"); + _exit(EXIT_FAILURE); + } + + if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (null_fd != STDOUT_FILENO) + null_fd = safe_close(null_fd); + + stdio_unset_cloexec(); + + 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); + if (r < 0) + log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); + + execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); + log_error_errno(errno, "Failed to execute tar: %m"); + _exit(EXIT_FAILURE); + } + + pipefd[0] = safe_close(pipefd[0]); + r = pipefd[1]; + pipefd[1] = -1; + + *ret = pid; + + return r; +} + +int import_fork_tar_c(const char *path, pid_t *ret) { + _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; + pid_t pid; + int r; + + assert(path); + assert(ret); + + if (pipe2(pipefd, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to create pipe for tar: %m"); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork off tar: %m"); + + if (pid == 0) { + int null_fd; + uint64_t retain = (1ULL << CAP_DAC_OVERRIDE); + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + pipefd[0] = safe_close(pipefd[0]); + + if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (pipefd[1] != STDOUT_FILENO) + pipefd[1] = safe_close(pipefd[1]); + + null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); + if (null_fd < 0) { + log_error_errno(errno, "Failed to open /dev/null: %m"); + _exit(EXIT_FAILURE); + } + + if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (null_fd != STDIN_FILENO) + null_fd = safe_close(null_fd); + + stdio_unset_cloexec(); + + 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); + if (r < 0) + log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); + + execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL); + log_error_errno(errno, "Failed to execute tar: %m"); + _exit(EXIT_FAILURE); + } + + pipefd[1] = safe_close(pipefd[1]); + r = pipefd[0]; + pipefd[0] = -1; + + *ret = pid; + + return r; +} diff --git a/src/grp-machine/grp-import/libimport/import-common.h b/src/grp-machine/grp-import/libimport/import-common.h new file mode 100644 index 0000000000..07d3250e71 --- /dev/null +++ b/src/grp-machine/grp-import/libimport/import-common.h @@ -0,0 +1,26 @@ +#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 . +***/ + +int import_make_read_only_fd(int fd); +int import_make_read_only(const char *path); + +int import_fork_tar_c(const char *path, pid_t *ret); +int import_fork_tar_x(const char *path, pid_t *ret); diff --git a/src/grp-machine/grp-import/libimport/import-compress.c b/src/grp-machine/grp-import/libimport/import-compress.c new file mode 100644 index 0000000000..4f1a9891e8 --- /dev/null +++ b/src/grp-machine/grp-import/libimport/import-compress.c @@ -0,0 +1,470 @@ +/*** + 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 . +***/ + +#include "basic/string-table.h" +#include "basic/util.h" + +#include "import-compress.h" + +void import_compress_free(ImportCompress *c) { + assert(c); + + if (c->type == IMPORT_COMPRESS_XZ) + lzma_end(&c->xz); + else if (c->type == IMPORT_COMPRESS_GZIP) { + if (c->encoding) + deflateEnd(&c->gzip); + else + inflateEnd(&c->gzip); + } else if (c->type == IMPORT_COMPRESS_BZIP2) { + if (c->encoding) + BZ2_bzCompressEnd(&c->bzip2); + else + BZ2_bzDecompressEnd(&c->bzip2); + } + + c->type = IMPORT_COMPRESS_UNKNOWN; +} + +int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) { + static const uint8_t xz_signature[] = { + 0xfd, '7', 'z', 'X', 'Z', 0x00 + }; + static const uint8_t gzip_signature[] = { + 0x1f, 0x8b + }; + static const uint8_t bzip2_signature[] = { + 'B', 'Z', 'h' + }; + + int r; + + assert(c); + + if (c->type != IMPORT_COMPRESS_UNKNOWN) + return 1; + + if (size < MAX3(sizeof(xz_signature), + sizeof(gzip_signature), + sizeof(bzip2_signature))) + return 0; + + assert(data); + + if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) { + lzma_ret xzr; + + xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); + if (xzr != LZMA_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_XZ; + + } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) { + r = inflateInit2(&c->gzip, 15+16); + if (r != Z_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_GZIP; + + } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) { + r = BZ2_bzDecompressInit(&c->bzip2, 0, 0); + if (r != BZ_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_BZIP2; + } else + c->type = IMPORT_COMPRESS_UNCOMPRESSED; + + c->encoding = false; + + return 1; +} + +int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) { + int r; + + assert(c); + assert(callback); + + r = import_uncompress_detect(c, data, size); + if (r <= 0) + return r; + + if (c->encoding) + return -EINVAL; + + if (size <= 0) + return 1; + + assert(data); + + switch (c->type) { + + case IMPORT_COMPRESS_UNCOMPRESSED: + r = callback(data, size, userdata); + if (r < 0) + return r; + + break; + + case IMPORT_COMPRESS_XZ: + c->xz.next_in = data; + c->xz.avail_in = size; + + while (c->xz.avail_in > 0) { + uint8_t buffer[16 * 1024]; + lzma_ret lzr; + + c->xz.next_out = buffer; + c->xz.avail_out = sizeof(buffer); + + lzr = lzma_code(&c->xz, LZMA_RUN); + if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) + return -EIO; + + r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata); + if (r < 0) + return r; + } + + break; + + case IMPORT_COMPRESS_GZIP: + c->gzip.next_in = (void*) data; + c->gzip.avail_in = size; + + while (c->gzip.avail_in > 0) { + uint8_t buffer[16 * 1024]; + + c->gzip.next_out = buffer; + c->gzip.avail_out = sizeof(buffer); + + r = inflate(&c->gzip, Z_NO_FLUSH); + if (r != Z_OK && r != Z_STREAM_END) + return -EIO; + + r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata); + if (r < 0) + return r; + } + + break; + + case IMPORT_COMPRESS_BZIP2: + c->bzip2.next_in = (void*) data; + c->bzip2.avail_in = size; + + while (c->bzip2.avail_in > 0) { + uint8_t buffer[16 * 1024]; + + c->bzip2.next_out = (char*) buffer; + c->bzip2.avail_out = sizeof(buffer); + + r = BZ2_bzDecompress(&c->bzip2); + if (r != BZ_OK && r != BZ_STREAM_END) + return -EIO; + + r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata); + if (r < 0) + return r; + } + + break; + + default: + assert_not_reached("Unknown compression"); + } + + return 1; +} + +int import_compress_init(ImportCompress *c, ImportCompressType t) { + int r; + + assert(c); + + switch (t) { + + case IMPORT_COMPRESS_XZ: { + lzma_ret xzr; + + xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); + if (xzr != LZMA_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_XZ; + break; + } + + case IMPORT_COMPRESS_GZIP: + r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); + if (r != Z_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_GZIP; + break; + + case IMPORT_COMPRESS_BZIP2: + r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0); + if (r != BZ_OK) + return -EIO; + + c->type = IMPORT_COMPRESS_BZIP2; + break; + + case IMPORT_COMPRESS_UNCOMPRESSED: + c->type = IMPORT_COMPRESS_UNCOMPRESSED; + break; + + default: + return -EOPNOTSUPP; + } + + c->encoding = true; + return 0; +} + +static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated) { + size_t l; + void *p; + + if (*buffer_allocated > *buffer_size) + return 0; + + l = MAX(16*1024U, (*buffer_size * 2)); + p = realloc(*buffer, l); + if (!p) + return -ENOMEM; + + *buffer = p; + *buffer_allocated = l; + + return 1; +} + +int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { + int r; + + assert(c); + assert(buffer); + assert(buffer_size); + assert(buffer_allocated); + + if (!c->encoding) + return -EINVAL; + + if (size <= 0) + return 0; + + assert(data); + + *buffer_size = 0; + + switch (c->type) { + + case IMPORT_COMPRESS_XZ: + + c->xz.next_in = data; + c->xz.avail_in = size; + + while (c->xz.avail_in > 0) { + lzma_ret lzr; + + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->xz.next_out = (uint8_t*) *buffer + *buffer_size; + c->xz.avail_out = *buffer_allocated - *buffer_size; + + lzr = lzma_code(&c->xz, LZMA_RUN); + if (lzr != LZMA_OK) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; + } + + break; + + case IMPORT_COMPRESS_GZIP: + + c->gzip.next_in = (void*) data; + c->gzip.avail_in = size; + + while (c->gzip.avail_in > 0) { + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; + c->gzip.avail_out = *buffer_allocated - *buffer_size; + + r = deflate(&c->gzip, Z_NO_FLUSH); + if (r != Z_OK) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; + } + + break; + + case IMPORT_COMPRESS_BZIP2: + + c->bzip2.next_in = (void*) data; + c->bzip2.avail_in = size; + + while (c->bzip2.avail_in > 0) { + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); + c->bzip2.avail_out = *buffer_allocated - *buffer_size; + + r = BZ2_bzCompress(&c->bzip2, BZ_RUN); + if (r != BZ_RUN_OK) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; + } + + break; + + case IMPORT_COMPRESS_UNCOMPRESSED: + + if (*buffer_allocated < size) { + void *p; + + p = realloc(*buffer, size); + if (!p) + return -ENOMEM; + + *buffer = p; + *buffer_allocated = size; + } + + memcpy(*buffer, data, size); + *buffer_size = size; + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { + int r; + + assert(c); + assert(buffer); + assert(buffer_size); + assert(buffer_allocated); + + if (!c->encoding) + return -EINVAL; + + *buffer_size = 0; + + switch (c->type) { + + case IMPORT_COMPRESS_XZ: { + lzma_ret lzr; + + c->xz.avail_in = 0; + + do { + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->xz.next_out = (uint8_t*) *buffer + *buffer_size; + c->xz.avail_out = *buffer_allocated - *buffer_size; + + lzr = lzma_code(&c->xz, LZMA_FINISH); + if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; + } while (lzr != LZMA_STREAM_END); + + break; + } + + case IMPORT_COMPRESS_GZIP: + c->gzip.avail_in = 0; + + do { + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; + c->gzip.avail_out = *buffer_allocated - *buffer_size; + + r = deflate(&c->gzip, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; + } while (r != Z_STREAM_END); + + break; + + case IMPORT_COMPRESS_BZIP2: + c->bzip2.avail_in = 0; + + do { + r = enlarge_buffer(buffer, buffer_size, buffer_allocated); + if (r < 0) + return r; + + c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); + c->bzip2.avail_out = *buffer_allocated - *buffer_size; + + r = BZ2_bzCompress(&c->bzip2, BZ_FINISH); + if (r != BZ_FINISH_OK && r != BZ_STREAM_END) + return -EIO; + + *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; + } while (r != BZ_STREAM_END); + + break; + + case IMPORT_COMPRESS_UNCOMPRESSED: + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = { + [IMPORT_COMPRESS_UNKNOWN] = "unknown", + [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed", + [IMPORT_COMPRESS_XZ] = "xz", + [IMPORT_COMPRESS_GZIP] = "gzip", + [IMPORT_COMPRESS_BZIP2] = "bzip2", +}; + +DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType); diff --git a/src/grp-machine/grp-import/libimport/import-compress.h b/src/grp-machine/grp-import/libimport/import-compress.h new file mode 100644 index 0000000000..130afb4cd0 --- /dev/null +++ b/src/grp-machine/grp-import/libimport/import-compress.h @@ -0,0 +1,61 @@ +#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 . +***/ + +#include +#include +#include +#include + +#include "basic/macro.h" + +typedef enum ImportCompressType { + IMPORT_COMPRESS_UNKNOWN, + IMPORT_COMPRESS_UNCOMPRESSED, + IMPORT_COMPRESS_XZ, + IMPORT_COMPRESS_GZIP, + IMPORT_COMPRESS_BZIP2, + _IMPORT_COMPRESS_TYPE_MAX, + _IMPORT_COMPRESS_TYPE_INVALID = -1, +} ImportCompressType; + +typedef struct ImportCompress { + ImportCompressType type; + bool encoding; + union { + lzma_stream xz; + z_stream gzip; + bz_stream bzip2; + }; +} ImportCompress; + +typedef int (*ImportCompressCallback)(const void *data, size_t size, void *userdata); + +void import_compress_free(ImportCompress *c); + +int import_uncompress_detect(ImportCompress *c, const void *data, size_t size); +int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata); + +int import_compress_init(ImportCompress *c, ImportCompressType t); +int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated); +int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated); + +const char* import_compress_type_to_string(ImportCompressType t) _const_; +ImportCompressType import_compress_type_from_string(const char *s) _pure_; diff --git a/src/grp-machine/grp-import/libimport/qcow2-util.c b/src/grp-machine/grp-import/libimport/qcow2-util.c new file mode 100644 index 0000000000..bc50b7d0b3 --- /dev/null +++ b/src/grp-machine/grp-import/libimport/qcow2-util.c @@ -0,0 +1,353 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/sparse-endian.h" +#include "basic/util.h" + +#include "qcow2-util.h" + +#define QCOW2_MAGIC 0x514649fb + +#define QCOW2_COPIED (1ULL << 63) +#define QCOW2_COMPRESSED (1ULL << 62) +#define QCOW2_ZERO (1ULL << 0) + +typedef struct _packed_ Header { + be32_t magic; + be32_t version; + + be64_t backing_file_offset; + be32_t backing_file_size; + + be32_t cluster_bits; + be64_t size; + be32_t crypt_method; + + be32_t l1_size; + be64_t l1_table_offset; + + be64_t refcount_table_offset; + be32_t refcount_table_clusters; + + be32_t nb_snapshots; + be64_t snapshots_offset; + + /* The remainder is only present on QCOW3 */ + be64_t incompatible_features; + be64_t compatible_features; + be64_t autoclear_features; + + be32_t refcount_order; + be32_t header_length; +} Header; + +#define HEADER_MAGIC(header) be32toh((header)->magic) +#define HEADER_VERSION(header) be32toh((header)->version) +#define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits) +#define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header)) +#define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3) +#define HEADER_SIZE(header) be64toh((header)->size) +#define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method) +#define HEADER_L1_SIZE(header) be32toh((header)->l1_size) +#define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t)) +#define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset) + +static uint32_t HEADER_HEADER_LENGTH(const Header *h) { + if (HEADER_VERSION(h) < 3) + return offsetof(Header, incompatible_features); + + return be32toh(h->header_length); +} + +static int copy_cluster( + int sfd, uint64_t soffset, + int dfd, uint64_t doffset, + uint64_t cluster_size, + void *buffer) { + + ssize_t l; + int r; + + r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size); + if (r >= 0) + return r; + + l = pread(sfd, buffer, cluster_size, soffset); + if (l < 0) + return -errno; + if ((uint64_t) l != cluster_size) + return -EIO; + + l = pwrite(dfd, buffer, cluster_size, doffset); + if (l < 0) + return -errno; + if ((uint64_t) l != cluster_size) + return -EIO; + + return 0; +} + +static int decompress_cluster( + int sfd, uint64_t soffset, + int dfd, uint64_t doffset, + uint64_t compressed_size, + uint64_t cluster_size, + void *buffer1, + void *buffer2) { + + _cleanup_free_ void *large_buffer = NULL; + z_stream s = {}; + uint64_t sz; + ssize_t l; + int r; + + if (compressed_size > cluster_size) { + /* The usual cluster buffer doesn't suffice, let's + * allocate a larger one, temporarily */ + + large_buffer = malloc(compressed_size); + if (!large_buffer) + return -ENOMEM; + + buffer1 = large_buffer; + } + + l = pread(sfd, buffer1, compressed_size, soffset); + if (l < 0) + return -errno; + if ((uint64_t) l != compressed_size) + return -EIO; + + s.next_in = buffer1; + s.avail_in = compressed_size; + s.next_out = buffer2; + s.avail_out = cluster_size; + + r = inflateInit2(&s, -12); + if (r != Z_OK) + return -EIO; + + r = inflate(&s, Z_FINISH); + sz = (uint8_t*) s.next_out - (uint8_t*) buffer2; + inflateEnd(&s); + if (r != Z_STREAM_END || sz != cluster_size) + return -EIO; + + l = pwrite(dfd, buffer2, cluster_size, doffset); + if (l < 0) + return -errno; + if ((uint64_t) l != cluster_size) + return -EIO; + + return 0; +} + +static int normalize_offset( + const Header *header, + uint64_t p, + uint64_t *ret, + bool *compressed, + uint64_t *compressed_size) { + + uint64_t q; + + q = be64toh(p); + + if (q & QCOW2_COMPRESSED) { + uint64_t sz, csize_shift, csize_mask; + + if (!compressed) + return -EOPNOTSUPP; + + csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8); + csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1; + sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511); + q &= ((1ULL << csize_shift) - 1); + + if (compressed_size) + *compressed_size = sz; + + *compressed = true; + + } else { + if (compressed) { + *compressed = false; + *compressed_size = 0; + } + + if (q & QCOW2_ZERO) { + /* We make no distinction between zero blocks and holes */ + *ret = 0; + return 0; + } + + q &= ~QCOW2_COPIED; + } + + *ret = q; + return q > 0; /* returns positive if not a hole */ +} + +static int verify_header(const Header *header) { + assert(header); + + if (HEADER_MAGIC(header) != QCOW2_MAGIC) + return -EBADMSG; + + if (HEADER_VERSION(header) != 2 && + HEADER_VERSION(header) != 3) + return -EOPNOTSUPP; + + if (HEADER_CRYPT_METHOD(header) != 0) + return -EOPNOTSUPP; + + if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */ + return -EBADMSG; + + if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */ + return -EBADMSG; + + if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0) + return -EBADMSG; + + if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */ + return -EBADMSG; + + if (HEADER_VERSION(header) == 3) { + + if (header->incompatible_features != 0) + return -EOPNOTSUPP; + + if (HEADER_HEADER_LENGTH(header) < sizeof(Header)) + return -EBADMSG; + } + + return 0; +} + +int qcow2_convert(int qcow2_fd, int raw_fd) { + _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL; + _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL; + uint64_t sz, i; + Header header; + ssize_t l; + int r; + + l = pread(qcow2_fd, &header, sizeof(header), 0); + if (l < 0) + return -errno; + if (l != sizeof(header)) + return -EIO; + + r = verify_header(&header); + if (r < 0) + return r; + + l1_table = new(be64_t, HEADER_L1_SIZE(&header)); + if (!l1_table) + return -ENOMEM; + + l2_table = malloc(HEADER_CLUSTER_SIZE(&header)); + if (!l2_table) + return -ENOMEM; + + buffer1 = malloc(HEADER_CLUSTER_SIZE(&header)); + if (!buffer1) + return -ENOMEM; + + buffer2 = malloc(HEADER_CLUSTER_SIZE(&header)); + if (!buffer2) + return -ENOMEM; + + /* Empty the file if it exists, we rely on zero bits */ + if (ftruncate(raw_fd, 0) < 0) + return -errno; + + if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0) + return -errno; + + sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header); + l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header)); + if (l < 0) + return -errno; + if ((uint64_t) l != sz) + return -EIO; + + for (i = 0; i < HEADER_L1_SIZE(&header); i ++) { + uint64_t l2_begin, j; + + r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin); + if (l < 0) + return -errno; + if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header)) + return -EIO; + + for (j = 0; j < HEADER_L2_SIZE(&header); j++) { + uint64_t data_begin, p, compressed_size; + bool compressed; + + p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header); + + r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size); + if (r < 0) + return r; + if (r == 0) + continue; + + if (compressed) + r = decompress_cluster( + qcow2_fd, data_begin, + raw_fd, p, + compressed_size, HEADER_CLUSTER_SIZE(&header), + buffer1, buffer2); + else + r = copy_cluster( + qcow2_fd, data_begin, + raw_fd, p, + HEADER_CLUSTER_SIZE(&header), buffer1); + if (r < 0) + return r; + } + } + + return 0; +} + +int qcow2_detect(int fd) { + be32_t id; + ssize_t l; + + l = pread(fd, &id, sizeof(id), 0); + if (l < 0) + return -errno; + if (l != sizeof(id)) + return -EIO; + + return htobe32(QCOW2_MAGIC) == id; +} diff --git a/src/grp-machine/grp-import/libimport/qcow2-util.h b/src/grp-machine/grp-import/libimport/qcow2-util.h new file mode 100644 index 0000000000..6dddac8cdf --- /dev/null +++ b/src/grp-machine/grp-import/libimport/qcow2-util.h @@ -0,0 +1,23 @@ +#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 . +***/ + +int qcow2_detect(int fd); +int qcow2_convert(int qcow2_fd, int raw_fd); diff --git a/src/grp-machine/grp-import/libimport/test-qcow2.c b/src/grp-machine/grp-import/libimport/test-qcow2.c new file mode 100644 index 0000000000..15d7c29aef --- /dev/null +++ b/src/grp-machine/grp-import/libimport/test-qcow2.c @@ -0,0 +1,54 @@ +/*** + 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 . +***/ + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/util.h" + +#include "qcow2-util.h" + +int main(int argc, char *argv[]) { + _cleanup_close_ int sfd = -1, dfd = -1; + int r; + + if (argc != 3) { + log_error("Needs two arguments."); + return EXIT_FAILURE; + } + + sfd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (sfd < 0) { + log_error_errno(errno, "Can't open source file: %m"); + return EXIT_FAILURE; + } + + dfd = open(argv[2], O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666); + if (dfd < 0) { + log_error_errno(errno, "Can't open destination file: %m"); + return EXIT_FAILURE; + } + + r = qcow2_convert(sfd, dfd); + if (r < 0) { + log_error_errno(r, "Failed to unpack: %m"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/grp-machine/grp-import/systemd-export/Makefile b/src/grp-machine/grp-import/systemd-export/Makefile new file mode 100644 index 0000000000..fa6306226f --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/Makefile @@ -0,0 +1,50 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-export + +systemd_export_SOURCES = \ + src/import/export.c \ + src/import/export-tar.c \ + src/import/export-tar.h \ + src/import/export-raw.c \ + src/import/export-raw.h \ + src/import/import-common.c \ + src/import/import-common.h \ + src/import/import-compress.c \ + src/import/import-compress.h + +systemd_export_CFLAGS = \ + $(XZ_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(BZIP2_CFLAGS) + +systemd_export_LDADD = \ + libsystemd-shared.la \ + $(XZ_LIBS) \ + $(ZLIB_LIBS) \ + $(BZIP2_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/systemd-export/export-raw.c b/src/grp-machine/grp-import/systemd-export/export-raw.c new file mode 100644 index 0000000000..c17fc952d3 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/export-raw.c @@ -0,0 +1,353 @@ +/*** + 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 . +***/ + +#include + +/* 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 +#undef basename + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/ratelimit.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "import-common.h" + +#include "export-raw.h" + +#define COPY_BUFFER_SIZE (16*1024) + +struct RawExport { + sd_event *event; + + RawExportFinished on_finished; + void *userdata; + + char *path; + + int input_fd; + int output_fd; + + ImportCompress compress; + + sd_event_source *output_event_source; + + void *buffer; + size_t buffer_size; + size_t buffer_allocated; + + uint64_t written_compressed; + uint64_t written_uncompressed; + + unsigned last_percent; + RateLimit progress_rate_limit; + + struct stat st; + + bool eof; + bool tried_reflink; + bool tried_sendfile; +}; + +RawExport *raw_export_unref(RawExport *e) { + if (!e) + return NULL; + + sd_event_source_unref(e->output_event_source); + + import_compress_free(&e->compress); + + sd_event_unref(e->event); + + safe_close(e->input_fd); + + free(e->buffer); + free(e->path); + free(e); + + return NULL; +} + +int raw_export_new( + RawExport **ret, + sd_event *event, + RawExportFinished on_finished, + void *userdata) { + + _cleanup_(raw_export_unrefp) RawExport *e = NULL; + int r; + + assert(ret); + + e = new0(RawExport, 1); + if (!e) + return -ENOMEM; + + e->output_fd = e->input_fd = -1; + e->on_finished = on_finished; + e->userdata = userdata; + + RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); + e->last_percent = (unsigned) -1; + + if (event) + e->event = sd_event_ref(event); + else { + r = sd_event_default(&e->event); + if (r < 0) + return r; + } + + *ret = e; + e = NULL; + + return 0; +} + +static void raw_export_report_progress(RawExport *e) { + unsigned percent; + assert(e); + + if (e->written_uncompressed >= (uint64_t) e->st.st_size) + percent = 100; + else + percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size); + + if (percent == e->last_percent) + return; + + if (!ratelimit_test(&e->progress_rate_limit)) + return; + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_info("Exported %u%%.", percent); + + e->last_percent = percent; +} + +static int raw_export_process(RawExport *e) { + ssize_t l; + int r; + + assert(e); + + if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { + + /* If we shall take an uncompressed snapshot we can + * reflink source to destination directly. Let's see + * if this works. */ + + r = btrfs_reflink(e->input_fd, e->output_fd); + if (r >= 0) { + r = 0; + goto finish; + } + + e->tried_reflink = true; + } + + if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { + + l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + e->tried_sendfile = true; + } else if (l == 0) { + r = 0; + goto finish; + } else { + e->written_uncompressed += l; + e->written_compressed += l; + + raw_export_report_progress(e); + + return 0; + } + } + + while (e->buffer_size <= 0) { + uint8_t input[COPY_BUFFER_SIZE]; + + if (e->eof) { + r = 0; + goto finish; + } + + l = read(e->input_fd, input, sizeof(input)); + if (l < 0) { + r = log_error_errno(errno, "Failed to read raw file: %m"); + goto finish; + } + + if (l == 0) { + e->eof = true; + r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); + } else { + e->written_uncompressed += l; + r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); + } + if (r < 0) { + r = log_error_errno(r, "Failed to encode: %m"); + goto finish; + } + } + + l = write(e->output_fd, e->buffer, e->buffer_size); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + r = log_error_errno(errno, "Failed to write output file: %m"); + goto finish; + } + + assert((size_t) l <= e->buffer_size); + memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); + e->buffer_size -= l; + e->written_compressed += l; + + raw_export_report_progress(e); + + return 0; + +finish: + if (r >= 0) { + (void) copy_times(e->input_fd, e->output_fd); + (void) copy_xattr(e->input_fd, e->output_fd); + } + + if (e->on_finished) + e->on_finished(e, r, e->userdata); + else + sd_event_exit(e->event, r); + + return 0; +} + +static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + RawExport *i = userdata; + + return raw_export_process(i); +} + +static int raw_export_on_defer(sd_event_source *s, void *userdata) { + RawExport *i = userdata; + + return raw_export_process(i); +} + +static int reflink_snapshot(int fd, const char *path) { + char *p, *d; + int new_fd, r; + + p = strdupa(path); + d = dirname(p); + + new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600); + if (new_fd < 0) { + _cleanup_free_ char *t = NULL; + + r = tempfn_random(path, NULL, &t); + if (r < 0) + return r; + + new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600); + if (new_fd < 0) + return -errno; + + (void) unlink(t); + } + + r = btrfs_reflink(fd, new_fd); + if (r < 0) { + safe_close(new_fd); + return r; + } + + return new_fd; +} + +int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) { + _cleanup_close_ int sfd = -1, tfd = -1; + int r; + + assert(e); + assert(path); + assert(fd >= 0); + assert(compress < _IMPORT_COMPRESS_TYPE_MAX); + assert(compress != IMPORT_COMPRESS_UNKNOWN); + + if (e->output_fd >= 0) + return -EBUSY; + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + r = free_and_strdup(&e->path, path); + if (r < 0) + return r; + + sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (sfd < 0) + return -errno; + + if (fstat(sfd, &e->st) < 0) + return -errno; + if (!S_ISREG(e->st.st_mode)) + return -ENOTTY; + + /* Try to take a reflink snapshot of the file, if we can t make the export atomic */ + tfd = reflink_snapshot(sfd, path); + if (tfd >= 0) { + e->input_fd = tfd; + tfd = -1; + } else { + e->input_fd = sfd; + sfd = -1; + } + + r = import_compress_init(&e->compress, compress); + if (r < 0) + return r; + + r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e); + if (r == -EPERM) { + r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); + } + if (r < 0) + return r; + + e->output_fd = fd; + return r; +} diff --git a/src/grp-machine/grp-import/systemd-export/export-raw.h b/src/grp-machine/grp-import/systemd-export/export-raw.h new file mode 100644 index 0000000000..cf34d512bc --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/export-raw.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "import-compress.h" + +typedef struct RawExport RawExport; +typedef void (*RawExportFinished)(RawExport *export, int error, void *userdata); + +int raw_export_new(RawExport **export, sd_event *event, RawExportFinished on_finished, void *userdata); +RawExport* raw_export_unref(RawExport *export); + +DEFINE_TRIVIAL_CLEANUP_FUNC(RawExport*, raw_export_unref); + +int raw_export_start(RawExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/grp-machine/grp-import/systemd-export/export-tar.c b/src/grp-machine/grp-import/systemd-export/export-tar.c new file mode 100644 index 0000000000..e2b3c5f2e9 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/export-tar.c @@ -0,0 +1,329 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/process-util.h" +#include "basic/ratelimit.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "import-common.h" + +#include "export-tar.h" + +#define COPY_BUFFER_SIZE (16*1024) + +struct TarExport { + sd_event *event; + + TarExportFinished on_finished; + void *userdata; + + char *path; + char *temp_path; + + int output_fd; + int tar_fd; + + ImportCompress compress; + + sd_event_source *output_event_source; + + void *buffer; + size_t buffer_size; + size_t buffer_allocated; + + uint64_t written_compressed; + uint64_t written_uncompressed; + + pid_t tar_pid; + + struct stat st; + uint64_t quota_referenced; + + unsigned last_percent; + RateLimit progress_rate_limit; + + bool eof; + bool tried_splice; +}; + +TarExport *tar_export_unref(TarExport *e) { + if (!e) + return NULL; + + sd_event_source_unref(e->output_event_source); + + if (e->tar_pid > 1) { + (void) kill_and_sigcont(e->tar_pid, SIGKILL); + (void) wait_for_terminate(e->tar_pid, NULL); + } + + if (e->temp_path) { + (void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA); + free(e->temp_path); + } + + import_compress_free(&e->compress); + + sd_event_unref(e->event); + + safe_close(e->tar_fd); + + free(e->buffer); + free(e->path); + free(e); + + return NULL; +} + +int tar_export_new( + TarExport **ret, + sd_event *event, + TarExportFinished on_finished, + void *userdata) { + + _cleanup_(tar_export_unrefp) TarExport *e = NULL; + int r; + + assert(ret); + + e = new0(TarExport, 1); + if (!e) + return -ENOMEM; + + e->output_fd = e->tar_fd = -1; + e->on_finished = on_finished; + e->userdata = userdata; + e->quota_referenced = (uint64_t) -1; + + RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); + e->last_percent = (unsigned) -1; + + if (event) + e->event = sd_event_ref(event); + else { + r = sd_event_default(&e->event); + if (r < 0) + return r; + } + + *ret = e; + e = NULL; + + return 0; +} + +static void tar_export_report_progress(TarExport *e) { + unsigned percent; + assert(e); + + /* Do we have any quota info? If not, we don't know anything about the progress */ + if (e->quota_referenced == (uint64_t) -1) + return; + + if (e->written_uncompressed >= e->quota_referenced) + percent = 100; + else + percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced); + + if (percent == e->last_percent) + return; + + if (!ratelimit_test(&e->progress_rate_limit)) + return; + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_info("Exported %u%%.", percent); + + e->last_percent = percent; +} + +static int tar_export_process(TarExport *e) { + ssize_t l; + int r; + + assert(e); + + if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { + + l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + e->tried_splice = true; + } else if (l == 0) { + r = 0; + goto finish; + } else { + e->written_uncompressed += l; + e->written_compressed += l; + + tar_export_report_progress(e); + + return 0; + } + } + + while (e->buffer_size <= 0) { + uint8_t input[COPY_BUFFER_SIZE]; + + if (e->eof) { + r = 0; + goto finish; + } + + l = read(e->tar_fd, input, sizeof(input)); + if (l < 0) { + r = log_error_errno(errno, "Failed to read tar file: %m"); + goto finish; + } + + if (l == 0) { + e->eof = true; + r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); + } else { + e->written_uncompressed += l; + r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); + } + if (r < 0) { + r = log_error_errno(r, "Failed to encode: %m"); + goto finish; + } + } + + l = write(e->output_fd, e->buffer, e->buffer_size); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + r = log_error_errno(errno, "Failed to write output file: %m"); + goto finish; + } + + assert((size_t) l <= e->buffer_size); + memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); + e->buffer_size -= l; + e->written_compressed += l; + + tar_export_report_progress(e); + + return 0; + +finish: + if (e->on_finished) + e->on_finished(e, r, e->userdata); + else + sd_event_exit(e->event, r); + + return 0; +} + +static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + TarExport *i = userdata; + + return tar_export_process(i); +} + +static int tar_export_on_defer(sd_event_source *s, void *userdata) { + TarExport *i = userdata; + + return tar_export_process(i); +} + +int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) { + _cleanup_close_ int sfd = -1; + int r; + + assert(e); + assert(path); + assert(fd >= 0); + assert(compress < _IMPORT_COMPRESS_TYPE_MAX); + assert(compress != IMPORT_COMPRESS_UNKNOWN); + + if (e->output_fd >= 0) + return -EBUSY; + + sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (sfd < 0) + return -errno; + + if (fstat(sfd, &e->st) < 0) + return -errno; + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + r = free_and_strdup(&e->path, path); + if (r < 0) + return r; + + e->quota_referenced = (uint64_t) -1; + + if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ + BtrfsQuotaInfo q; + + r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); + if (r >= 0) + e->quota_referenced = q.referenced; + + e->temp_path = mfree(e->temp_path); + + r = tempfn_random(path, NULL, &e->temp_path); + if (r < 0) + return r; + + /* Let's try to make a snapshot, if we can, so that the export is atomic */ + r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); + if (r < 0) { + log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); + e->temp_path = mfree(e->temp_path); + } + } + + r = import_compress_init(&e->compress, compress); + if (r < 0) + return r; + + r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e); + if (r == -EPERM) { + r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); + } + if (r < 0) + return r; + + e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid); + if (e->tar_fd < 0) { + e->output_event_source = sd_event_source_unref(e->output_event_source); + return e->tar_fd; + } + + e->output_fd = fd; + return r; +} diff --git a/src/grp-machine/grp-import/systemd-export/export-tar.h b/src/grp-machine/grp-import/systemd-export/export-tar.h new file mode 100644 index 0000000000..c048a6d642 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/export-tar.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "import-compress.h" + +typedef struct TarExport TarExport; +typedef void (*TarExportFinished)(TarExport *export, int error, void *userdata); + +int tar_export_new(TarExport **export, sd_event *event, TarExportFinished on_finished, void *userdata); +TarExport* tar_export_unref(TarExport *export); + +DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref); + +int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/grp-machine/grp-import/systemd-export/export.c b/src/grp-machine/grp-import/systemd-export/export.c new file mode 100644 index 0000000000..5db164c7d1 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-export/export.c @@ -0,0 +1,321 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/verbs.h" +#include "shared/import-util.h" +#include "shared/machine-image.h" + +#include "export-raw.h" +#include "export-tar.h" + +static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN; + +static void determine_compression_from_filename(const char *p) { + + if (arg_compress != IMPORT_COMPRESS_UNKNOWN) + return; + + if (!p) { + arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; + return; + } + + if (endswith(p, ".xz")) + arg_compress = IMPORT_COMPRESS_XZ; + else if (endswith(p, ".gz")) + arg_compress = IMPORT_COMPRESS_GZIP; + else if (endswith(p, ".bz2")) + arg_compress = IMPORT_COMPRESS_BZIP2; + else + arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; +} + +static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + log_notice("Transfer aborted."); + sd_event_exit(sd_event_source_get_event(s), EINTR); + return 0; +} + +static void on_tar_finished(TarExport *export, int error, void *userdata) { + sd_event *event = userdata; + assert(export); + + if (error == 0) + log_info("Operation completed successfully."); + + sd_event_exit(event, abs(error)); +} + +static int export_tar(int argc, char *argv[], void *userdata) { + _cleanup_(tar_export_unrefp) TarExport *export = NULL; + _cleanup_(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; + int r, fd; + + if (machine_name_is_valid(argv[1])) { + r = image_find(argv[1], &image); + if (r < 0) + return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); + if (r == 0) { + log_error("Machine image %s not found.", argv[1]); + return -ENOENT; + } + + local = image->path; + } else + local = argv[1]; + + if (argc >= 3) + path = argv[2]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + determine_compression_from_filename(path); + + if (path) { + open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); + if (open_fd < 0) + return log_error_errno(errno, "Failed to open tar image for export: %m"); + + fd = open_fd; + + log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); + } else { + _cleanup_free_ char *pretty = NULL; + + fd = STDOUT_FILENO; + + (void) readlink_malloc("/proc/self/fd/1", &pretty); + log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); + } + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + assert_se(sigprocmask_many(SIG_BLOCK, 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 = tar_export_new(&export, event, on_tar_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate exporter: %m"); + + r = tar_export_start(export, local, fd, arg_compress); + if (r < 0) + return log_error_errno(r, "Failed to export image: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + log_info("Exiting."); + return -r; +} + +static void on_raw_finished(RawExport *export, int error, void *userdata) { + sd_event *event = userdata; + assert(export); + + if (error == 0) + log_info("Operation completed successfully."); + + sd_event_exit(event, abs(error)); +} + +static int export_raw(int argc, char *argv[], void *userdata) { + _cleanup_(raw_export_unrefp) RawExport *export = NULL; + _cleanup_(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; + int r, fd; + + if (machine_name_is_valid(argv[1])) { + r = image_find(argv[1], &image); + if (r < 0) + return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); + if (r == 0) { + log_error("Machine image %s not found.", argv[1]); + return -ENOENT; + } + + local = image->path; + } else + local = argv[1]; + + if (argc >= 3) + path = argv[2]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + determine_compression_from_filename(path); + + if (path) { + open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); + if (open_fd < 0) + return log_error_errno(errno, "Failed to open raw image for export: %m"); + + fd = open_fd; + + log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); + } else { + _cleanup_free_ char *pretty = NULL; + + fd = STDOUT_FILENO; + + (void) readlink_malloc("/proc/self/fd/1", &pretty); + log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); + } + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + assert_se(sigprocmask_many(SIG_BLOCK, 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 = raw_export_new(&export, event, on_raw_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate exporter: %m"); + + r = raw_export_start(export, local, fd, arg_compress); + if (r < 0) + return log_error_errno(r, "Failed to export image: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + log_info("Exiting."); + return -r; +} + +static int help(int argc, char *argv[], void *userdata) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Export container or virtual machine images.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --format=FORMAT Select format\n\n" + "Commands:\n" + " tar NAME [FILE] Export a TAR image\n" + " raw NAME [FILE] Export a RAW image\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_FORMAT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "format", required_argument, NULL, ARG_FORMAT }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(0, NULL, NULL); + + case ARG_VERSION: + return version(); + + case ARG_FORMAT: + if (streq(optarg, "uncompressed")) + arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; + else if (streq(optarg, "xz")) + arg_compress = IMPORT_COMPRESS_XZ; + else if (streq(optarg, "gzip")) + arg_compress = IMPORT_COMPRESS_GZIP; + else if (streq(optarg, "bzip2")) + arg_compress = IMPORT_COMPRESS_BZIP2; + else { + log_error("Unknown format: %s", optarg); + return -EINVAL; + } + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int export_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "tar", 2, 3, 0, export_tar }, + { "raw", 2, 3, 0, export_raw }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +int main(int argc, char *argv[]) { + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + (void) ignore_signals(SIGPIPE, -1); + + r = export_main(argc, argv); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/grp-import/systemd-import/Makefile b/src/grp-machine/grp-import/systemd-import/Makefile new file mode 100644 index 0000000000..8d04ec18c5 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/Makefile @@ -0,0 +1,51 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-import +systemd_import_SOURCES = \ + src/import/import.c \ + src/import/import-raw.c \ + src/import/import-raw.h \ + src/import/import-tar.c \ + src/import/import-tar.h \ + src/import/import-common.c \ + src/import/import-common.h \ + src/import/import-compress.c \ + src/import/import-compress.h \ + src/import/qcow2-util.c \ + src/import/qcow2-util.h + +systemd_import_CFLAGS = \ + $(XZ_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(BZIP2_CFLAGS) + +systemd_import_LDADD = \ + libsystemd-shared.la \ + $(XZ_LIBS) \ + $(ZLIB_LIBS) \ + $(BZIP2_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/systemd-import/import-pubring.gpg b/src/grp-machine/grp-import/systemd-import/import-pubring.gpg new file mode 100644 index 0000000000..be27776896 Binary files /dev/null and b/src/grp-machine/grp-import/systemd-import/import-pubring.gpg differ diff --git a/src/grp-machine/grp-import/systemd-import/import-raw.c b/src/grp-machine/grp-import/systemd-import/import-raw.c new file mode 100644 index 0000000000..55a6d044ab --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/import-raw.c @@ -0,0 +1,468 @@ +/*** + 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 . +***/ + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/chattr-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/ratelimit.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "import-common.h" +#include "import-compress.h" +#include "qcow2-util.h" +#include "shared/machine-pool.h" + +#include "import-raw.h" + +struct RawImport { + sd_event *event; + + char *image_root; + + RawImportFinished on_finished; + void *userdata; + + char *local; + bool force_local; + bool read_only; + bool grow_machine_directory; + + char *temp_path; + char *final_path; + + int input_fd; + int output_fd; + + ImportCompress compress; + + uint64_t written_since_last_grow; + + sd_event_source *input_event_source; + + uint8_t buffer[16*1024]; + size_t buffer_size; + + uint64_t written_compressed; + uint64_t written_uncompressed; + + struct stat st; + + unsigned last_percent; + RateLimit progress_rate_limit; +}; + +RawImport* raw_import_unref(RawImport *i) { + if (!i) + return NULL; + + sd_event_unref(i->event); + + if (i->temp_path) { + (void) unlink(i->temp_path); + free(i->temp_path); + } + + import_compress_free(&i->compress); + + sd_event_source_unref(i->input_event_source); + + safe_close(i->output_fd); + + free(i->final_path); + free(i->image_root); + free(i->local); + free(i); + + return NULL; +} + +int raw_import_new( + RawImport **ret, + sd_event *event, + const char *image_root, + RawImportFinished on_finished, + void *userdata) { + + _cleanup_(raw_import_unrefp) RawImport *i = NULL; + int r; + + assert(ret); + + i = new0(RawImport, 1); + if (!i) + return -ENOMEM; + + i->input_fd = i->output_fd = -1; + i->on_finished = on_finished; + i->userdata = userdata; + + RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); + i->last_percent = (unsigned) -1; + + i->image_root = strdup(image_root ?: "/var/lib/machines"); + if (!i->image_root) + return -ENOMEM; + + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + + if (event) + i->event = sd_event_ref(event); + else { + r = sd_event_default(&i->event); + if (r < 0) + return r; + } + + *ret = i; + i = NULL; + + return 0; +} + +static void raw_import_report_progress(RawImport *i) { + unsigned percent; + assert(i); + + /* We have no size information, unless the source is a regular file */ + if (!S_ISREG(i->st.st_mode)) + return; + + if (i->written_compressed >= (uint64_t) i->st.st_size) + percent = 100; + else + percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); + + if (percent == i->last_percent) + return; + + if (!ratelimit_test(&i->progress_rate_limit)) + return; + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_info("Imported %u%%.", percent); + + i->last_percent = percent; +} + +static int raw_import_maybe_convert_qcow2(RawImport *i) { + _cleanup_close_ int converted_fd = -1; + _cleanup_free_ char *t = NULL; + int r; + + assert(i); + + r = qcow2_detect(i->output_fd); + if (r < 0) + return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); + if (r == 0) + return 0; + + /* This is a QCOW2 image, let's convert it */ + r = tempfn_random(i->final_path, NULL, &t); + if (r < 0) + return log_oom(); + + converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (converted_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", t); + + r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_warning_errno(r, "Failed to set file attributes on %s: %m", t); + + log_info("Unpacking QCOW2 file."); + + r = qcow2_convert(i->output_fd, converted_fd); + if (r < 0) { + unlink(t); + return log_error_errno(r, "Failed to convert qcow2 image: %m"); + } + + (void) unlink(i->temp_path); + free(i->temp_path); + i->temp_path = t; + t = NULL; + + safe_close(i->output_fd); + i->output_fd = converted_fd; + converted_fd = -1; + + return 1; +} + +static int raw_import_finish(RawImport *i) { + int r; + + assert(i); + assert(i->output_fd >= 0); + assert(i->temp_path); + assert(i->final_path); + + /* In case this was a sparse file, make sure the file system is right */ + if (i->written_uncompressed > 0) { + if (ftruncate(i->output_fd, i->written_uncompressed) < 0) + return log_error_errno(errno, "Failed to truncate file: %m"); + } + + r = raw_import_maybe_convert_qcow2(i); + if (r < 0) + return r; + + if (S_ISREG(i->st.st_mode)) { + (void) copy_times(i->input_fd, i->output_fd); + (void) copy_xattr(i->input_fd, i->output_fd); + } + + if (i->read_only) { + r = import_make_read_only_fd(i->output_fd); + if (r < 0) + return r; + } + + if (i->force_local) + (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + + r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); + if (r < 0) + return log_error_errno(r, "Failed to move image into place: %m"); + + i->temp_path = mfree(i->temp_path); + + return 0; +} + +static int raw_import_open_disk(RawImport *i) { + int r; + + assert(i); + + assert(!i->final_path); + assert(!i->temp_path); + assert(i->output_fd < 0); + + i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL); + if (!i->final_path) + return log_oom(); + + r = tempfn_random(i->final_path, NULL, &i->temp_path); + if (r < 0) + return log_oom(); + + (void) mkdir_parents_label(i->temp_path, 0700); + + i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (i->output_fd < 0) + return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path); + + r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); + + return 0; +} + +static int raw_import_try_reflink(RawImport *i) { + off_t p; + int r; + + assert(i); + assert(i->input_fd >= 0); + assert(i->output_fd >= 0); + + if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED) + return 0; + + if (!S_ISREG(i->st.st_mode)) + return 0; + + p = lseek(i->input_fd, 0, SEEK_CUR); + if (p == (off_t) -1) + return log_error_errno(errno, "Failed to read file offset of input file: %m"); + + /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */ + if ((uint64_t) p != (uint64_t) i->buffer_size) + return 0; + + r = btrfs_reflink(i->input_fd, i->output_fd); + if (r >= 0) + return 1; + + return 0; +} + +static int raw_import_write(const void *p, size_t sz, void *userdata) { + RawImport *i = userdata; + ssize_t n; + + if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { + i->written_since_last_grow = 0; + grow_machine_directory(); + } + + n = sparse_write(i->output_fd, p, sz, 64); + if (n < 0) + return -errno; + if ((size_t) n < sz) + return -EIO; + + i->written_uncompressed += sz; + i->written_since_last_grow += sz; + + return 0; +} + +static int raw_import_process(RawImport *i) { + ssize_t l; + int r; + + assert(i); + assert(i->buffer_size < sizeof(i->buffer)); + + l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + r = log_error_errno(errno, "Failed to read input file: %m"); + goto finish; + } + if (l == 0) { + if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { + log_error("Premature end of file: %m"); + r = -EIO; + goto finish; + } + + r = raw_import_finish(i); + goto finish; + } + + i->buffer_size += l; + + if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { + r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); + if (r < 0) { + log_error("Failed to detect file compression: %m"); + goto finish; + } + if (r == 0) /* Need more data */ + return 0; + + r = raw_import_open_disk(i); + if (r < 0) + goto finish; + + r = raw_import_try_reflink(i); + if (r < 0) + goto finish; + if (r > 0) { + r = raw_import_finish(i); + goto finish; + } + } + + r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i); + if (r < 0) { + log_error_errno(r, "Failed to decode and write: %m"); + goto finish; + } + + i->written_compressed += i->buffer_size; + i->buffer_size = 0; + + raw_import_report_progress(i); + + return 0; + +finish: + if (i->on_finished) + i->on_finished(i, r, i->userdata); + else + sd_event_exit(i->event, r); + + return 0; +} + +static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + RawImport *i = userdata; + + return raw_import_process(i); +} + +static int raw_import_on_defer(sd_event_source *s, void *userdata) { + RawImport *i = userdata; + + return raw_import_process(i); +} + +int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) { + int r; + + assert(i); + assert(fd >= 0); + assert(local); + + if (!machine_name_is_valid(local)) + return -EINVAL; + + if (i->input_fd >= 0) + return -EBUSY; + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + r = free_and_strdup(&i->local, local); + if (r < 0) + return r; + i->force_local = force_local; + i->read_only = read_only; + + if (fstat(fd, &i->st) < 0) + return -errno; + + r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i); + if (r == -EPERM) { + /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ + r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); + } + if (r < 0) + return r; + + i->input_fd = fd; + return r; +} diff --git a/src/grp-machine/grp-import/systemd-import/import-raw.h b/src/grp-machine/grp-import/systemd-import/import-raw.h new file mode 100644 index 0000000000..ae7e0b017d --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/import-raw.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "shared/import-util.h" + +typedef struct RawImport RawImport; +typedef void (*RawImportFinished)(RawImport *import, int error, void *userdata); + +int raw_import_new(RawImport **import, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata); +RawImport* raw_import_unref(RawImport *import); + +DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref); + +int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/grp-machine/grp-import/systemd-import/import-tar.c b/src/grp-machine/grp-import/systemd-import/import-tar.c new file mode 100644 index 0000000000..0d61b5f01d --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/import-tar.c @@ -0,0 +1,389 @@ +/*** + 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 . +***/ + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/ratelimit.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "import-common.h" +#include "import-compress.h" +#include "qcow2-util.h" +#include "shared/machine-pool.h" + +#include "import-tar.h" + +struct TarImport { + sd_event *event; + + char *image_root; + + TarImportFinished on_finished; + void *userdata; + + char *local; + bool force_local; + bool read_only; + bool grow_machine_directory; + + char *temp_path; + char *final_path; + + int input_fd; + int tar_fd; + + ImportCompress compress; + + uint64_t written_since_last_grow; + + sd_event_source *input_event_source; + + uint8_t buffer[16*1024]; + size_t buffer_size; + + uint64_t written_compressed; + uint64_t written_uncompressed; + + struct stat st; + + pid_t tar_pid; + + unsigned last_percent; + RateLimit progress_rate_limit; +}; + +TarImport* tar_import_unref(TarImport *i) { + if (!i) + return NULL; + + sd_event_source_unref(i->input_event_source); + + if (i->tar_pid > 1) { + (void) kill_and_sigcont(i->tar_pid, SIGKILL); + (void) wait_for_terminate(i->tar_pid, NULL); + } + + if (i->temp_path) { + (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + free(i->temp_path); + } + + import_compress_free(&i->compress); + + sd_event_unref(i->event); + + safe_close(i->tar_fd); + + free(i->final_path); + free(i->image_root); + free(i->local); + free(i); + + return NULL; +} + +int tar_import_new( + TarImport **ret, + sd_event *event, + const char *image_root, + TarImportFinished on_finished, + void *userdata) { + + _cleanup_(tar_import_unrefp) TarImport *i = NULL; + int r; + + assert(ret); + + i = new0(TarImport, 1); + if (!i) + return -ENOMEM; + + i->input_fd = i->tar_fd = -1; + i->on_finished = on_finished; + i->userdata = userdata; + + RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); + i->last_percent = (unsigned) -1; + + i->image_root = strdup(image_root ?: "/var/lib/machines"); + if (!i->image_root) + return -ENOMEM; + + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + + if (event) + i->event = sd_event_ref(event); + else { + r = sd_event_default(&i->event); + if (r < 0) + return r; + } + + *ret = i; + i = NULL; + + return 0; +} + +static void tar_import_report_progress(TarImport *i) { + unsigned percent; + assert(i); + + /* We have no size information, unless the source is a regular file */ + if (!S_ISREG(i->st.st_mode)) + return; + + if (i->written_compressed >= (uint64_t) i->st.st_size) + percent = 100; + else + percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); + + if (percent == i->last_percent) + return; + + if (!ratelimit_test(&i->progress_rate_limit)) + return; + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_info("Imported %u%%.", percent); + + i->last_percent = percent; +} + +static int tar_import_finish(TarImport *i) { + int r; + + assert(i); + assert(i->tar_fd >= 0); + assert(i->temp_path); + assert(i->final_path); + + i->tar_fd = safe_close(i->tar_fd); + + if (i->tar_pid > 0) { + r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + i->tar_pid = 0; + if (r < 0) + return r; + } + + if (i->read_only) { + r = import_make_read_only(i->temp_path); + if (r < 0) + return r; + } + + if (i->force_local) + (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + + r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); + if (r < 0) + return log_error_errno(r, "Failed to move image into place: %m"); + + i->temp_path = mfree(i->temp_path); + + return 0; +} + +static int tar_import_fork_tar(TarImport *i) { + int r; + + assert(i); + + assert(!i->final_path); + assert(!i->temp_path); + assert(i->tar_fd < 0); + + i->final_path = strjoin(i->image_root, "/", i->local, NULL); + if (!i->final_path) + return log_oom(); + + r = tempfn_random(i->final_path, NULL, &i->temp_path); + if (r < 0) + return log_oom(); + + (void) mkdir_parents_label(i->temp_path, 0700); + + r = btrfs_subvol_make(i->temp_path); + if (r == -ENOTTY) { + if (mkdir(i->temp_path, 0755) < 0) + return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); + } else if (r < 0) + return log_error_errno(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) + return i->tar_fd; + + return 0; +} + +static int tar_import_write(const void *p, size_t sz, void *userdata) { + TarImport *i = userdata; + int r; + + if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { + i->written_since_last_grow = 0; + grow_machine_directory(); + } + + r = loop_write(i->tar_fd, p, sz, false); + if (r < 0) + return r; + + i->written_uncompressed += sz; + i->written_since_last_grow += sz; + + return 0; +} + +static int tar_import_process(TarImport *i) { + ssize_t l; + int r; + + assert(i); + assert(i->buffer_size < sizeof(i->buffer)); + + l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + r = log_error_errno(errno, "Failed to read input file: %m"); + goto finish; + } + if (l == 0) { + if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { + log_error("Premature end of file: %m"); + r = -EIO; + goto finish; + } + + r = tar_import_finish(i); + goto finish; + } + + i->buffer_size += l; + + if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { + r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); + if (r < 0) { + log_error("Failed to detect file compression: %m"); + goto finish; + } + if (r == 0) /* Need more data */ + return 0; + + r = tar_import_fork_tar(i); + if (r < 0) + goto finish; + } + + r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i); + if (r < 0) { + log_error_errno(r, "Failed to decode and write: %m"); + goto finish; + } + + i->written_compressed += i->buffer_size; + i->buffer_size = 0; + + tar_import_report_progress(i); + + return 0; + +finish: + if (i->on_finished) + i->on_finished(i, r, i->userdata); + else + sd_event_exit(i->event, r); + + return 0; +} + +static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + TarImport *i = userdata; + + return tar_import_process(i); +} + +static int tar_import_on_defer(sd_event_source *s, void *userdata) { + TarImport *i = userdata; + + return tar_import_process(i); +} + +int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) { + int r; + + assert(i); + assert(fd >= 0); + assert(local); + + if (!machine_name_is_valid(local)) + return -EINVAL; + + if (i->input_fd >= 0) + return -EBUSY; + + r = fd_nonblock(fd, true); + if (r < 0) + return r; + + r = free_and_strdup(&i->local, local); + if (r < 0) + return r; + i->force_local = force_local; + i->read_only = read_only; + + if (fstat(fd, &i->st) < 0) + return -errno; + + r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i); + if (r == -EPERM) { + /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ + r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); + } + if (r < 0) + return r; + + i->input_fd = fd; + return r; +} diff --git a/src/grp-machine/grp-import/systemd-import/import-tar.h b/src/grp-machine/grp-import/systemd-import/import-tar.h new file mode 100644 index 0000000000..b5012f1ea4 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/import-tar.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "shared/import-util.h" + +typedef struct TarImport TarImport; +typedef void (*TarImportFinished)(TarImport *import, int error, void *userdata); + +int tar_import_new(TarImport **import, sd_event *event, const char *image_root, TarImportFinished on_finished, void *userdata); +TarImport* tar_import_unref(TarImport *import); + +DEFINE_TRIVIAL_CLEANUP_FUNC(TarImport*, tar_import_unref); + +int tar_import_start(TarImport *import, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/grp-machine/grp-import/systemd-import/import.c b/src/grp-machine/grp-import/systemd-import/import.c new file mode 100644 index 0000000000..38fe7f8110 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-import/import.c @@ -0,0 +1,338 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/verbs.h" +#include "shared/import-util.h" +#include "shared/machine-image.h" + +#include "import-raw.h" +#include "import-tar.h" + +static bool arg_force = false; +static bool arg_read_only = false; +static const char *arg_image_root = "/var/lib/machines"; + +static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + log_notice("Transfer aborted."); + sd_event_exit(sd_event_source_get_event(s), EINTR); + return 0; +} + +static void on_tar_finished(TarImport *import, int error, void *userdata) { + sd_event *event = userdata; + assert(import); + + if (error == 0) + log_info("Operation completed successfully."); + + sd_event_exit(event, abs(error)); +} + +static int import_tar(int argc, char *argv[], void *userdata) { + _cleanup_(tar_import_unrefp) TarImport *import = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *path = NULL, *local = NULL; + _cleanup_free_ char *ll = NULL; + _cleanup_close_ int open_fd = -1; + int r, fd; + + if (argc >= 2) + path = argv[1]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (argc >= 3) + local = argv[2]; + else if (path) + local = basename(path); + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = tar_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local image name '%s' is not valid.", local); + return -EINVAL; + } + + if (!arg_force) { + r = image_find(local, NULL); + if (r < 0) + return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); + else if (r > 0) { + log_error("Image '%s' already exists.", local); + return -EEXIST; + } + } + } else + local = "imported"; + + if (path) { + open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (open_fd < 0) + return log_error_errno(errno, "Failed to open tar image to import: %m"); + + fd = open_fd; + + log_info("Importing '%s', saving as '%s'.", path, local); + } else { + _cleanup_free_ char *pretty = NULL; + + fd = STDIN_FILENO; + + (void) readlink_malloc("/proc/self/fd/0", &pretty); + log_info("Importing '%s', saving as '%s'.", strna(pretty), local); + } + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + assert_se(sigprocmask_many(SIG_BLOCK, 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 = tar_import_new(&import, event, arg_image_root, on_tar_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate importer: %m"); + + r = tar_import_start(import, fd, local, arg_force, arg_read_only); + if (r < 0) + return log_error_errno(r, "Failed to import image: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + log_info("Exiting."); + return -r; +} + +static void on_raw_finished(RawImport *import, int error, void *userdata) { + sd_event *event = userdata; + assert(import); + + if (error == 0) + log_info("Operation completed successfully."); + + sd_event_exit(event, abs(error)); +} + +static int import_raw(int argc, char *argv[], void *userdata) { + _cleanup_(raw_import_unrefp) RawImport *import = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *path = NULL, *local = NULL; + _cleanup_free_ char *ll = NULL; + _cleanup_close_ int open_fd = -1; + int r, fd; + + if (argc >= 2) + path = argv[1]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (argc >= 3) + local = argv[2]; + else if (path) + local = basename(path); + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = raw_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local image name '%s' is not valid.", local); + return -EINVAL; + } + + if (!arg_force) { + r = image_find(local, NULL); + if (r < 0) + return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); + else if (r > 0) { + log_error("Image '%s' already exists.", local); + return -EEXIST; + } + } + } else + local = "imported"; + + if (path) { + open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (open_fd < 0) + return log_error_errno(errno, "Failed to open raw image to import: %m"); + + fd = open_fd; + + log_info("Importing '%s', saving as '%s'.", path, local); + } else { + _cleanup_free_ char *pretty = NULL; + + fd = STDIN_FILENO; + + (void) readlink_malloc("/proc/self/fd/0", &pretty); + log_info("Importing '%s', saving as '%s'.", pretty, local); + } + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + assert_se(sigprocmask_many(SIG_BLOCK, 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 = raw_import_new(&import, event, arg_image_root, on_raw_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate importer: %m"); + + r = raw_import_start(import, fd, local, arg_force, arg_read_only); + if (r < 0) + return log_error_errno(r, "Failed to import image: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + log_info("Exiting."); + return -r; +} + +static int help(int argc, char *argv[], void *userdata) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Import container or virtual machine images.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --force Force creation of image\n" + " --image-root=PATH Image root directory\n" + " --read-only Create a read-only image\n\n" + "Commands:\n" + " tar FILE [NAME] Import a TAR image\n" + " raw FILE [NAME] Import a RAW image\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_FORCE, + ARG_IMAGE_ROOT, + ARG_READ_ONLY, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "force", no_argument, NULL, ARG_FORCE }, + { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, + { "read-only", no_argument, NULL, ARG_READ_ONLY }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(0, NULL, NULL); + + case ARG_VERSION: + return version(); + + case ARG_FORCE: + arg_force = true; + break; + + case ARG_IMAGE_ROOT: + arg_image_root = optarg; + break; + + case ARG_READ_ONLY: + arg_read_only = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int import_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "tar", 2, 3, 0, import_tar }, + { "raw", 2, 3, 0, import_raw }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +int main(int argc, char *argv[]) { + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + (void) ignore_signals(SIGPIPE, -1); + + r = import_main(argc, argv); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/grp-import/systemd-importd/.gitignore b/src/grp-machine/grp-import/systemd-importd/.gitignore new file mode 100644 index 0000000000..01106e2e68 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/.gitignore @@ -0,0 +1 @@ +/org.freedesktop.import1.policy diff --git a/src/grp-machine/grp-import/systemd-importd/Makefile b/src/grp-machine/grp-import/systemd-importd/Makefile new file mode 100644 index 0000000000..19705a925e --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/Makefile @@ -0,0 +1,68 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-importd +systemd_importd_SOURCES = \ + src/import/importd.c + +systemd_importd_CFLAGS = \ + -D SYSTEMD_PULL_PATH=\"$(rootlibexecdir)/systemd-pull\" \ + -D SYSTEMD_IMPORT_PATH=\"$(rootlibexecdir)/systemd-import\" \ + -D SYSTEMD_EXPORT_PATH=\"$(rootlibexecdir)/systemd-export\" + +systemd_importd_LDADD = \ + libsystemd-shared.la + +dist_rootlibexec_DATA += \ + src/import/import-pubring.gpg + +nodist_systemunit_DATA += \ + units/systemd-importd.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.import1.busname + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.import1.busname + +SYSTEM_UNIT_ALIASES += \ + systemd-importd.service dbus-org.freedesktop.import1.service + +dist_dbussystemservice_DATA += \ + src/import/org.freedesktop.import1.service + +dist_dbuspolicy_DATA += \ + src/import/org.freedesktop.import1.conf + +polkitpolicy_files += \ + src/import/org.freedesktop.import1.policy + +polkitpolicy_in_files += \ + src/import/org.freedesktop.import1.policy.in + +EXTRA_DIST += \ + units/systemd-importd.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/systemd-importd/importd.c b/src/grp-machine/grp-import/systemd-importd/importd.c new file mode 100644 index 0000000000..f032f06fcf --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/importd.c @@ -0,0 +1,1219 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/hostname-util.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-table.h" +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "basic/web-util.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" +#include "shared/import-util.h" +#include "shared/machine-pool.h" + +typedef struct Transfer Transfer; +typedef struct Manager Manager; + +typedef enum TransferType { + TRANSFER_IMPORT_TAR, + TRANSFER_IMPORT_RAW, + TRANSFER_EXPORT_TAR, + TRANSFER_EXPORT_RAW, + TRANSFER_PULL_TAR, + TRANSFER_PULL_RAW, + _TRANSFER_TYPE_MAX, + _TRANSFER_TYPE_INVALID = -1, +} TransferType; + +struct Transfer { + Manager *manager; + + uint32_t id; + char *object_path; + + TransferType type; + ImportVerify verify; + + char *remote; + char *local; + bool force_local; + bool read_only; + + char *format; + + pid_t pid; + + int log_fd; + + char log_message[LINE_MAX]; + size_t log_message_size; + + sd_event_source *pid_event_source; + sd_event_source *log_event_source; + + unsigned n_canceled; + unsigned progress_percent; + + int stdin_fd; + int stdout_fd; +}; + +struct Manager { + sd_event *event; + sd_bus *bus; + + uint32_t current_transfer_id; + Hashmap *transfers; + + Hashmap *polkit_registry; + + int notify_fd; + + sd_event_source *notify_event_source; +}; + +#define TRANSFERS_MAX 64 + +static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = { + [TRANSFER_IMPORT_TAR] = "import-tar", + [TRANSFER_IMPORT_RAW] = "import-raw", + [TRANSFER_EXPORT_TAR] = "export-tar", + [TRANSFER_EXPORT_RAW] = "export-raw", + [TRANSFER_PULL_TAR] = "pull-tar", + [TRANSFER_PULL_RAW] = "pull-raw", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType); + +static Transfer *transfer_unref(Transfer *t) { + if (!t) + return NULL; + + if (t->manager) + hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id)); + + sd_event_source_unref(t->pid_event_source); + sd_event_source_unref(t->log_event_source); + + free(t->remote); + free(t->local); + free(t->format); + free(t->object_path); + + if (t->pid > 0) { + (void) kill_and_sigcont(t->pid, SIGKILL); + (void) wait_for_terminate(t->pid, NULL); + } + + safe_close(t->log_fd); + safe_close(t->stdin_fd); + safe_close(t->stdout_fd); + + free(t); + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref); + +static int transfer_new(Manager *m, Transfer **ret) { + _cleanup_(transfer_unrefp) Transfer *t = NULL; + uint32_t id; + int r; + + assert(m); + assert(ret); + + if (hashmap_size(m->transfers) >= TRANSFERS_MAX) + return -E2BIG; + + r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops); + if (r < 0) + return r; + + t = new0(Transfer, 1); + if (!t) + return -ENOMEM; + + t->type = _TRANSFER_TYPE_INVALID; + t->log_fd = -1; + t->stdin_fd = -1; + t->stdout_fd = -1; + t->verify = _IMPORT_VERIFY_INVALID; + + id = m->current_transfer_id + 1; + + if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0) + return -ENOMEM; + + r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t); + if (r < 0) + return r; + + m->current_transfer_id = id; + + t->manager = m; + t->id = id; + + *ret = t; + t = NULL; + + return 0; +} + +static void transfer_send_log_line(Transfer *t, const char *line) { + int r, priority = LOG_INFO; + + assert(t); + assert(line); + + syslog_parse_priority(&line, &priority, true); + + log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line); + + r = sd_bus_emit_signal( + t->manager->bus, + t->object_path, + "org.freedesktop.import1.Transfer", + "LogMessage", + "us", + priority, + line); + if (r < 0) + log_error_errno(r, "Cannot emit message: %m"); + } + +static void transfer_send_logs(Transfer *t, bool flush) { + assert(t); + + /* Try to send out all log messages, if we can. But if we + * can't we remove the messages from the buffer, but don't + * fail */ + + while (t->log_message_size > 0) { + _cleanup_free_ char *n = NULL; + char *e; + + if (t->log_message_size >= sizeof(t->log_message)) + e = t->log_message + sizeof(t->log_message); + else { + char *a, *b; + + a = memchr(t->log_message, 0, t->log_message_size); + b = memchr(t->log_message, '\n', t->log_message_size); + + if (a && b) + e = a < b ? a : b; + else if (a) + e = a; + else + e = b; + } + + if (!e) { + if (!flush) + return; + + e = t->log_message + t->log_message_size; + } + + n = strndup(t->log_message, e - t->log_message); + + /* Skip over NUL and newlines */ + while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n')) + e++; + + memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e); + t->log_message_size -= e - t->log_message; + + if (!n) { + log_oom(); + continue; + } + + if (isempty(n)) + continue; + + transfer_send_log_line(t, n); + } +} + +static int transfer_finalize(Transfer *t, bool success) { + int r; + + assert(t); + + transfer_send_logs(t, true); + + r = sd_bus_emit_signal( + t->manager->bus, + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "TransferRemoved", + "uos", + t->id, + t->object_path, + success ? "done" : + t->n_canceled > 0 ? "canceled" : "failed"); + + if (r < 0) + log_error_errno(r, "Cannot emit message: %m"); + + transfer_unref(t); + return 0; +} + +static int transfer_cancel(Transfer *t) { + int r; + + assert(t); + + r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL); + if (r < 0) + return r; + + t->n_canceled++; + return 0; +} + +static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) { + Transfer *t = userdata; + bool success = false; + + assert(s); + assert(t); + + if (si->si_code == CLD_EXITED) { + if (si->si_status != 0) + log_error("Import process failed with exit code %i.", si->si_status); + else { + log_debug("Import process succeeded."); + success = true; + } + + } else if (si->si_code == CLD_KILLED || + si->si_code == CLD_DUMPED) + + log_error("Import process terminated by signal %s.", signal_to_string(si->si_status)); + else + log_error("Import process failed due to unknown reason."); + + t->pid = 0; + + return transfer_finalize(t, success); +} + +static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Transfer *t = userdata; + ssize_t l; + + assert(s); + assert(t); + + l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size); + if (l <= 0) { + /* EOF/read error. We just close the pipe here, and + * close the watch, waiting for the SIGCHLD to arrive, + * before we do anything else. */ + + if (l < 0) + log_error_errno(errno, "Failed to read log message: %m"); + + t->log_event_source = sd_event_source_unref(t->log_event_source); + return 0; + } + + t->log_message_size += l; + + transfer_send_logs(t, false); + + return 0; +} + +static int transfer_start(Transfer *t) { + _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; + int r; + + assert(t); + assert(t->pid <= 0); + + if (pipe2(pipefd, O_CLOEXEC) < 0) + return -errno; + + t->pid = fork(); + if (t->pid < 0) + return -errno; + if (t->pid == 0) { + const char *cmd[] = { + NULL, /* systemd-import, systemd-export or systemd-pull */ + NULL, /* tar, raw */ + NULL, /* --verify= */ + NULL, /* verify argument */ + NULL, /* maybe --force */ + NULL, /* maybe --read-only */ + NULL, /* if so: the actual URL */ + NULL, /* maybe --format= */ + NULL, /* if so: the actual format */ + NULL, /* remote */ + NULL, /* local */ + NULL + }; + unsigned k = 0; + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + pipefd[0] = safe_close(pipefd[0]); + + if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (t->stdout_fd >= 0) { + if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (t->stdout_fd != STDOUT_FILENO) + safe_close(t->stdout_fd); + } else { + if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + } + + if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO) + pipefd[1] = safe_close(pipefd[1]); + + if (t->stdin_fd >= 0) { + if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (t->stdin_fd != STDIN_FILENO) + safe_close(t->stdin_fd); + } else { + int null_fd; + + null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); + if (null_fd < 0) { + log_error_errno(errno, "Failed to open /dev/null: %m"); + _exit(EXIT_FAILURE); + } + + if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (null_fd != STDIN_FILENO) + safe_close(null_fd); + } + + stdio_unset_cloexec(); + + setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1); + setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1); + + if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW)) + cmd[k++] = SYSTEMD_IMPORT_PATH; + else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) + cmd[k++] = SYSTEMD_EXPORT_PATH; + else + cmd[k++] = SYSTEMD_PULL_PATH; + + if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR)) + cmd[k++] = "tar"; + else + cmd[k++] = "raw"; + + if (t->verify != _IMPORT_VERIFY_INVALID) { + cmd[k++] = "--verify"; + cmd[k++] = import_verify_to_string(t->verify); + } + + if (t->force_local) + cmd[k++] = "--force"; + if (t->read_only) + cmd[k++] = "--read-only"; + + if (t->format) { + cmd[k++] = "--format"; + cmd[k++] = t->format; + } + + if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) { + if (t->remote) + cmd[k++] = t->remote; + else + cmd[k++] = "-"; + } + + if (t->local) + cmd[k++] = t->local; + cmd[k] = NULL; + + execv(cmd[0], (char * const *) cmd); + log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]); + _exit(EXIT_FAILURE); + } + + pipefd[1] = safe_close(pipefd[1]); + t->log_fd = pipefd[0]; + pipefd[0] = -1; + + t->stdin_fd = safe_close(t->stdin_fd); + + r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t); + if (r < 0) + return r; + + r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t); + if (r < 0) + return r; + + /* Make sure always process logging before SIGCHLD */ + r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5); + if (r < 0) + return r; + + r = sd_bus_emit_signal( + t->manager->bus, + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "TransferNew", + "uo", + t->id, + t->object_path); + if (r < 0) + return r; + + return 0; +} + +static Manager *manager_unref(Manager *m) { + Transfer *t; + + if (!m) + return NULL; + + sd_event_source_unref(m->notify_event_source); + safe_close(m->notify_fd); + + while ((t = hashmap_first(m->transfers))) + transfer_unref(t); + + hashmap_free(m->transfers); + + bus_verify_polkit_async_registry_free(m->polkit_registry); + + m->bus = sd_bus_flush_close_unref(m->bus); + sd_event_unref(m->event); + + free(m); + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref); + +static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *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 ucred *ucred = NULL; + Manager *m = userdata; + struct cmsghdr *cmsg; + unsigned percent; + char *p, *e; + Transfer *t; + Iterator i; + ssize_t n; + int r; + + n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + 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))) + ucred = (struct ucred*) CMSG_DATA(cmsg); + + if (msghdr.msg_flags & MSG_TRUNC) { + log_warning("Got overly long notification datagram, ignoring."); + return 0; + } + + if (!ucred || ucred->pid <= 0) { + log_warning("Got notification datagram lacking credential information, ignoring."); + return 0; + } + + HASHMAP_FOREACH(t, m->transfers, i) + if (ucred->pid == t->pid) + break; + + if (!t) { + log_warning("Got notification datagram from unexpected peer, ignoring."); + return 0; + } + + buf[n] = 0; + + p = startswith(buf, "X_IMPORT_PROGRESS="); + if (!p) { + p = strstr(buf, "\nX_IMPORT_PROGRESS="); + if (!p) + return 0; + + p += 19; + } + + e = strchrnul(p, '\n'); + *e = 0; + + r = safe_atou(p, &percent); + if (r < 0 || percent > 100) { + log_warning("Got invalid percent value, ignoring."); + return 0; + } + + t->progress_percent = percent; + + log_debug("Got percentage from client: %u%%", percent); + return 0; +} + +static int manager_new(Manager **ret) { + _cleanup_(manager_unrefp) Manager *m = NULL; + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/import/notify", + }; + static const int one = 1; + int r; + + assert(ret); + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_set_watchdog(m->event, true); + + r = sd_bus_default_system(&m->bus); + if (r < 0) + return r; + + m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->notify_fd < 0) + return -errno; + + (void) mkdir_parents_label(sa.un.sun_path, 0755); + (void) unlink(sa.un.sun_path); + + if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return -errno; + + if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + return -errno; + + r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m); + if (r < 0) + return r; + + *ret = m; + m = NULL; + + return 0; +} + +static Transfer *manager_find(Manager *m, TransferType type, const char *remote) { + Transfer *t; + Iterator i; + + assert(m); + assert(type >= 0); + assert(type < _TRANSFER_TYPE_MAX); + + HASHMAP_FOREACH(t, m->transfers, i) { + + if (t->type == type && + streq_ptr(t->remote, remote)) + return t; + } + + return NULL; +} + +static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { + _cleanup_(transfer_unrefp) Transfer *t = NULL; + int fd, force, read_only, r; + const char *local, *object; + Manager *m = userdata; + TransferType type; + uint32_t id; + + assert(msg); + assert(m); + + r = bus_verify_polkit_async( + msg, + CAP_SYS_ADMIN, + "org.freedesktop.import1.import", + 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, "hsbb", &fd, &local, &force, &read_only); + if (r < 0) + return r; + + if (!machine_name_is_valid(local)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + + r = setup_machine_directory((uint64_t) -1, error); + if (r < 0) + return r; + + type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW; + + r = transfer_new(m, &t); + if (r < 0) + return r; + + t->type = type; + t->force_local = force; + t->read_only = read_only; + + t->local = strdup(local); + if (!t->local) + return -ENOMEM; + + t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (t->stdin_fd < 0) + return -errno; + + r = transfer_start(t); + if (r < 0) + return r; + + object = t->object_path; + id = t->id; + t = NULL; + + return sd_bus_reply_method_return(msg, "uo", id, object); +} + +static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { + _cleanup_(transfer_unrefp) Transfer *t = NULL; + int fd, r; + const char *local, *object, *format; + Manager *m = userdata; + TransferType type; + uint32_t id; + + assert(msg); + assert(m); + + r = bus_verify_polkit_async( + msg, + CAP_SYS_ADMIN, + "org.freedesktop.import1.export", + 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, "shs", &local, &fd, &format); + if (r < 0) + return r; + + if (!machine_name_is_valid(local)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); + + type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; + + r = transfer_new(m, &t); + if (r < 0) + return r; + + t->type = type; + + if (!isempty(format)) { + t->format = strdup(format); + if (!t->format) + return -ENOMEM; + } + + t->local = strdup(local); + if (!t->local) + return -ENOMEM; + + t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (t->stdout_fd < 0) + return -errno; + + r = transfer_start(t); + if (r < 0) + return r; + + object = t->object_path; + id = t->id; + t = NULL; + + return sd_bus_reply_method_return(msg, "uo", id, object); +} + +static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { + _cleanup_(transfer_unrefp) Transfer *t = NULL; + const char *remote, *local, *verify, *object; + Manager *m = userdata; + ImportVerify v; + TransferType type; + 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, "sssb", &remote, &local, &verify, &force); + if (r < 0) + return r; + + if (!http_url_is_valid(remote)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote); + + 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); + + r = setup_machine_directory((uint64_t) -1, error); + if (r < 0) + return r; + + type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW; + + if (manager_find(m, type, 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 = type; + t->verify = v; + t->force_local = force; + + t->remote = strdup(remote); + 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Transfer *t; + Iterator i; + int r; + + assert(msg); + assert(m); + + r = sd_bus_message_new_method_return(msg, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(usssdo)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(t, m->transfers, i) { + + r = sd_bus_message_append( + reply, + "(usssdo)", + t->id, + transfer_type_to_string(t->type), + t->remote, + t->local, + (double) t->progress_percent / 100.0, + t->object_path); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) { + Transfer *t = userdata; + int r; + + assert(msg); + assert(t); + + r = bus_verify_polkit_async( + msg, + CAP_SYS_ADMIN, + "org.freedesktop.import1.pull", + NULL, + false, + UID_INVALID, + &t->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = transfer_cancel(t); + if (r < 0) + return r; + + return sd_bus_reply_method_return(msg, NULL); +} + +static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Transfer *t; + uint32_t id; + int r; + + assert(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, "u", &id); + if (r < 0) + return r; + if (id <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id"); + + t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); + if (!t) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id); + + r = transfer_cancel(t); + if (r < 0) + return r; + + return sd_bus_reply_method_return(msg, NULL); +} + +static int property_get_progress( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Transfer *t = userdata; + + assert(bus); + assert(reply); + assert(t); + + return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify); + +static const sd_bus_vtable transfer_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), + SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_SIGNAL("LogMessage", "us", 0), + SD_BUS_VTABLE_END, +}; + +static const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("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), + SD_BUS_SIGNAL("TransferRemoved", "uos", 0), + SD_BUS_VTABLE_END, +}; + +static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Transfer *t; + const char *p; + uint32_t id; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + p = startswith(path, "/org/freedesktop/import1/transfer/_"); + if (!p) + return 0; + + r = safe_atou32(p, &id); + if (r < 0 || id == 0) + return 0; + + t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); + if (!t) + return 0; + + *found = t; + return 1; +} + +static int transfer_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; + Transfer *t; + unsigned k = 0; + Iterator i; + + l = new0(char*, hashmap_size(m->transfers) + 1); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(t, m->transfers, i) { + + l[k] = strdup(t->object_path); + if (!l[k]) + return -ENOMEM; + + k++; + } + + *nodes = l; + l = NULL; + + return 1; +} + +static int manager_add_bus_objects(Manager *m) { + int r; + + assert(m); + + r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, 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/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add transfer enumerator: %m"); + + r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(m->bus, m->event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + return 0; +} + +static bool manager_check_idle(void *userdata) { + Manager *m = userdata; + + return hashmap_isempty(m->transfers); +} + +static int manager_run(Manager *m) { + assert(m); + + return bus_event_loop_with_idle( + m->event, + m->bus, + "org.freedesktop.import1", + DEFAULT_EXIT_USEC, + manager_check_idle, + m); +} + +int main(int argc, char *argv[]) { + _cleanup_(manager_unrefp) Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); + + r = manager_new(&m); + if (r < 0) { + log_error_errno(r, "Failed to allocate manager object: %m"); + goto finish; + } + + r = manager_add_bus_objects(m); + if (r < 0) + goto finish; + + r = manager_run(m); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.conf b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.conf new file mode 100644 index 0000000000..ed2539a03b --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.conf @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.policy.in b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.policy.in new file mode 100644 index 0000000000..85924ed743 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.policy.in @@ -0,0 +1,49 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Import a VM or container image + <_message>Authentication is required to import a VM or container image + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Export a VM or container image + <_message>Authentication is required to export a VM or container image + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Download a VM or container image + <_message>Authentication is required to download a VM or container image + + auth_admin + auth_admin + auth_admin_keep + + + + diff --git a/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.service b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.service new file mode 100644 index 0000000000..8fc4c47881 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/org.freedesktop.import1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.import1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.import1.service diff --git a/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in new file mode 100644 index 0000000000..0f5489e7e3 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Download Service +Documentation=man:systemd-importd.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/importd + +[Service] +ExecStart=@rootlibexecdir@/systemd-importd +BusName=org.freedesktop.import1 +CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE +NoNewPrivileges=yes +WatchdogSec=3min +KillMode=mixed +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.xml b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.xml new file mode 100644 index 0000000000..8fdced475c --- /dev/null +++ b/src/grp-machine/grp-import/systemd-importd/systemd-importd.service.xml @@ -0,0 +1,82 @@ + + + + + + + + + systemd-importd.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-importd.service + 8 + + + + systemd-importd.service + systemd-importd + VM and container image import and export service + + + + systemd-importd.service + /usr/lib/systemd/systemd-importd + + + + Description + + systemd-importd is a system service that allows importing, exporting and downloading of + system images suitable for running as VM or containers. It is a companion service for + systemd-machined.service8, and provides the implementation for + machinectl1's + pull-raw, pull-tar, import-raw, + import-tar, export-raw, and export-tar commands. + + See the + + importd D-Bus API Documentation for information about the + APIs systemd-importd provides. + + + + See Also + + systemd1, + machinectl1, + systemd-machined.service8, + systemd-nspawn1 + + + + diff --git a/src/grp-machine/grp-import/systemd-pull/Makefile b/src/grp-machine/grp-import/systemd-pull/Makefile new file mode 100644 index 0000000000..a9653a5f2c --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/Makefile @@ -0,0 +1,63 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-pull +systemd_pull_SOURCES = \ + src/import/pull.c \ + src/import/pull-raw.c \ + src/import/pull-raw.h \ + src/import/pull-tar.c \ + src/import/pull-tar.h \ + src/import/pull-job.c \ + src/import/pull-job.h \ + src/import/pull-common.c \ + src/import/pull-common.h \ + src/import/import-common.c \ + src/import/import-common.h \ + src/import/import-compress.c \ + src/import/import-compress.h \ + src/import/curl-util.c \ + src/import/curl-util.h \ + src/import/qcow2-util.c \ + src/import/qcow2-util.h + +systemd_pull_CFLAGS = \ + $(LIBCURL_CFLAGS) \ + $(XZ_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(BZIP2_CFLAGS) \ + $(GCRYPT_CFLAGS) \ + -D VENDOR_KEYRING_PATH=\"$(rootlibexecdir)/import-pubring.gpg\" \ + -D USER_KEYRING_PATH=\"$(pkgsysconfdir)/import-pubring.gpg\" + +systemd_pull_LDADD = \ + libsystemd-shared.la \ + $(LIBCURL_LIBS) \ + $(XZ_LIBS) \ + $(ZLIB_LIBS) \ + $(BZIP2_LIBS) \ + $(GCRYPT_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/grp-import/systemd-pull/curl-util.c b/src/grp-machine/grp-import/systemd-pull/curl-util.c new file mode 100644 index 0000000000..9e0d56e5c3 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/curl-util.c @@ -0,0 +1,449 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/string-util.h" + +#include "curl-util.h" + +static void curl_glue_check_finished(CurlGlue *g) { + CURLMsg *msg; + int k = 0; + + assert(g); + + msg = curl_multi_info_read(g->curl, &k); + if (!msg) + return; + + if (msg->msg != CURLMSG_DONE) + return; + + if (g->on_finished) + g->on_finished(g, msg->easy_handle, msg->data.result); +} + +static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + CurlGlue *g = userdata; + int action, k = 0, translated_fd; + + assert(s); + assert(g); + + translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd))); + + if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT)) + action = CURL_POLL_INOUT; + else if (revents & EPOLLIN) + action = CURL_POLL_IN; + else if (revents & EPOLLOUT) + action = CURL_POLL_OUT; + else + action = 0; + + if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) { + log_debug("Failed to propagate IO event."); + return -EINVAL; + } + + curl_glue_check_finished(g); + return 0; +} + +static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) { + sd_event_source *io; + CurlGlue *g = userdata; + uint32_t events = 0; + int r; + + assert(curl); + assert(g); + + io = hashmap_get(g->ios, FD_TO_PTR(s)); + + if (action == CURL_POLL_REMOVE) { + if (io) { + int fd; + + fd = sd_event_source_get_io_fd(io); + assert(fd >= 0); + + sd_event_source_set_enabled(io, SD_EVENT_OFF); + sd_event_source_unref(io); + + hashmap_remove(g->ios, FD_TO_PTR(s)); + hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); + + safe_close(fd); + } + + return 0; + } + + r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops); + if (r < 0) { + log_oom(); + return -1; + } + + r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops); + if (r < 0) { + log_oom(); + return -1; + } + + if (action == CURL_POLL_IN) + events = EPOLLIN; + else if (action == CURL_POLL_OUT) + events = EPOLLOUT; + else if (action == CURL_POLL_INOUT) + events = EPOLLIN|EPOLLOUT; + + if (io) { + if (sd_event_source_set_io_events(io, events) < 0) + return -1; + + if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0) + return -1; + } else { + _cleanup_close_ int fd = -1; + + /* When curl needs to remove an fd from us it closes + * the fd first, and only then calls into us. This is + * nasty, since we cannot pass the fd on to epoll() + * anymore. Hence, duplicate the fds here, and keep a + * copy for epoll which we control after use. */ + + fd = fcntl(s, F_DUPFD_CLOEXEC, 3); + if (fd < 0) + return -1; + + if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0) + return -1; + + (void) sd_event_source_set_description(io, "curl-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, FD_TO_PTR(fd), FD_TO_PTR(s)); + if (r < 0) { + log_oom(); + hashmap_remove(g->ios, FD_TO_PTR(s)); + sd_event_source_unref(io); + return -1; + } + + fd = -1; + } + + return 0; +} + +static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) { + CurlGlue *g = userdata; + int k = 0; + + assert(s); + assert(g); + + if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) { + log_debug("Failed to propagate timeout."); + return -EINVAL; + } + + curl_glue_check_finished(g); + return 0; +} + +static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) { + CurlGlue *g = userdata; + usec_t usec; + + assert(curl); + assert(g); + + if (timeout_ms < 0) { + if (g->timer) { + if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0) + return -1; + } + + return 0; + } + + usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1; + + if (g->timer) { + if (sd_event_source_set_time(g->timer, usec) < 0) + return -1; + + if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0) + return -1; + } else { + if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) + return -1; + + (void) sd_event_source_set_description(g->timer, "curl-timer"); + } + + return 0; +} + +CurlGlue *curl_glue_unref(CurlGlue *g) { + sd_event_source *io; + + if (!g) + return NULL; + + if (g->curl) + curl_multi_cleanup(g->curl); + + while ((io = hashmap_steal_first(g->ios))) { + int fd; + + fd = sd_event_source_get_io_fd(io); + assert(fd >= 0); + + hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); + + safe_close(fd); + sd_event_source_unref(io); + } + + hashmap_free(g->ios); + + sd_event_source_unref(g->timer); + sd_event_unref(g->event); + free(g); + + return NULL; +} + +int curl_glue_new(CurlGlue **glue, sd_event *event) { + _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL; + int r; + + g = new0(CurlGlue, 1); + if (!g) + return -ENOMEM; + + if (event) + g->event = sd_event_ref(event); + else { + r = sd_event_default(&g->event); + if (r < 0) + return r; + } + + g->curl = curl_multi_init(); + if (!g->curl) + return -ENOMEM; + + if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK) + return -EINVAL; + + if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK) + return -EINVAL; + + if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK) + return -EINVAL; + + if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK) + return -EINVAL; + + *glue = g; + g = NULL; + + return 0; +} + +int curl_glue_make(CURL **ret, const char *url, void *userdata) { + const char *useragent; + CURL *c; + int r; + + assert(ret); + assert(url); + + c = curl_easy_init(); + if (!c) + return -ENOMEM; + + /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */ + + if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) { + r = -EIO; + goto fail; + } + + if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) { + r = -EIO; + goto fail; + } + + useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION); + if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) { + r = -EIO; + goto fail; + } + + if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) { + r = -EIO; + goto fail; + } + + *ret = c; + return 0; + +fail: + curl_easy_cleanup(c); + return r; +} + +int curl_glue_add(CurlGlue *g, CURL *c) { + assert(g); + assert(c); + + if (curl_multi_add_handle(g->curl, c) != CURLM_OK) + return -EIO; + + return 0; +} + +void curl_glue_remove_and_free(CurlGlue *g, CURL *c) { + assert(g); + + if (!c) + return; + + if (g->curl) + curl_multi_remove_handle(g->curl, c); + + curl_easy_cleanup(c); +} + +struct curl_slist *curl_slist_new(const char *first, ...) { + struct curl_slist *l; + va_list ap; + + if (!first) + return NULL; + + l = curl_slist_append(NULL, first); + if (!l) + return NULL; + + va_start(ap, first); + + for (;;) { + struct curl_slist *n; + const char *i; + + i = va_arg(ap, const char*); + if (!i) + break; + + n = curl_slist_append(l, i); + if (!n) { + va_end(ap); + curl_slist_free_all(l); + return NULL; + } + + l = n; + } + + va_end(ap); + return l; +} + +int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) { + const char *p = contents; + size_t l; + char *s; + + l = strlen(field); + if (sz < l) + return 0; + + if (memcmp(p, field, l) != 0) + return 0; + + p += l; + sz -= l; + + if (memchr(p, 0, sz)) + return 0; + + /* Skip over preceeding whitespace */ + while (sz > 0 && strchr(WHITESPACE, p[0])) { + p++; + sz--; + } + + /* Truncate trailing whitespace*/ + while (sz > 0 && strchr(WHITESPACE, p[sz-1])) + sz--; + + s = strndup(p, sz); + if (!s) + return -ENOMEM; + + *value = s; + return 1; +} + +int curl_parse_http_time(const char *t, usec_t *ret) { + const char *e; + locale_t loc; + struct tm tm; + time_t v; + + assert(t); + assert(ret); + + loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0); + if (loc == (locale_t) 0) + return -errno; + + /* RFC822 */ + e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc); + if (!e || *e != 0) + /* RFC 850 */ + e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc); + if (!e || *e != 0) + /* ANSI C */ + e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc); + freelocale(loc); + if (!e || *e != 0) + return -EINVAL; + + v = timegm(&tm); + if (v == (time_t) -1) + return -EINVAL; + + *ret = (usec_t) v * USEC_PER_SEC; + return 0; +} diff --git a/src/grp-machine/grp-import/systemd-pull/curl-util.h b/src/grp-machine/grp-import/systemd-pull/curl-util.h new file mode 100644 index 0000000000..296b35d09c --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/curl-util.h @@ -0,0 +1,56 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/hashmap.h" + +typedef struct CurlGlue CurlGlue; + +struct CurlGlue { + sd_event *event; + CURLM *curl; + sd_event_source *timer; + Hashmap *ios; + Hashmap *translate_fds; + + void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code); + void *userdata; +}; + +int curl_glue_new(CurlGlue **glue, sd_event *event); +CurlGlue* curl_glue_unref(CurlGlue *glue); + +DEFINE_TRIVIAL_CLEANUP_FUNC(CurlGlue*, curl_glue_unref); + +int curl_glue_make(CURL **ret, const char *url, void *userdata); +int curl_glue_add(CurlGlue *g, CURL *c); +void curl_glue_remove_and_free(CurlGlue *g, CURL *c); + +struct curl_slist *curl_slist_new(const char *first, ...) _sentinel_; +int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value); +int curl_parse_http_time(const char *t, usec_t *ret); + +DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_easy_cleanup); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct curl_slist*, curl_slist_free_all); diff --git a/src/grp-machine/grp-import/systemd-pull/pull-common.c b/src/grp-machine/grp-import/systemd-pull/pull-common.c new file mode 100644 index 0000000000..bd1623e003 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-common.c @@ -0,0 +1,548 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/capability-util.h" +#include "basic/copy.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rm-rf.h" +#include "basic/signal-util.h" +#include "basic/siphash24.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "basic/web-util.h" + +#include "pull-common.h" +#include "pull-job.h" + +#define FILENAME_ESCAPE "/.#\"\'" +#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16) + +int pull_find_old_etags( + const char *url, + const char *image_root, + int dt, + const char *prefix, + const char *suffix, + char ***etags) { + + _cleanup_free_ char *escaped_url = NULL; + _cleanup_closedir_ DIR *d = NULL; + _cleanup_strv_free_ char **l = NULL; + struct dirent *de; + int r; + + assert(url); + assert(etags); + + if (!image_root) + image_root = "/var/lib/machines"; + + escaped_url = xescape(url, FILENAME_ESCAPE); + if (!escaped_url) + return -ENOMEM; + + d = opendir(image_root); + if (!d) { + if (errno == ENOENT) { + *etags = NULL; + return 0; + } + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + const char *a, *b; + char *u; + + if (de->d_type != DT_UNKNOWN && + de->d_type != dt) + continue; + + if (prefix) { + a = startswith(de->d_name, prefix); + if (!a) + continue; + } else + a = de->d_name; + + a = startswith(a, escaped_url); + if (!a) + continue; + + a = startswith(a, "."); + if (!a) + continue; + + if (suffix) { + b = endswith(de->d_name, suffix); + if (!b) + continue; + } else + b = strchr(de->d_name, 0); + + if (a >= b) + continue; + + r = cunescape_length(a, b - a, 0, &u); + if (r < 0) + return r; + + if (!http_etag_is_valid(u)) { + free(u); + continue; + } + + r = strv_consume(&l, u); + if (r < 0) + return r; + } + + *etags = l; + l = NULL; + + return 0; +} + +int pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) { + const char *p; + int r; + + assert(final); + assert(local); + + if (!image_root) + image_root = "/var/lib/machines"; + + p = strjoina(image_root, "/", local); + + if (force_local) + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + + r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA); + if (r == -ENOTTY) { + r = copy_tree(final, p, false); + if (r < 0) + return log_error_errno(r, "Failed to copy image: %m"); + } else if (r < 0) + return log_error_errno(r, "Failed to create local image: %m"); + + log_info("Created new local image '%s'.", local); + + return 0; +} + +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, *escaped_etag = NULL; + char *path; + + assert(url); + assert(ret); + + if (!image_root) + image_root = "/var/lib/machines"; + + escaped_url = xescape(url, FILENAME_ESCAPE); + if (!escaped_url) + return -ENOMEM; + + if (etag) { + escaped_etag = xescape(etag, FILENAME_ESCAPE); + if (!escaped_etag) + return -ENOMEM; + } + + 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; +} + +int pull_make_settings_job( + PullJob **ret, + const char *url, + CurlGlue *glue, + PullJobFinished on_finished, + void *userdata) { + + _cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL; + _cleanup_(pull_job_unrefp) PullJob *job = NULL; + const char *q; + int r; + + assert(ret); + assert(url); + assert(glue); + + r = import_url_last_component(url, &last_component); + if (r < 0) + return r; + + r = tar_strip_suffixes(last_component, &ll); + if (r < 0) + return r; + + q = strjoina(ll, ".nspawn"); + + r = import_url_change_last_component(url, q, &settings_url); + if (r < 0) + return r; + + r = pull_job_new(&job, settings_url, glue, userdata); + if (r < 0) + return r; + + job->on_finished = on_finished; + job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL; + + *ret = job; + job = NULL; + + return 0; +} + +int pull_make_verification_jobs( + PullJob **ret_checksum_job, + PullJob **ret_signature_job, + ImportVerify verify, + const char *url, + CurlGlue *glue, + PullJobFinished on_finished, + void *userdata) { + + _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL; + int r; + + assert(ret_checksum_job); + assert(ret_signature_job); + assert(verify >= 0); + assert(verify < _IMPORT_VERIFY_MAX); + assert(url); + assert(glue); + + if (verify != IMPORT_VERIFY_NO) { + _cleanup_free_ char *checksum_url = NULL; + + /* Queue job for the SHA256SUMS file for the image */ + r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url); + if (r < 0) + return r; + + r = pull_job_new(&checksum_job, checksum_url, glue, userdata); + if (r < 0) + return r; + + checksum_job->on_finished = on_finished; + checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL; + } + + if (verify == IMPORT_VERIFY_SIGNATURE) { + _cleanup_free_ char *signature_url = NULL; + + /* Queue job for the SHA256SUMS.gpg file for the image. */ + r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url); + if (r < 0) + return r; + + r = pull_job_new(&signature_job, signature_url, glue, userdata); + if (r < 0) + return r; + + signature_job->on_finished = on_finished; + signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL; + } + + *ret_checksum_job = checksum_job; + *ret_signature_job = signature_job; + + checksum_job = signature_job = NULL; + + return 0; +} + +int pull_verify(PullJob *main_job, + PullJob *settings_job, + PullJob *checksum_job, + PullJob *signature_job) { + + _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; + _cleanup_free_ char *fn = NULL; + _cleanup_close_ int sig_file = -1; + const char *p, *line; + char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; + _cleanup_(sigkill_waitp) pid_t pid = 0; + bool gpg_home_created = false; + int r; + + assert(main_job); + assert(main_job->state == PULL_JOB_DONE); + + if (!checksum_job) + return 0; + + assert(main_job->calc_checksum); + assert(main_job->checksum); + assert(checksum_job->state == PULL_JOB_DONE); + + if (!checksum_job->payload || checksum_job->payload_size <= 0) { + log_error("Checksum is empty, cannot verify."); + return -EBADMSG; + } + + r = import_url_last_component(main_job->url, &fn); + if (r < 0) + return log_oom(); + + if (!filename_is_valid(fn)) { + log_error("Cannot verify checksum, could not determine valid server-side file name."); + return -EBADMSG; + } + + line = strjoina(main_job->checksum, " *", fn, "\n"); + + p = memmem(checksum_job->payload, + checksum_job->payload_size, + line, + strlen(line)); + + if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { + log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with."); + return -EBADMSG; + } + + log_info("SHA256 checksum of %s is valid.", main_job->url); + + 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) { + + _cleanup_free_ char *settings_fn = NULL; + + assert(settings_job->calc_checksum); + assert(settings_job->checksum); + + r = import_url_last_component(settings_job->url, &settings_fn); + if (r < 0) + return log_oom(); + + if (!filename_is_valid(settings_fn)) { + log_error("Cannot verify checksum, could not determine server-side settings file name."); + return -EBADMSG; + } + + line = strjoina(settings_job->checksum, " *", settings_fn, "\n"); + + p = memmem(checksum_job->payload, + checksum_job->payload_size, + line, + strlen(line)); + + if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { + log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with."); + return -EBADMSG; + } + + log_info("SHA256 checksum of %s is valid.", settings_job->url); + } + + if (!signature_job) + return 0; + + assert(signature_job->state == PULL_JOB_DONE); + + if (!signature_job->payload || signature_job->payload_size <= 0) { + log_error("Signature is empty, cannot verify."); + return -EBADMSG; + } + + r = pipe2(gpg_pipe, O_CLOEXEC); + if (r < 0) + return log_error_errno(errno, "Failed to create pipe for gpg: %m"); + + sig_file = mkostemp(sig_file_path, O_RDWR); + if (sig_file < 0) + return log_error_errno(errno, "Failed to create temporary file: %m"); + + r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false); + if (r < 0) { + log_error_errno(r, "Failed to write to temporary file: %m"); + goto finish; + } + + if (!mkdtemp(gpg_home)) { + r = log_error_errno(errno, "Failed to create tempory home for gpg: %m"); + goto finish; + } + + gpg_home_created = true; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork off gpg: %m"); + if (pid == 0) { + const char *cmd[] = { + "gpg", + "--no-options", + "--no-default-keyring", + "--no-auto-key-locate", + "--no-auto-check-trustdb", + "--batch", + "--trust-model=always", + NULL, /* --homedir= */ + NULL, /* --keyring= */ + NULL, /* --verify */ + NULL, /* signature file */ + NULL, /* dash */ + NULL /* trailing NULL */ + }; + unsigned k = ELEMENTSOF(cmd) - 6; + int null_fd; + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + gpg_pipe[1] = safe_close(gpg_pipe[1]); + + if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (gpg_pipe[0] != STDIN_FILENO) + gpg_pipe[0] = safe_close(gpg_pipe[0]); + + null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); + if (null_fd < 0) { + log_error_errno(errno, "Failed to open /dev/null: %m"); + _exit(EXIT_FAILURE); + } + + if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { + log_error_errno(errno, "Failed to dup2() fd: %m"); + _exit(EXIT_FAILURE); + } + + if (null_fd != STDOUT_FILENO) + null_fd = safe_close(null_fd); + + cmd[k++] = strjoina("--homedir=", gpg_home); + + /* We add the user keyring only to the command line + * arguments, if it's around since gpg fails + * otherwise. */ + if (access(USER_KEYRING_PATH, F_OK) >= 0) + cmd[k++] = "--keyring=" USER_KEYRING_PATH; + else + cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH; + + cmd[k++] = "--verify"; + cmd[k++] = sig_file_path; + cmd[k++] = "-"; + cmd[k++] = NULL; + + stdio_unset_cloexec(); + + execvp("gpg2", (char * const *) cmd); + execvp("gpg", (char * const *) cmd); + log_error_errno(errno, "Failed to execute gpg: %m"); + _exit(EXIT_FAILURE); + } + + gpg_pipe[0] = safe_close(gpg_pipe[0]); + + r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false); + if (r < 0) { + log_error_errno(r, "Failed to write to pipe: %m"); + goto finish; + } + + gpg_pipe[1] = safe_close(gpg_pipe[1]); + + r = wait_for_terminate_and_warn("gpg", pid, true); + pid = 0; + if (r < 0) + goto finish; + if (r > 0) { + log_error("DOWNLOAD INVALID: Signature verification failed."); + r = -EBADMSG; + } else { + log_info("Signature verification succeeded."); + r = 0; + } + +finish: + if (sig_file >= 0) + (void) unlink(sig_file_path); + + if (gpg_home_created) + (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL); + + return r; +} diff --git a/src/grp-machine/grp-import/systemd-pull/pull-common.h b/src/grp-machine/grp-import/systemd-pull/pull-common.h new file mode 100644 index 0000000000..96198a695a --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-common.h @@ -0,0 +1,37 @@ +#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 . +***/ + +#include + +#include "shared/import-util.h" + +#include "pull-job.h" + +int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local); + +int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags); + +int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); + +int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); +int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); + +int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); diff --git a/src/grp-machine/grp-import/systemd-pull/pull-job.c b/src/grp-machine/grp-import/systemd-pull/pull-job.c new file mode 100644 index 0000000000..3735304e6d --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-job.c @@ -0,0 +1,619 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/xattr-util.h" +#include "shared/machine-pool.h" + +#include "pull-job.h" + +PullJob* pull_job_unref(PullJob *j) { + if (!j) + return NULL; + + curl_glue_remove_and_free(j->glue, j->curl); + curl_slist_free_all(j->request_header); + + safe_close(j->disk_fd); + + import_compress_free(&j->compress); + + if (j->checksum_context) + gcry_md_close(j->checksum_context); + + free(j->url); + free(j->etag); + strv_free(j->old_etags); + free(j->payload); + free(j->checksum); + + free(j); + + return NULL; +} + +static void pull_job_finish(PullJob *j, int ret) { + assert(j); + + if (j->state == PULL_JOB_DONE || + j->state == PULL_JOB_FAILED) + return; + + if (ret == 0) { + j->state = PULL_JOB_DONE; + j->progress_percent = 100; + log_info("Download of %s complete.", j->url); + } else { + j->state = PULL_JOB_FAILED; + j->error = ret; + } + + if (j->on_finished) + j->on_finished(j); +} + +void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { + PullJob *j = NULL; + CURLcode code; + long status; + int r; + + if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK) + return; + + if (!j || j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) + return; + + if (result != CURLE_OK) { + log_error("Transfer failed: %s", curl_easy_strerror(result)); + r = -EIO; + goto finish; + } + + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + if (code != CURLE_OK) { + log_error("Failed to retrieve response code: %s", curl_easy_strerror(code)); + r = -EIO; + goto finish; + } else if (status == 304) { + log_info("Image already downloaded. Skipping download."); + j->etag_exists = true; + r = 0; + goto finish; + } else if (status >= 300) { + log_error("HTTP request to %s failed with code %li.", j->url, status); + r = -EIO; + goto finish; + } else if (status < 200) { + log_error("HTTP request to %s finished with unexpected code %li.", j->url, status); + r = -EIO; + goto finish; + } + + if (j->state != PULL_JOB_RUNNING) { + log_error("Premature connection termination."); + r = -EIO; + goto finish; + } + + if (j->content_length != (uint64_t) -1 && + j->content_length != j->written_compressed) { + log_error("Download truncated."); + r = -EIO; + goto finish; + } + + if (j->checksum_context) { + uint8_t *k; + + k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256); + if (!k) { + log_error("Failed to get checksum."); + r = -EIO; + goto finish; + } + + j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256)); + if (!j->checksum) { + r = log_oom(); + goto finish; + } + + log_debug("SHA256 of %s is %s.", j->url, j->checksum); + } + + if (j->disk_fd >= 0 && j->allow_sparse) { + /* Make sure the file size is right, in case the file was + * sparse and we just seeked for the last part */ + + if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) { + r = log_error_errno(errno, "Failed to truncate file: %m"); + goto finish; + } + + if (j->etag) + (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0); + if (j->url) + (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0); + + if (j->mtime != 0) { + struct timespec ut[2]; + + timespec_store(&ut[0], j->mtime); + ut[1] = ut[0]; + (void) futimens(j->disk_fd, ut); + + (void) fd_setcrtime(j->disk_fd, j->mtime); + } + } + + r = 0; + +finish: + pull_job_finish(j, r); +} + +static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) { + PullJob *j = userdata; + ssize_t n; + + assert(j); + assert(p); + + if (sz <= 0) + return 0; + + if (j->written_uncompressed + sz < j->written_uncompressed) { + log_error("File too large, overflow"); + return -EOVERFLOW; + } + + if (j->written_uncompressed + sz > j->uncompressed_max) { + log_error("File overly large, refusing"); + return -EFBIG; + } + + if (j->disk_fd >= 0) { + + if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) { + j->written_since_last_grow = 0; + grow_machine_directory(); + } + + if (j->allow_sparse) + n = sparse_write(j->disk_fd, p, sz, 64); + else + n = write(j->disk_fd, p, sz); + if (n < 0) + return log_error_errno(errno, "Failed to write file: %m"); + if ((size_t) n < sz) { + log_error("Short write"); + return -EIO; + } + } else { + + if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) + return log_oom(); + + memcpy(j->payload + j->payload_size, p, sz); + j->payload_size += sz; + } + + j->written_uncompressed += sz; + j->written_since_last_grow += sz; + + return 0; +} + +static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { + int r; + + assert(j); + assert(p); + + if (sz <= 0) + return 0; + + if (j->written_compressed + sz < j->written_compressed) { + log_error("File too large, overflow"); + return -EOVERFLOW; + } + + if (j->written_compressed + sz > j->compressed_max) { + log_error("File overly large, refusing."); + return -EFBIG; + } + + if (j->content_length != (uint64_t) -1 && + j->written_compressed + sz > j->content_length) { + log_error("Content length incorrect."); + return -EFBIG; + } + + if (j->checksum_context) + gcry_md_write(j->checksum_context, p, sz); + + r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); + if (r < 0) + return r; + + j->written_compressed += sz; + + return 0; +} + +static int pull_job_open_disk(PullJob *j) { + int r; + + assert(j); + + if (j->on_open_disk) { + r = j->on_open_disk(j); + if (r < 0) + return r; + } + + if (j->disk_fd >= 0) { + /* Check if we can do sparse files */ + + if (lseek(j->disk_fd, SEEK_SET, 0) == 0) + j->allow_sparse = true; + else { + if (errno != ESPIPE) + return log_error_errno(errno, "Failed to seek on file descriptor: %m"); + + j->allow_sparse = false; + } + } + + if (j->calc_checksum) { + if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) { + log_error("Failed to initialize hash context."); + return -EIO; + } + } + + return 0; +} + +static int pull_job_detect_compression(PullJob *j) { + _cleanup_free_ uint8_t *stub = NULL; + size_t stub_size; + + int r; + + assert(j); + + r = import_uncompress_detect(&j->compress, j->payload, j->payload_size); + if (r < 0) + return log_error_errno(r, "Failed to initialize compressor: %m"); + if (r == 0) + return 0; + + log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type)); + + r = pull_job_open_disk(j); + if (r < 0) + return r; + + /* Now, take the payload we read so far, and decompress it */ + stub = j->payload; + stub_size = j->payload_size; + + j->payload = NULL; + j->payload_size = 0; + j->payload_allocated = 0; + + j->state = PULL_JOB_RUNNING; + + r = pull_job_write_compressed(j, stub, stub_size); + if (r < 0) + return r; + + return 0; +} + +static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) { + PullJob *j = userdata; + size_t sz = size * nmemb; + int r; + + assert(contents); + assert(j); + + switch (j->state) { + + case PULL_JOB_ANALYZING: + /* Let's first check what it actually is */ + + if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) { + r = log_oom(); + goto fail; + } + + memcpy(j->payload + j->payload_size, contents, sz); + j->payload_size += sz; + + r = pull_job_detect_compression(j); + if (r < 0) + goto fail; + + break; + + case PULL_JOB_RUNNING: + + r = pull_job_write_compressed(j, contents, sz); + if (r < 0) + goto fail; + + break; + + case PULL_JOB_DONE: + case PULL_JOB_FAILED: + r = -ESTALE; + goto fail; + + default: + assert_not_reached("Impossible state."); + } + + return sz; + +fail: + pull_job_finish(j, r); + return 0; +} + +static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { + PullJob *j = userdata; + size_t sz = size * nmemb; + _cleanup_free_ char *length = NULL, *last_modified = NULL; + char *etag; + int r; + + assert(contents); + assert(j); + + if (j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) { + r = -ESTALE; + goto fail; + } + + assert(j->state == PULL_JOB_ANALYZING); + + r = curl_header_strdup(contents, sz, "ETag:", &etag); + if (r < 0) { + log_oom(); + goto fail; + } + if (r > 0) { + free(j->etag); + j->etag = etag; + + if (strv_contains(j->old_etags, j->etag)) { + log_info("Image already downloaded. Skipping download."); + j->etag_exists = true; + pull_job_finish(j, 0); + return sz; + } + + return sz; + } + + r = curl_header_strdup(contents, sz, "Content-Length:", &length); + if (r < 0) { + log_oom(); + goto fail; + } + if (r > 0) { + (void) safe_atou64(length, &j->content_length); + + if (j->content_length != (uint64_t) -1) { + char bytes[FORMAT_BYTES_MAX]; + + if (j->content_length > j->compressed_max) { + log_error("Content too large."); + r = -EFBIG; + goto fail; + } + + log_info("Downloading %s for %s.", format_bytes(bytes, sizeof(bytes), j->content_length), j->url); + } + + return sz; + } + + r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified); + if (r < 0) { + log_oom(); + goto fail; + } + if (r > 0) { + (void) curl_parse_http_time(last_modified, &j->mtime); + return sz; + } + + if (j->on_header) { + r = j->on_header(j, contents, sz); + if (r < 0) + goto fail; + } + + return sz; + +fail: + pull_job_finish(j, r); + return 0; +} + +static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + PullJob *j = userdata; + unsigned percent; + usec_t n; + + assert(j); + + if (dltotal <= 0) + return 0; + + percent = ((100 * dlnow) / dltotal); + n = now(CLOCK_MONOTONIC); + + if (n > j->last_status_usec + USEC_PER_SEC && + percent != j->progress_percent && + dlnow < dltotal) { + char buf[FORMAT_TIMESPAN_MAX]; + + if (n - j->start_usec > USEC_PER_SEC && dlnow > 0) { + char y[FORMAT_BYTES_MAX]; + usec_t left, done; + + done = n - j->start_usec; + left = (usec_t) (((double) done * (double) dltotal) / dlnow) - done; + + log_info("Got %u%% of %s. %s left at %s/s.", + percent, + j->url, + format_timespan(buf, sizeof(buf), left, USEC_PER_SEC), + format_bytes(y, sizeof(y), (uint64_t) ((double) dlnow / ((double) done / (double) USEC_PER_SEC)))); + } else + log_info("Got %u%% of %s.", percent, j->url); + + j->progress_percent = percent; + j->last_status_usec = n; + + if (j->on_progress) + j->on_progress(j); + } + + return 0; +} + +int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) { + _cleanup_(pull_job_unrefp) PullJob *j = NULL; + + assert(url); + assert(glue); + assert(ret); + + j = new0(PullJob, 1); + if (!j) + return -ENOMEM; + + j->state = PULL_JOB_INIT; + j->disk_fd = -1; + j->userdata = userdata; + j->glue = glue; + j->content_length = (uint64_t) -1; + j->start_usec = now(CLOCK_MONOTONIC); + j->compressed_max = j->uncompressed_max = 8LLU * 1024LLU * 1024LLU * 1024LLU; /* 8GB */ + + j->url = strdup(url); + if (!j->url) + return -ENOMEM; + + *ret = j; + j = NULL; + + return 0; +} + +int pull_job_begin(PullJob *j) { + int r; + + assert(j); + + if (j->state != PULL_JOB_INIT) + return -EBUSY; + + if (j->grow_machine_directory) + grow_machine_directory(); + + r = curl_glue_make(&j->curl, j->url, j); + if (r < 0) + return r; + + if (!strv_isempty(j->old_etags)) { + _cleanup_free_ char *cc = NULL, *hdr = NULL; + + cc = strv_join(j->old_etags, ", "); + if (!cc) + return -ENOMEM; + + hdr = strappend("If-None-Match: ", cc); + if (!hdr) + return -ENOMEM; + + if (!j->request_header) { + j->request_header = curl_slist_new(hdr, NULL); + if (!j->request_header) + return -ENOMEM; + } else { + struct curl_slist *l; + + l = curl_slist_append(j->request_header, hdr); + if (!l) + return -ENOMEM; + + j->request_header = l; + } + } + + if (j->request_header) { + if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK) + return -EIO; + } + + if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK) + return -EIO; + + if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK) + return -EIO; + + r = curl_glue_add(j->glue, j->curl); + if (r < 0) + return r; + + j->state = PULL_JOB_ANALYZING; + + return 0; +} diff --git a/src/grp-machine/grp-import/systemd-pull/pull-job.h b/src/grp-machine/grp-import/systemd-pull/pull-job.h new file mode 100644 index 0000000000..296e32bd09 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-job.h @@ -0,0 +1,106 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "import-compress.h" + +#include "curl-util.h" + +typedef struct PullJob PullJob; +typedef void (*PullJobFinished)(PullJob *job); +typedef int (*PullJobOpenDisk)(PullJob *job); +typedef int (*PullJobHeader)(PullJob *job, const char *header, size_t sz); +typedef void (*PullJobProgress)(PullJob *job); + +typedef enum PullJobState { + PULL_JOB_INIT, + PULL_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */ + PULL_JOB_RUNNING, /* Writing to destination */ + PULL_JOB_DONE, + PULL_JOB_FAILED, + _PULL_JOB_STATE_MAX, + _PULL_JOB_STATE_INVALID = -1, +} PullJobState; + +#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) + +struct PullJob { + PullJobState state; + int error; + + char *url; + + void *userdata; + PullJobFinished on_finished; + PullJobOpenDisk on_open_disk; + PullJobHeader on_header; + PullJobProgress on_progress; + + CurlGlue *glue; + CURL *curl; + struct curl_slist *request_header; + + char *etag; + char **old_etags; + bool etag_exists; + + uint64_t content_length; + uint64_t written_compressed; + uint64_t written_uncompressed; + + uint64_t uncompressed_max; + uint64_t compressed_max; + + uint8_t *payload; + size_t payload_size; + size_t payload_allocated; + + int disk_fd; + + usec_t mtime; + + ImportCompress compress; + + unsigned progress_percent; + usec_t start_usec; + usec_t last_status_usec; + + bool allow_sparse; + + bool calc_checksum; + gcry_md_hd_t checksum_context; + + char *checksum; + + bool grow_machine_directory; + uint64_t written_since_last_grow; +}; + +int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata); +PullJob* pull_job_unref(PullJob *job); + +int pull_job_begin(PullJob *j); + +void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result); + +DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref); diff --git a/src/grp-machine/grp-import/systemd-pull/pull-raw.c b/src/grp-machine/grp-import/systemd-pull/pull-raw.c new file mode 100644 index 0000000000..9602915a1f --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-raw.c @@ -0,0 +1,653 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/chattr-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "basic/web-util.h" +#include "import-common.h" +#include "qcow2-util.h" +#include "shared/import-util.h" + +#include "curl-util.h" +#include "pull-common.h" +#include "pull-job.h" +#include "pull-raw.h" + +typedef enum RawProgress { + RAW_DOWNLOADING, + RAW_VERIFYING, + RAW_UNPACKING, + RAW_FINALIZING, + RAW_COPYING, +} RawProgress; + +struct RawPull { + sd_event *event; + CurlGlue *glue; + + char *image_root; + + PullJob *raw_job; + PullJob *settings_job; + PullJob *checksum_job; + PullJob *signature_job; + + RawPullFinished on_finished; + void *userdata; + + char *local; + bool force_local; + bool grow_machine_directory; + bool settings; + + char *final_path; + char *temp_path; + + char *settings_path; + char *settings_temp_path; + + ImportVerify verify; +}; + +RawPull* raw_pull_unref(RawPull *i) { + if (!i) + return NULL; + + pull_job_unref(i->raw_job); + pull_job_unref(i->settings_job); + pull_job_unref(i->checksum_job); + pull_job_unref(i->signature_job); + + curl_glue_unref(i->glue); + sd_event_unref(i->event); + + if (i->temp_path) { + (void) unlink(i->temp_path); + free(i->temp_path); + } + + if (i->settings_temp_path) { + (void) unlink(i->settings_temp_path); + free(i->settings_temp_path); + } + + free(i->final_path); + free(i->settings_path); + free(i->image_root); + free(i->local); + free(i); + + return NULL; +} + +int raw_pull_new( + RawPull **ret, + sd_event *event, + const char *image_root, + RawPullFinished on_finished, + void *userdata) { + + _cleanup_(raw_pull_unrefp) RawPull *i = NULL; + int r; + + assert(ret); + + i = new0(RawPull, 1); + if (!i) + return -ENOMEM; + + i->on_finished = on_finished; + i->userdata = userdata; + + i->image_root = strdup(image_root ?: "/var/lib/machines"); + if (!i->image_root) + return -ENOMEM; + + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + + if (event) + i->event = sd_event_ref(event); + else { + r = sd_event_default(&i->event); + if (r < 0) + return r; + } + + r = curl_glue_new(&i->glue, i->event); + if (r < 0) + return r; + + i->glue->on_finished = pull_job_curl_on_finished; + i->glue->userdata = i; + + *ret = i; + i = NULL; + + return 0; +} + +static void raw_pull_report_progress(RawPull *i, RawProgress p) { + unsigned percent; + + assert(i); + + switch (p) { + + case RAW_DOWNLOADING: { + unsigned remain = 80; + + percent = 0; + + if (i->settings_job) { + percent += i->settings_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->checksum_job) { + percent += i->checksum_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->signature_job) { + percent += i->signature_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->raw_job) + percent += i->raw_job->progress_percent * remain / 100; + break; + } + + case RAW_VERIFYING: + percent = 80; + break; + + case RAW_UNPACKING: + percent = 85; + break; + + case RAW_FINALIZING: + percent = 90; + break; + + case RAW_COPYING: + percent = 95; + break; + + default: + assert_not_reached("Unknown progress state"); + } + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_debug("Combined progress %u%%", percent); +} + +static int raw_pull_maybe_convert_qcow2(RawPull *i) { + _cleanup_close_ int converted_fd = -1; + _cleanup_free_ char *t = NULL; + int r; + + assert(i); + assert(i->raw_job); + + r = qcow2_detect(i->raw_job->disk_fd); + if (r < 0) + return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); + if (r == 0) + return 0; + + /* This is a QCOW2 image, let's convert it */ + r = tempfn_random(i->final_path, NULL, &t); + if (r < 0) + return log_oom(); + + converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (converted_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", t); + + r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_warning_errno(r, "Failed to set file attributes on %s: %m", t); + + log_info("Unpacking QCOW2 file."); + + r = qcow2_convert(i->raw_job->disk_fd, converted_fd); + if (r < 0) { + unlink(t); + return log_error_errno(r, "Failed to convert qcow2 image: %m"); + } + + (void) unlink(i->temp_path); + free(i->temp_path); + i->temp_path = t; + t = NULL; + + safe_close(i->raw_job->disk_fd); + i->raw_job->disk_fd = converted_fd; + converted_fd = -1; + + return 1; +} + +static int raw_pull_make_local_copy(RawPull *i) { + _cleanup_free_ char *tp = NULL; + _cleanup_close_ int dfd = -1; + const char *p; + int r; + + assert(i); + assert(i->raw_job); + + if (!i->local) + return 0; + + if (!i->final_path) { + r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path); + if (r < 0) + return log_oom(); + } + + if (i->raw_job->etag_exists) { + /* We have downloaded this one previously, reopen it */ + + assert(i->raw_job->disk_fd < 0); + + i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (i->raw_job->disk_fd < 0) + return log_error_errno(errno, "Failed to open vendor image: %m"); + } else { + /* We freshly downloaded the image, use it */ + + assert(i->raw_job->disk_fd >= 0); + + if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1) + return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m"); + } + + p = strjoina(i->image_root, "/", i->local, ".raw"); + + if (i->force_local) + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + + r = tempfn_random(p, NULL, &tp); + if (r < 0) + return log_oom(); + + dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (dfd < 0) + return log_error_errno(errno, "Failed to create writable copy of image: %m"); + + /* Turn off COW writing. This should greatly improve + * performance on COW file systems like btrfs, since it + * reduces fragmentation caused by not allowing in-place + * writes. */ + r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_warning_errno(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) { + unlink(tp); + return log_error_errno(r, "Failed to make writable copy of image: %m"); + } + + (void) copy_times(i->raw_job->disk_fd, dfd); + (void) copy_xattr(i->raw_job->disk_fd, dfd); + + dfd = safe_close(dfd); + + r = rename(tp, p); + if (r < 0) { + r = log_error_errno(errno, "Failed to move writable image into place: %m"); + unlink(tp); + return r; + } + + log_info("Created new local image '%s'.", i->local); + + if (i->settings) { + const char *local_settings; + assert(i->settings_job); + + if (!i->settings_path) { + r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); + if (r < 0) + return log_oom(); + } + + local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); + + r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0); + if (r == -EEXIST) + log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); + else if (r == -ENOENT) + log_debug_errno(r, "Skipping creation of settings file, since none was found."); + else if (r < 0) + log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); + else + log_info("Created new settings file %s.", local_settings); + } + + return 0; +} + +static bool raw_pull_is_done(RawPull *i) { + assert(i); + assert(i->raw_job); + + if (!PULL_JOB_IS_COMPLETE(i->raw_job)) + return false; + if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) + return false; + if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) + return false; + if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) + return false; + + return true; +} + +static void raw_pull_job_on_finished(PullJob *j) { + RawPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + if (j == i->settings_job) { + if (j->error != 0) + log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); + } else if (j->error != 0) { + if (j == i->checksum_job) + log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); + else if (j == i->signature_job) + log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); + else + log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); + + r = j->error; + goto finish; + } + + /* This is invoked if either the download completed + * successfully, or the download was skipped because we + * already have the etag. In this case ->etag_exists is + * true. + * + * We only do something when we got all three files */ + + if (!raw_pull_is_done(i)) + return; + + if (i->settings_job) + i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); + + if (!i->raw_job->etag_exists) { + /* This is a new download, verify it, and move it into place */ + assert(i->raw_job->disk_fd >= 0); + + raw_pull_report_progress(i, RAW_VERIFYING); + + r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job); + if (r < 0) + goto finish; + + raw_pull_report_progress(i, RAW_UNPACKING); + + r = raw_pull_maybe_convert_qcow2(i); + if (r < 0) + goto finish; + + raw_pull_report_progress(i, RAW_FINALIZING); + + r = import_make_read_only_fd(i->raw_job->disk_fd); + if (r < 0) + goto finish; + + r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); + if (r < 0) { + log_error_errno(r, "Failed to move RAW file into place: %m"); + goto finish; + } + + i->temp_path = mfree(i->temp_path); + + if (i->settings_job && + i->settings_job->error == 0 && + !i->settings_job->etag_exists) { + + assert(i->settings_temp_path); + assert(i->settings_path); + + r = import_make_read_only(i->settings_temp_path); + if (r < 0) + goto finish; + + r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); + if (r < 0) { + log_error_errno(r, "Failed to rename settings file: %m"); + goto finish; + } + + i->settings_temp_path = mfree(i->settings_temp_path); + } + } + + raw_pull_report_progress(i, RAW_COPYING); + + r = raw_pull_make_local_copy(i); + if (r < 0) + goto finish; + + r = 0; + +finish: + if (i->on_finished) + i->on_finished(i, r, i->userdata); + else + sd_event_exit(i->event, r); +} + +static int raw_pull_job_on_open_disk_raw(PullJob *j) { + RawPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->raw_job == j); + assert(!i->final_path); + assert(!i->temp_path); + + r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path); + if (r < 0) + return log_oom(); + + r = tempfn_random(i->final_path, NULL, &i->temp_path); + if (r < 0) + return log_oom(); + + (void) mkdir_parents_label(i->temp_path, 0700); + + j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (j->disk_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", i->temp_path); + + r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL); + if (r < 0) + log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); + + return 0; +} + +static int raw_pull_job_on_open_disk_settings(PullJob *j) { + RawPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->settings_job == j); + assert(!i->settings_path); + assert(!i->settings_temp_path); + + r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); + if (r < 0) + return log_oom(); + + r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); + if (r < 0) + return log_oom(); + + mkdir_parents_label(i->settings_temp_path, 0700); + + j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (j->disk_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); + + return 0; +} + +static void raw_pull_job_on_progress(PullJob *j) { + RawPull *i; + + assert(j); + assert(j->userdata); + + i = j->userdata; + + raw_pull_report_progress(i, RAW_DOWNLOADING); +} + +int raw_pull_start( + RawPull *i, + const char *url, + const char *local, + bool force_local, + ImportVerify verify, + bool settings) { + + int r; + + assert(i); + assert(verify < _IMPORT_VERIFY_MAX); + assert(verify >= 0); + + if (!http_url_is_valid(url)) + return -EINVAL; + + if (local && !machine_name_is_valid(local)) + return -EINVAL; + + if (i->raw_job) + return -EBUSY; + + r = free_and_strdup(&i->local, local); + if (r < 0) + return r; + + i->force_local = force_local; + i->verify = verify; + i->settings = settings; + + /* Queue job for the image itself */ + r = pull_job_new(&i->raw_job, url, i->glue, i); + if (r < 0) + return r; + + i->raw_job->on_finished = raw_pull_job_on_finished; + i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; + i->raw_job->on_progress = raw_pull_job_on_progress; + i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->raw_job->grow_machine_directory = i->grow_machine_directory; + + r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags); + if (r < 0) + return r; + + if (settings) { + r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; + i->settings_job->on_progress = raw_pull_job_on_progress; + i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; + + r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); + if (r < 0) + return r; + } + + r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + r = pull_job_begin(i->raw_job); + if (r < 0) + return r; + + if (i->settings_job) { + r = pull_job_begin(i->settings_job); + if (r < 0) + return r; + } + + if (i->checksum_job) { + i->checksum_job->on_progress = raw_pull_job_on_progress; + + r = pull_job_begin(i->checksum_job); + if (r < 0) + return r; + } + + if (i->signature_job) { + i->signature_job->on_progress = raw_pull_job_on_progress; + + r = pull_job_begin(i->signature_job); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/grp-machine/grp-import/systemd-pull/pull-raw.h b/src/grp-machine/grp-import/systemd-pull/pull-raw.h new file mode 100644 index 0000000000..93032edf09 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-raw.h @@ -0,0 +1,35 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/macro.h" +#include "shared/import-util.h" + +typedef struct RawPull RawPull; +typedef void (*RawPullFinished)(RawPull *pull, int error, void *userdata); + +int raw_pull_new(RawPull **pull, sd_event *event, const char *image_root, RawPullFinished on_finished, void *userdata); +RawPull* raw_pull_unref(RawPull *pull); + +DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); + +int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/grp-machine/grp-import/systemd-pull/pull-tar.c b/src/grp-machine/grp-import/systemd-pull/pull-tar.c new file mode 100644 index 0000000000..121ca31dbd --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-tar.c @@ -0,0 +1,564 @@ +/*** + 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "basic/web-util.h" +#include "import-common.h" +#include "shared/import-util.h" + +#include "curl-util.h" +#include "pull-common.h" +#include "pull-job.h" +#include "pull-tar.h" + +typedef enum TarProgress { + TAR_DOWNLOADING, + TAR_VERIFYING, + TAR_FINALIZING, + TAR_COPYING, +} TarProgress; + +struct TarPull { + sd_event *event; + CurlGlue *glue; + + char *image_root; + + PullJob *tar_job; + PullJob *settings_job; + PullJob *checksum_job; + PullJob *signature_job; + + TarPullFinished on_finished; + void *userdata; + + char *local; + bool force_local; + bool grow_machine_directory; + bool settings; + + pid_t tar_pid; + + char *final_path; + char *temp_path; + + char *settings_path; + char *settings_temp_path; + + ImportVerify verify; +}; + +TarPull* tar_pull_unref(TarPull *i) { + if (!i) + return NULL; + + if (i->tar_pid > 1) { + (void) kill_and_sigcont(i->tar_pid, SIGKILL); + (void) wait_for_terminate(i->tar_pid, NULL); + } + + pull_job_unref(i->tar_job); + pull_job_unref(i->settings_job); + pull_job_unref(i->checksum_job); + pull_job_unref(i->signature_job); + + curl_glue_unref(i->glue); + sd_event_unref(i->event); + + if (i->temp_path) { + (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + free(i->temp_path); + } + + if (i->settings_temp_path) { + (void) unlink(i->settings_temp_path); + free(i->settings_temp_path); + } + + free(i->final_path); + free(i->settings_path); + free(i->image_root); + free(i->local); + free(i); + + return NULL; +} + +int tar_pull_new( + TarPull **ret, + sd_event *event, + const char *image_root, + TarPullFinished on_finished, + void *userdata) { + + _cleanup_(tar_pull_unrefp) TarPull *i = NULL; + int r; + + assert(ret); + + i = new0(TarPull, 1); + if (!i) + return -ENOMEM; + + i->on_finished = on_finished; + i->userdata = userdata; + + i->image_root = strdup(image_root ?: "/var/lib/machines"); + if (!i->image_root) + return -ENOMEM; + + i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); + + if (event) + i->event = sd_event_ref(event); + else { + r = sd_event_default(&i->event); + if (r < 0) + return r; + } + + r = curl_glue_new(&i->glue, i->event); + if (r < 0) + return r; + + i->glue->on_finished = pull_job_curl_on_finished; + i->glue->userdata = i; + + *ret = i; + i = NULL; + + return 0; +} + +static void tar_pull_report_progress(TarPull *i, TarProgress p) { + unsigned percent; + + assert(i); + + switch (p) { + + case TAR_DOWNLOADING: { + unsigned remain = 85; + + percent = 0; + + if (i->settings_job) { + percent += i->settings_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->checksum_job) { + percent += i->checksum_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->signature_job) { + percent += i->signature_job->progress_percent * 5 / 100; + remain -= 5; + } + + if (i->tar_job) + percent += i->tar_job->progress_percent * remain / 100; + break; + } + + case TAR_VERIFYING: + percent = 85; + break; + + case TAR_FINALIZING: + percent = 90; + break; + + case TAR_COPYING: + percent = 95; + break; + + default: + assert_not_reached("Unknown progress state"); + } + + sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); + log_debug("Combined progress %u%%", percent); +} + +static int tar_pull_make_local_copy(TarPull *i) { + int r; + + assert(i); + assert(i->tar_job); + + if (!i->local) + return 0; + + if (!i->final_path) { + r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path); + if (r < 0) + return log_oom(); + } + + r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); + if (r < 0) + return r; + + if (i->settings) { + const char *local_settings; + assert(i->settings_job); + + if (!i->settings_path) { + r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); + if (r < 0) + return log_oom(); + } + + local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); + + r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0); + if (r == -EEXIST) + log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); + else if (r == -ENOENT) + log_debug_errno(r, "Skipping creation of settings file, since none was found."); + else if (r < 0) + log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); + else + log_info("Created new settings file %s.", local_settings); + } + + return 0; +} + +static bool tar_pull_is_done(TarPull *i) { + assert(i); + assert(i->tar_job); + + if (!PULL_JOB_IS_COMPLETE(i->tar_job)) + return false; + if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) + return false; + if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) + return false; + if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) + return false; + + return true; +} + +static void tar_pull_job_on_finished(PullJob *j) { + TarPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + + if (j == i->settings_job) { + if (j->error != 0) + log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); + } else if (j->error != 0) { + if (j == i->checksum_job) + log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); + else if (j == i->signature_job) + log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); + else + log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); + + r = j->error; + goto finish; + } + + /* This is invoked if either the download completed + * successfully, or the download was skipped because we + * already have the etag. */ + + if (!tar_pull_is_done(i)) + return; + + i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd); + if (i->settings_job) + i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); + + if (i->tar_pid > 0) { + r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + i->tar_pid = 0; + if (r < 0) + goto finish; + if (r > 0) { + r = -EIO; + goto finish; + } + } + + if (!i->tar_job->etag_exists) { + /* This is a new download, verify it, and move it into place */ + + tar_pull_report_progress(i, TAR_VERIFYING); + + r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job); + if (r < 0) + goto finish; + + tar_pull_report_progress(i, TAR_FINALIZING); + + r = import_make_read_only(i->temp_path); + if (r < 0) + goto finish; + + r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); + if (r < 0) { + log_error_errno(r, "Failed to rename to final image name: %m"); + goto finish; + } + + i->temp_path = mfree(i->temp_path); + + if (i->settings_job && + i->settings_job->error == 0 && + !i->settings_job->etag_exists) { + + assert(i->settings_temp_path); + assert(i->settings_path); + + /* Also move the settings file into place, if + * it exist. Note that we do so only if we + * also moved the tar file in place, to keep + * things strictly in sync. */ + + r = import_make_read_only(i->settings_temp_path); + if (r < 0) + goto finish; + + r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); + if (r < 0) { + log_error_errno(r, "Failed to rename settings file: %m"); + goto finish; + } + + i->settings_temp_path = mfree(i->settings_temp_path); + } + } + + tar_pull_report_progress(i, TAR_COPYING); + + r = tar_pull_make_local_copy(i); + if (r < 0) + goto finish; + + r = 0; + +finish: + if (i->on_finished) + i->on_finished(i, r, i->userdata); + else + sd_event_exit(i->event, r); +} + +static int tar_pull_job_on_open_disk_tar(PullJob *j) { + TarPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->tar_job == j); + assert(!i->final_path); + assert(!i->temp_path); + assert(i->tar_pid <= 0); + + r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); + if (r < 0) + return log_oom(); + + r = tempfn_random(i->final_path, NULL, &i->temp_path); + if (r < 0) + return log_oom(); + + mkdir_parents_label(i->temp_path, 0700); + + r = btrfs_subvol_make(i->temp_path); + if (r == -ENOTTY) { + if (mkdir(i->temp_path, 0755) < 0) + return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); + } else if (r < 0) + return log_error_errno(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) + return j->disk_fd; + + return 0; +} + +static int tar_pull_job_on_open_disk_settings(PullJob *j) { + TarPull *i; + int r; + + assert(j); + assert(j->userdata); + + i = j->userdata; + assert(i->settings_job == j); + assert(!i->settings_path); + assert(!i->settings_temp_path); + + r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); + if (r < 0) + return log_oom(); + + r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); + if (r < 0) + return log_oom(); + + mkdir_parents_label(i->settings_temp_path, 0700); + + j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (j->disk_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); + + return 0; +} + +static void tar_pull_job_on_progress(PullJob *j) { + TarPull *i; + + assert(j); + assert(j->userdata); + + i = j->userdata; + + tar_pull_report_progress(i, TAR_DOWNLOADING); +} + +int tar_pull_start( + TarPull *i, + const char *url, + const char *local, + bool force_local, + ImportVerify verify, + bool settings) { + + int r; + + assert(i); + assert(verify < _IMPORT_VERIFY_MAX); + assert(verify >= 0); + + if (!http_url_is_valid(url)) + return -EINVAL; + + if (local && !machine_name_is_valid(local)) + return -EINVAL; + + if (i->tar_job) + return -EBUSY; + + r = free_and_strdup(&i->local, local); + if (r < 0) + return r; + + i->force_local = force_local; + i->verify = verify; + i->settings = settings; + + /* Set up download job for TAR file */ + r = pull_job_new(&i->tar_job, url, i->glue, i); + if (r < 0) + return r; + + i->tar_job->on_finished = tar_pull_job_on_finished; + i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; + i->tar_job->on_progress = tar_pull_job_on_progress; + i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; + i->tar_job->grow_machine_directory = i->grow_machine_directory; + + r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); + if (r < 0) + return r; + + /* Set up download job for the settings file (.nspawn) */ + if (settings) { + r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i); + if (r < 0) + return r; + + i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings; + i->settings_job->on_progress = tar_pull_job_on_progress; + i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; + + r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); + if (r < 0) + return r; + } + + /* Set up download of checksum/signature files */ + r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); + if (r < 0) + return r; + + r = pull_job_begin(i->tar_job); + if (r < 0) + return r; + + if (i->settings_job) { + r = pull_job_begin(i->settings_job); + if (r < 0) + return r; + } + + if (i->checksum_job) { + i->checksum_job->on_progress = tar_pull_job_on_progress; + + r = pull_job_begin(i->checksum_job); + if (r < 0) + return r; + } + + if (i->signature_job) { + i->signature_job->on_progress = tar_pull_job_on_progress; + + r = pull_job_begin(i->signature_job); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/grp-machine/grp-import/systemd-pull/pull-tar.h b/src/grp-machine/grp-import/systemd-pull/pull-tar.h new file mode 100644 index 0000000000..81797fa359 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull-tar.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" +#include "shared/import-util.h" + +typedef struct TarPull TarPull; +typedef void (*TarPullFinished)(TarPull *pull, int error, void *userdata); + +int tar_pull_new(TarPull **pull, sd_event *event, const char *image_root, TarPullFinished on_finished, void *userdata); +TarPull* tar_pull_unref(TarPull *pull); + +DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); + +int tar_pull_start(TarPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/grp-machine/grp-import/systemd-pull/pull.c b/src/grp-machine/grp-import/systemd-pull/pull.c new file mode 100644 index 0000000000..4153c938d8 --- /dev/null +++ b/src/grp-machine/grp-import/systemd-pull/pull.c @@ -0,0 +1,338 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/parse-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/verbs.h" +#include "basic/web-util.h" +#include "shared/import-util.h" +#include "shared/machine-image.h" + +#include "pull-raw.h" +#include "pull-tar.h" + +static bool arg_force = false; +static const char *arg_image_root = "/var/lib/machines"; +static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; +static bool arg_settings = true; + +static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + log_notice("Transfer aborted."); + sd_event_exit(sd_event_source_get_event(s), EINTR); + return 0; +} + +static void on_tar_finished(TarPull *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_tar(int argc, char *argv[], void *userdata) { + _cleanup_(tar_pull_unrefp) TarPull *pull = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *url, *local; + _cleanup_free_ char *l = NULL, *ll = NULL; + int r; + + url = argv[1]; + if (!http_url_is_valid(url)) { + log_error("URL '%s' is not valid.", url); + return -EINVAL; + } + + if (argc >= 3) + local = argv[2]; + else { + r = import_url_last_component(url, &l); + if (r < 0) + return log_error_errno(r, "Failed get final component of URL: %m"); + + local = l; + } + + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = tar_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local image name '%s' is not valid.", local); + return -EINVAL; + } + + if (!arg_force) { + r = image_find(local, NULL); + if (r < 0) + return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); + else if (r > 0) { + log_error("Image '%s' already exists.", local); + return -EEXIST; + } + } + + log_info("Pulling '%s', saving as '%s'.", url, local); + } else + log_info("Pulling '%s'.", url); + + 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 = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate puller: %m"); + + r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); + 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 void on_raw_finished(RawPull *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_raw(int argc, char *argv[], void *userdata) { + _cleanup_(raw_pull_unrefp) RawPull *pull = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *url, *local; + _cleanup_free_ char *l = NULL, *ll = NULL; + int r; + + url = argv[1]; + if (!http_url_is_valid(url)) { + log_error("URL '%s' is not valid.", url); + return -EINVAL; + } + + if (argc >= 3) + local = argv[2]; + else { + r = import_url_last_component(url, &l); + if (r < 0) + return log_error_errno(r, "Failed get final component of URL: %m"); + + local = l; + } + + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = raw_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local image name '%s' is not valid.", local); + return -EINVAL; + } + + if (!arg_force) { + r = image_find(local, NULL); + if (r < 0) + return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); + else if (r > 0) { + log_error("Image '%s' already exists.", local); + return -EEXIST; + } + } + + log_info("Pulling '%s', saving as '%s'.", url, local); + } else + log_info("Pulling '%s'.", url); + + 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 = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event); + if (r < 0) + return log_error_errno(r, "Failed to allocate puller: %m"); + + r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); + 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" + "Download container or virtual machine images.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --force Force creation of image\n" + " --verify=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\n" + "Commands:\n" + " tar URL [NAME] Download a TAR image\n" + " raw URL [NAME] Download a RAW image\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_FORCE, + ARG_IMAGE_ROOT, + ARG_VERIFY, + ARG_SETTINGS, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "force", no_argument, NULL, ARG_FORCE }, + { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, + { "verify", required_argument, NULL, ARG_VERIFY }, + { "settings", required_argument, NULL, ARG_SETTINGS }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(0, NULL, NULL); + + case ARG_VERSION: + return version(); + + case ARG_FORCE: + arg_force = true; + break; + + case ARG_IMAGE_ROOT: + arg_image_root = optarg; + break; + + case ARG_VERIFY: + arg_verify = import_verify_from_string(optarg); + if (arg_verify < 0) { + log_error("Invalid verification setting '%s'", optarg); + return -EINVAL; + } + + break; + + case ARG_SETTINGS: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg); + + arg_settings = r; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int pull_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "tar", 2, 3, 0, pull_tar }, + { "raw", 2, 3, 0, pull_raw }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +int main(int argc, char *argv[]) { + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + (void) ignore_signals(SIGPIPE, -1); + + r = pull_main(argc, argv); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/machinectl/Makefile b/src/grp-machine/machinectl/Makefile new file mode 100644 index 0000000000..f6760f3174 --- /dev/null +++ b/src/grp-machine/machinectl/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +machinectl_SOURCES = \ + src/machine/machinectl.c + +machinectl_LDADD = \ + libsystemd-shared.la + +rootbin_PROGRAMS += \ + machinectl + +dist_bashcompletion_data += \ + shell-completion/bash/machinectl + +dist_zshcompletion_data += \ + shell-completion/zsh/_machinectl \ + shell-completion/zsh/_sd_machines + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/machinectl/machinectl.c b/src/grp-machine/machinectl/machinectl.c new file mode 100644 index 0000000000..d5304dba37 --- /dev/null +++ b/src/grp-machine/machinectl/machinectl.c @@ -0,0 +1,2875 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/copy.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/verbs.h" +#include "basic/web-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" +#include "shared/cgroup-show.h" +#include "shared/import-util.h" +#include "shared/logs-show.h" +#include "shared/pager.h" +#include "shared/ptyfwd.h" +#include "shared/spawn-polkit-agent.h" + +static char **arg_property = NULL; +static bool arg_all = false; +static bool arg_value = false; +static bool arg_full = false; +static bool arg_no_pager = false; +static bool arg_legend = true; +static const char *arg_kill_who = NULL; +static int arg_signal = SIGTERM; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_read_only = false; +static bool arg_mkdir = false; +static bool arg_quiet = false; +static bool arg_ask_password = true; +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_format = NULL; +static const char *arg_uid = NULL; +static char **arg_setenv = NULL; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +static OutputFlags get_output_flags(void) { + return + arg_all * OUTPUT_SHOW_ALL | + arg_full * OUTPUT_FULL_WIDTH | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + colors_enabled() * OUTPUT_COLOR | + !arg_quiet * OUTPUT_WARN_CUTOFF; +} + +typedef struct MachineInfo { + const char *name; + const char *class; + const char *service; +} MachineInfo; + +static int compare_machine_info(const void *a, const void *b) { + const MachineInfo *x = a, *y = b; + + return strcmp(x->name, y->name); +} + +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_(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; + sd_bus *bus = userdata; + int r; + + assert(bus); + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "ListMachines", + &error, + &reply, + NULL); + if (r < 0) { + log_error("Could not get machines: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "(ssso)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { + size_t l; + + if (name[0] == '.' && !arg_all) + continue; + + if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) + return log_oom(); + + machines[n_machines].name = name; + machines[n_machines].class = class; + machines[n_machines].service = service; + + l = strlen(name); + if (l > max_name) + max_name = l; + + l = strlen(class); + if (l > max_class) + max_class = l; + + l = strlen(service); + if (l > max_service) + max_service = l; + + n_machines++; + } + 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); + + qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info); + + if (arg_legend) + printf("%-*s %-*s %-*s\n", + (int) max_name, "MACHINE", + (int) max_class, "CLASS", + (int) max_service, "SERVICE"); + + for (j = 0; j < n_machines; j++) + printf("%-*s %-*s %-*s\n", + (int) max_name, machines[j].name, + (int) max_class, machines[j].class, + (int) max_service, machines[j].service); + + if (arg_legend) + printf("\n%zu machines listed.\n", n_machines); + + return 0; +} + +typedef struct ImageInfo { + const char *name; + const char *type; + bool read_only; + usec_t crtime; + usec_t mtime; + uint64_t size; +} ImageInfo; + +static int compare_image_info(const void *a, const void *b) { + const ImageInfo *x = a, *y = b; + + return strcmp(x->name, y->name); +} + +static int list_images(int argc, char *argv[], void *userdata) { + + _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_(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; + sd_bus *bus = userdata; + uint64_t crtime, mtime, size; + int read_only, r; + + assert(bus); + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "ListImages", + &error, + &reply, + ""); + if (r < 0) { + log_error("Could not get images: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) { + char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)]; + size_t l; + + if (name[0] == '.' && !arg_all) + continue; + + if (!GREEDY_REALLOC(images, n_allocated, n_images + 1)) + return log_oom(); + + images[n_images].name = name; + images[n_images].type = type; + images[n_images].read_only = read_only; + images[n_images].crtime = crtime; + images[n_images].mtime = mtime; + images[n_images].size = size; + + l = strlen(name); + if (l > max_name) + max_name = l; + + l = strlen(type); + if (l > max_type) + max_type = l; + + if (crtime != 0) { + l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime))); + if (l > max_crtime) + max_crtime = l; + } + + if (mtime != 0) { + l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime))); + if (l > max_mtime) + max_mtime = l; + } + + if (size != (uint64_t) -1) { + l = strlen(strna(format_bytes(buf, sizeof(buf), size))); + if (l > max_size) + max_size = l; + } + + n_images++; + } + 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); + + qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info); + + if (arg_legend) + printf("%-*s %-*s %-3s %-*s %-*s %-*s\n", + (int) max_name, "NAME", + (int) max_type, "TYPE", + "RO", + (int) max_size, "USAGE", + (int) max_crtime, "CREATED", + (int) max_mtime, "MODIFIED"); + + for (j = 0; j < n_images; j++) { + char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX]; + + printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n", + (int) max_name, images[j].name, + (int) max_type, images[j].type, + images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "", + (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)), + (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)), + (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime))); + } + + if (arg_legend) + printf("\n%zu images listed.\n", n_images); + + return 0; +} + +static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { + _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; + unsigned c; + + assert(bus); + assert(unit); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + &reply, + "s"); + if (r < 0) + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &cgroup); + if (r < 0) + return bus_log_parse_error(r); + + if (isempty(cgroup)) + return 0; + + c = columns(); + if (c > 18) + c -= 18; + else + c = 0; + + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + + return 0; +} + +static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(bus); + assert(name); + assert(prefix); + assert(prefix2); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineAddresses", + NULL, + &reply, + "s", name); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + int family; + const void *a; + size_t sz; + char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; + + r = sd_bus_message_read(reply, "i", &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); + + fputs(prefix, stdout); + fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout); + if (family == AF_INET6 && ifi > 0) + printf("%%%i", ifi); + fputc('\n', stdout); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (prefix != prefix2) + prefix = prefix2; + } + 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); + + return 0; +} + +static int print_os_release(sd_bus *bus, const char *name, const char *prefix) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *k, *v, *pretty = NULL; + int r; + + assert(bus); + assert(name); + assert(prefix); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineOSRelease", + NULL, + &reply, + "s", name); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'a', "{ss}"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) { + if (streq(k, "PRETTY_NAME")) + pretty = v; + + } + 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 (pretty) + printf("%s%s\n", prefix, pretty); + + return 0; +} + +typedef struct MachineStatusInfo { + char *name; + sd_id128_t id; + char *class; + char *service; + char *unit; + char *root_directory; + pid_t leader; + struct dual_timestamp timestamp; + int *netif; + unsigned n_netif; +} MachineStatusInfo; + +static void machine_status_info_clear(MachineStatusInfo *info) { + if (info) { + free(info->name); + free(info->class); + free(info->service); + free(info->unit); + free(info->root_directory); + free(info->netif); + zero(*info); + } +} + +static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + int ifi = -1; + + assert(bus); + assert(i); + + fputs(strna(i->name), stdout); + + if (!sd_id128_is_null(i->id)) + printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id)); + else + putchar('\n'); + + s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime); + s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime); + + if (s1) + printf("\t Since: %s; %s\n", s2, s1); + else if (s2) + printf("\t Since: %s\n", s2); + + if (i->leader > 0) { + _cleanup_free_ char *t = NULL; + + printf("\t Leader: %u", (unsigned) i->leader); + + get_process_comm(i->leader, &t); + if (t) + printf(" (%s)", t); + + putchar('\n'); + } + + if (i->service) { + printf("\t Service: %s", i->service); + + if (i->class) + printf("; class %s", i->class); + + putchar('\n'); + } else if (i->class) + printf("\t Class: %s\n", i->class); + + if (i->root_directory) + printf("\t Root: %s\n", i->root_directory); + + if (i->n_netif > 0) { + unsigned c; + + fputs("\t Iface:", stdout); + + for (c = 0; c < i->n_netif; c++) { + char name[IF_NAMESIZE+1] = ""; + + if (if_indextoname(i->netif[c], name)) { + fputc(' ', stdout); + fputs(name, stdout); + + if (ifi < 0) + ifi = i->netif[c]; + else + ifi = 0; + } else + printf(" %i", i->netif[c]); + } + + fputc('\n', stdout); + } + + print_addresses(bus, i->name, ifi, + "\t Address: ", + "\t "); + + print_os_release(bus, i->name, "\t OS: "); + + if (i->unit) { + printf("\t Unit: %s\n", i->unit); + show_unit_cgroup(bus, i->unit, i->leader); + + if (arg_transport == BUS_TRANSPORT_LOCAL) + + show_journal_by_unit( + stdout, + i->unit, + arg_output, + 0, + i->timestamp.monotonic, + arg_lines, + 0, + get_output_flags() | OUTPUT_BEGIN_NEWLINE, + SD_JOURNAL_LOCAL_ONLY, + true, + NULL); + } +} + +static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + MachineStatusInfo *i = userdata; + size_t l; + const void *v; + int r; + + assert_cc(sizeof(int32_t) == sizeof(int)); + r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l); + if (r < 0) + return r; + if (r == 0) + return -EBADMSG; + + i->n_netif = l / sizeof(int32_t); + i->netif = memdup(v, l); + if (!i->netif) + return -ENOMEM; + + return 0; +} + +static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) { + + static const struct bus_properties_map map[] = { + { "Name", "s", NULL, offsetof(MachineStatusInfo, name) }, + { "Class", "s", NULL, offsetof(MachineStatusInfo, class) }, + { "Service", "s", NULL, offsetof(MachineStatusInfo, service) }, + { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) }, + { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) }, + { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) }, + { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) }, + { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) }, + { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) }, + { "NetworkInterfaces", "ai", map_netif, 0 }, + {} + }; + + _cleanup_(machine_status_info_clear) MachineStatusInfo info = {}; + int r; + + assert(verb); + assert(bus); + assert(path); + assert(new_line); + + r = bus_map_all_properties(bus, + "org.freedesktop.machine1", + path, + map, + &info); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + if (*new_line) + printf("\n"); + *new_line = true; + + print_machine_status_info(bus, &info); + + return r; +} + +static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) { + int r; + + assert(bus); + assert(path); + assert(new_line); + + if (*new_line) + printf("\n"); + + *new_line = true; + + r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); + if (r < 0) + log_error_errno(r, "Could not get properties: %m"); + + return r; +} + +static int show_machine(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; + bool properties, new_line = false; + sd_bus *bus = userdata; + int r = 0, i; + + assert(bus); + + properties = !strstr(argv[0], "status"); + + pager_open(arg_no_pager, false); + + if (properties && argc <= 1) { + + /* If no argument is specified, inspect the manager + * itself */ + r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line); + if (r < 0) + return r; + } + + for (i = 1; i < argc; i++) { + const char *path = NULL; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachine", + &error, + &reply, + "s", argv[i]); + if (r < 0) { + log_error("Could not get path to machine: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return bus_log_parse_error(r); + + if (properties) + r = show_machine_properties(bus, path, &new_line); + else + r = show_machine_info(argv[0], bus, path, &new_line); + } + + return r; +} + +typedef struct ImageStatusInfo { + char *name; + char *path; + char *type; + int read_only; + usec_t crtime; + usec_t mtime; + uint64_t usage; + uint64_t limit; + uint64_t usage_exclusive; + uint64_t limit_exclusive; +} ImageStatusInfo; + +static void image_status_info_clear(ImageStatusInfo *info) { + if (info) { + free(info->name); + free(info->path); + free(info->type); + zero(*info); + } +} + +static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) { + char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; + char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2; + char bs[FORMAT_BYTES_MAX], *s3; + char bs_exclusive[FORMAT_BYTES_MAX], *s4; + + assert(bus); + assert(i); + + if (i->name) { + fputs(i->name, stdout); + putchar('\n'); + } + + if (i->type) + printf("\t Type: %s\n", i->type); + + if (i->path) + printf("\t Path: %s\n", i->path); + + printf("\t RO: %s%s%s\n", + i->read_only ? ansi_highlight_red() : "", + i->read_only ? "read-only" : "writable", + i->read_only ? ansi_normal() : ""); + + s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime); + s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime); + if (s1 && s2) + printf("\t Created: %s; %s\n", s2, s1); + else if (s2) + printf("\t Created: %s\n", s2); + + s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime); + s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime); + if (s1 && s2) + printf("\tModified: %s; %s\n", s2, s1); + else if (s2) + printf("\tModified: %s\n", s2); + + s3 = format_bytes(bs, sizeof(bs), i->usage); + s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL; + if (s3 && s4) + printf("\t Usage: %s (exclusive: %s)\n", s3, s4); + else if (s3) + printf("\t Usage: %s\n", s3); + + s3 = format_bytes(bs, sizeof(bs), i->limit); + s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL; + if (s3 && s4) + printf("\t Limit: %s (exclusive: %s)\n", s3, s4); + else if (s3) + printf("\t Limit: %s\n", s3); +} + +static int show_image_info(sd_bus *bus, const char *path, bool *new_line) { + + static const struct bus_properties_map map[] = { + { "Name", "s", NULL, offsetof(ImageStatusInfo, name) }, + { "Path", "s", NULL, offsetof(ImageStatusInfo, path) }, + { "Type", "s", NULL, offsetof(ImageStatusInfo, type) }, + { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) }, + { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) }, + { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) }, + { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) }, + { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) }, + { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) }, + { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) }, + {} + }; + + _cleanup_(image_status_info_clear) ImageStatusInfo info = {}; + int r; + + assert(bus); + assert(path); + assert(new_line); + + r = bus_map_all_properties(bus, + "org.freedesktop.machine1", + path, + map, + &info); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + if (*new_line) + printf("\n"); + *new_line = true; + + print_image_status_info(bus, &info); + + return r; +} + +typedef struct PoolStatusInfo { + char *path; + uint64_t usage; + uint64_t limit; +} PoolStatusInfo; + +static void pool_status_info_clear(PoolStatusInfo *info) { + if (info) { + free(info->path); + zero(*info); + info->usage = -1; + info->limit = -1; + } +} + +static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) { + char bs[FORMAT_BYTES_MAX], *s; + + if (i->path) + printf("\t Path: %s\n", i->path); + + s = format_bytes(bs, sizeof(bs), i->usage); + if (s) + printf("\t Usage: %s\n", s); + + s = format_bytes(bs, sizeof(bs), i->limit); + if (s) + printf("\t Limit: %s\n", s); +} + +static int show_pool_info(sd_bus *bus) { + + static const struct bus_properties_map map[] = { + { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) }, + { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) }, + { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) }, + {} + }; + + _cleanup_(pool_status_info_clear) PoolStatusInfo info = { + .usage = (uint64_t) -1, + .limit = (uint64_t) -1, + }; + int r; + + assert(bus); + + r = bus_map_all_properties(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + map, + &info); + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); + + print_pool_status_info(bus, &info); + + return 0; +} + + +static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) { + int r; + + assert(bus); + assert(path); + assert(new_line); + + if (*new_line) + printf("\n"); + + *new_line = true; + + r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); + if (r < 0) + log_error_errno(r, "Could not get properties: %m"); + + return r; +} + +static int show_image(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; + bool properties, new_line = false; + sd_bus *bus = userdata; + int r = 0, i; + + assert(bus); + + properties = !strstr(argv[0], "status"); + + pager_open(arg_no_pager, false); + + if (argc <= 1) { + + /* If no argument is specified, inspect the manager + * itself */ + + if (properties) + r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line); + else + r = show_pool_info(bus); + if (r < 0) + return r; + } + + for (i = 1; i < argc; i++) { + const char *path = NULL; + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetImage", + &error, + &reply, + "s", argv[i]); + if (r < 0) { + log_error("Could not get path to image: %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); + + if (properties) + r = show_image_properties(bus, path, &new_line); + else + r = show_image_info(bus, path, &new_line); + } + + return r; +} + +static int kill_machine(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + + polkit_agent_open_if_enabled(); + + if (!arg_kill_who) + arg_kill_who = "all"; + + for (i = 1; i < argc; i++) { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "KillMachine", + &error, + NULL, + "ssi", argv[i], arg_kill_who, arg_signal); + if (r < 0) { + log_error("Could not kill machine: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int reboot_machine(int argc, char *argv[], void *userdata) { + arg_kill_who = "leader"; + arg_signal = SIGINT; /* sysvinit + systemd */ + + return kill_machine(argc, argv, userdata); +} + +static int poweroff_machine(int argc, char *argv[], void *userdata) { + arg_kill_who = "leader"; + arg_signal = SIGRTMIN+4; /* only systemd */ + + return kill_machine(argc, argv, userdata); +} + +static int terminate_machine(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + + polkit_agent_open_if_enabled(); + + for (i = 1; i < argc; i++) { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "TerminateMachine", + &error, + NULL, + "s", argv[i]); + if (r < 0) { + log_error("Could not terminate machine: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int copy_files(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; + _cleanup_free_ char *abs_host_path = NULL; + char *dest, *host_path, *container_path; + sd_bus *bus = userdata; + bool copy_from; + int r; + + assert(bus); + + polkit_agent_open_if_enabled(); + + copy_from = streq(argv[0], "copy-from"); + dest = argv[3] ?: argv[2]; + host_path = copy_from ? dest : argv[2]; + container_path = copy_from ? argv[2] : dest; + + if (!path_is_absolute(host_path)) { + 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; + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + copy_from ? "CopyFromMachine" : "CopyToMachine"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "sss", + argv[1], + copy_from ? container_path : host_path, + copy_from ? host_path : container_path); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "BindMountMachine", + &error, + NULL, + "sssbb", + argv[1], + argv[2], + argv[3], + arg_read_only, + arg_mkdir); + if (r < 0) { + log_error("Failed to bind mount: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + PTYForward ** forward = (PTYForward**) userdata; + int r; + + assert(m); + assert(forward); + + if (*forward) { + /* If the forwarder is already initialized, tell it to + * exit on the next vhangup(), so that we still flush + * out what might be queued and exit then. */ + + r = pty_forward_set_ignore_vhangup(*forward, false); + if (r >= 0) + return 0; + + log_error_errno(r, "Failed to set ignore_vhangup flag: %m"); + } + + /* On error, or when the forwarder is not initialized yet, quit immediately */ + sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE); + return 0; +} + +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; + + assert(event); + assert(master >= 0); + assert(name); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, flags, forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(*forward, &last_char); + + machine_died = + (flags & PTY_FORWARD_IGNORE_VHANGUP) && + pty_forward_get_ignore_vhangup(*forward) == 0; + + *forward = pty_forward_free(*forward); + + if (last_char != '\n') + fputc('\n', stdout); + + if (machine_died) + log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); + else + log_info("Connection to machine %s terminated.", name); + + sd_event_get_exit_code(event, &ret); + return ret; +} + +static int login_machine(int argc, char *argv[], void *userdata) { + _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_(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; + + assert(bus); + + if (!strv_isempty(arg_setenv) || arg_uid) { + log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); + return -EINVAL; + } + + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Login only supported on local machines."); + return -EOPNOTSUPP; + } + + polkit_agent_open_if_enabled(); + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get event loop: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); + + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineLogin", + &error, + &reply, + "s", machine); + if (r < 0) { + log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine); +} + +static int shell_machine(int argc, char *argv[], void *userdata) { + _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_(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; + + assert(bus); + + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Shell only supported on local machines."); + return -EOPNOTSUPP; + } + + /* Pass $TERM to shell session, if not explicitly specified. */ + if (!strv_find_prefix(arg_setenv, "TERM=")) { + const char *t; + + t = strv_find_prefix(environ, "TERM="); + if (t) { + if (strv_extend(&arg_setenv, t) < 0) + return log_oom(); + } + } + + polkit_agent_open_if_enabled(); + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get event loop: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; + + if (arg_uid) + uid = arg_uid; + else if (machine) { + const char *at; + + at = strchr(machine, '@'); + if (at) { + uid = strndupa(machine, at - machine); + machine = at + 1; + } + } + + if (isempty(machine)) + machine = ".host"; + + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); + + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineShell"); + if (r < 0) + return bus_log_create_error(r); + + path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; + + r = sd_bus_message_append(m, "sss", machine, uid, path); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_setenv); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, 0, machine); +} + +static int remove_image(int argc, char *argv[], void *userdata) { + sd_bus *bus = userdata; + int r, i; + + assert(bus); + + polkit_agent_open_if_enabled(); + + for (i = 1; i < argc; i++) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "RemoveImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", argv[i]); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r)); + } + + return 0; +} + +static int rename_image(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "RenameImage", + &error, + NULL, + "ss", argv[1], argv[2]); + if (r < 0) { + log_error("Could not rename image: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int clone_image(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 = userdata; + int r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CloneImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r)); + + return 0; +} + +static int read_only_image(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int b = true, r; + + if (argc > 2) { + b = parse_boolean(argv[2]); + if (b < 0) { + log_error("Failed to parse boolean argument: %s", argv[2]); + return -EINVAL; + } + } + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "MarkImageReadOnly", + &error, + NULL, + "sb", argv[1], b); + if (r < 0) { + log_error("Could not mark image read-only: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int image_exists(sd_bus *bus, const char *name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(name); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetImage", + &error, + NULL, + "s", name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE)) + return 0; + + return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r)); + } + + return 1; +} + +static int make_service_name(const char *name, char **ret) { + int r; + + assert(name); + assert(ret); + + if (!machine_name_is_valid(name)) { + log_error("Invalid machine name %s.", name); + return -EINVAL; + } + + r = unit_name_build("systemd-nspawn", name, ".service", ret); + if (r < 0) + return log_error_errno(r, "Failed to build unit name: %m"); + + return 0; +} + +static int start_machine(int argc, char *argv[], void *userdata) { + _cleanup_(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; + + assert(bus); + + polkit_agent_open_if_enabled(); + + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_oom(); + + for (i = 1; i < argc; i++) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *unit = NULL; + const char *object; + + r = make_service_name(argv[i], &unit); + if (r < 0) + return r; + + r = image_exists(bus, argv[i]); + if (r < 0) + return r; + if (r == 0) { + log_error("Machine image '%s' does not exist.", argv[1]); + return -ENXIO; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + &error, + &reply, + "ss", unit, "fail"); + if (r < 0) { + log_error("Failed to start unit: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_add(w, object); + if (r < 0) + return log_oom(); + } + + r = bus_wait_for_jobs(w, arg_quiet, NULL); + if (r < 0) + return r; + + return 0; +} + +static int enable_machine(int argc, char *argv[], void *userdata) { + _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; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int carries_install_info = 0; + const char *method = NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + + polkit_agent_open_if_enabled(); + + method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles"; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (i = 1; i < argc; i++) { + _cleanup_free_ char *unit = NULL; + + r = make_service_name(argv[i], &unit); + if (r < 0) + return r; + + r = image_exists(bus, argv[i]); + if (r < 0) + return r; + if (r == 0) { + log_error("Machine image '%s' does not exist.", argv[1]); + return -ENXIO; + } + + r = sd_bus_message_append(m, "s", unit); + 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); + + if (streq(argv[0], "enable")) + r = sd_bus_message_append(m, "bb", false, false); + else + r = sd_bus_message_append(m, "b", false); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r)); + return r; + } + + if (streq(argv[0], "enable")) { + r = sd_bus_message_read(reply, "b", carries_install_info); + if (r < 0) + return bus_log_parse_error(r); + } + + r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); + if (r < 0) + goto finish; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reload", + &error, + NULL, + NULL); + if (r < 0) { + log_error("Failed to reload daemon: %s", bus_error_message(&error, -r)); + goto finish; + } + + r = 0; + +finish: + unit_file_changes_free(changes, n_changes); + + return r; +} + +static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char **our_path = userdata, *line; + unsigned priority; + int r; + + assert(m); + assert(our_path); + + r = sd_bus_message_read(m, "us", &priority, &line); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (!streq_ptr(*our_path, sd_bus_message_get_path(m))) + return 0; + + if (arg_quiet && LOG_PRI(priority) >= LOG_INFO) + return 0; + + log_full(priority, "%s", line); + return 0; +} + +static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char **our_path = userdata, *path, *result; + uint32_t id; + int r; + + assert(m); + assert(our_path); + + r = sd_bus_message_read(m, "uos", &id, &path, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (!streq_ptr(*our_path, path)) + return 0; + + sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done")); + return 0; +} + +static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + assert(s); + assert(si); + + if (!arg_quiet) + log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata)); + + sd_event_exit(sd_event_source_get_event(s), EINTR); + return 0; +} + +static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { + _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; + + assert(bus); + assert(m); + + polkit_agent_open_if_enabled(); + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get event loop: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + r = sd_bus_add_match( + bus, + &slot_job_removed, + "type='signal'," + "sender='org.freedesktop.import1'," + "interface='org.freedesktop.import1.Manager'," + "member='TransferRemoved'," + "path='/org/freedesktop/import1'", + match_transfer_removed, &path); + if (r < 0) + return log_error_errno(r, "Failed to install match: %m"); + + r = sd_bus_add_match( + bus, + &slot_log_message, + "type='signal'," + "sender='org.freedesktop.import1'," + "interface='org.freedesktop.import1.Transfer'," + "member='LogMessage'", + match_log_message, &path); + if (r < 0) + return log_error_errno(r, "Failed to install match: %m"); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to transfer image: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "uo", &id, &path); + if (r < 0) + return bus_log_parse_error(r); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + + if (!arg_quiet) + log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id); + + sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id)); + sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id)); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + return -r; +} + +static int import_tar(int argc, char *argv[], void *userdata) { + _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; + sd_bus *bus = userdata; + int r; + + assert(bus); + + if (argc >= 2) + path = argv[1]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (argc >= 3) + local = argv[2]; + else if (path) + local = basename(path); + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (!local) { + log_error("Need either path or local name."); + return -EINVAL; + } + + r = tar_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local name %s is not a suitable machine name.", local); + return -EINVAL; + } + + if (path) { + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", path); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ImportTar"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "hsbb", + fd >= 0 ? fd : STDIN_FILENO, + local, + arg_force, + arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + return transfer_image_common(bus, m); +} + +static int import_raw(int argc, char *argv[], void *userdata) { + _cleanup_(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; + sd_bus *bus = userdata; + int r; + + assert(bus); + + if (argc >= 2) + path = argv[1]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (argc >= 3) + local = argv[2]; + else if (path) + local = basename(path); + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (!local) { + log_error("Need either path or local name."); + return -EINVAL; + } + + r = raw_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local name %s is not a suitable machine name.", local); + return -EINVAL; + } + + if (path) { + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", path); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ImportRaw"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "hsbb", + fd >= 0 ? fd : STDIN_FILENO, + local, + arg_force, + arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + return transfer_image_common(bus, m); +} + +static void determine_compression_from_filename(const char *p) { + if (arg_format) + return; + + if (!p) + return; + + if (endswith(p, ".xz")) + arg_format = "xz"; + else if (endswith(p, ".gz")) + arg_format = "gzip"; + else if (endswith(p, ".bz2")) + arg_format = "bzip2"; +} + +static int export_tar(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_close_ int fd = -1; + const char *local = NULL, *path = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + local = argv[1]; + if (!machine_name_is_valid(local)) { + log_error("Machine name %s is not valid.", local); + return -EINVAL; + } + + if (argc >= 3) + path = argv[2]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (path) { + determine_compression_from_filename(path); + + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", path); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ExportTar"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "shs", + local, + fd >= 0 ? fd : STDOUT_FILENO, + arg_format); + if (r < 0) + return bus_log_create_error(r); + + return transfer_image_common(bus, m); +} + +static int export_raw(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_close_ int fd = -1; + const char *local = NULL, *path = NULL; + sd_bus *bus = userdata; + int r; + + assert(bus); + + local = argv[1]; + if (!machine_name_is_valid(local)) { + log_error("Machine name %s is not valid.", local); + return -EINVAL; + } + + if (argc >= 3) + path = argv[2]; + if (isempty(path) || streq(path, "-")) + path = NULL; + + if (path) { + determine_compression_from_filename(path); + + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", path); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ExportRaw"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "shs", + local, + fd >= 0 ? fd : STDOUT_FILENO, + arg_format); + if (r < 0) + return bus_log_create_error(r); + + return transfer_image_common(bus, m); +} + +static int pull_tar(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *l = NULL, *ll = NULL; + const char *local, *remote; + sd_bus *bus = userdata; + int r; + + assert(bus); + + remote = argv[1]; + if (!http_url_is_valid(remote)) { + log_error("URL '%s' is not valid.", remote); + return -EINVAL; + } + + if (argc >= 3) + local = argv[2]; + else { + r = import_url_last_component(remote, &l); + if (r < 0) + return log_error_errno(r, "Failed to get final component of URL: %m"); + + local = l; + } + + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = tar_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local 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", + "PullTar"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "sssb", + remote, + local, + import_verify_to_string(arg_verify), + arg_force); + if (r < 0) + return bus_log_create_error(r); + + return transfer_image_common(bus, m); +} + +static int pull_raw(int argc, char *argv[], void *userdata) { + _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; + int r; + + assert(bus); + + remote = argv[1]; + if (!http_url_is_valid(remote)) { + log_error("URL '%s' is not valid.", remote); + return -EINVAL; + } + + if (argc >= 3) + local = argv[2]; + else { + r = import_url_last_component(remote, &l); + if (r < 0) + return log_error_errno(r, "Failed to get final component of URL: %m"); + + local = l; + } + + if (isempty(local) || streq(local, "-")) + local = NULL; + + if (local) { + r = raw_strip_suffixes(local, &ll); + if (r < 0) + return log_oom(); + + local = ll; + + if (!machine_name_is_valid(local)) { + log_error("Local 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", + "PullRaw"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "sssb", + remote, + 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; + const char *remote; + const char *local; + double progress; +} TransferInfo; + +static int compare_transfer_info(const void *a, const void *b) { + const TransferInfo *x = a, *y = b; + + return strcmp(x->local, y->local); +} + +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_(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; + sd_bus *bus = userdata; + uint32_t id, max_id = 0; + double progress; + int r; + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method(bus, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ListTransfers", + &error, + &reply, + NULL); + if (r < 0) { + log_error("Could not get transfers: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "(usssdo)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) { + size_t l; + + if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1)) + return log_oom(); + + transfers[n_transfers].id = id; + transfers[n_transfers].type = type; + transfers[n_transfers].remote = remote; + transfers[n_transfers].local = local; + transfers[n_transfers].progress = progress; + + l = strlen(type); + if (l > max_type) + max_type = l; + + l = strlen(remote); + if (l > max_remote) + max_remote = l; + + l = strlen(local); + if (l > max_local) + max_local = l; + + if (id > max_id) + max_id = id; + + n_transfers++; + } + 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); + + qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info); + + if (arg_legend) + printf("%-*s %-*s %-*s %-*s %-*s\n", + (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID", + (int) 7, "PERCENT", + (int) max_type, "TYPE", + (int) max_local, "LOCAL", + (int) max_remote, "REMOTE"); + + for (j = 0; j < n_transfers; j++) + printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n", + (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id, + (int) 6, (unsigned) (transfers[j].progress * 100), + (int) max_type, transfers[j].type, + (int) max_local, transfers[j].local, + (int) max_remote, transfers[j].remote); + + if (arg_legend) + printf("\n%zu transfers listed.\n", n_transfers); + + return 0; +} + +static int cancel_transfer(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + int r, i; + + assert(bus); + + polkit_agent_open_if_enabled(); + + for (i = 1; i < argc; i++) { + uint32_t id; + + r = safe_atou32(argv[i], &id); + if (r < 0) + return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]); + + r = sd_bus_call_method( + bus, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "CancelTransfer", + &error, + NULL, + "u", id); + if (r < 0) { + log_error("Could not cancel transfer: %s", bus_error_message(&error, -r)); + return r; + } + } + + return 0; +} + +static int set_limit(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus = userdata; + uint64_t limit; + int r; + + if (STR_IN_SET(argv[argc-1], "-", "none", "infinity")) + limit = (uint64_t) -1; + else { + r = parse_size(argv[argc-1], 1024, &limit); + if (r < 0) + return log_error("Failed to parse size: %s", argv[argc-1]); + } + + if (argc > 2) + /* With two arguments changes the quota limit of the + * specified image */ + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "SetImageLimit", + &error, + NULL, + "st", argv[1], limit); + else + /* With one argument changes the pool quota limit */ + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "SetPoolLimit", + &error, + NULL, + "t", limit); + + if (r < 0) { + log_error("Could not set limit: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int clean_images(int argc, char *argv[], void *userdata) { + _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; + uint64_t usage, total = 0; + char fb[FORMAT_BYTES_MAX]; + sd_bus *bus = userdata; + const char *name; + unsigned c = 0; + int r; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CleanPool"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden"); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence permit a longer time for completion. */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply); + if (r < 0) + return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, 'a', "(st)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) { + log_info("Removed image '%s'. Freed exclusive disk space: %s", + name, format_bytes(fb, sizeof(fb), usage)); + + total += usage; + c++; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + log_info("Removed %u images in total. Total freed exclusive disk space %s.", + c, format_bytes(fb, sizeof(fb), total)); + + return 0; +} + +static int help(int argc, char *argv[], void *userdata) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the virtual machine and container\n" + "registration manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --no-ask-password Do not ask for system passwords\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " -p --property=NAME Show only properties by this name\n" + " -q --quiet Suppress output\n" + " -a --all Show all properties, including empty ones\n" + " --value When showing properties, only print the value\n" + " -l --full Do not ellipsize output\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " --uid=USER Specify user ID to invoke shell as\n" + " -E --setenv=VAR=VALUE Add an environment variable for shell\n" + " --read-only Create read-only bind mount\n" + " --mkdir Create directory before bind mounting, if missing\n" + " -n --lines=INTEGER Number of journal entries to show\n" + " -o --output=STRING Change journal output mode (short,\n" + " short-monotonic, verbose, export, json,\n" + " 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\n" + "Machine Commands:\n" + " list List running VMs and containers\n" + " status NAME... Show VM/container details\n" + " show [NAME...] Show properties of one or more VMs/containers\n" + " start NAME... Start container as a service\n" + " login [NAME] Get a login prompt in a container or on the\n" + " local host\n" + " shell [[USER@]NAME [COMMAND...]]\n" + " Invoke a shell (or other command) in a container\n" + " or on the local host\n" + " enable NAME... Enable automatic container start at boot\n" + " disable NAME... Disable automatic container start at boot\n" + " poweroff NAME... Power off one or more containers\n" + " reboot NAME... Reboot one or more containers\n" + " terminate NAME... Terminate one or more VMs/containers\n" + " kill NAME... Send signal to processes of a VM/container\n" + " copy-to NAME PATH [PATH] Copy files from the host to a container\n" + " copy-from NAME PATH [PATH] Copy files from a container to the host\n" + " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" + "Image Commands:\n" + " list-images Show available container and VM images\n" + " image-status [NAME...] Show image details\n" + " show-image [NAME...] Show properties of image\n" + " clone NAME NAME Clone an image\n" + " rename NAME NAME Rename an image\n" + " read-only NAME [BOOL] Mark or unmark image read-only\n" + " remove NAME... Remove an image\n" + " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n" + " clean Remove hidden (or all) images\n\n" + "Image Transfer Commands:\n" + " pull-tar URL [NAME] Download a TAR container image\n" + " pull-raw URL [NAME] Download a RAW container or VM image\n" + " import-tar FILE [NAME] Import a local TAR container image\n" + " import-raw FILE [NAME] Import a local RAW container or VM image\n" + " export-tar NAME [FILE] Export a TAR container image locally\n" + " export-raw NAME [FILE] Export a RAW container or VM image locally\n" + " list-transfers Show list of downloads in progress\n" + " cancel-transfer Cancel a download\n" + , program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, + ARG_VALUE, + ARG_KILL_WHO, + ARG_READ_ONLY, + ARG_MKDIR, + ARG_NO_ASK_PASSWORD, + ARG_VERIFY, + ARG_FORCE, + ARG_FORMAT, + ARG_UID, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "value", no_argument, NULL, ARG_VALUE }, + { "full", no_argument, NULL, 'l' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "read-only", no_argument, NULL, ARG_READ_ONLY }, + { "mkdir", no_argument, NULL, ARG_MKDIR }, + { "quiet", no_argument, NULL, 'q' }, + { "lines", required_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "verify", required_argument, NULL, ARG_VERIFY }, + { "force", no_argument, NULL, ARG_FORCE }, + { "format", required_argument, NULL, ARG_FORMAT }, + { "uid", required_argument, NULL, ARG_UID }, + { "setenv", required_argument, NULL, 'E' }, + {} + }; + + bool reorder = false; + int c, r, shell = -1; + + assert(argc >= 0); + assert(argv); + + for (;;) { + static const char option_string[] = "-hp:als:H:M:qn:o:"; + + c = getopt_long(argc, argv, option_string + reorder, options, NULL); + if (c < 0) + break; + + switch (c) { + + case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a + * non-option argument was discovered. */ + + assert(!reorder); + + /* We generally are fine with the fact that getopt_long() reorders the command line, and looks + * for switches after the main verb. However, for "shell" we really don't want that, since we + * want that switches specified after the machine name are passed to the program to execute, + * and not processed by us. To make this possible, we'll first invoke getopt_long() with + * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first + * non-option parameter. If it's the verb "shell" we remember its position and continue + * processing options. In this case, as soon as we hit the next non-option argument we found + * the machine name, and stop further processing. If the first non-option argument is any other + * verb than "shell" we switch to normal reordering mode and continue processing arguments + * normally. */ + + if (shell >= 0) { + /* If we already found the "shell" verb on the command line, and now found the next + * non-option argument, then this is the machine name and we should stop processing + * further arguments. */ + optind --; /* don't process this argument, go one step back */ + goto done; + } + if (streq(optarg, "shell")) + /* Remember the position of the "shell" verb, and continue processing normally. */ + shell = optind - 1; + else { + int saved_optind; + + /* OK, this is some other verb. In this case, turn on reordering again, and continue + * processing normally. */ + reorder = true; + + /* We changed the option string. getopt_long() only looks at it again if we invoke it + * at least once with a reset option index. Hence, let's reset the option index here, + * then invoke getopt_long() again (ignoring what it has to say, after all we most + * likely already processed it), and the bump the option index so that we read the + * intended argument again. */ + saved_optind = optind; + optind = 0; + (void) getopt_long(argc, argv, option_string + reorder, options, NULL); + optind = saved_optind - 1; /* go one step back, process this argument again */ + } + + break; + + case 'h': + return help(0, NULL, NULL); + + case ARG_VERSION: + return version(); + + case 'p': + r = strv_extend(&arg_property, optarg); + if (r < 0) + return log_oom(); + + /* If the user asked for a particular + * property, show it to him, even if it is + * empty. */ + arg_all = true; + break; + + case 'a': + arg_all = true; + break; + + case ARG_VALUE: + arg_value = true; + break; + + case 'l': + arg_full = true; + break; + + case 'n': + if (safe_atou(optarg, &arg_lines) < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + + case ARG_KILL_WHO: + arg_kill_who = optarg; + break; + + case 's': + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { + log_error("Failed to parse signal string %s.", optarg); + return -EINVAL; + } + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_READ_ONLY: + arg_read_only = true; + break; + + case ARG_MKDIR: + arg_mkdir = true; + break; + + case 'q': + arg_quiet = true; + break; + + case ARG_VERIFY: + arg_verify = import_verify_from_string(optarg); + if (arg_verify < 0) { + log_error("Failed to parse --verify= setting: %s", optarg); + return -EINVAL; + } + break; + + case ARG_FORCE: + arg_force = true; + break; + + case ARG_FORMAT: + if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) { + log_error("Unknown format: %s", optarg); + return -EINVAL; + } + + arg_format = optarg; + break; + + case ARG_UID: + arg_uid = optarg; + break; + + case 'E': + if (!env_assignment_is_valid(optarg)) { + log_error("Environment assignment invalid: %s", optarg); + return -EINVAL; + } + + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + } + +done: + if (shell >= 0) { + char *t; + int i; + + /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the + * argument list initially let's readjust it now, and move the "shell" verb to the back. */ + + optind -= 1; /* place the option index where the "shell" verb will be placed */ + + t = argv[shell]; + for (i = shell; i < optind; i++) + argv[i] = argv[i+1]; + argv[optind] = t; + } + + return 1; +} + +static int machinectl_main(int argc, char *argv[], sd_bus *bus) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines }, + { "list-images", VERB_ANY, 1, 0, list_images }, + { "status", 2, VERB_ANY, 0, show_machine }, + { "image-status", VERB_ANY, VERB_ANY, 0, show_image }, + { "show", VERB_ANY, VERB_ANY, 0, show_machine }, + { "show-image", VERB_ANY, VERB_ANY, 0, show_image }, + { "terminate", 2, VERB_ANY, 0, terminate_machine }, + { "reboot", 2, VERB_ANY, 0, reboot_machine }, + { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, + { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */ + { "kill", 2, VERB_ANY, 0, kill_machine }, + { "login", VERB_ANY, 2, 0, login_machine }, + { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, + { "bind", 3, 4, 0, bind_mount }, + { "copy-to", 3, 4, 0, copy_files }, + { "copy-from", 3, 4, 0, copy_files }, + { "remove", 2, VERB_ANY, 0, remove_image }, + { "rename", 3, 3, 0, rename_image }, + { "clone", 3, 3, 0, clone_image }, + { "read-only", 2, 3, 0, read_only_image }, + { "start", 2, VERB_ANY, 0, start_machine }, + { "enable", 2, VERB_ANY, 0, enable_machine }, + { "disable", 2, VERB_ANY, 0, enable_machine }, + { "import-tar", 2, 3, 0, import_tar }, + { "import-raw", 2, 3, 0, import_raw }, + { "export-tar", 2, 3, 0, export_tar }, + { "export-raw", 2, 3, 0, export_raw }, + { "pull-tar", 2, 3, 0, pull_tar }, + { "pull-raw", 2, 3, 0, pull_raw }, + { "list-transfers", VERB_ANY, 1, 0, list_transfers }, + { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer }, + { "set-limit", 2, 3, 0, set_limit }, + { "clean", VERB_ANY, 1, 0, clean_images }, + {} + }; + + return dispatch_verb(argc, argv, verbs, bus); +} + +int main(int argc, char*argv[]) { + sd_bus *bus = NULL; + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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; + } + + sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); + + r = machinectl_main(argc, argv, bus); + +finish: + sd_bus_flush_close_unref(bus); + pager_close(); + polkit_agent_close(); + + strv_free(arg_property); + strv_free(arg_setenv); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/machinectl/machinectl.completion.bash b/src/grp-machine/machinectl/machinectl.completion.bash new file mode 100644 index 0000000000..aebe48304d --- /dev/null +++ b/src/grp-machine/machinectl/machinectl.completion.bash @@ -0,0 +1,99 @@ +# machinectl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + (machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager; echo ".host") | \ + { while read a b; do echo " $a"; done; } | sort -u; +} + +_machinectl() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='--all -a --full --help -h --no-ask-password --no-legend --no-pager --version' + [ARG]='--host -H --kill-who -M --machine --property -p --signal -s' + ) + + local -A VERBS=( + [STANDALONE]='list list-images pull-tar pull-raw import-tar import-raw export-tar export-raw list-transfers cancel-transfer' + [MACHINES]='status show start stop login shell enable disable poweroff reboot terminate kill copy-to copy-from image-status show-image clone rename read-only remove set-limit' + ) + + _init_completion || return + + for ((i=0; i <= COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --signal|-s) + _signals + return + ;; + --kill-who) + comps='all leader' + ;; + --host|-H) + comps=$(compgen -A hostname) + ;; + --machine|-M) + comps=$( __get_machines ) + ;; + --property|-p) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + + elif __contains_word "$verb" ${VERBS[MACHINES]}; then + comps=$( __get_machines ) + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _machinectl machinectl diff --git a/src/grp-machine/machinectl/machinectl.completion.zsh b/src/grp-machine/machinectl/machinectl.completion.zsh new file mode 100644 index 0000000000..92d77109a5 --- /dev/null +++ b/src/grp-machine/machinectl/machinectl.completion.zsh @@ -0,0 +1,100 @@ +#compdef machinectl + +__get_available_machines () { + machinectl --no-legend list-images | {while read -r a b; do echo $a; done;} +} + +_available_machines() { + local -a _machines + _machines=("${(fo)$(__get_available_machines)}") + typeset -U _machines + if [[ -n "$_machines" ]]; then + _describe 'machines' _machines + else + _message 'no machines' + fi +} + +(( $+functions[_machinectl_command] )) || _machinectl_command() +{ + local -a _machinectl_cmds + _machinectl_cmds=( + "list:List currently running VMs/containers" + "status:Show VM/container status" + "show:Show properties of one or more VMs/containers" + "start:Start container as a service" + "stop:Stop container (equal to poweroff)" + "login:Get a login prompt on a VM/container" + "enable:Enable automatic container start at boot" + "disable:Disable automatic container start at boot" + "poweroff:Power off one or more VMs/containers" + "reboot:Reboot one or more VMs/containers" + "terminate:Terminate one or more VMs/containers" + "kill:Send signal to process or a VM/container" + "copy-to:Copy files from the host to a container" + "copy-from:Copy files from a container to the host" + "bind:Bind mount a path from the host into a container" + + "list-images:Show available container and VM images" + "image-status:Show image details" + "show-image:Show properties of image" + "clone:Clone an image" + "rename:Rename an image" + "read-only:Mark or unmark image read-only" + "remove:Remove an image" + + "pull-tar:Download a TAR container image" + "pull-raw:Download a RAW container or VM image" + "list-transfers:Show list of downloads in progress" + "cancel-transfer:Cancel a download" + ) + + if (( CURRENT == 1 )); then + _describe -t commands 'machinectl command' _machinectl_cmds || compadd "$@" + else + local curcontext="$curcontext" + cmd="${${_machinectl_cmds[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + if (( CURRENT == 2 )); then + case $cmd in + list*|cancel-transfer|pull-tar|pull-raw) + msg="no options" ;; + start) + _available_machines ;; + *) + _sd_machines + esac + else + case $cmd in + copy-to|copy-from|bind) + _files ;; + *) msg="no options" + esac + fi + else + _message "no more options" + fi + fi +} + +_arguments \ + {-h,--help}'[Prints a short help text and exits.]' \ + '--version[Prints a short version string and exits.]' \ + '--no-pager[Do not pipe output into a pager.]' \ + '--no-legend[Do not show the headers and footers.]' \ + '--no-ask-password[Do not ask for system passwords.]' \ + {-H+,--host=}'[Operate on remote host.]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container.]:machine:_sd_machines' \ + {-p+,--property=}'[Limit output to specified property.]:property:(Name Id Timestamp TimestampMonotonic Service Scope Leader Class State RootDirectory)' \ + {-a,--all}'[Show all proerties.]' \ + {-q,--quiet}'[Suppress output.]' \ + {-l,--full}'[Do not ellipsize cgroup members.]' \ + '--kill-who=[Who to send signal to.]:killwho:(leader all)' \ + {-s+,--signal=}'[Which signal to send.]:signal:_signals' \ + '--read-only[Create read-only bind mount.]' \ + '--mkdir[Create directory before bind mounting, if missing.]' \ + {-n+,--lines=}'[Number of journal entries to show.]:integer' \ + {-o+,--output=}'[Change journal output mode.]:output modes:_sd_outputmodes' \ + '--verify=[Verification mode for downloaded images.]:verify:(no checksum signature)' \ + '--force[Download image even if already exists.]' \ + '*::machinectl command:_machinectl_command' diff --git a/src/grp-machine/machinectl/machinectl.xml b/src/grp-machine/machinectl/machinectl.xml new file mode 100644 index 0000000000..597a5cc583 --- /dev/null +++ b/src/grp-machine/machinectl/machinectl.xml @@ -0,0 +1,1021 @@ + + + + + + + + + machinectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + machinectl + 1 + + + + machinectl + Control the systemd machine manager + + + + + machinectl + OPTIONS + COMMAND + NAME + + + + + Description + + machinectl may be used to introspect and + control the state of the + systemd1 + virtual machine and container registration manager + systemd-machined.service8. + + machinectl may be used to execute + operations on machines and images. Machines in this sense are + considered running instances of: + + + Virtual Machines (VMs) that virtualize hardware + to run full operating system (OS) instances (including their kernels) + in a virtualized environment on top of the host OS. + + Containers that share the hardware and + OS kernel with the host OS, in order to run + OS userspace instances on top the host OS. + + The host system itself + + + Machines are identified by names that follow the same rules + as UNIX and DNS host names, for details, see below. Machines are + instantiated from disk or file system images that frequently — but not + necessarily — carry the same name as machines running from + them. Images in this sense are considered: + + + Directory trees containing an OS, including its + top-level directories /usr, + /etc, and so on. + + btrfs subvolumes containing OS trees, similar to + normal directory trees. + + Binary "raw" disk images containing MBR or GPT + partition tables and Linux file system partitions. + + The file system tree of the host OS itself. + + + + + + Options + + The following options are understood: + + + + + + + When showing machine or image properties, + limit the output to certain properties as specified by the + argument. If not specified, all set properties are shown. The + argument should be a property name, such as + Name. If specified more than once, all + properties with the specified names are + shown. + + + + + + + When showing machine or image properties, show + all properties regardless of whether they are set or + not. + + When listing VM or container images, do not suppress + images beginning in a dot character + (.). + + When cleaning VM or container images, remove all images, not just hidden ones. + + + + + + When printing properties with show, only print the value, + and skip the property name and =. + + + + + + + Do not ellipsize process tree entries. + + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + When used with kill, choose + which processes to kill. Must be one of + , or to select + whether to kill only the leader process of the machine or all + processes of the machine. If omitted, defaults to + . + + + + + + + When used with kill, choose + which signal to send to selected processes. Must be one of the + well-known signal specifiers, such as + SIGTERM, SIGINT or + SIGSTOP. If omitted, defaults to + SIGTERM. + + + + + + When used with the shell + command, chooses the user ID to open the interactive shell + session as. If this switch is not specified, defaults to + root. Note that this switch is not + supported for the login command (see + below). + + + + + + + When used with the shell command, sets an environment + variable to pass to the executed shell. Takes an environment variable name and value, + separated by =. This switch may be used multiple times to set multiple + environment variables. Note that this switch is not supported for the + login command (see below). + + + + + + When used with bind, creates + the destination directory before applying the bind + mount. + + + + + + When used with bind, applies + a read-only bind mount. + + When used with clone, import-raw or import-tar a + read-only container or VM image is created. + + + + + + + When used with status, + controls the number of journal lines to show, counting from + the most recent ones. Takes a positive integer argument. + Defaults to 10. + + + + + + + + When used with status, + controls the formatting of the journal entries that are shown. + For the available choices, see + journalctl1. + Defaults to short. + + + + + + When downloading a container or VM image, + specify whether the image shall be verified before it is made + available. Takes one of no, + checksum and signature. + If no, no verification is done. If + checksum is specified, the download is + checked for integrity after the transfer is complete, but no + signatures are verified. If signature is + specified, the checksum is verified and the image's signature + is checked against a local keyring of trustable vendors. It is + strongly recommended to set this option to + signature if the server and protocol + support this. Defaults to + signature. + + + + + + When downloading a container or VM image, and + a local copy by the specified local machine name already + exists, delete it first and replace it by the newly downloaded + image. + + + + + + When used with the + or commands, specifies the + compression format to use for the resulting file. Takes one of + uncompressed, xz, + gzip, bzip2. By default, + the format is determined automatically from the image file + name passed. + + + + + + + + + + + + + + Commands + + The following commands are understood: + + Machine Commands + + + list + + List currently running (online) virtual + machines and containers. To enumerate machine images that can + be started, use list-images (see + below). Note that this command hides the special + .host machine by default. Use the + switch to show it. + + + + status NAME... + + Show runtime status information about + one or more virtual machines and containers, followed by the + most recent log data from the journal. This function is + intended to generate human-readable output. If you are looking + for computer-parsable output, use show + instead. Note that the log data shown is reported by the + virtual machine or container manager, and frequently contains + console output of the machine, but not necessarily journal + contents of the machine itself. + + + + show [NAME...] + + Show properties of one or more registered + virtual machines or containers or the manager itself. If no + argument is specified, properties of the manager will be + shown. If a NAME is specified, properties of this virtual + machine or container are shown. By default, empty properties + are suppressed. Use to show those too. + To select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required, and does + not print the cgroup tree or journal entries. Use + status if you are looking for formatted + human-readable output. + + + + start NAME... + + Start a container as a system service, using + systemd-nspawn1. + This starts systemd-nspawn@.service, + instantiated for the specified machine name, similar to the + effect of systemctl start on the service + name. systemd-nspawn looks for a container + image by the specified name in + /var/lib/machines/ (and other search + paths, see below) and runs it. Use + list-images (see below) for listing + available container images to start. + + Note that + systemd-machined.service8 + also interfaces with a variety of other container and VM + managers, systemd-nspawn is just one + implementation of it. Most of the commands available in + machinectl may be used on containers or VMs + controlled by other managers, not just + systemd-nspawn. Starting VMs and container + images on those managers requires manager-specific + tools. + + To interactively start a container on the command line + with full access to the container's console, please invoke + systemd-nspawn directly. To stop a running + container use machinectl poweroff. + + + + login [NAME] + + Open an interactive terminal login session in + a container or on the local host. If an argument is supplied, + it refers to the container machine to connect to. If none is + specified, or the container name is specified as the empty + string, or the special machine name .host + (see below) is specified, the connection is made to the local + host instead. This will create a TTY connection to a specific + container or the local host and asks for the execution of a + getty on it. Note that this is only supported for containers + running + systemd1 + as init system. + + This command will open a full login prompt on the + container or the local host, which then asks for username and + password. Use shell (see below) or + systemd-run1 + with the switch to directly invoke + a single command, either interactively or in the + background. + + + + shell [[NAME@]NAME [PATH [ARGUMENTS...]]] + + Open an interactive shell session in a + container or on the local host. The first argument refers to + the container machine to connect to. If none is specified, or + the machine name is specified as the empty string, or the + special machine name .host (see below) is + specified, the connection is made to the local host + instead. This works similar to login but + immediately invokes a user process. This command runs the + specified executable with the specified arguments, or + /bin/sh if none is specified. By default, + opens a root shell, but by using + , or by prefixing the machine name with + a username and an @ character, a different + user may be selected. Use to set + environment variables for the executed process. + + When using the shell command without + arguments, (thus invoking the executed shell or command on the + local host), it is in many ways similar to a su1 + session, but, unlike su, completely isolates + the new session from the originating session, so that it + shares no process or session properties, and is in a clean and + well-defined state. It will be tracked in a new utmp, login, + audit, security and keyring session, and will not inherit any + environment variables or resource limits, among other + properties. + + Note that + systemd-run1 + may be used in place of the shell command, + and allows more detailed, low-level configuration of the + invoked unit. However, it is frequently more privileged than + the shell command. + + + + enable NAME... + disable NAME... + + Enable or disable a container as a system + service to start at system boot, using + systemd-nspawn1. + This enables or disables + systemd-nspawn@.service, instantiated for + the specified machine name, similar to the effect of + systemctl enable or systemctl + disable on the service name. + + + + poweroff NAME... + + Power off one or more containers. This will + trigger a reboot by sending SIGRTMIN+4 to the container's init + process, which causes systemd-compatible init systems to shut + down cleanly. Use stop as alias for poweroff. + This operation does not work on containers that do not run a + systemd1-compatible + init system, such as sysvinit. Use + terminate (see below) to immediately + terminate a container or VM, without cleanly shutting it + down. + + + + reboot NAME... + + Reboot one or more containers. This will + trigger a reboot by sending SIGINT to the container's init + process, which is roughly equivalent to pressing Ctrl+Alt+Del + on a non-containerized system, and is compatible with + containers running any system manager. + + + + terminate NAME... + + Immediately terminates a virtual machine or + container, without cleanly shutting it down. This kills all + processes of the virtual machine or container and deallocates + all resources attached to that instance. Use + poweroff to issue a clean shutdown + request. + + + + kill NAME... + + Send a signal to one or more processes of the + virtual machine or container. This means processes as seen by + the host, not the processes inside the virtual machine or + container. Use to select which + process to kill. Use to select the + signal to send. + + + + bind NAME PATH [PATH] + + Bind mounts a directory from the host into the + specified container. The first directory argument is the + source directory on the host, the second directory argument + is the destination directory in the container. When the + latter is omitted, the destination path in the container is + the same as the source path on the host. When combined with + the switch, a ready-only bind + mount is created. When combined with the + switch, the destination path is first + created before the mount is applied. Note that this option is + currently only supported for + systemd-nspawn1 + containers. + + + + copy-to NAME PATH [PATH] + + Copies files or directories from the host + system into a running container. Takes a container name, + followed by the source path on the host and the destination + path in the container. If the destination path is omitted, the + same as the source path is used. + + + + + copy-from NAME PATH [PATH] + + Copies files or directories from a container + into the host system. Takes a container name, followed by the + source path in the container the destination path on the host. + If the destination path is omitted, the same as the source path + is used. + + + + Image Commands + + + list-images + + Show a list of locally installed container and + VM images. This enumerates all raw disk images and container + directories and subvolumes in + /var/lib/machines/ (and other search + paths, see below). Use start (see above) to + run a container off one of the listed images. Note that, by + default, containers whose name begins with a dot + (.) are not shown. To show these too, + specify . Note that a special image + .host always implicitly exists and refers + to the image the host itself is booted from. + + + + image-status [NAME...] + + Show terse status information about one or + more container or VM images. This function is intended to + generate human-readable output. Use + show-image (see below) to generate + computer-parsable output instead. + + + + show-image [NAME...] + + Show properties of one or more registered + virtual machine or container images, or the manager itself. If + no argument is specified, properties of the manager will be + shown. If a NAME is specified, properties of this virtual + machine or container image are shown. By default, empty + properties are suppressed. Use to show + those too. To select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required. Use + image-status if you are looking for + formatted human-readable output. + + + + clone NAME NAME + + Clones a container or VM image. The arguments specify the name of the image to clone and the + name of the newly cloned image. Note that plain directory container images are cloned into btrfs subvolume + images with this command, if the underlying file system supports this. Note that cloning a container or VM + image is optimized for btrfs file systems, and might not be efficient on others, due to file system + limitations. + + Note that this command leaves host name, machine ID and + all other settings that could identify the instance + unmodified. The original image and the cloned copy will hence + share these credentials, and it might be necessary to manually + change them in the copy. + + If combined with the switch a read-only cloned image is + created. + + + + rename NAME NAME + + Renames a container or VM image. The + arguments specify the name of the image to rename and the new + name of the image. + + + + read-only NAME [BOOL] + + Marks or (unmarks) a container or VM image + read-only. Takes a VM or container image name, followed by a + boolean as arguments. If the boolean is omitted, positive is + implied, i.e. the image is marked read-only. + + + + remove NAME... + + Removes one or more container or VM images. + The special image .host, which refers to + the host's own directory tree, may not be + removed. + + + + set-limit [NAME] BYTES + + Sets the maximum size in bytes that a specific + container or VM image, or all images, may grow up to on disk + (disk quota). Takes either one or two parameters. The first, + optional parameter refers to a container or VM image name. If + specified, the size limit of the specified image is changed. If + omitted, the overall size limit of the sum of all images stored + locally is changed. The final argument specifies the size + limit in bytes, possibly suffixed by the usual K, M, G, T + units. If the size limit shall be disabled, specify + - as size. + + Note that per-container size limits are only supported + on btrfs file systems. Also note that, if + set-limit is invoked without an image + parameter, and /var/lib/machines is + empty, and the directory is not located on btrfs, a btrfs + loopback file is implicitly created as + /var/lib/machines.raw with the given + size, and mounted to + /var/lib/machines. The size of the + loopback may later be readjusted with + set-limit, as well. If such a + loopback-mounted /var/lib/machines + directory is used, set-limit without an image + name alters both the quota setting within the file system as + well as the loopback file and file system size + itself. + + + + clean + + Remove hidden VM or container images (or all). This command removes all hidden machine images + from /var/lib/machines, i.e. those whose name begins with a dot. Use machinectl + list-images --all to see a list of all machine images, including the hidden ones. + + When combined with the switch removes all images, not just hidden ones. This + command effectively empties /var/lib/machines. + + Note that commands such as machinectl pull-tar or machinectl + pull-raw usually create hidden, read-only, unmodified machine images from the downloaded image first, + before cloning a writable working copy of it, in order to avoid duplicate downloads in case of images that are + reused multiple times. Use machinectl clean to remove old, hidden images created this + way. + + + + + Image Transfer Commands + + + pull-tar URL [NAME] + + Downloads a .tar + container image from the specified URL, and makes it available + under the specified local machine name. The URL must be of + type http:// or + https://, and must refer to a + .tar, .tar.gz, + .tar.xz or .tar.bz2 + archive file. If the local machine name is omitted, it + is automatically derived from the last component of the URL, + with its suffix removed. + + The image is verified before it is made available, + unless is specified. Verification + is done via SHA256SUMS and SHA256SUMS.gpg files that need to + be made available on the same web server, under the same URL + as the .tar file, but with the last + component (the filename) of the URL replaced. With + , only the SHA256 checksum + for the file is verified, based on the + SHA256SUMS file. With + , the SHA256SUMS file is + first verified with detached GPG signature file + SHA256SUMS.gpg. The public key for this + verification step needs to be available in + /usr/lib/systemd/import-pubring.gpg or + /etc/systemd/import-pubring.gpg. + + The container image will be downloaded and stored in a + read-only subvolume in + /var/lib/machines/ that is named after + the specified URL and its HTTP etag. A writable snapshot is + then taken from this subvolume, and named after the specified + local name. This behavior ensures that creating multiple + container instances of the same URL is efficient, as multiple + downloads are not necessary. In order to create only the + read-only image, and avoid creating its writable snapshot, + specify - as local machine name. + + Note that the read-only subvolume is prefixed with + .tar-, and is thus not shown by + list-images, unless + is passed. + + Note that pressing C-c during execution of this command + will not abort the download. Use + cancel-transfer, described + below. + + + + pull-raw URL [NAME] + + Downloads a .raw + container or VM disk image from the specified URL, and makes + it available under the specified local machine name. The URL + must be of type http:// or + https://. The container image must either + be a .qcow2 or raw disk image, optionally + compressed as .gz, + .xz, or .bz2. If the + local machine name is omitted, it is automatically + derived from the last component of the URL, with its suffix + removed. + + Image verification is identical for raw and tar images + (see above). + + If the downloaded image is in + .qcow2 format it is converted into a raw + image file before it is made available. + + Downloaded images of this type will be placed as + read-only .raw file in + /var/lib/machines/. A local, writable + (reflinked) copy is then made under the specified local + machine name. To omit creation of the local, writable copy + pass - as local machine name. + + Similar to the behavior of pull-tar, + the read-only image is prefixed with + .raw-, and thus not shown by + list-images, unless + is passed. + + Note that pressing C-c during execution of this command + will not abort the download. Use + cancel-transfer, described + below. + + + + import-tar FILE [NAME] + import-raw FILE [NAME] + Imports a TAR or RAW container or VM image, + and places it under the specified name in + /var/lib/machines/. When + import-tar is used, the file specified as + the first argument should be a tar archive, possibly compressed + with xz, gzip or bzip2. It will then be unpacked into its own + subvolume in /var/lib/machines. When + import-raw is used, the file should be a + qcow2 or raw disk image, possibly compressed with xz, gzip or + bzip2. If the second argument (the resulting image name) is + not specified, it is automatically derived from the file + name. If the file name is passed as -, the + image is read from standard input, in which case the second + argument is mandatory. + + Both pull-tar and pull-raw + will resize /var/lib/machines.raw and the + filesystem therein as necessary. Optionally, the + switch may be used to create a + read-only container or VM image. No cryptographic validation + is done when importing the images. + + Much like image downloads, ongoing imports may be listed + with list-transfers and aborted with + cancel-transfer. + + + + export-tar NAME [FILE] + export-raw NAME [FILE] + Exports a TAR or RAW container or VM image and + stores it in the specified file. The first parameter should be + a VM or container image name. The second parameter should be a + file path the TAR or RAW image is written to. If the path ends + in .gz, the file is compressed with gzip, if + it ends in .xz, with xz, and if it ends in + .bz2, with bzip2. If the path ends in + neither, the file is left uncompressed. If the second argument + is missing, the image is written to standard output. The + compression may also be explicitly selected with the + switch. This is in particular + useful if the second parameter is left unspecified. + + Much like image downloads and imports, ongoing exports + may be listed with list-transfers and + aborted with + cancel-transfer. + + Note that, currently, only directory and subvolume images + may be exported as TAR images, and only raw disk images as RAW + images. + + + + list-transfers + + Shows a list of container or VM image + downloads, imports and exports that are currently in + progress. + + + + cancel-transfers ID... + + Aborts a download, import or export of the + container or VM image with the specified ID. To list ongoing + transfers and their IDs, use + list-transfers. + + + + + + + + Machine and Image Names + + The machinectl tool operates on machines + and images whose names must be chosen following strict + rules. Machine names must be suitable for use as host names + following a conservative subset of DNS and UNIX/Linux + semantics. Specifically, they must consist of one or more + non-empty label strings, separated by dots. No leading or trailing + dots are allowed. No sequences of multiple dots are allowed. The + label strings may only consist of alphanumeric characters as well + as the dash and underscore. The maximum length of a machine name + is 64 characters. + + A special machine with the name .host + refers to the running host system itself. This is useful for execution + operations or inspecting the host system as well. Note that + machinectl list will not show this special + machine unless the switch is specified. + + Requirements on image names are less strict, however, they must be + valid UTF-8, must be suitable as file names (hence not be the + single or double dot, and not include a slash), and may not + contain control characters. Since many operations search for an + image by the name of a requested machine, it is recommended to name + images in the same strict fashion as machines. + + A special image with the name .host + refers to the image of the running host system. It hence + conceptually maps to the special .host machine + name described above. Note that machinectl + list-images will not show this special image either, unless + is specified. + + + + Files and Directories + + Machine images are preferably stored in + /var/lib/machines/, but are also searched for + in /usr/local/lib/machines/ and + /usr/lib/machines/. For compatibility reasons, + the directory /var/lib/container/ is + searched, too. Note that images stored below + /usr are always considered read-only. It is + possible to symlink machines images from other directories into + /var/lib/machines/ to make them available for + control with machinectl. + + Note that many image operations are only supported, + efficient or atomic on btrfs file systems. Due to this, if the + pull-tar, pull-raw, + import-tar, import-raw and + set-limit commands notice that + /var/lib/machines is empty and not located on + btrfs, they will implicitly set up a loopback file + /var/lib/machines.raw containing a btrfs file + system that is mounted to + /var/lib/machines. The size of this loopback + file may be controlled dynamically with + set-limit. + + Disk images are understood by + systemd-nspawn1 + and machinectl in three formats: + + + A simple directory tree, containing the files + and directories of the container to boot. + + Subvolumes (on btrfs file systems), which are + similar to the simple directories, described above. However, + they have additional benefits, such as efficient cloning and + quota reporting. + + "Raw" disk images, i.e. binary images of disks + with a GPT or MBR partition table. Images of this type are + regular files with the suffix + .raw. + + + See + systemd-nspawn1 + for more information on image formats, in particular its + and + options. + + + + Examples + + Download an Ubuntu image and open a shell in it + + # machinectl pull-tar https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-root.tar.gz +# systemd-nspawn -M trusty-server-cloudimg-amd64-root + + This downloads and verifies the specified + .tar image, and then uses + systemd-nspawn1 + to open a shell in it. + + + + Download a Fedora image, set a root password in it, start + it as service + + # machinectl pull-raw --verify=no https://dl.fedoraproject.org/pub/fedora/linux/releases/23/Cloud/x86_64/Images/Fedora-Cloud-Base-23-20151030.x86_64.raw.xz +# systemd-nspawn -M Fedora-Cloud-Base-23-20151030 +# passwd +# exit +# machinectl start Fedora-Cloud-Base-23-20151030 +# machinectl login Fedora-Cloud-Base-23-20151030 + + This downloads the specified .raw + image with verification disabled. Then, a shell is opened in it + and a root password is set. Afterwards the shell is left, and + the machine started as system service. With the last command a + login prompt into the container is requested. + + + + Exports a container image as tar file + + # machinectl export-tar fedora myfedora.tar.xz + + Exports the container fedora as an + xz-compressed tar file myfedora.tar.xz into the + current directory. + + + + Create a new shell session + + # machinectl shell --uid=lennart + + This creates a new shell session on the local host for + the user ID lennart, in a su1-like + fashion. + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + + + See Also + + systemd-machined.service8, + systemd-nspawn1, + systemd.special7, + tar1, + xz1, + gzip1, + bzip21 + + + + diff --git a/src/grp-machine/nss-mymachines/Makefile b/src/grp-machine/nss-mymachines/Makefile new file mode 100644 index 0000000000..54d78ed2f1 --- /dev/null +++ b/src/grp-machine/nss-mymachines/Makefile @@ -0,0 +1,45 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libnss_mymachines_la_SOURCES = \ + src/nss-mymachines/nss-mymachines.sym \ + src/nss-mymachines/nss-mymachines.c + +libnss_mymachines_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -shrext .so.2 \ + -Wl,--version-script=$(srcdir)/nss-mymachines.sym + +libnss_mymachines_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la + +lib_LTLIBRARIES += \ + libnss_mymachines.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/nss-mymachines/nss-mymachines.c b/src/grp-machine/nss-mymachines/nss-mymachines.c new file mode 100644 index 0000000000..0bcdd45d37 --- /dev/null +++ b/src/grp-machine/nss-mymachines/nss-mymachines.c @@ -0,0 +1,738 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/nss-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" + +NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); +NSS_GETPW_PROTOTYPES(mymachines); +NSS_GETGR_PROTOTYPES(mymachines); + +#define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000)) +#define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000)) + +static int count_addresses(sd_bus_message *m, int af, unsigned *ret) { + unsigned c = 0; + int r; + + assert(m); + assert(ret); + + while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) { + int family; + + r = sd_bus_message_read(m, "i", &family); + if (r < 0) + return r; + + r = sd_bus_message_skip(m, "ay"); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + if (af != AF_UNSPEC && family != af) + continue; + + c++; + } + if (r < 0) + return r; + + r = sd_bus_message_rewind(m, false); + if (r < 0) + return r; + + *ret = c; + return 0; +} + +enum nss_status _nss_mymachines_gethostbyname4_r( + const char *name, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp) { + + struct gaih_addrtuple *r_tuple, *r_tuple_first = 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; + unsigned i = 0, c = 0; + char *r_name; + int n_ifindices, r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(pat); + assert(buffer); + assert(errnop); + assert(h_errnop); + + r = sd_machine_get_class(name, &class); + if (r < 0) + goto fail; + if (!streq(class, "container")) { + r = -ENOTTY; + goto fail; + } + + n_ifindices = sd_machine_get_ifindices(name, &ifindices); + if (n_ifindices < 0) { + r = n_ifindices; + goto fail; + } + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineAddresses", + NULL, + &reply, + "s", name); + if (r < 0) + goto fail; + + r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + if (r < 0) + goto fail; + + r = count_addresses(reply, AF_UNSPEC, &c); + if (r < 0) + goto fail; + + if (c <= 0) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + l = strlen(name); + ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; + if (buflen < ms) { + *errnop = ENOMEM; + *h_errnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, name, l+1); + idx = ALIGN(l+1); + + /* Second, append addresses */ + r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); + while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + int family; + const void *a; + size_t sz; + + r = sd_bus_message_read(reply, "i", &family); + if (r < 0) + goto fail; + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + if (!IN_SET(family, AF_INET, AF_INET6)) { + r = -EAFNOSUPPORT; + goto fail; + } + + if (sz != FAMILY_ADDRESS_SIZE(family)) { + r = -EINVAL; + goto fail; + } + + r_tuple = (struct gaih_addrtuple*) (buffer + idx); + r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); + r_tuple->name = r_name; + r_tuple->family = family; + r_tuple->scopeid = n_ifindices == 1 ? ifindices[0] : 0; + memcpy(r_tuple->addr, a, sz); + + idx += ALIGN(sizeof(struct gaih_addrtuple)); + i++; + } + + assert(i == c); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + assert(idx == ms); + + if (*pat) + **pat = *r_tuple_first; + else + *pat = r_tuple_first; + + if (ttlp) + *ttlp = 0; + + /* Explicitly reset all error variables */ + *errnop = 0; + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + + return NSS_STATUS_SUCCESS; + +fail: + *errnop = -r; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_mymachines_gethostbyname3_r( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp, + char **canonp) { + + _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; + size_t l, idx, ms, alen; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(result); + assert(buffer); + assert(errnop); + assert(h_errnop); + + if (af == AF_UNSPEC) + af = AF_INET; + + if (af != AF_INET && af != AF_INET6) { + r = -EAFNOSUPPORT; + goto fail; + } + + r = sd_machine_get_class(name, &class); + if (r < 0) + goto fail; + if (!streq(class, "container")) { + r = -ENOTTY; + goto fail; + } + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineAddresses", + NULL, + &reply, + "s", name); + if (r < 0) + goto fail; + + r = sd_bus_message_enter_container(reply, 'a', "(iay)"); + if (r < 0) + goto fail; + + r = count_addresses(reply, af, &c); + if (r < 0) + goto fail; + + if (c <= 0) { + *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + alen = FAMILY_ADDRESS_SIZE(af); + l = strlen(name); + + ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); + + if (buflen < ms) { + *errnop = ENOMEM; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_TRYAGAIN; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, name, l+1); + idx = ALIGN(l+1); + + /* Second, create aliases array */ + r_aliases = buffer + idx; + ((char**) r_aliases)[0] = NULL; + idx += sizeof(char*); + + /* Third, append addresses */ + r_addr = buffer + idx; + while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { + int family; + const void *a; + size_t sz; + + r = sd_bus_message_read(reply, "i", &family); + if (r < 0) + goto fail; + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + if (family != af) + continue; + + if (sz != alen) { + r = -EINVAL; + goto fail; + } + + memcpy(r_addr + i*ALIGN(alen), a, alen); + i++; + } + + assert(i == c); + idx += c * ALIGN(alen); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + /* Third, append address pointer array */ + r_addr_list = buffer + idx; + for (i = 0; i < c; i++) + ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen); + + ((char**) r_addr_list)[i] = NULL; + idx += (c+1) * sizeof(char*); + + assert(idx == ms); + + result->h_name = r_name; + result->h_aliases = (char**) r_aliases; + result->h_addrtype = af; + result->h_length = alen; + result->h_addr_list = (char**) r_addr_list; + + if (ttlp) + *ttlp = 0; + + if (canonp) + *canonp = r_name; + + /* Explicitly reset all error variables */ + *errnop = 0; + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + + return NSS_STATUS_SUCCESS; + +fail: + *errnop = -r; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; +} + +NSS_GETHOSTBYNAME_FALLBACKS(mymachines); + +enum nss_status _nss_mymachines_getpwnam_r( + const char *name, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _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; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(pwd); + + p = startswith(name, "vu-"); + if (!p) + goto not_found; + + e = strrchr(p, '-'); + 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; + + machine = strndupa(p, e - p); + if (!machine_name_is_valid(machine)) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "MapFromMachineUser", + &error, + &reply, + "su", + machine, (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &mapped); + if (r < 0) + goto fail; + + /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */ + if (mapped < HOST_UID_LIMIT || mapped == uid) + goto not_found; + + l = strlen(name); + if (buflen < l+1) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memcpy(buffer, name, l+1); + + pwd->pw_name = buffer; + pwd->pw_uid = mapped; + pwd->pw_gid = 65534; /* nobody */ + pwd->pw_gecos = buffer; + pwd->pw_passwd = (char*) "*"; /* locked */ + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_mymachines_getpwuid_r( + uid_t uid, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _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; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!uid_is_valid(uid)) { + r = -EINVAL; + goto fail; + } + + /* We consider all uids < 65536 host uids */ + if (uid < HOST_UID_LIMIT) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "MapToMachineUser", + &error, + &reply, + "u", + (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped); + if (r < 0) + goto fail; + + if (mapped == uid) + goto not_found; + + if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + pwd->pw_name = buffer; + pwd->pw_uid = uid; + pwd->pw_gid = 65534; /* nobody */ + pwd->pw_gecos = buffer; + pwd->pw_passwd = (char*) "*"; /* locked */ + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_mymachines_getgrnam_r( + const char *name, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _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; + size_t l; + int r; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(gr); + + p = startswith(name, "vg-"); + if (!p) + goto not_found; + + e = strrchr(p, '-'); + 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; + + machine = strndupa(p, e - p); + if (!machine_name_is_valid(machine)) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "MapFromMachineGroup", + &error, + &reply, + "su", + machine, (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &mapped); + if (r < 0) + goto fail; + + if (mapped < HOST_GID_LIMIT || mapped == gid) + goto not_found; + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), name); + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = gid; + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_mymachines_getgrgid_r( + gid_t gid, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _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; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!gid_is_valid(gid)) { + r = -EINVAL; + goto fail; + } + + /* We consider all gids < 65536 host gids */ + if (gid < HOST_GID_LIMIT) + goto not_found; + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fail; + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "MapToMachineGroup", + &error, + &reply, + "u", + (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) + goto not_found; + + goto fail; + } + + r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped); + if (r < 0) + goto fail; + + if (mapped == gid) + goto not_found; + + if (buflen < sizeof(char*) + 1) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { + *errnop = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = gid; + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + + *errnop = 0; + return NSS_STATUS_SUCCESS; + +not_found: + *errnop = 0; + return NSS_STATUS_NOTFOUND; + +fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} diff --git a/src/grp-machine/nss-mymachines/nss-mymachines.sym b/src/grp-machine/nss-mymachines/nss-mymachines.sym new file mode 100644 index 0000000000..0728ac3ba7 --- /dev/null +++ b/src/grp-machine/nss-mymachines/nss-mymachines.sym @@ -0,0 +1,21 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. +***/ + +{ +global: + _nss_mymachines_gethostbyname_r; + _nss_mymachines_gethostbyname2_r; + _nss_mymachines_gethostbyname3_r; + _nss_mymachines_gethostbyname4_r; + _nss_mymachines_getpwnam_r; + _nss_mymachines_getpwuid_r; + _nss_mymachines_getgrnam_r; + _nss_mymachines_getgrgid_r; +local: *; +}; diff --git a/src/grp-machine/nss-mymachines/nss-mymachines.xml b/src/grp-machine/nss-mymachines/nss-mymachines.xml new file mode 100644 index 0000000000..ec047449bf --- /dev/null +++ b/src/grp-machine/nss-mymachines/nss-mymachines.xml @@ -0,0 +1,113 @@ + + + + + + + + + nss-mymachines + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + nss-mymachines + 8 + + + + nss-mymachines + libnss_mymachines.so.2 + Provide hostname resolution for local + container instances. + + + + libnss_mymachines.so.2 + + + + Description + + nss-mymachines is a plug-in module for the GNU Name Service Switch (NSS) functionality of + the GNU C Library (glibc), providing hostname resolution for the names of containers running + locally that are registered with + systemd-machined.service8. The + container names are resolved to the IP addresses of the specific container, ordered by their scope. This + functionality only applies to containers using network namespacing. + + The module also resolves user and group IDs used by containers to user and group names indicating the + container name, and back. This functionality only applies to containers using user namespacing. + + To activate the NSS module, add mymachines to the lines starting with + hosts:, passwd: and group: in + /etc/nsswitch.conf. + + It is recommended to place mymachines after the files or + compat entry of the /etc/nsswitch.conf lines to make sure that its mappings + are preferred over other resolvers such as DNS, but so that /etc/hosts, + /etc/passwd and /etc/group based mappings take precedence. + + + + Example + + Here is an example /etc/nsswitch.conf file that enables + nss-mymachines correctly: + + passwd: compat mymachines +group: compat mymachines +shadow: compat + +hosts: files mymachines resolve myhostname +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis + + + + + See Also + + systemd1, + systemd-machined.service8, + nss-resolve8, + nss-myhostname8, + nsswitch.conf5, + getent1 + + + + diff --git a/src/grp-machine/systemd-machined/.gitignore b/src/grp-machine/systemd-machined/.gitignore new file mode 100644 index 0000000000..e1065b5894 --- /dev/null +++ b/src/grp-machine/systemd-machined/.gitignore @@ -0,0 +1 @@ +/org.freedesktop.machine1.policy diff --git a/src/grp-machine/systemd-machined/Makefile b/src/grp-machine/systemd-machined/Makefile new file mode 100644 index 0000000000..c717267ed8 --- /dev/null +++ b/src/grp-machine/systemd-machined/Makefile @@ -0,0 +1,101 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_machined_SOURCES = \ + src/machine/machined.c \ + src/machine/machined.h + +systemd_machined_LDADD = \ + libmachine-core.la + +rootlibexec_PROGRAMS += \ + systemd-machined + +libmachine_core_la_SOURCES = \ + src/machine/machine.c \ + src/machine/machine.h \ + src/machine/machined-dbus.c \ + src/machine/machine-dbus.c \ + src/machine/machine-dbus.h \ + src/machine/image-dbus.c \ + src/machine/image-dbus.h \ + src/machine/operation.c \ + src/machine/operation.h + +libmachine_core_la_LIBADD = \ + libsystemd-shared.la + +noinst_LTLIBRARIES += \ + libmachine-core.la + +test_machine_tables_SOURCES = \ + src/machine/test-machine-tables.c + +test_machine_tables_LDADD = \ + libmachine-core.la + +tests += \ + test-machine-tables + +nodist_systemunit_DATA += \ + units/systemd-machined.service + +dist_systemunit_DATA += \ + units/machine.slice + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.machine1.busname + +dist_dbussystemservice_DATA += \ + src/machine/org.freedesktop.machine1.service + +dist_dbuspolicy_DATA += \ + src/machine/org.freedesktop.machine1.conf + +polkitpolicy_files += \ + src/machine/org.freedesktop.machine1.policy + +SYSTEM_UNIT_ALIASES += \ + systemd-machined.service dbus-org.freedesktop.machine1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.machine1.busname + +polkitpolicy_in_files += \ + src/machine/org.freedesktop.machine1.policy.in + +EXTRA_DIST += \ + units/systemd-machined.service.in + +# ------------------------------------------------------------------------------ +ifneq ($(ENABLE_IMPORTD),) + +ifneq ($(HAVE_LIBCURL),) +ifneq ($(HAVE_XZ),) +ifneq ($(HAVE_ZLIB),) +ifneq ($(HAVE_BZIP2),) +ifneq ($(HAVE_GCRYPT),) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-machine/systemd-machined/image-dbus.c b/src/grp-machine/systemd-machined/image-dbus.c new file mode 100644 index 0000000000..6ab627a710 --- /dev/null +++ b/src/grp-machine/systemd-machined/image-dbus.c @@ -0,0 +1,423 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/process-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "shared/bus-util.h" +#include "shared/machine-image.h" + +#include "image-dbus.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType); + +int bus_image_method_remove( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + Image *image = userdata; + Manager *m = image->userdata; + pid_t child; + int r; + + assert(message); + assert(image); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-images", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_remove(image); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } + + errno_pipe_fd[0] = -1; + + return 1; +} + +int bus_image_method_rename( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Image *image = userdata; + Manager *m = image->userdata; + const char *new_name; + int r; + + assert(message); + assert(image); + + r = sd_bus_message_read(message, "s", &new_name); + if (r < 0) + return r; + + if (!image_name_is_valid(new_name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-images", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = image_rename(image, new_name); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_image_method_clone( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + Image *image = userdata; + Manager *m = image->userdata; + const char *new_name; + int r, read_only; + pid_t child; + + assert(message); + assert(image); + assert(m); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + + r = sd_bus_message_read(message, "sb", &new_name, &read_only); + if (r < 0) + return r; + + if (!image_name_is_valid(new_name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-images", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_clone(image, new_name, read_only); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } + + errno_pipe_fd[0] = -1; + + return 1; +} + +int bus_image_method_mark_read_only( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Image *image = userdata; + Manager *m = image->userdata; + int r, read_only; + + assert(message); + + r = sd_bus_message_read(message, "b", &read_only); + if (r < 0) + return r; + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-images", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = image_read_only(image, read_only); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_image_method_set_limit( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Image *image = userdata; + Manager *m = image->userdata; + uint64_t limit; + int r; + + assert(message); + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + if (!FILE_SIZE_VALID_OR_INFINITY(limit)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-images", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = image_set_limit(image, limit); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable image_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0), + SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0), + SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0), + SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0), + SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0), + SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0), + SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0), + SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), + SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), + SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +static int image_flush_cache(sd_event_source *s, void *userdata) { + Manager *m = userdata; + Image *i; + + assert(s); + assert(m); + + while ((i = hashmap_steal_first(m->image_cache))) + image_unref(i); + + return 0; +} + +int image_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; + Image *image = NULL; + const char *p; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + + p = startswith(path, "/org/freedesktop/machine1/image/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + image = hashmap_get(m->image_cache, e); + if (image) { + *found = image; + return 1; + } + + r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops); + if (r < 0) + return r; + + if (!m->image_cache_defer_event) { + r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m); + if (r < 0) + return r; + + r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + return r; + } + + r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT); + if (r < 0) + return r; + + r = image_find(e, &image); + if (r <= 0) + return r; + + image->userdata = m; + + r = hashmap_put(m->image_cache, image->name, image); + if (r < 0) { + image_unref(image); + return r; + } + + *found = image; + return 1; +} + +char *image_bus_path(const char *name) { + _cleanup_free_ char *e = NULL; + + assert(name); + + e = bus_label_escape(name); + if (!e) + return NULL; + + return strappend("/org/freedesktop/machine1/image/", e); +} + +int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + _cleanup_strv_free_ char **l = NULL; + Image *image; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + images = hashmap_new(&string_hash_ops); + if (!images) + return -ENOMEM; + + r = image_discover(images); + if (r < 0) + return r; + + HASHMAP_FOREACH(image, images, i) { + char *p; + + p = image_bus_path(image->name); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + *nodes = l; + l = NULL; + + return 1; +} diff --git a/src/grp-machine/systemd-machined/image-dbus.h b/src/grp-machine/systemd-machined/image-dbus.h new file mode 100644 index 0000000000..b62da996c6 --- /dev/null +++ b/src/grp-machine/systemd-machined/image-dbus.h @@ -0,0 +1,35 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "machined.h" + +extern const sd_bus_vtable image_vtable[]; + +char *image_bus_path(const char *name); + +int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); + +int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/grp-machine/systemd-machined/machine-dbus.c b/src/grp-machine/systemd-machined/machine-dbus.c new file mode 100644 index 0000000000..91b5dfa993 --- /dev/null +++ b/src/grp-machine/systemd-machined/machine-dbus.c @@ -0,0 +1,1473 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +/* 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 +#undef basename + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/copy.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/in-addr-util.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-internal.h" +#include "sd-netlink/local-addresses.h" +#include "shared/bus-util.h" + +#include "machine-dbus.h" +#include "machine.h" + +static int property_get_id( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Machine *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append_array(reply, 'y', &m->id, 16); +} + +static int property_get_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Machine *m = userdata; + const char *state; + int r; + + assert(bus); + assert(reply); + assert(m); + + state = machine_state_to_string(machine_get_state(m)); + + r = sd_bus_message_append_basic(reply, 's', state); + if (r < 0) + return r; + + return 1; +} + +static int property_get_netif( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Machine *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + assert_cc(sizeof(int) == sizeof(int32_t)); + + return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int)); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass); + +int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = machine_stop(m); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Machine *m = userdata; + const char *swho; + int32_t signo; + KillWho who; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "si", &swho, &signo); + if (r < 0) + return r; + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); + } + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + + r = bus_verify_polkit_async( + message, + CAP_KILL, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = machine_kill(m, who, signo); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(iay)"); + if (r < 0) + return r; + + switch (m->class) { + + case MACHINE_HOST: { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int n, i; + + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + return n; + + for (a = addresses, i = 0; i < n; a++, i++) { + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + break; + } + + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; + + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; + + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); + if (r < 0) + return r; + + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); + + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); + + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; + + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } + + pair[1] = safe_close(pair[1]); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + break; + } + + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_strv_free_ char **l = NULL; + Machine *m = userdata; + char **k, **v; + int r; + + assert(message); + assert(m); + + switch (m->class) { + + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; + + break; + + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + siginfo_t si; + pid_t child; + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int fd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } + + r = copy_bytes(fd, pair[1], (uint64_t) -1, false); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + f = fdopen(pair[0], "re"); + if (!f) + return -errno; + + pair[0] = -1; + + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + break; + } + + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, l) { + r = sd_bus_message_append(reply, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_close_ int master = -1; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_namespace(master, &pty_name); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) { + int r; + + assert(m); + assert(ret); + + switch (m->class) { + + case MACHINE_HOST: + *ret = NULL; + break; + + case MACHINE_CONTAINER: { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + char *address; + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r == -ENOENT) + return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *pty_name = 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; + const char *p, *getty; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_namespace(master, &pty_name); + if (r < 0) + 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); + + r = container_bus_new(m, error, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + getty = strjoina("container-getty@", p, ".service"); + + r = sd_bus_call_method( + container_bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, NULL, + "ss", getty, "replace"); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; + _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; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); + if (r < 0) + return r; + user = empty_to_null(user); + if (isempty(path)) + path = "/bin/sh"; + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + + r = sd_bus_message_read_strv(message, &args); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + if (!strv_env_is_valid(env)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_namespace(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + 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); + + r = container_bus_new(m, error, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + r = sd_bus_message_new_method_call( + container_bus, + &tm, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + /* Name and mode */ + unit = strjoina("container-shell@", p, ".service"); + r = sd_bus_message_append(tm, "ss", unit, "fail"); + if (r < 0) + return r; + + /* Properties */ + r = sd_bus_message_open_container(tm, 'a', "(sv)"); + if (r < 0) + return r; + + 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)", + "Description", "s", description, + "StandardInputFileDescriptor", "h", slave, + "StandardOutputFileDescriptor", "h", slave, + "StandardErrorFileDescriptor", "h", slave, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login", + "WorkingDirectory", "s", "-~"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); + if (r < 0) + return r; + + if (!strv_isempty(env)) { + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, env); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + } + + /* Exec container */ + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, args); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "b", true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + /* Auxiliary units */ + r = sd_bus_message_append(tm, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(container_bus, tm, 0, error, NULL); + if (r < 0) + return r; + + slave = safe_close(slave); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; + bool mount_slave_created = false, mount_slave_mounted = false, + mount_tmp_created = false, mount_tmp_mounted = false, + mount_outside_created = false, mount_outside_mounted = false; + const char *dest, *src; + Machine *m = userdata; + int read_only, make_directory; + pid_t child; + siginfo_t si; + int r; + + assert(message); + assert(m); + + if (m->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); + + r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory); + if (r < 0) + return r; + + if (!path_is_absolute(src) || !path_is_safe(src)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../."); + + if (isempty(dest)) + dest = src; + else if (!path_is_absolute(dest) || !path_is_safe(dest)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../."); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + /* One day, when bind mounting /proc/self/fd/n works across + * namespace boundaries we should rework this logic to make + * use of it... */ + + p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/"); + if (laccess(p, F_OK) < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points."); + + /* Our goal is to install a new bind mount into the container, + possibly read-only. This is irritatingly complex + unfortunately, currently. + + First, we start by creating a private playground in /tmp, + that we can mount MS_SLAVE. (Which is necessary, since + MS_MOVE cannot be applied to mounts with MS_SHARED parent + mounts.) */ + + if (!mkdtemp(mount_slave)) + return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave); + + mount_slave_created = true; + + if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave); + goto finish; + } + + mount_slave_mounted = true; + + if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave); + goto finish; + } + + /* Second, we mount the source directory to a directory inside + of our MS_SLAVE playground. */ + mount_tmp = strjoina(mount_slave, "/mount"); + if (mkdir(mount_tmp, 0700) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); + goto finish; + } + + mount_tmp_created = true; + + if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to overmount %s: %m", mount_tmp); + goto finish; + } + + mount_tmp_mounted = true; + + /* Third, we remount the new bind mount read-only if requested. */ + if (read_only) + if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp); + goto finish; + } + + /* Fourth, we move the new bind mount into the propagation + * directory. This way it will appear there read-only + * right-away. */ + + mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX"); + if (!mkdtemp(mount_outside)) { + r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside); + goto finish; + } + + mount_outside_created = true; + + if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside); + goto finish; + } + + mount_outside_mounted = true; + mount_tmp_mounted = false; + + (void) rmdir(mount_tmp); + mount_tmp_created = false; + + (void) umount(mount_slave); + mount_slave_mounted = false; + + (void) rmdir(mount_slave); + mount_slave_created = false; + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + goto finish; + } + + child = fork(); + if (child < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + goto finish; + } + + if (child == 0) { + const char *mount_inside; + int mntfd; + const char *q; + + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + q = procfs_file_alloca(m->leader, "ns/mnt"); + mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (mntfd < 0) { + r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); + goto child_fail; + } + + if (setns(mntfd, CLONE_NEWNS) < 0) { + r = log_error_errno(errno, "Failed to join namespace of leader: %m"); + goto child_fail; + } + + if (make_directory) + (void) mkdir_p(dest, 0755); + + /* Fifth, move the mount to the right place inside */ + mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside)); + if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) { + r = log_error_errno(errno, "Failed to mount: %m"); + goto child_fail; + } + + _exit(EXIT_SUCCESS); + + child_fail: + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + _exit(EXIT_FAILURE); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) { + r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + goto finish; + } + if (si.si_code != CLD_EXITED) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + goto finish; + } + if (si.si_status != EXIT_SUCCESS) { + + if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) + r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); + else + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed."); + goto finish; + } + + r = sd_bus_reply_method_return(message, NULL); + +finish: + if (mount_outside_mounted) + umount(mount_outside); + if (mount_outside_created) + rmdir(mount_outside); + + if (mount_tmp_mounted) + umount(mount_tmp); + if (mount_tmp_created) + rmdir(mount_tmp); + + if (mount_slave_mounted) + umount(mount_slave); + if (mount_slave_created) + rmdir(mount_slave); + + return r; +} + +int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + _cleanup_close_ int hostfd = -1; + Machine *m = userdata; + bool copy_from; + pid_t child; + char *t; + int r; + + assert(message); + assert(m); + + if (m->manager->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); + + if (m->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines."); + + r = sd_bus_message_read(message, "ss", &src, &dest); + if (r < 0) + return r; + + if (!path_is_absolute(src)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute."); + + if (isempty(dest)) + dest = src; + else if (!path_is_absolute(dest)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); + + if (copy_from) { + container_path = src; + host_path = dest; + } else { + host_path = src; + container_path = dest; + } + + host_basename = basename(host_path); + t = strdupa(host_path); + host_dirname = dirname(t); + + container_basename = basename(container_path); + t = strdupa(container_path); + container_dirname = dirname(t); + + hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY); + if (hostfd < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname); + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + int containerfd; + const char *q; + int mntfd; + + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + q = procfs_file_alloca(m->leader, "ns/mnt"); + mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (mntfd < 0) { + r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); + goto child_fail; + } + + if (setns(mntfd, CLONE_NEWNS) < 0) { + r = log_error_errno(errno, "Failed to join namespace of leader: %m"); + goto child_fail; + } + + containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY); + if (containerfd < 0) { + r = log_error_errno(errno, "Failed top open destination directory: %m"); + goto child_fail; + } + + if (copy_from) + r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true); + else + r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true); + + hostfd = safe_close(hostfd); + containerfd = safe_close(containerfd); + + if (r < 0) { + r = log_error_errno(r, "Failed to copy tree: %m"); + goto child_fail; + } + + _exit(EXIT_SUCCESS); + + child_fail: + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + /* Copying might take a while, hence install a watch on the child, and return */ + + r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } + errno_pipe_fd[0] = -1; + + return 1; +} + +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_close_ int fd = -1; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + switch (m->class) { + + case MACHINE_HOST: + fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + break; + + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + siginfo_t si; + pid_t child; + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int dfd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + _exit(EXIT_FAILURE); + + r = send_one_fd(pair[1], dfd, 0); + dfd = safe_close(dfd); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + fd = receive_one_fd(pair[0], MSG_DONTWAIT); + if (fd < 0) + return fd; + + break; + } + + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); + } + + return sd_bus_reply_method_return(message, "h", fd); +} + +const sd_bus_vtable machine_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + if (streq(path, "/org/freedesktop/machine1/machine/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + pid_t pid; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + r = manager_get_machine_by_pid(m, pid, &machine); + if (r <= 0) + return 0; + } else { + _cleanup_free_ char *e = NULL; + const char *p; + + p = startswith(path, "/org/freedesktop/machine1/machine/"); + if (!p) + return 0; + + e = bus_label_unescape(p); + if (!e) + return -ENOMEM; + + machine = hashmap_get(m->machines, e); + if (!machine) + return 0; + } + + *found = machine; + return 1; +} + +char *machine_bus_path(Machine *m) { + _cleanup_free_ char *e = NULL; + + assert(m); + + e = bus_label_escape(m->name); + if (!e) + return NULL; + + return strappend("/org/freedesktop/machine1/machine/", e); +} + +int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + Machine *machine = NULL; + Manager *m = userdata; + Iterator i; + int r; + + assert(bus); + assert(path); + assert(nodes); + + HASHMAP_FOREACH(machine, m->machines, i) { + char *p; + + p = machine_bus_path(machine); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + *nodes = l; + l = NULL; + + return 1; +} + +int machine_send_signal(Machine *m, bool new_machine) { + _cleanup_free_ char *p = NULL; + + assert(m); + + p = machine_bus_path(m); + if (!p) + return -ENOMEM; + + return sd_bus_emit_signal( + m->manager->bus, + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + new_machine ? "MachineNew" : "MachineRemoved", + "so", m->name, p); +} + +int machine_send_create_reply(Machine *m, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; + _cleanup_free_ char *p = NULL; + + assert(m); + + if (!m->create_message) + return 0; + + c = m->create_message; + m->create_message = NULL; + + if (error) + return sd_bus_reply_method_error(c, error); + + /* Update the machine state file before we notify the client + * about the result. */ + machine_save(m); + + p = machine_bus_path(m); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(c, "o", p); +} diff --git a/src/grp-machine/systemd-machined/machine-dbus.h b/src/grp-machine/systemd-machined/machine-dbus.h new file mode 100644 index 0000000000..d3faf5cb07 --- /dev/null +++ b/src/grp-machine/systemd-machined/machine-dbus.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "machine.h" + +extern const sd_bus_vtable machine_vtable[]; + +char *machine_bus_path(Machine *s); +int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); + +int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); + +int machine_send_signal(Machine *m, bool new_machine); +int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/grp-machine/systemd-machined/machine.c b/src/grp-machine/systemd-machined/machine.c new file mode 100644 index 0000000000..701fec0e67 --- /dev/null +++ b/src/grp-machine/systemd-machined/machine.c @@ -0,0 +1,631 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/hashmap.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "machine-dbus.h" +#include "machine.h" + +Machine* machine_new(Manager *manager, MachineClass class, const char *name) { + Machine *m; + + assert(manager); + assert(class < _MACHINE_CLASS_MAX); + assert(name); + + /* Passing class == _MACHINE_CLASS_INVALID here is fine. It + * means as much as "we don't know yet", and that we'll figure + * it out later when loading the state file. */ + + m = new0(Machine, 1); + if (!m) + return NULL; + + m->name = strdup(name); + if (!m->name) + goto fail; + + if (class != MACHINE_HOST) { + m->state_file = strappend("/run/systemd/machines/", m->name); + if (!m->state_file) + goto fail; + } + + m->class = class; + + if (hashmap_put(manager->machines, m->name, m) < 0) + goto fail; + + m->manager = manager; + + return m; + +fail: + free(m->state_file); + free(m->name); + free(m); + + return NULL; +} + +void machine_free(Machine *m) { + assert(m); + + while (m->operations) + operation_free(m->operations); + + if (m->in_gc_queue) + LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); + + machine_release_unit(m); + + free(m->scope_job); + + (void) hashmap_remove(m->manager->machines, m->name); + + if (m->manager->host_machine == m) + m->manager->host_machine = NULL; + + if (m->leader > 0) + (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); + + sd_bus_message_unref(m->create_message); + + free(m->name); + free(m->state_file); + free(m->service); + free(m->root_directory); + free(m->netif); + free(m); +} + +int machine_save(Machine *m) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + + if (!m->state_file) + return 0; + + if (!m->started) + return 0; + + r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0); + if (r < 0) + goto fail; + + r = fopen_temporary(m->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + (void) fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "NAME=%s\n", + m->name); + + if (m->unit) { + _cleanup_free_ char *escaped; + + escaped = cescape(m->unit); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + + fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */ + } + + if (m->scope_job) + fprintf(f, "SCOPE_JOB=%s\n", m->scope_job); + + if (m->service) { + _cleanup_free_ char *escaped; + + escaped = cescape(m->service); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + fprintf(f, "SERVICE=%s\n", escaped); + } + + if (m->root_directory) { + _cleanup_free_ char *escaped; + + escaped = cescape(m->root_directory); + if (!escaped) { + r = -ENOMEM; + goto fail; + } + fprintf(f, "ROOT=%s\n", escaped); + } + + if (!sd_id128_is_null(m->id)) + fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id)); + + if (m->leader != 0) + fprintf(f, "LEADER="PID_FMT"\n", m->leader); + + if (m->class != _MACHINE_CLASS_INVALID) + fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class)); + + if (dual_timestamp_is_set(&m->timestamp)) + fprintf(f, + "REALTIME="USEC_FMT"\n" + "MONOTONIC="USEC_FMT"\n", + m->timestamp.realtime, + m->timestamp.monotonic); + + if (m->n_netif > 0) { + unsigned i; + + fputs("NETIF=", f); + + for (i = 0; i < m->n_netif; i++) { + if (i != 0) + fputc(' ', f); + + fprintf(f, "%i", m->netif[i]); + } + + fputc('\n', f); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, m->state_file) < 0) { + r = -errno; + goto fail; + } + + if (m->unit) { + char *sl; + + /* Create a symlink from the unit name to the machine + * name, so that we can quickly find the machine for + * each given unit. Ignore error. */ + sl = strjoina("/run/systemd/machines/unit:", m->unit); + (void) symlink(m->name, sl); + } + + return 0; + +fail: + (void) unlink(m->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file); +} + +static void machine_unlink(Machine *m) { + assert(m); + + if (m->unit) { + + char *sl; + + sl = strjoina("/run/systemd/machines/unit:", m->unit); + (void) unlink(sl); + } + + if (m->state_file) + (void) unlink(m->state_file); +} + +int machine_load(Machine *m) { + _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL; + int r; + + assert(m); + + if (!m->state_file) + return 0; + + r = parse_env_file(m->state_file, NEWLINE, + "SCOPE", &m->unit, + "SCOPE_JOB", &m->scope_job, + "SERVICE", &m->service, + "ROOT", &m->root_directory, + "ID", &id, + "LEADER", &leader, + "CLASS", &class, + "REALTIME", &realtime, + "MONOTONIC", &monotonic, + "NETIF", &netif, + NULL); + if (r < 0) { + if (r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to read %s: %m", m->state_file); + } + + if (id) + sd_id128_from_string(id, &m->id); + + if (leader) + parse_pid(leader, &m->leader); + + if (class) { + MachineClass c; + + c = machine_class_from_string(class); + if (c >= 0) + m->class = c; + } + + if (realtime) + timestamp_deserialize(realtime, &m->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &m->timestamp.monotonic); + + if (netif) { + size_t allocated = 0, nr = 0; + const char *p; + int *ni = NULL; + + p = netif; + for (;;) { + _cleanup_free_ char *word = NULL; + int ifi; + + 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 (parse_ifindex(word, &ifi) < 0) + continue; + + if (!GREEDY_REALLOC(ni, allocated, nr+1)) { + free(ni); + return log_oom(); + } + + ni[nr++] = ifi; + } + + free(m->netif); + m->netif = ni; + m->n_netif = nr; + } + + return r; +} + +static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) { + int r = 0; + + assert(m); + assert(m->class != MACHINE_HOST); + + if (!m->unit) { + _cleanup_free_ char *escaped = NULL; + char *scope, *description, *job = NULL; + + escaped = unit_name_escape(m->name); + if (!escaped) + return log_oom(); + + scope = strjoin("machine-", escaped, ".scope", NULL); + if (!scope) + return log_oom(); + + description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name); + + r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job); + if (r < 0) { + log_error("Failed to start machine scope: %s", bus_error_message(error, r)); + free(scope); + return r; + } else { + m->unit = scope; + + free(m->scope_job); + m->scope_job = job; + } + } + + if (m->unit) + hashmap_put(m->manager->machine_units, m->unit, m); + + return r; +} + +int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { + int r; + + assert(m); + + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + + if (m->started) + return 0; + + r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); + if (r < 0) + return r; + + /* Create cgroup */ + r = machine_start_scope(m, properties, error); + if (r < 0) + return r; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START), + "NAME=%s", m->name, + "LEADER="PID_FMT, m->leader, + LOG_MESSAGE("New machine %s.", m->name), + NULL); + + if (!dual_timestamp_is_set(&m->timestamp)) + dual_timestamp_get(&m->timestamp); + + m->started = true; + + /* Save new machine data */ + machine_save(m); + + machine_send_signal(m, true); + + return 0; +} + +static int machine_stop_scope(Machine *m) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + char *job = NULL; + int r; + + assert(m); + assert(m->class != MACHINE_HOST); + + if (!m->unit) + return 0; + + r = manager_stop_unit(m->manager, m->unit, &error, &job); + if (r < 0) { + log_error("Failed to stop machine scope: %s", bus_error_message(&error, r)); + return r; + } + + free(m->scope_job); + m->scope_job = job; + + return 0; +} + +int machine_stop(Machine *m) { + int r; + assert(m); + + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + + r = machine_stop_scope(m); + + m->stopping = true; + + machine_save(m); + + return r; +} + +int machine_finalize(Machine *m) { + assert(m); + + if (m->started) + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP), + "NAME=%s", m->name, + "LEADER="PID_FMT, m->leader, + LOG_MESSAGE("Machine %s terminated.", m->name), + NULL); + + machine_unlink(m); + machine_add_to_gc_queue(m); + + if (m->started) { + machine_send_signal(m, false); + m->started = false; + } + + return 0; +} + +bool machine_check_gc(Machine *m, bool drop_not_started) { + assert(m); + + if (m->class == MACHINE_HOST) + return true; + + if (drop_not_started && !m->started) + return false; + + if (m->scope_job && manager_job_is_active(m->manager, m->scope_job)) + return true; + + if (m->unit && manager_unit_is_active(m->manager, m->unit)) + return true; + + return false; +} + +void machine_add_to_gc_queue(Machine *m) { + assert(m); + + if (m->in_gc_queue) + return; + + LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m); + m->in_gc_queue = true; +} + +MachineState machine_get_state(Machine *s) { + assert(s); + + if (s->class == MACHINE_HOST) + return MACHINE_RUNNING; + + if (s->stopping) + return MACHINE_CLOSING; + + if (s->scope_job) + return MACHINE_OPENING; + + return MACHINE_RUNNING; +} + +int machine_kill(Machine *m, KillWho who, int signo) { + assert(m); + + if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) + return -EOPNOTSUPP; + + if (!m->unit) + return -ESRCH; + + if (who == KILL_LEADER) { + /* If we shall simply kill the leader, do so directly */ + + if (kill(m->leader, signo) < 0) + return -errno; + + return 0; + } + + /* Otherwise, make PID 1 do it for us, for the entire cgroup */ + return manager_kill_unit(m->manager, m->unit, signo, NULL); +} + +int machine_openpt(Machine *m, int flags) { + assert(m); + + switch (m->class) { + + case MACHINE_HOST: { + int fd; + + fd = posix_openpt(flags); + if (fd < 0) + return -errno; + + if (unlockpt(fd) < 0) + return -errno; + + return fd; + } + + case MACHINE_CONTAINER: + if (m->leader <= 0) + return -EINVAL; + + return openpt_in_namespace(m->leader, flags); + + default: + return -EOPNOTSUPP; + } +} + +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; + } +} + +void machine_release_unit(Machine *m) { + assert(m); + + if (!m->unit) + return; + + (void) hashmap_remove(m->manager->machine_units, m->unit); + m->unit = mfree(m->unit); +} + +static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { + [MACHINE_CONTAINER] = "container", + [MACHINE_VM] = "vm", + [MACHINE_HOST] = "host", +}; + +DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); + +static const char* const machine_state_table[_MACHINE_STATE_MAX] = { + [MACHINE_OPENING] = "opening", + [MACHINE_RUNNING] = "running", + [MACHINE_CLOSING] = "closing" +}; + +DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_LEADER] = "leader", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/grp-machine/systemd-machined/machine.h b/src/grp-machine/systemd-machined/machine.h new file mode 100644 index 0000000000..e93f0cf222 --- /dev/null +++ b/src/grp-machine/systemd-machined/machine.h @@ -0,0 +1,111 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" + +typedef enum KillWho KillWho; +typedef struct Machine Machine; + +#include "machined.h" +#include "operation.h" + +typedef enum MachineState { + MACHINE_OPENING, /* Machine is being registered */ + MACHINE_RUNNING, /* Machine is running */ + MACHINE_CLOSING, /* Machine is terminating */ + _MACHINE_STATE_MAX, + _MACHINE_STATE_INVALID = -1 +} MachineState; + +typedef enum MachineClass { + MACHINE_CONTAINER, + MACHINE_VM, + MACHINE_HOST, + _MACHINE_CLASS_MAX, + _MACHINE_CLASS_INVALID = -1 +} MachineClass; + +enum KillWho { + KILL_LEADER, + KILL_ALL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +}; + +struct Machine { + Manager *manager; + + char *name; + sd_id128_t id; + + MachineClass class; + + char *state_file; + char *service; + char *root_directory; + + char *unit; + char *scope_job; + + pid_t leader; + + dual_timestamp timestamp; + + bool in_gc_queue:1; + bool started:1; + bool stopping:1; + + sd_bus_message *create_message; + + int *netif; + unsigned n_netif; + + LIST_HEAD(Operation, operations); + + LIST_FIELDS(Machine, gc_queue); +}; + +Machine* machine_new(Manager *manager, MachineClass class, const char *name); +void machine_free(Machine *m); +bool machine_check_gc(Machine *m, bool drop_not_started); +void machine_add_to_gc_queue(Machine *m); +int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error); +int machine_stop(Machine *m); +int machine_finalize(Machine *m); +int machine_save(Machine *m); +int machine_load(Machine *m); +int machine_kill(Machine *m, KillWho who, int signo); + +void machine_release_unit(Machine *m); + +MachineState machine_get_state(Machine *u); + +const char* machine_class_to_string(MachineClass t) _const_; +MachineClass machine_class_from_string(const char *s) _pure_; + +const char* machine_state_to_string(MachineState t) _const_; +MachineState machine_state_from_string(const char *s) _pure_; + +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; + +int machine_openpt(Machine *m, int flags); +int machine_open_terminal(Machine *m, const char *path, int mode); diff --git a/src/grp-machine/systemd-machined/machine.slice b/src/grp-machine/systemd-machined/machine.slice new file mode 100644 index 0000000000..3d40dfd73b --- /dev/null +++ b/src/grp-machine/systemd-machined/machine.slice @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Slice +Documentation=man:systemd.special(7) +Before=slices.target diff --git a/src/grp-machine/systemd-machined/machined-dbus.c b/src/grp-machine/systemd-machined/machined-dbus.c new file mode 100644 index 0000000000..349ffcf298 --- /dev/null +++ b/src/grp-machine/systemd-machined/machined-dbus.c @@ -0,0 +1,1805 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" +#include "shared/machine-image.h" +#include "shared/machine-pool.h" + +#include "image-dbus.h" +#include "machine-dbus.h" +#include "machined.h" + +static int property_get_pool_path( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", "/var/lib/machines"); +} + +static int property_get_pool_usage( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_close_ int fd = -1; + uint64_t usage = (uint64_t) -1; + struct stat st; + + assert(bus); + assert(reply); + + /* We try to read the quota info from /var/lib/machines, as + * well as the usage of the loopback file + * /var/lib/machines.raw, and pick the larger value. */ + + fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd >= 0) { + BtrfsQuotaInfo q; + + if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0) + usage = q.referenced; + } + + if (stat("/var/lib/machines.raw", &st) >= 0) { + if (usage == (uint64_t) -1 || st.st_blocks * 512ULL > usage) + usage = st.st_blocks * 512ULL; + } + + return sd_bus_message_append(reply, "t", usage); +} + +static int property_get_pool_limit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_close_ int fd = -1; + uint64_t size = (uint64_t) -1; + struct stat st; + + assert(bus); + assert(reply); + + /* We try to read the quota limit from /var/lib/machines, as + * well as the size of the loopback file + * /var/lib/machines.raw, and pick the smaller value. */ + + fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd >= 0) { + BtrfsQuotaInfo q; + + if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0) + size = q.referenced_max; + } + + if (stat("/var/lib/machines.raw", &st) >= 0) { + if (size == (uint64_t) -1 || (uint64_t) st.st_size < size) + size = st.st_size; + } + + return sd_bus_message_append(reply, "t", size); +} + +static int method_get_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + p = machine_bus_path(machine); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = image_find(name, NULL); + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + if (r < 0) + return r; + + p = image_bus_path(name); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + Machine *machine = NULL; + pid_t pid; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + } + + r = manager_get_machine_by_pid(m, pid, &machine); + if (r < 0) + return r; + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid); + + p = machine_bus_path(machine); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + +static int method_list_machines(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Machine *machine; + Iterator i; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + r = sd_bus_message_open_container(reply, 'a', "(ssso)"); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + HASHMAP_FOREACH(machine, m->machines, i) { + _cleanup_free_ char *p = NULL; + + p = machine_bus_path(machine); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(ssso)", + machine->name, + strempty(machine_class_to_string(machine->class)), + machine->service, + p); + if (r < 0) + return sd_bus_error_set_errno(error, r); + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, bool read_network, Machine **_m, sd_bus_error *error) { + const char *name, *service, *class, *root_directory; + const int32_t *netif = NULL; + MachineClass c; + uint32_t leader; + sd_id128_t id; + const void *v; + Machine *m; + size_t n, n_netif = 0; + int r; + + assert(manager); + assert(message); + assert(_m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + if (!machine_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name"); + + r = sd_bus_message_read_array(message, 'y', &v, &n); + if (r < 0) + return r; + if (n == 0) + id = SD_ID128_NULL; + else if (n == 16) + memcpy(&id, v, n); + else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter"); + + r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory); + if (r < 0) + return r; + + if (read_network) { + size_t i; + + r = sd_bus_message_read_array(message, 'i', (const void**) &netif, &n_netif); + if (r < 0) + return r; + + n_netif /= sizeof(int32_t); + + for (i = 0; i < n_netif; i++) { + if (netif[i] <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid network interface index %i", netif[i]); + } + } + + if (isempty(class)) + c = _MACHINE_CLASS_INVALID; + else { + c = machine_class_from_string(class); + if (c < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter"); + } + + if (leader == 1) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); + + if (!isempty(root_directory) && !path_is_absolute(root_directory)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path"); + + if (leader == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + assert_cc(sizeof(uint32_t) == sizeof(pid_t)); + + r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); + if (r < 0) + return r; + } + + if (hashmap_get(manager->machines, name)) + return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name); + + r = manager_add_machine(manager, name, &m); + if (r < 0) + return r; + + m->leader = leader; + m->class = c; + m->id = id; + + if (!isempty(service)) { + m->service = strdup(service); + if (!m->service) { + r = -ENOMEM; + goto fail; + } + } + + if (!isempty(root_directory)) { + m->root_directory = strdup(root_directory); + if (!m->root_directory) { + r = -ENOMEM; + goto fail; + } + } + + if (n_netif > 0) { + assert_cc(sizeof(int32_t) == sizeof(int)); + m->netif = memdup(netif, sizeof(int32_t) * n_netif); + if (!m->netif) { + r = -ENOMEM; + goto fail; + } + + m->n_netif = n_netif; + } + + *_m = m; + + return 1; + +fail: + machine_add_to_gc_queue(m); + return r; +} + +static int method_create_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) { + Manager *manager = userdata; + Machine *m = NULL; + int r; + + assert(message); + assert(manager); + + r = method_create_or_register_machine(manager, message, read_network, &m, error); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + goto fail; + + r = machine_start(m, message, error); + if (r < 0) + goto fail; + + m->create_message = sd_bus_message_ref(message); + return 1; + +fail: + machine_add_to_gc_queue(m); + return r; +} + +static int method_create_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_create_machine_internal(message, true, userdata, error); +} + +static int method_create_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_create_machine_internal(message, false, userdata, error); +} + +static int method_register_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) { + Manager *manager = userdata; + _cleanup_free_ char *p = NULL; + Machine *m = NULL; + int r; + + assert(message); + assert(manager); + + r = method_create_or_register_machine(manager, message, read_network, &m, error); + if (r < 0) + return r; + + r = cg_pid_get_unit(m->leader, &m->unit); + if (r < 0) { + r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r)); + goto fail; + } + + r = machine_start(m, NULL, error); + if (r < 0) + goto fail; + + p = machine_bus_path(m); + if (!p) { + r = -ENOMEM; + goto fail; + } + + return sd_bus_reply_method_return(message, "o", p); + +fail: + machine_add_to_gc_queue(m); + return r; +} + +static int method_register_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_register_machine_internal(message, true, userdata, error); +} + +static int method_register_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_register_machine_internal(message, false, userdata, error); +} + +static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_terminate(message, machine, error); +} + +static int method_kill_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_kill(message, machine, error); +} + +static int method_get_machine_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_get_addresses(message, machine, error); +} + +static int method_get_machine_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_get_os_release(message, machine, error); +} + +static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + Manager *m = userdata; + Image *image; + Iterator i; + int r; + + assert(message); + assert(m); + + images = hashmap_new(&string_hash_ops); + if (!images) + return -ENOMEM; + + r = image_discover(images); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssbttto)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(image, images, i) { + _cleanup_free_ char *p = NULL; + + p = image_bus_path(image->name); + if (!p) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(ssbttto)", + image->name, + image_type_to_string(image->type), + image->read_only, + image->crtime, + image->mtime, + image->usage, + p); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_open_machine_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_pty(message, machine, error); +} + +static int method_open_machine_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_login(message, machine, error); +} + +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + +static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_bind_mount(message, machine, error); +} + +static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_copy(message, machine, error); +} + +static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_root_directory(message, machine, error); +} + +static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image* i = NULL; + const char *name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!image_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); + + r = image_find(name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + + i->userdata = userdata; + return bus_image_method_remove(message, i, error); +} + +static int method_rename_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image* i = NULL; + const char *old_name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &old_name); + if (r < 0) + return r; + + if (!image_name_is_valid(old_name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name); + + r = image_find(old_name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name); + + i->userdata = userdata; + return bus_image_method_rename(message, i, error); +} + +static int method_clone_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image *i = NULL; + const char *old_name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &old_name); + if (r < 0) + return r; + + if (!image_name_is_valid(old_name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name); + + r = image_find(old_name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name); + + i->userdata = userdata; + return bus_image_method_clone(message, i, error); +} + +static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image *i = NULL; + const char *name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!image_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); + + r = image_find(name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + + i->userdata = userdata; + return bus_image_method_mark_read_only(message, i, error); +} + +static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_fclose_ FILE *f = NULL; + bool success; + size_t n; + int r; + + assert(operation); + assert(operation->extra_fd >= 0); + + if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1) + return -errno; + + f = fdopen(operation->extra_fd, "re"); + if (!f) + return -errno; + + operation->extra_fd = -1; + + /* The resulting temporary file starts with a boolean value that indicates success or not. */ + errno = 0; + n = fread(&success, 1, sizeof(success), f); + if (n != sizeof(success)) + return ret < 0 ? ret : (errno != 0 ? -errno : -EIO); + + if (ret < 0) { + _cleanup_free_ char *name = NULL; + + /* The clean-up operation failed. In this case the resulting temporary file should contain a boolean + * set to false followed by the name of the failed image. Let's try to read this and use it for the + * error message. If we can't read it, don't mind, and return the naked error. */ + + if (success) /* The resulting temporary file could not be updated, ignore it. */ + return ret; + + r = read_nul_string(f, &name); + if (r < 0 || isempty(name)) /* Same here... */ + return ret; + + return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name); + } + + assert(success); + + r = sd_bus_message_new_method_return(operation->message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + /* On success the resulting temporary file will contain a list of image names that were removed followed by + * their size on disk. Let's read that and turn it into a bus message. */ + for (;;) { + _cleanup_free_ char *name = NULL; + uint64_t size; + + r = read_nul_string(f, &name); + if (r < 0) + return r; + if (isempty(name)) /* reached the end */ + break; + + errno = 0; + n = fread(&size, 1, sizeof(size), f); + if (n != sizeof(size)) + return errno != 0 ? -errno : -EIO; + + r = sd_bus_message_append(reply, "(st)", name, size); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) { + enum { + REMOVE_ALL, + REMOVE_HIDDEN, + } mode; + + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; + _cleanup_close_ int result_fd = -1; + Manager *m = userdata; + Operation *operation; + const char *mm; + pid_t child; + int r; + + assert(message); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + + r = sd_bus_message_read(message, "s", &mm); + if (r < 0) + return r; + + if (streq(mm, "all")) + mode = REMOVE_ALL; + else if (streq(mm, "hidden")) + mode = REMOVE_HIDDEN; + else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this + * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this + * continuously */ + result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC); + if (result_fd < 0) + return -errno; + + /* This might be a slow operation, run it asynchronously in a background process */ + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + bool success = true; + Image *image; + Iterator i; + ssize_t l; + + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + images = hashmap_new(&string_hash_ops); + if (!images) { + r = -ENOMEM; + goto child_fail; + } + + r = image_discover(images); + if (r < 0) + goto child_fail; + + l = write(result_fd, &success, sizeof(success)); + if (l < 0) { + r = -errno; + goto child_fail; + } + + HASHMAP_FOREACH(image, images, i) { + + /* We can't remove vendor images (i.e. those in /usr) */ + if (IMAGE_IS_VENDOR(image)) + continue; + + if (IMAGE_IS_HOST(image)) + continue; + + if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) + continue; + + r = image_remove(image); + if (r == -EBUSY) /* keep images that are currently being used. */ + continue; + if (r < 0) { + /* If the operation failed, let's override everything we wrote, and instead write there at which image we failed. */ + success = false; + (void) ftruncate(result_fd, 0); + (void) lseek(result_fd, 0, SEEK_SET); + (void) write(result_fd, &success, sizeof(success)); + (void) write(result_fd, image->name, strlen(image->name)+1); + goto child_fail; + } + + l = write(result_fd, image->name, strlen(image->name)+1); + if (l < 0) { + r = -errno; + goto child_fail; + } + + l = write(result_fd, &image->usage_exclusive, sizeof(image->usage_exclusive)); + if (l < 0) { + r = -errno; + goto child_fail; + } + } + + result_fd = safe_close(result_fd); + _exit(EXIT_SUCCESS); + + child_fail: + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + /* The clean-up might take a while, hence install a watch on the child and return */ + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0], &operation); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } + + operation->extra_fd = result_fd; + operation->done = clean_pool_done; + + result_fd = -1; + errno_pipe_fd[0] = -1; + + return 1; +} + +static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint64_t limit; + int r; + + assert(message); + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + if (!FILE_SIZE_VALID_OR_INFINITY(limit)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + /* Set up the machine directory if necessary */ + r = setup_machine_directory(limit, error); + if (r < 0) + return r; + + /* Resize the backing loopback device, if there is one, except if we asked to drop any limit */ + if (limit != (uint64_t) -1) { + r = btrfs_resize_loopback("/var/lib/machines", limit, false); + if (r == -ENOTTY) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); + if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */ + return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m"); + } + + (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) + return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m"); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(image_unrefp) Image *i = NULL; + const char *name; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!image_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); + + r = image_find(name, &i); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + + i->userdata = userdata; + return bus_image_method_set_limit(message, i, error); +} + +static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + const char *name, *p; + Machine *machine; + uint32_t uid; + int r; + + r = sd_bus_message_read(message, "su", &name, &uid); + if (r < 0) + return r; + + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + + p = procfs_file_alloca(machine->leader, "uid_map"); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno > 0) + return -errno; + + return -EIO; + } + + if (uid < uid_base || uid >= uid_base + uid_range) + continue; + + converted = uid - uid_base + uid_shift; + if (!uid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); +} + +static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + uid_t uid; + Iterator i; + int r; + + r = sd_bus_message_read(message, "u", &uid); + if (r < 0) + return r; + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + if (uid < 0x10000) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid); + + HASHMAP_FOREACH(machine, m->machines, i) { + _cleanup_fclose_ FILE *f = NULL; + char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + + if (machine->class != MACHINE_CONTAINER) + continue; + + xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); + f = fopen(p, "re"); + if (!f) { + log_warning_errno(errno, "Failed top open %s, ignoring,", p); + continue; + } + + for (;;) { + _cleanup_free_ char *o = NULL; + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno > 0) + return -errno; + + return -EIO; + } + + if (uid < uid_shift || uid >= uid_shift + uid_range) + continue; + + converted = (uid - uid_shift + uid_base); + if (!uid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); + + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); + } + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); +} + +static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { + _cleanup_fclose_ FILE *f = NULL; + Manager *m = groupdata; + const char *name, *p; + Machine *machine; + uint32_t gid; + int r; + + r = sd_bus_message_read(message, "su", &name, &gid); + if (r < 0) + return r; + + if (!gid_is_valid(gid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + + p = procfs_file_alloca(machine->leader, "gid_map"); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + gid_t gid_base, gid_shift, gid_range, converted; + int k; + + errno = 0; + k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno > 0) + return -errno; + + return -EIO; + } + + if (gid < gid_base || gid >= gid_base + gid_range) + continue; + + converted = gid - gid_base + gid_shift; + if (!gid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name); +} + +static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { + Manager *m = groupdata; + Machine *machine; + gid_t gid; + Iterator i; + int r; + + r = sd_bus_message_read(message, "u", &gid); + if (r < 0) + return r; + if (!gid_is_valid(gid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + if (gid < 0x10000) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid); + + HASHMAP_FOREACH(machine, m->machines, i) { + _cleanup_fclose_ FILE *f = NULL; + char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + + if (machine->class != MACHINE_CONTAINER) + continue; + + xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); + f = fopen(p, "re"); + if (!f) { + log_warning_errno(errno, "Failed top open %s, ignoring,", p); + continue; + } + + for (;;) { + _cleanup_free_ char *o = NULL; + gid_t gid_base, gid_shift, gid_range, converted; + int k; + + errno = 0; + k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f) && errno > 0) + return -errno; + + return -EIO; + } + + if (gid < gid_shift || gid >= gid_shift + gid_range) + continue; + + converted = (gid - gid_shift + gid_base); + if (!gid_is_valid(converted)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); + + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); + } + } + + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); +} + +const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0), + SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0), + SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0), + SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0), + SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0), + SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), + SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0), + SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), + SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_SIGNAL("MachineNew", "so", 0), + SD_BUS_SIGNAL("MachineRemoved", "so", 0), + SD_BUS_VTABLE_END +}; + +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *result, *unit; + Manager *m = userdata; + Machine *machine; + uint32_t id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + machine = hashmap_get(m->machine_units, unit); + if (!machine) + return 0; + + if (streq_ptr(path, machine->scope_job)) { + machine->scope_job = mfree(machine->scope_job); + + if (machine->started) { + if (streq(result, "done")) + machine_send_create_reply(machine, NULL); + else { + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + + sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); + + machine_send_create_reply(machine, &e); + } + } + + machine_save(machine); + } + + machine_add_to_gc_queue(machine); + return 0; +} + +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *unit = NULL; + const char *path; + Manager *m = userdata; + Machine *machine; + int r; + + assert(message); + assert(m); + + path = sd_bus_message_get_path(message); + if (!path) + return 0; + + r = unit_name_from_dbus_path(path, &unit); + if (r == -EINVAL) /* not for a unit */ + return 0; + if (r < 0) { + log_oom(); + return 0; + } + + machine = hashmap_get(m->machine_units, unit); + if (!machine) + return 0; + + machine_add_to_gc_queue(machine); + return 0; +} + +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *path, *unit; + Manager *m = userdata; + Machine *machine; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "so", &unit, &path); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + machine = hashmap_get(m->machine_units, unit); + if (!machine) + return 0; + + machine_add_to_gc_queue(machine); + return 0; +} + +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + Iterator i; + int b, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + if (b) + return 0; + + /* systemd finished reloading, let's recheck all our machines */ + log_debug("System manager has been reloaded, rechecking machines..."); + + HASHMAP_FOREACH(machine, m->machines, i) + machine_add_to_gc_queue(machine); + + return 0; +} + +int manager_start_scope( + Manager *manager, + const char *scope, + pid_t pid, + const char *slice, + const char *description, + sd_bus_message *more_properties, + sd_bus_error *error, + char **job) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(scope); + assert(pid > 1); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(slice)) { + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return r; + } + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "Delegate", "b", 1); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384)); + if (r < 0) + return bus_log_create_error(r); + + if (more_properties) { + r = sd_bus_message_copy(m, more_properties, true); + 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; + + 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; +} + +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(unit); + + r = sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + error, + &reply, + "ss", unit, "fail"); + if (r < 0) { + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { + + if (job) + *job = NULL; + + sd_bus_error_free(error); + return 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; +} + +int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) { + assert(manager); + assert(unit); + + return sd_bus_call_method( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + error, + NULL, + "ssi", unit, "all", signo); +} + +int manager_unit_is_active(Manager *manager, const char *unit) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *path = NULL; + const char *state; + int r; + + assert(manager); + assert(unit); + + path = unit_dbus_path_from_name(unit); + if (!path) + return -ENOMEM; + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", + &error, + &reply, + "s"); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || + sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) + return false; + + return r; + } + + r = sd_bus_message_read(reply, "s", &state); + if (r < 0) + return -EINVAL; + + return !STR_IN_SET(state, "inactive", "failed"); +} + +int manager_job_is_active(Manager *manager, const char *path) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(manager); + assert(path); + + r = sd_bus_get_property( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Job", + "State", + &error, + &reply, + "s"); + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) + return true; + + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) + return false; + + return r; + } + + /* We don't actually care about the state really. The fact + * that we could read the job state is enough for us */ + + return true; +} + +int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { + Machine *mm; + int r; + + assert(m); + assert(pid >= 1); + assert(machine); + + mm = hashmap_get(m->machine_leaders, PID_TO_PTR(pid)); + if (!mm) { + _cleanup_free_ char *unit = NULL; + + r = cg_pid_get_unit(pid, &unit); + if (r >= 0) + mm = hashmap_get(m->machine_units, unit); + } + if (!mm) + return 0; + + *machine = mm; + return 1; +} + +int manager_add_machine(Manager *m, const char *name, Machine **_machine) { + Machine *machine; + + assert(m); + assert(name); + + machine = hashmap_get(m->machines, name); + if (!machine) { + machine = machine_new(m, _MACHINE_CLASS_INVALID, name); + if (!machine) + return -ENOMEM; + } + + if (_machine) + *_machine = machine; + + return 0; +} diff --git a/src/grp-machine/systemd-machined/machined.c b/src/grp-machine/systemd-machined/machined.c new file mode 100644 index 0000000000..a01d1820a1 --- /dev/null +++ b/src/grp-machine/systemd-machined/machined.c @@ -0,0 +1,416 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/hostname-util.h" +#include "basic/label.h" +#include "basic/signal-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/machine-image.h" + +#include "machined.h" + +Manager *manager_new(void) { + Manager *m; + int r; + + m = new0(Manager, 1); + if (!m) + return NULL; + + m->machines = hashmap_new(&string_hash_ops); + m->machine_units = hashmap_new(&string_hash_ops); + m->machine_leaders = hashmap_new(NULL); + + if (!m->machines || !m->machine_units || !m->machine_leaders) { + manager_free(m); + return NULL; + } + + r = sd_event_default(&m->event); + if (r < 0) { + manager_free(m); + return NULL; + } + + sd_event_set_watchdog(m->event, true); + + return m; +} + +void manager_free(Manager *m) { + Machine *machine; + Image *i; + + assert(m); + + while (m->operations) + operation_free(m->operations); + + assert(m->n_operations == 0); + + while ((machine = hashmap_first(m->machines))) + machine_free(machine); + + hashmap_free(m->machines); + hashmap_free(m->machine_units); + hashmap_free(m->machine_leaders); + + while ((i = hashmap_steal_first(m->image_cache))) + image_unref(i); + + hashmap_free(m->image_cache); + + sd_event_source_unref(m->image_cache_defer_event); + + bus_verify_polkit_async_registry_free(m->polkit_registry); + + sd_bus_unref(m->bus); + sd_event_unref(m->event); + + free(m); +} + +static int manager_add_host_machine(Manager *m) { + _cleanup_free_ char *rd = NULL, *unit = NULL; + sd_id128_t mid; + Machine *t; + int r; + + if (m->host_machine) + return 0; + + r = sd_id128_get_machine(&mid); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + rd = strdup("/"); + if (!rd) + return log_oom(); + + unit = strdup("-.slice"); + if (!unit) + return log_oom(); + + t = machine_new(m, MACHINE_HOST, ".host"); + if (!t) + return log_oom(); + + t->leader = 1; + t->id = mid; + + t->root_directory = rd; + t->unit = unit; + rd = unit = NULL; + + dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + + m->host_machine = t; + + return 0; +} + +int manager_enumerate_machines(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + r = manager_add_host_machine(m); + if (r < 0) + return r; + + /* Read in machine data stored on disk */ + d = opendir("/run/systemd/machines"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/machines: %m"); + } + + FOREACH_DIRENT(de, d, return -errno) { + struct Machine *machine; + int k; + + if (!dirent_is_file(de)) + continue; + + /* Ignore symlinks that map the unit name to the machine */ + if (startswith(de->d_name, "unit:")) + continue; + + if (!machine_name_is_valid(de->d_name)) + continue; + + k = manager_add_machine(m, de->d_name, &machine); + if (k < 0) { + r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); + continue; + } + + machine_add_to_gc_queue(machine); + + k = machine_load(machine); + if (k < 0) + r = k; + } + + return r; +} + +static int manager_connect_bus(Manager *m) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(m); + assert(!m->bus); + + r = sd_bus_default_system(&m->bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); + + r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m); + if (r < 0) + return log_error_errno(r, "Failed to add manager object vtable: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add machine object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add machine enumerator: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add image object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add image enumerator: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + match_job_removed, + m); + if (r < 0) + return log_error_errno(r, "Failed to add match for JobRemoved: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='UnitRemoved'," + "path='/org/freedesktop/systemd1'", + match_unit_removed, + m); + if (r < 0) + return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'," + "arg0='org.freedesktop.systemd1.Unit'", + match_properties_changed, + m); + if (r < 0) + return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); + + r = sd_bus_add_match(m->bus, + NULL, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='Reloading'," + "path='/org/freedesktop/systemd1'", + match_reloading, + m); + if (r < 0) + return log_error_errno(r, "Failed to add match for Reloading: %m"); + + r = sd_bus_call_method( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Subscribe", + &error, + NULL, NULL); + if (r < 0) { + log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(m->bus, m->event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + return 0; +} + +void manager_gc(Manager *m, bool drop_not_started) { + Machine *machine; + + assert(m); + + while ((machine = m->machine_gc_queue)) { + LIST_REMOVE(gc_queue, m->machine_gc_queue, machine); + machine->in_gc_queue = false; + + /* First, if we are not closing yet, initiate stopping */ + if (!machine_check_gc(machine, drop_not_started) && + machine_get_state(machine) != MACHINE_CLOSING) + machine_stop(machine); + + /* Now, the stop probably made this referenced + * again, but if it didn't, then it's time to let it + * go entirely. */ + if (!machine_check_gc(machine, drop_not_started)) { + machine_finalize(machine); + machine_free(machine); + } + } +} + +int manager_startup(Manager *m) { + Machine *machine; + Iterator i; + int r; + + assert(m); + + /* Connect to the bus */ + r = manager_connect_bus(m); + if (r < 0) + return r; + + /* Deserialize state */ + manager_enumerate_machines(m); + + /* Remove stale objects before we start them */ + manager_gc(m, false); + + /* And start everything */ + HASHMAP_FOREACH(machine, m->machines, i) + machine_start(machine, NULL, NULL); + + return 0; +} + +static bool check_idle(void *userdata) { + Manager *m = userdata; + + if (m->operations) + return false; + + manager_gc(m, true); + + return hashmap_isempty(m->machines); +} + +int manager_run(Manager *m) { + assert(m); + + return bus_event_loop_with_idle( + m->event, + m->bus, + "org.freedesktop.machine1", + DEFAULT_EXIT_USEC, + check_idle, m); +} + +int main(int argc, char *argv[]) { + Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_set_facility(LOG_AUTH); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + /* Always create the directories people can create inotify + * watches in. Note that some applications might check for the + * existence of /run/systemd/machines/ to determine whether + * machined is available, so please always make sure this + * check stays in. */ + mkdir_label("/run/systemd/machines", 0755); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); + + m = manager_new(); + if (!m) { + r = log_oom(); + goto finish; + } + + r = manager_startup(m); + if (r < 0) { + log_error_errno(r, "Failed to fully start up daemon: %m"); + goto finish; + } + + log_debug("systemd-machined running as pid "PID_FMT, getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + r = manager_run(m); + + log_debug("systemd-machined stopped as pid "PID_FMT, getpid()); + +finish: + manager_free(m); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-machine/systemd-machined/machined.h b/src/grp-machine/systemd-machined/machined.h new file mode 100644 index 0000000000..b16ea3ef15 --- /dev/null +++ b/src/grp-machine/systemd-machined/machined.h @@ -0,0 +1,82 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include + +#include "basic/hashmap.h" +#include "basic/list.h" + +typedef struct Manager Manager; + +#include "image-dbus.h" +#include "machine-dbus.h" +#include "machine.h" +#include "operation.h" + +struct Manager { + sd_event *event; + sd_bus *bus; + + Hashmap *machines; + Hashmap *machine_units; + Hashmap *machine_leaders; + + Hashmap *polkit_registry; + + Hashmap *image_cache; + sd_event_source *image_cache_defer_event; + + LIST_HEAD(Machine, machine_gc_queue); + + Machine *host_machine; + + LIST_HEAD(Operation, operations); + unsigned n_operations; +}; + +Manager *manager_new(void); +void manager_free(Manager *m); + +int manager_add_machine(Manager *m, const char *name, Machine **_machine); +int manager_enumerate_machines(Manager *m); + +int manager_startup(Manager *m); +int manager_run(Manager *m); + +void manager_gc(Manager *m, bool drop_not_started); + +int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine); + +extern const sd_bus_vtable manager_vtable[]; + +int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); +int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); + +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, sd_bus_message *more_properties, sd_bus_error *error, char **job); +int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); +int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error); +int manager_unit_is_active(Manager *manager, const char *unit); +int manager_job_is_active(Manager *manager, const char *path); diff --git a/src/grp-machine/systemd-machined/operation.c b/src/grp-machine/systemd-machined/operation.c new file mode 100644 index 0000000000..f16c38c47a --- /dev/null +++ b/src/grp-machine/systemd-machined/operation.c @@ -0,0 +1,153 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/process-util.h" + +#include "operation.h" + +static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Operation *o = userdata; + int r; + + assert(o); + assert(si); + + log_debug("Operating " PID_FMT " is now complete with code=%s status=%i", + o->pid, + sigchld_code_to_string(si->si_code), si->si_status); + + o->pid = 0; + + if (si->si_code != CLD_EXITED) { + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + goto fail; + } + + if (si->si_status == EXIT_SUCCESS) + r = 0; + else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */ + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); + goto fail; + } + + if (o->done) { + /* A completion routine is set for this operation, call it. */ + r = o->done(o, r, &error); + if (r < 0) { + if (!sd_bus_error_is_set(&error)) + sd_bus_error_set_errno(&error, r); + + goto fail; + } + + } else { + /* The default operation when done is to simply return an error on failure or an empty success + * message on success. */ + if (r < 0) + goto fail; + + r = sd_bus_reply_method_return(o->message, NULL); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + } + + operation_free(o); + return 0; + +fail: + r = sd_bus_reply_method_error(o->message, &error); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; +} + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) { + Operation *o; + int r; + + assert(manager); + assert(child > 1); + assert(message); + assert(errno_fd >= 0); + + o = new0(Operation, 1); + if (!o) + return -ENOMEM; + + o->extra_fd = -1; + + r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); + if (r < 0) { + free(o); + return r; + } + + o->pid = child; + o->message = sd_bus_message_ref(message); + o->errno_fd = errno_fd; + + LIST_PREPEND(operations, manager->operations, o); + manager->n_operations++; + o->manager = manager; + + if (machine) { + LIST_PREPEND(operations_by_machine, machine->operations, o); + o->machine = machine; + } + + log_debug("Started new operation " PID_FMT ".", child); + + /* At this point we took ownership of both the child and the errno file descriptor! */ + + if (ret) + *ret = o; + + return 0; +} + +Operation *operation_free(Operation *o) { + if (!o) + return NULL; + + sd_event_source_unref(o->event_source); + + safe_close(o->errno_fd); + safe_close(o->extra_fd); + + if (o->pid > 1) + (void) sigkill_wait(o->pid); + + sd_bus_message_unref(o->message); + + if (o->manager) { + LIST_REMOVE(operations, o->manager->operations, o); + o->manager->n_operations--; + } + + if (o->machine) + LIST_REMOVE(operations_by_machine, o->machine->operations, o); + + free(o); + return NULL; +} diff --git a/src/grp-machine/systemd-machined/operation.h b/src/grp-machine/systemd-machined/operation.h new file mode 100644 index 0000000000..713ffe88c4 --- /dev/null +++ b/src/grp-machine/systemd-machined/operation.h @@ -0,0 +1,49 @@ +#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 . +***/ + +#include + +#include +#include + +#include "basic/list.h" + +typedef struct Operation Operation; + +#include "machined.h" + +#define OPERATIONS_MAX 64 + +struct Operation { + Manager *manager; + Machine *machine; + pid_t pid; + sd_bus_message *message; + int errno_fd; + int extra_fd; + sd_event_source *event_source; + int (*done)(Operation *o, int ret, sd_bus_error *error); + LIST_FIELDS(Operation, operations); + LIST_FIELDS(Operation, operations_by_machine); +}; + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret); +Operation *operation_free(Operation *o); diff --git a/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf b/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf new file mode 100644 index 0000000000..562b9d3cc0 --- /dev/null +++ b/src/grp-machine/systemd-machined/org.freedesktop.machine1.conf @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-machine/systemd-machined/org.freedesktop.machine1.policy.in b/src/grp-machine/systemd-machined/org.freedesktop.machine1.policy.in new file mode 100644 index 0000000000..69f78a5c25 --- /dev/null +++ b/src/grp-machine/systemd-machined/org.freedesktop.machine1.policy.in @@ -0,0 +1,102 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Log into a local container + <_message>Authentication is required to log into a local container. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Log into the local host + <_message>Authentication is required to log into the local host. + + auth_admin + auth_admin + yes + + + + + <_description>Acquire a shell in a local container + <_message>Authentication is required to acquire a shell in a local container. + + auth_admin + auth_admin + auth_admin_keep + + org.freedesktop.login1.login + + + + <_description>Acquire a shell on the local host + <_message>Authentication is required to acquire a shell on the local host. + + auth_admin + auth_admin + auth_admin_keep + + org.freedesktop.login1.host-login + + + + <_description>Acquire a pseudo TTY in a local container + <_message>Authentication is required to acquire a pseudo TTY in a local container. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Acquire a pseudo TTY on the local host + <_message>Authentication is required to acquire a pseudo TTY on the local host. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Manage local virtual machines and containers + <_message>Authentication is required to manage local virtual machines and containers. + + auth_admin + auth_admin + auth_admin_keep + + org.freedesktop.login1.shell org.freedesktop.login1.login + + + + <_description>Manage local virtual machine and container images + <_message>Authentication is required to manage local virtual machine and container images. + + auth_admin + auth_admin + auth_admin_keep + + + + diff --git a/src/grp-machine/systemd-machined/org.freedesktop.machine1.service b/src/grp-machine/systemd-machined/org.freedesktop.machine1.service new file mode 100644 index 0000000000..d3dc99852b --- /dev/null +++ b/src/grp-machine/systemd-machined/org.freedesktop.machine1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.machine1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.machine1.service diff --git a/src/grp-machine/systemd-machined/systemd-machined.service.in b/src/grp-machine/systemd-machined/systemd-machined.service.in new file mode 100644 index 0000000000..dcf9f347b7 --- /dev/null +++ b/src/grp-machine/systemd-machined/systemd-machined.service.in @@ -0,0 +1,25 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Virtual Machine and Container Registration Service +Documentation=man:systemd-machined.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/machined +Wants=machine.slice +After=machine.slice + +[Service] +ExecStart=@rootlibexecdir@/systemd-machined +BusName=org.freedesktop.machine1 +CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD +WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io + +# Note that machined cannot be placed in a mount namespace, since it +# needs access to the host's mount namespace in order to implement the +# "machinectl bind" operation. diff --git a/src/grp-machine/systemd-machined/systemd-machined.service.xml b/src/grp-machine/systemd-machined/systemd-machined.service.xml new file mode 100644 index 0000000000..999aeee1c6 --- /dev/null +++ b/src/grp-machine/systemd-machined/systemd-machined.service.xml @@ -0,0 +1,90 @@ + + + + + + + + + systemd-machined.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-machined.service + 8 + + + + systemd-machined.service + systemd-machined + Virtual machine and container registration manager + + + + systemd-machined.service + /usr/lib/systemd/systemd-machined + + + + Description + + systemd-machined is a system service that + keeps track of virtual machines and containers, and processes + belonging to them. + + See + systemd-nspawn1 + for some examples on how to run containers with OS tools. + + Use + nss-mymachines8 + to make the names of local containers known to + systemd-machined locally resolvable as host + names. + + See the + + machined D-Bus API Documentation for information about the + APIs systemd-machined provides. + + + + See Also + + systemd1, + machinectl1, + systemd-nspawn1, + nss-mymachines8, + systemd.special7 + + + + diff --git a/src/grp-machine/systemd-machined/test-machine-tables.c b/src/grp-machine/systemd-machined/test-machine-tables.c new file mode 100644 index 0000000000..7d44c55a4b --- /dev/null +++ b/src/grp-machine/systemd-machined/test-machine-tables.c @@ -0,0 +1,30 @@ +/*** + This file is part of systemd + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "shared/test-tables.h" + +#include "machine.h" + +int main(int argc, char **argv) { + test_table(machine_class, MACHINE_CLASS); + test_table(machine_state, MACHINE_STATE); + test_table(kill_who, KILL_WHO); + + return EXIT_SUCCESS; +} diff --git a/src/grp-network/90-networkd.preset b/src/grp-network/90-networkd.preset new file mode 100644 index 0000000000..a053f7a4b1 --- /dev/null +++ b/src/grp-network/90-networkd.preset @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +enable systemd-networkd.service + +disable systemd-networkd-wait-online.service diff --git a/src/grp-network/Makefile b/src/grp-network/Makefile new file mode 100644 index 0000000000..3313eb989e --- /dev/null +++ b/src/grp-network/Makefile @@ -0,0 +1,85 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +dist_network_DATA = \ + network/99-default.link \ + network/80-container-host0.network \ + network/80-container-ve.network \ + network/80-container-vz.network + +ifneq ($(ENABLE_NETWORKD),) +test_networkd_conf_SOURCES = \ + src/network/test-networkd-conf.c + +test_networkd_conf_LDADD = \ + libnetworkd-core.la + +test_network_SOURCES = \ + src/network/test-network.c + +test_network_LDADD = \ + libnetworkd-core.la + +ifneq ($(HAVE_LIBIPTC),) +test_network_LDADD += \ + libfirewall.la +endif # HAVE_LIBIPTC + +test_network_tables_SOURCES = \ + src/network/test-network-tables.c \ + src/shared/test-tables.h + +test_network_tables_LDADD = \ + libnetworkd-core.la \ + libudev-core.la + +ifneq ($(HAVE_LIBIPTC),) +test_network_tables_LDADD += \ + libfirewall.la +endif # HAVE_LIBIPTC + +tests += \ + test-networkd-conf \ + test-network \ + test-network-tables + +endif # ENABLE_NETWORKD + +gperf_gperf_sources += \ + src/network/networkd-gperf.gperf \ + src/network/networkd-network-gperf.gperf \ + src/network/networkd-netdev-gperf.gperf + +EXTRA_DIST += \ + units/systemd-networkd.service.m4.in \ + units/systemd-networkd-wait-online.service.in \ + test/networkd-test.py + +nested.subdirs += libnetworkd-core +nested.subdirs += networkctl +nested.subdirs += systemd-networkd +nested.subdirs += systemd-networkd-wait-online + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/libnetworkd-core/.gitignore b/src/grp-network/libnetworkd-core/.gitignore new file mode 100644 index 0000000000..aca55206b7 --- /dev/null +++ b/src/grp-network/libnetworkd-core/.gitignore @@ -0,0 +1,3 @@ +/networkd-network-gperf.c +/networkd-netdev-gperf.c +/networkd-gperf.c diff --git a/src/grp-network/libnetworkd-core/Makefile b/src/grp-network/libnetworkd-core/Makefile new file mode 100644 index 0000000000..321c05d7ea --- /dev/null +++ b/src/grp-network/libnetworkd-core/Makefile @@ -0,0 +1,97 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libnetworkd-core.la + +libnetworkd_core_la_CFLAGS = \ + +libnetworkd_core_la_SOURCES = \ + src/libsystemd-network/network-internal.h \ + src/network/networkd.h \ + src/network/networkd-conf.h \ + src/network/networkd-conf.c \ + src/network/networkd-link.h \ + src/network/networkd-link.c \ + src/network/networkd-netdev.h \ + src/network/networkd-netdev.c \ + src/network/networkd-netdev-vrf.h \ + src/network/networkd-netdev-vrf.c \ + src/network/networkd-netdev-tunnel.h \ + src/network/networkd-netdev-tunnel.c \ + src/network/networkd-netdev-veth.h \ + src/network/networkd-netdev-veth.c \ + src/network/networkd-netdev-vxlan.h \ + src/network/networkd-netdev-vxlan.c \ + src/network/networkd-netdev-vlan.h \ + src/network/networkd-netdev-vlan.c \ + src/network/networkd-netdev-macvlan.h \ + src/network/networkd-netdev-macvlan.c \ + src/network/networkd-netdev-ipvlan.h \ + src/network/networkd-netdev-ipvlan.c \ + src/network/networkd-netdev-dummy.h \ + src/network/networkd-netdev-dummy.c \ + src/network/networkd-netdev-tuntap.h \ + src/network/networkd-netdev-tuntap.c \ + src/network/networkd-netdev-bond.h \ + src/network/networkd-netdev-bond.c \ + src/network/networkd-netdev-bridge.h \ + src/network/networkd-netdev-bridge.c \ + src/network/networkd-link-bus.c \ + src/network/networkd-ipv4ll.c \ + src/network/networkd-dhcp4.c \ + src/network/networkd-dhcp6.c \ + src/network/networkd-ndisc.h \ + src/network/networkd-ndisc.c \ + src/network/networkd-network.h \ + src/network/networkd-network.c \ + src/network/networkd-network-bus.c \ + src/network/networkd-address.h \ + src/network/networkd-address.c \ + src/network/networkd-route.h \ + src/network/networkd-route.c \ + src/network/networkd-manager.c \ + src/network/networkd-manager-bus.c \ + src/network/networkd-fdb.h \ + src/network/networkd-fdb.c \ + src/network/networkd-brvlan.h \ + src/network/networkd-brvlan.c \ + src/network/networkd-address-pool.h \ + src/network/networkd-address-pool.c \ + src/network/networkd-util.h \ + src/network/networkd-util.c \ + src/network/networkd-lldp-tx.h \ + src/network/networkd-lldp-tx.c + +nodist_libnetworkd_core_la_SOURCES = \ + src/network/networkd-gperf.c \ + src/network/networkd-network-gperf.c \ + src/network/networkd-netdev-gperf.c + +libnetworkd_core_la_LIBADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/libnetworkd-core/networkd-address-pool.c b/src/grp-network/libnetworkd-core/networkd-address-pool.c new file mode 100644 index 0000000000..7b57f6be0e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-address-pool.c @@ -0,0 +1,172 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/set.h" +#include "basic/string-util.h" + +#include "networkd-address-pool.h" +#include "networkd.h" + +int address_pool_new( + Manager *m, + AddressPool **ret, + int family, + const union in_addr_union *u, + unsigned prefixlen) { + + AddressPool *p; + + assert(m); + assert(ret); + assert(u); + + p = new0(AddressPool, 1); + if (!p) + return -ENOMEM; + + p->manager = m; + p->family = family; + p->prefixlen = prefixlen; + p->in_addr = *u; + + LIST_PREPEND(address_pools, m->address_pools, p); + + *ret = p; + return 0; +} + +int address_pool_new_from_string( + Manager *m, + AddressPool **ret, + int family, + const char *p, + unsigned prefixlen) { + + union in_addr_union u; + int r; + + assert(m); + assert(ret); + assert(p); + + r = in_addr_from_string(family, p, &u); + if (r < 0) + return r; + + return address_pool_new(m, ret, family, &u, prefixlen); +} + +void address_pool_free(AddressPool *p) { + + if (!p) + return; + + if (p->manager) + LIST_REMOVE(address_pools, p->manager->address_pools, p); + + free(p); +} + +static bool address_pool_prefix_is_taken( + AddressPool *p, + const union in_addr_union *u, + unsigned prefixlen) { + + Iterator i; + Link *l; + Network *n; + + assert(p); + assert(u); + + HASHMAP_FOREACH(l, p->manager->links, i) { + Address *a; + Iterator j; + + /* Don't clash with assigned addresses */ + SET_FOREACH(a, l->addresses, j) { + if (a->family != p->family) + continue; + + if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) + return true; + } + + /* Don't clash with addresses already pulled from the pool, but not assigned yet */ + LIST_FOREACH(addresses, a, l->pool_addresses) { + if (a->family != p->family) + continue; + + if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) + return true; + } + } + + /* And don't clash with configured but un-assigned addresses either */ + LIST_FOREACH(networks, n, p->manager->networks) { + Address *a; + + LIST_FOREACH(addresses, a, n->static_addresses) { + if (a->family != p->family) + continue; + + if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) + return true; + } + } + + return false; +} + +int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) { + union in_addr_union u; + + assert(p); + assert(prefixlen > 0); + assert(found); + + if (p->prefixlen > prefixlen) + return 0; + + u = p->in_addr; + for (;;) { + if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { + _cleanup_free_ char *s = NULL; + int r; + + r = in_addr_to_string(p->family, &u, &s); + if (r < 0) + return r; + + log_debug("Found range %s/%u", strna(s), prefixlen); + + *found = u; + return 1; + } + + if (!in_addr_prefix_next(p->family, &u, prefixlen)) + return 0; + + if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen)) + return 0; + } + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-address-pool.h b/src/grp-network/libnetworkd-core/networkd-address-pool.h new file mode 100644 index 0000000000..5d7020739d --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-address-pool.h @@ -0,0 +1,43 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/in-addr-util.h" +#include "basic/list.h" + +typedef struct AddressPool AddressPool; +typedef struct Manager Manager; + +struct AddressPool { + Manager *manager; + + int family; + unsigned prefixlen; + + union in_addr_union in_addr; + + LIST_FIELDS(AddressPool, address_pools); +}; + +int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen); +int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); +void address_pool_free(AddressPool *p); + +int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); diff --git a/src/grp-network/libnetworkd-core/networkd-address.c b/src/grp-network/libnetworkd-core/networkd-address.c new file mode 100644 index 0000000000..cd24bc12f2 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-address.c @@ -0,0 +1,864 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/parse-util.h" +#include "basic/set.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "firewall-util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/conf-parser.h" + +#include "networkd-address.h" +#include "networkd.h" + +#define ADDRESSES_PER_LINK_MAX 2048U +#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U + +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; + + assert(network); + assert(ret); + + if (section) { + address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section)); + if (address) { + *ret = address; + address = NULL; + + return 0; + } + } + + if (network->n_static_addresses >= STATIC_ADDRESSES_PER_NETWORK_MAX) + return -E2BIG; + + r = address_new(&address); + if (r < 0) + return r; + + if (section) { + address->section = section; + hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); + } + + address->network = network; + LIST_APPEND(addresses, network->static_addresses, address); + network->n_static_addresses++; + + *ret = address; + address = NULL; + + return 0; +} + +void address_free(Address *address) { + if (!address) + return; + + if (address->network) { + LIST_REMOVE(addresses, address->network->static_addresses, address); + assert(address->network->n_static_addresses > 0); + address->network->n_static_addresses--; + + if (address->section) + hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section)); + } + + if (address->link) { + set_remove(address->link->addresses, address); + set_remove(address->link->addresses_foreign, address); + + if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) + memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); + } + + free(address); +} + +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; + + assert(address); + assert(link); + + masq = link->network && + 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) { + union in_addr_union masked = address->in_addr; + in_addr_mask(address->family, &masked, address->prefixlen); + + r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); + if (r < 0) + log_link_warning_errno(link, r, "Could not enable IP masquerading: %m"); + + address->ip_masquerade_done = masq; + } + + return 0; +} + +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(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) { + union in_addr_union masked = address->in_addr; + in_addr_mask(address->family, &masked, address->prefixlen); + + r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); + if (r < 0) + log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m"); + + address->ip_masquerade_done = false; + } + + return 0; +} + +int address_update( + Address *address, + unsigned char flags, + unsigned char scope, + const struct ifa_cacheinfo *cinfo) { + + bool ready; + int r; + + assert(address); + assert(cinfo); + assert_return(address->link, 1); + + if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + ready = address_is_ready(address); + + address->flags = flags; + address->scope = scope; + address->cinfo = *cinfo; + + link_update_operstate(address->link); + + if (!ready && address_is_ready(address)) { + link_check_ready(address->link); + + 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; + } + } + + 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_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); + + address = (Address) { + .family = family, + .in_addr = *in_addr, + .prefixlen = prefixlen, + }; + + existing = set_get(link->addresses, &address); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + + existing = set_get(link->addresses_foreign, &address); + if (existing) { + if (ret) + *ret = existing; + return 0; + } + + return -ENOENT; +} + +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(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"); + + 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->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"); + + 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"); + + link_ref(link); + + return 0; +} + +static int address_acquire(Link *link, Address *original, Address **ret) { + union in_addr_union in_addr = {}; + struct in_addr broadcast = {}; + _cleanup_address_free_ Address *na = NULL; + int r; + + assert(link); + assert(original); + assert(ret); + + /* Something useful was configured? just use it */ + if (in_addr_is_null(original->family, &original->in_addr) <= 0) + return 0; + + /* The address is configured to be 0.0.0.0 or [::] by the user? + * Then let's acquire something more useful from the pool. */ + r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); + if (r < 0) + return log_link_error_errno(link, r, "Failed to acquire address from pool: %m"); + if (r == 0) { + log_link_error(link, "Couldn't find free address for interface, all taken."); + return -EBUSY; + } + + if (original->family == AF_INET) { + /* Pick first address in range for ourselves ... */ + in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1); + + /* .. and use last as broadcast address */ + broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen); + } else if (original->family == AF_INET6) + in_addr.in6.s6_addr[15] |= 1; + + r = address_new(&na); + if (r < 0) + return r; + + na->family = original->family; + na->prefixlen = original->prefixlen; + na->scope = original->scope; + na->cinfo = original->cinfo; + + if (original->label) { + na->label = strdup(original->label); + if (!na->label) + return -ENOMEM; + } + + na->broadcast = broadcast; + na->in_addr = in_addr; + + LIST_PREPEND(addresses, link->pool_addresses, na); + + *ret = na; + na = NULL; + + return 0; +} + +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); + assert(address->family == AF_INET || address->family == AF_INET6); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); + + /* If this is a new address, then refuse adding more than the limit */ + if (address_get(link, address->family, &address->in_addr, address->prefixlen, NULL) <= 0 && + set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) + return -E2BIG; + + r = address_acquire(link, address, &address); + if (r < 0) + return r; + + 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"); + + 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) { + 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) + 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 (!in_addr_is_null(address->family, &address->in_addr_peer)) { + if (address->family == AF_INET) + r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in); + else if (address->family == AF_INET6) + r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6); + if (r < 0) + return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m"); + } else { + 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 = 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); + + 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; +} + +int config_parse_broadcast( + 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_address_free_ Address *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (n->family == AF_INET6) { + 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, r, "Broadcast is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + n->family = AF_INET; + n = NULL; + + return 0; +} + +int config_parse_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; + const char *address, *e; + union in_addr_union buffer; + int r, f; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(section, "Network")) { + /* we are not in an Address section, so treat + * this as the special '0' section */ + section_line = 0; + } + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + /* Address=address/prefixlen */ + + /* prefixlen */ + e = strchr(rvalue, '/'); + if (e) { + unsigned i; + + r = safe_atou(e + 1, &i); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1); + return 0; + } + + n->prefixlen = (unsigned char) i; + + address = strndupa(rvalue, e - rvalue); + } else + address = rvalue; + + r = in_addr_from_string_auto(address, &f, &buffer); + if (r < 0) { + 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, 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, 0, "Address is incompatible, ignoring assignment: %s", address); + return 0; + } + + n->family = f; + + if (streq(lvalue, "Address")) + n->in_addr = buffer; + else + n->in_addr_peer = buffer; + + if (n->family == AF_INET && n->broadcast.s_addr == 0) + n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen); + + n = NULL; + + return 0; +} + +int config_parse_label( + 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_address_free_ Address *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(&n->label, rvalue); + if (r < 0) + return log_oom(); + + n = NULL; + + return 0; +} + +int config_parse_lifetime(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_address_free_ Address *n = NULL; + unsigned k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (STR_IN_SET(rvalue, "forever", "infinity")) { + n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; + n = NULL; + + return 0; + } + + r = safe_atou(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); + return 0; + } + + if (k != 0) + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); + else { + n->cinfo.ifa_prefered = k; + n = NULL; + } + + return 0; +} + +bool address_is_ready(const Address *a) { + assert(a); + + return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); +} diff --git a/src/grp-network/libnetworkd-core/networkd-address.h b/src/grp-network/libnetworkd-core/networkd-address.h new file mode 100644 index 0000000000..9378621580 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-address.h @@ -0,0 +1,79 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include "basic/in-addr-util.h" + +typedef struct Address Address; + +#include "networkd-link.h" +#include "networkd-network.h" + +#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU + +typedef struct Network Network; +typedef struct Link Link; + +struct Address { + Network *network; + unsigned section; + + Link *link; + + int family; + unsigned char prefixlen; + unsigned char scope; + uint32_t flags; + char *label; + + struct in_addr broadcast; + struct ifa_cacheinfo cinfo; + + union in_addr_union in_addr; + union in_addr_union in_addr_peer; + + bool ip_masquerade_done:1; + + LIST_FIELDS(Address, addresses); +}; + +int address_new_static(Network *network, unsigned section, Address **ret); +int address_new(Address **ret); +void address_free(Address *address); +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, const 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) + +int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_broadcast(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_label(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_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-brvlan.c b/src/grp-network/libnetworkd-core/networkd-brvlan.c new file mode 100644 index 0000000000..118a6ed464 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-brvlan.c @@ -0,0 +1,331 @@ +/*** + This file is part of systemd. + + Copyright (C) 2016 BISDN GmbH. 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/parse-util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/conf-parser.h" +#include "shared/vlan-util.h" + +#include "networkd-brvlan.h" +#include "networkd.h" + +static bool is_bit_set(unsigned bit, uint32_t scope) { + assert(bit < sizeof(scope)*8); + return scope & (1 << bit); +} + +static inline void set_bit(unsigned nr, uint32_t *addr) { + if (nr < BRIDGE_VLAN_BITMAP_MAX) + addr[nr / 32] |= (((uint32_t) 1) << (nr % 32)); +} + +static int find_next_bit(int i, uint32_t x) { + int j; + + if (i >= 32) + return -1; + + /* find first bit */ + if (i < 0) + return BUILTIN_FFS_U32(x); + + /* mask off prior finds to get next */ + j = __builtin_ffs(x >> i); + return j ? j + i : 0; +} + +static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint16_t pvid, const uint32_t *br_vid_bitmap, const uint32_t *br_untagged_bitmap) { + struct bridge_vlan_info br_vlan; + int i, j, k, r, done, cnt; + uint16_t begin, end; + bool untagged = false; + + assert(link); + assert(req); + assert(br_vid_bitmap); + assert(br_untagged_bitmap); + + i = cnt = -1; + + begin = end = UINT16_MAX; + for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) { + unsigned base_bit; + uint32_t vid_map = br_vid_bitmap[k]; + uint32_t untagged_map = br_untagged_bitmap[k]; + + base_bit = k * 32; + i = -1; + done = 0; + do { + j = find_next_bit(i, vid_map); + if (j > 0) { + /* first hit of any bit */ + if (begin == UINT16_MAX && end == UINT16_MAX) { + begin = end = j - 1 + base_bit; + untagged = is_bit_set(j - 1, untagged_map); + goto next; + } + + /* this bit is a continuation of prior bits */ + if (j - 2 + base_bit == end && untagged == is_bit_set(j - 1, untagged_map) && (uint16_t)j - 1 + base_bit != pvid && (uint16_t)begin != pvid) { + end++; + goto next; + } + } else + done = 1; + + if (begin != UINT16_MAX) { + cnt++; + if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1) + break; + + br_vlan.flags = 0; + if (untagged) + br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED; + + if (begin == end) { + br_vlan.vid = begin; + + if (begin == pvid) + br_vlan.flags |= BRIDGE_VLAN_INFO_PVID; + + r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + } else { + br_vlan.vid = begin; + br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; + + r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + + br_vlan.vid = end; + br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END; + + r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + } + + if (done) + break; + } + if (j > 0) { + begin = end = j - 1 + base_bit; + untagged = is_bit_set(j - 1, untagged_map); + } + + next: + i = j; + } while(!done); + } + if (!cnt) + return -EINVAL; + + return cnt; +} + +static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_error_errno(link, r, "Could not add VLAN to bridge port: %m"); + + return 1; +} + +int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + uint16_t flags; + sd_netlink *rtnl; + + assert(link); + assert(link->manager); + assert(br_vid_bitmap); + assert(br_untagged_bitmap); + assert(link->network); + + /* pvid might not be in br_vid_bitmap yet */ + if (pvid) + set_bit(pvid, br_vid_bitmap); + + rtnl = link->manager->rtnl; + + /* create new RTM message */ + r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); + if (r < 0) + return log_link_error_errno(link, r, "Could not set message family: %m"); + + r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); + if (r < 0) + return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); + + /* master needs flag self */ + if (!link->network->bridge) { + flags = BRIDGE_FLAGS_SELF; + sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t)); + } + + /* add vlan info */ + r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap); + if (r < 0) + return log_link_error_errno(link, r, "Could not append VLANs: %m"); + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); + + /* send message to the kernel */ + r = sd_netlink_call_async(rtnl, req, set_brvlan_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + return 0; +} + +static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) { + int r; + char *p; + char *_rvalue = NULL; + uint16_t _vid = UINT16_MAX; + uint16_t _vid_end = UINT16_MAX; + + assert(rvalue); + assert(vid); + assert(vid_end); + + _rvalue = strdupa(rvalue); + p = strchr(_rvalue, '-'); + if (p) { + *p = '\0'; + p++; + r = parse_vlanid(_rvalue, &_vid); + if (r < 0) + return r; + + if (_vid == 0) + return -ERANGE; + + r = parse_vlanid(p, &_vid_end); + if (r < 0) + return r; + + if (_vid_end == 0) + return -ERANGE; + } else { + r = parse_vlanid(_rvalue, &_vid); + if (r < 0) + return r; + + if (_vid == 0) + return -ERANGE; + } + + *vid = _vid; + *vid_end = _vid_end; + return r; +} + +int config_parse_brvlan_vlan(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; + int r; + uint16_t vid, vid_end; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_vid_range(rvalue, &vid, &vid_end); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); + return 0; + } + + if (UINT16_MAX == vid_end) + set_bit(vid++, network->br_vid_bitmap); + else { + if (vid >= vid_end) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue); + return 0; + } + for (; vid <= vid_end; vid++) + set_bit(vid, network->br_vid_bitmap); + } + return 0; +} + +int config_parse_brvlan_untagged(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; + int r; + uint16_t vid, vid_end; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_vid_range(rvalue, &vid, &vid_end); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue); + return 0; + } + + if (UINT16_MAX == vid_end) { + set_bit(vid, network->br_vid_bitmap); + set_bit(vid, network->br_untagged_bitmap); + } else { + if (vid >= vid_end) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue); + return 0; + } + for (; vid <= vid_end; vid++) { + set_bit(vid, network->br_vid_bitmap); + set_bit(vid, network->br_untagged_bitmap); + } + } + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-brvlan.h b/src/grp-network/libnetworkd-core/networkd-brvlan.h new file mode 100644 index 0000000000..6aa6883bfc --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-brvlan.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2016 BISDN GmbH. 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 . +***/ + +#include + +typedef struct Link Link; + +int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap); + +int config_parse_brvlan_vlan(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_brvlan_untagged(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-conf.c b/src/grp-network/libnetworkd-core/networkd-conf.c new file mode 100644 index 0000000000..44f5170219 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-conf.c @@ -0,0 +1,112 @@ +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include + +#include "basic/def.h" +#include "basic/hexdecoct.h" +#include "basic/string-table.h" +#include "shared/conf-parser.h" +#include "systemd-network/dhcp-identifier.h" + +#include "networkd-conf.h" + +int manager_parse_config_file(Manager *m) { + assert(m); + + return config_parse_many(PKGSYSCONFDIR "/networkd.conf", + CONF_PATHS_NULSTR("systemd/networkd.conf.d"), + "DHCP\0", + config_item_perf_lookup, networkd_gperf_lookup, + false, m); +} + +static const char* const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "link-layer-time", + [DUID_TYPE_EN] = "vendor", + [DUID_TYPE_LL] = "link-layer", + [DUID_TYPE_UUID] = "uuid", +}; +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type"); + +int config_parse_duid_rawdata( + 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) { + + DUID *ret = data; + uint8_t raw_data[MAX_DUID_LEN]; + unsigned count = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ret); + + /* RawData contains DUID in format "NN:NN:NN..." */ + for (;;) { + int n1, n2, len, r; + uint32_t byte; + _cleanup_free_ char *cbyte = NULL; + + r = extract_first_word(&rvalue, &cbyte, ":", 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); + return 0; + } + if (r == 0) + break; + if (count >= MAX_DUID_LEN) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); + return 0; + } + + len = strlen(cbyte); + if (len != 1 && len != 2) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + n1 = unhexchar(cbyte[0]); + if (len == 2) + n2 = unhexchar(cbyte[1]); + else + n2 = 0; + + if (n1 < 0 || n2 < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + + byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; + raw_data[count++] = byte; + } + + assert_cc(sizeof(raw_data) == sizeof(ret->raw_data)); + memcpy(ret->raw_data, raw_data, count); + ret->raw_data_len = count; + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-conf.h b/src/grp-network/libnetworkd-core/networkd-conf.h new file mode 100644 index 0000000000..c7bfb42a72 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-conf.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "networkd.h" + +int manager_parse_config_file(Manager *m); + +const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length); + +int config_parse_duid_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_duid_rawdata( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-dhcp4.c b/src/grp-network/libnetworkd-core/networkd-dhcp4.c new file mode 100644 index 0000000000..2e7858ccd3 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-dhcp4.c @@ -0,0 +1,659 @@ +/*** + This file is part of systemd. + + Copyright 2013-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 . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/network-internal.h" + +#include "networkd.h" + +static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, + void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->dhcp4_messages > 0); + + link->dhcp4_messages--; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set DHCPv4 route: %m"); + link_enter_failed(link); + } + + if (link->dhcp4_messages == 0) { + link->dhcp4_configured = true; + link_check_ready(link); + } + + return 1; +} + +static int link_set_dhcp_routes(Link *link) { + struct in_addr gateway; + _cleanup_free_ sd_dhcp_route **static_routes = NULL; + int r, n, i; + + assert(link); + assert(link->dhcp_lease); + assert(link->network); + + if (!link->network->dhcp_use_routes) + return 0; + + r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); + if (r < 0 && r != -ENODATA) + return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); + + if (r >= 0) { + struct in_addr address; + _cleanup_route_free_ Route *route = NULL; + _cleanup_route_free_ Route *route_gw = NULL; + + r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); + + r = route_new(&route); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); + + route->protocol = RTPROT_DHCP; + + r = route_new(&route_gw); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); + + /* The dhcp netmask may mask out the gateway. Add an explicit + * 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.in = gateway; + route_gw->dst_prefixlen = 32; + route_gw->prefsrc.in = address; + route_gw->scope = RT_SCOPE_LINK; + 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) + return log_link_warning_errno(link, r, "Could not set host route: %m"); + + link->dhcp4_messages++; + + route->family = AF_INET; + 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) { + log_link_warning_errno(link, r, "Could not set routes: %m"); + link_enter_failed(link); + return r; + } + + link->dhcp4_messages++; + } + + n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); + if (n == -ENODATA) + return 0; + if (n < 0) + return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m"); + + for (i = 0; i < n; i++) { + _cleanup_route_free_ Route *route = NULL; + + r = route_new(&route); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); + + route->family = AF_INET; + 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) + return log_link_warning_errno(link, r, "Could not set host route: %m"); + + link->dhcp4_messages++; + } + + return 0; +} + +static int dhcp_lease_lost(Link *link) { + _cleanup_address_free_ Address *address = NULL; + struct in_addr addr; + struct in_addr netmask; + struct in_addr gateway; + unsigned prefixlen = 0; + int r; + + assert(link); + assert(link->dhcp_lease); + + log_link_warning(link, "DHCP lease lost"); + + if (link->network->dhcp_use_routes) { + _cleanup_free_ sd_dhcp_route **routes = NULL; + int n, i; + + n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); + if (n >= 0) { + for (i = 0; i < n; i++) { + _cleanup_route_free_ Route *route = NULL; + + r = route_new(&route); + if (r >= 0) { + route->family = AF_INET; + 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_remove(route, link, + link_route_remove_handler); + } + } + } + } + + 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(&route_gw); + if (r >= 0) { + route_gw->family = AF_INET; + route_gw->dst.in = gateway; + route_gw->dst_prefixlen = 32; + route_gw->scope = RT_SCOPE_LINK; + + route_remove(route_gw, link, + link_route_remove_handler); + } + + r = route_new(&route); + if (r >= 0) { + route->family = AF_INET; + route->gw.in = gateway; + + route_remove(route, link, + link_route_remove_handler); + } + } + + r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); + if (r >= 0) { + r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); + if (r >= 0) + prefixlen = in_addr_netmask_to_prefixlen(&netmask); + + address->family = AF_INET; + address->in_addr.in = addr; + address->prefixlen = prefixlen; + + address_remove(address, link, link_address_remove_handler); + } + } + + if (link->network->dhcp_use_mtu) { + uint16_t mtu; + + r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); + if (r >= 0 && link->original_mtu != mtu) { + r = link_set_mtu(link, link->original_mtu); + if (r < 0) { + log_link_warning(link, + "DHCP error: could not reset MTU"); + link_enter_failed(link); + return r; + } + } + } + + if (link->network->dhcp_use_hostname) { + const char *hostname = NULL; + + if (link->network->dhcp_hostname) + hostname = link->network->dhcp_hostname; + else + (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); + + if (hostname) { + /* If a hostname was set due to the lease, then unset it now. */ + r = link_set_hostname(link, NULL); + if (r < 0) + log_link_warning_errno(link, r, "Failed to reset transient hostname: %m"); + } + } + + link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); + link_dirty(link); + link->dhcp4_configured = false; + + return 0; +} + +static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, + void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set DHCPv4 address: %m"); + link_enter_failed(link); + } else if (r >= 0) + manager_rtnl_process_address(rtnl, m, link->manager); + + link_set_dhcp_routes(link); + + return 1; +} + +static int dhcp4_update_address(Link *link, + struct in_addr *address, + struct in_addr *netmask, + uint32_t lifetime) { + _cleanup_address_free_ Address *addr = NULL; + unsigned prefixlen; + int r; + + assert(address); + assert(netmask); + assert(lifetime); + + prefixlen = in_addr_netmask_to_prefixlen(netmask); + + r = address_new(&addr); + if (r < 0) + return r; + + addr->family = AF_INET; + addr->in_addr.in.s_addr = address->s_addr; + addr->cinfo.ifa_prefered = lifetime; + addr->cinfo.ifa_valid = lifetime; + addr->prefixlen = prefixlen; + addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr; + + /* 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; + + return 0; +} + +static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { + sd_dhcp_lease *lease; + struct in_addr address; + struct in_addr netmask; + uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; + int r; + + assert(link); + assert(client); + assert(link->network); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no lease: %m"); + + 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) + return log_link_warning_errno(link, r, "DHCP error: no address: %m"); + + r = sd_dhcp_lease_get_netmask(lease, &netmask); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no netmask: %m"); + + if (!link->network->dhcp_critical) { + r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); + } + + r = dhcp4_update_address(link, &address, &netmask, lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Could not update IP address: %m"); + link_enter_failed(link); + return r; + } + + return 0; +} + +static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { + sd_dhcp_lease *lease; + struct in_addr address; + struct in_addr netmask; + struct in_addr gateway; + unsigned prefixlen; + uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; + int r; + + assert(client); + assert(link); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "DHCP error: No lease: %m"); + + r = sd_dhcp_lease_get_address(lease, &address); + if (r < 0) + return log_link_error_errno(link, r, "DHCP error: No address: %m"); + + r = sd_dhcp_lease_get_netmask(lease, &netmask); + if (r < 0) + return log_link_error_errno(link, r, "DHCP error: No netmask: %m"); + + prefixlen = in_addr_netmask_to_prefixlen(&netmask); + + r = sd_dhcp_lease_get_router(lease, &gateway); + if (r < 0 && r != -ENODATA) + return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); + + if (r >= 0) + log_struct(LOG_INFO, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", + ADDRESS_FMT_VAL(address), + prefixlen, + ADDRESS_FMT_VAL(gateway)), + "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), + "PREFIXLEN=%u", prefixlen, + "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway), + NULL); + else + log_struct(LOG_INFO, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u", + ADDRESS_FMT_VAL(address), + prefixlen), + "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), + "PREFIXLEN=%u", prefixlen, + NULL); + + link->dhcp_lease = sd_dhcp_lease_ref(lease); + link_dirty(link); + + if (link->network->dhcp_use_mtu) { + uint16_t mtu; + + r = sd_dhcp_lease_get_mtu(lease, &mtu); + if (r >= 0) { + r = link_set_mtu(link, mtu); + if (r < 0) + log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu); + } + } + + if (link->network->dhcp_use_hostname) { + const char *hostname = NULL; + + if (link->network->dhcp_hostname) + hostname = link->network->dhcp_hostname; + else + (void) sd_dhcp_lease_get_hostname(lease, &hostname); + + if (hostname) { + r = link_set_hostname(link, hostname); + if (r < 0) + log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname); + } + } + + if (link->network->dhcp_use_timezone) { + const char *tz = NULL; + + (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); + + if (tz) { + r = link_set_timezone(link, tz); + if (r < 0) + log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz); + } + } + + if (!link->network->dhcp_critical) { + r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); + return r; + } + } + + r = dhcp4_update_address(link, &address, &netmask, lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Could not update IP address: %m"); + link_enter_failed(link); + return r; + } + + return 0; +} +static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { + Link *link = userdata; + int r = 0; + + assert(link); + assert(link->network); + assert(link->manager); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + case SD_DHCP_CLIENT_EVENT_EXPIRED: + case SD_DHCP_CLIENT_EVENT_STOP: + case SD_DHCP_CLIENT_EVENT_IP_CHANGE: + if (link->network->dhcp_critical) { + log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); + return; + } + + if (link->dhcp_lease) { + r = dhcp_lease_lost(link); + if (r < 0) { + link_enter_failed(link); + return; + } + } + + if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) { + r = dhcp_lease_acquired(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } + } + + break; + case SD_DHCP_CLIENT_EVENT_RENEW: + r = dhcp_lease_renew(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } + break; + case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: + r = dhcp_lease_acquired(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } + break; + default: + if (event < 0) + log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); + else + log_link_warning(link, "DHCP unknown event: %i", event); + break; + } + + return; +} + +int dhcp4_configure(Link *link) { + int r; + + assert(link); + assert(link->network); + assert(link->network->dhcp & ADDRESS_FAMILY_IPV4); + + 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) + return r; + + r = sd_dhcp_client_set_mac(link->dhcp_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); + if (r < 0) + return r; + + r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex); + if (r < 0) + return r; + + r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link); + if (r < 0) + return r; + + r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, + link->network->dhcp_broadcast); + if (r < 0) + return r; + + if (link->mtu) { + r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu); + if (r < 0) + return r; + } + + if (link->network->dhcp_use_mtu) { + r = sd_dhcp_client_set_request_option(link->dhcp_client, + SD_DHCP_OPTION_INTERFACE_MTU); + if (r < 0) + return r; + } + + if (link->network->dhcp_use_routes) { + r = sd_dhcp_client_set_request_option(link->dhcp_client, + SD_DHCP_OPTION_STATIC_ROUTE); + if (r < 0) + return r; + r = sd_dhcp_client_set_request_option(link->dhcp_client, + 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, SD_DHCP_OPTION_NTP_SERVER); + if (r < 0) + return r; + + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE); + if (r < 0) + return r; + + if (link->network->dhcp_send_hostname) { + _cleanup_free_ char *hostname = NULL; + const char *hn = NULL; + + if (!link->network->dhcp_hostname) { + hostname = gethostname_malloc(); + if (!hostname) + return -ENOMEM; + + hn = hostname; + } else + hn = link->network->dhcp_hostname; + + if (!is_localhost(hn)) { + r = sd_dhcp_client_set_hostname(link->dhcp_client, hn); + if (r < 0) + return r; + } + } + + if (link->network->dhcp_vendor_class_identifier) { + r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client, + link->network->dhcp_vendor_class_identifier); + if (r < 0) + return r; + } + + switch (link->network->dhcp_client_identifier) { + case DHCP_CLIENT_ID_DUID: { + /* If configured, apply user specified DUID and/or IAID */ + const DUID *duid = link_duid(link); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return r; + break; + } + case DHCP_CLIENT_ID_MAC: + r = sd_dhcp_client_set_client_id(link->dhcp_client, + ARPHRD_ETHER, + (const uint8_t *) &link->mac, + sizeof(link->mac)); + if (r < 0) + return r; + break; + default: + assert_not_reached("Unknown client identifier type."); + } + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-dhcp6.c b/src/grp-network/libnetworkd-core/networkd-dhcp6.c new file mode 100644 index 0000000000..d00190c520 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-dhcp6.c @@ -0,0 +1,266 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "systemd-network/network-internal.h" +#include "systemd-network/sd-dhcp6-client.h" + +#include "networkd.h" + +static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); + +static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, + Link *link) { + return 0; +} + +static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, + void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + if (link->rtnl_extended_attrs) { + log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism"); + + link->rtnl_extended_attrs = false; + dhcp6_lease_address_acquired(link->dhcp6_client, link); + + return 1; + } + + log_link_error_errno(link, r, "Could not set DHCPv6 address: %m"); + + link_enter_failed(link); + + } else if (r >= 0) + manager_rtnl_process_address(rtnl, m, link->manager); + + return 1; +} + +static int dhcp6_address_change( + Link *link, + struct in6_addr *ip6_addr, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + + _cleanup_address_free_ Address *addr = NULL; + char buffer[INET6_ADDRSTRLEN]; + int r; + + r = address_new(&addr); + if (r < 0) + return r; + + addr->family = AF_INET6; + memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr)); + + addr->flags = IFA_F_NOPREFIXROUTE; + addr->prefixlen = 128; + + addr->cinfo.ifa_prefered = lifetime_preferred; + addr->cinfo.ifa_valid = lifetime_valid; + + log_link_info(link, + "DHCPv6 address %s/%d timeout preferred %d valid %d", + inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)), + addr->prefixlen, lifetime_preferred, lifetime_valid); + + r = address_configure(addr, link, dhcp6_address_handler, true); + if (r < 0) + log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); + + return r; +} + +static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { + int r; + sd_dhcp6_lease *lease; + struct in6_addr ip6_addr; + uint32_t lifetime_preferred, lifetime_valid; + + r = sd_dhcp6_client_get_lease(client, &lease); + if (r < 0) + return r; + + sd_dhcp6_lease_reset_address_iter(lease); + + while (sd_dhcp6_lease_get_address(lease, &ip6_addr, + &lifetime_preferred, + &lifetime_valid) >= 0) { + + r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); + if (r < 0) + return r; + } + + return 0; +} + +static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { + int r; + Link *link = userdata; + + assert(link); + assert(link->network); + assert(link->manager); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch(event) { + case SD_DHCP6_CLIENT_EVENT_STOP: + case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: + case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: + if (sd_dhcp6_client_get_lease(client, NULL) >= 0) + log_link_warning(link, "DHCPv6 lease lost"); + + link->dhcp6_configured = false; + break; + + case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: + r = dhcp6_lease_address_acquired(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } + + /* fall through */ + case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: + r = dhcp6_lease_information_acquired(client, link); + if (r < 0) { + link_enter_failed(link); + return; + } + + link->dhcp6_configured = true; + break; + + default: + if (event < 0) + log_link_warning_errno(link, event, "DHCPv6 error: %m"); + else + log_link_warning(link, "DHCPv6 unknown event: %d", event); + return; + } + + link_check_ready(link); +} + +int dhcp6_request_address(Link *link, int ir) { + int r, inf_req; + bool running; + + assert(link); + assert(link->dhcp6_client); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + + r = sd_dhcp6_client_is_running(link->dhcp6_client); + if (r < 0) + return r; + else + running = !!r; + + if (running) { + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); + if (r < 0) + return r; + + if (inf_req == ir) + return 0; + + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return r; + } else { + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0) + return r; + } + + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir); + if (r < 0) + return r; + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + return r; + + return 0; +} + +int dhcp6_configure(Link *link) { + sd_dhcp6_client *client = NULL; + int r; + const DUID *duid; + + assert(link); + + if (link->dhcp6_client) + return 0; + + r = sd_dhcp6_client_new(&client); + if (r < 0) + return r; + + r = sd_dhcp6_client_attach_event(client, NULL, 0); + if (r < 0) + goto error; + + r = sd_dhcp6_client_set_mac(client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); + if (r < 0) + goto error; + + r = sd_dhcp6_client_set_iaid(client, link->network->iaid); + if (r < 0) + goto error; + + duid = link_duid(link); + r = sd_dhcp6_client_set_duid(client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + goto error; + + r = sd_dhcp6_client_set_ifindex(client, link->ifindex); + if (r < 0) + goto error; + + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); + if (r < 0) + goto error; + + link->dhcp6_client = client; + + return 0; + +error: + sd_dhcp6_client_unref(client); + return r; +} diff --git a/src/grp-network/libnetworkd-core/networkd-fdb.c b/src/grp-network/libnetworkd-core/networkd-fdb.c new file mode 100644 index 0000000000..2949def65b --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-fdb.c @@ -0,0 +1,254 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/conf-parser.h" +#include "shared/vlan-util.h" + +#include "networkd-fdb.h" +#include "networkd.h" + +#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U + +/* create a new FDB entry or get an existing one. */ +int fdb_entry_new_static( + Network *network, + unsigned section, + FdbEntry **ret) { + + _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; + struct ether_addr *mac_addr = NULL; + + assert(network); + assert(ret); + + /* search entry in hashmap first. */ + if (section) { + fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section)); + if (fdb_entry) { + *ret = fdb_entry; + fdb_entry = NULL; + + return 0; + } + } + + if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX) + return -E2BIG; + + /* allocate space for MAC address. */ + mac_addr = new0(struct ether_addr, 1); + if (!mac_addr) + return -ENOMEM; + + /* allocate space for and FDB entry. */ + fdb_entry = new0(FdbEntry, 1); + if (!fdb_entry) { + /* free previously allocated space for mac_addr. */ + free(mac_addr); + return -ENOMEM; + } + + /* init FDB structure. */ + fdb_entry->network = network; + fdb_entry->mac_addr = mac_addr; + + LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry); + network->n_static_fdb_entries++; + + if (section) { + fdb_entry->section = section; + hashmap_put(network->fdb_entries_by_section, + UINT_TO_PTR(fdb_entry->section), fdb_entry); + } + + /* return allocated FDB structure. */ + *ret = fdb_entry; + fdb_entry = NULL; + + return 0; +} + +static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_error_errno(link, r, "Could not add FDB entry: %m"); + + return 1; +} + +/* send a request to the kernel to add a FDB entry in its static MAC table. */ +int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + sd_netlink *rtnl; + int r; + + assert(link); + assert(link->manager); + assert(fdb_entry); + + rtnl = link->manager->rtnl; + + /* create new RTM message */ + r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE); + if (r < 0) + return rtnl_log_create_error(r); + + /* only NTF_SELF flag supported. */ + r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF); + if (r < 0) + return rtnl_log_create_error(r); + + /* only NUD_PERMANENT state supported. */ + r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr); + if (r < 0) + return rtnl_log_create_error(r); + + /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */ + if (0 != fdb_entry->vlan_id) { + r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id); + if (r < 0) + return rtnl_log_create_error(r); + } + + /* send message to the kernel to update its internal static MAC table. */ + r = sd_netlink_call_async(rtnl, req, set_fdb_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + return 0; +} + +/* remove and FDB entry. */ +void fdb_entry_free(FdbEntry *fdb_entry) { + if (!fdb_entry) + return; + + if (fdb_entry->network) { + LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry); + + assert(fdb_entry->network->n_static_fdb_entries > 0); + fdb_entry->network->n_static_fdb_entries--; + + if (fdb_entry->section) + hashmap_remove(fdb_entry->network->fdb_entries_by_section, UINT_TO_PTR(fdb_entry->section)); + } + + free(fdb_entry->mac_addr); + + free(fdb_entry); +} + +/* parse the HW address from config files. */ +int config_parse_fdb_hwaddr( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = fdb_entry_new_static(network, section_line, &fdb_entry); + if (r < 0) + return log_oom(); + + /* read in the MAC address for the FDB table. */ + r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &fdb_entry->mac_addr->ether_addr_octet[0], + &fdb_entry->mac_addr->ether_addr_octet[1], + &fdb_entry->mac_addr->ether_addr_octet[2], + &fdb_entry->mac_addr->ether_addr_octet[3], + &fdb_entry->mac_addr->ether_addr_octet[4], + &fdb_entry->mac_addr->ether_addr_octet[5]); + + if (ETHER_ADDR_LEN != r) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); + return 0; + } + + fdb_entry = NULL; + + return 0; +} + +/* parse the VLAN Id from config files. */ +int config_parse_fdb_vlan_id( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = fdb_entry_new_static(network, section_line, &fdb_entry); + if (r < 0) + return log_oom(); + + r = config_parse_vlanid(unit, filename, line, section, + section_line, lvalue, ltype, + rvalue, &fdb_entry->vlan_id, userdata); + if (r < 0) + return r; + + fdb_entry = NULL; + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-fdb.h b/src/grp-network/libnetworkd-core/networkd-fdb.h new file mode 100644 index 0000000000..a1ad4183dc --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-fdb.h @@ -0,0 +1,47 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/list.h" +#include "basic/macro.h" + +typedef struct Network Network; +typedef struct FdbEntry FdbEntry; +typedef struct Link Link; + +struct FdbEntry { + Network *network; + unsigned section; + + struct ether_addr *mac_addr; + uint16_t vlan_id; + + LIST_FIELDS(FdbEntry, static_fdb_entries); +}; + +int fdb_entry_new_static(Network *network, unsigned section, FdbEntry **ret); +void fdb_entry_free(FdbEntry *fdb_entry); +int fdb_entry_configure(Link *link, FdbEntry *fdb_entry); + +DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free); +#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep) + +int config_parse_fdb_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_fdb_vlan_id(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-gperf.gperf b/src/grp-network/libnetworkd-core/networkd-gperf.gperf new file mode 100644 index 0000000000..6588bada51 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-gperf.gperf @@ -0,0 +1,20 @@ +%{ +#include + +#include "shared/conf-parser.h" + +#include "networkd-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name networkd_gperf_hash +%define lookup-function-name networkd_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/grp-network/libnetworkd-core/networkd-ipv4ll.c b/src/grp-network/libnetworkd-core/networkd-ipv4ll.c new file mode 100644 index 0000000000..5c6ffe30a7 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-ipv4ll.c @@ -0,0 +1,243 @@ +/*** + This file is part of systemd. + + Copyright 2013-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 . +***/ + +#include + +#include + +#include "systemd-network/network-internal.h" + +#include "networkd.h" + +static int ipv4ll_address_lost(Link *link) { + _cleanup_address_free_ Address *address = NULL; + _cleanup_route_free_ Route *route = NULL; + struct in_addr addr; + int r; + + assert(link); + + link->ipv4ll_route = false; + link->ipv4ll_address = false; + + r = sd_ipv4ll_get_address(link->ipv4ll, &addr); + if (r < 0) + return 0; + + log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr)); + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return r; + } + + address->family = AF_INET; + address->in_addr.in = addr; + address->prefixlen = 16; + address->scope = RT_SCOPE_LINK; + + address_remove(address, link, link_address_remove_handler); + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return r; + } + + route->family = AF_INET; + route->scope = RT_SCOPE_LINK; + route->priority = IPV4LL_ROUTE_METRIC; + + route_remove(route, link, link_route_remove_handler); + + link_check_ready(link); + + return 0; +} + +static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(!link->ipv4ll_route); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + 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_check_ready(link); + + return 1; +} + +static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(!link->ipv4ll_address); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "could not set ipv4ll address: %m"); + link_enter_failed(link); + } else if (r >= 0) + manager_rtnl_process_address(rtnl, m, link->manager); + + link->ipv4ll_address = true; + + if (link->ipv4ll_route == true) + link_check_ready(link); + + return 1; +} + +static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { + _cleanup_address_free_ Address *ll_addr = NULL; + _cleanup_route_free_ Route *route = NULL; + struct in_addr address; + int r; + + assert(ll); + assert(link); + + r = sd_ipv4ll_get_address(ll, &address); + if (r == -ENOENT) + return 0; + else if (r < 0) + return r; + + log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u", + ADDRESS_FMT_VAL(address)); + + r = address_new(&ll_addr); + if (r < 0) + return r; + + ll_addr->family = AF_INET; + ll_addr->in_addr.in = address; + ll_addr->prefixlen = 16; + ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); + ll_addr->scope = RT_SCOPE_LINK; + + r = address_configure(ll_addr, link, ipv4ll_address_handler, false); + if (r < 0) + return r; + + link->ipv4ll_address = false; + + r = route_new(&route); + if (r < 0) + return r; + + route->family = AF_INET; + route->scope = RT_SCOPE_LINK; + route->protocol = RTPROT_STATIC; + route->priority = IPV4LL_ROUTE_METRIC; + + r = route_configure(route, link, ipv4ll_route_handler); + if (r < 0) + return r; + + link->ipv4ll_route = false; + + return 0; +} + +static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { + Link *link = userdata; + 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_IPV4LL_EVENT_STOP: + case SD_IPV4LL_EVENT_CONFLICT: + r = ipv4ll_address_lost(link); + if (r < 0) { + link_enter_failed(link); + return; + } + break; + case SD_IPV4LL_EVENT_BIND: + r = ipv4ll_address_claimed(ll, link); + if (r < 0) { + link_enter_failed(link); + return; + } + break; + default: + log_link_warning(link, "IPv4 link-local unknown event: %d", event); + break; + } +} + +int ipv4ll_configure(Link *link) { + uint64_t seed; + int r; + + assert(link); + assert(link->network); + assert(link->network->link_local & ADDRESS_FAMILY_IPV4); + + 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); + if (r >= 0) { + r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); + if (r < 0) + return r; + } + } + + r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); + if (r < 0) + return r; + + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (r < 0) + return r; + + r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); + if (r < 0) + return r; + + r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); + if (r < 0) + return r; + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-link-bus.c b/src/grp-network/libnetworkd-core/networkd-link-bus.c new file mode 100644 index 0000000000..aa352f447a --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-link-bus.c @@ -0,0 +1,141 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/parse-util.h" +#include "basic/strv.h" +#include "shared/bus-util.h" + +#include "networkd-link.h" +#include "networkd.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); + +const sd_bus_vtable link_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_VTABLE_END +}; + +static char *link_bus_path(Link *link) { + _cleanup_free_ char *ifindex = NULL; + char *p; + int r; + + assert(link); + assert(link->ifindex > 0); + + if (asprintf(&ifindex, "%d", link->ifindex) < 0) + return NULL; + + r = sd_bus_path_encode("/org/freedesktop/network1/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; + unsigned c = 0; + Link *link; + Iterator i; + + 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; +} + +int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + _cleanup_free_ char *identifier = NULL; + Manager *m = userdata; + Link *link; + int ifindex, r; + + assert(bus); + assert(path); + assert(interface); + assert(m); + assert(found); + + r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier); + if (r <= 0) + return 0; + + r = parse_ifindex(identifier, &ifindex); + if (r < 0) + return 0; + + r = link_get(m, ifindex, &link); + if (r < 0) + return 0; + + *found = link; + + return 1; +} + +int link_send_changed(Link *link, const char *property, ...) { + _cleanup_free_ char *p = NULL; + char **l; + + assert(link); + assert(link->manager); + + if (!link->manager->bus) + return 0; /* replace with assert when we have kdbus */ + + l = strv_from_stdarg_alloca(property); + + p = link_bus_path(link); + if (!p) + return -ENOMEM; + + return sd_bus_emit_properties_changed_strv( + link->manager->bus, + p, + "org.freedesktop.network1.Link", + l); +} diff --git a/src/grp-network/libnetworkd-core/networkd-link.c b/src/grp-network/libnetworkd-core/networkd-link.c new file mode 100644 index 0000000000..d73629d679 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-link.c @@ -0,0 +1,3412 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/set.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "sd-netlink/netlink-util.h" +#include "shared/bus-util.h" +#include "shared/udev-util.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/network-internal.h" + +#include "networkd-lldp-tx.h" +#include "networkd-ndisc.h" +#include "networkd.h" + +static bool link_dhcp6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->dhcp & ADDRESS_FAMILY_IPV6; +} + +static bool link_dhcp4_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->dhcp & ADDRESS_FAMILY_IPV4; +} + +static bool link_dhcp4_server_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->dhcp_server; +} + +static bool link_ipv4ll_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->link_local & ADDRESS_FAMILY_IPV4; +} + +static bool link_ipv6ll_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + return link->network->link_local & ADDRESS_FAMILY_IPV6; +} + +static bool link_ipv6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->network->bridge) + return false; + + /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */ + return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); +} + +static bool link_lldp_rx_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + if (link->network->bridge) + return false; + + return link->network->lldp_mode != LLDP_MODE_NO; +} + +static bool link_lldp_emit_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + return link->network->lldp_emit != LLDP_EMIT_NO; +} + +static bool link_ipv4_forward_enabled(Link *link) { + assert(link); + + 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_IPV4; +} + +static bool link_ipv6_forward_enabled(Link *link) { + assert(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; +} + +static bool link_proxy_arp_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->proxy_arp < 0) + return false; + + return true; +} + +static bool link_ipv6_accept_ra_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + 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) { + assert(link); + + if (!socket_ipv6_is_supported()) + return _IPV6_PRIVACY_EXTENSIONS_INVALID; + + if (link->flags & IFF_LOOPBACK) + return _IPV6_PRIVACY_EXTENSIONS_INVALID; + + if (!link->network) + return _IPV6_PRIVACY_EXTENSIONS_INVALID; + + return link->network->ipv6_privacy_extensions; +} + +static int link_enable_ipv6(Link *link) { + const char *p = NULL; + bool disabled; + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + disabled = !link_ipv6_enabled(link); + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); + + r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); + else { + if (disabled) + log_link_info(link, "IPv6 disabled for interface: %m"); + else + log_link_info(link, "IPv6 enabled for interface: %m"); + } + + return 0; +} + +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); + } +} + +#define FLAG_STRING(string, flag, old, new) \ + (((old ^ new) & flag) \ + ? ((old & flag) ? (" -" string) : (" +" string)) \ + : "") + +static int link_update_flags(Link *link, sd_netlink_message *m) { + unsigned flags, unknown_flags_added, unknown_flags_removed, unknown_flags; + uint8_t operstate; + int r; + + assert(link); + + r = sd_rtnl_message_link_get_flags(m, &flags); + if (r < 0) + return log_link_warning_errno(link, r, "Could not get link flags: %m"); + + r = sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &operstate); + if (r < 0) + /* if we got a message without operstate, take it to mean + the state was unchanged */ + operstate = link->kernel_operstate; + + if ((link->flags == flags) && (link->kernel_operstate == operstate)) + return 0; + + if (link->flags != flags) { + log_link_debug(link, "Flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags), + FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags), + FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags), + FLAG_STRING("UP", IFF_UP, link->flags, flags), + FLAG_STRING("DORMANT", IFF_DORMANT, link->flags, flags), + FLAG_STRING("LOWER_UP", IFF_LOWER_UP, link->flags, flags), + FLAG_STRING("RUNNING", IFF_RUNNING, link->flags, flags), + FLAG_STRING("MULTICAST", IFF_MULTICAST, link->flags, flags), + FLAG_STRING("BROADCAST", IFF_BROADCAST, link->flags, flags), + FLAG_STRING("POINTOPOINT", IFF_POINTOPOINT, link->flags, flags), + FLAG_STRING("PROMISC", IFF_PROMISC, link->flags, flags), + FLAG_STRING("ALLMULTI", IFF_ALLMULTI, link->flags, flags), + FLAG_STRING("PORTSEL", IFF_PORTSEL, link->flags, flags), + FLAG_STRING("AUTOMEDIA", IFF_AUTOMEDIA, link->flags, flags), + FLAG_STRING("DYNAMIC", IFF_DYNAMIC, link->flags, flags), + FLAG_STRING("NOARP", IFF_NOARP, link->flags, flags), + FLAG_STRING("NOTRAILERS", IFF_NOTRAILERS, link->flags, flags), + FLAG_STRING("DEBUG", IFF_DEBUG, link->flags, flags), + FLAG_STRING("ECHO", IFF_ECHO, link->flags, flags)); + + unknown_flags = ~(IFF_LOOPBACK | IFF_MASTER | IFF_SLAVE | IFF_UP | + IFF_DORMANT | IFF_LOWER_UP | IFF_RUNNING | + IFF_MULTICAST | IFF_BROADCAST | IFF_POINTOPOINT | + IFF_PROMISC | IFF_ALLMULTI | IFF_PORTSEL | + IFF_AUTOMEDIA | IFF_DYNAMIC | IFF_NOARP | + IFF_NOTRAILERS | IFF_DEBUG | IFF_ECHO); + unknown_flags_added = ((link->flags ^ flags) & flags & unknown_flags); + unknown_flags_removed = ((link->flags ^ flags) & link->flags & unknown_flags); + + /* link flags are currently at most 18 bits, let's align to + * printing 20 */ + if (unknown_flags_added) + log_link_debug(link, + "Unknown link flags gained: %#.5x (ignoring)", + unknown_flags_added); + + if (unknown_flags_removed) + log_link_debug(link, + "Unknown link flags lost: %#.5x (ignoring)", + unknown_flags_removed); + } + + link->flags = flags; + link->kernel_operstate = operstate; + + link_update_operstate(link); + + return 0; +} + +static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { + _cleanup_link_unref_ Link *link = NULL; + uint16_t type; + const char *ifname, *kind = NULL; + int r, ifindex; + unsigned short iftype; + + assert(manager); + assert(message); + assert(ret); + + /* check for link kind */ + r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); + if (r == 0) { + (void)sd_netlink_message_read_string(message, IFLA_INFO_KIND, &kind); + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) + return r; + else if (type != RTM_NEWLINK) + return -EINVAL; + + r = sd_rtnl_message_link_get_ifindex(message, &ifindex); + if (r < 0) + return r; + else if (ifindex <= 0) + return -EINVAL; + + r = sd_rtnl_message_link_get_type(message, &iftype); + if (r < 0) + return r; + + r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname); + if (r < 0) + return r; + + link = new0(Link, 1); + if (!link) + return -ENOMEM; + + link->n_ref = 1; + link->manager = manager; + link->state = LINK_STATE_PENDING; + link->rtnl_extended_attrs = true; + link->ifindex = ifindex; + link->iftype = iftype; + link->ifname = strdup(ifname); + if (!link->ifname) + return -ENOMEM; + + if (kind) { + link->kind = strdup(kind); + if (!link->kind) + return -ENOMEM; + } + + r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); + if (r < 0) + log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); + + if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) + return -ENOMEM; + + if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) + return -ENOMEM; + + if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) + return -ENOMEM; + + r = hashmap_ensure_allocated(&manager->links, NULL); + if (r < 0) + return r; + + r = hashmap_put(manager->links, INT_TO_PTR(link->ifindex), link); + if (r < 0) + return r; + + r = link_update_flags(link, message); + if (r < 0) + return r; + + *ret = link; + link = NULL; + + return 0; +} + +static void link_free(Link *link) { + Address *address; + Iterator i; + Link *carrier; + + if (!link) + return; + + 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); + address_free(address); + } + + sd_dhcp_server_unref(link->dhcp_server); + sd_dhcp_client_unref(link->dhcp_client); + sd_dhcp_lease_unref(link->dhcp_lease); + + link_lldp_emit_stop(link); + + free(link->lease_file); + + sd_lldp_unref(link->lldp); + free(link->lldp_file); + + sd_ipv4ll_unref(link->ipv4ll); + sd_dhcp6_client_unref(link->dhcp6_client); + sd_ndisc_unref(link->ndisc); + + set_free_free(link->ndisc_rdnss); + set_free_free(link->ndisc_dnssl); + + if (link->manager) + hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); + + free(link->ifname); + + free(link->kind); + + (void)unlink(link->state_file); + free(link->state_file); + + udev_device_unref(link->udev_device); + + HASHMAP_FOREACH (carrier, link->bound_to_links, i) + hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex)); + hashmap_free(link->bound_to_links); + + HASHMAP_FOREACH (carrier, link->bound_by_links, i) + hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); + hashmap_free(link->bound_by_links); + + free(link); +} + +Link *link_unref(Link *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) + return NULL; + + assert(link->n_ref > 0); + + link->n_ref++; + + return link; +} + +int link_get(Manager *m, int ifindex, Link **ret) { + Link *link; + + assert(m); + assert(ifindex); + assert(ret); + + link = hashmap_get(m->links, INT_TO_PTR(ifindex)); + if (!link) + return -ENODEV; + + *ret = link; + + return 0; +} + +static void link_set_state(Link *link, LinkState state) { + assert(link); + + if (link->state == state) + return; + + link->state = state; + + link_send_changed(link, "AdministrativeState", NULL); +} + +static void link_enter_unmanaged(Link *link) { + assert(link); + + log_link_debug(link, "Unmanaged"); + + link_set_state(link, LINK_STATE_UNMANAGED); + + link_dirty(link); +} + +static int link_stop_clients(Link *link) { + int r = 0, k; + + assert(link); + assert(link->manager); + assert(link->manager->event); + + if (link->dhcp_client) { + k = sd_dhcp_client_stop(link->dhcp_client); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); + } + + if (link->ipv4ll) { + k = sd_ipv4ll_stop(link->ipv4ll); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); + } + + if (link->dhcp6_client) { + k = sd_dhcp6_client_stop(link->dhcp6_client); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); + } + + if (link->ndisc) { + k = sd_ndisc_stop(link->ndisc); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); + } + + link_lldp_emit_stop(link); + return r; +} + +void link_enter_failed(Link *link) { + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + log_link_warning(link, "Failed"); + + link_set_state(link, LINK_STATE_FAILED); + + link_stop_clients(link); + + link_dirty(link); +} + +static Address* link_find_dhcp_server_address(Link *link) { + Address *address; + + assert(link); + assert(link->network); + + /* The first statically configured address if there is any */ + LIST_FOREACH(addresses, address, link->network->static_addresses) { + + if (address->family != AF_INET) + continue; + + if (in_addr_is_null(address->family, &address->in_addr)) + continue; + + return address; + } + + /* If that didn't work, find a suitable address we got from the pool */ + LIST_FOREACH(addresses, address, link->pool_addresses) { + if (address->family != AF_INET) + continue; + + return address; + } + + return NULL; +} + +static int link_enter_configured(Link *link) { + assert(link); + assert(link->network); + assert(link->state == LINK_STATE_SETTING_ROUTES); + + log_link_info(link, "Configured"); + + link_set_state(link, LINK_STATE_CONFIGURED); + + link_dirty(link); + + return 0; +} + +void link_check_ready(Link *link) { + Address *a; + Iterator i; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + if (!link->network) + return; + + if (!link->static_configured) + return; + + if (link_ipv4ll_enabled(link)) + if (!link->ipv4ll_address || + !link->ipv4ll_route) + return; + + if (link_ipv6ll_enabled(link)) + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) + return; + + 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); + + return; +} + +static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link->link_messages > 0); + assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, + LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED, + LINK_STATE_LINGER)); + + link->link_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + 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_check_ready(link); + } + + return 1; +} + +static int link_enter_set_routes(Link *link) { + Route *rt; + int r; + + assert(link); + assert(link->network); + assert(link->state == LINK_STATE_SETTING_ADDRESSES); + + link_set_state(link, LINK_STATE_SETTING_ROUTES); + + LIST_FOREACH(routes, rt, link->network->static_routes) { + r = route_configure(rt, link, route_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set routes: %m"); + link_enter_failed(link); + return r; + } + + link->link_messages++; + } + + if (link->link_messages == 0) { + link->static_configured = true; + link_check_ready(link); + } else + log_link_debug(link, "Setting routes"); + + return 0; +} + +int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -ESRCH) + log_link_warning_errno(link, r, "Could not drop route: %m"); + + return 1; +} + +static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, + LINK_STATE_FAILED, LINK_STATE_LINGER)); + + link->link_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "could not set address: %m"); + else if (r >= 0) + manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->link_messages == 0) { + log_link_debug(link, "Addresses set"); + link_enter_set_routes(link); + } + + return 1; +} + +static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { + _cleanup_free_ struct in_addr *addresses = NULL; + size_t n_addresses = 0, n_allocated = 0; + char **a; + + log_debug("Copying DNS server information from %s", link->ifname); + + if (!link->network) + return 0; + + STRV_FOREACH(a, link->network->dns) { + struct in_addr ia; + + /* Only look for IPv4 addresses */ + if (inet_pton(AF_INET, *a, &ia) <= 0) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia; + } + + if (link->network->dhcp_use_dns && + link->dhcp_lease) { + const struct in_addr *da = NULL; + int n; + + n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); + if (n > 0) { + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) + return log_oom(); + + memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); + n_addresses += n; + } + } + + if (n_addresses <= 0) + return 0; + + return sd_dhcp_server_set_dns(s, addresses, n_addresses); +} + +static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { + _cleanup_free_ struct in_addr *addresses = NULL; + size_t n_addresses = 0, n_allocated = 0; + char **a; + + if (!link->network) + return 0; + + log_debug("Copying NTP server information from %s", link->ifname); + + STRV_FOREACH(a, link->network->ntp) { + struct in_addr ia; + + /* Only look for IPv4 addresses */ + if (inet_pton(AF_INET, *a, &ia) <= 0) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia; + } + + if (link->network->dhcp_use_ntp && + link->dhcp_lease) { + const struct in_addr *da = NULL; + int n; + + n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da); + if (n > 0) { + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) + return log_oom(); + + memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); + n_addresses += n; + } + } + + if (n_addresses <= 0) + return 0; + + return sd_dhcp_server_set_ntp(s, addresses, n_addresses); +} + +static int link_enter_set_addresses(Link *link) { + Address *ad; + int r; + + assert(link); + assert(link->network); + assert(link->state != _LINK_STATE_INVALID); + + link_set_state(link, LINK_STATE_SETTING_ADDRESSES); + + LIST_FOREACH(addresses, ad, link->network->static_addresses) { + 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); + return r; + } + + link->link_messages++; + } + + /* now that we can figure out a default address for the dhcp server, + start it */ + if (link_dhcp4_server_enabled(link)) { + Address *address; + Link *uplink = NULL; + bool acquired_uplink = false; + + address = link_find_dhcp_server_address(link); + if (!address) { + log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance."); + link_enter_failed(link); + return 0; + } + + /* use the server address' subnet as the pool */ + r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, + link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); + if (r < 0) + return r; + + /* TODO: + r = sd_dhcp_server_set_router(link->dhcp_server, + &main_address->in_addr.in); + if (r < 0) + return r; + */ + + if (link->network->dhcp_server_max_lease_time_usec > 0) { + r = sd_dhcp_server_set_max_lease_time( + link->dhcp_server, + DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); + if (r < 0) + return r; + } + + if (link->network->dhcp_server_default_lease_time_usec > 0) { + r = sd_dhcp_server_set_default_lease_time( + link->dhcp_server, + DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); + if (r < 0) + return r; + } + + if (link->network->dhcp_server_emit_dns) { + + if (link->network->n_dhcp_server_dns > 0) + r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns); + else { + uplink = manager_find_uplink(link->manager, link); + acquired_uplink = true; + + if (!uplink) { + log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); + r = 0; + } else + r = link_push_dns_to_dhcp_server(uplink, link->dhcp_server); + } + if (r < 0) + log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); + } + + + if (link->network->dhcp_server_emit_ntp) { + + if (link->network->n_dhcp_server_ntp > 0) + r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp); + else { + if (!acquired_uplink) + uplink = manager_find_uplink(link->manager, link); + + if (!uplink) { + log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); + r = 0; + } else + r = link_push_ntp_to_dhcp_server(uplink, link->dhcp_server); + + } + if (r < 0) + log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); + } + + r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m"); + return r; + } + + if (link->network->dhcp_server_emit_timezone) { + _cleanup_free_ char *buffer = NULL; + const char *tz = NULL; + + if (link->network->dhcp_server_timezone) + tz = link->network->dhcp_server_timezone; + else { + r = get_timezone(&buffer); + if (r < 0) + log_warning_errno(r, "Failed to determine timezone: %m"); + else + tz = buffer; + } + + if (tz) { + r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); + if (r < 0) + return r; + } + } + + r = sd_dhcp_server_start(link->dhcp_server); + if (r < 0) { + log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m"); + + link_enter_failed(link); + + return 0; + } + + log_link_debug(link, "Offering DHCPv4 leases"); + } + + if (link->link_messages == 0) + link_enter_set_routes(link); + else + log_link_debug(link, "Setting addresses"); + + return 0; +} + +int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EADDRNOTAVAIL) + log_link_warning_errno(link, r, "Could not drop address: %m"); + + return 1; +} + +static int link_set_bridge_vlan(Link *link) { + int r = 0; + + r = br_vlan_configure(link, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); + if (r < 0) + log_link_error_errno(link, r, "Failed to assign VLANs to bridge port: %m"); + + return r; +} + +static int link_set_bridge_fdb(Link *link) { + FdbEntry *fdb_entry; + int r = 0; + + LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { + r = fdb_entry_configure(link, fdb_entry); + if (r < 0) { + log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); + break; + } + } + + return r; +} + +static int link_set_proxy_arp(Link *link) { + const char *p = NULL; + int r; + + if (!link_proxy_arp_enabled(link)) + return 0; + + p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); + + r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); + + return 0; +} + +static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + log_link_debug(link, "Set link"); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not join netdev: %m"); + link_enter_failed(link); + return 1; + } + + return 0; +} + +static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + _cleanup_link_unref_ Link *link = userdata; + const sd_bus_error *e; + + assert(m); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + e = sd_bus_message_get_error(m); + if (e) + log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message); + + return 1; +} + +int link_set_hostname(Link *link, const char *hostname) { + int r; + + assert(link); + assert(link->manager); + + log_link_debug(link, "Setting transient hostname: '%s'", strna(hostname)); + + if (!link->manager->bus) { + /* TODO: replace by assert when we can rely on kdbus */ + log_link_info(link, "Not connected to system bus, ignoring transient hostname."); + return 0; + } + + r = sd_bus_call_method_async( + link->manager->bus, + NULL, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetHostname", + set_hostname_handler, + link, + "sb", + hostname, + false); + + if (r < 0) + return log_link_error_errno(link, r, "Could not set transient hostname: %m"); + + link_ref(link); + + return 0; +} + +static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + _cleanup_link_unref_ Link *link = userdata; + const sd_bus_error *e; + + assert(m); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + e = sd_bus_message_get_error(m); + if (e) + log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message); + + return 1; +} + +int link_set_timezone(Link *link, const char *tz) { + int r; + + assert(link); + assert(link->manager); + assert(tz); + + log_link_debug(link, "Setting system timezone: '%s'", tz); + + if (!link->manager->bus) { + log_link_info(link, "Not connected to system bus, ignoring timezone."); + return 0; + } + + r = sd_bus_call_method_async( + link->manager->bus, + NULL, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTimezone", + set_timezone_handler, + link, + "sb", + tz, + false); + if (r < 0) + return log_link_error_errno(link, r, "Could not set timezone: %m"); + + link_ref(link); + + return 0; +} + +static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, r, "Could not set MTU: %m"); + + return 1; +} + +int link_set_mtu(Link *link, uint32_t mtu) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Setting MTU: %" PRIu32, mtu); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_netlink_message_append_u32(req, IFLA_MTU, mtu); + if (r < 0) + return log_link_error_errno(link, r, "Could not append MTU: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static int link_set_bridge(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->network); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); + if (r < 0) + return log_link_error_errno(link, r, "Could not set message family: %m"); + + r = sd_netlink_message_open_container(req, IFLA_PROTINFO); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); + + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m"); + + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m"); + + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); + + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); + + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); + + if (link->network->cost != 0) { + r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, link_set_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return r; +} + +static int link_lldp_save(Link *link) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + sd_lldp_neighbor **l = NULL; + int n = 0, r, i; + + assert(link); + assert(link->lldp_file); + + if (!link->lldp) { + (void) unlink(link->lldp_file); + return 0; + } + + r = sd_lldp_get_neighbors(link->lldp, &l); + if (r < 0) + goto finish; + if (r == 0) { + (void) unlink(link->lldp_file); + goto finish; + } + + n = r; + + r = fopen_temporary(link->lldp_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + for (i = 0; i < n; i++) { + const void *p; + le64_t u; + size_t sz; + + r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); + if (r < 0) + goto finish; + + u = htole64(sz); + (void) fwrite(&u, 1, sizeof(u), f); + (void) fwrite(p, 1, sz, f); + } + + r = fflush_and_check(f); + if (r < 0) + goto finish; + + if (rename(temp_path, link->lldp_file) < 0) { + r = -errno; + goto finish; + } + +finish: + if (r < 0) { + (void) unlink(link->lldp_file); + if (temp_path) + (void) unlink(temp_path); + + log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); + } + + if (l) { + for (i = 0; i < n; i++) + sd_lldp_neighbor_unref(l[i]); + free(l); + } + + return r; +} + +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + (void) link_lldp_save(link); + + if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + /* If we received information about a new neighbor, restart the LLDP "fast" logic */ + + log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); + + r = link_lldp_emit_start(link); + if (r < 0) + log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); + } +} + +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); + + /* start DHCPv6 client in stateless mode */ + r = dhcp6_request_address(link, true); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + else + log_link_debug(link, "Acquiring DHCPv6 lease"); + } + + if (link_ipv6_accept_ra_enabled(link)) { + assert(link->ndisc); + + log_link_debug(link, "Discovering IPv6 routers"); + + r = sd_ndisc_start(link->ndisc); + 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_ipv4_conf(Link *link) { + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(link->manager->event); + + if (link_ipv4ll_enabled(link)) { + assert(link->ipv4ll); + + log_link_debug(link, "Acquiring IPv4 link-local address"); + + r = sd_ipv4ll_start(link->ipv4ll); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); + } + + if (link_dhcp4_enabled(link)) { + assert(link->dhcp_client); + + log_link_debug(link, "Acquiring DHCPv4 lease"); + + r = sd_dhcp_client_start(link->dhcp_client); + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); + } + + return 0; +} + +static int link_acquire_conf(Link *link) { + int r; + + assert(link); + + r = link_acquire_ipv4_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; + } + + if (link_lldp_emit_enabled(link)) { + r = link_lldp_emit_start(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); + } + + return 0; +} + +bool link_has_carrier(Link *link) { + /* see Documentation/networking/operstates.txt in the kernel sources */ + + if (link->kernel_operstate == IF_OPER_UP) + return true; + + if (link->kernel_operstate == IF_OPER_UNKNOWN) + /* operstate may not be implemented, so fall back to flags */ + if ((link->flags & IFF_LOWER_UP) && !(link->flags & IFF_DORMANT)) + return true; + + return false; +} + +static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + /* we warn but don't fail the link, as it may be + brought up later */ + log_link_warning_errno(link, r, "Could not bring up interface: %m"); + + return 1; +} + +static int link_up(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + uint8_t ipv6ll_mode; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Bringing link up"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + /* set it free if not enslaved with networkd */ + if (!link->network->bridge && !link->network->bond && !link->network->vrf) { + r = sd_netlink_message_append_u32(req, IFLA_MASTER, 0); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_MASTER attribute: %m"); + } + + r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + if (link->network->mac) { + r = sd_netlink_message_append_ether_addr(req, IFLA_ADDRESS, link->network->mac); + if (r < 0) + return log_link_error_errno(link, r, "Could not set MAC address: %m"); + } + + /* If IPv6 not configured (no static IPv6 address and IPv6LL autoconfiguration is disabled) + for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */ + (void) link_enable_ipv6(link); + + if (link->network->mtu) { + /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes + on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */ + if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) { + + log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " + "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); + + link->network->mtu = IPV6_MIN_MTU; + } + + r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu); + if (r < 0) + return log_link_error_errno(link, r, "Could not set MTU: %m"); + } + + r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); + if (r < 0) + return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); + + if (link_ipv6_enabled(link)) { + /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ + r = sd_netlink_message_open_container(req, AF_INET6); + if (r < 0) + return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m"); + + if (!link_ipv6ll_enabled(link)) + ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE; + else { + const char *p = NULL; + _cleanup_free_ char *stable_secret = NULL; + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/stable_secret"); + r = read_one_line_file(p, &stable_secret); + + if (r < 0) + ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; + else + ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; + } + r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m"); + + if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) { + r = sd_netlink_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, r, "Could not bring down interface: %m"); + + return 1; +} + +static int link_down(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Bringing link down"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, + RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, link_down_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +static int link_handle_bound_to_list(Link *link) { + Link *l; + Iterator i; + int r; + bool required_up = false; + bool link_is_up = false; + + assert(link); + + if (hashmap_isempty(link->bound_to_links)) + return 0; + + if (link->flags & IFF_UP) + link_is_up = true; + + HASHMAP_FOREACH (l, link->bound_to_links, i) + if (link_has_carrier(l)) { + required_up = true; + break; + } + + if (!required_up && link_is_up) { + r = link_down(link); + if (r < 0) + return r; + } else if (required_up && !link_is_up) { + r = link_up(link); + if (r < 0) + return r; + } + + return 0; +} + +static int link_handle_bound_by_list(Link *link) { + Iterator i; + Link *l; + int r; + + assert(link); + + if (hashmap_isempty(link->bound_by_links)) + return 0; + + HASHMAP_FOREACH (l, link->bound_by_links, i) { + r = link_handle_bound_to_list(l); + if (r < 0) + return r; + } + + return 0; +} + +static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) { + int r; + + assert(link); + assert(carrier); + + if (link == carrier) + return 0; + + if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex))) + return 0; + + r = hashmap_ensure_allocated(h, NULL); + if (r < 0) + return r; + + r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier); + if (r < 0) + return r; + + return 0; +} + +static int link_new_bound_by_list(Link *link) { + Manager *m; + Link *carrier; + Iterator i; + int r; + bool list_updated = false; + + assert(link); + assert(link->manager); + + m = link->manager; + + HASHMAP_FOREACH(carrier, m->links, i) { + if (!carrier->network) + continue; + + if (strv_isempty(carrier->network->bind_carrier)) + continue; + + if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) { + r = link_put_carrier(link, carrier, &link->bound_by_links); + if (r < 0) + return r; + + list_updated = true; + } + } + + if (list_updated) + 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_dirty(carrier); + } + + return 0; +} + +static int link_new_bound_to_list(Link *link) { + Manager *m; + Link *carrier; + Iterator i; + int r; + bool list_updated = false; + + assert(link); + assert(link->manager); + + if (!link->network) + return 0; + + if (strv_isempty(link->network->bind_carrier)) + return 0; + + m = link->manager; + + HASHMAP_FOREACH (carrier, m->links, i) { + if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) { + r = link_put_carrier(link, carrier, &link->bound_to_links); + if (r < 0) + return r; + + list_updated = true; + } + } + + if (list_updated) + 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_dirty(carrier); + } + + return 0; +} + +static int link_new_carrier_maps(Link *link) { + int r; + + r = link_new_bound_by_list(link); + if (r < 0) + return r; + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + r = link_new_bound_to_list(link); + if (r < 0) + return r; + + r = link_handle_bound_to_list(link); + if (r < 0) + return r; + + return 0; +} + +static void link_free_bound_to_list(Link *link) { + Link *bound_to; + Iterator i; + + HASHMAP_FOREACH (bound_to, link->bound_to_links, i) { + hashmap_remove(link->bound_to_links, INT_TO_PTR(bound_to->ifindex)); + + if (hashmap_remove(bound_to->bound_by_links, INT_TO_PTR(link->ifindex))) + link_dirty(bound_to); + } + + return; +} + +static void link_free_bound_by_list(Link *link) { + Link *bound_by; + Iterator i; + + HASHMAP_FOREACH (bound_by, link->bound_by_links, i) { + hashmap_remove(link->bound_by_links, INT_TO_PTR(bound_by->ifindex)); + + if (hashmap_remove(bound_by->bound_to_links, INT_TO_PTR(link->ifindex))) { + link_dirty(bound_by); + link_handle_bound_to_list(bound_by); + } + } + + return; +} + +static void link_free_carrier_maps(Link *link) { + bool list_updated = false; + + assert(link); + + if (!hashmap_isempty(link->bound_to_links)) { + link_free_bound_to_list(link); + list_updated = true; + } + + if (!hashmap_isempty(link->bound_by_links)) { + link_free_bound_by_list(link); + list_updated = true; + } + + if (list_updated) + link_dirty(link); + + return; +} + +void link_drop(Link *link) { + if (!link || link->state == LINK_STATE_LINGER) + return; + + link_set_state(link, LINK_STATE_LINGER); + + link_free_carrier_maps(link); + + log_link_debug(link, "Link removed"); + + (void)unlink(link->state_file); + link_unref(link); + + return; +} + +static int link_joined(Link *link) { + int r; + + assert(link); + assert(link->network); + + if (!hashmap_isempty(link->bound_to_links)) { + r = link_handle_bound_to_list(link); + if (r < 0) + return r; + } else if (!(link->flags & IFF_UP)) { + r = link_up(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + if (link->network->bridge) { + r = link_set_bridge(link); + if (r < 0) + log_link_error_errno(link, r, "Could not set bridge message: %m"); + } + + if (link->network->bridge || streq_ptr("bridge", link->kind)) { + r = link_set_bridge_vlan(link); + if (r < 0) + log_link_error_errno(link, r, "Could not set bridge vlan: %m"); + } + + return link_enter_set_addresses(link); +} + +static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->network); + + link->enslaving--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not join netdev: %m"); + link_enter_failed(link); + return 1; + } else + log_link_debug(link, "Joined netdev"); + + if (link->enslaving <= 0) + link_joined(link); + + return 1; +} + +static int link_enter_join_netdev(Link *link) { + NetDev *netdev; + Iterator i; + int r; + + assert(link); + assert(link->network); + assert(link->state == LINK_STATE_PENDING); + + link_set_state(link, LINK_STATE_ENSLAVING); + + link_dirty(link); + + if (!link->network->bridge && + !link->network->bond && + !link->network->vrf && + hashmap_isempty(link->network->stacked_netdevs)) + return link_joined(link); + + if (link->network->bond) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->bond), + LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname), + NULL); + + r = netdev_join(link->network->bond, link, netdev_join_handler); + if (r < 0) { + log_struct_errno(LOG_WARNING, r, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->bond), + LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bond->ifname), + NULL); + + link_enter_failed(link); + return r; + } + + link->enslaving++; + } + + if (link->network->bridge) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->bridge), + LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname), + NULL); + + r = netdev_join(link->network->bridge, link, netdev_join_handler); + if (r < 0) { + log_struct_errno(LOG_WARNING, r, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->bridge), + LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bridge->ifname), + NULL), + link_enter_failed(link); + return r; + } + + link->enslaving++; + } + + if (link->network->vrf) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->vrf), + LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->vrf->ifname), + NULL); + r = netdev_join(link->network->vrf, link, netdev_join_handler); + if (r < 0) { + log_struct_errno(LOG_WARNING, r, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(link->network->vrf), + LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->vrf->ifname), + NULL); + link_enter_failed(link); + return r; + } + + link->enslaving++; + } + + HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { + + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(netdev), + LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname), + NULL); + + r = netdev_join(netdev, link, netdev_join_handler); + if (r < 0) { + log_struct_errno(LOG_WARNING, r, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(netdev), + LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname), + NULL); + link_enter_failed(link); + return r; + } + + link->enslaving++; + } + + return 0; +} + +static int link_set_ipv4_forward(Link *link) { + int r; + + if (!link_ipv4_forward_enabled(link)) + return 0; + + /* 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; + + /* On Linux, the IPv6 stack does 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("/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"); + + 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_accept_ra(Link *link) { + const char *p = NULL; + int r; + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); + + /* We handle router advertisements 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_dad_transmits(Link *link) { + char buf[DECIMAL_STR_MAX(int) + 1]; + const char *p = NULL; + int r; + + /* Make this a NOP if IPv6 is not available */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->ipv6_dad_transmits < 0) + return 0; + + 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_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 */ + if (!socket_ipv6_is_supported()) + return 0; + + if (link->flags & IFF_LOOPBACK) + return 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; + } + + SET_FOREACH(route, link->routes_foreign, i) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + r = route_remove(route, link, link_route_remove_handler); + if (r < 0) + return r; + } + + return 0; +} + +static int link_update_lldp(Link *link) { + int r; + + assert(link); + + if (!link->lldp) + return 0; + + if (link->flags & IFF_UP) { + r = sd_lldp_start(link->lldp); + if (r > 0) + log_link_debug(link, "Started LLDP."); + } else { + r = sd_lldp_stop(link->lldp); + if (r > 0) + log_link_debug(link, "Stopped LLDP."); + } + + return r; +} + +static int link_configure(Link *link) { + int r; + + assert(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; + + r = link_set_proxy_arp(link); + if (r < 0) + return r; + + r = link_set_ipv4_forward(link); + if (r < 0) + return r; + + r = link_set_ipv6_forward(link); + if (r < 0) + return r; + + r = link_set_ipv6_privacy_extensions(link); + if (r < 0) + return r; + + r = link_set_ipv6_accept_ra(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) + return r; + } + + if (link_dhcp4_enabled(link)) { + r = dhcp4_configure(link); + if (r < 0) + return r; + } + + if (link_dhcp4_server_enabled(link)) { + r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex); + if (r < 0) + return r; + + r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0); + if (r < 0) + return r; + } + + 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; + } + + if (link_lldp_rx_enabled(link)) { + r = sd_lldp_new(&link->lldp); + if (r < 0) + return r; + + r = sd_lldp_set_ifindex(link->lldp, link->ifindex); + if (r < 0) + return r; + + r = sd_lldp_match_capabilities(link->lldp, + link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? + SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : + SD_LLDP_SYSTEM_CAPABILITIES_ALL); + if (r < 0) + return r; + + r = sd_lldp_set_filter_address(link->lldp, &link->mac); + if (r < 0) + return r; + + r = sd_lldp_attach_event(link->lldp, NULL, 0); + if (r < 0) + return r; + + r = sd_lldp_set_callback(link->lldp, lldp_handler, link); + if (r < 0) + return r; + + r = link_update_lldp(link); + if (r < 0) + return r; + } + + if (link_has_carrier(link)) { + r = link_acquire_conf(link); + if (r < 0) + return r; + } + + return link_enter_join_netdev(link); +} + +static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m, + void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + Network *network; + int r; + + assert(link); + assert(link->ifname); + assert(link->manager); + + if (link->state != LINK_STATE_PENDING) + return 1; + + log_link_debug(link, "Link state is up-to-date"); + + r = link_new_bound_by_list(link); + if (r < 0) + return r; + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + 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 (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"); + } + + r = network_apply(link->manager, network, link); + if (r < 0) + return r; + } + + r = link_new_bound_to_list(link); + if (r < 0) + return r; + + r = link_configure(link); + if (r < 0) + return r; + + return 1; +} + +int link_initialized(Link *link, struct udev_device *device) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(device); + + if (link->state != LINK_STATE_PENDING) + return 0; + + if (link->udev_device) + return 0; + + log_link_debug(link, "udev initialized link"); + + link->udev_device = udev_device_ref(device); + + /* udev has initialized the link, but we don't know if we have yet + * processed the NEWLINK messages with the latest state. Do a GETLINK, + * when it returns we know that the pending NEWLINKs have already been + * processed and that we are up-to-date */ + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, + link->ifindex); + if (r < 0) + return r; + + r = sd_netlink_call_async(link->manager->rtnl, req, + link_initialized_and_synced, link, 0, NULL); + if (r < 0) + return r; + + link_ref(link); + + return 0; +} + +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); + + 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'; + + 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; + } + + r = network_apply(link->manager, network, link); + if (r < 0) + return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file)); + } + +network_file_fail: + + if (addresses) { + p = addresses; + + for (;;) { + _cleanup_free_ char *address_str = NULL; + char *prefixlen_str; + int family; + unsigned char prefixlen; + + 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; + + prefixlen_str = strchr(address_str, '/'); + if (!prefixlen_str) { + log_link_debug(link, "Failed to parse address and prefix length %s", address_str); + continue; + } + + *prefixlen_str++ = '\0'; + + r = sscanf(prefixlen_str, "%hhu", &prefixlen); + if (r != 1) { + log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str); + continue; + } + + 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; + } + + r = address_add(link, family, &address, prefixlen, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Failed to add address: %m"); + } + } + + if (routes) { + p = routes; + + 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; + + prefixlen_str = strchr(route_str, '/'); + if (!prefixlen_str) { + log_link_debug(link, "Failed to parse route %s", route_str); + continue; + } + + *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; + } + + 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; + } + + 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"); + + 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"); + } + + route->lifetime = lifetime; + sd_event_source_unref(route->expire); + route->expire = expire; + expire = NULL; + } + } + + 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; + } + + r = sd_dhcp_client_new(&link->dhcp_client); + if (r < 0) + return log_link_error_errno(link, r, "Falied to create DHCPv4 client: %m"); + + 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 initial DHCPv4 address %s: %m", dhcp4_address); + } + +dhcp4_address_fail: + + 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 initial IPv4LL address %s: %m", ipv4ll_address); + } + +ipv4ll_address_fail: + + return 0; +} + +int link_add(Manager *m, sd_netlink_message *message, Link **ret) { + Link *link; + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + char ifindex_str[2 + DECIMAL_STR_MAX(int)]; + int r; + + assert(m); + assert(m->rtnl); + assert(message); + assert(ret); + + r = link_new(m, message, ret); + if (r < 0) + return r; + + 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) { + 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 */ + log_link_debug(link, "link pending udev initialization..."); + return 0; + } + + r = link_initialized(link, device); + if (r < 0) + 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; +} + +static int link_carrier_gained(Link *link) { + int r; + + assert(link); + + 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); + return r; + } + + r = link_enter_set_addresses(link); + if (r < 0) + return r; + } + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + return 0; +} + +static int link_carrier_lost(Link *link) { + int r; + + assert(link); + + r = link_stop_clients(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + + r = link_handle_bound_by_list(link); + if (r < 0) + return r; + + return 0; +} + +int link_carrier_reset(Link *link) { + int r; + + assert(link); + + if (link_has_carrier(link)) { + r = link_carrier_lost(link); + if (r < 0) + return r; + + r = link_carrier_gained(link); + if (r < 0) + return r; + + log_link_info(link, "Reset carrier"); + } + + return 0; +} + +int link_update(Link *link, sd_netlink_message *m) { + struct ether_addr mac; + const char *ifname; + uint32_t mtu; + bool had_carrier, carrier_gained, carrier_lost; + int r; + + assert(link); + assert(link->ifname); + assert(m); + + if (link->state == LINK_STATE_LINGER) { + link_ref(link); + log_link_info(link, "Link readded"); + link_set_state(link, LINK_STATE_ENSLAVING); + + r = link_new_carrier_maps(link); + if (r < 0) + return r; + } + + r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname); + if (r >= 0 && !streq(ifname, link->ifname)) { + log_link_info(link, "Renamed to %s", ifname); + + link_free_carrier_maps(link); + + r = free_and_strdup(&link->ifname, ifname); + if (r < 0) + return r; + + r = link_new_carrier_maps(link); + if (r < 0) + return r; + } + + r = sd_netlink_message_read_u32(m, IFLA_MTU, &mtu); + if (r >= 0 && mtu > 0) { + link->mtu = mtu; + if (!link->original_mtu) { + link->original_mtu = mtu; + log_link_debug(link, "Saved original MTU: %" PRIu32, link->original_mtu); + } + + if (link->dhcp_client) { + r = sd_dhcp_client_set_mtu(link->dhcp_client, + link->mtu); + if (r < 0) { + log_link_warning_errno(link, r, "Could not update MTU in DHCP client: %m"); + return r; + } + } + } + + /* The kernel may broadcast NEWLINK messages without the MAC address + set, simply ignore them. */ + r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac); + if (r >= 0) { + if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, + ETH_ALEN)) { + + memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, + ETH_ALEN); + + log_link_debug(link, "MAC address: " + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + 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]); + + if (link->ipv4ll) { + r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); + } + + if (link->dhcp_client) { + const DUID *duid = link_duid(link); + + r = sd_dhcp_client_set_mac(link->dhcp_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m"); + } + + if (link->dhcp6_client) { + const DUID* duid = link_duid(link); + + r = sd_dhcp6_client_set_mac(link->dhcp6_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), + ARPHRD_ETHER); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); + + r = sd_dhcp6_client_set_iaid(link->dhcp6_client, + link->network->iaid); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); + + r = sd_dhcp6_client_set_duid(link->dhcp6_client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); + } + } + } + + had_carrier = link_has_carrier(link); + + r = link_update_flags(link, m); + if (r < 0) + return r; + + r = link_update_lldp(link); + if (r < 0) + return r; + + carrier_gained = !had_carrier && link_has_carrier(link); + carrier_lost = had_carrier && !link_has_carrier(link); + + if (carrier_gained) { + log_link_info(link, "Gained carrier"); + + r = link_carrier_gained(link); + if (r < 0) + return r; + } else if (carrier_lost) { + log_link_info(link, "Lost carrier"); + + r = link_carrier_lost(link); + if (r < 0) + return r; + } + + return 0; +} + +static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { + bool space = false; + Iterator i; + Link *link; + + assert(f); + assert(prefix); + + if (hashmap_isempty(h)) + return; + + fputs(prefix, f); + HASHMAP_FOREACH(link, h, i) { + if (space) + fputc(' ', f); + + fprintf(f, "%i", link->ifindex); + space = true; + } + + fputc('\n', f); +} + +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); + assert(link->state_file); + assert(link->lease_file); + assert(link->manager); + + if (link->state == LINK_STATE_LINGER) { + unlink(link->state_file); + return 0; + } + + link_lldp_save(link); + + admin_state = link_state_to_string(link->state); + assert(admin_state); + + oper_state = link_operstate_to_string(link->operstate); + assert(oper_state); + + r = fopen_temporary(link->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "ADMIN_STATE=%s\n" + "OPER_STATE=%s\n", + admin_state, oper_state); + + if (link->network) { + bool space; + sd_dhcp6_lease *dhcp6_lease = NULL; + const char *dhcp_domainname = NULL; + char **dhcp6_domains = NULL; + + if (link->dhcp6_client) { + r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); + if (r < 0 && r != -ENOMSG) + log_link_debug(link, "No DHCPv6 lease"); + } + + fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); + + fputs("DNS=", f); + space = false; + fputstrv(f, link->network->dns, NULL, &space); + + if (link->network->dhcp_use_dns && + link->dhcp_lease) { + const struct in_addr *addresses; + + r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_use_dns && dhcp6_lease) { + struct in6_addr *in6_addrs; + + r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; + } + } + + /* Make sure to flush out old entries before we use the NDISC data */ + ndisc_vacuum(link); + + if (link->network->dhcp_use_dns && link->ndisc_rdnss) { + NDiscRDNSS *dd; + + SET_FOREACH(dd, link->ndisc_rdnss, i) { + if (space) + fputc(' ', f); + + serialize_in6_addrs(f, &dd->address, 1); + space = true; + } + } + + fputc('\n', f); + + fputs("NTP=", f); + space = false; + fputstrv(f, link->network->ntp, NULL, &space); + + if (link->network->dhcp_use_ntp && + link->dhcp_lease) { + const struct in_addr *addresses; + + r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_use_ntp && dhcp6_lease) { + struct in6_addr *in6_addrs; + char **hosts; + + r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease, + &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; + } + + r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts); + if (r > 0) + fputstrv(f, hosts, NULL, &space); + } + + fputc('\n', f); + + if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { + if (link->dhcp_lease) + (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); + if (dhcp6_lease) + (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); + } + + fputs("DOMAINS=", f); + fputstrv(f, link->network->search_domains, NULL, &space); + + if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { + NDiscDNSSL *dd; + + if (dhcp_domainname) + fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp6_domains) + fputstrv(f, dhcp6_domains, NULL, &space); + + SET_FOREACH(dd, link->ndisc_dnssl, i) + fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); + } + + fputc('\n', f); + + fputs("ROUTE_DOMAINS=", f); + fputstrv(f, link->network->route_domains, NULL, NULL); + + if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { + NDiscDNSSL *dd; + + if (dhcp_domainname) + fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp6_domains) + fputstrv(f, dhcp6_domains, NULL, &space); + + SET_FOREACH(dd, link->ndisc_dnssl, i) + fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); + } + + fputc('\n', f); + + 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) + fputs_with_space(f, n, NULL, &space); + 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); + } + + print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); + print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); + + 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); + + 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) + goto fail; + + fprintf(f, + "DHCP_LEASE=%s\n", + link->lease_file); + } 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); + } + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, link->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(link->state_file); + if (temp_path) + (void) unlink(temp_path); + + 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); + + /* mark manager dirty as link is dirty */ + manager_dirty(link->manager); + + 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) + /* don't take another ref if the link was already dirty */ + 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", + [LINK_STATE_SETTING_ADDRESSES] = "configuring", + [LINK_STATE_SETTING_ROUTES] = "configuring", + [LINK_STATE_CONFIGURED] = "configured", + [LINK_STATE_UNMANAGED] = "unmanaged", + [LINK_STATE_FAILED] = "failed", + [LINK_STATE_LINGER] = "linger", +}; + +DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState); + +static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { + [LINK_OPERSTATE_OFF] = "off", + [LINK_OPERSTATE_NO_CARRIER] = "no-carrier", + [LINK_OPERSTATE_DORMANT] = "dormant", + [LINK_OPERSTATE_CARRIER] = "carrier", + [LINK_OPERSTATE_DEGRADED] = "degraded", + [LINK_OPERSTATE_ROUTABLE] = "routable", +}; + +DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState); diff --git a/src/grp-network/libnetworkd-core/networkd-link.h b/src/grp-network/libnetworkd-core/networkd-link.h new file mode 100644 index 0000000000..5c7b64a243 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-link.h @@ -0,0 +1,213 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include + +#include "basic/list.h" +#include "basic/set.h" +#include "sd-netlink/sd-netlink.h" +#include "systemd-network/sd-dhcp-client.h" +#include "systemd-network/sd-dhcp-server.h" +#include "systemd-network/sd-dhcp6-client.h" +#include "systemd-network/sd-ipv4ll.h" +#include "systemd-network/sd-lldp.h" +#include "systemd-network/sd-ndisc.h" + +typedef enum LinkState { + LINK_STATE_PENDING, + LINK_STATE_ENSLAVING, + LINK_STATE_SETTING_ADDRESSES, + LINK_STATE_SETTING_ROUTES, + LINK_STATE_CONFIGURED, + LINK_STATE_UNMANAGED, + LINK_STATE_FAILED, + LINK_STATE_LINGER, + _LINK_STATE_MAX, + _LINK_STATE_INVALID = -1 +} LinkState; + +typedef enum LinkOperationalState { + LINK_OPERSTATE_OFF, + LINK_OPERSTATE_NO_CARRIER, + LINK_OPERSTATE_DORMANT, + LINK_OPERSTATE_CARRIER, + LINK_OPERSTATE_DEGRADED, + LINK_OPERSTATE_ROUTABLE, + _LINK_OPERSTATE_MAX, + _LINK_OPERSTATE_INVALID = -1 +} LinkOperationalState; + +typedef struct Manager Manager; +typedef struct Network Network; +typedef struct Address Address; + +typedef struct Link { + Manager *manager; + + int n_ref; + + int ifindex; + char *ifname; + char *kind; + unsigned short iftype; + char *state_file; + struct ether_addr mac; + struct in6_addr ipv6ll_address; + uint32_t mtu; + struct udev_device *udev_device; + + unsigned flags; + uint8_t kernel_operstate; + + Network *network; + + LinkState state; + LinkOperationalState operstate; + + unsigned link_messages; + unsigned enslaving; + + Set *addresses; + Set *addresses_foreign; + Set *routes; + Set *routes_foreign; + + sd_dhcp_client *dhcp_client; + sd_dhcp_lease *dhcp_lease; + char *lease_file; + uint16_t original_mtu; + unsigned dhcp4_messages; + bool dhcp4_configured; + bool dhcp6_configured; + + unsigned ndisc_messages; + bool ndisc_configured; + + sd_ipv4ll *ipv4ll; + bool ipv4ll_address:1; + bool ipv4ll_route:1; + + bool static_configured; + + LIST_HEAD(Address, pool_addresses); + + sd_dhcp_server *dhcp_server; + + sd_ndisc *ndisc; + Set *ndisc_rdnss; + Set *ndisc_dnssl; + + sd_dhcp6_client *dhcp6_client; + bool rtnl_extended_attrs; + + /* This is about LLDP reception */ + sd_lldp *lldp; + char *lldp_file; + + /* This is about LLDP transmission */ + unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ + sd_event_source *lldp_emit_event_source; + + Hashmap *bound_by_links; + Hashmap *bound_to_links; +} Link; + +Link *link_unref(Link *link); +Link *link_ref(Link *link); +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_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_check_ready(Link *link); + +void link_update_operstate(Link *link); +int link_update(Link *link, sd_netlink_message *message); + +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 dhcp6_configure(Link *link); +int dhcp6_request_address(Link *link, int ir); + +const char* link_state_to_string(LinkState s) _const_; +LinkState link_state_from_string(const char *s) _pure_; + +const char* link_operstate_to_string(LinkOperationalState s) _const_; +LinkOperationalState link_operstate_from_string(const char *s) _pure_; + +extern const sd_bus_vtable link_vtable[]; + +int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); +int link_send_changed(Link *link, const char *property, ...) _sentinel_; + +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); +#define _cleanup_link_unref_ _cleanup_(link_unrefp) + +/* Macros which append INTERFACE= to the message */ + +#define log_link_full(link, level, error, ...) \ + ({ \ + Link *_l = (link); \ + _l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, ##__VA_ARGS__) : \ + log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + }) \ + +#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__) +#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__) +#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__) +#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__) + +#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ +#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname + +#define ADDRESS_FMT_VAL(address) \ + be32toh((address).s_addr) >> 24, \ + (be32toh((address).s_addr) >> 16) & 0xFFu, \ + (be32toh((address).s_addr) >> 8) & 0xFFu, \ + be32toh((address).s_addr) & 0xFFu diff --git a/src/grp-network/libnetworkd-core/networkd-lldp-tx.c b/src/grp-network/libnetworkd-core/networkd-lldp-tx.c new file mode 100644 index 0000000000..6a015ec24f --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-lldp-tx.c @@ -0,0 +1,417 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/parse-util.h" +#include "basic/random-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/unaligned.h" + +#include "networkd-lldp-tx.h" +#include "networkd.h" + +/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ +#define LLDP_TX_FAST_INIT 4U + +/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ +#define LLDP_TX_HOLD 4U + +/* The jitter range to add, see 9.2.2. */ +#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC) + +/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ +#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ +#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { + [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, + [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, + [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, +}; + +static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { + assert(p); + + if (id > 127) + return -EBADMSG; + if (sz > 511) + return -ENOBUFS; + + (*p)[0] = (id << 1) | !!(sz & 256); + (*p)[1] = sz & 255; + + *p = *p + 2; + return 0; +} + +static int lldp_make_packet( + LLDPEmit mode, + const struct ether_addr *hwaddr, + const char *machine_id, + const char *ifname, + uint16_t ttl, + const char *port_description, + const char *hostname, + const char *pretty_hostname, + uint16_t system_capabilities, + uint16_t enabled_capabilities, + void **ret, size_t *sz) { + + size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0; + _cleanup_free_ void *packet = NULL; + struct ether_header *h; + uint8_t *p; + size_t l; + int r; + + assert(mode > LLDP_EMIT_NO); + assert(mode < _LLDP_EMIT_MAX); + assert(hwaddr); + assert(machine_id); + assert(ifname); + assert(ret); + assert(sz); + + machine_id_length = strlen(machine_id); + ifname_length = strlen(ifname); + + if (port_description) + port_description_length = strlen(port_description); + + if (hostname) + hostname_length = strlen(hostname); + + if (pretty_hostname) + pretty_hostname_length = strlen(pretty_hostname); + + l = sizeof(struct ether_header) + + /* Chassis ID */ + 2 + 1 + machine_id_length + + /* Port ID */ + 2 + 1 + ifname_length + + /* TTL */ + 2 + 2 + + /* System Capabilities */ + 2 + 4 + + /* End */ + 2; + + /* Port Description */ + if (port_description) + l += 2 + port_description_length; + + /* System Name */ + if (hostname) + l += 2 + hostname_length; + + /* System Description */ + if (pretty_hostname) + l += 2 + pretty_hostname_length; + + packet = malloc(l); + if (!packet) + return -ENOMEM; + + h = (struct ether_header*) packet; + h->ether_type = htobe16(ETHERTYPE_LLDP); + memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); + memcpy(h->ether_shost, hwaddr, ETH_ALEN); + + p = (uint8_t*) packet + sizeof(struct ether_header); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; + p = mempcpy(p, machine_id, machine_id_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; + p = mempcpy(p, ifname, ifname_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2); + if (r < 0) + return r; + unaligned_write_be16(p, ttl); + p += 2; + + if (port_description) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length); + if (r < 0) + return r; + p = mempcpy(p, port_description, port_description_length); + } + + if (hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length); + if (r < 0) + return r; + p = mempcpy(p, hostname, hostname_length); + } + + if (pretty_hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length); + if (r < 0) + return r; + p = mempcpy(p, pretty_hostname, pretty_hostname_length); + } + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + if (r < 0) + return r; + unaligned_write_be16(p, system_capabilities); + p += 2; + unaligned_write_be16(p, enabled_capabilities); + p += 2; + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0); + if (r < 0) + return r; + + assert(p == (uint8_t*) packet + l); + + *ret = packet; + *sz = l; + + packet = NULL; + return 0; +} + +static int lldp_send_packet( + int ifindex, + const struct ether_addr *address, + const void *packet, + size_t packet_size) { + + union sockaddr_union sa = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + }; + + _cleanup_close_ int fd = -1; + ssize_t l; + + assert(ifindex > 0); + assert(address); + assert(packet || packet_size <= 0); + + memcpy(sa.ll.sll_addr, address, ETH_ALEN); + + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); + if (fd < 0) + return -errno; + + l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); + if (l < 0) + return -errno; + + if ((size_t) l != packet_size) + return -EIO; + + return 0; +} + +static int link_send_lldp(Link *link) { + char machine_id_string[SD_ID128_STRING_MAX]; + _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; + _cleanup_free_ void *packet = NULL; + size_t packet_size = 0; + sd_id128_t machine_id; + uint16_t caps; + usec_t ttl; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) + return 0; + + assert(link->network->lldp_emit < _LLDP_EMIT_MAX); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + (void) gethostname_strict(&hostname); + (void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL); + + assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC); + ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC); + + caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : + SD_LLDP_SYSTEM_CAPABILITIES_STATION; + + r = lldp_make_packet(link->network->lldp_emit, + &link->mac, + sd_id128_to_string(machine_id, machine_id_string), + link->ifname, + (uint16_t) ttl, + link->network ? link->network->description : NULL, + hostname, + pretty_hostname, + SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, + caps, + &packet, &packet_size); + if (r < 0) + return r; + + return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); +} + +static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { + Link *link = userdata; + usec_t current, delay, next; + int r; + + assert(s); + assert(userdata); + + log_link_debug(link, "Sending LLDP packet..."); + + r = link_send_lldp(link); + if (r < 0) + log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m"); + + if (link->lldp_tx_fast > 0) + link->lldp_tx_fast--; + + assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), ¤t) >= 0); + + delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC; + next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC); + + r = sd_event_source_set_time(s, next); + if (r < 0) + return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m"); + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) + return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m"); + + return 0; +} + +int link_lldp_emit_start(Link *link) { + usec_t next; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { + link_lldp_emit_stop(link); + return 0; + } + + /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ + + link->lldp_tx_fast = LLDP_TX_FAST_INIT; + + next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), + (usec_t) random_u64() % LLDP_JITTER_USEC); + + if (link->lldp_emit_event_source) { + usec_t old; + + /* Lower the timeout, maybe */ + r = sd_event_source_get_time(link->lldp_emit_event_source, &old); + if (r < 0) + return r; + + if (old <= next) + return 0; + + return sd_event_source_set_time(link->lldp_emit_event_source, next); + } else { + r = sd_event_add_time( + link->manager->event, + &link->lldp_emit_event_source, + clock_boottime_or_monotonic(), + next, + 0, + on_lldp_timer, + link); + if (r < 0) + return r; + + (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); + } + + return 0; +} + +void link_lldp_emit_stop(Link *link) { + assert(link); + + link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source); +} + +int config_parse_lldp_emit( + 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) { + + LLDPEmit *emit = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + *emit = LLDP_EMIT_NO; + else if (streq(rvalue, "nearest-bridge")) + *emit = LLDP_EMIT_NEAREST_BRIDGE; + else if (streq(rvalue, "non-tpmr-bridge")) + *emit = LLDP_EMIT_NON_TPMR_BRIDGE; + else if (streq(rvalue, "customer-bridge")) + *emit = LLDP_EMIT_CUSTOMER_BRIDGE; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); + return 0; + } + + *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO; + } + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-lldp-tx.h b/src/grp-network/libnetworkd-core/networkd-lldp-tx.h new file mode 100644 index 0000000000..4680c9d950 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-lldp-tx.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include "networkd-link.h" + +typedef enum LLDPEmit { + LLDP_EMIT_NO, + LLDP_EMIT_NEAREST_BRIDGE, + LLDP_EMIT_NON_TPMR_BRIDGE, + LLDP_EMIT_CUSTOMER_BRIDGE, + _LLDP_EMIT_MAX, +} LLDPEmit; + +int link_lldp_emit_start(Link *link); +void link_lldp_emit_stop(Link *link); + +int config_parse_lldp_emit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-manager-bus.c b/src/grp-network/libnetworkd-core/networkd-manager-bus.c new file mode 100644 index 0000000000..53652953fe --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-manager-bus.c @@ -0,0 +1,50 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "shared/bus-util.h" + +#include "networkd.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState); + +const sd_bus_vtable manager_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + SD_BUS_VTABLE_END +}; + +int manager_send_changed(Manager *manager, const char *property, ...) { + char **l; + + assert(manager); + + if (!manager->bus) + return 0; /* replace by assert when we have kdbus */ + + l = strv_from_stdarg_alloca(property); + + return sd_bus_emit_properties_changed_strv( + manager->bus, + "/org/freedesktop/network1", + "org.freedesktop.network1.Manager", + l); +} diff --git a/src/grp-network/libnetworkd-core/networkd-manager.c b/src/grp-network/libnetworkd-core/networkd-manager.c new file mode 100644 index 0000000000..1bf22edcb7 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-manager.c @@ -0,0 +1,1331 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . + ***/ + +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/ordered-set.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/virt.h" +#include "libudev-private.h" +#include "sd-netlink/local-addresses.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" +#include "shared/dns-domain.h" +#include "shared/udev-util.h" + +#include "networkd.h" + +/* use 8 MB for receive socket kernel queue. */ +#define RCVBUF_SIZE (8*1024*1024) + +const char* const network_dirs[] = { + "/etc/systemd/network", + "/run/systemd/network", + "/usr/lib/systemd/network", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/network", +#endif + NULL}; + +static int setup_default_address_pool(Manager *m) { + AddressPool *p; + int r; + + assert(m); + + /* Add in the well-known private address ranges. */ + + r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7); + if (r < 0) + return r; + + r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16); + if (r < 0) + return r; + + r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12); + if (r < 0) + return r; + + r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8); + if (r < 0) + return r; + + return 0; +} + +static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(m); + + m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); + + manager_connect_bus(m); + + return 0; +} + +static int manager_reset_all(Manager *m) { + Link *link; + Iterator i; + int r; + + assert(m); + + HASHMAP_FOREACH(link, m->links, i) { + r = link_carrier_reset(link); + if (r < 0) + log_link_warning_errno(link, r, "Could not reset carrier: %m"); + } + + return 0; +} + +static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + Manager *m = userdata; + int b, r; + + assert(message); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) { + log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); + return 0; + } + + if (b) + return 0; + + log_debug("Coming back from suspend, resetting all connections..."); + + manager_reset_all(m); + + return 0; +} + +int manager_connect_bus(Manager *m) { + int r; + + assert(m); + + r = sd_bus_default_system(&m->bus); + if (r == -ENOENT) { + /* We failed to connect? Yuck, we must be in early + * boot. Let's try in 5s again. As soon as we have + * kdbus we can stop doing this... */ + + log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); + + r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); + if (r < 0) + return log_error_errno(r, "Failed to install bus reconnect time event: %m"); + + return 0; + } + + if (r < 0) + return r; + + r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, + "type='signal'," + "sender='org.freedesktop.login1'," + "interface='org.freedesktop.login1.Manager'," + "member='PrepareForSleep'," + "path='/org/freedesktop/login1'", + match_prepare_for_sleep, + m); + if (r < 0) + return log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + + r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m); + if (r < 0) + return log_error_errno(r, "Failed to add manager object vtable: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.Link", link_vtable, link_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add link object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add link enumerator: %m"); + + r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/network", "org.freedesktop.network1.Network", network_vtable, network_object_find, m); + if (r < 0) + return log_error_errno(r, "Failed to add network object vtable: %m"); + + r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/network", network_node_enumerator, m); + if (r < 0) + return log_error_errno(r, "Failed to add network enumerator: %m"); + + r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(m->bus, m->event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + return 0; +} + +static int manager_udev_process_link(Manager *m, struct udev_device *device) { + Link *link = NULL; + int r, ifindex; + + assert(m); + assert(device); + + if (!streq_ptr(udev_device_get_action(device), "add")) + return 0; + + ifindex = udev_device_get_ifindex(device); + if (ifindex <= 0) { + log_debug("Ignoring udev ADD event for device with invalid ifindex"); + return 0; + } + + r = link_get(m, ifindex, &link); + if (r == -ENODEV) + return 0; + else if (r < 0) + return r; + + r = link_initialized(link, device); + if (r < 0) + return r; + + return 0; +} + +static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + struct udev_monitor *monitor = m->udev_monitor; + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + + device = udev_monitor_receive_device(monitor); + if (!device) + return -ENOMEM; + + manager_udev_process_link(m, device); + return 0; +} + +static int manager_connect_udev(Manager *m) { + int r; + + /* udev does not initialize devices inside containers, + * so we rely on them being already initialized before + * entering the container */ + if (detect_container() > 0) + return 0; + + m->udev = udev_new(); + if (!m->udev) + return -ENOMEM; + + m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_monitor) + return -ENOMEM; + + r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL); + if (r < 0) + return log_error_errno(r, "Could not add udev monitor filter: %m"); + + r = udev_monitor_enable_receiving(m->udev_monitor); + if (r < 0) { + log_error("Could not enable udev monitor"); + return r; + } + + r = sd_event_add_io(m->event, + &m->udev_event_source, + udev_monitor_get_fd(m->udev_monitor), + EPOLLIN, manager_dispatch_link_udev, + m); + if (r < 0) + return r; + + r = sd_event_source_set_description(m->udev_event_source, "networkd-udev"); + if (r < 0) + return r; + + 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: + route_free(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; + NetDev *netdev = NULL; + uint16_t type; + const char *name; + 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: Could not receive link: %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_NEWLINK && type != RTM_DELLINK) { + log_warning("rtnl: Received unexpected message type when processing link"); + return 0; + } + + r = sd_rtnl_message_link_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: Could not get ifindex from link: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); + return 0; + } + + 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; + } + + (void) link_get(m, ifindex, &link); + (void) netdev_get(m, name, &netdev); + + switch (type) { + case RTM_NEWLINK: + if (!link) { + /* link is new, so add it */ + r = link_add(m, message, &link); + if (r < 0) { + log_warning_errno(r, "Could not add new link: %m"); + return 0; + } + } + + if (netdev) { + /* netdev exists, so make sure the ifindex matches */ + r = netdev_set_ifindex(netdev, message); + if (r < 0) { + log_warning_errno(r, "Could not set ifindex on netdev: %m"); + return 0; + } + } + + r = link_update(link, message); + if (r < 0) + return 0; + + break; + + case RTM_DELLINK: + link_drop(link); + netdev_drop(netdev); + + break; + + default: + assert_not_reached("Received invalid RTNL message type."); + } + + return 1; +} + +static int systemd_netlink_fd(void) { + int n, fd, rtnl_fd = -EINVAL; + + n = sd_listen_fds(true); + if (n <= 0) + return -EINVAL; + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { + if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { + if (rtnl_fd >= 0) + return -EINVAL; + + rtnl_fd = fd; + } + } + + return rtnl_fd; +} + +static int manager_connect_rtnl(Manager *m) { + int fd, r; + + assert(m); + + fd = systemd_netlink_fd(); + if (fd < 0) + r = sd_netlink_open(&m->rtnl); + else + r = sd_netlink_open_fd(&m->rtnl, fd); + if (r < 0) + return r; + + r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE); + if (r < 0) + return r; + + r = sd_netlink_attach_event(m->rtnl, m->event, 0); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m); + if (r < 0) + return r; + + 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, &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 ordered_set_put_in_addr(OrderedSet *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 = ordered_set_consume(s, p); + if (r == -EEXIST) + return 0; + + return r; +} + +static int ordered_set_put_in_addrv(OrderedSet *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 = ordered_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, OrderedSet *s) { + bool space = false; + Iterator i; + char *p; + + if (ordered_set_isempty(s)) + return; + + fputs(field, f); + + ORDERED_SET_FOREACH(p, s, i) + fputs_with_space(f, p, NULL, &space); + + fputc('\n', f); +} + +static int manager_save(Manager *m) { + _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_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 = ordered_set_new(&string_hash_ops); + if (!dns) + return -ENOMEM; + + ntp = ordered_set_new(&string_hash_ops); + if (!ntp) + return -ENOMEM; + + search_domains = ordered_set_new(&dns_name_hash_ops); + if (!search_domains) + return -ENOMEM; + + route_domains = ordered_set_new(&dns_name_hash_ops); + if (!route_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 = ordered_set_put_strdupv(dns, link->network->dns); + if (r < 0) + return r; + + r = ordered_set_put_strdupv(ntp, link->network->ntp); + if (r < 0) + return r; + + r = ordered_set_put_strdupv(search_domains, link->network->search_domains); + if (r < 0) + return r; + + r = ordered_set_put_strdupv(route_domains, link->network->route_domains); + if (r < 0) + return r; + + if (!link->dhcp_lease) + continue; + + /* Secondly, add the entries acquired via DHCP */ + if (link->network->dhcp_use_dns) { + const struct in_addr *addresses; + + r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); + if (r > 0) { + r = ordered_set_put_in_addrv(dns, addresses, r); + if (r < 0) + return r; + } else if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp_use_ntp) { + const struct in_addr *addresses; + + r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); + if (r > 0) { + r = ordered_set_put_in_addrv(ntp, addresses, r); + if (r < 0) + return r; + } else if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { + const char *domainname; + + r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); + if (r >= 0) { + + if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) + r = ordered_set_put_strdup(search_domains, domainname); + else + r = ordered_set_put_strdup(route_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=", search_domains); + print_string_set(f, "ROUTE_DOMAINS=", route_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; + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + m->state_file = strdup("/run/systemd/netif/state"); + if (!m->state_file) + return -ENOMEM; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_set_watchdog(m->event, true); + + 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; + + r = manager_connect_udev(m); + if (r < 0) + return r; + + m->netdevs = hashmap_new(&string_hash_ops); + if (!m->netdevs) + return -ENOMEM; + + LIST_HEAD_INIT(m->networks); + + r = setup_default_address_pool(m); + if (r < 0) + return r; + + m->duid.type = DUID_TYPE_EN; + + *ret = m; + m = NULL; + + return 0; +} + +void manager_free(Manager *m) { + Network *network; + NetDev *netdev; + Link *link; + AddressPool *pool; + + if (!m) + return; + + free(m->state_file); + + while ((link = hashmap_first(m->links))) + link_unref(link); + hashmap_free(m->links); + + while ((network = m->networks)) + network_free(network); + + hashmap_free(m->networks_by_name); + + while ((netdev = hashmap_first(m->netdevs))) + netdev_unref(netdev); + hashmap_free(m->netdevs); + + while ((pool = m->address_pools)) + address_pool_free(pool); + + 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); +} + +static bool manager_check_idle(void *userdata) { + Manager *m = userdata; + Link *link; + Iterator i; + + assert(m); + + /* Check whether we are idle now. The only case when we decide to be idle is when there's only a loopback + * device around, for which we have no configuration, and which already left the PENDING state. In all other + * cases we are not idle. */ + + HASHMAP_FOREACH(link, m->links, i) { + /* We are not woken on udev activity, so let's just wait for the pending udev event */ + if (link->state == LINK_STATE_PENDING) + return false; + + if ((link->flags & IFF_LOOPBACK) == 0) + return false; + + if (link->network) + return false; + } + + return true; +} + +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, + m->bus, + "org.freedesktop.network1", + DEFAULT_EXIT_USEC, + manager_check_idle, + m); + else + /* failed to connect to the bus, so we lose exit-on-idle logic, + this should not happen except if dbus is not around at all */ + return sd_event_loop(m->event); +} + +int manager_load_config(Manager *m) { + int r; + + /* update timestamp */ + paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true); + + r = netdev_load(m); + if (r < 0) + return r; + + r = network_load(m); + if (r < 0) + return r; + + return 0; +} + +bool manager_should_reload(Manager *m) { + return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false); +} + +int manager_rtnl_enumerate_links(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *link; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (link = reply; link; link = sd_netlink_message_next(link)) { + int k; + + m->enumerating = true; + + k = manager_rtnl_process_link(m->rtnl, link, m); + if (k < 0) + r = k; + + m->enumerating = false; + } + + return r; +} + +int manager_rtnl_enumerate_addresses(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *addr; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (addr = reply; addr; addr = sd_netlink_message_next(addr)) { + int k; + + m->enumerating = true; + + k = manager_rtnl_process_address(m->rtnl, addr, m); + if (k < 0) + r = k; + + m->enumerating = false; + } + + return r; +} + +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(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (route = reply; route; route = sd_netlink_message_next(route)) { + int k; + + m->enumerating = true; + + k = manager_rtnl_process_route(m->rtnl, route, m); + if (k < 0) + r = k; + + m->enumerating = false; + } + + return r; +} + +int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { + AddressPool *p; + int r; + + assert(m); + assert(prefixlen > 0); + assert(found); + + LIST_FOREACH(address_pools, p, m->address_pools) { + if (p->family != family) + continue; + + r = address_pool_acquire(p, prefixlen, found); + if (r != 0) + return r; + } + + return 0; +} + +Link* manager_find_uplink(Manager *m, Link *exclude) { + _cleanup_free_ struct local_address *gateways = NULL; + int n, i; + + assert(m); + + /* Looks for a suitable "uplink", via black magic: an + * interface that is up and where the default route with the + * highest priority points to. */ + + n = local_gateways(m->rtnl, 0, AF_UNSPEC, &gateways); + if (n < 0) { + log_warning_errno(n, "Failed to determine list of default gateways: %m"); + return NULL; + } + + for (i = 0; i < n; i++) { + Link *link; + + link = hashmap_get(m->links, INT_TO_PTR(gateways[i].ifindex)); + if (!link) { + log_debug("Weird, found a gateway for a link we don't know. Ignoring."); + continue; + } + + if (link == exclude) + continue; + + if (link->operstate < LINK_OPERSTATE_ROUTABLE) + continue; + + return link; + } + + 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/grp-network/libnetworkd-core/networkd-ndisc.c b/src/grp-network/libnetworkd-core/networkd-ndisc.c new file mode 100644 index 0000000000..27fbf3bc76 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-ndisc.c @@ -0,0 +1,664 @@ +/*** + 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 . +***/ + +#include + +#include "systemd-network/sd-ndisc.h" + +#include "networkd-ndisc.h" +#include "networkd.h" + +#define NDISC_DNSSL_MAX 64U +#define NDISC_RDNSS_MAX 64U + +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_router_process_default(Link *link, sd_ndisc_router *rt) { + _cleanup_route_free_ Route *route = NULL; + struct in6_addr gateway; + uint16_t lifetime; + unsigned preference; + usec_t time_now; + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_get_lifetime(rt, &lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return; + } + if (lifetime == 0) /* not a default router */ + return; + + r = sd_ndisc_router_get_address(rt, &gateway); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return; + } + + r = sd_ndisc_router_get_preference(rt, &preference); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); + return; + } + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = preference; + 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_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { + _cleanup_address_free_ Address *address = NULL; + uint32_t lifetime_valid, lifetime_preferred; + unsigned prefixlen; + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix length: %m"); + return; + } + + r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); + return; + } + + r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); + return; + } + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return; + } + + address->family = AF_INET6; + r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix address: %m"); + return; + } + + 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.s6_addr[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.s6_addr[8] ^= 1 << 1; + address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.s6_addr[11] = 0xff; + address->in_addr.in6.s6_addr[12] = 0xfe; + address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.s6_addr[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_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { + _cleanup_route_free_ Route *route = NULL; + usec_t time_now; + uint32_t lifetime; + unsigned prefixlen; + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + + r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix length: %m"); + return; + } + + r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); + return; + } + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->flags = RTM_F_PREFIX; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get prefix address: %m"); + return; + } + + 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_process_route(Link *link, sd_ndisc_router *rt) { + _cleanup_route_free_ Route *route = NULL; + struct in6_addr gateway; + uint32_t lifetime; + unsigned preference, prefixlen; + usec_t time_now; + int r; + + assert(link); + + r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return; + } + if (lifetime == 0) + return; + + r = sd_ndisc_router_get_address(rt, &gateway); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); + return; + } + + r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get route prefix length: %m"); + return; + } + + r = sd_ndisc_router_route_get_preference(rt, &preference); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); + return; + } + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = preference; + route->gw.in6 = gateway; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = sd_ndisc_router_route_get_address(rt, &route->dst.in6); + if (r < 0) { + log_link_error_errno(link, r, "Failed to get route address: %m"); + return; + } + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set additional route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages++; +} + +static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) { + const NDiscRDNSS *x = p; + + siphash24_compress(&x->address, sizeof(x->address), state); +} + +static int ndisc_rdnss_compare_func(const void *_a, const void *_b) { + const NDiscRDNSS *a = _a, *b = _b; + + return memcmp(&a->address, &b->address, sizeof(a->address)); +} + +static const struct hash_ops ndisc_rdnss_hash_ops = { + .hash = ndisc_rdnss_hash_func, + .compare = ndisc_rdnss_compare_func +}; + +static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { + uint32_t lifetime; + const struct in6_addr *a; + usec_t time_now; + int i, n, r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + + r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); + return; + } + + n = sd_ndisc_router_rdnss_get_addresses(rt, &a); + if (n < 0) { + log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m"); + return; + } + + for (i = 0; i < n; i++) { + NDiscRDNSS d = { + .address = a[i] + }, *x; + + if (lifetime == 0) { + (void) set_remove(link->ndisc_rdnss, &d); + link_dirty(link); + continue; + } + + x = set_get(link->ndisc_rdnss, &d); + if (x) { + x->valid_until = time_now + lifetime * USEC_PER_SEC; + continue; + } + + ndisc_vacuum(link); + + if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { + log_link_warning(link, "Too many RDNSS records per link, ignoring."); + continue; + } + + r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops); + if (r < 0) { + log_oom(); + return; + } + + x = new0(NDiscRDNSS, 1); + if (!x) { + log_oom(); + return; + } + + x->address = a[i]; + x->valid_until = time_now + lifetime * USEC_PER_SEC; + + r = set_put(link->ndisc_rdnss, x); + if (r < 0) { + free(x); + log_oom(); + return; + } + + assert(r > 0); + link_dirty(link); + } +} + +static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) { + const NDiscDNSSL *x = p; + + siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state); +} + +static int ndisc_dnssl_compare_func(const void *_a, const void *_b) { + const NDiscDNSSL *a = _a, *b = _b; + + return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); +} + +static const struct hash_ops ndisc_dnssl_hash_ops = { + .hash = ndisc_dnssl_hash_func, + .compare = ndisc_dnssl_compare_func +}; + +static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { + _cleanup_strv_free_ char **l = NULL; + uint32_t lifetime; + usec_t time_now; + char **i; + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + + r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); + return; + } + + r = sd_ndisc_router_dnssl_get_domains(rt, &l); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m"); + return; + } + + STRV_FOREACH(i, l) { + _cleanup_free_ NDiscDNSSL *s; + NDiscDNSSL *x; + + s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1); + if (!s) { + log_oom(); + return; + } + + strcpy(NDISC_DNSSL_DOMAIN(s), *i); + + if (lifetime == 0) { + (void) set_remove(link->ndisc_dnssl, s); + link_dirty(link); + continue; + } + + x = set_get(link->ndisc_dnssl, s); + if (x) { + x->valid_until = time_now + lifetime * USEC_PER_SEC; + continue; + } + + ndisc_vacuum(link); + + if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { + log_link_warning(link, "Too many DNSSL records per link, ignoring."); + continue; + } + + r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops); + if (r < 0) { + log_oom(); + return; + } + + s->valid_until = time_now + lifetime * USEC_PER_SEC; + + r = set_put(link->ndisc_dnssl, s); + if (r < 0) { + log_oom(); + return; + } + + s = NULL; + assert(r > 0); + link_dirty(link); + } +} + +static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { + int r; + + assert(link); + assert(rt); + + r = sd_ndisc_router_option_rewind(rt); + for (;;) { + uint8_t type; + + if (r < 0) { + log_link_warning_errno(link, r, "Failed to iterate through options: %m"); + return; + } + if (r == 0) /* EOF */ + break; + + r = sd_ndisc_router_option_get_type(rt, &type); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA option type: %m"); + return; + } + + switch (type) { + + case SD_NDISC_OPTION_PREFIX_INFORMATION: { + uint8_t flags; + + r = sd_ndisc_router_prefix_get_flags(rt, &flags); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); + return; + } + + if (flags & ND_OPT_PI_FLAG_ONLINK) + ndisc_router_process_onlink_prefix(link, rt); + if (flags & ND_OPT_PI_FLAG_AUTO) + ndisc_router_process_autonomous_prefix(link, rt); + + break; + } + + case SD_NDISC_OPTION_ROUTE_INFORMATION: + ndisc_router_process_route(link, rt); + break; + + case SD_NDISC_OPTION_RDNSS: + ndisc_router_process_rdnss(link, rt); + break; + + case SD_NDISC_OPTION_DNSSL: + ndisc_router_process_dnssl(link, rt); + break; + } + + r = sd_ndisc_router_option_next(rt); + } +} + +static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) { + uint64_t flags; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(rt); + + r = sd_ndisc_router_get_flags(rt, &flags); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA flags: %m"); + return; + } + + if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { + /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ + r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); + else + log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); + } + + ndisc_router_process_default(link, rt); + ndisc_router_process_options(link, rt); +} + +static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { + Link *link = userdata; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + + case SD_NDISC_EVENT_ROUTER: + ndisc_router_handler(link, rt); + break; + + case SD_NDISC_EVENT_TIMEOUT: + link->ndisc_configured = true; + link_check_ready(link); + + break; + default: + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); + } +} + +int ndisc_configure(Link *link) { + int r; + + assert(link); + + r = sd_ndisc_new(&link->ndisc); + if (r < 0) + return r; + + r = sd_ndisc_attach_event(link->ndisc, NULL, 0); + if (r < 0) + return r; + + r = sd_ndisc_set_mac(link->ndisc, &link->mac); + if (r < 0) + return r; + + r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex); + if (r < 0) + return r; + + r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link); + if (r < 0) + return r; + + return 0; +} + +void ndisc_vacuum(Link *link) { + NDiscRDNSS *r; + NDiscDNSSL *d; + Iterator i; + usec_t time_now; + + assert(link); + + /* Removes all RDNSS and DNSSL entries whose validity time has passed */ + + time_now = now(clock_boottime_or_monotonic()); + + SET_FOREACH(r, link->ndisc_rdnss, i) + if (r->valid_until < time_now) { + (void) set_remove(link->ndisc_rdnss, r); + link_dirty(link); + } + + SET_FOREACH(d, link->ndisc_dnssl, i) + if (d->valid_until < time_now) { + (void) set_remove(link->ndisc_dnssl, d); + link_dirty(link); + } +} diff --git a/src/grp-network/libnetworkd-core/networkd-ndisc.h b/src/grp-network/libnetworkd-core/networkd-ndisc.h new file mode 100644 index 0000000000..2002f55107 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-ndisc.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "networkd-link.h" + +typedef struct NDiscRDNSS { + usec_t valid_until; + struct in6_addr address; +} NDiscRDNSS; + +typedef struct NDiscDNSSL { + usec_t valid_until; + /* The domain name follows immediately. */ +} NDiscDNSSL; + +static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) { + return ((char*) n) + ALIGN(sizeof(NDiscDNSSL)); +} + +int ndisc_configure(Link *link); +void ndisc_vacuum(Link *link); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-bond.c b/src/grp-network/libnetworkd-core/networkd-netdev-bond.c new file mode 100644 index 0000000000..7e7b8f8804 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-bond.c @@ -0,0 +1,445 @@ +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/missing.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/conf-parser.h" + +#include "networkd-netdev-bond.h" + +/* + * Number of seconds between instances where the bonding + * driver sends learning packets to each slaves peer switch + */ +#define LEARNING_PACKETS_INTERVAL_MIN_SEC (1 * USEC_PER_SEC) +#define LEARNING_PACKETS_INTERVAL_MAX_SEC (0x7fffffff * USEC_PER_SEC) + +/* Number of IGMP membership reports to be issued after + * a failover event. + */ +#define RESEND_IGMP_MIN 0 +#define RESEND_IGMP_MAX 255 +#define RESEND_IGMP_DEFAULT 1 + +/* + * Number of packets to transmit through a slave before + * moving to the next one. + */ +#define PACKETS_PER_SLAVE_MIN 0 +#define PACKETS_PER_SLAVE_MAX 65535 +#define PACKETS_PER_SLAVE_DEFAULT 1 + +/* + * Number of peer notifications (gratuitous ARPs and + * unsolicited IPv6 Neighbor Advertisements) to be issued after a + * failover event. + */ +#define GRATUITOUS_ARP_MIN 0 +#define GRATUITOUS_ARP_MAX 255 +#define GRATUITOUS_ARP_DEFAULT 1 + +static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = { + [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr", + [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup", + [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor", + [NETDEV_BOND_MODE_BROADCAST] = "broadcast", + [NETDEV_BOND_MODE_802_3AD] = "802.3ad", + [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb", + [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode"); + +static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = { + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2", + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4", + [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3", + [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3", + [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_xmit_hash_policy, + bond_xmit_hash_policy, + BondXmitHashPolicy, + "Failed to parse bond transmit hash policy") + +static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = { + [NETDEV_BOND_LACP_RATE_SLOW] = "slow", + [NETDEV_BOND_LACP_RATE_FAST] = "fast", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate") + +static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = { + [NETDEV_BOND_AD_SELECT_STABLE] = "stable", + [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth", + [NETDEV_BOND_AD_SELECT_COUNT] = "count", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select"); + +static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = { + [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none", + [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active", + [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC"); + +static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = { + [NETDEV_BOND_ARP_VALIDATE_NONE] = "none", + [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active", + [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup", + [NETDEV_BOND_ARP_VALIDATE_ALL]= "all", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate"); + +static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = { + [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any", + [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets"); + +static const char *bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { + [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always", + [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better", + [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure", +}; + +DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect); +DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect"); + +static uint8_t bond_mode_to_kernel(BondMode mode) { + switch (mode) { + case NETDEV_BOND_MODE_BALANCE_RR: + return BOND_MODE_ROUNDROBIN; + case NETDEV_BOND_MODE_ACTIVE_BACKUP: + return BOND_MODE_ACTIVEBACKUP; + case NETDEV_BOND_MODE_BALANCE_XOR: + return BOND_MODE_XOR; + case NETDEV_BOND_MODE_BROADCAST: + return BOND_MODE_BROADCAST; + case NETDEV_BOND_MODE_802_3AD: + return BOND_MODE_8023AD; + case NETDEV_BOND_MODE_BALANCE_TLB: + return BOND_MODE_TLB; + case NETDEV_BOND_MODE_BALANCE_ALB: + return BOND_MODE_ALB; + default: + return (uint8_t) -1; + } +} + +static uint8_t bond_xmit_hash_policy_to_kernel(BondXmitHashPolicy policy) { + switch (policy) { + case NETDEV_BOND_XMIT_HASH_POLICY_LAYER2: + return BOND_XMIT_POLICY_LAYER2; + case NETDEV_BOND_XMIT_HASH_POLICY_LAYER34: + return BOND_XMIT_POLICY_LAYER34; + case NETDEV_BOND_XMIT_HASH_POLICY_LAYER23: + return BOND_XMIT_POLICY_LAYER23; + case NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23: + return BOND_XMIT_POLICY_ENCAP23; + case NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34: + return BOND_XMIT_POLICY_ENCAP34; + default: + return (uint8_t) -1; + } +} + +static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Bond *b; + ArpIpTarget *target = NULL; + int r, i = 0; + + assert(netdev); + assert(!link); + 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)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MODE attribute: %m"); + } + + if (b->xmit_hash_policy != _NETDEV_BOND_XMIT_HASH_POLICY_INVALID) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, + bond_xmit_hash_policy_to_kernel(b->xmit_hash_policy)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_XMIT_HASH_POLICY attribute: %m"); + } + + if (b->lacp_rate != _NETDEV_BOND_LACP_RATE_INVALID && + b->mode == NETDEV_BOND_MODE_802_3AD) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate ); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_LACP_RATE attribute: %m"); + } + + if (b->miimon != 0) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_MIIMON, b->miimon / USEC_PER_MSEC); + if (r < 0) + log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_BOND_MIIMON attribute: %m"); + } + + if (b->downdelay != 0) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_DOWNDELAY, b->downdelay / USEC_PER_MSEC); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_DOWNDELAY attribute: %m"); + } + + if (b->updelay != 0) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_UPDELAY, b->updelay / USEC_PER_MSEC); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_UPDELAY attribute: %m"); + } + + if (b->arp_interval != 0) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_INTERVAL, b->arp_interval / USEC_PER_MSEC); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_INTERVAL attribute: %m"); + + if ((b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC) && + (b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC)) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_LP_INTERVAL attribute: %m"); + } + } + + if (b->ad_select != _NETDEV_BOND_AD_SELECT_INVALID && + b->mode == NETDEV_BOND_MODE_802_3AD) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_SELECT attribute: %m"); + } + + if (b->fail_over_mac != _NETDEV_BOND_FAIL_OVER_MAC_INVALID && + b->mode == NETDEV_BOND_MODE_ACTIVE_BACKUP) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_FAIL_OVER_MAC, b->fail_over_mac); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_FAIL_OVER_MAC attribute: %m"); + } + + if (b->arp_validate != _NETDEV_BOND_ARP_VALIDATE_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_VALIDATE, b->arp_validate); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_VALIDATE attribute: %m"); + } + + if (b->arp_all_targets != _NETDEV_BOND_ARP_ALL_TARGETS_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->arp_all_targets); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_VALIDATE attribute: %m"); + } + + if (b->primary_reselect != _NETDEV_BOND_PRIMARY_RESELECT_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->primary_reselect); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); + } + + if (b->resend_igmp <= RESEND_IGMP_MAX) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_RESEND_IGMP, b->resend_igmp); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_RESEND_IGMP attribute: %m"); + } + + if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX && + b->mode == NETDEV_BOND_MODE_BALANCE_RR) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_PACKETS_PER_SLAVE attribute: %m"); + } + + if (b->num_grat_arp <= GRATUITOUS_ARP_MAX) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_NUM_PEER_NOTIF, b->num_grat_arp); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_NUM_PEER_NOTIF attribute: %m"); + } + + if (b->min_links != 0) { + r = sd_netlink_message_append_u32(m, IFLA_BOND_MIN_LINKS, b->min_links); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MIN_LINKS attribute: %m"); + } + + r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m"); + + if (b->arp_interval > 0) { + if (b->n_arp_ip_targets > 0) { + + r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %m"); + + LIST_FOREACH(arp_ip_target, target, b->arp_ip_targets) { + r = sd_netlink_message_append_u32(m, i++, target->ip.in.s_addr); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %m"); + } + } + + return 0; +} + +int config_parse_arp_ip_target_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Bond *b = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + for (;;) { + _cleanup_free_ ArpIpTarget *buffer = NULL; + _cleanup_free_ char *n = NULL; + int f; + + 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) + return -ENOMEM; + + r = in_addr_from_string_auto(n, &f, &buffer->ip); + if (r < 0) { + 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, 0, "Bond ARP ip target address is invalid, ignoring assignment: %s", n); + return 0; + } + + LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer); + b->n_arp_ip_targets++; + + buffer = NULL; + } + + if (b->n_arp_ip_targets > 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; + + assert(netdev); + + b = BOND(netdev); + + assert(b); + + LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets) + free(t); + + b->arp_ip_targets = NULL; +} + +static void bond_init(NetDev *netdev) { + Bond *b; + + assert(netdev); + + b = BOND(netdev); + + assert(b); + + b->mode = _NETDEV_BOND_MODE_INVALID; + b->xmit_hash_policy = _NETDEV_BOND_XMIT_HASH_POLICY_INVALID; + b->lacp_rate = _NETDEV_BOND_LACP_RATE_INVALID; + b->ad_select = _NETDEV_BOND_AD_SELECT_INVALID; + b->fail_over_mac = _NETDEV_BOND_FAIL_OVER_MAC_INVALID; + b->arp_validate = _NETDEV_BOND_ARP_VALIDATE_INVALID; + b->arp_all_targets = _NETDEV_BOND_ARP_ALL_TARGETS_INVALID; + b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID; + + b->all_slaves_active = false; + + b->resend_igmp = RESEND_IGMP_DEFAULT; + b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT; + b->num_grat_arp = GRATUITOUS_ARP_DEFAULT; + b->lp_interval = LEARNING_PACKETS_INTERVAL_MIN_SEC; + + LIST_HEAD_INIT(b->arp_ip_targets); + b->n_arp_ip_targets = 0; +} + +const NetDevVTable bond_vtable = { + .object_size = sizeof(Bond), + .init = bond_init, + .done = bond_done, + .sections = "Match\0NetDev\0Bond\0", + .fill_message_create = netdev_bond_fill_message_create, + .create_type = NETDEV_CREATE_MASTER, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-bond.h b/src/grp-network/libnetworkd-core/networkd-netdev-bond.h new file mode 100644 index 0000000000..0ffb91b12e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-bond.h @@ -0,0 +1,172 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/in-addr-util.h" +#include "basic/list.h" + +#include "networkd-netdev.h" + +/* + * Maximum number of targets supported by the kernel for a single + * bond netdev. + */ +#define NETDEV_BOND_ARP_TARGETS_MAX 16 + +typedef enum BondMode { + NETDEV_BOND_MODE_BALANCE_RR, + NETDEV_BOND_MODE_ACTIVE_BACKUP, + NETDEV_BOND_MODE_BALANCE_XOR, + NETDEV_BOND_MODE_BROADCAST, + NETDEV_BOND_MODE_802_3AD, + NETDEV_BOND_MODE_BALANCE_TLB, + NETDEV_BOND_MODE_BALANCE_ALB, + _NETDEV_BOND_MODE_MAX, + _NETDEV_BOND_MODE_INVALID = -1 +} BondMode; + +typedef enum BondXmitHashPolicy { + NETDEV_BOND_XMIT_HASH_POLICY_LAYER2, + NETDEV_BOND_XMIT_HASH_POLICY_LAYER34, + NETDEV_BOND_XMIT_HASH_POLICY_LAYER23, + NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23, + NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34, + _NETDEV_BOND_XMIT_HASH_POLICY_MAX, + _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1 +} BondXmitHashPolicy; + +typedef enum BondLacpRate { + NETDEV_BOND_LACP_RATE_SLOW, + NETDEV_BOND_LACP_RATE_FAST, + _NETDEV_BOND_LACP_RATE_MAX, + _NETDEV_BOND_LACP_RATE_INVALID = -1, +} BondLacpRate; + +typedef enum BondAdSelect { + NETDEV_BOND_AD_SELECT_STABLE, + NETDEV_BOND_AD_SELECT_BANDWIDTH, + NETDEV_BOND_AD_SELECT_COUNT, + _NETDEV_BOND_AD_SELECT_MAX, + _NETDEV_BOND_AD_SELECT_INVALID = -1, +} BondAdSelect; + +typedef enum BondFailOverMac { + NETDEV_BOND_FAIL_OVER_MAC_NONE, + NETDEV_BOND_FAIL_OVER_MAC_ACTIVE, + NETDEV_BOND_FAIL_OVER_MAC_FOLLOW, + _NETDEV_BOND_FAIL_OVER_MAC_MAX, + _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1, +} BondFailOverMac; + +typedef enum BondArpValidate { + NETDEV_BOND_ARP_VALIDATE_NONE, + NETDEV_BOND_ARP_VALIDATE_ACTIVE, + NETDEV_BOND_ARP_VALIDATE_BACKUP, + NETDEV_BOND_ARP_VALIDATE_ALL, + _NETDEV_BOND_ARP_VALIDATE_MAX, + _NETDEV_BOND_ARP_VALIDATE_INVALID = -1, +} BondArpValidate; + +typedef enum BondArpAllTargets { + NETDEV_BOND_ARP_ALL_TARGETS_ANY, + NETDEV_BOND_ARP_ALL_TARGETS_ALL, + _NETDEV_BOND_ARP_ALL_TARGETS_MAX, + _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1, +} BondArpAllTargets; + +typedef enum BondPrimaryReselect { + NETDEV_BOND_PRIMARY_RESELECT_ALWAYS, + NETDEV_BOND_PRIMARY_RESELECT_BETTER, + NETDEV_BOND_PRIMARY_RESELECT_FAILURE, + _NETDEV_BOND_PRIMARY_RESELECT_MAX, + _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1, +} BondPrimaryReselect; + +typedef struct ArpIpTarget { + union in_addr_union ip; + + LIST_FIELDS(struct ArpIpTarget, arp_ip_target); +} ArpIpTarget; + +typedef struct Bond { + NetDev meta; + + BondMode mode; + BondXmitHashPolicy xmit_hash_policy; + BondLacpRate lacp_rate; + BondAdSelect ad_select; + BondFailOverMac fail_over_mac; + BondArpValidate arp_validate; + BondArpAllTargets arp_all_targets; + BondPrimaryReselect primary_reselect; + + bool all_slaves_active; + + unsigned resend_igmp; + unsigned packets_per_slave; + unsigned num_grat_arp; + unsigned min_links; + + usec_t miimon; + usec_t updelay; + usec_t downdelay; + usec_t arp_interval; + usec_t lp_interval; + + int n_arp_ip_targets; + ArpIpTarget *arp_ip_targets; +} Bond; + +DEFINE_NETDEV_CAST(BOND, Bond); +extern const NetDevVTable bond_vtable; + +const char *bond_mode_to_string(BondMode d) _const_; +BondMode bond_mode_from_string(const char *d) _pure_; + +const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_; +BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_; + +const char *bond_lacp_rate_to_string(BondLacpRate d) _const_; +BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_; + +const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_; +BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_; + +const char *bond_ad_select_to_string(BondAdSelect d) _const_; +BondAdSelect bond_ad_select_from_string(const char *d) _pure_; + +const char *bond_arp_validate_to_string(BondArpValidate d) _const_; +BondArpValidate bond_arp_validate_from_string(const char *d) _pure_; + +const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_; +BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_; + +const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_; +BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_; + +int config_parse_bond_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_xmit_hash_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_lacp_rate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_ad_select(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_fail_over_mac(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_arp_validate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_arp_all_targets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bond_primary_reselect(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_arp_ip_target_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-bridge.c b/src/grp-network/libnetworkd-core/networkd-netdev-bridge.c new file mode 100644 index 0000000000..2de2587e36 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-bridge.c @@ -0,0 +1,147 @@ +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/missing.h" +#include "sd-netlink/netlink-util.h" + +#include "networkd-netdev-bridge.h" +#include "networkd.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"); + } + + if (b->mcast_querier >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); + } + + if (b->mcast_snooping >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); + } + + if (b->vlan_filtering >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_VLAN_FILTERING, b->vlan_filtering); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING 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; +} + +static void bridge_init(NetDev *n) { + Bridge *b; + + b = BRIDGE(n); + + assert(b); + + b->mcast_querier = -1; + b->mcast_snooping = -1; + b->vlan_filtering = -1; +} + +const NetDevVTable bridge_vtable = { + .object_size = sizeof(Bridge), + .init = bridge_init, + .sections = "Match\0NetDev\0Bridge\0", + .post_create = netdev_bridge_post_create, + .create_type = NETDEV_CREATE_MASTER, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-bridge.h b/src/grp-network/libnetworkd-core/networkd-netdev-bridge.h new file mode 100644 index 0000000000..a637aea0a3 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-bridge.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + 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 . +***/ + +#include "networkd-netdev.h" + +typedef struct Bridge { + NetDev meta; + + int mcast_querier; + int mcast_snooping; + int vlan_filtering; + + usec_t forward_delay; + usec_t hello_time; + usec_t max_age; +} Bridge; + +DEFINE_NETDEV_CAST(BRIDGE, Bridge); +extern const NetDevVTable bridge_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-dummy.c b/src/grp-network/libnetworkd-core/networkd-netdev-dummy.c new file mode 100644 index 0000000000..6617a86c20 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-dummy.c @@ -0,0 +1,28 @@ +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + 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 . +***/ + + +#include "networkd-netdev-dummy.h" + +const NetDevVTable dummy_vtable = { + .object_size = sizeof(Dummy), + .sections = "Match\0NetDev\0", + .create_type = NETDEV_CREATE_INDEPENDENT, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-dummy.h b/src/grp-network/libnetworkd-core/networkd-netdev-dummy.h new file mode 100644 index 0000000000..efe302267e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-dummy.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + 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 . +***/ + +#include "networkd-netdev.h" + +typedef struct Dummy { + NetDev meta; +} Dummy; + +DEFINE_NETDEV_CAST(DUMMY, Dummy); +extern const NetDevVTable dummy_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-gperf.gperf b/src/grp-network/libnetworkd-core/networkd-netdev-gperf.gperf new file mode 100644 index 0000000000..e478b53f43 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-gperf.gperf @@ -0,0 +1,111 @@ +%{ +#include + +#include "shared/conf-parser.h" +#include "shared/vlan-util.h" +#include "systemd-network/network-internal.h" + +#include "networkd-netdev-bond.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev-ipvlan.h" +#include "networkd-netdev-macvlan.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vrf.h" +#include "networkd-netdev-vxlan.h" +#include "networkd-netdev.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name network_netdev_gperf_hash +%define lookup-function-name network_netdev_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) +NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) +NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) +NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) +NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) +NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) +VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) +Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) +Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) +Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) +Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) +Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) +Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) +Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) +Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) +Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) +VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) +VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) +VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) +VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) +VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) +VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) +VXLAN.PortRange, config_parse_port_range, 0, 0 +VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port) +Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) +Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) +Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) +Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) +Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) +Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) +Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 +Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) +Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) +Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) +Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) +Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) +Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) +Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) +Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) +Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) +Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) +Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) +Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) +Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) +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) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) +Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.c b/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.c new file mode 100644 index 0000000000..724fc86f92 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.c @@ -0,0 +1,74 @@ +/*** + This file is part of systemd. + + Copyright 2013-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 . +***/ + +#include + +#include "basic/string-table.h" +#include "shared/conf-parser.h" + +#include "networkd-netdev-ipvlan.h" + +static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = { + [NETDEV_IPVLAN_MODE_L2] = "L2", + [NETDEV_IPVLAN_MODE_L3] = "L3", +}; + +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; + int r; + + assert(netdev); + 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) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m"); + } + + return 0; +} + +static void ipvlan_init(NetDev *n) { + IPVlan *m; + + assert(n); + + m = IPVLAN(n); + + assert(m); + + m->mode = _NETDEV_IPVLAN_MODE_INVALID; +} + +const NetDevVTable ipvlan_vtable = { + .object_size = sizeof(IPVlan), + .init = ipvlan_init, + .sections = "Match\0NetDev\0IPVLAN\0", + .fill_message_create = netdev_ipvlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.h b/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.h new file mode 100644 index 0000000000..a7ccd4922a --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-ipvlan.h @@ -0,0 +1,45 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014-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 . +***/ + +#include "basic/missing.h" + +#include "networkd-netdev.h" + +typedef enum IPVlanMode { + NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, + NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3, + _NETDEV_IPVLAN_MODE_MAX, + _NETDEV_IPVLAN_MODE_INVALID = -1 +} IPVlanMode; + +typedef struct IPVlan { + NetDev meta; + + IPVlanMode mode; +} IPVlan; + +DEFINE_NETDEV_CAST(IPVLAN, IPVlan); +extern const NetDevVTable ipvlan_vtable; + +const char *ipvlan_mode_to_string(IPVlanMode d) _const_; +IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; + +int config_parse_ipvlan_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); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.c b/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.c new file mode 100644 index 0000000000..bf559f1b46 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.c @@ -0,0 +1,90 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/string-table.h" +#include "shared/conf-parser.h" + +#include "networkd-netdev-macvlan.h" + +static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = { + [NETDEV_MACVLAN_MODE_PRIVATE] = "private", + [NETDEV_MACVLAN_MODE_VEPA] = "vepa", + [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge", + [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru", +}; + +DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode"); + +static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { + MacVlan *m; + int r; + + assert(netdev); + assert(link); + assert(netdev->ifname); + + if (netdev->kind == NETDEV_KIND_MACVLAN) + m = MACVLAN(netdev); + else + m = MACVTAP(netdev); + + assert(m); + + if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { + r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m"); + } + + return 0; +} + +static void macvlan_init(NetDev *n) { + MacVlan *m; + + assert(n); + + if (n->kind == NETDEV_KIND_MACVLAN) + m = MACVLAN(n); + else + m = MACVTAP(n); + + assert(m); + + m->mode = _NETDEV_MACVLAN_MODE_INVALID; +} + +const NetDevVTable macvtap_vtable = { + .object_size = sizeof(MacVlan), + .init = macvlan_init, + .sections = "Match\0NetDev\0MACVTAP\0", + .fill_message_create = netdev_macvlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, +}; + +const NetDevVTable macvlan_vtable = { + .object_size = sizeof(MacVlan), + .init = macvlan_init, + .sections = "Match\0NetDev\0MACVLAN\0", + .fill_message_create = netdev_macvlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.h b/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.h new file mode 100644 index 0000000000..3663f4f051 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-macvlan.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + 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 . +***/ + +typedef struct MacVlan MacVlan; + +#include "networkd-netdev.h" + +typedef enum MacVlanMode { + NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, + NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA, + NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE, + NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU, + _NETDEV_MACVLAN_MODE_MAX, + _NETDEV_MACVLAN_MODE_INVALID = -1 +} MacVlanMode; + +struct MacVlan { + NetDev meta; + + MacVlanMode mode; +}; + +DEFINE_NETDEV_CAST(MACVLAN, MacVlan); +DEFINE_NETDEV_CAST(MACVTAP, MacVlan); +extern const NetDevVTable macvlan_vtable; +extern const NetDevVTable macvtap_vtable; + +const char *macvlan_mode_to_string(MacVlanMode d) _const_; +MacVlanMode macvlan_mode_from_string(const char *d) _pure_; + +int config_parse_macvlan_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); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.c b/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.c new file mode 100644 index 0000000000..e94c19126b --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.c @@ -0,0 +1,726 @@ +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include +#include +#include + +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/conf-parser.h" + +#include "networkd-link.h" +#include "networkd-netdev-tunnel.h" + +#define DEFAULT_TNL_HOP_LIMIT 64 +#define IP6_FLOWINFO_FLOWLABEL htobe32(0x000FFFFF) + +static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = { + [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6", + [NETDEV_IP6_TNL_MODE_IPIP6] = "ipip6", + [NETDEV_IP6_TNL_MODE_ANYIP6] = "any", +}; + +DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode); +DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode, "Failed to parse ip6 tunnel Mode"); + +static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = IPIP(netdev); + int r; + + assert(netdev); + assert(link); + assert(m); + assert(t); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); + + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); + + return r; +} + +static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = SIT(netdev); + int r; + + assert(netdev); + assert(link); + assert(m); + assert(t); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); + + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); + + return r; +} + +static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t; + int r; + + assert(netdev); + + if (netdev->kind == NETDEV_KIND_GRE) + t = GRE(netdev); + else + t = GRETAP(netdev); + + assert(t); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); + assert(link); + assert(m); + + r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in); + if (r < 0) + log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_TOS, t->tos); + if (r < 0) + log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m"); + + return r; +} + +static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t; + int r; + + assert(netdev); + + if (netdev->kind == NETDEV_KIND_IP6GRE) + t = IP6GRE(netdev); + else + t = IP6GRETAP(netdev); + + assert(t); + assert(t->family == AF_INET6); + assert(link); + assert(m); + + r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_REMOTE, &t->remote.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); + + if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_GRE_FLOWINFO, t->ipv6_flowlabel); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLOWINFO attribute: %m"); + } + + r = sd_netlink_message_append_u32(m, IFLA_GRE_FLAGS, t->flags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLAGS attribute: %m"); + + return r; +} + +static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = VTI(netdev); + uint32_t ikey, okey; + int r; + + assert(link); + assert(m); + assert(t); + + if (t->key != 0) + ikey = okey = htobe32(t->key); + else { + ikey = htobe32(t->ikey); + okey = htobe32(t->okey); + } + + r = sd_netlink_message_append_u32(m, IFLA_VTI_IKEY, ikey); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_IKEY attribute: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_VTI_OKEY, okey); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m"); + + return 0; +} + +static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = VTI(netdev); + int r; + + assert(netdev); + assert(link); + assert(m); + assert(t); + assert(t->family == AF_INET); + + r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + + r = netdev_vti_fill_message_key(netdev, link, m); + if (r < 0) + return r; + + r = sd_netlink_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + + return r; +} + +static int netdev_vti6_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = VTI6(netdev); + int r; + + assert(netdev); + assert(link); + assert(m); + assert(t); + assert(t->family == AF_INET6); + + r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + + r = netdev_vti_fill_message_key(netdev, link, m); + if (r < 0) + return r; + + r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_LOCAL, &t->local.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_REMOTE, &t->remote.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + + return r; +} + +static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t = IP6TNL(netdev); + uint8_t proto; + int r; + + assert(netdev); + assert(link); + assert(m); + assert(t); + assert(t->family == AF_INET6); + + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); + + if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLOWINFO, t->ipv6_flowlabel); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLOWINFO attribute: %m"); + } + + if (t->copy_dscp) + t->flags |= IP6_TNL_F_RCV_DSCP_COPY; + + if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m"); + } + + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); + + switch (t->ip6tnl_mode) { + case NETDEV_IP6_TNL_MODE_IP6IP6: + proto = IPPROTO_IPV6; + break; + case NETDEV_IP6_TNL_MODE_IPIP6: + proto = IPPROTO_IPIP; + break; + case NETDEV_IP6_TNL_MODE_ANYIP6: + default: + proto = 0; + break; + } + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_MODE attribute: %m"); + + return r; +} + +static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { + Tunnel *t = NULL; + + assert(netdev); + assert(filename); + + switch (netdev->kind) { + case NETDEV_KIND_IPIP: + t = IPIP(netdev); + break; + case NETDEV_KIND_SIT: + t = SIT(netdev); + break; + case NETDEV_KIND_GRE: + t = GRE(netdev); + break; + case NETDEV_KIND_GRETAP: + t = GRETAP(netdev); + break; + case NETDEV_KIND_IP6GRE: + t = IP6GRE(netdev); + break; + case NETDEV_KIND_IP6GRETAP: + t = IP6GRETAP(netdev); + break; + case NETDEV_KIND_VTI: + t = VTI(netdev); + break; + case NETDEV_KIND_VTI6: + t = VTI6(netdev); + break; + case NETDEV_KIND_IP6TNL: + t = IP6TNL(netdev); + break; + default: + assert_not_reached("Invalid tunnel kind"); + } + + assert(t); + + 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; + } + + if (netdev->kind == NETDEV_KIND_IP6TNL) { + if (t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) { + log_warning("IP6 Tunnel without mode configured in %s. Ignoring", filename); + return -EINVAL; + } + } + + return 0; +} + +int config_parse_tunnel_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Tunnel *t = userdata; + union in_addr_union *addr = data, buffer; + int r, f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "any")) { + t->family = 0; + return 0; + } else { + + 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; + *addr = buffer; + + return 0; +} + +int config_parse_tunnel_key(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) { + union in_addr_union buffer; + Tunnel *t = userdata; + uint32_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = in_addr_from_string(AF_INET, rvalue, &buffer); + if (r < 0) { + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue); + return 0; + } + } else + k = be32toh(buffer.in.s_addr); + + if (streq(lvalue, "Key")) + t->key = k; + else if (streq(lvalue, "InputKey")) + t->ikey = k; + else + t->okey = k; + + return 0; +} + +int config_parse_ipv6_flowlabel(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) { + IPv6FlowLabel *ipv6_flowlabel = data; + Tunnel *t = userdata; + int k = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ipv6_flowlabel); + + if (streq(rvalue, "inherit")) { + *ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; + 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) + 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 = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; + t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; + } + } + + return 0; +} + +int config_parse_encap_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) { + Tunnel *t = userdata; + int k = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "none")) + t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + 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); + return 0; + } + + if (k > 255 || k < 0) + 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; + } + } + + return 0; +} + +static void ipip_init(NetDev *n) { + Tunnel *t = IPIP(n); + + assert(n); + assert(t); + + t->pmtudisc = true; + t->family = AF_UNSPEC; +} + +static void sit_init(NetDev *n) { + Tunnel *t = SIT(n); + + assert(n); + assert(t); + + t->pmtudisc = true; + t->family = AF_UNSPEC; +} + +static void vti_init(NetDev *n) { + Tunnel *t; + + assert(n); + + if (n->kind == NETDEV_KIND_VTI) + t = VTI(n); + else + t = VTI6(n); + + assert(t); + + t->pmtudisc = true; +} + +static void gre_init(NetDev *n) { + Tunnel *t; + + assert(n); + + if (n->kind == NETDEV_KIND_GRE) + t = GRE(n); + else + t = GRETAP(n); + + assert(t); + + t->pmtudisc = true; + t->family = AF_UNSPEC; +} + +static void ip6gre_init(NetDev *n) { + Tunnel *t; + + assert(n); + + if (n->kind == NETDEV_KIND_IP6GRE) + t = IP6GRE(n); + else + t = IP6GRETAP(n); + + assert(t); + + t->ttl = DEFAULT_TNL_HOP_LIMIT; +} + +static void ip6tnl_init(NetDev *n) { + Tunnel *t = IP6TNL(n); + + assert(n); + assert(t); + + t->ttl = DEFAULT_TNL_HOP_LIMIT; + t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID; + t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID; +} + +const NetDevVTable ipip_vtable = { + .object_size = sizeof(Tunnel), + .init = ipip_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_ipip_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable sit_vtable = { + .object_size = sizeof(Tunnel), + .init = sit_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_sit_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable vti_vtable = { + .object_size = sizeof(Tunnel), + .init = vti_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_vti_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable vti6_vtable = { + .object_size = sizeof(Tunnel), + .init = vti_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_vti6_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable gre_vtable = { + .object_size = sizeof(Tunnel), + .init = gre_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_gre_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable gretap_vtable = { + .object_size = sizeof(Tunnel), + .init = gre_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_gre_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable ip6gre_vtable = { + .object_size = sizeof(Tunnel), + .init = ip6gre_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_ip6gre_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable ip6gretap_vtable = { + .object_size = sizeof(Tunnel), + .init = ip6gre_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_ip6gre_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; + +const NetDevVTable ip6tnl_vtable = { + .object_size = sizeof(Tunnel), + .init = ip6tnl_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_ip6tnl_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_tunnel_verify, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.h b/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.h new file mode 100644 index 0000000000..09bd86656e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-tunnel.h @@ -0,0 +1,119 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/in-addr-util.h" + +#include "networkd-netdev.h" + +typedef enum Ip6TnlMode { + NETDEV_IP6_TNL_MODE_IP6IP6, + NETDEV_IP6_TNL_MODE_IPIP6, + NETDEV_IP6_TNL_MODE_ANYIP6, + _NETDEV_IP6_TNL_MODE_MAX, + _NETDEV_IP6_TNL_MODE_INVALID = -1, +} Ip6TnlMode; + +typedef enum IPv6FlowLabel { + NETDEV_IPV6_FLOWLABEL_INHERIT = 0xFFFFF + 1, + _NETDEV_IPV6_FLOWLABEL_MAX, + _NETDEV_IPV6_FLOWLABEL_INVALID = -1, +} IPv6FlowLabel; + +typedef struct Tunnel { + NetDev meta; + + uint8_t encap_limit; + + int family; + int ipv6_flowlabel; + + unsigned ttl; + unsigned tos; + unsigned flags; + + uint32_t key; + uint32_t ikey; + uint32_t okey; + + union in_addr_union local; + union in_addr_union remote; + + Ip6TnlMode ip6tnl_mode; + + bool pmtudisc; + bool copy_dscp; +} Tunnel; + +DEFINE_NETDEV_CAST(IPIP, Tunnel); +DEFINE_NETDEV_CAST(GRE, Tunnel); +DEFINE_NETDEV_CAST(GRETAP, Tunnel); +DEFINE_NETDEV_CAST(IP6GRE, Tunnel); +DEFINE_NETDEV_CAST(IP6GRETAP, Tunnel); +DEFINE_NETDEV_CAST(SIT, Tunnel); +DEFINE_NETDEV_CAST(VTI, Tunnel); +DEFINE_NETDEV_CAST(VTI6, Tunnel); +DEFINE_NETDEV_CAST(IP6TNL, Tunnel); +extern const NetDevVTable ipip_vtable; +extern const NetDevVTable sit_vtable; +extern const NetDevVTable vti_vtable; +extern const NetDevVTable vti6_vtable; +extern const NetDevVTable gre_vtable; +extern const NetDevVTable gretap_vtable; +extern const NetDevVTable ip6gre_vtable; +extern const NetDevVTable ip6gretap_vtable; +extern const NetDevVTable ip6tnl_vtable; + +const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_; +Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_; + +int config_parse_ip6tnl_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_tunnel_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + +int config_parse_ipv6_flowlabel(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_encap_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_tunnel_key(const char *unit, const char *filename, + unsigned line, const char *section, + unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, + void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.c b/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.c new file mode 100644 index 0000000000..ab5b90fcc9 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.c @@ -0,0 +1,185 @@ +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/user-util.h" + +#include "networkd-netdev-tuntap.h" + +#define TUN_DEV "/dev/net/tun" + +static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) { + TunTap *t; + + assert(netdev); + assert(netdev->ifname); + assert(ifr); + + if (netdev->kind == NETDEV_KIND_TAP) { + t = TAP(netdev); + ifr->ifr_flags |= IFF_TAP; + } else { + t = TUN(netdev); + ifr->ifr_flags |= IFF_TUN; + } + + if (!t->packet_info) + ifr->ifr_flags |= IFF_NO_PI; + + if (t->one_queue) + ifr->ifr_flags |= IFF_ONE_QUEUE; + + if (t->multi_queue) + ifr->ifr_flags |= IFF_MULTI_QUEUE; + + if (t->vnet_hdr) + ifr->ifr_flags |= IFF_VNET_HDR; + + strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1); + + return 0; +} + +static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { + _cleanup_close_ int fd; + TunTap *t = NULL; + const char *user; + const char *group; + uid_t uid; + gid_t gid; + int r; + + assert(netdev); + assert(ifr); + + fd = open(TUN_DEV, O_RDWR); + if (fd < 0) + return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); + + r = ioctl(fd, TUNSETIFF, ifr); + if (r < 0) + return log_netdev_error_errno(netdev, -errno, "TUNSETIFF failed on tun dev: %m"); + + if (netdev->kind == NETDEV_KIND_TAP) + t = TAP(netdev); + else + t = TUN(netdev); + + assert(t); + + if (t->user_name) { + + user = t->user_name; + + r = get_user_creds(&user, &uid, NULL, NULL, NULL); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); + + r = ioctl(fd, TUNSETOWNER, uid); + if (r < 0) + return log_netdev_error_errno(netdev, -errno, "TUNSETOWNER failed on tun dev: %m"); + } + + if (t->group_name) { + + group = t->group_name; + + r = get_group_creds(&group, &gid); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); + + r = ioctl(fd, TUNSETGROUP, gid); + if (r < 0) + return log_netdev_error_errno(netdev, -errno, "TUNSETGROUP failed on tun dev: %m"); + + } + + r = ioctl(fd, TUNSETPERSIST, 1); + if (r < 0) + return log_netdev_error_errno(netdev, -errno, "TUNSETPERSIST failed on tun dev: %m"); + + return 0; +} + +static int netdev_create_tuntap(NetDev *netdev) { + struct ifreq ifr = {}; + int r; + + r = netdev_fill_tuntap_message(netdev, &ifr); + if (r < 0) + return r; + + return netdev_tuntap_add(netdev, &ifr); +} + +static void tuntap_done(NetDev *netdev) { + TunTap *t = NULL; + + assert(netdev); + + if (netdev->kind == NETDEV_KIND_TUN) + t = TUN(netdev); + else + t = TAP(netdev); + + assert(t); + + t->user_name = mfree(t->user_name); + t->group_name = mfree(t->group_name); +} + +static int tuntap_verify(NetDev *netdev, const char *filename) { + assert(netdev); + + if (netdev->mtu) + log_netdev_warning(netdev, "MTU configured for %s, ignoring", netdev_kind_to_string(netdev->kind)); + + if (netdev->mac) + log_netdev_warning(netdev, "MAC configured for %s, ignoring", netdev_kind_to_string(netdev->kind)); + + return 0; +} + +const NetDevVTable tun_vtable = { + .object_size = sizeof(TunTap), + .sections = "Match\0NetDev\0Tun\0", + .config_verify = tuntap_verify, + .done = tuntap_done, + .create = netdev_create_tuntap, + .create_type = NETDEV_CREATE_INDEPENDENT, +}; + +const NetDevVTable tap_vtable = { + .object_size = sizeof(TunTap), + .sections = "Match\0NetDev\0Tap\0", + .config_verify = tuntap_verify, + .done = tuntap_done, + .create = netdev_create_tuntap, + .create_type = NETDEV_CREATE_INDEPENDENT, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.h b/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.h new file mode 100644 index 0000000000..120f00a353 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-tuntap.h @@ -0,0 +1,40 @@ +#pragma once + +/*** + 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 . +***/ + +typedef struct TunTap TunTap; + +#include "networkd-netdev.h" + +struct TunTap { + NetDev meta; + + char *user_name; + char *group_name; + bool one_queue; + bool multi_queue; + bool packet_info; + bool vnet_hdr; +}; + +DEFINE_NETDEV_CAST(TUN, TunTap); +DEFINE_NETDEV_CAST(TAP, TunTap); +extern const NetDevVTable tun_vtable; +extern const NetDevVTable tap_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-veth.c b/src/grp-network/libnetworkd-core/networkd-netdev-veth.c new file mode 100644 index 0000000000..fb068c75bf --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-veth.c @@ -0,0 +1,112 @@ +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "sd-netlink/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; + int r; + + assert(netdev); + assert(!link); + 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"); + + if (v->ifname_peer) { + r = sd_netlink_message_append_string(m, IFLA_IFNAME, v->ifname_peer); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + } + + if (v->mac_peer) { + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, v->mac_peer); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + return r; +} + +static int netdev_veth_verify(NetDev *netdev, const char *filename) { + Veth *v; + int r; + + assert(netdev); + assert(filename); + + v = VETH(netdev); + + assert(v); + + if (!v->ifname_peer) { + log_warning("Veth NetDev without peer name configured in %s. Ignoring", + filename); + return -EINVAL; + } + + if (!v->mac_peer) { + r = netdev_get_mac(v->ifname_peer, &v->mac_peer); + if (r < 0) { + log_warning("Failed to generate predictable MAC address for %s. Ignoring", + v->ifname_peer); + return -EINVAL; + } + } + + return 0; +} + +static void veth_done(NetDev *n) { + Veth *v; + + assert(n); + + v = VETH(n); + + assert(v); + + free(v->ifname_peer); + free(v->mac_peer); +} + +const NetDevVTable veth_vtable = { + .object_size = sizeof(Veth), + .sections = "Match\0NetDev\0Peer\0", + .done = veth_done, + .fill_message_create = netdev_veth_fill_message_create, + .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = netdev_veth_verify, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-veth.h b/src/grp-network/libnetworkd-core/networkd-netdev-veth.h new file mode 100644 index 0000000000..e69bfbc8f0 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-veth.h @@ -0,0 +1,34 @@ +#pragma once + +/*** + 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 . +***/ + +typedef struct Veth Veth; + +#include "networkd-netdev.h" + +struct Veth { + NetDev meta; + + char *ifname_peer; + struct ether_addr *mac_peer; +}; + +DEFINE_NETDEV_CAST(VETH, Veth); +extern const NetDevVTable veth_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vlan.c b/src/grp-network/libnetworkd-core/networkd-netdev-vlan.c new file mode 100644 index 0000000000..0c24d5e859 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vlan.c @@ -0,0 +1,79 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "shared/vlan-util.h" + +#include "networkd-netdev-vlan.h" + +static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { + VLan *v; + int r; + + assert(netdev); + assert(link); + assert(req); + + v = VLAN(netdev); + + assert(v); + + r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_ID attribute: %m"); + + return 0; +} + +static int netdev_vlan_verify(NetDev *netdev, const char *filename) { + VLan *v; + + assert(netdev); + assert(filename); + + v = VLAN(netdev); + + assert(v); + + if (v->id == VLANID_INVALID) { + log_warning("VLAN without valid Id (%"PRIu16") configured in %s.", v->id, filename); + return -EINVAL; + } + + return 0; +} + +static void vlan_init(NetDev *netdev) { + VLan *v = VLAN(netdev); + + assert(netdev); + assert(v); + + v->id = VLANID_INVALID; +} + +const NetDevVTable vlan_vtable = { + .object_size = sizeof(VLan), + .init = vlan_init, + .sections = "Match\0NetDev\0VLAN\0", + .fill_message_create = netdev_vlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_vlan_verify, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vlan.h b/src/grp-network/libnetworkd-core/networkd-netdev-vlan.h new file mode 100644 index 0000000000..2dfe314b6e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vlan.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + 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 . +***/ + +typedef struct VLan VLan; + +#include "networkd-netdev.h" + +struct VLan { + NetDev meta; + + uint16_t id; +}; + +DEFINE_NETDEV_CAST(VLAN, VLan); +extern const NetDevVTable vlan_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vrf.c b/src/grp-network/libnetworkd-core/networkd-netdev-vrf.c new file mode 100644 index 0000000000..8f91a11757 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vrf.c @@ -0,0 +1,51 @@ +/*** + This file is part of systemd. + + Copyright 2016 Andreas Rammhold + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/missing.h" +#include "sd-netlink/sd-netlink.h" + +#include "networkd-netdev-vrf.h" + +static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Vrf *v; + int r; + + assert(netdev); + assert(!link); + assert(m); + + v = VRF(netdev); + + assert(v); + + r = sd_netlink_message_append_u32(m, IFLA_VRF_TABLE, v->table_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IPLA_VRF_TABLE attribute: %m"); + + return r; +} + +const NetDevVTable vrf_vtable = { + .object_size = sizeof(Vrf), + .sections = "NetDev\0VRF\0", + .fill_message_create = netdev_vrf_fill_message_create, + .create_type = NETDEV_CREATE_MASTER, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vrf.h b/src/grp-network/libnetworkd-core/networkd-netdev-vrf.h new file mode 100644 index 0000000000..3d92a26a4d --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vrf.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Andreas Rammhold + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Vrf Vrf; + +#include "networkd-netdev.h" + +struct Vrf { + NetDev meta; + + uint32_t table_id; +}; + +DEFINE_NETDEV_CAST(VRF, Vrf); +extern const NetDevVTable vrf_vtable; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.c b/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.c new file mode 100644 index 0000000000..8c611ac5db --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.c @@ -0,0 +1,295 @@ +/*** + This file is part of systemd. + + Copyright 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/conf-parser.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; + int r; + + assert(netdev); + 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); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); + } + + r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); + + if (v->ttl) { + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); + } + + if (v->tos) { + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); + } + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LEARNING attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_RSC, v->route_short_circuit); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_RSC attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_PROXY, v->arp_proxy); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PROXY attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L2MISS, v->l2miss); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L2MISS attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L3MISS, v->l3miss); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); + + if (v->fdb_ageing) { + r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); + if (r < 0) + 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"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_TX attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m"); + + if (v->port_range.low || v->port_range.high) { + struct ifla_vxlan_port_range port_range; + + port_range.low = htobe16(v->port_range.low); + port_range.high = htobe16(v->port_range.high); + + r = sd_netlink_message_append_data(m, IFLA_VXLAN_PORT_RANGE, &port_range, sizeof(port_range)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT_RANGE attribute: %m"); + } + + if (v->group_policy) { + r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GBP attribute: %m"); + } + + return r; +} + +int config_parse_vxlan_group_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + VxLan *v = userdata; + union in_addr_union *addr = data, buffer; + int r, f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = in_addr_from_string_auto(rvalue, &f, &buffer); + if (r < 0) { + 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, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue); + return 0; + } + + v->family = f; + *addr = buffer; + + return 0; +} + +int config_parse_port_range(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 *word = NULL; + VxLan *v = userdata; + unsigned low, high; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract VXLAN port range, ignoring: %s", rvalue); + return 0; + } + + if (r == 0) + return 0; + + r = parse_range(word, &low, &high); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN port range '%s'", word); + return 0; + } + + if (low <= 0 || low > 65535 || high <= 0 || high > 65535) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", word); + return 0; + } + + if (high < low) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse VXLAN port range '%s'. Port range %u .. %u not valid", word, low, high); + return 0; + } + + v->port_range.low = low; + v->port_range.high = high; + + return 0; +} + +int config_parse_destination_port(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) { + VxLan *v = userdata; + uint16_t port; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou16(rvalue, &port); + if (r < 0 || port <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN destination port '%s'.", rvalue); + return 0; + } + + v->dest_port = port; + + return 0; +} + +static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { + VxLan *v = VXLAN(netdev); + + assert(netdev); + assert(v); + assert(filename); + + if (v->id > VXLAN_VID_MAX) { + log_warning("VXLAN without valid Id configured in %s. Ignoring", filename); + return -EINVAL; + } + + return 0; +} + +static void vxlan_init(NetDev *netdev) { + VxLan *v; + + assert(netdev); + + v = VXLAN(netdev); + + assert(v); + + v->id = VXLAN_VID_MAX + 1; + v->learning = true; + v->udpcsum = false; + v->udp6zerocsumtx = false; + v->udp6zerocsumrx = false; +} + +const NetDevVTable vxlan_vtable = { + .object_size = sizeof(VxLan), + .init = vxlan_init, + .sections = "Match\0NetDev\0VXLAN\0", + .fill_message_create = netdev_vxlan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, + .config_verify = netdev_vxlan_verify, +}; diff --git a/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.h b/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.h new file mode 100644 index 0000000000..7950c867e3 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev-vxlan.h @@ -0,0 +1,92 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/in-addr-util.h" + +typedef struct VxLan VxLan; + +#include "networkd-netdev.h" + +#define VXLAN_VID_MAX (1u << 24) - 1 + +struct VxLan { + NetDev meta; + + uint64_t id; + + int family; + union in_addr_union group; + + unsigned tos; + unsigned ttl; + unsigned max_fdb; + + uint16_t dest_port; + + usec_t fdb_ageing; + + bool learning; + bool arp_proxy; + bool route_short_circuit; + bool l2miss; + bool l3miss; + bool udpcsum; + bool udp6zerocsumtx; + bool udp6zerocsumrx; + bool group_policy; + + struct ifla_vxlan_port_range port_range; +}; + +DEFINE_NETDEV_CAST(VXLAN, VxLan); +extern const NetDevVTable vxlan_vtable; + +int config_parse_vxlan_group_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); +int config_parse_port_range(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_port(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-netdev.c b/src/grp-network/libnetworkd-core/networkd-netdev.c new file mode 100644 index 0000000000..7cc5969cce --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev.c @@ -0,0 +1,717 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fd-util.h" +#include "basic/list.h" +#include "basic/siphash24.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/conf-parser.h" +#include "systemd-network/network-internal.h" + +#include "networkd-netdev.h" +#include "networkd.h" + +const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { + + [NETDEV_KIND_BRIDGE] = &bridge_vtable, + [NETDEV_KIND_BOND] = &bond_vtable, + [NETDEV_KIND_VLAN] = &vlan_vtable, + [NETDEV_KIND_MACVLAN] = &macvlan_vtable, + [NETDEV_KIND_MACVTAP] = &macvtap_vtable, + [NETDEV_KIND_IPVLAN] = &ipvlan_vtable, + [NETDEV_KIND_VXLAN] = &vxlan_vtable, + [NETDEV_KIND_IPIP] = &ipip_vtable, + [NETDEV_KIND_GRE] = &gre_vtable, + [NETDEV_KIND_GRETAP] = &gretap_vtable, + [NETDEV_KIND_IP6GRE] = &ip6gre_vtable, + [NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable, + [NETDEV_KIND_SIT] = &sit_vtable, + [NETDEV_KIND_VTI] = &vti_vtable, + [NETDEV_KIND_VTI6] = &vti6_vtable, + [NETDEV_KIND_VETH] = &veth_vtable, + [NETDEV_KIND_DUMMY] = &dummy_vtable, + [NETDEV_KIND_TUN] = &tun_vtable, + [NETDEV_KIND_TAP] = &tap_vtable, + [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable, + [NETDEV_KIND_VRF] = &vrf_vtable, + +}; + +static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { + [NETDEV_KIND_BRIDGE] = "bridge", + [NETDEV_KIND_BOND] = "bond", + [NETDEV_KIND_VLAN] = "vlan", + [NETDEV_KIND_MACVLAN] = "macvlan", + [NETDEV_KIND_MACVTAP] = "macvtap", + [NETDEV_KIND_IPVLAN] = "ipvlan", + [NETDEV_KIND_VXLAN] = "vxlan", + [NETDEV_KIND_IPIP] = "ipip", + [NETDEV_KIND_GRE] = "gre", + [NETDEV_KIND_GRETAP] = "gretap", + [NETDEV_KIND_IP6GRE] = "ip6gre", + [NETDEV_KIND_IP6GRETAP] = "ip6gretap", + [NETDEV_KIND_SIT] = "sit", + [NETDEV_KIND_VETH] = "veth", + [NETDEV_KIND_VTI] = "vti", + [NETDEV_KIND_VTI6] = "vti6", + [NETDEV_KIND_DUMMY] = "dummy", + [NETDEV_KIND_TUN] = "tun", + [NETDEV_KIND_TAP] = "tap", + [NETDEV_KIND_IP6TNL] = "ip6tnl", + [NETDEV_KIND_VRF] = "vrf", + +}; + +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_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + netdev_join_callback *callback; + + if (!netdev) + return; + + rtnl_message_new_synthetic_error(-ENODEV, 0, &m); + + while ((callback = netdev->callbacks)) { + if (m) { + assert(callback->link); + assert(callback->callback); + assert(netdev->manager); + assert(netdev->manager->rtnl); + + callback->callback(netdev->manager->rtnl, m, callback->link); + } + + LIST_REMOVE(callbacks, netdev->callbacks, callback); + link_unref(callback->link); + free(callback); + } +} + +static void netdev_free(NetDev *netdev) { + if (!netdev) + return; + + netdev_cancel_callbacks(netdev); + + if (netdev->ifname) + hashmap_remove(netdev->manager->netdevs, netdev->ifname); + + free(netdev->filename); + + free(netdev->description); + free(netdev->ifname); + free(netdev->mac); + + condition_free_list(netdev->match_host); + condition_free_list(netdev->match_virt); + condition_free_list(netdev->match_kernel); + condition_free_list(netdev->match_arch); + + if (NETDEV_VTABLE(netdev) && + NETDEV_VTABLE(netdev)->done) + NETDEV_VTABLE(netdev)->done(netdev); + + free(netdev); +} + +NetDev *netdev_unref(NetDev *netdev) { + if (netdev && (-- netdev->n_ref <= 0)) + netdev_free(netdev); + + return NULL; +} + +NetDev *netdev_ref(NetDev *netdev) { + if (netdev) + assert_se(++ netdev->n_ref >= 2); + + return netdev; +} + +void netdev_drop(NetDev *netdev) { + if (!netdev || netdev->state == NETDEV_STATE_LINGER) + return; + + netdev->state = NETDEV_STATE_LINGER; + + log_netdev_debug(netdev, "netdev removed"); + + netdev_cancel_callbacks(netdev); + + netdev_unref(netdev); + + return; +} + +int netdev_get(Manager *manager, const char *name, NetDev **ret) { + NetDev *netdev; + + assert(manager); + assert(name); + assert(ret); + + netdev = hashmap_get(manager->netdevs, name); + if (!netdev) { + *ret = NULL; + return -ENOENT; + } + + *ret = netdev; + + return 0; +} + +static int netdev_enter_failed(NetDev *netdev) { + netdev->state = NETDEV_STATE_FAILED; + + netdev_cancel_callbacks(netdev); + + return 0; +} + +static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(netdev); + assert(netdev->state == NETDEV_STATE_READY); + assert(netdev->manager); + assert(netdev->manager->rtnl); + assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)); + assert(link); + assert(callback); + + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_netlink_message_append_u32(req, IFLA_MASTER, netdev->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m"); + + r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL); + if (r < 0) + return log_netdev_error(netdev, "Could not send rtnetlink message: %m"); + + link_ref(link); + + log_netdev_debug(netdev, "Enslaving link '%s'", link->ifname); + + return 0; +} + +static int netdev_enter_ready(NetDev *netdev) { + netdev_join_callback *callback, *callback_next; + int r; + + assert(netdev); + assert(netdev->ifname); + + if (netdev->state != NETDEV_STATE_CREATING) + return 0; + + netdev->state = NETDEV_STATE_READY; + + log_netdev_info(netdev, "netdev ready"); + + LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) { + /* enslave the links that were attempted to be enslaved before the + * link was ready */ + r = netdev_enslave_ready(netdev, callback->link, callback->callback); + if (r < 0) + return r; + + LIST_REMOVE(callbacks, netdev->callbacks, callback); + link_unref(callback->link); + free(callback); + } + + if (NETDEV_VTABLE(netdev)->post_create) + NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL); + + return 0; +} + +/* callback for netdev's created without a backing Link */ +static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_netdev_unref_ NetDev *netdev = userdata; + int r; + + assert(netdev->state != _NETDEV_STATE_INVALID); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "Created"); + + return 1; +} + +int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { + int r; + + assert(netdev); + assert(netdev->manager); + assert(netdev->manager->rtnl); + assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)); + + if (netdev->state == NETDEV_STATE_READY) { + r = netdev_enslave_ready(netdev, link, callback); + if (r < 0) + return r; + } else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + + r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m); + if (r >= 0) + callback(netdev->manager->rtnl, m, link); + } else { + /* the netdev is not yet read, save this request for when it is */ + netdev_join_callback *cb; + + cb = new0(netdev_join_callback, 1); + if (!cb) + return log_oom(); + + cb->callback = callback; + cb->link = link; + link_ref(link); + + LIST_PREPEND(callbacks, netdev->callbacks, cb); + + log_netdev_debug(netdev, "Will enslave '%s', when ready", link->ifname); + } + + return 0; +} + +int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) { + uint16_t type; + const char *kind; + const char *received_kind; + const char *received_name; + int r, ifindex; + + assert(netdev); + assert(message); + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get rtnl message type: %m"); + + if (type != RTM_NEWLINK) { + log_netdev_error(netdev, "Cannot set ifindex from unexpected rtnl message type."); + return -EINVAL; + } + + r = sd_rtnl_message_link_get_ifindex(message, &ifindex); + if (r < 0) { + log_netdev_error_errno(netdev, r, "Could not get ifindex: %m"); + netdev_enter_failed(netdev); + return r; + } else if (ifindex <= 0) { + log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex); + netdev_enter_failed(netdev); + return -EINVAL; + } + + if (netdev->ifindex > 0) { + if (netdev->ifindex != ifindex) { + log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d", + ifindex, netdev->ifindex); + netdev_enter_failed(netdev); + return -EEXIST; + } else + /* ifindex already set to the same for this netdev */ + return 0; + } + + r = sd_netlink_message_read_string(message, IFLA_IFNAME, &received_name); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get IFNAME: %m"); + + if (!streq(netdev->ifname, received_name)) { + log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name); + netdev_enter_failed(netdev); + return r; + } + + r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m"); + + r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get KIND: %m"); + + r = sd_netlink_message_exit_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not exit container: %m"); + + if (netdev->kind == NETDEV_KIND_TAP) + /* the kernel does not distinguish between tun and tap */ + kind = "tun"; + else { + kind = netdev_kind_to_string(netdev->kind); + if (!kind) { + log_netdev_error(netdev, "Could not get kind"); + netdev_enter_failed(netdev); + return -EINVAL; + } + } + + if (!streq(kind, received_kind)) { + log_netdev_error(netdev, + "Received newlink with wrong KIND %s, " + "expected %s", received_kind, kind); + netdev_enter_failed(netdev); + return r; + } + + netdev->ifindex = ifindex; + + log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex); + + netdev_enter_ready(netdev); + + return 0; +} + +#define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48) + +int netdev_get_mac(const char *ifname, struct ether_addr **ret) { + _cleanup_free_ struct ether_addr *mac = NULL; + uint64_t result; + size_t l, sz; + uint8_t *v; + int r; + + assert(ifname); + assert(ret); + + mac = new0(struct ether_addr, 1); + if (!mac) + return -ENOMEM; + + l = strlen(ifname); + sz = sizeof(sd_id128_t) + l; + v = alloca(sz); + + /* fetch some persistent data unique to the machine */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + + /* combine with some data unique (on this machine) to this + * netdev */ + memcpy(v + sizeof(sd_id128_t), ifname, l); + + /* Let's hash the host machine ID plus the container name. We + * use a fixed, but originally randomly created hash key here. */ + result = siphash24(v, sz, HASH_KEY.bytes); + + assert_cc(ETH_ALEN <= sizeof(result)); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); + + /* see eth_random_addr in the kernel */ + mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + *ret = mac; + mac = NULL; + + return 0; +} + +static int netdev_create(NetDev *netdev, Link *link, + sd_netlink_message_handler_t callback) { + int r; + + assert(netdev); + assert(!link || callback); + + /* create netdev */ + if (NETDEV_VTABLE(netdev)->create) { + assert(!link); + + r = NETDEV_VTABLE(netdev)->create(netdev); + if (r < 0) + return r; + + log_netdev_debug(netdev, "Created"); + } else { + _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) + return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m"); + + if (netdev->mac) { + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); + } + + if (netdev->mtu) { + r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m"); + } + + if (link) { + r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m"); + } + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + if (NETDEV_VTABLE(netdev)->fill_message_create) { + r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + + if (link) { + r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + } else { + r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); + + netdev_ref(netdev); + } + + netdev->state = NETDEV_STATE_CREATING; + + log_netdev_debug(netdev, "Creating"); + } + + return 0; +} + +/* the callback must be called, possibly after a timeout, as otherwise the Link will hang */ +int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { + int r; + + assert(netdev); + assert(netdev->manager); + assert(netdev->manager->rtnl); + assert(NETDEV_VTABLE(netdev)); + + switch (NETDEV_VTABLE(netdev)->create_type) { + case NETDEV_CREATE_MASTER: + r = netdev_enslave(netdev, link, callback); + if (r < 0) + return r; + + break; + case NETDEV_CREATE_STACKED: + r = netdev_create(netdev, link, callback); + if (r < 0) + return r; + + break; + default: + assert_not_reached("Can not join independent netdev"); + } + + return 0; +} + +static int netdev_load_one(Manager *manager, const char *filename) { + _cleanup_netdev_unref_ NetDev *netdev = NULL; + _cleanup_free_ NetDev *netdev_raw = NULL; + _cleanup_fclose_ FILE *file = NULL; + int r; + + assert(manager); + assert(filename); + + file = fopen(filename, "re"); + if (!file) { + if (errno == ENOENT) + return 0; + else + return -errno; + } + + if (null_or_empty_fd(fileno(file))) { + log_debug("Skipping empty file: %s", filename); + return 0; + } + + netdev_raw = new0(NetDev, 1); + if (!netdev_raw) + return log_oom(); + + netdev_raw->kind = _NETDEV_KIND_INVALID; + + r = config_parse(NULL, filename, file, + "Match\0NetDev\0", + config_item_perf_lookup, network_netdev_gperf_lookup, + true, false, true, netdev_raw); + if (r < 0) + return r; + + r = fseek(file, 0, SEEK_SET); + if (r < 0) + return -errno; + + /* skip out early if configuration does not match the environment */ + if (net_match_config(NULL, NULL, NULL, NULL, NULL, + netdev_raw->match_host, netdev_raw->match_virt, + netdev_raw->match_kernel, netdev_raw->match_arch, + NULL, NULL, NULL, NULL, NULL, NULL) <= 0) + return 0; + + if (netdev_raw->kind == _NETDEV_KIND_INVALID) { + log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename); + return 0; + } + + if (!netdev_raw->ifname) { + log_warning("NetDev without Name configured in %s. Ignoring", filename); + return 0; + } + + netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size); + if (!netdev) + return log_oom(); + + netdev->n_ref = 1; + netdev->manager = manager; + netdev->state = _NETDEV_STATE_INVALID; + netdev->kind = netdev_raw->kind; + netdev->ifname = netdev_raw->ifname; + + if (NETDEV_VTABLE(netdev)->init) + NETDEV_VTABLE(netdev)->init(netdev); + + r = config_parse(NULL, filename, file, + NETDEV_VTABLE(netdev)->sections, + config_item_perf_lookup, network_netdev_gperf_lookup, + false, false, false, netdev); + if (r < 0) + return r; + + /* verify configuration */ + if (NETDEV_VTABLE(netdev)->config_verify) { + r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename); + if (r < 0) + return 0; + } + + netdev->filename = strdup(filename); + if (!netdev->filename) + return log_oom(); + + if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { + r = netdev_get_mac(netdev->ifname, &netdev->mac); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); + } + + r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev); + if (r < 0) + return r; + + LIST_HEAD_INIT(netdev->callbacks); + + log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); + + switch (NETDEV_VTABLE(netdev)->create_type) { + case NETDEV_CREATE_MASTER: + case NETDEV_CREATE_INDEPENDENT: + r = netdev_create(netdev, NULL, NULL); + if (r < 0) + return 0; + + break; + default: + break; + } + + netdev = NULL; + + return 0; +} + +int netdev_load(Manager *manager) { + _cleanup_strv_free_ char **files = NULL; + NetDev *netdev; + char **f; + int r; + + assert(manager); + + while ((netdev = hashmap_first(manager->netdevs))) + netdev_unref(netdev); + + r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); + if (r < 0) + return log_error_errno(r, "Failed to enumerate netdev files: %m"); + + STRV_FOREACH_BACKWARDS(f, files) { + r = netdev_load_one(manager, *f); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-netdev.h b/src/grp-network/libnetworkd-core/networkd-netdev.h new file mode 100644 index 0000000000..dcec00af47 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-netdev.h @@ -0,0 +1,200 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/list.h" +#include "basic/time-util.h" +#include "sd-netlink/sd-netlink.h" + +typedef struct netdev_join_callback netdev_join_callback; +typedef struct Link Link; + +struct netdev_join_callback { + sd_netlink_message_handler_t callback; + Link *link; + + LIST_FIELDS(netdev_join_callback, callbacks); +}; + +typedef enum NetDevKind { + NETDEV_KIND_BRIDGE, + NETDEV_KIND_BOND, + NETDEV_KIND_VLAN, + NETDEV_KIND_MACVLAN, + NETDEV_KIND_MACVTAP, + NETDEV_KIND_IPVLAN, + NETDEV_KIND_VXLAN, + NETDEV_KIND_IPIP, + NETDEV_KIND_GRE, + NETDEV_KIND_GRETAP, + NETDEV_KIND_IP6GRE, + NETDEV_KIND_IP6GRETAP, + NETDEV_KIND_SIT, + NETDEV_KIND_VETH, + NETDEV_KIND_VTI, + NETDEV_KIND_VTI6, + NETDEV_KIND_IP6TNL, + NETDEV_KIND_DUMMY, + NETDEV_KIND_TUN, + NETDEV_KIND_TAP, + NETDEV_KIND_VRF, + _NETDEV_KIND_MAX, + _NETDEV_KIND_INVALID = -1 +} NetDevKind; + +typedef enum NetDevState { + NETDEV_STATE_FAILED, + NETDEV_STATE_CREATING, + NETDEV_STATE_READY, + NETDEV_STATE_LINGER, + _NETDEV_STATE_MAX, + _NETDEV_STATE_INVALID = -1, +} NetDevState; + +typedef enum NetDevCreateType { + NETDEV_CREATE_INDEPENDENT, + NETDEV_CREATE_MASTER, + NETDEV_CREATE_STACKED, + _NETDEV_CREATE_MAX, + _NETDEV_CREATE_INVALID = -1, +} NetDevCreateType; + +typedef struct Manager Manager; +typedef struct Condition Condition; + +typedef struct NetDev { + Manager *manager; + + int n_ref; + + char *filename; + + Condition *match_host; + Condition *match_virt; + Condition *match_kernel; + Condition *match_arch; + + NetDevState state; + NetDevKind kind; + char *description; + char *ifname; + struct ether_addr *mac; + size_t mtu; + int ifindex; + + LIST_HEAD(netdev_join_callback, callbacks); +} NetDev; + +typedef struct NetDevVTable { + /* How much memory does an object of this unit type need */ + size_t object_size; + + /* Config file sections this netdev kind understands, separated + * by NUL chars */ + const char *sections; + + /* This should reset all type-specific variables. This should + * not allocate memory, and is called with zero-initialized + * data. It should hence only initialize variables that need + * to be set != 0. */ + void (*init)(NetDev *n); + + /* This should free all kind-specific variables. It should be + * idempotent. */ + void (*done)(NetDev *n); + + /* fill in message to create netdev */ + int (*fill_message_create)(NetDev *netdev, Link *link, sd_netlink_message *message); + + /* specifies if netdev is independent, or a master device or a stacked device */ + NetDevCreateType create_type; + + /* 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); +} NetDevVTable; + +extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; + +#define NETDEV_VTABLE(n) netdev_vtable[(n)->kind] + +/* For casting a netdev into the various netdev kinds */ +#define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ + static inline MixedCase* UPPERCASE(NetDev *n) { \ + if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ + return NULL; \ + \ + return (MixedCase*) n; \ + } + +/* For casting the various netdev kinds into a netdev */ +#define NETDEV(n) (&(n)->meta) + +int netdev_load(Manager *manager); +void netdev_drop(NetDev *netdev); + +NetDev *netdev_unref(NetDev *netdev); +NetDev *netdev_ref(NetDev *netdev); + +DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref); +#define _cleanup_netdev_unref_ _cleanup_(netdev_unrefp) + +int netdev_get(Manager *manager, const char *name, NetDev **ret); +int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); +int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback); +int netdev_get_mac(const char *ifname, struct ether_addr **ret); +int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t cb); + +const char *netdev_kind_to_string(NetDevKind d) _const_; +NetDevKind netdev_kind_from_string(const char *d) _pure_; + +int config_parse_netdev_kind(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 */ +const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsigned length); + +/* Macros which append INTERFACE= to the message */ + +#define log_netdev_full(netdev, level, error, ...) \ + ({ \ + NetDev *_n = (netdev); \ + _n ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _n->ifname, ##__VA_ARGS__) : \ + log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + }) + +#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_netdev_info(netdev, ...) log_netdev_full(netdev, LOG_INFO, 0, ##__VA_ARGS__) +#define log_netdev_notice(netdev, ...) log_netdev_full(netdev, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_netdev_warning(netdev, ...) log_netdev_full(netdev, LOG_WARNING, 0, ## __VA_ARGS__) +#define log_netdev_error(netdev, ...) log_netdev_full(netdev, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_netdev_debug_errno(netdev, error, ...) log_netdev_full(netdev, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_netdev_info_errno(netdev, error, ...) log_netdev_full(netdev, LOG_INFO, error, ##__VA_ARGS__) +#define log_netdev_notice_errno(netdev, error, ...) log_netdev_full(netdev, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_netdev_warning_errno(netdev, error, ...) log_netdev_full(netdev, LOG_WARNING, error, ##__VA_ARGS__) +#define log_netdev_error_errno(netdev, error, ...) log_netdev_full(netdev, LOG_ERR, error, ##__VA_ARGS__) + +#define LOG_NETDEV_MESSAGE(netdev, fmt, ...) "MESSAGE=%s: " fmt, (netdev)->ifname, ##__VA_ARGS__ +#define LOG_NETDEV_INTERFACE(netdev) "INTERFACE=%s", (netdev)->ifname diff --git a/src/grp-network/libnetworkd-core/networkd-network-bus.c b/src/grp-network/libnetworkd-core/networkd-network-bus.c new file mode 100644 index 0000000000..d4cd275ffe --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-network-bus.c @@ -0,0 +1,154 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" + +#include "networkd.h" + +static int property_get_ether_addrs( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Network *n = userdata; + const char *ether = NULL; + int r; + + assert(bus); + assert(reply); + assert(n); + + if (n->match_mac) + ether = ether_ntoa(n->match_mac); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + if (ether) { + r = sd_bus_message_append(reply, "s", strempty(ether)); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +const sd_bus_vtable network_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Description", "s", NULL, offsetof(Network, description), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Network, filename), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MatchMAC", "as", property_get_ether_addrs, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MatchPath", "as", NULL, offsetof(Network, match_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MatchDriver", "as", NULL, offsetof(Network, match_driver), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MatchType", "as", NULL, offsetof(Network, match_type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MatchName", "as", NULL, offsetof(Network, match_name), SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_VTABLE_END +}; + +static char *network_bus_path(Network *network) { + _cleanup_free_ char *name = NULL; + char *networkname, *d, *path; + int r; + + assert(network); + assert(network->filename); + + name = strdup(network->filename); + if (!name) + return NULL; + + networkname = basename(name); + + d = strrchr(networkname, '.'); + if (!d) + return NULL; + + assert(streq(d, ".network")); + + *d = '\0'; + + r = sd_bus_path_encode("/org/freedesktop/network1/network", networkname, &path); + if (r < 0) + return NULL; + + return path; +} + +int network_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; + Network *network; + int r; + + assert(bus); + assert(path); + assert(m); + assert(nodes); + + LIST_FOREACH(networks, network, m->networks) { + char *p; + + p = network_bus_path(network); + if (!p) + return -ENOMEM; + + r = strv_consume(&l, p); + if (r < 0) + return r; + } + + *nodes = l; + l = NULL; + + return 1; +} + +int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Network *network; + _cleanup_free_ char *name = NULL; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(m); + assert(found); + + r = sd_bus_path_decode(path, "/org/freedesktop/network1/network", &name); + if (r < 0) + return 0; + + r = network_get_by_name(m, name, &network); + if (r < 0) + return 0; + + *found = network; + + return 1; +} diff --git a/src/grp-network/libnetworkd-core/networkd-network-gperf.gperf b/src/grp-network/libnetworkd-core/networkd-network-gperf.gperf new file mode 100644 index 0000000000..e214790436 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-network-gperf.gperf @@ -0,0 +1,129 @@ +%{ +#include + +#include "shared/conf-parser.h" +#include "shared/vlan-util.h" +#include "systemd-network/network-internal.h" + +#include "networkd-conf.h" +#include "networkd.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name network_network_gperf_hash +%define lookup-function-name network_network_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac) +Match.Path, config_parse_strv, 0, offsetof(Network, match_path) +Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) +Match.Type, config_parse_strv, 0, offsetof(Network, match_type) +Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) +Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) +Network.Description, config_parse_string, 0, offsetof(Network, description) +Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) +Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) +Network.VLAN, config_parse_netdev, 0, 0 +Network.MACVLAN, config_parse_netdev, 0, 0 +Network.MACVTAP, config_parse_netdev, 0, 0 +Network.IPVLAN, config_parse_netdev, 0, 0 +Network.VXLAN, config_parse_netdev, 0, 0 +Network.Tunnel, config_parse_tunnel, 0, 0 +Network.VRF, config_parse_netdev, 0, 0 +Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) +Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) +Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) +Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) +Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) +Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) +Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) +Network.Address, config_parse_address, 0, 0 +Network.Gateway, config_parse_gateway, 0, 0 +Network.Domains, config_parse_domains, 0, 0 +Network.DNS, config_parse_strv, 0, offsetof(Network, dns) +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, 0 +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.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) +/* legacy alias for the above */ +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.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) +Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) +Address.Address, config_parse_address, 0, 0 +Address.Peer, config_parse_address, 0, 0 +Address.Broadcast, config_parse_broadcast, 0, 0 +Address.Label, config_parse_label, 0, 0 +Address.PreferredLifetime, config_parse_lifetime, 0, 0 +Route.Gateway, config_parse_gateway, 0, 0 +Route.Destination, config_parse_destination, 0, 0 +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 +Route.Table, config_parse_route_table, 0, 0 +DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) +DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) +DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) +DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) +DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) +DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes) +DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname) +DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) +DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) +DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) +DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) +DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) +DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) +IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) +IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) +DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) +DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) +DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) +DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 +DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) +DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 +DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) +DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) +DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) +DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) +DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) +Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) +Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) +Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) +Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave) +Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root) +Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood) +BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 +BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 +BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid) +BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 +BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 +/* backwards compatibility: do not add new entries to this section */ +Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) +DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) +DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) +DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) +DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) +DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) diff --git a/src/grp-network/libnetworkd-core/networkd-network.c b/src/grp-network/libnetworkd-core/networkd-network.c new file mode 100644 index 0000000000..697b748e52 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-network.c @@ -0,0 +1,1052 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fd-util.h" +#include "basic/hostname-util.h" +#include "basic/parse-util.h" +#include "basic/set.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/conf-parser.h" +#include "shared/dns-domain.h" +#include "systemd-network/network-internal.h" + +#include "networkd-network.h" +#include "networkd.h" + +static int network_load_one(Manager *manager, const char *filename) { + _cleanup_network_free_ Network *network = NULL; + _cleanup_fclose_ FILE *file = NULL; + char *d; + Route *route; + Address *address; + int r; + + assert(manager); + assert(filename); + + file = fopen(filename, "re"); + if (!file) { + if (errno == ENOENT) + return 0; + + return -errno; + } + + if (null_or_empty_fd(fileno(file))) { + log_debug("Skipping empty file: %s", filename); + return 0; + } + + network = new0(Network, 1); + if (!network) + return log_oom(); + + network->manager = manager; + + LIST_HEAD_INIT(network->static_addresses); + LIST_HEAD_INIT(network->static_routes); + LIST_HEAD_INIT(network->static_fdb_entries); + + network->stacked_netdevs = hashmap_new(&string_hash_ops); + if (!network->stacked_netdevs) + return log_oom(); + + network->addresses_by_section = hashmap_new(NULL); + if (!network->addresses_by_section) + return log_oom(); + + network->routes_by_section = hashmap_new(NULL); + if (!network->routes_by_section) + return log_oom(); + + network->fdb_entries_by_section = hashmap_new(NULL); + if (!network->fdb_entries_by_section) + return log_oom(); + + network->filename = strdup(filename); + if (!network->filename) + return log_oom(); + + network->name = strdup(basename(filename)); + if (!network->name) + return log_oom(); + + d = strrchr(network->name, '.'); + if (!d) + return -EINVAL; + + assert(streq(d, ".network")); + + *d = '\0'; + + network->dhcp = ADDRESS_FAMILY_NO; + network->dhcp_use_ntp = true; + network->dhcp_use_dns = true; + network->dhcp_use_hostname = true; + network->dhcp_use_routes = true; + network->dhcp_send_hostname = true; + network->dhcp_route_metric = DHCP_ROUTE_METRIC; + network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID; + + network->dhcp_server_emit_dns = true; + network->dhcp_server_emit_ntp = true; + network->dhcp_server_emit_router = true; + network->dhcp_server_emit_timezone = true; + + network->use_bpdu = true; + network->allow_port_to_be_root = true; + network->unicast_flood = true; + + network->lldp_mode = LLDP_MODE_ROUTERS_ONLY; + + 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; + network->duid.type = _DUID_TYPE_INVALID; + network->proxy_arp = -1; + network->ipv6_accept_ra_use_dns = true; + + r = config_parse(NULL, filename, file, + "Match\0" + "Link\0" + "Network\0" + "Address\0" + "Route\0" + "DHCP\0" + "DHCPv4\0" /* compat */ + "DHCPServer\0" + "IPv6AcceptRA\0" + "Bridge\0" + "BridgeFDB\0" + "BridgeVLAN\0", + config_item_perf_lookup, network_network_gperf_lookup, + false, false, true, network); + if (r < 0) + return r; + + /* IPMasquerade=yes implies IPForward=yes */ + if (network->ip_masquerade) + network->ip_forward |= ADDRESS_FAMILY_IPV4; + + LIST_PREPEND(networks, manager->networks, network); + + r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops); + if (r < 0) + return r; + + r = hashmap_put(manager->networks_by_name, network->name, network); + if (r < 0) + return r; + + LIST_FOREACH(routes, route, network->static_routes) { + if (!route->family) { + log_warning("Route section without Gateway field configured in %s. " + "Ignoring", filename); + return 0; + } + } + + LIST_FOREACH(addresses, address, network->static_addresses) { + if (!address->family) { + log_warning("Address section without Address field configured in %s. " + "Ignoring", filename); + return 0; + } + } + + network = NULL; + + return 0; +} + +int network_load(Manager *manager) { + Network *network; + _cleanup_strv_free_ char **files = NULL; + char **f; + int r; + + assert(manager); + + while ((network = manager->networks)) + network_free(network); + + r = conf_files_list_strv(&files, ".network", NULL, network_dirs); + if (r < 0) + return log_error_errno(r, "Failed to enumerate network files: %m"); + + STRV_FOREACH_BACKWARDS(f, files) { + r = network_load_one(manager, *f); + if (r < 0) + return r; + } + + return 0; +} + +void network_free(Network *network) { + NetDev *netdev; + Route *route; + Address *address; + FdbEntry *fdb_entry; + Iterator i; + + if (!network) + return; + + free(network->filename); + + free(network->match_mac); + strv_free(network->match_path); + strv_free(network->match_driver); + strv_free(network->match_type); + strv_free(network->match_name); + + free(network->description); + free(network->dhcp_vendor_class_identifier); + free(network->dhcp_hostname); + + free(network->mac); + + strv_free(network->ntp); + strv_free(network->dns); + strv_free(network->search_domains); + strv_free(network->route_domains); + strv_free(network->bind_carrier); + + netdev_unref(network->bridge); + netdev_unref(network->bond); + netdev_unref(network->vrf); + + HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) { + hashmap_remove(network->stacked_netdevs, netdev->ifname); + netdev_unref(netdev); + } + hashmap_free(network->stacked_netdevs); + + while ((route = network->static_routes)) + route_free(route); + + while ((address = network->static_addresses)) + address_free(address); + + while ((fdb_entry = network->static_fdb_entries)) + fdb_entry_free(fdb_entry); + + hashmap_free(network->addresses_by_section); + hashmap_free(network->routes_by_section); + hashmap_free(network->fdb_entries_by_section); + + if (network->manager) { + if (network->manager->networks) + LIST_REMOVE(networks, network->manager->networks, network); + + if (network->manager->networks_by_name) + hashmap_remove(network->manager->networks_by_name, network->name); + } + + free(network->name); + + condition_free_list(network->match_host); + condition_free_list(network->match_virt); + condition_free_list(network->match_kernel); + condition_free_list(network->match_arch); + + free(network->dhcp_server_timezone); + free(network->dhcp_server_dns); + free(network->dhcp_server_ntp); + + set_free_free(network->dnssec_negative_trust_anchors); + + free(network); +} + +int network_get_by_name(Manager *manager, const char *name, Network **ret) { + Network *network; + + assert(manager); + assert(name); + assert(ret); + + network = hashmap_get(manager->networks_by_name, name); + if (!network) + return -ENOENT; + + *ret = network; + + return 0; +} + +int network_get(Manager *manager, struct udev_device *device, + const char *ifname, const struct ether_addr *address, + Network **ret) { + Network *network; + struct udev_device *parent; + const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL; + + assert(manager); + assert(ret); + + if (device) { + path = udev_device_get_property_value(device, "ID_PATH"); + + parent = udev_device_get_parent(device); + if (parent) + parent_driver = udev_device_get_driver(parent); + + driver = udev_device_get_property_value(device, "ID_NET_DRIVER"); + + devtype = udev_device_get_devtype(device); + } + + LIST_FOREACH(networks, network, manager->networks) { + if (net_match_config(network->match_mac, network->match_path, + network->match_driver, network->match_type, + network->match_name, network->match_host, + network->match_virt, network->match_kernel, + network->match_arch, + address, path, parent_driver, driver, + devtype, ifname)) { + if (network->match_name && device) { + const char *attr; + uint8_t name_assign_type = NET_NAME_UNKNOWN; + + attr = udev_device_get_sysattr_value(device, "name_assign_type"); + if (attr) + (void) safe_atou8(attr, &name_assign_type); + + if (name_assign_type == NET_NAME_ENUM) + log_warning("%s: found matching network '%s', based on potentially unpredictable ifname", + ifname, network->filename); + else + log_debug("%s: found matching network '%s'", ifname, network->filename); + } else + log_debug("%s: found matching network '%s'", ifname, network->filename); + + *ret = network; + return 0; + } + } + + *ret = NULL; + + return -ENOENT; +} + +int network_apply(Manager *manager, Network *network, Link *link) { + int r; + + assert(manager); + assert(network); + assert(link); + + link->network = network; + + if (network->ipv4ll_route) { + Route *route; + + r = route_new_static(network, 0, &route); + if (r < 0) + return r; + + r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in); + if (r == 0) + return -EINVAL; + if (r < 0) + return -errno; + + route->family = AF_INET; + route->dst_prefixlen = 16; + route->scope = RT_SCOPE_LINK; + route->priority = IPV4LL_ROUTE_METRIC; + route->protocol = RTPROT_STATIC; + } + + if (!strv_isempty(network->dns) || + !strv_isempty(network->ntp) || + !strv_isempty(network->search_domains) || + !strv_isempty(network->route_domains)) { + manager_dirty(manager); + link_dirty(link); + } + + return 0; +} + +bool network_has_static_ipv6_addresses(Network *network) { + Address *address; + + assert(network); + + LIST_FOREACH(addresses, address, network->static_addresses) { + if (address->family == AF_INET6) + return true; + } + + return false; +} + +int config_parse_netdev(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_free_ char *kind_string = NULL; + char *p; + NetDev *netdev; + NetDevKind kind; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + kind_string = strdup(lvalue); + if (!kind_string) + return log_oom(); + + /* the keys are CamelCase versions of the kind */ + for (p = kind_string; *p; p++) + *p = tolower(*p); + + kind = netdev_kind_from_string(kind_string); + if (kind == _NETDEV_KIND_INVALID) { + 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, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (netdev->kind != kind) { + log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + switch (kind) { + case NETDEV_KIND_BRIDGE: + network->bridge = netdev; + + break; + case NETDEV_KIND_BOND: + network->bond = netdev; + + break; + case NETDEV_KIND_VRF: + network->vrf = netdev; + + break; + case NETDEV_KIND_VLAN: + case NETDEV_KIND_MACVLAN: + case NETDEV_KIND_MACVTAP: + case NETDEV_KIND_IPVLAN: + case NETDEV_KIND_VXLAN: + r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue); + return 0; + } + + break; + default: + assert_not_reached("Can not parse NetDev"); + } + + netdev_ref(netdev); + + return 0; +} + +int config_parse_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) { + + const char *p; + Network *n = data; + int r; + + assert(n); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + n->search_domains = strv_free(n->search_domains); + n->route_domains = strv_free(n->route_domains); + return 0; + } + + p = rvalue; + for (;;) { + _cleanup_free_ char *w = NULL, *normalized = NULL; + const char *domain; + bool is_route; + + r = extract_first_word(&p, &w, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue); + break; + } + if (r == 0) + break; + + is_route = w[0] == '~'; + domain = is_route ? w + 1 : w; + + if (dns_name_is_root(domain) || streq(domain, "*")) { + /* If the root domain appears as is, or the special token "*" is found, we'll consider this as + * routing domain, unconditionally. */ + is_route = true; + domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */ + + } else { + r = dns_name_normalize(domain, &normalized); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain); + continue; + } + + domain = normalized; + + if (is_localhost(domain)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain); + continue; + } + } + + if (is_route) { + r = strv_extend(&n->route_domains, domain); + if (r < 0) + return log_oom(); + + } else { + r = strv_extend(&n->search_domains, domain); + if (r < 0) + return log_oom(); + } + } + + strv_uniq(n->route_domains); + strv_uniq(n->search_domains); + + return 0; +} + +int config_parse_tunnel(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; + NetDev *netdev; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = netdev_get(network->manager, rvalue, &netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (netdev->kind != NETDEV_KIND_IPIP && + netdev->kind != NETDEV_KIND_SIT && + netdev->kind != NETDEV_KIND_GRE && + netdev->kind != NETDEV_KIND_GRETAP && + netdev->kind != NETDEV_KIND_IP6GRE && + netdev->kind != NETDEV_KIND_IP6GRETAP && + netdev->kind != NETDEV_KIND_VTI && + netdev->kind != NETDEV_KIND_VTI6 && + netdev->kind != NETDEV_KIND_IP6TNL + ) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "NetDev is not a tunnel, ignoring assignment: %s", rvalue); + return 0; + } + + r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue); + return 0; + } + + netdev_ref(netdev); + + return 0; +} + +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) { + + AddressFamilyBoolean *link_local = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Note that this is mostly like + * config_parse_address_family_boolean(), except that it + * applies only to IPv4 */ + + SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue)); + + return 0; +} + +int config_parse_dhcp( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + AddressFamilyBoolean *dhcp = data, s; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Note that this is mostly like + * config_parse_address_family_boolean(), except that it + * understands some old names for the enum values */ + + s = address_family_boolean_from_string(rvalue); + if (s < 0) { + + /* Previously, we had a slightly different enum here, + * support its values for compatbility. */ + + if (streq(rvalue, "none")) + s = ADDRESS_FAMILY_NO; + else if (streq(rvalue, "v4")) + s = ADDRESS_FAMILY_IPV4; + else if (streq(rvalue, "v6")) + s = ADDRESS_FAMILY_IPV6; + else if (streq(rvalue, "both")) + s = ADDRESS_FAMILY_YES; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue); + return 0; + } + } + + *dhcp = s; + return 0; +} + +static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { + [DHCP_CLIENT_ID_MAC] = "mac", + [DHCP_CLIENT_ID_DUID] = "duid" +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); + +int config_parse_ipv6token( + 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) { + + union in_addr_union buffer; + struct in6_addr *token = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(token); + + r = in_addr_from_string(AF_INET6, rvalue, &buffer); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); + return 0; + } + + r = in_addr_is_null(AF_INET6, &buffer); + if (r != 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue); + return 0; + } + + if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue); + return 0; + } + + *token = buffer.in6; + + return 0; +} + +static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { + [IPV6_PRIVACY_EXTENSIONS_NO] = "no", + [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", + [IPV6_PRIVACY_EXTENSIONS_YES] = "yes", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions); + +int config_parse_ipv6_privacy_extensions( + 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) { + + IPv6PrivacyExtensions *ipv6_privacy_extensions = data; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ipv6_privacy_extensions); + + /* 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) + *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES; + else if (k == 0) + *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO; + else { + IPv6PrivacyExtensions s; + + s = ipv6_privacy_extensions_from_string(rvalue); + if (s < 0) { + + if (streq(rvalue, "kernel")) + s = _IPV6_PRIVACY_EXTENSIONS_INVALID; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); + return 0; + } + } + + *ipv6_privacy_extensions = s; + } + + return 0; +} + +int config_parse_hostname( + 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) { + + char **hostname = data, *hn = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata); + if (r < 0) + return r; + + if (!hostname_is_valid(hn, false)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue); + free(hn); + return 0; + } + + free(*hostname); + *hostname = hostname_cleanup(hn); + return 0; +} + +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) { + + char **datap = data, *tz = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata); + if (r < 0) + return r; + + if (!timezone_is_valid(tz)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue); + free(tz); + return 0; + } + + free(*datap); + *datap = tz; + + return 0; +} + +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) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + struct in_addr a, *m; + + r = extract_first_word(&p, &w, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + + if (r == 0) + return 0; + + if (inet_pton(AF_INET, w, &a) <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); + continue; + } + + m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr)); + if (!m) + return log_oom(); + + m[n->n_dhcp_server_dns++] = a; + n->dhcp_server_dns = m; + } +} + +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) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + struct in_addr a, *m; + + r = extract_first_word(&p, &w, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + + if (r == 0) + return 0; + + if (inet_pton(AF_INET, w, &a) <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w); + continue; + } + + m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr)); + if (!m) + return log_oom(); + + m[n->n_dhcp_server_ntp++] = a; + 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(n); + 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_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops); + if (r < 0) + return log_oom(); + + r = set_put(n->dnssec_negative_trust_anchors, w); + if (r < 0) + return log_oom(); + if (r > 0) + w = NULL; + } + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); + +static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { + [DHCP_USE_DOMAINS_NO] = "no", + [DHCP_USE_DOMAINS_ROUTE] = "route", + [DHCP_USE_DOMAINS_YES] = "yes", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); + +DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); + +static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { + [LLDP_MODE_NO] = "no", + [LLDP_MODE_YES] = "yes", + [LLDP_MODE_ROUTERS_ONLY] = "routers-only", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); diff --git a/src/grp-network/libnetworkd-core/networkd-network.h b/src/grp-network/libnetworkd-core/networkd-network.h new file mode 100644 index 0000000000..66430a7c45 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-network.h @@ -0,0 +1,247 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "shared/condition.h" +#include "shared/resolve-util.h" +#include "systemd-network/dhcp-identifier.h" +#include "udev.h" + +#include "networkd-address.h" +#include "networkd-brvlan.h" +#include "networkd-fdb.h" +#include "networkd-lldp-tx.h" +#include "networkd-netdev.h" +#include "networkd-route.h" +#include "networkd-util.h" + +#define DHCP_ROUTE_METRIC 1024 +#define IPV4LL_ROUTE_METRIC 2048 + +#define BRIDGE_VLAN_BITMAP_MAX 4096 +#define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32) + +typedef enum DCHPClientIdentifier { + DHCP_CLIENT_ID_MAC, + DHCP_CLIENT_ID_DUID, + _DHCP_CLIENT_ID_MAX, + _DHCP_CLIENT_ID_INVALID = -1, +} DCHPClientIdentifier; + +typedef enum IPv6PrivacyExtensions { + /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ + IPV6_PRIVACY_EXTENSIONS_NO, + IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, + IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ + _IPV6_PRIVACY_EXTENSIONS_MAX, + _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, +} IPv6PrivacyExtensions; + +typedef enum DHCPUseDomains { + DHCP_USE_DOMAINS_NO, + DHCP_USE_DOMAINS_YES, + DHCP_USE_DOMAINS_ROUTE, + _DHCP_USE_DOMAINS_MAX, + _DHCP_USE_DOMAINS_INVALID = -1, +} DHCPUseDomains; + +typedef enum LLDPMode { + LLDP_MODE_NO = 0, + LLDP_MODE_YES = 1, + LLDP_MODE_ROUTERS_ONLY = 2, + _LLDP_MODE_MAX, + _LLDP_MODE_INVALID = -1, +} LLDPMode; + +typedef struct DUID { + /* Value of Type in [DHCP] section */ + DUIDType type; + + uint8_t raw_data_len; + uint8_t raw_data[MAX_DUID_LEN]; +} DUID; + +typedef struct Manager Manager; + +struct Network { + Manager *manager; + + char *filename; + char *name; + + struct ether_addr *match_mac; + char **match_path; + char **match_driver; + char **match_type; + char **match_name; + + Condition *match_host; + Condition *match_virt; + Condition *match_kernel; + Condition *match_arch; + + char *description; + + NetDev *bridge; + NetDev *bond; + NetDev *vrf; + Hashmap *stacked_netdevs; + + /* DHCP Client Support */ + AddressFamilyBoolean dhcp; + DCHPClientIdentifier dhcp_client_identifier; + char *dhcp_vendor_class_identifier; + char *dhcp_hostname; + bool dhcp_use_dns; + bool dhcp_use_ntp; + bool dhcp_use_mtu; + bool dhcp_use_hostname; + DHCPUseDomains dhcp_use_domains; + bool dhcp_send_hostname; + bool dhcp_broadcast; + bool dhcp_critical; + bool dhcp_use_routes; + bool dhcp_use_timezone; + unsigned dhcp_route_metric; + + /* DHCP Server Support */ + bool dhcp_server; + bool dhcp_server_emit_dns; + struct in_addr *dhcp_server_dns; + unsigned n_dhcp_server_dns; + bool dhcp_server_emit_ntp; + struct in_addr *dhcp_server_ntp; + unsigned n_dhcp_server_ntp; + bool dhcp_server_emit_router; + bool dhcp_server_emit_timezone; + char *dhcp_server_timezone; + usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; + uint32_t dhcp_server_pool_offset; + uint32_t dhcp_server_pool_size; + + /* IPV4LL Support */ + AddressFamilyBoolean link_local; + bool ipv4ll_route; + + /* Bridge Support */ + bool use_bpdu; + bool hairpin; + bool fast_leave; + bool allow_port_to_be_root; + bool unicast_flood; + unsigned cost; + + uint16_t pvid; + uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN]; + uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; + + AddressFamilyBoolean ip_forward; + bool ip_masquerade; + + int ipv6_accept_ra; + int ipv6_dad_transmits; + int ipv6_hop_limit; + int proxy_arp; + + bool ipv6_accept_ra_use_dns; + DHCPUseDomains ipv6_accept_ra_use_domains; + + union in_addr_union ipv6_token; + IPv6PrivacyExtensions ipv6_privacy_extensions; + + struct ether_addr *mac; + unsigned mtu; + uint32_t iaid; + DUID duid; + + LLDPMode lldp_mode; /* LLDP reception */ + LLDPEmit lldp_emit; /* LLDP transmission */ + + LIST_HEAD(Address, static_addresses); + LIST_HEAD(Route, static_routes); + LIST_HEAD(FdbEntry, static_fdb_entries); + + unsigned n_static_addresses; + unsigned n_static_routes; + unsigned n_static_fdb_entries; + + Hashmap *addresses_by_section; + Hashmap *routes_by_section; + Hashmap *fdb_entries_by_section; + + char **search_domains, **route_domains, **dns, **ntp, **bind_carrier; + + ResolveSupport llmnr; + ResolveSupport mdns; + DnssecMode dnssec_mode; + Set *dnssec_negative_trust_anchors; + + LIST_FIELDS(Network, networks); +}; + +void network_free(Network *network); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); +#define _cleanup_network_free_ _cleanup_(network_freep) + +int network_load(Manager *manager); + +int network_get_by_name(Manager *manager, const char *name, Network **ret); +int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); +int network_apply(Manager *manager, Network *network, Link *link); + +bool network_has_static_ipv6_addresses(Network *network); + +int config_parse_netdev(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_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_tunnel(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(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ipv6token(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_ipv6_privacy_extensions(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_hostname(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_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); +int config_parse_dhcp_use_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_lldp_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); + +/* 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); + +const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length); + +extern const sd_bus_vtable network_vtable[]; + +int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); + +const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; +IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; + +const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; +DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; + +const char* lldp_mode_to_string(LLDPMode m) _const_; +LLDPMode lldp_mode_from_string(const char *s) _pure_; diff --git a/src/grp-network/libnetworkd-core/networkd-route.c b/src/grp-network/libnetworkd-core/networkd-route.c new file mode 100644 index 0000000000..f6e2d4858e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-route.c @@ -0,0 +1,902 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/in-addr-util.h" +#include "basic/parse-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/conf-parser.h" + +#include "networkd-route.h" +#include "networkd.h" + +#define ROUTES_PER_LINK_MAX 2048U +#define STATIC_ROUTES_PER_NETWORK_MAX 1024U + +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; + + assert(network); + assert(ret); + + if (section) { + route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); + if (route) { + *ret = route; + route = NULL; + + return 0; + } + } + + if (network->n_static_routes >= STATIC_ROUTES_PER_NETWORK_MAX) + return -E2BIG; + + r = route_new(&route); + if (r < 0) + return r; + + route->protocol = RTPROT_STATIC; + + if (section) { + route->section = section; + + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); + if (r < 0) + return r; + } + + route->network = network; + LIST_PREPEND(routes, network->static_routes, route); + network->n_static_routes++; + + *ret = route; + route = NULL; + + return 0; +} + +void route_free(Route *route) { + if (!route) + return; + + if (route->network) { + LIST_REMOVE(routes, route->network->static_routes, route); + + assert(route->network->n_static_routes > 0); + route->network->n_static_routes--; + + if (route->section) + hashmap_remove(route->network->routes_by_section, 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); +} + +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, + const union in_addr_union *dst, + unsigned char dst_prefixlen, + unsigned char tos, + uint32_t priority, + unsigned char table, + Route **ret) { + + Route route, *existing; + + assert(link); + assert(dst); + + route = (Route) { + .family = family, + .dst = *dst, + .dst_prefixlen = dst_prefixlen, + .tos = tos, + .priority = priority, + .table = table, + }; + + existing = set_get(link->routes, &route); + if (existing) { + if (ret) + *ret = existing; + return 1; + } + + existing = set_get(link->routes_foreign, &route); + if (existing) { + if (ret) + *ret = existing; + return 0; + } + + return -ENOENT; +} + +static int route_add_internal( + Link *link, + Set **routes, + int family, + const 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, + const 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, + const 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, + const union in_addr_union *src, + unsigned char src_prefixlen, + const union in_addr_union *gw, + const 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; +} + +int route_remove(Route *route, Link *link, + sd_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + assert(route->family == AF_INET || route->family == AF_INET6); + + r = sd_rtnl_message_new_route(link->manager->rtnl, &req, + RTM_DELROUTE, route->family, + route->protocol); + if (r < 0) + return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); + + 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->gw.in); + else if (route->family == AF_INET6) + 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.in); + else if (route->family == AF_INET6) + 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"); + + r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + if (route->src_prefixlen) { + if (route->family == AF_INET) + 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.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); + + r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); + } + + 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.in); + else if (route->family == AF_INET6) + 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"); + } + + r = sd_rtnl_message_route_set_scope(req, route->scope); + if (r < 0) + return log_error_errno(r, "Could not set scope: %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_u32(req, RTA_OIF, link->ifindex); + if (r < 0) + return log_error_errno(r, "Could not append RTA_OIF 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"); + + link_ref(link); + + return 0; +} + +static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + link->link_messages--; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "could not remove route: %m"); + + if (link->link_messages == 0) + log_link_debug(link, "route removed"); + + return 1; +} + +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, route_expire_callback); + if (r < 0) + log_warning_errno(r, "Could not remove route: %m"); + else { + /* route may not be exist in kernel. If we fail still remove it */ + route->link->link_messages++; + route_free(route); + } + + return 1; +} + +int route_configure( + Route *route, + Link *link, + sd_netlink_message_handler_t callback) { + + _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); + assert(link->manager); + assert(link->manager->rtnl); + assert(link->ifindex > 0); + assert(route->family == AF_INET || route->family == AF_INET6); + + if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 && + set_size(link->routes) >= ROUTES_PER_LINK_MAX) + return -E2BIG; + + r = sd_rtnl_message_new_route(link->manager->rtnl, &req, + RTM_NEWROUTE, route->family, + route->protocol); + if (r < 0) + return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); + + 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->gw.in); + else if (route->family == AF_INET6) + 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"); + + r = sd_rtnl_message_route_set_family(req, route->family); + if (r < 0) + return log_error_errno(r, "Could not set route family: %m"); + } + + if (route->dst_prefixlen) { + if (route->family == AF_INET) + 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.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + + r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + if (route->src_prefixlen) { + if (route->family == AF_INET) + 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.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); + + r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); + } + + 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.in); + else if (route->family == AF_INET6) + 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"); + } + + r = sd_rtnl_message_route_set_scope(req, route->scope); + if (r < 0) + return log_error_errno(r, "Could not set scope: %m"); + + r = sd_rtnl_message_route_set_flags(req, route->flags); + if (r < 0) + return log_error_errno(r, "Could not set flags: %m"); + + if (route->table != RT_TABLE_DEFAULT) { + + if (route->table < 256) { + r = sd_rtnl_message_route_set_table(req, route->table); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + } else { + + r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + + /* Table attribute to allow more than 256. */ + r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); + if (r < 0) + return log_error_errno(r, "Could not append RTA_TABLE attribute: %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"); + + 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"); + + 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; +} + +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) { + + 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); + + if (streq(section, "Network")) { + /* we are not in an Route section, so treat + * this as the special '0' section */ + section_line = 0; + } + + 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, 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, + "Preferred source is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + n->family = f; + n->prefsrc = buffer; + n = NULL; + + return 0; +} + +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) { + + Network *network = userdata; + _cleanup_route_free_ Route *n = NULL; + const char *address, *e; + union in_addr_union buffer; + unsigned char prefixlen; + 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; + + /* Destination|Source=address/prefixlen */ + + /* address */ + e = strchr(rvalue, '/'); + if (e) + address = strndupa(rvalue, e - rvalue); + else + address = rvalue; + + r = in_addr_from_string_auto(address, &f, &buffer); + if (r < 0) { + 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, 0, "Unknown address family, ignoring assignment: %s", address); + return 0; + } + + /* prefixlen */ + if (e) { + r = safe_atou8(e + 1, &prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1); + return 0; + } + } else { + switch (f) { + case AF_INET: + prefixlen = 32; + break; + case AF_INET6: + prefixlen = 128; + break; + } + } + + n->family = f; + if (streq(lvalue, "Destination")) { + n->dst = buffer; + n->dst_prefixlen = prefixlen; + } else if (streq(lvalue, "Source")) { + n->src = buffer; + n->src_prefixlen = prefixlen; + } else + assert_not_reached(lvalue); + + n = NULL; + + return 0; +} + +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) { + Network *network = userdata; + _cleanup_route_free_ Route *n = NULL; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->priority = k; + n = NULL; + + return 0; +} + +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) { + Network *network = userdata; + _cleanup_route_free_ Route *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (streq(rvalue, "host")) + n->scope = RT_SCOPE_HOST; + else if (streq(rvalue, "link")) + n->scope = RT_SCOPE_LINK; + else if (streq(rvalue, "global")) + n->scope = RT_SCOPE_UNIVERSE; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_route_table(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_route_free_ Route *n = NULL; + Network *network = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->table = k; + + n = NULL; + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-route.h b/src/grp-network/libnetworkd-core/networkd-route.h new file mode 100644 index 0000000000..d4e4dbac0b --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-route.h @@ -0,0 +1,75 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +typedef struct Route Route; + +#include "networkd-network.h" + +struct Route { + Network *network; + unsigned section; + + Link *link; + + int family; + unsigned char dst_prefixlen; + unsigned char src_prefixlen; + unsigned char scope; + unsigned char protocol; /* RTPROT_* */ + unsigned char tos; + uint32_t priority; /* note that ip(8) calls this 'metric' */ + uint32_t table; + unsigned char pref; + unsigned flags; + + 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(Route **ret); +void route_free(Route *route); +int route_configure(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, const 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, const 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, const 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, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); + +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); +int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-network/libnetworkd-core/networkd-util.c b/src/grp-network/libnetworkd-core/networkd-util.c new file mode 100644 index 0000000000..e5a5bce8cb --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-util.c @@ -0,0 +1,102 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/parse-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/conf-parser.h" + +#include "networkd-util.h" + +const char *address_family_boolean_to_string(AddressFamilyBoolean b) { + if (b == ADDRESS_FAMILY_YES || + b == ADDRESS_FAMILY_NO) + return yes_no(b == ADDRESS_FAMILY_YES); + + if (b == ADDRESS_FAMILY_IPV4) + return "ipv4"; + if (b == ADDRESS_FAMILY_IPV6) + return "ipv6"; + + return NULL; +} + +AddressFamilyBoolean address_family_boolean_from_string(const char *s) { + int r; + + /* Make this a true superset of a boolean */ + + r = parse_boolean(s); + if (r > 0) + return ADDRESS_FAMILY_YES; + if (r == 0) + return ADDRESS_FAMILY_NO; + + if (streq(s, "ipv4")) + return ADDRESS_FAMILY_IPV4; + if (streq(s, "ipv6")) + return ADDRESS_FAMILY_IPV6; + + return _ADDRESS_FAMILY_BOOLEAN_INVALID; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option"); + +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) { + + AddressFamilyBoolean *fwd = data, s; + + assert(filename); + assert(lvalue); + 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_NO; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue); + return 0; + } + } + + *fwd = s; + + return 0; +} diff --git a/src/grp-network/libnetworkd-core/networkd-util.h b/src/grp-network/libnetworkd-core/networkd-util.h new file mode 100644 index 0000000000..d023782285 --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd-util.h @@ -0,0 +1,38 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/macro.h" + +typedef enum AddressFamilyBoolean { + /* This is a bitmask, though it usually doesn't feel that way! */ + ADDRESS_FAMILY_NO = 0, + ADDRESS_FAMILY_IPV4 = 1, + ADDRESS_FAMILY_IPV6 = 2, + ADDRESS_FAMILY_YES = 3, + _ADDRESS_FAMILY_BOOLEAN_MAX, + _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, +} AddressFamilyBoolean; + +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 *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; +AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; diff --git a/src/grp-network/libnetworkd-core/networkd.h b/src/grp-network/libnetworkd-core/networkd.h new file mode 100644 index 0000000000..dbc846a07e --- /dev/null +++ b/src/grp-network/libnetworkd-core/networkd.h @@ -0,0 +1,113 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "sd-netlink/sd-netlink.h" +#include "systemd-network/dhcp-identifier.h" +#include "udev.h" + +#include "networkd-address-pool.h" +#include "networkd-link.h" +#include "networkd-netdev-bond.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev-dummy.h" +#include "networkd-netdev-ipvlan.h" +#include "networkd-netdev-macvlan.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vrf.h" +#include "networkd-netdev-vxlan.h" +#include "networkd-network.h" +#include "networkd-util.h" + +extern const char* const network_dirs[]; + +struct Manager { + sd_netlink *rtnl; + sd_event *event; + sd_event_source *bus_retry_event_source; + sd_bus *bus; + sd_bus_slot *prepare_for_sleep_slot; + struct udev *udev; + struct udev_monitor *udev_monitor; + sd_event_source *udev_event_source; + + bool enumerating:1; + bool dirty:1; + + Set *dirty_links; + + char *state_file; + LinkOperationalState operational_state; + + Hashmap *links; + Hashmap *netdevs; + Hashmap *networks_by_name; + LIST_HEAD(Network, networks); + LIST_HEAD(AddressPool, address_pools); + + usec_t network_dirs_ts_usec; + + DUID duid; +}; + +static inline const DUID* link_duid(const Link *link) { + if (link->network->duid.type != _DUID_TYPE_INVALID) + return &link->network->duid; + else + return &link->manager->duid; +} + +extern const sd_bus_vtable manager_vtable[]; + +int manager_new(Manager **ret); +void manager_free(Manager *m); + +int manager_connect_bus(Manager *m); +int manager_run(Manager *m); + +int manager_load_config(Manager *m); +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_; +void manager_dirty(Manager *m); + +int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); + +Link* manager_find_uplink(Manager *m, Link *exclude); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); +#define _cleanup_manager_free_ _cleanup_(manager_freep) diff --git a/src/grp-network/network/80-container-host0.network b/src/grp-network/network/80-container-host0.network new file mode 100644 index 0000000000..b012cf98cb --- /dev/null +++ b/src/grp-network/network/80-container-host0.network @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This network file matches the container-side of the virtual Ethernet link +# created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for +# details. + +[Match] +Virtualization=container +Name=host0 + +[Network] +DHCP=yes +LinkLocalAddressing=yes +LLDP=yes +EmitLLDP=customer-bridge + +[DHCP] +UseTimezone=yes diff --git a/src/grp-network/network/80-container-ve.network b/src/grp-network/network/80-container-ve.network new file mode 100644 index 0000000000..ac796bfb07 --- /dev/null +++ b/src/grp-network/network/80-container-ve.network @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This network file matches the host-side of the virtual Ethernet link +# created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for +# details. + +[Match] +Name=ve-* +Driver=veth + +[Network] +# Default to using a /28 prefix, giving up to 13 addresses per container. +Address=0.0.0.0/28 +LinkLocalAddressing=yes +DHCPServer=yes +IPMasquerade=yes +LLDP=yes +EmitLLDP=customer-bridge diff --git a/src/grp-network/network/80-container-vz.network b/src/grp-network/network/80-container-vz.network new file mode 100644 index 0000000000..3d532d6f60 --- /dev/null +++ b/src/grp-network/network/80-container-vz.network @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# This network file matches the bridge interface created by systemd-nspawn's +# --network-zone= switch. See systemd-nspawn(1) for details. + +[Match] +Name=vz-* +Driver=bridge + +[Network] +# Default to using a /24 prefix, giving up to 253 addresses per virtual network. +Address=0.0.0.0/24 +LinkLocalAddressing=yes +DHCPServer=yes +IPMasquerade=yes +LLDP=yes +EmitLLDP=customer-bridge diff --git a/src/grp-network/network/99-default.link b/src/grp-network/network/99-default.link new file mode 100644 index 0000000000..79538f9b29 --- /dev/null +++ b/src/grp-network/network/99-default.link @@ -0,0 +1,3 @@ +[Link] +NamePolicy=kernel database onboard slot path +MACAddressPolicy=persistent diff --git a/src/grp-network/networkctl/Makefile b/src/grp-network/networkctl/Makefile new file mode 100644 index 0000000000..7b651c7f74 --- /dev/null +++ b/src/grp-network/networkctl/Makefile @@ -0,0 +1,39 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += \ + networkctl + +networkctl_SOURCES = \ + src/network/networkctl.c + +networkctl_LDADD = \ + libsystemd-shared.la \ + libsystemd-network.la + +dist_bashcompletion_data += \ + shell-completion/bash/networkctl + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/networkctl/networkctl.c b/src/grp-network/networkctl/networkctl.c new file mode 100644 index 0000000000..bedcca9a0e --- /dev/null +++ b/src/grp-network/networkctl/networkctl.c @@ -0,0 +1,1141 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/arphrd-list.h" +#include "basic/ether-addr-util.h" +#include "basic/fd-util.h" +#include "basic/locale-util.h" +#include "basic/parse-util.h" +#include "basic/socket-util.h" +#include "basic/sparse-endian.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/strxcpyx.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "basic/verbs.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" +#include "sd-hwdb/hwdb-util.h" +#include "sd-hwdb/sd-hwdb.h" +#include "sd-netlink/local-addresses.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "sd-network/sd-network.h" +#include "shared/pager.h" +#include "systemd-network/sd-lldp.h" + +static bool arg_no_pager = false; +static bool arg_legend = true; +static bool arg_all = false; + +static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) { + const char *t; + char *p; + + assert(ret); + + if (iftype == ARPHRD_ETHER && d) { + const char *devtype = NULL, *id = NULL; + /* WLANs have iftype ARPHRD_ETHER, but we want + * to show a more useful type string for + * them */ + + (void) sd_device_get_devtype(d, &devtype); + + if (streq_ptr(devtype, "wlan")) + id = "wlan"; + else if (streq_ptr(devtype, "wwan")) + id = "wwan"; + + if (id) { + p = strdup(id); + if (!p) + return -ENOMEM; + + *ret = p; + return 1; + } + } + + t = arphrd_to_name(iftype); + if (!t) { + *ret = NULL; + return 0; + } + + p = strdup(t); + if (!p) + return -ENOMEM; + + ascii_strlower(p); + *ret = p; + + return 0; +} + +static void operational_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "routable")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "degraded")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + +static void setup_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "configured")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "configuring")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { + *on = ansi_highlight_red(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + +typedef struct LinkInfo { + char name[IFNAMSIZ+1]; + int ifindex; + unsigned short iftype; + struct ether_addr mac_address; + uint32_t mtu; + + bool has_mac_address:1; + bool has_mtu:1; +} LinkInfo; + +static int link_info_compare(const void *a, const void *b) { + const LinkInfo *x = a, *y = b; + + return x->ifindex - y->ifindex; +} + +static int decode_link(sd_netlink_message *m, LinkInfo *info) { + const char *name; + uint16_t type; + int r; + + assert(m); + assert(info); + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + + if (type != RTM_NEWLINK) + return 0; + + r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name); + if (r < 0) + return r; + + r = sd_rtnl_message_link_get_type(m, &info->iftype); + if (r < 0) + return r; + + strscpy(info->name, sizeof info->name, name); + + info->has_mac_address = + sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && + memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; + + info->has_mtu = + sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) && + info->mtu > 0; + + return 1; +} + +static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) { + _cleanup_free_ LinkInfo *links = NULL; + char **i; + size_t c = 0; + int r; + + assert(rtnl); + assert(ret); + + links = new(LinkInfo, strv_length(l)); + if (!links) + return log_oom(); + + STRV_FOREACH(i, l) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + int ifindex; + + if (parse_ifindex(*i, &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); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i); + } + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return log_error_errno(r, "Failed to request link: %m"); + + r = decode_link(reply, links + c); + if (r < 0) + return r; + if (r > 0) + c++; + } + + qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); + + *ret = links; + links = NULL; + + return (int) c; +} + +static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + _cleanup_free_ LinkInfo *links = NULL; + size_t allocated = 0, c = 0; + sd_netlink_message *i; + int r; + + assert(rtnl); + assert(ret); + + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return log_error_errno(r, "Failed to enumerate links: %m"); + + for (i = reply; i; i = sd_netlink_message_next(i)) { + if (!GREEDY_REALLOC(links, allocated, c+1)) + return -ENOMEM; + + r = decode_link(i, links + c); + if (r < 0) + return r; + if (r > 0) + c++; + } + + qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); + + *ret = links; + links = NULL; + + return (int) c; +} + +static int list_links(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int c, i, r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); + if (c < 0) + return c; + + pager_open(arg_no_pager, false); + + if (arg_legend) + printf("%3s %-16s %-18s %-11s %-10s\n", + "IDX", + "LINK", + "TYPE", + "OPERATIONAL", + "SETUP"); + + for (i = 0; i < c; i++) { + _cleanup_free_ char *setup_state = NULL, *operational_state = 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)]; + _cleanup_free_ char *t = NULL; + + (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state); + operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); + + r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); + setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); + + xsprintf(devid, "n%i", links[i].ifindex); + (void) sd_device_new_from_device_id(&d, devid); + + (void) link_get_type_string(links[i].iftype, d, &t); + + printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n", + links[i].ifindex, links[i].name, strna(t), + on_color_operational, strna(operational_state), off_color_operational, + on_color_setup, strna(setup_state), off_color_setup); + } + + if (arg_legend) + printf("\n%i links listed.\n", c); + + return 0; +} + +/* IEEE Organizationally Unique Identifier vendor string */ +static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) { + const char *description; + char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc; + int r; + + assert(ret); + + if (!hwdb) + return -EINVAL; + + if (!mac) + return -EINVAL; + + /* skip commonly misused 00:00:00 (Xerox) prefix */ + if (memcmp(mac, "\0\0\0", 3) == 0) + return -EINVAL; + + 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) + return r; + + desc = strdup(description); + if (!desc) + return -ENOMEM; + + *ret = desc; + + return 0; +} + +static int get_gateway_description( + sd_netlink *rtnl, + sd_hwdb *hwdb, + int ifindex, + int family, + union in_addr_union *gateway, + char **gateway_description) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *m; + int r; + + assert(rtnl); + assert(ifindex >= 0); + assert(family == AF_INET || family == AF_INET6); + assert(gateway); + assert(gateway_description); + + r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_netlink_message_next(m)) { + union in_addr_union gw = {}; + struct ether_addr mac = {}; + uint16_t type; + int ifi, fam; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_error_errno(r, "got error: %m"); + continue; + } + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) { + log_error_errno(r, "could not get type: %m"); + continue; + } + + if (type != RTM_NEWNEIGH) { + log_error("type is not RTM_NEWNEIGH"); + continue; + } + + r = sd_rtnl_message_neigh_get_family(m, &fam); + if (r < 0) { + log_error_errno(r, "could not get family: %m"); + continue; + } + + if (fam != family) { + log_error("family is not correct"); + continue; + } + + r = sd_rtnl_message_neigh_get_ifindex(m, &ifi); + if (r < 0) { + log_error_errno(r, "could not get ifindex: %m"); + continue; + } + + if (ifindex > 0 && ifi != ifindex) + continue; + + switch (fam) { + case AF_INET: + r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in); + if (r < 0) + continue; + + break; + case AF_INET6: + r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6); + if (r < 0) + continue; + + break; + default: + continue; + } + + if (!in_addr_equal(fam, &gw, gateway)) + continue; + + r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac); + if (r < 0) + continue; + + r = ieee_oui(hwdb, &mac, gateway_description); + if (r < 0) + continue; + + return 0; + } + + return -ENODATA; +} + +static int dump_gateways( + sd_netlink *rtnl, + sd_hwdb *hwdb, + const char *prefix, + int ifindex) { + _cleanup_free_ struct local_address *local = NULL; + int r, n, i; + + assert(rtnl); + assert(prefix); + + n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); + if (n < 0) + return n; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *gateway = NULL, *description = NULL; + + r = in_addr_to_string(local[i].family, &local[i].address, &gateway); + if (r < 0) + return r; + + r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description); + if (r < 0) + log_debug_errno(r, "Could not get description of gateway: %m"); + + printf("%*s%s", + (int) strlen(prefix), + i == 0 ? prefix : "", + gateway); + + if (description) + printf(" (%s)", description); + + /* Show interface name for the entry if we show + * entries for all interfaces */ + if (ifindex <= 0) { + char name[IF_NAMESIZE+1]; + + if (if_indextoname(local[i].ifindex, name)) { + fputs(" on ", stdout); + fputs(name, stdout); + } else + printf(" on %%%i", local[i].ifindex); + } + + fputc('\n', stdout); + } + + return 0; +} + +static int dump_addresses( + sd_netlink *rtnl, + const char *prefix, + int ifindex) { + + _cleanup_free_ struct local_address *local = NULL; + int r, n, i; + + assert(rtnl); + assert(prefix); + + n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); + if (n < 0) + return n; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *pretty = NULL; + + r = in_addr_to_string(local[i].family, &local[i].address, &pretty); + if (r < 0) + return r; + + printf("%*s%s", + (int) strlen(prefix), + i == 0 ? prefix : "", + pretty); + + if (ifindex <= 0) { + char name[IF_NAMESIZE+1]; + + if (if_indextoname(local[i].ifindex, name)) { + fputs(" on ", stdout); + fputs(name, stdout); + } else + printf(" on %%%i", local[i].ifindex); + } + + fputc('\n', stdout); + } + + return 0; +} + +static int open_lldp_neighbors(int ifindex, FILE **ret) { + _cleanup_free_ char *p = NULL; + FILE *f; + + if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0) + return -ENOMEM; + + f = fopen(p, "re"); + if (!f) + return -errno; + + *ret = f; + return 0; +} + +static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) { + _cleanup_free_ void *raw = NULL; + size_t l; + le64_t u; + int r; + + assert(f); + assert(ret); + + l = fread(&u, 1, sizeof(u), f); + if (l == 0 && feof(f)) + return 0; + if (l != sizeof(u)) + return -EBADMSG; + + raw = new(uint8_t, le64toh(u)); + if (!raw) + return -ENOMEM; + + if (fread(raw, 1, le64toh(u), f) != le64toh(u)) + return -EBADMSG; + + r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u)); + if (r < 0) + return r; + + return 1; +} + +static int dump_lldp_neighbors(const char *prefix, int ifindex) { + _cleanup_fclose_ FILE *f = NULL; + int r, c = 0; + + assert(prefix); + assert(ifindex > 0); + + r = open_lldp_neighbors(ifindex, &f); + if (r < 0) + return r; + + for (;;) { + const char *system_name = NULL, *port_id = NULL, *port_description = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + + r = next_lldp_neighbor(f, &n); + if (r < 0) + return r; + if (r == 0) + break; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); + + printf("%s on port %s", strna(system_name), strna(port_id)); + + if (!isempty(port_description)) + printf(" (%s)", port_description); + + putchar('\n'); + + c++; + } + + return c; +} + +static void dump_ifindexes(const char *prefix, const int *ifindexes) { + unsigned c; + + assert(prefix); + + if (!ifindexes || ifindexes[0] <= 0) + return; + + for (c = 0; ifindexes[c] > 0; c++) { + char name[IF_NAMESIZE+1]; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + if (if_indextoname(ifindexes[c], name)) + fputs(name, stdout); + else + printf("%i", ifindexes[c]); + + fputc('\n', stdout); + } +} + +static void dump_list(const char *prefix, char **l) { + char **i; + + if (strv_isempty(l)) + return; + + STRV_FOREACH(i, l) { + printf("%*s%s\n", + (int) strlen(prefix), + i == l ? prefix : "", + *i); + } +} + +static int link_status_one( + sd_netlink *rtnl, + sd_hwdb *hwdb, + const LinkInfo *info) { + + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; + _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = 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; + const char *on_color_operational, *off_color_operational, + *on_color_setup, *off_color_setup; + _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; + int r; + + assert(rtnl); + assert(info); + + (void) sd_network_link_get_operational_state(info->ifindex, &operational_state); + operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); + + r = sd_network_link_get_setup_state(info->ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); + setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); + + (void) sd_network_link_get_dns(info->ifindex, &dns); + (void) sd_network_link_get_search_domains(info->ifindex, &search_domains); + (void) sd_network_link_get_route_domains(info->ifindex, &route_domains); + (void) sd_network_link_get_ntp(info->ifindex, &ntp); + + xsprintf(devid, "n%i", info->ifindex); + + (void) sd_device_new_from_device_id(&d, devid); + + if (d) { + (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link); + (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver); + (void) sd_device_get_property_value(d, "ID_PATH", &path); + + r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor); + if (r < 0) + (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor); + + r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model); + if (r < 0) + (void) sd_device_get_property_value(d, "ID_MODEL", &model); + } + + (void) link_get_type_string(info->iftype, d, &t); + + (void) sd_network_link_get_network_file(info->ifindex, &network); + + (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); + (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); + + printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); + + printf(" Link File: %s\n" + " Network File: %s\n" + " Type: %s\n" + " State: %s%s%s (%s%s%s)\n", + strna(link), + strna(network), + strna(t), + on_color_operational, strna(operational_state), off_color_operational, + on_color_setup, strna(setup_state), off_color_setup); + + if (path) + printf(" Path: %s\n", path); + if (driver) + printf(" Driver: %s\n", driver); + if (vendor) + printf(" Vendor: %s\n", vendor); + if (model) + printf(" Model: %s\n", model); + + if (info->has_mac_address) { + _cleanup_free_ char *description = NULL; + char ea[ETHER_ADDR_TO_STRING_MAX]; + + (void) ieee_oui(hwdb, &info->mac_address, &description); + + if (description) + printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description); + else + printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea)); + } + + if (info->has_mtu) + printf(" MTU: %u\n", info->mtu); + + (void) dump_addresses(rtnl, " Address: ", info->ifindex); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex); + + dump_list(" DNS: ", dns); + dump_list(" Search Domains: ", search_domains); + dump_list(" Route Domains: ", route_domains); + + dump_list(" NTP: ", ntp); + + dump_ifindexes("Carrier Bound To: ", carrier_bound_to); + dump_ifindexes("Carrier Bound By: ", carrier_bound_by); + + (void) sd_network_link_get_timezone(info->ifindex, &tz); + if (tz) + printf(" Time Zone: %s\n", tz); + + (void) dump_lldp_neighbors(" Connected To: ", info->ifindex); + + return 0; +} + +static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { + _cleanup_free_ char *operational_state = NULL; + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; + const char *on_color_operational, *off_color_operational; + + assert(rtnl); + + (void) sd_network_get_operational_state(&operational_state); + operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); + + printf("%s%s%s State: %s%s%s\n", + on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, + on_color_operational, strna(operational_state), off_color_operational); + + (void) dump_addresses(rtnl, " Address: ", 0); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0); + + (void) sd_network_get_dns(&dns); + dump_list(" DNS: ", dns); + + (void) sd_network_get_search_domains(&search_domains); + dump_list("Search Domains: ", search_domains); + + (void) sd_network_get_route_domains(&route_domains); + dump_list(" Route Domains: ", route_domains); + + (void) sd_network_get_ntp(&ntp); + dump_list(" NTP: ", ntp); + + return 0; +} + +static int link_status(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int r, c, i; + + pager_open(arg_no_pager, false); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = sd_hwdb_new(&hwdb); + if (r < 0) + log_debug_errno(r, "Failed to open hardware database: %m"); + + if (arg_all) + c = acquire_link_info_all(rtnl, &links); + else if (argc <= 1) + return system_status(rtnl, hwdb); + else + c = acquire_link_info_strv(rtnl, argv + 1, &links); + if (c < 0) + return c; + + for (i = 0; i < c; i++) { + if (i > 0) + fputc('\n', stdout); + + link_status_one(rtnl, hwdb, links + i); + } + + return 0; +} + +static char *lldp_capabilities_to_string(uint16_t x) { + static const char characters[] = { + 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm', + }; + char *ret; + unsigned i; + + ret = new(char, ELEMENTSOF(characters) + 1); + if (!ret) + return NULL; + + for (i = 0; i < ELEMENTSOF(characters); i++) + ret[i] = (x & (1U << i)) ? characters[i] : '.'; + + ret[i] = 0; + return ret; +} + +static void lldp_capabilities_legend(uint16_t x) { + unsigned w, i, cols = columns(); + static const char* const table[] = { + "o - Other", + "p - Repeater", + "b - Bridge", + "w - WLAN Access Point", + "r - Router", + "t - Telephone", + "d - DOCSIS cable device", + "a - Station", + "c - Customer VLAN", + "s - Service VLAN", + "m - Two-port MAC Relay (TPMR)", + }; + + if (x == 0) + return; + + printf("\nCapability Flags:\n"); + for (w = 0, i = 0; i < ELEMENTSOF(table); i++) + if (x & (1U << i) || arg_all) { + bool newline; + + newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols; + if (newline) + w = 0; + w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]); + } + puts(""); +} + +static int link_lldp_status(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int i, r, c, m = 0; + uint16_t all = 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); + if (c < 0) + return c; + + pager_open(arg_no_pager, false); + + if (arg_legend) + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + "LINK", + "CHASSIS ID", + "SYSTEM NAME", + "CAPS", + "PORT ID", + "PORT DESCRIPTION"); + + for (i = 0; i < c; i++) { + _cleanup_fclose_ FILE *f = NULL; + + r = open_lldp_neighbors(links[i].ifindex, &f); + if (r == -ENOENT) + continue; + if (r < 0) { + log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex); + continue; + } + + for (;;) { + _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL; + const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + uint16_t cc; + + r = next_lldp_neighbor(f, &n); + if (r < 0) { + log_warning_errno(r, "Failed to read neighbor data: %m"); + break; + } + if (r == 0) + break; + + (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); + + if (chassis_id) { + cid = ellipsize(chassis_id, 17, 100); + if (cid) + chassis_id = cid; + } + + if (port_id) { + pid = ellipsize(port_id, 17, 100); + if (pid) + port_id = pid; + } + + if (system_name) { + sname = ellipsize(system_name, 16, 100); + if (sname) + system_name = sname; + } + + if (port_description) { + pdesc = ellipsize(port_description, 16, 100); + if (pdesc) + port_description = pdesc; + } + + if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) { + capabilities = lldp_capabilities_to_string(cc); + all |= cc; + } + + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + links[i].name, + strna(chassis_id), + strna(system_name), + strna(capabilities), + strna(port_id), + strna(port_description)); + + m++; + } + } + + if (arg_legend) { + lldp_capabilities_legend(all); + printf("\n%i neighbors listed.\n", m); + } + + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Query and control the networking subsystem.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " -a --all Show status for all links\n\n" + "Commands:\n" + " list [LINK...] List links\n" + " status [LINK...] Show link status\n" + " lldp [LINK...] Show LLDP neighbors\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "all", no_argument, NULL, 'a' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + + case 'a': + arg_all = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + } + + return 1; +} + +static int networkctl_main(int argc, char *argv[]) { + const Verb verbs[] = { + { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links }, + { "status", VERB_ANY, VERB_ANY, 0, link_status }, + { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +static void warn_networkd_missing(void) { + + if (access("/run/systemd/netif/state", F_OK) >= 0) + return; + + fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n"); +} + +int main(int argc, char* argv[]) { + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + warn_networkd_missing(); + + r = networkctl_main(argc, argv); + +finish: + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-network/networkctl/networkctl.completion.bash b/src/grp-network/networkctl/networkctl.completion.bash new file mode 100644 index 0000000000..942c7e1c00 --- /dev/null +++ b/src/grp-network/networkctl/networkctl.completion.bash @@ -0,0 +1,70 @@ +# networkctl(1) completion -*- shell-script -*- +# +# 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 +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__get_links() { + networkctl list --no-legend --no-pager --all | { while read -r a b c; do echo " $b"; done; }; +} + +_networkctl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-a --all -h --help --version --no-pager --no-legend' + [ARG]='' + ) + + local -A VERBS=( + [STANDALONE]='list lldp' + [LINKS]='status' + ) + + _init_completion || return + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + elif __contains_word "$verb" ${VERBS[LINKS]}; then + comps=$( __get_links ) + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _networkctl networkctl diff --git a/src/grp-network/networkctl/networkctl.completion.zsh b/src/grp-network/networkctl/networkctl.completion.zsh new file mode 100644 index 0000000000..61f173b78e --- /dev/null +++ b/src/grp-network/networkctl/networkctl.completion.zsh @@ -0,0 +1,35 @@ +#compdef networkctl + +_networkctl_command(){ + local -a _networkctl_cmds + _networkctl_cmds=( + 'list:List existing links' + 'status:Show information about the specified links' + 'lldp:Show Link Layer Discovery Protocol status' + ) + if (( CURRENT == 1 )); then + _describe -t commands 'networkctl command' _networkctl_cmds + else + local curcontext="$curcontext" + local -a _links + cmd="${${_networkctl_cmds[(r)$words[1]:*]%%:*}}" + if [ $cmd = "status" ]; then + _links=( "${(foa)$(networkctl list --no-legend | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $2,$0}' 2>/dev/null)}" ) + if [[ -n "$_links" ]]; then + _describe -t links 'links' _links + else + _message "no links" + fi + else + _message "no more options" + fi + fi +} + +_arguments \ + {-a,--all}'[Show all links with status]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-legend[Do not print the column headers]' \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '*::networkctl commands:_networkctl_command' diff --git a/src/grp-network/networkctl/networkctl.xml b/src/grp-network/networkctl/networkctl.xml new file mode 100644 index 0000000000..24e1de6986 --- /dev/null +++ b/src/grp-network/networkctl/networkctl.xml @@ -0,0 +1,193 @@ + + + + + + + + + networkctl + systemd + + + + Documentation + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + networkctl + 1 + + + + networkctl + Query the status of network links + + + + + networkctl + OPTIONS + COMMAND + LINK + + + + + Description + + networkctl may be used to introspect the + state of the network links as seen by + systemd-networkd. Please refer to + systemd-networkd.service8 + for an introduction to the basic concepts, functionality, and + configuration syntax. + + + + Options + + The following options are understood: + + + + + + + + + + Show all links with status. + + + + + + + + + + + + + Commands + + The following commands are understood: + + + + + list + LINK... + + + + Show a list of existing links and their status. If no further arguments are specified shows all links, + otherwise just the specified links. Produces output similar to: + + IDX LINK TYPE OPERATIONAL SETUP + 1 lo loopback carrier unmanaged + 2 eth0 ether routable configured + 3 virbr0 ether no-carrier unmanaged + 4 virbr0-nic ether off unmanaged + +4 links listed. + + + + + + status + LINK... + + + + Show information about the specified links: type, + state, kernel module driver, hardware and IP address, + configured DNS servers, etc. + + When no links are specified, an overall network status is shown. Also see the option + . + + Produces output similar to: + +● State: routable + Address: 10.193.76.5 on eth0 + 192.168.122.1 on virbr0 + 169.254.190.105 on eth0 + fe80::5054:aa:bbbb:cccc on eth0 + Gateway: 10.193.11.1 (CISCO SYSTEMS, INC.) on eth0 + DNS: 8.8.8.8 + 8.8.4.4 + + + + + + + lldp + LINK... + + + + Show discovered LLDP (Link Layer Discovery Protocol) neighbors. If one or more link names are specified + only neighbors on those interfaces are shown. Otherwise shows discovered neighbors on all interfaces. Note + that for this feature to work, LLDP= must be turned on on the specific interface, see + systemd.network5 for + details. + + Produces output similar to: + LINK CHASSIS ID SYSTEM NAME CAPS PORT ID PORT DESCRIPTION +enp0s25 00:e0:4c:00:00:00 GS1900 ..b........ 2 Port #2 + +Capability Flags: +o - Other; p - Repeater; b - Bridge; w - WLAN Access Point; r - Router; +t - Telephone; d - DOCSIS cable device; a - Station; c - Customer VLAN; +s - Service VLAN, m - Two-port MAC Relay (TPMR) + +1 neighbors listed. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + systemd-networkd.service8, + systemd.network5, + systemd.netdev5 + + + diff --git a/src/grp-network/systemd-networkd-wait-online/Makefile b/src/grp-network/systemd-networkd-wait-online/Makefile new file mode 100644 index 0000000000..421bb9a673 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/Makefile @@ -0,0 +1,43 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += \ + systemd-networkd-wait-online + +systemd_networkd_wait_online_CFLAGS = \ + +systemd_networkd_wait_online_SOURCES = \ + src/libsystemd-network/network-internal.h \ + src/network/networkd-wait-online.h \ + src/network/networkd-wait-online-link.h \ + src/network/networkd-wait-online.c \ + src/network/networkd-wait-online-manager.c \ + src/network/networkd-wait-online-link.c + +systemd_networkd_wait_online_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.c b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.c new file mode 100644 index 0000000000..29384baca3 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.c @@ -0,0 +1,131 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "sd-network/sd-network.h" + +#include "networkd-wait-online-link.h" + +int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) { + _cleanup_(link_freep) Link *l = NULL; + int r; + + assert(m); + assert(ifindex > 0); + + r = hashmap_ensure_allocated(&m->links, NULL); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&m->links_by_name, &string_hash_ops); + if (r < 0) + return r; + + l = new0(Link, 1); + if (!l) + return -ENOMEM; + + l->manager = m; + + l->ifname = strdup(ifname); + if (!l->ifname) + return -ENOMEM; + + r = hashmap_put(m->links_by_name, l->ifname, l); + if (r < 0) + return r; + + l->ifindex = ifindex; + + r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); + if (r < 0) + return r; + + if (ret) + *ret = l; + l = NULL; + + return 0; +} + +Link *link_free(Link *l) { + + if (!l) + return NULL; + + if (l->manager) { + hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); + hashmap_remove(l->manager->links_by_name, l->ifname); + } + + free(l->ifname); + free(l); + return NULL; + } + +int link_update_rtnl(Link *l, sd_netlink_message *m) { + const char *ifname; + int r; + + assert(l); + assert(l->manager); + assert(m); + + r = sd_rtnl_message_link_get_flags(m, &l->flags); + if (r < 0) + return r; + + r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname); + if (r < 0) + return r; + + if (!streq(l->ifname, ifname)) { + char *new_ifname; + + new_ifname = strdup(ifname); + if (!new_ifname) + return -ENOMEM; + + hashmap_remove(l->manager->links_by_name, l->ifname); + free(l->ifname); + l->ifname = new_ifname; + + r = hashmap_put(l->manager->links_by_name, l->ifname, l); + if (r < 0) + return r; + } + + return 0; +} + +int link_update_monitor(Link *l) { + assert(l); + + l->operational_state = mfree(l->operational_state); + + sd_network_link_get_operational_state(l->ifindex, &l->operational_state); + + l->state = mfree(l->state); + + sd_network_link_get_setup_state(l->ifindex, &l->state); + + return 0; +} diff --git a/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.h b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.h new file mode 100644 index 0000000000..dc35085c55 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-link.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + 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 . +***/ + +typedef struct Link Link; + +#include "networkd-wait-online.h" + +struct Link { + Manager *manager; + + int ifindex; + char *ifname; + unsigned flags; + + char *operational_state; + char *state; +}; + +int link_new(Manager *m, Link **ret, int ifindex, const char *ifname); +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); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); diff --git a/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-manager.c b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-manager.c new file mode 100644 index 0000000000..e4f6d68f16 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online-manager.c @@ -0,0 +1,331 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/time-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "systemd-network/network-internal.h" + +#include "networkd-wait-online-link.h" +#include "networkd-wait-online.h" + +bool manager_ignore_link(Manager *m, Link *link) { + assert(m); + assert(link); + + /* always ignore the loopback interface */ + if (link->flags & IFF_LOOPBACK) + return true; + + /* if interfaces are given on the command line, ignore all others */ + if (m->interfaces && !strv_contains(m->interfaces, link->ifname)) + return true; + + /* ignore interfaces we explicitly are asked to ignore */ + return strv_fnmatch(m->ignore, link->ifname, 0); +} + +bool manager_all_configured(Manager *m) { + Iterator i; + Link *l; + char **ifname; + bool one_ready = false; + + /* wait for all the links given on the command line to appear */ + STRV_FOREACH(ifname, m->interfaces) { + l = hashmap_get(m->links_by_name, *ifname); + if (!l) { + log_debug("still waiting for %s", *ifname); + return false; + } + } + + /* wait for all links networkd manages to be in admin state 'configured' + and at least one link to gain a carrier */ + HASHMAP_FOREACH(l, m->links, i) { + if (manager_ignore_link(m, l)) { + log_info("ignoring: %s", l->ifname); + continue; + } + + if (!l->state) { + log_debug("link %s has not yet been processed by udev", + l->ifname); + return false; + } + + if (STR_IN_SET(l->state, "configuring", "pending")) { + log_debug("link %s is being processed by networkd", + l->ifname); + return false; + } + + if (l->operational_state && + STR_IN_SET(l->operational_state, "degraded", "routable")) + /* we wait for at least one link to be ready, + regardless of who manages it */ + one_ready = true; + } + + return one_ready; +} + +static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Manager *m = userdata; + uint16_t type; + Link *l; + const char *ifname; + int ifindex, r; + + assert(rtnl); + assert(m); + assert(mm); + + r = sd_netlink_message_get_type(mm, &type); + if (r < 0) + goto fail; + + r = sd_rtnl_message_link_get_ifindex(mm, &ifindex); + if (r < 0) + goto fail; + + r = sd_netlink_message_read_string(mm, IFLA_IFNAME, &ifname); + if (r < 0) + goto fail; + + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + + switch (type) { + + case RTM_NEWLINK: + if (!l) { + log_debug("Found link %i", ifindex); + + r = link_new(m, &l, ifindex, ifname); + if (r < 0) + goto fail; + + r = link_update_monitor(l); + if (r < 0) + goto fail; + } + + r = link_update_rtnl(l, mm); + if (r < 0) + goto fail; + + break; + + case RTM_DELLINK: + if (l) { + log_debug("Removing link %i", l->ifindex); + link_free(l); + } + + break; + } + + return 0; + +fail: + log_warning_errno(r, "Failed to process RTNL link message: %m"); + return 0; +} + +static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Manager *m = userdata; + int r; + + r = manager_process_link(rtnl, mm, m); + if (r < 0) + return r; + + if (manager_all_configured(m)) + sd_event_exit(m->event, 0); + + return 1; +} + +static int manager_rtnl_listen(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *i; + int r; + + assert(m); + + /* First, subscribe to interfaces coming and going */ + r = sd_netlink_open(&m->rtnl); + if (r < 0) + return r; + + r = sd_netlink_attach_event(m->rtnl, m->event, 0); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, on_rtnl_event, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, on_rtnl_event, m); + if (r < 0) + return r; + + /* Then, enumerate all links */ + r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (i = reply; i; i = sd_netlink_message_next(i)) { + r = manager_process_link(m->rtnl, i, m); + if (r < 0) + return r; + } + + return r; +} + +static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + Iterator i; + Link *l; + int r; + + assert(m); + + sd_network_monitor_flush(m->network_monitor); + + HASHMAP_FOREACH(l, m->links, i) { + r = link_update_monitor(l); + if (r < 0) + log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); + } + + if (manager_all_configured(m)) + sd_event_exit(m->event, 0); + + return 0; +} + +static int manager_network_monitor_listen(Manager *m) { + int r, fd, events; + + assert(m); + + r = sd_network_monitor_new(&m->network_monitor, NULL); + if (r < 0) + return r; + + fd = sd_network_monitor_get_fd(m->network_monitor); + if (fd < 0) + return fd; + + events = sd_network_monitor_get_events(m->network_monitor); + if (events < 0) + return events; + + r = sd_event_add_io(m->event, &m->network_monitor_event_source, + fd, events, &on_network_event, m); + if (r < 0) + return r; + + return 0; +} + +int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout) { + _cleanup_(manager_freep) Manager *m = NULL; + int r; + + assert(ret); + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + m->interfaces = interfaces; + m->ignore = ignore; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); + + if (timeout > 0) { + usec_t usec; + + usec = now(clock_boottime_or_monotonic()) + timeout; + + r = sd_event_add_time(m->event, NULL, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); + if (r < 0) + return r; + } + + sd_event_set_watchdog(m->event, true); + + r = manager_network_monitor_listen(m); + if (r < 0) + return r; + + r = manager_rtnl_listen(m); + if (r < 0) + return r; + + *ret = m; + m = NULL; + + return 0; +} + +void manager_free(Manager *m) { + Link *l; + + if (!m) + return; + + while ((l = hashmap_first(m->links))) + link_free(l); + hashmap_free(m->links); + hashmap_free(m->links_by_name); + + sd_event_source_unref(m->network_monitor_event_source); + sd_network_monitor_unref(m->network_monitor); + + sd_event_source_unref(m->rtnl_event_source); + sd_netlink_unref(m->rtnl); + + sd_event_unref(m->event); + free(m); + + return; +} diff --git a/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.c b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.c new file mode 100644 index 0000000000..feea7c9f34 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.c @@ -0,0 +1,167 @@ + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include + +#include "basic/signal-util.h" +#include "basic/strv.h" + +#include "networkd-wait-online.h" + +static bool arg_quiet = false; +static usec_t arg_timeout = 120 * USEC_PER_SEC; +static char **arg_interfaces = NULL; +static char **arg_ignore = NULL; + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Block until network is configured.\n\n" + " -h --help Show this help\n" + " --version Print version string\n" + " -q --quiet Do not show status information\n" + " -i --interface=INTERFACE Block until at least these interfaces have appeared\n" + " --ignore=INTERFACE Don't take these interfaces into account\n" + " --timeout=SECS Maximum time to wait for network connectivity\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_IGNORE, + ARG_TIMEOUT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "quiet", no_argument, NULL, 'q' }, + { "interface", required_argument, NULL, 'i' }, + { "ignore", required_argument, NULL, ARG_IGNORE }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hi:q", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case 'q': + arg_quiet = true; + break; + + case ARG_VERSION: + return version(); + + case 'i': + if (strv_extend(&arg_interfaces, optarg) < 0) + return log_oom(); + + break; + + case ARG_IGNORE: + if (strv_extend(&arg_ignore, optarg) < 0) + return log_oom(); + + break; + + case ARG_TIMEOUT: + r = parse_sec(optarg, &arg_timeout); + if (r < 0) + return r; + + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + _cleanup_(manager_freep) Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + if (arg_quiet) + log_set_max_level(LOG_WARNING); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + + r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout); + if (r < 0) { + log_error_errno(r, "Could not create manager: %m"); + goto finish; + } + + if (manager_all_configured(m)) { + r = 0; + goto finish; + } + + sd_notify(false, + "READY=1\n" + "STATUS=Waiting for network connections..."); + + r = sd_event_loop(m->event); + if (r < 0) { + log_error_errno(r, "Event loop failed: %m"); + goto finish; + } + +finish: + strv_free(arg_interfaces); + strv_free(arg_ignore); + + if (r >= 0) { + sd_notify(false, "STATUS=All interfaces configured..."); + + return EXIT_SUCCESS; + } else { + sd_notify(false, "STATUS=Failed waiting for network connectivity..."); + + return EXIT_FAILURE; + } +} diff --git a/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.h b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.h new file mode 100644 index 0000000000..b0d70a18d0 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/networkd-wait-online.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "sd-netlink/sd-netlink.h" +#include "sd-network/sd-network.h" + +typedef struct Manager Manager; + +#include "networkd-wait-online-link.h" + +struct Manager { + Hashmap *links; + Hashmap *links_by_name; + + char **interfaces; + char **ignore; + + sd_netlink *rtnl; + sd_event_source *rtnl_event_source; + + sd_network_monitor *network_monitor; + sd_event_source *network_monitor_event_source; + + sd_event *event; +}; + +void manager_free(Manager *m); +int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); + +bool manager_all_configured(Manager *m); +bool manager_ignore_link(Manager *m, Link *link); diff --git a/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in new file mode 100644 index 0000000000..a9bad7aa8f --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.in @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Wait for Network to be Configured +Documentation=man:systemd-networkd-wait-online.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +Requisite=systemd-networkd.service +After=systemd-networkd.service +Before=network-online.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-networkd-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.xml b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.xml new file mode 100644 index 0000000000..e21c805342 --- /dev/null +++ b/src/grp-network/systemd-networkd-wait-online/systemd-networkd-wait-online.service.xml @@ -0,0 +1,110 @@ + + + + + + + + + systemd-networkd.service + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd-networkd-wait-online.service + 8 + + + + systemd-networkd-wait-online.service + systemd-networkd-wait-online + Wait for network to come online + + + + systemd-networkd-wait-online.service + /usr/lib/systemd/systemd-networkd-wait-online + + + + Description + + systemd-networkd-wait-online is a + one-shot system service that waits for the network to be + configured. By default, it will wait for all links it is aware of + and which are managed by + systemd-networkd.service8 + to be fully configured or failed, and for at least one link to + gain a carrier. + + + + Options + + The following options are understood: + + + + + + + Network interface to wait for before deciding + if the system is online. This is useful when a system has + several interfaces which will be configured, but a particular + one is necessary to access some network resources. This option + may be used more than once to wait for multiple network + interfaces. When used, all other interfaces are ignored. + + + + + Network interfaces to be ignored when deciding + if the system is online. By default, only the loopback + interface is ignored. This option may be used more than once + to ignore multiple network interfaces. + + + + Fail the service if the network is not online + by the time the timeout elapses. A timeout of 0 disables the + timeout. Defaults to 120 seconds. + + + + + + See Also + + systemd1, + systemd-networkd.service8 + + + + diff --git a/src/grp-network/systemd-networkd/Makefile b/src/grp-network/systemd-networkd/Makefile new file mode 100644 index 0000000000..5a4845ab73 --- /dev/null +++ b/src/grp-network/systemd-networkd/Makefile @@ -0,0 +1,67 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += \ + systemd-networkd + +systemd_networkd_SOURCES = \ + src/network/networkd.c + +systemd_networkd_LDADD = \ + libnetworkd-core.la + +ifneq ($(HAVE_LIBIPTC),) +systemd_networkd_LDADD += \ + libfirewall.la +endif # HAVE_LIBIPTC + +dist_systemunit_DATA += \ + units/systemd-networkd.socket + +nodist_systemunit_DATA += \ + units/systemd-networkd.service \ + units/systemd-networkd-wait-online.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.network1.busname + +dist_dbussystemservice_DATA += \ + src/network/org.freedesktop.network1.service + +dist_dbuspolicy_DATA += \ + src/network/org.freedesktop.network1.conf + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-networkd.socket $(pkgsysconfdir)/system/sockets.target.wants/systemd-networkd.socket \ + $(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service \ + $(systemunitdir)/systemd-networkd-wait-online.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service + +SYSTEM_UNIT_ALIASES += \ + systemd-networkd.service dbus-org.freedesktop.network1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.network1.busname + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-network/systemd-networkd/networkd.c b/src/grp-network/systemd-networkd/networkd.c new file mode 100644 index 0000000000..4f79dfd409 --- /dev/null +++ b/src/grp-network/systemd-networkd/networkd.c @@ -0,0 +1,139 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/capability-util.h" +#include "basic/signal-util.h" +#include "basic/user-util.h" +#include "networkd-conf.h" +#include "networkd.h" + +int main(int argc, char *argv[]) { + _cleanup_manager_free_ Manager *m = NULL; + const char *user = "systemd-network"; + uid_t uid; + gid_t gid; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto out; + } + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Cannot resolve user name %s: %m", user); + goto out; + } + + /* Always create the directories people can create inotify + * watches in. */ + r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory: %m"); + + r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'links': %m"); + + r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'leases': %m"); + + r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'lldp': %m"); + + r = drop_privileges(uid, gid, + (1ULL << CAP_NET_ADMIN) | + (1ULL << CAP_NET_BIND_SERVICE) | + (1ULL << CAP_NET_BROADCAST) | + (1ULL << CAP_NET_RAW)); + if (r < 0) + goto out; + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + + r = manager_new(&m); + if (r < 0) { + log_error_errno(r, "Could not create manager: %m"); + goto out; + } + + r = manager_connect_bus(m); + if (r < 0) { + log_error_errno(r, "Could not connect to bus: %m"); + goto out; + } + + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse configuration file: %m"); + + r = manager_load_config(m); + if (r < 0) { + log_error_errno(r, "Could not load configuration files: %m"); + goto out; + } + + r = manager_rtnl_enumerate_links(m); + if (r < 0) { + log_error_errno(r, "Could not enumerate links: %m"); + goto out; + } + + r = manager_rtnl_enumerate_addresses(m); + if (r < 0) { + log_error_errno(r, "Could not enumerate addresses: %m"); + 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, + "READY=1\n" + "STATUS=Processing requests..."); + + r = manager_run(m); + if (r < 0) { + log_error_errno(r, "Event loop failed: %m"); + goto out; + } + +out: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-network/systemd-networkd/networkd.conf.xml b/src/grp-network/systemd-networkd/networkd.conf.xml new file mode 100644 index 0000000000..4bfc4f773a --- /dev/null +++ b/src/grp-network/systemd-networkd/networkd.conf.xml @@ -0,0 +1,154 @@ + + + + + + + + networkd.conf + systemd + + + + Developer + Vinay + Kulkarni + kulkarniv@vmware.com + + + + + + networkd.conf + 5 + + + + networkd.conf + networkd.conf.d + Global Network configuration files + + + + /etc/systemd/networkd.conf + /etc/systemd/networkd.conf.d/*.conf + /usr/lib/systemd/networkd.conf.d/*.conf + + + + Description + + These configuration files control global network parameters. + Currently the DHCP Unique Identifier (DUID). + + + + + + + [DHCP] Section Options + + This section configures the DHCP Unique Identifier (DUID) value used by DHCP + protocol. DHCPv6 client protocol sends the DHCP Unique Identifier and the interface + Identity Association Identifier (IAID) to a DHCP server when acquiring a dynamic IPv6 + address. DHCPv4 client protocol sends IAID and DUID to the DHCP server when acquiring + a dynamic IPv4 address if . IAID and DUID allows + a DHCP server to uniquely identify the machine and the interface requesting a DHCP IP. + To configure IAID and ClientIdentifier, see + systemd.network5. + + + The following options are understood: + + + + DUIDType= + Specifies how the DUID should be generated. See + RFC 3315 + for a description of all the options. + + The following values are understood: + + + + If DUIDType=vendor, then the DUID value will be generated using + 43793 as the vendor identifier (systemd) and hashed contents of + machine-id5. + This is the default if DUIDType= is not specified. + + + + + + + + Those values are parsed and can be used to set the DUID type + field, but DUID contents must be provided using DUIDRawData=. + + + + + + In all cases, DUIDRawData= can be used to override the + actual DUID value that is used. + + + + DUIDRawData= + Specifies the DHCP DUID value as a single newline-terminated, hexadecimal string, with each + byte separated by :. The DUID that is sent is composed of the DUID type specified by + DUIDType= and the value configured here. + + The DUID value specified here overrides the DUID that systemd-networkd generates using the machine-id + from the /etc/machine-id file. To configure DUID per-network, see + systemd.network 5. + The configured DHCP DUID should conform to the specification in + RFC 3315, + RFC 6355. To configure IAID, see + systemd.network5 + . + + + A <option>DUIDType=vendor</option> with a custom value + + DUIDType=vendor +DUIDRawData=00:00:ab:11:f9:2a:c2:77:29:f9:5c:00 + + This specifies a 14 byte DUID, with the type DUID-EN (00:02), enterprise number + 43793 (00:00:ab:11), and identifier value f9:2a:c2:77:29:f9:5c:00. + + + + + + + + + See Also + + systemd1, + systemd.network5, + machine-id1 + + + + diff --git a/src/grp-network/systemd-networkd/org.freedesktop.network1.conf b/src/grp-network/systemd-networkd/org.freedesktop.network1.conf new file mode 100644 index 0000000000..52dad33668 --- /dev/null +++ b/src/grp-network/systemd-networkd/org.freedesktop.network1.conf @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-network/systemd-networkd/org.freedesktop.network1.service b/src/grp-network/systemd-networkd/org.freedesktop.network1.service new file mode 100644 index 0000000000..bea885fe53 --- /dev/null +++ b/src/grp-network/systemd-networkd/org.freedesktop.network1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.network1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.network1.service diff --git a/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in b/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in new file mode 100644 index 0000000000..38d967d2d1 --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.service.m4.in @@ -0,0 +1,39 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Service +Documentation=man:systemd-networkd.service(8) +ConditionCapability=CAP_NET_ADMIN +DefaultDependencies=no +# dbus.service can be dropped once on kdbus, and systemd-udevd.service can be +# dropped once tuntap is moved to netlink +After=systemd-udevd.service dbus.service network-pre.target systemd-sysusers.service systemd-sysctl.service +Before=network.target multi-user.target shutdown.target +Conflicts=shutdown.target +Wants=network.target + +# On kdbus systems we pull in the busname explicitly, because it +# carries policy that allows the daemon to acquire its name. +Wants=org.freedesktop.network1.busname +After=org.freedesktop.network1.busname + +[Service] +Type=notify +Restart=on-failure +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-networkd +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io + +[Install] +WantedBy=multi-user.target +Also=systemd-networkd.socket diff --git a/src/grp-network/systemd-networkd/systemd-networkd.service.xml b/src/grp-network/systemd-networkd/systemd-networkd.service.xml new file mode 100644 index 0000000000..0bfe5519bc --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.service.xml @@ -0,0 +1,103 @@ + + + + + + + + + systemd-networkd.service + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd-networkd.service + 8 + + + + systemd-networkd.service + systemd-networkd + Network manager + + + + systemd-networkd.service + /usr/lib/systemd/systemd-networkd + + + + Description + + systemd-networkd is a system service that + manages networks. It detects and configures network devices as + they appear, as well as creating virtual network devices. + + To configure low-level link settings independently of + networks, see + systemd.link5. + + Network configurations applied before networkd is started + are not removed, and static configuration applied by networkd is + not removed when networkd exits. Dynamic configuration applied by + networkd may also optionally be left in place on shutdown. This + ensures restarting networkd does not cut the network connection, + and, in particular, that it is safe to transition between the + initrd and the real root, and back. + + + Configuration Files + The configuration files are read from the files located in the + system network directory /usr/lib/systemd/network, + the volatile runtime network directory + /run/systemd/network and the local administration + network directory /etc/systemd/network. + + Networks are configured in .network + files, see + systemd.network5, + and virtual network devices are configured in + .netdev files, see + systemd.netdev5. + + + + + See Also + + systemd1, + systemd.link5, + systemd.network5, + systemd.netdev5, + systemd-networkd-wait-online.service8 + + + + diff --git a/src/grp-network/systemd-networkd/systemd-networkd.socket b/src/grp-network/systemd-networkd/systemd-networkd.socket new file mode 100644 index 0000000000..9e4e9dd338 --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.socket @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Service Netlink Socket +Documentation=man:systemd-networkd.service(8) man:rtnetlink(7) +ConditionCapability=CAP_NET_ADMIN +DefaultDependencies=no +Before=sockets.target + +[Socket] +ReceiveBuffer=8M +ListenNetlink=route 1361 +PassCredentials=yes + +[Install] +WantedBy=sockets.target diff --git a/src/grp-network/systemd-networkd/systemd-networkd.sysusers b/src/grp-network/systemd-networkd/systemd-networkd.sysusers new file mode 100644 index 0000000000..208148d6b8 --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-network - "systemd Network Management" diff --git a/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles b/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles new file mode 100644 index 0000000000..24197555ee --- /dev/null +++ b/src/grp-network/systemd-networkd/systemd-networkd.tmpfiles @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/systemd/netif 0755 systemd-network systemd-network - +d /run/systemd/netif/links 0755 systemd-network systemd-network - +d /run/systemd/netif/leases 0755 systemd-network systemd-network - diff --git a/src/grp-network/test-network-tables.c b/src/grp-network/test-network-tables.c new file mode 100644 index 0000000000..366471e8bd --- /dev/null +++ b/src/grp-network/test-network-tables.c @@ -0,0 +1,26 @@ +#include "ethtool-util.h" +#include "networkd-netdev-bond.h" +#include "networkd-netdev-macvlan.h" +#include "networkd.h" +#include "sd-netlink/netlink-internal.h" +#include "shared/test-tables.h" +#include "systemd-network/dhcp6-internal.h" +#include "systemd-network/dhcp6-protocol.h" + +int main(int argc, char **argv) { + test_table(bond_mode, NETDEV_BOND_MODE); + /* test_table(link_state, LINK_STATE); — not a reversible mapping */ + test_table(link_operstate, LINK_OPERSTATE); + test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); + test_table(netdev_kind, NETDEV_KIND); + test_table(dhcp6_message_status, DHCP6_STATUS); + test_table(duplex, DUP); + test_table(wol, WOL); + test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA); + + test_table_sparse(macvlan_mode, NETDEV_MACVLAN_MODE); + test_table_sparse(ipvlan_mode, NETDEV_IPVLAN_MODE); + test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE); + + return EXIT_SUCCESS; +} diff --git a/src/grp-network/test-network.c b/src/grp-network/test-network.c new file mode 100644 index 0000000000..a6af304d03 --- /dev/null +++ b/src/grp-network/test-network.c @@ -0,0 +1,216 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/alloc-util.h" +#include "networkd.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/network-internal.h" + +static void test_deserialize_in_addr(void) { + _cleanup_free_ struct in_addr *addresses = NULL; + _cleanup_free_ struct in6_addr *addresses6 = NULL; + struct in_addr a, b, c; + struct in6_addr d, e, f; + int size; + const char *addresses_string = "192.168.0.1 0:0:0:0:0:FFFF:204.152.189.116 192.168.0.2 ::1 192.168.0.3 1:0:0:0:0:0:0:8"; + + assert_se(inet_pton(AF_INET, "0:0:0:0:0:FFFF:204.152.189.116", &a) == 0); + assert_se(inet_pton(AF_INET6, "192.168.0.1", &d) == 0); + + assert_se(inet_pton(AF_INET, "192.168.0.1", &a) == 1); + assert_se(inet_pton(AF_INET, "192.168.0.2", &b) == 1); + assert_se(inet_pton(AF_INET, "192.168.0.3", &c) == 1); + assert_se(inet_pton(AF_INET6, "0:0:0:0:0:FFFF:204.152.189.116", &d) == 1); + assert_se(inet_pton(AF_INET6, "::1", &e) == 1); + assert_se(inet_pton(AF_INET6, "1:0:0:0:0:0:0:8", &f) == 1); + + assert_se((size = deserialize_in_addrs(&addresses, addresses_string)) >= 0); + assert_se(size == 3); + assert_se(!memcmp(&a, &addresses[0], sizeof(struct in_addr))); + assert_se(!memcmp(&b, &addresses[1], sizeof(struct in_addr))); + assert_se(!memcmp(&c, &addresses[2], sizeof(struct in_addr))); + + assert_se((size = deserialize_in6_addrs(&addresses6, addresses_string)) >= 0); + assert_se(size == 3); + assert_se(!memcmp(&d, &addresses6[0], sizeof(struct in6_addr))); + assert_se(!memcmp(&e, &addresses6[1], sizeof(struct in6_addr))); + assert_se(!memcmp(&f, &addresses6[2], sizeof(struct in6_addr))); +} + +static void test_deserialize_dhcp_routes(void) { + size_t size, allocated; + + { + _cleanup_free_ struct sd_dhcp_route *routes = NULL; + assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, "") >= 0); + assert_se(size == 0); + } + + { + /* no errors */ + _cleanup_free_ struct sd_dhcp_route *routes = NULL; + const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0/24,10.1.2.1 0.0.0.0/0,10.0.1.1"; + + assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); + + assert_se(size == 3); + assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0")); + assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1")); + assert_se(routes[0].dst_prefixlen == 16); + + assert_se(routes[1].dst_addr.s_addr == inet_addr("10.1.2.0")); + assert_se(routes[1].gw_addr.s_addr == inet_addr("10.1.2.1")); + assert_se(routes[1].dst_prefixlen == 24); + + assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0")); + assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1")); + assert_se(routes[2].dst_prefixlen == 0); + } + + { + /* error in second word */ + _cleanup_free_ struct sd_dhcp_route *routes = NULL; + const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.1"; + + assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); + + assert_se(size == 2); + assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0")); + assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1")); + assert_se(routes[0].dst_prefixlen == 16); + + assert_se(routes[1].dst_addr.s_addr == inet_addr("0.0.0.0")); + assert_se(routes[1].gw_addr.s_addr == inet_addr("10.0.1.1")); + assert_se(routes[1].dst_prefixlen == 0); + } + + { + /* error in every word */ + _cleanup_free_ struct sd_dhcp_route *routes = NULL; + const char *routes_string = "192.168.0.0/55,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.X"; + + assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); + assert_se(size == 0); + } +} + +static int test_load_config(Manager *manager) { + int r; +/* TODO: should_reload, is false if the config dirs do not exist, so + * so we can't do this test here, move it to a test for paths_check_timestamps + * directly + * + * assert_se(network_should_reload(manager) == true); +*/ + + r = manager_load_config(manager); + if (r == -EPERM) + return r; + assert_se(r >= 0); + + assert_se(manager_should_reload(manager) == false); + + return 0; +} + +static void test_network_get(Manager *manager, struct udev_device *loopback) { + Network *network; + const struct ether_addr mac = {}; + + /* let's assume that the test machine does not have a .network file + that applies to the loopback device... */ + assert_se(network_get(manager, loopback, "lo", &mac, &network) == -ENOENT); + assert_se(!network); +} + +static void test_address_equality(void) { + _cleanup_address_free_ Address *a1 = NULL, *a2 = NULL; + + assert_se(address_new(&a1) >= 0); + assert_se(address_new(&a2) >= 0); + + assert_se(address_equal(NULL, NULL)); + assert_se(!address_equal(a1, NULL)); + assert_se(!address_equal(NULL, a2)); + assert_se(address_equal(a1, a2)); + + a1->family = AF_INET; + assert_se(!address_equal(a1, a2)); + + a2->family = AF_INET; + 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(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)); + + a1->family = AF_INET6; + assert_se(!address_equal(a1, a2)); + + a2->family = AF_INET6; + assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr.in6)); + assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr.in6)); + assert_se(address_equal(a1, a2)); + + a2->prefixlen = 8; + assert_se(address_equal(a1, a2)); + + assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr.in6)); + assert_se(!address_equal(a1, a2)); +} + +int main(void) { + _cleanup_manager_free_ Manager *manager = NULL; + struct udev *udev; + struct udev_device *loopback; + int r; + + test_deserialize_in_addr(); + test_deserialize_dhcp_routes(); + test_address_equality(); + + assert_se(manager_new(&manager) >= 0); + + r = test_load_config(manager); + if (r == -EPERM) + return EXIT_TEST_SKIP; + + udev = udev_new(); + assert_se(udev); + + loopback = udev_device_new_from_syspath(udev, "/sys/class/net/lo"); + assert_se(loopback); + assert_se(udev_device_get_ifindex(loopback) == 1); + + test_network_get(manager, loopback); + + assert_se(manager_rtnl_enumerate_links(manager) >= 0); + + udev_device_unref(loopback); + udev_unref(udev); +} diff --git a/src/grp-network/test-networkd-conf.c b/src/grp-network/test-networkd-conf.c new file mode 100644 index 0000000000..d6a24281bb --- /dev/null +++ b/src/grp-network/test-networkd-conf.c @@ -0,0 +1,141 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/ether-addr-util.h" +#include "basic/hexdecoct.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "networkd-conf.h" +#include "networkd-network.h" +#include "systemd-network/network-internal.h" + +static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) { + DUIDType actual = 0; + int r; + + r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual); + assert_se(r == ret); + assert_se(expected == actual); +} + +static void test_config_parse_duid_type(void) { + test_config_parse_duid_type_one("", 0, 0); + test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT); + test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN); + test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL); + test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID); + test_config_parse_duid_type_one("foo", 0, 0); +} + +static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { + DUID actual = {}; + int r; + _cleanup_free_ char *d = NULL; + + r = config_parse_duid_rawdata("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + d = hexmem(actual.raw_data, actual.raw_data_len); + log_info_errno(r, "\"%s\" → \"%s\" (%m)", + rvalue, strnull(d)); + assert_se(r == ret); + if (expected) { + assert_se(actual.raw_data_len == expected->raw_data_len); + assert_se(memcmp(actual.raw_data, expected->raw_data, expected->raw_data_len) == 0); + } +} + +static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const struct ether_addr* expected) { + struct ether_addr *actual = NULL; + int r; + + r = config_parse_hwaddr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + assert_se(ret == r); + if (expected) { + assert_se(actual); + assert(ether_addr_equal(expected, actual)); + } else { + assert_se(actual == NULL); + } + free(actual); +} + +#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80" + +#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80} + +static void test_config_parse_duid_rawdata(void) { + test_config_parse_duid_rawdata_one("", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("00:11:22:33:44:55:66:77", 0, + &(DUID){0, 8, {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}}); + test_config_parse_duid_rawdata_one("00:11:22:", 0, + &(DUID){0, 3, {0x00,0x11,0x22}}); + test_config_parse_duid_rawdata_one("000:11:22", 0, &(DUID){}); /* error, output is all zeros */ + test_config_parse_duid_rawdata_one("00:111:22", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("0:1:2:3:4:5:6:7", 0, + &(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}}); + test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}}); /* FIXME: should this be an error? */ + test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128 + 2, 0, &(DUID){0, 128, BYTES_1_128}); +} + +static void test_config_parse_hwaddr(void) { + const struct ether_addr t[] = { + { .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } }, + { .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } }, + }; + test_config_parse_hwaddr_one("", 0, NULL); + test_config_parse_hwaddr_one("no:ta:ma:ca:dd:re", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:fx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one(" aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\n", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc: dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:d d:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee", 0, NULL); + test_config_parse_hwaddr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL); + test_config_parse_hwaddr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("01:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("1:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]); + test_config_parse_hwaddr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]); + test_config_parse_hwaddr_one("01-23-45-67-89-ab", 0, &t[1]); + test_config_parse_hwaddr_one("aabb.ccdd.eeff", 0, &t[0]); + test_config_parse_hwaddr_one("0123.4567.89ab", 0, &t[1]); + test_config_parse_hwaddr_one("123.4567.89ab.", 0, NULL); + test_config_parse_hwaddr_one("aabbcc.ddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddee:ff", 0, NULL); + test_config_parse_hwaddr_one("012345.6789ab", 0, NULL); + test_config_parse_hwaddr_one("123.4567.89ab", 0, &t[1]); +} + +int main(int argc, char **argv) { + log_parse_environment(); + log_open(); + + test_config_parse_duid_type(); + test_config_parse_duid_rawdata(); + test_config_parse_hwaddr(); + + return 0; +} diff --git a/src/grp-resolve/90-resolved.preset b/src/grp-resolve/90-resolved.preset new file mode 100644 index 0000000000..c5a5063cc1 --- /dev/null +++ b/src/grp-resolve/90-resolved.preset @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# These ones should be enabled by default, even if distributions +# generally follow a default-off policy. + +enable systemd-resolved.service diff --git a/src/grp-resolve/Makefile b/src/grp-resolve/Makefile new file mode 100644 index 0000000000..8132573d49 --- /dev/null +++ b/src/grp-resolve/Makefile @@ -0,0 +1,31 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += libbasic-dns +nested.subdirs += nss-resolve +nested.subdirs += systemd-resolve +nested.subdirs += systemd-resolved + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-resolve/libbasic-dns/Makefile b/src/grp-resolve/libbasic-dns/Makefile new file mode 100644 index 0000000000..40ee1e85b5 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/Makefile @@ -0,0 +1,109 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +$(outdir)/dns_type-list.txt: src/resolve/dns-type.h + $(AM_V_GEN)$(SED) -n -r 's/.* DNS_TYPE_(\w+).*/\1/p' <$< >$@ + +$(outdir)/dns_type-to-name.h: src/resolve/dns_type-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char *dns_type_to_string(int type) {\n\tswitch(type) {" } {printf " case DNS_TYPE_%s: return ", $$1; sub(/_/, "-"); printf "\"%s\";\n", $$1 } END{ print " default: return NULL;\n\t}\n}\n" }' <$< >$@ + +$(outdir)/dns_type-from-name.gperf: src/resolve/dns_type-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct dns_type_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { s=$$1; sub(/_/, "-", s); printf "%s, ", $$s; printf "DNS_TYPE_%s\n", $$1 }' <$< >$@ + +tests += \ + test-dns-packet \ + test-resolve-tables \ + test-dnssec + +manual_tests += \ + test-dnssec-complex + +test_resolve_tables_SOURCES = \ + src/resolve/test-resolve-tables.c \ + src/resolve/dns_type-from-name.h \ + src/resolve/dns_type-to-name.h \ + $(basic_dns_sources) \ + src/shared/test-tables.h + +test_resolve_tables_CFLAGS = \ + $(GCRYPT_CFLAGS) + +test_resolve_tables_LDADD = \ + libsystemd-shared.la \ + $(GCRYPT_LIBS) \ + -lm + +test_dns_packet_SOURCES = \ + src/resolve/test-dns-packet.c \ + $(basic_dns_sources) + +test_dns_packet_CPPFLAGS = \ + -DRESOLVE_TEST_DIR=\"$(abs_top_srcdir)/src/resolve/test-data\" + +test_dns_packet_CFLAGS = \ + $(GCRYPT_CFLAGS) + +test_dns_packet_LDADD = \ + libsystemd-shared.la \ + $(GCRYPT_LIBS) \ + -lm + +EXTRA_DIST += \ + src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts \ + src/resolve/test-data/fedoraproject.org.pkts \ + src/resolve/test-data/gandi.net.pkts \ + src/resolve/test-data/google.com.pkts \ + src/resolve/test-data/root.pkts \ + src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts \ + src/resolve/test-data/teamits.com.pkts \ + src/resolve/test-data/zbyszek@fedoraproject.org.pkts \ + src/resolve/test-data/_443._tcp.fedoraproject.org.pkts \ + src/resolve/test-data/kyhwana.org.pkts \ + src/resolve/test-data/fake-caa.pkts + +test_dnssec_SOURCES = \ + src/resolve/test-dnssec.c \ + $(basic_dns_sources) + +test_dnssec_CFLAGS = \ + $(GCRYPT_CFLAGS) + +test_dnssec_LDADD = \ + libsystemd-shared.la \ + $(GCRYPT_LIBS) \ + -lm + +test_dnssec_complex_SOURCES = \ + src/resolve/test-dnssec-complex.c \ + src/resolve/dns-type.c \ + src/resolve/dns-type.h + +test_dnssec_complex_LDADD = \ + libsystemd-shared.la + +gperf_txt_sources += \ + src/resolve/dns_type-list.txt + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-resolve/libbasic-dns/dns-type.c b/src/grp-resolve/libbasic-dns/dns-type.c new file mode 100644 index 0000000000..eceb72f545 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/dns-type.c @@ -0,0 +1,333 @@ +/*** + 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 . +***/ + +#include + +#include "basic/parse-util.h" +#include "basic/string-util.h" + +#include "dns-type.h" + +typedef const struct { + uint16_t type; + const char *name; +} dns_type; + +static const struct dns_type_name * +lookup_dns_type (register const char *str, register unsigned int len); + +#include "dns_type-from-name.h" +#include "dns_type-to-name.h" + +int dns_type_from_string(const char *s) { + const struct dns_type_name *sc; + + assert(s); + + sc = lookup_dns_type(s, strlen(s)); + if (sc) + return sc->id; + + s = startswith_no_case(s, "TYPE"); + if (s) { + unsigned x; + + if (safe_atou(s, &x) >= 0 && + x <= UINT16_MAX) + return (int) x; + } + + return _DNS_TYPE_INVALID; +} + +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_zone_transer(uint16_t type) { + + /* Zone transfers, either normal or incremental */ + + return IN_SET(type, + DNS_TYPE_AXFR, + DNS_TYPE_IXFR); +} + +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 + * . */ + + 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); +} + +bool dns_type_needs_authentication(uint16_t type) { + + /* Returns true for all (non-obsolete) RR types where records are not useful if they aren't + * authenticated. I.e. everything that contains crypto keys. */ + + return IN_SET(type, + DNS_TYPE_CERT, + DNS_TYPE_SSHFP, + DNS_TYPE_IPSECKEY, + DNS_TYPE_DS, + DNS_TYPE_DNSKEY, + DNS_TYPE_TLSA, + DNS_TYPE_CDNSKEY, + DNS_TYPE_OPENPGPKEY, + DNS_TYPE_CAA); +} + +int dns_type_to_af(uint16_t t) { + switch (t) { + + case DNS_TYPE_A: + return AF_INET; + + case DNS_TYPE_AAAA: + return AF_INET6; + + case DNS_TYPE_ANY: + return AF_UNSPEC; + + default: + return -EINVAL; + } +} + +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; +} + +const char* tlsa_cert_usage_to_string(uint8_t cert_usage) { + + switch (cert_usage) { + + case 0: + return "CA constraint"; + + case 1: + return "Service certificate constraint"; + + case 2: + return "Trust anchor assertion"; + + case 3: + return "Domain-issued certificate"; + + case 4 ... 254: + return "Unassigned"; + + case 255: + return "Private use"; + } + + return NULL; /* clang cannot count that we covered everything */ +} + +const char* tlsa_selector_to_string(uint8_t selector) { + switch (selector) { + + case 0: + return "Full Certificate"; + + case 1: + return "SubjectPublicKeyInfo"; + + case 2 ... 254: + return "Unassigned"; + + case 255: + return "Private use"; + } + + return NULL; +} + +const char* tlsa_matching_type_to_string(uint8_t selector) { + + switch (selector) { + + case 0: + return "No hash used"; + + case 1: + return "SHA-256"; + + case 2: + return "SHA-512"; + + case 3 ... 254: + return "Unassigned"; + + case 255: + return "Private use"; + } + + return NULL; +} diff --git a/src/grp-resolve/libbasic-dns/dns-type.h b/src/grp-resolve/libbasic-dns/dns-type.h new file mode 100644 index 0000000000..be548b6073 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/dns-type.h @@ -0,0 +1,162 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/macro.h" + +/* DNS record types, taken from + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml. + */ +enum { + /* Normal records */ + DNS_TYPE_A = 0x01, + DNS_TYPE_NS, + DNS_TYPE_MD, + DNS_TYPE_MF, + DNS_TYPE_CNAME, + DNS_TYPE_SOA, + DNS_TYPE_MB, + DNS_TYPE_MG, + DNS_TYPE_MR, + DNS_TYPE_NULL, + DNS_TYPE_WKS, + DNS_TYPE_PTR, + DNS_TYPE_HINFO, + DNS_TYPE_MINFO, + DNS_TYPE_MX, + DNS_TYPE_TXT, + DNS_TYPE_RP, + DNS_TYPE_AFSDB, + DNS_TYPE_X25, + DNS_TYPE_ISDN, + DNS_TYPE_RT, + DNS_TYPE_NSAP, + DNS_TYPE_NSAP_PTR, + DNS_TYPE_SIG, + DNS_TYPE_KEY, + DNS_TYPE_PX, + DNS_TYPE_GPOS, + DNS_TYPE_AAAA, + DNS_TYPE_LOC, + DNS_TYPE_NXT, + DNS_TYPE_EID, + DNS_TYPE_NIMLOC, + DNS_TYPE_SRV, + DNS_TYPE_ATMA, + DNS_TYPE_NAPTR, + DNS_TYPE_KX, + DNS_TYPE_CERT, + DNS_TYPE_A6, + DNS_TYPE_DNAME, + DNS_TYPE_SINK, + DNS_TYPE_OPT, /* EDNS0 option */ + DNS_TYPE_APL, + DNS_TYPE_DS, + DNS_TYPE_SSHFP, + DNS_TYPE_IPSECKEY, + DNS_TYPE_RRSIG, + DNS_TYPE_NSEC, + DNS_TYPE_DNSKEY, + DNS_TYPE_DHCID, + DNS_TYPE_NSEC3, + DNS_TYPE_NSEC3PARAM, + DNS_TYPE_TLSA, + + DNS_TYPE_HIP = 0x37, + DNS_TYPE_NINFO, + DNS_TYPE_RKEY, + DNS_TYPE_TALINK, + DNS_TYPE_CDS, + DNS_TYPE_CDNSKEY, + DNS_TYPE_OPENPGPKEY, + + DNS_TYPE_SPF = 0x63, + DNS_TYPE_NID, + DNS_TYPE_L32, + DNS_TYPE_L64, + DNS_TYPE_LP, + DNS_TYPE_EUI48, + DNS_TYPE_EUI64, + + DNS_TYPE_TKEY = 0xF9, + DNS_TYPE_TSIG, + DNS_TYPE_IXFR, + DNS_TYPE_AXFR, + DNS_TYPE_MAILB, + DNS_TYPE_MAILA, + DNS_TYPE_ANY, + DNS_TYPE_URI, + DNS_TYPE_CAA, + DNS_TYPE_TA = 0x8000, + DNS_TYPE_DLV, + + _DNS_TYPE_MAX, + _DNS_TYPE_INVALID = -1 +}; + +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 +}; + +#define _DNS_CLASS_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) +#define _DNS_TYPE_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) + +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_type_needs_authentication(uint16_t type); +bool dns_type_is_zone_transer(uint16_t type); +int dns_type_to_af(uint16_t type); + +bool dns_class_is_pseudo(uint16_t class); +bool dns_class_is_valid_rr(uint16_t class); + +/* TYPE?? follows http://tools.ietf.org/html/rfc3597#section-5 */ +const char *dns_type_to_string(int type); +int dns_type_from_string(const char *s); + +const char *dns_class_to_string(uint16_t class); +int dns_class_from_string(const char *name); + +/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.2 */ +const char *tlsa_cert_usage_to_string(uint8_t cert_usage); + +/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.3 */ +const char *tlsa_selector_to_string(uint8_t selector); + +/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */ +const char *tlsa_matching_type_to_string(uint8_t selector); + +/* https://tools.ietf.org/html/rfc6844#section-5.1 */ +#define CAA_FLAG_CRITICAL (1u << 7) diff --git a/src/grp-resolve/libbasic-dns/resolved-def.h b/src/grp-resolve/libbasic-dns/resolved-def.h new file mode 100644 index 0000000000..c4c1915b18 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-def.h @@ -0,0 +1,38 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#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_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/grp-resolve/libbasic-dns/resolved-dns-answer.c b/src/grp-resolve/libbasic-dns/resolved-dns-answer.c new file mode 100644 index 0000000000..c45505b441 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-answer.c @@ -0,0 +1,859 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-answer.h" +#include "resolved-dns-dnssec.h" + +DnsAnswer *dns_answer_new(unsigned n) { + DnsAnswer *a; + + a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n); + if (!a) + return NULL; + + a->n_ref = 1; + a->n_allocated = n; + + return a; +} + +DnsAnswer *dns_answer_ref(DnsAnswer *a) { + if (!a) + return NULL; + + assert(a->n_ref > 0); + a->n_ref++; + 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; + + assert(a->n_ref > 0); + + if (a->n_ref == 1) { + dns_answer_flush(a); + free(a); + } else + a->n_ref--; + + return NULL; +} + +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; + + assert(rr); + + 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) + continue; + + r = dns_resource_record_equal(a->items[i].rr, rr); + if (r < 0) + return r; + if (r > 0) { + /* Don't mix contradicting TTLs (see below) */ + if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) + return -EINVAL; + + /* 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; + } + } + + return dns_answer_add_raw(a, rr, ifindex, flags); +} + +static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) { + DnsResourceRecord *rr; + DnsAnswerFlags flags; + int ifindex, r; + + 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, int ifindex) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL; + + soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name); + if (!soa) + return -ENOMEM; + + soa->ttl = ttl; + + soa->soa.mname = strdup(name); + if (!soa->soa.mname) + return -ENOMEM; + + soa->soa.rname = strappend("root.", name); + if (!soa->soa.rname) + return -ENOMEM; + + soa->soa.serial = 1; + soa->soa.refresh = 1; + soa->soa.retry = 1; + soa->soa.expire = 1; + soa->soa.minimum = ttl; + + return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED); +} + +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); + + 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; + + 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) + continue; + + 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_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); + + /* For a SOA record we can never find a matching SOA record */ + if (key->type == DNS_TYPE_SOA) + return 0; + + 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_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 (dns_answer_size(a) <= 0) { + *ret = dns_answer_ref(b); + return 0; + } + + if (dns_answer_size(b) <= 0) { + *ret = dns_answer_ref(a); + return 0; + } + + k = dns_answer_new(a->n_rrs + b->n_rrs); + if (!k) + return -ENOMEM; + + 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; +} + +int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) { + bool found = false, other = false; + DnsResourceRecord *rr; + unsigned i; + int r; + + assert(a); + assert(key); + + /* 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; + + r = dns_answer_add_raw(copy, rr, ifindex, flags); + if (r < 0) + return r; + } + + dns_answer_unref(*a); + *a = copy; + copy = NULL; + + return 1; + } + + /* 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 r; + } + + dns_answer_unref(*a); + *a = copy; + copy = NULL; + + return 1; + } + + /* Only a single reference, edit in-place */ + + 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) { + DnsAnswerItem *items; + unsigned i, start, end; + + if (!a) + return; + + if (a->n_rrs <= 1) + return; + + start = 0; + end = a->n_rrs-1; + + /* RFC 4795, Section 2.6 suggests we should order entries + * depending on whether the sender is a link-local address. */ + + items = newa(DnsAnswerItem, a->n_rrs); + for (i = 0; i < a->n_rrs; i++) { + + if (a->items[i].rr->key->class == DNS_CLASS_IN && + ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) || + (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local))) + /* Order address records that are not preferred to the end of the array */ + items[end--] = a->items[i]; + else + /* Order all other records to the beginning of the array */ + items[start++] = a->items[i]; + } + + assert(start == end+1); + memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs); +} + +int dns_answer_reserve(DnsAnswer **a, unsigned n_free) { + DnsAnswer *n; + + assert(a); + + if (n_free <= 0) + return 0; + + if (*a) { + unsigned ns; + + if ((*a)->n_ref > 1) + return -EBUSY; + + ns = (*a)->n_rrs + 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; + + n->n_allocated = ns; + } else { + n = dns_answer_new(n_free); + if (!n) + return -ENOMEM; + } + + *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 + * possible, 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/grp-resolve/libbasic-dns/resolved-dns-answer.h b/src/grp-resolve/libbasic-dns/resolved-dns-answer.h new file mode 100644 index 0000000000..bbb836fc7e --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-answer.h @@ -0,0 +1,148 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" + +typedef struct DnsAnswer DnsAnswer; +typedef struct DnsAnswerItem DnsAnswerItem; + +#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. + * + * Note that we usually encode 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 { + unsigned n_ref; + unsigned n_rrs, n_allocated; + DnsAnswerItem items[0]; +}; + +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, 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 ifindex); + +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; +} + +static inline bool dns_answer_isempty(DnsAnswer *a) { + return dns_answer_size(a) <= 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/grp-resolve/libbasic-dns/resolved-dns-dnssec.c b/src/grp-resolve/libbasic-dns/resolved-dns-dnssec.c new file mode 100644 index 0000000000..a53f2daa28 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-dnssec.c @@ -0,0 +1,2200 @@ +/*** + 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 . +***/ + +#ifdef HAVE_GCRYPT +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/string-table.h" +#include "shared/dns-domain.h" +#include "shared/gcrypt-util.h" + +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" + +#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 + */ + +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); +} + +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; +} + +#ifdef HAVE_GCRYPT + +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 that the RRSet matches the specified "key" in "a", + * using the signature "rrsig" and the key "dnskey". It's + * assumed that 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(false); + + 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; +} + +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(false); + + 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(false); + + 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 these NSEC3 RRs weren't correctly signed we can't make this + * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */ + if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1) + return 0; + /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */ + if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -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' */ + + if (!pp) { + /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR + * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are + * appropriately set. */ + + if (key->type == DNS_TYPE_DS) { + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) + return -EBADMSG; + } else { + if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) && + !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) + return -EBADMSG; + } + + /* 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; + } + + /* 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; + + /* 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 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); + 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; +} + +static 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 "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); +} + +#else + +int dnssec_verify_rrset( + DnsAnswer *a, + const DnsResourceKey *key, + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey, + usec_t realtime, + DnssecResult *result) { + + return -EOPNOTSUPP; +} + +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) { + + return -EOPNOTSUPP; +} + +int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_rrset_search( + DnsAnswer *a, + const DnsResourceKey *key, + DnsAnswer *validated_dnskeys, + usec_t realtime, + DnssecResult *result, + DnsResourceRecord **ret_rrsig) { + + return -EOPNOTSUPP; +} + +int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { + + return -EOPNOTSUPP; +} + +int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { + + return -EOPNOTSUPP; +} + +int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { + + return -EOPNOTSUPP; +} + +int dnssec_test_positive_wildcard( + DnsAnswer *answer, + const char *name, + const char *source, + const char *zone, + bool *authenticated) { + + return -EOPNOTSUPP; +} + +#endif + +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); + +static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = { + [DNSSEC_SECURE] = "secure", + [DNSSEC_INSECURE] = "insecure", + [DNSSEC_BOGUS] = "bogus", + [DNSSEC_INDETERMINATE] = "indeterminate", +}; +DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict); diff --git a/src/grp-resolve/libbasic-dns/resolved-dns-dnssec.h b/src/grp-resolve/libbasic-dns/resolved-dns-dnssec.h new file mode 100644 index 0000000000..b73cc24100 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-dnssec.h @@ -0,0 +1,103 @@ +#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 . +***/ + +#include "shared/dns-domain.h" + +typedef enum DnssecResult DnssecResult; +typedef enum DnssecVerdict DnssecVerdict; + +#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 +}; + +enum DnssecVerdict { + DNSSEC_SECURE, + DNSSEC_INSECURE, + DNSSEC_BOGUS, + DNSSEC_INDETERMINATE, + + _DNSSEC_VERDICT_MAX, + _DNSSEC_VERDICT_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_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_; + +const char* dnssec_verdict_to_string(DnssecVerdict m) _const_; +DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_; diff --git a/src/grp-resolve/libbasic-dns/resolved-dns-packet.c b/src/grp-resolve/libbasic-dns/resolved-dns-packet.c new file mode 100644 index 0000000000..e5a497e0ed --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-packet.c @@ -0,0 +1,2301 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include "basic/alloc-util.h" +#include "basic/string-table.h" +#include "basic/strv.h" +#include "basic/unaligned.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-packet.h" + +#define EDNS0_OPT_DO (1<<15) + +typedef struct DnsPacketRewinder { + DnsPacket *packet; + size_t saved_rindex; +} DnsPacketRewinder; + +static void rewind_dns_packet(DnsPacketRewinder *rewinder) { + if (rewinder->packet) + dns_packet_rewind(rewinder->packet, rewinder->saved_rindex); +} + +#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0) +#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0) + +int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { + DnsPacket *p; + size_t a; + + assert(ret); + + if (mtu <= UDP_PACKET_HEADER_SIZE) + a = DNS_PACKET_SIZE_START; + else + a = mtu - UDP_PACKET_HEADER_SIZE; + + if (a < DNS_PACKET_HEADER_SIZE) + a = DNS_PACKET_HEADER_SIZE; + + /* round up to next page size */ + a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket)); + + /* make sure we never allocate more than useful */ + if (a > DNS_PACKET_SIZE_MAX) + a = DNS_PACKET_SIZE_MAX; + + p = malloc0(ALIGN(sizeof(DnsPacket)) + a); + if (!p) + return -ENOMEM; + + 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; + + return 0; +} + +void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) { + + DnsPacketHeader *h; + + assert(p); + + h = DNS_PACKET_HEADER(p); + + switch(p->protocol) { + case DNS_PROTOCOL_LLMNR: + assert(!truncated); + + h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, + 0 /* opcode */, + 0 /* c */, + 0 /* tc */, + 0 /* t */, + 0 /* ra */, + 0 /* ad */, + 0 /* cd */, + 0 /* rcode */)); + 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 */, + 0 /* tc */, + 1 /* rd (ask for recursion) */, + 0 /* ra */, + 0 /* ad */, + 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; +} + +DnsPacket *dns_packet_ref(DnsPacket *p) { + + if (!p) + return NULL; + + assert(!p->on_stack); + + assert(p->n_ref > 0); + p->n_ref++; + return p; +} + +static void dns_packet_free(DnsPacket *p) { + char *s; + + assert(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); + + if (!p->on_stack) + free(p); +} + +DnsPacket *dns_packet_unref(DnsPacket *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + dns_packet_unref(p->more); + + if (p->n_ref == 1) + dns_packet_free(p); + else + p->n_ref--; + + return NULL; +} + +int dns_packet_validate(DnsPacket *p) { + assert(p); + + if (p->size < DNS_PACKET_HEADER_SIZE) + return -EBADMSG; + + if (p->size > DNS_PACKET_SIZE_MAX) + return -EBADMSG; + + return 1; +} + +int dns_packet_validate_reply(DnsPacket *p) { + int r; + + assert(p); + + r = dns_packet_validate(p); + if (r < 0) + return r; + + if (DNS_PACKET_QR(p) != 1) + return 0; + + if (DNS_PACKET_OPCODE(p) != 0) + 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) + return -EBADMSG; + + break; + + case DNS_PROTOCOL_MDNS: + /* RFC 6762, Section 18 */ + if (DNS_PACKET_RCODE(p) != 0) + return -EBADMSG; + + break; + + default: + break; + } + + return 1; +} + +int dns_packet_validate_query(DnsPacket *p) { + int r; + + assert(p); + + r = dns_packet_validate(p); + if (r < 0) + return r; + + if (DNS_PACKET_QR(p) != 0) + return 0; + + if (DNS_PACKET_OPCODE(p) != 0) + return -EBADMSG; + + if (DNS_PACKET_TC(p)) + return -EBADMSG; + + switch (p->protocol) { + + case DNS_PROTOCOL_LLMNR: + case DNS_PROTOCOL_DNS: + /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */ + if (DNS_PACKET_QDCOUNT(p) != 1) + return -EBADMSG; + + /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */ + if (DNS_PACKET_ANCOUNT(p) > 0) + return -EBADMSG; + + /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */ + if (DNS_PACKET_NSCOUNT(p) > 0) + return -EBADMSG; + + 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; + } + + return 1; +} + +static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) { + assert(p); + + if (p->size + add > p->allocated) { + size_t a; + + a = PAGE_ALIGN((p->size + add) * 2); + if (a > DNS_PACKET_SIZE_MAX) + a = DNS_PACKET_SIZE_MAX; + + if (p->size + add > a) + return -EMSGSIZE; + + if (p->_data) { + void *d; + + d = realloc(p->_data, a); + if (!d) + return -ENOMEM; + + p->_data = d; + } else { + p->_data = malloc(a); + if (!p->_data) + return -ENOMEM; + + memcpy(p->_data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size); + memzero((uint8_t*) p->_data + p->size, a - p->size); + } + + p->allocated = a; + } + + if (start) + *start = p->size; + + if (ret) + *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size; + + p->size += add; + return 0; +} + +void dns_packet_truncate(DnsPacket *p, size_t sz) { + Iterator i; + char *s; + void *n; + + assert(p); + + if (p->size <= sz) + return; + + HASHMAP_FOREACH_KEY(n, s, p->names, i) { + + if (PTR_TO_SIZE(n) < sz) + continue; + + hashmap_remove(p->names, s); + free(s); + } + + p->size = sz; +} + +int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) { + void *q; + int r; + + assert(p); + + r = dns_packet_extend(p, l, &q, start); + if (r < 0) + return r; + + memcpy(q, d, l); + return 0; +} + +int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) { + void *d; + int r; + + assert(p); + + r = dns_packet_extend(p, sizeof(uint8_t), &d, start); + if (r < 0) + return r; + + ((uint8_t*) d)[0] = v; + + return 0; +} + +int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) { + void *d; + int r; + + assert(p); + + r = dns_packet_extend(p, sizeof(uint16_t), &d, start); + if (r < 0) + return r; + + unaligned_write_be16(d, v); + + return 0; +} + +int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) { + void *d; + int r; + + assert(p); + + r = dns_packet_extend(p, sizeof(uint32_t), &d, start); + if (r < 0) + return r; + + unaligned_write_be32(d, v); + + return 0; +} + +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; + int r; + + assert(p); + assert(s || size == 0); + + if (size > 255) + return -E2BIG; + + r = dns_packet_extend(p, 1 + size, &d, start); + if (r < 0) + return r; + + ((uint8_t*) d)[0] = (uint8_t) size; + + memcpy_safe(((uint8_t*) d) + 1, s, size); + + return 0; +} + +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, (void**) &w, start); + if (r < 0) + return r; + + *(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; +} + +int dns_packet_append_name( + DnsPacket *p, + const char *name, + bool allow_compression, + bool canonical_candidate, + size_t *start) { + + size_t saved_size; + int r; + + assert(p); + assert(name); + + if (p->refuse_compression) + allow_compression = false; + + saved_size = p->size; + + while (!dns_name_is_root(name)) { + const char *z = name; + char label[DNS_LABEL_MAX]; + size_t n = 0; + + if (allow_compression) + n = PTR_TO_SIZE(hashmap_get(p->names, name)); + if (n > 0) { + assert(n < p->size); + + if (n < 0x4000) { + r = dns_packet_append_uint16(p, 0xC000 | n, NULL); + if (r < 0) + goto fail; + + goto done; + } + } + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + goto fail; + + 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; + + r = hashmap_put(p->names, s, SIZE_TO_PTR(n)); + if (r < 0) + goto fail; + + s = NULL; + } + } + + r = dns_packet_append_uint8(p, 0, NULL); + if (r < 0) + return r; + +done: + if (start) + *start = saved_size; + + return 0; + +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) { + size_t saved_size; + int r; + + assert(p); + assert(k); + + saved_size = p->size; + + r = dns_packet_append_name(p, dns_resource_key_name(k), true, true, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, k->type, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, k->class, NULL); + if (r < 0) + goto fail; + + if (start) + *start = saved_size; + + return 0; + +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +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; + + assert(p); + assert(types); + assert(length > 0); + + saved_size = p->size; + + r = dns_packet_append_uint8(p, window, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, length, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, types, length, NULL); + if (r < 0) + goto fail; + + if (start) + *start = saved_size; + + return 0; +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { + Iterator i; + uint8_t window = 0; + uint8_t entry = 0; + uint8_t bitmaps[32] = {}; + unsigned n; + size_t saved_size; + int r; + + assert(p); + + saved_size = p->size; + + BITMAP_FOREACH(n, types, i) { + assert(n <= 0xffff); + + if ((n >> 8) != window && bitmaps[entry / 8] != 0) { + r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); + if (r < 0) + goto fail; + + zero(bitmaps); + } + + window = n >> 8; + entry = n & 255; + + bitmaps[entry / 8] |= 1 << (7 - (entry % 8)); + } + + 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, int rcode, 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); + assert(rcode >= 0); + assert(rcode <= _DNS_RCODE_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; + + /* class: 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, ((uint16_t) rcode & 0x0FF0) << 4, 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 && !DNS_PACKET_QR(p)) { + /* If DO is on and this is not a reply, 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_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); + assert(rr); + + saved_size = p->size; + + r = dns_packet_append_key(p, rr->key, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->ttl, NULL); + if (r < 0) + goto fail; + + /* Initially we write 0 here */ + r = dns_packet_append_uint16(p, 0, &rdlength_offset); + if (r < 0) + goto fail; + + rds = p->size - saved_size; + + switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + + case DNS_TYPE_SRV: + r = dns_packet_append_uint16(p, rr->srv.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->srv.weight, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->srv.port, NULL); + if (r < 0) + goto fail; + + 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, false, NULL); + break; + + case DNS_TYPE_HINFO: + r = dns_packet_append_string(p, rr->hinfo.cpu, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->hinfo.os, NULL); + break; + + case DNS_TYPE_SPF: /* exactly the same as TXT */ + case DNS_TYPE_TXT: + + if (!rr->txt.items) { + /* RFC 6763, section 6.1 suggests to generate + * single empty string for an empty array. */ + + r = dns_packet_append_raw_string(p, NULL, 0, NULL); + if (r < 0) + goto fail; + } else { + 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; + } + } + + r = 0; + break; + + case DNS_TYPE_A: + r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); + break; + + case DNS_TYPE_AAAA: + r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); + break; + + case DNS_TYPE_SOA: + 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, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->soa.serial, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->soa.refresh, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->soa.retry, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->soa.expire, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->soa.minimum, NULL); + break; + + case DNS_TYPE_MX: + r = dns_packet_append_uint16(p, rr->mx.priority, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL); + break; + + case DNS_TYPE_LOC: + r = dns_packet_append_uint8(p, rr->loc.version, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.horiz_pre, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->loc.vert_pre, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.latitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.longitude, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->loc.altitude, NULL); + break; + + case DNS_TYPE_DS: + r = dns_packet_append_uint16(p, rr->ds.key_tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->ds.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->ds.digest_type, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->ds.digest, rr->ds.digest_size, NULL); + break; + + case DNS_TYPE_SSHFP: + r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, NULL); + break; + + case DNS_TYPE_DNSKEY: + r = dns_packet_append_uint16(p, rr->dnskey.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->dnskey.protocol, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->dnskey.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->dnskey.key, rr->dnskey.key_size, NULL); + break; + + case DNS_TYPE_RRSIG: + r = dns_packet_append_uint16(p, rr->rrsig.type_covered, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->rrsig.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->rrsig.labels, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.original_ttl, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.expiration, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint32(p, rr->rrsig.inception, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->rrsig.key_tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); + break; + + case DNS_TYPE_NSEC: + r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec.types, NULL); + if (r < 0) + goto fail; + + break; + + case DNS_TYPE_NSEC3: + r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_types(p, rr->nsec3.types, NULL); + if (r < 0) + goto fail; + + break; + + case DNS_TYPE_TLSA: + r = dns_packet_append_uint8(p, rr->tlsa.cert_usage, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->tlsa.selector, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_uint8(p, rr->tlsa.matching_type, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL); + break; + + case DNS_TYPE_CAA: + r = dns_packet_append_uint8(p, rr->caa.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->caa.tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL); + break; + + case DNS_TYPE_OPT: + case DNS_TYPE_OPENPGPKEY: + case _DNS_TYPE_INVALID: /* unparseable */ + default: + + r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL); + break; + } + if (r < 0) + goto fail; + + /* Let's calculate the actual data size and update the field */ + rdlength = p->size - rdlength_offset - sizeof(uint16_t); + if (rdlength > 0xFFFF) { + r = -ENOSPC; + goto fail; + } + + end = p->size; + p->size = rdlength_offset; + r = dns_packet_append_uint16(p, rdlength, NULL); + if (r < 0) + goto fail; + p->size = end; + + if (start) + *start = saved_size; + + if (rdata_start) + *rdata_start = rds; + + return 0; + +fail: + dns_packet_truncate(p, saved_size); + return r; +} + +int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) { + DnsResourceKey *key; + int r; + + assert(p); + + DNS_QUESTION_FOREACH(key, q) { + r = dns_packet_append_key(p, key, NULL); + if (r < 0) + return r; + } + + return 0; +} + +int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) { + DnsResourceRecord *rr; + int r; + + assert(p); + + DNS_ANSWER_FOREACH(rr, a) { + r = dns_packet_append_rr(p, rr, NULL, NULL); + if (r < 0) + return r; + } + + return 0; +} + +int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) { + assert(p); + + if (p->rindex + sz > p->size) + return -EMSGSIZE; + + if (ret) + *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex; + + if (start) + *start = p->rindex; + + p->rindex += sz; + return 0; +} + +void dns_packet_rewind(DnsPacket *p, size_t idx) { + assert(p); + assert(idx <= p->size); + assert(idx >= DNS_PACKET_HEADER_SIZE); + + p->rindex = idx; +} + +int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) { + const void *q; + int r; + + assert(p); + assert(d); + + r = dns_packet_read(p, sz, &q, start); + if (r < 0) + return r; + + memcpy(d, q, sz); + return 0; +} + +static int dns_packet_read_memdup( + DnsPacket *p, size_t size, + void **ret, size_t *ret_size, + size_t *ret_start) { + + const void *src; + size_t start; + int r; + + assert(p); + assert(ret); + + r = dns_packet_read(p, size, &src, &start); + if (r < 0) + return r; + + if (size <= 0) + *ret = NULL; + else { + void *copy; + + copy = memdup(src, size); + if (!copy) + return -ENOMEM; + + *ret = copy; + } + + if (ret_size) + *ret_size = size; + if (ret_start) + *ret_start = start; + + return 0; +} + +int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { + const void *d; + int r; + + assert(p); + + r = dns_packet_read(p, sizeof(uint8_t), &d, start); + if (r < 0) + return r; + + *ret = ((uint8_t*) d)[0]; + return 0; +} + +int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) { + const void *d; + int r; + + assert(p); + + r = dns_packet_read(p, sizeof(uint16_t), &d, start); + if (r < 0) + return r; + + *ret = unaligned_read_be16(d); + + return 0; +} + +int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { + const void *d; + int r; + + assert(p); + + r = dns_packet_read(p, sizeof(uint32_t), &d, start); + if (r < 0) + return r; + + *ret = unaligned_read_be32(d); + + return 0; +} + +int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + const void *d; + char *t; + uint8_t c; + int r; + + assert(p); + INIT_REWINDER(rewinder, p); + + r = dns_packet_read_uint8(p, &c, NULL); + if (r < 0) + return r; + + r = dns_packet_read(p, c, &d, NULL); + if (r < 0) + return r; + + if (memchr(d, 0, c)) + return -EBADMSG; + + t = strndup(d, c); + if (!t) + return -ENOMEM; + + if (!utf8_is_valid(t)) { + free(t); + return -EBADMSG; + } + + *ret = t; + + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + uint8_t c; + int r; + + assert(p); + INIT_REWINDER(rewinder, p); + + r = dns_packet_read_uint8(p, &c, NULL); + if (r < 0) + return r; + + r = dns_packet_read(p, c, ret, NULL); + if (r < 0) + return r; + + if (size) + *size = c; + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +int dns_packet_read_name( + DnsPacket *p, + char **_ret, + bool allow_compression, + size_t *start) { + + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t after_rindex = 0, jump_barrier; + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + bool first = true; + int r; + + assert(p); + assert(_ret); + INIT_REWINDER(rewinder, p); + jump_barrier = p->rindex; + + if (p->refuse_compression) + allow_compression = false; + + for (;;) { + uint8_t c, d; + + r = dns_packet_read_uint8(p, &c, NULL); + if (r < 0) + return r; + + if (c == 0) + /* End of name */ + break; + else if (c <= 63) { + const char *label; + + /* Literal label */ + r = dns_packet_read(p, c, (const void**) &label, NULL); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + continue; + } else if (allow_compression && (c & 0xc0) == 0xc0) { + uint16_t ptr; + + /* Pointer */ + r = dns_packet_read_uint8(p, &d, NULL); + if (r < 0) + return r; + + ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; + if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) + return -EBADMSG; + + if (after_rindex == 0) + after_rindex = p->rindex; + + /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ + jump_barrier = ptr; + p->rindex = ptr; + } else + return -EBADMSG; + } + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + + ret[n] = 0; + + if (after_rindex != 0) + p->rindex= after_rindex; + + *_ret = ret; + ret = NULL; + + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { + uint8_t window; + uint8_t length; + const uint8_t *bitmap; + uint8_t bit = 0; + unsigned i; + bool found = false; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + int r; + + assert(p); + assert(types); + INIT_REWINDER(rewinder, p); + + r = bitmap_ensure_allocated(types); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &window, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &length, NULL); + if (r < 0) + return r; + + if (length == 0 || length > 32) + return -EBADMSG; + + r = dns_packet_read(p, length, (const void **)&bitmap, NULL); + if (r < 0) + return r; + + for (i = 0; i < length; i++) { + uint8_t bitmask = 1 << 7; + + if (!bitmap[i]) { + found = false; + bit += 8; + continue; + } + + found = true; + + while (bitmask) { + if (bitmap[i] & bitmask) { + uint16_t n; + + n = (uint16_t) window << 8 | (uint16_t) bit; + + /* Ignore pseudo-types. see RFC4034 section 4.1.2 */ + if (dns_type_is_pseudo(n)) + continue; + + r = bitmap_set(*types, n); + if (r < 0) + return r; + } + + bit++; + bitmask >>= 1; + } + } + + if (!found) + return -EBADMSG; + + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + int r; + + INIT_REWINDER(rewinder, p); + + while (p->rindex < rewinder.saved_rindex + size) { + r = dns_packet_read_type_window(p, types, NULL); + if (r < 0) + return r; + + /* don't read past end of current RR */ + if (p->rindex > rewinder.saved_rindex + size) + return -EBADMSG; + } + + if (p->rindex != rewinder.saved_rindex + size) + return -EBADMSG; + + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + _cleanup_free_ char *name = NULL; + bool cache_flush = false; + uint16_t class, type; + DnsResourceKey *key; + int r; + + assert(p); + assert(ret); + INIT_REWINDER(rewinder, p); + + r = dns_packet_read_name(p, &name, true, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint16(p, &type, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint16(p, &class, NULL); + if (r < 0) + return r; + + 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) + return -ENOMEM; + + name = NULL; + *ret = key; + + if (ret_cache_flush) + *ret_cache_flush = cache_flush; + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +static bool loc_size_ok(uint8_t size) { + uint8_t m = size >> 4, e = size & 0xF; + + return m <= 9 && e <= 9 && (m > 0 || e == 0); +} + +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; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t offset; + uint16_t rdlength; + bool cache_flush; + int r; + + assert(p); + assert(ret); + + INIT_REWINDER(rewinder, p); + + r = dns_packet_read_key(p, &key, &cache_flush, NULL); + if (r < 0) + return r; + + if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type)) + return -EBADMSG; + + rr = dns_resource_record_new(key); + if (!rr) + return -ENOMEM; + + r = dns_packet_read_uint32(p, &rr->ttl, NULL); + if (r < 0) + return r; + + /* 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) + return r; + + if (p->rindex + rdlength > p->size) + return -EBADMSG; + + offset = p->rindex; + + switch (rr->key->type) { + + case DNS_TYPE_SRV: + r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); + if (r < 0) + return r; + r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); + if (r < 0) + return r; + r = dns_packet_read_uint16(p, &rr->srv.port, NULL); + if (r < 0) + return r; + r = dns_packet_read_name(p, &rr->srv.name, true, NULL); + break; + + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + r = dns_packet_read_name(p, &rr->ptr.name, true, NULL); + break; + + case DNS_TYPE_HINFO: + r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->hinfo.os, NULL); + break; + + 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. */ + + 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) { + DnsTxtItem *i; + const void *data; + size_t sz; + + r = dns_packet_read_raw_string(p, &data, &sz, NULL); + if (r < 0) + return r; + + 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; + } + } + + r = 0; + break; + + case DNS_TYPE_A: + r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); + break; + + case DNS_TYPE_AAAA: + r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); + break; + + case DNS_TYPE_SOA: + r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); + if (r < 0) + return r; + + r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); + break; + + case DNS_TYPE_MX: + r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); + if (r < 0) + return r; + + r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); + break; + + case DNS_TYPE_LOC: { + uint8_t t; + size_t pos; + + r = dns_packet_read_uint8(p, &t, &pos); + if (r < 0) + return r; + + if (t == 0) { + rr->loc.version = t; + + r = dns_packet_read_uint8(p, &rr->loc.size, NULL); + if (r < 0) + return r; + + if (!loc_size_ok(rr->loc.size)) + return -EBADMSG; + + r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); + if (r < 0) + return r; + + if (!loc_size_ok(rr->loc.horiz_pre)) + return -EBADMSG; + + r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); + if (r < 0) + return r; + + if (!loc_size_ok(rr->loc.vert_pre)) + return -EBADMSG; + + r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); + if (r < 0) + return r; + + break; + } else { + dns_packet_rewind(p, pos); + rr->unparseable = true; + goto unparseable; + } + } + + case DNS_TYPE_DS: + r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, rdlength - 4, + &rr->ds.digest, &rr->ds.digest_size, + NULL); + if (r < 0) + return r; + + if (rr->ds.digest_size <= 0) + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + return -EBADMSG; + + break; + + case DNS_TYPE_SSHFP: + r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, rdlength - 2, + &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, + NULL); + + if (rr->sshfp.fingerprint_size <= 0) + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + return -EBADMSG; + + break; + + case DNS_TYPE_DNSKEY: + r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, rdlength - 4, + &rr->dnskey.key, &rr->dnskey.key_size, + NULL); + + if (rr->dnskey.key_size <= 0) + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + return -EBADMSG; + + break; + + case DNS_TYPE_RRSIG: + r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); + if (r < 0) + return r; + + r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, + &rr->rrsig.signature, &rr->rrsig.signature_size, + NULL); + + if (rr->rrsig.signature_size <= 0) + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + return -EBADMSG; + + break; + + 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) + return r; + + r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); + + /* 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; + + r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); + if (r < 0) + return r; + + /* this may be zero */ + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &size, NULL); + if (r < 0) + return r; + + if (size <= 0) + return -EBADMSG; + + r = dns_packet_read_memdup(p, size, + &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, + NULL); + if (r < 0) + return r; + + r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); + + /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ + + break; + } + + case DNS_TYPE_TLSA: + r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL); + if (r < 0) + return r; + + r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, rdlength - 3, + &rr->tlsa.data, &rr->tlsa.data_size, + NULL); + + if (rr->tlsa.data_size <= 0) + /* the accepted size depends on the algorithm, but for now + just ensure that the value is greater than zero */ + return -EBADMSG; + + break; + + case DNS_TYPE_CAA: + r = dns_packet_read_uint8(p, &rr->caa.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->caa.tag, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, + rdlength + offset - p->rindex, + &rr->caa.value, &rr->caa.value_size, NULL); + + break; + + case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */ + case DNS_TYPE_OPENPGPKEY: + default: + unparseable: + r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); + + break; + } + if (r < 0) + return r; + if (p->rindex != offset + rdlength) + return -EBADMSG; + + *ret = rr; + rr = NULL; + + if (ret_cache_flush) + *ret_cache_flush = cache_flush; + if (start) + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); + + return 0; +} + +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) { + *rfc6975 = false; + return true; /* if it's not version 0, it's OK, but we will ignore the OPT field contents */ + } + + p = rr->opt.data; + l = rr->opt.data_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; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; + unsigned n, i; + int r; + + if (p->extracted) + return 0; + + INIT_REWINDER(rewinder, p); + dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); + + n = DNS_PACKET_QDCOUNT(p); + if (n > 0) { + question = dns_question_new(n); + if (!question) + return -ENOMEM; + + for (i = 0; i < n; i++) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + bool cache_flush; + + r = dns_packet_read_key(p, &key, &cache_flush, NULL); + if (r < 0) + return r; + + if (cache_flush) + return -EBADMSG; + + if (!dns_type_is_valid_query(key->type)) + return -EBADMSG; + + r = dns_question_add(question, key); + if (r < 0) + return r; + } + } + + n = DNS_PACKET_RRCOUNT(p); + if (n > 0) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL; + bool bad_opt = false; + + answer = dns_answer_new(n); + if (!answer) + return -ENOMEM; + + for (i = 0; i < n; i++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + bool cache_flush; + + r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); + if (r < 0) + return r; + + /* 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 is 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 (DNS_PACKET_QR(p)) { + /* Additional checks for responses */ + + if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) { + /* If this is a reply and we don't know the EDNS version then something + * is weird... */ + log_debug("EDNS version newer that our request, bad server."); + return -EBADMSG; + } + + if (has_rfc6975) { + /* If the 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 routers 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) + return r; + } + + /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note + * that we only do this if we actually decided to keep the RR around. */ + dns_resource_record_unref(previous); + previous = dns_resource_record_ref(rr); + } + + if (bad_opt) + p->opt = dns_resource_record_unref(p->opt); + } + + p->question = question; + question = NULL; + + p->answer = answer; + answer = NULL; + + p->extracted = true; + + /* no CANCEL, always rewind */ + return 0; +} + +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", + [DNS_RCODE_SERVFAIL] = "SERVFAIL", + [DNS_RCODE_NXDOMAIN] = "NXDOMAIN", + [DNS_RCODE_NOTIMP] = "NOTIMP", + [DNS_RCODE_REFUSED] = "REFUSED", + [DNS_RCODE_YXDOMAIN] = "YXDOMAIN", + [DNS_RCODE_YXRRSET] = "YRRSET", + [DNS_RCODE_NXRRSET] = "NXRRSET", + [DNS_RCODE_NOTAUTH] = "NOTAUTH", + [DNS_RCODE_NOTZONE] = "NOTZONE", + [DNS_RCODE_BADVERS] = "BADVERS", + [DNS_RCODE_BADKEY] = "BADKEY", + [DNS_RCODE_BADTIME] = "BADTIME", + [DNS_RCODE_BADMODE] = "BADMODE", + [DNS_RCODE_BADNAME] = "BADNAME", + [DNS_RCODE_BADALG] = "BADALG", + [DNS_RCODE_BADTRUNC] = "BADTRUNC", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int); + +static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { + [DNS_PROTOCOL_DNS] = "dns", + [DNS_PROTOCOL_MDNS] = "mdns", + [DNS_PROTOCOL_LLMNR] = "llmnr", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); diff --git a/src/grp-resolve/libbasic-dns/resolved-dns-packet.h b/src/grp-resolve/libbasic-dns/resolved-dns-packet.h new file mode 100644 index 0000000000..a9e95ebd75 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-packet.h @@ -0,0 +1,302 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include +#include + +#include "basic/hashmap.h" +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +typedef struct DnsPacket DnsPacket; +typedef struct DnsPacketHeader DnsPacketHeader; + +#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, + DNS_PROTOCOL_MDNS, + DNS_PROTOCOL_LLMNR, + _DNS_PROTOCOL_MAX, + _DNS_PROTOCOL_INVALID = -1 +} DnsProtocol; + +struct DnsPacketHeader { + uint16_t id; + be16_t flags; + be16_t qdcount; + be16_t ancount; + be16_t nscount; + be16_t arcount; +}; + +#define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader) +#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr)) + +/* The various DNS protocols deviate in how large a packet can grow, + but the TCP transport has a 16bit size field, hence that appears to + be the absolute maximum. */ +#define DNS_PACKET_SIZE_MAX 0xFFFF + +/* 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 { + int n_ref; + DnsProtocol protocol; + 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; + int family, ipproto; + union in_addr_union sender, destination; + uint16_t sender_port, destination_port; + uint32_t ttl; + + /* 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) { + if (_unlikely_(!p)) + return NULL; + + if (p->_data) + return p->_data; + + return ((uint8_t*) p) + ALIGN(sizeof(DnsPacket)); +} + +#define DNS_PACKET_HEADER(p) ((DnsPacketHeader*) DNS_PACKET_DATA(p)) +#define DNS_PACKET_ID(p) DNS_PACKET_HEADER(p)->id +#define DNS_PACKET_QR(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 15) & 1) +#define DNS_PACKET_OPCODE(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 11) & 15) +#define DNS_PACKET_AA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1) +#define DNS_PACKET_TC(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 9) & 1) +#define DNS_PACKET_RD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1) +#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_FLAG_TC (UINT16_C(1) << 9) + +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) & 0xF); +} + +static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) { + + /* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */ + + if (p->opt) + return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class); + + return DNS_PACKET_UNICAST_SIZE_MAX; +} + +static inline bool DNS_PACKET_DO(DnsPacket *p) { + if (!p->opt) + return false; + + return !!(p->opt->ttl & (1U << 15)); +} + +static inline bool DNS_PACKET_VERSION_SUPPORTED(DnsPacket *p) { + /* Returns true if this packet is in a version we support. Which means either non-EDNS or EDNS(0), but not EDNS + * of any newer versions */ + + if (!p->opt) + return true; + + return DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(p->opt); +} + +/* LLMNR defines some bits differently */ +#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p) +#define DNS_PACKET_LLMNR_T(p) DNS_PACKET_RD(p) + +#define DNS_PACKET_QDCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->qdcount) +#define DNS_PACKET_ANCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->ancount) +#define DNS_PACKET_NSCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->nscount) +#define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount) + +#define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \ + (((uint16_t) !!(qr) << 15) | \ + ((uint16_t) ((opcode) & 15) << 11) | \ + ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \ + ((uint16_t) !!(tc) << 9) | \ + ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \ + ((uint16_t) !!(ra) << 7) | \ + ((uint16_t) !!(ad) << 5) | \ + ((uint16_t) !!(cd) << 4) | \ + ((uint16_t) ((rcode) & 15))) + +static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { + return + (unsigned) DNS_PACKET_ANCOUNT(p) + + (unsigned) DNS_PACKET_NSCOUNT(p) + + (unsigned) DNS_PACKET_ARCOUNT(p); +} + +int dns_packet_new(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); + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref); + +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_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, size_t *rdata_start); +int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start); +int dns_packet_append_question(DnsPacket *p, DnsQuestion *q); +int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a); + +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); +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_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, + DNS_RCODE_SERVFAIL = 2, + DNS_RCODE_NXDOMAIN = 3, + DNS_RCODE_NOTIMP = 4, + DNS_RCODE_REFUSED = 5, + DNS_RCODE_YXDOMAIN = 6, + DNS_RCODE_YXRRSET = 7, + DNS_RCODE_NXRRSET = 8, + DNS_RCODE_NOTAUTH = 9, + DNS_RCODE_NOTZONE = 10, + DNS_RCODE_BADVERS = 16, + DNS_RCODE_BADSIG = 16, /* duplicate value! */ + DNS_RCODE_BADKEY = 17, + DNS_RCODE_BADTIME = 18, + DNS_RCODE_BADMODE = 19, + DNS_RCODE_BADNAME = 20, + DNS_RCODE_BADALG = 21, + DNS_RCODE_BADTRUNC = 22, + _DNS_RCODE_MAX_DEFINED, + _DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */ +}; + +const char* dns_rcode_to_string(int i) _const_; +int dns_rcode_from_string(const char *s) _pure_; + +const char* dns_protocol_to_string(DnsProtocol p) _const_; +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 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 uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) { + uint64_t f; + + /* Converts a protocol + family into a flags field as used in queries and responses */ + + f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0; + + switch (protocol) { + case DNS_PROTOCOL_DNS: + return f|SD_RESOLVED_DNS; + + case DNS_PROTOCOL_LLMNR: + return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); + + case DNS_PROTOCOL_MDNS: + return f|(family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4); + + default: + return f; + } +} diff --git a/src/grp-resolve/libbasic-dns/resolved-dns-question.c b/src/grp-resolve/libbasic-dns/resolved-dns-question.c new file mode 100644 index 0000000000..ee53dbff9d --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-question.c @@ -0,0 +1,469 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "shared/dns-domain.h" + +#include "dns-type.h" +#include "resolved-dns-question.h" + +DnsQuestion *dns_question_new(unsigned n) { + DnsQuestion *q; + + assert(n > 0); + + q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n); + if (!q) + return NULL; + + q->n_ref = 1; + q->n_allocated = n; + + return q; +} + +DnsQuestion *dns_question_ref(DnsQuestion *q) { + if (!q) + return NULL; + + assert(q->n_ref > 0); + q->n_ref++; + return q; +} + +DnsQuestion *dns_question_unref(DnsQuestion *q) { + if (!q) + return NULL; + + assert(q->n_ref > 0); + + if (q->n_ref == 1) { + unsigned i; + + for (i = 0; i < q->n_keys; i++) + dns_resource_key_unref(q->keys[i]); + free(q); + } else + q->n_ref--; + + return NULL; +} + +int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { + unsigned i; + int r; + + assert(key); + + if (!q) + return -ENOSPC; + + for (i = 0; i < q->n_keys; i++) { + r = dns_resource_key_equal(q->keys[i], key); + if (r < 0) + return r; + if (r > 0) + return 0; + } + + if (q->n_keys >= q->n_allocated) + return -ENOSPC; + + q->keys[q->n_keys++] = dns_resource_key_ref(key); + return 0; +} + +int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { + unsigned i; + int r; + + assert(rr); + + if (!q) + return 0; + + for (i = 0; i < q->n_keys; i++) { + r = dns_resource_key_match_rr(q->keys[i], rr, search_domain); + if (r != 0) + return r; + } + + return 0; +} + +int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { + unsigned i; + int r; + + assert(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++) { + /* 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; + } + + return 0; +} + +int dns_question_is_valid_for_query(DnsQuestion *q) { + const char *name; + unsigned i; + int r; + + if (!q) + return 0; + + if (q->n_keys <= 0) + return 0; + + if (q->n_keys > 65535) + return 0; + + name = dns_resource_key_name(q->keys[0]); + if (!name) + return 0; + + /* Check that all keys in this question bear the same name */ + for (i = 0; i < q->n_keys; i++) { + assert(q->keys[i]); + + if (i > 0) { + r = dns_name_equal(dns_resource_key_name(q->keys[i]), name); + if (r <= 0) + return r; + } + + if (!dns_type_is_valid_query(q->keys[i]->type)) + return 0; + } + + return 1; +} + +int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) { + unsigned j; + int r; + + assert(k); + + if (!a) + return 0; + + for (j = 0; j < a->n_keys; j++) { + r = dns_resource_key_equal(a->keys[j], k); + if (r != 0) + return r; + } + + return 0; +} + +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) + return a->n_keys == 0; + + /* Checks if all keys in a are also contained b, and vice versa */ + + for (j = 0; j < a->n_keys; j++) { + r = dns_question_contains(b, a->keys[j]); + if (r <= 0) + return r; + } + + for (j = 0; j < b->n_keys; j++) { + r = dns_question_contains(a, b->keys[j]); + if (r <= 0) + return r; + } + + return 1; +} + +int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) { + _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; + DnsResourceKey *key; + bool same = true; + int r; + + assert(cname); + assert(ret); + assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)); + + if (dns_question_size(q) <= 0) { + *ret = NULL; + return 0; + } + + 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; + + if (r == 0) { + same = false; + break; + } + } + + /* Fully the same, indicate we didn't do a thing */ + if (same) { + *ret = NULL; + return 0; + } + + n = dns_question_new(q->n_keys); + if (!n) + return -ENOMEM; + + /* Create a new question, and patch in the new name */ + DNS_QUESTION_FOREACH(key, q) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; + + k = dns_resource_key_new_redirect(key, cname); + if (!k) + return -ENOMEM; + + r = dns_question_add(n, k); + if (r < 0) + return r; + } + + *ret = n; + n = NULL; + + 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/grp-resolve/libbasic-dns/resolved-dns-question.h b/src/grp-resolve/libbasic-dns/resolved-dns-question.h new file mode 100644 index 0000000000..fe47a6bd9e --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-question.h @@ -0,0 +1,74 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" + +typedef struct DnsQuestion DnsQuestion; + +#include "resolved-dns-rr.h" + +/* A simple array of resource keys */ + +struct DnsQuestion { + unsigned n_ref; + unsigned n_keys, n_allocated; + DnsResourceKey* keys[0]; +}; + +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, 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; +} + +static inline bool dns_question_isempty(DnsQuestion *q) { + return dns_question_size(q) <= 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/grp-resolve/libbasic-dns/resolved-dns-rr.c b/src/grp-resolve/libbasic-dns/resolved-dns-rr.c new file mode 100644 index 0000000000..73ca3c7fb9 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-rr.c @@ -0,0 +1,1840 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/hexdecoct.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "shared/dns-domain.h" + +#include "dns-type.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-rr.h" + +DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) { + DnsResourceKey *k; + size_t l; + + assert(name); + + l = strlen(name); + k = malloc0(sizeof(DnsResourceKey) + l + 1); + if (!k) + return NULL; + + k->n_ref = 1; + k->class = class; + k->type = type; + + strcpy((char*) k + sizeof(DnsResourceKey), name); + + return k; +} + +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; + + 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; + } +} + +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(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; + + 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) { + DnsResourceKey *k; + + assert(name); + + k = new0(DnsResourceKey, 1); + if (!k) + return NULL; + + k->n_ref = 1; + k->class = class; + k->type = type; + k->_name = name; + + return k; +} + +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++; + + return k; +} + +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) { + free(k->_name); + free(k); + } else + k->n_ref--; + + return NULL; +} + +const char* dns_resource_key_name(const DnsResourceKey *key) { + const char *name; + + if (!key) + return NULL; + + if (key->_name) + name = key->_name; + else + name = (char*) key + sizeof(DnsResourceKey); + + if (dns_name_is_root(name)) + return "."; + else + return name; +} + +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; + + if (a->class != b->class) + return 0; + + if (a->type != b->type) + return 0; + + return 1; +} + +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; + + 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_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) { + int r; + + assert(key); + assert(cname); + + if (cname->class != key->class && key->class != DNS_CLASS_ANY) + return 0; + + 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; + + 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; +} + +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; + + assert(k); + + 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) { + const DnsResourceKey *x = a, *y = b; + int ret; + + ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y)); + if (ret != 0) + return ret; + + if (x->type < y->type) + return -1; + if (x->type > y->type) + return 1; + + if (x->class < y->class) + return -1; + if (x->class > y->class) + return 1; + + return 0; +} + +const struct hash_ops dns_resource_key_hash_ops = { + .hash = dns_resource_key_hash_func, + .compare = dns_resource_key_compare_func +}; + +char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) { + const char *c, *t; + char *ans = buf; + + /* 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); + t = dns_type_to_string(key->type); + + snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u", + dns_resource_key_name(key), + c ?: "", c ? "" : "CLASS", c ? 0 : key->class, + t ?: "", t ? "" : "TYPE", t ? 0 : key->class); + + return ans; +} + +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; + + rr = new0(DnsResourceRecord, 1); + if (!rr) + return NULL; + + 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; +} + +DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + key = dns_resource_key_new(class, type, name); + if (!key) + return NULL; + + return dns_resource_record_new(key); +} + +DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) { + if (!rr) + return NULL; + + assert(rr->n_ref > 0); + rr->n_ref++; + + return rr; +} + +DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { + if (!rr) + return NULL; + + assert(rr->n_ref > 0); + + if (rr->n_ref > 1) { + rr->n_ref--; + return NULL; + } + + if (rr->key) { + switch(rr->key->type) { + + case DNS_TYPE_SRV: + free(rr->srv.name); + break; + + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + free(rr->ptr.name); + break; + + case DNS_TYPE_HINFO: + free(rr->hinfo.cpu); + free(rr->hinfo.os); + break; + + case DNS_TYPE_TXT: + case DNS_TYPE_SPF: + dns_txt_item_free_all(rr->txt.items); + break; + + case DNS_TYPE_SOA: + free(rr->soa.mname); + free(rr->soa.rname); + break; + + case DNS_TYPE_MX: + free(rr->mx.exchange); + break; + + case DNS_TYPE_DS: + free(rr->ds.digest); + break; + + case DNS_TYPE_SSHFP: + free(rr->sshfp.fingerprint); + break; + + case DNS_TYPE_DNSKEY: + free(rr->dnskey.key); + break; + + case DNS_TYPE_RRSIG: + free(rr->rrsig.signer); + free(rr->rrsig.signature); + break; + + case DNS_TYPE_NSEC: + free(rr->nsec.next_domain_name); + bitmap_free(rr->nsec.types); + break; + + case DNS_TYPE_NSEC3: + free(rr->nsec3.next_hashed_name); + free(rr->nsec3.salt); + bitmap_free(rr->nsec3.types); + break; + + case DNS_TYPE_LOC: + case DNS_TYPE_A: + case DNS_TYPE_AAAA: + break; + + case DNS_TYPE_TLSA: + free(rr->tlsa.data); + break; + + case DNS_TYPE_CAA: + free(rr->caa.tag); + free(rr->caa.value); + break; + + case DNS_TYPE_OPENPGPKEY: + default: + free(rr->generic.data); + } + + free(rr->wire_format); + dns_resource_key_unref(rr->key); + } + + free(rr->to_string); + free(rr); + + return NULL; +} + +int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_free_ char *ptr = NULL; + int r; + + assert(ret); + assert(address); + assert(hostname); + + r = dns_name_reverse(family, address, &ptr); + if (r < 0) + return r; + + key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr); + if (!key) + return -ENOMEM; + + ptr = NULL; + + rr = dns_resource_record_new(key); + if (!rr) + return -ENOMEM; + + rr->ptr.name = strdup(hostname); + if (!rr->ptr.name) + return -ENOMEM; + + *ret = rr; + rr = NULL; + + return 0; +} + +int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) { + DnsResourceRecord *rr; + + assert(ret); + assert(address); + assert(family); + + if (family == AF_INET) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); + if (!rr) + return -ENOMEM; + + rr->a.in_addr = address->in; + + } else if (family == AF_INET6) { + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = address->in6; + } else + return -EAFNOSUPPORT; + + *ret = rr; + + return 0; +} + +#define FIELD_EQUAL(a, b, field) \ + ((a).field ## _size == (b).field ## _size && \ + memcmp((a).field, (b).field, (a).field ## _size) == 0) + +int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { + int r; + + assert(a); + assert(b); + + if (a == b) + return 1; + + r = dns_resource_key_equal(a->key, b->key); + if (r <= 0) + return r; + + if (a->unparseable != b->unparseable) + return 0; + + switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) { + + case DNS_TYPE_SRV: + r = dns_name_equal(a->srv.name, b->srv.name); + if (r <= 0) + return r; + + return a->srv.priority == b->srv.priority && + a->srv.weight == b->srv.weight && + a->srv.port == b->srv.port; + + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + return dns_name_equal(a->ptr.name, b->ptr.name); + + case DNS_TYPE_HINFO: + return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) && + strcaseeq(a->hinfo.os, b->hinfo.os); + + case DNS_TYPE_SPF: /* exactly the same as TXT */ + case DNS_TYPE_TXT: + 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; + + case DNS_TYPE_AAAA: + return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0; + + case DNS_TYPE_SOA: + r = dns_name_equal(a->soa.mname, b->soa.mname); + if (r <= 0) + return r; + r = dns_name_equal(a->soa.rname, b->soa.rname); + if (r <= 0) + return r; + + return a->soa.serial == b->soa.serial && + a->soa.refresh == b->soa.refresh && + a->soa.retry == b->soa.retry && + a->soa.expire == b->soa.expire && + a->soa.minimum == b->soa.minimum; + + case DNS_TYPE_MX: + if (a->mx.priority != b->mx.priority) + return 0; + + return dns_name_equal(a->mx.exchange, b->mx.exchange); + + case DNS_TYPE_LOC: + assert(a->loc.version == b->loc.version); + + return a->loc.size == b->loc.size && + a->loc.horiz_pre == b->loc.horiz_pre && + a->loc.vert_pre == b->loc.vert_pre && + a->loc.latitude == b->loc.latitude && + a->loc.longitude == b->loc.longitude && + a->loc.altitude == b->loc.altitude; + + case DNS_TYPE_DS: + return a->ds.key_tag == b->ds.key_tag && + a->ds.algorithm == b->ds.algorithm && + a->ds.digest_type == b->ds.digest_type && + FIELD_EQUAL(a->ds, b->ds, digest); + + case DNS_TYPE_SSHFP: + return a->sshfp.algorithm == b->sshfp.algorithm && + a->sshfp.fptype == b->sshfp.fptype && + FIELD_EQUAL(a->sshfp, b->sshfp, fingerprint); + + case DNS_TYPE_DNSKEY: + return a->dnskey.flags == b->dnskey.flags && + a->dnskey.protocol == b->dnskey.protocol && + a->dnskey.algorithm == b->dnskey.algorithm && + FIELD_EQUAL(a->dnskey, b->dnskey, key); + + case DNS_TYPE_RRSIG: + /* do the fast comparisons first */ + return a->rrsig.type_covered == b->rrsig.type_covered && + a->rrsig.algorithm == b->rrsig.algorithm && + a->rrsig.labels == b->rrsig.labels && + a->rrsig.original_ttl == b->rrsig.original_ttl && + a->rrsig.expiration == b->rrsig.expiration && + a->rrsig.inception == b->rrsig.inception && + a->rrsig.key_tag == b->rrsig.key_tag && + FIELD_EQUAL(a->rrsig, b->rrsig, signature) && + dns_name_equal(a->rrsig.signer, b->rrsig.signer); + + case DNS_TYPE_NSEC: + return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) && + bitmap_equal(a->nsec.types, b->nsec.types); + + case DNS_TYPE_NSEC3: + return a->nsec3.algorithm == b->nsec3.algorithm && + a->nsec3.flags == b->nsec3.flags && + a->nsec3.iterations == b->nsec3.iterations && + FIELD_EQUAL(a->nsec3, b->nsec3, salt) && + FIELD_EQUAL(a->nsec3, b->nsec3, next_hashed_name) && + bitmap_equal(a->nsec3.types, b->nsec3.types); + + case DNS_TYPE_TLSA: + return a->tlsa.cert_usage == b->tlsa.cert_usage && + a->tlsa.selector == b->tlsa.selector && + a->tlsa.matching_type == b->tlsa.matching_type && + FIELD_EQUAL(a->tlsa, b->tlsa, data); + + case DNS_TYPE_CAA: + return a->caa.flags == b->caa.flags && + streq(a->caa.tag, b->caa.tag) && + FIELD_EQUAL(a->caa, b->caa, value); + + case DNS_TYPE_OPENPGPKEY: + default: + return FIELD_EQUAL(a->generic, b->generic, data); + } +} + +static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude, + uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) { + char *s; + char NS = latitude >= 1U<<31 ? 'N' : 'S'; + char EW = longitude >= 1U<<31 ? 'E' : 'W'; + + int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude); + int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude); + double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude); + double siz = (size >> 4) * exp10((double) (size & 0xF)); + double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF)); + double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF)); + + if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm", + (lat / 60000 / 60), + (lat / 60000) % 60, + (lat % 60000) / 1000., + NS, + (lon / 60000 / 60), + (lon / 60000) % 60, + (lon % 60000) / 1000., + EW, + alt / 100., + siz / 100., + hor / 100., + ver / 100.) < 0) + return NULL; + + return s; +} + +static int format_timestamp_dns(char *buf, size_t l, time_t sec) { + struct tm tm; + + assert(buf); + assert(l > strlen("YYYYMMDDHHmmSS")); + + if (!gmtime_r(&sec, &tm)) + return -EINVAL; + + if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0) + return -EINVAL; + + return 0; +} + +static char *format_types(Bitmap *types) { + _cleanup_strv_free_ char **strv = NULL; + _cleanup_free_ char *str = NULL; + Iterator i; + unsigned type; + int r; + + BITMAP_FOREACH(type, types, i) { + if (dns_type_to_string(type)) { + r = strv_extend(&strv, dns_type_to_string(type)); + if (r < 0) + return NULL; + } else { + char *t; + + r = asprintf(&t, "TYPE%u", type); + if (r < 0) + return NULL; + + r = strv_consume(&strv, t); + if (r < 0) + return NULL; + } + } + + str = strv_join(strv, " "); + if (!str) + return NULL; + + return strjoin("( ", str, " )", NULL); +} + +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 *t = NULL; + char *s, k[DNS_RESOURCE_KEY_STRING_MAX]; + int r; + + assert(rr); + + if (rr->to_string) + return rr->to_string; + + dns_resource_key_to_string(rr->key, k, sizeof(k)); + + switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + + case DNS_TYPE_SRV: + r = asprintf(&s, "%s %u %u %u %s", + k, + rr->srv.priority, + rr->srv.weight, + rr->srv.port, + strna(rr->srv.name)); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + s = strjoin(k, " ", rr->ptr.name, NULL); + if (!s) + return NULL; + + break; + + case DNS_TYPE_HINFO: + s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL); + if (!s) + return NULL; + break; + + case DNS_TYPE_SPF: /* exactly the same as TXT */ + case DNS_TYPE_TXT: + t = format_txt(rr->txt.items); + if (!t) + return NULL; + + s = strjoin(k, " ", t, NULL); + if (!s) + return NULL; + break; + + case DNS_TYPE_A: { + _cleanup_free_ char *x = NULL; + + r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x); + if (r < 0) + return NULL; + + s = strjoin(k, " ", x, NULL); + if (!s) + 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 NULL; + + s = strjoin(k, " ", t, NULL); + if (!s) + return NULL; + break; + + case DNS_TYPE_SOA: + r = asprintf(&s, "%s %s %s %u %u %u %u %u", + k, + strna(rr->soa.mname), + strna(rr->soa.rname), + rr->soa.serial, + rr->soa.refresh, + rr->soa.retry, + rr->soa.expire, + rr->soa.minimum); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_MX: + r = asprintf(&s, "%s %u %s", + k, + rr->mx.priority, + rr->mx.exchange); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_LOC: + assert(rr->loc.version == 0); + + t = format_location(rr->loc.latitude, + rr->loc.longitude, + rr->loc.altitude, + rr->loc.size, + rr->loc.horiz_pre, + rr->loc.vert_pre); + if (!t) + return NULL; + + s = strjoin(k, " ", t, NULL); + if (!s) + return NULL; + break; + + case DNS_TYPE_DS: + t = hexmem(rr->ds.digest, rr->ds.digest_size); + if (!t) + return NULL; + + r = asprintf(&s, "%s %u %u %u %s", + k, + rr->ds.key_tag, + rr->ds.algorithm, + rr->ds.digest_type, + t); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_SSHFP: + t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); + if (!t) + return NULL; + + r = asprintf(&s, "%s %u %u %s", + k, + rr->sshfp.algorithm, + rr->sshfp.fptype, + t); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_DNSKEY: { + _cleanup_free_ char *alg = NULL; + char *ss; + int n; + uint16_t key_tag; + + key_tag = dnssec_keytag(rr, true); + + r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg); + if (r < 0) + return NULL; + + r = asprintf(&s, "%s %u %u %s %n", + k, + rr->dnskey.flags, + rr->dnskey.protocol, + alg, + &n); + if (r < 0) + return NULL; + + r = base64_append(&s, n, + rr->dnskey.key, rr->dnskey.key_size, + 8, columns()); + if (r < 0) + return NULL; + + r = asprintf(&ss, "%s\n" + " -- Flags:%s%s%s\n" + " -- Key tag: %u", + s, + rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "", + rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "", + rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "", + key_tag); + if (r < 0) + return NULL; + free(s); + s = ss; + + break; + } + + case DNS_TYPE_RRSIG: { + _cleanup_free_ char *alg = NULL; + char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1]; + const char *type; + int n; + + type = dns_type_to_string(rr->rrsig.type_covered); + + r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg); + if (r < 0) + return NULL; + + r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration); + if (r < 0) + return NULL; + + r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception); + if (r < 0) + return NULL; + + /* TYPE?? follows + * http://tools.ietf.org/html/rfc3597#section-5 */ + + r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %n", + k, + type ?: "TYPE", + type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered, + alg, + rr->rrsig.labels, + rr->rrsig.original_ttl, + expiration, + inception, + rr->rrsig.key_tag, + rr->rrsig.signer, + &n); + if (r < 0) + return NULL; + + r = base64_append(&s, n, + rr->rrsig.signature, rr->rrsig.signature_size, + 8, columns()); + if (r < 0) + return NULL; + + break; + } + + case DNS_TYPE_NSEC: + t = format_types(rr->nsec.types); + if (!t) + return NULL; + + r = asprintf(&s, "%s %s %s", + k, + rr->nsec.next_domain_name, + t); + if (r < 0) + return NULL; + break; + + case DNS_TYPE_NSEC3: { + _cleanup_free_ char *salt = NULL, *hash = NULL; + + if (rr->nsec3.salt_size > 0) { + salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size); + if (!salt) + return NULL; + } + + hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false); + if (!hash) + return NULL; + + t = format_types(rr->nsec3.types); + if (!t) + return NULL; + + r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s", + k, + rr->nsec3.algorithm, + rr->nsec3.flags, + rr->nsec3.iterations, + rr->nsec3.salt_size > 0 ? salt : "-", + hash, + t); + if (r < 0) + return NULL; + + break; + } + + case DNS_TYPE_TLSA: { + const char *cert_usage, *selector, *matching_type; + + cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage); + selector = tlsa_selector_to_string(rr->tlsa.selector); + matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type); + + t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); + if (!t) + return NULL; + + r = asprintf(&s, + "%s %u %u %u %s\n" + " -- Cert. usage: %s\n" + " -- Selector: %s\n" + " -- Matching type: %s", + k, + rr->tlsa.cert_usage, + rr->tlsa.selector, + rr->tlsa.matching_type, + t, + cert_usage, + selector, + matching_type); + if (r < 0) + return NULL; + + break; + } + + case DNS_TYPE_CAA: { + _cleanup_free_ char *value; + + value = octescape(rr->caa.value, rr->caa.value_size); + if (!value) + return NULL; + + r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u", + k, + rr->caa.flags, + rr->caa.tag, + value, + rr->caa.flags ? "\n -- Flags:" : "", + rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL); + if (r < 0) + return NULL; + + break; + } + + case DNS_TYPE_OPENPGPKEY: { + int n; + + r = asprintf(&s, "%s %n", + k, + &n); + if (r < 0) + return NULL; + + r = base64_append(&s, n, + rr->generic.data, rr->generic.data_size, + 8, columns()); + if (r < 0) + return NULL; + break; + } + + default: + t = hexmem(rr->generic.data, rr->generic.data_size); + if (!t) + return NULL; + + /* Format as documented in RFC 3597, Section 5 */ + r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t); + if (r < 0) + return NULL; + break; + } + + rr->to_string = s; + return s; +} + +ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) { + assert(rr); + assert(out); + + switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + case DNS_TYPE_SRV: + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + case DNS_TYPE_HINFO: + case DNS_TYPE_SPF: + case DNS_TYPE_TXT: + case DNS_TYPE_A: + case DNS_TYPE_AAAA: + case DNS_TYPE_SOA: + case DNS_TYPE_MX: + case DNS_TYPE_LOC: + case DNS_TYPE_DS: + case DNS_TYPE_DNSKEY: + case DNS_TYPE_RRSIG: + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + return -EINVAL; + + case DNS_TYPE_SSHFP: + *out = rr->sshfp.fingerprint; + return rr->sshfp.fingerprint_size; + + case DNS_TYPE_TLSA: + *out = rr->tlsa.data; + return rr->tlsa.data_size; + + + case DNS_TYPE_OPENPGPKEY: + default: + *out = rr->generic.data; + return rr->generic.data_size; + } +} + +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; +} + +int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) { + const char *n; + int r; + + assert(rr); + assert(ret); + + /* Returns the RRset's signer, if it is known. */ + + if (rr->n_skip_labels_signer == (unsigned) -1) + return -ENODATA; + + 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_resource_record_source(DnsResourceRecord *rr, const char **ret) { + const char *n; + int r; + + 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; +} + +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; + + case DNS_TYPE_TLSA: + siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state); + siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state); + siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state); + siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state); + break; + + case DNS_TYPE_CAA: + siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state); + string_hash_func(rr->caa.tag, state); + siphash24_compress(rr->caa.value, rr->caa.value_size, state); + break; + + case DNS_TYPE_OPENPGPKEY: + default: + siphash24_compress(rr->generic.data, rr->generic.data_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 ordering, 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, +}; + +DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL; + DnsResourceRecord *t; + + assert(rr); + + copy = dns_resource_record_new(rr->key); + if (!copy) + return NULL; + + copy->ttl = rr->ttl; + copy->expiry = rr->expiry; + copy->n_skip_labels_signer = rr->n_skip_labels_signer; + copy->n_skip_labels_source = rr->n_skip_labels_source; + copy->unparseable = rr->unparseable; + + switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + + case DNS_TYPE_SRV: + copy->srv.priority = rr->srv.priority; + copy->srv.weight = rr->srv.weight; + copy->srv.port = rr->srv.port; + copy->srv.name = strdup(rr->srv.name); + if (!copy->srv.name) + return NULL; + break; + + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + copy->ptr.name = strdup(rr->ptr.name); + if (!copy->ptr.name) + return NULL; + break; + + case DNS_TYPE_HINFO: + copy->hinfo.cpu = strdup(rr->hinfo.cpu); + if (!copy->hinfo.cpu) + return NULL; + + copy->hinfo.os = strdup(rr->hinfo.os); + if(!copy->hinfo.os) + return NULL; + break; + + case DNS_TYPE_TXT: + case DNS_TYPE_SPF: + copy->txt.items = dns_txt_item_copy(rr->txt.items); + if (!copy->txt.items) + return NULL; + break; + + case DNS_TYPE_A: + copy->a = rr->a; + break; + + case DNS_TYPE_AAAA: + copy->aaaa = rr->aaaa; + break; + + case DNS_TYPE_SOA: + copy->soa.mname = strdup(rr->soa.mname); + if (!copy->soa.mname) + return NULL; + copy->soa.rname = strdup(rr->soa.rname); + if (!copy->soa.rname) + return NULL; + copy->soa.serial = rr->soa.serial; + copy->soa.refresh = rr->soa.refresh; + copy->soa.retry = rr->soa.retry; + copy->soa.expire = rr->soa.expire; + copy->soa.minimum = rr->soa.minimum; + break; + + case DNS_TYPE_MX: + copy->mx.priority = rr->mx.priority; + copy->mx.exchange = strdup(rr->mx.exchange); + if (!copy->mx.exchange) + return NULL; + break; + + case DNS_TYPE_LOC: + copy->loc = rr->loc; + break; + + case DNS_TYPE_SSHFP: + copy->sshfp.algorithm = rr->sshfp.algorithm; + copy->sshfp.fptype = rr->sshfp.fptype; + copy->sshfp.fingerprint = memdup(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); + if (!copy->sshfp.fingerprint) + return NULL; + copy->sshfp.fingerprint_size = rr->sshfp.fingerprint_size; + break; + + case DNS_TYPE_DNSKEY: + copy->dnskey.flags = rr->dnskey.flags; + copy->dnskey.protocol = rr->dnskey.protocol; + copy->dnskey.algorithm = rr->dnskey.algorithm; + copy->dnskey.key = memdup(rr->dnskey.key, rr->dnskey.key_size); + if (!copy->dnskey.key) + return NULL; + copy->dnskey.key_size = rr->dnskey.key_size; + break; + + case DNS_TYPE_RRSIG: + copy->rrsig.type_covered = rr->rrsig.type_covered; + copy->rrsig.algorithm = rr->rrsig.algorithm; + copy->rrsig.labels = rr->rrsig.labels; + copy->rrsig.original_ttl = rr->rrsig.original_ttl; + copy->rrsig.expiration = rr->rrsig.expiration; + copy->rrsig.inception = rr->rrsig.inception; + copy->rrsig.key_tag = rr->rrsig.key_tag; + copy->rrsig.signer = strdup(rr->rrsig.signer); + if (!copy->rrsig.signer) + return NULL; + copy->rrsig.signature = memdup(rr->rrsig.signature, rr->rrsig.signature_size); + if (!copy->rrsig.signature) + return NULL; + copy->rrsig.signature_size = rr->rrsig.signature_size; + break; + + case DNS_TYPE_NSEC: + copy->nsec.next_domain_name = strdup(rr->nsec.next_domain_name); + if (!copy->nsec.next_domain_name) + return NULL; + copy->nsec.types = bitmap_copy(rr->nsec.types); + if (!copy->nsec.types) + return NULL; + break; + + case DNS_TYPE_DS: + copy->ds.key_tag = rr->ds.key_tag; + copy->ds.algorithm = rr->ds.algorithm; + copy->ds.digest_type = rr->ds.digest_type; + copy->ds.digest = memdup(rr->ds.digest, rr->ds.digest_size); + if (!copy->ds.digest) + return NULL; + copy->ds.digest_size = rr->ds.digest_size; + break; + + case DNS_TYPE_NSEC3: + copy->nsec3.algorithm = rr->nsec3.algorithm; + copy->nsec3.flags = rr->nsec3.flags; + copy->nsec3.iterations = rr->nsec3.iterations; + copy->nsec3.salt = memdup(rr->nsec3.salt, rr->nsec3.salt_size); + if (!copy->nsec3.salt) + return NULL; + copy->nsec3.salt_size = rr->nsec3.salt_size; + copy->nsec3.next_hashed_name = memdup(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size); + if (!copy->nsec3.next_hashed_name_size) + return NULL; + copy->nsec3.next_hashed_name_size = rr->nsec3.next_hashed_name_size; + copy->nsec3.types = bitmap_copy(rr->nsec3.types); + if (!copy->nsec3.types) + return NULL; + break; + + case DNS_TYPE_TLSA: + copy->tlsa.cert_usage = rr->tlsa.cert_usage; + copy->tlsa.selector = rr->tlsa.selector; + copy->tlsa.matching_type = rr->tlsa.matching_type; + copy->tlsa.data = memdup(rr->tlsa.data, rr->tlsa.data_size); + if (!copy->tlsa.data) + return NULL; + copy->tlsa.data_size = rr->tlsa.data_size; + break; + + case DNS_TYPE_CAA: + copy->caa.flags = rr->caa.flags; + copy->caa.tag = strdup(rr->caa.tag); + if (!copy->caa.tag) + return NULL; + copy->caa.value = memdup(rr->caa.value, rr->caa.value_size); + if (!copy->caa.value) + return NULL; + copy->caa.value_size = rr->caa.value_size; + break; + + case DNS_TYPE_OPT: + default: + copy->generic.data = memdup(rr->generic.data, rr->generic.data_size); + if (!copy->generic.data) + return NULL; + copy->generic.data_size = rr->generic.data_size; + break; + } + + t = copy; + copy = NULL; + + return t; +} + +int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) { + DnsResourceRecord *old_rr, *new_rr; + uint32_t new_ttl; + + assert(rr); + old_rr = *rr; + + if (old_rr->key->type == DNS_TYPE_OPT) + return -EINVAL; + + new_ttl = MIN(old_rr->ttl, max_ttl); + if (new_ttl == old_rr->ttl) + return 0; + + if (old_rr->n_ref == 1) { + /* Patch in place */ + old_rr->ttl = new_ttl; + return 1; + } + + new_rr = dns_resource_record_copy(old_rr); + if (!new_rr) + return -ENOMEM; + + new_rr->ttl = new_ttl; + + dns_resource_record_unref(*rr); + *rr = new_rr; + + return 1; +} + +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); +} + +DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) { + DnsTxtItem *i, *copy = NULL, *end = NULL; + + LIST_FOREACH(items, i, first) { + DnsTxtItem *j; + + j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1); + if (!j) { + dns_txt_item_free_all(copy); + return NULL; + } + + LIST_INSERT_AFTER(items, copy, end, j); + end = j; + } + + return copy; +} + +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/grp-resolve/libbasic-dns/resolved-dns-rr.h b/src/grp-resolve/libbasic-dns/resolved-dns-rr.h new file mode 100644 index 0000000000..13240e57ed --- /dev/null +++ b/src/grp-resolve/libbasic-dns/resolved-dns-rr.h @@ -0,0 +1,354 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include + +#include "basic/bitmap.h" +#include "basic/hashmap.h" +#include "basic/in-addr-util.h" +#include "basic/list.h" +#include "basic/string-util.h" + +#include "dns-type.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 +}; + +/* DNSSEC digest identifiers, see + * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ +enum { + 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 -1) for const keys, see below */ + uint16_t class, type; + char *_name; /* don't access directly, 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; + 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 data_size; + } generic, opt; + + struct { + uint16_t priority; + uint16_t weight; + uint16_t port; + char *name; + } srv; + + struct { + char *name; + } ptr, ns, cname, dname; + + struct { + char *cpu; + char *os; + } hinfo; + + struct { + DnsTxtItem *items; + } txt, spf; + + struct { + struct in_addr in_addr; + } a; + + struct { + struct in6_addr in6_addr; + } aaaa; + + struct { + char *mname; + char *rname; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; + } soa; + + struct { + uint16_t priority; + char *exchange; + } mx; + + /* https://tools.ietf.org/html/rfc1876 */ + struct { + uint8_t version; + uint8_t size; + uint8_t horiz_pre; + uint8_t vert_pre; + uint32_t latitude; + uint32_t longitude; + uint32_t altitude; + } loc; + + /* https://tools.ietf.org/html/rfc4255#section-3.1 */ + struct { + uint8_t algorithm; + uint8_t fptype; + void *fingerprint; + size_t fingerprint_size; + } sshfp; + + /* http://tools.ietf.org/html/rfc4034#section-2.1 */ + struct { + uint16_t flags; + uint8_t protocol; + uint8_t algorithm; + void* key; + size_t key_size; + } dnskey; + + /* http://tools.ietf.org/html/rfc4034#section-3.1 */ + struct { + uint16_t type_covered; + uint8_t algorithm; + uint8_t labels; + uint32_t original_ttl; + uint32_t expiration; + uint32_t inception; + uint16_t key_tag; + char *signer; + void *signature; + 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; + uint16_t iterations; + void *salt; + size_t salt_size; + void *next_hashed_name; + size_t next_hashed_name_size; + Bitmap *types; + } nsec3; + + /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23 */ + struct { + uint8_t cert_usage; + uint8_t selector; + uint8_t matching_type; + void *data; + size_t data_size; + } tlsa; + + /* https://tools.ietf.org/html/rfc6844 */ + struct { + uint8_t flags; + char *tag; + void *value; + size_t value_size; + } caa; + }; +}; + +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; +} + +static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) { + assert(rr); + assert(rr->key->type == DNS_TYPE_OPT); + + return ((rr->ttl >> 16) & 0xFF) == 0; +} + +DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); +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); +const char* dns_resource_key_name(const 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, 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); + +/* _DNS_{CLASS,TYPE}_STRING_MAX include one byte for NUL, which we use for space instead below. + * DNS_HOSTNAME_MAX does not include the NUL byte, so we need to add 1. */ +#define DNS_RESOURCE_KEY_STRING_MAX (_DNS_CLASS_STRING_MAX + _DNS_TYPE_STRING_MAX + DNS_HOSTNAME_MAX + 1) + +char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size); +ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out); + +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); +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); +const char* dns_resource_record_to_string(DnsResourceRecord *rr); +DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr); +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); + +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); + +int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl); + +DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); +bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); +DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i); + +void dns_resource_record_hash_func(const void *i, struct siphash *state); + +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/grp-resolve/libbasic-dns/test-data/_443._tcp.fedoraproject.org.pkts b/src/grp-resolve/libbasic-dns/test-data/_443._tcp.fedoraproject.org.pkts new file mode 100644 index 0000000000..a383c6286d Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/_443._tcp.fedoraproject.org.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/_openpgpkey.fedoraproject.org.pkts b/src/grp-resolve/libbasic-dns/test-data/_openpgpkey.fedoraproject.org.pkts new file mode 100644 index 0000000000..15de02e997 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/_openpgpkey.fedoraproject.org.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/fake-caa.pkts b/src/grp-resolve/libbasic-dns/test-data/fake-caa.pkts new file mode 100644 index 0000000000..1c3ecc5491 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/fake-caa.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/fedoraproject.org.pkts b/src/grp-resolve/libbasic-dns/test-data/fedoraproject.org.pkts new file mode 100644 index 0000000000..17874844d9 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/fedoraproject.org.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/gandi.net.pkts b/src/grp-resolve/libbasic-dns/test-data/gandi.net.pkts new file mode 100644 index 0000000000..5ef51e0c8e Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/gandi.net.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/google.com.pkts b/src/grp-resolve/libbasic-dns/test-data/google.com.pkts new file mode 100644 index 0000000000..f98c4cd855 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/google.com.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/kyhwana.org.pkts b/src/grp-resolve/libbasic-dns/test-data/kyhwana.org.pkts new file mode 100644 index 0000000000..e28a725c9a Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/kyhwana.org.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/root.pkts b/src/grp-resolve/libbasic-dns/test-data/root.pkts new file mode 100644 index 0000000000..54ba668c75 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/root.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/grp-resolve/libbasic-dns/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts new file mode 100644 index 0000000000..a854249532 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/teamits.com.pkts b/src/grp-resolve/libbasic-dns/test-data/teamits.com.pkts new file mode 100644 index 0000000000..11deb39677 Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/teamits.com.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-data/zbyszek@fedoraproject.org.pkts b/src/grp-resolve/libbasic-dns/test-data/zbyszek@fedoraproject.org.pkts new file mode 100644 index 0000000000..f0a6f982df Binary files /dev/null and b/src/grp-resolve/libbasic-dns/test-data/zbyszek@fedoraproject.org.pkts differ diff --git a/src/grp-resolve/libbasic-dns/test-dns-packet.c b/src/grp-resolve/libbasic-dns/test-dns-packet.c new file mode 100644 index 0000000000..c9e210fa94 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/test-dns-packet.c @@ -0,0 +1,133 @@ +/*** + This file is part of systemd + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/glob-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unaligned.h" + +#include "resolved-dns-packet.h" +#include "resolved-dns-rr.h" + +#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1) + +static void verify_rr_copy(DnsResourceRecord *rr) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL; + const char *a, *b; + + assert_se(copy = dns_resource_record_copy(rr)); + assert_se(dns_resource_record_equal(copy, rr) > 0); + + assert_se(a = dns_resource_record_to_string(rr)); + assert_se(b = dns_resource_record_to_string(copy)); + + assert_se(streq(a, b)); +} + +static uint64_t hash(DnsResourceRecord *rr) { + struct siphash state; + + siphash24_init(&state, HASH_KEY.bytes); + dns_resource_record_hash_func(rr, &state); + return siphash24_finalize(&state); +} + +static void test_packet_from_file(const char* filename, bool canonical) { + _cleanup_free_ char *data = NULL; + size_t data_size, packet_size, offset; + + assert_se(read_full_file(filename, &data, &data_size) >= 0); + assert_se(data); + assert_se(data_size > 8); + + log_info("============== %s %s==============", filename, canonical ? "canonical " : ""); + + for (offset = 0; offset < data_size; offset += 8 + packet_size) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL; + const char *s, *s2; + uint64_t hash1, hash2; + + packet_size = unaligned_read_le64(data + offset); + assert_se(packet_size > 0); + assert_se(offset + 8 + packet_size <= data_size); + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0); + + assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0); + + verify_rr_copy(rr); + + s = dns_resource_record_to_string(rr); + assert_se(s); + puts(s); + + hash1 = hash(rr); + + assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0); + + assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0); + assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0); + + verify_rr_copy(rr); + + s2 = dns_resource_record_to_string(rr); + assert_se(s2); + assert_se(streq(s, s2)); + + hash2 = hash(rr); + assert_se(hash1 == hash2); + } +} + +int main(int argc, char **argv) { + int i, N; + _cleanup_globfree_ glob_t g = {}; + char **fnames; + + log_parse_environment(); + + if (argc >= 2) { + N = argc - 1; + fnames = argv + 1; + } else { + assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0); + N = g.gl_pathc; + fnames = g.gl_pathv; + } + + for (i = 0; i < N; i++) { + test_packet_from_file(fnames[i], false); + puts(""); + test_packet_from_file(fnames[i], true); + if (i + 1 < N) + puts(""); + } + + return EXIT_SUCCESS; +} diff --git a/src/grp-resolve/libbasic-dns/test-dnssec-complex.c b/src/grp-resolve/libbasic-dns/test-dnssec-complex.c new file mode 100644 index 0000000000..ef78cd1ea5 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/test-dnssec-complex.c @@ -0,0 +1,237 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "sd-bus/bus-common-errors.h" + +#include "dns-type.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); + +#ifdef 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/grp-resolve/libbasic-dns/test-dnssec.c b/src/grp-resolve/libbasic-dns/test-dnssec.c new file mode 100644 index 0000000000..1f05196d8e --- /dev/null +++ b/src/grp-resolve/libbasic-dns/test-dnssec.c @@ -0,0 +1,344 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/string-util.h" + +#include "resolved-dns-dnssec.h" +#include "resolved-dns-rr.h" + +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); +} + +#ifdef HAVE_GCRYPT + +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_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_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_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); +} + +#endif + +int main(int argc, char*argv[]) { + + test_dnssec_canonicalize(); + +#ifdef HAVE_GCRYPT + test_dnssec_verify_dns_key(); + test_dnssec_verify_rrset(); + test_dnssec_verify_rrset2(); + test_dnssec_nsec3_hash(); +#endif + + return 0; +} diff --git a/src/grp-resolve/libbasic-dns/test-resolve-tables.c b/src/grp-resolve/libbasic-dns/test-resolve-tables.c new file mode 100644 index 0000000000..0eaab70687 --- /dev/null +++ b/src/grp-resolve/libbasic-dns/test-resolve-tables.c @@ -0,0 +1,65 @@ +/*** + This file is part of systemd + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "shared/test-tables.h" + +#include "dns-type.h" + +int main(int argc, char **argv) { + uint16_t i; + + test_table_sparse(dns_type, DNS_TYPE); + + log_info("/* DNS_TYPE */"); + for (i = 0; i < _DNS_TYPE_MAX; i++) { + const char *s; + + s = dns_type_to_string(i); + assert_se(s == NULL || strlen(s) < _DNS_TYPE_STRING_MAX); + + if (s) + log_info("%-*s %s%s%s%s%s%s%s%s%s", + (int) _DNS_TYPE_STRING_MAX - 1, s, + dns_type_is_pseudo(i) ? "pseudo " : "", + dns_type_is_valid_query(i) ? "valid_query " : "", + dns_type_is_valid_rr(i) ? "is_valid_rr " : "", + dns_type_may_redirect(i) ? "may_redirect " : "", + dns_type_is_dnssec(i) ? "dnssec " : "", + dns_type_is_obsolete(i) ? "obsolete " : "", + dns_type_may_wildcard(i) ? "wildcard " : "", + dns_type_apex_only(i) ? "apex_only " : "", + dns_type_needs_authentication(i) ? "needs_authentication" : ""); + } + + log_info("/* DNS_CLASS */"); + for (i = 0; i < _DNS_CLASS_MAX; i++) { + const char *s; + + s = dns_class_to_string(i); + assert_se(s == NULL || strlen(s) < _DNS_CLASS_STRING_MAX); + + if (s) + log_info("%-*s %s%s", + (int) _DNS_CLASS_STRING_MAX - 1, s, + dns_class_is_pseudo(i) ? "is_pseudo " : "", + dns_class_is_valid_rr(i) ? "is_valid_rr " : ""); + } + + return EXIT_SUCCESS; +} diff --git a/src/grp-resolve/nss-resolve/Makefile b/src/grp-resolve/nss-resolve/Makefile new file mode 100644 index 0000000000..badbacdb3a --- /dev/null +++ b/src/grp-resolve/nss-resolve/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libnss_resolve_la_SOURCES = \ + src/nss-resolve/nss-resolve.sym \ + src/nss-resolve/nss-resolve.c + +libnss_resolve_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -shrext .so.2 \ + -Wl,--version-script=$(srcdir)/nss-resolve.sym + +libnss_resolve_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la \ + -ldl + +lib_LTLIBRARIES += \ + libnss_resolve.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-resolve/nss-resolve/nss-resolve.c b/src/grp-resolve/nss-resolve/nss-resolve.c new file mode 100644 index 0000000000..e316803a58 --- /dev/null +++ b/src/grp-resolve/nss-resolve/nss-resolve.c @@ -0,0 +1,675 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/nss-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" + +NSS_GETHOSTBYNAME_PROTOTYPES(resolve); +NSS_GETHOSTBYADDR_PROTOTYPES(resolve); + +#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) + +typedef void (*voidfunc_t)(void); + +static voidfunc_t find_fallback(const char *module, const char *symbol) { + void *dl; + + /* Try to find a fallback NSS module symbol */ + + dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE); + if (!dl) + return NULL; + + return dlsym(dl, symbol); +} + +static bool bus_error_shall_fallback(sd_bus_error *e) { + return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) || + sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) || + sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) || + sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED); +} + +static int count_addresses(sd_bus_message *m, int af, const char **canonical) { + int c = 0, r; + + assert(m); + assert(canonical); + + r = sd_bus_message_enter_container(m, 'a', "(iiay)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { + int family, ifindex; + + assert_cc(sizeof(int32_t) == sizeof(int)); + + r = sd_bus_message_read(m, "ii", &ifindex, &family); + if (r < 0) + return r; + + r = sd_bus_message_skip(m, "ay"); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + if (af != AF_UNSPEC && family != af) + continue; + + c++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = sd_bus_message_read(m, "s", canonical); + if (r < 0) + return r; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + return c; +} + +enum nss_status _nss_resolve_gethostbyname4_r( + const char *name, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp) { + + _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *canonical = NULL; + size_t l, ms, idx; + char *r_name; + int c, r, i = 0; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(pat); + assert(buffer); + assert(errnop); + assert(h_errnop); + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fallback; + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveHostname"); + if (r < 0) + goto fail; + + r = sd_bus_message_set_auto_start(req, false); + if (r < 0) + goto fail; + + r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0); + if (r < 0) + goto fail; + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + if (r < 0) { + if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + if (bus_error_shall_fallback(&error)) + goto fallback; + + goto fail; + } + + c = count_addresses(reply, AF_UNSPEC, &canonical); + if (c < 0) { + r = c; + goto fail; + } + if (c == 0) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + if (isempty(canonical)) + canonical = name; + + l = strlen(canonical); + ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; + if (buflen < ms) { + *errnop = ENOMEM; + *h_errnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, canonical, l+1); + idx = ALIGN(l+1); + + /* Second, append addresses */ + r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); + + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); + if (r < 0) + goto fail; + + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int family, ifindex; + const void *a; + size_t sz; + + assert_cc(sizeof(int32_t) == sizeof(int)); + + r = sd_bus_message_read(reply, "ii", &ifindex, &family); + if (r < 0) + goto fail; + + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + if (!IN_SET(family, AF_INET, AF_INET6)) + continue; + + if (sz != FAMILY_ADDRESS_SIZE(family)) { + r = -EINVAL; + goto fail; + } + + r_tuple = (struct gaih_addrtuple*) (buffer + idx); + r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); + r_tuple->name = r_name; + r_tuple->family = family; + r_tuple->scopeid = ifindex; + memcpy(r_tuple->addr, a, sz); + + idx += ALIGN(sizeof(struct gaih_addrtuple)); + i++; + } + if (r < 0) + goto fail; + + assert(i == c); + assert(idx == ms); + + if (*pat) + **pat = *r_tuple_first; + else + *pat = r_tuple_first; + + if (ttlp) + *ttlp = 0; + + /* Explicitly reset all error variables */ + *errnop = 0; + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + + return NSS_STATUS_SUCCESS; + +fallback: + { + _nss_gethostbyname4_r_t fallback; + + fallback = (_nss_gethostbyname4_r_t) + find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r"); + + if (fallback) + return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp); + } + +fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_resolve_gethostbyname3_r( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp, + char **canonp) { + + _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + size_t l, idx, ms, alen; + const char *canonical; + int c, r, i = 0; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(result); + assert(buffer); + assert(errnop); + assert(h_errnop); + + if (af == AF_UNSPEC) + af = AF_INET; + + if (af != AF_INET && af != AF_INET6) { + r = -EAFNOSUPPORT; + goto fail; + } + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fallback; + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveHostname"); + if (r < 0) + goto fail; + + r = sd_bus_message_set_auto_start(req, false); + if (r < 0) + goto fail; + + r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0); + if (r < 0) + goto fail; + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + if (r < 0) { + if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + if (bus_error_shall_fallback(&error)) + goto fallback; + + goto fail; + } + + c = count_addresses(reply, af, &canonical); + if (c < 0) { + r = c; + goto fail; + } + if (c == 0) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + if (isempty(canonical)) + canonical = name; + + alen = FAMILY_ADDRESS_SIZE(af); + l = strlen(canonical); + + ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); + + if (buflen < ms) { + *errnop = ENOMEM; + *h_errnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } + + /* First, append name */ + r_name = buffer; + memcpy(r_name, canonical, l+1); + idx = ALIGN(l+1); + + /* Second, create empty aliases array */ + r_aliases = buffer + idx; + ((char**) r_aliases)[0] = NULL; + idx += sizeof(char*); + + /* Third, append addresses */ + r_addr = buffer + idx; + + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); + if (r < 0) + goto fail; + + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + int ifindex, family; + const void *a; + size_t sz; + + r = sd_bus_message_read(reply, "ii", &ifindex, &family); + if (r < 0) + goto fail; + + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + + if (family != af) + continue; + + if (sz != alen) { + r = -EINVAL; + goto fail; + } + + memcpy(r_addr + i*ALIGN(alen), a, alen); + i++; + } + if (r < 0) + goto fail; + + assert(i == c); + idx += c * ALIGN(alen); + + /* Fourth, append address pointer array */ + r_addr_list = buffer + idx; + for (i = 0; i < c; i++) + ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen); + + ((char**) r_addr_list)[i] = NULL; + idx += (c+1) * sizeof(char*); + + assert(idx == ms); + + result->h_name = r_name; + result->h_aliases = (char**) r_aliases; + result->h_addrtype = af; + result->h_length = alen; + result->h_addr_list = (char**) r_addr_list; + + /* Explicitly reset all error variables */ + *errnop = 0; + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + + if (ttlp) + *ttlp = 0; + + if (canonp) + *canonp = r_name; + + return NSS_STATUS_SUCCESS; + +fallback: + { + _nss_gethostbyname3_r_t fallback; + + fallback = (_nss_gethostbyname3_r_t) + find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r"); + if (fallback) + return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp); + } + +fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_resolve_gethostbyaddr2_r( + const void* addr, socklen_t len, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp) { + + _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + unsigned c = 0, i = 0; + size_t ms = 0, idx; + const char *n; + int r, ifindex; + + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(addr); + assert(result); + assert(buffer); + assert(errnop); + assert(h_errnop); + + if (!IN_SET(af, AF_INET, AF_INET6)) { + *errnop = EAFNOSUPPORT; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + if (len != FAMILY_ADDRESS_SIZE(af)) { + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + + r = sd_bus_open_system(&bus); + if (r < 0) + goto fallback; + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveAddress"); + if (r < 0) + goto fail; + + r = sd_bus_message_set_auto_start(req, false); + if (r < 0) + goto fail; + + r = sd_bus_message_append(req, "ii", 0, af); + if (r < 0) + goto fail; + + r = sd_bus_message_append_array(req, 'y', addr, len); + if (r < 0) + goto fail; + + r = sd_bus_message_append(req, "t", (uint64_t) 0); + if (r < 0) + goto fail; + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + if (r < 0) { + if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + if (bus_error_shall_fallback(&error)) + goto fallback; + + + *errnop = -r; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + + r = sd_bus_message_enter_container(reply, 'a', "(is)"); + if (r < 0) + goto fail; + + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { + + if (ifindex < 0) { + r = -EINVAL; + goto fail; + } + + c++; + ms += ALIGN(strlen(n) + 1); + } + if (r < 0) + goto fail; + + r = sd_bus_message_rewind(reply, false); + if (r < 0) + return r; + + if (c <= 0) { + *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + ms += ALIGN(len) + /* the address */ + 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ + c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ + + if (buflen < ms) { + *errnop = ENOMEM; + *h_errnop = TRY_AGAIN; + return NSS_STATUS_TRYAGAIN; + } + + /* First, place address */ + r_addr = buffer; + memcpy(r_addr, addr, len); + idx = ALIGN(len); + + /* Second, place address list */ + r_addr_list = buffer + idx; + ((char**) r_addr_list)[0] = r_addr; + ((char**) r_addr_list)[1] = NULL; + idx += sizeof(char*) * 2; + + /* Third, reserve space for the aliases array */ + r_aliases = buffer + idx; + idx += sizeof(char*) * c; + + /* Fourth, place aliases */ + i = 0; + r_name = buffer + idx; + while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { + char *p; + size_t l; + + l = strlen(n); + p = buffer + idx; + memcpy(p, n, l+1); + + if (i > 0) + ((char**) r_aliases)[i-1] = p; + i++; + + idx += ALIGN(l+1); + } + if (r < 0) + goto fail; + + ((char**) r_aliases)[c-1] = NULL; + assert(idx == ms); + + result->h_name = r_name; + result->h_aliases = (char**) r_aliases; + result->h_addrtype = af; + result->h_length = len; + result->h_addr_list = (char**) r_addr_list; + + if (ttlp) + *ttlp = 0; + + /* Explicitly reset all error variables */ + *errnop = 0; + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + + return NSS_STATUS_SUCCESS; + +fallback: + { + _nss_gethostbyaddr2_r_t fallback; + + fallback = (_nss_gethostbyaddr2_r_t) + find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r"); + + if (fallback) + return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp); + } + +fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; +} + +NSS_GETHOSTBYNAME_FALLBACKS(resolve); +NSS_GETHOSTBYADDR_FALLBACKS(resolve); diff --git a/src/grp-resolve/nss-resolve/nss-resolve.sym b/src/grp-resolve/nss-resolve/nss-resolve.sym new file mode 100644 index 0000000000..df8dff2a20 --- /dev/null +++ b/src/grp-resolve/nss-resolve/nss-resolve.sym @@ -0,0 +1,19 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. +***/ + +{ +global: + _nss_resolve_gethostbyname_r; + _nss_resolve_gethostbyname2_r; + _nss_resolve_gethostbyname3_r; + _nss_resolve_gethostbyname4_r; + _nss_resolve_gethostbyaddr_r; + _nss_resolve_gethostbyaddr2_r; +local: *; +}; diff --git a/src/grp-resolve/nss-resolve/nss-resolve.xml b/src/grp-resolve/nss-resolve/nss-resolve.xml new file mode 100644 index 0000000000..d9e56453e8 --- /dev/null +++ b/src/grp-resolve/nss-resolve/nss-resolve.xml @@ -0,0 +1,111 @@ + + + + + + + + + nss-resolve + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + nss-resolve + 8 + + + + nss-resolve + libnss_resolve.so.2 + Provide hostname resolution via systemd-resolved.service + + + + libnss_resolve.so.2 + + + + Description + + nss-resolve is a plug-in module for the GNU Name Service Switch (NSS) functionality of the + GNU C Library (glibc) enabling it to resolve host names via the + systemd-resolved8 local network + name resolution service. It replaces the nss-dns plug-in module that traditionally resolves + hostnames via DNS. + + To activate the NSS module, add resolve to the line starting with + hosts: in /etc/nsswitch.conf. + + It is recommended to place resolve early in /etc/nsswitch.conf' + hosts: line (but after the files or mymachines entries), + replacing the dns entry if it exists, to ensure DNS queries are always routed via + systemd-resolved8. + + Note that nss-resolve will chain-load nss-dns if + systemd-resolved.service is not running, ensuring that basic DNS resolution continues to work + if the service is down. + + + + Example + + Here is an example /etc/nsswitch.conf file that enables nss-resolve + correctly: + +passwd: compat mymachines +group: compat mymachines +shadow: compat + +hosts: files mymachines resolve myhostname +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis + + + + + See Also + + systemd1, + systemd-resolved8, + nss-mymachines8, + nss-myhostname8, + nsswitch.conf5 + + + + diff --git a/src/grp-resolve/systemd-resolve/Makefile b/src/grp-resolve/systemd-resolve/Makefile new file mode 100644 index 0000000000..9d9b46d58d --- /dev/null +++ b/src/grp-resolve/systemd-resolve/Makefile @@ -0,0 +1,53 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_resolve_SOURCES = \ + src/resolve/resolve-tool.c \ + $(basic_dns_sources) \ + src/shared/gcrypt-util.c \ + src/shared/gcrypt-util.h + +nodist_systemd_resolve_SOURCES = \ + src/resolve/dns_type-from-name.h \ + src/resolve/dns_type-to-name.h + +systemd_resolve_CFLAGS = \ + $(GCRYPT_CFLAGS) + +systemd_resolve_LDADD = \ + libsystemd-shared.la \ + $(GCRYPT_LIBS) \ + -lm + +bin_PROGRAMS += \ + systemd-resolve + +dist_bashcompletion_data += \ + shell-completion/bash/systemd-resolve + +dist_zshcompletion_data += \ + shell-completion/zsh/_systemd-resolve + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-resolve/systemd-resolve/resolve-tool.c b/src/grp-resolve/systemd-resolve/resolve-tool.c new file mode 100644 index 0000000000..433a103b57 --- /dev/null +++ b/src/grp-resolve/systemd-resolve/resolve-tool.c @@ -0,0 +1,2007 @@ +/*** + 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 . +***/ + +#include +#include + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/in-addr-util.h" +#include "basic/parse-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "resolved-def.h" +#include "resolved-dns-packet.h" +#include "sd-bus/bus-error.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/bus-util.h" +#include "shared/gcrypt-util.h" +#include "shared/pager.h" + +#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) + +static int arg_family = AF_UNSPEC; +static int arg_ifindex = 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 bool arg_no_pager = false; + +typedef enum ServiceFamily { + SERVICE_FAMILY_TCP, + SERVICE_FAMILY_UDP, + SERVICE_FAMILY_SCTP, + _SERVICE_FAMILY_INVALID = -1, +} ServiceFamily; +static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP; + +typedef enum RawType { + RAW_NONE, + RAW_PAYLOAD, + RAW_PACKET, +} RawType; +static RawType arg_raw = RAW_NONE; + +static enum { + MODE_RESOLVE_HOST, + MODE_RESOLVE_RECORD, + MODE_RESOLVE_SERVICE, + MODE_RESOLVE_OPENPGP, + MODE_RESOLVE_TLSA, + MODE_STATISTICS, + MODE_RESET_STATISTICS, + MODE_FLUSH_CACHES, + MODE_STATUS, +} arg_mode = MODE_RESOLVE_HOST; + +static ServiceFamily service_family_from_string(const char *s) { + if (s == NULL || streq(s, "tcp")) + return SERVICE_FAMILY_TCP; + if (streq(s, "udp")) + return SERVICE_FAMILY_UDP; + if (streq(s, "sctp")) + return SERVICE_FAMILY_SCTP; + return _SERVICE_FAMILY_INVALID; +} + +static const char* service_family_to_string(ServiceFamily service) { + switch(service) { + case SERVICE_FAMILY_TCP: + return "_tcp"; + case SERVICE_FAMILY_UDP: + return "_udp"; + case SERVICE_FAMILY_SCTP: + return "_sctp"; + default: + assert_not_reached("invalid service"); + } +} + +static void print_source(uint64_t flags, usec_t rtt) { + char rtt_str[FORMAT_TIMESTAMP_MAX]; + + if (!arg_legend) + return; + + if (flags == 0) + return; + + fputs("\n-- Information acquired via", stdout); + + if (flags != 0) + 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_MDNS_IPV4 ? "mDNS/IPv4" : "", + flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : ""); + + assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); + + printf(" in %s", rtt_str); + + 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_(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; + int r; + uint64_t flags; + usec_t ts; + + assert(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 (family %s, interface %s).", name, 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", + "ResolveHostname"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "isit", arg_ifindex, name, 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, "%s: resolve call failed: %s", name, bus_error_message(&error, r)); + + ts = now(CLOCK_MONOTONIC) - ts; + + 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; + size_t sz; + + 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_ifindex_to_string(family, a, ifindex, &pretty); + if (r < 0) + return log_error_errno(r, "Failed to print address for %s: %m", name); + + printf("%*s%s %s%s%s\n", + (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", + pretty, + isempty(ifname) ? "" : "%", ifname); + + 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, "st", &canonical, &flags); + if (r < 0) + return bus_log_parse_error(r); + + if (!streq(name, canonical)) + printf("%*s%s (%s)\n", + (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", + canonical); + + if (c == 0) { + log_error("%s: no addresses found", name); + return -ESRCH; + } + + print_source(flags, ts); + + return 0; +} + +static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) { + _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; + unsigned c = 0; + usec_t ts; + int r; + + assert(bus); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + if (ifindex <= 0) + ifindex = arg_ifindex; + + r = in_addr_ifindex_to_string(family, address, ifindex, &pretty); + if (r < 0) + return log_oom(); + + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname); + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveAddress"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "ii", ifindex, family); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "t", 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) { + log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); + return r; + } + + ts = now(CLOCK_MONOTONIC) - ts; + + r = sd_bus_message_enter_container(reply, 'a', "(is)"); + if (r < 0) + return bus_log_create_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) { + const char *n; + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(reply, "is", &ifindex, &n); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%*s%*s%*s%s %s\n", + (int) strlen(pretty), c == 0 ? pretty : "", + isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%", + (int) strlen(ifname), c == 0 ? ifname : "", + c == 0 ? ":" : " ", + n); + + 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, "t", &flags); + if (r < 0) + return bus_log_parse_error(r); + + if (c == 0) { + log_error("%s: no names found", pretty); + return -ESRCH; + } + + print_source(flags, ts); + + return 0; +} + +static int output_rr_packet(const void *d, size_t l, int ifindex) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + char ifname[IF_NAMESIZE] = ""; + + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return log_oom(); + + p->refuse_compression = true; + + r = dns_packet_append_blob(p, d, l, NULL); + if (r < 0) + return log_oom(); + + r = dns_packet_read_rr(p, &rr, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse RR: %m"); + + if (arg_raw == RAW_PAYLOAD) { + void *data; + ssize_t k; + + k = dns_resource_record_payload(rr, &data); + if (k < 0) + return log_error_errno(k, "Cannot dump RR: %m"); + fwrite(data, 1, k, stdout); + } else { + const char *s; + + s = dns_resource_record_to_string(rr); + if (!s) + return log_oom(); + + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); + } + + return 0; +} + +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; + int r; + usec_t ts; + bool needs_authentication = false; + + assert(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(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname); + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveRecord"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, 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) { + log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); + return r; + } + + ts = now(CLOCK_MONOTONIC) - ts; + + r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { + uint16_t c, t; + int ifindex; + const void *d; + size_t l; + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &d, &l); + 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 (arg_raw == RAW_PACKET) { + uint64_t u64 = htole64(l); + + fwrite(&u64, sizeof(u64), 1, stdout); + fwrite(d, 1, l, stdout); + } else { + r = output_rr_packet(d, l, ifindex); + if (r < 0) + return r; + } + + if (dns_type_needs_authentication(t)) + needs_authentication = true; + + n++; + } + 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, "t", &flags); + if (r < 0) + return bus_log_parse_error(r); + + if (n == 0) { + log_error("%s: no records found", name); + return -ESRCH; + } + + print_source(flags, ts); + + if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { + fflush(stdout); + + fprintf(stderr, "\n%s" + "WARNING: The resources shown contain cryptographic key data which could not be\n" + " authenticated. It is not suitable to authenticate any communication.\n" + " This is usually indication that DNSSEC authentication was not enabled\n" + " or is not available for the selected protocol or DNS servers.%s\n", + ansi_highlight_red(), + ansi_normal()); + } + + 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 (class == 0) + class = arg_class ?: DNS_CLASS_IN; + if (type == 0) + type = arg_type ?: DNS_TYPE_A; + + 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); + + name = empty_to_null(name); + type = empty_to_null(type); + + 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); + + canonical_name = empty_to_null(canonical_name); + canonical_type = empty_to_null(canonical_type); + + 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 resolve_openpgp(sd_bus *bus, const char *address) { + const char *domain, *full; + int r; + _cleanup_free_ char *hashed = NULL; + + assert(bus); + assert(address); + + domain = strrchr(address, '@'); + if (!domain) { + log_error("Address does not contain '@': \"%s\"", address); + return -EINVAL; + } else if (domain == address || domain[1] == '\0') { + log_error("Address starts or ends with '@': \"%s\"", address); + return -EINVAL; + } + domain++; + + r = string_hashsum_sha224(address, domain - 1 - address, &hashed); + if (r < 0) + return log_error_errno(r, "Hashing failed: %m"); + + full = strjoina(hashed, "._openpgpkey.", domain); + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_OPENPGPKEY); +} + +static int resolve_tlsa(sd_bus *bus, const char *address) { + const char *port; + uint16_t port_num = 443; + _cleanup_free_ char *full = NULL; + int r; + + assert(bus); + assert(address); + + port = strrchr(address, ':'); + if (port) { + r = safe_atou16(port + 1, &port_num); + if (r < 0 || port_num == 0) + return log_error_errno(r, "Invalid port \"%s\".", port + 1); + + address = strndupa(address, port - address); + } + + r = asprintf(&full, "_%u.%s.%s", + port_num, + service_family_to_string(arg_service_family), + address); + if (r < 0) + return log_oom(); + + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_TLSA); +} + +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 Verdicts%s\n" + " Secure: %" PRIu64 "\n" + " Insecure: %" PRIu64 "\n" + " Bogus: %" PRIu64 "\n" + " Indeterminate: %" 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 int flush_caches(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", + "FlushCaches", + &error, + NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r)); + + return 0; +} + +static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char ***l = userdata; + int r; + + assert(bus); + assert(member); + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "(iay)"); + if (r < 0) + return r; + + for (;;) { + const void *a; + char *pretty; + int family; + size_t sz; + + r = sd_bus_message_enter_container(m, 'r', "iay"); + if (r < 0) + return r; + if (r == 0) + break; + + r = sd_bus_message_read(m, "i", &family); + if (r < 0) + return r; + + r = sd_bus_message_read_array(m, 'y', &a, &sz); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + if (!IN_SET(family, AF_INET, AF_INET6)) { + log_debug("Unexpected family, ignoring."); + continue; + } + + if (sz != FAMILY_ADDRESS_SIZE(family)) { + log_debug("Address size mismatch, ignoring."); + continue; + } + + r = in_addr_to_string(family, a, &pretty); + if (r < 0) + return r; + + r = strv_consume(l, pretty); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char ***l = userdata; + int r; + + assert(bus); + assert(member); + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "(sb)"); + if (r < 0) + return r; + + for (;;) { + const char *domain; + int route_only; + char *pretty; + + r = sd_bus_message_read(m, "(sb)", &domain, &route_only); + if (r < 0) + return r; + if (r == 0) + break; + + if (route_only) + pretty = strappend("~", domain); + else + pretty = strdup(domain); + if (!pretty) + return -ENOMEM; + + r = strv_consume(l, pretty); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empty_line) { + + struct link_info { + uint64_t scopes_mask; + char *llmnr; + char *mdns; + char *dnssec; + char **dns; + char **domains; + char **ntas; + int dnssec_supported; + } link_info = {}; + + static const struct bus_properties_map property_map[] = { + { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) }, + { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) }, + { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) }, + { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) }, + { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) }, + { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) }, + { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) }, + { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) }, + {} + }; + + _cleanup_free_ char *ifi = NULL, *p = NULL; + char ifname[IF_NAMESIZE] = ""; + char **i; + int r; + + assert(bus); + assert(ifindex > 0); + assert(empty_line); + + if (!name) { + if (!if_indextoname(ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for %i: %m", ifindex); + + name = ifname; + } + + if (asprintf(&ifi, "%i", ifindex) < 0) + return log_oom(); + + r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p); + if (r < 0) + return log_oom(); + + r = bus_map_all_properties(bus, + "org.freedesktop.resolve1", + p, + property_map, + &link_info); + if (r < 0) { + log_error_errno(r, "Failed to get link data for %i: %m", ifindex); + goto finish; + } + + pager_open(arg_no_pager, false); + + if (*empty_line) + fputc('\n', stdout); + + printf("%sLink %i (%s)%s\n", + ansi_highlight(), ifindex, name, ansi_normal()); + + if (link_info.scopes_mask == 0) + printf(" Current Scopes: none\n"); + else + printf(" Current Scopes:%s%s%s%s%s\n", + link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "", + link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", + link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "", + link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "", + link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : ""); + + printf(" LLMNR setting: %s\n" + "MulticastDNS setting: %s\n" + " DNSSEC setting: %s\n" + " DNSSEC supported: %s\n", + strna(link_info.llmnr), + strna(link_info.mdns), + strna(link_info.dnssec), + yes_no(link_info.dnssec_supported)); + + STRV_FOREACH(i, link_info.dns) { + printf(" %s %s\n", + i == link_info.dns ? "DNS Servers:" : " ", + *i); + } + + STRV_FOREACH(i, link_info.domains) { + printf(" %s %s\n", + i == link_info.domains ? "DNS Domain:" : " ", + *i); + } + + STRV_FOREACH(i, link_info.ntas) { + printf(" %s %s\n", + i == link_info.ntas ? "DNSSEC NTA:" : " ", + *i); + } + + *empty_line = true; + + r = 0; + +finish: + strv_free(link_info.dns); + strv_free(link_info.domains); + free(link_info.llmnr); + free(link_info.mdns); + free(link_info.dnssec); + strv_free(link_info.ntas); + return r; +} + +static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char ***l = userdata; + int r; + + assert(bus); + assert(member); + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "(iiay)"); + if (r < 0) + return r; + + for (;;) { + const void *a; + char *pretty; + int family, ifindex; + size_t sz; + + r = sd_bus_message_enter_container(m, 'r', "iiay"); + if (r < 0) + return r; + if (r == 0) + break; + + r = sd_bus_message_read(m, "ii", &ifindex, &family); + if (r < 0) + return r; + + r = sd_bus_message_read_array(m, 'y', &a, &sz); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + if (ifindex != 0) /* only show the global ones here */ + continue; + + if (!IN_SET(family, AF_INET, AF_INET6)) { + log_debug("Unexpected family, ignoring."); + continue; + } + + if (sz != FAMILY_ADDRESS_SIZE(family)) { + log_debug("Address size mismatch, ignoring."); + continue; + } + + r = in_addr_to_string(family, a, &pretty); + if (r < 0) + return r; + + r = strv_consume(l, pretty); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char ***l = userdata; + int r; + + assert(bus); + assert(member); + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "(isb)"); + if (r < 0) + return r; + + for (;;) { + const char *domain; + int route_only, ifindex; + char *pretty; + + r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only); + if (r < 0) + return r; + if (r == 0) + break; + + if (ifindex != 0) /* only show the global ones here */ + continue; + + if (route_only) + pretty = strappend("~", domain); + else + pretty = strdup(domain); + if (!pretty) + return -ENOMEM; + + r = strv_consume(l, pretty); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 0; +} + +static int status_global(sd_bus *bus, bool *empty_line) { + + struct global_info { + char **dns; + char **domains; + char **ntas; + } global_info = {}; + + static const struct bus_properties_map property_map[] = { + { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) }, + { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) }, + { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) }, + {} + }; + + char **i; + int r; + + assert(bus); + assert(empty_line); + + r = bus_map_all_properties(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + property_map, + &global_info); + if (r < 0) { + log_error_errno(r, "Failed to get global data: %m"); + goto finish; + } + + if (strv_isempty(global_info.dns) && strv_isempty(global_info.domains) && strv_isempty(global_info.ntas)) { + r = 0; + goto finish; + } + + pager_open(arg_no_pager, false); + + printf("%sGlobal%s\n", ansi_highlight(), ansi_normal()); + STRV_FOREACH(i, global_info.dns) { + printf(" %s %s\n", + i == global_info.dns ? "DNS Servers:" : " ", + *i); + } + + STRV_FOREACH(i, global_info.domains) { + printf(" %s %s\n", + i == global_info.domains ? "DNS Domain:" : " ", + *i); + } + + strv_sort(global_info.ntas); + STRV_FOREACH(i, global_info.ntas) { + printf(" %s %s\n", + i == global_info.ntas ? "DNSSEC NTA:" : " ", + *i); + } + + *empty_line = true; + + r = 0; + +finish: + strv_free(global_info.dns); + strv_free(global_info.domains); + strv_free(global_info.ntas); + + return r; +} + +static int status_all(sd_bus *bus) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + sd_netlink_message *i; + bool empty_line = false; + int r; + + assert(bus); + + r = status_global(bus, &empty_line); + if (r < 0) + return r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return log_error_errno(r, "Failed to enumerate links: %m"); + + r = 0; + for (i = reply; i; i = sd_netlink_message_next(i)) { + const char *name; + int ifindex, q; + uint16_t type; + + q = sd_netlink_message_get_type(i, &type); + if (q < 0) + return rtnl_log_parse_error(q); + + if (type != RTM_NEWLINK) + continue; + + q = sd_rtnl_message_link_get_ifindex(i, &ifindex); + if (q < 0) + return rtnl_log_parse_error(q); + + if (ifindex == LOOPBACK_IFINDEX) + continue; + + q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); + if (q < 0) + return rtnl_log_parse_error(q); + + q = status_ifindex(bus, ifindex, name, &empty_line); + if (q < 0 && r >= 0) + r = q; + } + + return r; +} + +static void help_protocol_types(void) { + if (arg_legend) + puts("Known protocol types:"); + puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6"); +} + +static void help_dns_types(void) { + const char *t; + int i; + + if (arg_legend) + puts("Known DNS RR types:"); + for (i = 0; i < _DNS_TYPE_MAX; i++) { + t = dns_type_to_string(i); + if (t) + puts(t); + } +} + +static void help_dns_classes(void) { + const char *t; + int i; + + if (arg_legend) + puts("Known DNS RR classes:"); + for (i = 0; i < _DNS_CLASS_MAX; i++) { + t = dns_class_to_string(i); + if (t) + puts(t); + } +} + +static void help(void) { + printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n" + "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n" + "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n" + "%1$s [OPTIONS...] --statistics\n" + "%1$s [OPTIONS...] --reset-statistics\n" + "\n" + "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -4 Resolve IPv4 addresses\n" + " -6 Resolve IPv6 addresses\n" + " -i --interface=INTERFACE Look on interface\n" + " -p --protocol=PROTO|help Look via protocol\n" + " -t --type=TYPE|help Query RR with DNS type\n" + " -c --class=CLASS|help Query RR with DNS class\n" + " --service Resolve service (SRV)\n" + " --service-address=BOOL Resolve address for services (default: yes)\n" + " --service-txt=BOOL Resolve TXT records for services (default: yes)\n" + " --openpgp Query OpenPGP public key\n" + " --tlsa Query TLS public key\n" + " --cname=BOOL Follow CNAME redirects (default: yes)\n" + " --search=BOOL Use search domains for single-label names\n" + " (default: yes)\n" + " --raw[=payload|packet] Dump the answer as binary data\n" + " --legend=BOOL Print headers and additional info (default: yes)\n" + " --statistics Show resolver statistics\n" + " --reset-statistics Reset resolver statistics\n" + " --status Show link and server status\n" + " --flush-caches Flush all local DNS caches\n" + , 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_OPENPGP, + ARG_TLSA, + ARG_RAW, + ARG_SEARCH, + ARG_STATISTICS, + ARG_RESET_STATISTICS, + ARG_STATUS, + ARG_FLUSH_CACHES, + ARG_NO_PAGER, + }; + + 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", 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 }, + { "openpgp", no_argument, NULL, ARG_OPENPGP }, + { "tlsa", optional_argument, NULL, ARG_TLSA }, + { "raw", optional_argument, NULL, ARG_RAW }, + { "search", required_argument, NULL, ARG_SEARCH }, + { "statistics", no_argument, NULL, ARG_STATISTICS, }, + { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, + { "status", no_argument, NULL, ARG_STATUS }, + { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0) + switch(c) { + + case 'h': + help(); + return 0; /* done */; + + case ARG_VERSION: + return version(); + + case '4': + arg_family = AF_INET; + break; + + case '6': + arg_family = AF_INET6; + break; + + case 'i': { + int ifi; + + if (parse_ifindex(optarg, &ifi) >= 0) + arg_ifindex = ifi; + else { + ifi = if_nametoindex(optarg); + if (ifi <= 0) + return log_error_errno(errno, "Unknown interface %s: %m", optarg); + + arg_ifindex = ifi; + } + + break; + } + + case 't': + if (streq(optarg, "help")) { + help_dns_types(); + return 0; + } + + r = dns_type_from_string(optarg); + if (r < 0) { + log_error("Failed to parse RR record type %s", optarg); + return r; + } + arg_type = (uint16_t) r; + assert((int) arg_type == r); + + arg_mode = MODE_RESOLVE_RECORD; + break; + + case 'c': + if (streq(optarg, "help")) { + help_dns_classes(); + return 0; + } + + 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: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --legend= argument"); + + arg_legend = r; + break; + + case 'p': + if (streq(optarg, "help")) { + help_protocol_types(); + return 0; + } else if (streq(optarg, "dns")) + arg_flags |= SD_RESOLVED_DNS; + else if (streq(optarg, "llmnr")) + arg_flags |= SD_RESOLVED_LLMNR; + else if (streq(optarg, "llmnr-ipv4")) + arg_flags |= SD_RESOLVED_LLMNR_IPV4; + else if (streq(optarg, "llmnr-ipv6")) + arg_flags |= SD_RESOLVED_LLMNR_IPV6; + else { + log_error("Unknown protocol specifier: %s", optarg); + return -EINVAL; + } + + break; + + case ARG_SERVICE: + arg_mode = MODE_RESOLVE_SERVICE; + break; + + case ARG_OPENPGP: + arg_mode = MODE_RESOLVE_OPENPGP; + break; + + case ARG_TLSA: + arg_mode = MODE_RESOLVE_TLSA; + arg_service_family = service_family_from_string(optarg); + if (arg_service_family < 0) { + log_error("Unknown service family \"%s\".", optarg); + return -EINVAL; + } + break; + + case ARG_RAW: + if (on_tty()) { + log_error("Refusing to write binary data to tty."); + return -ENOTTY; + } + + if (optarg == NULL || streq(optarg, "payload")) + arg_raw = RAW_PAYLOAD; + else if (streq(optarg, "packet")) + arg_raw = RAW_PACKET; + else { + log_error("Unknown --raw specifier \"%s\".", optarg); + return -EINVAL; + } + + arg_legend = false; + break; + + case ARG_CNAME: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --cname= argument."); + SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); + break; + + case ARG_SERVICE_ADDRESS: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --service-address= argument."); + SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); + break; + + case ARG_SERVICE_TXT: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --service-txt= argument."); + SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); + break; + + case ARG_SEARCH: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --search argument."); + SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); + break; + + case ARG_STATISTICS: + arg_mode = MODE_STATISTICS; + break; + + case ARG_RESET_STATISTICS: + arg_mode = MODE_RESET_STATISTICS; + break; + + case ARG_FLUSH_CACHES: + arg_mode = MODE_FLUSH_CACHES; + break; + + case ARG_STATUS: + arg_mode = MODE_STATUS; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_type == 0 && arg_class != 0) { + log_error("--class= may only be used in conjunction with --type=."); + return -EINVAL; + } + + if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) { + 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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error_errno(r, "sd_bus_open_system: %m"); + goto finish; + } + + switch (arg_mode) { + + 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 = in_addr_ifindex_from_string_auto(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 { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + break; + + case MODE_RESOLVE_OPENPGP: + if (argc < optind + 1) { + log_error("E-mail address required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_openpgp(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + + case MODE_RESOLVE_TLSA: + if (argc < optind + 1) { + log_error("Domain name required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_tlsa(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + + case MODE_STATISTICS: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + r = show_statistics(bus); + break; + + case MODE_RESET_STATISTICS: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + r = reset_statistics(bus); + break; + + case MODE_FLUSH_CACHES: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + r = flush_caches(bus); + break; + + case MODE_STATUS: + + if (argc > optind) { + char **ifname; + bool empty_line = false; + + r = 0; + STRV_FOREACH(ifname, argv + optind) { + int ifindex, q; + + q = parse_ifindex(argv[optind], &ifindex); + if (q < 0) { + ifindex = if_nametoindex(argv[optind]); + if (ifindex <= 0) { + log_error_errno(errno, "Failed to resolve interface name: %s", argv[optind]); + continue; + } + } + + q = status_ifindex(bus, ifindex, NULL, &empty_line); + if (q < 0 && r >= 0) + r = q; + } + } else + r = status_all(bus); + + break; + } + +finish: + pager_close(); + + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-resolve/systemd-resolve/systemd-resolve.completion.bash b/src/grp-resolve/systemd-resolve/systemd-resolve.completion.bash new file mode 100644 index 0000000000..0c501c9405 --- /dev/null +++ b/src/grp-resolve/systemd-resolve/systemd-resolve.completion.bash @@ -0,0 +1,64 @@ +# systemd-resolve(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2016 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 +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_interfaces(){ + { cd /sys/class/net && echo *; } | \ + while read -d' ' -r name; do + [[ "$name" != "lo" ]] && echo "$name" + done +} + +_systemd-resolve() { + local i comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version -4 -6 + --service --openpgp --tlsa --statistics --reset-statistics + --service-address=no --service-txt=no + --cname=no --search=no --legend=no' + [ARG]='-i --interface -p --protocol -t --type -c --class' + ) + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --interface|-i) + comps=$( __get_interfaces ) + ;; + --protocol|-p|--type|-t|--class|-c) + comps=$( systemd-resolve --legend=no "$prev" help; echo help ) + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi +} + +complete -F _systemd-resolve systemd-resolve diff --git a/src/grp-resolve/systemd-resolve/systemd-resolve.completion.zsh b/src/grp-resolve/systemd-resolve/systemd-resolve.completion.zsh new file mode 100644 index 0000000000..c318ab50f1 --- /dev/null +++ b/src/grp-resolve/systemd-resolve/systemd-resolve.completion.zsh @@ -0,0 +1,64 @@ +#compdef systemd-resolve + +# +# This file is part of systemd. +# +# Copyright 2016 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 +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +_dns_protocol() { + local -a _protocol + _protocol=( $(_call_program protocol ${service} --legend=no --protocol help; echo help) ) + _values 'protocol' "$_protocol[@]" +} + +_dns_type() { + local -a _type + _type=( $(_call_program type ${service} --legend=no --type help; echo help) ) + _values 'type' "$_type[@]" +} + +_dns_class() { + local -a _class + _class=( $(_call_program class ${service} --legend=no --class help; echo help) ) + _values 'class' "$_class[@]" +} + +_systemd-resolve_none() { + _alternative : \ + 'domain:DNS address:' \ + 'address:email address:' +} + +_arguments \ + {-h,--help}'[Print a short help text and exit]' \ + '--version[Print a short version string and exit]' \ + '--legend=no[Do not show headers and footers]' \ + '-4[Resolve IPv4 addresses]' \ + '-6[Resolve IPv6 addresses]' \ + {-i+,--interface=}'[Look on interface]:interface:_net_interfaces' \ + {-p+,--protocol=}'[Look via protocol]:protocol:_dns_protocol' \ + {-t+,--type=}'[Query RR with DNS type]:type:_dns_type' \ + {-c+,--class=}'[Query RR with DNS class]:class:_dns_class' \ + '--service[Resolve services]' \ + '--service-address=no[Do not resolve address for services]' \ + '--service-txt=no[Do not resolve TXT records for services]' \ + '--openpgp[Query OpenPGP public key]' \ + '--tlsa[Query TLS public key]' \ + '--cname=no[Do not follow CNAME redirects]' \ + '--search=no[Do not use search domains]' \ + '--statistics[Show resolver statistics]' \ + '--reset-statistics[Reset resolver statistics]' \ + '*::default: _systemd-resolve_none' diff --git a/src/grp-resolve/systemd-resolve/systemd-resolve.xml b/src/grp-resolve/systemd-resolve/systemd-resolve.xml new file mode 100644 index 0000000000..ca26bb4d49 --- /dev/null +++ b/src/grp-resolve/systemd-resolve/systemd-resolve.xml @@ -0,0 +1,394 @@ + + + + + + + + + systemd-resolve + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-resolve + 1 + + + + systemd-resolve + Resolve domain names, IPV4 and IPv6 addresses, DNS resource records, and services + + + + + systemd-resolve + OPTIONS + HOSTNAME + + + + systemd-resolve + OPTIONS + ADDRESS + + + + systemd-resolve + OPTIONS + --type=TYPE + DOMAIN + + + + systemd-resolve + OPTIONS + --service + NAME + TYPE DOMAIN + + + + systemd-resolve + OPTIONS + --openpgp + USER@DOMAIN + + + + systemd-resolve + OPTIONS + --tlsa + DOMAIN:PORT + + + + systemd-resolve + OPTIONS + --statistics + + + + systemd-resolve + OPTIONS + --reset-statistics + + + + + + Description + + systemd-resolve may be used to resolve domain names, IPv4 and IPv6 addresses, DNS resource + records and services with the + systemd-resolved.service8 + resolver service. By default, the specified list of parameters will be resolved as hostnames, retrieving their IPv4 + and IPv6 addresses. If the parameters specified are formatted as IPv4 or IPv6 operation the reverse operation is + done, and a hostname is retrieved for the specified addresses. + + The program's output contains information about the protocol used for the look-up and on which network + interface the data was discovered. It also contains information on whether the information could be + authenticated. All data for which local DNSSEC validation succeeds is considered authenticated. Moreover all data + originating from local, trusted sources is also reported authenticated, including resolution of the local host + name, the localhost host name or all data from /etc/hosts. + + The switch may be used to specify a DNS resource record type (A, AAAA, SOA, MX, ...) in + order to request a specific DNS resource record, instead of the address or reverse address lookups. + The special value help may be used to list known values. + + The switch may be used to resolve SRV and DNS-SD services (see below). In this mode, between one and three + arguments are required. If three parameters are passed the first is assumed to be the DNS-SD service name, the + second the SRV service type, and the third the domain to search in. In this case a full DNS-SD style SRV and TXT + lookup is executed. If only two parameters are specified, the first is assumed to be the SRV service type, and the + second the domain to look in. In this case no TXT RR is requested. Finally, if only one parameter is specified, it + is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no + TXT). + + The switch may be used to query PGP keys stored as + OPENPGPKEY resource records. + When this option is specified one or more e-mail address must be specified. + + The switch maybe be used to query TLS public + keys stored as + TLSA resource records. + When this option is specified one or more domain names must be specified. + + The switch may be used to show resolver statistics, including information about + the number of successful and failed DNSSEC validations. + + The may be used to reset various statistics counters maintained the + resolver, including those shown in the output. This operation requires root + privileges. + + + + Options + + + + + + By default, when resolving a hostname, both IPv4 and IPv6 + addresses are acquired. By specifying only IPv4 addresses are requested, by specifying + only IPv6 addresses are requested. + + + + + INTERFACE + INTERFACE + + Specifies the network interface to execute the query on. This may either be specified as numeric + interface index or as network interface string (e.g. en0). Note that this option has no + effect if system-wide DNS configuration (as configured in /etc/resolv.conf or + /etc/systemd/resolve.conf) in place of per-link configuration is used. + + + + PROTOCOL + PROTOCOL + + Specifies the network protocol for the query. May be one of dns + (i.e. classic unicast DNS), llmnr (Link-Local Multicast Name Resolution), + llmnr-ipv4, llmnr-ipv6 (LLMNR via the indicated underlying IP + protocols). By default the lookup is done via all protocols suitable for the lookup. If used, limits the set of + protocols that may be used. Use this option multiple times to enable resolving via multiple protocols at the + same time. The setting llmnr is identical to specifying this switch once with + llmnr-ipv4 and once via llmnr-ipv6. Note that this option does not force + the service to resolve the operation with the specified protocol, as that might require a suitable network + interface and configuration. + The special value help may be used to list known values. + + + + + TYPE + TYPE + CLASS + CLASS + + Specifies the DNS resource record type (e.g. A, AAAA, MX, …) and class (e.g. IN, ANY, …) to + look up. If these options are used a DNS resource record set matching the specified class and type is + requested. The class defaults to IN if only a type is specified. + The special value help may be used to list known values. + + + + + + + Enables service resolution. This enables DNS-SD and simple SRV service resolution, depending + on the specified list of parameters (see above). + + + + BOOL + + Takes a boolean parameter. If true (the default), when doing a service lookup with + the hostnames contained in the SRV resource records are resolved as well. + + + + BOOL + + Takes a boolean parameter. If true (the default), when doing a DNS-SD service lookup with + the TXT service metadata record is resolved as well. + + + + + + Enables OPENPGPKEY resource record resolution (see above). Specified e-mail + addresses are converted to the corresponding DNS domain name, and any OPENPGPKEY keys are + printed. + + + + + + Enables TLSA resource record resolution (see above). + A query will be performed for each of the specified names prefixed with + the port and family + (_port._family.domain). + The port number may be specified after a colon + (:), otherwise 443 will be used + by default. The family may be specified as an argument after + , otherwise tcp will be + used. + + + + BOOL + + Takes a boolean parameter. If true (the default), DNS CNAME or DNAME redirections are + followed. Otherwise, if a CNAME or DNAME record is encountered while resolving, an error is + returned. + + + + BOOL + + Takes a boolean parameter. If true (the default), any specified single-label hostnames will be + searched in the domains configured in the search domain list, if it is non-empty. Otherwise, the search domain + logic is disabled. + + + + =payload|packet + + Dump the answer as binary data. If there is no argument or if the argument is + payload, the payload of the packet is exported. If the argument is + packet, the whole packet is dumped in wire format, prefixed by + length specified as a little-endian 64-bit number. This format allows multiple packets + to be dumped and unambigously parsed. + + + + BOOL + + Takes a boolean parameter. If true (the default), column headers and meta information about the + query response are shown. Otherwise, this output is suppressed. + + + + + + If specified general resolver statistics are shown, including information whether DNSSEC is + enabled and available, as well as resolution and validation statistics. + + + + + + Resets the statistics counters shown in to zero. + + + + + + Flushes all DNS resource record caches the service maintains locally. + + + + + + Shows the global and per-link DNS settings in currently in effect. + + + + + + + + + + Examples + + + Retrieve the addresses of the <literal>www.0pointer.net</literal> domain + + $ systemd-resolve www.0pointer.net +www.0pointer.net: 2a01:238:43ed:c300:10c3:bcf3:3266:da74 + 85.214.157.71 + +-- Information acquired via protocol DNS in 611.6ms. +-- Data is authenticated: no + + + + + Retrieve the domain of the <literal>85.214.157.71</literal> IP address + + $ systemd-resolve 85.214.157.71 +85.214.157.71: gardel.0pointer.net + +-- Information acquired via protocol DNS in 1.2997s. +-- Data is authenticated: no + + + + + Retrieve the MX record of the <literal>0pointer.net</literal> domain + + $ systemd-resolve -t MX yahoo.com --legend=no +yahoo.com. IN MX 1 mta7.am0.yahoodns.net +yahoo.com. IN MX 1 mta6.am0.yahoodns.net +yahoo.com. IN MX 1 mta5.am0.yahoodns.net + + + + + Resolve an SRV service + + $ systemd-resolve --service _xmpp-server._tcp gmail.com +_xmpp-server._tcp/gmail.com: alt1.xmpp-server.l.google.com:5269 [priority=20, weight=0] + 173.194.210.125 + alt4.xmpp-server.l.google.com:5269 [priority=20, weight=0] + 173.194.65.125 + ... + + + + + Retrieve a PGP key + + $ systemd-resolve --openpgp zbyszek@fedoraproject.org +d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org. IN OPENPGPKEY + mQINBFBHPMsBEACeInGYJCb+7TurKfb6wGyTottCDtiSJB310i37/6ZYoeIay/5soJjlMyf + MFQ9T2XNT/0LM6gTa0MpC1st9LnzYTMsT6tzRly1D1UbVI6xw0g0vE5y2Cjk3xUwAynCsSs + ... + + + + + Retrieve a TLS key (<literal>=tcp</literal> and + <literal>:443</literal> could be skipped) + + $ systemd-resolve --tlsa=tcp fedoraproject.org:443 +_443._tcp.fedoraproject.org IN TLSA 0 0 1 19400be5b7a31fb733917700789d2f0a2471c0c9d506c0e504c06c16d7cb17c0 + -- Cert. usage: CA constraint + -- Selector: Full Certificate + -- Matching type: SHA-256 + + + + + + See Also + + systemd1, + systemd-resolved.service8 + + + diff --git a/src/grp-resolve/systemd-resolved/.gitignore b/src/grp-resolve/systemd-resolved/.gitignore new file mode 100644 index 0000000000..f0835923b7 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/.gitignore @@ -0,0 +1,6 @@ +/resolved-gperf.c +/resolved.conf +/dns_type-from-name.gperf +/dns_type-from-name.h +/dns_type-list.txt +/dns_type-to-name.h diff --git a/src/grp-resolve/systemd-resolved/Makefile b/src/grp-resolve/systemd-resolved/Makefile new file mode 100644 index 0000000000..78b352b76f --- /dev/null +++ b/src/grp-resolve/systemd-resolved/Makefile @@ -0,0 +1,141 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_RESOLVED),) + +basic_dns_sources = \ + src/resolve/resolved-dns-dnssec.c \ + src/resolve/resolved-dns-dnssec.h \ + src/resolve/resolved-dns-packet.c \ + src/resolve/resolved-dns-packet.h \ + src/resolve/resolved-dns-rr.c \ + src/resolve/resolved-dns-rr.h \ + src/resolve/resolved-dns-answer.c \ + src/resolve/resolved-dns-answer.h \ + src/resolve/resolved-dns-question.c \ + src/resolve/resolved-dns-question.h \ + src/resolve/dns-type.c \ + src/resolve/dns-type.h + +systemd_resolved_SOURCES = \ + src/resolve/resolved.c \ + src/resolve/resolved-manager.c \ + src/resolve/resolved-manager.h \ + src/resolve/resolved-conf.c \ + src/resolve/resolved-conf.h \ + src/resolve/resolved-resolv-conf.c \ + src/resolve/resolved-resolv-conf.h \ + src/resolve/resolved-bus.c \ + src/resolve/resolved-bus.h \ + src/resolve/resolved-link.h \ + src/resolve/resolved-link.c \ + src/resolve/resolved-link-bus.c \ + src/resolve/resolved-link-bus.h \ + src/resolve/resolved-llmnr.h \ + src/resolve/resolved-llmnr.c \ + src/resolve/resolved-mdns.h \ + src/resolve/resolved-mdns.c \ + src/resolve/resolved-def.h \ + $(basic_dns_sources) \ + src/resolve/resolved-dns-query.h \ + src/resolve/resolved-dns-query.c \ + src/resolve/resolved-dns-synthesize.h \ + src/resolve/resolved-dns-synthesize.c \ + src/resolve/resolved-dns-transaction.h \ + src/resolve/resolved-dns-transaction.c \ + src/resolve/resolved-dns-scope.h \ + src/resolve/resolved-dns-scope.c \ + src/resolve/resolved-dns-server.h \ + src/resolve/resolved-dns-server.c \ + src/resolve/resolved-dns-search-domain.h \ + src/resolve/resolved-dns-search-domain.c \ + src/resolve/resolved-dns-cache.h \ + src/resolve/resolved-dns-cache.c \ + src/resolve/resolved-dns-zone.h \ + src/resolve/resolved-dns-zone.c \ + src/resolve/resolved-dns-stream.h \ + src/resolve/resolved-dns-stream.c \ + src/resolve/resolved-dns-trust-anchor.h \ + src/resolve/resolved-dns-trust-anchor.c \ + src/resolve/resolved-dns-stub.h \ + src/resolve/resolved-dns-stub.c \ + src/resolve/resolved-etc-hosts.h \ + src/resolve/resolved-etc-hosts.c \ + src/shared/gcrypt-util.c \ + src/shared/gcrypt-util.h + +nodist_systemd_resolved_SOURCES = \ + src/resolve/dns_type-from-name.h \ + src/resolve/dns_type-to-name.h \ + src/resolve/resolved-gperf.c + +systemd_resolved_CFLAGS = \ + $(GCRYPT_CFLAGS) + +systemd_resolved_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la \ + $(GCRYPT_LIBS) \ + -lm + +rootlibexec_PROGRAMS += \ + systemd-resolved + +nodist_systemunit_DATA += \ + units/systemd-resolved.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.resolve1.busname + +dist_dbuspolicy_DATA += \ + src/resolve/org.freedesktop.resolve1.conf + +dist_dbussystemservice_DATA += \ + src/resolve/org.freedesktop.resolve1.service + +SYSTEM_UNIT_ALIASES += \ + systemd-resolved.service dbus-org.freedesktop.resolve1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.resolve1.busname + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-resolved.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-resolved.service + +nodist_pkgsysconf_DATA += \ + src/resolve/resolved.conf + +endif # ENABLE_RESOLVED +gperf_gperf_sources += \ + src/resolve/resolved-gperf.gperf + +EXTRA_DIST += \ + units/systemd-resolved.service.m4.in \ + src/resolve/resolved.conf.in + +dist_rootlibexec_DATA += \ + src/resolve/resolv.conf + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-resolve/systemd-resolved/RFCs b/src/grp-resolve/systemd-resolved/RFCs new file mode 100644 index 0000000000..09c85f9518 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/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/grp-resolve/systemd-resolved/dnssec-trust-anchors.d.xml b/src/grp-resolve/systemd-resolved/dnssec-trust-anchors.d.xml new file mode 100644 index 0000000000..4bdc167f79 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/dnssec-trust-anchors.d.xml @@ -0,0 +1,200 @@ + + + + + + + + dnssec-trust-anchors.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + dnssec-trust-anchors.d + 5 + + + + dnssec-trust-anchors.d + systemd.positive + systemd.negative + DNSSEC trust anchor configuration files + + + + /etc/dnssec-trust-anchors.d/*.positive + /run/dnssec-trust-anchors.d/*.positive + /usr/lib/dnssec-trust-anchors.d/*.positive + /etc/dnssec-trust-anchors.d/*.negative + /run/dnssec-trust-anchors.d/*.negative + /usr/lib/dnssec-trust-anchors.d/*.negative + + + + Description + + The DNSSEC trust anchor configuration files define positive + and negative trust anchors + systemd-resolved.service8 + bases DNSSEC integrity proofs on. + + + + Positive Trust Anchors + + Positive trust anchor configuration files contain DNSKEY and + DS resource record definitions to use as base for DNSSEC integrity + proofs. See RFC 4035, + Section 4.4 for more information about DNSSEC trust + anchors. + + Positive trust anchors are read from files with the suffix + .positive located in + /etc/dnssec-trust-anchors.d/, + /run/dnssec-trust-anchors.d/ and + /usr/lib/dnssec-trust-anchors.d/. These + directories are searched in the specified order, and a trust + anchor file of the same name in an earlier path overrides a trust + anchor files in a later path. To disable a trust anchor file + shipped in /usr/lib/dnssec-trust-anchors.d/ + it is sufficient to provide an identically-named file in + /etc/dnssec-trust-anchors.d/ or + /run/dnssec-trust-anchors.d/ that is either + empty or a symlink to /dev/null ("masked"). + + Positive trust anchor files are simple text files resembling + DNS zone files, as documented in RFC 1035, Section + 5. One DS or DNSKEY resource record may be listed per + line. Empty lines and lines starting with a semicolon + (;) are ignored and considered comments. A DS + resource record is specified like in the following example: + + . IN DS 19036 8 2 49aac11d7b6f6446702e54a1607371607a1a41855200fd2ce1cdde32f24e8fb5 + + The first word specifies the domain, use + . for the root domain. The domain may be + specified with or without trailing dot, which is considered + equivalent. The second word must be IN the + third word DS. The following words specify the + key tag, signature algorithm, digest algorithm, followed by the + hex-encoded key fingerprint. See RFC 4034, + Section 5 for details about the precise syntax and meaning + of these fields. + + Alternatively, DNSKEY resource records may be used to define + trust anchors, like in the following example: + + . IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= + + The first word specifies the domain again, the second word + must be IN, followed by + DNSKEY. The subsequent words encode the DNSKEY + flags, protocol and algorithm fields, followed by the key data + encoded in Base64. See RFC 4034, + Section 2 for details about the precise syntax and meaning + of these fields. + + If multiple DS or DNSKEY records are defined for the same + domain (possibly even in different trust anchor files), all keys + are used and are considered equivalent as base for DNSSEC + proofs. + + Note that systemd-resolved will + automatically use a built-in trust anchor key for the Internet + root domain if no positive trust anchors are defined for the root + domain. In most cases it is hence unnecessary to define an + explicit key with trust anchor files. The built-in key is disabled + as soon as at least one trust anchor key for the root domain is + defined in trust anchor files. + + It is generally recommended to encode trust anchors in DS + resource records, rather than DNSKEY resource records. + + If a trust anchor specified via a DS record is found revoked + it is automatically removed from the trust anchor database for the + runtime. See RFC + 5011 for details about revoked trust anchors. Note that + systemd-resolved will not update its trust + anchor database from DNS servers automatically. Instead, it is + recommended to update the resolver software or update the new + trust anchor via adding in new trust anchor files. + + The current DNSSEC trust anchor for the Internet's root + domain is available at the IANA + Trust Anchor and Keys page. + + + + Negative Trust Anchors + + Negative trust anchors define domains where DNSSEC + validation shall be turned off. Negative trust anchor files are + found at the same location as positive trust anchor files, and + follow the same overriding rules. They are text files with the + .negative suffix. Empty lines and lines whose + first character is ; are ignored. Each line + specifies one domain name where DNSSEC validation shall be + disabled on. + + Negative trust anchors are useful to support private DNS + subtrees that are not referenced from the Internet DNS hierarchy, + and not signed. + + RFC + 7646 for details on negative trust anchors. + + If no negative trust anchor files are configured a built-in + set of well-known private DNS zone domains is used as negative + trust anchors. + + It is also possibly to define per-interface negative trust + anchors using the DNSSECNegativeTrustAnchors= + setting in + systemd.network5 + files. + + + + See Also + + systemd1, + systemd-resolved.service8, + resolved.conf5, + systemd.network5 + + + + diff --git a/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.conf b/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.conf new file mode 100644 index 0000000000..25b09774e5 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.service b/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.service new file mode 100644 index 0000000000..7ac5c323f0 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/org.freedesktop.resolve1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.resolve1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.resolve1.service diff --git a/src/grp-resolve/systemd-resolved/resolv.conf b/src/grp-resolve/systemd-resolved/resolv.conf new file mode 100644 index 0000000000..b8034d6829 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolv.conf @@ -0,0 +1,11 @@ +# This is a static resolv.conf file for connecting local clients to +# systemd-resolved via its DNS stub listener on 127.0.0.53. +# +# Third party programs must not access this file directly, but only through the +# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way, +# replace this symlink by a static file or a different symlink. +# +# See systemd-resolved.service(8) for details about the supported modes of +# operation for /etc/resolv.conf. + +nameserver 127.0.0.53 diff --git a/src/grp-resolve/systemd-resolved/resolved-bus.c b/src/grp-resolve/systemd-resolved/resolved-bus.c new file mode 100644 index 0000000000..3c2a8b0892 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-bus.c @@ -0,0 +1,1690 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "resolved-def.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" +#include "shared/dns-domain.h" + +#include "resolved-bus.h" +#include "resolved-dns-synthesize.h" +#include "resolved-link-bus.h" + +static int reply_query_state(DnsQuery *q) { + + switch (q->state) { + + case DNS_TRANSACTION_NO_SERVERS: + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); + + case DNS_TRANSACTION_TIMEOUT: + return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); + + case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: + return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); + + case DNS_TRANSACTION_INVALID_REPLY: + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); + + case DNS_TRANSACTION_ERRNO: + return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m"); + + case DNS_TRANSACTION_ABORTED: + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); + + 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_NETWORK_DOWN: + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down"); + + case DNS_TRANSACTION_NOT_FOUND: + /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we + * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ + return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); + + 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", dns_query_string(q)); + else { + const char *rc, *n; + char p[DECIMAL_STR_MAX(q->answer_rcode)]; + + rc = dns_rcode_to_string(q->answer_rcode); + if (!rc) { + sprintf(p, "%i", q->answer_rcode); + rc = p; + } + + n = strjoina(_BUS_ERROR_DNS, 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); + } + + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_VALIDATING: + case DNS_TRANSACTION_SUCCESS: + default: + assert_not_reached("Impossible state"); + } +} + +static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) { + int r; + + assert(reply); + assert(rr); + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", ifindex); + if (r < 0) + return r; + + if (rr->key->type == DNS_TYPE_A) { + r = sd_bus_message_append(reply, "i", AF_INET); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr)); + + } else if (rr->key->type == DNS_TYPE_AAAA) { + r = sd_bus_message_append(reply, "i", AF_INET6); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr)); + } else + return -EAFNOSUPPORT; + + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 0; +} + +static void bus_method_resolve_hostname_complete(DnsQuery *q) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *normalized = NULL; + DnsResourceRecord *rr; + unsigned added = 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; + + r = sd_bus_message_new_method_return(q->request, &reply); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + goto finish; + + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + DnsQuestion *question; + + question = dns_query_question_for_protocol(q, q->answer_protocol); + + 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, rr, ifindex); + if (r < 0) + goto finish; + + 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; + + /* The key names are not necessarily normalized, make sure that they are when we return them to our bus + * clients. */ + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + if (r < 0) + goto finish; + + /* Return the precise spelling and uppercasing and CNAME target reported by the server */ + assert(canonical); + r = sd_bus_message_append( + reply, "st", + normalized, + 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) { + log_error_errno(r, "Failed to send hostname reply: %m"); + sd_bus_reply_method_errno(q->request, r, NULL); + } + + dns_query_free(q); +} + +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_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); + + if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */ + *flags |= SD_RESOLVED_PROTOCOLS_ALL; + + return 0; +} + +static int parse_as_address(sd_bus_message *m, int ifindex, const char *hostname, int family, uint64_t flags) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *canonical = NULL; + union in_addr_union parsed; + int r, ff, parsed_ifindex = 0; + + /* Check if the hostname is actually already an IP address formatted as string. In that case just parse it, + * let's not attempt to look it up. */ + + r = in_addr_ifindex_from_string_auto(hostname, &ff, &parsed, &parsed_ifindex); + if (r < 0) /* not an address */ + return 0; + + if (family != AF_UNSPEC && ff != family) + return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address is not of the requested family."); + if (ifindex > 0 && parsed_ifindex > 0 && parsed_ifindex != ifindex) + return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address interface index does not match requested interface."); + + if (parsed_ifindex > 0) + ifindex = parsed_ifindex; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "ii", ifindex, ff); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &parsed, FAMILY_ADDRESS_SIZE(ff)); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + /* When an IP address is specified we just return it as canonical name, in order to avoid a DNS + * look-up. However, we reformat it to make sure it's in a truly canonical form (i.e. on IPv6 the inner + * omissions are always done the same way). */ + r = in_addr_ifindex_to_string(ff, &parsed, ifindex, &canonical); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", canonical, + SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(flags), ff, true)); + if (r < 0) + return r; + + return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL); +} + +static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; + Manager *m = userdata; + const char *hostname; + 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, "isit", &ifindex, &hostname, &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); + + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); + if (r < 0) + return r; + + r = parse_as_address(message, ifindex, hostname, family, flags); + if (r != 0) + return r; + + 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 = dns_question_new_address(&question_utf8, family, hostname, false); + 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_utf8, question_idna, ifindex, flags); + if (r < 0) + return r; + + q->request = sd_bus_message_ref(message); + q->request_family = family; + q->complete = bus_method_resolve_hostname_complete; + q->suppress_unroutable_family = family == AF_UNSPEC; + + 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; +} + +static void bus_method_resolve_address_complete(DnsQuery *q) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + DnsQuestion *question; + DnsResourceRecord *rr; + unsigned added = 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; + + r = sd_bus_message_new_method_return(q->request, &reply); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(reply, 'a', "(is)"); + if (r < 0) + goto finish; + + question = dns_query_question_for_protocol(q, q->answer_protocol); + + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + _cleanup_free_ char *normalized = NULL; + + r = dns_question_matches_rr(question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; + + r = dns_name_normalize(rr->ptr.name, &normalized); + if (r < 0) + goto finish; + + r = sd_bus_message_append(reply, "(is)", ifindex, normalized); + if (r < 0) + goto finish; + + added++; + } + + if (added <= 0) { + _cleanup_free_ char *ip = NULL; + + (void) 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", strnull(ip)); + goto finish; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto finish; + + 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; + + r = sd_bus_send(q->manager->bus, reply, NULL); + +finish: + if (r < 0) { + log_error_errno(r, "Failed to send address reply: %m"); + 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_question_unrefp) DnsQuestion *question = NULL; + Manager *m = userdata; + int family, ifindex; + uint64_t flags; + const void *d; + DnsQuery *q; + size_t sz; + int r; + + 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; + + 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_read(message, "t", &flags); + if (r < 0) + return r; + + r = check_ifindex_flags(ifindex, &flags, 0, error); + if (r < 0) + return r; + + r = dns_question_new_reverse(&question, family, d); + if (r < 0) + return r; + + 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_family = family; + memcpy(&q->request_address, d, sz); + q->complete = bus_method_resolve_address_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; +} + +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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + DnsResourceRecord *rr; + DnsQuestion *question; + unsigned added = 0; + int ifindex; + int 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; + + r = sd_bus_message_new_method_return(q->request, &reply); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(reply, 'a', "(iqqay)"); + if (r < 0) + goto finish; + + 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; + + r = bus_message_append_rr(reply, rr, ifindex); + if (r < 0) + goto finish; + + 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", dns_query_string(q)); + goto finish; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto finish; + + 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; + + r = sd_bus_send(q->manager->bus, reply, NULL); + +finish: + if (r < 0) { + log_error_errno(r, "Failed to send record reply: %m"); + sd_bus_reply_method_errno(q->request, r, NULL); + } + + dns_query_free(q); +} + +static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + Manager *m = userdata; + uint16_t class, type; + const char *name; + int r, ifindex; + uint64_t flags; + DnsQuery *q; + + 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_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); + + 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_zone_transer(type)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface."); + 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; + + question = dns_question_new(1); + if (!question) + return -ENOMEM; + + key = dns_resource_key_new(class, type, name); + if (!key) + return -ENOMEM; + + r = dns_question_add(question, key); + if (r < 0) + return r; + + r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + if (r < 0) + return r; + + /* Let's request that the TTL is fixed up for locally cached entries, after all we return it in the wire format + * blob */ + q->clamp_ttl = true; + + q->request = sd_bus_message_ref(message); + q->complete = bus_method_resolve_record_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; +} + +static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_free_ char *normalized = 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 = dns_name_normalize(rr->srv.name, &normalized); + if (r < 0) + return r; + + r = sd_bus_message_append( + reply, + "qqqs", + rr->srv.priority, rr->srv.weight, rr->srv.port, normalized); + 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; + + if (canonical) { + normalized = mfree(normalized); + + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + 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", normalized); + 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) { + 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; + 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", dns_server_ifindex(s)); + 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_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', "(isb)"); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, m->search_domains) { + r = sd_bus_message_append(reply, "(isb)", 0, d->name, d->route_only); + if (r < 0) + return r; + } + + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(domains, d, l->search_domains) { + r = sd_bus_message_append(reply, "(isb)", l->ifindex, d->name, d->route_only); + 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_verdict[DNSSEC_SECURE], + (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE], + (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS], + (uint64_t) m->n_dnssec_verdict[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_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) { + + Manager *m = userdata; + const char *domain; + Iterator i; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + SET_FOREACH(domain, m->trust_anchor.negative_by_name, i) { + r = sd_bus_message_append(reply, "s", domain); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +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; + zero(m->n_dnssec_verdict); + + 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 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_any_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_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_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 int bus_method_flush_caches(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + assert(message); + assert(m); + + manager_flush_caches(m); + + return sd_bus_reply_method_return(message, NULL); +} + +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(isb)", bus_property_get_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_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 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("FlushCaches", NULL, NULL, bus_method_flush_caches, 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", "ia(sb)", NULL, bus_method_set_link_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, +}; + +static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(m); + + m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); + + manager_connect_bus(m); + return 0; +} + +static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + Manager *m = userdata; + int b, r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) { + log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); + return 0; + } + + if (b) + return 0; + + log_debug("Coming back from suspend, verifying all RRs..."); + + manager_verify_all(m); + return 0; +} + +int manager_connect_bus(Manager *m) { + int r; + + assert(m); + + if (m->bus) + return 0; + + r = sd_bus_default_system(&m->bus); + if (r < 0) { + /* We failed to connect? Yuck, we must be in early + * boot. Let's try in 5s again. As soon as we have + * kdbus we can stop doing this... */ + + log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); + + r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, 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; + } + + r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, 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"); + + r = sd_bus_attach_event(m->bus, m->event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, + "type='signal'," + "sender='org.freedesktop.login1'," + "interface='org.freedesktop.login1.Manager'," + "member='PrepareForSleep'," + "path='/org/freedesktop/login1'", + match_prepare_for_sleep, + m); + if (r < 0) + log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + + return 0; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-bus.h b/src/grp-resolve/systemd-resolved/resolved-bus.h new file mode 100644 index 0000000000..f49e1337d2 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-bus.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "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/grp-resolve/systemd-resolved/resolved-conf.c b/src/grp-resolve/systemd-resolved/resolved-conf.c new file mode 100644 index 0000000000..c6d70b5938 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-conf.c @@ -0,0 +1,241 @@ +/*** + 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 . + ***/ + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/extract-word.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "shared/conf-parser.h" + +#include "resolved-conf.h" + +int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { + union in_addr_union address; + int family, r, ifindex = 0; + DnsServer *s; + + assert(m); + assert(word); + + r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex); + if (r < 0) + return r; + + /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */ + if (!dns_server_address_valid(family, &address)) + return 0; + + /* Filter out duplicates */ + s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex); + 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, ifindex); +} + +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { + int r; + + assert(m); + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + 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: %m", word); + } + + return 0; +} + +int manager_add_search_domain_by_string(Manager *m, const char *domain) { + DnsSearchDomain *d; + bool route_only; + int r; + + assert(m); + assert(domain); + + route_only = *domain == '~'; + if (route_only) + domain++; + + if (dns_name_is_root(domain) || streq(domain, "*")) { + route_only = true; + 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); + else { + r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain); + if (r < 0) + return r; + } + + d->route_only = route_only; + return 0; +} + +int manager_parse_search_domains_and_warn(Manager *m, const char *string) { + int r; + + assert(m); + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + + 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: %m", word); + } + + return 0; +} + +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) { + + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(m); + + if (isempty(rvalue)) + /* Empty assignment means clear the list */ + dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); + else { + /* 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); + return 0; + } + } + + /* If we have a manual setting, then we stop reading + * /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_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) { + + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(m); + + 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 search domains string '%s'. Ignoring.", rvalue); + return 0; + } + } + + /* 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); + + 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/grp-resolve/systemd-resolved/resolved-conf.h b/src/grp-resolve/systemd-resolved/resolved-conf.h new file mode 100644 index 0000000000..e1fd2cceec --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-conf.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + 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 . +***/ + +#include "resolved-manager.h" + +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_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/grp-resolve/systemd-resolved/resolved-dns-cache.c b/src/grp-resolve/systemd-resolved/resolved-dns-cache.c new file mode 100644 index 0000000000..690ed6f0c1 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-cache.c @@ -0,0 +1,1065 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-packet.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-cache.h" + +/* 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 2h in our cache */ +#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR) + +typedef enum DnsCacheItemType DnsCacheItemType; +typedef struct DnsCacheItem DnsCacheItem; + +enum DnsCacheItemType { + DNS_CACHE_POSITIVE, + DNS_CACHE_NODATA, + DNS_CACHE_NXDOMAIN, +}; + +struct DnsCacheItem { + DnsCacheItemType type; + DnsResourceKey *key; + DnsResourceRecord *rr; + + usec_t until; + bool authenticated:1; + bool shared_owner:1; + + int ifindex; + int owner_family; + union in_addr_union owner_address; + + unsigned prioq_idx; + LIST_FIELDS(DnsCacheItem, by_key); +}; + +static void dns_cache_item_free(DnsCacheItem *i) { + if (!i) + return; + + dns_resource_record_unref(i->rr); + dns_resource_key_unref(i->key); + free(i); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free); + +static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) { + DnsCacheItem *first; + + assert(c); + + if (!i) + return; + + first = hashmap_get(c->by_key, i->key); + LIST_REMOVE(by_key, first, i); + + if (first) + assert_se(hashmap_replace(c->by_key, first->key, first) >= 0); + else + hashmap_remove(c->by_key, i->key); + + prioq_remove(c->by_expiry, i, &i->prioq_idx); + + dns_cache_item_free(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); + + first = hashmap_remove(c->by_key, key); + if (!first) + return false; + + LIST_FOREACH_SAFE(by_key, i, n, first) { + prioq_remove(c->by_expiry, i, &i->prioq_idx); + dns_cache_item_free(i); + } + + return true; +} + +void dns_cache_flush(DnsCache *c) { + DnsResourceKey *key; + + assert(c); + + 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); + + 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) { + assert(c); + + if (add <= 0) + return; + + /* Makes space for n new entries. Note that we actually allow + * the cache to grow beyond CACHE_MAX, but only when we shall + * add more RRs to the cache than CACHE_MAX at once. In that + * case the cache will be emptied completely otherwise. */ + + for (;;) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + DnsCacheItem *i; + + if (prioq_size(c->by_expiry) <= 0) + break; + + if (prioq_size(c->by_expiry) + add < CACHE_MAX) + break; + + i = prioq_peek(c->by_expiry); + assert(i); + + /* 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); + } +} + +void dns_cache_prune(DnsCache *c) { + usec_t t = 0; + + assert(c); + + /* Remove all entries that are past their TTL */ + + for (;;) { + DnsCacheItem *i; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + i = prioq_peek(c->by_expiry); + if (!i) + break; + + if (t <= 0) + t = now(clock_boottime_or_monotonic()); + + if (i->until > t) + break; + + /* Depending whether this is an mDNS shared entry + * either remove only this one RR or the whole RRset */ + log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)", + i->shared_owner ? "shared " : "", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (t - i->until) / USEC_PER_SEC); + + 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); + } + } +} + +static int dns_cache_item_prioq_compare_func(const void *a, const void *b) { + const DnsCacheItem *x = a, *y = b; + + if (x->until < y->until) + return -1; + if (x->until > y->until) + return 1; + return 0; +} + +static int dns_cache_init(DnsCache *c) { + int r; + + assert(c); + + r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops); + if (r < 0) + return r; + + return r; +} + +static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) { + DnsCacheItem *first; + int r; + + assert(c); + assert(i); + + r = prioq_put(c->by_expiry, i, &i->prioq_idx); + if (r < 0) + return r; + + 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 { + r = hashmap_put(c->by_key, i->key, i); + if (r < 0) { + prioq_remove(c->by_expiry, i, &i->prioq_idx); + return r; + } + } + + return 0; +} + +static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { + DnsCacheItem *i; + + assert(c); + assert(rr); + + LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key)) + if (i->rr && dns_resource_record_equal(i->rr, rr) > 0) + return i; + + return NULL; +} + +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 ifindex, + int owner_family, + const union in_addr_union *owner_address) { + + assert(c); + assert(i); + assert(rr); + assert(owner_address); + + i->type = DNS_CACHE_POSITIVE; + + if (!i->by_key_prev) + /* We are the first item in the list, we need to + * update the key used in the hashmap */ + + assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0); + + dns_resource_record_ref(rr); + dns_resource_record_unref(i->rr); + i->rr = rr; + + dns_resource_key_unref(i->key); + i->key = dns_resource_key_ref(rr->key); + + i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); + i->authenticated = authenticated; + i->shared_owner = shared_owner; + + i->ifindex = ifindex; + + i->owner_family = owner_family; + i->owner_address = *owner_address; + + prioq_reshuffle(c->by_expiry, i, &i->prioq_idx); +} + +static int dns_cache_put_positive( + DnsCache *c, + DnsResourceRecord *rr, + bool authenticated, + bool shared_owner, + usec_t timestamp, + int ifindex, + int owner_family, + const union in_addr_union *owner_address) { + + _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; + DnsCacheItem *existing; + char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE]; + int r, k; + + assert(c); + assert(rr); + assert(owner_address); + + /* 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) { + k = dns_cache_remove_by_rr(c, rr); + log_debug("%s: %s", + k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry", + dns_resource_key_to_string(rr->key, key_str, sizeof key_str)); + return 0; + } + + /* Entry exists already? Update TTL, timestamp and owner*/ + existing = dns_cache_get(c, rr); + if (existing) { + dns_cache_item_update_positive( + c, + existing, + rr, + authenticated, + shared_owner, + timestamp, + ifindex, + owner_family, + owner_address); + return 0; + } + + /* Otherwise, add the new RR */ + r = dns_cache_init(c); + if (r < 0) + return r; + + dns_cache_make_space(c, 1); + + i = new0(DnsCacheItem, 1); + if (!i) + return -ENOMEM; + + i->type = DNS_CACHE_POSITIVE; + i->key = dns_resource_key_ref(rr->key); + i->rr = dns_resource_record_ref(rr); + i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); + i->authenticated = authenticated; + i->shared_owner = shared_owner; + i->ifindex = ifindex; + 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; + + if (log_get_max_level() >= LOG_DEBUG) { + _cleanup_free_ char *t = NULL; + + (void) in_addr_to_string(i->owner_family, &i->owner_address, &t); + + log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s", + i->authenticated ? "authenticated" : "unauthenticated", + i->shared_owner ? " shared" : "", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (i->until - timestamp) / USEC_PER_SEC, + i->ifindex == 0 ? "*" : strna(if_indextoname(i->ifindex, ifname)), + af_to_name_short(i->owner_family), + strna(t)); + } + + i = NULL; + return 0; +} + +static int dns_cache_put_negative( + DnsCache *c, + DnsResourceKey *key, + int rcode, + bool authenticated, + uint32_t nsec_ttl, + usec_t timestamp, + DnsResourceRecord *soa, + int owner_family, + const union in_addr_union *owner_address) { + + _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + int r; + + assert(c); + assert(key); + assert(soa); + assert(owner_address); + + /* 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 (dns_type_is_pseudo(key->type)) + return 0; + + if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { + log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + return 0; + } + + if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) + return 0; + + r = dns_cache_init(c); + if (r < 0) + return r; + + dns_cache_make_space(c, 1); + + i = new0(DnsCacheItem, 1); + if (!i) + return -ENOMEM; + + i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN; + 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; + + log_debug("Added %s cache entry for %s "USEC_FMT"s", + i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (i->until - timestamp) / USEC_PER_SEC); + + 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); + } +} + +static bool rr_eligible(DnsResourceRecord *rr) { + assert(rr); + + /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since + * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS + * existence from any cached NSEC/NSEC3, but that should be fine. */ + + switch (rr->key->type) { + + case DNS_TYPE_NSEC: + return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) || + bitmap_isset(rr->nsec.types, DNS_TYPE_SOA); + + case DNS_TYPE_NSEC3: + return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) || + bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA); + + default: + return true; + } +} + +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) { + + DnsResourceRecord *soa = NULL, *rr; + DnsAnswerFlags flags; + unsigned cache_keys; + int r, ifindex; + + assert(c); + assert(owner_address); + + dns_cache_remove_previous(c, key, answer); + + /* 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; + + if (dns_answer_size(answer) <= 0) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Not caching negative entry without a SOA record: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + return 0; + } + + cache_keys = dns_answer_size(answer); + if (key) + cache_keys++; + + /* Make some space for our new entries */ + dns_cache_make_space(c, cache_keys); + + if (timestamp <= 0) + timestamp = now(clock_boottime_or_monotonic()); + + /* Second, add in positive entries for all contained RRs */ + DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) { + if ((flags & DNS_ANSWER_CACHEABLE) == 0) + continue; + + r = rr_eligible(rr); + if (r < 0) + return r; + if (r == 0) + continue; + + r = dns_cache_put_positive( + c, + rr, + flags & DNS_ANSWER_AUTHENTICATED, + flags & DNS_ANSWER_SHARED_OWNER, + timestamp, + ifindex, + owner_family, owner_address); + if (r < 0) + goto fail; + } + + 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_match_key(answer, key, NULL); + if (r < 0) + goto fail; + if (r > 0) + return 0; + + /* 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; + + /* See https://tools.ietf.org/html/rfc2308, which say that a + * matching SOA record in the packet is used to enable + * negative caching. */ + r = dns_answer_find_soa(answer, key, &soa, &flags); + if (r < 0) + goto fail; + if (r == 0) + return 0; + + /* 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, + authenticated, + nsec_ttl, + timestamp, + soa, + owner_family, owner_address); + if (r < 0) + goto fail; + + return 0; + +fail: + /* Adding all RRs failed. Let's clean up what we already + * added, just in case */ + + if (key) + dns_cache_remove_by_key(c, 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_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) + return i; + + 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; + + /* 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 NULL; +} + +int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + unsigned n = 0; + int r; + bool nxdomain = false; + DnsCacheItem *j, *first, *nsec = NULL; + bool have_authenticated = false, have_non_authenticated = false; + usec_t current; + + 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 don't use the cache, so + * that the caller refreshes via the network. */ + + log_debug("Ignoring cache for ANY lookup: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + + c->n_miss++; + + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } + + first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key); + if (!first) { + /* If one question cannot be answered we need to refresh */ + + log_debug("Cache miss for %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + + c->n_miss++; + + *ret = NULL; + *rcode = DNS_RCODE_SUCCESS; + return 0; + } + + LIST_FOREACH(by_key, j, first) { + if (j->rr) { + if (j->rr->key->type == DNS_TYPE_NSEC) + nsec = j; + + n++; + } else if (j->type == DNS_CACHE_NXDOMAIN) + nxdomain = true; + + if (j->authenticated) + have_authenticated = true; + else + have_non_authenticated = true; + } + + if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) { + /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from + * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */ + + log_debug("NSEC NODATA cache hit for %s", + dns_resource_key_to_string(key, key_str, sizeof 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. */ + + *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; + } + + log_debug("%s cache hit for %s", + n > 0 ? "Positive" : + nxdomain ? "NXDOMAIN" : "NODATA", + dns_resource_key_to_string(key, key_str, sizeof 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; + } + + answer = dns_answer_new(n); + if (!answer) + return -ENOMEM; + + if (clamp_ttl) + current = now(clock_boottime_or_monotonic()); + + LIST_FOREACH(by_key, j, first) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + if (!j->rr) + continue; + + if (clamp_ttl) { + rr = dns_resource_record_ref(j->rr); + + r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC); + if (r < 0) + return r; + } + + r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, 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; +} + +int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) { + DnsCacheItem *i, *first; + bool same_owner = true; + + assert(cache); + assert(rr); + + dns_cache_prune(cache); + + /* See if there's a cache entry for the same key. If there + * isn't there's no conflict */ + first = hashmap_get(cache->by_key, rr->key); + if (!first) + return 0; + + /* See if the RR key is owned by the same owner, if so, there + * isn't a conflict either */ + LIST_FOREACH(by_key, i, first) { + if (i->owner_family != owner_family || + !in_addr_equal(owner_family, &i->owner_address, owner_address)) { + same_owner = false; + break; + } + } + if (same_owner) + return 0; + + /* See if there's the exact same RR in the cache. If yes, then + * there's no conflict. */ + if (dns_cache_get(cache, rr)) + return 0; + + /* There's a conflict */ + 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; + + if (!cache) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, cache->by_key, iterator) { + DnsCacheItem *j; + + LIST_FOREACH(by_key, j, i) { + + fputc('\t', f); + + if (j->rr) { + const char *t; + t = dns_resource_record_to_string(j->rr); + if (!t) { + log_oom(); + continue; + } + + fputs(t, f); + fputc('\n', f); + } else { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f); + fputs(" -- ", f); + fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); + fputc('\n', f); + } + } + } +} + +bool dns_cache_is_empty(DnsCache *cache) { + if (!cache) + return true; + + 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/grp-resolve/systemd-resolved/resolved-dns-cache.h b/src/grp-resolve/systemd-resolved/resolved-dns-cache.h new file mode 100644 index 0000000000..7c1581c99b --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-cache.h @@ -0,0 +1,52 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/prioq.h" +#include "basic/time-util.h" + +typedef struct DnsCache { + Hashmap *by_key; + Prioq *by_expiry; + unsigned n_hit; + unsigned n_miss; +} DnsCache; + +#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, 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, bool clamp_ttl, 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/grp-resolve/systemd-resolved/resolved-dns-query.c b/src/grp-resolve/systemd-resolved/resolved-dns-query.c new file mode 100644 index 0000000000..3a187ee349 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-query.c @@ -0,0 +1,1122 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/string-util.h" +#include "dns-type.h" +#include "sd-netlink/local-addresses.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-query.h" +#include "resolved-dns-synthesize.h" +#include "resolved-etc-hosts.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 int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; + + assert(ret); + assert(q); + assert(s); + + 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; +} + +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); + set_remove(t->notify_query_candidates_done, 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; + else + next = dns_scope_get_search_domains(c->scope); + + for (;;) { + if (!next) /* We hit the end of the list */ + return 0; + + if (!next->route_only) + break; + + /* Skip over route-only domains */ + next = next->domains_next; + } + + 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_ensure_allocated(&t->notify_query_candidates_done, 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; + } + + t->clamp_ttl = c->query->clamp_ttl; + return 1; + +gc: + dns_transaction_gc(t); + return r; +} + +static int dns_query_candidate_go(DnsQueryCandidate *c) { + DnsTransaction *t; + Iterator i; + int r; + unsigned n = 0; + + 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; + + n++; + } + + /* If there was nothing to start, then let's proceed immediately */ + if (n == 0) + dns_query_candidate_notify(c); + + 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_ERRNO; + + 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 bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) { + int family; + + assert(c); + + /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of + * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR, + * or a routable IPv6 address if we query an AAAA RR. */ + + if (!c->query->suppress_unroutable_family) + return true; + + if (c->scope->protocol != DNS_PROTOCOL_DNS) + return true; + + family = dns_type_to_af(type); + if (family < 0) + return true; + + if (c->scope->link) + return link_relevant(c->scope->link, family, false); + else + return manager_routable(c->scope->manager, family); +} + +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; + DnsResourceKey *qkey; + + if (!dns_query_candidate_is_routable(c, key->type)) + continue; + + if (c->search_domain) { + r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name); + if (r < 0) + goto fail; + + qkey = new_key; + } else + qkey = key; + + if (!dns_scope_good_key(c->scope, qkey)) + continue; + + r = dns_query_candidate_add_transaction(c, qkey); + 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_errno = 0; + 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; + + while (q->auxiliary_queries) + dns_query_free(q->auxiliary_queries); + + 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); + + dns_packet_unref(q->request_dns_packet); + + if (q->request_dns_stream) { + /* Detach the stream from our query, in case something else keeps a reference to it. */ + q->request_dns_stream->complete = NULL; + q->request_dns_stream->on_packet = NULL; + q->request_dns_stream->query = NULL; + dns_stream_unref(q->request_dns_stream); + } + + free(q->request_address_string); + + if (q->manager) { + LIST_REMOVE(queries, q->manager->dns_queries, q); + q->manager->n_dns_queries--; + } + + free(q); + + return NULL; +} + +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; + DnsResourceKey *key; + bool good = false; + int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + assert(m); + + 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; + + q = new0(DnsQuery, 1); + if (!q) + return -ENOMEM; + + 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; + + /* First dump UTF8 question */ + DNS_QUESTION_FOREACH(key, question_utf8) + log_debug("Looking up RR for %s.", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + + /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */ + DNS_QUESTION_FOREACH(key, question_idna) { + r = dns_question_contains(question_utf8, key); + if (r < 0) + return r; + if (r > 0) + continue; + + log_debug("Looking up IDNA RR for %s.", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + } + + LIST_PREPEND(queries, m->dns_queries, q); + m->n_dns_queries++; + q->manager = m; + + if (ret) + *ret = q; + q = NULL; + + return 0; +} + +int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) { + assert(q); + assert(auxiliary_for); + + /* Ensure 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(!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 + * after calling this function. */ + + q->state = state; + + dns_query_stop(q); + if (q->complete) + q->complete(q); +} + +static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { + DnsQuery *q = userdata; + + assert(s); + assert(q); + + dns_query_complete(q, DNS_TRANSACTION_TIMEOUT); + return 0; +} + +static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; + int r; + + assert(q); + assert(s); + + r = dns_query_candidate_new(&c, q, s); + if (r < 0) + return r; + + /* 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) + goto fail; + if (r > 0) { + /* OK, we need a search domain now. Let's find one for this scope */ + + 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 = dns_query_candidate_setup_transactions(c); + if (r < 0) + goto fail; + + return 0; + +fail: + dns_query_candidate_free(c); + return r; +} + +static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + int r; + + assert(q); + assert(state); + + /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the + * the normal lookup finished. The data from the network hence takes precedence over the data we + * synthesize. (But note that many scopes refuse to resolve certain domain names) */ + + if (!IN_SET(*state, + DNS_TRANSACTION_RCODE_FAILURE, + DNS_TRANSACTION_NO_SERVERS, + DNS_TRANSACTION_TIMEOUT, + DNS_TRANSACTION_ATTEMPTS_MAX_REACHED, + DNS_TRANSACTION_NETWORK_DOWN, + DNS_TRANSACTION_NOT_FOUND)) + return 0; + + r = dns_synthesize_answer( + q->manager, + q->question_utf8, + q->ifindex, + &answer); + + if (r <= 0) + return r; + + dns_query_reset_answer(q); + + q->answer = answer; + answer = NULL; + q->answer_rcode = DNS_RCODE_SUCCESS; + q->answer_protocol = dns_synthesize_protocol(q->flags); + q->answer_family = dns_synthesize_family(q->flags); + q->answer_authenticated = true; + + *state = DNS_TRANSACTION_SUCCESS; + + return 1; +} + +static int dns_query_try_etc_hosts(DnsQuery *q) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + int r; + + assert(q); + + /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The + * data from /etc/hosts hence takes precedence over the network. */ + + r = manager_etc_hosts_lookup( + q->manager, + q->question_utf8, + &answer); + if (r <= 0) + return r; + + dns_query_reset_answer(q); + + q->answer = answer; + answer = NULL; + q->answer_rcode = DNS_RCODE_SUCCESS; + q->answer_protocol = dns_synthesize_protocol(q->flags); + q->answer_family = dns_synthesize_family(q->flags); + q->answer_authenticated = true; + + return 1; +} + +int dns_query_go(DnsQuery *q) { + DnsScopeMatch found = DNS_SCOPE_NO; + DnsScope *s, *first = NULL; + DnsQueryCandidate *c; + int r; + + assert(q); + + if (q->state != DNS_TRANSACTION_NULL) + return 0; + + r = dns_query_try_etc_hosts(q); + if (r < 0) + return r; + if (r > 0) { + dns_query_complete(q, DNS_TRANSACTION_SUCCESS); + return 1; + } + + 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) + return match; + + if (match == DNS_SCOPE_NO) + continue; + + found = match; + + if (match == DNS_SCOPE_YES) { + first = s; + break; + } else { + assert(match == DNS_SCOPE_MAYBE); + + if (!first) + first = s; + } + } + + if (found == DNS_SCOPE_NO) { + DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; + + r = dns_query_synthesize_reply(q, &state); + if (r < 0) + return r; + + dns_query_complete(q, state); + return 1; + } + + 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) + goto fail; + + if (match != found) + continue; + + r = dns_query_add_candidate(q, s); + if (r < 0) + goto fail; + } + + dns_query_reset_answer(q); + + r = sd_event_add_time( + q->manager->event, + &q->timeout_event_source, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0, + on_query_timeout, 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 */ + 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--; + dns_query_ready(q); + + return 1; + +fail: + dns_query_stop(q); + return r; +} + +static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { + DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; + 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); + + if (!c) { + r = dns_query_synthesize_reply(q, &state); + if (r < 0) + goto fail; + + dns_query_complete(q, state); + return; + } + + if (c->error_code != 0) { + /* If the candidate had an error condition of its own, start with that. */ + state = DNS_TRANSACTION_ERRNO; + q->answer = dns_answer_unref(q->answer); + q->answer_rcode = 0; + q->answer_dnssec_result = _DNSSEC_RESULT_INVALID; + q->answer_errno = c->error_code; + } + + SET_FOREACH(t, c->transactions, i) { + + switch (t->state) { + + case DNS_TRANSACTION_SUCCESS: { + /* We found a successfully reply, merge it into the answer */ + r = dns_answer_extend(&q->answer, t->answer); + if (r < 0) + goto fail; + + q->answer_rcode = t->answer_rcode; + q->answer_errno = 0; + + if (t->answer_authenticated) { + has_authenticated = true; + dnssec_result_authenticated = t->answer_dnssec_result; + } else { + has_non_authenticated = true; + dnssec_result_non_authenticated = t->answer_dnssec_result; + } + + state = DNS_TRANSACTION_SUCCESS; + break; + } + + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_VALIDATING: + case DNS_TRANSACTION_ABORTED: + /* Ignore transactions that didn't complete */ + continue; + + default: + /* Any kind of failure? Store the data away, + * if there's nothing stored yet. */ + + if (state == DNS_TRANSACTION_SUCCESS) + continue; + + q->answer = dns_answer_unref(q->answer); + q->answer_rcode = t->answer_rcode; + q->answer_dnssec_result = t->answer_dnssec_result; + q->answer_errno = t->answer_errno; + + 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; + } + + q->answer_protocol = c->scope->protocol; + q->answer_family = c->scope->family; + + dns_search_domain_unref(q->answer_search_domain); + q->answer_search_domain = dns_search_domain_ref(c->search_domain); + + r = dns_query_synthesize_reply(q, &state); + if (r < 0) + goto fail; + + dns_query_complete(q, state); + return; + +fail: + q->answer_errno = -r; + dns_query_complete(q, DNS_TRANSACTION_ERRNO); +} + +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; + + 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; + + default: + /* Any kind of failure */ + bad = c; + break; + } + } + + if (pending) + return; + + dns_query_accept(q, bad); +} + +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_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)); + + 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)); + } + + if (r == 0 && k == 0) /* No actual cname happened? */ + return -ELOOP; + + if (q->answer_protocol == DNS_PROTOCOL_DNS) { + /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources + * cannot invade the local namespace. The opposite way we permit: local names may redirect to global + * ones. */ + + q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */ + } + + /* Turn off searching for the new name */ + q->flags |= SD_RESOLVED_NO_SEARCH; + + 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; + + 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; + + assert(t); + assert(q); + + log_debug("Client of active query vanished, aborting query."); + dns_query_complete(q, DNS_TRANSACTION_ABORTED); + return 0; +} + +int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { + int r; + + assert(q); + assert(m); + + if (!q->bus_track) { + r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q); + if (r < 0) + return r; + } + + r = sd_bus_track_add_sender(q->bus_track, m); + if (r < 0) + return r; + + 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/grp-resolve/systemd-resolved/resolved-dns-query.h b/src/grp-resolve/systemd-resolved/resolved-dns-query.h new file mode 100644 index 0000000000..a501cb1203 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-query.h @@ -0,0 +1,141 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include + +#include "basic/set.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-question.h" + +typedef struct DnsQuery DnsQuery; +typedef struct DnsQueryCandidate DnsQueryCandidate; + +#include "resolved-dns-search-domain.h" +#include "resolved-dns-stream.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; + + /* 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; + + /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address + * family */ + bool suppress_unroutable_family; + + + /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */ + bool clamp_ttl; + + DnsTransactionState state; + unsigned n_cname_redirects; + + LIST_HEAD(DnsQueryCandidate, candidates); + sd_event_source *timeout_event_source; + + /* Discovered data */ + DnsAnswer *answer; + int answer_rcode; + DnssecResult answer_dnssec_result; + bool answer_authenticated; + DnsProtocol answer_protocol; + int answer_family; + DnsSearchDomain *answer_search_domain; + int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ + + /* Bus client information */ + sd_bus_message *request; + int request_family; + bool request_address_valid; + union in_addr_union request_address; + unsigned block_all_complete; + char *request_address_string; + + /* DNS stub information */ + DnsPacket *request_dns_packet; + DnsStream *request_dns_stream; + + /* Completion callback */ + void (*complete)(DnsQuery* q); + unsigned block_ready; + + sd_bus_track *bus_track; + + LIST_FIELDS(DnsQuery, queries); + LIST_FIELDS(DnsQuery, auxiliary_queries); +}; + +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_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/grp-resolve/systemd-resolved/resolved-dns-scope.c b/src/grp-resolve/systemd-resolved/resolved-dns-scope.c new file mode 100644 index 0000000000..9da091b5e9 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-scope.c @@ -0,0 +1,1038 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hostname-util.h" +#include "basic/missing.h" +#include "basic/random-util.h" +#include "basic/socket-util.h" +#include "basic/strv.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-scope.h" +#include "resolved-llmnr.h" +#include "resolved-mdns.h" + +#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC) +#define MULTICAST_RATELIMIT_BURST 1000 + +/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */ +#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC) +#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC) + +int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) { + DnsScope *s; + + assert(m); + assert(ret); + + s = new0(DnsScope, 1); + if (!s) + return -ENOMEM; + + s->manager = m; + s->link = l; + s->protocol = protocol; + s->family = family; + s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC; + + 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); + } else + s->dnssec_mode = DNSSEC_NO; + + 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)); + + /* Enforce ratelimiting for the multicast protocols */ + RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST); + + *ret = s; + return 0; +} + +static void dns_scope_abort_transactions(DnsScope *s) { + assert(s); + + while (s->transactions) { + DnsTransaction *t = s->transactions; + + /* Abort the transaction, but make sure it is not + * freed while we still look at it */ + + t->block_gc++; + 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; + + 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); + + ordered_hashmap_free(s->conflict_queue); + sd_event_source_unref(s->conflict_event_source); + + dns_cache_flush(&s->cache); + dns_zone_flush(&s->zone); + + LIST_REMOVE(scopes, s->manager->dns_scopes, s); + free(s); + + return NULL; +} + +DnsServer *dns_scope_get_dns_server(DnsScope *s) { + assert(s); + + if (s->protocol != DNS_PROTOCOL_DNS) + return NULL; + + if (s->link) + return link_get_dns_server(s->link); + else + return manager_get_dns_server(s->manager); +} + +void dns_scope_next_dns_server(DnsScope *s) { + assert(s); + + if (s->protocol != DNS_PROTOCOL_DNS) + return; + + if (s->link) + link_next_dns_server(s->link); + else + manager_next_dns_server(s->manager); +} + +void dns_scope_packet_received(DnsScope *s, usec_t rtt) { + assert(s); + + 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) { + assert(s); + + if (s->resend_timeout <= usec) + s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC); +} + +static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { + union in_addr_union addr; + int ifindex = 0, r; + int family; + uint32_t mtu; + + assert(s); + assert(p); + assert(p->protocol == s->protocol); + + if (s->link) { + mtu = s->link->mtu; + ifindex = s->link->ifindex; + } else + mtu = manager_find_mtu(s->manager); + + switch (s->protocol) { + + case DNS_PROTOCOL_DNS: + assert(fd >= 0); + + if (DNS_PACKET_QDCOUNT(p) > 1) + return -EOPNOTSUPP; + + if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) + return -EMSGSIZE; + + if (p->size + UDP_PACKET_HEADER_SIZE > mtu) + return -EMSGSIZE; + + r = manager_write(s->manager, fd, p); + if (r < 0) + return r; + + break; + + case DNS_PROTOCOL_LLMNR: + assert(fd < 0); + + if (DNS_PACKET_QDCOUNT(p) > 1) + return -EOPNOTSUPP; + + if (!ratelimit_test(&s->ratelimit)) + return -EBUSY; + + family = s->family; + + if (family == AF_INET) { + addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; + fd = manager_llmnr_ipv4_udp_fd(s->manager); + } else if (family == AF_INET6) { + addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS; + fd = manager_llmnr_ipv6_udp_fd(s->manager); + } else + return -EAFNOSUPPORT; + if (fd < 0) + return fd; + + r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, 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, NULL, p); + if (r < 0) + return r; + + break; + + default: + return -EAFNOSUPPORT; + } + + return 1; +} + +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; + static const int one = 1; + int ret, r, ifindex; + + assert(s); + + if (server) { + assert(family == AF_UNSPEC); + assert(!address); + + ifindex = dns_server_ifindex(server); + + sa.sa.sa_family = server->family; + if (server->family == AF_INET) { + sa.in.sin_port = htobe16(port); + sa.in.sin_addr = server->address.in; + salen = sizeof(sa.in); + } else if (server->family == AF_INET6) { + sa.in6.sin6_port = htobe16(port); + sa.in6.sin6_addr = server->address.in6; + sa.in6.sin6_scope_id = ifindex; + salen = sizeof(sa.in6); + } else + return -EAFNOSUPPORT; + } else { + assert(family != AF_UNSPEC); + assert(address); + + sa.sa.sa_family = family; + ifindex = s->link ? s->link->ifindex : 0; + + if (family == AF_INET) { + sa.in.sin_port = htobe16(port); + sa.in.sin_addr = address->in; + salen = sizeof(sa.in); + } else if (family == AF_INET6) { + sa.in6.sin6_port = htobe16(port); + sa.in6.sin6_addr = address->in6; + sa.in6.sin6_scope_id = ifindex; + salen = sizeof(sa.in6); + } else + return -EAFNOSUPPORT; + } + + fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + if (type == SOCK_STREAM) { + r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + if (r < 0) + return -errno; + } + + if (s->link) { + be32_t ifindex_be = htobe32(ifindex); + + if (sa.sa.sa_family == AF_INET) { + r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); + if (r < 0) + return -errno; + } else if (sa.sa.sa_family == AF_INET6) { + r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); + if (r < 0) + return -errno; + } + } + + if (s->protocol == DNS_PROTOCOL_LLMNR) { + /* RFC 4795, section 2.5 requires the TTL to be set to 1 */ + + if (sa.sa.sa_family == AF_INET) { + r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); + if (r < 0) + return -errno; + } else if (sa.sa.sa_family == AF_INET6) { + r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); + if (r < 0) + return -errno; + } + } + + r = connect(fd, &sa.sa, salen); + if (r < 0 && errno != EINPROGRESS) + return -errno; + + ret = fd; + fd = -1; + + return ret; +} + +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_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) { + DnsSearchDomain *d; + + assert(s); + assert(domain); + + /* 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 (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) + return DNS_SCOPE_NO; + + 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, + * LLMNR or mDNS. Instead, always rely on synthesized RRs for + * these. */ + if (is_localhost(domain) || + dns_name_endswith(domain, "127.in-addr.arpa") > 0 || + 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; + + /* 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, "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; + + case DNS_PROTOCOL_MDNS: + 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_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ + dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */ + manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */ + return DNS_SCOPE_MAYBE; + + return DNS_SCOPE_NO; + + 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_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; + + return DNS_SCOPE_NO; + + default: + assert_not_reached("Unknown scope protocol"); + } +} + +bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) { + int key_family; + + assert(s); + assert(key); + + /* 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 (key->class != DNS_CLASS_IN) + return false; + + 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 */ + + key_family = dns_type_to_af(key->type); + if (key_family < 0) + return true; + + return key_family == s->family; +} + +static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) { + int fd; + + assert(s); + assert(s->link); + + if (s->family == AF_INET) { + struct ip_mreqn mreqn = { + .imr_multiaddr = in, + .imr_ifindex = s->link->ifindex, + }; + + fd = manager_llmnr_ipv4_udp_fd(s->manager); + if (fd < 0) + return fd; + + /* Always first try to drop membership before we add + * one. This is necessary on some devices, such as + * veth. */ + if (b) + (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); + + if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) + return -errno; + + } else if (s->family == AF_INET6) { + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = in6, + .ipv6mr_interface = s->link->ifindex, + }; + + fd = manager_llmnr_ipv6_udp_fd(s->manager); + if (fd < 0) + return fd; + + if (b) + (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + + if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + return -errno; + } else + return -EAFNOSUPPORT; + + return 0; +} + +int dns_scope_llmnr_membership(DnsScope *s, bool b) { + assert(s); + + 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) { + assert(s); + + 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, + int rcode, + DnsQuestion *q, + DnsAnswer *answer, + DnsAnswer *soa, + bool tentative, + DnsPacket **ret) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + assert(s); + assert(ret); + + if (dns_question_isempty(q) && + dns_answer_isempty(answer) && + dns_answer_isempty(soa)) + return -EINVAL; + + r = dns_packet_new(&p, s->protocol, 0); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->id = id; + DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( + 1 /* qr */, + 0 /* opcode */, + 0 /* c */, + 0 /* tc */, + tentative, + 0 /* (ra) */, + 0 /* (ad) */, + 0 /* (cd) */, + rcode)); + + r = dns_packet_append_question(p, q); + if (r < 0) + return r; + DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); + + r = dns_packet_append_answer(p, answer); + if (r < 0) + return r; + DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer)); + + r = dns_packet_append_answer(p, soa); + if (r < 0) + return r; + DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa)); + + *ret = p; + p = NULL; + + return 0; +} + +static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { + DnsResourceRecord *rr; + DnsResourceKey *key; + + assert(s); + assert(p); + + DNS_QUESTION_FOREACH(key, p->question) + dns_zone_verify_conflicts(&s->zone, key); + + DNS_ANSWER_FOREACH(rr, p->answer) + dns_zone_verify_conflicts(&s->zone, rr->key); +} + +void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; + DnsResourceKey *key = NULL; + bool tentative = false; + int r; + + assert(s); + assert(p); + + if (p->protocol != DNS_PROTOCOL_LLMNR) + return; + + if (p->ipproto == IPPROTO_UDP) { + /* Don't accept UDP queries directed to anything but + * the LLMNR multicast addresses. See RFC 4795, + * section 2.5. */ + + if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS)) + return; + + if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS)) + return; + } + + r = dns_packet_extract(p); + if (r < 0) { + log_debug_errno(r, "Failed to extract resource records from incoming packet: %m"); + return; + } + + if (DNS_PACKET_LLMNR_C(p)) { + /* Somebody notified us about a possible conflict */ + dns_scope_verify_conflicts(s, p); + return; + } + + assert(dns_question_size(p->question) == 1); + key = p->question->keys[0]; + + r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); + if (r < 0) { + log_debug_errno(r, "Failed to lookup key: %m"); + return; + } + if (r == 0) + return; + + if (answer) + dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0); + + r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply); + if (r < 0) { + log_debug_errno(r, "Failed to build reply packet: %m"); + return; + } + + if (stream) { + r = dns_stream_write_packet(stream, reply); + if (r < 0) { + log_debug_errno(r, "Failed to enqueue reply packet: %m"); + return; + } + + /* Let's take an extra reference on this stream, so that it stays around after returning. The reference + * will be dangling until the stream is disconnected, and the default completion handler of the stream + * will then unref the stream and destroy it */ + if (DNS_STREAM_QUEUED(stream)) + dns_stream_ref(stream); + } else { + int fd; + + if (!ratelimit_test(&s->ratelimit)) + return; + + if (p->family == AF_INET) + fd = manager_llmnr_ipv4_udp_fd(s->manager); + else if (p->family == AF_INET6) + fd = manager_llmnr_ipv6_udp_fd(s->manager); + else { + log_debug("Unknown protocol"); + return; + } + if (fd < 0) { + log_debug_errno(fd, "Failed to get reply socket: %m"); + return; + } + + /* Note that we always immediately reply to all LLMNR + * requests, and do not wait any time, since we + * verified uniqueness for all records. Also see RFC + * 4795, Section 2.7 */ + + r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, NULL, reply); + if (r < 0) { + log_debug_errno(r, "Failed to send reply packet: %m"); + return; + } + } +} + +DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok) { + DnsTransaction *t; + + assert(scope); + assert(key); + + /* Try to find an ongoing transaction that is a equal to the + * specified question */ + 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_RCODE_FAILURE) && + t->answer_source != DNS_TRANSACTION_NETWORK) + return NULL; + + return t; +} + +static int dns_scope_make_conflict_packet( + DnsScope *s, + DnsResourceRecord *rr, + DnsPacket **ret) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + + assert(s); + assert(rr); + assert(ret); + + r = dns_packet_new(&p, s->protocol, 0); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( + 0 /* qr */, + 0 /* opcode */, + 1 /* conflict */, + 0 /* tc */, + 0 /* t */, + 0 /* (ra) */, + 0 /* (ad) */, + 0 /* (cd) */, + 0)); + + /* 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); + + r = dns_packet_append_key(p, rr->key, NULL); + if (r < 0) + return r; + + r = dns_packet_append_rr(p, rr, NULL, NULL); + if (r < 0) + return r; + + *ret = p; + p = NULL; + + return 0; +} + +static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) { + DnsScope *scope = userdata; + int r; + + assert(es); + assert(scope); + + scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source); + + for (;;) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + + rr = ordered_hashmap_steal_first(scope->conflict_queue); + if (!rr) + break; + + r = dns_scope_make_conflict_packet(scope, rr, &p); + if (r < 0) { + log_error_errno(r, "Failed to make conflict packet: %m"); + return 0; + } + + r = dns_scope_emit_udp(scope, -1, p); + if (r < 0) + log_debug_errno(r, "Failed to send conflict packet: %m"); + } + + return 0; +} + +int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { + usec_t jitter; + int r; + + assert(scope); + assert(rr); + + /* We don't send these queries immediately. Instead, we queue + * them, and send them after some jitter delay. */ + r = ordered_hashmap_ensure_allocated(&scope->conflict_queue, &dns_resource_key_hash_ops); + if (r < 0) { + log_oom(); + return r; + } + + /* We only place one RR per key in the conflict + * messages, not all of them. That should be enough to + * indicate where there might be a conflict */ + r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) + return log_debug_errno(r, "Failed to queue conflicting RR: %m"); + + dns_resource_record_ref(rr); + + if (scope->conflict_event_source) + return 0; + + random_bytes(&jitter, sizeof(jitter)); + jitter %= LLMNR_JITTER_INTERVAL_USEC; + + r = sd_event_add_time(scope->manager->event, + &scope->conflict_event_source, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + jitter, + LLMNR_JITTER_INTERVAL_USEC, + on_conflict_dispatch, scope); + 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; +} + +void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { + unsigned i; + int r; + + assert(scope); + assert(p); + + if (p->protocol != DNS_PROTOCOL_LLMNR) + return; + + if (DNS_PACKET_RRCOUNT(p) <= 0) + return; + + if (DNS_PACKET_LLMNR_C(p) != 0) + return; + + if (DNS_PACKET_LLMNR_T(p) != 0) + return; + + if (manager_our_packet(scope->manager, p)) + return; + + r = dns_packet_extract(p); + if (r < 0) { + log_debug_errno(r, "Failed to extract packet: %m"); + return; + } + + log_debug("Checking for conflicts..."); + + for (i = 0; i < p->answer->n_rrs; i++) { + + /* Check for conflicts against the local zone. If we + * found one, we won't check any further */ + r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr); + if (r != 0) + continue; + + /* Check for conflicts against the local cache. If so, + * send out an advisory query, to inform everybody */ + r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender); + if (r <= 0) + continue; + + dns_scope_notify_conflict(scope, p->answer->items[i].rr); + } +} + +void dns_scope_dump(DnsScope *s, FILE *f) { + assert(s); + + if (!f) + f = stdout; + + fputs("[Scope protocol=", f); + fputs(dns_protocol_to_string(s->protocol), f); + + if (s->link) { + fputs(" interface=", f); + fputs(s->link->name, f); + } + + if (s->family != AF_UNSPEC) { + fputs(" family=", f); + fputs(af_to_name(s->family), f); + } + + fputs("]\n", f); + + if (!dns_zone_is_empty(&s->zone)) { + fputs("ZONE:\n", f); + dns_zone_dump(&s->zone, f); + } + + if (!dns_cache_is_empty(&s->cache)) { + fputs("CACHE:\n", 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); +} + +bool dns_scope_network_good(DnsScope *s) { + /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes + * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global + * DNS scope we check whether there are any links that are up and have an address. */ + + if (s->link) + return true; + + return manager_routable(s->manager, AF_UNSPEC); +} + +int dns_scope_ifindex(DnsScope *s) { + assert(s); + + if (s->link) + return s->link->ifindex; + + return 0; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-scope.h b/src/grp-resolve/systemd-resolved/resolved-dns-scope.h new file mode 100644 index 0000000000..4a6842013a --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-scope.h @@ -0,0 +1,111 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" +#include "resolved-dns-dnssec.h" +#include "resolved-dns-packet.h" + +typedef struct DnsScope DnsScope; + +#include "resolved-dns-cache.h" +#include "resolved-dns-server.h" +#include "resolved-dns-zone.h" +#include "resolved-link.h" + +typedef enum DnsScopeMatch { + DNS_SCOPE_NO, + DNS_SCOPE_MAYBE, + DNS_SCOPE_YES, + _DNS_SCOPE_MATCH_MAX, + _DNS_SCOPE_INVALID = -1 +} DnsScopeMatch; + +struct DnsScope { + Manager *manager; + + DnsProtocol protocol; + int family; + DnssecMode dnssec_mode; + + Link *link; + + DnsCache cache; + DnsZone zone; + + OrderedHashmap *conflict_queue; + sd_event_source *conflict_event_source; + + RateLimit ratelimit; + + usec_t resend_timeout; + usec_t max_rtt; + + 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); +}; + +int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); +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_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); +bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key); + +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); + +DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok); + +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); + +bool dns_scope_network_good(DnsScope *s); + +int dns_scope_ifindex(DnsScope *s); diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-search-domain.c b/src/grp-resolve/systemd-resolved/resolved-dns-search-domain.c new file mode 100644 index 0000000000..7798c498a0 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-search-domain.c @@ -0,0 +1,228 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "shared/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 (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/grp-resolve/systemd-resolved/resolved-dns-search-domain.h b/src/grp-resolve/systemd-resolved/resolved-dns-search-domain.h new file mode 100644 index 0000000000..f047941db2 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-search-domain.h @@ -0,0 +1,74 @@ +#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 . +***/ + +#include "basic/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 route_only: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/grp-resolve/systemd-resolved/resolved-dns-server.c b/src/grp-resolve/systemd-resolved/resolved-dns-server.c new file mode 100644 index 0000000000..bc97d89219 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-server.c @@ -0,0 +1,778 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/siphash24.h" +#include "basic/string-table.h" +#include "basic/string-util.h" + +#include "resolved-dns-server.h" +#include "resolved-dns-stub.h" +#include "resolved-resolv-conf.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, + DnsServerType type, + Link *l, + int family, + const union in_addr_union *in_addr, + int ifindex) { + + 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->ifindex = ifindex; + s->resend_timeout = DNS_TIMEOUT_MIN_USEC; + + switch (type) { + + case DNS_SERVER_LINK: + s->link = l; + 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->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 + * the new one */ + if (type != DNS_SERVER_FALLBACK && + m->current_dns_server && + m->current_dns_server->type == DNS_SERVER_FALLBACK) + manager_set_dns_server(m, NULL); + + if (ret) + *ret = s; + + return 0; +} + +DnsServer* dns_server_ref(DnsServer *s) { + if (!s) + return NULL; + + assert(s->n_ref > 0); + s->n_ref++; + + return 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); + s->link->n_dns_servers--; + 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->current_dns_server == s) + manager_set_dns_server(s->manager, NULL); + + dns_server_unref(s); +} + +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"); + } +} + +static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + if (s->verified_feature_level > level) + return; + + 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; + } + + assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); +} + +static void dns_server_reset_counters(DnsServer *s) { + assert(s); + + s->n_failed_udp = 0; + s->n_failed_tcp = 0; + 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. */ +} + +void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) { + assert(s); + + 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 = 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_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_bad_opt(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + 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; +} + +void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + /* Invoked whenever we got a FORMERR, SERVFAIL or NOTIMP rcode from a server and downgrading the feature level + * for the transaction made it go away. In this case we immediately downgrade to the feature level that made + * things work. */ + + if (s->verified_feature_level > level) + s->verified_feature_level = level; + + if (s->possible_feature_level > level) { + s->possible_feature_level = level; + dns_server_reset_counters(s); + } + + log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s)); +} + +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; +} + +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->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, 0, NULL); +} + +int dns_server_ifindex(const DnsServer *s) { + assert(s); + + /* The link ifindex always takes precedence */ + if (s->link) + return s->link->ifindex; + + if (s->ifindex > 0) + return s->ifindex; + + return 0; +} + +const char *dns_server_string(DnsServer *server) { + assert(server); + + if (!server->server_string) + (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &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; +} + +void dns_server_warn_downgrade(DnsServer *server) { + assert(server); + + if (server->warned_downgrade) + return; + + log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE), + LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)), + "DNS_SERVER=%s", dns_server_string(server), + "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level), + NULL); + + server->warned_downgrade = true; +} + +static void dns_server_hash_func(const void *p, struct siphash *state) { + const DnsServer *s = p; + + assert(s); + + siphash24_compress(&s->family, sizeof(s->family), state); + siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state); + siphash24_compress(&s->ifindex, sizeof(s->ifindex), state); +} + +static int dns_server_compare_func(const void *a, const void *b) { + const DnsServer *x = a, *y = b; + int r; + + if (x->family < y->family) + return -1; + if (x->family > y->family) + return 1; + + r = memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); + if (r != 0) + return r; + + if (x->ifindex < y->ifindex) + return -1; + if (x->ifindex > y->ifindex) + return 1; + + return 0; +} + +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, int ifindex) { + DnsServer *s; + + LIST_FOREACH(servers, s, first) + if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex) + 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 %s DNS server %s.", + dns_server_type_to_string(s->type), + 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 chosen 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); +} + +bool dns_server_address_valid(int family, const union in_addr_union *sa) { + + /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */ + + if (in_addr_is_null(family, sa)) + return false; + + if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB)) + return false; + + return true; +} + +static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { + [DNS_SERVER_SYSTEM] = "system", + [DNS_SERVER_FALLBACK] = "fallback", + [DNS_SERVER_LINK] = "link", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType); + +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/grp-resolve/systemd-resolved/resolved-dns-server.h b/src/grp-resolve/systemd-resolved/resolved-dns-server.h new file mode 100644 index 0000000000..9e216fb991 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-server.h @@ -0,0 +1,147 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/in-addr-util.h" + +typedef struct DnsServer DnsServer; + +typedef enum DnsServerType { + DNS_SERVER_SYSTEM, + DNS_SERVER_FALLBACK, + DNS_SERVER_LINK, +} DnsServerType; +#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1) + +const char* dns_server_type_to_string(DnsServerType i) _const_; +DnsServerType dns_server_type_from_string(const char *s) _pure_; + +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; + + unsigned n_ref; + + DnsServerType type; + Link *link; + + int family; + union in_addr_union address; + int ifindex; /* for IPv6 link-local DNS servers */ + + 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_truncated:1; + bool packet_bad_opt:1; + bool packet_rrsig_missing:1; + + usec_t verified_usec; + usec_t features_grace_period_usec; + + /* Whether we already warned about downgrading to non-DNSSEC mode for this server */ + bool warned_downgrade:1; + + /* 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 **ret, + DnsServerType type, + Link *link, + int family, + const union in_addr_union *address, + int ifindex); + +DnsServer* dns_server_ref(DnsServer *s); +DnsServer* dns_server_unref(DnsServer *s); + +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_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); +void dns_server_packet_rcode_downgrade(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); +int dns_server_ifindex(const DnsServer *s); + +bool dns_server_dnssec_supported(DnsServer *server); + +void dns_server_warn_downgrade(DnsServer *server); + +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex); + +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); + +bool dns_server_address_valid(int family, const union in_addr_union *sa); + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); + +extern const struct hash_ops dns_server_hash_ops; diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-stream.c b/src/grp-resolve/systemd-resolved/resolved-dns-stream.c new file mode 100644 index 0000000000..4ba5b797b7 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-stream.c @@ -0,0 +1,421 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/missing.h" + +#include "resolved-dns-stream.h" + +#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC) +#define DNS_STREAMS_MAX 128 + +static void dns_stream_stop(DnsStream *s) { + assert(s); + + s->io_event_source = sd_event_source_unref(s->io_event_source); + s->timeout_event_source = sd_event_source_unref(s->timeout_event_source); + s->fd = safe_close(s->fd); +} + +static int dns_stream_update_io(DnsStream *s) { + int f = 0; + + assert(s); + + if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size) + f |= EPOLLOUT; + if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) + f |= EPOLLIN; + + return sd_event_source_set_io_events(s->io_event_source, f); +} + +static int dns_stream_complete(DnsStream *s, int error) { + assert(s); + + dns_stream_stop(s); + + if (s->complete) + s->complete(s, error); + else /* the default action if no completion function is set is to close the stream */ + dns_stream_unref(s); + + return 0; +} + +static int dns_stream_identify(DnsStream *s) { + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) + + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; + } control; + struct msghdr mh = {}; + struct cmsghdr *cmsg; + socklen_t sl; + int r; + + assert(s); + + if (s->identified) + return 0; + + /* Query the local side */ + s->local_salen = sizeof(s->local); + r = getsockname(s->fd, &s->local.sa, &s->local_salen); + if (r < 0) + return -errno; + if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0) + s->ifindex = s->local.in6.sin6_scope_id; + + /* Query the remote side */ + s->peer_salen = sizeof(s->peer); + r = getpeername(s->fd, &s->peer.sa, &s->peer_salen); + if (r < 0) + return -errno; + if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0) + s->ifindex = s->peer.in6.sin6_scope_id; + + /* Check consistency */ + assert(s->peer.sa.sa_family == s->local.sa.sa_family); + assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); + + /* Query connection meta information */ + sl = sizeof(control); + if (s->peer.sa.sa_family == AF_INET) { + r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl); + if (r < 0) + return -errno; + } else if (s->peer.sa.sa_family == AF_INET6) { + + r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl); + if (r < 0) + return -errno; + } else + return -EAFNOSUPPORT; + + mh.msg_control = &control; + mh.msg_controllen = sl; + + CMSG_FOREACH(cmsg, &mh) { + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + assert(s->peer.sa.sa_family == AF_INET6); + + switch (cmsg->cmsg_type) { + + case IPV6_PKTINFO: { + struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (s->ifindex <= 0) + s->ifindex = i->ipi6_ifindex; + break; + } + + case IPV6_HOPLIMIT: + s->ttl = *(int *) CMSG_DATA(cmsg); + break; + } + + } else if (cmsg->cmsg_level == IPPROTO_IP) { + assert(s->peer.sa.sa_family == AF_INET); + + switch (cmsg->cmsg_type) { + + case IP_PKTINFO: { + struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (s->ifindex <= 0) + s->ifindex = i->ipi_ifindex; + break; + } + + case IP_TTL: + s->ttl = *(int *) CMSG_DATA(cmsg); + break; + } + } + } + + /* The Linux kernel sets the interface index to the loopback + * device if the connection came from the local host since it + * avoids the routing table in such a case. Let's unset the + * interface index in such a case. */ + if (s->ifindex == LOOPBACK_IFINDEX) + s->ifindex = 0; + + /* If we don't know the interface index still, we look for the + * first local interface with a matching address. Yuck! */ + if (s->ifindex <= 0) + s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr); + + if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) { + uint32_t ifindex = htobe32(s->ifindex); + + /* Make sure all packets for this connection are sent on the same interface */ + if (s->local.sa.sa_family == AF_INET) { + r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); + if (r < 0) + log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m"); + } else if (s->local.sa.sa_family == AF_INET6) { + r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); + if (r < 0) + log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m"); + } + } + + s->identified = true; + + return 0; +} + +static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) { + DnsStream *s = userdata; + + assert(s); + + return dns_stream_complete(s, ETIMEDOUT); +} + +static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + DnsStream *s = userdata; + int r; + + assert(s); + + r = dns_stream_identify(s); + if (r < 0) + return dns_stream_complete(s, -r); + + if ((revents & EPOLLOUT) && + s->write_packet && + s->n_written < sizeof(s->write_size) + s->write_packet->size) { + + struct iovec iov[2]; + ssize_t ss; + + iov[0].iov_base = &s->write_size; + iov[0].iov_len = sizeof(s->write_size); + iov[1].iov_base = DNS_PACKET_DATA(s->write_packet); + iov[1].iov_len = s->write_packet->size; + + IOVEC_INCREMENT(iov, 2, s->n_written); + + ss = writev(fd, iov, 2); + if (ss < 0) { + if (errno != EINTR && errno != EAGAIN) + return dns_stream_complete(s, errno); + } else + s->n_written += ss; + + /* Are we done? If so, disable the event source for EPOLLOUT */ + if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) { + r = dns_stream_update_io(s); + if (r < 0) + return dns_stream_complete(s, -r); + } + } + + if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) && + (!s->read_packet || + s->n_read < sizeof(s->read_size) + s->read_packet->size)) { + + if (s->n_read < sizeof(s->read_size)) { + ssize_t ss; + + ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read); + if (ss < 0) { + if (errno != EINTR && errno != EAGAIN) + return dns_stream_complete(s, errno); + } else if (ss == 0) + return dns_stream_complete(s, ECONNRESET); + else + s->n_read += ss; + } + + if (s->n_read >= sizeof(s->read_size)) { + + if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE) + return dns_stream_complete(s, EBADMSG); + + if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) { + ssize_t ss; + + if (!s->read_packet) { + r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size)); + if (r < 0) + return dns_stream_complete(s, -r); + + s->read_packet->size = be16toh(s->read_size); + s->read_packet->ipproto = IPPROTO_TCP; + s->read_packet->family = s->peer.sa.sa_family; + s->read_packet->ttl = s->ttl; + s->read_packet->ifindex = s->ifindex; + + if (s->read_packet->family == AF_INET) { + s->read_packet->sender.in = s->peer.in.sin_addr; + s->read_packet->sender_port = be16toh(s->peer.in.sin_port); + s->read_packet->destination.in = s->local.in.sin_addr; + s->read_packet->destination_port = be16toh(s->local.in.sin_port); + } else { + assert(s->read_packet->family == AF_INET6); + s->read_packet->sender.in6 = s->peer.in6.sin6_addr; + s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port); + s->read_packet->destination.in6 = s->local.in6.sin6_addr; + s->read_packet->destination_port = be16toh(s->local.in6.sin6_port); + + if (s->read_packet->ifindex == 0) + s->read_packet->ifindex = s->peer.in6.sin6_scope_id; + if (s->read_packet->ifindex == 0) + s->read_packet->ifindex = s->local.in6.sin6_scope_id; + } + } + + ss = read(fd, + (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size), + sizeof(s->read_size) + be16toh(s->read_size) - s->n_read); + if (ss < 0) { + if (errno != EINTR && errno != EAGAIN) + return dns_stream_complete(s, errno); + } else if (ss == 0) + return dns_stream_complete(s, ECONNRESET); + else + s->n_read += ss; + } + + /* Are we done? If so, disable the event source for EPOLLIN */ + if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) { + r = dns_stream_update_io(s); + if (r < 0) + return dns_stream_complete(s, -r); + + /* If there's a packet handler + * installed, call that. Note that + * this is optional... */ + if (s->on_packet) + return s->on_packet(s); + } + } + } + + if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && + (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) + return dns_stream_complete(s, 0); + + return 0; +} + +DnsStream *dns_stream_unref(DnsStream *s) { + if (!s) + return NULL; + + assert(s->n_ref > 0); + s->n_ref--; + + if (s->n_ref > 0) + return NULL; + + dns_stream_stop(s); + + if (s->manager) { + LIST_REMOVE(streams, s->manager->dns_streams, s); + s->manager->n_dns_streams--; + } + + dns_packet_unref(s->write_packet); + dns_packet_unref(s->read_packet); + + free(s); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref); + +DnsStream *dns_stream_ref(DnsStream *s) { + if (!s) + return NULL; + + assert(s->n_ref > 0); + s->n_ref++; + + return s; +} + +int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) { + _cleanup_(dns_stream_unrefp) DnsStream *s = NULL; + int r; + + assert(m); + assert(fd >= 0); + + if (m->n_dns_streams > DNS_STREAMS_MAX) + return -EBUSY; + + s = new0(DnsStream, 1); + if (!s) + return -ENOMEM; + + s->n_ref = 1; + s->fd = -1; + s->protocol = protocol; + + 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, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0, + on_stream_timeout, s); + 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; + m->n_dns_streams++; + + *ret = s; + s = NULL; + + return 0; +} + +int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { + assert(s); + + if (s->write_packet) + return -EBUSY; + + s->write_packet = dns_packet_ref(p); + s->write_size = htobe16(p->size); + s->n_written = 0; + + return dns_stream_update_io(s); +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-stream.h b/src/grp-resolve/systemd-resolved/resolved-dns-stream.h new file mode 100644 index 0000000000..92b2579fc6 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-stream.h @@ -0,0 +1,80 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/socket-util.h" +#include "resolved-dns-packet.h" + +typedef struct DnsStream DnsStream; + +#include "resolved-dns-transaction.h" + +/* Streams are used by three subsystems: + * + * 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP + * 2. The LLMNR logic when accepting a TCP-based lookup + * 3. The DNS stub logic when accepting a TCP-based lookup + */ + +struct DnsStream { + Manager *manager; + int n_ref; + + DnsProtocol protocol; + + int fd; + union sockaddr_union peer; + socklen_t peer_salen; + union sockaddr_union local; + socklen_t local_salen; + int ifindex; + uint32_t ttl; + bool identified; + + sd_event_source *io_event_source; + sd_event_source *timeout_event_source; + + be16_t write_size, read_size; + DnsPacket *write_packet, *read_packet; + size_t n_written, n_read; + + int (*on_packet)(DnsStream *s); + int (*complete)(DnsStream *s, int error); + + DnsTransaction *transaction; /* when used by the transaction logic */ + DnsQuery *query; /* when used by the DNS stub logic */ + + LIST_FIELDS(DnsStream, streams); +}; + +int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd); +DnsStream *dns_stream_unref(DnsStream *s); +DnsStream *dns_stream_ref(DnsStream *s); + +int dns_stream_write_packet(DnsStream *s, DnsPacket *p); + +static inline bool DNS_STREAM_QUEUED(DnsStream *s) { + assert(s); + + if (s->fd < 0) /* already stopped? */ + return false; + + return !!s->write_packet; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-stub.c b/src/grp-resolve/systemd-resolved/resolved-dns-stub.c new file mode 100644 index 0000000000..520a67a7b4 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-stub.c @@ -0,0 +1,573 @@ +/*** + 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 . +***/ + +#include "basic/fd-util.h" +#include "basic/socket-util.h" + +#include "resolved-dns-stub.h" + +/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet, + * IP and UDP header sizes */ +#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U) + +static int dns_stub_make_reply_packet( + uint16_t id, + int rcode, + DnsQuestion *q, + DnsAnswer *answer, + bool add_opt, /* add an OPT RR to this packet */ + bool edns0_do, /* set the EDNS0 DNSSEC OK bit */ + bool ad, /* set the DNSSEC authenticated data bit */ + DnsPacket **ret) { + + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsResourceRecord *rr; + unsigned c = 0; + int r; + + /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence + * roundtrips aren't expensive. */ + + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return r; + + /* If the client didn't do EDNS, clamp the rcode to 4 bit */ + if (!add_opt && rcode > 0xF) + rcode = DNS_RCODE_SERVFAIL; + + DNS_PACKET_HEADER(p)->id = id; + DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( + 1 /* qr */, + 0 /* opcode */, + 0 /* aa */, + 0 /* tc */, + 1 /* rd */, + 1 /* ra */, + ad /* ad */, + 0 /* cd */, + rcode)); + + r = dns_packet_append_question(p, q); + if (r < 0) + return r; + DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); + + DNS_ANSWER_FOREACH(rr, answer) { + r = dns_question_matches_rr(q, rr, NULL); + if (r < 0) + return r; + if (r > 0) + goto add; + + r = dns_question_matches_cname_or_dname(q, rr, NULL); + if (r < 0) + return r; + if (r > 0) + goto add; + + continue; + add: + r = dns_packet_append_rr(p, rr, NULL, NULL); + if (r < 0) + return r; + + c++; + } + DNS_PACKET_HEADER(p)->ancount = htobe16(c); + + if (add_opt) { + r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); + if (r < 0) + return r; + } + + *ret = p; + p = NULL; + + return 0; +} + +static void dns_stub_detach_stream(DnsStream *s) { + assert(s); + + s->complete = NULL; + s->on_packet = NULL; + s->query = NULL; +} + +static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) { + int r; + + assert(m); + assert(p); + assert(reply); + + if (s) + r = dns_stream_write_packet(s, reply); + else { + int fd; + + /* Truncate the message to the right size */ + if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) { + dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX); + DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC); + } + + fd = manager_dns_stub_udp_fd(m); + if (fd < 0) + return log_debug_errno(fd, "Failed to get reply socket: %m"); + + /* Note that it is essential here that we explicitly choose the source IP address for this packet. This + * is because otherwise the kernel will choose it automatically based on the routing table and will + * thus pick 127.0.0.1 rather than 127.0.0.53. */ + + r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply); + } + if (r < 0) + return log_debug_errno(r, "Failed to send reply packet: %m"); + + return 0; +} + +static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) { + _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; + int r; + + assert(m); + assert(p); + + r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply); + if (r < 0) + return log_debug_errno(r, "Failed to build failure packet: %m"); + + return dns_stub_send(m, s, p, reply); +} + +static void dns_stub_query_complete(DnsQuery *q) { + int r; + + assert(q); + assert(q->request_dns_packet); + + switch (q->state) { + + case DNS_TRANSACTION_SUCCESS: { + _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; + + r = dns_stub_make_reply_packet( + DNS_PACKET_ID(q->request_dns_packet), + q->answer_rcode, + q->question_idna, + q->answer, + !!q->request_dns_packet->opt, + DNS_PACKET_DO(q->request_dns_packet), + DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated, + &reply); + if (r < 0) { + log_debug_errno(r, "Failed to build reply packet: %m"); + break; + } + + (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply); + break; + } + + case DNS_TRANSACTION_RCODE_FAILURE: + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode); + break; + + case DNS_TRANSACTION_NOT_FOUND: + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN); + break; + + case DNS_TRANSACTION_TIMEOUT: + case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: + /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */ + break; + + case DNS_TRANSACTION_NO_SERVERS: + case DNS_TRANSACTION_INVALID_REPLY: + case DNS_TRANSACTION_ERRNO: + case DNS_TRANSACTION_ABORTED: + case DNS_TRANSACTION_DNSSEC_FAILED: + case DNS_TRANSACTION_NO_TRUST_ANCHOR: + case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: + case DNS_TRANSACTION_NETWORK_DOWN: + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL); + break; + + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_VALIDATING: + default: + assert_not_reached("Impossible state"); + } + + /* If there's a packet to write set, let's leave the stream around */ + if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) { + + /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The + * default completion action of the stream will drop the reference. */ + + dns_stub_detach_stream(q->request_dns_stream); + q->request_dns_stream = NULL; + } + + dns_query_free(q); +} + +static int dns_stub_stream_complete(DnsStream *s, int error) { + assert(s); + + log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m"); + + assert(s->query); + dns_query_free(s->query); + + return 0; +} + +static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { + DnsQuery *q = NULL; + int r; + + assert(m); + assert(p); + assert(p->protocol == DNS_PROTOCOL_DNS); + + /* Takes ownership of the *s stream object */ + + if (in_addr_is_localhost(p->family, &p->sender) <= 0 || + in_addr_is_localhost(p->family, &p->destination) <= 0) { + log_error("Got packet on unexpected IP range, refusing."); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + goto fail; + } + + r = dns_packet_extract(p); + if (r < 0) { + log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m"); + dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR); + goto fail; + } + + if (!DNS_PACKET_VERSION_SUPPORTED(p)) { + log_debug("Got EDNS OPT field with unsupported version number."); + dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS); + goto fail; + } + + if (dns_type_is_obsolete(p->question->keys[0]->type)) { + log_debug("Got message with obsolete key type, refusing."); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + goto fail; + } + + if (dns_type_is_zone_transer(p->question->keys[0]->type)) { + log_debug("Got request for zone transfer, refusing."); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + goto fail; + } + + if (!DNS_PACKET_RD(p)) { + /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */ + log_debug("Got request with recursion disabled, refusing."); + dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED); + goto fail; + } + + if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) { + log_debug("Got request with DNSSEC CD bit set, refusing."); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + goto fail; + } + + r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME); + if (r < 0) { + log_error_errno(r, "Failed to generate query object: %m"); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + goto fail; + } + + /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */ + q->clamp_ttl = true; + + q->request_dns_packet = dns_packet_ref(p); + q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */ + q->complete = dns_stub_query_complete; + + if (s) { + s->on_packet = NULL; + s->complete = dns_stub_stream_complete; + s->query = q; + } + + r = dns_query_go(q); + if (r < 0) { + log_error_errno(r, "Failed to start query: %m"); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + goto fail; + } + + log_info("Processing query..."); + return; + +fail: + if (s && DNS_STREAM_QUEUED(s)) + dns_stub_detach_stream(s); + + dns_query_free(q); +} + +static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + Manager *m = userdata; + int r; + + r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p); + if (r <= 0) + return r; + + if (dns_packet_validate_query(p) > 0) { + log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p)); + + dns_stub_process_query(m, NULL, p); + } else + log_debug("Invalid DNS stub UDP packet, ignoring."); + + return 0; +} + +int manager_dns_stub_udp_fd(Manager *m) { + static const int one = 1; + + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(53), + .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), + }; + + int r; + + if (m->dns_stub_udp_fd >= 0) + return m->dns_stub_udp_fd; + + m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->dns_stub_udp_fd < 0) + return -errno; + + r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* Make sure no traffic from outside the local host can leak to onto this socket */ + r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp"); + + return m->dns_stub_udp_fd; + +fail: + m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd); + return r; +} + +static int on_dns_stub_stream_packet(DnsStream *s) { + assert(s); + assert(s->read_packet); + + if (dns_packet_validate_query(s->read_packet) > 0) { + log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); + + dns_stub_process_query(s->manager, s, s->read_packet); + } else + log_debug("Invalid DNS stub TCP packet, ignoring."); + + /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now, + * or that didn't happen in which case we want to free the stream */ + dns_stream_unref(s); + + return 0; +} + +static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + DnsStream *stream; + Manager *m = userdata; + int cfd, r; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd); + if (r < 0) { + safe_close(cfd); + return r; + } + + stream->on_packet = on_dns_stub_stream_packet; + + /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action + * of the stream, or by our packet callback, or when the manager is shut down. */ + + return 0; +} + +int manager_dns_stub_tcp_fd(Manager *m) { + static const int one = 1; + + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), + .in.sin_port = htobe16(53), + }; + + int r; + + if (m->dns_stub_tcp_fd >= 0) + return m->dns_stub_tcp_fd; + + m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->dns_stub_tcp_fd < 0) + return -errno; + + r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + /* Make sure no traffic from outside the local host can leak to onto this socket */ + r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = listen(m->dns_stub_tcp_fd, SOMAXCONN); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp"); + + return m->dns_stub_tcp_fd; + +fail: + m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd); + return r; +} + +int manager_dns_stub_start(Manager *m) { + int r; + + assert(m); + + r = manager_dns_stub_udp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + r = manager_dns_stub_tcp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + return 0; + +eaddrinuse: + log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support."); + manager_dns_stub_stop(m); + + return 0; +} + +void manager_dns_stub_stop(Manager *m) { + assert(m); + + m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source); + m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source); + + m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd); + m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd); +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-stub.h b/src/grp-resolve/systemd-resolved/resolved-dns-stub.h new file mode 100644 index 0000000000..fce4d25ede --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-stub.h @@ -0,0 +1,31 @@ +#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 . +***/ + +#include "resolved-manager.h" + +/* 127.0.0.53 in native endian */ +#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U) + +int manager_dns_stub_udp_fd(Manager *m); +int manager_dns_stub_tcp_fd(Manager *m); + +void manager_dns_stub_stop(Manager *m); +int manager_dns_stub_start(Manager *m); diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.c b/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.c new file mode 100644 index 0000000000..16e0410f98 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.c @@ -0,0 +1,414 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "sd-netlink/local-addresses.h" + +#include "resolved-dns-synthesize.h" + +int dns_synthesize_ifindex(int ifindex) { + + /* When the caller asked for resolving on a specific + * interface, we synthesize the answer for that + * interface. However, if nothing specific was claimed and we + * only return localhost RRs, we synthesize the answer for + * localhost. */ + + if (ifindex > 0) + return ifindex; + + return LOOPBACK_IFINDEX; +} + +int dns_synthesize_family(uint64_t flags) { + + /* Picks an address family depending on set flags. This is + * purely for synthesized answers, where the family we return + * for the reply should match what was requested in the + * question, even though we are synthesizing the answer + * here. */ + + if (!(flags & SD_RESOLVED_DNS)) { + if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4)) + return AF_INET; + if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6)) + return AF_INET6; + } + + return AF_UNSPEC; +} + +DnsProtocol dns_synthesize_protocol(uint64_t flags) { + + /* Similar as dns_synthesize_family() but does this for the + * protocol. If resolving via DNS was requested, we claim it + * was DNS. Similar, if nothing specific was + * requested. However, if only resolving via LLMNR was + * requested we return that. */ + + if (flags & SD_RESOLVED_DNS) + return DNS_PROTOCOL_DNS; + if (flags & SD_RESOLVED_LLMNR) + return DNS_PROTOCOL_LLMNR; + if (flags & SD_RESOLVED_MDNS) + return DNS_PROTOCOL_MDNS; + + return DNS_PROTOCOL_DNS; +} + +static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { + int r; + + assert(m); + assert(key); + assert(answer); + + r = dns_answer_reserve(answer, 2); + if (r < 0) + return r; + + if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key)); + if (!rr) + return -ENOMEM; + + rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); + + r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key)); + if (!rr) + return -ENOMEM; + + rr->aaaa.in6_addr = in6addr_loopback; + + r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + return 0; +} + +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); + if (!rr) + return -ENOMEM; + + rr->ptr.name = strdup(to); + if (!rr->ptr.name) + return -ENOMEM; + + return dns_answer_add(*answer, rr, ifindex, flags); +} + +static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { + int r; + + assert(m); + assert(key); + assert(answer); + + if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) { + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_rr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + r = dns_answer_reserve(answer, n_addresses); + if (r < 0) + return r; + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + return 0; +} + +static int answer_add_addresses_ptr( + DnsAnswer **answer, + const char *name, + struct local_address *addresses, + unsigned n_addresses, + int af, const union in_addr_union *match) { + + unsigned j; + int r; + + assert(answer); + assert(name); + + for (j = 0; j < n_addresses; j++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + if (af != AF_UNSPEC) { + + if (addresses[j].family != af) + continue; + + if (match && !in_addr_equal(af, match, &addresses[j].address)) + continue; + } + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + return 0; +} + +static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(m); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_addresses(m->rtnl, ifindex, af, &addresses); + if (n < 0) + return n; + + if (n == 0) { + struct local_address buffer[2]; + + /* If we have no local addresses then use ::1 + * and 127.0.0.2 as local ones. */ + + if (af == AF_INET || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET, + .ifindex = dns_synthesize_ifindex(ifindex), + .address.in.s_addr = htobe32(0x7F000002), + }; + + if (af == AF_INET6 || af == AF_UNSPEC) + buffer[n++] = (struct local_address) { + .family = AF_INET6, + .ifindex = dns_synthesize_ifindex(ifindex), + .address.in6 = in6addr_loopback, + }; + + return answer_add_addresses_rr(answer, dns_resource_key_name(key), buffer, n); + } + } + + return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); +} + +static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n, r; + + assert(m); + assert(address); + assert(answer); + + if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { + + /* Always map the IPv4 address 127.0.0.2 to the local + * hostname, in addition to "localhost": */ + + r = dns_answer_reserve(answer, 3); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + return 0; + } + + n = local_addresses(m->rtnl, ifindex, af, &addresses); + if (n < 0) + return n; + + r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address); + if (r < 0) + return r; + + return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address); +} + +static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n = 0, af; + + assert(m); + assert(key); + assert(answer); + + af = dns_type_to_af(key->type); + if (af >= 0) { + n = local_gateways(m->rtnl, ifindex, af, &addresses); + if (n < 0) + return n; + } + + return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); +} + +static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; + int n; + + assert(m); + assert(address); + assert(answer); + + n = local_gateways(m->rtnl, ifindex, af, &addresses); + if (n < 0) + return n; + + return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address); +} + +int dns_synthesize_answer( + Manager *m, + DnsQuestion *q, + int ifindex, + DnsAnswer **ret) { + + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnsResourceKey *key; + bool found = false; + int r; + + assert(m); + assert(q); + + DNS_QUESTION_FOREACH(key, q) { + union in_addr_union address; + const char *name; + int af; + + if (key->class != DNS_CLASS_IN && + key->class != DNS_CLASS_ANY) + continue; + + name = dns_resource_key_name(key); + + if (is_localhost(name)) { + + r = synthesize_localhost_rr(m, key, ifindex, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); + + } else if (manager_is_own_hostname(m, name)) { + + r = synthesize_system_hostname_rr(m, key, ifindex, &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(m, key, ifindex, &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(m, key, ifindex, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); + + } else if (dns_name_address(name, &af, &address) > 0) { + + r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m"); + + r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m"); + } else + continue; + + found = true; + } + + r = found; + + if (ret) { + *ret = answer; + answer = NULL; + } + + return r; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.h b/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.h new file mode 100644 index 0000000000..2309105068 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-synthesize.h @@ -0,0 +1,31 @@ +#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 . +***/ + +#include "resolved-dns-answer.h" +#include "resolved-dns-question.h" + +#include "resolved-manager.h" + +int dns_synthesize_ifindex(int ifindex); +int dns_synthesize_family(uint64_t flags); +DnsProtocol dns_synthesize_protocol(uint64_t flags); + +int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret); diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-transaction.c b/src/grp-resolve/systemd-resolved/resolved-dns-transaction.c new file mode 100644 index 0000000000..3cf72e0ff8 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-transaction.c @@ -0,0 +1,3108 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/errno-list.h" +#include "basic/fd-util.h" +#include "basic/random-util.h" +#include "basic/string-table.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-cache.h" +#include "resolved-dns-transaction.h" +#include "resolved-llmnr.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; + t->answer_errno = 0; +} + +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); + set_remove(z->notify_transactions_done, t); + dns_transaction_gc(z); + } +} + +static void dns_transaction_close_connection(DnsTransaction *t) { + assert(t); + + if (t->stream) { + /* Let's detach the stream from our transaction, in case something else keeps a reference to it. */ + t->stream->complete = NULL; + t->stream->on_packet = NULL; + t->stream->transaction = NULL; + t->stream = dns_stream_unref(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) { + DnsQueryCandidate *c; + DnsZoneItem *i; + DnsTransaction *z; + + if (!t) + return NULL; + + log_debug("Freeing transaction %" PRIu16 ".", t->id); + + dns_transaction_close_connection(t); + dns_transaction_stop_timeout(t); + + dns_packet_unref(t->sent); + dns_transaction_reset_answer(t); + + dns_server_unref(t->server); + + if (t->scope) { + 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)); + } + + while ((c = set_steal_first(t->notify_query_candidates))) + set_remove(c->transactions, t); + set_free(t->notify_query_candidates); + + while ((c = set_steal_first(t->notify_query_candidates_done))) + set_remove(c->transactions, t); + set_free(t->notify_query_candidates_done); + + while ((i = set_steal_first(t->notify_zone_items))) + i->probe_transaction = NULL; + set_free(t->notify_zone_items); + + while ((i = set_steal_first(t->notify_zone_items_done))) + i->probe_transaction = NULL; + set_free(t->notify_zone_items_done); + + while ((z = set_steal_first(t->notify_transactions))) + set_remove(z->dnssec_transactions, t); + set_free(t->notify_transactions); + + while ((z = set_steal_first(t->notify_transactions_done))) + set_remove(z->dnssec_transactions, t); + set_free(t->notify_transactions_done); + + 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); + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free); + +bool dns_transaction_gc(DnsTransaction *t) { + assert(t); + + if (t->block_gc > 0) + return true; + + if (set_isempty(t->notify_query_candidates) && + set_isempty(t->notify_query_candidates_done) && + set_isempty(t->notify_zone_items) && + set_isempty(t->notify_zone_items_done) && + set_isempty(t->notify_transactions) && + set_isempty(t->notify_transactions_done)) { + 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) { + _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL; + int r; + + assert(ret); + 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_by_key, &dns_resource_key_hash_ops); + if (r < 0) + return r; + + t = new0(DnsTransaction, 1); + if (!t) + 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; + t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; + + t->id = pick_new_id(s->manager); + + r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t); + if (r < 0) { + t->id = 0; + return r; + } + + 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; + + t = NULL; + + return 0; +} + +static void dns_transaction_shuffle_id(DnsTransaction *t) { + uint16_t new_id; + assert(t); + + /* 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) { + _cleanup_free_ char *pretty = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + DnsZoneItem *z; + + assert(t); + assert(p); + + if (manager_our_packet(t->scope->manager, p) != 0) + return; + + (void) in_addr_to_string(p->family, &p->sender, &pretty); + + log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str), + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + af_to_name_short(t->scope->family), + strnull(pretty)); + + /* RFC 4795, Section 4.1 says that the peer with the + * lexicographically smaller IP address loses */ + if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) { + log_debug("Peer has lexicographically larger IP address and thus lost in the conflict."); + return; + } + + log_debug("We have the lexicographically larger IP address and thus lost in the conflict."); + + t->block_gc++; + + 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); + + /* Secondly, report this as conflict, so that we might + * look for a different hostname */ + dns_zone_item_conflict(z); + } + t->block_gc--; + + dns_transaction_gc(t); +} + +void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { + DnsQueryCandidate *c; + DnsZoneItem *z; + DnsTransaction *d; + const char *st; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + assert(t); + assert(!DNS_TRANSACTION_IS_LIVE(state)); + + if (state == DNS_TRANSACTION_DNSSEC_FAILED) { + dns_resource_key_to_string(t->key, key_str, sizeof key_str); + + log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE), + LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)), + "DNS_TRANSACTION=%" PRIu16, t->id, + "DNS_QUESTION=%s", key_str, + "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result), + "DNS_SERVER=%s", dns_server_string(t->server), + "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level), + NULL); + } + + /* Note that this call might invalidate the query. Callers + * should hence not attempt to access the query or transaction + * after calling this function. */ + + if (state == DNS_TRANSACTION_ERRNO) + st = errno_to_name(t->answer_errno); + else + st = dns_transaction_state_to_string(state); + + log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str), + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + af_to_name_short(t->scope->family), + st, + t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source), + t->answer_authenticated ? "authenticated" : "unsigned"); + + t->state = state; + + 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_MOVE(c, t->notify_query_candidates_done, t->notify_query_candidates) + dns_query_candidate_notify(c); + SWAP_TWO(t->notify_query_candidates, t->notify_query_candidates_done); + + SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) + dns_zone_item_notify(z); + SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); + + SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) + dns_transaction_notify(d, t); + SWAP_TWO(t->notify_transactions, t->notify_transactions_done); + + 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); + + /* Pick a DNS server and a feature level for it. */ + + server = dns_scope_get_dns_server(t->scope); + if (!server) + return -ESRCH; + + /* If we changed the server invalidate the feature level clamping, as the new server might have completely + * different properties. */ + if (server != t->server) + t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; + + t->current_feature_level = dns_server_possible_feature_level(server); + + /* Clamp the feature level if that is requested. */ + if (t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID && + t->current_feature_level > t->clamp_feature_level) + t->current_feature_level = t->clamp_feature_level; + + log_debug("Using feature level %s for transaction %u.", dns_server_feature_level_to_string(t->current_feature_level), t->id); + + if (server == t->server) + return 0; + + dns_server_unref(t->server); + t->server = dns_server_ref(server); + + log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id); + + return 1; +} + +static void dns_transaction_retry(DnsTransaction *t, bool next_server) { + int r; + + assert(t); + + log_debug("Retrying transaction %" PRIu16 ".", t->id); + + /* Before we try again, switch to a new server. */ + if (next_server) + dns_scope_next_dns_server(t->scope); + + r = dns_transaction_go(t); + if (r < 0) { + t->answer_errno = -r; + dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); + } +} + +static int dns_transaction_maybe_restart(DnsTransaction *t) { + int r; + + assert(t); + + /* Returns > 0 if the transaction was restarted, 0 if not */ + + 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); + + r = dns_transaction_go(t); + if (r < 0) + return r; + + return 1; +} + +static int on_stream_complete(DnsStream *s, int error) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsTransaction *t; + + assert(s); + assert(s->transaction); + + /* Copy the data we care about out of the stream before we + * destroy it. */ + t = s->transaction; + p = dns_packet_ref(s->read_packet); + + dns_transaction_close_connection(t); + + if (ERRNO_IS_DISCONNECT(error)) { + usec_t usec; + + if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { + /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the + * question on this scope. */ + dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND); + return 0; + } + + 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, true); + return 0; + } + if (error != 0) { + t->answer_errno = error; + dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); + return 0; + } + + if (dns_packet_validate_reply(p) <= 0) { + log_debug("Invalid TCP reply packet."); + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return 0; + } + + dns_scope_check_conflicts(t->scope, p); + + t->block_gc++; + dns_transaction_process_reply(t, p); + t->block_gc--; + + /* 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) { + _cleanup_close_ int fd = -1; + int r; + + assert(t); + + dns_transaction_close_connection(t); + + switch (t->scope->protocol) { + + case DNS_PROTOCOL_DNS: + 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_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; + + /* Otherwise, try to talk to the owner of a + * the IP address, in case this is a reverse + * PTR lookup */ + + r = dns_name_address(dns_resource_key_name(t->key), &family, &address); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + if (family != t->scope->family) + return -ESRCH; + + fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT); + } + + break; + + default: + return -EAFNOSUPPORT; + } + + if (fd < 0) + return fd; + + 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); + if (r < 0) { + t->stream = dns_stream_unref(t->stream); + return r; + } + + t->stream->complete = on_stream_complete; + t->stream->transaction = t; + + /* The interface index is difficult to determine if we are + * connecting to the local host, hence fill this in right away + * instead of determining it from the socket */ + t->stream->ifindex = dns_scope_ifindex(t->scope); + + dns_transaction_reset_answer(t); + + t->tried_stream = true; + + return 0; +} + +static void dns_transaction_cache_answer(DnsTransaction *t) { + assert(t); + + /* 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; + + /* Caching disabled? */ + if (!t->scope->manager->enable_cache) + return; + + /* 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 (!IN_SET(dt->answer_rcode, DNS_RCODE_NXDOMAIN, DNS_RCODE_SERVFAIL)) { + log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode)); + goto fail; + } + + /* Fall-through: NXDOMAIN/SERVFAIL is good enough for us. This is because some DNS servers + * erronously return NXDOMAIN/SERVFAIL for empty non-terminals (Akamai...) or missing DS + * records (Facebook), 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) + goto fail; + 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) + goto fail; + 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 == -EBADMSG) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } + if (r < 0) + goto fail; + + 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; + } + + if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER) + dns_server_warn_downgrade(t->server); + + 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); + + return; + +fail: + t->answer_errno = -r; + dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); +} + +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_fix_rcode(DnsTransaction *t) { + int r; + + assert(t); + + /* Fix up the RCODE to SUCCESS if we get at least one matching RR in a response. Note that this contradicts the + * DNS RFCs a bit. Specifically, RFC 6604 Section 3 clarifies that the RCODE shall say something about a + * CNAME/DNAME chain element coming after the last chain element contained in the message, and not the first + * one included. However, it also indicates that not all DNS servers implement this correctly. Moreover, when + * using DNSSEC we usually only can prove the first element of a CNAME/DNAME chain anyway, hence let's settle + * on always processing the RCODE as referring to the immediate look-up we do, i.e. the first element of a + * CNAME/DNAME chain. This way, we uniformly handle CNAME/DNAME chains, regardless if the DNS server + * incorrectly implements RCODE, whether DNSSEC is in use, or whether the DNS server only supplied us with an + * incomplete CNAME/DNAME chain. + * + * Or in other words: if we get at least one positive reply in a message we patch NXDOMAIN to become SUCCESS, + * and then rely on the CNAME chasing logic to figure out that there's actually a CNAME error with a new + * lookup. */ + + if (t->answer_rcode != DNS_RCODE_NXDOMAIN) + return 0; + + r = dns_transaction_has_positive_answer(t, NULL); + if (r <= 0) + return r; + + t->answer_rcode = DNS_RCODE_SUCCESS; + return 0; +} + +void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { + usec_t ts; + int r; + + assert(t); + assert(p); + 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: + /* For LLMNR we will not accept any packets from other interfaces */ + + if (p->ifindex != dns_scope_ifindex(t->scope)) + return; + + if (p->family != t->scope->family) + return; + + /* Tentative packets are not full responses but still + * useful for identifying uniqueness conflicts during + * probing. */ + if (DNS_PACKET_LLMNR_T(p)) { + dns_transaction_tentative(t, p); + return; + } + + break; + + case DNS_PROTOCOL_MDNS: + /* For mDNS we will not accept any packets from other interfaces */ + + if (p->ifindex != dns_scope_ifindex(t->scope)) + 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: + assert_not_reached("Invalid DNS protocol."); + } + + if (t->received != p) { + dns_packet_unref(t->received); + 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 */ + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } + + if (DNS_PACKET_ID(p) != t->id) { + /* Not the reply to our query? Somebody must be fucking with us */ + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } + } + + 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); + + if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) { + + /* Request failed, immediately try again with reduced features */ + + if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) { + /* This was already at the lowest possible feature level? If so, we can't downgrade + * this transaction anymore, hence let's process the response, and accept the rcode. */ + log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p))); + break; + } + + /* Reduce this feature level by one and try again. */ + t->clamp_feature_level = t->current_feature_level - 1; + + log_debug("Server returned error %s, retrying transaction with reduced feature level %s.", + dns_rcode_to_string(DNS_PACKET_RCODE(p)), + dns_server_feature_level_to_string(t->clamp_feature_level)); + + dns_transaction_retry(t, false /* use the same server */); + 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: + 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) { + /* No servers found? Damn! */ + 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_DNS) + goto fail; + + /* On DNS, couldn't send? Try immediately again, with a new server */ + dns_transaction_retry(t, true); + } + + return; + } + + /* 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; + } + + if (t->server) { + /* Report that we successfully received a valid packet with a good rcode after we initially got a bad + * rcode and subsequently downgraded the protocol */ + + if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN) && + t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID) + dns_server_packet_rcode_downgrade(t->server, t->clamp_feature_level); + + /* Report that the OPT RR was missing */ + if (!p->opt) + dns_server_packet_bad_opt(t->server, t->current_feature_level); + + /* Report that we successfully received a packet */ + 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) + goto fail; + if (r > 0) /* Transaction got restarted... */ + return; + + if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) { + + /* Only consider responses with equivalent query section to the request */ + r = dns_packet_is_reply_for(p, t->key); + if (r < 0) + goto fail; + 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; + + r = dns_transaction_fix_rcode(t); + if (r < 0) + goto fail; + + /* 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) + goto fail; + 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); + return; + +fail: + t->answer_errno = -r; + dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); +} + +static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsTransaction *t = userdata; + int r; + + assert(t); + assert(t->scope); + + r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); + if (ERRNO_IS_DISCONNECT(-r)) { + usec_t usec; + + /* 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, true); + return 0; + } + if (r < 0) { + dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); + t->answer_errno = -r; + 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_udp(DnsTransaction *t) { + int r; + + assert(t); + + if (t->scope->protocol == DNS_PROTOCOL_DNS) { + + r = dns_transaction_pick_server(t); + if (r < 0) + return r; + + if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP) + return -EAGAIN; + + if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type)) + return -EOPNOTSUPP; + + if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */ + int fd; + + dns_transaction_close_connection(t); + + fd = dns_scope_socket_udp(t->scope, t->server, 53); + if (fd < 0) + return fd; + + 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; + } + + (void) sd_event_source_set_description(t->dns_udp_event_source, "dns-transaction-udp"); + t->dns_udp_fd = fd; + } + + 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) + return r; + + dns_transaction_reset_answer(t); + + return 0; +} + +static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) { + DnsTransaction *t = userdata; + + assert(s); + assert(t); + + if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) { + /* Timeout reached? Increase the timeout for the server used */ + switch (t->scope->protocol) { + + 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; + + case DNS_PROTOCOL_LLMNR: + case DNS_PROTOCOL_MDNS: + dns_scope_packet_lost(t->scope, usec - t->start_usec); + break; + + default: + assert_not_reached("Invalid DNS protocol."); + } + + if (t->initial_jitter_scheduled) + t->initial_jitter_elapsed = true; + } + + log_debug("Timeout reached on transaction %" PRIu16 ".", t->id); + + dns_transaction_retry(t, true); + return 0; +} + +static usec_t transaction_get_resend_timeout(DnsTransaction *t) { + assert(t); + assert(t->scope); + + switch (t->scope->protocol) { + + case DNS_PROTOCOL_DNS: + assert(t->server); + return t->server->resend_timeout; + + 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."); + } +} + +static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { + int r; + + assert(t); + + dns_transaction_stop_timeout(t); + + r = dns_scope_network_good(t->scope); + if (r < 0) + return r; + if (r == 0) { + dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN); + return 0; + } + + 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 && 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; + } + + t->n_attempts++; + t->start_usec = ts; + + 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 and trust them. */ + 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, dns_scope_ifindex(t->scope), &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->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 + * might flush the cache. */ + dns_scope_get_dns_server(t->scope); + + /* Let's then prune all outdated entries */ + dns_cache_prune(&t->scope->cache); + + r = dns_cache_lookup(&t->scope->cache, t->key, t->clamp_ttl, &t->answer_rcode, &t->answer, &t->answer_authenticated); + if (r < 0) + return r; + if (r > 0) { + 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_RCODE_FAILURE); + return 0; + } + } + + 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_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; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + assert(t); + + /* Returns > 0 if the transaction is now pending, returns 0 if could be processed immediately and has finished + * now. */ + + 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("Transaction %" PRIu16 " for <%s> scope %s on %s/%s.", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str), + dns_protocol_to_string(t->scope->protocol), + t->scope->link ? t->scope->link->name : "*", + af_to_name_short(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_scheduled = true; + + random_bytes(&jitter, sizeof(jitter)); + + 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, 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 %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter); + return 0; + } + + /* Otherwise, we need to ask the network */ + r = dns_transaction_make_packet(t); + if (r < 0) + return r; + + if (t->scope->protocol == DNS_PROTOCOL_LLMNR && + (dns_name_endswith(dns_resource_key_name(t->key), "in-addr.arpa") > 0 || + dns_name_endswith(dns_resource_key_name(t->key), "ip6.arpa") > 0)) { + + /* RFC 4795, Section 2.4. says reverse lookups shall + * 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 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; + } + 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 (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) { + /* On LLMNR, if we cannot connect to a host via TCP when doing reverse lookups. This means we cannot + * answer this request with this protocol. */ + dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND); + return 0; + } + if (r < 0) { + if (t->scope->protocol != DNS_PROTOCOL_DNS) + return r; + + /* Couldn't send? Try immediately again, with a new server */ + 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, 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) { + char s[DNS_RESOURCE_KEY_STRING_MAX], saux[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", + aux->id, + dns_resource_key_to_string(t->key, s, sizeof s), + t->id, + dns_resource_key_to_string(aux->key, saux, sizeof saux)); + + 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_ensure_allocated(&aux->notify_transactions_done, 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_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) { + int r; + + assert(t); + + /* Check whether the specified name is in 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. */ + + r = dns_resource_key_match_rr(t->key, rr, NULL); + if (r != 0) + return r; + + return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL); +} + +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; + + /* Note that we do not check the feature level actually used for the transaction but instead the feature level + * the server is known to support currently, as the transaction feature level might be lower than what the + * server actually supports, since we might have downgraded this transaction's feature level because we got a + * SERVFAIL earlier and wanted to check whether downgrading fixes it. */ + + 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 queries with no matching response RR, and no NSEC/NSEC3, the DS RR + * - For 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; + uint16_t type = 0; + + name = dns_resource_key_name(t->key); + + /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this + * could also be used as indication that we are not at a zone apex, but in real world setups there are + * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even + * though they have further children. If this was a DS request, then it's signed when the parent zone + * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR, + * to see if that is signed. */ + + if (t->key->type == DNS_TYPE_DS) { + r = dns_name_parent(&name); + if (r > 0) { + type = DNS_TYPE_SOA; + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).", + t->id, dns_resource_key_name(t->key)); + } else + name = NULL; + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) { + + type = DNS_TYPE_DS; + log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).", + t->id, dns_resource_key_name(t->key)); + + } else { + type = DNS_TYPE_SOA; + 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, type, 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) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + DnsTransaction *dt; + const char *name; + uint16_t type = 0; + 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_resource_key_to_string(t->key, key_str, sizeof key_str)); + return false; + } + + name = dns_resource_key_name(t->key); + + if (t->key->type == DNS_TYPE_DS) { + + /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed, + * hence check the parent SOA in this case. */ + + r = dns_name_parent(&name); + if (r < 0) + return r; + if (r == 0) + return true; + + type = DNS_TYPE_SOA; + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) + /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */ + type = DNS_TYPE_DS; + else + /* For all other negative replies, check for the SOA lookup */ + type = DNS_TYPE_SOA; + + /* 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 != type) + 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; +} + +typedef enum { + DNSSEC_PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */ + DNSSEC_PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */ + DNSSEC_PHASE_ALL, /* Phase #3, validate everything else */ +} Phase; + +static int dnssec_validate_records( + DnsTransaction *t, + Phase phase, + bool *have_nsec, + DnsAnswer **validated) { + + DnsResourceRecord *rr; + int r; + + /* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */ + + 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 == DNSSEC_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 == DNSSEC_PHASE_DNSKEY) + continue; + break; + + default: + /* We validate all other RRs only in the ALL phases */ + if (phase != DNSSEC_PHASE_ALL) + continue; + } + + 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; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key); + + /* Exit the loop, we dropped something from the answer, start from the beginning */ + return 1; + } + + /* 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 != DNSSEC_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; + + manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key); + + /* Exit the loop, we dropped something from the answer, start from the beginning */ + return 1; + } + } + + 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; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + + 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; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + + /* 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) { + char s[DNS_RESOURCE_KEY_STRING_MAX]; + + /* 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. */ + + log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", + dns_resource_key_to_string(rr->key, s, sizeof s)); + + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); + if (r < 0) + return r; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + } + + 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; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + } + + 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) { + if (IN_SET(result, + DNSSEC_INVALID, + DNSSEC_SIGNATURE_EXPIRED, + DNSSEC_NO_SIGNATURE)) + manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key); + else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */ + manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key); + + /* 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; + + /* We dropped something from the answer, start from the beginning. */ + return 1; + } + + return 2; /* Finito. */ +} + +int dns_transaction_validate_dnssec(DnsTransaction *t) { + _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; + Phase phase; + DnsAnswerFlags flags; + int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + 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 for %" PRIu16 ", used server feature level does not support DNSSEC.", t->id); + return 0; + } + + log_debug("Validating response from transaction %" PRIu16 " (%s).", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str)); + + /* 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 = DNSSEC_PHASE_DNSKEY; + for (;;) { + bool have_nsec = false; + + r = dnssec_validate_records(t, phase, &have_nsec, &validated); + if (r <= 0) + return r; + + /* Try again as long as we managed to achieve something */ + if (r == 1) + continue; + + if (phase == DNSSEC_PHASE_DNSKEY && have_nsec) { + /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */ + phase = DNSSEC_PHASE_NSEC; + continue; + } + + if (phase != DNSSEC_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 = DNSSEC_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, key_str); + t->answer_dnssec_result = DNSSEC_VALIDATED; + t->answer_rcode = DNS_RCODE_NXDOMAIN; + t->answer_authenticated = authenticated; + + manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key); + 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, key_str); + t->answer_dnssec_result = DNSSEC_VALIDATED; + t->answer_rcode = DNS_RCODE_SUCCESS; + t->answer_authenticated = authenticated; + + manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key); + 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, key_str); + t->answer_dnssec_result = DNSSEC_UNSIGNED; + t->answer_authenticated = false; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key); + 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; + manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key); + } else { + t->answer_dnssec_result = DNSSEC_UNSIGNED; + t->answer_authenticated = false; + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key); + } + + break; + + case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM: + /* We don't know the NSEC3 algorithm used? */ + t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM; + manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key); + 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; + manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key); + break; + + default: + assert_not_reached("Unexpected NSEC result."); + } + } + + return 1; +} + +static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { + [DNS_TRANSACTION_NULL] = "null", + [DNS_TRANSACTION_PENDING] = "pending", + [DNS_TRANSACTION_VALIDATING] = "validating", + [DNS_TRANSACTION_RCODE_FAILURE] = "rcode-failure", + [DNS_TRANSACTION_SUCCESS] = "success", + [DNS_TRANSACTION_NO_SERVERS] = "no-servers", + [DNS_TRANSACTION_TIMEOUT] = "timeout", + [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached", + [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply", + [DNS_TRANSACTION_ERRNO] = "errno", + [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", + [DNS_TRANSACTION_NETWORK_DOWN] = "network-down", + [DNS_TRANSACTION_NOT_FOUND] = "not-found", +}; +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/grp-resolve/systemd-resolved/resolved-dns-transaction.h b/src/grp-resolve/systemd-resolved/resolved-dns-transaction.h new file mode 100644 index 0000000000..26307f84ac --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-transaction.h @@ -0,0 +1,180 @@ +#pragma once + +/*** + 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 . +***/ + +typedef struct DnsTransaction DnsTransaction; +typedef enum DnsTransactionState DnsTransactionState; +typedef enum DnsTransactionSource DnsTransactionSource; + +enum DnsTransactionState { + DNS_TRANSACTION_NULL, + DNS_TRANSACTION_PENDING, + DNS_TRANSACTION_VALIDATING, + DNS_TRANSACTION_RCODE_FAILURE, + DNS_TRANSACTION_SUCCESS, + DNS_TRANSACTION_NO_SERVERS, + DNS_TRANSACTION_TIMEOUT, + DNS_TRANSACTION_ATTEMPTS_MAX_REACHED, + DNS_TRANSACTION_INVALID_REPLY, + DNS_TRANSACTION_ERRNO, + DNS_TRANSACTION_ABORTED, + DNS_TRANSACTION_DNSSEC_FAILED, + DNS_TRANSACTION_NO_TRUST_ANCHOR, + DNS_TRANSACTION_RR_TYPE_UNSUPPORTED, + DNS_TRANSACTION_NETWORK_DOWN, + DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */ + _DNS_TRANSACTION_STATE_MAX, + _DNS_TRANSACTION_STATE_INVALID = -1 +}; + +#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-scope.h" + +struct DnsTransaction { + DnsScope *scope; + + DnsResourceKey *key; + + DnsTransactionState state; + + uint16_t id; + + bool tried_stream:1; + + bool initial_jitter_scheduled:1; + bool initial_jitter_elapsed:1; + + bool clamp_ttl:1; + + DnsPacket *sent, *received; + + DnsAnswer *answer; + int answer_rcode; + DnssecResult answer_dnssec_result; + DnsTransactionSource answer_source; + uint32_t answer_nsec_ttl; + int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ + + /* 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; + + /* The features of the DNS server at time of transaction start */ + DnsServerFeatureLevel current_feature_level; + + /* If we got SERVFAIL back, we retry the lookup, using a lower feature level than we used before. */ + DnsServerFeatureLevel clamp_feature_level; + + /* Query candidates this transaction is referenced by and that + * shall be notified about this specific transaction + * completing. */ + Set *notify_query_candidates, *notify_query_candidates_done; + + /* Zone items this transaction is referenced by and that shall + * be notified about completion. */ + Set *notify_zone_items, *notify_zone_items_done; + + /* 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, *notify_transactions_done; + + /* The opposite direction: the transactions this transaction + * created in order to request DNSKEY or DS RRs. */ + Set *dnssec_transactions; + + unsigned block_gc; + + LIST_FIELDS(DnsTransaction, transactions_by_scope); +}; + +int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key); +DnsTransaction* dns_transaction_free(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_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) diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.c b/src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.c new file mode 100644 index 0000000000..c9f221d425 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.c @@ -0,0 +1,744 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hexdecoct.h" +#include "basic/parse-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "resolved-dns-dnssec.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-trust-anchor.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/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.h b/src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.h new file mode 100644 index 0000000000..ee5cda0748 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-trust-anchor.h @@ -0,0 +1,43 @@ +#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 . +***/ + +#include "basic/hashmap.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-rr.h" + +typedef struct DnsTrustAnchor DnsTrustAnchor; + +/* 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/grp-resolve/systemd-resolved/resolved-dns-zone.c b/src/grp-resolve/systemd-resolved/resolved-dns-zone.c new file mode 100644 index 0000000000..d3ef27fefd --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-zone.c @@ -0,0 +1,665 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/list.h" +#include "basic/string-util.h" +#include "resolved-dns-packet.h" +#include "shared/dns-domain.h" + +#include "resolved-dns-zone.h" + +/* Never allow more than 1K entries */ +#define ZONE_MAX 1024 + +void dns_zone_item_probe_stop(DnsZoneItem *i) { + DnsTransaction *t; + assert(i); + + if (!i->probe_transaction) + return; + + t = i->probe_transaction; + i->probe_transaction = NULL; + + set_remove(t->notify_zone_items, i); + set_remove(t->notify_zone_items_done, i); + dns_transaction_gc(t); +} + +static void dns_zone_item_free(DnsZoneItem *i) { + if (!i) + return; + + dns_zone_item_probe_stop(i); + dns_resource_record_unref(i->rr); + + free(i); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free); + +static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { + DnsZoneItem *first; + + assert(z); + + if (!i) + return; + + first = hashmap_get(z->by_key, i->rr->key); + LIST_REMOVE(by_key, first, i); + if (first) + assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); + else + hashmap_remove(z->by_key, i->rr->key); + + first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); + LIST_REMOVE(by_name, first, i); + if (first) + assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); + else + hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key)); + + dns_zone_item_free(i); +} + +void dns_zone_flush(DnsZone *z) { + DnsZoneItem *i; + + assert(z); + + while ((i = hashmap_first(z->by_key))) + dns_zone_item_remove_and_free(z, i); + + assert(hashmap_size(z->by_key) == 0); + assert(hashmap_size(z->by_name) == 0); + + z->by_key = hashmap_free(z->by_key); + z->by_name = hashmap_free(z->by_name); +} + +static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { + DnsZoneItem *i; + + assert(z); + assert(rr); + + LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key)) + if (dns_resource_record_equal(i->rr, rr) > 0) + return i; + + return NULL; +} + +void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) { + DnsZoneItem *i; + + assert(z); + assert(rr); + + i = dns_zone_get(z, rr); + if (i) + dns_zone_item_remove_and_free(z, i); +} + +static int dns_zone_init(DnsZone *z) { + int r; + + assert(z); + + r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops); + if (r < 0) + return r; + + return 0; +} + +static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { + DnsZoneItem *first; + int r; + + first = hashmap_get(z->by_key, i->rr->key); + if (first) { + LIST_PREPEND(by_key, first, i); + assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); + } else { + r = hashmap_put(z->by_key, i->rr->key, i); + if (r < 0) + return r; + } + + first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); + if (first) { + LIST_PREPEND(by_name, first, i); + assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); + } else { + r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i); + if (r < 0) + return r; + } + + return 0; +} + +static int dns_zone_item_probe_start(DnsZoneItem *i) { + DnsTransaction *t; + int r; + + assert(i); + + if (i->probe_transaction) + return 0; + + 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->notify_zone_items, NULL); + if (r < 0) + goto gc; + + r = set_ensure_allocated(&t->notify_zone_items_done, NULL); + if (r < 0) + goto gc; + + r = set_put(t->notify_zone_items, i); + if (r < 0) + goto gc; + + i->probe_transaction = t; + + if (t->state == DNS_TRANSACTION_NULL) { + + i->block_ready++; + r = dns_transaction_go(t); + i->block_ready--; + + if (r < 0) { + dns_zone_item_probe_stop(i); + return r; + } + } + + dns_zone_item_notify(i); + return 0; + +gc: + dns_transaction_gc(t); + return r; +} + +int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { + _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL; + DnsZoneItem *existing; + int r; + + assert(z); + assert(s); + assert(rr); + + if (dns_class_is_pseudo(rr->key->class)) + return -EINVAL; + if (dns_type_is_pseudo(rr->key->type)) + return -EINVAL; + + existing = dns_zone_get(z, rr); + if (existing) + return 0; + + r = dns_zone_init(z); + if (r < 0) + return r; + + i = new0(DnsZoneItem, 1); + if (!i) + return -ENOMEM; + + i->scope = s; + i->rr = dns_resource_record_ref(rr); + i->probing_enabled = probe; + + r = dns_zone_link_item(z, i); + if (r < 0) + return r; + + if (probe) { + DnsZoneItem *first, *j; + bool established = false; + + /* Check if there's already an RR with the same name + * established. If so, it has been probed already, and + * we don't ned to probe again. */ + + LIST_FIND_HEAD(by_name, i, first); + LIST_FOREACH(by_name, j, first) { + if (i == j) + continue; + + if (j->state == DNS_ZONE_ITEM_ESTABLISHED) + established = true; + } + + if (established) + i->state = DNS_ZONE_ITEM_ESTABLISHED; + else { + i->state = DNS_ZONE_ITEM_PROBING; + + r = dns_zone_item_probe_start(i); + if (r < 0) { + dns_zone_item_remove_and_free(z, i); + i = NULL; + return r; + } + } + } else + i->state = DNS_ZONE_ITEM_ESTABLISHED; + + i = NULL; + return 0; +} + +int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + unsigned n_answer = 0; + DnsZoneItem *j, *first; + bool tentative = true, need_soa = false; + int r; + + /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the + * ifindex field in the answer with it */ + + assert(z); + assert(key); + assert(ret_answer); + + /* First iteration, count what we have */ + + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { + bool found = false, added = false; + int k; + + /* If this is a generic match, then we have to + * go through the list by the name and look + * for everything manually */ + + 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; + + k = dns_resource_key_match_rr(key, j->rr, NULL); + if (k < 0) + return k; + if (k > 0) { + n_answer++; + added = true; + } + + } + + if (found && !added) + need_soa = true; + + } else { + bool found = false; + + /* If this is a specific match, then look for + * the right key immediately */ + + 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; + + found = true; + n_answer++; + } + + 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; + + need_soa = true; + break; + } + } + } + + if (n_answer <= 0 && !need_soa) + goto return_empty; + + if (n_answer > 0) { + answer = dns_answer_new(n_answer); + if (!answer) + return -ENOMEM; + } + + if (need_soa) { + soa = dns_answer_new(1); + if (!soa) + return -ENOMEM; + } + + /* Second iteration, actually add the RRs to the answers */ + if (key->type == DNS_TYPE_ANY || key->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; + + found = true; + + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; + + 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, ifindex, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + added = true; + } + } + + if (found && !added) { + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex); + if (r < 0) + return r; + } + } 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; + + found = true; + + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; + + r = dns_answer_add(answer, j->rr, ifindex, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + if (!found) { + bool add_soa = 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; + + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; + + add_soa = true; + } + + if (add_soa) { + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex); + 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; + + 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) { + assert(i); + + if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED)) + return; + + log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr))); + + dns_zone_item_probe_stop(i); + + /* Withdraw the conflict item */ + i->state = DNS_ZONE_ITEM_WITHDRAWN; + + /* Maybe change the hostname */ + if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0) + manager_next_hostname(i->scope->manager); +} + +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, DNS_TRANSACTION_VALIDATING)) + return; + + if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) { + bool we_lost = false; + + /* The probe got a successful reply. If we so far + * weren't established we just give up. If we already + * were established, and the peer has the + * lexicographically larger IP address we continue + * and defend it. */ + + if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) { + log_debug("Got a successful probe for not yet established RR, we lost."); + we_lost = true; + } else { + assert(i->probe_transaction->received); + we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0; + if (we_lost) + log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost."); + } + + if (we_lost) { + dns_zone_item_conflict(i); + return; + } + + log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost."); + } + + 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) { + int r; + + assert(i); + + if (i->state != DNS_ZONE_ITEM_ESTABLISHED) + return 0; + + 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); + if (r < 0) { + log_error_errno(r, "Failed to start probing for verifying RR: %m"); + i->state = DNS_ZONE_ITEM_ESTABLISHED; + return r; + } + + return 0; +} + +int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { + DnsZoneItem *i, *first; + int c = 0; + + assert(zone); + assert(rr); + + /* This checks whether a response RR we received from somebody + * else is one that we actually thought was uniquely ours. If + * so, we'll verify our RRs. */ + + /* No conflict if we don't have the name at all. */ + first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key)); + if (!first) + return 0; + + /* No conflict if we have the exact same RR */ + if (dns_zone_get(zone, rr)) + return 0; + + /* OK, somebody else has RRs for the same name. Yuck! Let's + * start probing again */ + + LIST_FOREACH(by_name, i, first) { + if (dns_resource_record_equal(i->rr, rr)) + continue; + + dns_zone_item_verify(i); + c++; + } + + return c; +} + +int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { + DnsZoneItem *i, *first; + int c = 0; + + assert(zone); + + /* Somebody else notified us about a possible conflict. Let's + * verify if that's true. */ + + first = hashmap_get(zone->by_name, dns_resource_key_name(key)); + if (!first) + return 0; + + LIST_FOREACH(by_name, i, first) { + dns_zone_item_verify(i); + c++; + } + + return c; +} + +void dns_zone_verify_all(DnsZone *zone) { + DnsZoneItem *i; + Iterator iterator; + + assert(zone); + + HASHMAP_FOREACH(i, zone->by_key, iterator) { + DnsZoneItem *j; + + LIST_FOREACH(by_key, j, i) + dns_zone_item_verify(j); + } +} + +void dns_zone_dump(DnsZone *zone, FILE *f) { + Iterator iterator; + DnsZoneItem *i; + + if (!zone) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, zone->by_key, iterator) { + DnsZoneItem *j; + + LIST_FOREACH(by_key, j, i) { + const char *t; + + t = dns_resource_record_to_string(j->rr); + if (!t) { + log_oom(); + continue; + } + + fputc('\t', f); + fputs(t, f); + fputc('\n', f); + } + } +} + +bool dns_zone_is_empty(DnsZone *zone) { + if (!zone) + return true; + + return hashmap_isempty(zone->by_key); +} diff --git a/src/grp-resolve/systemd-resolved/resolved-dns-zone.h b/src/grp-resolve/systemd-resolved/resolved-dns-zone.h new file mode 100644 index 0000000000..789fe24cde --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-dns-zone.h @@ -0,0 +1,82 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/hashmap.h" + +typedef struct DnsZone { + Hashmap *by_key; + Hashmap *by_name; +} DnsZone; + +#include "resolved-dns-answer.h" +#include "resolved-dns-question.h" +#include "resolved-dns-rr.h" + +typedef enum DnsZoneItemState DnsZoneItemState; +typedef struct DnsZoneItem DnsZoneItem; + +#include "resolved-dns-transaction.h" + +/* RFC 4795 Section 2.8. suggests a TTL of 30s by default */ +#define LLMNR_DEFAULT_TTL (30) + +enum DnsZoneItemState { + DNS_ZONE_ITEM_PROBING, + DNS_ZONE_ITEM_ESTABLISHED, + DNS_ZONE_ITEM_VERIFYING, + DNS_ZONE_ITEM_WITHDRAWN, +}; + +struct DnsZoneItem { + DnsScope *scope; + DnsResourceRecord *rr; + + DnsZoneItemState state; + + unsigned block_ready; + + bool probing_enabled; + + LIST_FIELDS(DnsZoneItem, by_key); + LIST_FIELDS(DnsZoneItem, by_name); + + DnsTransaction *probe_transaction; +}; + +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, DnsResourceKey *key, int ifindex, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); + +void dns_zone_item_conflict(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); + +void dns_zone_verify_all(DnsZone *zone); + +void dns_zone_item_probe_stop(DnsZoneItem *i); + +void dns_zone_dump(DnsZone *zone, FILE *f); +bool dns_zone_is_empty(DnsZone *zone); diff --git a/src/grp-resolve/systemd-resolved/resolved-etc-hosts.c b/src/grp-resolve/systemd-resolved/resolved-etc-hosts.c new file mode 100644 index 0000000000..2bab6fdc35 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-etc-hosts.c @@ -0,0 +1,449 @@ +/*** + 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 . +***/ + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" + +#include "resolved-dns-synthesize.h" +#include "resolved-etc-hosts.h" + +/* Recheck /etc/hosts at most once every 2s */ +#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC) + +typedef struct EtcHostsItem { + int family; + union in_addr_union address; + + char **names; +} EtcHostsItem; + +typedef struct EtcHostsItemByName { + char *name; + + EtcHostsItem **items; + size_t n_items, n_allocated; +} EtcHostsItemByName; + +void manager_etc_hosts_flush(Manager *m) { + EtcHostsItem *item; + EtcHostsItemByName *bn; + + while ((item = set_steal_first(m->etc_hosts_by_address))) { + strv_free(item->names); + free(item); + } + + while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) { + free(bn->name); + free(bn->items); + free(bn); + } + + m->etc_hosts_by_address = set_free(m->etc_hosts_by_address); + m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name); + + m->etc_hosts_mtime = USEC_INFINITY; +} + +static void etc_hosts_item_hash_func(const void *p, struct siphash *state) { + const EtcHostsItem *item = p; + + siphash24_compress(&item->family, sizeof(item->family), state); + + if (item->family == AF_INET) + siphash24_compress(&item->address.in, sizeof(item->address.in), state); + else if (item->family == AF_INET6) + siphash24_compress(&item->address.in6, sizeof(item->address.in6), state); +} + +static int etc_hosts_item_compare_func(const void *a, const void *b) { + const EtcHostsItem *x = a, *y = b; + + if (x->family != y->family) + return x->family - y->family; + + if (x->family == AF_INET) + return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr)); + + if (x->family == AF_INET6) + return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr)); + + return trivial_compare_func(a, b); +} + +static const struct hash_ops etc_hosts_item_ops = { + .hash = etc_hosts_item_hash_func, + .compare = etc_hosts_item_compare_func, +}; + +static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) { + + EtcHostsItem key = { + .family = family, + .address = *address, + }; + EtcHostsItem *item; + char **n; + int r; + + assert(m); + assert(address); + + r = in_addr_is_null(family, address); + if (r < 0) + return r; + if (r > 0) + /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to + * nothing. */ + item = NULL; + else { + /* If this is a normal address, then, simply add entry mapping it to the specified names */ + + item = set_get(m->etc_hosts_by_address, &key); + if (item) { + r = strv_extend_strv(&item->names, names, true); + if (r < 0) + return log_oom(); + } else { + + r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops); + if (r < 0) + return log_oom(); + + item = new0(EtcHostsItem, 1); + if (!item) + return log_oom(); + + item->family = family; + item->address = *address; + item->names = names; + + r = set_put(m->etc_hosts_by_address, item); + if (r < 0) { + free(item); + return log_oom(); + } + } + } + + STRV_FOREACH(n, names) { + EtcHostsItemByName *bn; + + bn = hashmap_get(m->etc_hosts_by_name, *n); + if (!bn) { + r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops); + if (r < 0) + return log_oom(); + + bn = new0(EtcHostsItemByName, 1); + if (!bn) + return log_oom(); + + bn->name = strdup(*n); + if (!bn->name) { + free(bn); + return log_oom(); + } + + r = hashmap_put(m->etc_hosts_by_name, bn->name, bn); + if (r < 0) { + free(bn->name); + free(bn); + return log_oom(); + } + } + + if (item) { + if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1)) + return log_oom(); + + bn->items[bn->n_items++] = item; + } + } + + return 0; +} + +static int parse_line(Manager *m, unsigned nr, const char *line) { + _cleanup_free_ char *address = NULL; + _cleanup_strv_free_ char **names = NULL; + union in_addr_union in; + bool suppressed = false; + int family, r; + + assert(m); + assert(line); + + r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX); + if (r < 0) + return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr); + if (r == 0) { + log_error("Premature end of line, in line /etc/hosts:%u.", nr); + return -EINVAL; + } + + r = in_addr_from_string_auto(address, &family, &in); + if (r < 0) + return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr); + + for (;;) { + _cleanup_free_ char *name = NULL; + + r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX); + if (r < 0) + return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr); + if (r == 0) + break; + + r = dns_name_is_valid(name); + if (r <= 0) + return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr); + + if (is_localhost(name)) { + /* Suppress the "localhost" line that is often seen */ + suppressed = true; + continue; + } + + r = strv_push(&names, name); + if (r < 0) + return log_oom(); + + name = NULL; + } + + if (strv_isempty(names)) { + + if (suppressed) + return 0; + + log_error("Line is missing any host names, in line /etc/hosts:%u.", nr); + return -EINVAL; + } + + /* Takes possession of the names strv */ + r = add_item(m, family, &in, names); + if (r < 0) + return r; + + names = NULL; + return r; +} + +int manager_etc_hosts_read(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + struct stat st; + usec_t ts; + unsigned nr = 0; + int r; + + assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0); + + /* See if we checked /etc/hosts recently already */ + if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts) + return 0; + + m->etc_hosts_last = ts; + + if (m->etc_hosts_mtime != USEC_INFINITY) { + if (stat("/etc/hosts", &st) < 0) { + if (errno == ENOENT) { + r = 0; + goto clear; + } + + return log_error_errno(errno, "Failed to stat /etc/hosts: %m"); + } + + /* Did the mtime change? If not, there's no point in re-reading the file. */ + if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime) + return 0; + } + + f = fopen("/etc/hosts", "re"); + if (!f) { + if (errno == ENOENT) { + r = 0; + goto clear; + } + + return log_error_errno(errno, "Failed to open /etc/hosts: %m"); + } + + /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next + * invocation */ + r = fstat(fileno(f), &st); + if (r < 0) + return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m"); + + manager_etc_hosts_flush(m); + + FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) { + char *l; + + nr++; + + l = strstrip(line); + if (isempty(l)) + continue; + if (l[0] == '#') + continue; + + r = parse_line(m, nr, l); + if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */ + goto clear; + } + + m->etc_hosts_mtime = timespec_load(&st.st_mtim); + m->etc_hosts_last = ts; + + return 1; + +clear: + manager_etc_hosts_flush(m); + return r; +} + +int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { + bool found_a = false, found_aaaa = false; + EtcHostsItemByName *bn; + EtcHostsItem k = {}; + DnsResourceKey *t; + const char *name; + unsigned i; + int r; + + assert(m); + assert(q); + assert(answer); + + r = manager_etc_hosts_read(m); + if (r < 0) + return r; + + name = dns_question_first_name(q); + if (!name) + return 0; + + r = dns_name_address(name, &k.family, &k.address); + if (r > 0) { + EtcHostsItem *item; + DnsResourceKey *found_ptr = NULL; + + item = set_get(m->etc_hosts_by_address, &k); + if (!item) + return 0; + + /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data + * we'll only return if the request was for PTR. */ + + DNS_QUESTION_FOREACH(t, q) { + if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) + continue; + if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) + continue; + + r = dns_name_equal(dns_resource_key_name(t), name); + if (r < 0) + return r; + if (r > 0) { + found_ptr = t; + break; + } + } + + if (found_ptr) { + char **n; + + r = dns_answer_reserve(answer, strv_length(item->names)); + if (r < 0) + return r; + + STRV_FOREACH(n, item->names) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + rr = dns_resource_record_new(found_ptr); + if (!rr) + return -ENOMEM; + + rr->ptr.name = strdup(*n); + if (!rr->ptr.name) + return -ENOMEM; + + r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + } + + return 1; + } + + bn = hashmap_get(m->etc_hosts_by_name, name); + if (!bn) + return 0; + + r = dns_answer_reserve(answer, bn->n_items); + if (r < 0) + return r; + + DNS_QUESTION_FOREACH(t, q) { + if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY)) + continue; + if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) + continue; + + r = dns_name_equal(dns_resource_key_name(t), name); + if (r < 0) + return r; + if (r == 0) + continue; + + if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY)) + found_a = true; + if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) + found_aaaa = true; + + if (found_a && found_aaaa) + break; + } + + for (i = 0; i < bn->n_items; i++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + + if ((found_a && bn->items[i]->family != AF_INET) && + (found_aaaa && bn->items[i]->family != AF_INET6)) + continue; + + r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name); + if (r < 0) + return r; + + r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + } + + return 1; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-etc-hosts.h b/src/grp-resolve/systemd-resolved/resolved-etc-hosts.h new file mode 100644 index 0000000000..e68d87417e --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-etc-hosts.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include "resolved-dns-answer.h" +#include "resolved-dns-question.h" + +#include "resolved-manager.h" + +void manager_etc_hosts_flush(Manager *m); +int manager_etc_hosts_read(Manager *m); +int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer); diff --git a/src/grp-resolve/systemd-resolved/resolved-gperf.gperf b/src/grp-resolve/systemd-resolved/resolved-gperf.gperf new file mode 100644 index 0000000000..f3a7c48abc --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-gperf.gperf @@ -0,0 +1,24 @@ +%{ +#include + +#include "shared/conf-parser.h" + +#include "resolved-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name resolved_gperf_hash +%define lookup-function-name resolved_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +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) +Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) diff --git a/src/grp-resolve/systemd-resolved/resolved-link-bus.c b/src/grp-resolve/systemd-resolved/resolved-link-bus.c new file mode 100644 index 0000000000..9cbadece42 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-link-bus.c @@ -0,0 +1,630 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/parse-util.h" +#include "basic/strv.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" +#include "shared/resolve-util.h" + +#include "resolved-bus.h" +#include "resolved-link-bus.h" +#include "resolved-resolv-conf.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport); + +static int property_get_dnssec_mode( + 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, "s", dnssec_mode_to_string(link_get_dnssec_mode(l))); +} + +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', "(sb)"); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, l->search_domains) { + r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only); + 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)); +} + +static int verify_unmanaged_link(Link *l, sd_bus_error *error) { + assert(l); + + 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); + + return 0; +} + +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 = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + 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"); + + if (!dns_server_address_valid(family, d)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address"); + + 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, 0); + 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, 0); + if (r < 0) + goto clear; + } + + } + + dns_server_unlink_marked(l->dns_servers); + link_allocate_scopes(l); + + (void) link_save_user(l); + (void) manager_write_resolv_conf(l->manager); + + return sd_bus_reply_method_return(message, NULL); + +clear: + dns_server_unlink_all(l->dns_servers); + return r; +} + +int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Link *l = userdata; + int r; + + assert(message); + assert(l); + + r = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(message, 'a', "(sb)"); + if (r < 0) + return r; + + for (;;) { + const char *name; + int route_only; + + r = sd_bus_message_read(message, "(sb)", &name, &route_only); + if (r < 0) + return r; + if (r == 0) + break; + + 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 search domain %s", name); + if (!route_only && dns_name_is_root(name)) + 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); + + r = sd_bus_message_rewind(message, false); + if (r < 0) + return r; + + for (;;) { + DnsSearchDomain *d; + const char *name; + int route_only; + + r = sd_bus_message_read(message, "(sb)", &name, &route_only); + if (r < 0) + goto clear; + if (r == 0) + break; + + r = dns_search_domain_find(l->search_domains, name, &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, &d, DNS_SEARCH_DOMAIN_LINK, l, name); + if (r < 0) + goto clear; + } + + d->route_only = route_only; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + goto clear; + + dns_search_domain_unlink_marked(l->search_domains); + + (void) link_save_user(l); + (void) manager_write_resolv_conf(l->manager); + + 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 = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + 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); + + (void) link_save_user(l); + + 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 = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + 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); + + (void) link_save_user(l); + + 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 = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + 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); + + (void) link_save_user(l); + + 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 = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + 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 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; + + (void) link_save_user(l); + + 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; + int r; + + assert(message); + assert(l); + + r = verify_unmanaged_link(l, error); + if (r < 0) + return r; + + link_flush_settings(l); + link_allocate_scopes(l); + link_add_rrs(l, false); + + (void) link_save_user(l); + (void) manager_write_resolv_conf(l->manager); + + 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", "a(sb)", 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, 0, 0), + SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "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", "a(sb)", NULL, bus_link_method_set_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/grp-resolve/systemd-resolved/resolved-link-bus.h b/src/grp-resolve/systemd-resolved/resolved-link-bus.h new file mode 100644 index 0000000000..b1ac57961d --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-link-bus.h @@ -0,0 +1,38 @@ +#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 . +***/ + +#include + +#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_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/grp-resolve/systemd-resolved/resolved-link.c b/src/grp-resolve/systemd-resolved/resolved-link.c new file mode 100644 index 0000000000..d37ebbc419 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-link.c @@ -0,0 +1,1115 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "sd-network/sd-network.h" + +#include "resolved-link.h" + +int link_new(Manager *m, Link **ret, int ifindex) { + _cleanup_(link_freep) Link *l = NULL; + int r; + + assert(m); + assert(ifindex > 0); + + r = hashmap_ensure_allocated(&m->links, NULL); + if (r < 0) + return r; + + l = new0(Link, 1); + if (!l) + return -ENOMEM; + + l->ifindex = ifindex; + l->llmnr_support = RESOLVE_SUPPORT_YES; + l->mdns_support = RESOLVE_SUPPORT_NO; + l->dnssec_mode = _DNSSEC_MODE_INVALID; + l->operstate = IF_OPER_UNKNOWN; + + if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0) + return -ENOMEM; + + r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); + if (r < 0) + return r; + + l->manager = m; + + if (ret) + *ret = l; + l = NULL; + + 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) + (void) link_address_free(l->addresses); + + if (l->manager) + hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); + + 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->state_file); + + free(l); + return NULL; +} + +void link_allocate_scopes(Link *l) { + int r; + + assert(l); + + 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) + log_warning_errno(r, "Failed to allocate DNS scope: %m"); + } + } else + l->unicast_scope = dns_scope_free(l->unicast_scope); + + 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) + log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m"); + } + } else + l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); + + 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); + if (r < 0) + log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m"); + } + } 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) { + LinkAddress *a; + + LIST_FOREACH(addresses, a, l->addresses) + link_address_add_rrs(a, force_remove); +} + +int link_process_rtnl(Link *l, sd_netlink_message *m) { + const char *n = NULL; + int r; + + assert(l); + assert(m); + + r = sd_rtnl_message_link_get_flags(m, &l->flags); + if (r < 0) + return r; + + (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu); + (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate); + + if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) { + strncpy(l->name, n, sizeof(l->name)-1); + char_array_0(l->name); + } + + link_allocate_scopes(l); + link_add_rrs(l, false); + + return 0; +} + +static int link_update_dns_server_one(Link *l, const char *name) { + union in_addr_union a; + DnsServer *s; + int family, r; + + assert(l); + assert(name); + + r = in_addr_from_string_auto(name, &family, &a); + if (r < 0) + return r; + + s = dns_server_find(l->dns_servers, family, &a, 0); + if (s) { + dns_server_move_back_and_unmark(s); + return 0; + } + + return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0); +} + +static int link_update_dns_servers(Link *l) { + _cleanup_strv_free_ char **nameservers = NULL; + char **nameserver; + 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; + + dns_server_mark_all(l->dns_servers); + + STRV_FOREACH(nameserver, nameservers) { + r = link_update_dns_server_one(l, *nameserver); + if (r < 0) + goto clear; + } + + dns_server_unlink_marked(l->dns_servers); + return 0; + +clear: + 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; + + 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_mdns_support(Link *l) { + _cleanup_free_ char *b = NULL; + int r; + + assert(l); + + r = sd_network_link_get_mdns(l->ifindex, &b); + if (r == -ENODATA) { + r = 0; + goto clear; + } + if (r < 0) + 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; + int r; + + assert(l); + + 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; + + r = set_put_strdupv(ns, ntas); + 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->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors); + return r; +} + +static int link_update_search_domain_one(Link *l, const char *name, bool route_only) { + DnsSearchDomain *d; + int r; + + assert(l); + assert(name); + + r = dns_search_domain_find(l->search_domains, name, &d); + if (r < 0) + return r; + if (r > 0) + dns_search_domain_move_back_and_unmark(d); + else { + r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name); + if (r < 0) + return r; + } + + d->route_only = route_only; + return 0; +} + +static int link_update_search_domains(Link *l) { + _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL; + char **i; + int r, q; + + assert(l); + + r = sd_network_link_get_search_domains(l->ifindex, &sdomains); + if (r < 0 && r != -ENODATA) + goto clear; + + q = sd_network_link_get_route_domains(l->ifindex, &rdomains); + if (q < 0 && q != -ENODATA) { + r = q; + goto clear; + } + + if (r == -ENODATA && q == -ENODATA) { + /* networkd knows nothing about this interface, and that's fine. */ + r = 0; + goto clear; + } + + dns_search_domain_mark_all(l->search_domains); + + STRV_FOREACH(i, sdomains) { + r = link_update_search_domain_one(l, *i, false); + if (r < 0) + goto clear; + } + + STRV_FOREACH(i, rdomains) { + r = link_update_search_domain_one(l, *i, true); + 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_managed(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 0; + if (r < 0) + return r; + + 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_managed(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(Link *l) { + assert(l); + + link_read_settings(l); + link_load_user(l); + link_allocate_scopes(l); + link_add_rrs(l, false); + + return 0; +} + +bool link_relevant(Link *l, int family, bool local_multicast) { + _cleanup_free_ char *state = NULL; + LinkAddress *a; + + assert(l); + + /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link + * beat, can do multicast and has at least one link-local (or better) IP address. + * + * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at + * least one routable address.*/ + + if (l->flags & (IFF_LOOPBACK|IFF_DORMANT)) + return false; + + if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP)) + return false; + + if (local_multicast) { + if (l->flags & IFF_POINTOPOINT) + return false; + + if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST) + return false; + } + + /* Check kernel operstate + * https://www.kernel.org/doc/Documentation/networking/operstates.txt */ + if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP)) + return false; + + (void) 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 ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a, local_multicast)) + return true; + + return false; +} + +LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) { + LinkAddress *a; + + assert(l); + + LIST_FOREACH(addresses, a, l->addresses) + if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr)) + return a; + + return NULL; +} + +DnsServer* link_set_dns_server(Link *l, DnsServer *s) { + assert(l); + + if (l->current_dns_server == s) + return s; + + if (s) + log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name); + + 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); + + return s; +} + +DnsServer *link_get_dns_server(Link *l) { + assert(l); + + if (!l->current_dns_server) + link_set_dns_server(l, l->dns_servers); + + return l->current_dns_server; +} + +void link_next_dns_server(Link *l) { + assert(l); + + if (!l->current_dns_server) + return; + + /* 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; + } + + 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; + + assert(l); + assert(in_addr); + + a = new0(LinkAddress, 1); + if (!a) + return -ENOMEM; + + a->family = family; + a->in_addr = *in_addr; + + a->link = l; + LIST_PREPEND(addresses, l->addresses, a); + + if (ret) + *ret = a; + + return 0; +} + +LinkAddress *link_address_free(LinkAddress *a) { + if (!a) + return NULL; + + if (a->link) { + LIST_REMOVE(addresses, a->link->addresses, a); + + if (a->llmnr_address_rr) { + if (a->family == AF_INET && a->link->llmnr_ipv4_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); + else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); + } + + if (a->llmnr_ptr_rr) { + if (a->family == AF_INET && a->link->llmnr_ipv4_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); + else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); + } + } + + dns_resource_record_unref(a->llmnr_address_rr); + dns_resource_record_unref(a->llmnr_ptr_rr); + + free(a); + return NULL; +} + +void link_address_add_rrs(LinkAddress *a, bool force_remove) { + int r; + + assert(a); + + if (a->family == AF_INET) { + + if (!force_remove && + link_address_relevant(a, true) && + a->link->llmnr_ipv4_scope && + 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); + if (!a->link->manager->llmnr_host_ipv4_key) { + r = -ENOMEM; + goto fail; + } + } + + if (!a->llmnr_address_rr) { + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key); + if (!a->llmnr_address_rr) { + r = -ENOMEM; + goto fail; + } + + a->llmnr_address_rr->a.in_addr = a->in_addr.in; + a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; + } + + if (!a->llmnr_ptr_rr) { + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); + if (r < 0) + goto fail; + + a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; + } + + r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true); + if (r < 0) + log_warning_errno(r, "Failed to add A record to LLMNR zone: %m"); + + r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); + if (r < 0) + log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); + } else { + if (a->llmnr_address_rr) { + if (a->link->llmnr_ipv4_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); + a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); + } + + if (a->llmnr_ptr_rr) { + if (a->link->llmnr_ipv4_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); + a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); + } + } + } + + if (a->family == AF_INET6) { + + if (!force_remove && + link_address_relevant(a, true) && + a->link->llmnr_ipv6_scope && + 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); + if (!a->link->manager->llmnr_host_ipv6_key) { + r = -ENOMEM; + goto fail; + } + } + + if (!a->llmnr_address_rr) { + a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key); + if (!a->llmnr_address_rr) { + r = -ENOMEM; + goto fail; + } + + a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6; + a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; + } + + if (!a->llmnr_ptr_rr) { + r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); + if (r < 0) + goto fail; + + a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; + } + + r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true); + if (r < 0) + log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m"); + + r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false); + if (r < 0) + log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); + } else { + if (a->llmnr_address_rr) { + if (a->link->llmnr_ipv6_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); + a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); + } + + if (a->llmnr_ptr_rr) { + if (a->link->llmnr_ipv6_scope) + dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); + a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); + } + } + } + + return; + +fail: + log_debug_errno(r, "Failed to update address RRs: %m"); +} + +int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) { + int r; + assert(a); + assert(m); + + r = sd_rtnl_message_addr_get_flags(m, &a->flags); + if (r < 0) + return r; + + sd_rtnl_message_addr_get_scope(m, &a->scope); + + link_allocate_scopes(a->link); + link_add_rrs(a->link, false); + + return 0; +} + +bool link_address_relevant(LinkAddress *a, bool local_multicast) { + assert(a); + + if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE)) + return false; + + if (a->scope >= (local_multicast ? RT_SCOPE_HOST : RT_SCOPE_LINK)) + return false; + + return true; +} + +static bool link_needs_save(Link *l) { + assert(l); + + /* Returns true if any of the settings where set different from the default */ + + if (l->is_managed) + return false; + + if (l->llmnr_support != RESOLVE_SUPPORT_YES || + l->mdns_support != RESOLVE_SUPPORT_NO || + l->dnssec_mode != _DNSSEC_MODE_INVALID) + return true; + + if (l->dns_servers || + l->search_domains) + return true; + + if (!set_isempty(l->dnssec_negative_trust_anchors)) + return true; + + return false; +} + +int link_save_user(Link *l) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *v; + int r; + + assert(l); + assert(l->state_file); + + if (!link_needs_save(l)) { + (void) unlink(l->state_file); + return 0; + } + + r = mkdir_parents(l->state_file, 0700); + if (r < 0) + goto fail; + + r = fopen_temporary(l->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + fputs("# This is private data. Do not parse.\n", f); + + v = resolve_support_to_string(l->llmnr_support); + if (v) + fprintf(f, "LLMNR=%s\n", v); + + v = resolve_support_to_string(l->mdns_support); + if (v) + fprintf(f, "MDNS=%s\n", v); + + v = dnssec_mode_to_string(l->dnssec_mode); + if (v) + fprintf(f, "DNSSEC=%s\n", v); + + if (l->dns_servers) { + DnsServer *server; + + fputs("SERVERS=", f); + LIST_FOREACH(servers, server, l->dns_servers) { + + if (server != l->dns_servers) + fputc(' ', f); + + v = dns_server_string(server); + if (!v) { + r = -ENOMEM; + goto fail; + } + + fputs(v, f); + } + fputc('\n', f); + } + + if (l->search_domains) { + DnsSearchDomain *domain; + + fputs("DOMAINS=", f); + LIST_FOREACH(domains, domain, l->search_domains) { + + if (domain != l->search_domains) + fputc(' ', f); + + if (domain->route_only) + fputc('~', f); + + fputs(DNS_SEARCH_DOMAIN_NAME(domain), f); + } + fputc('\n', f); + } + + if (!set_isempty(l->dnssec_negative_trust_anchors)) { + bool space = false; + Iterator i; + char *nta; + + fputs("NTAS=", f); + SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) { + + if (space) + fputc(' ', f); + + fputs(nta, f); + space = true; + } + fputc('\n', f); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, l->state_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(l->state_file); + + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save link data %s: %m", l->state_file); +} + +int link_load_user(Link *l) { + _cleanup_free_ char + *llmnr = NULL, + *mdns = NULL, + *dnssec = NULL, + *servers = NULL, + *domains = NULL, + *ntas = NULL; + + ResolveSupport s; + int r; + + assert(l); + assert(l->state_file); + + /* Try to load only a single time */ + if (l->loaded) + return 0; + l->loaded = true; + + if (l->is_managed) + return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */ + + r = parse_env_file(l->state_file, NEWLINE, + "LLMNR", &llmnr, + "MDNS", &mdns, + "DNSSEC", &dnssec, + "SERVERS", &servers, + "DOMAINS", &domains, + "NTAS", &ntas, + NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + goto fail; + + link_flush_settings(l); + + /* If we can't recognize the LLMNR or MDNS setting we don't override the default */ + s = resolve_support_from_string(llmnr); + if (s >= 0) + l->llmnr_support = s; + + s = resolve_support_from_string(mdns); + if (s >= 0) + l->mdns_support = s; + + /* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */ + l->dnssec_mode = dnssec_mode_from_string(dnssec); + + if (servers) { + const char *p = servers; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + goto fail; + if (r == 0) + break; + + r = link_update_dns_server_one(l, word); + if (r < 0) { + log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word); + continue; + } + } + } + + if (domains) { + const char *p = domains; + + for (;;) { + _cleanup_free_ char *word = NULL; + const char *n; + bool is_route; + + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + goto fail; + if (r == 0) + break; + + is_route = word[0] == '~'; + n = is_route ? word + 1 : word; + + r = link_update_search_domain_one(l, n, is_route); + if (r < 0) { + log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word); + continue; + } + } + } + + if (ntas) { + _cleanup_set_free_free_ Set *ns = NULL; + + ns = set_new(&dns_name_hash_ops); + if (!ns) { + r = -ENOMEM; + goto fail; + } + + r = set_put_strsplit(ns, ntas, NULL, 0); + if (r < 0) + goto fail; + + l->dnssec_negative_trust_anchors = ns; + ns = NULL; + } + + return 0; + +fail: + return log_error_errno(r, "Failed to load link data %s: %m", l->state_file); +} + +void link_remove_user(Link *l) { + assert(l); + assert(l->state_file); + + (void) unlink(l->state_file); +} diff --git a/src/grp-resolve/systemd-resolved/resolved-link.h b/src/grp-resolve/systemd-resolved/resolved-link.h new file mode 100644 index 0000000000..0d7ce15f65 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-link.h @@ -0,0 +1,118 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/in-addr-util.h" +#include "basic/ratelimit.h" +#include "resolved-dns-rr.h" +#include "shared/resolve-util.h" + +typedef struct Link Link; +typedef struct LinkAddress LinkAddress; + +#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; + + int family; + union in_addr_union in_addr; + + unsigned char flags, scope; + + DnsResourceRecord *llmnr_address_rr; + DnsResourceRecord *llmnr_ptr_rr; + + LIST_FIELDS(LinkAddress, addresses); +}; + +struct Link { + Manager *manager; + + int ifindex; + unsigned flags; + + LIST_HEAD(LinkAddress, addresses); + + LIST_HEAD(DnsServer, dns_servers); + DnsServer *current_dns_server; + unsigned n_dns_servers; + + LIST_HEAD(DnsSearchDomain, search_domains); + unsigned n_search_domains; + + 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; + uint8_t operstate; + + bool loaded; + char *state_file; +}; + +int link_new(Manager *m, Link **ret, int ifindex); +Link *link_free(Link *l); +int link_process_rtnl(Link *l, sd_netlink_message *m); +int link_update(Link *l); +bool link_relevant(Link *l, int family, bool local_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_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_save_user(Link *l); +int link_load_user(Link *l); +void link_remove_user(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); +bool link_address_relevant(LinkAddress *l, bool local_multicast); +void link_address_add_rrs(LinkAddress *a, bool force_remove); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); diff --git a/src/grp-resolve/systemd-resolved/resolved-llmnr.c b/src/grp-resolve/systemd-resolved/resolved-llmnr.c new file mode 100644 index 0000000000..1092b8b659 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-llmnr.c @@ -0,0 +1,472 @@ +/*** + 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 . + ***/ + +#include +#include + +#include "basic/fd-util.h" + +#include "resolved-llmnr.h" +#include "resolved-manager.h" + +void manager_llmnr_stop(Manager *m) { + assert(m); + + m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source); + m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); + + m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source); + m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); + + m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source); + m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); + + m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source); + m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); +} + +int manager_llmnr_start(Manager *m) { + int r; + + assert(m); + + if (m->llmnr_support == RESOLVE_SUPPORT_NO) + return 0; + + r = manager_llmnr_ipv4_udp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + r = manager_llmnr_ipv4_tcp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + if (socket_ipv6_is_supported()) { + r = manager_llmnr_ipv6_udp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + + r = manager_llmnr_ipv6_tcp_fd(m); + if (r == -EADDRINUSE) + goto eaddrinuse; + if (r < 0) + return r; + } + + return 0; + +eaddrinuse: + log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support."); + m->llmnr_support = RESOLVE_SUPPORT_NO; + manager_llmnr_stop(m); + + return 0; +} + +static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + DnsTransaction *t = NULL; + Manager *m = userdata; + DnsScope *scope; + int r; + + assert(s); + assert(fd >= 0); + assert(m); + + r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); + if (r <= 0) + return r; + + scope = manager_find_scope(m, p); + if (!scope) + log_warning("Got LLMNR UDP packet on unknown scope. Ignoring."); + else if (dns_packet_validate_reply(p) > 0) { + log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p)); + + dns_scope_check_conflicts(scope, p); + + t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); + if (t) + dns_transaction_process_reply(t, p); + + } else if (dns_packet_validate_query(p) > 0) { + log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p)); + + dns_scope_process_query(scope, NULL, p); + } else + log_debug("Invalid LLMNR UDP packet, ignoring."); + + return 0; +} + +int manager_llmnr_ipv4_udp_fd(Manager *m) { + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(LLMNR_PORT), + }; + static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255; + int r; + + assert(m); + + if (m->llmnr_ipv4_udp_fd >= 0) + return m->llmnr_ipv4_udp_fd; + + m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->llmnr_ipv4_udp_fd < 0) + return -errno; + + /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ + r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_udp_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->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, 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: + m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); + return r; +} + +int manager_llmnr_ipv6_udp_fd(Manager *m) { + union sockaddr_union sa = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(LLMNR_PORT), + }; + static const int one = 1, ttl = 255; + int r; + + assert(m); + + if (m->llmnr_ipv6_udp_fd >= 0) + return m->llmnr_ipv6_udp_fd; + + m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->llmnr_ipv6_udp_fd < 0) + return -errno; + + r = setsockopt(m->llmnr_ipv6_udp_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->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = -errno; + goto fail; + } + + 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) + goto fail; + + (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp"); + + return m->llmnr_ipv6_udp_fd; + +fail: + m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); + return r; +} + +static int on_llmnr_stream_packet(DnsStream *s) { + DnsScope *scope; + + assert(s); + assert(s->read_packet); + + scope = manager_find_scope(s->manager, s->read_packet); + if (!scope) + log_warning("Got LLMNR TCP packet on unknown scope. Ignoring."); + else if (dns_packet_validate_query(s->read_packet) > 0) { + log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); + + dns_scope_process_query(scope, s, s->read_packet); + } else + log_debug("Invalid LLMNR TCP packet, ignoring."); + + dns_stream_unref(s); + return 0; +} + +static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + DnsStream *stream; + Manager *m = userdata; + int cfd, r; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd); + if (r < 0) { + safe_close(cfd); + return r; + } + + stream->on_packet = on_llmnr_stream_packet; + return 0; +} + +int manager_llmnr_ipv4_tcp_fd(Manager *m) { + union sockaddr_union sa = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(LLMNR_PORT), + }; + static const int one = 1, pmtu = IP_PMTUDISC_DONT; + int r; + + assert(m); + + if (m->llmnr_ipv4_tcp_fd >= 0) + return m->llmnr_ipv4_tcp_fd; + + m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->llmnr_ipv4_tcp_fd < 0) + return -errno; + + /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ + r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv4_tcp_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->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, 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: + m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); + return r; +} + +int manager_llmnr_ipv6_tcp_fd(Manager *m) { + union sockaddr_union sa = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(LLMNR_PORT), + }; + static const int one = 1; + int r; + + assert(m); + + if (m->llmnr_ipv6_tcp_fd >= 0) + return m->llmnr_ipv6_tcp_fd; + + m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (m->llmnr_ipv6_tcp_fd < 0) + return -errno; + + /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ + r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN); + if (r < 0) { + r = -errno; + goto fail; + } + + 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) + goto fail; + + (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp"); + + return m->llmnr_ipv6_tcp_fd; + +fail: + m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); + return r; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-llmnr.h b/src/grp-resolve/systemd-resolved/resolved-llmnr.h new file mode 100644 index 0000000000..8133582fa7 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-llmnr.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "resolved-manager.h" + +#define LLMNR_PORT 5355 + +int manager_llmnr_ipv4_udp_fd(Manager *m); +int manager_llmnr_ipv6_udp_fd(Manager *m); +int manager_llmnr_ipv4_tcp_fd(Manager *m); +int manager_llmnr_ipv6_tcp_fd(Manager *m); + +void manager_llmnr_stop(Manager *m); +int manager_llmnr_start(Manager *m); diff --git a/src/grp-resolve/systemd-resolved/resolved-manager.c b/src/grp-resolve/systemd-resolved/resolved-manager.c new file mode 100644 index 0000000000..800f06db13 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-manager.c @@ -0,0 +1,1379 @@ +/*** + 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 . + ***/ + +#include +#include +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/ordered-set.h" +#include "basic/parse-util.h" +#include "basic/random-util.h" +#include "basic/socket-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/utf8.h" +#include "sd-netlink/netlink-util.h" +#include "shared/dns-domain.h" +#include "systemd-network/network-internal.h" + +#include "resolved-bus.h" +#include "resolved-conf.h" +#include "resolved-dns-stub.h" +#include "resolved-etc-hosts.h" +#include "resolved-llmnr.h" +#include "resolved-manager.h" +#include "resolved-mdns.h" +#include "resolved-resolv-conf.h" + +#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) + +static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Manager *m = userdata; + uint16_t type; + Link *l; + int ifindex, r; + + assert(rtnl); + assert(m); + assert(mm); + + r = sd_netlink_message_get_type(mm, &type); + if (r < 0) + goto fail; + + r = sd_rtnl_message_link_get_ifindex(mm, &ifindex); + if (r < 0) + goto fail; + + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + + switch (type) { + + case RTM_NEWLINK:{ + bool is_new = !l; + + if (!l) { + r = link_new(m, &l, ifindex); + if (r < 0) + goto fail; + } + + r = link_process_rtnl(l, mm); + if (r < 0) + goto fail; + + r = link_update(l); + if (r < 0) + goto fail; + + if (is_new) + log_debug("Found new link %i/%s", ifindex, l->name); + + break; + } + + case RTM_DELLINK: + if (l) { + log_debug("Removing link %i/%s", l->ifindex, l->name); + link_remove_user(l); + link_free(l); + } + + break; + } + + return 0; + +fail: + log_warning_errno(r, "Failed to process RTNL link message: %m"); + return 0; +} + +static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Manager *m = userdata; + union in_addr_union address; + uint16_t type; + int r, ifindex, family; + LinkAddress *a; + Link *l; + + assert(rtnl); + assert(mm); + assert(m); + + r = sd_netlink_message_get_type(mm, &type); + if (r < 0) + goto fail; + + r = sd_rtnl_message_addr_get_ifindex(mm, &ifindex); + if (r < 0) + goto fail; + + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + if (!l) + return 0; + + r = sd_rtnl_message_addr_get_family(mm, &family); + if (r < 0) + goto fail; + + switch (family) { + + case AF_INET: + r = sd_netlink_message_read_in_addr(mm, IFA_LOCAL, &address.in); + if (r < 0) { + r = sd_netlink_message_read_in_addr(mm, IFA_ADDRESS, &address.in); + if (r < 0) + goto fail; + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(mm, IFA_LOCAL, &address.in6); + if (r < 0) { + r = sd_netlink_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6); + if (r < 0) + goto fail; + } + + break; + + default: + return 0; + } + + a = link_find_address(l, family, &address); + + switch (type) { + + case RTM_NEWADDR: + + if (!a) { + r = link_address_new(l, &a, family, &address); + if (r < 0) + return r; + } + + r = link_address_update_rtnl(a, mm); + if (r < 0) + return r; + + break; + + case RTM_DELADDR: + link_address_free(a); + break; + } + + return 0; + +fail: + log_warning_errno(r, "Failed to process RTNL address message: %m"); + return 0; +} + +static int manager_rtnl_listen(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *i; + int r; + + assert(m); + + /* First, subscribe to interfaces coming and going */ + r = sd_netlink_open(&m->rtnl); + if (r < 0) + return r; + + r = sd_netlink_attach_event(m->rtnl, m->event, SD_EVENT_PRIORITY_IMPORTANT); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m); + if (r < 0) + return r; + + r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m); + if (r < 0) + return r; + + /* Then, enumerate all links */ + r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (i = reply; i; i = sd_netlink_message_next(i)) { + r = manager_process_link(m->rtnl, i, m); + if (r < 0) + return r; + } + + req = sd_netlink_message_unref(req); + reply = sd_netlink_message_unref(reply); + + /* Finally, enumerate all addresses, too */ + r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (i = reply; i; i = sd_netlink_message_next(i)) { + r = manager_process_address(m->rtnl, i, m); + if (r < 0) + return r; + } + + return r; +} + +static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + Iterator i; + Link *l; + int r; + + assert(m); + + sd_network_monitor_flush(m->network_monitor); + + HASHMAP_FOREACH(l, m->links, i) { + r = link_update(l); + if (r < 0) + log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); + } + + (void) manager_write_resolv_conf(m); + + return 0; +} + +static int manager_network_monitor_listen(Manager *m) { + int r, fd, events; + + assert(m); + + r = sd_network_monitor_new(&m->network_monitor, NULL); + if (r < 0) + return r; + + fd = sd_network_monitor_get_fd(m->network_monitor); + if (fd < 0) + return fd; + + events = sd_network_monitor_get_events(m->network_monitor); + if (events < 0) + return events; + + r = sd_event_add_io(m->event, &m->network_event_source, fd, events, &on_network_event, m); + if (r < 0) + return r; + + r = sd_event_source_set_priority(m->network_event_source, SD_EVENT_PRIORITY_IMPORTANT+5); + if (r < 0) + return r; + + (void) sd_event_source_set_description(m->network_event_source, "network-monitor"); + + return 0; +} + +static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { + _cleanup_free_ char *h = NULL, *n = NULL; + char label[DNS_LABEL_MAX]; + const char *p; + int r, k; + + assert(llmnr_hostname); + assert(mdns_hostname); + + /* Extract and normalize the first label of the locally + * configured hostname, and check it's not "localhost". */ + + h = gethostname_malloc(); + if (!h) + return log_oom(); + + p = h; + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return log_error_errno(r, "Failed to unescape host name: %m"); + if (r == 0) { + log_error("Couldn't find a single label in hosntame."); + return -EINVAL; + } + + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + return log_error_errno(k, "Failed to undo IDNA: %m"); + if (k > 0) + r = k; + + if (!utf8_is_valid(label)) { + log_error("System hostname is not UTF-8 clean."); + return -EINVAL; + } + + r = dns_label_escape_new(label, r, &n); + if (r < 0) + return log_error_errno(r, "Failed to escape host name: %m"); + + if (is_localhost(n)) { + log_debug("System hostname is 'localhost', ignoring."); + return -EINVAL; + } + + r = dns_name_concat(n, "local", mdns_hostname); + if (r < 0) + return log_error_errno(r, "Failed to determine mDNS hostname: %m"); + + *llmnr_hostname = n; + n = NULL; + + return 0; +} + +static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; + Manager *m = userdata; + int r; + + assert(m); + + r = determine_hostname(&llmnr_hostname, &mdns_hostname); + if (r < 0) + return 0; /* ignore invalid hostnames */ + + if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) + return 0; + + log_info("System hostname changed to '%s'.", llmnr_hostname); + + free(m->llmnr_hostname); + free(m->mdns_hostname); + + m->llmnr_hostname = llmnr_hostname; + m->mdns_hostname = mdns_hostname; + + llmnr_hostname = mdns_hostname = NULL; + + manager_refresh_rrs(m); + + return 0; +} + +static int manager_watch_hostname(Manager *m) { + int r; + + assert(m); + + m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); + if (m->hostname_fd < 0) { + log_warning_errno(errno, "Failed to watch hostname: %m"); + return 0; + } + + r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m); + if (r < 0) { + if (r == -EPERM) + /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */ + m->hostname_fd = safe_close(m->hostname_fd); + else + 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 'gnu-linux'."); + m->llmnr_hostname = strdup("gnu-linux"); + if (!m->llmnr_hostname) + return log_oom(); + + m->mdns_hostname = strdup("gnu-linux.local"); + if (!m->mdns_hostname) + return log_oom(); + } else + log_info("Using system hostname '%s'.", m->llmnr_hostname); + + return 0; +} + +static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + size_t size = 0; + DnsScope *scope; + + assert(s); + assert(si); + assert(m); + + f = open_memstream(&buffer, &size); + if (!f) + return log_oom(); + + LIST_FOREACH(scopes, scope, m->dns_scopes) + dns_scope_dump(scope, f); + + if (fflush_and_check(f) < 0) + return log_oom(); + + log_dump(LOG_INFO, buffer); + return 0; +} + +static int manager_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(si); + assert(m); + + manager_flush_caches(m); + + return 0; +} + +int manager_new(Manager **ret) { + _cleanup_(manager_freep) Manager *m = NULL; + int r; + + assert(ret); + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + 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->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1; + m->hostname_fd = -1; + + m->llmnr_support = RESOLVE_SUPPORT_YES; + m->mdns_support = RESOLVE_SUPPORT_NO; + m->dnssec_mode = DEFAULT_DNSSEC_MODE; + m->enable_cache = true; + m->read_resolv_conf = true; + m->need_builtin_fallbacks = true; + m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY; + + r = dns_trust_anchor_load(&m->trust_anchor); + if (r < 0) + return r; + + r = manager_parse_config_file(m); + if (r < 0) + return r; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); + + sd_event_set_watchdog(m->event, true); + + r = manager_watch_hostname(m); + if (r < 0) + return r; + + r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC); + if (r < 0) + return r; + + r = manager_network_monitor_listen(m); + if (r < 0) + return r; + + r = manager_rtnl_listen(m); + if (r < 0) + return r; + + r = manager_connect_bus(m); + if (r < 0) + return r; + + (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); + (void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m); + + manager_cleanup_saved_user(m); + + *ret = m; + m = NULL; + + return 0; +} + +int manager_start(Manager *m) { + int r; + + assert(m); + + r = manager_dns_stub_start(m); + if (r < 0) + return r; + + r = manager_llmnr_start(m); + if (r < 0) + return r; + + r = manager_mdns_start(m); + if (r < 0) + return r; + + return 0; +} + +Manager *manager_free(Manager *m) { + Link *l; + + 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); + + dns_scope_free(m->unicast_scope); + + /* At this point only orphaned streams should remain. All others should have been freed already by their + * owners */ + while (m->dns_streams) + dns_stream_unref(m->dns_streams); + + hashmap_free(m->links); + hashmap_free(m->dns_transactions); + + 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); + manager_dns_stub_stop(m); + + sd_bus_slot_unref(m->prepare_for_sleep_slot); + sd_event_source_unref(m->bus_retry_event_source); + sd_bus_unref(m->bus); + + sd_event_source_unref(m->sigusr1_event_source); + sd_event_source_unref(m->sigusr2_event_source); + + sd_event_unref(m->event); + + dns_resource_key_unref(m->llmnr_host_ipv4_key); + dns_resource_key_unref(m->llmnr_host_ipv6_key); + + sd_event_source_unref(m->hostname_event_source); + safe_close(m->hostname_fd); + free(m->llmnr_hostname); + free(m->mdns_hostname); + + dns_trust_anchor_flush(&m->trust_anchor); + manager_etc_hosts_flush(m); + + free(m); + + return NULL; +} + +int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) + + CMSG_SPACE(int) /* ttl/hoplimit */ + + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */]; + } control; + union sockaddr_union sa; + struct msghdr mh = {}; + struct cmsghdr *cmsg; + struct iovec iov; + ssize_t ms, l; + int r; + + assert(m); + assert(fd >= 0); + assert(ret); + + ms = next_datagram_size_fd(fd); + if (ms < 0) + return ms; + + r = dns_packet_new(&p, protocol, ms); + if (r < 0) + return r; + + iov.iov_base = DNS_PACKET_DATA(p); + iov.iov_len = p->allocated; + + mh.msg_name = &sa.sa; + mh.msg_namelen = sizeof(sa); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = &control; + mh.msg_controllen = sizeof(control); + + l = recvmsg(fd, &mh, 0); + if (l == 0) + return 0; + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + assert(!(mh.msg_flags & MSG_CTRUNC)); + assert(!(mh.msg_flags & MSG_TRUNC)); + + p->size = (size_t) l; + + p->family = sa.sa.sa_family; + p->ipproto = IPPROTO_UDP; + if (p->family == AF_INET) { + p->sender.in = sa.in.sin_addr; + p->sender_port = be16toh(sa.in.sin_port); + } else if (p->family == AF_INET6) { + p->sender.in6 = sa.in6.sin6_addr; + p->sender_port = be16toh(sa.in6.sin6_port); + p->ifindex = sa.in6.sin6_scope_id; + } else + return -EAFNOSUPPORT; + + CMSG_FOREACH(cmsg, &mh) { + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + assert(p->family == AF_INET6); + + switch (cmsg->cmsg_type) { + + case IPV6_PKTINFO: { + struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); + + if (p->ifindex <= 0) + p->ifindex = i->ipi6_ifindex; + + p->destination.in6 = i->ipi6_addr; + break; + } + + case IPV6_HOPLIMIT: + p->ttl = *(int *) CMSG_DATA(cmsg); + break; + + } + } else if (cmsg->cmsg_level == IPPROTO_IP) { + assert(p->family == AF_INET); + + switch (cmsg->cmsg_type) { + + case IP_PKTINFO: { + struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); + + if (p->ifindex <= 0) + p->ifindex = i->ipi_ifindex; + + p->destination.in = i->ipi_addr; + break; + } + + case IP_TTL: + p->ttl = *(int *) CMSG_DATA(cmsg); + break; + } + } + } + + /* The Linux kernel sets the interface index to the loopback + * device if the packet came from the local host since it + * avoids the routing table in such a case. Let's unset the + * interface index in such a case. */ + if (p->ifindex == LOOPBACK_IFINDEX) + p->ifindex = 0; + + if (protocol != DNS_PROTOCOL_DNS) { + /* If we don't know the interface index still, we look for the + * first local interface with a matching address. Yuck! */ + if (p->ifindex <= 0) + p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + } + + *ret = p; + p = NULL; + + return 1; +} + +static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { + int r; + + assert(fd >= 0); + assert(mh); + + for (;;) { + if (sendmsg(fd, mh, flags) >= 0) + return 0; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -errno; + + r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; + } +} + +static int write_loop(int fd, void *message, size_t length) { + int r; + + assert(fd >= 0); + assert(message); + + for (;;) { + if (write(fd, message, length) >= 0) + return 0; + + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -errno; + + r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; + } +} + +int manager_write(Manager *m, int fd, DnsPacket *p) { + int r; + + 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) + return r; + + return 0; +} + +static int manager_ipv4_send( + Manager *m, + int fd, + int ifindex, + const struct in_addr *destination, + uint16_t port, + const struct in_addr *source, + DnsPacket *p) { + union sockaddr_union sa = { + .in.sin_family = AF_INET, + }; + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))]; + } control; + struct msghdr mh = {}; + struct iovec iov; + + assert(m); + assert(fd >= 0); + assert(destination); + assert(port > 0); + assert(p); + + iov.iov_base = DNS_PACKET_DATA(p); + iov.iov_len = p->size; + + sa.in.sin_addr = *destination; + sa.in.sin_port = htobe16(port), + + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_name = &sa.sa; + mh.msg_namelen = sizeof(sa.in); + + if (ifindex > 0) { + struct cmsghdr *cmsg; + struct in_pktinfo *pi; + + zero(control); + + mh.msg_control = &control; + mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_len = mh.msg_controllen; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + + pi = (struct in_pktinfo*) CMSG_DATA(cmsg); + pi->ipi_ifindex = ifindex; + + if (source) + pi->ipi_spec_dst = *source; + } + + return sendmsg_loop(fd, &mh, 0); +} + +static int manager_ipv6_send( + Manager *m, + int fd, + int ifindex, + const struct in6_addr *destination, + uint16_t port, + const struct in6_addr *source, + DnsPacket *p) { + + union sockaddr_union sa = { + .in6.sin6_family = AF_INET6, + }; + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } control; + struct msghdr mh = {}; + struct iovec iov; + + assert(m); + assert(fd >= 0); + assert(destination); + assert(port > 0); + assert(p); + + iov.iov_base = DNS_PACKET_DATA(p); + iov.iov_len = p->size; + + sa.in6.sin6_addr = *destination; + sa.in6.sin6_port = htobe16(port), + sa.in6.sin6_scope_id = ifindex; + + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_name = &sa.sa; + mh.msg_namelen = sizeof(sa.in6); + + if (ifindex > 0) { + struct cmsghdr *cmsg; + struct in6_pktinfo *pi; + + zero(control); + + mh.msg_control = &control; + mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_len = mh.msg_controllen; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + + pi = (struct in6_pktinfo*) CMSG_DATA(cmsg); + pi->ipi6_ifindex = ifindex; + + if (source) + pi->ipi6_addr = *source; + } + + return sendmsg_loop(fd, &mh, 0); +} + +int manager_send( + Manager *m, + int fd, + int ifindex, + int family, + const union in_addr_union *destination, + uint16_t port, + const union in_addr_union *source, + DnsPacket *p) { + + assert(m); + assert(fd >= 0); + assert(destination); + assert(port > 0); + assert(p); + + 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, &destination->in, port, &source->in, p); + if (family == AF_INET6) + return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p); + + return -EAFNOSUPPORT; +} + +uint32_t manager_find_mtu(Manager *m) { + uint32_t mtu = 0; + Link *l; + Iterator i; + + /* If we don't know on which link a DNS packet would be + * delivered, let's find the largest MTU that works on all + * interfaces we know of */ + + HASHMAP_FOREACH(l, m->links, i) { + if (l->mtu <= 0) + continue; + + if (mtu <= 0 || l->mtu < mtu) + mtu = l->mtu; + } + + return mtu; +} + +int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) { + LinkAddress *a; + + assert(m); + + a = manager_find_link_address(m, family, in_addr); + if (a) + return a->link->ifindex; + + return 0; +} + +void manager_refresh_rrs(Manager *m) { + Iterator i; + Link *l; + + assert(m); + + m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); + m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); + + HASHMAP_FOREACH(l, m->links, i) { + link_add_rrs(l, true); + link_add_rrs(l, false); + } +} + +int manager_next_hostname(Manager *m) { + const char *p; + uint64_t u, a; + char *h, *k; + int r; + + assert(m); + + p = strchr(m->llmnr_hostname, 0); + assert(p); + + while (p > m->llmnr_hostname) { + if (!strchr("0123456789", p[-1])) + break; + + p--; + } + + if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0) + u = 1; + + /* Add a random number to the old value. This way we can avoid + * that two hosts pick the same hostname, win on IPv4 and lose + * on IPv6 (or vice versa), and pick the same hostname + * replacement hostname, ad infinitum. We still want the + * numbers to go up monotonically, hence we just add a random + * value 1..10 */ + + random_bytes(&a, sizeof(a)); + u += 1 + a % 10; + + if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0) + return -ENOMEM; + + r = dns_name_concat(h, "local", &k); + if (r < 0) { + free(h); + return r; + } + + log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h); + + free(m->llmnr_hostname); + m->llmnr_hostname = h; + + free(m->mdns_hostname); + m->mdns_hostname = k; + + manager_refresh_rrs(m); + + return 0; +} + +LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr) { + Iterator i; + Link *l; + + assert(m); + + HASHMAP_FOREACH(l, m->links, i) { + LinkAddress *a; + + a = link_find_address(l, family, in_addr); + if (a) + return a; + } + + return NULL; +} + +bool manager_our_packet(Manager *m, DnsPacket *p) { + assert(m); + assert(p); + + return !!manager_find_link_address(m, p->family, &p->sender); +} + +DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { + Link *l; + + assert(m); + assert(p); + + l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); + if (!l) + return NULL; + + 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; +} + +void manager_verify_all(Manager *m) { + DnsScope *s; + + assert(m); + + LIST_FOREACH(scopes, s, m->dns_scopes) + dns_zone_verify_all(&s->zone); +} + +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); + + r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops); + if (r < 0) + return r; + + /* 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; + } + + /* 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; + } + } + + /* 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; +} + +/* filter_route is a tri-state: + * < 0: no filtering + * = 0 or false: return only domains which should be used for searching + * > 0 or true: return only domains which are for routing only + */ +int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) { + DnsSearchDomain *d; + Iterator i; + Link *l; + int r; + + assert(m); + assert(domains); + + r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, m->search_domains) { + + if (filter_route >= 0 && + d->route_only != !!filter_route) + continue; + + r = ordered_set_put(*domains, d->name); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } + + HASHMAP_FOREACH(l, m->links, i) { + + LIST_FOREACH(domains, d, l->search_domains) { + + if (filter_route >= 0 && + d->route_only != !!filter_route) + continue; + + r = ordered_set_put(*domains, d->name); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } + } + + return 0; +} + +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; +} + +void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) { + + assert(verdict >= 0); + assert(verdict < _DNSSEC_VERDICT_MAX); + + if (log_get_max_level() >= LOG_DEBUG) { + char s[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Found verdict for lookup %s: %s", + dns_resource_key_to_string(key, s, sizeof s), + dnssec_verdict_to_string(verdict)); + } + + m->n_dnssec_verdict[verdict]++; +} + +bool manager_routable(Manager *m, int family) { + Iterator i; + Link *l; + + assert(m); + + /* Returns true if the host has at least one interface with a routable address of the specified type */ + + HASHMAP_FOREACH(l, m->links, i) + if (link_relevant(l, family, false)) + return true; + + return false; +} + +void manager_flush_caches(Manager *m) { + DnsScope *scope; + + assert(m); + + LIST_FOREACH(scopes, scope, m->dns_scopes) + dns_cache_flush(&scope->cache); + + log_info("Flushed all caches."); +} + +void manager_cleanup_saved_user(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r; + + assert(m); + + /* Clean up all saved per-link files in /run/systemd/resolve/netif/ that don't have a matching interface + * anymore. These files are created to persist settings pushed in by the user via the bus, so that resolved can + * be restarted without losing this data. */ + + d = opendir("/run/systemd/resolve/netif/"); + if (!d) { + if (errno == ENOENT) + return; + + log_warning_errno(errno, "Failed to open interface directory: %m"); + return; + } + + FOREACH_DIRENT_ALL(de, d, log_error_errno(errno, "Failed to read interface directory: %m")) { + _cleanup_free_ char *p = NULL; + int ifindex; + Link *l; + + if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG)) + continue; + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + r = parse_ifindex(de->d_name, &ifindex); + if (r < 0) /* Probably some temporary file from a previous run. Delete it */ + goto rm; + + l = hashmap_get(m->links, INT_TO_PTR(ifindex)); + if (!l) /* link vanished */ + goto rm; + + if (l->is_managed) /* now managed by networkd, hence the bus settings are useless */ + goto rm; + + continue; + + rm: + p = strappend("/run/systemd/resolve/netif/", de->d_name); + if (!p) { + log_oom(); + return; + } + + (void) unlink(p); + } +} diff --git a/src/grp-resolve/systemd-resolved/resolved-manager.h b/src/grp-resolve/systemd-resolved/resolved-manager.h new file mode 100644 index 0000000000..6c5274ce56 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-manager.h @@ -0,0 +1,183 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/ordered-set.h" +#include "sd-netlink/sd-netlink.h" +#include "sd-network/sd-network.h" +#include "shared/resolve-util.h" + +typedef struct Manager Manager; + +#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; + + ResolveSupport llmnr_support; + ResolveSupport mdns_support; + DnssecMode dnssec_mode; + bool enable_cache; + + /* Network */ + Hashmap *links; + + sd_netlink *rtnl; + sd_event_source *rtnl_event_source; + + sd_network_monitor *network_monitor; + sd_event_source *network_event_source; + + /* DNS query management */ + Hashmap *dns_transactions; + LIST_HEAD(DnsQuery, dns_queries); + unsigned n_dns_queries; + + LIST_HEAD(DnsStream, dns_streams); + unsigned n_dns_streams; + + /* 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; + + 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; + + /* LLMNR */ + int llmnr_ipv4_udp_fd; + int llmnr_ipv6_udp_fd; + int llmnr_ipv4_tcp_fd; + int llmnr_ipv6_tcp_fd; + + sd_event_source *llmnr_ipv4_udp_event_source; + sd_event_source *llmnr_ipv6_udp_event_source; + 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; + + /* The hostname we publish on LLMNR and mDNS */ + char *llmnr_hostname; + char *mdns_hostname; + DnsResourceKey *llmnr_host_ipv4_key; + DnsResourceKey *llmnr_host_ipv6_key; + + /* Watch the system hostname */ + int hostname_fd; + sd_event_source *hostname_event_source; + + /* Watch for system suspends */ + sd_bus_slot *prepare_for_sleep_slot; + + sd_event_source *sigusr1_event_source; + sd_event_source *sigusr2_event_source; + + unsigned n_transactions_total; + unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX]; + + /* Data from /etc/hosts */ + Set* etc_hosts_by_address; + Hashmap* etc_hosts_by_name; + usec_t etc_hosts_last, etc_hosts_mtime; + + /* Local DNS stub on 127.0.0.53:53 */ + int dns_stub_udp_fd; + int dns_stub_tcp_fd; + + sd_event_source *dns_stub_udp_event_source; + sd_event_source *dns_stub_tcp_event_source; +}; + +/* Manager */ + +int manager_new(Manager **ret); +Manager* manager_free(Manager *m); + +int manager_start(Manager *m); + +uint32_t manager_find_mtu(Manager *m); + +int manager_write(Manager *m, int fd, DnsPacket *p); +int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p); +int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); + +int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr); +LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr); + +void manager_refresh_rrs(Manager *m); +int manager_next_hostname(Manager *m); + +bool manager_our_packet(Manager *m, DnsPacket *p); +DnsScope* manager_find_scope(Manager *m, DnsPacket *p); + +void manager_verify_all(Manager *m); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); + +#define EXTRA_CMSG_SPACE 1024 + +int manager_is_own_hostname(Manager *m, const char *name); + +int manager_compile_dns_servers(Manager *m, OrderedSet **servers); +int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route); + +DnssecMode manager_get_dnssec_mode(Manager *m); +bool manager_dnssec_supported(Manager *m); + +void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key); + +bool manager_routable(Manager *m, int family); + +void manager_flush_caches(Manager *m); + +void manager_cleanup_saved_user(Manager *m); diff --git a/src/grp-resolve/systemd-resolved/resolved-mdns.c b/src/grp-resolve/systemd-resolved/resolved-mdns.c new file mode 100644 index 0000000000..0c78f7bda5 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-mdns.c @@ -0,0 +1,288 @@ +/*** + This file is part of systemd. + + Copyright 2015 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include +#include +#include + +#include "basic/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/grp-resolve/systemd-resolved/resolved-mdns.h b/src/grp-resolve/systemd-resolved/resolved-mdns.h new file mode 100644 index 0000000000..5d274648f4 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-mdns.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#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/grp-resolve/systemd-resolved/resolved-resolv-conf.c b/src/grp-resolve/systemd-resolved/resolved-resolv-conf.c new file mode 100644 index 0000000000..5d6b4203bc --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-resolv-conf.c @@ -0,0 +1,267 @@ +/*** + 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 . + ***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/fileio.h" +#include "basic/ordered-set.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/dns-domain.h" + +#include "resolved-conf.h" +#include "resolved-resolv-conf.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_parse_dns_server_string_and_warn(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); + + if (!dns_server_string(s)) { + 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", dns_server_string(s)); +} + +static void write_resolv_conf_search( + OrderedSet *domains, + FILE *f) { + unsigned length = 0, count = 0; + Iterator i; + char *domain; + + assert(domains); + assert(f); + + fputs("search", f); + + ORDERED_SET_FOREACH(domain, domains, i) { + if (++count > MAXDNSRCH) { + fputs("\n# Too many search domains configured, remaining ones ignored.", f); + break; + } + length += strlen(domain) + 1; + if (length > 256) { + fputs("\n# Total length of all search domains is too long, remaining ones ignored.", f); + break; + } + fputc(' ', f); + fputs(domain, f); + } + + fputs("\n", 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" + "# This is a dynamic resolv.conf file for connecting local clients directly to\n" + "# all known DNS servers.\n#\n" + "# Third party programs must not access this file directly, but only through the\n" + "# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n" + "# replace this symlink by a static file or a different symlink.\n#\n" + "# See systemd-resolved.service(8) for details about the supported modes of\n" + "# operation for /etc/resolv.conf.\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)) + write_resolv_conf_search(domains, f); + + return fflush_and_check(f); +} + +int manager_write_resolv_conf(Manager *m) { + + _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 */ + (void) 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 log_warning_errno(r, "Failed to compile list of DNS servers: %m"); + + r = manager_compile_search_domains(m, &domains, false); + if (r < 0) + return log_warning_errno(r, "Failed to compile list of search domains: %m"); + + r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path); + if (r < 0) + return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m"); + + (void) fchmod(fileno(f), 0644); + + r = write_resolv_conf_contents(f, dns, domains); + if (r < 0) { + log_error_errno(r, "Failed to write private resolv.conf contents: %m"); + goto fail; + } + + if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) { + r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m"); + goto fail; + } + + return 0; + +fail: + (void) unlink(PRIVATE_RESOLV_CONF); + (void) unlink(temp_path); + + return r; +} diff --git a/src/grp-resolve/systemd-resolved/resolved-resolv-conf.h b/src/grp-resolve/systemd-resolved/resolved-resolv-conf.h new file mode 100644 index 0000000000..75fa080e4c --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved-resolv-conf.h @@ -0,0 +1,27 @@ +#pragma once + +/*** + 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 . +***/ + +#include "resolved-manager.h" + +#define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf" + +int manager_read_resolv_conf(Manager *m); +int manager_write_resolv_conf(Manager *m); diff --git a/src/grp-resolve/systemd-resolved/resolved.c b/src/grp-resolve/systemd-resolved/resolved.c new file mode 100644 index 0000000000..39fa423eeb --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved.c @@ -0,0 +1,121 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/capability-util.h" +#include "basic/mkdir.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/user-util.h" + +#include "resolved-conf.h" +#include "resolved-manager.h" +#include "resolved-resolv-conf.h" + +int main(int argc, char *argv[]) { + _cleanup_(manager_freep) Manager *m = NULL; + const char *user = "systemd-resolve"; + uid_t uid; + gid_t gid; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + umask(0022); + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "SELinux setup failed: %m"); + goto finish; + } + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Cannot resolve user name %s: %m", user); + goto finish; + } + + /* Always create the directory where resolv.conf will live */ + r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid); + if (r < 0) { + log_error_errno(r, "Could not create runtime directory: %m"); + goto finish; + } + + /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ + r = drop_privileges(uid, gid, + (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */ + (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ + (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); + if (r < 0) + goto finish; + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, -1) >= 0); + + r = manager_new(&m); + if (r < 0) { + log_error_errno(r, "Could not create manager: %m"); + goto finish; + } + + r = manager_start(m); + if (r < 0) { + log_error_errno(r, "Failed to start manager: %m"); + goto finish; + } + + /* Write finish default resolv.conf to avoid a dangling symlink */ + (void) manager_write_resolv_conf(m); + + /* Let's drop the remaining caps now */ + r = capability_bounding_set_drop(0, true); + if (r < 0) { + log_error_errno(r, "Failed to drop remaining caps: %m"); + goto finish; + } + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + r = sd_event_loop(m->event); + if (r < 0) { + log_error_errno(r, "Event loop failed: %m"); + goto finish; + } + + sd_event_get_exit_code(m->event, &r); + +finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-resolve/systemd-resolved/resolved.conf.in b/src/grp-resolve/systemd-resolved/resolved.conf.in new file mode 100644 index 0000000000..3bd8389c88 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved.conf.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See resolved.conf(5) for details + +[Resolve] +#DNS= +#FallbackDNS=@DNS_SERVERS@ +#Domains= +#LLMNR=yes +#DNSSEC=@DEFAULT_DNSSEC_MODE@ +#Cache=yes diff --git a/src/grp-resolve/systemd-resolved/resolved.conf.xml b/src/grp-resolve/systemd-resolved/resolved.conf.xml new file mode 100644 index 0000000000..7556c6ff31 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/resolved.conf.xml @@ -0,0 +1,230 @@ + + + + + + + + resolved.conf + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + resolved.conf + 5 + + + + resolved.conf + resolved.conf.d + Network Name Resolution configuration files + + + + /etc/systemd/resolved.conf + /etc/systemd/resolved.conf.d/*.conf + /run/systemd/resolved.conf.d/*.conf + /usr/lib/systemd/resolved.conf.d/*.conf + + + + Description + + These configuration files control local DNS and LLMNR + name resolution. + + + + + + + Options + + The following options are available in the [Resolve] section: + + + + + DNS= + A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests + are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from + systemd-networkd.service8 or + set at runtime by external applications. For compatibility reasons, if this setting is not specified, the DNS + servers listed in /etc/resolv.conf are used instead, if that file exists and any servers + are configured in it. This setting defaults to the empty list. + + + + FallbackDNS= + A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any + per-link DNS servers obtained from + systemd-networkd.service8 + take precedence over this setting, as do any servers set via DNS= above or + /etc/resolv.conf. This setting is hence only used if no other DNS server information is + known. If this option is not given, a compiled-in list of DNS servers is used instead. + + + + Domains= + A space-separated list of domains. These domains are used as search suffixes when resolving + single-label host names (domain names which contain no dot), in order to qualify them into fully-qualified + domain names (FQDNs). Search domains are strictly processed in the order they are specified, until the name + with the suffix appended is found. For compatibility reasons, if this setting is not specified, the search + domains listed in /etc/resolv.conf are used instead, if that file exists and any domains + are configured in it. This setting defaults to the empty list. + + Specified domain names may optionally be prefixed with ~. In this case they do not + define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured + with the system DNS= setting (see above), in case additional, suitable per-link DNS servers + are known. If no per-link DNS servers are known using the ~ syntax has no effect. Use the + construct ~. (which is composed of ~ to indicate a routing domain and + . to indicate the DNS root domain that is the implied suffix of all DNS domains) to use the + system DNS server defined with DNS= preferably for all domains. + + + + LLMNR= + Takes a boolean argument or + resolve. Controls Link-Local Multicast Name + Resolution support (RFC 4794) on + the local host. If true, enables full LLMNR responder and + resolver support. If false, disables both. If set to + resolve, only resolution support is enabled, + but responding is disabled. Note that + systemd-networkd.service8 + also maintains per-link LLMNR settings. LLMNR will be + enabled on a link only if the per-link and the + global setting is on. + + + + DNSSEC= + Takes a boolean argument or + allow-downgrade. If true all DNS lookups are + DNSSEC-validated locally (excluding LLMNR and Multicast + DNS). If the response to a lookup request is detected to be invalid + a lookup failure is returned to applications. Note that + this mode requires a DNS server that supports DNSSEC. If the + DNS server does not properly support DNSSEC all validations + will fail. If set to allow-downgrade DNSSEC + validation is attempted, but if the server does not support + DNSSEC properly, DNSSEC mode is automatically disabled. Note + that this mode makes DNSSEC validation vulnerable to + "downgrade" attacks, where an attacker might be able to + trigger a downgrade to non-DNSSEC mode by synthesizing a DNS + response that suggests DNSSEC was not supported. If set to + false, DNS lookups are not DNSSEC validated. + + Note that DNSSEC validation requires retrieval of + additional DNS data, and thus results in a small DNS look-up + time penalty. + + DNSSEC requires knowledge of "trust anchors" to prove + data integrity. The trust anchor for the Internet root domain + is built into the resolver, additional trust anchors may be + defined with + dnssec-trust-anchors.d5. + Trust anchors may change at regular intervals, and old trust + anchors may be revoked. In such a case DNSSEC validation is + not possible until new trust anchors are configured locally or + the resolver software package is updated with the new root + trust anchor. In effect, when the built-in trust anchor is + revoked and DNSSEC= is true, all further + lookups will fail, as it cannot be proved anymore whether + lookups are correctly signed, or validly unsigned. If + DNSSEC= is set to + allow-downgrade the resolver will + automatically turn off DNSSEC validation in such a case. + + Client programs looking up DNS data will be informed + whether lookups could be verified using DNSSEC, or whether the + returned data could not be verified (either because the data + was found unsigned in the DNS, or the DNS server did not + support DNSSEC or no appropriate trust anchors were known). In + the latter case it is assumed that client programs employ a + secondary scheme to validate the returned DNS data, should + this be required. + + It is recommended to set DNSSEC= to + true on systems where it is known that the DNS server supports + DNSSEC correctly, and where software or trust anchor updates + happen regularly. On other systems it is recommended to set + DNSSEC= to + allow-downgrade. + + In addition to this global DNSSEC setting + systemd-networkd.service8 + also maintains per-link DNSSEC settings. For system DNS + servers (see above), only the global DNSSEC setting is in + effect. For per-link DNS servers the per-link + setting is in effect, unless it is unset in which case the + global setting is used instead. + + Site-private DNS zones generally conflict with DNSSEC + operation, unless a negative (if the private zone is not + signed) or positive (if the private zone is signed) trust + anchor is configured for them. If + allow-downgrade mode is selected, it is + attempted to detect site-private DNS zones using top-level + domains (TLDs) that are not known by the DNS root server. This + logic does not work in all private zone setups. + + Defaults to off. + + + + + Cache= + Takes a boolean argument. If "yes" (the default), resolving a domain name which already got + queried earlier will return the previous result as long as it is still valid, and thus does not result in a new + network request. Be aware that that turning off caching comes at a performance penalty, which is particularly + high when DNSSEC is used. + + Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address + (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching. + + + + + + + See Also + + systemd1, + systemd-resolved.service8, + systemd-networkd.service8, + dnssec-trust-anchors.d5, + resolv.conf4 + + + + diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in b/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in new file mode 100644 index 0000000000..15ab56a066 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.service.m4.in @@ -0,0 +1,34 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Name Resolution +Documentation=man:systemd-resolved.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/resolved +Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers +Documentation=http://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients +After=systemd-networkd.service network.target + +# On kdbus systems we pull in the busname explicitly, because it +# carries policy that allows the daemon to acquire its name. +Wants=org.freedesktop.resolve1.busname +After=org.freedesktop.resolve1.busname + +[Service] +Type=notify +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-resolved +CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_NET_RAW CAP_NET_BIND_SERVICE +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io + +[Install] +WantedBy=multi-user.target diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.service.xml b/src/grp-resolve/systemd-resolved/systemd-resolved.service.xml new file mode 100644 index 0000000000..aa1c2365e5 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.service.xml @@ -0,0 +1,234 @@ + + + + + + + + + systemd-resolved.service + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd-resolved.service + 8 + + + + systemd-resolved.service + systemd-resolved + Network Name Resolution manager + + + + systemd-resolved.service + /usr/lib/systemd/systemd-resolved + + + + Description + + systemd-resolved is a system service that provides network name resolution to local + applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR resolver and + responder. Local applications may submit network name resolution requests via three interfaces: + + + The native, fully-featured API systemd-resolved exposes on the bus. See the + API Documentation for + details. Usage of this API is generally recommended to clients as it is asynchronous and fully featured (for + example, properly returns DNSSEC validation status and interface scope for addresses as necessary for supporting + link-local networking). + + The glibc + getaddrinfo3 API as defined + by RFC3493 and its related resolver functions, + including gethostbyname3. This + API is widely supported, including beyond the Linux platform. In its current form it does not expose DNSSEC + validation status information however, and is synchronous only. This API is backed by the glibc Name Service + Switch (nss5). Usage of the + glibc NSS module nss-resolve8 + is required in order to allow glibc's NSS resolver functions to resolve host names via + systemd-resolved. + + Additionally, systemd-resolved provides a local DNS stub listener on IP + address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly, bypassing any local + API may be directed to this stub, in order to connect them to systemd-resolved. Note however + that it is strongly recommended that local programs use the glibc NSS or bus APIs instead (as described above), + as various network resolution concepts (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped + to the unicast DNS protocol. + + + The DNS servers contacted are determined from the global settings in + /etc/systemd/resolved.conf, the per-link static settings in + /etc/systemd/network/*.network files, the per-link dynamic settings received over DHCP and any + DNS server information made available by other system services. See + resolved.conf5 and + systemd.network5 for details + about systemd's own configuration files for DNS servers. To improve compatibility, + /etc/resolv.conf is read in order to discover configured system DNS servers, but only if it is + not a symlink to /run/systemd/resolve/resolv.conf (see below). + + systemd-resolved synthesizes DNS resource records (RRs) for the following cases: + + + The local, configured hostname is resolved to + all locally configured IP addresses ordered by their scope, or + — if none are configured — the IPv4 address 127.0.0.2 (which + is on the local loopback) and the IPv6 address ::1 (which is the + local host). + + The hostnames localhost and + localhost.localdomain (as well as any hostname + ending in .localhost or .localhost.localdomain) + are resolved to the IP addresses 127.0.0.1 and ::1. + + The hostname gateway is + resolved to all current default routing gateway addresses, + ordered by their metric. This assigns a stable hostname to the + current gateway, useful for referencing it independently of the + current network configuration state. + + The mappings defined in /etc/hosts are resolved to their configured + addresses and back. + + + Lookup requests are routed to the available DNS servers + and LLMNR interfaces according to the following rules: + + + Lookups for the special hostname + localhost are never routed to the + network. (A few other, special domains are handled the same way.) + + Single-label names are routed to all local + interfaces capable of IP multicasting, using the LLMNR + protocol. Lookups for IPv4 addresses are only sent via LLMNR on + IPv4, and lookups for IPv6 addresses are only sent via LLMNR on + IPv6. Lookups for the locally configured host name and the + gateway host name are never routed to + LLMNR. + + Multi-label names are routed to all local + interfaces that have a DNS sever configured, plus the globally + configured DNS server if there is one. Address lookups from the + link-local address range are never routed to + DNS. + + + If lookups are routed to multiple interfaces, the first + successful response is returned (thus effectively merging the + lookup zones on all matching interfaces). If the lookup failed on + all interfaces, the last failing response is returned. + + Routing of lookups may be influenced by configuring + per-interface domain names. See + systemd.network5 + for details. Lookups for a hostname ending in one of the + per-interface domains are exclusively routed to the matching + interfaces. + + See the resolved D-Bus API + Documentation for information about the APIs systemd-resolved provides. + + + + + <filename>/etc/resolv.conf</filename> + + Three modes of handling /etc/resolv.conf (see + resolv.conf5) are + supported: + + + A static file /usr/lib/systemd/resolv.conf is provided that lists + the 127.0.0.53 DNS stub (see above) as only DNS server. This file may be symlinked from + /etc/resolv.conf in order to connect all local clients that bypass local DNS APIs to + systemd-resolved. This mode of operation is recommended. + + systemd-resolved maintains the + /run/systemd/resolve/resolv.conf file for compatibility with traditional Linux + programs. This file may be symlinked from /etc/resolv.conf and is always kept up-to-date, + containing information about all known DNS servers. Note the file format's limitations: it does not know a + concept of per-interface DNS servers and hence only contains system-wide DNS server definitions. Note that + /run/systemd/resolve/resolv.conf should not be used directly by applications, but only + through a symlink from /etc/resolv.conf. If this mode of operation is used local clients + that bypass any local DNS API will also bypass systemd-resolved and will talk directly to the + known DNS servers. + + Alternatively, /etc/resolv.conf may be managed by other packages, in which + case systemd-resolved will read it for DNS configuration data. In this mode of operation + systemd-resolved is consumer rather than provider of this configuration + file. + + + Note that the selected mode of operation for this file is detected fully automatically, depending on whether + /etc/resolv.conf is a symlink to /run/systemd/resolve/resolv.conf or + lists 127.0.0.53 as DNS server. + + + + Signals + + + + SIGUSR1 + + Upon reception of the SIGUSR1 process signal systemd-resolved will dump the + contents of all DNS resource record caches it maintains into the system logs. + + + + SIGUSR2 + + Upon reception of the SIGUSR2 process signal systemd-resolved will flush all + caches it maintains. Note that it should normally not be necessary to request this explicitly – except for + debugging purposes – as systemd-resolved flushes the caches automatically anyway any time + the host's network configuration changes. + + + + + + See Also + + systemd1, + resolved.conf5, + dnssec-trust-anchors.d5, + nss-resolve8, + systemd-resolve1, + resolv.conf5, + hosts5, + systemd.network5, + systemd-networkd.service8 + + + + diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.sysusers b/src/grp-resolve/systemd-resolved/systemd-resolved.sysusers new file mode 100644 index 0000000000..5872bf2db7 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-resolve - "systemd Resolver" diff --git a/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles b/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles new file mode 100644 index 0000000000..760fe11412 --- /dev/null +++ b/src/grp-resolve/systemd-resolved/systemd-resolved.tmpfiles @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +L! /etc/resolv.conf - - - - ../usr/lib/systemd/resolv.conf diff --git a/src/grp-system/90-systemd.preset b/src/grp-system/90-systemd.preset new file mode 100644 index 0000000000..138937011c --- /dev/null +++ b/src/grp-system/90-systemd.preset @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# These ones should be enabled by default, even if distributions +# generally follow a default-off policy. + +enable remote-fs.target +enable machines.target + +enable getty@.service + +disable console-getty.service +disable console-shell.service +disable debug-shell.service + +disable halt.target +disable kexec.target +disable poweroff.target +disable reboot.target +disable rescue.target diff --git a/src/grp-system/Makefile b/src/grp-system/Makefile new file mode 100644 index 0000000000..482fbb5670 --- /dev/null +++ b/src/grp-system/Makefile @@ -0,0 +1,32 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += grp-utils +nested.subdirs += libcore +nested.subdirs += systemctl +nested.subdirs += systemd +nested.subdirs += systemd-shutdown + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/bootup.xml b/src/grp-system/bootup.xml new file mode 100644 index 0000000000..986996398c --- /dev/null +++ b/src/grp-system/bootup.xml @@ -0,0 +1,305 @@ + + + + + + + + + bootup + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + bootup + 7 + + + + bootup + System bootup process + + + + Description + + A number of different components are involved in the system + boot. Immediately after power-up, the system BIOS will do minimal + hardware initialization, and hand control over to a boot loader + stored on a persistent storage device. This boot loader will then + invoke an OS kernel from disk (or the network). In the Linux case, + this kernel (optionally) extracts and executes an initial RAM disk + image (initrd), such as generated by + dracut8, + which looks for the root file system (possibly using + systemd1 + for this). After the root file system is found and mounted, the + initrd hands over control to the host's system manager (such as + systemd1) + stored on the OS image, which is then responsible for probing all + remaining hardware, mounting all necessary file systems and + spawning all configured services. + + On shutdown, the system manager stops all services, unmounts + all file systems (detaching the storage technologies backing + them), and then (optionally) jumps back into the initrd code which + unmounts/detaches the root file system and the storage it resides + on. As a last step, the system is powered down. + + Additional information about the system boot process may be + found in + boot7. + + + + System Manager Bootup + + At boot, the system manager on the OS image is responsible + for initializing the required file systems, services and drivers + that are necessary for operation of the system. On + systemd1 + systems, this process is split up in various discrete steps which + are exposed as target units. (See + systemd.target5 + for detailed information about target units.) The boot-up process + is highly parallelized so that the order in which specific target + units are reached is not deterministic, but still adheres to a + limited amount of ordering structure. + + When systemd starts up the system, it will activate all + units that are dependencies of default.target + (as well as recursively all dependencies of these dependencies). + Usually, default.target is simply an alias of + graphical.target or + multi-user.target, depending on whether the + system is configured for a graphical UI or only for a text + console. To enforce minimal ordering between the units pulled in, + a number of well-known target units are available, as listed on + systemd.special7. + + The following chart is a structural overview of these + well-known units and their position in the boot-up logic. The + arrows describe which units are pulled in and ordered before which + other units. Units near the top are started before units nearer to + the bottom of the chart. + +local-fs-pre.target + | + v +(various mounts and (various swap (various cryptsetup + fsck services...) devices...) devices...) (various low-level (various low-level + | | | services: udevd, API VFS mounts: + v v v tmpfiles, random mqueue, configfs, + local-fs.target swap.target cryptsetup.target seed, sysctl, ...) debugfs, ...) + | | | | | + \__________________|_________________ | ___________________|____________________/ + \|/ + v + sysinit.target + | + ____________________________________/|\________________________________________ + / | | | \ + | | | | | + v v | v v + (various (various | (various rescue.service + timers...) paths...) | sockets...) | + | | | | v + v v | v rescue.target + timers.target paths.target | sockets.target + | | | | + v \_________________ | ___________________/ + \|/ + v + basic.target + | + ____________________________________/| emergency.service + / | | | + | | | v + v v v emergency.target + display- (various system (various system + manager.service services services) + | required for | + | graphical UIs) v + | | multi-user.target + | | | + \_________________ | _________________/ + \|/ + v + graphical.target + + Target units that are commonly used as boot targets are + emphasized. These units are good choices as + goal targets, for example by passing them to the + systemd.unit= kernel command line option (see + systemd1) + or by symlinking default.target to them. + + + timers.target is pulled-in by + basic.target asynchronously. This allows + timers units to depend on services which become only available + later in boot. + + + + Bootup in the Initial RAM Disk (initrd) + The initial RAM disk implementation (initrd) can be set up + using systemd as well. In this case, boot up inside the initrd + follows the following structure. + + The default target in the initrd is + initrd.target. The bootup process begins + identical to the system manager bootup (see above) until it + reaches basic.target. From there, systemd + approaches the special target initrd.target. + When the root device becomes available, + initd-root-device.target is reached. + If the root device can be mounted at + /sysroot, the + sysroot.mount unit becomes active and + initrd-root-fs.target is reached. The service + initrd-parse-etc.service scans + /sysroot/etc/fstab for a possible + /usr mount point and additional entries + marked with the x-initrd.mount option. All + entries found are mounted below /sysroot, and + initrd-fs.target is reached. The service + initrd-cleanup.service isolates to the + initrd-switch-root.target, where cleanup + services can run. As the very last step, the + initrd-switch-root.service is activated, + which will cause the system to switch its root to + /sysroot. + + + : (beginning identical to above) + : + v + basic.target + | emergency.service + ______________________/| | + / | v + | initrd-root-device.target emergency.target + | | + | v + | sysroot.mount + | | + | v + | initrd-root-fs.target + | | + | v + v initrd-parse-etc.service + (custom initrd | + services...) v + | (sysroot-usr.mount and + | various mounts marked + | with fstab option + | x-initrd.mount...) + | | + | v + | initrd-fs.target + \______________________ | + \| + v + initrd.target + | + v + initrd-cleanup.service + isolates to + initrd-switch-root.target + | + v + ______________________/| + / v + | initrd-udevadm-cleanup-db.service + v | + (custom initrd | + services...) | + \______________________ | + \| + v + initrd-switch-root.target + | + v + initrd-switch-root.service + | + v + Transition to Host OS + + + + System Manager Shutdown + + System shutdown with systemd also consists of various target + units with some minimal ordering structure applied: + + (conflicts with (conflicts with + all system all file system + services) mounts, swaps, + | cryptsetup + | devices, ...) + | | + v v + shutdown.target umount.target + | | + \_______ ______/ + \ / + v + (various low-level + services) + | + v + final.target + | + _____________________________________/ \_________________________________ + / | | \ + | | | | + v v v v +systemd-reboot.service systemd-poweroff.service systemd-halt.service systemd-kexec.service + | | | | + v v v v + reboot.target poweroff.target halt.target kexec.target + + Commonly used system shutdown targets are + emphasized. + + + + See Also + + systemd1, + boot7, + systemd.special7, + systemd.target5, + dracut8 + + + + diff --git a/src/grp-system/grp-utils/Makefile b/src/grp-system/grp-utils/Makefile new file mode 100644 index 0000000000..2c8cc75a7c --- /dev/null +++ b/src/grp-system/grp-utils/Makefile @@ -0,0 +1,32 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-analyze +nested.subdirs += systemd-delta +nested.subdirs += systemd-fstab-generator +nested.subdirs += systemd-run +nested.subdirs += systemd-sysv-generator + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-analyze/.gitignore b/src/grp-system/grp-utils/systemd-analyze/.gitignore new file mode 100644 index 0000000000..752ea236c8 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/.gitignore @@ -0,0 +1 @@ +/systemd-analyze diff --git a/src/grp-system/grp-utils/systemd-analyze/Makefile b/src/grp-system/grp-utils/systemd-analyze/Makefile new file mode 100644 index 0000000000..cb1b72e77d --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/Makefile @@ -0,0 +1,39 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-analyze +systemd_analyze_SOURCES = \ + src/analyze/analyze.c \ + src/analyze/analyze-verify.c \ + src/analyze/analyze-verify.h + +systemd_analyze_CFLAGS = \ + $(SECCOMP_CFLAGS) \ + $(MOUNT_CFLAGS) + +systemd_analyze_LDADD = \ + libcore.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-analyze/analyze-verify.c b/src/grp-system/grp-utils/systemd-analyze/analyze-verify.c new file mode 100644 index 0000000000..e27ae4f5c5 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/analyze-verify.c @@ -0,0 +1,305 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "manager.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/pager.h" + +#include "analyze-verify.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; + + _cleanup_strv_free_ char **ans = NULL; + int r; + + STRV_FOREACH(filename, filenames) { + char *t; + + t = dirname_malloc(*filename); + if (!t) + return -ENOMEM; + + r = strv_consume(&ans, t); + if (r < 0) + return r; + } + + assert_se(strv_uniq(ans)); + + r = strv_extend(&ans, ""); + if (r < 0) + return r; + + *var = strv_join(ans, ":"); + if (!*var) + return -ENOMEM; + + return 0; +} + +static int verify_socket(Unit *u) { + int r; + + assert(u); + + if (u->type != UNIT_SOCKET) + return 0; + + /* Cannot run this without the service being around */ + + /* This makes sure instance is created if necessary. */ + r = socket_instantiate_service(SOCKET(u)); + if (r < 0) { + log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m"); + return r; + } + + /* This checks both type of sockets */ + if (UNIT_ISSET(SOCKET(u)->service)) { + Service *service; + + service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); + log_unit_debug(u, "Using %s", UNIT(service)->id); + + if (UNIT(service)->load_state != UNIT_LOADED) { + log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id); + return -ENOENT; + } + } + + return 0; +} + +static int verify_executable(Unit *u, ExecCommand *exec) { + if (exec == NULL) + return 0; + + if (access(exec->path, X_OK) < 0) + return log_unit_error_errno(u, errno, "Command %s is not executable: %m", exec->path); + + return 0; +} + +static int verify_executables(Unit *u) { + ExecCommand *exec; + int r = 0, k; + unsigned i; + + assert(u); + + exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : + u->type == UNIT_MOUNT ? MOUNT(u)->control_command : + u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; + k = verify_executable(u, exec); + if (k < 0 && r == 0) + r = k; + + if (u->type == UNIT_SERVICE) + for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { + k = verify_executable(u, SERVICE(u)->exec_command[i]); + if (k < 0 && r == 0) + r = k; + } + + if (u->type == UNIT_SOCKET) + for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { + k = verify_executable(u, SOCKET(u)->exec_command[i]); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static int verify_documentation(Unit *u, bool check_man) { + char **p; + int r = 0, k; + + STRV_FOREACH(p, u->documentation) { + log_unit_debug(u, "Found documentation item: %s", *p); + + if (check_man && startswith(*p, "man:")) { + k = show_man_page(*p + 4, true); + if (k != 0) { + if (k < 0) + log_unit_error_errno(u, r, "Can't show %s: %m", *p); + else { + log_unit_error_errno(u, r, "man %s command failed with code %d", *p + 4, k); + k = -ENOEXEC; + } + if (r == 0) + r = k; + } + } + } + + /* Check remote URLs? */ + + return r; +} + +static int verify_unit(Unit *u, bool check_man) { + _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + int r, k; + + assert(u); + + if (log_get_max_level() >= LOG_DEBUG) + 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, &err, NULL); + if (r < 0) + 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) + r = k; + + k = verify_executables(u); + if (k < 0 && r == 0) + r = k; + + k = verify_documentation(u, check_man); + if (k < 0 && r == 0) + r = k; + + return r; +} + +int verify_units(char **filenames, UnitFileScope scope, bool check_man) { + _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + _cleanup_free_ char *var = NULL; + Manager *m = NULL; + FILE *serial = NULL; + FDSet *fdset = NULL; + char **filename; + int r = 0, k; + + Unit *units[strv_length(filenames)]; + int i, count = 0; + + if (strv_isempty(filenames)) + return 0; + + /* set the path */ + r = generate_path(&var, filenames); + if (r < 0) + return log_error_errno(r, "Failed to generate unit load path: %m"); + + assert_se(set_unit_path(var) >= 0); + + r = manager_new(scope, true, &m); + if (r < 0) + return log_error_errno(r, "Failed to initialize manager: %m"); + + log_debug("Starting manager..."); + + r = manager_startup(m, serial, fdset); + if (r < 0) { + log_error_errno(r, "Failed to start manager: %m"); + goto finish; + } + + manager_clear_jobs(m); + + log_debug("Loading remaining units from the command line..."); + + STRV_FOREACH(filename, filenames) { + _cleanup_free_ char *prepared = NULL; + + log_debug("Handling %s...", *filename); + + 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) + r = k; + } else + count++; + } + + for (i = 0; i < count; i++) { + k = verify_unit(units[i], check_man); + if (k < 0 && r == 0) + r = k; + } + +finish: + manager_free(m); + + return r; +} diff --git a/src/grp-system/grp-utils/systemd-analyze/analyze-verify.h b/src/grp-system/grp-utils/systemd-analyze/analyze-verify.h new file mode 100644 index 0000000000..7b89007fd0 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/analyze-verify.h @@ -0,0 +1,26 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "shared/path-lookup.h" + +int verify_units(char **filenames, UnitFileScope scope, bool check_man); diff --git a/src/grp-system/grp-utils/systemd-analyze/analyze.c b/src/grp-system/grp-utils/systemd-analyze/analyze.c new file mode 100644 index 0000000000..e79a109651 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/analyze.c @@ -0,0 +1,1486 @@ +/*** + This file is part of systemd. + + Copyright 2010-2013 Lennart Poettering + Copyright 2013 Simon Peeters + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/glob-util.h" +#include "basic/hashmap.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/special.h" +#include "basic/strv.h" +#include "basic/strxcpyx.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" +#include "shared/pager.h" + +#include "analyze-verify.h" + +#define SCALE_X (0.1 / 1000.0) /* pixels per us */ +#define SCALE_Y (20.0) + +#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0)) + +#define svg(...) printf(__VA_ARGS__) + +#define svg_bar(class, x1, x2, y) \ + svg(" \n", \ + (class), \ + SCALE_X * (x1), SCALE_Y * (y), \ + SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0) + +#define svg_text(b, x, y, format, ...) \ + do { \ + svg(" ", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \ + svg(format, ## __VA_ARGS__); \ + svg("\n"); \ + } while (false) + +static enum dot { + DEP_ALL, + DEP_ORDER, + DEP_REQUIRE +} arg_dot = DEP_ALL; +static char** arg_dot_from_patterns = NULL; +static char** arg_dot_to_patterns = NULL; +static usec_t arg_fuzz = 0; +static bool arg_no_pager = false; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_user = false; +static bool arg_man = true; + +struct boot_times { + usec_t firmware_time; + usec_t loader_time; + usec_t kernel_time; + usec_t kernel_done_time; + usec_t initrd_time; + usec_t userspace_time; + usec_t finish_time; + usec_t security_start_time; + usec_t security_finish_time; + usec_t generators_start_time; + usec_t generators_finish_time; + usec_t unitsload_start_time; + usec_t unitsload_finish_time; + + /* + * If we're analyzing the user instance, all timestamps will be offset + * by its own start-up timestamp, which may be arbitrarily big. + * With "plot", this causes arbitrarily wide output SVG files which almost + * completely consist of empty space. Thus we cancel out this offset. + * + * This offset is subtracted from times above by acquire_boot_times(), + * but it still needs to be subtracted from unit-specific timestamps + * (so it is stored here for reference). + */ + usec_t reverse_offset; +}; + +struct unit_times { + char *name; + usec_t activating; + usec_t activated; + usec_t deactivated; + usec_t deactivating; + usec_t time; +}; + +struct host_info { + char *hostname; + char *kernel_name; + char *kernel_release; + char *kernel_version; + char *os_pretty_name; + char *virtualization; + char *architecture; +}; + +static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(property); + assert(val); + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + interface, + property, + &error, + 't', val); + + if (r < 0) { + log_error("Failed to parse reply: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(path); + assert(property); + assert(strv); + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + property, + &error, + strv); + if (r < 0) { + log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int compare_unit_time(const void *a, const void *b) { + return compare(((struct unit_times *)b)->time, + ((struct unit_times *)a)->time); +} + +static int compare_unit_start(const void *a, const void *b) { + return compare(((struct unit_times *)a)->activating, + ((struct unit_times *)b)->activating); +} + +static void free_unit_times(struct unit_times *t, unsigned n) { + struct unit_times *p; + + for (p = t; p < t + n; p++) + free(p->name); + + free(t); +} + +static void subtract_timestamp(usec_t *a, usec_t b) { + assert(a); + + if (*a > 0) { + assert(*a >= b); + *a -= b; + } +} + +static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { + static struct boot_times times; + static bool cached = false; + + if (cached) + goto finish; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + if (bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "FirmwareTimestampMonotonic", + ×.firmware_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LoaderTimestampMonotonic", + ×.loader_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KernelTimestamp", + ×.kernel_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "InitRDTimestampMonotonic", + ×.initrd_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UserspaceTimestampMonotonic", + ×.userspace_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "FinishTimestampMonotonic", + ×.finish_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SecurityStartTimestampMonotonic", + ×.security_start_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SecurityFinishTimestampMonotonic", + ×.security_finish_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GeneratorsStartTimestampMonotonic", + ×.generators_start_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GeneratorsFinishTimestampMonotonic", + ×.generators_finish_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitsLoadStartTimestampMonotonic", + ×.unitsload_start_time) < 0 || + bus_get_uint64_property(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitsLoadFinishTimestampMonotonic", + ×.unitsload_finish_time) < 0) + return -EIO; + + if (times.finish_time <= 0) { + log_error("Bootup is not yet finished. Please try again later."); + return -EINPROGRESS; + } + + if (arg_user) { + /* + * User-instance-specific timestamps processing + * (see comment to reverse_offset in struct boot_times). + */ + times.reverse_offset = times.userspace_time; + + times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0; + subtract_timestamp(×.finish_time, times.reverse_offset); + + subtract_timestamp(×.security_start_time, times.reverse_offset); + subtract_timestamp(×.security_finish_time, times.reverse_offset); + + subtract_timestamp(×.generators_start_time, times.reverse_offset); + subtract_timestamp(×.generators_finish_time, times.reverse_offset); + + subtract_timestamp(×.unitsload_start_time, times.reverse_offset); + subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); + } else { + if (times.initrd_time) + times.kernel_done_time = times.initrd_time; + else + times.kernel_done_time = times.userspace_time; + } + + cached = true; + +finish: + *bt = × + return 0; +} + +static void free_host_info(struct host_info *hi) { + + if (!hi) + return; + + free(hi->hostname); + free(hi->kernel_name); + free(hi->kernel_release); + free(hi->kernel_version); + free(hi->os_pretty_name); + free(hi->virtualization); + free(hi->architecture); + 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_(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; + size_t size = 0; + UnitInfo u; + + r = acquire_boot_times(bus, &boot_times); + if (r < 0) + goto fail; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &error, &reply, + NULL); + if (r < 0) { + log_error("Failed to list units: %s", bus_error_message(&error, -r)); + goto fail; + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + struct unit_times *t; + + if (!GREEDY_REALLOC(unit_times, size, c+1)) { + r = log_oom(); + goto fail; + } + + t = unit_times+c; + t->name = NULL; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + if (bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveExitTimestampMonotonic", + &t->activating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveEnterTimestampMonotonic", + &t->activated) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveExitTimestampMonotonic", + &t->deactivating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveEnterTimestampMonotonic", + &t->deactivated) < 0) { + r = -EIO; + goto fail; + } + + subtract_timestamp(&t->activating, boot_times->reverse_offset); + subtract_timestamp(&t->activated, boot_times->reverse_offset); + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + + if (t->activated >= t->activating) + t->time = t->activated - t->activating; + else if (t->deactivated >= t->activating) + t->time = t->deactivated - t->activating; + else + t->time = 0; + + if (t->activating == 0) + continue; + + t->name = strdup(u.id); + if (t->name == NULL) { + r = log_oom(); + goto fail; + } + c++; + } + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + *out = unit_times; + return c; + +fail: + if (unit_times) + free_unit_times(unit_times, (unsigned) c); + return r; +} + +static int acquire_host_info(sd_bus *bus, struct host_info **hi) { + 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) }, + { "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) }, + {} + }; + + _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(); + + r = bus_map_all_properties(bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + hostname_map, + host); + if (r < 0) + 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", + "/org/freedesktop/systemd1", + manager_map, + host); + if (r < 0) + return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r)); + + *hi = host; + host = NULL; + + return 0; +} + +static int pretty_boot_time(sd_bus *bus, char **_buf) { + char ts[FORMAT_TIMESPAN_MAX]; + struct boot_times *t; + static char buf[4096]; + size_t size; + char *ptr; + int r; + + r = acquire_boot_times(bus, &t); + if (r < 0) + return r; + + ptr = buf; + size = sizeof(buf); + + size = strpcpyf(&ptr, size, "Startup finished in "); + if (t->firmware_time) + size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC)); + if (t->loader_time) + size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC)); + if (t->kernel_time) + size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC)); + if (t->initrd_time > 0) + size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC)); + + size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC)); + strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); + + ptr = strdup(buf); + if (!ptr) + return log_oom(); + + *_buf = ptr; + return 0; +} + +static void svg_graph_box(double height, double begin, double end) { + long long i; + + /* outside box, fill */ + svg("\n", + SCALE_X * (end - begin), SCALE_Y * height); + + for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) { + /* lines for each second */ + if (i % 5000000 == 0) + svg(" \n" + " %.01fs\n", + SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i); + else if (i % 1000000 == 0) + svg(" \n" + " %.01fs\n", + SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i); + else + svg(" \n", + SCALE_X * i, SCALE_X * i, SCALE_Y * height); + } +} + +static int analyze_plot(sd_bus *bus) { + _cleanup_(free_host_infop) struct host_info *host = NULL; + struct unit_times *times; + struct boot_times *boot; + int n, m = 1, y=0; + double width; + _cleanup_free_ char *pretty_times = NULL; + struct unit_times *u; + + n = acquire_boot_times(bus, &boot); + if (n < 0) + return n; + + n = pretty_boot_time(bus, &pretty_times); + if (n < 0) + return n; + + n = acquire_host_info(bus, &host); + if (n < 0) + return n; + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + qsort(times, n, sizeof(struct unit_times), compare_unit_start); + + width = SCALE_X * (boot->firmware_time + boot->finish_time); + if (width < 800.0) + width = 800.0; + + if (boot->firmware_time > boot->loader_time) + m++; + if (boot->loader_time) { + m++; + if (width < 1000.0) + width = 1000.0; + } + if (boot->initrd_time) + m++; + if (boot->kernel_time) + m++; + + for (u = times; u < times + n; u++) { + double text_start, text_width; + + if (u->activating < boot->userspace_time || + u->activating > boot->finish_time) { + u->name = mfree(u->name); + continue; + } + + /* If the text cannot fit on the left side then + * increase the svg width so it fits on the right. + * TODO: calculate the text width more accurately */ + text_width = 8.0 * strlen(u->name); + text_start = (boot->firmware_time + u->activating) * SCALE_X; + if (text_width > text_start && text_width + text_start > width) + width = text_width + text_start; + + if (u->deactivated > u->activating && u->deactivated <= boot->finish_time + && u->activated == 0 && u->deactivating == 0) + u->activated = u->deactivating = u->deactivated; + if (u->activated < u->activating || u->activated > boot->finish_time) + u->activated = boot->finish_time; + if (u->deactivating < u->activated || u->activated > boot->finish_time) + u->deactivating = boot->finish_time; + if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time) + u->deactivated = boot->finish_time; + m++; + } + + svg("\n" + "\n"); + + svg("\n\n", + 80.0 + width, 150.0 + (m * SCALE_Y) + + 5 * SCALE_Y /* legend */); + + /* write some basic info as a comment, including some help */ + svg("\n" + "\n" + "\n" + "\n" + "\n\n" + "\n\n", VERSION); + + /* style sheet */ + svg("\n \n\n\n"); + + svg("\n"); + svg("%s", pretty_times); + svg("%s %s (%s %s %s) %s %s", + isempty(host->os_pretty_name) ? "GNU/Linux" : host->os_pretty_name, + strempty(host->hostname), + strempty(host->kernel_name), + strempty(host->kernel_release), + strempty(host->kernel_version), + strempty(host->architecture), + strempty(host->virtualization)); + + svg("\n", 20.0 + (SCALE_X * boot->firmware_time)); + svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); + + if (boot->firmware_time) { + svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y); + svg_text(true, -(double) boot->firmware_time, y, "firmware"); + y++; + } + if (boot->loader_time) { + svg_bar("loader", -(double) boot->loader_time, 0, y); + svg_text(true, -(double) boot->loader_time, y, "loader"); + y++; + } + if (boot->kernel_time) { + svg_bar("kernel", 0, boot->kernel_done_time, y); + svg_text(true, 0, y, "kernel"); + y++; + } + if (boot->initrd_time) { + svg_bar("initrd", boot->initrd_time, boot->userspace_time, y); + svg_text(true, boot->initrd_time, y, "initrd"); + y++; + } + svg_bar("active", boot->userspace_time, boot->finish_time, y); + svg_bar("security", boot->security_start_time, boot->security_finish_time, y); + svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y); + svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y); + svg_text(true, boot->userspace_time, y, "systemd"); + y++; + + for (u = times; u < times + n; u++) { + char ts[FORMAT_TIMESPAN_MAX]; + bool b; + + if (!u->name) + continue; + + svg_bar("activating", u->activating, u->activated, y); + svg_bar("active", u->activated, u->deactivating, y); + svg_bar("deactivating", u->deactivating, u->deactivated, y); + + /* place the text on the left if we have passed the half of the svg width */ + b = u->activating * SCALE_X < width / 2; + if (u->time) + svg_text(b, u->activating, y, "%s (%s)", + u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC)); + else + svg_text(b, u->activating, y, "%s", u->name); + y++; + } + + svg("\n"); + + /* Legend */ + svg("\n"); + y++; + svg_bar("activating", 0, 300000, y); + svg_text(true, 400000, y, "Activating"); + y++; + svg_bar("active", 0, 300000, y); + svg_text(true, 400000, y, "Active"); + y++; + svg_bar("deactivating", 0, 300000, y); + svg_text(true, 400000, y, "Deactivating"); + y++; + svg_bar("security", 0, 300000, y); + svg_text(true, 400000, y, "Setting up security module"); + y++; + svg_bar("generators", 0, 300000, y); + svg_text(true, 400000, y, "Generators"); + y++; + svg_bar("unitsload", 0, 300000, y); + svg_text(true, 400000, y, "Loading unit files"); + y++; + + svg("\n\n"); + + svg("\n"); + + free_unit_times(times, (unsigned) n); + + n = 0; + return n; +} + +static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches, + bool last, struct unit_times *times, struct boot_times *boot) { + unsigned int i; + char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX]; + + for (i = level; i != 0; i--) + printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE)); + + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); + + if (times) { + if (times->time) + printf("%s%s @%s +%s%s", ansi_highlight_red(), name, + format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC), + format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC)); + else + printf("%s", name); + } else + printf("%s", name); + printf("\n"); + + return 0; +} + +static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { + _cleanup_free_ char *path = NULL; + + assert(bus); + assert(name); + assert(deps); + + path = unit_dbus_path_from_name(name); + if (path == NULL) + return -ENOMEM; + + return bus_get_unit_property_strv(bus, path, "After", deps); +} + +static Hashmap *unit_times_hashmap; + +static int list_dependencies_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + usec_t usa = 0, usb = 0; + struct unit_times *times; + + times = hashmap_get(unit_times_hashmap, *a); + if (times) + usa = times->activated; + times = hashmap_get(unit_times_hashmap, *b); + if (times) + usb = times->activated; + + return usb - usa; +} + +static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units, + unsigned int branches) { + _cleanup_strv_free_ char **deps = NULL; + char **c; + int r = 0; + usec_t service_longest = 0; + int to_print = 0; + struct unit_times *times; + struct boot_times *boot; + + if (strv_extend(units, name)) + return log_oom(); + + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; + + qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); + + r = acquire_boot_times(bus, &boot); + if (r < 0) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); + if (times + && times->activated + && times->activated <= boot->finish_time + && (times->activated >= service_longest + || service_longest == 0)) { + service_longest = times->activated; + break; + } + } + + if (service_longest == 0 ) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); + if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz) + to_print++; + } + + if (!to_print) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); + if (!times + || !times->activated + || times->activated > boot->finish_time + || service_longest - times->activated > arg_fuzz) + continue; + + to_print--; + + r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot); + if (r < 0) + return r; + + if (strv_contains(*units, *c)) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0), + true, NULL, boot); + if (r < 0) + return r; + continue; + } + + r = list_dependencies_one(bus, *c, level + 1, units, + (branches << 1) | (to_print ? 1 : 0)); + if (r < 0) + return r; + + if (!to_print) + break; + } + return 0; +} + +static int list_dependencies(sd_bus *bus, const char *name) { + _cleanup_strv_free_ char **units = NULL; + char ts[FORMAT_TIMESPAN_MAX]; + struct unit_times *times; + int r; + const char *id; + _cleanup_free_ char *path = 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); + + path = unit_dbus_path_from_name(name); + if (path == NULL) + return -ENOMEM; + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &reply, + "s"); + if (r < 0) { + log_error("Failed to get ID: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &id); + if (r < 0) + return bus_log_parse_error(r); + + times = hashmap_get(unit_times_hashmap, id); + + r = acquire_boot_times(bus, &boot); + if (r < 0) + return r; + + if (times) { + if (times->time) + printf("%s%s +%s%s\n", ansi_highlight_red(), id, + format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC)); + else + printf("%s\n", id); + } + + return list_dependencies_one(bus, name, 0, &units, 0); +} + +static int analyze_critical_chain(sd_bus *bus, char *names[]) { + struct unit_times *times; + unsigned int i; + Hashmap *h; + int n, r; + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + h = hashmap_new(&string_hash_ops); + if (!h) + return -ENOMEM; + + for (i = 0; i < (unsigned)n; i++) { + r = hashmap_put(h, times[i].name, ×[i]); + if (r < 0) + return r; + } + unit_times_hashmap = h; + + pager_open(arg_no_pager, false); + + puts("The time after the unit is active or started is printed after the \"@\" character.\n" + "The time the unit takes to start is printed after the \"+\" character.\n"); + + if (!strv_isempty(names)) { + char **name; + STRV_FOREACH(name, names) + list_dependencies(bus, *name); + } else + list_dependencies(bus, SPECIAL_DEFAULT_TARGET); + + hashmap_free(h); + free_unit_times(times, (unsigned) n); + return 0; +} + +static int analyze_blame(sd_bus *bus) { + struct unit_times *times; + unsigned i; + int n; + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + qsort(times, n, sizeof(struct unit_times), compare_unit_time); + + pager_open(arg_no_pager, false); + + for (i = 0; i < (unsigned) n; i++) { + char ts[FORMAT_TIMESPAN_MAX]; + + if (times[i].time > 0) + printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name); + } + + free_unit_times(times, (unsigned) n); + return 0; +} + +static int analyze_time(sd_bus *bus) { + _cleanup_free_ char *buf = NULL; + int r; + + r = pretty_boot_time(bus, &buf); + if (r < 0) + return r; + + puts(buf); + return 0; +} + +static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) { + _cleanup_strv_free_ char **units = NULL; + char **unit; + int r; + bool match_patterns; + + assert(u); + assert(prop); + assert(color); + + match_patterns = strv_fnmatch(patterns, u->id, 0); + + if (!strv_isempty(from_patterns) && + !match_patterns && + !strv_fnmatch(from_patterns, u->id, 0)) + return 0; + + r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + bool match_patterns2; + + match_patterns2 = strv_fnmatch(patterns, *unit, 0); + + if (!strv_isempty(to_patterns) && + !match_patterns2 && + !strv_fnmatch(to_patterns, *unit, 0)) + continue; + + if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) + continue; + + printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); + } + + return 0; +} + +static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { + int r; + + assert(bus); + assert(u); + + 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 (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, "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); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + } + + return 0; +} + +static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { + _cleanup_strv_free_ char **expanded_patterns = NULL; + char **pattern; + int r; + + STRV_FOREACH(pattern, patterns) { + _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) + return log_oom(); + + if (string_is_glob(*pattern)) + continue; + + unit = unit_dbus_path_from_name(*pattern); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + if (!streq(*pattern, unit_id)) { + if (strv_extend(&expanded_patterns, unit_id) < 0) + return log_oom(); + } + } + + *ret = expanded_patterns; + expanded_patterns = NULL; /* do not free */ + + return 0; +} + +static int dot(sd_bus *bus, char* patterns[]) { + _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; + int r; + UnitInfo u; + + r = expand_patterns(bus, patterns, &expanded_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &error, + &reply, + ""); + if (r < 0) { + log_error("Failed to list units: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + printf("digraph systemd {\n"); + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + + r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + printf("}\n"); + + log_info(" Color legend: black = Requires\n" + " dark blue = Requisite\n" + " dark grey = Wants\n" + " red = Conflicts\n" + " green = After\n"); + + if (on_tty()) + log_notice("-- You probably want to process this output with graphviz' dot tool.\n" + "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); + + return 0; +} + +static int dump(sd_bus *bus, char **args) { + _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; + + if (!strv_isempty(args)) { + log_error("Too many arguments."); + return -E2BIG; + } + + pager_open(arg_no_pager, false); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Dump", + &error, + &reply, + ""); + 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) + return bus_log_parse_error(r); + + fputs(text, stdout); + return 0; +} + +static int set_log_level(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", + "LogLevel", + &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; +} + +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; +} + +static void help(void) { + + pager_open(arg_no_pager, false); + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Profile systemd, show unit dependencies, check unit files.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --system Operate on system systemd instance\n" + " --user Operate on user systemd instance\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --order Show only order in the graph\n" + " --require Show only requirement in the graph\n" + " --from-pattern=GLOB Show only origins in the graph\n" + " --to-pattern=GLOB Show only destinations in the graph\n" + " --fuzz=SECONDS Also print also services which finished SECONDS\n" + " earlier than the latest in the branch\n" + " --man[=BOOL] Do [not] check for existence of man pages\n\n" + "Commands:\n" + " time Print time spent in the kernel\n" + " blame Print list of running units ordered by time to init\n" + " critical-chain Print a tree of the time critical chain of units\n" + " plot Output SVG graphic showing service initialization\n" + " dot Output dependency graph in dot(1) format\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); + + /* When updating this list, including descriptions, apply + * changes to shell-completion/bash/systemd-analyze and + * shell-completion/zsh/_systemd-analyze too. */ +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_ORDER, + ARG_REQUIRE, + ARG_USER, + ARG_SYSTEM, + ARG_DOT_FROM_PATTERN, + ARG_DOT_TO_PATTERN, + ARG_FUZZ, + ARG_NO_PAGER, + ARG_MAN, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "order", no_argument, NULL, ARG_ORDER }, + { "require", no_argument, NULL, ARG_REQUIRE }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN }, + { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN }, + { "fuzz", required_argument, NULL, ARG_FUZZ }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "man", optional_argument, NULL, ARG_MAN }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + {} + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_USER: + arg_user = true; + break; + + case ARG_SYSTEM: + arg_user = false; + break; + + case ARG_ORDER: + arg_dot = DEP_ORDER; + break; + + case ARG_REQUIRE: + arg_dot = DEP_REQUIRE; + break; + + case ARG_DOT_FROM_PATTERN: + if (strv_extend(&arg_dot_from_patterns, optarg) < 0) + return log_oom(); + + break; + + case ARG_DOT_TO_PATTERN: + if (strv_extend(&arg_dot_to_patterns, optarg) < 0) + return log_oom(); + + break; + + case ARG_FUZZ: + r = parse_sec(optarg, &arg_fuzz); + if (r < 0) + return r; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_MAN: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --man= argument."); + return -EINVAL; + } + + arg_man = !!r; + } else + arg_man = true; + + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option code."); + } + + return 1; /* work to do */ +} + +int main(int argc, char *argv[]) { + int r; + + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (streq_ptr(argv[optind], "verify")) + r = verify_units(argv+optind+1, + arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, + arg_man); + else { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + + 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; + } + + if (!argv[optind] || streq(argv[optind], "time")) + r = analyze_time(bus); + else if (streq(argv[optind], "blame")) + r = analyze_blame(bus); + else if (streq(argv[optind], "critical-chain")) + r = analyze_critical_chain(bus, argv+optind+1); + else if (streq(argv[optind], "plot")) + r = analyze_plot(bus); + else if (streq(argv[optind], "dot")) + r = dot(bus, argv+optind+1); + else if (streq(argv[optind], "dump")) + 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]); + } + +finish: + pager_close(); + + strv_free(arg_dot_from_patterns); + strv_free(arg_dot_to_patterns); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.bash b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.bash new file mode 100644 index 0000000000..7a5f46ba1d --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.bash @@ -0,0 +1,117 @@ +# systemd-analyze(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# Copyright 2013 Harald Hoyer +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +_systemd_analyze() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + + local -A OPTS=( + [STANDALONE]='--help --version --system --user --order --require --no-pager --man' + [ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern ' + ) + + local -A VERBS=( + [STANDALONE]='time blame plot dump' + [CRITICAL_CHAIN]='critical-chain' + [DOT]='dot' + [LOG_LEVEL]='set-log-level' + [VERIFY]='verify' + ) + + _init_completion || return + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --host|-H) + comps=$(compgen -A hostname) + ;; + --machine|-M) + comps=$( __get_machines ) + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ -z $verb && $cur = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user' + fi + + elif __contains_word "$verb" ${VERBS[CRITICAL_CHAIN]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user --fuzz' + fi + + elif __contains_word "$verb" ${VERBS[DOT]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user --from-pattern --to-pattern --order --require' + fi + + elif __contains_word "$verb" ${VERBS[LOG_LEVEL]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user' + else + comps='debug info notice warning err crit alert emerg' + fi + + elif __contains_word "$verb" ${VERBS[VERIFY]}; then + if [[ $cur = -* ]]; then + comps='--help --version --system --user --man' + else + comps=$( compgen -A file -- "$cur" ) + compopt -o filenames + fi + + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd_analyze systemd-analyze diff --git a/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.zsh b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.zsh new file mode 100644 index 0000000000..efafddc686 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.completion.zsh @@ -0,0 +1,58 @@ +#compdef systemd-analyze + +_systemd_analyze_set-log-level() { + local -a _levels + _levels=(debug info notice warning err crit alert emerg) + _describe -t level 'logging level' _levels || compadd "$@" +} + +_systemd_analyze_verify() { + _sd_unit_files +} + +_systemd_analyze_command(){ + local -a _systemd_analyze_cmds + # Descriptions taken from systemd-analyze --help. + _systemd_analyze_cmds=( + 'time:Print time spent in the kernel before reaching userspace' + 'blame:Print list of running units ordered by time to init' + 'critical-chain:Print a tree of the time critical chain of units' + 'plot:Output SVG graphic showing service initialization' + 'dot:Dump dependency graph (in dot(1) format)' + 'dump:Dump server status' + 'set-log-level:Set systemd log threshold' + 'verify:Check unit files for correctness' + ) + + if (( CURRENT == 1 )); then + _describe "options" _systemd_analyze_cmds + else + local curcontext="$curcontext" + cmd="${${_systemd_analyze_cmds[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + if (( $+functions[_systemd_analyze_$cmd] )) && (( CURRENT == 2 )); then + _systemd_analyze_$cmd + else + _message "no more options" + fi + else + _message "unknown systemd-analyze command: $words[1]" + fi + fi +} + +_arguments \ + {-h,--help}'[Show help text]' \ + '--version[Show package version]' \ + '--system[Operate on system systemd instance]' \ + '--user[Operate on user systemd instance]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \ + '--order[When generating graph for dot, show only order]' \ + '--require[When generating graph for dot, show only requirement]' \ + '--fuzz=[When printing the tree of the critical chain, print also services, which finished TIMESPAN earlier, than the latest in the branch]:TIMESPAN' \ + '--from-pattern=[When generating a dependency graph, filter only origins]:GLOB' \ + '--to-pattern=[When generating a dependency graph, filter only destinations]:GLOB' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machine:_sd_machines' \ + '*::systemd-analyze commands:_systemd_analyze_command' diff --git a/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.xml b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.xml new file mode 100644 index 0000000000..bc37765dff --- /dev/null +++ b/src/grp-system/grp-utils/systemd-analyze/systemd-analyze.xml @@ -0,0 +1,388 @@ + + + + + + + + + systemd-analyze + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + Developer + Harald + Hoyer + harald@redhat.com + + + + + + systemd-analyze + 1 + + + + systemd-analyze + Analyze system boot-up performance + + + + + systemd-analyze + OPTIONS + time + + + systemd-analyze + OPTIONS + blame + + + systemd-analyze + OPTIONS + critical-chain + UNIT + + + systemd-analyze + OPTIONS + plot + > file.svg + + + systemd-analyze + OPTIONS + dot + PATTERN + > file.dot + + + systemd-analyze + OPTIONS + dump + + + systemd-analyze + OPTIONS + set-log-level + LEVEL + + + systemd-analyze + OPTIONS + set-log-target + TARGET + + + systemd-analyze + OPTIONS + verify + FILES + + + + + Description + + systemd-analyze may be used to determine + system boot-up performance statistics and retrieve other state and + tracing information from the system and service manager, and to + verify the correctness of unit files. + + systemd-analyze time prints the time + spent in the kernel before userspace has been reached, the time + spent in the initial RAM disk (initrd) before normal system + userspace has been reached, and the time normal system userspace + took to initialize. Note that these measurements simply measure + the time passed up to the point where all system services have + been spawned, but not necessarily until they fully finished + initialization or the disk is idle. + + systemd-analyze blame prints a list of + all running units, ordered by the time they took to initialize. + This information may be used to optimize boot-up times. Note that + the output might be misleading as the initialization of one + service might be slow simply because it waits for the + initialization of another service to complete. + + systemd-analyze critical-chain + [UNIT...] prints a tree of + the time-critical chain of units (for each of the specified + UNITs or for the default target + otherwise). The time after the unit is active or started is + printed after the "@" character. The time the unit takes to start + is printed after the "+" character. Note that the output might be + misleading as the initialization of one service might depend on + socket activation and because of the parallel execution of + units. + + systemd-analyze plot prints an SVG + graphic detailing which system services have been started at what + time, highlighting the time they spent on initialization. + + systemd-analyze dot generates textual + dependency graph description in dot format for further processing + with the GraphViz + dot1 + tool. Use a command line like systemd-analyze dot | dot + -Tsvg > systemd.svg to generate a graphical dependency + tree. Unless or + is passed, the generated graph will + show both ordering and requirement dependencies. Optional pattern + globbing style specifications (e.g. *.target) + may be given at the end. A unit dependency is included in the + graph if any of these patterns match either the origin or + destination node. + + systemd-analyze dump outputs a (usually + very long) human-readable serialization of the complete server + state. Its format is subject to change without notice and should + not be parsed by applications. + + systemd-analyze set-log-level + LEVEL changes the current log + level of the systemd daemon to + LEVEL (accepts the same values as + described in + systemd1). + + systemd-analyze set-log-target + TARGET changes the current log + target of the systemd daemon to + TARGET (accepts the same values as + , described in + systemd1). + + systemd-analyze verify will load unit + files and print warnings if any errors are detected. Files + specified on the command line will be loaded, but also any other + units referenced by them. This command works by prepending the + directories for all command line arguments at the beginning of the + unit load path, which means that all units files found in those + directories will be used in preference to the unit files found in + the standard locations, even if not listed explicitly. + + If no command is passed, systemd-analyze + time is implied. + + + + + Options + + The following options are understood: + + + + + + Operates on the user systemd + instance. + + + + + + Operates on the system systemd instance. This + is the implied default. + + + + + + + When used in conjunction with the + dot command (see above), selects which + dependencies are shown in the dependency graph. If + is passed, only dependencies of type + After= or Before= are + shown. If is passed, only + dependencies of type Requires=, + Requisite=, + Wants= and Conflicts= + are shown. If neither is passed, this shows dependencies of + all these types. + + + + + + + When used in conjunction with the + dot command (see above), this selects which + relationships are shown in the dependency graph. Both options + require a + glob7 + pattern as an argument, which will be matched against the + left-hand and the right-hand, respectively, nodes of a + relationship. + + Each of these can be used more than once, in which case + the unit name must match one of the values. When tests for + both sides of the relation are present, a relation must pass + both tests to be shown. When patterns are also specified as + positional arguments, they must match at least one side of the + relation. In other words, patterns specified with those two + options will trim the list of edges matched by the positional + arguments, if any are given, and fully determine the list of + edges shown otherwise. + + + + timespan + + When used in conjunction with the + critical-chain command (see above), also + show units, which finished timespan + earlier, than the latest unit in the same level. The unit of + timespan is seconds unless + specified with a different unit, e.g. + "50ms". + + + + + + Do not invoke man to verify the existence of + man pages listed in Documentation=. + + + + + + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + Examples for <command>dot</command> + + + Plots all dependencies of any unit whose name starts with + <literal>avahi-daemon</literal> + + $ systemd-analyze dot 'avahi-daemon.*' | dot -Tsvg > avahi.svg + $ eog avahi.svg + + + + Plots the dependencies between all known target units + + systemd-analyze dot --to-pattern='*.target' --from-pattern='*.target' | dot -Tsvg > targets.svg +$ eog targets.svg + + + + + Examples for <command>verify</command> + + The following errors are currently detected: + + unknown sections and directives, + + + missing dependencies which are required to start + the given unit, + + man pages listed in + Documentation= which are not found in the + system, + + commands listed in ExecStart= + and similar which are not found in the system or not + executable. + + + + Misspelt directives + + $ cat ./user.slice +[Unit] +WhatIsThis=11 +Documentation=man:nosuchfile(1) +Requires=different.service + +[Service] +Desription=x + +$ systemd-analyze verify ./user.slice +[./user.slice:9] Unknown lvalue 'WhatIsThis' in section 'Unit' +[./user.slice:13] Unknown section 'Service'. Ignoring. +Error: org.freedesktop.systemd1.LoadFailed: + Unit different.service failed to load: + No such file or directory. +Failed to create user.slice/start: Invalid argument +user.slice: man nosuchfile(1) command failed with code 16 + + + + + Missing service units + + $ tail ./a.socket ./b.socket +==> ./a.socket <== +[Socket] +ListenStream=100 + +==> ./b.socket <== +[Socket] +ListenStream=100 +Accept=yes + +$ systemd-analyze verify ./a.socket ./b.socket +Service a.service not loaded, a.socket cannot be started. +Service b@0.service not loaded, b.socket cannot be started. + + + + + + + + See Also + + systemd1, + systemctl1 + + + + diff --git a/src/grp-system/grp-utils/systemd-delta/Makefile b/src/grp-system/grp-utils/systemd-delta/Makefile new file mode 100644 index 0000000000..7273647c52 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-delta/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-delta +systemd_delta_SOURCES = \ + src/delta/delta.c + +systemd_delta_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-delta/delta.c b/src/grp-system/grp-utils/systemd-delta/delta.c new file mode 100644 index 0000000000..5da779624d --- /dev/null +++ b/src/grp-system/grp-utils/systemd-delta/delta.c @@ -0,0 +1,635 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "shared/pager.h" + +static const char prefixes[] = + "/etc\0" + "/run\0" + "/usr/local/lib\0" + "/usr/local/share\0" + "/usr/lib\0" + "/usr/share\0" +#ifdef HAVE_SPLIT_USR + "/lib\0" +#endif + ; + +static const char suffixes[] = + "sysctl.d\0" + "tmpfiles.d\0" + "modules-load.d\0" + "binfmt.d\0" + "systemd/system\0" + "systemd/user\0" + "systemd/system-preset\0" + "systemd/user-preset\0" + "udev/rules.d\0" + "modprobe.d\0"; + +static const char have_dropins[] = + "systemd/system\0" + "systemd/user\0"; + +static bool arg_no_pager = false; +static int arg_diff = -1; + +static enum { + SHOW_MASKED = 1 << 0, + SHOW_EQUIVALENT = 1 << 1, + SHOW_REDIRECTED = 1 << 2, + SHOW_OVERRIDDEN = 1 << 3, + SHOW_UNCHANGED = 1 << 4, + SHOW_EXTENDED = 1 << 5, + + SHOW_DEFAULTS = + (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED) +} arg_flags = 0; + +static int equivalent(const char *a, const char *b) { + _cleanup_free_ char *x = NULL, *y = NULL; + + x = canonicalize_file_name(a); + if (!x) + return -errno; + + y = canonicalize_file_name(b); + if (!y) + return -errno; + + return path_equal(x, y); +} + +static int notify_override_masked(const char *top, const char *bottom) { + if (!(arg_flags & SHOW_MASKED)) + return 0; + + printf("%s%s%s %s %s %s\n", + ansi_highlight_red(), "[MASKED]", ansi_normal(), + top, special_glyph(ARROW), bottom); + return 1; +} + +static int notify_override_equivalent(const char *top, const char *bottom) { + if (!(arg_flags & SHOW_EQUIVALENT)) + return 0; + + printf("%s%s%s %s %s %s\n", + ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(), + top, special_glyph(ARROW), bottom); + return 1; +} + +static int notify_override_redirected(const char *top, const char *bottom) { + if (!(arg_flags & SHOW_REDIRECTED)) + return 0; + + printf("%s%s%s %s %s %s\n", + ansi_highlight(), "[REDIRECTED]", ansi_normal(), + top, special_glyph(ARROW), bottom); + return 1; +} + +static int notify_override_overridden(const char *top, const char *bottom) { + if (!(arg_flags & SHOW_OVERRIDDEN)) + return 0; + + printf("%s%s%s %s %s %s\n", + ansi_highlight(), "[OVERRIDDEN]", ansi_normal(), + top, special_glyph(ARROW), bottom); + return 1; +} + +static int notify_override_extended(const char *top, const char *bottom) { + if (!(arg_flags & SHOW_EXTENDED)) + return 0; + + printf("%s%s%s %s %s %s\n", + ansi_highlight(), "[EXTENDED]", ansi_normal(), + top, special_glyph(ARROW), bottom); + return 1; +} + +static int notify_override_unchanged(const char *f) { + if (!(arg_flags & SHOW_UNCHANGED)) + return 0; + + printf("[UNCHANGED] %s\n", f); + return 1; +} + +static int found_override(const char *top, const char *bottom) { + _cleanup_free_ char *dest = NULL; + int k; + pid_t pid; + + assert(top); + assert(bottom); + + if (null_or_empty_path(top) > 0) + return notify_override_masked(top, bottom); + + k = readlink_malloc(top, &dest); + if (k >= 0) { + if (equivalent(dest, bottom) > 0) + return notify_override_equivalent(top, bottom); + else + return notify_override_redirected(top, bottom); + } + + k = notify_override_overridden(top, bottom); + if (!arg_diff) + return k; + + putchar('\n'); + + fflush(stdout); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork off diff: %m"); + else if (pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + execlp("diff", "diff", "-us", "--", bottom, top, NULL); + log_error_errno(errno, "Failed to execute diff: %m"); + _exit(EXIT_FAILURE); + } + + wait_for_terminate_and_warn("diff", pid, false); + putchar('\n'); + + return k; +} + +static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) { + _cleanup_free_ char *unit = NULL; + _cleanup_free_ char *path = NULL; + _cleanup_strv_free_ char **list = NULL; + char **file; + char *c; + int r; + + assert(!endswith(drop, "/")); + + path = strjoin(toppath, "/", drop, NULL); + if (!path) + return -ENOMEM; + + log_debug("Looking at %s", path); + + unit = strdup(drop); + if (!unit) + return -ENOMEM; + + c = strrchr(unit, '.'); + if (!c) + return -EINVAL; + *c = 0; + + r = get_files_in_directory(path, &list); + if (r < 0) + return log_error_errno(r, "Failed to enumerate %s: %m", path); + + STRV_FOREACH(file, list) { + Hashmap *h; + int k; + char *p; + char *d; + + if (!endswith(*file, ".conf")) + continue; + + p = strjoin(path, "/", *file, NULL); + if (!p) + return -ENOMEM; + d = p + strlen(toppath) + 1; + + log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); + k = hashmap_put(top, d, p); + if (k >= 0) { + p = strdup(p); + if (!p) + return -ENOMEM; + d = p + strlen(toppath) + 1; + } else if (k != -EEXIST) { + free(p); + return k; + } + + log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); + free(hashmap_remove(bottom, d)); + k = hashmap_put(bottom, d, p); + if (k < 0) { + free(p); + return k; + } + + h = hashmap_get(drops, unit); + if (!h) { + h = hashmap_new(&string_hash_ops); + if (!h) + return -ENOMEM; + hashmap_put(drops, unit, h); + unit = strdup(unit); + if (!unit) + return -ENOMEM; + } + + p = strdup(p); + if (!p) + return -ENOMEM; + + log_debug("Adding to drops: %s %s %s %s %s", + unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); + k = hashmap_put(h, basename(p), p); + if (k < 0) { + free(p); + if (k != -EEXIST) + return k; + } + } + return 0; +} + +static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) { + _cleanup_closedir_ DIR *d; + + assert(top); + assert(bottom); + assert(drops); + assert(path); + + log_debug("Looking at %s", path); + + d = opendir(path); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open %s: %m", path); + } + + for (;;) { + struct dirent *de; + int k; + char *p; + + errno = 0; + de = readdir(d); + if (!de) + return -errno; + + dirent_ensure_type(d, de); + + if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) + enumerate_dir_d(top, bottom, drops, path, de->d_name); + + if (!dirent_is_file(de)) + continue; + + p = strjoin(path, "/", de->d_name, NULL); + if (!p) + return -ENOMEM; + + log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p); + k = hashmap_put(top, basename(p), p); + if (k >= 0) { + p = strdup(p); + if (!p) + return -ENOMEM; + } else if (k != -EEXIST) { + free(p); + return k; + } + + log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p); + free(hashmap_remove(bottom, basename(p))); + k = hashmap_put(bottom, basename(p), p); + if (k < 0) { + free(p); + return k; + } + } +} + +static int process_suffix(const char *suffix, const char *onlyprefix) { + const char *p; + char *f; + Hashmap *top, *bottom, *drops; + Hashmap *h; + char *key; + int r = 0, k; + Iterator i, j; + int n_found = 0; + bool dropins; + + assert(suffix); + assert(!startswith(suffix, "/")); + assert(!strstr(suffix, "//")); + + dropins = nulstr_contains(have_dropins, suffix); + + top = hashmap_new(&string_hash_ops); + bottom = hashmap_new(&string_hash_ops); + drops = hashmap_new(&string_hash_ops); + if (!top || !bottom || !drops) { + r = -ENOMEM; + goto finish; + } + + NULSTR_FOREACH(p, prefixes) { + _cleanup_free_ char *t = NULL; + + t = strjoin(p, "/", suffix, NULL); + if (!t) { + r = -ENOMEM; + goto finish; + } + + k = enumerate_dir(top, bottom, drops, t, dropins); + if (r == 0) + r = k; + } + + HASHMAP_FOREACH_KEY(f, key, top, i) { + char *o; + + o = hashmap_get(bottom, key); + assert(o); + + if (!onlyprefix || startswith(o, onlyprefix)) { + if (path_equal(o, f)) { + notify_override_unchanged(f); + } else { + k = found_override(f, o); + if (k < 0) + r = k; + else + n_found += k; + } + } + + h = hashmap_get(drops, key); + if (h) + HASHMAP_FOREACH(o, h, j) + if (!onlyprefix || startswith(o, onlyprefix)) + n_found += notify_override_extended(f, o); + } + +finish: + hashmap_free_free(top); + hashmap_free_free(bottom); + + HASHMAP_FOREACH_KEY(h, key, drops, i) { + hashmap_free_free(hashmap_remove(drops, key)); + hashmap_remove(drops, key); + free(key); + } + hashmap_free(drops); + + return r < 0 ? r : n_found; +} + +static int process_suffixes(const char *onlyprefix) { + const char *n; + int n_found = 0, r; + + NULSTR_FOREACH(n, suffixes) { + r = process_suffix(n, onlyprefix); + if (r < 0) + return r; + + n_found += r; + } + + return n_found; +} + +static int process_suffix_chop(const char *arg) { + const char *p; + + assert(arg); + + if (!path_is_absolute(arg)) + return process_suffix(arg, NULL); + + /* Strip prefix from the suffix */ + NULSTR_FOREACH(p, prefixes) { + const char *suffix; + + suffix = startswith(arg, p); + if (suffix) { + suffix += strspn(suffix, "/"); + if (*suffix) + return process_suffix(suffix, NULL); + else + return process_suffixes(arg); + } + } + + log_error("Invalid suffix specification %s.", arg); + return -EINVAL; +} + +static void help(void) { + printf("%s [OPTIONS...] [SUFFIX...]\n\n" + "Find overridden configuration files.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --diff[=1|0] Show a diff when overridden files differ\n" + " -t --type=LIST... Only display a selected set of override types\n" + , program_invocation_short_name); +} + +static int parse_flags(const char *flag_str, int flags) { + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) { + if (strneq("masked", word, l)) + flags |= SHOW_MASKED; + else if (strneq ("equivalent", word, l)) + flags |= SHOW_EQUIVALENT; + else if (strneq("redirected", word, l)) + flags |= SHOW_REDIRECTED; + else if (strneq("overridden", word, l)) + flags |= SHOW_OVERRIDDEN; + else if (strneq("unchanged", word, l)) + flags |= SHOW_UNCHANGED; + else if (strneq("extended", word, l)) + flags |= SHOW_EXTENDED; + else if (strneq("default", word, l)) + flags |= SHOW_DEFAULTS; + else + return -EINVAL; + } + return flags; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_NO_PAGER = 0x100, + ARG_DIFF, + ARG_VERSION + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "diff", optional_argument, NULL, ARG_DIFF }, + { "type", required_argument, NULL, 't' }, + {} + }; + + int c; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 't': { + int f; + f = parse_flags(optarg, arg_flags); + if (f < 0) { + log_error("Failed to parse flags field."); + return -EINVAL; + } + arg_flags = f; + break; + } + + case ARG_DIFF: + if (!optarg) + arg_diff = 1; + else { + int b; + + b = parse_boolean(optarg); + if (b < 0) { + log_error("Failed to parse diff boolean."); + return -EINVAL; + } + + arg_diff = b; + } + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, k, n_found = 0; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_flags == 0) + arg_flags = SHOW_DEFAULTS; + + if (arg_diff < 0) + arg_diff = !!(arg_flags & SHOW_OVERRIDDEN); + else if (arg_diff) + arg_flags |= SHOW_OVERRIDDEN; + + pager_open(arg_no_pager, false); + + if (optind < argc) { + int i; + + for (i = optind; i < argc; i++) { + path_kill_slashes(argv[i]); + + k = process_suffix_chop(argv[i]); + if (k < 0) + r = k; + else + n_found += k; + } + + } else { + k = process_suffixes(NULL); + if (k < 0) + r = k; + else + n_found += k; + } + + if (r >= 0) + printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found); + +finish: + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.bash b/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.bash new file mode 100644 index 0000000000..cb1732895f --- /dev/null +++ b/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.bash @@ -0,0 +1,61 @@ +# systemd-delta(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_systemd-delta() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local comps + + local -A OPTS=( + [STANDALONE]='--help -h --no-pager --version' + [ARG]='--diff --type -t' + ) + + _init_completion || return + + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --diff) + comps='yes no' + ;; + --type|-t) + comps='masked equivalent redirected overridden unchanged extended default' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd-delta systemd-delta diff --git a/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.zsh b/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.zsh new file mode 100644 index 0000000000..757f1b66fb --- /dev/null +++ b/src/grp-system/grp-utils/systemd-delta/systemd-delta.completion.zsh @@ -0,0 +1,15 @@ +#compdef systemd-delta + +_delta_type() { + local -a _delta_types + _delta_types=(masked equivalent redirected overridden unchanged) + _values -s , "${_delta_types[@]}" +} + +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--diff=[Show a diff when overridden files differ]:boolean:(1 0)' \ + {-t+,--type=}'[Only display a selected set of override types]:types:_delta_type' \ + ':SUFFIX:(tmpfiles.d sysctl.d systemd/system)' diff --git a/src/grp-system/grp-utils/systemd-delta/systemd-delta.xml b/src/grp-system/grp-utils/systemd-delta/systemd-delta.xml new file mode 100644 index 0000000000..99709604aa --- /dev/null +++ b/src/grp-system/grp-utils/systemd-delta/systemd-delta.xml @@ -0,0 +1,205 @@ + + + + + + + + + systemd-delta + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-delta + 1 + + + + systemd-delta + Find overridden configuration files + + + + + systemd-delta + OPTIONS + PREFIX/SUFFIX|SUFFIX + + + + + Description + + systemd-delta may be used to identify and + compare configuration files that override other configuration + files. Files in /etc have highest priority, + files in /run have the second highest + priority, ..., files in /lib have lowest + priority. Files in a directory with higher priority override files + with the same name in directories of lower priority. In addition, + certain configuration files can have .d + directories which contain "drop-in" files with configuration + snippets which augment the main configuration file. "Drop-in" + files can be overridden in the same way by placing files with the + same name in a directory of higher priority (except that, in case + of "drop-in" files, both the "drop-in" file name and the name of + the containing directory, which corresponds to the name of the + main configuration file, must match). For a fuller explanation, + see + systemd.unit5. + + + The command line argument will be split into a prefix and a + suffix. Either is optional. The prefix must be one of the + directories containing configuration files + (/etc, /run, + /usr/lib, ...). If it is given, only + overriding files contained in this directory will be shown. + Otherwise, all overriding files will be shown. The suffix must be + a name of a subdirectory containing configuration files like + tmpfiles.d, sysctl.d or + systemd/system. If it is given, only + configuration files in this subdirectory (across all configuration + paths) will be analyzed. Otherwise, all configuration files will + be analyzed. If the command line argument is not given at all, all + configuration files will be analyzed. See below for some + examples. + + + + Options + + The following options are understood: + + + + + + + When listing the differences, only list those + that are asked for. The list itself is a comma-separated list + of desired difference types. + + Recognized types are: + + + + masked + + Show masked files + + + + equivalent + + Show overridden files that while + overridden, do not differ in content. + + + + redirected + + Show files that are redirected to + another. + + + + overridden + + Show overridden, and changed + files. + + + + extended + + Show *.conf files + in drop-in directories for units. + + + + unchanged + + Show unmodified files + too. + + + + + + + + + When showing modified files, when a file is + overridden show a diff as well. This option takes a boolean + argument. If omitted, it defaults to + . + + + + + + + + + + Examples + + To see all local configuration: + systemd-delta + + To see all runtime configuration: + systemd-delta /run + + To see all system unit configuration changes: + systemd-delta systemd/system + + To see all runtime "drop-in" changes for system units: + systemd-delta --type=extended /run/systemd/system + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemd.unit5 + + + + diff --git a/src/grp-system/grp-utils/systemd-fstab-generator/Makefile b/src/grp-system/grp-utils/systemd-fstab-generator/Makefile new file mode 100644 index 0000000000..43475f69e1 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-fstab-generator/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemgenerator_PROGRAMS += systemd-fstab-generator +systemd_fstab_generator_SOURCES = \ + src/fstab-generator/fstab-generator.c \ + src/core/mount-setup.c + +systemd_fstab_generator_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-fstab-generator/fstab-generator.c b/src/grp-system/grp-utils/systemd-fstab-generator/fstab-generator.c new file mode 100644 index 0000000000..70866cacda --- /dev/null +++ b/src/grp-system/grp-utils/systemd-fstab-generator/fstab-generator.c @@ -0,0 +1,724 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/fstab-util.h" +#include "shared/generator.h" + +#include "mount-setup.h" + +static const char *arg_dest = "/tmp"; +static bool arg_fstab_enabled = true; +static char *arg_root_what = NULL; +static char *arg_root_fstype = NULL; +static char *arg_root_options = NULL; +static int arg_root_rw = -1; +static char *arg_usr_what = NULL; +static char *arg_usr_fstype = NULL; +static char *arg_usr_options = NULL; + +static int add_swap( + const char *what, + struct mntent *me, + bool noauto, + bool nofail) { + + _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(what); + assert(me); + + if (access("/proc/swaps", F_OK) < 0) { + log_info("Swap not supported, ignoring fstab swap entry for %s.", what); + return 0; + } + + if (detect_container() > 0) { + log_info("Running in a container, ignoring fstab swap entry for %s.", what); + return 0; + } + + r = unit_name_from_path(what, ".swap", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + unit = strjoin(arg_dest, "/", name, NULL); + if (!unit) + return log_oom(); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, + errno == EEXIST ? + "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : + "Failed to create unit file %s: %m", + unit); + + fprintf(f, + "# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "SourcePath=/etc/fstab\n" + "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" + "[Swap]\n" + "What=%s\n", + what); + + if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults")) + fprintf(f, "Options=%s\n", me->mnt_opts); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + /* use what as where, to have a nicer error message */ + r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); + if (r < 0) + return r; + + if (!noauto) { + lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET, + nofail ? ".wants/" : ".requires/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + } + + return 0; +} + +static bool mount_is_network(struct mntent *me) { + assert(me); + + return fstab_test_option(me->mnt_opts, "_netdev\0") || + fstype_is_network(me->mnt_type); +} + +static bool mount_in_initrd(struct mntent *me) { + assert(me); + + return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") || + streq(me->mnt_dir, "/usr"); +} + +static int write_idle_timeout(FILE *f, const char *where, const char *opts) { + _cleanup_free_ char *timeout = NULL; + char timespan[FORMAT_TIMESPAN_MAX]; + usec_t u; + int r; + + r = fstab_filter_options(opts, "x-systemd.idle-timeout\0", NULL, &timeout, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to parse options: %m"); + if (r == 0) + return 0; + + r = parse_sec(timeout, &u); + if (r < 0) { + log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); + return 0; + } + + fprintf(f, "TimeoutIdleSec=%s\n", format_timespan(timespan, sizeof(timespan), u, 0)); + + return 0; +} + +static int write_requires_after(FILE *f, const char *opts) { + _cleanup_strv_free_ char **names = NULL, **units = NULL; + _cleanup_free_ char *res = NULL; + char **s; + int r; + + assert(f); + assert(opts); + + r = fstab_extract_values(opts, "x-systemd.requires", &names); + if (r < 0) + return log_warning_errno(r, "Failed to parse options: %m"); + if (r == 0) + return 0; + + STRV_FOREACH(s, names) { + char *x; + + r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".mount", &x); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + r = strv_consume(&units, x); + if (r < 0) + return log_oom(); + } + + if (units) { + res = strv_join(units, " "); + if (!res) + return log_oom(); + fprintf(f, "After=%1$s\nRequires=%1$s\n", res); + } + + return 0; +} + +static int write_requires_mounts_for(FILE *f, const char *opts) { + _cleanup_strv_free_ char **paths = NULL; + _cleanup_free_ char *res = NULL; + int r; + + assert(f); + assert(opts); + + r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths); + if (r < 0) + return log_warning_errno(r, "Failed to parse options: %m"); + if (r == 0) + return 0; + + res = strv_join(paths, " "); + if (!res) + return log_oom(); + + fprintf(f, "RequiresMountsFor=%s\n", res); + + return 0; +} + +static int add_mount( + const char *what, + const char *where, + const char *fstype, + const char *opts, + int passno, + bool noauto, + bool nofail, + bool automount, + const char *post, + const char *source) { + + _cleanup_free_ char + *name = NULL, *unit = NULL, *lnk = NULL, + *automount_name = NULL, *automount_unit = NULL, + *filtered = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(what); + assert(where); + assert(opts); + assert(post); + assert(source); + + if (streq_ptr(fstype, "autofs")) + return 0; + + if (!is_path(where)) { + log_warning("Mount point %s is not a valid path, ignoring.", where); + return 0; + } + + if (mount_point_is_api(where) || + mount_point_ignore(where)) + return 0; + + if (path_equal(where, "/")) { + if (noauto) + log_warning("Ignoring \"noauto\" for root device"); + if (nofail) + log_warning("Ignoring \"nofail\" for root device"); + if (automount) + log_warning("Ignoring automount option for root device"); + + noauto = nofail = automount = false; + } + + r = unit_name_from_path(where, ".mount", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + unit = strjoin(arg_dest, "/", name, NULL); + if (!unit) + return log_oom(); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, + errno == EEXIST ? + "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : + "Failed to create unit file %s: %m", + unit); + + fprintf(f, + "# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "SourcePath=%s\n" + "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", + source); + + if (!noauto && !nofail && !automount) + fprintf(f, "Before=%s\n", post); + + if (!automount && opts) { + r = write_requires_after(f, opts); + if (r < 0) + return r; + r = write_requires_mounts_for(f, opts); + if (r < 0) + return r; + } + + if (passno != 0) { + r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); + if (r < 0) + return r; + } + + fprintf(f, + "\n" + "[Mount]\n" + "What=%s\n" + "Where=%s\n", + what, + where); + + if (!isempty(fstype) && !streq(fstype, "auto")) + fprintf(f, "Type=%s\n", fstype); + + r = generator_write_timeouts(arg_dest, what, where, opts, &filtered); + if (r < 0) + return r; + + if (!isempty(filtered) && !streq(filtered, "defaults")) + fprintf(f, "Options=%s\n", filtered); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + if (!noauto && !automount) { + lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + } + + if (automount) { + r = unit_name_from_path(where, ".automount", &automount_name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + automount_unit = strjoin(arg_dest, "/", automount_name, NULL); + if (!automount_unit) + return log_oom(); + + fclose(f); + f = fopen(automount_unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit); + + fprintf(f, + "# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "SourcePath=%s\n" + "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", + source); + + fprintf(f, "Before=%s\n", post); + + if (opts) { + r = write_requires_after(f, opts); + if (r < 0) + return r; + r = write_requires_mounts_for(f, opts); + if (r < 0) + return r; + } + + fprintf(f, + "\n" + "[Automount]\n" + "Where=%s\n", + where); + + r = write_idle_timeout(f, where, opts); + if (r < 0) + return r; + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); + + free(lnk); + lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(automount_unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + } + + return 0; +} + +static int parse_fstab(bool initrd) { + _cleanup_endmntent_ FILE *f = NULL; + const char *fstab_path; + struct mntent *me; + int r = 0; + + fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab"; + f = setmntent(fstab_path, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open %s: %m", fstab_path); + } + + while ((me = getmntent(f))) { + _cleanup_free_ char *where = NULL, *what = NULL; + bool noauto, nofail; + int k; + + if (initrd && !mount_in_initrd(me)) + continue; + + what = fstab_node_to_udev_node(me->mnt_fsname); + if (!what) + return log_oom(); + + if (is_device_path(what) && path_is_read_only_fs("sys") > 0) { + log_info("Running in a container, ignoring fstab device entry for %s.", what); + continue; + } + + where = initrd ? strappend("/sysroot/", me->mnt_dir) : strdup(me->mnt_dir); + if (!where) + return log_oom(); + + if (is_path(where)) + path_kill_slashes(where); + + noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); + nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); + log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s", + what, where, me->mnt_type, + yes_no(noauto), yes_no(nofail)); + + if (streq(me->mnt_type, "swap")) + k = add_swap(what, me, noauto, nofail); + else { + bool automount; + const char *post; + + automount = fstab_test_option(me->mnt_opts, + "comment=systemd.automount\0" + "x-systemd.automount\0"); + if (initrd) + post = SPECIAL_INITRD_FS_TARGET; + else if (mount_is_network(me)) + post = SPECIAL_REMOTE_FS_TARGET; + else + post = SPECIAL_LOCAL_FS_TARGET; + + k = add_mount(what, + where, + me->mnt_type, + me->mnt_opts, + me->mnt_passno, + noauto, + nofail, + automount, + post, + fstab_path); + } + + if (k < 0) + r = k; + } + + return r; +} + +static int add_sysroot_mount(void) { + _cleanup_free_ char *what = NULL; + const char *opts; + int r; + + if (isempty(arg_root_what)) { + log_debug("Could not find a root= entry on the kernel command line."); + return 0; + } + + if (streq(arg_root_what, "gpt-auto")) { + /* This is handled by the gpt-auto generator */ + log_debug("Skipping root directory handling, as gpt-auto was requested."); + return 0; + } + + if (path_equal(arg_root_what, "/dev/nfs")) { + /* This is handled by the kernel or the initrd */ + log_debug("Skipping root directory handling, as /dev/nfs was requested."); + return 0; + } + + what = fstab_node_to_udev_node(arg_root_what); + if (!what) + return log_oom(); + + if (!arg_root_options) + opts = arg_root_rw > 0 ? "rw" : "ro"; + else if (arg_root_rw >= 0 || + !fstab_test_option(arg_root_options, "ro\0" "rw\0")) + opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro"); + else + opts = arg_root_options; + + log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); + + if (is_device_path(what)) { + r = generator_write_initrd_root_device_deps(arg_dest, what); + if (r < 0) + return r; + } + + return add_mount(what, + "/sysroot", + arg_root_fstype, + opts, + is_device_path(what) ? 1 : 0, /* passno */ + false, /* noauto off */ + false, /* nofail off */ + false, /* automount off */ + SPECIAL_INITRD_ROOT_FS_TARGET, + "/proc/cmdline"); +} + +static int add_sysroot_usr_mount(void) { + _cleanup_free_ char *what = NULL; + const char *opts; + + if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options) + return 0; + + if (arg_root_what && !arg_usr_what) { + /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */ + arg_usr_what = strdup(arg_root_what); + if (!arg_usr_what) + return log_oom(); + } + + if (arg_root_fstype && !arg_usr_fstype) { + arg_usr_fstype = strdup(arg_root_fstype); + if (!arg_usr_fstype) + return log_oom(); + } + + if (arg_root_options && !arg_usr_options) { + arg_usr_options = strdup(arg_root_options); + if (!arg_usr_options) + return log_oom(); + } + + if (!arg_usr_what) + return 0; + + what = fstab_node_to_udev_node(arg_usr_what); + if (!what) + return log_oom(); + + if (!arg_usr_options) + opts = arg_root_rw > 0 ? "rw" : "ro"; + else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0")) + opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro"); + else + opts = arg_usr_options; + + log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype)); + return add_mount(what, + "/sysroot/usr", + arg_usr_fstype, + opts, + is_device_path(what) ? 1 : 0, /* passno */ + false, /* noauto off */ + false, /* nofail off */ + false, /* automount off */ + SPECIAL_INITRD_FS_TARGET, + "/proc/cmdline"); +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last + * instance should take precedence. In the case of multiple rootflags= + * or usrflags= the arguments should be concatenated */ + + if (STR_IN_SET(key, "fstab", "rd.fstab") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse fstab switch %s. Ignoring.", value); + else + arg_fstab_enabled = r; + + } else if (streq(key, "root") && value) { + + if (free_and_strdup(&arg_root_what, value) < 0) + return log_oom(); + + } else if (streq(key, "rootfstype") && value) { + + if (free_and_strdup(&arg_root_fstype, value) < 0) + return log_oom(); + + } else if (streq(key, "rootflags") && value) { + char *o; + + o = arg_root_options ? + strjoin(arg_root_options, ",", value, NULL) : + strdup(value); + if (!o) + return log_oom(); + + free(arg_root_options); + arg_root_options = o; + + } else if (streq(key, "mount.usr") && value) { + + if (free_and_strdup(&arg_usr_what, value) < 0) + return log_oom(); + + } else if (streq(key, "mount.usrfstype") && value) { + + if (free_and_strdup(&arg_usr_fstype, value) < 0) + return log_oom(); + + } else if (streq(key, "mount.usrflags") && value) { + char *o; + + o = arg_usr_options ? + strjoin(arg_usr_options, ",", value, NULL) : + strdup(value); + if (!o) + return log_oom(); + + free(arg_usr_options); + arg_usr_options = o; + + } else if (streq(key, "rw") && !value) + arg_root_rw = true; + else if (streq(key, "ro") && !value) + arg_root_rw = false; + + return 0; +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + /* Always honour root= and usr= in the kernel command line if we are in an initrd */ + if (in_initrd()) { + int k; + + r = add_sysroot_mount(); + + k = add_sysroot_usr_mount(); + if (k < 0) + r = k; + } else + r = 0; + + /* Honour /etc/fstab only when that's enabled */ + if (arg_fstab_enabled) { + int k; + + log_debug("Parsing /etc/fstab"); + + /* Parse the local /etc/fstab, possibly from the initrd */ + k = parse_fstab(false); + if (k < 0) + r = k; + + /* If running in the initrd also parse the /etc/fstab from the host */ + if (in_initrd()) { + log_debug("Parsing /sysroot/etc/fstab"); + + k = parse_fstab(true); + if (k < 0) + r = k; + } + } + + free(arg_root_what); + free(arg_root_fstype); + free(arg_root_options); + + free(arg_usr_what); + free(arg_usr_fstype); + free(arg_usr_options); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.c b/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.c new file mode 120000 index 0000000000..7f7ff15b46 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.c @@ -0,0 +1 @@ +../../libcore/mount-setup.c \ No newline at end of file diff --git a/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.h b/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.h new file mode 120000 index 0000000000..50721d8bfc --- /dev/null +++ b/src/grp-system/grp-utils/systemd-fstab-generator/mount-setup.h @@ -0,0 +1 @@ +../../libcore/mount-setup.h \ No newline at end of file diff --git a/src/grp-system/grp-utils/systemd-fstab-generator/systemd-fstab-generator.xml b/src/grp-system/grp-utils/systemd-fstab-generator/systemd-fstab-generator.xml new file mode 100644 index 0000000000..a971cb3675 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-fstab-generator/systemd-fstab-generator.xml @@ -0,0 +1,183 @@ + + + + + + + + systemd-fstab-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-fstab-generator + 8 + + + + systemd-fstab-generator + Unit generator for /etc/fstab + + + + /usr/lib/systemd/system-generators/systemd-fstab-generator + + + + Description + + systemd-fstab-generator is a generator + that translates /etc/fstab (see + fstab5 + for details) into native systemd units early at boot and when + configuration of the system manager is reloaded. This will + instantiate mount and swap units as necessary. + + The passno field is treated like a simple + boolean, and the ordering information is discarded. However, if + the root file system is checked, it is checked before all the + other file systems. + + See + systemd.mount5 + and + systemd.swap5 + for more information about special /etc/fstab + mount options this generator understands. + + systemd-fstab-generator implements + systemd.generator7. + + + + Kernel Command Line + + systemd-fstab-generator understands the + following kernel command line parameters: + + + + + fstab= + rd.fstab= + + Takes a boolean argument. Defaults to + yes. If no, causes the + generator to ignore any mounts or swaps configured in + /etc/fstab. rd.fstab= + is honored only by initial RAM disk (initrd) while + fstab= is honored by both the main system + and the initrd. + + + root= + + Takes the root filesystem to mount in the + initrd. root= is honored by the + initrd. + + + rootfstype= + + Takes the root filesystem type that will be + passed to the mount command. rootfstype= is + honored by the initrd. + + + rootflags= + + Takes the root filesystem mount options to + use. rootflags= is honored by the + initrd. + + + mount.usr= + + Takes the /usr filesystem + to be mounted by the initrd. If + mount.usrfstype= or + mount.usrflags= is set, then + mount.usr= will default to the value set in + root=. + + Otherwise, this parameter defaults to the + /usr entry found in + /etc/fstab on the root filesystem. + + mount.usr= is honored by the initrd. + + + + mount.usrfstype= + + Takes the /usr filesystem + type that will be passed to the mount command. If + mount.usr= or + mount.usrflags= is set, then + mount.usrfstype= will default to the value + set in rootfstype=. + + Otherwise, this value will be read from the + /usr entry in + /etc/fstab on the root filesystem. + + mount.usrfstype= is honored by the + initrd. + + + mount.usrflags= + + Takes the /usr filesystem + mount options to use. If mount.usr= or + mount.usrfstype= is set, then + mount.usrflags= will default to the value + set in rootflags=. + + Otherwise, this value will be read from the + /usr entry in + /etc/fstab on the root filesystem. + + mount.usrflags= is honored by the + initrd. + + + + + + See Also + + systemd1, + fstab5, + systemd.mount5, + systemd.swap5, + systemd-cryptsetup-generator8 + + + + diff --git a/src/grp-system/grp-utils/systemd-run/Makefile b/src/grp-system/grp-utils/systemd-run/Makefile new file mode 100644 index 0000000000..9664591eb6 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-run/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-run +systemd_run_SOURCES = \ + src/run/run.c + +systemd_run_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-run/run.c b/src/grp-system/grp-utils/systemd-run/run.c new file mode 100644 index 0000000000..4aaf446177 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-run/run.c @@ -0,0 +1,1261 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/calendarspec.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" +#include "shared/ptyfwd.h" +#include "shared/spawn-polkit-agent.h" + +static bool arg_ask_password = true; +static bool arg_scope = false; +static bool arg_remain_after_exit = false; +static bool arg_no_block = false; +static const char *arg_unit = NULL; +static const char *arg_description = NULL; +static const char *arg_slice = NULL; +static bool arg_send_sighup = false; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static const char *arg_host = NULL; +static bool arg_user = false; +static const char *arg_service_type = NULL; +static const char *arg_exec_user = NULL; +static const char *arg_exec_group = NULL; +static int arg_nice = 0; +static bool arg_nice_set = false; +static char **arg_environment = NULL; +static char **arg_property = NULL; +static bool arg_pty = false; +static usec_t arg_on_active = 0; +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 const char *arg_on_calendar = NULL; +static char **arg_timer_property = NULL; +static bool arg_quiet = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +static void help(void) { + printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" + "Run the specified command in a transient scope or service or timer\n" + "unit. If a timer option is specified and the unit specified with\n" + "the --unit option exists, the command can be omitted.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" + " --user Run as user unit\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --scope Run this as scope rather than service\n" + " --unit=UNIT Run under the specified unit name\n" + " -p --property=NAME=VALUE Set unit property\n" + " --description=TEXT Description for unit\n" + " --slice=SLICE Run in the specified slice\n" + " --no-block Do not wait until operation finished\n" + " -r --remain-after-exit Leave service around until explicitly stopped\n" + " --send-sighup Send SIGHUP when terminating\n" + " --service-type=TYPE Service type\n" + " --uid=USER Run as system user\n" + " --gid=GROUP Run as system group\n" + " --nice=NICE Nice level\n" + " -E --setenv=NAME=VALUE Set environment\n" + " -t --pty Run service on pseudo tty\n" + " -q --quiet Suppress information messages during runtime\n\n" + "Timer options:\n\n" + " --on-active=SECONDS Run after SECONDS delay\n" + " --on-boot=SECONDS Run SECONDS after machine was booted up\n" + " --on-startup=SECONDS Run SECONDS after systemd activation\n" + " --on-unit-active=SECONDS Run SECONDS after the last activation\n" + " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" + " --on-calendar=SPEC Realtime timer\n" + " --timer-property=NAME=VALUE Set timer unit property\n", + program_invocation_short_name); +} + +static bool with_timer(void) { + return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_USER, + ARG_SYSTEM, + ARG_SCOPE, + ARG_UNIT, + ARG_DESCRIPTION, + ARG_SLICE, + ARG_SEND_SIGHUP, + ARG_SERVICE_TYPE, + ARG_EXEC_USER, + ARG_EXEC_GROUP, + ARG_NICE, + ARG_ON_ACTIVE, + ARG_ON_BOOT, + ARG_ON_STARTUP, + ARG_ON_UNIT_ACTIVE, + ARG_ON_UNIT_INACTIVE, + ARG_ON_CALENDAR, + ARG_TIMER_PROPERTY, + ARG_NO_BLOCK, + ARG_NO_ASK_PASSWORD, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "scope", no_argument, NULL, ARG_SCOPE }, + { "unit", required_argument, NULL, ARG_UNIT }, + { "description", required_argument, NULL, ARG_DESCRIPTION }, + { "slice", required_argument, NULL, ARG_SLICE }, + { "remain-after-exit", no_argument, NULL, 'r' }, + { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, + { "uid", required_argument, NULL, ARG_EXEC_USER }, + { "gid", required_argument, NULL, ARG_EXEC_GROUP }, + { "nice", required_argument, NULL, ARG_NICE }, + { "setenv", required_argument, NULL, 'E' }, + { "property", required_argument, NULL, 'p' }, + { "tty", no_argument, NULL, 't' }, /* deprecated */ + { "pty", no_argument, NULL, 't' }, + { "quiet", no_argument, NULL, 'q' }, + { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, + { "on-boot", required_argument, NULL, ARG_ON_BOOT }, + { "on-startup", required_argument, NULL, ARG_ON_STARTUP }, + { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE }, + { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, + { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, + { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + {}, + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case ARG_VERSION: + return version(); + + case ARG_USER: + arg_user = true; + break; + + case ARG_SYSTEM: + arg_user = false; + break; + + case ARG_SCOPE: + arg_scope = true; + break; + + case ARG_UNIT: + arg_unit = optarg; + break; + + case ARG_DESCRIPTION: + arg_description = optarg; + break; + + case ARG_SLICE: + arg_slice = optarg; + break; + + case ARG_SEND_SIGHUP: + arg_send_sighup = true; + break; + + case 'r': + arg_remain_after_exit = true; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_SERVICE_TYPE: + arg_service_type = optarg; + break; + + case ARG_EXEC_USER: + arg_exec_user = optarg; + break; + + case ARG_EXEC_GROUP: + arg_exec_group = optarg; + break; + + case ARG_NICE: + r = safe_atoi(optarg, &arg_nice); + if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) { + log_error("Failed to parse nice value"); + return -EINVAL; + } + + arg_nice_set = true; + break; + + case 'E': + if (strv_extend(&arg_environment, optarg) < 0) + return log_oom(); + + break; + + case 'p': + if (strv_extend(&arg_property, optarg) < 0) + return log_oom(); + + break; + + case 't': + arg_pty = true; + break; + + case 'q': + arg_quiet = true; + break; + + case ARG_ON_ACTIVE: + + r = parse_sec(optarg, &arg_on_active); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_BOOT: + + r = parse_sec(optarg, &arg_on_boot); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_STARTUP: + + r = parse_sec(optarg, &arg_on_startup); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_UNIT_ACTIVE: + + r = parse_sec(optarg, &arg_on_unit_active); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_UNIT_INACTIVE: + + r = parse_sec(optarg, &arg_on_unit_inactive); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + 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; + } + + calendar_spec_free(spec); + arg_on_calendar = optarg; + break; + } + + case ARG_TIMER_PROPERTY: + + if (strv_extend(&arg_timer_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_NO_BLOCK: + arg_no_block = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if ((optind >= argc) && (!arg_unit || !with_timer())) { + log_error("Command line to execute required."); + return -EINVAL; + } + + if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Execution in user context is not supported on non-local systems."); + return -EINVAL; + } + + if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Scope execution is not supported on non-local systems."); + return -EINVAL; + } + + if (arg_scope && (arg_remain_after_exit || arg_service_type)) { + log_error("--remain-after-exit and --service-type= are not supported in --scope mode."); + return -EINVAL; + } + + if (arg_pty && (with_timer() || arg_scope)) { + log_error("--pty is not compatible in timer or --scope mode."); + 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; + } + + if (arg_timer_property && !with_timer()) { + log_error("--timer-property= has no effect without any other timer options."); + return -EINVAL; + } + + return 1; +} + +static int transient_unit_set_properties(sd_bus_message *m, char **properties) { + char **i; + int r; + + r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); + if (r < 0) + return r; + + STRV_FOREACH(i, properties) { + r = bus_append_unit_property_assignment(m, *i); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_cgroup_set_properties(sd_bus_message *m) { + int r; + assert(m); + + if (!isempty(arg_slice)) { + _cleanup_free_ char *slice; + + r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_kill_set_properties(sd_bus_message *m) { + assert(m); + + if (arg_send_sighup) + return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup); + else + return 0; +} + +static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_property); + if (r < 0) + return r; + + r = transient_kill_set_properties(m); + if (r < 0) + return r; + + r = transient_cgroup_set_properties(m); + if (r < 0) + return r; + + if (arg_remain_after_exit) { + r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit); + if (r < 0) + return r; + } + + if (arg_service_type) { + r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type); + if (r < 0) + return r; + } + + if (arg_exec_user) { + r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user); + if (r < 0) + return r; + } + + if (arg_exec_group) { + r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group); + if (r < 0) + return r; + } + + if (arg_nice_set) { + r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); + if (r < 0) + return r; + } + + if (pty_path) { + const char *e; + + r = sd_bus_message_append(m, + "(sv)(sv)(sv)(sv)", + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_path); + if (r < 0) + return r; + + e = getenv("TERM"); + if (e) { + char *n; + + n = strjoina("TERM=", e); + r = sd_bus_message_append(m, + "(sv)", + "Environment", "as", 1, n); + if (r < 0) + return r; + } + } + + if (!strv_isempty(arg_environment)) { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, arg_environment); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + /* Exec container */ + { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", argv[0]); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, argv); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "b", false); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_scope_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_property); + if (r < 0) + return r; + + r = transient_kill_set_properties(m); + if (r < 0) + return r; + + r = transient_cgroup_set_properties(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); + if (r < 0) + return r; + + return 0; +} + +static int transient_timer_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, arg_timer_property); + 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) + return r; + } + + if (arg_on_boot) { + r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot); + if (r < 0) + return r; + } + + if (arg_on_startup) { + r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup); + if (r < 0) + return r; + } + + if (arg_on_unit_active) { + r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active); + if (r < 0) + return r; + } + + if (arg_on_unit_inactive) { + r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive); + if (r < 0) + return r; + } + + if (arg_on_calendar) { + r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar); + if (r < 0) + return r; + } + + 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_(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; + int r; + + assert(bus); + assert(argv); + + if (arg_pty) { + + if (arg_transport == BUS_TRANSPORT_LOCAL) { + master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); + if (master < 0) + return log_error_errno(errno, "Failed to acquire pseudo tty: %m"); + + r = ptsname_malloc(master, &pty_path); + if (r < 0) + return log_error_errno(r, "Failed to determine tty name: %m"); + + if (unlockpt(master) < 0) + return log_error_errno(errno, "Failed to unlock tty: %m"); + + } else if (arg_transport == BUS_TRANSPORT_MACHINE) { + _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL; + const char *s; + + r = sd_bus_default_system(&system_bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); + + r = sd_bus_call_method(system_bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachinePTY", + &error, + &pty_reply, + "s", arg_host); + if (r < 0) { + log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(pty_reply, "hs", &master, &s); + if (r < 0) + return bus_log_parse_error(r); + + master = fcntl(master, F_DUPFD_CLOEXEC, 3); + if (master < 0) + return log_error_errno(errno, "Failed to duplicate master fd: %m"); + + pty_path = strdup(s); + if (!pty_path) + return log_oom(); + } else + assert_not_reached("Can't allocate tty via ssh"); + } + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + if (arg_unit) { + 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 { + r = make_unit_name(bus, UNIT_SERVICE, &service); + if (r < 0) + return r; + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and mode */ + r = sd_bus_message_append(m, "ss", service, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_service_set_properties(m, argv, pty_path); + 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); + + /* Auxiliary units */ + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return bus_log_create_error(r); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + 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; + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + } + + if (master >= 0) { + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + char last_char = 0; + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get event loop: %m"); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + if (!arg_quiet) + log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service); + + 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"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(forward, &last_char); + + forward = pty_forward_free(forward); + + if (!arg_quiet && last_char != '\n') + fputc('\n', stdout); + + } else if (!arg_quiet) + log_info("Running as unit: %s", service); + + return 0; +} + +static int start_transient_scope( + sd_bus *bus, + char **argv) { + + _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; + const char *object = NULL; + int r; + + assert(bus); + assert(argv); + + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_oom(); + + if (arg_unit) { + 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 { + r = make_unit_name(bus, UNIT_SCOPE, &scope); + if (r < 0) + return r; + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and Mode */ + r = sd_bus_message_append(m, "ss", scope, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_scope_set_properties(m); + 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); + + /* Auxiliary units */ + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return bus_log_create_error(r); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); + return r; + } + + if (arg_nice_set) { + if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) + return log_error_errno(errno, "Failed to set nice level: %m"); + } + + if (arg_exec_group) { + gid_t gid; + + r = get_group_creds(&arg_exec_group, &gid); + if (r < 0) + return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); + + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); + } + + if (arg_exec_user) { + const char *home, *shell; + uid_t uid; + gid_t gid; + + r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell); + if (r < 0) + return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); + + r = strv_extendf(&user_env, "HOME=%s", home); + if (r < 0) + return log_oom(); + + r = strv_extendf(&user_env, "SHELL=%s", shell); + if (r < 0) + return log_oom(); + + r = strv_extendf(&user_env, "USER=%s", arg_exec_user); + if (r < 0) + return log_oom(); + + r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user); + if (r < 0) + return log_oom(); + + if (!arg_exec_group) { + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); + } + + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); + } + + env = strv_env_merge(3, environ, user_env, arg_environment); + if (!env) + return log_oom(); + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + + if (!arg_quiet) + log_info("Running scope as unit: %s", scope); + + execvpe(argv[0], argv, env); + + return log_error_errno(errno, "Failed to execute: %m"); +} + +static int start_transient_timer( + sd_bus *bus, + char **argv) { + + _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; + int r; + + assert(bus); + assert(argv); + + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_oom(); + + if (arg_unit) { + switch (unit_name_to_type(arg_unit)) { + + case UNIT_SERVICE: + service = strdup(arg_unit); + if (!service) + return log_oom(); + + r = unit_name_change_suffix(service, ".timer", &timer); + if (r < 0) + return log_error_errno(r, "Failed to change unit suffix: %m"); + break; + + case UNIT_TIMER: + timer = strdup(arg_unit); + if (!timer) + return log_oom(); + + r = unit_name_change_suffix(timer, ".service", &service); + if (r < 0) + return log_error_errno(r, "Failed to change unit suffix: %m"); + break; + + default: + 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"); + + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + + break; + } + } 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, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and Mode */ + r = sd_bus_message_append(m, "ss", timer, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_timer_set_properties(m); + 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_open_container(m, 'a', "(sa(sv))"); + if (r < 0) + return bus_log_create_error(r); + + if (argv[0]) { + r = sd_bus_message_open_container(m, 'r', "sa(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", service); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_service_set_properties(m, argv, NULL); + 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); + 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); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + + log_info("Running timer as unit: %s", timer); + if (argv[0]) + log_info("Will run service as unit: %s", service); + + return 0; +} + +int main(int argc, char* argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *description = NULL, *command = NULL; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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: %m", argv[optind]); + goto finish; + } + + argv[optind] = command; + } + + if (!arg_description) { + description = strv_join(argv + optind, " "); + if (!description) { + r = log_oom(); + goto finish; + } + + if (arg_unit && isempty(description)) { + r = free_and_strdup(&description, arg_unit); + if (r < 0) + goto finish; + } + + arg_description = description; + } + + 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; + } + + if (arg_scope) + r = start_transient_scope(bus, argv + optind); + else if (with_timer()) + r = start_transient_timer(bus, argv + optind); + else + r = start_transient_service(bus, argv + optind); + +finish: + strv_free(arg_environment); + strv_free(arg_property); + strv_free(arg_timer_property); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-system/grp-utils/systemd-run/systemd-run.completion.bash b/src/grp-system/grp-utils/systemd-run/systemd-run.completion.bash new file mode 100644 index 0000000000..022331e6a9 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-run/systemd-run.completion.bash @@ -0,0 +1,117 @@ +# systemd-run(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__systemctl() { + local mode=$1; shift 1 + systemctl $mode --full --no-legend "$@" +} + +__get_slice_units () { __systemctl $1 list-units --all -t slice \ + | { while read -r a b c d; do echo " $a"; done; }; } + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +_systemd_run() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --user --system --scope --unit --description --slice + -r --remain-after-exit --send-sighup -H --host -M --machine --service-type + --on-active --on-boot --on-startup --on-unit-active --on-unit-inactive + --on-calendar --timer-property -t --pty -q --quiet --no-block + --uid --gid --nice --setenv -p --property --no-ask-password' + + local mode=--system + local i + local opts_with_values=( + --unit --description --slice --service-type -H --host -M --machine -p --property --on-active + --on-boot --on-startup --on-unit-active --on-unit-inactive --on-calendar --timer-property + ) + for (( i=1; i <= COMP_CWORD; i++ )); do + if [[ ${COMP_WORDS[i]} != -* ]]; then + local root_command=${COMP_WORDS[i]} + _command_offset $i + return + fi + + [[ ${COMP_WORDS[i]} == "--user" ]] && mode=--user + + [[ $i -lt $COMP_CWORD && " ${opts_with_values[@]} " =~ " ${COMP_WORDS[i]} " ]] && ((i++)) + done + + case "$prev" in + --unit|--description|--on-active|--on-boot|--on-startup|--on-unit-active|--on-unit-inactive|--on-calendar) + # argument required but no completions available + return + ;; + --slice) + local comps=$(__get_slice_units $mode) + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + --service-type) + local comps='simple forking oneshot dbus notify idle' + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + -p|--property) + local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= + SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= + DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= + BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= + KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= + LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= + LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= + LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= + PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory= + TTYPath= SyslogIdentifier= SyslogLevelPrefix= SyslogLevel= + SyslogFacility= TimerSlackNSec= OOMScoreAdjust= ReadWritePaths= + ReadOnlyPaths= InaccessiblePaths= EnvironmentFile= + ProtectSystem= ProtectHome= RuntimeDirectory= PassEnvironment=' + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + -H|--host) + local comps=$(compgen -A hostname) + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + -M|--machine) + local comps=$( __get_machines ) + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + --timer-property) + local comps='AccuracySec= WakeSystem=' + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + esac + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 +} + +complete -F _systemd_run systemd-run diff --git a/src/grp-system/grp-utils/systemd-run/systemd-run.completion.zsh b/src/grp-system/grp-utils/systemd-run/systemd-run.completion.zsh new file mode 100644 index 0000000000..6362b97766 --- /dev/null +++ b/src/grp-system/grp-utils/systemd-run/systemd-run.completion.zsh @@ -0,0 +1,60 @@ +#compdef systemd-run + +__systemctl() { + local -a _modes + _modes=("--user" "--system") + systemctl ${words:*_modes} --full --no-legend --no-pager "$@" 2>/dev/null +} + +__get_slices () { + __systemctl list-units --all -t slice \ + | { while read -r a b; do echo $a; done; }; +} + +__slices () { + local -a _slices + _slices=(${(fo)"$(__get_slices)"}) + typeset -U _slices + _describe 'slices' _slices +} + +_arguments \ + {-h,--help}'[Show help message]' \ + '--version[Show package version]' \ + '--user[Run as user unit]' \ + {-H+,--host=}'[Operate on remote host]:[user@]host:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \ + '--scope[Run this as scope rather than service]' \ + '--unit=[Run under the specified unit name]:unit name' \ + {-p+,--property=}'[Set unit property]:NAME=VALUE:(( \ + CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \ + SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \ + DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \ + BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \ + KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= \ + LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \ + LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= \ + LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= \ + PrivateNetwork= NoNewPrivileges= WorkingDirectory= RootDirectory= \ + TTYPath= SyslogIdentifier= SyslogLevelPrefix= SyslogLevel= \ + SyslogFacility= TimerSlackNSec= OOMScoreAdjust= ReadWritePaths= \ + ReadOnlyPaths= InaccessiblePaths= EnvironmentFile= \ + ProtectSystem= ProtectHome= RuntimeDirectory= PassEnvironment= \ + ))' \ + '--description=[Description for unit]:description' \ + '--slice=[Run in the specified slice]:slices:__slices' \ + {-r,--remain-after-exit}'[Leave service around until explicitly stopped]' \ + '--send-sighup[Send SIGHUP when terminating]' \ + '--service-type=[Service type]:type:(simple forking oneshot dbus notify idle)' \ + '--uid=[Run as system user]:user:_users' \ + '--gid=[Run as system group]:group:_groups' \ + '--nice=[Nice level]:nice level' \ + '--setenv=[Set environment]:NAME=VALUE' \ + '--on-active=[Run after SEC seconds]:SEC' \ + '--on-boot=[Run after SEC seconds from machine was booted up]:SEC' \ + '--on-statup=[Run after SEC seconds from systemd was first started]:SEC' \ + '--on-unit-active=[Run after SEC seconds from the last activation]:SEC' \ + '--on-unit-inactive=[Run after SEC seconds from the last deactivation]:SEC' \ + '--on-calendar=[Realtime timer]:SPEC' \ + '--timer-property=[Set timer unit property]:NAME=VALUE' \ + '*::command:_command' diff --git a/src/grp-system/grp-utils/systemd-run/systemd-run.xml b/src/grp-system/grp-utils/systemd-run/systemd-run.xml new file mode 100644 index 0000000000..9c1a29218e --- /dev/null +++ b/src/grp-system/grp-utils/systemd-run/systemd-run.xml @@ -0,0 +1,459 @@ + + + + + + + + + systemd-run + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-run + 1 + + + + systemd-run + Run programs in transient scope or service or timer units + + + + + systemd-run + OPTIONS + COMMAND + ARGS + + + + systemd-run + OPTIONS + TIMER OPTIONS + COMMAND + ARGS + + + + + Description + + systemd-run may be used to create and + start a transient .service or + .scope unit and run the specified + COMMAND in it. It may also be used to + create and start transient .timer + units. + + If a command is run as transient service unit, it will be + started and managed by the service manager like any other service, + and thus shows up in the output of systemctl + list-units like any other unit. It will run in a clean + and detached execution environment, with the service manager as + its parent process. In this mode, systemd-run + will start the service asynchronously in the background and return + after the command has begun execution. + + If a command is run as transient scope unit, it will be + started by systemd-run itself as parent process + and will thus inherit the execution environment of the + caller. However, the processes of the command are managed by the + service manager similar to normal services, and will show up in + the output of systemctl list-units. Execution + in this case is synchronous, and will return only when the command + finishes. This mode is enabled via the + switch (see below). + + If a command is run with timer options such as + (see below), a transient timer + unit is created alongside the service unit for the specified + command. Only the transient timer unit is started immediately, the + transient service unit will be started when the transient timer + elapses. If the is specified, the + COMMAND may be omitted. In this case, + systemd-run only creates a + .timer unit that invokes the specified unit + when elapsing. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + + Create a transient .scope unit instead of + the default transient .service unit. + + + + + + + + Use this unit name instead of an automatically + generated one. + + + + + + + Sets a unit property for the scope or service + unit that is created. This takes an assignment in the same + format as + systemctl1's + set-property command. + + + + + + + Provide a description for the service or scope + unit. If not specified, the command itself will be used as a + description. See Description= in + systemd.unit5. + + + + + + + Make the new .service or + .scope unit part of the specified slice, + instead of the system.slice. + + + + + + + After the service or scope process has + terminated, keep the service around until it is explicitly + stopped. This is useful to collect runtime information about + the service after it finished running. Also see + RemainAfterExit= in + systemd.service5. + + + + + + + + When terminating the scope or service unit, + send a SIGHUP immediately after SIGTERM. This is useful to + indicate to shells and shell-like processes that the + connection has been severed. Also see + SendSIGHUP= in + systemd.kill5. + + + + + + + + Sets the service type. Also see + Type= in + systemd.service5. This + option has no effect in conjunction with + . Defaults to + simple. + + + + + + + + Runs the service process under the UNIX user + and group. Also see User= and + Group= in + systemd.exec5. + + + + + + + Runs the service process with the specified + nice level. Also see Nice= in + systemd.exec5. + + + + + + + + Runs the service process with the specified environment variable set. + Also see Environment= in + systemd.exec5. + + + + + + + + When invoking a command, the service connects + its standard input and output to the invoking tty via a + pseudo TTY device. This allows invoking binaries as services + that expect interactive user input, such as interactive + command shells. + + + + + + + Suppresses additional informational output + while running. This is particularly useful in combination with + when it will suppress the initial + message explaining how to terminate the TTY connection. + + + + + + + + + + Defines monotonic timers relative to different + starting points. Also see OnActiveSec=, + OnBootSec=, + OnStartupSec=, + OnUnitActiveSec= and + OnUnitInactiveSec= in + systemd.timer5. This + options have no effect in conjunction with + . + + + + + + + Defines realtime (i.e. wallclock) timers with + calendar event expressions. Also see + OnCalendar= in + systemd.timer5. This + option has no effect in conjunction with + . + + + + + + + Sets a timer unit property for the timer unit + that is created. It is similar with + but only for created timer + unit. This option only has effect in conjunction with + , , + , + , + , + . This takes an assignment in + the same format as + systemctl1's + set-property command. + + + + + + + Do not synchronously wait for the requested operation + to finish. If this is not specified, the job will be + verified, enqueued and systemd-run will + wait until the unit's start-up is completed. By passing this + argument, it is only verified and enqueued. + + + + + + + + + + + + + All command line arguments after the first non-option + argument become part of the command line of the launched + process. If a command is run as service unit, its first argument + needs to be an absolute binary path. + + + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + Examples + + + Logging environment variables provided by systemd to services + + # systemd-run env +Running as unit: run-19945.service +# journalctl -u run-19945.service +Sep 08 07:37:21 bupkis systemd[1]: Starting /usr/bin/env... +Sep 08 07:37:21 bupkis systemd[1]: Started /usr/bin/env. +Sep 08 07:37:21 bupkis env[19948]: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin +Sep 08 07:37:21 bupkis env[19948]: LANG=en_US.UTF-8 +Sep 08 07:37:21 bupkis env[19948]: BOOT_IMAGE=/vmlinuz-3.11.0-0.rc5.git6.2.fc20.x86_64 + + + + Limiting resources available to a command + + # systemd-run -p BlockIOWeight=10 updatedb + + This command invokes the + updatedb8 + tool, but lowers the block I/O weight for it to 10. See + systemd.resource-control5 + for more information on the BlockIOWeight= + property. + + + + Running commands at a specified time + + The following command will touch a file after 30 seconds. + + # date; systemd-run --on-active=30 --timer-property=AccuracySec=100ms /bin/touch /tmp/foo +Mon Dec 8 20:44:24 KST 2014 +Running as unit: run-71.timer +Will run service as unit: run-71.service +# journalctl -b -u run-71.timer +-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. -- +Dec 08 20:44:38 container systemd[1]: Starting /bin/touch /tmp/foo. +Dec 08 20:44:38 container systemd[1]: Started /bin/touch /tmp/foo. +# journalctl -b -u run-71.service +-- Logs begin at Fri 2014-12-05 19:09:21 KST, end at Mon 2014-12-08 20:44:54 KST. -- +Dec 08 20:44:48 container systemd[1]: Starting /bin/touch /tmp/foo... +Dec 08 20:44:48 container systemd[1]: Started /bin/touch /tmp/foo. + + + + Allowing access to the tty + + The following command invokes /bin/bash as a service + passing its standard input, output and error to the calling TTY. + + # systemd-run -t --send-sighup /bin/bash + + + + Start <command>screen</command> as a user service + + $ systemd-run --scope --user screen +Running scope as unit run-r14b0047ab6df45bfb45e7786cc839e76.scope. + +$ screen -ls +There is a screen on: + 492..laptop (Detached) +1 Socket in /var/run/screen/S-fatima. + + + This starts the screen process as a child of the + systemd --user process that was started by + user@.service, in a scope unit. A + systemd.scope5 + unit is used instead of a + systemd.service5 + unit, because screen will exit when detaching from the terminal, + and a service unit would be terminated. Running screen + as a user unit has the advantage that it is not part of the session scope. + If KillUserProcesses=yes is configured in + logind.conf5, + the default, the session scope will be terminated when the user logs + out of that session. + + The user@.service is started automatically + when the user first logs in, and stays around as long as at least one + login session is open. After the user logs out of the last session, + user@.service and all services underneath it + are terminated. This behaviour is the default, when "lingering" is + not enabled for that user. Enabling lingering means that + user@.service is started automatically during + boot, even if the user is not logged in, and that the service is + not terminated when the user logs out. + + Enabling lingering allows the user to run processes without being logged in, + for example to allow screen to persist after the user logs out, + even if the session scope is terminated. In the default configuration, users can + enable lingering for themselves: + + $ loginctl enable-linger + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.service5, + systemd.scope5, + systemd.slice5, + systemd.exec5, + systemd.resource-control5, + systemd.timer5, + machinectl1 + + + + diff --git a/src/grp-system/grp-utils/systemd-sysv-generator/.gitignore b/src/grp-system/grp-utils/systemd-sysv-generator/.gitignore new file mode 100644 index 0000000000..c3fea7424f --- /dev/null +++ b/src/grp-system/grp-utils/systemd-sysv-generator/.gitignore @@ -0,0 +1 @@ +/README diff --git a/src/grp-system/grp-utils/systemd-sysv-generator/Makefile b/src/grp-system/grp-utils/systemd-sysv-generator/Makefile new file mode 100644 index 0000000000..9dec62efdc --- /dev/null +++ b/src/grp-system/grp-utils/systemd-sysv-generator/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_sysv_generator_SOURCES = \ + src/sysv-generator/sysv-generator.c + +systemd_sysv_generator_LDADD = \ + libcore.la + +ifneq ($(HAVE_SYSV_COMPAT),) +sysvinit_DATA = \ + docs/sysvinit/README + +$(outdir)/README: docs/sysvinit/README.in + $(SED_PROCESS) + +CLEANFILES += \ + docs/sysvinit/README +endif # HAVE_SYSV_COMPAT + +EXTRA_DIST += \ + docs/sysvinit/README.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/grp-utils/systemd-sysv-generator/README.in b/src/grp-system/grp-utils/systemd-sysv-generator/README.in new file mode 100644 index 0000000000..996402d06b --- /dev/null +++ b/src/grp-system/grp-utils/systemd-sysv-generator/README.in @@ -0,0 +1,27 @@ +You are looking for the traditional init scripts in @SYSTEM_SYSVINIT_PATH@, +and they are gone? + +Here's an explanation on what's going on: + +You are running a systemd-based OS where traditional init scripts have +been replaced by native systemd services files. Service files provide +very similar functionality to init scripts. To make use of service +files simply invoke "systemctl", which will output a list of all +currently running services (and other units). Use "systemctl +list-unit-files" to get a listing of all known unit files, including +stopped, disabled and masked ones. Use "systemctl start +foobar.service" and "systemctl stop foobar.service" to start or stop a +service, respectively. For further details, please refer to +systemctl(1). + +Note that traditional init scripts continue to function on a systemd +system. An init script @SYSTEM_SYSVINIT_PATH@/foobar is implicitly mapped +into a service unit foobar.service during system initialization. + +Thank you! + +Further reading: + man:systemctl(1) + man:systemd(1) + http://0pointer.de/blog/projects/systemd-for-admins-3.html + http://www.freedesktop.org/wiki/Software/systemd/Incompatibilities diff --git a/src/grp-system/grp-utils/systemd-sysv-generator/systemd-sysv-generator.xml b/src/grp-system/grp-utils/systemd-sysv-generator/systemd-sysv-generator.xml new file mode 100644 index 0000000000..2353eb3efe --- /dev/null +++ b/src/grp-system/grp-utils/systemd-sysv-generator/systemd-sysv-generator.xml @@ -0,0 +1,97 @@ + + + + + + + + systemd-sysv-generator + systemd + + + + Documentation + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-sysv-generator + 8 + + + + systemd-sysv-generator + Unit generator for SysV init scripts + + + + /usr/lib/systemd/system-generators/systemd-sysv-generator + + + + Description + + systemd-sysv-generator is a generator + that creates wrapper .service units for + SysV init + scripts in /etc/init.d/* at boot and when + configuration of the system manager is reloaded. This will allow + systemd1 + to support them similarly to native units. + + LSB headers + in SysV init scripts are interpreted, and the ordering specified + in the header is turned into dependencies between the generated + unit and other units. The LSB facilities + $remote_fs, $network, + $named, $portmap, + $time are supported and will be turned into + dependencies on specific native systemd targets. See + systemd.special5 + for more details. + + SysV runlevels have corresponding systemd targets + (runlevelX.target). + The wrapper unit that is generated will be wanted by those targets + which correspond to runlevels for which the script is + enabled. + + systemd does not support SysV scripts as + part of early boot, so all wrapper units are ordered after + basic.target. + + systemd-sysv-generator implements + systemd.generator7. + + + + See Also + + systemd1, + systemd.service5, + systemd.target5 + + + + diff --git a/src/grp-system/grp-utils/systemd-sysv-generator/sysv-generator.c b/src/grp-system/grp-utils/systemd-sysv-generator/sysv-generator.c new file mode 100644 index 0000000000..de973034be --- /dev/null +++ b/src/grp-system/grp-utils/systemd-sysv-generator/sysv-generator.c @@ -0,0 +1,982 @@ +/*** + This file is part of systemd. + + Copyright 2014 Thomas H.P. Andersen + Copyright 2010 Lennart Poettering + Copyright 2011 Michal Schmidt + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/hexdecoct.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/install.h" +#include "shared/path-lookup.h" + +static const struct { + const char *path; + const char *target; +} rcnd_table[] = { + /* Standard SysV runlevels for start-up */ + { "rc1.d", SPECIAL_RESCUE_TARGET }, + { "rc2.d", SPECIAL_MULTI_USER_TARGET }, + { "rc3.d", SPECIAL_MULTI_USER_TARGET }, + { "rc4.d", SPECIAL_MULTI_USER_TARGET }, + { "rc5.d", SPECIAL_GRAPHICAL_TARGET }, + + /* We ignore the SysV runlevels for shutdown here, as SysV services get default dependencies anyway, and that + * means they are shut down anyway at system power off if running. */ +}; + +static const char *arg_dest = "/tmp"; + +typedef struct SysvStub { + char *name; + char *path; + char *description; + int sysv_start_priority; + char *pid_file; + char **before; + char **after; + char **wants; + char **wanted_by; + 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); + free(s->pid_file); + strv_free(s->before); + strv_free(s->after); + strv_free(s->wants); + strv_free(s->wanted_by); + free(s); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, free_sysvstub); + +static void free_sysvstub_hashmapp(Hashmap **h) { + SysvStub *stub; + + while ((stub = hashmap_steal_first(*h))) + free_sysvstub(stub); + + hashmap_free(*h); +} + +static int add_symlink(const char *service, const char *where) { + const char *from, *to; + int r; + + assert(service); + assert(where); + + from = strjoina(arg_dest, "/", service); + to = strjoina(arg_dest, "/", where, ".wants/", service); + + mkdir_parents_label(to, 0755); + + r = symlink(from, to); + if (r < 0) { + if (errno == EEXIST) + return 0; + + return -errno; + } + + return 1; +} + +static int add_alias(const char *service, const char *alias) { + const char *link; + int r; + + assert(service); + assert(alias); + + link = strjoina(arg_dest, "/", alias); + + r = symlink(service, link); + if (r < 0) { + if (errno == EEXIST) + return 0; + + return -errno; + } + + return 1; +} + +static int generate_unit_file(SysvStub *s) { + _cleanup_fclose_ FILE *f = NULL; + const char *unit; + char **p; + int r; + + 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); + (void) unlink(unit); + } + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-sysv-generator\n\n" + "[Unit]\n" + "Documentation=man:systemd-sysv-generator(8)\n" + "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); + + fprintf(f, + "\n[Service]\n" + "Type=forking\n" + "Restart=no\n" + "TimeoutSec=5min\n" + "IgnoreSIGPIPE=no\n" + "KillMode=process\n" + "GuessMainPID=no\n" + "RemainAfterExit=%s\n", + yes_no(!s->pid_file)); + + if (s->pid_file) + fprintf(f, "PIDFile=%s\n", s->pid_file); + + fprintf(f, + "ExecStart=%s start\n" + "ExecStop=%s stop\n", + s->path, s->path); + + 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_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p); + } + + return 1; +} + +static bool usage_contains_reload(const char *line) { + return (strcasestr(line, "{reload|") || + strcasestr(line, "{reload}") || + strcasestr(line, "{reload\"") || + strcasestr(line, "|reload|") || + strcasestr(line, "|reload}") || + strcasestr(line, "|reload\"")); +} + +static char *sysv_translate_name(const char *name) { + _cleanup_free_ char *c = NULL; + char *res; + + c = strdup(name); + if (!c) + return NULL; + + res = endswith(c, ".sh"); + if (res) + *res = 0; + + if (unit_name_mangle(c, UNIT_NAME_NOGLOB, &res) < 0) + return NULL; + + return res; +} + +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 + * standardized name or a distribution-specific one. Since we + * just follow what already exists and do not introduce new + * uses or names we don't care who introduced a new name. */ + + static const char * const table[] = { + /* LSB defined facilities */ + "local_fs", NULL, + "network", SPECIAL_NETWORK_ONLINE_TARGET, + "named", SPECIAL_NSS_LOOKUP_TARGET, + "portmap", SPECIAL_RPCBIND_TARGET, + "remote_fs", SPECIAL_REMOTE_FS_TARGET, + "syslog", NULL, + "time", SPECIAL_TIME_SYNC_TARGET, + }; + + char *filename_no_sh, *e, *m; + const char *n; + unsigned i; + int r; + + assert(name); + 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; + + m = strdup(table[i+1]); + if (!m) + return log_oom(); + + *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 */ + filename_no_sh = strdupa(filename); + e = endswith(filename_no_sh, ".sh"); + if (e) { + *e = '\0'; + filename = filename_no_sh; + } + + /* Names equaling the file name of the services are redundant */ + if (streq_ptr(n, filename)) + return 0; + + /* 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) { + int r; + + assert(s); + assert(full_text); + assert(text); + + for (;;) { + _cleanup_free_ char *word = NULL, *m = NULL; + + 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; + + 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); + 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(); + } + + break; + + case _UNIT_TYPE_INVALID: + log_warning("Unit name '%s' is invalid", m); + break; + + default: + log_warning("Unknown unit type for unit '%s'", m); + } + } + + return 0; +} + +static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) { + int r; + + assert(s); + assert(full_text); + assert(text); + + for (;;) { + _cleanup_free_ char *word = NULL, *m = NULL; + bool is_before; + + 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:"); + + if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) { + /* the network-online target is special, as it needs to be actively pulled in */ + r = strv_extend(&s->after, m); + if (r < 0) + return log_oom(); + + r = strv_extend(&s->wants, m); + } else + r = strv_extend(is_before ? &s->before : &s->after, m); + if (r < 0) + return log_oom(); + } + + return 0; +} + +static int load_sysv(SysvStub *s) { + _cleanup_fclose_ FILE *f; + unsigned line = 0; + int r; + enum { + NORMAL, + DESCRIPTION, + LSB, + LSB_DESCRIPTION, + USAGE_CONTINUATION + } state = NORMAL; + _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) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open %s: %m", s->path); + } + + log_debug("Loading SysV script %s", s->path); + + FOREACH_LINE(l, f, goto fail) { + char *t; + + line++; + + t = strstrip(l); + if (*t != '#') { + /* Try to figure out whether this init script supports + * the reload operation. This heuristic looks for + * "Usage" lines which include the reload option. */ + if ( state == USAGE_CONTINUATION || + (state == NORMAL && strcasestr(t, "usage"))) { + if (usage_contains_reload(t)) { + supports_reload = true; + state = NORMAL; + } else if (t[strlen(t)-1] == '\\') + state = USAGE_CONTINUATION; + else + state = NORMAL; + } + + continue; + } + + if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) { + state = LSB; + s->has_lsb = true; + continue; + } + + if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) { + state = NORMAL; + continue; + } + + t++; + t += strspn(t, WHITESPACE); + + if (state == NORMAL) { + + /* Try to parse Red Hat style description */ + + if (startswith_no_case(t, "description:")) { + + size_t k; + const char *j; + + k = strlen(t); + if (k > 0 && t[k-1] == '\\') { + state = DESCRIPTION; + t[k-1] = 0; + } + + j = empty_to_null(strstrip(t+12)); + + r = free_and_strdup(&chkconfig_description, j); + if (r < 0) + return log_oom(); + + } else if (startswith_no_case(t, "pidfile:")) { + const char *fn; + + state = NORMAL; + + fn = strstrip(t+8); + if (!path_is_absolute(fn)) { + log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line); + continue; + } + + r = free_and_strdup(&s->pid_file, fn); + if (r < 0) + return log_oom(); + } + + } else if (state == DESCRIPTION) { + + /* Try to parse Red Hat style description + * continuation */ + + size_t k; + char *j; + + k = strlen(t); + if (k > 0 && t[k-1] == '\\') + t[k-1] = 0; + else + state = NORMAL; + + j = strstrip(t); + if (!isempty(j)) { + char *d = NULL; + + if (chkconfig_description) + d = strjoin(chkconfig_description, " ", j, NULL); + else + d = strdup(j); + if (!d) + return log_oom(); + + free(chkconfig_description); + chkconfig_description = d; + } + + } else if (state == LSB || state == LSB_DESCRIPTION) { + + if (startswith_no_case(t, "Provides:")) { + state = LSB; + + r = handle_provides(s, line, t, t + 9); + if (r < 0) + return r; + + } else if (startswith_no_case(t, "Required-Start:") || + startswith_no_case(t, "Should-Start:") || + startswith_no_case(t, "X-Start-Before:") || + startswith_no_case(t, "X-Start-After:")) { + + state = LSB; + + r = handle_dependencies(s, line, t, strchr(t, ':') + 1); + if (r < 0) + return r; + + } else if (startswith_no_case(t, "Description:")) { + const char *j; + + state = LSB_DESCRIPTION; + + j = empty_to_null(strstrip(t+12)); + + r = free_and_strdup(&long_description, j); + if (r < 0) + return log_oom(); + + } else if (startswith_no_case(t, "Short-Description:")) { + const char *j; + + state = LSB; + + j = empty_to_null(strstrip(t+18)); + + r = free_and_strdup(&short_description, j); + if (r < 0) + return log_oom(); + + } else if (state == LSB_DESCRIPTION) { + + if (startswith(l, "#\t") || startswith(l, "# ")) { + const char *j; + + j = strstrip(t); + if (!isempty(j)) { + char *d = NULL; + + if (long_description) + d = strjoin(long_description, " ", t, NULL); + else + d = strdup(j); + if (!d) + return log_oom(); + + free(long_description); + long_description = d; + } + + } else + state = LSB; + } + } + } + + s->reload = supports_reload; + + /* We use the long description only if + * no short description is set. */ + + if (short_description) + description = short_description; + else if (chkconfig_description) + description = chkconfig_description; + else if (long_description) + description = long_description; + else + description = NULL; + + if (description) { + char *d; + + d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description); + if (!d) + 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) { + SysvStub *other; + Iterator j; + int r; + + assert(s); + + if (!s->loaded) + return 0; + + if (s->sysv_start_priority < 0) + return 0; + + HASHMAP_FOREACH(other, all_services, j) { + if (s == other) + continue; + + if (!other->loaded) + continue; + + if (other->sysv_start_priority < 0) + continue; + + /* If both units have modern headers we don't care + * about the priorities */ + if (s->has_lsb && other->has_lsb) + continue; + + if (other->sysv_start_priority < s->sysv_start_priority) { + r = strv_extend(&s->after, other->name); + if (r < 0) + return log_oom(); + + } else if (other->sysv_start_priority > s->sysv_start_priority) { + r = strv_extend(&s->before, other->name); + if (r < 0) + return log_oom(); + } else + continue; + + /* FIXME: Maybe we should compare the name here lexicographically? */ + } + + return 0; +} + +static int acquire_search_path(const char *def, const char *envvar, char ***ret) { + _cleanup_strv_free_ char **l = NULL; + const char *e; + int r; + + assert(def); + assert(envvar); + + e = getenv(envvar); + if (e) { + r = path_split_and_make_absolute(e, &l); + if (r < 0) + return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar); + } + + if (strv_isempty(l)) { + strv_free(l); + + l = strv_new(def, NULL); + if (!l) + return log_oom(); + } + + if (!path_strv_resolve_uniq(l, NULL)) + return log_oom(); + + *ret = l; + l = NULL; + + return 0; +} + +static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { + _cleanup_strv_free_ char **sysvinit_path = NULL; + char **path; + int r; + + assert(lp); + + r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path); + if (r < 0) + return r; + + STRV_FOREACH(path, sysvinit_path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(*path); + if (!d) { + if (errno != ENOENT) + log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path); + continue; + } + + 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; + + if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { + log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); + continue; + } + + if (!(st.st_mode & S_IXUSR)) + continue; + + if (!S_ISREG(st.st_mode)) + continue; + + name = sysv_translate_name(de->d_name); + if (!name) + return log_oom(); + + if (hashmap_contains(all_services, name)) + continue; + + r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); + if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { + 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(); + + service = new0(SysvStub, 1); + if (!service) + return log_oom(); + + 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(); + + service = NULL; + } + } + + return 0; +} + +static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) { + Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; + _cleanup_strv_free_ char **sysvrcnd_path = NULL; + SysvStub *service; + unsigned i; + Iterator j; + char **p; + int r; + + assert(lp); + + r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path); + if (r < 0) + return r; + + STRV_FOREACH(p, sysvrcnd_path) { + for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { + + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *path = NULL; + struct dirent *de; + + path = strjoin(*p, "/", rcnd_table[i].path, NULL); + if (!path) { + r = log_oom(); + goto finish; + } + + d = opendir(path); + if (!d) { + if (errno != ENOENT) + log_warning_errno(errno, "Opening %s failed, ignoring: %m", path); + + continue; + } + + 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 (de->d_name[0] != 'S') + continue; + + if (strlen(de->d_name) < 4) + continue; + + a = undecchar(de->d_name[1]); + b = undecchar(de->d_name[2]); + + if (a < 0 || b < 0) + continue; + + fpath = strjoin(*p, "/", de->d_name, NULL); + if (!fpath) { + r = log_oom(); + goto finish; + } + + name = sysv_translate_name(de->d_name + 3); + if (!name) { + r = log_oom(); + goto finish; + } + + 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); + continue; + } + + service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority); + + r = set_ensure_allocated(&runlevel_services[i], NULL); + if (r < 0) { + log_oom(); + goto finish; + } + + r = set_put(runlevel_services[i], service); + 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) { + log_oom(); + goto finish; + } + r = strv_extend(&service->wanted_by, rcnd_table[i].target); + if (r < 0) { + log_oom(); + goto finish; + } + } + + r = 0; + +finish: + for (i = 0; i < ELEMENTSOF(rcnd_table); i++) + set_free(runlevel_services[i]); + + return r; +} + +int main(int argc, char *argv[]) { + _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."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[3]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); + if (r < 0) { + log_error_errno(r, "Failed to find lookup paths: %m"); + goto finish; + } + + all_services = hashmap_new(&string_hash_ops); + if (!all_services) { + r = log_oom(); + goto finish; + } + + r = enumerate_sysv(&lp, all_services); + if (r < 0) + goto finish; + + r = set_dependencies_from_rcnd(&lp, all_services); + if (r < 0) + goto finish; + + HASHMAP_FOREACH(service, all_services, j) + (void) load_sysv(service); + + HASHMAP_FOREACH(service, all_services, j) { + (void) fix_order(service, all_services); + (void) generate_unit_file(service); + } + + r = 0; + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-system/kernel-command-line.xml b/src/grp-system/kernel-command-line.xml new file mode 100644 index 0000000000..3ecc969c10 --- /dev/null +++ b/src/grp-system/kernel-command-line.xml @@ -0,0 +1,384 @@ + + + + + + + + + kernel-command-line + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + kernel-command-line + 7 + + + + kernel-command-line + Kernel command line parameters + + + + /proc/cmdline + + + + Description + + The kernel, the initial RAM disk (initrd) and + basic userspace functionality may be configured at boot via + kernel command line arguments. + + For command line parameters understood by the kernel, please + see kernel-parameters.txt + and + bootparam7. + + For command line parameters understood by the initial RAM + disk, please see + dracut.cmdline7, + or the documentation of the specific initrd implementation of your + installation. + + + + Core OS Command Line Arguments + + + + systemd.unit= + rd.systemd.unit= + systemd.dump_core= + systemd.crash_chvt= + systemd.crash_shell= + systemd.crash_reboot= + systemd.confirm_spawn= + systemd.show_status= + systemd.log_target= + systemd.log_level= + systemd.log_color= + systemd.log_location= + systemd.default_standard_output= + systemd.default_standard_error= + systemd.setenv= + systemd.machine_id= + + Parameters understood by the system and service + manager to control system behavior. For details, see + systemd1. + + + + + systemd.mask= + systemd.wants= + systemd.debug-shell + + Additional parameters understood by + systemd-debug-generator8, + to mask or start specific units at boot, or invoke a debug + shell on tty9. + + + + + systemd.restore_state= + + This parameter is understood by several system tools + to control whether or not they should restore system state + from the previous boot. For details, see + systemd-backlight@.service8 + and + systemd-rfkill.service8. + + + + + + quiet + + Parameter understood by both the kernel and the system + and service manager to control console log verbosity. For + details, see + systemd1. + + + + + debug + + Parameter understood by both the kernel and the system + and service manager to control console log verbosity. For + details, see + systemd1. + + + + + -b + rd.emergency + emergency + rd.rescue + rescue + single + s + S + 1 + 2 + 3 + 4 + 5 + + Parameters understood by the system and service + manager, as compatibility and convenience options. For details, see + systemd1. + + + + + locale.LANG= + locale.LANGUAGE= + locale.LC_CTYPE= + locale.LC_NUMERIC= + locale.LC_TIME= + locale.LC_COLLATE= + locale.LC_MONETARY= + locale.LC_MESSAGES= + locale.LC_PAPER= + locale.LC_NAME= + locale.LC_ADDRESS= + locale.LC_TELEPHONE= + locale.LC_MEASUREMENT= + locale.LC_IDENTIFICATION= + + Parameters understood by the system and service + manager to control locale and language settings. For + details, see + systemd1. + + + + + fsck.mode= + fsck.repair= + + + Parameters understood by the file system checker + services. For details, see + systemd-fsck@.service8. + + + + + quotacheck.mode= + + + Parameter understood by the file quota checker + service. For details, see + systemd-quotacheck.service8. + + + + + systemd.journald.forward_to_syslog= + systemd.journald.forward_to_kmsg= + systemd.journald.forward_to_console= + systemd.journald.forward_to_wall= + + + Parameters understood by the journal service. For + details, see + systemd-journald.service8. + + + + + vconsole.keymap= + vconsole.keymap.toggle= + vconsole.font= + vconsole.font.map= + vconsole.font.unimap= + + + Parameters understood by the virtual console setup + logic. For details, see + systemd-vconsole-setup.service8. + + + + + udev.log-priority= + rd.udev.log-priority= + udev.children-max= + rd.udev.children-max= + udev.exec-delay= + rd.udev.exec-delay= + udev.event-timeout= + rd.udev.event-timeout= + net.ifnames= + + + Parameters understood by the device event managing + daemon. For details, see + systemd-udevd.service8. + + + + + plymouth.enable= + + + May be used to disable the Plymouth boot splash. For + details, see + plymouth8. + + + + + luks= + rd.luks= + luks.crypttab= + rd.luks.crypttab= + luks.name= + rd.luks.name= + luks.uuid= + rd.luks.uuid= + luks.options= + rd.luks.options= + luks.key= + rd.luks.key= + + + Configures the LUKS full-disk encryption logic at + boot. For details, see + systemd-cryptsetup-generator8. + + + + + fstab= + rd.fstab= + + + Configures the /etc/fstab logic + at boot. For details, see + systemd-fstab-generator8. + + + + + root= + rootfstype= + rootflags= + ro + rw + + + Configures the root file system and its file system + type and mount options, as well as whether it shall be + mounted read-only or read-writable initially. For details, + see + systemd-fstab-generator8. + + + + + systemd.gpt_auto= + rd.systemd.gpt_auto= + + + Configures whether GPT based partition auto-discovery + shall be attempted. For details, see + systemd-gpt-auto-generator8. + + + + + systemd.default_timeout_start_sec= + + + Overwrites the default start job timeout DefaultTimeoutStartSec= at boot. For details, + see systemd-system.conf5. + + + + + modules-load= + rd.modules-load= + + + Load a specific kernel module early at boot. For + details, see + systemd-modules-load.service8. + + + + + resume= + + + Enables resume from hibernation using the specified + device. All + fstab5-like + paths are supported. For details, see + systemd-hibernate-resume-generator8. + + + + + + + + See Also + + systemd1, + bootparam7, + dracut.cmdline7, + systemd-debug-generator8, + systemd-fsck@.service8, + systemd-quotacheck.service8, + systemd-journald.service8, + systemd-vconsole-setup.service8, + systemd-udevd.service8, + plymouth8, + systemd-cryptsetup-generator8, + systemd-fstab-generator8, + systemd-gpt-auto-generator8, + systemd-modules-load.service8, + systemd-backlight@.service8, + systemd-rfkill.service8, + systemd-hibernate-resume-generator8 + + + + diff --git a/src/grp-system/libcore/.gitignore b/src/grp-system/libcore/.gitignore new file mode 100644 index 0000000000..465b4fcc20 --- /dev/null +++ b/src/grp-system/libcore/.gitignore @@ -0,0 +1,3 @@ +/macros.systemd +/triggers.systemd +/systemd.pc diff --git a/src/grp-system/libcore/Makefile b/src/grp-system/libcore/Makefile new file mode 100644 index 0000000000..02b3add171 --- /dev/null +++ b/src/grp-system/libcore/Makefile @@ -0,0 +1,168 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libcore.la + +libcore_la_SOURCES = \ + src/core/unit.c \ + src/core/unit.h \ + src/core/unit-printf.c \ + src/core/unit-printf.h \ + src/core/job.c \ + src/core/job.h \ + src/core/manager.c \ + src/core/manager.h \ + src/core/transaction.c \ + src/core/transaction.h \ + src/core/load-fragment.c \ + src/core/load-fragment.h \ + src/core/service.c \ + src/core/service.h \ + src/core/socket.c \ + src/core/socket.h \ + src/core/busname.c \ + src/core/busname.h \ + src/core/bus-policy.c \ + src/core/bus-policy.h \ + src/core/target.c \ + src/core/target.h \ + src/core/device.c \ + src/core/device.h \ + src/core/mount.c \ + src/core/mount.h \ + src/core/automount.c \ + src/core/automount.h \ + src/core/swap.c \ + src/core/swap.h \ + src/core/timer.c \ + src/core/timer.h \ + src/core/path.c \ + src/core/path.h \ + src/core/slice.c \ + src/core/slice.h \ + src/core/scope.c \ + src/core/scope.h \ + src/core/load-dropin.c \ + src/core/load-dropin.h \ + src/core/execute.c \ + src/core/execute.h \ + src/core/kill.c \ + src/core/kill.h \ + src/core/dbus.c \ + src/core/dbus.h \ + src/core/dbus-manager.c \ + src/core/dbus-manager.h \ + src/core/dbus-unit.c \ + src/core/dbus-unit.h \ + src/core/dbus-job.c \ + src/core/dbus-job.h \ + src/core/dbus-service.c \ + src/core/dbus-service.h \ + src/core/dbus-socket.c \ + src/core/dbus-socket.h \ + src/core/dbus-busname.c \ + src/core/dbus-busname.h \ + src/core/dbus-target.c \ + src/core/dbus-target.h \ + src/core/dbus-device.c \ + src/core/dbus-device.h \ + src/core/dbus-mount.c \ + src/core/dbus-mount.h \ + src/core/dbus-automount.c \ + src/core/dbus-automount.h \ + src/core/dbus-swap.c \ + src/core/dbus-swap.h \ + src/core/dbus-timer.c \ + src/core/dbus-timer.h \ + src/core/dbus-path.c \ + src/core/dbus-path.h \ + src/core/dbus-slice.c \ + src/core/dbus-slice.h \ + src/core/dbus-scope.c \ + src/core/dbus-scope.h \ + src/core/dbus-execute.c \ + src/core/dbus-execute.h \ + src/core/dbus-kill.c \ + src/core/dbus-kill.h \ + src/core/dbus-cgroup.c \ + src/core/dbus-cgroup.h \ + src/core/cgroup.c \ + src/core/cgroup.h \ + src/core/selinux-access.c \ + src/core/selinux-access.h \ + src/core/selinux-setup.c \ + src/core/selinux-setup.h \ + src/core/smack-setup.c \ + src/core/smack-setup.h \ + src/core/ima-setup.c \ + src/core/ima-setup.h \ + src/core/locale-setup.h \ + src/core/locale-setup.c \ + src/core/hostname-setup.c \ + src/core/hostname-setup.h \ + src/core/machine-id-setup.c \ + src/core/machine-id-setup.h \ + src/core/mount-setup.c \ + src/core/mount-setup.h \ + src/core/kmod-setup.c \ + src/core/kmod-setup.h \ + src/core/loopback-setup.h \ + src/core/loopback-setup.c \ + src/core/namespace.c \ + src/core/namespace.h \ + src/core/killall.h \ + src/core/killall.c \ + src/core/audit-fd.c \ + src/core/audit-fd.h \ + src/core/show-status.c \ + src/core/show-status.h \ + src/core/failure-action.c \ + src/core/failure-action.h + +nodist_libcore_la_SOURCES = \ + src/core/load-fragment-gperf.c \ + src/core/load-fragment-gperf-nulstr.c + +libcore_la_CFLAGS = \ + $(PAM_CFLAGS) \ + $(AUDIT_CFLAGS) \ + $(KMOD_CFLAGS) \ + $(APPARMOR_CFLAGS) \ + $(MOUNT_CFLAGS) \ + $(SECCOMP_CFLAGS) + +libcore_la_LIBADD = \ + libsystemd-shared.la \ + $(PAM_LIBS) \ + $(AUDIT_LIBS) \ + $(KMOD_LIBS) \ + $(APPARMOR_LIBS) \ + $(MOUNT_LIBS) + +$(outdir)/load-fragment-gperf-nulstr.c: src/core/load-fragment-gperf.gperf + $(AM_V_GEN)$(AWK) 'BEGIN{ keywords=0 ; FS="," ; print "extern const char load_fragment_gperf_nulstr[];" ; print "const char load_fragment_gperf_nulstr[] ="} ; keyword==1 { print "\"" $$1 "\\0\"" } ; /%%/ { keyword=1} ; END { print ";" }' < $< > $@ + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/libcore/audit-fd.c b/src/grp-system/libcore/audit-fd.c new file mode 100644 index 0000000000..8ea525eda3 --- /dev/null +++ b/src/grp-system/libcore/audit-fd.c @@ -0,0 +1,73 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include + +#include "audit-fd.h" + +#ifdef HAVE_AUDIT + +#include +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/util.h" + +static bool initialized = false; +static int audit_fd; + +int get_audit_fd(void) { + + if (!initialized) { + audit_fd = audit_open(); + + if (audit_fd < 0) { + if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) + log_error_errno(errno, "Failed to connect to audit log: %m"); + + audit_fd = errno ? -errno : -EINVAL; + } + + initialized = true; + } + + return audit_fd; +} + +void close_audit_fd(void) { + + if (initialized && audit_fd >= 0) + safe_close(audit_fd); + + initialized = true; + audit_fd = -ECONNRESET; +} + +#else + +int get_audit_fd(void) { + return -EAFNOSUPPORT; +} + +void close_audit_fd(void) { +} + +#endif diff --git a/src/grp-system/libcore/audit-fd.h b/src/grp-system/libcore/audit-fd.h new file mode 100644 index 0000000000..0eccb59210 --- /dev/null +++ b/src/grp-system/libcore/audit-fd.h @@ -0,0 +1,23 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int get_audit_fd(void); +void close_audit_fd(void); diff --git a/src/grp-system/libcore/automount.c b/src/grp-system/libcore/automount.c new file mode 100644 index 0000000000..30c30469ca --- /dev/null +++ b/src/grp-system/libcore/automount.c @@ -0,0 +1,1124 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "automount.h" +#include "dbus-automount.h" +#include "mount.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = { + [AUTOMOUNT_DEAD] = UNIT_INACTIVE, + [AUTOMOUNT_WAITING] = UNIT_ACTIVE, + [AUTOMOUNT_RUNNING] = UNIT_ACTIVE, + [AUTOMOUNT_FAILED] = UNIT_FAILED +}; + +struct expire_data { + int dev_autofs_fd; + int ioctl_fd; +}; + +static inline void expire_data_free(struct expire_data *data) { + if (!data) + return; + + safe_close(data->dev_autofs_fd); + safe_close(data->ioctl_fd); + free(data); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free); + +static int open_dev_autofs(Manager *m); +static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata); +static int automount_start_expire(Automount *a); +static void automount_stop_expire(Automount *a); +static int automount_send_ready(Automount *a, Set *tokens, int status); + +static void automount_init(Unit *u) { + Automount *a = AUTOMOUNT(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + a->pipe_fd = -1; + a->directory_mode = 0755; + UNIT(a)->ignore_on_isolate = true; +} + +static void unmount_autofs(Automount *a) { + int r; + + assert(a); + + if (a->pipe_fd < 0) + return; + + a->pipe_event_source = sd_event_source_unref(a->pipe_event_source); + a->pipe_fd = safe_close(a->pipe_fd); + + /* If we reload/reexecute things we keep the mount point + * around */ + if (a->where && + (UNIT(a)->manager->exit_code != MANAGER_RELOAD && + UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) { + automount_send_ready(a, a->tokens, -EHOSTDOWN); + automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); + + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount: %m"); + } +} + +static void automount_done(Unit *u) { + Automount *a = AUTOMOUNT(u); + + assert(a); + + unmount_autofs(a); + + a->where = mfree(a->where); + + a->tokens = set_free(a->tokens); + a->expire_tokens = set_free(a->expire_tokens); + + a->expire_event_source = sd_event_source_unref(a->expire_event_source); +} + +static int automount_add_mount_links(Automount *a) { + _cleanup_free_ char *parent = NULL; + + assert(a); + + parent = dirname_malloc(a->where); + if (!parent) + return -ENOMEM; + + return unit_require_mounts_for(UNIT(a), parent); +} + +static int automount_add_default_dependencies(Automount *a) { + int r; + + assert(a); + + if (!UNIT(a)->default_dependencies) + return 0; + + if (!MANAGER_IS_SYSTEM(UNIT(a)->manager)) + return 0; + + r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + return r; + + return 0; +} + +static int automount_verify(Automount *a) { + _cleanup_free_ char *e = NULL; + int r; + + assert(a); + + if (UNIT(a)->load_state != UNIT_LOADED) + return 0; + + if (path_equal(a->where, "/")) { + log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing."); + return -EINVAL; + } + + r = unit_name_from_path(a->where, ".automount", &e); + if (r < 0) + return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m"); + + if (!unit_has_name(UNIT(a), e)) { + log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int automount_load(Unit *u) { + Automount *a = AUTOMOUNT(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + /* Load a .automount file */ + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + Unit *x; + + if (!a->where) { + r = unit_name_to_path(u->id, &a->where); + if (r < 0) + return r; + } + + path_kill_slashes(a->where); + + r = unit_load_related_unit(u, ".mount", &x); + if (r < 0) + return r; + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; + + r = automount_add_mount_links(a); + if (r < 0) + return r; + + r = automount_add_default_dependencies(a); + if (r < 0) + return r; + } + + return automount_verify(a); +} + +static void automount_set_state(Automount *a, AutomountState state) { + AutomountState old_state; + assert(a); + + old_state = a->state; + a->state = state; + + if (state != AUTOMOUNT_RUNNING) + automount_stop_expire(a); + + if (state != AUTOMOUNT_WAITING && + state != AUTOMOUNT_RUNNING) + unmount_autofs(a); + + if (state != old_state) + log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state)); + + unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true); +} + +static int automount_coldplug(Unit *u) { + Automount *a = AUTOMOUNT(u); + int r; + + assert(a); + assert(a->state == AUTOMOUNT_DEAD); + + if (a->deserialized_state != a->state) { + + r = open_dev_autofs(u->manager); + if (r < 0) + return r; + + if (a->deserialized_state == AUTOMOUNT_WAITING || + a->deserialized_state == AUTOMOUNT_RUNNING) { + assert(a->pipe_fd >= 0); + + r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); + if (r < 0) + return r; + + (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); + } + + automount_set_state(a, a->deserialized_state); + } + + return 0; +} + +static void automount_dump(Unit *u, FILE *f, const char *prefix) { + char time_string[FORMAT_TIMESPAN_MAX]; + Automount *a = AUTOMOUNT(u); + + assert(a); + + fprintf(f, + "%sAutomount State: %s\n" + "%sResult: %s\n" + "%sWhere: %s\n" + "%sDirectoryMode: %04o\n" + "%sTimeoutIdleUSec: %s\n", + prefix, automount_state_to_string(a->state), + prefix, automount_result_to_string(a->result), + prefix, a->where, + prefix, a->directory_mode, + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, a->timeout_idle_usec, USEC_PER_SEC)); +} + +static void automount_enter_dead(Automount *a, AutomountResult f) { + assert(a); + + if (f != AUTOMOUNT_SUCCESS) + a->result = f; + + automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); +} + +static int open_dev_autofs(Manager *m) { + struct autofs_dev_ioctl param; + + assert(m); + + if (m->dev_autofs_fd >= 0) + return m->dev_autofs_fd; + + label_fix("/dev/autofs", false, false); + + m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY); + if (m->dev_autofs_fd < 0) + return log_error_errno(errno, "Failed to open /dev/autofs: %m"); + + init_autofs_dev_ioctl(¶m); + if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) { + m->dev_autofs_fd = safe_close(m->dev_autofs_fd); + return -errno; + } + + log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor); + + return m->dev_autofs_fd; +} + +static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) { + struct autofs_dev_ioctl *param; + size_t l; + + assert(dev_autofs_fd >= 0); + assert(where); + + l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1; + param = alloca(l); + + init_autofs_dev_ioctl(param); + param->size = l; + param->ioctlfd = -1; + param->openmount.devid = devid; + strcpy(param->path, where); + + if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) + return -errno; + + if (param->ioctlfd < 0) + return -EIO; + + (void) fd_cloexec(param->ioctlfd, true); + return param->ioctlfd; +} + +static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) { + uint32_t major, minor; + struct autofs_dev_ioctl param; + + assert(dev_autofs_fd >= 0); + assert(ioctl_fd >= 0); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctl_fd; + + if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0) + return -errno; + + major = param.protover.version; + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctl_fd; + + if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0) + return -errno; + + minor = param.protosubver.sub_version; + + log_debug("Autofs protocol version %i.%i", major, minor); + return 0; +} + +static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) { + struct autofs_dev_ioctl param; + + assert(dev_autofs_fd >= 0); + assert(ioctl_fd >= 0); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctl_fd; + + /* Convert to seconds, rounding up. */ + param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC; + + if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0) + return -errno; + + return 0; +} + +static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) { + struct autofs_dev_ioctl param; + + assert(dev_autofs_fd >= 0); + assert(ioctl_fd >= 0); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = ioctl_fd; + + if (status != 0) { + param.fail.token = token; + param.fail.status = status; + } else + param.ready.token = token; + + if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0) + return -errno; + + return 0; +} + +static int automount_send_ready(Automount *a, Set *tokens, int status) { + _cleanup_close_ int ioctl_fd = -1; + unsigned token; + int r; + + assert(a); + assert(status <= 0); + + if (set_isempty(tokens)) + return 0; + + ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); + if (ioctl_fd < 0) + return ioctl_fd; + + if (status != 0) + log_unit_debug_errno(UNIT(a), status, "Sending failure: %m"); + else + log_unit_debug(UNIT(a), "Sending success."); + + r = 0; + + /* Autofs thankfully does not hand out 0 as a token */ + while ((token = PTR_TO_UINT(set_steal_first(tokens)))) { + int k; + + /* Autofs fun fact II: + * + * if you pass a positive status code here, the kernel will + * freeze! Yay! */ + + k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, + ioctl_fd, + token, + status); + if (k < 0) + r = k; + } + + return r; +} + +static void automount_trigger_notify(Unit *u, Unit *other) { + Automount *a = AUTOMOUNT(u); + int r; + + assert(a); + assert(other); + + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) + return; + + /* Don't propagate state changes from the mount if we are already down */ + if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) + return; + + /* Propagate start limit hit state */ + if (other->start_limit_hit) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); + return; + } + + /* Don't propagate anything if there's still a job queued */ + if (other->job) + return; + + /* The mount is successfully established */ + if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { + (void) automount_send_ready(a, a->tokens, 0); + + r = automount_start_expire(a); + if (r < 0) + log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); + + automount_set_state(a, AUTOMOUNT_RUNNING); + } + + if (IN_SET(MOUNT(other)->state, + MOUNT_MOUNTING, MOUNT_MOUNTING_DONE, + MOUNT_MOUNTED, MOUNT_REMOUNTING, + MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, + MOUNT_FAILED)) { + + (void) automount_send_ready(a, a->expire_tokens, -ENODEV); + } + + if (MOUNT(other)->state == MOUNT_DEAD) + (void) automount_send_ready(a, a->expire_tokens, 0); + + /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ + if (IN_SET(MOUNT(other)->state, + MOUNT_DEAD, MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, + MOUNT_FAILED)) { + + (void) automount_send_ready(a, a->tokens, -ENODEV); + + automount_set_state(a, AUTOMOUNT_WAITING); + } +} + +static void automount_enter_waiting(Automount *a) { + _cleanup_close_ int ioctl_fd = -1; + int p[2] = { -1, -1 }; + char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1]; + char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1 + + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1]; + bool mounted = false; + int r, dev_autofs_fd; + struct stat st; + + assert(a); + assert(a->pipe_fd < 0); + assert(a->where); + + set_clear(a->tokens); + + r = unit_fail_if_symlink(UNIT(a), a->where); + if (r < 0) + goto fail; + + (void) mkdir_p_label(a->where, 0555); + + unit_warn_if_dir_nonempty(UNIT(a), a->where); + + dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); + if (dev_autofs_fd < 0) { + r = dev_autofs_fd; + goto fail; + } + + if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { + r = -errno; + goto fail; + } + + xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp()); + xsprintf(name, "systemd-"PID_FMT, getpid()); + if (mount(name, a->where, "autofs", 0, options) < 0) { + r = -errno; + goto fail; + } + + mounted = true; + + p[1] = safe_close(p[1]); + + if (stat(a->where, &st) < 0) { + r = -errno; + goto fail; + } + + ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); + if (ioctl_fd < 0) { + r = ioctl_fd; + goto fail; + } + + r = autofs_protocol(dev_autofs_fd, ioctl_fd); + if (r < 0) + goto fail; + + r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec); + if (r < 0) + goto fail; + + /* Autofs fun fact: + * + * Unless we close the ioctl fd here, for some weird reason + * the direct mount will not receive events from the + * kernel. */ + + r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); + + a->pipe_fd = p[0]; + a->dev_id = st.st_dev; + + automount_set_state(a, AUTOMOUNT_WAITING); + + return; + +fail: + log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); + + safe_close_pair(p); + + if (mounted) { + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount, ignoring: %m"); + } + + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); +} + +static void *expire_thread(void *p) { + struct autofs_dev_ioctl param; + _cleanup_(expire_data_freep) struct expire_data *data = (struct expire_data*)p; + int r; + + assert(data->dev_autofs_fd >= 0); + assert(data->ioctl_fd >= 0); + + init_autofs_dev_ioctl(¶m); + param.ioctlfd = data->ioctl_fd; + + do { + r = ioctl(data->dev_autofs_fd, AUTOFS_DEV_IOCTL_EXPIRE, ¶m); + } while (r >= 0); + + if (errno != EAGAIN) + log_warning_errno(errno, "Failed to expire automount, ignoring: %m"); + + return NULL; +} + +static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) { + Automount *a = AUTOMOUNT(userdata); + _cleanup_(expire_data_freep) struct expire_data *data = NULL; + int r; + + assert(a); + assert(source == a->expire_event_source); + + data = new0(struct expire_data, 1); + if (!data) + return log_oom(); + + data->ioctl_fd = -1; + + data->dev_autofs_fd = fcntl(UNIT(a)->manager->dev_autofs_fd, F_DUPFD_CLOEXEC, 3); + if (data->dev_autofs_fd < 0) + return log_unit_error_errno(UNIT(a), errno, "Failed to duplicate autofs fd: %m"); + + data->ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); + if (data->ioctl_fd < 0) + return log_unit_error_errno(UNIT(a), data->ioctl_fd, "Couldn't open autofs ioctl fd: %m"); + + r = asynchronous_job(expire_thread, data); + if (r < 0) + return log_unit_error_errno(UNIT(a), r, "Failed to start expire job: %m"); + + data = NULL; + + return automount_start_expire(a); +} + +static int automount_start_expire(Automount *a) { + int r; + usec_t timeout; + + assert(a); + + if (a->timeout_idle_usec == 0) + return 0; + + timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/3, USEC_PER_SEC); + + if (a->expire_event_source) { + r = sd_event_source_set_time(a->expire_event_source, timeout); + if (r < 0) + return r; + + return sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_ONESHOT); + } + + r = sd_event_add_time( + UNIT(a)->manager->event, + &a->expire_event_source, + CLOCK_MONOTONIC, timeout, 0, + automount_dispatch_expire, a); + if (r < 0) + return r; + + (void) sd_event_source_set_description(a->expire_event_source, "automount-expire"); + + return 0; +} + +static void automount_stop_expire(Automount *a) { + assert(a); + + if (!a->expire_event_source) + return; + + (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); +} + +static void automount_enter_runnning(Automount *a) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + struct stat st; + int r; + + assert(a); + + /* We don't take mount requests anymore if we are supposed to + * shut down anyway */ + if (unit_stop_pending(UNIT(a))) { + log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); + automount_send_ready(a, a->tokens, -EHOSTDOWN); + automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); + return; + } + + mkdir_p_label(a->where, a->directory_mode); + + /* Before we do anything, let's see if somebody is playing games with us? */ + if (lstat(a->where, &st) < 0) { + log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); + goto fail; + } + + if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) + log_unit_info(UNIT(a), "Automount point already active?"); + else { + Unit *trigger; + + trigger = UNIT_TRIGGER(UNIT(a)); + if (!trigger) { + log_unit_error(UNIT(a), "Unit to trigger vanished."); + goto fail; + } + + r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, 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; + } + } + + automount_set_state(a, AUTOMOUNT_RUNNING); + return; + +fail: + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); +} + +static int automount_start(Unit *u) { + Automount *a = AUTOMOUNT(u); + Unit *trigger; + int r; + + assert(a); + assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); + + if (path_is_mount_point(a->where, 0) > 0) { + log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where); + return -EEXIST; + } + + trigger = UNIT_TRIGGER(u); + if (!trigger || trigger->load_state != UNIT_LOADED) { + log_unit_error(u, "Refusing to start, unit to trigger not loaded."); + return -ENOENT; + } + + r = unit_start_limit_test(u); + if (r < 0) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + + a->result = AUTOMOUNT_SUCCESS; + automount_enter_waiting(a); + return 1; +} + +static int automount_stop(Unit *u) { + Automount *a = AUTOMOUNT(u); + + assert(a); + assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING); + + automount_enter_dead(a, AUTOMOUNT_SUCCESS); + return 1; +} + +static int automount_serialize(Unit *u, FILE *f, FDSet *fds) { + Automount *a = AUTOMOUNT(u); + Iterator i; + void *p; + int r; + + assert(a); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", automount_state_to_string(a->state)); + unit_serialize_item(u, f, "result", automount_result_to_string(a->result)); + unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id); + + SET_FOREACH(p, a->tokens, i) + unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p)); + SET_FOREACH(p, a->expire_tokens, i) + unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p)); + + r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd); + if (r < 0) + return r; + + return 0; +} + +static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Automount *a = AUTOMOUNT(u); + int r; + + assert(a); + assert(fds); + + if (streq(key, "state")) { + AutomountState state; + + state = automount_state_from_string(value); + if (state < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + a->deserialized_state = state; + } else if (streq(key, "result")) { + AutomountResult f; + + f = automount_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != AUTOMOUNT_SUCCESS) + a->result = f; + + } else if (streq(key, "dev-id")) { + unsigned d; + + if (safe_atou(value, &d) < 0) + log_unit_debug(u, "Failed to parse dev-id value: %s", value); + else + a->dev_id = (unsigned) d; + } else if (streq(key, "token")) { + unsigned token; + + if (safe_atou(value, &token) < 0) + log_unit_debug(u, "Failed to parse token value: %s", value); + else { + r = set_ensure_allocated(&a->tokens, NULL); + if (r < 0) { + log_oom(); + return 0; + } + + r = set_put(a->tokens, UINT_TO_PTR(token)); + if (r < 0) + log_unit_error_errno(u, r, "Failed to add token to set: %m"); + } + } else if (streq(key, "expire-token")) { + unsigned token; + + if (safe_atou(value, &token) < 0) + log_unit_debug(u, "Failed to parse token value: %s", value); + else { + r = set_ensure_allocated(&a->expire_tokens, NULL); + if (r < 0) { + log_oom(); + return 0; + } + + r = set_put(a->expire_tokens, UINT_TO_PTR(token)); + if (r < 0) + log_unit_error_errno(u, r, "Failed to add expire token to set: %m"); + } + } else if (streq(key, "pipe-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse pipe-fd value: %s", value); + else { + safe_close(a->pipe_fd); + a->pipe_fd = fdset_remove(fds, fd); + } + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +static UnitActiveState automount_active_state(Unit *u) { + assert(u); + + return state_translation_table[AUTOMOUNT(u)->state]; +} + +static const char *automount_sub_state_to_string(Unit *u) { + assert(u); + + return automount_state_to_string(AUTOMOUNT(u)->state); +} + +static bool automount_check_gc(Unit *u) { + assert(u); + + if (!UNIT_TRIGGER(u)) + return false; + + return UNIT_VTABLE(UNIT_TRIGGER(u))->check_gc(UNIT_TRIGGER(u)); +} + +static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) { + _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; + Unit *trigger; + int r; + + assert(a); + assert(fd == a->pipe_fd); + + if (events != EPOLLIN) { + log_unit_error(UNIT(a), "Got invalid poll event %"PRIu32" on pipe (fd=%d)", events, fd); + goto fail; + } + + r = loop_read_exact(a->pipe_fd, &packet, sizeof(packet), true); + if (r < 0) { + log_unit_error_errno(UNIT(a), r, "Invalid read from pipe: %m"); + goto fail; + } + + switch (packet.hdr.type) { + + case autofs_ptype_missing_direct: + + if (packet.v5_packet.pid > 0) { + _cleanup_free_ char *p = NULL; + + get_process_comm(packet.v5_packet.pid, &p); + log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p)); + } else + log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where); + + r = set_ensure_allocated(&a->tokens, NULL); + if (r < 0) { + log_unit_error(UNIT(a), "Failed to allocate token set."); + goto fail; + } + + r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); + if (r < 0) { + log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); + goto fail; + } + + automount_enter_runnning(a); + break; + + case autofs_ptype_expire_direct: + log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); + + automount_stop_expire(a); + + r = set_ensure_allocated(&a->expire_tokens, NULL); + if (r < 0) { + log_unit_error(UNIT(a), "Failed to allocate token set."); + goto fail; + } + + r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); + if (r < 0) { + log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); + goto fail; + } + + /* Before we do anything, let's see if somebody is playing games with us? */ + if (lstat(a->where, &st) < 0) { + log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); + goto fail; + } + + if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) { + log_unit_info(UNIT(a), "Automount point already unmounted?"); + automount_send_ready(a, a->expire_tokens, 0); + break; + } + + trigger = UNIT_TRIGGER(UNIT(a)); + if (!trigger) { + log_unit_error(UNIT(a), "Unit to trigger vanished."); + goto fail; + } + + r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, 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; + } + break; + + default: + log_unit_error(UNIT(a), "Received unknown automount request %i", packet.hdr.type); + break; + } + + return 0; + +fail: + automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); + return 0; +} + +static void automount_shutdown(Manager *m) { + assert(m); + + m->dev_autofs_fd = safe_close(m->dev_autofs_fd); +} + +static void automount_reset_failed(Unit *u) { + Automount *a = AUTOMOUNT(u); + + assert(a); + + if (a->state == AUTOMOUNT_FAILED) + automount_set_state(a, AUTOMOUNT_DEAD); + + a->result = AUTOMOUNT_SUCCESS; +} + +static bool automount_supported(void) { + static int supported = -1; + + if (supported < 0) + supported = access("/dev/autofs", F_OK) >= 0; + + return supported; +} + +static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { + [AUTOMOUNT_SUCCESS] = "success", + [AUTOMOUNT_FAILURE_RESOURCES] = "resources", + [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); + +const UnitVTable automount_vtable = { + .object_size = sizeof(Automount), + + .sections = + "Unit\0" + "Automount\0" + "Install\0", + + .init = automount_init, + .load = automount_load, + .done = automount_done, + + .coldplug = automount_coldplug, + + .dump = automount_dump, + + .start = automount_start, + .stop = automount_stop, + + .serialize = automount_serialize, + .deserialize_item = automount_deserialize_item, + + .active_state = automount_active_state, + .sub_state_to_string = automount_sub_state_to_string, + + .check_gc = automount_check_gc, + + .trigger_notify = automount_trigger_notify, + + .reset_failed = automount_reset_failed, + + .bus_vtable = bus_automount_vtable, + + .shutdown = automount_shutdown, + .supported = automount_supported, + + .status_message_formats = { + .finished_start_job = { + [JOB_DONE] = "Set up automount %s.", + [JOB_FAILED] = "Failed to set up automount %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Unset automount %s.", + [JOB_FAILED] = "Failed to unset automount %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/automount.h b/src/grp-system/libcore/automount.h new file mode 100644 index 0000000000..76a201178e --- /dev/null +++ b/src/grp-system/libcore/automount.h @@ -0,0 +1,59 @@ +#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 . +***/ + +typedef struct Automount Automount; + +#include "unit.h" + +typedef enum AutomountResult { + AUTOMOUNT_SUCCESS, + AUTOMOUNT_FAILURE_RESOURCES, + AUTOMOUNT_FAILURE_START_LIMIT_HIT, + AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT, + _AUTOMOUNT_RESULT_MAX, + _AUTOMOUNT_RESULT_INVALID = -1 +} AutomountResult; + +struct Automount { + Unit meta; + + AutomountState state, deserialized_state; + + char *where; + usec_t timeout_idle_usec; + + int pipe_fd; + sd_event_source *pipe_event_source; + mode_t directory_mode; + dev_t dev_id; + + Set *tokens; + Set *expire_tokens; + + sd_event_source *expire_event_source; + + AutomountResult result; +}; + +extern const UnitVTable automount_vtable; + +const char* automount_result_to_string(AutomountResult i) _const_; +AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/bus-policy.c b/src/grp-system/libcore/bus-policy.c new file mode 100644 index 0000000000..b6dc2f5438 --- /dev/null +++ b/src/grp-system/libcore/bus-policy.c @@ -0,0 +1,181 @@ +/*** + This file is part of systemd. + + Copyright 2014 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/string-table.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-kernel.h" +#include "sd-bus/kdbus.h" + +#include "bus-policy.h" + +int bus_kernel_translate_access(BusPolicyAccess access) { + assert(access >= 0); + assert(access < _BUS_POLICY_ACCESS_MAX); + + switch (access) { + + case BUS_POLICY_ACCESS_SEE: + return KDBUS_POLICY_SEE; + + case BUS_POLICY_ACCESS_TALK: + return KDBUS_POLICY_TALK; + + case BUS_POLICY_ACCESS_OWN: + return KDBUS_POLICY_OWN; + + default: + assert_not_reached("Unknown policy access"); + } +} + +int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item) { + int r; + + assert(policy); + assert(item); + + switch (policy->type) { + + case BUSNAME_POLICY_TYPE_USER: { + const char *user = policy->name; + uid_t uid; + + r = get_user_creds(&user, &uid, NULL, NULL, NULL); + if (r < 0) + return r; + + item->policy_access.type = KDBUS_POLICY_ACCESS_USER; + item->policy_access.id = uid; + break; + } + + case BUSNAME_POLICY_TYPE_GROUP: { + const char *group = policy->name; + gid_t gid; + + r = get_group_creds(&group, &gid); + if (r < 0) + return r; + + item->policy_access.type = KDBUS_POLICY_ACCESS_GROUP; + item->policy_access.id = gid; + break; + } + + default: + assert_not_reached("Unknown policy type"); + } + + item->policy_access.access = bus_kernel_translate_access(policy->access); + + return 0; +} + +int bus_kernel_make_starter( + int fd, + const char *name, + bool activating, + bool accept_fd, + BusNamePolicy *policy, + BusPolicyAccess world_policy) { + + struct kdbus_cmd_free cmd_free = { .size = sizeof(cmd_free) }; + struct kdbus_cmd_hello *hello; + struct kdbus_item *n; + size_t policy_cnt = 0; + BusNamePolicy *po; + size_t size; + int r; + + assert(fd >= 0); + assert(name); + + LIST_FOREACH(policy, po, policy) + policy_cnt++; + + if (world_policy >= 0) + policy_cnt++; + + size = offsetof(struct kdbus_cmd_hello, items) + + ALIGN8(offsetof(struct kdbus_item, str) + strlen(name) + 1) + + policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); + + hello = alloca0_align(size, 8); + + n = hello->items; + strcpy(n->str, name); + n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; + n->type = KDBUS_ITEM_NAME; + n = KDBUS_ITEM_NEXT(n); + + LIST_FOREACH(policy, po, policy) { + n->type = KDBUS_ITEM_POLICY_ACCESS; + n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); + + r = bus_kernel_translate_policy(po, n); + if (r < 0) + return r; + + n = KDBUS_ITEM_NEXT(n); + } + + if (world_policy >= 0) { + n->type = KDBUS_ITEM_POLICY_ACCESS; + n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); + n->policy_access.type = KDBUS_POLICY_ACCESS_WORLD; + n->policy_access.access = bus_kernel_translate_access(world_policy); + } + + hello->size = size; + hello->flags = + (activating ? KDBUS_HELLO_ACTIVATOR : KDBUS_HELLO_POLICY_HOLDER) | + (accept_fd ? KDBUS_HELLO_ACCEPT_FD : 0); + hello->pool_size = KDBUS_POOL_SIZE; + hello->attach_flags_send = _KDBUS_ATTACH_ANY; + hello->attach_flags_recv = _KDBUS_ATTACH_ANY; + + if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) { + if (errno == ENOTTY) /* Major API change */ + return -ESOCKTNOSUPPORT; + return -errno; + } + + /* not interested in any output values */ + cmd_free.offset = hello->offset; + (void) ioctl(fd, KDBUS_CMD_FREE, &cmd_free); + + /* The higher 32bit of the bus_flags fields are considered + * 'incompatible flags'. Refuse them all for now. */ + if (hello->bus_flags > 0xFFFFFFFFULL) + return -ESOCKTNOSUPPORT; + + return fd; +} + +static const char* const bus_policy_access_table[_BUS_POLICY_ACCESS_MAX] = { + [BUS_POLICY_ACCESS_SEE] = "see", + [BUS_POLICY_ACCESS_TALK] = "talk", + [BUS_POLICY_ACCESS_OWN] = "own", +}; + +DEFINE_STRING_TABLE_LOOKUP(bus_policy_access, BusPolicyAccess); diff --git a/src/grp-system/libcore/bus-policy.h b/src/grp-system/libcore/bus-policy.h new file mode 100644 index 0000000000..8cb6910c00 --- /dev/null +++ b/src/grp-system/libcore/bus-policy.h @@ -0,0 +1,64 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Daniel Mack + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" +#include "basic/macro.h" +#include "sd-bus/kdbus.h" + +typedef struct BusNamePolicy BusNamePolicy; + +typedef enum BusPolicyAccess { + BUS_POLICY_ACCESS_SEE, + BUS_POLICY_ACCESS_TALK, + BUS_POLICY_ACCESS_OWN, + _BUS_POLICY_ACCESS_MAX, + _BUS_POLICY_ACCESS_INVALID = -1 +} BusPolicyAccess; + +typedef enum BusNamePolicyType { + BUSNAME_POLICY_TYPE_USER, + BUSNAME_POLICY_TYPE_GROUP, + _BUSNAME_POLICY_TYPE_MAX, + _BUSNAME_POLICY_TYPE_INVALID = -1 +} BusNamePolicyType; + +struct BusNamePolicy { + BusNamePolicyType type; + BusPolicyAccess access; + + char *name; + + LIST_FIELDS(BusNamePolicy, policy); +}; + +int bus_kernel_translate_access(BusPolicyAccess access); +int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item); + +const char* bus_policy_access_to_string(BusPolicyAccess i) _const_; +BusPolicyAccess bus_policy_access_from_string(const char *s) _pure_; + +int bus_kernel_make_starter( + int fd, + const char *name, + bool activating, + bool accept_fd, + BusNamePolicy *policy, + BusPolicyAccess world_policy); diff --git a/src/grp-system/libcore/busname.c b/src/grp-system/libcore/busname.c new file mode 100644 index 0000000000..f21a2d23c1 --- /dev/null +++ b/src/grp-system/libcore/busname.c @@ -0,0 +1,1078 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "sd-bus/bus-internal.h" +#include "sd-bus/bus-kernel.h" +#include "sd-bus/kdbus.h" +#include "shared/bus-util.h" + +#include "bus-policy.h" +#include "busname.h" +#include "dbus-busname.h" +#include "service.h" + +static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = { + [BUSNAME_DEAD] = UNIT_INACTIVE, + [BUSNAME_MAKING] = UNIT_ACTIVATING, + [BUSNAME_REGISTERED] = UNIT_ACTIVE, + [BUSNAME_LISTENING] = UNIT_ACTIVE, + [BUSNAME_RUNNING] = UNIT_ACTIVE, + [BUSNAME_SIGTERM] = UNIT_DEACTIVATING, + [BUSNAME_SIGKILL] = UNIT_DEACTIVATING, + [BUSNAME_FAILED] = UNIT_FAILED +}; + +static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + +static void busname_init(Unit *u) { + BusName *n = BUSNAME(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + n->starter_fd = -1; + n->accept_fd = true; + n->activating = true; + + n->timeout_usec = u->manager->default_timeout_start_usec; +} + +static void busname_unwatch_control_pid(BusName *n) { + assert(n); + + if (n->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(n), n->control_pid); + n->control_pid = 0; +} + +static void busname_free_policy(BusName *n) { + BusNamePolicy *p; + + assert(n); + + while ((p = n->policy)) { + LIST_REMOVE(policy, n->policy, p); + + free(p->name); + free(p); + } +} + +static void busname_close_fd(BusName *n) { + assert(n); + + n->starter_event_source = sd_event_source_unref(n->starter_event_source); + n->starter_fd = safe_close(n->starter_fd); +} + +static void busname_done(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + n->name = mfree(n->name); + + busname_free_policy(n); + busname_unwatch_control_pid(n); + busname_close_fd(n); + + unit_ref_unset(&n->service); + + n->timer_event_source = sd_event_source_unref(n->timer_event_source); +} + +static int busname_arm_timer(BusName *n, usec_t usec) { + int r; + + assert(n); + + if (n->timer_event_source) { + r = sd_event_source_set_time(n->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(n)->manager->event, + &n->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + busname_dispatch_timer, n); + if (r < 0) + return r; + + (void) sd_event_source_set_description(n->timer_event_source, "busname-timer"); + + return 0; +} + +static int busname_add_default_default_dependencies(BusName *n) { + int r; + + assert(n); + + r = unit_add_dependency_by_name(UNIT(n), UNIT_BEFORE, SPECIAL_BUSNAMES_TARGET, NULL, true); + if (r < 0) + return r; + + if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) { + r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) + return r; + } + + return unit_add_two_dependencies_by_name(UNIT(n), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); +} + +static int busname_add_extras(BusName *n) { + Unit *u = UNIT(n); + int r; + + assert(n); + + if (!n->name) { + r = unit_name_to_prefix(u->id, &n->name); + if (r < 0) + return r; + } + + if (!u->description) { + r = unit_set_description(u, n->name); + if (r < 0) + return r; + } + + if (n->activating) { + if (!UNIT_DEREF(n->service)) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) + return r; + + unit_ref_set(&n->service, x); + } + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(n->service), true); + if (r < 0) + return r; + } + + if (u->default_dependencies) { + r = busname_add_default_default_dependencies(n); + if (r < 0) + return r; + } + + return 0; +} + +static int busname_verify(BusName *n) { + char *e; + + assert(n); + + if (UNIT(n)->load_state != UNIT_LOADED) + return 0; + + if (!service_name_is_valid(n->name)) { + log_unit_error(UNIT(n), "Name= setting is not a valid service name Refusing."); + return -EINVAL; + } + + e = strjoina(n->name, ".busname"); + if (!unit_has_name(UNIT(n), e)) { + log_unit_error(UNIT(n), "Name= setting doesn't match unit name. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int busname_load(Unit *u) { + BusName *n = BUSNAME(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + r = unit_load_fragment_and_dropin(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + /* This is a new unit? Then let's add in some extras */ + r = busname_add_extras(n); + if (r < 0) + return r; + } + + return busname_verify(n); +} + +static void busname_dump(Unit *u, FILE *f, const char *prefix) { + BusName *n = BUSNAME(u); + + assert(n); + assert(f); + + fprintf(f, + "%sBus Name State: %s\n" + "%sResult: %s\n" + "%sName: %s\n" + "%sActivating: %s\n" + "%sAccept FD: %s\n", + prefix, busname_state_to_string(n->state), + prefix, busname_result_to_string(n->result), + prefix, n->name, + prefix, yes_no(n->activating), + prefix, yes_no(n->accept_fd)); + + if (n->control_pid > 0) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, n->control_pid); +} + +static void busname_unwatch_fd(BusName *n) { + int r; + + assert(n); + + if (!n->starter_event_source) + return; + + r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_OFF); + if (r < 0) + log_unit_debug_errno(UNIT(n), r, "Failed to disable event source: %m"); +} + +static int busname_watch_fd(BusName *n) { + int r; + + assert(n); + + if (n->starter_fd < 0) + return 0; + + if (n->starter_event_source) { + r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON); + if (r < 0) + goto fail; + } else { + r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(n->starter_event_source, "busname-starter"); + } + + return 0; + +fail: + log_unit_warning_errno(UNIT(n), r, "Failed to watch starter fd: %m"); + busname_unwatch_fd(n); + return r; +} + +static int busname_open_fd(BusName *n) { + _cleanup_free_ char *path = NULL; + const char *mode; + + assert(n); + + if (n->starter_fd >= 0) + return 0; + + mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user"; + n->starter_fd = bus_kernel_open_bus_fd(mode, &path); + if (n->starter_fd < 0) + return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus"); + + return 0; +} + +static void busname_set_state(BusName *n, BusNameState state) { + BusNameState old_state; + assert(n); + + old_state = n->state; + n->state = state; + + if (!IN_SET(state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { + n->timer_event_source = sd_event_source_unref(n->timer_event_source); + busname_unwatch_control_pid(n); + } + + if (state != BUSNAME_LISTENING) + busname_unwatch_fd(n); + + if (!IN_SET(state, BUSNAME_LISTENING, BUSNAME_MAKING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) + busname_close_fd(n); + + if (state != old_state) + log_unit_debug(UNIT(n), "Changed %s -> %s", busname_state_to_string(old_state), busname_state_to_string(state)); + + unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true); +} + +static int busname_coldplug(Unit *u) { + BusName *n = BUSNAME(u); + int r; + + assert(n); + assert(n->state == BUSNAME_DEAD); + + if (n->deserialized_state == n->state) + return 0; + + 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) + return r; + + r = busname_arm_timer(n, usec_add(u->state_change_timestamp.monotonic, n->timeout_usec)); + if (r < 0) + return r; + } + + if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_LISTENING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) { + r = busname_open_fd(n); + if (r < 0) + return r; + } + + if (n->deserialized_state == BUSNAME_LISTENING) { + r = busname_watch_fd(n); + if (r < 0) + return r; + } + + busname_set_state(n, n->deserialized_state); + return 0; +} + +static int busname_make_starter(BusName *n, pid_t *_pid) { + pid_t pid; + int r; + + r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec)); + if (r < 0) + goto fail; + + /* We have to resolve the user/group names out-of-process, + * hence let's fork here. It's messy, but well, what can we + * do? */ + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + int ret; + + (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + (void) ignore_signals(SIGPIPE, -1); + log_forget_fds(); + + r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, n->policy, n->policy_world); + if (r < 0) { + ret = EXIT_MAKE_STARTER; + goto fail_child; + } + + _exit(0); + + fail_child: + log_open(); + log_error_errno(r, "Failed to create starter connection at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); + + _exit(ret); + } + + r = unit_watch_pid(UNIT(n), pid); + if (r < 0) + goto fail; + + *_pid = pid; + return 0; + +fail: + n->timer_event_source = sd_event_source_unref(n->timer_event_source); + return r; +} + +static void busname_enter_dead(BusName *n, BusNameResult f) { + assert(n); + + if (f != BUSNAME_SUCCESS) + n->result = f; + + busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); +} + +static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f) { + KillContext kill_context = {}; + int r; + + assert(n); + + if (f != BUSNAME_SUCCESS) + n->result = f; + + kill_context_init(&kill_context); + + r = unit_kill_context(UNIT(n), + &kill_context, + state != BUSNAME_SIGTERM ? KILL_KILL : KILL_TERMINATE, + -1, + n->control_pid, + false); + if (r < 0) { + log_unit_warning_errno(UNIT(n), r, "Failed to kill control process: %m"); + goto fail; + } + + if (r > 0) { + r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec)); + if (r < 0) { + log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m"); + goto fail; + } + + busname_set_state(n, state); + } else if (state == BUSNAME_SIGTERM) + busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_SUCCESS); + else + busname_enter_dead(n, BUSNAME_SUCCESS); + + return; + +fail: + busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); +} + +static void busname_enter_listening(BusName *n) { + int r; + + assert(n); + + if (n->activating) { + r = busname_watch_fd(n); + if (r < 0) { + log_unit_warning_errno(UNIT(n), r, "Failed to watch names: %m"); + goto fail; + } + + busname_set_state(n, BUSNAME_LISTENING); + } else + busname_set_state(n, BUSNAME_REGISTERED); + + return; + +fail: + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_RESOURCES); +} + +static void busname_enter_making(BusName *n) { + int r; + + assert(n); + + r = busname_open_fd(n); + if (r < 0) + goto fail; + + if (n->policy) { + /* If there is a policy, we need to resolve user/group + * names, which we can't do from PID1, hence let's + * fork. */ + busname_unwatch_control_pid(n); + + r = busname_make_starter(n, &n->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(n), r, "Failed to fork 'making' task: %m"); + goto fail; + } + + busname_set_state(n, BUSNAME_MAKING); + } else { + /* If there is no policy, we can do everything + * directly from PID 1, hence do so. */ + + r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world); + if (r < 0) { + log_unit_warning_errno(UNIT(n), r, "Failed to make starter: %m"); + goto fail; + } + + busname_enter_listening(n); + } + + return; + +fail: + busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); +} + +static void busname_enter_running(BusName *n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + bool pending = false; + Unit *other; + Iterator i; + int r; + + assert(n); + + if (!n->activating) + return; + + /* We don't take connections anymore if we are supposed to + * shut down anyway */ + + if (unit_stop_pending(UNIT(n))) { + log_unit_debug(UNIT(n), "Suppressing activation request since unit stop is scheduled."); + + /* Flush all queued activation reqeuest by closing and reopening the connection */ + bus_kernel_drop_one(n->starter_fd); + + busname_enter_listening(n); + return; + } + + /* If there's already a start pending don't bother to do + * anything */ + SET_FOREACH(other, UNIT(n)->dependencies[UNIT_TRIGGERS], i) + if (unit_active_or_pending(other)) { + pending = true; + break; + } + + if (!pending) { + 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; + } + + busname_set_state(n, BUSNAME_RUNNING); + return; + +fail: + log_unit_warning(UNIT(n), "Failed to queue service startup job: %s", bus_error_message(&error, r)); + busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); +} + +static int busname_start(Unit *u) { + BusName *n = BUSNAME(u); + int r; + + assert(n); + + /* We cannot fulfill this request right now, try again later + * please! */ + if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) + return -EAGAIN; + + /* Already on it! */ + if (n->state == BUSNAME_MAKING) + return 0; + + if (n->activating && UNIT_ISSET(n->service)) { + Service *service; + + service = SERVICE(UNIT_DEREF(n->service)); + + if (UNIT(service)->load_state != UNIT_LOADED) { + log_unit_error(u, "Bus service %s not loaded, refusing.", UNIT(service)->id); + return -ENOENT; + } + } + + assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); + + r = unit_start_limit_test(u); + if (r < 0) { + busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT); + return r; + } + + n->result = BUSNAME_SUCCESS; + busname_enter_making(n); + + return 1; +} + +static int busname_stop(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + /* Already on it */ + if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) + return 0; + + /* If there's already something running, we go directly into + * kill mode. */ + + if (n->state == BUSNAME_MAKING) { + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_SUCCESS); + return -EAGAIN; + } + + assert(IN_SET(n->state, BUSNAME_REGISTERED, BUSNAME_LISTENING, BUSNAME_RUNNING)); + + busname_enter_dead(n, BUSNAME_SUCCESS); + return 1; +} + +static int busname_serialize(Unit *u, FILE *f, FDSet *fds) { + BusName *n = BUSNAME(u); + int r; + + assert(n); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", busname_state_to_string(n->state)); + unit_serialize_item(u, f, "result", busname_result_to_string(n->result)); + + if (n->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid); + + r = unit_serialize_item_fd(u, f, fds, "starter-fd", n->starter_fd); + if (r < 0) + return r; + + return 0; +} + +static int busname_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + BusName *n = BUSNAME(u); + + assert(n); + assert(key); + assert(value); + + if (streq(key, "state")) { + BusNameState state; + + state = busname_state_from_string(value); + if (state < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + n->deserialized_state = state; + + } else if (streq(key, "result")) { + BusNameResult f; + + f = busname_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != BUSNAME_SUCCESS) + n->result = f; + + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse control-pid value: %s", value); + else + n->control_pid = pid; + } else if (streq(key, "starter-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse starter fd value: %s", value); + else { + safe_close(n->starter_fd); + n->starter_fd = fdset_remove(fds, fd); + } + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState busname_active_state(Unit *u) { + assert(u); + + return state_translation_table[BUSNAME(u)->state]; +} + +_pure_ static const char *busname_sub_state_to_string(Unit *u) { + assert(u); + + return busname_state_to_string(BUSNAME(u)->state); +} + +static int busname_peek_message(BusName *n) { + struct kdbus_cmd_recv cmd_recv = { + .size = sizeof(cmd_recv), + .flags = KDBUS_RECV_PEEK, + }; + struct kdbus_cmd_free cmd_free = { + .size = sizeof(cmd_free), + }; + const char *comm = NULL; + struct kdbus_item *d; + struct kdbus_msg *k; + size_t start, ps, sz, delta; + void *p = NULL; + pid_t pid = 0; + int r; + + /* Generate a friendly debug log message about which process + * caused triggering of this bus name. This simply peeks the + * metadata of the first queued message and logs it. */ + + assert(n); + + /* Let's shortcut things a bit, if debug logging is turned off + * anyway. */ + + if (log_get_max_level() < LOG_DEBUG) + return 0; + + r = ioctl(n->starter_fd, KDBUS_CMD_RECV, &cmd_recv); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return log_unit_error_errno(UNIT(n), errno, "Failed to query activation message: %m"); + } + + /* We map as late as possible, and unmap imemdiately after + * use. On 32bit address space is scarce and we want to be + * able to handle a lot of activator connections at the same + * time, and hence shouldn't keep the mmap()s around for + * longer than necessary. */ + + ps = page_size(); + start = (cmd_recv.msg.offset / ps) * ps; + delta = cmd_recv.msg.offset - start; + sz = PAGE_ALIGN(delta + cmd_recv.msg.msg_size); + + p = mmap(NULL, sz, PROT_READ, MAP_SHARED, n->starter_fd, start); + if (p == MAP_FAILED) { + r = log_unit_error_errno(UNIT(n), errno, "Failed to map activation message: %m"); + goto finish; + } + + k = (struct kdbus_msg *) ((uint8_t *) p + delta); + KDBUS_ITEM_FOREACH(d, k, items) { + switch (d->type) { + + case KDBUS_ITEM_PIDS: + pid = d->pids.pid; + break; + + case KDBUS_ITEM_PID_COMM: + comm = d->str; + break; + } + } + + if (pid > 0) + log_unit_debug(UNIT(n), "Activation triggered by process " PID_FMT " (%s)", pid, strna(comm)); + + r = 0; + +finish: + if (p) + (void) munmap(p, sz); + + cmd_free.offset = cmd_recv.msg.offset; + if (ioctl(n->starter_fd, KDBUS_CMD_FREE, &cmd_free) < 0) + log_unit_warning(UNIT(n), "Failed to free peeked message, ignoring: %m"); + + return r; +} + +static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + BusName *n = userdata; + + assert(n); + assert(fd >= 0); + + if (n->state != BUSNAME_LISTENING) + return 0; + + log_unit_debug(UNIT(n), "Activation request"); + + if (revents != EPOLLIN) { + log_unit_error(UNIT(n), "Got unexpected poll event (0x%x) on starter fd.", revents); + goto fail; + } + + busname_peek_message(n); + busname_enter_running(n); + return 0; +fail: + + busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); + return 0; +} + +static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { + BusName *n = BUSNAME(u); + BusNameResult f; + + assert(n); + assert(pid >= 0); + + if (pid != n->control_pid) + return; + + n->control_pid = 0; + + if (is_clean_exit(code, status, NULL)) + f = BUSNAME_SUCCESS; + else if (code == CLD_EXITED) + f = BUSNAME_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = BUSNAME_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = BUSNAME_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown sigchld code"); + + log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, + "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); + + if (f != BUSNAME_SUCCESS) + n->result = f; + + switch (n->state) { + + case BUSNAME_MAKING: + if (f == BUSNAME_SUCCESS) + busname_enter_listening(n); + else + busname_enter_signal(n, BUSNAME_SIGTERM, f); + break; + + case BUSNAME_SIGTERM: + case BUSNAME_SIGKILL: + busname_enter_dead(n, f); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); +} + +static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + BusName *n = BUSNAME(userdata); + + assert(n); + assert(n->timer_event_source == source); + + switch (n->state) { + + case BUSNAME_MAKING: + log_unit_warning(UNIT(n), "Making timed out. Terminating."); + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_TIMEOUT); + break; + + case BUSNAME_SIGTERM: + log_unit_warning(UNIT(n), "Stopping timed out. Killing."); + busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_FAILURE_TIMEOUT); + break; + + case BUSNAME_SIGKILL: + log_unit_warning(UNIT(n), "Processes still around after SIGKILL. Ignoring."); + busname_enter_dead(n, BUSNAME_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +static void busname_reset_failed(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + if (n->state == BUSNAME_FAILED) + busname_set_state(n, BUSNAME_DEAD); + + n->result = BUSNAME_SUCCESS; +} + +static void busname_trigger_notify(Unit *u, Unit *other) { + BusName *n = BUSNAME(u); + + assert(n); + assert(other); + + if (!IN_SET(n->state, BUSNAME_RUNNING, BUSNAME_LISTENING)) + return; + + if (other->start_limit_hit) { + busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT); + return; + } + + if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + return; + + if (IN_SET(SERVICE(other)->state, + SERVICE_DEAD, SERVICE_FAILED, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, + SERVICE_AUTO_RESTART)) + busname_enter_listening(n); + + if (SERVICE(other)->state == SERVICE_RUNNING) + busname_set_state(n, BUSNAME_RUNNING); +} + +static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, BUSNAME(u)->control_pid, error); +} + +static int busname_get_timeout(Unit *u, usec_t *timeout) { + BusName *n = BUSNAME(u); + usec_t t; + int r; + + if (!n->timer_event_source) + return 0; + + r = sd_event_source_get_time(n->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +static bool busname_supported(void) { + return false; +} + +static int busname_control_pid(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + return n->control_pid; +} + +static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { + [BUSNAME_SUCCESS] = "success", + [BUSNAME_FAILURE_RESOURCES] = "resources", + [BUSNAME_FAILURE_TIMEOUT] = "timeout", + [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", + [BUSNAME_FAILURE_SIGNAL] = "signal", + [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", + [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(busname_result, BusNameResult); + +const UnitVTable busname_vtable = { + .object_size = sizeof(BusName), + + .sections = + "Unit\0" + "BusName\0" + "Install\0", + .private_section = "BusName", + + .init = busname_init, + .done = busname_done, + .load = busname_load, + + .coldplug = busname_coldplug, + + .dump = busname_dump, + + .start = busname_start, + .stop = busname_stop, + + .kill = busname_kill, + + .get_timeout = busname_get_timeout, + + .serialize = busname_serialize, + .deserialize_item = busname_deserialize_item, + + .active_state = busname_active_state, + .sub_state_to_string = busname_sub_state_to_string, + + .sigchld_event = busname_sigchld_event, + + .trigger_notify = busname_trigger_notify, + + .reset_failed = busname_reset_failed, + + .supported = busname_supported, + + .control_pid = busname_control_pid, + + .bus_vtable = bus_busname_vtable, + + .status_message_formats = { + .finished_start_job = { + [JOB_DONE] = "Listening on %s.", + [JOB_FAILED] = "Failed to listen on %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Closed %s.", + [JOB_FAILED] = "Failed stopping %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/busname.h b/src/grp-system/libcore/busname.h new file mode 100644 index 0000000000..aa7f0ecb1b --- /dev/null +++ b/src/grp-system/libcore/busname.h @@ -0,0 +1,69 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct BusName BusName; +typedef struct BusNamePolicy BusNamePolicy; + +#include "bus-policy.h" +#include "unit.h" + +typedef enum BusNameResult { + BUSNAME_SUCCESS, + BUSNAME_FAILURE_RESOURCES, + BUSNAME_FAILURE_TIMEOUT, + BUSNAME_FAILURE_EXIT_CODE, + BUSNAME_FAILURE_SIGNAL, + BUSNAME_FAILURE_CORE_DUMP, + BUSNAME_FAILURE_START_LIMIT_HIT, + BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT, + _BUSNAME_RESULT_MAX, + _BUSNAME_RESULT_INVALID = -1 +} BusNameResult; + +struct BusName { + Unit meta; + + char *name; + int starter_fd; + + bool activating; + bool accept_fd; + + UnitRef service; + + BusNameState state, deserialized_state; + BusNameResult result; + + usec_t timeout_usec; + + sd_event_source *starter_event_source; + sd_event_source *timer_event_source; + + pid_t control_pid; + + LIST_HEAD(BusNamePolicy, policy); + BusPolicyAccess policy_world; +}; + +extern const UnitVTable busname_vtable; + +const char* busname_result_to_string(BusNameResult i) _const_; +BusNameResult busname_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/cgroup.c b/src/grp-system/libcore/cgroup.c new file mode 100644 index 0000000000..0c1365f329 --- /dev/null +++ b/src/grp-system/libcore/cgroup.c @@ -0,0 +1,1992 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" + +#include "cgroup.h" + +#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) + +static void cgroup_compat_warn(void) { + static bool cgroup_compat_warned = false; + + if (cgroup_compat_warned) + return; + + log_warning("cgroup compatibility translation between legacy and unified hierarchy settings activated. See cgroup-compat debug messages for details."); + cgroup_compat_warned = true; +} + +#define log_cgroup_compat(unit, fmt, ...) do { \ + cgroup_compat_warn(); \ + log_unit_debug(unit, "cgroup-compat: " fmt, ##__VA_ARGS__); \ + } while (false) + +void cgroup_context_init(CGroupContext *c) { + assert(c); + + /* Initialize everything to the kernel defaults, assuming the + * structure is preinitialized to 0 */ + + c->cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->cpu_quota_per_sec_usec = USEC_INFINITY; + + c->memory_high = CGROUP_LIMIT_MAX; + c->memory_max = CGROUP_LIMIT_MAX; + + c->memory_limit = CGROUP_LIMIT_MAX; + + c->io_weight = CGROUP_WEIGHT_INVALID; + c->startup_io_weight = CGROUP_WEIGHT_INVALID; + + c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + + c->tasks_max = (uint64_t) -1; +} + +void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { + assert(c); + assert(a); + + LIST_REMOVE(device_allow, c->device_allow, a); + free(a->path); + free(a); +} + +void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w) { + assert(c); + assert(w); + + LIST_REMOVE(device_weights, c->io_device_weights, w); + free(w->path); + free(w); +} + +void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) { + assert(c); + assert(l); + + LIST_REMOVE(device_limits, c->io_device_limits, l); + free(l->path); + free(l); +} + +void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) { + assert(c); + assert(w); + + LIST_REMOVE(device_weights, c->blockio_device_weights, w); + free(w->path); + free(w); +} + +void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) { + assert(c); + assert(b); + + LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b); + free(b->path); + free(b); +} + +void cgroup_context_done(CGroupContext *c) { + assert(c); + + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + + while (c->io_device_limits) + cgroup_context_free_io_device_limit(c, c->io_device_limits); + + while (c->blockio_device_weights) + cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); + + while (c->blockio_device_bandwidths) + cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths); + + while (c->device_allow) + cgroup_context_free_device_allow(c, c->device_allow); +} + +void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + CGroupIODeviceLimit *il; + CGroupIODeviceWeight *iw; + CGroupBlockIODeviceBandwidth *b; + CGroupBlockIODeviceWeight *w; + CGroupDeviceAllow *a; + char u[FORMAT_TIMESPAN_MAX]; + + assert(c); + assert(f); + + prefix = strempty(prefix); + + fprintf(f, + "%sCPUAccounting=%s\n" + "%sIOAccounting=%s\n" + "%sBlockIOAccounting=%s\n" + "%sMemoryAccounting=%s\n" + "%sTasksAccounting=%s\n" + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" + "%sCPUQuotaPerSecSec=%s\n" + "%sIOWeight=%" PRIu64 "\n" + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" + "%sMemoryLow=%" PRIu64 "\n" + "%sMemoryHigh=%" PRIu64 "\n" + "%sMemoryMax=%" PRIu64 "\n" + "%sMemoryLimit=%" PRIu64 "\n" + "%sTasksMax=%" PRIu64 "\n" + "%sDevicePolicy=%s\n" + "%sDelegate=%s\n", + prefix, yes_no(c->cpu_accounting), + prefix, yes_no(c->io_accounting), + prefix, yes_no(c->blockio_accounting), + prefix, yes_no(c->memory_accounting), + prefix, yes_no(c->tasks_accounting), + prefix, c->cpu_shares, + prefix, c->startup_cpu_shares, + prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), + prefix, c->io_weight, + prefix, c->startup_io_weight, + prefix, c->blockio_weight, + prefix, c->startup_blockio_weight, + prefix, c->memory_low, + prefix, c->memory_high, + prefix, c->memory_max, + prefix, c->memory_limit, + prefix, c->tasks_max, + prefix, cgroup_device_policy_to_string(c->device_policy), + prefix, yes_no(c->delegate)); + + LIST_FOREACH(device_allow, a, c->device_allow) + fprintf(f, + "%sDeviceAllow=%s %s%s%s\n", + prefix, + a->path, + a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); + + LIST_FOREACH(device_weights, iw, c->io_device_weights) + fprintf(f, + "%sIODeviceWeight=%s %" PRIu64, + prefix, + iw->path, + iw->weight); + + LIST_FOREACH(device_limits, il, c->io_device_limits) { + char buf[FORMAT_BYTES_MAX]; + CGroupIOLimitType type; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + if (il->limits[type] != cgroup_io_limit_defaults[type]) + fprintf(f, + "%s%s=%s %s\n", + prefix, + cgroup_io_limit_type_to_string(type), + il->path, + format_bytes(buf, sizeof(buf), il->limits[type])); + } + + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + fprintf(f, + "%sBlockIODeviceWeight=%s %" PRIu64, + prefix, + w->path, + w->weight); + + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + char buf[FORMAT_BYTES_MAX]; + + if (b->rbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOReadBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->rbps)); + if (b->wbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOWriteBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->wbps)); + } +} + +static int lookup_block_device(const char *p, dev_t *dev) { + struct stat st; + int r; + + assert(p); + assert(dev); + + r = stat(p, &st); + if (r < 0) + return log_warning_errno(errno, "Couldn't stat device %s: %m", p); + + if (S_ISBLK(st.st_mode)) + *dev = st.st_rdev; + else if (major(st.st_dev) != 0) { + /* If this is not a device node then find the block + * device this file is stored on */ + *dev = st.st_dev; + + /* If this is a partition, try to get the originating + * block device */ + block_get_whole_disk(*dev, dev); + } else { + log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p); + return -ENODEV; + } + + return 0; +} + +static int whitelist_device(const char *path, const char *node, const char *acc) { + char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4]; + struct stat st; + int r; + + assert(path); + assert(acc); + + if (stat(node, &st) < 0) { + log_warning("Couldn't stat device %s", node); + return -errno; + } + + if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { + log_warning("%s is not a device.", node); + return -ENODEV; + } + + sprintf(buf, + "%c %u:%u %s", + S_ISCHR(st.st_mode) ? 'c' : 'b', + major(st.st_rdev), minor(st.st_rdev), + acc); + + r = cg_set_attribute("devices", path, "devices.allow", buf); + if (r < 0) + 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; +} + +static int whitelist_major(const char *path, const char *name, char type, const char *acc) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + bool good = false; + int r; + + assert(path); + assert(acc); + assert(type == 'b' || type == 'c'); + + f = fopen("/proc/devices", "re"); + if (!f) + return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type); + + FOREACH_LINE(line, f, goto fail) { + char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w; + unsigned maj; + + truncate_nl(line); + + if (type == 'c' && streq(line, "Character devices:")) { + good = true; + continue; + } + + if (type == 'b' && streq(line, "Block devices:")) { + good = true; + continue; + } + + if (isempty(line)) { + good = false; + continue; + } + + if (!good) + continue; + + p = strstrip(line); + + w = strpbrk(p, WHITESPACE); + if (!w) + continue; + *w = 0; + + r = safe_atou(p, &maj); + if (r < 0) + continue; + if (maj <= 0) + continue; + + w++; + w += strspn(w, WHITESPACE); + + if (fnmatch(name, w, 0) != 0) + continue; + + sprintf(buf, + "%c %u:* %s", + type, + maj, + acc); + + r = cg_set_attribute("devices", path, "devices.allow", buf); + if (r < 0) + 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 0; + +fail: + log_warning_errno(errno, "Failed to read /proc/devices: %m"); + return -errno; +} + +static bool cgroup_context_has_io_config(CGroupContext *c) { + return c->io_accounting || + c->io_weight != CGROUP_WEIGHT_INVALID || + c->startup_io_weight != CGROUP_WEIGHT_INVALID || + c->io_device_weights || + c->io_device_limits; +} + +static bool cgroup_context_has_blockio_config(CGroupContext *c) { + return c->blockio_accounting || + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->blockio_device_weights || + c->blockio_device_bandwidths; +} + +static uint64_t cgroup_context_io_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_io_weight != CGROUP_WEIGHT_INVALID) + return c->startup_io_weight; + else if (c->io_weight != CGROUP_WEIGHT_INVALID) + return c->io_weight; + else + return CGROUP_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_context_blkio_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->startup_blockio_weight; + else if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->blockio_weight; + else + return CGROUP_BLKIO_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) { + return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT, + CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX); +} + +static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) { + return CLAMP(io_weight * CGROUP_BLKIO_WEIGHT_DEFAULT / CGROUP_WEIGHT_DEFAULT, + CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX); +} + +static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_t io_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight); + r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.weight: %m"); +} + +static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight_device: %m"); +} + +static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { + char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; + char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; + CGroupIOLimitType type; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { + if (limits[type] != cgroup_io_limit_defaults[type]) { + xsprintf(limit_bufs[type], "%" PRIu64, limits[type]); + n++; + } else { + xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0"); + } + } + + xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev), + limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX], + limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]); + r = cg_set_attribute("io", u->cgroup_path, "io.max", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.max: %m"); + return n; +} + +static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + if (rbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.read_bps_device: %m"); + + if (wbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps); + r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.write_bps_device: %m"); + + return n; +} + +static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { + return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX; +} + +static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { + char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max"; + int r; + + if (v != CGROUP_LIMIT_MAX) + xsprintf(buf, "%" PRIu64 "\n", v); + + r = cg_set_attribute("memory", u->cgroup_path, file, buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set %s: %m", file); +} + +static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) { + const char *path; + CGroupContext *c; + bool is_root; + int r; + + assert(u); + + c = unit_get_cgroup_context(u); + path = u->cgroup_path; + + assert(c); + assert(path); + + if (mask == 0) + return; + + /* Some cgroup attributes are not supported on the root cgroup, + * hence silently ignore */ + is_root = isempty(path) || path_equal(path, "/"); + if (is_root) + /* Make sure we don't try to display messages with an empty path. */ + path = "/"; + + /* We generally ignore errors caused by read-only mounted + * cgroup trees (assuming we are running in a container then), + * and missing cgroups, i.e. EROFS and ENOENT. */ + + if ((mask & CGROUP_MASK_CPU) && !is_root) { + char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; + + sprintf(buf, "%" PRIu64 "\n", + IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares : + 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_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.shares: %m"); + + 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_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_period_us: %m"); + + if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { + sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC); + r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf); + } else + r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_quota_us: %m"); + } + + if (mask & CGROUP_MASK_IO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); + + if (!is_root) { + char buf[8+DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_io) + weight = cgroup_context_io_weight(c, state); + else if (has_blockio) { + uint64_t blkio_weight = cgroup_context_blkio_weight(c, state); + + weight = cgroup_weight_blkio_to_io(blkio_weight); + + log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64, + blkio_weight, weight); + } else + weight = CGROUP_WEIGHT_DEFAULT; + + xsprintf(buf, "default %" PRIu64 "\n", weight); + r = cg_set_attribute("io", path, "io.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.weight: %m"); + + if (has_io) { + CGroupIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) + cgroup_apply_io_device_weight(u, w->path, w->weight); + } else if (has_blockio) { + CGroupBlockIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) { + weight = cgroup_weight_blkio_to_io(w->weight); + + log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s", + w->weight, weight, w->path); + + cgroup_apply_io_device_weight(u, w->path, weight); + } + } + } + + /* Apply limits and free ones without config. */ + if (has_io) { + CGroupIODeviceLimit *l, *next; + + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + if (!cgroup_apply_io_device_limit(u, l->path, l->limits)) + cgroup_context_free_io_device_limit(c, l); + } + } else if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; + + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; + CGroupIOLimitType type; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + limits[type] = cgroup_io_limit_defaults[type]; + + limits[CGROUP_IO_RBPS_MAX] = b->rbps; + limits[CGROUP_IO_WBPS_MAX] = b->wbps; + + log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s", + b->rbps, b->wbps, b->path); + + if (!cgroup_apply_io_device_limit(u, b->path, limits)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } + } + + if (mask & CGROUP_MASK_BLKIO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); + + if (!is_root) { + char buf[DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_blockio) + weight = cgroup_context_blkio_weight(c, state); + else if (has_io) { + uint64_t io_weight = cgroup_context_io_weight(c, state); + + weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state)); + + log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64, + io_weight, weight); + } else + weight = CGROUP_BLKIO_WEIGHT_DEFAULT; + + xsprintf(buf, "%" PRIu64 "\n", weight); + r = cg_set_attribute("blkio", path, "blkio.weight", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight: %m"); + + if (has_blockio) { + CGroupBlockIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + cgroup_apply_blkio_device_weight(u, w->path, w->weight); + } else if (has_io) { + CGroupIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) { + weight = cgroup_weight_io_to_blkio(w->weight); + + log_cgroup_compat(u, "Applying IODeviceWeight %" PRIu64 " as BlockIODeviceWeight %" PRIu64 " for %s", + w->weight, weight, w->path); + + cgroup_apply_blkio_device_weight(u, w->path, weight); + } + } + } + + /* Apply limits and free ones without config. */ + if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; + + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } else if (has_io) { + CGroupIODeviceLimit *l, *next; + + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s", + l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path); + + if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX])) + cgroup_context_free_io_device_limit(c, l); + } + } + } + + if ((mask & CGROUP_MASK_MEMORY) && !is_root) { + if (cg_unified() > 0) { + uint64_t max = c->memory_max; + + if (cgroup_context_has_unified_memory_config(c)) + max = c->memory_max; + else { + max = c->memory_limit; + + if (max != CGROUP_LIMIT_MAX) + log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max); + } + + cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low); + cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); + cgroup_apply_unified_memory_limit(u, "memory.max", max); + } else { + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + uint64_t val = c->memory_limit; + + if (val == CGROUP_LIMIT_MAX) { + val = c->memory_max; + + if (val != CGROUP_LIMIT_MAX) + log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", c->memory_max); + } + + if (val == CGROUP_LIMIT_MAX) + strncpy(buf, "-1\n", sizeof(buf)); + else + xsprintf(buf, "%" PRIu64 "\n", val); + + r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set memory.limit_in_bytes: %m"); + } + } + + if ((mask & CGROUP_MASK_DEVICES) && !is_root) { + CGroupDeviceAllow *a; + + /* Changing the devices list of a populated cgroup + * might result in EINVAL, hence ignore EINVAL + * here. */ + + if (c->device_allow || c->device_policy != CGROUP_AUTO) + r = cg_set_attribute("devices", path, "devices.deny", "a"); + else + r = cg_set_attribute("devices", path, "devices.allow", "a"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to reset devices.list: %m"); + + if (c->device_policy == CGROUP_CLOSED || + (c->device_policy == CGROUP_AUTO && c->device_allow)) { + static const char auto_devices[] = + "/dev/null\0" "rwm\0" + "/dev/zero\0" "rwm\0" + "/dev/full\0" "rwm\0" + "/dev/random\0" "rwm\0" + "/dev/urandom\0" "rwm\0" + "/dev/tty\0" "rwm\0" + "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */ + /* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */ + "/run/systemd/inaccessible/chr\0" "rwm\0" + "/run/systemd/inaccessible/blk\0" "rwm\0"; + + const char *x, *y; + + NULSTR_FOREACH_PAIR(x, y, auto_devices) + whitelist_device(path, x, y); + + whitelist_major(path, "pts", 'c', "rw"); + whitelist_major(path, "kdbus", 'c', "rw"); + whitelist_major(path, "kdbus/*", 'c', "rw"); + } + + LIST_FOREACH(device_allow, a, c->device_allow) { + char acc[4]; + unsigned k = 0; + + if (a->r) + acc[k++] = 'r'; + if (a->w) + acc[k++] = 'w'; + if (a->m) + acc[k++] = 'm'; + + if (k == 0) + continue; + + acc[k++] = 0; + + if (startswith(a->path, "/dev/")) + whitelist_device(path, a->path, acc); + else if (startswith(a->path, "block-")) + whitelist_major(path, a->path + 6, 'b', acc); + else if (startswith(a->path, "char-")) + whitelist_major(path, a->path + 5, 'c', acc); + else + log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path); + } + } + + if ((mask & CGROUP_MASK_PIDS) && !is_root) { + + if (c->tasks_max != (uint64_t) -1) { + char buf[DECIMAL_STR_MAX(uint64_t) + 2]; + + sprintf(buf, "%" PRIu64 "\n", c->tasks_max); + r = cg_set_attribute("pids", path, "pids.max", buf); + } else + r = cg_set_attribute("pids", path, "pids.max", "max"); + + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max: %m"); + } +} + +CGroupMask cgroup_context_get_mask(CGroupContext *c) { + CGroupMask mask = 0; + + /* Figure out which controllers we need */ + + if (c->cpu_accounting || + c->cpu_shares != CGROUP_CPU_SHARES_INVALID || + c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID || + c->cpu_quota_per_sec_usec != USEC_INFINITY) + mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; + + if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c)) + mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; + + if (c->memory_accounting || + c->memory_limit != CGROUP_LIMIT_MAX || + cgroup_context_has_unified_memory_config(c)) + mask |= CGROUP_MASK_MEMORY; + + if (c->device_allow || + c->device_policy != CGROUP_AUTO) + mask |= CGROUP_MASK_DEVICES; + + if (c->tasks_accounting || + c->tasks_max != (uint64_t) -1) + mask |= CGROUP_MASK_PIDS; + + return mask; +} + +CGroupMask unit_get_own_mask(Unit *u) { + CGroupContext *c; + + /* Returns the mask of controllers the unit needs for itself */ + + c = unit_get_cgroup_context(u); + if (!c) + return 0; + + /* If delegation is turned on, then turn on all cgroups, + * unless we are on the legacy hierarchy and the process we + * fork into it is known to drop privileges, and hence + * shouldn't get access to the controllers. + * + * Note that on the unified hierarchy it is safe to delegate + * controllers to unprivileged services. */ + + if (c->delegate) { + ExecContext *e; + + e = unit_get_exec_context(u); + if (!e || + exec_context_maintains_privileges(e) || + cg_unified() > 0) + return _CGROUP_MASK_ALL; + } + + return cgroup_context_get_mask(c); +} + +CGroupMask unit_get_members_mask(Unit *u) { + assert(u); + + /* Returns the mask of controllers all of the unit's children + * require, merged */ + + if (u->cgroup_members_mask_valid) + return u->cgroup_members_mask; + + u->cgroup_members_mask = 0; + + if (u->type == UNIT_SLICE) { + Unit *member; + Iterator i; + + SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) { + + if (member == u) + continue; + + if (UNIT_DEREF(member->slice) != u) + continue; + + u->cgroup_members_mask |= + unit_get_own_mask(member) | + unit_get_members_mask(member); + } + } + + u->cgroup_members_mask_valid = true; + return u->cgroup_members_mask; +} + +CGroupMask unit_get_siblings_mask(Unit *u) { + assert(u); + + /* Returns the mask of controllers all of the unit's siblings + * require, i.e. the members mask of the unit's parent slice + * if there is one. */ + + if (UNIT_ISSET(u->slice)) + return unit_get_members_mask(UNIT_DEREF(u->slice)); + + return unit_get_own_mask(u) | unit_get_members_mask(u); +} + +CGroupMask unit_get_subtree_mask(Unit *u) { + + /* Returns the mask of this subtree, meaning of the group + * itself and its children. */ + + return unit_get_own_mask(u) | unit_get_members_mask(u); +} + +CGroupMask unit_get_target_mask(Unit *u) { + CGroupMask mask; + + /* This returns the cgroup mask of all controllers to enable + * for a specific cgroup, i.e. everything it needs itself, + * plus all that its children need, plus all that its siblings + * need. This is primarily useful on the legacy cgroup + * hierarchy, where we need to duplicate each cgroup in each + * hierarchy that shall be enabled for it. */ + + mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u); + mask &= u->manager->cgroup_supported; + + return mask; +} + +CGroupMask unit_get_enable_mask(Unit *u) { + CGroupMask mask; + + /* This returns the cgroup mask of all controllers to enable + * for the children of a specific cgroup. This is primarily + * useful for the unified cgroup hierarchy, where each cgroup + * controls which controllers are enabled for its children. */ + + mask = unit_get_members_mask(u); + mask &= u->manager->cgroup_supported; + + return mask; +} + +/* Recurse from a unit up through its containing slices, propagating + * mask bits upward. A unit is also member of itself. */ +void unit_update_cgroup_members_masks(Unit *u) { + CGroupMask m; + bool more; + + assert(u); + + /* Calculate subtree mask */ + m = unit_get_subtree_mask(u); + + /* See if anything changed from the previous invocation. If + * not, we're done. */ + if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask) + return; + + more = + u->cgroup_subtree_mask_valid && + ((m & ~u->cgroup_subtree_mask) != 0) && + ((~m & u->cgroup_subtree_mask) == 0); + + u->cgroup_subtree_mask = m; + u->cgroup_subtree_mask_valid = true; + + if (UNIT_ISSET(u->slice)) { + Unit *s = UNIT_DEREF(u->slice); + + if (more) + /* There's more set now than before. We + * propagate the new mask to the parent's mask + * (not caring if it actually was valid or + * not). */ + + s->cgroup_members_mask |= m; + + else + /* There's less set now than before (or we + * don't know), we need to recalculate + * everything, so let's invalidate the + * parent's members mask */ + + s->cgroup_members_mask_valid = false; + + /* And now make sure that this change also hits our + * grandparents */ + unit_update_cgroup_members_masks(s); + } +} + +static const char *migrate_callback(CGroupMask mask, void *userdata) { + Unit *u = userdata; + + assert(mask != 0); + assert(u); + + while (u) { + if (u->cgroup_path && + u->cgroup_realized && + (u->cgroup_realized_mask & mask) == mask) + return u->cgroup_path; + + u = UNIT_DEREF(u->slice); + } + + return NULL; +} + +char *unit_default_cgroup_path(Unit *u) { + _cleanup_free_ char *escaped = NULL, *slice = NULL; + int r; + + assert(u); + + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return strdup(u->manager->cgroup_root); + + if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) { + r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); + if (r < 0) + return NULL; + } + + escaped = cg_escape(u->id); + if (!escaped) + return NULL; + + if (slice) + return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL); + else + return strjoin(u->manager->cgroup_root, "/", escaped, NULL); +} + +int unit_set_cgroup_path(Unit *u, const char *path) { + _cleanup_free_ char *p = NULL; + int r; + + assert(u); + + if (path) { + p = strdup(path); + if (!p) + return -ENOMEM; + } else + p = NULL; + + if (streq_ptr(u->cgroup_path, p)) + return 0; + + if (p) { + r = hashmap_put(u->manager->cgroup_unit, p, u); + if (r < 0) + return r; + } + + unit_release_cgroup(u); + + u->cgroup_path = p; + p = NULL; + + return 1; +} + +int unit_watch_cgroup(Unit *u) { + _cleanup_free_ char *events = NULL; + int r; + + assert(u); + + if (!u->cgroup_path) + return 0; + + if (u->cgroup_inotify_wd >= 0) + return 0; + + /* Only applies to the unified hierarchy */ + r = cg_unified(); + if (r < 0) + return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m"); + if (r == 0) + return 0; + + /* Don't watch the root slice, it's pointless. */ + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return 0; + + r = hashmap_ensure_allocated(&u->manager->cgroup_inotify_wd_unit, &trivial_hash_ops); + if (r < 0) + return log_oom(); + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", &events); + if (r < 0) + return log_oom(); + + u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, events, IN_MODIFY); + if (u->cgroup_inotify_wd < 0) { + + if (errno == ENOENT) /* If the directory is already + * gone we don't need to track + * it, so this is not an error */ + return 0; + + return log_unit_error_errno(u, errno, "Failed to add inotify watch descriptor for control group %s: %m", u->cgroup_path); + } + + r = hashmap_put(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd), u); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to add inotify watch descriptor to hash map: %m"); + + return 0; +} + +static int unit_create_cgroup( + Unit *u, + CGroupMask target_mask, + CGroupMask enable_mask) { + + CGroupContext *c; + int r; + + assert(u); + + c = unit_get_cgroup_context(u); + if (!c) + return 0; + + if (!u->cgroup_path) { + _cleanup_free_ char *path = NULL; + + path = unit_default_cgroup_path(u); + if (!path) + return log_oom(); + + r = unit_set_cgroup_path(u, path); + if (r == -EEXIST) + return log_unit_error_errno(u, r, "Control group %s exists already.", path); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path); + } + + /* First, create our own group */ + r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path); + + /* Start watching it */ + (void) unit_watch_cgroup(u); + + /* Enable all controllers we need */ + r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path); + + /* Keep track that this is now realized */ + u->cgroup_realized = true; + u->cgroup_realized_mask = target_mask; + u->cgroup_enabled_mask = enable_mask; + + if (u->type != UNIT_SLICE && !c->delegate) { + + /* Then, possibly move things over, but not if + * subgroups may contain processes, which is the case + * for slice and delegation units. */ + r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path); + } + + return 0; +} + +int unit_attach_pids_to_cgroup(Unit *u) { + int r; + assert(u); + + r = unit_realize_cgroup(u); + if (r < 0) + return r; + + r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->pids, migrate_callback, u); + if (r < 0) + return r; + + return 0; +} + +static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) { + assert(u); + + return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask; +} + +/* Check if necessary controllers and attributes for a unit are in place. + * + * If so, do nothing. + * If not, create paths, move processes over, and set attributes. + * + * Returns 0 on success and < 0 on failure. */ +static int unit_realize_cgroup_now(Unit *u, ManagerState state) { + CGroupMask target_mask, enable_mask; + int r; + + assert(u); + + if (u->in_cgroup_queue) { + LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); + u->in_cgroup_queue = false; + } + + target_mask = unit_get_target_mask(u); + enable_mask = unit_get_enable_mask(u); + + if (unit_has_mask_realized(u, target_mask, enable_mask)) + return 0; + + /* First, realize parents */ + if (UNIT_ISSET(u->slice)) { + r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state); + if (r < 0) + return r; + } + + /* And then do the real work */ + r = unit_create_cgroup(u, target_mask, enable_mask); + if (r < 0) + return r; + + /* Finally, apply the necessary attributes. */ + cgroup_context_apply(u, target_mask, state); + + return 0; +} + +static void unit_add_to_cgroup_queue(Unit *u) { + + if (u->in_cgroup_queue) + return; + + LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u); + u->in_cgroup_queue = true; +} + +unsigned manager_dispatch_cgroup_queue(Manager *m) { + ManagerState state; + unsigned n = 0; + Unit *i; + int r; + + state = manager_state(m); + + while ((i = m->cgroup_queue)) { + assert(i->in_cgroup_queue); + + r = unit_realize_cgroup_now(i, state); + if (r < 0) + log_warning_errno(r, "Failed to realize cgroups for queued unit %s, ignoring: %m", i->id); + + n++; + } + + return n; +} + +static void unit_queue_siblings(Unit *u) { + Unit *slice; + + /* This adds the siblings of the specified unit and the + * siblings of all parent units to the cgroup queue. (But + * neither the specified unit itself nor the parents.) */ + + while ((slice = UNIT_DEREF(u->slice))) { + Iterator i; + Unit *m; + + SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) { + if (m == u) + continue; + + /* Skip units that have a dependency on the slice + * but aren't actually in it. */ + if (UNIT_DEREF(m->slice) != slice) + continue; + + /* No point in doing cgroup application for units + * without active processes. */ + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m))) + continue; + + /* If the unit doesn't need any new controllers + * and has current ones realized, it doesn't need + * any changes. */ + if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m))) + continue; + + unit_add_to_cgroup_queue(m); + } + + u = slice; + } +} + +int unit_realize_cgroup(Unit *u) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return 0; + + /* So, here's the deal: when realizing the cgroups for this + * unit, we need to first create all parents, but there's more + * actually: for the weight-based controllers we also need to + * make sure that all our siblings (i.e. units that are in the + * same slice as we are) have cgroups, too. Otherwise, things + * would become very uneven as each of their processes would + * get as much resources as all our group together. This call + * will synchronously create the parent cgroups, but will + * defer work on the siblings to the next event loop + * iteration. */ + + /* Add all sibling slices to the cgroup queue. */ + unit_queue_siblings(u); + + /* And realize this one now (and apply the values) */ + return unit_realize_cgroup_now(u, manager_state(u->manager)); +} + +void unit_release_cgroup(Unit *u) { + assert(u); + + /* Forgets all cgroup details for this cgroup */ + + if (u->cgroup_path) { + (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); + u->cgroup_path = mfree(u->cgroup_path); + } + + if (u->cgroup_inotify_wd >= 0) { + if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0) + log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id); + + (void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd)); + u->cgroup_inotify_wd = -1; + } +} + +void unit_prune_cgroup(Unit *u) { + int r; + bool is_root_slice; + + assert(u); + + /* Removes the cgroup, if empty and possible, and stops watching it. */ + + if (!u->cgroup_path) + return; + + is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE); + + r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice); + if (r < 0) { + log_unit_debug_errno(u, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path); + return; + } + + if (is_root_slice) + return; + + unit_release_cgroup(u); + + u->cgroup_realized = false; + u->cgroup_realized_mask = 0; + u->cgroup_enabled_mask = 0; +} + +int unit_search_main_pid(Unit *u, pid_t *ret) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid = 0, npid, mypid; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENXIO; + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f); + if (r < 0) + return r; + + mypid = getpid(); + while (cg_read_pid(f, &npid) > 0) { + pid_t ppid; + + if (npid == pid) + continue; + + /* Ignore processes that aren't our kids */ + if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid) + continue; + + if (pid != 0) + /* Dang, there's more than one daemonized PID + in this group, so we don't know what process + is the main process. */ + + return -ENODATA; + + pid = npid; + } + + *ret = pid; + return 0; +} + +static int unit_watch_pids_in_path(Unit *u, const char *path) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int ret = 0, r; + + assert(u); + assert(path); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); + if (r < 0) + ret = r; + else { + pid_t pid; + + while ((r = cg_read_pid(f, &pid)) > 0) { + r = unit_watch_pid(u, pid); + if (r < 0 && ret >= 0) + ret = r; + } + + if (r < 0 && ret >= 0) + ret = r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); + if (r < 0) { + if (ret >= 0) + ret = r; + } else { + char *fn; + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + + if (!p) + return -ENOMEM; + + r = unit_watch_pids_in_path(u, p); + if (r < 0 && ret >= 0) + ret = r; + } + + if (r < 0 && ret >= 0) + ret = r; + } + + return ret; +} + +int unit_watch_all_pids(Unit *u) { + assert(u); + + /* Adds all PIDs from our cgroup to the set of PIDs we + * watch. This is a fallback logic for cases where we do not + * get reliable cgroup empty notifications: we try to use + * SIGCHLD as replacement. */ + + if (!u->cgroup_path) + return -ENOENT; + + if (cg_unified() > 0) /* On unified we can use proper notifications */ + return 0; + + return unit_watch_pids_in_path(u, u->cgroup_path); +} + +int unit_notify_cgroup_empty(Unit *u) { + int r; + + assert(u); + + if (!u->cgroup_path) + return 0; + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); + if (r <= 0) + return r; + + unit_add_to_gc_queue(u); + + if (UNIT_VTABLE(u)->notify_cgroup_empty) + UNIT_VTABLE(u)->notify_cgroup_empty(u); + + return 0; +} + +static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(fd >= 0); + assert(m); + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return log_error_errno(errno, "Failed to read control group inotify events: %m"); + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + Unit *u; + + if (e->wd < 0) + /* Queue overflow has no watch descriptor */ + continue; + + if (e->mask & IN_IGNORED) + /* The watch was just removed */ + continue; + + u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd)); + if (!u) /* Not that inotify might deliver + * events for a watch even after it + * was removed, because it was queued + * before the removal. Let's ignore + * this here safely. */ + continue; + + (void) unit_notify_cgroup_empty(u); + } + } +} + +int manager_setup_cgroup(Manager *m) { + _cleanup_free_ char *path = NULL; + CGroupController c; + int r, unified; + char *e; + + assert(m); + + /* 1. Determine hierarchy */ + m->cgroup_root = mfree(m->cgroup_root); + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); + if (r < 0) + return log_error_errno(r, "Cannot determine cgroup we are running in: %m"); + + /* Chop off the init scope, if we are already located in it */ + e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); + + /* LEGACY: Also chop off the system slice if we are in + * it. This is to support live upgrades from older systemd + * versions where PID 1 was moved there. Also see + * cg_get_root_path(). */ + if (!e && MANAGER_IS_SYSTEM(m)) { + e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); + if (!e) + e = endswith(m->cgroup_root, "/system"); /* even more legacy */ + } + if (e) + *e = 0; + + /* And make sure to store away the root value without trailing + * slash, even for the root dir, so that we can easily prepend + * it everywhere. */ + while ((e = endswith(m->cgroup_root, "/"))) + *e = 0; + + /* 2. Show data */ + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); + if (r < 0) + return log_error_errno(r, "Cannot find cgroup mount point: %m"); + + unified = cg_unified(); + if (unified < 0) + return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m"); + if (unified > 0) + log_debug("Unified cgroup hierarchy is located at %s.", path); + else + log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + + if (!m->test_run) { + const char *scope_path; + + /* 3. Install agent */ + if (unified) { + + /* In the unified hierarchy we can get + * cgroup empty notifications via inotify. */ + + m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); + safe_close(m->cgroup_inotify_fd); + + m->cgroup_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (m->cgroup_inotify_fd < 0) + return log_error_errno(errno, "Failed to create control group inotify object: %m"); + + r = sd_event_add_io(m->event, &m->cgroup_inotify_event_source, m->cgroup_inotify_fd, EPOLLIN, on_cgroup_inotify_event, m); + if (r < 0) + return log_error_errno(r, "Failed to watch control group inotify object: %m"); + + /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also + * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ + r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); + if (r < 0) + return log_error_errno(r, "Failed to set priority of inotify event source: %m"); + + (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify"); + + } else if (MANAGER_IS_SYSTEM(m)) { + + /* On the legacy hierarchy we only get + * notifications via cgroup agents. (Which + * isn't really reliable, since it does not + * generate events when control groups with + * children run empty. */ + + r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); + if (r < 0) + log_warning_errno(r, "Failed to install release agent, ignoring: %m"); + else if (r > 0) + log_debug("Installed release agent."); + else if (r == 0) + log_debug("Release agent already installed."); + } + + /* 4. Make sure we are in the special "init.scope" unit in the root slice. */ + scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); + if (r < 0) + return log_error_errno(r, "Failed to create %s control group: %m", scope_path); + + /* also, move all other userspace processes remaining + * in the root cgroup into that scope. */ + r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); + if (r < 0) + log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); + + /* 5. And pin it, so that it cannot be unmounted */ + safe_close(m->pin_cgroupfs_fd); + m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); + if (m->pin_cgroupfs_fd < 0) + return log_error_errno(errno, "Failed to open pin file: %m"); + + /* 6. Always enable hierarchical support if it exists... */ + if (!unified) + (void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); + } + + /* 7. Figure out which controllers are supported */ + r = cg_mask_supported(&m->cgroup_supported); + if (r < 0) + return log_error_errno(r, "Failed to determine supported controllers: %m"); + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) + log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & CGROUP_CONTROLLER_TO_MASK(c))); + + return 0; +} + +void manager_shutdown_cgroup(Manager *m, bool delete) { + assert(m); + + /* We can't really delete the group, since we are in it. But + * let's trim it. */ + if (delete && m->cgroup_root) + (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); + + m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit); + + m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); + m->cgroup_inotify_fd = safe_close(m->cgroup_inotify_fd); + + m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd); + + m->cgroup_root = mfree(m->cgroup_root); +} + +Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { + char *p; + Unit *u; + + assert(m); + assert(cgroup); + + u = hashmap_get(m->cgroup_unit, cgroup); + if (u) + return u; + + p = strdupa(cgroup); + for (;;) { + char *e; + + e = strrchr(p, '/'); + if (!e || e == p) + return hashmap_get(m->cgroup_unit, SPECIAL_ROOT_SLICE); + + *e = 0; + + u = hashmap_get(m->cgroup_unit, p); + if (u) + return u; + } +} + +Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(m); + + if (pid <= 0) + return NULL; + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); + if (r < 0) + return NULL; + + return manager_get_unit_by_cgroup(m, cgroup); +} + +Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { + Unit *u; + + assert(m); + + if (pid <= 0) + return NULL; + + if (pid == 1) + return hashmap_get(m->units, SPECIAL_INIT_SCOPE); + + u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid)); + if (u) + return u; + + u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid)); + if (u) + return u; + + return manager_get_unit_by_pid_cgroup(m, pid); +} + +int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { + Unit *u; + + assert(m); + assert(cgroup); + + log_debug("Got cgroup empty notification for: %s", cgroup); + + u = manager_get_unit_by_cgroup(m, cgroup); + if (!u) + return 0; + + return unit_notify_cgroup_empty(u); +} + +int unit_get_memory_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) + return -ENODATA; + + if (cg_unified() <= 0) + r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); + else + r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + +int unit_get_tasks_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) + return -ENODATA; + + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + +static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { + _cleanup_free_ char *v = NULL; + uint64_t ns; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0) + return -ENODATA; + + r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + r = safe_atou64(v, &ns); + if (r < 0) + return r; + + *ret = ns; + return 0; +} + +int unit_get_cpu_usage(Unit *u, nsec_t *ret) { + nsec_t ns; + int r; + + r = unit_get_cpu_usage_raw(u, &ns); + if (r < 0) + return r; + + if (ns > u->cpuacct_usage_base) + ns -= u->cpuacct_usage_base; + else + ns = 0; + + *ret = ns; + return 0; +} + +int unit_reset_cpu_usage(Unit *u) { + nsec_t ns; + int r; + + assert(u); + + r = unit_get_cpu_usage_raw(u, &ns); + if (r < 0) { + u->cpuacct_usage_base = 0; + return r; + } + + u->cpuacct_usage_base = ns; + return 0; +} + +bool unit_cgroup_delegate(Unit *u) { + CGroupContext *c; + + assert(u); + + c = unit_get_cgroup_context(u); + if (!c) + return false; + + return c->delegate; +} + +void unit_invalidate_cgroup(Unit *u, CGroupMask m) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return; + + if (m == 0) + return; + + /* always invalidate compat pairs together */ + if (m & (CGROUP_MASK_IO | CGROUP_MASK_BLKIO)) + m |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; + + if ((u->cgroup_realized_mask & m) == 0) + return; + + u->cgroup_realized_mask &= ~m; + unit_add_to_cgroup_queue(u); +} + +void manager_invalidate_startup_units(Manager *m) { + Iterator i; + Unit *u; + + assert(m); + + SET_FOREACH(u, m->startup_units, i) + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO); +} + +static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { + [CGROUP_AUTO] = "auto", + [CGROUP_CLOSED] = "closed", + [CGROUP_STRICT] = "strict", +}; + +DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy); diff --git a/src/grp-system/libcore/cgroup.h b/src/grp-system/libcore/cgroup.h new file mode 100644 index 0000000000..bf40c3cc74 --- /dev/null +++ b/src/grp-system/libcore/cgroup.h @@ -0,0 +1,183 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/cgroup-util.h" +#include "basic/list.h" +#include "basic/time-util.h" + +typedef struct CGroupContext CGroupContext; +typedef struct CGroupDeviceAllow CGroupDeviceAllow; +typedef struct CGroupIODeviceWeight CGroupIODeviceWeight; +typedef struct CGroupIODeviceLimit CGroupIODeviceLimit; +typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; +typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth; + +typedef enum CGroupDevicePolicy { + + /* When devices listed, will allow those, plus built-in ones, + if none are listed will allow everything. */ + CGROUP_AUTO, + + /* Everything forbidden, except built-in ones and listed ones. */ + CGROUP_CLOSED, + + /* Everythings forbidden, except for the listed devices */ + CGROUP_STRICT, + + _CGROUP_DEVICE_POLICY_MAX, + _CGROUP_DEVICE_POLICY_INVALID = -1 +} CGroupDevicePolicy; + +struct CGroupDeviceAllow { + LIST_FIELDS(CGroupDeviceAllow, device_allow); + char *path; + bool r:1; + bool w:1; + bool m:1; +}; + +struct CGroupIODeviceWeight { + LIST_FIELDS(CGroupIODeviceWeight, device_weights); + char *path; + uint64_t weight; +}; + +struct CGroupIODeviceLimit { + LIST_FIELDS(CGroupIODeviceLimit, device_limits); + char *path; + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; +}; + +struct CGroupBlockIODeviceWeight { + LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); + char *path; + uint64_t weight; +}; + +struct CGroupBlockIODeviceBandwidth { + LIST_FIELDS(CGroupBlockIODeviceBandwidth, device_bandwidths); + char *path; + uint64_t rbps; + uint64_t wbps; +}; + +struct CGroupContext { + bool cpu_accounting; + bool io_accounting; + bool blockio_accounting; + bool memory_accounting; + bool tasks_accounting; + + /* For unified hierarchy */ + uint64_t io_weight; + uint64_t startup_io_weight; + LIST_HEAD(CGroupIODeviceWeight, io_device_weights); + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); + + uint64_t memory_low; + uint64_t memory_high; + uint64_t memory_max; + + /* For legacy hierarchies */ + uint64_t cpu_shares; + uint64_t startup_cpu_shares; + usec_t cpu_quota_per_sec_usec; + + uint64_t blockio_weight; + uint64_t startup_blockio_weight; + LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights); + LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths); + + uint64_t memory_limit; + + CGroupDevicePolicy device_policy; + LIST_HEAD(CGroupDeviceAllow, device_allow); + + /* Common */ + uint64_t tasks_max; + + bool delegate; +}; + +#include "unit.h" + +void cgroup_context_init(CGroupContext *c); +void cgroup_context_done(CGroupContext *c); +void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); + +CGroupMask cgroup_context_get_mask(CGroupContext *c); + +void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a); +void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w); +void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l); +void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); +void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); + +CGroupMask unit_get_own_mask(Unit *u); +CGroupMask unit_get_siblings_mask(Unit *u); +CGroupMask unit_get_members_mask(Unit *u); +CGroupMask unit_get_subtree_mask(Unit *u); + +CGroupMask unit_get_target_mask(Unit *u); +CGroupMask unit_get_enable_mask(Unit *u); + +void unit_update_cgroup_members_masks(Unit *u); + +char *unit_default_cgroup_path(Unit *u); +int unit_set_cgroup_path(Unit *u, const char *path); + +int unit_realize_cgroup(Unit *u); +void unit_release_cgroup(Unit *u); +void unit_prune_cgroup(Unit *u); +int unit_watch_cgroup(Unit *u); + +int unit_attach_pids_to_cgroup(Unit *u); + +int manager_setup_cgroup(Manager *m); +void manager_shutdown_cgroup(Manager *m, bool delete); + +unsigned manager_dispatch_cgroup_queue(Manager *m); + +Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); +Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); +Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); + +int unit_search_main_pid(Unit *u, pid_t *ret); +int unit_watch_all_pids(Unit *u); + +int unit_get_memory_current(Unit *u, uint64_t *ret); +int unit_get_tasks_current(Unit *u, uint64_t *ret); +int unit_get_cpu_usage(Unit *u, nsec_t *ret); +int unit_reset_cpu_usage(Unit *u); + +bool unit_cgroup_delegate(Unit *u); + +int unit_notify_cgroup_empty(Unit *u); +int manager_notify_cgroup_empty(Manager *m, const char *group); + +void unit_invalidate_cgroup(Unit *u, CGroupMask m); + +void manager_invalidate_startup_units(Manager *m); + +const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; +CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/dbus-automount.c b/src/grp-system/libcore/dbus-automount.c new file mode 100644 index 0000000000..2949c49a3f --- /dev/null +++ b/src/grp-system/libcore/dbus-automount.c @@ -0,0 +1,35 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "automount.h" +#include "dbus-automount.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult); + +const sd_bus_vtable bus_automount_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Automount, where), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Automount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Automount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; diff --git a/src/grp-system/libcore/dbus-automount.h b/src/grp-system/libcore/dbus-automount.h new file mode 100644 index 0000000000..7b51eb973a --- /dev/null +++ b/src/grp-system/libcore/dbus-automount.h @@ -0,0 +1,23 @@ +#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 . +***/ + + +extern const sd_bus_vtable bus_automount_vtable[]; diff --git a/src/grp-system/libcore/dbus-busname.c b/src/grp-system/libcore/dbus-busname.c new file mode 100644 index 0000000000..65815bb826 --- /dev/null +++ b/src/grp-system/libcore/dbus-busname.c @@ -0,0 +1,38 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "busname.h" +#include "dbus-busname.h" +#include "unit.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult); + +const sd_bus_vtable bus_busname_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Name", "s", NULL, offsetof(BusName, name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(BusName, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(BusName, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(BusName, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Activating", "b", bus_property_get_bool, offsetof(BusName, activating), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("AcceptFileDescriptors", "b", bus_property_get_bool, offsetof(BusName, accept_fd), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; diff --git a/src/grp-system/libcore/dbus-busname.h b/src/grp-system/libcore/dbus-busname.h new file mode 100644 index 0000000000..8643d1a404 --- /dev/null +++ b/src/grp-system/libcore/dbus-busname.h @@ -0,0 +1,23 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +extern const sd_bus_vtable bus_busname_vtable[]; diff --git a/src/grp-system/libcore/dbus-cgroup.c b/src/grp-system/libcore/dbus-cgroup.c new file mode 100644 index 0000000000..da1c333043 --- /dev/null +++ b/src/grp-system/libcore/dbus-cgroup.c @@ -0,0 +1,1110 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/path-util.h" +#include "shared/bus-util.h" + +#include "cgroup.h" +#include "dbus-cgroup.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); + +static int property_get_io_device_weight( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupIODeviceWeight *w; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_weights, w, c->io_device_weights) { + r = sd_bus_message_append(reply, "(st)", w->path, w->weight); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_io_device_limits( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupIODeviceLimit *l; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_limits, l, c->io_device_limits) { + CGroupIOLimitType type; + + type = cgroup_io_limit_type_from_string(property); + if (type < 0 || l->limits[type] == cgroup_io_limit_defaults[type]) + continue; + + r = sd_bus_message_append(reply, "(st)", l->path, l->limits[type]); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_blockio_device_weight( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupBlockIODeviceWeight *w; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_weights, w, c->blockio_device_weights) { + r = sd_bus_message_append(reply, "(st)", w->path, w->weight); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_blockio_device_bandwidths( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupBlockIODeviceBandwidth *b; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + uint64_t v; + + if (streq(property, "BlockIOReadBandwidth")) + v = b->rbps; + else + v = b->wbps; + + if (v == CGROUP_LIMIT_MAX) + continue; + + r = sd_bus_message_append(reply, "(st)", b->path, v); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_device_allow( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupDeviceAllow *a; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + return r; + + LIST_FOREACH(device_allow, a, c->device_allow) { + unsigned k = 0; + char rwm[4]; + + if (a->r) + rwm[k++] = 'r'; + if (a->w) + rwm[k++] = 'w'; + if (a->m) + rwm[k++] = 'm'; + + rwm[k] = 0; + + r = sd_bus_message_append(reply, "(ss)", a->path, rwm); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), + SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), + SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), + SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), + SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), + SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0), + SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0), + SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0), + SD_BUS_PROPERTY("IODeviceWeight", "a(st)", property_get_io_device_weight, 0, 0), + SD_BUS_PROPERTY("IOReadBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), + SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), + SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), + SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0), + SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), + SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), + SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), + SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0), + SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), + SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), + SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), + SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0), + SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0), + SD_BUS_VTABLE_END +}; + +static int bus_cgroup_set_transient_property( + Unit *u, + CGroupContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "Delegate")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->delegate = b; + unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no"); + } + + return 1; + } + + return 0; +} + +int bus_cgroup_set_property( + Unit *u, + CGroupContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + CGroupIOLimitType iol_type; + int r; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "CPUAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->cpu_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU); + unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); + } + + return 1; + + } else if (streq(name, "CPUShares")) { + uint64_t shares; + + r = sd_bus_message_read(message, "t", &shares); + if (r < 0) + return r; + + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); + + if (mode != UNIT_CHECK) { + c->cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "CPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares); + } + + return 1; + + } else if (streq(name, "StartupCPUShares")) { + uint64_t shares; + + r = sd_bus_message_read(message, "t", &shares); + if (r < 0) + return r; + + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); + + if (mode != UNIT_CHECK) { + c->startup_cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupCPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares); + } + + return 1; + + } else if (streq(name, "CPUQuotaPerSecUSec")) { + uint64_t u64; + + r = sd_bus_message_read(message, "t", &u64); + if (r < 0) + return r; + + if (u64 <= 0) + return sd_bus_error_set_errnof(error, EINVAL, "CPUQuotaPerSecUSec value out of range"); + + if (mode != UNIT_CHECK) { + c->cpu_quota_per_sec_usec = u64; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); + } + + return 1; + + } else if (streq(name, "IOAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->io_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + unit_write_drop_in_private(u, mode, name, b ? "IOAccounting=yes" : "IOAccounting=no"); + } + + return 1; + + } else if (streq(name, "IOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (!CGROUP_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "IOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->io_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + if (weight == CGROUP_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "IOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "IOWeight=%" PRIu64, weight); + } + + return 1; + + } else if (streq(name, "StartupIOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (CGROUP_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupIOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->startup_io_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + if (weight == CGROUP_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupIOWeight=%" PRIu64, weight); + } + + return 1; + + } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) { + const char *path; + unsigned n = 0; + uint64_t u64; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { + + if (mode != UNIT_CHECK) { + CGroupIODeviceLimit *a = NULL, *b; + + LIST_FOREACH(device_limits, b, c->io_device_limits) { + if (path_equal(path, b->path)) { + a = b; + break; + } + } + + if (!a) { + CGroupIOLimitType type; + + a = new0(CGroupIODeviceLimit, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + a->limits[type] = cgroup_io_limit_defaults[type]; + + LIST_PREPEND(device_limits, c->io_device_limits, a); + } + + a->limits[iol_type] = u64; + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + CGroupIODeviceLimit *a; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t size = 0; + + if (n == 0) { + LIST_FOREACH(device_limits, a, c->io_device_limits) + a->limits[iol_type] = cgroup_io_limit_defaults[iol_type]; + } + + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fprintf(f, "%s=\n", name); + LIST_FOREACH(device_limits, a, c->io_device_limits) + if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type]) + fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "IODeviceWeight")) { + const char *path; + uint64_t weight; + unsigned n = 0; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { + + if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID) + return sd_bus_error_set_errnof(error, EINVAL, "IODeviceWeight out of range"); + + if (mode != UNIT_CHECK) { + CGroupIODeviceWeight *a = NULL, *b; + + LIST_FOREACH(device_weights, b, c->io_device_weights) { + if (path_equal(b->path, path)) { + a = b; + break; + } + } + + if (!a) { + a = new0(CGroupIODeviceWeight, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + LIST_PREPEND(device_weights,c->io_device_weights, a); + } + + a->weight = weight; + } + + n++; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + CGroupIODeviceWeight *a; + size_t size = 0; + + if (n == 0) { + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + } + + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fputs("IODeviceWeight=\n", f); + LIST_FOREACH(device_weights, a, c->io_device_weights) + fprintf(f, "IODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "BlockIOAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->blockio_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); + } + + return 1; + + } else if (streq(name, "BlockIOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "BlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight); + } + + return 1; + + } else if (streq(name, "StartupBlockIOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->startup_blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight); + } + + return 1; + + } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + const char *path; + bool read = true; + unsigned n = 0; + uint64_t u64; + + if (streq(name, "BlockIOWriteBandwidth")) + read = false; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { + + if (mode != UNIT_CHECK) { + CGroupBlockIODeviceBandwidth *a = NULL, *b; + + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + if (path_equal(path, b->path)) { + a = b; + break; + } + } + + if (!a) { + a = new0(CGroupBlockIODeviceBandwidth, 1); + if (!a) + return -ENOMEM; + + a->rbps = CGROUP_LIMIT_MAX; + a->wbps = CGROUP_LIMIT_MAX; + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); + } + + if (read) + a->rbps = u64; + else + a->wbps = u64; + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + CGroupBlockIODeviceBandwidth *a; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t size = 0; + + if (n == 0) { + LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) { + if (read) + a->rbps = CGROUP_LIMIT_MAX; + else + a->wbps = CGROUP_LIMIT_MAX; + } + } + + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + if (read) { + fputs("BlockIOReadBandwidth=\n", f); + LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) + if (a->rbps != CGROUP_LIMIT_MAX) + fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->rbps); + } else { + fputs("BlockIOWriteBandwidth=\n", f); + LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) + if (a->wbps != CGROUP_LIMIT_MAX) + fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->wbps); + } + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "BlockIODeviceWeight")) { + const char *path; + uint64_t weight; + unsigned n = 0; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { + + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) + return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range"); + + if (mode != UNIT_CHECK) { + CGroupBlockIODeviceWeight *a = NULL, *b; + + LIST_FOREACH(device_weights, b, c->blockio_device_weights) { + if (path_equal(b->path, path)) { + a = b; + break; + } + } + + if (!a) { + a = new0(CGroupBlockIODeviceWeight, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + LIST_PREPEND(device_weights,c->blockio_device_weights, a); + } + + a->weight = weight; + } + + n++; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + CGroupBlockIODeviceWeight *a; + size_t size = 0; + + if (n == 0) { + while (c->blockio_device_weights) + cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); + } + + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fputs("BlockIODeviceWeight=\n", f); + LIST_FOREACH(device_weights, a, c->blockio_device_weights) + fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "MemoryAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->memory_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); + } + + return 1; + + } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax")) { + uint64_t v; + + r = sd_bus_message_read(message, "t", &v); + if (r < 0) + return r; + if (v <= 0) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); + + if (mode != UNIT_CHECK) { + if (streq(name, "MemoryLow")) + c->memory_low = v; + else if (streq(name, "MemoryHigh")) + c->memory_high = v; + else + c->memory_max = v; + + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + + if (v == CGROUP_LIMIT_MAX) + unit_write_drop_in_private_format(u, mode, name, "%s=infinity", name); + else + unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, v); + } + + return 1; + + } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale")) { + uint32_t raw; + uint64_t v; + + r = sd_bus_message_read(message, "u", &raw); + if (r < 0) + return r; + + v = physical_memory_scale(raw, UINT32_MAX); + if (v <= 0 || v == UINT64_MAX) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); + + if (mode != UNIT_CHECK) { + const char *e; + + /* Chop off suffix */ + assert_se(e = endswith(name, "Scale")); + name = strndupa(name, e - name); + + if (streq(name, "MemoryLow")) + c->memory_low = v; + else if (streq(name, "MemoryHigh")) + c->memory_high = v; + else + c->memory_max = v; + + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu32 "%%", name, + (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); + } + + return 1; + + } else if (streq(name, "MemoryLimit")) { + uint64_t limit; + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + if (limit <= 0) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); + + if (mode != UNIT_CHECK) { + c->memory_limit = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit); + } + + return 1; + + } else if (streq(name, "MemoryLimitScale")) { + uint64_t limit; + uint32_t raw; + + r = sd_bus_message_read(message, "u", &raw); + if (r < 0) + return r; + + limit = physical_memory_scale(raw, UINT32_MAX); + if (limit <= 0 || limit == UINT64_MAX) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); + + if (mode != UNIT_CHECK) { + c->memory_limit = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + unit_write_drop_in_private_format(u, mode, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", + (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); + } + + return 1; + + } else if (streq(name, "DevicePolicy")) { + const char *policy; + CGroupDevicePolicy p; + + r = sd_bus_message_read(message, "s", &policy); + if (r < 0) + return r; + + p = cgroup_device_policy_from_string(policy); + if (p < 0) + return -EINVAL; + + if (mode != UNIT_CHECK) { + c->device_policy = p; + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); + unit_write_drop_in_private_format(u, mode, name, "DevicePolicy=%s", policy); + } + + return 1; + + } else if (streq(name, "DeviceAllow")) { + const char *path, *rwm; + unsigned n = 0; + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { + + if ((!startswith(path, "/dev/") && + !startswith(path, "/run/systemd/inaccessible/") && + !startswith(path, "block-") && + !startswith(path, "char-")) || + strpbrk(path, WHITESPACE)) + return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires device node"); + + if (isempty(rwm)) + rwm = "rwm"; + + if (!in_charset(rwm, "rwm")) + return sd_bus_error_set_errnof(error, EINVAL, "DeviceAllow= requires combination of rwm flags"); + + if (mode != UNIT_CHECK) { + CGroupDeviceAllow *a = NULL, *b; + + LIST_FOREACH(device_allow, b, c->device_allow) { + if (path_equal(b->path, path)) { + a = b; + break; + } + } + + if (!a) { + a = new0(CGroupDeviceAllow, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + + LIST_PREPEND(device_allow, c->device_allow, a); + } + + a->r = !!strchr(rwm, 'r'); + a->w = !!strchr(rwm, 'w'); + a->m = !!strchr(rwm, 'm'); + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + CGroupDeviceAllow *a; + size_t size = 0; + + if (n == 0) { + while (c->device_allow) + cgroup_context_free_device_allow(c, c->device_allow); + } + + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fputs("DeviceAllow=\n", f); + 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" : ""); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "TasksAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no"); + } + + return 1; + + } else if (streq(name, "TasksMax")) { + uint64_t limit; + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + if (limit <= 0) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name); + + if (mode != UNIT_CHECK) { + c->tasks_max = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "TasksMax=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit); + } + + return 1; + } else if (streq(name, "TasksMaxScale")) { + uint64_t limit; + uint32_t raw; + + r = sd_bus_message_read(message, "u", &raw); + if (r < 0) + return r; + + limit = system_tasks_max_scale(raw, UINT32_MAX); + if (limit <= 0 || limit >= UINT64_MAX) + return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name); + + if (mode != UNIT_CHECK) { + c->tasks_max = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu32 "%%", + (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); + } + + return 1; + } + + if (u->transient && u->load_state == UNIT_STUB) { + r = bus_cgroup_set_transient_property(u, c, name, message, mode, error); + if (r != 0) + return r; + + } + + return 0; +} diff --git a/src/grp-system/libcore/dbus-cgroup.h b/src/grp-system/libcore/dbus-cgroup.h new file mode 100644 index 0000000000..84d0f1ba04 --- /dev/null +++ b/src/grp-system/libcore/dbus-cgroup.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "cgroup.h" + +extern const sd_bus_vtable bus_cgroup_vtable[]; + +int bus_cgroup_set_property(Unit *u, CGroupContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/grp-system/libcore/dbus-device.c b/src/grp-system/libcore/dbus-device.c new file mode 100644 index 0000000000..e1a12224d3 --- /dev/null +++ b/src/grp-system/libcore/dbus-device.c @@ -0,0 +1,28 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "dbus-device.h" +#include "device.h" +#include "unit.h" + +const sd_bus_vtable bus_device_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("SysFSPath", "s", NULL, offsetof(Device, sysfs), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_VTABLE_END +}; diff --git a/src/grp-system/libcore/dbus-device.h b/src/grp-system/libcore/dbus-device.h new file mode 100644 index 0000000000..eb1d8c3278 --- /dev/null +++ b/src/grp-system/libcore/dbus-device.h @@ -0,0 +1,24 @@ +#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 . +***/ + +#include "unit.h" + +extern const sd_bus_vtable bus_device_vtable[]; diff --git a/src/grp-system/libcore/dbus-execute.c b/src/grp-system/libcore/dbus-execute.c new file mode 100644 index 0000000000..f7c217efc1 --- /dev/null +++ b/src/grp-system/libcore/dbus-execute.c @@ -0,0 +1,1555 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#ifdef HAVE_SECCOMP +#include +#endif + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/ioprio.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rlimit-util.h" +#include "shared/bus-util.h" + +#include "dbus-execute.h" +#include "execute.h" +#include "namespace.h" +#ifdef HAVE_SECCOMP +#include "shared/seccomp-util.h" +#endif +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/utf8.h" + +BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); + +static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); +static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); + +static int property_get_environment_files( + 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; + char **j; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(sb)"); + if (r < 0) + return r; + + STRV_FOREACH(j, c->environment_files) { + const char *fn = *j; + + r = sd_bus_message_append(reply, "(sb)", fn[0] == '-' ? fn + 1 : fn, fn[0] == '-'); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_oom_score_adjust( + 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; + int32_t n; + + assert(bus); + assert(reply); + assert(c); + + if (c->oom_score_adjust_set) + n = c->oom_score_adjust; + else { + _cleanup_free_ char *t = NULL; + + n = 0; + if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) + safe_atoi32(t, &n); + } + + return sd_bus_message_append(reply, "i", n); +} + +static int property_get_nice( + 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; + int32_t n; + + assert(bus); + assert(reply); + assert(c); + + if (c->nice_set) + n = c->nice; + else { + errno = 0; + n = getpriority(PRIO_PROCESS, 0); + if (errno > 0) + n = 0; + } + + return sd_bus_message_append(reply, "i", n); +} + +static int property_get_ioprio( + 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; + int32_t n; + + assert(bus); + assert(reply); + assert(c); + + if (c->ioprio_set) + n = c->ioprio; + else { + n = ioprio_get(IOPRIO_WHO_PROCESS, 0); + if (n < 0) + n = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4); + } + + return sd_bus_message_append(reply, "i", n); +} + +static int property_get_cpu_sched_policy( + 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; + int32_t n; + + assert(bus); + assert(reply); + assert(c); + + if (c->cpu_sched_set) + n = c->cpu_sched_policy; + else { + n = sched_getscheduler(0); + if (n < 0) + n = SCHED_OTHER; + } + + return sd_bus_message_append(reply, "i", n); +} + +static int property_get_cpu_sched_priority( + 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; + int32_t n; + + assert(bus); + assert(reply); + assert(c); + + if (c->cpu_sched_set) + n = c->cpu_sched_priority; + else { + struct sched_param p = {}; + + if (sched_getparam(0, &p) >= 0) + n = p.sched_priority; + else + n = 0; + } + + return sd_bus_message_append(reply, "i", n); +} + +static int property_get_cpu_affinity( + 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); + + if (c->cpuset) + return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); + else + return sd_bus_message_append_array(reply, 'y', NULL, 0); +} + +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) { + + ExecContext *c = userdata; + uint64_t u; + + assert(bus); + assert(reply); + assert(c); + + if (c->timer_slack_nsec != NSEC_INFINITY) + u = (uint64_t) c->timer_slack_nsec; + else + u = (uint64_t) prctl(PR_GET_TIMERSLACK); + + return sd_bus_message_append(reply, "t", u); +} + +static int property_get_capability_bounding_set( + 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_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_empty_string( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", ""); +} + +static int property_get_syscall_filter( + 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; + _cleanup_strv_free_ char **l = NULL; + int r; + +#ifdef HAVE_SECCOMP + Iterator i; + void *id; +#endif + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'r', "bas"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "b", c->syscall_whitelist); + if (r < 0) + return r; + +#ifdef HAVE_SECCOMP + SET_FOREACH(id, c->syscall_filter, i) { + char *name; + + name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); + if (!name) + continue; + + r = strv_consume(&l, name); + if (r < 0) + return r; + } +#endif + + strv_sort(l); + + r = sd_bus_message_append_strv(reply, l); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +static int property_get_syscall_archs( + 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; + _cleanup_strv_free_ char **l = NULL; + int r; + +#ifdef HAVE_SECCOMP + Iterator i; + void *id; +#endif + + assert(bus); + assert(reply); + assert(c); + +#ifdef HAVE_SECCOMP + SET_FOREACH(id, c->syscall_archs, i) { + const char *name; + + name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1); + if (!name) + continue; + + r = strv_extend(&l, name); + if (r < 0) + return -ENOMEM; + } +#endif + + strv_sort(l); + + r = sd_bus_message_append_strv(reply, l); + if (r < 0) + return r; + + return 0; +} + +static int property_get_syscall_errno( + 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", (int32_t) c->syscall_errno); +} + +static int property_get_selinux_context( + 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, "(bs)", c->selinux_context_ignore, c->selinux_context); +} + +static int property_get_apparmor_profile( + 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, "(bs)", c->apparmor_profile_ignore, c->apparmor_profile); +} + +static int property_get_smack_process_label( + 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, "(bs)", c->smack_process_label_ignore, c->smack_process_label); +} + +static int property_get_personality( + 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, "s", personality_to_string(c->personality)); +} + +static int property_get_address_families( + 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; + _cleanup_strv_free_ char **l = NULL; + Iterator i; + void *af; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'r', "bas"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "b", c->address_families_whitelist); + if (r < 0) + return r; + + SET_FOREACH(af, c->address_families, i) { + const char *name; + + name = af_to_name(PTR_TO_INT(af)); + if (!name) + continue; + + r = strv_extend(&l, name); + if (r < 0) + return -ENOMEM; + } + + strv_sort(l); + + r = sd_bus_message_append_strv(reply, l); + if (r < 0) + return r; + + 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", 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), + SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TTYVTDisallocate", "b", bus_property_get_bool, offsetof(ExecContext, tty_vt_disallocate), SD_BUS_VTABLE_PROPERTY_CONST), + 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_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + 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), + SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ReadOnlyPaths", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Personality", "s", property_get_personality, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, runtime_directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; + +static int append_exec_command(sd_bus_message *reply, ExecCommand *c) { + int r; + + assert(reply); + assert(c); + + if (!c->path) + return 0; + + r = sd_bus_message_open_container(reply, 'r', "sasbttttuii"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", c->path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(reply, c->argv); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "bttttuii", + c->ignore, + c->exec_status.start_timestamp.realtime, + c->exec_status.start_timestamp.monotonic, + c->exec_status.exit_timestamp.realtime, + c->exec_status.exit_timestamp.monotonic, + (uint32_t) c->exec_status.pid, + (int32_t) c->exec_status.code, + (int32_t) c->exec_status.status); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +int bus_property_get_exec_command( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) { + + ExecCommand *c = (ExecCommand*) userdata; + int r; + + assert(bus); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)"); + if (r < 0) + return r; + + r = append_exec_command(reply, c); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +int bus_property_get_exec_command_list( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) { + + ExecCommand *c = *(ExecCommand**) userdata; + int r; + + assert(bus); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "(sasbttttuii)"); + if (r < 0) + return r; + + LIST_FOREACH(command, c, c) { + r = append_exec_command(reply, c); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +int bus_exec_context_set_transient_property( + Unit *u, + ExecContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + const char *soft = NULL; + int r, ri; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "User")) { + const char *uu; + + r = sd_bus_message_read(message, "s", &uu); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(uu)) + c->user = mfree(c->user); + else if (free_and_strdup(&c->user, uu) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "User=%s", uu); + } + + return 1; + + } else if (streq(name, "Group")) { + const char *gg; + + r = sd_bus_message_read(message, "s", &gg); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(gg)) + c->group = mfree(c->group); + else if (free_and_strdup(&c->group, gg) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "Group=%s", gg); + } + + 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 if (free_and_strdup(&c->syslog_identifier, id) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s", 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", 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", facility); + } + + return 1; + } else if (streq(name, "Nice")) { + int n; + + r = sd_bus_message_read(message, "i", &n); + if (r < 0) + return r; + + if (n < PRIO_MIN || n >= PRIO_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); + + if (mode != UNIT_CHECK) { + c->nice = n; + unit_write_drop_in_private_format(u, mode, name, "Nice=%i", n); + } + + return 1; + + } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) { + const char *s; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (!path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); + + if (mode != UNIT_CHECK) { + 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; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s); + } + + 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; + } + + c->working_directory_missing_ok = missing_ok; + unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s); + } + + return 1; + + } else if (streq(name, "StandardInput")) { + const char *s; + ExecInput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_input_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard input name"); + + if (mode != UNIT_CHECK) { + c->std_input = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardInput=%s", exec_input_to_string(p)); + } + + return 1; + + + } else if (streq(name, "StandardOutput")) { + const char *s; + ExecOutput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_output_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard output name"); + + if (mode != UNIT_CHECK) { + c->std_output = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardOutput=%s", exec_output_to_string(p)); + } + + return 1; + + } else if (streq(name, "StandardError")) { + const char *s; + ExecOutput p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = exec_output_from_string(s); + if (p < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard error name"); + + if (mode != UNIT_CHECK) { + c->std_error = p; + + unit_write_drop_in_private_format(u, mode, name, "StandardError=%s", exec_output_to_string(p)); + } + + return 1; + + } else if (STR_IN_SET(name, + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", + "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (streq(name, "IgnoreSIGPIPE")) + c->ignore_sigpipe = b; + else if (streq(name, "TTYVHangup")) + c->tty_vhangup = b; + else if (streq(name, "TTYReset")) + c->tty_reset = b; + else if (streq(name, "PrivateTmp")) + c->private_tmp = b; + else if (streq(name, "PrivateDevices")) + c->private_devices = b; + else if (streq(name, "PrivateNetwork")) + c->private_network = b; + else if (streq(name, "NoNewPrivileges")) + c->no_new_privileges = b; + else if (streq(name, "SyslogLevelPrefix")) + c->syslog_level_prefix = b; + else if (streq(name, "MemoryDenyWriteExecute")) + c->memory_deny_write_execute = b; + else if (streq(name, "RestrictRealtime")) + c->restrict_realtime = b; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); + } + + return 1; + + } else if (streq(name, "UtmpIdentifier")) { + const char *id; + + r = sd_bus_message_read(message, "s", &id); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(id)) + c->utmp_id = mfree(c->utmp_id); + else if (free_and_strdup(&c->utmp_id, id) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s", strempty(id)); + } + + return 1; + + } else if (streq(name, "UtmpMode")) { + const char *s; + ExecUtmpMode m; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + m = exec_utmp_mode_from_string(s); + if (m < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode"); + + if (mode != UNIT_CHECK) { + c->utmp_mode = m; + + unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s", exec_utmp_mode_to_string(m)); + } + + return 1; + + } else if (streq(name, "PAMName")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(n)) + c->pam_name = mfree(c->pam_name); + else if (free_and_strdup(&c->pam_name, n) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PAMName=%s", strempty(n)); + } + + return 1; + + } else if (streq(name, "Environment")) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + if (!strv_env_is_valid(l)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + char **e; + + if (strv_length(l) == 0) { + c->environment = strv_free(c->environment); + unit_write_drop_in_private_format(u, mode, name, "Environment="); + } else { + e = strv_env_merge(2, c->environment, l); + if (!e) + return -ENOMEM; + + strv_free(c->environment); + c->environment = e; + + joined = strv_join_quoted(c->environment); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "Environment=%s", 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); + } + + 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", 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", *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", 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="); + } 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="); + } 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, "PassEnvironment=%s", joined); + } + } + + return 1; + + } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", + "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { + _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 (STR_IN_SET(name, "ReadWriteDirectories", "ReadWritePaths")) + dirs = &c->read_write_paths; + else if (STR_IN_SET(name, "ReadOnlyDirectories", "ReadOnlyPaths")) + dirs = &c->read_only_paths; + else /* "InaccessiblePaths" */ + dirs = &c->inaccessible_paths; + + if (strv_length(l) == 0) { + *dirs = strv_free(*dirs); + unit_write_drop_in_private_format(u, mode, name, "%s=", 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", 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", 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", 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=", 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", name, joined); + } + } + + return 1; + + } else if (streq(name, "SELinuxContext")) { + const char *s; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(s)) + c->selinux_context = mfree(c->selinux_context); + else if (free_and_strdup(&c->selinux_context, s) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, strempty(s)); + } + + return 1; + + } + + ri = rlimit_from_string(name); + if (ri < 0) { + soft = endswith(name, "Soft"); + if (soft) { + const char *n; + + n = strndupa(name, soft - name); + ri = rlimit_from_string(n); + if (ri >= 0) + name = n; + + } + } + + if (ri >= 0) { + uint64_t rl; + rlim_t x; + + r = sd_bus_message_read(message, "t", &rl); + if (r < 0) + return r; + + if (rl == (uint64_t) -1) + x = RLIM_INFINITY; + else { + x = (rlim_t) rl; + + if ((uint64_t) x != rl) + return -ERANGE; + } + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *f = NULL; + struct rlimit nl; + + if (c->rlimit[ri]) { + nl = *c->rlimit[ri]; + + if (soft) + nl.rlim_cur = x; + else + nl.rlim_max = x; + } else + /* When the resource limit is not initialized yet, then assign the value to both fields */ + nl = (struct rlimit) { + .rlim_cur = x, + .rlim_max = x, + }; + + r = rlimit_format(&nl, &f); + if (r < 0) + return r; + + if (c->rlimit[ri]) + *c->rlimit[ri] = nl; + else { + c->rlimit[ri] = newdup(struct rlimit, &nl, 1); + if (!c->rlimit[ri]) + return -ENOMEM; + } + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, f); + } + + return 1; + } + + return 0; +} diff --git a/src/grp-system/libcore/dbus-execute.h b/src/grp-system/libcore/dbus-execute.h new file mode 100644 index 0000000000..bdfef41db4 --- /dev/null +++ b/src/grp-system/libcore/dbus-execute.h @@ -0,0 +1,45 @@ +#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 . +***/ + +#include + +#include "execute.h" + +#define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \ + BUS_PROPERTY_DUAL_TIMESTAMP(prefix "StartTimestamp", (offset) + offsetof(ExecStatus, start_timestamp), flags), \ + BUS_PROPERTY_DUAL_TIMESTAMP(prefix "ExitTimestamp", (offset) + offsetof(ExecStatus, exit_timestamp), flags), \ + SD_BUS_PROPERTY(prefix "PID", "u", bus_property_get_pid, (offset) + offsetof(ExecStatus, pid), flags), \ + SD_BUS_PROPERTY(prefix "Code", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, code), flags), \ + SD_BUS_PROPERTY(prefix "Status", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, status), flags) + +#define BUS_EXEC_COMMAND_VTABLE(name, offset, flags) \ + SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command, offset, flags) + +#define BUS_EXEC_COMMAND_LIST_VTABLE(name, offset, flags) \ + SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command_list, offset, flags) + +extern const sd_bus_vtable bus_exec_vtable[]; + +int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); +int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); +int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); + +int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/grp-system/libcore/dbus-job.c b/src/grp-system/libcore/dbus-job.c new file mode 100644 index 0000000000..ee1774da36 --- /dev/null +++ b/src/grp-system/libcore/dbus-job.c @@ -0,0 +1,194 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/string-util.h" + +#include "dbus-job.h" +#include "dbus.h" +#include "job.h" +#include "selinux-access.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); + +static int property_get_unit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Job *j = userdata; + + assert(bus); + assert(reply); + assert(j); + + p = unit_dbus_path(j->unit); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(so)", j->unit->id, p); +} + +int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Job *j = userdata; + int r; + + assert(message); + assert(j); + + r = mac_selinux_unit_access_check(j->unit, message, "stop", error); + if (r < 0) + return r; + + /* Access is granted to the job owner */ + if (!sd_bus_track_contains(j->clients, sd_bus_message_get_sender(message))) { + + /* And for everybody else consult PolicyKit */ + r = bus_verify_manage_units_async(j->unit->manager, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + } + + job_finish_and_invalidate(j, JOB_CANCELED, true, false); + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable bus_job_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("State", "s", property_get_state, offsetof(Job, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_VTABLE_END +}; + +static int send_new_signal(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + Job *j = userdata; + int r; + + assert(bus); + assert(j); + + p = job_dbus_path(j); + if (!p) + return -ENOMEM; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobNew"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "uos", j->id, p, j->unit->id); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +static int send_changed_signal(sd_bus *bus, void *userdata) { + _cleanup_free_ char *p = NULL; + Job *j = userdata; + + assert(bus); + assert(j); + + p = job_dbus_path(j); + if (!p) + return -ENOMEM; + + return sd_bus_emit_properties_changed(bus, p, "org.freedesktop.systemd1.Job", "State", NULL); +} + +void bus_job_send_change_signal(Job *j) { + int r; + + assert(j); + + if (j->in_dbus_queue) { + LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + j->in_dbus_queue = false; + } + + r = bus_foreach_bus(j->manager, j->clients, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); + if (r < 0) + log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id); + + j->sent_dbus_new_signal = true; +} + +static int send_removed_signal(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + Job *j = userdata; + int r; + + assert(bus); + assert(j); + + p = job_dbus_path(j); + if (!p) + return -ENOMEM; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "uoss", j->id, p, j->unit->id, job_result_to_string(j->result)); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +void bus_job_send_removed_signal(Job *j) { + int r; + + assert(j); + + if (!j->sent_dbus_new_signal) + bus_job_send_change_signal(j); + + r = bus_foreach_bus(j->manager, j->clients, send_removed_signal, j); + if (r < 0) + log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id); +} diff --git a/src/grp-system/libcore/dbus-job.h b/src/grp-system/libcore/dbus-job.h new file mode 100644 index 0000000000..95664cb90c --- /dev/null +++ b/src/grp-system/libcore/dbus-job.h @@ -0,0 +1,31 @@ +#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 . +***/ + +#include + +#include "job.h" + +extern const sd_bus_vtable bus_job_vtable[]; + +int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error); + +void bus_job_send_change_signal(Job *j); +void bus_job_send_removed_signal(Job *j); diff --git a/src/grp-system/libcore/dbus-kill.c b/src/grp-system/libcore/dbus-kill.c new file mode 100644 index 0000000000..44bc2db7e2 --- /dev/null +++ b/src/grp-system/libcore/dbus-kill.c @@ -0,0 +1,123 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/signal-util.h" +#include "shared/bus-util.h" + +#include "dbus-kill.h" +#include "kill.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode); + +const sd_bus_vtable bus_kill_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool, offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; + +int bus_kill_context_set_transient_property( + Unit *u, + KillContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "KillMode")) { + const char *m; + KillMode k; + + r = sd_bus_message_read(message, "s", &m); + if (r < 0) + return r; + + k = kill_mode_from_string(m); + if (k < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m); + + if (mode != UNIT_CHECK) { + c->kill_mode = k; + + unit_write_drop_in_private_format(u, mode, name, "KillMode=%s", kill_mode_to_string(k)); + } + + return 1; + + } else if (streq(name, "KillSignal")) { + int sig; + + r = sd_bus_message_read(message, "i", &sig); + if (r < 0) + return r; + + if (!SIGNAL_VALID(sig)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig); + + if (mode != UNIT_CHECK) { + c->kill_signal = sig; + + unit_write_drop_in_private_format(u, mode, name, "KillSignal=%s", signal_to_string(sig)); + } + + return 1; + + } else if (streq(name, "SendSIGHUP")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->send_sighup = b; + + unit_write_drop_in_private_format(u, mode, name, "SendSIGHUP=%s", yes_no(b)); + } + + return 1; + + } else if (streq(name, "SendSIGKILL")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->send_sigkill = b; + + unit_write_drop_in_private_format(u, mode, name, "SendSIGKILL=%s", yes_no(b)); + } + + return 1; + + } + + return 0; +} diff --git a/src/grp-system/libcore/dbus-kill.h b/src/grp-system/libcore/dbus-kill.h new file mode 100644 index 0000000000..b32ce9d223 --- /dev/null +++ b/src/grp-system/libcore/dbus-kill.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "kill.h" +#include "unit.h" + +extern const sd_bus_vtable bus_kill_vtable[]; + +int bus_kill_context_set_transient_property(Unit *u, KillContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/grp-system/libcore/dbus-manager.c b/src/grp-system/libcore/dbus-manager.c new file mode 100644 index 0000000000..c5684014d3 --- /dev/null +++ b/src/grp-system/libcore/dbus-manager.c @@ -0,0 +1,2307 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/build.h" +#include "basic/clock-util.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/virt.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/install.h" +#include "shared/watchdog.h" + +#include "dbus-execute.h" +#include "dbus-job.h" +#include "dbus-manager.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "selinux-access.h" + +static int property_get_version( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", PACKAGE_VERSION); +} + +static int property_get_features( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", SYSTEMD_FEATURES); +} + +static int property_get_virtualization( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int v; + + assert(bus); + assert(reply); + + 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( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", architecture_to_string(uname_architecture())); +} + +static int property_get_tainted( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf; + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + if (m->taint_usr) + e = stpcpy(e, "split-usr:"); + + if (access("/proc/cgroups", F_OK) < 0) + e = stpcpy(e, "cgroups-missing:"); + + if (clock_is_localtime(NULL) > 0) + e = stpcpy(e, "local-hwclock:"); + + /* remove the last ':' */ + if (e != buf) + e[-1] = 0; + + return sd_bus_message_append(reply, "s", buf); +} + +static int property_get_log_target( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", log_target_to_string(log_get_target())); +} + +static int property_set_log_target( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + const char *t; + int r; + + assert(bus); + assert(value); + + r = sd_bus_message_read(value, "s", &t); + if (r < 0) + return r; + + return log_set_target_from_string(t); +} + +static int property_get_log_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *t = NULL; + int r; + + assert(bus); + assert(reply); + + r = log_level_to_string_alloc(log_get_max_level(), &t); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "s", t); +} + +static int property_set_log_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + const char *t; + int r; + + assert(bus); + assert(value); + + r = sd_bus_message_read(value, "s", &t); + if (r < 0) + return r; + + 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( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->units)); +} + +static int property_get_n_failed_units( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "u", (uint32_t) set_size(m->failed_units)); +} + +static int property_get_n_jobs( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "u", (uint32_t) hashmap_size(m->jobs)); +} + +static int property_get_progress( + 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; + double d; + + assert(bus); + assert(reply); + assert(m); + + if (dual_timestamp_is_set(&m->finish_timestamp)) + d = 1.0; + else + d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs); + + return sd_bus_message_append(reply, "d", d); +} + +static int property_get_system_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "s", manager_state_to_string(manager_state(m))); +} + +static int property_set_runtime_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + usec_t *t = userdata; + int r; + + assert(bus); + assert(value); + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + r = sd_bus_message_read(value, "t", t); + if (r < 0) + return r; + + 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; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (isempty(name)) { + _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); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + u = manager_get_unit_by_pid(m, pid); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); + } else { + u = manager_get_unit(m, name); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name); + } + + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + + path = unit_dbus_path(u); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + +static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + Manager *m = userdata; + pid_t pid; + Unit *u; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + + /* Anyone can call this method */ + + r = sd_bus_message_read(message, "u", &pid); + if (r < 0) + return r; + if (pid < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pid); + + if (pid == 0) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + } + + u = manager_get_unit_by_pid(m, pid); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid); + + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + + path = unit_dbus_path(u); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + +static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (isempty(name)) { + _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); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + u = manager_get_unit_by_pid(m, pid); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit."); + } else { + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + } + + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + + path = unit_dbus_path(u); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + +static int method_start_unit_generic(sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) { + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + + return bus_unit_method_start_generic(message, u, job_type, reload_if_possible, error); +} + +static int method_start_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_START, false, error); +} + +static int method_stop_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_STOP, false, error); +} + +static int method_reload_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_RELOAD, false, error); +} + +static int method_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_RESTART, false, error); +} + +static int method_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, false, error); +} + +static int method_reload_or_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_RESTART, true, error); +} + +static int method_reload_or_try_restart_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error); +} + +static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *old_name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &old_name); + if (r < 0) + return r; + + u = manager_get_unit(m, old_name); + if (!u || !u->job || u->job->type != JOB_START) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name); + + return method_start_unit_generic(message, m, JOB_START, false, error); +} + +static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(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 is not loaded.", name); + + return bus_unit_method_kill(message, u, error); +} + +static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(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 is not loaded.", name); + + return bus_unit_method_reset_failed(message, u, error); +} + +static int method_set_unit_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + 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); +} + +static int reply_unit_info(sd_bus_message *reply, Unit *u) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + Unit *following; + + following = unit_following(u); + + unit_path = unit_dbus_path(u); + if (!unit_path) + return -ENOMEM; + + if (u->job) { + job_path = job_dbus_path(u->job); + if (!job_path) + return -ENOMEM; + } + + return sd_bus_message_append( + reply, "(ssssssouso)", + u->id, + unit_description(u), + unit_load_state_to_string(u->load_state), + unit_active_state_to_string(unit_active_state(u)), + unit_sub_state_to_string(u), + following ? following->id : "", + unit_path, + u->job ? u->job->id : 0, + u->job ? job_type_to_string(u->job->type) : "", + job_path ? job_path : "/"); +} + +static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + int r; + char **unit; + _cleanup_strv_free_ char **units = NULL; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &units); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + Unit *u; + + if (!unit_name_is_valid(*unit, UNIT_NAME_ANY)) + continue; + + r = manager_load_unit(m, *unit, NULL, error, &u); + if (r < 0) + return r; + + r = reply_unit_info(reply, u); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_get_unit_processes(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; + + 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_get_processes(message, u, error); +} + +static int transient_unit_from_message( + Manager *m, + sd_bus_message *message, + const char *name, + Unit **unit, + sd_bus_error *error) { + + UnitType t; + Unit *u; + int r; + + assert(m); + 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 (!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 + * fill in the transient data instead */ + r = unit_make_transient(u); + if (r < 0) + return r; + + /* Set our properties */ + r = bus_unit_set_properties(u, message, UNIT_RUNTIME, false, error); + if (r < 0) + return r; + + /* Now load the missing bits of the unit we just created */ + unit_add_to_load_queue(u); + manager_dispatch_load_queue(m); + + *unit = u; + + return 0; +} + +static int transient_aux_units_from_message( + Manager *m, + sd_bus_message *message, + sd_bus_error *error) { + + int r; + + assert(m); + assert(message); + + r = sd_bus_message_enter_container(message, 'a', "(sa(sv))"); + if (r < 0) + 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) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + return 0; +} + +static int method_start_transient_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *name, *smode; + Manager *m = userdata; + JobMode mode; + Unit *u; + 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, "ss", &name, &smode); + if (r < 0) + return r; + + 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); + + r = bus_verify_manage_units_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = transient_unit_from_message(m, message, name, &u, error); + if (r < 0) + return r; + + r = transient_aux_units_from_message(m, message, error); + if (r < 0) + return r; + + /* Finally, start it */ + return bus_unit_queue_job(message, u, JOB_START, mode, false, error); +} + +static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *path = NULL; + Manager *m = userdata; + uint32_t id; + Job *j; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = sd_bus_message_read(message, "u", &id); + if (r < 0) + return r; + + j = manager_get_job(m, id); + if (!j) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); + + r = mac_selinux_unit_access_check(j->unit, message, "status", error); + if (r < 0) + return r; + + path = job_dbus_path(j); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + +static int method_cancel_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + uint32_t id; + Job *j; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "u", &id); + if (r < 0) + return r; + + j = manager_get_job(m, id); + if (!j) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id); + + return bus_job_method_cancel(message, j, error); +} + +static int method_clear_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + manager_clear_jobs(m); + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + manager_reset_failed(m); + + return sd_bus_reply_method_return(message, NULL); +} + +static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + const char *k; + Iterator i; + Unit *u; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); + if (r < 0) + return r; + + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + if (k != u->id) + continue; + + if (!strv_isempty(states) && + !strv_contains(states, unit_load_state_to_string(u->load_state)) && + !strv_contains(states, unit_active_state_to_string(unit_active_state(u))) && + !strv_contains(states, unit_sub_state_to_string(u))) + continue; + + if (!strv_isempty(patterns) && + !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) + continue; + + r = reply_unit_info(reply, u); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return list_units_filtered(message, userdata, error, NULL, NULL); +} + +static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + return list_units_filtered(message, userdata, error, states, NULL); +} + +static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_units_filtered(message, userdata, error, states, patterns); +} + +static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + Iterator i; + Job *j; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(usssoo)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(j, m->jobs, i) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + + job_path = job_dbus_path(j); + if (!job_path) + return -ENOMEM; + + unit_path = unit_dbus_path(j->unit); + if (!unit_path) + return -ENOMEM; + + r = sd_bus_message_append( + reply, "(usssoo)", + j->id, + j->unit->id, + job_type_to_string(j->type), + job_state_to_string(j->state), + job_path, + unit_path); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_subscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + if (sd_bus_message_get_bus(message) == m->api_bus) { + + /* Note that direct bus connection subscribe by + * default, we only track peers on the API bus here */ + + if (!m->subscribed) { + r = sd_bus_track_new(sd_bus_message_get_bus(message), &m->subscribed, NULL, NULL); + if (r < 0) + return r; + } + + r = sd_bus_track_add_sender(m->subscribed, message); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed."); + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_unsubscribe(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + if (sd_bus_message_get_bus(message) == m->api_bus) { + r = sd_bus_track_remove_sender(m->subscribed, message); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed."); + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *dump = NULL; + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + size_t size; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + f = open_memstream(&dump, &size); + if (!f) + return -ENOMEM; + + manager_dump_units(m, f, NULL); + manager_dump_jobs(m, f, NULL); + + r = fflush_and_check(f); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "s", dump); +} + +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) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_reload_daemon_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + /* Instead of sending the reply back right away, we just + * remember that we need to and then send it after the reload + * is finished. That way the caller knows when the reload + * finished. */ + + assert(!m->queued_message); + r = sd_bus_message_new_method_return(message, &m->queued_message); + if (r < 0) + return r; + + m->exit_code = MANAGER_RELOAD; + + return 1; +} + +static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_reload_daemon_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + /* We don't send a reply back here, the client should + * just wait for us disconnecting. */ + + m->exit_code = MANAGER_REEXECUTE; + return 1; +} + +static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "halt", error); + if (r < 0) + return r; + + /* Exit() (in contrast to SetExitCode()) is actually allowed even if + * we are running on the host. It will fall back on reboot() in + * systemd-shutdown if it cannot do the exit() because it isn't a + * container. */ + + m->exit_code = MANAGER_EXIT; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reboot", error); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers."); + + m->exit_code = MANAGER_REBOOT; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "halt", error); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers."); + + m->exit_code = MANAGER_POWEROFF; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "halt", error); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers."); + + m->exit_code = MANAGER_HALT; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reboot", error); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers."); + + m->exit_code = MANAGER_KEXEC; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) { + char *ri = NULL, *rt = NULL; + const char *root, *init; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reboot", error); + if (r < 0) + return r; + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager."); + + r = sd_bus_message_read(message, "ss", &root, &init); + if (r < 0) + return r; + + if (path_equal(root, "/") || !path_is_absolute(root)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid switch root path %s", root); + + /* Safety check */ + if (isempty(init)) { + if (!path_is_os_tree(root)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path %s does not seem to be an OS tree. os-release file is missing.", root); + } else { + _cleanup_free_ char *p = NULL; + + if (!path_is_absolute(init)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid init path %s", init); + + p = strappend(root, init); + if (!p) + return -ENOMEM; + + if (access(p, X_OK) < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p); + } + + rt = strdup(root); + if (!rt) + return -ENOMEM; + + if (!isempty(init)) { + ri = strdup(init); + if (!ri) { + free(rt); + return -ENOMEM; + } + } + + free(m->switch_root); + m->switch_root = rt; + + free(m->switch_root_init); + m->switch_root_init = ri; + + m->exit_code = MANAGER_SWITCH_ROOT; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **plus = NULL; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &plus); + if (r < 0) + return r; + if (!strv_env_is_valid(plus)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_set_environment_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = manager_environment_add(m, NULL, plus); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_unset_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **minus = NULL; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &minus); + if (r < 0) + return r; + + if (!strv_env_name_or_assignment_is_valid(minus)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments"); + + r = bus_verify_set_environment_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = manager_environment_add(m, minus, NULL); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_unset_and_set_environment(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **minus = NULL, **plus = NULL; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &minus); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &plus); + if (r < 0) + return r; + + if (!strv_env_name_or_assignment_is_valid(minus)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments"); + if (!strv_env_is_valid(plus)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_set_environment_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = manager_environment_add(m, minus, plus); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) { + uint8_t code; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "exit", error); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(message, 'y', &code); + if (r < 0) + return r; + + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); + + m->return_value = code; + + return sd_bus_reply_method_return(message, NULL); +} + +static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + UnitFileList *item; + Hashmap *h; + Iterator i; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + h = hashmap_new(&string_hash_ops); + if (!h) + return -ENOMEM; + + r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns); + if (r < 0) + goto fail; + + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + goto fail; + + HASHMAP_FOREACH(item, h, i) { + + r = sd_bus_message_append(reply, "(ss)", item->path, unit_file_state_to_string(item->state)); + if (r < 0) + goto fail; + } + + unit_file_list_free(h); + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); + +fail: + unit_file_list_free(h); + return r; +} + +static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return list_unit_files_by_patterns(message, userdata, error, NULL, NULL); +} + +static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_unit_files_by_patterns(message, userdata, error, states, patterns); +} + +static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + UnitFileState state; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = unit_file_get_state(m->unit_file_scope, NULL, name, &state); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state)); +} + +static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *default_target = NULL; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + /* Anyone can call this method */ + + r = mac_selinux_access_check(message, "status", error); + if (r < 0) + return r; + + r = unit_file_get_default(m->unit_file_scope, NULL, &default_target); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "s", default_target); +} + +static int send_unit_files_changed(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + int r; + + assert(bus); + + r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged"); + if (r < 0) + return r; + + return sd_bus_send(bus, message, NULL); +} + +static int reply_unit_file_changes_and_free( + Manager *m, + sd_bus_message *message, + int carries_install_info, + UnitFileChange *changes, + unsigned n_changes) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + unsigned i; + int r; + + if (unit_file_changes_have_modification(changes, n_changes)) { + r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); + if (r < 0) + log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); + } + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + goto fail; + + if (carries_install_info >= 0) { + r = sd_bus_message_append(reply, "b", carries_install_info); + if (r < 0) + goto fail; + } + + r = sd_bus_message_open_container(reply, 'a', "(sss)"); + if (r < 0) + goto fail; + + for (i = 0; i < n_changes; i++) + if (changes[i].type >= 0) { + const char *change = unit_file_change_type_to_string(changes[i].type); + assert(change != NULL); + + r = sd_bus_message_append( + reply, "(sss)", + change, + changes[i].path, + changes[i].source); + if (r < 0) + goto fail; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto fail; + + unit_file_changes_free(changes, n_changes); + return sd_bus_send(NULL, reply, NULL); + +fail: + unit_file_changes_free(changes, n_changes); + return r; +} + +/* Create an error reply, using the error information from changes[] + * if possible, and fall back to generating an error from error code c. + * The error message only describes the first error. + * + * Coordinate with unit_file_dump_changes() in install.c. + */ +static int install_error( + sd_bus_error *error, + int c, + UnitFileChange *changes, + unsigned n_changes) { + int r; + unsigned i; + assert(c < 0); + + for (i = 0; i < n_changes; i++) + switch(changes[i].type) { + case 0 ... INT_MAX: + continue; + case -EEXIST: + if (changes[i].source) + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists and is a symlink to %s.", + changes[i].path, changes[i].source); + else + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists.", + changes[i].path); + goto found; + case -ERFKILL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, + "Unit file %s is masked.", changes[i].path); + goto found; + case -EADDRNOTAVAIL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, + "Unit %s is transient or generated.", changes[i].path); + goto found; + case -ELOOP: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, + "Refusing to operate on linked unit file %s", changes[i].path); + goto found; + default: + r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); + goto found; + } + + r = c; + found: + unit_file_changes_free(changes, n_changes); + return r; +} + +static int method_enable_unit_files_generic( + sd_bus_message *message, + Manager *m, + int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes), + bool carries_install_info, + sd_bus_error *error) { + + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int runtime, force, r; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "bb", &runtime, &force); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); +} + +static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error); +} + +static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error); +} + +static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); +} + +static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) { + return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes); +} + +static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error); +} + +static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error); +} + +static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) { + + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + UnitFilePresetMode mm; + int runtime, force, r; + const char *mode; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force); + if (r < 0) + return r; + + if (isempty(mode)) + mm = UNIT_FILE_PRESET_FULL; + else { + mm = unit_file_preset_mode_from_string(mode); + if (mm < 0) + return -EINVAL; + } + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); +} + +static int method_disable_unit_files_generic( + sd_bus_message *message, + Manager *m, + int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), + sd_bus_error *error) { + + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int r, runtime; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &runtime); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + +static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_disable_unit_files_generic(message, userdata, unit_file_disable, error); +} + +static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error); +} + +static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + +static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + const char *name; + int force, r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "enable", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "sb", &name, &force); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + +static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + UnitFilePresetMode mm; + const char *mode; + int force, runtime, r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "enable", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "sbb", &mode, &runtime, &force); + if (r < 0) + return r; + + if (isempty(mode)) + mm = UNIT_FILE_PRESET_FULL; + else { + mm = unit_file_preset_mode_from_string(mode); + if (mm < 0) + return -EINVAL; + } + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + +static int method_add_dependency_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + Manager *m = userdata; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int runtime, force, r; + char *target, *type; + UnitDependency dep; + + assert(message); + assert(m); + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "ssbb", &target, &type, &runtime, &force); + if (r < 0) + return r; + + dep = unit_dependency_from_string(type); + if (dep < 0) + return -EINVAL; + + r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); +} + +const sd_bus_vtable bus_manager_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Version", "s", property_get_version, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Features", "s", property_get_features, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Virtualization", "s", property_get_virtualization, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Architecture", "s", property_get_architecture, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Tainted", "s", property_get_tainted, 0, SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("FirmwareTimestamp", offsetof(Manager, firmware_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("LoaderTimestamp", offsetof(Manager, loader_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("KernelTimestamp", offsetof(Manager, kernel_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("InitRDTimestamp", offsetof(Manager, initrd_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("UserspaceTimestamp", offsetof(Manager, userspace_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("FinishTimestamp", offsetof(Manager, finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("SecurityStartTimestamp", offsetof(Manager, security_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("SecurityFinishTimestamp", offsetof(Manager, security_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsStartTimestamp", offsetof(Manager, generators_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("GeneratorsFinishTimestamp", offsetof(Manager, generators_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadStartTimestamp", offsetof(Manager, units_load_start_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("UnitsLoadFinishTimestamp", offsetof(Manager, units_load_finish_timestamp), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0), + SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0), + SD_BUS_PROPERTY("NNames", "u", property_get_n_names, 0, 0), + SD_BUS_PROPERTY("NFailedUnits", "u", property_get_n_failed_units, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("NJobs", "u", property_get_n_jobs, 0, 0), + SD_BUS_PROPERTY("NInstalledJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_installed_jobs), 0), + SD_BUS_PROPERTY("NFailedJobs", "u", bus_property_get_unsigned, offsetof(Manager, n_failed_jobs), 0), + SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), + SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0), + SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), + 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("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), 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_VTABLE_HIDDEN), /* obsolete alias name */ + 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), + SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("StopUnit", "ss", "o", method_stop_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadUnit", "ss", "o", method_reload_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RestartUnit", "ss", "o", method_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_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), + SD_BUS_METHOD("Reboot", NULL, NULL, method_reboot, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("PowerOff", NULL, NULL, method_poweroff, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("Halt", NULL, NULL, method_halt, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("KExec", NULL, NULL, method_kexec, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("SwitchRoot", "ss", NULL, method_switch_root, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)), + SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_SIGNAL("UnitNew", "so", 0), + SD_BUS_SIGNAL("UnitRemoved", "so", 0), + SD_BUS_SIGNAL("JobNew", "uos", 0), + SD_BUS_SIGNAL("JobRemoved", "uoss", 0), + SD_BUS_SIGNAL("StartupFinished", "tttttt", 0), + SD_BUS_SIGNAL("UnitFilesChanged", NULL, 0), + SD_BUS_SIGNAL("Reloading", "b", 0), + + SD_BUS_VTABLE_END +}; + +static int send_finished(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + usec_t *times = userdata; + int r; + + assert(bus); + assert(times); + + r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished"); + if (r < 0) + return r; + + r = sd_bus_message_append(message, "tttttt", times[0], times[1], times[2], times[3], times[4], times[5]); + if (r < 0) + return r; + + return sd_bus_send(bus, message, NULL); +} + +void bus_manager_send_finished( + Manager *m, + usec_t firmware_usec, + usec_t loader_usec, + usec_t kernel_usec, + usec_t initrd_usec, + usec_t userspace_usec, + usec_t total_usec) { + + int r; + + assert(m); + + r = bus_foreach_bus( + m, + NULL, + send_finished, + (usec_t[6]) { + firmware_usec, + loader_usec, + kernel_usec, + initrd_usec, + userspace_usec, + total_usec + }); + if (r < 0) + log_debug_errno(r, "Failed to send finished signal: %m"); +} + +static int send_reloading(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + int r; + + assert(bus); + + r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Reloading"); + if (r < 0) + return r; + + r = sd_bus_message_append(message, "b", PTR_TO_INT(userdata)); + if (r < 0) + return r; + + return sd_bus_send(bus, message, NULL); +} + +void bus_manager_send_reloading(Manager *m, bool active) { + int r; + + assert(m); + + r = bus_foreach_bus(m, NULL, send_reloading, INT_TO_PTR(active)); + if (r < 0) + log_debug_errno(r, "Failed to send reloading signal: %m"); +} + +static int send_changed_signal(sd_bus *bus, void *userdata) { + assert(bus); + + return sd_bus_emit_properties_changed_strv(bus, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + NULL); +} + +void bus_manager_send_change_signal(Manager *m) { + int r; + + assert(m); + + r = bus_foreach_bus(m, NULL, send_changed_signal, NULL); + if (r < 0) + log_debug_errno(r, "Failed to send manager change signal: %m"); +} diff --git a/src/grp-system/libcore/dbus-manager.h b/src/grp-system/libcore/dbus-manager.h new file mode 100644 index 0000000000..36a2e9481b --- /dev/null +++ b/src/grp-system/libcore/dbus-manager.h @@ -0,0 +1,28 @@ +#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 . +***/ + +#include "manager.h" + +extern const sd_bus_vtable bus_manager_vtable[]; + +void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); +void bus_manager_send_reloading(Manager *m, bool active); +void bus_manager_send_change_signal(Manager *m); diff --git a/src/grp-system/libcore/dbus-mount.c b/src/grp-system/libcore/dbus-mount.c new file mode 100644 index 0000000000..837a262f61 --- /dev/null +++ b/src/grp-system/libcore/dbus-mount.c @@ -0,0 +1,212 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "dbus-cgroup.h" +#include "dbus-execute.h" +#include "dbus-kill.h" +#include "dbus-mount.h" +#include "mount.h" +#include "unit.h" + +static int property_get_what( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Mount *m = userdata; + const char *d; + + assert(bus); + assert(reply); + assert(m); + + if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what) + d = m->parameters_proc_self_mountinfo.what; + else if (m->from_fragment && m->parameters_fragment.what) + d = m->parameters_fragment.what; + else + d = ""; + + return sd_bus_message_append(reply, "s", d); +} + +static int property_get_options( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Mount *m = userdata; + const char *d; + + assert(bus); + assert(reply); + assert(m); + + if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options) + d = m->parameters_proc_self_mountinfo.options; + else if (m->from_fragment && m->parameters_fragment.options) + d = m->parameters_fragment.options; + else + d = ""; + + return sd_bus_message_append(reply, "s", d); +} + +static int property_get_type( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Mount *m = userdata; + const char *d; + + assert(bus); + assert(reply); + assert(m); + + if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) + d = m->parameters_proc_self_mountinfo.fstype; + else if (m->from_fragment && m->parameters_fragment.fstype) + d = m->parameters_fragment.fstype; + else + d = ""; + + return sd_bus_message_append(reply, "s", d); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResult); + +const sd_bus_vtable bus_mount_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Where", "s", NULL, offsetof(Mount, where), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("What", "s", property_get_what, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Options","s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Type", "s", property_get_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Mount, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +static int bus_mount_set_transient_property( + Mount *m, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + const char *new_property; + char **property; + char *p; + int r; + + assert(m); + assert(name); + assert(message); + + if (streq(name, "What")) + property = &m->parameters_fragment.what; + else if (streq(name, "Options")) + property = &m->parameters_fragment.options; + else if (streq(name, "Type")) + property = &m->parameters_fragment.fstype; + else + return 0; + + r = sd_bus_message_read(message, "s", &new_property); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + p = strdup(new_property); + if (!p) + return -ENOMEM; + + free(*property); + *property = p; + } + + return 1; +} + +int bus_mount_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Mount *m = MOUNT(u); + int r; + + assert(m); + assert(name); + assert(message); + + r = bus_cgroup_set_property(u, &m->cgroup_context, name, message, mode, error); + if (r != 0) + return r; + + if (u->transient && u->load_state == UNIT_STUB) { + /* This is a transient unit, let's load a little more */ + + r = bus_mount_set_transient_property(m, name, message, mode, error); + if (r != 0) + return r; + + r = bus_exec_context_set_transient_property(u, &m->exec_context, name, message, mode, error); + if (r != 0) + return r; + + r = bus_kill_context_set_transient_property(u, &m->kill_context, name, message, mode, error); + if (r != 0) + return r; + } + + return 0; +} + +int bus_mount_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} diff --git a/src/grp-system/libcore/dbus-mount.h b/src/grp-system/libcore/dbus-mount.h new file mode 100644 index 0000000000..f9844e449d --- /dev/null +++ b/src/grp-system/libcore/dbus-mount.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_mount_vtable[]; + +int bus_mount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_mount_commit_properties(Unit *u); diff --git a/src/grp-system/libcore/dbus-path.c b/src/grp-system/libcore/dbus-path.c new file mode 100644 index 0000000000..d312652be4 --- /dev/null +++ b/src/grp-system/libcore/dbus-path.c @@ -0,0 +1,87 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "dbus-path.h" +#include "path.h" +#include "unit.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, path_result, PathResult); + +static int property_get_paths( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Path *p = userdata; + PathSpec *k; + int r; + + assert(bus); + assert(reply); + assert(p); + + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + return r; + + LIST_FOREACH(spec, k, p->specs) { + r = sd_bus_message_append(reply, "(ss)", path_type_to_string(k->type), k->path); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_unit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *p = userdata, *trigger; + + assert(bus); + assert(reply); + assert(p); + + trigger = UNIT_TRIGGER(p); + + return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); +} + +const sd_bus_vtable bus_path_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Paths", "a(ss)", property_get_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MakeDirectory", "b", bus_property_get_bool, offsetof(Path, make_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Path, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_VTABLE_END +}; diff --git a/src/grp-system/libcore/dbus-path.h b/src/grp-system/libcore/dbus-path.h new file mode 100644 index 0000000000..d3c19e0c2b --- /dev/null +++ b/src/grp-system/libcore/dbus-path.h @@ -0,0 +1,24 @@ +#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 . +***/ + + + +extern const sd_bus_vtable bus_path_vtable[]; diff --git a/src/grp-system/libcore/dbus-scope.c b/src/grp-system/libcore/dbus-scope.c new file mode 100644 index 0000000000..d319597c10 --- /dev/null +++ b/src/grp-system/libcore/dbus-scope.c @@ -0,0 +1,230 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-internal.h" +#include "shared/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; + 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 */ + + r = scope_abandon(s); + if (r == -ESTALE) + return sd_bus_error_setf(error, BUS_ERROR_SCOPE_NOT_RUNNING, "Scope %s is not running, cannot abandon.", UNIT(s)->id); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, scope_result, ScopeResult); + +const sd_bus_vtable bus_scope_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Controller", "s", NULL, offsetof(Scope, controller), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Scope, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Scope, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_SIGNAL("RequestStop", NULL, 0), + SD_BUS_METHOD("Abandon", NULL, NULL, bus_scope_abandon, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +static int bus_scope_set_transient_property( + Scope *s, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(s); + assert(name); + assert(message); + + if (streq(name, "PIDs")) { + unsigned n = 0; + uint32_t pid; + + r = sd_bus_message_enter_container(message, 'a', "u"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "u", &pid)) > 0) { + + if (pid <= 1) + return -EINVAL; + + if (mode != UNIT_CHECK) { + r = unit_watch_pid(UNIT(s), pid); + if (r < 0 && r != -EEXIST) + return r; + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (n <= 0) + return -EINVAL; + + return 1; + + } else if (streq(name, "Controller")) { + const char *controller; + char *c; + + r = sd_bus_message_read(message, "s", &controller); + if (r < 0) + return r; + + if (!isempty(controller) && !service_name_is_valid(controller)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Controller '%s' is not a valid bus name.", controller); + + if (mode != UNIT_CHECK) { + if (isempty(controller)) + c = NULL; + else { + c = strdup(controller); + if (!c) + return -ENOMEM; + } + + free(s->controller); + s->controller = c; + } + + return 1; + + } else if (streq(name, "TimeoutStopUSec")) { + + if (mode != UNIT_CHECK) { + r = sd_bus_message_read(message, "t", &s->timeout_stop_usec); + if (r < 0) + return r; + + unit_write_drop_in_private_format(UNIT(s), mode, name, "TimeoutStopSec="USEC_FMT"us", s->timeout_stop_usec); + } else { + r = sd_bus_message_skip(message, "t"); + if (r < 0) + return r; + } + + return 1; + } + + return 0; +} + +int bus_scope_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Scope *s = SCOPE(u); + int r; + + assert(s); + assert(name); + assert(message); + + r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); + if (r != 0) + return r; + + if (u->load_state == UNIT_STUB) { + /* While we are created we still accept PIDs */ + + r = bus_scope_set_transient_property(s, name, message, mode, error); + if (r != 0) + return r; + + r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, mode, error); + if (r != 0) + return r; + } + + return 0; +} + +int bus_scope_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} + +int bus_scope_send_request_stop(Scope *s) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + int r; + + assert(s); + + if (!s->controller) + return 0; + + p = unit_dbus_path(UNIT(s)); + if (!p) + return -ENOMEM; + + r = sd_bus_message_new_signal( + UNIT(s)->manager->api_bus, + &m, + p, + "org.freedesktop.systemd1.Scope", + "RequestStop"); + if (r < 0) + return r; + + return sd_bus_send_to(UNIT(s)->manager->api_bus, m, s->controller, NULL); +} diff --git a/src/grp-system/libcore/dbus-scope.h b/src/grp-system/libcore/dbus-scope.h new file mode 100644 index 0000000000..f96ddef0cf --- /dev/null +++ b/src/grp-system/libcore/dbus-scope.h @@ -0,0 +1,31 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_scope_vtable[]; + +int bus_scope_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_scope_commit_properties(Unit *u); + +int bus_scope_send_request_stop(Scope *s); diff --git a/src/grp-system/libcore/dbus-service.c b/src/grp-system/libcore/dbus-service.c new file mode 100644 index 0000000000..a3524815bd --- /dev/null +++ b/src/grp-system/libcore/dbus-service.c @@ -0,0 +1,323 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/bus-util.h" + +#include "dbus-cgroup.h" +#include "dbus-execute.h" +#include "dbus-kill.h" +#include "dbus-service.h" +#include "service.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); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); + +const sd_bus_vtable bus_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), + BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), + /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("GuessMainPID", "b", bus_property_get_bool, offsetof(Service, guess_main_pid), SD_BUS_VTABLE_PROPERTY_CONST), + 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", 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), + SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +static int bus_service_set_transient_property( + Service *s, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(s); + assert(name); + assert(message); + + if (streq(name, "RemainAfterExit")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + s->remain_after_exit = b; + unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s", yes_no(b)); + } + + return 1; + + } else if (streq(name, "Type")) { + const char *t; + ServiceType k; + + r = sd_bus_message_read(message, "s", &t); + if (r < 0) + return r; + + k = service_type_from_string(t); + if (k < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service type %s", t); + + if (mode != UNIT_CHECK) { + s->type = k; + unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s", service_type_to_string(s->type)); + } + + return 1; + } else if (streq(name, "RuntimeMaxUSec")) { + usec_t u; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + s->runtime_max_usec = u; + unit_write_drop_in_private_format(UNIT(s), mode, name, "RuntimeMaxSec=" USEC_FMT "us", u); + } + + 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; + } + + s->exec_context.stdio_as_fds = true; + } + + return 1; + + } else if (streq(name, "ExecStart")) { + unsigned n = 0; + + r = sd_bus_message_enter_container(message, 'a', "(sasb)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { + _cleanup_strv_free_ char **argv = NULL; + const char *path; + int b; + + r = sd_bus_message_read(message, "s", &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return sd_bus_error_set_errnof(error, EINVAL, "Path %s is not absolute.", path); + + r = sd_bus_message_read_strv(message, &argv); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + ExecCommand *c; + + c = new0(ExecCommand, 1); + if (!c) + return -ENOMEM; + + c->path = strdup(path); + if (!c->path) { + free(c); + return -ENOMEM; + } + + c->argv = argv; + argv = NULL; + + c->ignore = b; + + path_kill_slashes(c->path); + exec_command_append_list(&s->exec_command[SERVICE_EXEC_START], c); + } + + n++; + } + + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + ExecCommand *c; + size_t size = 0; + + if (n == 0) + s->exec_command[SERVICE_EXEC_START] = exec_command_free_list(s->exec_command[SERVICE_EXEC_START]); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fputs("ExecStart=\n", f); + + LIST_FOREACH(command, c, s->exec_command[SERVICE_EXEC_START]) { + _cleanup_free_ char *a; + + a = strv_join_quoted(c->argv); + if (!a) + return -ENOMEM; + + fprintf(f, "ExecStart=%s@%s %s\n", + c->ignore ? "-" : "", + c->path, + a); + } + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(UNIT(s), mode, name, buf); + } + + return 1; + } + + return 0; +} + +int bus_service_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Service *s = SERVICE(u); + int r; + + assert(s); + assert(name); + assert(message); + + r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); + if (r != 0) + return r; + + if (u->transient && u->load_state == UNIT_STUB) { + /* This is a transient unit, let's load a little more */ + + r = bus_service_set_transient_property(s, name, message, mode, error); + if (r != 0) + return r; + + r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, mode, error); + if (r != 0) + return r; + + r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, mode, error); + if (r != 0) + return r; + } + + return 0; +} + +int bus_service_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} diff --git a/src/grp-system/libcore/dbus-service.h b/src/grp-system/libcore/dbus-service.h new file mode 100644 index 0000000000..291959325c --- /dev/null +++ b/src/grp-system/libcore/dbus-service.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_service_vtable[]; + +int bus_service_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_service_commit_properties(Unit *u); diff --git a/src/grp-system/libcore/dbus-slice.c b/src/grp-system/libcore/dbus-slice.c new file mode 100644 index 0000000000..e37f50b283 --- /dev/null +++ b/src/grp-system/libcore/dbus-slice.c @@ -0,0 +1,52 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "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), + SD_BUS_VTABLE_END +}; + +int bus_slice_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Slice *s = SLICE(u); + + assert(name); + assert(u); + + return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); +} + +int bus_slice_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} diff --git a/src/grp-system/libcore/dbus-slice.h b/src/grp-system/libcore/dbus-slice.h new file mode 100644 index 0000000000..8e4cabbf8a --- /dev/null +++ b/src/grp-system/libcore/dbus-slice.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_slice_vtable[]; + +int bus_slice_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_slice_commit_properties(Unit *u); diff --git a/src/grp-system/libcore/dbus-socket.c b/src/grp-system/libcore/dbus-socket.c new file mode 100644 index 0000000000..4d99628725 --- /dev/null +++ b/src/grp-system/libcore/dbus-socket.c @@ -0,0 +1,185 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "dbus-cgroup.h" +#include "dbus-execute.h" +#include "dbus-socket.h" +#include "socket.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); + +static int property_get_listen( + 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); + SocketPort *p; + int r; + + assert(bus); + assert(reply); + assert(s); + + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + return r; + + LIST_FOREACH(port, p, s->ports) { + _cleanup_free_ char *address = NULL; + const char *a; + + switch (p->type) { + case SOCKET_SOCKET: { + r = socket_address_print(&p->address, &address); + if (r) + return r; + + a = address; + break; + } + + case SOCKET_SPECIAL: + case SOCKET_MQUEUE: + case SOCKET_FIFO: + case SOCKET_USB_FUNCTION: + a = p->path; + break; + + default: + assert_not_reached("Unknown socket type"); + } + + r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), a); + if (r < 0) + return r; + } + + 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), + SD_BUS_PROPERTY("Backlog", "u", bus_property_get_unsigned, offsetof(Socket, backlog), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Socket, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("BindToDevice", "s", NULL, offsetof(Socket, bind_to_device), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SocketUser", "s", NULL, offsetof(Socket, user), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SocketGroup", "s", NULL, offsetof(Socket, group), SD_BUS_VTABLE_PROPERTY_CONST), + 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), + SD_BUS_PROPERTY("KeepAliveProbes", "u", bus_property_get_unsigned, offsetof(Socket, keep_alive_cnt), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DeferAcceptUSec" , "t", bus_property_get_usec, offsetof(Socket, defer_accept), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NoDelay", "b", bus_property_get_bool, offsetof(Socket, no_delay), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Priority", "i", bus_property_get_int, offsetof(Socket, priority), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ReceiveBuffer", "t", bus_property_get_size, offsetof(Socket, receive_buffer), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SendBuffer", "t", bus_property_get_size, offsetof(Socket, send_buffer), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IPTOS", "i", bus_property_get_int, offsetof(Socket, ip_tos), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IPTTL", "i", bus_property_get_int, offsetof(Socket, ip_ttl), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PipeSize", "t", bus_property_get_size, offsetof(Socket, pipe_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FreeBind", "b", bus_property_get_bool, offsetof(Socket, free_bind), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Transparent", "b", bus_property_get_bool, offsetof(Socket, transparent), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SmackLabel", "s", NULL, offsetof(Socket, smack), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SmackLabelIPIn", "s", NULL, offsetof(Socket, smack_ip_in), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SmackLabelIPOut", "s", NULL, offsetof(Socket, smack_ip_out), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Socket, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + 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), + SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), 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), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +int bus_socket_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Socket *s = SOCKET(u); + + assert(s); + assert(name); + assert(message); + + return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); +} + +int bus_socket_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} diff --git a/src/grp-system/libcore/dbus-socket.h b/src/grp-system/libcore/dbus-socket.h new file mode 100644 index 0000000000..a31906feea --- /dev/null +++ b/src/grp-system/libcore/dbus-socket.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_socket_vtable[]; + +int bus_socket_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_socket_commit_properties(Unit *u); diff --git a/src/grp-system/libcore/dbus-swap.c b/src/grp-system/libcore/dbus-swap.c new file mode 100644 index 0000000000..fc911b038f --- /dev/null +++ b/src/grp-system/libcore/dbus-swap.c @@ -0,0 +1,116 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2010 Maarten Lankhorst + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "dbus-cgroup.h" +#include "dbus-execute.h" +#include "dbus-swap.h" +#include "swap.h" +#include "unit.h" + +static int property_get_priority( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Swap *s = SWAP(userdata); + int p; + + assert(bus); + assert(reply); + assert(s); + + if (s->from_proc_swaps) + p = s->parameters_proc_swaps.priority; + else if (s->from_fragment) + p = s->parameters_fragment.priority; + else + p = -1; + + return sd_bus_message_append(reply, "i", p); +} + +static int property_get_options( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Swap *s = SWAP(userdata); + const char *options = NULL; + + assert(bus); + assert(reply); + assert(s); + + if (s->from_fragment) + options = s->parameters_fragment.options; + + return sd_bus_message_append(reply, "s", options); +} + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, swap_result, SwapResult); + +const sd_bus_vtable bus_swap_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("What", "s", NULL, offsetof(Swap, what), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Priority", "i", property_get_priority, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Options", "s", property_get_options, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +int bus_swap_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Swap *s = SWAP(u); + + assert(s); + assert(name); + assert(message); + + return bus_cgroup_set_property(u, &s->cgroup_context, name, message, mode, error); +} + +int bus_swap_commit_properties(Unit *u) { + assert(u); + + unit_update_cgroup_members_masks(u); + unit_realize_cgroup(u); + + return 0; +} diff --git a/src/grp-system/libcore/dbus-swap.h b/src/grp-system/libcore/dbus-swap.h new file mode 100644 index 0000000000..19151fb771 --- /dev/null +++ b/src/grp-system/libcore/dbus-swap.h @@ -0,0 +1,30 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2010 Maarten Lankhorst + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_swap_vtable[]; + +int bus_swap_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); +int bus_swap_commit_properties(Unit *u); diff --git a/src/grp-system/libcore/dbus-target.c b/src/grp-system/libcore/dbus-target.c new file mode 100644 index 0000000000..6858b1ce72 --- /dev/null +++ b/src/grp-system/libcore/dbus-target.c @@ -0,0 +1,26 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "dbus-target.h" +#include "unit.h" + +const sd_bus_vtable bus_target_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_VTABLE_END +}; diff --git a/src/grp-system/libcore/dbus-target.h b/src/grp-system/libcore/dbus-target.h new file mode 100644 index 0000000000..c97a9d626e --- /dev/null +++ b/src/grp-system/libcore/dbus-target.h @@ -0,0 +1,24 @@ +#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 . +***/ + +#include + +extern const sd_bus_vtable bus_target_vtable[]; diff --git a/src/grp-system/libcore/dbus-timer.c b/src/grp-system/libcore/dbus-timer.c new file mode 100644 index 0000000000..ce454385f8 --- /dev/null +++ b/src/grp-system/libcore/dbus-timer.c @@ -0,0 +1,353 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/strv.h" +#include "shared/bus-util.h" + +#include "dbus-timer.h" +#include "timer.h" +#include "unit.h" + +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult); + +static int property_get_monotonic_timers( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Timer *t = userdata; + TimerValue *v; + int r; + + assert(bus); + assert(reply); + assert(t); + + r = sd_bus_message_open_container(reply, 'a', "(stt)"); + if (r < 0) + return r; + + LIST_FOREACH(value, v, t->values) { + _cleanup_free_ char *buf = NULL; + const char *s; + size_t l; + + if (v->base == TIMER_CALENDAR) + continue; + + s = timer_base_to_string(v->base); + assert(endswith(s, "Sec")); + + /* s/Sec/USec/ */ + l = strlen(s); + buf = new(char, l+2); + if (!buf) + return -ENOMEM; + + memcpy(buf, s, l-3); + memcpy(buf+l-3, "USec", 5); + + r = sd_bus_message_append(reply, "(stt)", buf, v->value, v->next_elapse); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_calendar_timers( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Timer *t = userdata; + TimerValue *v; + int r; + + assert(bus); + assert(reply); + assert(t); + + r = sd_bus_message_open_container(reply, 'a', "(sst)"); + if (r < 0) + return r; + + LIST_FOREACH(value, v, t->values) { + _cleanup_free_ char *buf = NULL; + + if (v->base != TIMER_CALENDAR) + continue; + + r = calendar_spec_to_string(v->calendar_spec, &buf); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "(sst)", timer_base_to_string(v->base), buf, v->next_elapse); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_unit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata, *trigger; + + assert(bus); + assert(reply); + assert(u); + + trigger = UNIT_TRIGGER(u); + + return sd_bus_message_append(reply, "s", trigger ? trigger->id : ""); +} + +static int property_get_next_elapse_monotonic( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Timer *t = userdata; + usec_t x; + + assert(bus); + assert(reply); + assert(t); + + if (t->next_elapse_monotonic_or_boottime <= 0) + x = 0; + else if (t->wake_system) { + usec_t a, b; + + a = now(CLOCK_MONOTONIC); + b = now(clock_boottime_or_monotonic()); + + if (t->next_elapse_monotonic_or_boottime + a > b) + x = t->next_elapse_monotonic_or_boottime + a - b; + else + x = 0; + } else + x = t->next_elapse_monotonic_or_boottime; + + return sd_bus_message_append(reply, "t", x); +} + +const sd_bus_vtable bus_timer_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + 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 +}; + +static int bus_timer_set_transient_property( + Timer *t, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(t); + assert(name); + assert(message); + + if (STR_IN_SET(name, + "OnActiveSec", + "OnBootSec", + "OnStartupSec", + "OnUnitActiveSec", + "OnUnitInactiveSec")) { + + TimerValue *v; + TimerBase b = _TIMER_BASE_INVALID; + usec_t u = 0; + + b = timer_base_from_string(name); + if (b < 0) + return -EINVAL; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + + v = new0(TimerValue, 1); + if (!v) + return -ENOMEM; + + v->base = b; + v->value = u; + + LIST_PREPEND(value, t->values, v); + } + + return 1; + + } else if (streq(name, "OnCalendar")) { + + TimerValue *v; + CalendarSpec *c = NULL; + const char *str; + + r = sd_bus_message_read(message, "s", &str); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + r = calendar_spec_from_string(str, &c); + if (r < 0) + return r; + + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, str); + + v = new0(TimerValue, 1); + if (!v) { + calendar_spec_free(c); + return -ENOMEM; + } + + v->base = TIMER_CALENDAR; + v->calendar_spec = c; + + LIST_PREPEND(value, t->values, v); + } + + return 1; + + } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) { + usec_t u = 0; + + if (streq(name, "AccuracySec")) + log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + t->accuracy_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "AccuracySec=" USEC_FMT "us", u); + } + + return 1; + + } 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) { + t->random_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=" USEC_FMT "us", u); + } + + return 1; + + } else if (streq(name, "WakeSystem")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + t->wake_system = b; + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", 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", name, yes_no(b)); + } + + return 1; + } + + return 0; +} + +int bus_timer_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Timer *t = TIMER(u); + int r; + + assert(t); + assert(name); + assert(message); + + if (u->transient && u->load_state == UNIT_STUB) { + r = bus_timer_set_transient_property(t, name, message, mode, error); + if (r != 0) + return r; + } + + return 0; +} diff --git a/src/grp-system/libcore/dbus-timer.h b/src/grp-system/libcore/dbus-timer.h new file mode 100644 index 0000000000..505fb5df72 --- /dev/null +++ b/src/grp-system/libcore/dbus-timer.h @@ -0,0 +1,28 @@ +#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 . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_timer_vtable[]; + +int bus_timer_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/grp-system/libcore/dbus-unit.c b/src/grp-system/libcore/dbus-unit.c new file mode 100644 index 0000000000..7e2a63b434 --- /dev/null +++ b/src/grp-system/libcore/dbus-unit.c @@ -0,0 +1,1424 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" + +#include "dbus-unit.h" +#include "dbus.h" +#include "selinux-access.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); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); + +static int property_get_names( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + Iterator i; + const char *t; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + SET_FOREACH(t, u->names, i) { + r = sd_bus_message_append(reply, "s", t); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_following( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata, *f; + + assert(bus); + assert(reply); + assert(u); + + f = unit_following(u); + return sd_bus_message_append(reply, "s", f ? f->id : ""); +} + +static int property_get_dependencies( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Set *s = *(Set**) userdata; + Iterator j; + Unit *u; + int r; + + assert(bus); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + SET_FOREACH(u, s, j) { + r = sd_bus_message_append(reply, "s", u->id); + if (r < 0) + return r; + } + + 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, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", unit_description(u)); +} + +static int property_get_active_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", unit_active_state_to_string(unit_active_state(u))); +} + +static int property_get_sub_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u)); +} + +static int property_get_unit_file_preset( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_unit_file_preset(u); + + return sd_bus_message_append(reply, "s", + r < 0 ? "": + r > 0 ? "enabled" : "disabled"); +} + +static int property_get_unit_file_state( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", unit_file_state_to_string(unit_get_unit_file_state(u))); +} + +static int property_get_can_start( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_start); +} + +static int property_get_can_stop( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + /* On the lower levels we assume that every unit we can start + * we can also stop */ + + return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop); +} + +static int property_get_can_reload( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", unit_can_reload(u)); +} + +static int property_get_can_isolate( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", unit_can_isolate(u) && !u->refuse_manual_start); +} + +static int property_get_job( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + if (!u->job) + return sd_bus_message_append(reply, "(uo)", 0, "/"); + + p = job_dbus_path(u->job); + if (!p) + return -ENOMEM; + + return sd_bus_message_append(reply, "(uo)", u->job->id, p); +} + +static int property_get_need_daemon_reload( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "b", unit_need_daemon_reload(u)); +} + +static int property_get_conditions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + const char *(*to_string)(ConditionType type) = NULL; + Condition **list = userdata, *c; + int r; + + assert(bus); + assert(reply); + assert(list); + + to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string; + + r = sd_bus_message_open_container(reply, 'a', "(sbbsi)"); + if (r < 0) + return r; + + LIST_FOREACH(conditions, c, *list) { + int tristate; + + tristate = + c->result == CONDITION_UNTESTED ? 0 : + c->result == CONDITION_SUCCEEDED ? 1 : -1; + + r = sd_bus_message_append(reply, "(sbbsi)", + to_string(c->type), + c->trigger, c->negate, + c->parameter, tristate); + if (r < 0) + return r; + + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_load_error( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + if (u->load_error != 0) + sd_bus_error_set_errno(&e, u->load_error); + + return sd_bus_message_append(reply, "(ss)", e.name, e.message); +} + +static int bus_verify_manage_units_async_full( + Unit *u, + const char *verb, + int capability, + const char *polkit_message, + sd_bus_message *call, + sd_bus_error *error) { + + const char *details[9] = { + "unit", u->id, + "verb", verb, + }; + + if (polkit_message) { + details[4] = "polkit.message"; + details[5] = polkit_message; + details[6] = "polkit.gettext_domain"; + details[7] = GETTEXT_PACKAGE; + } + + return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error); +} + +int bus_unit_method_start_generic( + sd_bus_message *message, + Unit *u, + JobType job_type, + bool reload_if_possible, + sd_bus_error *error) { + + const char *smode; + JobMode mode; + _cleanup_free_ char *verb = NULL; + static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { + [JOB_START] = N_("Authentication is required to start '$(unit)'."), + [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), + [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), + [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), + [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), + }; + int r; + + assert(message); + assert(u); + assert(job_type >= 0 && job_type < _JOB_TYPE_MAX); + + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(job_type), + error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "s", &smode); + if (r < 0) + return r; + + mode = job_mode_from_string(smode); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); + + if (reload_if_possible) + verb = strjoin("reload-or-", job_type_to_string(job_type), NULL); + else + verb = strdup(job_type_to_string(job_type)); + if (!verb) + return -ENOMEM; + + r = bus_verify_manage_units_async_full( + u, + verb, + CAP_SYS_ADMIN, + job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL, + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error); +} + +static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_START, false, error); +} + +static int method_stop(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_STOP, false, error); +} + +static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_RELOAD, false, error); +} + +static int method_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_RESTART, false, error); +} + +static int method_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, false, error); +} + +static int method_reload_or_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_RESTART, true, error); +} + +static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error); +} + +int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Unit *u = userdata; + const char *swho; + int32_t signo; + KillWho who; + int r; + + assert(message); + assert(u); + + r = mac_selinux_unit_access_check(u, message, "stop", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "si", &swho, &signo); + if (r < 0) + return r; + + if (isempty(swho)) + who = KILL_ALL; + else { + who = kill_who_from_string(swho); + if (who < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho); + } + + if (!SIGNAL_VALID(signo)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range."); + + r = bus_verify_manage_units_async_full( + u, + "kill", + CAP_KILL, + N_("Authentication is required to kill '$(unit)'."), + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_kill(u, who, signo, error); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Unit *u = userdata; + int r; + + assert(message); + assert(u); + + r = mac_selinux_unit_access_check(u, message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async_full( + u, + "reset-failed", + CAP_SYS_ADMIN, + N_("Authentication is required to reset the \"failed\" state of '$(unit)'."), + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + unit_reset_failed(u); + + return sd_bus_reply_method_return(message, NULL); +} + +int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Unit *u = userdata; + int runtime, r; + + assert(message); + assert(u); + + r = mac_selinux_unit_access_check(u, message, "start", error); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &runtime); + if (r < 0) + return r; + + r = bus_verify_manage_units_async_full( + u, + "set-property", + CAP_SYS_ADMIN, + N_("Authentication is required to set properties on '$(unit)'."), + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = bus_unit_set_properties(u, message, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, error); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST), + 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("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), 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("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), 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), + SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), + 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), + SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0), + SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0), + BUS_PROPERTY_DUAL_TIMESTAMP("StateChangeTimestamp", offsetof(Unit, state_change_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("InactiveEnterTimestamp", offsetof(Unit, inactive_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanStart", "b", property_get_can_start, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Job", "(uo)", property_get_job, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST), + 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("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), + SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0), + 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("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ + SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END +}; + +static int property_get_slice( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + + assert(bus); + assert(reply); + assert(u); + + return sd_bus_message_append(reply, "s", unit_slice_name(u)); +} + +static int property_get_current_memory( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t sz = (uint64_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_memory_current(u, &sz); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get memory.usage_in_bytes attribute: %m"); + + return sd_bus_message_append(reply, "t", sz); +} + +static int property_get_current_tasks( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t cn = (uint64_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_tasks_current(u, &cn); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m"); + + return sd_bus_message_append(reply, "t", cn); +} + +static int property_get_cpu_usage( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + nsec_t ns = (nsec_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_cpu_usage(u, &ns); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get cpuacct.usage attribute: %m"); + + return sd_bus_message_append(reply, "t", ns); +} + +static int property_get_cgroup( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + const char *t; + + assert(bus); + assert(reply); + assert(u); + + /* Three cases: a) u->cgroup_path is NULL, in which case the + * unit has no control group, which we report as the empty + * string. b) u->cgroup_path is the empty string, which + * indicates the root cgroup, which we report as "/". c) all + * other cases we report as-is. */ + + if (u->cgroup_path) + t = isempty(u->cgroup_path) ? "/" : u->cgroup_path; + else + t = ""; + + return sd_bus_message_append(reply, "s", t); +} + +static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) { + _cleanup_free_ char *buf = NULL, *cmdline = NULL; + int r; + + assert(reply); + assert(pid > 0); + + r = set_put(pids, PID_TO_PTR(pid)); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) + return r; + + if (!p) { + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf); + if (r == -ESRCH) + return 0; + if (r < 0) + return r; + + p = buf; + } + + (void) get_process_cmdline(pid, 0, true, &cmdline); + + return sd_bus_message_append(reply, + "(sus)", + p, + (uint32_t) pid, + cmdline); +} + +static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(reply); + assert(p); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); + if (r == ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + pid_t pid; + + r = cg_read_pid(f, &pid); + if (r < 0) + return r; + if (r == 0) + break; + + if (is_kernel_thread(pid) > 0) + continue; + + r = append_process(reply, p, pid, pids); + if (r < 0) + return r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *g = NULL, *j = NULL; + + r = cg_read_subgroup(d, &g); + if (r < 0) + return r; + if (r == 0) + break; + + j = strjoin(p, "/", g, NULL); + if (!j) + return -ENOMEM; + + r = append_cgroup(reply, j, pids); + if (r < 0) + return r; + } + + return 0; +} + +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(set_freep) Set *pids = NULL; + Unit *u = userdata; + pid_t pid; + int r; + + assert(message); + + pids = set_new(NULL); + if (!pids) + return -ENOMEM; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(sus)"); + if (r < 0) + return r; + + if (u->cgroup_path) { + r = append_cgroup(reply, u->cgroup_path, pids); + if (r < 0) + return r; + } + + /* The main and control pids might live outside of the cgroup, hence fetch them separately */ + pid = unit_main_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + pid = unit_control_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +const sd_bus_vtable bus_unit_cgroup_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), + SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), + SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), + SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), + SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), + SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END +}; + +static int send_new_signal(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + int r; + + assert(bus); + assert(u); + + p = unit_dbus_path(u); + if (!p) + return -ENOMEM; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitNew"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "so", u->id, p); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +static int send_changed_signal(sd_bus *bus, void *userdata) { + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + int r; + + assert(bus); + assert(u); + + p = unit_dbus_path(u); + if (!p) + return -ENOMEM; + + /* Send a properties changed signal. First for the specific + * type, then for the generic unit. The clients may rely on + * this order to get atomic behavior if needed. */ + + r = sd_bus_emit_properties_changed_strv( + bus, p, + unit_dbus_interface_from_type(u->type), + NULL); + if (r < 0) + return r; + + return sd_bus_emit_properties_changed_strv( + bus, p, + "org.freedesktop.systemd1.Unit", + NULL); +} + +void bus_unit_send_change_signal(Unit *u) { + int r; + assert(u); + + if (u->in_dbus_queue) { + LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = false; + } + + if (!u->id) + return; + + r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id); + + u->sent_dbus_new_signal = true; +} + +static int send_removed_signal(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + int r; + + assert(bus); + assert(u); + + p = unit_dbus_path(u); + if (!p) + return -ENOMEM; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "so", u->id, p); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +void bus_unit_send_removed_signal(Unit *u) { + int r; + assert(u); + + if (!u->sent_dbus_new_signal) + bus_unit_send_change_signal(u); + + if (!u->id) + return; + + r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id); +} + +int bus_unit_queue_job( + sd_bus_message *message, + Unit *u, + JobType type, + JobMode mode, + bool reload_if_possible, + sd_bus_error *error) { + + _cleanup_free_ char *path = NULL; + Job *j; + int r; + + assert(message); + assert(u); + assert(type >= 0 && type < _JOB_TYPE_MAX); + assert(mode >= 0 && mode < _JOB_MODE_MAX); + + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(type), + error); + if (r < 0) + return r; + + if (reload_if_possible && unit_can_reload(u)) { + if (type == JOB_RESTART) + type = JOB_RELOAD_OR_START; + else if (type == JOB_TRY_RESTART) + type = JOB_TRY_RELOAD; + } + + if (type == JOB_STOP && + (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && + unit_active_state(u) == UNIT_INACTIVE) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); + + 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_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, error, &j); + if (r < 0) + return r; + + if (sd_bus_message_get_bus(message) == u->manager->api_bus) { + if (!j->clients) { + r = sd_bus_track_new(sd_bus_message_get_bus(message), &j->clients, NULL, NULL); + if (r < 0) + return r; + } + + r = sd_bus_track_add_sender(j->clients, message); + if (r < 0) + return r; + } + + path = job_dbus_path(j); + if (!path) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", path); +} + +static int bus_unit_set_transient_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(u); + assert(name); + assert(message); + + if (streq(name, "Description")) { + const char *d; + + r = sd_bus_message_read(message, "s", &d); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + r = unit_set_description(u, d); + if (r < 0) + return r; + + unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s", d); + } + + return 1; + + } else if (streq(name, "DefaultDependencies")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + u->default_dependencies = b; + unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s", yes_no(b)); + } + + return 1; + + } else if (streq(name, "Slice")) { + Unit *slice; + const char *s; + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups."); + if (u->type == UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units."); + if (unit_has_name(u, SPECIAL_INIT_SCOPE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope"); + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s); + + /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be + * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare() + * instead of manager_load_unit() on purpose, here. */ + r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice); + if (r < 0) + return r; + + if (slice->type != UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s); + + if (mode != UNIT_CHECK) { + r = unit_set_slice(u, slice); + if (r < 0) + return r; + + unit_write_drop_in_private_format(u, mode, name, "Slice=%s", s); + } + + return 1; + + } else if (STR_IN_SET(name, + "Requires", "RequiresOverridable", + "Requisite", "RequisiteOverridable", + "Wants", + "BindsTo", + "Conflicts", + "Before", "After", + "OnFailure", + "PropagatesReloadTo", "ReloadPropagatedFrom", + "PartOf")) { + + UnitDependency d; + const char *other; + + 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) + return r; + + while ((r = sd_bus_message_read(message, "s", &other)) > 0) { + if (!unit_name_is_valid(other, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name %s", other); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *label = NULL; + + r = unit_add_dependency_by_name(u, d, other, NULL, true); + if (r < 0) + return r; + + label = strjoin(name, "-", other, NULL); + if (!label) + return -ENOMEM; + + unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s", name, other); + } + + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + return 1; + } + + return 0; +} + +int bus_unit_set_properties( + Unit *u, + sd_bus_message *message, + UnitSetPropertiesMode mode, + bool commit, + sd_bus_error *error) { + + bool for_real = false; + unsigned n = 0; + int r; + + assert(u); + assert(message); + + /* We iterate through the array twice. First run we just check + * if all passed data is valid, second run actually applies + * it. This is to implement transaction-like behaviour without + * actually providing full transactions. */ + + r = sd_bus_message_enter_container(message, 'a', "(sv)"); + if (r < 0) + return r; + + for (;;) { + const char *name; + + r = sd_bus_message_enter_container(message, 'r', "sv"); + if (r < 0) + return r; + if (r == 0) { + if (for_real || mode == UNIT_CHECK) + break; + + /* Reached EOF. Let's try again, and this time for realz... */ + r = sd_bus_message_rewind(message, false); + if (r < 0) + return r; + + for_real = true; + continue; + } + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!UNIT_VTABLE(u)->bus_set_property) + return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties."); + + r = sd_bus_message_enter_container(message, 'v', NULL); + if (r < 0) + return r; + + r = UNIT_VTABLE(u)->bus_set_property(u, name, message, for_real ? mode : UNIT_CHECK, error); + if (r == 0 && u->transient && u->load_state == UNIT_STUB) + r = bus_unit_set_transient_property(u, name, message, for_real ? mode : UNIT_CHECK, error); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name); + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + n += for_real; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties) + UNIT_VTABLE(u)->bus_commit_properties(u); + + return n; +} + +int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { + assert(u); + + 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 %s is masked.", u->id); + + if (u->load_state == UNIT_NOT_FOUND) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not found.", u->id); + + return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id); +} diff --git a/src/grp-system/libcore/dbus-unit.h b/src/grp-system/libcore/dbus-unit.h new file mode 100644 index 0000000000..758045a47c --- /dev/null +++ b/src/grp-system/libcore/dbus-unit.h @@ -0,0 +1,41 @@ +#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 . +***/ + +#include + +#include "unit.h" + +extern const sd_bus_vtable bus_unit_vtable[]; +extern const sd_bus_vtable bus_unit_cgroup_vtable[]; + +void bus_unit_send_change_signal(Unit *u); +void bus_unit_send_removed_signal(Unit *u); + +int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); +int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error); + +int bus_unit_queue_job(sd_bus_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_method_get_processes(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/grp-system/libcore/dbus.c b/src/grp-system/libcore/dbus.c new file mode 100644 index 0000000000..e8e57e6a89 --- /dev/null +++ b/src/grp-system/libcore/dbus.c @@ -0,0 +1,1244 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/strxcpyx.h" +#include "basic/user-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-internal.h" +#include "shared/bus-util.h" + +#include "dbus-cgroup.h" +#include "dbus-execute.h" +#include "dbus-job.h" +#include "dbus-kill.h" +#include "dbus-manager.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "selinux-access.h" + +#define CONNECTIONS_MAX 4096 + +static void destroy_bus(Manager *m, sd_bus **bus); + +int bus_send_queued_message(Manager *m) { + int r; + + assert(m); + + if (!m->queued_message) + return 0; + + /* If we cannot get rid of this message we won't dispatch any + * D-Bus messages, so that we won't end up wanting to queue + * another message. */ + + r = sd_bus_send(NULL, m->queued_message, NULL); + if (r < 0) + log_warning_errno(r, "Failed to send queued message: %m"); + + m->queued_message = sd_bus_message_unref(m->queued_message); + + return 0; +} + +int bus_forward_agent_released(Manager *m, const char *path) { + int r; + + assert(m); + assert(path); + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (!m->system_bus) + return 0; + + /* If we are running a system instance we forward the agent message on the system bus, so that the user + * instances get notified about this, too */ + + r = sd_bus_emit_signal(m->system_bus, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + "s", path); + if (r < 0) + return log_warning_errno(r, "Failed to propagate agent release message: %m"); + + return 1; +} + +static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Manager *m = userdata; + const char *cgroup; + uid_t sender_uid; + int r; + + assert(message); + assert(m); + + /* only accept org.freedesktop.systemd1.Agent from UID=0 */ + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0 || sender_uid != 0) + return 0; + + /* parse 'cgroup-empty' notification */ + r = sd_bus_message_read(message, "s", &cgroup); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + manager_notify_cgroup_empty(m, cgroup); + return 0; +} + +static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + sd_bus *bus; + + assert(message); + assert(m); + assert_se(bus = sd_bus_message_get_bus(message)); + + if (bus == m->api_bus) + destroy_bus(m, &m->api_bus); + if (bus == m->system_bus) + destroy_bus(m, &m->system_bus); + if (set_remove(m->private_buses, bus)) { + log_debug("Got disconnect on private connection."); + destroy_bus(m, &bus); + } + + return 0; +} + +static int signal_activation_request(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + _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; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) || + manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) { + r = sd_bus_error_setf(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down."); + goto failed; + } + + r = manager_load_unit(m, name, NULL, &error, &u); + if (r < 0) + goto failed; + + if (u->refuse_manual_start) { + r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only.", u->id); + goto failed; + } + + r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); + if (r < 0) + goto failed; + + /* Successfully queued, that's it for us */ + return 0; + +failed: + if (!sd_bus_error_is_set(&error)) + sd_bus_error_set_errno(&error, r); + + log_debug("D-Bus activation failed for %s: %s", name, bus_error_message(&error, r)); + + r = sd_bus_message_new_signal(sd_bus_message_get_bus(message), &reply, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure"); + if (r < 0) { + bus_log_create_error(r); + return 0; + } + + r = sd_bus_message_append(reply, "sss", name, error.name, error.message); + if (r < 0) { + bus_log_create_error(r); + return 0; + } + + r = sd_bus_send_to(NULL, reply, "org.freedesktop.DBus", NULL); + if (r < 0) + return log_error_errno(r, "Failed to respond with to bus activation request: %m"); + + return 0; +} + +#ifdef HAVE_SELINUX +static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *verb, *path; + Unit *u = NULL; + Job *j; + int r; + + assert(message); + + /* Our own method calls are all protected individually with + * selinux checks, but the built-in interfaces need to be + * protected too. */ + + if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set")) + verb = "reload"; + else if (sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", NULL) || + sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Properties", NULL) || + sd_bus_message_is_method_call(message, "org.freedesktop.DBus.ObjectManager", NULL) || + sd_bus_message_is_method_call(message, "org.freedesktop.DBus.Peer", NULL)) + verb = "status"; + else + return 0; + + path = sd_bus_message_get_path(message); + + if (object_path_startswith("/org/freedesktop/systemd1", path)) { + + r = mac_selinux_access_check(message, verb, error); + if (r < 0) + return r; + + return 0; + } + + if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) { + _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); + if (r < 0) + return 0; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return 0; + + u = manager_get_unit_by_pid(m, pid); + } else { + r = manager_get_job_from_dbus_path(m, path, &j); + if (r >= 0) + u = j->unit; + else + manager_load_unit_from_dbus_path(m, path, NULL, &u); + } + + if (!u) + return 0; + + r = mac_selinux_unit_access_check(u, message, verb, error); + if (r < 0) + return r; + + return 0; +} +#endif + +static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Job *j; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = manager_get_job_from_dbus_path(m, path, &j); + if (r < 0) + return 0; + + *found = j; + return 1; +} + +static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) { + Unit *u; + int r; + + assert(m); + assert(bus); + assert(path); + + if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + sd_bus_message *message; + pid_t pid; + + message = sd_bus_get_current_message(bus); + if (!message) + return 0; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_pid(creds, &pid); + if (r < 0) + return r; + + u = manager_get_unit_by_pid(m, pid); + } else { + r = manager_load_unit_from_dbus_path(m, path, error, &u); + if (r < 0) + return 0; + } + + if (!u) + return 0; + + *unit = u; + return 1; +} + +static int bus_unit_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + return find_unit(m, bus, path, (Unit**) found, error); +} + +static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Unit *u; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = find_unit(m, bus, path, &u, error); + if (r <= 0) + return r; + + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) + return 0; + + *found = u; + return 1; +} + +static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + Unit *u; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = find_unit(m, bus, path, &u, error); + if (r <= 0) + return r; + + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) + return 0; + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return 0; + + *found = u; + return 1; +} + +static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + CGroupContext *c; + Unit *u; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = find_unit(m, bus, path, &u, error); + if (r <= 0) + return r; + + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) + return 0; + + c = unit_get_cgroup_context(u); + if (!c) + return 0; + + *found = c; + return 1; +} + +static int bus_exec_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + ExecContext *c; + Unit *u; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = find_unit(m, bus, path, &u, error); + if (r <= 0) + return r; + + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) + return 0; + + c = unit_get_exec_context(u); + if (!c) + return 0; + + *found = c; + return 1; +} + +static int bus_kill_context_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { + Manager *m = userdata; + KillContext *c; + Unit *u; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(found); + assert(m); + + r = find_unit(m, bus, path, &u, error); + if (r <= 0) + return r; + + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) + return 0; + + c = unit_get_kill_context(u); + if (!c) + return 0; + + *found = c; + return 1; +} + +static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_free_ char **l = NULL; + Manager *m = userdata; + unsigned k = 0; + Iterator i; + Job *j; + + l = new0(char*, hashmap_size(m->jobs)+1); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(j, m->jobs, i) { + l[k] = job_dbus_path(j); + if (!l[k]) + return -ENOMEM; + + k++; + } + + assert(hashmap_size(m->jobs) == k); + + *nodes = l; + l = NULL; + + return k; +} + +static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + _cleanup_free_ char **l = NULL; + Manager *m = userdata; + unsigned k = 0; + Iterator i; + Unit *u; + + l = new0(char*, hashmap_size(m->units)+1); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(u, m->units, i) { + l[k] = unit_dbus_path(u); + if (!l[k]) + return -ENOMEM; + + k++; + } + + *nodes = l; + l = NULL; + + return k; +} + +static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { + UnitType t; + int r; + + assert(m); + assert(bus); + +#ifdef HAVE_SELINUX + r = sd_bus_add_filter(bus, NULL, mac_selinux_filter, m); + if (r < 0) + return log_error_errno(r, "Failed to add SELinux access filter: %m"); +#endif + + r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m); + if (r < 0) + return log_error_errno(r, "Failed to register Manager vtable: %m"); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/job", "org.freedesktop.systemd1.Job", bus_job_vtable, bus_job_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register Job vtable: %m"); + + r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/job", bus_job_enumerate, m); + if (r < 0) + return log_error_errno(r, "Failed to add job enumerator: %m"); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", "org.freedesktop.systemd1.Unit", bus_unit_vtable, bus_unit_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register Unit vtable: %m"); + + r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/unit", bus_unit_enumerate, m); + if (r < 0) + return log_error_errno(r, "Failed to add job enumerator: %m"); + + for (t = 0; t < _UNIT_TYPE_MAX; t++) { + const char *interface; + + assert_se(interface = unit_dbus_interface_from_type(t)); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); + + if (unit_vtable[t]->cgroup_context_offset > 0) { + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); + } + + if (unit_vtable[t]->exec_context_offset > 0) { + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); + } + + if (unit_vtable[t]->kill_context_offset > 0) { + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); + if (r < 0) + return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); + } + } + + return 0; +} + +static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { + int r; + + assert(m); + assert(bus); + + r = sd_bus_add_match( + bus, + NULL, + "sender='org.freedesktop.DBus.Local'," + "type='signal'," + "path='/org/freedesktop/DBus/Local'," + "interface='org.freedesktop.DBus.Local'," + "member='Disconnected'", + signal_disconnected, m); + + if (r < 0) + return log_error_errno(r, "Failed to register match for Disconnected message: %m"); + + return 0; +} + +static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + _cleanup_close_ int nfd = -1; + Manager *m = userdata; + sd_id128_t id; + int r; + + assert(s); + assert(m); + + nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (nfd < 0) { + log_warning_errno(errno, "Failed to accept private connection, ignoring: %m"); + return 0; + } + + if (set_size(m->private_buses) >= CONNECTIONS_MAX) { + log_warning("Too many concurrent connections, refusing"); + return 0; + } + + r = set_ensure_allocated(&m->private_buses, NULL); + if (r < 0) { + log_oom(); + return 0; + } + + r = sd_bus_new(&bus); + if (r < 0) { + log_warning_errno(r, "Failed to allocate new private connection bus: %m"); + return 0; + } + + r = sd_bus_set_fd(bus, nfd, nfd); + if (r < 0) { + log_warning_errno(r, "Failed to set fd on new connection bus: %m"); + return 0; + } + + nfd = -1; + + r = bus_check_peercred(bus); + if (r < 0) { + log_warning_errno(r, "Incoming private connection from unprivileged client, refusing: %m"); + return 0; + } + + assert_se(sd_id128_randomize(&id) >= 0); + + r = sd_bus_set_server(bus, 1, id); + if (r < 0) { + log_warning_errno(r, "Failed to enable server support for new connection bus: %m"); + return 0; + } + + r = sd_bus_negotiate_creds(bus, 1, + SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| + SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT); + if (r < 0) { + log_warning_errno(r, "Failed to enable credentials for new connection: %m"); + return 0; + } + + r = sd_bus_start(bus); + if (r < 0) { + log_warning_errno(r, "Failed to start new connection bus: %m"); + return 0; + } + + r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) { + log_warning_errno(r, "Failed to attach new connection bus to event loop: %m"); + return 0; + } + + r = bus_setup_disconnected_match(m, bus); + if (r < 0) + return 0; + + r = bus_setup_api_vtables(m, bus); + if (r < 0) { + log_warning_errno(r, "Failed to set up API vtables on new connection bus: %m"); + return 0; + } + + r = set_put(m->private_buses, bus); + if (r < 0) { + log_warning_errno(r, "Failed to add new connection bus to set: %m"); + return 0; + } + + bus = NULL; + + log_debug("Accepted new private connection."); + + return 0; +} + +int manager_sync_bus_names(Manager *m, sd_bus *bus) { + _cleanup_strv_free_ char **names = NULL; + const char *name; + Iterator i; + Unit *u; + int r; + + assert(m); + assert(bus); + + r = sd_bus_list_names(bus, &names, NULL); + if (r < 0) + return log_error_errno(r, "Failed to get initial list of names: %m"); + + /* 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); + + 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; +} + +static int bus_setup_api(Manager *m, sd_bus *bus) { + Iterator i; + char *name; + Unit *u; + int r; + + assert(m); + assert(bus); + + /* Let's make sure we have enough credential bits so that we can make security and selinux decisions */ + r = sd_bus_negotiate_creds(bus, 1, + SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| + SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT); + if (r < 0) + log_warning_errno(r, "Failed to enable credential passing, ignoring: %m"); + + r = bus_setup_api_vtables(m, bus); + if (r < 0) + return r; + + HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) { + r = unit_install_bus_match(u, bus, name); + if (r < 0) + log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); + } + + r = sd_bus_add_match( + bus, + NULL, + "type='signal'," + "sender='org.freedesktop.DBus'," + "path='/org/freedesktop/DBus'," + "interface='org.freedesktop.systemd1.Activator'," + "member='ActivationRequest'", + signal_activation_request, m); + if (r < 0) + log_warning_errno(r, "Failed to subscribe to activation signal: %m"); + + /* Allow replacing of our name, to ease implementation of + * reexecution, where we keep the old connection open until + * after the new connection is set up and the name installed + * to allow clients to synchronously wait for reexecution to + * finish */ + r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + 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_(sd_bus_unrefp) sd_bus *bus = NULL; + int r; + + if (m->api_bus) + return 0; + + /* The API and system bus is the same if we are running in system mode */ + if (MANAGER_IS_SYSTEM(m) && m->system_bus) + bus = sd_bus_ref(m->system_bus); + else { + if (MANAGER_IS_SYSTEM(m)) + r = sd_bus_open_system(&bus); + else + r = sd_bus_open_user(&bus); + + if (r < 0) { + log_debug("Failed to connect to API bus, retrying later..."); + return 0; + } + + r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) { + log_error_errno(r, "Failed to attach API bus to event loop: %m"); + return 0; + } + + r = bus_setup_disconnected_match(m, bus); + if (r < 0) + return 0; + } + + r = bus_setup_api(m, bus); + if (r < 0) { + log_error_errno(r, "Failed to set up API bus: %m"); + return 0; + } + + m->api_bus = bus; + bus = NULL; + + return 0; +} + +static int bus_setup_system(Manager *m, sd_bus *bus) { + int r; + + assert(m); + assert(bus); + + /* if we are a user instance we get the Released message via the system bus */ + if (MANAGER_IS_USER(m)) { + r = sd_bus_add_match( + bus, + NULL, + "type='signal'," + "interface='org.freedesktop.systemd1.Agent'," + "member='Released'," + "path='/org/freedesktop/systemd1/agent'", + signal_agent_released, m); + if (r < 0) + log_warning_errno(r, "Failed to register Released match on system bus: %m"); + } + + log_debug("Successfully connected to system bus."); + return 0; +} + +static int bus_init_system(Manager *m) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + int r; + + if (m->system_bus) + return 0; + + /* The API and system bus is the same if we are running in system mode */ + if (MANAGER_IS_SYSTEM(m) && m->api_bus) { + m->system_bus = sd_bus_ref(m->api_bus); + return 0; + } + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_debug("Failed to connect to system bus, retrying later..."); + return 0; + } + + r = bus_setup_disconnected_match(m, bus); + if (r < 0) + return 0; + + r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) { + log_error_errno(r, "Failed to attach system bus to event loop: %m"); + return 0; + } + + r = bus_setup_system(m, bus); + if (r < 0) { + log_error_errno(r, "Failed to set up system bus: %m"); + return 0; + } + + m->system_bus = bus; + bus = NULL; + + return 0; +} + +static int bus_init_private(Manager *m) { + _cleanup_close_ int fd = -1; + union sockaddr_union sa = { + .un.sun_family = AF_UNIX + }; + sd_event_source *s; + socklen_t salen; + int r; + + assert(m); + + if (m->private_listen_fd >= 0) + return 0; + + /* We don't need the private socket if we have kdbus */ + if (m->kdbus_fd >= 0) + return 0; + + if (MANAGER_IS_SYSTEM(m)) { + + /* We want the private bus only when running as init */ + if (getpid() != 1) + return 0; + + strcpy(sa.un.sun_path, "/run/systemd/private"); + salen = SOCKADDR_UN_LEN(sa.un); + } else { + size_t left = sizeof(sa.un.sun_path); + char *p = sa.un.sun_path; + const char *e; + + e = secure_getenv("XDG_RUNTIME_DIR"); + if (!e) { + log_error("Failed to determine XDG_RUNTIME_DIR"); + return -EHOSTDOWN; + } + + left = strpcpy(&p, left, e); + left = strpcpy(&p, left, "/systemd/private"); + + salen = sizeof(sa.un) - left; + } + + (void) mkdir_parents_label(sa.un.sun_path, 0755); + (void) unlink(sa.un.sun_path); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate private socket: %m"); + + r = bind(fd, &sa.sa, salen); + if (r < 0) + return log_error_errno(errno, "Failed to bind private socket: %m"); + + r = listen(fd, SOMAXCONN); + if (r < 0) + return log_error_errno(errno, "Failed to make private socket listening: %m"); + + r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate event source: %m"); + + (void) sd_event_source_set_description(s, "bus-connection"); + + m->private_listen_fd = fd; + m->private_listen_event_source = s; + fd = -1; + + log_debug("Successfully created private D-Bus server."); + + return 0; +} + +int bus_init(Manager *m, bool try_bus_connect) { + int r; + + if (try_bus_connect) { + r = bus_init_system(m); + if (r < 0) + return r; + + r = bus_init_api(m); + if (r < 0) + return r; + } + + r = bus_init_private(m); + if (r < 0) + return r; + + return 0; +} + +static void destroy_bus(Manager *m, sd_bus **bus) { + Iterator i; + Job *j; + + assert(m); + assert(bus); + + if (!*bus) + return; + + /* Get rid of tracked clients on this bus */ + if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus) + m->subscribed = sd_bus_track_unref(m->subscribed); + + HASHMAP_FOREACH(j, m->jobs, i) + if (j->clients && sd_bus_track_get_bus(j->clients) == *bus) + j->clients = sd_bus_track_unref(j->clients); + + /* Get rid of queued message on this bus */ + if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus) + m->queued_message = sd_bus_message_unref(m->queued_message); + + /* Possibly flush unwritten data, but only if we are + * unprivileged, since we don't want to sync here */ + if (!MANAGER_IS_SYSTEM(m)) + sd_bus_flush(*bus); + + /* And destroy the object */ + sd_bus_close(*bus); + *bus = sd_bus_unref(*bus); +} + +void bus_done(Manager *m) { + sd_bus *b; + + assert(m); + + if (m->api_bus) + destroy_bus(m, &m->api_bus); + if (m->system_bus) + destroy_bus(m, &m->system_bus); + while ((b = set_steal_first(m->private_buses))) + destroy_bus(m, &b); + + m->private_buses = set_free(m->private_buses); + + m->subscribed = sd_bus_track_unref(m->subscribed); + m->deserialized_subscribed = strv_free(m->deserialized_subscribed); + + if (m->private_listen_event_source) + m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source); + + m->private_listen_fd = safe_close(m->private_listen_fd); + + bus_verify_polkit_async_registry_free(m->polkit_registry); +} + +int bus_fdset_add_all(Manager *m, FDSet *fds) { + Iterator i; + sd_bus *b; + int fd; + + assert(m); + assert(fds); + + /* When we are about to reexecute we add all D-Bus fds to the + * set to pass over to the newly executed systemd. They won't + * be used there however, except thatt they are closed at the + * very end of deserialization, those making it possible for + * clients to synchronously wait for systemd to reexec by + * simply waiting for disconnection */ + + if (m->api_bus) { + fd = sd_bus_get_fd(m->api_bus); + if (fd >= 0) { + fd = fdset_put_dup(fds, fd); + if (fd < 0) + return fd; + } + } + + SET_FOREACH(b, m->private_buses, i) { + fd = sd_bus_get_fd(b); + if (fd >= 0) { + fd = fdset_put_dup(fds, fd); + if (fd < 0) + return fd; + } + } + + /* We don't offer any APIs on the system bus (well, unless it + * is the same as the API bus) hence we don't bother with it + * here */ + + return 0; +} + +int bus_foreach_bus( + Manager *m, + sd_bus_track *subscribed2, + int (*send_message)(sd_bus *bus, void *userdata), + void *userdata) { + + Iterator i; + sd_bus *b; + int r, ret = 0; + + /* Send to all direct buses, unconditionally */ + SET_FOREACH(b, m->private_buses, i) { + r = send_message(b, userdata); + if (r < 0) + ret = r; + } + + /* Send to API bus, but only if somebody is subscribed */ + if (sd_bus_track_count(m->subscribed) > 0 || + sd_bus_track_count(subscribed2) > 0) { + r = send_message(m->api_bus, userdata); + if (r < 0) + ret = r; + } + + return ret; +} + +void bus_track_serialize(sd_bus_track *t, FILE *f) { + const char *n; + + assert(f); + + for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) + fprintf(f, "subscribed=%s\n", n); +} + +int bus_track_deserialize_item(char ***l, const char *line) { + const char *e; + int r; + + assert(l); + assert(line); + + e = startswith(line, "subscribed="); + if (!e) + return 0; + + r = strv_extend(l, e); + if (r < 0) + return r; + + return 1; +} + +int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { + int r = 0; + + assert(m); + assert(t); + assert(l); + + if (!strv_isempty(*l) && m->api_bus) { + char **i; + + if (!*t) { + r = sd_bus_track_new(m->api_bus, t, NULL, NULL); + if (r < 0) + return r; + } + + r = 0; + STRV_FOREACH(i, *l) { + int k; + + k = sd_bus_track_add_name(*t, *i); + if (k < 0) + r = k; + } + } + + *l = strv_free(*l); + + return r; +} + +int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error); +} + +int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error); +} + +int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error); +} + +int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error); +} diff --git a/src/grp-system/libcore/dbus.h b/src/grp-system/libcore/dbus.h new file mode 100644 index 0000000000..6baaffbd75 --- /dev/null +++ b/src/grp-system/libcore/dbus.h @@ -0,0 +1,44 @@ +#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 . +***/ + +#include "manager.h" + +int bus_send_queued_message(Manager *m); + +int bus_init(Manager *m, bool try_bus_connect); +void bus_done(Manager *m); + +int bus_fdset_add_all(Manager *m, FDSet *fds); + +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); +int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error); +int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error); +int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error); + +int bus_forward_agent_released(Manager *m, const char *path); diff --git a/src/grp-system/libcore/device.c b/src/grp-system/libcore/device.c new file mode 100644 index 0000000000..11ee2ad047 --- /dev/null +++ b/src/grp-system/libcore/device.c @@ -0,0 +1,877 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "shared/udev-util.h" + +#include "dbus-device.h" +#include "device.h" +#include "swap.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = { + [DEVICE_DEAD] = UNIT_INACTIVE, + [DEVICE_TENTATIVE] = UNIT_ACTIVATING, + [DEVICE_PLUGGED] = UNIT_ACTIVE, +}; + +static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + +static void device_unset_sysfs(Device *d) { + Hashmap *devices; + Device *first; + + assert(d); + + if (!d->sysfs) + return; + + /* Remove this unit from the chain of devices which share the + * same sysfs path. */ + devices = UNIT(d)->manager->devices_by_sysfs; + first = hashmap_get(devices, d->sysfs); + LIST_REMOVE(same_sysfs, first, d); + + if (first) + hashmap_remove_and_replace(devices, d->sysfs, first->sysfs, first); + else + hashmap_remove(devices, d->sysfs); + + d->sysfs = mfree(d->sysfs); +} + +static int device_set_sysfs(Device *d, const char *sysfs) { + Device *first; + char *copy; + int r; + + assert(d); + + if (streq_ptr(d->sysfs, sysfs)) + return 0; + + r = hashmap_ensure_allocated(&UNIT(d)->manager->devices_by_sysfs, &string_hash_ops); + if (r < 0) + return r; + + copy = strdup(sysfs); + if (!copy) + return -ENOMEM; + + device_unset_sysfs(d); + + first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, sysfs); + LIST_PREPEND(same_sysfs, first, d); + + r = hashmap_replace(UNIT(d)->manager->devices_by_sysfs, copy, first); + if (r < 0) { + LIST_REMOVE(same_sysfs, first, d); + free(copy); + return r; + } + + d->sysfs = copy; + + return 0; +} + +static void device_init(Unit *u) { + Device *d = DEVICE(u); + + assert(d); + assert(UNIT(d)->load_state == UNIT_STUB); + + /* In contrast to all other unit types we timeout jobs waiting + * for devices by default. This is because they otherwise wait + * indefinitely for plugged in devices, something which cannot + * happen for the other units since their operations time out + * anyway. */ + u->job_timeout = u->manager->default_timeout_start_usec; + + u->ignore_on_isolate = true; +} + +static void device_done(Unit *u) { + Device *d = DEVICE(u); + + assert(d); + + device_unset_sysfs(d); +} + +static void device_set_state(Device *d, DeviceState state) { + DeviceState old_state; + assert(d); + + old_state = d->state; + d->state = state; + + if (state != old_state) + log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state)); + + unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true); +} + +static int device_coldplug(Unit *u) { + Device *d = DEVICE(u); + + assert(d); + assert(d->state == DEVICE_DEAD); + + if (d->found & DEVICE_FOUND_UDEV) + /* If udev says the device is around, it's around */ + device_set_state(d, DEVICE_PLUGGED); + else if (d->found != DEVICE_NOT_FOUND && d->deserialized_state != DEVICE_PLUGGED) + /* If a device is found in /proc/self/mountinfo or + * /proc/swaps, and was not yet announced via udev, + * it's "tentatively" around. */ + device_set_state(d, DEVICE_TENTATIVE); + + return 0; +} + +static int device_serialize(Unit *u, FILE *f, FDSet *fds) { + Device *d = DEVICE(u); + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", device_state_to_string(d->state)); + + return 0; +} + +static int device_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Device *d = DEVICE(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + DeviceState state; + + state = device_state_from_string(value); + if (state < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + d->deserialized_state = state; + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +static void device_dump(Unit *u, FILE *f, const char *prefix) { + Device *d = DEVICE(u); + + assert(d); + + fprintf(f, + "%sDevice State: %s\n" + "%sSysfs Path: %s\n", + prefix, device_state_to_string(d->state), + prefix, strna(d->sysfs)); +} + +_pure_ static UnitActiveState device_active_state(Unit *u) { + assert(u); + + return state_translation_table[DEVICE(u)->state]; +} + +_pure_ static const char *device_sub_state_to_string(Unit *u) { + assert(u); + + return device_state_to_string(DEVICE(u)->state); +} + +static int device_update_description(Unit *u, struct udev_device *dev, const char *path) { + const char *model; + int r; + + assert(u); + assert(dev); + assert(path); + + model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); + if (!model) + model = udev_device_get_property_value(dev, "ID_MODEL"); + + if (model) { + const char *label; + + /* Try to concatenate the device model string with a label, if there is one */ + label = udev_device_get_property_value(dev, "ID_FS_LABEL"); + if (!label) + label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"); + if (!label) + label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER"); + + if (label) { + _cleanup_free_ char *j; + + j = strjoin(model, " ", label, NULL); + if (j) + r = unit_set_description(u, j); + else + r = -ENOMEM; + } else + r = unit_set_description(u, model); + } else + r = unit_set_description(u, path); + + if (r < 0) + log_unit_error_errno(u, r, "Failed to set device description: %m"); + + return r; +} + +static int device_add_udev_wants(Unit *u, struct udev_device *dev) { + const char *wants; + const char *word, *state; + size_t l; + int r; + const char *property; + + assert(u); + assert(dev); + + property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; + wants = udev_device_get_property_value(dev, property); + if (!wants) + return 0; + + FOREACH_WORD_QUOTED(word, l, wants, state) { + _cleanup_free_ char *n = NULL; + char e[l+1]; + + memcpy(e, word, l); + e[l] = 0; + + r = unit_name_mangle(e, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to mangle unit name: %m"); + + r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); + } + if (!isempty(state)) + log_unit_warning(u, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev))); + + return 0; +} + +static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) { + _cleanup_free_ char *e = NULL; + const char *sysfs = NULL; + Unit *u = NULL; + bool delete; + int r; + + assert(m); + assert(path); + + if (dev) { + sysfs = udev_device_get_syspath(dev); + if (!sysfs) + return 0; + } + + r = unit_name_from_path(path, ".device", &e); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name from device path: %m"); + + u = manager_get_unit(m, e); + + /* The device unit can still be present even if the device was + * unplugged: a mount unit can reference it hence preventing + * the GC to have garbaged it. That's desired since the device + * unit may have a dependency on the mount unit which was + * added during the loading of the later. */ + if (dev && u && DEVICE(u)->state == DEVICE_PLUGGED) { + /* This unit is in plugged state: we're sure it's + * attached to a device. */ + if (!path_equal(DEVICE(u)->sysfs, sysfs)) { + log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", + e, DEVICE(u)->sysfs, sysfs); + return -EEXIST; + } + } + + if (!u) { + delete = true; + + u = unit_new(m, sizeof(Device)); + if (!u) + return log_oom(); + + r = unit_add_name(u, e); + if (r < 0) + goto fail; + + unit_add_to_load_queue(u); + } else + delete = false; + + /* If this was created via some dependency and has not + * actually been seen yet ->sysfs will not be + * initialized. Hence initialize it if necessary. */ + if (sysfs) { + r = device_set_sysfs(DEVICE(u), sysfs); + if (r < 0) + goto fail; + + (void) device_update_description(u, dev, path); + + /* The additional systemd udev properties we only interpret + * for the main object */ + if (main) + (void) device_add_udev_wants(u, dev); + } + + + /* Note that this won't dispatch the load queue, the caller + * has to do that if needed and appropriate */ + + unit_add_to_dbus_queue(u); + return 0; + +fail: + log_unit_warning_errno(u, r, "Failed to set up device unit: %m"); + + if (delete) + unit_free(u); + + return r; +} + +static int device_process_new(Manager *m, struct udev_device *dev) { + const char *sysfs, *dn, *alias; + struct udev_list_entry *item = NULL, *first = NULL; + int r; + + assert(m); + + sysfs = udev_device_get_syspath(dev); + if (!sysfs) + return 0; + + /* Add the main unit named after the sysfs path */ + r = device_setup_unit(m, dev, sysfs, true); + if (r < 0) + return r; + + /* Add an additional unit for the device node */ + dn = udev_device_get_devnode(dev); + if (dn) + (void) device_setup_unit(m, dev, dn, false); + + /* Add additional units for all symlinks */ + first = udev_device_get_devlinks_list_entry(dev); + udev_list_entry_foreach(item, first) { + const char *p; + struct stat st; + + /* Don't bother with the /dev/block links */ + p = udev_list_entry_get_name(item); + + if (path_startswith(p, "/dev/block/") || + path_startswith(p, "/dev/char/")) + continue; + + /* Verify that the symlink in the FS actually belongs + * to this device. This is useful to deal with + * conflicting devices, e.g. when two disks want the + * same /dev/disk/by-label/xxx link because they have + * the same label. We want to make sure that the same + * device that won the symlink wins in systemd, so we + * check the device node major/minor */ + if (stat(p, &st) >= 0) + if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) || + st.st_rdev != udev_device_get_devnum(dev)) + continue; + + (void) device_setup_unit(m, dev, p, false); + } + + /* Add additional units for all explicitly configured + * aliases */ + alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"); + if (alias) { + const char *word, *state; + size_t l; + + FOREACH_WORD_QUOTED(word, l, alias, state) { + char e[l+1]; + + memcpy(e, word, l); + e[l] = 0; + + if (path_is_absolute(e)) + (void) device_setup_unit(m, dev, e, false); + else + log_warning("SYSTEMD_ALIAS for %s is not an absolute path, ignoring: %s", sysfs, e); + } + if (!isempty(state)) + log_warning("SYSTEMD_ALIAS for %s has trailing garbage, ignoring.", sysfs); + } + + return 0; +} + +static void device_update_found_one(Device *d, bool add, DeviceFound found, bool now) { + DeviceFound n, previous; + + assert(d); + + n = add ? (d->found | found) : (d->found & ~found); + if (n == d->found) + return; + + previous = d->found; + d->found = n; + + if (!now) + return; + + if (d->found & DEVICE_FOUND_UDEV) + /* When the device is known to udev we consider it + * plugged. */ + device_set_state(d, DEVICE_PLUGGED); + else if (d->found != DEVICE_NOT_FOUND && (previous & DEVICE_FOUND_UDEV) == 0) + /* If the device has not been seen by udev yet, but is + * now referenced by the kernel, then we assume the + * kernel knows it now, and udev might soon too. */ + device_set_state(d, DEVICE_TENTATIVE); + else + /* If nobody sees the device, or if the device was + * previously seen by udev and now is only referenced + * from the kernel, then we consider the device is + * gone, the kernel just hasn't noticed it yet. */ + device_set_state(d, DEVICE_DEAD); +} + +static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) { + Device *d, *l; + + assert(m); + assert(sysfs); + + if (found == DEVICE_NOT_FOUND) + return 0; + + l = hashmap_get(m->devices_by_sysfs, sysfs); + LIST_FOREACH(same_sysfs, d, l) + device_update_found_one(d, add, found, now); + + return 0; +} + +static int device_update_found_by_name(Manager *m, const char *path, bool add, DeviceFound found, bool now) { + _cleanup_free_ char *e = NULL; + Unit *u; + int r; + + assert(m); + assert(path); + + if (found == DEVICE_NOT_FOUND) + return 0; + + r = unit_name_from_path(path, ".device", &e); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name from device path: %m"); + + u = manager_get_unit(m, e); + if (!u) + return 0; + + device_update_found_one(DEVICE(u), add, found, now); + return 0; +} + +static bool device_is_ready(struct udev_device *dev) { + const char *ready; + + assert(dev); + + ready = udev_device_get_property_value(dev, "SYSTEMD_READY"); + if (!ready) + return true; + + return parse_boolean(ready) != 0; +} + +static Unit *device_following(Unit *u) { + Device *d = DEVICE(u); + Device *other, *first = NULL; + + assert(d); + + if (startswith(u->id, "sys-")) + return NULL; + + /* Make everybody follow the unit that's named after the sysfs path */ + for (other = d->same_sysfs_next; other; other = other->same_sysfs_next) + if (startswith(UNIT(other)->id, "sys-")) + return UNIT(other); + + for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) { + if (startswith(UNIT(other)->id, "sys-")) + return UNIT(other); + + first = other; + } + + return UNIT(first); +} + +static int device_following_set(Unit *u, Set **_set) { + Device *d = DEVICE(u), *other; + Set *set; + int r; + + assert(d); + assert(_set); + + if (LIST_JUST_US(same_sysfs, d)) { + *_set = NULL; + return 0; + } + + set = set_new(NULL); + if (!set) + return -ENOMEM; + + LIST_FOREACH_AFTER(same_sysfs, other, d) { + r = set_put(set, other); + if (r < 0) + goto fail; + } + + LIST_FOREACH_BEFORE(same_sysfs, other, d) { + r = set_put(set, other); + if (r < 0) + goto fail; + } + + *_set = set; + return 1; + +fail: + set_free(set); + return r; +} + +static void device_shutdown(Manager *m) { + assert(m); + + m->udev_event_source = sd_event_source_unref(m->udev_event_source); + + if (m->udev_monitor) { + udev_monitor_unref(m->udev_monitor); + m->udev_monitor = NULL; + } + + m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); +} + +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; + + assert(m); + + if (!m->udev_monitor) { + m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_monitor) { + log_oom(); + goto fail; + } + + /* This will fail if we are unprivileged, but that + * should not matter much, as user instances won't run + * during boot. */ + (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) { + log_error_errno(r, "Failed to add udev tag match: %m"); + goto fail; + } + + r = udev_monitor_enable_receiving(m->udev_monitor); + 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) { + 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) { + log_oom(); + goto fail; + } + + r = udev_enumerate_add_match_tag(e, "systemd"); + 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) { + log_error_errno(r, "Failed to install initialization match into enumeration: %m"); + goto fail; + } + + r = udev_enumerate_scan_devices(e); + 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) { + _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + const char *sysfs; + + sysfs = udev_list_entry_get_name(item); + + dev = udev_device_new_from_syspath(m->udev, sysfs); + if (!dev) { + log_oom(); + continue; + } + + if (!device_is_ready(dev)) + continue; + + (void) device_process_new(m, dev); + + device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); + } + + return; + +fail: + device_shutdown(m); +} + +static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + Manager *m = userdata; + const char *action, *sysfs; + int r; + + assert(m); + + if (revents != EPOLLIN) { + static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); + + if (!ratelimit_test(&limit)) + log_error_errno(errno, "Failed to get udev event: %m"); + if (!(revents & EPOLLIN)) + return 0; + } + + /* + * libudev might filter-out devices which pass the bloom + * filter, so getting NULL here is not necessarily an error. + */ + dev = udev_monitor_receive_device(m->udev_monitor); + if (!dev) + return 0; + + sysfs = udev_device_get_syspath(dev); + if (!sysfs) { + log_error("Failed to get udev sys path."); + return 0; + } + + action = udev_device_get_action(dev); + if (!action) { + log_error("Failed to get udev action string."); + return 0; + } + + if (streq(action, "remove")) { + r = swap_process_device_remove(m, dev); + if (r < 0) + log_error_errno(r, "Failed to process swap device remove event: %m"); + + /* If we get notified that a device was removed by + * udev, then it's completely gone, hence unset all + * found bits */ + device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV|DEVICE_FOUND_MOUNT|DEVICE_FOUND_SWAP, true); + + } else if (device_is_ready(dev)) { + + (void) device_process_new(m, dev); + + r = swap_process_device_new(m, dev); + if (r < 0) + log_error_errno(r, "Failed to process swap device new event: %m"); + + manager_dispatch_load_queue(m); + + /* The device is found now, set the udev found bit */ + device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, true); + + } else { + /* The device is nominally around, but not ready for + * us. Hence unset the udev bit, but leave the rest + * around. */ + + device_update_found_by_sysfs(m, sysfs, false, DEVICE_FOUND_UDEV, true); + } + + return 0; +} + +static bool device_supported(void) { + static int read_only = -1; + + /* If /sys is read-only we don't support device units, and any + * attempts to start one should fail immediately. */ + + if (read_only < 0) + read_only = path_is_read_only_fs("/sys"); + + return read_only <= 0; +} + +int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now) { + _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + struct stat st; + + assert(m); + assert(node); + + if (!device_supported()) + return 0; + + /* This is called whenever we find a device referenced in + * /proc/swaps or /proc/self/mounts. Such a device might be + * mounted/enabled at a time where udev has not finished + * probing it yet, and we thus haven't learned about it + * yet. In this case we will set the device unit to + * "tentative" state. */ + + if (add) { + if (!path_startswith(node, "/dev")) + return 0; + + /* We make an extra check here, if the device node + * actually exists. If it's missing, then this is an + * indication that device was unplugged but is still + * referenced in /proc/swaps or + * /proc/self/mountinfo. Note that this check doesn't + * really cover all cases where a device might be gone + * away, since drives that can have a medium inserted + * will still have a device node even when the medium + * is not there... */ + + if (stat(node, &st) >= 0) { + if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) + return 0; + + dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev); + if (!dev && errno != ENOENT) + return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); + + } else if (errno != ENOENT) + return log_error_errno(errno, "Failed to stat device node file %s: %m", node); + + /* If the device is known in the kernel and newly + * appeared, then we'll create a device unit for it, + * under the name referenced in /proc/swaps or + * /proc/self/mountinfo. */ + + (void) device_setup_unit(m, dev, node, false); + } + + /* Update the device unit's state, should it exist */ + return device_update_found_by_name(m, node, add, found, now); +} + +const UnitVTable device_vtable = { + .object_size = sizeof(Device), + .sections = + "Unit\0" + "Device\0" + "Install\0", + + .init = device_init, + .done = device_done, + .load = unit_load_fragment_and_dropin_optional, + + .coldplug = device_coldplug, + + .serialize = device_serialize, + .deserialize_item = device_deserialize_item, + + .dump = device_dump, + + .active_state = device_active_state, + .sub_state_to_string = device_sub_state_to_string, + + .bus_vtable = bus_device_vtable, + + .following = device_following, + .following_set = device_following_set, + + .enumerate = device_enumerate, + .shutdown = device_shutdown, + .supported = device_supported, + + .status_message_formats = { + .starting_stopping = { + [0] = "Expecting device %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Found device %s.", + [JOB_TIMEOUT] = "Timed out waiting for device %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/device.h b/src/grp-system/libcore/device.h new file mode 100644 index 0000000000..184a1a349b --- /dev/null +++ b/src/grp-system/libcore/device.h @@ -0,0 +1,47 @@ +#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 . +***/ + +typedef struct Device Device; + +typedef enum DeviceFound { + DEVICE_NOT_FOUND = 0, + DEVICE_FOUND_UDEV = 1, + DEVICE_FOUND_MOUNT = 2, + DEVICE_FOUND_SWAP = 4, +} DeviceFound; + +struct Device { + Unit meta; + + char *sysfs; + DeviceFound found; + + /* In order to be able to distinguish dependencies on + different device nodes we might end up creating multiple + devices for the same sysfs path. We chain them up here. */ + LIST_FIELDS(struct Device, same_sysfs); + + DeviceState state, deserialized_state; +}; + +extern const UnitVTable device_vtable; + +int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now); diff --git a/src/grp-system/libcore/execute.c b/src/grp-system/libcore/execute.c new file mode 100644 index 0000000000..b73577817f --- /dev/null +++ b/src/grp-system/libcore/execute.c @@ -0,0 +1,3286 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PAM +#include +#endif + +#ifdef HAVE_SELINUX +#include +#endif + +#ifdef HAVE_SECCOMP +#include +#endif + +#ifdef HAVE_APPARMOR +#include +#endif + +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#ifdef HAVE_APPARMOR +#include "shared/apparmor-util.h" +#endif +#include "basic/async.h" +#include "basic/barrier.h" +#include "basic/cap-list.h" +#include "basic/capability-util.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/errno-list.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/io-util.h" +#include "basic/ioprio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rlimit-util.h" +#include "basic/rm-rf.h" + +#include "execute.h" +#include "namespace.h" +#ifdef HAVE_SECCOMP +#include "shared/seccomp-util.h" +#endif +#include "basic/securebits.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/smack-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/utmp-wtmp.h" + +#include "unit.h" + +#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) +#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC) + +/* This assumes there is a 'tty' group */ +#define TTY_MODE 0620 + +#define SNDBUF_SIZE (8*1024*1024) + +static int shift_fds(int fds[], unsigned n_fds) { + int start, restart_from; + + if (n_fds <= 0) + return 0; + + /* Modifies the fds array! (sorts it) */ + + assert(fds); + + start = 0; + for (;;) { + int i; + + restart_from = -1; + + for (i = start; i < (int) n_fds; i++) { + int nfd; + + /* Already at right index? */ + if (fds[i] == i+3) + continue; + + nfd = fcntl(fds[i], F_DUPFD, i + 3); + if (nfd < 0) + return -errno; + + safe_close(fds[i]); + fds[i] = nfd; + + /* Hmm, the fd we wanted isn't free? Then + * let's remember that and try again from here */ + if (nfd != i+3 && restart_from < 0) + restart_from = i; + } + + if (restart_from < 0) + break; + + start = restart_from; + } + + return 0; +} + +static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { + unsigned i; + int r; + + if (n_fds <= 0) + return 0; + + assert(fds); + + /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */ + + for (i = 0; i < n_fds; i++) { + + r = fd_nonblock(fds[i], nonblock); + if (r < 0) + return r; + + /* We unconditionally drop FD_CLOEXEC from the fds, + * since after all we want to pass these fds to our + * children */ + + r = fd_cloexec(fds[i], false); + if (r < 0) + return r; + } + + return 0; +} + +static const char *exec_context_tty_path(const ExecContext *context) { + assert(context); + + if (context->stdio_as_fds) + return NULL; + + if (context->tty_path) + return context->tty_path; + + return "/dev/console"; +} + +static void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) { + const char *path; + + assert(context); + + path = exec_context_tty_path(context); + + if (context->tty_vhangup) { + if (p && p->stdin_fd >= 0) + (void) terminal_vhangup_fd(p->stdin_fd); + else if (path) + (void) terminal_vhangup(path); + } + + if (context->tty_reset) { + if (p && p->stdin_fd >= 0) + (void) reset_terminal_fd(p->stdin_fd, true); + else if (path) + (void) reset_terminal(path); + } + + if (context->tty_vt_disallocate && path) + (void) vt_disallocate(path); +} + +static bool is_terminal_output(ExecOutput o) { + return + o == EXEC_OUTPUT_TTY || + o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || + o == EXEC_OUTPUT_KMSG_AND_CONSOLE || + o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE; +} + +static int open_null_as(int flags, int nfd) { + int fd, r; + + assert(nfd >= 0); + + fd = open("/dev/null", flags|O_NOCTTY); + if (fd < 0) + return -errno; + + if (fd != nfd) { + r = dup2(fd, nfd) < 0 ? -errno : nfd; + safe_close(fd); + } else + r = nfd; + + return r; +} + +static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; + uid_t olduid = UID_INVALID; + gid_t oldgid = GID_INVALID; + int r; + + if (gid != GID_INVALID) { + oldgid = getgid(); + + r = setegid(gid); + if (r < 0) + return -errno; + } + + if (uid != UID_INVALID) { + olduid = getuid(); + + r = seteuid(uid); + if (r < 0) { + r = -errno; + goto restore_gid; + } + } + + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + r = -errno; + + /* If we fail to restore the uid or gid, things will likely + fail later on. This should only happen if an LSM interferes. */ + + if (uid != UID_INVALID) + (void) seteuid(olduid); + + restore_gid: + if (gid != GID_INVALID) + (void) setegid(oldgid); + + return r; +} + +static int connect_logger_as( + Unit *unit, + const ExecContext *context, + ExecOutput output, + const char *ident, + int nfd, + uid_t uid, + gid_t gid) { + + int fd, r; + + assert(context); + assert(output < _EXEC_OUTPUT_MAX); + assert(ident); + assert(nfd >= 0); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -errno; + + r = connect_journal_socket(fd, uid, gid); + if (r < 0) + return r; + + if (shutdown(fd, SHUT_RD) < 0) { + safe_close(fd); + return -errno; + } + + (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); + + dprintf(fd, + "%s\n" + "%s\n" + "%i\n" + "%i\n" + "%i\n" + "%i\n" + "%i\n", + context->syslog_identifier ? context->syslog_identifier : ident, + unit->id, + context->syslog_priority, + !!context->syslog_level_prefix, + output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE, + output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE, + is_terminal_output(output)); + + if (fd == nfd) + return nfd; + + r = dup2(fd, nfd) < 0 ? -errno : nfd; + safe_close(fd); + + return r; +} +static int open_terminal_as(const char *path, mode_t mode, int nfd) { + int fd, r; + + assert(path); + assert(nfd >= 0); + + fd = open_terminal(path, mode | O_NOCTTY); + if (fd < 0) + return fd; + + if (fd != nfd) { + r = dup2(fd, nfd) < 0 ? -errno : nfd; + safe_close(fd); + } else + r = nfd; + + return r; +} + +static bool is_terminal_input(ExecInput i) { + return + i == EXEC_INPUT_TTY || + i == EXEC_INPUT_TTY_FORCE || + i == EXEC_INPUT_TTY_FAIL; +} + +static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { + + if (is_terminal_input(std_input) && !apply_tty_stdin) + return EXEC_INPUT_NULL; + + if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0) + return EXEC_INPUT_NULL; + + return std_input; +} + +static int fixup_output(ExecOutput std_output, int socket_fd) { + + if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0) + return EXEC_OUTPUT_INHERIT; + + return std_output; +} + +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, params->apply_tty_stdin); + + switch (i) { + + case EXEC_INPUT_NULL: + return open_null_as(O_RDONLY, STDIN_FILENO); + + case EXEC_INPUT_TTY: + case EXEC_INPUT_TTY_FORCE: + case EXEC_INPUT_TTY_FAIL: { + int fd, r; + + fd = acquire_terminal(exec_context_tty_path(context), + i == EXEC_INPUT_TTY_FAIL, + i == EXEC_INPUT_TTY_FORCE, + false, + USEC_INFINITY); + if (fd < 0) + return fd; + + if (fd != STDIN_FILENO) { + r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; + safe_close(fd); + } else + r = STDIN_FILENO; + + return r; + } + + case EXEC_INPUT_SOCKET: + return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO; + + default: + assert_not_reached("Unknown input type"); + } +} + +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, + dev_t *journal_stream_dev, + ino_t *journal_stream_ino) { + + ExecOutput o; + ExecInput i; + int r; + + assert(unit); + assert(context); + assert(params); + assert(ident); + assert(journal_stream_dev); + assert(journal_stream_ino); + + 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) { + ExecOutput e; + e = fixup_output(context->std_error, socket_fd); + + /* This expects the input and output are already set up */ + + /* Don't change the stderr file descriptor if we inherit all + * the way and are not on a tty */ + if (e == EXEC_OUTPUT_INHERIT && + o == EXEC_OUTPUT_INHERIT && + i == EXEC_INPUT_NULL && + !is_terminal_input(context->std_input) && + getppid () != 1) + return fileno; + + /* Duplicate from stdout if possible */ + if (e == o || e == EXEC_OUTPUT_INHERIT) + return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno; + + o = e; + + } else if (o == EXEC_OUTPUT_INHERIT) { + /* If input got downgraded, inherit the original value */ + if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) + return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); + + /* If the input is connected to anything that's not a /dev/null, inherit that... */ + if (i != EXEC_INPUT_NULL) + return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; + + /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ + if (getppid() != 1) + return fileno; + + /* We need to open /dev/null here anew, to get the right access mode. */ + return open_null_as(O_WRONLY, fileno); + } + + switch (o) { + + case EXEC_OUTPUT_NULL: + return open_null_as(O_WRONLY, fileno); + + case EXEC_OUTPUT_TTY: + if (is_terminal_input(i)) + return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; + + /* We don't reset the terminal if this is just about output */ + return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno); + + case EXEC_OUTPUT_SYSLOG: + case EXEC_OUTPUT_SYSLOG_AND_CONSOLE: + case EXEC_OUTPUT_KMSG: + case EXEC_OUTPUT_KMSG_AND_CONSOLE: + case EXEC_OUTPUT_JOURNAL: + case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: + r = connect_logger_as(unit, context, o, ident, fileno, uid, gid); + if (r < 0) { + log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr"); + r = open_null_as(O_WRONLY, fileno); + } else { + struct stat st; + + /* If we connected this fd to the journal via a stream, patch the device/inode into the passed + * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits + * services to detect whether they are connected to the journal or not. */ + + if (fstat(fileno, &st) >= 0) { + *journal_stream_dev = st.st_dev; + *journal_stream_ino = st.st_ino; + } + } + return r; + + case EXEC_OUTPUT_SOCKET: + assert(socket_fd >= 0); + return dup2(socket_fd, fileno) < 0 ? -errno : fileno; + + default: + assert_not_reached("Unknown error type"); + } +} + +static int chown_terminal(int fd, uid_t uid) { + struct stat st; + + assert(fd >= 0); + + /* Before we chown/chmod the TTY, let's ensure this is actually a tty */ + if (isatty(fd) < 1) + return 0; + + /* This might fail. What matters are the results. */ + (void) fchown(fd, uid, -1); + (void) fchmod(fd, TTY_MODE); + + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE) + return -EPERM; + + return 0; +} + +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); + + saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3); + if (saved_stdin < 0) + return -errno; + + saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3); + if (saved_stdout < 0) + return -errno; + + fd = acquire_terminal( + "/dev/console", + false, + false, + false, + DEFAULT_CONFIRM_USEC); + if (fd < 0) + return fd; + + r = chown_terminal(fd, getuid()); + if (r < 0) + return r; + + r = reset_terminal_fd(fd, true); + if (r < 0) + return r; + + 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; + + saved_stdin = saved_stdout = -1; + + return 0; +} + +_printf_(1, 2) static int write_confirm_message(const char *format, ...) { + _cleanup_close_ int fd = -1; + va_list ap; + + assert(format); + + fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + va_start(ap, format); + vdprintf(fd, format, ap); + va_end(ap); + + return 0; +} + +static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) { + int r = 0; + + assert(saved_stdin); + assert(saved_stdout); + + release_terminal(); + + if (*saved_stdin >= 0) + if (dup2(*saved_stdin, STDIN_FILENO) < 0) + r = -errno; + + if (*saved_stdout >= 0) + if (dup2(*saved_stdout, STDOUT_FILENO) < 0) + r = -errno; + + *saved_stdin = safe_close(*saved_stdin); + *saved_stdout = safe_close(*saved_stdout); + + return r; +} + +static int ask_for_confirmation(char *response, char **argv) { + int saved_stdout = -1, saved_stdin = -1, r; + _cleanup_free_ char *line = NULL; + + r = setup_confirm_stdio(&saved_stdin, &saved_stdout); + if (r < 0) + return r; + + line = exec_command_line(argv); + if (!line) + return -ENOMEM; + + r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line); + + restore_confirm_stdio(&saved_stdin, &saved_stdout); + + return r; +} + +static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) { + bool keep_groups = false; + int r; + + assert(context); + + /* Lookup and set GID and supplementary group list. Here too + * we avoid NSS lookups for gid=0. */ + + if (context->group || username) { + /* First step, initialize groups from /etc/groups */ + if (username && gid != 0) { + if (initgroups(username, gid) < 0) + return -errno; + + keep_groups = true; + } + + /* Second step, set our gids */ + if (setresgid(gid, gid, gid) < 0) + return -errno; + } + + if (context->supplementary_groups) { + int ngroups_max, k; + gid_t *gids; + char **i; + + /* Final step, initialize any manually set supplementary groups */ + assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0); + + if (!(gids = new(gid_t, ngroups_max))) + return -ENOMEM; + + if (keep_groups) { + k = getgroups(ngroups_max, gids); + if (k < 0) { + free(gids); + return -errno; + } + } else + k = 0; + + STRV_FOREACH(i, context->supplementary_groups) { + const char *g; + + if (k >= ngroups_max) { + free(gids); + return -E2BIG; + } + + g = *i; + r = get_group_creds(&g, gids+k); + if (r < 0) { + free(gids); + return r; + } + + k++; + } + + if (setgroups(k, gids) < 0) { + free(gids); + return -errno; + } + + free(gids); + } + + return 0; +} + +static int enforce_user(const ExecContext *context, uid_t uid) { + assert(context); + + /* Sets (but doesn't look up) the uid and make sure we keep the + * capabilities while doing so. */ + + if (context->capability_ambient_set != 0) { + + /* First step: If we need to keep capabilities but + * drop privileges we need to make sure we keep our + * caps, while we drop privileges. */ + if (uid != 0) { + int sb = context->secure_bits | 1<= 0); + + parent_pid = getpid(); + + pam_pid = fork(); + if (pam_pid < 0) { + r = -errno; + goto fail; + } + + if (pam_pid == 0) { + int sig, ret = EXIT_PAM; + + /* The child's job is to reset the PAM session on + * termination */ + barrier_set_role(&barrier, BARRIER_CHILD); + + /* This string must fit in 10 chars (i.e. the length + * of "/sbin/init"), to look pretty in /bin/ps */ + rename_process("(sd-pam)"); + + /* Make sure we don't keep open the passed fds in this + child. We assume that otherwise only those fds are + open here that have been opened by PAM. */ + close_many(fds, n_fds); + + /* Drop privileges - we don't need any to pam_close_session + * and this will make PR_SET_PDEATHSIG work in most cases. + * If this fails, ignore the error - but expect sd-pam threads + * to fail to exit normally */ + if (setresuid(uid, uid, uid) < 0) + log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m"); + + (void) ignore_signals(SIGPIPE, -1); + + /* Wait until our parent died. This will only work if + * the above setresuid() succeeds, otherwise the kernel + * will not allow unprivileged parents kill their privileged + * children this way. We rely on the control groups kill logic + * to do the rest for us. */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + goto child_finish; + + /* Tell the parent that our setup is done. This is especially + * important regarding dropping privileges. Otherwise, unit + * setup might race against our setresuid(2) call. */ + barrier_place(&barrier); + + /* Check if our parent process might already have + * died? */ + if (getppid() == parent_pid) { + sigset_t ss; + + assert_se(sigemptyset(&ss) >= 0); + assert_se(sigaddset(&ss, SIGTERM) >= 0); + + for (;;) { + if (sigwait(&ss, &sig) < 0) { + if (errno == EINTR) + continue; + + goto child_finish; + } + + assert(sig == SIGTERM); + break; + } + } + + /* If our parent died we'll end the session */ + if (getppid() != parent_pid) { + pam_code = pam_close_session(handle, flags); + if (pam_code != PAM_SUCCESS) + goto child_finish; + } + + ret = 0; + + child_finish: + pam_end(handle, pam_code | flags); + _exit(ret); + } + + barrier_set_role(&barrier, BARRIER_PARENT); + + /* If the child was forked off successfully it will do all the + * cleanups, so forget about the handle here. */ + handle = NULL; + + /* Unblock SIGTERM again in the parent */ + assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0); + + /* We close the log explicitly here, since the PAM modules + * might have opened it, but we don't want this fd around. */ + closelog(); + + /* Synchronously wait for the child to initialize. We don't care for + * errors as we cannot recover. However, warn loudly if it happens. */ + if (!barrier_place_and_sync(&barrier)) + log_error("PAM initialization failed"); + + strv_free(*env); + *env = e; + + return 0; + +fail: + if (pam_code != PAM_SUCCESS) { + log_error("PAM failed: %s", pam_strerror(handle, pam_code)); + r = -EPERM; /* PAM errors do not map to errno */ + } else + log_error_errno(r, "PAM failed: %m"); + + if (handle) { + if (close_session) + pam_code = pam_close_session(handle, flags); + + pam_end(handle, pam_code | flags); + } + + strv_free(e); + closelog(); + + return r; +} +#endif + +static void rename_process_from_path(const char *path) { + char process_name[11]; + const char *p; + size_t l; + + /* This resulting string must fit in 10 chars (i.e. the length + * of "/sbin/init") to look pretty in /bin/ps */ + + p = basename(path); + if (isempty(p)) { + rename_process("(...)"); + return; + } + + l = strlen(p); + if (l > 8) { + /* The end of the process name is usually more + * interesting, since the first bit might just be + * "systemd-" */ + p = p + l - 8; + l = 8; + } + + process_name[0] = '('; + memcpy(process_name+1, p, l); + process_name[1+l] = ')'; + process_name[1+l+1] = 0; + + rename_process(process_name); +} + +#ifdef HAVE_SECCOMP + +static int apply_seccomp(const ExecContext *c) { + uint32_t negative_action, action; + scmp_filter_ctx *seccomp; + Iterator i; + void *id; + int r; + + assert(c); + + negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno); + + seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + if (c->syscall_archs) { + + SET_FOREACH(id, c->syscall_archs, i) { + r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); + if (r == -EEXIST) + continue; + if (r < 0) + goto finish; + } + + } else { + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + } + + action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action; + SET_FOREACH(id, c->syscall_filter, i) { + r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0); + if (r < 0) + goto finish; + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + +static int apply_address_families(const ExecContext *c) { + scmp_filter_ctx *seccomp; + Iterator i; + int r; + + assert(c); + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + + if (c->address_families_whitelist) { + int af, first = 0, last = 0; + void *afp; + + /* If this is a whitelist, we first block the address + * families that are out of range and then everything + * that is not in the set. First, we find the lowest + * and highest address family in the set. */ + + SET_FOREACH(afp, c->address_families, i) { + af = PTR_TO_INT(afp); + + if (af <= 0 || af >= af_max()) + continue; + + if (first == 0 || af < first) + first = af; + + if (last == 0 || af > last) + last = af; + } + + assert((first == 0) == (last == 0)); + + if (first == 0) { + + /* No entries in the valid range, block everything */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 0); + if (r < 0) + goto finish; + + } else { + + /* Block everything below the first entry */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_LT, first)); + if (r < 0) + goto finish; + + /* Block everything above the last entry */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_GT, last)); + if (r < 0) + goto finish; + + /* Block everything between the first and last + * entry */ + for (af = 1; af < af_max(); af++) { + + if (set_contains(c->address_families, INT_TO_PTR(af))) + continue; + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_EQ, af)); + if (r < 0) + goto finish; + } + } + + } else { + void *af; + + /* If this is a blacklist, then generate one rule for + * each address family that are then combined in OR + * checks. */ + + SET_FOREACH(af, c->address_families, i) { + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af))); + if (r < 0) + goto finish; + } + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + +static int apply_memory_deny_write_execute(const ExecContext *c) { + scmp_filter_ctx *seccomp; + int r; + + assert(c); + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mmap), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); + if (r < 0) + goto finish; + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mprotect), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); + if (r < 0) + goto finish; + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + +static int apply_restrict_realtime(const ExecContext *c) { + static const int permitted_policies[] = { + SCHED_OTHER, + SCHED_BATCH, + SCHED_IDLE, + }; + + scmp_filter_ctx *seccomp; + unsigned i; + int r, p, max_policy = 0; + + assert(c); + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + /* Determine the highest policy constant we want to allow */ + for (i = 0; i < ELEMENTSOF(permitted_policies); i++) + if (permitted_policies[i] > max_policy) + max_policy = permitted_policies[i]; + + /* Go through all policies with lower values than that, and block them -- unless they appear in the + * whitelist. */ + for (p = 0; p < max_policy; p++) { + bool good = false; + + /* Check if this is in the whitelist. */ + for (i = 0; i < ELEMENTSOF(permitted_policies); i++) + if (permitted_policies[i] == p) { + good = true; + break; + } + + if (good) + continue; + + /* Deny this policy */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(sched_setscheduler), + 1, + SCMP_A1(SCMP_CMP_EQ, p)); + if (r < 0) + goto finish; + } + + /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are unsigned here, + * hence no need no check for < 0 values. */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(sched_setscheduler), + 1, + SCMP_A1(SCMP_CMP_GT, max_policy)); + if (r < 0) + goto finish; + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); + return r; +} + +#endif + +static void do_idle_pipe_dance(int idle_pipe[4]) { + assert(idle_pipe); + + + idle_pipe[1] = safe_close(idle_pipe[1]); + idle_pipe[2] = safe_close(idle_pipe[2]); + + if (idle_pipe[0] >= 0) { + int r; + + r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); + + if (idle_pipe[3] >= 0 && r == 0 /* timeout */) { + ssize_t n; + + /* Signal systemd that we are bored and want to continue. */ + n = write(idle_pipe[3], "x", 1); + if (n > 0) + /* Wait for systemd to react to the signal above. */ + fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC); + } + + idle_pipe[0] = safe_close(idle_pipe[0]); + + } + + idle_pipe[3] = safe_close(idle_pipe[3]); +} + +static int build_environment( + const ExecContext *c, + const ExecParameters *p, + unsigned n_fds, + const char *home, + const char *username, + const char *shell, + dev_t journal_stream_dev, + ino_t journal_stream_ino, + char ***ret) { + + _cleanup_strv_free_ char **our_env = NULL; + unsigned n_env = 0; + char *x; + + assert(c); + assert(ret); + + our_env = new0(char*, 12); + 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; + + if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0) + return -ENOMEM; + our_env[n_env++] = x; + + joined = strv_join(p->fd_names, ":"); + if (!joined) + return -ENOMEM; + + x = strjoin("LISTEN_FDNAMES=", joined, NULL); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (p->watchdog_usec > 0) { + if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0) + return -ENOMEM; + our_env[n_env++] = x; + + if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, p->watchdog_usec) < 0) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (home) { + x = strappend("HOME=", home); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (username) { + x = strappend("LOGNAME=", username); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + + x = strappend("USER=", username); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (shell) { + x = strappend("SHELL=", shell); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (is_terminal_input(c->std_input) || + c->std_output == EXEC_OUTPUT_TTY || + c->std_error == EXEC_OUTPUT_TTY || + c->tty_path) { + + x = strdup(default_term_for_tty(exec_context_tty_path(c))); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; + } + + if (journal_stream_dev != 0 && journal_stream_ino != 0) { + if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0) + return -ENOMEM; + + our_env[n_env++] = x; + } + + our_env[n_env++] = NULL; + assert(n_env <= 12); + + *ret = our_env; + our_env = NULL; + + 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, + ExecRuntime *runtime) { + + assert(context); + assert(params); + + if (!strv_isempty(context->read_write_paths) || + !strv_isempty(context->read_only_paths) || + !strv_isempty(context->inaccessible_paths)) + return true; + + if (context->mount_flags != 0) + return true; + + if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) + return true; + + if (context->private_devices || + context->protect_system != PROTECT_SYSTEM_NO || + context->protect_home != PROTECT_HOME_NO) + return true; + + 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 (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, + const ExecContext *context, + const ExecParameters *params, + ExecRuntime *runtime, + char **argv, + int socket_fd, + int *fds, unsigned n_fds, + char **files_env, + int *exit_status) { + + _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; + _cleanup_free_ char *mac_selinux_context_net = NULL; + const char *username = NULL, *home = NULL, *shell = NULL, *wd; + dev_t journal_stream_dev = 0; + ino_t journal_stream_ino = 0; + bool needs_mount_namespace; + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; + int i, r; + + assert(unit); + assert(command); + assert(context); + assert(params); + assert(exit_status); + + rename_process_from_path(command->path); + + /* We reset exactly these signals, since they are the + * only ones we set to SIG_IGN in the main daemon. All + * others we leave untouched because we set them to + * SIG_DFL or a valid handler initially, both of which + * will be demoted to SIG_DFL. */ + (void) default_signals(SIGNALS_CRASH_HANDLER, + SIGNALS_IGNORE, -1); + + if (context->ignore_sigpipe) + (void) ignore_signals(SIGPIPE, -1); + + r = reset_signal_mask(); + if (r < 0) { + *exit_status = EXIT_SIGNAL_MASK; + return r; + } + + if (params->idle_pipe) + do_idle_pipe_dance(params->idle_pipe); + + /* Close sockets very early to make sure we don't + * block init reexecution because it cannot bind its + * sockets */ + + log_forget_fds(); + + r = close_remaining_fds(params, runtime, socket_fd, fds, n_fds); + if (r < 0) { + *exit_status = EXIT_FDS; + return r; + } + + if (!context->same_pgrp) + if (setsid() < 0) { + *exit_status = EXIT_SETSID; + return -errno; + } + + exec_context_tty_reset(context, params); + + if (params->confirm_spawn) { + char response; + + r = ask_for_confirmation(&response, argv); + if (r == -ETIMEDOUT) + write_confirm_message("Confirmation question timed out, assuming positive response.\n"); + else if (r < 0) + write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r)); + else if (response == 's') { + write_confirm_message("Skipping execution.\n"); + *exit_status = EXIT_CONFIRM; + return -ECANCELED; + } else if (response == 'n') { + write_confirm_message("Failing execution.\n"); + *exit_status = 0; + return 0; + } + } + + if (context->user) { + username = context->user; + r = get_user_creds(&username, &uid, &gid, &home, &shell); + if (r < 0) { + *exit_status = EXIT_USER; + return r; + } + } + + if (context->group) { + const char *g = context->group; + + r = get_group_creds(&g, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + + + /* If a socket is connected to STDIN/STDOUT/STDERR, we + * must sure to drop O_NONBLOCK */ + if (socket_fd >= 0) + (void) fd_nonblock(socket_fd, false); + + r = setup_input(context, params, socket_fd); + if (r < 0) { + *exit_status = EXIT_STDIN; + return r; + } + + r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); + if (r < 0) { + *exit_status = EXIT_STDOUT; + return r; + } + + r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino); + if (r < 0) { + *exit_status = EXIT_STDERR; + return r; + } + + if (params->cgroup_path) { + r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL); + if (r < 0) { + *exit_status = EXIT_CGROUP; + return r; + } + } + + if (context->oom_score_adjust_set) { + char t[DECIMAL_STR_MAX(context->oom_score_adjust)]; + + /* When we can't make this change due to EPERM, then + * let's silently skip over it. User namespaces + * prohibit write access to this file, and we + * shouldn't trip up over that. */ + + sprintf(t, "%i", context->oom_score_adjust); + r = write_string_file("/proc/self/oom_score_adj", t, 0); + if (r == -EPERM || r == -EACCES) { + log_open(); + log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m"); + log_close(); + } else if (r < 0) { + *exit_status = EXIT_OOM_ADJUST; + return -errno; + } + } + + if (context->nice_set) + if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) { + *exit_status = EXIT_NICE; + return -errno; + } + + if (context->cpu_sched_set) { + struct sched_param param = { + .sched_priority = context->cpu_sched_priority, + }; + + r = sched_setscheduler(0, + context->cpu_sched_policy | + (context->cpu_sched_reset_on_fork ? + SCHED_RESET_ON_FORK : 0), + ¶m); + if (r < 0) { + *exit_status = EXIT_SETSCHEDULER; + return -errno; + } + } + + if (context->cpuset) + if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { + *exit_status = EXIT_CPUAFFINITY; + return -errno; + } + + if (context->ioprio_set) + if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { + *exit_status = EXIT_IOPRIO; + return -errno; + } + + if (context->timer_slack_nsec != NSEC_INFINITY) + if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { + *exit_status = EXIT_TIMERSLACK; + return -errno; + } + + if (context->personality != PERSONALITY_INVALID) + if (personality(context->personality) < 0) { + *exit_status = EXIT_PERSONALITY; + return -errno; + } + + if (context->utmp_id) + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); + + if (context->user && is_terminal_input(context->std_input)) { + r = chown_terminal(STDIN_FILENO, uid); + if (r < 0) { + *exit_status = EXIT_STDIN; + return r; + } + } + + /* If delegation is enabled we'll pass ownership of the cgroup + * (but only in systemd's own controller hierarchy!) to the + * user of the new process. */ + if (params->cgroup_path && context->user && params->cgroup_delegate) { + r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid); + if (r < 0) { + *exit_status = EXIT_CGROUP; + return r; + } + + + r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0755, uid, gid); + if (r < 0) { + *exit_status = EXIT_CGROUP; + return r; + } + } + + if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) { + char **rt; + + STRV_FOREACH(rt, context->runtime_directory) { + _cleanup_free_ char *p; + + p = strjoin(params->runtime_prefix, "/", *rt, NULL); + if (!p) { + *exit_status = EXIT_RUNTIME_DIRECTORY; + return -ENOMEM; + } + + r = mkdir_p_label(p, context->runtime_directory_mode); + if (r < 0) { + *exit_status = EXIT_RUNTIME_DIRECTORY; + return r; + } + + r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid); + if (r < 0) { + *exit_status = EXIT_RUNTIME_DIRECTORY; + return r; + } + } + } + + r = build_environment( + context, + params, + n_fds, + home, + username, + shell, + journal_stream_dev, + journal_stream_ino, + &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; + } + + accum_env = strv_env_merge(5, + params->environment, + our_env, + pass_env, + context->environment, + files_env, + NULL); + if (!accum_env) { + *exit_status = EXIT_MEMORY; + return -ENOMEM; + } + accum_env = strv_env_clean(accum_env); + + umask(context->umask); + + if (params->apply_permissions && !command->privileged) { + r = enforce_groups(context, username, gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } +#ifdef HAVE_SMACK + if (context->smack_process_label) { + r = mac_smack_apply_pid(0, context->smack_process_label); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#ifdef SMACK_DEFAULT_PROCESS_LABEL + else { + _cleanup_free_ char *exec_label = NULL; + + r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label); + if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + + r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + } +#endif +#endif +#ifdef HAVE_PAM + if (context->pam_name && username) { + r = setup_pam(context->pam_name, username, uid, context->tty_path, &accum_env, fds, n_fds); + if (r < 0) { + *exit_status = EXIT_PAM; + return r; + } + } +#endif + } + + if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { + r = setup_netns(runtime->netns_storage_socket); + if (r < 0) { + *exit_status = EXIT_NETWORK; + return r; + } + } + + needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); + + if (needs_mount_namespace) { + char *tmp = NULL, *var = NULL; + + /* The runtime struct only contains the parent + * of the private /tmp, which is + * non-accessible to world users. Inside of it + * there's a /tmp that is sticky, and that's + * the one we want to use here. */ + + if (context->private_tmp && runtime) { + if (runtime->tmp_dir) + tmp = strjoina(runtime->tmp_dir, "/tmp"); + if (runtime->var_tmp_dir) + var = strjoina(runtime->var_tmp_dir, "/tmp"); + } + + r = setup_namespace( + params->apply_chroot ? context->root_directory : NULL, + context->read_write_paths, + context->read_only_paths, + context->inaccessible_paths, + tmp, + var, + context->private_devices, + context->protect_home, + context->protect_system, + context->mount_flags); + + /* If we couldn't set up the namespace this is + * probably due to a missing capability. In this case, + * silently proceeed. */ + if (r == -EPERM || r == -EACCES) { + log_open(); + log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m"); + log_close(); + } else if (r < 0) { + *exit_status = EXIT_NAMESPACE; + return r; + } + } + + 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) { + *exit_status = EXIT_CHROOT; + return -errno; + } + + if (chdir(wd) < 0 && + !context->working_directory_missing_ok) { + *exit_status = EXIT_CHDIR; + return -errno; + } + } else { + const char *d; + + d = strjoina(strempty(context->root_directory), "/", strempty(wd)); + if (chdir(d) < 0 && + !context->working_directory_missing_ok) { + *exit_status = EXIT_CHDIR; + return -errno; + } + } + +#ifdef HAVE_SELINUX + if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) { + r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); + if (r < 0) { + *exit_status = EXIT_SELINUX_CONTEXT; + return r; + } + } +#endif + + /* We repeat the fd closing here, to make sure that + * nothing is leaked from the PAM modules. Note that + * we are more aggressive this time since socket_fd + * and the netns fds we don't need anymore. The custom + * endpoint fd was needed to upload the policy and can + * now be closed as well. */ + r = close_all_fds(fds, n_fds); + if (r >= 0) + r = shift_fds(fds, n_fds); + if (r >= 0) + r = flags_fds(fds, n_fds, context->non_blocking); + if (r < 0) { + *exit_status = EXIT_FDS; + return r; + } + + if (params->apply_permissions && !command->privileged) { + + bool use_address_families = context->address_families_whitelist || + !set_isempty(context->address_families); + bool use_syscall_filter = context->syscall_whitelist || + !set_isempty(context->syscall_filter) || + !set_isempty(context->syscall_archs); + int secure_bits = context->secure_bits; + + for (i = 0; i < _RLIMIT_MAX; i++) { + + if (!context->rlimit[i]) + continue; + + r = setrlimit_closest(i, context->rlimit[i]); + if (r < 0) { + *exit_status = EXIT_LIMITS; + return r; + } + } + + /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */ + if (context->restrict_realtime && !context->rlimit[RLIMIT_RTPRIO]) { + if (setrlimit(RLIMIT_RTPRIO, &RLIMIT_MAKE_CONST(0)) < 0) { + *exit_status = EXIT_LIMITS; + return -errno; + } + } + + 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->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<no_new_privileges || + (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || context->memory_deny_write_execute || context->restrict_realtime || use_syscall_filter))) + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + *exit_status = EXIT_NO_NEW_PRIVILEGES; + return -errno; + } + +#ifdef HAVE_SECCOMP + if (use_address_families) { + r = apply_address_families(context); + if (r < 0) { + *exit_status = EXIT_ADDRESS_FAMILIES; + return r; + } + } + + if (context->memory_deny_write_execute) { + r = apply_memory_deny_write_execute(context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return r; + } + } + + if (context->restrict_realtime) { + r = apply_restrict_realtime(context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return r; + } + } + + if (use_syscall_filter) { + r = apply_seccomp(context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return r; + } + } +#endif + +#ifdef HAVE_SELINUX + if (mac_selinux_use()) { + char *exec_context = mac_selinux_context_net ?: context->selinux_context; + + if (exec_context) { + r = setexeccon(exec_context); + if (r < 0) { + *exit_status = EXIT_SELINUX_CONTEXT; + return r; + } + } + } +#endif + +#ifdef HAVE_APPARMOR + if (context->apparmor_profile && mac_apparmor_use()) { + r = aa_change_onexec(context->apparmor_profile); + if (r < 0 && !context->apparmor_profile_ignore) { + *exit_status = EXIT_APPARMOR_PROFILE; + return -errno; + } + } +#endif + } + + final_argv = replace_env_argv(argv, accum_env); + if (!final_argv) { + *exit_status = EXIT_MEMORY; + return -ENOMEM; + } + + if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + _cleanup_free_ char *line; + + line = exec_command_line(final_argv); + if (line) { + log_open(); + log_struct(LOG_DEBUG, + LOG_UNIT_ID(unit), + "EXECUTABLE=%s", command->path, + LOG_UNIT_MESSAGE(unit, "Executing: %s", line), + NULL); + log_close(); + } + } + + execve(command->path, final_argv, accum_env); + *exit_status = EXIT_EXEC; + return -errno; +} + +int exec_spawn(Unit *unit, + ExecCommand *command, + const ExecContext *context, + const ExecParameters *params, + ExecRuntime *runtime, + pid_t *ret) { + + _cleanup_strv_free_ char **files_env = NULL; + int *fds = NULL; unsigned n_fds = 0; + _cleanup_free_ char *line = NULL; + int socket_fd, r; + char **argv; + pid_t pid; + + assert(unit); + assert(command); + assert(context); + assert(ret); + assert(params); + assert(params->fds || params->n_fds <= 0); + + if (context->std_input == EXEC_INPUT_SOCKET || + context->std_output == EXEC_OUTPUT_SOCKET || + context->std_error == EXEC_OUTPUT_SOCKET) { + + if (params->n_fds != 1) { + log_unit_error(unit, "Got more than one socket."); + return -EINVAL; + } + + socket_fd = params->fds[0]; + } else { + socket_fd = -1; + fds = params->fds; + n_fds = params->n_fds; + } + + r = exec_context_load_environment(unit, context, &files_env); + if (r < 0) + return log_unit_error_errno(unit, r, "Failed to load environment files: %m"); + + argv = params->argv ?: command->argv; + line = exec_command_line(argv); + if (!line) + return log_oom(); + + log_struct(LOG_DEBUG, + LOG_UNIT_ID(unit), + LOG_UNIT_MESSAGE(unit, "About to execute: %s", line), + "EXECUTABLE=%s", command->path, + NULL); + pid = fork(); + if (pid < 0) + return log_unit_error_errno(unit, errno, "Failed to fork: %m"); + + if (pid == 0) { + int exit_status; + + r = exec_child(unit, + command, + context, + params, + runtime, + argv, + socket_fd, + fds, n_fds, + files_env, + &exit_status); + if (r < 0) { + log_open(); + log_struct_errno(LOG_ERR, r, + LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), + LOG_UNIT_ID(unit), + LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", + exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD), + command->path), + "EXECUTABLE=%s", command->path, + NULL); + } + + _exit(exit_status); + } + + log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid); + + /* We add the new process to the cgroup both in the child (so + * that we can be sure that no user code is ever executed + * outside of the cgroup) and in the parent (so that we can be + * sure that when we kill the cgroup the process will be + * killed too). */ + if (params->cgroup_path) + (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid); + + exec_status_start(&command->exec_status, pid); + + *ret = pid; + return 0; +} + +void exec_context_init(ExecContext *c) { + assert(c); + + c->umask = 0022; + c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); + c->cpu_sched_policy = SCHED_OTHER; + c->syslog_priority = LOG_DAEMON|LOG_INFO; + c->syslog_level_prefix = true; + c->ignore_sigpipe = true; + 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) { + unsigned l; + + assert(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]); + + c->working_directory = mfree(c->working_directory); + c->root_directory = mfree(c->root_directory); + c->tty_path = mfree(c->tty_path); + c->syslog_identifier = mfree(c->syslog_identifier); + c->user = mfree(c->user); + c->group = mfree(c->group); + + c->supplementary_groups = strv_free(c->supplementary_groups); + + c->pam_name = mfree(c->pam_name); + + c->read_only_paths = strv_free(c->read_only_paths); + c->read_write_paths = strv_free(c->read_write_paths); + c->inaccessible_paths = strv_free(c->inaccessible_paths); + + if (c->cpuset) + CPU_FREE(c->cpuset); + + c->utmp_id = mfree(c->utmp_id); + c->selinux_context = mfree(c->selinux_context); + c->apparmor_profile = mfree(c->apparmor_profile); + + c->syscall_filter = set_free(c->syscall_filter); + c->syscall_archs = set_free(c->syscall_archs); + c->address_families = set_free(c->address_families); + + c->runtime_directory = strv_free(c->runtime_directory); +} + +int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) { + char **i; + + assert(c); + + if (!runtime_prefix) + return 0; + + STRV_FOREACH(i, c->runtime_directory) { + _cleanup_free_ char *p; + + p = strjoin(runtime_prefix, "/", *i, NULL); + if (!p) + return -ENOMEM; + + /* We execute this synchronously, since we need to be + * sure this is gone when we start the service + * next. */ + (void) rm_rf(p, REMOVE_ROOT); + } + + return 0; +} + +void exec_command_done(ExecCommand *c) { + assert(c); + + c->path = mfree(c->path); + + c->argv = strv_free(c->argv); +} + +void exec_command_done_array(ExecCommand *c, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) + exec_command_done(c+i); +} + +ExecCommand* exec_command_free_list(ExecCommand *c) { + ExecCommand *i; + + while ((i = c)) { + LIST_REMOVE(command, c, i); + exec_command_done(i); + free(i); + } + + return NULL; +} + +void exec_command_free_array(ExecCommand **c, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) + c[i] = exec_command_free_list(c[i]); +} + +typedef struct InvalidEnvInfo { + Unit *unit; + const char *path; +} InvalidEnvInfo; + +static void invalid_env(const char *p, void *userdata) { + InvalidEnvInfo *info = userdata; + + log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path); +} + +int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { + char **i, **r = NULL; + + assert(c); + assert(l); + + STRV_FOREACH(i, c->environment_files) { + char *fn; + int k; + bool ignore = false; + char **p; + _cleanup_globfree_ glob_t pglob = {}; + int count, n; + + fn = *i; + + if (fn[0] == '-') { + ignore = true; + fn++; + } + + if (!path_is_absolute(fn)) { + if (ignore) + continue; + + strv_free(r); + return -EINVAL; + } + + /* Filename supports globbing, take all matching files */ + errno = 0; + if (glob(fn, 0, NULL, &pglob) != 0) { + if (ignore) + continue; + + strv_free(r); + return errno > 0 ? -errno : -EINVAL; + } + count = pglob.gl_pathc; + if (count == 0) { + if (ignore) + continue; + + strv_free(r); + return -EINVAL; + } + for (n = 0; n < count; n++) { + k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p); + if (k < 0) { + if (ignore) + continue; + + strv_free(r); + return k; + } + /* Log invalid environment variables with filename */ + if (p) { + InvalidEnvInfo info = { + .unit = unit, + .path = pglob.gl_pathv[n] + }; + + p = strv_env_clean_with_callback(p, invalid_env, &info); + } + + if (r == NULL) + r = p; + else { + char **m; + + m = strv_env_merge(2, r, p); + strv_free(r); + strv_free(p); + if (!m) + return -ENOMEM; + + r = m; + } + } + } + + *l = r; + + return 0; +} + +static bool tty_may_match_dev_console(const char *tty) { + _cleanup_free_ char *active = NULL; + char *console; + + if (!tty) + return true; + + if (startswith(tty, "/dev/")) + tty += 5; + + /* trivial identity? */ + if (streq(tty, "console")) + return true; + + console = resolve_dev_console(&active); + /* if we could not resolve, assume it may */ + if (!console) + return true; + + /* "tty0" means the active VC, so it may be the same sometimes */ + return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty)); +} + +bool exec_context_may_touch_console(ExecContext *ec) { + + return (ec->tty_reset || + ec->tty_vhangup || + ec->tty_vt_disallocate || + is_terminal_input(ec->std_input) || + is_terminal_output(ec->std_output) || + is_terminal_output(ec->std_error)) && + tty_may_match_dev_console(exec_context_tty_path(ec)); +} + +static void strv_fprintf(FILE *f, char **l) { + char **g; + + assert(f); + + STRV_FOREACH(g, l) + fprintf(f, " %s", *g); +} + +void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { + char **e, **d; + unsigned i; + + assert(c); + assert(f); + + prefix = strempty(prefix); + + fprintf(f, + "%sUMask: %04o\n" + "%sWorkingDirectory: %s\n" + "%sRootDirectory: %s\n" + "%sNonBlocking: %s\n" + "%sPrivateTmp: %s\n" + "%sPrivateNetwork: %s\n" + "%sPrivateDevices: %s\n" + "%sProtectHome: %s\n" + "%sProtectSystem: %s\n" + "%sIgnoreSIGPIPE: %s\n" + "%sMemoryDenyWriteExecute: %s\n" + "%sRestrictRealtime: %s\n", + prefix, c->umask, + prefix, c->working_directory ? c->working_directory : "/", + prefix, c->root_directory ? c->root_directory : "/", + prefix, yes_no(c->non_blocking), + prefix, yes_no(c->private_tmp), + prefix, yes_no(c->private_network), + prefix, yes_no(c->private_devices), + prefix, protect_home_to_string(c->protect_home), + prefix, protect_system_to_string(c->protect_system), + prefix, yes_no(c->ignore_sigpipe), + prefix, yes_no(c->memory_deny_write_execute), + prefix, yes_no(c->restrict_realtime)); + + STRV_FOREACH(e, c->environment) + fprintf(f, "%sEnvironment: %s\n", prefix, *e); + + 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", + prefix, c->nice); + + if (c->oom_score_adjust_set) + fprintf(f, + "%sOOMScoreAdjust: %i\n", + prefix, c->oom_score_adjust); + + for (i = 0; i < RLIM_NLIMITS; i++) + 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; + + ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str); + fprintf(f, + "%sIOSchedulingClass: %s\n" + "%sIOPriority: %i\n", + prefix, strna(class_str), + prefix, (int) IOPRIO_PRIO_DATA(c->ioprio)); + } + + if (c->cpu_sched_set) { + _cleanup_free_ char *policy_str = NULL; + + sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str); + fprintf(f, + "%sCPUSchedulingPolicy: %s\n" + "%sCPUSchedulingPriority: %i\n" + "%sCPUSchedulingResetOnFork: %s\n", + prefix, strna(policy_str), + prefix, c->cpu_sched_priority, + prefix, yes_no(c->cpu_sched_reset_on_fork)); + } + + if (c->cpuset) { + fprintf(f, "%sCPUAffinity:", prefix); + for (i = 0; i < c->cpuset_ncpus; i++) + if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset)) + fprintf(f, " %u", i); + fputs("\n", f); + } + + if (c->timer_slack_nsec != NSEC_INFINITY) + fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec); + + fprintf(f, + "%sStandardInput: %s\n" + "%sStandardOutput: %s\n" + "%sStandardError: %s\n", + prefix, exec_input_to_string(c->std_input), + prefix, exec_output_to_string(c->std_output), + prefix, exec_output_to_string(c->std_error)); + + if (c->tty_path) + fprintf(f, + "%sTTYPath: %s\n" + "%sTTYReset: %s\n" + "%sTTYVHangup: %s\n" + "%sTTYVTDisallocate: %s\n", + prefix, c->tty_path, + prefix, yes_no(c->tty_reset), + prefix, yes_no(c->tty_vhangup), + prefix, yes_no(c->tty_vt_disallocate)); + + if (c->std_output == EXEC_OUTPUT_SYSLOG || + c->std_output == EXEC_OUTPUT_KMSG || + c->std_output == EXEC_OUTPUT_JOURNAL || + c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || + c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || + c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE || + c->std_error == EXEC_OUTPUT_SYSLOG || + c->std_error == EXEC_OUTPUT_KMSG || + c->std_error == EXEC_OUTPUT_JOURNAL || + c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || + c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || + c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) { + + _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL; + + log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str); + log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str); + + fprintf(f, + "%sSyslogFacility: %s\n" + "%sSyslogLevel: %s\n", + prefix, strna(fac_str), + prefix, strna(lvl_str)); + } + + if (c->secure_bits) + fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", + prefix, + (c->secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<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 & (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); + } + + if (c->user) + fprintf(f, "%sUser: %s\n", prefix, c->user); + if (c->group) + fprintf(f, "%sGroup: %s\n", prefix, c->group); + + if (strv_length(c->supplementary_groups) > 0) { + fprintf(f, "%sSupplementaryGroups:", prefix); + strv_fprintf(f, c->supplementary_groups); + fputs("\n", f); + } + + if (c->pam_name) + fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); + + if (strv_length(c->read_write_paths) > 0) { + fprintf(f, "%sReadWritePaths:", prefix); + strv_fprintf(f, c->read_write_paths); + fputs("\n", f); + } + + if (strv_length(c->read_only_paths) > 0) { + fprintf(f, "%sReadOnlyPaths:", prefix); + strv_fprintf(f, c->read_only_paths); + fputs("\n", f); + } + + if (strv_length(c->inaccessible_paths) > 0) { + fprintf(f, "%sInaccessiblePaths:", prefix); + strv_fprintf(f, c->inaccessible_paths); + fputs("\n", f); + } + + if (c->utmp_id) + fprintf(f, + "%sUtmpIdentifier: %s\n", + prefix, c->utmp_id); + + if (c->selinux_context) + fprintf(f, + "%sSELinuxContext: %s%s\n", + prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context); + + if (c->personality != PERSONALITY_INVALID) + fprintf(f, + "%sPersonality: %s\n", + prefix, strna(personality_to_string(c->personality))); + + if (c->syscall_filter) { +#ifdef HAVE_SECCOMP + Iterator j; + void *id; + bool first = true; +#endif + + fprintf(f, + "%sSystemCallFilter: ", + prefix); + + if (!c->syscall_whitelist) + fputc('~', f); + +#ifdef HAVE_SECCOMP + SET_FOREACH(id, c->syscall_filter, j) { + _cleanup_free_ char *name = NULL; + + if (first) + first = false; + else + fputc(' ', f); + + name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); + fputs(strna(name), f); + } +#endif + + fputc('\n', f); + } + + if (c->syscall_archs) { +#ifdef HAVE_SECCOMP + Iterator j; + void *id; +#endif + + fprintf(f, + "%sSystemCallArchitectures:", + prefix); + +#ifdef HAVE_SECCOMP + SET_FOREACH(id, c->syscall_archs, j) + fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1))); +#endif + fputc('\n', f); + } + + if (c->syscall_errno > 0) + fprintf(f, + "%sSystemCallErrorNumber: %s\n", + prefix, strna(errno_to_name(c->syscall_errno))); + + if (c->apparmor_profile) + fprintf(f, + "%sAppArmorProfile: %s%s\n", + prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); +} + +bool exec_context_maintains_privileges(ExecContext *c) { + assert(c); + + /* Returns true if the process forked off would run under + * an unchanged UID or as root. */ + + if (!c->user) + return true; + + if (streq(c->user, "root") || streq(c->user, "0")) + return true; + + return false; +} + +void exec_status_start(ExecStatus *s, pid_t pid) { + assert(s); + + zero(*s); + s->pid = pid; + dual_timestamp_get(&s->start_timestamp); +} + +void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) { + assert(s); + + if (s->pid && s->pid != pid) + zero(*s); + + s->pid = pid; + dual_timestamp_get(&s->exit_timestamp); + + s->code = code; + s->status = status; + + if (context) { + if (context->utmp_id) + utmp_put_dead_process(context->utmp_id, pid, code, status); + + exec_context_tty_reset(context, NULL); + } +} + +void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { + char buf[FORMAT_TIMESTAMP_MAX]; + + assert(s); + assert(f); + + if (s->pid <= 0) + return; + + prefix = strempty(prefix); + + fprintf(f, + "%sPID: "PID_FMT"\n", + prefix, s->pid); + + if (s->start_timestamp.realtime > 0) + fprintf(f, + "%sStart Timestamp: %s\n", + prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime)); + + if (s->exit_timestamp.realtime > 0) + fprintf(f, + "%sExit Timestamp: %s\n" + "%sExit Code: %s\n" + "%sExit Status: %i\n", + prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime), + prefix, sigchld_code_to_string(s->code), + prefix, s->status); +} + +char *exec_command_line(char **argv) { + size_t k; + char *n, *p, **a; + bool first = true; + + assert(argv); + + k = 1; + STRV_FOREACH(a, argv) + k += strlen(*a)+3; + + if (!(n = new(char, k))) + return NULL; + + p = n; + STRV_FOREACH(a, argv) { + + if (!first) + *(p++) = ' '; + else + first = false; + + if (strpbrk(*a, WHITESPACE)) { + *(p++) = '\''; + p = stpcpy(p, *a); + *(p++) = '\''; + } else + p = stpcpy(p, *a); + + } + + *p = 0; + + /* FIXME: this doesn't really handle arguments that have + * spaces and ticks in them */ + + return n; +} + +void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) { + _cleanup_free_ char *cmd = NULL; + const char *prefix2; + + assert(c); + assert(f); + + prefix = strempty(prefix); + prefix2 = strjoina(prefix, "\t"); + + cmd = exec_command_line(c->argv); + fprintf(f, + "%sCommand Line: %s\n", + prefix, cmd ? cmd : strerror(ENOMEM)); + + exec_status_dump(&c->exec_status, f, prefix2); +} + +void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { + assert(f); + + prefix = strempty(prefix); + + LIST_FOREACH(command, c, c) + exec_command_dump(c, f, prefix); +} + +void exec_command_append_list(ExecCommand **l, ExecCommand *e) { + ExecCommand *end; + + assert(l); + assert(e); + + if (*l) { + /* It's kind of important, that we keep the order here */ + LIST_FIND_TAIL(command, *l, end); + LIST_INSERT_AFTER(command, *l, end, e); + } else + *l = e; +} + +int exec_command_set(ExecCommand *c, const char *path, ...) { + va_list ap; + char **l, *p; + + assert(c); + assert(path); + + va_start(ap, path); + l = strv_new_ap(path, ap); + va_end(ap); + + if (!l) + return -ENOMEM; + + p = strdup(path); + if (!p) { + strv_free(l); + return -ENOMEM; + } + + free(c->path); + c->path = p; + + strv_free(c->argv); + c->argv = l; + + return 0; +} + +int exec_command_append(ExecCommand *c, const char *path, ...) { + _cleanup_strv_free_ char **l = NULL; + va_list ap; + int r; + + assert(c); + assert(path); + + va_start(ap, path); + l = strv_new_ap(path, ap); + va_end(ap); + + if (!l) + return -ENOMEM; + + r = strv_extend_strv(&c->argv, l, false); + if (r < 0) + return r; + + return 0; +} + + +static int exec_runtime_allocate(ExecRuntime **rt) { + + if (*rt) + return 0; + + *rt = new0(ExecRuntime, 1); + if (!*rt) + return -ENOMEM; + + (*rt)->n_ref = 1; + (*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1; + + return 0; +} + +int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) { + int r; + + assert(rt); + assert(c); + assert(id); + + if (*rt) + return 1; + + if (!c->private_network && !c->private_tmp) + return 0; + + r = exec_runtime_allocate(rt); + if (r < 0) + return r; + + if (c->private_network && (*rt)->netns_storage_socket[0] < 0) { + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, (*rt)->netns_storage_socket) < 0) + return -errno; + } + + if (c->private_tmp && !(*rt)->tmp_dir) { + r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir); + if (r < 0) + return r; + } + + return 1; +} + +ExecRuntime *exec_runtime_ref(ExecRuntime *r) { + assert(r); + assert(r->n_ref > 0); + + r->n_ref++; + return r; +} + +ExecRuntime *exec_runtime_unref(ExecRuntime *r) { + + if (!r) + return NULL; + + assert(r->n_ref > 0); + + r->n_ref--; + if (r->n_ref > 0) + return NULL; + + free(r->tmp_dir); + free(r->var_tmp_dir); + safe_close_pair(r->netns_storage_socket); + free(r); + + return NULL; +} + +int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) { + assert(u); + assert(f); + assert(fds); + + if (!rt) + return 0; + + if (rt->tmp_dir) + unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir); + + if (rt->var_tmp_dir) + unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir); + + if (rt->netns_storage_socket[0] >= 0) { + int copy; + + copy = fdset_put_dup(fds, rt->netns_storage_socket[0]); + if (copy < 0) + return copy; + + unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy); + } + + if (rt->netns_storage_socket[1] >= 0) { + int copy; + + copy = fdset_put_dup(fds, rt->netns_storage_socket[1]); + if (copy < 0) + return copy; + + unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy); + } + + return 0; +} + +int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) { + int r; + + assert(rt); + assert(key); + assert(value); + + if (streq(key, "tmp-dir")) { + char *copy; + + r = exec_runtime_allocate(rt); + if (r < 0) + return log_oom(); + + copy = strdup(value); + if (!copy) + return log_oom(); + + free((*rt)->tmp_dir); + (*rt)->tmp_dir = copy; + + } else if (streq(key, "var-tmp-dir")) { + char *copy; + + r = exec_runtime_allocate(rt); + if (r < 0) + return log_oom(); + + copy = strdup(value); + if (!copy) + return log_oom(); + + free((*rt)->var_tmp_dir); + (*rt)->var_tmp_dir = copy; + + } else if (streq(key, "netns-socket-0")) { + int fd; + + r = exec_runtime_allocate(rt); + if (r < 0) + return log_oom(); + + if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse netns socket value: %s", value); + else { + safe_close((*rt)->netns_storage_socket[0]); + (*rt)->netns_storage_socket[0] = fdset_remove(fds, fd); + } + } else if (streq(key, "netns-socket-1")) { + int fd; + + r = exec_runtime_allocate(rt); + if (r < 0) + return log_oom(); + + if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse netns socket value: %s", value); + else { + safe_close((*rt)->netns_storage_socket[1]); + (*rt)->netns_storage_socket[1] = fdset_remove(fds, fd); + } + } else + return 0; + + return 1; +} + +static void *remove_tmpdir_thread(void *p) { + _cleanup_free_ char *path = p; + + (void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL); + return NULL; +} + +void exec_runtime_destroy(ExecRuntime *rt) { + int r; + + if (!rt) + return; + + /* If there are multiple users of this, let's leave the stuff around */ + if (rt->n_ref > 1) + return; + + if (rt->tmp_dir) { + log_debug("Spawning thread to nuke %s", rt->tmp_dir); + + r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir); + if (r < 0) { + log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir); + free(rt->tmp_dir); + } + + rt->tmp_dir = NULL; + } + + if (rt->var_tmp_dir) { + log_debug("Spawning thread to nuke %s", rt->var_tmp_dir); + + r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir); + if (r < 0) { + log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir); + free(rt->var_tmp_dir); + } + + rt->var_tmp_dir = NULL; + } + + safe_close_pair(rt->netns_storage_socket); +} + +static const char* const exec_input_table[_EXEC_INPUT_MAX] = { + [EXEC_INPUT_NULL] = "null", + [EXEC_INPUT_TTY] = "tty", + [EXEC_INPUT_TTY_FORCE] = "tty-force", + [EXEC_INPUT_TTY_FAIL] = "tty-fail", + [EXEC_INPUT_SOCKET] = "socket" +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); + +static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { + [EXEC_OUTPUT_INHERIT] = "inherit", + [EXEC_OUTPUT_NULL] = "null", + [EXEC_OUTPUT_TTY] = "tty", + [EXEC_OUTPUT_SYSLOG] = "syslog", + [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console", + [EXEC_OUTPUT_KMSG] = "kmsg", + [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console", + [EXEC_OUTPUT_JOURNAL] = "journal", + [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console", + [EXEC_OUTPUT_SOCKET] = "socket" +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/grp-system/libcore/execute.h b/src/grp-system/libcore/execute.h new file mode 100644 index 0000000000..cf5e7e4617 --- /dev/null +++ b/src/grp-system/libcore/execute.h @@ -0,0 +1,292 @@ +#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 . +***/ + +#include +#include +#include +#include + +#include "basic/cgroup-util.h" +#include "basic/list.h" +#include "basic/missing.h" +#include "shared/fdset.h" + +typedef struct ExecCommand ExecCommand; +typedef struct ExecContext ExecContext; +typedef struct ExecParameters ExecParameters; +typedef struct ExecRuntime ExecRuntime; +typedef struct ExecStatus ExecStatus; + +#include "namespace.h" + +typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, + EXEC_UTMP_LOGIN, + EXEC_UTMP_USER, + _EXEC_UTMP_MODE_MAX, + _EXEC_UTMP_MODE_INVALID = -1 +} ExecUtmpMode; + +typedef enum ExecInput { + EXEC_INPUT_NULL, + EXEC_INPUT_TTY, + EXEC_INPUT_TTY_FORCE, + EXEC_INPUT_TTY_FAIL, + EXEC_INPUT_SOCKET, + _EXEC_INPUT_MAX, + _EXEC_INPUT_INVALID = -1 +} ExecInput; + +typedef enum ExecOutput { + EXEC_OUTPUT_INHERIT, + EXEC_OUTPUT_NULL, + EXEC_OUTPUT_TTY, + EXEC_OUTPUT_SYSLOG, + EXEC_OUTPUT_SYSLOG_AND_CONSOLE, + EXEC_OUTPUT_KMSG, + EXEC_OUTPUT_KMSG_AND_CONSOLE, + EXEC_OUTPUT_JOURNAL, + EXEC_OUTPUT_JOURNAL_AND_CONSOLE, + EXEC_OUTPUT_SOCKET, + _EXEC_OUTPUT_MAX, + _EXEC_OUTPUT_INVALID = -1 +} ExecOutput; + +struct ExecStatus { + dual_timestamp start_timestamp; + dual_timestamp exit_timestamp; + pid_t pid; + int code; /* as in siginfo_t::si_code */ + int status; /* as in sigingo_t::si_status */ +}; + +struct ExecCommand { + char *path; + char **argv; + ExecStatus exec_status; + LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */ + bool ignore:1; + bool privileged:1; +}; + +struct ExecRuntime { + int n_ref; + + char *tmp_dir; + char *var_tmp_dir; + + int netns_storage_socket[2]; +}; + +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; + int nice; + int ioprio; + int cpu_sched_policy; + int cpu_sched_priority; + + cpu_set_t *cpuset; + unsigned cpuset_ncpus; + + ExecInput std_input; + ExecOutput std_output; + ExecOutput std_error; + + nsec_t timer_slack_nsec; + + bool stdio_as_fds; + + char *tty_path; + + bool tty_reset; + bool tty_vhangup; + bool tty_vt_disallocate; + + bool ignore_sigpipe; + + /* Since resolving these names might involve socket + * connections and we don't want to deadlock ourselves these + * names are resolved on execution only and in the child + * process. */ + char *user; + char *group; + char **supplementary_groups; + + char *pam_name; + + char *utmp_id; + ExecUtmpMode utmp_mode; + + bool selinux_context_ignore; + char *selinux_context; + + bool apparmor_profile_ignore; + char *apparmor_profile; + + bool smack_process_label_ignore; + char *smack_process_label; + + char **read_write_paths, **read_only_paths, **inaccessible_paths; + unsigned long mount_flags; + + uint64_t capability_bounding_set; + uint64_t capability_ambient_set; + int secure_bits; + + int syslog_priority; + char *syslog_identifier; + bool syslog_level_prefix; + + bool cpu_sched_reset_on_fork; + bool non_blocking; + bool private_tmp; + bool private_network; + bool private_devices; + ProtectSystem protect_system; + ProtectHome protect_home; + + bool no_new_privileges; + + /* This is not exposed to the user but available + * internally. We need it to make sure that whenever we spawn + * /usr/bin/mount it is run in the same process group as us so + * that the autofs logic detects that it belongs to us and we + * don't enter a trigger loop. */ + bool same_pgrp; + + unsigned long personality; + + Set *syscall_filter; + Set *syscall_archs; + int syscall_errno; + bool syscall_whitelist:1; + + Set *address_families; + bool address_families_whitelist:1; + + char **runtime_directory; + mode_t runtime_directory_mode; + + bool memory_deny_write_execute; + bool restrict_realtime; + + bool oom_score_adjust_set:1; + bool nice_set:1; + bool ioprio_set:1; + bool cpu_sched_set:1; + bool no_new_privileges_set:1; +}; + +struct ExecParameters { + char **argv; + char **environment; + + 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; + + const char *runtime_prefix; + + usec_t watchdog_usec; + + int *idle_pipe; + + int stdin_fd; + int stdout_fd; + int stderr_fd; +}; + +#include "unit.h" + +int exec_spawn(Unit *unit, + ExecCommand *command, + const ExecContext *context, + const ExecParameters *exec_params, + ExecRuntime *runtime, + pid_t *ret); + +void exec_command_done(ExecCommand *c); +void exec_command_done_array(ExecCommand *c, unsigned n); + +ExecCommand* exec_command_free_list(ExecCommand *c); +void exec_command_free_array(ExecCommand **c, unsigned n); + +char *exec_command_line(char **argv); + +void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix); +void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix); +void exec_command_append_list(ExecCommand **l, ExecCommand *e); +int exec_command_set(ExecCommand *c, const char *path, ...); +int exec_command_append(ExecCommand *c, const char *path, ...); + +void exec_context_init(ExecContext *c); +void exec_context_done(ExecContext *c); +void exec_context_dump(ExecContext *c, FILE* f, const char *prefix); + +int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root); + +int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l); + +bool exec_context_may_touch_console(ExecContext *c); +bool exec_context_maintains_privileges(ExecContext *c); + +void exec_status_start(ExecStatus *s, pid_t pid); +void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); +void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); + +int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id); +ExecRuntime *exec_runtime_ref(ExecRuntime *r); +ExecRuntime *exec_runtime_unref(ExecRuntime *r); + +int exec_runtime_serialize(Unit *unit, ExecRuntime *rt, FILE *f, FDSet *fds); +int exec_runtime_deserialize_item(Unit *unit, ExecRuntime **rt, const char *key, const char *value, FDSet *fds); + +void exec_runtime_destroy(ExecRuntime *rt); + +const char* exec_output_to_string(ExecOutput i) _const_; +ExecOutput exec_output_from_string(const char *s) _pure_; + +const char* exec_input_to_string(ExecInput i) _const_; +ExecInput exec_input_from_string(const char *s) _pure_; + +const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; +ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/failure-action.c b/src/grp-system/libcore/failure-action.c new file mode 100644 index 0000000000..e22c71dac6 --- /dev/null +++ b/src/grp-system/libcore/failure-action.c @@ -0,0 +1,129 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + Copyright 2012 Michael Olbrich + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/terminal-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "failure-action.h" + +static void log_and_status(Manager *m, const char *message) { + log_warning("%s", message); + manager_status_printf(m, STATUS_TYPE_EMERGENCY, + ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, + "%s", message); +} + +int failure_action( + Manager *m, + FailureAction action, + const char *reboot_arg) { + + assert(m); + assert(action >= 0); + assert(action < _FAILURE_ACTION_MAX); + + if (action == FAILURE_ACTION_NONE) + return -ECANCELED; + + if (!MANAGER_IS_SYSTEM(m)) { + /* Downgrade all options to simply exiting if we run + * in user mode */ + + log_warning("Exiting as result of failure."); + m->exit_code = MANAGER_EXIT; + return -ECANCELED; + } + + switch (action) { + + case FAILURE_ACTION_REBOOT: + log_and_status(m, "Rebooting as result of failure."); + + (void) update_reboot_parameter_and_warn(reboot_arg); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); + + break; + + case FAILURE_ACTION_REBOOT_FORCE: + log_and_status(m, "Forcibly rebooting as result of failure."); + + (void) update_reboot_parameter_and_warn(reboot_arg); + m->exit_code = MANAGER_REBOOT; + + break; + + case FAILURE_ACTION_REBOOT_IMMEDIATE: + log_and_status(m, "Rebooting immediately as result of failure."); + + sync(); + + if (!isempty(reboot_arg)) { + log_info("Rebooting with argument '%s'.", reboot_arg); + syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); + } + + log_info("Rebooting."); + reboot(RB_AUTOBOOT); + break; + + case FAILURE_ACTION_POWEROFF: + log_and_status(m, "Powering off as result of failure."); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); + break; + + case FAILURE_ACTION_POWEROFF_FORCE: + log_and_status(m, "Forcibly powering off as result of failure."); + m->exit_code = MANAGER_POWEROFF; + break; + + case FAILURE_ACTION_POWEROFF_IMMEDIATE: + log_and_status(m, "Powering off immediately as result of failure."); + + sync(); + + log_info("Powering off."); + reboot(RB_POWER_OFF); + break; + + default: + assert_not_reached("Unknown failure action"); + } + + return -ECANCELED; +} + +static const char* const failure_action_table[_FAILURE_ACTION_MAX] = { + [FAILURE_ACTION_NONE] = "none", + [FAILURE_ACTION_REBOOT] = "reboot", + [FAILURE_ACTION_REBOOT_FORCE] = "reboot-force", + [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", + [FAILURE_ACTION_POWEROFF] = "poweroff", + [FAILURE_ACTION_POWEROFF_FORCE] = "poweroff-force", + [FAILURE_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate" +}; +DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction); diff --git a/src/grp-system/libcore/failure-action.h b/src/grp-system/libcore/failure-action.h new file mode 100644 index 0000000000..87b091f46e --- /dev/null +++ b/src/grp-system/libcore/failure-action.h @@ -0,0 +1,42 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + Copyright 2012 Michael Olbrich + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef enum FailureAction { + FAILURE_ACTION_NONE, + FAILURE_ACTION_REBOOT, + FAILURE_ACTION_REBOOT_FORCE, + FAILURE_ACTION_REBOOT_IMMEDIATE, + FAILURE_ACTION_POWEROFF, + FAILURE_ACTION_POWEROFF_FORCE, + FAILURE_ACTION_POWEROFF_IMMEDIATE, + _FAILURE_ACTION_MAX, + _FAILURE_ACTION_INVALID = -1 +} FailureAction; + +#include "basic/macro.h" + +#include "manager.h" + +int failure_action(Manager *m, FailureAction action, const char *reboot_arg); + +const char* failure_action_to_string(FailureAction i) _const_; +FailureAction failure_action_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/hostname-setup.c b/src/grp-system/libcore/hostname-setup.c new file mode 100644 index 0000000000..71e5649952 --- /dev/null +++ b/src/grp-system/libcore/hostname-setup.c @@ -0,0 +1,69 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "hostname-setup.h" + +int hostname_setup(void) { + int r; + _cleanup_free_ char *b = NULL; + const char *hn; + bool enoent = false; + + r = read_hostname_config("/etc/hostname", &b); + if (r < 0) { + if (r == -ENOENT) + enoent = true; + else + log_warning_errno(r, "Failed to read configured hostname: %m"); + + hn = NULL; + } else + hn = b; + + if (isempty(hn)) { + /* Don't override the hostname if it is already set + * and not explicitly configured */ + if (hostname_is_set()) + return 0; + + if (enoent) + log_info("No hostname configured."); + + hn = "localhost"; + } + + 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/grp-system/libcore/hostname-setup.h b/src/grp-system/libcore/hostname-setup.h new file mode 100644 index 0000000000..73e8c75c71 --- /dev/null +++ b/src/grp-system/libcore/hostname-setup.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int hostname_setup(void); diff --git a/src/grp-system/libcore/ima-setup.c b/src/grp-system/libcore/ima-setup.c new file mode 100644 index 0000000000..0b283d95f1 --- /dev/null +++ b/src/grp-system/libcore/ima-setup.c @@ -0,0 +1,81 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy + TORSEC group — http://security.polito.it + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/util.h" + +#include "ima-setup.h" + +#define IMA_SECFS_DIR "/sys/kernel/security/ima" +#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" +#define IMA_POLICY_PATH "/etc/ima/ima-policy" + +int ima_setup(void) { +#ifdef HAVE_IMA + _cleanup_fclose_ FILE *input = NULL; + _cleanup_close_ int imafd = -1; + unsigned lineno = 0; + char line[page_size()]; + + if (access(IMA_SECFS_DIR, F_OK) < 0) { + log_debug("IMA support is disabled in the kernel, ignoring."); + return 0; + } + + input = fopen(IMA_POLICY_PATH, "re"); + if (!input) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m"); + return 0; + } + + if (access(IMA_SECFS_POLICY, F_OK) < 0) { + log_warning("Another IMA custom policy has already been loaded, ignoring."); + return 0; + } + + imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC); + if (imafd < 0) { + log_error_errno(errno, "Failed to open the IMA kernel interface "IMA_SECFS_POLICY", ignoring: %m"); + return 0; + } + + FOREACH_LINE(line, input, + return log_error_errno(errno, "Failed to read the IMA custom policy file "IMA_POLICY_PATH": %m")) { + size_t len; + + len = strlen(line); + lineno++; + + if (len > 0 && write(imafd, line, len) < 0) + return log_error_errno(errno, "Failed to load the IMA custom policy file "IMA_POLICY_PATH"%u: %m", + lineno); + } + + log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH"."); +#endif /* HAVE_IMA */ + return 0; +} diff --git a/src/grp-system/libcore/ima-setup.h b/src/grp-system/libcore/ima-setup.h new file mode 100644 index 0000000000..472b58cb00 --- /dev/null +++ b/src/grp-system/libcore/ima-setup.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy + TORSEC group — http://security.polito.it + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int ima_setup(void); diff --git a/src/grp-system/libcore/job.c b/src/grp-system/libcore/job.c new file mode 100644 index 0000000000..9dc44189e4 --- /dev/null +++ b/src/grp-system/libcore/job.c @@ -0,0 +1,1260 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/escape.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/set.h" +#include "basic/special.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/virt.h" + +#include "dbus-job.h" +#include "dbus.h" +#include "job.h" +#include "unit.h" + +Job* job_new_raw(Unit *unit) { + Job *j; + + /* used for deserialization */ + + assert(unit); + + j = new0(Job, 1); + if (!j) + return NULL; + + j->manager = unit->manager; + j->unit = unit; + j->type = _JOB_TYPE_INVALID; + + return j; +} + +Job* job_new(Unit *unit, JobType type) { + Job *j; + + assert(type < _JOB_TYPE_MAX); + + j = job_new_raw(unit); + if (!j) + return NULL; + + j->id = j->manager->current_job_id++; + j->type = type; + + /* We don't link it here, that's what job_dependency() is for */ + + return j; +} + +void job_free(Job *j) { + assert(j); + assert(!j->installed); + assert(!j->transaction_prev); + assert(!j->transaction_next); + assert(!j->subject_list); + assert(!j->object_list); + + if (j->in_run_queue) + LIST_REMOVE(run_queue, j->manager->run_queue, j); + + if (j->in_dbus_queue) + LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + + sd_event_source_unref(j->timer_event_source); + + sd_bus_track_unref(j->clients); + strv_free(j->deserialized_clients); + + free(j); +} + +static void job_set_state(Job *j, JobState state) { + assert(j); + assert(state >= 0); + assert(state < _JOB_STATE_MAX); + + if (j->state == state) + return; + + j->state = state; + + if (!j->installed) + return; + + if (j->state == JOB_RUNNING) + j->unit->manager->n_running_jobs++; + else { + assert(j->state == JOB_WAITING); + assert(j->unit->manager->n_running_jobs > 0); + + j->unit->manager->n_running_jobs--; + + if (j->unit->manager->n_running_jobs <= 0) + j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source); + } +} + +void job_uninstall(Job *j) { + Job **pj; + + assert(j->installed); + + job_set_state(j, JOB_WAITING); + + pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; + assert(*pj == j); + + /* Detach from next 'bigger' objects */ + + /* daemon-reload should be transparent to job observers */ + if (!MANAGER_IS_RELOADING(j->manager)) + bus_job_send_removed_signal(j); + + *pj = NULL; + + unit_add_to_gc_queue(j->unit); + + hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id)); + j->installed = false; +} + +static bool job_type_allows_late_merge(JobType t) { + /* Tells whether it is OK to merge a job of type 't' with an already + * running job. + * Reloads cannot be merged this way. Think of the sequence: + * 1. Reload of a daemon is in progress; the daemon has already loaded + * its config file, but hasn't completed the reload operation yet. + * 2. Edit foo's config file. + * 3. Trigger another reload to have the daemon use the new config. + * Should the second reload job be merged into the first one, the daemon + * would not know about the new config. + * JOB_RESTART jobs on the other hand can be merged, because they get + * patched into JOB_START after stopping the unit. So if we see a + * JOB_RESTART running, it means the unit hasn't stopped yet and at + * this time the merge is still allowed. */ + return t != JOB_RELOAD; +} + +static void job_merge_into_installed(Job *j, Job *other) { + assert(j->installed); + assert(j->unit == other->unit); + + if (j->type != JOB_NOP) + job_type_merge_and_collapse(&j->type, other->type, j->unit); + else + assert(other->type == JOB_NOP); + + j->irreversible = j->irreversible || other->irreversible; + j->ignore_order = j->ignore_order || other->ignore_order; +} + +Job* job_install(Job *j) { + Job **pj; + Job *uj; + + assert(!j->installed); + assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); + assert(j->state == JOB_WAITING); + + pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; + uj = *pj; + + if (uj) { + if (job_type_is_conflicting(uj->type, j->type)) + job_finish_and_invalidate(uj, JOB_CANCELED, false, false); + else { + /* not conflicting, i.e. mergeable */ + + if (uj->state == JOB_WAITING || + (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) { + job_merge_into_installed(uj, j); + log_unit_debug(uj->unit, + "Merged into installed job %s/%s as %u", + uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); + return uj; + } else { + /* already running and not safe to merge into */ + /* Patch uj to become a merged job and re-run it. */ + /* XXX It should be safer to queue j to run after uj finishes, but it is + * not currently possible to have more than one installed job per unit. */ + job_merge_into_installed(uj, j); + log_unit_debug(uj->unit, + "Merged into running job, re-running: %s/%s as %u", + uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); + + job_set_state(uj, JOB_WAITING); + return uj; + } + } + } + + /* Install the job */ + *pj = j; + j->installed = true; + + j->manager->n_installed_jobs++; + log_unit_debug(j->unit, + "Installed new job %s/%s as %u", + j->unit->id, job_type_to_string(j->type), (unsigned) j->id); + return j; +} + +int job_install_deserialized(Job *j) { + Job **pj; + + assert(!j->installed); + + if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) { + log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type))); + return -EINVAL; + } + + pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; + if (*pj) { + log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job."); + return -EEXIST; + } + + *pj = j; + j->installed = true; + + if (j->state == JOB_RUNNING) + j->unit->manager->n_running_jobs++; + + log_unit_debug(j->unit, + "Reinstalled deserialized job %s/%s as %u", + j->unit->id, job_type_to_string(j->type), (unsigned) j->id); + return 0; +} + +JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) { + JobDependency *l; + + assert(object); + + /* Adds a new job link, which encodes that the 'subject' job + * needs the 'object' job in some way. If 'subject' is NULL + * this means the 'anchor' job (i.e. the one the user + * explicitly asked for) is the requester. */ + + if (!(l = new0(JobDependency, 1))) + return NULL; + + l->subject = subject; + l->object = object; + l->matters = matters; + l->conflicts = conflicts; + + if (subject) + LIST_PREPEND(subject, subject->subject_list, l); + + LIST_PREPEND(object, object->object_list, l); + + return l; +} + +void job_dependency_free(JobDependency *l) { + assert(l); + + if (l->subject) + LIST_REMOVE(subject, l->subject->subject_list, l); + + LIST_REMOVE(object, l->object->object_list, l); + + free(l); +} + +void job_dump(Job *j, FILE*f, const char *prefix) { + assert(j); + assert(f); + + if (!prefix) + prefix = ""; + + fprintf(f, + "%s-> Job %u:\n" + "%s\tAction: %s -> %s\n" + "%s\tState: %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->irreversible)); +} + +/* + * Merging is commutative, so imagine the matrix as symmetric. We store only + * its lower triangle to avoid duplication. We don't store the main diagonal, + * because A merged with A is simply A. + * + * If the resulting type is collapsed immediately afterwards (to get rid of + * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain), + * the following properties hold: + * + * Merging is associative! A merged with B, and then merged with C is the same + * as A merged with the result of B merged with C. + * + * Mergeability is transitive! If A can be merged with B and B with C then + * A also with C. + * + * Also, if A merged with B cannot be merged with C, then either A or B cannot + * be merged with C either. + */ +static const JobType job_merging_table[] = { +/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD */ +/*********************************************************************************/ +/*JOB_START */ +/*JOB_VERIFY_ACTIVE */ JOB_START, +/*JOB_STOP */ -1, -1, +/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1, +/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, +}; + +JobType job_type_lookup_merge(JobType a, JobType b) { + assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2); + assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING); + assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING); + + if (a == b) + return a; + + if (a < b) { + JobType tmp = a; + a = b; + b = tmp; + } + + return job_merging_table[(a - 1) * a / 2 + b]; +} + +bool job_type_is_redundant(JobType a, UnitActiveState b) { + switch (a) { + + case JOB_START: + return + b == UNIT_ACTIVE || + b == UNIT_RELOADING; + + case JOB_STOP: + return + b == UNIT_INACTIVE || + b == UNIT_FAILED; + + case JOB_VERIFY_ACTIVE: + return + b == UNIT_ACTIVE || + b == UNIT_RELOADING; + + case JOB_RELOAD: + return + b == UNIT_RELOADING; + + case JOB_RESTART: + return + b == UNIT_ACTIVATING; + + case JOB_NOP: + return true; + + default: + assert_not_reached("Invalid job type"); + } +} + +JobType job_type_collapse(JobType t, Unit *u) { + UnitActiveState s; + + switch (t) { + + case JOB_TRY_RESTART: + s = unit_active_state(u); + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) + return JOB_NOP; + + return JOB_RESTART; + + case JOB_TRY_RELOAD: + s = unit_active_state(u); + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) + return JOB_NOP; + + return JOB_RELOAD; + + case JOB_RELOAD_OR_START: + s = unit_active_state(u); + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) + return JOB_START; + + return JOB_RELOAD; + + default: + return t; + } +} + +int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) { + JobType t; + + t = job_type_lookup_merge(*a, b); + if (t < 0) + return -EEXIST; + + *a = job_type_collapse(t, u); + return 0; +} + +static bool job_is_runnable(Job *j) { + Iterator i; + Unit *other; + + assert(j); + assert(j->installed); + + /* Checks whether there is any job running for the units this + * job needs to be running after (in the case of a 'positive' + * job type) or before (in the case of a 'negative' job + * type. */ + + /* Note that unit types have a say in what is runnable, + * too. For example, if they return -EAGAIN from + * unit_start() they can indicate they are not + * runnable yet. */ + + /* First check if there is an override */ + if (j->ignore_order) + return true; + + if (j->type == JOB_NOP) + return true; + + if (j->type == JOB_START || + j->type == JOB_VERIFY_ACTIVE || + j->type == JOB_RELOAD) { + + /* Immediate result is that the job is or might be + * started. In this case let's wait for the + * dependencies, regardless whether they are + * starting or stopping something. */ + + SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) + if (other->job) + return false; + } + + /* Also, if something else is being stopped and we should + * change state after it, then let's wait. */ + + SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) + if (other->job && + (other->job->type == JOB_STOP || + other->job->type == JOB_RESTART)) + return false; + + /* This means that for a service a and a service b where b + * shall be started after a: + * + * start a + start b → 1st step start a, 2nd step start b + * start a + stop b → 1st step stop b, 2nd step start a + * stop a + start b → 1st step stop a, 2nd step start b + * stop a + stop b → 1st step stop b, 2nd step stop a + * + * This has the side effect that restarts are properly + * synchronized too. */ + + return true; +} + +static void job_change_type(Job *j, JobType newtype) { + assert(j); + + log_unit_debug(j->unit, + "Converting job %s/%s -> %s/%s", + j->unit->id, job_type_to_string(j->type), + j->unit->id, job_type_to_string(newtype)); + + j->type = newtype; +} + +static int job_perform_on_unit(Job **j) { + 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); + break; + + case JOB_RESTART: + t = JOB_STOP; + /* fall through */ + case JOB_STOP: + r = unit_stop(u); + break; + + case JOB_RELOAD: + r = unit_reload(u); + break; + + default: + assert_not_reached("Invalid job type"); + } + + /* Log if the job still exists and the start/stop/reload function + * actually did something. */ + *j = manager_get_job(m, id); + if (*j && r > 0) + unit_status_emit_starting_stopping_reloading(u, t); + + return r; +} + +int job_run_and_invalidate(Job *j) { + int r; + + assert(j); + assert(j->installed); + assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); + assert(j->in_run_queue); + + LIST_REMOVE(run_queue, j->manager->run_queue, j); + j->in_run_queue = false; + + if (j->state != JOB_WAITING) + return 0; + + if (!job_is_runnable(j)) + return -EAGAIN; + + job_set_state(j, JOB_RUNNING); + job_add_to_dbus_queue(j); + + + switch (j->type) { + + case JOB_VERIFY_ACTIVE: { + UnitActiveState t = unit_active_state(j->unit); + if (UNIT_IS_ACTIVE_OR_RELOADING(t)) + r = -EALREADY; + else if (t == UNIT_ACTIVATING) + r = -EAGAIN; + else + r = -EBADR; + break; + } + + case JOB_START: + case JOB_STOP: + case JOB_RESTART: + r = job_perform_on_unit(&j); + + /* If the unit type does not support starting/stopping, + * then simply wait. */ + if (r == -EBADR) + r = 0; + break; + + case JOB_RELOAD: + r = job_perform_on_unit(&j); + break; + + case JOB_NOP: + r = -EALREADY; + break; + + default: + assert_not_reached("Unknown job type"); + } + + if (j) { + if (r == -EALREADY) + r = job_finish_and_invalidate(j, JOB_DONE, true, true); + else if (r == -EBADR) + r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false); + else if (r == -ENOEXEC) + r = job_finish_and_invalidate(j, JOB_INVALID, true, false); + else if (r == -EPROTO) + r = job_finish_and_invalidate(j, JOB_ASSERT, true, false); + else if (r == -EOPNOTSUPP) + r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); + else if (r == -EAGAIN) + job_set_state(j, JOB_WAITING); + else if (r < 0) + r = job_finish_and_invalidate(j, JOB_FAILED, true, false); + } + + return r; +} + +_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { + + static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = { + [JOB_DONE] = "Started %s.", + [JOB_TIMEOUT] = "Timed out starting %s.", + [JOB_FAILED] = "Failed to start %s.", + [JOB_DEPENDENCY] = "Dependency failed for %s.", + [JOB_ASSERT] = "Assertion failed for %s.", + [JOB_UNSUPPORTED] = "Starting of %s not supported.", + }; + static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { + [JOB_DONE] = "Stopped %s.", + [JOB_FAILED] = "Stopped (with error) %s.", + [JOB_TIMEOUT] = "Timed out stopping %s.", + }; + static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = { + [JOB_DONE] = "Reloaded %s.", + [JOB_FAILED] = "Reload failed for %s.", + [JOB_TIMEOUT] = "Timed out reloading %s.", + }; + /* When verify-active detects the unit is inactive, report it. + * Most likely a DEPEND warning from a requisiting unit will + * occur next and it's nice to see what was requisited. */ + static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = { + [JOB_SKIPPED] = "%s is not active.", + }; + + const UnitStatusMessageFormats *format_table; + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + 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] : + format_table->finished_stop_job[result]; + if (format) + return format; + } + } + + /* Return generic strings */ + if (t == JOB_START) + return generic_finished_start_job[result]; + else if (t == JOB_STOP || t == JOB_RESTART) + return generic_finished_stop_job[result]; + else if (t == JOB_RELOAD) + return generic_finished_reload_job[result]; + else if (t == JOB_VERIFY_ACTIVE) + return generic_finished_verify_active_job[result]; + + return NULL; +} + +static void job_print_status_message(Unit *u, JobType t, JobResult result) { + static struct { + const char *color, *word; + } const statuses[_JOB_RESULT_MAX] = { + [JOB_DONE] = {ANSI_GREEN, " OK "}, + [JOB_TIMEOUT] = {ANSI_HIGHLIGHT_RED, " TIME "}, + [JOB_FAILED] = {ANSI_HIGHLIGHT_RED, "FAILED"}, + [JOB_DEPENDENCY] = {ANSI_HIGHLIGHT_YELLOW, "DEPEND"}, + [JOB_SKIPPED] = {ANSI_HIGHLIGHT, " INFO "}, + [JOB_ASSERT] = {ANSI_HIGHLIGHT_YELLOW, "ASSERT"}, + [JOB_UNSUPPORTED] = {ANSI_HIGHLIGHT_YELLOW, "UNSUPP"}, + }; + + const char *format; + const char *status; + + 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; + + if (log_get_show_color()) + status = strjoina(statuses[result].color, statuses[result].word, ANSI_NORMAL); + else + status = statuses[result].word; + + if (result != JOB_DONE) + manager_flip_auto_status(u->manager, true); + + DISABLE_WARNING_FORMAT_NONLITERAL; + unit_status_printf(u, status, format); + REENABLE_WARNING; + + if (t == JOB_START && result == JOB_FAILED) { + _cleanup_free_ char *quoted; + + quoted = shell_maybe_quote(u->id); + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted)); + } +} + +static void job_log_status_message(Unit *u, JobType t, JobResult result) { + const char *format; + char buf[LINE_MAX]; + sd_id128_t mid; + static const int job_result_log_level[_JOB_RESULT_MAX] = { + [JOB_DONE] = LOG_INFO, + [JOB_CANCELED] = LOG_INFO, + [JOB_TIMEOUT] = LOG_ERR, + [JOB_FAILED] = LOG_ERR, + [JOB_DEPENDENCY] = LOG_WARNING, + [JOB_SKIPPED] = LOG_NOTICE, + [JOB_INVALID] = LOG_INFO, + [JOB_ASSERT] = LOG_WARNING, + [JOB_UNSUPPORTED] = LOG_WARNING, + }; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + /* Skip this if it goes to the console. since we already print + * to the console anyway... */ + + if (log_on_console()) + return; + + format = job_get_status_message_format(u, t, result); + if (!format) + return; + + DISABLE_WARNING_FORMAT_NONLITERAL; + xsprintf(buf, format, unit_description(u)); + REENABLE_WARNING; + + switch (t) { + + case JOB_START: + mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; + break; + + case JOB_RELOAD: + mid = SD_MESSAGE_UNIT_RELOADED; + 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), + "RESULT=%s", job_result_to_string(result), + NULL); + return; + } + + log_struct(job_result_log_level[result], + LOG_MESSAGE_ID(mid), + LOG_UNIT_ID(u), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); +} + +static void job_emit_status_message(Unit *u, JobType t, JobResult result) { + + /* No message if the job did not actually do anything due to failed condition. */ + if (t == JOB_START && result == JOB_DONE && !u->condition_result) + return; + + job_log_status_message(u, t, result); + job_print_status_message(u, t, result); +} + +static void job_fail_dependencies(Unit *u, UnitDependency d) { + Unit *other; + Iterator i; + + assert(u); + + SET_FOREACH(other, u->dependencies[d], i) { + Job *j = other->job; + + if (!j) + continue; + if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE)) + continue; + + job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); + } +} + +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { + Unit *u; + Unit *other; + JobType t; + Iterator i; + + assert(j); + assert(j->installed); + assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); + + u = j->unit; + t = j->type; + + j->result = result; + + log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); + + /* If this job did nothing to respective unit we don't log the status message */ + if (!already) + job_emit_status_message(u, t, result); + + job_add_to_dbus_queue(j); + + /* Patch restart jobs so that they become normal start jobs */ + if (result == JOB_DONE && t == JOB_RESTART) { + + job_change_type(j, JOB_START); + job_set_state(j, JOB_WAITING); + + job_add_to_run_queue(j); + + goto finish; + } + + if (result == JOB_FAILED || result == JOB_INVALID) + j->manager->n_failed_jobs++; + + job_uninstall(j); + job_free(j); + + /* Fail depending jobs on failure */ + if (result != JOB_DONE && recursive) { + if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) { + job_fail_dependencies(u, UNIT_REQUIRED_BY); + job_fail_dependencies(u, UNIT_REQUISITE_OF); + job_fail_dependencies(u, UNIT_BOUND_BY); + } else if (t == JOB_STOP) + job_fail_dependencies(u, UNIT_CONFLICTED_BY); + } + + /* Trigger OnFailure dependencies that are not generated by + * the unit itself. We don't treat JOB_CANCELED as failure in + * this context. And JOB_FAILURE is already handled by the + * unit itself. */ + if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { + log_struct(LOG_NOTICE, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_UNIT_ID(u), + LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.", + u->id, + job_type_to_string(t), + job_result_to_string(result)), + NULL); + + unit_start_on_failure(u); + } + + unit_trigger_notify(u); + +finish: + /* Try to start the next jobs that can be started */ + SET_FOREACH(other, u->dependencies[UNIT_AFTER], i) + if (other->job) + job_add_to_run_queue(other->job); + SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i) + if (other->job) + job_add_to_run_queue(other->job); + + manager_check_finished(u->manager); + + return 0; +} + +static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) { + Job *j = userdata; + Unit *u; + + assert(j); + assert(s == j->timer_event_source); + + log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); + + u = j->unit; + job_finish_and_invalidate(j, JOB_TIMEOUT, true, false); + + failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); + + return 0; +} + +int job_start_timer(Job *j) { + int r; + + if (j->timer_event_source) + return 0; + + j->begin_usec = now(CLOCK_MONOTONIC); + + if (j->unit->job_timeout == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + j->manager->event, + &j->timer_event_source, + CLOCK_MONOTONIC, + usec_add(j->begin_usec, j->unit->job_timeout), 0, + job_dispatch_timer, j); + if (r < 0) + return r; + + (void) sd_event_source_set_description(j->timer_event_source, "job-start"); + + return 0; +} + +void job_add_to_run_queue(Job *j) { + assert(j); + assert(j->installed); + + if (j->in_run_queue) + return; + + if (!j->manager->run_queue) + sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT); + + LIST_PREPEND(run_queue, j->manager->run_queue, j); + j->in_run_queue = true; +} + +void job_add_to_dbus_queue(Job *j) { + assert(j); + assert(j->installed); + + if (j->in_dbus_queue) + return; + + /* We don't check if anybody is subscribed here, since this + * job might just have been created and not yet assigned to a + * connection/client. */ + + LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j); + j->in_dbus_queue = true; +} + +char *job_dbus_path(Job *j) { + char *p; + + assert(j); + + if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0) + return NULL; + + return p; +} + +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-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)); + + if (j->begin_usec > 0) + fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec); + + bus_track_serialize(j->clients, f); + + /* End marker */ + fputc('\n', f); + return 0; +} + +int job_deserialize(Job *j, FILE *f, FDSet *fds) { + assert(j); + + for (;;) { + char line[LINE_MAX], *l, *v; + size_t k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + return 0; + return -errno; + } + + char_array_0(line); + l = strstrip(line); + + /* End marker */ + if (l[0] == 0) + return 0; + + k = strcspn(l, "="); + + if (l[k] == '=') { + l[k] = 0; + v = l+k+1; + } else + v = l+k; + + if (streq(l, "job-id")) { + + if (safe_atou32(v, &j->id) < 0) + log_debug("Failed to parse job id value %s", v); + + } else if (streq(l, "job-type")) { + JobType t; + + t = job_type_from_string(v); + if (t < 0) + log_debug("Failed to parse job type %s", v); + else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION) + log_debug("Cannot deserialize job of type %s", v); + else + j->type = t; + + } else if (streq(l, "job-state")) { + JobState s; + + s = job_state_from_string(v); + if (s < 0) + log_debug("Failed to parse job state %s", v); + else + job_set_state(j, s); + + } else if (streq(l, "job-irreversible")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse job irreversible flag %s", v); + else + j->irreversible = j->irreversible || b; + + } else if (streq(l, "job-sent-dbus-new-signal")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse job sent_dbus_new_signal flag %s", v); + else + j->sent_dbus_new_signal = j->sent_dbus_new_signal || b; + + } else if (streq(l, "job-ignore-order")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse job ignore_order flag %s", v); + else + j->ignore_order = j->ignore_order || b; + + } else if (streq(l, "job-begin")) { + unsigned long long ull; + + if (sscanf(v, "%llu", &ull) != 1) + log_debug("Failed to parse job-begin value %s", v); + else + j->begin_usec = ull; + + } else if (streq(l, "subscribed")) { + + if (strv_extend(&j->deserialized_clients, v) < 0) + return log_oom(); + } + } +} + +int job_coldplug(Job *j) { + int r; + + assert(j); + + /* After deserialization is complete and the bus connection + * set up again, let's start watching our subscribers again */ + r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients); + if (r < 0) + return r; + + if (j->state == JOB_WAITING) + job_add_to_run_queue(j); + + if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY) + return 0; + + j->timer_event_source = sd_event_source_unref(j->timer_event_source); + + r = sd_event_add_time( + j->manager->event, + &j->timer_event_source, + CLOCK_MONOTONIC, + usec_add(j->begin_usec, j->unit->job_timeout), 0, + job_dispatch_timer, j); + if (r < 0) + log_debug_errno(r, "Failed to restart timeout for job: %m"); + + (void) sd_event_source_set_description(j->timer_event_source, "job-timeout"); + + return r; +} + +void job_shutdown_magic(Job *j) { + assert(j); + + /* The shutdown target gets some special treatment here: we + * tell the kernel to begin with flushing its disk caches, to + * optimize shutdown time a bit. Ideally we wouldn't hardcode + * this magic into PID 1. However all other processes aren't + * options either since they'd exit much sooner than PID 1 and + * asynchronous sync() would cause their exit to be + * delayed. */ + + if (j->type != JOB_START) + return; + + if (!MANAGER_IS_SYSTEM(j->unit->manager)) + return; + + if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET)) + return; + + /* In case messages on console has been disabled on boot */ + j->unit->manager->no_console_output = false; + + if (detect_container() > 0) + return; + + asynchronous_sync(); +} + +int job_get_timeout(Job *j, usec_t *timeout) { + usec_t x = USEC_INFINITY, y = USEC_INFINITY; + Unit *u = j->unit; + int r; + + assert(u); + + if (j->timer_event_source) { + r = sd_event_source_get_time(j->timer_event_source, &x); + if (r < 0) + return r; + } + + if (UNIT_VTABLE(u)->get_timeout) { + r = UNIT_VTABLE(u)->get_timeout(u, &y); + if (r < 0) + return r; + } + + if (x == USEC_INFINITY && y == USEC_INFINITY) + return 0; + + *timeout = MIN(x, y); + return 1; +} + +static const char* const job_state_table[_JOB_STATE_MAX] = { + [JOB_WAITING] = "waiting", + [JOB_RUNNING] = "running" +}; + +DEFINE_STRING_TABLE_LOOKUP(job_state, JobState); + +static const char* const job_type_table[_JOB_TYPE_MAX] = { + [JOB_START] = "start", + [JOB_VERIFY_ACTIVE] = "verify-active", + [JOB_STOP] = "stop", + [JOB_RELOAD] = "reload", + [JOB_RELOAD_OR_START] = "reload-or-start", + [JOB_RESTART] = "restart", + [JOB_TRY_RESTART] = "try-restart", + [JOB_TRY_RELOAD] = "try-reload", + [JOB_NOP] = "nop", +}; + +DEFINE_STRING_TABLE_LOOKUP(job_type, JobType); + +static const char* const job_mode_table[_JOB_MODE_MAX] = { + [JOB_FAIL] = "fail", + [JOB_REPLACE] = "replace", + [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly", + [JOB_ISOLATE] = "isolate", + [JOB_FLUSH] = "flush", + [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies", + [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements", +}; + +DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode); + +static const char* const job_result_table[_JOB_RESULT_MAX] = { + [JOB_DONE] = "done", + [JOB_CANCELED] = "canceled", + [JOB_TIMEOUT] = "timeout", + [JOB_FAILED] = "failed", + [JOB_DEPENDENCY] = "dependency", + [JOB_SKIPPED] = "skipped", + [JOB_INVALID] = "invalid", + [JOB_ASSERT] = "assert", + [JOB_UNSUPPORTED] = "unsupported", +}; + +DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); + +const char* job_type_to_access_method(JobType t) { + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART)) + return "start"; + else if (t == JOB_STOP) + return "stop"; + else + return "reload"; +} diff --git a/src/grp-system/libcore/job.h b/src/grp-system/libcore/job.h new file mode 100644 index 0000000000..f88e5a0782 --- /dev/null +++ b/src/grp-system/libcore/job.h @@ -0,0 +1,242 @@ +#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 . +***/ + +#include + +#include + +#include "basic/list.h" +#include "basic/unit-name.h" + +typedef struct Job Job; +typedef struct JobDependency JobDependency; +typedef enum JobType JobType; +typedef enum JobState JobState; +typedef enum JobMode JobMode; +typedef enum JobResult JobResult; + +/* Be careful when changing the job types! Adjust job_merging_table[] accordingly! */ +enum JobType { + JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */ + JOB_VERIFY_ACTIVE, + + JOB_STOP, + + JOB_RELOAD, /* if running, reload */ + + /* Note that restarts are first treated like JOB_STOP, but + * then instead of finishing are patched to become + * JOB_START. */ + JOB_RESTART, /* If running, stop. Then start unconditionally. */ + + _JOB_TYPE_MAX_MERGING, + + /* JOB_NOP can enter into a transaction, but as it won't pull in + * any dependencies and it uses the special 'nop_job' slot in Unit, + * it won't have to merge with anything (except possibly into another + * JOB_NOP, previously installed). JOB_NOP is special-cased in + * job_type_is_*() functions so that the transaction can be + * activated. */ + JOB_NOP = _JOB_TYPE_MAX_MERGING, /* do nothing */ + + _JOB_TYPE_MAX_IN_TRANSACTION, + + /* JOB_TRY_RESTART can never appear in a transaction, because + * it always collapses into JOB_RESTART or JOB_NOP before entering. + * Thus we never need to merge it with anything. */ + JOB_TRY_RESTART = _JOB_TYPE_MAX_IN_TRANSACTION, /* if running, stop and then start */ + + /* Similar to JOB_TRY_RESTART but collapses to JOB_RELOAD or JOB_NOP */ + JOB_TRY_RELOAD, + + /* JOB_RELOAD_OR_START won't enter into a transaction and cannot result + * from transaction merging (there's no way for JOB_RELOAD and + * JOB_START to meet in one transaction). It can result from a merge + * during job installation, but then it will immediately collapse into + * one of the two simpler types. */ + JOB_RELOAD_OR_START, /* if running, reload, otherwise start */ + + _JOB_TYPE_MAX, + _JOB_TYPE_INVALID = -1 +}; + +enum JobState { + JOB_WAITING, + JOB_RUNNING, + _JOB_STATE_MAX, + _JOB_STATE_INVALID = -1 +}; + +enum JobMode { + JOB_FAIL, /* Fail if a conflicting job is already queued */ + JOB_REPLACE, /* Replace an existing conflicting job */ + JOB_REPLACE_IRREVERSIBLY,/* Like JOB_REPLACE + produce irreversible jobs */ + JOB_ISOLATE, /* Start a unit, and stop all others */ + JOB_FLUSH, /* Flush out all other queued jobs when queing this one */ + JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */ + JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */ + _JOB_MODE_MAX, + _JOB_MODE_INVALID = -1 +}; + +enum JobResult { + JOB_DONE, /* Job completed successfully */ + JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */ + JOB_TIMEOUT, /* Job timeout elapsed */ + JOB_FAILED, /* Job failed */ + JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */ + JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */ + JOB_INVALID, /* JOB_RELOAD of inactive unit */ + JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ + JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */ + _JOB_RESULT_MAX, + _JOB_RESULT_INVALID = -1 +}; + +#include "unit.h" + +struct JobDependency { + /* Encodes that the 'subject' job needs the 'object' job in + * some way. This structure is used only while building a transaction. */ + Job *subject; + Job *object; + + LIST_FIELDS(JobDependency, subject); + LIST_FIELDS(JobDependency, object); + + bool matters; + bool conflicts; +}; + +struct Job { + Manager *manager; + Unit *unit; + + LIST_FIELDS(Job, transaction); + LIST_FIELDS(Job, run_queue); + LIST_FIELDS(Job, dbus_queue); + + LIST_HEAD(JobDependency, subject_list); + LIST_HEAD(JobDependency, object_list); + + /* Used for graph algs as a "I have been here" marker */ + Job* marker; + unsigned generation; + + uint32_t id; + + JobType type; + JobState state; + + sd_event_source *timer_event_source; + usec_t begin_usec; + + /* + * This tracks where to send signals, and also which clients + * are allowed to call DBus methods on the job (other than + * root). + * + * There can be more than one client, because of job merging. + */ + sd_bus_track *clients; + char **deserialized_clients; + + JobResult result; + + bool installed:1; + bool in_run_queue:1; + bool matters_to_anchor:1; + bool in_dbus_queue:1; + bool sent_dbus_new_signal:1; + bool ignore_order:1; + bool irreversible:1; +}; + +Job* job_new(Unit *unit, JobType type); +Job* job_new_raw(Unit *unit); +void job_free(Job *job); +Job* job_install(Job *j); +int job_install_deserialized(Job *j); +void job_uninstall(Job *j); +void job_dump(Job *j, FILE*f, const char *prefix); +int job_serialize(Job *j, FILE *f, FDSet *fds); +int job_deserialize(Job *j, FILE *f, FDSet *fds); +int job_coldplug(Job *j); + +JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts); +void job_dependency_free(JobDependency *l); + +int job_merge(Job *j, Job *other); + +JobType job_type_lookup_merge(JobType a, JobType b) _pure_; + +_pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) { + return job_type_lookup_merge(a, b) >= 0; +} + +_pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) { + return a != JOB_NOP && b != JOB_NOP && !job_type_is_mergeable(a, b); +} + +_pure_ static inline bool job_type_is_superset(JobType a, JobType b) { + /* Checks whether operation a is a "superset" of b in its actions */ + if (b == JOB_NOP) + return true; + if (a == JOB_NOP) + return false; + return a == job_type_lookup_merge(a, b); +} + +bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_; + +/* Collapses a state-dependent job type into a simpler type by observing + * the state of the unit which it is going to be applied to. */ +JobType job_type_collapse(JobType t, Unit *u); + +int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u); + +void job_add_to_run_queue(Job *j); +void job_add_to_dbus_queue(Job *j); + +int job_start_timer(Job *j); + +int job_run_and_invalidate(Job *j); +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already); + +char *job_dbus_path(Job *j); + +void job_shutdown_magic(Job *j); + +int job_get_timeout(Job *j, usec_t *timeout) _pure_; + +const char* job_type_to_string(JobType t) _const_; +JobType job_type_from_string(const char *s) _pure_; + +const char* job_state_to_string(JobState t) _const_; +JobState job_state_from_string(const char *s) _pure_; + +const char* job_mode_to_string(JobMode t) _const_; +JobMode job_mode_from_string(const char *s) _pure_; + +const char* job_result_to_string(JobResult t) _const_; +JobResult job_result_from_string(const char *s) _pure_; + +const char* job_type_to_access_method(JobType t); diff --git a/src/grp-system/libcore/kill.c b/src/grp-system/libcore/kill.c new file mode 100644 index 0000000000..9f8b45971f --- /dev/null +++ b/src/grp-system/libcore/kill.c @@ -0,0 +1,69 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/signal-util.h" +#include "basic/string-table.h" +#include "basic/util.h" + +#include "kill.h" + +void kill_context_init(KillContext *c) { + assert(c); + + c->kill_signal = SIGTERM; + c->send_sigkill = true; + c->send_sighup = false; +} + +void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { + assert(c); + + if (!prefix) + prefix = ""; + + fprintf(f, + "%sKillMode: %s\n" + "%sKillSignal: SIG%s\n" + "%sSendSIGKILL: %s\n" + "%sSendSIGHUP: %s\n", + prefix, kill_mode_to_string(c->kill_mode), + prefix, signal_to_string(c->kill_signal), + prefix, yes_no(c->send_sigkill), + prefix, yes_no(c->send_sighup)); +} + +static const char* const kill_mode_table[_KILL_MODE_MAX] = { + [KILL_CONTROL_GROUP] = "control-group", + [KILL_PROCESS] = "process", + [KILL_MIXED] = "mixed", + [KILL_NONE] = "none" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_MAIN] = "main", + [KILL_CONTROL] = "control", + [KILL_ALL] = "all", + [KILL_MAIN_FAIL] = "main-fail", + [KILL_CONTROL_FAIL] = "control-fail", + [KILL_ALL_FAIL] = "all-fail" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/grp-system/libcore/kill.h b/src/grp-system/libcore/kill.h new file mode 100644 index 0000000000..481ffdf2ff --- /dev/null +++ b/src/grp-system/libcore/kill.h @@ -0,0 +1,65 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/macro.h" + +typedef struct KillContext KillContext; + +typedef enum KillMode { + /* The kill mode is a property of a unit. */ + KILL_CONTROL_GROUP = 0, + KILL_PROCESS, + KILL_MIXED, + KILL_NONE, + _KILL_MODE_MAX, + _KILL_MODE_INVALID = -1 +} KillMode; + +struct KillContext { + KillMode kill_mode; + int kill_signal; + bool send_sigkill; + bool send_sighup; +}; + +typedef enum KillWho { + /* Kill who is a property of an operation */ + KILL_MAIN, + KILL_CONTROL, + KILL_ALL, + KILL_MAIN_FAIL, + KILL_CONTROL_FAIL, + KILL_ALL_FAIL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +} KillWho; + +void kill_context_init(KillContext *c); +void kill_context_dump(KillContext *c, FILE *f, const char *prefix); + +const char *kill_mode_to_string(KillMode k) _const_; +KillMode kill_mode_from_string(const char *s) _pure_; + +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/killall.c b/src/grp-system/libcore/killall.c new file mode 100644 index 0000000000..27946f1f84 --- /dev/null +++ b/src/grp-system/libcore/killall.c @@ -0,0 +1,249 @@ +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "killall.h" + +static bool ignore_proc(pid_t pid, bool warn_rootfs) { + _cleanup_fclose_ FILE *f = NULL; + char c; + const char *p; + size_t count; + uid_t uid; + int r; + + /* We are PID 1, let's not commit suicide */ + if (pid == 1) + return true; + + r = get_process_uid(pid, &uid); + if (r < 0) + return true; /* not really, but better safe than sorry */ + + /* Non-root processes otherwise are always subject to be killed */ + if (uid != 0) + return false; + + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); + if (!f) + return true; /* not really, but has the desired effect */ + + count = fread(&c, 1, 1, f); + + /* Kernel threads have an empty cmdline */ + if (count <= 0) + return true; + + /* Processes with argv[0][0] = '@' we ignore from the killing + * spree. + * + * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ + if (c == '@' && warn_rootfs) { + _cleanup_free_ char *comm = NULL; + + r = pid_from_same_root_fs(pid); + if (r < 0) + return true; + + get_process_comm(pid, &comm); + + if (r) + log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is " + "running from the root file system, and thus likely to block re-mounting of the " + "root file system to read-only. Please consider moving it into an initrd file " + "system instead.", pid, strna(comm)); + return true; + } else if (c == '@') + return true; + + return false; +} + +static void wait_for_children(Set *pids, sigset_t *mask) { + usec_t until; + + assert(mask); + + if (set_isempty(pids)) + return; + + until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; + for (;;) { + struct timespec ts; + int k; + usec_t n; + void *p; + Iterator i; + + /* First, let the kernel inform us about killed + * children. Most processes will probably be our + * children, but some are not (might be our + * grandchildren instead...). */ + for (;;) { + pid_t pid; + + pid = waitpid(-1, NULL, WNOHANG); + if (pid == 0) + break; + if (pid < 0) { + if (errno == ECHILD) + break; + + log_error_errno(errno, "waitpid() failed: %m"); + return; + } + + (void) set_remove(pids, PID_TO_PTR(pid)); + } + + /* Now explicitly check who might be remaining, who + * might not be our child. */ + SET_FOREACH(p, pids, i) { + + /* We misuse getpgid as a check whether a + * process still exists. */ + if (getpgid(PTR_TO_PID(p)) >= 0) + continue; + + if (errno != ESRCH) + continue; + + set_remove(pids, p); + } + + if (set_isempty(pids)) + return; + + n = now(CLOCK_MONOTONIC); + if (n >= until) + return; + + timespec_store(&ts, until - n); + k = sigtimedwait(mask, NULL, &ts); + if (k != SIGCHLD) { + + if (k < 0 && errno != EAGAIN) { + log_error_errno(errno, "sigtimedwait() failed: %m"); + return; + } + + if (k >= 0) + log_warning("sigtimedwait() returned unexpected signal."); + } + } +} + +static int killall(int sig, Set *pids, bool send_sighup) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *d; + + dir = opendir("/proc"); + if (!dir) + return -errno; + + while ((d = readdir(dir))) { + pid_t pid; + int r; + + if (d->d_type != DT_DIR && + d->d_type != DT_UNKNOWN) + continue; + + if (parse_pid(d->d_name, &pid) < 0) + continue; + + if (ignore_proc(pid, sig == SIGKILL && !in_initrd())) + continue; + + if (sig == SIGKILL) { + _cleanup_free_ char *s = NULL; + + get_process_comm(pid, &s); + log_notice("Sending SIGKILL to PID "PID_FMT" (%s).", pid, strna(s)); + } + + if (kill(pid, sig) >= 0) { + if (pids) { + r = set_put(pids, PID_TO_PTR(pid)); + if (r < 0) + log_oom(); + } + } else if (errno != ENOENT) + log_warning_errno(errno, "Could not kill %d: %m", pid); + + if (send_sighup) { + /* Optionally, also send a SIGHUP signal, but + only if the process has a controlling + tty. This is useful to allow handling of + shells which ignore SIGTERM but react to + SIGHUP. We do not send this to processes that + have no controlling TTY since we don't want to + trigger reloads of daemon processes. Also we + make sure to only send this after SIGTERM so + that SIGTERM is always first in the queue. */ + + + if (get_ctty_devnr(pid, NULL) >= 0) + kill(pid, SIGHUP); + } + } + + return set_size(pids); +} + +void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { + sigset_t mask, oldmask; + _cleanup_set_free_ Set *pids = NULL; + + if (wait_for_exit) + pids = set_new(NULL); + + assert_se(sigemptyset(&mask) == 0); + assert_se(sigaddset(&mask, SIGCHLD) == 0); + assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0); + + if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) + log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m"); + + killall(sig, pids, send_sighup); + + if (kill(-1, SIGCONT) < 0 && errno != ESRCH) + log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m"); + + if (wait_for_exit) + wait_for_children(pids, &mask); + + assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); +} diff --git a/src/grp-system/libcore/killall.h b/src/grp-system/libcore/killall.h new file mode 100644 index 0000000000..acc2439f00 --- /dev/null +++ b/src/grp-system/libcore/killall.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup); diff --git a/src/grp-system/libcore/kmod-setup.c b/src/grp-system/libcore/kmod-setup.c new file mode 100644 index 0000000000..c91c280e7d --- /dev/null +++ b/src/grp-system/libcore/kmod-setup.c @@ -0,0 +1,129 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#ifdef HAVE_KMOD +#include +#endif + +#include "basic/capability-util.h" +#include "basic/macro.h" +#include "shared/bus-util.h" + +#include "kmod-setup.h" + +#ifdef HAVE_KMOD +static void systemd_kmod_log( + void *data, + int priority, + const char *file, int line, + const char *fn, + const char *format, + va_list args) { + + /* library logging is enabled at debug only */ + DISABLE_WARNING_FORMAT_NONLITERAL; + log_internalv(LOG_DEBUG, 0, file, line, fn, format, args); + REENABLE_WARNING; +} +#endif + +int kmod_setup(void) { +#ifdef HAVE_KMOD + + static const struct { + const char *module; + const char *path; + bool warn_if_unavailable:1; + bool warn_if_module:1; + bool (*condition_fn)(void); + } kmod_table[] = { + /* auto-loading on use doesn't work before udev is up */ + { "autofs4", "/sys/class/misc/autofs", true, false, NULL }, + + /* early configure of ::1 on the loopback device */ + { "ipv6", "/sys/module/ipv6", false, true, NULL }, + + /* this should never be a module */ + { "unix", "/proc/net/unix", true, true, NULL }, + +#ifdef HAVE_LIBIPTC + /* netfilter is needed by networkd, nspawn among others, and cannot be autoloaded */ + { "ip_tables", "/proc/net/ip_tables_names", false, false, NULL }, +#endif + }; + struct kmod_ctx *ctx = NULL; + unsigned int i; + int r; + + if (have_effective_cap(CAP_SYS_MODULE) == 0) + return 0; + + for (i = 0; i < ELEMENTSOF(kmod_table); i++) { + struct kmod_module *mod; + + if (kmod_table[i].path && access(kmod_table[i].path, F_OK) >= 0) + continue; + + if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn()) + continue; + + if (kmod_table[i].warn_if_module) + log_debug("Your kernel apparently lacks built-in %s support. Might be " + "a good idea to compile it in. We'll now try to work around " + "this by loading the module...", kmod_table[i].module); + + if (!ctx) { + ctx = kmod_new(NULL, NULL); + if (!ctx) + return log_oom(); + + kmod_set_log_fn(ctx, systemd_kmod_log, NULL); + kmod_load_resources(ctx); + } + + r = kmod_module_new_from_name(ctx, kmod_table[i].module, &mod); + if (r < 0) { + log_error("Failed to lookup module '%s'", kmod_table[i].module); + continue; + } + + r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); + if (r == 0) + log_debug("Inserted module '%s'", kmod_module_get_name(mod)); + else if (r == KMOD_PROBE_APPLY_BLACKLIST) + log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); + else { + bool print_warning = kmod_table[i].warn_if_unavailable || (r < 0 && r != -ENOENT); + + log_full_errno(print_warning ? LOG_WARNING : LOG_DEBUG, r, + "Failed to insert module '%s': %m", kmod_module_get_name(mod)); + } + + kmod_module_unref(mod); + } + + if (ctx) + kmod_unref(ctx); + +#endif + return 0; +} diff --git a/src/grp-system/libcore/kmod-setup.h b/src/grp-system/libcore/kmod-setup.h new file mode 100644 index 0000000000..685f4df301 --- /dev/null +++ b/src/grp-system/libcore/kmod-setup.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int kmod_setup(void); diff --git a/src/grp-system/libcore/linux/auto_dev-ioctl.h b/src/grp-system/libcore/linux/auto_dev-ioctl.h new file mode 100644 index 0000000000..aeaeb3ea7a --- /dev/null +++ b/src/grp-system/libcore/linux/auto_dev-ioctl.h @@ -0,0 +1,228 @@ +/* + * Copyright 2008 Red Hat, Inc. All rights reserved. + * Copyright 2008 Ian Kent + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + */ + +#ifndef _LINUX_AUTO_DEV_IOCTL_H +#define _LINUX_AUTO_DEV_IOCTL_H + +#include + +#ifdef __KERNEL__ +#include +#else +#include +#endif /* __KERNEL__ */ + +#define AUTOFS_DEVICE_NAME "autofs" + +#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 +#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 + +#define AUTOFS_DEVID_LEN 16 + +#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) + +/* + * An ioctl interface for autofs mount point control. + */ + +struct args_protover { + __u32 version; +}; + +struct args_protosubver { + __u32 sub_version; +}; + +struct args_openmount { + __u32 devid; +}; + +struct args_ready { + __u32 token; +}; + +struct args_fail { + __u32 token; + __s32 status; +}; + +struct args_setpipefd { + __s32 pipefd; +}; + +struct args_timeout { + __u64 timeout; +}; + +struct args_requester { + __u32 uid; + __u32 gid; +}; + +struct args_expire { + __u32 how; +}; + +struct args_askumount { + __u32 may_umount; +}; + +struct args_ismountpoint { + union { + struct args_in { + __u32 type; + } in; + struct args_out { + __u32 devid; + __u32 magic; + } out; + }; +}; + +/* + * All the ioctls use this structure. + * When sending a path size must account for the total length + * of the chunk of memory otherwise is is the size of the + * structure. + */ + +struct autofs_dev_ioctl { + __u32 ver_major; + __u32 ver_minor; + __u32 size; /* total size of data passed in + * including this struct */ + __s32 ioctlfd; /* automount command fd */ + + /* Command parameters */ + + union { + struct args_protover protover; + struct args_protosubver protosubver; + struct args_openmount openmount; + struct args_ready ready; + struct args_fail fail; + struct args_setpipefd setpipefd; + struct args_timeout timeout; + struct args_requester requester; + struct args_expire expire; + struct args_askumount askumount; + struct args_ismountpoint ismountpoint; + }; + + char path[0]; +}; + +static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) { + memset(in, 0, sizeof(struct autofs_dev_ioctl)); + in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; + in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; + in->size = sizeof(struct autofs_dev_ioctl); + in->ioctlfd = -1; + return; +} + +/* + * If you change this make sure you make the corresponding change + * to autofs-dev-ioctl.c:lookup_ioctl() + */ +enum { + /* Get various version info */ + AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, + AUTOFS_DEV_IOCTL_PROTOVER_CMD, + AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, + + /* Open mount ioctl fd */ + AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, + + /* Close mount ioctl fd */ + AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, + + /* Mount/expire status returns */ + AUTOFS_DEV_IOCTL_READY_CMD, + AUTOFS_DEV_IOCTL_FAIL_CMD, + + /* Activate/deactivate autofs mount */ + AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, + AUTOFS_DEV_IOCTL_CATATONIC_CMD, + + /* Expiry timeout */ + AUTOFS_DEV_IOCTL_TIMEOUT_CMD, + + /* Get mount last requesting uid and gid */ + AUTOFS_DEV_IOCTL_REQUESTER_CMD, + + /* Check for eligible expire candidates */ + AUTOFS_DEV_IOCTL_EXPIRE_CMD, + + /* Request busy status */ + AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, + + /* Check if path is a mountpoint */ + AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, +}; + +#define AUTOFS_IOCTL 0x93 + +#define AUTOFS_DEV_IOCTL_VERSION \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_PROTOVER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_OPENMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_READY \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_FAIL \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_SETPIPEFD \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_CATATONIC \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_TIMEOUT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_REQUESTER \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_EXPIRE \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) + +#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ + _IOWR(AUTOFS_IOCTL, \ + AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) + +#endif /* _LINUX_AUTO_DEV_IOCTL_H */ diff --git a/src/grp-system/libcore/load-dropin.c b/src/grp-system/libcore/load-dropin.c new file mode 100644 index 0000000000..9a65b5038b --- /dev/null +++ b/src/grp-system/libcore/load-dropin.c @@ -0,0 +1,91 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include "basic/log.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "shared/conf-parser.h" + +#include "load-dropin.h" +#include "load-fragment.h" +#include "unit.h" + +static int add_dependency_consumer( + UnitDependency dependency, + const char *entry, + const char* filepath, + void *arg) { + Unit *u = arg; + int r; + + assert(u); + + r = unit_add_dependency_by_name(u, dependency, entry, filepath, true); + if (r < 0) + log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", entry, u->id); + + return 0; +} + +int unit_load_dropin(Unit *u) { + _cleanup_strv_free_ char **l = NULL; + Iterator i; + char *t, **f; + int r; + + assert(u); + + /* Load dependencies from supplementary drop-in directories */ + + SET_FOREACH(t, u->names, i) { + char **p; + + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { + unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS, + add_dependency_consumer, u, NULL); + unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES, + add_dependency_consumer, u, NULL); + } + } + + r = unit_find_dropin_paths(u, &l); + if (r <= 0) + return 0; + + if (!u->dropin_paths) { + u->dropin_paths = l; + l = NULL; + } else { + r = strv_extend_strv(&u->dropin_paths, l, true); + if (r < 0) + return log_oom(); + } + + STRV_FOREACH(f, u->dropin_paths) { + config_parse(u->id, *f, NULL, + UNIT_VTABLE(u)->sections, + config_item_perf_lookup, load_fragment_gperf_lookup, + false, false, false, u); + } + + u->dropin_mtime = now(CLOCK_REALTIME); + + return 0; +} diff --git a/src/grp-system/libcore/load-dropin.h b/src/grp-system/libcore/load-dropin.h new file mode 100644 index 0000000000..d91cf05197 --- /dev/null +++ b/src/grp-system/libcore/load-dropin.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include "shared/dropin.h" + +#include "unit.h" + +/* Read service data supplementary drop-in directories */ + +static inline int unit_find_dropin_paths(Unit *u, char ***paths) { + return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path, + u->manager->unit_path_cache, + u->names, + paths); +} + +int unit_load_dropin(Unit *u); diff --git a/src/grp-system/libcore/load-fragment-gperf.gperf.m4 b/src/grp-system/libcore/load-fragment-gperf.gperf.m4 new file mode 100644 index 0000000000..6a5c16a000 --- /dev/null +++ b/src/grp-system/libcore/load-fragment-gperf.gperf.m4 @@ -0,0 +1,401 @@ +%{ +#include +#include "conf-parser.h" +#include "load-fragment.h" +#include "missing.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name load_fragment_gperf_hash +%define lookup-function-name load_fragment_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +m4_dnl Define the context options only once +m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', +`$1.WorkingDirectory, config_parse_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) +$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups) +$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) +$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) +$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) +$1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context) +$1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context) +$1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context) +$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) +$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) +$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) +$1.Environment, config_parse_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) +$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path) +$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset) +$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup) +$1.TTYVTDisallocate, config_parse_bool, 0, offsetof($1, exec_context.tty_vt_disallocate) +$1.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.syslog_identifier) +$1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority) +$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority) +$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) +$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) +$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) +$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', +`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) +$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) +$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context) +$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute) +$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime) +$1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)', +`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.RestrictRealtime, 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.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) +$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) +$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) +$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) +$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) +$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) +$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) +$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) +$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) +$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) +$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) +$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) +$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) +$1.ReadWritePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) +$1.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) +$1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) +$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp) +$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) +$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices) +$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) +$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context) +$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) +$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality) +$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode) +$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory) +m4_ifdef(`HAVE_PAM', +`$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)', +`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) +$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) +m4_ifdef(`HAVE_SELINUX', +`$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', +`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +m4_ifdef(`HAVE_APPARMOR', +`$1.AppArmorProfile, config_parse_exec_apparmor_profile, 0, offsetof($1, exec_context)', +`$1.AppArmorProfile, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +m4_ifdef(`HAVE_SMACK', +`$1.SmackProcessLabel, config_parse_exec_smack_process_label, 0, offsetof($1, exec_context)', +`$1.SmackProcessLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')' +)m4_dnl +m4_define(`KILL_CONTEXT_CONFIG_ITEMS', +`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) +$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup) +$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) +$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)' +)m4_dnl +m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', +`$1.Slice, config_parse_unit_slice, 0, 0 +$1.CPUAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.cpu_accounting) +$1.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.cpu_shares) +$1.StartupCPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.startup_cpu_shares) +$1.CPUQuota, config_parse_cpu_quota, 0, offsetof($1, cgroup_context) +$1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting) +$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +$1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +$1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context) +$1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy) +$1.IOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.io_accounting) +$1.IOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.io_weight) +$1.StartupIOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.startup_io_weight) +$1.IODeviceWeight, config_parse_io_device_weight, 0, offsetof($1, cgroup_context) +$1.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting) +$1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight) +$1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight) +$1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) +$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.tasks_max) +$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) +$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0' +)m4_dnl +Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) +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.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 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 +Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0 +Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0 +Unit.After, config_parse_unit_deps, UNIT_AFTER, 0 +Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0 +Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 +Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 +Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 +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) +Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) +Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) +Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) +Unit.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_warn_compat, DISABLED_LEGACY, 0 +Unit.JobTimeoutSec, config_parse_sec_fix_0, 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) +Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) +m4_dnl The following is a legacy alias name for compatibility +Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) +Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) +Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) +Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) +Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions) +Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions) +Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions) +Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions) +Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions) +Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions) +Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions) +Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions) +Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions) +Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions) +Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions) +Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions) +Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions) +Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) +Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions) +Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions) +Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions) +Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) +Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions) +Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) +Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) +Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts) +Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts) +Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts) +Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts) +Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts) +Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts) +Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts) +Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts) +Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts) +Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts) +Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts) +Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) +Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts) +Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts) +Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts) +Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) +Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) +m4_dnl +Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) +Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) +Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) +Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) +Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command) +Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command) +Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command) +Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec) +Service.TimeoutSec, config_parse_service_timeout, 0, 0 +Service.TimeoutStartSec, config_parse_service_timeout, 0, 0 +Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 +Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) +Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) +m4_dnl The following three only exist for compatibility, they moved into Unit, see above +Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) +Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) +Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) +Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) +Service.FailureAction, config_parse_failure_action, 0, offsetof(Service, failure_action) +Service.Type, config_parse_service_type, 0, offsetof(Service, type) +Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) +Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) +Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only) +Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit) +Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid) +Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_prevent_status) +Service.RestartForceExitStatus, config_parse_set_status, 0, offsetof(Service, restart_force_status) +Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status) +Service.SysVStartPriority, config_parse_warn_compat, DISABLED_LEGACY, 0 +Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) +Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name) +Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max) +Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) +Service.Sockets, config_parse_service_sockets, 0, 0 +Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0 +Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors) +Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings) +EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl +KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl +m4_dnl +Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0 +Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0 +Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 +Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 +Socket.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 +Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command) +Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) +Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) +Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) +Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) +Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user) +Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group) +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) +Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval) +Socket.KeepAliveProbes, config_parse_unsigned, 0, offsetof(Socket, keep_alive_cnt) +Socket.DeferAcceptSec, config_parse_sec, 0, offsetof(Socket, defer_accept) +Socket.NoDelay, config_parse_bool, 0, offsetof(Socket, no_delay) +Socket.Priority, config_parse_int, 0, offsetof(Socket, priority) +Socket.ReceiveBuffer, config_parse_iec_size, 0, offsetof(Socket, receive_buffer) +Socket.SendBuffer, config_parse_iec_size, 0, offsetof(Socket, send_buffer) +Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos) +Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl) +Socket.Mark, config_parse_int, 0, offsetof(Socket, mark) +Socket.PipeSize, config_parse_iec_size, 0, offsetof(Socket, pipe_size) +Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind) +Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent) +Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) +Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) +Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) +Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) +Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) +Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) +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 +Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) +Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) +m4_ifdef(`HAVE_SMACK', +`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) +Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) +Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)', +`Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +m4_ifdef(`HAVE_SELINUX', +`Socket.SELinuxContextFromNet, config_parse_bool, 0, offsetof(Socket, selinux_context_from_net)', +`Socket.SELinuxContextFromNet, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl +KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl +m4_dnl +BusName.Name, config_parse_string, 0, offsetof(BusName, name) +BusName.Activating, config_parse_bool, 0, offsetof(BusName, activating) +BusName.Service, config_parse_busname_service, 0, 0 +BusName.AllowUser, config_parse_bus_policy, 0, 0 +BusName.AllowGroup, config_parse_bus_policy, 0, 0 +BusName.AllowWorld, config_parse_bus_policy_world, 0, offsetof(BusName, policy_world) +BusName.SELinuxContext, config_parse_exec_selinux_context, 0, 0 +BusName.AcceptFileDescriptors, config_parse_bool, 0, offsetof(BusName, accept_fd) +m4_dnl +Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what) +Mount.Where, config_parse_path, 0, offsetof(Mount, where) +Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options) +Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) +Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec) +Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) +Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options) +EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl +KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl +m4_dnl +Automount.Where, config_parse_path, 0, offsetof(Automount, where) +Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode) +Automount.TimeoutIdleSec, config_parse_sec, 0, offsetof(Automount, timeout_idle_usec) +m4_dnl +Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) +Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) +Swap.Options, config_parse_string, 0, offsetof(Swap, parameters_fragment.options) +Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec) +EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl +KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl +m4_dnl +Timer.OnCalendar, config_parse_timer, 0, 0 +Timer.OnActiveSec, config_parse_timer, 0, 0 +Timer.OnBootSec, config_parse_timer, 0, 0 +Timer.OnStartupSec, config_parse_timer, 0, 0 +Timer.OnUnitActiveSec, config_parse_timer, 0, 0 +Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 +Timer.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 +Path.PathExistsGlob, config_parse_path_spec, 0, 0 +Path.PathChanged, config_parse_path_spec, 0, 0 +Path.PathModified, config_parse_path_spec, 0, 0 +Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0 +Path.Unit, config_parse_trigger_unit, 0, 0 +Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory) +Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode) +m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Slice)m4_dnl +m4_dnl +CGROUP_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl +KILL_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl +Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec) +m4_dnl The [Install] section is ignored here. +Install.Alias, NULL, 0, 0 +Install.WantedBy, NULL, 0, 0 +Install.RequiredBy, NULL, 0, 0 +Install.Also, NULL, 0, 0 +Install.DefaultInstance, NULL, 0, 0 diff --git a/src/grp-system/libcore/load-fragment.c b/src/grp-system/libcore/load-fragment.c new file mode 100644 index 0000000000..9a6ffc8bff --- /dev/null +++ b/src/grp-system/libcore/load-fragment.c @@ -0,0 +1,4133 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2012 Holger Hans Peter Freyther + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include +#include +#ifdef HAVE_SECCOMP +#include +#endif +#include +#include +#include +#include + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/cap-list.h" +#include "basic/capability-util.h" +#include "basic/cpu-set-util.h" +#include "basic/env-util.h" +#include "basic/errno-list.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/ioprio.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rlimit-util.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-internal.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" + +#include "cgroup.h" +#include "load-fragment.h" +#ifdef HAVE_SECCOMP +#include "shared/seccomp-util.h" +#endif +#include "basic/securebits.h" +#include "basic/signal-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/utf8.h" +#include "basic/web-util.h" + +#include "unit-printf.h" +#include "unit.h" + +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) { + Disabled reason = ltype; + + switch(reason) { + case DISABLED_CONFIGURATION: + 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, 0, + "Support for option %s= has been removed and it is ignored", lvalue); + break; + case DISABLED_EXPERIMENTAL: + log_syntax(unit, LOG_INFO, filename, line, 0, + "Support for option %s= has not yet been enabled and it is ignored", lvalue); + break; + }; + + 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) { + + UnitDependency d = ltype; + Unit *u = userdata; + const char *p; + + assert(filename); + assert(lvalue); + assert(rvalue); + + p = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + int r; + + 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, word, &k); + if (r < 0) { + 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: %m", k); + } + + 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, + 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; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + 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: %m", rvalue); + return 0; + } + + 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) { + + Unit *u = userdata; + _cleanup_free_ char *k = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + 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: %m", rvalue); + return 0; + } + + 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) { + + _cleanup_free_ char *k = NULL; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + 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: %m", rvalue); + return 0; + } + + return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, 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) { + + char ***x = data; + const char *word, *state; + Unit *u = userdata; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *k = NULL; + char t[l+1]; + + memcpy(t, word, l); + t[l] = 0; + + 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: %m", t); + return 0; + } + + 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, "Symlink path %s is not absolute, ignoring: %m", k); + return 0; + } + + path_kill_slashes(k); + + r = strv_push(x, k); + if (r < 0) + return log_oom(); + + k = NULL; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring."); + + return 0; +} + +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) { + + _cleanup_free_ SocketPort *p = NULL; + SocketPort *tail; + Socket *s; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + s = SOCKET(data); + + if (isempty(rvalue)) { + /* An empty assignment removes all ports */ + socket_free_ports(s); + return 0; + } + + p = new0(SocketPort, 1); + if (!p) + return log_oom(); + + if (ltype != SOCKET_SOCKET) { + + p->type = ltype; + r = unit_full_printf(UNIT(s), rvalue, &p->path); + if (r < 0) { + 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); + + } else if (streq(lvalue, "ListenNetlink")) { + _cleanup_free_ char *k = NULL; + + 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: %m", rvalue); + return 0; + } + + 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); + return 0; + } + + } else { + _cleanup_free_ char *k = NULL; + + 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: %m", rvalue); + return 0; + } + + 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); + return 0; + } + + if (streq(lvalue, "ListenStream")) + p->address.type = SOCK_STREAM; + else if (streq(lvalue, "ListenDatagram")) + p->address.type = SOCK_DGRAM; + else { + assert(streq(lvalue, "ListenSequentialPacket")); + p->address.type = SOCK_SEQPACKET; + } + + if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue); + return 0; + } + } + + p->fd = -1; + p->auxiliary_fds = NULL; + p->n_auxiliary_fds = 0; + p->socket = s; + + if (s->ports) { + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); + } else + LIST_PREPEND(port, s->ports, p); + p = NULL; + + 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, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Socket *s; + SocketAddressBindIPv6Only b; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + s = SOCKET(data); + + b = socket_address_bind_ipv6_only_from_string(rvalue); + if (b < 0) { + int r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); + return 0; + } + + s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; + } else + s->bind_ipv6_only = b; + + return 0; +} + +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) { + + ExecContext *c = data; + int priority, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atoi(rvalue, &priority); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); + return 0; + } + + if (priority < PRIO_MIN || priority >= PRIO_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue); + return 0; + } + + c->nice = priority; + c->nice_set = true; + + return 0; +} + +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) { + + ExecContext *c = data; + int oa, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atoi(rvalue, &oa); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); + return 0; + } + + if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue); + return 0; + } + + c->oom_score_adjust = oa; + c->oom_score_adjust_set = true; + + return 0; +} + +int config_parse_exec( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecCommand **e = data; + const char *p; + bool semicolon; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(e); + + e += ltype; + rvalue += strspn(rvalue, WHITESPACE); + + if (isempty(rvalue)) { + /* An empty assignment resets the list */ + *e = exec_command_free_list(*e); + return 0; + } + + p = rvalue; + do { + _cleanup_free_ char *path = NULL, *firstword = NULL; + bool separate_argv0 = false, ignore = false, privileged = false; + _cleanup_free_ ExecCommand *nce = NULL; + _cleanup_strv_free_ char **n = NULL; + size_t nlen = 0, nbufsize = 0; + char *f; + int i; + + semicolon = false; + + r = extract_first_word_and_warn(&p, &firstword, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue); + if (r <= 0) + return 0; + + f = firstword; + for (i = 0; i < 3; i++) { + /* We accept an absolute path as first argument. + * If it's prefixed with - and the path doesn't exist, + * we ignore it instead of erroring out; + * if it's prefixed with @, we allow overriding of argv[0]; + * and if it's prefixed with !, it will be run with full privileges */ + if (*f == '-' && !ignore) + ignore = true; + else if (*f == '@' && !separate_argv0) + separate_argv0 = true; + else if (*f == '+' && !privileged) + privileged = true; + else + break; + f++; + } + + if (isempty(f)) { + /* First word is either "-" or "@" with no command. */ + 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, 0, "Executable path contains special characters, ignoring: %s", rvalue); + return 0; + } + if (!path_is_absolute(f)) { + 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, 0, "Executable path specifies a directory, ignoring: %s", rvalue); + return 0; + } + + if (f == firstword) { + path = firstword; + firstword = NULL; + } else { + path = strdup(f); + if (!path) + return log_oom(); + } + + if (!separate_argv0) { + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + f = strdup(path); + if (!f) + return log_oom(); + n[nlen++] = f; + n[nlen] = NULL; + } + + path_kill_slashes(path); + + while (!isempty(p)) { + _cleanup_free_ char *word = NULL; + + /* Check explicitly for an unquoted semicolon as + * command separator token. */ + if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) { + p++; + p += strspn(p, WHITESPACE); + semicolon = true; + break; + } + + /* Check for \; explicitly, to not confuse it with \\; + * or "\;" or "\\;" etc. extract_first_word would + * return the same for all of those. */ + if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) { + p += 2; + p += strspn(p, WHITESPACE); + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + f = strdup(";"); + if (!f) + return log_oom(); + n[nlen++] = f; + n[nlen] = NULL; + continue; + } + + r = extract_first_word_and_warn(&p, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue); + if (r == 0) + break; + else if (r < 0) + return 0; + + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + n[nlen++] = word; + n[nlen] = NULL; + word = NULL; + } + + if (!n || !n[0]) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); + return 0; + } + + nce = new0(ExecCommand, 1); + if (!nce) + return log_oom(); + + nce->argv = n; + nce->path = path; + nce->ignore = ignore; + nce->privileged = privileged; + + exec_command_append_list(e, nce); + + /* Do not _cleanup_free_ these. */ + n = NULL; + path = NULL; + nce = NULL; + + rvalue = p; + } while (semicolon); + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); + +int config_parse_socket_bindtodevice( + const char* 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 = data; + char *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (rvalue[0] && !streq(rvalue, "*")) { + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue); + return 0; + } + + n = strdup(rvalue); + if (!n) + return log_oom(); + } else + n = NULL; + + free(s->bind_to_device); + s->bind_to_device = n; + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); + +int config_parse_exec_io_class(const char *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; + int x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + x = ioprio_class_from_string(rvalue); + if (x < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue); + return 0; + } + + c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio)); + c->ioprio_set = true; + + return 0; +} + +int config_parse_exec_io_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) { + + ExecContext *c = data; + int i, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atoi(rvalue, &i); + if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue); + return 0; + } + + c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i); + c->ioprio_set = true; + + return 0; +} + +int config_parse_exec_cpu_sched_policy(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + + ExecContext *c = data; + int x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + x = sched_policy_from_string(rvalue); + if (x < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); + return 0; + } + + c->cpu_sched_policy = x; + /* Moving to or from real-time policy? We need to adjust the priority */ + c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x)); + c->cpu_sched_set = true; + + return 0; +} + +int config_parse_exec_cpu_sched_prio(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; + int i, min, max, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atoi(rvalue, &i); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue); + return 0; + } + + /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */ + min = sched_get_priority_min(c->cpu_sched_policy); + max = sched_get_priority_max(c->cpu_sched_policy); + + if (i < min || i > max) { + log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue); + return 0; + } + + c->cpu_sched_priority = i; + c->cpu_sched_set = true; + + return 0; +} + +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) { + + ExecContext *c = data; + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + int ncpus; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); + if (ncpus < 0) + return ncpus; + + if (c->cpuset) + CPU_FREE(c->cpuset); + + if (ncpus == 0) + /* An empty assignment resets the CPU list */ + c->cpuset = NULL; + else { + c->cpuset = cpuset; + cpuset = NULL; + } + c->cpuset_ncpus = ncpus; + + return 0; +} + +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) { + + ExecContext *c = data; + size_t l; + const char *word, *state; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* An empty assignment resets the field */ + c->secure_bits = 0; + return 0; + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + if (first_word(word, "keep-caps")) + c->secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1< replace */ + *capability_set = sum; + else + /* 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) { + + struct rlimit **rl = data, d = {}; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = rlimit_parse(ltype, rvalue, &d); + if (r == -EILSEQ) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } + + if (rl[ltype]) + *rl[ltype] = d; + else { + rl[ltype] = newdup(struct rlimit, &d, 1); + if (!rl[ltype]) + return log_oom(); + } + + return 0; +} + +#ifdef HAVE_SYSV_COMPAT +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 *priority = data; + int i, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atoi(rvalue, &i); + if (r < 0 || i < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue); + return 0; + } + + *priority = (int) i; + return 0; +} +#endif + +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); + +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) { + + + unsigned long flags = 0; + ExecContext *c = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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; + } + + c->mount_flags = flags; + + return 0; +} + +int config_parse_exec_selinux_context( + 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 ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + c->selinux_context = mfree(c->selinux_context); + c->selinux_context_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + return 0; + } + + free(c->selinux_context); + c->selinux_context = k; + c->selinux_context_ignore = ignore; + + return 0; +} + +int config_parse_exec_apparmor_profile( + 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 ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + c->apparmor_profile = mfree(c->apparmor_profile); + c->apparmor_profile_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + return 0; + } + + free(c->apparmor_profile); + c->apparmor_profile = k; + c->apparmor_profile_ignore = ignore; + + return 0; +} + +int config_parse_exec_smack_process_label( + 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 ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + c->smack_process_label = mfree(c->smack_process_label); + c->smack_process_label_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); + return 0; + } + + free(c->smack_process_label); + c->smack_process_label = k; + c->smack_process_label_ignore = ignore; + + return 0; +} + +int config_parse_timer(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) { + + Timer *t = data; + usec_t u = 0; + TimerValue *v; + TimerBase b; + CalendarSpec *c = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets list */ + timer_free_values(t); + return 0; + } + + b = timer_base_from_string(lvalue); + if (b < 0) { + log_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, 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, 0, "Failed to parse timer value, ignoring: %s", rvalue); + return 0; + } + } + + v = new0(TimerValue, 1); + if (!v) { + calendar_spec_free(c); + return log_oom(); + } + + v->base = b; + v->value = u; + v->calendar_spec = c; + + LIST_PREPEND(value, t->values, v); + + return 0; +} + +int config_parse_trigger_unit( + 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; + Unit *u = data; + UnitType type; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) { + 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: %m"); + return 0; + } + + type = unit_name_to_type(p); + if (type < 0) { + 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, 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, NULL, true); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p); + return 0; + } + + return 0; +} + +int config_parse_path_spec(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) { + + Path *p = data; + PathSpec *s; + PathType b; + _cleanup_free_ char *k = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment clears list */ + path_free_specs(p); + return 0; + } + + b = path_type_from_string(lvalue); + if (b < 0) { + log_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) { + 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, 0, "Path is not absolute, ignoring: %s", k); + return 0; + } + + s = new0(PathSpec, 1); + if (!s) + return log_oom(); + + s->unit = UNIT(p); + s->path = path_kill_slashes(k); + k = NULL; + s->type = b; + s->inotify_fd = -1; + + LIST_PREPEND(spec, p->specs, s); + + return 0; +} + +int config_parse_socket_service( + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *p = NULL; + Socket *s = data; + Unit *x; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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 (!endswith(p, ".service")) { + 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)); + return 0; + } + + unit_ref_set(&s->service, x); + + 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, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Service *s = data; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + p = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + 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), 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, 0, "Unit must be of type socket, ignoring: %s", k); + continue; + } + + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); + + r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k); + } + + return 0; +} + +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) { + + _cleanup_free_ char *k = NULL; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + 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: %m", rvalue); + return 0; + } + + if (!service_name_is_valid(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k); + return 0; + } + + return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); +} + +int config_parse_service_timeout( + 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) { + + Service *s = userdata; + usec_t usec; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(s); + + /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */ + + r = parse_sec(rvalue, &usec); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); + return 0; + } + + /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens + * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle + * all other timeouts. */ + if (usec <= 0) + usec = USEC_INFINITY; + + if (!streq(lvalue, "TimeoutStopSec")) { + s->start_timeout_defined = true; + s->timeout_start_usec = usec; + } + + if (!streq(lvalue, "TimeoutStartSec")) + s->timeout_stop_usec = usec; + + return 0; +} + +int config_parse_sec_fix_0( + 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) { + + usec_t *usec = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(usec); + + /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for + * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a + * timeout. */ + + r = parse_sec(rvalue, usec); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); + return 0; + } + + if (*usec <= 0) + *usec = USEC_INFINITY; + + return 0; +} + +int config_parse_busname_service( + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + BusName *n = data; + int r; + Unit *x; + _cleanup_free_ char *p = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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); + return 0; + } + + if (!endswith(p, ".service")) { + 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)); + return 0; + } + + unit_ref_set(&n->service, x); + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access"); + +int config_parse_bus_policy( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ BusNamePolicy *p = NULL; + _cleanup_free_ char *id_str = NULL; + BusName *busname = data; + char *access_str; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + p = new0(BusNamePolicy, 1); + if (!p) + return log_oom(); + + if (streq(lvalue, "AllowUser")) + p->type = BUSNAME_POLICY_TYPE_USER; + else if (streq(lvalue, "AllowGroup")) + p->type = BUSNAME_POLICY_TYPE_GROUP; + else + assert_not_reached("Unknown lvalue"); + + id_str = strdup(rvalue); + if (!id_str) + return log_oom(); + + access_str = strpbrk(id_str, WHITESPACE); + if (!access_str) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue); + return 0; + } + + *access_str = '\0'; + access_str++; + access_str += strspn(access_str, WHITESPACE); + + p->access = bus_policy_access_from_string(access_str); + if (p->access < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str); + return 0; + } + + p->name = id_str; + id_str = NULL; + + LIST_PREPEND(policy, busname->policy, p); + p = NULL; + + return 0; +} + +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, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***env = data; + Unit *u = userdata; + _cleanup_free_ char *n = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment frees the list */ + *env = strv_free(*env); + return 0; + } + + 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); + return 0; + } + + 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, n); + if (r < 0) + return log_oom(); + + return 0; +} + +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) { + + Unit *u = userdata; + char*** env = data; + const char *word, *state; + size_t l; + _cleanup_free_ char *k = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *env = strv_free(*env); + return 0; + } + + 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); + return 0; + } + } + + if (!k) { + k = strdup(rvalue); + if (!k) + return log_oom(); + } + + FOREACH_WORD_QUOTED(word, l, k, state) { + _cleanup_free_ char *n = NULL; + char **x; + + r = cunescape_length(word, l, 0, &n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue); + continue; + } + + if (!env_assignment_is_valid(n)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue); + continue; + } + + x = strv_env_set(*env, n); + if (!x) + return log_oom(); + + strv_free(*env); + *env = x; + } + if (!isempty(state)) + 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; +} + +int config_parse_ip_tos(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 *ip_tos = data, x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + x = ip_tos_from_string(rvalue); + if (x < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue); + return 0; + } + + *ip_tos = x; + return 0; +} + +int config_parse_unit_condition_path( + 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; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *list = condition_free_list(*list); + return 0; + } + + trigger = rvalue[0] == '|'; + if (trigger) + rvalue++; + + negate = rvalue[0] == '!'; + if (negate) + rvalue++; + + 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); + return 0; + } + + if (!path_is_absolute(p)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p); + return 0; + } + + c = condition_new(t, p, trigger, negate); + if (!c) + return log_oom(); + + LIST_PREPEND(conditions, *list, c); + return 0; +} + +int config_parse_unit_condition_string( + 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 *s = NULL; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *list = condition_free_list(*list); + return 0; + } + + trigger = rvalue[0] == '|'; + if (trigger) + rvalue++; + + negate = rvalue[0] == '!'; + if (negate) + rvalue++; + + 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); + return 0; + } + + c = condition_new(t, s, trigger, negate); + if (!c) + return log_oom(); + + LIST_PREPEND(conditions, *list, c); + return 0; +} + +int config_parse_unit_condition_null( + 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) { + + Condition **list = data, *c; + bool trigger, negate; + int b; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *list = condition_free_list(*list); + return 0; + } + + trigger = rvalue[0] == '|'; + if (trigger) + rvalue++; + + negate = rvalue[0] == '!'; + if (negate) + rvalue++; + + b = parse_boolean(rvalue); + if (b < 0) { + log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); + return 0; + } + + if (!b) + negate = !negate; + + c = condition_new(CONDITION_NULL, NULL, trigger, negate); + if (!c) + return log_oom(); + + LIST_PREPEND(conditions, *list, c); + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier"); + +int config_parse_unit_requires_mounts_for( + 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; + const char *word, *state; + size_t l; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + int r; + _cleanup_free_ char *n; + + n = strndup(word, l); + if (!n) + return log_oom(); + + if (!utf8_is_valid(n)) { + 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); + continue; + } + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} + +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) { + + Unit *u = userdata; + int r; + char **a, **b; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + u->documentation = strv_free(u->documentation); + return 0; + } + + r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype, + rvalue, data, userdata); + if (r < 0) + return r; + + for (a = b = u->documentation; a && *a; a++) { + + if (documentation_url_is_valid(*a)) + *(b++) = *a; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a); + free(*a); + } + } + if (b) + *b = NULL; + + return r; +} + +#ifdef HAVE_SECCOMP +static int syscall_filter_parse_one( + const char *unit, + const char *filename, + unsigned line, + ExecContext *c, + bool invert, + const char *t, + bool warn) { + int r; + + if (*t == '@') { + const SystemCallFilterSet *set; + + for (set = syscall_filter_sets; set->set_name; set++) + if (streq(set->set_name, t)) { + const char *sys; + + NULSTR_FOREACH(sys, set->value) { + r = syscall_filter_parse_one(unit, filename, line, c, invert, sys, false); + if (r < 0) + return r; + } + break; + } + } else { + int id; + + id = seccomp_syscall_resolve_name(t); + if (id == __NR_SCMP_ERROR) { + if (warn) + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t); + return 0; + } + + /* If we previously wanted to forbid a syscall and now + * we want to allow it, then remove it from the list + */ + if (!invert == c->syscall_whitelist) { + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == 0) + return 0; + if (r < 0) + return log_oom(); + } else + set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); + } + return 0; +} + +int config_parse_syscall_filter( + 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 invert = false; + const char *word, *state; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + c->syscall_filter = set_free(c->syscall_filter); + c->syscall_whitelist = false; + return 0; + } + + if (rvalue[0] == '~') { + invert = true; + rvalue++; + } + + if (!c->syscall_filter) { + c->syscall_filter = set_new(NULL); + if (!c->syscall_filter) + return log_oom(); + + if (invert) + /* Allow everything but the ones listed */ + c->syscall_whitelist = false; + else { + /* Allow nothing but the ones listed */ + c->syscall_whitelist = true; + + /* Accept default syscalls if we are on a whitelist */ + r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false); + if (r < 0) + return r; + } + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + + t = strndup(word, l); + if (!t) + return log_oom(); + + r = syscall_filter_parse_one(unit, filename, line, c, invert, t, true); + if (r < 0) + return r; + } + if (!isempty(state)) + 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. */ + if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager)) + c->no_new_privileges = true; + + return 0; +} + +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) { + + Set **archs = data; + const char *word, *state; + size_t l; + int r; + + if (isempty(rvalue)) { + *archs = set_free(*archs); + return 0; + } + + r = set_ensure_allocated(archs, NULL); + if (r < 0) + return log_oom(); + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + uint32_t a; + + t = strndup(word, l); + if (!t) + return log_oom(); + + r = seccomp_arch_from_string(t, &a); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t); + continue; + } + + r = set_put(*archs, UINT32_TO_PTR(a + 1)); + if (r == 0) + continue; + if (r < 0) + return log_oom(); + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} + +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) { + + ExecContext *c = data; + int e; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets to KILL */ + c->syscall_errno = 0; + return 0; + } + + e = errno_from_name(rvalue); + if (e < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue); + return 0; + } + + c->syscall_errno = e; + return 0; +} + +int config_parse_address_families( + 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; + bool invert = false; + const char *word, *state; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + c->address_families = set_free(c->address_families); + c->address_families_whitelist = false; + return 0; + } + + if (rvalue[0] == '~') { + invert = true; + rvalue++; + } + + if (!c->address_families) { + c->address_families = set_new(NULL); + if (!c->address_families) + return log_oom(); + + c->address_families_whitelist = !invert; + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL; + int af; + + t = strndup(word, l); + if (!t) + return log_oom(); + + af = af_from_name(t); + if (af <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t); + continue; + } + + /* If we previously wanted to forbid an address family and now + * we want to allow it, then remove it from the list + */ + if (!invert == c->address_families_whitelist) { + r = set_put(c->address_families, INT_TO_PTR(af)); + if (r == 0) + continue; + if (r < 0) + return log_oom(); + } else + set_remove(c->address_families, INT_TO_PTR(af)); + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} +#endif + +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) { + + _cleanup_free_ char *k = NULL; + Unit *u = userdata, *slice = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + return 0; + } + + r = manager_load_unit(u->manager, k, NULL, NULL, &slice); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k); + return 0; + } + + r = unit_set_slice(u, slice); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id); + return 0; + } + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); + +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) { + + uint64_t *shares = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = cg_cpu_shares_parse(rvalue, shares); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue); + return 0; + } + + return 0; +} + +int config_parse_cpu_quota( + 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) { + + CGroupContext *c = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + c->cpu_quota_per_sec_usec = USEC_INFINITY; + return 0; + } + + r = parse_percent(rvalue); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue); + return 0; + } + + c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U; + return 0; +} + +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) { + + CGroupContext *c = data; + uint64_t bytes = CGROUP_LIMIT_MAX; + int r; + + if (!isempty(rvalue) && !streq(rvalue, "infinity")) { + + r = parse_percent(rvalue); + if (r < 0) { + r = parse_size(rvalue, 1024, &bytes); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue); + return 0; + } + } else + bytes = physical_memory_scale(r, 100U); + + if (bytes <= 0 || bytes >= UINT64_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue); + return 0; + } + } + + if (streq(lvalue, "MemoryLow")) + c->memory_low = bytes; + else if (streq(lvalue, "MemoryHigh")) + c->memory_high = bytes; + else if (streq(lvalue, "MemoryMax")) + c->memory_max = bytes; + else + c->memory_limit = bytes; + + return 0; +} + +int config_parse_tasks_max( + 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 *tasks_max = data, u; + int r; + + if (isempty(rvalue) || streq(rvalue, "infinity")) { + *tasks_max = (uint64_t) -1; + return 0; + } + + r = parse_percent(rvalue); + if (r < 0) { + r = safe_atou64(rvalue, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); + return 0; + } + } else + u = system_tasks_max_scale(r, 100U); + + if (u <= 0 || u >= UINT64_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue); + return 0; + } + + *tasks_max = u; + return 0; +} + +int config_parse_device_allow( + 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 *path = NULL, *t = NULL; + CGroupContext *c = data; + CGroupDeviceAllow *a; + const char *m = NULL; + size_t n; + int r; + + if (isempty(rvalue)) { + while (c->device_allow) + cgroup_context_free_device_allow(c, c->device_allow); + + return 0; + } + + r = unit_full_printf(userdata, rvalue, &t); + if(r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve specifiers in %s, ignoring: %m", + rvalue); + } + + n = strcspn(t, WHITESPACE); + + path = strndup(t, n); + if (!path) + return log_oom(); + + if (!startswith(path, "/dev/") && + !startswith(path, "block-") && + !startswith(path, "char-")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + m = t + n + strspn(t + n, WHITESPACE); + if (isempty(m)) + m = "rwm"; + + if (!in_charset(m, "rwm")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m); + return 0; + } + + a = new0(CGroupDeviceAllow, 1); + if (!a) + return log_oom(); + + a->path = path; + path = NULL; + a->r = !!strchr(m, 'r'); + a->w = !!strchr(m, 'w'); + a->m = !!strchr(m, 'm'); + + LIST_PREPEND(device_allow, c->device_allow, a); + return 0; +} + +int config_parse_io_weight( + 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 *weight = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = cg_weight_parse(rvalue, weight); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue); + return 0; + } + + return 0; +} + +int config_parse_io_device_weight( + 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 *path = NULL; + CGroupIODeviceWeight *w; + CGroupContext *c = data; + const char *weight; + uint64_t u; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + weight = rvalue + n; + weight += strspn(weight, WHITESPACE); + + if (isempty(weight)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + r = cg_weight_parse(weight, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight); + return 0; + } + + assert(u != CGROUP_WEIGHT_INVALID); + + w = new0(CGroupIODeviceWeight, 1); + if (!w) + return log_oom(); + + w->path = path; + path = NULL; + + w->weight = u; + + LIST_PREPEND(device_weights, c->io_device_weights, w); + return 0; +} + +int config_parse_io_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) { + + _cleanup_free_ char *path = NULL; + CGroupIODeviceLimit *l = NULL, *t; + CGroupContext *c = data; + CGroupIOLimitType type; + const char *limit; + uint64_t num; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + type = cgroup_io_limit_type_from_string(lvalue); + assert(type >= 0); + + if (isempty(rvalue)) { + LIST_FOREACH(device_limits, l, c->io_device_limits) + l->limits[type] = cgroup_io_limit_defaults[type]; + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + limit = rvalue + n; + limit += strspn(limit, WHITESPACE); + + if (!*limit) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + if (streq("infinity", limit)) { + num = CGROUP_LIMIT_MAX; + } else { + r = parse_size(limit, 1000, &num); + if (r < 0 || num <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue); + return 0; + } + } + + LIST_FOREACH(device_limits, t, c->io_device_limits) { + if (path_equal(path, t->path)) { + l = t; + break; + } + } + + if (!l) { + CGroupIOLimitType ttype; + + l = new0(CGroupIODeviceLimit, 1); + if (!l) + return log_oom(); + + l->path = path; + path = NULL; + for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++) + l->limits[ttype] = cgroup_io_limit_defaults[ttype]; + + LIST_PREPEND(device_limits, c->io_device_limits, l); + } + + l->limits[type] = num; + + return 0; +} + +int config_parse_blockio_weight( + 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 *weight = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = cg_blkio_weight_parse(rvalue, weight); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue); + return 0; + } + + return 0; +} + +int config_parse_blockio_device_weight( + 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 *path = NULL; + CGroupBlockIODeviceWeight *w; + CGroupContext *c = data; + const char *weight; + uint64_t u; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + while (c->blockio_device_weights) + cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); + + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + weight = rvalue + n; + weight += strspn(weight, WHITESPACE); + + if (isempty(weight)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + r = cg_blkio_weight_parse(weight, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight); + return 0; + } + + assert(u != CGROUP_BLKIO_WEIGHT_INVALID); + + w = new0(CGroupBlockIODeviceWeight, 1); + if (!w) + return log_oom(); + + w->path = path; + path = NULL; + + w->weight = u; + + LIST_PREPEND(device_weights, c->blockio_device_weights, w); + return 0; +} + +int config_parse_blockio_bandwidth( + 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 *path = NULL; + CGroupBlockIODeviceBandwidth *b = NULL, *t; + CGroupContext *c = data; + const char *bandwidth; + uint64_t bytes; + bool read; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + read = streq("BlockIOReadBandwidth", lvalue); + + if (isempty(rvalue)) { + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + b->rbps = CGROUP_LIMIT_MAX; + b->wbps = CGROUP_LIMIT_MAX; + } + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + bandwidth = rvalue + n; + bandwidth += strspn(bandwidth, WHITESPACE); + + if (!*bandwidth) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + 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, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue); + return 0; + } + + LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { + if (path_equal(path, t->path)) { + b = t; + break; + } + } + + if (!t) { + b = new0(CGroupBlockIODeviceBandwidth, 1); + if (!b) + return log_oom(); + + b->path = path; + path = NULL; + b->rbps = CGROUP_LIMIT_MAX; + b->wbps = CGROUP_LIMIT_MAX; + + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); + } + + if (read) + b->rbps = bytes; + else + b->wbps = bytes; + + return 0; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); + +int config_parse_job_mode_isolate( + 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) { + + JobMode *m = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue); + return 0; + } + + *m = r ? JOB_ISOLATE : JOB_REPLACE; + return 0; +} + +int config_parse_runtime_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) { + + char***rt = data; + Unit *u = userdata; + const char *word, *state; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *rt = strv_free(*rt); + return 0; + } + + FOREACH_WORD_QUOTED(word, l, rvalue, state) { + _cleanup_free_ char *t = NULL, *n = NULL; + + t = strndup(word, l); + if (!t) + return log_oom(); + + r = unit_name_printf(u, t, &n); + if (r < 0) { + 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, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue); + continue; + } + + r = strv_push(rt, n); + if (r < 0) + return log_oom(); + + n = NULL; + } + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} + +int config_parse_set_status( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t l; + const char *word, *state; + int r; + ExitStatusSet *status_set = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* Empty assignment resets the list */ + if (isempty(rvalue)) { + exit_status_set_free(status_set); + return 0; + } + + FOREACH_WORD(word, l, rvalue, state) { + _cleanup_free_ char *temp; + int val; + Set **set; + + temp = strndup(word, l); + if (!temp) + return log_oom(); + + r = safe_atoi(temp, &val); + if (r < 0) { + val = signal_from_string_try_harder(temp); + + if (val <= 0) { + 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, 0, "Value %d is outside range 0-255, ignoring", val); + continue; + } + set = &status_set->status; + } + + r = set_ensure_allocated(set, NULL); + if (r < 0) + return log_oom(); + + r = set_put(*set, INT_TO_PTR(val)); + if (r < 0) { + 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, 0, "Trailing garbage, ignoring."); + + return 0; +} + +int config_parse_namespace_path_strv( + 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) { + + char*** sv = data; + const char *prev; + const char *cur; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *sv = strv_free(*sv); + return 0; + } + + prev = cur = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL; + int offset; + + 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(word)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word); + prev = cur; + continue; + } + + 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(word + offset); + + r = strv_push(sv, word); + if (r < 0) + return log_oom(); + + prev = cur; + word = NULL; + } + + return 0; +} + +int config_parse_no_new_privileges( + 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; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); + return 0; + } + + c->no_new_privileges = !!k; + c->no_new_privileges_set = true; + + return 0; +} + +int config_parse_protect_home( + 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; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* 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) + c->protect_home = PROTECT_HOME_YES; + else if (k == 0) + c->protect_home = PROTECT_HOME_NO; + else { + ProtectHome h; + + h = protect_home_from_string(rvalue); + if (h < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); + return 0; + } + + c->protect_home = h; + } + + return 0; +} + +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) { + + ExecContext *c = data; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* 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) + c->protect_system = PROTECT_SYSTEM_YES; + else if (k == 0) + c->protect_system = PROTECT_SYSTEM_NO; + else { + ProtectSystem s; + + s = protect_system_from_string(rvalue); + if (s < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); + return 0; + } + + c->protect_system = s; + } + + return 0; +} + +#define FOLLOW_MAX 8 + +static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { + char *id = NULL; + unsigned c = 0; + int fd, r; + FILE *f; + + assert(filename); + assert(*filename); + assert(_f); + assert(names); + + /* This will update the filename pointer if the loaded file is + * reached by a symlink. The old string will be freed. */ + + for (;;) { + char *target, *name; + + if (c++ >= FOLLOW_MAX) + return -ELOOP; + + path_kill_slashes(*filename); + + /* Add the file name we are currently looking at to + * the names of this unit, but only if it is a valid + * unit name. */ + name = basename(*filename); + if (unit_name_is_valid(name, UNIT_NAME_ANY)) { + + id = set_get(names, name); + if (!id) { + id = strdup(name); + if (!id) + return -ENOMEM; + + r = set_consume(names, id); + if (r < 0) + return r; + } + } + + /* Try to open the file name, but don't if its a symlink */ + fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd >= 0) + break; + + if (errno != ELOOP) + return -errno; + + /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ + r = readlink_and_make_absolute(*filename, &target); + if (r < 0) + return r; + + free(*filename); + *filename = target; + } + + f = fdopen(fd, "re"); + if (!f) { + safe_close(fd); + return -errno; + } + + *_f = f; + *_final = id; + + return 0; +} + +static int merge_by_names(Unit **u, Set *names, const char *id) { + char *k; + int r; + + assert(u); + assert(*u); + assert(names); + + /* Let's try to add in all symlink names we found */ + while ((k = set_steal_first(names))) { + + /* First try to merge in the other name into our + * unit */ + r = unit_merge_by_name(*u, k); + if (r < 0) { + Unit *other; + + /* Hmm, we couldn't merge the other unit into + * ours? Then let's try it the other way + * round */ + + /* If the symlink name we are looking at is unit template, then + we must search for instance of this template */ + if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) { + _cleanup_free_ char *instance = NULL; + + r = unit_name_replace_instance(k, (*u)->instance, &instance); + if (r < 0) + return r; + + other = manager_get_unit((*u)->manager, instance); + } else + other = manager_get_unit((*u)->manager, k); + + free(k); + + if (other) { + r = unit_merge(other, *u); + if (r >= 0) { + *u = other; + return merge_by_names(u, names, NULL); + } + } + + return r; + } + + if (id == k) + unit_choose_id(*u, id); + + free(k); + } + + return 0; +} + +static int load_from_path(Unit *u, const char *path) { + _cleanup_set_free_free_ Set *symlink_names = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *filename = NULL; + char *id = NULL; + Unit *merged; + struct stat st; + int r; + + assert(u); + assert(path); + + symlink_names = set_new(&string_hash_ops); + if (!symlink_names) + return -ENOMEM; + + if (path_is_absolute(path)) { + + filename = strdup(path); + if (!filename) + return -ENOMEM; + + r = open_follow(&filename, &f, symlink_names, &id); + if (r < 0) { + filename = mfree(filename); + if (r != -ENOENT) + return r; + } + + } else { + char **p; + + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { + + /* Instead of opening the path right away, we manually + * follow all symlinks and add their name to our unit + * name set while doing so */ + filename = path_make_absolute(path, *p); + if (!filename) + return -ENOMEM; + + if (u->manager->unit_path_cache && + !set_get(u->manager->unit_path_cache, filename)) + r = -ENOENT; + else + r = open_follow(&filename, &f, symlink_names, &id); + if (r >= 0) + break; + filename = mfree(filename); + + /* ENOENT means that the file is missing or is a dangling symlink. + * ENOTDIR means that one of paths we expect to be is a directory + * is not a directory, we should just ignore that. + * EACCES means that the directory or file permissions are wrong. + */ + if (r == -EACCES) + log_debug_errno(r, "Cannot access \"%s\": %m", filename); + else if (!IN_SET(r, -ENOENT, -ENOTDIR)) + return r; + + /* Empty the symlink names for the next run */ + set_clear_free(symlink_names); + } + } + + if (!filename) + /* Hmm, no suitable file found? */ + return 0; + + if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { + log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); + return -ELOOP; + } + + merged = u; + r = merge_by_names(&merged, symlink_names, id); + if (r < 0) + return r; + + if (merged != u) { + u->load_state = UNIT_MERGED; + return 0; + } + + if (fstat(fileno(f), &st) < 0) + return -errno; + + if (null_or_empty(&st)) { + u->load_state = UNIT_MASKED; + u->fragment_mtime = 0; + } else { + u->load_state = UNIT_LOADED; + u->fragment_mtime = timespec_load(&st.st_mtim); + + /* Now, parse the file contents */ + r = config_parse(u->id, filename, f, + UNIT_VTABLE(u)->sections, + config_item_perf_lookup, load_fragment_gperf_lookup, + false, true, false, u); + if (r < 0) + return r; + } + + free(u->fragment_path); + u->fragment_path = filename; + filename = NULL; + + if (u->source_path) { + if (stat(u->source_path, &st) >= 0) + u->source_mtime = timespec_load(&st.st_mtim); + else + u->source_mtime = 0; + } + + return 0; +} + +int unit_load_fragment(Unit *u) { + int r; + Iterator i; + const char *t; + + assert(u); + assert(u->load_state == UNIT_STUB); + assert(u->id); + + if (u->transient) { + u->load_state = UNIT_LOADED; + return 0; + } + + /* First, try to find the unit under its id. We always look + * for unit files in the default directories, to make it easy + * to override things by placing things in /etc/systemd/system */ + r = load_from_path(u, u->id); + if (r < 0) + return r; + + /* Try to find an alias we can load this with */ + if (u->load_state == UNIT_STUB) { + SET_FOREACH(t, u->names, i) { + + if (t == u->id) + continue; + + r = load_from_path(u, t); + if (r < 0) + return r; + + if (u->load_state != UNIT_STUB) + break; + } + } + + /* And now, try looking for it under the suggested (originally linked) path */ + if (u->load_state == UNIT_STUB && u->fragment_path) { + + r = load_from_path(u, u->fragment_path); + if (r < 0) + return r; + + if (u->load_state == UNIT_STUB) + /* Hmm, this didn't work? Then let's get rid + * of the fragment path stored for us, so that + * we don't point to an invalid location. */ + u->fragment_path = mfree(u->fragment_path); + } + + /* Look for a template */ + if (u->load_state == UNIT_STUB && u->instance) { + _cleanup_free_ char *k = NULL; + + r = unit_name_template(u->id, &k); + if (r < 0) + return r; + + r = load_from_path(u, k); + if (r < 0) + return r; + + if (u->load_state == UNIT_STUB) { + SET_FOREACH(t, u->names, i) { + _cleanup_free_ char *z = NULL; + + if (t == u->id) + continue; + + r = unit_name_template(t, &z); + if (r < 0) + return r; + + r = load_from_path(u, z); + if (r < 0) + return r; + + if (u->load_state != UNIT_STUB) + break; + } + } + } + + return 0; +} + +void unit_dump_config_items(FILE *f) { + static const struct { + const ConfigParserCallback callback; + const char *rvalue; + } table[] = { +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR) + { config_parse_warn_compat, "NOTSUPPORTED" }, +#endif + { config_parse_int, "INTEGER" }, + { config_parse_unsigned, "UNSIGNED" }, + { config_parse_iec_size, "SIZE" }, + { config_parse_iec_uint64, "SIZE" }, + { config_parse_si_size, "SIZE" }, + { config_parse_bool, "BOOLEAN" }, + { config_parse_string, "STRING" }, + { config_parse_path, "PATH" }, + { config_parse_unit_path_printf, "PATH" }, + { config_parse_strv, "STRING [...]" }, + { config_parse_exec_nice, "NICE" }, + { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" }, + { config_parse_exec_io_class, "IOCLASS" }, + { config_parse_exec_io_priority, "IOPRIORITY" }, + { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" }, + { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" }, + { config_parse_exec_cpu_affinity, "CPUAFFINITY" }, + { config_parse_mode, "MODE" }, + { config_parse_unit_env_file, "FILE" }, + { config_parse_output, "OUTPUT" }, + { config_parse_input, "INPUT" }, + { config_parse_log_facility, "FACILITY" }, + { config_parse_log_level, "LEVEL" }, + { config_parse_exec_secure_bits, "SECUREBITS" }, + { config_parse_capability_set, "BOUNDINGSET" }, + { config_parse_limit, "LIMIT" }, + { config_parse_unit_deps, "UNIT [...]" }, + { config_parse_exec, "PATH [ARGUMENT [...]]" }, + { config_parse_service_type, "SERVICETYPE" }, + { config_parse_service_restart, "SERVICERESTART" }, +#ifdef HAVE_SYSV_COMPAT + { config_parse_sysv_priority, "SYSVPRIORITY" }, +#endif + { config_parse_kill_mode, "KILLMODE" }, + { config_parse_signal, "SIGNAL" }, + { config_parse_socket_listen, "SOCKET [...]" }, + { config_parse_socket_bind, "SOCKETBIND" }, + { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, + { config_parse_sec, "SECONDS" }, + { config_parse_nsec, "NANOSECONDS" }, + { config_parse_namespace_path_strv, "PATH [...]" }, + { config_parse_unit_requires_mounts_for, "PATH [...]" }, + { config_parse_exec_mount_flags, "MOUNTFLAG [...]" }, + { config_parse_unit_string_printf, "STRING" }, + { config_parse_trigger_unit, "UNIT" }, + { config_parse_timer, "TIMER" }, + { config_parse_path_spec, "PATH" }, + { config_parse_notify_access, "ACCESS" }, + { config_parse_ip_tos, "TOS" }, + { config_parse_unit_condition_path, "CONDITION" }, + { config_parse_unit_condition_string, "CONDITION" }, + { config_parse_unit_condition_null, "CONDITION" }, + { config_parse_unit_slice, "SLICE" }, + { config_parse_documentation, "URL" }, + { config_parse_service_timeout, "SECONDS" }, + { config_parse_failure_action, "ACTION" }, + { config_parse_set_status, "STATUS" }, + { config_parse_service_sockets, "SOCKETS" }, + { config_parse_environ, "ENVIRON" }, +#ifdef HAVE_SECCOMP + { config_parse_syscall_filter, "SYSCALLS" }, + { config_parse_syscall_archs, "ARCHS" }, + { config_parse_syscall_errno, "ERRNO" }, + { config_parse_address_families, "FAMILIES" }, +#endif + { config_parse_cpu_shares, "SHARES" }, + { config_parse_memory_limit, "LIMIT" }, + { config_parse_device_allow, "DEVICE" }, + { config_parse_device_policy, "POLICY" }, + { config_parse_io_limit, "LIMIT" }, + { config_parse_io_weight, "WEIGHT" }, + { config_parse_io_device_weight, "DEVICEWEIGHT" }, + { config_parse_blockio_bandwidth, "BANDWIDTH" }, + { config_parse_blockio_weight, "WEIGHT" }, + { config_parse_blockio_device_weight, "DEVICEWEIGHT" }, + { config_parse_long, "LONG" }, + { config_parse_socket_service, "SERVICE" }, +#ifdef HAVE_SELINUX + { config_parse_exec_selinux_context, "LABEL" }, +#endif + { config_parse_job_mode, "MODE" }, + { config_parse_job_mode_isolate, "BOOLEAN" }, + { config_parse_personality, "PERSONALITY" }, + }; + + const char *prev = NULL; + const char *i; + + assert(f); + + NULSTR_FOREACH(i, load_fragment_gperf_nulstr) { + const char *rvalue = "OTHER", *lvalue; + unsigned j; + size_t prefix_len; + const char *dot; + const ConfigPerfItem *p; + + assert_se(p = load_fragment_gperf_lookup(i, strlen(i))); + + dot = strchr(i, '.'); + lvalue = dot ? dot + 1 : i; + prefix_len = dot-i; + + if (dot) + if (!prev || !strneq(prev, i, prefix_len+1)) { + if (prev) + fputc('\n', f); + + fprintf(f, "[%.*s]\n", (int) prefix_len, i); + } + + for (j = 0; j < ELEMENTSOF(table); j++) + if (p->parse == table[j].callback) { + rvalue = table[j].rvalue; + break; + } + + fprintf(f, "%s=%s\n", lvalue, rvalue); + prev = i; + } +} diff --git a/src/grp-system/libcore/load-fragment.h b/src/grp-system/libcore/load-fragment.h new file mode 100644 index 0000000000..b36a2e3a02 --- /dev/null +++ b/src/grp-system/libcore/load-fragment.h @@ -0,0 +1,123 @@ +#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 . +***/ + +#include "unit.h" + +/* Read service data from .desktop file style configuration fragments */ + +int unit_load_fragment(Unit *u); + +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); +int config_parse_exec(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_service_timeout(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_service_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_service_restart(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_bindtodevice(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_output(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_input(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_io_class(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_io_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_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_cpu_sched_prio(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_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_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_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_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); +int config_parse_timer(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_trigger_unit(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_path_spec(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_service(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_service_sockets(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_busname_service(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_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bus_policy_world(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_env_file(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_ip_tos(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_condition_path(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_condition_string(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_condition_null(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_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_notify_access(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_failure_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_unit_requires_mounts_for(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_filter(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_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); +int config_parse_tasks_max(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_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_device_allow(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_io_weight(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_io_device_weight(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_io_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_blockio_weight(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_blockio_device_weight(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_blockio_bandwidth(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_netclass(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_job_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_job_mode_isolate(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_selinux_context(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_apparmor_profile(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_smack_process_label(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_families(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_runtime_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_set_status(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_namespace_path_strv(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_no_new_privileges(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_quota(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_protect_home(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_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); +int config_parse_sec_fix_0(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); +extern const char load_fragment_gperf_nulstr[]; + +typedef enum Disabled { + DISABLED_CONFIGURATION, + DISABLED_LEGACY, + DISABLED_EXPERIMENTAL, +} Disabled; diff --git a/src/grp-system/libcore/locale-setup.c b/src/grp-system/libcore/locale-setup.c new file mode 100644 index 0000000000..ed50796c09 --- /dev/null +++ b/src/grp-system/libcore/locale-setup.c @@ -0,0 +1,125 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/env-util.h" +#include "basic/fileio.h" +#include "basic/locale-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "basic/virt.h" + +#include "locale-setup.h" + +int locale_setup(char ***environment) { + char **add; + char *variables[_VARIABLE_LC_MAX] = {}; + int r = 0, i; + + if (detect_container() <= 0) { + r = parse_env_file("/proc/cmdline", WHITESPACE, + "locale.LANG", &variables[VARIABLE_LANG], + "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], + "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], + "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], + "locale.LC_TIME", &variables[VARIABLE_LC_TIME], + "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], + "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], + "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], + "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], + "locale.LC_NAME", &variables[VARIABLE_LC_NAME], + "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], + "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], + "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], + "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); + } + + /* Hmm, nothing set on the kernel cmd line? Then let's + * try /etc/locale.conf */ + if (r <= 0) { + r = parse_env_file("/etc/locale.conf", NEWLINE, + "LANG", &variables[VARIABLE_LANG], + "LANGUAGE", &variables[VARIABLE_LANGUAGE], + "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], + "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], + "LC_TIME", &variables[VARIABLE_LC_TIME], + "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], + "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], + "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], + "LC_PAPER", &variables[VARIABLE_LC_PAPER], + "LC_NAME", &variables[VARIABLE_LC_NAME], + "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], + "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], + "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], + "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read /etc/locale.conf: %m"); + } + + add = NULL; + for (i = 0; i < _VARIABLE_LC_MAX; i++) { + char *s; + + if (!variables[i]) + continue; + + s = strjoin(locale_variable_to_string(i), "=", variables[i], NULL); + if (!s) { + r = -ENOMEM; + goto finish; + } + + if (strv_consume(&add, s) < 0) { + r = -ENOMEM; + goto finish; + } + } + + if (!strv_isempty(add)) { + char **e; + + e = strv_env_merge(2, *environment, add); + if (!e) { + r = -ENOMEM; + goto finish; + } + + strv_free(*environment); + *environment = e; + } + + r = 0; + +finish: + strv_free(add); + + for (i = 0; i < _VARIABLE_LC_MAX; i++) + free(variables[i]); + + return r; +} diff --git a/src/grp-system/libcore/locale-setup.h b/src/grp-system/libcore/locale-setup.h new file mode 100644 index 0000000000..3b97497afe --- /dev/null +++ b/src/grp-system/libcore/locale-setup.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int locale_setup(char ***environment); diff --git a/src/grp-system/libcore/loopback-setup.c b/src/grp-system/libcore/loopback-setup.c new file mode 100644 index 0000000000..d5b65bca9c --- /dev/null +++ b/src/grp-system/libcore/loopback-setup.c @@ -0,0 +1,90 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/missing.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" + +#include "loopback-setup.h" + +static int start_loopback(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); + if (r < 0) + return r; + + r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +static bool check_loopback(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + unsigned flags; + int r; + + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX); + if (r < 0) + return false; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return false; + + r = sd_rtnl_message_link_get_flags(reply, &flags); + if (r < 0) + return false; + + return flags & IFF_UP; +} + +int loopback_setup(void) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return r; + + r = start_loopback(rtnl); + if (r < 0) { + + /* If we lack the permissions to configure the + * loopback device, but we find it to be already + * configured, let's exit cleanly, in order to + * supported unprivileged containers. */ + if (r == -EPERM && check_loopback(rtnl)) + return 0; + + return log_warning_errno(r, "Failed to configure loopback device: %m"); + } + + return 0; +} diff --git a/src/grp-system/libcore/loopback-setup.h b/src/grp-system/libcore/loopback-setup.h new file mode 100644 index 0000000000..e7547b8a26 --- /dev/null +++ b/src/grp-system/libcore/loopback-setup.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int loopback_setup(void); diff --git a/src/grp-system/libcore/machine-id-setup.c b/src/grp-system/libcore/machine-id-setup.c new file mode 100644 index 0000000000..0b65583686 --- /dev/null +++ b/src/grp-system/libcore/machine-id-setup.c @@ -0,0 +1,259 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/umask-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "sd-id128/id128-util.h" + +#include "machine-id-setup.h" + +static int generate_machine_id(const char *root, sd_id128_t *ret) { + const char *dbus_machine_id; + _cleanup_close_ int fd = -1; + int r; + + assert(ret); + + /* First, try reading the D-Bus machine id, unless it is a symlink */ + dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id"); + fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd >= 0) { + if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) { + log_info("Initializing machine ID from D-Bus machine ID."); + return 0; + } + + fd = safe_close(fd); + } + + if (isempty(root)) { + /* If that didn't work, see if we are running in a container, + * and a machine ID was passed in via $container_uuid the way + * libvirt/LXC does it */ + + if (detect_container() > 0) { + _cleanup_free_ char *e = NULL; + + if (getenv_for_pid(1, "container_uuid", &e) > 0 && + sd_id128_from_string(e, ret) >= 0) { + log_info("Initializing machine ID from container UUID."); + return 0; + } + + } else if (detect_vm() == VIRTUALIZATION_KVM) { + + /* If we are not running in a container, see if we are + * running in qemu/kvm and a machine ID was passed in + * via -uuid on the qemu/kvm command line */ + + if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) { + log_info("Initializing machine ID from KVM UUID."); + return 0; + } + } + } + + /* If that didn't work, generate a random machine id */ + r = sd_id128_randomize(ret); + if (r < 0) + return log_error_errno(r, "Failed to generate randomized : %m"); + + log_info("Initializing machine ID from random generator."); + return 0; +} + +int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) { + const char *etc_machine_id, *run_machine_id; + _cleanup_close_ int fd = -1; + bool writable; + int r; + + etc_machine_id = prefix_roota(root, "/etc/machine-id"); + + RUN_WITH_UMASK(0000) { + /* We create this 0444, to indicate that this isn't really + * something you should ever modify. Of course, since the file + * will be owned by root it doesn't matter much, but maybe + * people look. */ + + (void) mkdir_parents(etc_machine_id, 0755); + fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); + if (fd < 0) { + int old_errno = errno; + + fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (old_errno == EROFS && errno == ENOENT) + log_error_errno(errno, + "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n" + "Booting up is supported only when:\n" + "1) /etc/machine-id exists and is populated.\n" + "2) /etc/machine-id exists and is empty.\n" + "3) /etc/machine-id is missing and /etc is writable.\n"); + else + log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); + + return -errno; + } + + writable = false; + } else + writable = true; + } + + /* A we got a valid machine ID argument, that's what counts */ + if (sd_id128_is_null(machine_id)) { + + /* Try to read any existing machine ID */ + if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) + return 0; + + /* Hmm, so, the id currently stored is not useful, then let's generate one */ + r = generate_machine_id(root, &machine_id); + if (r < 0) + return r; + + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) + return log_error_errno(errno, "Failed to seek: %m"); + } + + if (writable) + if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0) + goto finish; + + fd = safe_close(fd); + + /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */ + + run_machine_id = prefix_roota(root, "/run/machine-id"); + + RUN_WITH_UMASK(0022) + r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false); + if (r < 0) { + (void) unlink(run_machine_id); + return log_error_errno(r, "Cannot write %s: %m", run_machine_id); + } + + /* And now, let's mount it over */ + if (mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL) < 0) { + (void) unlink_noerrno(run_machine_id); + return log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id); + } + + log_info("Installed transient %s file.", etc_machine_id); + + /* Mark the mount read-only */ + if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0) + log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id); + +finish: + if (ret) + *ret = machine_id; + + return 0; +} + +int machine_id_commit(const char *root) { + _cleanup_close_ int fd = -1, initial_mntns_fd = -1; + const char *etc_machine_id; + sd_id128_t id; + int r; + + /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed + * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the + * original mount namespace, thus revealing the file that was just created. */ + + etc_machine_id = prefix_roota(root, "/etc/machine-id"); + + r = path_is_mount_point(etc_machine_id, 0); + if (r < 0) + return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id); + if (r == 0) { + log_debug("%s is not a mount point. Nothing to do.", etc_machine_id); + return 0; + } + + /* Read existing machine-id */ + fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); + + r = fd_is_temporary_fs(fd); + if (r < 0) + return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id); + if (r == 0) { + log_error("%s is not on a temporary file system.", etc_machine_id); + return -EROFS; + } + + r = id128_read_fd(fd, ID128_PLAIN, &id); + if (r < 0) + return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id); + + fd = safe_close(fd); + + /* Store current mount namespace */ + r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Can't fetch current mount namespace: %m"); + + /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */ + if (unshare(CLONE_NEWNS) < 0) + return log_error_errno(errno, "Failed to enter new namespace: %m"); + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m"); + + if (umount(etc_machine_id) < 0) + return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id); + + /* Update a persistent version of etc_machine_id */ + r = id128_write(etc_machine_id, ID128_PLAIN, id, true); + if (r < 0) + return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id); + + /* Return to initial namespace and proceed a lazy tmpfs unmount */ + r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1); + if (r < 0) + return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id); + + if (umount2(etc_machine_id, MNT_DETACH) < 0) + return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id); + + return 0; +} diff --git a/src/grp-system/libcore/machine-id-setup.h b/src/grp-system/libcore/machine-id-setup.h new file mode 100644 index 0000000000..29f4620646 --- /dev/null +++ b/src/grp-system/libcore/machine-id-setup.h @@ -0,0 +1,23 @@ +#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 . +***/ + +int machine_id_commit(const char *root); +int machine_id_setup(const char *root, sd_id128_t requested, sd_id128_t *ret); diff --git a/src/grp-system/libcore/manager.c b/src/grp-system/libcore/manager.c new file mode 100644 index 0000000000..26395bc2b3 --- /dev/null +++ b/src/grp-system/libcore/manager.c @@ -0,0 +1,3143 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_AUDIT +#include +#endif + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/env-util.h" +#include "basic/escape.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/ratelimit.h" +#include "basic/rm-rf.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-kernel.h" +#include "shared/boot-timestamps.h" +#include "shared/bus-util.h" +#include "shared/path-lookup.h" +#include "shared/watchdog.h" + +#include "audit-fd.h" +#include "dbus-job.h" +#include "dbus-manager.h" +#include "dbus-unit.h" +#include "dbus.h" +#include "locale-setup.h" +#include "manager.h" +#include "transaction.h" + +#define NOTIFY_RCVBUF_SIZE (8*1024*1024) +#define CGROUPS_AGENT_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) +#define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3) +#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 + +static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); +static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); +static int manager_run_generators(Manager *m); + +static void manager_watch_jobs_in_progress(Manager *m) { + usec_t next; + int r; + + assert(m); + + if (m->jobs_in_progress_event_source) + return; + + next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC; + r = sd_event_add_time( + m->event, + &m->jobs_in_progress_event_source, + CLOCK_MONOTONIC, + next, 0, + manager_dispatch_jobs_in_progress, m); + if (r < 0) + return; + + (void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress"); +} + +#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1)) + +static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) { + char *p = buffer; + + assert(buflen >= CYLON_BUFFER_EXTRA + width + 1); + assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */ + + if (pos > 1) { + if (pos > 2) + p = mempset(p, ' ', pos-2); + if (log_get_show_color()) + p = stpcpy(p, ANSI_RED); + *p++ = '*'; + } + + if (pos > 0 && pos <= width) { + if (log_get_show_color()) + p = stpcpy(p, ANSI_HIGHLIGHT_RED); + *p++ = '*'; + } + + if (log_get_show_color()) + p = stpcpy(p, ANSI_NORMAL); + + if (pos < width) { + if (log_get_show_color()) + p = stpcpy(p, ANSI_RED); + *p++ = '*'; + if (pos < width-1) + p = mempset(p, ' ', width-1-pos); + if (log_get_show_color()) + strcpy(p, ANSI_NORMAL); + } +} + +void manager_flip_auto_status(Manager *m, bool enable) { + assert(m); + + if (enable) { + if (m->show_status == SHOW_STATUS_AUTO) + manager_set_show_status(m, SHOW_STATUS_TEMPORARY); + } else { + if (m->show_status == SHOW_STATUS_TEMPORARY) + manager_set_show_status(m, SHOW_STATUS_AUTO); + } +} + +static void manager_print_jobs_in_progress(Manager *m) { + _cleanup_free_ char *job_of_n = NULL; + Iterator i; + Job *j; + unsigned counter = 0, print_nr; + char cylon[6 + CYLON_BUFFER_EXTRA + 1]; + unsigned cylon_pos; + char time[FORMAT_TIMESPAN_MAX], limit[FORMAT_TIMESPAN_MAX] = "no limit"; + uint64_t x; + + assert(m); + assert(m->n_running_jobs > 0); + + manager_flip_auto_status(m, true); + + print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs; + + HASHMAP_FOREACH(j, m->jobs, i) + if (j->state == JOB_RUNNING && counter++ == print_nr) + break; + + /* m->n_running_jobs must be consistent with the contents of m->jobs, + * so the above loop must have succeeded in finding j. */ + assert(counter == print_nr + 1); + assert(j); + + cylon_pos = m->jobs_in_progress_iteration % 14; + if (cylon_pos >= 8) + cylon_pos = 14 - cylon_pos; + draw_cylon(cylon, sizeof(cylon), 6, cylon_pos); + + m->jobs_in_progress_iteration++; + + if (m->n_running_jobs > 1) { + if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0) + job_of_n = NULL; + } + + format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC); + if (job_get_timeout(j, &x) > 0) + format_timespan(limit, sizeof(limit), x - j->begin_usec, 1*USEC_PER_SEC); + + manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon, + "%sA %s job is running for %s (%s / %s)", + strempty(job_of_n), + job_type_to_string(j->type), + unit_description(j->unit), + time, limit); +} + +static int have_ask_password(void) { + _cleanup_closedir_ DIR *dir; + + dir = opendir("/run/systemd/ask-password"); + if (!dir) { + if (errno == ENOENT) + return false; + else + return -errno; + } + + for (;;) { + struct dirent *de; + + errno = 0; + de = readdir(dir); + if (!de && errno > 0) + return -errno; + if (!de) + return false; + + if (startswith(de->d_name, "ask.")) + return true; + } +} + +static int manager_dispatch_ask_password_fd(sd_event_source *source, + int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(m); + + flush_fd(fd); + + m->have_ask_password = have_ask_password(); + if (m->have_ask_password < 0) + /* Log error but continue. Negative have_ask_password + * is treated as unknown status. */ + log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m"); + + return 0; +} + +static void manager_close_ask_password(Manager *m) { + assert(m); + + m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source); + m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); + m->have_ask_password = -EINVAL; +} + +static int manager_check_ask_password(Manager *m) { + int r; + + assert(m); + + if (!m->ask_password_event_source) { + assert(m->ask_password_inotify_fd < 0); + + mkdir_p_label("/run/systemd/ask-password", 0755); + + m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (m->ask_password_inotify_fd < 0) + return log_error_errno(errno, "inotify_init1() failed: %m"); + + if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) { + log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m"); + manager_close_ask_password(m); + return -errno; + } + + r = sd_event_add_io(m->event, &m->ask_password_event_source, + m->ask_password_inotify_fd, EPOLLIN, + manager_dispatch_ask_password_fd, m); + if (r < 0) { + log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m"); + manager_close_ask_password(m); + return -errno; + } + + (void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password"); + + /* Queries might have been added meanwhile... */ + manager_dispatch_ask_password_fd(m->ask_password_event_source, + m->ask_password_inotify_fd, EPOLLIN, m); + } + + return m->have_ask_password; +} + +static int manager_watch_idle_pipe(Manager *m) { + int r; + + assert(m); + + if (m->idle_pipe_event_source) + return 0; + + if (m->idle_pipe[2] < 0) + return 0; + + r = sd_event_add_io(m->event, &m->idle_pipe_event_source, m->idle_pipe[2], EPOLLIN, manager_dispatch_idle_pipe_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to watch idle pipe: %m"); + + (void) sd_event_source_set_description(m->idle_pipe_event_source, "manager-idle-pipe"); + + return 0; +} + +static void manager_close_idle_pipe(Manager *m) { + assert(m); + + m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); + + safe_close_pair(m->idle_pipe); + safe_close_pair(m->idle_pipe + 2); +} + +static int manager_setup_time_change(Manager *m) { + int r; + + /* We only care for the cancellation event, hence we set the + * timeout to the latest possible value. */ + struct itimerspec its = { + .it_value.tv_sec = TIME_T_MAX, + }; + + assert(m); + assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); + + if (m->test_run) + return 0; + + /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever + * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + + m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->time_change_fd < 0) + return log_error_errno(errno, "Failed to create timerfd: %m"); + + if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { + log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); + m->time_change_fd = safe_close(m->time_change_fd); + return 0; + } + + r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to create time change event source: %m"); + + (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change"); + + log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); + + return 0; +} + +static int enable_special_signals(Manager *m) { + _cleanup_close_ int fd = -1; + + 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. */ + if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL) + log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m"); + + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + /* Support systems without virtual console */ + if (fd != -ENOENT) + log_warning_errno(errno, "Failed to open /dev/tty0: %m"); + } else { + /* Enable that we get SIGWINCH on kbrequest */ + if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0) + log_warning_errno(errno, "Failed to enable kbrequest handling: %m"); + } + + return 0; +} + +static int manager_setup_signals(Manager *m) { + struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + sigset_t mask; + int r; + + assert(m); + + assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); + + /* We make liberal use of realtime signals here. On + * Linux/glibc we have 30 of them (with the exception of Linux + * on hppa, see below), between SIGRTMIN+0 ... SIGRTMIN+30 + * (aka SIGRTMAX). */ + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, + SIGCHLD, /* Child died */ + SIGTERM, /* Reexecute daemon */ + SIGHUP, /* Reload configuration */ + SIGUSR1, /* systemd/upstart: reconnect to D-Bus */ + SIGUSR2, /* systemd: dump status */ + SIGINT, /* Kernel sends us this on control-alt-del */ + SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */ + SIGPWR, /* Some kernel drivers and upsd send us this on power failure */ + + SIGRTMIN+0, /* systemd: start default.target */ + SIGRTMIN+1, /* systemd: isolate rescue.target */ + SIGRTMIN+2, /* systemd: isolate emergency.target */ + SIGRTMIN+3, /* systemd: start halt.target */ + SIGRTMIN+4, /* systemd: start poweroff.target */ + SIGRTMIN+5, /* systemd: start reboot.target */ + SIGRTMIN+6, /* systemd: start kexec.target */ + + /* ... space for more special targets ... */ + + SIGRTMIN+13, /* systemd: Immediate halt */ + SIGRTMIN+14, /* systemd: Immediate poweroff */ + SIGRTMIN+15, /* systemd: Immediate reboot */ + SIGRTMIN+16, /* systemd: Immediate kexec */ + + /* ... space for more immediate system state changes ... */ + + SIGRTMIN+20, /* systemd: enable status messages */ + SIGRTMIN+21, /* systemd: disable status messages */ + SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ + SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ + SIGRTMIN+24, /* systemd: Immediate exit (--user only) */ + + /* .. one free signal here ... */ + +#if !defined(__hppa64__) && !defined(__hppa__) + /* Apparently Linux on hppa has fewer RT + * signals (SIGRTMAX is SIGRTMIN+25 there), + * hence let's not try to make use of them + * here. Since these commands are accessible + * by different means and only really a safety + * net, the missing functionality on hppa + * shouldn't matter. */ + + SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ + SIGRTMIN+27, /* systemd: set log target to console */ + SIGRTMIN+28, /* systemd: set log target to kmsg */ + SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */ + + /* ... one free signal here SIGRTMIN+30 ... */ +#endif + -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (m->signal_fd < 0) + return -errno; + + r = sd_event_add_io(m->event, &m->signal_event_source, m->signal_fd, EPOLLIN, manager_dispatch_signal_fd, m); + if (r < 0) + return r; + + (void) sd_event_source_set_description(m->signal_event_source, "manager-signal"); + + /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the + * notify processing can still figure out to which process/service a message belongs, before we reap the + * process. Also, process this before handling cgroup notifications, so that we always collect child exit + * status information before detecting that there's no process in a cgroup. */ + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); + if (r < 0) + return r; + + if (MANAGER_IS_SYSTEM(m)) + return enable_special_signals(m); + + return 0; +} + +static void manager_clean_environment(Manager *m) { + assert(m); + + /* Let's remove some environment variables that we + * need ourselves to communicate with our clients */ + strv_env_unset_many( + m->environment, + "NOTIFY_SOCKET", + "MAINPID", + "MANAGERPID", + "LISTEN_PID", + "LISTEN_FDS", + "LISTEN_FDNAMES", + "WATCHDOG_PID", + "WATCHDOG_USEC", + NULL); +} + +static int manager_default_environment(Manager *m) { + assert(m); + + if (MANAGER_IS_SYSTEM(m)) { + /* The system manager always starts with a clean + * environment for its children. It does not import + * the kernel or the parents exported variables. + * + * The initial passed environ is untouched to keep + * /proc/self/environ valid; it is used for tagging + * the init process inside containers. */ + m->environment = strv_new("PATH=" DEFAULT_PATH, + NULL); + + /* Import locale variables LC_*= from configuration */ + locale_setup(&m->environment); + } else { + /* The user manager passes its own environment + * along to its children. */ + m->environment = strv_copy(environ); + } + + if (!m->environment) + return -ENOMEM; + + manager_clean_environment(m); + strv_sort(m->environment); + + return 0; +} + + +int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { + Manager *m; + int r; + + assert(_m); + assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + m->unit_file_scope = scope; + 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_MAX; + +#ifdef ENABLE_EFI + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) + boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); +#endif + + /* Prepare log fields we can use for structured logging */ + if (MANAGER_IS_SYSTEM(m)) { + m->unit_log_field = "UNIT="; + m->unit_log_format_string = "UNIT=%s"; + } else { + m->unit_log_field = "USER_UNIT="; + m->unit_log_format_string = "USER_UNIT=%s"; + } + + m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; + + m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = + m->ask_password_inotify_fd = -1; + + m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ + + m->have_ask_password = -EINVAL; /* we don't know */ + m->first_boot = -1; + + m->test_run = test_run; + + /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ + RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7); + + r = manager_default_environment(m); + if (r < 0) + goto fail; + + r = hashmap_ensure_allocated(&m->units, &string_hash_ops); + if (r < 0) + goto fail; + + r = hashmap_ensure_allocated(&m->jobs, NULL); + if (r < 0) + goto fail; + + r = hashmap_ensure_allocated(&m->cgroup_unit, &string_hash_ops); + if (r < 0) + goto fail; + + r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops); + if (r < 0) + goto fail; + + r = sd_event_default(&m->event); + if (r < 0) + goto fail; + + r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + goto fail; + + r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue"); + + r = manager_setup_signals(m); + if (r < 0) + goto fail; + + r = manager_setup_cgroup(m); + if (r < 0) + goto fail; + + r = manager_setup_time_change(m); + if (r < 0) + goto fail; + + m->udev = udev_new(); + if (!m->udev) { + r = -ENOMEM; + goto fail; + } + + /* Note that we set up neither kdbus, nor the notify fd + * here. We do that after deserialization, since they might + * have gotten serialized across the reexec. */ + + m->taint_usr = dir_is_empty("/usr") > 0; + + *_m = m; + return 0; + +fail: + manager_free(m); + return r; +} + +static int manager_setup_notify(Manager *m) { + int r; + + if (m->test_run) + return 0; + + if (m->notify_fd < 0) { + _cleanup_close_ int fd = -1; + union sockaddr_union sa = { + .sa.sa_family = AF_UNIX, + }; + static const int one = 1; + const char *e; + + /* First free all secondary fields */ + m->notify_socket = mfree(m->notify_socket); + m->notify_event_source = sd_event_source_unref(m->notify_event_source); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate notification socket: %m"); + + fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); + + e = manager_get_runtime_prefix(m); + if (!e) { + log_error("Failed to determine runtime prefix."); + return -EINVAL; + } + + m->notify_socket = strappend(e, "/systemd/notify"); + if (!m->notify_socket) + return log_oom(); + + (void) mkdir_parents_label(m->notify_socket, 0755); + (void) unlink(m->notify_socket); + + strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); + + m->notify_fd = fd; + fd = -1; + + log_debug("Using notification socket %s", m->notify_socket); + } + + if (!m->notify_event_source) { + r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_dispatch_notify_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate notify event source: %m"); + + /* Process notification messages 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, SD_EVENT_PRIORITY_NORMAL-7); + if (r < 0) + return log_error_errno(r, "Failed to set priority of notify event source: %m"); + + (void) sd_event_source_set_description(m->notify_event_source, "manager-notify"); + } + + return 0; +} + +static int manager_setup_cgroups_agent(Manager *m) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + int r; + + /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering + * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and + * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on + * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number + * of D-Bus connections may be queued until the kernel will start dropping further incoming connections, + * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX + * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and + * we thus won't lose messages. + * + * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen + * to it. The system instance hence listens on this special socket, but the user instances listen on the system + * bus for these messages. */ + + if (m->test_run) + return 0; + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */ + return 0; + + if (m->cgroups_agent_fd < 0) { + _cleanup_close_ int fd = -1; + + /* First free all secondary fields */ + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m"); + + fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE); + + (void) unlink(sa.un.sun_path); + + /* Only allow root to connect to this socket */ + RUN_WITH_UMASK(0077) + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + m->cgroups_agent_fd = fd; + fd = -1; + } + + if (!m->cgroups_agent_event_source) { + r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate cgroups agent event source: %m"); + + /* Process cgroups notifications early, but after having processed service notification messages or + * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification, + * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of + * cgroup inotify for the unified cgroup stuff. */ + r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5); + if (r < 0) + return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); + + (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent"); + } + + return 0; +} + +static int manager_connect_bus(Manager *m, bool reexecuting) { + bool try_bus_connect; + + assert(m); + + if (m->test_run) + return 0; + + try_bus_connect = + m->kdbus_fd >= 0 || + reexecuting || + (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")); + + /* Try to connect to the buses, if possible. */ + return bus_init(m, try_bus_connect); +} + +static unsigned manager_dispatch_cleanup_queue(Manager *m) { + Unit *u; + unsigned n = 0; + + assert(m); + + while ((u = m->cleanup_queue)) { + assert(u->in_cleanup_queue); + + unit_free(u); + n++; + } + + return n; +} + +enum { + GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */ + GC_OFFSET_UNSURE, /* No clue */ + GC_OFFSET_GOOD, /* We still need this unit */ + GC_OFFSET_BAD, /* We don't need this unit anymore */ + _GC_OFFSET_MAX +}; + +static void unit_gc_mark_good(Unit *u, unsigned gc_marker) +{ + Iterator i; + Unit *other; + + u->gc_marker = gc_marker + GC_OFFSET_GOOD; + + /* Recursively mark referenced units as GOOD as well */ + SET_FOREACH(other, u->dependencies[UNIT_REFERENCES], i) + if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE) + unit_gc_mark_good(other, gc_marker); +} + +static void unit_gc_sweep(Unit *u, unsigned gc_marker) { + Iterator i; + Unit *other; + bool is_bad; + + assert(u); + + if (u->gc_marker == gc_marker + GC_OFFSET_GOOD || + u->gc_marker == gc_marker + GC_OFFSET_BAD || + u->gc_marker == gc_marker + GC_OFFSET_UNSURE || + u->gc_marker == gc_marker + GC_OFFSET_IN_PATH) + return; + + if (u->in_cleanup_queue) + goto bad; + + if (unit_check_gc(u)) + goto good; + + u->gc_marker = gc_marker + GC_OFFSET_IN_PATH; + + is_bad = true; + + SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) { + unit_gc_sweep(other, gc_marker); + + if (other->gc_marker == gc_marker + GC_OFFSET_GOOD) + goto good; + + if (other->gc_marker != gc_marker + GC_OFFSET_BAD) + is_bad = false; + } + + if (is_bad) + goto bad; + + /* We were unable to find anything out about this entry, so + * let's investigate it later */ + u->gc_marker = gc_marker + GC_OFFSET_UNSURE; + unit_add_to_gc_queue(u); + return; + +bad: + /* We definitely know that this one is not useful anymore, so + * let's mark it for deletion */ + u->gc_marker = gc_marker + GC_OFFSET_BAD; + unit_add_to_cleanup_queue(u); + return; + +good: + unit_gc_mark_good(u, gc_marker); +} + +static unsigned manager_dispatch_gc_queue(Manager *m) { + Unit *u; + unsigned n = 0; + unsigned gc_marker; + + assert(m); + + /* log_debug("Running GC..."); */ + + m->gc_marker += _GC_OFFSET_MAX; + if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX) + m->gc_marker = 1; + + gc_marker = m->gc_marker; + + while ((u = m->gc_queue)) { + assert(u->in_gc_queue); + + unit_gc_sweep(u, gc_marker); + + LIST_REMOVE(gc_queue, m->gc_queue, u); + u->in_gc_queue = false; + + n++; + + if (u->gc_marker == gc_marker + GC_OFFSET_BAD || + u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { + if (u->id) + log_unit_debug(u, "Collecting."); + u->gc_marker = gc_marker + GC_OFFSET_BAD; + unit_add_to_cleanup_queue(u); + } + } + + m->n_in_gc_queue = 0; + + return n; +} + +static void manager_clear_jobs_and_units(Manager *m) { + Unit *u; + + assert(m); + + while ((u = hashmap_first(m->units))) + unit_free(u); + + manager_dispatch_cleanup_queue(m); + + assert(!m->load_queue); + assert(!m->run_queue); + assert(!m->dbus_unit_queue); + assert(!m->dbus_job_queue); + assert(!m->cleanup_queue); + assert(!m->gc_queue); + + assert(hashmap_isempty(m->jobs)); + assert(hashmap_isempty(m->units)); + + m->n_on_console = 0; + m->n_running_jobs = 0; +} + +Manager* manager_free(Manager *m) { + UnitType c; + int i; + + if (!m) + return NULL; + + manager_clear_jobs_and_units(m); + + for (c = 0; c < _UNIT_TYPE_MAX; c++) + if (unit_vtable[c]->shutdown) + unit_vtable[c]->shutdown(m); + + /* If we reexecute ourselves, we keep the root cgroup + * around */ + manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); + + lookup_paths_flush_generator(&m->lookup_paths); + + bus_done(m); + + hashmap_free(m->units); + hashmap_free(m->jobs); + hashmap_free(m->watch_pids1); + hashmap_free(m->watch_pids2); + hashmap_free(m->watch_bus); + + set_free(m->startup_units); + set_free(m->failed_units); + + sd_event_source_unref(m->signal_event_source); + sd_event_source_unref(m->notify_event_source); + sd_event_source_unref(m->cgroups_agent_event_source); + sd_event_source_unref(m->time_change_event_source); + sd_event_source_unref(m->jobs_in_progress_event_source); + sd_event_source_unref(m->run_queue_event_source); + + safe_close(m->signal_fd); + safe_close(m->notify_fd); + safe_close(m->cgroups_agent_fd); + safe_close(m->time_change_fd); + safe_close(m->kdbus_fd); + + manager_close_ask_password(m); + + manager_close_idle_pipe(m); + + udev_unref(m->udev); + sd_event_unref(m->event); + + free(m->notify_socket); + + lookup_paths_free(&m->lookup_paths); + strv_free(m->environment); + + hashmap_free(m->cgroup_unit); + set_free_free(m->unit_path_cache); + + free(m->switch_root); + free(m->switch_root_init); + + for (i = 0; i < _RLIMIT_MAX; i++) + m->rlimit[i] = mfree(m->rlimit[i]); + + assert(hashmap_isempty(m->units_requiring_mounts_for)); + hashmap_free(m->units_requiring_mounts_for); + + free(m); + return NULL; +} + +void manager_enumerate(Manager *m) { + UnitType c; + + assert(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++) { + if (!unit_type_supported(c)) { + log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); + continue; + } + + if (!unit_vtable[c]->enumerate) + continue; + + unit_vtable[c]->enumerate(m); + } + + manager_dispatch_load_queue(m); +} + +static void manager_coldplug(Manager *m) { + Iterator i; + Unit *u; + char *k; + int r; + + assert(m); + + /* Then, let's set up their initial state. */ + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + + /* ignore aliases */ + if (u->id != k) + continue; + + r = unit_coldplug(u); + if (r < 0) + log_warning_errno(r, "We couldn't coldplug %s, proceeding anyway: %m", u->id); + } +} + +static void manager_build_unit_path_cache(Manager *m) { + char **i; + int r; + + assert(m); + + set_free_free(m->unit_path_cache); + + m->unit_path_cache = set_new(&string_hash_ops); + if (!m->unit_path_cache) { + r = -ENOMEM; + goto fail; + } + + /* This simply builds a list of files we know exist, so that + * we don't always have to go to disk */ + + STRV_FOREACH(i, m->lookup_paths.search_path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(*i); + if (!d) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i); + continue; + } + + FOREACH_DIRENT(de, d, r = -errno; goto fail) { + char *p; + + p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); + if (!p) { + r = -ENOMEM; + goto fail; + } + + r = set_consume(m->unit_path_cache, p); + if (r < 0) + goto fail; + } + } + + return; + +fail: + log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m"); + m->unit_path_cache = set_free_free(m->unit_path_cache); +} + +static void manager_distribute_fds(Manager *m, FDSet *fds) { + Iterator i; + Unit *u; + + assert(m); + + HASHMAP_FOREACH(u, m->units, i) { + + if (fdset_size(fds) <= 0) + break; + + if (!UNIT_VTABLE(u)->distribute_fds) + continue; + + UNIT_VTABLE(u)->distribute_fds(u, fds); + } +} + +int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { + int r, q; + + assert(m); + + r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + if (r < 0) + return r; + + /* Make sure the transient directory always exists, so that it remains in the search path */ + r = mkdir_p_label(m->lookup_paths.transient, 0755); + if (r < 0) + return r; + + dual_timestamp_get(&m->generators_start_timestamp); + r = manager_run_generators(m); + dual_timestamp_get(&m->generators_finish_timestamp); + if (r < 0) + return r; + + lookup_paths_reduce(&m->lookup_paths); + manager_build_unit_path_cache(m); + + /* If we will deserialize make sure that during enumeration + * this is already known, so we increase the counter here + * already */ + if (serialization) + m->n_reloading++; + + /* First, enumerate what we can from all config files */ + dual_timestamp_get(&m->units_load_start_timestamp); + manager_enumerate(m); + dual_timestamp_get(&m->units_load_finish_timestamp); + + /* Second, deserialize if there is something to deserialize */ + if (serialization) + r = manager_deserialize(m, serialization, fds); + + /* Any fds left? Find some unit which wants them. This is + * useful to allow container managers to pass some file + * descriptors to us pre-initialized. This enables + * socket-based activation of entire containers. */ + manager_distribute_fds(m, fds); + + /* We might have deserialized the notify fd, but if we didn't + * then let's create the bus now */ + q = manager_setup_notify(m); + if (q < 0 && r == 0) + r = q; + + q = manager_setup_cgroups_agent(m); + if (q < 0 && r == 0) + r = q; + + /* We might have deserialized the kdbus control fd, but if we + * didn't, then let's create the bus now. */ + manager_connect_bus(m, !!serialization); + bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed); + + /* Third, fire things up! */ + manager_coldplug(m); + + if (serialization) { + assert(m->n_reloading > 0); + m->n_reloading--; + + /* Let's wait for the UnitNew/JobNew messages being + * sent, before we notify that the reload is + * finished */ + m->send_reloading_done = true; + } + + return r; +} + +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { + int r; + Transaction *tr; + + assert(m); + assert(type < _JOB_TYPE_MAX); + assert(unit); + assert(mode < _JOB_MODE_MAX); + + if (mode == JOB_ISOLATE && type != JOB_START) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); + + if (mode == JOB_ISOLATE && !unit->allow_isolate) + return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); + + log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); + + type = job_type_collapse(type, unit); + + tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY); + if (!tr) + return -ENOMEM; + + 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) + goto tr_abort; + + if (mode == JOB_ISOLATE) { + r = transaction_add_isolate_jobs(tr, m); + if (r < 0) + goto tr_abort; + } + + r = transaction_activate(tr, m, mode, e); + if (r < 0) + goto tr_abort; + + log_unit_debug(unit, + "Enqueued job %s/%s as %u", unit->id, + job_type_to_string(type), (unsigned) tr->anchor_job->id); + + if (_ret) + *_ret = tr->anchor_job; + + transaction_free(tr); + return 0; + +tr_abort: + transaction_abort(tr); + transaction_free(tr); + return r; +} + +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; + + assert(m); + assert(type < _JOB_TYPE_MAX); + assert(name); + assert(mode < _JOB_MODE_MAX); + + r = manager_load_unit(m, name, NULL, NULL, &unit); + if (r < 0) + return r; + + 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) { + assert(m); + + return hashmap_get(m->jobs, UINT32_TO_PTR(id)); +} + +Unit *manager_get_unit(Manager *m, const char *name) { + assert(m); + assert(name); + + return hashmap_get(m->units, name); +} + +unsigned manager_dispatch_load_queue(Manager *m) { + Unit *u; + unsigned n = 0; + + assert(m); + + /* Make sure we are not run recursively */ + if (m->dispatching_load_queue) + return 0; + + m->dispatching_load_queue = true; + + /* Dispatches the load queue. Takes a unit from the queue and + * tries to load its data until the queue is empty */ + + while ((u = m->load_queue)) { + assert(u->in_load_queue); + + unit_load(u); + n++; + } + + m->dispatching_load_queue = false; + return n; +} + +int manager_load_unit_prepare( + Manager *m, + const char *name, + const char *path, + sd_bus_error *e, + Unit **_ret) { + + Unit *ret; + UnitType t; + int r; + + assert(m); + assert(name || path); + + /* This will prepare the unit for loading, but not actually + * load anything from disk. */ + + if (path && !is_path(path)) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); + + if (!name) + name = basename(path); + + t = unit_name_to_type(name); + + if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is missing the instance name.", name); + + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); + } + + ret = manager_get_unit(m, name); + if (ret) { + *_ret = ret; + return 1; + } + + ret = unit_new(m, unit_vtable[t]->object_size); + if (!ret) + return -ENOMEM; + + if (path) { + ret->fragment_path = strdup(path); + if (!ret->fragment_path) { + unit_free(ret); + return -ENOMEM; + } + } + + r = unit_add_name(ret, name); + if (r < 0) { + unit_free(ret); + return r; + } + + unit_add_to_load_queue(ret); + unit_add_to_dbus_queue(ret); + unit_add_to_gc_queue(ret); + + if (_ret) + *_ret = ret; + + return 0; +} + +int manager_load_unit( + Manager *m, + const char *name, + const char *path, + sd_bus_error *e, + Unit **_ret) { + + int r; + + assert(m); + + /* This will load the service information files, but not actually + * start any services or anything. */ + + r = manager_load_unit_prepare(m, name, path, e, _ret); + if (r != 0) + return r; + + manager_dispatch_load_queue(m); + + if (_ret) + *_ret = unit_follow_merge(*_ret); + + return 0; +} + +void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { + Iterator i; + Job *j; + + assert(s); + assert(f); + + HASHMAP_FOREACH(j, s->jobs, i) + job_dump(j, f, prefix); +} + +void manager_dump_units(Manager *s, FILE *f, const char *prefix) { + Iterator i; + Unit *u; + const char *t; + + assert(s); + assert(f); + + HASHMAP_FOREACH_KEY(u, t, s->units, i) + if (u->id == t) + unit_dump(u, f, prefix); +} + +void manager_clear_jobs(Manager *m) { + Job *j; + + assert(m); + + while ((j = hashmap_first(m->jobs))) + /* No need to recurse. We're cancelling all jobs. */ + job_finish_and_invalidate(j, JOB_CANCELED, false, false); +} + +static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { + Manager *m = userdata; + Job *j; + + assert(source); + assert(m); + + while ((j = m->run_queue)) { + assert(j->installed); + assert(j->in_run_queue); + + job_run_and_invalidate(j); + } + + if (m->n_running_jobs > 0) + manager_watch_jobs_in_progress(m); + + if (m->n_on_console > 0) + manager_watch_idle_pipe(m); + + return 1; +} + +static unsigned manager_dispatch_dbus_queue(Manager *m) { + Job *j; + Unit *u; + unsigned n = 0; + + assert(m); + + if (m->dispatching_dbus_queue) + return 0; + + m->dispatching_dbus_queue = true; + + while ((u = m->dbus_unit_queue)) { + assert(u->in_dbus_queue); + + bus_unit_send_change_signal(u); + n++; + } + + while ((j = m->dbus_job_queue)) { + assert(j->in_dbus_queue); + + bus_job_send_change_signal(j); + n++; + } + + m->dispatching_dbus_queue = false; + + if (m->send_reloading_done) { + m->send_reloading_done = false; + + bus_manager_send_reloading(m, false); + } + + if (m->queued_message) + bus_send_queued_message(m); + + return n; +} + +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + char buf[PATH_MAX+1]; + ssize_t n; + + n = recv(fd, buf, sizeof(buf), 0); + if (n < 0) + return log_error_errno(errno, "Failed to read cgroups agent message: %m"); + if (n == 0) { + log_error("Got zero-length cgroups agent message, ignoring."); + return 0; + } + if ((size_t) n >= sizeof(buf)) { + log_error("Got overly long cgroups agent message, ignoring."); + return 0; + } + + if (memchr(buf, 0, n)) { + log_error("Got cgroups agent message with embedded NUL byte, ignoring."); + return 0; + } + buf[n] = 0; + + manager_notify_cgroup_empty(m, buf); + bus_forward_agent_released(m, buf); + + return 0; +} + +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); + assert(u); + assert(buf); + assert(n > 0); + + tags = strv_split(buf, "\n\r"); + if (!tags) { + log_oom(); + return; + } + + if (UNIT_VTABLE(u)->notify_message) + UNIT_VTABLE(u)->notify_message(u, pid, tags, fds); + else + log_unit_debug(u, "Got notification message for unit. Ignoring."); +} + +static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + + _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; + + assert(m); + assert(m->notify_fd == fd); + + if (revents != EPOLLIN) { + log_warning("Got unexpected poll event for notify fd."); + return 0; + } + + n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + 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); + + } 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); + } + } + + 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(); + } + } + + 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."); + return 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; + } + + 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; + } + + 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."); + + return 0; +} + +static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { + uint64_t iteration; + + assert(m); + assert(u); + assert(si); + + sd_event_get_iteration(m->event, &iteration); + + log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); + + unit_unwatch_pid(u, si->si_pid); + + if (UNIT_VTABLE(u)->sigchld_event) { + if (set_size(u->pids) <= 1 || + iteration != u->sigchldgen || + unit_main_pid(u) == si->si_pid || + unit_control_pid(u) == si->si_pid) { + UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); + u->sigchldgen = iteration; + } else + log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids)); + } +} + +static int manager_dispatch_sigchld(Manager *m) { + assert(m); + + for (;;) { + siginfo_t si = {}; + + /* First we call waitd() for a PID and do not reap the + * zombie. That way we can still access /proc/$PID for + * it while it is a zombie. */ + if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) { + + if (errno == ECHILD) + break; + + if (errno == EINTR) + continue; + + return -errno; + } + + if (si.si_pid <= 0) + break; + + if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { + _cleanup_free_ char *name = NULL; + Unit *u1, *u2, *u3; + + get_process_comm(si.si_pid, &name); + + log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)", + si.si_pid, strna(name), + sigchld_code_to_string(si.si_code), + si.si_status, + strna(si.si_code == CLD_EXITED + ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL) + : signal_to_string(si.si_status))); + + /* And now figure out the unit this belongs + * to, it might be multiple... */ + u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid); + if (u1) + invoke_sigchld_event(m, u1, &si); + u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid)); + if (u2 && u2 != u1) + invoke_sigchld_event(m, u2, &si); + u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid)); + if (u3 && u3 != u2 && u3 != u1) + invoke_sigchld_event(m, u3, &si); + } + + /* And now, we actually reap the zombie. */ + if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { + if (errno == EINTR) + continue; + + return -errno; + } + } + + return 0; +} + +static int manager_start_target(Manager *m, const char *name, JobMode mode) { + _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, &error, NULL); + if (r < 0) + log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); + + return r; +} + +static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + ssize_t n; + struct signalfd_siginfo sfsi; + bool sigchld = false; + int r; + + assert(m); + assert(m->signal_fd == fd); + + if (revents != EPOLLIN) { + log_warning("Got unexpected events from signal file descriptor."); + return 0; + } + + for (;;) { + n = read(m->signal_fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { + + if (n >= 0) + return -EIO; + + if (errno == EINTR || errno == EAGAIN) + break; + + return -errno; + } + + log_received_signal(sfsi.ssi_signo == SIGCHLD || + (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) + ? LOG_DEBUG : LOG_INFO, + &sfsi); + + switch (sfsi.ssi_signo) { + + case SIGCHLD: + sigchld = true; + break; + + case SIGTERM: + if (MANAGER_IS_SYSTEM(m)) { + /* This is for compatibility with the + * original sysvinit */ + m->exit_code = MANAGER_REEXECUTE; + break; + } + + /* Fall through */ + + case SIGINT: + if (MANAGER_IS_SYSTEM(m)) { + + /* If the user presses C-A-D more than + * 7 times within 2s, we reboot + * immediately. */ + + if (ratelimit_test(&m->ctrl_alt_del_ratelimit)) + manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); + else { + log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); + status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately."); + m->exit_code = MANAGER_REBOOT; + } + + break; + } + + /* Run the exit target if there is one, if not, just exit. */ + if (manager_start_target(m, SPECIAL_EXIT_TARGET, JOB_REPLACE) < 0) { + m->exit_code = MANAGER_EXIT; + return 0; + } + + break; + + case SIGWINCH: + if (MANAGER_IS_SYSTEM(m)) + manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); + + /* This is a nop on non-init */ + break; + + case SIGPWR: + if (MANAGER_IS_SYSTEM(m)) + manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); + + /* This is a nop on non-init */ + break; + + case SIGUSR1: { + Unit *u; + + u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); + + if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { + log_info("Trying to reconnect to bus..."); + bus_init(m, true); + } + + if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) { + log_info("Loading D-Bus service..."); + manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE); + } + + break; + } + + case SIGUSR2: { + _cleanup_free_ char *dump = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t size; + + f = open_memstream(&dump, &size); + if (!f) { + log_warning_errno(errno, "Failed to allocate memory stream: %m"); + break; + } + + manager_dump_units(m, f, "\t"); + manager_dump_jobs(m, f, "\t"); + + r = fflush_and_check(f); + if (r < 0) { + log_warning_errno(r, "Failed to write status stream: %m"); + break; + } + + log_dump(LOG_INFO, dump); + break; + } + + case SIGHUP: + m->exit_code = MANAGER_RELOAD; + break; + + default: { + + /* Starting SIGRTMIN+0 */ + static const char * const target_table[] = { + [0] = SPECIAL_DEFAULT_TARGET, + [1] = SPECIAL_RESCUE_TARGET, + [2] = SPECIAL_EMERGENCY_TARGET, + [3] = SPECIAL_HALT_TARGET, + [4] = SPECIAL_POWEROFF_TARGET, + [5] = SPECIAL_REBOOT_TARGET, + [6] = SPECIAL_KEXEC_TARGET + }; + + /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */ + static const ManagerExitCode code_table[] = { + [0] = MANAGER_HALT, + [1] = MANAGER_POWEROFF, + [2] = MANAGER_REBOOT, + [3] = MANAGER_KEXEC + }; + + if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && + (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { + int idx = (int) sfsi.ssi_signo - SIGRTMIN; + manager_start_target(m, target_table[idx], + (idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE); + break; + } + + if ((int) sfsi.ssi_signo >= SIGRTMIN+13 && + (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) { + m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13]; + break; + } + + switch (sfsi.ssi_signo - SIGRTMIN) { + + case 20: + manager_set_show_status(m, SHOW_STATUS_YES); + break; + + case 21: + manager_set_show_status(m, SHOW_STATUS_NO); + break; + + case 22: + log_set_max_level(LOG_DEBUG); + log_info("Setting log level to debug."); + break; + + case 23: + log_set_max_level(LOG_INFO); + log_info("Setting log level to info."); + break; + + case 24: + if (MANAGER_IS_USER(m)) { + m->exit_code = MANAGER_EXIT; + return 0; + } + + /* This is a nop on init */ + break; + + case 26: + case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_notice("Setting log target to journal-or-kmsg."); + break; + + case 27: + log_set_target(LOG_TARGET_CONSOLE); + log_notice("Setting log target to console."); + break; + + case 28: + log_set_target(LOG_TARGET_KMSG); + log_notice("Setting log target to kmsg."); + break; + + default: + log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); + } + } + } + } + + if (sigchld) + manager_dispatch_sigchld(m); + + return 0; +} + +static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + Iterator i; + Unit *u; + + assert(m); + assert(m->time_change_fd == fd); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + LOG_MESSAGE("Time has been changed"), + NULL); + + /* Restart the watch */ + m->time_change_event_source = sd_event_source_unref(m->time_change_event_source); + m->time_change_fd = safe_close(m->time_change_fd); + + manager_setup_time_change(m); + + HASHMAP_FOREACH(u, m->units, i) + if (UNIT_VTABLE(u)->time_change) + UNIT_VTABLE(u)->time_change(u); + + return 0; +} + +static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(m); + assert(m->idle_pipe[2] == fd); + + m->no_console_output = m->n_on_console > 0; + + manager_close_idle_pipe(m); + + return 0; +} + +static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata) { + Manager *m = userdata; + int r; + uint64_t next; + + assert(m); + assert(source); + + manager_print_jobs_in_progress(m); + + next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_PERIOD_USEC; + r = sd_event_source_set_time(source, next); + if (r < 0) + return r; + + return sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); +} + +int manager_loop(Manager *m) { + int r; + + RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000); + + assert(m); + m->exit_code = MANAGER_OK; + + /* Release the path cache */ + m->unit_path_cache = set_free_free(m->unit_path_cache); + + manager_check_finished(m); + + /* There might still be some zombies hanging around from + * before we were exec()'ed. Let's reap them. */ + r = manager_dispatch_sigchld(m); + if (r < 0) + return r; + + while (m->exit_code == MANAGER_OK) { + usec_t wait_usec; + + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) + watchdog_ping(); + + if (!ratelimit_test(&rl)) { + /* Yay, something is going seriously wrong, pause a little */ + log_warning("Looping too fast. Throttling execution a little."); + sleep(1); + } + + if (manager_dispatch_load_queue(m) > 0) + continue; + + if (manager_dispatch_gc_queue(m) > 0) + continue; + + if (manager_dispatch_cleanup_queue(m) > 0) + continue; + + if (manager_dispatch_cgroup_queue(m) > 0) + continue; + + if (manager_dispatch_dbus_queue(m) > 0) + continue; + + /* Sleep for half the watchdog time */ + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) { + wait_usec = m->runtime_watchdog / 2; + if (wait_usec <= 0) + wait_usec = 1; + } else + wait_usec = USEC_INFINITY; + + r = sd_event_run(m->event, wait_usec); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + } + + return m->exit_code; +} + +int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) { + _cleanup_free_ char *n = NULL; + Unit *u; + int r; + + assert(m); + assert(s); + assert(_u); + + r = unit_name_from_dbus_path(s, &n); + if (r < 0) + return r; + + r = manager_load_unit(m, n, NULL, e, &u); + if (r < 0) + return r; + + *_u = u; + + return 0; +} + +int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { + const char *p; + unsigned id; + Job *j; + int r; + + assert(m); + assert(s); + assert(_j); + + p = startswith(s, "/org/freedesktop/systemd1/job/"); + if (!p) + return -EINVAL; + + r = safe_atou(p, &id); + if (r < 0) + return r; + + j = manager_get_job(m, id); + if (!j) + return -ENOENT; + + *_j = j; + + return 0; +} + +void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { + +#ifdef HAVE_AUDIT + _cleanup_free_ char *p = NULL; + const char *msg; + int audit_fd, r; + + if (!MANAGER_IS_SYSTEM(m)) + return; + + audit_fd = get_audit_fd(); + if (audit_fd < 0) + return; + + /* Don't generate audit events if the service was already + * started and we're just deserializing */ + if (MANAGER_IS_RELOADING(m)) + return; + + if (u->type != UNIT_SERVICE) + return; + + r = unit_name_to_prefix_and_instance(u->id, &p); + if (r < 0) { + log_error_errno(r, "Failed to extract prefix and instance of unit name: %m"); + return; + } + + msg = strjoina("unit=", p); + if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) { + if (errno == EPERM) + /* We aren't allowed to send audit messages? + * Then let's not retry again. */ + close_audit_fd(); + else + log_warning_errno(errno, "Failed to send audit message: %m"); + } +#endif + +} + +void manager_send_unit_plymouth(Manager *m, Unit *u) { + static const union sockaddr_union sa = PLYMOUTH_SOCKET; + _cleanup_free_ char *message = NULL; + _cleanup_close_ int fd = -1; + int n = 0; + + /* Don't generate plymouth events if the service was already + * started and we're just deserializing */ + if (MANAGER_IS_RELOADING(m)) + return; + + if (!MANAGER_IS_SYSTEM(m)) + return; + + if (detect_container() > 0) + return; + + if (u->type != UNIT_SERVICE && + u->type != UNIT_MOUNT && + u->type != UNIT_SWAP) + return; + + /* We set SOCK_NONBLOCK here so that we rather drop the + * message then wait for plymouth */ + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) { + log_error_errno(errno, "socket() failed: %m"); + return; + } + + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + + if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) + log_error_errno(errno, "connect() failed: %m"); + return; + } + + if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) { + log_oom(); + return; + } + + errno = 0; + if (write(fd, message, n + 1) != n + 1) + if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) + log_error_errno(errno, "Failed to write Plymouth message: %m"); +} + +int manager_open_serialization(Manager *m, FILE **_f) { + const char *path; + int fd = -1; + FILE *f; + + assert(_f); + + path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; + fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); + if (fd < 0) + return -errno; + + log_debug("Serializing state to %s", path); + + f = fdopen(fd, "w+"); + if (!f) { + safe_close(fd); + return -errno; + } + + *_f = f; + + return 0; +} + +int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { + Iterator i; + Unit *u; + const char *t; + char **e; + int r; + + assert(m); + assert(f); + assert(fds); + + m->n_reloading++; + + fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id); + fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); + fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs); + fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs); + + dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp); + dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp); + dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp); + dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp); + + if (!in_initrd()) { + dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp); + dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); + dual_timestamp_serialize(f, "security-start-timestamp", &m->security_start_timestamp); + dual_timestamp_serialize(f, "security-finish-timestamp", &m->security_finish_timestamp); + dual_timestamp_serialize(f, "generators-start-timestamp", &m->generators_start_timestamp); + dual_timestamp_serialize(f, "generators-finish-timestamp", &m->generators_finish_timestamp); + dual_timestamp_serialize(f, "units-load-start-timestamp", &m->units_load_start_timestamp); + dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp); + } + + if (!switching_root) { + STRV_FOREACH(e, m->environment) { + _cleanup_free_ char *ce; + + ce = cescape(*e); + if (!ce) + return -ENOMEM; + + fprintf(f, "env=%s\n", *e); + } + } + + if (m->notify_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->notify_fd); + if (copy < 0) + return copy; + + fprintf(f, "notify-fd=%i\n", copy); + fprintf(f, "notify-socket=%s\n", m->notify_socket); + } + + if (m->cgroups_agent_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->cgroups_agent_fd); + if (copy < 0) + return copy; + + fprintf(f, "cgroups-agent-fd=%i\n", copy); + } + + if (m->kdbus_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->kdbus_fd); + if (copy < 0) + return copy; + + fprintf(f, "kdbus-fd=%i\n", copy); + } + + bus_track_serialize(m->subscribed, f); + + fputc('\n', f); + + HASHMAP_FOREACH_KEY(u, t, m->units, i) { + if (u->id != t) + continue; + + /* Start marker */ + fputs(u->id, f); + fputc('\n', f); + + r = unit_serialize(u, f, fds, !switching_root); + if (r < 0) { + m->n_reloading--; + return r; + } + } + + assert(m->n_reloading > 0); + m->n_reloading--; + + if (ferror(f)) + return -EIO; + + r = bus_fdset_add_all(m, fds); + if (r < 0) + return r; + + return 0; +} + +int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + int r = 0; + + assert(m); + assert(f); + + log_debug("Deserializing state..."); + + m->n_reloading++; + + for (;;) { + char line[LINE_MAX], *l; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + r = 0; + else + r = -errno; + + goto finish; + } + + char_array_0(line); + l = strstrip(line); + + if (l[0] == 0) + break; + + if (startswith(l, "current-job-id=")) { + uint32_t id; + + if (safe_atou32(l+15, &id) < 0) + log_debug("Failed to parse current job id value %s", l+15); + else + m->current_job_id = MAX(m->current_job_id, id); + + } else if (startswith(l, "n-installed-jobs=")) { + uint32_t n; + + if (safe_atou32(l+17, &n) < 0) + log_debug("Failed to parse installed jobs counter %s", l+17); + else + m->n_installed_jobs += n; + + } else if (startswith(l, "n-failed-jobs=")) { + uint32_t n; + + if (safe_atou32(l+14, &n) < 0) + log_debug("Failed to parse failed jobs counter %s", l+14); + else + m->n_failed_jobs += n; + + } else if (startswith(l, "taint-usr=")) { + int b; + + b = parse_boolean(l+10); + if (b < 0) + log_debug("Failed to parse taint /usr flag %s", l+10); + else + m->taint_usr = m->taint_usr || b; + + } else if (startswith(l, "firmware-timestamp=")) + dual_timestamp_deserialize(l+19, &m->firmware_timestamp); + else if (startswith(l, "loader-timestamp=")) + dual_timestamp_deserialize(l+17, &m->loader_timestamp); + else if (startswith(l, "kernel-timestamp=")) + dual_timestamp_deserialize(l+17, &m->kernel_timestamp); + else if (startswith(l, "initrd-timestamp=")) + dual_timestamp_deserialize(l+17, &m->initrd_timestamp); + else if (startswith(l, "userspace-timestamp=")) + dual_timestamp_deserialize(l+20, &m->userspace_timestamp); + else if (startswith(l, "finish-timestamp=")) + dual_timestamp_deserialize(l+17, &m->finish_timestamp); + else if (startswith(l, "security-start-timestamp=")) + dual_timestamp_deserialize(l+25, &m->security_start_timestamp); + else if (startswith(l, "security-finish-timestamp=")) + dual_timestamp_deserialize(l+26, &m->security_finish_timestamp); + else if (startswith(l, "generators-start-timestamp=")) + dual_timestamp_deserialize(l+27, &m->generators_start_timestamp); + else if (startswith(l, "generators-finish-timestamp=")) + dual_timestamp_deserialize(l+28, &m->generators_finish_timestamp); + else if (startswith(l, "units-load-start-timestamp=")) + dual_timestamp_deserialize(l+27, &m->units_load_start_timestamp); + else if (startswith(l, "units-load-finish-timestamp=")) + dual_timestamp_deserialize(l+28, &m->units_load_finish_timestamp); + else if (startswith(l, "env=")) { + _cleanup_free_ char *uce = NULL; + char **e; + + r = cunescape(l + 4, UNESCAPE_RELAX, &uce); + if (r < 0) + goto finish; + + e = strv_env_set(m->environment, uce); + if (!e) { + r = -ENOMEM; + goto finish; + } + + strv_free(m->environment); + m->environment = e; + + } else if (startswith(l, "notify-fd=")) { + int fd; + + if (safe_atoi(l + 10, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse notify fd: %s", l + 10); + else { + m->notify_event_source = sd_event_source_unref(m->notify_event_source); + safe_close(m->notify_fd); + m->notify_fd = fdset_remove(fds, fd); + } + + } else if (startswith(l, "notify-socket=")) { + char *n; + + n = strdup(l+14); + if (!n) { + r = -ENOMEM; + goto finish; + } + + free(m->notify_socket); + m->notify_socket = n; + + } else if (startswith(l, "cgroups-agent-fd=")) { + int fd; + + if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse cgroups agent fd: %s", l + 10); + else { + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + safe_close(m->cgroups_agent_fd); + m->cgroups_agent_fd = fdset_remove(fds, fd); + } + + } else if (startswith(l, "kdbus-fd=")) { + int fd; + + if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse kdbus fd: %s", l + 9); + else { + safe_close(m->kdbus_fd); + m->kdbus_fd = fdset_remove(fds, fd); + } + + } else { + int k; + + k = bus_track_deserialize_item(&m->deserialized_subscribed, l); + if (k < 0) + log_debug_errno(k, "Failed to deserialize bus tracker object: %m"); + else if (k == 0) + log_debug("Unknown serialization item '%s'", l); + } + } + + for (;;) { + Unit *u; + char name[UNIT_NAME_MAX+2]; + + /* Start marker */ + if (!fgets(name, sizeof(name), f)) { + if (feof(f)) + r = 0; + else + r = -errno; + + goto finish; + } + + char_array_0(name); + + r = manager_load_unit(m, strstrip(name), NULL, NULL, &u); + if (r < 0) + goto finish; + + r = unit_deserialize(u, f, fds); + if (r < 0) + goto finish; + } + +finish: + if (ferror(f)) + r = -EIO; + + assert(m->n_reloading > 0); + m->n_reloading--; + + return r; +} + +int manager_reload(Manager *m) { + int r, q; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_fdset_free_ FDSet *fds = NULL; + + assert(m); + + r = manager_open_serialization(m, &f); + if (r < 0) + return r; + + m->n_reloading++; + bus_manager_send_reloading(m, true); + + fds = fdset_new(); + if (!fds) { + m->n_reloading--; + return -ENOMEM; + } + + r = manager_serialize(m, f, fds, false); + if (r < 0) { + m->n_reloading--; + return r; + } + + if (fseeko(f, 0, SEEK_SET) < 0) { + m->n_reloading--; + return -errno; + } + + /* From here on there is no way back. */ + manager_clear_jobs_and_units(m); + lookup_paths_flush_generator(&m->lookup_paths); + lookup_paths_free(&m->lookup_paths); + + q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + if (q < 0 && r >= 0) + r = q; + + /* Find new unit paths */ + q = manager_run_generators(m); + if (q < 0 && r >= 0) + r = q; + + lookup_paths_reduce(&m->lookup_paths); + manager_build_unit_path_cache(m); + + /* First, enumerate what we can from all config files */ + manager_enumerate(m); + + /* Second, deserialize our stored data */ + q = manager_deserialize(m, f, fds); + if (q < 0 && r >= 0) + r = q; + + fclose(f); + f = NULL; + + /* Re-register notify_fd as event source */ + q = manager_setup_notify(m); + if (q < 0 && r >= 0) + r = q; + + q = manager_setup_cgroups_agent(m); + if (q < 0 && r >= 0) + r = q; + + /* 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--; + + m->send_reloading_done = true; + + return r; +} + +void manager_reset_failed(Manager *m) { + Unit *u; + Iterator i; + + assert(m); + + HASHMAP_FOREACH(u, m->units, i) + unit_reset_failed(u); +} + +bool manager_unit_inactive_or_pending(Manager *m, const char *name) { + Unit *u; + + assert(m); + assert(name); + + /* Returns true if the unit is inactive or going down */ + u = manager_get_unit(m, name); + if (!u) + return true; + + return unit_inactive_or_pending(u); +} + +static void manager_notify_finished(Manager *m) { + char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; + usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; + + if (m->test_run) + return; + + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { + + /* Note that m->kernel_usec.monotonic is always at 0, + * and m->firmware_usec.monotonic and + * m->loader_usec.monotonic should be considered + * negative values. */ + + firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic; + loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic; + userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic; + + if (dual_timestamp_is_set(&m->initrd_timestamp)) { + + kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic; + initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC="USEC_FMT, kernel_usec, + "INITRD_USEC="USEC_FMT, initrd_usec, + "USERSPACE_USEC="USEC_FMT, userspace_usec, + LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), + NULL); + } else { + kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; + initrd_usec = 0; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC="USEC_FMT, kernel_usec, + "USERSPACE_USEC="USEC_FMT, userspace_usec, + LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), + NULL); + } + } else { + firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; + total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "USERSPACE_USEC="USEC_FMT, userspace_usec, + LOG_MESSAGE("Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), + NULL); + } + + bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); + + sd_notifyf(false, + "READY=1\n" + "STATUS=Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)); +} + +void manager_check_finished(Manager *m) { + assert(m); + + if (MANAGER_IS_RELOADING(m)) + return; + + /* Verify that we are actually running currently. Initially + * the exit code is set to invalid, and during operation it is + * then set to MANAGER_OK */ + if (m->exit_code != MANAGER_OK) + return; + + if (hashmap_size(m->jobs) > 0) { + if (m->jobs_in_progress_event_source) + /* Ignore any failure, this is only for feedback */ + (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); + + return; + } + + manager_flip_auto_status(m, false); + + /* Notify Type=idle units that we are done now */ + manager_close_idle_pipe(m); + + /* Turn off confirm spawn now */ + m->confirm_spawn = false; + + /* No need to update ask password status when we're going non-interactive */ + manager_close_ask_password(m); + + /* This is no longer the first boot */ + manager_set_first_boot(m, false); + + if (dual_timestamp_is_set(&m->finish_timestamp)) + return; + + dual_timestamp_get(&m->finish_timestamp); + + manager_notify_finished(m); + + manager_invalidate_startup_units(m); +} + +static int manager_run_generators(Manager *m) { + _cleanup_strv_free_ char **paths = NULL; + const char *argv[5]; + char **path; + int r; + + assert(m); + + if (m->test_run) + return 0; + + paths = generator_binary_paths(m->unit_file_scope); + if (!paths) + return log_oom(); + + /* Optimize by skipping the whole process by not creating output directories + * if no generators are found. */ + STRV_FOREACH(path, paths) { + if (access(*path, F_OK) >= 0) + goto found; + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); + } + + return 0; + + found: + r = lookup_paths_mkdir_generator(&m->lookup_paths); + if (r < 0) + goto finish; + + argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */ + argv[1] = m->lookup_paths.generator; + argv[2] = m->lookup_paths.generator_early; + argv[3] = m->lookup_paths.generator_late; + argv[4] = NULL; + + RUN_WITH_UMASK(0022) + execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); + +finish: + lookup_paths_trim_generator(&m->lookup_paths); + return r; +} + +int manager_environment_add(Manager *m, char **minus, char **plus) { + char **a = NULL, **b = NULL, **l; + assert(m); + + l = m->environment; + + if (!strv_isempty(minus)) { + a = strv_env_delete(l, 1, minus); + if (!a) + return -ENOMEM; + + l = a; + } + + if (!strv_isempty(plus)) { + b = strv_env_merge(2, l, plus); + if (!b) { + strv_free(a); + return -ENOMEM; + } + + l = b; + } + + if (m->environment != l) + strv_free(m->environment); + if (a != l) + strv_free(a); + if (b != l) + strv_free(b); + + m->environment = l; + manager_clean_environment(m); + strv_sort(m->environment); + + return 0; +} + +int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) { + int i; + + assert(m); + + for (i = 0; i < _RLIMIT_MAX; i++) { + m->rlimit[i] = mfree(m->rlimit[i]); + + if (!default_rlimit[i]) + continue; + + m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1); + if (!m->rlimit[i]) + return -ENOMEM; + } + + return 0; +} + +void manager_recheck_journal(Manager *m) { + Unit *u; + + assert(m); + + if (!MANAGER_IS_SYSTEM(m)) + return; + + u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); + if (u && SOCKET(u)->state != SOCKET_RUNNING) { + log_close_journal(); + return; + } + + u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE); + if (u && SERVICE(u)->state != SERVICE_RUNNING) { + log_close_journal(); + return; + } + + /* Hmm, OK, so the socket is fully up and the service is up + * too, then let's make use of the thing. */ + log_open(); +} + +void manager_set_show_status(Manager *m, ShowStatus mode) { + assert(m); + assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY)); + + if (!MANAGER_IS_SYSTEM(m)) + 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) + (void) touch("/run/systemd/show-status"); + else + (void) unlink("/run/systemd/show-status"); +} + +static bool manager_get_show_status(Manager *m, StatusType type) { + assert(m); + + if (!MANAGER_IS_SYSTEM(m)) + return false; + + if (m->no_console_output) + return false; + + if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING)) + return false; + + /* If we cannot find out the status properly, just proceed. */ + if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) + return false; + + if (m->show_status > 0) + return true; + + return false; +} + +void manager_set_first_boot(Manager *m, bool b) { + assert(m); + + if (!MANAGER_IS_SYSTEM(m)) + return; + + if (m->first_boot != (int) b) { + if (b) + (void) touch("/run/systemd/first-boot"); + else + (void) unlink("/run/systemd/first-boot"); + } + + m->first_boot = b; +} + +void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { + va_list ap; + + /* If m is NULL, assume we're after shutdown and let the messages through. */ + + if (m && !manager_get_show_status(m, type)) + return; + + /* XXX We should totally drop the check for ephemeral here + * and thus effectively make 'Type=idle' pointless. */ + if (type == STATUS_TYPE_EPHEMERAL && m && m->n_on_console > 0) + return; + + va_start(ap, format); + status_vprintf(status, true, type == STATUS_TYPE_EPHEMERAL, format, ap); + va_end(ap); +} + +Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { + char p[strlen(path)+1]; + + assert(m); + assert(path); + + strcpy(p, path); + path_kill_slashes(p); + + return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p); +} + +const char *manager_get_runtime_prefix(Manager *m) { + assert(m); + + return MANAGER_IS_SYSTEM(m) ? + "/run" : + getenv("XDG_RUNTIME_DIR"); +} + +int manager_update_failed_units(Manager *m, Unit *u, bool failed) { + unsigned size; + int r; + + assert(m); + assert(u->manager == m); + + size = set_size(m->failed_units); + + if (failed) { + r = set_ensure_allocated(&m->failed_units, NULL); + if (r < 0) + return log_oom(); + + if (set_put(m->failed_units, u) < 0) + return log_oom(); + } else + (void) set_remove(m->failed_units, u); + + if (set_size(m->failed_units) != size) + bus_manager_send_change_signal(m); + + return 0; +} + +ManagerState manager_state(Manager *m) { + Unit *u; + + assert(m); + + /* Did we ever finish booting? If not then we are still starting up */ + if (!dual_timestamp_is_set(&m->finish_timestamp)) { + + u = manager_get_unit(m, SPECIAL_BASIC_TARGET); + if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) + return MANAGER_INITIALIZING; + + return MANAGER_STARTING; + } + + /* Is the special shutdown target queued? If so, we are in shutdown state */ + u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET); + if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)) + return MANAGER_STOPPING; + + /* Are the rescue or emergency targets active or queued? If so we are in maintenance state */ + u = manager_get_unit(m, SPECIAL_RESCUE_TARGET); + if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) || + (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)))) + return MANAGER_MAINTENANCE; + + u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET); + if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) || + (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START)))) + return MANAGER_MAINTENANCE; + + /* Are there any failed units? If so, we are in degraded mode */ + if (set_size(m->failed_units) > 0) + return MANAGER_DEGRADED; + + return MANAGER_RUNNING; +} + +static const char *const manager_state_table[_MANAGER_STATE_MAX] = { + [MANAGER_INITIALIZING] = "initializing", + [MANAGER_STARTING] = "starting", + [MANAGER_RUNNING] = "running", + [MANAGER_DEGRADED] = "degraded", + [MANAGER_MAINTENANCE] = "maintenance", + [MANAGER_STOPPING] = "stopping", +}; + +DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState); diff --git a/src/grp-system/libcore/manager.h b/src/grp-system/libcore/manager.h new file mode 100644 index 0000000000..252919c27f --- /dev/null +++ b/src/grp-system/libcore/manager.h @@ -0,0 +1,380 @@ +#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 . +***/ + +#include +#include +#include + +#include +#include + +#include "basic/cgroup-util.h" +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/ratelimit.h" +#include "shared/fdset.h" + +/* Enforce upper limit how many names we allow */ +#define MANAGER_MAX_NAMES 131072 /* 128K */ + +typedef struct Manager Manager; + +typedef enum ManagerState { + MANAGER_INITIALIZING, + MANAGER_STARTING, + MANAGER_RUNNING, + MANAGER_DEGRADED, + MANAGER_MAINTENANCE, + MANAGER_STOPPING, + _MANAGER_STATE_MAX, + _MANAGER_STATE_INVALID = -1 +} ManagerState; + +typedef enum ManagerExitCode { + MANAGER_OK, + MANAGER_EXIT, + MANAGER_RELOAD, + MANAGER_REEXECUTE, + MANAGER_REBOOT, + MANAGER_POWEROFF, + MANAGER_HALT, + MANAGER_KEXEC, + MANAGER_SWITCH_ROOT, + _MANAGER_EXIT_CODE_MAX, + _MANAGER_EXIT_CODE_INVALID = -1 +} ManagerExitCode; + +typedef enum StatusType { + STATUS_TYPE_EPHEMERAL, + STATUS_TYPE_NORMAL, + STATUS_TYPE_EMERGENCY, +} StatusType; + +#include "basic/unit-name.h" +#include "shared/path-lookup.h" + +#include "execute.h" +#include "job.h" +#include "show-status.h" + +struct Manager { + /* Note that the set of units we know of is allowed to be + * inconsistent. However the subset of it that is loaded may + * not, and the list of jobs may neither. */ + + /* Active jobs and units */ + Hashmap *units; /* name string => Unit object n:1 */ + Hashmap *jobs; /* job id => Job object 1:1 */ + + /* To make it easy to iterate through the units of a specific + * type we maintain a per type linked list */ + LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]); + + /* Units that need to be loaded */ + LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */ + + /* Jobs that need to be run */ + LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */ + + /* Units and jobs that have not yet been announced via + * D-Bus. When something about a job changes it is added here + * if it is not in there yet. This allows easy coalescing of + * D-Bus change signals. */ + LIST_HEAD(Unit, dbus_unit_queue); + LIST_HEAD(Job, dbus_job_queue); + + /* Units to remove */ + LIST_HEAD(Unit, cleanup_queue); + + /* Units to check when doing GC */ + LIST_HEAD(Unit, gc_queue); + + /* Units that should be realized */ + LIST_HEAD(Unit, cgroup_queue); + + sd_event *event; + + /* We use two hash tables here, since the same PID might be + * watched by two different units: once the unit that forked + * it off, and possibly a different unit to which it was + * joined as cgroup member. Since we know that it is either + * one or two units for each PID we just use to hashmaps + * here. */ + Hashmap *watch_pids1; /* pid => Unit object n:1 */ + Hashmap *watch_pids2; /* pid => Unit object n:1 */ + + /* A set contains all units which cgroup should be refreshed after startup */ + Set *startup_units; + + /* A set which contains all currently failed units */ + Set *failed_units; + + sd_event_source *run_queue_event_source; + + char *notify_socket; + int notify_fd; + sd_event_source *notify_event_source; + + int cgroups_agent_fd; + sd_event_source *cgroups_agent_event_source; + + int signal_fd; + sd_event_source *signal_event_source; + + int time_change_fd; + sd_event_source *time_change_event_source; + + sd_event_source *jobs_in_progress_event_source; + + UnitFileScope unit_file_scope; + LookupPaths lookup_paths; + Set *unit_path_cache; + + char **environment; + + usec_t runtime_watchdog; + usec_t shutdown_watchdog; + + dual_timestamp firmware_timestamp; + dual_timestamp loader_timestamp; + dual_timestamp kernel_timestamp; + dual_timestamp initrd_timestamp; + dual_timestamp userspace_timestamp; + dual_timestamp finish_timestamp; + + dual_timestamp security_start_timestamp; + dual_timestamp security_finish_timestamp; + dual_timestamp generators_start_timestamp; + dual_timestamp generators_finish_timestamp; + dual_timestamp units_load_start_timestamp; + dual_timestamp units_load_finish_timestamp; + + struct udev* udev; + + /* Data specific to the device subsystem */ + struct udev_monitor* udev_monitor; + sd_event_source *udev_event_source; + Hashmap *devices_by_sysfs; + + /* Data specific to the mount subsystem */ + struct libmnt_monitor *mount_monitor; + sd_event_source *mount_event_source; + + /* Data specific to the swap filesystem */ + FILE *proc_swaps; + sd_event_source *swap_event_source; + Hashmap *swaps_by_devnode; + + /* Data specific to the D-Bus subsystem */ + sd_bus *api_bus, *system_bus; + Set *private_buses; + int private_listen_fd; + sd_event_source *private_listen_event_source; + + /* Contains all the clients that are subscribed to signals via + the API bus. Note that private bus connections are always + considered subscribes, since they last for very short only, + and it is much simpler that way. */ + sd_bus_track *subscribed; + char **deserialized_subscribed; + + /* This is used during reloading: before the reload we queue + * the reply message here, and afterwards we send it */ + sd_bus_message *queued_message; + + Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ + + bool send_reloading_done; + + uint32_t current_job_id; + uint32_t default_unit_job_id; + + /* Data specific to the Automount subsystem */ + int dev_autofs_fd; + + /* Data specific to the cgroup subsystem */ + Hashmap *cgroup_unit; + CGroupMask cgroup_supported; + char *cgroup_root; + + /* Notifications from cgroups, when the unified hierarchy is + * used is done via inotify. */ + int cgroup_inotify_fd; + sd_event_source *cgroup_inotify_event_source; + Hashmap *cgroup_inotify_wd_unit; + + /* Make sure the user cannot accidentally unmount our cgroup + * file system */ + int pin_cgroupfs_fd; + + int gc_marker; + unsigned n_in_gc_queue; + + /* Flags */ + ManagerExitCode exit_code:5; + + bool dispatching_load_queue:1; + bool dispatching_dbus_queue:1; + + bool taint_usr:1; + + bool test_run:1; + + /* If non-zero, exit with the following value when the systemd + * process terminate. Useful for containers: systemd-nspawn could get + * the return value. */ + uint8_t return_value; + + ShowStatus show_status; + bool confirm_spawn; + bool no_console_output; + + ExecOutput default_std_output, default_std_error; + + usec_t default_restart_usec, default_timeout_start_usec, default_timeout_stop_usec; + + usec_t default_start_limit_interval; + unsigned default_start_limit_burst; + + bool default_cpu_accounting; + bool default_memory_accounting; + bool default_io_accounting; + bool default_blockio_accounting; + bool default_tasks_accounting; + + uint64_t default_tasks_max; + usec_t default_timer_accuracy_usec; + + struct rlimit *rlimit[_RLIMIT_MAX]; + + /* non-zero if we are reloading or reexecuting, */ + int n_reloading; + + unsigned n_installed_jobs; + unsigned n_failed_jobs; + + /* Jobs in progress watching */ + unsigned n_running_jobs; + unsigned n_on_console; + unsigned jobs_in_progress_iteration; + + /* Do we have any outstanding password prompts? */ + int have_ask_password; + int ask_password_inotify_fd; + sd_event_source *ask_password_event_source; + + /* Type=idle pipes */ + int idle_pipe[4]; + sd_event_source *idle_pipe_event_source; + + char *switch_root; + char *switch_root_init; + + /* This maps all possible path prefixes to the units needing + * them. It's a hashmap with a path string as key and a Set as + * value where Unit objects are contained. */ + Hashmap *units_requiring_mounts_for; + + /* Reference to the kdbus bus control fd */ + int kdbus_fd; + + /* Used for processing polkit authorization responses */ + Hashmap *polkit_registry; + + /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */ + RateLimit ctrl_alt_del_ratelimit; + + const char *unit_log_field; + const char *unit_log_format_string; + + int first_boot; /* tri-state */ +}; + +#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) +#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) + +#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) + +int manager_new(UnitFileScope scope, bool test_run, Manager **m); +Manager* manager_free(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_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, 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); + +void manager_clear_jobs(Manager *m); + +unsigned manager_dispatch_load_queue(Manager *m); + +int manager_environment_add(Manager *m, char **minus, char **plus); +int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit); + +int manager_loop(Manager *m); + +int manager_open_serialization(Manager *m, FILE **_f); + +int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root); +int manager_deserialize(Manager *m, FILE *f, FDSet *fds); + +int manager_reload(Manager *m); + +void manager_reset_failed(Manager *m); + +void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); +void manager_send_unit_plymouth(Manager *m, Unit *u); + +bool manager_unit_inactive_or_pending(Manager *m, const char *name); + +void manager_check_finished(Manager *m); + +void manager_recheck_journal(Manager *m); + +void manager_set_show_status(Manager *m, ShowStatus mode); +void manager_set_first_boot(Manager *m, bool b); + +void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5); +void manager_flip_auto_status(Manager *m, bool enable); + +Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path); + +const char *manager_get_runtime_prefix(Manager *m); + +ManagerState manager_state(Manager *m); + +int manager_update_failed_units(Manager *m, Unit *u, bool failed); + +const char *manager_state_to_string(ManagerState m) _const_; +ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/mount-setup.c b/src/grp-system/libcore/mount-setup.c new file mode 100644 index 0000000000..0de1c63b3e --- /dev/null +++ b/src/grp-system/libcore/mount-setup.c @@ -0,0 +1,420 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fs-util.h" +#include "basic/label.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/smack-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/bus-util.h" +#include "shared/dev-setup.h" +#include "shared/efivars.h" + +#include "mount-setup.h" + +typedef enum MountMode { + MNT_NONE = 0, + MNT_FATAL = 1 << 0, + MNT_IN_CONTAINER = 1 << 1, +} MountMode; + +typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + bool (*condition_fn)(void); + MountMode mode; +} MountPoint; + +/* The first three entries we might need before SELinux is up. The + * fourth (securityfs) is needed by IMA to load a custom policy. The + * other ones we can delay until SELinux and IMA are loaded. When + * SMACK is enabled we need smackfs, too, so it's a fifth one. */ +#ifdef HAVE_SMACK +#define N_EARLY_MOUNT 5 +#else +#define N_EARLY_MOUNT 4 +#endif + +static const MountPoint mount_table[] = { + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, +#ifdef HAVE_SMACK + { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV, + mac_smack_use, MNT_FATAL }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + mac_smack_use, MNT_FATAL }, +#endif + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, + NULL, MNT_IN_CONTAINER }, +#ifdef HAVE_SMACK + { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + mac_smack_use, MNT_FATAL }, +#endif + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER }, + { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, + cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, + cg_is_legacy_wanted, MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, + cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, + { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, +#ifdef ENABLE_EFI + { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + is_efi_boot, MNT_NONE }, +#endif +}; + +/* These are API file systems that might be mounted by other software, + * we just list them here so that we know that we should ignore them */ + +static const char ignore_paths[] = + /* SELinux file systems */ + "/sys/fs/selinux\0" + /* Container bind mounts */ + "/proc/sys\0" + "/dev/console\0" + "/proc/kmsg\0"; + +bool mount_point_is_api(const char *path) { + unsigned i; + + /* Checks if this mount point is considered "API", and hence + * should be ignored */ + + for (i = 0; i < ELEMENTSOF(mount_table); i ++) + if (path_equal(path, mount_table[i].where)) + return true; + + return path_startswith(path, "/sys/fs/cgroup/"); +} + +bool mount_point_ignore(const char *path) { + const char *i; + + NULSTR_FOREACH(i, ignore_paths) + if (path_equal(path, i)) + return true; + + return false; +} + +static int mount_one(const MountPoint *p, bool relabel) { + int r; + + assert(p); + + if (p->condition_fn && !p->condition_fn()) + return 0; + + /* Relabel first, just in case */ + if (relabel) + (void) label_fix(p->where, true, true); + + r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW); + if (r < 0 && r != -ENOENT) { + log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); + return (p->mode & MNT_FATAL) ? r : 0; + } + if (r > 0) + return 0; + + /* Skip securityfs in a container */ + if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0) + return 0; + + /* The access mode here doesn't really matter too much, since + * the mounted file system will take precedence anyway. */ + if (relabel) + (void) mkdir_p_label(p->where, 0755); + else + (void) mkdir_p(p->where, 0755); + + log_debug("Mounting %s to %s of type %s with options %s.", + p->what, + p->where, + p->type, + strna(p->options)); + + if (mount(p->what, + p->where, + p->type, + p->flags, + p->options) < 0) { + log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where); + return (p->mode & MNT_FATAL) ? -errno : 0; + } + + /* Relabel again, since we now mounted something fresh here */ + if (relabel) + (void) label_fix(p->where, false, false); + + return 1; +} + +static int mount_points_setup(unsigned n, bool loaded_policy) { + unsigned i; + int r = 0; + + for (i = 0; i < n; i ++) { + int j; + + j = mount_one(mount_table + i, loaded_policy); + if (j != 0 && r >= 0) + r = j; + } + + return r; +} + +int mount_setup_early(void) { + assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table)); + + /* Do a minimal mount of /proc and friends to enable the most + * basic stuff, such as SELinux */ + return mount_points_setup(N_EARLY_MOUNT, false); +} + +int mount_cgroup_controllers(char ***join_controllers) { + _cleanup_set_free_free_ Set *controllers = NULL; + int r; + + if (!cg_is_legacy_wanted()) + return 0; + + /* Mount all available cgroup controllers that are built into the kernel. */ + + controllers = set_new(&string_hash_ops); + if (!controllers) + return log_oom(); + + r = cg_kernel_controllers(controllers); + if (r < 0) + return log_error_errno(r, "Failed to enumerate cgroup controllers: %m"); + + for (;;) { + _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL; + MountPoint p = { + .what = "cgroup", + .type = "cgroup", + .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV, + .mode = MNT_IN_CONTAINER, + }; + char ***k = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + if (join_controllers) + for (k = join_controllers; *k; k++) + if (strv_find(*k, controller)) + break; + + if (k && *k) { + char **i, **j; + + for (i = *k, j = *k; *i; i++) { + + if (!streq(*i, controller)) { + _cleanup_free_ char *t; + + t = set_remove(controllers, *i); + if (!t) { + free(*i); + continue; + } + } + + *(j++) = *i; + } + + *j = NULL; + + options = strv_join(*k, ","); + if (!options) + return log_oom(); + } else { + options = controller; + controller = NULL; + } + + where = strappend("/sys/fs/cgroup/", options); + if (!where) + return log_oom(); + + p.where = where; + p.options = options; + + r = mount_one(&p, true); + if (r < 0) + return r; + + if (r > 0 && k && *k) { + char **i; + + for (i = *k; *i; i++) { + _cleanup_free_ char *t = NULL; + + t = strappend("/sys/fs/cgroup/", *i); + if (!t) + return log_oom(); + + r = symlink(options, t); + if (r >= 0) { +#ifdef SMACK_RUN_LABEL + _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); + } + } + } + + /* Now that we mounted everything, let's make the tmpfs the + * cgroup file systems are mounted into read-only. */ + (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755"); + + return 0; +} + +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + /* No need to label /dev twice in a row... */ + if (_unlikely_(ftwbuf->level == 0)) + return FTW_CONTINUE; + + label_fix(fpath, false, false); + + /* /run/initramfs is static data and big, no need to + * dynamically relabel its contents at boot... */ + if (_unlikely_(ftwbuf->level == 1 && + tflag == FTW_D && + streq(fpath, "/run/initramfs"))) + return FTW_SKIP_SUBTREE; + + return FTW_CONTINUE; +}; +#endif + +int mount_setup(bool loaded_policy) { + int r = 0; + + r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy); + + if (r < 0) + return r; + +#if defined(HAVE_SELINUX) || defined(HAVE_SMACK) + /* Nodes in devtmpfs and /run need to be manually updated for + * the appropriate labels, after mounting. The other virtual + * API file systems like /sys and /proc do not need that, they + * use the same label for all their files. */ + if (loaded_policy) { + usec_t before_relabel, after_relabel; + char timespan[FORMAT_TIMESPAN_MAX]; + + before_relabel = now(CLOCK_MONOTONIC); + + nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + + after_relabel = now(CLOCK_MONOTONIC); + + log_info("Relabelled /dev and /run in %s.", + format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0)); + } +#endif + + /* Create a few default symlinks, which are normally created + * by udevd, but some scripts might need them before we start + * udevd. */ + dev_setup(NULL, UID_INVALID, GID_INVALID); + + /* Mark the root directory as shared in regards to mount + * propagation. The kernel defaults to "private", but we think + * it makes more sense to have a default of "shared" so that + * nspawn and the container tools work out of the box. If + * specific setups need other settings they can reset the + * propagation mode to private if needed. */ + if (detect_container() <= 0) + if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) + log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m"); + + /* Create a few directories we always want around, Note that + * sd_booted() checks for /run/systemd/system, so this mkdir + * really needs to stay for good, otherwise software that + * copied sd-daemon.c into their sources will misdetect + * systemd. */ + (void) mkdir_label("/run/systemd", 0755); + (void) mkdir_label("/run/systemd/system", 0755); + (void) mkdir_label("/run/systemd/inaccessible", 0000); + /* Set up inaccessible items */ + (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0); + (void) mkdir_label("/run/systemd/inaccessible/dir", 0000); + (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0)); + (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0)); + (void) mkfifo("/run/systemd/inaccessible/fifo", 0000); + (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0); + + return 0; +} diff --git a/src/grp-system/libcore/mount-setup.h b/src/grp-system/libcore/mount-setup.h new file mode 100644 index 0000000000..647bd770ae --- /dev/null +++ b/src/grp-system/libcore/mount-setup.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#include + +int mount_setup_early(void); +int mount_setup(bool loaded_policy); + +int mount_cgroup_controllers(char ***join_controllers); + +bool mount_point_is_api(const char *path); +bool mount_point_ignore(const char *path); diff --git a/src/grp-system/libcore/mount.c b/src/grp-system/libcore/mount.c new file mode 100644 index 0000000000..bc5f29692d --- /dev/null +++ b/src/grp-system/libcore/mount.c @@ -0,0 +1,1883 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/exit-status.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "shared/fstab-util.h" + +#include "dbus-mount.h" +#include "manager.h" +#include "mount-setup.h" +#include "mount.h" +#include "unit.h" + +#define RETRY_UMOUNT_MAX 32 + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); + +static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { + [MOUNT_DEAD] = UNIT_INACTIVE, + [MOUNT_MOUNTING] = UNIT_ACTIVATING, + [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE, + [MOUNT_MOUNTED] = UNIT_ACTIVE, + [MOUNT_REMOUNTING] = UNIT_RELOADING, + [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING, + [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING, + [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING, + [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING, + [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING, + [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING, + [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING, + [MOUNT_FAILED] = UNIT_FAILED +}; + +static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); +static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + +static bool mount_needs_network(const char *options, const char *fstype) { + if (fstab_test_option(options, "_netdev\0")) + return true; + + if (fstype && fstype_is_network(fstype)) + return true; + + return false; +} + +static bool mount_is_network(const MountParameters *p) { + assert(p); + + return mount_needs_network(p->options, p->fstype); +} + +static bool mount_is_loop(const MountParameters *p) { + assert(p); + + if (fstab_test_option(p->options, "loop\0")) + return true; + + return false; +} + +static bool mount_is_bind(const MountParameters *p) { + assert(p); + + if (fstab_test_option(p->options, "bind\0" "rbind\0")) + return true; + + if (p->fstype && STR_IN_SET(p->fstype, "bind", "rbind")) + return true; + + return false; +} + +static bool mount_is_auto(const MountParameters *p) { + assert(p); + + return !fstab_test_option(p->options, "noauto\0"); +} + +static bool mount_is_automount(const MountParameters *p) { + assert(p); + + return fstab_test_option(p->options, + "comment=systemd.automount\0" + "x-systemd.automount\0"); +} + +static bool mount_state_active(MountState state) { + return IN_SET(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); +} + +static bool needs_quota(const MountParameters *p) { + assert(p); + + /* Quotas are not enabled on network filesystems, + * but we want them, for example, on storage connected via iscsi */ + if (p->fstype && fstype_is_network(p->fstype)) + return false; + + if (mount_is_bind(p)) + return false; + + return fstab_test_option(p->options, + "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0"); +} + +static void mount_init(Unit *u) { + Mount *m = MOUNT(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + m->timeout_usec = u->manager->default_timeout_start_usec; + m->directory_mode = 0755; + + if (unit_has_name(u, "-.mount")) { + /* Don't allow start/stop for root directory */ + u->refuse_manual_start = true; + u->refuse_manual_stop = true; + } else { + /* The stdio/kmsg bridge socket is on /, in order to avoid a + * dep loop, don't use kmsg logging for -.mount */ + m->exec_context.std_output = u->manager->default_std_output; + m->exec_context.std_error = u->manager->default_std_error; + } + + /* We need to make sure that /usr/bin/mount is always called + * in the same process group as us, so that the autofs kernel + * side doesn't send us another mount request while we are + * already trying to comply its last one. */ + m->exec_context.same_pgrp = true; + + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; + + u->ignore_on_isolate = true; +} + +static int mount_arm_timer(Mount *m, usec_t usec) { + int r; + + assert(m); + + if (m->timer_event_source) { + r = sd_event_source_set_time(m->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(m)->manager->event, + &m->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + mount_dispatch_timer, m); + if (r < 0) + return r; + + (void) sd_event_source_set_description(m->timer_event_source, "mount-timer"); + + return 0; +} + +static void mount_unwatch_control_pid(Mount *m) { + assert(m); + + if (m->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(m), m->control_pid); + m->control_pid = 0; +} + +static void mount_parameters_done(MountParameters *p) { + assert(p); + + free(p->what); + free(p->options); + free(p->fstype); + + p->what = p->options = p->fstype = NULL; +} + +static void mount_done(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + m->where = mfree(m->where); + + mount_parameters_done(&m->parameters_proc_self_mountinfo); + mount_parameters_done(&m->parameters_fragment); + + m->exec_runtime = exec_runtime_unref(m->exec_runtime); + exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX); + m->control_command = NULL; + + mount_unwatch_control_pid(m); + + m->timer_event_source = sd_event_source_unref(m->timer_event_source); +} + +_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) { + assert(m); + + if (m->from_fragment) + return &m->parameters_fragment; + + return NULL; +} + +_pure_ static MountParameters* get_mount_parameters(Mount *m) { + assert(m); + + if (m->from_proc_self_mountinfo) + return &m->parameters_proc_self_mountinfo; + + return get_mount_parameters_fragment(m); +} + +static int mount_add_mount_links(Mount *m) { + _cleanup_free_ char *parent = NULL; + MountParameters *pm; + Unit *other; + Iterator i; + Set *s; + int r; + + assert(m); + + if (!path_equal(m->where, "/")) { + /* Adds in links to other mount points that might lie further + * up in the hierarchy */ + + parent = dirname_malloc(m->where); + if (!parent) + return -ENOMEM; + + r = unit_require_mounts_for(UNIT(m), parent); + if (r < 0) + return r; + } + + /* Adds in links to other mount points that might be needed + * for the source path (if this is a bind mount or a loop mount) to be + * available. */ + pm = get_mount_parameters_fragment(m); + if (pm && pm->what && + path_is_absolute(pm->what) && + (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) { + + r = unit_require_mounts_for(UNIT(m), pm->what); + if (r < 0) + return r; + } + + /* Adds in links to other units that use this path or paths + * further down in the hierarchy */ + s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where); + SET_FOREACH(other, s, i) { + + if (other->load_state != UNIT_LOADED) + continue; + + if (other == UNIT(m)) + continue; + + r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true); + if (r < 0) + return r; + + if (UNIT(m)->fragment_path) { + /* If we have fragment configuration, then make this dependency required */ + r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true); + if (r < 0) + return r; + } + } + + return 0; +} + +static int mount_add_device_links(Mount *m) { + MountParameters *p; + bool device_wants_mount = false; + int r; + + assert(m); + + p = get_mount_parameters(m); + if (!p) + return 0; + + if (!p->what) + return 0; + + if (mount_is_bind(p)) + return 0; + + if (!is_device_path(p->what)) + return 0; + + /* /dev/root is a really weird thing, it's not a real device, + * but just a path the kernel exports for the root file system + * specified on the kernel command line. Ignore it here. */ + if (path_equal(p->what, "/dev/root")) + return 0; + + if (path_equal(m->where, "/")) + return 0; + + if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager)) + device_wants_mount = true; + + 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; + + return 0; +} + +static int mount_add_quota_links(Mount *m) { + int r; + MountParameters *p; + + assert(m); + + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) + return 0; + + p = get_mount_parameters_fragment(m); + if (!p) + return 0; + + if (!needs_quota(p)) + return 0; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true); + if (r < 0) + return r; + + return 0; +} + +static bool should_umount(Mount *m) { + MountParameters *p; + + if (PATH_IN_SET(m->where, "/", "/usr") || + path_startswith(m->where, "/run/initramfs")) + return false; + + p = get_mount_parameters(m); + if (p && fstab_test_option(p->options, "x-initrd.mount\0") && + !in_initrd()) + return false; + + return true; +} + +static int mount_add_default_dependencies(Mount *m) { + MountParameters *p; + const char *after; + int r; + + assert(m); + + if (!UNIT(m)->default_dependencies) + return 0; + + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) + return 0; + + /* We do not add any default dependencies to /, /usr or + * /run/initramfs/, since they are guaranteed to stay + * mounted the whole time, since our system is on it. + * Also, don't bother with anything mounted below virtual + * file systems, it's also going to be virtual, and hence + * not worth the effort. */ + if (PATH_IN_SET(m->where, "/", "/usr") || + path_startswith(m->where, "/run/initramfs") || + path_startswith(m->where, "/proc") || + path_startswith(m->where, "/sys") || + path_startswith(m->where, "/dev")) + return 0; + + p = get_mount_parameters(m); + if (!p) + return 0; + + if (mount_is_network(p)) { + /* 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. */ + + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true); + if (r < 0) + return r; + + /* 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); + if (r < 0) + return r; + } + + return 0; +} + +static int mount_verify(Mount *m) { + _cleanup_free_ char *e = NULL; + int r; + + assert(m); + + if (UNIT(m)->load_state != UNIT_LOADED) + return 0; + + if (!m->from_fragment && !m->from_proc_self_mountinfo) + return -ENOENT; + + r = unit_name_from_path(m->where, ".mount", &e); + if (r < 0) + return log_unit_error_errno(UNIT(m), r, "Failed to generate unit name from mount path: %m"); + + if (!unit_has_name(UNIT(m), e)) { + log_unit_error(UNIT(m), "Where= setting doesn't match unit name. Refusing."); + return -EINVAL; + } + + if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) { + log_unit_error(UNIT(m), "Cannot create mount unit for API file system %s. Refusing.", m->where); + return -EINVAL; + } + + if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { + log_unit_error(UNIT(m), "What= setting is missing. Refusing."); + return -EBADMSG; + } + + if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) { + log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int mount_add_extras(Mount *m) { + Unit *u = UNIT(m); + int r; + + assert(m); + + if (u->fragment_path) + m->from_fragment = true; + + if (!m->where) { + r = unit_name_to_path(u->id, &m->where); + if (r < 0) + return r; + } + + path_kill_slashes(m->where); + + if (!u->description) { + r = unit_set_description(u, m->where); + if (r < 0) + return r; + } + + r = mount_add_device_links(m); + if (r < 0) + return r; + + r = mount_add_mount_links(m); + if (r < 0) + return r; + + r = mount_add_quota_links(m); + if (r < 0) + return r; + + r = unit_patch_contexts(u); + if (r < 0) + return r; + + r = unit_add_exec_dependencies(u, &m->exec_context); + if (r < 0) + return r; + + r = unit_set_default_slice(u); + if (r < 0) + return r; + + r = mount_add_default_dependencies(m); + if (r < 0) + return r; + + return 0; +} + +static int mount_load(Unit *u) { + Mount *m = MOUNT(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + if (m->from_proc_self_mountinfo) + r = unit_load_fragment_and_dropin_optional(u); + else + r = unit_load_fragment_and_dropin(u); + + if (r < 0) + return r; + + /* This is a new unit? Then let's add in some extras */ + if (u->load_state == UNIT_LOADED) { + r = mount_add_extras(m); + if (r < 0) + return r; + } + + return mount_verify(m); +} + +static void mount_set_state(Mount *m, MountState state) { + MountState old_state; + assert(m); + + old_state = m->state; + m->state = state; + + if (!mount_state_active(state)) { + m->timer_event_source = sd_event_source_unref(m->timer_event_source); + mount_unwatch_control_pid(m); + m->control_command = NULL; + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; + } + + if (state != old_state) + log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); + + unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS); + m->reload_result = MOUNT_SUCCESS; +} + +static int mount_coldplug(Unit *u) { + Mount *m = MOUNT(u); + MountState new_state = MOUNT_DEAD; + int r; + + assert(m); + assert(m->state == MOUNT_DEAD); + + if (m->deserialized_state != m->state) + new_state = m->deserialized_state; + else if (m->from_proc_self_mountinfo) + new_state = MOUNT_MOUNTED; + + if (new_state == m->state) + return 0; + + if (m->control_pid > 0 && + pid_is_unwaited(m->control_pid) && + mount_state_active(new_state)) { + + r = unit_watch_pid(UNIT(m), m->control_pid); + if (r < 0) + return r; + + r = mount_arm_timer(m, usec_add(u->state_change_timestamp.monotonic, m->timeout_usec)); + if (r < 0) + return r; + } + + mount_set_state(m, new_state); + return 0; +} + +static void mount_dump(Unit *u, FILE *f, const char *prefix) { + Mount *m = MOUNT(u); + MountParameters *p; + + assert(m); + assert(f); + + p = get_mount_parameters(m); + + fprintf(f, + "%sMount State: %s\n" + "%sResult: %s\n" + "%sWhere: %s\n" + "%sWhat: %s\n" + "%sFile System Type: %s\n" + "%sOptions: %s\n" + "%sFrom /proc/self/mountinfo: %s\n" + "%sFrom fragment: %s\n" + "%sDirectoryMode: %04o\n", + prefix, mount_state_to_string(m->state), + prefix, mount_result_to_string(m->result), + prefix, m->where, + prefix, p ? strna(p->what) : "n/a", + prefix, p ? strna(p->fstype) : "n/a", + prefix, p ? strna(p->options) : "n/a", + prefix, yes_no(m->from_proc_self_mountinfo), + prefix, yes_no(m->from_fragment), + prefix, m->directory_mode); + + if (m->control_pid > 0) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, m->control_pid); + + exec_context_dump(&m->exec_context, f, prefix); + kill_context_dump(&m->kill_context, f, prefix); +} + +static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { + pid_t pid; + int r; + ExecParameters exec_params = { + .apply_permissions = true, + .apply_chroot = true, + .apply_tty_stdin = true, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, + }; + + assert(m); + assert(c); + assert(_pid); + + (void) unit_realize_cgroup(UNIT(m)); + if (m->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(m)); + m->reset_cpu_usage = false; + } + + r = unit_setup_exec_runtime(UNIT(m)); + if (r < 0) + return r; + + r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec)); + if (r < 0) + return r; + + exec_params.environment = UNIT(m)->manager->environment; + exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; + exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; + exec_params.cgroup_path = UNIT(m)->cgroup_path; + exec_params.cgroup_delegate = m->cgroup_context.delegate; + exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(m)->manager); + + r = exec_spawn(UNIT(m), + c, + &m->exec_context, + &exec_params, + m->exec_runtime, + &pid); + if (r < 0) + return r; + + r = unit_watch_pid(UNIT(m), pid); + if (r < 0) + /* FIXME: we need to do something here */ + return r; + + *_pid = pid; + + return 0; +} + +static void mount_enter_dead(Mount *m, MountResult f) { + assert(m); + + if (f != MOUNT_SUCCESS) + m->result = f; + + exec_runtime_destroy(m->exec_runtime); + m->exec_runtime = exec_runtime_unref(m->exec_runtime); + + exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager)); + + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); +} + +static void mount_enter_mounted(Mount *m, MountResult f) { + assert(m); + + if (f != MOUNT_SUCCESS) + m->result = f; + + mount_set_state(m, MOUNT_MOUNTED); +} + +static void mount_enter_signal(Mount *m, MountState state, MountResult f) { + int r; + + assert(m); + + if (f != MOUNT_SUCCESS) + m->result = f; + + r = unit_kill_context( + UNIT(m), + &m->kill_context, + (state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM) ? + KILL_KILL : KILL_TERMINATE, + -1, + m->control_pid, + false); + if (r < 0) + goto fail; + + if (r > 0) { + r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec)); + if (r < 0) + goto fail; + + mount_set_state(m, state); + } else if (state == MOUNT_REMOUNTING_SIGTERM) + mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_SUCCESS); + else if (state == MOUNT_REMOUNTING_SIGKILL) + mount_enter_mounted(m, MOUNT_SUCCESS); + else if (state == MOUNT_MOUNTING_SIGTERM) + mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_SUCCESS); + else if (state == MOUNT_UNMOUNTING_SIGTERM) + mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS); + else + mount_enter_dead(m, MOUNT_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m"); + + if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) + mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); + else + mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); +} + +static void mount_enter_unmounting(Mount *m) { + int r; + + assert(m); + + /* Start counting our attempts */ + if (!IN_SET(m->state, + MOUNT_UNMOUNTING, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_UNMOUNTING_SIGKILL)) + m->n_retry_umount = 0; + + m->control_command_id = MOUNT_EXEC_UNMOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; + + r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL); + if (r < 0) + goto fail; + + mount_unwatch_control_pid(m); + + r = mount_spawn(m, m->control_command, &m->control_pid); + if (r < 0) + goto fail; + + mount_set_state(m, MOUNT_UNMOUNTING); + + return; + +fail: + log_unit_warning_errno(UNIT(m), r, "Failed to run 'umount' task: %m"); + 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; + + assert(m); + + m->control_command_id = MOUNT_EXEC_MOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; + + r = unit_fail_if_symlink(UNIT(m), m->where); + if (r < 0) + goto fail; + + (void) mkdir_p_label(m->where, m->directory_mode); + + unit_warn_if_dir_nonempty(UNIT(m), m->where); + + /* Create the source directory for bind-mounts if needed */ + p = get_mount_parameters_fragment(m); + if (p && mount_is_bind(p)) + (void) mkdir_p_label(p->what, m->directory_mode); + + if (m->from_fragment) { + _cleanup_free_ char *opts = NULL; + + r = mount_get_opts(m, &opts); + if (r < 0) + goto fail; + + r = exec_command_set(m->control_command, MOUNT_PATH, + m->parameters_fragment.what, m->where, NULL); + if (r >= 0 && m->sloppy_options) + r = exec_command_append(m->control_command, "-s", NULL); + if (r >= 0 && m->parameters_fragment.fstype) + r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + if (r >= 0 && !isempty(opts)) + r = exec_command_append(m->control_command, "-o", opts, NULL); + } else + r = -ENOENT; + + if (r < 0) + goto fail; + + mount_unwatch_control_pid(m); + + r = mount_spawn(m, m->control_command, &m->control_pid); + if (r < 0) + goto fail; + + mount_set_state(m, MOUNT_MOUNTING); + + return; + +fail: + log_unit_warning_errno(UNIT(m), r, "Failed to run 'mount' task: %m"); + mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); +} + +static void mount_enter_remounting(Mount *m) { + int r; + + assert(m); + + m->control_command_id = MOUNT_EXEC_REMOUNT; + m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; + + if (m->from_fragment) { + const char *o; + + if (m->parameters_fragment.options) + o = strjoina("remount,", m->parameters_fragment.options); + else + o = "remount"; + + r = exec_command_set(m->control_command, MOUNT_PATH, + m->parameters_fragment.what, m->where, + "-o", o, NULL); + if (r >= 0 && m->sloppy_options) + r = exec_command_append(m->control_command, "-s", NULL); + if (r >= 0 && m->parameters_fragment.fstype) + r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + } else + r = -ENOENT; + + if (r < 0) + goto fail; + + mount_unwatch_control_pid(m); + + r = mount_spawn(m, m->control_command, &m->control_pid); + if (r < 0) + goto fail; + + mount_set_state(m, MOUNT_REMOUNTING); + + return; + +fail: + log_unit_warning_errno(UNIT(m), r, "Failed to run 'remount' task: %m"); + m->reload_result = MOUNT_FAILURE_RESOURCES; + mount_enter_mounted(m, MOUNT_SUCCESS); +} + +static int mount_start(Unit *u) { + Mount *m = MOUNT(u); + int r; + + assert(m); + + /* We cannot fulfill this request right now, try again later + * please! */ + if (m->state == MOUNT_UNMOUNTING || + m->state == MOUNT_UNMOUNTING_SIGTERM || + m->state == MOUNT_UNMOUNTING_SIGKILL || + m->state == MOUNT_MOUNTING_SIGTERM || + m->state == MOUNT_MOUNTING_SIGKILL) + return -EAGAIN; + + /* Already on it! */ + if (m->state == MOUNT_MOUNTING) + return 0; + + assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); + + r = unit_start_limit_test(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + + m->result = MOUNT_SUCCESS; + m->reload_result = MOUNT_SUCCESS; + m->reset_cpu_usage = true; + + mount_enter_mounting(m); + return 1; +} + +static int mount_stop(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + /* Already on it */ + if (m->state == MOUNT_UNMOUNTING || + m->state == MOUNT_UNMOUNTING_SIGKILL || + m->state == MOUNT_UNMOUNTING_SIGTERM || + m->state == MOUNT_MOUNTING_SIGTERM || + m->state == MOUNT_MOUNTING_SIGKILL) + return 0; + + assert(m->state == MOUNT_MOUNTING || + m->state == MOUNT_MOUNTING_DONE || + m->state == MOUNT_MOUNTED || + m->state == MOUNT_REMOUNTING || + m->state == MOUNT_REMOUNTING_SIGTERM || + m->state == MOUNT_REMOUNTING_SIGKILL); + + mount_enter_unmounting(m); + return 1; +} + +static int mount_reload(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + if (m->state == MOUNT_MOUNTING_DONE) + return -EAGAIN; + + assert(m->state == MOUNT_MOUNTED); + + mount_enter_remounting(m); + return 1; +} + +static int mount_serialize(Unit *u, FILE *f, FDSet *fds) { + Mount *m = MOUNT(u); + + assert(m); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", mount_state_to_string(m->state)); + unit_serialize_item(u, f, "result", mount_result_to_string(m->result)); + unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result)); + + if (m->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, m->control_pid); + + if (m->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id)); + + return 0; +} + +static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Mount *m = MOUNT(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + MountState state; + + if ((state = mount_state_from_string(value)) < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + m->deserialized_state = state; + } else if (streq(key, "result")) { + MountResult f; + + f = mount_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != MOUNT_SUCCESS) + m->result = f; + + } else if (streq(key, "reload-result")) { + MountResult f; + + f = mount_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse reload result value: %s", value); + else if (f != MOUNT_SUCCESS) + m->reload_result = f; + + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse control-pid value: %s", value); + else + m->control_pid = pid; + } else if (streq(key, "control-command")) { + MountExecCommand id; + + id = mount_exec_command_from_string(value); + if (id < 0) + log_unit_debug(u, "Failed to parse exec-command value: %s", value); + else { + m->control_command_id = id; + m->control_command = m->exec_command + id; + } + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState mount_active_state(Unit *u) { + assert(u); + + return state_translation_table[MOUNT(u)->state]; +} + +_pure_ static const char *mount_sub_state_to_string(Unit *u) { + assert(u); + + return mount_state_to_string(MOUNT(u)->state); +} + +_pure_ static bool mount_check_gc(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->from_proc_self_mountinfo; +} + +static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Mount *m = MOUNT(u); + MountResult f; + + assert(m); + assert(pid >= 0); + + if (pid != m->control_pid) + return; + + m->control_pid = 0; + + if (is_clean_exit(code, status, NULL)) + f = MOUNT_SUCCESS; + else if (code == CLD_EXITED) + f = MOUNT_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = MOUNT_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = MOUNT_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); + + if (f != MOUNT_SUCCESS) + m->result = f; + + if (m->control_command) { + exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status); + + m->control_command = NULL; + m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; + } + + log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, + "Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status); + + /* Note that mount(8) returning and the kernel sending us a + * mount table change event might happen out-of-order. If an + * operation succeed we assume the kernel will follow soon too + * and already change into the resulting state. If it fails + * we check if the kernel still knows about the mount. and + * change state accordingly. */ + + switch (m->state) { + + case MOUNT_MOUNTING: + case MOUNT_MOUNTING_DONE: + case MOUNT_MOUNTING_SIGKILL: + case MOUNT_MOUNTING_SIGTERM: + + if (f == MOUNT_SUCCESS) + mount_enter_mounted(m, f); + else if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, f); + else + mount_enter_dead(m, f); + break; + + case MOUNT_REMOUNTING: + case MOUNT_REMOUNTING_SIGKILL: + case MOUNT_REMOUNTING_SIGTERM: + + m->reload_result = f; + if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, MOUNT_SUCCESS); + else + mount_enter_dead(m, MOUNT_SUCCESS); + + break; + + case MOUNT_UNMOUNTING: + case MOUNT_UNMOUNTING_SIGKILL: + case MOUNT_UNMOUNTING_SIGTERM: + + if (f == MOUNT_SUCCESS) { + + if (m->from_proc_self_mountinfo) { + + /* Still a mount point? If so, let's + * try again. Most likely there were + * multiple mount points stacked on + * top of each other. Note that due to + * the io event priority logic we can + * be sure the new mountinfo is loaded + * before we process the SIGCHLD for + * the mount command. */ + + if (m->n_retry_umount < RETRY_UMOUNT_MAX) { + log_unit_debug(u, "Mount still present, trying again."); + m->n_retry_umount++; + mount_enter_unmounting(m); + } else { + log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount); + mount_enter_mounted(m, f); + } + } else + mount_enter_dead(m, f); + + } else if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, f); + else + mount_enter_dead(m, f); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); +} + +static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + Mount *m = MOUNT(userdata); + + assert(m); + assert(m->timer_event_source == source); + + switch (m->state) { + + case MOUNT_MOUNTING: + case MOUNT_MOUNTING_DONE: + log_unit_warning(UNIT(m), "Mounting timed out. Stopping."); + mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); + break; + + case MOUNT_REMOUNTING: + log_unit_warning(UNIT(m), "Remounting timed out. Stopping."); + m->reload_result = MOUNT_FAILURE_TIMEOUT; + mount_enter_mounted(m, MOUNT_SUCCESS); + break; + + case MOUNT_UNMOUNTING: + log_unit_warning(UNIT(m), "Unmounting timed out. Stopping."); + mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); + break; + + case MOUNT_MOUNTING_SIGTERM: + if (m->kill_context.send_sigkill) { + log_unit_warning(UNIT(m), "Mounting timed out. Killing."); + mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(m), "Mounting timed out. Skipping SIGKILL. Ignoring."); + + if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); + else + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); + } + break; + + case MOUNT_REMOUNTING_SIGTERM: + if (m->kill_context.send_sigkill) { + log_unit_warning(UNIT(m), "Remounting timed out. Killing."); + mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(m), "Remounting timed out. Skipping SIGKILL. Ignoring."); + + if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); + else + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); + } + break; + + case MOUNT_UNMOUNTING_SIGTERM: + if (m->kill_context.send_sigkill) { + log_unit_warning(UNIT(m), "Unmounting timed out. Killing."); + mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(m), "Unmounting timed out. Skipping SIGKILL. Ignoring."); + + if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); + else + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); + } + break; + + case MOUNT_MOUNTING_SIGKILL: + case MOUNT_REMOUNTING_SIGKILL: + case MOUNT_UNMOUNTING_SIGKILL: + log_unit_warning(UNIT(m),"Mount process still around after SIGKILL. Ignoring."); + + if (m->from_proc_self_mountinfo) + mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT); + else + mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +static int mount_setup_unit( + Manager *m, + const char *what, + const char *where, + const char *options, + const char *fstype, + bool set_flags) { + + _cleanup_free_ char *e = NULL, *w = NULL, *o = NULL, *f = NULL; + bool load_extras = false; + MountParameters *p; + bool delete, changed = false; + Unit *u; + int r; + + assert(m); + assert(what); + assert(where); + assert(options); + assert(fstype); + + /* Ignore API mount points. They should never be referenced in + * dependencies ever. */ + if (mount_point_is_api(where) || mount_point_ignore(where)) + return 0; + + if (streq(fstype, "autofs")) + return 0; + + /* probably some kind of swap, ignore */ + if (!is_path(where)) + return 0; + + r = unit_name_from_path(where, ".mount", &e); + if (r < 0) + return r; + + u = manager_get_unit(m, e); + if (!u) { + delete = true; + + u = unit_new(m, sizeof(Mount)); + if (!u) + return log_oom(); + + r = unit_add_name(u, e); + if (r < 0) + goto fail; + + MOUNT(u)->where = strdup(where); + if (!MOUNT(u)->where) { + r = -ENOMEM; + goto fail; + } + + u->source_path = strdup("/proc/self/mountinfo"); + if (!u->source_path) { + r = -ENOMEM; + goto fail; + } + + if (MANAGER_IS_SYSTEM(m)) { + const char* target; + + target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; + r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true); + if (r < 0) + goto fail; + + if (should_umount(MOUNT(u))) { + r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + goto fail; + } + } + + unit_add_to_load_queue(u); + changed = true; + } else { + delete = false; + + if (!MOUNT(u)->where) { + MOUNT(u)->where = strdup(where); + if (!MOUNT(u)->where) { + r = -ENOMEM; + goto fail; + } + } + + if (MANAGER_IS_SYSTEM(m) && + mount_needs_network(options, fstype)) { + /* _netdev option may have shown up late, or on a + * remount. Add remote-fs dependencies, even though + * local-fs ones may already be there. */ + unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true); + load_extras = true; + } + + if (u->load_state == UNIT_NOT_FOUND) { + u->load_state = UNIT_LOADED; + u->load_error = 0; + + /* Load in the extras later on, after we + * finished initialization of the unit */ + load_extras = true; + changed = true; + } + } + + w = strdup(what); + o = strdup(options); + f = strdup(fstype); + if (!w || !o || !f) { + r = -ENOMEM; + goto fail; + } + + p = &MOUNT(u)->parameters_proc_self_mountinfo; + + changed = changed || + !streq_ptr(p->options, options) || + !streq_ptr(p->what, what) || + !streq_ptr(p->fstype, fstype); + + if (set_flags) { + MOUNT(u)->is_mounted = true; + MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; + MOUNT(u)->just_changed = changed; + } + + MOUNT(u)->from_proc_self_mountinfo = true; + + free(p->what); + p->what = w; + w = NULL; + + free(p->options); + p->options = o; + o = NULL; + + free(p->fstype); + p->fstype = f; + f = NULL; + + if (load_extras) { + r = mount_add_extras(MOUNT(u)); + if (r < 0) + goto fail; + } + + if (changed) + unit_add_to_dbus_queue(u); + + return 0; + +fail: + log_warning_errno(r, "Failed to set up mount unit: %m"); + + if (delete && u) + unit_free(u); + + return r; +} + +static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { + _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; + int r = 0; + + assert(m); + + t = mnt_new_table(); + if (!t) + return log_oom(); + + i = mnt_new_iter(MNT_ITER_FORWARD); + if (!i) + return log_oom(); + + r = mnt_table_parse_mtab(t, NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); + + r = 0; + for (;;) { + const char *device, *path, *options, *fstype; + _cleanup_free_ char *d = NULL, *p = NULL; + struct libmnt_fs *fs; + int k; + + k = mnt_table_next_fs(t, i, &fs); + if (k == 1) + break; + if (k < 0) + return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m"); + + device = mnt_fs_get_source(fs); + path = mnt_fs_get_target(fs); + options = mnt_fs_get_options(fs); + fstype = mnt_fs_get_fstype(fs); + + if (!device || !path) + continue; + + if (cunescape(device, UNESCAPE_RELAX, &d) < 0) + return log_oom(); + + if (cunescape(path, UNESCAPE_RELAX, &p) < 0) + return log_oom(); + + (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags); + + k = mount_setup_unit(m, d, p, options, fstype, set_flags); + if (r == 0 && k < 0) + r = k; + } + + return r; +} + +static void mount_shutdown(Manager *m) { + + assert(m); + + m->mount_event_source = sd_event_source_unref(m->mount_event_source); + + mnt_unref_monitor(m->mount_monitor); + m->mount_monitor = NULL; +} + +static int mount_get_timeout(Unit *u, usec_t *timeout) { + Mount *m = MOUNT(u); + usec_t t; + int r; + + if (!m->timer_event_source) + return 0; + + r = sd_event_source_get_time(m->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +static void mount_enumerate(Manager *m) { + int r; + + assert(m); + + mnt_init_debug(0); + + if (!m->mount_monitor) { + int fd; + + m->mount_monitor = mnt_new_monitor(); + if (!m->mount_monitor) { + log_oom(); + goto fail; + } + + r = mnt_monitor_enable_kernel(m->mount_monitor, 1); + 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) { + 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) { + 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) { + 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) { + 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"); + } + + r = mount_load_proc_self_mountinfo(m, false); + if (r < 0) + goto fail; + + return; + +fail: + mount_shutdown(m); +} + +static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + _cleanup_set_free_ Set *around = NULL, *gone = NULL; + Manager *m = userdata; + const char *what; + Iterator i; + Unit *u; + int r; + + assert(m); + assert(revents & EPOLLIN); + + if (fd == mnt_monitor_get_fd(m->mount_monitor)) { + bool rescan = false; + + /* Drain all events and verify that the event is valid. + * + * Note that libmount also monitors /run/mount mkdir if the + * directory does not exist yet. The mkdir may generate event + * which is irrelevant for us. + * + * error: r < 0; valid: r == 0, false positive: rc == 1 */ + do { + r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); + if (r == 0) + rescan = true; + else if (r < 0) + return log_error_errno(r, "Failed to drain libmount events"); + } while (r == 0); + + log_debug("libmount event [rescan: %s]", yes_no(rescan)); + if (!rescan) + return 0; + } + + r = mount_load_proc_self_mountinfo(m, true); + if (r < 0) { + /* Reset flags, just in case, for later calls */ + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { + Mount *mount = MOUNT(u); + + mount->is_mounted = mount->just_mounted = mount->just_changed = false; + } + + return 0; + } + + manager_dispatch_load_queue(m); + + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { + Mount *mount = MOUNT(u); + + if (!mount->is_mounted) { + + /* A mount point is not around right now. It + * might be gone, or might never have + * existed. */ + + if (mount->from_proc_self_mountinfo && + mount->parameters_proc_self_mountinfo.what) { + + /* Remember that this device might just have disappeared */ + if (set_ensure_allocated(&gone, &string_hash_ops) < 0 || + set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0) + log_oom(); /* we don't care too much about OOM here... */ + } + + mount->from_proc_self_mountinfo = false; + + switch (mount->state) { + + case MOUNT_MOUNTED: + /* This has just been unmounted by + * somebody else, follow the state + * change. */ + mount->result = MOUNT_SUCCESS; /* make sure we forget any earlier umount failures */ + mount_enter_dead(mount, MOUNT_SUCCESS); + break; + + default: + break; + } + + } else if (mount->just_mounted || mount->just_changed) { + + /* A mount point was added or changed */ + + switch (mount->state) { + + case MOUNT_DEAD: + case MOUNT_FAILED: + /* This has just been mounted by + * somebody else, follow the state + * change. */ + mount_enter_mounted(mount, MOUNT_SUCCESS); + break; + + case MOUNT_MOUNTING: + mount_set_state(mount, MOUNT_MOUNTING_DONE); + break; + + default: + /* Nothing really changed, but let's + * issue an notification call + * nonetheless, in case somebody is + * waiting for this. (e.g. file system + * ro/rw remounts.) */ + mount_set_state(mount, mount->state); + break; + } + } + + if (mount->is_mounted && + mount->from_proc_self_mountinfo && + mount->parameters_proc_self_mountinfo.what) { + + if (set_ensure_allocated(&around, &string_hash_ops) < 0 || + set_put(around, mount->parameters_proc_self_mountinfo.what) < 0) + log_oom(); + } + + /* Reset the flags for later calls */ + mount->is_mounted = mount->just_mounted = mount->just_changed = false; + } + + SET_FOREACH(what, gone, i) { + if (set_contains(around, what)) + continue; + + /* Let the device units know that the device is no longer mounted */ + (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true); + } + + return 0; +} + +static void mount_reset_failed(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + if (m->state == MOUNT_FAILED) + mount_set_state(m, MOUNT_DEAD); + + m->result = MOUNT_SUCCESS; + m->reload_result = MOUNT_SUCCESS; +} + +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 int mount_control_pid(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->control_pid; +} + +static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { + [MOUNT_EXEC_MOUNT] = "ExecMount", + [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", + [MOUNT_EXEC_REMOUNT] = "ExecRemount", +}; + +DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand); + +static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { + [MOUNT_SUCCESS] = "success", + [MOUNT_FAILURE_RESOURCES] = "resources", + [MOUNT_FAILURE_TIMEOUT] = "timeout", + [MOUNT_FAILURE_EXIT_CODE] = "exit-code", + [MOUNT_FAILURE_SIGNAL] = "signal", + [MOUNT_FAILURE_CORE_DUMP] = "core-dump", + [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); + +const UnitVTable mount_vtable = { + .object_size = sizeof(Mount), + .exec_context_offset = offsetof(Mount, exec_context), + .cgroup_context_offset = offsetof(Mount, cgroup_context), + .kill_context_offset = offsetof(Mount, kill_context), + .exec_runtime_offset = offsetof(Mount, exec_runtime), + + .sections = + "Unit\0" + "Mount\0" + "Install\0", + .private_section = "Mount", + + .init = mount_init, + .load = mount_load, + .done = mount_done, + + .coldplug = mount_coldplug, + + .dump = mount_dump, + + .start = mount_start, + .stop = mount_stop, + .reload = mount_reload, + + .kill = mount_kill, + + .serialize = mount_serialize, + .deserialize_item = mount_deserialize_item, + + .active_state = mount_active_state, + .sub_state_to_string = mount_sub_state_to_string, + + .check_gc = mount_check_gc, + + .sigchld_event = mount_sigchld_event, + + .reset_failed = mount_reset_failed, + + .control_pid = mount_control_pid, + + .bus_vtable = bus_mount_vtable, + .bus_set_property = bus_mount_set_property, + .bus_commit_properties = bus_mount_commit_properties, + + .get_timeout = mount_get_timeout, + + .can_transient = true, + + .enumerate = mount_enumerate, + .shutdown = mount_shutdown, + + .status_message_formats = { + .starting_stopping = { + [0] = "Mounting %s...", + [1] = "Unmounting %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Mounted %s.", + [JOB_FAILED] = "Failed to mount %s.", + [JOB_TIMEOUT] = "Timed out mounting %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Unmounted %s.", + [JOB_FAILED] = "Failed unmounting %s.", + [JOB_TIMEOUT] = "Timed out unmounting %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/mount.h b/src/grp-system/libcore/mount.h new file mode 100644 index 0000000000..da529c44f4 --- /dev/null +++ b/src/grp-system/libcore/mount.h @@ -0,0 +1,108 @@ +#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 . +***/ + +typedef struct Mount Mount; + +#include "execute.h" +#include "kill.h" + +typedef enum MountExecCommand { + MOUNT_EXEC_MOUNT, + MOUNT_EXEC_UNMOUNT, + MOUNT_EXEC_REMOUNT, + _MOUNT_EXEC_COMMAND_MAX, + _MOUNT_EXEC_COMMAND_INVALID = -1 +} MountExecCommand; + +typedef enum MountResult { + MOUNT_SUCCESS, + MOUNT_FAILURE_RESOURCES, + MOUNT_FAILURE_TIMEOUT, + MOUNT_FAILURE_EXIT_CODE, + MOUNT_FAILURE_SIGNAL, + MOUNT_FAILURE_CORE_DUMP, + MOUNT_FAILURE_START_LIMIT_HIT, + _MOUNT_RESULT_MAX, + _MOUNT_RESULT_INVALID = -1 +} MountResult; + +typedef struct MountParameters { + char *what; + char *options; + char *fstype; +} MountParameters; + +struct Mount { + Unit meta; + + char *where; + + MountParameters parameters_proc_self_mountinfo; + MountParameters parameters_fragment; + + bool from_proc_self_mountinfo:1; + bool from_fragment:1; + + /* Used while looking for mount points that vanished or got + * added from/to /proc/self/mountinfo */ + bool is_mounted:1; + bool just_mounted:1; + bool just_changed:1; + + bool reset_cpu_usage:1; + + bool sloppy_options; + + MountResult result; + MountResult reload_result; + + mode_t directory_mode; + + usec_t timeout_usec; + + ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX]; + + ExecContext exec_context; + KillContext kill_context; + CGroupContext cgroup_context; + + ExecRuntime *exec_runtime; + + MountState state, deserialized_state; + + ExecCommand* control_command; + MountExecCommand control_command_id; + pid_t control_pid; + + sd_event_source *timer_event_source; + + unsigned n_retry_umount; +}; + +extern const UnitVTable mount_vtable; + +void mount_fd_event(Manager *m, int events); + +const char* mount_exec_command_to_string(MountExecCommand i) _const_; +MountExecCommand mount_exec_command_from_string(const char *s) _pure_; + +const char* mount_result_to_string(MountResult i) _const_; +MountResult mount_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/namespace.c b/src/grp-system/libcore/namespace.c new file mode 100644 index 0000000000..f76a0a7fbf --- /dev/null +++ b/src/grp-system/libcore/namespace.c @@ -0,0 +1,665 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/socket-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/dev-setup.h" + +#include "loopback-setup.h" +#include "namespace.h" + +#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC) + +typedef enum MountMode { + /* This is ordered by priority! */ + INACCESSIBLE, + READONLY, + PRIVATE_TMP, + PRIVATE_VAR_TMP, + PRIVATE_DEV, + READWRITE +} MountMode; + +typedef struct BindMount { + const char *path; + MountMode mode; + bool done; + bool ignore; +} BindMount; + +static int append_mounts(BindMount **p, char **strv, MountMode mode) { + char **i; + + assert(p); + + STRV_FOREACH(i, strv) { + + (*p)->ignore = false; + (*p)->done = false; + + if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') { + (*p)->ignore = true; + (*i)++; + } + + if (!path_is_absolute(*i)) + return -EINVAL; + + (*p)->path = *i; + (*p)->mode = mode; + (*p)++; + } + + return 0; +} + +static int mount_path_compare(const void *a, const void *b) { + const BindMount *p = a, *q = b; + int d; + + d = path_compare(p->path, q->path); + + if (d == 0) { + /* If the paths are equal, check the mode */ + if (p->mode < q->mode) + return -1; + + if (p->mode > q->mode) + return 1; + + return 0; + } + + /* If the paths are not equal, then order prefixes first */ + return d; +} + +static void drop_duplicates(BindMount *m, unsigned *n) { + BindMount *f, *t, *previous; + + assert(m); + assert(n); + + for (f = m, t = m, previous = NULL; f < m+*n; f++) { + + /* The first one wins */ + if (previous && path_equal(f->path, previous->path)) + continue; + + *t = *f; + + previous = t; + + t++; + } + + *n = t - m; +} + +static int mount_dev(BindMount *m) { + static const char devnodes[] = + "/dev/null\0" + "/dev/zero\0" + "/dev/full\0" + "/dev/random\0" + "/dev/urandom\0" + "/dev/tty\0"; + + char temporary_mount[] = "/tmp/namespace-dev-XXXXXX"; + const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL; + _cleanup_umask_ mode_t u; + int r; + + assert(m); + + u = umask(0000); + + if (!mkdtemp(temporary_mount)) + return -errno; + + dev = strjoina(temporary_mount, "/dev"); + (void) mkdir(dev, 0755); + if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) { + r = -errno; + goto fail; + } + + devpts = strjoina(temporary_mount, "/dev/pts"); + (void) mkdir(devpts, 0755); + if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) { + r = -errno; + goto fail; + } + + devptmx = strjoina(temporary_mount, "/dev/ptmx"); + if (symlink("pts/ptmx", devptmx) < 0) { + r = -errno; + goto fail; + } + + devshm = strjoina(temporary_mount, "/dev/shm"); + (void) mkdir(devshm, 01777); + r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL); + if (r < 0) { + r = -errno; + goto fail; + } + + devmqueue = strjoina(temporary_mount, "/dev/mqueue"); + (void) mkdir(devmqueue, 0755); + (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL); + + devhugepages = strjoina(temporary_mount, "/dev/hugepages"); + (void) mkdir(devhugepages, 0755); + (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL); + + devlog = strjoina(temporary_mount, "/dev/log"); + (void) symlink("/run/systemd/journal/dev-log", devlog); + + NULSTR_FOREACH(d, devnodes) { + _cleanup_free_ char *dn = NULL; + struct stat st; + + r = stat(d, &st); + if (r < 0) { + + if (errno == ENOENT) + continue; + + r = -errno; + goto fail; + } + + if (!S_ISBLK(st.st_mode) && + !S_ISCHR(st.st_mode)) { + r = -EINVAL; + goto fail; + } + + if (st.st_rdev == 0) + continue; + + dn = strappend(temporary_mount, d); + if (!dn) { + r = -ENOMEM; + goto fail; + } + + mac_selinux_create_file_prepare(d, st.st_mode); + r = mknod(dn, st.st_mode, st.st_rdev); + mac_selinux_create_file_clear(); + + if (r < 0) { + r = -errno; + goto fail; + } + } + + dev_setup(temporary_mount, UID_INVALID, GID_INVALID); + + /* Create the /dev directory if missing. It is more likely to be + * missing when the service is started with RootDirectory. This is + * consistent with mount units creating the mount points when missing. + */ + (void) mkdir_p_label(m->path, 0755); + + /* Unmount everything in old /dev */ + umount_recursive(m->path, 0); + if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) { + r = -errno; + goto fail; + } + + rmdir(dev); + rmdir(temporary_mount); + + return 0; + +fail: + if (devpts) + umount(devpts); + + if (devshm) + umount(devshm); + + if (devhugepages) + umount(devhugepages); + + if (devmqueue) + umount(devmqueue); + + umount(dev); + rmdir(dev); + rmdir(temporary_mount); + + return r; +} + +static int apply_mount( + BindMount *m, + const char *tmp_dir, + const char *var_tmp_dir) { + + const char *what; + int r; + struct stat target; + + assert(m); + + switch (m->mode) { + + case INACCESSIBLE: + + /* First, get rid of everything that is below if there + * is anything... Then, overmount it with an + * inaccessible path. */ + umount_recursive(m->path, 0); + + if (lstat(m->path, &target) < 0) { + if (m->ignore && errno == ENOENT) + return 0; + return -errno; + } + + what = mode_to_inaccessible_node(target.st_mode); + if (!what) { + log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed"); + return -ELOOP; + } + break; + case READONLY: + case READWRITE: + /* Nothing to mount here, we just later toggle the + * MS_RDONLY bit for the mount point */ + return 0; + + case PRIVATE_TMP: + what = tmp_dir; + break; + + case PRIVATE_VAR_TMP: + what = var_tmp_dir; + break; + + case PRIVATE_DEV: + return mount_dev(m); + + default: + assert_not_reached("Unknown mode"); + } + + assert(what); + + r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); + if (r >= 0) { + log_debug("Successfully mounted %s to %s", what, m->path); + return r; + } else { + if (m->ignore && errno == ENOENT) + return 0; + return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path); + } +} + +static int make_read_only(BindMount *m) { + int r; + + assert(m); + + if (IN_SET(m->mode, INACCESSIBLE, READONLY)) + r = bind_remount_recursive(m->path, true); + else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) { + r = bind_remount_recursive(m->path, false); + if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/ + if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) + r = -errno; + } else + r = 0; + + if (m->ignore && r == -ENOENT) + return 0; + + return r; +} + +int setup_namespace( + const char* root_directory, + char** read_write_paths, + char** read_only_paths, + char** inaccessible_paths, + const char* tmp_dir, + const char* var_tmp_dir, + bool private_dev, + ProtectHome protect_home, + ProtectSystem protect_system, + unsigned long mount_flags) { + + BindMount *m, *mounts = NULL; + unsigned n; + int r = 0; + + if (mount_flags == 0) + mount_flags = MS_SHARED; + + if (unshare(CLONE_NEWNS) < 0) + return -errno; + + n = !!tmp_dir + !!var_tmp_dir + + strv_length(read_write_paths) + + strv_length(read_only_paths) + + strv_length(inaccessible_paths) + + private_dev + + (protect_home != PROTECT_HOME_NO ? 3 : 0) + + (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) + + (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0); + + if (n > 0) { + m = mounts = (BindMount *) alloca0(n * sizeof(BindMount)); + r = append_mounts(&m, read_write_paths, READWRITE); + if (r < 0) + return r; + + r = append_mounts(&m, read_only_paths, READONLY); + if (r < 0) + return r; + + r = append_mounts(&m, inaccessible_paths, INACCESSIBLE); + if (r < 0) + return r; + + if (tmp_dir) { + m->path = prefix_roota(root_directory, "/tmp"); + m->mode = PRIVATE_TMP; + m++; + } + + if (var_tmp_dir) { + m->path = prefix_roota(root_directory, "/var/tmp"); + m->mode = PRIVATE_VAR_TMP; + m++; + } + + if (private_dev) { + m->path = prefix_roota(root_directory, "/dev"); + m->mode = PRIVATE_DEV; + m++; + } + + if (protect_home != PROTECT_HOME_NO) { + const char *home_dir, *run_user_dir, *root_dir; + + home_dir = prefix_roota(root_directory, "/home"); + home_dir = strjoina("-", home_dir); + run_user_dir = prefix_roota(root_directory, "/run/user"); + run_user_dir = strjoina("-", run_user_dir); + root_dir = prefix_roota(root_directory, "/root"); + root_dir = strjoina("-", root_dir); + + r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir), + protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE); + if (r < 0) + return r; + } + + if (protect_system != PROTECT_SYSTEM_NO) { + const char *usr_dir, *boot_dir, *etc_dir; + + usr_dir = prefix_roota(root_directory, "/usr"); + boot_dir = prefix_roota(root_directory, "/boot"); + boot_dir = strjoina("-", boot_dir); + etc_dir = prefix_roota(root_directory, "/etc"); + + r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL + ? STRV_MAKE(usr_dir, boot_dir, etc_dir) + : STRV_MAKE(usr_dir, boot_dir), READONLY); + if (r < 0) + return r; + } + + assert(mounts + n == m); + + qsort(mounts, n, sizeof(BindMount), mount_path_compare); + drop_duplicates(mounts, &n); + } + + if (n > 0 || root_directory) { + /* Remount / as SLAVE so that nothing now mounted in the namespace + shows up in the parent */ + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) + return -errno; + } + + if (root_directory) { + /* Turn directory into bind mount */ + if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) + return -errno; + } + + if (n > 0) { + for (m = mounts; m < mounts + n; ++m) { + r = apply_mount(m, tmp_dir, var_tmp_dir); + if (r < 0) + goto fail; + } + + for (m = mounts; m < mounts + n; ++m) { + r = make_read_only(m); + if (r < 0) + goto fail; + } + } + + if (root_directory) { + /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ + r = mount_move_root(root_directory); + + /* at this point, we cannot rollback */ + if (r < 0) + return r; + } + + /* Remount / as the desired mode. Not that this will not + * reestablish propagation from our side to the host, since + * what's disconnected is disconnected. */ + if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) + /* at this point, we cannot rollback */ + return -errno; + + return 0; + +fail: + if (n > 0) { + for (m = mounts; m < mounts + n; ++m) + if (m->done) + (void) umount2(m->path, MNT_DETACH); + } + + return r; +} + +static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { + _cleanup_free_ char *x = NULL; + char bid[SD_ID128_STRING_MAX]; + sd_id128_t boot_id; + int r; + + assert(id); + assert(prefix); + assert(path); + + /* We include the boot id in the directory so that after a + * reboot we can easily identify obsolete directories. */ + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + + x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL); + if (!x) + return -ENOMEM; + + RUN_WITH_UMASK(0077) + if (!mkdtemp(x)) + return -errno; + + RUN_WITH_UMASK(0000) { + char *y; + + y = strjoina(x, "/tmp"); + + if (mkdir(y, 0777 | S_ISVTX) < 0) + return -errno; + } + + *path = x; + x = NULL; + + return 0; +} + +int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { + char *a, *b; + int r; + + assert(id); + assert(tmp_dir); + assert(var_tmp_dir); + + r = setup_one_tmp_dir(id, "/tmp", &a); + if (r < 0) + return r; + + r = setup_one_tmp_dir(id, "/var/tmp", &b); + if (r < 0) { + char *t; + + t = strjoina(a, "/tmp"); + rmdir(t); + rmdir(a); + + free(a); + return r; + } + + *tmp_dir = a; + *var_tmp_dir = b; + + return 0; +} + +int setup_netns(int netns_storage_socket[2]) { + _cleanup_close_ int netns = -1; + int r, q; + + assert(netns_storage_socket); + assert(netns_storage_socket[0] >= 0); + assert(netns_storage_socket[1] >= 0); + + /* We use the passed socketpair as a storage buffer for our + * namespace reference fd. Whatever process runs this first + * shall create a new namespace, all others should just join + * it. To serialize that we use a file lock on the socket + * pair. + * + * It's a bit crazy, but hey, works great! */ + + if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0) + return -errno; + + 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) { + r = -errno; + goto fail; + } + + loopback_setup(); + + netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (netns < 0) { + r = -errno; + goto fail; + } + + r = 1; + + } 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; + } + + r = 0; + } + + q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT); + if (q < 0) { + r = q; + goto fail; + } + +fail: + (void) lockf(netns_storage_socket[0], F_ULOCK, 0); + return r; +} + +static const char *const protect_home_table[_PROTECT_HOME_MAX] = { + [PROTECT_HOME_NO] = "no", + [PROTECT_HOME_YES] = "yes", + [PROTECT_HOME_READ_ONLY] = "read-only", +}; + +DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome); + +static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { + [PROTECT_SYSTEM_NO] = "no", + [PROTECT_SYSTEM_YES] = "yes", + [PROTECT_SYSTEM_FULL] = "full", +}; + +DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem); diff --git a/src/grp-system/libcore/namespace.h b/src/grp-system/libcore/namespace.h new file mode 100644 index 0000000000..1ae206efd1 --- /dev/null +++ b/src/grp-system/libcore/namespace.h @@ -0,0 +1,63 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" + +typedef enum ProtectHome { + PROTECT_HOME_NO, + PROTECT_HOME_YES, + PROTECT_HOME_READ_ONLY, + _PROTECT_HOME_MAX, + _PROTECT_HOME_INVALID = -1 +} ProtectHome; + +typedef enum ProtectSystem { + PROTECT_SYSTEM_NO, + PROTECT_SYSTEM_YES, + PROTECT_SYSTEM_FULL, + _PROTECT_SYSTEM_MAX, + _PROTECT_SYSTEM_INVALID = -1 +} ProtectSystem; + +int setup_namespace(const char *chroot, + char **read_write_paths, + char **read_only_paths, + char **inaccessible_paths, + const char *tmp_dir, + const char *var_tmp_dir, + bool private_dev, + ProtectHome protect_home, + ProtectSystem protect_system, + unsigned long mount_flags); + +int setup_tmp_dirs(const char *id, + char **tmp_dir, + char **var_tmp_dir); + +int setup_netns(int netns_storage_socket[2]); + +const char* protect_home_to_string(ProtectHome p) _const_; +ProtectHome protect_home_from_string(const char *s) _pure_; + +const char* protect_system_to_string(ProtectSystem p) _const_; +ProtectSystem protect_system_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/path.c b/src/grp-system/libcore/path.c new file mode 100644 index 0000000000..ee1b9a3480 --- /dev/null +++ b/src/grp-system/libcore/path.c @@ -0,0 +1,785 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "dbus-path.h" +#include "path.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { + [PATH_DEAD] = UNIT_INACTIVE, + [PATH_WAITING] = UNIT_ACTIVE, + [PATH_RUNNING] = UNIT_ACTIVE, + [PATH_FAILED] = UNIT_FAILED +}; + +static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + +int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { + + static const int flags_table[_PATH_TYPE_MAX] = { + [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, + [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, + [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO + }; + + bool exists = false; + char *slash, *oldslash = NULL; + int r; + + assert(s); + assert(s->unit); + assert(handler); + + path_spec_unwatch(s); + + s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (s->inotify_fd < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(s->event_source, "path"); + + /* This assumes the path was passed through path_kill_slashes()! */ + + for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { + char *cut = NULL; + int flags; + char tmp; + + if (slash) { + cut = slash + (slash == s->path); + tmp = *cut; + *cut = '\0'; + + flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + } else + flags = flags_table[s->type]; + + r = inotify_add_watch(s->inotify_fd, s->path, flags); + if (r < 0) { + if (errno == EACCES || errno == ENOENT) { + if (cut) + *cut = tmp; + break; + } + + r = log_warning_errno(errno, "Failed to add watch on %s: %s", s->path, errno == ENOSPC ? "too many watches" : strerror(-r)); + if (cut) + *cut = tmp; + goto fail; + } else { + exists = true; + + /* Path exists, we don't need to watch parent too closely. */ + if (oldslash) { + char *cut2 = oldslash + (oldslash == s->path); + char tmp2 = *cut2; + *cut2 = '\0'; + + (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is we get spurious events. */ + + *cut2 = tmp2; + } + } + + if (cut) + *cut = tmp; + + if (slash) + oldslash = slash; + else { + /* whole path has been iterated over */ + s->primary_wd = r; + break; + } + } + + if (!exists) { + r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path); + /* either EACCESS or ENOENT */ + goto fail; + } + + return 0; + +fail: + path_spec_unwatch(s); + return r; +} + +void path_spec_unwatch(PathSpec *s) { + assert(s); + + s->event_source = sd_event_source_unref(s->event_source); + s->inotify_fd = safe_close(s->inotify_fd); +} + +int path_spec_fd_event(PathSpec *s, uint32_t revents) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + int r = 0; + + if (revents != EPOLLIN) { + log_error("Got invalid poll event on inotify."); + return -EINVAL; + } + + l = read(s->inotify_fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_error_errno(errno, "Failed to read inotify event: %m"); + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && + s->primary_wd == e->wd) + r = 1; + } + + return r; +} + +static bool path_spec_check_good(PathSpec *s, bool initial) { + bool good = false; + + switch (s->type) { + + case PATH_EXISTS: + good = access(s->path, F_OK) >= 0; + break; + + case PATH_EXISTS_GLOB: + good = glob_exists(s->path) > 0; + break; + + case PATH_DIRECTORY_NOT_EMPTY: { + int k; + + k = dir_is_empty(s->path); + good = !(k == -ENOENT || k > 0); + break; + } + + case PATH_CHANGED: + case PATH_MODIFIED: { + bool b; + + b = access(s->path, F_OK) >= 0; + good = !initial && b != s->previous_exists; + s->previous_exists = b; + break; + } + + default: + ; + } + + return good; +} + +static void path_spec_mkdir(PathSpec *s, mode_t mode) { + int r; + + if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) + return; + + r = mkdir_p_label(s->path, mode); + if (r < 0) + log_warning_errno(r, "mkdir(%s) failed: %m", s->path); +} + +static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) { + fprintf(f, + "%s%s: %s\n", + prefix, + path_type_to_string(s->type), + s->path); +} + +void path_spec_done(PathSpec *s) { + assert(s); + assert(s->inotify_fd == -1); + + free(s->path); +} + +static void path_init(Unit *u) { + Path *p = PATH(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + p->directory_mode = 0755; +} + +void path_free_specs(Path *p) { + PathSpec *s; + + assert(p); + + while ((s = p->specs)) { + path_spec_unwatch(s); + LIST_REMOVE(spec, p->specs, s); + path_spec_done(s); + free(s); + } +} + +static void path_done(Unit *u) { + Path *p = PATH(u); + + assert(p); + + path_free_specs(p); +} + +static int path_add_mount_links(Path *p) { + PathSpec *s; + int r; + + assert(p); + + LIST_FOREACH(spec, s, p->specs) { + r = unit_require_mounts_for(UNIT(p), s->path); + if (r < 0) + return r; + } + + return 0; +} + +static int path_verify(Path *p) { + assert(p); + + if (UNIT(p)->load_state != UNIT_LOADED) + return 0; + + if (!p->specs) { + log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int path_add_default_dependencies(Path *p) { + int r; + + assert(p); + + 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 (MANAGER_IS_SYSTEM(UNIT(p)->manager)) { + 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); +} + +static int path_load(Unit *u) { + Path *p = PATH(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + r = unit_load_fragment_and_dropin(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + + if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) + return r; + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; + } + + r = path_add_mount_links(p); + if (r < 0) + return r; + + r = path_add_default_dependencies(p); + if (r < 0) + return r; + } + + return path_verify(p); +} + +static void path_dump(Unit *u, FILE *f, const char *prefix) { + Path *p = PATH(u); + Unit *trigger; + PathSpec *s; + + assert(p); + assert(f); + + trigger = UNIT_TRIGGER(u); + + fprintf(f, + "%sPath State: %s\n" + "%sResult: %s\n" + "%sUnit: %s\n" + "%sMakeDirectory: %s\n" + "%sDirectoryMode: %04o\n", + prefix, path_state_to_string(p->state), + prefix, path_result_to_string(p->result), + prefix, trigger ? trigger->id : "n/a", + prefix, yes_no(p->make_directory), + prefix, p->directory_mode); + + LIST_FOREACH(spec, s, p->specs) + path_spec_dump(s, f, prefix); +} + +static void path_unwatch(Path *p) { + PathSpec *s; + + assert(p); + + LIST_FOREACH(spec, s, p->specs) + path_spec_unwatch(s); +} + +static int path_watch(Path *p) { + int r; + PathSpec *s; + + assert(p); + + LIST_FOREACH(spec, s, p->specs) { + r = path_spec_watch(s, path_dispatch_io); + if (r < 0) + return r; + } + + return 0; +} + +static void path_set_state(Path *p, PathState state) { + PathState old_state; + assert(p); + + old_state = p->state; + p->state = state; + + if (state != PATH_WAITING && + (state != PATH_RUNNING || p->inotify_triggered)) + path_unwatch(p); + + if (state != old_state) + log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state)); + + unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true); +} + +static void path_enter_waiting(Path *p, bool initial, bool recheck); + +static int path_coldplug(Unit *u) { + Path *p = PATH(u); + + assert(p); + assert(p->state == PATH_DEAD); + + if (p->deserialized_state != p->state) { + + if (p->deserialized_state == PATH_WAITING || + p->deserialized_state == PATH_RUNNING) + path_enter_waiting(p, true, true); + else + path_set_state(p, p->deserialized_state); + } + + return 0; +} + +static void path_enter_dead(Path *p, PathResult f) { + assert(p); + + if (f != PATH_SUCCESS) + p->result = f; + + path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); +} + +static void path_enter_running(Path *p) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *trigger; + int r; + + assert(p); + + /* Don't start job if we are supposed to go down */ + if (unit_stop_pending(UNIT(p))) + return; + + trigger = UNIT_TRIGGER(UNIT(p)); + if (!trigger) { + log_unit_error(UNIT(p), "Unit to trigger vanished."); + path_enter_dead(p, PATH_FAILURE_RESOURCES); + return; + } + + r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); + if (r < 0) + goto fail; + + p->inotify_triggered = false; + + r = path_watch(p); + if (r < 0) + goto fail; + + path_set_state(p, PATH_RUNNING); + return; + +fail: + log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r)); + path_enter_dead(p, PATH_FAILURE_RESOURCES); +} + +static bool path_check_good(Path *p, bool initial) { + PathSpec *s; + bool good = false; + + assert(p); + + LIST_FOREACH(spec, s, p->specs) { + good = path_spec_check_good(s, initial); + + if (good) + break; + } + + return good; +} + +static void path_enter_waiting(Path *p, bool initial, bool recheck) { + int r; + + if (recheck) + if (path_check_good(p, initial)) { + log_unit_debug(UNIT(p), "Got triggered."); + path_enter_running(p); + return; + } + + r = path_watch(p); + if (r < 0) + goto fail; + + /* Hmm, so now we have created inotify watches, but the file + * might have appeared/been removed by now, so we must + * recheck */ + + if (recheck) + if (path_check_good(p, false)) { + log_unit_debug(UNIT(p), "Got triggered."); + path_enter_running(p); + return; + } + + path_set_state(p, PATH_WAITING); + return; + +fail: + log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m"); + path_enter_dead(p, PATH_FAILURE_RESOURCES); +} + +static void path_mkdir(Path *p) { + PathSpec *s; + + assert(p); + + if (!p->make_directory) + return; + + LIST_FOREACH(spec, s, p->specs) + path_spec_mkdir(s, p->directory_mode); +} + +static int path_start(Unit *u) { + Path *p = PATH(u); + Unit *trigger; + int r; + + assert(p); + assert(p->state == PATH_DEAD || p->state == PATH_FAILED); + + trigger = UNIT_TRIGGER(u); + if (!trigger || trigger->load_state != UNIT_LOADED) { + log_unit_error(u, "Refusing to start, unit to trigger not loaded."); + return -ENOENT; + } + + r = unit_start_limit_test(u); + if (r < 0) { + path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); + return r; + } + + path_mkdir(p); + + p->result = PATH_SUCCESS; + path_enter_waiting(p, true, true); + + return 1; +} + +static int path_stop(Unit *u) { + Path *p = PATH(u); + + assert(p); + assert(p->state == PATH_WAITING || p->state == PATH_RUNNING); + + path_enter_dead(p, PATH_SUCCESS); + return 1; +} + +static int path_serialize(Unit *u, FILE *f, FDSet *fds) { + Path *p = PATH(u); + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", path_state_to_string(p->state)); + unit_serialize_item(u, f, "result", path_result_to_string(p->result)); + + return 0; +} + +static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Path *p = PATH(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + PathState state; + + state = path_state_from_string(value); + if (state < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + p->deserialized_state = state; + + } else if (streq(key, "result")) { + PathResult f; + + f = path_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != PATH_SUCCESS) + p->result = f; + + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState path_active_state(Unit *u) { + assert(u); + + return state_translation_table[PATH(u)->state]; +} + +_pure_ static const char *path_sub_state_to_string(Unit *u) { + assert(u); + + return path_state_to_string(PATH(u)->state); +} + +static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + PathSpec *s = userdata; + Path *p; + int changed; + + assert(s); + assert(s->unit); + assert(fd >= 0); + + p = PATH(s->unit); + + if (p->state != PATH_WAITING && + p->state != PATH_RUNNING) + return 0; + + /* log_debug("inotify wakeup on %s.", u->id); */ + + LIST_FOREACH(spec, s, p->specs) + if (path_spec_owns_inotify_fd(s, fd)) + break; + + if (!s) { + log_error("Got event on unknown fd."); + goto fail; + } + + changed = path_spec_fd_event(s, revents); + if (changed < 0) + goto fail; + + /* If we are already running, then remember that one event was + * dispatched so that we restart the service only if something + * actually changed on disk */ + p->inotify_triggered = true; + + if (changed) + path_enter_running(p); + else + path_enter_waiting(p, false, true); + + return 0; + +fail: + path_enter_dead(p, PATH_FAILURE_RESOURCES); + return 0; +} + +static void path_trigger_notify(Unit *u, Unit *other) { + Path *p = PATH(u); + + assert(u); + assert(other); + + /* Invoked whenever the unit we trigger changes state or gains + * or loses a job */ + + if (other->load_state != UNIT_LOADED) + return; + + if (p->state == PATH_RUNNING && + UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_unit_debug(UNIT(p), "Got notified about unit deactivation."); + + /* Hmm, so inotify was triggered since the + * last activation, so I guess we need to + * recheck what is going on. */ + path_enter_waiting(p, false, p->inotify_triggered); + } +} + +static void path_reset_failed(Unit *u) { + Path *p = PATH(u); + + assert(p); + + if (p->state == PATH_FAILED) + path_set_state(p, PATH_DEAD); + + p->result = PATH_SUCCESS; +} + +static const char* const path_type_table[_PATH_TYPE_MAX] = { + [PATH_EXISTS] = "PathExists", + [PATH_EXISTS_GLOB] = "PathExistsGlob", + [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty", + [PATH_CHANGED] = "PathChanged", + [PATH_MODIFIED] = "PathModified", +}; + +DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); + +static const char* const path_result_table[_PATH_RESULT_MAX] = { + [PATH_SUCCESS] = "success", + [PATH_FAILURE_RESOURCES] = "resources", + [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); + +const UnitVTable path_vtable = { + .object_size = sizeof(Path), + + .sections = + "Unit\0" + "Path\0" + "Install\0", + + .init = path_init, + .done = path_done, + .load = path_load, + + .coldplug = path_coldplug, + + .dump = path_dump, + + .start = path_start, + .stop = path_stop, + + .serialize = path_serialize, + .deserialize_item = path_deserialize_item, + + .active_state = path_active_state, + .sub_state_to_string = path_sub_state_to_string, + + .trigger_notify = path_trigger_notify, + + .reset_failed = path_reset_failed, + + .bus_vtable = bus_path_vtable +}; diff --git a/src/grp-system/libcore/path.h b/src/grp-system/libcore/path.h new file mode 100644 index 0000000000..4230c8fb99 --- /dev/null +++ b/src/grp-system/libcore/path.h @@ -0,0 +1,93 @@ +#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 . +***/ + +typedef struct Path Path; +typedef struct PathSpec PathSpec; + +#include "unit.h" + +typedef enum PathType { + PATH_EXISTS, + PATH_EXISTS_GLOB, + PATH_DIRECTORY_NOT_EMPTY, + PATH_CHANGED, + PATH_MODIFIED, + _PATH_TYPE_MAX, + _PATH_TYPE_INVALID = -1 +} PathType; + +typedef struct PathSpec { + Unit *unit; + + char *path; + + sd_event_source *event_source; + + LIST_FIELDS(struct PathSpec, spec); + + PathType type; + int inotify_fd; + int primary_wd; + + bool previous_exists; +} PathSpec; + +int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler); +void path_spec_unwatch(PathSpec *s); +int path_spec_fd_event(PathSpec *s, uint32_t events); +void path_spec_done(PathSpec *s); + +static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { + return s->inotify_fd == fd; +} + +typedef enum PathResult { + PATH_SUCCESS, + PATH_FAILURE_RESOURCES, + PATH_FAILURE_START_LIMIT_HIT, + _PATH_RESULT_MAX, + _PATH_RESULT_INVALID = -1 +} PathResult; + +struct Path { + Unit meta; + + LIST_HEAD(PathSpec, specs); + + PathState state, deserialized_state; + + bool inotify_triggered; + + bool make_directory; + mode_t directory_mode; + + PathResult result; +}; + +void path_free_specs(Path *p); + +extern const UnitVTable path_vtable; + +const char* path_type_to_string(PathType i) _const_; +PathType path_type_from_string(const char *s) _pure_; + +const char* path_result_to_string(PathResult i) _const_; +PathResult path_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/scope.c b/src/grp-system/libcore/scope.c new file mode 100644 index 0000000000..8998f751f5 --- /dev/null +++ b/src/grp-system/libcore/scope.c @@ -0,0 +1,622 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" + +#include "dbus-scope.h" +#include "load-dropin.h" +#include "scope.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = { + [SCOPE_DEAD] = UNIT_INACTIVE, + [SCOPE_RUNNING] = UNIT_ACTIVE, + [SCOPE_ABANDONED] = UNIT_ACTIVE, + [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING, + [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING, + [SCOPE_FAILED] = UNIT_FAILED +}; + +static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + +static void scope_init(Unit *u) { + Scope *s = SCOPE(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + s->timeout_stop_usec = u->manager->default_timeout_stop_usec; + u->ignore_on_isolate = true; +} + +static void scope_done(Unit *u) { + Scope *s = SCOPE(u); + + assert(u); + + free(s->controller); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); +} + +static int scope_arm_timer(Scope *s, usec_t usec) { + int r; + + assert(s); + + if (s->timer_event_source) { + r = sd_event_source_set_time(s->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + scope_dispatch_timer, s); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->timer_event_source, "scope-timer"); + + return 0; +} + +static void scope_set_state(Scope *s, ScopeState state) { + ScopeState old_state; + assert(s); + + old_state = s->state; + s->state = state; + + if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) + unit_unwatch_all_pids(UNIT(s)); + + if (state != old_state) + log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state)); + + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); +} + +static int scope_add_default_dependencies(Scope *s) { + int r; + + 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), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); + if (r < 0) + return r; + + return 0; +} + +static int scope_verify(Scope *s) { + assert(s); + + if (UNIT(s)->load_state != UNIT_LOADED) + return 0; + + if (set_isempty(UNIT(s)->pids) && + !MANAGER_IS_RELOADING(UNIT(s)->manager) && + !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { + log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int scope_load(Unit *u) { + Scope *s = SCOPE(u); + int r; + + assert(s); + assert(u->load_state == UNIT_STUB); + + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) + /* Refuse to load non-transient scope units, but allow them while reloading. */ + return -ENOENT; + + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + r = unit_patch_contexts(u); + if (r < 0) + return r; + + r = unit_set_default_slice(u); + if (r < 0) + return r; + + r = scope_add_default_dependencies(s); + if (r < 0) + return r; + } + + return scope_verify(s); +} + +static int scope_coldplug(Unit *u) { + Scope *s = SCOPE(u); + int r; + + assert(s); + assert(s->state == SCOPE_DEAD); + + if (s->deserialized_state == s->state) + return 0; + + if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) { + r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec)); + if (r < 0) + return r; + } + + if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED)) + unit_watch_all_pids(UNIT(s)); + + scope_set_state(s, s->deserialized_state); + return 0; +} + +static void scope_dump(Unit *u, FILE *f, const char *prefix) { + Scope *s = SCOPE(u); + + assert(s); + assert(f); + + fprintf(f, + "%sScope State: %s\n" + "%sResult: %s\n", + prefix, scope_state_to_string(s->state), + prefix, scope_result_to_string(s->result)); + + cgroup_context_dump(&s->cgroup_context, f, prefix); + kill_context_dump(&s->kill_context, f, prefix); +} + +static void scope_enter_dead(Scope *s, ScopeResult f) { + assert(s); + + if (f != SCOPE_SUCCESS) + s->result = f; + + scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); +} + +static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { + bool skip_signal = false; + int r; + + assert(s); + + if (f != SCOPE_SUCCESS) + s->result = f; + + unit_watch_all_pids(UNIT(s)); + + /* If we have a controller set let's ask the controller nicely + * to terminate the scope, instead of us going directly into + * SIGTERM berserk mode */ + if (state == SCOPE_STOP_SIGTERM) + skip_signal = bus_scope_send_request_stop(s) > 0; + + if (!skip_signal) { + r = unit_kill_context( + UNIT(s), + &s->kill_context, + state != SCOPE_STOP_SIGTERM ? KILL_KILL : + s->was_abandoned ? KILL_TERMINATE_AND_LOG : + KILL_TERMINATE, + -1, -1, false); + if (r < 0) + goto fail; + } else + r = 1; + + if (r > 0) { + r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); + if (r < 0) + goto fail; + + scope_set_state(s, state); + } else if (state == SCOPE_STOP_SIGTERM) + scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS); + else + scope_enter_dead(s, SCOPE_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); + + scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); +} + +static int scope_start(Unit *u) { + Scope *s = SCOPE(u); + int r; + + assert(s); + + if (unit_has_name(u, SPECIAL_INIT_SCOPE)) + return -EPERM; + + if (s->state == SCOPE_FAILED) + return -EPERM; + + /* We can't fulfill this right now, please try again later */ + if (s->state == SCOPE_STOP_SIGTERM || + s->state == SCOPE_STOP_SIGKILL) + return -EAGAIN; + + assert(s->state == SCOPE_DEAD); + + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) + return -ENOENT; + + (void) unit_realize_cgroup(u); + (void) unit_reset_cpu_usage(u); + + r = unit_attach_pids_to_cgroup(u); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); + scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); + return r; + } + + s->result = SCOPE_SUCCESS; + + scope_set_state(s, SCOPE_RUNNING); + return 1; +} + +static int scope_stop(Unit *u) { + Scope *s = SCOPE(u); + + assert(s); + + if (s->state == SCOPE_STOP_SIGTERM || + s->state == SCOPE_STOP_SIGKILL) + return 0; + + assert(s->state == SCOPE_RUNNING || + s->state == SCOPE_ABANDONED); + + scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS); + return 1; +} + +static void scope_reset_failed(Unit *u) { + Scope *s = SCOPE(u); + + assert(s); + + if (s->state == SCOPE_FAILED) + scope_set_state(s, SCOPE_DEAD); + + s->result = SCOPE_SUCCESS; +} + +static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, -1, error); +} + +static int scope_get_timeout(Unit *u, usec_t *timeout) { + Scope *s = SCOPE(u); + usec_t t; + int r; + + if (!s->timer_event_source) + return 0; + + r = sd_event_source_get_time(s->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +static int scope_serialize(Unit *u, FILE *f, FDSet *fds) { + Scope *s = SCOPE(u); + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", scope_state_to_string(s->state)); + unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned)); + return 0; +} + +static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Scope *s = SCOPE(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + ScopeState state; + + state = scope_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, "was-abandoned")) { + int k; + + k = parse_boolean(value); + if (k < 0) + log_unit_debug(u, "Failed to parse boolean value: %s", value); + else + s->was_abandoned = k; + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +static bool scope_check_gc(Unit *u) { + assert(u); + + /* Never clean up scopes that still have a process around, + * even if the scope is formally dead. */ + + if (!u->cgroup_path) + return false; + + return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0; +} + +static void scope_notify_cgroup_empty_event(Unit *u) { + Scope *s = SCOPE(u); + assert(u); + + log_unit_debug(u, "cgroup is empty"); + + if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) + scope_enter_dead(s, SCOPE_SUCCESS); +} + +static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { + + /* If we get a SIGCHLD event for one of the processes we were + interested in, then we look for others to watch, under the + assumption that we'll sooner or later get a SIGCHLD for + them, as the original process we watched was probably the + parent of them, and they are hence now our children. */ + + unit_tidy_watch_pids(u, 0, 0); + unit_watch_all_pids(u); + + /* If the PID set is empty now, then let's finish this off + (On unified we use proper notifications) */ + if (cg_unified() <= 0 && set_isempty(u->pids)) + scope_notify_cgroup_empty_event(u); +} + +static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + Scope *s = SCOPE(userdata); + + assert(s); + assert(s->timer_event_source == source); + + switch (s->state) { + + case SCOPE_STOP_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Stopping timed out. Killing."); + scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL."); + scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); + } + + break; + + case SCOPE_STOP_SIGKILL: + log_unit_warning(UNIT(s), "Still around after SIGKILL. Ignoring."); + scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +int scope_abandon(Scope *s) { + assert(s); + + if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) + return -EPERM; + + if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED)) + return -ESTALE; + + s->was_abandoned = true; + s->controller = mfree(s->controller); + + /* The client is no longer watching the remaining processes, + * so let's step in here, under the assumption that the + * remaining processes will be sooner or later reassigned to + * us as parent. */ + + unit_tidy_watch_pids(UNIT(s), 0, 0); + unit_watch_all_pids(UNIT(s)); + + /* If the PID set is empty now, then let's finish this off */ + if (set_isempty(UNIT(s)->pids)) + scope_notify_cgroup_empty_event(UNIT(s)); + else + scope_set_state(s, SCOPE_ABANDONED); + + return 0; +} + +_pure_ static UnitActiveState scope_active_state(Unit *u) { + assert(u); + + return state_translation_table[SCOPE(u)->state]; +} + +_pure_ static const char *scope_sub_state_to_string(Unit *u) { + assert(u); + + return scope_state_to_string(SCOPE(u)->state); +} + +static void scope_enumerate(Manager *m) { + Unit *u; + int r; + + assert(m); + + /* Let's unconditionally add the "init.scope" special unit + * that encapsulates PID 1. Note that PID 1 already is in the + * cgroup for this, we hence just need to allocate the object + * for it and that's it. */ + + u = manager_get_unit(m, SPECIAL_INIT_SCOPE); + if (!u) { + u = unit_new(m, sizeof(Scope)); + if (!u) { + log_oom(); + return; + } + + r = unit_add_name(u, SPECIAL_INIT_SCOPE); + if (r < 0) { + unit_free(u); + 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; + + /* Prettify things, if we can. */ + if (!u->description) + u->description = strdup("System and Service Manager"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd(1)"); + + unit_add_to_load_queue(u); + unit_add_to_dbus_queue(u); +} + +static const char* const scope_result_table[_SCOPE_RESULT_MAX] = { + [SCOPE_SUCCESS] = "success", + [SCOPE_FAILURE_RESOURCES] = "resources", + [SCOPE_FAILURE_TIMEOUT] = "timeout", +}; + +DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult); + +const UnitVTable scope_vtable = { + .object_size = sizeof(Scope), + .cgroup_context_offset = offsetof(Scope, cgroup_context), + .kill_context_offset = offsetof(Scope, kill_context), + + .sections = + "Unit\0" + "Scope\0" + "Install\0", + .private_section = "Scope", + + .can_transient = true, + + .init = scope_init, + .load = scope_load, + .done = scope_done, + + .coldplug = scope_coldplug, + + .dump = scope_dump, + + .start = scope_start, + .stop = scope_stop, + + .kill = scope_kill, + + .get_timeout = scope_get_timeout, + + .serialize = scope_serialize, + .deserialize_item = scope_deserialize_item, + + .active_state = scope_active_state, + .sub_state_to_string = scope_sub_state_to_string, + + .check_gc = scope_check_gc, + + .sigchld_event = scope_sigchld_event, + + .reset_failed = scope_reset_failed, + + .notify_cgroup_empty = scope_notify_cgroup_empty_event, + + .bus_vtable = bus_scope_vtable, + .bus_set_property = bus_scope_set_property, + .bus_commit_properties = bus_scope_commit_properties, + + .enumerate = scope_enumerate, +}; diff --git a/src/grp-system/libcore/scope.h b/src/grp-system/libcore/scope.h new file mode 100644 index 0000000000..eaf8e8b447 --- /dev/null +++ b/src/grp-system/libcore/scope.h @@ -0,0 +1,58 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Scope Scope; + +#include "cgroup.h" +#include "kill.h" +#include "unit.h" + +typedef enum ScopeResult { + SCOPE_SUCCESS, + SCOPE_FAILURE_RESOURCES, + SCOPE_FAILURE_TIMEOUT, + _SCOPE_RESULT_MAX, + _SCOPE_RESULT_INVALID = -1 +} ScopeResult; + +struct Scope { + Unit meta; + + CGroupContext cgroup_context; + KillContext kill_context; + + ScopeState state, deserialized_state; + ScopeResult result; + + usec_t timeout_stop_usec; + + char *controller; + bool was_abandoned; + + sd_event_source *timer_event_source; +}; + +extern const UnitVTable scope_vtable; + +int scope_abandon(Scope *s); + +const char* scope_result_to_string(ScopeResult i) _const_; +ScopeResult scope_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/selinux-access.c b/src/grp-system/libcore/selinux-access.c new file mode 100644 index 0000000000..f6dbfa64b7 --- /dev/null +++ b/src/grp-system/libcore/selinux-access.c @@ -0,0 +1,284 @@ +/*** + This file is part of systemd. + + Copyright 2012 Dan Walsh + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "selinux-access.h" + +#ifdef HAVE_SELINUX + +#include +#include +#include +#include +#ifdef HAVE_AUDIT +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/stdio-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "audit-fd.h" + +static bool initialized = false; + +struct audit_info { + sd_bus_creds *creds; + const char *path; + const char *cmdline; +}; + +/* + Any time an access gets denied this callback will be called + with the audit data. We then need to just copy the audit data into the msgbuf. +*/ +static int audit_callback( + void *auditdata, + security_class_t cls, + char *msgbuf, + size_t msgbufsize) { + + const struct audit_info *audit = auditdata; + uid_t uid = 0, login_uid = 0; + gid_t gid = 0; + char login_uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a"; + char uid_buf[DECIMAL_STR_MAX(uid_t) + 1] = "n/a"; + char gid_buf[DECIMAL_STR_MAX(gid_t) + 1] = "n/a"; + + if (sd_bus_creds_get_audit_login_uid(audit->creds, &login_uid) >= 0) + xsprintf(login_uid_buf, UID_FMT, login_uid); + if (sd_bus_creds_get_euid(audit->creds, &uid) >= 0) + xsprintf(uid_buf, UID_FMT, uid); + if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0) + xsprintf(gid_buf, GID_FMT, gid); + + snprintf(msgbuf, msgbufsize, + "auid=%s uid=%s gid=%s%s%s%s%s%s%s", + login_uid_buf, uid_buf, gid_buf, + audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "", + audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : ""); + + return 0; +} + +static int callback_type_to_priority(int type) { + switch(type) { + + case SELINUX_ERROR: + return LOG_ERR; + + case SELINUX_WARNING: + return LOG_WARNING; + + case SELINUX_INFO: + return LOG_INFO; + + case SELINUX_AVC: + default: + return LOG_NOTICE; + } +} + +/* + libselinux uses this callback when access gets denied or other + events happen. If audit is turned on, messages will be reported + using audit netlink, otherwise they will be logged using the usual + channels. + + Code copied from dbus and modified. +*/ +_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { + va_list ap; + const char *fmt2; + +#ifdef HAVE_AUDIT + int fd; + + fd = get_audit_fd(); + + if (fd >= 0) { + _cleanup_free_ char *buf = NULL; + int r; + + va_start(ap, fmt); + r = vasprintf(&buf, fmt, ap); + va_end(ap); + + if (r >= 0) { + audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + return 0; + } + } +#endif + + fmt2 = strjoina("selinux: ", fmt); + + va_start(ap, fmt); + log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap); + va_end(ap); + + return 0; +} + +static int access_init(sd_bus_error *error) { + + if (!mac_selinux_use()) + return 0; + + if (initialized) + return 1; + + if (avc_open(NULL, 0) != 0) { + int enforce, saved_errno = errno; + + enforce = security_getenforce(); + log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m"); + + /* 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; + + /* 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)); + } + + selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); + + initialized = true; + return 1; +} + +/* + This function communicates with the kernel to check whether or not it should + allow the access. + If the machine is in permissive mode it will return ok. Audit messages will + still be generated if the access would be denied in enforcing mode. +*/ +int mac_selinux_generic_access_check( + sd_bus_message *message, + const char *path, + const char *permission, + sd_bus_error *error) { + + _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; + char *fcon = NULL; + char **cmdline = NULL; + int r = 0; + + assert(message); + assert(permission); + assert(error); + + r = access_init(error); + if (r <= 0) + return r; + + r = sd_bus_query_sender_creds( + message, + SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID| + SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUGMENT /* get more bits from /proc */, + &creds); + if (r < 0) + goto finish; + + /* The SELinux context is something we really should have + * gotten directly from the message or sender, and not be an + * augmented field. If it was augmented we cannot use it for + * authorization, since this is racy and vulnerable. Let's add + * an extra check, just in case, even though this really + * shouldn't be possible. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM); + + r = sd_bus_creds_get_selinux_context(creds, &scon); + if (r < 0) + goto finish; + + if (path) { + /* Get the file context of the unit file */ + + r = getfilecon_raw(path, &fcon); + if (r < 0) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); + goto finish; + } + + tclass = "service"; + } else { + r = getcon_raw(&fcon); + if (r < 0) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); + goto finish; + } + + tclass = "system"; + } + + sd_bus_creds_get_cmdline(creds, &cmdline); + cl = strv_join(cmdline, " "); + + audit_info.creds = creds; + audit_info.path = path; + audit_info.cmdline = cl; + + r = selinux_check_access(scon, fcon, tclass, permission, &audit_info); + if (r < 0) + r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); + + log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r); + +finish: + freecon(fcon); + + if (r < 0 && security_getenforce() != 1) { + sd_bus_error_free(error); + r = 0; + } + + 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 diff --git a/src/grp-system/libcore/selinux-access.h b/src/grp-system/libcore/selinux-access.h new file mode 100644 index 0000000000..8d91eea38f --- /dev/null +++ b/src/grp-system/libcore/selinux-access.h @@ -0,0 +1,46 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Dan Walsh + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "shared/bus-util.h" + +#include "manager.h" + +int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error); + +#ifdef HAVE_SELINUX + +#define mac_selinux_access_check(message, permission, error) \ + mac_selinux_generic_access_check((message), NULL, (permission), (error)) + +#define mac_selinux_unit_access_check(unit, message, permission, error) \ + ({ \ + Unit *_unit = (unit); \ + mac_selinux_generic_access_check((message), _unit->source_path ?: _unit->fragment_path, (permission), (error)); \ + }) + +#else + +#define mac_selinux_access_check(message, permission, error) 0 +#define mac_selinux_unit_access_check(unit, message, permission, error) 0 + +#endif diff --git a/src/grp-system/libcore/selinux-setup.c b/src/grp-system/libcore/selinux-setup.c new file mode 100644 index 0000000000..08c61af146 --- /dev/null +++ b/src/grp-system/libcore/selinux-setup.c @@ -0,0 +1,122 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_SELINUX +#include +#endif + +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "selinux-setup.h" + +#ifdef HAVE_SELINUX +_printf_(2,3) +static int null_log(int type, const char *fmt, ...) { + return 0; +} +#endif + +int mac_selinux_setup(bool *loaded_policy) { + +#ifdef HAVE_SELINUX + int enforce = 0; + usec_t before_load, after_load; + char *con; + int r; + union selinux_callback cb; + bool initialized = false; + + assert(loaded_policy); + + /* Turn off all of SELinux' own logging, we want to do that */ + cb.func_log = null_log; + selinux_set_callback(SELINUX_CB_LOG, cb); + + /* Don't load policy in the initrd if we don't appear to have + * it. For the real root, we check below if we've already + * loaded policy, and return gracefully. + */ + if (in_initrd() && access(selinux_path(), F_OK) < 0) + return 0; + + /* Already initialized by somebody else? */ + r = getcon_raw(&con); + if (r == 0) { + initialized = !streq(con, "kernel"); + freecon(con); + } + + /* Make sure we have no fds open while loading the policy and + * transitioning */ + log_close(); + + /* Now load the 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]; + + mac_selinux_retest(); + + /* Transition to the new context */ + r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); + if (r < 0 || !label) { + log_open(); + log_error("Failed to compute init label, ignoring."); + } else { + r = setcon_raw(label); + + log_open(); + if (r < 0) + log_error("Failed to transition into init label '%s', ignoring.", label); + } + + after_load = now(CLOCK_MONOTONIC); + + log_info("Successfully loaded SELinux policy in %s.", + format_timespan(timespan, sizeof(timespan), after_load - before_load, 0)); + + *loaded_policy = true; + + } else { + log_open(); + + if (enforce > 0) { + if (!initialized) { + log_emergency("Failed to load SELinux policy."); + return -EIO; + } + + log_warning("Failed to load new SELinux policy. Continuing with old policy."); + } else + log_debug("Unable to load SELinux policy. Ignoring."); + } +#endif + + return 0; +} diff --git a/src/grp-system/libcore/selinux-setup.h b/src/grp-system/libcore/selinux-setup.h new file mode 100644 index 0000000000..7b613249b0 --- /dev/null +++ b/src/grp-system/libcore/selinux-setup.h @@ -0,0 +1,24 @@ +#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 . +***/ + +#include + +int mac_selinux_setup(bool *loaded_policy); diff --git a/src/grp-system/libcore/service.c b/src/grp-system/libcore/service.c new file mode 100644 index 0000000000..43d195bbba --- /dev/null +++ b/src/grp-system/libcore/service.c @@ -0,0 +1,3392 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/escape.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-kernel.h" +#include "shared/bus-util.h" + +#include "dbus-service.h" +#include "load-dropin.h" +#include "load-fragment.h" +#include "manager.h" +#include "service.h" +#include "unit-printf.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, + [SERVICE_START_PRE] = UNIT_ACTIVATING, + [SERVICE_START] = UNIT_ACTIVATING, + [SERVICE_START_POST] = UNIT_ACTIVATING, + [SERVICE_RUNNING] = UNIT_ACTIVE, + [SERVICE_EXITED] = UNIT_ACTIVE, + [SERVICE_RELOAD] = UNIT_RELOADING, + [SERVICE_STOP] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_FAILED] = UNIT_FAILED, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING +}; + +/* For Type=idle we never want to delay any other jobs, hence we + * consider idle jobs active as soon as we start working on them */ +static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, + [SERVICE_START_PRE] = UNIT_ACTIVE, + [SERVICE_START] = UNIT_ACTIVE, + [SERVICE_START_POST] = UNIT_ACTIVE, + [SERVICE_RUNNING] = UNIT_ACTIVE, + [SERVICE_EXITED] = UNIT_ACTIVE, + [SERVICE_RELOAD] = UNIT_RELOADING, + [SERVICE_STOP] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_STOP_POST] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING, + [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING, + [SERVICE_FAILED] = UNIT_FAILED, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING +}; + +static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata); +static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); +static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata); + +static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); +static void service_enter_reload_by_notify(Service *s); + +static void service_init(Unit *u) { + Service *s = SERVICE(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + s->timeout_start_usec = u->manager->default_timeout_start_usec; + s->timeout_stop_usec = u->manager->default_timeout_stop_usec; + s->restart_usec = u->manager->default_restart_usec; + s->runtime_max_usec = USEC_INFINITY; + s->type = _SERVICE_TYPE_INVALID; + s->socket_fd = -1; + s->stdin_fd = s->stdout_fd = s->stderr_fd = -1; + s->guess_main_pid = true; + + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; +} + +static void service_unwatch_control_pid(Service *s) { + assert(s); + + if (s->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(s), s->control_pid); + s->control_pid = 0; +} + +static void service_unwatch_main_pid(Service *s) { + assert(s); + + if (s->main_pid <= 0) + return; + + unit_unwatch_pid(UNIT(s), s->main_pid); + s->main_pid = 0; +} + +static void service_unwatch_pid_file(Service *s) { + if (!s->pid_file_pathspec) + return; + + log_unit_debug(UNIT(s), "Stopping watch for PID file %s", s->pid_file_pathspec->path); + path_spec_unwatch(s->pid_file_pathspec); + path_spec_done(s->pid_file_pathspec); + s->pid_file_pathspec = mfree(s->pid_file_pathspec); +} + +static int service_set_main_pid(Service *s, pid_t pid) { + pid_t ppid; + + assert(s); + + if (pid <= 1) + return -EINVAL; + + if (pid == getpid()) + return -EINVAL; + + if (s->main_pid == pid && s->main_pid_known) + return 0; + + if (s->main_pid != pid) { + service_unwatch_main_pid(s); + exec_status_start(&s->main_exec_status, pid); + } + + s->main_pid = pid; + s->main_pid_known = true; + + 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 + s->main_pid_alien = false; + + return 0; +} + +void service_close_socket_fd(Service *s) { + assert(s); + + /* Undo the effect of service_set_socket_fd(). */ + + s->socket_fd = asynchronous_close(s->socket_fd); + + if (UNIT_ISSET(s->accept_socket)) { + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); + } +} + +static void service_stop_watchdog(Service *s) { + assert(s); + + s->watchdog_event_source = sd_event_source_unref(s->watchdog_event_source); + s->watchdog_timestamp = DUAL_TIMESTAMP_NULL; +} + +static usec_t service_get_watchdog_usec(Service *s) { + assert(s); + + if (s->watchdog_override_enable) + return s->watchdog_override_usec; + else + return s->watchdog_usec; +} + +static void service_start_watchdog(Service *s) { + int r; + usec_t watchdog_usec; + + assert(s); + + watchdog_usec = service_get_watchdog_usec(s); + if (watchdog_usec == 0 || watchdog_usec == USEC_INFINITY) + return; + + if (s->watchdog_event_source) { + r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, watchdog_usec)); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m"); + return; + } + + r = sd_event_source_set_enabled(s->watchdog_event_source, SD_EVENT_ONESHOT); + } else { + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->watchdog_event_source, + CLOCK_MONOTONIC, + usec_add(s->watchdog_timestamp.monotonic, watchdog_usec), 0, + service_dispatch_watchdog, s); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m"); + return; + } + + (void) sd_event_source_set_description(s->watchdog_event_source, "service-watchdog"); + + /* Let's process everything else which might be a sign + * of living before we consider a service died. */ + r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE); + } + + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m"); +} + +static void service_reset_watchdog(Service *s) { + assert(s); + + dual_timestamp_get(&s->watchdog_timestamp); + service_start_watchdog(s); +} + +static void service_reset_watchdog_timeout(Service *s, usec_t watchdog_override_usec) { + assert(s); + + s->watchdog_override_enable = true; + s->watchdog_override_usec = watchdog_override_usec; + service_reset_watchdog(s); + + log_unit_debug(UNIT(s), "watchdog_usec="USEC_FMT, s->watchdog_usec); + log_unit_debug(UNIT(s), "watchdog_override_usec="USEC_FMT, s->watchdog_override_usec); +} + +static void service_fd_store_unlink(ServiceFDStore *fs) { + + if (!fs) + return; + + if (fs->service) { + assert(fs->service->n_fd_store > 0); + LIST_REMOVE(fd_store, fs->service->fd_store, fs); + fs->service->n_fd_store--; + } + + if (fs->event_source) { + sd_event_source_set_enabled(fs->event_source, SD_EVENT_OFF); + sd_event_source_unref(fs->event_source); + } + + free(fs->fdname); + safe_close(fs->fd); + free(fs); +} + +static void service_release_resources(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + 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); + + assert(s->n_fd_store == 0); +} + +static void service_done(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + s->pid_file = mfree(s->pid_file); + s->status_text = mfree(s->status_text); + + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); + s->control_command = NULL; + s->main_command = NULL; + + exit_status_set_free(&s->restart_prevent_status); + exit_status_set_free(&s->restart_force_status); + exit_status_set_free(&s->success_status); + + /* This will leak a process, but at least no memory or any of + * our resources */ + service_unwatch_main_pid(s); + service_unwatch_control_pid(s); + service_unwatch_pid_file(s); + + if (s->bus_name) { + unit_unwatch_bus_name(u, s->bus_name); + s->bus_name = mfree(s->bus_name); + } + + s->bus_name_owner = mfree(s->bus_name_owner); + + service_close_socket_fd(s); + + unit_ref_unset(&s->accept_socket); + + service_stop_watchdog(s); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + service_release_resources(u); +} + +static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + ServiceFDStore *fs = userdata; + + assert(e); + assert(fs); + + /* If we get either EPOLLHUP or EPOLLERR, it's time to remove this entry from the fd store */ + service_fd_store_unlink(fs); + return 0; +} + +static int service_add_fd_store(Service *s, int fd, const char *name) { + ServiceFDStore *fs; + int r; + + assert(s); + assert(fd >= 0); + + if (s->n_fd_store >= s->n_fd_store_max) + return 0; + + LIST_FOREACH(fd_store, fs, s->fd_store) { + r = same_fd(fs->fd, fd); + if (r < 0) + return r; + if (r > 0) { + /* Already included */ + safe_close(fd); + return 1; + } + } + + fs = new0(ServiceFDStore, 1); + if (!fs) + return -ENOMEM; + + 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; + } + + (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); + + LIST_PREPEND(fd_store, s->fd_store, fs); + s->n_fd_store++; + + return 1; +} + +static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { + int r; + + assert(s); + + if (fdset_size(fds) <= 0) + return 0; + + while (s->n_fd_store < s->n_fd_store_max) { + _cleanup_close_ int fd = -1; + + fd = fdset_steal_first(fds); + if (fd < 0) + break; + + 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) { + log_unit_debug(UNIT(s), "Added fd to fd store."); + fd = -1; + } + } + + if (fdset_size(fds) > 0) + log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max); + + return 0; +} + +static int service_arm_timer(Service *s, usec_t usec) { + int r; + + assert(s); + + if (s->timer_event_source) { + r = sd_event_source_set_time(s->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + service_dispatch_timer, s); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->timer_event_source, "service-timer"); + + return 0; +} + +static int service_verify(Service *s) { + assert(s); + + if (UNIT(s)->load_state != UNIT_LOADED) + return 0; + + if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) { + log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing."); + return -EINVAL; + } + + if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) { + log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); + return -EINVAL; + } + + if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) { + log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing."); + return -EINVAL; + } + + if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { + log_unit_error(UNIT(s), "Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing."); + return -EINVAL; + } + + if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) { + log_unit_error(UNIT(s), "Service has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing."); + return -EINVAL; + } + + if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) { + log_unit_error(UNIT(s), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing."); + return -EINVAL; + } + + if (s->type == SERVICE_DBUS && !s->bus_name) { + log_unit_error(UNIT(s), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing."); + return -EINVAL; + } + + if (s->bus_name && s->type != SERVICE_DBUS) + log_unit_warning(UNIT(s), "Service has a D-Bus service name specified, but is not of type dbus. Ignoring."); + + if (s->exec_context.pam_name && !(s->kill_context.kill_mode == KILL_CONTROL_GROUP || s->kill_context.kill_mode == KILL_MIXED)) { + log_unit_error(UNIT(s), "Service has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing."); + return -EINVAL; + } + + if (s->usb_function_descriptors && !s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring."); + + if (!s->usb_function_descriptors && s->usb_function_strings) + log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring."); + + if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT) + log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring."); + + return 0; +} + +static int service_add_default_dependencies(Service *s) { + int r; + + assert(s); + + if (!UNIT(s)->default_dependencies) + return 0; + + /* Add a number of automatic dependencies useful for the + * majority of services. */ + + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { + /* 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; + + /* 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); +} + +static void service_fix_output(Service *s) { + assert(s); + + /* If nothing has been explicitly configured, patch default + * output in. If input is socket/tty we avoid this however, + * since in that case we want output to default to the same + * place as we read input from. */ + + if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && + s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_error = UNIT(s)->manager->default_std_error; + + if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_output = UNIT(s)->manager->default_std_output; +} + +static int service_setup_bus_name(Service *s) { + int r; + + assert(s); + + if (!s->bus_name) + return 0; + + 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; + + assert(s); + + if (s->type == _SERVICE_TYPE_INVALID) { + /* Figure out a type automatically */ + if (s->bus_name) + s->type = SERVICE_DBUS; + else if (s->exec_command[SERVICE_EXEC_START]) + s->type = SERVICE_SIMPLE; + else + s->type = SERVICE_ONESHOT; + } + + /* Oneshot services have disabled start timeout by default */ + if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined) + s->timeout_start_usec = USEC_INFINITY; + + service_fix_output(s); + + r = unit_patch_contexts(UNIT(s)); + if (r < 0) + return r; + + r = unit_add_exec_dependencies(UNIT(s), &s->exec_context); + if (r < 0) + return r; + + r = unit_set_default_slice(UNIT(s)); + if (r < 0) + return r; + + if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE) + s->notify_access = NOTIFY_MAIN; + + if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) + s->notify_access = NOTIFY_MAIN; + + r = service_add_default_dependencies(s); + if (r < 0) + return r; + + r = service_setup_bus_name(s); + if (r < 0) + return r; + + return 0; +} + +static int service_load(Unit *u) { + Service *s = SERVICE(u); + int r; + + assert(s); + + /* Load a .service file */ + r = unit_load_fragment(u); + if (r < 0) + return r; + + /* Still nothing found? Then let's give up */ + if (u->load_state == UNIT_STUB) + return -ENOENT; + + /* This is a new unit? Then let's add in some extras */ + if (u->load_state == UNIT_LOADED) { + + /* We were able to load something, then let's add in + * the dropin directories. */ + r = unit_load_dropin(u); + if (r < 0) + return r; + + /* This is a new unit? Then let's add in some + * extras */ + r = service_add_extras(s); + if (r < 0) + return r; + } + + return service_verify(s); +} + +static void service_dump(Unit *u, FILE *f, const char *prefix) { + ServiceExecCommand c; + Service *s = SERVICE(u); + const char *prefix2; + + assert(s); + + prefix = strempty(prefix); + prefix2 = strjoina(prefix, "\t"); + + fprintf(f, + "%sService State: %s\n" + "%sResult: %s\n" + "%sReload Result: %s\n" + "%sPermissionsStartOnly: %s\n" + "%sRootDirectoryStartOnly: %s\n" + "%sRemainAfterExit: %s\n" + "%sGuessMainPID: %s\n" + "%sType: %s\n" + "%sRestart: %s\n" + "%sNotifyAccess: %s\n" + "%sNotifyState: %s\n", + prefix, service_state_to_string(s->state), + prefix, service_result_to_string(s->result), + prefix, service_result_to_string(s->reload_result), + prefix, yes_no(s->permissions_start_only), + prefix, yes_no(s->root_directory_start_only), + prefix, yes_no(s->remain_after_exit), + prefix, yes_no(s->guess_main_pid), + prefix, service_type_to_string(s->type), + prefix, service_restart_to_string(s->restart), + prefix, notify_access_to_string(s->notify_access), + prefix, notify_state_to_string(s->notify_state)); + + if (s->control_pid > 0) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, s->control_pid); + + if (s->main_pid > 0) + fprintf(f, + "%sMain PID: "PID_FMT"\n" + "%sMain PID Known: %s\n" + "%sMain PID Alien: %s\n", + prefix, s->main_pid, + prefix, yes_no(s->main_pid_known), + prefix, yes_no(s->main_pid_alien)); + + if (s->pid_file) + fprintf(f, + "%sPIDFile: %s\n", + prefix, s->pid_file); + + if (s->bus_name) + fprintf(f, + "%sBusName: %s\n" + "%sBus Name Good: %s\n", + prefix, s->bus_name, + prefix, yes_no(s->bus_name_good)); + + kill_context_dump(&s->kill_context, f, prefix); + exec_context_dump(&s->exec_context, f, prefix); + + for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) { + + if (!s->exec_command[c]) + continue; + + fprintf(f, "%s-> %s:\n", + prefix, service_exec_command_to_string(c)); + + exec_command_dump_list(s->exec_command[c], f, prefix2); + } + + if (s->status_text) + fprintf(f, "%sStatus Text: %s\n", + prefix, s->status_text); + + if (s->n_fd_store_max > 0) + fprintf(f, + "%sFile Descriptor Store Max: %u\n" + "%sFile Descriptor Store Current: %u\n", + prefix, s->n_fd_store_max, + prefix, s->n_fd_store); +} + +static int service_load_pid_file(Service *s, bool may_warn) { + _cleanup_free_ char *k = NULL; + int r; + pid_t pid; + + assert(s); + + if (!s->pid_file) + return -ENOENT; + + r = read_one_line_file(s->pid_file, &k); + if (r < 0) { + if (may_warn) + log_unit_info_errno(UNIT(s), r, "PID file %s not readable (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state)); + return r; + } + + r = parse_pid(k, &pid); + if (r < 0) { + if (may_warn) + log_unit_info_errno(UNIT(s), r, "Failed to read PID from file %s: %m", s->pid_file); + return r; + } + + if (!pid_is_alive(pid)) { + if (may_warn) + log_unit_info(UNIT(s), "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); + return -ESRCH; + } + + if (s->main_pid_known) { + if (pid == s->main_pid) + return 0; + + log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); + + service_unwatch_main_pid(s); + s->main_pid_known = false; + } else + log_unit_debug(UNIT(s), "Main PID loaded: "PID_FMT, pid); + + r = service_set_main_pid(s, pid); + if (r < 0) + return r; + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) { + /* FIXME: we need to do something here */ + log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); + return r; + } + + return 0; +} + +static void service_search_main_pid(Service *s) { + pid_t pid = 0; + int r; + + assert(s); + + /* If we know it anyway, don't ever fallback to unreliable + * heuristics */ + if (s->main_pid_known) + return; + + if (!s->guess_main_pid) + return; + + assert(s->main_pid <= 0); + + if (unit_search_main_pid(UNIT(s), &pid) < 0) + return; + + log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); + if (service_set_main_pid(s, pid) < 0) + return; + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + /* FIXME: we need to do something here */ + log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); +} + +static void service_set_state(Service *s, ServiceState state) { + ServiceState old_state; + const UnitActiveState *table; + + assert(s); + + table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; + + old_state = s->state; + s->state = state; + + service_unwatch_pid_file(s); + + if (!IN_SET(state, + SERVICE_START_PRE, 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, + SERVICE_AUTO_RESTART)) + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + + if (!IN_SET(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)) { + service_unwatch_main_pid(s); + s->main_command = NULL; + } + + if (!IN_SET(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)) { + service_unwatch_control_pid(s); + s->control_command = NULL; + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + } + + if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) + unit_unwatch_all_pids(UNIT(s)); + + if (!IN_SET(state, + SERVICE_START_PRE, 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) && + !(state == SERVICE_DEAD && UNIT(s)->job)) + service_close_socket_fd(s); + + if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + service_stop_watchdog(s); + + /* For the inactive states unit_notify() will trim the cgroup, + * but for exit we have to do that ourselves... */ + if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager)) + unit_prune_cgroup(UNIT(s)); + + /* For remain_after_exit services, let's see if we can "release" the + * hold on the console, since unit_notify() only does that in case of + * change of state */ + if (state == SERVICE_EXITED && + s->remain_after_exit && + UNIT(s)->manager->n_on_console > 0) { + + ExecContext *ec; + + ec = unit_get_exec_context(UNIT(s)); + if (ec && exec_context_may_touch_console(ec)) { + Manager *m = UNIT(s)->manager; + + m->n_on_console--; + if (m->n_on_console == 0) + /* unset no_console_output flag, since the console is free */ + m->no_console_output = false; + } + } + + if (old_state != 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); +} + +static usec_t service_coldplug_timeout(Service *s) { + assert(s); + + switch (s->deserialized_state) { + + case SERVICE_START_PRE: + case SERVICE_START: + case SERVICE_START_POST: + case SERVICE_RELOAD: + return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec); + + case SERVICE_RUNNING: + return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec); + + case SERVICE_STOP: + case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + case SERVICE_STOP_POST: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec); + + case SERVICE_AUTO_RESTART: + return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec); + + default: + return USEC_INFINITY; + } +} + +static int service_coldplug(Unit *u) { + Service *s = SERVICE(u); + int r; + + assert(s); + assert(s->state == SERVICE_DEAD); + + if (s->deserialized_state == s->state) + return 0; + + r = service_arm_timer(s, service_coldplug_timeout(s)); + if (r < 0) + return r; + + 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; + } + + 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, char ***fd_names) { + _cleanup_strv_free_ char **rfd_names = NULL; + _cleanup_free_ int *rfds = NULL; + int rn_fds = 0, r; + + assert(s); + assert(fds); + assert(fd_names); + + if (s->socket_fd >= 0) { + + /* Pass the per-connection socket */ + + rfds = new(int, 1); + if (!rfds) + return -ENOMEM; + rfds[0] = s->socket_fd; + + rfd_names = strv_new("connection", NULL); + if (!rfd_names) + return -ENOMEM; + + rn_fds = 1; + } else { + Iterator i; + Unit *u; + + /* Pass all our configured sockets for singleton services */ + + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { + _cleanup_free_ int *cfds = NULL; + Socket *sock; + int cn_fds; + + if (u->type != UNIT_SOCKET) + continue; + + sock = SOCKET(u); + + 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)); + if (!t) + return -ENOMEM; + + rfds = t; + + 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; + *fd_names = rfd_names; + + rfds = NULL; + rfd_names = NULL; + + return rn_fds; +} + +static int service_spawn( + Service *s, + ExecCommand *c, + usec_t timeout, + bool pass_fds, + bool apply_permissions, + bool apply_chroot, + bool apply_tty_stdin, + bool is_control, + pid_t *_pid) { + + _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = 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, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, + }; + + int r; + + assert(s); + assert(c); + assert(_pid); + + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } + + r = unit_setup_exec_runtime(UNIT(s)); + if (r < 0) + return r; + + if (pass_fds || + s->exec_context.std_input == EXEC_INPUT_SOCKET || + s->exec_context.std_output == EXEC_OUTPUT_SOCKET || + s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { + + r = service_collect_fds(s, &fds, &fd_names); + if (r < 0) + return r; + + n_fds = r; + } + + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); + if (r < 0) + return r; + + r = unit_full_printf_strv(UNIT(s), c->argv, &argv); + if (r < 0) + return r; + + our_env = new0(char*, 6); + if (!our_env) + return -ENOMEM; + + if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) + if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) + return -ENOMEM; + + if (s->main_pid > 0) + if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) + return -ENOMEM; + + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) + if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) + return -ENOMEM; + + if (s->socket_fd >= 0) { + union sockaddr_union sa; + socklen_t salen = sizeof(sa); + + r = getpeername(s->socket_fd, &sa.sa, &salen); + if (r < 0) + return -errno; + + if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) { + _cleanup_free_ char *addr = NULL; + char *t; + int port; + + r = sockaddr_pretty(&sa.sa, salen, true, false, &addr); + if (r < 0) + return r; + + t = strappend("REMOTE_ADDR=", addr); + if (!t) + return -ENOMEM; + our_env[n_env++] = t; + + port = sockaddr_port(&sa.sa); + if (port < 0) + return port; + + if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) + return -ENOMEM; + our_env[n_env++] = t; + } + } + + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); + if (!final_env) + return -ENOMEM; + + if (is_control && UNIT(s)->cgroup_path) { + path = strjoina(UNIT(s)->cgroup_path, "/control"); + (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); + } else + path = UNIT(s)->cgroup_path; + + 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; + exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; + exec_params.cgroup_path = path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; + exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); + exec_params.watchdog_usec = s->watchdog_usec; + 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, + &s->exec_context, + &exec_params, + s->exec_runtime, + &pid); + if (r < 0) + return r; + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + /* FIXME: we need to do something here */ + return r; + + *_pid = pid; + + return 0; +} + +static int main_pid_good(Service *s) { + assert(s); + + /* Returns 0 if the pid is dead, 1 if it is good, -1 if we + * don't know */ + + /* If we know the pid file, then let's just check if it is + * still valid */ + if (s->main_pid_known) { + + /* If it's an alien child let's check if it is still + * alive ... */ + if (s->main_pid_alien && s->main_pid > 0) + return pid_is_alive(s->main_pid); + + /* .. otherwise assume we'll get a SIGCHLD for it, + * which we really should wait for to collect exit + * status and code */ + return s->main_pid > 0; + } + + /* We don't know the pid */ + return -EAGAIN; +} + +_pure_ static int control_pid_good(Service *s) { + assert(s); + + return s->control_pid > 0; +} + +static int cgroup_good(Service *s) { + int r; + + assert(s); + + if (!UNIT(s)->cgroup_path) + return 0; + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path); + if (r < 0) + return r; + + return !r; +} + +static bool service_shall_restart(Service *s) { + assert(s); + + /* Don't restart after manual stops */ + if (s->forbid_restart) + return false; + + /* Never restart if this is configured as special exception */ + if (exit_status_set_test(&s->restart_prevent_status, s->main_exec_status.code, s->main_exec_status.status)) + return false; + + /* Restart if the exit code/status are configured as restart triggers */ + if (exit_status_set_test(&s->restart_force_status, s->main_exec_status.code, s->main_exec_status.status)) + return true; + + switch (s->restart) { + + case SERVICE_RESTART_NO: + return false; + + case SERVICE_RESTART_ALWAYS: + return true; + + case SERVICE_RESTART_ON_SUCCESS: + return s->result == SERVICE_SUCCESS; + + case SERVICE_RESTART_ON_FAILURE: + return s->result != SERVICE_SUCCESS; + + case SERVICE_RESTART_ON_ABNORMAL: + return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE); + + case SERVICE_RESTART_ON_WATCHDOG: + return s->result == SERVICE_FAILURE_WATCHDOG; + + case SERVICE_RESTART_ON_ABORT: + return IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP); + + default: + assert_not_reached("unknown restart setting"); + } +} + +static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { + int r; + assert(s); + + if (f != SERVICE_SUCCESS) + s->result = f; + + service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); + + if (s->result != SERVICE_SUCCESS) { + log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result)); + failure_action(UNIT(s)->manager, s->failure_action, UNIT(s)->reboot_arg); + } + + if (allow_restart && service_shall_restart(s)) { + + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_AUTO_RESTART); + } + + /* The next restart might not be a manual stop, hence reset the flag indicating manual stops */ + s->forbid_restart = false; + + /* We want fresh tmpdirs in case service is started again immediately */ + exec_runtime_destroy(s->exec_runtime); + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + + /* Also, remove the runtime directory in */ + exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + + /* Try to delete the pid file. At this point it will be + * out-of-date, and some software might be confused by it, so + * let's remove it. */ + if (s->pid_file) + (void) unlink(s->pid_file); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run install restart timer: %m"); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); +} + +static void service_enter_stop_post(Service *s, ServiceResult f) { + int r; + assert(s); + + if (f != SERVICE_SUCCESS) + s->result = f; + + service_unwatch_control_pid(s); + unit_watch_all_pids(UNIT(s)); + + s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST]; + if (s->control_command) { + s->control_command_id = SERVICE_EXEC_STOP_POST; + + r = service_spawn(s, + s->control_command, + s->timeout_stop_usec, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + true, + &s->control_pid); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_STOP_POST); + } else + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); +} + +static int state_to_kill_operation(ServiceState state) { + switch (state) { + + case SERVICE_STOP_SIGABRT: + return KILL_ABORT; + + case SERVICE_STOP_SIGTERM: + case SERVICE_FINAL_SIGTERM: + return KILL_TERMINATE; + + case SERVICE_STOP_SIGKILL: + case SERVICE_FINAL_SIGKILL: + return KILL_KILL; + + default: + return _KILL_OPERATION_INVALID; + } +} + +static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) { + int r; + + assert(s); + + if (f != SERVICE_SUCCESS) + s->result = f; + + unit_watch_all_pids(UNIT(s)); + + r = unit_kill_context( + UNIT(s), + &s->kill_context, + state_to_kill_operation(state), + s->main_pid, + s->control_pid, + s->main_pid_alien); + + if (r < 0) + goto fail; + + if (r > 0) { + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); + if (r < 0) + goto fail; + + service_set_state(s, state); + } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill) + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS); + else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) + service_enter_stop_post(s, SERVICE_SUCCESS); + else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill) + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS); + else + service_enter_dead(s, SERVICE_SUCCESS, true); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); + + if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL)) + service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES); + else + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); +} + +static void service_enter_stop_by_notify(Service *s) { + assert(s); + + unit_watch_all_pids(UNIT(s)); + + service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); + + /* The service told us it's stopping, so it's as if we SIGTERM'd it. */ + service_set_state(s, SERVICE_STOP_SIGTERM); +} + +static void service_enter_stop(Service *s, ServiceResult f) { + int r; + + assert(s); + + if (f != SERVICE_SUCCESS) + s->result = f; + + service_unwatch_control_pid(s); + unit_watch_all_pids(UNIT(s)); + + s->control_command = s->exec_command[SERVICE_EXEC_STOP]; + if (s->control_command) { + s->control_command_id = SERVICE_EXEC_STOP; + + r = service_spawn(s, + s->control_command, + s->timeout_stop_usec, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + true, + &s->control_pid); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_STOP); + } else + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop' task: %m"); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); +} + +static bool service_good(Service *s) { + int main_pid_ok; + assert(s); + + if (s->type == SERVICE_DBUS && !s->bus_name_good) + return false; + + main_pid_ok = main_pid_good(s); + if (main_pid_ok > 0) /* It's alive */ + return true; + if (main_pid_ok == 0) /* It's dead */ + return false; + + /* OK, we don't know anything about the main PID, maybe + * because there is none. Let's check the control group + * instead. */ + + return cgroup_good(s) != 0; +} + +static void service_enter_running(Service *s, ServiceResult f) { + assert(s); + + if (f != SERVICE_SUCCESS) + s->result = f; + + service_unwatch_control_pid(s); + + if (service_good(s)) { + + /* If there are any queued up sd_notify() + * notifications, process them now */ + if (s->notify_state == NOTIFY_RELOADING) + service_enter_reload_by_notify(s); + else if (s->notify_state == NOTIFY_STOPPING) + service_enter_stop_by_notify(s); + else { + service_set_state(s, SERVICE_RUNNING); + service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec)); + } + + } else if (s->remain_after_exit) + service_set_state(s, SERVICE_EXITED); + else + service_enter_stop(s, SERVICE_SUCCESS); +} + +static void service_enter_start_post(Service *s) { + int r; + assert(s); + + service_unwatch_control_pid(s); + service_reset_watchdog(s); + + s->control_command = s->exec_command[SERVICE_EXEC_START_POST]; + if (s->control_command) { + s->control_command_id = SERVICE_EXEC_START_POST; + + r = service_spawn(s, + s->control_command, + s->timeout_start_usec, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + true, + &s->control_pid); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_START_POST); + } else + service_enter_running(s, SERVICE_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); +} + +static void service_kill_control_processes(Service *s) { + char *p; + + if (!UNIT(s)->cgroup_path) + return; + + p = strjoina(UNIT(s)->cgroup_path, "/control"); + cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, CGROUP_SIGCONT|CGROUP_IGNORE_SELF|CGROUP_REMOVE, NULL, NULL, NULL); +} + +static void service_enter_start(Service *s) { + ExecCommand *c; + usec_t timeout; + pid_t pid; + int r; + + assert(s); + + service_unwatch_control_pid(s); + service_unwatch_main_pid(s); + + /* We want to ensure that nobody leaks processes from + * START_PRE here, so let's go on a killing spree, People + * should not spawn long running processes from START_PRE. */ + service_kill_control_processes(s); + + if (s->type == SERVICE_FORKING) { + s->control_command_id = SERVICE_EXEC_START; + c = s->control_command = s->exec_command[SERVICE_EXEC_START]; + + s->main_command = NULL; + } else { + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + s->control_command = NULL; + + c = s->main_command = s->exec_command[SERVICE_EXEC_START]; + } + + if (!c) { + assert(s->type == SERVICE_ONESHOT); + service_enter_start_post(s); + return; + } + + if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) + /* For simple + idle this is the main process. We don't apply any timeout here, but + * service_enter_running() will later apply the .runtime_max_usec timeout. */ + timeout = USEC_INFINITY; + else + timeout = s->timeout_start_usec; + + r = service_spawn(s, + c, + timeout, + true, + true, + true, + true, + false, + &pid); + if (r < 0) + goto fail; + + if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) { + /* For simple services we immediately start + * the START_POST binaries. */ + + service_set_main_pid(s, pid); + service_enter_start_post(s); + + } else if (s->type == SERVICE_FORKING) { + + /* For forking services we wait until the start + * process exited. */ + + s->control_pid = pid; + service_set_state(s, SERVICE_START); + + } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) { + + /* For oneshot services we wait until the start + * process exited, too, but it is our main process. */ + + /* For D-Bus services we know the main pid right away, + * but wait for the bus name to appear on the + * bus. Notify services are similar. */ + + service_set_main_pid(s, pid); + service_set_state(s, SERVICE_START); + } else + assert_not_reached("Unknown service type"); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'start' task: %m"); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); +} + +static void service_enter_start_pre(Service *s) { + int r; + + assert(s); + + service_unwatch_control_pid(s); + + s->control_command = s->exec_command[SERVICE_EXEC_START_PRE]; + if (s->control_command) { + /* Before we start anything, let's clear up what might + * be left from previous runs. */ + service_kill_control_processes(s); + + s->control_command_id = SERVICE_EXEC_START_PRE; + + r = service_spawn(s, + s->control_command, + s->timeout_start_usec, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + true, + true, + &s->control_pid); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_START_PRE); + } else + service_enter_start(s); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); +} + +static void service_enter_restart(Service *s) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(s); + + if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { + /* Don't restart things if we are going down anyway */ + log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart."); + + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec)); + if (r < 0) + goto fail; + + return; + } + + /* Any units that are bound to this service must also be + * 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, &error, NULL); + if (r < 0) + goto fail; + + /* Note that we stay in the SERVICE_AUTO_RESTART state here, + * it will be canceled as part of the service_stop() call that + * is executed as part of JOB_RESTART. */ + + log_unit_debug(UNIT(s), "Scheduled restart job."); + return; + +fail: + log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, -r)); + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); +} + +static void service_enter_reload_by_notify(Service *s) { + assert(s); + + service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_start_usec)); + service_set_state(s, SERVICE_RELOAD); +} + +static void service_enter_reload(Service *s) { + int r; + + 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) { + s->control_command_id = SERVICE_EXEC_RELOAD; + + r = service_spawn(s, + s->control_command, + s->timeout_start_usec, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + false, + true, + &s->control_pid); + if (r < 0) + goto fail; + + service_set_state(s, SERVICE_RELOAD); + } else + service_enter_running(s, SERVICE_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'reload' task: %m"); + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); +} + +static void service_run_next_control(Service *s) { + usec_t timeout; + int r; + + assert(s); + assert(s->control_command); + assert(s->control_command->command_next); + + assert(s->control_command_id != SERVICE_EXEC_START); + + s->control_command = s->control_command->command_next; + service_unwatch_control_pid(s); + + if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + timeout = s->timeout_start_usec; + else + timeout = s->timeout_stop_usec; + + r = service_spawn(s, + s->control_command, + timeout, + false, + !s->permissions_start_only, + !s->root_directory_start_only, + s->control_command_id == SERVICE_EXEC_START_PRE || + s->control_command_id == SERVICE_EXEC_STOP_POST, + true, + &s->control_pid); + if (r < 0) + goto fail; + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m"); + + if (s->state == SERVICE_START_PRE) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + else if (s->state == SERVICE_STOP) + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + else if (s->state == SERVICE_STOP_POST) + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); + else if (s->state == SERVICE_RELOAD) { + s->reload_result = SERVICE_FAILURE_RESOURCES; + service_enter_running(s, SERVICE_SUCCESS); + } else + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); +} + +static void service_run_next_main(Service *s) { + pid_t pid; + int r; + + assert(s); + assert(s->main_command); + assert(s->main_command->command_next); + assert(s->type == SERVICE_ONESHOT); + + s->main_command = s->main_command->command_next; + service_unwatch_main_pid(s); + + r = service_spawn(s, + s->main_command, + s->timeout_start_usec, + true, + true, + true, + true, + false, + &pid); + if (r < 0) + goto fail; + + service_set_main_pid(s, pid); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run next main task: %m"); + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); +} + +static int service_start(Unit *u) { + Service *s = SERVICE(u); + int r; + + assert(s); + + /* We cannot fulfill this request right now, try again later + * please! */ + if (IN_SET(s->state, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) + return -EAGAIN; + + /* Already on it! */ + if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) + return 0; + + /* A service that will be restarted must be stopped first to + * trigger BindsTo and/or OnFailure dependencies. If a user + * does not want to wait for the holdoff time to elapse, the + * service should be manually restarted, not started. We + * simply return EAGAIN here, so that any start jobs stay + * queued, and assume that the auto restart timer will + * eventually trigger the restart. */ + if (s->state == SERVICE_AUTO_RESTART) + return -EAGAIN; + + assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); + + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_start_limit_test(u); + if (r < 0) { + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); + return r; + } + + s->result = SERVICE_SUCCESS; + s->reload_result = SERVICE_SUCCESS; + s->main_pid_known = false; + s->main_pid_alien = false; + s->forbid_restart = false; + s->reset_cpu_usage = true; + + s->status_text = mfree(s->status_text); + s->status_errno = 0; + + s->notify_state = NOTIFY_UNKNOWN; + + s->watchdog_override_enable = false; + s->watchdog_override_usec = 0; + + service_enter_start_pre(s); + return 1; +} + +static int service_stop(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + /* Don't create restart jobs from manual stops. */ + s->forbid_restart = true; + + /* Already on it */ + if (IN_SET(s->state, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) + return 0; + + /* A restart will be scheduled or is in progress. */ + if (s->state == SERVICE_AUTO_RESTART) { + service_set_state(s, SERVICE_DEAD); + return 0; + } + + /* If there's already something running we go directly into + * kill mode. */ + if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) { + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); + return 0; + } + + assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED)); + + service_enter_stop(s, SERVICE_SUCCESS); + return 1; +} + +static int service_reload(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED); + + service_enter_reload(s); + return 1; +} + +_pure_ static bool service_can_reload(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return !!s->exec_command[SERVICE_EXEC_RELOAD]; +} + +static int service_serialize(Unit *u, FILE *f, FDSet *fds) { + Service *s = SERVICE(u); + ServiceFDStore *fs; + int r; + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", service_state_to_string(s->state)); + unit_serialize_item(u, f, "result", service_result_to_string(s->result)); + unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result)); + + if (s->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); + + if (s->main_pid_known && s->main_pid > 0) + unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid); + + unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known)); + 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); + + 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 + * first one again */ + if (s->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); + + 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; + + r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_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; + + 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) { + unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid); + dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp); + dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp); + + if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { + unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code); + unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status); + } + } + + dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); + + unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart)); + + if (s->watchdog_override_enable) + unit_serialize_item_format(u, f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec); + + return 0; +} + +static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Service *s = SERVICE(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + ServiceState state; + + state = service_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, "result")) { + ServiceResult f; + + f = service_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != SERVICE_SUCCESS) + s->result = f; + + } else if (streq(key, "reload-result")) { + ServiceResult f; + + f = service_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse reload result value: %s", value); + else if (f != SERVICE_SUCCESS) + s->reload_result = f; + + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse control-pid value: %s", value); + else + s->control_pid = pid; + } else if (streq(key, "main-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse main-pid value: %s", value); + else { + service_set_main_pid(s, pid); + unit_watch_pid(UNIT(s), pid); + } + } else if (streq(key, "main-pid-known")) { + int b; + + b = parse_boolean(value); + if (b < 0) + log_unit_debug(u, "Failed to parse main-pid-known value: %s", value); + else + s->main_pid_known = b; + } else if (streq(key, "bus-name-good")) { + int b; + + b = parse_boolean(value); + if (b < 0) + 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; + + r = cunescape(value, 0, &t); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value); + else { + free(s->status_text); + s->status_text = t; + } + + } else if (streq(key, "control-command")) { + ServiceExecCommand id; + + id = service_exec_command_from_string(value); + if (id < 0) + log_unit_debug(u, "Failed to parse exec-command value: %s", value); + else { + s->control_command_id = id; + s->control_command = s->exec_command[id]; + } + } else if (streq(key, "socket-fd")) { + int fd; + + if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse socket-fd value: %s", value); + else { + asynchronous_close(s->socket_fd); + s->socket_fd = fdset_remove(fds, fd); + } + } else if (streq(key, "fd-store-fd")) { + const char *fdv; + size_t pf; + int 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 { + _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) + fdset_remove(fds, fd); + } + + } else if (streq(key, "main-exec-status-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse main-exec-status-pid value: %s", value); + else + s->main_exec_status.pid = pid; + } else if (streq(key, "main-exec-status-code")) { + int i; + + if (safe_atoi(value, &i) < 0) + log_unit_debug(u, "Failed to parse main-exec-status-code value: %s", value); + else + s->main_exec_status.code = i; + } else if (streq(key, "main-exec-status-status")) { + int i; + + if (safe_atoi(value, &i) < 0) + log_unit_debug(u, "Failed to parse main-exec-status-status value: %s", value); + else + s->main_exec_status.status = i; + } else if (streq(key, "main-exec-status-start")) + dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp); + else if (streq(key, "main-exec-status-exit")) + dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp); + else if (streq(key, "watchdog-timestamp")) + dual_timestamp_deserialize(value, &s->watchdog_timestamp); + else if (streq(key, "forbid-restart")) { + int b; + + b = parse_boolean(value); + if (b < 0) + 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); + s->exec_context.stdio_as_fds = true; + } + } 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); + s->exec_context.stdio_as_fds = true; + } + } 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); + s->exec_context.stdio_as_fds = true; + } + } else if (streq(key, "watchdog-override-usec")) { + usec_t watchdog_override_usec; + if (timestamp_deserialize(value, &watchdog_override_usec) < 0) + log_unit_debug(u, "Failed to parse watchdog_override_usec value: %s", value); + else { + s->watchdog_override_enable = true; + s->watchdog_override_usec = watchdog_override_usec; + } + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState service_active_state(Unit *u) { + const UnitActiveState *table; + + assert(u); + + table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table; + + return table[SERVICE(u)->state]; +} + +static const char *service_sub_state_to_string(Unit *u) { + assert(u); + + return service_state_to_string(SERVICE(u)->state); +} + +static bool service_check_gc(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + /* Never clean up services that still have a process around, + * even if the service is formally dead. */ + if (cgroup_good(s) > 0 || + main_pid_good(s) > 0 || + control_pid_good(s) > 0) + return true; + + return false; +} + +static int service_retry_pid_file(Service *s) { + int r; + + assert(s->pid_file); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + + r = service_load_pid_file(s, false); + if (r < 0) + return r; + + service_unwatch_pid_file(s); + + service_enter_running(s, SERVICE_SUCCESS); + return 0; +} + +static int service_watch_pid_file(Service *s) { + int r; + + log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path); + + r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io); + if (r < 0) + goto fail; + + /* the pidfile might have appeared just before we set the watch */ + log_unit_debug(UNIT(s), "Trying to read PID file %s in case it changed", s->pid_file_pathspec->path); + service_retry_pid_file(s); + + return 0; +fail: + log_unit_error_errno(UNIT(s), r, "Failed to set a watch for PID file %s: %m", s->pid_file_pathspec->path); + service_unwatch_pid_file(s); + return r; +} + +static int service_demand_pid_file(Service *s) { + PathSpec *ps; + + assert(s->pid_file); + assert(!s->pid_file_pathspec); + + ps = new0(PathSpec, 1); + if (!ps) + return -ENOMEM; + + ps->unit = UNIT(s); + ps->path = strdup(s->pid_file); + if (!ps->path) { + free(ps); + return -ENOMEM; + } + + path_kill_slashes(ps->path); + + /* PATH_CHANGED would not be enough. There are daemons (sendmail) that + * keep their PID file open all the time. */ + ps->type = PATH_MODIFIED; + ps->inotify_fd = -1; + + s->pid_file_pathspec = ps; + + return service_watch_pid_file(s); +} + +static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata) { + PathSpec *p = userdata; + Service *s; + + assert(p); + + s = SERVICE(p->unit); + + assert(s); + assert(fd >= 0); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + assert(s->pid_file_pathspec); + assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); + + log_unit_debug(UNIT(s), "inotify event"); + + if (path_spec_fd_event(p, events) < 0) + goto fail; + + if (service_retry_pid_file(s) == 0) + return 0; + + if (service_watch_pid_file(s) < 0) + goto fail; + + return 0; + +fail: + service_unwatch_pid_file(s); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + return 0; +} + +static void service_notify_cgroup_empty_event(Unit *u) { + Service *s = SERVICE(u); + + assert(u); + + log_unit_debug(u, "cgroup is empty"); + + switch (s->state) { + + /* Waiting for SIGCHLD is usually more interesting, + * because it includes return codes/signals. Which is + * why we ignore the cgroup events for most cases, + * except when we don't know pid which to expect the + * SIGCHLD for. */ + + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_unit_warning(u, "Daemon never wrote its PID file. Failing."); + + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + else + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + } + break; + + case SERVICE_RUNNING: + /* service_enter_running() will figure out what to do */ + service_enter_running(s, SERVICE_SUCCESS); + break; + + case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_stop_post(s, SERVICE_SUCCESS); + + break; + + case SERVICE_STOP_POST: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + if (main_pid_good(s) <= 0 && !control_pid_good(s)) + service_enter_dead(s, SERVICE_SUCCESS, true); + + break; + + default: + ; + } +} + +static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Service *s = SERVICE(u); + ServiceResult f; + + assert(s); + assert(pid >= 0); + + if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) : + is_clean_exit_lsb(code, status, &s->success_status)) + f = SERVICE_SUCCESS; + else if (code == CLD_EXITED) + f = SERVICE_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SERVICE_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SERVICE_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); + + if (s->main_pid == pid) { + /* Forking services may occasionally move to a new PID. + * As long as they update the PID file before exiting the old + * PID, they're fine. */ + if (service_load_pid_file(s, false) == 0) + return; + + s->main_pid = 0; + exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); + + if (s->main_command) { + /* If this is not a forking service than the + * main process got started and hence we copy + * the exit status so that it is recorded both + * as main and as control process exit + * status */ + + s->main_command->exec_status = s->main_exec_status; + + if (s->main_command->ignore) + f = SERVICE_SUCCESS; + } else if (s->exec_command[SERVICE_EXEC_START]) { + + /* If this is a forked process, then we should + * ignore the return value if this was + * configured for the starter process */ + + if (s->exec_command[SERVICE_EXEC_START]->ignore) + f = SERVICE_SUCCESS; + } + + log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + LOG_UNIT_ID(u), + LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s", + sigchld_code_to_string(code), status, + strna(code == CLD_EXITED + ? exit_status_to_string(status, EXIT_STATUS_FULL) + : signal_to_string(status))), + "EXIT_CODE=%s", sigchld_code_to_string(code), + "EXIT_STATUS=%i", status, + NULL); + + if (f != SERVICE_SUCCESS) + s->result = f; + + if (s->main_command && + s->main_command->command_next && + f == SERVICE_SUCCESS) { + + /* There is another command to * + * execute, so let's do that. */ + + log_unit_debug(u, "Running next main command for state %s.", service_state_to_string(s->state)); + service_run_next_main(s); + + } else { + + /* The service exited, so the service is officially + * gone. */ + s->main_command = NULL; + + switch (s->state) { + + case SERVICE_START_POST: + case SERVICE_RELOAD: + case SERVICE_STOP: + /* Need to wait until the operation is + * done */ + break; + + case SERVICE_START: + if (s->type == SERVICE_ONESHOT) { + /* This was our main goal, so let's go on */ + if (f == SERVICE_SUCCESS) + service_enter_start_post(s); + else + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); + break; + } + + /* Fall through */ + + case SERVICE_RUNNING: + service_enter_running(s, f); + break; + + case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + + if (!control_pid_good(s)) + service_enter_stop_post(s, f); + + /* If there is still a control process, wait for that first */ + break; + + case SERVICE_STOP_POST: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + + if (!control_pid_good(s)) + service_enter_dead(s, f, true); + break; + + default: + assert_not_reached("Uh, main process died at wrong time."); + } + } + + } else if (s->control_pid == pid) { + s->control_pid = 0; + + if (s->control_command) { + exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + + if (s->control_command->ignore) + f = SERVICE_SUCCESS; + } + + log_unit_full(u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, + "Control process exited, code=%s status=%i", + sigchld_code_to_string(code), status); + + if (f != SERVICE_SUCCESS) + s->result = f; + + /* Immediately get rid of the cgroup, so that the + * kernel doesn't delay the cgroup empty messages for + * the service cgroup any longer than necessary */ + service_kill_control_processes(s); + + if (s->control_command && + s->control_command->command_next && + f == SERVICE_SUCCESS) { + + /* There is another command to * + * execute, so let's do that. */ + + log_unit_debug(u, "Running next control command for state %s.", service_state_to_string(s->state)); + service_run_next_control(s); + + } else { + /* No further commands for this step, so let's + * figure out what to do next */ + + s->control_command = NULL; + s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; + + log_unit_debug(u, "Got final SIGCHLD for state %s.", service_state_to_string(s->state)); + + switch (s->state) { + + case SERVICE_START_PRE: + if (f == SERVICE_SUCCESS) + service_enter_start(s); + else + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); + break; + + case SERVICE_START: + if (s->type != SERVICE_FORKING) + /* Maybe spurious event due to a reload that changed the type? */ + break; + + if (f != SERVICE_SUCCESS) { + service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); + break; + } + + if (s->pid_file) { + bool has_start_post; + int r; + + /* Let's try to load the pid file here if we can. + * The PID file might actually be created by a START_POST + * script. In that case don't worry if the loading fails. */ + + has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + r = service_load_pid_file(s, !has_start_post); + if (!has_start_post && r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); + break; + } + } else + service_search_main_pid(s); + + service_enter_start_post(s); + break; + + case SERVICE_START_POST: + if (f != SERVICE_SUCCESS) { + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); + break; + } + + if (s->pid_file) { + int r; + + r = service_load_pid_file(s, true); + if (r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_stop(s, SERVICE_FAILURE_RESOURCES); + break; + } + } else + service_search_main_pid(s); + + service_enter_running(s, SERVICE_SUCCESS); + break; + + case SERVICE_RELOAD: + if (f == SERVICE_SUCCESS) + if (service_load_pid_file(s, true) < 0) + service_search_main_pid(s); + + s->reload_result = f; + service_enter_running(s, SERVICE_SUCCESS); + break; + + case SERVICE_STOP: + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); + break; + + case SERVICE_STOP_SIGABRT: + case SERVICE_STOP_SIGTERM: + case SERVICE_STOP_SIGKILL: + if (main_pid_good(s) <= 0) + service_enter_stop_post(s, f); + + /* If there is still a service + * process around, wait until + * that one quit, too */ + break; + + case SERVICE_STOP_POST: + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + if (main_pid_good(s) <= 0) + service_enter_dead(s, f, true); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + } + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); + + /* We got one SIGCHLD for the service, let's watch all + * processes that are now running of the service, and watch + * that. Among the PIDs we then watch will be children + * reassigned to us, which hopefully allows us to identify + * when all children are gone */ + unit_tidy_watch_pids(u, s->main_pid, s->control_pid); + unit_watch_all_pids(u); + + /* If the PID set is empty now, then let's finish this off + (On unified we use proper notifications) */ + if (cg_unified() <= 0 && set_isempty(u->pids)) + service_notify_cgroup_empty_event(u); +} + +static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + Service *s = SERVICE(userdata); + + assert(s); + assert(source == s->timer_event_source); + + switch (s->state) { + + case SERVICE_START_PRE: + case SERVICE_START: + log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre"); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_START_POST: + log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping."); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_RUNNING: + log_unit_warning(UNIT(s), "Service reached runtime time limit. Stopping."); + service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_RELOAD: + log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process."); + service_kill_control_processes(s); + s->reload_result = SERVICE_FAILURE_TIMEOUT; + service_enter_running(s, SERVICE_SUCCESS); + break; + + case SERVICE_STOP: + log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_STOP_SIGABRT: + log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating."); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_STOP_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Killing."); + service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Skipping SIGKILL."); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); + } + + break; + + case SERVICE_STOP_SIGKILL: + /* Uh, we sent a SIGKILL and it is still not gone? + * Must be something we cannot kill, so let's just be + * weirded out and continue */ + + log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring."); + service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_STOP_POST: + log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating."); + service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + + case SERVICE_FINAL_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Killing."); + service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Skipping SIGKILL. Entering failed mode."); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); + } + + break; + + case SERVICE_FINAL_SIGKILL: + log_unit_warning(UNIT(s), "Processes still around after final SIGKILL. Entering failed mode."); + service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); + break; + + case SERVICE_AUTO_RESTART: + log_unit_info(UNIT(s), + s->restart_usec > 0 ? + "Service hold-off time over, scheduling restart." : + "Service has no hold-off time, scheduling restart."); + service_enter_restart(s); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata) { + Service *s = SERVICE(userdata); + char t[FORMAT_TIMESPAN_MAX]; + usec_t watchdog_usec; + + assert(s); + assert(source == s->watchdog_event_source); + + watchdog_usec = service_get_watchdog_usec(s); + + log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", + format_timespan(t, sizeof(t), watchdog_usec, 1)); + + service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); + + return 0; +} + +static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) { + Service *s = SERVICE(u); + _cleanup_free_ char *cc = NULL; + bool notify_dbus = false; + const char *e; + + assert(u); + + cc = strv_join(tags, ", "); + + if (s->notify_access == NOTIFY_NONE) { + log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception is disabled.", pid); + return; + } else if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { + if (s->main_pid != 0) + log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid); + else + log_unit_debug(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid); + return; + } else + log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc); + + /* Interpret MAINPID= */ + e = strv_find_startswith(tags, "MAINPID="); + if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) { + if (parse_pid(e, &pid) < 0) + log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e); + else { + service_set_main_pid(s, pid); + unit_watch_pid(UNIT(s), pid); + notify_dbus = true; + } + } + + /* Interpret RELOADING= */ + if (strv_find(tags, "RELOADING=1")) { + + s->notify_state = NOTIFY_RELOADING; + + if (s->state == SERVICE_RUNNING) + service_enter_reload_by_notify(s); + + notify_dbus = true; + } + + /* Interpret READY= */ + if (strv_find(tags, "READY=1")) { + + s->notify_state = NOTIFY_READY; + + /* Type=notify services inform us about completed + * initialization with READY=1 */ + if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START) + service_enter_start_post(s); + + /* Sending READY=1 while we are reloading informs us + * that the reloading is complete */ + if (s->state == SERVICE_RELOAD && s->control_pid == 0) + service_enter_running(s, SERVICE_SUCCESS); + + notify_dbus = true; + } + + /* Interpret STOPPING= */ + if (strv_find(tags, "STOPPING=1")) { + + s->notify_state = NOTIFY_STOPPING; + + if (s->state == SERVICE_RUNNING) + service_enter_stop_by_notify(s); + + notify_dbus = true; + } + + /* Interpret STATUS= */ + e = strv_find_startswith(tags, "STATUS="); + if (e) { + _cleanup_free_ char *t = NULL; + + if (!isempty(e)) { + if (!utf8_is_valid(e)) + log_unit_warning(u, "Status message in notification message is not UTF-8 clean."); + else { + t = strdup(e); + if (!t) + log_oom(); + } + } + + if (!streq_ptr(s->status_text, t)) { + + free(s->status_text); + s->status_text = t; + t = NULL; + + notify_dbus = true; + } + } + + /* Interpret ERRNO= */ + e = strv_find_startswith(tags, "ERRNO="); + if (e) { + int status_errno; + + if (safe_atoi(e, &status_errno) < 0 || status_errno < 0) + log_unit_warning(u, "Failed to parse ERRNO= field in notification message: %s", e); + else { + if (s->status_errno != status_errno) { + s->status_errno = status_errno; + notify_dbus = true; + } + } + } + + /* Interpret WATCHDOG= */ + if (strv_find(tags, "WATCHDOG=1")) + service_reset_watchdog(s); + + 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); + } + + e = strv_find_startswith(tags, "WATCHDOG_USEC="); + if (e) { + usec_t watchdog_override_usec; + if (safe_atou64(e, &watchdog_override_usec) < 0) + log_unit_warning(u, "Failed to parse WATCHDOG_USEC=%s", e); + else + service_reset_watchdog_timeout(s, watchdog_override_usec); + } + + /* Notify clients about changed status or main pid */ + if (notify_dbus) + unit_add_to_dbus_queue(u); +} + +static int service_get_timeout(Unit *u, usec_t *timeout) { + Service *s = SERVICE(u); + uint64_t t; + int r; + + if (!s->timer_event_source) + return 0; + + r = sd_event_source_get_time(s->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +static void service_bus_name_owner_change( + Unit *u, + const char *name, + const char *old_owner, + const char *new_owner) { + + Service *s = SERVICE(u); + int r; + + assert(s); + assert(name); + + assert(streq(s->bus_name, name)); + assert(old_owner || new_owner); + + if (old_owner && new_owner) + log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", name, old_owner, new_owner); + else if (old_owner) + log_unit_debug(u, "D-Bus name %s no longer registered by %s", name, old_owner); + else + log_unit_debug(u, "D-Bus name %s now registered by %s", name, new_owner); + + 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 + * do */ + if (s->state == SERVICE_RUNNING) + service_enter_running(s, SERVICE_SUCCESS); + else if (s->state == SERVICE_START && new_owner) + service_enter_start_post(s); + + } else if (new_owner && + s->main_pid <= 0 && + (s->state == SERVICE_START || + s->state == SERVICE_START_POST || + s->state == SERVICE_RUNNING || + s->state == SERVICE_RELOAD)) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + pid_t pid; + + /* Try to acquire PID from bus service */ + + r = sd_bus_get_name_creds(u->manager->api_bus, name, SD_BUS_CREDS_PID, &creds); + if (r >= 0) + r = sd_bus_creds_get_pid(creds, &pid); + if (r >= 0) { + log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid); + + service_set_main_pid(s, pid); + unit_watch_pid(UNIT(s), pid); + } + } +} + +int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context_net) { + _cleanup_free_ char *peer = NULL; + int r; + + assert(s); + assert(fd >= 0); + + /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs + * to be configured. We take ownership of the passed fd on success. */ + + if (UNIT(s)->load_state != UNIT_LOADED) + return -EINVAL; + + if (s->socket_fd >= 0) + return -EBUSY; + + if (s->state != SERVICE_DEAD) + return -EAGAIN; + + if (getpeername_pretty(fd, true, &peer) >= 0) { + + if (UNIT(s)->description) { + _cleanup_free_ char *a; + + a = strjoin(UNIT(s)->description, " (", peer, ")", NULL); + if (!a) + return -ENOMEM; + + r = unit_set_description(UNIT(s), a); + } else + r = unit_set_description(UNIT(s), peer); + + if (r < 0) + return r; + } + + r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); + if (r < 0) + return r; + + s->socket_fd = fd; + s->socket_fd_selinux_context_net = selinux_context_net; + + unit_ref_set(&s->accept_socket, UNIT(sock)); + return 0; +} + +static void service_reset_failed(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + if (s->state == SERVICE_FAILED) + service_set_state(s, SERVICE_DEAD); + + s->result = SERVICE_SUCCESS; + s->reload_result = SERVICE_SUCCESS; +} + +static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + Service *s = SERVICE(u); + + return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); +} + +static int service_main_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->main_pid; +} + +static int service_control_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->control_pid; +} + +static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { + [SERVICE_RESTART_NO] = "no", + [SERVICE_RESTART_ON_SUCCESS] = "on-success", + [SERVICE_RESTART_ON_FAILURE] = "on-failure", + [SERVICE_RESTART_ON_ABNORMAL] = "on-abnormal", + [SERVICE_RESTART_ON_WATCHDOG] = "on-watchdog", + [SERVICE_RESTART_ON_ABORT] = "on-abort", + [SERVICE_RESTART_ALWAYS] = "always", +}; + +DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart); + +static const char* const service_type_table[_SERVICE_TYPE_MAX] = { + [SERVICE_SIMPLE] = "simple", + [SERVICE_FORKING] = "forking", + [SERVICE_ONESHOT] = "oneshot", + [SERVICE_DBUS] = "dbus", + [SERVICE_NOTIFY] = "notify", + [SERVICE_IDLE] = "idle" +}; + +DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); + +static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { + [SERVICE_EXEC_START_PRE] = "ExecStartPre", + [SERVICE_EXEC_START] = "ExecStart", + [SERVICE_EXEC_START_POST] = "ExecStartPost", + [SERVICE_EXEC_RELOAD] = "ExecReload", + [SERVICE_EXEC_STOP] = "ExecStop", + [SERVICE_EXEC_STOP_POST] = "ExecStopPost", +}; + +DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand); + +static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { + [NOTIFY_NONE] = "none", + [NOTIFY_MAIN] = "main", + [NOTIFY_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); + +static const char* const notify_state_table[_NOTIFY_STATE_MAX] = { + [NOTIFY_UNKNOWN] = "unknown", + [NOTIFY_READY] = "ready", + [NOTIFY_RELOADING] = "reloading", + [NOTIFY_STOPPING] = "stopping", +}; + +DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState); + +static const char* const service_result_table[_SERVICE_RESULT_MAX] = { + [SERVICE_SUCCESS] = "success", + [SERVICE_FAILURE_RESOURCES] = "resources", + [SERVICE_FAILURE_TIMEOUT] = "timeout", + [SERVICE_FAILURE_EXIT_CODE] = "exit-code", + [SERVICE_FAILURE_SIGNAL] = "signal", + [SERVICE_FAILURE_CORE_DUMP] = "core-dump", + [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); + +const UnitVTable service_vtable = { + .object_size = sizeof(Service), + .exec_context_offset = offsetof(Service, exec_context), + .cgroup_context_offset = offsetof(Service, cgroup_context), + .kill_context_offset = offsetof(Service, kill_context), + .exec_runtime_offset = offsetof(Service, exec_runtime), + + .sections = + "Unit\0" + "Service\0" + "Install\0", + .private_section = "Service", + + .init = service_init, + .done = service_done, + .load = service_load, + .release_resources = service_release_resources, + + .coldplug = service_coldplug, + + .dump = service_dump, + + .start = service_start, + .stop = service_stop, + .reload = service_reload, + + .can_reload = service_can_reload, + + .kill = service_kill, + + .serialize = service_serialize, + .deserialize_item = service_deserialize_item, + + .active_state = service_active_state, + .sub_state_to_string = service_sub_state_to_string, + + .check_gc = service_check_gc, + + .sigchld_event = service_sigchld_event, + + .reset_failed = service_reset_failed, + + .notify_cgroup_empty = service_notify_cgroup_empty_event, + .notify_message = service_notify_message, + + .main_pid = service_main_pid, + .control_pid = service_control_pid, + + .bus_name_owner_change = service_bus_name_owner_change, + + .bus_vtable = bus_service_vtable, + .bus_set_property = bus_service_set_property, + .bus_commit_properties = bus_service_commit_properties, + + .get_timeout = service_get_timeout, + .can_transient = true, + + .status_message_formats = { + .starting_stopping = { + [0] = "Starting %s...", + [1] = "Stopping %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Started %s.", + [JOB_FAILED] = "Failed to start %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Stopped %s.", + [JOB_FAILED] = "Stopped (with error) %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/service.h b/src/grp-system/libcore/service.h new file mode 100644 index 0000000000..34a9fbe72e --- /dev/null +++ b/src/grp-system/libcore/service.h @@ -0,0 +1,223 @@ +#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 . +***/ + +#include "basic/exit-status.h" +#include "basic/ratelimit.h" + +typedef struct Service Service; +typedef struct ServiceFDStore ServiceFDStore; + +#include "kill.h" +#include "path.h" + +typedef enum ServiceRestart { + SERVICE_RESTART_NO, + SERVICE_RESTART_ON_SUCCESS, + SERVICE_RESTART_ON_FAILURE, + SERVICE_RESTART_ON_ABNORMAL, + SERVICE_RESTART_ON_WATCHDOG, + SERVICE_RESTART_ON_ABORT, + SERVICE_RESTART_ALWAYS, + _SERVICE_RESTART_MAX, + _SERVICE_RESTART_INVALID = -1 +} ServiceRestart; + +typedef enum ServiceType { + SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */ + SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */ + SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */ + SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */ + SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */ + SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */ + _SERVICE_TYPE_MAX, + _SERVICE_TYPE_INVALID = -1 +} ServiceType; + +typedef enum ServiceExecCommand { + SERVICE_EXEC_START_PRE, + SERVICE_EXEC_START, + SERVICE_EXEC_START_POST, + SERVICE_EXEC_RELOAD, + SERVICE_EXEC_STOP, + SERVICE_EXEC_STOP_POST, + _SERVICE_EXEC_COMMAND_MAX, + _SERVICE_EXEC_COMMAND_INVALID = -1 +} ServiceExecCommand; + +typedef enum NotifyAccess { + NOTIFY_NONE, + NOTIFY_ALL, + NOTIFY_MAIN, + _NOTIFY_ACCESS_MAX, + _NOTIFY_ACCESS_INVALID = -1 +} NotifyAccess; + +typedef enum NotifyState { + NOTIFY_UNKNOWN, + NOTIFY_READY, + NOTIFY_RELOADING, + NOTIFY_STOPPING, + _NOTIFY_STATE_MAX, + _NOTIFY_STATE_INVALID = -1 +} NotifyState; + +typedef enum ServiceResult { + SERVICE_SUCCESS, + SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ + SERVICE_FAILURE_TIMEOUT, + SERVICE_FAILURE_EXIT_CODE, + SERVICE_FAILURE_SIGNAL, + SERVICE_FAILURE_CORE_DUMP, + SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT_HIT, + _SERVICE_RESULT_MAX, + _SERVICE_RESULT_INVALID = -1 +} ServiceResult; + +struct ServiceFDStore { + Service *service; + + int fd; + char *fdname; + sd_event_source *event_source; + + LIST_FIELDS(ServiceFDStore, fd_store); +}; + +struct Service { + Unit meta; + + ServiceType type; + ServiceRestart restart; + ExitStatusSet restart_prevent_status; + ExitStatusSet restart_force_status; + ExitStatusSet success_status; + + /* If set we'll read the main daemon PID from this file */ + char *pid_file; + + usec_t restart_usec; + usec_t timeout_start_usec; + usec_t timeout_stop_usec; + usec_t runtime_max_usec; + + dual_timestamp watchdog_timestamp; + usec_t watchdog_usec; + usec_t watchdog_override_usec; + bool watchdog_override_enable; + sd_event_source *watchdog_event_source; + + ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX]; + + ExecContext exec_context; + KillContext kill_context; + CGroupContext cgroup_context; + + ServiceState state, deserialized_state; + + /* The exit status of the real main process */ + ExecStatus main_exec_status; + + /* The currently executed control process */ + ExecCommand *control_command; + + /* The currently executed main process, which may be NULL if + * the main process got started via forking mode and not by + * us */ + ExecCommand *main_command; + + /* The ID of the control command currently being executed */ + ServiceExecCommand control_command_id; + + /* Runtime data of the execution context */ + ExecRuntime *exec_runtime; + + pid_t main_pid, control_pid; + int socket_fd; + bool socket_fd_selinux_context_net; + + bool permissions_start_only; + bool root_directory_start_only; + bool remain_after_exit; + bool guess_main_pid; + + /* If we shut down, remember why */ + ServiceResult result; + ServiceResult reload_result; + + bool main_pid_known:1; + bool main_pid_alien:1; + bool bus_name_good:1; + bool forbid_restart:1; + bool start_timeout_defined:1; + + bool reset_cpu_usage:1; + + char *bus_name; + char *bus_name_owner; /* unique name of the current owner */ + + char *status_text; + int status_errno; + + FailureAction failure_action; + + UnitRef accept_socket; + + sd_event_source *timer_event_source; + PathSpec *pid_file_pathspec; + + NotifyAccess notify_access; + NotifyState notify_state; + + ServiceFDStore *fd_store; + unsigned n_fd_store; + unsigned n_fd_store_max; + + 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); +void service_close_socket_fd(Service *s); + +const char* service_restart_to_string(ServiceRestart i) _const_; +ServiceRestart service_restart_from_string(const char *s) _pure_; + +const char* service_type_to_string(ServiceType i) _const_; +ServiceType service_type_from_string(const char *s) _pure_; + +const char* service_exec_command_to_string(ServiceExecCommand i) _const_; +ServiceExecCommand service_exec_command_from_string(const char *s) _pure_; + +const char* notify_access_to_string(NotifyAccess i) _const_; +NotifyAccess notify_access_from_string(const char *s) _pure_; + +const char* notify_state_to_string(NotifyState i) _const_; +NotifyState notify_state_from_string(const char *s) _pure_; + +const char* service_result_to_string(ServiceResult i) _const_; +ServiceResult service_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/show-status.c b/src/grp-system/libcore/show-status.c new file mode 100644 index 0000000000..bb6889630d --- /dev/null +++ b/src/grp-system/libcore/show-status.c @@ -0,0 +1,125 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "show-status.h" + +int parse_show_status(const char *v, ShowStatus *ret) { + int r; + + assert(v); + assert(ret); + + if (streq(v, "auto")) { + *ret = SHOW_STATUS_AUTO; + return 0; + } + + r = parse_boolean(v); + if (r < 0) + return r; + + *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/grp-system/libcore/show-status.h b/src/grp-system/libcore/show-status.h new file mode 100644 index 0000000000..56c56eb358 --- /dev/null +++ b/src/grp-system/libcore/show-status.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/macro.h" + +/* Manager status */ + +typedef enum ShowStatus { + _SHOW_STATUS_UNSET = -2, + SHOW_STATUS_AUTO = -1, + SHOW_STATUS_NO = 0, + SHOW_STATUS_YES = 1, + SHOW_STATUS_TEMPORARY = 2, +} 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/grp-system/libcore/slice.c b/src/grp-system/libcore/slice.c new file mode 100644 index 0000000000..d646b838ac --- /dev/null +++ b/src/grp-system/libcore/slice.c @@ -0,0 +1,347 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" + +#include "dbus-slice.h" +#include "slice.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { + [SLICE_DEAD] = UNIT_INACTIVE, + [SLICE_ACTIVE] = UNIT_ACTIVE +}; + +static void slice_init(Unit *u) { + assert(u); + assert(u->load_state == UNIT_STUB); + + u->ignore_on_isolate = true; +} + +static void slice_set_state(Slice *t, SliceState state) { + SliceState old_state; + assert(t); + + old_state = t->state; + t->state = state; + + if (state != old_state) + log_debug("%s changed %s -> %s", + UNIT(t)->id, + slice_state_to_string(old_state), + slice_state_to_string(state)); + + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); +} + +static int slice_add_parent_slice(Slice *s) { + char *a, *dash; + Unit *parent; + int r; + + assert(s); + + if (UNIT_ISSET(UNIT(s)->slice)) + return 0; + + if (unit_has_name(UNIT(s), SPECIAL_ROOT_SLICE)) + return 0; + + a = strdupa(UNIT(s)->id); + dash = strrchr(a, '-'); + if (dash) + strcpy(dash, ".slice"); + else + a = (char*) SPECIAL_ROOT_SLICE; + + r = manager_load_unit(UNIT(s)->manager, a, NULL, NULL, &parent); + if (r < 0) + return r; + + unit_ref_set(&UNIT(s)->slice, parent); + return 0; +} + +static int slice_add_default_dependencies(Slice *s) { + int r; + + 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), + UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); + if (r < 0) + return r; + + return 0; +} + +static int slice_verify(Slice *s) { + _cleanup_free_ char *parent = NULL; + int r; + + assert(s); + + if (UNIT(s)->load_state != UNIT_LOADED) + return 0; + + if (!slice_name_is_valid(UNIT(s)->id)) { + log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id); + return -EINVAL; + } + + r = slice_build_parent_slice(UNIT(s)->id, &parent); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m"); + + if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) { + log_unit_error(UNIT(s), "Located outside of parent slice. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int slice_load(Unit *u) { + Slice *s = SLICE(u); + int r; + + assert(s); + assert(u->load_state == UNIT_STUB); + + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) + return r; + + /* This is a new unit? Then let's add in some extras */ + if (u->load_state == UNIT_LOADED) { + + r = unit_patch_contexts(u); + if (r < 0) + return r; + + r = slice_add_parent_slice(s); + if (r < 0) + return r; + + r = slice_add_default_dependencies(s); + if (r < 0) + return r; + } + + return slice_verify(s); +} + +static int slice_coldplug(Unit *u) { + Slice *t = SLICE(u); + + assert(t); + assert(t->state == SLICE_DEAD); + + if (t->deserialized_state != t->state) + slice_set_state(t, t->deserialized_state); + + return 0; +} + +static void slice_dump(Unit *u, FILE *f, const char *prefix) { + Slice *t = SLICE(u); + + assert(t); + assert(f); + + fprintf(f, + "%sSlice State: %s\n", + prefix, slice_state_to_string(t->state)); + + cgroup_context_dump(&t->cgroup_context, f, prefix); +} + +static int slice_start(Unit *u) { + Slice *t = SLICE(u); + + assert(t); + assert(t->state == SLICE_DEAD); + + (void) unit_realize_cgroup(u); + (void) unit_reset_cpu_usage(u); + + slice_set_state(t, SLICE_ACTIVE); + return 1; +} + +static int slice_stop(Unit *u) { + Slice *t = SLICE(u); + + assert(t); + assert(t->state == SLICE_ACTIVE); + + /* We do not need to destroy the cgroup explicitly, + * unit_notify() will do that for us anyway. */ + + slice_set_state(t, SLICE_DEAD); + return 1; +} + +static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, -1, error); +} + +static int slice_serialize(Unit *u, FILE *f, FDSet *fds) { + Slice *s = SLICE(u); + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", slice_state_to_string(s->state)); + return 0; +} + +static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Slice *s = SLICE(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + SliceState state; + + state = slice_state_from_string(value); + if (state < 0) + log_debug("Failed to parse state value %s", value); + else + s->deserialized_state = state; + + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + +_pure_ static UnitActiveState slice_active_state(Unit *u) { + assert(u); + + return state_translation_table[SLICE(u)->state]; +} + +_pure_ static const char *slice_sub_state_to_string(Unit *u) { + assert(u); + + return slice_state_to_string(SLICE(u)->state); +} + +static void slice_enumerate(Manager *m) { + Unit *u; + int r; + + assert(m); + + u = manager_get_unit(m, SPECIAL_ROOT_SLICE); + if (!u) { + u = unit_new(m, sizeof(Slice)); + if (!u) { + log_oom(); + return; + } + + r = unit_add_name(u, SPECIAL_ROOT_SLICE); + if (r < 0) { + unit_free(u); + 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) + u->description = strdup("Root Slice"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd.special(7)"); + + unit_add_to_load_queue(u); + unit_add_to_dbus_queue(u); +} + +const UnitVTable slice_vtable = { + .object_size = sizeof(Slice), + .cgroup_context_offset = offsetof(Slice, cgroup_context), + + .sections = + "Unit\0" + "Slice\0" + "Install\0", + .private_section = "Slice", + + .can_transient = true, + + .init = slice_init, + .load = slice_load, + + .coldplug = slice_coldplug, + + .dump = slice_dump, + + .start = slice_start, + .stop = slice_stop, + + .kill = slice_kill, + + .serialize = slice_serialize, + .deserialize_item = slice_deserialize_item, + + .active_state = slice_active_state, + .sub_state_to_string = slice_sub_state_to_string, + + .bus_vtable = bus_slice_vtable, + .bus_set_property = bus_slice_set_property, + .bus_commit_properties = bus_slice_commit_properties, + + .enumerate = slice_enumerate, + + .status_message_formats = { + .finished_start_job = { + [JOB_DONE] = "Created slice %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Removed slice %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/slice.h b/src/grp-system/libcore/slice.h new file mode 100644 index 0000000000..c9f3f61067 --- /dev/null +++ b/src/grp-system/libcore/slice.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Slice Slice; + +struct Slice { + Unit meta; + + SliceState state, deserialized_state; + + CGroupContext cgroup_context; +}; + +extern const UnitVTable slice_vtable; diff --git a/src/grp-system/libcore/smack-setup.c b/src/grp-system/libcore/smack-setup.c new file mode 100644 index 0000000000..ab316b69c9 --- /dev/null +++ b/src/grp-system/libcore/smack-setup.c @@ -0,0 +1,347 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation + Authors: + Nathaniel Chen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "smack-setup.h" + +#ifdef HAVE_SMACK + +static int write_access2_rules(const char* srcdir) { + _cleanup_close_ int load2_fd = -1, change_fd = -1; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *entry; + char buf[NAME_MAX]; + int dfd = -1; + int r = 0; + + load2_fd = open("/sys/fs/smackfs/load2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (load2_fd < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/load2': %m"); + return -errno; /* negative error */ + } + + change_fd = open("/sys/fs/smackfs/change-rule", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (change_fd < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/change-rule': %m"); + return -errno; /* negative error */ + } + + /* write rules to load2 or change-rule 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; + + if (!dirent_is_file(entry)) + continue; + + 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)) { + + _cleanup_free_ char *sbj = NULL, *obj = NULL, *acc1 = NULL, *acc2 = NULL; + + if (isempty(truncate_nl(buf))) + continue; + + /* if 3 args -> load rule : subject object access1 */ + /* if 4 args -> change rule : subject object access1 access2 */ + if (sscanf(buf, "%ms %ms %ms %ms", &sbj, &obj, &acc1, &acc2) < 3) { + log_error_errno(errno, "Failed to parse rule '%s' in '%s', ignoring.", buf, entry->d_name); + continue; + } + + if (write(isempty(acc2) ? load2_fd : change_fd, buf, strlen(buf)) < 0) { + if (r == 0) + r = -errno; + log_error_errno(errno, "Failed to write '%s' to '%s' in '%s'", + buf, isempty(acc2) ? "/sys/fs/smackfs/load2" : "/sys/fs/smackfs/change-rule", entry->d_name); + } + } + } + + return r; +} + +static int write_cipso2_rules(const char* srcdir) { + _cleanup_close_ int cipso2_fd = -1; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *entry; + char buf[NAME_MAX]; + int dfd = -1; + int r = 0; + + cipso2_fd = open("/sys/fs/smackfs/cipso2", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (cipso2_fd < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/cipso2': %m"); + return -errno; /* negative error */ + } + + /* write rules to cipso2 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; + + if (!dirent_is_file(entry)) + continue; + + fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + if (r == 0) + r = -errno; + log_error_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; + } + + /* cipso2 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 (isempty(truncate_nl(buf))) + continue; + + if (write(cipso2_fd, buf, strlen(buf)) < 0) { + if (r == 0) + r = -errno; + log_error_errno(errno, "Failed to write '%s' to '/sys/fs/smackfs/cipso2' in '%s'", + buf, entry->d_name); + break; + } + } + } + + 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) { + +#ifdef HAVE_SMACK + + int r; + + assert(loaded_policy); + + r = write_access2_rules("/etc/smack/accesses.d/"); + switch(r) { + case -ENOENT: + log_debug("Smack is not enabled in the kernel."); + return 0; + case ENOENT: + log_debug("Smack access rules directory '/etc/smack/accesses.d/' not found"); + return 0; + case 0: + log_info("Successfully loaded Smack policies."); + break; + default: + 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 < 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/"); + switch(r) { + case -ENOENT: + log_debug("Smack/CIPSO is not enabled in the kernel."); + return 0; + case ENOENT: + log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found"); + break; + case 0: + log_info("Successfully loaded Smack/CIPSO policies."); + break; + default: + 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; + +#endif + + return 0; +} diff --git a/src/grp-system/libcore/smack-setup.h b/src/grp-system/libcore/smack-setup.h new file mode 100644 index 0000000000..78164c85e6 --- /dev/null +++ b/src/grp-system/libcore/smack-setup.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation + Authors: + Nathaniel Chen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int mac_smack_setup(bool *loaded_policy); diff --git a/src/grp-system/libcore/socket.c b/src/grp-system/libcore/socket.c new file mode 100644 index 0000000000..3e0b3e2e49 --- /dev/null +++ b/src/grp-system/libcore/socket.c @@ -0,0 +1,2994 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/copy.h" +#include "basic/def.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/label.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/smack-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "dbus-socket.h" +#include "socket.h" +#include "unit-printf.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { + [SOCKET_DEAD] = UNIT_INACTIVE, + [SOCKET_START_PRE] = UNIT_ACTIVATING, + [SOCKET_START_CHOWN] = UNIT_ACTIVATING, + [SOCKET_START_POST] = UNIT_ACTIVATING, + [SOCKET_LISTENING] = UNIT_ACTIVE, + [SOCKET_RUNNING] = UNIT_ACTIVE, + [SOCKET_STOP_PRE] = UNIT_DEACTIVATING, + [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING, + [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING, + [SOCKET_STOP_POST] = UNIT_DEACTIVATING, + [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING, + [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING, + [SOCKET_FAILED] = UNIT_FAILED +}; + +static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + +static void socket_init(Unit *u) { + Socket *s = SOCKET(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + s->backlog = SOMAXCONN; + s->timeout_usec = u->manager->default_timeout_start_usec; + s->directory_mode = 0755; + s->socket_mode = 0666; + + s->max_connections = 64; + + s->priority = -1; + s->ip_tos = -1; + s->ip_ttl = -1; + s->mark = -1; + + s->exec_context.std_output = u->manager->default_std_output; + s->exec_context.std_error = u->manager->default_std_error; + + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + + s->trigger_limit.interval = USEC_INFINITY; + s->trigger_limit.burst = (unsigned) -1; +} + +static void socket_unwatch_control_pid(Socket *s) { + assert(s); + + if (s->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(s), s->control_pid); + s->control_pid = 0; +} + +static void socket_cleanup_fd_list(SocketPort *p) { + assert(p); + + close_many(p->auxiliary_fds, p->n_auxiliary_fds); + p->auxiliary_fds = mfree(p->auxiliary_fds); + p->n_auxiliary_fds = 0; +} + +void socket_free_ports(Socket *s) { + SocketPort *p; + + assert(s); + + while ((p = s->ports)) { + LIST_REMOVE(port, s->ports, p); + + sd_event_source_unref(p->event_source); + + socket_cleanup_fd_list(p); + safe_close(p->fd); + free(p->path); + free(p); + } +} + +static void socket_done(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + socket_free_ports(s); + + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); + s->control_command = NULL; + + socket_unwatch_control_pid(s); + + unit_ref_unset(&s->service); + + s->tcp_congestion = mfree(s->tcp_congestion); + s->bind_to_device = mfree(s->bind_to_device); + + 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); + + 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); +} + +static int socket_arm_timer(Socket *s, usec_t usec) { + int r; + + assert(s); + + if (s->timer_event_source) { + r = sd_event_source_set_time(s->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + socket_dispatch_timer, s); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->timer_event_source, "socket-timer"); + + return 0; +} + +int socket_instantiate_service(Socket *s) { + _cleanup_free_ char *prefix = NULL, *name = NULL; + int r; + Unit *u; + + assert(s); + + /* This fills in s->service if it isn't filled in yet. For + * Accept=yes sockets we create the next connection service + * here. For Accept=no this is mostly a NOP since the service + * is figured out at load time anyway. */ + + if (UNIT_DEREF(s->service)) + return 0; + + if (!s->accept) + return 0; + + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) + return r; + + if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0) + return -ENOMEM; + + r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u); + if (r < 0) + return r; + + unit_ref_set(&s->service, u); + + return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); +} + +static bool have_non_accept_socket(Socket *s) { + SocketPort *p; + + assert(s); + + if (!s->accept) + return true; + + LIST_FOREACH(port, p, s->ports) { + + if (p->type != SOCKET_SOCKET) + return true; + + if (!socket_address_can_accept(&p->address)) + return true; + } + + return false; +} + +static int socket_add_mount_links(Socket *s) { + SocketPort *p; + int r; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + const char *path = NULL; + + if (p->type == SOCKET_SOCKET) + path = socket_address_get_path(&p->address); + else if (IN_SET(p->type, SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_USB_FUNCTION)) + path = p->path; + + if (!path) + continue; + + r = unit_require_mounts_for(UNIT(s), path); + if (r < 0) + return r; + } + + return 0; +} + +static int socket_add_device_link(Socket *s) { + char *t; + + assert(s); + + if (!s->bind_to_device || streq(s->bind_to_device, "lo")) + return 0; + + t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device); + 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; + + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) + return r; + } + + return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); +} + +_pure_ static bool socket_has_exec(Socket *s) { + unsigned i; + assert(s); + + for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++) + if (s->exec_command[i]) + return true; + + return false; +} + +static int socket_add_extras(Socket *s) { + Unit *u = UNIT(s); + int r; + + assert(s); + + /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit + * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept() + * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly + * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic + * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to + * process whatever is queued in one go, and thus should normally never have to be started frequently. This is + * different for Accept=yes where each connection is processed by a new service instance, and thus frequent + * service starts are typical. */ + + if (s->trigger_limit.interval == USEC_INFINITY) + s->trigger_limit.interval = 2 * USEC_PER_SEC; + + if (s->trigger_limit.burst == (unsigned) -1) { + if (s->accept) + s->trigger_limit.burst = 200; + else + s->trigger_limit.burst = 20; + } + + if (have_non_accept_socket(s)) { + + if (!UNIT_DEREF(s->service)) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) + return r; + + unit_ref_set(&s->service, x); + } + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true); + if (r < 0) + return r; + } + + r = socket_add_mount_links(s); + if (r < 0) + return r; + + r = socket_add_device_link(s); + if (r < 0) + return r; + + r = unit_patch_contexts(u); + if (r < 0) + return r; + + if (socket_has_exec(s)) { + r = unit_add_exec_dependencies(u, &s->exec_context); + if (r < 0) + return r; + + r = unit_set_default_slice(u); + if (r < 0) + return r; + } + + r = socket_add_default_dependencies(s); + if (r < 0) + return r; + + return 0; +} + +static const char *socket_find_symlink_target(Socket *s) { + const char *found = NULL; + SocketPort *p; + + LIST_FOREACH(port, p, s->ports) { + const char *f = NULL; + + switch (p->type) { + + case SOCKET_FIFO: + f = p->path; + break; + + case SOCKET_SOCKET: + if (p->address.sockaddr.un.sun_path[0] != 0) + f = p->address.sockaddr.un.sun_path; + break; + + default: + break; + } + + if (f) { + if (found) + return NULL; + + found = f; + } + } + + return found; +} + +static int socket_verify(Socket *s) { + assert(s); + + if (UNIT(s)->load_state != UNIT_LOADED) + return 0; + + if (!s->ports) { + log_unit_error(UNIT(s), "Unit lacks Listen setting. Refusing."); + return -EINVAL; + } + + if (s->accept && have_non_accept_socket(s)) { + log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing."); + return -EINVAL; + } + + if (s->accept && s->max_connections <= 0) { + log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing."); + return -EINVAL; + } + + if (s->accept && UNIT_DEREF(s->service)) { + log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing."); + return -EINVAL; + } + + if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { + log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing."); + return -EINVAL; + } + + if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) { + log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int socket_load(Unit *u) { + Socket *s = SOCKET(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + r = unit_load_fragment_and_dropin(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + /* This is a new unit? Then let's add in some extras */ + r = socket_add_extras(s); + if (r < 0) + return r; + } + + return socket_verify(s); +} + +_const_ static const char* listen_lookup(int family, int type) { + + if (family == AF_NETLINK) + return "ListenNetlink"; + + if (type == SOCK_STREAM) + return "ListenStream"; + else if (type == SOCK_DGRAM) + return "ListenDatagram"; + else if (type == SOCK_SEQPACKET) + return "ListenSequentialPacket"; + + assert_not_reached("Unknown socket type"); + return NULL; +} + +static void socket_dump(Unit *u, FILE *f, const char *prefix) { + char time_string[FORMAT_TIMESPAN_MAX]; + SocketExecCommand c; + Socket *s = SOCKET(u); + SocketPort *p; + const char *prefix2; + + assert(s); + assert(f); + + prefix = strempty(prefix); + prefix2 = strjoina(prefix, "\t"); + + fprintf(f, + "%sSocket State: %s\n" + "%sResult: %s\n" + "%sBindIPv6Only: %s\n" + "%sBacklog: %u\n" + "%sSocketMode: %04o\n" + "%sDirectoryMode: %04o\n" + "%sKeepAlive: %s\n" + "%sNoDelay: %s\n" + "%sFreeBind: %s\n" + "%sTransparent: %s\n" + "%sBroadcast: %s\n" + "%sPassCredentials: %s\n" + "%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), + prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), + prefix, s->backlog, + prefix, s->socket_mode, + prefix, s->directory_mode, + prefix, yes_no(s->keep_alive), + prefix, yes_no(s->no_delay), + prefix, yes_no(s->free_bind), + prefix, yes_no(s->transparent), + prefix, yes_no(s->broadcast), + prefix, yes_no(s->pass_cred), + 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) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, s->control_pid); + + if (s->bind_to_device) + fprintf(f, + "%sBindToDevice: %s\n", + prefix, s->bind_to_device); + + if (s->accept) + fprintf(f, + "%sAccepted: %u\n" + "%sNConnections: %u\n" + "%sMaxConnections: %u\n", + prefix, s->n_accepted, + prefix, s->n_connections, + prefix, s->max_connections); + + if (s->priority >= 0) + fprintf(f, + "%sPriority: %i\n", + prefix, s->priority); + + if (s->receive_buffer > 0) + fprintf(f, + "%sReceiveBuffer: %zu\n", + prefix, s->receive_buffer); + + if (s->send_buffer > 0) + fprintf(f, + "%sSendBuffer: %zu\n", + prefix, s->send_buffer); + + if (s->ip_tos >= 0) + fprintf(f, + "%sIPTOS: %i\n", + prefix, s->ip_tos); + + if (s->ip_ttl >= 0) + fprintf(f, + "%sIPTTL: %i\n", + prefix, s->ip_ttl); + + if (s->pipe_size > 0) + fprintf(f, + "%sPipeSize: %zu\n", + prefix, s->pipe_size); + + if (s->mark >= 0) + fprintf(f, + "%sMark: %i\n", + prefix, s->mark); + + if (s->mq_maxmsg > 0) + fprintf(f, + "%sMessageQueueMaxMessages: %li\n", + prefix, s->mq_maxmsg); + + if (s->mq_msgsize > 0) + fprintf(f, + "%sMessageQueueMessageSize: %li\n", + prefix, s->mq_msgsize); + + if (s->reuse_port) + fprintf(f, + "%sReusePort: %s\n", + prefix, yes_no(s->reuse_port)); + + if (s->smack) + fprintf(f, + "%sSmackLabel: %s\n", + prefix, s->smack); + + if (s->smack_ip_in) + fprintf(f, + "%sSmackLabelIPIn: %s\n", + prefix, s->smack_ip_in); + + if (s->smack_ip_out) + fprintf(f, + "%sSmackLabelIPOut: %s\n", + prefix, s->smack_ip_out); + + if (!isempty(s->user) || !isempty(s->group)) + fprintf(f, + "%sSocketUser: %s\n" + "%sSocketGroup: %s\n", + prefix, strna(s->user), + prefix, strna(s->group)); + + if (s->keep_alive_time > 0) + fprintf(f, + "%sKeepAliveTimeSec: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_time, USEC_PER_SEC)); + + if (s->keep_alive_interval) + fprintf(f, + "%sKeepAliveIntervalSec: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_interval, USEC_PER_SEC)); + + if (s->keep_alive_cnt) + fprintf(f, + "%sKeepAliveProbes: %u\n", + prefix, s->keep_alive_cnt); + + if (s->defer_accept) + fprintf(f, + "%sDeferAcceptSec: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->defer_accept, USEC_PER_SEC)); + + LIST_FOREACH(port, p, s->ports) { + + if (p->type == SOCKET_SOCKET) { + const char *t; + int r; + char *k = NULL; + + r = socket_address_print(&p->address, &k); + if (r < 0) + t = strerror(-r); + else + t = k; + + fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t); + free(k); + } else if (p->type == SOCKET_SPECIAL) + fprintf(f, "%sListenSpecial: %s\n", prefix, p->path); + else if (p->type == SOCKET_USB_FUNCTION) + fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path); + else if (p->type == SOCKET_MQUEUE) + fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path); + else + fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); + } + + fprintf(f, + "%sTriggerLimitIntervalSec: %s\n" + "%sTriggerLimitBurst: %u\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), + prefix, s->trigger_limit.burst); + + exec_context_dump(&s->exec_context, f, prefix); + kill_context_dump(&s->kill_context, f, prefix); + + for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) { + if (!s->exec_command[c]) + continue; + + fprintf(f, "%s-> %s:\n", + prefix, socket_exec_command_to_string(c)); + + exec_command_dump_list(s->exec_command[c], f, prefix2); + } +} + +static int instance_from_socket(int fd, unsigned nr, char **instance) { + socklen_t l; + char *r; + union sockaddr_union local, remote; + + assert(fd >= 0); + assert(instance); + + l = sizeof(local); + if (getsockname(fd, &local.sa, &l) < 0) + return -errno; + + l = sizeof(remote); + if (getpeername(fd, &remote.sa, &l) < 0) + return -errno; + + switch (local.sa.sa_family) { + + case AF_INET: { + uint32_t + a = be32toh(local.in.sin_addr.s_addr), + b = be32toh(remote.in.sin_addr.s_addr); + + if (asprintf(&r, + "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + nr, + a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, + be16toh(local.in.sin_port), + b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF, + be16toh(remote.in.sin_port)) < 0) + return -ENOMEM; + + break; + } + + case AF_INET6: { + static const unsigned char ipv4_prefix[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF + }; + + if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 && + memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { + const uint8_t + *a = local.in6.sin6_addr.s6_addr+12, + *b = remote.in6.sin6_addr.s6_addr+12; + + if (asprintf(&r, + "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u", + nr, + a[0], a[1], a[2], a[3], + be16toh(local.in6.sin6_port), + b[0], b[1], b[2], b[3], + be16toh(remote.in6.sin6_port)) < 0) + return -ENOMEM; + } else { + char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN]; + + if (asprintf(&r, + "%u-%s:%u-%s:%u", + nr, + inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)), + be16toh(local.in6.sin6_port), + inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)), + be16toh(remote.in6.sin6_port)) < 0) + return -ENOMEM; + } + + break; + } + + case AF_UNIX: { + struct ucred ucred; + int k; + + k = getpeercred(fd, &ucred); + if (k >= 0) { + if (asprintf(&r, + "%u-"PID_FMT"-"UID_FMT, + nr, ucred.pid, ucred.uid) < 0) + return -ENOMEM; + } else if (k == -ENODATA) { + /* This handles the case where somebody is + * connecting from another pid/uid namespace + * (e.g. from outside of our container). */ + if (asprintf(&r, + "%u-unknown", + nr) < 0) + return -ENOMEM; + } else + return k; + + break; + } + + default: + assert_not_reached("Unhandled socket type."); + } + + *instance = r; + return 0; +} + +static void socket_close_fds(Socket *s) { + SocketPort *p; + char **i; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + bool was_open; + + was_open = p->fd >= 0; + + p->event_source = sd_event_source_unref(p->event_source); + p->fd = safe_close(p->fd); + socket_cleanup_fd_list(p); + + /* One little note: we should normally not delete any sockets in the file system here! After all some + * other process we spawned might still have a reference of this fd and wants to continue to use + * it. Therefore we normally delete sockets in the file system before we create a new one, not after we + * stopped using one! That all said, if the user explicitly requested this, we'll delete them here + * anyway, but only then. */ + + if (!was_open || !s->remove_on_stop) + continue; + + switch (p->type) { + + case SOCKET_FIFO: + (void) unlink(p->path); + break; + + case SOCKET_MQUEUE: + (void) mq_unlink(p->path); + break; + + case SOCKET_SOCKET: + (void) socket_address_unlink(&p->address); + break; + + default: + break; + } + } + + if (s->remove_on_stop) + STRV_FOREACH(i, s->symlinks) + (void) unlink(*i); +} + +static void socket_apply_socket_options(Socket *s, int fd) { + int r; + + assert(s); + assert(fd >= 0); + + if (s->keep_alive) { + int b = s->keep_alive; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m"); + } + + if (s->keep_alive_time) { + int value = s->keep_alive_time / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m"); + } + + if (s->keep_alive_interval) { + int value = s->keep_alive_interval / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m"); + } + + if (s->keep_alive_cnt) { + int value = s->keep_alive_cnt; + if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m"); + } + + if (s->defer_accept) { + int value = s->defer_accept / USEC_PER_SEC; + if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m"); + } + + if (s->no_delay) { + int b = s->no_delay; + + 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) { + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m"); + } + + if (s->pass_cred) { + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m"); + } + + if (s->pass_sec) { + int one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m"); + } + + if (s->priority >= 0) + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m"); + + if (s->receive_buffer > 0) { + int value = (int) s->receive_buffer; + + /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */ + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m"); + } + + if (s->send_buffer > 0) { + int value = (int) s->send_buffer; + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_SNDBUF failed: %m"); + } + + if (s->mark >= 0) + if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_MARK failed: %m"); + + if (s->ip_tos >= 0) + if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0) + log_unit_warning_errno(UNIT(s), errno, "IP_TOS failed: %m"); + + if (s->ip_ttl >= 0) { + int x; + + r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl)); + + if (socket_ipv6_is_supported()) + x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl)); + else { + x = -1; + errno = EAFNOSUPPORT; + } + + if (r < 0 && x < 0) + log_unit_warning_errno(UNIT(s), errno, "IP_TTL/IPV6_UNICAST_HOPS failed: %m"); + } + + if (s->tcp_congestion) + if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0) + log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m"); + + if (s->smack_ip_in) { + r = mac_smack_apply_fd(fd, SMACK_ATTR_IPIN, s->smack_ip_in); + if (r < 0) + log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m"); + } + + if (s->smack_ip_out) { + r = mac_smack_apply_fd(fd, SMACK_ATTR_IPOUT, s->smack_ip_out); + if (r < 0) + log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m"); + } +} + +static void socket_apply_fifo_options(Socket *s, int fd) { + int r; + + assert(s); + assert(fd >= 0); + + if (s->pipe_size > 0) + if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) + 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, "SMACK relabelling failed, ignoring: %m"); + } +} + +static int fifo_address_create( + const char *path, + mode_t directory_mode, + mode_t socket_mode) { + + _cleanup_close_ int fd = -1; + mode_t old_mask; + struct stat st; + int r; + + assert(path); + + mkdir_parents_label(path, directory_mode); + + r = mac_selinux_create_file_prepare(path, S_IFIFO); + if (r < 0) + return r; + + /* Enforce the right access mode for the fifo */ + old_mask = umask(~ socket_mode); + + /* Include the original umask in our mask */ + (void) umask(~socket_mode | old_mask); + + r = mkfifo(path, socket_mode); + (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); + if (fd < 0) { + r = -errno; + goto fail; + } + + mac_selinux_create_file_clear(); + + if (fstat(fd, &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISFIFO(st.st_mode) || + (st.st_mode & 0777) != (socket_mode & ~old_mask) || + st.st_uid != getuid() || + st.st_gid != getgid()) { + r = -EEXIST; + goto fail; + } + + r = fd; + fd = -1; + + return r; + +fail: + mac_selinux_create_file_clear(); + return r; +} + +static int special_address_create(const char *path, bool writable) { + _cleanup_close_ int fd = -1; + struct stat st; + int r; + + assert(path); + + 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) + 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)) + return -EEXIST; + + r = fd; + fd = -1; + + return r; +} + +static int usbffs_address_create(const char *path) { + _cleanup_close_ int fd = -1; + struct stat st; + int r; + + assert(path); + + fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + /* Check whether this is a regular file (ffs endpoint)*/ + if (!S_ISREG(st.st_mode)) + return -EEXIST; + + r = fd; + fd = -1; + + return r; +} + +static int mq_address_create( + const char *path, + mode_t mq_mode, + long maxmsg, + long msgsize) { + + _cleanup_close_ int fd = -1; + struct stat st; + mode_t old_mask; + struct mq_attr _attr, *attr = NULL; + int r; + + assert(path); + + if (maxmsg > 0 && msgsize > 0) { + _attr = (struct mq_attr) { + .mq_flags = O_NONBLOCK, + .mq_maxmsg = maxmsg, + .mq_msgsize = msgsize, + }; + attr = &_attr; + } + + /* Enforce the right access mode for the mq */ + old_mask = umask(~ mq_mode); + + /* Include the original umask in our mask */ + (void) umask(~mq_mode | old_mask); + fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr); + (void) umask(old_mask); + + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + if ((st.st_mode & 0777) != (mq_mode & ~old_mask) || + st.st_uid != getuid() || + st.st_gid != getgid()) + return -EEXIST; + + r = fd; + fd = -1; + + return r; +} + +static int socket_symlink(Socket *s) { + const char *p; + char **i; + + assert(s); + + p = socket_find_symlink_target(s); + if (!p) + return 0; + + STRV_FOREACH(i, s->symlinks) + symlink_label(p, *i); + + return 0; +} + +static int usbffs_write_descs(int fd, Service *s) { + int r; + + if (!s->usb_function_descriptors || !s->usb_function_strings) + return -EINVAL; + + r = copy_file_fd(s->usb_function_descriptors, fd, false); + if (r < 0) + return r; + + return copy_file_fd(s->usb_function_strings, fd, false); +} + +static int usbffs_select_ep(const struct dirent *d) { + return d->d_name[0] != '.' && !streq(d->d_name, "ep0"); +} + +static int usbffs_dispatch_eps(SocketPort *p) { + _cleanup_free_ struct dirent **ent = NULL; + _cleanup_free_ char *path = NULL; + int r, i, n, k; + + path = dirname_malloc(p->path); + if (!path) + return -ENOMEM; + + r = scandir(path, &ent, usbffs_select_ep, alphasort); + if (r < 0) + return -errno; + + n = r; + p->auxiliary_fds = new(int, n); + if (!p->auxiliary_fds) + return -ENOMEM; + + p->n_auxiliary_fds = n; + + k = 0; + for (i = 0; i < n; ++i) { + _cleanup_free_ char *ep = NULL; + + ep = path_make_absolute(ent[i]->d_name, path); + if (!ep) + return -ENOMEM; + + path_kill_slashes(ep); + + r = usbffs_address_create(ep); + if (r < 0) + goto fail; + + p->auxiliary_fds[k] = r; + + ++k; + free(ent[i]); + } + + return r; + +fail: + close_many(p->auxiliary_fds, k); + p->auxiliary_fds = mfree(p->auxiliary_fds); + p->n_auxiliary_fds = 0; + + return r; +} + +static int socket_determine_selinux_label(Socket *s, char **ret) { + ExecCommand *c; + int r; + + assert(s); + assert(ret); + + if (s->selinux_context_from_net) { + /* If this is requested, get label from the network label */ + + r = mac_selinux_get_our_label(ret); + if (r == -EOPNOTSUPP) + goto no_label; + + } else { + /* Otherwise, get it from the executable we are about to start */ + r = socket_instantiate_service(s); + if (r < 0) + return r; + + if (!UNIT_ISSET(s->service)) + goto no_label; + + c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]; + if (!c) + goto no_label; + + r = mac_selinux_get_create_label_from_exe(c->path, ret); + if (r == -EPERM || r == -EOPNOTSUPP) + goto no_label; + } + + return r; + +no_label: + *ret = NULL; + return 0; +} + +static int socket_open_fds(Socket *s) { + _cleanup_(mac_selinux_freep) char *label = NULL; + bool know_label = false; + SocketPort *p; + int r; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + + if (p->fd >= 0) + continue; + + switch (p->type) { + + case SOCKET_SOCKET: + + if (!know_label) { + /* Figure out label, if we don't it know yet. We do it once, for the first socket where + * we need this and remember it for the rest. */ + + r = socket_determine_selinux_label(s, &label); + if (r < 0) + goto rollback; + + know_label = true; + } + + /* Apply the socket protocol */ + switch (p->address.type) { + + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (s->socket_protocol == IPPROTO_SCTP) + p->address.protocol = s->socket_protocol; + break; + + case SOCK_DGRAM: + if (s->socket_protocol == IPPROTO_UDPLITE) + p->address.protocol = s->socket_protocol; + break; + } + + r = socket_address_listen( + &p->address, + SOCK_CLOEXEC|SOCK_NONBLOCK, + s->backlog, + s->bind_ipv6_only, + s->bind_to_device, + s->reuse_port, + s->free_bind, + s->transparent, + s->directory_mode, + s->socket_mode, + label); + if (r < 0) + goto rollback; + + p->fd = r; + socket_apply_socket_options(s, p->fd); + socket_symlink(s); + break; + + case SOCKET_SPECIAL: + + p->fd = special_address_create(p->path, s->writable); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + break; + + case SOCKET_FIFO: + + p->fd = fifo_address_create( + p->path, + s->directory_mode, + s->socket_mode); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + + socket_apply_fifo_options(s, p->fd); + socket_symlink(s); + break; + + case SOCKET_MQUEUE: + + p->fd = mq_address_create( + p->path, + s->socket_mode, + s->mq_maxmsg, + s->mq_msgsize); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + break; + + case SOCKET_USB_FUNCTION: { + _cleanup_free_ char *ep = NULL; + + ep = path_make_absolute("ep0", p->path); + + p->fd = usbffs_address_create(ep); + if (p->fd < 0) { + r = p->fd; + goto rollback; + } + + r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service))); + if (r < 0) + goto rollback; + + r = usbffs_dispatch_eps(p); + if (r < 0) + goto rollback; + + break; + } + default: + assert_not_reached("Unknown port type"); + } + } + + return 0; + +rollback: + socket_close_fds(s); + return r; +} + +static void socket_unwatch_fds(Socket *s) { + SocketPort *p; + int r; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + continue; + + if (!p->event_source) + continue; + + r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); + if (r < 0) + log_unit_debug_errno(UNIT(s), r, "Failed to disable event source: %m"); + } +} + +static int socket_watch_fds(Socket *s) { + SocketPort *p; + int r; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + continue; + + if (p->event_source) { + r = sd_event_source_set_enabled(p->event_source, SD_EVENT_ON); + if (r < 0) + goto fail; + } else { + r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(p->event_source, "socket-port-io"); + } + } + + return 0; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to watch listening fds: %m"); + socket_unwatch_fds(s); + return r; +} + +enum { + SOCKET_OPEN_NONE, + SOCKET_OPEN_SOME, + SOCKET_OPEN_ALL, +}; + +static int socket_check_open(Socket *s) { + bool have_open = false, have_closed = false; + SocketPort *p; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + have_closed = true; + else + have_open = true; + + if (have_open && have_closed) + return SOCKET_OPEN_SOME; + } + + if (have_open) + return SOCKET_OPEN_ALL; + + return SOCKET_OPEN_NONE; +} + +static void socket_set_state(Socket *s, SocketState state) { + SocketState old_state; + assert(s); + + old_state = s->state; + s->state = state; + + if (!IN_SET(state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) { + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + socket_unwatch_control_pid(s); + s->control_command = NULL; + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + } + + if (state != SOCKET_LISTENING) + socket_unwatch_fds(s); + + if (!IN_SET(state, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_LISTENING, + SOCKET_RUNNING, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL)) + socket_close_fds(s); + + if (state != old_state) + log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state)); + + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); +} + +static int socket_coldplug(Unit *u) { + Socket *s = SOCKET(u); + int r; + + assert(s); + assert(s->state == SOCKET_DEAD); + + if (s->deserialized_state == s->state) + return 0; + + 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, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) { + + r = unit_watch_pid(UNIT(s), s->control_pid); + if (r < 0) + return r; + + r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); + if (r < 0) + return r; + } + + if (IN_SET(s->deserialized_state, + SOCKET_START_CHOWN, + SOCKET_START_POST, + SOCKET_LISTENING, + SOCKET_RUNNING)) { + + /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors + * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for + * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open, + * and if there's a mismatch, warn loudly. */ + + r = socket_check_open(s); + if (r == SOCKET_OPEN_NONE) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "no open socket file descriptor left. " + "The socket unit is not functional until restarted."); + else if (r == SOCKET_OPEN_SOME) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "and some socket file descriptors have not been opened yet. " + "The socket unit is not fully functional until restarted."); + } + + if (s->deserialized_state == SOCKET_LISTENING) { + r = socket_watch_fds(s); + if (r < 0) + return r; + } + + socket_set_state(s, s->deserialized_state); + return 0; +} + +static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { + _cleanup_free_ char **argv = NULL; + pid_t pid; + int r; + ExecParameters exec_params = { + .apply_permissions = true, + .apply_chroot = true, + .apply_tty_stdin = true, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, + }; + + assert(s); + assert(c); + assert(_pid); + + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } + + r = unit_setup_exec_runtime(UNIT(s)); + if (r < 0) + return r; + + r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + if (r < 0) + return r; + + r = unit_full_printf_strv(UNIT(s), c->argv, &argv); + if (r < 0) + return r; + + exec_params.argv = argv; + exec_params.environment = UNIT(s)->manager->environment; + exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; + exec_params.cgroup_path = UNIT(s)->cgroup_path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; + exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); + + r = exec_spawn(UNIT(s), + c, + &s->exec_context, + &exec_params, + s->exec_runtime, + &pid); + if (r < 0) + return r; + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + /* FIXME: we need to do something here */ + return r; + + *_pid = pid; + return 0; +} + +static int socket_chown(Socket *s, pid_t *_pid) { + pid_t pid; + int r; + + r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + if (r < 0) + goto fail; + + /* We have to resolve the user names out-of-process, hence + * let's fork here. It's messy, but well, what can we do? */ + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + SocketPort *p; + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; + int ret; + + (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + (void) ignore_signals(SIGPIPE, -1); + log_forget_fds(); + + if (!isempty(s->user)) { + const char *user = s->user; + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + ret = EXIT_USER; + goto fail_child; + } + } + + if (!isempty(s->group)) { + const char *group = s->group; + + r = get_group_creds(&group, &gid); + if (r < 0) { + ret = EXIT_GROUP; + goto fail_child; + } + } + + LIST_FOREACH(port, p, s->ports) { + const char *path = NULL; + + if (p->type == SOCKET_SOCKET) + path = socket_address_get_path(&p->address); + else if (p->type == SOCKET_FIFO) + path = p->path; + + if (!path) + continue; + + if (chown(path, uid, gid) < 0) { + r = -errno; + ret = EXIT_CHOWN; + goto fail_child; + } + } + + _exit(0); + + fail_child: + log_open(); + log_error_errno(r, "Failed to chown socket at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); + + _exit(ret); + } + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + goto fail; + + *_pid = pid; + return 0; + +fail: + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + return r; +} + +static void socket_enter_dead(Socket *s, SocketResult f) { + assert(s); + + if (f != SOCKET_SUCCESS) + s->result = f; + + exec_runtime_destroy(s->exec_runtime); + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + + exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); +} + +static void socket_enter_signal(Socket *s, SocketState state, SocketResult f); + +static void socket_enter_stop_post(Socket *s, SocketResult f) { + int r; + assert(s); + + if (f != SOCKET_SUCCESS) + s->result = f; + + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_STOP_POST; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) + goto fail; + + socket_set_state(s, SOCKET_STOP_POST); + } else + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m"); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { + int r; + + assert(s); + + if (f != SOCKET_SUCCESS) + s->result = f; + + r = unit_kill_context( + UNIT(s), + &s->kill_context, + (state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM) ? + KILL_KILL : KILL_TERMINATE, + -1, + s->control_pid, + false); + if (r < 0) + goto fail; + + if (r > 0) { + r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + if (r < 0) + goto fail; + + socket_set_state(s, state); + } else if (state == SOCKET_STOP_PRE_SIGTERM) + socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_SUCCESS); + else if (state == SOCKET_STOP_PRE_SIGKILL) + socket_enter_stop_post(s, SOCKET_SUCCESS); + else if (state == SOCKET_FINAL_SIGTERM) + socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_SUCCESS); + else + socket_enter_dead(s, SOCKET_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); + + if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) + socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); + else + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_stop_pre(Socket *s, SocketResult f) { + int r; + assert(s); + + if (f != SOCKET_SUCCESS) + s->result = f; + + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_STOP_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) + goto fail; + + socket_set_state(s, SOCKET_STOP_PRE); + } else + socket_enter_stop_post(s, SOCKET_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-pre' task: %m"); + socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_listening(Socket *s) { + int r; + assert(s); + + r = socket_watch_fds(s); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); + goto fail; + } + + socket_set_state(s, SOCKET_LISTENING); + return; + +fail: + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_start_post(Socket *s) { + int r; + assert(s); + + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_POST; + s->control_command = s->exec_command[SOCKET_EXEC_START_POST]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m"); + goto fail; + } + + socket_set_state(s, SOCKET_START_POST); + } else + socket_enter_listening(s); + + return; + +fail: + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_start_chown(Socket *s) { + int r; + + assert(s); + + r = socket_open_fds(s); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m"); + goto fail; + } + + if (!isempty(s->user) || !isempty(s->group)) { + + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_CHOWN; + s->control_command = NULL; + + r = socket_chown(s, &s->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to fork 'start-chown' task: %m"); + goto fail; + } + + socket_set_state(s, SOCKET_START_CHOWN); + } else + socket_enter_start_post(s); + + return; + +fail: + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); +} + +static void socket_enter_start_pre(Socket *s) { + int r; + assert(s); + + socket_unwatch_control_pid(s); + s->control_command_id = SOCKET_EXEC_START_PRE; + s->control_command = s->exec_command[SOCKET_EXEC_START_PRE]; + + if (s->control_command) { + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m"); + goto fail; + } + + socket_set_state(s, SOCKET_START_PRE); + } else + socket_enter_start_chown(s); + + return; + +fail: + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); +} + +static void flush_ports(Socket *s) { + SocketPort *p; + + /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy + * anymore */ + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + continue; + + (void) flush_accept(p->fd); + (void) flush_fd(p->fd); + } +} + +static void socket_enter_running(Socket *s, int cfd) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or + * close it. */ + + assert(s); + + /* We don't take connections anymore if we are supposed to shut down anyway */ + if (unit_stop_pending(UNIT(s))) { + + log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); + + if (cfd >= 0) + cfd = safe_close(cfd); + else + flush_ports(s); + + return; + } + + if (!ratelimit_test(&s->trigger_limit)) { + safe_close(cfd); + log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); + socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); + return; + } + + if (cfd < 0) { + Iterator i; + Unit *other; + bool pending = false; + + /* If there's already a start pending don't bother to + * do anything */ + SET_FOREACH(other, UNIT(s)->dependencies[UNIT_TRIGGERS], i) + if (unit_active_or_pending(other)) { + pending = true; + break; + } + + if (!pending) { + if (!UNIT_ISSET(s->service)) { + log_unit_error(UNIT(s), "Service to activate vanished, refusing activation."); + r = -ENOENT; + goto fail; + } + + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); + if (r < 0) + goto fail; + } + + socket_set_state(s, SOCKET_RUNNING); + } else { + _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL; + Service *service; + + if (s->n_connections >= s->max_connections) { + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); + safe_close(cfd); + return; + } + + r = socket_instantiate_service(s); + if (r < 0) + goto fail; + + r = instance_from_socket(cfd, s->n_accepted, &instance); + if (r < 0) { + if (r != -ENOTCONN) + goto fail; + + /* ENOTCONN is legitimate if TCP RST was received. + * This connection is over, but the socket unit lives on. */ + log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); + safe_close(cfd); + return; + } + + r = unit_name_to_prefix(UNIT(s)->id, &prefix); + if (r < 0) + goto fail; + + r = unit_name_build(prefix, instance, ".service", &name); + if (r < 0) + goto fail; + + r = unit_add_name(UNIT_DEREF(s->service), name); + if (r < 0) + goto fail; + + service = SERVICE(UNIT_DEREF(s->service)); + unit_ref_unset(&s->service); + + s->n_accepted++; + unit_choose_id(UNIT(service), name); + + r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); + if (r < 0) + goto fail; + + cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ + s->n_connections++; + + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the service + * closes and forgets the connection fd again, immediately. */ + service_close_socket_fd(service); + goto fail; + } + + /* Notify clients about changed counters */ + unit_add_to_dbus_queue(UNIT(s)); + } + + return; + +fail: + log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", + cfd >= 0 ? "template" : "non-template", + bus_error_message(&error, r)); + + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); + safe_close(cfd); +} + +static void socket_run_next(Socket *s) { + int r; + + assert(s); + assert(s->control_command); + assert(s->control_command->command_next); + + socket_unwatch_control_pid(s); + + s->control_command = s->control_command->command_next; + + r = socket_spawn(s, s->control_command, &s->control_pid); + if (r < 0) + goto fail; + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run next task: %m"); + + if (s->state == SOCKET_START_POST) + socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); + else if (s->state == SOCKET_STOP_POST) + socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); + else + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); +} + +static int socket_start(Unit *u) { + Socket *s = SOCKET(u); + int r; + + assert(s); + + /* We cannot fulfill this request right now, try again later + * please! */ + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) + return -EAGAIN; + + /* Already on it! */ + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) + return 0; + + /* Cannot run this without the service being around */ + if (UNIT_ISSET(s->service)) { + Service *service; + + service = SERVICE(UNIT_DEREF(s->service)); + + if (UNIT(service)->load_state != UNIT_LOADED) { + log_unit_error(u, "Socket service %s not loaded, refusing.", UNIT(service)->id); + return -ENOENT; + } + + /* If the service is already active we cannot start the + * socket */ + if (service->state != SERVICE_DEAD && + service->state != SERVICE_FAILED && + service->state != SERVICE_AUTO_RESTART) { + log_unit_error(u, "Socket service %s already active, refusing.", UNIT(service)->id); + return -EBUSY; + } + } + + assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); + + r = unit_start_limit_test(u); + if (r < 0) { + socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); + return r; + } + + s->result = SOCKET_SUCCESS; + s->reset_cpu_usage = true; + + socket_enter_start_pre(s); + + return 1; +} + +static int socket_stop(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + /* Already on it */ + if (IN_SET(s->state, + SOCKET_STOP_PRE, + SOCKET_STOP_PRE_SIGTERM, + SOCKET_STOP_PRE_SIGKILL, + SOCKET_STOP_POST, + SOCKET_FINAL_SIGTERM, + SOCKET_FINAL_SIGKILL)) + return 0; + + /* If there's already something running we go directly into + * kill mode. */ + if (IN_SET(s->state, + SOCKET_START_PRE, + SOCKET_START_CHOWN, + SOCKET_START_POST)) { + socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS); + return -EAGAIN; + } + + assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING); + + socket_enter_stop_pre(s, SOCKET_SUCCESS); + return 1; +} + +static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { + Socket *s = SOCKET(u); + SocketPort *p; + int r; + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", socket_state_to_string(s->state)); + unit_serialize_item(u, f, "result", socket_result_to_string(s->result)); + unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted); + + if (s->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); + + if (s->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id)); + + LIST_FOREACH(port, p, s->ports) { + int copy; + + if (p->fd < 0) + continue; + + copy = fdset_put_dup(fds, p->fd); + if (copy < 0) + return copy; + + if (p->type == SOCKET_SOCKET) { + _cleanup_free_ char *t = NULL; + + r = socket_address_print(&p->address, &t); + if (r < 0) + return r; + + if (socket_address_family(&p->address) == AF_NETLINK) + unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t); + else + unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t); + + } else if (p->type == SOCKET_SPECIAL) + unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path); + else if (p->type == SOCKET_MQUEUE) + unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path); + else if (p->type == SOCKET_USB_FUNCTION) + unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path); + else { + assert(p->type == SOCKET_FIFO); + unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path); + } + } + + return 0; +} + +static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Socket *s = SOCKET(u); + + assert(u); + assert(key); + assert(value); + + if (streq(key, "state")) { + SocketState state; + + state = socket_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, "result")) { + SocketResult f; + + f = socket_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != SOCKET_SUCCESS) + s->result = f; + + } else if (streq(key, "n-accepted")) { + unsigned k; + + if (safe_atou(value, &k) < 0) + log_unit_debug(u, "Failed to parse n-accepted value: %s", value); + else + s->n_accepted += k; + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse control-pid value: %s", value); + else + s->control_pid = pid; + } else if (streq(key, "control-command")) { + SocketExecCommand id; + + id = socket_exec_command_from_string(value); + if (id < 0) + log_unit_debug(u, "Failed to parse exec-command value: %s", value); + else { + s->control_command_id = id; + s->control_command = s->exec_command[id]; + } + } else if (streq(key, "fifo")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse fifo value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_FIFO && + path_equal_or_files_same(p->path, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "special")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse special value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_SPECIAL && + path_equal_or_files_same(p->path, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "mqueue")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse mqueue value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_MQUEUE && + streq(p->path, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "socket")) { + int fd, type, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse socket value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (socket_address_is(&p->address, value+skip, type)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "netlink")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse socket value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (socket_address_is_netlink(&p->address, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else if (streq(key, "ffs")) { + int fd, skip = 0; + SocketPort *p; + + if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) + log_unit_debug(u, "Failed to parse ffs value: %s", value); + else { + + LIST_FOREACH(port, p, s->ports) + if (p->type == SOCKET_USB_FUNCTION && + path_equal_or_files_same(p->path, value+skip)) + break; + + if (p) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); + } + } + + } else + log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); + + return 0; +} + +static void socket_distribute_fds(Unit *u, FDSet *fds) { + Socket *s = SOCKET(u); + SocketPort *p; + + assert(u); + + LIST_FOREACH(port, p, s->ports) { + Iterator i; + int fd; + + if (p->type != SOCKET_SOCKET) + continue; + + if (p->fd >= 0) + continue; + + FDSET_FOREACH(fd, fds, i) { + if (socket_address_matches_fd(&p->address, fd)) { + p->fd = fdset_remove(fds, fd); + s->deserialized_state = SOCKET_LISTENING; + break; + } + } + } +} + +_pure_ static UnitActiveState socket_active_state(Unit *u) { + assert(u); + + return state_translation_table[SOCKET(u)->state]; +} + +_pure_ static const char *socket_sub_state_to_string(Unit *u) { + assert(u); + + return socket_state_to_string(SOCKET(u)->state); +} + +const char* socket_port_type_to_string(SocketPort *p) { + + assert(p); + + switch (p->type) { + + case SOCKET_SOCKET: + + switch (p->address.type) { + + case SOCK_STREAM: + return "Stream"; + + case SOCK_DGRAM: + return "Datagram"; + + case SOCK_SEQPACKET: + return "SequentialPacket"; + + case SOCK_RAW: + if (socket_address_family(&p->address) == AF_NETLINK) + return "Netlink"; + + default: + return NULL; + } + + case SOCKET_SPECIAL: + return "Special"; + + case SOCKET_MQUEUE: + return "MessageQueue"; + + case SOCKET_FIFO: + return "FIFO"; + + case SOCKET_USB_FUNCTION: + return "USBFunction"; + + default: + return NULL; + } +} + +_pure_ static bool socket_check_gc(Unit *u) { + Socket *s = SOCKET(u); + + assert(u); + + return s->n_connections > 0; +} + +static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + SocketPort *p = userdata; + int cfd = -1; + + assert(p); + assert(fd >= 0); + + if (p->socket->state != SOCKET_LISTENING) + return 0; + + log_unit_debug(UNIT(p->socket), "Incoming traffic"); + + if (revents != EPOLLIN) { + + if (revents & EPOLLHUP) + log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); + else + log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents); + goto fail; + } + + if (p->socket->accept && + p->type == SOCKET_SOCKET && + socket_address_can_accept(&p->address)) { + + for (;;) { + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); + if (cfd < 0) { + + if (errno == EINTR) + continue; + + log_unit_error_errno(UNIT(p->socket), errno, "Failed to accept socket: %m"); + goto fail; + } + + break; + } + + socket_apply_socket_options(p->socket, cfd); + } + + socket_enter_running(p->socket, cfd); + return 0; + +fail: + socket_enter_stop_pre(p->socket, SOCKET_FAILURE_RESOURCES); + return 0; +} + +static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Socket *s = SOCKET(u); + SocketResult f; + + assert(s); + assert(pid >= 0); + + if (pid != s->control_pid) + return; + + s->control_pid = 0; + + if (is_clean_exit(code, status, NULL)) + f = SOCKET_SUCCESS; + else if (code == CLD_EXITED) + f = SOCKET_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SOCKET_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SOCKET_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown sigchld code"); + + if (s->control_command) { + exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + + if (s->control_command->ignore) + f = SOCKET_SUCCESS; + } + + log_unit_full(u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, + "Control process exited, code=%s status=%i", + sigchld_code_to_string(code), status); + + if (f != SOCKET_SUCCESS) + s->result = f; + + if (s->control_command && + s->control_command->command_next && + f == SOCKET_SUCCESS) { + + log_unit_debug(u, "Running next command for state %s", socket_state_to_string(s->state)); + socket_run_next(s); + } else { + s->control_command = NULL; + s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + + /* No further commands for this step, so let's figure + * out what to do next */ + + log_unit_debug(u, "Got final SIGCHLD for state %s", socket_state_to_string(s->state)); + + switch (s->state) { + + case SOCKET_START_PRE: + if (f == SOCKET_SUCCESS) + socket_enter_start_chown(s); + else + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f); + break; + + case SOCKET_START_CHOWN: + if (f == SOCKET_SUCCESS) + socket_enter_start_post(s); + else + socket_enter_stop_pre(s, f); + break; + + case SOCKET_START_POST: + if (f == SOCKET_SUCCESS) + socket_enter_listening(s); + else + socket_enter_stop_pre(s, f); + break; + + case SOCKET_STOP_PRE: + case SOCKET_STOP_PRE_SIGTERM: + case SOCKET_STOP_PRE_SIGKILL: + socket_enter_stop_post(s, f); + break; + + case SOCKET_STOP_POST: + case SOCKET_FINAL_SIGTERM: + case SOCKET_FINAL_SIGKILL: + socket_enter_dead(s, f); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); +} + +static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + Socket *s = SOCKET(userdata); + + assert(s); + assert(s->timer_event_source == source); + + switch (s->state) { + + case SOCKET_START_PRE: + log_unit_warning(UNIT(s), "Starting timed out. Terminating."); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); + break; + + case SOCKET_START_CHOWN: + case SOCKET_START_POST: + log_unit_warning(UNIT(s), "Starting timed out. Stopping."); + socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); + break; + + case SOCKET_STOP_PRE: + log_unit_warning(UNIT(s), "Stopping timed out. Terminating."); + socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); + break; + + case SOCKET_STOP_PRE_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Stopping timed out. Killing."); + socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL. Ignoring."); + socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); + } + break; + + case SOCKET_STOP_PRE_SIGKILL: + log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring."); + socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); + break; + + case SOCKET_STOP_POST: + log_unit_warning(UNIT(s), "Stopping timed out (2). Terminating."); + socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); + break; + + case SOCKET_FINAL_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Stopping timed out (2). Killing."); + socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Stopping timed out (2). Skipping SIGKILL. Ignoring."); + socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); + } + break; + + case SOCKET_FINAL_SIGKILL: + log_unit_warning(UNIT(s), "Still around after SIGKILL (2). Entering failed mode."); + socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +int socket_collect_fds(Socket *s, int **fds) { + int *rfds, k = 0, n = 0; + SocketPort *p; + + assert(s); + assert(fds); + + /* Called from the service code for requesting our fds */ + + LIST_FOREACH(port, p, s->ports) { + if (p->fd >= 0) + n++; + n += p->n_auxiliary_fds; + } + + if (n <= 0) { + *fds = NULL; + return 0; + } + + rfds = new(int, n); + if (!rfds) + return -ENOMEM; + + 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 == n); + + *fds = rfds; + return n; +} + +static void socket_reset_failed(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + if (s->state == SOCKET_FAILED) + socket_set_state(s, SOCKET_DEAD); + + s->result = SOCKET_SUCCESS; +} + +void socket_connection_unref(Socket *s) { + assert(s); + + /* The service is dead. Yay! + * + * This is strictly for one-instance-per-connection + * services. */ + + assert(s->n_connections > 0); + s->n_connections--; + + log_unit_debug(UNIT(s), "One connection closed, %u left.", s->n_connections); +} + +static void socket_trigger_notify(Unit *u, Unit *other) { + Socket *s = SOCKET(u); + + assert(u); + assert(other); + + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + return; + + /* Don't propagate state changes from the service if we are already down */ + if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING)) + return; + + /* We don't care for the service state if we are in Accept=yes mode */ + if (s->accept) + return; + + /* Propagate start limit hit state */ + if (other->start_limit_hit) { + socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT); + return; + } + + /* Don't propagate anything if there's still a job queued */ + if (other->job) + return; + + if (IN_SET(SERVICE(other)->state, + SERVICE_DEAD, SERVICE_FAILED, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, + SERVICE_AUTO_RESTART)) + socket_enter_listening(s); + + if (SERVICE(other)->state == SERVICE_RUNNING) + socket_set_state(s, SOCKET_RUNNING); +} + +static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error); +} + +static int socket_get_timeout(Unit *u, usec_t *timeout) { + Socket *s = SOCKET(u); + usec_t t; + int r; + + if (!s->timer_event_source) + return 0; + + r = sd_event_source_get_time(s->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +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. */ + + if (s->fdname) + return s->fdname; + + return UNIT(s)->id; +} + +static int socket_control_pid(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + return s->control_pid; +} + +static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { + [SOCKET_EXEC_START_PRE] = "StartPre", + [SOCKET_EXEC_START_CHOWN] = "StartChown", + [SOCKET_EXEC_START_POST] = "StartPost", + [SOCKET_EXEC_STOP_PRE] = "StopPre", + [SOCKET_EXEC_STOP_POST] = "StopPost" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); + +static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { + [SOCKET_SUCCESS] = "success", + [SOCKET_FAILURE_RESOURCES] = "resources", + [SOCKET_FAILURE_TIMEOUT] = "timeout", + [SOCKET_FAILURE_EXIT_CODE] = "exit-code", + [SOCKET_FAILURE_SIGNAL] = "signal", + [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", + [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult); + +const UnitVTable socket_vtable = { + .object_size = sizeof(Socket), + .exec_context_offset = offsetof(Socket, exec_context), + .cgroup_context_offset = offsetof(Socket, cgroup_context), + .kill_context_offset = offsetof(Socket, kill_context), + .exec_runtime_offset = offsetof(Socket, exec_runtime), + + .sections = + "Unit\0" + "Socket\0" + "Install\0", + .private_section = "Socket", + + .init = socket_init, + .done = socket_done, + .load = socket_load, + + .coldplug = socket_coldplug, + + .dump = socket_dump, + + .start = socket_start, + .stop = socket_stop, + + .kill = socket_kill, + + .get_timeout = socket_get_timeout, + + .serialize = socket_serialize, + .deserialize_item = socket_deserialize_item, + .distribute_fds = socket_distribute_fds, + + .active_state = socket_active_state, + .sub_state_to_string = socket_sub_state_to_string, + + .check_gc = socket_check_gc, + + .sigchld_event = socket_sigchld_event, + + .trigger_notify = socket_trigger_notify, + + .reset_failed = socket_reset_failed, + + .control_pid = socket_control_pid, + + .bus_vtable = bus_socket_vtable, + .bus_set_property = bus_socket_set_property, + .bus_commit_properties = bus_socket_commit_properties, + + .status_message_formats = { + /*.starting_stopping = { + [0] = "Starting socket %s...", + [1] = "Stopping socket %s...", + },*/ + .finished_start_job = { + [JOB_DONE] = "Listening on %s.", + [JOB_FAILED] = "Failed to listen on %s.", + [JOB_TIMEOUT] = "Timed out starting %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Closed %s.", + [JOB_FAILED] = "Failed stopping %s.", + [JOB_TIMEOUT] = "Timed out stopping %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/socket.h b/src/grp-system/libcore/socket.h new file mode 100644 index 0000000000..3b214946e0 --- /dev/null +++ b/src/grp-system/libcore/socket.h @@ -0,0 +1,186 @@ +#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 . +***/ + +#include "basic/socket-util.h" + +typedef struct Socket Socket; + +#include "mount.h" +#include "service.h" + +typedef enum SocketExecCommand { + SOCKET_EXEC_START_PRE, + SOCKET_EXEC_START_CHOWN, + SOCKET_EXEC_START_POST, + SOCKET_EXEC_STOP_PRE, + SOCKET_EXEC_STOP_POST, + _SOCKET_EXEC_COMMAND_MAX, + _SOCKET_EXEC_COMMAND_INVALID = -1 +} SocketExecCommand; + +typedef enum SocketType { + SOCKET_SOCKET, + SOCKET_FIFO, + SOCKET_SPECIAL, + SOCKET_MQUEUE, + SOCKET_USB_FUNCTION, + _SOCKET_FIFO_MAX, + _SOCKET_FIFO_INVALID = -1 +} SocketType; + +typedef enum SocketResult { + SOCKET_SUCCESS, + SOCKET_FAILURE_RESOURCES, + SOCKET_FAILURE_TIMEOUT, + SOCKET_FAILURE_EXIT_CODE, + SOCKET_FAILURE_SIGNAL, + SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_START_LIMIT_HIT, + SOCKET_FAILURE_TRIGGER_LIMIT_HIT, + SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, + _SOCKET_RESULT_MAX, + _SOCKET_RESULT_INVALID = -1 +} SocketResult; + +typedef struct SocketPort { + Socket *socket; + + SocketType type; + int fd; + int *auxiliary_fds; + int n_auxiliary_fds; + + SocketAddress address; + char *path; + sd_event_source *event_source; + + LIST_FIELDS(struct SocketPort, port); +} SocketPort; + +struct Socket { + Unit meta; + + LIST_HEAD(SocketPort, ports); + + unsigned n_accepted; + unsigned n_connections; + unsigned max_connections; + + unsigned backlog; + unsigned keep_alive_cnt; + usec_t timeout_usec; + usec_t keep_alive_time; + usec_t keep_alive_interval; + usec_t defer_accept; + + ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX]; + ExecContext exec_context; + KillContext kill_context; + CGroupContext cgroup_context; + ExecRuntime *exec_runtime; + + /* For Accept=no sockets refers to the one service we'll + activate. For Accept=yes sockets is either NULL, or filled + when the next service we spawn. */ + UnitRef service; + + SocketState state, deserialized_state; + + sd_event_source *timer_event_source; + + ExecCommand* control_command; + SocketExecCommand control_command_id; + pid_t control_pid; + + mode_t directory_mode; + mode_t socket_mode; + + SocketResult result; + + char **symlinks; + + bool accept; + bool remove_on_stop; + bool writable; + + int socket_protocol; + + /* Socket options */ + bool keep_alive; + bool no_delay; + bool free_bind; + bool transparent; + bool broadcast; + bool pass_cred; + bool pass_sec; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + SocketAddressBindIPv6Only bind_ipv6_only; + + int priority; + int mark; + size_t receive_buffer; + size_t send_buffer; + int ip_tos; + int ip_ttl; + size_t pipe_size; + char *bind_to_device; + char *tcp_congestion; + bool reuse_port; + long mq_maxmsg; + long mq_msgsize; + + char *smack; + char *smack_ip_in; + char *smack_ip_out; + + bool selinux_context_from_net; + + char *user, *group; + + bool reset_cpu_usage:1; + + char *fdname; + + RateLimit trigger_limit; +}; + +/* Called from the service code when collecting 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); + +int socket_instantiate_service(Socket *s); + +char *socket_fdname(Socket *s); + +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_; + +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_; diff --git a/src/grp-system/libcore/swap.c b/src/grp-system/libcore/swap.c new file mode 100644 index 0000000000..b787fc15a1 --- /dev/null +++ b/src/grp-system/libcore/swap.c @@ -0,0 +1,1533 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/virt.h" +#include "shared/fstab-util.h" +#include "shared/udev-util.h" + +#include "dbus-swap.h" +#include "swap.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { + [SWAP_DEAD] = UNIT_INACTIVE, + [SWAP_ACTIVATING] = UNIT_ACTIVATING, + [SWAP_ACTIVATING_DONE] = UNIT_ACTIVE, + [SWAP_ACTIVE] = UNIT_ACTIVE, + [SWAP_DEACTIVATING] = UNIT_DEACTIVATING, + [SWAP_ACTIVATING_SIGTERM] = UNIT_DEACTIVATING, + [SWAP_ACTIVATING_SIGKILL] = UNIT_DEACTIVATING, + [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING, + [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING, + [SWAP_FAILED] = UNIT_FAILED +}; + +static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); +static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + +static void swap_unset_proc_swaps(Swap *s) { + assert(s); + + if (!s->from_proc_swaps) + return; + + s->parameters_proc_swaps.what = mfree(s->parameters_proc_swaps.what); + + s->from_proc_swaps = false; +} + +static int swap_set_devnode(Swap *s, const char *devnode) { + Hashmap *swaps; + Swap *first; + int r; + + assert(s); + + r = hashmap_ensure_allocated(&UNIT(s)->manager->swaps_by_devnode, &string_hash_ops); + if (r < 0) + return r; + + swaps = UNIT(s)->manager->swaps_by_devnode; + + if (s->devnode) { + first = hashmap_get(swaps, s->devnode); + + LIST_REMOVE(same_devnode, first, s); + if (first) + hashmap_replace(swaps, first->devnode, first); + else + hashmap_remove(swaps, s->devnode); + + s->devnode = mfree(s->devnode); + } + + if (devnode) { + s->devnode = strdup(devnode); + if (!s->devnode) + return -ENOMEM; + + first = hashmap_get(swaps, s->devnode); + LIST_PREPEND(same_devnode, first, s); + + return hashmap_replace(swaps, first->devnode, first); + } + + return 0; +} + +static void swap_init(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + assert(UNIT(s)->load_state == UNIT_STUB); + + s->timeout_usec = u->manager->default_timeout_start_usec; + + s->exec_context.std_output = u->manager->default_std_output; + s->exec_context.std_error = u->manager->default_std_error; + + s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; + + s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; + + u->ignore_on_isolate = true; +} + +static void swap_unwatch_control_pid(Swap *s) { + assert(s); + + if (s->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(s), s->control_pid); + s->control_pid = 0; +} + +static void swap_done(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + swap_unset_proc_swaps(s); + swap_set_devnode(s, NULL); + + s->what = mfree(s->what); + s->parameters_fragment.what = mfree(s->parameters_fragment.what); + s->parameters_fragment.options = mfree(s->parameters_fragment.options); + + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); + s->control_command = NULL; + + swap_unwatch_control_pid(s); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); +} + +static int swap_arm_timer(Swap *s, usec_t usec) { + int r; + + assert(s); + + if (s->timer_event_source) { + r = sd_event_source_set_time(s->timer_event_source, usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT); + } + + if (usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time( + UNIT(s)->manager->event, + &s->timer_event_source, + CLOCK_MONOTONIC, + usec, 0, + swap_dispatch_timer, s); + if (r < 0) + return r; + + (void) sd_event_source_set_description(s->timer_event_source, "swap-timer"); + + return 0; +} + +static int swap_add_device_links(Swap *s) { + assert(s); + + if (!s->what) + return 0; + + if (!s->from_fragment) + return 0; + + if (is_device_path(s->what)) + return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO); + else + /* File based swap devices need to be ordered after + * systemd-remount-fs.service, since they might need a + * writable file system. */ + return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true); +} + +static int swap_add_default_dependencies(Swap *s) { + int r; + + assert(s); + + if (!UNIT(s)->default_dependencies) + return 0; + + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) + 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); +} + +static int swap_verify(Swap *s) { + _cleanup_free_ char *e = NULL; + int r; + + if (UNIT(s)->load_state != UNIT_LOADED) + return 0; + + r = unit_name_from_path(s->what, ".swap", &e); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to generate unit name from path: %m"); + + if (!unit_has_name(UNIT(s), e)) { + log_unit_error(UNIT(s), "Value of What= and unit name do not match, not loading."); + return -EINVAL; + } + + if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { + log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load."); + return -EINVAL; + } + + return 0; +} + +static int swap_load_devnode(Swap *s) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct stat st; + const char *p; + + assert(s); + + if (stat(s->what, &st) < 0 || !S_ISBLK(st.st_mode)) + return 0; + + d = udev_device_new_from_devnum(UNIT(s)->manager->udev, 'b', st.st_rdev); + if (!d) + return 0; + + p = udev_device_get_devnode(d); + if (!p) + return 0; + + return swap_set_devnode(s, p); +} + +static int swap_load(Unit *u) { + int r; + Swap *s = SWAP(u); + + assert(s); + assert(u->load_state == UNIT_STUB); + + /* Load a .swap file */ + r = unit_load_fragment_and_dropin_optional(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + + if (UNIT(s)->fragment_path) + s->from_fragment = true; + + if (!s->what) { + if (s->parameters_fragment.what) + s->what = strdup(s->parameters_fragment.what); + else if (s->parameters_proc_swaps.what) + s->what = strdup(s->parameters_proc_swaps.what); + else { + r = unit_name_to_path(u->id, &s->what); + if (r < 0) + return r; + } + + if (!s->what) + return -ENOMEM; + } + + path_kill_slashes(s->what); + + if (!UNIT(s)->description) { + r = unit_set_description(u, s->what); + if (r < 0) + return r; + } + + r = unit_require_mounts_for(UNIT(s), s->what); + if (r < 0) + return r; + + r = swap_add_device_links(s); + if (r < 0) + return r; + + r = swap_load_devnode(s); + if (r < 0) + return r; + + r = unit_patch_contexts(u); + if (r < 0) + return r; + + r = unit_add_exec_dependencies(u, &s->exec_context); + if (r < 0) + return r; + + r = unit_set_default_slice(u); + if (r < 0) + return r; + + r = swap_add_default_dependencies(s); + if (r < 0) + return r; + } + + return swap_verify(s); +} + +static int swap_setup_unit( + Manager *m, + const char *what, + const char *what_proc_swaps, + int priority, + bool set_flags) { + + _cleanup_free_ char *e = NULL; + bool delete = false; + Unit *u = NULL; + int r; + SwapParameters *p; + + assert(m); + assert(what); + assert(what_proc_swaps); + + r = unit_name_from_path(what, ".swap", &e); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to generate unit name from path: %m"); + + u = manager_get_unit(m, e); + + if (u && + SWAP(u)->from_proc_swaps && + !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) { + log_error("Swap %s appeared twice with different device paths %s and %s", e, SWAP(u)->parameters_proc_swaps.what, what_proc_swaps); + return -EEXIST; + } + + if (!u) { + delete = true; + + u = unit_new(m, sizeof(Swap)); + if (!u) + return log_oom(); + + r = unit_add_name(u, e); + if (r < 0) + goto fail; + + SWAP(u)->what = strdup(what); + if (!SWAP(u)->what) { + r = -ENOMEM; + goto fail; + } + + unit_add_to_load_queue(u); + } else + delete = false; + + p = &SWAP(u)->parameters_proc_swaps; + + if (!p->what) { + p->what = strdup(what_proc_swaps); + if (!p->what) { + r = -ENOMEM; + goto fail; + } + } + + if (set_flags) { + SWAP(u)->is_active = true; + SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; + } + + SWAP(u)->from_proc_swaps = true; + + p->priority = priority; + + unit_add_to_dbus_queue(u); + return 0; + +fail: + log_unit_warning_errno(u, r, "Failed to load swap unit: %m"); + + if (delete && u) + unit_free(u); + + return r; +} + +static int swap_process_new(Manager *m, const char *device, int prio, bool set_flags) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + const char *dn; + struct stat st; + int r; + + assert(m); + + r = swap_setup_unit(m, device, device, prio, set_flags); + if (r < 0) + return r; + + /* If this is a block device, then let's add duplicates for + * all other names of this block device */ + if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode)) + return 0; + + d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev); + if (!d) + return 0; + + /* Add the main device node */ + dn = udev_device_get_devnode(d); + if (dn && !streq(dn, device)) + swap_setup_unit(m, dn, device, prio, set_flags); + + /* Add additional units for all symlinks */ + first = udev_device_get_devlinks_list_entry(d); + udev_list_entry_foreach(item, first) { + const char *p; + + /* Don't bother with the /dev/block links */ + p = udev_list_entry_get_name(item); + + if (streq(p, device)) + continue; + + if (path_startswith(p, "/dev/block/")) + continue; + + if (stat(p, &st) >= 0) + if (!S_ISBLK(st.st_mode) || + st.st_rdev != udev_device_get_devnum(d)) + continue; + + swap_setup_unit(m, p, device, prio, set_flags); + } + + return r; +} + +static void swap_set_state(Swap *s, SwapState state) { + SwapState old_state; + Swap *other; + + assert(s); + + old_state = s->state; + s->state = state; + + if (state != SWAP_ACTIVATING && + state != SWAP_ACTIVATING_SIGTERM && + state != SWAP_ACTIVATING_SIGKILL && + state != SWAP_ACTIVATING_DONE && + state != SWAP_DEACTIVATING && + state != SWAP_DEACTIVATING_SIGTERM && + state != SWAP_DEACTIVATING_SIGKILL) { + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + swap_unwatch_control_pid(s); + s->control_command = NULL; + s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; + } + + if (state != old_state) + log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state)); + + unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); + + /* If there other units for the same device node have a job + queued it might be worth checking again if it is runnable + now. This is necessary, since swap_start() refuses + operation with EAGAIN if there's already another job for + the same device node queued. */ + LIST_FOREACH_OTHERS(same_devnode, other, s) + if (UNIT(other)->job) + job_add_to_run_queue(UNIT(other)->job); +} + +static int swap_coldplug(Unit *u) { + Swap *s = SWAP(u); + SwapState new_state = SWAP_DEAD; + int r; + + assert(s); + assert(s->state == SWAP_DEAD); + + if (s->deserialized_state != s->state) + new_state = s->deserialized_state; + else if (s->from_proc_swaps) + new_state = SWAP_ACTIVE; + + if (new_state == s->state) + return 0; + + 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) + return r; + + r = swap_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec)); + if (r < 0) + return r; + } + + swap_set_state(s, new_state); + return 0; +} + +static void swap_dump(Unit *u, FILE *f, const char *prefix) { + Swap *s = SWAP(u); + SwapParameters *p; + + assert(s); + assert(f); + + if (s->from_proc_swaps) + p = &s->parameters_proc_swaps; + else if (s->from_fragment) + p = &s->parameters_fragment; + else + p = NULL; + + fprintf(f, + "%sSwap State: %s\n" + "%sResult: %s\n" + "%sWhat: %s\n" + "%sFrom /proc/swaps: %s\n" + "%sFrom fragment: %s\n", + prefix, swap_state_to_string(s->state), + prefix, swap_result_to_string(s->result), + prefix, s->what, + prefix, yes_no(s->from_proc_swaps), + prefix, yes_no(s->from_fragment)); + + if (s->devnode) + fprintf(f, "%sDevice Node: %s\n", prefix, s->devnode); + + if (p) + fprintf(f, + "%sPriority: %i\n" + "%sOptions: %s\n", + prefix, p->priority, + prefix, strempty(p->options)); + + if (s->control_pid > 0) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, s->control_pid); + + exec_context_dump(&s->exec_context, f, prefix); + kill_context_dump(&s->kill_context, f, prefix); +} + +static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { + pid_t pid; + int r; + ExecParameters exec_params = { + .apply_permissions = true, + .apply_chroot = true, + .apply_tty_stdin = true, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, + }; + + assert(s); + assert(c); + assert(_pid); + + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } + + r = unit_setup_exec_runtime(UNIT(s)); + if (r < 0) + goto fail; + + r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + if (r < 0) + goto fail; + + exec_params.environment = UNIT(s)->manager->environment; + exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; + exec_params.cgroup_path = UNIT(s)->cgroup_path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; + exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); + + r = exec_spawn(UNIT(s), + c, + &s->exec_context, + &exec_params, + s->exec_runtime, + &pid); + if (r < 0) + goto fail; + + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) + /* FIXME: we need to do something here */ + goto fail; + + *_pid = pid; + + return 0; + +fail: + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + return r; +} + +static void swap_enter_dead(Swap *s, SwapResult f) { + assert(s); + + if (f != SWAP_SUCCESS) + s->result = f; + + exec_runtime_destroy(s->exec_runtime); + s->exec_runtime = exec_runtime_unref(s->exec_runtime); + + exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager)); + + swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); +} + +static void swap_enter_active(Swap *s, SwapResult f) { + assert(s); + + if (f != SWAP_SUCCESS) + s->result = f; + + swap_set_state(s, SWAP_ACTIVE); +} + +static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { + int r; + + assert(s); + + if (f != SWAP_SUCCESS) + s->result = f; + + r = unit_kill_context( + UNIT(s), + &s->kill_context, + (state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM) ? + KILL_KILL : KILL_TERMINATE, + -1, + s->control_pid, + false); + if (r < 0) + goto fail; + + if (r > 0) { + r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec)); + if (r < 0) + goto fail; + + swap_set_state(s, state); + } else if (state == SWAP_ACTIVATING_SIGTERM) + swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_SUCCESS); + else if (state == SWAP_DEACTIVATING_SIGTERM) + swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_SUCCESS); + else + swap_enter_dead(s, SWAP_SUCCESS); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); + swap_enter_dead(s, SWAP_FAILURE_RESOURCES); +} + +static void swap_enter_activating(Swap *s) { + _cleanup_free_ char *opts = NULL; + int r; + + assert(s); + + s->control_command_id = SWAP_EXEC_ACTIVATE; + s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE; + + if (s->from_fragment) { + int priority = -1; + + r = fstab_find_pri(s->parameters_fragment.options, &priority); + if (r < 0) + log_warning_errno(r, "Failed to parse swap priority \"%s\", ignoring: %m", s->parameters_fragment.options); + else if (r == 1 && s->parameters_fragment.priority >= 0) + log_warning("Duplicate swap priority configuration by Priority and Options fields."); + + if (r <= 0 && s->parameters_fragment.priority >= 0) { + if (s->parameters_fragment.options) + r = asprintf(&opts, "%s,pri=%i", s->parameters_fragment.options, s->parameters_fragment.priority); + else + r = asprintf(&opts, "pri=%i", s->parameters_fragment.priority); + if (r < 0) + goto fail; + } + } + + r = exec_command_set(s->control_command, "/sbin/swapon", NULL); + if (r < 0) + goto fail; + + if (s->parameters_fragment.options || opts) { + r = exec_command_append(s->control_command, "-o", + opts ? : s->parameters_fragment.options, NULL); + if (r < 0) + goto fail; + } + + r = exec_command_append(s->control_command, s->what, NULL); + if (r < 0) + goto fail; + + swap_unwatch_control_pid(s); + + r = swap_spawn(s, s->control_command, &s->control_pid); + if (r < 0) + goto fail; + + swap_set_state(s, SWAP_ACTIVATING); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapon' task: %m"); + swap_enter_dead(s, SWAP_FAILURE_RESOURCES); +} + +static void swap_enter_deactivating(Swap *s) { + int r; + + assert(s); + + s->control_command_id = SWAP_EXEC_DEACTIVATE; + s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE; + + r = exec_command_set(s->control_command, + "/sbin/swapoff", + s->what, + NULL); + if (r < 0) + goto fail; + + swap_unwatch_control_pid(s); + + r = swap_spawn(s, s->control_command, &s->control_pid); + if (r < 0) + goto fail; + + swap_set_state(s, SWAP_DEACTIVATING); + + return; + +fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run 'swapoff' task: %m"); + swap_enter_active(s, SWAP_FAILURE_RESOURCES); +} + +static int swap_start(Unit *u) { + Swap *s = SWAP(u), *other; + int r; + + assert(s); + + /* We cannot fulfill this request right now, try again later + * please! */ + + if (s->state == SWAP_DEACTIVATING || + s->state == SWAP_DEACTIVATING_SIGTERM || + s->state == SWAP_DEACTIVATING_SIGKILL || + s->state == SWAP_ACTIVATING_SIGTERM || + s->state == SWAP_ACTIVATING_SIGKILL) + return -EAGAIN; + + if (s->state == SWAP_ACTIVATING) + return 0; + + assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED); + + if (detect_container() > 0) + return -EPERM; + + /* If there's a job for another swap unit for the same node + * running, then let's not dispatch this one for now, and wait + * until that other job has finished. */ + LIST_FOREACH_OTHERS(same_devnode, other, s) + if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) + return -EAGAIN; + + r = unit_start_limit_test(u); + if (r < 0) { + swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); + return r; + } + + s->result = SWAP_SUCCESS; + s->reset_cpu_usage = true; + + swap_enter_activating(s); + return 1; +} + +static int swap_stop(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + if (s->state == SWAP_DEACTIVATING || + s->state == SWAP_DEACTIVATING_SIGTERM || + s->state == SWAP_DEACTIVATING_SIGKILL || + s->state == SWAP_ACTIVATING_SIGTERM || + s->state == SWAP_ACTIVATING_SIGKILL) + return 0; + + assert(s->state == SWAP_ACTIVATING || + s->state == SWAP_ACTIVATING_DONE || + s->state == SWAP_ACTIVE); + + if (detect_container() > 0) + return -EPERM; + + swap_enter_deactivating(s); + return 1; +} + +static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { + Swap *s = SWAP(u); + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); + unit_serialize_item(u, f, "result", swap_result_to_string(s->result)); + + if (s->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid); + + if (s->control_command_id >= 0) + unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id)); + + return 0; +} + +static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Swap *s = SWAP(u); + + assert(s); + assert(fds); + + if (streq(key, "state")) { + SwapState state; + + state = swap_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, "result")) { + SwapResult f; + + f = swap_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != SWAP_SUCCESS) + s->result = f; + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_unit_debug(u, "Failed to parse control-pid value: %s", value); + else + s->control_pid = pid; + + } else if (streq(key, "control-command")) { + SwapExecCommand id; + + id = swap_exec_command_from_string(value); + if (id < 0) + log_unit_debug(u, "Failed to parse exec-command value: %s", value); + else { + s->control_command_id = id; + s->control_command = s->exec_command + id; + } + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState swap_active_state(Unit *u) { + assert(u); + + return state_translation_table[SWAP(u)->state]; +} + +_pure_ static const char *swap_sub_state_to_string(Unit *u) { + assert(u); + + return swap_state_to_string(SWAP(u)->state); +} + +_pure_ static bool swap_check_gc(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + return s->from_proc_swaps; +} + +static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { + Swap *s = SWAP(u); + SwapResult f; + + assert(s); + assert(pid >= 0); + + if (pid != s->control_pid) + return; + + s->control_pid = 0; + + if (is_clean_exit(code, status, NULL)) + f = SWAP_SUCCESS; + else if (code == CLD_EXITED) + f = SWAP_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = SWAP_FAILURE_SIGNAL; + else if (code == CLD_DUMPED) + f = SWAP_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown code"); + + if (f != SWAP_SUCCESS) + s->result = f; + + if (s->control_command) { + exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + + s->control_command = NULL; + s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; + } + + log_unit_full(u, f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, + "Swap process exited, code=%s status=%i", sigchld_code_to_string(code), status); + + switch (s->state) { + + case SWAP_ACTIVATING: + case SWAP_ACTIVATING_DONE: + case SWAP_ACTIVATING_SIGTERM: + case SWAP_ACTIVATING_SIGKILL: + + if (f == SWAP_SUCCESS) + swap_enter_active(s, f); + else + swap_enter_dead(s, f); + break; + + case SWAP_DEACTIVATING: + case SWAP_DEACTIVATING_SIGKILL: + case SWAP_DEACTIVATING_SIGTERM: + + swap_enter_dead(s, f); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); +} + +static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + Swap *s = SWAP(userdata); + + assert(s); + assert(s->timer_event_source == source); + + switch (s->state) { + + case SWAP_ACTIVATING: + case SWAP_ACTIVATING_DONE: + log_unit_warning(UNIT(s), "Activation timed out. Stopping."); + swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); + break; + + case SWAP_DEACTIVATING: + log_unit_warning(UNIT(s), "Deactivation timed out. Stopping."); + swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); + break; + + case SWAP_ACTIVATING_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Activation timed out. Killing."); + swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Activation timed out. Skipping SIGKILL. Ignoring."); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); + } + break; + + case SWAP_DEACTIVATING_SIGTERM: + if (s->kill_context.send_sigkill) { + log_unit_warning(UNIT(s), "Deactivation timed out. Killing."); + swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); + } else { + log_unit_warning(UNIT(s), "Deactivation timed out. Skipping SIGKILL. Ignoring."); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); + } + break; + + case SWAP_ACTIVATING_SIGKILL: + case SWAP_DEACTIVATING_SIGKILL: + log_unit_warning(UNIT(s), "Swap process still around after SIGKILL. Ignoring."); + swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + +static int swap_load_proc_swaps(Manager *m, bool set_flags) { + unsigned i; + int r = 0; + + assert(m); + + rewind(m->proc_swaps); + + (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n"); + + for (i = 1;; i++) { + _cleanup_free_ char *dev = NULL, *d = NULL; + int prio = 0, k; + + k = fscanf(m->proc_swaps, + "%ms " /* device/file */ + "%*s " /* type of swap */ + "%*s " /* swap size */ + "%*s " /* used */ + "%i\n", /* priority */ + &dev, &prio); + if (k != 2) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u.", i); + continue; + } + + if (cunescape(dev, UNESCAPE_RELAX, &d) < 0) + return log_oom(); + + device_found_node(m, d, true, DEVICE_FOUND_SWAP, set_flags); + + k = swap_process_new(m, d, prio, set_flags); + if (k < 0) + r = k; + } + + return r; +} + +static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + Unit *u; + int r; + + assert(m); + assert(revents & EPOLLPRI); + + r = swap_load_proc_swaps(m, true); + if (r < 0) { + log_error_errno(r, "Failed to reread /proc/swaps: %m"); + + /* Reset flags, just in case, for late calls */ + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { + Swap *swap = SWAP(u); + + swap->is_active = swap->just_activated = false; + } + + return 0; + } + + manager_dispatch_load_queue(m); + + LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { + Swap *swap = SWAP(u); + + if (!swap->is_active) { + /* This has just been deactivated */ + + swap_unset_proc_swaps(swap); + + switch (swap->state) { + + case SWAP_ACTIVE: + swap_enter_dead(swap, SWAP_SUCCESS); + break; + + default: + /* Fire again */ + swap_set_state(swap, swap->state); + break; + } + + if (swap->what) + device_found_node(m, swap->what, false, DEVICE_FOUND_SWAP, true); + + } else if (swap->just_activated) { + + /* New swap entry */ + + switch (swap->state) { + + case SWAP_DEAD: + case SWAP_FAILED: + swap_enter_active(swap, SWAP_SUCCESS); + break; + + case SWAP_ACTIVATING: + swap_set_state(swap, SWAP_ACTIVATING_DONE); + break; + + default: + /* Nothing really changed, but let's + * issue an notification call + * nonetheless, in case somebody is + * waiting for this. */ + swap_set_state(swap, swap->state); + break; + } + } + + /* Reset the flags for later calls */ + swap->is_active = swap->just_activated = false; + } + + return 1; +} + +static Unit *swap_following(Unit *u) { + Swap *s = SWAP(u); + Swap *other, *first = NULL; + + assert(s); + + /* If the user configured the swap through /etc/fstab or + * a device unit, follow that. */ + + if (s->from_fragment) + return NULL; + + LIST_FOREACH_OTHERS(same_devnode, other, s) + if (other->from_fragment) + return UNIT(other); + + /* Otherwise, make everybody follow the unit that's named after + * the swap device in the kernel */ + + if (streq_ptr(s->what, s->devnode)) + return NULL; + + LIST_FOREACH_AFTER(same_devnode, other, s) + if (streq_ptr(other->what, other->devnode)) + return UNIT(other); + + LIST_FOREACH_BEFORE(same_devnode, other, s) { + if (streq_ptr(other->what, other->devnode)) + return UNIT(other); + + first = other; + } + + /* Fall back to the first on the list */ + return UNIT(first); +} + +static int swap_following_set(Unit *u, Set **_set) { + Swap *s = SWAP(u), *other; + Set *set; + int r; + + assert(s); + assert(_set); + + if (LIST_JUST_US(same_devnode, s)) { + *_set = NULL; + return 0; + } + + set = set_new(NULL); + if (!set) + return -ENOMEM; + + LIST_FOREACH_OTHERS(same_devnode, other, s) { + r = set_put(set, other); + if (r < 0) + goto fail; + } + + *_set = set; + return 1; + +fail: + set_free(set); + return r; +} + +static void swap_shutdown(Manager *m) { + assert(m); + + m->swap_event_source = sd_event_source_unref(m->swap_event_source); + + m->proc_swaps = safe_fclose(m->proc_swaps); + + m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); +} + +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) { + 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) { + 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) { + 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"); + } + + r = swap_load_proc_swaps(m, false); + if (r < 0) + goto fail; + + return; + +fail: + swap_shutdown(m); +} + +int swap_process_device_new(Manager *m, struct udev_device *dev) { + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_free_ char *e = NULL; + const char *dn; + Swap *s; + int r = 0; + + assert(m); + assert(dev); + + dn = udev_device_get_devnode(dev); + if (!dn) + return 0; + + r = unit_name_from_path(dn, ".swap", &e); + if (r < 0) + return r; + + s = hashmap_get(m->units, e); + if (s) + r = swap_set_devnode(s, dn); + + first = udev_device_get_devlinks_list_entry(dev); + udev_list_entry_foreach(item, first) { + _cleanup_free_ char *n = NULL; + int q; + + q = unit_name_from_path(udev_list_entry_get_name(item), ".swap", &n); + if (q < 0) + return q; + + s = hashmap_get(m->units, n); + if (s) { + q = swap_set_devnode(s, dn); + if (q < 0) + r = q; + } + } + + return r; +} + +int swap_process_device_remove(Manager *m, struct udev_device *dev) { + const char *dn; + int r = 0; + Swap *s; + + dn = udev_device_get_devnode(dev); + if (!dn) + return 0; + + while ((s = hashmap_get(m->swaps_by_devnode, dn))) { + int q; + + q = swap_set_devnode(s, NULL); + if (q < 0) + r = q; + } + + return r; +} + +static void swap_reset_failed(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + if (s->state == SWAP_FAILED) + swap_set_state(s, SWAP_DEAD); + + s->result = SWAP_SUCCESS; +} + +static int swap_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error); +} + +static int swap_get_timeout(Unit *u, usec_t *timeout) { + Swap *s = SWAP(u); + usec_t t; + int r; + + if (!s->timer_event_source) + return 0; + + r = sd_event_source_get_time(s->timer_event_source, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + return 0; + + *timeout = t; + return 1; +} + +static bool swap_supported(void) { + static int supported = -1; + + /* If swap support is not available in the kernel, or we are + * running in a container we don't support swap units, and any + * attempts to starting one should fail immediately. */ + + if (supported < 0) + supported = + access("/proc/swaps", F_OK) >= 0 && + detect_container() <= 0; + + return supported; +} + +static int swap_control_pid(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + return s->control_pid; +} + +static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { + [SWAP_EXEC_ACTIVATE] = "ExecActivate", + [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", +}; + +DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand); + +static const char* const swap_result_table[_SWAP_RESULT_MAX] = { + [SWAP_SUCCESS] = "success", + [SWAP_FAILURE_RESOURCES] = "resources", + [SWAP_FAILURE_TIMEOUT] = "timeout", + [SWAP_FAILURE_EXIT_CODE] = "exit-code", + [SWAP_FAILURE_SIGNAL] = "signal", + [SWAP_FAILURE_CORE_DUMP] = "core-dump", + [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); + +const UnitVTable swap_vtable = { + .object_size = sizeof(Swap), + .exec_context_offset = offsetof(Swap, exec_context), + .cgroup_context_offset = offsetof(Swap, cgroup_context), + .kill_context_offset = offsetof(Swap, kill_context), + .exec_runtime_offset = offsetof(Swap, exec_runtime), + + .sections = + "Unit\0" + "Swap\0" + "Install\0", + .private_section = "Swap", + + .init = swap_init, + .load = swap_load, + .done = swap_done, + + .coldplug = swap_coldplug, + + .dump = swap_dump, + + .start = swap_start, + .stop = swap_stop, + + .kill = swap_kill, + + .get_timeout = swap_get_timeout, + + .serialize = swap_serialize, + .deserialize_item = swap_deserialize_item, + + .active_state = swap_active_state, + .sub_state_to_string = swap_sub_state_to_string, + + .check_gc = swap_check_gc, + + .sigchld_event = swap_sigchld_event, + + .reset_failed = swap_reset_failed, + + .control_pid = swap_control_pid, + + .bus_vtable = bus_swap_vtable, + .bus_set_property = bus_swap_set_property, + .bus_commit_properties = bus_swap_commit_properties, + + .following = swap_following, + .following_set = swap_following_set, + + .enumerate = swap_enumerate, + .shutdown = swap_shutdown, + .supported = swap_supported, + + .status_message_formats = { + .starting_stopping = { + [0] = "Activating swap %s...", + [1] = "Deactivating swap %s...", + }, + .finished_start_job = { + [JOB_DONE] = "Activated swap %s.", + [JOB_FAILED] = "Failed to activate swap %s.", + [JOB_TIMEOUT] = "Timed out activating swap %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Deactivated swap %s.", + [JOB_FAILED] = "Failed deactivating swap %s.", + [JOB_TIMEOUT] = "Timed out deactivating swap %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/swap.h b/src/grp-system/libcore/swap.h new file mode 100644 index 0000000000..c2cbfc5d2a --- /dev/null +++ b/src/grp-system/libcore/swap.h @@ -0,0 +1,110 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2010 Maarten Lankhorst + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +typedef struct Swap Swap; + +typedef enum SwapExecCommand { + SWAP_EXEC_ACTIVATE, + SWAP_EXEC_DEACTIVATE, + _SWAP_EXEC_COMMAND_MAX, + _SWAP_EXEC_COMMAND_INVALID = -1 +} SwapExecCommand; + +typedef enum SwapResult { + SWAP_SUCCESS, + SWAP_FAILURE_RESOURCES, + SWAP_FAILURE_TIMEOUT, + SWAP_FAILURE_EXIT_CODE, + SWAP_FAILURE_SIGNAL, + SWAP_FAILURE_CORE_DUMP, + SWAP_FAILURE_START_LIMIT_HIT, + _SWAP_RESULT_MAX, + _SWAP_RESULT_INVALID = -1 +} SwapResult; + +typedef struct SwapParameters { + char *what; + char *options; + int priority; +} SwapParameters; + +struct Swap { + Unit meta; + + char *what; + + /* If the device has already shown up, this is the device + * node, which might be different from what, due to + * symlinks */ + char *devnode; + + SwapParameters parameters_proc_swaps; + SwapParameters parameters_fragment; + + bool from_proc_swaps:1; + bool from_fragment:1; + + /* Used while looking for swaps that vanished or got added + * from/to /proc/swaps */ + bool is_active:1; + bool just_activated:1; + + bool reset_cpu_usage:1; + + SwapResult result; + + usec_t timeout_usec; + + ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX]; + ExecContext exec_context; + KillContext kill_context; + CGroupContext cgroup_context; + + ExecRuntime *exec_runtime; + + SwapState state, deserialized_state; + + ExecCommand* control_command; + SwapExecCommand control_command_id; + pid_t control_pid; + + sd_event_source *timer_event_source; + + /* In order to be able to distinguish dependencies on + different device nodes we might end up creating multiple + devices for the same swap. We chain them up here. */ + + LIST_FIELDS(struct Swap, same_devnode); +}; + +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_exec_command_to_string(SwapExecCommand i) _const_; +SwapExecCommand swap_exec_command_from_string(const char *s) _pure_; + +const char* swap_result_to_string(SwapResult i) _const_; +SwapResult swap_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/target.c b/src/grp-system/libcore/target.c new file mode 100644 index 0000000000..e62a49be84 --- /dev/null +++ b/src/grp-system/libcore/target.c @@ -0,0 +1,224 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/log.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" + +#include "dbus-target.h" +#include "target.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = { + [TARGET_DEAD] = UNIT_INACTIVE, + [TARGET_ACTIVE] = UNIT_ACTIVE +}; + +static void target_set_state(Target *t, TargetState state) { + TargetState old_state; + assert(t); + + old_state = t->state; + t->state = state; + + if (state != old_state) + log_debug("%s changed %s -> %s", + UNIT(t)->id, + target_state_to_string(old_state), + target_state_to_string(state)); + + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); +} + +static int target_add_default_dependencies(Target *t) { + + static const UnitDependency deps[] = { + UNIT_REQUIRES, + UNIT_REQUISITE, + UNIT_WANTS, + UNIT_BINDS_TO, + UNIT_PART_OF + }; + + Iterator i; + Unit *other; + int r; + unsigned k; + + assert(t); + + /* Imply ordering for requirement dependencies on target + * units. Note that when the user created a contradicting + * ordering manually we won't add anything in here to make + * sure we don't create a loop. */ + + for (k = 0; k < ELEMENTSOF(deps); k++) + SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(other, UNIT(t)); + if (r < 0) + return r; + } + + /* Make sure targets are unloaded on shutdown */ + return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); +} + +static int target_load(Unit *u) { + Target *t = TARGET(u); + int r; + + assert(t); + + r = unit_load_fragment_and_dropin(u); + if (r < 0) + return r; + + /* This is a new unit? Then let's add in some extras */ + if (u->load_state == UNIT_LOADED && u->default_dependencies) { + r = target_add_default_dependencies(t); + if (r < 0) + return r; + } + + return 0; +} + +static int target_coldplug(Unit *u) { + Target *t = TARGET(u); + + assert(t); + assert(t->state == TARGET_DEAD); + + if (t->deserialized_state != t->state) + target_set_state(t, t->deserialized_state); + + return 0; +} + +static void target_dump(Unit *u, FILE *f, const char *prefix) { + Target *t = TARGET(u); + + assert(t); + assert(f); + + fprintf(f, + "%sTarget State: %s\n", + prefix, target_state_to_string(t->state)); +} + +static int target_start(Unit *u) { + Target *t = TARGET(u); + + assert(t); + assert(t->state == TARGET_DEAD); + + target_set_state(t, TARGET_ACTIVE); + return 1; +} + +static int target_stop(Unit *u) { + Target *t = TARGET(u); + + assert(t); + assert(t->state == TARGET_ACTIVE); + + target_set_state(t, TARGET_DEAD); + return 1; +} + +static int target_serialize(Unit *u, FILE *f, FDSet *fds) { + Target *s = TARGET(u); + + assert(s); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", target_state_to_string(s->state)); + return 0; +} + +static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Target *s = TARGET(u); + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + TargetState state; + + state = target_state_from_string(value); + if (state < 0) + log_debug("Failed to parse state value %s", value); + else + s->deserialized_state = state; + + } else + log_debug("Unknown serialization key '%s'", key); + + return 0; +} + +_pure_ static UnitActiveState target_active_state(Unit *u) { + assert(u); + + return state_translation_table[TARGET(u)->state]; +} + +_pure_ static const char *target_sub_state_to_string(Unit *u) { + assert(u); + + return target_state_to_string(TARGET(u)->state); +} + +const UnitVTable target_vtable = { + .object_size = sizeof(Target), + + .sections = + "Unit\0" + "Target\0" + "Install\0", + + .load = target_load, + .coldplug = target_coldplug, + + .dump = target_dump, + + .start = target_start, + .stop = target_stop, + + .serialize = target_serialize, + .deserialize_item = target_deserialize_item, + + .active_state = target_active_state, + .sub_state_to_string = target_sub_state_to_string, + + .bus_vtable = bus_target_vtable, + + .status_message_formats = { + .finished_start_job = { + [JOB_DONE] = "Reached target %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Stopped target %s.", + }, + }, +}; diff --git a/src/grp-system/libcore/target.h b/src/grp-system/libcore/target.h new file mode 100644 index 0000000000..339aea154e --- /dev/null +++ b/src/grp-system/libcore/target.h @@ -0,0 +1,30 @@ +#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 . +***/ + +typedef struct Target Target; + +struct Target { + Unit meta; + + TargetState state, deserialized_state; +}; + +extern const UnitVTable target_vtable; diff --git a/src/grp-system/libcore/timer.c b/src/grp-system/libcore/timer.c new file mode 100644 index 0000000000..8b9fa71d27 --- /dev/null +++ b/src/grp-system/libcore/timer.c @@ -0,0 +1,860 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/random-util.h" +#include "basic/special.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/virt.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#include "dbus-timer.h" +#include "timer.h" +#include "unit.h" + +static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = { + [TIMER_DEAD] = UNIT_INACTIVE, + [TIMER_WAITING] = UNIT_ACTIVE, + [TIMER_RUNNING] = UNIT_ACTIVE, + [TIMER_ELAPSED] = UNIT_ACTIVE, + [TIMER_FAILED] = UNIT_FAILED +}; + +static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata); + +static void timer_init(Unit *u) { + Timer *t = TIMER(u); + + assert(u); + assert(u->load_state == UNIT_STUB); + + 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) { + TimerValue *v; + + assert(t); + + while ((v = t->values)) { + LIST_REMOVE(value, t->values, v); + calendar_spec_free(v->calendar_spec); + free(v); + } +} + +static void timer_done(Unit *u) { + Timer *t = TIMER(u); + + assert(t); + + timer_free_values(t); + + t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source); + t->realtime_event_source = sd_event_source_unref(t->realtime_event_source); + + free(t->stamp_path); +} + +static int timer_verify(Timer *t) { + assert(t); + + if (UNIT(t)->load_state != UNIT_LOADED) + return 0; + + if (!t->values) { + log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); + return -EINVAL; + } + + return 0; +} + +static int timer_add_default_dependencies(Timer *t) { + int r; + TimerValue *v; + + 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; + + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { + r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) + return r; + + LIST_FOREACH(value, v, t->values) { + if (v->base == TIMER_CALENDAR) { + r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true); + if (r < 0) + return r; + break; + } + } + } + + return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); +} + +static int timer_setup_persistent(Timer *t) { + int r; + + assert(t); + + if (!t->persistent) + return 0; + + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { + + r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers"); + if (r < 0) + return r; + + t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id); + } else { + const char *e; + + e = getenv("XDG_DATA_HOME"); + if (e) + t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL); + else { + + _cleanup_free_ char *h = NULL; + + r = get_home_dir(&h); + if (r < 0) + return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m"); + + t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL); + } + } + + if (!t->stamp_path) + return log_oom(); + + return 0; +} + +static int timer_load(Unit *u) { + Timer *t = TIMER(u); + int r; + + assert(u); + assert(u->load_state == UNIT_STUB); + + r = unit_load_fragment_and_dropin(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_LOADED) { + + if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { + Unit *x; + + r = unit_load_related_unit(u, ".service", &x); + if (r < 0) + return r; + + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; + } + + r = timer_setup_persistent(t); + if (r < 0) + return r; + + r = timer_add_default_dependencies(t); + if (r < 0) + return r; + } + + return timer_verify(t); +} + +static void timer_dump(Unit *u, FILE *f, const char *prefix) { + char buf[FORMAT_TIMESPAN_MAX]; + Timer *t = TIMER(u); + Unit *trigger; + TimerValue *v; + + trigger = UNIT_TRIGGER(u); + + fprintf(f, + "%sTimer State: %s\n" + "%sResult: %s\n" + "%sUnit: %s\n" + "%sPersistent: %s\n" + "%sWakeSystem: %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, yes_no(t->remain_after_elapse)); + + LIST_FOREACH(value, v, t->values) { + + if (v->base == TIMER_CALENDAR) { + _cleanup_free_ char *p = NULL; + + calendar_spec_to_string(v->calendar_spec, &p); + + fprintf(f, + "%s%s: %s\n", + prefix, + timer_base_to_string(v->base), + strna(p)); + } else { + char timespan1[FORMAT_TIMESPAN_MAX]; + + fprintf(f, + "%s%s: %s\n", + prefix, + timer_base_to_string(v->base), + format_timespan(timespan1, sizeof(timespan1), v->value, 0)); + } + } +} + +static void timer_set_state(Timer *t, TimerState state) { + TimerState old_state; + assert(t); + + old_state = t->state; + t->state = state; + + if (state != TIMER_WAITING) { + t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source); + t->realtime_event_source = sd_event_source_unref(t->realtime_event_source); + } + + if (state != old_state) + log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state)); + + unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true); +} + +static void timer_enter_waiting(Timer *t, bool initial); + +static int timer_coldplug(Unit *u) { + Timer *t = TIMER(u); + + assert(t); + assert(t->state == TIMER_DEAD); + + 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); + + return 0; +} + +static void timer_enter_dead(Timer *t, TimerResult f) { + assert(t); + + if (f != TIMER_SUCCESS) + t->result = 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; + + if (t <= 0) + return 0; + + a = now(clock_boottime_or_monotonic()); + b = now(CLOCK_MONOTONIC); + + if (t + a > b) + return t + a - b; + else + 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; + Unit *trigger; + int r; + + assert(t); + + trigger = UNIT_TRIGGER(UNIT(t)); + if (!trigger) { + log_unit_error(UNIT(t), "Unit to trigger vanished."); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); + return; + } + + /* If we shall wake the system we use the boottime clock + * rather than the monotonic clock. */ + + ts_realtime = now(CLOCK_REALTIME); + ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC); + t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0; + + LIST_FOREACH(value, v, t->values) { + + if (v->disabled) + continue; + + if (v->base == TIMER_CALENDAR) { + usec_t b; + + /* If we know the last time this was + * triggered, schedule the job based relative + * to that. If we don't just start from + * now. */ + + b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime; + + r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse); + if (r < 0) + continue; + + if (!found_realtime) + t->next_elapse_realtime = v->next_elapse; + else + t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse); + + found_realtime = true; + + } else { + switch (v->base) { + + case TIMER_ACTIVE: + if (state_translation_table[t->state] == UNIT_ACTIVE) + base = UNIT(t)->inactive_exit_timestamp.monotonic; + else + base = ts_monotonic; + break; + + case TIMER_BOOT: + 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 = trigger->inactive_exit_timestamp.monotonic; + + if (base <= 0) + base = t->last_trigger.monotonic; + + if (base <= 0) + continue; + + break; + + case TIMER_UNIT_INACTIVE: + leave_around = true; + base = trigger->inactive_enter_timestamp.monotonic; + + if (base <= 0) + base = t->last_trigger.monotonic; + + if (base <= 0) + continue; + + break; + + default: + assert_not_reached("Unknown timer base"); + } + + if (t->wake_system) + base = monotonic_to_boottime(base); + + v->next_elapse = base + v->value; + + if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) { + /* This is a one time trigger, disable it now */ + v->disabled = true; + continue; + } + + if (!found_monotonic) + t->next_elapse_monotonic_or_boottime = v->next_elapse; + else + t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse); + + found_monotonic = true; + } + } + + if (!found_monotonic && !found_realtime) { + log_unit_debug(UNIT(t), "Timer is elapsed."); + timer_enter_elapsed(t, leave_around); + return; + } + + if (found_monotonic) { + char buf[FORMAT_TIMESPAN_MAX]; + usec_t left; + + 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); + if (r < 0) + goto fail; + + r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT); + if (r < 0) + goto fail; + } else { + + r = sd_event_add_time( + UNIT(t)->manager->event, + &t->monotonic_event_source, + t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC, + t->next_elapse_monotonic_or_boottime, t->accuracy_usec, + timer_dispatch, t); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic"); + } + + } else if (t->monotonic_event_source) { + + r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF); + if (r < 0) + goto fail; + } + + 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) { + r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime); + if (r < 0) + goto fail; + + r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT); + if (r < 0) + goto fail; + } else { + r = sd_event_add_time( + UNIT(t)->manager->event, + &t->realtime_event_source, + t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME, + t->next_elapse_realtime, t->accuracy_usec, + timer_dispatch, t); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime"); + } + + } else if (t->realtime_event_source) { + + r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF); + if (r < 0) + goto fail; + } + + timer_set_state(t, TIMER_WAITING); + return; + +fail: + log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m"); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); +} + +static void timer_enter_running(Timer *t) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *trigger; + int r; + + assert(t); + + /* Don't start job if we are supposed to go down */ + if (unit_stop_pending(UNIT(t))) + return; + + trigger = UNIT_TRIGGER(UNIT(t)); + if (!trigger) { + log_unit_error(UNIT(t), "Unit to trigger vanished."); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); + return; + } + + r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, 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, MODE_INVALID); + + timer_set_state(t, TIMER_RUNNING); + return; + +fail: + log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r)); + timer_enter_dead(t, TIMER_FAILURE_RESOURCES); +} + +static int timer_start(Unit *u) { + Timer *t = TIMER(u); + TimerValue *v; + Unit *trigger; + int r; + + assert(t); + assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); + + trigger = UNIT_TRIGGER(u); + if (!trigger || trigger->load_state != UNIT_LOADED) { + log_unit_error(u, "Refusing to start, unit to trigger not loaded."); + return -ENOENT; + } + + r = unit_start_limit_test(u); + if (r < 0) { + timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); + return r; + } + + t->last_trigger = DUAL_TIMESTAMP_NULL; + + /* Reenable all timers that depend on unit activation time */ + LIST_FOREACH(value, v, t->values) + if (v->base == TIMER_ACTIVE) + v->disabled = false; + + if (t->stamp_path) { + struct stat st; + + if (stat(t->stamp_path, &st) >= 0) + t->last_trigger.realtime = timespec_load(&st.st_atim); + else if (errno == ENOENT) + /* The timer has never run before, + * make sure a stamp file exists. + */ + touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); + } + + t->result = TIMER_SUCCESS; + timer_enter_waiting(t, true); + return 1; +} + +static int timer_stop(Unit *u) { + Timer *t = TIMER(u); + + assert(t); + assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED); + + timer_enter_dead(t, TIMER_SUCCESS); + return 1; +} + +static int timer_serialize(Unit *u, FILE *f, FDSet *fds) { + Timer *t = TIMER(u); + + assert(u); + assert(f); + assert(fds); + + unit_serialize_item(u, f, "state", timer_state_to_string(t->state)); + unit_serialize_item(u, f, "result", timer_result_to_string(t->result)); + + if (t->last_trigger.realtime > 0) + unit_serialize_item_format(u, f, "last-trigger-realtime", "%" PRIu64, t->last_trigger.realtime); + + if (t->last_trigger.monotonic > 0) + unit_serialize_item_format(u, f, "last-trigger-monotonic", "%" PRIu64, t->last_trigger.monotonic); + + return 0; +} + +static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { + Timer *t = TIMER(u); + int r; + + assert(u); + assert(key); + assert(value); + assert(fds); + + if (streq(key, "state")) { + TimerState state; + + state = timer_state_from_string(value); + if (state < 0) + log_unit_debug(u, "Failed to parse state value: %s", value); + else + t->deserialized_state = state; + } else if (streq(key, "result")) { + TimerResult f; + + f = timer_result_from_string(value); + if (f < 0) + log_unit_debug(u, "Failed to parse result value: %s", value); + else if (f != TIMER_SUCCESS) + t->result = f; + } else if (streq(key, "last-trigger-realtime")) { + + r = safe_atou64(value, &t->last_trigger.realtime); + if (r < 0) + log_unit_debug(u, "Failed to parse last-trigger-realtime value: %s", value); + + } else if (streq(key, "last-trigger-monotonic")) { + + r = safe_atou64(value, &t->last_trigger.monotonic); + if (r < 0) + log_unit_debug(u, "Failed to parse last-trigger-monotonic value: %s", value); + + } else + log_unit_debug(u, "Unknown serialization key: %s", key); + + return 0; +} + +_pure_ static UnitActiveState timer_active_state(Unit *u) { + assert(u); + + return state_translation_table[TIMER(u)->state]; +} + +_pure_ static const char *timer_sub_state_to_string(Unit *u) { + assert(u); + + return timer_state_to_string(TIMER(u)->state); +} + +static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) { + Timer *t = TIMER(userdata); + + assert(t); + + if (t->state != TIMER_WAITING) + return 0; + + log_unit_debug(UNIT(t), "Timer elapsed."); + timer_enter_running(t); + return 0; +} + +static void timer_trigger_notify(Unit *u, Unit *other) { + Timer *t = TIMER(u); + TimerValue *v; + + assert(u); + assert(other); + + if (other->load_state != UNIT_LOADED) + return; + + /* Reenable all timers that depend on unit state */ + LIST_FOREACH(value, v, t->values) + if (v->base == TIMER_UNIT_ACTIVE || + v->base == TIMER_UNIT_INACTIVE) + v->disabled = false; + + switch (t->state) { + + case TIMER_WAITING: + case TIMER_ELAPSED: + + /* Recalculate sleep time */ + timer_enter_waiting(t, false); + break; + + case TIMER_RUNNING: + + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_unit_debug(UNIT(t), "Got notified about unit deactivation."); + timer_enter_waiting(t, false); + } + break; + + case TIMER_DEAD: + case TIMER_FAILED: + break; + + default: + assert_not_reached("Unknown timer state"); + } +} + +static void timer_reset_failed(Unit *u) { + Timer *t = TIMER(u); + + assert(t); + + if (t->state == TIMER_FAILED) + timer_set_state(t, TIMER_DEAD); + + t->result = TIMER_SUCCESS; +} + +static void timer_time_change(Unit *u) { + Timer *t = TIMER(u); + + assert(u); + + if (t->state != TIMER_WAITING) + return; + + log_unit_debug(u, "Time change, recalculating next elapse."); + timer_enter_waiting(t, false); +} + +static const char* const timer_base_table[_TIMER_BASE_MAX] = { + [TIMER_ACTIVE] = "OnActiveSec", + [TIMER_BOOT] = "OnBootSec", + [TIMER_STARTUP] = "OnStartupSec", + [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec", + [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec", + [TIMER_CALENDAR] = "OnCalendar" +}; + +DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); + +static const char* const timer_result_table[_TIMER_RESULT_MAX] = { + [TIMER_SUCCESS] = "success", + [TIMER_FAILURE_RESOURCES] = "resources", + [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit", +}; + +DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); + +const UnitVTable timer_vtable = { + .object_size = sizeof(Timer), + + .sections = + "Unit\0" + "Timer\0" + "Install\0", + .private_section = "Timer", + + .init = timer_init, + .done = timer_done, + .load = timer_load, + + .coldplug = timer_coldplug, + + .dump = timer_dump, + + .start = timer_start, + .stop = timer_stop, + + .serialize = timer_serialize, + .deserialize_item = timer_deserialize_item, + + .active_state = timer_active_state, + .sub_state_to_string = timer_sub_state_to_string, + + .trigger_notify = timer_trigger_notify, + + .reset_failed = timer_reset_failed, + .time_change = timer_time_change, + + .bus_vtable = bus_timer_vtable, + .bus_set_property = bus_timer_set_property, + + .can_transient = true, +}; diff --git a/src/grp-system/libcore/timer.h b/src/grp-system/libcore/timer.h new file mode 100644 index 0000000000..99b47c3880 --- /dev/null +++ b/src/grp-system/libcore/timer.h @@ -0,0 +1,89 @@ +#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 . +***/ + +#include "basic/calendarspec.h" + +typedef struct Timer Timer; + +typedef enum TimerBase { + TIMER_ACTIVE, + TIMER_BOOT, + TIMER_STARTUP, + TIMER_UNIT_ACTIVE, + TIMER_UNIT_INACTIVE, + TIMER_CALENDAR, + _TIMER_BASE_MAX, + _TIMER_BASE_INVALID = -1 +} TimerBase; + +typedef struct TimerValue { + TimerBase base; + bool disabled; + + usec_t value; /* only for monotonic events */ + CalendarSpec *calendar_spec; /* only for calendar events */ + usec_t next_elapse; + + LIST_FIELDS(struct TimerValue, value); +} TimerValue; + +typedef enum TimerResult { + TIMER_SUCCESS, + TIMER_FAILURE_RESOURCES, + TIMER_FAILURE_START_LIMIT_HIT, + _TIMER_RESULT_MAX, + _TIMER_RESULT_INVALID = -1 +} TimerResult; + +struct Timer { + Unit meta; + + usec_t accuracy_usec; + usec_t random_usec; + + LIST_HEAD(TimerValue, values); + usec_t next_elapse_realtime; + usec_t next_elapse_monotonic_or_boottime; + dual_timestamp last_trigger; + + TimerState state, deserialized_state; + + sd_event_source *monotonic_event_source; + sd_event_source *realtime_event_source; + + TimerResult result; + + bool persistent; + bool wake_system; + bool remain_after_elapse; + + char *stamp_path; +}; + +void timer_free_values(Timer *t); + +extern const UnitVTable timer_vtable; + +const char *timer_base_to_string(TimerBase i) _const_; +TimerBase timer_base_from_string(const char *s) _pure_; + +const char* timer_result_to_string(TimerResult i) _const_; +TimerResult timer_result_from_string(const char *s) _pure_; diff --git a/src/grp-system/libcore/transaction.c b/src/grp-system/libcore/transaction.c new file mode 100644 index 0000000000..aa57eee556 --- /dev/null +++ b/src/grp-system/libcore/transaction.c @@ -0,0 +1,1103 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/terminal-util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" + +#include "dbus-unit.h" +#include "transaction.h" + +static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies); + +static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) { + assert(tr); + assert(j); + + /* Deletes one job from the transaction */ + + transaction_unlink_job(tr, j, delete_dependencies); + + job_free(j); +} + +static void transaction_delete_unit(Transaction *tr, Unit *u) { + Job *j; + + /* Deletes all jobs associated with a certain unit from the + * transaction */ + + while ((j = hashmap_get(tr->jobs, u))) + transaction_delete_job(tr, j, true); +} + +void transaction_abort(Transaction *tr) { + Job *j; + + assert(tr); + + while ((j = hashmap_first(tr->jobs))) + transaction_delete_job(tr, j, false); + + assert(hashmap_isempty(tr->jobs)); +} + +static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) { + JobDependency *l; + + /* A recursive sweep through the graph that marks all units + * that matter to the anchor job, i.e. are directly or + * indirectly a dependency of the anchor job via paths that + * are fully marked as mattering. */ + + j->matters_to_anchor = true; + j->generation = generation; + + LIST_FOREACH(subject, l, j->subject_list) { + + /* This link does not matter */ + if (!l->matters) + continue; + + /* This unit has already been marked */ + if (l->object->generation == generation) + continue; + + transaction_find_jobs_that_matter_to_anchor(l->object, generation); + } +} + +static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) { + JobDependency *l, *last; + + assert(j); + assert(other); + assert(j->unit == other->unit); + assert(!j->installed); + + /* Merges 'other' into 'j' and then deletes 'other'. */ + + j->type = t; + j->state = JOB_WAITING; + 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 */ + last = NULL; + LIST_FOREACH(subject, l, other->subject_list) { + assert(l->subject == other); + l->subject = j; + last = l; + } + + /* Merge both lists */ + if (last) { + last->subject_next = j->subject_list; + if (j->subject_list) + j->subject_list->subject_prev = last; + j->subject_list = other->subject_list; + } + + /* Patch us in as new owner of the JobDependency objects */ + last = NULL; + LIST_FOREACH(object, l, other->object_list) { + assert(l->object == other); + l->object = j; + last = l; + } + + /* Merge both lists */ + if (last) { + last->object_next = j->object_list; + if (j->object_list) + j->object_list->object_prev = last; + j->object_list = other->object_list; + } + + /* Kill the other job */ + other->subject_list = NULL; + other->object_list = NULL; + transaction_delete_job(tr, other, true); +} + +_pure_ static bool job_is_conflicted_by(Job *j) { + JobDependency *l; + + assert(j); + + /* Returns true if this job is pulled in by a least one + * ConflictedBy dependency. */ + + LIST_FOREACH(object, l, j->object_list) + if (l->conflicts) + return true; + + return false; +} + +static int delete_one_unmergeable_job(Transaction *tr, Job *j) { + Job *k; + + assert(j); + + /* Tries to delete one item in the linked list + * j->transaction_next->transaction_next->... that conflicts + * with another one, in an attempt to make an inconsistent + * transaction work. */ + + /* We rely here on the fact that if a merged with b does not + * merge with c, either a or b merge with c neither */ + LIST_FOREACH(transaction, j, j) + LIST_FOREACH(transaction, k, j->transaction_next) { + Job *d; + + /* Is this one mergeable? Then skip it */ + if (job_type_is_mergeable(j->type, k->type)) + continue; + + /* Ok, we found two that conflict, let's see if we can + * drop one of them */ + if (!j->matters_to_anchor && !k->matters_to_anchor) { + + /* Both jobs don't matter, so let's + * find the one that is smarter to + * remove. Let's think positive and + * rather remove stops then starts -- + * except if something is being + * stopped because it is conflicted by + * another unit in which case we + * rather remove the start. */ + + log_unit_debug(j->unit, + "Looking at job %s/%s conflicted_by=%s", + j->unit->id, job_type_to_string(j->type), + yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); + log_unit_debug(k->unit, + "Looking at job %s/%s conflicted_by=%s", + k->unit->id, job_type_to_string(k->type), + yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); + + if (j->type == JOB_STOP) { + + if (job_is_conflicted_by(j)) + d = k; + else + d = j; + + } else if (k->type == JOB_STOP) { + + if (job_is_conflicted_by(k)) + d = j; + else + d = k; + } else + d = j; + + } else if (!j->matters_to_anchor) + d = j; + else if (!k->matters_to_anchor) + d = k; + else + return -ENOEXEC; + + /* Ok, we can drop one, so let's do so. */ + log_unit_debug(d->unit, + "Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s", + j->unit->id, job_type_to_string(j->type), + k->unit->id, job_type_to_string(k->type), + d->unit->id, job_type_to_string(d->type)); + transaction_delete_job(tr, d, true); + return 0; + } + + return -EINVAL; +} + +static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { + Job *j; + Iterator i; + int r; + + assert(tr); + + /* First step, check whether any of the jobs for one specific + * task conflict. If so, try to drop one of them. */ + HASHMAP_FOREACH(j, tr->jobs, i) { + JobType t; + Job *k; + + t = j->type; + LIST_FOREACH(transaction, k, j->transaction_next) { + if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0) + continue; + + /* OK, we could not merge all jobs for this + * action. Let's see if we can get rid of one + * of them */ + + r = delete_one_unmergeable_job(tr, j); + if (r >= 0) + /* Ok, we managed to drop one, now + * let's ask our callers to call us + * again after garbage collecting */ + return -EAGAIN; + + /* We couldn't merge anything. Failure */ + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, + "Transaction contains conflicting jobs '%s' and '%s' for %s. " + "Probably contradicting requirement dependencies configured.", + job_type_to_string(t), + job_type_to_string(k->type), + k->unit->id); + } + } + + /* Second step, merge the jobs. */ + HASHMAP_FOREACH(j, tr->jobs, i) { + JobType t = j->type; + Job *k; + + /* Merge all transaction jobs for j->unit */ + LIST_FOREACH(transaction, k, j->transaction_next) + assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0); + + while ((k = j->transaction_next)) { + if (tr->anchor_job == k) { + transaction_merge_and_delete_job(tr, k, j, t); + j = k; + } else + transaction_merge_and_delete_job(tr, j, k, t); + } + + assert(!j->transaction_next); + assert(!j->transaction_prev); + } + + return 0; +} + +static void transaction_drop_redundant(Transaction *tr) { + Job *j; + Iterator i; + + /* Goes through the transaction and removes all jobs of the units + * whose jobs are all noops. If not all of a unit's jobs are + * redundant, they are kept. */ + + assert(tr); + +rescan: + HASHMAP_FOREACH(j, tr->jobs, i) { + Job *k; + + LIST_FOREACH(transaction, k, j) { + + if (tr->anchor_job == k || + !job_type_is_redundant(k->type, unit_active_state(k->unit)) || + (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) + goto next_unit; + } + + /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */ + transaction_delete_job(tr, j, false); + goto rescan; + next_unit:; + } +} + +_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { + assert(u); + assert(!j->transaction_prev); + + /* Checks whether at least one of the jobs for this unit + * matters to the anchor. */ + + LIST_FOREACH(transaction, j, j) + if (j->matters_to_anchor) + return true; + + return false; +} + +static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) { + Iterator i; + Unit *u; + int r; + + assert(tr); + assert(j); + assert(!j->transaction_prev); + + /* Does a recursive sweep through the ordering graph, looking + * for a cycle. If we find a cycle we try to break it. */ + + /* Have we seen this before? */ + if (j->generation == generation) { + Job *k, *delete; + + /* If the marker is NULL we have been here already and + * decided the job was loop-free from here. Hence + * shortcut things and return right-away. */ + if (!j->marker) + return 0; + + /* So, the marker is not NULL and we already have been + * here. We have a cycle. Let's try to break it. We go + * backwards in our path and try to find a suitable + * job to remove. We use the marker to find our way + * back, since smart how we are we stored our way back + * in there. */ + log_unit_warning(j->unit, + "Found ordering cycle on %s/%s", + j->unit->id, job_type_to_string(j->type)); + + delete = NULL; + for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) { + + /* logging for j not k here to provide consistent narrative */ + log_unit_warning(j->unit, + "Found dependency on %s/%s", + k->unit->id, job_type_to_string(k->type)); + + if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k)) + /* Ok, we can drop this one, so let's + * do so. */ + delete = k; + + /* Check if this in fact was the beginning of + * the cycle */ + if (k == j) + break; + } + + + if (delete) { + const char *status; + /* logging for j not k here to provide consistent narrative */ + log_unit_warning(j->unit, + "Breaking ordering cycle by deleting job %s/%s", + delete->unit->id, job_type_to_string(delete->type)); + log_unit_error(delete->unit, + "Job %s/%s deleted to break ordering cycle starting with %s/%s", + delete->unit->id, job_type_to_string(delete->type), + j->unit->id, job_type_to_string(j->type)); + + if (log_get_show_color()) + status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL; + else + status = " SKIP "; + + unit_status_printf(delete->unit, status, + "Ordering cycle found, skipping %s"); + transaction_delete_unit(tr, delete->unit); + return -EAGAIN; + } + + log_error("Unable to break cycle"); + + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, + "Transaction order is cyclic. See system logs for details."); + } + + /* Make the marker point to where we come from, so that we can + * find our way backwards if we want to break a cycle. We use + * a special marker for the beginning: we point to + * ourselves. */ + j->marker = from ? from : j; + j->generation = generation; + + /* We assume that the dependencies are bidirectional, and + * hence can ignore UNIT_AFTER */ + SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) { + Job *o; + + /* Is there a job for this unit? */ + o = hashmap_get(tr->jobs, u); + if (!o) { + /* Ok, there is no job for this in the + * transaction, but maybe there is already one + * running? */ + o = u->job; + if (!o) + continue; + } + + r = transaction_verify_order_one(tr, o, j, generation, e); + if (r < 0) + return r; + } + + /* Ok, let's backtrack, and remember that this entry is not on + * our path anymore. */ + j->marker = NULL; + + return 0; +} + +static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) { + Job *j; + int r; + Iterator i; + unsigned g; + + assert(tr); + assert(generation); + + /* Check if the ordering graph is cyclic. If it is, try to fix + * that up by dropping one of the jobs. */ + + g = (*generation)++; + + HASHMAP_FOREACH(j, tr->jobs, i) { + r = transaction_verify_order_one(tr, j, NULL, g, e); + if (r < 0) + return r; + } + + return 0; +} + +static void transaction_collect_garbage(Transaction *tr) { + Iterator i; + Job *j; + + assert(tr); + + /* Drop jobs that are not required by any other job */ + +rescan: + HASHMAP_FOREACH(j, tr->jobs, i) { + if (tr->anchor_job == j || j->object_list) { + /* log_debug("Keeping job %s/%s because of %s/%s", */ + /* j->unit->id, job_type_to_string(j->type), */ + /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */ + /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */ + continue; + } + + /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */ + transaction_delete_job(tr, j, true); + goto rescan; + } +} + +static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) { + Iterator i; + Job *j; + + assert(tr); + + /* Checks whether applying this transaction means that + * existing jobs would be replaced */ + + HASHMAP_FOREACH(j, tr->jobs, i) { + + /* Assume merged */ + assert(!j->transaction_prev); + assert(!j->transaction_next); + + if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) && + job_type_is_conflicting(j->unit->job->type, j->type)) + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, + "Transaction is destructive."); + } + + return 0; +} + +static void transaction_minimize_impact(Transaction *tr) { + Job *j; + Iterator i; + + assert(tr); + + /* Drops all unnecessary jobs that reverse already active jobs + * or that stop a running service. */ + +rescan: + HASHMAP_FOREACH(j, tr->jobs, i) { + LIST_FOREACH(transaction, j, j) { + bool stops_running_service, changes_existing_job; + + /* If it matters, we shouldn't drop it */ + if (j->matters_to_anchor) + continue; + + /* Would this stop a running service? + * Would this change an existing job? + * If so, let's drop this entry */ + + stops_running_service = + j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit)); + + changes_existing_job = + j->unit->job && + job_type_is_conflicting(j->type, j->unit->job->type); + + if (!stops_running_service && !changes_existing_job) + continue; + + if (stops_running_service) + log_unit_debug(j->unit, + "%s/%s would stop a running service.", + j->unit->id, job_type_to_string(j->type)); + + if (changes_existing_job) + log_unit_debug(j->unit, + "%s/%s would change existing job.", + j->unit->id, job_type_to_string(j->type)); + + /* Ok, let's get rid of this */ + log_unit_debug(j->unit, + "Deleting %s/%s to minimize impact.", + j->unit->id, job_type_to_string(j->type)); + + transaction_delete_job(tr, j, true); + goto rescan; + } + } +} + +static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { + Iterator i; + Job *j; + int r; + + /* Moves the transaction jobs to the set of active jobs */ + + if (mode == JOB_ISOLATE || mode == JOB_FLUSH) { + + /* When isolating first kill all installed jobs which + * aren't part of the new transaction */ + HASHMAP_FOREACH(j, m->jobs, i) { + assert(j->installed); + + if (j->unit->ignore_on_isolate) + continue; + + if (hashmap_get(tr->jobs, j->unit)) + continue; + + /* Not invalidating recursively. Avoids triggering + * OnFailure= actions of dependent jobs. Also avoids + * invalidating our iterator. */ + job_finish_and_invalidate(j, JOB_CANCELED, false, false); + } + } + + HASHMAP_FOREACH(j, tr->jobs, i) { + /* Assume merged */ + assert(!j->transaction_prev); + assert(!j->transaction_next); + + r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j); + if (r < 0) + goto rollback; + } + + while ((j = hashmap_steal_first(tr->jobs))) { + Job *installed_job; + + /* Clean the job dependencies */ + transaction_unlink_job(tr, j, false); + + installed_job = job_install(j); + if (installed_job != j) { + /* j has been merged into a previously installed job */ + if (tr->anchor_job == j) + tr->anchor_job = installed_job; + hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); + job_free(j); + j = installed_job; + } + + job_add_to_run_queue(j); + job_add_to_dbus_queue(j); + job_start_timer(j); + job_shutdown_magic(j); + } + + return 0; + +rollback: + + HASHMAP_FOREACH(j, tr->jobs, i) + hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); + + return r; +} + +int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e) { + Iterator i; + Job *j; + int r; + unsigned generation = 1; + + assert(tr); + + /* This applies the changes recorded in tr->jobs to + * the actual list of jobs, if possible. */ + + /* Reset the generation counter of all installed jobs. The detection of cycles + * looks at installed jobs. If they had a non-zero generation from some previous + * walk of the graph, the algorithm would break. */ + HASHMAP_FOREACH(j, m->jobs, i) + j->generation = 0; + + /* First step: figure out which jobs matter */ + transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++); + + /* Second step: Try not to stop any running services if + * we don't have to. Don't try to reverse running + * jobs if we don't have to. */ + if (mode == JOB_FAIL) + transaction_minimize_impact(tr); + + /* Third step: Drop redundant jobs */ + transaction_drop_redundant(tr); + + for (;;) { + /* Fourth step: Let's remove unneeded jobs that might + * be lurking. */ + if (mode != JOB_ISOLATE) + transaction_collect_garbage(tr); + + /* Fifth step: verify order makes sense and correct + * cycles if necessary and possible */ + r = transaction_verify_order(tr, &generation, e); + if (r >= 0) + break; + + if (r != -EAGAIN) { + log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r)); + return r; + } + + /* Let's see if the resulting transaction ordering + * graph is still cyclic... */ + } + + for (;;) { + /* Sixth step: let's drop unmergeable entries if + * necessary and possible, merge entries we can + * merge */ + r = transaction_merge_jobs(tr, e); + if (r >= 0) + break; + + if (r != -EAGAIN) { + log_warning("Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r)); + return r; + } + + /* Seventh step: an entry got dropped, let's garbage + * collect its dependencies. */ + if (mode != JOB_ISOLATE) + transaction_collect_garbage(tr); + + /* Let's see if the resulting transaction still has + * unmergeable entries ... */ + } + + /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */ + transaction_drop_redundant(tr); + + /* Ninth step: check whether we can actually apply this */ + r = transaction_is_destructive(tr, mode, e); + if (r < 0) { + log_notice("Requested transaction contradicts existing jobs: %s", bus_error_message(e, r)); + return r; + } + + /* Tenth step: apply changes */ + r = transaction_apply(tr, m, mode); + if (r < 0) + return log_warning_errno(r, "Failed to apply transaction: %m"); + + assert(hashmap_isempty(tr->jobs)); + + if (!hashmap_isempty(m->jobs)) { + /* Are there any jobs now? Then make sure we have the + * idle pipe around. We don't really care too much + * whether this works or not, as the idle pipe is a + * feature for cosmetics, not actually useful for + * anything beyond that. */ + + if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 && + m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) { + (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); + (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); + } + } + + return 0; +} + +static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) { + Job *j, *f; + + assert(tr); + assert(unit); + + /* Looks for an existing prospective job and returns that. If + * it doesn't exist it is created and added to the prospective + * jobs list. */ + + f = hashmap_get(tr->jobs, unit); + + LIST_FOREACH(transaction, j, f) { + assert(j->unit == unit); + + if (j->type == type) { + if (is_new) + *is_new = false; + return j; + } + } + + j = job_new(unit, type); + if (!j) + return NULL; + + j->generation = 0; + j->marker = NULL; + j->matters_to_anchor = false; + j->irreversible = tr->irreversible; + + LIST_PREPEND(transaction, f, j); + + if (hashmap_replace(tr->jobs, unit, f) < 0) { + LIST_REMOVE(transaction, f, j); + job_free(j); + return NULL; + } + + if (is_new) + *is_new = true; + + /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */ + + return j; +} + +static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) { + assert(tr); + assert(j); + + if (j->transaction_prev) + j->transaction_prev->transaction_next = j->transaction_next; + else if (j->transaction_next) + hashmap_replace(tr->jobs, j->unit, j->transaction_next); + else + hashmap_remove_value(tr->jobs, j->unit, j); + + if (j->transaction_next) + j->transaction_next->transaction_prev = j->transaction_prev; + + j->transaction_prev = j->transaction_next = NULL; + + while (j->subject_list) + job_dependency_free(j->subject_list); + + while (j->object_list) { + Job *other = j->object_list->matters ? j->object_list->subject : NULL; + + job_dependency_free(j->object_list); + + if (other && delete_dependencies) { + log_unit_debug(other->unit, + "Deleting job %s/%s as dependency of job %s/%s", + other->unit->id, job_type_to_string(other->type), + j->unit->id, job_type_to_string(j->type)); + transaction_delete_job(tr, other, delete_dependencies); + } + } +} + +int transaction_add_job_and_dependencies( + Transaction *tr, + JobType type, + Unit *unit, + Job *by, + bool matters, + bool conflicts, + bool ignore_requirements, + bool ignore_order, + sd_bus_error *e) { + Job *ret; + Iterator i; + Unit *dep; + int r; + bool is_new; + + assert(tr); + assert(type < _JOB_TYPE_MAX); + assert(type < _JOB_TYPE_MAX_IN_TRANSACTION); + assert(unit); + + /* Before adding jobs for this unit, let's ensure that its state has been loaded + * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). + * This way, we "recursively" coldplug units, ensuring that we do not look at state of + * not-yet-coldplugged units. */ + if (MANAGER_IS_RELOADING(unit->manager)) + unit_coldplug(unit); + + /* log_debug("Pulling in %s/%s from %s/%s", */ + /* unit->id, job_type_to_string(type), */ + /* by ? by->unit->id : "NA", */ + /* by ? job_type_to_string(by->type) : "NA"); */ + + if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED)) + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id); + + if (type != JOB_STOP) { + r = bus_unit_check_load_state(unit, e); + if (r < 0) + return r; + } + + 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.", + job_type_to_string(type), unit->id); + + + /* First add the job. */ + ret = transaction_add_one_job(tr, type, unit, &is_new); + if (!ret) + return -ENOMEM; + + ret->ignore_order = ret->ignore_order || ignore_order; + + /* Then, add a link to the job. */ + if (by) { + if (!job_dependency_new(by, ret, matters, conflicts)) + return -ENOMEM; + } else { + /* If the job has no parent job, it is the anchor job. */ + assert(!tr->anchor_job); + tr->anchor_job = ret; + } + + if (is_new && !ignore_requirements && type != JOB_NOP) { + Set *following; + + /* If we are following some other unit, make sure we + * 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, 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); + } + } + + set_free(following); + } + + /* 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, false, false, ignore_order, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; + + sd_bus_error_free(e); + } + } + + SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; + + 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, ignore_order, e); + if (r < 0) { + /* unit masked, job type not applicable and unit not found are not considered as errors. */ + log_unit_full(dep, + IN_SET(r, -ERFKILL, -EBADR, -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, false, false, ignore_order, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; + + 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, true, false, ignore_order, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; + + sd_bus_error_free(e); + } + } + + SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e); + if (r < 0) { + log_unit_warning(dep, + "Cannot add dependency job, ignoring: %s", + bus_error_message(e, r)); + sd_bus_error_free(e); + } + } + + } + + if (type == JOB_STOP || type == JOB_RESTART) { + static const UnitDependency propagate_deps[] = { + UNIT_REQUIRED_BY, + UNIT_REQUISITE_OF, + UNIT_BOUND_BY, + UNIT_CONSISTS_OF, + }; + + JobType ptype; + unsigned j; + + /* We propagate STOP as STOP, but RESTART only + * as TRY_RESTART, in order not to start + * dependencies that are not around. */ + ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type; + + for (j = 0; j < ELEMENTSOF(propagate_deps); j++) + SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) { + JobType nt; + + nt = job_type_collapse(ptype, dep); + if (nt == JOB_NOP) + continue; + + r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e); + if (r < 0) { + if (r != -EBADR) /* job type not applicable */ + goto fail; + + sd_bus_error_free(e); + } + } + } + + if (type == JOB_RELOAD) { + + SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { + JobType nt; + + nt = job_type_collapse(JOB_TRY_RELOAD, dep); + if (nt == JOB_NOP) + continue; + + r = transaction_add_job_and_dependencies(tr, nt, dep, ret, false, false, false, ignore_order, e); + if (r < 0) { + log_unit_warning(dep, + "Cannot add dependency reload job, ignoring: %s", + bus_error_message(e, r)); + sd_bus_error_free(e); + } + } + } + + /* JOB_VERIFY_STARTED require no dependency handling */ + } + + return 0; + +fail: + return r; +} + +int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { + Iterator i; + Unit *u; + char *k; + int r; + + assert(tr); + assert(m); + + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + + /* ignore aliases */ + if (u->id != k) + continue; + + if (u->ignore_on_isolate) + continue; + + /* No need to stop inactive jobs */ + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job) + continue; + + /* Is there already something listed for this? */ + if (hashmap_get(tr->jobs, u)) + continue; + + 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"); + } + + return 0; +} + +Transaction *transaction_new(bool irreversible) { + Transaction *tr; + + tr = new0(Transaction, 1); + if (!tr) + return NULL; + + tr->jobs = hashmap_new(NULL); + if (!tr->jobs) { + free(tr); + return NULL; + } + + tr->irreversible = irreversible; + + return tr; +} + +void transaction_free(Transaction *tr) { + assert(hashmap_isempty(tr->jobs)); + hashmap_free(tr->jobs); + free(tr); +} diff --git a/src/grp-system/libcore/transaction.h b/src/grp-system/libcore/transaction.h new file mode 100644 index 0000000000..19d8d3d434 --- /dev/null +++ b/src/grp-system/libcore/transaction.h @@ -0,0 +1,52 @@ +#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 . +***/ + +#include "basic/hashmap.h" + +typedef struct Transaction Transaction; + +#include "job.h" +#include "manager.h" +#include "unit.h" + +struct Transaction { + /* Jobs to be added */ + Hashmap *jobs; /* Unit object => Job object list 1:1 */ + Job *anchor_job; /* the job the user asked for */ + bool irreversible; +}; + +Transaction *transaction_new(bool irreversible); +void transaction_free(Transaction *tr); + +int transaction_add_job_and_dependencies( + Transaction *tr, + JobType type, + Unit *unit, + Job *by, + bool matters, + bool conflicts, + bool ignore_requirements, + bool ignore_order, + sd_bus_error *e); +int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e); +int transaction_add_isolate_jobs(Transaction *tr, Manager *m); +void transaction_abort(Transaction *tr); diff --git a/src/grp-system/libcore/unit-printf.c b/src/grp-system/libcore/unit-printf.c new file mode 100644 index 0000000000..14c36ab494 --- /dev/null +++ b/src/grp-system/libcore/unit-printf.c @@ -0,0 +1,305 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/formats-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "shared/specifier.h" + +#include "unit-printf.h" +#include "unit.h" + +static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + + assert(u); + + return unit_name_to_prefix_and_instance(u->id, ret); +} + +static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + + assert(u); + + return unit_name_to_prefix(u->id, ret); +} + +static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) { + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + int r; + + assert(u); + + r = unit_name_to_prefix(u->id, &p); + if (r < 0) + return r; + + return unit_name_unescape(p, ret); +} + +static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + + assert(u); + + return unit_name_unescape(strempty(u->instance), ret); +} + +static int specifier_filename(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + + assert(u); + + if (u->instance) + return unit_name_path_unescape(u->instance, ret); + else + return unit_name_to_path(u->id, ret); +} + +static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + char *n; + + assert(u); + + if (u->cgroup_path) + n = strdup(u->cgroup_path); + else + n = unit_default_cgroup_path(u); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + char *n; + + assert(u); + + n = strdup(u->manager->cgroup_root); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +static int specifier_cgroup_slice(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + char *n; + + assert(u); + + if (UNIT_ISSET(u->slice)) { + Unit *slice; + + slice = UNIT_DEREF(u->slice); + + if (slice->cgroup_path) + n = strdup(slice->cgroup_path); + else + n = unit_default_cgroup_path(slice); + } else + n = strdup(u->manager->cgroup_root); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +static int specifier_runtime(char specifier, void *data, void *userdata, char **ret) { + Unit *u = userdata; + const char *e; + char *n = NULL; + + assert(u); + + e = manager_get_runtime_prefix(u->manager); + if (!e) + return -EOPNOTSUPP; + n = strdup(e); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { + char *t; + + /* 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 = 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; +} + +static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { + + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ + + return get_home_dir(ret); +} + +static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { + + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ + + return get_shell(ret); +} + +int unit_name_printf(Unit *u, const char* format, char **ret) { + + /* + * This will use the passed string as format string and + * replace the following specifiers: + * + * %n: the full id of the unit (foo@bar.waldo) + * %N: the id of the unit without the suffix (foo@bar) + * %p: the prefix (foo) + * %i: the instance (bar) + */ + + const Specifier table[] = { + { 'n', specifier_string, u->id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'i', specifier_string, u->instance }, + { 0, NULL, NULL } + }; + + assert(u); + assert(format); + assert(ret); + + return specifier_printf(format, table, u, ret); +} + +int unit_full_printf(Unit *u, const char *format, char **ret) { + + /* This is similar to unit_name_printf() but also supports + * unescaping. Also, adds a couple of additional codes: + * + * %f the instance if set, otherwise the id + * %c cgroup path of unit + * %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 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 + * %v `uname -r` of the running system + */ + + const Specifier table[] = { + { 'n', specifier_string, u->id }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'P', specifier_prefix_unescaped, NULL }, + { 'i', specifier_string, u->instance }, + { 'I', specifier_instance_unescaped, NULL }, + + { 'f', specifier_filename, NULL }, + { 'c', specifier_cgroup, NULL }, + { 'r', specifier_cgroup_slice, NULL }, + { 'R', specifier_cgroup_root, NULL }, + { 't', specifier_runtime, NULL }, + + { 'U', specifier_user_id, NULL }, + { 'u', specifier_user_name, NULL }, + { 'h', specifier_user_home, NULL }, + { 's', specifier_user_shell, NULL }, + + { 'm', specifier_machine_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} + }; + + assert(u); + assert(format); + assert(ret); + + return specifier_printf(format, table, u, ret); +} + +int unit_full_printf_strv(Unit *u, char **l, char ***ret) { + size_t n; + char **r, **i, **j; + int q; + + /* Applies unit_full_printf to every entry in l */ + + assert(u); + + n = strv_length(l); + r = new(char*, n+1); + if (!r) + return -ENOMEM; + + for (i = l, j = r; *i; i++, j++) { + q = unit_full_printf(u, *i, j); + if (q < 0) + goto fail; + } + + *j = NULL; + *ret = r; + return 0; + +fail: + for (j--; j >= r; j--) + free(*j); + + free(r); + return q; +} diff --git a/src/grp-system/libcore/unit-printf.h b/src/grp-system/libcore/unit-printf.h new file mode 100644 index 0000000000..4fc8531228 --- /dev/null +++ b/src/grp-system/libcore/unit-printf.h @@ -0,0 +1,26 @@ +#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 . +***/ + +#include "unit.h" + +int unit_name_printf(Unit *u, const char* text, char **ret); +int unit_full_printf(Unit *u, const char *text, char **ret); +int unit_full_printf_strv(Unit *u, char **l, char ***ret); diff --git a/src/grp-system/libcore/unit.c b/src/grp-system/libcore/unit.c new file mode 100644 index 0000000000..aff4dbd2ca --- /dev/null +++ b/src/grp-system/libcore/unit.c @@ -0,0 +1,3872 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/escape.h" +#include "basic/fileio-label.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/umask-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/virt.h" +#include "sd-bus/bus-common-errors.h" +#include "shared/bus-util.h" +#include "shared/dropin.h" + +#include "dbus-unit.h" +#include "dbus.h" +#include "execute.h" +#include "load-dropin.h" +#include "load-fragment.h" +#include "unit.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_DEVICE] = &device_vtable, + [UNIT_MOUNT] = &mount_vtable, + [UNIT_AUTOMOUNT] = &automount_vtable, + [UNIT_SWAP] = &swap_vtable, + [UNIT_TIMER] = &timer_vtable, + [UNIT_PATH] = &path_vtable, + [UNIT_SLICE] = &slice_vtable, + [UNIT_SCOPE] = &scope_vtable +}; + +static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency); + +Unit *unit_new(Manager *m, size_t size) { + Unit *u; + + assert(m); + assert(size >= sizeof(Unit)); + + u = malloc0(size); + if (!u) + return NULL; + + u->names = set_new(&string_hash_ops); + if (!u->names) { + free(u); + return NULL; + } + + u->manager = m; + u->type = _UNIT_TYPE_INVALID; + u->default_dependencies = true; + u->unit_file_state = _UNIT_FILE_STATE_INVALID; + u->unit_file_preset = -1; + u->on_failure_job_mode = JOB_REPLACE; + u->cgroup_inotify_wd = -1; + u->job_timeout = USEC_INFINITY; + u->sigchldgen = 0; + + RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst); + RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); + + return u; +} + +bool unit_has_name(Unit *u, const char *name) { + assert(u); + assert(name); + + return !!set_get(u->names, (char*) name); +} + +static void unit_init(Unit *u) { + CGroupContext *cc; + ExecContext *ec; + KillContext *kc; + + assert(u); + assert(u->manager); + assert(u->type >= 0); + + cc = unit_get_cgroup_context(u); + if (cc) { + cgroup_context_init(cc); + + /* Copy in the manager defaults into the cgroup + * context, _before_ the rest of the settings have + * been initialized */ + + cc->cpu_accounting = u->manager->default_cpu_accounting; + cc->io_accounting = u->manager->default_io_accounting; + 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); + if (ec) + exec_context_init(ec); + + kc = unit_get_kill_context(u); + if (kc) + kill_context_init(kc); + + if (UNIT_VTABLE(u)->init) + UNIT_VTABLE(u)->init(u); +} + +int unit_add_name(Unit *u, const char *text) { + _cleanup_free_ char *s = NULL, *i = NULL; + UnitType t; + int r; + + assert(u); + assert(text); + + if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) { + + if (!u->instance) + return -EINVAL; + + r = unit_name_replace_instance(text, u->instance, &s); + if (r < 0) + return r; + } else { + s = strdup(text); + if (!s) + return -ENOMEM; + } + + if (set_contains(u->names, s)) + return 0; + if (hashmap_contains(u->manager->units, s)) + return -EEXIST; + + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return -EINVAL; + + t = unit_name_to_type(s); + if (t < 0) + return -EINVAL; + + if (u->type != _UNIT_TYPE_INVALID && t != u->type) + return -EINVAL; + + r = unit_name_to_instance(s, &i); + if (r < 0) + return r; + + if (i && !unit_type_may_template(t)) + return -EINVAL; + + /* Ensure that this unit is either instanced or not instanced, + * but not both. Note that we do allow names with different + * instance names however! */ + if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) + return -EINVAL; + + if (!unit_type_may_alias(t) && !set_isempty(u->names)) + return -EEXIST; + + if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) + return -E2BIG; + + r = set_put(u->names, s); + if (r < 0) + return r; + assert(r > 0); + + r = hashmap_put(u->manager->units, s, u); + if (r < 0) { + (void) set_remove(u->names, s); + return r; + } + + if (u->type == _UNIT_TYPE_INVALID) { + u->type = t; + u->id = s; + u->instance = i; + + LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u); + + unit_init(u); + + i = NULL; + } + + s = NULL; + + unit_add_to_dbus_queue(u); + return 0; +} + +int unit_choose_id(Unit *u, const char *name) { + _cleanup_free_ char *t = NULL; + char *s, *i; + int r; + + assert(u); + assert(name); + + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + + if (!u->instance) + return -EINVAL; + + r = unit_name_replace_instance(name, u->instance, &t); + if (r < 0) + return r; + + name = t; + } + + /* Selects one of the names of this unit as the id */ + s = set_get(u->names, (char*) name); + if (!s) + return -ENOENT; + + /* Determine the new instance from the new id */ + r = unit_name_to_instance(s, &i); + if (r < 0) + return r; + + u->id = s; + + free(u->instance); + u->instance = i; + + unit_add_to_dbus_queue(u); + + return 0; +} + +int unit_set_description(Unit *u, const char *description) { + char *s; + + assert(u); + + if (isempty(description)) + s = NULL; + else { + s = strdup(description); + if (!s) + return -ENOMEM; + } + + free(u->description); + u->description = s; + + unit_add_to_dbus_queue(u); + return 0; +} + +bool unit_check_gc(Unit *u) { + UnitActiveState state; + assert(u); + + if (u->job) + return true; + + if (u->nop_job) + return true; + + state = unit_active_state(u); + + /* If the unit is inactive and failed and no job is queued for + * it, then release its runtime resources */ + if (UNIT_IS_INACTIVE_OR_FAILED(state) && + UNIT_VTABLE(u)->release_resources) + UNIT_VTABLE(u)->release_resources(u); + + /* But we keep the unit object around for longer when it is + * referenced or configured to not be gc'ed */ + if (state != UNIT_INACTIVE) + return true; + + if (u->no_gc) + return true; + + if (u->refs) + return true; + + if (UNIT_VTABLE(u)->check_gc) + if (UNIT_VTABLE(u)->check_gc(u)) + return true; + + return false; +} + +void unit_add_to_load_queue(Unit *u) { + assert(u); + assert(u->type != _UNIT_TYPE_INVALID); + + if (u->load_state != UNIT_STUB || u->in_load_queue) + return; + + LIST_PREPEND(load_queue, u->manager->load_queue, u); + u->in_load_queue = true; +} + +void unit_add_to_cleanup_queue(Unit *u) { + assert(u); + + if (u->in_cleanup_queue) + return; + + LIST_PREPEND(cleanup_queue, u->manager->cleanup_queue, u); + u->in_cleanup_queue = true; +} + +void unit_add_to_gc_queue(Unit *u) { + assert(u); + + if (u->in_gc_queue || u->in_cleanup_queue) + return; + + if (unit_check_gc(u)) + return; + + LIST_PREPEND(gc_queue, u->manager->gc_queue, u); + u->in_gc_queue = true; + + u->manager->n_in_gc_queue++; +} + +void unit_add_to_dbus_queue(Unit *u) { + assert(u); + assert(u->type != _UNIT_TYPE_INVALID); + + if (u->load_state == UNIT_STUB || u->in_dbus_queue) + return; + + /* Shortcut things if nobody cares */ + if (sd_bus_track_count(u->manager->subscribed) <= 0 && + set_isempty(u->manager->private_buses)) { + u->sent_dbus_new_signal = true; + return; + } + + LIST_PREPEND(dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = true; +} + +static void bidi_set_free(Unit *u, Set *s) { + Iterator i; + Unit *other; + + assert(u); + + /* Frees the set and makes sure we are dropped from the + * inverse pointers */ + + SET_FOREACH(other, s, i) { + UnitDependency d; + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + set_remove(other->dependencies[d], u); + + unit_add_to_gc_queue(other); + } + + set_free(s); +} + +static void unit_remove_transient(Unit *u) { + char **i; + + assert(u); + + if (!u->transient) + return; + + if (u->fragment_path) + (void) unlink(u->fragment_path); + + STRV_FOREACH(i, u->dropin_paths) { + _cleanup_free_ char *p = NULL, *pp = NULL; + + p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */ + if (!p) + continue; + + pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */ + if (!pp) + continue; + + /* Only drop transient drop-ins */ + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + + (void) unlink(*i); + (void) rmdir(p); + } +} + +static void unit_free_requires_mounts_for(Unit *u) { + char **j; + + STRV_FOREACH(j, u->requires_mounts_for) { + char s[strlen(*j) + 1]; + + PATH_FOREACH_PREFIX_MORE(s, *j) { + char *y; + Set *x; + + x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y); + if (!x) + continue; + + set_remove(x, u); + + if (set_isempty(x)) { + hashmap_remove(u->manager->units_requiring_mounts_for, y); + free(y); + set_free(x); + } + } + } + + u->requires_mounts_for = strv_free(u->requires_mounts_for); +} + +static void unit_done(Unit *u) { + ExecContext *ec; + CGroupContext *cc; + + assert(u); + + if (u->type < 0) + return; + + if (UNIT_VTABLE(u)->done) + UNIT_VTABLE(u)->done(u); + + ec = unit_get_exec_context(u); + if (ec) + exec_context_done(ec); + + cc = unit_get_cgroup_context(u); + if (cc) + cgroup_context_done(cc); +} + +void unit_free(Unit *u) { + UnitDependency d; + Iterator i; + char *t; + + assert(u); + + if (u->transient_file) + fclose(u->transient_file); + + if (!MANAGER_IS_RELOADING(u->manager)) + unit_remove_transient(u); + + bus_unit_send_removed_signal(u); + + unit_done(u); + + sd_bus_slot_unref(u->match_bus_slot); + + unit_free_requires_mounts_for(u); + + SET_FOREACH(t, u->names, i) + hashmap_remove_value(u->manager->units, t, u); + + if (u->job) { + Job *j = u->job; + job_uninstall(j); + job_free(j); + } + + if (u->nop_job) { + Job *j = u->nop_job; + job_uninstall(j); + job_free(j); + } + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + bidi_set_free(u, u->dependencies[d]); + + if (u->type != _UNIT_TYPE_INVALID) + LIST_REMOVE(units_by_type, u->manager->units_by_type[u->type], u); + + if (u->in_load_queue) + LIST_REMOVE(load_queue, u->manager->load_queue, u); + + if (u->in_dbus_queue) + LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); + + if (u->in_cleanup_queue) + LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u); + + if (u->in_gc_queue) { + LIST_REMOVE(gc_queue, u->manager->gc_queue, u); + u->manager->n_in_gc_queue--; + } + + if (u->in_cgroup_queue) + LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); + + unit_release_cgroup(u); + + (void) manager_update_failed_units(u->manager, u, false); + set_remove(u->manager->startup_units, u); + + free(u->description); + strv_free(u->documentation); + free(u->fragment_path); + free(u->source_path); + strv_free(u->dropin_paths); + free(u->instance); + + free(u->job_timeout_reboot_arg); + + set_free_free(u->names); + + unit_unwatch_all_pids(u); + + condition_free_list(u->conditions); + condition_free_list(u->asserts); + + free(u->reboot_arg); + + unit_ref_unset(&u->slice); + + while (u->refs) + unit_ref_unset(u->refs); + + free(u); +} + +UnitActiveState unit_active_state(Unit *u) { + assert(u); + + if (u->load_state == UNIT_MERGED) + return unit_active_state(unit_follow_merge(u)); + + /* After a reload it might happen that a unit is not correctly + * loaded but still has a process around. That's why we won't + * shortcut failed loading to UNIT_INACTIVE_FAILED. */ + + return UNIT_VTABLE(u)->active_state(u); +} + +const char* unit_sub_state_to_string(Unit *u) { + assert(u); + + return UNIT_VTABLE(u)->sub_state_to_string(u); +} + +static int complete_move(Set **s, Set **other) { + int r; + + assert(s); + assert(other); + + if (!*other) + return 0; + + if (*s) { + r = set_move(*s, *other); + if (r < 0) + return r; + } else { + *s = *other; + *other = NULL; + } + + return 0; +} + +static int merge_names(Unit *u, Unit *other) { + char *t; + Iterator i; + int r; + + assert(u); + assert(other); + + r = complete_move(&u->names, &other->names); + if (r < 0) + return r; + + set_free_free(other->names); + other->names = NULL; + other->id = NULL; + + SET_FOREACH(t, u->names, i) + assert_se(hashmap_replace(u->manager->units, t, u) == 0); + + return 0; +} + +static int reserve_dependencies(Unit *u, Unit *other, UnitDependency d) { + unsigned n_reserve; + + assert(u); + assert(other); + assert(d < _UNIT_DEPENDENCY_MAX); + + /* + * If u does not have this dependency set allocated, there is no need + * to reserve anything. In that case other's set will be transferred + * as a whole to u by complete_move(). + */ + if (!u->dependencies[d]) + return 0; + + /* merge_dependencies() will skip a u-on-u dependency */ + n_reserve = set_size(other->dependencies[d]) - !!set_get(other->dependencies[d], u); + + return set_reserve(u->dependencies[d], n_reserve); +} + +static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitDependency d) { + Iterator i; + Unit *back; + int r; + + assert(u); + assert(other); + assert(d < _UNIT_DEPENDENCY_MAX); + + /* Fix backwards pointers */ + SET_FOREACH(back, other->dependencies[d], i) { + UnitDependency k; + + for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) { + /* Do not add dependencies between u and itself */ + if (back == u) { + if (set_remove(back->dependencies[k], other)) + maybe_warn_about_dependency(u, other_id, k); + } else { + r = set_remove_and_put(back->dependencies[k], other, u); + if (r == -EEXIST) + set_remove(back->dependencies[k], other); + else + assert(r >= 0 || r == -ENOENT); + } + } + } + + /* Also do not move dependencies on u to itself */ + back = set_remove(other->dependencies[d], u); + if (back) + maybe_warn_about_dependency(u, other_id, d); + + /* The move cannot fail. The caller must have performed a reservation. */ + assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0); + + other->dependencies[d] = set_free(other->dependencies[d]); +} + +int unit_merge(Unit *u, Unit *other) { + UnitDependency d; + const char *other_id = NULL; + int r; + + assert(u); + assert(other); + assert(u->manager == other->manager); + assert(u->type != _UNIT_TYPE_INVALID); + + other = unit_follow_merge(other); + + if (other == u) + return 0; + + if (u->type != other->type) + return -EINVAL; + + if (!u->instance != !other->instance) + return -EINVAL; + + if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ + return -EEXIST; + + if (other->load_state != UNIT_STUB && + other->load_state != UNIT_NOT_FOUND) + return -EEXIST; + + if (other->job) + return -EEXIST; + + if (other->nop_job) + return -EEXIST; + + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) + return -EEXIST; + + if (other->id) + other_id = strdupa(other->id); + + /* Make reservations to ensure merge_dependencies() won't fail */ + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + r = reserve_dependencies(u, other, d); + /* + * We don't rollback reservations if we fail. We don't have + * a way to undo reservations. A reservation is not a leak. + */ + if (r < 0) + return r; + } + + /* Merge names */ + r = merge_names(u, other); + if (r < 0) + return r; + + /* Redirect all references */ + while (other->refs) + unit_ref_set(other->refs, u); + + /* Merge dependencies */ + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) + merge_dependencies(u, other, other_id, d); + + other->load_state = UNIT_MERGED; + other->merged_into = u; + + /* If there is still some data attached to the other node, we + * don't need it anymore, and can free it. */ + if (other->load_state != UNIT_STUB) + if (UNIT_VTABLE(other)->done) + UNIT_VTABLE(other)->done(other); + + unit_add_to_dbus_queue(u); + unit_add_to_cleanup_queue(other); + + return 0; +} + +int unit_merge_by_name(Unit *u, const char *name) { + _cleanup_free_ char *s = NULL; + Unit *other; + int r; + + assert(u); + assert(name); + + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + if (!u->instance) + return -EINVAL; + + r = unit_name_replace_instance(name, u->instance, &s); + if (r < 0) + return r; + + name = s; + } + + other = manager_get_unit(u->manager, name); + if (other) + return unit_merge(u, other); + + return unit_add_name(u, name); +} + +Unit* unit_follow_merge(Unit *u) { + assert(u); + + while (u->load_state == UNIT_MERGED) + assert_se(u = u->merged_into); + + return u; +} + +int unit_add_exec_dependencies(Unit *u, ExecContext *c) { + int r; + + assert(u); + assert(c); + + if (c->working_directory) { + r = unit_require_mounts_for(u, c->working_directory); + if (r < 0) + return r; + } + + if (c->root_directory) { + r = unit_require_mounts_for(u, c->root_directory); + if (r < 0) + return r; + } + + if (!MANAGER_IS_SYSTEM(u->manager)) + return 0; + + if (c->private_tmp) { + r = unit_require_mounts_for(u, "/tmp"); + if (r < 0) + return r; + + r = unit_require_mounts_for(u, "/var/tmp"); + if (r < 0) + return r; + } + + if (c->std_output != EXEC_OUTPUT_KMSG && + c->std_output != EXEC_OUTPUT_SYSLOG && + c->std_output != EXEC_OUTPUT_JOURNAL && + c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE && + c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE && + c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && + c->std_error != EXEC_OUTPUT_KMSG && + c->std_error != EXEC_OUTPUT_SYSLOG && + c->std_error != EXEC_OUTPUT_JOURNAL && + c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE && + c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE && + c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE) + return 0; + + /* If syslog or kernel logging is requested, make sure our own + * logging daemon is run first. */ + + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true); + if (r < 0) + return r; + + return 0; +} + +const char *unit_description(Unit *u) { + assert(u); + + if (u->description) + return u->description; + + return strna(u->id); +} + +void unit_dump(Unit *u, FILE *f, const char *prefix) { + char *t, **j; + UnitDependency d; + Iterator i; + const char *prefix2; + char + timestamp0[FORMAT_TIMESTAMP_MAX], + timestamp1[FORMAT_TIMESTAMP_MAX], + timestamp2[FORMAT_TIMESTAMP_MAX], + timestamp3[FORMAT_TIMESTAMP_MAX], + timestamp4[FORMAT_TIMESTAMP_MAX], + timespan[FORMAT_TIMESPAN_MAX]; + Unit *following; + _cleanup_set_free_ Set *following_set = NULL; + int r; + + assert(u); + assert(u->type >= 0); + + prefix = strempty(prefix); + prefix2 = strjoina(prefix, "\t"); + + fprintf(f, + "%s-> Unit %s:\n" + "%s\tDescription: %s\n" + "%s\tInstance: %s\n" + "%s\tUnit Load State: %s\n" + "%s\tUnit Active State: %s\n" + "%s\tState Change Timestamp: %s\n" + "%s\tInactive Exit Timestamp: %s\n" + "%s\tActive Enter Timestamp: %s\n" + "%s\tActive Exit Timestamp: %s\n" + "%s\tInactive Enter Timestamp: %s\n" + "%s\tGC Check Good: %s\n" + "%s\tNeed Daemon Reload: %s\n" + "%s\tTransient: %s\n" + "%s\tSlice: %s\n" + "%s\tCGroup: %s\n" + "%s\tCGroup realized: %s\n" + "%s\tCGroup mask: 0x%x\n" + "%s\tCGroup members mask: 0x%x\n", + prefix, u->id, + prefix, unit_description(u), + prefix, strna(u->instance), + prefix, unit_load_state_to_string(u->load_state), + prefix, unit_active_state_to_string(unit_active_state(u)), + prefix, strna(format_timestamp(timestamp0, sizeof(timestamp0), u->state_change_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), + prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), + prefix, yes_no(unit_check_gc(u)), + prefix, yes_no(unit_need_daemon_reload(u)), + prefix, yes_no(u->transient), + prefix, strna(unit_slice_name(u)), + prefix, strna(u->cgroup_path), + prefix, yes_no(u->cgroup_realized), + prefix, u->cgroup_realized_mask, + prefix, u->cgroup_members_mask); + + SET_FOREACH(t, u->names, i) + fprintf(f, "%s\tName: %s\n", prefix, t); + + STRV_FOREACH(j, u->documentation) + fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); + + following = unit_following(u); + if (following) + fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); + + r = unit_following_set(u, &following_set); + if (r >= 0) { + Unit *other; + + SET_FOREACH(other, following_set, i) + fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id); + } + + if (u->fragment_path) + fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path); + + if (u->source_path) + fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path); + + STRV_FOREACH(j, u->dropin_paths) + fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j); + + if (u->job_timeout != USEC_INFINITY) + fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); + + if (u->job_timeout_action != FAILURE_ACTION_NONE) + fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, failure_action_to_string(u->job_timeout_action)); + + if (u->job_timeout_reboot_arg) + fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg); + + condition_dump_list(u->conditions, f, prefix, condition_type_to_string); + condition_dump_list(u->asserts, f, prefix, assert_type_to_string); + + if (dual_timestamp_is_set(&u->condition_timestamp)) + fprintf(f, + "%s\tCondition Timestamp: %s\n" + "%s\tCondition Result: %s\n", + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)), + prefix, yes_no(u->condition_result)); + + if (dual_timestamp_is_set(&u->assert_timestamp)) + fprintf(f, + "%s\tAssert Timestamp: %s\n" + "%s\tAssert Result: %s\n", + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->assert_timestamp.realtime)), + prefix, yes_no(u->assert_result)); + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + Unit *other; + + SET_FOREACH(other, u->dependencies[d], i) + fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id); + } + + if (!strv_isempty(u->requires_mounts_for)) { + fprintf(f, + "%s\tRequiresMountsFor:", prefix); + + STRV_FOREACH(j, u->requires_mounts_for) + fprintf(f, " %s", *j); + + fputs("\n", f); + } + + if (u->load_state == UNIT_LOADED) { + + fprintf(f, + "%s\tStopWhenUnneeded: %s\n" + "%s\tRefuseManualStart: %s\n" + "%s\tRefuseManualStop: %s\n" + "%s\tDefaultDependencies: %s\n" + "%s\tOnFailureJobMode: %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)); + + if (UNIT_VTABLE(u)->dump) + UNIT_VTABLE(u)->dump(u, f, prefix2); + + } else if (u->load_state == UNIT_MERGED) + fprintf(f, + "%s\tMerged into: %s\n", + prefix, u->merged_into->id); + else if (u->load_state == UNIT_ERROR) + fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error)); + + + if (u->job) + job_dump(u->job, f, prefix2); + + if (u->nop_job) + job_dump(u->nop_job, f, prefix2); + +} + +/* Common implementation for multiple backends */ +int unit_load_fragment_and_dropin(Unit *u) { + int r; + + assert(u); + + /* Load a .{service,socket,...} file */ + r = unit_load_fragment(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_STUB) + return -ENOENT; + + /* Load drop-in directory data */ + r = unit_load_dropin(unit_follow_merge(u)); + if (r < 0) + return r; + + return 0; +} + +/* Common implementation for multiple backends */ +int unit_load_fragment_and_dropin_optional(Unit *u) { + int r; + + assert(u); + + /* Same as unit_load_fragment_and_dropin(), but whether + * something can be loaded or not doesn't matter. */ + + /* Load a .service file */ + r = unit_load_fragment(u); + if (r < 0) + return r; + + if (u->load_state == UNIT_STUB) + u->load_state = UNIT_LOADED; + + /* Load drop-in directory data */ + r = unit_load_dropin(unit_follow_merge(u)); + if (r < 0) + return r; + + return 0; +} + +int unit_add_default_target_dependency(Unit *u, Unit *target) { + assert(u); + assert(target); + + if (target->type != UNIT_TARGET) + return 0; + + /* Only add the dependency if both units are loaded, so that + * that loop check below is reliable */ + if (u->load_state != UNIT_LOADED || + target->load_state != UNIT_LOADED) + return 0; + + /* If either side wants no automatic dependencies, then let's + * skip this */ + if (!u->default_dependencies || + !target->default_dependencies) + return 0; + + /* Don't create loops */ + if (set_get(target->dependencies[UNIT_BEFORE], u)) + return 0; + + return unit_add_dependency(target, UNIT_AFTER, u, true); +} + +static int unit_add_target_dependencies(Unit *u) { + + static const UnitDependency deps[] = { + UNIT_REQUIRED_BY, + UNIT_REQUISITE_OF, + UNIT_WANTED_BY, + UNIT_BOUND_BY + }; + + Unit *target; + Iterator i; + unsigned k; + int r = 0; + + assert(u); + + for (k = 0; k < ELEMENTSOF(deps); k++) + SET_FOREACH(target, u->dependencies[deps[k]], i) { + r = unit_add_default_target_dependency(u, target); + if (r < 0) + return r; + } + + return r; +} + +static int unit_add_slice_dependencies(Unit *u) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return 0; + + if (UNIT_ISSET(u->slice)) + return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true); + + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return 0; + + 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) { + char **i; + int r; + + assert(u); + + STRV_FOREACH(i, u->requires_mounts_for) { + char prefix[strlen(*i) + 1]; + + PATH_FOREACH_PREFIX_MORE(prefix, *i) { + _cleanup_free_ char *p = NULL; + Unit *m; + + r = unit_name_from_path(prefix, ".mount", &p); + if (r < 0) + return r; + + 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; + + if (m->load_state != UNIT_LOADED) + continue; + + r = unit_add_dependency(u, UNIT_AFTER, m, true); + if (r < 0) + return r; + + if (m->fragment_path) { + r = unit_add_dependency(u, UNIT_REQUIRES, m, true); + if (r < 0) + return r; + } + } + } + + return 0; +} + +static int unit_add_startup_units(Unit *u) { + CGroupContext *c; + int r; + + c = unit_get_cgroup_context(u); + if (!c) + return 0; + + if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID && + c->startup_io_weight == CGROUP_WEIGHT_INVALID && + c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) + return 0; + + r = set_ensure_allocated(&u->manager->startup_units, NULL); + if (r < 0) + return r; + + return set_put(u->manager->startup_units, u); +} + +int unit_load(Unit *u) { + int r; + + assert(u); + + if (u->in_load_queue) { + LIST_REMOVE(load_queue, u->manager->load_queue, u); + u->in_load_queue = false; + } + + if (u->type == _UNIT_TYPE_INVALID) + return -EINVAL; + + if (u->load_state != UNIT_STUB) + return 0; + + if (u->transient_file) { + r = fflush_and_check(u->transient_file); + if (r < 0) + goto fail; + + fclose(u->transient_file); + u->transient_file = NULL; + + u->fragment_mtime = now(CLOCK_REALTIME); + } + + if (UNIT_VTABLE(u)->load) { + r = UNIT_VTABLE(u)->load(u); + if (r < 0) + goto fail; + } + + if (u->load_state == UNIT_STUB) { + r = -ENOENT; + goto fail; + } + + if (u->load_state == UNIT_LOADED) { + + r = unit_add_target_dependencies(u); + if (r < 0) + goto fail; + + r = unit_add_slice_dependencies(u); + if (r < 0) + goto fail; + + r = unit_add_mount_dependencies(u); + if (r < 0) + goto fail; + + r = unit_add_startup_units(u); + if (r < 0) + goto fail; + + if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { + log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing."); + r = -EINVAL; + goto fail; + } + + unit_update_cgroup_members_masks(u); + } + + assert((u->load_state != UNIT_MERGED) == !u->merged_into); + + unit_add_to_dbus_queue(unit_follow_merge(u)); + unit_add_to_gc_queue(u); + + return 0; + +fail: + u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : UNIT_ERROR; + u->load_error = r; + unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); + + log_unit_debug_errno(u, r, "Failed to load configuration: %m"); + + return r; +} + +static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) { + Condition *c; + int triggered = -1; + + assert(u); + assert(to_string); + + /* If the condition list is empty, then it is true */ + if (!first) + return true; + + /* Otherwise, if all of the non-trigger conditions apply and + * if any of the trigger conditions apply (unless there are + * none) we return true */ + LIST_FOREACH(conditions, c, first) { + int r; + + r = condition_test(c); + if (r < 0) + log_unit_warning(u, + "Couldn't determine result for %s=%s%s%s, assuming failed: %m", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter); + else + log_unit_debug(u, + "%s=%s%s%s %s.", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + condition_result_to_string(c->result)); + + if (!c->trigger && r <= 0) + return false; + + if (c->trigger && triggered <= 0) + triggered = r > 0; + } + + return triggered != 0; +} + +static bool unit_condition_test(Unit *u) { + assert(u); + + dual_timestamp_get(&u->condition_timestamp); + u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string); + + return u->condition_result; +} + +static bool unit_assert_test(Unit *u) { + assert(u); + + dual_timestamp_get(&u->assert_timestamp); + u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string); + + 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(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); + + if (t != JOB_RELOAD) { + format_table = &UNIT_VTABLE(u)->status_message_formats; + if (format_table) { + format = format_table->starting_stopping[t == JOB_STOP]; + if (format) + return format; + } + } + + /* Return generic strings */ + if (t == JOB_START) + return "Starting %s."; + else if (t == JOB_STOP) + return "Stopping %s."; + else + return "Reloading %s."; +} + +static void unit_status_print_starting_stopping(Unit *u, JobType t) { + const char *format; + + 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; + unit_status_printf(u, "", format); + REENABLE_WARNING; +} + +static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { + const char *format; + char buf[LINE_MAX]; + sd_id128_t mid; + + assert(u); + + if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) + return; + + if (log_on_console()) + return; + + /* We log status messages for all units and all operations. */ + + format = unit_get_status_message_format(u, t); + + DISABLE_WARNING_FORMAT_NONLITERAL; + xsprintf(buf, format, unit_description(u)); + REENABLE_WARNING; + + mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : + t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : + SD_MESSAGE_UNIT_RELOADING; + + /* Note that we deliberately use LOG_MESSAGE() instead of + * LOG_UNIT_MESSAGE() here, since this is supposed to mimic + * closely what is written to screen using the status output, + * which is supposed the highest level, friendliest output + * possible, which means we should avoid the low-level unit + * name. */ + log_struct(LOG_INFO, + LOG_MESSAGE_ID(mid), + LOG_UNIT_ID(u), + LOG_MESSAGE("%s", buf), + NULL); +} + +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); + unit_status_print_starting_stopping(u, t); +} + +int unit_start_limit_test(Unit *u) { + assert(u); + + if (ratelimit_test(&u->start_limit)) { + u->start_limit_hit = false; + return 0; + } + + log_unit_warning(u, "Start request repeated too quickly."); + u->start_limit_hit = true; + + return failure_action(u->manager, u->start_limit_action, u->reboot_arg); +} + +/* Errors: + * -EBADR: This unit type does not support starting. + * -EALREADY: Unit is already started. + * -EAGAIN: An operation is already in progress. Retry later. + * -ECANCELED: Too many requests for now. + * -EPROTO: Assert failed + * -EINVAL: Unit not loaded + * -EOPNOTSUPP: Unit type not supported + */ +int unit_start(Unit *u) { + UnitActiveState state; + Unit *following; + + assert(u); + + /* If this is already started, then this will succeed. Note + * that this will even succeed if this unit is not startable + * by the user. This is relied on to detect when we need to + * wait for units and when waiting is finished. */ + state = unit_active_state(u); + if (UNIT_IS_ACTIVE_OR_RELOADING(state)) + return -EALREADY; + + /* Units that aren't loaded cannot be started */ + if (u->load_state != UNIT_LOADED) + return -EINVAL; + + /* If the conditions failed, don't do anything at all. If we + * already are activating this call might still be useful to + * speed up activation in case there is some hold-off time, + * but we don't want to recheck the condition in that case. */ + if (state != UNIT_ACTIVATING && + !unit_condition_test(u)) { + log_unit_debug(u, "Starting requested but condition failed. Not starting unit."); + return -EALREADY; + } + + /* If the asserts failed, fail the entire job */ + if (state != UNIT_ACTIVATING && + !unit_assert_test(u)) { + log_unit_notice(u, "Starting requested but asserts failed."); + return -EPROTO; + } + + /* Units of types that aren't supported cannot be + * started. Note that we do this test only after the condition + * checks, so that we rather return condition check errors + * (which are usually not considered a true failure) than "not + * supported" errors (which are considered a failure). + */ + if (!unit_supported(u)) + return -EOPNOTSUPP; + + /* Forward to the main object, if we aren't it. */ + following = unit_following(u); + if (following) { + log_unit_debug(u, "Redirecting start request from %s to %s.", u->id, following->id); + return unit_start(following); + } + + /* If it is stopped, but we cannot start it, then fail */ + if (!UNIT_VTABLE(u)->start) + return -EBADR; + + /* We don't suppress calls to ->start() here when we are + * already starting, to allow this request to be used as a + * "hurry up" call, for example when the unit is in some "auto + * restart" state where it waits for a holdoff timer to elapse + * before it will start again. */ + + unit_add_to_dbus_queue(u); + + return UNIT_VTABLE(u)->start(u); +} + +bool unit_can_start(Unit *u) { + assert(u); + + if (u->load_state != UNIT_LOADED) + return false; + + if (!unit_supported(u)) + return false; + + return !!UNIT_VTABLE(u)->start; +} + +bool unit_can_isolate(Unit *u) { + assert(u); + + return unit_can_start(u) && + u->allow_isolate; +} + +/* Errors: + * -EBADR: This unit type does not support stopping. + * -EALREADY: Unit is already stopped. + * -EAGAIN: An operation is already in progress. Retry later. + */ +int unit_stop(Unit *u) { + UnitActiveState state; + Unit *following; + + assert(u); + + state = unit_active_state(u); + if (UNIT_IS_INACTIVE_OR_FAILED(state)) + return -EALREADY; + + following = unit_following(u); + if (following) { + log_unit_debug(u, "Redirecting stop request from %s to %s.", u->id, following->id); + return unit_stop(following); + } + + if (!UNIT_VTABLE(u)->stop) + return -EBADR; + + unit_add_to_dbus_queue(u); + + return UNIT_VTABLE(u)->stop(u); +} + +/* Errors: + * -EBADR: This unit type does not support reloading. + * -ENOEXEC: Unit is not started. + * -EAGAIN: An operation is already in progress. Retry later. + */ +int unit_reload(Unit *u) { + UnitActiveState state; + Unit *following; + + assert(u); + + if (u->load_state != UNIT_LOADED) + return -EINVAL; + + if (!unit_can_reload(u)) + return -EBADR; + + state = unit_active_state(u); + if (state == UNIT_RELOADING) + return -EALREADY; + + if (state != UNIT_ACTIVE) { + log_unit_warning(u, "Unit cannot be reloaded because it is inactive."); + return -ENOEXEC; + } + + following = unit_following(u); + if (following) { + log_unit_debug(u, "Redirecting reload request from %s to %s.", u->id, following->id); + return unit_reload(following); + } + + unit_add_to_dbus_queue(u); + + return UNIT_VTABLE(u)->reload(u); +} + +bool unit_can_reload(Unit *u) { + assert(u); + + if (!UNIT_VTABLE(u)->reload) + return false; + + if (!UNIT_VTABLE(u)->can_reload) + return true; + + return UNIT_VTABLE(u)->can_reload(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_REQUISITE_OF, + UNIT_WANTED_BY, + UNIT_BOUND_BY, + }; + + Unit *other; + Iterator i; + unsigned j; + int r; + + assert(u); + + /* If this service shall be shut down when unneeded then do + * so. */ + + if (!u->stop_when_unneeded) + return; + + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + return; + + for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) + SET_FOREACH(other, u->dependencies[needed_dependencies[j]], i) + if (unit_active_or_pending(other)) + return; + + /* If stopping a unit fails continuously we might enter a stop + * loop here, hence stop acting on the service being + * unnecessary after a while. */ + if (!ratelimit_test(&u->auto_stop_ratelimit)) { + log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); + return; + } + + log_unit_info(u, "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, &error, NULL); + if (r < 0) + 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; + int r; + + assert(u); + + if (u->job) + return; + + if (unit_active_state(u) != UNIT_ACTIVE) + return; + + SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) { + if (other->job) + continue; + + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) + continue; + + stop = true; + break; + } + + if (!stop) + return; + + /* If stopping a unit fails continuously we might enter a stop + * loop here, hence stop acting on the service being + * unnecessary after a while. */ + if (!ratelimit_test(&u->auto_stop_ratelimit)) { + log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id); + return; + } + + assert(other); + log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); + + /* A unit we need to run is gone. Sniff. Let's stop this. */ + 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: %s", bus_error_message(&error, r)); +} + +static void retroactively_start_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(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, 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, 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, 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, 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, NULL, NULL); +} + +static void retroactively_stop_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); + + /* Pull down units which are bound to us recursively if enabled */ + SET_FOREACH(other, u->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, NULL, NULL); +} + +static void check_unneeded_dependencies(Unit *u) { + Iterator i; + Unit *other; + + assert(u); + assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); + + /* Garbage collect services that might not be needed anymore, if enabled */ + SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], 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_BINDS_TO], i) + if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) + unit_check_unneeded(other); +} + +void unit_start_on_failure(Unit *u) { + Unit *other; + Iterator i; + + assert(u); + + if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) + return; + + log_unit_info(u, "Triggering OnFailure= dependencies."); + + SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { + int r; + + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL); + if (r < 0) + log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m"); + } +} + +void unit_trigger_notify(Unit *u) { + Unit *other; + Iterator i; + + assert(u); + + SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i) + if (UNIT_VTABLE(other)->trigger_notify) + UNIT_VTABLE(other)->trigger_notify(other, u); +} + +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { + Manager *m; + bool unexpected; + + assert(u); + assert(os < _UNIT_ACTIVE_STATE_MAX); + assert(ns < _UNIT_ACTIVE_STATE_MAX); + + /* Note that this is called for all low-level state changes, + * even if they might map to the same high-level + * UnitActiveState! That means that ns == os is an expected + * behavior here. For example: if a mount point is remounted + * this function will be called too! */ + + m = u->manager; + + /* Update timestamps for state changes */ + if (!MANAGER_IS_RELOADING(m)) { + dual_timestamp_get(&u->state_change_timestamp); + + if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) + u->inactive_exit_timestamp = u->state_change_timestamp; + else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns)) + u->inactive_enter_timestamp = u->state_change_timestamp; + + if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns)) + u->active_enter_timestamp = u->state_change_timestamp; + else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns)) + u->active_exit_timestamp = u->state_change_timestamp; + } + + /* Keep track of failed units */ + (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); + + /* Make sure the cgroup is always removed when we become inactive */ + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + unit_prune_cgroup(u); + + /* Note that this doesn't apply to RemainAfterExit services exiting + * successfully, since there's no change of state in that case. Which is + * why it is handled in service_set_state() */ + if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { + ExecContext *ec; + + ec = unit_get_exec_context(u); + if (ec && exec_context_may_touch_console(ec)) { + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { + m->n_on_console--; + + if (m->n_on_console == 0) + /* unset no_console_output flag, since the console is free */ + m->no_console_output = false; + } else + m->n_on_console++; + } + } + + if (u->job) { + unexpected = false; + + if (u->job->state == JOB_WAITING) + + /* So we reached a different state for this + * job. Let's see if we can run it now if it + * failed previously due to EAGAIN. */ + job_add_to_run_queue(u->job); + + /* Let's check whether this state change constitutes a + * finished job, or maybe contradicts a running job and + * hence needs to invalidate jobs. */ + + switch (u->job->type) { + + case JOB_START: + case JOB_VERIFY_ACTIVE: + + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) + job_finish_and_invalidate(u->job, JOB_DONE, true, false); + else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { + unexpected = true; + + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); + } + + break; + + case JOB_RELOAD: + case JOB_RELOAD_OR_START: + case JOB_TRY_RELOAD: + + if (u->job->state == JOB_RUNNING) { + if (ns == UNIT_ACTIVE) + job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); + else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { + unexpected = true; + + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); + } + } + + break; + + case JOB_STOP: + case JOB_RESTART: + case JOB_TRY_RESTART: + + if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + job_finish_and_invalidate(u->job, JOB_DONE, true, false); + else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { + unexpected = true; + job_finish_and_invalidate(u->job, JOB_FAILED, true, false); + } + + break; + + default: + assert_not_reached("Job type unknown"); + } + + } else + unexpected = true; + + if (!MANAGER_IS_RELOADING(m)) { + + /* If this state change happened without being + * requested by a job, then let's retroactively start + * or stop dependencies. We skip that step when + * deserializing, since we don't want to create any + * additional jobs just because something is already + * activated. */ + + if (unexpected) { + if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns)) + retroactively_start_dependencies(u); + else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + retroactively_stop_dependencies(u); + } + + /* stop unneeded units regardless if going down was expected or not */ + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + check_unneeded_dependencies(u); + + if (ns != os && ns == UNIT_FAILED) { + log_unit_notice(u, "Unit entered failed state."); + unit_start_on_failure(u); + } + } + + /* Some names are special */ + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + + if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) + /* The bus might have just become available, + * hence try to connect to it, if we aren't + * yet connected. */ + bus_init(m, true); + + if (u->type == UNIT_SERVICE && + !UNIT_IS_ACTIVE_OR_RELOADING(os) && + !MANAGER_IS_RELOADING(m)) { + /* Write audit record if we have just finished starting up */ + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); + u->in_audit = true; + } + + if (!UNIT_IS_ACTIVE_OR_RELOADING(os)) + manager_send_unit_plymouth(m, u); + + } else { + + /* We don't care about D-Bus here, since we'll get an + * asynchronous notification for it anyway. */ + + if (u->type == UNIT_SERVICE && + UNIT_IS_INACTIVE_OR_FAILED(ns) && + !UNIT_IS_INACTIVE_OR_FAILED(os) && + !MANAGER_IS_RELOADING(m)) { + + /* Hmm, if there was no start record written + * write it now, so that we always have a nice + * pair */ + if (!u->in_audit) { + manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE); + + if (ns == UNIT_INACTIVE) + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true); + } else + /* Write audit record if we have just finished shutting down */ + manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); + + u->in_audit = false; + } + } + + manager_recheck_journal(m); + unit_trigger_notify(u); + + if (!MANAGER_IS_RELOADING(u->manager)) { + /* Maybe we finished startup and are now ready for + * being stopped because unneeded? */ + unit_check_unneeded(u); + + /* Maybe we finished startup, but something we needed + * has vanished? Let's die then. (This happens when + * something BindsTo= to a Type=oneshot unit, as these + * units go directly from starting to inactive, + * without ever entering started.) */ + unit_check_binds_to(u); + } + + unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); +} + +int unit_watch_pid(Unit *u, pid_t pid) { + int q, r; + + assert(u); + assert(pid >= 1); + + /* Watch a specific PID. We only support one or two units + * watching each PID for now, not more. */ + + r = set_ensure_allocated(&u->pids, NULL); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&u->manager->watch_pids1, NULL); + if (r < 0) + return r; + + r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u); + if (r == -EEXIST) { + r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL); + if (r < 0) + return r; + + r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u); + } + + q = set_put(u->pids, PID_TO_PTR(pid)); + if (q < 0) + return q; + + return r; +} + +void unit_unwatch_pid(Unit *u, pid_t pid) { + assert(u); + assert(pid >= 1); + + (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u); + (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u); + (void) set_remove(u->pids, PID_TO_PTR(pid)); +} + +void unit_unwatch_all_pids(Unit *u) { + assert(u); + + while (!set_isempty(u->pids)) + unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids))); + + u->pids = set_free(u->pids); +} + +void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { + Iterator i; + void *e; + + assert(u); + + /* Cleans dead PIDs from our list */ + + SET_FOREACH(e, u->pids, i) { + pid_t pid = PTR_TO_PID(e); + + if (pid == except1 || pid == except2) + continue; + + if (!pid_is_unwaited(pid)) + unit_unwatch_pid(u, pid); + } +} + +bool unit_job_is_applicable(Unit *u, JobType j) { + assert(u); + assert(j >= 0 && j < _JOB_TYPE_MAX); + + switch (j) { + + case JOB_VERIFY_ACTIVE: + case JOB_START: + case JOB_STOP: + case JOB_NOP: + return true; + + case JOB_RESTART: + case JOB_TRY_RESTART: + return unit_can_start(u); + + case JOB_RELOAD: + case JOB_TRY_RELOAD: + return unit_can_reload(u); + + case JOB_RELOAD_OR_START: + return unit_can_reload(u) && unit_can_start(u); + + default: + assert_not_reached("Invalid job type"); + } +} + +static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency) { + assert(u); + + /* Only warn about some unit types */ + if (!IN_SET(dependency, UNIT_CONFLICTS, UNIT_CONFLICTED_BY, UNIT_BEFORE, UNIT_AFTER, UNIT_ON_FAILURE, UNIT_TRIGGERS, UNIT_TRIGGERED_BY)) + return; + + if (streq_ptr(u->id, other)) + log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id); + else + log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other), u->id); +} + +int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) { + + static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { + [UNIT_REQUIRES] = UNIT_REQUIRED_BY, + [UNIT_WANTS] = UNIT_WANTED_BY, + [UNIT_REQUISITE] = UNIT_REQUISITE_OF, + [UNIT_BINDS_TO] = UNIT_BOUND_BY, + [UNIT_PART_OF] = UNIT_CONSISTS_OF, + [UNIT_REQUIRED_BY] = UNIT_REQUIRES, + [UNIT_REQUISITE_OF] = UNIT_REQUISITE, + [UNIT_WANTED_BY] = UNIT_WANTS, + [UNIT_BOUND_BY] = UNIT_BINDS_TO, + [UNIT_CONSISTS_OF] = UNIT_PART_OF, + [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, + [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, + [UNIT_BEFORE] = UNIT_AFTER, + [UNIT_AFTER] = UNIT_BEFORE, + [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID, + [UNIT_REFERENCES] = UNIT_REFERENCED_BY, + [UNIT_REFERENCED_BY] = UNIT_REFERENCES, + [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, + [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, + [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, + [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, + [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, + }; + int r, q = 0, v = 0, w = 0; + Unit *orig_u = u, *orig_other = other; + + assert(u); + assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX); + assert(other); + + u = unit_follow_merge(u); + other = unit_follow_merge(other); + + /* We won't allow dependencies on ourselves. We will not + * consider them an error however. */ + if (u == other) { + maybe_warn_about_dependency(orig_u, orig_other->id, d); + return 0; + } + + r = set_ensure_allocated(&u->dependencies[d], NULL); + if (r < 0) + return r; + + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) { + r = set_ensure_allocated(&other->dependencies[inverse_table[d]], NULL); + if (r < 0) + return r; + } + + if (add_reference) { + r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], NULL); + if (r < 0) + return r; + + r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], NULL); + if (r < 0) + return r; + } + + q = set_put(u->dependencies[d], other); + if (q < 0) + return q; + + if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) { + v = set_put(other->dependencies[inverse_table[d]], u); + if (v < 0) { + r = v; + goto fail; + } + } + + if (add_reference) { + w = set_put(u->dependencies[UNIT_REFERENCES], other); + if (w < 0) { + r = w; + goto fail; + } + + r = set_put(other->dependencies[UNIT_REFERENCED_BY], u); + if (r < 0) + goto fail; + } + + unit_add_to_dbus_queue(u); + return 0; + +fail: + if (q > 0) + set_remove(u->dependencies[d], other); + + if (v > 0) + set_remove(other->dependencies[inverse_table[d]], u); + + if (w > 0) + set_remove(u->dependencies[UNIT_REFERENCES], other); + + return r; +} + +int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) { + int r; + + assert(u); + + r = unit_add_dependency(u, d, other, add_reference); + if (r < 0) + return r; + + return unit_add_dependency(u, e, other, add_reference); +} + +static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) { + int r; + + assert(u); + assert(name || path); + assert(buf); + assert(ret); + + if (!name) + name = basename(path); + + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + *buf = NULL; + *ret = name; + return 0; + } + + if (u->instance) + r = unit_name_replace_instance(name, u->instance, buf); + else { + _cleanup_free_ char *i = NULL; + + r = unit_name_to_prefix(u->id, &i); + if (r < 0) + return r; + + r = unit_name_replace_instance(name, i, buf); + } + if (r < 0) + return r; + + *ret = *buf; + return 0; +} + +int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) { + _cleanup_free_ char *buf = NULL; + Unit *other; + int r; + + 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(u, d, other, add_reference); +} + +int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) { + _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(u, d, e, other, add_reference); +} + +int set_unit_path(const char *p) { + /* This is mostly for debug purposes */ + if (setenv("SYSTEMD_UNIT_PATH", p, 1) < 0) + return -errno; + + return 0; +} + +char *unit_dbus_path(Unit *u) { + assert(u); + + if (!u->id) + return NULL; + + return unit_dbus_path_from_name(u->id); +} + +int unit_set_slice(Unit *u, Unit *slice) { + assert(u); + assert(slice); + + /* Sets the unit slice if it has not been set before. Is extra + * careful, to only allow this for units that actually have a + * cgroup context. Also, we don't allow to set this for slices + * (since the parent slice is derived from the name). Make + * sure the unit we set is actually a slice. */ + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return -EOPNOTSUPP; + + if (u->type == UNIT_SLICE) + return -EINVAL; + + if (unit_active_state(u) != UNIT_INACTIVE) + return -EBUSY; + + if (slice->type != UNIT_SLICE) + return -EINVAL; + + if (unit_has_name(u, SPECIAL_INIT_SCOPE) && + !unit_has_name(slice, SPECIAL_ROOT_SLICE)) + return -EPERM; + + if (UNIT_DEREF(u->slice) == slice) + return 0; + + /* Disallow slice changes if @u is already bound to cgroups */ + if (UNIT_ISSET(u->slice) && u->cgroup_realized) + return -EBUSY; + + unit_ref_unset(&u->slice); + unit_ref_set(&u->slice, slice); + return 1; +} + +int unit_set_default_slice(Unit *u) { + _cleanup_free_ char *b = NULL; + const char *slice_name; + Unit *slice; + int r; + + assert(u); + + if (UNIT_ISSET(u->slice)) + return 0; + + if (u->instance) { + _cleanup_free_ char *prefix = NULL, *escaped = NULL; + + /* Implicitly place all instantiated units in their + * own per-template slice */ + + r = unit_name_to_prefix(u->id, &prefix); + if (r < 0) + return r; + + /* The prefix is already escaped, but it might include + * "-" which has a special meaning for slice units, + * hence escape it here extra. */ + escaped = unit_name_escape(prefix); + if (!escaped) + return -ENOMEM; + + if (MANAGER_IS_SYSTEM(u->manager)) + b = strjoin("system-", escaped, ".slice", NULL); + else + b = strappend(escaped, ".slice"); + if (!b) + return -ENOMEM; + + slice_name = b; + } else + slice_name = + MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE) + ? SPECIAL_SYSTEM_SLICE + : SPECIAL_ROOT_SLICE; + + r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice); + if (r < 0) + return r; + + return unit_set_slice(u, slice); +} + +const char *unit_slice_name(Unit *u) { + assert(u); + + if (!UNIT_ISSET(u->slice)) + return NULL; + + return UNIT_DEREF(u->slice)->id; +} + +int unit_load_related_unit(Unit *u, const char *type, Unit **_found) { + _cleanup_free_ char *t = NULL; + int r; + + assert(u); + assert(type); + assert(_found); + + r = unit_name_change_suffix(u->id, type, &t); + if (r < 0) + return r; + if (unit_has_name(u, t)) + return -EINVAL; + + r = manager_load_unit(u->manager, t, NULL, NULL, _found); + assert(r < 0 || *_found != u); + return r; +} + +static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + const char *name, *old_owner, *new_owner; + Unit *u = userdata; + int r; + + assert(message); + assert(u); + + r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + if (UNIT_VTABLE(u)->bus_name_owner_change) + UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner); + + return 0; +} + +int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { + const char *match; + + assert(u); + assert(bus); + assert(name); + + if (u->match_bus_slot) + return -EBUSY; + + match = strjoina("type='signal'," + "sender='org.freedesktop.DBus'," + "path='/org/freedesktop/DBus'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='", name, "'"); + + return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); +} + +int unit_watch_bus_name(Unit *u, const char *name) { + int r; + + assert(u); + assert(name); + + /* Watch a specific name on the bus. We only support one unit + * watching each name for now. */ + + 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, u->manager->api_bus, name); + if (r < 0) + return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); + } + + r = hashmap_put(u->manager->watch_bus, name, u); + if (r < 0) { + u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); + return log_warning_errno(r, "Failed to put bus name to hashmap: %m"); + } + + return 0; +} + +void unit_unwatch_bus_name(Unit *u, const char *name) { + assert(u); + assert(name); + + hashmap_remove_value(u->manager->watch_bus, name, u); + u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); +} + +bool unit_can_serialize(Unit *u) { + assert(u); + + return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; +} + +int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { + int r; + + assert(u); + assert(f); + assert(fds); + + if (unit_can_serialize(u)) { + ExecRuntime *rt; + + r = UNIT_VTABLE(u)->serialize(u, f, fds); + if (r < 0) + return r; + + rt = unit_get_exec_runtime(u); + if (rt) { + r = exec_runtime_serialize(u, rt, f, fds); + if (r < 0) + return r; + } + } + + dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp); + + dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp); + dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp); + dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); + dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); + + dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); + dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp); + + if (dual_timestamp_is_set(&u->condition_timestamp)) + unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); + + if (dual_timestamp_is_set(&u->assert_timestamp)) + unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result)); + + unit_serialize_item(u, f, "transient", yes_no(u->transient)); + unit_serialize_item_format(u, f, "cpuacct-usage-base", "%" PRIu64, u->cpuacct_usage_base); + + if (u->cgroup_path) + unit_serialize_item(u, f, "cgroup", u->cgroup_path); + unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + + if (serialize_jobs) { + if (u->job) { + fprintf(f, "job\n"); + job_serialize(u->job, f, fds); + } + + if (u->nop_job) { + fprintf(f, "job\n"); + job_serialize(u->nop_job, f, fds); + } + } + + /* End marker */ + fputc('\n', f); + 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; + + assert(u); + assert(f); + assert(key); + assert(format); + + fputs(key, f); + fputc('=', f); + + va_start(ap, format); + vfprintf(f, format, ap); + va_end(ap); + + fputc('\n', f); +} + +int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + ExecRuntime **rt = NULL; + size_t offset; + int r; + + assert(u); + assert(f); + assert(fds); + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset > 0) + rt = (ExecRuntime**) ((uint8_t*) u + offset); + + for (;;) { + char line[LINE_MAX], *l, *v; + size_t k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + return 0; + return -errno; + } + + char_array_0(line); + l = strstrip(line); + + /* End marker */ + if (isempty(l)) + break; + + k = strcspn(l, "="); + + if (l[k] == '=') { + l[k] = 0; + v = l+k+1; + } else + v = l+k; + + if (streq(l, "job")) { + if (v[0] == '\0') { + /* new-style serialized job */ + Job *j; + + j = job_new_raw(u); + if (!j) + return log_oom(); + + r = job_deserialize(j, f, fds); + if (r < 0) { + job_free(j); + return r; + } + + r = hashmap_put(u->manager->jobs, UINT32_TO_PTR(j->id), j); + if (r < 0) { + job_free(j); + return r; + } + + r = job_install_deserialized(j); + if (r < 0) { + hashmap_remove(u->manager->jobs, UINT32_TO_PTR(j->id)); + job_free(j); + return r; + } + } else /* legacy for pre-44 */ + log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v); + continue; + } else if (streq(l, "state-change-timestamp")) { + dual_timestamp_deserialize(v, &u->state_change_timestamp); + continue; + } else if (streq(l, "inactive-exit-timestamp")) { + dual_timestamp_deserialize(v, &u->inactive_exit_timestamp); + continue; + } else if (streq(l, "active-enter-timestamp")) { + dual_timestamp_deserialize(v, &u->active_enter_timestamp); + continue; + } else if (streq(l, "active-exit-timestamp")) { + dual_timestamp_deserialize(v, &u->active_exit_timestamp); + continue; + } else if (streq(l, "inactive-enter-timestamp")) { + dual_timestamp_deserialize(v, &u->inactive_enter_timestamp); + continue; + } else if (streq(l, "condition-timestamp")) { + dual_timestamp_deserialize(v, &u->condition_timestamp); + continue; + } else if (streq(l, "assert-timestamp")) { + dual_timestamp_deserialize(v, &u->assert_timestamp); + continue; + } else if (streq(l, "condition-result")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse condition result value %s, ignoring.", v); + else + u->condition_result = r; + + continue; + + } else if (streq(l, "assert-result")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse assert result value %s, ignoring.", v); + else + u->assert_result = r; + + continue; + + } else if (streq(l, "transient")) { + + r = parse_boolean(v); + if (r < 0) + log_unit_debug(u, "Failed to parse transient bool %s, ignoring.", v); + else + u->transient = r; + + continue; + + } else if (streq(l, "cpuacct-usage-base")) { + + r = safe_atou64(v, &u->cpuacct_usage_base); + if (r < 0) + log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v); + + continue; + + } else if (streq(l, "cgroup")) { + + r = unit_set_cgroup_path(u, v); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v); + + (void) unit_watch_cgroup(u); + + continue; + } else if (streq(l, "cgroup-realized")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_unit_debug(u, "Failed to parse cgroup-realized bool %s, ignoring.", v); + else + u->cgroup_realized = b; + + continue; + } + + if (unit_can_serialize(u)) { + if (rt) { + r = exec_runtime_deserialize_item(u, rt, l, v, fds); + if (r < 0) { + log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l); + continue; + } + + /* Returns positive if key was handled by the call */ + if (r > 0) + continue; + } + + r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds); + if (r < 0) + log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l); + } + } + + /* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is + * useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from + * before 228 where the base for timeouts was not persistent across reboots. */ + + if (!dual_timestamp_is_set(&u->state_change_timestamp)) + dual_timestamp_get(&u->state_change_timestamp); + + return 0; +} + +int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) { + Unit *device; + _cleanup_free_ char *e = NULL; + int r; + + assert(u); + + /* Adds in links to the device node that this unit is based on */ + if (isempty(what)) + return 0; + + if (!is_device_path(what)) + return 0; + + /* When device units aren't supported (such as in a + * container), don't create dependencies on them. */ + if (!unit_type_supported(UNIT_DEVICE)) + return 0; + + r = unit_name_from_path(what, ".device", &e); + if (r < 0) + return r; + + r = manager_load_unit(u->manager, e, NULL, NULL, &device); + if (r < 0) + return r; + + r = unit_add_two_dependencies(u, UNIT_AFTER, + MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS, + device, true); + if (r < 0) + return r; + + if (wants) { + r = unit_add_dependency(device, UNIT_WANTS, u, false); + if (r < 0) + return r; + } + + return 0; +} + +int unit_coldplug(Unit *u) { + int r = 0, q = 0; + + assert(u); + + /* Make sure we don't enter a loop, when coldplugging + * recursively. */ + if (u->coldplugged) + return 0; + + u->coldplugged = true; + + if (UNIT_VTABLE(u)->coldplug) + r = UNIT_VTABLE(u)->coldplug(u); + + if (u->job) + q = job_coldplug(u->job); + + if (r < 0) + return r; + if (q < 0) + return q; + + return 0; +} + +static bool fragment_mtime_newer(const char *path, usec_t mtime) { + struct stat st; + + if (!path) + return false; + + if (stat(path, &st) < 0) + /* What, cannot access this anymore? */ + return true; + + if (mtime > 0) + /* For non-empty files check the mtime */ + return timespec_load(&st.st_mtim) > mtime; + else if (!null_or_empty(&st)) + /* For masked files check if they are still so */ + return true; + + return false; +} + +bool unit_need_daemon_reload(Unit *u) { + _cleanup_strv_free_ char **t = NULL; + char **path; + + assert(u); + + if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime)) + return true; + + if (fragment_mtime_newer(u->source_path, u->source_mtime)) + return true; + + (void) unit_find_dropin_paths(u, &t); + if (!strv_equal(u->dropin_paths, t)) + return true; + + STRV_FOREACH(path, u->dropin_paths) + if (fragment_mtime_newer(*path, u->dropin_mtime)) + return true; + + return false; +} + +void unit_reset_failed(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->reset_failed) + UNIT_VTABLE(u)->reset_failed(u); + + RATELIMIT_RESET(u->start_limit); + u->start_limit_hit = false; +} + +Unit *unit_following(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->following) + return UNIT_VTABLE(u)->following(u); + + return NULL; +} + +bool unit_stop_pending(Unit *u) { + assert(u); + + /* This call does check the current state of the unit. It's + * hence useful to be called from state change calls of the + * unit itself, where the state isn't updated yet. This is + * different from unit_inactive_or_pending() which checks both + * the current state and for a queued job. */ + + return u->job && u->job->type == JOB_STOP; +} + +bool unit_inactive_or_pending(Unit *u) { + assert(u); + + /* Returns true if the unit is inactive or going down */ + + if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))) + return true; + + if (unit_stop_pending(u)) + return true; + + return false; +} + +bool unit_active_or_pending(Unit *u) { + assert(u); + + /* Returns true if the unit is active or going up */ + + if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + return true; + + if (u->job && + (u->job->type == JOB_START || + u->job->type == JOB_RELOAD_OR_START || + u->job->type == JOB_RESTART)) + return true; + + return false; +} + +int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { + assert(u); + assert(w >= 0 && w < _KILL_WHO_MAX); + assert(SIGNAL_VALID(signo)); + + if (!UNIT_VTABLE(u)->kill) + return -EOPNOTSUPP; + + return UNIT_VTABLE(u)->kill(u, w, signo, error); +} + +static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { + Set *pid_set; + int r; + + pid_set = set_new(NULL); + if (!pid_set) + return NULL; + + /* Exclude the main/control pids from being killed via the cgroup */ + if (main_pid > 0) { + r = set_put(pid_set, PID_TO_PTR(main_pid)); + if (r < 0) + goto fail; + } + + if (control_pid > 0) { + r = set_put(pid_set, PID_TO_PTR(control_pid)); + if (r < 0) + goto fail; + } + + return pid_set; + +fail: + set_free(pid_set); + return NULL; +} + +int unit_kill_common( + Unit *u, + KillWho who, + int signo, + pid_t main_pid, + pid_t control_pid, + sd_bus_error *error) { + + int r = 0; + bool killed = false; + + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) { + if (main_pid < 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); + else if (main_pid == 0) + return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); + } + + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) { + if (control_pid < 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); + else if (control_pid == 0) + return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); + } + + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (control_pid > 0) { + if (kill(control_pid, signo) < 0) + r = -errno; + else + killed = true; + } + + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (main_pid > 0) { + if (kill(main_pid, signo) < 0) + r = -errno; + else + killed = true; + } + + if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) { + _cleanup_set_free_ Set *pid_set = NULL; + int q; + + /* Exclude the main/control pids from being killed via the cgroup */ + pid_set = unit_pid_set(main_pid, control_pid); + if (!pid_set) + return -ENOMEM; + + q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, NULL, NULL); + if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) + r = q; + else + killed = true; + } + + if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL)) + return -ESRCH; + + return r; +} + +int unit_following_set(Unit *u, Set **s) { + assert(u); + assert(s); + + if (UNIT_VTABLE(u)->following_set) + return UNIT_VTABLE(u)->following_set(u, s); + + *s = NULL; + return 0; +} + +UnitFileState unit_get_unit_file_state(Unit *u) { + int r; + + assert(u); + + if (u->unit_file_state < 0 && u->fragment_path) { + r = unit_file_get_state( + u->manager->unit_file_scope, + NULL, + basename(u->fragment_path), + &u->unit_file_state); + if (r < 0) + u->unit_file_state = UNIT_FILE_BAD; + } + + return u->unit_file_state; +} + +int unit_get_unit_file_preset(Unit *u) { + assert(u); + + if (u->unit_file_preset < 0 && u->fragment_path) + u->unit_file_preset = unit_file_query_preset( + u->manager->unit_file_scope, + NULL, + basename(u->fragment_path)); + + return u->unit_file_preset; +} + +Unit* unit_ref_set(UnitRef *ref, Unit *u) { + assert(ref); + assert(u); + + if (ref->unit) + unit_ref_unset(ref); + + ref->unit = u; + LIST_PREPEND(refs, u->refs, ref); + return u; +} + +void unit_ref_unset(UnitRef *ref) { + assert(ref); + + if (!ref->unit) + return; + + /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might + * be unreferenced now. */ + unit_add_to_gc_queue(ref->unit); + + LIST_REMOVE(refs, ref->unit->refs, ref); + ref->unit = NULL; +} + +int unit_patch_contexts(Unit *u) { + CGroupContext *cc; + ExecContext *ec; + unsigned i; + int r; + + assert(u); + + /* Patch in the manager defaults into the exec and cgroup + * contexts, _after_ the rest of the settings have been + * initialized */ + + ec = unit_get_exec_context(u); + if (ec) { + /* This only copies in the ones that need memory */ + for (i = 0; i < _RLIMIT_MAX; i++) + if (u->manager->rlimit[i] && !ec->rlimit[i]) { + ec->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1); + if (!ec->rlimit[i]) + return -ENOMEM; + } + + if (MANAGER_IS_USER(u->manager) && + !ec->working_directory) { + + r = get_home_dir(&ec->working_directory); + if (r < 0) + return r; + + /* Allow user services to run, even if the + * home directory is missing */ + ec->working_directory_missing_ok = true; + } + + if (MANAGER_IS_USER(u->manager) && + (ec->syscall_whitelist || + !set_isempty(ec->syscall_filter) || + !set_isempty(ec->syscall_archs) || + ec->address_families_whitelist || + !set_isempty(ec->address_families))) + ec->no_new_privileges = true; + + if (ec->private_devices) + ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD); + } + + cc = unit_get_cgroup_context(u); + if (cc) { + + if (ec && + ec->private_devices && + cc->device_policy == CGROUP_AUTO) + cc->device_policy = CGROUP_CLOSED; + } + + return 0; +} + +ExecContext *unit_get_exec_context(Unit *u) { + size_t offset; + assert(u); + + if (u->type < 0) + return NULL; + + offset = UNIT_VTABLE(u)->exec_context_offset; + if (offset <= 0) + return NULL; + + return (ExecContext*) ((uint8_t*) u + offset); +} + +KillContext *unit_get_kill_context(Unit *u) { + size_t offset; + assert(u); + + if (u->type < 0) + return NULL; + + offset = UNIT_VTABLE(u)->kill_context_offset; + if (offset <= 0) + return NULL; + + return (KillContext*) ((uint8_t*) u + offset); +} + +CGroupContext *unit_get_cgroup_context(Unit *u) { + size_t offset; + + if (u->type < 0) + return NULL; + + offset = UNIT_VTABLE(u)->cgroup_context_offset; + if (offset <= 0) + return NULL; + + return (CGroupContext*) ((uint8_t*) u + offset); +} + +ExecRuntime *unit_get_exec_runtime(Unit *u) { + size_t offset; + + if (u->type < 0) + return NULL; + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + if (offset <= 0) + return NULL; + + return *(ExecRuntime**) ((uint8_t*) u + offset); +} + +static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) { + assert(u); + + if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT)) + return NULL; + + if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */ + return u->manager->lookup_paths.transient; + + if (mode == UNIT_RUNTIME) + return u->manager->lookup_paths.runtime_control; + + if (mode == UNIT_PERSISTENT) + return u->manager->lookup_paths.persistent_control; + + return NULL; +} + +int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { + _cleanup_free_ char *p = NULL, *q = NULL; + const char *dir, *wrapped; + int r; + + assert(u); + + if (u->transient_file) { + /* When this is a transient unit file in creation, then let's not create a new drop-in but instead + * write to the transient unit file. */ + fputs(data, u->transient_file); + fputc('\n', u->transient_file); + return 0; + } + + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) + return 0; + + dir = unit_drop_in_dir(u, mode); + if (!dir) + return -EINVAL; + + wrapped = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\"\n" + "# or an equivalent operation. Do not edit.\n", + data, + "\n"); + + r = drop_in_file(dir, u->id, 50, name, &p, &q); + if (r < 0) + return r; + + (void) mkdir_p(p, 0755); + r = write_string_file_atomic_label(q, wrapped); + if (r < 0) + return r; + + r = strv_push(&u->dropin_paths, q); + if (r < 0) + return r; + q = NULL; + + strv_uniq(u->dropin_paths); + + u->dropin_mtime = now(CLOCK_REALTIME); + + return 0; +} + +int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) { + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + assert(u); + assert(name); + assert(format); + + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) + return 0; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return unit_write_drop_in(u, mode, name, p); +} + +int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { + const char *ndata; + + assert(u); + assert(name); + assert(data); + + if (!UNIT_VTABLE(u)->private_section) + return -EINVAL; + + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) + return 0; + + ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data); + + return unit_write_drop_in(u, mode, name, ndata); +} + +int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) { + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + assert(u); + assert(name); + assert(format); + + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) + return 0; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return unit_write_drop_in_private(u, mode, name, p); +} + +int unit_make_transient(Unit *u) { + FILE *f; + char *path; + + assert(u); + + if (!UNIT_VTABLE(u)->can_transient) + return -EOPNOTSUPP; + + path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL); + if (!path) + return -ENOMEM; + + /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are + * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */ + + RUN_WITH_UMASK(0022) { + f = fopen(path, "we"); + if (!f) { + free(path); + return -errno; + } + } + + if (u->transient_file) + fclose(u->transient_file); + u->transient_file = f; + + free(u->fragment_path); + u->fragment_path = 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; + + u->load_state = UNIT_STUB; + u->load_error = 0; + u->transient = true; + + unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); + + fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n", + u->transient_file); + + return 0; +} + +static void log_kill(pid_t pid, int sig, void *userdata) { + _cleanup_free_ char *comm = NULL; + + (void) get_process_comm(pid, &comm); + + /* Don't log about processes marked with brackets, under the assumption that these are temporary processes + only, like for example systemd's own PAM stub process. */ + if (comm && comm[0] == '(') + return; + + log_unit_notice(userdata, + "Killing process " PID_FMT " (%s) with signal SIG%s.", + pid, + strna(comm), + signal_to_string(sig)); +} + +static int operation_to_signal(KillContext *c, KillOperation k) { + assert(c); + + switch (k) { + + case KILL_TERMINATE: + case KILL_TERMINATE_AND_LOG: + return c->kill_signal; + + case KILL_KILL: + return SIGKILL; + + case KILL_ABORT: + return SIGABRT; + + default: + assert_not_reached("KillOperation unknown"); + } +} + +int unit_kill_context( + Unit *u, + KillContext *c, + KillOperation k, + pid_t main_pid, + pid_t control_pid, + bool main_pid_alien) { + + bool wait_for_exit = false, send_sighup; + cg_kill_log_func_t log_func; + int sig, r; + + assert(u); + assert(c); + + /* Kill the processes belonging to this unit, in preparation for shutting the unit down. Returns > 0 if we + * killed something worth waiting for, 0 otherwise. */ + + if (c->kill_mode == KILL_NONE) + return 0; + + sig = operation_to_signal(c, k); + + send_sighup = + c->send_sighup && + IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) && + sig != SIGHUP; + + log_func = + k != KILL_TERMINATE || + IN_SET(sig, SIGKILL, SIGABRT) ? log_kill : NULL; + + if (main_pid > 0) { + if (log_func) + log_func(main_pid, sig, u); + + r = kill_and_sigcont(main_pid, sig); + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + (void) get_process_comm(main_pid, &comm); + + log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm)); + } else { + if (!main_pid_alien) + wait_for_exit = true; + + if (r != -ESRCH && send_sighup) + (void) kill(main_pid, SIGHUP); + } + } + + if (control_pid > 0) { + if (log_func) + log_func(control_pid, sig, u); + + r = kill_and_sigcont(control_pid, sig); + if (r < 0 && r != -ESRCH) { + _cleanup_free_ char *comm = NULL; + (void) get_process_comm(control_pid, &comm); + + log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm)); + } else { + wait_for_exit = true; + + if (r != -ESRCH && send_sighup) + (void) kill(control_pid, SIGHUP); + } + } + + if (u->cgroup_path && + (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) { + _cleanup_set_free_ Set *pid_set = NULL; + + /* Exclude the main/control pids from being killed via the cgroup */ + pid_set = unit_pid_set(main_pid, control_pid); + if (!pid_set) + return -ENOMEM; + + r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, + sig, + CGROUP_SIGCONT|CGROUP_IGNORE_SELF, + pid_set, + log_func, u); + if (r < 0) { + if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) + log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path); + + } else if (r > 0) { + + /* FIXME: For now, on the legacy hierarchy, we + * will not wait for the cgroup members to die + * if we are running in a container or if this + * is a delegation unit, simply because cgroup + * notification is unreliable in these + * cases. It doesn't work at all in + * containers, and outside of containers it + * can be confused easily by left-over + * directories in the cgroup — which however + * should not exist in non-delegated units. On + * the unified hierarchy that's different, + * there we get proper events. Hence rely on + * them.*/ + + if (cg_unified() > 0 || + (detect_container() == 0 && !unit_cgroup_delegate(u))) + wait_for_exit = true; + + if (send_sighup) { + set_free(pid_set); + + pid_set = unit_pid_set(main_pid, control_pid); + if (!pid_set) + return -ENOMEM; + + cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, + SIGHUP, + CGROUP_IGNORE_SELF, + pid_set, + NULL, NULL); + } + } + } + + return wait_for_exit; +} + +int unit_require_mounts_for(Unit *u, const char *path) { + char prefix[strlen(path) + 1], *p; + int r; + + assert(u); + assert(path); + + /* Registers a unit for requiring a certain path and all its + * prefixes. We keep a simple array of these paths in the + * unit, since its usually short. However, we build a prefix + * table for all possible prefixes so that new appearing mount + * units can easily determine which units to make themselves a + * dependency of. */ + + if (!path_is_absolute(path)) + return -EINVAL; + + p = strdup(path); + if (!p) + return -ENOMEM; + + path_kill_slashes(p); + + if (!path_is_safe(p)) { + free(p); + return -EPERM; + } + + if (strv_contains(u->requires_mounts_for, p)) { + free(p); + return 0; + } + + r = strv_consume(&u->requires_mounts_for, p); + if (r < 0) + return r; + + PATH_FOREACH_PREFIX_MORE(prefix, p) { + Set *x; + + x = hashmap_get(u->manager->units_requiring_mounts_for, prefix); + if (!x) { + char *q; + + r = hashmap_ensure_allocated(&u->manager->units_requiring_mounts_for, &string_hash_ops); + if (r < 0) + return r; + + q = strdup(prefix); + if (!q) + return -ENOMEM; + + x = set_new(NULL); + if (!x) { + free(q); + return -ENOMEM; + } + + r = hashmap_put(u->manager->units_requiring_mounts_for, q, x); + if (r < 0) { + free(q); + set_free(x); + return r; + } + } + + r = set_put(x, u); + if (r < 0) + return r; + } + + return 0; +} + +int unit_setup_exec_runtime(Unit *u) { + ExecRuntime **rt; + size_t offset; + Iterator i; + Unit *other; + + offset = UNIT_VTABLE(u)->exec_runtime_offset; + assert(offset > 0); + + /* Check if there already is an ExecRuntime for this unit? */ + rt = (ExecRuntime**) ((uint8_t*) u + offset); + if (*rt) + return 0; + + /* Try to get it from somebody else */ + SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) { + + *rt = unit_get_exec_runtime(other); + if (*rt) { + exec_runtime_ref(*rt); + return 0; + } + } + + return exec_runtime_make(rt, unit_get_exec_context(u), u->id); +} + +bool unit_type_supported(UnitType t) { + if (_unlikely_(t < 0)) + return false; + if (_unlikely_(t >= _UNIT_TYPE_MAX)) + return false; + + if (!unit_vtable[t]->supported) + return true; + + return unit_vtable[t]->supported(); +} + +void unit_warn_if_dir_nonempty(Unit *u, const char* where) { + int r; + + assert(u); + assert(where); + + r = dir_is_empty(where); + if (r > 0) + return; + if (r < 0) { + log_unit_warning_errno(u, r, "Failed to check directory %s: %m", where); + return; + } + + log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + LOG_UNIT_ID(u), + LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), + "WHERE=%s", where, + NULL); +} + +int unit_fail_if_symlink(Unit *u, const char* where) { + int r; + + assert(u); + assert(where); + + r = is_symlink(where); + if (r < 0) { + log_unit_debug_errno(u, r, "Failed to check symlink %s, ignoring: %m", where); + return 0; + } + if (r == 0) + return 0; + + log_struct(LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + LOG_UNIT_ID(u), + LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where), + "WHERE=%s", where, + NULL); + + return -ELOOP; +} + +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 though nothing was + * actually loaded, as those unit types don't require a file + * on disk to validly load. */ + + 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); +} + +pid_t unit_control_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->control_pid) + return UNIT_VTABLE(u)->control_pid(u); + + return 0; +} + +pid_t unit_main_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->main_pid) + return UNIT_VTABLE(u)->main_pid(u); + + return 0; +} diff --git a/src/grp-system/libcore/unit.h b/src/grp-system/libcore/unit.h new file mode 100644 index 0000000000..01123cab00 --- /dev/null +++ b/src/grp-system/libcore/unit.h @@ -0,0 +1,644 @@ +#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 . +***/ + +#include +#include +#include + +#include "basic/list.h" +#include "basic/unit-name.h" +#include "shared/condition.h" +#include "shared/install.h" + +typedef struct Unit Unit; +typedef struct UnitRef UnitRef; +typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; +typedef struct UnitVTable UnitVTable; + +#include "failure-action.h" + +typedef enum KillOperation { + KILL_TERMINATE, + KILL_TERMINATE_AND_LOG, + KILL_KILL, + KILL_ABORT, + _KILL_OPERATION_MAX, + _KILL_OPERATION_INVALID = -1 +} KillOperation; + +static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) { + return t == UNIT_ACTIVE || t == UNIT_RELOADING; +} + +static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) { + return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_RELOADING; +} + +static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) { + return t == UNIT_INACTIVE || t == UNIT_FAILED || t == UNIT_DEACTIVATING; +} + +static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) { + return t == UNIT_INACTIVE || t == UNIT_FAILED; +} + +#include "job.h" + +struct UnitRef { + /* Keeps tracks of references to a unit. This is useful so + * that we can merge two units if necessary and correct all + * references to them */ + + Unit* unit; + LIST_FIELDS(UnitRef, refs); +}; + +struct Unit { + Manager *manager; + + UnitType type; + UnitLoadState load_state; + Unit *merged_into; + + char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ + char *instance; + + Set *names; + Set *dependencies[_UNIT_DEPENDENCY_MAX]; + + char **requires_mounts_for; + + char *description; + char **documentation; + + char *fragment_path; /* if loaded from a config file this is the primary path to it */ + char *source_path; /* if converted, the source file */ + char **dropin_paths; + + usec_t fragment_mtime; + usec_t source_mtime; + usec_t dropin_mtime; + + /* If this is a transient unit we are currently writing, this is where we are writing it to */ + FILE *transient_file; + + /* If there is something to do with this unit, then this is the installed job for it */ + Job *job; + + /* JOB_NOP jobs are special and can be installed without disturbing the real job. */ + Job *nop_job; + + /* The slot used for watching NameOwnerChanged signals */ + sd_bus_slot *match_bus_slot; + + /* Job timeout and action to take */ + usec_t job_timeout; + FailureAction job_timeout_action; + char *job_timeout_reboot_arg; + + /* References to this */ + LIST_HEAD(UnitRef, refs); + + /* Conditions to check */ + LIST_HEAD(Condition, conditions); + LIST_HEAD(Condition, asserts); + + dual_timestamp condition_timestamp; + dual_timestamp assert_timestamp; + + /* Updated whenever the low-level state changes */ + dual_timestamp state_change_timestamp; + + /* Updated whenever the (high-level) active state enters or leaves the active or inactive states */ + dual_timestamp inactive_exit_timestamp; + dual_timestamp active_enter_timestamp; + dual_timestamp active_exit_timestamp; + dual_timestamp inactive_enter_timestamp; + + UnitRef slice; + + /* Per type list */ + LIST_FIELDS(Unit, units_by_type); + + /* All units which have requires_mounts_for set */ + LIST_FIELDS(Unit, has_requires_mounts_for); + + /* Load queue */ + LIST_FIELDS(Unit, load_queue); + + /* D-Bus queue */ + LIST_FIELDS(Unit, dbus_queue); + + /* Cleanup queue */ + LIST_FIELDS(Unit, cleanup_queue); + + /* GC queue */ + LIST_FIELDS(Unit, gc_queue); + + /* CGroup realize members queue */ + LIST_FIELDS(Unit, cgroup_queue); + + /* Units with the same CGroup netclass */ + LIST_FIELDS(Unit, cgroup_netclass); + + /* PIDs we keep an eye on. Note that a unit might have many + * more, but these are the ones we care enough about to + * process SIGCHLD for */ + Set *pids; + + /* Used in sigchld event invocation to avoid repeat events being invoked */ + uint64_t sigchldgen; + + /* Used during GC sweeps */ + unsigned gc_marker; + + /* Error code when we didn't manage to load the unit (negative) */ + int load_error; + + /* Put a ratelimit on unit starting */ + RateLimit start_limit; + FailureAction start_limit_action; + char *reboot_arg; + + /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ + RateLimit auto_stop_ratelimit; + + /* Cached unit file state and preset */ + UnitFileState unit_file_state; + int unit_file_preset; + + /* Where the cpuacct.usage cgroup counter was at the time the unit was started */ + nsec_t cpuacct_usage_base; + + /* Counterparts in the cgroup filesystem */ + char *cgroup_path; + CGroupMask cgroup_realized_mask; + CGroupMask cgroup_enabled_mask; + CGroupMask cgroup_subtree_mask; + CGroupMask cgroup_members_mask; + int cgroup_inotify_wd; + + uint32_t cgroup_netclass_id; + + /* How to start OnFailure units */ + JobMode on_failure_job_mode; + + /* Garbage collect us we nobody wants or requires us anymore */ + bool stop_when_unneeded; + + /* Create default dependencies */ + bool default_dependencies; + + /* Refuse manual starting, allow starting only indirectly via dependency. */ + bool refuse_manual_start; + + /* Don't allow the user to stop this unit manually, allow stopping only indirectly via dependency. */ + bool refuse_manual_stop; + + /* Allow isolation requests */ + bool allow_isolate; + + /* Ignore this unit when isolating */ + bool ignore_on_isolate; + + /* Did the last condition check succeed? */ + bool condition_result; + bool assert_result; + + /* Is this a transient unit? */ + bool transient; + + bool in_load_queue:1; + bool in_dbus_queue:1; + bool in_cleanup_queue:1; + bool in_gc_queue:1; + bool in_cgroup_queue:1; + + bool sent_dbus_new_signal:1; + + bool no_gc:1; + + bool in_audit:1; + + bool cgroup_realized:1; + bool cgroup_members_mask_valid:1; + bool cgroup_subtree_mask_valid:1; + + bool start_limit_hit:1; + + /* Did we already invoke unit_coldplug() for this unit? */ + bool coldplugged:1; +}; + +struct UnitStatusMessageFormats { + const char *starting_stopping[2]; + const char *finished_start_job[_JOB_RESULT_MAX]; + const char *finished_stop_job[_JOB_RESULT_MAX]; +}; + +typedef enum UnitSetPropertiesMode { + UNIT_CHECK = 0, + UNIT_RUNTIME = 1, + UNIT_PERSISTENT = 2, +} UnitSetPropertiesMode; + +#include "automount.h" +#include "busname.h" +#include "device.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 */ + size_t object_size; + + /* If greater than 0, the offset into the object where + * ExecContext is found, if the unit type has that */ + size_t exec_context_offset; + + /* If greater than 0, the offset into the object where + * CGroupContext is found, if the unit type has that */ + size_t cgroup_context_offset; + + /* If greater than 0, the offset into the object where + * KillContext is found, if the unit type has that */ + size_t kill_context_offset; + + /* If greater than 0, the offset into the object where the + * pointer to ExecRuntime is found, if the unit type has + * that */ + size_t exec_runtime_offset; + + /* The name of the configuration file section with the private settings of this unit */ + const char *private_section; + + /* Config file sections this unit type understands, separated + * by NUL chars */ + const char *sections; + + /* This should reset all type-specific variables. This should + * not allocate memory, and is called with zero-initialized + * data. It should hence only initialize variables that need + * to be set != 0. */ + void (*init)(Unit *u); + + /* This should free all type-specific variables. It should be + * idempotent. */ + void (*done)(Unit *u); + + /* Actually load data from disk. This may fail, and should set + * load_state to UNIT_LOADED, UNIT_MERGED or leave it at + * UNIT_STUB if no configuration could be found. */ + int (*load)(Unit *u); + + /* If a lot of units got created via enumerate(), this is + * where to actually set the state and call unit_notify(). */ + int (*coldplug)(Unit *u); + + void (*dump)(Unit *u, FILE *f, const char *prefix); + + int (*start)(Unit *u); + int (*stop)(Unit *u); + int (*reload)(Unit *u); + + int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error); + + bool (*can_reload)(Unit *u); + + /* Write all data that cannot be restored from other sources + * away using unit_serialize_item() */ + int (*serialize)(Unit *u, FILE *f, FDSet *fds); + + /* Restore one item from the serialization */ + 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 */ + 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 */ + UnitActiveState (*active_state)(Unit *u); + + /* Returns the substate specific to this unit type as + * string. This is purely information so that we can give the + * user a more fine grained explanation in which actual state a + * unit is in. */ + const char* (*sub_state_to_string)(Unit *u); + + /* Return true when there is reason to keep this entry around + * even nothing references it and it isn't active in any + * way */ + bool (*check_gc)(Unit *u); + + /* When the unit is not running and no job for it queued we + * shall release its runtime resources */ + void (*release_resources)(Unit *u); + + /* Invoked on every child that died */ + void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); + + /* Reset failed state if we are in failed state */ + void (*reset_failed)(Unit *u); + + /* Called whenever any of the cgroups this unit watches for + * ran empty */ + void (*notify_cgroup_empty)(Unit *u); + + /* Called whenever a process of this unit sends us a message */ + void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds); + + /* Called whenever a name this Unit registered for comes or + * goes away. */ + void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); + + /* Called for each property that is being set */ + int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); + + /* Called after at least one property got changed to apply the necessary change */ + int (*bus_commit_properties)(Unit *u); + + /* Return the unit this unit is following */ + Unit *(*following)(Unit *u); + + /* Return the set of units that are following each other */ + int (*following_set)(Unit *u, Set **s); + + /* Invoked each time a unit this unit is triggering changes + * state or gains/loses a job */ + void (*trigger_notify)(Unit *u, Unit *trigger); + + /* Called whenever CLOCK_REALTIME made a jump */ + void (*time_change)(Unit *u); + + /* Returns the next timeout of a unit */ + int (*get_timeout)(Unit *u, usec_t *timeout); + + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*main_pid)(Unit *u); + + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*control_pid)(Unit *u); + + /* This is called for each unit type and should be used to + * enumerate existing devices and load them. However, + * 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. */ + void (*enumerate)(Manager *m); + + /* Type specific cleanups. */ + void (*shutdown)(Manager *m); + + /* If this function is set and return false all jobs for units + * of this type will immediately fail. */ + bool (*supported)(void); + + /* The bus vtable */ + const sd_bus_vtable *bus_vtable; + + /* The strings to print in status messages */ + UnitStatusMessageFormats status_message_formats; + + /* True if transient units of this type are OK */ + bool can_transient:1; +}; + +extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; + +#define UNIT_VTABLE(u) unit_vtable[(u)->type] + +/* For casting a unit into the various unit types */ +#define DEFINE_CAST(UPPERCASE, MixedCase) \ + static inline MixedCase* UPPERCASE(Unit *u) { \ + if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \ + return NULL; \ + \ + return (MixedCase*) u; \ + } + +/* For casting the various unit types into a unit */ +#define UNIT(u) (&(u)->meta) + +#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) +#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) +#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0) + +#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS])) + +DEFINE_CAST(SERVICE, Service); +DEFINE_CAST(SOCKET, Socket); +DEFINE_CAST(BUSNAME, BusName); +DEFINE_CAST(TARGET, Target); +DEFINE_CAST(DEVICE, Device); +DEFINE_CAST(MOUNT, Mount); +DEFINE_CAST(AUTOMOUNT, Automount); +DEFINE_CAST(SWAP, Swap); +DEFINE_CAST(TIMER, Timer); +DEFINE_CAST(PATH, Path); +DEFINE_CAST(SLICE, Slice); +DEFINE_CAST(SCOPE, Scope); + +Unit *unit_new(Manager *m, size_t size); +void unit_free(Unit *u); + +int unit_add_name(Unit *u, const char *name); + +int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); +int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference); + +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_exec_dependencies(Unit *u, ExecContext *c); + +int unit_choose_id(Unit *u, const char *name); +int unit_set_description(Unit *u, const char *description); + +bool unit_check_gc(Unit *u); + +void unit_add_to_load_queue(Unit *u); +void unit_add_to_dbus_queue(Unit *u); +void unit_add_to_cleanup_queue(Unit *u); +void unit_add_to_gc_queue(Unit *u); + +int unit_merge(Unit *u, Unit *other); +int unit_merge_by_name(Unit *u, const char *other); + +Unit *unit_follow_merge(Unit *u) _pure_; + +int unit_load_fragment_and_dropin(Unit *u); +int unit_load_fragment_and_dropin_optional(Unit *u); +int unit_load(Unit *unit); + +int unit_set_slice(Unit *u, Unit *slice); +int unit_set_default_slice(Unit *u); + +const char *unit_description(Unit *u) _pure_; + +bool unit_has_name(Unit *u, const char *name); + +UnitActiveState unit_active_state(Unit *u); + +const char* unit_sub_state_to_string(Unit *u); + +void unit_dump(Unit *u, FILE *f, const char *prefix); + +bool unit_can_reload(Unit *u) _pure_; +bool unit_can_start(Unit *u) _pure_; +bool unit_can_isolate(Unit *u) _pure_; + +int unit_start(Unit *u); +int unit_stop(Unit *u); +int unit_reload(Unit *u); + +int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error); +int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error); + +void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success); + +int unit_watch_pid(Unit *u, pid_t pid); +void unit_unwatch_pid(Unit *u, pid_t pid); +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(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); + +bool unit_job_is_applicable(Unit *u, JobType j); + +int set_unit_path(const char *p); + +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); +int unit_deserialize(Unit *u, FILE *f, FDSet *fds); + +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); + +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0); +void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t); + +bool unit_need_daemon_reload(Unit *u); + +void unit_reset_failed(Unit *u); + +Unit *unit_following(Unit *u); +int unit_following_set(Unit *u, Set **s); + +const char *unit_slice_name(Unit *u); + +bool unit_stop_pending(Unit *u) _pure_; +bool unit_inactive_or_pending(Unit *u) _pure_; +bool unit_active_or_pending(Unit *u); + +int unit_add_default_target_dependency(Unit *u, Unit *target); + +void unit_start_on_failure(Unit *u); +void unit_trigger_notify(Unit *u); + +UnitFileState unit_get_unit_file_state(Unit *u); +int unit_get_unit_file_preset(Unit *u); + +Unit* unit_ref_set(UnitRef *ref, Unit *u); +void unit_ref_unset(UnitRef *ref); + +#define UNIT_DEREF(ref) ((ref).unit) +#define UNIT_ISSET(ref) (!!(ref).unit) + +int unit_patch_contexts(Unit *u); + +ExecContext *unit_get_exec_context(Unit *u) _pure_; +KillContext *unit_get_kill_context(Unit *u) _pure_; +CGroupContext *unit_get_cgroup_context(Unit *u) _pure_; + +ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_; + +int unit_setup_exec_runtime(Unit *u); + +int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data); +int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5); + +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_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); + +int unit_require_mounts_for(Unit *u, const char *path); + +bool unit_type_supported(UnitType t); + +bool unit_is_pristine(Unit *u); + +pid_t unit_control_pid(Unit *u); +pid_t unit_main_pid(Unit *u); + +static inline bool unit_supported(Unit *u) { + return unit_type_supported(u->type); +} + +void unit_warn_if_dir_nonempty(Unit *u, const char* where); +int unit_fail_if_symlink(Unit *u, const char* where); + +int unit_start_limit_test(Unit *u); + +/* Macros which append UNIT= or USER_UNIT= to the message */ + +#define log_unit_full(unit, level, error, ...) \ + ({ \ + Unit *_u = (unit); \ + _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \ + log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + }) + +#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, 0, ##__VA_ARGS__) +#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_unit_debug_errno(unit, error, ...) log_unit_full(unit, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_unit_info_errno(unit, error, ...) log_unit_full(unit, LOG_INFO, error, ##__VA_ARGS__) +#define log_unit_notice_errno(unit, error, ...) log_unit_full(unit, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_unit_warning_errno(unit, error, ...) log_unit_full(unit, LOG_WARNING, error, ##__VA_ARGS__) +#define log_unit_error_errno(unit, error, ...) log_unit_full(unit, LOG_ERR, error, ##__VA_ARGS__) + +#define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__ +#define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id diff --git a/src/grp-system/systemctl/.gitignore b/src/grp-system/systemctl/.gitignore new file mode 100644 index 0000000000..ebd59d3c9e --- /dev/null +++ b/src/grp-system/systemctl/.gitignore @@ -0,0 +1,2 @@ +/systemctl +/_systemctl diff --git a/src/grp-system/systemctl/Makefile b/src/grp-system/systemctl/Makefile new file mode 100644 index 0000000000..3551118514 --- /dev/null +++ b/src/grp-system/systemctl/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemctl +systemctl_SOURCES = \ + src/systemctl/systemctl.c + +systemctl_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/systemctl/halt.xml b/src/grp-system/systemctl/halt.xml new file mode 100644 index 0000000000..e3fa60a915 --- /dev/null +++ b/src/grp-system/systemctl/halt.xml @@ -0,0 +1,176 @@ + + + + + + + + + halt + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + halt + 8 + + + + halt + poweroff + reboot + Halt, power-off or reboot the machine + + + + + halt + OPTIONS + + + poweroff + OPTIONS + + + reboot + OPTIONS + + + + + Description + + halt, poweroff, + reboot may be used to halt, power-off or reboot + the machine. + + + + + Options + + The following options are understood: + + + + + + + + + + + + Halt the machine, regardless of which one of + the three commands is invoked. + + + + + + + Power-off the machine, regardless of which one + of the three commands is invoked. + + + + + + Reboot the machine, regardless of which one of + the three commands is invoked. + + + + + + + Force immediate halt, power-off, reboot. Do + not contact the init system. + + + + + + + Only write wtmp shutdown entry, do not + actually halt, power-off, reboot. + + + + + + + Do not write wtmp shutdown + entry. + + + + + + + Don't sync hard disks/storage media before + halt, power-off, reboot. + + + + + + Do not send wall message before halt, + power-off, reboot. + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + Notes + + These are legacy commands available for compatibility + only. + + + + See Also + + systemd1, + systemctl1, + shutdown8, + wall1 + + + + diff --git a/src/grp-system/systemctl/runlevel.xml b/src/grp-system/systemctl/runlevel.xml new file mode 100644 index 0000000000..ca29c7c22c --- /dev/null +++ b/src/grp-system/systemctl/runlevel.xml @@ -0,0 +1,192 @@ + + + + + + + + + runlevel + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + runlevel + 8 + + + + runlevel + Print previous and current SysV runlevel + + + + + runlevel + options + + + + + Overview + + "Runlevels" are an obsolete way to start and stop groups of + services used in SysV init. systemd provides a compatibility layer + that maps runlevels to targets, and associated binaries like + runlevel. Nevertheless, only one runlevel can + be "active" at a given time, while systemd can activate multiple + targets concurrently, so the mapping to runlevels is confusing + and only approximate. Runlevels should not be used in new code, + and are mostly useful as a shorthand way to refer the matching + systemd targets in kernel boot parameters. + + + Mapping between runlevels and systemd targets + + + + + + Runlevel + Target + + + + + 0 + poweroff.target + + + 1 + rescue.target + + + 2, 3, 4 + multi-user.target + + + 5 + graphical.target + + + 6 + reboot.target + + + +
+
+ + + Description + + runlevel prints the previous and current + SysV runlevel if they are known. + + The two runlevel characters are separated by a single space + character. If a runlevel cannot be determined, N is printed + instead. If neither can be determined, the word "unknown" is + printed. + + Unless overridden in the environment, this will check the + utmp database for recent runlevel changes. + + + + Options + + The following option is understood: + + + + + + + + + + + + + Exit status + + If one or both runlevels could be determined, 0 is returned, + a non-zero failure code otherwise. + + + + + Environment + + + + $RUNLEVEL + + If $RUNLEVEL is set, + runlevel will print this value as current + runlevel and ignore utmp. + + + + $PREVLEVEL + + If $PREVLEVEL is set, + runlevel will print this value as previous + runlevel and ignore utmp. + + + + + + Files + + + + /var/run/utmp + + The utmp database runlevel + reads the previous and current runlevel + from. + + + + + + See Also + + systemd1, + systemd.target5, + systemctl1 + + + +
diff --git a/src/grp-system/systemctl/shutdown.xml b/src/grp-system/systemctl/shutdown.xml new file mode 100644 index 0000000000..a8af387c67 --- /dev/null +++ b/src/grp-system/systemctl/shutdown.xml @@ -0,0 +1,175 @@ + + + + + + + + + shutdown + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + shutdown + 8 + + + + shutdown + Halt, power-off or reboot the machine + + + + + shutdown + OPTIONS + TIME + WALL + + + + + Description + + shutdown may be used to halt, power-off + or reboot the machine. + + The first argument may be a time string (which is usually + now). Optionally, this may be followed by a + wall message to be sent to all logged-in users before going + down. + + The time string may either be in the format + hh:mm for hour/minutes specifying the time to + execute the shutdown at, specified in 24h clock format. + Alternatively it may be in the syntax +m + referring to the specified number of minutes m from now. + now is an alias for +0, i.e. + for triggering an immediate shutdown. If no time argument is + specified, +1 is implied. + + Note that to specify a wall message you must specify a time + argument, too. + + If the time argument is used, 5 minutes before the system + goes down the /run/nologin file is created to + ensure that further logins shall not be allowed. + + + + Options + + The following options are understood: + + + + + + + + + + + + + Halt the machine. + + + + + + + Power-off the machine (the + default). + + + + + + + Reboot the + machine. + + + + + + Equivalent to , + unless is specified. + + + + + + Do not halt, power-off, reboot, just write + wall message. + + + + + + Do not send wall + message before + halt, power-off, reboot. + + + + + + Cancel a pending shutdown. This may be used + cancel the effect of an invocation of + shutdown with a time argument that is not + +0 or + now. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemctl1, + halt8, + wall1 + + + + diff --git a/src/grp-system/systemctl/systemctl.c b/src/grp-system/systemctl/systemctl.c new file mode 100644 index 0000000000..0f115db14c --- /dev/null +++ b/src/grp-system/systemctl/systemctl.c @@ -0,0 +1,8076 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2013 Marc-Antoine Perennou + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/copy.h" +#include "basic/env-util.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/list.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rlimit-util.h" +#include "basic/set.h" +#include "basic/sigbus.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "basic/verbs.h" +#include "basic/virt.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "sd-bus/bus-message.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" +#include "shared/cgroup-show.h" +#include "shared/dropin.h" +#include "shared/efivars.h" +#include "shared/initreq.h" +#include "shared/install.h" +#include "shared/logs-show.h" +#include "shared/pager.h" +#include "shared/path-lookup.h" +#include "shared/spawn-ask-password-agent.h" +#include "shared/spawn-polkit-agent.h" +#include "shared/utmp-wtmp.h" + +/* The init script exit status codes + 0 program is running or service is OK + 1 program is dead and /var/run pid file exists + 2 program is dead and /var/lock lock file exists + 3 program is not running + 4 program or service status is unknown + 5-99 reserved for future LSB use + 100-149 reserved for distribution use + 150-199 reserved for application use + 200-254 reserved +*/ +enum { + EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, + EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, + EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, + EXIT_PROGRAM_NOT_RUNNING = 3, + EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, +}; + +static char **arg_types = NULL; +static char **arg_states = NULL; +static char **arg_properties = NULL; +static bool arg_all = false; +static enum dependency { + DEPENDENCY_FORWARD, + DEPENDENCY_REVERSE, + DEPENDENCY_AFTER, + DEPENDENCY_BEFORE, + _DEPENDENCY_MAX +} arg_dependency = DEPENDENCY_FORWARD; +static const char *arg_job_mode = "replace"; +static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +static bool arg_no_block = false; +static bool arg_no_legend = false; +static bool arg_no_pager = false; +static bool arg_no_wtmp = false; +static bool arg_no_sync = false; +static bool arg_no_wall = false; +static bool arg_no_reload = false; +static bool arg_value = false; +static bool arg_show_types = false; +static bool arg_ignore_inhibitors = false; +static bool arg_dry = false; +static bool arg_quiet = false; +static bool arg_full = false; +static bool arg_recursive = false; +static int arg_force = 0; +static bool arg_ask_password = false; +static bool arg_runtime = false; +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 char *arg_root = NULL; +static usec_t arg_when = 0; +static enum action { + _ACTION_INVALID, + ACTION_SYSTEMCTL, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_KEXEC, + ACTION_EXIT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, + ACTION_RUNLEVEL2, + ACTION_RUNLEVEL3, + ACTION_RUNLEVEL4, + ACTION_RUNLEVEL5, + ACTION_RESCUE, + ACTION_EMERGENCY, + ACTION_DEFAULT, + ACTION_RELOAD, + ACTION_REEXEC, + ACTION_RUNLEVEL, + ACTION_CANCEL_SHUTDOWN, + _ACTION_MAX +} arg_action = ACTION_SYSTEMCTL; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +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 int daemon_reload(int argc, char *argv[], void* userdata); +static int trivial_method(int argc, char *argv[], void *userdata); +static int halt_now(enum action a); +static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state); + +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 int map_string_no_copy(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char *s; + const char **p = userdata; + int r; + + r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &s); + if (r < 0) + return r; + + if (!isempty(s)) + *p = s; + + return 0; +} + +static void ask_password_agent_open_if_enabled(void) { + + /* Open the password agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + if (arg_scope != UNIT_FILE_SYSTEM) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + ask_password_agent_open(); +} + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + if (arg_scope != UNIT_FILE_SYSTEM) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +static OutputFlags get_output_flags(void) { + return + arg_all * OUTPUT_SHOW_ALL | + arg_full * OUTPUT_FULL_WIDTH | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + colors_enabled() * OUTPUT_COLOR | + !arg_quiet * OUTPUT_WARN_CUTOFF; +} + +static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) { + assert(error); + + if (!sd_bus_error_is_set(error)) + return r; + + if (sd_bus_error_has_name(error, SD_BUS_ERROR_ACCESS_DENIED) || + sd_bus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) || + sd_bus_error_has_name(error, BUS_ERROR_NO_ISOLATION) || + sd_bus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE)) + return EXIT_NOPERMISSION; + + if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT)) + return EXIT_NOTINSTALLED; + + if (sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) || + sd_bus_error_has_name(error, SD_BUS_ERROR_NOT_SUPPORTED)) + return EXIT_NOTIMPLEMENTED; + + if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) + return EXIT_NOTCONFIGURED; + + if (r != 0) + return r; + + return EXIT_FAILURE; +} + +static bool install_client_side(void) { + + /* Decides when to execute enable/disable/... operations + * client-side rather than server-side. */ + + if (running_in_chroot() > 0) + return true; + + if (sd_booted() <= 0) + return true; + + if (!isempty(arg_root)) + return true; + + 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; +} + +static int compare_unit_info(const void *a, const void *b) { + const UnitInfo *u = a, *v = b; + const char *d1, *d2; + int r; + + /* First, order by machine */ + if (!u->machine && v->machine) + return -1; + if (u->machine && !v->machine) + return 1; + if (u->machine && v->machine) { + r = strcasecmp(u->machine, v->machine); + if (r != 0) + return r; + } + + /* Second, order by unit type */ + d1 = strrchr(u->id, '.'); + d2 = strrchr(v->id, '.'); + if (d1 && d2) { + r = strcasecmp(d1, d2); + if (r != 0) + return r; + } + + /* Third, order by name */ + return strcasecmp(u->id, v->id); +} + +static bool output_show_unit(const UnitInfo *u, char **patterns) { + assert(u); + + if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) + return false; + + if (arg_types) { + const char *dot; + + dot = strrchr(u->id, '.'); + if (!dot) + return false; + + if (!strv_find(arg_types, dot+1)) + return false; + } + + if (arg_all) + return true; + + /* Note that '--all' is not purely a state filter, but also a + * filter that hides units that "follow" other units (which is + * used for device units that appear under different names). */ + if (!isempty(u->following)) + return false; + + if (!strv_isempty(arg_states)) + return true; + + /* By default show all units except the ones in inactive + * state and with no pending job */ + if (u->job_id > 0) + return true; + + if (streq(u->active_state, "inactive")) + return false; + + return true; +} + +static int output_units_list(const UnitInfo *unit_infos, unsigned c) { + unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len; + const UnitInfo *u; + unsigned n_shown = 0; + int job_count = 0; + + max_id_len = strlen("UNIT"); + load_len = strlen("LOAD"); + active_len = strlen("ACTIVE"); + sub_len = strlen("SUB"); + job_len = strlen("JOB"); + desc_len = 0; + + for (u = unit_infos; u < unit_infos + c; u++) { + max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0)); + load_len = MAX(load_len, strlen(u->load_state)); + active_len = MAX(active_len, strlen(u->active_state)); + sub_len = MAX(sub_len, strlen(u->sub_state)); + + if (u->job_id != 0) { + job_len = MAX(job_len, strlen(u->job_type)); + job_count++; + } + + if (!arg_no_legend && + (streq(u->active_state, "failed") || + STR_IN_SET(u->load_state, "error", "not-found", "masked"))) + circle_len = 2; + } + + if (!arg_full && original_stdout_is_tty) { + unsigned basic_len; + + id_len = MIN(max_id_len, 25u); + basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len; + + if (job_count) + basic_len += job_len + 1; + + if (basic_len < (unsigned) columns()) { + unsigned extra_len, incr; + extra_len = columns() - basic_len; + + /* Either UNIT already got 25, or is fully satisfied. + * Grant up to 25 to DESC now. */ + incr = MIN(extra_len, 25u); + desc_len += incr; + extra_len -= incr; + + /* split the remaining space between UNIT and DESC, + * but do not give UNIT more than it needs. */ + if (extra_len > 0) { + incr = MIN(extra_len / 2, max_id_len - id_len); + id_len += incr; + desc_len += extra_len - incr; + } + } + } else + id_len = max_id_len; + + for (u = unit_infos; u < unit_infos + c; u++) { + _cleanup_free_ char *e = NULL, *j = NULL; + const char *on_loaded = "", *off_loaded = ""; + const char *on_active = "", *off_active = ""; + const char *on_circle = "", *off_circle = ""; + const char *id; + bool circle = false; + + if (!n_shown && !arg_no_legend) { + + if (circle_len > 0) + fputs(" ", stdout); + + printf("%-*s %-*s %-*s %-*s ", + id_len, "UNIT", + load_len, "LOAD", + active_len, "ACTIVE", + sub_len, "SUB"); + + if (job_count) + printf("%-*s ", job_len, "JOB"); + + if (!arg_full && arg_no_pager) + printf("%.*s\n", desc_len, "DESCRIPTION"); + else + printf("%s\n", "DESCRIPTION"); + } + + n_shown++; + + if (STR_IN_SET(u->load_state, "error", "not-found", "masked") && !arg_plain) { + on_loaded = ansi_highlight_red(); + on_circle = ansi_highlight_yellow(); + off_loaded = off_circle = ansi_normal(); + circle = true; + } else if (streq(u->active_state, "failed") && !arg_plain) { + on_circle = on_active = ansi_highlight_red(); + off_circle = off_active = ansi_normal(); + circle = true; + } + + if (u->machine) { + j = strjoin(u->machine, ":", u->id, NULL); + if (!j) + return log_oom(); + + id = j; + } else + id = u->id; + + if (arg_full) { + e = ellipsize(id, id_len, 33); + if (!e) + return log_oom(); + + id = e; + } + + if (circle_len > 0) + printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle); + + printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", + on_active, id_len, id, off_active, + on_loaded, load_len, u->load_state, off_loaded, + on_active, active_len, u->active_state, + sub_len, u->sub_state, off_active, + job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); + + if (desc_len > 0) + printf("%.*s\n", desc_len, u->description); + else + printf("%s\n", u->description); + } + + if (!arg_no_legend) { + const char *on, *off; + + if (n_shown) { + puts("\n" + "LOAD = Reflects whether the unit definition was properly loaded.\n" + "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" + "SUB = The low-level unit activation state, values depend on unit type."); + puts(job_count ? "JOB = Pending job for the unit.\n" : ""); + on = ansi_highlight(); + off = ansi_normal(); + } else { + on = ansi_highlight_red(); + off = ansi_normal(); + } + + if (arg_all) + printf("%s%u loaded units listed.%s\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); + else + printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); + } + + return 0; +} + +static int get_unit_list( + sd_bus *bus, + const char *machine, + char **patterns, + UnitInfo **unit_infos, + int c, + sd_bus_message **_reply) { + + _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; + bool fallback = false; + + assert(bus); + assert(unit_infos); + assert(_reply); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitsByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, patterns); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || + sd_bus_error_has_name(&error, SD_BUS_ERROR_ACCESS_DENIED))) { + /* Fallback to legacy ListUnitsFiltered method */ + fallback = true; + log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitsFiltered"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } + if (r < 0) + return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + u.machine = machine; + + if (!output_show_unit(&u, fallback ? patterns : NULL)) + continue; + + if (!GREEDY_REALLOC(*unit_infos, size, c+1)) + return log_oom(); + + (*unit_infos)[c++] = u; + } + 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); + + *_reply = reply; + reply = NULL; + + return c; +} + +static void message_set_freep(Set **set) { + sd_bus_message *m; + + while ((m = set_steal_first(*set))) + sd_bus_message_unref(m); + + set_free(*set); +} + +static int get_unit_list_recursive( + sd_bus *bus, + char **patterns, + UnitInfo **_unit_infos, + Set **_replies, + char ***_machines) { + + _cleanup_free_ UnitInfo *unit_infos = NULL; + _cleanup_(message_set_freep) Set *replies; + sd_bus_message *reply; + int c, r; + + assert(bus); + assert(_replies); + assert(_unit_infos); + assert(_machines); + + replies = set_new(NULL); + if (!replies) + return log_oom(); + + c = get_unit_list(bus, NULL, patterns, &unit_infos, 0, &reply); + if (c < 0) + return c; + + r = set_put(replies, reply); + if (r < 0) { + sd_bus_message_unref(reply); + return log_oom(); + } + + if (arg_recursive) { + _cleanup_strv_free_ char **machines = NULL; + char **i; + + r = sd_get_machine_names(&machines); + if (r < 0) + return log_error_errno(r, "Failed to get machine names: %m"); + + STRV_FOREACH(i, machines) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL; + int k; + + r = sd_bus_open_system_machine(&container, *i); + if (r < 0) { + log_warning_errno(r, "Failed to connect to container %s, ignoring: %m", *i); + continue; + } + + k = get_unit_list(container, *i, patterns, &unit_infos, c, &reply); + if (k < 0) + return k; + + c = k; + + r = set_put(replies, reply); + if (r < 0) { + sd_bus_message_unref(reply); + return log_oom(); + } + } + + *_machines = machines; + machines = NULL; + } else + *_machines = NULL; + + *_unit_infos = unit_infos; + unit_infos = NULL; + + *_replies = replies; + replies = NULL; + + return c; +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); + if (r < 0) + return r; + + qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info); + return output_units_list(unit_infos, r); +} + +static int get_triggered_units( + sd_bus *bus, + const char* path, + char*** ret) { + + _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", + path, + "org.freedesktop.systemd1.Unit", + "Triggers", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to determine triggers: %s", bus_error_message(&error, r)); + + return 0; +} + +static int get_listening( + sd_bus *bus, + const char* unit_path, + char*** listening) { + + _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; + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.systemd1.Socket", + "Listen", + &error, + &reply, + "a(ss)"); + 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) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ss)", &type, &path)) > 0) { + + r = strv_extend(listening, type); + if (r < 0) + return log_oom(); + + r = strv_extend(listening, path); + if (r < 0) + return log_oom(); + + n++; + } + 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); + + return n; +} + +struct socket_info { + const char *machine; + const char* id; + + char* type; + char* path; + + /* Note: triggered is a list here, although it almost certainly + * will always be one unit. Nevertheless, dbus API allows for multiple + * values, so let's follow that. */ + char** triggered; + + /* The strv above is shared. free is set only in the first one. */ + bool own_triggered; +}; + +static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) { + int o; + + assert(a); + assert(b); + + if (!a->machine && b->machine) + return -1; + if (a->machine && !b->machine) + return 1; + if (a->machine && b->machine) { + o = strcasecmp(a->machine, b->machine); + if (o != 0) + return o; + } + + o = strcmp(a->path, b->path); + if (o == 0) + o = strcmp(a->type, b->type); + + return o; +} + +static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { + struct socket_info *s; + unsigned pathlen = strlen("LISTEN"), + typelen = strlen("TYPE") * arg_show_types, + socklen = strlen("UNIT"), + servlen = strlen("ACTIVATES"); + const char *on, *off; + + for (s = socket_infos; s < socket_infos + cs; s++) { + unsigned tmp = 0; + char **a; + + socklen = MAX(socklen, strlen(s->id)); + if (arg_show_types) + typelen = MAX(typelen, strlen(s->type)); + pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0)); + + STRV_FOREACH(a, s->triggered) + tmp += strlen(*a) + 2*(a != s->triggered); + servlen = MAX(servlen, tmp); + } + + if (cs) { + if (!arg_no_legend) + printf("%-*s %-*.*s%-*s %s\n", + pathlen, "LISTEN", + typelen + arg_show_types, typelen + arg_show_types, "TYPE ", + socklen, "UNIT", + "ACTIVATES"); + + for (s = socket_infos; s < socket_infos + cs; s++) { + _cleanup_free_ char *j = NULL; + const char *path; + char **a; + + if (s->machine) { + j = strjoin(s->machine, ":", s->path, NULL); + if (!j) + return log_oom(); + path = j; + } else + path = s->path; + + if (arg_show_types) + printf("%-*s %-*s %-*s", + pathlen, path, typelen, s->type, socklen, s->id); + else + printf("%-*s %-*s", + pathlen, path, socklen, s->id); + STRV_FOREACH(a, s->triggered) + printf("%s %s", + a == s->triggered ? "" : ",", *a); + printf("\n"); + } + + on = ansi_highlight(); + off = ansi_normal(); + if (!arg_no_legend) + printf("\n"); + } else { + on = ansi_highlight_red(); + off = ansi_normal(); + } + + if (!arg_no_legend) { + printf("%s%u sockets listed.%s\n", on, cs, off); + if (!arg_all) + printf("Pass --all to see loaded but inactive sockets, too.\n"); + } + + return 0; +} + +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; + _cleanup_free_ struct socket_info *socket_infos = NULL; + const UnitInfo *u; + struct socket_info *s; + unsigned cs = 0; + size_t size = 0; + int r = 0, n; + sd_bus *bus; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); + if (n < 0) + return n; + + for (u = unit_infos; u < unit_infos + n; u++) { + _cleanup_strv_free_ char **listening = NULL, **triggered = NULL; + int i, c; + + if (!endswith(u->id, ".socket")) + continue; + + r = get_triggered_units(bus, u->unit_path, &triggered); + if (r < 0) + goto cleanup; + + c = get_listening(bus, u->unit_path, &listening); + if (c < 0) { + r = c; + goto cleanup; + } + + if (!GREEDY_REALLOC(socket_infos, size, cs + c)) { + r = log_oom(); + goto cleanup; + } + + for (i = 0; i < c; i++) + socket_infos[cs + i] = (struct socket_info) { + .machine = u->machine, + .id = u->id, + .type = listening[i*2], + .path = listening[i*2 + 1], + .triggered = triggered, + .own_triggered = i==0, + }; + + /* from this point on we will cleanup those socket_infos */ + cs += c; + free(listening); + listening = triggered = NULL; /* avoid cleanup */ + } + + qsort_safe(socket_infos, cs, sizeof(struct socket_info), + (__compar_fn_t) socket_info_compare); + + output_sockets_list(socket_infos, cs); + + cleanup: + assert(cs == 0 || socket_infos); + for (s = socket_infos; s < socket_infos + cs; s++) { + free(s->type); + free(s->path); + if (s->own_triggered) + strv_free(s->triggered); + } + + return r; +} + +static int get_next_elapse( + sd_bus *bus, + const char *path, + dual_timestamp *next) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + dual_timestamp t; + int r; + + assert(bus); + assert(path); + assert(next); + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "NextElapseUSecMonotonic", + &error, + 't', + &t.monotonic); + 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, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "NextElapseUSecRealtime", + &error, + 't', + &t.realtime); + if (r < 0) + return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); + + *next = t; + return 0; +} + +static int get_last_trigger( + sd_bus *bus, + const char *path, + usec_t *last) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(path); + assert(last); + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Timer", + "LastTriggerUSec", + &error, + 't', + last); + if (r < 0) + return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r)); + + return 0; +} + +struct timer_info { + const char* machine; + const char* id; + usec_t next_elapse; + usec_t last_trigger; + char** triggered; +}; + +static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) { + int o; + + assert(a); + assert(b); + + if (!a->machine && b->machine) + return -1; + if (a->machine && !b->machine) + return 1; + if (a->machine && b->machine) { + o = strcasecmp(a->machine, b->machine); + if (o != 0) + return o; + } + + if (a->next_elapse < b->next_elapse) + return -1; + if (a->next_elapse > b->next_elapse) + return 1; + + return strcmp(a->id, b->id); +} + +static int output_timers_list(struct timer_info *timer_infos, unsigned n) { + struct timer_info *t; + unsigned + nextlen = strlen("NEXT"), + leftlen = strlen("LEFT"), + lastlen = strlen("LAST"), + passedlen = strlen("PASSED"), + unitlen = strlen("UNIT"), + activatelen = strlen("ACTIVATES"); + + const char *on, *off; + + assert(timer_infos || n == 0); + + for (t = timer_infos; t < timer_infos + n; t++) { + unsigned ul = 0; + char **a; + + if (t->next_elapse > 0) { + char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = ""; + + format_timestamp(tstamp, sizeof(tstamp), t->next_elapse); + nextlen = MAX(nextlen, strlen(tstamp) + 1); + + format_timestamp_relative(trel, sizeof(trel), t->next_elapse); + leftlen = MAX(leftlen, strlen(trel)); + } + + if (t->last_trigger > 0) { + char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = ""; + + format_timestamp(tstamp, sizeof(tstamp), t->last_trigger); + lastlen = MAX(lastlen, strlen(tstamp) + 1); + + format_timestamp_relative(trel, sizeof(trel), t->last_trigger); + passedlen = MAX(passedlen, strlen(trel)); + } + + unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0)); + + STRV_FOREACH(a, t->triggered) + ul += strlen(*a) + 2*(a != t->triggered); + + activatelen = MAX(activatelen, ul); + } + + if (n > 0) { + if (!arg_no_legend) + printf("%-*s %-*s %-*s %-*s %-*s %s\n", + nextlen, "NEXT", + leftlen, "LEFT", + lastlen, "LAST", + passedlen, "PASSED", + unitlen, "UNIT", + "ACTIVATES"); + + for (t = timer_infos; t < timer_infos + n; t++) { + _cleanup_free_ char *j = NULL; + const char *unit; + char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a"; + char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a"; + char **a; + + format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse); + format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse); + + format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger); + format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger); + + if (t->machine) { + j = strjoin(t->machine, ":", t->id, NULL); + if (!j) + return log_oom(); + unit = j; + } else + unit = t->id; + + printf("%-*s %-*s %-*s %-*s %-*s", + nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit); + + STRV_FOREACH(a, t->triggered) + printf("%s %s", + a == t->triggered ? "" : ",", *a); + printf("\n"); + } + + on = ansi_highlight(); + off = ansi_normal(); + if (!arg_no_legend) + printf("\n"); + } else { + on = ansi_highlight_red(); + off = ansi_normal(); + } + + if (!arg_no_legend) { + printf("%s%u timers listed.%s\n", on, n, off); + if (!arg_all) + printf("Pass --all to see loaded but inactive timers, too.\n"); + } + + return 0; +} + +static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) { + usec_t next_elapse; + + assert(nw); + assert(next); + + if (next->monotonic != USEC_INFINITY && next->monotonic > 0) { + usec_t converted; + + if (next->monotonic > nw->monotonic) + converted = nw->realtime + (next->monotonic - nw->monotonic); + else + converted = nw->realtime - (nw->monotonic - next->monotonic); + + if (next->realtime != USEC_INFINITY && next->realtime > 0) + next_elapse = MIN(converted, next->realtime); + else + next_elapse = converted; + + } else + next_elapse = next->realtime; + + return next_elapse; +} + +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; + _cleanup_free_ UnitInfo *unit_infos = NULL; + struct timer_info *t; + const UnitInfo *u; + size_t size = 0; + int n, c = 0; + dual_timestamp nw; + sd_bus *bus; + int r = 0; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); + if (n < 0) + return n; + + dual_timestamp_get(&nw); + + for (u = unit_infos; u < unit_infos + n; u++) { + _cleanup_strv_free_ char **triggered = NULL; + dual_timestamp next = DUAL_TIMESTAMP_NULL; + usec_t m, last = 0; + + if (!endswith(u->id, ".timer")) + continue; + + r = get_triggered_units(bus, u->unit_path, &triggered); + if (r < 0) + goto cleanup; + + r = get_next_elapse(bus, u->unit_path, &next); + if (r < 0) + goto cleanup; + + get_last_trigger(bus, u->unit_path, &last); + + if (!GREEDY_REALLOC(timer_infos, size, c+1)) { + r = log_oom(); + goto cleanup; + } + + m = calc_next_elapse(&nw, &next); + + timer_infos[c++] = (struct timer_info) { + .machine = u->machine, + .id = u->id, + .next_elapse = m, + .last_trigger = last, + .triggered = triggered, + }; + + triggered = NULL; /* avoid cleanup */ + } + + qsort_safe(timer_infos, c, sizeof(struct timer_info), + (__compar_fn_t) timer_info_compare); + + output_timers_list(timer_infos, c); + + cleanup: + for (t = timer_infos; t < timer_infos + c; t++) + strv_free(t->triggered); + + return r; +} + +static int compare_unit_file_list(const void *a, const void *b) { + const char *d1, *d2; + const UnitFileList *u = a, *v = b; + + d1 = strrchr(u->path, '.'); + d2 = strrchr(v->path, '.'); + + if (d1 && d2) { + int r; + + r = strcasecmp(d1, d2); + if (r != 0) + return r; + } + + return strcasecmp(basename(u->path), basename(v->path)); +} + +static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) { + assert(u); + + if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE)) + return false; + + if (!strv_isempty(arg_types)) { + const char *dot; + + dot = strrchr(u->path, '.'); + if (!dot) + return false; + + if (!strv_find(arg_types, dot+1)) + return false; + } + + if (!strv_isempty(states) && + !strv_find(states, unit_file_state_to_string(u->state))) + return false; + + return true; +} + +static void output_unit_file_list(const UnitFileList *units, unsigned c) { + unsigned max_id_len, id_cols, state_cols; + const UnitFileList *u; + + max_id_len = strlen("UNIT FILE"); + state_cols = strlen("STATE"); + + for (u = units; u < units + c; u++) { + max_id_len = MAX(max_id_len, strlen(basename(u->path))); + state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state))); + } + + if (!arg_full) { + unsigned basic_cols; + + id_cols = MIN(max_id_len, 25u); + basic_cols = 1 + id_cols + state_cols; + if (basic_cols < (unsigned) columns()) + id_cols += MIN(columns() - basic_cols, max_id_len - id_cols); + } else + id_cols = max_id_len; + + if (!arg_no_legend && c > 0) + printf("%-*s %-*s\n", + id_cols, "UNIT FILE", + state_cols, "STATE"); + + for (u = units; u < units + c; u++) { + _cleanup_free_ char *e = NULL; + const char *on, *off; + const char *id; + + if (IN_SET(u->state, + UNIT_FILE_MASKED, + UNIT_FILE_MASKED_RUNTIME, + UNIT_FILE_DISABLED, + UNIT_FILE_BAD)) { + on = ansi_highlight_red(); + off = ansi_normal(); + } else if (u->state == UNIT_FILE_ENABLED) { + on = ansi_highlight_green(); + off = ansi_normal(); + } else + on = off = ""; + + id = basename(u->path); + + e = arg_full ? NULL : ellipsize(id, id_cols, 33); + + printf("%-*s %s%-*s%s\n", + id_cols, e ? e : id, + on, state_cols, unit_file_state_to_string(u->state), off); + } + + if (!arg_no_legend) + printf("\n%u unit files listed.\n", c); +} + +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; + unsigned c = 0; + const char *state; + char *path; + int r; + bool fallback = false; + + if (install_client_side()) { + Hashmap *h; + UnitFileList *u; + Iterator i; + unsigned n_units; + + h = hashmap_new(&string_hash_ops); + if (!h) + return log_oom(); + + r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1)); + if (r < 0) { + unit_file_list_free(h); + return log_error_errno(r, "Failed to get unit file list: %m"); + } + + n_units = hashmap_size(h); + + units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */ + if (!units) { + unit_file_list_free(h); + return log_oom(); + } + + HASHMAP_FOREACH(u, h, i) { + if (!output_show_unit_file(u, NULL, NULL)) + continue; + + units[c++] = *u; + free(u); + } + + assert(c <= n_units); + hashmap_free(h); + + r = 0; + } else { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = 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_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitFilesByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + 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, &reply); + if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + /* Fallback to legacy ListUnitFiles method */ + fallback = true; + log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitFiles"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } + 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) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) { + + if (!GREEDY_REALLOC(units, size, c + 1)) + return log_oom(); + + units[c] = (struct UnitFileList) { + path, + unit_file_state_from_string(state) + }; + + if (output_show_unit_file(&units[c], + fallback ? arg_states : NULL, + fallback ? strv_skip(argv, 1) : NULL)) + 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); + } + + pager_open(arg_no_pager, false); + + qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list); + output_unit_file_list(units, c); + + if (install_client_side()) + for (unit = units; unit < units + c; unit++) + free(unit->path); + + return 0; +} + +static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) { + _cleanup_free_ char *n = NULL; + size_t max_len = MAX(columns(),20u); + size_t len = 0; + int i; + + if (!arg_plain) { + + for (i = level - 1; i >= 0; i--) { + len += 2; + if (len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + printf("%s", special_glyph(branches & (1 << i) ? TREE_VERTICAL : TREE_SPACE)); + } + len += 2; + + if (len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); + } + + if (arg_full) { + printf("%s\n", name); + return 0; + } + + n = ellipsize(name, max_len-len, 100); + if (!n) + return log_oom(); + + printf("%s\n", n); + return 0; +} + +static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { + + static const char *dependencies[_DEPENDENCY_MAX] = { + [DEPENDENCY_FORWARD] = "Requires\0" + "Requisite\0" + "Wants\0" + "ConsistsOf\0" + "BindsTo\0", + [DEPENDENCY_REVERSE] = "RequiredBy\0" + "RequisiteOf\0" + "WantedBy\0" + "PartOf\0" + "BoundBy\0", + [DEPENDENCY_AFTER] = "After\0", + [DEPENDENCY_BEFORE] = "Before\0", + }; + + _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; + + assert(bus); + assert(name); + assert(deps); + assert_cc(ELEMENTSOF(dependencies) == _DEPENDENCY_MAX); + + path = unit_dbus_path_from_name(name); + if (!path) + return log_oom(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", "org.freedesktop.systemd1.Unit"); + 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) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *prop; + + r = sd_bus_message_read(reply, "s", &prop); + if (r < 0) + return bus_log_parse_error(r); + + if (!nulstr_contains(dependencies[arg_dependency], prop)) { + r = sd_bus_message_skip(reply, "v"); + if (r < 0) + return bus_log_parse_error(r); + } else { + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, "as"); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_message_read_strv_extend(reply, &ret); + 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_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + } + 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); + + *deps = ret; + ret = NULL; + + return 0; +} + +static int list_dependencies_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + + if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) + return 1; + if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET) + return -1; + + return strcasecmp(*a, *b); +} + +static int list_dependencies_one( + sd_bus *bus, + const char *name, + int level, + char ***units, + unsigned int branches) { + + _cleanup_strv_free_ char **deps = NULL; + char **c; + int r = 0; + + assert(bus); + assert(name); + assert(units); + + r = strv_extend(units, name); + if (r < 0) + return log_oom(); + + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; + + qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); + + STRV_FOREACH(c, deps) { + if (strv_contains(*units, *c)) { + if (!arg_plain) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); + if (r < 0) + return r; + } + continue; + } + + if (arg_plain) + printf(" "); + else { + UnitActiveState active_state = _UNIT_ACTIVE_STATE_INVALID; + const char *on; + + (void) get_state_one_unit(bus, *c, &active_state); + + switch (active_state) { + case UNIT_ACTIVE: + case UNIT_RELOADING: + case UNIT_ACTIVATING: + on = ansi_highlight_green(); + break; + + case UNIT_INACTIVE: + case UNIT_DEACTIVATING: + on = ansi_normal(); + break; + + default: + on = ansi_highlight_red(); + break; + } + + printf("%s%s%s ", on, special_glyph(BLACK_CIRCLE), ansi_normal()); + } + + r = list_dependencies_print(*c, level, branches, c[1] == NULL); + if (r < 0) + return r; + + if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { + r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1)); + if (r < 0) + return r; + } + } + + if (!arg_plain) + strv_remove(*units, name); + + return 0; +} + +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; + + 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"); + + u = unit; + } else + u = SPECIAL_DEFAULT_TARGET; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + puts(u); + + return list_dependencies_one(bus, u, 0, &units, 0); +} + +struct machine_info { + bool is_host; + char *name; + char *state; + char *control_group; + uint32_t n_failed_units; + uint32_t n_jobs; + usec_t timestamp; +}; + +static const struct bus_properties_map machine_info_property_map[] = { + { "SystemState", "s", NULL, offsetof(struct machine_info, state) }, + { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) }, + { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) }, + { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) }, + { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) }, + {} +}; + +static void machine_info_clear(struct machine_info *info) { + assert(info); + + free(info->name); + free(info->state); + free(info->control_group); + zero(*info); +} + +static void free_machines_list(struct machine_info *machine_infos, int n) { + int i; + + if (!machine_infos) + return; + + for (i = 0; i < n; i++) + machine_info_clear(&machine_infos[i]); + + free(machine_infos); +} + +static int compare_machine_info(const void *a, const void *b) { + const struct machine_info *u = a, *v = b; + + if (u->is_host != v->is_host) + return u->is_host > v->is_host ? -1 : 1; + + return strcasecmp(u->name, v->name); +} + +static int get_machine_properties(sd_bus *bus, struct machine_info *mi) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL; + int r; + + assert(mi); + + if (!bus) { + r = sd_bus_open_system_machine(&container, mi->name); + if (r < 0) + return r; + + bus = container; + } + + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi); + if (r < 0) + return r; + + return 0; +} + +static bool output_show_machine(const char *name, char **patterns) { + return strv_fnmatch_or_empty(patterns, name, FNM_NOESCAPE); +} + +static int get_machine_list( + sd_bus *bus, + struct machine_info **_machine_infos, + char **patterns) { + + struct machine_info *machine_infos = NULL; + _cleanup_strv_free_ char **m = NULL; + _cleanup_free_ char *hn = NULL; + size_t sz = 0; + char **i; + int c = 0, r; + + hn = gethostname_malloc(); + if (!hn) + return log_oom(); + + if (output_show_machine(hn, patterns)) { + if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) + return log_oom(); + + machine_infos[c].is_host = true; + machine_infos[c].name = hn; + hn = NULL; + + get_machine_properties(bus, &machine_infos[c]); + c++; + } + + r = sd_get_machine_names(&m); + if (r < 0) + return log_error_errno(r, "Failed to get machine list: %m"); + + STRV_FOREACH(i, m) { + _cleanup_free_ char *class = NULL; + + if (!output_show_machine(*i, patterns)) + continue; + + sd_machine_get_class(*i, &class); + if (!streq_ptr(class, "container")) + continue; + + if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + machine_infos[c].is_host = false; + machine_infos[c].name = strdup(*i); + if (!machine_infos[c].name) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + get_machine_properties(NULL, &machine_infos[c]); + c++; + } + + *_machine_infos = machine_infos; + return c; +} + +static void output_machines_list(struct machine_info *machine_infos, unsigned n) { + struct machine_info *m; + unsigned + circle_len = 0, + namelen = sizeof("NAME") - 1, + statelen = sizeof("STATE") - 1, + failedlen = sizeof("FAILED") - 1, + jobslen = sizeof("JOBS") - 1; + + assert(machine_infos || n == 0); + + for (m = machine_infos; m < machine_infos + n; m++) { + namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0)); + statelen = MAX(statelen, m->state ? strlen(m->state) : 0); + failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units)); + jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs)); + + if (!arg_plain && !streq_ptr(m->state, "running")) + circle_len = 2; + } + + if (!arg_no_legend) { + if (circle_len > 0) + fputs(" ", stdout); + + printf("%-*s %-*s %-*s %-*s\n", + namelen, "NAME", + statelen, "STATE", + failedlen, "FAILED", + jobslen, "JOBS"); + } + + for (m = machine_infos; m < machine_infos + n; m++) { + const char *on_state = "", *off_state = ""; + const char *on_failed = "", *off_failed = ""; + bool circle = false; + + if (streq_ptr(m->state, "degraded")) { + on_state = ansi_highlight_red(); + off_state = ansi_normal(); + circle = true; + } else if (!streq_ptr(m->state, "running")) { + on_state = ansi_highlight_yellow(); + off_state = ansi_normal(); + circle = true; + } + + if (m->n_failed_units > 0) { + on_failed = ansi_highlight_red(); + off_failed = ansi_normal(); + } else + on_failed = off_failed = ""; + + if (circle_len > 0) + printf("%s%s%s ", on_state, circle ? special_glyph(BLACK_CIRCLE) : " ", off_state); + + if (m->is_host) + printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", + (int) (namelen - (sizeof(" (host)")-1)), strna(m->name), + on_state, statelen, strna(m->state), off_state, + on_failed, failedlen, m->n_failed_units, off_failed, + jobslen, m->n_jobs); + else + printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", + namelen, strna(m->name), + on_state, statelen, strna(m->state), off_state, + on_failed, failedlen, m->n_failed_units, off_failed, + jobslen, m->n_jobs); + } + + if (!arg_no_legend) + printf("\n%u machines listed.\n", n); +} + +static int list_machines(int argc, char *argv[], void *userdata) { + struct machine_info *machine_infos = NULL; + sd_bus *bus; + int r; + + if (geteuid() != 0) { + log_error("Must be root."); + return -EPERM; + } + + 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; + + pager_open(arg_no_pager, false); + + qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info); + output_machines_list(machine_infos, r); + free_machines_list(machine_infos, r); + + return 0; +} + +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 (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; + + r = 0; + } 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; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetDefaultTarget", + &error, + &reply, + NULL); + 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) + return bus_log_parse_error(r); + } + + if (path) + printf("%s\n", path); + + return 0; +} + +static int set_default(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *unit = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int r; + + 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 (install_client_side()) { + r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); + unit_file_dump_changes(r, "set default", changes, n_changes, arg_quiet); + + if (r > 0) + r = 0; + } else { + _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", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetDefaultTarget", + &error, + &reply, + "sb", unit, 1); + 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, &changes, &n_changes); + if (r < 0) + goto finish; + + /* Try to reload if enabled */ + if (!arg_no_reload) + r = daemon_reload(argc, argv, userdata); + else + r = 0; + } + +finish: + unit_file_changes_free(changes, n_changes); + + return r; +} + +struct job_info { + uint32_t id; + const char *name, *type, *state; +}; + +static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) { + unsigned id_len, unit_len, type_len, state_len; + const struct job_info *j; + const char *on, *off; + bool shorten = false; + + assert(n == 0 || jobs); + + if (n == 0) { + if (!arg_no_legend) { + on = ansi_highlight_green(); + off = ansi_normal(); + + printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off); + } + return; + } + + pager_open(arg_no_pager, false); + + id_len = strlen("JOB"); + unit_len = strlen("UNIT"); + type_len = strlen("TYPE"); + state_len = strlen("STATE"); + + for (j = jobs; j < jobs + n; j++) { + uint32_t id = j->id; + assert(j->name && j->type && j->state); + + id_len = MAX(id_len, DECIMAL_STR_WIDTH(id)); + unit_len = MAX(unit_len, strlen(j->name)); + type_len = MAX(type_len, strlen(j->type)); + state_len = MAX(state_len, strlen(j->state)); + } + + if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) { + unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3); + shorten = true; + } + + if (!arg_no_legend) + printf("%*s %-*s %-*s %-*s\n", + id_len, "JOB", + unit_len, "UNIT", + type_len, "TYPE", + state_len, "STATE"); + + for (j = jobs; j < jobs + n; j++) { + _cleanup_free_ char *e = NULL; + + if (streq(j->state, "running")) { + on = ansi_highlight(); + off = ansi_normal(); + } else + on = off = ""; + + e = shorten ? ellipsize(j->name, unit_len, 33) : NULL; + printf("%*u %s%-*s%s %-*s %s%-*s%s\n", + id_len, j->id, + on, unit_len, e ? e : j->name, off, + type_len, j->type, + on, state_len, j->state, off); + } + + if (!arg_no_legend) { + on = ansi_highlight(); + off = ansi_normal(); + + printf("\n%s%u jobs listed%s.\n", on, n, off); + } +} + +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(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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListJobs", + &error, + &reply, + NULL); + 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) + return bus_log_parse_error(r); + + 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(argv, 1))) { + skipped = true; + continue; + } + + if (!GREEDY_REALLOC(jobs, size, c + 1)) + return log_oom(); + + jobs[c++] = job; + } + 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); + + pager_open(arg_no_pager, false); + + output_jobs_list(jobs, c, skipped); + return 0; +} + +static int cancel_job(int argc, char *argv[], void *userdata) { + sd_bus *bus; + char **name; + int r = 0; + + if (argc <= 1) + return trivial_method(argc, argv, userdata); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + 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; + + q = safe_atou32(*name, &id); + if (q < 0) + return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name); + + q = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "CancelJob", + &error, + NULL, + "u", id); + if (q < 0) { + log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q)); + if (r == 0) + r = q; + } + } + + return r; +} + +static int need_daemon_reload(sd_bus *bus, const char *unit) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *path; + int b, r; + + /* We ignore all errors here, since this is used to show a + * warning only */ + + /* We don't use unit_dbus_path_from_name() directly since we + * don't want to load the unit if it isn't loaded. */ + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + NULL, + &reply, + "s", unit); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return r; + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "NeedDaemonReload", + NULL, + 'b', &b); + if (r < 0) + return r; + + return b; +} + +static void warn_unit_file_changed(const char *name) { + assert(name); + + log_warning("%sWarning:%s %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", + ansi_highlight_red(), + ansi_normal(), + name, + arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); +} + +static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) { + char **p; + + assert(lp); + assert(unit_name); + assert(unit_path); + + STRV_FOREACH(p, lp->search_path) { + _cleanup_free_ char *path; + + path = path_join(arg_root, *p, unit_name); + if (!path) + return log_oom(); + + if (access(path, F_OK) == 0) { + *unit_path = path; + path = NULL; + return 1; + } + } + + return 0; +} + +static int unit_find_paths( + sd_bus *bus, + const char *unit_name, + LookupPaths *lp, + char **fragment_path, + char ***dropin_paths) { + + _cleanup_free_ char *path = NULL; + _cleanup_strv_free_ char **dropins = NULL; + int r; + + /** + * Finds where the unit is defined on disk. Returns 0 if the unit + * is not found. Returns 1 if it is found, and sets + * - the path to the unit in *path, if it exists on disk, + * - and a strv of existing drop-ins in *dropins, + * if the arg is not NULL and any dropins were found. + */ + + assert(unit_name); + assert(fragment_path); + assert(lp); + + 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_free_ char *unit = NULL; + + unit = unit_dbus_path_from_name(unit_name); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "FragmentPath", + &error, + &path); + if (r < 0) + return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r)); + + if (dropin_paths) { + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "DropInPaths", + &error, + &dropins); + if (r < 0) + return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r)); + } + } else { + _cleanup_set_free_ Set *names; + + names = set_new(NULL); + if (!names) + return log_oom(); + + r = set_put(names, unit_name); + if (r < 0) + return log_error_errno(r, "Failed to add unit name: %m"); + + r = unit_file_find_path(lp, unit_name, &path); + if (r < 0) + return r; + + if (r == 0) { + _cleanup_free_ char *template = NULL; + + r = unit_name_template(unit_name, &template); + if (r < 0 && r != -EINVAL) + return log_error_errno(r, "Failed to determine template name: %m"); + if (r >= 0) { + r = unit_file_find_path(lp, template, &path); + if (r < 0) + return r; + } + } + + if (dropin_paths) { + r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); + if (r < 0) + return r; + } + } + + r = 0; + + if (!isempty(path)) { + *fragment_path = path; + path = NULL; + r = 1; + } + + if (dropin_paths && !strv_isempty(dropins)) { + *dropin_paths = dropins; + dropins = NULL; + r = 1; + } + + if (r == 0 && !arg_force) + log_error("No files found for %s.", unit_name); + + return r; +} + +static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state) { + _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 *buf = NULL; + UnitActiveState state; + const char *path; + int r; + + assert(name); + assert(active_state); + + /* We don't use unit_dbus_path_from_name() directly since we don't want to load the unit unnecessarily, if it + * isn't loaded. */ + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &error, + &reply, + "s", name); + if (r < 0) { + if (!sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT)) + return log_error_errno(r, "Failed to retrieve unit: %s", bus_error_message(&error, r)); + + /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are + * considered inactive. */ + state = UNIT_INACTIVE; + + } else { + 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", + "ActiveState", + &error, + &buf); + if (r < 0) + return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); + + state = unit_active_state_from_string(buf); + if (state == _UNIT_ACTIVE_STATE_INVALID) { + log_error("Invalid unit state '%s' for: %s", buf, name); + return -EINVAL; + } + } + + *active_state = state; + return 0; +} + +static int check_triggering_units( + sd_bus *bus, + const char *name) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *path = NULL, *n = NULL, *load_state = NULL; + _cleanup_strv_free_ char **triggered_by = NULL; + bool print_warning_label = true; + UnitActiveState active_state; + char **i; + int r; + + r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + + path = unit_dbus_path_from_name(n); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "LoadState", + &error, + &load_state); + if (r < 0) + return log_error_errno(r, "Failed to get load state of %s: %s", n, bus_error_message(&error, r)); + + if (streq(load_state, "masked")) + return 0; + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "TriggeredBy", + &error, + &triggered_by); + 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 = get_state_one_unit(bus, *i, &active_state); + if (r < 0) + return r; + + if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING)) + continue; + + if (print_warning_label) { + log_warning("Warning: Stopping %s, but it can still be activated by:", n); + print_warning_label = false; + } + + log_warning(" %s", *i); + } + + return 0; +} + +static const struct { + const char *verb; + const char *method; +} unit_actions[] = { + { "start", "StartUnit" }, + { "stop", "StopUnit" }, + { "condstop", "StopUnit" }, + { "reload", "ReloadUnit" }, + { "restart", "RestartUnit" }, + { "try-restart", "TryRestartUnit" }, + { "condrestart", "TryRestartUnit" }, + { "reload-or-restart", "ReloadOrRestartUnit" }, + { "try-reload-or-restart", "ReloadOrTryRestartUnit" }, + { "reload-or-try-restart", "ReloadOrTryRestartUnit" }, + { "condreload", "ReloadOrTryRestartUnit" }, + { "force-reload", "ReloadOrTryRestartUnit" } +}; + +static const char *verb_to_method(const char *verb) { + uint i; + + for (i = 0; i < ELEMENTSOF(unit_actions); i++) + if (streq_ptr(unit_actions[i].verb, verb)) + return unit_actions[i].method; + + return "StartUnit"; +} + +static const char *method_to_verb(const char *method) { + uint i; + + for (i = 0; i < ELEMENTSOF(unit_actions); i++) + if (streq_ptr(unit_actions[i].method, method)) + return unit_actions[i].verb; + + return "n/a"; +} + +static int start_unit_one( + sd_bus *bus, + const char *method, + const char *name, + const char *mode, + sd_bus_error *error, + BusWaitForJobs *w) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *path; + int r; + + assert(method); + assert(name); + assert(mode); + assert(error); + + log_debug("Calling manager for %s on %s, %s", method, name, mode); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + error, + &reply, + "ss", name, mode); + if (r < 0) { + const char *verb; + + /* There's always a fallback possible for legacy actions. */ + if (arg_action != ACTION_SYSTEMCTL) + return r; + + 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 %s logs and 'systemctl%s status %s' for details.", + arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", + arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + name); + + return r; + } + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return bus_log_parse_error(r); + + if (need_daemon_reload(bus, name) > 0) + warn_unit_file_changed(name); + + if (w) { + log_debug("Adding %s to the set", path); + r = bus_wait_for_jobs_add(w, path); + if (r < 0) + return log_oom(); + } + + return 0; +} + +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; + + if (suffix) + r = unit_name_mangle_with_suffix(*name, UNIT_NAME_GLOB, suffix, &t); + else + r = unit_name_mangle(*name, UNIT_NAME_GLOB, &t); + if (r < 0) + return log_error_errno(r, "Failed to mangle name: %m"); + + if (string_is_glob(t)) + r = strv_consume(&globs, t); + else + r = strv_consume(&mangled, t); + if (r < 0) + return log_oom(); + } + + /* Query the manager only if any of the names are a glob, since + * this is fairly expensive */ + if (!strv_isempty(globs)) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ UnitInfo *unit_infos = NULL; + size_t allocated, n; + + r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply); + if (r < 0) + return r; + + n = strv_length(mangled); + allocated = n + 1; + + for (i = 0; i < r; i++) { + if (!GREEDY_REALLOC(mangled, allocated, n+2)) + return log_oom(); + + mangled[n] = strdup(unit_infos[i].id); + if (!mangled[n]) + return log_oom(); + + mangled[++n] = NULL; + } + } + + *ret = mangled; + mangled = NULL; /* do not free */ + + return 0; +} + +static const struct { + const char *target; + const char *verb; + const char *mode; +} action_table[_ACTION_MAX] = { + [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, + [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, + [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, + [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, + [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, + [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, + [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, + [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, + [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, + [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, + [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, + [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, +}; + +static enum action verb_to_action(const char *verb) { + enum action i; + + for (i = _ACTION_INVALID; i < _ACTION_MAX; i++) + if (streq_ptr(action_table[i].verb, verb)) + return i; + + return _ACTION_INVALID; +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + ask_password_agent_open_if_enabled(); + polkit_agent_open_if_enabled(); + + if (arg_action == ACTION_SYSTEMCTL) { + enum action action; + + method = verb_to_method(argv[0]); + action = verb_to_action(argv[0]); + + if (streq(argv[0], "isolate")) { + mode = "isolate"; + suffix = ".target"; + } else + mode = action_table[action].mode ?: arg_job_mode; + + one_name = action_table[action].target; + } else { + assert(arg_action < ELEMENTSOF(action_table)); + assert(action_table[arg_action].target); + + method = "StartUnit"; + + mode = action_table[arg_action].mode; + one_name = action_table[arg_action].target; + } + + if (one_name) + names = strv_new(one_name, NULL); + else { + r = expand_names(bus, strv_skip(argv, 1), suffix, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); + } + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + STRV_FOREACH(name, names) { + _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); + if (r >= 0 && q < 0) + r = translate_bus_error_to_exit_status(q, &error); + } + + if (!arg_no_block) { + int q, arg_count = 0; + const char* extra_args[4] = {}; + + if (arg_scope != UNIT_FILE_SYSTEM) + extra_args[arg_count++] = "--user"; + + assert(IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_REMOTE, BUS_TRANSPORT_MACHINE)); + if (arg_transport == BUS_TRANSPORT_REMOTE) { + extra_args[arg_count++] = "-H"; + extra_args[arg_count++] = arg_host; + } else if (arg_transport == BUS_TRANSPORT_MACHINE) { + extra_args[arg_count++] = "-M"; + extra_args[arg_count++] = arg_host; + } + + q = bus_wait_for_jobs(w, arg_quiet, extra_args); + if (q < 0) + return q; + + /* When stopping units, warn if they can still be triggered by + * another active unit (socket, path, timer) */ + if (!arg_quiet && streq(method, "StopUnit")) + STRV_FOREACH(name, names) + check_triggering_units(bus, *name); + } + + 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 logind_reboot(enum action a) { +#ifdef HAVE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *method, *description; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + switch (a) { + + case ACTION_REBOOT: + method = "Reboot"; + description = "reboot system"; + break; + + case ACTION_POWEROFF: + method = "PowerOff"; + description = "power off system"; + break; + + case ACTION_SUSPEND: + method = "Suspend"; + description = "suspend system"; + break; + + case ACTION_HIBERNATE: + method = "Hibernate"; + description = "hibernate system"; + break; + + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + description = "put system into hybrid sleep"; + break; + + default: + return -EINVAL; + } + + polkit_agent_open_if_enabled(); + (void) logind_set_wall_message(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + method, + &error, + NULL, + "b", arg_ask_password); + if (r < 0) + return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r)); + + return 0; +#else + return -ENOSYS; +#endif +} + +static int logind_check_inhibitors(enum action a) { +#ifdef HAVE_LOGIND + _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 (arg_ignore_inhibitors || arg_force > 0) + return 0; + + if (arg_when > 0) + return 0; + + if (geteuid() == 0) + return 0; + + if (!on_tty()) + return 0; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return 0; + + 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", + "ListInhibitors", + NULL, + &reply, + NULL); + if (r < 0) + /* If logind is not around, then there are no inhibitors... */ + return 0; + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { + _cleanup_free_ char *comm = NULL, *user = NULL; + _cleanup_strv_free_ char **sv = NULL; + + if (!streq(mode, "block")) + continue; + + sv = strv_split(what, ":"); + if (!sv) + return log_oom(); + + if ((pid_t) pid < 0) + return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid); + + if (!strv_contains(sv, + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_KEXEC) ? "shutdown" : "sleep")) + continue; + + get_process_comm(pid, &comm); + user = uid_to_name(uid); + + log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".", + who, (pid_t) pid, strna(comm), strna(user), why); + + 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); + + /* Check for current sessions */ + sd_get_sessions(&sessions); + STRV_FOREACH(s, sessions) { + _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL; + + if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) + continue; + + if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) + continue; + + if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty"))) + continue; + + sd_session_get_tty(*s, &tty); + sd_session_get_seat(*s, &seat); + sd_session_get_service(*s, &service); + user = uid_to_name(uid); + + log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty); + c++; + } + + if (c <= 0) + return 0; + + log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.", + action_table[a].verb); + + return -EPERM; +#else + return 0; +#endif +} + +static int logind_prepare_firmware_setup(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; + + 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) + return 0; + + if (arg_transport == BUS_TRANSPORT_LOCAL) { + + r = efi_set_reboot_to_firmware(true); + if (r < 0) + log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m"); + else + return r; + } + + 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.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetExitCode", + &error, + NULL, + "y", code); + if (r < 0) + return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r)); + + return 0; +} + +static int start_special(int argc, char *argv[], void *userdata) { + enum action a; + int r; + + assert(argv); + + a = verb_to_action(argv[0]); + + r = logind_check_inhibitors(a); + if (r < 0) + return r; + + if (arg_force >= 2 && geteuid() != 0) { + log_error("Must be root."); + return -EPERM; + } + + r = prepare_firmware_setup(); + if (r < 0) + return r; + + if (a == ACTION_REBOOT && argc > 1) { + r = update_reboot_parameter_and_warn(argv[1]); + if (r < 0) + return r; + + } else if (a == ACTION_EXIT && argc > 1) { + uint8_t code; + + /* 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 && + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT)) + return halt_now(a); + + if (arg_force >= 1 && + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_KEXEC, + ACTION_EXIT)) + return trivial_method(argc, argv, userdata); + + /* First try logind, to allow authentication with polkit */ + if (IN_SET(a, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP)) { + 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 */ + } + + return start_unit(argc, argv, userdata); +} + +static int start_system_special(int argc, char *argv[], void *userdata) { + /* Like start_special above, but raises an error when running in user mode */ + + if (arg_scope != UNIT_FILE_SYSTEM) { + log_error("Bad action for %s mode.", + arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user"); + return -EINVAL; + } + + return start_special(argc, argv, userdata); +} + +static int check_unit_generic(int code, const UnitActiveState good_states[], int nb_states, char **args) { + _cleanup_strv_free_ char **names = NULL; + UnitActiveState active_state; + sd_bus *bus; + char **name; + int r, i; + bool found = false; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = expand_names(bus, args, NULL, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); + + STRV_FOREACH(name, names) { + r = get_state_one_unit(bus, *name, &active_state); + if (r < 0) + return r; + + if (!arg_quiet) + puts(unit_active_state_to_string(active_state)); + + for (i = 0; i < nb_states; ++i) + if (good_states[i] == active_state) + found = true; + } + + /* use the given return code for the case that we won't find + * any unit which matches the list */ + return found ? 0 : code; +} + +static int check_unit_active(int argc, char *argv[], void *userdata) { + const UnitActiveState states[] = { UNIT_ACTIVE, UNIT_RELOADING }; + /* According to LSB: 3, "program is not running" */ + return check_unit_generic(EXIT_PROGRAM_NOT_RUNNING, states, ELEMENTSOF(states), strv_skip(argv, 1)); +} + +static int check_unit_failed(int argc, char *argv[], void *userdata) { + const UnitActiveState states[] = { UNIT_FAILED }; + return check_unit_generic(EXIT_PROGRAM_DEAD_AND_PID_EXISTS, states, ELEMENTSOF(states), strv_skip(argv, 1)); +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + if (!arg_kill_who) + arg_kill_who = "all"; + + /* --fail was specified */ + if (streq(arg_job_mode, "fail")) + kill_who = strjoina(arg_kill_who, "-fail"); + + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + q = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + &error, + NULL, + "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal); + if (q < 0) { + log_error_errno(q, "Failed to kill unit %s: %s", *names, bus_error_message(&error, q)); + if (r == 0) + r = q; + } + } + + return r; +} + +typedef struct ExecStatusInfo { + char *name; + + char *path; + char **argv; + + bool ignore; + + usec_t start_timestamp; + usec_t exit_timestamp; + pid_t pid; + int code; + int status; + + LIST_FIELDS(struct ExecStatusInfo, exec); +} ExecStatusInfo; + +static void exec_status_info_free(ExecStatusInfo *i) { + assert(i); + + free(i->name); + free(i->path); + strv_free(i->argv); + free(i); +} + +static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) { + uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic; + const char *path; + uint32_t pid; + int32_t code, status; + int ignore, r; + + assert(m); + assert(i); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii"); + if (r < 0) + return bus_log_parse_error(r); + else if (r == 0) + return 0; + + r = sd_bus_message_read(m, "s", &path); + if (r < 0) + return bus_log_parse_error(r); + + i->path = strdup(path); + if (!i->path) + return log_oom(); + + r = sd_bus_message_read_strv(m, &i->argv); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(m, + "bttttuii", + &ignore, + &start_timestamp, &start_timestamp_monotonic, + &exit_timestamp, &exit_timestamp_monotonic, + &pid, + &code, &status); + if (r < 0) + return bus_log_parse_error(r); + + i->ignore = ignore; + i->start_timestamp = (usec_t) start_timestamp; + i->exit_timestamp = (usec_t) exit_timestamp; + i->pid = (pid_t) pid; + i->code = code; + i->status = status; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 1; +} + +typedef struct UnitCondition { + char *name; + char *param; + bool trigger; + bool negate; + int tristate; + + LIST_FIELDS(struct UnitCondition, conditions); +} UnitCondition; + +static void unit_condition_free(UnitCondition *c) { + if (!c) + return; + + free(c->name); + free(c->param); + free(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(UnitCondition*, unit_condition_free); + +typedef struct UnitStatusInfo { + const char *id; + const char *load_state; + const char *active_state; + const char *sub_state; + const char *unit_file_state; + const char *unit_file_preset; + + const char *description; + const char *following; + + char **documentation; + + const char *fragment_path; + const char *source_path; + const char *control_group; + + char **dropin_paths; + + const char *load_error; + const char *result; + + usec_t inactive_exit_timestamp; + usec_t inactive_exit_timestamp_monotonic; + usec_t active_enter_timestamp; + usec_t active_exit_timestamp; + usec_t inactive_enter_timestamp; + + bool need_daemon_reload; + bool transient; + + /* Service */ + pid_t main_pid; + pid_t control_pid; + const char *status_text; + const char *pid_file; + bool running:1; + int status_errno; + + usec_t start_timestamp; + usec_t exit_timestamp; + + int exit_code, exit_status; + + usec_t condition_timestamp; + bool condition_result; + LIST_HEAD(UnitCondition, conditions); + + usec_t assert_timestamp; + bool assert_result; + bool failed_assert_trigger; + bool failed_assert_negate; + const char *failed_assert; + const char *failed_assert_parameter; + + /* Socket */ + unsigned n_accepted; + unsigned n_connections; + bool accept; + + /* Pairs of type, path */ + char **listen; + + /* Device */ + const char *sysfs_path; + + /* Mount, Automount */ + const char *where; + + /* Swap */ + const char *what; + + /* CGroup */ + uint64_t memory_current; + uint64_t memory_low; + uint64_t memory_high; + uint64_t memory_max; + uint64_t memory_limit; + uint64_t cpu_usage_nsec; + uint64_t tasks_current; + uint64_t tasks_max; + + LIST_HEAD(ExecStatusInfo, exec); +} UnitStatusInfo; + +static void unit_status_info_free(UnitStatusInfo *info) { + ExecStatusInfo *p; + UnitCondition *c; + + strv_free(info->documentation); + strv_free(info->dropin_paths); + strv_free(info->listen); + + while ((c = info->conditions)) { + LIST_REMOVE(conditions, info->conditions, c); + unit_condition_free(c); + } + + while ((p = info->exec)) { + LIST_REMOVE(exec, info->exec, p); + exec_status_info_free(p); + } +} + +static void print_status_info( + sd_bus *bus, + UnitStatusInfo *i, + bool *ellipsized) { + + ExecStatusInfo *p; + const char *active_on, *active_off, *on, *off, *ss; + usec_t timestamp; + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + const char *path; + char **t, **t2; + int r; + + assert(i); + + /* This shows pretty information about a unit. See + * print_property() for a low-level property printer */ + + if (streq_ptr(i->active_state, "failed")) { + active_on = ansi_highlight_red(); + active_off = ansi_normal(); + } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { + active_on = ansi_highlight_green(); + active_off = ansi_normal(); + } else + active_on = active_off = ""; + + printf("%s%s%s %s", active_on, special_glyph(BLACK_CIRCLE), active_off, strna(i->id)); + + if (i->description && !streq_ptr(i->id, i->description)) + printf(" - %s", i->description); + + printf("\n"); + + if (i->following) + printf(" Follow: unit currently follows state of %s\n", i->following); + + if (streq_ptr(i->load_state, "error")) { + on = ansi_highlight_red(); + off = ansi_normal(); + } else + on = off = ""; + + path = i->source_path ? i->source_path : i->fragment_path; + + 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)) + printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", + on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); + else if (path && !isempty(i->unit_file_state)) + printf(" Loaded: %s%s%s (%s; %s)\n", + on, strna(i->load_state), off, path, i->unit_file_state); + else if (path) + printf(" Loaded: %s%s%s (%s)\n", + on, strna(i->load_state), off, path); + else + 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; + char ** dropin; + + STRV_FOREACH(dropin, i->dropin_paths) { + if (! dir || last) { + printf(dir ? " " : " Drop-In: "); + + dir = mfree(dir); + + dir = dirname_malloc(*dropin); + if (!dir) { + log_oom(); + return; + } + + printf("%s\n %s", dir, + special_glyph(TREE_RIGHT)); + } + + last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); + + printf("%s%s", basename(*dropin), last ? "\n" : ", "); + } + } + + ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; + if (ss) + printf(" Active: %s%s (%s)%s", + active_on, strna(i->active_state), ss, active_off); + else + printf(" Active: %s%s%s", + active_on, strna(i->active_state), active_off); + + if (!isempty(i->result) && !streq(i->result, "success")) + printf(" (Result: %s)", i->result); + + timestamp = (streq_ptr(i->active_state, "active") || + streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp : + (streq_ptr(i->active_state, "inactive") || + streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp : + streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp : + i->active_exit_timestamp; + + s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); + s2 = format_timestamp(since2, sizeof(since2), timestamp); + + if (s1) + printf(" since %s; %s\n", s2, s1); + else if (s2) + printf(" since %s\n", s2); + else + printf("\n"); + + if (!i->condition_result && i->condition_timestamp > 0) { + UnitCondition *c; + int n = 0; + + s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); + s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); + + printf("Condition: start %scondition failed%s at %s%s%s\n", + ansi_highlight_yellow(), ansi_normal(), + s2, s1 ? "; " : "", strempty(s1)); + + LIST_FOREACH(conditions, c, i->conditions) + if (c->tristate < 0) + n++; + + LIST_FOREACH(conditions, c, i->conditions) + if (c->tristate < 0) + printf(" %s %s=%s%s%s was not met\n", + --n ? special_glyph(TREE_BRANCH) : special_glyph(TREE_RIGHT), + c->name, + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->param); + } + + if (!i->assert_result && i->assert_timestamp > 0) { + s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp); + s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp); + + printf(" Assert: start %sassertion failed%s at %s%s%s\n", + ansi_highlight_red(), ansi_normal(), + s2, s1 ? "; " : "", strempty(s1)); + if (i->failed_assert_trigger) + printf(" none of the trigger assertions were met\n"); + else if (i->failed_assert) + printf(" %s=%s%s was not met\n", + i->failed_assert, + i->failed_assert_negate ? "!" : "", + i->failed_assert_parameter); + } + + if (i->sysfs_path) + printf(" Device: %s\n", i->sysfs_path); + if (i->where) + printf(" Where: %s\n", i->where); + if (i->what) + printf(" What: %s\n", i->what); + + STRV_FOREACH(t, i->documentation) + printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t); + + STRV_FOREACH_PAIR(t, t2, i->listen) + printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t); + + if (i->accept) + printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); + + LIST_FOREACH(exec, p, i->exec) { + _cleanup_free_ char *argv = NULL; + bool good; + + /* Only show exited processes here */ + if (p->code == 0) + continue; + + argv = strv_join(p->argv, " "); + printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv)); + + good = is_clean_exit_lsb(p->code, p->status, NULL); + if (!good) { + on = ansi_highlight_red(); + off = ansi_normal(); + } else + on = off = ""; + + printf("%s(code=%s, ", on, sigchld_code_to_string(p->code)); + + if (p->code == CLD_EXITED) { + const char *c; + + printf("status=%i", p->status); + + c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD); + if (c) + printf("/%s", c); + + } else + printf("signal=%s", signal_to_string(p->status)); + + printf(")%s\n", off); + + if (i->main_pid == p->pid && + i->start_timestamp == p->start_timestamp && + i->exit_timestamp == p->start_timestamp) + /* Let's not show this twice */ + i->main_pid = 0; + + if (p->pid == i->control_pid) + i->control_pid = 0; + } + + if (i->main_pid > 0 || i->control_pid > 0) { + if (i->main_pid > 0) { + printf(" Main PID: "PID_FMT, i->main_pid); + + if (i->running) { + _cleanup_free_ char *comm = NULL; + (void) get_process_comm(i->main_pid, &comm); + if (comm) + printf(" (%s)", comm); + } else if (i->exit_code > 0) { + printf(" (code=%s, ", sigchld_code_to_string(i->exit_code)); + + if (i->exit_code == CLD_EXITED) { + const char *c; + + printf("status=%i", i->exit_status); + + c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD); + if (c) + printf("/%s", c); + + } else + printf("signal=%s", signal_to_string(i->exit_status)); + printf(")"); + } + } + + if (i->control_pid > 0) { + _cleanup_free_ char *c = NULL; + + if (i->main_pid > 0) + fputs("; Control PID: ", stdout); + else + fputs("Cntrl PID: ", stdout); /* if first in column, abbreviated so it fits alignment */ + + printf(PID_FMT, i->control_pid); + + (void) get_process_comm(i->control_pid, &c); + if (c) + printf(" (%s)", c); + } + + printf("\n"); + } + + if (i->status_text) + printf(" Status: \"%s\"\n", i->status_text); + if (i->status_errno > 0) + printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno)); + + if (i->tasks_current != (uint64_t) -1) { + printf(" Tasks: %" PRIu64, i->tasks_current); + + if (i->tasks_max != (uint64_t) -1) + printf(" (limit: %" PRIu64 ")\n", i->tasks_max); + else + printf("\n"); + } + + if (i->memory_current != (uint64_t) -1) { + char buf[FORMAT_BYTES_MAX]; + + printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current)); + + if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX || + i->memory_limit != CGROUP_LIMIT_MAX) { + const char *prefix = ""; + + printf(" ("); + if (i->memory_low > 0) { + printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low)); + prefix = " "; + } + if (i->memory_high != CGROUP_LIMIT_MAX) { + printf("%shigh: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_high)); + prefix = " "; + } + if (i->memory_max != CGROUP_LIMIT_MAX) { + printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max)); + prefix = " "; + } + if (i->memory_limit != CGROUP_LIMIT_MAX) { + printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit)); + prefix = " "; + } + printf(")"); + } + printf("\n"); + } + + if (i->cpu_usage_nsec != (uint64_t) -1) { + char buf[FORMAT_TIMESPAN_MAX]; + printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC)); + } + + if (i->control_group) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + static const char prefix[] = " "; + unsigned c; + + printf(" CGroup: %s\n", i->control_group); + + c = columns(); + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; + else + c = 0; + + r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error); + if (r == -EBADR) { + unsigned k = 0; + pid_t extra[2]; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (i->main_pid > 0) + extra[k++] = i->main_pid; + + if (i->control_pid > 0) + extra[k++] = i->control_pid; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags()); + } else if (r < 0) + log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r)); + } + + if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) + show_journal_by_unit( + stdout, + i->id, + arg_output, + 0, + i->inactive_exit_timestamp_monotonic, + arg_lines, + getuid(), + get_output_flags() | OUTPUT_BEGIN_NEWLINE, + SD_JOURNAL_LOCAL_ONLY, + arg_scope == UNIT_FILE_SYSTEM, + ellipsized); + + if (i->need_daemon_reload) + warn_unit_file_changed(i->id); +} + +static void show_unit_help(UnitStatusInfo *i) { + char **p; + + assert(i); + + if (!i->documentation) { + log_info("Documentation for %s not known.", i->id); + return; + } + + STRV_FOREACH(p, i->documentation) + if (startswith(*p, "man:")) + show_man_page(*p + 4, false); + else + log_info("Can't show: %s", *p); +} + +static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) { + int r; + + assert(name); + assert(m); + assert(i); + + switch (contents[0]) { + + case SD_BUS_TYPE_STRING: { + const char *s; + + r = sd_bus_message_read(m, "s", &s); + if (r < 0) + return bus_log_parse_error(r); + + if (!isempty(s)) { + if (streq(name, "Id")) + i->id = s; + else if (streq(name, "LoadState")) + i->load_state = s; + else if (streq(name, "ActiveState")) + i->active_state = s; + else if (streq(name, "SubState")) + i->sub_state = s; + else if (streq(name, "Description")) + i->description = s; + else if (streq(name, "FragmentPath")) + i->fragment_path = s; + else if (streq(name, "SourcePath")) + i->source_path = s; +#ifndef NOLEGACY + else if (streq(name, "DefaultControlGroup")) { + const char *e; + e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":"); + if (e) + i->control_group = e; + } +#endif + else if (streq(name, "ControlGroup")) + i->control_group = s; + else if (streq(name, "StatusText")) + i->status_text = s; + else if (streq(name, "PIDFile")) + i->pid_file = s; + else if (streq(name, "SysFSPath")) + i->sysfs_path = s; + else if (streq(name, "Where")) + i->where = s; + else if (streq(name, "What")) + i->what = s; + else if (streq(name, "Following")) + i->following = s; + else if (streq(name, "UnitFileState")) + i->unit_file_state = s; + else if (streq(name, "UnitFilePreset")) + i->unit_file_preset = s; + else if (streq(name, "Result")) + i->result = s; + } + + break; + } + + case SD_BUS_TYPE_BOOLEAN: { + int b; + + r = sd_bus_message_read(m, "b", &b); + if (r < 0) + return bus_log_parse_error(r); + + if (streq(name, "Accept")) + i->accept = b; + else if (streq(name, "NeedDaemonReload")) + i->need_daemon_reload = b; + else if (streq(name, "ConditionResult")) + i->condition_result = b; + else if (streq(name, "AssertResult")) + i->assert_result = b; + else if (streq(name, "Transient")) + i->transient = b; + + break; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t u; + + r = sd_bus_message_read(m, "u", &u); + if (r < 0) + return bus_log_parse_error(r); + + if (streq(name, "MainPID")) { + if (u > 0) { + i->main_pid = (pid_t) u; + i->running = true; + } + } else if (streq(name, "ControlPID")) + i->control_pid = (pid_t) u; + else if (streq(name, "ExecMainPID")) { + if (u > 0) + i->main_pid = (pid_t) u; + } else if (streq(name, "NAccepted")) + i->n_accepted = u; + else if (streq(name, "NConnections")) + i->n_connections = u; + + break; + } + + case SD_BUS_TYPE_INT32: { + int32_t j; + + r = sd_bus_message_read(m, "i", &j); + if (r < 0) + return bus_log_parse_error(r); + + if (streq(name, "ExecMainCode")) + i->exit_code = (int) j; + else if (streq(name, "ExecMainStatus")) + i->exit_status = (int) j; + else if (streq(name, "StatusErrno")) + i->status_errno = (int) j; + + break; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t u; + + r = sd_bus_message_read(m, "t", &u); + if (r < 0) + return bus_log_parse_error(r); + + if (streq(name, "ExecMainStartTimestamp")) + i->start_timestamp = (usec_t) u; + else if (streq(name, "ExecMainExitTimestamp")) + i->exit_timestamp = (usec_t) u; + else if (streq(name, "ActiveEnterTimestamp")) + i->active_enter_timestamp = (usec_t) u; + else if (streq(name, "InactiveEnterTimestamp")) + i->inactive_enter_timestamp = (usec_t) u; + else if (streq(name, "InactiveExitTimestamp")) + i->inactive_exit_timestamp = (usec_t) u; + else if (streq(name, "InactiveExitTimestampMonotonic")) + i->inactive_exit_timestamp_monotonic = (usec_t) u; + else if (streq(name, "ActiveExitTimestamp")) + i->active_exit_timestamp = (usec_t) u; + else if (streq(name, "ConditionTimestamp")) + i->condition_timestamp = (usec_t) u; + else if (streq(name, "AssertTimestamp")) + i->assert_timestamp = (usec_t) u; + else if (streq(name, "MemoryCurrent")) + i->memory_current = u; + else if (streq(name, "MemoryLow")) + i->memory_low = u; + else if (streq(name, "MemoryHigh")) + i->memory_high = u; + else if (streq(name, "MemoryMax")) + i->memory_max = u; + else if (streq(name, "MemoryLimit")) + i->memory_limit = u; + else if (streq(name, "TasksCurrent")) + i->tasks_current = u; + else if (streq(name, "TasksMax")) + i->tasks_max = u; + else if (streq(name, "CPUUsageNSec")) + i->cpu_usage_nsec = u; + + break; + } + + case SD_BUS_TYPE_ARRAY: + + if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) { + _cleanup_free_ ExecStatusInfo *info = NULL; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)"); + if (r < 0) + return bus_log_parse_error(r); + + info = new0(ExecStatusInfo, 1); + if (!info) + return log_oom(); + + while ((r = exec_status_info_deserialize(m, info)) > 0) { + + info->name = strdup(name); + if (!info->name) + return log_oom(); + + LIST_PREPEND(exec, i->exec, info); + + info = new0(ExecStatusInfo, 1); + if (!info) + return log_oom(); + } + + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) { + const char *type, *path; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) { + + r = strv_extend(&i->listen, type); + if (r < 0) + return r; + + r = strv_extend(&i->listen, path); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "DropInPaths")) { + + r = sd_bus_message_read_strv(m, &i->dropin_paths); + if (r < 0) + return bus_log_parse_error(r); + + } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Documentation")) { + + r = sd_bus_message_read_strv(m, &i->documentation); + if (r < 0) + return bus_log_parse_error(r); + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Conditions")) { + const char *cond, *param; + int trigger, negate; + int32_t state; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { + _cleanup_(unit_condition_freep) UnitCondition *c = NULL; + + log_debug("%s trigger=%d negate=%d %s →%d", cond, trigger, negate, param, state); + + c = new0(UnitCondition, 1); + if (!c) + return log_oom(); + + c->name = strdup(cond); + c->param = strdup(param); + if (!c->name || !c->param) + return log_oom(); + + c->trigger = trigger; + c->negate = negate; + c->tristate = state; + + LIST_PREPEND(conditions, i->conditions, c); + c = NULL; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) { + const char *cond, *param; + int trigger, negate; + int32_t state; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { + log_debug("%s %d %d %s %d", cond, trigger, negate, param, state); + if (state < 0 && (!trigger || !i->failed_assert)) { + i->failed_assert = cond; + i->failed_assert_trigger = trigger; + i->failed_assert_negate = negate; + i->failed_assert_parameter = param; + } + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + } else + goto skip; + + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + + if (streq(name, "LoadError")) { + const char *n, *message; + + r = sd_bus_message_read(m, "(ss)", &n, &message); + if (r < 0) + return bus_log_parse_error(r); + + if (!isempty(message)) + i->load_error = message; + } else + goto skip; + + break; + + default: + goto skip; + } + + return 0; + +skip: + r = sd_bus_message_skip(m, contents); + if (r < 0) + return bus_log_parse_error(r); + + return 0; +} + +#define print_prop(name, fmt, ...) \ + do { \ + if (arg_value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + +static int print_property(const char *name, sd_bus_message *m, const char *contents) { + int r; + + assert(name); + assert(m); + + /* This is a low-level property printer, see + * print_status_info() for the nicer output */ + + if (arg_properties && !strv_find(arg_properties, name)) { + /* skip what we didn't read */ + r = sd_bus_message_skip(m, contents); + return r; + } + + switch (contents[0]) { + + case SD_BUS_TYPE_STRUCT_BEGIN: + + if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) { + uint32_t u; + + r = sd_bus_message_read(m, "(uo)", &u, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (u > 0) + print_prop(name, "%"PRIu32, u); + else if (arg_all) + print_prop(name, "%s", ""); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Unit")) { + const char *s; + + r = sd_bus_message_read(m, "(so)", &s, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || !isempty(s)) + print_prop(name, "%s", s); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "LoadError")) { + const char *a = NULL, *b = NULL; + + r = sd_bus_message_read(m, "(ss)", &a, &b); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || !isempty(a) || !isempty(b)) + print_prop(name, "%s \"%s\"", strempty(a), strempty(b)); + + return 0; + } else if (streq_ptr(name, "SystemCallFilter")) { + _cleanup_strv_free_ char **l = NULL; + int whitelist; + + r = sd_bus_message_enter_container(m, 'r', "bas"); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(m, "b", &whitelist); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_strv(m, &l); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || whitelist || !strv_isempty(l)) { + bool first = true; + char **i; + + if (!arg_value) { + fputs(name, stdout); + fputc('=', stdout); + } + + if (!whitelist) + fputc('~', stdout); + + STRV_FOREACH(i, l) { + if (first) + first = false; + else + fputc(' ', stdout); + + fputs(*i, stdout); + } + fputc('\n', stdout); + } + + return 0; + } + + break; + + case SD_BUS_TYPE_ARRAY: + + if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "EnvironmentFiles")) { + const char *path; + int ignore; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sb)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0) + print_prop("EnvironmentFile", "%s (ignore_errors=%s)", path, yes_no(ignore)); + + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Paths")) { + const char *type, *path; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) + print_prop(type, "%s", path); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) { + const char *type, *path; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) + if (arg_value) + puts(path); + else + printf("Listen%s=%s\n", type, path); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Timers")) { + const char *base; + uint64_t value, next_elapse; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(stt)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) { + char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; + + print_prop(base, "{ value=%s ; next_elapse=%s }", + format_timespan(timespan1, sizeof(timespan1), value, 0), + format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) { + ExecStatusInfo info = {}; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = exec_status_info_deserialize(m, &info)) > 0) { + char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; + _cleanup_free_ char *tt; + + tt = strv_join(info.argv, " "); + + print_prop(name, + "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }", + strna(info.path), + strna(tt), + yes_no(info.ignore), + strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), + strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), + info.pid, + sigchld_code_to_string(info.code), + info.status, + info.code == CLD_EXITED ? "" : "/", + strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); + + free(info.path); + strv_free(info.argv); + zero(info); + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "DeviceAllow")) { + const char *path, *rwm; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0) + print_prop(name, "%s %s", strna(path), strna(rwm)); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "IODeviceWeight") || streq(name, "BlockIODeviceWeight"))) { + const char *path; + uint64_t weight; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0) + print_prop(name, "%s %"PRIu64, strna(path), weight); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (cgroup_io_limit_type_from_string(name) >= 0 || + streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { + const char *path; + uint64_t bandwidth; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0) + print_prop(name, "%s %"PRIu64, strna(path), bandwidth); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + } + + break; + } + + r = bus_print_property(name, m, arg_value, arg_all); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) { + r = sd_bus_message_skip(m, contents); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all) + printf("%s=[unprintable]\n", name); + } + + return 0; +} + +static int show_one( + const char *verb, + sd_bus *bus, + const char *path, + const char *unit, + bool show_properties, + bool *new_line, + bool *ellipsized) { + + static const struct bus_properties_map property_map[] = { + { "LoadState", "s", map_string_no_copy, offsetof(UnitStatusInfo, load_state) }, + { "ActiveState", "s", map_string_no_copy, offsetof(UnitStatusInfo, active_state) }, + {} + }; + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_set_free_ Set *found_properties = NULL; + _cleanup_(unit_status_info_free) UnitStatusInfo info = { + .memory_current = (uint64_t) -1, + .memory_high = CGROUP_LIMIT_MAX, + .memory_max = CGROUP_LIMIT_MAX, + .memory_limit = (uint64_t) -1, + .cpu_usage_nsec = (uint64_t) -1, + .tasks_current = (uint64_t) -1, + .tasks_max = (uint64_t) -1, + }; + int r; + + assert(path); + assert(new_line); + + log_debug("Showing one %s", path); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + + if (unit) { + r = bus_message_map_all_properties(reply, property_map, &info); + if (r < 0) + return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); + + if (streq_ptr(info.load_state, "not-found") && streq_ptr(info.active_state, "inactive")) { + log_error("Unit %s could not be found.", unit); + + if (streq(verb, "status")) + return EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN; + + return -ENOENT; + } + + r = sd_bus_message_rewind(reply, true); + if (r < 0) + return log_error_errno(r, "Failed to rewind: %s", bus_error_message(&error, r)); + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + if (*new_line) + printf("\n"); + + *new_line = true; + + while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *name, *contents; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_peek_type(reply, NULL, &contents); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return bus_log_parse_error(r); + + if (show_properties) { + r = set_ensure_allocated(&found_properties, &string_hash_ops); + if (r < 0) + return log_oom(); + + r = set_put(found_properties, name); + if (r < 0 && r != EEXIST) + return log_oom(); + + r = print_property(name, reply, contents); + } else + r = status_property(name, reply, &info, contents); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + 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 (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 = 0; + if (show_properties) { + char **pp; + + STRV_FOREACH(pp, arg_properties) + if (!set_contains(found_properties, *pp)) { + log_warning("Property %s does not exist.", *pp); + r = -ENXIO; + } + + } else if (streq(verb, "help")) + show_unit_help(&info); + else if (streq(verb, "status")) { + print_status_info(bus, &info, ellipsized); + + if (info.active_state && STR_IN_SET(info.active_state, "inactive", "failed")) + r = EXIT_PROGRAM_NOT_RUNNING; + else + r = EXIT_PROGRAM_RUNNING_OR_SERVICE_OK; + } + + return r; +} + +static int get_unit_dbus_path_by_pid( + sd_bus *bus, + uint32_t pid, + char **unit) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + char *u; + int r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitByPID", + &error, + &reply, + "u", pid); + 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) + return bus_log_parse_error(r); + + u = strdup(u); + if (!u) + return log_oom(); + + *unit = u; + return 0; +} + +static int show_all( + const char* verb, + sd_bus *bus, + bool show_properties, + bool *new_line, + bool *ellipsized) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ UnitInfo *unit_infos = NULL; + const UnitInfo *u; + unsigned c; + int r, ret = 0; + + r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + c = (unsigned) r; + + qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info); + + for (u = unit_infos; u < unit_infos + c; u++) { + _cleanup_free_ char *p = NULL; + + p = unit_dbus_path_from_name(u->id); + if (!p) + return log_oom(); + + r = show_one(verb, bus, p, u->id, show_properties, new_line, ellipsized); + if (r < 0) + return r; + else if (r > 0 && ret == 0) + ret = r; + } + + return ret; +} + +static int show_system_status(sd_bus *bus) { + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; + _cleanup_free_ char *hn = NULL; + _cleanup_(machine_info_clear) struct machine_info mi = {}; + const char *on, *off; + int r; + + hn = gethostname_malloc(); + if (!hn) + return log_oom(); + + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi); + if (r < 0) + return log_error_errno(r, "Failed to read server status: %m"); + + if (streq_ptr(mi.state, "degraded")) { + on = ansi_highlight_red(); + off = ansi_normal(); + } else if (!streq_ptr(mi.state, "running")) { + on = ansi_highlight_yellow(); + off = ansi_normal(); + } else + on = off = ""; + + printf("%s%s%s %s\n", on, special_glyph(BLACK_CIRCLE), off, arg_host ? arg_host : hn); + + printf(" State: %s%s%s\n", + on, strna(mi.state), off); + + printf(" Jobs: %" PRIu32 " queued\n", mi.n_jobs); + printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units); + + printf(" Since: %s; %s\n", + format_timestamp(since2, sizeof(since2), mi.timestamp), + format_timestamp_relative(since1, sizeof(since1), mi.timestamp)); + + printf(" CGroup: %s\n", mi.control_group ?: "/"); + if (IN_SET(arg_transport, + BUS_TRANSPORT_LOCAL, + BUS_TRANSPORT_MACHINE)) { + static const char prefix[] = " "; + unsigned c; + + c = columns(); + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; + else + c = 0; + + show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags()); + } + + return 0; +} + +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(argv); + + show_properties = streq(argv[0], "show"); + show_status = streq(argv[0], "status"); + show_help = streq(argv[0], "help"); + + if (show_help && argc <= 1) { + log_error("This command expects one or more unit names. Did you mean --help?"); + return -EINVAL; + } + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + if (show_status) + /* Increase max number of open files to 16K if we can, we + * might needs this when browsing journal files, which might + * be split up into many files. */ + setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); + + /* If no argument is specified inspect the manager itself */ + if (show_properties && argc <= 1) + return show_one(argv[0], bus, "/org/freedesktop/systemd1", NULL, show_properties, &new_line, &ellipsized); + + if (show_status && argc <= 1) { + + show_system_status(bus); + new_line = true; + + if (arg_all) + ret = show_all(argv[0], bus, false, &new_line, &ellipsized); + } else { + _cleanup_free_ char **patterns = NULL; + char **name; + + STRV_FOREACH(name, strv_skip(argv, 1)) { + _cleanup_free_ char *path = NULL, *unit = NULL; + uint32_t id; + + if (safe_atou32(*name, &id) < 0) { + if (strv_push(&patterns, *name) < 0) + return log_oom(); + + continue; + } else if (show_properties) { + /* Interpret as job id */ + if (asprintf(&path, "/org/freedesktop/systemd1/job/%u", id) < 0) + return log_oom(); + + } else { + /* Interpret as PID */ + r = get_unit_dbus_path_by_pid(bus, id, &path); + if (r < 0) { + ret = r; + continue; + } + + r = unit_name_from_dbus_path(path, &unit); + if (r < 0) + return log_oom(); + } + + r = show_one(argv[0], bus, path, unit, show_properties, &new_line, &ellipsized); + if (r < 0) + return r; + else if (r > 0 && ret == 0) + ret = r; + } + + if (!strv_isempty(patterns)) { + _cleanup_strv_free_ char **names = NULL; + + r = expand_names(bus, patterns, NULL, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); + + STRV_FOREACH(name, names) { + _cleanup_free_ char *path; + + path = unit_dbus_path_from_name(*name); + if (!path) + return log_oom(); + + r = show_one(argv[0], bus, path, *name, show_properties, &new_line, &ellipsized); + if (r < 0) + return r; + if (r > 0 && ret == 0) + ret = r; + } + } + } + + if (ellipsized && !arg_quiet) + printf("Hint: Some lines were ellipsized, use -l to show in full.\n"); + + return ret; +} + +static int cat_file(const char *filename, bool newline) { + _cleanup_close_ int fd; + + fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + printf("%s%s# %s%s\n", + newline ? "\n" : "", + ansi_highlight_blue(), + filename, + ansi_normal()); + fflush(stdout); + + return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false); +} + +static int cat(int argc, char *argv[], void *userdata) { + _cleanup_lookup_paths_free_ LookupPaths lp = {}; + _cleanup_strv_free_ char **names = NULL; + char **name; + sd_bus *bus; + bool first = true; + int r; + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Cannot remotely cat units."); + return -EINVAL; + } + + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + if (r < 0) + return log_error_errno(r, "Failed to determine unit paths: %m"); + + 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"); + + pager_open(arg_no_pager, false); + + STRV_FOREACH(name, names) { + _cleanup_free_ char *fragment_path = NULL; + _cleanup_strv_free_ char **dropin_paths = NULL; + char **path; + + r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths); + if (r < 0) + return r; + else if (r == 0) + return -ENOENT; + + if (first) + first = false; + else + puts(""); + + if (fragment_path) { + r = cat_file(fragment_path, false); + if (r < 0) + return log_warning_errno(r, "Failed to cat %s: %m", fragment_path); + } + + STRV_FOREACH(path, dropin_paths) { + r = cat_file(*path, path == dropin_paths); + if (r < 0) + return log_warning_errno(r, "Failed to cat %s: %m", *path); + } + } + + return 0; +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetUnitProperties"); + if (r < 0) + return bus_log_create_error(r); + + r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + + r = sd_bus_message_append(m, "sb", n, arg_runtime); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + STRV_FOREACH(i, strv_skip(argv, 2)) { + r = bus_append_unit_property_assignment(m, *i); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) + return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r)); + + return 0; +} + +static int daemon_reload(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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + switch (arg_action) { + + case ACTION_RELOAD: + method = "Reload"; + break; + + case ACTION_REEXEC: + method = "Reexecute"; + break; + + case ACTION_SYSTEMCTL: + method = streq(argv[0], "daemon-reexec") ? "Reexecute" : + /* "daemon-reload" */ "Reload"; + break; + + default: + assert_not_reached("Unexpected action"); + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (r < 0) + return bus_log_create_error(r); + + /* Note we use an extra-long timeout here. This is because a reload or reexec means generators are rerun which + * are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can have + * their timeout, and for everything else there's the same time budget in place. */ + + r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); + + /* On reexecution, we expect a disconnect, not a reply */ + if (IN_SET(r, -ETIMEDOUT, -ECONNRESET) && streq(method, "Reexecute")) + r = 0; + + if (r < 0 && arg_action == ACTION_SYSTEMCTL) + return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); + + /* Note that for the legacy commands (i.e. those with action != ACTION_SYSTEMCTL) we support fallbacks to the + * old ways of doing things, hence don't log any error in that case here. */ + + return r < 0 ? r : 0; +} + +static int trivial_method(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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + method = + streq(argv[0], "clear-jobs") || + streq(argv[0], "cancel") ? "ClearJobs" : + streq(argv[0], "reset-failed") ? "ResetFailed" : + streq(argv[0], "halt") ? "Halt" : + streq(argv[0], "reboot") ? "Reboot" : + streq(argv[0], "kexec") ? "KExec" : + streq(argv[0], "exit") ? "Exit" : + /* poweroff */ "PowerOff"; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + &error, + NULL, + NULL); + if (r < 0 && arg_action == ACTION_SYSTEMCTL) + return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + + /* Note that for the legacy commands (i.e. those with action != ACTION_SYSTEMCTL) we support fallbacks to the + * old ways of doing things, hence don't log any error in that case here. */ + + return r < 0 ? r : 0; +} + +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 (argc <= 1) + return trivial_method(argc, argv, userdata); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + q = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ResetFailedUnit", + &error, + NULL, + "s", *name); + if (q < 0) { + log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q)); + if (r == 0) + r = q; + } + } + + return r; +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + pager_open(arg_no_pager, false); + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Environment", + &error, + &reply, + "as"); + 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) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) + puts(text); + 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); + + return 0; +} + +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; + sd_bus *bus; + int r; + + 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 = argv[1]; + + if (argc >= 3) + init = argv[2]; + else { + r = parse_env_file("/proc/cmdline", WHITESPACE, + "init", &cmdline_init, + NULL); + if (r < 0) + log_debug_errno(r, "Failed to parse /proc/cmdline: %m"); + + init = cmdline_init; + } + + init = empty_to_null(init); + if (init) { + const char *root_systemd_path = NULL, *root_init_path = NULL; + + root_systemd_path = strjoina(root, "/" SYSTEMD_BINARY_PATH); + root_init_path = strjoina(root, "/", init); + + /* If the passed init is actually the same as the + * systemd binary, then let's suppress it. */ + if (files_same(root_init_path, root_systemd_path) > 0) + 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( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SwitchRoot", + &error, + NULL, + "ss", root, init); + if (r < 0) + return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r)); + + return 0; +} + +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(argc > 1); + assert(argv); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + method = streq(argv[0], "set-environment") + ? "SetEnvironment" + : "UnsetEnvironment"; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_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) + return log_error_errno(r, "Failed to set environment: %s", bus_error_message(&error, r)); + + return 0; +} + +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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetEnvironment"); + if (r < 0) + return bus_log_create_error(r); + + if (argc < 2) + r = sd_bus_message_append_strv(m, environ); + else { + char **a, **b; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + STRV_FOREACH(a, strv_skip(argv, 1)) { + + if (!env_name_is_valid(*a)) { + log_error("Not a valid environment variable name: %s", *a); + return -EINVAL; + } + + STRV_FOREACH(b, environ) { + const char *eq; + + eq = startswith(*b, *a); + if (eq && *eq == '=') { + + r = sd_bus_message_append(m, "s", *b); + if (r < 0) + return bus_log_create_error(r); + + break; + } + } + } + + r = sd_bus_message_close_container(m); + } + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) + return log_error_errno(r, "Failed to import environment: %s", bus_error_message(&error, r)); + + return 0; +} + +static int enable_sysv_units(const char *verb, char **args) { + int r = 0; + +#if defined(HAVE_SYSV_COMPAT) + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + unsigned f = 0; + + /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ + + if (arg_scope != UNIT_FILE_SYSTEM) + return 0; + + if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) + return 0; + + if (!STR_IN_SET(verb, + "enable", + "disable", + "is-enabled")) + return 0; + + r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); + if (r < 0) + return r; + + r = 0; + while (args[f]) { + + const char *argv[] = { + ROOTLIBEXECDIR "/systemd-sysv-install", + NULL, + NULL, + NULL, + NULL, + }; + + _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; + bool found_native = false, found_sysv; + siginfo_t status; + const char *name; + unsigned c = 1; + pid_t pid; + int j; + + name = args[f++]; + + if (!endswith(name, ".service")) + continue; + + if (path_is_absolute(name)) + continue; + + j = unit_file_exists(arg_scope, &paths, name); + if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) + return log_error_errno(j, "Failed to lookup unit file state: %m"); + found_native = j != 0; + + /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled, + * prefer the native unit */ + if (found_native && streq(verb, "is-enabled")) + continue; + + p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name); + if (!p) + return log_oom(); + + p[strlen(p) - strlen(".service")] = 0; + found_sysv = access(p, F_OK) >= 0; + if (!found_sysv) + continue; + + if (found_native) + log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); + else + log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); + + if (!isempty(arg_root)) + argv[c++] = q = strappend("--root=", arg_root); + + argv[c++] = verb; + argv[c++] = basename(p); + argv[c] = NULL; + + l = strv_join((char**)argv, " "); + if (!l) + return log_oom(); + + log_info("Executing: %s", l); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + execv(argv[0], (char**) argv); + log_error_errno(errno, "Failed to execute %s: %m", argv[0]); + _exit(EXIT_FAILURE); + } + + j = wait_for_terminate(pid, &status); + if (j < 0) + return log_error_errno(j, "Failed to wait for child: %m"); + + if (status.si_code == CLD_EXITED) { + if (streq(verb, "is-enabled")) { + if (status.si_status == 0) { + if (!arg_quiet) + puts("enabled"); + r = 1; + } else { + if (!arg_quiet) + puts("disabled"); + } + + } else if (status.si_status != 0) + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ + } else { + log_error("Unexpected waitid() result."); + return -EPROTO; + } + + if (found_native) + continue; + + /* Remove this entry, so that we don't try enabling it as native unit */ + assert(f > 0); + f--; + assert(args[f] == name); + strv_remove(args, name); + } + +#endif + return r; +} + +static int mangle_names(char **original_names, char ***mangled_names) { + char **i, **l, **name; + int r; + + l = i = new(char*, strv_length(original_names) + 1); + if (!l) + return log_oom(); + + STRV_FOREACH(name, original_names) { + + /* When enabling units qualified path names are OK, + * too, hence allow them explicitly. */ + + if (is_path(*name)) { + *i = strdup(*name); + if (!*i) { + strv_free(l); + return log_oom(); + } + } else { + r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i); + if (r < 0) { + strv_free(l); + return log_error_errno(r, "Failed to mangle unit name: %m"); + } + } + + i++; + } + + *i = NULL; + *mangled_names = l; + + return 0; +} + +static int unit_exists(const char *unit) { + _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; + static const struct bus_properties_map property_map[] = { + { "LoadState", "s", map_string_no_copy, offsetof(UnitStatusInfo, load_state) }, + { "ActiveState", "s", map_string_no_copy, offsetof(UnitStatusInfo, active_state)}, + {}, + }; + UnitStatusInfo info = {}; + sd_bus *bus; + int r; + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + + r = bus_message_map_all_properties(reply, property_map, &info); + if (r < 0) + return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); + + return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive"); +} + +static int enable_unit(int argc, char *argv[], void *userdata) { + _cleanup_strv_free_ char **names = NULL; + const char *verb = argv[0]; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int carries_install_info = -1; + bool ignore_carries_install_info = arg_quiet; + int r; + + if (!argv[1]) + return 0; + + r = mangle_names(strv_skip(argv, 1), &names); + if (r < 0) + return r; + + r = enable_sysv_units(verb, names); + if (r < 0) + return r; + + /* If the operation was fully executed by the SysV compat, let's finish early */ + if (strv_isempty(names)) { + if (arg_no_reload || install_client_side()) + return 0; + return daemon_reload(argc, argv, userdata); + } + + 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; + } else if (streq(verb, "disable")) + r = unit_file_disable(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); + else if (streq(verb, "reenable")) { + r = unit_file_reenable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); + carries_install_info = r; + } else if (streq(verb, "link")) + r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); + else if (streq(verb, "preset")) { + r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes); + } else if (streq(verb, "mask")) + r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); + else if (streq(verb, "unmask")) + r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); + else if (streq(verb, "revert")) + r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes); + else + assert_not_reached("Unknown verb"); + + unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet); + if (r < 0) + goto finish; + r = 0; + } else { + _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; + bool expect_carries_install_info = false; + bool send_runtime = true, send_force = true, send_preset_mode = false; + const char *method; + sd_bus *bus; + + if (STR_IN_SET(verb, "mask", "unmask")) { + r = unit_exists(*names); + if (r < 0) + return r; + if (r == 0) + log_notice("Unit %s does not exist, proceeding anyway.", *names); + } + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + if (streq(verb, "enable")) { + method = "EnableUnitFiles"; + expect_carries_install_info = true; + } else if (streq(verb, "disable")) { + method = "DisableUnitFiles"; + send_force = false; + } else if (streq(verb, "reenable")) { + method = "ReenableUnitFiles"; + expect_carries_install_info = true; + } else if (streq(verb, "link")) + method = "LinkUnitFiles"; + else if (streq(verb, "preset")) { + + if (arg_preset_mode != UNIT_FILE_PRESET_FULL) { + method = "PresetUnitFilesWithMode"; + send_preset_mode = true; + } else + method = "PresetUnitFiles"; + + expect_carries_install_info = true; + ignore_carries_install_info = true; + } else if (streq(verb, "mask")) + method = "MaskUnitFiles"; + else if (streq(verb, "unmask")) { + method = "UnmaskUnitFiles"; + send_force = false; + } else if (streq(verb, "revert")) { + method = "RevertUnitFiles"; + send_runtime = send_force = false; + } else + assert_not_reached("Unknown verb"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, names); + if (r < 0) + return bus_log_create_error(r); + + if (send_preset_mode) { + r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode)); + if (r < 0) + return bus_log_create_error(r); + } + + if (send_runtime) { + r = sd_bus_message_append(m, "b", arg_runtime); + if (r < 0) + return bus_log_create_error(r); + } + + if (send_force) { + r = sd_bus_message_append(m, "b", arg_force); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r)); + + if (expect_carries_install_info) { + r = sd_bus_message_read(reply, "b", &carries_install_info); + if (r < 0) + return bus_log_parse_error(r); + } + + r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); + if (r < 0) + goto finish; + + /* Try to reload if enabled */ + if (!arg_no_reload) + r = daemon_reload(argc, argv, userdata); + else + r = 0; + } + + if (carries_install_info == 0 && !ignore_carries_install_info) + log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n" + "settings in the [Install] section, and DefaultInstance for template units).\n" + "This means they are not meant to be enabled using systemctl.\n" + "Possible reasons for having this kind of units are:\n" + "1) A unit may be statically enabled by being symlinked from another unit's\n" + " .wants/ or .requires/ directory.\n" + "2) A unit's purpose may be to act as a helper for some other unit which has\n" + " a requirement dependency on it.\n" + "3) A unit may be started when needed via activation (socket, path, timer,\n" + " D-Bus, udev, scripted systemctl call, ...).\n" + "4) In case of template units, the unit is meant to be enabled with some\n" + " instance name specified."); + + 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; + + 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(strv_length(new_args), new_args, userdata); + } + +finish: + unit_file_changes_free(changes, n_changes); + + return r; +} + +static int add_dependency(int argc, char *argv[], void *userdata) { + _cleanup_strv_free_ char **names = NULL; + _cleanup_free_ char *target = NULL; + const char *verb = argv[0]; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + UnitDependency dep; + int r = 0; + + if (!argv[1]) + return 0; + + 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(strv_skip(argv, 2), &names); + if (r < 0) + return r; + + if (streq(verb, "add-wants")) + dep = UNIT_WANTS; + else if (streq(verb, "add-requires")) + dep = UNIT_REQUIRES; + else + assert_not_reached("Unknown verb"); + + if (install_client_side()) { + r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); + unit_file_dump_changes(r, "add dependency on", changes, n_changes, arg_quiet); + + if (r > 0) + r = 0; + } else { + _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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "AddDependencyUnitFiles"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, names); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssbb", target, unit_dependency_to_string(dep), arg_runtime, arg_force); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to add dependency: %s", bus_error_message(&error, r)); + + r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); + if (r < 0) + goto finish; + + if (arg_no_reload) { + r = 0; + goto finish; + } + + r = daemon_reload(argc, argv, userdata); + } + +finish: + unit_file_changes_free(changes, n_changes); + + return r; +} + +static int preset_all(int argc, char *argv[], void *userdata) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int r; + + if (install_client_side()) { + r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); + unit_file_dump_changes(r, "preset", changes, n_changes, arg_quiet); + + if (r > 0) + r = 0; + } else { + _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; + + r = acquire_bus(BUS_MANAGER, &bus); + if (r < 0) + return r; + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "PresetAllUnitFiles", + &error, + &reply, + "sbb", + unit_file_preset_mode_to_string(arg_preset_mode), + arg_runtime, + arg_force); + if (r < 0) + return log_error_errno(r, "Failed to preset all units: %s", bus_error_message(&error, r)); + + r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); + if (r < 0) + goto finish; + + if (arg_no_reload) { + r = 0; + goto finish; + } + + r = daemon_reload(argc, argv, userdata); + } + +finish: + unit_file_changes_free(changes, n_changes); + + return r; +} + +static int unit_is_enabled(int argc, char *argv[], void *userdata) { + + _cleanup_strv_free_ char **names = NULL; + bool enabled; + char **name; + int r; + + r = mangle_names(strv_skip(argv, 1), &names); + if (r < 0) + return r; + + r = enable_sysv_units(argv[0], names); + if (r < 0) + return r; + + enabled = r > 0; + + if (install_client_side()) { + + STRV_FOREACH(name, names) { + UnitFileState state; + + 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, + UNIT_FILE_ENABLED, + UNIT_FILE_ENABLED_RUNTIME, + UNIT_FILE_STATIC, + UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED)) + enabled = true; + + if (!arg_quiet) + puts(unit_file_state_to_string(state)); + } + + r = 0; + } 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *s; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitFileState", + &error, + &reply, + "s", *name); + 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) + return bus_log_parse_error(r); + + if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated")) + enabled = true; + + if (!arg_quiet) + puts(s); + } + } + + return enabled ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int is_system_running(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *state = NULL; + sd_bus *bus; + int r; + + if (running_in_chroot() > 0 || (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", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SystemState", + NULL, + &state); + if (r < 0) { + if (!arg_quiet) + puts("unknown"); + return 0; + } + + if (!arg_quiet) + puts(state); + + return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) { + _cleanup_free_ char *t = NULL; + int r; + + assert(new_path); + assert(original_path); + assert(ret_tmp_fn); + + r = tempfn_random(new_path, NULL, &t); + if (r < 0) + return log_error_errno(r, "Failed to determine temporary filename for \"%s\": %m", new_path); + + r = mkdir_parents(new_path, 0755); + 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) + return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t); + + } else if (r < 0) + return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path); + + *ret_tmp_fn = t; + t = NULL; + + return 0; +} + +static int get_file_to_edit( + const LookupPaths *paths, + const char *name, + char **ret_path) { + + _cleanup_free_ char *path = NULL, *run = NULL; + + assert(name); + assert(ret_path); + + path = strjoin(paths->persistent_config, "/", name, NULL); + if (!path) + return log_oom(); + + if (arg_runtime) { + run = strjoin(paths->runtime_config, "/", name, NULL); + if (!run) + return log_oom(); + } + + if (arg_runtime) { + if (access(path, F_OK) >= 0) { + log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path); + return -EEXIST; + } + + *ret_path = run; + run = NULL; + } else { + *ret_path = path; + path = NULL; + } + + return 0; +} + +static int unit_file_create_new( + const LookupPaths *paths, + const char *unit_name, + const char *suffix, + char **ret_new_path, + char **ret_tmp_path) { + + char *tmp_new_path, *tmp_tmp_path, *ending; + int r; + + assert(unit_name); + assert(ret_new_path); + assert(ret_tmp_path); + + ending = strjoina(unit_name, suffix); + r = get_file_to_edit(paths, ending, &tmp_new_path); + if (r < 0) + return r; + + r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path); + if (r < 0) { + free(tmp_new_path); + return r; + } + + *ret_new_path = tmp_new_path; + *ret_tmp_path = tmp_tmp_path; + + return 0; +} + +static int unit_file_create_copy( + const LookupPaths *paths, + const char *unit_name, + const char *fragment_path, + char **ret_new_path, + char **ret_tmp_path) { + + char *tmp_new_path, *tmp_tmp_path; + int r; + + assert(fragment_path); + assert(unit_name); + assert(ret_new_path); + assert(ret_tmp_path); + + r = get_file_to_edit(paths, unit_name, &tmp_new_path); + if (r < 0) + return r; + + if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) { + char response; + + r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", tmp_new_path, fragment_path); + if (r < 0) { + free(tmp_new_path); + return r; + } + if (response != 'y') { + log_warning("%s ignored", unit_name); + free(tmp_new_path); + return -EKEYREJECTED; + } + } + + r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path); + if (r < 0) { + free(tmp_new_path); + return r; + } + + *ret_new_path = tmp_new_path; + *ret_tmp_path = tmp_tmp_path; + + return 0; +} + +static int run_editor(char **paths) { + pid_t pid; + int r; + + assert(paths); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + + if (pid == 0) { + const char **args; + char *editor, **editor_args = NULL; + char **tmp_path, **original_path, *p; + unsigned n_editor_args = 0, i = 1; + size_t argc; + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + argc = strv_length(paths)/2 + 1; + + /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL + * If neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present, + * we try to execute well known editors + */ + editor = getenv("SYSTEMD_EDITOR"); + if (!editor) + editor = getenv("EDITOR"); + if (!editor) + editor = getenv("VISUAL"); + + if (!isempty(editor)) { + editor_args = strv_split(editor, WHITESPACE); + if (!editor_args) { + (void) log_oom(); + _exit(EXIT_FAILURE); + } + n_editor_args = strv_length(editor_args); + argc += n_editor_args - 1; + } + args = newa(const char*, argc + 1); + + if (n_editor_args > 0) { + args[0] = editor_args[0]; + for (; i < n_editor_args; i++) + args[i] = editor_args[i]; + } + + STRV_FOREACH_PAIR(original_path, tmp_path, paths) { + args[i] = *tmp_path; + i++; + } + args[i] = NULL; + + if (n_editor_args > 0) + execvp(args[0], (char* const*) args); + + FOREACH_STRING(p, "editor", "nano", "vim", "vi") { + args[0] = p; + execvp(p, (char* const*) args); + /* We do not fail if the editor doesn't exist + * because we want to try each one of them before + * failing. + */ + if (errno != ENOENT) { + log_error_errno(errno, "Failed to execute %s: %m", editor); + _exit(EXIT_FAILURE); + } + } + + log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR, $EDITOR or $VISUAL."); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn("editor", pid, true); + if (r < 0) + return log_error_errno(r, "Failed to wait for child: %m"); + + return 0; +} + +static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { + _cleanup_lookup_paths_free_ LookupPaths lp = {}; + char **name; + int r; + + assert(names); + assert(paths); + + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + if (r < 0) + return r; + + STRV_FOREACH(name, names) { + _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL; + + r = unit_find_paths(bus, *name, &lp, &path, NULL); + if (r < 0) + return r; + else if (!arg_force) { + if (r == 0) { + log_error("Run 'systemctl edit --force %s' to create a new unit.", *name); + return -ENOENT; + } else if (!path) { + // FIXME: support units with path==NULL (no FragmentPath) + log_error("No fragment exists for %s.", *name); + return -ENOENT; + } + } + + if (path) { + if (arg_full) + r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path); + else + r = unit_file_create_new(&lp, *name, ".d/override.conf", &new_path, &tmp_path); + } else + r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path); + if (r < 0) + return r; + + r = strv_push_pair(paths, new_path, tmp_path); + if (r < 0) + return log_oom(); + new_path = tmp_path = NULL; + } + + return 0; +} + +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; + + if (!on_tty()) { + log_error("Cannot edit units if not on a tty."); + return -EINVAL; + } + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Cannot edit units remotely."); + return -EINVAL; + } + + 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"); + + r = find_paths_to_edit(bus, names, &paths); + if (r < 0) + return r; + + if (strv_isempty(paths)) + return -ENOENT; + + r = run_editor(paths); + if (r < 0) + 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 (null_or_empty_path(*tmp)) { + 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); + goto end; + } + } + + r = 0; + + if (!arg_no_reload && !install_client_side()) + r = daemon_reload(argc, argv, userdata); + +end: + STRV_FOREACH_PAIR(original, tmp, paths) { + (void) unlink(*tmp); + + /* Removing empty dropin dirs */ + if (!arg_full) { + _cleanup_free_ char *dir; + + dir = dirname_malloc(*original); + if (!dir) + return log_oom(); + + /* no need to check if the dir is empty, rmdir + * does nothing if it is not the case. + */ + (void) rmdir(dir); + } + } + + return r; +} + +static void systemctl_help(void) { + + pager_open(arg_no_pager, false); + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Query or send control commands to the systemd manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --system Connect to system manager\n" + " --user Connect to user service manager\n" + " -H --host=[USER@]HOST\n" + " Operate on remote host\n" + " -M --machine=CONTAINER\n" + " Operate on local container\n" + " -t --type=TYPE List units of a particular type\n" + " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n" + " -p --property=NAME Show only properties by this name\n" + " -a --all Show all loaded units/properties, including dead/empty\n" + " ones. To list all units installed on the system, use\n" + " the 'list-unit-files' command instead.\n" + " -l --full Don't ellipsize unit names on output\n" + " -r --recursive Show unit list of host and local containers\n" + " --reverse Show reverse dependencies with 'list-dependencies'\n" + " --job-mode=MODE Specify how to deal with already queued jobs, when\n" + " queueing a new job\n" + " --show-types When showing sockets, explicitly show their type\n" + " --value When showing properties, only print the value\n" + " -i --ignore-inhibitors\n" + " When shutting down or sleeping, ignore inhibitors\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " --now Start or stop unit in addition to enabling or disabling it\n" + " -q --quiet Suppress output\n" + " --no-block Do not wait until operation finished\n" + " --no-wall Don't send wall message before halt/power-off/reboot\n" + " --no-reload Don't reload daemon after en-/dis-abling unit files\n" + " --no-legend Do not print a legend (column headers and hints)\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password\n" + " Do not ask for system passwords\n" + " --global Enable/disable unit files globally\n" + " --runtime Enable unit files only temporarily until next reboot\n" + " -f --force When enabling unit files, override existing symlinks\n" + " When shutting down, execute action immediately\n" + " --preset-mode= Apply only enable, only disable, or all presets\n" + " --root=PATH Enable unit files in the specified root directory\n" + " -n --lines=INTEGER Number of journal entries to show\n" + " -o --output=STRING Change journal output mode (short, short-iso,\n" + " short-precise, short-monotonic, verbose,\n" + " export, json, json-pretty, json-sse, cat)\n" + " --firmware-setup Tell the firmware to show the setup menu on next boot\n" + " --plain Print unit dependencies as a list instead of a tree\n\n" + "Unit Commands:\n" + " list-units [PATTERN...] List loaded units\n" + " list-sockets [PATTERN...] List loaded sockets ordered by address\n" + " list-timers [PATTERN...] List loaded timers ordered by next elapse\n" + " start NAME... Start (activate) one or more units\n" + " stop NAME... Stop (deactivate) one or more units\n" + " reload NAME... Reload one or more units\n" + " restart NAME... Start or restart one or more units\n" + " try-restart NAME... Restart one or more units if active\n" + " reload-or-restart NAME... Reload one or more units if possible,\n" + " otherwise start or restart\n" + " try-reload-or-restart NAME... If active, reload one or more units,\n" + " if supported, otherwise restart\n" + " isolate NAME Start one unit and stop all others\n" + " kill NAME... Send signal to processes of a unit\n" + " is-active PATTERN... Check whether units are active\n" + " is-failed PATTERN... Check whether units are failed\n" + " status [PATTERN...|PID...] Show runtime status of one or more units\n" + " show [PATTERN...|JOB...] Show properties of one or more\n" + " units/jobs or the manager\n" + " cat PATTERN... Show files and drop-ins of one or more units\n" + " set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n" + " help PATTERN...|PID... Show manual for one or more units\n" + " reset-failed [PATTERN...] Reset failed state for all, one, or more\n" + " units\n" + " list-dependencies [NAME] Recursively show units which are required\n" + " or wanted by this unit or by which this\n" + " unit is required or wanted\n\n" + "Unit File Commands:\n" + " list-unit-files [PATTERN...] List installed unit files\n" + " enable [NAME...|PATH...] Enable one or more unit files\n" + " disable NAME... Disable one or more unit files\n" + " reenable NAME... Reenable one or more unit files\n" + " preset NAME... Enable/disable one or more unit files\n" + " based on preset configuration\n" + " preset-all Enable/disable all unit files based on\n" + " preset configuration\n" + " is-enabled NAME... Check whether unit files are enabled\n" + " mask NAME... Mask one or more units\n" + " unmask NAME... Unmask one or more units\n" + " link PATH... Link one or more units files into\n" + " the search path\n" + " revert NAME... Revert one or more unit files to vendor\n" + " version\n" + " add-wants TARGET NAME... Add 'Wants' dependency for the target\n" + " on specified one or more units\n" + " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" + " on specified one or more units\n" + " edit NAME... Edit one or more unit files\n" + " get-default Get the name of the default target\n" + " set-default NAME Set the default target\n\n" + "Machine Commands:\n" + " list-machines [PATTERN...] List local containers and host\n\n" + "Job Commands:\n" + " list-jobs [PATTERN...] List jobs\n" + " cancel [JOB...] Cancel all, one, or more jobs\n\n" + "Environment Commands:\n" + " show-environment Dump environment\n" + " set-environment NAME=VALUE... Set one or more environment variables\n" + " unset-environment NAME... Unset one or more environment variables\n" + " import-environment [NAME...] Import all or some environment variables\n\n" + "Manager Lifecycle Commands:\n" + " daemon-reload Reload systemd manager configuration\n" + " daemon-reexec Reexecute systemd manager\n\n" + "System Commands:\n" + " is-system-running Check whether system is fully running\n" + " default Enter system default mode\n" + " rescue Enter system rescue mode\n" + " emergency Enter system emergency mode\n" + " halt Shut down and halt the system\n" + " poweroff Shut down and power-off the system\n" + " reboot [ARG] Shut down and reboot the system\n" + " kexec Shut down and reboot the system with kexec\n" + " exit [EXIT_CODE] Request user instance or container exit\n" + " switch-root ROOT [INIT] Change to a different root file system\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Hibernate and suspend the system\n", + program_invocation_short_name); +} + +static void halt_help(void) { + printf("%s [OPTIONS...]%s\n\n" + "%s the system.\n\n" + " --help Show this help\n" + " --halt Halt the machine\n" + " -p --poweroff Switch off the machine\n" + " --reboot Reboot the machine\n" + " -f --force Force immediate halt/power-off/reboot\n" + " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n" + " -d --no-wtmp Don't write wtmp record\n" + " --no-wall Don't send wall message before halt/power-off/reboot\n", + program_invocation_short_name, + arg_action == ACTION_REBOOT ? " [ARG]" : "", + arg_action == ACTION_REBOOT ? "Reboot" : + arg_action == ACTION_POWEROFF ? "Power off" : + "Halt"); +} + +static void shutdown_help(void) { + printf("%s [OPTIONS...] [TIME] [WALL...]\n\n" + "Shut down the system.\n\n" + " --help Show this help\n" + " -H --halt Halt the machine\n" + " -P --poweroff Power-off the machine\n" + " -r --reboot Reboot the machine\n" + " -h Equivalent to --poweroff, overridden by --halt\n" + " -k Don't halt/power-off/reboot, just send warnings\n" + " --no-wall Don't send wall message before halt/power-off/reboot\n" + " -c Cancel a pending shutdown\n", + program_invocation_short_name); +} + +static void telinit_help(void) { + printf("%s [OPTIONS...] {COMMAND}\n\n" + "Send control commands to the init daemon.\n\n" + " --help Show this help\n" + " --no-wall Don't send wall message before halt/power-off/reboot\n\n" + "Commands:\n" + " 0 Power-off the machine\n" + " 6 Reboot the machine\n" + " 2, 3, 4, 5 Start runlevelX.target unit\n" + " 1, s, S Enter rescue mode\n" + " q, Q Reload init daemon configuration\n" + " u, U Reexecute init daemon\n", + program_invocation_short_name); +} + +static void runlevel_help(void) { + printf("%s [OPTIONS...]\n\n" + "Prints the previous and current runlevel of the init system.\n\n" + " --help Show this help\n", + program_invocation_short_name); +} + +static void help_types(void) { + int i; + + if (!arg_no_legend) + puts("Available unit types:"); + 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[]) { + + enum { + ARG_FAIL = 0x100, + ARG_REVERSE, + ARG_AFTER, + ARG_BEFORE, + ARG_SHOW_TYPES, + ARG_IRREVERSIBLE, + ARG_IGNORE_DEPENDENCIES, + ARG_VALUE, + ARG_VERSION, + ARG_USER, + ARG_SYSTEM, + ARG_GLOBAL, + ARG_NO_BLOCK, + ARG_NO_LEGEND, + ARG_NO_PAGER, + ARG_NO_WALL, + ARG_ROOT, + ARG_NO_RELOAD, + ARG_KILL_WHO, + ARG_NO_ASK_PASSWORD, + ARG_FAILED, + ARG_RUNTIME, + ARG_FORCE, + ARG_PLAIN, + ARG_STATE, + ARG_JOB_MODE, + ARG_PRESET_MODE, + ARG_FIRMWARE_SETUP, + ARG_NOW, + ARG_MESSAGE, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "type", required_argument, NULL, 't' }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "reverse", no_argument, NULL, ARG_REVERSE }, + { "after", no_argument, NULL, ARG_AFTER }, + { "before", no_argument, NULL, ARG_BEFORE }, + { "show-types", no_argument, NULL, ARG_SHOW_TYPES }, + { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */ + { "full", no_argument, NULL, 'l' }, + { "job-mode", required_argument, NULL, ARG_JOB_MODE }, + { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */ + { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ + { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ + { "ignore-inhibitors", no_argument, NULL, 'i' }, + { "value", no_argument, NULL, ARG_VALUE }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "global", no_argument, NULL, ARG_GLOBAL }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, + { "quiet", no_argument, NULL, 'q' }, + { "root", required_argument, NULL, ARG_ROOT }, + { "force", no_argument, NULL, ARG_FORCE }, + { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "runtime", no_argument, NULL, ARG_RUNTIME }, + { "lines", required_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + { "plain", no_argument, NULL, ARG_PLAIN }, + { "state", required_argument, NULL, ARG_STATE }, + { "recursive", no_argument, NULL, 'r' }, + { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, + { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "now", no_argument, NULL, ARG_NOW }, + { "message", required_argument, NULL, ARG_MESSAGE }, + {} + }; + + const char *p; + int c, r; + + assert(argc >= 0); + assert(argv); + + /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ + arg_ask_password = true; + + while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0) + + switch (c) { + + case 'h': + systemctl_help(); + return 0; + + case ARG_VERSION: + return version(); + + case 't': { + if (isempty(optarg)) { + log_error("--type requires arguments."); + return -EINVAL; + } + + p = optarg; + for (;;) { + _cleanup_free_ char *type = NULL; + + r = extract_first_word(&p, &type, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse type: %s", optarg); + + if (r == 0) + break; + + if (streq(type, "help")) { + help_types(); + return 0; + } + + if (unit_type_from_string(type) >= 0) { + if (strv_push(&arg_types, type) < 0) + return log_oom(); + type = NULL; + continue; + } + + /* It's much nicer to use --state= for + * load states, but let's support this + * in --types= too for compatibility + * with old versions */ + if (unit_load_state_from_string(type) >= 0) { + if (strv_push(&arg_states, type) < 0) + return log_oom(); + type = NULL; + continue; + } + + log_error("Unknown unit type or load state '%s'.", type); + log_info("Use -t help to see a list of allowed values."); + return -EINVAL; + } + + break; + } + + case 'p': { + /* Make sure that if the empty property list + was specified, we won't show any properties. */ + if (isempty(optarg) && !arg_properties) { + arg_properties = new0(char*, 1); + if (!arg_properties) + return log_oom(); + } else { + p = optarg; + for (;;) { + _cleanup_free_ char *prop = NULL; + + r = extract_first_word(&p, &prop, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse property: %s", optarg); + + if (r == 0) + break; + + if (strv_push(&arg_properties, prop) < 0) + return log_oom(); + + prop = NULL; + } + } + + /* If the user asked for a particular + * property, show it to him, even if it is + * empty. */ + arg_all = true; + + break; + } + + case 'a': + arg_all = true; + break; + + case ARG_REVERSE: + arg_dependency = DEPENDENCY_REVERSE; + break; + + case ARG_AFTER: + arg_dependency = DEPENDENCY_AFTER; + break; + + case ARG_BEFORE: + arg_dependency = DEPENDENCY_BEFORE; + break; + + case ARG_SHOW_TYPES: + arg_show_types = true; + break; + + case ARG_VALUE: + arg_value = true; + break; + + case ARG_JOB_MODE: + arg_job_mode = optarg; + break; + + case ARG_FAIL: + arg_job_mode = "fail"; + break; + + case ARG_IRREVERSIBLE: + arg_job_mode = "replace-irreversibly"; + break; + + case ARG_IGNORE_DEPENDENCIES: + arg_job_mode = "ignore-dependencies"; + break; + + case ARG_USER: + arg_scope = UNIT_FILE_USER; + break; + + case ARG_SYSTEM: + arg_scope = UNIT_FILE_SYSTEM; + break; + + case ARG_GLOBAL: + arg_scope = UNIT_FILE_GLOBAL; + break; + + case ARG_NO_BLOCK: + arg_no_block = true; + break; + + case ARG_NO_LEGEND: + arg_no_legend = true; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_WALL: + arg_no_wall = true; + break; + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, false, &arg_root); + if (r < 0) + return r; + break; + + case 'l': + arg_full = true; + break; + + case ARG_FAILED: + if (strv_extend(&arg_states, "failed") < 0) + return log_oom(); + + break; + + case 'q': + arg_quiet = true; + break; + + case ARG_FORCE: + arg_force++; + break; + + case 'f': + arg_force++; + break; + + case ARG_NO_RELOAD: + arg_no_reload = true; + break; + + case ARG_KILL_WHO: + arg_kill_who = optarg; + break; + + case 's': + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { + log_error("Failed to parse signal string %s.", optarg); + return -EINVAL; + } + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_RUNTIME: + arg_runtime = true; + break; + + case 'n': + if (safe_atou(optarg, &arg_lines) < 0) { + log_error("Failed to parse lines '%s'", optarg); + return -EINVAL; + } + break; + + case 'o': + arg_output = output_mode_from_string(optarg); + if (arg_output < 0) { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + break; + + case 'i': + arg_ignore_inhibitors = true; + break; + + case ARG_PLAIN: + arg_plain = true; + break; + + case ARG_FIRMWARE_SETUP: + arg_firmware_setup = true; + break; + + case ARG_STATE: { + if (isempty(optarg)) { + log_error("--signal requires arguments."); + return -EINVAL; + } + + p = optarg; + for (;;) { + _cleanup_free_ char *s = NULL; + + r = extract_first_word(&p, &s, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse signal: %s", optarg); + + 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; + } + + case 'r': + if (geteuid() != 0) { + log_error("--recursive requires root privileges."); + return -EPERM; + } + + arg_recursive = true; + break; + + case ARG_PRESET_MODE: + + arg_preset_mode = unit_file_preset_mode_from_string(optarg); + if (arg_preset_mode < 0) { + log_error("Failed to parse preset mode: %s.", optarg); + return -EINVAL; + } + + break; + + case ARG_NOW: + arg_now = true; + break; + + case ARG_MESSAGE: + if (strv_extend(&arg_wall, optarg) < 0) + return log_oom(); + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) { + log_error("Cannot access user instance remotely."); + return -EINVAL; + } + + return 1; +} + +static int halt_parse_argv(int argc, char *argv[]) { + + enum { + ARG_HELP = 0x100, + ARG_HALT, + ARG_REBOOT, + ARG_NO_WALL + }; + + static const struct option options[] = { + { "help", no_argument, NULL, ARG_HELP }, + { "halt", no_argument, NULL, ARG_HALT }, + { "poweroff", no_argument, NULL, 'p' }, + { "reboot", no_argument, NULL, ARG_REBOOT }, + { "force", no_argument, NULL, 'f' }, + { "wtmp-only", no_argument, NULL, 'w' }, + { "no-wtmp", no_argument, NULL, 'd' }, + { "no-sync", no_argument, NULL, 'n' }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, + {} + }; + + int c, r, runlevel; + + assert(argc >= 0); + assert(argv); + + if (utmp_get_runlevel(&runlevel, NULL) >= 0) + if (runlevel == '0' || runlevel == '6') + arg_force = 2; + + while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) + switch (c) { + + case ARG_HELP: + halt_help(); + return 0; + + case ARG_HALT: + arg_action = ACTION_HALT; + break; + + case 'p': + if (arg_action != ACTION_REBOOT) + arg_action = ACTION_POWEROFF; + break; + + case ARG_REBOOT: + arg_action = ACTION_REBOOT; + break; + + case 'f': + arg_force = 2; + break; + + case 'w': + arg_dry = true; + break; + + case 'd': + arg_no_wtmp = true; + break; + + case 'n': + arg_no_sync = true; + break; + + case ARG_NO_WALL: + arg_no_wall = true; + break; + + case 'i': + case 'h': + /* Compatibility nops */ + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { + r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL); + if (r < 0) + return r; + } else if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + return 1; +} + +static int parse_shutdown_time_spec(const char *t, usec_t *_u) { + assert(t); + assert(_u); + + if (streq(t, "now")) + *_u = 0; + else if (!strchr(t, ':')) { + uint64_t u; + + if (safe_atou64(t, &u) < 0) + return -EINVAL; + + *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; + } else { + char *e = NULL; + long hour, minute; + struct tm tm = {}; + time_t s; + usec_t n; + + errno = 0; + hour = strtol(t, &e, 10); + if (errno > 0 || *e != ':' || hour < 0 || hour > 23) + return -EINVAL; + + minute = strtol(e+1, &e, 10); + if (errno > 0 || *e != 0 || minute < 0 || minute > 59) + return -EINVAL; + + n = now(CLOCK_REALTIME); + s = (time_t) (n / USEC_PER_SEC); + + assert_se(localtime_r(&s, &tm)); + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + tm.tm_sec = 0; + + assert_se(s = mktime(&tm)); + + *_u = (usec_t) s * USEC_PER_SEC; + + while (*_u <= n) + *_u += USEC_PER_DAY; + } + + return 0; +} + +static int shutdown_parse_argv(int argc, char *argv[]) { + + enum { + ARG_HELP = 0x100, + ARG_NO_WALL + }; + + static const struct option options[] = { + { "help", no_argument, NULL, ARG_HELP }, + { "halt", no_argument, NULL, 'H' }, + { "poweroff", no_argument, NULL, 'P' }, + { "reboot", no_argument, NULL, 'r' }, + { "kexec", no_argument, NULL, 'K' }, /* not documented extension */ + { "no-wall", no_argument, NULL, ARG_NO_WALL }, + {} + }; + + char **wall = NULL; + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "HPrhkKtafFc", options, NULL)) >= 0) + switch (c) { + + case ARG_HELP: + shutdown_help(); + return 0; + + case 'H': + arg_action = ACTION_HALT; + break; + + case 'P': + arg_action = ACTION_POWEROFF; + break; + + case 'r': + if (kexec_loaded()) + arg_action = ACTION_KEXEC; + else + arg_action = ACTION_REBOOT; + break; + + case 'K': + arg_action = ACTION_KEXEC; + break; + + case 'h': + if (arg_action != ACTION_HALT) + arg_action = ACTION_POWEROFF; + break; + + case 'k': + arg_dry = true; + break; + + case ARG_NO_WALL: + arg_no_wall = true; + break; + + case 't': + case 'a': + case 'f': + case 'F': + /* Compatibility nops */ + break; + + case 'c': + arg_action = ACTION_CANCEL_SHUTDOWN; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { + r = parse_shutdown_time_spec(argv[optind], &arg_when); + if (r < 0) { + log_error("Failed to parse time specification: %s", argv[optind]); + return r; + } + } else + arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; + + if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) + /* No time argument for shutdown cancel */ + wall = argv + optind; + else if (argc > optind + 1) + /* We skip the time argument */ + wall = argv + optind + 1; + + if (wall) { + arg_wall = strv_copy(wall); + if (!arg_wall) + return log_oom(); + } + + optind = argc; + + return 1; +} + +static int telinit_parse_argv(int argc, char *argv[]) { + + enum { + ARG_HELP = 0x100, + ARG_NO_WALL + }; + + static const struct option options[] = { + { "help", no_argument, NULL, ARG_HELP }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, + {} + }; + + static const struct { + char from; + enum action to; + } table[] = { + { '0', ACTION_POWEROFF }, + { '6', ACTION_REBOOT }, + { '1', ACTION_RESCUE }, + { '2', ACTION_RUNLEVEL2 }, + { '3', ACTION_RUNLEVEL3 }, + { '4', ACTION_RUNLEVEL4 }, + { '5', ACTION_RUNLEVEL5 }, + { 's', ACTION_RESCUE }, + { 'S', ACTION_RESCUE }, + { 'q', ACTION_RELOAD }, + { 'Q', ACTION_RELOAD }, + { 'u', ACTION_REEXEC }, + { 'U', ACTION_REEXEC } + }; + + unsigned i; + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) + switch (c) { + + case ARG_HELP: + telinit_help(); + return 0; + + case ARG_NO_WALL: + arg_no_wall = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind >= argc) { + log_error("%s: required argument missing.", program_invocation_short_name); + return -EINVAL; + } + + if (optind + 1 < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + if (strlen(argv[optind]) != 1) { + log_error("Expected single character argument."); + return -EINVAL; + } + + for (i = 0; i < ELEMENTSOF(table); i++) + if (table[i].from == argv[optind][0]) + break; + + if (i >= ELEMENTSOF(table)) { + log_error("Unknown command '%s'.", argv[optind]); + return -EINVAL; + } + + arg_action = table[i].to; + + optind++; + + return 1; +} + +static int runlevel_parse_argv(int argc, char *argv[]) { + + enum { + ARG_HELP = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, ARG_HELP }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) + switch (c) { + + case ARG_HELP: + runlevel_help(); + return 0; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + return 1; +} + +static int parse_argv(int argc, char *argv[]) { + assert(argc >= 0); + assert(argv); + + if (program_invocation_short_name) { + + if (strstr(program_invocation_short_name, "halt")) { + arg_action = ACTION_HALT; + return halt_parse_argv(argc, argv); + } else if (strstr(program_invocation_short_name, "poweroff")) { + arg_action = ACTION_POWEROFF; + return halt_parse_argv(argc, argv); + } else if (strstr(program_invocation_short_name, "reboot")) { + if (kexec_loaded()) + arg_action = ACTION_KEXEC; + else + arg_action = ACTION_REBOOT; + return halt_parse_argv(argc, argv); + } else if (strstr(program_invocation_short_name, "shutdown")) { + arg_action = ACTION_POWEROFF; + return shutdown_parse_argv(argc, argv); + } else if (strstr(program_invocation_short_name, "init")) { + + if (sd_booted() > 0) { + arg_action = _ACTION_INVALID; + return telinit_parse_argv(argc, argv); + } else { + /* Hmm, so some other init system is + * running, we need to forward this + * request to it. For now we simply + * guess that it is Upstart. */ + + execv(TELINIT, argv); + + log_error("Couldn't find an alternative telinit implementation to spawn."); + return -EIO; + } + + } else if (strstr(program_invocation_short_name, "runlevel")) { + arg_action = ACTION_RUNLEVEL; + return runlevel_parse_argv(argc, argv); + } + } + + arg_action = ACTION_SYSTEMCTL; + return systemctl_parse_argv(argc, argv); +} + +#ifdef HAVE_SYSV_COMPAT +_pure_ static int action_to_runlevel(void) { + + static const char table[_ACTION_MAX] = { + [ACTION_HALT] = '0', + [ACTION_POWEROFF] = '0', + [ACTION_REBOOT] = '6', + [ACTION_RUNLEVEL2] = '2', + [ACTION_RUNLEVEL3] = '3', + [ACTION_RUNLEVEL4] = '4', + [ACTION_RUNLEVEL5] = '5', + [ACTION_RESCUE] = '1' + }; + + assert(arg_action < _ACTION_MAX); + + return table[arg_action]; +} +#endif + +static int talk_initctl(void) { +#ifdef HAVE_SYSV_COMPAT + struct init_request request = { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL + }; + + _cleanup_close_ int fd = -1; + char rl; + int r; + + rl = action_to_runlevel(); + if (!rl) + return 0; + + request.runlevel = rl; + + fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); + } + + r = loop_write(fd, &request, sizeof(request), false); + if (r < 0) + return log_error_errno(r, "Failed to write to "INIT_FIFO": %m"); + + return 1; +#else + return 0; +#endif +} + +static int systemctl_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_NOCHROOT, list_units }, + { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files }, + { "list-sockets", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_sockets }, + { "list-timers", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_timers }, + { "list-jobs", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_jobs }, + { "list-machines", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_machines }, + { "clear-jobs", VERB_ANY, 1, VERB_NOCHROOT, trivial_method }, + { "cancel", VERB_ANY, VERB_ANY, VERB_NOCHROOT, cancel_job }, + { "start", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "stop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "condstop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ + { "reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "reload-or-try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatbility with old systemctl <= 228 */ + { "try-reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, + { "force-reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with SysV */ + { "condreload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ + { "condrestart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with RH */ + { "isolate", 2, 2, VERB_NOCHROOT, start_unit }, + { "kill", 2, VERB_ANY, VERB_NOCHROOT, kill_unit }, + { "is-active", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, + { "check", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, + { "is-failed", 2, VERB_ANY, VERB_NOCHROOT, check_unit_failed }, + { "show", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, + { "cat", 2, VERB_ANY, VERB_NOCHROOT, cat }, + { "status", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, + { "help", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, + { "daemon-reload", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, + { "daemon-reexec", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, + { "show-environment", VERB_ANY, 1, VERB_NOCHROOT, show_environment }, + { "set-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, + { "unset-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, + { "import-environment", VERB_ANY, VERB_ANY, VERB_NOCHROOT, import_environment }, + { "halt", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "poweroff", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "reboot", VERB_ANY, 2, VERB_NOCHROOT, start_system_special }, + { "kexec", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "suspend", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "hibernate", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "hybrid-sleep", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "default", VERB_ANY, 1, VERB_NOCHROOT, start_special }, + { "rescue", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "emergency", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, + { "exit", VERB_ANY, 2, VERB_NOCHROOT, start_special }, + { "reset-failed", VERB_ANY, VERB_ANY, VERB_NOCHROOT, 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 }, + { "revert", 2, VERB_ANY, 0, enable_unit }, + { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root }, + { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies }, + { "set-default", 2, 2, 0, set_default }, + { "get-default", VERB_ANY, 1, 0, get_default }, + { "set-property", 3, VERB_ANY, VERB_NOCHROOT, 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, VERB_NOCHROOT, edit }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +static int reload_with_fallback(void) { + + /* First, try systemd via D-Bus. */ + if (daemon_reload(0, NULL, NULL) >= 0) + return 0; + + /* Nothing else worked, so let's try signals */ + assert(IN_SET(arg_action, ACTION_RELOAD, ACTION_REEXEC)); + + if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) + return log_error_errno(errno, "kill() failed: %m"); + + return 0; +} + +static int start_with_fallback(void) { + + /* 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) + return 0; + + log_error("Failed to talk to init daemon."); + return -EIO; +} + +static int halt_now(enum action a) { + int r; + + /* The kernel will automaticall flush ATA disks and suchlike + * on reboot(), but the file systems need to be synce'd + * explicitly in advance. */ + if (!arg_no_sync) + (void) sync(); + + /* Make sure C-A-D is handled by the kernel from this point + * on... */ + (void) reboot(RB_ENABLE_CAD); + + switch (a) { + + case ACTION_HALT: + log_info("Halting."); + (void) reboot(RB_HALT_SYSTEM); + return -errno; + + case ACTION_POWEROFF: + log_info("Powering off."); + (void) reboot(RB_POWER_OFF); + return -errno; + + case ACTION_KEXEC: + case ACTION_REBOOT: { + _cleanup_free_ char *param = NULL; + + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { + log_info("Rebooting with argument '%s'.", param); + (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); + } + + log_info("Rebooting."); + (void) reboot(RB_AUTOBOOT); + return -errno; + } + + default: + assert_not_reached("Unknown action."); + } +} + +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 = 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); + + (void) logind_set_wall_message(); + + 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_dry || arg_force > 0) { + log_error("Must be root."); + return -EPERM; + } + + /* 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 = logind_reboot(arg_action); + if (r >= 0) + return r; + if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + /* 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_dry && !arg_force) + return start_with_fallback(); + + assert(geteuid() == 0); + + if (!arg_no_wtmp) { + if (sd_booted() > 0) + log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); + else { + r = utmp_put_shutdown(); + if (r < 0) + log_warning_errno(r, "Failed to write utmp record: %m"); + } + } + + if (arg_dry) + return 0; + + r = halt_now(arg_action); + return log_error_errno(r, "Failed to reboot: %m"); +} + +static int runlevel_main(void) { + int r, runlevel, previous; + + r = utmp_get_runlevel(&runlevel, &previous); + if (r < 0) { + puts("unknown"); + return r; + } + + printf("%c %c\n", + previous <= 0 ? 'N' : previous, + runlevel <= 0 ? 'N' : runlevel); + + 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[]) { + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + sigbus_install(); + + /* Explicitly not on_tty() to avoid setting cached value. + * This becomes relevant for piping output which might be + * ellipsized. */ + original_stdout_is_tty = isatty(STDOUT_FILENO); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) { + log_info("Running in chroot, ignoring request."); + r = 0; + goto finish; + } + + /* 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(argc, argv); + break; + + case ACTION_HALT: + case ACTION_POWEROFF: + case ACTION_REBOOT: + case ACTION_KEXEC: + r = halt_main(); + break; + + case ACTION_RUNLEVEL2: + case ACTION_RUNLEVEL3: + case ACTION_RUNLEVEL4: + case ACTION_RUNLEVEL5: + case ACTION_RESCUE: + case ACTION_EMERGENCY: + case ACTION_DEFAULT: + r = start_with_fallback(); + break; + + case ACTION_RELOAD: + case ACTION_REEXEC: + r = reload_with_fallback(); + break; + + 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"); + } + +finish: + release_busses(); + + pager_close(); + ask_password_agent_close(); + polkit_agent_close(); + + strv_free(arg_types); + strv_free(arg_states); + strv_free(arg_properties); + + strv_free(arg_wall); + free(arg_root); + + /* 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/grp-system/systemctl/systemctl.completion.bash.in b/src/grp-system/systemctl/systemctl.completion.bash.in new file mode 100644 index 0000000000..6f2b3f122c --- /dev/null +++ b/src/grp-system/systemctl/systemctl.completion.bash.in @@ -0,0 +1,284 @@ +# systemctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__systemctl() { + local mode=$1; shift 1 + systemctl $mode --full --no-legend "$@" +} + +__systemd_properties() { + local mode=$1 + { __systemctl $mode show --all; + @rootlibexecdir@/systemd --dump-configuration-items; } | + while IFS='=' read -r key value; do + [[ $value ]] && echo "$key" + done +} + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__filter_units_by_property () { + local mode=$1 property=$2 value=$3 ; shift 3 + local units=("$@") + local props + IFS=$'\n' read -rd '' -a props < \ + <(__systemctl $mode show --property "$property" -- "${units[@]}") + for ((i=0; $i < ${#units[*]}; i++)); do + if [[ "${props[i]}" = "$property=$value" ]]; then + echo " ${units[i]}" + fi + done +} + +__get_all_units () { { __systemctl $1 list-unit-files; __systemctl $1 list-units --all; } \ + | { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; }; } +__get_template_names () { __systemctl $1 list-unit-files \ + | { while read -r a b; do [[ $a =~ @\. ]] && echo " ${a%%@.*}@"; done; }; } + +__get_active_units () { __systemctl $1 list-units \ + | { while read -r a b; do echo " $a"; done; }; } +__get_startable_units () { + # find startable inactive units + __filter_units_by_property $mode ActiveState inactive $( + __filter_units_by_property $mode CanStart yes $( + __systemctl $mode list-unit-files --state enabled,disabled,static | \ + { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; } + __systemctl $mode list-units --state inactive,failed | \ + { while read -r a b; do echo " $a"; done; } )) +} +__get_restartable_units () { + # filter out masked and not-found + __filter_units_by_property $mode CanStart yes $( + __systemctl $mode list-unit-files --state enabled,disabled,static | \ + { while read -r a b; do [[ $a =~ @\. ]] || echo " $a"; done; } + __systemctl $mode list-units | \ + { while read -r a b; do echo " $a"; done; } ) +} +__get_failed_units () { __systemctl $1 list-units \ + | { while read -r a b c d; do [[ $c == "failed" ]] && echo " $a"; done; }; } +__get_enabled_units () { __systemctl $1 list-unit-files \ + | { while read -r a b c ; do [[ $b == "enabled" ]] && echo " $a"; done; }; } +__get_disabled_units () { __systemctl $1 list-unit-files \ + | { while read -r a b c ; do [[ $b == "disabled" ]] && echo " $a"; done; }; } +__get_masked_units () { __systemctl $1 list-unit-files \ + | { while read -r a b c ; do [[ $b == "masked" ]] && echo " $a"; done; }; } +__get_all_unit_files () { { __systemctl $1 list-unit-files; } | { while read -r a b; do echo " $a"; done; }; } + +__get_machines() { + local a b + { machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager; } | \ + { while read a b; do echo " $a"; done; } +} + +_systemctl () { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps mode + + local -A OPTS=( + [STANDALONE]='--all -a --reverse --after --before --defaults --force -f --full -l --global + --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall + --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup + --show-types -i --ignore-inhibitors --plain' + [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root + --preset-mode -n --lines -o --output -M --machine' + ) + + if __contains_word "--user" ${COMP_WORDS[*]}; then + mode=--user + elif __contains_word "--global" ${COMP_WORDS[*]}; then + mode=--user + else + mode=--system + fi + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --signal|-s) + _signals + return + ;; + --type|-t) + comps=$(__systemctl $mode -t help) + ;; + --state) + comps=$(__systemctl $mode --state=help) + ;; + --job-mode) + comps='fail replace replace-irreversibly isolate + ignore-dependencies ignore-requirements flush' + ;; + --kill-who) + comps='all control main' + ;; + --root) + comps=$(compgen -A directory -- "$cur" ) + compopt -o filenames + ;; + --host|-H) + comps=$(compgen -A hostname) + ;; + --property|-p) + comps=$(__systemd_properties $mode) + ;; + --preset-mode) + comps='full enable-only disable-only' + ;; + --output|-o) + comps='short short-iso short-precise short-monotonic verbose export json + json-pretty json-sse cat' + ;; + --machine|-M) + comps=$( __get_machines ) + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [ALL_UNITS]='is-active is-failed is-enabled status show cat mask preset help list-dependencies edit set-property' + [ENABLED_UNITS]='disable' + [DISABLED_UNITS]='enable' + [REENABLABLE_UNITS]='reenable' + [FAILED_UNITS]='reset-failed' + [STARTABLE_UNITS]='start' + [STOPPABLE_UNITS]='stop condstop kill try-restart condrestart' + [ISOLATABLE_UNITS]='isolate' + [RELOADABLE_UNITS]='reload condreload try-reload-or-restart force-reload' + [RESTARTABLE_UNITS]='restart reload-or-restart' + [TARGET_AND_UNITS]='add-wants add-requires' + [MASKED_UNITS]='unmask' + [JOBS]='cancel' + [ENVS]='set-environment unset-environment' + [STANDALONE]='daemon-reexec daemon-reload default + emergency exit halt hibernate hybrid-sleep kexec list-jobs + list-sockets list-timers list-units list-unit-files poweroff + reboot rescue show-environment suspend get-default + is-system-running' + [FILE]='link switch-root' + [TARGETS]='set-default' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps="${VERBS[*]}" + + elif __contains_word "$verb" ${VERBS[ALL_UNITS]}; then + comps=$( __get_all_units $mode ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[ENABLED_UNITS]}; then + comps=$( __get_enabled_units $mode ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[DISABLED_UNITS]}; then + comps=$( __get_disabled_units $mode; + __get_template_names $mode) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[REENABLABLE_UNITS]}; then + comps=$( __get_disabled_units $mode; + __get_enabled_units $mode; + __get_template_names $mode) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[STARTABLE_UNITS]}; then + comps=$( __get_startable_units $mode; + __get_template_names $mode) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[RESTARTABLE_UNITS]}; then + comps=$( __get_restartable_units $mode; + __get_template_names $mode) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[STOPPABLE_UNITS]}; then + comps=$( __filter_units_by_property $mode CanStop yes \ + $( __get_active_units $mode ) ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[RELOADABLE_UNITS]}; then + comps=$( __filter_units_by_property $mode CanReload yes \ + $( __get_active_units $mode ) ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[ISOLATABLE_UNITS]}; then + comps=$( __filter_units_by_property $mode AllowIsolate yes \ + $( __get_all_units $mode ) ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[FAILED_UNITS]}; then + comps=$( __get_failed_units $mode ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[MASKED_UNITS]}; then + comps=$( __get_masked_units $mode ) + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[TARGET_AND_UNITS]}; then + if __contains_word "$prev" ${VERBS[TARGET_AND_UNITS]} \ + || __contains_word "$prev" ${OPTS[STANDALONE]}; then + comps=$( __systemctl $mode list-unit-files --type target --all \ + | { while read -r a b; do echo " $a"; done; } ) + else + comps=$( __get_all_unit_files $mode ) + fi + compopt -o filenames + + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + + elif __contains_word "$verb" ${VERBS[JOBS]}; then + comps=$( __systemctl $mode list-jobs | { while read -r a b; do echo " $a"; done; } ) + + elif __contains_word "$verb" ${VERBS[ENVS]}; then + comps=$( __systemctl $mode show-environment \ + | while read -r line; do echo " ${line%%=*}=";done ) + compopt -o nospace + + elif __contains_word "$verb" ${VERBS[FILE]}; then + comps=$( compgen -A file -- "$cur" ) + compopt -o filenames + elif __contains_word "$verb" ${VERBS[TARGETS]}; then + comps=$( __systemctl $mode list-unit-files --type target --full --all \ + | { while read -r a b; do echo " $a"; done; } ) + fi + + COMPREPLY=( $(compgen -o filenames -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemctl systemctl diff --git a/src/grp-system/systemctl/systemctl.completion.zsh.in b/src/grp-system/systemctl/systemctl.completion.zsh.in new file mode 100644 index 0000000000..44c31b7833 --- /dev/null +++ b/src/grp-system/systemctl/systemctl.completion.zsh.in @@ -0,0 +1,391 @@ +#compdef systemctl + +(( $+functions[_systemctl_command] )) || _systemctl_command() +{ + local -a _systemctl_cmds + _systemctl_cmds=( + "list-sockets:List sockets" + "list-timers:List timers" + "list-units:List units" + "start:Start (activate) one or more units" + "stop:Stop (deactivate) one or more units" + "reload:Reload one or more units" + "restart:Start or restart one or more units" + "condrestart:Restart one or more units if active" + "try-restart:Restart one or more units if active" + "reload-or-restart:Reload one or more units if possible, otherwise start or restart" + "force-reload:Reload one or more units if possible, otherwise restart if active" + "hibernate:Hibernate the system" + "hybrid-sleep:Hibernate and suspend the system" + "try-reload-or-restart:Reload one or more units if possible, otherwise restart if active" + "isolate:Start one unit and stop all others" + "kill:Send signal to processes of a unit" + "is-active:Check whether units are active" + "is-failed:Check whether units are failed" + "status:Show runtime status of one or more units" + "show:Show properties of one or more units/jobs or the manager" + "cat:Show the source unit files and drop-ins" + "reset-failed:Reset failed state for all, one, or more units" + "list-unit-files:List installed unit files" + "enable:Enable one or more unit files" + "disable:Disable one or more unit files" + "reenable:Reenable one or more unit files" + "preset:Enable/disable one or more unit files based on preset configuration" + "set-default:Set the default target" + "get-default:Query the default target" + "edit:Edit one or more unit files" + "is-system-running:Query overall status of the system" + "help:Show documentation for specified units" + "list-dependencies:Show unit dependency tree" + "mask:Mask one or more units" + "unmask:Unmask one or more units" + "link:Link one or more units files into the search path" + "is-enabled:Check whether unit files are enabled" + "list-jobs:List jobs" + "cancel:Cancel all, one, or more jobs" + "show-environment:Dump environment" + "set-environment:Set one or more environment variables" + "unset-environment:Unset one or more environment variables" + "daemon-reload:Reload systemd manager configuration" + "daemon-reexec:Reexecute systemd manager" + "default:Enter system default mode" + "rescue:Enter system rescue mode" + "emergency:Enter system emergency mode" + "halt:Shut down and halt the system" + "suspend:Suspend the system" + "poweroff:Shut down and power-off the system" + "reboot:Shut down and reboot the system" + "kexec:Shut down and reboot the system with kexec" + "exit:Ask for user instance termination" + "switch-root:Change root directory" + ) + + if (( CURRENT == 1 )); then + _describe -t commands 'systemctl command' _systemctl_cmds || compadd "$@" + else + local curcontext="$curcontext" expl + + cmd="${${_systemctl_cmds[(r)$words[1]:*]%%:*}}" + # Deal with any aliases + case $cmd in + condrestart) cmd="try-restart";; + force-reload) cmd="try-reload-or-restart";; + esac + + if (( $#cmd )); then + curcontext="${curcontext%:*:*}:systemctl-${cmd}:" + + local update_policy + zstyle -s ":completion:${curcontext}:" cache-policy update_policy + if [[ -z "$update_policy" ]]; then + zstyle ":completion:${curcontext}:" cache-policy _systemctl_caching_policy + fi + + _call_function ret _systemctl_$cmd || _message 'no more arguments' + else + _message "unknown systemctl command: $words[1]" + fi + return ret + fi +} + +__systemctl() +{ + systemctl $_sys_service_mgr --full --no-legend --no-pager "$@" +} + + +# Fills the unit list +_systemctl_all_units() +{ + if ( [[ ${+_sys_all_units} -eq 0 ]] || _cache_invalid SYS_ALL_UNITS ) && + ! _retrieve_cache SYS_ALL_UNITS; + then + _sys_all_units=( ${${(f)"$(__systemctl list-units --all)"}%% *} ) + _store_cache SYS_ALL_UNITS _sys_all_units + fi +} + +# Fills the unit list including all file units +_systemctl_really_all_units() +{ + local -a all_unit_files; + local -a really_all_units; + if ( [[ ${+_sys_really_all_units} -eq 0 ]] || _cache_invalid SYS_REALLY_ALL_UNITS ) && + ! _retrieve_cache SYS_REALLY_ALL_UNITS; + then + all_unit_files=( ${${(f)"$(__systemctl list-unit-files)"}%% *} ) + _systemctl_all_units + really_all_units=($_sys_all_units $all_unit_files) + _sys_really_all_units=(${(u)really_all_units}) + _store_cache SYS_REALLY_ALL_UNITS _sys_really_all_units + fi +} + +_filter_units_by_property() { + local property=$1 value=$2 ; shift ; shift + local -a units ; units=($*) + local props + for props in ${(ps:\n\n:)"$(_call_program units "$service show --no-pager --property="Id,$property" -- ${units} 2>/dev/null")"}; do + props=(${(f)props}) + if [[ "${props[2]}" = "$property=$value" ]]; then + echo -E - " ${props[1]#Id=}" + fi + done +} + +_systemctl_get_template_names() { echo -E - ${^${(M)${(f)"$(__systemctl list-unit-files)"}##*@.[^[:space:]]##}%%@.*}\@ } + + +_systemctl_active_units() {_sys_active_units=( ${${(f)"$(__systemctl list-units)"}%% *} )} + +_systemctl_startable_units(){ + _sys_startable_units=( $( _filter_units_by_property ActiveState inactive $( + _filter_units_by_property CanStart yes $( + __systemctl $mode list-unit-files --state enabled,disabled,static | \ + { while read -r a b; do [[ $a =~ @\. ]] || echo -E - " $a"; done; } + __systemctl $mode list-units --state inactive,failed | \ + { while read -r a b; do echo -E - " $a"; done; } )) ) ) +} + +_systemctl_restartable_units(){ + _sys_restartable_units=( $(_filter_units_by_property CanStart yes $( + __systemctl $mode list-unit-files --state enabled,disabled,static | \ + { while read -r a b; do [[ $a =~ @\. ]] || echo -E - " $a"; done; } + __systemctl $mode list-units | \ + { while read -r a b; do echo -E - " $a"; done; } )) ) +} + +_systemctl_failed_units() {_sys_failed_units=( ${${(f)"$(__systemctl list-units --state=failed)"}%% *} ) } +_systemctl_unit_state() { typeset -gA _sys_unit_state; _sys_unit_state=( $(__systemctl list-unit-files) ) } + +local fun +# Completion functions for ALL_UNITS +for fun in is-active is-failed is-enabled status show cat mask preset help list-dependencies edit ; do + (( $+functions[_systemctl_$fun] )) || _systemctl_$fun() + { + _systemctl_really_all_units + _wanted systemd-units expl unit \ + compadd "$@" -a - _sys_really_all_units + } +done + +# Completion functions for ENABLED_UNITS +(( $+functions[_systemctl_disable] )) || _systemctl_disable() +{ + local _sys_unit_state; _systemctl_unit_state + _wanted systemd-units expl 'enabled unit' \ + compadd "$@" - ${(k)_sys_unit_state[(R)enabled]} +} + +(( $+functions[_systemctl_reenable] )) || _systemctl_reenable() +{ + local _sys_unit_state; _systemctl_unit_state + _wanted systemd-units expl 'enabled/disabled unit' \ + compadd "$@" - ${(k)_sys_unit_state[(R)(enabled|disabled)]} $(_systemctl_get_template_names) +} + +# Completion functions for DISABLED_UNITS +(( $+functions[_systemctl_enable] )) || _systemctl_enable() +{ + local _sys_unit_state; _systemctl_unit_state + _wanted systemd-units expl 'disabled unit' \ + compadd "$@" - ${(k)_sys_unit_state[(R)disabled]} $(_systemctl_get_template_names) +} + +# Completion functions for FAILED_UNITS +(( $+functions[_systemctl_reset-failed] )) || _systemctl_reset-failed() +{ + local _sys_failed_units; _systemctl_failed_units + _wanted systemd-units expl 'failed unit' \ + compadd "$@" -a - _sys_failed_units || _message "no failed unit found" +} + +# Completion functions for STARTABLE_UNITS +(( $+functions[_systemctl_start] )) || _systemctl_start() +{ + local _sys_startable_units; _systemctl_startable_units + _wanted systemd-units expl 'startable unit' \ + compadd "$@" - ${_sys_startable_units[*]} $(_systemctl_get_template_names) +} + +# Completion functions for STOPPABLE_UNITS +for fun in stop kill try-restart condrestart ; do + (( $+functions[_systemctl_$fun] )) || _systemctl_$fun() + { + local _sys_active_units; _systemctl_active_units + _wanted systemd-units expl 'stoppable unit' \ + compadd "$@" - $( _filter_units_by_property CanStop yes \ + ${_sys_active_units[*]} ) + } +done + +# Completion functions for ISOLATABLE_UNITS +(( $+functions[_systemctl_isolate] )) || _systemctl_isolate() +{ + _systemctl_all_units + _wanted systemd-units expl 'isolatable unit' \ + compadd "$@" - $( _filter_units_by_property AllowIsolate yes \ + ${_sys_all_units[*]} ) +} + +# Completion functions for RELOADABLE_UNITS +for fun in reload try-reload-or-restart force-reload ; do + (( $+functions[_systemctl_$fun] )) || _systemctl_$fun() + { + local _sys_active_units; _systemctl_active_units + _wanted systemd-units expl 'reloadable unit' \ + compadd "$@" - $( _filter_units_by_property CanReload yes \ + ${_sys_active_units[*]} ) + } +done + +# Completion functions for RESTARTABLE_UNITS +for fun in restart reload-or-restart ; do + (( $+functions[_systemctl_$fun] )) || _systemctl_$fun() + { + local _sys_restartable_units; _systemctl_restartable_units + _wanted systemd-units expl 'restartable unit' \ + compadd "$@" - ${_sys_restartable_units[*]} $(_systemctl_get_template_names) + } +done + +# Completion functions for MASKED_UNITS +(( $+functions[_systemctl_unmask] )) || _systemctl_unmask() +{ + local _sys_unit_state; _systemctl_unit_state + _wanted systemd-units expl 'masked unit' \ + compadd "$@" - ${(k)_sys_unit_state[(R)masked]} || _message "no masked units found" +} + +# Completion functions for JOBS +(( $+functions[_systemctl_cancel] )) || _systemctl_cancel() +{ + _wanted systemd-jobs expl job \ + compadd "$@" - ${${(f)"$(__systemctl list-jobs)"}%% *} || + _message "no jobs found" +} + +# Completion functions for TARGETS +(( $+functions[_systemctl_set-default] )) || _systemctl_set-default() +{ + _wanted systemd-targets expl target \ + compadd "$@" - ${${(f)"$(__systemctl list-unit-files --type target --all)"}%% *} || + _message "no targets found" +} + +# Completion functions for ENVS +for fun in set-environment unset-environment ; do + (( $+functions[_systemctl_$fun] )) || _systemctl_$fun() + { + local fun=$0 ; fun=${fun##_systemctl_} + local suf + if [[ "${fun}" = "set-environment" ]]; then + suf='-S=' + fi + _wanted systemd-environment expl 'environment variable' \ + compadd "$@" ${suf} - ${${(f)"$(systemctl show-environment)"}%%=*} + } +done + +(( $+functions[_systemctl_link] )) || _systemctl_link() { + _sd_unit_files +} + +(( $+functions[_systemctl_switch-root] )) || _systemctl_switch-root() { + _files +} + +# no systemctl completion for: +# [STANDALONE]='daemon-reexec daemon-reload default +# emergency exit halt kexec list-jobs list-units +# list-unit-files poweroff reboot rescue show-environment' + +_systemctl_caching_policy() +{ + local _sysunits + local -a oldcache + + # rebuild if cache is more than a day old + oldcache=( "$1"(mh+1) ) + (( $#oldcache )) && return 0 + + _sysunits=(${${(f)"$(__systemctl --all)"}%% *}) + + if (( $#_sysunits )); then + for unit in $_sysunits; do + [[ "$unit" -nt "$1" ]] && return 0 + done + fi + + return 1 +} + +_unit_states() { + local -a _states + _states=("${(fo)$(__systemctl --state=help)}") + _values -s , "${_states[@]}" +} + +_unit_types() { + local -a _types + _types=("${(fo)$(__systemctl -t help)}") + _values -s , "${_types[@]}" +} + +_unit_properties() { + if ( [[ ${+_sys_all_properties} -eq 0 ]] || _cache_invalid SYS_ALL_PROPERTIES ) && + ! _retrieve_cache SYS_ALL_PROPERTIES; + then + _sys_all_properties=( ${${(M)${(f)"$(__systemctl show --all; + @rootlibexecdir@/systemd --dump-configuration-items)"}##[[:alnum:]]##=*}%%=*} + ) + _store_cache SYS_ALL_PROPRTIES _sys_all_properties + fi + _values -s , "${_sys_all_properties[@]}" +} + +_job_modes() { + local -a _modes + _modes=(fail replace replace-irreversibly isolate ignore-dependencies ignore-requirements flush) + _values -s , "${_modes[@]}" +} + +local -a _modes; _modes=("--user" "--system") +local _sys_service_mgr=${${words:*_modes}[(R)(${(j.|.)_modes})]:---system} +_arguments -s \ + {-h,--help}'[Show help]' \ + '--version[Show package version]' \ + {-t+,--type=}'[List only units of a particular type]:unit type:_unit_types' \ + '--state=[Display units in the specified state]:unit state:_unit_states' \ + '--job-mode=[Specify how to deal with other jobs]:mode:_job_modes' \ + {-p+,--property=}'[Show only properties by specific name]:unit property:_unit_properties' \ + {-a,--all}'[Show all units/properties, including dead/empty ones]' \ + '--reverse[Show reverse dependencies]' \ + '--after[Show units ordered after]' \ + '--before[Show units ordered before]' \ + {-l,--full}"[Don't ellipsize unit names on output]" \ + '--show-types[When showing sockets, show socket type]' \ + {-i,--ignore-inhibitors}'[When executing a job, ignore jobs dependencies]' \ + {-q,--quiet}'[Suppress output]' \ + '--no-block[Do not wait until operation finished]' \ + '--no-legend[Do not print a legend, i.e. the column headers and the footer with hints]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--system[Connect to system manager]' \ + '--user[Connect to user service manager]' \ + "--no-wall[Don't send wall message before halt/power-off/reboot]" \ + '--global[Enable/disable unit files globally]' \ + "--no-reload[When enabling/disabling unit files, don't reload daemon configuration]" \ + '--no-ask-password[Do not ask for system passwords]' \ + '--kill-who=[Who to send signal to]:killwho:(main control all)' \ + {-s+,--signal=}'[Which signal to send]:signal:_signals' \ + {-f,--force}'[When enabling unit files, override existing symlinks. When shutting down, execute action immediately]' \ + '--root=[Enable unit files in the specified root directory]:directory:_directories' \ + '--runtime[Enable unit files only temporarily until next reboot]' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-P,--privileged}'[Acquire privileges before execution]' \ + {-n+,--lines=}'[Journal entries to show]:number of entries' \ + {-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \ + '--firmware-setup[Tell the firmware to show the setup menu on next boot]' \ + '--plain[When used with list-dependencies, print output as a list]' \ + '*::systemctl command:_systemctl_command' diff --git a/src/grp-system/systemctl/systemctl.xml b/src/grp-system/systemctl/systemctl.xml new file mode 100644 index 0000000000..e7880d24f7 --- /dev/null +++ b/src/grp-system/systemctl/systemctl.xml @@ -0,0 +1,1826 @@ + + +%entities; +]> + + + + + + + systemctl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemctl + 1 + + + + systemctl + Control the systemd system and service manager + + + + + systemctl + OPTIONS + COMMAND + NAME + + + + + Description + + systemctl may be used to introspect and + control the state of the systemd system and + service manager. Please refer to + systemd1 + for an introduction into the basic concepts and functionality this + tool manages. + + + + Options + + The following options are understood: + + + + + + + + The argument should be a comma-separated list of unit + types such as and + . + + + If one of the arguments is a unit type, when listing + units, limit display to certain unit types. Otherwise, units + of all types will be shown. + + As a special case, if one of the arguments is + , a list of allowed values will be + printed and the program will exit. + + + + + + + + The argument should be a comma-separated list of unit + LOAD, SUB, or ACTIVE states. When listing units, show only + those in the specified states. Use + to show only failed units. + + As a special case, if one of the arguments is + , a list of allowed values will be + printed and the program will exit. + + + + + + + + + When showing unit/job/manager properties with the + show command, limit display to properties + specified in the argument. The argument should be a + comma-separated list of property names, such as + MainPID. Unless specified, all known + properties are shown. If specified more than once, all + properties with the specified names are shown. Shell + completion is implemented for property names. + + For the manager itself, + systemctl show will show all available + properties. Those properties are documented in + systemd-system.conf5. + + + Properties for units vary by unit type, so showing any + unit (even a non-existent one) is a way to list properties + pertaining to this type. Similarly, showing any job will list + properties pertaining to all jobs. Properties for units are + documented in + systemd.unit5, + and the pages for individual unit types + systemd.service5, + systemd.socket5, + etc. + + + + + + + + + When listing units with list-units, also show inactive units and + units which are following other units. When showing unit/job/manager properties, show all + properties regardless whether they are set or not. + + To list all units installed in the file system, use the + list-unit-files command instead. + + + + + + + + + When listing units, also show units of local + containers. Units of local containers will be prefixed with + the container name, separated by a single colon character + (:). + + + + + + + + Show reverse dependencies between units with + list-dependencies, i.e. follow + dependencies of type WantedBy=, + RequiredBy=, + PartOf=, BoundBy=, + instead of Wants= and similar. + + + + + + + + + With list-dependencies, show the + units that are ordered before the specified unit. In other + words, recursively list units following the + After= dependency. + + Note that any After= dependency is + automatically mirrored to create a + Before= dependency. Temporal dependencies + may be specified explicitly, but are also created implicitly + for units which are WantedBy= targets + (see + systemd.target5), + and as a result of other directives (for example + RequiresMountsFor=). Both explicitly + and implicitly introduced dependencies are shown with + list-dependencies. + + + + + + + + With list-dependencies, show the + units that are ordered after the specified unit. In other + words, recursively list units following the + Before= dependency. + + + + + + + + + Do not ellipsize unit names, process tree entries, + journal output, or truncate unit descriptions in the output + of status, list-units, + list-jobs, and + list-timers. + + + + + + + + When printing properties with show, + only print the value, and skip the property name and + =. + + + + + + + + When showing sockets, show the type of the socket. + + + + + + + + When queuing a new job, this option controls how to deal with + already queued jobs. It takes one of fail, + replace, + replace-irreversibly, + isolate, + ignore-dependencies, + ignore-requirements or + flush. Defaults to + replace, except when the + isolate command is used which implies the + isolate job mode. + + If fail is specified and a requested + operation conflicts with a pending job (more specifically: + causes an already pending start job to be reversed into a stop + job or vice versa), cause the operation to fail. + + If replace (the default) is + specified, any conflicting pending job will be replaced, as + necessary. + + If replace-irreversibly is specified, + operate like replace, but also mark the new + jobs as irreversible. This prevents future conflicting + transactions from replacing these jobs (or even being enqueued + while the irreversible jobs are still pending). Irreversible + jobs can still be cancelled using the cancel + command. + + isolate is only valid for start + operations and causes all other units to be stopped when the + specified unit is started. This mode is always used when the + isolate command is used. + + flush will cause all queued jobs to + be canceled when the new job is enqueued. + + If ignore-dependencies is specified, + then all unit dependencies are ignored for this new job and + the operation is executed immediately. If passed, no required + units of the unit passed will be pulled in, and no ordering + dependencies will be honored. This is mostly a debugging and + rescue tool for the administrator and should not be used by + applications. + + ignore-requirements is similar to + ignore-dependencies, but only causes the + requirement dependencies to be ignored, the ordering + dependencies will still be honoured. + + + + + + + + + Shorthand for fail. + When used with the kill command, + if no units were killed, the operation results in an error. + + + + + + + + + + When system shutdown or a sleep state is requested, + ignore inhibitor locks. Applications can establish inhibitor + locks to avoid that certain important operations (such as CD + burning or suchlike) are interrupted by system shutdown or a + sleep state. Any user may take these locks and privileged + users may override these locks. If any locks are taken, + shutdown and sleep state requests will normally fail + (regardless of whether privileged or not) and a list of active locks + is printed. However, if + is specified, the locks are ignored and not printed, and the + operation attempted anyway, possibly requiring additional + privileges. + + + + + + + + + Suppress printing of the results of various commands + and also the hints about truncated log lines. This does not + suppress output of commands for which the printed output is + the only result (like show). Errors are + always printed. + + + + + + + + Do not synchronously wait for the requested operation + to finish. If this is not specified, the job will be + verified, enqueued and systemctl will + wait until the unit's start-up is completed. By passing this + argument, it is only verified and enqueued. + + + + + + + + + + + + + Do not send wall message before halt, power-off, + reboot. + + + + + + + + When used with enable and + disable, operate on the global user + configuration directory, thus enabling or disabling a unit + file globally for all future logins of all users. + + + + + + + + When used with enable and + disable, do not implicitly reload daemon + configuration after executing the changes. + + + + + + + + When used with start and related + commands, disables asking for passwords. Background services + may require input of a password or passphrase string, for + example to unlock system hard disks or cryptographic + certificates. Unless this option is specified and the + command is invoked from a terminal, + systemctl will query the user on the + terminal for the necessary secrets. Use this option to + switch this behavior off. In this case, the password must be + supplied by some other means (for example graphical password + agents) or the service might fail. This also disables + querying the user for authentication for privileged + operations. + + + + + + + + When used with kill, choose which + processes to send a signal to. Must be one of + , or + to select whether to kill only the main + process, the control process or all processes of the + unit. The main process of the unit is the one that defines + the life-time of it. A control process of a unit is one that + is invoked by the manager to induce state changes of it. For + example, all processes started due to the + ExecStartPre=, + ExecStop= or + ExecReload= settings of service units are + control processes. Note that there is only one control + process per unit at a time, as only one state change is + executed at a time. For services of type + Type=forking, the initial process started + by the manager for ExecStart= is a + control process, while the process ultimately forked off by + that one is then considered the main process of the unit (if + it can be determined). This is different for service units + of other types, where the process forked off by the manager + for ExecStart= is always the main process + itself. A service unit consists of zero or one main process, + zero or one control process plus any number of additional + processes. Not all unit types manage processes of these + types however. For example, for mount units, control processes + are defined (which are the invocations of + &MOUNT_PATH; and + &UMOUNT_PATH;), but no main process + is defined. If omitted, defaults to + . + + + + + + + + + + When used with kill, choose which + signal to send to selected processes. Must be one of the + well-known signal specifiers such as SIGTERM, SIGINT or + SIGSTOP. If omitted, defaults to + . + + + + + + + + + When used with enable, overwrite + any existing conflicting symlinks. + + When used with edit, create all of the + specified units which do not already exist. + + When used with halt, poweroff, reboot or + kexec, execute the selected operation without shutting down all units. However, all + processes will be killed forcibly and all file systems are unmounted or remounted read-only. This is hence a + drastic but relatively safe option to request an immediate reboot. If is specified + twice for these operations (with the exception of kexec), they will be executed + immediately, without terminating any processes or unmounting any file systems. Warning: specifying + twice with any of these operations might result in data loss. Note that when + is specified twice the selected operation is executed by + systemctl itself, and the system manager is not contacted. This means the command should + succeed even when the system manager hangs or crashed. + + + + + + + + When used with halt, + poweroff, reboot or + kexec, set a short message explaining the reason + for the operation. The message will be logged together with the + default shutdown message. + + + + + + + + When used with enable, the units + will also be started. When used with disable or + mask, the units will also be stopped. The start + or stop operation is only carried out when the respective enable or + disable operation has been successful. + + + + + + + + When used with + enable/disable/is-enabled + (and related commands), use an alternate root path when + looking for unit files. + + + + + + + + + When used with enable, + disable, edit, + (and related commands), make changes only temporarily, so + that they are lost on the next reboot. This will have the + effect that changes are not made in subdirectories of + /etc but in /run, + with identical immediate effects, however, since the latter + is lost on reboot, the changes are lost too. + + Similarly, when used with + set-property, make changes only + temporarily, so that they are lost on the next + reboot. + + + + + + + + Takes one of full (the default), + enable-only, + disable-only. When used with the + preset or preset-all + commands, controls whether units shall be disabled and + enabled according to the preset rules, or only enabled, or + only disabled. + + + + + + + + + When used with status, controls the + number of journal lines to show, counting from the most + recent ones. Takes a positive integer argument. Defaults to + 10. + + + + + + + + + When used with status, controls the + formatting of the journal entries that are shown. For the + available choices, see + journalctl1. + Defaults to short. + + + + + + + + When used with the reboot command, + indicate to the system's firmware to boot into setup + mode. Note that this is currently only supported on some EFI + systems and only if the system was booted in EFI + mode. + + + + + + + + When used with list-dependencies, + list-units or list-machines, the + the output is printed as a list instead of a tree, and the bullet + circles are omitted. + + + + + + + + + + + + + + + Commands + + The following commands are understood: + + + Unit Commands + + + + list-units PATTERN... + + + List units that systemd has loaded. This includes units that + are either referenced directly or through a dependency, or units that were active in the + past and have failed. By default only units which are active, have pending jobs, or have + failed are shown; this can be changed with option . If one or more + PATTERNs are specified, only units matching one of them are + shown. The units that are shown are additionally filtered by + and if those options are specified. + + This is the default command. + + + + + list-sockets PATTERN... + + + List socket units ordered by listening address. + If one or more PATTERNs are + specified, only socket units matching one of them are + shown. Produces output similar to + +LISTEN UNIT ACTIVATES +/dev/initctl systemd-initctl.socket systemd-initctl.service +... +[::]:22 sshd.socket sshd.service +kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service + +5 sockets listed. + Note: because the addresses might contains spaces, this output + is not suitable for programmatic consumption. + + + See also the options , + , and . + + + + + list-timers PATTERN... + + + List timer units ordered by the time they elapse + next. If one or more PATTERNs + are specified, only units matching one of them are shown. + + + See also the options and + . + + + + + start PATTERN... + + + Start (activate) one or more units specified on the + command line. + + Note that glob patterns operate on the set of primary names of currently loaded units. Units which + are not active and are not in a failed state usually are not loaded, and will not be matched by any + pattern. In addition, in case of instantiated units, systemd is often unaware of the instance name until + the instance has been started. Therefore, using glob patterns with start has limited + usefulness. Also, secondary alias names of units are not considered. + + + + stop PATTERN... + + + Stop (deactivate) one or more units specified on the + command line. + + + + reload PATTERN... + + + Asks all units listed on the command line to reload + their configuration. Note that this will reload the + service-specific configuration, not the unit configuration + file of systemd. If you want systemd to reload the + configuration file of a unit, use the + daemon-reload command. In other words: + for the example case of Apache, this will reload Apache's + httpd.conf in the web server, not the + apache.service systemd unit + file. + + This command should not be confused with the + daemon-reload command. + + + + + restart PATTERN... + + + Restart one or more units specified on the command + line. If the units are not running yet, they will be + started. + + + + try-restart PATTERN... + + + Restart one or more units specified on the command + line if the units are running. This does nothing if units are not + running. + + + + + reload-or-restart PATTERN... + + + Reload one or more units if they support it. If not, + restart them instead. If the units are not running yet, they + will be started. + + + + try-reload-or-restart PATTERN... + + + Reload one or more units if they support it. If not, + restart them instead. This does nothing if the units are not + running. + + + + + isolate NAME + + + Start the unit specified on the command line and its + dependencies and stop all others. If a unit name with no + extension is given, an extension of + .target will be assumed. + + This is similar to changing the runlevel in a + traditional init system. The isolate + command will immediately stop processes that are not enabled + in the new unit, possibly including the graphical + environment or terminal you are currently using. + + Note that this is allowed only on units where + is enabled. See + systemd.unit5 + for details. + + + + kill PATTERN... + + + Send a signal to one or more processes of the + unit. Use to select which + process to kill. Use to select + the signal to send. + + + + is-active PATTERN... + + + Check whether any of the specified units are active + (i.e. running). Returns an exit code + 0 if at least one is active, or + non-zero otherwise. Unless is + specified, this will also print the current unit state to + standard output. + + + + is-failed PATTERN... + + + Check whether any of the specified units are in a + "failed" state. Returns an exit code + 0 if at least one has failed, + non-zero otherwise. Unless is + specified, this will also print the current unit state to + standard output. + + + + status PATTERN...|PID...] + + + Show terse runtime status information about one or + more units, followed by most recent log data from the + journal. If no units are specified, show system status. If + combined with , also show the status of + all units (subject to limitations specified with + ). If a PID is passed, show information + about the unit the process belongs to. + + This function is intended to generate human-readable + output. If you are looking for computer-parsable output, + use show instead. By default, this + function only shows 10 lines of output and ellipsizes + lines to fit in the terminal window. This can be changed + with and , + see above. In addition, journalctl + --unit=NAME or + journalctl + --user-unit=NAME use + a similar filter for messages and might be more + convenient. + + + + + show PATTERN...|JOB... + + + Show properties of one or more units, jobs, or the + manager itself. If no argument is specified, properties of + the manager will be shown. If a unit name is specified, + properties of the unit is shown, and if a job ID is + specified, properties of the job is shown. By default, empty + properties are suppressed. Use to + show those too. To select specific properties to show, use + . This command is intended to be + used whenever computer-parsable output is required. Use + status if you are looking for formatted + human-readable output. + + + + cat PATTERN... + + + Show backing files of one or more units. Prints the + "fragment" and "drop-ins" (source files) of units. Each + file is preceded by a comment which includes the file + name. + + + + set-property NAME ASSIGNMENT... + + + Set the specified unit properties at runtime where + this is supported. This allows changing configuration + parameter properties such as resource control settings at + runtime. Not all properties may be changed at runtime, but + many resource control settings (primarily those in + systemd.resource-control5) + may. The changes are applied instantly, and stored on disk + for future boots, unless is + passed, in which case the settings only apply until the + next reboot. The syntax of the property assignment follows + closely the syntax of assignments in unit files. + + Example: systemctl set-property foobar.service CPUShares=777 + + If the specified unit appears to be inactive, the + changes will be only stored on disk as described + previously hence they will be effective when the unit will + be started. + + Note that this command allows changing multiple + properties at the same time, which is preferable over + setting them individually. Like unit file configuration + settings, assigning the empty list to list parameters will + reset the list. + + + + + help PATTERN...|PID... + + + Show manual pages for one or more units, if + available. If a PID is given, the manual pages for the unit + the process belongs to are shown. + + + + + reset-failed [PATTERN...] + + + Reset the failed state of the + specified units, or if no unit name is passed, reset the state of all + units. When a unit fails in some way (i.e. process exiting + with non-zero error code, terminating abnormally or timing + out), it will automatically enter the + failed state and its exit code and status + is recorded for introspection by the administrator until the + service is restarted or reset with this command. + + + + + + list-dependencies + NAME + + + + Shows units required and wanted by the specified + unit. This recursively lists units following the + Requires=, + Requisite=, + ConsistsOf=, + Wants=, BindsTo= + dependencies. If no unit is specified, + default.target is implied. + + By default, only target units are recursively + expanded. When is passed, all other + units are recursively expanded as well. + + Options , + , + may be used to change what types of dependencies + are shown. + + + + + + + Unit File Commands + + + + list-unit-files PATTERN... + + + List unit files installed on the system, in combination with their enablement state (as reported by + is-enabled). If one or more PATTERNs are specified, only unit + files whose name matches one of them are shown (patterns matching unit file system paths are not + supported). + + + + + enable NAME... + enable PATH... + + + Enable one or more units or unit instances. This will create a set of symlinks, as encoded in the + [Install] sections of the indicated unit files. After the symlinks have been created, + the system manager configuration is reloaded (in a way equivalent to daemon-reload), in + order to ensure the changes are taken into account immediately. Note that this does + not have the effect of also starting any of the units being enabled. If this is + desired, combine this command with the switch, or invoke start + with appropriate arguments later. Note that in case of unit instance enablement (i.e. enablement of units of + the form foo@bar.service), symlinks named the same as instances are created in the + unit configuration diectory, however they point to the single template unit file they are instantiated + from. + + This command expects either valid unit names (in which case various unit file directories are + automatically searched for unit files with appropriate names), or absolute paths to unit files (in which + case these files are read directly). If a specified unit file is located outside of the usual unit file + directories, an additional symlink is created, linking it into the unit configuration path, thus ensuring + it is found when requested by commands such as start. + + This command will print the file system operations executed. This output may be suppressed by passing + . + + + Note that this operation creates only the symlinks suggested in the [Install] + section of the unit files. While this command is the recommended way to manipulate the unit configuration + directory, the administrator is free to make additional changes manually by placing or removing symlinks + below this directory. This is particularly useful to create configurations that deviate from the suggested + default installation. In this case, the administrator must make sure to invoke + daemon-reload manually as necessary, in order to ensure the changes are taken into + account. + + + Enabling units should not be confused with starting (activating) units, as done by the + start command. Enabling and starting units is orthogonal: units may be enabled without + being started and started without being enabled. Enabling simply hooks the unit into various suggested + places (for example, so that the unit is automatically started on boot or when a particular kind of + hardware is plugged in). Starting actually spawns the daemon process (in case of service units), or binds + the socket (in case of socket units), and so on. + + Depending on whether , , , + or is specified, this enables the unit for the system, for the calling user only, + for only this boot of the system, or for all future logins of all users, or only this boot. Note that in + the last case, no systemd daemon configuration is reloaded. + + Using enable on masked units is not supported and results in an error. + + + + + disable NAME... + + + Disables one or more units. This removes all symlinks to the unit files backing the specified units + from the unit configuration directory, and hence undoes any changes made by enable or + link. Note that this removes all symlinks to matching unit files, + including manually created symlinks, and not just those actually created by enable or + link. Note that while disable undoes the effect of + enable, the two commands are otherwise not symmetric, as disable may + remove more symlinks than a prior enable invocation of the same unit created. + + This command expects valid unit names only, it does not accept paths to unit files. + + In addition to the units specified as arguments, all units are disabled that are listed in the + Also= setting contained in the [Install] section of any of the unit + files being operated on. + + This command implicitly reloads the system manager configuration after completing the operation. Note + that this command does not implicitly stop the units that are being disabled. If this is desired, either + combine this command with the switch, or invoke the stop command + with appropriate arguments later. + + This command will print information about the file system operations (symlink removals) + executed. This output may be suppressed by passing . + + + This command honors , , + and in a similar way as enable. + + + + + reenable NAME... + + + Reenable one or more units, as specified on the command line. This is a combination of + disable and enable and is useful to reset the symlinks a unit file is + enabled with to the defaults configured in its [Install] section. This commands expects + a unit uname only, it does not accept paths to unit files. + + + + + preset NAME... + + + Reset the enable/disable status one or more unit files, as specified on + the command line, to the defaults configured in the preset policy files. This + has the same effect as disable or + enable, depending how the unit is listed in the preset + files. + + Use to control whether units shall be + enabled and disabled, or only enabled, or only disabled. + + If the unit carries no install information, it will be silently ignored + by this command. + + For more information on the preset policy format, see + systemd.preset5. + For more information on the concept of presets, please consult the + Preset + document. + + + + + preset-all + + + Resets all installed unit files to the defaults + configured in the preset policy file (see above). + + Use to control + whether units shall be enabled and disabled, or only + enabled, or only disabled. + + + + + is-enabled NAME... + + + Checks whether any of the specified unit files are + enabled (as with enable). Returns an + exit code of 0 if at least one is enabled, non-zero + otherwise. Prints the current enable status (see table). + To suppress this output, use . + + + + + <command>is-enabled</command> output + + + + + + Name + Description + Exit Code + + + + + enabled + Enabled via .wants/, .requires/ or alias symlinks (permanently in /etc/systemd/system/, or transiently in /run/systemd/system/). + 0 + + + enabled-runtime + + + linked + Made available through one or more symlinks to the unit file (permanently in /etc/systemd/system/ or transiently in /run/systemd/system/), even though the unit file might reside outside of the unit file search path. + > 0 + + + linked-runtime + + + masked + Completely disabled, so that any start operation on it fails (permanently in /etc/systemd/system/ or transiently in /run/systemd/systemd/). + > 0 + + + masked-runtime + + + static + The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section. + 0 + + + indirect + The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled. + 0 + + + disabled + The unit file is not enabled, but contains an [Install] section with installation instructions. + > 0 + + + generated + The unit file was generated dynamically via a generator tool. See systemd.generator7. Generated unit files may not be enabled, they are enabled implicitly by their generator. + 0 + + + transient + The unit file has been created dynamically with the runtime API. Transient units may not be enabled. + 0 + + + bad + The unit file is invalid or another error occurred. Note that is-enabled will not actually return this state, but print an error message instead. However the unit file listing printed by list-unit-files might show it. + > 0 + + + +
+ +
+
+ + + mask NAME... + + + Mask one or more units, as specified on the command line. This will link these unit files to + /dev/null, making it impossible to start them. This is a stronger version of + disable, since it prohibits all kinds of activation of the unit, including enablement + and manual activation. Use this option with care. This honors the option to only + mask temporarily until the next reboot of the system. The option may be used to + ensure that the units are also stopped. This command expects valid unit names only, it does not accept unit + file paths. + + + + + unmask NAME... + + + Unmask one or more unit files, as specified on the command line. This will undo the effect of + mask. This command expects valid unit names only, it does not accept unit file + paths. + + + + + link PATH... + + + Link a unit file that is not in the unit file search paths into the unit file search path. This + command expects an absolute path to a unit file. The effect of this may be undone with + disable. The effect of this command is that a unit file is made available for commands + such as start, even though it is not installed directly in the unit search path. + + + + + revert NAME... + + + Revert one or more unit files to their vendor versions. This command removes drop-in configuration + files that modify the specified units, as well as any user-configured unit file that overrides a matching + vendor supplied unit file. Specifically, for a unit foo.service the matching directories + foo.service.d/ with all their contained files are removed, both below the persistent and + runtime configuration directories (i.e. below /etc/systemd/system and + /run/systemd/system); if the unit file has a vendor-supplied version (i.e. a unit file + located below /usr) any matching peristent or runtime unit file that overrides it is + removed, too. Note that if a unit file has no vendor-supplied version (i.e. is only defined below + /etc/systemd/system or /run/systemd/system, but not in a unit + file stored below /usr), then it is not removed. Also, if a unit is masked, it is + unmasked. + + Effectively, this command may be used to undo all changes made with systemctl + edit, systemctl set-property and systemctl mask and puts + the original unit file with its settings back in effect. + + + + + add-wants TARGET + NAME... + add-requires TARGET + NAME... + + + Adds Wants= or Requires= + dependencies, respectively, to the specified + TARGET for one or more units. + + This command honors , + , and + in a way similar to + enable. + + + + + + edit NAME... + + + Edit a drop-in snippet or a whole replacement file if + is specified, to extend or override the + specified unit. + + Depending on whether (the default), + , or is specified, + this command creates a drop-in file for each unit either for the system, + for the calling user, or for all futures logins of all users. Then, + the editor (see the "Environment" section below) is invoked on + temporary files which will be written to the real location if the + editor exits successfully. + + If is specified, this will copy the + original units instead of creating drop-in files. + + If is specified and any units do + not already exist, new unit files will be opened for editing. + + If is specified, the changes will + be made temporarily in /run and they will be + lost on the next reboot. + + If the temporary file is empty upon exit, the modification of + the related unit is canceled. + + After the units have been edited, systemd configuration is + reloaded (in a way that is equivalent to daemon-reload). + + + Note that this command cannot be used to remotely edit units + and that you cannot temporarily edit units which are in + /etc, since they take precedence over + /run. + + + + + get-default + + + Return the default target to boot into. This returns + the target unit name default.target + is aliased (symlinked) to. + + + + + set-default NAME + + + Set the default target to boot into. This sets + (symlinks) the default.target alias + to the given target unit. + + + +
+
+ + + Machine Commands + + + + list-machines PATTERN... + + + List the host and all running local containers with + their state. If one or more + PATTERNs are specified, only + containers matching one of them are shown. + + + + + + + + Job Commands + + + + list-jobs PATTERN... + + + List jobs that are in progress. If one or more + PATTERNs are specified, only + jobs for units matching one of them are shown. + + + + cancel JOB... + + + Cancel one or more jobs specified on the command line + by their numeric job IDs. If no job ID is specified, cancel + all pending jobs. + + + + + + + Environment Commands + + + + show-environment + + + Dump the systemd manager environment block. The + environment block will be dumped in straight-forward form + suitable for sourcing into a shell script. This environment + block will be passed to all processes the manager + spawns. + + + + set-environment VARIABLE=VALUE... + + + Set one or more systemd manager environment variables, + as specified on the command line. + + + + unset-environment VARIABLE... + + + Unset one or more systemd manager environment + variables. If only a variable name is specified, it will be + removed regardless of its value. If a variable and a value + are specified, the variable is only removed if it has the + specified value. + + + + + import-environment + VARIABLE... + + + + Import all, one or more environment variables set on + the client into the systemd manager environment block. If + no arguments are passed, the entire environment block is + imported. Otherwise, a list of one or more environment + variable names should be passed, whose client-side values + are then imported into the manager's environment + block. + + + + + + + Manager Lifecycle Commands + + + + daemon-reload + + + Reload the systemd manager configuration. This will + rerun all generators (see + systemd.generator7), + reload all unit files, and recreate the entire dependency + tree. While the daemon is being reloaded, all sockets + systemd listens on behalf of user configuration will stay + accessible. + + This command should not be confused with the + reload command. + + + + daemon-reexec + + + Reexecute the systemd manager. This will serialize the + manager state, reexecute the process and deserialize the + state again. This command is of little use except for + debugging and package upgrades. Sometimes, it might be + helpful as a heavy-weight daemon-reload. + While the daemon is being reexecuted, all sockets systemd listening + on behalf of user configuration will stay accessible. + + + + + + + + System Commands + + + + is-system-running + + + Checks whether the system is operational. This + returns success (exit code 0) when the system is fully up + and running, specifically not in startup, shutdown or + maintenance mode, and with no failed services. Failure is + returned otherwise (exit code non-zero). In addition, the + current state is printed in a short string to standard + output, see the table below. Use to + suppress this output. + + + <command>is-system-running</command> output + + + + + + + Name + Description + Exit Code + + + + + initializing + Early bootup, before + basic.target is reached + or the maintenance state entered. + + > 0 + + + starting + Late bootup, before the job queue + becomes idle for the first time, or one of the + rescue targets are reached. + > 0 + + + running + The system is fully + operational. + 0 + + + degraded + The system is operational but one or more + units failed. + > 0 + + + maintenance + The rescue or emergency target is + active. + > 0 + + + stopping + The manager is shutting + down. + > 0 + + + offline + The manager is not + running. Specifically, this is the operational + state if an incompatible program is running as + system manager (PID 1). + > 0 + + + unknown + The operational state could not be + determined, due to lack of resources or another + error cause. + > 0 + + + +
+
+
+ + + default + + + Enter default mode. This is mostly equivalent to + isolate default.target. + + + + + rescue + + + Enter rescue mode. This is mostly equivalent to + isolate rescue.target, but also prints a + wall message to all users. + + + + emergency + + + Enter emergency mode. This is mostly equivalent to + isolate emergency.target, but also prints + a wall message to all users. + + + + halt + + + Shut down and halt the system. This is mostly equivalent to start halt.target + --job-mode=replace-irreversibly, but also prints a wall message to all users. If combined with + , shutdown of all running services is skipped, however all processes are killed and + all file systems are unmounted or mounted read-only, immediately followed by the system halt. If + is specified twice, the operation is immediately executed without terminating any + processes or unmounting any file systems. This may result in data loss. Note that when + is specified twice the halt operation is executed by + systemctl itself, and the system manager is not contacted. This means the command should + succeed even when the system manager hangs or crashed. + + + + poweroff + + + Shut down and power-off the system. This is mostly equivalent to start poweroff.target + --job-mode=replace-irreversibly, but also prints a wall message to all users. If combined with + , shutdown of all running services is skipped, however all processes are killed and + all file systems are unmounted or mounted read-only, immediately followed by the powering off. If + is specified twice, the operation is immediately executed without terminating any + processes or unmounting any file systems. This may result in data loss. Note that when + is specified twice the power-off operation is executed by + systemctl itself, and the system manager is not contacted. This means the command should + succeed even when the system manager hangs or crashed. + + + + reboot arg + + + Shut down and reboot the system. This is mostly equivalent to start reboot.target + --job-mode=replace-irreversibly, but also prints a wall message to all users. If combined with + , shutdown of all running services is skipped, however all processes are killed and + all file systems are unmounted or mounted read-only, immediately followed by the reboot. If + is specified twice, the operation is immediately executed without terminating any + processes or unmounting any file systems. This may result in data loss. Note that when + is specified twice the reboot operation is executed by + systemctl itself, and the system manager is not contacted. This means the command should + succeed even when the system manager hangs or crashed. + + If the optional argument + arg is given, it will be passed + as the optional argument to the + reboot2 + system call. The value is architecture and firmware + specific. As an example, recovery might + be used to trigger system recovery, and + fota might be used to trigger a + firmware over the air update. + + + + + kexec + + + Shut down and reboot the system via kexec. This is + mostly equivalent to start kexec.target --job-mode=replace-irreversibly, + but also prints a wall message to all users. If combined + with , shutdown of all running + services is skipped, however all processes are killed and + all file systems are unmounted or mounted read-only, + immediately followed by the reboot. + + + + + exit EXIT_CODE + + + Ask the systemd manager to quit. This is only + supported for user service managers (i.e. in conjunction + with the option) or in containers + and is equivalent to poweroff otherwise. + + The systemd manager can exit with a non-zero exit + code if the optional argument + EXIT_CODE is given. + + + + + switch-root ROOT INIT + + + Switches to a different root directory and executes a + new system manager process below it. This is intended for + usage in initial RAM disks ("initrd"), and will transition + from the initrd's system manager process (a.k.a. "init" + process) to the main system manager process. This call takes two + arguments: the directory that is to become the new root directory, and + the path to the new system manager binary below it to + execute as PID 1. If the latter is omitted or the empty + string, a systemd binary will automatically be searched for + and used as init. If the system manager path is omitted or + equal to the empty string, the state of the initrd's system + manager process is passed to the main system manager, which + allows later introspection of the state of the services + involved in the initrd boot. + + + + + suspend + + + Suspend the system. This will trigger activation of + the special suspend.target target. + + + + + + hibernate + + + Hibernate the system. This will trigger activation of + the special hibernate.target target. + + + + + + hybrid-sleep + + + Hibernate and suspend the system. This will trigger + activation of the special + hybrid-sleep.target target. + + +
+
+ + + Parameter Syntax + + Unit commands listed above take either a single unit name (designated as NAME), + or multiple unit specifications (designated as PATTERN...). In the first case, the + unit name with or without a suffix must be given. If the suffix is not specified (unit name is "abbreviated"), + systemctl will append a suitable suffix, .service by default, and a type-specific suffix in + case of commands which operate only on specific unit types. For example, + # systemctl start sshd and + # systemctl start sshd.service + are equivalent, as are + # systemctl isolate default + and + # systemctl isolate default.target + Note that (absolute) paths to device nodes are automatically converted to device unit names, and other (absolute) + paths to mount unit names. + # systemctl status /dev/sda +# systemctl status /home + are equivalent to: + # systemctl status dev-sda.device +# systemctl status home.mount + In the second case, shell-style globs will be matched against the primary names of all currently loaded units; + literal unit names, with or without a suffix, will be treated as in the first case. This means that literal unit + names always refer to exactly one unit, but globs may match zero units and this is not considered an + error. + + Glob patterns use + fnmatch3, + so normal shell-style globbing rules are used, and + *, ?, + [] may be used. See + glob7 + for more details. The patterns are matched against the primary names of + currently loaded units, and patterns which do not match anything + are silently skipped. For example: + # systemctl stop sshd@*.service + will stop all sshd@.service instances. Note that alias names of units, and units that aren't + loaded are not considered for glob expansion. + + + For unit file commands, the specified NAME should be the name of the unit file + (possibly abbreviated, see above), or the absolute path to the unit file: + # systemctl enable foo.service + or + # systemctl link /path/to/foo.service + + + +
+ + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + Environment + + + + $SYSTEMD_EDITOR + + Editor to use when editing units; overrides + $EDITOR and $VISUAL. If neither + $SYSTEMD_EDITOR nor $EDITOR nor + $VISUAL are present or if it is set to an empty + string or if their execution failed, systemctl will try to execute well + known editors in this order: + editor1, + nano1, + vim1, + vi1. + + + + + + + + + See Also + + systemd1, + journalctl1, + loginctl1, + machinectl1, + systemd.unit5, + systemd.resource-control5, + systemd.special7, + wall1, + systemd.preset5, + systemd.generator7, + glob7 + + + +
diff --git a/src/grp-system/systemctl/systemd-sysv-install.SKELETON b/src/grp-system/systemctl/systemd-sysv-install.SKELETON new file mode 100755 index 0000000000..a53a3e6221 --- /dev/null +++ b/src/grp-system/systemctl/systemd-sysv-install.SKELETON @@ -0,0 +1,47 @@ +#!/bin/sh +# This script is called by "systemctl enable/disable" when the given unit is a +# SysV init.d script. It needs to call the distribution's mechanism for +# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This +# can optionally take a --root argument for enabling a SysV init script +# in a chroot or similar. +set -e + +usage() { + echo "Usage: $0 [--root=path] enable|disable|is-enabled " >&2 + exit 1 +} + +# parse options +eval set -- "$(getopt -o r: --long root: -- "$@")" +while true; do + case "$1" in + -r|--root) + ROOT="$2" + shift 2 ;; + --) shift ; break ;; + *) usage ;; + esac +done + +NAME="$2" +[ -n "$NAME" ] || usage + +case "$1" in + enable) + # call the command to enable SysV init script $NAME here + # (consider optional $ROOT) + echo "IMPLEMENT ME: enabling SysV init.d script $NAME" + ;; + disable) + # call the command to disable SysV init script $NAME here + # (consider optional $ROOT) + echo "IMPLEMENT ME: disabling SysV init.d script $NAME" + ;; + is-enabled) + # exit with 0 if $NAME is enabled, non-zero if it is disabled + # (consider optional $ROOT) + echo "IMPLEMENT ME: checking SysV init.d script $NAME" + ;; + *) + usage ;; +esac diff --git a/src/grp-system/systemctl/systemd.preset.xml b/src/grp-system/systemctl/systemd.preset.xml new file mode 100644 index 0000000000..b7164014f0 --- /dev/null +++ b/src/grp-system/systemctl/systemd.preset.xml @@ -0,0 +1,189 @@ + + + + + + + + systemd.preset + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.preset + 5 + + + + systemd.preset + Service enablement presets + + + + /etc/systemd/system-preset/*.preset + /run/systemd/system-preset/*.preset + /usr/lib/systemd/system-preset/*.preset + /etc/systemd/user-preset/*.preset + /run/systemd/user-preset/*.preset + /usr/lib/systemd/user-preset/*.preset + + + + Description + + Preset files may be used to encode policy which units shall + be enabled by default and which ones shall be disabled. They are + read by systemctl preset (for more information + see + systemctl1) + which uses this information to enable or disable a unit according + to preset policy. systemctl preset is used by + the post install scriptlets of RPM packages (or other OS package + formats), to enable/disable specific units by default on package + installation, enforcing distribution, spin or administrator preset + policy. This allows choosing a certain set of units to be + enabled/disabled even before installing the actual package. + + For more information on the preset logic please have a look + at the Presets + document. + + It is not recommended to ship preset files within the + respective software packages implementing the units, but rather + centralize them in a distribution or spin default policy, which + can be amended by administrator policy. + + If no preset files exist, systemctl + preset will enable all units that are installed by + default. If this is not desired and all units shall rather be + disabled, it is necessary to ship a preset file with a single, + catchall "disable *" line. (See example 1, + below.) + + + + Preset File Format + + The preset files contain a list of directives consisting of + either the word enable or + disable followed by a space and a unit name + (possibly with shell style wildcards), separated by newlines. + Empty lines and lines whose first non-whitespace character is # or + ; are ignored. + + Two different directives are understood: + enable may be used to enable units by default, + disable to disable units by default. + + If multiple lines apply to a unit name, the first matching + one takes precedence over all others. + + Each preset file shall be named in the style of + <priority>-<policy-name>.preset. Files + in /etc/ override files with the same name in + /usr/lib/ and /run/. + Files in /run/ override files with the same + name in /usr/lib/. Packages should install + their preset files in /usr/lib/. Files in + /etc/ are reserved for the local + administrator, who may use this logic to override the preset files + installed by vendor packages. All preset files are sorted by their + filename in lexicographic order, regardless of which of the + directories they reside in. If multiple files specify the same + unit name, the entry in the file with the lexicographically + earliest name will be applied. It is recommended to prefix all + filenames with a two-digit number and a dash, to simplify the + ordering of the files. + + If the administrator wants to disable a preset file supplied + by the vendor, the recommended way is to place a symlink to + /dev/null in + /etc/systemd/system-preset/ bearing the same + filename. + + + + Example + + + Default off example <filename>/usr/lib/systemd/system-preset/99-default.preset</filename>: + + disable * + + + This disables all units. Due to the filename prefix + 99-, it will be read last and hence can easily + be overridden by spin or administrator preset policy or + suchlike. + + + A GNOME spin example <filename>/usr/lib/systemd/system-preset/50-gnome.preset</filename>: + + enable gdm.service +enable colord.service +enable accounts-daemon.service +enable avahi-daemon.* + + + + This enables the three mentioned units, plus all + avahi-daemon regardless of which unit type. A + file like this could be useful for inclusion in a GNOME spin of a + distribution. It will ensure that the units necessary for GNOME + are properly enabled as they are installed. It leaves all other + units untouched, and subject to other (later) preset files, for + example like the one from the first example above. + + + Administrator policy <filename>/etc/systemd/system-preset/00-lennart.preset</filename>: + + enable httpd.service +enable sshd.service +enable postfix.service +disable * + + + This enables three specific services and disables all + others. This is useful for administrators to specifically select + the units to enable, and disable all others. Due to the filename + prefix 00- it will be read early and hence + overrides all other preset policy files. + + + + See Also + + systemd1, + systemctl1, + systemd-delta1 + + + + diff --git a/src/grp-system/systemctl/telinit.xml b/src/grp-system/systemctl/telinit.xml new file mode 100644 index 0000000000..02d31fbd46 --- /dev/null +++ b/src/grp-system/systemctl/telinit.xml @@ -0,0 +1,179 @@ + + + + + + + + + telinit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + telinit + 8 + + + + telinit + Change SysV runlevel + + + + + telinit OPTIONS COMMAND + + + + + Description + + telinit may be used to change the SysV + system runlevel. Since the concept of SysV runlevels is obsolete + the runlevel requests will be transparently translated into + systemd unit activation requests. + + + + + Options + + The following options are understood: + + + + + + + + + + + + Do not send wall message before + reboot/halt/power-off. + + + + The following commands are understood: + + + + 0 + + Power-off the machine. This is translated into + an activation request for poweroff.target + and is equivalent to systemctl + poweroff. + + + + 6 + + Reboot the machine. This is translated into an + activation request for reboot.target and + is equivalent to systemctl + reboot. + + + + 2 + 3 + 4 + 5 + + Change the SysV runlevel. This is translated + into an activation request for + runlevel2.target, + runlevel3.target, ... and is equivalent + to systemctl isolate runlevel2.target, + systemctl isolate runlevel3.target, + ... + + + + 1 + s + S + + Change into system rescue mode. This is + translated into an activation request for + rescue.target and is equivalent to + systemctl rescue. + + + + q + Q + + Reload daemon configuration. This is + equivalent to systemctl + daemon-reload. + + + + u + U + + Serialize state, reexecute daemon and + deserialize state again. This is equivalent to + systemctl daemon-reexec. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + Notes + + This is a legacy command available for compatibility only. + It should not be used anymore, as the concept of runlevels is + obsolete. + + + + See Also + + systemd1, + systemctl1, + wall1 + + + + diff --git a/src/grp-system/systemd-shutdown/Makefile b/src/grp-system/systemd-shutdown/Makefile new file mode 100644 index 0000000000..f68758174a --- /dev/null +++ b/src/grp-system/systemd-shutdown/Makefile @@ -0,0 +1,39 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-shutdown +systemd_shutdown_SOURCES = \ + src/core/umount.c \ + src/core/umount.h \ + src/core/shutdown.c \ + src/core/mount-setup.c \ + src/core/mount-setup.h \ + src/core/killall.h \ + src/core/killall.c + +systemd_shutdown_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/systemd-shutdown/halt.target b/src/grp-system/systemd-shutdown/halt.target new file mode 100644 index 0000000000..a21d984b26 --- /dev/null +++ b/src/grp-system/systemd-shutdown/halt.target @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Halt +Documentation=man:systemd.special(7) +DefaultDependencies=no +Requires=systemd-halt.service +After=systemd-halt.service +AllowIsolate=yes + +[Install] +Alias=ctrl-alt-del.target diff --git a/src/grp-system/systemd-shutdown/kexec.target b/src/grp-system/systemd-shutdown/kexec.target new file mode 100644 index 0000000000..90795d0c5a --- /dev/null +++ b/src/grp-system/systemd-shutdown/kexec.target @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot via kexec +Documentation=man:systemd.special(7) +DefaultDependencies=no +Requires=systemd-kexec.service +After=systemd-kexec.service +AllowIsolate=yes + +[Install] +Alias=ctrl-alt-del.target diff --git a/src/grp-system/systemd-shutdown/killall.c b/src/grp-system/systemd-shutdown/killall.c new file mode 120000 index 0000000000..5ddb36306b --- /dev/null +++ b/src/grp-system/systemd-shutdown/killall.c @@ -0,0 +1 @@ +../libcore/killall.c \ No newline at end of file diff --git a/src/grp-system/systemd-shutdown/killall.h b/src/grp-system/systemd-shutdown/killall.h new file mode 120000 index 0000000000..60b334fe91 --- /dev/null +++ b/src/grp-system/systemd-shutdown/killall.h @@ -0,0 +1 @@ +../libcore/killall.h \ No newline at end of file diff --git a/src/grp-system/systemd-shutdown/mount-setup.c b/src/grp-system/systemd-shutdown/mount-setup.c new file mode 120000 index 0000000000..a4ab487157 --- /dev/null +++ b/src/grp-system/systemd-shutdown/mount-setup.c @@ -0,0 +1 @@ +../libcore/mount-setup.c \ No newline at end of file diff --git a/src/grp-system/systemd-shutdown/mount-setup.h b/src/grp-system/systemd-shutdown/mount-setup.h new file mode 120000 index 0000000000..1f984851f8 --- /dev/null +++ b/src/grp-system/systemd-shutdown/mount-setup.h @@ -0,0 +1 @@ +../libcore/mount-setup.h \ No newline at end of file diff --git a/src/grp-system/systemd-shutdown/poweroff.target b/src/grp-system/systemd-shutdown/poweroff.target new file mode 100644 index 0000000000..dd92d816ca --- /dev/null +++ b/src/grp-system/systemd-shutdown/poweroff.target @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Power-Off +Documentation=man:systemd.special(7) +DefaultDependencies=no +Requires=systemd-poweroff.service +After=systemd-poweroff.service +AllowIsolate=yes +JobTimeoutSec=30min +JobTimeoutAction=poweroff-force + +[Install] +Alias=ctrl-alt-del.target diff --git a/src/grp-system/systemd-shutdown/reboot.target b/src/grp-system/systemd-shutdown/reboot.target new file mode 100644 index 0000000000..668b98d9e4 --- /dev/null +++ b/src/grp-system/systemd-shutdown/reboot.target @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot +Documentation=man:systemd.special(7) +DefaultDependencies=no +Requires=systemd-reboot.service +After=systemd-reboot.service +AllowIsolate=yes +JobTimeoutSec=30min +JobTimeoutAction=reboot-force + +[Install] +Alias=ctrl-alt-del.target diff --git a/src/grp-system/systemd-shutdown/shutdown.c b/src/grp-system/systemd-shutdown/shutdown.c new file mode 100644 index 0000000000..093b6eea87 --- /dev/null +++ b/src/grp-system/systemd-shutdown/shutdown.c @@ -0,0 +1,446 @@ +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/def.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/switch-root.h" +#include "shared/watchdog.h" + +#include "killall.h" +#include "umount.h" + +#define FINALIZE_ATTEMPTS 50 + +static char* arg_verb; +static uint8_t arg_exit_code; + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_LOG_LEVEL = 0x100, + ARG_LOG_TARGET, + ARG_LOG_COLOR, + ARG_LOG_LOCATION, + ARG_EXIT_CODE, + }; + + static const struct option options[] = { + { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, + { "log-target", required_argument, NULL, ARG_LOG_TARGET }, + { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, + { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, + {} + }; + + int c, r; + + assert(argc >= 1); + assert(argv); + + /* "-" prevents getopt from permuting argv[] and moving the verb away + * from argv[1]. Our interface to initrd promises it'll be there. */ + while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0) + switch (c) { + + case ARG_LOG_LEVEL: + r = log_set_max_level_from_string(optarg); + if (r < 0) + log_error("Failed to parse log level %s, ignoring.", optarg); + + break; + + case ARG_LOG_TARGET: + r = log_set_target_from_string(optarg); + if (r < 0) + log_error("Failed to parse log target %s, ignoring", optarg); + + break; + + case ARG_LOG_COLOR: + + if (optarg) { + r = log_show_color_from_string(optarg); + if (r < 0) + log_error("Failed to parse log color setting %s, ignoring", optarg); + } else + log_show_color(true); + + break; + + case ARG_LOG_LOCATION: + if (optarg) { + r = log_show_location_from_string(optarg); + if (r < 0) + log_error("Failed to parse log location setting %s, ignoring", optarg); + } else + log_show_location(true); + + break; + + case ARG_EXIT_CODE: + r = safe_atou8(optarg, &arg_exit_code); + if (r < 0) + log_error("Failed to parse exit code %s, ignoring", optarg); + + break; + + case '\001': + if (!arg_verb) + arg_verb = optarg; + else + log_error("Excess arguments, ignoring"); + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option code."); + } + + if (!arg_verb) { + log_error("Verb argument missing."); + return -EINVAL; + } + + return 0; +} + +static int switch_root_initramfs(void) { + if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m"); + + if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) + return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m"); + + /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors. + * /run/initramfs/shutdown will take care of these. + * Also do not detach the old root, because /run/initramfs/shutdown needs to access it. + */ + return switch_root("/run/initramfs", "/oldroot", false, MS_BIND); +} + +int main(int argc, char *argv[]) { + bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; + bool in_container, use_watchdog = false; + _cleanup_free_ char *cgroup = NULL; + char *arguments[3]; + unsigned retries; + int cmd, r; + static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; + + log_parse_environment(); + r = parse_argv(argc, argv); + if (r < 0) + goto error; + + /* journald will die if not gone yet. The log target defaults + * to console, but may have been changed by command line options. */ + + log_close_console(); /* force reopen of /dev/console */ + log_open(); + + umask(0022); + + if (getpid() != 1) { + log_error("Not executed by init (PID 1)."); + r = -EPERM; + goto error; + } + + if (streq(arg_verb, "reboot")) + cmd = RB_AUTOBOOT; + else if (streq(arg_verb, "poweroff")) + cmd = RB_POWER_OFF; + else if (streq(arg_verb, "halt")) + cmd = RB_HALT_SYSTEM; + else if (streq(arg_verb, "kexec")) + cmd = LINUX_REBOOT_CMD_KEXEC; + else if (streq(arg_verb, "exit")) + cmd = 0; /* ignored, just checking that arg_verb is valid */ + else { + r = -EINVAL; + log_error("Unknown action '%s'.", arg_verb); + goto error; + } + + (void) cg_get_root_path(&cgroup); + in_container = detect_container() > 0; + + use_watchdog = !!getenv("WATCHDOG_USEC"); + + /* Lock us into memory */ + mlockall(MCL_CURRENT|MCL_FUTURE); + + /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that + * slow IO is processed here already and the final process killing spree is not impacted by processes + * desperately trying to sync IO to disk within their timeout. */ + if (!in_container) + sync(); + + log_info("Sending SIGTERM to remaining processes..."); + broadcast_signal(SIGTERM, true, true); + + log_info("Sending SIGKILL to remaining processes..."); + broadcast_signal(SIGKILL, true, false); + + need_umount = !in_container; + need_swapoff = !in_container; + need_loop_detach = !in_container; + need_dm_detach = !in_container; + + /* Unmount all mountpoints, swaps, and loopback devices */ + for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { + bool changed = false; + + if (use_watchdog) + watchdog_ping(); + + /* Let's trim the cgroup tree on each iteration so + that we leave an empty cgroup tree around, so that + container managers get a nice notify event when we + are down */ + if (cgroup) + cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); + + if (need_umount) { + log_info("Unmounting file systems."); + r = umount_all(&changed); + if (r == 0) { + need_umount = false; + log_info("All filesystems unmounted."); + } else if (r > 0) + log_info("Not all file systems unmounted, %d left.", r); + else + log_error_errno(r, "Failed to unmount file systems: %m"); + } + + if (need_swapoff) { + log_info("Deactivating swaps."); + r = swapoff_all(&changed); + if (r == 0) { + need_swapoff = false; + log_info("All swaps deactivated."); + } else if (r > 0) + log_info("Not all swaps deactivated, %d left.", r); + else + log_error_errno(r, "Failed to deactivate swaps: %m"); + } + + if (need_loop_detach) { + log_info("Detaching loop devices."); + r = loopback_detach_all(&changed); + if (r == 0) { + need_loop_detach = false; + log_info("All loop devices detached."); + } else if (r > 0) + log_info("Not all loop devices detached, %d left.", r); + else + log_error_errno(r, "Failed to detach loop devices: %m"); + } + + if (need_dm_detach) { + log_info("Detaching DM devices."); + r = dm_detach_all(&changed); + if (r == 0) { + need_dm_detach = false; + log_info("All DM devices detached."); + } else if (r > 0) + log_info("Not all DM devices detached, %d left.", r); + else + log_error_errno(r, "Failed to detach DM devices: %m"); + } + + if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { + if (retries > 0) + log_info("All filesystems, swaps, loop devices, DM devices detached."); + /* Yay, done */ + goto initrd_jump; + } + + /* If in this iteration we didn't manage to + * unmount/deactivate anything, we simply give up */ + if (!changed) { + log_info("Cannot finalize remaining%s%s%s%s continuing.", + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + goto initrd_jump; + } + + log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.", + retries + 1, + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + } + + log_error("Too many iterations, giving up."); + + initrd_jump: + + arguments[0] = NULL; + arguments[1] = arg_verb; + arguments[2] = NULL; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + if (!in_container && !in_initrd() && + access("/run/initramfs/shutdown", X_OK) == 0) { + r = switch_root_initramfs(); + if (r >= 0) { + argv[0] = (char*) "/shutdown"; + + setsid(); + make_console_stdio(); + + log_info("Successfully changed into root pivot.\n" + "Returning to initrd..."); + + execv("/shutdown", argv); + log_error_errno(errno, "Failed to execute shutdown binary: %m"); + } else + log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); + + } + + if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) + log_error("Failed to finalize %s%s%s%s ignoring", + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + + /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be + * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we + * sync'ed things already once above, but we did some more work since then which might have caused IO, hence + * let's doit once more. */ + if (!in_container) + sync(); + + if (streq(arg_verb, "exit")) { + if (in_container) + exit(arg_exit_code); + else { + /* We cannot exit() on the host, fallback on another + * method. */ + cmd = RB_POWER_OFF; + } + } + + switch (cmd) { + + case LINUX_REBOOT_CMD_KEXEC: + + if (!in_container) { + /* We cheat and exec kexec to avoid doing all its work */ + pid_t pid; + + log_info("Rebooting with kexec."); + + pid = fork(); + if (pid < 0) + log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + const char * const args[] = { + KEXEC, "-e", NULL + }; + + /* Child */ + + execv(args[0], (char * const *) args); + _exit(EXIT_FAILURE); + } else + wait_for_terminate_and_warn("kexec", pid, true); + } + + cmd = RB_AUTOBOOT; + /* Fall through */ + + case RB_AUTOBOOT: + + if (!in_container) { + _cleanup_free_ char *param = NULL; + + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { + log_info("Rebooting with argument '%s'.", param); + syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); + } + } + + log_info("Rebooting."); + break; + + case RB_POWER_OFF: + log_info("Powering off."); + break; + + case RB_HALT_SYSTEM: + log_info("Halting system."); + break; + + default: + assert_not_reached("Unknown magic"); + } + + reboot(cmd); + if (errno == EPERM && in_container) { + /* If we are in a container, and we lacked + * CAP_SYS_BOOT just exit, this will kill our + * container for good. */ + log_info("Exiting container."); + exit(0); + } + + r = log_error_errno(errno, "Failed to invoke reboot(): %m"); + + error: + log_emergency_errno(r, "Critical error while doing system shutdown: %m"); + freeze(); +} diff --git a/src/grp-system/systemd-shutdown/systemd-halt.service.in b/src/grp-system/systemd-shutdown/systemd-halt.service.in new file mode 100644 index 0000000000..d55d622c1c --- /dev/null +++ b/src/grp-system/systemd-shutdown/systemd-halt.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Halt +Documentation=man:systemd-halt.service(8) +DefaultDependencies=no +Requires=shutdown.target umount.target final.target +After=shutdown.target umount.target final.target + +[Service] +Type=oneshot +ExecStart=@SYSTEMCTL@ --force halt diff --git a/src/grp-system/systemd-shutdown/systemd-kexec.service.in b/src/grp-system/systemd-shutdown/systemd-kexec.service.in new file mode 100644 index 0000000000..61303f917f --- /dev/null +++ b/src/grp-system/systemd-shutdown/systemd-kexec.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot via kexec +Documentation=man:systemd-halt.service(8) +DefaultDependencies=no +Requires=shutdown.target umount.target final.target +After=shutdown.target umount.target final.target + +[Service] +Type=oneshot +ExecStart=@SYSTEMCTL@ --force kexec diff --git a/src/grp-system/systemd-shutdown/systemd-poweroff.service.in b/src/grp-system/systemd-shutdown/systemd-poweroff.service.in new file mode 100644 index 0000000000..3630719733 --- /dev/null +++ b/src/grp-system/systemd-shutdown/systemd-poweroff.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Power-Off +Documentation=man:systemd-halt.service(8) +DefaultDependencies=no +Requires=shutdown.target umount.target final.target +After=shutdown.target umount.target final.target + +[Service] +Type=oneshot +ExecStart=@SYSTEMCTL@ --force poweroff diff --git a/src/grp-system/systemd-shutdown/systemd-reboot.service.in b/src/grp-system/systemd-shutdown/systemd-reboot.service.in new file mode 100644 index 0000000000..d99bd3e701 --- /dev/null +++ b/src/grp-system/systemd-shutdown/systemd-reboot.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot +Documentation=man:systemd-halt.service(8) +DefaultDependencies=no +Requires=shutdown.target umount.target final.target +After=shutdown.target umount.target final.target + +[Service] +Type=oneshot +ExecStart=@SYSTEMCTL@ --force reboot diff --git a/src/grp-system/systemd-shutdown/systemd-shutdown.xml b/src/grp-system/systemd-shutdown/systemd-shutdown.xml new file mode 100644 index 0000000000..d16e5d628f --- /dev/null +++ b/src/grp-system/systemd-shutdown/systemd-shutdown.xml @@ -0,0 +1,119 @@ + + + + + + + + + systemd-halt.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-halt.service + 8 + + + + systemd-halt.service + systemd-poweroff.service + systemd-reboot.service + systemd-kexec.service + systemd-shutdown + System shutdown logic + + + + systemd-halt.service + systemd-poweroff.service + systemd-reboot.service + systemd-kexec.service + /usr/lib/systemd/systemd-shutdown + /usr/lib/systemd/system-shutdown/ + + + + Description + + systemd-halt.service is a system + service that is pulled in by halt.target and + is responsible for the actual system halt. Similarly, + systemd-poweroff.service is pulled in by + poweroff.target, + systemd-reboot.service by + reboot.target and + systemd-kexec.service by + kexec.target to execute the respective + actions. + + When these services are run, they ensure that PID 1 is + replaced by the + /usr/lib/systemd/systemd-shutdown tool which + is then responsible for the actual shutdown. Before shutting down, + this binary will try to unmount all remaining file systems, + disable all remaining swap devices, detach all remaining storage + devices and kill all remaining processes. + + It is necessary to have this code in a separate binary + because otherwise rebooting after an upgrade might be broken — the + running PID 1 could still depend on libraries which are not + available any more, thus keeping the file system busy, which then + cannot be re-mounted read-only. + + Immediately before executing the actual system + halt/poweroff/reboot/kexec systemd-shutdown + will run all executables in + /usr/lib/systemd/system-shutdown/ and pass + one arguments to them: either halt, + poweroff, reboot or + kexec, depending on the chosen action. All + executables in this directory are executed in parallel, and + execution of the action is not continued before all executables + finished. + + Note that systemd-halt.service (and the + related units) should never be executed directly. Instead, trigger + system shutdown with a command such as systemctl + halt or suchlike. + + + + See Also + + systemd1, + systemctl1, + systemd.special7, + reboot2, + systemd-suspend.service8 + + + + diff --git a/src/grp-system/systemd-shutdown/umount.c b/src/grp-system/systemd-shutdown/umount.c new file mode 100644 index 0000000000..0079f8ec78 --- /dev/null +++ b/src/grp-system/systemd-shutdown/umount.c @@ -0,0 +1,616 @@ +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/list.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/fstab-util.h" +#include "shared/udev-util.h" + +#include "mount-setup.h" +#include "umount.h" + +typedef struct MountPoint { + char *path; + char *options; + dev_t devnum; + LIST_FIELDS(struct MountPoint, mount_point); +} MountPoint; + +static void mount_point_free(MountPoint **head, MountPoint *m) { + assert(head); + assert(m); + + LIST_REMOVE(mount_point, *head, m); + + free(m->path); + free(m); +} + +static void mount_points_list_free(MountPoint **head) { + assert(head); + + while (*head) + mount_point_free(head, *head); +} + +static int mount_points_list_get(MountPoint **head) { + _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; + unsigned int i; + int r; + + assert(head); + + proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); + if (!proc_self_mountinfo) + return -errno; + + for (i = 1;; i++) { + _cleanup_free_ char *path = NULL, *options = NULL; + char *p = NULL; + MountPoint *m; + 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 flags */ + "%*[^-]" /* (7) optional fields */ + "- " /* (8) separator */ + "%*s " /* (9) file system type */ + "%*s" /* (10) mount source */ + "%ms" /* (11) mount options */ + "%*[^\n]", /* some rubbish at the end */ + &path, &options); + if (k != 2) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/self/mountinfo:%u.", i); + continue; + } + + r = cunescape(path, UNESCAPE_RELAX, &p); + if (r < 0) + return r; + + /* Ignore mount points we can't unmount because they + * are API or because we are keeping them open (like + * /dev/console). Also, ignore all mounts below API + * file systems, since they are likely virtual too, + * and hence not worth spending time on. Also, in + * unprivileged containers we might lack the rights to + * unmount these things, hence don't bother. */ + if (mount_point_is_api(p) || + mount_point_ignore(p) || + path_startswith(p, "/dev") || + path_startswith(p, "/sys") || + path_startswith(p, "/proc")) { + free(p); + continue; + } + + m = new0(MountPoint, 1); + if (!m) { + free(p); + return -ENOMEM; + } + + m->path = p; + m->options = options; + options = NULL; + + LIST_PREPEND(mount_point, *head, m); + } + + return 0; +} + +static int swap_list_get(MountPoint **head) { + _cleanup_fclose_ FILE *proc_swaps = NULL; + unsigned int i; + int r; + + assert(head); + + proc_swaps = fopen("/proc/swaps", "re"); + if (!proc_swaps) + return (errno == ENOENT) ? 0 : -errno; + + (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); + + for (i = 2;; i++) { + MountPoint *swap; + char *dev = NULL, *d; + int k; + + k = fscanf(proc_swaps, + "%ms " /* device/file */ + "%*s " /* type of swap */ + "%*s " /* swap size */ + "%*s " /* used */ + "%*s\n", /* priority */ + &dev); + + if (k != 1) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u.", i); + free(dev); + continue; + } + + if (endswith(dev, " (deleted)")) { + free(dev); + continue; + } + + r = cunescape(dev, UNESCAPE_RELAX, &d); + free(dev); + if (r < 0) + return r; + + swap = new0(MountPoint, 1); + if (!swap) { + free(d); + return -ENOMEM; + } + + swap->path = d; + LIST_PREPEND(mount_point, *head, swap); + } + + return 0; +} + +static int loopback_list_get(MountPoint **head) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + int r; + + assert(head); + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_sysname(e, "loop*"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + MountPoint *lb; + _cleanup_udev_device_unref_ struct udev_device *d; + char *loop; + const char *dn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + dn = udev_device_get_devnode(d); + if (!dn) + continue; + + loop = strdup(dn); + if (!loop) + return -ENOMEM; + + lb = new0(MountPoint, 1); + if (!lb) { + free(loop); + return -ENOMEM; + } + + lb->path = loop; + LIST_PREPEND(mount_point, *head, lb); + } + + return 0; +} + +static int dm_list_get(MountPoint **head) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + int r; + + assert(head); + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return r; + + r = udev_enumerate_add_match_sysname(e, "dm-*"); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + MountPoint *m; + _cleanup_udev_device_unref_ struct udev_device *d; + dev_t devnum; + char *node; + const char *dn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + return -ENOMEM; + + devnum = udev_device_get_devnum(d); + dn = udev_device_get_devnode(d); + if (major(devnum) == 0 || !dn) + continue; + + node = strdup(dn); + if (!node) + return -ENOMEM; + + m = new(MountPoint, 1); + if (!m) { + free(node); + return -ENOMEM; + } + + m->path = node; + m->devnum = devnum; + LIST_PREPEND(mount_point, *head, m); + } + + return 0; +} + +static int delete_loopback(const char *device) { + _cleanup_close_ int fd = -1; + int r; + + fd = open(device, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return errno == ENOENT ? 0 : -errno; + + r = ioctl(fd, LOOP_CLR_FD, 0); + if (r >= 0) + return 1; + + /* ENXIO: not bound, so no error */ + if (errno == ENXIO) + return 0; + + return -errno; +} + +static int delete_dm(dev_t devnum) { + _cleanup_close_ int fd = -1; + int r; + struct dm_ioctl dm = { + .version = {DM_VERSION_MAJOR, + DM_VERSION_MINOR, + DM_VERSION_PATCHLEVEL}, + .data_size = sizeof(dm), + .dev = devnum, + }; + + assert(major(devnum) != 0); + + fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC); + if (fd < 0) + return -errno; + + r = ioctl(fd, DM_DEV_REMOVE, &dm); + return r >= 0 ? 0 : -errno; +} + +static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { + MountPoint *m, *n; + int n_failed = 0; + + assert(head); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + + /* If we are in a container, don't attempt to + read-only mount anything as that brings no real + 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. + * + * Mount points can be stacked. If a mount + * point is stacked below / or /usr, we + * cannot umount or remount it directly, + * since there is no way to refer to the + * underlying mount. There's nothing we can do + * about it for the general case, but we can + * do something about it if it is aliased + * somehwere else via a bind mount. If we + * explicitly remount the super block of that + * alias read-only we hence should be + * relatively safe regarding keeping the fs we + * can otherwise not see dirty. */ + 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 + * anyway, since we are running from it. They have + * already been remounted ro. */ + if (path_equal(m->path, "/") +#ifndef HAVE_SPLIT_USR + || path_equal(m->path, "/usr") +#endif + || path_startswith(m->path, "/run/initramfs") + ) + continue; + + /* Trying to umount. We don't force here since we rely + * on busy NFS and FUSE file systems to return EBUSY + * until we closed everything on top of them. */ + log_info("Unmounting %s.", m->path); + if (umount2(m->path, 0) == 0) { + if (changed) + *changed = true; + + mount_point_free(head, m); + } else if (log_error) { + log_warning_errno(errno, "Could not unmount %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int swap_points_list_off(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0; + + assert(head); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + log_info("Deactivating swap %s.", m->path); + if (swapoff(m->path) == 0) { + if (changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int loopback_points_list_detach(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0, k; + struct stat root_st; + + assert(head); + + k = lstat("/", &root_st); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + int r; + struct stat loopback_st; + + if (k >= 0 && + major(root_st.st_dev) != 0 && + lstat(m->path, &loopback_st) >= 0 && + root_st.st_dev == loopback_st.st_rdev) { + n_failed++; + continue; + } + + log_info("Detaching loopback %s.", m->path); + r = delete_loopback(m->path); + if (r >= 0) { + if (r > 0 && changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning_errno(errno, "Could not detach loopback %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +static int dm_points_list_detach(MountPoint **head, bool *changed) { + MountPoint *m, *n; + int n_failed = 0, k; + struct stat root_st; + + assert(head); + + k = lstat("/", &root_st); + + LIST_FOREACH_SAFE(mount_point, m, n, *head) { + int r; + + if (k >= 0 && + major(root_st.st_dev) != 0 && + root_st.st_dev == m->devnum) { + n_failed++; + continue; + } + + log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum)); + r = delete_dm(m->devnum); + if (r >= 0) { + if (changed) + *changed = true; + + mount_point_free(head, m); + } else { + log_warning_errno(errno, "Could not detach DM %s: %m", m->path); + n_failed++; + } + } + + return n_failed; +} + +int umount_all(bool *changed) { + int r; + bool umount_changed; + LIST_HEAD(MountPoint, mp_list_head); + + LIST_HEAD_INIT(mp_list_head); + r = mount_points_list_get(&mp_list_head); + if (r < 0) + goto end; + + /* retry umount, until nothing can be umounted anymore */ + do { + umount_changed = false; + + mount_points_list_umount(&mp_list_head, &umount_changed, false); + if (umount_changed) + *changed = true; + + } while (umount_changed); + + /* umount one more time with logging enabled */ + r = mount_points_list_umount(&mp_list_head, &umount_changed, true); + if (r <= 0) + goto end; + + end: + mount_points_list_free(&mp_list_head); + + return r; +} + +int swapoff_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, swap_list_head); + + LIST_HEAD_INIT(swap_list_head); + + r = swap_list_get(&swap_list_head); + if (r < 0) + goto end; + + r = swap_points_list_off(&swap_list_head, changed); + + end: + mount_points_list_free(&swap_list_head); + + return r; +} + +int loopback_detach_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, loopback_list_head); + + LIST_HEAD_INIT(loopback_list_head); + + r = loopback_list_get(&loopback_list_head); + if (r < 0) + goto end; + + r = loopback_points_list_detach(&loopback_list_head, changed); + + end: + mount_points_list_free(&loopback_list_head); + + return r; +} + +int dm_detach_all(bool *changed) { + int r; + LIST_HEAD(MountPoint, dm_list_head); + + LIST_HEAD_INIT(dm_list_head); + + r = dm_list_get(&dm_list_head); + if (r < 0) + goto end; + + r = dm_points_list_detach(&dm_list_head, changed); + + end: + mount_points_list_free(&dm_list_head); + + return r; +} diff --git a/src/grp-system/systemd-shutdown/umount.h b/src/grp-system/systemd-shutdown/umount.h new file mode 100644 index 0000000000..4e2215a47d --- /dev/null +++ b/src/grp-system/systemd-shutdown/umount.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int umount_all(bool *changed); + +int swapoff_all(bool *changed); + +int loopback_detach_all(bool *changed); + +int dm_detach_all(bool *changed); diff --git a/src/grp-system/systemd/50-systemd-user.xorg b/src/grp-system/systemd/50-systemd-user.xorg new file mode 100755 index 0000000000..4d49767228 --- /dev/null +++ b/src/grp-system/systemd/50-systemd-user.xorg @@ -0,0 +1,7 @@ +#!/bin/sh + +systemctl --user import-environment DISPLAY XAUTHORITY + +if which dbus-update-activation-environment >/dev/null 2>&1; then + dbus-update-activation-environment DISPLAY XAUTHORITY +fi diff --git a/src/grp-system/systemd/Makefile b/src/grp-system/systemd/Makefile new file mode 100644 index 0000000000..7f7fbb963e --- /dev/null +++ b/src/grp-system/systemd/Makefile @@ -0,0 +1,73 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd +systemd_SOURCES = \ + src/core/main.c + +systemd_CFLAGS = \ + $(SECCOMP_CFLAGS) \ + $(MOUNT_CFLAGS) + +systemd_LDADD = \ + libcore.la + +dist_pkgsysconf_DATA += \ + src/core/system.conf \ + src/core/user.conf + +dist_dbuspolicy_DATA += \ + src/core/org.freedesktop.systemd1.conf + +dist_dbussystemservice_DATA += \ + src/core/org.freedesktop.systemd1.service + +polkitpolicy_in_in_files += \ + src/core/org.freedesktop.systemd1.policy.in.in + +pkgconfigdata_DATA += \ + src/core/systemd.pc + +nodist_rpmmacros_DATA = \ + src/core/macros.systemd + +BUILT_SOURCES += \ + src/core/triggers.systemd + +EXTRA_DIST += \ + src/core/systemd.pc.in \ + src/core/macros.systemd.in \ + src/core/triggers.systemd.in + +dist_xinitrc_SCRIPTS = \ + xorg/50-systemd-user.sh + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.systemd1.busname + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.systemd1.busname + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-system/systemd/macros.systemd.in b/src/grp-system/systemd/macros.systemd.in new file mode 100644 index 0000000000..6e8a3b3e3d --- /dev/null +++ b/src/grp-system/systemd/macros.systemd.in @@ -0,0 +1,113 @@ +# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */ +# +# This file is part of systemd. +# +# Copyright 2012 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# RPM macros for packages installing systemd unit files + +%_unitdir @systemunitdir@ +%_userunitdir @userunitdir@ +%_presetdir @systempresetdir@ +%_udevhwdbdir @udevhwdbdir@ +%_udevrulesdir @udevrulesdir@ +%_journalcatalogdir @catalogdir@ +%_tmpfilesdir @tmpfilesdir@ +%_sysusersdir @sysusersdir@ +%_sysctldir @sysctldir@ +%_binfmtdir @binfmtdir@ +%_systemdgeneratordir @systemgeneratordir@ +%_systemdusergeneratordir @usergeneratordir@ + +%systemd_requires \ +Requires(post): systemd \ +Requires(preun): systemd \ +Requires(postun): systemd \ +%{nil} + +%systemd_ordering \ +OrderWithRequires(post): systemd \ +OrderWithRequires(preun): systemd \ +OrderWithRequires(postun): systemd \ +%{nil} + +%systemd_post() \ +if [ $1 -eq 1 ] ; then \ + # Initial installation \ + systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \ +fi \ +%{nil} + +%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}} + +%systemd_preun() \ +if [ $1 -eq 0 ] ; then \ + # Package removal, not upgrade \ + systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \ +fi \ +%{nil} + +%systemd_user_preun() \ +if [ $1 -eq 0 ] ; then \ + # Package removal, not upgrade \ + systemctl --no-reload --user --global disable %{?*} > /dev/null 2>&1 || : \ +fi \ +%{nil} + +%systemd_postun() %{nil} + +%systemd_user_postun() %{nil} + +%systemd_postun_with_restart() \ +if [ $1 -ge 1 ] ; then \ + # Package upgrade, not uninstall \ + systemctl try-restart %{?*} >/dev/null 2>&1 || : \ +fi \ +%{nil} + +%systemd_user_postun_with_restart() %{nil} + +%udev_hwdb_update() \ +udevadm hwdb --update >/dev/null 2>&1 || : \ +%{nil} + +%udev_rules_update() \ +udevadm control --reload >/dev/null 2>&1 || : \ +%{nil} + +%journal_catalog_update() \ +journalctl --update-catalog >/dev/null 2>&1 || : \ +%{nil} + +%tmpfiles_create() \ +systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \ +%{nil} + +%sysusers_create() \ +systemd-sysusers %{?*} >/dev/null 2>&1 || : \ +%{nil} + +%sysusers_create_inline() \ +echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : \ +%{nil} + +%sysctl_apply() \ +@rootlibexecdir@/systemd-sysctl %{?*} >/dev/null 2>&1 || : \ +%{nil} + +%binfmt_apply() \ +@rootlibexecdir@/systemd-binfmt %{?*} >/dev/null 2>&1 || : \ +%{nil} diff --git a/src/grp-system/systemd/main.c b/src/grp-system/systemd/main.c new file mode 100644 index 0000000000..c558a4b21c --- /dev/null +++ b/src/grp-system/systemd/main.c @@ -0,0 +1,2195 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SECCOMP +#include +#endif +#ifdef HAVE_VALGRIND_VALGRIND_H +#include +#endif + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/build.h" +#include "basic/capability-util.h" +#include "basic/clock-util.h" +#include "basic/cpu-set-util.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/raw-clone.h" +#include "basic/rlimit-util.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/virt.h" +#include "dbus-manager.h" +#include "hostname-setup.h" +#include "ima-setup.h" +#include "killall.h" +#include "kmod-setup.h" +#include "load-fragment.h" +#include "loopback-setup.h" +#include "machine-id-setup.h" +#include "manager.h" +#include "mount-setup.h" +#include "sd-bus/bus-error.h" +#include "selinux-setup.h" +#include "shared/bus-util.h" +#include "shared/conf-parser.h" +#include "shared/fdset.h" +#include "shared/pager.h" +#include "shared/switch-root.h" +#include "shared/watchdog.h" +#include "smack-setup.h" + +static enum { + ACTION_RUN, + ACTION_HELP, + ACTION_VERSION, + ACTION_TEST, + ACTION_DUMP_CONFIGURATION_ITEMS, + ACTION_DONE +} arg_action = ACTION_RUN; +static char *arg_default_unit = NULL; +static bool arg_system = false; +static bool arg_dump_core = true; +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; +static bool arg_no_pager = false; +static char ***arg_join_controllers = NULL; +static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; +static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; +static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC; +static usec_t arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; +static usec_t arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; +static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL; +static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST; +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 = 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; +static FILE* arg_serialization = NULL; +static bool arg_default_cpu_accounting = false; +static bool arg_default_io_accounting = false; +static bool arg_default_blockio_accounting = false; +static bool arg_default_memory_accounting = false; +static bool arg_default_tasks_accounting = true; +static uint64_t arg_default_tasks_max = UINT64_MAX; +static sd_id128_t arg_machine_id = {}; + +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 */ + (void) raise(sig); + else if (!arg_dump_core) + log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); + else { + sa = (struct sigaction) { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + + /* We want to wait for the core process, hence let's enable SIGCHLD */ + (void) sigaction(SIGCHLD, &sa, NULL); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); + else if (pid == 0) { + /* Enable default signal handler for core dump */ + + sa = (struct sigaction) { + .sa_handler = SIG_DFL, + }; + (void) sigaction(sig, &sa, NULL); + + /* Don't limit the coredump size */ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + + /* Just to be sure... */ + (void) chdir("/"); + + /* Raise the signal again */ + pid = raw_getpid(); + (void) kill(pid, sig); /* raise() would kill the parent */ + + assert_not_reached("We shouldn't be here..."); + _exit(EXIT_FAILURE); + } else { + siginfo_t status; + int r; + + /* Order things nicely. */ + r = wait_for_terminate(pid, &status); + if (r < 0) + log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); + else if (status.si_code != CLD_DUMPED) + log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", + signal_to_string(sig), + pid, sigchld_code_to_string(status.si_code), + status.si_status, + strna(status.si_code == CLD_EXITED + ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL) + : signal_to_string(status.si_status))); + else + log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid); + } + } + + if (arg_crash_chvt >= 0) + (void) chvt(arg_crash_chvt); + + sa = (struct sigaction) { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; + + /* Let the kernel reap children for us */ + (void) sigaction(SIGCHLD, &sa, NULL); + + if (arg_crash_shell) { + log_notice("Executing crash shell in 10s..."); + (void) sleep(10); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Failed to fork off crash shell: %m"); + else if (pid == 0) { + (void) setsid(); + (void) make_console_stdio(); + (void) execle("/bin/sh", "/bin/sh", NULL, environ); + + log_emergency_errno(errno, "execle() failed: %m"); + _exit(EXIT_FAILURE); + } else { + log_info("Spawned crash shell as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } + + freeze_or_reboot(); +} + +static void install_crash_handler(void) { + static const struct sigaction sa = { + .sa_handler = crash, + .sa_flags = SA_NODEFER, /* So that we can raise the signal again from the signal handler */ + }; + int r; + + /* We ignore the return value here, since, we don't mind if we + * cannot set up a crash handler */ + r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1); + if (r < 0) + log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); +} + +static int console_setup(void) { + _cleanup_close_ int tty_fd = -1; + int r; + + tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (tty_fd < 0) + return log_error_errno(tty_fd, "Failed to open /dev/console: %m"); + + /* We don't want to force text mode. plymouth may be showing + * pictures already from initrd. */ + r = reset_terminal_fd(tty_fd, false); + if (r < 0) + return log_error_errno(r, "Failed to reset /dev/console: %m"); + + return 0; +} + +static int parse_crash_chvt(const char *value) { + int b; + + if (safe_atoi(value, &arg_crash_chvt) >= 0) + return 0; + + 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; +} + +static int set_machine_id(const char *m) { + sd_id128_t t; + assert(m); + + if (sd_id128_from_string(m, &t) < 0) + return -EINVAL; + + if (sd_id128_is_null(t)) + return -EINVAL; + + arg_machine_id = t; + return 0; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + int r; + + assert(key); + + if (streq(key, "systemd.unit") && value) { + + if (!in_initrd()) + return free_and_strdup(&arg_default_unit, value); + + } else if (streq(key, "rd.systemd.unit") && value) { + + if (in_initrd()) + return free_and_strdup(&arg_default_unit, value); + + } else if (streq(key, "systemd.dump_core") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse dump core switch %s. Ignoring.", 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); + if (r < 0) + log_warning("Failed to parse crash shell switch %s. Ignoring.", value); + else + arg_crash_shell = r; + + } else if (streq(key, "systemd.crash_reboot") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse crash reboot switch %s. Ignoring.", value); + else + arg_crash_reboot = r; + + } else if (streq(key, "systemd.confirm_spawn") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse confirm spawn switch %s. Ignoring.", value); + else + arg_confirm_spawn = r; + + } else if (streq(key, "systemd.show_status") && value) { + + r = parse_show_status(value, &arg_show_status); + if (r < 0) + log_warning("Failed to parse show status switch %s. Ignoring.", value); + + } else if (streq(key, "systemd.default_standard_output") && value) { + + r = exec_output_from_string(value); + if (r < 0) + log_warning("Failed to parse default standard output switch %s. Ignoring.", value); + else + arg_default_std_output = r; + + } else if (streq(key, "systemd.default_standard_error") && value) { + + r = exec_output_from_string(value); + if (r < 0) + log_warning("Failed to parse default standard error switch %s. Ignoring.", value); + else + arg_default_std_error = r; + + } else if (streq(key, "systemd.setenv") && value) { + + if (env_assignment_is_valid(value)) { + char **env; + + env = strv_env_set(arg_default_environment, value); + if (env) + arg_default_environment = env; + else + log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", 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) + arg_show_status = SHOW_STATUS_AUTO; + + } else if (streq(key, "debug") && !value) { + + /* Note that log_parse_environment() handles 'debug' + * too, and sets the log level to LOG_DEBUG. */ + + if (detect_container() > 0) + log_set_target(LOG_TARGET_CONSOLE); + + } else if (!value) { + const char *target; + + /* SysV compatibility */ + target = runlevel_to_target(key); + if (target) + return free_and_strdup(&arg_default_unit, target); + + } else if (streq(key, "systemd.default_timeout_start_sec") && value) { + + r = parse_sec(value, &arg_default_timeout_start_usec); + if (r < 0) + log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); + + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; + } + + return 0; +} + +#define DEFINE_SETTER(name, func, descr) \ + static int 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 r; \ + \ + assert(filename); \ + assert(lvalue); \ + assert(rvalue); \ + \ + r = func(rvalue); \ + if (r < 0) \ + log_syntax(unit, LOG_ERR, filename, line, r, \ + "Invalid " descr "'%s': %m", \ + rvalue); \ + \ + return 0; \ + } + +DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level") +DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target") +DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" ) +DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location") + +static int config_parse_cpu_affinity2( + 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_cpu_free_ cpu_set_t *c = NULL; + int ncpus; + + ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue); + if (ncpus < 0) + return ncpus; + + if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) + log_warning("Failed to set CPU affinity: %m"); + + return 0; +} + +static int config_parse_show_status( + 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 k; + ShowStatus *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + 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); + return 0; + } + + return 0; +} + +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) { + + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + 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; + } + + return 0; +} + +static int config_parse_join_controllers(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; + unsigned n = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + + arg_join_controllers = strv_free_free(arg_join_controllers); + + for (;;) { + _cleanup_free_ char *word = NULL; + char **l; + 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; + + l = strv_split(word, ","); + if (!l) + return log_oom(); + strv_uniq(l); + + if (strv_length(l) <= 1) { + strv_free(l); + continue; + } + + if (!arg_join_controllers) { + arg_join_controllers = new(char**, 2); + if (!arg_join_controllers) { + strv_free(l); + return log_oom(); + } + + arg_join_controllers[0] = l; + arg_join_controllers[1] = NULL; + + n = 1; + } else { + char ***a; + char ***t; + + t = new0(char**, n+2); + if (!t) { + strv_free(l); + return log_oom(); + } + + n = 0; + + for (a = arg_join_controllers; *a; a++) { + + if (strv_overlap(*a, l)) { + if (strv_extend_strv(&l, *a, false) < 0) { + strv_free(l); + strv_free_free(t); + return log_oom(); + } + + } else { + char **c; + + c = strv_copy(*a); + if (!c) { + strv_free(l); + strv_free_free(t); + return log_oom(); + } + + t[n++] = c; + } + } + + t[n++] = strv_uniq(l); + + strv_free_free(arg_join_controllers); + arg_join_controllers = t; + } + } + if (!isempty(rvalue)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + + return 0; +} + +static int parse_config_file(void) { + + const ConfigTableItem items[] = { + { "Manager", "LogLevel", config_parse_level2, 0, NULL }, + { "Manager", "LogTarget", config_parse_target, 0, NULL }, + { "Manager", "LogColor", config_parse_color, 0, NULL }, + { "Manager", "LogLocation", config_parse_location, 0, NULL }, + { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, + { "Manager", "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", "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_capability_set, 0, &arg_capability_bounding_set }, +#ifdef HAVE_SECCOMP + { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, +#endif + { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, + { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec }, + { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, + { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, + { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, + { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, + { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, + { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ + { "Manager", "DefaultStartLimitIntervalSec",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, RLIMIT_CPU, arg_default_rlimit }, + { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit }, + { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit }, + { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit }, + { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit }, + { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit }, + { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit }, + { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit }, + { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit }, + { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit }, + { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit }, + { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit }, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit }, + { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit }, + { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit }, + { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit }, + { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, + { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_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_system ? + PKGSYSCONFDIR "/system.conf" : + PKGSYSCONFDIR "/user.conf"; + + conf_dirs_nulstr = arg_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); + + /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY + * like everywhere else. */ + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; + if (arg_default_timeout_stop_usec <= 0) + arg_default_timeout_stop_usec = USEC_INFINITY; + + return 0; +} + +static void manager_set_defaults(Manager *m) { + + assert(m); + + m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec; + m->default_std_output = arg_default_std_output; + m->default_std_error = arg_default_std_error; + m->default_timeout_start_usec = arg_default_timeout_start_usec; + m->default_timeout_stop_usec = arg_default_timeout_stop_usec; + m->default_restart_usec = arg_default_restart_usec; + m->default_start_limit_interval = arg_default_start_limit_interval; + m->default_start_limit_burst = arg_default_start_limit_burst; + m->default_cpu_accounting = arg_default_cpu_accounting; + m->default_io_accounting = arg_default_io_accounting; + 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); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_LOG_LEVEL = 0x100, + ARG_LOG_TARGET, + ARG_LOG_COLOR, + ARG_LOG_LOCATION, + ARG_UNIT, + ARG_SYSTEM, + ARG_USER, + ARG_TEST, + ARG_NO_PAGER, + 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_MACHINE_ID + }; + + static const struct option options[] = { + { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, + { "log-target", required_argument, NULL, ARG_LOG_TARGET }, + { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, + { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "unit", required_argument, NULL, ARG_UNIT }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "user", no_argument, NULL, ARG_USER }, + { "test", no_argument, NULL, ARG_TEST }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "help", no_argument, NULL, 'h' }, + { "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 }, + {} + }; + + int c, r; + + assert(argc >= 1); + assert(argv); + + if (getpid() == 1) + opterr = 0; + + while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0) + + switch (c) { + + case ARG_LOG_LEVEL: + r = log_set_max_level_from_string(optarg); + if (r < 0) { + log_error("Failed to parse log level %s.", optarg); + return r; + } + + break; + + case ARG_LOG_TARGET: + r = log_set_target_from_string(optarg); + if (r < 0) { + log_error("Failed to parse log target %s.", optarg); + return r; + } + + break; + + case ARG_LOG_COLOR: + + if (optarg) { + r = log_show_color_from_string(optarg); + if (r < 0) { + log_error("Failed to parse log color setting %s.", optarg); + return r; + } + } else + log_show_color(true); + + break; + + case ARG_LOG_LOCATION: + if (optarg) { + r = log_show_location_from_string(optarg); + if (r < 0) { + log_error("Failed to parse log location setting %s.", optarg); + return r; + } + } else + log_show_location(true); + + break; + + case ARG_DEFAULT_STD_OUTPUT: + r = exec_output_from_string(optarg); + if (r < 0) { + log_error("Failed to parse default standard output setting %s.", optarg); + return r; + } else + arg_default_std_output = r; + break; + + case ARG_DEFAULT_STD_ERROR: + r = exec_output_from_string(optarg); + if (r < 0) { + log_error("Failed to parse default standard error output setting %s.", optarg); + return r; + } else + arg_default_std_error = r; + break; + + case ARG_UNIT: + + r = free_and_strdup(&arg_default_unit, optarg); + if (r < 0) + return log_error_errno(r, "Failed to set default unit %s: %m", optarg); + + break; + + case ARG_SYSTEM: + arg_system = true; + break; + + case ARG_USER: + arg_system = false; + break; + + case ARG_TEST: + arg_action = ACTION_TEST; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_VERSION: + arg_action = ACTION_VERSION; + break; + + case ARG_DUMP_CONFIGURATION_ITEMS: + arg_action = ACTION_DUMP_CONFIGURATION_ITEMS; + break; + + case ARG_DUMP_CORE: + 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; + } + 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: + 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; + } + break; + + case ARG_CONFIRM_SPAWN: + r = optarg ? parse_boolean(optarg) : 1; + if (r < 0) { + log_error("Failed to parse confirm spawn boolean %s.", optarg); + return r; + } + arg_confirm_spawn = r; + break; + + case ARG_SHOW_STATUS: + if (optarg) { + r = parse_show_status(optarg, &arg_show_status); + if (r < 0) { + log_error("Failed to parse show status boolean %s.", optarg); + return r; + } + } else + arg_show_status = SHOW_STATUS_YES; + break; + + case ARG_DESERIALIZE: { + int fd; + FILE *f; + + r = safe_atoi(optarg, &fd); + if (r < 0 || fd < 0) { + log_error("Failed to parse deserialize option %s.", optarg); + return -EINVAL; + } + + (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; + } + + case ARG_SWITCHED_ROOT: + 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; + break; + + case 'D': + log_set_max_level(LOG_DEBUG); + break; + + case 'b': + case 's': + case 'z': + /* Just to eat away the sysvinit kernel + * cmdline args without getopt() error + * messages that we'll parse in + * parse_proc_cmdline_word() or ignore. */ + + case '?': + if (getpid() != 1) + return -EINVAL; + else + return 0; + + default: + assert_not_reached("Unhandled option code."); + } + + if (optind < argc && getpid() != 1) { + /* Hmm, when we aren't run as init system + * let's complain about excess arguments */ + + log_error("Excess arguments."); + return -EINVAL; + } + + return 0; +} + +static int help(void) { + + printf("%s [OPTIONS...]\n\n" + "Starts up and maintains the system or user services.\n\n" + " -h --help Show this help\n" + " --test Determine startup sequence, dump it and exit\n" + " --no-pager Do not pipe output into a pager\n" + " --dump-configuration-items Dump understood unit configuration items\n" + " --unit=UNIT Set default unit\n" + " --system Run a system instance, even if PID != 1\n" + " --user Run a user instance\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[=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); + + return 0; +} + +static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) { + _cleanup_fdset_free_ FDSet *fds = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + assert(_f); + assert(_fds); + + r = manager_open_serialization(m, &f); + 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) + return log_oom(); + + r = manager_serialize(m, f, fds, switching_root); + if (r < 0) + return log_error_errno(r, "Failed to serialize state: %m"); + + 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) + return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m"); + + r = fdset_cloexec(fds, false); + if (r < 0) + return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); + + *_f = f; + *_fds = fds; + + f = NULL; + fds = NULL; + + return 0; +} + +static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { + struct rlimit nl; + int r; + + assert(saved_rlimit); + + /* Save the original RLIMIT_NOFILE so that we can reset it + * later when transitioning from the initrd to the main + * systemd or suchlike. */ + if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) + return log_error_errno(errno, "Reading RLIMIT_NOFILE failed: %m"); + + /* Make sure forked processes get the default kernel setting */ + if (!arg_default_rlimit[RLIMIT_NOFILE]) { + struct rlimit *rl; + + rl = newdup(struct rlimit, saved_rlimit, 1); + if (!rl) + return log_oom(); + + arg_default_rlimit[RLIMIT_NOFILE] = rl; + } + + /* Bump up the resource limit for ourselves substantially */ + nl.rlim_cur = nl.rlim_max = 64*1024; + r = setrlimit_closest(RLIMIT_NOFILE, &nl); + if (r < 0) + return log_error_errno(r, "Setting RLIMIT_NOFILE failed: %m"); + + return 0; +} + +static void test_usr(void) { + + /* Check that /usr is not a separate fs */ + + if (dir_is_empty("/usr") <= 0) + return; + + log_warning("/usr appears to be on its own filesystem and is not already mounted. This is not a supported setup. " + "Some things will probably break (sometimes even silently) in mysterious ways. " + "Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information."); +} + +static int initialize_join_controllers(void) { + /* By default, mount "cpu" + "cpuacct" together, and "net_cls" + * + "net_prio". We'd like to add "cpuset" to the mix, but + * "cpuset" doesn't really work for groups with no initialized + * attributes. */ + + arg_join_controllers = new(char**, 3); + if (!arg_join_controllers) + return -ENOMEM; + + arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL); + if (!arg_join_controllers[0]) + goto oom; + + 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) { +#ifdef HAVE_SECCOMP + scmp_filter_ctx *seccomp; + Iterator i; + void *id; + int r; + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return log_oom(); + + SET_FOREACH(id, arg_syscall_archs, i) { + r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); + if (r == -EEXIST) + continue; + if (r < 0) { + log_error_errno(r, "Failed to add architecture to seccomp: %m"); + goto finish; + } + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) { + log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); + goto finish; + } + + r = seccomp_load(seccomp); + if (r < 0) + log_error_errno(r, "Failed to add install architecture seccomp: %m"); + +finish: + seccomp_release(seccomp); + return r; +#else + return 0; +#endif +} + +static int status_welcome(void) { + _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; + int r; + + r = parse_env_file("/etc/os-release", NEWLINE, + "PRETTY_NAME", &pretty_name, + "ANSI_COLOR", &ansi_color, + NULL); + if (r == -ENOENT) + r = parse_env_file("/usr/lib/os-release", NEWLINE, + "PRETTY_NAME", &pretty_name, + "ANSI_COLOR", &ansi_color, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to read os-release file: %m"); + + if (log_get_show_color()) + return status_printf(NULL, false, false, + "\nWelcome to \x1B[%sm%s\x1B[0m!\n", + isempty(ansi_color) ? "1" : ansi_color, + isempty(pretty_name) ? "GNU/Linux" : pretty_name); + else + return status_printf(NULL, false, false, + "\nWelcome to %s!\n", + isempty(pretty_name) ? "GNU/Linux" : pretty_name); +} + +static int write_container_id(void) { + const char *c; + int r; + + c = getenv("container"); + if (isempty(c)) + return 0; + + RUN_WITH_UMASK(0022) + 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; +} + +static int fixup_environment(void) { + _cleanup_free_ char *term = NULL; + int r; + + /* We expect the environment to be set correctly + * if run inside a container. */ + if (detect_container() > 0) + return 0; + + /* When started as PID1, the kernel uses /dev/console + * for our stdios and uses TERM=linux whatever the + * backend device used by the console. We try to make + * a better guess here since some consoles might not + * have support for color mode for example. + * + * However if TERM was configured through the kernel + * command line then leave it alone. */ + + r = get_proc_cmdline_key("TERM=", &term); + if (r < 0) + return r; + + if (r == 0) { + term = strdup(default_term_for_tty("/dev/console") + 5); + if (!term) + return -ENOMEM; + } + + if (setenv("TERM", term, 1) < 0) + return -errno; + + return 0; +} + +int main(int argc, char *argv[]) { + Manager *m = NULL; + int r, retval = EXIT_FAILURE; + usec_t before_startup, after_startup; + char timespan[FORMAT_TIMESPAN_MAX]; + FDSet *fds = NULL; + bool reexecute = false; + const char *shutdown_verb = NULL; + dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL; + dual_timestamp userspace_timestamp = DUAL_TIMESTAMP_NULL; + dual_timestamp kernel_timestamp = DUAL_TIMESTAMP_NULL; + dual_timestamp security_start_timestamp = DUAL_TIMESTAMP_NULL; + dual_timestamp security_finish_timestamp = DUAL_TIMESTAMP_NULL; + static char systemd[] = "systemd"; + bool skip_setup = false; + unsigned j; + bool loaded_policy = false; + bool arm_reboot_watchdog = false; + bool queue_default_job = false; + bool empty_etc = false; + char *switch_root_dir = NULL, *switch_root_init = NULL; + struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0); + const char *error_message = NULL; + +#ifdef HAVE_SYSV_COMPAT + if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { + /* This is compatibility support for SysV, where + * calling init as a user is identical to telinit. */ + + execv(SYSTEMCTL_BINARY_PATH, argv); + log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); + return 1; + } +#endif + + dual_timestamp_from_monotonic(&kernel_timestamp, 0); + dual_timestamp_get(&userspace_timestamp); + + /* Determine if this is a reexecution or normal bootup. We do + * the full command line parsing much later, so let's just + * have a quick peek here. */ + if (strv_find(argv+1, "--deserialize")) + skip_setup = true; + + /* If we have switched root, do all the special setup + * things */ + if (strv_find(argv+1, "--switched-root")) + skip_setup = false; + + /* If we get started via the /sbin/init symlink then we are + called 'init'. After a subsequent reexecution we are then + called 'systemd'. That is confusing, hence let's call us + systemd right-away. */ + program_invocation_short_name = systemd; + prctl(PR_SET_NAME, systemd); + + saved_argv = argv; + saved_argc = argc; + + log_set_upgrade_syslog_to_journal(true); + + /* Disable the umask logic */ + if (getpid() == 1) + umask(0); + + if (getpid() == 1 && detect_container() <= 0) { + + /* Running outside of a container as PID 1 */ + arg_system = true; + log_set_target(LOG_TARGET_KMSG); + log_open(); + + if (in_initrd()) + initrd_timestamp = userspace_timestamp; + + if (!skip_setup) { + r = mount_setup_early(); + if (r < 0) { + error_message = "Failed to early mount API filesystems"; + goto finish; + } + dual_timestamp_get(&security_start_timestamp); + if (mac_selinux_setup(&loaded_policy) < 0) { + error_message = "Failed to load SELinux policy"; + goto finish; + } else if (ima_setup() < 0) { + error_message = "Failed to load IMA policy"; + goto finish; + } else if (mac_smack_setup(&loaded_policy) < 0) { + error_message = "Failed to load SMACK policy"; + goto finish; + } + dual_timestamp_get(&security_finish_timestamp); + } + + if (mac_selinux_init() < 0) { + error_message = "Failed to initialize SELinux policy"; + goto finish; + } + + if (!skip_setup) { + if (clock_is_localtime(NULL) > 0) { + int min; + + /* + * The very first call of settimeofday() also does a time warp in the kernel. + * + * In the rtc-in-local time mode, we set the kernel's timezone, and rely on + * external tools to take care of maintaining the RTC and do all adjustments. + * This matches the behavior of Windows, which leaves the RTC alone if the + * registry tells that the RTC runs in UTC. + */ + r = clock_set_timezone(&min); + if (r < 0) + log_error_errno(r, "Failed to apply local time delta, ignoring: %m"); + else + log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); + } else if (!in_initrd()) { + /* + * Do a dummy very first call to seal the kernel's time warp magic. + * + * Do not call this from inside the initrd. The initrd might not + * carry /etc/adjtime with LOCAL, but the real system could be set up + * that way. In such case, we need to delay the time-warp or the sealing + * until we reach the real system. + * + * Do no set the kernel's timezone. The concept of local time cannot + * be supported reliably, the time will jump or be incorrect at every daylight + * saving time change. All kernel local time concepts will be treated + * as UTC that way. + */ + (void) clock_reset_timewarp(); + } + + r = clock_apply_epoch(); + if (r < 0) + log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); + else if (r > 0) + log_info("System time before build time, advancing clock."); + } + + /* Set the default for later on, but don't actually + * open the logs like this for now. Note that if we + * are transitioning from the initrd there might still + * be journal fd open, and we shouldn't attempt + * opening that before we parsed /proc/cmdline which + * might redirect output elsewhere. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + + } else if (getpid() == 1) { + /* Running inside a container, as PID 1 */ + arg_system = true; + log_set_target(LOG_TARGET_CONSOLE); + log_close_console(); /* force reopen of /dev/console */ + log_open(); + + /* For the later on, see above... */ + log_set_target(LOG_TARGET_JOURNAL); + + /* clear the kernel timestamp, + * because we are in a container */ + kernel_timestamp = DUAL_TIMESTAMP_NULL; + } else { + /* Running as user instance */ + arg_system = false; + log_set_target(LOG_TARGET_AUTO); + log_open(); + + /* clear the kernel timestamp, + * because we are not PID 1 */ + kernel_timestamp = DUAL_TIMESTAMP_NULL; + } + + if (getpid() == 1) { + /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit) + * will process core dumps for system services by default. */ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + + /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored + * until the systemd-coredump tool is enabled via sysctl. */ + if (!skip_setup) + (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + } + + if (arg_system) { + if (fixup_environment() < 0) { + error_message = "Failed to fix up PID1 environment"; + goto finish; + } + + /* Try to figure out if we can use colors with the console. No + * need to do that for user instances since they never log + * into the console. */ + log_show_color(colors_enabled()); + make_null_stdio(); + } + + /* Initialize default unit */ + 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"; + goto finish; + } + + r = initialize_join_controllers(); + if (r < 0) { + error_message = "Failed to initialize cgroup controllers"; + goto finish; + } + + /* Mount /proc, /sys and friends, so that /proc/cmdline and + * /proc/$PID/fd is available. */ + if (getpid() == 1) { + + /* Load the kernel modules early, so that we kdbus.ko is loaded before kdbusfs shall be mounted */ + if (!skip_setup) + kmod_setup(); + + r = mount_setup(loaded_policy); + if (r < 0) { + error_message = "Failed to mount API filesystems"; + goto finish; + } + } + + /* Reset all signal handlers. */ + (void) reset_all_signal_handlers(); + (void) ignore_signals(SIGNALS_IGNORE, -1); + + arg_default_tasks_max = system_tasks_max_scale(15U, 100U); /* 15% the system PIDs equals 4915 by default. */ + + if (parse_config_file() < 0) { + error_message = "Failed to parse config file"; + goto finish; + } + + if (arg_system) { + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } + + /* Note that this also parses bits from the kernel command + * line, including "debug". */ + log_parse_environment(); + + if (parse_argv(argc, argv) < 0) { + error_message = "Failed to parse commandline arguments"; + goto finish; + } + + if (arg_action == ACTION_TEST && + geteuid() == 0) { + log_error("Don't run test mode as root."); + goto finish; + } + + if (!arg_system && + arg_action == ACTION_RUN && + sd_booted() <= 0) { + log_error("Trying to run as user instance, but the system has not been booted with systemd."); + goto finish; + } + + if (arg_system && + arg_action == ACTION_RUN && + running_in_chroot() > 0) { + log_error("Cannot be run in a chroot() environment."); + goto finish; + } + + if (arg_action == ACTION_TEST) + skip_setup = true; + + if (arg_action == ACTION_TEST || arg_action == ACTION_HELP) + pager_open(arg_no_pager, false); + + if (arg_action == ACTION_HELP) { + retval = help(); + goto finish; + } else if (arg_action == ACTION_VERSION) { + retval = version(); + goto finish; + } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { + unit_dump_config_items(stdout); + retval = EXIT_SUCCESS; + goto finish; + } else if (arg_action == ACTION_DONE) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (!arg_system && + !getenv("XDG_RUNTIME_DIR")) { + log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); + goto finish; + } + + assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST); + + /* Close logging fds, in order not to confuse fdset below */ + log_close(); + + /* Remember open file descriptors for later deserialization */ + r = fdset_new_fill(&fds); + if (r < 0) { + log_emergency_errno(r, "Failed to allocate fd set: %m"); + error_message = "Failed to allocate fd set"; + goto finish; + } else + fdset_cloexec(fds, true); + + if (arg_serialization) + assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); + + if (arg_system) + /* Become a session leader if we aren't one yet. */ + setsid(); + + /* Move out of the way, so that we won't block unmounts */ + assert_se(chdir("/") == 0); + + /* Reset the console, but only if this is really init and we + * are freshly booted */ + if (arg_system && arg_action == ACTION_RUN) { + + /* If we are init, we connect stdin/stdout/stderr to + * /dev/null and make sure we don't have a controlling + * tty. */ + release_terminal(); + + if (getpid() == 1 && !skip_setup) + console_setup(); + } + + /* Open the logging devices, if possible and necessary */ + log_open(); + + if (arg_show_status == _SHOW_STATUS_UNSET) + arg_show_status = SHOW_STATUS_YES; + + /* Make sure we leave a core dump without panicing the + * kernel. */ + if (getpid() == 1) { + install_crash_handler(); + + r = mount_cgroup_controllers(arg_join_controllers); + if (r < 0) + goto finish; + } + + if (arg_system) { + int v; + + log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")", + arg_action == ACTION_TEST ? "test " : "" ); + + v = detect_virtualization(); + if (v > 0) + log_info("Detected virtualization %s.", virtualization_to_string(v)); + + write_container_id(); + + log_info("Detected architecture %s.", architecture_to_string(uname_architecture())); + + if (in_initrd()) + log_info("Running in initial RAM disk."); + + /* Let's check whether /etc is already populated. We + * don't actually really check for that, but use + * /etc/machine-id as flag file. This allows container + * managers and installers to provision a couple of + * files already. If the container manager wants to + * provision the machine ID itself it should pass + * $container_uuid to PID 1. */ + + empty_etc = access("/etc/machine-id", F_OK) < 0; + if (empty_etc) + log_info("Running with unpopulated /etc."); + } else { + _cleanup_free_ char *t; + + t = uid_to_name(getuid()); + log_debug(PACKAGE_STRING " running in %suser mode for user "UID_FMT"/%s. (" SYSTEMD_FEATURES ")", + arg_action == ACTION_TEST ? " test" : "", getuid(), t); + } + + if (arg_system && !skip_setup) { + if (arg_show_status > 0) + status_welcome(); + + hostname_setup(); + machine_id_setup(NULL, arg_machine_id, NULL); + loopback_setup(); + bump_unix_max_dgram_qlen(); + + test_usr(); + } + + if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) + watchdog_set_timeout(&arg_runtime_watchdog); + + if (arg_timer_slack_nsec != NSEC_INFINITY) + if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) + log_error_errno(errno, "Failed to adjust timer slack: %m"); + + 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, true); + if (r < 0) { + log_emergency_errno(r, "Failed to drop capability bounding set: %m"); + error_message = "Failed to drop capability bounding set"; + goto finish; + } + } + + if (arg_syscall_archs) { + r = enforce_syscall_archs(arg_syscall_archs); + if (r < 0) { + error_message = "Failed to set syscall architectures"; + goto finish; + } + } + + if (!arg_system) + /* Become reaper of our children */ + if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) + log_warning_errno(errno, "Failed to make us a subreaper: %m"); + + if (arg_system) { + bump_rlimit_nofile(&saved_rlimit_nofile); + + 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_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."); + } + } + + r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m); + if (r < 0) { + log_emergency_errno(r, "Failed to allocate manager object: %m"); + error_message = "Failed to allocate manager object"; + goto finish; + } + + m->confirm_spawn = arg_confirm_spawn; + m->runtime_watchdog = arg_runtime_watchdog; + m->shutdown_watchdog = arg_shutdown_watchdog; + m->userspace_timestamp = userspace_timestamp; + m->kernel_timestamp = kernel_timestamp; + m->initrd_timestamp = initrd_timestamp; + m->security_start_timestamp = security_start_timestamp; + m->security_finish_timestamp = security_finish_timestamp; + + manager_set_defaults(m); + manager_set_show_status(m, arg_show_status); + manager_set_first_boot(m, empty_etc); + + /* Remember whether we should queue the default job */ + queue_default_job = !arg_serialization || arg_switched_root; + + before_startup = now(CLOCK_MONOTONIC); + + r = manager_startup(m, arg_serialization, fds); + if (r < 0) + log_error_errno(r, "Failed to fully start up daemon: %m"); + + /* This will close all file descriptors that were opened, but + * not claimed by any unit. */ + fds = fdset_free(fds); + + arg_serialization = safe_fclose(arg_serialization); + + if (queue_default_job) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *target = NULL; + Job *default_unit_job; + + log_debug("Activating default unit: %s", arg_default_unit); + + r = manager_load_unit(m, arg_default_unit, NULL, &error, &target); + if (r < 0) + log_error("Failed to load default target: %s", bus_error_message(&error, r)); + else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) + log_error_errno(target->load_error, "Failed to load default target: %m"); + else if (target->load_state == UNIT_MASKED) + log_error("Default target masked."); + + if (!target || target->load_state != UNIT_LOADED) { + log_info("Trying to load rescue target..."); + + r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); + if (r < 0) { + log_emergency("Failed to load rescue target: %s", bus_error_message(&error, r)); + error_message = "Failed to load rescue target"; + goto finish; + } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) { + log_emergency_errno(target->load_error, "Failed to load rescue target: %m"); + error_message = "Failed to load rescue target"; + goto finish; + } else if (target->load_state == UNIT_MASKED) { + log_emergency("Rescue target masked."); + error_message = "Rescue target masked"; + goto finish; + } + } + + assert(target->load_state == UNIT_LOADED); + + if (arg_action == ACTION_TEST) { + printf("-> By units:\n"); + manager_dump_units(m, stdout, "\t"); + } + + 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)); + + 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"; + goto finish; + } + } else if (r < 0) { + log_emergency("Failed to isolate default target: %s", bus_error_message(&error, r)); + error_message = "Failed to isolate default target"; + goto finish; + } + + m->default_unit_job_id = default_unit_job->id; + + after_startup = now(CLOCK_MONOTONIC); + log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG, + "Loaded units and determined initial transaction in %s.", + format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC)); + + if (arg_action == ACTION_TEST) { + printf("-> By jobs:\n"); + manager_dump_jobs(m, stdout, "\t"); + retval = EXIT_SUCCESS; + goto finish; + } + } + + for (;;) { + r = manager_loop(m); + if (r < 0) { + log_emergency_errno(r, "Failed to run main loop: %m"); + error_message = "Failed to run main loop"; + goto finish; + } + + switch (m->exit_code) { + + case MANAGER_RELOAD: + log_info("Reloading."); + + r = parse_config_file(); + if (r < 0) + log_error("Failed to parse config file."); + + manager_set_defaults(m); + + r = manager_reload(m); + if (r < 0) + log_error_errno(r, "Failed to reload: %m"); + break; + + case MANAGER_REEXECUTE: + + if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) { + error_message = "Failed to prepare for reexecution"; + goto finish; + } + + reexecute = true; + log_notice("Reexecuting."); + goto finish; + + case MANAGER_SWITCH_ROOT: + /* Steal the switch root parameters */ + switch_root_dir = m->switch_root; + switch_root_init = m->switch_root_init; + m->switch_root = m->switch_root_init = NULL; + + if (!switch_root_init) + if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) { + error_message = "Failed to prepare for reexecution"; + goto finish; + } + + reexecute = true; + log_notice("Switching root."); + goto finish; + + case MANAGER_EXIT: + retval = m->return_value; + + if (MANAGER_IS_USER(m)) { + log_debug("Exit."); + goto finish; + } + + /* fallthrough */ + case MANAGER_REBOOT: + case MANAGER_POWEROFF: + case MANAGER_HALT: + case MANAGER_KEXEC: { + static const char * const table[_MANAGER_EXIT_CODE_MAX] = { + [MANAGER_EXIT] = "exit", + [MANAGER_REBOOT] = "reboot", + [MANAGER_POWEROFF] = "poweroff", + [MANAGER_HALT] = "halt", + [MANAGER_KEXEC] = "kexec" + }; + + assert_se(shutdown_verb = table[m->exit_code]); + arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT; + + log_notice("Shutting down."); + goto finish; + } + + default: + assert_not_reached("Unknown exit code."); + } + } + +finish: + pager_close(); + + if (m) + arg_shutdown_watchdog = m->shutdown_watchdog; + + 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); + 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(); + + if (reexecute) { + const char **args; + unsigned i, args_size; + + /* Close and disarm the watchdog, so that the new + * instance can reinitialize it, but doesn't get + * rebooted while we do that */ + watchdog_close(true); + + /* Reset the RLIMIT_NOFILE to the kernel default, so + * that the new systemd can pass the kernel default to + * its child processes */ + if (saved_rlimit_nofile.rlim_cur > 0) + (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile); + + if (switch_root_dir) { + /* Kill all remaining processes from the + * initrd, but don't wait for them, so that we + * can handle the SIGCHLD for them after + * deserializing. */ + broadcast_signal(SIGTERM, false, true); + + /* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */ + r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE); + if (r < 0) + log_error_errno(r, "Failed to switch root, trying to continue: %m"); + } + + /* Reopen the console */ + (void) make_console_stdio(); + + args_size = MAX(6, argc+1); + args = newa(const char*, args_size); + + if (!switch_root_init) { + char sfd[DECIMAL_STR_MAX(int) + 1]; + + /* First try to spawn ourselves with the right + * path, and with full serialization. We do + * this only if the user didn't specify an + * explicit init to spawn. */ + + assert(arg_serialization); + assert(fds); + + xsprintf(sfd, "%i", fileno(arg_serialization)); + + i = 0; + args[i++] = SYSTEMD_BINARY_PATH; + if (switch_root_dir) + args[i++] = "--switched-root"; + args[i++] = arg_system ? "--system" : "--user"; + args[i++] = "--deserialize"; + args[i++] = sfd; + args[i++] = NULL; + + assert(i <= args_size); + + /* + * 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 + * serialization. We pass the original argv[] and + * envp[]. (Well, modulo the ordering changes due to + * getopt() in argv[], and some cleanups in envp[], + * but let's hope that doesn't matter.) */ + + arg_serialization = safe_fclose(arg_serialization); + fds = fdset_free(fds); + + for (j = 1, i = 1; j < (unsigned) argc; j++) + args[i++] = argv[j]; + args[i++] = NULL; + assert(i <= args_size); + + /* Reenable any blocked signals, especially important + * if we switch from initial ramdisk to init=... */ + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + if (switch_root_init) { + args[0] = switch_root_init; + (void) execv(args[0], (char* const*) args); + log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m"); + } + + args[0] = "/sbin/init"; + (void) execv(args[0], (char* const*) args); + + if (errno == ENOENT) { + log_warning("No /sbin/init, trying fallback"); + + args[0] = "/bin/sh"; + args[1] = NULL; + (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); + fds = fdset_free(fds); + +#ifdef HAVE_VALGRIND_VALGRIND_H + /* If we are PID 1 and running under valgrind, then let's exit + * here explicitly. valgrind will only generate nice output on + * exit(), not on exec(), hence let's do the former not the + * latter here. */ + if (getpid() == 1 && RUNNING_ON_VALGRIND) + return 0; +#endif + + if (shutdown_verb) { + char log_level[DECIMAL_STR_MAX(int) + 1]; + char exit_code[DECIMAL_STR_MAX(uint8_t) + 1]; + const char* command_line[11] = { + SYSTEMD_SHUTDOWN_BINARY_PATH, + shutdown_verb, + "--log-level", log_level, + "--log-target", + }; + unsigned pos = 5; + _cleanup_strv_free_ char **env_block = NULL; + + assert(command_line[pos] == NULL); + env_block = strv_copy(environ); + + 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: + command_line[pos++] = "kmsg"; + break; + + case LOG_TARGET_NULL: + command_line[pos++] = "null"; + break; + + case LOG_TARGET_CONSOLE: + default: + command_line[pos++] = "console"; + break; + }; + + if (log_get_show_color()) + command_line[pos++] = "--log-color"; + + if (log_get_show_location()) + command_line[pos++] = "--log-location"; + + if (streq(shutdown_verb, "exit")) { + command_line[pos++] = "--exit-code"; + command_line[pos++] = exit_code; + xsprintf(exit_code, "%d", retval); + } + + assert(pos < ELEMENTSOF(command_line)); + + if (arm_reboot_watchdog && arg_shutdown_watchdog > 0 && arg_shutdown_watchdog != USEC_INFINITY) { + char *e; + + /* If we reboot let's set the shutdown + * watchdog and tell the shutdown binary to + * repeatedly ping it */ + r = watchdog_set_timeout(&arg_shutdown_watchdog); + watchdog_close(r < 0); + + /* Tell the binary how often to ping, ignore failure */ + if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0) + (void) strv_push(&env_block, e); + } else + watchdog_close(true); + + /* Avoid the creation of new processes forked by the + * kernel; at this point, we will not listen to the + * signals anyway */ + if (detect_container() <= 0) + (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); + + execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); + log_error_errno(errno, "Failed to execute shutdown binary, %s: %m", + getpid() == 1 ? "freezing" : "quitting"); + } + + if (getpid() == 1) { + if (error_message) + manager_status_printf(NULL, STATUS_TYPE_EMERGENCY, + ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL, + "%s, freezing.", error_message); + freeze_or_reboot(); + } + + return retval; +} diff --git a/src/grp-system/systemd/org.freedesktop.systemd1.conf b/src/grp-system/systemd/org.freedesktop.systemd1.conf new file mode 100644 index 0000000000..3c64f20872 --- /dev/null +++ b/src/grp-system/systemd/org.freedesktop.systemd1.conf @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-system/systemd/org.freedesktop.systemd1.policy.in.in b/src/grp-system/systemd/org.freedesktop.systemd1.policy.in.in new file mode 100644 index 0000000000..cc39a9e1c3 --- /dev/null +++ b/src/grp-system/systemd/org.freedesktop.systemd1.policy.in.in @@ -0,0 +1,70 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Send passphrase back to system + <_message>Authentication is required to send the entered passphrase back to the system. + + no + no + auth_admin_keep + + @rootlibexecdir@/systemd-reply-password + + + + <_description>Manage system services or other units + <_message>Authentication is required to manage system services or other units. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Manage system service or unit files + <_message>Authentication is required to manage system service or unit files. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Set or unset system and service manager environment variables + <_message>Authentication is required to set or unset system and service manager environment variables. + + auth_admin + auth_admin + auth_admin_keep + + + + + <_description>Reload the systemd state + <_message>Authentication is required to reload the systemd state. + + auth_admin + auth_admin + auth_admin_keep + + + + diff --git a/src/grp-system/systemd/org.freedesktop.systemd1.service b/src/grp-system/systemd/org.freedesktop.systemd1.service new file mode 100644 index 0000000000..d4df3e93a2 --- /dev/null +++ b/src/grp-system/systemd/org.freedesktop.systemd1.service @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.systemd1 +Exec=/bin/false +User=root diff --git a/src/grp-system/systemd/system.conf b/src/grp-system/systemd/system.conf new file mode 100644 index 0000000000..c6bb050aac --- /dev/null +++ b/src/grp-system/systemd/system.conf @@ -0,0 +1,61 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See systemd-system.conf(5) for details. + +[Manager] +#LogLevel=info +#LogTarget=journal-or-kmsg +#LogColor=yes +#LogLocation=no +#DumpCore=yes +#ShowStatus=yes +#CrashChangeVT=no +#CrashShell=no +#CrashReboot=no +#CPUAffinity=1 2 +#JoinControllers=cpu,cpuacct net_cls,net_prio +#RuntimeWatchdogSec=0 +#ShutdownWatchdogSec=10min +#CapabilityBoundingSet= +#SystemCallArchitectures= +#TimerSlackNSec= +#DefaultTimerAccuracySec=1min +#DefaultStandardOutput=journal +#DefaultStandardError=inherit +#DefaultTimeoutStartSec=90s +#DefaultTimeoutStopSec=90s +#DefaultRestartSec=100ms +#DefaultStartLimitIntervalSec=10s +#DefaultStartLimitBurst=5 +#DefaultEnvironment= +#DefaultCPUAccounting=no +#DefaultIOAccounting=no +#DefaultBlockIOAccounting=no +#DefaultMemoryAccounting=no +#DefaultTasksAccounting=yes +#DefaultTasksMax=15% +#DefaultLimitCPU= +#DefaultLimitFSIZE= +#DefaultLimitDATA= +#DefaultLimitSTACK= +#DefaultLimitCORE= +#DefaultLimitRSS= +#DefaultLimitNOFILE= +#DefaultLimitAS= +#DefaultLimitNPROC= +#DefaultLimitMEMLOCK= +#DefaultLimitLOCKS= +#DefaultLimitSIGPENDING= +#DefaultLimitMSGQUEUE= +#DefaultLimitNICE= +#DefaultLimitRTPRIO= +#DefaultLimitRTTIME= diff --git a/src/grp-system/systemd/systemd-system.conf.xml b/src/grp-system/systemd/systemd-system.conf.xml new file mode 100644 index 0000000000..1bb40fd234 --- /dev/null +++ b/src/grp-system/systemd/systemd-system.conf.xml @@ -0,0 +1,393 @@ + + + + + + + + systemd-system.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-system.conf + 5 + + + + systemd-system.conf + system.conf.d + systemd-user.conf + user.conf.d + System and session service manager configuration files + + + + /etc/systemd/system.conf, + /etc/systemd/system.conf.d/*.conf, + /run/systemd/system.conf.d/*.conf, + /usr/lib/systemd/system.conf.d/*.conf + /etc/systemd/user.conf, + /etc/systemd/user.conf.d/*.conf, + /run/systemd/user.conf.d/*.conf, + /usr/lib/systemd/user.conf.d/*.conf + + + + Description + + When run as a system instance, systemd interprets the + configuration file system.conf and the files + in system.conf.d directories; when run as a + user instance, systemd interprets the configuration file + user.conf and the files in + user.conf.d directories. These configuration + files contain a few settings controlling basic manager + operations. + + + + + + Options + + All options are configured in the + [Manager] section: + + + + + LogLevel= + LogTarget= + LogColor= + LogLocation= + DumpCore=yes + CrashChangeVT=no + CrashShell=no + CrashReboot=no + ShowStatus=yes + DefaultStandardOutput=journal + DefaultStandardError=inherit + + Configures various parameters of basic manager + operation. These options may be overridden by the respective + command line arguments. See + systemd1 + for details about these command line + arguments. + + + + CPUAffinity= + + Configures the initial CPU affinity for the + init process. Takes a list of CPU indices or ranges separated + by either whitespace or commas. CPU ranges are specified by + the lower and upper CPU indices separated by a + dash. + + + + JoinControllers=cpu,cpuacct net_cls,netprio + + Configures controllers that shall be mounted + in a single hierarchy. By default, systemd will mount all + controllers which are enabled in the kernel in individual + hierarchies, with the exception of those listed in this + setting. Takes a space-separated list of comma-separated + controller names, in order to allow multiple joined + hierarchies. Defaults to 'cpu,cpuacct'. Pass an empty string + to ensure that systemd mounts all controllers in separate + hierarchies. + + Note that this option is only applied once, at very + early boot. If you use an initial RAM disk (initrd) that uses + systemd, it might hence be necessary to rebuild the initrd if + this option is changed, and make sure the new configuration + file is included in it. Otherwise, the initrd might mount the + controller hierarchies in a different configuration than + intended, and the main system cannot remount them + anymore. + + + + RuntimeWatchdogSec= + ShutdownWatchdogSec= + + Configure the hardware watchdog at runtime and + at reboot. Takes a timeout value in seconds (or in other time + units if suffixed with ms, + min, h, + d, w). If + RuntimeWatchdogSec= is set to a non-zero + value, the watchdog hardware + (/dev/watchdog) will be programmed to + automatically reboot the system if it is not contacted within + the specified timeout interval. The system manager will ensure + to contact it at least once in half the specified timeout + interval. This feature requires a hardware watchdog device to + be present, as it is commonly the case in embedded and server + systems. Not all hardware watchdogs allow configuration of the + reboot timeout, in which case the closest available timeout is + picked. ShutdownWatchdogSec= may be used to + configure the hardware watchdog when the system is asked to + reboot. It works as a safety net to ensure that the reboot + takes place even if a clean reboot attempt times out. By + default RuntimeWatchdogSec= defaults to 0 + (off), and ShutdownWatchdogSec= to 10min. + These settings have no effect if a hardware watchdog is not + available. + + + + CapabilityBoundingSet= + + Controls which capabilities to include in the + capability bounding set for PID 1 and its children. See + capabilities7 + for details. Takes a whitespace-separated list of capability + names as read by + cap_from_name3. + Capabilities listed will be included in the bounding set, all + others are removed. If the list of capabilities is prefixed + with ~, all but the listed capabilities will be included, the + effect of the assignment inverted. Note that this option also + affects the respective capabilities in the effective, + permitted and inheritable capability sets. The capability + bounding set may also be individually configured for units + using the CapabilityBoundingSet= directive + for units, but note that capabilities dropped for PID 1 cannot + be regained in individual units, they are lost for + good. + + + + SystemCallArchitectures= + + Takes a space-separated list of architecture + identifiers. Selects from which architectures system calls may + be invoked on this system. This may be used as an effective + way to disable invocation of non-native binaries system-wide, + for example to prohibit execution of 32-bit x86 binaries on + 64-bit x86-64 systems. This option operates system-wide, and + acts similar to the + SystemCallArchitectures= setting of unit + files, see + systemd.exec5 + for details. This setting defaults to the empty list, in which + case no filtering of system calls based on architecture is + applied. Known architecture identifiers are + x86, x86-64, + x32, arm and the special + identifier native. The latter implicitly + maps to the native architecture of the system (or more + specifically, the architecture the system manager was compiled + for). Set this setting to native to + prohibit execution of any non-native binaries. When a binary + executes a system call of an architecture that is not listed + in this setting, it will be immediately terminated with the + SIGSYS signal. + + + + TimerSlackNSec= + + Sets the timer slack in nanoseconds for PID 1, + which is inherited by all executed processes, unless + overridden individually, for example with the + TimerSlackNSec= setting in service units + (for details see + systemd.exec5). + The timer slack controls the accuracy of wake-ups triggered by + system timers. See + prctl2 + for more information. Note that in contrast to most other time + span definitions this parameter takes an integer value in + nano-seconds if no unit is specified. The usual time units are + understood too. + + + + DefaultTimerAccuracySec= + + Sets the default accuracy of timer units. This + controls the global default for the + AccuracySec= setting of timer units, see + systemd.timer5 + for details. AccuracySec= set in individual + units override the global default for the specific unit. + Defaults to 1min. Note that the accuracy of timer units is + also affected by the configured timer slack for PID 1, see + TimerSlackNSec= above. + + + + DefaultTimeoutStartSec= + DefaultTimeoutStopSec= + DefaultRestartSec= + + Configures the default timeouts for starting + and stopping of units, as well as the default time to sleep + between automatic restarts of units, as configured per-unit in + TimeoutStartSec=, + TimeoutStopSec= and + RestartSec= (for services, see + systemd.service5 + for details on the per-unit settings). For non-service units, + DefaultTimeoutStartSec= sets the default + TimeoutSec= + value. DefaultTimeoutStartSec= and + DefaultTimeoutStopSec= default to + 90s. DefaultRestartSec= defaults to + 100ms. + + + + DefaultStartLimitIntervalSec= + DefaultStartLimitBurst= + + Configure the default unit start rate + limiting, as configured per-service by + StartLimitIntervalSec= and + StartLimitBurst=. See + systemd.service5 + for details on the per-service settings. + DefaultStartLimitIntervalSec= defaults to + 10s. DefaultStartLimitBurst= defaults to + 5. + + + + DefaultEnvironment= + + Sets manager environment variables passed to + all executed processes. Takes a space-separated list of + variable assignments. See + environ7 + for details about environment variables. + + Example: + + DefaultEnvironment="VAR1=word1 word2" VAR2=word3 "VAR3=word 5 6" + + Sets three variables + VAR1, + VAR2, + VAR3. + + + + DefaultCPUAccounting= + DefaultBlockIOAccounting= + DefaultMemoryAccounting= + DefaultTasksAccounting= + + Configure the default resource accounting + settings, as configured per-unit by + CPUAccounting=, + BlockIOAccounting=, + MemoryAccounting= and + TasksAccounting=. See + systemd.resource-control5 + for details on the per-unit + settings. DefaulTasksAccounting= defaults + to on, the other three settings to off. + + + + DefaultTasksMax= + + Configure the default value for the per-unit TasksMax= setting. See + systemd.resource-control5 + for details. This setting applies to all unit types that support resource control settings, with the exception + of slice units. Defaults to 15%, which equals 4915 with the kernel's defaults on the host, but might be smaller + in OS containers. + + + + DefaultLimitCPU= + DefaultLimitFSIZE= + DefaultLimitDATA= + DefaultLimitSTACK= + DefaultLimitCORE= + DefaultLimitRSS= + DefaultLimitNOFILE= + DefaultLimitAS= + DefaultLimitNPROC= + DefaultLimitMEMLOCK= + DefaultLimitLOCKS= + DefaultLimitSIGPENDING= + DefaultLimitMSGQUEUE= + DefaultLimitNICE= + DefaultLimitRTPRIO= + DefaultLimitRTTIME= + + These settings control various default + resource limits for units. See + setrlimit2 + for details. The resource limit is possible to specify in two formats, + to set soft and hard limits to the same value, + or to set both limits individually (e.g. DefaultLimitAS=4G:16G). + Use the string infinity to + configure no limit on a specific resource. The multiplicative + suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E + may be used for resource limits measured in bytes + (e.g. DefaultLimitAS=16G). For the limits referring to time values, + the usual time units ms, s, min, h and so on may be used (see + systemd.time7 + for details). Note that if no time unit is specified for + DefaultLimitCPU= the default unit of seconds is + implied, while for DefaultLimitRTTIME= the default + unit of microseconds is implied. Also, note that the effective + granularity of the limits might influence their + enforcement. For example, time limits specified for + DefaultLimitCPU= will be rounded up implicitly to + multiples of 1s. These settings may be overridden in individual units + using the corresponding LimitXXX= directives. Note that these resource + limits are only defaults for units, they are not applied to PID 1 + itself. + + + + + + See Also + + systemd1, + systemd.directives7, + systemd.exec5, + systemd.service5, + environ7, + capabilities7 + + + + diff --git a/src/grp-system/systemd/systemd-tmpfs.tmpfiles b/src/grp-system/systemd/systemd-tmpfs.tmpfiles new file mode 100644 index 0000000000..98050d329d --- /dev/null +++ b/src/grp-system/systemd/systemd-tmpfs.tmpfiles @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Exclude namespace mountpoints created with PrivateTmp=yes +x /tmp/systemd-private-%b-* +X /tmp/systemd-private-%b-*/tmp +x /var/tmp/systemd-private-%b-* +X /var/tmp/systemd-private-%b-*/tmp diff --git a/src/grp-system/systemd/systemd.automount.xml b/src/grp-system/systemd/systemd.automount.xml new file mode 100644 index 0000000000..a43dc981bd --- /dev/null +++ b/src/grp-system/systemd/systemd.automount.xml @@ -0,0 +1,173 @@ + + + + + + + + systemd.automount + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.automount + 5 + + + + systemd.automount + Automount unit configuration + + + + automount.automount + + + + Description + + A unit configuration file whose name ends in + .automount encodes information about a file + system automount point controlled and supervised by + systemd. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The automount specific configuration options + are configured in the [Automount] section. + + Automount units must be named after the automount directories they control. Example: the automount point + /home/lennart must be configured in a unit file + home-lennart.automount. For details about the escaping logic used to convert a file system + path to a unit name see + systemd.unit5. Note that + automount units cannot be templated, nor is it possible to add multiple names to an automount unit by creating + additional symlinks to its unit file. + + For each automount unit file a matching mount unit file (see + systemd.mount5 + for details) must exist which is activated when the automount path + is accessed. Example: if an automount unit + home-lennart.automount is active and the user + accesses /home/lennart the mount unit + home-lennart.mount will be activated. + + Automount units may be used to implement on-demand mounting + as well as parallelized mounting of file systems. + + + + Automatic Dependencies + + If an automount unit is beneath another mount unit in the + file system hierarchy, both a requirement and an ordering + dependency between both units are created automatically. + + An implicit Before= dependency is created + between an automount unit and the mount unit it activates. + + Automount units acquire automatic Before= and Conflicts= on + umount.target in order to be stopped during shutdown, unless + DefaultDependencies=no is set in the [Unit] section. + + + + + <filename>fstab</filename> + + Automount units may either be configured via unit files, or + via /etc/fstab (see + fstab5 + for details). + + For details how systemd parses + /etc/fstab see + systemd.mount5. + + If an automount point is configured in both + /etc/fstab and a unit file, the configuration + in the latter takes precedence. + + + + Options + + Automount files must include an [Automount] section, which + carries information about the file system automount points it + supervises. The options specific to the [Automount] section of + automount units are the following: + + + + + Where= + Takes an absolute path of a directory of the + automount point. If the automount point does not exist at time + that the automount point is installed, it is created. This + string must be reflected in the unit filename. (See above.) + This option is mandatory. + + + + DirectoryMode= + Directories of automount points (and any + parent directories) are automatically created if needed. This + option specifies the file system access mode used when + creating these directories. Takes an access mode in octal + notation. Defaults to 0755. + + + TimeoutIdleSec= + Configures an idle timeout. Once the mount has been + idle for the specified time, systemd will attempt to unmount. Takes a + unit-less value in seconds, or a time span value such as "5min 20s". + Pass 0 to disable the timeout logic. The timeout is disabled by + default. + + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.mount5, + mount8, + automount8, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.device.xml b/src/grp-system/systemd/systemd.device.xml new file mode 100644 index 0000000000..effed098dd --- /dev/null +++ b/src/grp-system/systemd/systemd.device.xml @@ -0,0 +1,182 @@ + + + + + + + + systemd.device + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.device + 5 + + + + systemd.device + Device unit configuration + + + + device.device + + + + Description + + A unit configuration file whose name ends in + .device encodes information about a device unit + as exposed in the + sysfs/udev7 + device tree. + + This unit type has no specific options. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic + [Unit] and [Install] + sections. A separate [Device] section does not + exist, since no device-specific options may be configured. + + systemd will dynamically create device units for all kernel + devices that are marked with the "systemd" udev tag (by default + all block and network devices, and a few others). This may be used + to define dependencies between devices and other units. To tag a + udev device, use TAG+="systemd" in the udev + rules file, see + udev7 + for details. + + Device units are named after the /sys + and /dev paths they control. Example: the + device /dev/sda5 is exposed in + systemd as dev-sda5.device. For details about + the escaping logic used to convert a file system path to a unit + name see + systemd.unit5. + + + + Automatic Dependencies + + Many unit types automatically acquire dependencies on device + units of devices they require. For example, + .socket unit acquire dependencies on the + device units of the network interface specified in + BindToDevice=. Similar, swap and mount units + acquire dependencies on the units encapsulating their backing + block devices. + + + + The udev Database + + The settings of device units may either be configured via + unit files, or directly from the udev database (which is + recommended). The following udev device properties are understood + by systemd: + + + + SYSTEMD_WANTS= + SYSTEMD_USER_WANTS= + Adds dependencies of type + Wants from the device unit to all listed + units. The first form is used by the system systemd instance, + the second by user systemd instances. Those settings may be + used to activate arbitrary units when a specific device + becomes available. + + Note that this and the other tags are not taken into + account unless the device is tagged with the + systemd string in the udev database, + because otherwise the device is not exposed as a systemd unit + (see above). + + Note that systemd will only act on + Wants dependencies when a device first + becomes active. It will not act on them if they are added to + devices that are already active. Use + SYSTEMD_READY= (see below) to influence on + which udev event to trigger the dependencies. + + + + + SYSTEMD_ALIAS= + Adds an additional alias name to the device + unit. This must be an absolute path that is automatically + transformed into a unit name. (See above.) + + + + SYSTEMD_READY= + If set to 0, systemd will consider this device + unplugged even if it shows up in the udev tree. If this + property is unset or set to 1, the device will be considered + plugged if it is visible in the udev tree. This property has + no influence on the behavior when a device disappears from the + udev tree. + + This option is useful to support devices that initially + show up in an uninitialized state in the tree, and for which a + changed event is generated the moment they + are fully set up. Note that SYSTEMD_WANTS= + (see above) is not acted on as long as + SYSTEMD_READY=0 is set for a + device. + + + + ID_MODEL_FROM_DATABASE= + ID_MODEL= + + If set, this property is used as description + string for the device unit. + + + + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + udev7, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.exec.xml b/src/grp-system/systemd/systemd.exec.xml new file mode 100644 index 0000000000..41ae6e76de --- /dev/null +++ b/src/grp-system/systemd/systemd.exec.xml @@ -0,0 +1,1612 @@ + + + + + + + + systemd.exec + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.exec + 5 + + + + systemd.exec + Execution environment configuration + + + + service.service, + socket.socket, + mount.mount, + swap.swap + + + + Description + + Unit configuration files for services, sockets, mount + points, and swap devices share a subset of configuration options + which define the execution environment of spawned + processes. + + This man page lists the configuration options shared by + these four unit types. See + systemd.unit5 + for the common options of all unit configuration files, and + systemd.service5, + systemd.socket5, + systemd.swap5, + and + systemd.mount5 + for more information on the specific unit configuration files. The + execution specific configuration options are configured in the + [Service], [Socket], [Mount], or [Swap] sections, depending on the + unit type. + + + + Automatic Dependencies + + A few execution parameters result in additional, automatic + dependencies to be added. + + Units with WorkingDirectory= or + RootDirectory= set automatically gain + dependencies of type Requires= and + After= on all mount units required to access + the specified paths. This is equivalent to having them listed + explicitly in RequiresMountsFor=. + + Similar, units with PrivateTmp= enabled + automatically get mount unit dependencies for all mounts + required to access /tmp and + /var/tmp. + + Units whose standard output or error output is connected to , + or (or their combinations with console output, see below) automatically acquire dependencies + of type After= on systemd-journald.socket. + + + + Options + + + + + WorkingDirectory= + + Takes a directory path relative to the service's root + directory specified by RootDirectory=, or the + special value ~. Sets the working directory + for executed processes. If set to ~, the + home directory of the user specified in + User= is used. If not set, defaults to the + root directory when systemd is running as a system instance + and the respective user's home directory if run as user. If + the setting is prefixed with the - + character, a missing working directory is not considered + fatal. If RootDirectory= is not set, then + WorkingDirectory= is relative to the root of + the system running the service manager. + Note that setting this parameter might result in + additional dependencies to be added to the unit (see + above). + + + + RootDirectory= + + Takes a directory path relative to the host's root directory + (i.e. the root of the system running the service manager). Sets the + root directory for executed processes, with the chroot2 + system call. If this is used, it must be ensured that the + process binary and all its auxiliary files are available in + the chroot() jail. Note that setting this + parameter might result in additional dependencies to be added + to the unit (see above). + + + + User= + Group= + + Sets the Unix user or group that the processes + are executed as, respectively. Takes a single user or group + name or ID as argument. If no group is set, the default group + of the user is chosen. These do not affect commands prefixed with +. + + + + SupplementaryGroups= + + Sets the supplementary Unix groups the + processes are executed as. This takes a space-separated list + of group names or IDs. This option may be specified more than + once, in which case all listed groups are set as supplementary + groups. When the empty string is assigned, the list of + supplementary groups is reset, and all assignments prior to + this one will have no effect. In any way, this option does not + override, but extends the list of supplementary groups + configured in the system group database for the + user. This does not affect commands prefixed with +. + + + + Nice= + + Sets the default nice level (scheduling + priority) for executed processes. Takes an integer between -20 + (highest priority) and 19 (lowest priority). See + setpriority2 + for details. + + + + OOMScoreAdjust= + + Sets the adjustment level for the + Out-Of-Memory killer for executed processes. Takes an integer + between -1000 (to disable OOM killing for this process) and + 1000 (to make killing of this process under memory pressure + very likely). See proc.txt + for details. + + + + IOSchedulingClass= + + Sets the I/O scheduling class for executed + processes. Takes an integer between 0 and 3 or one of the + strings , , + or . See + ioprio_set2 + for details. + + + + IOSchedulingPriority= + + Sets the I/O scheduling priority for executed + processes. Takes an integer between 0 (highest priority) and 7 + (lowest priority). The available priorities depend on the + selected I/O scheduling class (see above). See + ioprio_set2 + for details. + + + + CPUSchedulingPolicy= + + Sets the CPU scheduling policy for executed + processes. Takes one of + , + , + , + or + . See + sched_setscheduler2 + for details. + + + + CPUSchedulingPriority= + + Sets the CPU scheduling priority for executed + processes. The available priority range depends on the + selected CPU scheduling policy (see above). For real-time + scheduling policies an integer between 1 (lowest priority) and + 99 (highest priority) can be used. See + sched_setscheduler2 + for details. + + + + CPUSchedulingResetOnFork= + + Takes a boolean argument. If true, elevated + CPU scheduling priorities and policies will be reset when the + executed processes fork, and can hence not leak into child + processes. See + sched_setscheduler2 + for details. Defaults to false. + + + + CPUAffinity= + + Controls the CPU affinity of the executed + processes. Takes a list of CPU indices or ranges separated by + either whitespace or commas. CPU ranges are specified by the + lower and upper CPU indices separated by a dash. + This option may be specified more than once, in which case the + specified CPU affinity masks are merged. If the empty string + is assigned, the mask is reset, all assignments prior to this + will have no effect. See + sched_setaffinity2 + for details. + + + + UMask= + + Controls the file mode creation mask. Takes an + access mode in octal notation. See + umask2 + for details. Defaults to 0022. + + + + Environment= + + Sets environment variables for executed + processes. Takes a space-separated list of variable + assignments. This option may be specified more than once, in + which case all listed variables will be set. If the same + variable is set twice, the later setting will override the + earlier setting. If the empty string is assigned to this + option, the list of environment variables is reset, all prior + assignments have no effect. Variable expansion is not + performed inside the strings, however, specifier expansion is + possible. The $ character has no special meaning. If you need + to assign a value containing spaces to a variable, use double + quotes (") for the assignment. + + Example: + Environment="VAR1=word1 word2" VAR2=word3 "VAR3=$word 5 6" + gives three variables VAR1, + VAR2, VAR3 + with the values word1 word2, + word3, $word 5 6. + + + + See + environ7 + for details about environment variables. + + + EnvironmentFile= + Similar to Environment= but + reads the environment variables from a text file. The text + file should contain new-line-separated variable assignments. + Empty lines, lines without an = separator, + or lines starting with ; or # will be ignored, + which may be used for commenting. A line ending with a + backslash will be concatenated with the following one, + allowing multiline variable definitions. The parser strips + leading and trailing whitespace from the values of + assignments, unless you use double quotes ("). + + The argument passed should be an absolute filename or + wildcard expression, optionally prefixed with + -, which indicates that if the file does + not exist, it will not be read and no error or warning message + is logged. This option may be specified more than once in + which case all specified files are read. If the empty string + is assigned to this option, the list of file to read is reset, + all prior assignments have no effect. + + The files listed with this directive will be read + shortly before the process is executed (more specifically, + after all processes from a previous unit state terminated. + This means you can generate these files in one unit state, and + read it with this option in the next). + + Settings from these + files override settings made with + Environment=. If the same variable is set + twice from these files, the files will be read in the order + they are specified and the later setting will override the + earlier setting. + + + + PassEnvironment= + + Pass environment variables from the systemd system + manager to executed processes. Takes a space-separated list of variable + names. This option may be specified more than once, in which case all + listed variables will be set. If the empty string is assigned to this + option, the list of environment variables is reset, all prior + assignments have no effect. Variables that are not set in the system + manager will not be passed and will be silently ignored. + + Variables passed from this setting are overridden by those passed + from Environment= or + EnvironmentFile=. + + Example: + PassEnvironment=VAR1 VAR2 VAR3 + passes three variables VAR1, + VAR2, VAR3 + with the values set for those variables in PID1. + + + See + environ7 + for details about environment variables. + + + + StandardInput= + Controls where file descriptor 0 (STDIN) of + the executed processes is connected to. Takes one of + , + , + , + or + . + + If is selected, standard input + will be connected to /dev/null, i.e. all + read attempts by the process will result in immediate + EOF. + + If is selected, standard input is + connected to a TTY (as configured by + TTYPath=, see below) and the executed + process becomes the controlling process of the terminal. If + the terminal is already being controlled by another process, + the executed process waits until the current controlling + process releases the terminal. + + is similar to + , but the executed process is forcefully + and immediately made the controlling process of the terminal, + potentially removing previous controlling processes from the + terminal. + + is similar to + but if the terminal already has a + controlling process start-up of the executed process + fails. + + The option is only valid in + socket-activated services, and only when the socket + configuration file (see + systemd.socket5 + for details) specifies a single socket only. If this option is + set, standard input will be connected to the socket the + service was activated from, which is primarily useful for + compatibility with daemons designed for use with the + traditional + inetd8 + daemon. + + This setting defaults to + . + + + + StandardOutput= + Controls where file descriptor 1 (STDOUT) of + the executed processes is connected to. Takes one of + , + , + , + , + , + , + , + , + or + . + + duplicates the file descriptor + of standard input for standard output. + + connects standard output to + /dev/null, i.e. everything written to it + will be lost. + + connects standard output to a tty + (as configured via TTYPath=, see below). If + the TTY is used for output only, the executed process will not + become the controlling process of the terminal, and will not + fail or wait for other processes to release the + terminal. + + connects standard output with + the journal which is accessible via + journalctl1. + Note that everything that is written to syslog or kmsg (see + below) is implicitly stored in the journal as well, the + specific two options listed below are hence supersets of this + one. + + connects standard output to the + syslog3 + system syslog service, in addition to the journal. Note that + the journal daemon is usually configured to forward everything + it receives to syslog anyway, in which case this option is no + different from . + + connects standard output with the + kernel log buffer which is accessible via + dmesg1, + in addition to the journal. The journal daemon might be + configured to send all logs to kmsg anyway, in which case this + option is no different from . + + , + and + work in a similar way as the + three options above but copy the output to the system console + as well. + + connects standard output to a + socket acquired via socket activation. The semantics are + similar to the same option of + StandardInput=. + + If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the + kernel log buffer, the unit will implicitly gain a dependency of type After= on + systemd-journald.socket (also see the automatic dependencies section above). + + This setting defaults to the value set with + in + systemd-system.conf5, + which defaults to . Note that setting + this parameter might result in additional dependencies to be + added to the unit (see above). + + + + StandardError= + Controls where file descriptor 2 (STDERR) of + the executed processes is connected to. The available options + are identical to those of StandardOutput=, + with one exception: if set to the + file descriptor used for standard output is duplicated for + standard error. This setting defaults to the value set with + in + systemd-system.conf5, + which defaults to . Note that setting + this parameter might result in additional dependencies to be + added to the unit (see above). + + + + TTYPath= + Sets the terminal device node to use if + standard input, output, or error are connected to a TTY (see + above). Defaults to + /dev/console. + + + TTYReset= + Reset the terminal device specified with + TTYPath= before and after execution. + Defaults to no. + + + TTYVHangup= + Disconnect all clients which have opened the + terminal device specified with TTYPath= + before and after execution. Defaults to + no. + + + TTYVTDisallocate= + If the terminal device specified with + TTYPath= is a virtual console terminal, try + to deallocate the TTY before and after execution. This ensures + that the screen and scrollback buffer is cleared. Defaults to + no. + + + SyslogIdentifier= + Sets the process name to prefix log lines sent + to the logging system or the kernel log buffer with. If not + set, defaults to the process name of the executed process. + This option is only useful when + StandardOutput= or + StandardError= are set to + , or + (or to the same settings in combination + with ). + + + SyslogFacility= + Sets the syslog facility to use when logging + to syslog. One of , + , , + , , + , , + , , + , , + , , + , , + , , + , or + . See + syslog3 + for details. This option is only useful when + StandardOutput= or + StandardError= are set to + . Defaults to + . + + + SyslogLevel= + The default syslog level to use when logging to + syslog or the kernel log buffer. One of + , + , + , + , + , + , + , + . See + syslog3 + for details. This option is only useful when + StandardOutput= or + StandardError= are set to + or . Note that + individual lines output by the daemon might be prefixed with a + different log level which can be used to override the default + log level specified here. The interpretation of these prefixes + may be disabled with SyslogLevelPrefix=, + see below. For details, see + sd-daemon3. + + Defaults to + . + + + + SyslogLevelPrefix= + Takes a boolean argument. If true and + StandardOutput= or + StandardError= are set to + , or + , log lines written by the executed + process that are prefixed with a log level will be passed on + to syslog with this log level set but the prefix removed. If + set to false, the interpretation of these prefixes is disabled + and the logged lines are passed on as-is. For details about + this prefixing see + sd-daemon3. + Defaults to true. + + + + TimerSlackNSec= + Sets the timer slack in nanoseconds for the + executed processes. The timer slack controls the accuracy of + wake-ups triggered by timers. See + prctl2 + for more information. Note that in contrast to most other time + span definitions this parameter takes an integer value in + nano-seconds if no unit is specified. The usual time units are + understood too. + + + + LimitCPU= + LimitFSIZE= + LimitDATA= + LimitSTACK= + LimitCORE= + LimitRSS= + LimitNOFILE= + LimitAS= + LimitNPROC= + LimitMEMLOCK= + LimitLOCKS= + LimitSIGPENDING= + LimitMSGQUEUE= + LimitNICE= + LimitRTPRIO= + LimitRTTIME= + Set soft and hard limits on various resources for executed processes. See + setrlimit2 for details on + the resource limit concept. Resource limits may be specified in two formats: either as single value to set a + specific soft and hard limit to the same value, or as colon-separated pair to set + both limits individually (e.g. LimitAS=4G:16G). Use the string infinity + to configure no limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base + 1024) may be used for resource limits measured in bytes (e.g. LimitAS=16G). For the limits referring to time + values, the usual time units ms, s, min, h and so on may be used (see + systemd.time7 for + details). Note that if no time unit is specified for LimitCPU= the default unit of seconds + is implied, while for LimitRTTIME= the default unit of microseconds is implied. Also, note + that the effective granularity of the limits might influence their enforcement. For example, time limits + specified for LimitCPU= will be rounded up implicitly to multiples of 1s. For + LimitNICE= the value may be specified in two syntaxes: if prefixed with + + or -, the value is understood as regular Linux nice value in the range -20..19. If not + prefixed like this the value is understood as raw resource limit parameter in the range 0..40 (with 0 being + equivalent to 1). + + Note that most process resource limits configured with + these options are per-process, and processes may fork in order + to acquire a new set of resources that are accounted + independently of the original process, and may thus escape + limits set. Also note that LimitRSS= is not + implemented on Linux, and setting it has no effect. Often it + is advisable to prefer the resource controls listed in + systemd.resource-control5 + over these per-process limits, as they apply to services as a + whole, may be altered dynamically at runtime, and are + generally more expressive. For example, + MemoryLimit= is a more powerful (and + working) replacement for LimitRSS=. + + + Limit directives and their equivalent with ulimit + + + + + + + + Directive + ulimit equivalent + Unit + + + + + LimitCPU= + ulimit -t + Seconds + + + LimitFSIZE= + ulimit -f + Bytes + + + LimitDATA= + ulimit -d + Bytes + + + LimitSTACK= + ulimit -s + Bytes + + + LimitCORE= + ulimit -c + Bytes + + + LimitRSS= + ulimit -m + Bytes + + + LimitNOFILE= + ulimit -n + Number of File Descriptors + + + LimitAS= + ulimit -v + Bytes + + + LimitNPROC= + ulimit -u + Number of Processes + + + LimitMEMLOCK= + ulimit -l + Bytes + + + LimitLOCKS= + ulimit -x + Number of Locks + + + LimitSIGPENDING= + ulimit -i + Number of Queued Signals + + + LimitMSGQUEUE= + ulimit -q + Bytes + + + LimitNICE= + ulimit -e + Nice Level + + + LimitRTPRIO= + ulimit -r + Realtime Priority + + + LimitRTTIME= + No equivalent + Microseconds + + + +
+
+ + + PAMName= + Sets the PAM service name to set up a session + as. If set, the executed process will be registered as a PAM + session under the specified service name. This is only useful + in conjunction with the User= setting. If + not set, no PAM session will be opened for the executed + processes. See + pam8 + for details. + + + + CapabilityBoundingSet= + + Controls which capabilities to include in the capability bounding set for the executed + process. See capabilities7 for + details. Takes a whitespace-separated list of capability names as read by cap_from_name3, + e.g. CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, + CAP_SYS_PTRACE. Capabilities listed will be included in the bounding set, all others are + removed. If the list of capabilities is prefixed with ~, all but the listed capabilities + will be included, the effect of the assignment inverted. Note that this option also affects the respective + capabilities in the effective, permitted and inheritable capability sets. If this option is not used, the + capability bounding set is not modified on process execution, hence no limits on the capabilities of the + process are enforced. This option may appear more than once, in which case the bounding sets are merged. If the + empty string is assigned to this option, the bounding set is reset to the empty capability set, and all prior + settings have no effect. If set to ~ (without any further argument), the bounding set is + reset to the full set of available capabilities, also undoing any previous settings. This does not affect + commands prefixed with +. + + + + AmbientCapabilities= + + Controls which capabilities to include in the + ambient capability set for the executed process. Takes a + whitespace-separated list of capability names as read by + cap_from_name3, + e.g. CAP_SYS_ADMIN, + CAP_DAC_OVERRIDE, + CAP_SYS_PTRACE. This option may appear more than + once in which case the ambient capability sets are merged. + If the list of capabilities is prefixed with ~, all + but the listed capabilities will be included, the effect of the + assignment inverted. If the empty string is + assigned to this option, the ambient capability set is reset to + the empty capability set, and all prior settings have no effect. + If set to ~ (without any further argument), the + ambient capability set is reset to the full set of available + capabilities, also undoing any previous settings. Note that adding + capabilities to ambient capability set adds them to the process's + inherited capability set. + + Ambient capability sets are useful if you want to execute a process + as a non-privileged user but still want to give it some capabilities. + Note that in this case option keep-caps is + automatically added to SecureBits= to retain the + capabilities over the user change. AmbientCapabilities= does not affect + commands prefixed with +. + + + + SecureBits= + Controls the secure bits set for the executed + process. Takes a space-separated combination of options from + the following list: + , + , + , + , + , and + . + This option may appear more than once, in which case the secure + bits are ORed. If the empty string is assigned to this option, + the bits are reset to 0. This does not affect commands prefixed with +. + See capabilities7 + for details. + + + + ReadWritePaths= + ReadOnlyPaths= + InaccessiblePaths= + + Sets up a new file system namespace for + executed processes. These options may be used to limit access + a process might have to the main file system hierarchy. Each + setting takes a space-separated list of paths relative to + the host's root directory (i.e. the system running the service manager). + Note that if entries contain symlinks, they are resolved from the host's root directory as well. + Entries (files or directories) listed in + ReadWritePaths= are accessible from + within the namespace with the same access rights as from + outside. Entries listed in + ReadOnlyPaths= are accessible for + reading only, writing will be refused even if the usual file + access controls would permit this. Entries listed in + InaccessiblePaths= will be made + inaccessible for processes inside the namespace, and may not + countain any other mountpoints, including those specified by + ReadWritePaths= or + ReadOnlyPaths=. + Note that restricting access with these options does not extend + to submounts of a directory that are created later on. + Non-directory paths can be specified as well. These + options may be specified more than once, in which case all + paths listed will have limited access from within the + namespace. If the empty string is assigned to this option, the + specific list is reset, and all prior assignments have no + effect. + Paths in + ReadOnlyPaths= + and + InaccessiblePaths= + may be prefixed with + -, in which case + they will be ignored when they do not + exist. Note that using this + setting will disconnect propagation of + mounts from the service to the host + (propagation in the opposite direction + continues to work). This means that + this setting may not be used for + services which shall be able to + install mount points in the main mount + namespace. + + + + PrivateTmp= + + Takes a boolean argument. If true, sets up a + new file system namespace for the executed processes and + mounts private /tmp and + /var/tmp directories inside it that is + not shared by processes outside of the namespace. This is + useful to secure access to temporary files of the process, but + makes sharing between processes via /tmp + or /var/tmp impossible. If this is + enabled, all temporary files created by a service in these + directories will be removed after the service is stopped. + Defaults to false. It is possible to run two or more units + within the same private /tmp and + /var/tmp namespace by using the + JoinsNamespaceOf= directive, see + systemd.unit5 + for details. Note that using this setting will disconnect + propagation of mounts from the service to the host + (propagation in the opposite direction continues to work). + This means that this setting may not be used for services + which shall be able to install mount points in the main mount + namespace. + + + + PrivateDevices= + + Takes a boolean argument. If true, sets up a + new /dev namespace for the executed processes and only adds + API pseudo devices such as /dev/null, + /dev/zero or + /dev/random (as well as the pseudo TTY + subsystem) to it, but no physical devices such as + /dev/sda. This is useful to securely turn + off physical device access by the executed process. Defaults + to false. Enabling this option will also remove + CAP_MKNOD from the capability bounding + set for the unit (see above), and set + DevicePolicy=closed (see + systemd.resource-control5 + for details). Note that using this setting will disconnect + propagation of mounts from the service to the host + (propagation in the opposite direction continues to work). + This means that this setting may not be used for services + which shall be able to install mount points in the main mount + namespace. The /dev namespace will be mounted read-only and 'noexec'. + The latter may break old programs which try to set up executable + memory by using mmap2 + of /dev/zero instead of using MAP_ANON. + + + + PrivateNetwork= + + Takes a boolean argument. If true, sets up a + new network namespace for the executed processes and + configures only the loopback network device + lo inside it. No other network devices will + be available to the executed process. This is useful to + securely turn off network access by the executed process. + Defaults to false. It is possible to run two or more units + within the same private network namespace by using the + JoinsNamespaceOf= directive, see + systemd.unit5 + for details. Note that this option will disconnect all socket + families from the host, this includes AF_NETLINK and AF_UNIX. + The latter has the effect that AF_UNIX sockets in the abstract + socket namespace will become unavailable to the processes + (however, those located in the file system will continue to be + accessible). + + + + ProtectSystem= + + Takes a boolean argument or + full. If true, mounts the + /usr and /boot + directories read-only for processes invoked by this unit. If + set to full, the /etc + directory is mounted read-only, too. This setting ensures that + any modification of the vendor-supplied operating system (and + optionally its configuration) is prohibited for the service. + It is recommended to enable this setting for all long-running + services, unless they are involved with system updates or need + to modify the operating system in other ways. Note however + that processes retaining the CAP_SYS_ADMIN capability can undo + the effect of this setting. This setting is hence particularly + useful for daemons which have this capability removed, for + example with CapabilityBoundingSet=. + Defaults to off. + + + + ProtectHome= + + Takes a boolean argument or + read-only. If true, the directories + /home, /root and + /run/user + are made inaccessible and empty for processes invoked by this + unit. If set to read-only, the three + directories are made read-only instead. It is recommended to + enable this setting for all long-running services (in + particular network-facing ones), to ensure they cannot get + access to private user data, unless the services actually + require access to the user's private data. Note however that + processes retaining the CAP_SYS_ADMIN capability can undo the + effect of this setting. This setting is hence particularly + useful for daemons which have this capability removed, for + example with CapabilityBoundingSet=. + Defaults to off. + + + + MountFlags= + + Takes a mount propagation flag: + , or + , which control whether mounts in the + file system namespace set up for this unit's processes will + receive or propagate mounts or unmounts. See + mount2 + for details. Defaults to . Use + to ensure that mounts and unmounts are + propagated from the host to the container and vice versa. Use + to run processes so that none of their + mounts and unmounts will propagate to the host. Use + to also ensure that no mounts and + unmounts from the host will propagate into the unit processes' + namespace. Note that means that file + systems mounted on the host might stay mounted continuously in + the unit's namespace, and thus keep the device busy. Note that + the file system namespace related options + (PrivateTmp=, + PrivateDevices=, + ProtectSystem=, + ProtectHome=, + ReadOnlyPaths=, + InaccessiblePaths= and + ReadWritePaths=) require that mount + and unmount propagation from the unit's file system namespace + is disabled, and hence downgrade to + . + + + + UtmpIdentifier= + + Takes a four character identifier string for + an utmp5 + and wtmp entry for this service. This should only be + set for services such as getty + implementations (such as agetty8) + where utmp/wtmp entries must be created and cleared before and + after execution, or for services that shall be executed as if + they were run by a getty process (see + below). If the configured string is longer than four + characters, it is truncated and the terminal four characters + are used. This setting interprets %I style string + replacements. This setting is unset by default, i.e. no + utmp/wtmp entries are created or cleaned up for this + service. + + + + UtmpMode= + + Takes one of init, + login or user. If + UtmpIdentifier= is set, controls which + type of utmp5/wtmp + entries for this service are generated. This setting has no + effect unless UtmpIdentifier= is set + too. If init is set, only an + INIT_PROCESS entry is generated and the + invoked process must implement a + getty-compatible utmp/wtmp logic. If + login is set, first an + INIT_PROCESS entry, followed by a + LOGIN_PROCESS entry is generated. In + this case, the invoked process must implement a login1-compatible + utmp/wtmp logic. If user is set, first an + INIT_PROCESS entry, then a + LOGIN_PROCESS entry and finally a + USER_PROCESS entry is generated. In this + case, the invoked process may be any process that is suitable + to be run as session leader. Defaults to + init. + + + + SELinuxContext= + + Set the SELinux security context of the + executed process. If set, this will override the automated + domain transition. However, the policy still needs to + authorize the transition. This directive is ignored if SELinux + is disabled. If prefixed by -, all errors + will be ignored. This does not affect commands prefixed with +. + See setexeccon3 + for details. + + + + AppArmorProfile= + + Takes a profile name as argument. The process + executed by the unit will switch to this profile when started. + Profiles must already be loaded in the kernel, or the unit + will fail. This result in a non operation if AppArmor is not + enabled. If prefixed by -, all errors will + be ignored. This does not affect commands prefixed with +. + + + + SmackProcessLabel= + + Takes a security + label as argument. The process executed by the unit will be + started under this label and SMACK will decide whether the + process is allowed to run or not, based on it. The process + will continue to run under the label specified here unless the + executable has its own label, in + which case the process will transition to run under that + label. When not specified, the label that systemd is running + under is used. This directive is ignored if SMACK is + disabled. + + The value may be prefixed by -, in + which case all errors will be ignored. An empty value may be + specified to unset previous assignments. This does not affect + commands prefixed with +. + + + + + IgnoreSIGPIPE= + + Takes a boolean argument. If true, causes + SIGPIPE to be ignored in the executed + process. Defaults to true because SIGPIPE + generally is useful only in shell pipelines. + + + + NoNewPrivileges= + + Takes a boolean argument. If true, ensures + that the service process and all its children can never gain + new privileges. This option is more powerful than the + respective secure bits flags (see above), as it also prohibits + UID changes of any kind. This is the simplest, most effective + way to ensure that a process and its children can never + elevate privileges again. + + + + SystemCallFilter= + + Takes a space-separated list of system call + names. If this setting is used, all system calls executed by + the unit processes except for the listed ones will result in + immediate process termination with the + SIGSYS signal (whitelisting). If the + first character of the list is ~, the + effect is inverted: only the listed system calls will result + in immediate process termination (blacklisting). If running in + user mode, or in system mode, but without the + CAP_SYS_ADMIN capability (e.g. setting + User=nobody), + NoNewPrivileges=yes is implied. This + feature makes use of the Secure Computing Mode 2 interfaces of + the kernel ('seccomp filtering') and is useful for enforcing a + minimal sandboxing environment. Note that the + execve, + rt_sigreturn, + sigreturn, + exit_group, exit + system calls are implicitly whitelisted and do not need to be + listed explicitly. This option may be specified more than once, + in which case the filter masks are merged. If the empty string + is assigned, the filter is reset, all prior assignments will + have no effect. This does not affect commands prefixed with +. + + If you specify both types of this option (i.e. + whitelisting and blacklisting), the first encountered will + take precedence and will dictate the default action + (termination or approval of a system call). Then the next + occurrences of this option will add or delete the listed + system calls from the set of the filtered system calls, + depending of its type and the default action. (For example, if + you have started with a whitelisting of + read and write, and + right after it add a blacklisting of + write, then write + will be removed from the set.) + + As the number of possible system + calls is large, predefined sets of system calls are provided. + A set starts with @ character, followed by + name of the set. + + + Currently predefined system call sets + + + + + + + Set + Description + + + + + @clock + System calls for changing the system clock (adjtimex2, settimeofday2, and related calls) + + + @cpu-emulation + System calls for CPU emulation functionality (vm862 and related calls) + + + @debug + Debugging, performance monitoring and tracing functionality (ptrace2, perf_event_open2 and related calls) + + + @io-event + Event loop system calls (poll2, select2, epoll7, eventfd2 and related calls) + + + @ipc + SysV IPC, POSIX Message Queues or other IPC (mq_overview7, svipc7) + + + @keyring + Kernel keyring access (keyctl2 and related calls) + + + @module + Kernel module control (init_module2, delete_module2 and related calls) + + + @mount + File system mounting and unmounting (mount2, chroot2, and related calls) + + + @network-io + Socket I/O (including local AF_UNIX): socket7, unix7 + + + @obsolete + Unusual, obsolete or unimplemented (create_module2, gtty2, …) + + + @privileged + All system calls which need super-user capabilities (capabilities7) + + + @process + Process control, execution, namespaces (execve2, kill2, namespaces7, … + + + @raw-io + Raw I/O port access (ioperm2, iopl2, pciconfig_read(), … + + + +
+ + Note, that as new system calls are added to the kernel, additional system calls might be added to the groups + above, so the contents of the sets may change between systemd versions.
+
+ + + SystemCallErrorNumber= + + Takes an errno error number + name to return when the system call filter configured with + SystemCallFilter= is triggered, instead of + terminating the process immediately. Takes an error name such + as EPERM, EACCES or + EUCLEAN. When this setting is not used, + or when the empty string is assigned, the process will be + terminated immediately when the filter is + triggered. + + + + SystemCallArchitectures= + + Takes a space-separated list of architecture + identifiers to include in the system call filter. The known + architecture identifiers are x86, + x86-64, x32, + arm as well as the special identifier + native. Only system calls of the + specified architectures will be permitted to processes of this + unit. This is an effective way to disable compatibility with + non-native architectures for processes, for example to + prohibit execution of 32-bit x86 binaries on 64-bit x86-64 + systems. The special native identifier + implicitly maps to the native architecture of the system (or + more strictly: to the architecture the system manager is + compiled for). If running in user mode, or in system mode, + but without the CAP_SYS_ADMIN + capability (e.g. setting User=nobody), + NoNewPrivileges=yes is implied. Note + that setting this option to a non-empty list implies that + native is included too. By default, this + option is set to the empty list, i.e. no architecture system + call filtering is applied. + + + + RestrictAddressFamilies= + + Restricts the set of socket address families + accessible to the processes of this unit. Takes a + space-separated list of address family names to whitelist, + such as + AF_UNIX, + AF_INET or + AF_INET6. When + prefixed with ~ the listed address + families will be applied as blacklist, otherwise as whitelist. + Note that this restricts access to the + socket2 + system call only. Sockets passed into the process by other + means (for example, by using socket activation with socket + units, see + systemd.socket5) + are unaffected. Also, sockets created with + socketpair() (which creates connected + AF_UNIX sockets only) are unaffected. Note that this option + has no effect on 32-bit x86 and is ignored (but works + correctly on x86-64). If running in user mode, or in system + mode, but without the CAP_SYS_ADMIN + capability (e.g. setting User=nobody), + NoNewPrivileges=yes is implied. By + default, no restriction applies, all address families are + accessible to processes. If assigned the empty string, any + previous list changes are undone. + + Use this option to limit exposure of processes to remote + systems, in particular via exotic network protocols. Note that + in most cases, the local AF_UNIX address + family should be included in the configured whitelist as it is + frequently used for local communication, including for + syslog2 + logging. This does not affect commands prefixed with +. + + + + Personality= + + Controls which kernel architecture uname2 shall report, + when invoked by unit processes. Takes one of the architecture identifiers x86, + x86-64, ppc, ppc-le, ppc64, + ppc64-le, s390 or s390x. Which personality + architectures are supported depends on the system architecture. Usually the 64bit versions of the various + system architectures support their immediate 32bit personality architecture counterpart, but no others. For + example, x86-64 systems support the x86-64 and + x86 personalities but no others. The personality feature is useful when running 32-bit + services on a 64-bit host system. If not specified, the personality is left unmodified and thus reflects the + personality of the host system's kernel. + + + + RuntimeDirectory= + RuntimeDirectoryMode= + + Takes a list of directory names. If set, one + or more directories by the specified names will be created + below /run (for system services) or below + $XDG_RUNTIME_DIR (for user services) when + the unit is started, and removed when the unit is stopped. The + directories will have the access mode specified in + RuntimeDirectoryMode=, and will be owned by + the user and group specified in User= and + Group=. Use this to manage one or more + runtime directories of the unit and bind their lifetime to the + daemon runtime. The specified directory names must be + relative, and may not include a /, i.e. + must refer to simple directories to create or remove. This is + particularly useful for unprivileged daemons that cannot + create runtime directories in /run due to + lack of privileges, and to make sure the runtime directory is + cleaned up automatically after use. For runtime directories + that require more complex or different configuration or + lifetime guarantees, please consider using + tmpfiles.d5. + + + + MemoryDenyWriteExecute= + + Takes a boolean argument. If set, attempts to create memory mappings that are writable and + executable at the same time, or to change existing memory mappings to become executable are prohibited. + Specifically, a system call filter is added that rejects + mmap2 + system calls with both PROT_EXEC and PROT_WRITE set + and mprotect2 + system calls with PROT_EXEC set. Note that this option is incompatible with programs + that generate program code dynamically at runtime, such as JIT execution engines, or programs compiled making + use of the code "trampoline" feature of various C compilers. This option improves service security, as it makes + harder for software exploits to change running code dynamically. + + + + + RestrictRealtime= + + Takes a boolean argument. If set, any attempts to enable realtime scheduling in a process of + the unit are refused. This restricts access to realtime task scheduling policies such as + SCHED_FIFO, SCHED_RR or SCHED_DEADLINE. See + sched7 for details about + these scheduling policies. Realtime scheduling policies may be used to monopolize CPU time for longer periods + of time, and may hence be used to lock up or otherwise trigger Denial-of-Service situations on the system. It + is hence recommended to restrict access to realtime scheduling to the few programs that actually require + them. Defaults to off. + + +
+
+ + + Environment variables in spawned processes + + Processes started by the system are executed in a clean + environment in which select variables listed below are set. System + processes started by systemd do not inherit variables from PID 1, + but processes started by user systemd instances inherit all + environment variables from the user systemd instance. + + + + + $PATH + + Colon-separated list of directories to use + when launching executables. Systemd uses a fixed value of + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin. + + + + + $LANG + + Locale. Can be set in + locale.conf5 + or on the kernel command line (see + systemd1 + and + kernel-command-line7). + + + + + $USER + $LOGNAME + $HOME + $SHELL + + User name (twice), home directory, and the + login shell. The variables are set for the units that have + User= set, which includes user + systemd instances. See + passwd5. + + + + + $XDG_RUNTIME_DIR + + The directory for volatile state. Set for the + user systemd instance, and also in user + sessions. See + pam_systemd8. + + + + + $XDG_SESSION_ID + $XDG_SEAT + $XDG_VTNR + + The identifier of the session, the seat name, + and virtual terminal of the session. Set by + pam_systemd8 + for login sessions. $XDG_SEAT and + $XDG_VTNR will only be set when attached to + a seat and a tty. + + + + $MAINPID + + The PID of the units main process if it is + known. This is only set for control processes as invoked by + ExecReload= and similar. + + + + $MANAGERPID + + The PID of the user systemd + instance, set for processes spawned by it. + + + + $LISTEN_FDS + $LISTEN_PID + $LISTEN_FDNAMES + + Information about file descriptors passed to a + service for socket activation. See + sd_listen_fds3. + + + + + $NOTIFY_SOCKET + + The socket + sd_notify() talks to. See + sd_notify3. + + + + + $WATCHDOG_PID + $WATCHDOG_USEC + + Information about watchdog keep-alive notifications. See + sd_watchdog_enabled3. + + + + + $TERM + + Terminal type, set only for units connected to + a terminal (StandardInput=tty, + StandardOutput=tty, or + StandardError=tty). See + termcap5. + + + + + $JOURNAL_STREAM + + If the standard output or standard error output of the executed processes are connected to the + journal (for example, by setting StandardError=journal) $JOURNAL_STREAM + contains the device and inode numbers of the connection file descriptor, formatted in decimal, separated by a + colon (:). This permits invoked processes to safely detect whether their standard output or + standard error output are connected to the journal. The device and inode numbers of the file descriptors should + be compared with the values set in the environment variable to determine whether the process output is still + connected to the journal. Note that it is generally not sufficient to only check whether + $JOURNAL_STREAM is set at all as services might invoke external processes replacing their + standard output or standard error output, without unsetting the environment variable. + + This environment variable is primarily useful to allow services to optionally upgrade their used log + protocol to the native journal protocol (using + sd_journal_print3 and other + functions) if their standard output or standard error output is connected to the journal anyway, thus enabling + delivery of structured metadata along with logged messages. + + + + Additional variables may be configured by the following + means: for processes spawned in specific units, use the + Environment=, EnvironmentFile= + and PassEnvironment= options above; to specify + variables globally, use DefaultEnvironment= + (see + systemd-system.conf5) + or the kernel option systemd.setenv= (see + systemd1). + Additional variables may also be set through PAM, + cf. pam_env8. + + + + See Also + + systemd1, + systemctl1, + journalctl8, + systemd.unit5, + systemd.service5, + systemd.socket5, + systemd.swap5, + systemd.mount5, + systemd.kill5, + systemd.resource-control5, + systemd.time7, + systemd.directives7, + tmpfiles.d5, + exec3 + + + +
diff --git a/src/grp-system/systemd/systemd.generator.xml b/src/grp-system/systemd/systemd.generator.xml new file mode 100644 index 0000000000..b268104c9d --- /dev/null +++ b/src/grp-system/systemd/systemd.generator.xml @@ -0,0 +1,348 @@ + + +%entities; +]> + + + + + + systemd.generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.generator + 7 + + + + systemd.generator + Systemd unit generators + + + + + /path/to/generator + normal-dir + early-dir + late-dir + + + + /run/systemd/system-generators/* +/etc/systemd/system-generators/* +/usr/local/lib/systemd/system-generators/* +&systemgeneratordir;/* + + + + /run/systemd/user-generators/* +/etc/systemd/user-generators/* +/usr/local/lib/systemd/user-generators/* +&usergeneratordir;/* + + + + + Description + Generators are small binaries that live in + &usergeneratordir;/ and other directories + listed above. + systemd1 + will execute those binaries very early at bootup and at + configuration reload time — before unit files are loaded. + Generators can dynamically generate unit files or create symbolic + links to unit files to add additional dependencies, thus extending + or overriding existing definitions. Their main purpose is to + convert configuration files that are not native unit files + dynamically into native unit files. + + Generators are loaded from a set of paths determined during + compilation, as listed above. System and user generators are loaded + from directories with names ending in + system-generators/ and + user-generators/, respectively. Generators + found in directories listed earlier override the ones with the + same name in directories lower in the list. A symlink to + /dev/null or an empty file can be used to + mask a generator, thereby preventing it from running. Please note + that the order of the two directories with the highest priority is + reversed with respect to the unit load path, and generators in + /run overwrite those in + /etc. + + After installing new generators or updating the + configuration, systemctl daemon-reload may be + executed. This will delete the previous configuration created by + generators, re-run all generators, and cause + systemd to reload units from disk. See + systemctl1 + for more information. + + + + + Writing generators + + Generators are invoked with three arguments: paths to + runtime directories where generators can place their generated + unit files or symlinks. + + + + normal-dir + argv[1] may be used to override unit files in + /usr, but not those in + /etc. This means that unit files placed + in this directory take precedence over vendor unit + configuration but not over native user/administrator unit + configuration. + + + + early-dir + argv[2] may be used to override unit files in + /usr and in + /etc. This means that unit files placed + in this directory take precedence over all configuration, + both vendor and user/administrator. + + + + late-dir + argv[3] may be used to extend the unit file tree without + overriding any other unit files. Any native configuration + files supplied by the vendor or user/administrator take + precedence over the generated ones placed in this directory. + + + + + + Notes + + + + + All generators are executed in parallel. That means all + executables are started at the very same time and need to + be able to cope with this parallelism. + + + + + + Generators are run very early at boot and cannot rely on + any external services. They may not talk to any other + process. That includes simple things such as logging to + syslog3, + or systemd itself (this means: no + systemctl1)! + Non-essential file systems like + /var and /home + are mounted after generators have run. Generators + can however rely on the most basic kernel functionality to be + available, including a mounted /sys, + /proc, /dev, + /usr. + + + + + + Units written by generators are removed when the configuration + is reloaded. That means the lifetime of the generated + units is closely bound to the reload cycles of + systemd itself. + + + + + + Generators should only be used to generate unit files, not + any other kind of configuration. Due to the lifecycle + logic mentioned above, generators are not a good fit to + generate dynamic configuration for other services. If you + need to generate dynamic configuration for other services, + do so in normal services you order before the service in + question. + + + + + + Since + syslog3 + is not available (see above), log messages have to be + written to /dev/kmsg instead. + + + + + + It is a good idea to use the + SourcePath= directive in generated unit + files to specify the source configuration file you are + generating the unit from. This makes things more easily + understood by the user and also has the benefit that + systemd can warn the user about configuration files that + changed on disk but have not been read yet by systemd. + + + + + + Generators may write out dynamic unit files or just hook + unit files into other units with the usual + .wants/ or + .requires/ symlinks. Often, it is + nicer to simply instantiate a template unit file from + /usr with a generator instead of + writing out entirely dynamic unit files. Of course, this + works only if a single parameter is to be used. + + + + + + If you are careful, you can implement generators in shell + scripts. We do recommend C code however, since generators + are executed synchronously and hence delay the + entire boot if they are slow. + + + + + Regarding overriding semantics: there are two rules we + try to follow when thinking about the overriding semantics: + + + + + User configuration should override vendor + configuration. This (mostly) means that stuff from + /etc should override stuff from + /usr. + + + + Native configuration should override non-native + configuration. This (mostly) means that stuff you + generate should never override native unit files for the + same purpose. + + + + Of these two rules the first rule is probably the more + important one and breaks the second one sometimes. Hence, + when deciding whether to user argv[1], argv[2], or argv[3], + your default choice should probably be argv[1]. + + + + + Instead of heading off now and writing all kind of + generators for legacy configuration file formats, please + think twice! It is often a better idea to just deprecate + old stuff instead of keeping it artificially alive. + + + + + + + + Examples + + systemd-fstab-generator + + systemd-fstab-generator8 + converts /etc/fstab into native mount + units. It uses argv[1] as location to place the generated unit + files in order to allow the user to override + /etc/fstab with her own native unit files, + but also to ensure that /etc/fstab + overrides any vendor default from /usr. + + + After editing /etc/fstab, the user + should invoke systemctl daemon-reload. This + will re-run all generators and cause systemd + to reload units from disk. To actually mount new directories + added to fstab, systemctl start + /path/to/mountpoint or + systemctl start local-fs.target may be used. + + + + + systemd-system-update-generator + + systemd-system-update-generator8 + temporarily redirects default.target to + system-update.target, if a system update is + scheduled. Since this needs to override the default user + configuration for default.target, it uses + argv[2]. For details about this logic, see + systemd.offline-updates7. + + + + + Debugging a generator + + dir=$(mktemp -d) +SYSTEMD_LOG_LEVEL=debug &systemgeneratordir;/systemd-fstab-generator \ + "$dir" "$dir" "$dir" +find $dir + + + + + See also + + + systemd1, + systemd-cryptsetup-generator8, + systemd-debug-generator8, + systemd-fstab-generator8, + fstab5, + systemd-getty-generator8, + systemd-gpt-auto-generator8, + systemd-hibernate-resume-generator8, + systemd-system-update-generator8, + systemd-sysv-generator8, + systemd.unit5, + systemctl1 + + + diff --git a/src/grp-system/systemd/systemd.journal-fields.xml b/src/grp-system/systemd/systemd.journal-fields.xml new file mode 100644 index 0000000000..494f97aad1 --- /dev/null +++ b/src/grp-system/systemd/systemd.journal-fields.xml @@ -0,0 +1,525 @@ + + + + + + + + + systemd.journal-fields + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.journal-fields + 7 + + + + systemd.journal-fields + Special journal fields + + + + Description + + Entries in the journal resemble an environment block in + their syntax but with fields that can include binary data. + Primarily, fields are formatted UTF-8 text strings, and binary + formatting is used only where formatting as UTF-8 text strings + makes little sense. New fields may freely be defined by + applications, but a few fields have special meaning. All fields + with special meanings are optional. In some cases, fields may + appear more than once per entry. + + + + User Journal Fields + + User fields are fields that are directly passed from clients + and stored in the journal. + + + + MESSAGE= + + The human-readable message string for this entry. This + is supposed to be the primary text shown to the user. It is + usually not translated (but might be in some cases), and is + not supposed to be parsed for metadata. + + + + + MESSAGE_ID= + + A 128-bit message identifier ID for recognizing + certain message types, if this is desirable. This should + contain a 128-bit ID formatted as a lower-case hexadecimal + string, without any separating dashes or suchlike. This is + recommended to be a UUID-compatible ID, but this is not + enforced, and formatted differently. Developers can generate + a new ID for this purpose with journalctl + . + + + + + + PRIORITY= + + A priority value between 0 (emerg) + and 7 (debug) formatted as a decimal + string. This field is compatible with syslog's priority + concept. + + + + + CODE_FILE= + CODE_LINE= + CODE_FUNC= + + The code location generating this message, if known. + Contains the source filename, the line number and the + function name. + + + + + ERRNO= + + The low-level Unix error number causing this entry, if + any. Contains the numeric value of + errno3 + formatted as a decimal string. + + + + + SYSLOG_FACILITY= + SYSLOG_IDENTIFIER= + SYSLOG_PID= + + Syslog compatibility fields containing the facility + (formatted as decimal string), the identifier string (i.e. + "tag"), and the client PID. (Note that the tag is usually + derived from glibc's + program_invocation_short_name variable, + see + program_invocation_short_name3.) + + + + + + + + Trusted Journal Fields + + Fields prefixed with an underscore are trusted fields, i.e. + fields that are implicitly added by the journal and cannot be + altered by client code. + + + + _PID= + _UID= + _GID= + + The process, user, and group ID of the process the + journal entry originates from formatted as a decimal + string. + + + + + _COMM= + _EXE= + _CMDLINE= + + The name, the executable path, and the command line of + the process the journal entry originates from. + + + + + _CAP_EFFECTIVE= + + The effective + capabilities7 + of the process the journal entry originates from. + + + + + _AUDIT_SESSION= + _AUDIT_LOGINUID= + + The session and login UID of the process the journal + entry originates from, as maintained by the kernel audit + subsystem. + + + + + _SYSTEMD_CGROUP= + _SYSTEMD_SESSION= + _SYSTEMD_UNIT= + _SYSTEMD_USER_UNIT= + _SYSTEMD_OWNER_UID= + _SYSTEMD_SLICE= + + + The control group path in the systemd hierarchy, the + systemd session ID (if any), the systemd unit name (if any), + the systemd user session unit name (if any), the owner UID + of the systemd session (if any) and the systemd slice unit + of the process the journal entry originates from. + + + + + _SELINUX_CONTEXT= + + The SELinux security context (label) of the process + the journal entry originates from. + + + + + _SOURCE_REALTIME_TIMESTAMP= + + The earliest trusted timestamp of the message, if any + is known that is different from the reception time of the + journal. This is the time in microseconds since the epoch + UTC, formatted as a decimal string. + + + + + _BOOT_ID= + + The kernel boot ID for the boot the message was + generated in, formatted as a 128-bit hexadecimal + string. + + + + + _MACHINE_ID= + + The machine ID of the originating host, as available + in + machine-id5. + + + + + _HOSTNAME= + + The name of the originating host. + + + + + _TRANSPORT= + + How the entry was received by the journal service. + Valid transports are: + + + + + + + + for those read from the kernel audit subsystem + + + + + + + + + + for internally generated messages + + + + + + + + + + for those received via the local syslog socket + with the syslog protocol + + + + + + + + + + for those received via the native journal + protocol + + + + + + + + + + for those read from a service's standard output + or error output + + + + + + + + + + for those read from the kernel + + + + + + + + + + + Kernel Journal Fields + + Kernel fields are fields that are used by messages + originating in the kernel and stored in the journal. + + + + _KERNEL_DEVICE= + + The kernel device name. If the entry is associated to + a block device, the major and minor of the device node, + separated by : and prefixed by + b. Similar for character devices but + prefixed by c. For network devices, this + is the interface index prefixed by n. For + all other devices, this is the subsystem name prefixed by + +, followed by :, + followed by the kernel device name. + + + + _KERNEL_SUBSYSTEM= + + The kernel subsystem name. + + + + _UDEV_SYSNAME= + + The kernel device name as it shows up in the device + tree below /sys. + + + + _UDEV_DEVNODE= + + The device node path of this device in + /dev. + + + + _UDEV_DEVLINK= + + Additional symlink names pointing to the device node + in /dev. This field is frequently set + more than once per entry. + + + + + + + Fields to log on behalf of a different program + + Fields in this section are used by programs to specify that + they are logging on behalf of another program or unit. + + + Fields used by the systemd-coredump + coredump kernel helper: + + + + + COREDUMP_UNIT= + COREDUMP_USER_UNIT= + + Used to annotate messages containing coredumps from + system and session units. See + coredumpctl1. + + + + + + Privileged programs (currently UID 0) may attach + OBJECT_PID= to a message. This will instruct + systemd-journald to attach additional fields on + behalf of the caller: + + + + OBJECT_PID=PID + + PID of the program that this message pertains to. + + + + + + OBJECT_UID= + OBJECT_GID= + OBJECT_COMM= + OBJECT_EXE= + OBJECT_CMDLINE= + OBJECT_AUDIT_SESSION= + OBJECT_AUDIT_LOGINUID= + OBJECT_SYSTEMD_CGROUP= + OBJECT_SYSTEMD_SESSION= + OBJECT_SYSTEMD_OWNER_UID= + OBJECT_SYSTEMD_UNIT= + OBJECT_SYSTEMD_USER_UNIT= + + These are additional fields added automatically by + systemd-journald. Their meaning is the + same as + _UID=, + _GID=, + _COMM=, + _EXE=, + _CMDLINE=, + _AUDIT_SESSION=, + _AUDIT_LOGINUID=, + _SYSTEMD_CGROUP=, + _SYSTEMD_SESSION=, + _SYSTEMD_UNIT=, + _SYSTEMD_USER_UNIT=, and + _SYSTEMD_OWNER_UID= + as described above, except that the process identified by + PID is described, instead of the + process which logged the message. + + + + + + + + Address Fields + + During serialization into external formats, such as the + Journal + Export Format or the Journal + JSON Format, the addresses of journal entries are + serialized into fields prefixed with double underscores. Note that + these are not proper fields when stored in the journal but for + addressing metadata of entries. They cannot be written as part of + structured log entries via calls such as + sd_journal_send3. + They may also not be used as matches for + sd_journal_add_match3 + + + + __CURSOR= + + The cursor for the entry. A cursor is an opaque text + string that uniquely describes the position of an entry in + the journal and is portable across machines, platforms and + journal files. + + + + + + __REALTIME_TIMESTAMP= + + The wallclock time + (CLOCK_REALTIME) at the point in time + the entry was received by the journal, in microseconds since + the epoch UTC, formatted as a decimal string. This has + different properties from + _SOURCE_REALTIME_TIMESTAMP=, as it is + usually a bit later but more likely to be monotonic. + + + + + + __MONOTONIC_TIMESTAMP= + + The monotonic time + (CLOCK_MONOTONIC) at the point in time + the entry was received by the journal in microseconds, + formatted as a decimal string. To be useful as an address + for the entry, this should be combined with the boot ID in + _BOOT_ID=. + + + + + + + + See Also + + systemd1, + journalctl1, + journald.conf5, + sd-journal3, + coredumpctl1, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.kill.xml b/src/grp-system/systemd/systemd.kill.xml new file mode 100644 index 0000000000..13b7ab14df --- /dev/null +++ b/src/grp-system/systemd/systemd.kill.xml @@ -0,0 +1,189 @@ + + + + + + + + systemd.kill + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.kill + 5 + + + + systemd.kill + Process killing procedure + configuration + + + + service.service, + socket.socket, + mount.mount, + swap.swap, + scope.scope + + + + Description + + Unit configuration files for services, sockets, mount + points, swap devices and scopes share a subset of configuration + options which define the killing procedure of processes belonging + to the unit. + + This man page lists the configuration options shared by + these five unit types. See + systemd.unit5 + for the common options shared by all unit configuration files, and + systemd.service5, + systemd.socket5, + systemd.swap5, + systemd.mount5 + and + systemd.scope5 + for more information on the configuration file options specific to + each unit type. + + The kill procedure configuration options are configured in + the [Service], [Socket], [Mount] or [Swap] section, depending on + the unit type. + + + + Options + + + + + KillMode= + Specifies how processes of this unit shall be + killed. One of + , + , + , + . + + If set to , all remaining + processes in the control group of this unit will be killed on + unit stop (for services: after the stop command is executed, + as configured with ExecStop=). If set to + , only the main process itself is + killed. If set to , the + SIGTERM signal (see below) is sent to the + main process while the subsequent SIGKILL + signal (see below) is sent to all remaining processes of the + unit's control group. If set to , no + process is killed. In this case, only the stop command will be + executed on unit stop, but no process be killed otherwise. + Processes remaining alive after stop are left in their control + group and the control group continues to exist after stop + unless it is empty. + + Processes will first be terminated via + SIGTERM (unless the signal to send is + changed via KillSignal=). Optionally, this + is immediately followed by a SIGHUP (if + enabled with SendSIGHUP=). If then, after a + delay (configured via the TimeoutStopSec= + option), processes still remain, the termination request is + repeated with the SIGKILL signal (unless + this is disabled via the SendSIGKILL= + option). See + kill2 + for more information. + + Defaults to + . + + + + KillSignal= + Specifies which signal to use when killing a + service. This controls the signal that is sent as first step + of shutting down a unit (see above), and is usually followed + by SIGKILL (see above and below). For a + list of valid signals, see + signal7. + Defaults to SIGTERM. + + Note that, right after sending the signal specified in + this setting, systemd will always send + SIGCONT, to ensure that even suspended + tasks can be terminated cleanly. + + + + + SendSIGHUP= + Specifies whether to send + SIGHUP to remaining processes immediately + after sending the signal configured with + KillSignal=. This is useful to indicate to + shells and shell-like programs that their connection has been + severed. Takes a boolean value. Defaults to "no". + + + + + SendSIGKILL= + Specifies whether to send + SIGKILL to remaining processes after a + timeout, if the normal shutdown procedure left processes of + the service around. Takes a boolean value. Defaults to "yes". + + + + + + + + See Also + + systemd1, + systemctl1, + journalctl8, + systemd.unit5, + systemd.service5, + systemd.socket5, + systemd.swap5, + systemd.mount5, + systemd.exec5, + systemd.directives7, + kill2, + signal7 + + + + diff --git a/src/grp-system/systemd/systemd.link.xml b/src/grp-system/systemd/systemd.link.xml new file mode 100644 index 0000000000..d5b4d1038d --- /dev/null +++ b/src/grp-system/systemd/systemd.link.xml @@ -0,0 +1,477 @@ + + + + + + + + systemd.link + systemd + + + + Developer + Tom + Gundersen + + + + + + systemd.link + 5 + + + + systemd.link + Network device configuration + + + + link.link + + + + Description + + Network link configuration is performed by the + net_setup_link udev builtin. + + The link files are read from the files located in the system + network directory /usr/lib/systemd/network, + the volatile runtime network directory + /run/systemd/network, and the local + administration network directory + /etc/systemd/network. Link files must have + the extension .link; other extensions are + ignored. All link files are collectively sorted and processed in + lexical order, regardless of the directories in which they live. + However, files with identical filenames replace each other. Files + in /etc have the highest priority, files in + /run take precedence over files with the same + name in /usr/lib. This can be used to + override a system-supplied link file with a local file if needed. + As a special case, an empty file (file size 0) or symlink with the + same name pointing to /dev/null disables the + configuration file entirely (it is "masked"). + + The link file contains a [Match] section, + which determines if a given link file may be applied to a given + device, as well as a [Link] section specifying + how the device should be configured. The first (in lexical order) + of the link files that matches a given device is applied. Note + that a default file 99-default.link is + shipped by the system, any user-supplied + .link should hence have a lexically earlier + name to be considered at all. + + See + udevadm8 + for diagnosing problems with .link files. + + + + [Match] Section Options + + A link file is said to match a device if each of the entries + in the [Match] section matches, or if the + section is empty. The following keys are accepted: + + + + MACAddress= + + The hardware address. + + + + OriginalName= + + A whitespace-separated list of shell-style globs matching + the device name, as exposed by the udev property + "INTERFACE". This can not be used to match on names that have + already been changed from userspace. Caution is advised when matching on + kernel-assigned names, as they are known to be unstable + between reboots. + + + + Path= + + A whitespace-separated list of shell-style globs matching + the persistent path, as exposed by the udev property + ID_PATH. + + + + Driver= + + A whitespace-separated list of shell-style globs matching + the driver currently bound to the device, + as exposed by the udev property DRIVER + of its parent device, or if that is not set, the + driver as exposed by ethtool -i + of the device itself. + + + + Type= + + A whitespace-separated list of shell-style globs matching + the device type, as exposed by the udev + property DEVTYPE. + + + + Host= + + Matches against the hostname or machine + ID of the host. See ConditionHost= in + systemd.unit5 + for details. + + + + Virtualization= + + Checks whether the system is executed in + a virtualized environment and optionally test + whether it is a specific implementation. See + ConditionVirtualization= in + systemd.unit5 + for details. + + + + KernelCommandLine= + + Checks whether a specific kernel command line option + is set (or if prefixed with the exclamation mark unset). See + ConditionKernelCommandLine= in + systemd.unit5 + for details. + + + + Architecture= + + Checks whether the system is running on a specific + architecture. See ConditionArchitecture= + in + systemd.unit5 + for details. + + + + + + + + [Link] Section Options + + The [Link] section accepts the following + keys: + + + + Description= + + A description of the device. + + + + Alias= + + The ifalias is set to this + value. + + + + MACAddressPolicy= + + The policy by which the MAC address should be set. The + available policies are: + + + + + persistent + + If the hardware has a persistent MAC address, as + most hardware should, and if it is used by the kernel, + nothing is done. Otherwise, a new MAC address is + generated which is guaranteed to be the same on every + boot for the given machine and the given device, but + which is otherwise random. This feature depends on ID_NET_NAME_* + properties to exist for the link. On hardware where these + properties are not set, the generation of a persistent MAC address + will fail. + + + + random + + If the kernel is using a random MAC address, + nothing is done. Otherwise, a new address is randomly + generated each time the device appears, typically at + boot. Either way, the random address will have the + unicast and + locally administered bits set. + + + + none + + Keeps the MAC address assigned by the kernel. + + + + + + + MACAddress= + + The MAC address to use, if no + MACAddressPolicy= + is specified. + + + + NamePolicy= + + An ordered, space-separated list of policies by which + the interface name should be set. + NamePolicy may be disabled by specifying + net.ifnames=0 on the kernel command line. + Each of the policies may fail, and the first successful one + is used. The name is not set directly, but is exported to + udev as the property ID_NET_NAME, which + is, by default, used by a udev rule to set + NAME. If the name has already been set by + userspace, no renaming is performed. The available policies + are: + + + + kernel + + If the kernel claims that the name it has set + for a device is predictable, then no renaming is + performed. + + + + database + + The name is set based on entries in the udev's + Hardware Database with the key + ID_NET_NAME_FROM_DATABASE. + + + + + onboard + + The name is set based on information given by + the firmware for on-board devices, as exported by the + udev property ID_NET_NAME_ONBOARD. + + + + + slot + + The name is set based on information given by + the firmware for hot-plug devices, as exported by the + udev property ID_NET_NAME_SLOT. + + + + + path + + The name is set based on the device's physical + location, as exported by the udev property + ID_NET_NAME_PATH. + + + + mac + + The name is set based on the device's persistent + MAC address, as exported by the udev property + ID_NET_NAME_MAC. + + + + + + + Name= + + The interface name to use in case all the + policies specified in + NamePolicy= fail, or in case + NamePolicy= is missing or + disabled. + + + + MTUBytes= + + The maximum transmission unit in bytes to set for the + device. The usual suffixes K, M, G, are supported and are + understood to the base of 1024. + + + + BitsPerSecond= + + The speed to set for the device, the value is rounded + down to the nearest Mbps. The usual suffixes K, M, G, are + supported and are understood to the base of 1000. + + + + Duplex= + + The duplex mode to set for the device. The accepted + values are half and + full. + + + + WakeOnLan= + + The Wake-on-LAN policy to set for the device. The + supported values are: + + + + phy + + Wake on PHY activity. + + + + magic + + Wake on receipt of a magic packet. + + + + + off + + Never wake. + + + + + + + + + + Examples + + + /usr/lib/systemd/network/99-default.link + + The link file 99-default.link that is + shipped with systemd defines the default naming policy for + links. + + [Link] +NamePolicy=kernel database onboard slot path +MACAddressPolicy=persistent + + + + /etc/systemd/network/10-dmz.link + + This example assigns the fixed name + dmz0 to the interface with the MAC address + 00:a0:de:63:7a:e6: + + [Match] +MACAddress=00:a0:de:63:7a:e6 + +[Link] +Name=dmz0 + + + + /etc/systemd/network/10-internet.link + + This example assigns the fixed name + internet0 to the interface with the device + path pci-0000:00:1a.0-*: + + [Match] +Path=pci-0000:00:1a.0-* + +[Link] +Name=internet0 + + + + /etc/systemd/network/25-wireless.link + + Here's an overly complex example that shows the use of a large number of [Match] and [Link] settings. + + [Match] +MACAddress=12:34:56:78:9a:bc +Driver=brcmsmac +Path=pci-0000:02:00.0-* +Type=wlan +Virtualization=no +Host=my-laptop +Architecture=x86-64 + +[Link] +Name=wireless0 +MTUBytes=1450 +BitsPerSecond=10M +WakeOnLan=magic +MACAddress=cb:a9:87:65:43:21 + + + + + See Also + + + systemd-udevd.service8 + , + + udevadm8 + , + + systemd.netdev5 + , + + systemd.network5 + + + + + diff --git a/src/grp-system/systemd/systemd.mount.xml b/src/grp-system/systemd/systemd.mount.xml new file mode 100644 index 0000000000..66cddd72e0 --- /dev/null +++ b/src/grp-system/systemd/systemd.mount.xml @@ -0,0 +1,406 @@ + + + + + + + + systemd.mount + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.mount + 5 + + + + systemd.mount + Mount unit configuration + + + + mount.mount + + + + Description + + A unit configuration file whose name ends in + .mount encodes information about a file system + mount point controlled and supervised by systemd. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The mount specific configuration options are + configured in the [Mount] section. + + Additional options are listed in + systemd.exec5, + which define the execution environment the + mount8 + binary is executed in, and in + systemd.kill5, + which define the way the processes are terminated, and in + systemd.resource-control5, + which configure resource control settings for the processes of the + service. Note that the User= and Group= options are not + particularly useful for mount units specifying a + Type= option or using configuration not + specified in /etc/fstab; + mount8 + will refuse options that are not listed in + /etc/fstab if it is not run as UID 0. + + Mount units must be named after the mount point directories they control. Example: the mount point /home/lennart must be configured in a unit file home-lennart.mount. + For details about the escaping logic used to convert a file system path to a unit name, see + systemd.unit5. Note that mount + units cannot be templated, nor is possible to add multiple names to a mount unit by creating additional symlinks to + it. + + Optionally, a mount unit may be accompanied by an automount + unit, to allow on-demand or parallelized mounting. See + systemd.automount5. + + Mount points created at runtime (independently of unit files + or /etc/fstab) will be monitored by systemd + and appear like any other mount unit in systemd. See + /proc/self/mountinfo description in + proc5. + + + Some file systems have special semantics as API file systems + for kernel-to-userspace and userspace-to-userspace interfaces. Some + of them may not be changed via mount units, and cannot be + disabled. For a longer discussion see API + File Systems. + + + + Automatic Dependencies + + If a mount unit is beneath another mount unit in the file + system hierarchy, both a requirement dependency and an ordering + dependency between both units are created automatically. + + Block device backed file systems automatically gain + BindsTo= and After= type + dependencies on the device unit encapsulating the block + device (see below). + + If traditional file system quota is enabled for a mount + unit, automatic Wants= and + Before= dependencies on + systemd-quotacheck.service and + quotaon.service are added. + + For mount units with DefaultDependencies=yes in the [Unit] section (the + default) a couple additional dependencies are added. Mount units referring to local file systems automatically gain + an After= dependency on local-fs-pre.target. Network mount units + automatically acquire After= dependencies on remote-fs-pre.target, + network.target and network-online.target. Towards the latter a + Wants= unit is added as well. Mount units referring to local and network file systems are + distinguished by their file system type specification. In some cases this is not sufficient (for example network + block device based mounts, such as iSCSI), in which case may be added to the mount option + string of the unit, which forces systemd to consider the mount unit a network mount. Mount units (regardless if + local or network) also acquire automatic Before= and Conflicts= on + umount.target in order to be stopped during shutdown. + + Additional implicit dependencies may be added as result of + execution and resource control parameters as documented in + systemd.exec5 + and + systemd.resource-control5. + + + + <filename>fstab</filename> + + Mount units may either be configured via unit files, or via + /etc/fstab (see + fstab5 + for details). Mounts listed in /etc/fstab + will be converted into native units dynamically at boot and when + the configuration of the system manager is reloaded. In general, + configuring mount points through /etc/fstab + is the preferred approach. See + systemd-fstab-generator8 + for details about the conversion. + + The NFS mount option for NFS background mounts + as documented in nfs5 + is not supported in /etc/fstab entries. The systemd mount option + provides similar functionality and should be used instead. + + When reading /etc/fstab a few special + mount options are understood by systemd which influence how + dependencies are created for mount points. systemd will create a + dependency of type Wants= or + (see option + below), from either local-fs.target or + remote-fs.target, depending whether the file + system is local or remote. + + + + + + + Configures a Requires= and + an After= dependency between the created + mount unit and another systemd unit, such as a device or mount + unit. The argument should be a unit name, or an absolute path + to a device node or mount point. This option may be specified + more than once. This option is particularly useful for mount + point declarations that need an additional device to be around + (such as an external journal device for journal file systems) + or an additional mount to be in place (such as an overlay file + system that merges multiple mount points). See + After= and Requires= in + systemd.unit5 + for details. + + + + + + Configures a + RequiresMountsFor= dependency between the + created mount unit and other mount units. The argument must be + an absolute path. This option may be specified more than once. + See RequiresMountsFor= in + systemd.unit5 + for details. + + + + + + An automount unit will be created for the file + system. See + systemd.automount5 + for details. + + + + + + Configures the idle timeout of the + automount unit. See TimeoutIdleSec= in + systemd.automount5 + for details. + + + + + + Configure how long systemd should wait for a + device to show up before giving up on an entry from + /etc/fstab. Specify a time in seconds or + explicitly append a unit such as s, + min, h, + ms. + + Note that this option can only be used in + /etc/fstab, and will be + ignored when part of the Options= + setting in a unit file. + + + + + + + + With , this mount will + not be added as a dependency for + local-fs.target or + remote-fs.target. This means that it will + not be mounted automatically during boot, unless it is pulled + in by some other unit. The option has the + opposite meaning and is the default. + + + + + + + With , this mount will + be only wanted, not required, by + local-fs.target or + remote-fs.target. This means that the + boot will continue even if this mount point is not mounted + successfully. + + + + + + + An additional filesystem to be mounted in the + initramfs. See initrd-fs.target + description in + systemd.special7. + + + + + If a mount point is configured in both + /etc/fstab and a unit file that is stored + below /usr, the former will take precedence. + If the unit file is stored below /etc, it + will take precedence. This means: native unit files take + precedence over traditional configuration files, but this is + superseded by the rule that configuration in + /etc will always take precedence over + configuration in /usr. + + + + Options + + Mount files must include a [Mount] section, which carries + information about the file system mount points it supervises. A + number of options that may be used in this section are shared with + other unit types. These options are documented in + systemd.exec5 + and + systemd.kill5. + The options specific to the [Mount] section of mount units are the + following: + + + + + What= + Takes an absolute path of a device node, file + or other resource to mount. See + mount8 + for details. If this refers to a device node, a dependency on + the respective device unit is automatically created. (See + systemd.device5 + for more information.) This option is + mandatory. + + + + Where= + Takes an absolute path of a directory of the + mount point. If the mount point does not exist at the time of + mounting, it is created. This string must be reflected in the + unit filename. (See above.) This option is + mandatory. + + + + Type= + Takes a string for the file system type. See + mount8 + for details. This setting is optional. + + + + Options= + + Mount options to use when mounting. This takes + a comma-separated list of options. This setting is + optional. + + + + SloppyOptions= + + Takes a boolean argument. If true, parsing of + the options specified in Options= is + relaxed, and unknown mount options are tolerated. This + corresponds with + mount8's + -s switch. Defaults to + off. + + + + DirectoryMode= + Directories of mount points (and any parent + directories) are automatically created if needed. This option + specifies the file system access mode used when creating these + directories. Takes an access mode in octal notation. Defaults + to 0755. + + + + TimeoutSec= + Configures the time to wait for the mount + command to finish. If a command does not exit within the + configured time, the mount will be considered failed and be + shut down again. All commands still running will be terminated + forcibly via SIGTERM, and after another + delay of this time with SIGKILL. (See + in + systemd.kill5.) + Takes a unit-less value in seconds, or a time span value such + as "5min 20s". Pass 0 to disable the timeout logic. The + default value is set from the manager configuration file's + DefaultTimeoutStart= + variable. + + + + Check + systemd.exec5 + and + systemd.kill5 + for more settings. + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.exec5, + systemd.kill5, + systemd.resource-control5, + systemd.service5, + systemd.device5, + proc5, + mount8, + systemd-fstab-generator8, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.netdev.xml b/src/grp-system/systemd/systemd.netdev.xml new file mode 100644 index 0000000000..a5c6f0fa40 --- /dev/null +++ b/src/grp-system/systemd/systemd.netdev.xml @@ -0,0 +1,1164 @@ + + + + + + + + + systemd.network + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd.netdev + 5 + + + + systemd.netdev + Virtual Network Device configuration + + + + netdev.netdev + + + + Description + + Network setup is performed by + systemd-networkd8. + + + Virtual Network Device files must have the extension + .netdev; other extensions are ignored. + Virtual network devices are created as soon as networkd is + started. If a netdev with the specified name already exists, + networkd will use that as-is rather than create its own. Note that + the settings of the pre-existing netdev will not be changed by + networkd. + + The .netdev files are read from the + files located in the system network directory + /usr/lib/systemd/network, the volatile + runtime network directory + /run/systemd/network and the local + administration network directory + /etc/systemd/network. All configuration files + are collectively sorted and processed in lexical order, regardless + of the directories in which they live. However, files with + identical filenames replace each other. Files in + /etc have the highest priority, files in + /run take precedence over files with the same + name in /usr/lib. This can be used to + override a system-supplied configuration file with a local file if + needed. As a special case, an empty file (file size 0) or symlink + with the same name pointing to /dev/null + disables the configuration file entirely (it is "masked"). + + + + Supported netdev kinds + + The following kinds of virtual network devices may be + configured in .netdev files: + + + Supported kinds of virtual network devices + + + + + + Kind + Description + + + bond + A bond device is an aggregation of all its slave devices. See Linux Ethernet Bonding Driver HOWTO for details.Local configuration + + bridge + A bridge device is a software switch, and each of its slave devices and the bridge itself are ports of the switch. + + dummy + A dummy device drops all packets sent to it. + + gre + A Level 3 GRE tunnel over IPv4. See RFC 2784 for details. + + gretap + A Level 2 GRE tunnel over IPv4. + + ip6gre + A Level 3 GRE tunnel over IPv6. + + ip6tnl + An IPv4 or IPv6 tunnel over IPv6 + + ip6gretap + A Level 2 GRE tunnel over IPv6. + + ipip + An IPv4 over IPv4 tunnel. + + ipvlan + An ipvlan device is a stacked device which receives packets from its underlying device based on IP address filtering. + + macvlan + A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering. + + macvtap + A macvtap device is a stacked device which receives packets from its underlying device based on MAC address filtering. + + sit + An IPv6 over IPv4 tunnel. + + tap + A persistent Level 2 tunnel between a network device and a device node. + + tun + A persistent Level 3 tunnel between a network device and a device node. + + veth + An Ethernet tunnel between a pair of network devices. + + vlan + A VLAN is a stacked device which receives packets from its underlying device based on VLAN tagging. See IEEE 802.1Q for details. + + vti + An IPv4 over IPSec tunnel. + + vti6 + An IPv6 over IPSec tunnel. + + vxlan + A virtual extensible LAN (vxlan), for connecting Cloud computing deployments. + + vrf + A Virtual Routing and Forwarding (VRF) interface to create separate routing and forwarding domains. + + + +
+ +
+ + + [Match] Section Options + + A virtual network device is only created if the + [Match] section matches the current + environment, or if the section is empty. The following keys are + accepted: + + + + Host= + + Matches against the hostname or machine ID of the + host. See ConditionHost= in + systemd.unit5 + for details. + + + + + Virtualization= + + Checks whether the system is executed in a virtualized + environment and optionally test whether it is a specific + implementation. See + ConditionVirtualization= in + systemd.unit5 + for details. + + + + + KernelCommandLine= + + Checks whether a specific kernel command line option + is set (or if prefixed with the exclamation mark unset). See + ConditionKernelCommandLine= in + systemd.unit5 + for details. + + + + + Architecture= + + Checks whether the system is running on a specific + architecture. See ConditionArchitecture= in + systemd.unit5 + for details. + + + + + + + + + [NetDev] Section Options + + The [NetDev] section accepts the + following keys: + + + + Description= + + A free-form description of the netdev. + + + + Name= + + The interface name used when creating the netdev. + This option is compulsory. + + + + Kind= + + The netdev kind. This option is compulsory. See the + Supported netdev kinds section for the + valid keys. + + + + MTUBytes= + + The maximum transmission unit in bytes to set for + the device. The usual suffixes K, M, G, are supported and + are understood to the base of 1024. This key is not + currently supported for tun or + tap devices. + + + + + MACAddress= + + The MAC address to use for the device. If none is + given, one is generated based on the interface name and + the + machine-id5. + This key is not currently supported for + tun or tap devices. + + + + + + + + [Bridge] Section Options + + The [Bridge] section only applies for + netdevs of kind bridge, and accepts the + following keys: + + + + HelloTimeSec= + + HelloTimeSec specifies the number of seconds between two hello packets + sent out by the root bridge and the designated bridges. Hello packets are + used to communicate information about the topology throughout the entire + bridged local area network. + + + + MaxAgeSec= + + MaxAgeSec specifies the number of seconds of maximum message age. + If the last seen (received) hello packet is more than this number of + seconds old, the bridge in question will start the takeover procedure + in attempt to become the Root Bridge itself. + + + + ForwardDelaySec= + + ForwardDelaySec specifies the number of seconds spent in each + of the Listening and Learning states before the Forwarding state is entered. + + + + MulticastQuerier= + + A boolean. This setting controls the IFLA_BR_MCAST_QUERIER option in the kernel. + If enabled, the kernel will send general ICMP queries from a zero source address. + This feature should allow faster convergence on startup, but it causes some + multicast-aware switches to misbehave and disrupt forwarding of multicast packets. + When unset, the kernel's default setting applies. + + + + + MulticastSnooping= + + A boolean. This setting controls the IFLA_BR_MCAST_SNOOPING option in the kernel. + If enabled, IGMP snooping monitors the Internet Group Management Protocol (IGMP) traffic + between hosts and multicast routers. When unset, the kernel's default setting applies. + + + + + VLANFiltering= + + A boolean. This setting controls the IFLA_BR_VLAN_FILTERING option in the kernel. + If enabled, the bridge will be started in VLAN-filtering mode. When unset, the kernel's + default setting applies. + + + + + + + + + [VLAN] Section Options + + The [VLAN] section only applies for + netdevs of kind vlan, and accepts the + following key: + + + + Id= + + The VLAN ID to use. An integer in the range 0–4094. + This option is compulsory. + + + + + + + + [MACVLAN] Section Options + + The [MACVLAN] section only applies for + netdevs of kind macvlan, and accepts the + following key: + + + + Mode= + + The MACVLAN mode to use. The supported options are + private, + vepa, + bridge, and + passthru. + + + + + + + + + [MACVTAP] Section Options + + The [MACVTAP] section applies for + netdevs of kind macvtap and accepts the + same key as [MACVLAN]. + + + + + [IPVLAN] Section Options + + The [IPVLAN] section only applies for + netdevs of kind ipvlan, and accepts the + following key: + + + + Mode= + + The IPVLAN mode to use. The supported options are + L2 and L3. + + + + + + + + + [VXLAN] Section Options + The [VXLAN] section only applies for + netdevs of kind vxlan, and accepts the + following keys: + + + + Id= + + The VXLAN ID to use. + + + + Group= + + An assigned multicast group IP address. + + + + TOS= + + The Type Of Service byte value for a vxlan interface. + + + + TTL= + + A fixed Time To Live N on Virtual eXtensible Local + Area Network packets. N is a number in the range 1–255. 0 + is a special value meaning that packets inherit the TTL + value. + + + + MacLearning= + + A boolean. When true, enables dynamic MAC learning + to discover remote MAC addresses. + + + + FDBAgeingSec= + + The lifetime of Forwarding Database entry learnt by + the kernel, in seconds. + + + + MaximumFDBEntries= + + Configures maximum number of FDB entries. + + + + ARPProxy= + + A boolean. When true, enables ARP proxying. + + + + L2MissNotification= + + A boolean. When true, enables netlink LLADDR miss + notifications. + + + + L3MissNotification= + + A boolean. When true, enables netlink IP address miss + notifications. + + + + RouteShortCircuit= + + A boolean. When true, route short circuiting is turned + on. + + + + UDPCheckSum= + + A boolean. When true, transmitting UDP checksums when doing VXLAN/IPv4 is turned on. + + + + UDP6ZeroChecksumTx= + + A boolean. When true, sending zero checksums in VXLAN/IPv6 is turned on. + + + + UDP6ZeroCheckSumRx= + + A boolean. When true, receiving zero checksums in VXLAN/IPv6 is turned on. + + + + GroupPolicyExtension= + + A boolean. When true, it enables Group Policy VXLAN extension security label mechanism + across network peers based on VXLAN. For details about the Group Policy VXLAN, see the + + VXLAN Group Policy document. Defaults to false. + + + + DestinationPort= + + Configures the default destination UDP port on a per-device basis. + If destination port is not specified then Linux kernel default will be used. + Set destination port 4789 to get the IANA assigned value, + and destination port 0 to get default values. + + + + PortRange= + + Configures VXLAN port range. VXLAN bases source + UDP port based on flow to help the receiver to be able + to load balance based on outer header flow. It + restricts the port range to the normal UDP local + ports, and allows overriding via configuration. + + + + + + [Tunnel] Section Options + + The [Tunnel] section only applies for + netdevs of kind + ipip, + sit, + gre, + gretap, + ip6gre, + ip6gretap, + vti, + vti6, and + ip6tnl and accepts + the following keys: + + + + Local= + + A static local address for tunneled packets. It must + be an address on another interface of this host. + + + + Remote= + + The remote endpoint of the tunnel. + + + + TOS= + + The Type Of Service byte value for a tunnel interface. + For details about the TOS, see the + Type of + Service in the Internet Protocol Suite document. + + + + + TTL= + + A fixed Time To Live N on tunneled packets. N is a + number in the range 1–255. 0 is a special value meaning that + packets inherit the TTL value. The default value for IPv4 + tunnels is: inherit. The default value for IPv6 tunnels is + 64. + + + + DiscoverPathMTU= + + A boolean. When true, enables Path MTU Discovery on + the tunnel. + + + + IPv6FlowLabel= + + Configures the 20-bit flow label (see + RFC 6437) field in the IPv6 header (see + RFC 2460), which is used by a node to label packets of a flow. + It is only used for IPv6 tunnels. + A flow label of zero is used to indicate packets that have + not been labeled. + It can be configured to a value in the range 0–0xFFFFF, or be + set to inherit, in which case the original flowlabel is used. + + + + CopyDSCP= + + A boolean. When true, the Differentiated Service Code + Point (DSCP) field will be copied to the inner header from + outer header during the decapsulation of an IPv6 tunnel + packet. DSCP is a field in an IP packet that enables different + levels of service to be assigned to network traffic. + Defaults to no. + + + + + EncapsulationLimit= + + The Tunnel Encapsulation Limit option specifies how many additional + levels of encapsulation are permitted to be prepended to the packet. + For example, a Tunnel Encapsulation Limit option containing a limit + value of zero means that a packet carrying that option may not enter + another tunnel before exiting the current tunnel. + (see RFC 2473). + The valid range is 0–255 and none. Defaults to 4. + + + + + Key= + + The Key= parameter specifies the same key to use in + both directions (InputKey= and OutputKey=). + The Key= is either a number or an IPv4 address-like dotted quad. + It is used as mark-configured SAD/SPD entry as part of the lookup key (both in data + and control path) in ip xfrm (framework used to implement IPsec protocol). + See + ip-xfrm — transform configuration for details. It is only used for VTI/VTI6 + tunnels. + + + + InputKey= + + The InputKey= parameter specifies the key to use for input. + The format is same as Key=. It is only used for VTI/VTI6 tunnels. + + + + OutputKey= + + The OutputKey= parameter specifies the key to use for output. + The format is same as Key=. It is only used for VTI/VTI6 tunnels. + + + + Mode= + + An ip6tnl tunnel can be in one of three + modes + ip6ip6 for IPv6 over IPv6, + ipip6 for IPv4 over IPv6 or + any for either. + + + + + + + [Peer] Section Options + + The [Peer] section only applies for + netdevs of kind veth and accepts the + following keys: + + + + Name= + + The interface name used when creating the netdev. + This option is compulsory. + + + + MACAddress= + + The peer MACAddress, if not set, it is generated in + the same way as the MAC address of the main + interface. + + + + + + [Tun] Section Options + + The [Tun] section only applies for + netdevs of kind tun, and accepts the following + keys: + + + + OneQueue= + Takes a boolean argument. Configures whether + all packets are queued at the device (enabled), or a fixed + number of packets are queued at the device and the rest at the + qdisc. Defaults to + no. + + + + MultiQueue= + Takes a boolean argument. Configures whether + to use multiple file descriptors (queues) to parallelize + packets sending and receiving. Defaults to + no. + + + + PacketInfo= + Takes a boolean argument. Configures whether + packets should be prepended with four extra bytes (two flag + bytes and two protocol bytes). If disabled, it indicates that + the packets will be pure IP packets. Defaults to + no. + + + + VNetHeader= + Takes a boolean argument. Configures + IFF_VNET_HDR flag for a tap device. It allows sending + and receiving larger Generic Segmentation Offload (GSO) + packets. This may increase throughput significantly. + Defaults to + no. + + + + User= + User to grant access to the + /dev/net/tun device. + + + + Group= + Group to grant access to the + /dev/net/tun device. + + + + + + + + + [Tap] Section Options + + The [Tap] section only applies for + netdevs of kind tap, and accepts the same keys + as the [Tun] section. + + + + [Bond] Section Options + + The [Bond] section accepts the following + key: + + + + Mode= + + Specifies one of the bonding policies. The default is + balance-rr (round robin). Possible values are + balance-rr, + active-backup, + balance-xor, + broadcast, + 802.3ad, + balance-tlb, and + balance-alb. + + + + + + TransmitHashPolicy= + + Selects the transmit hash policy to use for slave + selection in balance-xor, 802.3ad, and tlb modes. Possible + values are + layer2, + layer3+4, + layer2+3, + encap2+3, and + encap3+4. + + + + + + LACPTransmitRate= + + Specifies the rate with which link partner transmits + Link Aggregation Control Protocol Data Unit packets in + 802.3ad mode. Possible values are slow, + which requests partner to transmit LACPDUs every 30 seconds, + and fast, which requests partner to + transmit LACPDUs every second. The default value is + slow. + + + + + MIIMonitorSec= + + Specifies the frequency that Media Independent + Interface link monitoring will occur. A value of zero + disables MII link monitoring. This value is rounded down to + the nearest millisecond. The default value is 0. + + + + + UpDelaySec= + + Specifies the delay before a link is enabled after a + link up status has been detected. This value is rounded down + to a multiple of MIIMonitorSec. The default value is + 0. + + + + + DownDelaySec= + + Specifies the delay before a link is disabled after a + link down status has been detected. This value is rounded + down to a multiple of MIIMonitorSec. The default value is + 0. + + + + + LearnPacketIntervalSec= + + Specifies the number of seconds between instances where the bonding + driver sends learning packets to each slave peer switch. + The valid range is 1–0x7fffffff; the default value is 1. This option + has an effect only for the balance-tlb and balance-alb modes. + + + + + AdSelect= + + Specifies the 802.3ad aggregation selection logic to use. Possible values are + stable, + bandwidth and + count. + + + + + + FailOverMACPolicy= + + Specifies whether the active-backup mode should set all slaves to + the same MAC address at the time of enslavement or, when enabled, to perform special handling of the + bond's MAC address in accordance with the selected policy. The default policy is none. + Possible values are + none, + active and + follow. + + + + + + ARPValidate= + + Specifies whether or not ARP probes and replies should be + validated in any mode that supports ARP monitoring, or whether + non-ARP traffic should be filtered (disregarded) for link + monitoring purposes. Possible values are + none, + active, + backup and + all. + + + + + + ARPIntervalSec= + + Specifies the ARP link monitoring frequency in milliseconds. + A value of 0 disables ARP monitoring. The default value is 0. + + + + + + ARPIPTargets= + + Specifies the IP addresses to use as ARP monitoring peers when + ARPIntervalSec is greater than 0. These are the targets of the ARP request + sent to determine the health of the link to the targets. + Specify these values in IPv4 dotted decimal format. At least one IP + address must be given for ARP monitoring to function. The + maximum number of targets that can be specified is 16. The + default value is no IP addresses. + + + + + + ARPAllTargets= + + Specifies the quantity of ARPIPTargets that must be reachable + in order for the ARP monitor to consider a slave as being up. + This option affects only active-backup mode for slaves with + ARPValidate enabled. Possible values are + any and + all. + + + + + + PrimaryReselectPolicy= + + Specifies the reselection policy for the primary slave. This + affects how the primary slave is chosen to become the active slave + when failure of the active slave or recovery of the primary slave + occurs. This option is designed to prevent flip-flopping between + the primary slave and other slaves. Possible values are + always, + better and + failure. + + + + + + ResendIGMP= + + Specifies the number of IGMP membership reports to be issued after + a failover event. One membership report is issued immediately after + the failover, subsequent packets are sent in each 200ms interval. + The valid range is 0–255. Defaults to 1. A value of 0 + prevents the IGMP membership report from being issued in response + to the failover event. + + + + + + PacketsPerSlave= + + Specify the number of packets to transmit through a slave before + moving to the next one. When set to 0, then a slave is chosen at + random. The valid range is 0–65535. Defaults to 1. This option + only has effect when in balance-rr mode. + + + + + + GratuitousARP= + + Specify the number of peer notifications (gratuitous ARPs and + unsolicited IPv6 Neighbor Advertisements) to be issued after a + failover event. As soon as the link is up on the new slave, + a peer notification is sent on the bonding device and each + VLAN sub-device. This is repeated at each link monitor interval + (ARPIntervalSec or MIIMonitorSec, whichever is active) if the number is + greater than 1. The valid range is 0–255. The default value is 1. + These options affect only the active-backup mode. + + + + + + AllSlavesActive= + + A boolean. Specifies that duplicate frames (received on inactive ports) + should be dropped when false, or delivered when true. Normally, bonding will drop + duplicate frames (received on inactive ports), which is desirable for + most users. But there are some times it is nice to allow duplicate + frames to be delivered. The default value is false (drop duplicate frames + received on inactive ports). + + + + + + MinLinks= + + Specifies the minimum number of links that must be active before + asserting carrier. The default value is 0. + + + + + + + For more detail information see + + Linux Ethernet Bonding Driver HOWTO + + + + + Example + + /etc/systemd/network/25-bridge.netdev + + [NetDev] +Name=bridge0 +Kind=bridge + + + + /etc/systemd/network/25-vlan1.netdev + + [Match] +Virtualization=no + +[NetDev] +Name=vlan1 +Kind=vlan + +[VLAN] +Id=1 + + + /etc/systemd/network/25-ipip.netdev + [NetDev] +Name=ipip-tun +Kind=ipip +MTUBytes=1480 + +[Tunnel] +Local=192.168.223.238 +Remote=192.169.224.239 +TTL=64 + + + /etc/systemd/network/25-tap.netdev + [NetDev] +Name=tap-test +Kind=tap + +[Tap] +MultiQueue=true +PacketInfo=true + + + /etc/systemd/network/25-sit.netdev + [NetDev] +Name=sit-tun +Kind=sit +MTUBytes=1480 + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 + + + + /etc/systemd/network/25-gre.netdev + [NetDev] +Name=gre-tun +Kind=gre +MTUBytes=1480 + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 + + + + /etc/systemd/network/25-vti.netdev + + [NetDev] +Name=vti-tun +Kind=vti +MTUBytes=1480 + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 + + + + /etc/systemd/network/25-veth.netdev + [NetDev] +Name=veth-test +Kind=veth + +[Peer] +Name=veth-peer + + + + /etc/systemd/network/25-bond.netdev + [NetDev] +Name=bond1 +Kind=bond + +[Bond] +Mode=802.3ad +TransmitHashPolicy=layer3+4 +MIIMonitorSec=1s +LACPTransmitRate=fast + + + + + /etc/systemd/network/25-dummy.netdev + [NetDev] +Name=dummy-test +Kind=dummy +MACAddress=12:34:56:78:9a:bc + + + /etc/systemd/network/25-vrf.netdev + Create a VRF interface with table 42. + [NetDev] +Name=vrf-test +Kind=vrf + +[VRF] +TableId=42 + + + + See Also + + systemd1, + systemd-networkd8, + systemd.link5, + systemd.network5 + + + +
diff --git a/src/grp-system/systemd/systemd.network.xml b/src/grp-system/systemd/systemd.network.xml new file mode 100644 index 0000000000..4541a55490 --- /dev/null +++ b/src/grp-system/systemd/systemd.network.xml @@ -0,0 +1,1312 @@ + + + + + + + + + systemd.network + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd.network + 5 + + + + systemd.network + Network configuration + + + + network.network + + + + Description + + Network setup is performed by + systemd-networkd8. + + + Network files must have the extension + .network; other extensions are ignored. + Networks are applied to links whenever the links appear. + + The .network files are read from the + files located in the system network directory + /usr/lib/systemd/network, the volatile + runtime network directory + /run/systemd/network and the local + administration network directory + /etc/systemd/network. All configuration files + are collectively sorted and processed in lexical order, regardless + of the directories in which they live. However, files with + identical filenames replace each other. Files in + /etc have the highest priority, files in + /run take precedence over files with the same + name in /usr/lib. This can be used to + override a system-supplied configuration file with a local file if + needed. As a special case, an empty file (file size 0) or symlink + with the same name pointing to /dev/null + disables the configuration file entirely (it is "masked"). + + Note that an interface without any static IPv6 addresses configured, and neither DHCPv6 nor IPv6LL enabled, + shall be considered to have no IPv6 support. IPv6 will be automatically disabled for that interface by writing "1" + to /proc/sys/net/ipv6/conf/ifname/disable_ipv6. + + + + + [Match] Section Options + + The network file contains a [Match] + section, which determines if a given network file may be applied + to a given device; and a [Network] section + specifying how the device should be configured. The first (in + lexical order) of the network files that matches a given device + is applied, all later files are ignored, even if they match as + well. + + A network file is said to match a device if each of the + entries in the [Match] section matches, or if + the section is empty. The following keys are accepted: + + + + MACAddress= + + The hardware address of the interface (use full colon-delimited hexadecimal, e.g., + 01:23:45:67:89:ab). + + + + Path= + + A whitespace-separated list of shell-style globs + matching the persistent path, as exposed by the udev + property ID_PATH. + + + + Driver= + + A whitespace-separated list of shell-style globs + matching the driver currently bound to the device, as + exposed by the udev property DRIVER + of its parent device, or if that is not set the driver + as exposed by ethtool -i of the + device itself. + + + + Type= + + A whitespace-separated list of shell-style globs + matching the device type, as exposed by the udev property + DEVTYPE. + + + + Name= + + A whitespace-separated list of shell-style globs + matching the device name, as exposed by the udev property + INTERFACE. + + + + Host= + + Matches against the hostname or machine ID of the + host. See ConditionHost= in + systemd.unit5 + for details. + + + + + Virtualization= + + Checks whether the system is executed in a virtualized + environment and optionally test whether it is a specific + implementation. See ConditionVirtualization= in + systemd.unit5 + for details. + + + + + KernelCommandLine= + + Checks whether a specific kernel command line option is + set (or if prefixed with the exclamation mark unset). See + ConditionKernelCommandLine= in + systemd.unit5 + for details. + + + + + Architecture= + + Checks whether the system is running on a specific + architecture. See ConditionArchitecture= in + systemd.unit5 + for details. + + + + + + + + + [Link] Section Options + + The [Link] section accepts the following keys: + + + + MACAddress= + + The hardware address to set for the device. + + + + MTUBytes= + + The maximum transmission unit in bytes to set for the + device. The usual suffixes K, M, G, are supported and are + understood to the base of 1024. + Note that if IPv6 is enabled on the interface, and the MTU is chosen + below 1280 (the minimum MTU for IPv6) it will automatically be increased to this value. + + + + + + + [Network] Section Options + + The [Network] section accepts the following keys: + + + + Description= + + A description of the device. This is only used for + presentation purposes. + + + + DHCP= + + Enables DHCPv4 and/or DHCPv6 client support. Accepts + yes, no, + ipv4, or ipv6. + + Note that DHCPv6 will by default be triggered by Router + Advertisement, if that is enabled, regardless of this parameter. + By enabling DHCPv6 support explicitly, the DHCPv6 client will + be started regardless of the presence of routers on the link, + or what flags the routers pass. See + IPv6AcceptRA=. + + Furthermore, note that by default the domain name + specified through DHCP is not used for name resolution. + See option below. + + See the [DHCP] section below for further configuration options for the DHCP client + support. + + + + DHCPServer= + + A boolean. Enables DHCPv4 server support. Defaults + to no. Further settings for the DHCP + server may be set in the [DHCPServer] + section described below. + + + + LinkLocalAddressing= + + Enables link-local address autoconfiguration. Accepts + yes, no, + ipv4, or ipv6. Defaults to + ipv6. + + + + IPv4LLRoute= + + A boolean. When true, sets up the route needed for + non-IPv4LL hosts to communicate with IPv4LL-only hosts. Defaults + to false. + + + + + IPv6Token= + + An IPv6 address with the top 64 bits unset. When set, indicates the + 64-bit interface part of SLAAC IPv6 addresses for this link. Note that + the token is only ever used for SLAAC, and not for DHCPv6 addresses, even + in the case DHCP is requested by router advertisement. By default, the + token is autogenerated. + + + + LLMNR= + + A boolean or resolve. When true, + enables Link-Local + Multicast Name Resolution on the link. When set to + resolve, only resolution is enabled, + but not host registration and announcement. Defaults to + true. This setting is read by + systemd-resolved.service8. + + + + MulticastDNS= + + A boolean or resolve. When true, + enables Multicast + DNS support on the link. When set to + resolve, only resolution is enabled, + but not host or service registration and + announcement. Defaults to false. This setting is read by + systemd-resolved.service8. + + + + DNSSEC= + + A boolean or + allow-downgrade. When true, enables + DNSSEC + DNS validation support on the link. When set to + allow-downgrade, compatibility with + non-DNSSEC capable networks is increased, by automatically + turning off DNSEC in this case. This option defines a + per-interface setting for + resolved.conf5's + global DNSSEC= option. Defaults to + false. This setting is read by + systemd-resolved.service8. + + + + DNSSECNegativeTrustAnchors= + A space-separated list of DNSSEC negative + trust anchor domains. If specified and DNSSEC is enabled, + look-ups done via the interface's DNS server will be subject + to the list of negative trust anchors, and not require + authentication for the specified domains, or anything below + it. Use this to disable DNSSEC authentication for specific + private domains, that cannot be proven valid using the + Internet DNS hierarchy. Defaults to the empty list. This + setting is read by + systemd-resolved.service8. + + + + LLDP= + + Controls support for Ethernet LLDP packet reception. LLDP is a link-layer protocol commonly + implemented on professional routers and bridges which announces which physical port a system is connected + to, as well as other related data. Accepts a boolean or the special value + routers-only. When true, incoming LLDP packets are accepted and a database of all LLDP + neighbors maintained. If routers-only is set only LLDP data of various types of routers + is collected and LLDP data about other types of devices ignored (such as stations, telephones and + others). If false, LLDP reception is disabled. Defaults to routers-only. Use + networkctl1 to query the + collected neighbor data. LLDP is only available on Ethernet links. See EmitLLDP= below + for enabling LLDP packet emission from the local system. + + + + + EmitLLDP= + + Controls support for Ethernet LLDP packet emission. Accepts a boolean parameter or the special values + nearest-bridge, non-tpmr-bridge and + customer-bridge. Defaults to false, which turns off LLDP packet emission. If not false, + a short LLDP packet with information about the local system is sent out in regular intervals on the + link. The LLDP packet will contain information about the local host name, the local machine ID (as stored + in machine-id5) and the + local interface name, as well as the pretty hostname of the system (as set in + machine-info5). LLDP + emission is only available on Ethernet links. Note that this setting passes data suitable for + identification of host to the network and should thus not be enabled on untrusted networks, where such + identification data should not be made available. Use this option to permit other systems to identify on + which interfaces they are connected to this system. The three special values control propagation of the + LLDP packets. The nearest-bridge setting permits propagation only to the nearest + connected bridge, non-tpmr-bridge permits propagation across Two-Port MAC Relays, but + not any other bridges, and customer-bridge permits propagation until a customer bridge + is reached. For details about these concepts, see IEEE 802.1AB-2009. Note that + configuring this setting to true is equivalent to nearest-bridge, the recommended and + most restricted level of propagation. See LLDP= above for an option to enable LLDP + reception. + + + + BindCarrier= + + A link name or a list of link names. When set, controls the behavior of the current + link. When all links in the list are in an operational down state, the current link is brought + down. When at least one link has carrier, the current interface is brought up. + + + + + Address= + + A static IPv4 or IPv6 address and its prefix length, + separated by a / character. Specify + this key more than once to configure several addresses. + The format of the address must be as described in + inet_pton3. + This is a short-hand for an [Address] section only + containing an Address key (see below). This option may be + specified more than once. + + + If the specified address is 0.0.0.0 (for IPv4) or + [::] (for IPv6), a new address range of the requested size + is automatically allocated from a system-wide pool of + unused ranges. The allocated range is checked against all + current network interfaces and all known network + configuration files to avoid address range conflicts. The + default system-wide pool consists of 192.168.0.0/16, + 172.16.0.0/12 and 10.0.0.0/8 for IPv4, and fc00::/7 for + IPv6. This functionality is useful to manage a large + number of dynamically created network interfaces with the + same network configuration and automatic address range + assignment. + + + + + Gateway= + + The gateway address, which must be in the format + described in + inet_pton3. + This is a short-hand for a [Route] section only containing + a Gateway key. This option may be specified more than + once. + + + + DNS= + + A DNS server address, which must be in the format + described in + inet_pton3. + This option may be specified more than once. This setting is read by + systemd-resolved.service8. + + + + Domains= + + The domains used for DNS host name resolution on this link. Takes a list of DNS domain names which + are used as search suffixes for extending single-label host names (host names containing no dots) to become + fully qualified domain names (FQDNs). If a single-label host name is resolved on this interface, each of + the specified search domains are appended to it in turn, converting it into a fully qualified domain name, + until one of them may be successfully resolved. + + The specified domains are also used for routing of DNS queries: look-ups for host names ending in the + domains specified here are preferably routed to the DNS servers configured for this interface. If a domain + name is prefixed with ~, the domain name becomes a pure "routing" domain, is used for + DNS query routing purposes only and is not used in the described domain search logic. By specifying a + routing domain of ~. (the tilde indicating definition of a routing domain, the dot + referring to the DNS root domain which is the implied suffix of all valid DNS names) it is possible to + route all DNS traffic preferably to the DNS server specified for this interface. The route domain logic is + particularly useful on multi-homed hosts with DNS servers serving particular private DNS zones on each + interface. + + This setting is read by + systemd-resolved.service8. + + + + NTP= + + An NTP server address. This option may be specified more than once. This setting is read by + systemd-timesyncd.service8. + + + + IPForward= + Configures IP packet forwarding for the + system. If enabled, incoming packets on any network + interface will be forwarded to any other interfaces + according to the routing table. Takes either a boolean + argument, or the values ipv4 or + ipv6, which only enable IP packet + forwarding for the specified address family. This controls + the net.ipv4.ip_forward and + net.ipv6.conf.all.forwarding sysctl + options of the network interface (see ip-sysctl.txt + for details about sysctl options). Defaults to + no. + + Note: this setting controls a global kernel option, + and does so one way only: if a network that has this setting + enabled is set up the global setting is turned on. However, + it is never turned off again, even after all networks with + this setting enabled are shut down again. + + To allow IP packet forwarding only between specific + network interfaces use a firewall. + + + + IPMasquerade= + Configures IP masquerading for the network + interface. If enabled, packets forwarded from the network + interface will be appear as coming from the local host. + Takes a boolean argument. Implies + IPForward=ipv4. Defaults to + no. + + + IPv6PrivacyExtensions= + Configures use of stateless temporary + addresses that change over time (see RFC 4941, + Privacy Extensions for Stateless Address Autoconfiguration + in IPv6). Takes a boolean or the special values + prefer-public and + kernel. When true, enables the privacy + extensions and prefers temporary addresses over public + addresses. When prefer-public, enables the + privacy extensions, but prefers public addresses over + temporary addresses. When false, the privacy extensions + remain disabled. When kernel, the kernel's + default setting will be left in place. Defaults to + no. + + + IPv6AcceptRA= + Enable or disable IPv6 Router Advertisement (RA) reception support for the interface. Takes + a boolean parameter. If true, RAs are accepted; if false, RAs are ignored, independently of the local + forwarding state. When not set, the kernel default is used, and RAs are accepted only when local forwarding + is disabled for that interface. When RAs are accepted, they may trigger the start of the DHCPv6 client if + the relevant flags are set in the RA data, or if no routers are found on the link. + + Further settings for the IPv6 RA support may be configured in the + [IPv6AcceptRA] section, see below. + + Also see ip-sysctl.txt in the kernel + documentation regarding accept_ra, but note that systemd's setting of + 1 (i.e. true) corresponds to kernel's setting of 2. + + + + IPv6DuplicateAddressDetection= + Configures the amount of IPv6 Duplicate + Address Detection (DAD) probes to send. Defaults to unset. + + + + IPv6HopLimit= + Configures IPv6 Hop Limit. For each router that + forwards the packet, the hop limit is decremented by 1. When the + hop limit field reaches zero, the packet is discarded. + Defaults to unset. + + + + ProxyARP= + A boolean. Configures proxy ARP. Proxy ARP is the technique in which one host, + usually a router, answers ARP requests intended for another machine. By "faking" its identity, + the router accepts responsibility for routing packets to the "real" destination. (see RFC 1027. + Defaults to unset. + + + + Bridge= + + The name of the bridge to add the link to. + + + + Bond= + + The name of the bond to add the link to. + + + + VRF= + + The name of the VRF to add the link to. + + + + VLAN= + + The name of a VLAN to create on the link. This + option may be specified more than once. + + + + MACVLAN= + + The name of a MACVLAN to create on the link. This + option may be specified more than once. + + + + VXLAN= + + The name of a VXLAN to create on the link. This + option may be specified more than once. + + + + Tunnel= + + The name of a Tunnel to create on the link. This + option may be specified more than once. + + + + + + + + [Address] Section Options + + An [Address] section accepts the + following keys. Specify several [Address] + sections to configure several addresses. + + + + Address= + + As in the [Network] section. This + key is mandatory. + + + + Peer= + + The peer address in a point-to-point connection. + Accepts the same format as the Address + key. + + + + Broadcast= + + The broadcast address, which must be in the format + described in + inet_pton3. + This key only applies to IPv4 addresses. If it is not + given, it is derived from the Address + key. + + + + Label= + + An address label. + + + + PreferredLifetime= + + Allows the default "preferred lifetime" of the address to be overridden. + Only three settings are accepted: forever or infinity + which is the default and means that the address never expires, and 0 which means + that the address is considered immediately "expired" and will not be used, + unless explicitly requested. A setting of PreferredLifetime=0 is useful for + addresses which are added to be used only by a specific application, + which is then configured to use them explicitly. + + + + + + + [Route] Section Options + The [Route] section accepts the + following keys. Specify several [Route] + sections to configure several routes. + + + + Gateway= + + As in the [Network] section. + + + + Destination= + + The destination prefix of the route. Possibly + followed by a slash and the prefix length. If omitted, a + full-length host route is assumed. + + + + Source= + + The source prefix of the route. Possibly followed by + a slash and the prefix length. If omitted, a full-length + host route is assumed. + + + + Metric= + + The metric of the route (an unsigned integer). + + + + Scope= + + The scope of the route, which can be global, + link or host. Defaults to + global. + + + + PreferredSource= + + The preferred source address of the route. The address + must be in the format described in + inet_pton3. + + + + Table=num + + The table identifier for the route (a number between 1 and 4294967295, or 0 to unset). + The table can be retrieved using ip route show table num. + + + + + + + + [DHCP] Section Options + The [DHCP] section configures the + DHCPv4 and DHCP6 client, if it is enabled with the + DHCP= setting described above: + + + + UseDNS= + + When true (the default), the DNS servers received + from the DHCP server will be used and take precedence over + any statically configured ones. + + This corresponds to the + option in resolv.conf5. + + + + UseNTP= + + When true (the default), the NTP servers received + from the DHCP server will be used by systemd-timesyncd + and take precedence over any statically configured ones. + + + + UseMTU= + + When true, the interface maximum transmission unit + from the DHCP server will be used on the current link. + Defaults to false. + + + + SendHostname= + + When true (the default), the machine's hostname will + be sent to the DHCP server. + + + + UseHostname= + + When true (the default), the hostname received from + the DHCP server will be set as the transient hostname of the system + + + + + Hostname= + + Use this value for the hostname which is sent to the + DHCP server, instead of machine's hostname. + + + + UseDomains= + + Takes a boolean argument, or the special value route. When true, the domain name + received from the DHCP server will be used as DNS search domain over this link, similar to the effect of + the setting. If set to route, the domain name received from + the DHCP server will be used for routing DNS queries only, but not for searching, similar to the effect of + the setting when the argument is prefixed with ~. Defaults to + false. + + It is recommended to enable this option only on trusted networks, as setting this affects resolution + of all host names, in particular of single-label names. It is generally safer to use the supplied domain + only as routing domain, rather than as search domain, in order to not have it affect local resolution of + single-label names. + + When set to true, this setting corresponds to the option in resolv.conf5. + + + + UseRoutes= + + When true (the default), the static routes will be + requested from the DHCP server and added to the routing + table with a metric of 1024. + + + + + UseTimezone= + + When true, the timezone received from the + DHCP server will be set as timezone of the local + system. Defaults to no. + + + + CriticalConnection= + + When true, the connection will never be torn down + even if the DHCP lease expires. This is contrary to the + DHCP specification, but may be the best choice if, say, + the root filesystem relies on this connection. Defaults to + false. + + + + + ClientIdentifier= + + The DHCPv4 client identifier to use. Either mac to use the MAC address of the link + or duid (the default, see below) to use an RFC4361-compliant Client ID. + + + + + VendorClassIdentifier= + + The vendor class identifier used to identify vendor + type and configuration. + + + + + DUIDType= + + Override the global DUIDType setting for this network. See + networkd.conf5 + for a description of possible values. + + + + + DUIDRawData= + + Override the global DUIDRawData setting for this network. See + networkd.conf5 + for a description of possible values. + + + + + IAID= + + The DHCP Identity Association Identifier (IAID) for the interface, a 32-bit unsigned integer. + + + + + RequestBroadcast= + + Request the server to use broadcast messages before + the IP address has been configured. This is necessary for + devices that cannot receive RAW packets, or that cannot + receive packets at all before an IP address has been + configured. On the other hand, this must not be enabled on + networks where broadcasts are filtered out. + + + + + RouteMetric= + + Set the routing metric for routes specified by the + DHCP server. + + + + + + + [IPv6AcceptRA] Section Options + The [IPv6AcceptRA] section configures the IPv6 Router Advertisement + (RA) client, if it is enabled with the IPv6AcceptRA= setting described + above: + + + + UseDNS= + + When true (the default), the DNS servers received in the Router Advertisement will be used and take + precedence over any statically configured ones. + + This corresponds to the option in resolv.conf5. + + + + + UseDomains= + + Takes a boolean argument, or the special value route. When true, the domain name + received via IPv6 Router Advertisement (RA) will be used as DNS search domain over this link, similar to + the effect of the setting. If set to route, the domain name + received via IPv6 RA will be used for routing DNS queries only, but not for searching, similar to the + effect of the setting when the argument is prefixed with + ~. Defaults to false. + + It is recommended to enable this option only on trusted networks, as setting this affects resolution + of all host names, in particular of single-label names. It is generally safer to use the supplied domain + only as routing domain, rather than as search domain, in order to not have it affect local resolution of + single-label names. + + When set to true, this setting corresponds to the option in resolv.conf5. + + + + + + + + [DHCPServer] Section Options + The [DHCPServer] section contains + settings for the DHCP server, if enabled via the + DHCPServer= option described above: + + + + + PoolOffset= + PoolSize= + + Configures the pool of addresses to hand out. The pool + is a contiguous sequence of IP addresses in the subnet configured for + the server address, which does not include the subnet nor the broadcast + address. PoolOffset= takes the offset of the pool + from the start of subnet, or zero to use the default value. + PoolSize= takes the number of IP addresses in the + pool or zero to use the default value. By default, the pool starts at + the first address after the subnet address and takes up the rest of + the subnet, excluding the broadcast address. If the pool includes + the server address (the default), this is reserved and not handed + out to clients. + + + + DefaultLeaseTimeSec= + MaxLeaseTimeSec= + + Control the default and maximum DHCP lease + time to pass to clients. These settings take time values in seconds or + another common time unit, depending on the suffix. The default + lease time is used for clients that did not ask for a specific + lease time. If a client asks for a lease time longer than the + maximum lease time, it is automatically shortened to the + specified time. The default lease time defaults to 1h, the + maximum lease time to 12h. Shorter lease times are beneficial + if the configuration data in DHCP leases changes frequently + and clients shall learn the new settings with shorter + latencies. Longer lease times reduce the generated DHCP + network traffic. + + + + EmitDNS= + DNS= + + Configures whether the DHCP leases handed out + to clients shall contain DNS server information. The + EmitDNS= setting takes a boolean argument + and defaults to yes. The DNS servers to + pass to clients may be configured with the + DNS= option, which takes a list of IPv4 + addresses. If the EmitDNS= option is + enabled but no servers configured, the servers are + automatically propagated from an "uplink" interface that has + appropriate servers set. The "uplink" interface is determined + by the default route of the system with the highest + priority. Note that this information is acquired at the time + the lease is handed out, and does not take uplink interfaces + into account that acquire DNS or NTP server information at a + later point. DNS server propagation does not take + /etc/resolv.conf into account. Also, note + that the leases are not refreshed if the uplink network + configuration changes. To ensure clients regularly acquire the + most current uplink DNS server information, it is thus + advisable to shorten the DHCP lease time via + MaxLeaseTimeSec= described + above. + + + + EmitNTP= + NTP= + + Similar to the EmitDNS= and + DNS= settings described above, these + settings configure whether and what NTP server information + shall be emitted as part of the DHCP lease. The same syntax, + propagation semantics and defaults apply as for + EmitDNS= and + DNS=. + + + + EmitRouter= + + Similar to the EmitDNS= + setting described above, this setting configures whether the + DHCP lease should contain the router option. The same syntax, + propagation semantics and defaults apply as for + EmitDNS=. + + + + EmitTimezone= + Timezone= + + Configures whether the DHCP leases handed out + to clients shall contain timezone information. The + EmitTimezone= setting takes a boolean + argument and defaults to yes. The + Timezone= setting takes a timezone string + (such as Europe/Berlin or + UTC) to pass to clients. If no explicit + timezone is set, the system timezone of the local host is + propagated, as determined by the + /etc/localtime symlink. + + + + + + + [Bridge] Section Options + The [Bridge] section accepts the + following keys. + + + UnicastFlood= + + A boolean. Controls whether the bridge should flood + traffic for which an FDB entry is missing and the destination + is unknown through this port. Defaults to on. + + + + + HairPin= + + A boolean. Configures whether traffic may be sent back + out of the port on which it was received. By default, this + flag is false, and the bridge will not forward traffic back + out of the receiving port. + + + + UseBPDU= + + A boolean. Configures whether STP Bridge Protocol Data Units will be + processed by the bridge port. Defaults to yes. + + + + FastLeave= + + A boolean. This flag allows the bridge to immediately stop multicast + traffic on a port that receives an IGMP Leave message. It is only used with + IGMP snooping if enabled on the bridge. Defaults to off. + + + + AllowPortToBeRoot= + + A boolean. Configures whether a given port is allowed to + become a root port. Only used when STP is enabled on the bridge. + Defaults to on. + + + + Cost= + + Sets the "cost" of sending packets of this interface. + Each port in a bridge may have a different speed and the cost + is used to decide which link to use. Faster interfaces + should have lower costs. + + + + + + [BridgeFDB] Section Options + The [BridgeFDB] section manages the + forwarding database table of a port and accepts the following + keys. Specify several [BridgeFDB] sections to + configure several static MAC table entries. + + + + MACAddress= + + As in the [Network] section. This + key is mandatory. + + + + VLANId= + + The VLAN ID for the new static MAC table entry. If + omitted, no VLAN ID info is appended to the new static MAC + table entry. + + + + + + [BridgeVLAN] Section Options + The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts + the following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. + The VLANFiltering= option has to be enabled, see [Bridge] section in + systemd.netdev5. + + + + VLAN= + + The VLAN ID allowed on the port. This can be either a single ID or a range M-N. VLAN IDs are valid + from 1 to 4094. + + + + EgressUntagged= + + The VLAN ID specified here will be used to untag frames on egress. Configuring + EgressUntagged= implicates the use of VLAN= above and will enable the + VLAN ID for ingress as well. This can be either a single ID or a range M-N. + + + + PVID= + + The Port VLAN ID specified here is assigned to all untagged frames at ingress. + PVID= can be used only once. Configuring PVID= implicates the use of + VLAN= above and will enable the VLAN ID for ingress as well. + + + + + + + Example + + /etc/systemd/network/50-static.network + + [Match] +Name=enp2s0 + +[Network] +Address=192.168.0.15/24 +Gateway=192.168.0.1 + + + + /etc/systemd/network/80-dhcp.network + + [Match] +Name=en* + +[Network] +DHCP=yes + + + + /etc/systemd/network/25-bridge-static.network + + [Match] +Name=bridge0 + +[Network] +Address=192.168.0.15/24 +Gateway=192.168.0.1 +DNS=192.168.0.1 + + + + /etc/systemd/network/25-bridge-slave-interface.network + + [Match] +Name=enp2s0 + +[Network] +Bridge=bridge0 + + + /etc/systemd/network/25-bridge-slave-interface-vlan.network + + [Match] +Name=enp2s0 + +[Network] +Bridge=bridge0 + +[BridgeVLAN] +VLAN=1-32 +PVID=42 +EgressUntagged=42 + +[BridgeVLAN] +VLAN=100-200 + +[BridgeVLAN] +EgressUntagged=300-400 + + + /etc/systemd/network/25-ipip.network + + [Match] +Name=em1 + +[Network] +Tunnel=ipip-tun + + + + /etc/systemd/network/25-sit.network + + [Match] +Name=em1 + +[Network] +Tunnel=sit-tun + + + + /etc/systemd/network/25-gre.network + + [Match] +Name=em1 + +[Network] +Tunnel=gre-tun + + + + /etc/systemd/network/25-vti.network + + [Match] +Name=em1 + +[Network] +Tunnel=vti-tun + + + + /etc/systemd/network/25-bond.network + + [Match] +Name=bond1 + +[Network] +DHCP=yes + + + + + /etc/systemd/network/25-vrf.network + Add the bond1 interface to the VRF master interface vrf-test. This will redirect routes generated on this interface to be within the routing table defined during VRF creation. Traffic won't be redirected towards the VRFs routing table unless specific ip-rules are added. + [Match] +Name=bond1 + +[Network] +VRF=vrf-test + + + + + + + See Also + + systemd1, + systemd-networkd.service8, + systemd.link5, + systemd.netdev5, + systemd-resolved.service8 + + + + diff --git a/src/grp-system/systemd/systemd.nspawn.xml b/src/grp-system/systemd/systemd.nspawn.xml new file mode 100644 index 0000000000..b1344d6c10 --- /dev/null +++ b/src/grp-system/systemd/systemd.nspawn.xml @@ -0,0 +1,463 @@ + + +%entities; +]> + + + + + + + systemd.nspawn + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.nspawn + 5 + + + + systemd.nspawn + Container settings + + + + /etc/systemd/nspawn/machine.nspawn + /run/systemd/nspawn/machine.nspawn + /var/lib/machines/machine.nspawn + + + + Description + + An nspawn container settings file (suffix + .nspawn) encodes additional runtime + information about a local container, and is searched, read and + used by + systemd-nspawn1 + when starting a container. Files of this type are named after the + containers they define settings for. They are optional, and only + required for containers whose execution environment shall differ + from the defaults. Files of this type mostly contain settings that + may also be set on the systemd-nspawn command + line, and make it easier to persistently attach specific settings + to specific containers. The syntax of these files is inspired by + .desktop files following the XDG + Desktop Entry Specification, which in turn are inspired by + Microsoft Windows .ini files. + + Boolean arguments used in these settings files can be + written in various formats. For positive settings, the strings + , , + and are equivalent. For negative settings, the + strings , , + and are + equivalent. + + Empty lines and lines starting with # or ; are + ignored. This may be used for commenting. Lines ending + in a backslash are concatenated with the following + line while reading and the backslash is replaced by a + space character. This may be used to wrap long lines. + + + + + <filename>.nspawn</filename> File Discovery + + Files are searched by appending the + .nspawn suffix to the machine name of the + container, as specified with the + switch of systemd-nspawn, or derived from the + directory or image file name. This file is first searched in + /etc/systemd/nspawn/ and + /run/systemd/nspawn/. If found in these + directories, its settings are read and all of them take full effect + (but are possibly overridden by corresponding command line + arguments). If not found, the file will then be searched next to + the image file or in the immediate parent of the root directory of + the container. If the file is found there, only a subset of the + settings will take effect however. All settings that possibly + elevate privileges or grant additional access to resources of the + host (such as files or directories) are ignored. To which options + this applies is documented below. + + Persistent settings files created and maintained by the + administrator (and thus trusted) should be placed in + /etc/systemd/nspawn/, while automatically + downloaded (and thus potentially untrusted) settings files are + placed in /var/lib/machines/ instead (next to + the container images), where their security impact is limited. In + order to add privileged settings to .nspawn + files acquired from the image vendor, it is recommended to copy the + settings files into /etc/systemd/nspawn/ and + edit them there, so that the privileged options become + available. The precise algorithm for how the files are searched and + interpreted may be configured with + systemd-nspawn's + switch, see + systemd-nspawn1 + for details. + + + + [Exec] Section Options + + Settings files may include an [Exec] + section, which carries various execution parameters: + + + + + Boot= + + Takes a boolean argument, which defaults to off. If enabled, systemd-nspawn + will automatically search for an init executable and invoke it. In this case, the + specified parameters using Parameters= are passed as additional arguments to the + init process. This setting corresponds to the switch on the + systemd-nspawn command line. This option may not be combined with + ProcessTwo=yes. This option is the default if the + systemd-nspawn@.service template unit file is used. + + + + ProcessTwo= + + Takes a boolean argument, which defaults to off. If enabled, the specified program is run as + PID 2. A stub init process is run as PID 1. This setting corresponds to the switch + on the systemd-nspawn command line. This option may not be combined with + Boot=yes. + + + + Parameters= + + Takes a space-separated list of + arguments. This is either a command line, beginning with the + binary name to execute, or – if Boot= is + enabled – the list of arguments to pass to the init + process. This setting corresponds to the command line + parameters passed on the systemd-nspawn + command line. + + + + Environment= + + Takes an environment variable assignment + consisting of key and value, separated by + =. Sets an environment variable for the + main process invoked in the container. This setting may be + used multiple times to set multiple environment variables. It + corresponds to the command line + switch. + + + + User= + + Takes a UNIX user name. Specifies the user + name to invoke the main process of the container as. This user + must be known in the container's user database. This + corresponds to the command line + switch. + + + + WorkingDirectory= + + Selects the working directory for the process invoked in the container. Expects an absolute + path in the container's file system namespace. This corresponds to the command line + switch. + + + + Capability= + DropCapability= + + Takes a space-separated list of Linux process + capabilities (see + capabilities7 + for details). The Capability= setting + specifies additional capabilities to pass on top of the + default set of capabilities. The + DropCapability= setting specifies + capabilities to drop from the default set. These settings + correspond to the and + command line + switches. Note that Capability= is a + privileged setting, and only takes effect in + .nspawn files in + /etc/systemd/nspawn/ and + /run/system/nspawn/ (see above). On the + other hand, DropCapability= takes effect in + all cases. + + + + KillSignal= + + Specify the process signal to send to the + container's PID 1 when nspawn itself receives SIGTERM, in + order to trigger an orderly shutdown of the container. + Defaults to SIGRTMIN+3 if is used + (on systemd-compatible init systems SIGRTMIN+3 triggers an + orderly shutdown). For a list of valid signals, see + signal7. + + + + Personality= + + Configures the kernel personality for the + container. This is equivalent to the + switch. + + + + MachineID= + + Configures the 128-bit machine ID (UUID) to pass to + the container. This is equivalent to the + command line switch. This option is + privileged (see above). + + + + PrivateUsers= + + Configures support for usernamespacing. This is equivalent to the + command line switch, and takes the same options. This option is privileged + (see above). This option is the default if the systemd-nspawn@.service template unit file + is used. + + + + NotifyReady= + + Configures support for notifications from the container's init process. + This is equivalent to use command line switch, + and takes the same options. See systemd-nspawn1 + for details about the specific options supported. + + + + + + [Files] Section Options + + Settings files may include a [Files] + section, which carries various parameters configuring the file + system of the container: + + + + + ReadOnly= + + Takes a boolean argument, which defaults to off. If + specified, the container will be run with a read-only file + system. This setting corresponds to the + command line + switch. + + + + Volatile= + + Takes a boolean argument, or the special value + state. This configures whether to run the + container with volatile state and/or configuration. This + option is equivalent to , see + systemd-nspawn1 + for details about the specific options + supported. + + + + Bind= + BindReadOnly= + + Adds a bind mount from the host into the + container. Takes a single path, a pair of two paths separated + by a colon, or a triplet of two paths plus an option string + separated by colons. This option may be used multiple times to + configure multiple bind mounts. This option is equivalent to + the command line switches and + , see + systemd-nspawn1 + for details about the specific options supported. This setting + is privileged (see above). + + + + TemporaryFileSystem= + + Adds a tmpfs mount to the + container. Takes a path or a pair of path and option string, + separated by a colon. This option may be used multiple times to + configure multiple tmpfs mounts. This + option is equivalent to the command line switch + , see + systemd-nspawn1 + for details about the specific options supported. This setting + is privileged (see above). + + + + PrivateUsersChown= + + Configures whether the ownership of the files and directories in the container tree shall be + adjusted to the UID/GID range used, if necessary and user namespacing is enabled. This is equivalent to the + command line switch. This option is privileged (see + above). + + + + + + + [Network] Section Options + + Settings files may include a [Network] + section, which carries various parameters configuring the network + connectivity of the container: + + + + + Private= + + Takes a boolean argument, which defaults to off. If + enabled, the container will run in its own network namespace + and not share network interfaces and configuration with the + host. This setting corresponds to the + command line + switch. + + + + VirtualEthernet= + + Takes a boolean argument. Configures whether to create a virtual Ethernet connection + (veth) between host and the container. This setting implies + Private=yes. This setting corresponds to the command line + switch. This option is privileged (see above). This option is the default if the + systemd-nspawn@.service template unit file is used. + + + + VirtualEthernetExtra= + + Takes a colon-separated pair of interface + names. Configures an additional virtual Ethernet connection + (veth) between host and the container. The + first specified name is the interface name on the host, the + second the interface name in the container. The latter may be + omitted in which case it is set to the same name as the host + side interface. This setting implies + Private=yes. This setting corresponds to + the command line + switch, and maybe be used multiple times. It is independent of + VirtualEthernet=. This option is privileged + (see above). + + + + Interface= + + Takes a space-separated list of interfaces to + add to the container. This option corresponds to the + command line switch and + implies Private=yes. This option is + privileged (see above). + + + + MACVLAN= + IPVLAN= + + Takes a space-separated list of interfaces to + add MACLVAN or IPVLAN interfaces to, which are then added to + the container. These options correspond to the + and + command line switches and + imply Private=yes. These options are + privileged (see above). + + + + Bridge= + + Takes an interface name. This setting implies + VirtualEthernet=yes and + Private=yes and has the effect that the + host side of the created virtual Ethernet link is connected to + the specified bridge interface. This option corresponds to the + command line switch. This + option is privileged (see above). + + + + Zone= + + Takes a network zone name. This setting implies VirtualEthernet=yes and + Private=yes and has the effect that the host side of the created virtual Ethernet link is + connected to an automatically managed bridge interface named after the passed argument, prefixed with + vz-. This option corresponds to the command line + switch. This option is privileged (see above). + + + + Port= + + Exposes a TCP or UDP port of the container on + the host. This option corresponds to the + command line switch, see + systemd-nspawn1 + for the precise syntax of the argument this option takes. This + option is privileged (see above). + + + + + + See Also + + systemd1, + systemd-nspawn1, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.offline-updates.xml b/src/grp-system/systemd/systemd.offline-updates.xml new file mode 100644 index 0000000000..ae53b8552d --- /dev/null +++ b/src/grp-system/systemd/systemd.offline-updates.xml @@ -0,0 +1,169 @@ + + + + + + + + systemd.offline-updates + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.offline-updates + 7 + + + + systemd.offline-updates + Implementation of offline updates in systemd + + + + Implementing Offline System Updates + + This man page describes how to implement "offline" system updates with systemd. By "offline" + OS updates we mean package installations and updates that are run with the system booted into a + special system update mode, in order to avoid problems related to conflicts of libraries and + services that are currently running with those on disk. This document is inspired by this + GNOME design whiteboard. + + + The logic: + + + + The package manager prepares system updates by downloading all (RPM or DEB or + whatever) packages to update off-line in a special directory + /var/lib/system-update (or + another directory of the package/upgrade manager's choice). + + + + When the user OK'ed the update, the symlink /system-update is + created that points to /var/lib/system-update (or + wherever the directory with the upgrade files is located) and the system is rebooted. This + symlink is in the root directory, since we need to check for it very early at boot, at a + time where /var is not available yet. + + + + Very early in the new boot + systemd-update-generator8 + checks whether /system-update exists. If so, it (temporarily and for + this boot only) redirects (i.e. symlinks) default.target to + system-update.target, a special target that is pulls in the base system + (i.e. sysinit.target, so that all file systems are mounted but little + else) and the system update units. + + + + The system now continues to boot into default.target, and thus + into system-update.target. This target pulls in the system update unit, + which starts the system update script after all file systems have been mounted. + + + + As the first step, the update script should check if the + /system-update symlink points to the location used by that update + script. In case it does not exists or points to a different location, the script must exit + without error. It is possible for multiple update services to be installed, and for multiple + update scripts to be launched in parallel, and only the one that corresponds to the tool + that created the symlink before reboot should perform any actions. It + is unsafe to run multiple updates in parallel. + + + + The update script should now do its job. If applicable and possible, it should + create a file system snapshot, then install all packages. + After completion (regardless whether the update succeeded or failed) the machine + must be rebooted, for example by calling systemctl reboot. + In addition, on failure the script should revert to the old file system snapshot + (without the symlink). + + + + The system is rebooted. Since the /system-update symlink is gone, + the generator won't redirect default.target after reboot and the + system now boots into the default target again. + + + + + + Recommendations + + + + To make things a bit more robust we recommend hooking the update script into + system-update.target via a .wants/ + symlink in the distribution package, rather than depending on systemctl + enable in the postinst scriptlets of your package. More specifically, for your + update script create a .service file, without [Install] section, and then add a symlink like + /usr/lib/systemd/system-update.target.wants/foobar.service + → ../foobar.service to your package. + + + + Make sure to remove the /system-update symlink as early as + possible in the update script to avoid reboot loops in case the update fails. + + + + Use FailureAction=reboot in the service file for your update script + to ensure that a reboot is automatically triggered if the update fails. + FailureAction= makes sure that the specified unit is activated if your + script exits uncleanly (by non-zero error code, or signal/coredump). If your script succeeds + you should trigger the reboot in your own code, for example by invoking logind's + Reboot() call or calling systemct reboot. See + logind dbus API + for details. + + + + The update service should declare DefaultDependencies=false, + and pull in any services it requires explicitly. + + + + + + See also + + + Implementing Offline System Updates, + systemd1, + systemd.generator7, + systemd-update-generator8, + dnf.plugin.system-upgrade8 + + + diff --git a/src/grp-system/systemd/systemd.path.xml b/src/grp-system/systemd/systemd.path.xml new file mode 100644 index 0000000000..7200c8fe27 --- /dev/null +++ b/src/grp-system/systemd/systemd.path.xml @@ -0,0 +1,202 @@ + + + + + + + + systemd.path + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.path + 5 + + + + systemd.path + Path unit configuration + + + + path.path + + + + Description + + A unit configuration file whose name ends in + .path encodes information about a path + monitored by systemd, for path-based activation. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The path specific configuration options are + configured in the [Path] section. + + For each path file, a matching unit file must exist, + describing the unit to activate when the path changes. By default, + a service by the same name as the path (except for the suffix) is + activated. Example: a path file foo.path + activates a matching service foo.service. The + unit to activate may be controlled by Unit= + (see below). + + Internally, path units use the + inotify7 + API to monitor file systems. Due to that, it suffers by the same + limitations as inotify, and for example cannot be used to monitor + files or directories changed by other machines on remote NFS file + systems. + + + + Automatic Dependencies + + If a path unit is beneath another mount unit in the file + system hierarchy, both a requirement and an ordering dependency + between both units are created automatically. + + An implicit Before= dependency is added + between a path unit and the unit it is supposed to activate. + + Unless DefaultDependencies=false in the [Unit] section is used, path + units will implicitly have dependencies of type Before= on paths.target, + dependencies of type After= and Requires= on + sysinit.target, and have dependencies of type Conflicts= and + Before= on shutdown.target. These ensure that path units are terminated + cleanly prior to system shutdown. Only path units involved with early boot or late system shutdown should disable + this option. + + + + + Options + + Path files must include a [Path] section, which carries + information about the path(s) it monitors. The options specific to + the [Path] section of path units are the following: + + + + PathExists= + PathExistsGlob= + PathChanged= + PathModified= + DirectoryNotEmpty= + + Defines paths to monitor for certain changes: + PathExists= may be used to watch the mere + existence of a file or directory. If the file specified + exists, the configured unit is activated. + PathExistsGlob= works similar, but checks + for the existence of at least one file matching the globbing + pattern specified. PathChanged= may be used + to watch a file or directory and activate the configured unit + whenever it changes. It is not activated on every write to the + watched file but it is activated if the file which was open + for writing gets closed. PathModified= is + similar, but additionally it is activated also on simple + writes to the watched file. + DirectoryNotEmpty= may be used to watch a + directory and activate the configured unit whenever it + contains at least one file. + + The arguments of these directives must be absolute file + system paths. + + Multiple directives may be combined, of the same and of + different types, to watch multiple paths. If the empty string + is assigned to any of these options, the list of paths to + watch is reset, and any prior assignments of these options + will not have any effect. + + If a path already exists (in case of + PathExists= and + PathExistsGlob=) or a directory already is + not empty (in case of DirectoryNotEmpty=) + at the time the path unit is activated, then the configured + unit is immediately activated as well. Something similar does + not apply to PathChanged= and + PathModified=. + + If the path itself or any of the containing directories + are not accessible, systemd will watch for + permission changes and notice that conditions are satisfied + when permissions allow that. + + + Unit= + + The unit to activate when any of the + configured paths changes. The argument is a unit name, whose + suffix is not .path. If not specified, this + value defaults to a service that has the same name as the path + unit, except for the suffix. (See above.) It is recommended + that the unit name that is activated and the unit name of the + path unit are named identical, except for the + suffix. + + + MakeDirectory= + + Takes a boolean argument. If true, the + directories to watch are created before watching. This option + is ignored for PathExists= settings. + Defaults to . + + + DirectoryMode= + + If MakeDirectory= is + enabled, use the mode specified here to create the directories + in question. Takes an access mode in octal notation. Defaults + to . + + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.service5, + inotify7, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.pc.in b/src/grp-system/systemd/systemd.pc.in new file mode 100644 index 0000000000..ac52b30dd3 --- /dev/null +++ b/src/grp-system/systemd/systemd.pc.in @@ -0,0 +1,34 @@ +# 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. + +prefix=@prefix@ +systemdutildir=@rootlibexecdir@ +systemdsystemunitdir=@systemunitdir@ +systemdsystempresetdir=@systempresetdir@ +systemduserunitdir=@userunitdir@ +systemduserpresetdir=@userpresetdir@ +systemdsystemconfdir=@pkgsysconfdir@/system +systemduserconfdir=@pkgsysconfdir@/user +systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system +systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user +systemdsystemgeneratordir=@systemgeneratordir@ +systemdusergeneratordir=@usergeneratordir@ +systemdsleepdir=@systemsleepdir@ +systemdshutdowndir=@systemshutdowndir@ +tmpfilesdir=@tmpfilesdir@ +sysusersdir=@sysusersdir@ +sysctldir=@sysctldir@ +binfmtdir=@binfmtdir@ +modulesloaddir=@modulesloaddir@ +catalogdir=@catalogdir@ +systemuidmax=@systemuidmax@ +systemgidmax=@systemgidmax@ + +Name: systemd +Description: systemd System and Service Manager +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ diff --git a/src/grp-system/systemd/systemd.resource-control.xml b/src/grp-system/systemd/systemd.resource-control.xml new file mode 100644 index 0000000000..bf44a68345 --- /dev/null +++ b/src/grp-system/systemd/systemd.resource-control.xml @@ -0,0 +1,696 @@ + + + + + + + + systemd.resource-control + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.resource-control + 5 + + + + systemd.resource-control + Resource control unit settings + + + + + slice.slice, + scope.scope, + service.service, + socket.socket, + mount.mount, + swap.swap + + + + + Description + + Unit configuration files for services, slices, scopes, + sockets, mount points, and swap devices share a subset of + configuration options for resource control of spawned + processes. Internally, this relies on the Control Groups + kernel concept for organizing processes in a hierarchical tree of + named groups for the purpose of resource management. + + This man page lists the configuration options shared by + those six unit types. See + systemd.unit5 + for the common options of all unit configuration files, and + systemd.slice5, + systemd.scope5, + systemd.service5, + systemd.socket5, + systemd.mount5, + and + systemd.swap5 + for more information on the specific unit configuration files. The + resource control configuration options are configured in the + [Slice], [Scope], [Service], [Socket], [Mount], or [Swap] + sections, depending on the unit type. + + See the New + Control Group Interfaces for an introduction on how to make + use of resource control APIs from programs. + + + + Automatic Dependencies + + Units with the Slice= setting set automatically acquire Requires= and + After= dependencies on the specified slice unit. + + + + Unified and Legacy Control Group Hierarchies + + The unified control group hierarchy is the new version of kernel control group interface. Depending on the + resource type, there are differences in resource control capabilities. Also, because of interface changes, some + resource types have a separate set of options on the unified hierarchy. + + + + + + + IO prefixed settings are superset of and replace BlockIO + prefixed ones. On unified hierarchy, IO resource control also applies to buffered writes. + + + + + + MemoryMax= replaces MemoryLimit=. MemoryLow= + and MemoryHigh= are effective only on unified hierarchy. + + + + + + To ease the transition, there is best-effort translation between the two versions of settings. If all + settings of a unit for a given resource type are for the other hierarchy type, the settings are translated and + applied. If there are any valid settings for the hierarchy in use, all translations are disabled for the resource + type. Mixing the two types of settings on a unit can lead to confusing results. + + + + Options + + Units of the types listed above can have settings + for resource control configuration: + + + + + CPUAccounting= + + + Turn on CPU usage accounting for this unit. Takes a + boolean argument. Note that turning on CPU accounting for + one unit will also implicitly turn it on for all units + contained in the same slice and for all its parent slices + and the units contained therein. The system default for this + setting may be controlled with + DefaultCPUAccounting= in + systemd-system.conf5. + + + + + CPUShares=weight + StartupCPUShares=weight + + + Assign the specified CPU time share weight to the + processes executed. These options take an integer value and + control the cpu.shares control group + attribute. The allowed range is 2 to 262144. Defaults to + 1024. For details about this control group attribute, see + sched-design-CFS.txt. + The available CPU time is split up among all units within + one slice relative to their CPU time share weight. + + While StartupCPUShares= only + applies to the startup phase of the system, + CPUShares= applies to normal runtime of + the system, and if the former is not set also to the startup + phase. Using StartupCPUShares= allows + prioritizing specific services at boot-up differently than + during normal runtime. + + These options imply + CPUAccounting=true. + + + + + CPUQuota= + + + Assign the specified CPU time quota to the processes + executed. Takes a percentage value, suffixed with "%". The + percentage specifies how much CPU time the unit shall get at + maximum, relative to the total CPU time available on one + CPU. Use values > 100% for allotting CPU time on more than + one CPU. This controls the + cpu.cfs_quota_us control group + attribute. For details about this control group attribute, + see sched-design-CFS.txt. + + Example: CPUQuota=20% ensures that + the executed processes will never get more than 20% CPU time + on one CPU. + + Implies CPUAccounting=true. + + + + + MemoryAccounting= + + + Turn on process and kernel memory accounting for this + unit. Takes a boolean argument. Note that turning on memory + accounting for one unit will also implicitly turn it on for + all units contained in the same slice and for all its parent + slices and the units contained therein. The system default + for this setting may be controlled with + DefaultMemoryAccounting= in + systemd-system.conf5. + + + + + MemoryLow=bytes + + + Specify the best-effort memory usage protection of the executed processes in this unit. If the memory + usages of this unit and all its ancestors are below their low boundaries, this unit's memory won't be + reclaimed as long as memory can be reclaimed from unprotected units. + + Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is + parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a + percentage value may be specified, which is taken relative to the installed physical memory on the + system. This controls the memory.low control group attribute. For details about this + control group attribute, see cgroup-v2.txt. + + Implies MemoryAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. + + + + + MemoryHigh=bytes + + + Specify the high limit on memory usage of the executed processes in this unit. Memory usage may go + above the limit if unavoidable, but the processes are heavily slowed down and memory is taken away + aggressively in such cases. This is the main mechanism to control memory usage of a unit. + + Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is + parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a + percentage value may be specified, which is taken relative to the installed physical memory on the + system. If assigned the + special value infinity, no memory limit is applied. This controls the + memory.high control group attribute. For details about this control group attribute, see + cgroup-v2.txt. + + Implies MemoryAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. + + + + + MemoryMax=bytes + + + Specify the absolute limit on memory usage of the executed processes in this unit. If memory usage + cannot be contained under the limit, out-of-memory killer is invoked inside the unit. It is recommended to + use MemoryHigh= as the main control mechanism and use MemoryMax= as the + last line of defense. + + Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is + parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a + percentage value may be specified, which is taken relative to the installed physical memory on the system. If + assigned the special value infinity, no memory limit is applied. This controls the + memory.max control group attribute. For details about this control group attribute, see + cgroup-v2.txt. + + Implies MemoryAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. Use + MemoryLimit= on systems using the legacy control group hierarchy. + + + + + MemoryLimit=bytes + + + Specify the limit on maximum memory usage of the executed processes. The limit specifies how much + process and kernel memory can be used by tasks in this unit. Takes a memory size in bytes. If the value is + suffixed with K, M, G or T, the specified memory size is parsed as Kilobytes, Megabytes, Gigabytes, or + Terabytes (with the base 1024), respectively. Alternatively, a percentage value may be specified, which is + taken relative to the installed physical memory on the system. If assigned the special value + infinity, no memory limit is applied. This controls the + memory.limit_in_bytes control group attribute. For details about this control group + attribute, see memory.txt. + + Implies MemoryAccounting=true. + + This setting is supported only if the legacy control group hierarchy is used. Use + MemoryMax= on systems using the unified control group hierarchy. + + + + + TasksAccounting= + + + Turn on task accounting for this unit. Takes a + boolean argument. If enabled, the system manager will keep + track of the number of tasks in the unit. The number of + tasks accounted this way includes both kernel threads and + userspace processes, with each thread counting + individually. Note that turning on tasks accounting for one + unit will also implicitly turn it on for all units contained + in the same slice and for all its parent slices and the + units contained therein. The system default for this setting + may be controlled with + DefaultTasksAccounting= in + systemd-system.conf5. + + + + + TasksMax=N + + + Specify the maximum number of tasks that may be created in the unit. This ensures that the number of + tasks accounted for the unit (see above) stays below a specific limit. This either takes an absolute number + of tasks or a percentage value that is taken relative to the configured maximum number of tasks on the + system. If assigned the special value infinity, no tasks limit is applied. This controls + the pids.max control group attribute. For details about this control group attribute, see + pids.txt. + + Implies TasksAccounting=true. The + system default for this setting may be controlled with + DefaultTasksMax= in + systemd-system.conf5. + + + + + IOAccounting= + + + Turn on Block I/O accounting for this unit, if the unified control group hierarchy is used on the + system. Takes a boolean argument. Note that turning on block I/O accounting for one unit will also implicitly + turn it on for all units contained in the same slice and all for its parent slices and the units contained + therein. The system default for this setting may be controlled with DefaultIOAccounting= + in + systemd-system.conf5. + + This setting is supported only if the unified control group hierarchy is used. Use + BlockIOAccounting= on systems using the legacy control group hierarchy. + + + + + IOWeight=weight + StartupIOWeight=weight + + + Set the default overall block I/O weight for the executed processes, if the unified control group + hierarchy is used on the system. Takes a single weight value (between 1 and 10000) to set the default block + I/O weight. This controls the io.weight control group attribute, which defaults to + 100. For details about this control group attribute, see cgroup-v2.txt. The available I/O + bandwidth is split up among all units within one slice relative to their block I/O weight. + + While StartupIOWeight= only applies + to the startup phase of the system, + IOWeight= applies to the later runtime of + the system, and if the former is not set also to the startup + phase. This allows prioritizing specific services at boot-up + differently than during runtime. + + Implies IOAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. Use + BlockIOWeight= and StartupBlockIOWeight= on systems using the legacy + control group hierarchy. + + + + + IODeviceWeight=device weight + + + Set the per-device overall block I/O weight for the executed processes, if the unified control group + hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify + the device specific weight value, between 1 and 10000. (Example: "/dev/sda 1000"). The file path may be + specified as path to a block device node or as any other file, in which case the backing block device of the + file system of the file is determined. This controls the io.weight control group + attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices. For + details about this control group attribute, see cgroup-v2.txt. + + Implies IOAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. Use + BlockIODeviceWeight= on systems using the legacy control group hierarchy. + + + + + IOReadBandwidthMax=device bytes + IOWriteBandwidthMax=device bytes + + + Set the per-device overall block I/O bandwidth maximum limit for the executed processes, if the unified + control group hierarchy is used on the system. This limit is not work-conserving and the executed processes + are not allowed to use more even if the device has idle capacity. Takes a space-separated pair of a file + path and a bandwidth value (in bytes per second) to specify the device specific bandwidth. The file path may + be a path to a block device node, or as any other file in which case the backing block device of the file + system of the file is used. If the bandwidth is suffixed with K, M, G, or T, the specified bandwidth is + parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes, respectively, to the base of 1000. (Example: + "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 5M"). This controls the io.max control + group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For details + about this control group attribute, see cgroup-v2.txt. + + + Implies IOAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. Use + BlockIOAccounting= on systems using the legacy control group hierarchy. + + + + + IOReadIOPSMax=device IOPS + IOWriteIOPSMax=device IOPS + + + Set the per-device overall block I/O IOs-Per-Second maximum limit for the executed processes, if the + unified control group hierarchy is used on the system. This limit is not work-conserving and the executed + processes are not allowed to use more even if the device has idle capacity. Takes a space-separated pair of + a file path and an IOPS value to specify the device specific IOPS. The file path may be a path to a block + device node, or as any other file in which case the backing block device of the file system of the file is + used. If the IOPS is suffixed with K, M, G, or T, the specified IOPS is parsed as KiloIOPS, MegaIOPS, + GigaIOPS, or TeraIOPS, respectively, to the base of 1000. (Example: + "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 1K"). This controls the io.max control + group attributes. Use this option multiple times to set IOPS limits for multiple devices. For details about + this control group attribute, see cgroup-v2.txt. + + + Implies IOAccounting=true. + + This setting is supported only if the unified control group hierarchy is used. + + + + + BlockIOAccounting= + + + Turn on Block I/O accounting for this unit, if the legacy control group hierarchy is used on the + system. Takes a boolean argument. Note that turning on block I/O accounting for one unit will also implicitly + turn it on for all units contained in the same slice and all for its parent slices and the units contained + therein. The system default for this setting may be controlled with + DefaultBlockIOAccounting= in + systemd-system.conf5. + + This setting is supported only if the legacy control group hierarchy is used. Use + IOAccounting= on systems using the unified control group hierarchy. + + + + + BlockIOWeight=weight + StartupBlockIOWeight=weight + + Set the default overall block I/O weight for the executed processes, if the legacy control + group hierarchy is used on the system. Takes a single weight value (between 10 and 1000) to set the default + block I/O weight. This controls the blkio.weight control group attribute, which defaults to + 500. For details about this control group attribute, see blkio-controller.txt. + The available I/O bandwidth is split up among all units within one slice relative to their block I/O + weight. + + While StartupBlockIOWeight= only + applies to the startup phase of the system, + BlockIOWeight= applies to the later runtime + of the system, and if the former is not set also to the + startup phase. This allows prioritizing specific services at + boot-up differently than during runtime. + + Implies + BlockIOAccounting=true. + + This setting is supported only if the legacy control group hierarchy is used. Use + IOWeight= and StartupIOWeight= on systems using the unified control group + hierarchy. + + + + + + BlockIODeviceWeight=device weight + + + Set the per-device overall block I/O weight for the executed processes, if the legacy control group + hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify + the device specific weight value, between 10 and 1000. (Example: "/dev/sda 500"). The file path may be + specified as path to a block device node or as any other file, in which case the backing block device of the + file system of the file is determined. This controls the blkio.weight_device control group + attribute, which defaults to 1000. Use this option multiple times to set weights for multiple devices. For + details about this control group attribute, see blkio-controller.txt. + + Implies + BlockIOAccounting=true. + + This setting is supported only if the legacy control group hierarchy is used. Use + IODeviceWeight= on systems using the unified control group hierarchy. + + + + + BlockIOReadBandwidth=device bytes + BlockIOWriteBandwidth=device bytes + + + Set the per-device overall block I/O bandwidth limit for the executed processes, if the legacy control + group hierarchy is used on the system. Takes a space-separated pair of a file path and a bandwidth value (in + bytes per second) to specify the device specific bandwidth. The file path may be a path to a block device + node, or as any other file in which case the backing block device of the file system of the file is used. If + the bandwidth is suffixed with K, M, G, or T, the specified bandwidth is parsed as Kilobytes, Megabytes, + Gigabytes, or Terabytes, respectively, to the base of 1000. (Example: + "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 5M"). This controls the + blkio.throttle.read_bps_device and blkio.throttle.write_bps_device + control group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For + details about these control group attributes, see blkio-controller.txt. + + + Implies + BlockIOAccounting=true. + + This setting is supported only if the legacy control group hierarchy is used. Use + IOReadBandwidthMax= and IOWriteBandwidthMax= on systems using the + unified control group hierarchy. + + + + + DeviceAllow= + + + Control access to specific device nodes by the + executed processes. Takes two space-separated strings: a + device node specifier followed by a combination of + r, w, + m to control + reading, writing, + or creation of the specific device node(s) by the unit + (mknod), respectively. This controls + the devices.allow and + devices.deny control group + attributes. For details about these control group + attributes, see devices.txt. + + The device node specifier is either a path to a device + node in the file system, starting with + /dev/, or a string starting with either + char- or block- + followed by a device group name, as listed in + /proc/devices. The latter is useful to + whitelist all current and future devices belonging to a + specific device group at once. The device group is matched + according to file name globbing rules, you may hence use the + * and ? + wildcards. Examples: /dev/sda5 is a + path to a device node, referring to an ATA or SCSI block + device. char-pts and + char-alsa are specifiers for all pseudo + TTYs and all ALSA sound devices, + respectively. char-cpu/* is a specifier + matching all CPU related device groups. + + + + + DevicePolicy=auto|closed|strict + + + + Control the policy for allowing device access: + + + + + + means to only allow types of access that are + explicitly specified. + + + + + + + in addition, allows access to standard pseudo + devices including + /dev/null, + /dev/zero, + /dev/full, + /dev/random, and + /dev/urandom. + + + + + + + + + in addition, allows access to all devices if no + explicit DeviceAllow= is present. + This is the default. + + + + + + + + + Slice= + + + The name of the slice unit to place the unit + in. Defaults to system.slice for all + non-instantiated units of all unit types (except for slice + units themselves see below). Instance units are by default + placed in a subslice of system.slice + that is named after the template name. + + This option may be used to arrange systemd units in a + hierarchy of slices each of which might have resource + settings applied. + + For units of type slice, the only accepted value for + this setting is the parent slice. Since the name of a slice + unit implies the parent slice, it is hence redundant to ever + set this parameter directly for slice units. + + Special care should be taken when relying on the default slice assignment in templated service units + that have DefaultDependencies=no set, see + systemd.service5, section + "Automatic Dependencies" for details. + + + + + + Delegate= + + + Turns on delegation of further resource control + partitioning to processes of the unit. For unprivileged + services (i.e. those using the User= + setting), this allows processes to create a subhierarchy + beneath its control group path. For privileged services and + scopes, this ensures the processes will have all control + group controllers enabled. + + + + + + + + See Also + + systemd1, + systemd.unit5, + systemd.service5, + systemd.slice5, + systemd.scope5, + systemd.socket5, + systemd.mount5, + systemd.swap5, + systemd.directives7, + systemd.special7, + The documentation for control groups and specific controllers in the Linux kernel: + cgroups.txt, + cpuacct.txt, + memory.txt, + blkio-controller.txt. + + + diff --git a/src/grp-system/systemd/systemd.scope.xml b/src/grp-system/systemd/systemd.scope.xml new file mode 100644 index 0000000000..f69b2ef635 --- /dev/null +++ b/src/grp-system/systemd/systemd.scope.xml @@ -0,0 +1,108 @@ + + + + + + + + systemd.scope + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.scope + 5 + + + + systemd.scope + Scope unit configuration + + + + scope.scope + + + + Description + + Scope units are not configured via unit configuration files, + but are only created programmatically using the bus interfaces of + systemd. They are named similar to filenames. A unit whose name + ends in .scope refers to a scope unit. Scopes + units manage a set of system processes. Unlike service units, scope + units manage externally created processes, and do not fork off + processes on its own. + + The main purpose of scope units is grouping worker processes + of a system service for organization and for managing resources. + + systemd-run may + be used to easily launch a command in a new scope unit from the + command line. + + See the New + Control Group Interfaces for an introduction on how to make + use of scope units from programs. + + + + Automatic Dependencies + + Unless DefaultDependencies=false + is used, scope units will implicitly have dependencies of + type Conflicts= and + Before= on + shutdown.target. These ensure + that scope units are removed prior to system + shutdown. Only scope units involved with early boot or + late system shutdown should disable this option. + + + Additional implicit dependencies may be added as result of + resource control parameters as documented in + systemd.resource-control5. + + + + + See Also + + systemd1, + systemd-run1, + systemd.unit5, + systemd.resource-control5, + systemd.service5, + systemd.directives7. + + + + diff --git a/src/grp-system/systemd/systemd.service.xml b/src/grp-system/systemd/systemd.service.xml new file mode 100644 index 0000000000..875d368fcf --- /dev/null +++ b/src/grp-system/systemd/systemd.service.xml @@ -0,0 +1,1350 @@ + + + + + + + + systemd.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.service + 5 + + + + systemd.service + Service unit configuration + + + + service.service + + + + Description + + A unit configuration file whose name ends in + .service encodes information about a process + controlled and supervised by systemd. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic + [Unit] and [Install] + sections. The service specific configuration options are + configured in the [Service] section. + + Additional options are listed in + systemd.exec5, + which define the execution environment the commands are executed + in, and in + systemd.kill5, + which define the way the processes of the service are terminated, + and in + systemd.resource-control5, + which configure resource control settings for the processes of the + service. + + If a service is requested under a certain name but no unit + configuration file is found, systemd looks for a SysV init script + by the same name (with the .service suffix + removed) and dynamically creates a service unit from that script. + This is useful for compatibility with SysV. Note that this + compatibility is quite comprehensive but not 100%. For details + about the incompatibilities, see the Incompatibilities + with SysV document. + + + + Automatic Dependencies + + Services with Type=dbus set automatically + acquire dependencies of type Requires= and + After= on + dbus.socket. + + Socket activated service are automatically ordered after + their activated .socket units via an + automatic After= dependency. + + Unless DefaultDependencies= in the [Unit] is set to + , service units will implicitly have dependencies of type Requires= and + After= on sysinit.target, a dependency of type After= on + basic.target as well as dependencies of type Conflicts= and + Before= on shutdown.target. These ensure that normal service units pull in + basic system initialization, and are terminated cleanly prior to system shutdown. Only services involved with early + boot or late system shutdown should disable this option. + + Instanced service units (i.e. service units with an @ in their name) are assigned by + default a per-template slice unit (see + systemd.slice5), named after the + template unit, containing all instances of the specific template. This slice is normally stopped at shutdown, + together with all template instances. If that is not desired, set DefaultDependencies=no in the + template unit, and either define your own per-template slice unit file that also sets + DefaultDependencies=no, or set Slice=system.slice (or another suitable slice) + in the template unit. Also see + systemd.resource-control5. + + Additional implicit dependencies may be added as result of + execution and resource control parameters as documented in + systemd.exec5 + and + systemd.resource-control5. + + + + Options + + Service files must include a [Service] + section, which carries information about the service and the + process it supervises. A number of options that may be used in + this section are shared with other unit types. These options are + documented in + systemd.exec5 + and + systemd.kill5. + The options specific to the [Service] section + of service units are the following: + + + + Type= + + Configures the process start-up type for this + service unit. One of + , + , + , + , + or + . + + If set to (the default if + neither Type= nor + BusName=, but ExecStart= + are specified), it is expected that the process configured + with ExecStart= is the main process of the + service. In this mode, if the process offers functionality to + other processes on the system, its communication channels + should be installed before the daemon is started up (e.g. + sockets set up by systemd, via socket activation), as systemd + will immediately proceed starting follow-up units. + + If set to , it is expected that + the process configured with ExecStart= will + call fork() as part of its start-up. The + parent process is expected to exit when start-up is complete + and all communication channels are set up. The child continues + to run as the main daemon process. This is the behavior of + traditional UNIX daemons. If this setting is used, it is + recommended to also use the PIDFile= + option, so that systemd can identify the main process of the + daemon. systemd will proceed with starting follow-up units as + soon as the parent process exits. + + Behavior of is similar to + ; however, it is expected that the + process has to exit before systemd starts follow-up units. + RemainAfterExit= is particularly useful for + this type of service. This is the implied default if neither + Type= or ExecStart= are + specified. + + Behavior of is similar to + ; however, it is expected that the + daemon acquires a name on the D-Bus bus, as configured by + BusName=. systemd will proceed with + starting follow-up units after the D-Bus bus name has been + acquired. Service units with this option configured implicitly + gain dependencies on the dbus.socket + unit. This type is the default if BusName= + is specified. + + Behavior of is similar to + ; however, it is expected that the + daemon sends a notification message via + sd_notify3 + or an equivalent call when it has finished starting up. + systemd will proceed with starting follow-up units after this + notification message has been sent. If this option is used, + NotifyAccess= (see below) should be set to + open access to the notification socket provided by systemd. If + NotifyAccess= is missing or set to + , it will be forcibly set to + . Note that currently + Type= will not work + if used in combination with + PrivateNetwork=. + + Behavior of is very similar to + ; however, actual execution of the + service binary is delayed until all jobs are dispatched. This + may be used to avoid interleaving of output of shell services + with the status output on the console. + + + + + RemainAfterExit= + + Takes a boolean value that specifies whether + the service shall be considered active even when all its + processes exited. Defaults to . + + + + + GuessMainPID= + + Takes a boolean value that specifies whether + systemd should try to guess the main PID of a service if it + cannot be determined reliably. This option is ignored unless + is set and + is unset because for the other types + or with an explicitly configured PID file, the main PID is + always known. The guessing algorithm might come to incorrect + conclusions if a daemon consists of more than one process. If + the main PID cannot be determined, failure detection and + automatic restarting of a service will not work reliably. + Defaults to . + + + + + PIDFile= + + Takes an absolute file name pointing to the + PID file of this daemon. Use of this option is recommended for + services where Type= is set to + . systemd will read the PID of the + main process of the daemon after start-up of the service. + systemd will not write to the file configured here, although + it will remove the file after the service has shut down if it + still exists. + + + + + + BusName= + + Takes a D-Bus bus name that this service is + reachable as. This option is mandatory for services where + Type= is set to + . + + + + + ExecStart= + Commands with their arguments that are + executed when this service is started. The value is split into + zero or more command lines according to the rules described + below (see section "Command Lines" below). + + + When Type= is not + , only one command may and must be + given. When Type=oneshot is used, zero or + more commands may be specified. This can be specified by + providing multiple command lines in the same directive, or + alternatively, this directive may be specified more than once + with the same effect. If the empty string is assigned to this + option, the list of commands to start is reset, prior + assignments of this option will have no effect. If no + ExecStart= is specified, then the service + must have RemainAfterExit=yes set. + + For each of the specified commands, the first argument must be an absolute path to an + executable. Optionally, if this file name is prefixed with @, the second token will be + passed as argv[0] to the executed process, followed by the further arguments specified. If + the absolute filename is prefixed with -, an exit code of the command normally considered a + failure (i.e. non-zero exit status or abnormal exit due to signal) is ignored and considered success. If the + absolute path is prefixed with + then it is executed with full + privileges. -, @, and + may be used together and they + can appear in any order. + + If more than one command is specified, the commands are + invoked sequentially in the order they appear in the unit + file. If one of the commands fails (and is not prefixed with + -), other lines are not executed, and the + unit is considered failed. + + Unless Type=forking is set, the + process started via this command line will be considered the + main process of the daemon. + + + + + ExecStartPre= + ExecStartPost= + Additional commands that are executed before + or after the command in ExecStart=, + respectively. Syntax is the same as for + ExecStart=, except that multiple command + lines are allowed and the commands are executed one after the + other, serially. + + If any of those commands (not prefixed with + -) fail, the rest are not executed and the + unit is considered failed. + + ExecStart= commands are only run after + all ExecStartPre= commands that were not prefixed + with a - exit successfully. + + ExecStartPost= commands are only run after + the service has started successfully, as determined by Type= + (i.e. the process has been started for Type=simple + or Type=idle, the process exits successfully for + Type=oneshot, the initial process exits successfully + for Type=forking, READY=1 is sent + for Type=notify, or the BusName= + has been taken for Type=dbus). + + Note that ExecStartPre= may not be + used to start long-running processes. All processes forked + off by processes invoked via ExecStartPre= will + be killed before the next service process is run. + + Note that if any of the commands specified in ExecStartPre=, + ExecStart=, or ExecStartPost= fail (and are not prefixed with + -, see above) or time out before the service is fully up, execution continues with commands + specified in ExecStopPost=, the commands in ExecStop= are skipped. + + + + + ExecReload= + Commands to execute to trigger a configuration + reload in the service. This argument takes multiple command + lines, following the same scheme as described for + ExecStart= above. Use of this setting is + optional. Specifier and environment variable substitution is + supported here following the same scheme as for + ExecStart=. + + One additional, special environment variable is set: if + known, $MAINPID is set to the main process + of the daemon, and may be used for command lines like the + following: + + /bin/kill -HUP $MAINPID + + Note however that reloading a daemon by sending a signal + (as with the example line above) is usually not a good choice, + because this is an asynchronous operation and hence not + suitable to order reloads of multiple services against each + other. It is strongly recommended to set + ExecReload= to a command that not only + triggers a configuration reload of the daemon, but also + synchronously waits for it to complete. + + + + + ExecStop= + Commands to execute to stop the service + started via ExecStart=. This argument takes + multiple command lines, following the same scheme as described + for ExecStart= above. Use of this setting + is optional. After the commands configured in this option are + run, all processes remaining for a service are terminated + according to the KillMode= setting (see + systemd.kill5). + If this option is not specified, the process is terminated by + sending the signal specified in KillSignal= + when service stop is requested. Specifier and environment + variable substitution is supported (including + $MAINPID, see above). + + Note that it is usually not sufficient to specify a + command for this setting that only asks the service to + terminate (for example, by queuing some form of termination + signal for it), but does not wait for it to do so. Since the + remaining processes of the services are killed using + SIGKILL immediately after the command + exited, this would not result in a clean stop. The specified + command should hence be a synchronous operation, not an + asynchronous one. + + Note that the commands specified in ExecStop= are only executed when the service + started successfully first. They are not invoked if the service was never started at all, or in case its + start-up failed, for example because any of the commands specified in ExecStart=, + ExecStartPre= or ExecStartPost= failed (and weren't prefixed with + -, see above) or timed out. Use ExecStopPost= to invoke commands when a + service failed to start up correctly and is shut down again. + + It is recommended to use this setting for commands that communicate with the service requesting clean + termination. When the commands specified with this option are executed it should be assumed that the service is + still fully up and is able to react correctly to all commands. For post-mortem clean-up steps use + ExecStopPost= instead. + + + + ExecStopPost= + Additional commands that are executed after the service is stopped. This includes cases where + the commands configured in ExecStop= were used, where the service does not have any + ExecStop= defined, or where the service exited unexpectedly. This argument takes multiple + command lines, following the same scheme as described for ExecStart=. Use of these settings + is optional. Specifier and environment variable substitution is supported. Note that – unlike + ExecStop= – commands specified with this setting are invoked when a service failed to start + up correctly and is shut down again. + + It is recommended to use this setting for clean-up operations that shall be executed even when the + service failed to start up correctly. Commands configured with this setting need to be able to operate even if + the service failed starting up half-way and left incompletely initialized data around. As the service's + processes have been terminated already when the commands specified with this setting are executed they should + not attempt to communicate with them. + + + + RestartSec= + Configures the time to sleep before restarting + a service (as configured with Restart=). + Takes a unit-less value in seconds, or a time span value such + as "5min 20s". Defaults to 100ms. + + + + TimeoutStartSec= + Configures the time to wait for start-up. If a + daemon service does not signal start-up completion within the + configured time, the service will be considered failed and + will be shut down again. Takes a unit-less value in seconds, + or a time span value such as "5min 20s". Pass + infinity to disable the timeout logic. Defaults to + DefaultTimeoutStartSec= from the manager + configuration file, except when + Type=oneshot is used, in which case the + timeout is disabled by default (see + systemd-system.conf5). + + + + + TimeoutStopSec= + Configures the time to wait for stop. If a + service is asked to stop, but does not terminate in the + specified time, it will be terminated forcibly via + SIGTERM, and after another timeout of + equal duration with SIGKILL (see + KillMode= in + systemd.kill5). + Takes a unit-less value in seconds, or a time span value such + as "5min 20s". Pass infinity to disable the + timeout logic. Defaults to + DefaultTimeoutStopSec= from the manager + configuration file (see + systemd-system.conf5). + + + + + TimeoutSec= + A shorthand for configuring both + TimeoutStartSec= and + TimeoutStopSec= to the specified value. + + + + + RuntimeMaxSec= + + Configures a maximum time for the service to run. If this is used and the service has been + active for longer than the specified time it is terminated and put into a failure state. Note that this setting + does not have any effect on Type=oneshot services, as they terminate immediately after + activation completed. Pass infinity (the default) to configure no runtime + limit. + + + + WatchdogSec= + Configures the watchdog timeout for a service. + The watchdog is activated when the start-up is completed. The + service must call + sd_notify3 + regularly with WATCHDOG=1 (i.e. the + "keep-alive ping"). If the time between two such calls is + larger than the configured time, then the service is placed in + a failed state and it will be terminated with + SIGABRT. By setting + Restart= to , + , or + , the service will be automatically + restarted. The time configured here will be passed to the + executed service process in the + WATCHDOG_USEC= environment variable. This + allows daemons to automatically enable the keep-alive pinging + logic if watchdog support is enabled for the service. If this + option is used, NotifyAccess= (see below) + should be set to open access to the notification socket + provided by systemd. If NotifyAccess= is + not set, it will be implicitly set to . + Defaults to 0, which disables this feature. The service can + check whether the service manager expects watchdog keep-alive + notifications. See + sd_watchdog_enabled3 + for details. + sd_event_set_watchdog3 + may be used to enable automatic watchdog notification support. + + + + + Restart= + Configures whether the service shall be + restarted when the service process exits, is killed, or a + timeout is reached. The service process may be the main + service process, but it may also be one of the processes + specified with ExecStartPre=, + ExecStartPost=, + ExecStop=, + ExecStopPost=, or + ExecReload=. When the death of the process + is a result of systemd operation (e.g. service stop or + restart), the service will not be restarted. Timeouts include + missing the watchdog "keep-alive ping" deadline and a service + start, reload, and stop operation timeouts. + + Takes one of + , + , + , + , + , + , or + . + If set to (the default), the service will + not be restarted. If set to , it + will be restarted only when the service process exits cleanly. + In this context, a clean exit means an exit code of 0, or one + of the signals + SIGHUP, + SIGINT, + SIGTERM or + SIGPIPE, and + additionally, exit statuses and signals specified in + SuccessExitStatus=. If set to + , the service will be restarted + when the process exits with a non-zero exit code, is + terminated by a signal (including on core dump, but excluding + the aforementioned four signals), when an operation (such as + service reload) times out, and when the configured watchdog + timeout is triggered. If set to , + the service will be restarted when the process is terminated + by a signal (including on core dump, excluding the + aforementioned four signals), when an operation times out, or + when the watchdog timeout is triggered. If set to + , the service will be restarted only + if the service process exits due to an uncaught signal not + specified as a clean exit status. If set to + , the service will be restarted + only if the watchdog timeout for the service expires. If set + to , the service will be restarted + regardless of whether it exited cleanly or not, got terminated + abnormally by a signal, or hit a timeout. + + + Exit causes and the effect of the <varname>Restart=</varname> settings on them + + + + + + + Restart settings/Exit causes + + + + + + + + + + + + Clean exit code or signal + + X + X + + + + + + + Unclean exit code + + X + + X + + + + + + Unclean signal + + X + + X + X + X + + + + Timeout + + X + + X + X + + + + + Watchdog + + X + + X + X + + X + + + +
+ + As exceptions to the setting above, the service will not + be restarted if the exit code or signal is specified in + RestartPreventExitStatus= (see below). + Also, the services will always be restarted if the exit code + or signal is specified in + RestartForceExitStatus= (see below). + + Setting this to is the + recommended choice for long-running services, in order to + increase reliability by attempting automatic recovery from + errors. For services that shall be able to terminate on their + own choice (and avoid immediate restarting), + is an alternative choice. +
+
+ + + SuccessExitStatus= + Takes a list of exit status definitions that, + when returned by the main service process, will be considered + successful termination, in addition to the normal successful + exit code 0 and the signals SIGHUP, + SIGINT, SIGTERM, and + SIGPIPE. Exit status definitions can + either be numeric exit codes or termination signal names, + separated by spaces. For example: + + SuccessExitStatus=1 2 8 SIGKILL + + ensures that exit codes 1, 2, 8 and + the termination signal SIGKILL are + considered clean service terminations. + + + Note that if a process has a signal handler installed + and exits by calling + _exit2 + in response to a signal, the information about the signal is + lost. Programs should instead perform cleanup and kill + themselves with the same signal instead. See + Proper + handling of SIGINT/SIGQUIT — How to be a proper + program. + + This option may appear more than once, in which case the + list of successful exit statuses is merged. If the empty + string is assigned to this option, the list is reset, all + prior assignments of this option will have no + effect. + + + + RestartPreventExitStatus= + Takes a list of exit status definitions that, + when returned by the main service process, will prevent + automatic service restarts, regardless of the restart setting + configured with Restart=. Exit status + definitions can either be numeric exit codes or termination + signal names, and are separated by spaces. Defaults to the + empty list, so that, by default, no exit status is excluded + from the configured restart logic. For example: + + RestartPreventExitStatus=1 6 SIGABRT + + ensures that exit codes 1 and 6 and the termination signal + SIGABRT will not result in automatic + service restarting. This option may appear more than once, in + which case the list of restart-preventing statuses is + merged. If the empty string is assigned to this option, the + list is reset and all prior assignments of this option will + have no effect. + + + + RestartForceExitStatus= + Takes a list of exit status definitions that, + when returned by the main service process, will force automatic + service restarts, regardless of the restart setting configured + with Restart=. The argument format is + similar to + RestartPreventExitStatus=. + + + + PermissionsStartOnly= + Takes a boolean argument. If true, the + permission-related execution options, as configured with + User= and similar options (see + systemd.exec5 + for more information), are only applied to the process started + with + ExecStart=, and not to the various other + ExecStartPre=, + ExecStartPost=, + ExecReload=, + ExecStop=, and + ExecStopPost= + commands. If false, the setting is applied to all configured + commands the same way. Defaults to false. + + + + RootDirectoryStartOnly= + Takes a boolean argument. If true, the root + directory, as configured with the + RootDirectory= option (see + systemd.exec5 + for more information), is only applied to the process started + with ExecStart=, and not to the various + other ExecStartPre=, + ExecStartPost=, + ExecReload=, ExecStop=, + and ExecStopPost= commands. If false, the + setting is applied to all configured commands the same way. + Defaults to false. + + + + NonBlocking= + Set the O_NONBLOCK flag + for all file descriptors passed via socket-based activation. + If true, all file descriptors >= 3 (i.e. all except stdin, + stdout, and stderr) will have the + O_NONBLOCK flag set and hence are in + non-blocking mode. This option is only useful in conjunction + with a socket unit, as described in + systemd.socket5. + Defaults to false. + + + + NotifyAccess= + Controls access to the service status + notification socket, as accessible via the + sd_notify3 + call. Takes one of (the default), + or . If + , no daemon status updates are accepted + from the service processes, all status update messages are + ignored. If , only service updates sent + from the main process of the service are accepted. If + , all services updates from all members of + the service's control group are accepted. This option should + be set to open access to the notification socket when using + Type=notify or + WatchdogSec= (see above). If those options + are used but NotifyAccess= is not + configured, it will be implicitly set to + . + + + + Sockets= + Specifies the name of the socket units this + service shall inherit socket file descriptors from when the + service is started. Normally, it should not be necessary to use + this setting, as all socket file descriptors whose unit shares + the same name as the service (subject to the different unit + name suffix of course) are passed to the spawned + process. + + Note that the same socket file descriptors may be passed + to multiple processes simultaneously. Also note that a + different service may be activated on incoming socket traffic + than the one which is ultimately configured to inherit the + socket file descriptors. Or, in other words: the + Service= setting of + .socket units does not have to match the + inverse of the Sockets= setting of the + .service it refers to. + + This option may appear more than once, in which case the + list of socket units is merged. If the empty string is + assigned to this option, the list of sockets is reset, and all + prior uses of this setting will have no + effect. + + + + FailureAction= + Configure the action to take when the service enters a failed state. Takes the same values as + the unit setting StartLimitAction= and executes the same actions (see + systemd.unit5). Defaults to + . + + + + FileDescriptorStoreMax= + Configure how many file descriptors may be + stored in the service manager for the service using + sd_pid_notify_with_fds3's + FDSTORE=1 messages. This is useful for + implementing service restart schemes where the state is + serialized to /run and the file + descriptors passed to the service manager, to allow restarts + without losing state. Defaults to 0, i.e. no file descriptors + may be stored in the service manager by default. All file + descriptors passed to the service manager from a specific + service are passed back to the service's main process on the + next service restart. Any file descriptors passed to the + service manager are automatically closed when POLLHUP or + POLLERR is seen on them, or when the service is fully stopped + and no job queued or being executed for it. + + + + USBFunctionDescriptors= + Configure the location of a file containing + USB + FunctionFS descriptors, for implementation of USB + gadget functions. This is used only in conjunction with a + socket unit with ListenUSBFunction= + configured. The contents of this file are written to the + ep0 file after it is + opened. + + + + USBFunctionStrings= + Configure the location of a file containing + USB FunctionFS strings. Behavior is similar to + USBFunctionDescriptors= + above. + + +
+ + Check + systemd.exec5 + and + systemd.kill5 + for more settings. + +
+ + + Command lines + + This section describes command line parsing and + variable and specifier substitutions for + ExecStart=, + ExecStartPre=, + ExecStartPost=, + ExecReload=, + ExecStop=, and + ExecStopPost= options. + + Multiple command lines may be concatenated in a single + directive by separating them with semicolons (these semicolons + must be passed as separate words). Lone semicolons may be escaped + as \;. + + Each command line is split on whitespace, with the first + item being the command to execute, and the subsequent items being + the arguments. Double quotes ("...") and single quotes ('...') may + be used, in which case everything until the next matching quote + becomes part of the same argument. C-style escapes are also + supported. The table below contains the list of allowed escape + patterns. Only patterns which match the syntax in the table are + allowed; others will result in an error, and must be escaped by + doubling the backslash. Quotes themselves are removed after + parsing and escape sequences substituted. In addition, a trailing + backslash (\) may be used to merge lines. + + + This syntax is intended to be very similar to shell syntax, + but only the meta-characters and expansions described in the + following paragraphs are understood. Specifically, redirection + using + <, + <<, + >, and + >>, pipes using + |, running programs in the background using + &, and other elements of shell + syntax are not supported. + + The command to execute must be an absolute path name. It may + contain spaces, but control characters are not allowed. + + The command line accepts % specifiers as + described in + systemd.unit5. + Note that the first argument of the command line (i.e. the program + to execute) may not include specifiers. + + Basic environment variable substitution is supported. Use + ${FOO} as part of a word, or as a word of its + own, on the command line, in which case it will be replaced by the + value of the environment variable including all whitespace it + contains, resulting in a single argument. Use + $FOO as a separate word on the command line, in + which case it will be replaced by the value of the environment + variable split at whitespace, resulting in zero or more arguments. + For this type of expansion, quotes are respected when splitting + into words, and afterwards removed. + + Example: + + Environment="ONE=one" 'TWO=two two' +ExecStart=/bin/echo $ONE $TWO ${TWO} + + This will execute /bin/echo with four + arguments: one, two, + two, and two two. + + Example: + Environment=ONE='one' "TWO='two two' too" THREE= +ExecStart=/bin/echo ${ONE} ${TWO} ${THREE} +ExecStart=/bin/echo $ONE $TWO $THREE + This results in echo being + called twice, the first time with arguments + 'one', + 'two two' too, , + and the second time with arguments + one, two two, + too. + + + To pass a literal dollar sign, use $$. + Variables whose value is not known at expansion time are treated + as empty strings. Note that the first argument (i.e. the program + to execute) may not be a variable. + + Variables to be used in this fashion may be defined through + Environment= and + EnvironmentFile=. In addition, variables listed + in the section "Environment variables in spawned processes" in + systemd.exec5, + which are considered "static configuration", may be used (this + includes e.g. $USER, but not + $TERM). + + Note that shell command lines are not directly supported. If + shell command lines are to be used, they need to be passed + explicitly to a shell implementation of some kind. Example: + ExecStart=/bin/sh -c 'dmesg | tac' + + Example: + + ExecStart=/bin/echo one ; /bin/echo "two two" + + This will execute /bin/echo two times, + each time with one argument: one and + two two, respectively. Because two commands are + specified, Type=oneshot must be used. + + Example: + + ExecStart=/bin/echo / >/dev/null & \; \ +/bin/ls + + This will execute /bin/echo + with five arguments: /, + >/dev/null, + &, ;, and + /bin/ls. + + + C escapes supported in command lines and environment variables + + + + + + Literal + Actual value + + + + + \a + bell + + + \b + backspace + + + \f + form feed + + + \n + newline + + + \r + carriage return + + + \t + tab + + + \v + vertical tab + + + \\ + backslash + + + \" + double quotation mark + + + \' + single quotation mark + + + \s + space + + + \xxx + character number xx in hexadecimal encoding + + + \nnn + character number nnn in octal encoding + + + +
+
+ + + Examples + + + Simple service + + The following unit file creates a service that will + execute /usr/sbin/foo-daemon. Since no + Type= is specified, the default + Type= will be assumed. + systemd will assume the unit to be started immediately after the + program has begun executing. + + [Unit] +Description=Foo + +[Service] +ExecStart=/usr/sbin/foo-daemon + +[Install] +WantedBy=multi-user.target + + Note that systemd assumes here that the process started by + systemd will continue running until the service terminates. If + the program daemonizes itself (i.e. forks), please use + Type= instead. + + Since no ExecStop= was specified, + systemd will send SIGTERM to all processes started from this + service, and after a timeout also SIGKILL. This behavior can be + modified, see + systemd.kill5 + for details. + + Note that this unit type does not include any type of + notification when a service has completed initialization. For + this, you should use other unit types, such as + Type= if the service + understands systemd's notification protocol, + Type= if the service + can background itself or + Type= if the unit + acquires a DBus name once initialization is complete. See + below. + + + + Oneshot service + + Sometimes, units should just execute an action without + keeping active processes, such as a filesystem check or a + cleanup action on boot. For this, + Type= exists. Units + of this type will wait until the process specified terminates + and then fall back to being inactive. The following unit will + perform a cleanup action: + + [Unit] +Description=Cleanup old Foo data + +[Service] +Type=oneshot +ExecStart=/usr/sbin/foo-cleanup + +[Install] +WantedBy=multi-user.target + + Note that systemd will consider the unit to be in the + state "starting" until the program has terminated, so ordered + dependencies will wait for the program to finish before starting + themselves. The unit will revert to the "inactive" state after + the execution is done, never reaching the "active" state. That + means another request to start the unit will perform the action + again. + + Type= are the + only service units that may have more than one + ExecStart= specified. They will be executed + in order until either they are all successful or one of them + fails. + + + + Stoppable oneshot service + + Similarly to the oneshot services, there are sometimes + units that need to execute a program to set up something and + then execute another to shut it down, but no process remains + active while they are considered "started". Network + configuration can sometimes fall into this category. Another use + case is if a oneshot service shall not be executed each time + when they are pulled in as a dependency, but only the first + time. + + For this, systemd knows the setting + RemainAfterExit=, which + causes systemd to consider the unit to be active if the start + action exited successfully. This directive can be used with all + types, but is most useful with + Type= and + Type=. With + Type=, systemd waits + until the start action has completed before it considers the + unit to be active, so dependencies start only after the start + action has succeeded. With + Type=, dependencies + will start immediately after the start action has been + dispatched. The following unit provides an example for a simple + static firewall. + + [Unit] +Description=Simple firewall + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/local/sbin/simple-firewall-start +ExecStop=/usr/local/sbin/simple-firewall-stop + +[Install] +WantedBy=multi-user.target + + Since the unit is considered to be running after the start + action has exited, invoking systemctl start + on that unit again will cause no action to be taken. + + + + Traditional forking services + + Many traditional daemons/services background (i.e. fork, + daemonize) themselves when starting. Set + Type= in the + service's unit file to support this mode of operation. systemd + will consider the service to be in the process of initialization + while the original program is still running. Once it exits + successfully and at least a process remains (and + RemainAfterExit=), the + service is considered started. + + Often, a traditional daemon only consists of one process. + Therefore, if only one process is left after the original + process terminates, systemd will consider that process the main + process of the service. In that case, the + $MAINPID variable will be available in + ExecReload=, ExecStop=, + etc. + + In case more than one process remains, systemd will be + unable to determine the main process, so it will not assume + there is one. In that case, $MAINPID will not + expand to anything. However, if the process decides to write a + traditional PID file, systemd will be able to read the main PID + from there. Please set PIDFile= accordingly. + Note that the daemon should write that file before finishing + with its initialization. Otherwise, systemd might try to read the + file before it exists. + + The following example shows a simple daemon that forks and + just starts one process in the background: + + [Unit] +Description=Some simple daemon + +[Service] +Type=forking +ExecStart=/usr/sbin/my-simple-daemon -d + +[Install] +WantedBy=multi-user.target + + Please see + systemd.kill5 + for details on how you can influence the way systemd terminates + the service. + + + + DBus services + + For services that acquire a name on the DBus system bus, + use Type= and set + BusName= accordingly. The service should not + fork (daemonize). systemd will consider the service to be + initialized once the name has been acquired on the system bus. + The following example shows a typical DBus service: + + [Unit] +Description=Simple DBus service + +[Service] +Type=dbus +BusName=org.example.simple-dbus-service +ExecStart=/usr/sbin/simple-dbus-service + +[Install] +WantedBy=multi-user.target + + For bus-activatable services, do not + include a [Install] section in the systemd + service file, but use the SystemdService= + option in the corresponding DBus service file, for example + (/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service): + + [D-BUS Service] +Name=org.example.simple-dbus-service +Exec=/usr/sbin/simple-dbus-service +User=root +SystemdService=simple-dbus-service.service + + Please see + systemd.kill5 + for details on how you can influence the way systemd terminates + the service. + + + + Services that notify systemd about their initialization + + Type= services + are really easy to write, but have the major disadvantage of + systemd not being able to tell when initialization of the given + service is complete. For this reason, systemd supports a simple + notification protocol that allows daemons to make systemd aware + that they are done initializing. Use + Type= for this. A + typical service file for such a daemon would look like + this: + + [Unit] +Description=Simple notifying service + +[Service] +Type=notify +ExecStart=/usr/sbin/simple-notifying-service + +[Install] +WantedBy=multi-user.target + + Note that the daemon has to support systemd's notification + protocol, else systemd will think the service has not started yet + and kill it after a timeout. For an example of how to update + daemons to support this protocol transparently, take a look at + sd_notify3. + systemd will consider the unit to be in the 'starting' state + until a readiness notification has arrived. + + Please see + systemd.kill5 + for details on how you can influence the way systemd terminates + the service. + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.exec5, + systemd.resource-control5, + systemd.kill5, + systemd.directives7 + + + +
diff --git a/src/grp-system/systemd/systemd.slice.xml b/src/grp-system/systemd/systemd.slice.xml new file mode 100644 index 0000000000..eee98d99ee --- /dev/null +++ b/src/grp-system/systemd/systemd.slice.xml @@ -0,0 +1,132 @@ + + + + + + + + systemd.slice + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.slice + 5 + + + + systemd.slice + Slice unit configuration + + + + slice.slice + + + + Description + + A unit configuration file whose name ends in + .slice encodes information about a slice which + is a concept for hierarchically managing resources of a group of + processes. This management is performed by creating a node in the + Linux Control Group (cgroup) tree. Units that manage processes + (primarily scope and service units) may be assigned to a specific + slice. For each slice, certain resource limits may be set that + apply to all processes of all units contained in that + slice. Slices are organized hierarchically in a tree. The name of + the slice encodes the location in the tree. The name consists of a + dash-separated series of names, which describes the path to the + slice from the root slice. The root slice is named, + -.slice. Example: + foo-bar.slice is a slice that is located + within foo.slice, which in turn is located in + the root slice -.slice. + + + Note that slice units cannot be templated, nor is possible to add multiple names to a slice unit by creating + additional symlinks to it. + + By default, service and scope units are placed in + system.slice, virtual machines and containers + registered with + systemd-machined1 + are found in machine.slice, and user sessions + handled by + systemd-logind1 + in user.slice. See + systemd.special5 + for more information. + + See + systemd.unit5 + for the common options of all unit configuration + files. The common configuration items are configured + in the generic [Unit] and [Install] sections. The + slice specific configuration options are configured in + the [Slice] section. Currently, only generic resource control settings + as described in + systemd.resource-control5 are allowed. + + + See the New + Control Group Interfaces for an introduction on how to make + use of slice units from programs. + + + + Automatic Dependencies + + Slice units automatically gain dependencies of type + After= and Requires= on + their immediate parent slice unit. + + Unless DefaultDependencies=false is used in the [Unit] section, slice + units will implicitly have dependencies of type Conflicts= and Before= on + shutdown.target. These ensure that slice units are removed prior to system shutdown. Only + slice units involved with early boot or late system shutdown should disable this option. + + + + + See Also + + systemd1, + systemd.unit5, + systemd.resource-control5, + systemd.service5, + systemd.scope5, + systemd.special7, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.socket.xml b/src/grp-system/systemd/systemd.socket.xml new file mode 100644 index 0000000000..5bf54d8ef3 --- /dev/null +++ b/src/grp-system/systemd/systemd.socket.xml @@ -0,0 +1,860 @@ + + + + + + + + systemd.socket + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.socket + 5 + + + + systemd.socket + Socket unit configuration + + + + socket.socket + + + + Description + + A unit configuration file whose name ends in + .socket encodes information about an IPC or + network socket or a file system FIFO controlled and supervised by + systemd, for socket-based activation. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The socket specific configuration options are + configured in the [Socket] section. + + Additional options are listed in + systemd.exec5, + which define the execution environment the + , , + and + commands are executed in, and in + systemd.kill5, + which define the way the processes are terminated, and in + systemd.resource-control5, + which configure resource control settings for the processes of the + socket. + + For each socket file, a matching service file must exist, + describing the service to start on incoming traffic on the socket + (see + systemd.service5 + for more information about .service files). The name of the + .service unit is by default the same as the name of the .socket + unit, but can be altered with the option + described below. Depending on the setting of the + option described below, this .service + unit must either be named like the .socket unit, but with the + suffix replaced, unless overridden with ; + or it must be a template unit named the same way. Example: a + socket file foo.socket needs a matching + service foo.service if + is set. If + is set, a service template file + foo@.service must exist from which services + are instantiated for each incoming connection. + + Unless DefaultDependencies= in the [Unit] section is set to + , socket units will implicitly have dependencies of type Requires= and + After= on sysinit.target as well as dependencies of type + Conflicts= and Before= on shutdown.target. These ensure + that socket units pull in basic system initialization, and are terminated cleanly prior to system shutdown. Only + sockets involved with early boot or late system shutdown should disable this option. + + Socket units will have a Before= + dependency on the service which they trigger added implicitly. No + implicit WantedBy= or + RequiredBy= dependency from the socket to the + service is added. This means that the service may be started + without the socket, in which case it must be able to open sockets + by itself. To prevent this, an explicit + Requires= dependency may be added. + + Socket units may be used to implement on-demand starting of + services, as well as parallelized starting of services. See the + blog stories linked at the end for an introduction. + + Note that the daemon software configured for socket + activation with socket units needs to be able to accept sockets + from systemd, either via systemd's native socket passing interface + (see + sd_listen_fds3 + for details) or via the traditional + inetd8-style + socket passing (i.e. sockets passed in via standard input and + output, using StandardInput=socket in the + service file). + + + + Automatic Dependencies + + Socket units automatically gain a Before= + dependency on the service units they activate. + + Socket units referring to file system paths (such as AF_UNIX + sockets or FIFOs) implicitly gain Requires= and + After= dependencies on all mount units + necessary to access those paths. + + Socket units using the BindToDevice= + setting automatically gain a BindsTo= and + After= dependency on the device unit + encapsulating the specified network interface. + + If DefaultDependencies=yes is set (the + default), socket units automatically gain a + Before= dependency on + sockets.target. They also gain a pair of + After= and Requires= + dependency on sysinit.target, and a pair of + Before= and Conflicts= + dependencies on shutdown.target. These + dependencies ensure that the socket unit is started before normal + services at boot, and is stopped on shutdown. + + Additional implicit dependencies may be added as result of + execution and resource control parameters as documented in + systemd.exec5 + and + systemd.resource-control5. + + + + Options + + Socket files must include a [Socket] section, which carries + information about the socket or FIFO it supervises. A number of + options that may be used in this section are shared with other + unit types. These options are documented in + systemd.exec5 + and + systemd.kill5. + The options specific to the [Socket] section of socket units are + the following: + + + + ListenStream= + ListenDatagram= + ListenSequentialPacket= + Specifies an address to listen on for a stream + (SOCK_STREAM), datagram + (SOCK_DGRAM), or sequential packet + (SOCK_SEQPACKET) socket, respectively. + The address can be written in various formats: + + If the address starts with a slash + (/), it is read as file system socket in + the AF_UNIX socket family. + + If the address starts with an at symbol + (@), it is read as abstract namespace + socket in the AF_UNIX family. The + @ is replaced with a + NUL character before binding. For + details, see + unix7. + + If the address string is a single number, it is read as + port number to listen on via IPv6. Depending on the value of + BindIPv6Only= (see below) this might result + in the service being available via both IPv6 and IPv4 + (default) or just via IPv6. + + + If the address string is a string in the format + v.w.x.y:z, it is read as IPv4 specifier for listening on an + address v.w.x.y on a port z. + + If the address string is a string in the format [x]:y, + it is read as IPv6 address x on a port y. Note that this might + make the service available via IPv4, too, depending on the + BindIPv6Only= setting (see below). + + + Note that SOCK_SEQPACKET (i.e. + ListenSequentialPacket=) is only available + for AF_UNIX sockets. + SOCK_STREAM (i.e. + ListenStream=) when used for IP sockets + refers to TCP sockets, SOCK_DGRAM (i.e. + ListenDatagram=) to UDP. + + These options may be specified more than once, in which + case incoming traffic on any of the sockets will trigger + service activation, and all listed sockets will be passed to + the service, regardless of whether there is incoming traffic + on them or not. If the empty string is assigned to any of + these options, the list of addresses to listen on is reset, + all prior uses of any of these options will have no + effect. + + It is also possible to have more than one socket unit + for the same service when using Service=, + and the service will receive all the sockets configured in all + the socket units. Sockets configured in one unit are passed in + the order of configuration, but no ordering between socket + units is specified. + + If an IP address is used here, it is often desirable to + listen on it before the interface it is configured on is up + and running, and even regardless of whether it will be up and + running at any point. To deal with this, it is recommended to + set the FreeBind= option described + below. + + + + ListenFIFO= + Specifies a file system FIFO to listen on. + This expects an absolute file system path as argument. + Behavior otherwise is very similar to the + ListenDatagram= directive + above. + + + + ListenSpecial= + Specifies a special file in the file system to + listen on. This expects an absolute file system path as + argument. Behavior otherwise is very similar to the + ListenFIFO= directive above. Use this to + open character device nodes as well as special files in + /proc and + /sys. + + + + ListenNetlink= + Specifies a Netlink family to create a socket + for to listen on. This expects a short string referring to the + AF_NETLINK family name (such as + audit or kobject-uevent) + as argument, optionally suffixed by a whitespace followed by a + multicast group integer. Behavior otherwise is very similar to + the ListenDatagram= directive + above. + + + + ListenMessageQueue= + Specifies a POSIX message queue name to listen + on. This expects a valid message queue name (i.e. beginning + with /). Behavior otherwise is very similar to the + ListenFIFO= directive above. On Linux + message queue descriptors are actually file descriptors and + can be inherited between processes. + + + + ListenUSBFunction= + Specifies a USB + FunctionFS endpoint location to listen on, for + implementation of USB gadget functions. This expects an + absolute file system path as the argument. Behavior otherwise + is very similar to the ListenFIFO= + directive above. Use this to open the FunctionFS endpoint + ep0. When using this option, the + activated service has to have the + USBFunctionDescriptors= and + USBFunctionStrings= options set. + + + + + SocketProtocol= + Takes a one of + or . Specifies a socket protocol + (IPPROTO_UDPLITE) UDP-Lite + (IPPROTO_SCTP) SCTP socket respectively. + + + + + BindIPv6Only= + Takes a one of , + or . Controls + the IPV6_V6ONLY socket option (see + ipv67 + for details). If , IPv6 sockets bound + will be accessible via both IPv4 and IPv6. If + , they will be accessible via IPv6 + only. If (which is the default, + surprise!), the system wide default setting is used, as + controlled by + /proc/sys/net/ipv6/bindv6only, which in + turn defaults to the equivalent of + . + + + + + Backlog= + Takes an unsigned integer argument. Specifies + the number of connections to queue that have not been accepted + yet. This setting matters only for stream and sequential + packet sockets. See + listen2 + for details. Defaults to SOMAXCONN (128). + + + + BindToDevice= + Specifies a network interface name to bind + this socket to. If set, traffic will only be accepted from the + specified network interfaces. This controls the + SO_BINDTODEVICE socket option (see socket7 + for details). If this option is used, an automatic dependency + from this socket unit on the network interface device unit + (systemd.device5 + is created. Note that setting this parameter might result in + additional dependencies to be added to the unit (see + above). + + + + SocketUser= + SocketGroup= + + Takes a UNIX user/group name. When specified, + all AF_UNIX sockets and FIFO nodes in the file system are + owned by the specified user and group. If unset (the default), + the nodes are owned by the root user/group (if run in system + context) or the invoking user/group (if run in user context). + If only a user is specified but no group, then the group is + derived from the user's default group. + + + + SocketMode= + If listening on a file system socket or FIFO, + this option specifies the file system access mode used when + creating the file node. Takes an access mode in octal + notation. Defaults to 0666. + + + + DirectoryMode= + If listening on a file system socket or FIFO, + the parent directories are automatically created if needed. + This option specifies the file system access mode used when + creating these directories. Takes an access mode in octal + notation. Defaults to 0755. + + + + Accept= + Takes a boolean argument. If true, a service + instance is spawned for each incoming connection and only the + connection socket is passed to it. If false, all listening + sockets themselves are passed to the started service unit, and + only one service unit is spawned for all connections (also see + above). This value is ignored for datagram sockets and FIFOs + where a single service unit unconditionally handles all + incoming traffic. Defaults to . For + performance reasons, it is recommended to write new daemons + only in a way that is suitable for + . A daemon listening on an + AF_UNIX socket may, but does not need to, + call + close2 + on the received socket before exiting. However, it must not + unlink the socket from a file system. It should not invoke + shutdown2 + on sockets it got with Accept=false, but it + may do so for sockets it got with + Accept=true set. Setting + Accept=true is mostly useful to allow + daemons designed for usage with + inetd8 + to work unmodified with systemd socket + activation. + + For IPv4 and IPv6 connections, the REMOTE_ADDR + environment variable will contain the remote IP address, and REMOTE_PORT + will contain the remote port. This is the same as the format used by CGI. + For SOCK_RAW, the port is the IP protocol. + + + + Writable= + Takes a boolean argument. May only be used in + conjunction with ListenSpecial=. If true, + the specified special file is opened in read-write mode, if + false, in read-only mode. Defaults to false. + + + + MaxConnections= + The maximum number of connections to + simultaneously run services instances for, when + is set. If more concurrent + connections are coming in, they will be refused until at least + one existing connection is terminated. This setting has no + effect on sockets configured with + or datagram sockets. Defaults to + 64. + + + + KeepAlive= + Takes a boolean argument. If true, the TCP/IP + stack will send a keep alive message after 2h (depending on + the configuration of + /proc/sys/net/ipv4/tcp_keepalive_time) + for all TCP streams accepted on this socket. This controls the + SO_KEEPALIVE socket option (see + socket7 + and the TCP + Keepalive HOWTO for details.) Defaults to + . + + + + KeepAliveTimeSec= + Takes time (in seconds) as argument. The connection needs to remain + idle before TCP starts sending keepalive probes. This controls the TCP_KEEPIDLE + socket option (see + socket7 + and the TCP + Keepalive HOWTO for details.) + Defaults value is 7200 seconds (2 hours). + + + + KeepAliveIntervalSec= + Takes time (in seconds) as argument between + individual keepalive probes, if the socket option SO_KEEPALIVE + has been set on this socket. This controls + the TCP_KEEPINTVL socket option (see + socket7 + and the TCP + Keepalive HOWTO for details.) Defaults value is 75 + seconds. + + + + KeepAliveProbes= + Takes an integer as argument. It is the number of + unacknowledged probes to send before considering the + connection dead and notifying the application layer. This + controls the TCP_KEEPCNT socket option (see + socket7 + and the TCP + Keepalive HOWTO for details.) Defaults value is + 9. + + + + NoDelay= + Takes a boolean argument. TCP Nagle's + algorithm works by combining a number of small outgoing + messages, and sending them all at once. This controls the + TCP_NODELAY socket option (see + tcp7 + Defaults to . + + + + Priority= + Takes an integer argument controlling the + priority for all traffic sent from this socket. This controls + the SO_PRIORITY socket option (see + socket7 + for details.). + + + + DeferAcceptSec= + + Takes time (in seconds) as argument. If set, + the listening process will be awakened only when data arrives + on the socket, and not immediately when connection is + established. When this option is set, the + TCP_DEFER_ACCEPT socket option will be + used (see + tcp7), + and the kernel will ignore initial ACK packets without any + data. The argument specifies the approximate amount of time + the kernel should wait for incoming data before falling back + to the normal behavior of honouring empty ACK packets. This + option is beneficial for protocols where the client sends the + data first (e.g. HTTP, in contrast to SMTP), because the + server process will not be woken up unnecessarily before it + can take any action. + + + If the client also uses the + TCP_DEFER_ACCEPT option, the latency of + the initial connection may be reduced, because the kernel will + send data in the final packet establishing the connection (the + third packet in the "three-way handshake"). + + Disabled by default. + + + + + ReceiveBuffer= + SendBuffer= + Takes an integer argument controlling the + receive or send buffer sizes of this socket, respectively. + This controls the SO_RCVBUF and SO_SNDBUF socket options (see + socket7 + for details.). The usual suffixes K, M, G are supported and + are understood to the base of 1024. + + + + IPTOS= + Takes an integer argument controlling the IP + Type-Of-Service field for packets generated from this socket. + This controls the IP_TOS socket option (see + ip7 + for details.). Either a numeric string or one of + , , + or may + be specified. + + + + IPTTL= + Takes an integer argument controlling the IPv4 + Time-To-Live/IPv6 Hop-Count field for packets generated from + this socket. This sets the IP_TTL/IPV6_UNICAST_HOPS socket + options (see + ip7 + and + ipv67 + for details.) + + + + Mark= + Takes an integer value. Controls the firewall + mark of packets generated by this socket. This can be used in + the firewall logic to filter packets from this socket. This + sets the SO_MARK socket option. See + iptables8 + for details. + + + + ReusePort= + Takes a boolean value. If true, allows + multiple + bind2s + to this TCP or UDP port. This controls the SO_REUSEPORT socket + option. See + socket7 + for details. + + + + SmackLabel= + SmackLabelIPIn= + SmackLabelIPOut= + Takes a string value. Controls the extended + attributes security.SMACK64, + security.SMACK64IPIN and + security.SMACK64IPOUT, respectively, i.e. + the security label of the FIFO, or the security label for the + incoming or outgoing connections of the socket, respectively. + See Smack.txt + for details. + + + + SELinuxContextFromNet= + Takes a boolean argument. When true, systemd + will attempt to figure out the SELinux label used for the + instantiated service from the information handed by the peer + over the network. Note that only the security level is used + from the information provided by the peer. Other parts of the + resulting SELinux context originate from either the target + binary that is effectively triggered by socket unit or from + the value of the SELinuxContext= option. + This configuration option only affects sockets with + Accept= mode set to + true. Also note that this option is useful + only when MLS/MCS SELinux policy is deployed. Defaults to + false. + + + + PipeSize= + Takes a size in bytes. Controls the pipe + buffer size of FIFOs configured in this socket unit. See + fcntl2 + for details. The usual suffixes K, M, G are supported and are + understood to the base of 1024. + + + + MessageQueueMaxMessages=, + MessageQueueMessageSize= + These two settings take integer values and + control the mq_maxmsg field or the mq_msgsize field, + respectively, when creating the message queue. Note that + either none or both of these variables need to be set. See + mq_setattr3 + for details. + + + + FreeBind= + Takes a boolean value. Controls whether the + socket can be bound to non-local IP addresses. This is useful + to configure sockets listening on specific IP addresses before + those IP addresses are successfully configured on a network + interface. This sets the IP_FREEBIND socket option. For + robustness reasons it is recommended to use this option + whenever you bind a socket to a specific IP address. Defaults + to . + + + + Transparent= + Takes a boolean value. Controls the + IP_TRANSPARENT socket option. Defaults to + . + + + + Broadcast= + Takes a boolean value. This controls the + SO_BROADCAST socket option, which allows broadcast datagrams + to be sent from this socket. Defaults to + . + + + + PassCredentials= + Takes a boolean value. This controls the + SO_PASSCRED socket option, which allows + AF_UNIX sockets to receive the + credentials of the sending process in an ancillary message. + Defaults to . + + + + PassSecurity= + Takes a boolean value. This controls the + SO_PASSSEC socket option, which allows + AF_UNIX sockets to receive the security + context of the sending process in an ancillary message. + Defaults to . + + + + TCPCongestion= + Takes a string value. Controls the TCP + congestion algorithm used by this socket. Should be one of + "westwood", "veno", "cubic", "lp" or any other available + algorithm supported by the IP stack. This setting applies only + to stream sockets. + + + + ExecStartPre= + ExecStartPost= + Takes one or more command lines, which are + executed before or after the listening sockets/FIFOs are + created and bound, respectively. The first token of the + command line must be an absolute filename, then followed by + arguments for the process. Multiple command lines may be + specified following the same scheme as used for + ExecStartPre= of service unit + files. + + + + ExecStopPre= + ExecStopPost= + Additional commands that are executed before + or after the listening sockets/FIFOs are closed and removed, + respectively. Multiple command lines may be specified + following the same scheme as used for + ExecStartPre= of service unit + files. + + + + TimeoutSec= + Configures the time to wait for the commands + specified in ExecStartPre=, + ExecStartPost=, + ExecStopPre= and + ExecStopPost= to finish. If a command does + not exit within the configured time, the socket will be + considered failed and be shut down again. All commands still + running will be terminated forcibly via + SIGTERM, and after another delay of this + time with SIGKILL. (See + in + systemd.kill5.) + Takes a unit-less value in seconds, or a time span value such + as "5min 20s". Pass 0 to disable the + timeout logic. Defaults to + DefaultTimeoutStartSec= from the manager + configuration file (see + systemd-system.conf5). + + + + + Service= + Specifies the service unit name to activate on + incoming traffic. This setting is only allowed for sockets + with Accept=no. It defaults to the service + that bears the same name as the socket (with the suffix + replaced). In most cases, it should not be necessary to use + this option. Note that setting this parameter might result in + additional dependencies to be added to the unit (see + above). + + + + RemoveOnStop= + Takes a boolean argument. If enabled, any file + nodes created by this socket unit are removed when it is + stopped. This applies to AF_UNIX sockets in the file system, + POSIX message queues, FIFOs, as well as any symlinks to them + configured with Symlinks=. Normally, it + should not be necessary to use this option, and is not + recommended as services might continue to run after the socket + unit has been terminated and it should still be possible to + communicate with them via their file system node. Defaults to + off. + + + + Symlinks= + Takes a list of file system paths. The + specified paths will be created as symlinks to the AF_UNIX + socket path or FIFO path of this socket unit. If this setting + is used, only one AF_UNIX socket in the file system or one + FIFO may be configured for the socket unit. Use this option to + manage one or more symlinked alias names for a socket, binding + their lifecycle together. Defaults to the empty + list. + + + + FileDescriptorName= + Assigns a name to all file descriptors this + socket unit encapsulates. This is useful to help activated + services identify specific file descriptors, if multiple fds + are passed. Services may use the + sd_listen_fds_with_names3 + call to acquire the names configured for the received file + descriptors. Names may contain any ASCII character, but must + exclude control characters and :, and must + be at most 255 characters in length. If this setting is not + used, the file descriptor name defaults to the name of the + socket unit, including its .socket + suffix. + + + + TriggerLimitIntervalSec= + TriggerLimitBurst= + + Configures a limit on how often this socket unit my be activated within a specific time + interval. The TriggerLimitIntervalSec= may be used to configure the length of the time + interval in the usual time units us, ms, s, + min, h, … and defaults to 2s (See + systemd.time7 for details on + the various time units understood). The TriggerLimitBurst= setting takes a positive integer + value and specifies the number of permitted activations per time interval, and defaults to 200 for + Accept=yes sockets (thus by default permitting 200 activations per 2s), and 20 otherwise (20 + activations per 2s). Set either to 0 to disable any form of trigger rate limiting. If the limit is hit, the + socket unit is placed into a failure mode, and will not be connectible anymore until restarted. Note that this + limit is enforced before the service activation is enqueued. + + + + + Check + systemd.exec5 + and + systemd.kill5 + for more settings. + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.exec5, + systemd.kill5, + systemd.resource-control5, + systemd.service5, + systemd.directives7, + sd_listen_fds3, + sd_listen_fds_with_names3 + + + For more extensive descriptions see the "systemd for Developers" series: + Socket Activation, + Socket Activation, part II, + Converting inetd Services, + Socket Activated Internet Services and OS Containers. + + + + diff --git a/src/grp-system/systemd/systemd.special.xml b/src/grp-system/systemd/systemd.special.xml new file mode 100644 index 0000000000..18ad8f92e5 --- /dev/null +++ b/src/grp-system/systemd/systemd.special.xml @@ -0,0 +1,941 @@ + + + + + + + + + systemd.special + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.special + 7 + + + + systemd.special + Special systemd units + + + + basic.target, + bluetooth.target, + ctrl-alt-del.target, + cryptsetup.target, + cryptsetup-pre.target, + dbus.service, + dbus.socket, + default.target, + display-manager.service, + emergency.target, + exit.target, + final.target, + getty.target, + graphical.target, + halt.target, + hibernate.target, + hybrid-sleep.target, + initrd-fs.target, + kbrequest.target, + kexec.target, + local-fs.target, + local-fs-pre.target, + multi-user.target, + network.target, + network-online.target, + network-pre.target, + nss-lookup.target, + nss-user-lookup.target, + paths.target, + poweroff.target, + printer.target, + reboot.target, + remote-fs.target, + remote-fs-pre.target, + rescue.target, + initrd-root-device.target, + initrd-root-fs.target, + rpcbind.target, + runlevel2.target, + runlevel3.target, + runlevel4.target, + runlevel5.target, + shutdown.target, + sigpwr.target, + sleep.target, + slices.target, + smartcard.target, + sockets.target, + sound.target, + suspend.target, + swap.target, + sysinit.target, + syslog.socket, + system-update.target, + time-sync.target, + timers.target, + umount.target, + -.slice, + system.slice, + user.slice, + machine.slice + + + + Description + + A few units are treated specially by systemd. They have + special internal semantics and cannot be renamed. + + + + Special System Units + + + + basic.target + + A special target unit covering basic boot-up. + + systemd automatically adds dependency of the type + After= for this target unit to all + services (except for those with + DefaultDependencies=no). + + Usually, this should pull-in all local mount points plus + /var, /tmp and + /var/tmp, swap devices, sockets, timers, + path units and other basic initialization necessary for general + purpose daemons. The mentioned mount points are special cased + to allow them to be remote. + + + This target usually does not pull in any non-target units + directly, but rather does so indirectly via other early boot targets. + It is instead meant as a synchronization point for late boot + services. Refer to + bootup7 + for details on the targets involved. + + + + + + ctrl-alt-del.target + + systemd starts this target whenever Control+Alt+Del is + pressed on the console. Usually, this should be aliased + (symlinked) to reboot.target. + + + + cryptsetup.target + + A target that pulls in setup services for all + encrypted block devices. + + + + dbus.service + + A special unit for the D-Bus bus daemon. As soon as + this service is fully started up systemd will connect to it + and register its service. + + + + dbus.socket + + A special unit for the D-Bus system bus socket. All + units with Type=dbus automatically gain a + dependency on this unit. + + + + default.target + + The default unit systemd starts at bootup. Usually, + this should be aliased (symlinked) to + multi-user.target or + graphical.target. + + The default unit systemd starts at bootup can be + overridden with the systemd.unit= kernel + command line option. + + + + display-manager.service + + The display manager service. Usually, this should be + aliased (symlinked) to gdm.service or a + similar display manager service. + + + + emergency.target + + A special target unit that starts an emergency shell on the main console. This target does not pull in + any services or mounts. It is the most minimal version of starting the system in order to acquire an + interactive shell; the only processes running are usually just the system manager (PID 1) and the shell + process. This unit is supposed to be used with the kernel command line option + systemd.unit=; it is also used when a file system check on a required file system fails, + and boot-up cannot continue. Compare with rescue.target, which serves a similar purpose, + but also starts the most basic services and mounts all file systems. + + Use the systemd.unit=emergency.target kernel command line option to boot into this + mode. A short alias for this kernel command line option is emergency, for compatibility + with SysV. + + In many ways booting into emergency.target is similar to the effect of booting + with init=/bin/sh on the kernel command line, except that emergency mode provides you with + the full system and service manager, and allows starting individual units in order to continue the boot + process in steps. + + + + exit.target + + A special service unit for shutting down the system or + user service manager. It is equivalent to + poweroff.target on non-container + systems, and also works in containers. + + systemd will start this unit when it receives a + request to shut down over D-Bus or a + SIGTERM or SIGINT + signal when running as user service daemon. + + Normally, this (indirectly) pulls in + shutdown.target, which in turn should be + conflicted by all units that want to be scheduled for + shutdown when the service manager starts to exit. + + + + final.target + + A special target unit that is used during the shutdown + logic and may be used to pull in late services after all + normal services are already terminated and all mounts + unmounted. + + + + + getty.target + + A special target unit that pulls in statically + configured local TTY getty instances. + + + + + graphical.target + + A special target unit for setting up a graphical login + screen. This pulls in + multi-user.target. + + Units that are needed for graphical logins shall add + Wants= dependencies for their unit to + this unit (or multi-user.target) during + installation. This is best configured via + WantedBy=graphical.target in the unit's + [Install] section. + + + + hibernate.target + + A special target unit for hibernating the system. This + pulls in sleep.target. + + + + hybrid-sleep.target + + A special target unit for hibernating and suspending + the system at the same time. This pulls in + sleep.target. + + + + halt.target + + A special target unit for shutting down and halting + the system. Note that this target is distinct from + poweroff.target in that it generally + really just halts the system rather than powering it + down. + + Applications wanting to halt the system should start + this unit. + + + + initrd-fs.target + + systemd-fstab-generator3 + automatically adds dependencies of type + Before= to + sysroot-usr.mount and all mount points + found in /etc/fstab that have + and not have + mount options set. + + + + kbrequest.target + + systemd starts this target whenever Alt+ArrowUp is + pressed on the console. This is a good candidate to be + aliased (symlinked) to + rescue.target. + + + + kexec.target + + A special target unit for shutting down and rebooting + the system via kexec. + + Applications wanting to reboot the system with kexec + should start this unit. + + + + local-fs.target + + systemd-fstab-generator3 + automatically adds dependencies of type + Before= to all mount units that refer to + local mount points for this target unit. In addition, it + adds dependencies of type Wants= to this + target unit for those mounts listed in + /etc/fstab that have the + mount option set. + + + + multi-user.target + + A special target unit for setting up a multi-user + system (non-graphical). This is pulled in by + graphical.target. + + Units that are needed for a multi-user system shall + add Wants= dependencies for their unit to + this unit during installation. This is best configured via + WantedBy=multi-user.target in the unit's + [Install] section. + + + + network-online.target + + Units that strictly require a configured network + connection should pull in + network-online.target (via a + Wants= type dependency) and order + themselves after it. This target unit is intended to pull in + a service that delays further execution until the network is + sufficiently set up. What precisely this requires is left to + the implementation of the network managing service. + + Note the distinction between this unit and + network.target. This unit is an active + unit (i.e. pulled in by the consumer rather than the + provider of this functionality) and pulls in a service which + possibly adds substantial delays to further execution. In + contrast, network.target is a passive + unit (i.e. pulled in by the provider of the functionality, + rather than the consumer) that usually does not delay + execution much. Usually, network.target + is part of the boot of most systems, while + network-online.target is not, except + when at least one unit requires it. Also see Running + Services After the Network is up for more + information. + + All mount units for remote network file systems + automatically pull in this unit, and order themselves after + it. Note that networking daemons that simply provide + functionality to other hosts generally do not need to pull + this in. + + + + paths.target + + A special target unit that sets up all path units (see + systemd.path5 + for details) that shall be active after boot. + + It is recommended that path units installed by + applications get pulled in via Wants= + dependencies from this unit. This is best configured via a + WantedBy=paths.target in the path unit's + [Install] section. + + + + poweroff.target + + A special target unit for shutting down and powering + off the system. + + Applications wanting to power off the system should + start this unit. + + runlevel0.target is an alias for + this target unit, for compatibility with SysV. + + + + reboot.target + + A special target unit for shutting down and rebooting + the system. + + Applications wanting to reboot the system should start + this unit. + + runlevel6.target is an alias for + this target unit, for compatibility with SysV. + + + + remote-fs.target + + Similar to local-fs.target, but + for remote mount points. + + systemd automatically adds dependencies of type + After= for this target unit to all SysV + init script service units with an LSB header referring to + the $remote_fs facility. + + + + rescue.target + + A special target unit that pulls in the base system (including system mounts) and spawns a rescue + shell. Isolate to this target in order to administer the system in single-user mode with all file systems + mounted but with no services running, except for the most basic. Compare with + emergency.target, which is much more reduced and does not provide the file systems or + most basic services. + + runlevel1.target is an alias for this target unit, for compatibility with + SysV. + + Use the systemd.unit=rescue.target kernel command line option to boot into this + mode. A short alias for this kernel command line option is 1, for compatibility with + SysV. + + + + initrd-root-device.target + + A special initrd target unit that is reached when the root filesystem device is available, but before + it has been mounted. + systemd-fstab-generator3 + and + systemd-gpt-auto-generator3 + automatically setup the appropriate dependencies to make this happen. + + + + + initrd-root-fs.target + + systemd-fstab-generator3 + automatically adds dependencies of type + Before= to the + sysroot.mount unit, which is generated + from the kernel command line. + + + + + runlevel2.target + runlevel3.target + runlevel4.target + runlevel5.target + + These are targets that are called whenever the SysV + compatibility code asks for runlevel 2, 3, 4, 5, + respectively. It is a good idea to make this an alias for + (i.e. symlink to) graphical.target + (for runlevel 5) or multi-user.target + (the others). + + + + shutdown.target + + A special target unit that terminates the services on + system shutdown. + + Services that shall be terminated on system shutdown + shall add Conflicts= and + Before= dependencies to this unit for + their service unit, which is implicitly done when + DefaultDependencies=yes is set (the + default). + + + + sigpwr.target + + A special target that is started when systemd receives + the SIGPWR process signal, which is normally sent by the + kernel or UPS daemons when power fails. + + + + sleep.target + + A special target unit that is pulled in by + suspend.target, + hibernate.target and + hybrid-sleep.target and may be used to + hook units into the sleep state logic. + + + + slices.target + + A special target unit that sets up all slice units (see + systemd.slice5 for + details) that shall be active after boot. By default the generic user.slice, + system.slice, machines.slice slice units, as well as the root + slice unit -.slice are pulled in and ordered before this unit (see below). + + It's a good idea to add WantedBy=slices.target lines to the [Install] + section of all slices units that may be installed dynamically. + + + + sockets.target + + A special target unit that sets up all socket + units (see + systemd.socket5 + for details) that shall be active after boot. + + Services that can be socket-activated shall add + Wants= dependencies to this unit for + their socket unit during installation. This is best + configured via a WantedBy=sockets.target + in the socket unit's [Install] + section. + + + + suspend.target + + A special target unit for suspending the system. This + pulls in sleep.target. + + + + swap.target + + Similar to local-fs.target, but + for swap partitions and swap files. + + + + sysinit.target + + systemd automatically adds dependencies of the types + Requires= and After= + for this target unit to all services (except for those with + DefaultDependencies=no). + + This target pulls in the services required for system + initialization. System services pulled in by this target should + declare DefaultDependencies=no and specify + all their dependencies manually, including access to anything + more than a read only root filesystem. For details on the + dependencies of this target, refer to + bootup7. + + + + + syslog.socket + + The socket unit syslog implementations should listen + on. All userspace log messages will be made available on + this socket. For more information about syslog integration, + please consult the Syslog + Interface document. + + + + system-update.target + + A special target unit that is used for off-line system + updates. + systemd-system-update-generator8 + will redirect the boot process to this target if + /system-update exists. For more + information see the System + Updates Specification. + + + + timers.target + + A special target unit that sets up all timer units + (see + systemd.timer5 + for details) that shall be active after boot. + + It is recommended that timer units installed by + applications get pulled in via Wants= + dependencies from this unit. This is best configured via + WantedBy=timers.target in the timer + unit's [Install] section. + + + + umount.target + + A special target unit that unmounts all mount and + automount points on system shutdown. + + Mounts that shall be unmounted on system shutdown + shall add Conflicts dependencies to this unit for their + mount unit, which is implicitly done when + DefaultDependencies=yes is set (the + default). + + + + + + + + Special System Units for Devices + + Some target units are automatically pulled in as devices of + certain kinds show up in the system. These may be used to + automatically activate various services based on the specific type + of the available hardware. + + + + bluetooth.target + + This target is started automatically as soon as a + Bluetooth controller is plugged in or becomes available at + boot. + + This may be used to pull in Bluetooth management + daemons dynamically when Bluetooth hardware is found. + + + + printer.target + + This target is started automatically as soon as a + printer is plugged in or becomes available at boot. + + This may be used to pull in printer management daemons + dynamically when printer hardware is found. + + + + smartcard.target + + This target is started automatically as soon as a + smartcard controller is plugged in or becomes available at + boot. + + This may be used to pull in smartcard management + daemons dynamically when smartcard hardware is found. + + + + sound.target + + This target is started automatically as soon as a + sound card is plugged in or becomes available at + boot. + + This may be used to pull in audio management daemons + dynamically when audio hardware is found. + + + + + + + Special Passive System Units + + A number of special system targets are defined that can be + used to properly order boot-up of optional services. These targets + are generally not part of the initial boot transaction, unless + they are explicitly pulled in by one of the implementing services. + Note specifically that these passive target + units are generally not pulled in by the consumer of a service, + but by the provider of the service. This means: a consuming + service should order itself after these targets (as appropriate), + but not pull it in. A providing service should order itself before + these targets (as appropriate) and pull it in (via a + Wants= type dependency). + + Note that these passive units cannot be started manually, + i.e. systemctl start time-sync.target will fail + with an error. They can only be pulled in by dependency. This is + enforced since they exist for ordering purposes only and thus are + not useful as only unit within a transaction. + + + + cryptsetup-pre.target + + This passive target unit may be pulled in by services + that want to run before any encrypted block device is set + up. All encrypted block devices are set up after this target + has been reached. Since the shutdown order is implicitly the + reverse start-up order between units, this target is + particularly useful to ensure that a service is shut down + only after all encrypted block devices are fully + stopped. + + + + local-fs-pre.target + + This target unit is + automatically ordered before + all local mount points marked + with + (see above). It can be used to + execute certain units before + all local mounts. + + + + network.target + + This unit is supposed to indicate when network + functionality is available, but it is only very weakly + defined what that is supposed to mean, with one exception: + at shutdown, a unit that is ordered after + network.target will be stopped before + the network — to whatever level it might be set up then — + is shut down. It is hence useful when writing service files + that require network access on shutdown, which should order + themselves after this target, but not pull it in. Also see + Running + Services After the Network is up for more + information. Also see + network-online.target described + above. + + systemd automatically adds dependencies of type + After= for this target unit to all SysV + init script service units with an LSB header referring to + the $network facility. + + + + network-pre.target + + This passive target unit may be pulled in by services + that want to run before any network is set up, for example + for the purpose of setting up a firewall. All network + management software orders itself after this target, but + does not pull it in. + + + + nss-lookup.target + + A target that should be used as synchronization point + for all host/network name service lookups. Note that this is + independent of user/group name lookups for which + nss-user-lookup.target should be used. + All services for which the availability of full host/network + name resolution is essential should be ordered after this + target, but not pull it in. systemd automatically adds + dependencies of type After= for this + target unit to all SysV init script service units with an + LSB header referring to the $named + facility. + + + + nss-user-lookup.target + + A target that should be used as synchronization point + for all user/group name service lookups. Note that this is + independent of host/network name lookups for which + nss-lookup.target should be used. All + services for which the availability of the full user/group + database is essential should be ordered after this target, + but not pull it in. Note that system users are always + resolvable, and hence do not require any special ordering + against this target. + + + + remote-fs-pre.target + + This target unit is automatically ordered before all + remote mount point units (see above). It can be used to run + certain units before the remote mounts are established. Note + that this unit is generally not part of the initial + transaction, unless the unit that wants to be ordered before + all remote mounts pulls it in via a + Wants= type dependency. If the unit wants + to be pulled in by the first remote mount showing up, it + should use network-online.target (see + above). + + + + rpcbind.target + + The portmapper/rpcbind pulls in this target and orders + itself before it, to indicate its availability. systemd + automatically adds dependencies of type + After= for this target unit to all SysV + init script service units with an LSB header referring to + the $portmap facility. + + + + time-sync.target + + Services responsible for synchronizing the system + clock from a remote source (such as NTP client + implementations) should pull in this target and order + themselves before it. All services where correct time is + essential should be ordered after this unit, but not pull it + in. systemd automatically adds dependencies of type + After= for this target unit to all SysV + init script service units with an LSB header referring to + the $time facility. + + + + + + + Special User Units + + When systemd runs as a user instance, the following special + units are available, which have similar definitions as their + system counterparts: + exit.target, + default.target, + shutdown.target, + sockets.target, + timers.target, + paths.target, + bluetooth.target, + printer.target, + smartcard.target, + sound.target. + + + + Special Slice Units + + There are four .slice units which form + the basis of the hierarchy for assignment of resources for + services, users, and virtual machines or containers. + + + + -.slice + + The root slice is the root of the hierarchy. It + usually does not contain units directly, but may be used to + set defaults for the whole tree. + + + + + system.slice + + By default, all system services started by + systemd are found in this slice. + + + + + user.slice + + By default, all user processes and services started on + behalf of the user, including the per-user systemd instance + are found in this slice. + + + + + machine.slice + + By default, all virtual machines and containers + registered with systemd-machined are + found in this slice. + + + + + + + + See Also + + systemd1, + systemd.unit5, + systemd.service5, + systemd.socket5, + systemd.target5, + systemd.slice5, + bootup7, + systemd-fstab-generator8 + + + + diff --git a/src/grp-system/systemd/systemd.swap.xml b/src/grp-system/systemd/systemd.swap.xml new file mode 100644 index 0000000000..cf4e1ba839 --- /dev/null +++ b/src/grp-system/systemd/systemd.swap.xml @@ -0,0 +1,250 @@ + + + + + + + + systemd.swap + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.swap + 5 + + + + systemd.swap + Swap unit configuration + + + + swap.swap + + + + Description + + A unit configuration file whose name ends in + .swap encodes information about a swap device + or file for memory paging controlled and supervised by + systemd. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The swap specific configuration options are + configured in the [Swap] section. + + Additional options are listed in + systemd.exec5, + which define the execution environment the swapon8 + binary is executed in, in + systemd.kill5, + which define the way these processes are + terminated, and in + systemd.resource-control5, + which configure resource control settings for these processes of the + unit. + + Swap units must be named after the devices or files they control. Example: the swap device /dev/sda5 must be configured in a unit file dev-sda5.swap. For + details about the escaping logic used to convert a file system path to a unit name, see + systemd.unit5. Note that swap + units cannot be templated, nor is possible to add multiple names to a swap unit by creating additional symlinks to + it. + + + + Automatic Dependencies + + All swap units automatically get the + BindsTo= and After= + dependencies on the device units or the mount units of the files + they are activated from. + + Swap units with DefaultDependencies= in the [Unit] section enabled + implicitly acquire a Conflicts= and an After= dependency on + umount.target so that they are deactivated at shutdown, unless + DefaultDependencies=no is specified. + + Additional implicit dependencies may be added as result of + execution and resource control parameters as documented in + systemd.exec5 + and + systemd.resource-control5. + + + + <filename>fstab</filename> + + Swap units may either be configured via unit files, or via + /etc/fstab (see + fstab5 + for details). Swaps listed in /etc/fstab will + be converted into native units dynamically at boot and when the + configuration of the system manager is reloaded. See + systemd-fstab-generator8 + for details about the conversion. + + If a swap device or file is configured in both + /etc/fstab and a unit file, the configuration + in the latter takes precedence. + + When reading /etc/fstab, a few special + options are understood by systemd which influence how dependencies + are created for swap units. + + + + + + + With , the swap unit + will not be added as a dependency for + swap.target. This means that it will not + be activated automatically during boot, unless it is pulled in + by some other unit. The option has the + opposite meaning and is the default. + + + + + + + With , the swap unit + will be only wanted, not required by + swap.target. This means that the boot + will continue even if this swap device is not activated + successfully. + + + + + + + Options + + Swap files must include a [Swap] section, which carries + information about the swap device it supervises. A number of + options that may be used in this section are shared with other + unit types. These options are documented in + systemd.exec5 + and + systemd.kill5. + The options specific to the [Swap] section of swap units are the + following: + + + + + What= + Takes an absolute path of a device node or + file to use for paging. See + swapon8 + for details. If this refers to a device node, a dependency on + the respective device unit is automatically created. (See + systemd.device5 + for more information.) If this refers to a file, a dependency + on the respective mount unit is automatically created. (See + systemd.mount5 + for more information.) This option is + mandatory. + + + + Priority= + + Swap priority to use when activating the swap + device or file. This takes an integer. This setting is + optional and ignored when the priority is set by in the + Options= key. + + + + Options= + + May contain an option string for the swap + device. This may be used for controlling discard options among + other functionality, if the swap backing device supports the + discard or trim operation. (See + swapon8 + for more information.) + + + + TimeoutSec= + Configures the time to wait for the swapon + command to finish. If a command does not exit within the + configured time, the swap will be considered failed and be + shut down again. All commands still running will be terminated + forcibly via SIGTERM, and after another + delay of this time with SIGKILL. (See + in + systemd.kill5.) + Takes a unit-less value in seconds, or a time span value such + as "5min 20s". Pass 0 to disable the + timeout logic. Defaults to + DefaultTimeoutStartSec= from the manager + configuration file (see + systemd-system.conf5). + + + + + Check + systemd.exec5 + and + systemd.kill5 + for more settings. + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.exec5, + systemd.kill5, + systemd.resource-control5, + systemd.device5, + systemd.mount5, + swapon8, + systemd-fstab-generator8, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.target.xml b/src/grp-system/systemd/systemd.target.xml new file mode 100644 index 0000000000..2e35e54fc4 --- /dev/null +++ b/src/grp-system/systemd/systemd.target.xml @@ -0,0 +1,112 @@ + + + + + + + + systemd.target + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.target + 5 + + + + systemd.target + Target unit configuration + + + + target.target + + + + Description + + A unit configuration file whose name ends in + .target encodes information about a target unit + of systemd, which is used for grouping units and as well-known + synchronization points during start-up. + + This unit type has no specific options. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. A separate [Target] section does not exist, + since no target-specific options may be configured. + + Target units do not offer any additional functionality on + top of the generic functionality provided by units. They exist + merely to group units via dependencies (useful as boot targets), + and to establish standardized names for synchronization points + used in dependencies between units. Among other things, target + units are a more flexible replacement for SysV runlevels in the + classic SysV init system. (And for compatibility reasons special + target units such as runlevel3.target exist + which are used by the SysV runlevel compatibility code in systemd. + See + systemd.special7 + for details). + + + + Automatic Dependencies + + Unless DefaultDependencies= is set to + in either of releated units or an explicit ordering + dependency is already defined, target units will implicitly complement all + configured dependencies of type Wants= or + Requires= with dependencies of type + After=. Note that Wants= or + Requires= must be defined in the target unit itself — if + you for example define Wants=some.target in + some.service, the implicit ordering will not be added. + + All target units automatically gain Conflicts= + dependency against shutdown.target unless DefaultDependencies= + is set to . + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.special7, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.time.xml b/src/grp-system/systemd/systemd.time.xml new file mode 100644 index 0000000000..aae3accb6c --- /dev/null +++ b/src/grp-system/systemd/systemd.time.xml @@ -0,0 +1,316 @@ + + + + + + + + + systemd.time + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.time + 7 + + + + systemd.time + Time and date specifications + + + + Description + + In systemd, timestamps, time spans, and calendar events are + displayed and may be specified in closely related syntaxes. + + + + Displaying Time Spans + + Time spans refer to time durations. On display, systemd will + present time spans as a space-separated series of time values each + suffixed by a time unit. + + 2h 30min + + All specified time values are meant to be added up. The + above hence refers to 150 minutes. + + + + Parsing Time Spans + + When parsing, systemd will accept the same time span syntax. + Separating spaces may be omitted. The following time units are + understood: + + + usec, us + msec, ms + seconds, second, sec, s + minutes, minute, min, m + hours, hour, hr, h + days, day, d + weeks, week, w + months, month, M (defined as 30.44 days) + years, year, y (define as 365.25 days) + + + If no time unit is specified, generally seconds are assumed, + but some exceptions exist and are marked as such. In a few cases + ns, nsec is accepted too, + where the granularity of the time span allows for this. + + Examples for valid time span specifications: + + 2 h +2hours +48hr +1y 12month +55s500ms +300ms20s 5day + + + + Displaying Timestamps + + Timestamps refer to specific, unique points in time. On + display, systemd will format these in the local timezone as + follows: + + Fri 2012-11-23 23:02:15 CET + + The weekday is printed according to the locale choice of the + user. + + + + Parsing Timestamps + + When parsing, systemd will accept a similar syntax, but + expects no timezone specification, unless it is given as the + literal string "UTC". In this case, the time is considered in UTC, + otherwise in the local timezone. The weekday specification is + optional, but when the weekday is specified, it must either be in + the abbreviated (Wed) or non-abbreviated + (Wednesday) English language form (case does + not matter), and is not subject to the locale choice of the user. + Either the date, or the time part may be omitted, in which case + the current date or 00:00:00, respectively, is assumed. The seconds + component of the time may also be omitted, in which case ":00" is + assumed. Year numbers may be specified in full or may be + abbreviated (omitting the century). + + A timestamp is considered invalid if a weekday is specified + and the date does not actually match the specified day of the + week. + + When parsing, systemd will also accept a few special + placeholders instead of timestamps: now may be + used to refer to the current time (or of the invocation of the + command that is currently executed). today, + yesterday, and tomorrow refer to + 00:00:00 of the current day, the day before, or the next day, + respectively. + + When parsing, systemd will also accept relative time + specifications. A time span (see above) that is prefixed with + + is evaluated to the current time plus the + specified time span. Correspondingly, a time span that is prefixed + with - is evaluated to the current time minus + the specified time span. Instead of prefixing the time span with + + or -, it may also be + suffixed with a space and the word left or + ago. + + Finally, a timespan prefixed with @ is + evaluated relative to the UNIX time epoch 1st Jan, 1970, + 00:00. + + Examples for valid timestamps and their normalized form + (assuming the current time was 2012-11-23 18:15:22 and the timezone + was UTC+8, for example TZ=Asia/Shanghai): + + Fri 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 + 2012-11-23 11:12:13 → Fri 2012-11-23 11:12:13 +2012-11-23 11:12:13 UTC → Fri 2012-11-23 19:12:13 + 2012-11-23 → Fri 2012-11-23 00:00:00 + 12-11-23 → Fri 2012-11-23 00:00:00 + 11:12:13 → Fri 2012-11-23 11:12:13 + 11:12:13.9900009 → Fri 2012-11-23 11:12:13 + format_timestamp_us: Fri 2012-11-23 11:12:13.990000 + 11:12 → Fri 2012-11-23 11:12:00 + now → Fri 2012-11-23 18:15:22 + today → Fri 2012-11-23 00:00:00 + today UTC → Fri 2012-11-23 16:00:00 + yesterday → Fri 2012-11-22 00:00:00 + tomorrow → Fri 2012-11-24 00:00:00 + +3h30min → Fri 2012-11-23 21:45:22 + +3h30min UTC → -EINVAL + -5s → Fri 2012-11-23 18:15:17 + 11min ago → Fri 2012-11-23 18:04:22 + 11min ago UTC → -EINVAL + @1395716396 → Tue 2014-03-25 03:59:56 + + Note that timestamps printed by systemd will not be parsed + correctly by systemd, as the timezone specification is not + accepted, and printing timestamps is subject to locale settings + for the weekday, while parsing only accepts English weekday + names. + + In some cases, systemd will display a relative timestamp + (relative to the current time, or the time of invocation of the + command) instead or in addition to an absolute timestamp as + described above. A relative timestamp is formatted as + follows: + + 2 months 5 days ago + + Note that any relative timestamp will also parse correctly + where a timestamp is expected. (see above) + + + + Calendar Events + + Calendar events may be used to refer to one or more points + in time in a single expression. They form a superset of the + absolute timestamps explained above: + + Thu,Fri 2012-*-1,5 11:12:13 + + The above refers to 11:12:13 of the first or fifth day of + any month of the year 2012, but only if that day is a Thursday or + Friday. + + The weekday specification is optional. If specified, it + should consist of one or more English language weekday names, + either in the abbreviated (Wed) or non-abbreviated (Wednesday) + form (case does not matter), separated by commas. Specifying two + weekdays separated by .. refers to a range of + continuous weekdays. , and .. + may be combined freely. + + In the date and time specifications, any component may be + specified as * in which case any value will + match. Alternatively, each component can be specified as a list of + values separated by commas. Values may also be suffixed with + / and a repetition value, which indicates that + the value itself and the value plus all multiples of the repetition value + are matched. Each component may also contain a range of values + separated by ... + + The seconds component may contain decimal fractions both in + the value and the repetition. All fractions are rounded to 6 + decimal places. + + Either time or date specification may be omitted, in which + case the current day and 00:00:00 is implied, respectively. If the + second component is not specified, :00 is + assumed. + + A timezone specification is not expected, unless it is given + as the literal string "UTC", similarly to timestamps. + + The special expressions + minutely, + hourly, daily, + monthly, weekly, + yearly, + quarterly, + semiannually may be used as + calendar events which refer to + *-*-* *:*:00, + *-*-* *:00:00, + *-*-* 00:00:00, + *-*-01 00:00:00, + Mon *-*-* 00:00:00, + *-01-01 00:00:00, + *-01,04,07,10-01 00:00:00 and + *-01,07-01 00:00:00, respectively. + + + Examples for valid timestamps and their + normalized form: + + Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00 + Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00 + Wed *-1 → Wed *-*-01 00:00:00 + Wed..Wed,Wed *-1 → Wed *-*-01 00:00:00 + Wed, 17:48 → Wed *-*-* 17:48:00 +Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03 + *-*-7 0:0:0 → *-*-07 00:00:00 + 10-15 → *-10-15 00:00:00 + monday *-12-* 17:00 → Mon *-12-* 17:00:00 + Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45 + 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00 + 12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00 + mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45 + 03-05 08:05:40 → *-03-05 08:05:40 + 08:05:40 → *-*-* 08:05:40 + 05:40 → *-*-* 05:40:00 + Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40 + Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40 + 2003-03-05 05:40 → 2003-03-05 05:40:00 +05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001 + 2003-02..04-05 → 2003-02,03,04-05 00:00:00 + 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC + 2003-03-05 → 2003-03-05 00:00:00 + 03-05 → *-03-05 00:00:00 + hourly → *-*-* *:00:00 + daily → *-*-* 00:00:00 + daily UTC → *-*-* 00:00:00 UTC + monthly → *-*-01 00:00:00 + weekly → Mon *-*-* 00:00:00 + yearly → *-01-01 00:00:00 + annually → *-01-01 00:00:00 + *:2/3 → *-*-* *:02/3:00 + + Calendar events are used by timer units, see + systemd.timer5 + for details. + + + + + See Also + + systemd1, + journalctl1, + systemd.timer5, + systemd.unit5, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/systemd.timer.xml b/src/grp-system/systemd/systemd.timer.xml new file mode 100644 index 0000000000..4fe140e4bc --- /dev/null +++ b/src/grp-system/systemd/systemd.timer.xml @@ -0,0 +1,313 @@ + + + + + + + + systemd.timer + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.timer + 5 + + + + systemd.timer + Timer unit configuration + + + + timer.timer + + + + Description + + A unit configuration file whose name ends in + .timer encodes information about a timer + controlled and supervised by systemd, for timer-based + activation. + + This man page lists the configuration options specific to + this unit type. See + systemd.unit5 + for the common options of all unit configuration files. The common + configuration items are configured in the generic [Unit] and + [Install] sections. The timer specific configuration options are + configured in the [Timer] section. + + For each timer file, a matching unit file must exist, + describing the unit to activate when the timer elapses. By + default, a service by the same name as the timer (except for the + suffix) is activated. Example: a timer file + foo.timer activates a matching service + foo.service. The unit to activate may be + controlled by Unit= (see below). + + Note that in case the unit to activate is already active at the time the timer elapses it is not restarted, + but simply left running. There is no concept of spawning new service instances in this case. Due to this, services + with RemainAfterExit= set (which stay around continuously even after the service's main process + exited) are usually not suitable for activation via repetitive timers, as they will only be activated once, and + then stay around forever. + + + + Automatic Dependencies + + Timer units automatically gain a Before= + dependency on the service they are supposed to activate. + + Unless DefaultDependencies= in the [Unit] section is set to + , all timer units will implicitly have dependencies of type Requires= and + After= on sysinit.target, a dependency of type Before= + on timers.target, as well as Conflicts= and Before= on + shutdown.target to ensure that they are stopped cleanly prior to system shutdown. Timer units + with at least one OnCalendar= directive will have an additional After= + dependency on timer-sync.target to avoid being started before the system clock has been + correctly set. Only timer units involved with early boot or late system shutdown should disable the + DefaultDependencies= option. + + + + Options + + Timer files must include a [Timer] section, which carries + information about the timer it defines. The options specific to + the [Timer] section of timer units are the following: + + + + OnActiveSec= + OnBootSec= + OnStartupSec= + OnUnitActiveSec= + OnUnitInactiveSec= + + Defines monotonic timers relative to different + starting points: OnActiveSec= defines a + timer relative to the moment the timer itself is activated. + OnBootSec= defines a timer relative to when + the machine was booted up. OnStartupSec= + defines a timer relative to when systemd was first started. + OnUnitActiveSec= defines a timer relative + to when the unit the timer is activating was last activated. + OnUnitInactiveSec= defines a timer relative + to when the unit the timer is activating was last + deactivated. + + Multiple directives may be combined of the same and of + different types. For example, by combining + OnBootSec= and + OnUnitActiveSec=, it is possible to define + a timer that elapses in regular intervals and activates a + specific service each time. + + The arguments to the directives are time spans + configured in seconds. Example: "OnBootSec=50" means 50s after + boot-up. The argument may also include time units. Example: + "OnBootSec=5h 30min" means 5 hours and 30 minutes after + boot-up. For details about the syntax of time spans, see + systemd.time7. + + If a timer configured with OnBootSec= + or OnStartupSec= is already in the past + when the timer unit is activated, it will immediately elapse + and the configured unit is started. This is not the case for + timers defined in the other directives. + + These are monotonic timers, independent of wall-clock + time and timezones. If the computer is temporarily suspended, + the monotonic clock stops too. + + If the empty string is assigned to any of these options, + the list of timers is reset, and all prior assignments will + have no effect. + + Note that timers do not necessarily expire at the + precise time configured with these settings, as they are + subject to the AccuracySec= setting + below. + + + + + OnCalendar= + + Defines realtime (i.e. wallclock) timers with + calendar event expressions. See + systemd.time7 + for more information on the syntax of calendar event + expressions. Otherwise, the semantics are similar to + OnActiveSec= and related settings. + + Note that timers do not necessarily expire at the + precise time configured with this setting, as it is subject to + the AccuracySec= setting + below. + + + + AccuracySec= + + Specify the accuracy the timer shall elapse + with. Defaults to 1min. The timer is scheduled to elapse + within a time window starting with the time specified in + OnCalendar=, + OnActiveSec=, + OnBootSec=, + OnStartupSec=, + OnUnitActiveSec= or + OnUnitInactiveSec= and ending the time + configured with AccuracySec= later. Within + this time window, the expiry time will be placed at a + host-specific, randomized, but stable position that is + synchronized between all local timer units. This is done in + order to optimize power consumption to suppress unnecessary + CPU wake-ups. To get best accuracy, set this option to + 1us. Note that the timer is still subject to the timer slack + configured via + systemd-system.conf5's + TimerSlackNSec= setting. See + prctl2 + for details. To optimize power consumption, make sure to set + this value as high as possible and as low as + necessary. + + + + RandomizedDelaySec= + + Delay the timer by a randomly selected, evenly + distributed amount of time between 0 and the specified time + value. Defaults to 0, indicating that no randomized delay + shall be applied. Each timer unit will determine this delay + randomly each time it is started, and the delay will simply be + added on top of the next determined elapsing time. This is + useful to stretch dispatching of similarly configured timer + events over a certain amount time, to avoid that they all fire + at the same time, possibly resulting in resource + congestion. Note the relation to + AccuracySec= above: the latter allows the + service manager to coalesce timer events within a specified + time range in order to minimize wakeups, the former does the + opposite: it stretches timer events over a time range, to make + it unlikely that they fire simultaneously. If + RandomizedDelaySec= and + AccuracySec= are used in conjunction, first + the randomized delay is added, and then the result is + possibly further shifted to coalesce it with other timer + events happening on the system. As mentioned above + AccuracySec= defaults to 1min and + RandomizedDelaySec= to 0, thus encouraging + coalescing of timer events. In order to optimally stretch + timer events over a certain range of time, make sure to set + RandomizedDelaySec= to a higher value, and + AccuracySec=1us. + + + + Unit= + + The unit to activate when this timer elapses. + The argument is a unit name, whose suffix is not + .timer. If not specified, this value + defaults to a service that has the same name as the timer + unit, except for the suffix. (See above.) It is recommended + that the unit name that is activated and the unit name of the + timer unit are named identically, except for the + suffix. + + + + + Persistent= + + Takes a boolean argument. If true, the time + when the service unit was last triggered is stored on disk. + When the timer is activated, the service unit is triggered + immediately if it would have been triggered at least once + during the time when the timer was inactive. This is useful to + catch up on missed runs of the service when the machine was + off. Note that this setting only has an effect on timers + configured with OnCalendar=. Defaults + to false. + + + + + WakeSystem= + + Takes a boolean argument. If true, an elapsing + timer will cause the system to resume from suspend, should it + be suspended and if the system supports this. Note that this + option will only make sure the system resumes on the + appropriate times, it will not take care of suspending it + again after any work that is to be done is finished. Defaults + to false. + + + + RemainAfterElapse= + + Takes a boolean argument. If true, an elapsed + timer will stay loaded, and its state remains queriable. If + false, an elapsed timer unit that cannot elapse anymore is + unloaded. Turning this off is particularly useful for + transient timer units that shall disappear after they first + elapse. Note that this setting has an effect on repeatedly + starting a timer unit that only elapses once: if + RemainAfterElapse= is on, it will not be + started again, and is guaranteed to elapse only once. However, + if RemainAfterElapse= is off, it might be + started again if it is already elapsed, and thus be triggered + multiple times. Defaults to + yes. + + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + systemd.service5, + systemd.time7, + systemd.directives7, + systemd-system.conf5, + prctl2 + + + + diff --git a/src/grp-system/systemd/systemd.tmpfiles b/src/grp-system/systemd/systemd.tmpfiles new file mode 100644 index 0000000000..00951c92c9 --- /dev/null +++ b/src/grp-system/systemd/systemd.tmpfiles @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +d /run/user 0755 root root - +F! /run/utmp 0664 root utmp - + +d /run/systemd/ask-password 0755 root root - +d /run/systemd/seats 0755 root root - +d /run/systemd/sessions 0755 root root - +d /run/systemd/users 0755 root root - +d /run/systemd/machines 0755 root root - +d /run/systemd/shutdown 0755 root root - + +d /var/lib/systemd 0755 root root - diff --git a/src/grp-system/systemd/systemd.unit.xml b/src/grp-system/systemd/systemd.unit.xml new file mode 100644 index 0000000000..85a7b12d76 --- /dev/null +++ b/src/grp-system/systemd/systemd.unit.xml @@ -0,0 +1,1484 @@ + + +%entities; +]> + + + + + + + systemd.unit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.unit + 5 + + + + systemd.unit + Unit configuration + + + + service.service, + socket.socket, + device.device, + mount.mount, + automount.automount, + swap.swap, + target.target, + path.path, + timer.timer, + slice.slice, + scope.scope + + /etc/systemd/system/* +/run/systemd/system/* +/usr/lib/systemd/system/* + + + + ~/.config/systemd/user/* +/etc/systemd/user/* +$XDG_RUNTIME_DIR/systemd/user/* +/run/systemd/user/* +~/.local/share/systemd/user/* +/usr/lib/systemd/user/* + + + + + + Description + + A unit configuration file encodes information about a + service, a socket, a device, a mount point, an automount point, a + swap file or partition, a start-up target, a watched file system + path, a timer controlled and supervised by + systemd1, + a resource management slice or + a group of externally created processes. The syntax is inspired by + XDG + Desktop Entry Specification .desktop + files, which are in turn inspired by Microsoft Windows + .ini files. + + This man page lists the common configuration options of all + the unit types. These options need to be configured in the [Unit] + or [Install] sections of the unit files. + + In addition to the generic [Unit] and [Install] sections + described here, each unit may have a type-specific section, e.g. + [Service] for a service unit. See the respective man pages for + more information: + systemd.service5, + systemd.socket5, + systemd.device5, + systemd.mount5, + systemd.automount5, + systemd.swap5, + systemd.target5, + systemd.path5, + systemd.timer5, + systemd.slice5, + systemd.scope5. + + + Various settings are allowed to be specified more than once, + in which case the interpretation depends on the setting. Often, + multiple settings form a list, and setting to an empty value + "resets", which means that previous assignments are ignored. When + this is allowed, it is mentioned in the description of the + setting. Note that using multiple assignments to the same value + makes the unit file incompatible with parsers for the XDG + .desktop file format. + + Unit files are loaded from a set of paths determined during + compilation, described in the next section. + + Unit files may contain additional options on top of those + listed here. If systemd encounters an unknown option, it will + write a warning log message but continue loading the unit. If an + option or section name is prefixed with , it is + ignored completely by systemd. Options within an ignored section + do not need the prefix. Applications may use this to include + additional information in the unit files. + + Boolean arguments used in unit files can be written in + various formats. For positive settings the strings + , , + and are equivalent. For negative settings, the + strings , , + and are + equivalent. + + Time span values encoded in unit files can be written in + various formats. A stand-alone number specifies a time in seconds. + If suffixed with a time unit, the unit is honored. A concatenation + of multiple values with units is supported, in which case the + values are added up. Example: "50" refers to 50 seconds; "2min + 200ms" refers to 2 minutes plus 200 milliseconds, i.e. 120200ms. + The following time units are understood: s, min, h, d, w, ms, us. + For details see + systemd.time7. + + Empty lines and lines starting with # or ; are + ignored. This may be used for commenting. Lines ending + in a backslash are concatenated with the following + line while reading and the backslash is replaced by a + space character. This may be used to wrap long lines. + + Along with a unit file foo.service, the + directory foo.service.wants/ may exist. All + unit files symlinked from such a directory are implicitly added as + dependencies of type Wants= to the unit. This + is useful to hook units into the start-up of other units, without + having to modify their unit files. For details about the semantics + of Wants=, see below. The preferred way to + create symlinks in the .wants/ directory of a + unit file is with the enable command of the + systemctl1 + tool which reads information from the [Install] section of unit + files (see below). A similar functionality exists for + Requires= type dependencies as well, the + directory suffix is .requires/ in this + case. + + Along with a unit file foo.service, a "drop-in" directory + foo.service.d/ may exist. All files with the suffix .conf from this + directory will be parsed after the file itself is parsed. This is useful to alter or add configuration settings for + a unit, without having to modify unit files. Each drop-in file must have appropriate section headers. Note that for + instantiated units, this logic will first look for the instance .d/ subdirectory and read its + .conf files, followed by the template .d/ subdirectory and the + .conf files there. Also note that settings from the [Install] section are not + honoured in drop-in unit files, and have no effect. + + In addition to /etc/systemd/system, + the drop-in .conf files for system services + can be placed in /usr/lib/systemd/system or + /run/systemd/system directories. Drop-in + files in /etc take precedence over those in + /run which in turn take precedence over + those in /usr/lib. Drop-in files under any of + these directories take precedence over unit files wherever located. + (Of course, since /run is temporary and + /usr/lib is for vendors, it is unlikely + drop-ins should be used in either of those places.) + + + Some unit names reflect paths existing in the file system + namespace. Example: a device unit + dev-sda.device refers to a device with the + device node /dev/sda in the + file system namespace. If this applies, a special way to escape + the path name is used, so that the result is usable as part of a + filename. Basically, given a path, "/" is replaced by "-", and all + other characters which are not ASCII alphanumerics are replaced by + C-style "\x2d" escapes (except that "_" is never replaced and "." + is only replaced when it would be the first character in the + escaped path). The root directory "/" is encoded as single dash, + while otherwise the initial and ending "/" are removed from all + paths during transformation. This escaping is reversible. Properly + escaped paths can be generated using the + systemd-escape1 + command. + + Optionally, units may be instantiated from a + template file at runtime. This allows creation of + multiple units from a single configuration file. If + systemd looks for a unit configuration file, it will + first search for the literal unit name in the + file system. If that yields no success and the unit + name contains an @ character, systemd will look for a + unit template that shares the same name but with the + instance string (i.e. the part between the @ character + and the suffix) removed. Example: if a service + getty@tty3.service is requested + and no file by that name is found, systemd will look + for getty@.service and + instantiate a service from that configuration file if + it is found. + + To refer to the instance string from within the + configuration file you may use the special %i + specifier in many of the configuration options. See below for + details. + + If a unit file is empty (i.e. has the file size 0) or is + symlinked to /dev/null, its configuration + will not be loaded and it appears with a load state of + masked, and cannot be activated. Use this as an + effective way to fully disable a unit, making it impossible to + start it even manually. + + The unit file format is covered by the + Interface + Stability Promise. + + + + + Automatic Dependencies + + Note that while systemd offers a flexible dependency system + between units it is recommended to use this functionality only + sparingly and instead rely on techniques such as bus-based or + socket-based activation which make dependencies implicit, + resulting in a both simpler and more flexible system. + + A number of unit dependencies are automatically established, + depending on unit configuration. On top of that, for units with + DefaultDependencies=yes (the default) a couple + of additional dependencies are added. The precise effect of + DefaultDependencies=yes depends on the unit + type (see below). + + If DefaultDependencies=yes is set, units + that are referenced by other units of type + .target via a Wants= or + Requires= dependency might automatically gain + an Before= dependency too. See + systemd.target5 + for details. + + + + Unit File Load Path + + Unit files are loaded from a set of paths determined during + compilation, described in the two tables below. Unit files found + in directories listed earlier override files with the same name in + directories lower in the list. + + When the variable $SYSTEMD_UNIT_PATH is set, + the contents of this variable overrides the unit load path. If + $SYSTEMD_UNIT_PATH ends with an empty component + (:), the usual unit load path will be appended + to the contents of the variable. + + + + Load path when running in system mode (<option>--system</option>). + + + + + + + + Path + Description + + + + + /etc/systemd/system + Local configuration + + + /run/systemd/system + Runtime units + + + /usr/lib/systemd/system + Units of installed packages + + + +
+ + + + Load path when running in user mode (<option>--user</option>). + + + + + + + + Path + Description + + + + + $XDG_CONFIG_HOME/systemd/user + User configuration (only used when $XDG_CONFIG_HOME is set) + + + $HOME/.config/systemd/user + User configuration (only used when $XDG_CONFIG_HOME is not set) + + + /etc/systemd/user + Local configuration + + + $XDG_RUNTIME_DIR/systemd/user + Runtime units (only used when $XDG_RUNTIME_DIR is set) + + + /run/systemd/user + Runtime units + + + $XDG_DATA_HOME/systemd/user + Units of packages that have been installed in the home directory (only used when $XDG_DATA_HOME is set) + + + $HOME/.local/share/systemd/user + Units of packages that have been installed in the home directory (only used when $XDG_DATA_HOME is not set) + + + /usr/lib/systemd/user + Units of packages that have been installed system-wide + + + +
+ + Additional units might be loaded into systemd ("linked") + from directories not on the unit load path. See the + link command for + systemctl1. + Also, some units are dynamically created via a + systemd.generator7. + +
+ + + [Unit] Section Options + + The unit file may include a [Unit] section, which carries + generic information about the unit that is not dependent on the + type of unit: + + + + + Description= + A free-form string describing the unit. This + is intended for use in UIs to show descriptive information + along with the unit name. The description should contain a + name that means something to the end user. Apache2 + Web Server is a good example. Bad examples are + high-performance light-weight HTTP server + (too generic) or Apache2 (too specific and + meaningless for people who do not know + Apache). + + + + Documentation= + A space-separated list of URIs referencing + documentation for this unit or its configuration. Accepted are + only URIs of the types http://, + https://, file:, + info:, man:. For more + information about the syntax of these URIs, see uri7. + The URIs should be listed in order of relevance, starting with + the most relevant. It is a good idea to first reference + documentation that explains what the unit's purpose is, + followed by how it is configured, followed by any other + related documentation. This option may be specified more than + once, in which case the specified list of URIs is merged. If + the empty string is assigned to this option, the list is reset + and all prior assignments will have no + effect. + + + + Requires= + + Configures requirement dependencies on other + units. If this unit gets activated, the units listed here will + be activated as well. If one of the other units gets + deactivated or its activation fails, this unit will be + deactivated. This option may be specified more than once or + multiple space-separated units may be specified in one option + in which case requirement dependencies for all listed names + will be created. Note that requirement dependencies do not + influence the order in which services are started or stopped. + This has to be configured independently with the + After= or Before= + options. If a unit foo.service requires a + unit bar.service as configured with + Requires= and no ordering is configured + with After= or Before=, + then both units will be started simultaneously and without any + delay between them if foo.service is + activated. Often, it is a better choice to use + Wants= instead of + Requires= in order to achieve a system that + is more robust when dealing with failing services. + + Note that dependencies of this type may also be + configured outside of the unit configuration file by adding a + symlink to a .requires/ directory + accompanying the unit file. For details, see + above. + + + + Requisite= + + Similar to Requires=. + However, if the units listed here are not started already, + they will not be started and the transaction will fail + immediately. + + + + Wants= + + A weaker version of + Requires=. Units listed in this option will + be started if the configuring unit is. However, if the listed + units fail to start or cannot be added to the transaction, + this has no impact on the validity of the transaction as a + whole. This is the recommended way to hook start-up of one + unit to the start-up of another unit. + + Note that dependencies of this type may also be + configured outside of the unit configuration file by adding + symlinks to a .wants/ directory + accompanying the unit file. For details, see + above. + + + + BindsTo= + + Configures requirement dependencies, very + similar in style to Requires=, however in + addition to this behavior, it also declares that this unit is + stopped when any of the units listed suddenly disappears. + Units can suddenly, unexpectedly disappear if a service + terminates on its own choice, a device is unplugged or a mount + point unmounted without involvement of + systemd. + + + + PartOf= + + Configures dependencies similar to + Requires=, but limited to stopping and + restarting of units. When systemd stops or restarts the units + listed here, the action is propagated to this unit. Note that + this is a one-way dependency — changes to this unit do not + affect the listed units. + + + + Conflicts= + + A space-separated list of unit names. + Configures negative requirement dependencies. If a unit has a + Conflicts= setting on another unit, + starting the former will stop the latter and vice versa. Note + that this setting is independent of and orthogonal to the + After= and Before= + ordering dependencies. + + If a unit A that conflicts with a unit B is scheduled to + be started at the same time as B, the transaction will either + fail (in case both are required part of the transaction) or be + modified to be fixed (in case one or both jobs are not a + required part of the transaction). In the latter case, the job + that is not the required will be removed, or in case both are + not required, the unit that conflicts will be started and the + unit that is conflicted is stopped. + + + + Before= + After= + + A space-separated list of unit names. + Configures ordering dependencies between units. If a unit + foo.service contains a setting + and both units are being + started, bar.service's start-up is + delayed until foo.service is started up. + Note that this setting is independent of and orthogonal to the + requirement dependencies as configured by + Requires=. It is a common pattern to + include a unit name in both the After= and + Requires= option, in which case the unit + listed will be started before the unit that is configured with + these options. This option may be specified more than once, in + which case ordering dependencies for all listed names are + created. After= is the inverse of + Before=, i.e. while + After= ensures that the configured unit is + started after the listed unit finished starting up, + Before= ensures the opposite, i.e. that the + configured unit is fully started up before the listed unit is + started. Note that when two units with an ordering dependency + between them are shut down, the inverse of the start-up order + is applied. i.e. if a unit is configured with + After= on another unit, the former is + stopped before the latter if both are shut down. Given two units + with any ordering dependency between them, if one unit is shut + down and the other is started up, the shutdown is ordered + before the start-up. It doesn't matter if the ordering + dependency is After= or + Before=. It also doesn't matter which of the + two is shut down, as long as one is shut down and the other is + started up. The shutdown is ordered before the start-up in all + cases. If two units have no ordering dependencies between them, + they are shut down or started up simultaneously, and no ordering + takes place. + + + + + OnFailure= + + A space-separated list of one or more units + that are activated when this unit enters the + failed state. + + + + PropagatesReloadTo= + ReloadPropagatedFrom= + + A space-separated list of one or more units + where reload requests on this unit will be propagated to, or + reload requests on the other unit will be propagated to this + unit, respectively. Issuing a reload request on a unit will + automatically also enqueue a reload request on all units that + the reload request shall be propagated to via these two + settings. + + + + JoinsNamespaceOf= + + For units that start processes (such as + service units), lists one or more other units whose network + and/or temporary file namespace to join. This only applies to + unit types which support the + PrivateNetwork= and + PrivateTmp= directives (see + systemd.exec5 + for details). If a unit that has this setting set is started, + its processes will see the same /tmp, + /var/tmp and network namespace as one + listed unit that is started. If multiple listed units are + already started, it is not defined which namespace is joined. + Note that this setting only has an effect if + PrivateNetwork= and/or + PrivateTmp= is enabled for both the unit + that joins the namespace and the unit whose namespace is + joined. + + + + RequiresMountsFor= + + Takes a space-separated list of absolute + paths. Automatically adds dependencies of type + Requires= and After= for + all mount units required to access the specified path. + + Mount points marked with are not + mounted automatically and will be ignored for the purposes of + this option. If such a mount should be a requirement for this + unit, direct dependencies on the mount units may be added + (Requires= and After= or + some other combination). + + + + OnFailureJobMode= + + Takes a value of + fail, + replace, + replace-irreversibly, + isolate, + flush, + ignore-dependencies or + ignore-requirements. Defaults to + replace. Specifies how the units listed in + OnFailure= will be enqueued. See + systemctl1's + option for details on the + possible values. If this is set to isolate, + only a single unit may be listed in + OnFailure=.. + + + + IgnoreOnIsolate= + + Takes a boolean argument. If + , this unit will not be stopped when + isolating another unit. Defaults to + . + + + + StopWhenUnneeded= + + Takes a boolean argument. If + , this unit will be stopped when it is no + longer used. Note that, in order to minimize the work to be + executed, systemd will not stop units by default unless they + are conflicting with other units, or the user explicitly + requested their shut down. If this option is set, a unit will + be automatically cleaned up if no other active unit requires + it. Defaults to . + + + + RefuseManualStart= + RefuseManualStop= + + Takes a boolean argument. If + , this unit can only be activated or + deactivated indirectly. In this case, explicit start-up or + termination requested by the user is denied, however if it is + started or stopped as a dependency of another unit, start-up + or termination will succeed. This is mostly a safety feature + to ensure that the user does not accidentally activate units + that are not intended to be activated explicitly, and not + accidentally deactivate units that are not intended to be + deactivated. These options default to + . + + + + AllowIsolate= + + Takes a boolean argument. If + , this unit may be used with the + systemctl isolate command. Otherwise, this + will be refused. It probably is a good idea to leave this + disabled except for target units that shall be used similar to + runlevels in SysV init systems, just as a precaution to avoid + unusable system states. This option defaults to + . + + + + DefaultDependencies= + + Takes a boolean argument. If + , (the default), a few default + dependencies will implicitly be created for the unit. The + actual dependencies created depend on the unit type. For + example, for service units, these dependencies ensure that the + service is started only after basic system initialization is + completed and is properly terminated on system shutdown. See + the respective man pages for details. Generally, only services + involved with early boot or late shutdown should set this + option to . It is highly recommended to + leave this option enabled for the majority of common units. If + set to , this option does not disable + all implicit dependencies, just non-essential + ones. + + + + JobTimeoutSec= + JobTimeoutAction= + JobTimeoutRebootArgument= + + When a job for this unit is queued, a time-out may be configured. If this time limit is + reached, the job will be cancelled, the unit however will not change state or even enter the + failed mode. This value defaults to infinity (job timeouts disabled), + except for device units. NB: this timeout is independent from any unit-specific timeout (for example, the + timeout set with TimeoutStartSec= in service units) as the job timeout has no effect on the + unit itself, only on the job that might be pending for it. Or in other words: unit-specific timeouts are useful + to abort unit state changes, and revert them. The job timeout set with this option however is useful to abort + only the job waiting for the unit state to change. + + JobTimeoutAction= + optionally configures an additional + action to take when the time-out is + hit. It takes the same values as the + per-service + StartLimitAction= + setting, see + systemd.service5 + for details. Defaults to + . JobTimeoutRebootArgument= + configures an optional reboot string + to pass to the + reboot2 + system call. + + + + StartLimitIntervalSec= + StartLimitBurst= + + Configure unit start rate limiting. By default, units which are started more than 5 times + within 10 seconds are not permitted to start any more times until the 10 second interval ends. With these two + options, this rate limiting may be modified. Use StartLimitIntervalSec= to configure the + checking interval (defaults to DefaultStartLimitIntervalSec= in manager configuration file, + set to 0 to disable any kind of rate limiting). Use StartLimitBurst= to configure how many + starts per interval are allowed (defaults to DefaultStartLimitBurst= in manager + configuration file). These configuration options are particularly useful in conjunction with the service + setting Restart= (see + systemd.service5); however, + they apply to all kinds of starts (including manual), not just those triggered by the + Restart= logic. Note that units which are configured for Restart= and + which reach the start limit are not attempted to be restarted anymore; however, they may still be restarted + manually at a later point, from which point on, the restart logic is again activated. Note that + systemctl reset-failed will cause the restart rate counter for a service to be flushed, + which is useful if the administrator wants to manually start a unit and the start limit interferes with + that. Note that this rate-limiting is enforced after any unit condition checks are executed, and hence unit + activations with failing conditions are not counted by this rate limiting. Slice, target, device and scope + units do not enforce this setting, as they are unit types whose activation may either never fail, or may + succeed only a single time. + + + + StartLimitAction= + + Configure the action to take if the rate limit configured with + StartLimitIntervalSec= and StartLimitBurst= is hit. Takes one of + , , , + , , or + . If is set, hitting the rate limit will trigger no + action besides that the start will not be permitted. causes a reboot following the + normal shutdown procedure (i.e. equivalent to systemctl reboot). + causes a forced reboot which will terminate all processes forcibly but should + cause no dirty file systems on reboot (i.e. equivalent to systemctl reboot -f) and + causes immediate execution of the + reboot2 system call, which + might result in data loss. Similarly, , , + have the effect of powering down the system with similar + semantics. Defaults to . + + + + RebootArgument= + Configure the optional argument for the + reboot2 system call if + StartLimitAction= or a service's FailureAction= is a reboot action. This + works just like the optional argument to systemctl reboot command. + + + + ConditionArchitecture= + ConditionVirtualization= + ConditionHost= + ConditionKernelCommandLine= + ConditionSecurity= + ConditionCapability= + ConditionACPower= + ConditionNeedsUpdate= + ConditionFirstBoot= + ConditionPathExists= + ConditionPathExistsGlob= + ConditionPathIsDirectory= + ConditionPathIsSymbolicLink= + ConditionPathIsMountPoint= + ConditionPathIsReadWrite= + ConditionDirectoryNotEmpty= + ConditionFileNotEmpty= + ConditionFileIsExecutable= + + + + Before starting a unit, verify that the specified condition is true. If it is not true, the + starting of the unit will be (mostly silently) skipped, however all ordering dependencies of it are still + respected. A failing condition will not result in the unit being moved into a failure state. The condition is + checked at the time the queued start job is to be executed. Use condition expressions in order to silently skip + units that do not apply to the local running system, for example because the kernel or runtime environment + doesn't require its functionality. Use the various AssertArchitecture=, + AssertVirtualization=, … options for a similar mechanism that puts the unit in a failure + state and logs about the failed check (see below). + + ConditionArchitecture= may be used to + check whether the system is running on a specific + architecture. Takes one of + x86, + x86-64, + ppc, + ppc-le, + ppc64, + ppc64-le, + ia64, + parisc, + parisc64, + s390, + s390x, + sparc, + sparc64, + mips, + mips-le, + mips64, + mips64-le, + alpha, + arm, + arm-be, + arm64, + arm64-be, + sh, + sh64, + m86k, + tilegx, + cris to test + against a specific architecture. The architecture is + determined from the information returned by + uname2 + and is thus subject to + personality2. + Note that a Personality= setting in the + same unit file has no effect on this condition. A special + architecture name native is mapped to the + architecture the system manager itself is compiled for. The + test may be negated by prepending an exclamation mark. + + ConditionVirtualization= may be used + to check whether the system is executed in a virtualized + environment and optionally test whether it is a specific + implementation. Takes either boolean value to check if being + executed in any virtualized environment, or one of + vm and + container to test against a generic type of + virtualization solution, or one of + qemu, + kvm, + zvm, + vmware, + microsoft, + oracle, + xen, + bochs, + uml, + openvz, + lxc, + lxc-libvirt, + systemd-nspawn, + docker, + rkt to test + against a specific implementation. See + systemd-detect-virt1 + for a full list of known virtualization technologies and their + identifiers. If multiple virtualization technologies are + nested, only the innermost is considered. The test may be + negated by prepending an exclamation mark. + + ConditionHost= may be used to match + against the hostname or machine ID of the host. This either + takes a hostname string (optionally with shell style globs) + which is tested against the locally set hostname as returned + by + gethostname2, + or a machine ID formatted as string (see + machine-id5). + The test may be negated by prepending an exclamation + mark. + + ConditionKernelCommandLine= may be + used to check whether a specific kernel command line option is + set (or if prefixed with the exclamation mark unset). The + argument must either be a single word, or an assignment (i.e. + two words, separated =). In the former case + the kernel command line is searched for the word appearing as + is, or as left hand side of an assignment. In the latter case, + the exact assignment is looked for with right and left hand + side matching. + + ConditionSecurity= may be used to + check whether the given security module is enabled on the + system. Currently, the recognized values are + selinux, + apparmor, + ima, + smack and + audit. The test may be negated by + prepending an exclamation mark. + + ConditionCapability= may be used to + check whether the given capability exists in the capability + bounding set of the service manager (i.e. this does not check + whether capability is actually available in the permitted or + effective sets, see + capabilities7 + for details). Pass a capability name such as + CAP_MKNOD, possibly prefixed with an + exclamation mark to negate the check. + + ConditionACPower= may be used to + check whether the system has AC power, or is exclusively + battery powered at the time of activation of the unit. This + takes a boolean argument. If set to true, + the condition will hold only if at least one AC connector of + the system is connected to a power source, or if no AC + connectors are known. Conversely, if set to + false, the condition will hold only if + there is at least one AC connector known and all AC connectors + are disconnected from a power source. + + ConditionNeedsUpdate= takes one of + /var or /etc as + argument, possibly prefixed with a ! (for + inverting the condition). This condition may be used to + conditionalize units on whether the specified directory + requires an update because /usr's + modification time is newer than the stamp file + .updated in the specified directory. This + is useful to implement offline updates of the vendor operating + system resources in /usr that require + updating of /etc or + /var on the next following boot. Units + making use of this condition should order themselves before + systemd-update-done.service8, + to make sure they run before the stamp file's modification + time gets reset indicating a completed update. + + ConditionFirstBoot= takes a boolean + argument. This condition may be used to conditionalize units + on whether the system is booting up with an unpopulated + /etc directory. This may be used to + populate /etc on the first boot after + factory reset, or when a new system instances boots up for the + first time. + + With ConditionPathExists= a file + existence condition is checked before a unit is started. If + the specified absolute path name does not exist, the condition + will fail. If the absolute path name passed to + ConditionPathExists= is prefixed with an + exclamation mark (!), the test is negated, + and the unit is only started if the path does not + exist. + + ConditionPathExistsGlob= is similar + to ConditionPathExists=, but checks for the + existence of at least one file or directory matching the + specified globbing pattern. + + ConditionPathIsDirectory= is similar + to ConditionPathExists= but verifies + whether a certain path exists and is a directory. + + ConditionPathIsSymbolicLink= is + similar to ConditionPathExists= but + verifies whether a certain path exists and is a symbolic + link. + + ConditionPathIsMountPoint= is similar + to ConditionPathExists= but verifies + whether a certain path exists and is a mount point. + + ConditionPathIsReadWrite= is similar + to ConditionPathExists= but verifies + whether the underlying file system is readable and writable + (i.e. not mounted read-only). + + ConditionDirectoryNotEmpty= is + similar to ConditionPathExists= but + verifies whether a certain path exists and is a non-empty + directory. + + ConditionFileNotEmpty= is similar to + ConditionPathExists= but verifies whether a + certain path exists and refers to a regular file with a + non-zero size. + + ConditionFileIsExecutable= is similar + to ConditionPathExists= but verifies + whether a certain path exists, is a regular file and marked + executable. + + If multiple conditions are specified, the unit will be + executed if all of them apply (i.e. a logical AND is applied). + Condition checks can be prefixed with a pipe symbol (|) in + which case a condition becomes a triggering condition. If at + least one triggering condition is defined for a unit, then the + unit will be executed if at least one of the triggering + conditions apply and all of the non-triggering conditions. If + you prefix an argument with the pipe symbol and an exclamation + mark, the pipe symbol must be passed first, the exclamation + second. Except for + ConditionPathIsSymbolicLink=, all path + checks follow symlinks. If any of these options is assigned + the empty string, the list of conditions is reset completely, + all previous condition settings (of any kind) will have no + effect. + + + + AssertArchitecture= + AssertVirtualization= + AssertHost= + AssertKernelCommandLine= + AssertSecurity= + AssertCapability= + AssertACPower= + AssertNeedsUpdate= + AssertFirstBoot= + AssertPathExists= + AssertPathExistsGlob= + AssertPathIsDirectory= + AssertPathIsSymbolicLink= + AssertPathIsMountPoint= + AssertPathIsReadWrite= + AssertDirectoryNotEmpty= + AssertFileNotEmpty= + AssertFileIsExecutable= + + Similar to the ConditionArchitecture=, + ConditionVirtualization=, …, condition settings described above, these settings add + assertion checks to the start-up of the unit. However, unlike the conditions settings, any assertion setting + that is not met results in failure of the start job (which means this is logged loudly). Use assertion + expressions for units that cannot operate when specific requirements are not met, and when this is something + the administrator or user should look into. + + + + SourcePath= + A path to a configuration file this unit has + been generated from. This is primarily useful for + implementation of generator tools that convert configuration + from an external configuration file format into native unit + files. This functionality should not be used in normal + units. + + + + + + + + [Install] Section Options + + Unit files may include an [Install] section, which carries installation information for + the unit. This section is not interpreted by + systemd1 during runtime; it is + used by the enable and disable commands of the + systemctl1 tool during + installation of a unit. Note that settings in the [Install] section may not appear in + .d/*.conf unit file drop-ins (see above). + + + + Alias= + + A space-separated list of additional names this unit shall be installed under. The names listed + here must have the same suffix (i.e. type) as the unit file name. This option may be specified more than once, + in which case all listed names are used. At installation time, systemctl enable will create + symlinks from these names to the unit filename. Note that not all unit types support such alias names, and this + setting is not supported for them. Specifically, mount, slice, swap, and automount units do not support + aliasing. + + + + WantedBy= + RequiredBy= + + This option may be used more than once, or a + space-separated list of unit names may be given. A symbolic + link is created in the .wants/ or + .requires/ directory of each of the + listed units when this unit is installed by systemctl + enable. This has the effect that a dependency of + type Wants= or Requires= + is added from the listed unit to the current unit. The primary + result is that the current unit will be started when the + listed unit is started. See the description of + Wants= and Requires= in + the [Unit] section for details. + + WantedBy=foo.service in a service + bar.service is mostly equivalent to + Alias=foo.service.wants/bar.service in the + same file. In case of template units, systemctl + enable must be called with an instance name, and + this instance will be added to the + .wants/ or + .requires/ list of the listed unit. E.g. + WantedBy=getty.target in a service + getty@.service will result in + systemctl enable getty@tty2.service + creating a + getty.target.wants/getty@tty2.service + link to getty@.service. + + + + + Also= + + Additional units to install/deinstall when + this unit is installed/deinstalled. If the user requests + installation/deinstallation of a unit with this option + configured, systemctl enable and + systemctl disable will automatically + install/uninstall units listed in this option as well. + + This option may be used more than once, or a + space-separated list of unit names may be + given. + + + + DefaultInstance= + + In template unit files, this specifies for + which instance the unit shall be enabled if the template is + enabled without any explicitly set instance. This option has + no effect in non-template unit files. The specified string + must be usable as instance identifier. + + + + The following specifiers are interpreted in the Install + section: %n, %N, %p, %i, %U, %u, %m, %H, %b, %v. For their meaning + see the next section. + + + + + Specifiers + + Many settings resolve specifiers which may be used to write + generic unit files referring to runtime or unit parameters that + are replaced when the unit files are loaded. The following + specifiers are understood: + + + Specifiers available in unit files + + + + + + + Specifier + Meaning + Details + + + + + %n + Full unit name + + + + %N + Unescaped full unit name + Same as %n, but with escaping undone + + + %p + Prefix name + For instantiated units, this refers to the string before the @ character of the unit name. For non-instantiated units, this refers to the name of the unit with the type suffix removed. + + + %P + Unescaped prefix name + Same as %p, but with escaping undone + + + %i + Instance name + For instantiated units: this is the string between the @ character and the suffix of the unit name. + + + %I + Unescaped instance name + Same as %i, but with escaping undone + + + %f + Unescaped filename + This is either the unescaped instance name (if applicable) with / prepended (if applicable), or the unescaped prefix name prepended with /. + + + %c + Control group path of the unit + This path does not include the /sys/fs/cgroup/systemd/ prefix. + + + %r + Control group path of the slice the unit is placed in + This usually maps to the parent cgroup path of %c. + + + %R + Root control group path below which slices and units are placed + For system instances, this resolves to /, except in containers, where this maps to the container's root control group path. + + + %t + Runtime directory + This is either /run (for the system manager) or the path $XDG_RUNTIME_DIR resolves to (for user managers). + + + %u + User name + This is the name of the user running the service manager instance. In case of the system manager this resolves to root. + + + %U + User UID + This is the numeric UID of the user running the service manager instance. In case of the system manager this resolves to 0. + + + %h + User home directory + This is the home directory of the user running the service manager instance. In case of the system manager this resolves to /root. + + + %s + User shell + This is the shell of the user running the service manager instance. In case of the system manager this resolves to /bin/sh. + + + %m + Machine ID + The machine ID of the running system, formatted as string. See machine-id5 for more information. + + + %b + Boot ID + The boot ID of the running system, formatted as string. See random4 for more information. + + + %H + Host name + The hostname of the running system at the point in time the unit configuration is loaded. + + + %v + Kernel release + Identical to uname -r output + + + %% + Single percent sign + Use %% in place of % to specify a single percent sign. + + + +
+ + Please note that specifiers %U, + %h, %s are mostly useless + when systemd is running in system mode. PID 1 cannot query the + user account database for information, so the specifiers only work + as shortcuts for things which are already specified in a different + way in the unit file. They are fully functional when systemd is + running in mode. +
+ + + Examples + + + Allowing units to be enabled + + The following snippet (highlighted) allows a unit (e.g. + foo.service) to be enabled via + systemctl enable: + + [Unit] +Description=Foo + +[Service] +ExecStart=/usr/sbin/foo-daemon + +[Install] +WantedBy=multi-user.target + + After running systemctl enable, a + symlink + /etc/systemd/system/multi-user.target.wants/foo.service + linking to the actual unit will be created. It tells systemd to + pull in the unit when starting + multi-user.target. The inverse + systemctl disable will remove that symlink + again. + + + + Overriding vendor settings + + There are two methods of overriding vendor settings in + unit files: copying the unit file from + /usr/lib/systemd/system to + /etc/systemd/system and modifying the + chosen settings. Alternatively, one can create a directory named + unit.d/ within + /etc/systemd/system and place a drop-in + file name.conf + there that only changes the specific settings one is interested + in. Note that multiple such drop-in files are read if + present. + + The advantage of the first method is that one easily + overrides the complete unit, the vendor unit is not parsed at + all anymore. It has the disadvantage that improvements to the + unit file by the vendor are not automatically incorporated on + updates. + + The advantage of the second method is that one only + overrides the settings one specifically wants, where updates to + the unit by the vendor automatically apply. This has the + disadvantage that some future updates by the vendor might be + incompatible with the local changes. + + Note that for drop-in files, if one wants to remove + entries from a setting that is parsed as a list (and is not a + dependency), such as ConditionPathExists= (or + e.g. ExecStart= in service units), one needs + to first clear the list before re-adding all entries except the + one that is to be removed. See below for an example. + + This also applies for user instances of systemd, but with + different locations for the unit files. See the section on unit + load paths for further details. + + Suppose there is a vendor-supplied unit + /usr/lib/systemd/system/httpd.service with + the following contents: + + [Unit] +Description=Some HTTP server +After=remote-fs.target sqldb.service +Requires=sqldb.service +AssertPathExists=/srv/webserver + +[Service] +Type=notify +ExecStart=/usr/sbin/some-fancy-httpd-server +Nice=5 + +[Install] +WantedBy=multi-user.target + + Now one wants to change some settings as an administrator: + firstly, in the local setup, /srv/webserver + might not exist, because the HTTP server is configured to use + /srv/www instead. Secondly, the local + configuration makes the HTTP server also depend on a memory + cache service, memcached.service, that + should be pulled in (Requires=) and also be + ordered appropriately (After=). Thirdly, in + order to harden the service a bit more, the administrator would + like to set the PrivateTmp= setting (see + systemd.service5 + for details). And lastly, the administrator would like to reset + the niceness of the service to its default value of 0. + + The first possibility is to copy the unit file to + /etc/systemd/system/httpd.service and + change the chosen settings: + + [Unit] +Description=Some HTTP server +After=remote-fs.target sqldb.service memcached.service +Requires=sqldb.service memcached.service +AssertPathExists=/srv/www + +[Service] +Type=notify +ExecStart=/usr/sbin/some-fancy-httpd-server +Nice=0 +PrivateTmp=yes + +[Install] +WantedBy=multi-user.target + + Alternatively, the administrator could create a drop-in + file + /etc/systemd/system/httpd.service.d/local.conf + with the following contents: + + [Unit] +After=memcached.service +Requires=memcached.service +# Reset all assertions and then re-add the condition we want +AssertPathExists= +AssertPathExists=/srv/www + +[Service] +Nice=0 +PrivateTmp=yes + + Note that dependencies (After=, etc.) + cannot be reset to an empty list, so dependencies can only be + added in drop-ins. If you want to remove dependencies, you have + to override the entire unit. + + + + + + See Also + + systemd1, + systemctl1, + systemd.special7, + systemd.service5, + systemd.socket5, + systemd.device5, + systemd.mount5, + systemd.automount5, + systemd.swap5, + systemd.target5, + systemd.path5, + systemd.timer5, + systemd.scope5, + systemd.slice5, + systemd.time7, + systemd-analyze1, + capabilities7, + systemd.directives7, + uname1 + + + +
diff --git a/src/grp-system/systemd/systemd.xml b/src/grp-system/systemd/systemd.xml new file mode 100644 index 0000000000..4f0201fc76 --- /dev/null +++ b/src/grp-system/systemd/systemd.xml @@ -0,0 +1,1155 @@ + + + + + + + + + systemd + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd + 1 + + + + systemd + init + systemd system and service manager + + + + + systemd OPTIONS + + + init OPTIONS COMMAND + + + + + Description + + systemd is a system and service manager for GNU/Linux operating + systems. When run as first process on boot (as PID 1), it acts as + init system that brings up and maintains userspace + services. + + For compatibility with SysV, if systemd is called as + init and a PID that is not 1, it will execute + telinit and pass all command line arguments + unmodified. That means init and + telinit are mostly equivalent when invoked from + normal login sessions. See + telinit8 + for more information. + + When run as a system instance, systemd interprets the + configuration file system.conf and the files + in system.conf.d directories; when run as a + user instance, systemd interprets the configuration file + user.conf and the files in + user.conf.d directories. See + systemd-system.conf5 + for more information. + + + + Options + + The following options are understood: + + + + + + Determine startup sequence, dump it and exit. + This is an option useful for debugging only. + + + + + Dump understood unit configuration items. This + outputs a terse but complete list of configuration items + understood in unit definition files. + + + + + Set default unit to activate on startup. If + not specified, defaults to + default.target. + + + + + + For , tell systemd to + run a system instance, even if the process ID is not 1, i.e. + systemd is not run as init process. + does the opposite, running a user instance even if the process + ID is 1. Normally, it should not be necessary to pass these + options, as systemd automatically detects the mode it is + started in. These options are hence of little use except for + debugging. Note that it is not supported booting and + maintaining a full system with systemd running in + mode, but PID not 1. In practice, + passing explicitly is only useful in + conjunction with . + + + + + Enable core dumping on crash. This switch has + no effect when running as user instance. This setting may also + be enabled during boot on the kernel command line via the + systemd.dump_core= option, see + below. + + + + VT + + Switch to a specific virtual console (VT) on + crash. Takes a positive integer in the range 1–63, or a + boolean argument. If an integer is passed, selects which VT to + switch to. If yes, the VT kernel messages + are written to is selected. If no, no VT + switch is attempted. This switch has no effect when running as + user instance. This setting may also be enabled during boot, + on the kernel command line via the + systemd.crash_vt= option, see + below. + + + + + + Run a shell on crash. This switch has no + effect when running as user instance. This setting may also be + enabled during boot, on the kernel command line via the + systemd.crash_shell= option, see + below. + + + + + + Automatically reboot the system on crash. This + switch has no effect when running as user instance. This + setting may also be enabled during boot, on the kernel command + line via the systemd.crash_reboot= option, + see below. + + + + + + Ask for confirmation when spawning processes. + This switch has no effect when run as user + instance. + + + + + Show terse service status information while + booting. This switch has no effect when run as user instance. + Takes a boolean argument which may be omitted which is + interpreted as . + + + + + Set log target. Argument must be one of + , + , + , + , + . + + + + + Set log level. As + argument this accepts a numerical log + level or the well-known syslog3 + symbolic names (lowercase): + , + , + , + , + , + , + , + . + + + + + Highlight important log messages. Argument is + a boolean value. If the argument is omitted, it defaults to + . + + + + + Include code location in log messages. This is + mostly relevant for debugging purposes. Argument is a boolean + value. If the argument is omitted it defaults to + . + + + + + + Sets the default output or error output for + all services and sockets, respectively. That is, controls the + default for and + (see + systemd.exec5 + for details). Takes one of + , + , + , + , + , + , + , + , + . If the + argument is omitted + defaults to + and + to + . + + + + + + Override the machine-id set on the hard drive, + useful for network booting or for containers. May not be set + to all zeros. + + + + + + + + + Concepts + + systemd provides a dependency system between various + entities called "units" of 12 different types. Units encapsulate + various objects that are relevant for system boot-up and + maintenance. The majority of units are configured in unit + configuration files, whose syntax and basic set of options is + described in + systemd.unit5, + however some are created automatically from other configuration, + dynamically from system state or programmatically at runtime. + Units may be "active" (meaning started, bound, plugged in, ..., + depending on the unit type, see below), or "inactive" (meaning + stopped, unbound, unplugged, ...), as well as in the process of + being activated or deactivated, i.e. between the two states (these + states are called "activating", "deactivating"). A special + "failed" state is available as well, which is very similar to + "inactive" and is entered when the service failed in some way + (process returned error code on exit, or crashed, or an operation + timed out). If this state is entered, the cause will be logged, + for later reference. Note that the various unit types may have a + number of additional substates, which are mapped to the five + generalized unit states described here. + + The following unit types are available: + + + Service units, which start and control daemons + and the processes they consist of. For details, see + systemd.service5. + + Socket units, which encapsulate local IPC or + network sockets in the system, useful for socket-based + activation. For details about socket units, see + systemd.socket5, + for details on socket-based activation and other forms of + activation, see + daemon7. + + Target units are useful to group units, or + provide well-known synchronization points during boot-up, see + systemd.target5. + + Device units expose kernel devices in systemd + and may be used to implement device-based activation. For + details, see + systemd.device5. + + Mount units control mount points in the file + system, for details see + systemd.mount5. + + Automount units provide automount capabilities, + for on-demand mounting of file systems as well as parallelized + boot-up. See + systemd.automount5. + + Timer units are useful for triggering activation + of other units based on timers. You may find details in + systemd.timer5. + + Swap units are very similar to mount units and + encapsulate memory swap partitions or files of the operating + system. They are described in + systemd.swap5. + + Path units may be used to activate other + services when file system objects change or are modified. See + systemd.path5. + + Slice units may be used to group units which + manage system processes (such as service and scope units) in a + hierarchical tree for resource management purposes. See + systemd.slice5. + + Scope units are similar to service units, but + manage foreign processes instead of starting them as well. See + systemd.scope5. + + + + Units are named as their configuration files. Some units + have special semantics. A detailed list is available in + systemd.special7. + + systemd knows various kinds of dependencies, including + positive and negative requirement dependencies (i.e. + Requires= and Conflicts=) as + well as ordering dependencies (After= and + Before=). NB: ordering and requirement + dependencies are orthogonal. If only a requirement dependency + exists between two units (e.g. foo.service + requires bar.service), but no ordering + dependency (e.g. foo.service after + bar.service) and both are requested to start, + they will be started in parallel. It is a common pattern that both + requirement and ordering dependencies are placed between two + units. Also note that the majority of dependencies are implicitly + created and maintained by systemd. In most cases, it should be + unnecessary to declare additional dependencies manually, however + it is possible to do this. + + Application programs and units (via dependencies) may + request state changes of units. In systemd, these requests are + encapsulated as 'jobs' and maintained in a job queue. Jobs may + succeed or can fail, their execution is ordered based on the + ordering dependencies of the units they have been scheduled + for. + + On boot systemd activates the target unit + default.target whose job is to activate + on-boot services and other on-boot units by pulling them in via + dependencies. Usually, the unit name is just an alias (symlink) for + either graphical.target (for fully-featured + boots into the UI) or multi-user.target (for + limited console-only boots for use in embedded or server + environments, or similar; a subset of graphical.target). However, + it is at the discretion of the administrator to configure it as an + alias to any other target unit. See + systemd.special7 + for details about these target units. + + Processes systemd spawns are placed in individual Linux + control groups named after the unit which they belong to in the + private systemd hierarchy. (see cgroups.txt + for more information about control groups, or short "cgroups"). + systemd uses this to effectively keep track of processes. Control + group information is maintained in the kernel, and is accessible + via the file system hierarchy (beneath + /sys/fs/cgroup/systemd/), or in tools such as + systemd-cgls1 + or + ps1 + (ps xawf -eo pid,user,cgroup,args is + particularly useful to list all processes and the systemd units + they belong to.). + + systemd is compatible with the SysV init system to a large + degree: SysV init scripts are supported and simply read as an + alternative (though limited) configuration file format. The SysV + /dev/initctl interface is provided, and + compatibility implementations of the various SysV client tools are + available. In addition to that, various established Unix + functionality such as /etc/fstab or the + utmp database are supported. + + systemd has a minimal transaction system: if a unit is + requested to start up or shut down it will add it and all its + dependencies to a temporary transaction. Then, it will verify if + the transaction is consistent (i.e. whether the ordering of all + units is cycle-free). If it is not, systemd will try to fix it up, + and removes non-essential jobs from the transaction that might + remove the loop. Also, systemd tries to suppress non-essential + jobs in the transaction that would stop a running service. Finally + it is checked whether the jobs of the transaction contradict jobs + that have already been queued, and optionally the transaction is + aborted then. If all worked out and the transaction is consistent + and minimized in its impact it is merged with all already + outstanding jobs and added to the run queue. Effectively this + means that before executing a requested operation, systemd will + verify that it makes sense, fixing it if possible, and only + failing if it really cannot work. + + Systemd contains native implementations of various tasks + that need to be executed as part of the boot process. For example, + it sets the hostname or configures the loopback network device. It + also sets up and mounts various API file systems, such as + /sys or /proc. + + For more information about the concepts and + ideas behind systemd, please refer to the + Original Design Document. + + Note that some but not all interfaces provided + by systemd are covered by the + Interface + Stability Promise. + + Units may be generated dynamically at boot and system + manager reload time, for example based on other configuration + files or parameters passed on the kernel command line. For details, see + systemd.generator7. + + Systems which invoke systemd in a container or initrd + environment should implement the + Container Interface or + initrd Interface + specifications, respectively. + + + + Directories + + + + System unit directories + + The systemd system manager reads unit + configuration from various directories. Packages that want to + install unit files shall place them in the directory returned + by pkg-config systemd + --variable=systemdsystemunitdir. Other directories + checked are /usr/local/lib/systemd/system + and /usr/lib/systemd/system. User + configuration always takes precedence. pkg-config + systemd --variable=systemdsystemconfdir returns the + path of the system configuration directory. Packages should + alter the content of these directories only with the + enable and disable + commands of the + systemctl1 + tool. Full list of directories is provided in + systemd.unit5. + + + + + + + User unit directories + + Similar rules apply for the user unit + directories. However, here the + XDG + Base Directory specification is followed to find + units. Applications should place their unit files in the + directory returned by pkg-config systemd + --variable=systemduserunitdir. Global configuration + is done in the directory reported by pkg-config + systemd --variable=systemduserconfdir. The + enable and disable + commands of the + systemctl1 + tool can handle both global (i.e. for all users) and private + (for one user) enabling/disabling of units. Full list of + directories is provided in + systemd.unit5. + + + + + + + SysV init scripts directory + + The location of the SysV init script directory + varies between distributions. If systemd cannot find a native + unit file for a requested service, it will look for a SysV + init script of the same name (with the + .service suffix + removed). + + + + + + SysV runlevel link farm directory + + The location of the SysV runlevel link farm + directory varies between distributions. systemd will take the + link farm into account when figuring out whether a service + shall be enabled. Note that a service unit with a native unit + configuration file cannot be started by activating it in the + SysV runlevel link farm. + + + + + + Signals + + + + SIGTERM + + Upon receiving this signal the systemd system + manager serializes its state, reexecutes itself and + deserializes the saved state again. This is mostly equivalent + to systemctl daemon-reexec. + + systemd user managers will start the + exit.target unit when this signal is + received. This is mostly equivalent to systemctl + --user start exit.target. + + + + SIGINT + + Upon receiving this signal the systemd system + manager will start the + ctrl-alt-del.target unit. This is mostly + equivalent to systemctl start + ctl-alt-del.target. If this signal is received more + than 7 times per 2s, an immediate reboot is triggered. + Note that pressing Ctrl-Alt-Del on the console will trigger + this signal. Hence, if a reboot is hanging, pressing + Ctrl-Alt-Del more than 7 times in 2s is a relatively safe way + to trigger an immediate reboot. + + systemd user managers treat this signal the same way as + SIGTERM. + + + + SIGWINCH + + When this signal is received the systemd + system manager will start the + kbrequest.target unit. This is mostly + equivalent to systemctl start + kbrequest.target. + + This signal is ignored by systemd user + managers. + + + + SIGPWR + + When this signal is received the systemd + manager will start the sigpwr.target + unit. This is mostly equivalent to systemctl start + sigpwr.target. + + + + SIGUSR1 + + When this signal is received the systemd + manager will try to reconnect to the D-Bus + bus. + + + + SIGUSR2 + + When this signal is received the systemd + manager will log its complete state in human-readable form. + The data logged is the same as printed by + systemd-analyze dump. + + + + SIGHUP + + Reloads the complete daemon configuration. + This is mostly equivalent to systemctl + daemon-reload. + + + + SIGRTMIN+0 + + Enters default mode, starts the + default.target unit. This is mostly + equivalent to systemctl start + default.target. + + + + SIGRTMIN+1 + + Enters rescue mode, starts the + rescue.target unit. This is mostly + equivalent to systemctl isolate + rescue.target. + + + + SIGRTMIN+2 + + Enters emergency mode, starts the + emergency.service unit. This is mostly + equivalent to systemctl isolate + emergency.service. + + + + SIGRTMIN+3 + + Halts the machine, starts the + halt.target unit. This is mostly + equivalent to systemctl start + halt.target. + + + + SIGRTMIN+4 + + Powers off the machine, starts the + poweroff.target unit. This is mostly + equivalent to systemctl start + poweroff.target. + + + + SIGRTMIN+5 + + Reboots the machine, starts the + reboot.target unit. This is mostly + equivalent to systemctl start + reboot.target. + + + + SIGRTMIN+6 + + Reboots the machine via kexec, starts the + kexec.target unit. This is mostly + equivalent to systemctl start + kexec.target. + + + + SIGRTMIN+13 + + Immediately halts the machine. + + + + SIGRTMIN+14 + + Immediately powers off the machine. + + + + SIGRTMIN+15 + + Immediately reboots the machine. + + + + SIGRTMIN+16 + + Immediately reboots the machine with kexec. + + + + SIGRTMIN+20 + + Enables display of status messages on the + console, as controlled via + systemd.show_status=1 on the kernel command + line. + + + + SIGRTMIN+21 + + Disables display of + status messages on the console, as + controlled via + systemd.show_status=0 + on the kernel command + line. + + + + SIGRTMIN+22 + SIGRTMIN+23 + + Sets the log level to debug + (or info on + SIGRTMIN+23), as controlled via + systemd.log_level=debug (or + systemd.log_level=info on + SIGRTMIN+23) on the kernel command + line. + + + + SIGRTMIN+24 + + Immediately exits the manager (only available + for --user instances). + + + + SIGRTMIN+26 + SIGRTMIN+27 + SIGRTMIN+28 + + Sets the log level to + journal-or-kmsg (or + console on + SIGRTMIN+27, kmsg on + SIGRTMIN+28), as controlled via + systemd.log_target=journal-or-kmsg (or + systemd.log_target=console on + SIGRTMIN+27 or + systemd.log_target=kmsg on + SIGRTMIN+28) on the kernel command + line. + + + + + + Environment + + + + $SYSTEMD_LOG_LEVEL + systemd reads the log level from this + environment variable. This can be overridden with + . + + + + $SYSTEMD_LOG_TARGET + systemd reads the log target from this + environment variable. This can be overridden with + . + + + + $SYSTEMD_LOG_COLOR + Controls whether systemd highlights important + log messages. This can be overridden with + . + + + + $SYSTEMD_LOG_LOCATION + Controls whether systemd prints the code + location along with log messages. This can be overridden with + . + + + + $XDG_CONFIG_HOME + $XDG_CONFIG_DIRS + $XDG_DATA_HOME + $XDG_DATA_DIRS + + The systemd user manager uses these variables + in accordance to the XDG + Base Directory specification to find its + configuration. + + + + $SYSTEMD_UNIT_PATH + + Controls where systemd looks for unit + files. + + + + $SYSTEMD_SYSVINIT_PATH + + Controls where systemd looks for SysV init + scripts. + + + + $SYSTEMD_SYSVRCND_PATH + + Controls where systemd looks for SysV init + script runlevel link farms. + + + + $SYSTEMD_COLORS + + Controls whether colorized output should be generated. + + + + + $LISTEN_PID + $LISTEN_FDS + $LISTEN_FDNAMES + + Set by systemd for supervised processes during + socket-based activation. See + sd_listen_fds3 + for more information. + + + + $NOTIFY_SOCKET + + Set by systemd for supervised processes for + status and start-up completion notification. See + sd_notify3 + for more information. + + + + + + Kernel Command Line + + When run as system instance systemd parses a number of + kernel command line argumentsIf run inside a Linux + container these arguments may be passed as command line arguments + to systemd itself, next to any of the command line options listed + in the Options section above. If run outside of Linux containers, + these arguments are parsed from /proc/cmdline + instead.: + + + + systemd.unit= + rd.systemd.unit= + + Overrides the unit to activate on boot. + Defaults to default.target. This may be + used to temporarily boot into a different boot unit, for + example rescue.target or + emergency.service. See + systemd.special7 + for details about these units. The option prefixed with + rd. is honored only in the initial RAM disk + (initrd), while the one that is not prefixed only in the main + system. + + + + systemd.dump_core= + + Takes a boolean argument. If + , the systemd manager (PID 1) dumps core + when it crashes. Otherwise, no core dump is created. Defaults + to . + + + + systemd.crash_chvt= + + Takes a positive integer, or a boolean + argument. If a positive integer (in the range 1–63) is + specified, the system manager (PID 1) will activate the specified + virtual terminal (VT) when it crashes. Defaults to + no, meaning that no such switch is + attempted. If set to yes, the VT the + kernel messages are written to is selected. + + + + systemd.crash_shell= + + Takes a boolean argument. If + , the system manager (PID 1) spawns a + shell when it crashes, after a 10s delay. Otherwise, no shell + is spawned. Defaults to , for security + reasons, as the shell is not protected by password + authentication. + + + + systemd.crash_reboot= + + Takes a boolean argument. If + , the system manager (PID 1) will reboot + the machine automatically when it crashes, after a 10s delay. + Otherwise, the system will hang indefinitely. Defaults to + , in order to avoid a reboot loop. If + combined with systemd.crash_shell=, the + system is rebooted after the shell exits. + + + + systemd.confirm_spawn= + + Takes a boolean argument. If + , the system manager (PID 1) asks for + confirmation when spawning processes. Defaults to + . + + + + systemd.show_status= + + Takes a boolean argument or the constant + auto. If , the + systemd manager (PID 1) shows terse service status updates on + the console during bootup. auto behaves + like until a service fails or there is + a significant delay in boot. Defaults to + , unless is passed + as kernel command line option, in which case it defaults to + auto. + + + + systemd.log_target= + systemd.log_level= + systemd.log_color= + systemd.log_location= + + Controls log output, with the same effect as + the $SYSTEMD_LOG_TARGET, + $SYSTEMD_LOG_LEVEL, + $SYSTEMD_LOG_COLOR, + $SYSTEMD_LOG_LOCATION environment variables + described above. + + + + systemd.default_standard_output= + systemd.default_standard_error= + Controls default standard output and error + output for services, with the same effect as the + and + command line + arguments described above, respectively. + + + + systemd.setenv= + + Takes a string argument in the form + VARIABLE=VALUE. May be used to set default environment + variables to add to forked child processes. May be used more + than once to set multiple variables. + + + + systemd.machine_id= + + Takes a 32 character hex value to be + used for setting the machine-id. Intended mostly for + network booting where the same machine-id is desired + for every boot. + + + + quiet + + Turn off status output at boot, much like + systemd.show_status=false would. Note that + this option is also read by the kernel itself and disables + kernel log output. Passing this option hence turns off the + usual output from both the system manager and the kernel. + + + + + debug + + Turn on debugging output. This is equivalent + to systemd.log_level=debug. Note that this + option is also read by the kernel itself and enables kernel + debug output. Passing this option hence turns on the debug + output from both the system manager and the + kernel. + + + + emergency + rd.emergency + -b + + Boot into emergency mode. This is equivalent + to systemd.unit=emergency.target or + rd.systemd.unit=emergency.target, respectively, and + provided for compatibility reasons and to be easier to type. + + + + rescue + rd.rescue + single + s + S + 1 + + Boot into rescue mode. This is equivalent to + systemd.unit=rescue.target or + rd.systemd.unit=rescue.target, respectively, and + provided for compatibility reasons and to be easier to type. + + + + 2 + 3 + 4 + 5 + + Boot into the specified legacy SysV runlevel. + These are equivalent to + systemd.unit=runlevel2.target, + systemd.unit=runlevel3.target, + systemd.unit=runlevel4.target, and + systemd.unit=runlevel5.target, + respectively, and provided for compatibility reasons and to be + easier to type. + + + + locale.LANG= + locale.LANGUAGE= + locale.LC_CTYPE= + locale.LC_NUMERIC= + locale.LC_TIME= + locale.LC_COLLATE= + locale.LC_MONETARY= + locale.LC_MESSAGES= + locale.LC_PAPER= + locale.LC_NAME= + locale.LC_ADDRESS= + locale.LC_TELEPHONE= + locale.LC_MEASUREMENT= + locale.LC_IDENTIFICATION= + + Set the system locale to use. This overrides + the settings in /etc/locale.conf. For + more information, see + locale.conf5 + and + locale7. + + + + + For other kernel command line parameters understood by + components of the core OS, please refer to + kernel-command-line7. + + + + Sockets and FIFOs + + + + /run/systemd/notify + + Daemon status notification socket. This is an + AF_UNIX datagram socket and is used to + implement the daemon notification logic as implemented by + sd_notify3. + + + + + /run/systemd/private + + Used internally as communication channel + between + systemctl1 + and the systemd process. This is an + AF_UNIX stream socket. This interface is + private to systemd and should not be used in external + projects. + + + + /dev/initctl + + Limited compatibility support for the SysV + client interface, as implemented by the + systemd-initctl.service unit. This is a + named pipe in the file system. This interface is obsolete and + should not be used in new applications. + + + + + + See Also + + The systemd Homepage, + systemd-system.conf5, + locale.conf5, + systemctl1, + journalctl1, + systemd-notify1, + daemon7, + sd-daemon3, + systemd.unit5, + systemd.special5, + pkg-config1, + kernel-command-line7, + bootup7, + systemd.directives7 + + + + diff --git a/src/grp-system/systemd/triggers.systemd.in b/src/grp-system/systemd/triggers.systemd.in new file mode 100644 index 0000000000..0d8c303136 --- /dev/null +++ b/src/grp-system/systemd/triggers.systemd.in @@ -0,0 +1,66 @@ +# -*- 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 . + +# The contents of this are an example to be copied into systemd.spec. +# +# Minimum rpm version supported: 4.13.0 + +%transfiletriggerin -P 900900 -p -- @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 -- @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 -- @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/grp-system/systemd/user.conf b/src/grp-system/systemd/user.conf new file mode 100644 index 0000000000..b427f1ef6d --- /dev/null +++ b/src/grp-system/systemd/user.conf @@ -0,0 +1,44 @@ +# 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. +# +# You can override the directives in this file by creating files in +# /etc/systemd/user.conf.d/*.conf. +# +# See systemd-user.conf(5) for details + +[Manager] +#LogLevel=info +#LogTarget=console +#LogColor=yes +#LogLocation=no +#SystemCallArchitectures= +#TimerSlackNSec= +#DefaultTimerAccuracySec=1min +#DefaultStandardOutput=inherit +#DefaultStandardError=inherit +#DefaultTimeoutStartSec=90s +#DefaultTimeoutStopSec=90s +#DefaultRestartSec=100ms +#DefaultStartLimitIntervalSec=10s +#DefaultStartLimitBurst=5 +#DefaultEnvironment= +#DefaultLimitCPU= +#DefaultLimitFSIZE= +#DefaultLimitDATA= +#DefaultLimitSTACK= +#DefaultLimitCORE= +#DefaultLimitRSS= +#DefaultLimitNOFILE= +#DefaultLimitAS= +#DefaultLimitNPROC= +#DefaultLimitMEMLOCK= +#DefaultLimitLOCKS= +#DefaultLimitSIGPENDING= +#DefaultLimitMSGQUEUE= +#DefaultLimitNICE= +#DefaultLimitRTPRIO= +#DefaultLimitRTTIME= diff --git a/src/grp-timedate/Makefile b/src/grp-timedate/Makefile new file mode 100644 index 0000000000..b9277c7c64 --- /dev/null +++ b/src/grp-timedate/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-timedated +nested.subdirs += timedatectl + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-timedate/systemd-timedated/.gitignore b/src/grp-timedate/systemd-timedated/.gitignore new file mode 100644 index 0000000000..48757f0968 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/.gitignore @@ -0,0 +1 @@ +org.freedesktop.timedate1.policy diff --git a/src/grp-timedate/systemd-timedated/Makefile b/src/grp-timedate/systemd-timedated/Makefile new file mode 100644 index 0000000000..d06142536e --- /dev/null +++ b/src/grp-timedate/systemd-timedated/Makefile @@ -0,0 +1,65 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_TIMEDATED),) +systemd_timedated_SOURCES = \ + src/timedate/timedated.c + +systemd_timedated_LDADD = \ + libsystemd-shared.la + +rootlibexec_PROGRAMS += \ + systemd-timedated + +dist_dbussystemservice_DATA += \ + src/timedate/org.freedesktop.timedate1.service + +dist_dbuspolicy_DATA += \ + src/timedate/org.freedesktop.timedate1.conf + +nodist_systemunit_DATA += \ + units/systemd-timedated.service + +dist_systemunit_DATA_busnames += \ + units/org.freedesktop.timedate1.busname + +polkitpolicy_files += \ + src/timedate/org.freedesktop.timedate1.policy + +SYSTEM_UNIT_ALIASES += \ + systemd-timedated.service dbus-org.freedesktop.timedate1.service + +BUSNAMES_TARGET_WANTS += \ + org.freedesktop.timedate1.busname + +endif # ENABLE_TIMEDATED + +polkitpolicy_in_files += \ + src/timedate/org.freedesktop.timedate1.policy.in + +EXTRA_DIST += \ + units/systemd-timedated.service.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.conf b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.conf new file mode 100644 index 0000000000..36557d5841 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.policy.in b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.policy.in new file mode 100644 index 0000000000..aa30b70831 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.policy.in @@ -0,0 +1,62 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Set system time + <_message>Authentication is required to set the system time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + org.freedesktop.timedate1.set-timezone org.freedesktop.timedate1.set-ntp + + + + <_description>Set system timezone + <_message>Authentication is required to set the system timezone. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set RTC to local timezone or UTC + <_message>Authentication is required to control whether + the RTC stores the local or UTC time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Turn network time synchronization on or off + <_message>Authentication is required to control whether + network time synchronization shall be enabled. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.service b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.service new file mode 100644 index 0000000000..875f4bec78 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/org.freedesktop.timedate1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.timedate1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.timedate1.service diff --git a/src/grp-timedate/systemd-timedated/systemd-timedated.service.in b/src/grp-timedate/systemd-timedated/systemd-timedated.service.in new file mode 100644 index 0000000000..bc1795d747 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/systemd-timedated.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Time & Date Service +Documentation=man:systemd-timedated.service(8) man:localtime(5) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/timedated + +[Service] +ExecStart=@rootlibexecdir@/systemd-timedated +BusName=org.freedesktop.timedate1 +CapabilityBoundingSet=CAP_SYS_TIME +WatchdogSec=3min +PrivateTmp=yes +ProtectSystem=yes +ProtectHome=yes +MemoryDenyWriteExecute=yes +SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io diff --git a/src/grp-timedate/systemd-timedated/systemd-timedated.service.xml b/src/grp-timedate/systemd-timedated/systemd-timedated.service.xml new file mode 100644 index 0000000000..e44163aefb --- /dev/null +++ b/src/grp-timedate/systemd-timedated/systemd-timedated.service.xml @@ -0,0 +1,85 @@ + + + + + + + + + systemd-timedated.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-timedated.service + 8 + + + + systemd-timedated.service + systemd-timedated + Time and date bus mechanism + + + + systemd-timedated.service + /usr/lib/systemd/systemd-timedated + + + + Description + + systemd-timedated is a system service + that may be used as a mechanism to change the system clock and + timezone, as well as to enable/disable NTP time synchronization. + systemd-timedated is automatically activated + on request and terminates itself when it is unused. + + The tool + timedatectl1 + is a command line client to this service. + + See the + developer documentation for information about the APIs + systemd-timedated provides. + + + + See Also + + systemd1, + timedatectl1, + localtime5, + hwclock8 + + + + diff --git a/src/grp-timedate/systemd-timedated/timedated.c b/src/grp-timedate/systemd-timedated/timedated.c new file mode 100644 index 0000000000..aad94024b8 --- /dev/null +++ b/src/grp-timedate/systemd-timedated/timedated.c @@ -0,0 +1,747 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/clock-util.h" +#include "basic/def.h" +#include "basic/fileio-label.h" +#include "basic/fs-util.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-bus/bus-common-errors.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" +#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" + +static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", EOPNOTSUPP), + SD_BUS_ERROR_MAP_END +}; + +typedef struct Context { + char *zone; + bool local_rtc; + bool can_ntp; + bool use_ntp; + Hashmap *polkit_registry; +} Context; + +static void context_free(Context *c) { + assert(c); + + free(c->zone); + bus_verify_polkit_async_registry_free(c->polkit_registry); +} + +static int context_read_data(Context *c) { + _cleanup_free_ char *t = NULL; + int r; + + assert(c); + + r = get_timezone(&t); + if (r == -EINVAL) + log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); + else if (r < 0) + log_warning_errno(r, "Failed to get target of /etc/localtime: %m"); + + free(c->zone); + c->zone = t; + t = NULL; + + c->local_rtc = clock_is_localtime(NULL) > 0; + + return 0; +} + +static int context_write_data_timezone(Context *c) { + _cleanup_free_ char *p = NULL; + int r = 0; + + assert(c); + + if (isempty(c->zone)) { + if (unlink("/etc/localtime") < 0 && errno != ENOENT) + r = -errno; + + return r; + } + + p = strappend("../usr/share/zoneinfo/", c->zone); + if (!p) + return log_oom(); + + r = symlink_atomic(p, "/etc/localtime"); + if (r < 0) + return r; + + return 0; +} + +static int context_write_data_local_rtc(Context *c) { + int r; + _cleanup_free_ char *s = NULL, *w = NULL; + + assert(c); + + r = read_full_file("/etc/adjtime", &s, NULL); + if (r < 0) { + if (r != -ENOENT) + return r; + + if (!c->local_rtc) + return 0; + + w = strdup(NULL_ADJTIME_LOCAL); + if (!w) + return -ENOMEM; + } else { + char *p; + const char *e = "\n"; /* default if there is less than 3 lines */ + const char *prepend = ""; + size_t a, b; + + p = strchrnul(s, '\n'); + if (*p == '\0') + /* only one line, no \n terminator */ + prepend = "\n0\n"; + else if (p[1] == '\0') { + /* only one line, with \n terminator */ + ++p; + prepend = "0\n"; + } else { + p = strchr(p+1, '\n'); + if (!p) { + /* only two lines, no \n terminator */ + prepend = "\n"; + p = s + strlen(s); + } else { + char *end; + /* third line might have a \n terminator or not */ + p++; + end = strchr(p, '\n'); + /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */ + if (end) + e = end; + } + } + + a = p - s; + b = strlen(e); + + w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1); + if (!w) + return -ENOMEM; + + *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + + if (streq(w, NULL_ADJTIME_UTC)) { + if (unlink("/etc/adjtime") < 0) + if (errno != ENOENT) + return -errno; + + return 0; + } + } + + mac_selinux_init(); + return write_string_file_atomic_label("/etc/adjtime", w); +} + +static int context_read_ntp(Context *c, 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; + const char *s; + int r; + + assert(c); + assert(bus); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitFileState", + &error, + &reply, + "s", + "systemd-timesyncd.service"); + + if (r < 0) { + if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) || + sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") || + sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit")) + return 0; + + return r; + } + + r = sd_bus_message_read(reply, "s", &s); + if (r < 0) + return r; + + c->can_ntp = true; + c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime"); + + return 0; +} + +static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { + int r; + + assert(bus); + assert(error); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + enabled ? "StartUnit" : "StopUnit", + error, + NULL, + "ss", + "systemd-timesyncd.service", + "replace"); + if (r < 0) { + if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) || + sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") || + sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) + return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); + + return r; + } + + return 0; +} + +static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { + int r; + + assert(bus); + assert(error); + + if (enabled) + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "EnableUnitFiles", + error, + NULL, + "asbb", 1, + "systemd-timesyncd.service", + false, true); + else + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "DisableUnitFiles", + error, + NULL, + "asb", 1, + "systemd-timesyncd.service", + false); + + if (r < 0) { + if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) + return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); + + return r; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reload", + error, + NULL, + NULL); + if (r < 0) + return r; + + return 0; +} + +static int property_get_rtc_time( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + struct tm tm; + usec_t t; + int r; + + zero(tm); + r = clock_get_hwclock(&tm); + if (r == -EBUSY) { + log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp."); + t = 0; + } else if (r == -ENOENT) { + log_debug("/dev/rtc not found."); + t = 0; /* no RTC found */ + } else if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m"); + else + t = (usec_t) timegm(&tm) * USEC_PER_SEC; + + return sd_bus_message_append(reply, "t", t); +} + +static int property_get_time( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME)); +} + +static int property_get_ntp_sync( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + return sd_bus_message_append(reply, "b", ntp_synced()); +} + +static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + const char *z; + int interactive; + char *t; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "sb", &z, &interactive); + if (r < 0) + return r; + + if (!timezone_is_valid(z)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z); + + if (streq_ptr(z, c->zone)) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async( + m, + CAP_SYS_TIME, + "org.freedesktop.timedate1.set-timezone", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + t = strdup(z); + if (!t) + return -ENOMEM; + + free(c->zone); + c->zone = t; + + /* 1. Write new configuration file */ + r = context_write_data_timezone(c); + if (r < 0) { + log_error_errno(r, "Failed to set time zone: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m"); + } + + /* 2. Tell the kernel our timezone */ + clock_set_timezone(NULL); + + if (c->local_rtc) { + struct timespec ts; + struct tm *tm; + + /* 3. Sync RTC from system clock, with the new delta */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + assert_se(tm = localtime(&ts.tv_sec)); + clock_set_hwclock(tm); + } + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), + "TIMEZONE=%s", c->zone, + LOG_MESSAGE("Changed time zone to '%s'.", c->zone), + NULL); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int lrtc, fix_system, interactive; + Context *c = userdata; + struct timespec ts; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive); + if (r < 0) + return r; + + if (lrtc == c->local_rtc) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async( + m, + CAP_SYS_TIME, + "org.freedesktop.timedate1.set-local-rtc", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; + + c->local_rtc = lrtc; + + /* 1. Write new configuration file */ + r = context_write_data_local_rtc(c); + if (r < 0) { + log_error_errno(r, "Failed to set RTC to local/UTC: %m"); + return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m"); + } + + /* 2. Tell the kernel our timezone */ + clock_set_timezone(NULL); + + /* 3. Synchronize clocks */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + + if (fix_system) { + struct tm tm; + + /* Sync system clock from RTC; first, + * initialize the timezone fields of + * struct tm. */ + if (c->local_rtc) + tm = *localtime(&ts.tv_sec); + else + tm = *gmtime(&ts.tv_sec); + + /* Override the main fields of + * struct tm, but not the timezone + * fields */ + if (clock_get_hwclock(&tm) >= 0) { + + /* And set the system clock + * with this */ + if (c->local_rtc) + ts.tv_sec = mktime(&tm); + else + ts.tv_sec = timegm(&tm); + + clock_settime(CLOCK_REALTIME, &ts); + } + + } else { + struct tm *tm; + + /* Sync RTC from system clock */ + if (c->local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); + + clock_set_hwclock(tm); + } + + log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC"); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int relative, interactive; + Context *c = userdata; + int64_t utc; + struct timespec ts; + usec_t start; + struct tm* tm; + int r; + + assert(m); + assert(c); + + if (c->use_ntp) + return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled"); + + /* this only gets used if dbus does not provide a timestamp */ + start = now(CLOCK_MONOTONIC); + + r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive); + if (r < 0) + return r; + + if (!relative && utc <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time"); + + if (relative && utc == 0) + return sd_bus_reply_method_return(m, NULL); + + if (relative) { + usec_t n, x; + + n = now(CLOCK_REALTIME); + x = n + utc; + + if ((utc > 0 && x < n) || + (utc < 0 && x > n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow"); + + timespec_store(&ts, x); + } else + timespec_store(&ts, (usec_t) utc); + + r = bus_verify_polkit_async( + m, + CAP_SYS_TIME, + "org.freedesktop.timedate1.set-time", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; + + /* adjust ts for time spent in program */ + r = sd_bus_message_get_monotonic_usec(m, &start); + /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */ + if (r < 0 && r != -ENODATA) + return r; + + timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start)); + + /* Set system clock */ + if (clock_settime(CLOCK_REALTIME, &ts) < 0) { + log_error_errno(errno, "Failed to set local time: %m"); + return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m"); + } + + /* Sync down to RTC */ + if (c->local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); + clock_set_hwclock(tm); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "REALTIME="USEC_FMT, timespec_load(&ts), + LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)), + NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int enabled, interactive; + Context *c = userdata; + int r; + + assert(m); + assert(c); + + r = sd_bus_message_read(m, "bb", &enabled, &interactive); + if (r < 0) + return r; + + if ((bool)enabled == c->use_ntp) + return sd_bus_reply_method_return(m, NULL); + + r = bus_verify_polkit_async( + m, + CAP_SYS_TIME, + "org.freedesktop.timedate1.set-ntp", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; + + r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled); + if (r < 0) + return r; + + r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled); + if (r < 0) + return r; + + c->use_ntp = enabled; + log_info("Set NTP to %s", enabled ? "enabled" : "disabled"); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); + + return sd_bus_reply_method_return(m, NULL); +} + +static const sd_bus_vtable timedate_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0), + SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0), + SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0), + SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0), + SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(c); + assert(event); + assert(_bus); + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); + + r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c); + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); + + r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0); + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + + *_bus = bus; + bus = NULL; + + return 0; +} + +int main(int argc, char *argv[]) { + Context context = {}; + _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); + log_parse_environment(); + log_open(); + + umask(0022); + + 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"); + goto finish; + } + + sd_event_set_watchdog(event, true); + + r = connect_bus(&context, event, &bus); + if (r < 0) + goto finish; + + (void) sd_bus_negotiate_timestamp(bus, true); + + r = context_read_data(&context); + if (r < 0) { + log_error_errno(r, "Failed to read time zone data: %m"); + goto finish; + } + + r = context_read_ntp(&context, bus); + if (r < 0) { + log_error_errno(r, "Failed to determine whether NTP is enabled: %m"); + goto finish; + } + + r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + +finish: + context_free(&context); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-timedate/timedatectl/Makefile b/src/grp-timedate/timedatectl/Makefile new file mode 100644 index 0000000000..b1093dad9b --- /dev/null +++ b/src/grp-timedate/timedatectl/Makefile @@ -0,0 +1,43 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_TIMEDATED),) +timedatectl_SOURCES = \ + src/timedate/timedatectl.c + +timedatectl_LDADD = \ + libsystemd-shared.la + +bin_PROGRAMS += \ + timedatectl + +dist_bashcompletion_data += \ + shell-completion/bash/timedatectl + +dist_zshcompletion_data += \ + shell-completion/zsh/_timedatectl +endif # ENABLE_TIMEDATED + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-timedate/timedatectl/timedatectl.c b/src/grp-timedate/timedatectl/timedatectl.c new file mode 100644 index 0000000000..14d66b557b --- /dev/null +++ b/src/grp-timedate/timedatectl/timedatectl.c @@ -0,0 +1,507 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/parse-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/pager.h" +#include "shared/spawn-polkit-agent.h" + +static bool arg_no_pager = false; +static bool arg_ask_password = true; +static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +static char *arg_host = NULL; +static bool arg_adjust_system_clock = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + usec_t time; + char *timezone; + + usec_t rtc_time; + int rtc_local; + + int ntp_enabled; + int ntp_capable; + int ntp_synced; +} StatusInfo; + +static void status_info_clear(StatusInfo *info) { + if (info) { + free(info->timezone); + zero(*info); + } +} + +static void print_status_info(const StatusInfo *i) { + char a[FORMAT_TIMESTAMP_MAX]; + struct tm tm; + time_t sec; + bool have_time = false; + const char *old_tz = NULL, *tz; + int r; + + assert(i); + + /* Save the old $TZ */ + tz = getenv("TZ"); + if (tz) + old_tz = strdupa(tz); + + /* Set the new $TZ */ + if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0) + log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); + else + tzset(); + + if (i->time != 0) { + sec = (time_t) (i->time / USEC_PER_SEC); + have_time = true; + } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) { + sec = time(NULL); + have_time = true; + } else + log_warning("Could not get time from timedated and not operating locally, ignoring."); + + if (have_time) { + xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)); + printf(" Local time: %.*s\n", (int) sizeof(a), a); + + xstrftime(a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)); + printf(" Universal time: %.*s\n", (int) sizeof(a), a); + } else { + printf(" Local time: %s\n", "n/a"); + printf(" Universal time: %s\n", "n/a"); + } + + if (i->rtc_time > 0) { + time_t rtc_sec; + + rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC); + xstrftime(a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)); + printf(" RTC time: %.*s\n", (int) sizeof(a), a); + } else + printf(" RTC time: %s\n", "n/a"); + + if (have_time) + xstrftime(a, "%Z, %z", localtime_r(&sec, &tm)); + + /* Restore the $TZ */ + if (old_tz) + r = setenv("TZ", old_tz, true); + else + r = unsetenv("TZ"); + if (r < 0) + log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); + else + tzset(); + + printf(" Time zone: %s (%.*s)\n" + " Network time on: %s\n" + "NTP synchronized: %s\n" + " RTC in local TZ: %s\n", + strna(i->timezone), (int) sizeof(a), have_time ? a : "n/a", + i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a", + yes_no(i->ntp_synced), + yes_no(i->rtc_local)); + + if (i->rtc_local) + printf("\n%s" + "Warning: The system is configured to read the RTC time in the local time zone.\n" + " This mode can not be fully supported. It will create various problems\n" + " with time zone changes and daylight saving time adjustments. The RTC\n" + " time is never updated, it relies on external facilities to maintain it.\n" + " If at all possible, use RTC in UTC by calling\n" + " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal()); +} + +static int show_status(sd_bus *bus, char **args, unsigned n) { + _cleanup_(status_info_clear) StatusInfo info = {}; + static const struct bus_properties_map map[] = { + { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) }, + { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) }, + { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) }, + { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) }, + { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) }, + { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) }, + { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) }, + {} + }; + int r; + + assert(bus); + + r = bus_map_all_properties(bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + map, + &info); + if (r < 0) + return log_error_errno(r, "Failed to query server: %m"); + + print_status_info(&info); + + return r; +} + +static int set_time(sd_bus *bus, char **args, unsigned n) { + _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; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + r = parse_timestamp(args[1], &t); + if (r < 0) { + log_error("Failed to parse time specification: %s", args[1]); + return r; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTime", + &error, + NULL, + "xbb", (int64_t)t, relative, interactive); + if (r < 0) + log_error("Failed to set time: %s", bus_error_message(&error, -r)); + + return r; +} + +static int set_timezone(sd_bus *bus, char **args, unsigned n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call_method(bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTimezone", + &error, + NULL, + "sb", args[1], arg_ask_password); + if (r < 0) + log_error("Failed to set time zone: %s", bus_error_message(&error, -r)); + + return r; +} + +static int set_local_rtc(sd_bus *bus, char **args, unsigned n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r, b; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + b = parse_boolean(args[1]); + if (b < 0) { + log_error("Failed to parse local RTC setting: %s", args[1]); + return b; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetLocalRTC", + &error, + NULL, + "bbb", b, arg_adjust_system_clock, arg_ask_password); + if (r < 0) + log_error("Failed to set local RTC: %s", bus_error_message(&error, -r)); + + return r; +} + +static int set_ntp(sd_bus *bus, char **args, unsigned n) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int b, r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + b = parse_boolean(args[1]); + if (b < 0) { + log_error("Failed to parse NTP setting: %s", args[1]); + return b; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetNTP", + &error, + NULL, + "bb", b, arg_ask_password); + if (r < 0) + log_error("Failed to set ntp: %s", bus_error_message(&error, -r)); + + return r; +} + +static int list_timezones(sd_bus *bus, char **args, unsigned n) { + _cleanup_strv_free_ char **zones = NULL; + int r; + + assert(args); + assert(n == 1); + + r = get_timezones(&zones); + if (r < 0) + return log_error_errno(r, "Failed to read list of time zones: %m"); + + pager_open(arg_no_pager, false); + strv_print(zones); + + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...] COMMAND ...\n\n" + "Query or change system time and date settings.\n\n" + " -h --help Show this help message\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --adjust-system-clock Adjust system clock when changing local RTC mode\n\n" + "Commands:\n" + " status Show current time settings\n" + " set-time TIME Set system time\n" + " set-timezone ZONE Set system time zone\n" + " list-timezones Show known time zones\n" + " set-local-rtc BOOL Control whether RTC is in local time\n" + " set-ntp BOOL Enable or disable network time synchronization\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_ADJUST_SYSTEM_CLOCK, + ARG_NO_ASK_PASSWORD + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'H': + arg_transport = BUS_TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = BUS_TRANSPORT_MACHINE; + arg_host = optarg; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case ARG_ADJUST_SYSTEM_CLOCK: + arg_adjust_system_clock = true; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(sd_bus *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-time", EQUAL, 2, set_time }, + { "set-timezone", EQUAL, 2, set_timezone }, + { "list-timezones", EQUAL, 1, list_timezones }, + { "set-local-rtc", EQUAL, 2, set_local_rtc }, + { "set-ntp", EQUAL, 2, set_ntp, }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + sd_bus *bus = NULL; + int r; + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + 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; + } + + r = timedatectl_main(bus, argc, argv); + +finish: + sd_bus_flush_close_unref(bus); + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-timedate/timedatectl/timedatectl.completion.bash b/src/grp-timedate/timedatectl/timedatectl.completion.bash new file mode 100644 index 0000000000..a57fbd2546 --- /dev/null +++ b/src/grp-timedate/timedatectl/timedatectl.completion.bash @@ -0,0 +1,76 @@ +# timedatectl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_timedatectl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --adjust-system-clock --no-pager + --no-ask-password -H --host --machine' + + if __contains_word "$prev" $OPTS; then + case $prev in + --host|-H) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ $cur = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [BOOLEAN]='set-local-rtc set-ntp' + [STANDALONE]='status set-time list-timezones' + [TIMEZONES]='set-timezone' + [TIME]='set-time' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[BOOLEAN]}; then + comps='true false' + elif __contains_word "$verb" ${VERBS[TIMEZONES]}; then + comps=$(command timedatectl list-timezones) + elif __contains_word "$verb" ${VERBS[STANDALONE]} ${VERBS[TIME]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _timedatectl timedatectl diff --git a/src/grp-timedate/timedatectl/timedatectl.completion.zsh b/src/grp-timedate/timedatectl/timedatectl.completion.zsh new file mode 100644 index 0000000000..dfdcfebb3c --- /dev/null +++ b/src/grp-timedate/timedatectl/timedatectl.completion.zsh @@ -0,0 +1,66 @@ +#compdef timedatectl + +_timedatectl_set-timezone(){ + local -a _timezones + _timezones=( ${(f)"$(_call_program timezones "${service}" list-timezones)"} ) + compadd "$_timezones[@]" +} + +_timedatectl_set-time(){ + _message "YYYY-MM-DD HH:MM:SS" +} + +_timedatectl_set-local-rtc(){ + local -a _options + _options=( + '0:Maintain RTC in universal time' + '1:Maintain RTC in local time' + ) + _describe options _options +} + +_timedatectl_set-ntp(){ + local -a _options + _options=( + '0:Disable NTP based network time configuration' + '1:Enable NTP based network time configuration' + ) + _describe options _options +} + +_timedatectl_command(){ + local -a _timedatectl_cmds + _timedatectl_cmds=( + 'status:Show current time settings' + 'set-time:Set system time' + 'set-timezone:Set system timezone' + 'list-timezones:Show known timezones' + 'set-local-rtc:Control whether RTC is in local time' + 'set-ntp:Control whether NTP is enabled' + ) + if (( CURRENT == 1 )); then + _describe -t commands 'timedatectl command' _timedatectl_cmds + else + local curcontext="$curcontext" + cmd="${${_timedatectl_cmds[(r)$words[1]:*]%%:*}}" + if (( $#cmd )); then + if (( $+functions[_timedatectl_$cmd] )); then + _timedatectl_$cmd + else + _message "no more options" + fi + else + _message "unknown timedatectl command: $words[1]" + fi + fi +} + +_arguments -s \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--adjust-system-clock[Adjust system clock when changing local RTC mode]' \ + '--no-pager[Do not pipe output into a pager]' \ + '--no-ask-password[Do not prompt for password]' \ + {-H+,--host=}'[Operate on remote host]:userathost:_sd_hosts_or_user_at_host' \ + {-M+,--machine=}'[Operate on local container]:machines:_sd_machines' \ + '*::timedatectl commands:_timedatectl_command' diff --git a/src/grp-timedate/timedatectl/timedatectl.xml b/src/grp-timedate/timedatectl/timedatectl.xml new file mode 100644 index 0000000000..415e2c799a --- /dev/null +++ b/src/grp-timedate/timedatectl/timedatectl.xml @@ -0,0 +1,253 @@ + + + + + + + + + timedatectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + timedatectl + 1 + + + + timedatectl + Control the system time and date + + + + + timedatectl OPTIONS COMMAND + + + + + Description + + timedatectl may be used to query and + change the system clock and its settings. + + Use + systemd-firstboot1 + to initialize the system time zone for mounted (but not booted) + system images. + + + + Options + + The following options are understood: + + + + + + Do not query the user for authentication for + privileged operations. + + + + + + If set-local-rtc is invoked + and this option is passed, the system clock is synchronized + from the RTC again, taking the new setting into account. + Otherwise, the RTC is synchronized from the system + clock. + + + + + + + + + + + The following commands are understood: + + + + status + + Show current settings of the system clock and + RTC, including whether network time synchronization is + on. Note that whether network time synchronization is on + simply reflects whether the + systemd-timesyncd.service unit is + enabled. Even if this command shows the status as off, a + different service might still synchronize the clock with the + network. + + + + set-time [TIME] + + Set the system clock to the specified time. + This will also update the RTC time accordingly. The time may + be specified in the format "2012-10-30 + 18:17:16". + + + + set-timezone [TIMEZONE] + + Set the system time zone to the specified + value. Available timezones can be listed with + list-timezones. If the RTC is configured to + be in the local time, this will also update the RTC time. This + call will alter the /etc/localtime + symlink. See + localtime5 + for more information. + + + + list-timezones + + List available time zones, one per line. + Entries from the list can be set as the system timezone with + set-timezone. + + + + set-local-rtc [BOOL] + + Takes a boolean argument. If + 0, the system is configured to maintain the + RTC in universal time. If 1, it will + maintain the RTC in local time instead. Note that maintaining + the RTC in the local timezone is not fully supported and will + create various problems with time zone changes and daylight + saving adjustments. If at all possible, keep the RTC in UTC + mode. Note that invoking this will also synchronize the RTC + from the system clock, unless + is passed (see above). + This command will change the 3rd line of + /etc/adjtime, as documented in + hwclock8. + + + + + set-ntp [BOOL] + + Takes a boolean argument. Controls whether + network time synchronization is active and enabled (if + available). This enables and starts, or disables and stops the + systemd-timesyncd.service unit. It does + not affect the state of any other, unrelated network time + synchronization services that might be installed on the + system. This command is hence mostly equivalent to: + systemctl enable --now + systemd-timesyncd.service and systemctl + disable --now systemd-timesyncd.service, but is + protected by a different access policy. + + Note that even if time synchronization is turned off + with this command, another unrelated system service might + still synchronize the clock with the network. Also note that, + strictly speaking, + systemd-timesyncd.service does more than + just network time synchronization, as it ensures a monotonic + clock on systems without RTC even if no network is + available. See + systemd-timesyncd.service8 + for details about this. + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + + + Examples + Show current settings: + $ timedatectl + Local time: Di 2015-04-07 16:26:56 CEST + Universal time: Di 2015-04-07 14:26:56 UTC + RTC time: Di 2015-04-07 14:26:56 + Time zone: Europe/Berlin (CEST, +0200) + Network time on: yes +NTP synchronized: yes + RTC in local TZ: no + + + Enable network time synchronization: + $ timedatectl set-ntp true +==== AUTHENTICATING FOR org.freedesktop.timedate1.set-ntp === +Authentication is required to control whether network time synchronization shall be enabled. +Authenticating as: user +Password: ******** +==== AUTHENTICATION COMPLETE === + + $ systemctl status systemd-timesyncd.service +● systemd-timesyncd.service - Network Time Synchronization + Loaded: loaded (/usr/lib/systemd/system/systemd-timesyncd.service; enabled) + Active: active (running) since Mo 2015-03-30 14:20:38 CEST; 5s ago + Docs: man:systemd-timesyncd.service(8) + Main PID: 595 (systemd-timesyn) + Status: "Using Time Server 216.239.38.15:123 (time4.google.com)." + CGroup: /system.slice/systemd-timesyncd.service + └─595 /usr/lib/systemd/systemd-timesyncd +... + + + + + See Also + + systemd1, + hwclock8, + date1, + localtime5, + systemctl1, + systemd-timedated.service8, + systemd-timesyncd.service8, + systemd-firstboot1 + + + + diff --git a/src/grp-udev/.gitignore b/src/grp-udev/.gitignore new file mode 100644 index 0000000000..f5d8be3dc1 --- /dev/null +++ b/src/grp-udev/.gitignore @@ -0,0 +1,4 @@ +/udev.pc +/keyboard-keys-from-name.gperf +/keyboard-keys-from-name.h +/keyboard-keys-list.txt diff --git a/src/grp-udev/Makefile b/src/grp-udev/Makefile new file mode 100644 index 0000000000..ac635bfdba --- /dev/null +++ b/src/grp-udev/Makefile @@ -0,0 +1,90 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +INSTALL_DIRS += \ + $(sysconfdir)/udev/rules.d + +dist_udevrules_DATA += \ + rules/50-udev-default.rules \ + rules/60-block.rules \ + rules/60-drm.rules \ + rules/60-evdev.rules \ + rules/60-persistent-storage-tape.rules \ + rules/60-persistent-input.rules \ + rules/60-persistent-alsa.rules \ + rules/60-persistent-storage.rules \ + rules/60-serial.rules \ + rules/64-btrfs.rules \ + rules/70-mouse.rules \ + rules/70-touchpad.rules \ + rules/75-net-description.rules \ + rules/78-sound-card.rules \ + rules/80-net-setup-link.rules + +nodist_udevrules_DATA += \ + rules/99-systemd.rules + +udevconfdir = $(sysconfdir)/udev +dist_udevconf_DATA = \ + src/udev/udev.conf + +pkgconfigdata_DATA += \ + src/udev/udev.pc + +EXTRA_DIST += \ + rules/99-systemd.rules.in \ + src/udev/udev.pc.in + +EXTRA_DIST += \ + units/systemd-udevd.service.in \ + units/systemd-udev-trigger.service.in \ + units/systemd-udev-settle.service.in + +SOCKETS_TARGET_WANTS += \ + systemd-udevd-control.socket \ + systemd-udevd-kernel.socket + +SYSINIT_TARGET_WANTS += \ + systemd-udevd.service \ + systemd-udev-trigger.service + +ifneq ($(HAVE_KMOD),) +dist_udevrules_DATA += \ + rules/80-drivers.rules +endif # HAVE_KMOD + +nested.subdirs += ata_id +nested.subdirs += cdrom_id +nested.subdirs += collect +nested.subdirs += hwdb +nested.subdirs += libudev-core +nested.subdirs += mtd_probe +nested.subdirs += scsi_id +nested.subdirs += systemd-hwdb +nested.subdirs += systemd-udevd +nested.subdirs += udevadm +nested.subdirs += v4l_id + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/ata_id/Makefile b/src/grp-udev/ata_id/Makefile new file mode 100644 index 0000000000..00a8c37ac2 --- /dev/null +++ b/src/grp-udev/ata_id/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ata_id_SOURCES = \ + src/udev/ata_id/ata_id.c + +ata_id_LDADD = \ + libshared.la + +udevlibexec_PROGRAMS += \ + ata_id + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/ata_id/ata_id.c b/src/grp-udev/ata_id/ata_id.c new file mode 100644 index 0000000000..4728d72a57 --- /dev/null +++ b/src/grp-udev/ata_id/ata_id.c @@ -0,0 +1,675 @@ +/* + * ata_id - reads product/serial number from ATA drives + * + * Copyright (C) 2005-2008 Kay Sievers + * Copyright (C) 2009 Lennart Poettering + * Copyright (C) 2009-2010 David Zeuthen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "libudev-private.h" +#include "shared/udev-util.h" + +#define COMMAND_TIMEOUT_MSEC (30 * 1000) + +static int disk_scsi_inquiry_command(int fd, + void *buf, + size_t buf_len) +{ + uint8_t cdb[6] = { + /* + * INQUIRY, see SPC-4 section 6.4 + */ + [0] = 0x12, /* OPERATION CODE: INQUIRY */ + [3] = (buf_len >> 8), /* ALLOCATION LENGTH */ + [4] = (buf_len & 0xff), + }; + uint8_t sense[32] = {}; + struct sg_io_v4 io_v4 = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, + .request_len = sizeof(cdb), + .request = (uintptr_t) cdb, + .max_response_len = sizeof(sense), + .response = (uintptr_t) sense, + .din_xfer_len = buf_len, + .din_xferp = (uintptr_t) buf, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + int ret; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr = { + .interface_id = 'S', + .cmdp = (unsigned char*) cdb, + .cmd_len = sizeof (cdb), + .dxferp = buf, + .dxfer_len = buf_len, + .sbp = sense, + .mx_sb_len = sizeof(sense), + .dxfer_direction = SG_DXFER_FROM_DEV, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + return ret; + + /* even if the ioctl succeeds, we need to check the return value */ + if (!(io_hdr.status == 0 && + io_hdr.host_status == 0 && + io_hdr.driver_status == 0)) { + errno = EIO; + return -1; + } + } else + return ret; + } + + /* even if the ioctl succeeds, we need to check the return value */ + if (!(io_v4.device_status == 0 && + io_v4.transport_status == 0 && + io_v4.driver_status == 0)) { + errno = EIO; + return -1; + } + + return 0; +} + +static int disk_identify_command(int fd, + void *buf, + size_t buf_len) +{ + uint8_t cdb[12] = { + /* + * ATA Pass-Through 12 byte command, as described in + * + * T10 04-262r8 ATA Command Pass-Through + * + * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf + */ + [0] = 0xa1, /* OPERATION CODE: 12 byte pass through */ + [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ + [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ + [3] = 0, /* FEATURES */ + [4] = 1, /* SECTORS */ + [5] = 0, /* LBA LOW */ + [6] = 0, /* LBA MID */ + [7] = 0, /* LBA HIGH */ + [8] = 0 & 0x4F, /* SELECT */ + [9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */ + }; + uint8_t sense[32] = {}; + uint8_t *desc = sense + 8; + struct sg_io_v4 io_v4 = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, + .request_len = sizeof(cdb), + .request = (uintptr_t) cdb, + .max_response_len = sizeof(sense), + .response = (uintptr_t) sense, + .din_xfer_len = buf_len, + .din_xferp = (uintptr_t) buf, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + int ret; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr = { + .interface_id = 'S', + .cmdp = (unsigned char*) cdb, + .cmd_len = sizeof (cdb), + .dxferp = buf, + .dxfer_len = buf_len, + .sbp = sense, + .mx_sb_len = sizeof (sense), + .dxfer_direction = SG_DXFER_FROM_DEV, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + return ret; + } else + return ret; + } + + if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + return -1; + } + + return 0; +} + +static int disk_identify_packet_device_command(int fd, + void *buf, + size_t buf_len) +{ + uint8_t cdb[16] = { + /* + * ATA Pass-Through 16 byte command, as described in + * + * T10 04-262r8 ATA Command Pass-Through + * + * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf + */ + [0] = 0x85, /* OPERATION CODE: 16 byte pass through */ + [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ + [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ + [3] = 0, /* FEATURES */ + [4] = 0, /* FEATURES */ + [5] = 0, /* SECTORS */ + [6] = 1, /* SECTORS */ + [7] = 0, /* LBA LOW */ + [8] = 0, /* LBA LOW */ + [9] = 0, /* LBA MID */ + [10] = 0, /* LBA MID */ + [11] = 0, /* LBA HIGH */ + [12] = 0, /* LBA HIGH */ + [13] = 0, /* DEVICE */ + [14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */ + [15] = 0, /* CONTROL */ + }; + uint8_t sense[32] = {}; + uint8_t *desc = sense + 8; + struct sg_io_v4 io_v4 = { + .guard = 'Q', + .protocol = BSG_PROTOCOL_SCSI, + .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, + .request_len = sizeof (cdb), + .request = (uintptr_t) cdb, + .max_response_len = sizeof (sense), + .response = (uintptr_t) sense, + .din_xfer_len = buf_len, + .din_xferp = (uintptr_t) buf, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + int ret; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr = { + .interface_id = 'S', + .cmdp = (unsigned char*) cdb, + .cmd_len = sizeof (cdb), + .dxferp = buf, + .dxfer_len = buf_len, + .sbp = sense, + .mx_sb_len = sizeof (sense), + .dxfer_direction = SG_DXFER_FROM_DEV, + .timeout = COMMAND_TIMEOUT_MSEC, + }; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + return ret; + } else + return ret; + } + + if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + return -1; + } + + return 0; +} + +/** + * disk_identify_get_string: + * @identify: A block of IDENTIFY data + * @offset_words: Offset of the string to get, in words. + * @dest: Destination buffer for the string. + * @dest_len: Length of destination buffer, in bytes. + * + * Copies the ATA string from @identify located at @offset_words into @dest. + */ +static void disk_identify_get_string(uint8_t identify[512], + unsigned int offset_words, + char *dest, + size_t dest_len) +{ + unsigned int c1; + unsigned int c2; + + while (dest_len > 0) { + c1 = identify[offset_words * 2 + 1]; + c2 = identify[offset_words * 2]; + *dest = c1; + dest++; + *dest = c2; + dest++; + offset_words++; + dest_len -= 2; + } +} + +static void disk_identify_fixup_string(uint8_t identify[512], + unsigned int offset_words, + size_t len) +{ + disk_identify_get_string(identify, offset_words, + (char *) identify + offset_words * 2, len); +} + +static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words) +{ + uint16_t *p; + + p = (uint16_t *) identify; + p[offset_words] = le16toh (p[offset_words]); +} + +/** + * disk_identify: + * @udev: The libudev context. + * @fd: File descriptor for the block device. + * @out_identify: Return location for IDENTIFY data. + * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. + * + * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the + * device represented by @fd. If successful, then the result will be + * copied into @out_identify and @out_is_packet_device. + * + * This routine is based on code from libatasmart, Copyright 2008 + * Lennart Poettering, LGPL v2.1. + * + * Returns: 0 if the data was successfully obtained, otherwise + * non-zero with errno set. + */ +static int disk_identify(struct udev *udev, + int fd, + uint8_t out_identify[512], + int *out_is_packet_device) +{ + int ret; + uint8_t inquiry_buf[36]; + int peripheral_device_type; + int all_nul_bytes; + int n; + int is_packet_device = 0; + + /* init results */ + memzero(out_identify, 512); + + /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device + * we could accidentally blank media. This is because MMC's BLANK + * command has the same op-code (0x61). + * + * To prevent this from happening we bail out if the device + * isn't a Direct Access Block Device, e.g. SCSI type 0x00 + * (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY + * command first... libata is handling this via its SCSI + * emulation layer. + * + * This also ensures that we're actually dealing with a device + * that understands SCSI commands. + * + * (Yes, it is a bit perverse that we're tunneling the ATA + * command through SCSI and relying on the ATA driver + * emulating SCSI well-enough...) + * + * (See commit 160b069c25690bfb0c785994c7c3710289179107 for + * the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635 + * for the original bug-report.) + */ + ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf)); + if (ret != 0) + goto out; + + /* SPC-4, section 6.4.2: Standard INQUIRY data */ + peripheral_device_type = inquiry_buf[0] & 0x1f; + if (peripheral_device_type == 0x05) + { + is_packet_device = 1; + ret = disk_identify_packet_device_command(fd, out_identify, 512); + goto check_nul_bytes; + } + if (peripheral_device_type != 0x00) { + ret = -1; + errno = EIO; + goto out; + } + + /* OK, now issue the IDENTIFY DEVICE command */ + ret = disk_identify_command(fd, out_identify, 512); + if (ret != 0) + goto out; + + check_nul_bytes: + /* Check if IDENTIFY data is all NUL bytes - if so, bail */ + all_nul_bytes = 1; + for (n = 0; n < 512; n++) { + if (out_identify[n] != '\0') { + all_nul_bytes = 0; + break; + } + } + + if (all_nul_bytes) { + ret = -1; + errno = EIO; + goto out; + } + +out: + if (out_is_packet_device != NULL) + *out_is_packet_device = is_packet_device; + return ret; +} + +int main(int argc, char *argv[]) +{ + _cleanup_udev_unref_ struct udev *udev = NULL; + struct hd_driveid id; + union { + uint8_t byte[512]; + uint16_t wyde[256]; + } identify; + char model[41]; + char model_enc[256]; + char serial[21]; + char revision[9]; + const char *node = NULL; + int export = 0; + _cleanup_close_ int fd = -1; + uint16_t word; + int is_packet_device = 0; + static const struct option options[] = { + { "export", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + log_parse_environment(); + log_open(); + + udev = udev_new(); + if (udev == NULL) + return 0; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "xh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'x': + export = 1; + break; + case 'h': + printf("Usage: ata_id [--export] [--help] \n" + " -x,--export print values as environment keys\n" + " -h,--help print this help text\n\n"); + return 0; + } + } + + node = argv[optind]; + if (node == NULL) { + log_error("no node specified"); + return 1; + } + + fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC); + if (fd < 0) { + log_error("unable to open '%s'", node); + return 1; + } + + if (disk_identify(udev, fd, identify.byte, &is_packet_device) == 0) { + /* + * fix up only the fields from the IDENTIFY data that we are going to + * use and copy it into the hd_driveid struct for convenience + */ + disk_identify_fixup_string(identify.byte, 10, 20); /* serial */ + disk_identify_fixup_string(identify.byte, 23, 8); /* fwrev */ + disk_identify_fixup_string(identify.byte, 27, 40); /* model */ + disk_identify_fixup_uint16(identify.byte, 0); /* configuration */ + disk_identify_fixup_uint16(identify.byte, 75); /* queue depth */ + disk_identify_fixup_uint16(identify.byte, 76); /* SATA capabilities */ + disk_identify_fixup_uint16(identify.byte, 82); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 83); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 84); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 85); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 86); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 87); /* command set supported */ + disk_identify_fixup_uint16(identify.byte, 89); /* time required for SECURITY ERASE UNIT */ + disk_identify_fixup_uint16(identify.byte, 90); /* time required for enhanced SECURITY ERASE UNIT */ + disk_identify_fixup_uint16(identify.byte, 91); /* current APM values */ + disk_identify_fixup_uint16(identify.byte, 94); /* current AAM value */ + disk_identify_fixup_uint16(identify.byte, 108); /* WWN */ + disk_identify_fixup_uint16(identify.byte, 109); /* WWN */ + disk_identify_fixup_uint16(identify.byte, 110); /* WWN */ + disk_identify_fixup_uint16(identify.byte, 111); /* WWN */ + disk_identify_fixup_uint16(identify.byte, 128); /* device lock function */ + disk_identify_fixup_uint16(identify.byte, 217); /* nominal media rotation rate */ + memcpy(&id, identify.byte, sizeof id); + } else { + /* If this fails, then try HDIO_GET_IDENTITY */ + if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { + log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node); + return 2; + } + } + + memcpy(model, id.model, 40); + model[40] = '\0'; + udev_util_encode_string(model, model_enc, sizeof(model_enc)); + util_replace_whitespace((char *) id.model, model, 40); + util_replace_chars(model, NULL); + util_replace_whitespace((char *) id.serial_no, serial, 20); + util_replace_chars(serial, NULL); + util_replace_whitespace((char *) id.fw_rev, revision, 8); + util_replace_chars(revision, NULL); + + if (export) { + /* Set this to convey the disk speaks the ATA protocol */ + printf("ID_ATA=1\n"); + + if ((id.config >> 8) & 0x80) { + /* This is an ATAPI device */ + switch ((id.config >> 8) & 0x1f) { + case 0: + printf("ID_TYPE=cd\n"); + break; + case 1: + printf("ID_TYPE=tape\n"); + break; + case 5: + printf("ID_TYPE=cd\n"); + break; + case 7: + printf("ID_TYPE=optical\n"); + break; + default: + printf("ID_TYPE=generic\n"); + break; + } + } else { + printf("ID_TYPE=disk\n"); + } + printf("ID_BUS=ata\n"); + printf("ID_MODEL=%s\n", model); + printf("ID_MODEL_ENC=%s\n", model_enc); + printf("ID_REVISION=%s\n", revision); + if (serial[0] != '\0') { + printf("ID_SERIAL=%s_%s\n", model, serial); + printf("ID_SERIAL_SHORT=%s\n", serial); + } else { + printf("ID_SERIAL=%s\n", model); + } + + if (id.command_set_1 & (1<<5)) { + printf("ID_ATA_WRITE_CACHE=1\n"); + printf("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0); + } + if (id.command_set_1 & (1<<10)) { + printf("ID_ATA_FEATURE_SET_HPA=1\n"); + printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0); + + /* + * TODO: use the READ NATIVE MAX ADDRESS command to get the native max address + * so it is easy to check whether the protected area is in use. + */ + } + if (id.command_set_1 & (1<<3)) { + printf("ID_ATA_FEATURE_SET_PM=1\n"); + printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0); + } + if (id.command_set_1 & (1<<1)) { + printf("ID_ATA_FEATURE_SET_SECURITY=1\n"); + printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0); + printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2); + if ((id.cfs_enable_1 & (1<<1))) /* enabled */ { + if (id.dlf & (1<<8)) + printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n"); + else + printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n"); + } + if (id.dlf & (1<<5)) + printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2); + if (id.dlf & (1<<4)) + printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n"); + if (id.dlf & (1<<3)) + printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n"); + if (id.dlf & (1<<2)) + printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n"); + } + if (id.command_set_1 & (1<<0)) { + printf("ID_ATA_FEATURE_SET_SMART=1\n"); + printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0); + } + if (id.command_set_2 & (1<<9)) { + printf("ID_ATA_FEATURE_SET_AAM=1\n"); + printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0); + printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8); + printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff); + } + if (id.command_set_2 & (1<<5)) { + printf("ID_ATA_FEATURE_SET_PUIS=1\n"); + printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0); + } + if (id.command_set_2 & (1<<3)) { + printf("ID_ATA_FEATURE_SET_APM=1\n"); + printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0); + if ((id.cfs_enable_2 & (1<<3))) + printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff); + } + if (id.command_set_2 & (1<<0)) + printf("ID_ATA_DOWNLOAD_MICROCODE=1\n"); + + /* + * Word 76 indicates the capabilities of a SATA device. A PATA device shall set + * word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then + * the device does not claim compliance with the Serial ATA specification and words + * 76 through 79 are not valid and shall be ignored. + */ + + word = identify.wyde[76]; + if (word != 0x0000 && word != 0xffff) { + printf("ID_ATA_SATA=1\n"); + /* + * If bit 2 of word 76 is set to one, then the device supports the Gen2 + * signaling rate of 3.0 Gb/s (see SATA 2.6). + * + * If bit 1 of word 76 is set to one, then the device supports the Gen1 + * signaling rate of 1.5 Gb/s (see SATA 2.6). + */ + if (word & (1<<2)) + printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n"); + if (word & (1<<1)) + printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n"); + } + + /* Word 217 indicates the nominal media rotation rate of the device */ + word = identify.wyde[217]; + if (word == 0x0001) + printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */ + else if (word >= 0x0401 && word <= 0xfffe) + printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word); + + /* + * Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier + * format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE. + * All other values are reserved. + */ + word = identify.wyde[108]; + if ((word & 0xf000) == 0x5000) { + uint64_t wwwn; + + wwwn = identify.wyde[108]; + wwwn <<= 16; + wwwn |= identify.wyde[109]; + wwwn <<= 16; + wwwn |= identify.wyde[110]; + wwwn <<= 16; + wwwn |= identify.wyde[111]; + printf("ID_WWN=0x%1$" PRIx64 "\n" + "ID_WWN_WITH_EXTENSION=0x%1$" PRIx64 "\n", + wwwn); + } + + /* from Linux's include/linux/ata.h */ + if (identify.wyde[0] == 0x848a || + identify.wyde[0] == 0x844a || + (identify.wyde[83] & 0xc004) == 0x4004) + printf("ID_ATA_CFA=1\n"); + } else { + if (serial[0] != '\0') + printf("%s_%s\n", model, serial); + else + printf("%s\n", model); + } + + return 0; +} diff --git a/src/grp-udev/cdrom_id/60-cdrom_id.rules b/src/grp-udev/cdrom_id/60-cdrom_id.rules new file mode 100644 index 0000000000..5c3b52ebb9 --- /dev/null +++ b/src/grp-udev/cdrom_id/60-cdrom_id.rules @@ -0,0 +1,25 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="cdrom_end" +SUBSYSTEM!="block", GOTO="cdrom_end" +KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" +ENV{DEVTYPE}!="disk", GOTO="cdrom_end" + +# unconditionally tag device as CDROM +KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" + +# media eject button pressed +ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end" + +# import device and media properties and lock tray to +# enable the receiving of media eject button events +IMPORT{program}="cdrom_id --lock-media $devnode" + +# ejecting a CD does not remove the device node, so mark the systemd device +# unit as inactive while there is no medium; this automatically cleans up of +# stale mounts after ejecting +ENV{DISK_MEDIA_CHANGE}=="?*", ENV{ID_CDROM_MEDIA}!="?*", ENV{SYSTEMD_READY}="0" + +KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100" + +LABEL="cdrom_end" diff --git a/src/grp-udev/cdrom_id/Makefile b/src/grp-udev/cdrom_id/Makefile new file mode 100644 index 0000000000..a9297413d3 --- /dev/null +++ b/src/grp-udev/cdrom_id/Makefile @@ -0,0 +1,38 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +cdrom_id_SOURCES = \ + src/udev/cdrom_id/cdrom_id.c + +cdrom_id_LDADD = \ + libshared.la + +udevlibexec_PROGRAMS += \ + cdrom_id + +dist_udevrules_DATA += \ + rules/60-cdrom_id.rules + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/cdrom_id/cdrom_id.c b/src/grp-udev/cdrom_id/cdrom_id.c new file mode 100644 index 0000000000..9a5af6d614 --- /dev/null +++ b/src/grp-udev/cdrom_id/cdrom_id.c @@ -0,0 +1,1086 @@ +/* + * cdrom_id - optical drive and media information prober + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/random-util.h" +#include "libudev-private.h" + +/* device info */ +static unsigned int cd_cd_rom; +static unsigned int cd_cd_r; +static unsigned int cd_cd_rw; +static unsigned int cd_dvd_rom; +static unsigned int cd_dvd_r; +static unsigned int cd_dvd_rw; +static unsigned int cd_dvd_ram; +static unsigned int cd_dvd_plus_r; +static unsigned int cd_dvd_plus_rw; +static unsigned int cd_dvd_plus_r_dl; +static unsigned int cd_dvd_plus_rw_dl; +static unsigned int cd_bd; +static unsigned int cd_bd_r; +static unsigned int cd_bd_re; +static unsigned int cd_hddvd; +static unsigned int cd_hddvd_r; +static unsigned int cd_hddvd_rw; +static unsigned int cd_mo; +static unsigned int cd_mrw; +static unsigned int cd_mrw_w; + +/* media info */ +static unsigned int cd_media; +static unsigned int cd_media_cd_rom; +static unsigned int cd_media_cd_r; +static unsigned int cd_media_cd_rw; +static unsigned int cd_media_dvd_rom; +static unsigned int cd_media_dvd_r; +static unsigned int cd_media_dvd_rw; +static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */ +static unsigned int cd_media_dvd_rw_seq; /* sequential mode */ +static unsigned int cd_media_dvd_ram; +static unsigned int cd_media_dvd_plus_r; +static unsigned int cd_media_dvd_plus_rw; +static unsigned int cd_media_dvd_plus_r_dl; +static unsigned int cd_media_dvd_plus_rw_dl; +static unsigned int cd_media_bd; +static unsigned int cd_media_bd_r; +static unsigned int cd_media_bd_re; +static unsigned int cd_media_hddvd; +static unsigned int cd_media_hddvd_r; +static unsigned int cd_media_hddvd_rw; +static unsigned int cd_media_mo; +static unsigned int cd_media_mrw; +static unsigned int cd_media_mrw_w; + +static const char *cd_media_state = NULL; +static unsigned int cd_media_session_next; +static unsigned int cd_media_session_count; +static unsigned int cd_media_track_count; +static unsigned int cd_media_track_count_data; +static unsigned int cd_media_track_count_audio; +static unsigned long long int cd_media_session_last_offset; + +#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) +#define SK(errcode) (((errcode) >> 16) & 0xF) +#define ASC(errcode) (((errcode) >> 8) & 0xFF) +#define ASCQ(errcode) ((errcode) & 0xFF) + +static bool is_mounted(const char *device) +{ + struct stat statbuf; + FILE *fp; + int maj, min; + bool mounted = false; + + if (stat(device, &statbuf) < 0) + return false; + + fp = fopen("/proc/self/mountinfo", "re"); + if (fp == NULL) + return false; + while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { + if (makedev(maj, min) == statbuf.st_rdev) { + mounted = true; + break; + } + } + fclose(fp); + return mounted; +} + +static void info_scsi_cmd_err(struct udev *udev, const char *cmd, int err) +{ + if (err == -1) { + log_debug("%s failed", cmd); + return; + } + log_debug("%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh", cmd, SK(err), ASC(err), ASCQ(err)); +} + +struct scsi_cmd { + struct cdrom_generic_command cgc; + union { + struct request_sense s; + unsigned char u[18]; + } _sense; + struct sg_io_hdr sg_io; +}; + +static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) +{ + memzero(cmd, sizeof(struct scsi_cmd)); + cmd->cgc.quiet = 1; + cmd->cgc.sense = &cmd->_sense.s; + cmd->sg_io.interface_id = 'S'; + cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); + cmd->sg_io.cmdp = cmd->cgc.cmd; + cmd->sg_io.sbp = cmd->_sense.u; + cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; +} + +static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg) +{ + cmd->sg_io.cmd_len = i + 1; + cmd->cgc.cmd[i] = arg; +} + +#define CHECK_CONDITION 0x01 + +static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) +{ + int ret = 0; + + if (bufsize > 0) { + cmd->sg_io.dxferp = buf; + cmd->sg_io.dxfer_len = bufsize; + cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; + } else { + cmd->sg_io.dxfer_direction = SG_DXFER_NONE; + } + if (ioctl(fd, SG_IO, &cmd->sg_io)) + return -1; + + if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { + errno = EIO; + ret = -1; + if (cmd->sg_io.masked_status & CHECK_CONDITION) { + ret = ERRCODE(cmd->_sense.u); + if (ret == 0) + ret = -1; + } + } + return ret; +} + +static int media_lock(struct udev *udev, int fd, bool lock) +{ + int err; + + /* disable the kernel's lock logic */ + err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); + if (err < 0) + log_debug("CDROM_CLEAR_OPTIONS, CDO_LOCK failed"); + + err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); + if (err < 0) + log_debug("CDROM_LOCKDOOR failed"); + + return err; +} + +static int media_eject(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x1b); + scsi_cmd_set(udev, &sc, 4, 0x02); + scsi_cmd_set(udev, &sc, 5, 0); + err = scsi_cmd_run(udev, &sc, fd, NULL, 0); + if ((err != 0)) { + info_scsi_cmd_err(udev, "START_STOP_UNIT", err); + return -1; + } + return 0; +} + +static int cd_capability_compat(struct udev *udev, int fd) +{ + int capability; + + capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); + if (capability < 0) { + log_debug("CDROM_GET_CAPABILITY failed"); + return -1; + } + + if (capability & CDC_CD_R) + cd_cd_r = 1; + if (capability & CDC_CD_RW) + cd_cd_rw = 1; + if (capability & CDC_DVD) + cd_dvd_rom = 1; + if (capability & CDC_DVD_R) + cd_dvd_r = 1; + if (capability & CDC_DVD_RAM) + cd_dvd_ram = 1; + if (capability & CDC_MRW) + cd_mrw = 1; + if (capability & CDC_MRW_W) + cd_mrw_w = 1; + return 0; +} + +static int cd_media_compat(struct udev *udev, int fd) +{ + if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { + log_debug("CDROM_DRIVE_STATUS != CDS_DISC_OK"); + return -1; + } + cd_media = 1; + return 0; +} + +static int cd_inquiry(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char inq[128]; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x12); + scsi_cmd_set(udev, &sc, 4, 36); + scsi_cmd_set(udev, &sc, 5, 0); + err = scsi_cmd_run(udev, &sc, fd, inq, 36); + if ((err != 0)) { + info_scsi_cmd_err(udev, "INQUIRY", err); + return -1; + } + + if ((inq[0] & 0x1F) != 5) { + log_debug("not an MMC unit"); + return -1; + } + + log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32); + return 0; +} + +static void feature_profile_media(struct udev *udev, int cur_profile) +{ + switch (cur_profile) { + case 0x03: + case 0x04: + case 0x05: + log_debug("profile 0x%02x ", cur_profile); + cd_media = 1; + cd_media_mo = 1; + break; + case 0x08: + log_debug("profile 0x%02x media_cd_rom", cur_profile); + cd_media = 1; + cd_media_cd_rom = 1; + break; + case 0x09: + log_debug("profile 0x%02x media_cd_r", cur_profile); + cd_media = 1; + cd_media_cd_r = 1; + break; + case 0x0a: + log_debug("profile 0x%02x media_cd_rw", cur_profile); + cd_media = 1; + cd_media_cd_rw = 1; + break; + case 0x10: + log_debug("profile 0x%02x media_dvd_ro", cur_profile); + cd_media = 1; + cd_media_dvd_rom = 1; + break; + case 0x11: + log_debug("profile 0x%02x media_dvd_r", cur_profile); + cd_media = 1; + cd_media_dvd_r = 1; + break; + case 0x12: + log_debug("profile 0x%02x media_dvd_ram", cur_profile); + cd_media = 1; + cd_media_dvd_ram = 1; + break; + case 0x13: + log_debug("profile 0x%02x media_dvd_rw_ro", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_ro = 1; + break; + case 0x14: + log_debug("profile 0x%02x media_dvd_rw_seq", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_seq = 1; + break; + case 0x1B: + log_debug("profile 0x%02x media_dvd_plus_r", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r = 1; + break; + case 0x1A: + log_debug("profile 0x%02x media_dvd_plus_rw", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw = 1; + break; + case 0x2A: + log_debug("profile 0x%02x media_dvd_plus_rw_dl", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw_dl = 1; + break; + case 0x2B: + log_debug("profile 0x%02x media_dvd_plus_r_dl", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r_dl = 1; + break; + case 0x40: + log_debug("profile 0x%02x media_bd", cur_profile); + cd_media = 1; + cd_media_bd = 1; + break; + case 0x41: + case 0x42: + log_debug("profile 0x%02x media_bd_r", cur_profile); + cd_media = 1; + cd_media_bd_r = 1; + break; + case 0x43: + log_debug("profile 0x%02x media_bd_re", cur_profile); + cd_media = 1; + cd_media_bd_re = 1; + break; + case 0x50: + log_debug("profile 0x%02x media_hddvd", cur_profile); + cd_media = 1; + cd_media_hddvd = 1; + break; + case 0x51: + log_debug("profile 0x%02x media_hddvd_r", cur_profile); + cd_media = 1; + cd_media_hddvd_r = 1; + break; + case 0x52: + log_debug("profile 0x%02x media_hddvd_rw", cur_profile); + cd_media = 1; + cd_media_hddvd_rw = 1; + break; + default: + log_debug("profile 0x%02x ", cur_profile); + break; + } +} + +static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) +{ + unsigned int i; + + for (i = 0; i+4 <= size; i += 4) { + int profile; + + profile = profiles[i] << 8 | profiles[i+1]; + switch (profile) { + case 0x03: + case 0x04: + case 0x05: + log_debug("profile 0x%02x mo", profile); + cd_mo = 1; + break; + case 0x08: + log_debug("profile 0x%02x cd_rom", profile); + cd_cd_rom = 1; + break; + case 0x09: + log_debug("profile 0x%02x cd_r", profile); + cd_cd_r = 1; + break; + case 0x0A: + log_debug("profile 0x%02x cd_rw", profile); + cd_cd_rw = 1; + break; + case 0x10: + log_debug("profile 0x%02x dvd_rom", profile); + cd_dvd_rom = 1; + break; + case 0x12: + log_debug("profile 0x%02x dvd_ram", profile); + cd_dvd_ram = 1; + break; + case 0x13: + case 0x14: + log_debug("profile 0x%02x dvd_rw", profile); + cd_dvd_rw = 1; + break; + case 0x1B: + log_debug("profile 0x%02x dvd_plus_r", profile); + cd_dvd_plus_r = 1; + break; + case 0x1A: + log_debug("profile 0x%02x dvd_plus_rw", profile); + cd_dvd_plus_rw = 1; + break; + case 0x2A: + log_debug("profile 0x%02x dvd_plus_rw_dl", profile); + cd_dvd_plus_rw_dl = 1; + break; + case 0x2B: + log_debug("profile 0x%02x dvd_plus_r_dl", profile); + cd_dvd_plus_r_dl = 1; + break; + case 0x40: + cd_bd = 1; + log_debug("profile 0x%02x bd", profile); + break; + case 0x41: + case 0x42: + cd_bd_r = 1; + log_debug("profile 0x%02x bd_r", profile); + break; + case 0x43: + cd_bd_re = 1; + log_debug("profile 0x%02x bd_re", profile); + break; + case 0x50: + cd_hddvd = 1; + log_debug("profile 0x%02x hddvd", profile); + break; + case 0x51: + cd_hddvd_r = 1; + log_debug("profile 0x%02x hddvd_r", profile); + break; + case 0x52: + cd_hddvd_rw = 1; + log_debug("profile 0x%02x hddvd_rw", profile); + break; + default: + log_debug("profile 0x%02x ", profile); + break; + } + } + return 0; +} + +/* returns 0 if media was detected */ +static int cd_profiles_old_mmc(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + int err; + + unsigned char header[32]; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x51); + scsi_cmd_set(udev, &sc, 8, sizeof(header)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); + if (cd_media == 1) { + log_debug("no current profile, but disc is present; assuming CD-ROM"); + cd_media_cd_rom = 1; + cd_media_track_count = 1; + cd_media_track_count_data = 1; + return 0; + } else { + log_debug("no current profile, assuming no media"); + return -1; + } + }; + + cd_media = 1; + + if (header[2] & 16) { + cd_media_cd_rw = 1; + log_debug("profile 0x0a media_cd_rw"); + } else if ((header[2] & 3) < 2 && cd_cd_r) { + cd_media_cd_r = 1; + log_debug("profile 0x09 media_cd_r"); + } else { + cd_media_cd_rom = 1; + log_debug("profile 0x08 media_cd_rom"); + } + return 0; +} + +/* returns 0 if media was detected */ +static int cd_profiles(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char features[65530]; + unsigned int cur_profile = 0; + unsigned int len; + unsigned int i; + int err; + int ret; + + ret = -1; + + /* First query the current profile */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 8, 8); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, 8); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); + /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ + if (SK(err) == 0x5 && (ASC(err) == 0x20 || ASC(err) == 0x24)) { + log_debug("drive is pre-MMC2 and does not support 46h get configuration command"); + log_debug("trying to work around the problem"); + ret = cd_profiles_old_mmc(udev, fd); + } + goto out; + } + + cur_profile = features[6] << 8 | features[7]; + if (cur_profile > 0) { + log_debug("current profile 0x%02x", cur_profile); + feature_profile_media (udev, cur_profile); + ret = 0; /* we have media */ + } else { + log_debug("no current profile, assuming no media"); + } + + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); + + if (len > sizeof(features)) { + log_debug("can not get features in a single query, truncating"); + len = sizeof(features); + } else if (len <= 8) + len = sizeof(features); + + /* Now get the full feature buffer */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); + return -1; + } + + /* parse the length once more, in case the drive decided to have other features suddenly :) */ + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); + + if (len > sizeof(features)) { + log_debug("can not get features in a single query, truncating"); + len = sizeof(features); + } + + /* device features */ + for (i = 8; i+4 < len; i += (4 + features[i+3])) { + unsigned int feature; + + feature = features[i] << 8 | features[i+1]; + + switch (feature) { + case 0x00: + log_debug("GET CONFIGURATION: feature 'profiles', with %i entries", features[i+3] / 4); + feature_profiles(udev, &features[i]+4, MIN(features[i+3], len - i - 4)); + break; + default: + log_debug("GET CONFIGURATION: feature 0x%04x , with 0x%02x bytes", feature, features[i+3]); + break; + } + } +out: + return ret; +} + +static int cd_media_info(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char header[32]; + static const char *media_status[] = { + "blank", + "appendable", + "complete", + "other" + }; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x51); + scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); + return -1; + }; + + cd_media = 1; + log_debug("disk type %02x", header[8]); + log_debug("hardware reported media status: %s", media_status[header[2] & 3]); + + /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ + if (!cd_media_cd_rom) + cd_media_state = media_status[header[2] & 3]; + + /* fresh DVD-RW in restricted overwite mode reports itself as + * "appendable"; change it to "blank" to make it consistent with what + * gets reported after blanking, and what userspace expects */ + if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) + cd_media_state = media_status[0]; + + /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are + * always "complete", DVD-RAM are "other" or "complete" if the disc is + * write protected; we need to check the contents if it is blank */ + if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { + unsigned char buffer[32 * 2048]; + unsigned char len; + int offset; + + if (cd_media_dvd_ram) { + /* a write protected dvd-ram may report "complete" status */ + + unsigned char dvdstruct[8]; + unsigned char format[12]; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0xAD); + scsi_cmd_set(udev, &sc, 7, 0xC0); + scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); + scsi_cmd_set(udev, &sc, 11, 0); + err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err); + return -1; + } + if (dvdstruct[4] & 0x02) { + cd_media_state = media_status[2]; + log_debug("write-protected DVD-RAM media inserted"); + goto determined; + } + + /* let's make sure we don't try to read unformatted media */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x23); + scsi_cmd_set(udev, &sc, 8, sizeof(format)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err); + return -1; + } + + len = format[3]; + if (len & 7 || len < 16) { + log_debug("invalid format capacities length"); + return -1; + } + + switch(format[8] & 3) { + case 1: + log_debug("unformatted DVD-RAM media inserted"); + /* This means that last format was interrupted + * or failed, blank dvd-ram discs are factory + * formatted. Take no action here as it takes + * quite a while to reformat a dvd-ram and it's + * not automatically started */ + goto determined; + + case 2: + log_debug("formatted DVD-RAM media inserted"); + break; + + case 3: + cd_media = 0; //return no media + log_debug("format capacities returned no media"); + return -1; + } + } + + /* Take a closer look at formatted media (unformatted DVD+RW + * has "blank" status", DVD-RAM was examined earlier) and check + * for ISO and UDF PVDs or a fs superblock presence and do it + * in one ioctl (we need just sectors 0 and 16) */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x28); + scsi_cmd_set(udev, &sc, 5, 0); + scsi_cmd_set(udev, &sc, 8, 32); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); + if ((err != 0)) { + cd_media = 0; + info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); + return -1; + } + + /* if any non-zero data is found in sector 16 (iso and udf) or + * eventually 0 (fat32 boot sector, ext2 superblock, etc), disc + * is assumed non-blank */ + + for (offset = 32768; offset < (32768 + 2048); offset++) { + if (buffer [offset]) { + log_debug("data in block 16, assuming complete"); + goto determined; + } + } + + for (offset = 0; offset < 2048; offset++) { + if (buffer [offset]) { + log_debug("data in block 0, assuming complete"); + goto determined; + } + } + + cd_media_state = media_status[0]; + log_debug("no data in blocks 0 or 16, assuming blank"); + } + +determined: + /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in + * restricted overwrite mode can never append, only in sequential mode */ + if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) + cd_media_session_next = header[10] << 8 | header[5]; + cd_media_session_count = header[9] << 8 | header[4]; + cd_media_track_count = header[11] << 8 | header[6]; + + return 0; +} + +static int cd_media_toc(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char header[12]; + unsigned char toc[65536]; + unsigned int len, i, num_tracks; + unsigned char *p; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 6, 1); + scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC", err); + return -1; + } + + len = (header[0] << 8 | header[1]) + 2; + log_debug("READ TOC: len: %d, start track: %d, end track: %d", len, header[2], header[3]); + if (len > sizeof(toc)) + return -1; + if (len < 2) + return -1; + /* 2: first track, 3: last track */ + num_tracks = header[3] - header[2] + 1; + + /* empty media has no tracks */ + if (len < 8) + return 0; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ + scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, toc, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC (tracks)", err); + return -1; + } + + /* Take care to not iterate beyond the last valid track as specified in + * the TOC, but also avoid going beyond the TOC length, just in case + * the last track number is invalidly large */ + for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { + unsigned int block; + unsigned int is_data_track; + + is_data_track = (p[1] & 0x04) != 0; + + block = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + log_debug("track=%u info=0x%x(%s) start_block=%u", + p[2], p[1] & 0x0f, is_data_track ? "data":"audio", block); + + if (is_data_track) + cd_media_track_count_data++; + else + cd_media_track_count_audio++; + } + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ + scsi_cmd_set(udev, &sc, 8, sizeof(header)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC (multi session)", err); + return -1; + } + len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7]; + log_debug("last track %u starts at block %u", header[4+2], len); + cd_media_session_last_offset = (unsigned long long int)len * 2048; + return 0; +} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + static const struct option options[] = { + { "lock-media", no_argument, NULL, 'l' }, + { "unlock-media", no_argument, NULL, 'u' }, + { "eject-media", no_argument, NULL, 'e' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + bool eject = false; + bool lock = false; + bool unlock = false; + const char *node = NULL; + int fd = -1; + int cnt; + int rc = 0; + + log_parse_environment(); + log_open(); + + udev = udev_new(); + if (udev == NULL) + goto exit; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "deluh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'l': + lock = true; + break; + case 'u': + unlock = true; + break; + case 'e': + eject = true; + break; + case 'd': + log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_DEBUG); + log_open(); + break; + case 'h': + printf("Usage: cdrom_id [options] \n" + " -l,--lock-media lock the media (to enable eject request events)\n" + " -u,--unlock-media unlock the media\n" + " -e,--eject-media eject the media\n" + " -d,--debug debug to stderr\n" + " -h,--help print this help text\n\n"); + goto exit; + default: + rc = 1; + goto exit; + } + } + + node = argv[optind]; + if (!node) { + log_error("no device"); + fprintf(stderr, "no device\n"); + rc = 1; + goto exit; + } + + initialize_srand(); + for (cnt = 20; cnt > 0; cnt--) { + struct timespec duration; + + fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|(is_mounted(node) ? 0 : O_EXCL)); + if (fd >= 0 || errno != EBUSY) + break; + duration.tv_sec = 0; + duration.tv_nsec = (100 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); + nanosleep(&duration, NULL); + } + if (fd < 0) { + log_debug("unable to open '%s'", node); + fprintf(stderr, "unable to open '%s'\n", node); + rc = 1; + goto exit; + } + log_debug("probing: '%s'", node); + + /* same data as original cdrom_id */ + if (cd_capability_compat(udev, fd) < 0) { + rc = 1; + goto exit; + } + + /* check for media - don't bail if there's no media as we still need to + * to read profiles */ + cd_media_compat(udev, fd); + + /* check if drive talks MMC */ + if (cd_inquiry(udev, fd) < 0) + goto work; + + /* read drive and possibly current profile */ + if (cd_profiles(udev, fd) != 0) + goto work; + + /* at this point we are guaranteed to have media in the drive - find out more about it */ + + /* get session/track info */ + cd_media_toc(udev, fd); + + /* get writable media state */ + cd_media_info(udev, fd); + +work: + /* lock the media, so we enable eject button events */ + if (lock && cd_media) { + log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (lock)"); + media_lock(udev, fd, true); + } + + if (unlock && cd_media) { + log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); + media_lock(udev, fd, false); + } + + if (eject) { + log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); + media_lock(udev, fd, false); + log_debug("START_STOP_UNIT (eject)"); + media_eject(udev, fd); + } + + printf("ID_CDROM=1\n"); + if (cd_cd_rom) + printf("ID_CDROM_CD=1\n"); + if (cd_cd_r) + printf("ID_CDROM_CD_R=1\n"); + if (cd_cd_rw) + printf("ID_CDROM_CD_RW=1\n"); + if (cd_dvd_rom) + printf("ID_CDROM_DVD=1\n"); + if (cd_dvd_r) + printf("ID_CDROM_DVD_R=1\n"); + if (cd_dvd_rw) + printf("ID_CDROM_DVD_RW=1\n"); + if (cd_dvd_ram) + printf("ID_CDROM_DVD_RAM=1\n"); + if (cd_dvd_plus_r) + printf("ID_CDROM_DVD_PLUS_R=1\n"); + if (cd_dvd_plus_rw) + printf("ID_CDROM_DVD_PLUS_RW=1\n"); + if (cd_dvd_plus_r_dl) + printf("ID_CDROM_DVD_PLUS_R_DL=1\n"); + if (cd_dvd_plus_rw_dl) + printf("ID_CDROM_DVD_PLUS_RW_DL=1\n"); + if (cd_bd) + printf("ID_CDROM_BD=1\n"); + if (cd_bd_r) + printf("ID_CDROM_BD_R=1\n"); + if (cd_bd_re) + printf("ID_CDROM_BD_RE=1\n"); + if (cd_hddvd) + printf("ID_CDROM_HDDVD=1\n"); + if (cd_hddvd_r) + printf("ID_CDROM_HDDVD_R=1\n"); + if (cd_hddvd_rw) + printf("ID_CDROM_HDDVD_RW=1\n"); + if (cd_mo) + printf("ID_CDROM_MO=1\n"); + if (cd_mrw) + printf("ID_CDROM_MRW=1\n"); + if (cd_mrw_w) + printf("ID_CDROM_MRW_W=1\n"); + + if (cd_media) + printf("ID_CDROM_MEDIA=1\n"); + if (cd_media_mo) + printf("ID_CDROM_MEDIA_MO=1\n"); + if (cd_media_mrw) + printf("ID_CDROM_MEDIA_MRW=1\n"); + if (cd_media_mrw_w) + printf("ID_CDROM_MEDIA_MRW_W=1\n"); + if (cd_media_cd_rom) + printf("ID_CDROM_MEDIA_CD=1\n"); + if (cd_media_cd_r) + printf("ID_CDROM_MEDIA_CD_R=1\n"); + if (cd_media_cd_rw) + printf("ID_CDROM_MEDIA_CD_RW=1\n"); + if (cd_media_dvd_rom) + printf("ID_CDROM_MEDIA_DVD=1\n"); + if (cd_media_dvd_r) + printf("ID_CDROM_MEDIA_DVD_R=1\n"); + if (cd_media_dvd_ram) + printf("ID_CDROM_MEDIA_DVD_RAM=1\n"); + if (cd_media_dvd_rw) + printf("ID_CDROM_MEDIA_DVD_RW=1\n"); + if (cd_media_dvd_plus_r) + printf("ID_CDROM_MEDIA_DVD_PLUS_R=1\n"); + if (cd_media_dvd_plus_rw) + printf("ID_CDROM_MEDIA_DVD_PLUS_RW=1\n"); + if (cd_media_dvd_plus_rw_dl) + printf("ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1\n"); + if (cd_media_dvd_plus_r_dl) + printf("ID_CDROM_MEDIA_DVD_PLUS_R_DL=1\n"); + if (cd_media_bd) + printf("ID_CDROM_MEDIA_BD=1\n"); + if (cd_media_bd_r) + printf("ID_CDROM_MEDIA_BD_R=1\n"); + if (cd_media_bd_re) + printf("ID_CDROM_MEDIA_BD_RE=1\n"); + if (cd_media_hddvd) + printf("ID_CDROM_MEDIA_HDDVD=1\n"); + if (cd_media_hddvd_r) + printf("ID_CDROM_MEDIA_HDDVD_R=1\n"); + if (cd_media_hddvd_rw) + printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); + + if (cd_media_state != NULL) + printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); + if (cd_media_session_next > 0) + printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", cd_media_session_next); + if (cd_media_session_count > 0) + printf("ID_CDROM_MEDIA_SESSION_COUNT=%u\n", cd_media_session_count); + if (cd_media_session_count > 1 && cd_media_session_last_offset > 0) + printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu\n", cd_media_session_last_offset); + if (cd_media_track_count > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT=%u\n", cd_media_track_count); + if (cd_media_track_count_audio > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%u\n", cd_media_track_count_audio); + if (cd_media_track_count_data > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%u\n", cd_media_track_count_data); +exit: + if (fd >= 0) + close(fd); + udev_unref(udev); + log_close(); + return rc; +} diff --git a/src/grp-udev/collect/Makefile b/src/grp-udev/collect/Makefile new file mode 100644 index 0000000000..60af3b7627 --- /dev/null +++ b/src/grp-udev/collect/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +collect_SOURCES = \ + src/udev/collect/collect.c + +collect_LDADD = \ + libshared.la + +udevlibexec_PROGRAMS += \ + collect + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/collect/collect.c b/src/grp-udev/collect/collect.c new file mode 100644 index 0000000000..0e8f6b97fe --- /dev/null +++ b/src/grp-udev/collect/collect.c @@ -0,0 +1,491 @@ +/* + * Collect variables across events. + * + * usage: collect [--add|--remove] + * + * Adds ID to the list governed by . + * must be part of the ID list . + * If all IDs given by are listed (ie collect has been + * invoked for each ID in ) collect returns 0, the + * number of missing IDs otherwise. + * A negative number is returned on error. + * + * Copyright(C) 2007, Hannes Reinecke + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "libudev-private.h" + +#define BUFSIZE 16 +#define UDEV_ALARM_TIMEOUT 180 + +enum collect_state { + STATE_NONE, + STATE_OLD, + STATE_CONFIRMED, +}; + +struct _mate { + struct udev_list_node node; + char *name; + enum collect_state state; +}; + +static struct udev_list_node bunch; +static int debug; + +/* This can increase dynamically */ +static size_t bufsize = BUFSIZE; + +static inline struct _mate *node_to_mate(struct udev_list_node *node) +{ + return container_of(node, struct _mate, node); +} + +noreturn static void sig_alrm(int signo) +{ + exit(4); +} + +static void usage(void) +{ + printf("%s [options] \n\n" + "Collect variables across events.\n\n" + " -h --help Print this message\n" + " -a --add Add ID to the list \n" + " -r --remove Remove ID from the list \n" + " -d --debug Debug to stderr\n\n" + " Adds ID to the list governed by .\n" + " must be part of the list .\n" + " If all IDs given by are listed (ie collect has been\n" + " invoked for each ID in ) collect returns 0, the\n" + " number of missing IDs otherwise.\n" + " On error a negative number is returned.\n\n" + , program_invocation_short_name); +} + +/* + * prepare + * + * Prepares the database file + */ +static int prepare(char *dir, char *filename) +{ + char buf[512]; + int r, fd; + + r = mkdir(dir, 0700); + if (r < 0 && errno != EEXIST) + return -errno; + + xsprintf(buf, "%s/%s", dir, filename); + + fd = open(buf,O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR); + if (fd < 0) + fprintf(stderr, "Cannot open %s: %m\n", buf); + + if (lockf(fd,F_TLOCK,0) < 0) { + if (debug) + fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT); + if (errno == EAGAIN || errno == EACCES) { + alarm(UDEV_ALARM_TIMEOUT); + lockf(fd, F_LOCK, 0); + if (debug) + fprintf(stderr, "Acquired lock on %s\n", buf); + } else { + if (debug) + fprintf(stderr, "Could not get lock on %s: %m\n", buf); + } + } + + return fd; +} + +/* + * Read checkpoint file + * + * Tricky reading this. We allocate a buffer twice as large + * as we're going to read. Then we read into the upper half + * of that buffer and start parsing. + * Once we do _not_ find end-of-work terminator (whitespace + * character) we move the upper half to the lower half, + * adjust the read pointer and read the next bit. + * Quite clever methinks :-) + * I should become a programmer ... + * + * Yes, one could have used fgets() for this. But then we'd + * have to use freopen etc which I found quite tedious. + */ +static int checkout(int fd) +{ + int len; + char *buf, *ptr, *word = NULL; + struct _mate *him; + + restart: + len = bufsize >> 1; + buf = malloc(bufsize + 1); + if (!buf) + return log_oom(); + memset(buf, ' ', bufsize); + buf[bufsize] = '\0'; + + ptr = buf + len; + while ((read(fd, buf + len, len)) > 0) { + while (ptr && *ptr) { + word = ptr; + ptr = strpbrk(word," \n\t\r"); + if (!ptr && word < (buf + len)) { + bufsize = bufsize << 1; + if (debug) + fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize); + free(buf); + lseek(fd, 0, SEEK_SET); + goto restart; + } + if (ptr) { + *ptr = '\0'; + ptr++; + if (!strlen(word)) + continue; + + if (debug) + fprintf(stderr, "Found word %s\n", word); + him = malloc(sizeof (struct _mate)); + if (!him) { + free(buf); + return log_oom(); + } + him->name = strdup(word); + if (!him->name) { + free(buf); + free(him); + return log_oom(); + } + him->state = STATE_OLD; + udev_list_node_append(&him->node, &bunch); + word = NULL; + } + } + memcpy(buf, buf + len, len); + memset(buf + len, ' ', len); + + if (!ptr) + ptr = word; + if (!ptr) + break; + ptr -= len; + } + + free(buf); + return 0; +} + +/* + * invite + * + * Adds a new ID 'us' to the internal list, + * marks it as confirmed. + */ +static void invite(char *us) +{ + struct udev_list_node *him_node; + struct _mate *who = NULL; + + if (debug) + fprintf(stderr, "Adding ID '%s'\n", us); + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (streq(him->name, us)) { + him->state = STATE_CONFIRMED; + who = him; + } + } + if (debug && !who) + fprintf(stderr, "ID '%s' not in database\n", us); + +} + +/* + * reject + * + * Marks the ID 'us' as invalid, + * causing it to be removed when the + * list is written out. + */ +static void reject(char *us) +{ + struct udev_list_node *him_node; + struct _mate *who = NULL; + + if (debug) + fprintf(stderr, "Removing ID '%s'\n", us); + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (streq(him->name, us)) { + him->state = STATE_NONE; + who = him; + } + } + if (debug && !who) + fprintf(stderr, "ID '%s' not in database\n", us); +} + +/* + * kickout + * + * Remove all IDs in the internal list which are not part + * of the list passed via the command line. + */ +static void kickout(void) +{ + struct udev_list_node *him_node; + struct udev_list_node *tmp; + + udev_list_node_foreach_safe(him_node, tmp, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (him->state == STATE_OLD) { + udev_list_node_remove(&him->node); + free(him->name); + free(him); + } + } +} + +/* + * missing + * + * Counts all missing IDs in the internal list. + */ +static int missing(int fd) +{ + char *buf; + int ret = 0; + struct udev_list_node *him_node; + + buf = malloc(bufsize); + if (!buf) + return log_oom(); + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (him->state == STATE_NONE) { + ret++; + } else { + while (strlen(him->name)+1 >= bufsize) { + char *tmpbuf; + + bufsize = bufsize << 1; + tmpbuf = realloc(buf, bufsize); + if (!tmpbuf) { + free(buf); + return log_oom(); + } + buf = tmpbuf; + } + snprintf(buf, strlen(him->name)+2, "%s ", him->name); + if (write(fd, buf, strlen(buf)) < 0) { + free(buf); + return -1; + } + } + } + + free(buf); + return ret; +} + +/* + * everybody + * + * Prints out the status of the internal list. + */ +static void everybody(void) +{ + struct udev_list_node *him_node; + const char *state = ""; + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + switch (him->state) { + case STATE_NONE: + state = "none"; + break; + case STATE_OLD: + state = "old"; + break; + case STATE_CONFIRMED: + state = "confirmed"; + break; + } + fprintf(stderr, "ID: %s=%s\n", him->name, state); + } +} + +int main(int argc, char **argv) +{ + struct udev *udev; + static const struct option options[] = { + { "add", no_argument, NULL, 'a' }, + { "remove", no_argument, NULL, 'r' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + int argi; + char *checkpoint, *us; + int fd; + int i; + int ret = EXIT_SUCCESS; + int prune = 0; + char tmpdir[UTIL_PATH_SIZE]; + + udev = udev_new(); + if (udev == NULL) { + ret = EXIT_FAILURE; + goto exit; + } + + for (;;) { + int option; + + option = getopt_long(argc, argv, "ardh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'a': + prune = 0; + break; + case 'r': + prune = 1; + break; + case 'd': + debug = 1; + break; + case 'h': + usage(); + goto exit; + default: + ret = 1; + goto exit; + } + } + + argi = optind; + if (argi + 2 > argc) { + printf("Missing parameter(s)\n"); + ret = 1; + goto exit; + } + checkpoint = argv[argi++]; + us = argv[argi++]; + + if (signal(SIGALRM, sig_alrm) == SIG_ERR) { + fprintf(stderr, "Cannot set SIGALRM: %m\n"); + ret = 2; + goto exit; + } + + udev_list_node_init(&bunch); + + if (debug) + fprintf(stderr, "Using checkpoint '%s'\n", checkpoint); + + strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL); + fd = prepare(tmpdir, checkpoint); + if (fd < 0) { + ret = 3; + goto out; + } + + if (checkout(fd) < 0) { + ret = 2; + goto out; + } + + for (i = argi; i < argc; i++) { + struct udev_list_node *him_node; + struct _mate *who; + + who = NULL; + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (streq(him->name, argv[i])) + who = him; + } + if (!who) { + struct _mate *him; + + if (debug) + fprintf(stderr, "ID %s: not in database\n", argv[i]); + him = new(struct _mate, 1); + if (!him) { + ret = ENOMEM; + goto out; + } + + him->name = strdup(argv[i]); + if (!him->name) { + free(him); + ret = ENOMEM; + goto out; + } + + him->state = STATE_NONE; + udev_list_node_append(&him->node, &bunch); + } else { + if (debug) + fprintf(stderr, "ID %s: found in database\n", argv[i]); + who->state = STATE_CONFIRMED; + } + } + + if (prune) + reject(us); + else + invite(us); + + if (debug) { + everybody(); + fprintf(stderr, "Prune lists\n"); + } + kickout(); + + lseek(fd, 0, SEEK_SET); + ftruncate(fd, 0); + ret = missing(fd); + + lockf(fd, F_ULOCK, 0); + close(fd); +out: + if (debug) + everybody(); + if (ret >= 0) + printf("COLLECT_%s=%d\n", checkpoint, ret); +exit: + udev_unref(udev); + return ret; +} diff --git a/src/grp-udev/hwdb/.gitignore b/src/grp-udev/hwdb/.gitignore new file mode 100644 index 0000000000..c4796815d2 --- /dev/null +++ b/src/grp-udev/hwdb/.gitignore @@ -0,0 +1,8 @@ +/20-acpi-vendor.hwdb.base +/acpi_id_registry.html +/ma-large.txt +/ma-medium.txt +/ma-small.txt +/pci.ids +/pnp_id_registry.html +/usb.ids diff --git a/src/grp-udev/hwdb/20-OUI.hwdb b/src/grp-udev/hwdb/20-OUI.hwdb new file mode 100644 index 0000000000..dd63627328 --- /dev/null +++ b/src/grp-udev/hwdb/20-OUI.hwdb @@ -0,0 +1,74853 @@ +# This file is part of systemd. +# +# Data imported from: +# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=txt +# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-M&format=txt +# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-S&format=txt + +OUI:70B3D5913* + ID_OUI_FROM_DATABASE=Shenzhen Riitek Technology Co.,Ltd + +OUI:70B3D588A* + ID_OUI_FROM_DATABASE=Perceptics, LLC + +OUI:70B3D5001* + ID_OUI_FROM_DATABASE=SOREDI touch systems GmbH + +OUI:70B3D5D70* + ID_OUI_FROM_DATABASE=Rational Production srl Unipersonale + +OUI:70B3D5426* + ID_OUI_FROM_DATABASE=Zehnder Group Nederland + +OUI:70B3D5266* + ID_OUI_FROM_DATABASE=Spectra Displays Ltd + +OUI:70B3D5D38* + ID_OUI_FROM_DATABASE=Vista Research, Inc. + +OUI:70B3D5996* + ID_OUI_FROM_DATABASE=XpertSea Solutions inc. + +OUI:70B3D5605* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D516E* + ID_OUI_FROM_DATABASE=Jemac Sweden AB + +OUI:70B3D5671* + ID_OUI_FROM_DATABASE=Sea Shell Corporation + +OUI:70B3D57EC* + ID_OUI_FROM_DATABASE=GRIDSMART Technologies + +OUI:70B3D5AF9* + ID_OUI_FROM_DATABASE=Critical Link LLC + +OUI:70B3D5D59* + ID_OUI_FROM_DATABASE=WyreStorm Technologies Ltd + +OUI:70B3D5A3C* + ID_OUI_FROM_DATABASE=Wave Music Ltd + +OUI:70B3D5FC1* + ID_OUI_FROM_DATABASE=InDiCor + +OUI:70B3D5BB2* + ID_OUI_FROM_DATABASE=Mettler Toledo Hi-Speed + +OUI:70B3D514F* + ID_OUI_FROM_DATABASE=Mobile Devices Unlimited + +OUI:70B3D5891* + ID_OUI_FROM_DATABASE=neocontrol soluções em automação + +OUI:70B3D501E* + ID_OUI_FROM_DATABASE=ePOINT Embedded Computing Limited + +OUI:70B3D5C9D* + ID_OUI_FROM_DATABASE=APG Cash Drawer + +OUI:70B3D534C* + ID_OUI_FROM_DATABASE=GLT Exports Ltd + +OUI:70B3D5B51* + ID_OUI_FROM_DATABASE=Critical Link LLC + +OUI:70B3D5D29* + ID_OUI_FROM_DATABASE=Sportzcast + +OUI:70B3D513B* + ID_OUI_FROM_DATABASE=Sienna Corporation + +OUI:70B3D537F* + ID_OUI_FROM_DATABASE=IDS Innomic GmbH + +OUI:70B3D5694* + ID_OUI_FROM_DATABASE=MoviTHERM + +OUI:70B3D5B28* + ID_OUI_FROM_DATABASE=HUSTY M.Styczen J.Hupert sp.j. + +OUI:70B3D514E* + ID_OUI_FROM_DATABASE=Innosonix GmbH + +OUI:70B3D5185* + ID_OUI_FROM_DATABASE=R&D Gran-System-S LLC + +OUI:70B3D55B1* + ID_OUI_FROM_DATABASE=EPD Electronics + +OUI:70B3D5EAE* + ID_OUI_FROM_DATABASE=Orlaco Products B.V. + +OUI:70B3D5066* + ID_OUI_FROM_DATABASE=North Pole Engineering, Inc. + +OUI:70B3D557D* + ID_OUI_FROM_DATABASE=WICOM1 GmbH + +OUI:70B3D5E77* + ID_OUI_FROM_DATABASE=OPTIX JSC + +OUI:70B3D5740* + ID_OUI_FROM_DATABASE=Prisma Telecom Testing Srl + +OUI:70B3D584E* + ID_OUI_FROM_DATABASE=Chromalox, Inc. + +OUI:70B3D5817* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D543B* + ID_OUI_FROM_DATABASE=Kalycito Infotech Private Limited + +OUI:70B3D5C88* + ID_OUI_FROM_DATABASE=SINED srl + +OUI:70B3D5CD6* + ID_OUI_FROM_DATABASE=VideoRay LLC + +OUI:70B3D5A1D* + ID_OUI_FROM_DATABASE=Fluid Components International + +OUI:70B3D5079* + ID_OUI_FROM_DATABASE=CheckBill Co,Ltd. + +OUI:70B3D58FF* + ID_OUI_FROM_DATABASE=IMST GmbH + +OUI:70B3D5DC0* + ID_OUI_FROM_DATABASE=ATEME + +OUI:70B3D5F79* + ID_OUI_FROM_DATABASE=Firehose Labs, Inc. + +OUI:70B3D5404* + ID_OUI_FROM_DATABASE=RANIX,Inc. + +OUI:70B3D552D* + ID_OUI_FROM_DATABASE=Tanaka Electric Industry Co., Ltd. + +OUI:70B3D5BE5* + ID_OUI_FROM_DATABASE=Pantec Engineering AG + +OUI:70B3D5D90* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D545F* + ID_OUI_FROM_DATABASE=Cloud4Wi + +OUI:70B3D535E* + ID_OUI_FROM_DATABASE=EIDOS s.p.a. + +OUI:70B3D5F96* + ID_OUI_FROM_DATABASE=Ecologicsense + +OUI:70B3D50D8* + ID_OUI_FROM_DATABASE=Laser Imagineering GmbH + +OUI:70B3D57E4* + ID_OUI_FROM_DATABASE=C21 Systems Ltd + +OUI:70B3D5995* + ID_OUI_FROM_DATABASE=LayTec AG + +OUI:70B3D5957* + ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG + +OUI:70B3D54DD* + ID_OUI_FROM_DATABASE=Road-iQ, LLC + +OUI:70B3D5FBA* + ID_OUI_FROM_DATABASE=Apogee Applied Research, Inc. + +OUI:70B3D563B* + ID_OUI_FROM_DATABASE=Lazer Safe Pty Ltd + +OUI:70B3D56D1* + ID_OUI_FROM_DATABASE=Visual Engineering Technologies Ltd + +OUI:70B3D50DF* + ID_OUI_FROM_DATABASE=B.E.A. sa + +OUI:70B3D5342* + ID_OUI_FROM_DATABASE=Solectrix + +OUI:70B3D5E20* + ID_OUI_FROM_DATABASE=Signature Control Systems, LLC. + +OUI:70B3D5538* + ID_OUI_FROM_DATABASE=sydetion UG (h.b.) + +OUI:70B3D52DC* + ID_OUI_FROM_DATABASE=Bolide Technology Group, Inc. + +OUI:70B3D5B85* + ID_OUI_FROM_DATABASE=Fenotech Inc. + +OUI:70B3D52EE* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D51E5* + ID_OUI_FROM_DATABASE=VendNovation LLC + +OUI:70B3D5FE7* + ID_OUI_FROM_DATABASE=VEILUX INC. + +OUI:70B3D508F* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D5E55* + ID_OUI_FROM_DATABASE=BELT S.r.l. + +OUI:70B3D588F* + ID_OUI_FROM_DATABASE=Quaesta Instruments, LLC + +OUI:70B3D5835* + ID_OUI_FROM_DATABASE=CommBox P/L + +OUI:70B3D5730* + ID_OUI_FROM_DATABASE=Videogenix + +OUI:70B3D55A9* + ID_OUI_FROM_DATABASE=Bunka Shutter Co., Ltd. + +OUI:70B3D5714* + ID_OUI_FROM_DATABASE=Alturna Networks + +OUI:70B3D52A7* + ID_OUI_FROM_DATABASE=Plasmability, LLC + +OUI:70B3D5D7A* + ID_OUI_FROM_DATABASE=Speedifi Inc + +OUI:70B3D5737* + ID_OUI_FROM_DATABASE=SD Biosensor + +OUI:70B3D56DA* + ID_OUI_FROM_DATABASE=Enovative Networks, Inc. + +OUI:70B3D50D7* + ID_OUI_FROM_DATABASE=Russian Telecom Equipment Company + +OUI:70B3D546F* + ID_OUI_FROM_DATABASE=serva transport systems GmbH + +OUI:70B3D5A00* + ID_OUI_FROM_DATABASE=ATX NETWORKS LTD + +OUI:70B3D51AF* + ID_OUI_FROM_DATABASE=Teenage Engineering AB + +OUI:70B3D55C8* + ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd + +OUI:70B3D5D9C* + ID_OUI_FROM_DATABASE=Subinitial LLC + +OUI:70B3D5923* + ID_OUI_FROM_DATABASE=eumig industrie-tv GmbH + +OUI:70B3D578B* + ID_OUI_FROM_DATABASE=Jingtu Printing Systems Co., Ltd + +OUI:70B3D54BB* + ID_OUI_FROM_DATABASE=Plazma-T + +OUI:70B3D5DF2* + ID_OUI_FROM_DATABASE=AML + +OUI:70B3D5CB8* + ID_OUI_FROM_DATABASE=Verti Tecnologia + +OUI:70B3D5A97* + ID_OUI_FROM_DATABASE=Bizwerks, LLC + +OUI:70B3D5417* + ID_OUI_FROM_DATABASE=Figment Design Laboratories + +OUI:70B3D5CF6* + ID_OUI_FROM_DATABASE=Tornado Modular Systems + +OUI:70B3D591E* + ID_OUI_FROM_DATABASE=Creotech Instruments S.A. + +OUI:70B3D5775* + ID_OUI_FROM_DATABASE=Sonel S.A. + +OUI:70B3D55FD* + ID_OUI_FROM_DATABASE=Windar Photonics + +OUI:70B3D5078* + ID_OUI_FROM_DATABASE=OrbiWise SA + +OUI:70B3D5B78* + ID_OUI_FROM_DATABASE=HOERMANN GmbH + +OUI:70B3D5D61* + ID_OUI_FROM_DATABASE=VITEC + +OUI:70B3D5F78* + ID_OUI_FROM_DATABASE=Manvish eTech Pvt. Ltd. + +OUI:70B3D5146* + ID_OUI_FROM_DATABASE=3City Electronics + +OUI:70B3D515F* + ID_OUI_FROM_DATABASE=SAVRONİK ELEKTRONİK + +OUI:70B3D54DC* + ID_OUI_FROM_DATABASE=JK DEVICE CORPORATION + +OUI:70B3D5341* + ID_OUI_FROM_DATABASE=Vtron Pty Ltd + +OUI:70B3D5875* + ID_OUI_FROM_DATABASE=Peek Traffic + +OUI:70B3D56A5* + ID_OUI_FROM_DATABASE=Akenori PTE LTD + +OUI:70B3D56E0* + ID_OUI_FROM_DATABASE=ABB SPA - DMPC + +OUI:70B3D5C5B* + ID_OUI_FROM_DATABASE=ACD Elektronik GmbH + +OUI:70B3D5030* + ID_OUI_FROM_DATABASE=Tresent Technologies + +OUI:70B3D57CD* + ID_OUI_FROM_DATABASE=Molekuler Goruntuleme A.S. + +OUI:70B3D55EA* + ID_OUI_FROM_DATABASE=KYS,INC + +OUI:70B3D57B3* + ID_OUI_FROM_DATABASE=BroadSoft Inc + +OUI:70B3D5C39* + ID_OUI_FROM_DATABASE=MeshWorks Wireless Oy + +OUI:70B3D5660* + ID_OUI_FROM_DATABASE=Smart Service Technologies CO., LTD + +OUI:70B3D5EFE* + ID_OUI_FROM_DATABASE=MEIDEN SYSTEM SOLUTIONS + +OUI:70B3D5C60* + ID_OUI_FROM_DATABASE=Aircell Inc + +OUI:70B3D5C03* + ID_OUI_FROM_DATABASE=XAVi Technologies Corp. + +OUI:70B3D50FF* + ID_OUI_FROM_DATABASE=INTERNET PROTOCOLO LOGICA SL + +OUI:70B3D5502* + ID_OUI_FROM_DATABASE=Glidewell Laboratories + +OUI:70B3D56F8* + ID_OUI_FROM_DATABASE=SENSEON Corporation + +OUI:70B3D535D* + ID_OUI_FROM_DATABASE=Fresh Idea Factory BV + +OUI:70B3D5344* + ID_OUI_FROM_DATABASE=IHI Inspection & Instrumentation Co., Ltd. + +OUI:70B3D53DB* + ID_OUI_FROM_DATABASE=KST technology + +OUI:70B3D54CD* + ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. + +OUI:70B3D522E* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5EA3* + ID_OUI_FROM_DATABASE=Gridless Power Corperation + +OUI:70B3D5A5A* + ID_OUI_FROM_DATABASE=RCS Energy Management Ltd + +OUI:70B3D513A* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D575D* + ID_OUI_FROM_DATABASE=Nanjing Magewell Electronics Co., Ltd. + +OUI:70B3D50EC* + ID_OUI_FROM_DATABASE=ACS MOTION CONTROL + +OUI:70B3D51F5* + ID_OUI_FROM_DATABASE=Martec S.p.A. + +OUI:70B3D522B* + ID_OUI_FROM_DATABASE=VITEC + +OUI:70B3D5708* + ID_OUI_FROM_DATABASE=IBM Research GmbH + +OUI:70B3D5C81* + ID_OUI_FROM_DATABASE=DSP DESIGN + +OUI:70B3D5FA1* + ID_OUI_FROM_DATABASE=BBI Engineering, Inc. + +OUI:70B3D560C* + ID_OUI_FROM_DATABASE=IST ElektronikgesmbH + +OUI:70B3D5936* + ID_OUI_FROM_DATABASE=FARO TECHNOLOGIES, INC. + +OUI:70B3D5EF2* + ID_OUI_FROM_DATABASE=Kongsberg Intergrated Tactical Systems + +OUI:70B3D5C58* + ID_OUI_FROM_DATABASE=RMI Laser LLC + +OUI:70B3D5507* + ID_OUI_FROM_DATABASE=Human Oriented Technology, Inc. + +OUI:70B3D5BC6* + ID_OUI_FROM_DATABASE=Hatteland Display AS + +OUI:70B3D555D* + ID_OUI_FROM_DATABASE=LunaNexus Inc + +OUI:70B3D5DF3* + ID_OUI_FROM_DATABASE=SPC Bioclinicum + +OUI:70B3D5CF1* + ID_OUI_FROM_DATABASE=LightDec GmbH & Co. KG + +OUI:70B3D58DC* + ID_OUI_FROM_DATABASE=Niveo International BV + +OUI:70B3D5D1E* + ID_OUI_FROM_DATABASE=Houston Radar LLC + +OUI:70B3D554D* + ID_OUI_FROM_DATABASE=Qingdao Haitian Weiye Automation Control System Co., Ltd + +OUI:70B3D5A15* + ID_OUI_FROM_DATABASE=Intercore GmbH + +OUI:70B3D545E* + ID_OUI_FROM_DATABASE=eSOL Co.,Ltd. + +OUI:70B3D5B88* + ID_OUI_FROM_DATABASE=ARP Corporation + +OUI:70B3D5BBE* + ID_OUI_FROM_DATABASE=Sunrise Systems Electronics Co. Inc. + +OUI:70B3D5CBC* + ID_OUI_FROM_DATABASE=Procon Electronics Pty Ltd + +OUI:70B3D52DA* + ID_OUI_FROM_DATABASE=Skywave Networks Private Limited + +OUI:70B3D5B08* + ID_OUI_FROM_DATABASE=Secuinfo Co. Ltd + +OUI:70B3D533C* + ID_OUI_FROM_DATABASE=Videri Inc. + +OUI:70B3D53E5* + ID_OUI_FROM_DATABASE=ATEME + +OUI:70B3D5FC5* + ID_OUI_FROM_DATABASE=Eltwin A/S + +OUI:70B3D5ECF* + ID_OUI_FROM_DATABASE=Ipitek + +OUI:70B3D5099* + ID_OUI_FROM_DATABASE=Schwer+Kopka GmbH + +OUI:70B3D57E7* + ID_OUI_FROM_DATABASE=Atessa, Inc. + +OUI:70B3D539B* + ID_OUI_FROM_DATABASE=IROC AB + +OUI:70B3D53CE* + ID_OUI_FROM_DATABASE=Aditec GmbH + +OUI:70B3D5190* + ID_OUI_FROM_DATABASE=Fantom Wireless, Inc. + +OUI:70B3D56B6* + ID_OUI_FROM_DATABASE=INRADIOS GmbH + +OUI:70B3D529D* + ID_OUI_FROM_DATABASE=XTech2 SIA + +OUI:70B3D5814* + ID_OUI_FROM_DATABASE=Ingenieurbuero SOMTRONIK + +OUI:70B3D542F* + ID_OUI_FROM_DATABASE=SINTOKOGIO, LTD + +OUI:70B3D5702* + ID_OUI_FROM_DATABASE=Sensor Highway Ltd + +OUI:70B3D50E0* + ID_OUI_FROM_DATABASE=PLCiS + +OUI:70B3D5EDC* + ID_OUI_FROM_DATABASE=J.D. Koftinoff Software, Ltd. + +OUI:001BC50C9* + ID_OUI_FROM_DATABASE=UAB Kitron + +OUI:001BC50C8* + ID_OUI_FROM_DATABASE=Dialine + +OUI:70B3D551E* + ID_OUI_FROM_DATABASE=Fundación Cardiovascular de Colombia + +OUI:70B3D5EB0* + ID_OUI_FROM_DATABASE=Nautel Limted + +OUI:70B3D5625* + ID_OUI_FROM_DATABASE=VX Instruments GmbH + +OUI:70B3D5D86* + ID_OUI_FROM_DATABASE=WPGSYS Pte Ltd + +OUI:70B3D54C8* + ID_OUI_FROM_DATABASE=Hosokawa Micron Powder Systems + +OUI:001BC50B4* + ID_OUI_FROM_DATABASE=COBAN SRL + +OUI:001BC50AE* + ID_OUI_FROM_DATABASE=Techlan Reti s.r.l. + +OUI:001BC50C2* + ID_OUI_FROM_DATABASE=TechSolutions A/S + +OUI:001BC50BF* + ID_OUI_FROM_DATABASE=TN Core Co.,Ltd. + +OUI:001BC50BE* + ID_OUI_FROM_DATABASE=YESpay International Ltd + +OUI:001BC50B9* + ID_OUI_FROM_DATABASE=Denki Kogyo Company, Limited + +OUI:001BC50B8* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC50B5* + ID_OUI_FROM_DATABASE=Exibea AB + +OUI:001BC50C3* + ID_OUI_FROM_DATABASE=inomatic GmbH + +OUI:001BC50AF* + ID_OUI_FROM_DATABASE=Enerwise Solutions Ltd. + +OUI:001BC50A5* + ID_OUI_FROM_DATABASE=Tesla Controls + +OUI:001BC50A0* + ID_OUI_FROM_DATABASE=HomerSoft sp. z o.o. + +OUI:001BC509F* + ID_OUI_FROM_DATABASE=ENTE Sp. z o.o. + +OUI:001BC50AB* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC50AA* + ID_OUI_FROM_DATABASE=Senceive Ltd + +OUI:001BC50A4* + ID_OUI_FROM_DATABASE=RADMOR S.A. + +OUI:001BC509A* + ID_OUI_FROM_DATABASE=Shenzhen Guang Lian Zhi Tong Limited + +OUI:001BC5099* + ID_OUI_FROM_DATABASE=UAB Kitron + +OUI:001BC5098* + ID_OUI_FROM_DATABASE=Cubic Systems, Inc. + +OUI:001BC5096* + ID_OUI_FROM_DATABASE=Sanstreak Corp. + +OUI:001BC5095* + ID_OUI_FROM_DATABASE=PREVAC sp. z o.o. + +OUI:001BC508F* + ID_OUI_FROM_DATABASE=Unilever R&D + +OUI:001BC508E* + ID_OUI_FROM_DATABASE=TrendPoint Systems + +OUI:001BC508B* + ID_OUI_FROM_DATABASE=Nistica + +OUI:001BC508C* + ID_OUI_FROM_DATABASE=Triax A/S + +OUI:001BC5090* + ID_OUI_FROM_DATABASE=Seven Solutions S.L + +OUI:001BC507D* + ID_OUI_FROM_DATABASE=Greatcom AG + +OUI:001BC5079* + ID_OUI_FROM_DATABASE=HPI High Pressure Instrumentation GmbH + +OUI:001BC5083* + ID_OUI_FROM_DATABASE=DIWEL + +OUI:001BC5065* + ID_OUI_FROM_DATABASE=Plair Media Inc. + +OUI:001BC506F* + ID_OUI_FROM_DATABASE=LLC Emzior + +OUI:001BC5073* + ID_OUI_FROM_DATABASE=tado GmbH + +OUI:001BC5069* + ID_OUI_FROM_DATABASE=Datasat Digital Entertainment + +OUI:001BC5051* + ID_OUI_FROM_DATABASE=QQ Navigation AB + +OUI:001BC505F* + ID_OUI_FROM_DATABASE=Klingenthaler Musikelektronik GmbH + +OUI:001BC505B* + ID_OUI_FROM_DATABASE=konzeptpark GmbH + +OUI:001BC5055* + ID_OUI_FROM_DATABASE=LUMIPLAN TRANSPORT + +OUI:001BC5047* + ID_OUI_FROM_DATABASE=PT. Amanindo Nusapadu + +OUI:001BC504B* + ID_OUI_FROM_DATABASE=Silicon Controls + +OUI:001BC5041* + ID_OUI_FROM_DATABASE=DesignA Electronics Limited + +OUI:001BC503D* + ID_OUI_FROM_DATABASE=rioxo GmbH + +OUI:001BC503C* + ID_OUI_FROM_DATABASE=Xiphos Systems Corp. + +OUI:001BC5037* + ID_OUI_FROM_DATABASE=ITW Reyflex North America + +OUI:001BC5033* + ID_OUI_FROM_DATABASE=JE Suunnittelu Oy + +OUI:001BC5032* + ID_OUI_FROM_DATABASE=Osborne Coinage Co + +OUI:001BC5036* + ID_OUI_FROM_DATABASE=LOMAR SRL + +OUI:001BC5035* + ID_OUI_FROM_DATABASE=RTLS Ltd. + +OUI:001BC502D* + ID_OUI_FROM_DATABASE=DDTRONIK Dariusz Dowgiert + +OUI:001BC502C* + ID_OUI_FROM_DATABASE=Care Everywhere LLC + +OUI:001BC502B* + ID_OUI_FROM_DATABASE=Saturn South Pty Ltd + +OUI:001BC5029* + ID_OUI_FROM_DATABASE=2 FRANCE MARINE + +OUI:001BC5028* + ID_OUI_FROM_DATABASE=STECHWIN.CO.LTD. + +OUI:001BC5017* + ID_OUI_FROM_DATABASE=cPacket Networks + +OUI:001BC500E* + ID_OUI_FROM_DATABASE=Vigor Electric Corp + +OUI:001BC5013* + ID_OUI_FROM_DATABASE=Zamir Recognition Systems Ltd. + +OUI:001BC5014* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC5009* + ID_OUI_FROM_DATABASE=Solomon Systech Pte Ltd + +OUI:001BC500A* + ID_OUI_FROM_DATABASE=Mercury HMI Ltd + +OUI:001BC500D* + ID_OUI_FROM_DATABASE=Advanced Scientific Concepts, Inc. + +OUI:001BC5004* + ID_OUI_FROM_DATABASE=Intellvisions Software Ltd + +OUI:001BC5003* + ID_OUI_FROM_DATABASE=MicroSigns Technologies Inc + +OUI:001BC5000* + ID_OUI_FROM_DATABASE=Converging Systems Inc. + +OUI:70B3D541A* + ID_OUI_FROM_DATABASE=HYOSUNG Power & Industrial Systems + +OUI:70B3D5BAB* + ID_OUI_FROM_DATABASE=Axotec Technologies GmbH + +OUI:70B3D569E* + ID_OUI_FROM_DATABASE=PTYPE Co., LTD. + +OUI:70B3D575C* + ID_OUI_FROM_DATABASE=UPM Technology, Inc + +OUI:70B3D5124* + ID_OUI_FROM_DATABASE=Forschungs- und Transferzentrum Leipzig e.V. + +OUI:70B3D5F6D* + ID_OUI_FROM_DATABASE=Qowisio + +OUI:70B3D5578* + ID_OUI_FROM_DATABASE=IMAGE TECH CO.,LTD + +OUI:70B3D5187* + ID_OUI_FROM_DATABASE=Elektronik & Präzisionsbau Saalfeld GmbH + +OUI:70B3D50EE* + ID_OUI_FROM_DATABASE=Picture Elements, Inc. + +OUI:70B3D528A* + ID_OUI_FROM_DATABASE=Transit Solutions, LLC. + +OUI:70B3D55A0* + ID_OUI_FROM_DATABASE=Ascon Tecnologic S.r.l. + +OUI:70B3D5A6F* + ID_OUI_FROM_DATABASE=8Cups + +OUI:70B3D5929* + ID_OUI_FROM_DATABASE=OutSys + +OUI:70B3D5120* + ID_OUI_FROM_DATABASE=GSP Sprachtechnologie GmbH + +OUI:70B3D523E* + ID_OUI_FROM_DATABASE=Tornado Modular Systems + +OUI:70B3D5EA2* + ID_OUI_FROM_DATABASE=Transportal Solutions Ltd + +OUI:70B3D545D* + ID_OUI_FROM_DATABASE=Sensapex Oy + +OUI:70B3D58B0* + ID_OUI_FROM_DATABASE=IES S.r.l. + +OUI:70B3D5FB6* + ID_OUI_FROM_DATABASE=KRONOTECH SRL + +OUI:70B3D5EB7* + ID_OUI_FROM_DATABASE=Skreens + +OUI:70B3D538F* + ID_OUI_FROM_DATABASE=Sorynorydotcom Inc + +OUI:70B3D57E9* + ID_OUI_FROM_DATABASE=Mecsel Oy + +OUI:70B3D506C* + ID_OUI_FROM_DATABASE=AppTek + +OUI:70B3D5818* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5895* + ID_OUI_FROM_DATABASE=Integrated Control Corp. + +OUI:70B3D5232* + ID_OUI_FROM_DATABASE=UCONSYS + +OUI:70B3D501A* + ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH + +OUI:70B3D537B* + ID_OUI_FROM_DATABASE=Power Ltd. + +OUI:70B3D5FCC* + ID_OUI_FROM_DATABASE=DIgSILENT GmbH + +OUI:70B3D5FD8* + ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme + +OUI:70B3D576B* + ID_OUI_FROM_DATABASE=EMPELOR GmbH + +OUI:70B3D50B8* + ID_OUI_FROM_DATABASE=Lucas-Nülle GmbH + +OUI:70B3D5FEB* + ID_OUI_FROM_DATABASE=Les distributions Multi-Secure incorporee + +OUI:70B3D5658* + ID_OUI_FROM_DATABASE=emperor brands + +OUI:70B3D53A9* + ID_OUI_FROM_DATABASE=Vivalnk + +OUI:001BC5087* + ID_OUI_FROM_DATABASE=Onnet Technologies and Innovations LLC + +OUI:70B3D5720* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D56BF* + ID_OUI_FROM_DATABASE=Otto Bihler Maschinenfabrik GmbH & Co. KG + +OUI:70B3D5194* + ID_OUI_FROM_DATABASE=Husty M.Styczen J.Hupert Sp.J. + +OUI:70B3D5742* + ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd + +OUI:70B3D5493* + ID_OUI_FROM_DATABASE=Impulse Networks Pte Ltd + +OUI:70B3D5BB7* + ID_OUI_FROM_DATABASE=Innoflight, Inc. + +OUI:70B3D5B31* + ID_OUI_FROM_DATABASE=Qwave Inc + +OUI:70B3D5DA8* + ID_OUI_FROM_DATABASE=Tagarno AS + +OUI:70B3D5ADE* + ID_OUI_FROM_DATABASE=ISAC SRL + +OUI:70B3D5E85* + ID_OUI_FROM_DATABASE=Explorer Inc. + +OUI:70B3D5F3C* + ID_OUI_FROM_DATABASE=Gigaray + +OUI:70B3D5F13* + ID_OUI_FROM_DATABASE=MEDIAM Sp. z o.o. + +OUI:70B3D5295* + ID_OUI_FROM_DATABASE=Cello Electronics (UK) Ltd + +OUI:70B3D5283* + ID_OUI_FROM_DATABASE=TextNinja Co. + +OUI:70B3D5D66* + ID_OUI_FROM_DATABASE=Ascendent Technology Group + +OUI:70B3D53A5* + ID_OUI_FROM_DATABASE=KMtronic ltd + +OUI:70B3D56A0* + ID_OUI_FROM_DATABASE=Active Research Limited + +OUI:70B3D5E3E* + ID_OUI_FROM_DATABASE=Sol Welding srl + +OUI:70B3D54AE* + ID_OUI_FROM_DATABASE=Reinhardt System- und Messelectronic GmbH + +OUI:70B3D538C* + ID_OUI_FROM_DATABASE=MiraeSignal Co., Ltd + +OUI:70B3D5B1D* + ID_OUI_FROM_DATABASE=Safelet BV + +OUI:70B3D504C* + ID_OUI_FROM_DATABASE=mapna group + +OUI:70B3D56EC* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5ABA* + ID_OUI_FROM_DATABASE=CL International + +OUI:70B3D565A* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5FFE* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5721* + ID_OUI_FROM_DATABASE=Zoe Medical + +OUI:70B3D5186* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:70B3D53BC* + ID_OUI_FROM_DATABASE=SciTronix + +OUI:70B3D5BE6* + ID_OUI_FROM_DATABASE=CCII Systems (Pty) Ltd + +OUI:70B3D5BD2* + ID_OUI_FROM_DATABASE=Burk Technology + +OUI:70B3D5925* + ID_OUI_FROM_DATABASE=Diamante Lighting Srl + +OUI:70B3D5C8B* + ID_OUI_FROM_DATABASE=Asia Pacific Satellite Coummunication Inc. + +OUI:70B3D57B4* + ID_OUI_FROM_DATABASE=Zumbach Electronic AG + +OUI:70B3D5B11* + ID_OUI_FROM_DATABASE=CAB S.R.L. + +OUI:70B3D5513* + ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme + +OUI:70B3D5F99* + ID_OUI_FROM_DATABASE=TEX COMPUTER SRL + +OUI:70B3D53A7* + ID_OUI_FROM_DATABASE=Varikorea + +OUI:70B3D5174* + ID_OUI_FROM_DATABASE=Carlson Wireless Technologies Inc. + +OUI:70B3D57D5* + ID_OUI_FROM_DATABASE=SICS Swedish ICT + +OUI:70B3D5448* + ID_OUI_FROM_DATABASE=B/E Aerospace, Inc. + +OUI:70B3D5A53* + ID_OUI_FROM_DATABASE=GS Industrie-Elektronik GmbH + +OUI:70B3D5BE3* + ID_OUI_FROM_DATABASE=Saratov Electrounit Production Plant named after Sergo Ordzhonikidze, OJSC + +OUI:70B3D5AAD* + ID_OUI_FROM_DATABASE=Bartec GmbH + +OUI:70B3D5E82* + ID_OUI_FROM_DATABASE=RF Track + +OUI:70B3D585D* + ID_OUI_FROM_DATABASE=ATHREYA INC + +OUI:70B3D5821* + ID_OUI_FROM_DATABASE=HL2 group + +OUI:70B3D5938* + ID_OUI_FROM_DATABASE=JETI Technische Instrumente GmbH + +OUI:70B3D5A21* + ID_OUI_FROM_DATABASE=PPI Inc. + +OUI:70B3D5E4F* + ID_OUI_FROM_DATABASE=RWS Automation GmbH + +OUI:70B3D55C4* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D567B* + ID_OUI_FROM_DATABASE=Stesalit Systems Ltd + +OUI:70B3D51C8* + ID_OUI_FROM_DATABASE=LDA audio video profesional S.L. + +OUI:70B3D5142* + ID_OUI_FROM_DATABASE=DAVE SRL + +OUI:70B3D5D60* + ID_OUI_FROM_DATABASE=Flintab AB + +OUI:70B3D51B4* + ID_OUI_FROM_DATABASE=5nines + +OUI:70B3D5522* + ID_OUI_FROM_DATABASE=Syncopated Engineering Inc + +OUI:70B3D578E* + ID_OUI_FROM_DATABASE=effectas GmbH + +OUI:70B3D5CC1* + ID_OUI_FROM_DATABASE=BEEcube Inc. + +OUI:70B3D59D4* + ID_OUI_FROM_DATABASE=Transas Marine Limited + +OUI:70B3D5AE2* + ID_OUI_FROM_DATABASE=Transas Marine Limited + +OUI:001BC5015* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5476* + ID_OUI_FROM_DATABASE=FR-Team International SA + +OUI:70B3D5326* + ID_OUI_FROM_DATABASE=NEMEUS-SAS + +OUI:70B3D5FE6* + ID_OUI_FROM_DATABASE=SHIZUKI ELECTRIC CO.,INC + +OUI:70B3D5943* + ID_OUI_FROM_DATABASE=Abbott Medical Optics Inc. + +OUI:70B3D5AE0* + ID_OUI_FROM_DATABASE=AnyComm.Co.,Ltd. + +OUI:70B3D52B3* + ID_OUI_FROM_DATABASE=HAS co.,ltd. + +OUI:70B3D511D* + ID_OUI_FROM_DATABASE=Texka Labs + +OUI:70B3D5D5A* + ID_OUI_FROM_DATABASE=WyreStorm Technologies Ltd + +OUI:70B3D5CB6* + ID_OUI_FROM_DATABASE=Kuebrich Ingeniergesellschaft mbh & Co. KG + +OUI:70B3D50BE* + ID_OUI_FROM_DATABASE=ChamSys Ltd + +OUI:70B3D51E0* + ID_OUI_FROM_DATABASE=TOPROOTTechnology Corp. Ltd. + +OUI:70B3D50C8* + ID_OUI_FROM_DATABASE=Fin Robotics Inc + +OUI:70B3D574A* + ID_OUI_FROM_DATABASE=Mettler Toledo Hi-Speed + +OUI:70B3D539D* + ID_OUI_FROM_DATABASE=Comark Interactive Solutions + +OUI:70B3D5D07* + ID_OUI_FROM_DATABASE=Waversa Systems + +OUI:70B3D54D1* + ID_OUI_FROM_DATABASE=Contraves Advanced Devices Sdn. Bhd. + +OUI:70B3D5771* + ID_OUI_FROM_DATABASE=Apator Miitors ApS + +OUI:70B3D5CA9* + ID_OUI_FROM_DATABASE=Nxcontrol system Co., Ltd. + +OUI:70B3D5EDF* + ID_OUI_FROM_DATABASE=GridNavigator + +OUI:70B3D520A* + ID_OUI_FROM_DATABASE=Golden Grid Systems + +OUI:70B3D5517* + ID_OUI_FROM_DATABASE=ISPHER + +OUI:70B3D5140* + ID_OUI_FROM_DATABASE=Virta Laboratories, Inc. + +OUI:70B3D59F0* + ID_OUI_FROM_DATABASE=FUJICOM Co.,Ltd. + +OUI:70B3D576F* + ID_OUI_FROM_DATABASE=OTI LTD + +OUI:70B3D54EB* + ID_OUI_FROM_DATABASE=INFOSOFT DIGITAL DESIGN & SERVICES PRIVATE LIMITED + +OUI:70B3D5D12* + ID_OUI_FROM_DATABASE=FIDELTRONIK POLAND SP. Z O.O. + +OUI:70B3D58F0* + ID_OUI_FROM_DATABASE=ERAESEEDS co.,ltd. + +OUI:70B3D5EE5* + ID_OUI_FROM_DATABASE=Beijing Hzhytech Technology Co.Ltd + +OUI:70B3D5BED* + ID_OUI_FROM_DATABASE=Itrinegy Ltd. + +OUI:70B3D5AF5* + ID_OUI_FROM_DATABASE=Net And Print Inc. + +OUI:70B3D564A* + ID_OUI_FROM_DATABASE=Netbric Technology Co.,Ltd. + +OUI:70B3D5ECA* + ID_OUI_FROM_DATABASE=Transtronic AB + +OUI:70B3D5852* + ID_OUI_FROM_DATABASE=NetBoxSC, LLC + +OUI:70B3D5E76* + ID_OUI_FROM_DATABASE=Dorsett Technologies, Inc. + +OUI:70B3D5172* + ID_OUI_FROM_DATABASE=LumiGrow, Inc + +OUI:70B3D58A4* + ID_OUI_FROM_DATABASE=Phyton, Inc. Microsystems and Development Tools + +OUI:70B3D5166* + ID_OUI_FROM_DATABASE=SERIAL IMAGE INC. + +OUI:70B3D5E59* + ID_OUI_FROM_DATABASE=FRACARRO SPA + +OUI:70B3D576D* + ID_OUI_FROM_DATABASE=Trimble + +OUI:70B3D5F73* + ID_OUI_FROM_DATABASE=ASL Holdings + +OUI:70B3D5D0E* + ID_OUI_FROM_DATABASE=Beijing Aumiwalker technology CO.,LTD + +OUI:70B3D5827* + ID_OUI_FROM_DATABASE=Metromatics Pty Ltd + +OUI:70B3D5F54* + ID_OUI_FROM_DATABASE=Revolution Retail Systems + +OUI:70B3D5B91* + ID_OUI_FROM_DATABASE=Dynetics, Inc. + +OUI:70B3D54DE* + ID_OUI_FROM_DATABASE=Oso Technologies, Inc. + +OUI:70B3D5916* + ID_OUI_FROM_DATABASE=Techno Mathematical Co.,Ltd + +OUI:70B3D5958* + ID_OUI_FROM_DATABASE=pureLiFi Ltd + +OUI:70B3D54F4* + ID_OUI_FROM_DATABASE=WiTagg, Inc + +OUI:70B3D5F4C* + ID_OUI_FROM_DATABASE=Global Lightning Protection Services A(S + +OUI:70B3D5789* + ID_OUI_FROM_DATABASE=SEMEX-EngCon GmbH + +OUI:70B3D5CFD* + ID_OUI_FROM_DATABASE=iLOQ Oy + +OUI:70B3D5BAD* + ID_OUI_FROM_DATABASE=Technik & Design GmbH + +OUI:70B3D5324* + ID_OUI_FROM_DATABASE=Thales Nederland BV + +OUI:70B3D5935* + ID_OUI_FROM_DATABASE=Sensor Developments + +OUI:70B3D5431* + ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. + +OUI:70B3D5C73* + ID_OUI_FROM_DATABASE=C.D.N.CORPORATION + +OUI:70B3D5D80* + ID_OUI_FROM_DATABASE=AMMT GmbH + +OUI:70B3D5D0A* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5CE3* + ID_OUI_FROM_DATABASE=Dalcnet srl + +OUI:70B3D5AB5* + ID_OUI_FROM_DATABASE=BroadSoft Inc + +OUI:70B3D59CA* + ID_OUI_FROM_DATABASE=KOMSIS ELEKTRONIK SISTEMLERI SAN. TIC. LTD.STI + +OUI:70B3D50E6* + ID_OUI_FROM_DATABASE=Nasdaq + +OUI:70B3D5087* + ID_OUI_FROM_DATABASE=Tempus Fugit Consoles bvba + +OUI:70B3D57B7* + ID_OUI_FROM_DATABASE=LSB - LA SALLE BLANCHE + +OUI:70B3D5E30* + ID_OUI_FROM_DATABASE=QUISS AG + +OUI:70B3D5D2D* + ID_OUI_FROM_DATABASE=Evolute Systems Private Limited + +OUI:70B3D5B35* + ID_OUI_FROM_DATABASE=Rexxam Co.,Ltd. + +OUI:70B3D579B* + ID_OUI_FROM_DATABASE=Soniclean Pty Ltd + +OUI:70B3D5F72* + ID_OUI_FROM_DATABASE=Hanshin Electronics + +OUI:70B3D5AA8* + ID_OUI_FROM_DATABASE=West-Com Nurse Call Systems, Inc. + +OUI:70B3D5DF6* + ID_OUI_FROM_DATABASE=Tiab Limited + +OUI:70B3D53E3* + ID_OUI_FROM_DATABASE=Head + +OUI:70B3D5285* + ID_OUI_FROM_DATABASE=Bentec GmbH Drilling & Oilfield Systems + +OUI:70B3D544E* + ID_OUI_FROM_DATABASE=Solace Systems Inc. + +OUI:70B3D513F* + ID_OUI_FROM_DATABASE=Farmobile + +OUI:70B3D5D48* + ID_OUI_FROM_DATABASE=HEADROOM Broadcast GmbH + +OUI:70B3D599F* + ID_OUI_FROM_DATABASE=Confed Holding B.V. + +OUI:70B3D5AE3* + ID_OUI_FROM_DATABASE=Zhejiang Wellsun Electric Meter Co.,Ltd + +OUI:70B3D5BEF* + ID_OUI_FROM_DATABASE=Sensortech Systems Inc. + +OUI:70B3D5B24* + ID_OUI_FROM_DATABASE=Datasat Digital Entertainment + +OUI:70B3D50C1* + ID_OUI_FROM_DATABASE=Nexus Technologies Pty Ltd + +OUI:70B3D5E6E* + ID_OUI_FROM_DATABASE=Lieron BVBA + +OUI:70B3D501C* + ID_OUI_FROM_DATABASE=Kumu Networks + +OUI:70B3D5799* + ID_OUI_FROM_DATABASE=Vitec System Engineering Inc. + +OUI:70B3D5DE8* + ID_OUI_FROM_DATABASE=Nation-E Ltd. + +OUI:70B3D583F* + ID_OUI_FROM_DATABASE=Lumine Lighting Solutions Oy + +OUI:70B3D5E23* + ID_OUI_FROM_DATABASE=Smith Meter, Inc. + +OUI:70B3D5A6E* + ID_OUI_FROM_DATABASE=JSC Electrical Equipment Factory + +OUI:70B3D5F7E* + ID_OUI_FROM_DATABASE=Alpha Elettronica s.r.l. + +OUI:70B3D541E* + ID_OUI_FROM_DATABASE=Redler Computers + +OUI:70B3D5D0D* + ID_OUI_FROM_DATABASE=Logiwaste AB + +OUI:70B3D5F2C* + ID_OUI_FROM_DATABASE=Hengen Technologies GmbH + +OUI:70B3D5459* + ID_OUI_FROM_DATABASE=Protium Technologies, Inc. + +OUI:70B3D5811* + ID_OUI_FROM_DATABASE=CJSC «INTERSET» + +OUI:70B3D56ED* + ID_OUI_FROM_DATABASE=Wiingtech International Co. LTD. + +OUI:70B3D5F39* + ID_OUI_FROM_DATABASE=Zenros ApS + +OUI:70B3D5C56* + ID_OUI_FROM_DATABASE=TELETASK + +OUI:70B3D5E58* + ID_OUI_FROM_DATABASE=Thurlby Thandar Instruments LTD + +OUI:70B3D50AB* + ID_OUI_FROM_DATABASE=KST technology + +OUI:70B3D5062* + ID_OUI_FROM_DATABASE=RM Michaelides Software & Elektronik GmbH + +OUI:70B3D571E* + ID_OUI_FROM_DATABASE=Motec Pty Ltd + +OUI:70B3D58E0* + ID_OUI_FROM_DATABASE=SOUDAX EQUIPEMENTS + +OUI:70B3D5412* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5E75* + ID_OUI_FROM_DATABASE=Nke + +OUI:70B3D5DE7* + ID_OUI_FROM_DATABASE=Innominds Software Private Limited + +OUI:70B3D51F4* + ID_OUI_FROM_DATABASE=Hangzhou Woosiyuan Communication Co.,Ltd. + +OUI:70B3D5092* + ID_OUI_FROM_DATABASE=inomed Medizintechnik GmbH + +OUI:70B3D51A1* + ID_OUI_FROM_DATABASE=HMicro Inc + +OUI:70B3D5B8D* + ID_OUI_FROM_DATABASE=JungwooEng Co., Ltd + +OUI:70B3D520C* + ID_OUI_FROM_DATABASE=Siemens Healthcare Diagnostics + +OUI:70B3D5709* + ID_OUI_FROM_DATABASE=AML + +OUI:70B3D5682* + ID_OUI_FROM_DATABASE=Rosslare Enterprises Limited + +OUI:70B3D50A4* + ID_OUI_FROM_DATABASE=Communication Technology Ltd. + +OUI:70B3D5A99* + ID_OUI_FROM_DATABASE=Bandelin electronic GmbH & Co. KG + +OUI:70B3D57EF* + ID_OUI_FROM_DATABASE=CRAVIS CO., LIMITED + +OUI:70B3D5A62* + ID_OUI_FROM_DATABASE=Environexus + +OUI:70B3D5E92* + ID_OUI_FROM_DATABASE=FUJI DATA SYSTEM CO.,LTD. + +OUI:70B3D5C6D* + ID_OUI_FROM_DATABASE=Cyviz AS + +OUI:70B3D5C43* + ID_OUI_FROM_DATABASE=Future Skies + +OUI:70B3D5D4E* + ID_OUI_FROM_DATABASE=FLSmidth + +OUI:70B3D51AC* + ID_OUI_FROM_DATABASE=SVP Broadcast Microwave S.L. + +OUI:70B3D5090* + ID_OUI_FROM_DATABASE=POWERCRAFT ELECTRONICS PVT. LTD. + +OUI:70B3D53F1* + ID_OUI_FROM_DATABASE=Olympus NDT Canada + +OUI:70B3D59A7* + ID_OUI_FROM_DATABASE=Honeywell + +OUI:70B3D5DC5* + ID_OUI_FROM_DATABASE=Excel Medical Electronics LLC + +OUI:70B3D501D* + ID_OUI_FROM_DATABASE=Weigl Elektronik & Mediaprojekte + +OUI:70B3D5F68* + ID_OUI_FROM_DATABASE=AL ZAJEL MODERN TELECOMM + +OUI:70B3D5DEC* + ID_OUI_FROM_DATABASE=Condev-Automation GmbH + +OUI:70B3D5564* + ID_OUI_FROM_DATABASE=christmann informationstechnik + medien GmbH & Co. KG + +OUI:70B3D5E07* + ID_OUI_FROM_DATABASE=Baader Planetarium GmbH + +OUI:70B3D528E* + ID_OUI_FROM_DATABASE=TEX COMPUTER SRL + +OUI:70B3D5655* + ID_OUI_FROM_DATABASE=AOT System GmbH + +OUI:70B3D5A2F* + ID_OUI_FROM_DATABASE=Botek Systems AB + +OUI:70B3D5B15* + ID_OUI_FROM_DATABASE=Eta Beta Srl + +OUI:70B3D5B2A* + ID_OUI_FROM_DATABASE=Myro Control, LLC + +OUI:70B3D5433* + ID_OUI_FROM_DATABASE=Flexsolution APS + +OUI:70B3D5077* + ID_OUI_FROM_DATABASE=InAccess Networks SA + +OUI:70B3D55E8* + ID_OUI_FROM_DATABASE=VITEC + +OUI:70B3D50FB* + ID_OUI_FROM_DATABASE=Cygnus LLC + +OUI:70B3D5DDD* + ID_OUI_FROM_DATABASE=BIO RAD LABORATORIES + +OUI:70B3D554E* + ID_OUI_FROM_DATABASE=RFL Electronics, Inc. + +OUI:70B3D5BBD* + ID_OUI_FROM_DATABASE=Providius Corp + +OUI:70B3D5C78* + ID_OUI_FROM_DATABASE=NETA Elektronik AS + +OUI:70B3D5BCA* + ID_OUI_FROM_DATABASE=Deymed Diagnostic + +OUI:70B3D5EE4* + ID_OUI_FROM_DATABASE=O-Net Automation Technology (Shenzhen)Limited + +OUI:70B3D55B5* + ID_OUI_FROM_DATABASE=Lehigh Electric Products Co + +OUI:70B3D5B8C* + ID_OUI_FROM_DATABASE=ePOINT Embedded Computing Limited + +OUI:70B3D540A* + ID_OUI_FROM_DATABASE=Monroe Electronics, Inc. + +OUI:70B3D5109* + ID_OUI_FROM_DATABASE=DITEST FAHRZEUGDIAGNOSE GMBH + +OUI:70B3D5F63* + ID_OUI_FROM_DATABASE=Ars Products + +OUI:70B3D50AA* + ID_OUI_FROM_DATABASE=Wanco Inc + +OUI:70B3D53ED* + ID_OUI_FROM_DATABASE=Ultra Electronics Sonar System Division + +OUI:001BC50C1* + ID_OUI_FROM_DATABASE=EREE Electronique + +OUI:001BC50C0* + ID_OUI_FROM_DATABASE=Digital Loggers, Inc. + +OUI:001BC50C7* + ID_OUI_FROM_DATABASE=WIZZILAB SAS + +OUI:001BC50B3* + ID_OUI_FROM_DATABASE=FSM Solutions Limited + +OUI:001BC50BD* + ID_OUI_FROM_DATABASE=Bridge Diagnostics, Inc. + +OUI:001BC50AD* + ID_OUI_FROM_DATABASE=Tierra Japan Co.,Ltd + +OUI:001BC50A9* + ID_OUI_FROM_DATABASE=Elektrometal SA + +OUI:001BC50B7* + ID_OUI_FROM_DATABASE=Autelis, LLC + +OUI:001BC50A3* + ID_OUI_FROM_DATABASE=P A Network Laboratory Co.,Ltd + +OUI:001BC50A1* + ID_OUI_FROM_DATABASE=Hangzhou Zhiping Technology Co., Ltd. + +OUI:001BC5097* + ID_OUI_FROM_DATABASE=Plexstar Inc. + +OUI:001BC5094* + ID_OUI_FROM_DATABASE=reelyActive + +OUI:001BC509B* + ID_OUI_FROM_DATABASE=YIK Corporation + +OUI:001BC509E* + ID_OUI_FROM_DATABASE=K+K Messtechnik GmbH + +OUI:001BC508D* + ID_OUI_FROM_DATABASE=EUREK SRL + +OUI:001BC5091* + ID_OUI_FROM_DATABASE=3green ApS + +OUI:001BC508A* + ID_OUI_FROM_DATABASE=Topicon + +OUI:001BC507C* + ID_OUI_FROM_DATABASE=head + +OUI:001BC5078* + ID_OUI_FROM_DATABASE=Donbass Soft Ltd and Co.KG + +OUI:001BC5082* + ID_OUI_FROM_DATABASE=TGS Geophysical Company (UK) Limited + +OUI:001BC5085* + ID_OUI_FROM_DATABASE=Oberon microsystems, Inc. + +OUI:001BC5086* + ID_OUI_FROM_DATABASE=CAST Group of Companies Inc. + +OUI:001BC5064* + ID_OUI_FROM_DATABASE=Enkora Oy Ltd + +OUI:001BC505E* + ID_OUI_FROM_DATABASE=Ecomed-Complex + +OUI:001BC506E* + ID_OUI_FROM_DATABASE=Two Dimensional Instruments, LLC + +OUI:001BC5068* + ID_OUI_FROM_DATABASE=HCS KABLOLAMA SISTEMLERI SAN. ve TIC.A.S. + +OUI:001BC5072* + ID_OUI_FROM_DATABASE=Ohio Semitronics, Inc. + +OUI:001BC505A* + ID_OUI_FROM_DATABASE=POSTEC DATA SYSTEMS + +OUI:001BC504A* + ID_OUI_FROM_DATABASE=Certis Technology International Pte Ltd + +OUI:001BC5054* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC5050* + ID_OUI_FROM_DATABASE=TeliSwitch Solutions + +OUI:001BC5046* + ID_OUI_FROM_DATABASE=Trans-European Research and Education Networking Association (TERENA) + +OUI:001BC5040* + ID_OUI_FROM_DATABASE=OOO Actidata + +OUI:001BC503B* + ID_OUI_FROM_DATABASE=Promixis, LLC + +OUI:001BC5034* + ID_OUI_FROM_DATABASE=InterCEL Pty Ltd + +OUI:001BC5031* + ID_OUI_FROM_DATABASE=ADIXEIN LIMITED + +OUI:001BC502A* + ID_OUI_FROM_DATABASE=Analytical Instrument Systems, Inc. + +OUI:001BC5027* + ID_OUI_FROM_DATABASE=CAMEA, spol. s r.o. + +OUI:001BC5023* + ID_OUI_FROM_DATABASE=MAGO di Della Mora Walter + +OUI:001BC501F* + ID_OUI_FROM_DATABASE=Saturn Solutions Ltd + +OUI:001BC5012* + ID_OUI_FROM_DATABASE=Tokyo Cosmos Electric, Inc. + +OUI:001BC500F* + ID_OUI_FROM_DATABASE=Simavita Pty Ltd + +OUI:001BC500B* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC500C* + ID_OUI_FROM_DATABASE=Quantum Technology Sciences, Inc. + +OUI:001BC5008* + ID_OUI_FROM_DATABASE=Dalaj Electro-Telecom + +OUI:001BC5005* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC5016* + ID_OUI_FROM_DATABASE=Energotechnica OOO NPP Ltd + +OUI:001BC5002* + ID_OUI_FROM_DATABASE=GORAMO - Janusz Gorecki + +OUI:001BC5019* + ID_OUI_FROM_DATABASE=Dunlop Systems & Components + +OUI:70B3D5D81* + ID_OUI_FROM_DATABASE=PDD Group Ltd + +OUI:70B3D5B7A* + ID_OUI_FROM_DATABASE=MAHLE + +OUI:70B3D5AFE* + ID_OUI_FROM_DATABASE=MESOTECHNIC + +OUI:70B3D5659* + ID_OUI_FROM_DATABASE=E2G srl + +OUI:70B3D54A1* + ID_OUI_FROM_DATABASE=Herholdt Controls srl + +OUI:70B3D5D1B* + ID_OUI_FROM_DATABASE=Grupo Epelsa S.L. + +OUI:70B3D5D9E* + ID_OUI_FROM_DATABASE=Grupo Epelsa S.L. + +OUI:70B3D5DB4* + ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd + +OUI:70B3D5781* + ID_OUI_FROM_DATABASE=Project Service S.a.s. + +OUI:70B3D5A72* + ID_OUI_FROM_DATABASE=Business Marketers Group, Inc. + +OUI:70B3D533B* + ID_OUI_FROM_DATABASE=Seal Shield, LLC + +OUI:70B3D5E49* + ID_OUI_FROM_DATABASE=Kendrion Mechatronics Center GmbH + +OUI:70B3D5214* + ID_OUI_FROM_DATABASE=signalparser + +OUI:70B3D53C6* + ID_OUI_FROM_DATABASE=ACD Elekronik GmbH + +OUI:70B3D5384* + ID_OUI_FROM_DATABASE=Sensohive Technologies + +OUI:70B3D5893* + ID_OUI_FROM_DATABASE=Cubitech + +OUI:70B3D509E* + ID_OUI_FROM_DATABASE=MobiPromo + +OUI:70B3D549E* + ID_OUI_FROM_DATABASE=CAPTEMP, Lda + +OUI:70B3D54B6* + ID_OUI_FROM_DATABASE=VEILUX INC. + +OUI:70B3D57CF* + ID_OUI_FROM_DATABASE=ORCA Technologies, LLC + +OUI:70B3D55A3* + ID_OUI_FROM_DATABASE=CT Company + +OUI:70B3D580A* + ID_OUI_FROM_DATABASE=SENSING LABS + +OUI:70B3D5E45* + ID_OUI_FROM_DATABASE=Momentum Data Systems + +OUI:70B3D5059* + ID_OUI_FROM_DATABASE=Pro-Digital Projetos Eletronicos Ltda + +OUI:70B3D5091* + ID_OUI_FROM_DATABASE=PROFITT Ltd + +OUI:70B3D5647* + ID_OUI_FROM_DATABASE=KZTA + +OUI:70B3D56DF* + ID_OUI_FROM_DATABASE=Mango DSP, Inc. + +OUI:70B3D5CBE* + ID_OUI_FROM_DATABASE=Ensura Solutions BV + +OUI:70B3D5CAC* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D58B9* + ID_OUI_FROM_DATABASE=Toptech Systems, Inc. + +OUI:70B3D574E* + ID_OUI_FROM_DATABASE=PushCorp, Inc. + +OUI:70B3D5FD2* + ID_OUI_FROM_DATABASE=DALIAN LEVEAR ELECTRIC CO., LTD + +OUI:70B3D5F08* + ID_OUI_FROM_DATABASE=Szabo Software & Engineering UK Ltd + +OUI:70B3D56A1* + ID_OUI_FROM_DATABASE=GLIAL TECHNOLOGY + +OUI:70B3D5955* + ID_OUI_FROM_DATABASE=Dynacard Co., Ltd. + +OUI:70B3D512B* + ID_OUI_FROM_DATABASE=RIC Electronics + +OUI:70B3D517E* + ID_OUI_FROM_DATABASE=OCULI VISION + +OUI:70B3D5046* + ID_OUI_FROM_DATABASE=Shenzhen Rihuida Electronics Co,. Ltd + +OUI:70B3D5268* + ID_OUI_FROM_DATABASE=Cardinal Scale Mfg Co + +OUI:70B3D5B7E* + ID_OUI_FROM_DATABASE=Elbit Systems of America - Fort Worth Operations + +OUI:70B3D5DB5* + ID_OUI_FROM_DATABASE=Xiamen Point Circle Technologh Co,ltd + +OUI:70B3D5429* + ID_OUI_FROM_DATABASE=Redco Audio Inc + +OUI:70B3D52E7* + ID_OUI_FROM_DATABASE=Atos spa + +OUI:70B3D5766* + ID_OUI_FROM_DATABASE=Tirasoft Nederland + +OUI:70B3D517D* + ID_OUI_FROM_DATABASE=Entech Electronics + +OUI:70B3D599A* + ID_OUI_FROM_DATABASE=KEVIC. inc, + +OUI:70B3D502A* + ID_OUI_FROM_DATABASE=BAE Systems Surface Ships Limited + +OUI:70B3D5DE2* + ID_OUI_FROM_DATABASE=ACD Elekronik GmbH + +OUI:70B3D52BB* + ID_OUI_FROM_DATABASE=Automation Networks & Solutions LLC + +OUI:70B3D5AB4* + ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH + +OUI:70B3D535A* + ID_OUI_FROM_DATABASE=Applied Radar, Inc. + +OUI:70B3D5042* + ID_OUI_FROM_DATABASE=Coveloz Technologies Inc. + +OUI:70B3D5C1B* + ID_OUI_FROM_DATABASE=Labinvent JSC + +OUI:70B3D5E2E* + ID_OUI_FROM_DATABASE=Merz s.r.o. + +OUI:70B3D5DE4* + ID_OUI_FROM_DATABASE=MAVILI ELEKTRONIK TIC. VE SAN. A.S. + +OUI:70B3D5712* + ID_OUI_FROM_DATABASE=APG Cash Drawer, LLC + +OUI:70B3D5580* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5776* + ID_OUI_FROM_DATABASE=Power Ltd. + +OUI:70B3D5B72* + ID_OUI_FROM_DATABASE=UB330.net d.o.o. + +OUI:70B3D57D9* + ID_OUI_FROM_DATABASE=ATOM GIKEN Co.,Ltd. + +OUI:70B3D57DE* + ID_OUI_FROM_DATABASE=Telaeris, Inc. + +OUI:70B3D5A10* + ID_OUI_FROM_DATABASE=w-tec AG + +OUI:70B3D513E* + ID_OUI_FROM_DATABASE=Stara S/A Indústria de Implementos Agrícolas + +OUI:70B3D50BA* + ID_OUI_FROM_DATABASE=Ayre Acoustics, Inc. + +OUI:70B3D5AAE* + ID_OUI_FROM_DATABASE=Nuviz Oy + +OUI:70B3D5881* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D51FE* + ID_OUI_FROM_DATABASE=MobiPromo + +OUI:70B3D5F0B* + ID_OUI_FROM_DATABASE=RF Industries + +OUI:70B3D5217* + ID_OUI_FROM_DATABASE=Tecnint HTE SRL + +OUI:70B3D55E6* + ID_OUI_FROM_DATABASE=Mechatronics Systems Private Limited + +OUI:70B3D588B* + ID_OUI_FROM_DATABASE=WUHAN EASYLINKIN TECHNOLOGY co.,LTD + +OUI:70B3D549F* + ID_OUI_FROM_DATABASE=B.P.A. SRL + +OUI:70B3D5C91* + ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG + +OUI:70B3D5986* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5FF3* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D57CE* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5F4F* + ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. + +OUI:70B3D54E9* + ID_OUI_FROM_DATABASE=ADETEC SAS + +OUI:70B3D509D* + ID_OUI_FROM_DATABASE=P&S GmbH + +OUI:70B3D5FB0* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:70B3D5885* + ID_OUI_FROM_DATABASE=QuirkLogic + +OUI:70B3D5274* + ID_OUI_FROM_DATABASE=Stercom Power Solutions GmbH + +OUI:70B3D5615* + ID_OUI_FROM_DATABASE=JSC OTZVUK + +OUI:70B3D5889* + ID_OUI_FROM_DATABASE=Innovative Circuit Technology + +OUI:70B3D558D* + ID_OUI_FROM_DATABASE=DORLET SAU + +OUI:70B3D5FAA* + ID_OUI_FROM_DATABASE=LogiM GmbH Software und Entwicklung + +OUI:70B3D5854* + ID_OUI_FROM_DATABASE=Adimec Advanced Image Systems + +OUI:70B3D5FB3* + ID_OUI_FROM_DATABASE=3PS Inc + +OUI:70B3D5CAA* + ID_OUI_FROM_DATABASE=Bel Power Solutions GmbH + +OUI:70B3D5B9E* + ID_OUI_FROM_DATABASE=POLSYSTEM SI SP. Z O.O., S.K.A. + +OUI:70B3D54B0* + ID_OUI_FROM_DATABASE=Tecogen Inc. + +OUI:70B3D54A7* + ID_OUI_FROM_DATABASE=aelettronica group srl + +OUI:70B3D5238* + ID_OUI_FROM_DATABASE=Arete Associates + +OUI:70B3D59B6* + ID_OUI_FROM_DATABASE=Intercomp S.p.A. + +OUI:70B3D510C* + ID_OUI_FROM_DATABASE=Vocality International Ltd + +OUI:70B3D5B7D* + ID_OUI_FROM_DATABASE=LOGIX ITS Inc + +OUI:70B3D5307* + ID_OUI_FROM_DATABASE=Energi innovation Aps + +OUI:70B3D59FA* + ID_OUI_FROM_DATABASE=Ideas srl + +OUI:70B3D5649* + ID_OUI_FROM_DATABASE=swissled technologies AG + +OUI:70B3D5C0E* + ID_OUI_FROM_DATABASE=SYSDEV Srl + +OUI:70B3D54C7* + ID_OUI_FROM_DATABASE=SOLVERIS sp. z o.o. + +OUI:70B3D57A4* + ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC + +OUI:70B3D5494* + ID_OUI_FROM_DATABASE=Schildknecht AG + +OUI:70B3D5BF2* + ID_OUI_FROM_DATABASE=TWIN DEVELOPMENT + +OUI:70B3D5901* + ID_OUI_FROM_DATABASE=ATS-CONVERS + +OUI:70B3D5654* + ID_OUI_FROM_DATABASE=EMAC, Inc. + +OUI:70B3D5F6E* + ID_OUI_FROM_DATABASE=Streambox Inc + +OUI:70B3D5D5B* + ID_OUI_FROM_DATABASE=WyreStorm Technologies Ltd + +OUI:70B3D56CD* + ID_OUI_FROM_DATABASE=NORTHBOUND NETWORKS PTY. LTD. + +OUI:70B3D5B7C* + ID_OUI_FROM_DATABASE=Electronic Navigation Ltd + +OUI:70B3D5C92* + ID_OUI_FROM_DATABASE=Unitro Fleischmann + +OUI:70B3D58A0* + ID_OUI_FROM_DATABASE=DM RADIOCOM + +OUI:70B3D5081* + ID_OUI_FROM_DATABASE=IST Technologies (SHENZHEN) Limited + +OUI:70B3D5C77* + ID_OUI_FROM_DATABASE=Yönnet Akıllı Bina ve Otomasyon Sistemleri + +OUI:70B3D5D74* + ID_OUI_FROM_DATABASE=Sandia National Laboratories + +OUI:70B3D5FCF* + ID_OUI_FROM_DATABASE=Acc+Ess Ltd + +OUI:70B3D53C0* + ID_OUI_FROM_DATABASE=DK-Technologies A/S + +OUI:70B3D52F6* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5364* + ID_OUI_FROM_DATABASE=ADAMCZEWSKI elektronische Messtechnik GmbH + +OUI:70B3D548E* + ID_OUI_FROM_DATABASE=Allim System Co,.Ltd. + +OUI:70B3D59ED* + ID_OUI_FROM_DATABASE=Benchmark Electronics BV + +OUI:70B3D5774* + ID_OUI_FROM_DATABASE=Micram Instruments Ltd + +OUI:70B3D576A* + ID_OUI_FROM_DATABASE=Swiftnet SOC Ltd + +OUI:70B3D5D7E* + ID_OUI_FROM_DATABASE=Triax A/S + +OUI:70B3D5061* + ID_OUI_FROM_DATABASE=IntelliDesign Pty Ltd + +OUI:70B3D5383* + ID_OUI_FROM_DATABASE=LPA Excil Electronics + +OUI:70B3D59F6* + ID_OUI_FROM_DATABASE=Edgeware AB + +OUI:70B3D5504* + ID_OUI_FROM_DATABASE=Xsight Systems Ltd. + +OUI:70B3D52CE* + ID_OUI_FROM_DATABASE=KDT + +OUI:70B3D5C07* + ID_OUI_FROM_DATABASE=ARECO + +OUI:70B3D5DCA* + ID_OUI_FROM_DATABASE=DSan Corporation + +OUI:70B3D525B* + ID_OUI_FROM_DATABASE=GID Industrial + +OUI:70B3D5313* + ID_OUI_FROM_DATABASE=DIEHL Controls + +OUI:70B3D5278* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5645* + ID_OUI_FROM_DATABASE=Project Decibel, Inc. + +OUI:70B3D5377* + ID_OUI_FROM_DATABASE=Monnit Corporation + +OUI:70B3D5A12* + ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S.L. + +OUI:70B3D5B34* + ID_OUI_FROM_DATABASE=Medtronic + +OUI:70B3D5303* + ID_OUI_FROM_DATABASE=Fuchu Giken, Inc. + +OUI:70B3D56F9* + ID_OUI_FROM_DATABASE=ENVItech s.r.o. + +OUI:70B3D5100* + ID_OUI_FROM_DATABASE=Gupsy GmbH + +OUI:70B3D5C89* + ID_OUI_FROM_DATABASE=ARD + +OUI:70B3D52E0* + ID_OUI_FROM_DATABASE=Peter Huber + +OUI:70B3D5088* + ID_OUI_FROM_DATABASE=OptiScan Biomedical Corp. + +OUI:70B3D5226* + ID_OUI_FROM_DATABASE=Yaviar + +OUI:70B3D5D57* + ID_OUI_FROM_DATABASE=TRIUMPH BOARD a.s. + +OUI:70B3D54DB* + ID_OUI_FROM_DATABASE=Temperature@lert + +OUI:70B3D5343* + ID_OUI_FROM_DATABASE=Elektro-System s.c. + +OUI:70B3D59F2* + ID_OUI_FROM_DATABASE=Acorde Technologies + +OUI:70B3D5E3B* + ID_OUI_FROM_DATABASE=ComNav Technology Ltd. + +OUI:70B3D5339* + ID_OUI_FROM_DATABASE=Sierra Nevada Corporation + +OUI:70B3D5703* + ID_OUI_FROM_DATABASE=StromIdee GmbH + +OUI:70B3D5E6D* + ID_OUI_FROM_DATABASE=Domus S.C. + +OUI:70B3D5F00* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D57C3* + ID_OUI_FROM_DATABASE=Flexim Security Oy + +OUI:70B3D53E4* + ID_OUI_FROM_DATABASE=Neptec Technologies Corp. + +OUI:70B3D5595* + ID_OUI_FROM_DATABASE=PLR Prueftechnik Linke und Ruehe GmbH + +OUI:70B3D5E36* + ID_OUI_FROM_DATABASE=Guidance Navigation Limited + +OUI:70B3D5B47* + ID_OUI_FROM_DATABASE=DSIT Solutions LTD + +OUI:70B3D52F1* + ID_OUI_FROM_DATABASE=Inspike S.R.L. + +OUI:70B3D5AFB* + ID_OUI_FROM_DATABASE=Shanghai Tianhe Automation Instrumentation Co., Ltd. + +OUI:70B3D5AD5* + ID_OUI_FROM_DATABASE=Birdland Audio + +OUI:70B3D5F93* + ID_OUI_FROM_DATABASE=Hella Gutmann Solutions GmbH + +OUI:70B3D5F01* + ID_OUI_FROM_DATABASE=Software Systems Plus + +OUI:70B3D53E2* + ID_OUI_FROM_DATABASE=AVI Pty Ltd + +OUI:70B3D5C2A* + ID_OUI_FROM_DATABASE=Array Telepresence + +OUI:70B3D5F8E* + ID_OUI_FROM_DATABASE=Isabellenhütte Heusler Gmbh &Co KG + +OUI:70B3D5FA2* + ID_OUI_FROM_DATABASE=Sarokal Test Systems Oy + +OUI:70B3D5134* + ID_OUI_FROM_DATABASE=Conjing Networks Inc. + +OUI:70B3D5208* + ID_OUI_FROM_DATABASE=DSP DESIGN LTD + +OUI:70B3D5870* + ID_OUI_FROM_DATABASE=bentrup Industriesteuerungen + +OUI:70B3D54D8* + ID_OUI_FROM_DATABASE=Versilis Inc. + +OUI:70B3D5A26* + ID_OUI_FROM_DATABASE=Hear Gear, Inc. + +OUI:70B3D5C87* + ID_OUI_FROM_DATABASE=Siemens AG + +OUI:70B3D57AE* + ID_OUI_FROM_DATABASE=Exi Flow Measurement Ltd + +OUI:70B3D54DF* + ID_OUI_FROM_DATABASE=Nidec Avtron Automation Corp + +OUI:70B3D54AD* + ID_OUI_FROM_DATABASE=GACI + +OUI:70B3D5DF9* + ID_OUI_FROM_DATABASE=Korea Plant Maintenance + +OUI:70B3D5BAE* + ID_OUI_FROM_DATABASE=WARECUBE,INC + +OUI:70B3D5119* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5C85* + ID_OUI_FROM_DATABASE=Solid State Disks Ltd + +OUI:70B3D5A44* + ID_OUI_FROM_DATABASE=FSR Inc + +OUI:70B3D5347* + ID_OUI_FROM_DATABASE=OAS Sweden AB + +OUI:70B3D5361* + ID_OUI_FROM_DATABASE=Parent Power + +OUI:70B3D5FA4* + ID_OUI_FROM_DATABASE=Energybox Limited + +OUI:70B3D5A2A* + ID_OUI_FROM_DATABASE=Redwood Systems + +OUI:70B3D573E* + ID_OUI_FROM_DATABASE=Trident RFID Pty Ltd + +OUI:70B3D565D* + ID_OUI_FROM_DATABASE=GEGA ELECTRONIQUE + +OUI:70B3D59BD* + ID_OUI_FROM_DATABASE=Signal Processing Devices Sweden AB + +OUI:70B3D5AA7* + ID_OUI_FROM_DATABASE=ATEME + +OUI:70B3D520E* + ID_OUI_FROM_DATABASE=Amrehn & Partner EDV-Service GmbH + +OUI:70B3D5E70* + ID_OUI_FROM_DATABASE=DISK Multimedia s.r.o. + +OUI:70B3D5EAB* + ID_OUI_FROM_DATABASE=APEN GROUP SpA (VAT IT08767740155) + +OUI:70B3D579E* + ID_OUI_FROM_DATABASE=CW2. Gmbh & Co. KG + +OUI:70B3D5EB1* + ID_OUI_FROM_DATABASE=CP contech electronic GmbH + +OUI:70B3D5D97* + ID_OUI_FROM_DATABASE=BRS Sistemas Eletronicos + +OUI:70B3D563A* + ID_OUI_FROM_DATABASE=DAVE SRL + +OUI:70B3D5C9B* + ID_OUI_FROM_DATABASE=Tieto Sweden AB + +OUI:70B3D54F0* + ID_OUI_FROM_DATABASE=Li Seng Technology Ltd., + +OUI:70B3D5F92* + ID_OUI_FROM_DATABASE=TechOne + +OUI:70B3D5D1F* + ID_OUI_FROM_DATABASE=Embsec AB + +OUI:70B3D5796* + ID_OUI_FROM_DATABASE=GAMPT mbH + +OUI:70B3D50A5* + ID_OUI_FROM_DATABASE=FUELCELLPOWER + +OUI:70B3D5AB9* + ID_OUI_FROM_DATABASE=Dynamic Controls + +OUI:70B3D5C25* + ID_OUI_FROM_DATABASE=speedsignal GmbH + +OUI:70B3D56E4* + ID_OUI_FROM_DATABASE=Institute of Power Engineering, Gdansk Division + +OUI:70B3D5D46* + ID_OUI_FROM_DATABASE=Contineo s.r.o. + +OUI:70B3D5665* + ID_OUI_FROM_DATABASE=CertUsus GmbH + +OUI:70B3D52CF* + ID_OUI_FROM_DATABASE=MB Connect Line GmbH + +OUI:70B3D5A50* + ID_OUI_FROM_DATABASE=LECIP CORPORATION + +OUI:70B3D5A4B* + ID_OUI_FROM_DATABASE=McKay Brothers LLC + +OUI:70B3D55E4* + ID_OUI_FROM_DATABASE=DSP DESIGN + +OUI:70B3D5DF0* + ID_OUI_FROM_DATABASE=astozi consulting Tomasz Zieba + +OUI:70B3D5CF3* + ID_OUI_FROM_DATABASE=Mesh Motion Inc + +OUI:70B3D50B3* + ID_OUI_FROM_DATABASE=Reonix Automation + +OUI:70B3D534A* + ID_OUI_FROM_DATABASE=PAVO TASARIM ÜRETİM TİC A.Ş. + +OUI:70B3D56E5* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D58F5* + ID_OUI_FROM_DATABASE=Stmovic + +OUI:70B3D5ABF* + ID_OUI_FROM_DATABASE=AGR International + +OUI:70B3D5755* + ID_OUI_FROM_DATABASE=LandmarkTech Systems Technology Co.,Ltd. + +OUI:70B3D5B3C* + ID_OUI_FROM_DATABASE=DORLET SAU + +OUI:70B3D557B* + ID_OUI_FROM_DATABASE=ELAMAKATO GmbH + +OUI:70B3D5A57* + ID_OUI_FROM_DATABASE=PCSC + +OUI:70B3D58E1* + ID_OUI_FROM_DATABASE=WoKa-Elektronik GmbH + +OUI:70B3D5BA4* + ID_OUI_FROM_DATABASE=EIWA GIKEN INC. + +OUI:70B3D5F8C* + ID_OUI_FROM_DATABASE=EUROPEAN ADVANCED TECHNOLOGIES + +OUI:70B3D585C* + ID_OUI_FROM_DATABASE=Robot Pub Group + +OUI:70B3D5AFA* + ID_OUI_FROM_DATABASE=Power Security Systems Ltd. + +OUI:70B3D577C* + ID_OUI_FROM_DATABASE=HUSTY M.Styczen J.Hupert Sp.J. + +OUI:70B3D5CDE* + ID_OUI_FROM_DATABASE=Multipure International + +OUI:70B3D5F2B* + ID_OUI_FROM_DATABASE=SENSYS GmbH + +OUI:70B3D5731* + ID_OUI_FROM_DATABASE=Phoniro Systems AB + +OUI:70B3D5941* + ID_OUI_FROM_DATABASE=Triax A/S + +OUI:70B3D536D* + ID_OUI_FROM_DATABASE=Cyberteam Sp z o o + +OUI:70B3D5B82* + ID_OUI_FROM_DATABASE=Lookout Portable Security + +OUI:70B3D5220* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5D0C* + ID_OUI_FROM_DATABASE=Connor Winfield LTD + +OUI:70B3D5CB7* + ID_OUI_FROM_DATABASE=HKC Limited + +OUI:70B3D53E1* + ID_OUI_FROM_DATABASE=Barnstormer Softworks + +OUI:70B3D596F* + ID_OUI_FROM_DATABASE=4CAM GmbH + +OUI:70B3D58CE* + ID_OUI_FROM_DATABASE=CORES Corporation + +OUI:70B3D523C* + ID_OUI_FROM_DATABASE=Quasonix, LLC + +OUI:70B3D579A* + ID_OUI_FROM_DATABASE=Innerspec Technologies Inc. + +OUI:70B3D551D* + ID_OUI_FROM_DATABASE=Tecnint HTE SRL + +OUI:70B3D561F* + ID_OUI_FROM_DATABASE=Labotect Labor-Technik-Göttingen GmbH + +OUI:70B3D54C1* + ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S. L. + +OUI:70B3D5A0E* + ID_OUI_FROM_DATABASE=Vetaphone A/S + +OUI:70B3D55AA* + ID_OUI_FROM_DATABASE=Chugoku Electric Manufacturing Co.,Inc + +OUI:70B3D52D4* + ID_OUI_FROM_DATABASE=CT Company + +OUI:70B3D5C96* + ID_OUI_FROM_DATABASE=UNI DIMENXI SDN BHD + +OUI:70B3D5E74* + ID_OUI_FROM_DATABASE=Exfrontier Co., Ltd. + +OUI:70B3D5FD3* + ID_OUI_FROM_DATABASE=AKIS technologies + +OUI:70B3D5E53* + ID_OUI_FROM_DATABASE=MI INC. + +OUI:70B3D52F0* + ID_OUI_FROM_DATABASE=Clock-O-Matic + +OUI:70B3D57A9* + ID_OUI_FROM_DATABASE=adidas AG + +OUI:70B3D5AC8* + ID_OUI_FROM_DATABASE=Heartland.Data Inc. + +OUI:70B3D5F2A* + ID_OUI_FROM_DATABASE=WIBOND Informationssysteme GmbH + +OUI:70B3D52A5* + ID_OUI_FROM_DATABASE=Taitotekniikka + +OUI:70B3D519C* + ID_OUI_FROM_DATABASE=Kubu, Inc. + +OUI:70B3D503B* + ID_OUI_FROM_DATABASE=SSL - Electrical Aerospace Ground Equipment Section + +OUI:70B3D5E9A* + ID_OUI_FROM_DATABASE=Meta Computing Services, Corp + +OUI:70B3D5E26* + ID_OUI_FROM_DATABASE=FEITIAN CO.,LTD. + +OUI:70B3D5E4C* + ID_OUI_FROM_DATABASE=IAI-Israel Aerospace Industries MBT + +OUI:70B3D5A40* + ID_OUI_FROM_DATABASE=STRACK LIFT AUTOMATION GmbH + +OUI:70B3D5016* + ID_OUI_FROM_DATABASE=Guardian Controls International Ltd + +OUI:70B3D5A7A* + ID_OUI_FROM_DATABASE=Fluid Management Technology + +OUI:70B3D5C6A* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5819* + ID_OUI_FROM_DATABASE=«Intellect module» LLC + +OUI:70B3D5EF3* + ID_OUI_FROM_DATABASE=octoScope + +OUI:70B3D59F4* + ID_OUI_FROM_DATABASE=Tband srl + +OUI:70B3D535C* + ID_OUI_FROM_DATABASE=ACS electronics srl + +OUI:70B3D528F* + ID_OUI_FROM_DATABASE=Overline Systems + +OUI:70B3D5F38* + ID_OUI_FROM_DATABASE=Scanvaegt Nordic A/S + +OUI:001BC50C5* + ID_OUI_FROM_DATABASE=Gill Instruments Ltd + +OUI:70B3D5994* + ID_OUI_FROM_DATABASE=KeFF Networks + +OUI:70B3D5B3D* + ID_OUI_FROM_DATABASE=Inras GmbH + +OUI:001BC50BB* + ID_OUI_FROM_DATABASE=Triax A/S + +OUI:001BC50B1* + ID_OUI_FROM_DATABASE=Roslen Eco-Networking Products + +OUI:001BC50A7* + ID_OUI_FROM_DATABASE=L.G.L. Electronics S.p.a. + +OUI:001BC509C* + ID_OUI_FROM_DATABASE=S.I.C.E.S. srl + +OUI:001BC5092* + ID_OUI_FROM_DATABASE=Arnouse Digital Devices, Corp. + +OUI:001BC5088* + ID_OUI_FROM_DATABASE=UAB Kitron + +OUI:001BC5080* + ID_OUI_FROM_DATABASE=LUMINO GmbH + +OUI:001BC5076* + ID_OUI_FROM_DATABASE=PLAiR Media, Inc + +OUI:001BC506C* + ID_OUI_FROM_DATABASE=Luxcon System Limited + +OUI:001BC505C* + ID_OUI_FROM_DATABASE=Suretrak Global Pty Ltd + +OUI:001BC5062* + ID_OUI_FROM_DATABASE=Sulaon Oy + +OUI:001BC5052* + ID_OUI_FROM_DATABASE=Engineering Center ENERGOSERVICE + +OUI:001BC5058* + ID_OUI_FROM_DATABASE=optiMEAS GmbH + +OUI:001BC5048* + ID_OUI_FROM_DATABASE=XPossible Technologies Pte Ltd + +OUI:001BC504E* + ID_OUI_FROM_DATABASE=Mitsubishi Electric India PVT. LTD + +OUI:001BC503E* + ID_OUI_FROM_DATABASE=Daylight Solutions, Inc + +OUI:001BC5044* + ID_OUI_FROM_DATABASE=ZAO "RADIUS Avtomatika" + +OUI:001BC5039* + ID_OUI_FROM_DATABASE=EURESYS S.A. + +OUI:001BC502F* + ID_OUI_FROM_DATABASE=Fibrain Co. Ltd. + +OUI:001BC5025* + ID_OUI_FROM_DATABASE=andersen lighting GmbH + +OUI:001BC5020* + ID_OUI_FROM_DATABASE=Momentum Data Systems + +OUI:001BC501D* + ID_OUI_FROM_DATABASE=Rose + Herleth GbR + +OUI:001BC5021* + ID_OUI_FROM_DATABASE=Openpeak, Inc + +OUI:001BC5010* + ID_OUI_FROM_DATABASE=Softel SA de CV + +OUI:001BC5006* + ID_OUI_FROM_DATABASE=TRIAX-HIRSCHMANN Multi-Media GmbH + +OUI:001BC501A* + ID_OUI_FROM_DATABASE=ABA ELECTRONICS TECHNOLOGY CO.,LTD + +OUI:70B3D576E* + ID_OUI_FROM_DATABASE=Grupo Epelsa S.L. + +OUI:70B3D58F3* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5C7F* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D55ED* + ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG + +OUI:70B3D5AEA* + ID_OUI_FROM_DATABASE=BBR Verkehrstechnik GmbH + +OUI:70B3D5EEE* + ID_OUI_FROM_DATABASE=SOCIEDAD IBERICA DE CONSTRUCCIONES ELECTRICAS, S.A. (SICE) + +OUI:70B3D5794* + ID_OUI_FROM_DATABASE=Shadin Avionics + +OUI:70B3D52EF* + ID_OUI_FROM_DATABASE=IEM SA + +OUI:70B3D5B6D* + ID_OUI_FROM_DATABASE=Movis + +OUI:70B3D5325* + ID_OUI_FROM_DATABASE=BlueMark Innovations BV + +OUI:70B3D58B2* + ID_OUI_FROM_DATABASE=NPF Modem, LLC + +OUI:70B3D53CC* + ID_OUI_FROM_DATABASE=TerOpta Ltd + +OUI:70B3D55D1* + ID_OUI_FROM_DATABASE=Software Motor Corp + +OUI:70B3D5A2E* + ID_OUI_FROM_DATABASE=Kokam Co., Ltd + +OUI:70B3D58A6* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5FB5* + ID_OUI_FROM_DATABASE=Orange Tree Technologies Ltd + +OUI:70B3D5E02* + ID_OUI_FROM_DATABASE=YEHL & JORDAN LLC + +OUI:70B3D5F56* + ID_OUI_FROM_DATABASE=VirtualHere Pty. Ltd. + +OUI:70B3D58BE* + ID_OUI_FROM_DATABASE=Connoiseur Electronics Private Limited + +OUI:70B3D526B* + ID_OUI_FROM_DATABASE=Sorama BV + +OUI:70B3D52FA* + ID_OUI_FROM_DATABASE=Toray Medical Co.,Ltd + +OUI:70B3D5975* + ID_OUI_FROM_DATABASE=Coester Automação Ltda + +OUI:70B3D513C* + ID_OUI_FROM_DATABASE=Detec Systems Ltd + +OUI:70B3D5455* + ID_OUI_FROM_DATABASE=Heartlandmicropayments + +OUI:70B3D5070* + ID_OUI_FROM_DATABASE=Lumiplan Duhamel + +OUI:70B3D58D8* + ID_OUI_FROM_DATABASE=VNG Corporation + +OUI:70B3D5392* + ID_OUI_FROM_DATABASE=Contec DTx + +OUI:70B3D514D* + ID_OUI_FROM_DATABASE=2-Observe + +OUI:70B3D5A9F* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D59CB* + ID_OUI_FROM_DATABASE=Alligator Communications + +OUI:70B3D5B67* + ID_OUI_FROM_DATABASE=RedWave Labs Ltd + +OUI:70B3D5B93* + ID_OUI_FROM_DATABASE=INTERNET PROTOCOLO LOGICA SL + +OUI:70B3D5B89* + ID_OUI_FROM_DATABASE=IDA + +OUI:70B3D57E2* + ID_OUI_FROM_DATABASE=Depro Électronique inc + +OUI:70B3D5772* + ID_OUI_FROM_DATABASE=enModus + +OUI:70B3D53C5* + ID_OUI_FROM_DATABASE=P4Q ELECTRONICS, S.L. + +OUI:70B3D5FE2* + ID_OUI_FROM_DATABASE=Galileo Tıp Teknolojileri San. ve Tic. A.S. + +OUI:70B3D5C24* + ID_OUI_FROM_DATABASE=Elbit Systems of America - Fort Worth Operations + +OUI:70B3D5AC6* + ID_OUI_FROM_DATABASE=SMTC Corporation + +OUI:70B3D5028* + ID_OUI_FROM_DATABASE=AT-Automation Technology GmbH + +OUI:70B3D50B9* + ID_OUI_FROM_DATABASE=Easy Digital Concept + +OUI:70B3D5CF2* + ID_OUI_FROM_DATABASE=tinnos + +OUI:70B3D59E0* + ID_OUI_FROM_DATABASE=ES Industrial Systems Co., Ltd. + +OUI:70B3D560F* + ID_OUI_FROM_DATABASE=Tanaka Information System, LLC. + +OUI:70B3D50FA* + ID_OUI_FROM_DATABASE=InsideRF Co., Ltd. + +OUI:70B3D531E* + ID_OUI_FROM_DATABASE=GILLAM-FEI S.A. + +OUI:70B3D54D5* + ID_OUI_FROM_DATABASE=Morgan Rekofa GmbH + +OUI:70B3D58C5* + ID_OUI_FROM_DATABASE=HMicro Inc + +OUI:70B3D55FF* + ID_OUI_FROM_DATABASE=Vaisala Oyj + +OUI:70B3D58EF* + ID_OUI_FROM_DATABASE=Beeper Communications Ltd. + +OUI:70B3D5277* + ID_OUI_FROM_DATABASE=Voltaware Limited + +OUI:70B3D5179* + ID_OUI_FROM_DATABASE=ALTRAN UK + +OUI:70B3D574F* + ID_OUI_FROM_DATABASE=United States Technologies Inc. + +OUI:70B3D5804* + ID_OUI_FROM_DATABASE=PMT Corporation + +OUI:70B3D5D2B* + ID_OUI_FROM_DATABASE=StreamPlay Oy Ltd + +OUI:70B3D539C* + ID_OUI_FROM_DATABASE=GD Mission Systems + +OUI:70B3D5840* + ID_OUI_FROM_DATABASE=xm + +OUI:70B3D54FE* + ID_OUI_FROM_DATABASE=WiTagg, Inc + +OUI:70B3D51FD* + ID_OUI_FROM_DATABASE=BRS Sistemas Eletrônicos + +OUI:70B3D5631* + ID_OUI_FROM_DATABASE=SENSO2ME + +OUI:70B3D5396* + ID_OUI_FROM_DATABASE=CTG sp. z o. o. + +OUI:70B3D562B* + ID_OUI_FROM_DATABASE=Silicann Systems GmbH + +OUI:70B3D56B5* + ID_OUI_FROM_DATABASE=ART SPA + +OUI:70B3D5E7C* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D53D9* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D535F* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5FFF* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D533E* + ID_OUI_FROM_DATABASE=Dynamic Connect (Suzhou) Hi-Tech Electronic Co.,Ltd. + +OUI:70B3D5180* + ID_OUI_FROM_DATABASE=LHA Systems (Pty) Ltd + +OUI:70B3D5ABE* + ID_OUI_FROM_DATABASE=MART NETWORK SOLUTIONS LTD + +OUI:70B3D5932* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:70B3D50C4* + ID_OUI_FROM_DATABASE=TIAMA + +OUI:70B3D58CF* + ID_OUI_FROM_DATABASE=Dainichi Denshi Co.,LTD + +OUI:70B3D5750* + ID_OUI_FROM_DATABASE=Neurio Technology Inc. + +OUI:70B3D5FC9* + ID_OUI_FROM_DATABASE=Shanghai EICT Global Service Co., Ltd + +OUI:70B3D5147* + ID_OUI_FROM_DATABASE=ROMO Wind A/S + +OUI:70B3D575B* + ID_OUI_FROM_DATABASE=Netool LLC + +OUI:70B3D578C* + ID_OUI_FROM_DATABASE=Survalent Technology Corporation + +OUI:70B3D537D* + ID_OUI_FROM_DATABASE=The DX Shop Limited + +OUI:70B3D5696* + ID_OUI_FROM_DATABASE=Open Grow + +OUI:70B3D5FCA* + ID_OUI_FROM_DATABASE=M2M Cybernetics Pvt Ltd + +OUI:70B3D502D* + ID_OUI_FROM_DATABASE=NEXTtec srl + +OUI:70B3D53D5* + ID_OUI_FROM_DATABASE=oxynet Solutions + +OUI:70B3D5D11* + ID_OUI_FROM_DATABASE=EREE Electronique + +OUI:70B3D51B5* + ID_OUI_FROM_DATABASE=StarBridge, Inc. + +OUI:70B3D55CD* + ID_OUI_FROM_DATABASE=MVT Video Technologies R + H Maedler GbR + +OUI:70B3D5AF7* + ID_OUI_FROM_DATABASE=DimoSystems BV + +OUI:70B3D59D2* + ID_OUI_FROM_DATABASE=ACS MOTION CONTROL + +OUI:70B3D5A5E* + ID_OUI_FROM_DATABASE=ConectaIP Tecnologia S.L. + +OUI:70B3D512C* + ID_OUI_FROM_DATABASE=CIELLE S.R.L. + +OUI:70B3D5486* + ID_OUI_FROM_DATABASE=ChongQing JianTao Technology Co., Ltd. + +OUI:70B3D512E* + ID_OUI_FROM_DATABASE=GreenFlux + +OUI:70B3D5B59* + ID_OUI_FROM_DATABASE=FutureTechnologyLaboratories INC. + +OUI:70B3D58B3* + ID_OUI_FROM_DATABASE=Firefly RFID Solutions + +OUI:70B3D5599* + ID_OUI_FROM_DATABASE=LECO Corporation + +OUI:70B3D5896* + ID_OUI_FROM_DATABASE=Shanghai Longpal Communication Equipment Co., Ltd. + +OUI:70B3D5692* + ID_OUI_FROM_DATABASE=HOSIN INDUSTRIAL LIMITED + +OUI:70B3D5AE7* + ID_OUI_FROM_DATABASE=E-T-A Elektrotechnische Apparate GmbH + +OUI:70B3D5400* + ID_OUI_FROM_DATABASE=Vtron Pty Ltd + +OUI:70B3D5E0F* + ID_OUI_FROM_DATABASE=Vtron Pty Ltd + +OUI:70B3D512F* + ID_OUI_FROM_DATABASE=DSP4YOU LTd + +OUI:70B3D59B1* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5CA4* + ID_OUI_FROM_DATABASE=Netemera Sp. z o.o. + +OUI:70B3D571B* + ID_OUI_FROM_DATABASE=elsys + +OUI:70B3D548F* + ID_OUI_FROM_DATABASE=Seiwa Giken + +OUI:70B3D5DDC* + ID_OUI_FROM_DATABASE=Syscom Instruments SA + +OUI:70B3D5C15* + ID_OUI_FROM_DATABASE=Sensobox GmbH + +OUI:70B3D5D2F* + ID_OUI_FROM_DATABASE=L.I.F.E. Corporation SA + +OUI:70B3D536A* + ID_OUI_FROM_DATABASE=Becton Dickinson + +OUI:70B3D566B* + ID_OUI_FROM_DATABASE=Innitive B.V. + +OUI:70B3D58F6* + ID_OUI_FROM_DATABASE=Dofuntech Co.,LTD. + +OUI:70B3D5A06* + ID_OUI_FROM_DATABASE=Kopis Mobile LLC + +OUI:70B3D5679* + ID_OUI_FROM_DATABASE=EMAC, Inc. + +OUI:001BC5042* + ID_OUI_FROM_DATABASE=ChamSys Ltd + +OUI:70B3D52F9* + ID_OUI_FROM_DATABASE=CONSOSPY + +OUI:70B3D5BB8* + ID_OUI_FROM_DATABASE=Al Kamel Systems S.L. + +OUI:70B3D5439* + ID_OUI_FROM_DATABASE=TriLED + +OUI:70B3D5E48* + ID_OUI_FROM_DATABASE=TDI. Co., LTD + +OUI:70B3D518D* + ID_OUI_FROM_DATABASE=Foro Tel + +OUI:70B3D5A95* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D5570* + ID_OUI_FROM_DATABASE=Bayern Engineering GmbH & Co. KG + +OUI:70B3D5524* + ID_OUI_FROM_DATABASE=Wuxi New Optical Communication Co.,Ltd. + +OUI:70B3D5DE0* + ID_OUI_FROM_DATABASE=eCozy GmbH + +OUI:70B3D5C26* + ID_OUI_FROM_DATABASE=Triple Play Communications + +OUI:70B3D591F* + ID_OUI_FROM_DATABASE=JSC InformInvestGroup + +OUI:70B3D53E9* + ID_OUI_FROM_DATABASE=APOLLO GIKEN Co.,Ltd. + +OUI:70B3D5773* + ID_OUI_FROM_DATABASE=Rugged Science + +OUI:70B3D57AA* + ID_OUI_FROM_DATABASE=Sadel S.p.A. + +OUI:70B3D56FB* + ID_OUI_FROM_DATABASE=Shachihata Inc. + +OUI:70B3D5D73* + ID_OUI_FROM_DATABASE=ERMINE Corporation + +OUI:70B3D5D3F* + ID_OUI_FROM_DATABASE=GLOBALCOM ENGINEERING SPA + +OUI:70B3D5DF7* + ID_OUI_FROM_DATABASE=Refecor Oy + +OUI:70B3D5152* + ID_OUI_FROM_DATABASE=Xped Corporation Pty Ltd + +OUI:70B3D559D* + ID_OUI_FROM_DATABASE=servicios de consultoria independiente S.L. + +OUI:70B3D5933* + ID_OUI_FROM_DATABASE=SARL S@TIS + +OUI:70B3D57F2* + ID_OUI_FROM_DATABASE=TCI + +OUI:70B3D5E4B* + ID_OUI_FROM_DATABASE=DELTA + +OUI:70B3D52EA* + ID_OUI_FROM_DATABASE=Schneider Electric Motion + +OUI:70B3D55FC* + ID_OUI_FROM_DATABASE=SURTEC + +OUI:70B3D5328* + ID_OUI_FROM_DATABASE=HIPODROMO DE AGUA CALIENTE SA CV + +OUI:70B3D5F85* + ID_OUI_FROM_DATABASE=Solystic + +OUI:70B3D5A04* + ID_OUI_FROM_DATABASE=Galea Electric S.L. + +OUI:70B3D56BE* + ID_OUI_FROM_DATABASE=VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD + +OUI:70B3D5A01* + ID_OUI_FROM_DATABASE=FeldTech GmbH + +OUI:70B3D57C1* + ID_OUI_FROM_DATABASE=Data Sciences International + +OUI:70B3D5903* + ID_OUI_FROM_DATABASE=Cymtec Ltd + +OUI:70B3D51A0* + ID_OUI_FROM_DATABASE=UFATECH LTD + +OUI:70B3D5EA4* + ID_OUI_FROM_DATABASE=Grupo Epelsa S.L. + +OUI:70B3D51AD* + ID_OUI_FROM_DATABASE=Techworld Industries Ltd + +OUI:70B3D5866* + ID_OUI_FROM_DATABASE=MEPS Realtime + +OUI:70B3D53D8* + ID_OUI_FROM_DATABASE=Abitsoftware, Ltd. + +OUI:70B3D5F4D* + ID_OUI_FROM_DATABASE=Honeywell International Inc. + +OUI:70B3D5230* + ID_OUI_FROM_DATABASE=CT Company + +OUI:70B3D5FCD* + ID_OUI_FROM_DATABASE=Engage Technologies + +OUI:70B3D507E* + ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic CO., LTD + +OUI:70B3D5664* + ID_OUI_FROM_DATABASE=Sankyo Intec co.,ltd + +OUI:70B3D54BA* + ID_OUI_FROM_DATABASE=Sinftech LLC + +OUI:70B3D53FF* + ID_OUI_FROM_DATABASE=Hydra Controls + +OUI:70B3D5470* + ID_OUI_FROM_DATABASE=KITRON UAB + +OUI:70B3D552B* + ID_OUI_FROM_DATABASE=GE Aviation Cheltenham + +OUI:70B3D587B* + ID_OUI_FROM_DATABASE=Liquid Instruments Pty Ltd + +OUI:70B3D55B0* + ID_OUI_FROM_DATABASE=Qxperts Italia S.r.l. + +OUI:70B3D5435* + ID_OUI_FROM_DATABASE=Wuhan Xingtuxinke ELectronic Co.,Ltd + +OUI:70B3D50CD* + ID_OUI_FROM_DATABASE=AML Oceanographic + +OUI:70B3D56E8* + ID_OUI_FROM_DATABASE=BluWireless Technology Ltd + +OUI:70B3D56B3* + ID_OUI_FROM_DATABASE=DuraComm Corporation + +OUI:70B3D5F36* + ID_OUI_FROM_DATABASE=dinosys + +OUI:70B3D5AC9* + ID_OUI_FROM_DATABASE=Trinity Solutions LLC + +OUI:70B3D50A6* + ID_OUI_FROM_DATABASE=PA CONSULTING SERVICES + +OUI:70B3D5B8B* + ID_OUI_FROM_DATABASE=Profound Medical Inc. + +OUI:70B3D51A5* + ID_OUI_FROM_DATABASE=METRONIC APARATURA KONTROLNO - POMIAROWA + +OUI:70B3D5A1C* + ID_OUI_FROM_DATABASE=MECA SYSTEM + +OUI:70B3D5AD6* + ID_OUI_FROM_DATABASE=Lemonade Lab Inc + +OUI:70B3D5D87* + ID_OUI_FROM_DATABASE=Zigen Corp + +OUI:70B3D5589* + ID_OUI_FROM_DATABASE=Cityntel OU + +OUI:70B3D582E* + ID_OUI_FROM_DATABASE=PlayAlive A/S + +OUI:70B3D500E* + ID_OUI_FROM_DATABASE=Magosys Systems LTD + +OUI:70B3D5C3C* + ID_OUI_FROM_DATABASE=PEEK TRAFFIC + +OUI:70B3D517F* + ID_OUI_FROM_DATABASE=MB Connect Line GmbH + +OUI:70B3D5FE8* + ID_OUI_FROM_DATABASE=PCME Ltd. + +OUI:70B3D5505* + ID_OUI_FROM_DATABASE=MC2-Technologies + +OUI:70B3D5F2D* + ID_OUI_FROM_DATABASE=ID Lock AS + +OUI:70B3D50DC* + ID_OUI_FROM_DATABASE=Talleres de Escoriaza + +OUI:70B3D5AF3* + ID_OUI_FROM_DATABASE=New Japan Radio Co., Ltd + +OUI:70B3D5500* + ID_OUI_FROM_DATABASE=Mistral Solutions Pvt. LTD + +OUI:70B3D527D* + ID_OUI_FROM_DATABASE=Telenor Connexion AB + +OUI:70B3D5B29* + ID_OUI_FROM_DATABASE=WiViCom Co., Ltd. + +OUI:70B3D5ED5* + ID_OUI_FROM_DATABASE=hangzhou battle link technology Co.,Ltd + +OUI:70B3D5E95* + ID_OUI_FROM_DATABASE=BroadSoft Inc + +OUI:70B3D565B* + ID_OUI_FROM_DATABASE=Roush + +OUI:70B3D5D7F* + ID_OUI_FROM_DATABASE=ConectaIP Tecnologia S.L. + +OUI:70B3D53D2* + ID_OUI_FROM_DATABASE=Imagine Inc. + +OUI:70B3D57A7* + ID_OUI_FROM_DATABASE=Symbicon Ltd + +OUI:70B3D5D9B* + ID_OUI_FROM_DATABASE=Russian Telecom Equipment Company + +OUI:70B3D5E4E* + ID_OUI_FROM_DATABASE=Midfin Systems + +OUI:70B3D548C* + ID_OUI_FROM_DATABASE=Integrated Systems Engineering, Inc. + +OUI:70B3D5FC6* + ID_OUI_FROM_DATABASE=Tecnint HTE SRL + +OUI:70B3D52BE* + ID_OUI_FROM_DATABASE=Coherent Logix, Inc. + +OUI:70B3D57F1* + ID_OUI_FROM_DATABASE=AeroVision Avionics, Inc. + +OUI:70B3D57B8* + ID_OUI_FROM_DATABASE=SerEnergy A/S + +OUI:70B3D5CCE* + ID_OUI_FROM_DATABASE=Proconex 2010 Inc. + +OUI:70B3D5300* + ID_OUI_FROM_DATABASE=Novo DR Ltd. + +OUI:70B3D5BD3* + ID_OUI_FROM_DATABASE=FOTONA D.D. + +OUI:70B3D5600* + ID_OUI_FROM_DATABASE=Stellwerk GmbH + +OUI:70B3D5421* + ID_OUI_FROM_DATABASE=North Star Bestech Co., + +OUI:70B3D5C55* + ID_OUI_FROM_DATABASE=Intelligent Energy Ltd + +OUI:70B3D5E28* + ID_OUI_FROM_DATABASE=iotec GmbH + +OUI:70B3D56F3* + ID_OUI_FROM_DATABASE=iungo + +OUI:70B3D5182* + ID_OUI_FROM_DATABASE=Kitron UAB + +OUI:70B3D5820* + ID_OUI_FROM_DATABASE=Becker Nachrichtentechnik GmbH + +OUI:70B3D5732* + ID_OUI_FROM_DATABASE=TOFWERK AG + +OUI:70B3D507D* + ID_OUI_FROM_DATABASE=PANORAMIC POWER + +OUI:70B3D5B9B* + ID_OUI_FROM_DATABASE=Elektronik Art + +OUI:70B3D5327* + ID_OUI_FROM_DATABASE=Seneco A/S + +OUI:70B3D508E* + ID_OUI_FROM_DATABASE=Beijing CONvision Technology Co.,Ltd + +OUI:70B3D5B8F* + ID_OUI_FROM_DATABASE=Assembly Contracts Ltd + +OUI:70B3D55C5* + ID_OUI_FROM_DATABASE=Haag-Streit AG + +OUI:70B3D5164* + ID_OUI_FROM_DATABASE=Tokyo Drawing Ltd. + +OUI:70B3D59F1* + ID_OUI_FROM_DATABASE=RFEL Ltd + +OUI:70B3D584A* + ID_OUI_FROM_DATABASE=MOG Laboratories Pty Ltd + +OUI:70B3D59F5* + ID_OUI_FROM_DATABASE=Vickers Electronics Ltd + +OUI:70B3D5FDE* + ID_OUI_FROM_DATABASE=AERONAUTICAL & GENERAL INSTRUMENTS LTD. + +OUI:70B3D5104* + ID_OUI_FROM_DATABASE=Plum sp. z o.o + +OUI:70B3D5C61* + ID_OUI_FROM_DATABASE=JC HUNTER TECHNOLOGIES + +OUI:70B3D5A5B* + ID_OUI_FROM_DATABASE=Christ Elektronik GmbH + +OUI:70B3D539A* + ID_OUI_FROM_DATABASE=Videotrend srl + +OUI:70B3D5CE7* + ID_OUI_FROM_DATABASE=June Automation Singapore Pte. Ltd. + +OUI:70B3D5EC1* + ID_OUI_FROM_DATABASE=Xafax Nederland bv + +OUI:70B3D5A6D* + ID_OUI_FROM_DATABASE=Metek Meteorologische Messtechnik GmbH + +OUI:70B3D53B8* + ID_OUI_FROM_DATABASE=nVideon, Inc. + +OUI:70B3D5610* + ID_OUI_FROM_DATABASE=POLVISION + +OUI:70B3D5ECE* + ID_OUI_FROM_DATABASE=COMM-connect A/S + +OUI:70B3D5EB2* + ID_OUI_FROM_DATABASE=Shooter Detection Systems + +OUI:70B3D5250* + ID_OUI_FROM_DATABASE=Datum Electronics Limited + +OUI:70B3D5FDA* + ID_OUI_FROM_DATABASE=ACD Elektronik GmbH + +OUI:70B3D56D9* + ID_OUI_FROM_DATABASE=VECTARE Inc + +OUI:70B3D5BDA* + ID_OUI_FROM_DATABASE=5-D Systems, Inc. + +OUI:70B3D5559* + ID_OUI_FROM_DATABASE=Eagle Mountain Technology + +OUI:70B3D5AEE* + ID_OUI_FROM_DATABASE=DiTEST Fahrzeugdiagnose GmbH + +OUI:70B3D5710* + ID_OUI_FROM_DATABASE=Guardian Controls International Ltd + +OUI:70B3D5A0B* + ID_OUI_FROM_DATABASE=ambiHome GmbH + +OUI:70B3D5204* + ID_OUI_FROM_DATABASE=TWC + +OUI:70B3D583B* + ID_OUI_FROM_DATABASE=Telefonix Incorporated + +OUI:70B3D5029* + ID_OUI_FROM_DATABASE=Marimo electronics Co.,Ltd. + +OUI:70B3D5010* + ID_OUI_FROM_DATABASE=Hanwa Electronic Ind.Co.,Ltd. + +OUI:70B3D586D* + ID_OUI_FROM_DATABASE=Census Digital Incorporated + +OUI:70B3D560B* + ID_OUI_FROM_DATABASE=Edgeware AB + +OUI:70B3D5ADD* + ID_OUI_FROM_DATABASE=GHL Systems Berhad + +OUI:70B3D56F6* + ID_OUI_FROM_DATABASE=Acco Brands Europe + +OUI:70B3D5D8F* + ID_OUI_FROM_DATABASE=Molu Technology Inc., LTD. + +OUI:70B3D56F2* + ID_OUI_FROM_DATABASE=P&C Micro's Pty Ltd + +OUI:70B3D543D* + ID_OUI_FROM_DATABASE=Veryx Technologies Private Limited + +OUI:70B3D5AB7* + ID_OUI_FROM_DATABASE=SIGLEAD INC + +OUI:70B3D5D47* + ID_OUI_FROM_DATABASE=YotaScope Technologies Co., Ltd. + +OUI:70B3D580F* + ID_OUI_FROM_DATABASE=Quickware Eng & Des LLC + +OUI:70B3D5F61* + ID_OUI_FROM_DATABASE=Power Diagnostic Service + +OUI:70B3D50F0* + ID_OUI_FROM_DATABASE=Avionica + +OUI:70B3D5E08* + ID_OUI_FROM_DATABASE=Olssen + +OUI:70B3D5FD1* + ID_OUI_FROM_DATABASE=RedRat Ltd + +OUI:70B3D5C8D* + ID_OUI_FROM_DATABASE=KST technology + +OUI:70B3D5044* + ID_OUI_FROM_DATABASE=Don Electronics Ltd + +OUI:70B3D55F0* + ID_OUI_FROM_DATABASE=managee GmbH & Co KG + +OUI:70B3D5956* + ID_OUI_FROM_DATABASE=AeroVision Avionics, Inc. + +OUI:70B3D5F62* + ID_OUI_FROM_DATABASE=FRS GmbH & Co. KG + +OUI:70B3D50A9* + ID_OUI_FROM_DATABASE=ProConnections, Inc. + +OUI:70B3D5EDD* + ID_OUI_FROM_DATABASE=Solar Network & Partners + +OUI:70B3D53DE* + ID_OUI_FROM_DATABASE=ELOMAC Elektronik GmbH + +OUI:70B3D5139* + ID_OUI_FROM_DATABASE=Tunstall A/S + +OUI:70B3D5871* + ID_OUI_FROM_DATABASE=Oso Technologies + +OUI:70B3D5AE9* + ID_OUI_FROM_DATABASE=Cari Electronic + +OUI:70B3D5D8B* + ID_OUI_FROM_DATABASE=Lenoxi Automation s.r.o. + +OUI:001BC50C4* + ID_OUI_FROM_DATABASE=ELDES + +OUI:001BC50BA* + ID_OUI_FROM_DATABASE=NT MICROSYSTEMS + +OUI:001BC50B0* + ID_OUI_FROM_DATABASE=J-D.COM + +OUI:001BC50A6* + ID_OUI_FROM_DATABASE=Balter Security GmbH + +OUI:001BC5075* + ID_OUI_FROM_DATABASE=Kitron GmbH + +OUI:001BC5074* + ID_OUI_FROM_DATABASE=Dynasthetics + +OUI:001BC507F* + ID_OUI_FROM_DATABASE=Hitechlab Inc + +OUI:001BC507E* + ID_OUI_FROM_DATABASE=Bio Molecular System Pty Ltd + +OUI:001BC506A* + ID_OUI_FROM_DATABASE=IST GmbH + +OUI:001BC506B* + ID_OUI_FROM_DATABASE=Verified Energy, LLC. + +OUI:001BC5061* + ID_OUI_FROM_DATABASE=Scientific-Technical Center Epsilon Limited company + +OUI:001BC5060* + ID_OUI_FROM_DATABASE=ENSTECH + +OUI:001BC504D* + ID_OUI_FROM_DATABASE=eiraku electric corp. + +OUI:001BC504C* + ID_OUI_FROM_DATABASE=Rhino Controls Ltd. + +OUI:001BC5057* + ID_OUI_FROM_DATABASE=EREE Electronique + +OUI:001BC5056* + ID_OUI_FROM_DATABASE=ThinKom Solutions, Inc + +OUI:001BC5043* + ID_OUI_FROM_DATABASE=Coincident, Inc. + +OUI:001BC5038* + ID_OUI_FROM_DATABASE=SEED International Ltd. + +OUI:001BC502E* + ID_OUI_FROM_DATABASE=BETTINI SRL + +OUI:001BC5024* + ID_OUI_FROM_DATABASE=ANNECY ELECTRONIQUE SAS + +OUI:001BC501B* + ID_OUI_FROM_DATABASE=Commonwealth Scientific and Industrial Research Organisation + +OUI:001BC501C* + ID_OUI_FROM_DATABASE=Coolit Systems, Inc. + +OUI:70B3D5C3B* + ID_OUI_FROM_DATABASE=Vironova AB + +OUI:70B3D56A9* + ID_OUI_FROM_DATABASE=OHMORI ELECTRIC INDUSTRIES CO.LTD + +OUI:70B3D552E* + ID_OUI_FROM_DATABASE=Swissponic Sagl + +OUI:70B3D5551* + ID_OUI_FROM_DATABASE=infrachip + +OUI:70B3D5323* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5054* + ID_OUI_FROM_DATABASE=Groupeer Technologies + +OUI:70B3D5353* + ID_OUI_FROM_DATABASE=Digital Outfit + +OUI:70B3D5F5B* + ID_OUI_FROM_DATABASE=A.F.MENSAH, INC + +OUI:70B3D5A92* + ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG + +OUI:70B3D5B39* + ID_OUI_FROM_DATABASE=MB Connect Line GmbH + +OUI:70B3D51E3* + ID_OUI_FROM_DATABASE=Hatel Elektronik LTD. STI. + +OUI:70B3D5CCA* + ID_OUI_FROM_DATABASE=SIEMENS AS + +OUI:70B3D5EF6* + ID_OUI_FROM_DATABASE=CHARGELIB + +OUI:70B3D5B02* + ID_OUI_FROM_DATABASE=Nordic Automation Systems AS + +OUI:70B3D5B05* + ID_OUI_FROM_DATABASE=E-PLUS TECHNOLOGY CO., LTD + +OUI:70B3D5847* + ID_OUI_FROM_DATABASE=Ai-Lynx + +OUI:70B3D5148* + ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. + +OUI:70B3D5BFE* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5261* + ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC + +OUI:70B3D5618* + ID_OUI_FROM_DATABASE=Motec Pty Ltd + +OUI:70B3D5892* + ID_OUI_FROM_DATABASE=ABB + +OUI:70B3D59BA* + ID_OUI_FROM_DATABASE=ATIM Radiocommunication + +OUI:70B3D54C4* + ID_OUI_FROM_DATABASE=OOO Research and Production Center Computer Technologies + +OUI:70B3D59B3* + ID_OUI_FROM_DATABASE=K&J Schmittschneider AG + +OUI:70B3D5747* + ID_OUI_FROM_DATABASE=Eva Automation + +OUI:70B3D53F4* + ID_OUI_FROM_DATABASE=Wincode Technology Co., Ltd. + +OUI:70B3D56E6* + ID_OUI_FROM_DATABASE=Eleven Engineering Incorporated + +OUI:70B3D5E39* + ID_OUI_FROM_DATABASE=Thinnect, Inc, + +OUI:70B3D5216* + ID_OUI_FROM_DATABASE=FLEXTRONICS + +OUI:70B3D50FE* + ID_OUI_FROM_DATABASE=Vocality International Ltd + +OUI:70B3D54B7* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D555C* + ID_OUI_FROM_DATABASE=Saratoga Speed, Inc. + +OUI:70B3D5428* + ID_OUI_FROM_DATABASE=Presentation Switchers, Inc. + +OUI:70B3D5C32* + ID_OUI_FROM_DATABASE=INFRASAFE/ ADVANTOR SYSTEMS + +OUI:70B3D5592* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5F1A* + ID_OUI_FROM_DATABASE=Sator Controls s.r.o. + +OUI:70B3D505A* + ID_OUI_FROM_DATABASE=Uni Control System Sp. z o. o. + +OUI:70B3D59EF* + ID_OUI_FROM_DATABASE=Cottonwood Creek Technologies, Inc. + +OUI:70B3D59EB* + ID_OUI_FROM_DATABASE=Preston Industries dba PolyScience + +OUI:70B3D5A3B* + ID_OUI_FROM_DATABASE=Grace Design/Lunatec LLC + +OUI:70B3D57A0* + ID_OUI_FROM_DATABASE=Reactec Ltd + +OUI:70B3D529B* + ID_OUI_FROM_DATABASE=DermaLumics S.L. + +OUI:70B3D5202* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D5259* + ID_OUI_FROM_DATABASE=Zebra Elektronik A.S. + +OUI:70B3D5FBE* + ID_OUI_FROM_DATABASE=Hanbat National University + +OUI:70B3D5FE4* + ID_OUI_FROM_DATABASE=CARE PVT LTD + +OUI:70B3D58B1* + ID_OUI_FROM_DATABASE=M-Tech Innovations Limited + +OUI:70B3D5D34* + ID_OUI_FROM_DATABASE=G-PHILOS CO.,LTD + +OUI:70B3D5528* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5B81* + ID_OUI_FROM_DATABASE=Instro Precision Limited + +OUI:70B3D5479* + ID_OUI_FROM_DATABASE=LINEAGE POWER PVT. LTD. + +OUI:70B3D5A66* + ID_OUI_FROM_DATABASE=Trapeze Software Group Inc + +OUI:70B3D51AB* + ID_OUI_FROM_DATABASE=Access Control Systems JSC + +OUI:70B3D536C* + ID_OUI_FROM_DATABASE=Sicon srl + +OUI:70B3D504D* + ID_OUI_FROM_DATABASE=Sicon srl + +OUI:70B3D5A78* + ID_OUI_FROM_DATABASE=Bionics co.,ltd. + +OUI:70B3D5E7E* + ID_OUI_FROM_DATABASE=Groupe Citypassenger Inc + +OUI:70B3D58D3* + ID_OUI_FROM_DATABASE=PERFORMANCE CONTROLS, INC. + +OUI:70B3D5D63* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5508* + ID_OUI_FROM_DATABASE=INSEVIS GmbH + +OUI:70B3D5440* + ID_OUI_FROM_DATABASE=Discover Video + +OUI:70B3D550E* + ID_OUI_FROM_DATABASE=Micro Trend Automation Co., LTD + +OUI:70B3D5879* + ID_OUI_FROM_DATABASE=ZIGPOS GmbH + +OUI:70B3D5C9F* + ID_OUI_FROM_DATABASE=Triax A/S + +OUI:70B3D58E4* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5350* + ID_OUI_FROM_DATABASE=Tickster AB + +OUI:70B3D52F3* + ID_OUI_FROM_DATABASE=Scame Sistemi srl + +OUI:70B3D5DAD* + ID_OUI_FROM_DATABASE=GD Mission Systems + +OUI:70B3D568D* + ID_OUI_FROM_DATABASE=Meta-chrom Co. Ltd. + +OUI:70B3D591B* + ID_OUI_FROM_DATABASE=Dolotron d.o.o. + +OUI:70B3D5A96* + ID_OUI_FROM_DATABASE=Östling Marking Systems GmbH + +OUI:70B3D5367* + ID_OUI_FROM_DATABASE=Living Water + +OUI:70B3D5114* + ID_OUI_FROM_DATABASE=Project H Pty Ltd + +OUI:70B3D5906* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5243* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:70B3D59F3* + ID_OUI_FROM_DATABASE=IEEE Registration Authority + +OUI:70B3D5AAC* + ID_OUI_FROM_DATABASE=SensoTec GmbH + +OUI:70B3D5F9A* + ID_OUI_FROM_DATABASE=Krabbenhøft og Ingolfsson + +OUI:70B3D5349* + ID_OUI_FROM_DATABASE=SLAT + +OUI:70B3D5D91* + ID_OUI_FROM_DATABASE=FoodALYT GmbH + +OUI:70B3D53AE* + ID_OUI_FROM_DATABASE=Exicom Technologies fze + +OUI:70B3D57DD* + ID_OUI_FROM_DATABASE=Excel Medical Electronics LLC + +OUI:70B3D5E71* + ID_OUI_FROM_DATABASE=SiS Technology + +OUI:70B3D5EA0* + ID_OUI_FROM_DATABASE=PARK24 + +OUI:70B3D5D05* + ID_OUI_FROM_DATABASE=Colmek + +OUI:70B3D5BF5* + ID_OUI_FROM_DATABASE=Acacia Research + +OUI:70B3D5499* + ID_OUI_FROM_DATABASE=Pycom Ltd + +OUI:70B3D521E* + ID_OUI_FROM_DATABASE=Hildebrand Technology Limited + +OUI:70B3D5D67* + ID_OUI_FROM_DATABASE=ALPHA Corporation + +OUI:70B3D5C4F* + ID_OUI_FROM_DATABASE=AE Van de Vliet BVBA + +OUI:70B3D5BD9* + ID_OUI_FROM_DATABASE=SolwayTech + +OUI:70B3D58DB* + ID_OUI_FROM_DATABASE=Kratos Analytical Ltd + +OUI:70B3D5A4F* + ID_OUI_FROM_DATABASE=Weltek Technologies Co. Ltd. + +OUI:70B3D51A3* + ID_OUI_FROM_DATABASE=Telairity Semiconductor + +OUI:70B3D5650* + ID_OUI_FROM_DATABASE=GIFAS-ELECTRIC GmbH + +OUI:70B3D5C63* + ID_OUI_FROM_DATABASE=Xentech Solutions Limited + +OUI:70B3D5106* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D56C5* + ID_OUI_FROM_DATABASE=CJSC «Russian telecom equipment company» (CJSC RTEC) + +OUI:70B3D5FE9* + ID_OUI_FROM_DATABASE=Camsat Przemysław Gralak + +OUI:70B3D54C5* + ID_OUI_FROM_DATABASE=Moving iMage Technologies LLC + +OUI:70B3D591A* + ID_OUI_FROM_DATABASE=Fujian Landfone Information Technology Co.,Ltd + +OUI:70B3D59EC* + ID_OUI_FROM_DATABASE=eSoftThings + +OUI:70B3D5761* + ID_OUI_FROM_DATABASE=Critical Link LLC + +OUI:70B3D5C22* + ID_OUI_FROM_DATABASE=Skyriver Communications Inc. + +OUI:70B3D53BB* + ID_OUI_FROM_DATABASE=A-M Systems + +OUI:70B3D5B44* + ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD. + +OUI:70B3D5584* + ID_OUI_FROM_DATABASE=Sertone, a division of Opti-Knights Ltd + +OUI:70B3D53EF* + ID_OUI_FROM_DATABASE=Vtron Pty Ltd + +OUI:70B3D57C2* + ID_OUI_FROM_DATABASE=Morgan Schaffer Inc. + +OUI:70B3D5697* + ID_OUI_FROM_DATABASE=Alazar Technologies Inc. + +OUI:70B3D561A* + ID_OUI_FROM_DATABASE=Rocket Lab Ltd. + +OUI:70B3D5855* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5F8D* + ID_OUI_FROM_DATABASE=Flextronics Canafa Design Services + +OUI:70B3D59AE* + ID_OUI_FROM_DATABASE=Volansys technologies pvt ltd + +OUI:70B3D542C* + ID_OUI_FROM_DATABASE=D.Marchiori Srl + +OUI:70B3D5CE5* + ID_OUI_FROM_DATABASE=GridBridge Inc + +OUI:70B3D51EF* + ID_OUI_FROM_DATABASE=ADTEK + +OUI:70B3D58AB* + ID_OUI_FROM_DATABASE=EMAC, Inc. + +OUI:70B3D5729* + ID_OUI_FROM_DATABASE=EMAC, Inc. + +OUI:70B3D5E93* + ID_OUI_FROM_DATABASE=ECON Technology Co.Ltd + +OUI:70B3D5CF4* + ID_OUI_FROM_DATABASE=Harbin Cheng Tian Technology Development Co., Ltd. + +OUI:70B3D54BD* + ID_OUI_FROM_DATABASE=Boulder Amplifiers, Inc. + +OUI:70B3D545C* + ID_OUI_FROM_DATABASE=AlyTech + +OUI:70B3D54B9* + ID_OUI_FROM_DATABASE=SHEN ZHEN TTK TECHNOLOGY CO,LTD + +OUI:70B3D51C4* + ID_OUI_FROM_DATABASE=Smeg S.p.A. + +OUI:70B3D5A59* + ID_OUI_FROM_DATABASE=Muuntosähkö Oy - Trafox + +OUI:70B3D5D76* + ID_OUI_FROM_DATABASE=attocube systems AG + +OUI:70B3D57E3* + ID_OUI_FROM_DATABASE=RedLeaf Security + +OUI:70B3D583C* + ID_OUI_FROM_DATABASE=Sinoembed + +OUI:70B3D57EB* + ID_OUI_FROM_DATABASE=Xerox International Partners + +OUI:70B3D5F57* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D501F* + ID_OUI_FROM_DATABASE=SPX Flow Technology BV + +OUI:70B3D5407* + ID_OUI_FROM_DATABASE=IDOSENS + +OUI:70B3D5C01* + ID_OUI_FROM_DATABASE=SmartGuard LLC + +OUI:70B3D57C8* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D57B9* + ID_OUI_FROM_DATABASE=QIAGEN Instruments AG + +OUI:70B3D5DD7* + ID_OUI_FROM_DATABASE=DETECT Australia + +OUI:70B3D5DDF* + ID_OUI_FROM_DATABASE=AeroVision Avionics, Inc. + +OUI:70B3D5BF1* + ID_OUI_FROM_DATABASE=Flashnet SRL + +OUI:70B3D50C5* + ID_OUI_FROM_DATABASE=Precitec Optronik GmbH + +OUI:70B3D5E35* + ID_OUI_FROM_DATABASE=Nanospeed Technologies Limited + +OUI:70B3D55EE* + ID_OUI_FROM_DATABASE=Mikrotron Mikrocomputer, Digital- und Analogtechnik GmbH + +OUI:70B3D5F11* + ID_OUI_FROM_DATABASE=BroadSoft Inc + +OUI:70B3D5B3E* + ID_OUI_FROM_DATABASE=Paradigm Communication Systems Ltd + +OUI:70B3D5161* + ID_OUI_FROM_DATABASE=MB Connect Line GmbH + +OUI:70B3D5033* + ID_OUI_FROM_DATABASE=Sailmon BV + +OUI:70B3D5D42* + ID_OUI_FROM_DATABASE=DSP DESIGN + +OUI:70B3D5A56* + ID_OUI_FROM_DATABASE=DORLET SAU + +OUI:70B3D5FDF* + ID_OUI_FROM_DATABASE=NARA CONTROLS INC. + +OUI:70B3D5430* + ID_OUI_FROM_DATABASE=Algodue Elettronica Srl + +OUI:70B3D55B6* + ID_OUI_FROM_DATABASE=Ethical Lighting and Sensor Solutions Limited + +OUI:70B3D52EB* + ID_OUI_FROM_DATABASE=BRNET CO.,LTD. + +OUI:70B3D5E7A* + ID_OUI_FROM_DATABASE=ART SPA + +OUI:70B3D5A93* + ID_OUI_FROM_DATABASE=Mes Communication Co., Ltd + +OUI:70B3D50FC* + ID_OUI_FROM_DATABASE=vitalcare + +OUI:70B3D59C8* + ID_OUI_FROM_DATABASE=Applied Systems Engineering, Inc. + +OUI:70B3D5D8C* + ID_OUI_FROM_DATABASE=Damerell Design Limited (DCL) + +OUI:70B3D5FBB* + ID_OUI_FROM_DATABASE=Vena Engineering Corporation + +OUI:70B3D5C21* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5628* + ID_OUI_FROM_DATABASE=MECT S.R.L. + +OUI:70B3D532F* + ID_OUI_FROM_DATABASE=Movidius SRL + +OUI:70B3D59C9* + ID_OUI_FROM_DATABASE=PK Sound + +OUI:70B3D5ADA* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5554* + ID_OUI_FROM_DATABASE=Teletypes Manufacturing Plant + +OUI:70B3D5E0D* + ID_OUI_FROM_DATABASE=Sigma Connectivity AB + +OUI:70B3D572D* + ID_OUI_FROM_DATABASE=Kron Medidores + +OUI:70B3D5D8D* + ID_OUI_FROM_DATABASE=Pullnet Technology,S.L. + +OUI:70B3D52BC* + ID_OUI_FROM_DATABASE=EQUIPOS DE TELECOMUNICACIÓN OPTOELECTRÓNICOS, S.A. + +OUI:70B3D52D6* + ID_OUI_FROM_DATABASE=Kvazar LLC + +OUI:70B3D5231* + ID_OUI_FROM_DATABASE=DELTA TAU DATA SYSTEMS, INC. + +OUI:70B3D5823* + ID_OUI_FROM_DATABASE=SP Controls + +OUI:70B3D580D* + ID_OUI_FROM_DATABASE=Data Physics Corporation + +OUI:70B3D5336* + ID_OUI_FROM_DATABASE=Synaccess Networks Inc. + +OUI:70B3D5A51* + ID_OUI_FROM_DATABASE=RF Code + +OUI:70B3D57AF* + ID_OUI_FROM_DATABASE=Hessware GmbH + +OUI:70B3D53F9* + ID_OUI_FROM_DATABASE=Herrick Tech Labs + +OUI:70B3D54B1* + ID_OUI_FROM_DATABASE=LACE LLC. + +OUI:70B3D5530* + ID_OUI_FROM_DATABASE=iSiS-Ex Limited + +OUI:70B3D5A27* + ID_OUI_FROM_DATABASE=HDL da Amazônia Industria Eletrônica Ltda + +OUI:70B3D57AD* + ID_OUI_FROM_DATABASE=Insitu Inc + +OUI:70B3D5E21* + ID_OUI_FROM_DATABASE=LLVISION TECHNOLOGY CO.,LTD + +OUI:70B3D5583* + ID_OUI_FROM_DATABASE=Ducommun Inc. + +OUI:70B3D5F1E* + ID_OUI_FROM_DATABASE=ATX NETWORKS LTD + +OUI:70B3D59E7* + ID_OUI_FROM_DATABASE=Xiamen Maxincom Technologies Co., Ltd. + +OUI:70B3D5472* + ID_OUI_FROM_DATABASE=Quadio Devices Private Limited + +OUI:70B3D57A8* + ID_OUI_FROM_DATABASE=dieEntwickler Elektronik GmbH + +OUI:70B3D503F* + ID_OUI_FROM_DATABASE=Elesar Limited + +OUI:70B3D53D7* + ID_OUI_FROM_DATABASE=Remote Sensing Solutions, Inc. + +OUI:70B3D5838* + ID_OUI_FROM_DATABASE=Tofino + +OUI:70B3D51F3* + ID_OUI_FROM_DATABASE=Smart Energy Code Company Limited + +OUI:70B3D56BB* + ID_OUI_FROM_DATABASE=LUCEO + +OUI:70B3D5C5A* + ID_OUI_FROM_DATABASE=Commsignia Ltd. + +OUI:70B3D59DB* + ID_OUI_FROM_DATABASE=CAS Medical Systems, Inc + +OUI:70B3D5BDD* + ID_OUI_FROM_DATABASE=CDR SRL + +OUI:70B3D5F5A* + ID_OUI_FROM_DATABASE=HAMEG GmbH + +OUI:70B3D574C* + ID_OUI_FROM_DATABASE=Kwant Controls BV + +OUI:70B3D56FA* + ID_OUI_FROM_DATABASE=Dataforth Corporation + +OUI:70B3D5E90* + ID_OUI_FROM_DATABASE=Getein Biotechnology Co.,ltd + +OUI:70B3D5C3F* + ID_OUI_FROM_DATABASE=Code Blue Corporation + +OUI:70B3D5917* + ID_OUI_FROM_DATABASE=KSJ Co.Ltd + +OUI:70B3D5EC6* + ID_OUI_FROM_DATABASE=ESII + +OUI:70B3D5849* + ID_OUI_FROM_DATABASE=RF-Tuote Oy + +OUI:70B3D59D3* + ID_OUI_FROM_DATABASE=Communication Technology Ltd. + +OUI:70B3D546B* + ID_OUI_FROM_DATABASE=Airborne Engineering Limited + +OUI:70B3D5EFB* + ID_OUI_FROM_DATABASE=PXM sp.k. + +OUI:70B3D5A81* + ID_OUI_FROM_DATABASE=Sienda New Media Technologies GmbH + +OUI:70B3D5A1B* + ID_OUI_FROM_DATABASE=Potter Electric Signal Co. + +OUI:70B3D5EAC* + ID_OUI_FROM_DATABASE=Kentech Instruments Limited + +OUI:70B3D58AD* + ID_OUI_FROM_DATABASE=Global Communications Technology LLC + +OUI:70B3D5C97* + ID_OUI_FROM_DATABASE=CSINFOTEL + +OUI:70B3D5F10* + ID_OUI_FROM_DATABASE=Riegl Laser Measurement Systems GmbH + +OUI:70B3D54AA* + ID_OUI_FROM_DATABASE=Twoway Communications, Inc. + +OUI:70B3D55A2* + ID_OUI_FROM_DATABASE=Wallner Automation GmbH + +OUI:70B3D5418* + ID_OUI_FROM_DATABASE=DEV Systemtechnik GmbH& Co KG + +OUI:70B3D5D79* + ID_OUI_FROM_DATABASE=GOMA ELETTRONICA SpA + +OUI:70B3D558E* + ID_OUI_FROM_DATABASE=Veilux Inc. + +OUI:70B3D5181* + ID_OUI_FROM_DATABASE=Task Sistemas + +OUI:70B3D5DCF* + ID_OUI_FROM_DATABASE=KLS Netherlands B.V. + +OUI:70B3D5A25* + ID_OUI_FROM_DATABASE=PulseTor LLC + +OUI:70B3D5D3B* + ID_OUI_FROM_DATABASE=NimbeLink Corp + +OUI:70B3D5882* + ID_OUI_FROM_DATABASE=SIMON TECH, S.L. + +OUI:70B3D5959* + ID_OUI_FROM_DATABASE=Zulex International Co.,Ltd. + +OUI:70B3D5CFF* + ID_OUI_FROM_DATABASE=DTECH Labs, Inc. + +OUI:70B3D50C0* + ID_OUI_FROM_DATABASE=Molu Technology Inc., LTD. + +OUI:70B3D53DF* + ID_OUI_FROM_DATABASE=MultiDyne + +OUI:70B3D582C* + ID_OUI_FROM_DATABASE=NELS Ltd. + +OUI:70B3D5501* + ID_OUI_FROM_DATABASE=Peek Traffic + +OUI:70B3D5025* + ID_OUI_FROM_DATABASE=Elsuhd Net Ltd Co. + +OUI:70B3D558F* + ID_OUI_FROM_DATABASE=LSL systems + +OUI:70B3D5A55* + ID_OUI_FROM_DATABASE=Embest Technology Co., Ltd + +OUI:70B3D5BE1* + ID_OUI_FROM_DATABASE=FeCon GmbH + +OUI:70B3D526E* + ID_OUI_FROM_DATABASE=HI-TECH SYSTEM Co. Ltd. + +OUI:70B3D55E9* + ID_OUI_FROM_DATABASE=Zehetner-Elektronik GmbH + +OUI:70B3D5205* + ID_OUI_FROM_DATABASE=Esource Srl + +OUI:70B3D5112* + ID_OUI_FROM_DATABASE=DiTEST Fahrzeugdiagnose GmbH + +OUI:70B3D53DA* + ID_OUI_FROM_DATABASE=Loop Labs, Inc. + +OUI:70B3D5FF5* + ID_OUI_FROM_DATABASE=Prolan Process Control Co. + +OUI:70B3D52D5* + ID_OUI_FROM_DATABASE=Teuco Guzzini + +OUI:70B3D544B* + ID_OUI_FROM_DATABASE=Open System Solutions Limited + +OUI:70B3D5BEC* + ID_OUI_FROM_DATABASE=Tokyo Communication Equipment MFG Co.,ltd. + +OUI:70B3D56D6* + ID_OUI_FROM_DATABASE=KMtronic Ltd. + +OUI:70B3D557C* + ID_OUI_FROM_DATABASE=Automata GmbH & Co. KG + +OUI:70B3D5DA1* + ID_OUI_FROM_DATABASE=Qprel srl + +OUI:70B3D5097* + ID_OUI_FROM_DATABASE=Avant Technologies + +OUI:70B3D505F* + ID_OUI_FROM_DATABASE=UNISOR MULTISYSTEMS LTD + +OUI:70B3D548D* + ID_OUI_FROM_DATABASE=OMEGA BILANCE SRL SOCIETA' UNIPERSONALE + +OUI:70B3D5E99* + ID_OUI_FROM_DATABASE=Advitronics telecom bv + +OUI:70B3D5362* + ID_OUI_FROM_DATABASE=Asiga + +OUI:70B3D5751* + ID_OUI_FROM_DATABASE=GNF + +OUI:70B3D5587* + ID_OUI_FROM_DATABASE=INCAA Computers + +OUI:70B3D5E27* + ID_OUI_FROM_DATABASE=Woodside Electronics + +OUI:70B3D5346* + ID_OUI_FROM_DATABASE=Ultamation Limited + +OUI:70B3D5052* + ID_OUI_FROM_DATABASE=Sudo Premium Engineering + +OUI:70B3D5F34* + ID_OUI_FROM_DATABASE=MacGray Services + +OUI:70B3D5B16* + ID_OUI_FROM_DATABASE=XI'AN SHENMING ELECTRON TECHNOLOGY CO.,LTD + +OUI:70B3D52FD* + ID_OUI_FROM_DATABASE=Special Projects Group, Inc + +OUI:70B3D5305* + ID_OUI_FROM_DATABASE=CAITRON Industrial Solutions GmbH + +OUI:70B3D5634* + ID_OUI_FROM_DATABASE=idaqs Co.,Ltd. + +OUI:70B3D5CE9* + ID_OUI_FROM_DATABASE=KINEMETRICS + +OUI:70B3D5B8A* + ID_OUI_FROM_DATABASE=Nexus Tech. VN + +OUI:70B3D53A8* + ID_OUI_FROM_DATABASE=JamHub Corp. + +OUI:70B3D5AA0* + ID_OUI_FROM_DATABASE=Simple Works, Inc. + +OUI:70B3D5058* + ID_OUI_FROM_DATABASE=Telink Semiconductor CO, Limtied, Taiwan + +OUI:70B3D5EFA* + ID_OUI_FROM_DATABASE=NextEra Energy Resources, LLC + +OUI:70B3D595A* + ID_OUI_FROM_DATABASE=Sigmann Elektronik GmbH + +OUI:70B3D52E3* + ID_OUI_FROM_DATABASE=Meiknologic GmbH + +OUI:70B3D5ABC* + ID_OUI_FROM_DATABASE=BKM-Micronic Richtfunkanlagen GmbH + +OUI:70B3D5E4A* + ID_OUI_FROM_DATABASE=ICP NewTech Ltd + +OUI:70B3D5ECD* + ID_OUI_FROM_DATABASE=SBS-Feintechnik GmbH & Co. KG + +OUI:70B3D5918* + ID_OUI_FROM_DATABASE=Glova Rail A/S + +OUI:70B3D5308* + ID_OUI_FROM_DATABASE=DSD MICROTECHNOLOGY,INC. + +OUI:70B3D56D3* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + +OUI:70B3D5908* + ID_OUI_FROM_DATABASE=Accusonic + +OUI:70B3D5183* + ID_OUI_FROM_DATABASE=Evco S.p.a. + +OUI:70B3D55A8* + ID_OUI_FROM_DATABASE=Farmobile + +OUI:70B3D5C2C* + ID_OUI_FROM_DATABASE=Dromont S.p.A. + +OUI:001BC50C6* + ID_OUI_FROM_DATABASE=Connode + +OUI:70B3D53C2* + ID_OUI_FROM_DATABASE=Cellular Specialties, Inc. + +OUI:70B3D5EF9* + ID_OUI_FROM_DATABASE=Critical Link + +OUI:001BC50B2* + ID_OUI_FROM_DATABASE=SKODA electric a.s. + +OUI:001BC50B6* + ID_OUI_FROM_DATABASE=Veilux inc. + +OUI:001BC50AC* + ID_OUI_FROM_DATABASE=AVnu Alliance + +OUI:001BC50BC* + ID_OUI_FROM_DATABASE=kuwatec, Inc. + +OUI:001BC50A2* + ID_OUI_FROM_DATABASE=Hettich Benelux + +OUI:001BC50A8* + ID_OUI_FROM_DATABASE=Link Precision + +OUI:001BC509D* + ID_OUI_FROM_DATABASE=Navitar Inc + +OUI:001BC5093* + ID_OUI_FROM_DATABASE=Ambient Devices, Inc. + +OUI:001BC5081* + ID_OUI_FROM_DATABASE=WonATech Co., Ltd. + +OUI:001BC5084* + ID_OUI_FROM_DATABASE=Applied Innovations Research LLC + +OUI:001BC507B* + ID_OUI_FROM_DATABASE=QCORE Medical + +OUI:001BC507A* + ID_OUI_FROM_DATABASE=Servicios Electronicos Industriales Berbel s.l. + +OUI:001BC5089* + ID_OUI_FROM_DATABASE=SIGNATURE CONTROL SYSTEMS, INC. + +OUI:001BC5077* + ID_OUI_FROM_DATABASE=Momentum Data Systems + +OUI:001BC5063* + ID_OUI_FROM_DATABASE=Check-It Solutions Inc + +OUI:001BC5070* + ID_OUI_FROM_DATABASE=Siemens Industries, Inc, Retail & Commercial Systems + +OUI:001BC5071* + ID_OUI_FROM_DATABASE=Center for E-Commerce Infrastructure Development, The University of Hong Kong + +OUI:001BC5067* + ID_OUI_FROM_DATABASE=Embit srl + +OUI:001BC5066* + ID_OUI_FROM_DATABASE=Manufacturas y transformados AB + +OUI:001BC506D* + ID_OUI_FROM_DATABASE=TES Electronic Solutions (I) Pvt. Ltd. + +OUI:001BC504F* + ID_OUI_FROM_DATABASE=Orbital Systems, Ltd. + +OUI:001BC505D* + ID_OUI_FROM_DATABASE=JSC Prominform + +OUI:001BC5053* + ID_OUI_FROM_DATABASE=Metrycom Communications Ltd + +OUI:001BC5059* + ID_OUI_FROM_DATABASE=INPIXAL + +OUI:001BC5045* + ID_OUI_FROM_DATABASE=Marvel Digital International Limited + +OUI:001BC5049* + ID_OUI_FROM_DATABASE=EUROCONTROL S.p.A. + +OUI:001BC503F* + ID_OUI_FROM_DATABASE=ELTRADE Ltd + +OUI:001BC503A* + ID_OUI_FROM_DATABASE=MindMade Sp. z o.o. + +OUI:001BC5030* + ID_OUI_FROM_DATABASE=OctoGate it Security Systems GmbH + +OUI:001BC5026* + ID_OUI_FROM_DATABASE=DIMEP Sistemas + +OUI:001BC5022* + ID_OUI_FROM_DATABASE=CJSC STC SIMOS + +OUI:001BC501E* + ID_OUI_FROM_DATABASE=Private + +OUI:001BC5011* + ID_OUI_FROM_DATABASE=OOO NPP Mera + +OUI:001BC5007* + ID_OUI_FROM_DATABASE=Energy Aware Technology + +OUI:001BC5001* + ID_OUI_FROM_DATABASE=OpenRB.com, Direct SIA + +OUI:70B3D56C7* + ID_OUI_FROM_DATABASE=Becton Dickinson + +OUI:70B3D5CA8* + ID_OUI_FROM_DATABASE=Grupo Epelsa S.L. + +OUI:70B3D5609* + ID_OUI_FROM_DATABASE=PBSI Group Limited + +OUI:70B3D5B40* + ID_OUI_FROM_DATABASE=Wuhan Xingtuxinke ELectronic Co.,Ltd + +OUI:70B3D50D6* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5297* + ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG + +OUI:70B3D5942* + ID_OUI_FROM_DATABASE=TruTeq Devices (Pty) Ltd + +OUI:70B3D5F7B* + ID_OUI_FROM_DATABASE=KST technology + +OUI:70B3D5B99* + ID_OUI_FROM_DATABASE=DomoSafety S.A. + +OUI:70B3D5D51* + ID_OUI_FROM_DATABASE=Azcom Technology S.r.l. + +OUI:70B3D5BA1* + ID_OUI_FROM_DATABASE=Cathwell AS + +OUI:70B3D5286* + ID_OUI_FROM_DATABASE=Pedax Danmark + +OUI:70B3D51A9* + ID_OUI_FROM_DATABASE=OCEANIX INC. + +OUI:70B3D5733* + ID_OUI_FROM_DATABASE=SA Instrumentation Limited + +OUI:70B3D5652* + ID_OUI_FROM_DATABASE=Robert Bosch, LLC + +OUI:70B3D5F65* + ID_OUI_FROM_DATABASE=MARKUS LABS + +OUI:70B3D5F2E* + ID_OUI_FROM_DATABASE=Shanghai JCY Technology Company + +OUI:70B3D57F8* + ID_OUI_FROM_DATABASE=Solvera Lynx d.d. + +OUI:70B3D55BE* + ID_OUI_FROM_DATABASE=CASWA + +OUI:70B3D5F55* + ID_OUI_FROM_DATABASE=Kohler Mira Ltd + +OUI:70B3D57ED* + ID_OUI_FROM_DATABASE=The Things Network Foundation + +OUI:70B3D5A9D* + ID_OUI_FROM_DATABASE=VITEC MULTIMEDIA + +OUI:70B3D51A8* + ID_OUI_FROM_DATABASE=STC Rainbow Ltd. + +OUI:70B3D53CA* + ID_OUI_FROM_DATABASE=TTI Ltd + +OUI:70B3D5E1C* + ID_OUI_FROM_DATABASE=Xcenter AS + +OUI:70B3D5184* + ID_OUI_FROM_DATABASE=XV360 Optical Information Systems Ltd. + +OUI:70B3D5B5C* + ID_OUI_FROM_DATABASE=Prozess Technologie + +OUI:70B3D5AF4* + ID_OUI_FROM_DATABASE=TATTILE SRL + +OUI:70B3D5531* + ID_OUI_FROM_DATABASE=ATEME + +OUI:70B3D5BA7* + ID_OUI_FROM_DATABASE=Digital Yacht Ltd + +OUI:70B3D51C7* + ID_OUI_FROM_DATABASE=Hoshin Electronics Co., Ltd. + +OUI:70B3D528B* + ID_OUI_FROM_DATABASE=Arnouse Digital Devices, Corp. + +OUI:70B3D5D94* + ID_OUI_FROM_DATABASE=Dewetron GmbH + +OUI:70B3D5974* + ID_OUI_FROM_DATABASE=Jireh Industries Ltd. + +OUI:70B3D5544* + ID_OUI_FROM_DATABASE=Silicon Safe Ltd + +OUI:70B3D5EE1* + ID_OUI_FROM_DATABASE=allora Factory BVBA + +OUI:70B3D5389* + ID_OUI_FROM_DATABASE=Private + +OUI:70B3D5640* + ID_OUI_FROM_DATABASE=Electronic Equipment Company Pvt. Ltd. + +OUI:70B3D5D65* + ID_OUI_FROM_DATABASE=CRDE + +OUI:70B3D5AE1* + ID_OUI_FROM_DATABASE=DimoCore Corporation + +OUI:70B3D53C3* + ID_OUI_FROM_DATABASE=AIMCO + +OUI:70B3D53F6* + ID_OUI_FROM_DATABASE=Sycomp Electronic GmbH + +OUI:70B3D530C* + ID_OUI_FROM_DATABASE=Sicon srl + +OUI:70B3D590C* + ID_OUI_FROM_DATABASE=ANTEK GmbH + +OUI:70B3D510A* + ID_OUI_FROM_DATABASE=SEASON DESIGN TECHNOLOGY + +OUI:70B3D547C* + ID_OUI_FROM_DATABASE=Par-Tech, Inc. + +OUI:70B3D5AC3* + ID_OUI_FROM_DATABASE=Novoptel GmbH + +OUI:70B3D597C* + ID_OUI_FROM_DATABASE=Nu-Tek Power Controls and Automation + +OUI:70B3D58F2* + ID_OUI_FROM_DATABASE=Rimota Limited + +OUI:70B3D50BD* + ID_OUI_FROM_DATABASE=Andium + +OUI:70B3D5E3D* + ID_OUI_FROM_DATABASE=Leo Bodnar Electronics Ltd + +OUI:70B3D5B26* + ID_OUI_FROM_DATABASE=INTEC International GmbH + +OUI:70B3D5643* + ID_OUI_FROM_DATABASE=Marques,S.A. + +OUI:70B3D5A7C* + ID_OUI_FROM_DATABASE=Transelektronik Messgeräte GmbH + +OUI:70B3D5C27* + ID_OUI_FROM_DATABASE=GD Mission Systems + +OUI:70B3D594F* + ID_OUI_FROM_DATABASE=MART NETWORK SOLUTIONS LTD + +OUI:70B3D5DFF* + ID_OUI_FROM_DATABASE=Spanawave Corporation + +OUI:70B3D565C* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5B55* + ID_OUI_FROM_DATABASE=CTAG - ESG36871424 + +OUI:70B3D5B33* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D518B* + ID_OUI_FROM_DATABASE=Aplex Technology Inc. + +OUI:70B3D5CCC* + ID_OUI_FROM_DATABASE=AEC s.r.l. + +OUI:70B3D53B2* + ID_OUI_FROM_DATABASE=Sicon srl + +OUI:70B3D5387* + ID_OUI_FROM_DATABASE=GWF MessSysteme AG + +OUI:70B3D551B* + ID_OUI_FROM_DATABASE=Vitrea Smart Home Technologies + +OUI:70B3D5E9B* + ID_OUI_FROM_DATABASE=NUMATA R&D Co.,Ltd + +OUI:70B3D5C34* + ID_OUI_FROM_DATABASE=Technical Panels Co. Ltd. + +OUI:70B3D5200* + ID_OUI_FROM_DATABASE=NextEV Co., Ltd. + +OUI:70B3D5A2C* + ID_OUI_FROM_DATABASE=TLV CO., LTD. + +OUI:70B3D5AA1* + ID_OUI_FROM_DATABASE=Shenzhen Weema TV Technology Co.,Ltd. + +OUI:70B3D50D2* + ID_OUI_FROM_DATABASE=UNMANNED SPA + +OUI:70B3D5AF1* + ID_OUI_FROM_DATABASE=Emka Technologies + +OUI:70B3D5296* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:70B3D5167* + ID_OUI_FROM_DATABASE=Eiden Co.,Ltd. + +OUI:70B3D5A4A* + ID_OUI_FROM_DATABASE=Beijing Arrow SEED Technology Co,.Ltd. + +OUI:70B3D585B* + ID_OUI_FROM_DATABASE=TSUBAKIMOTO CHAIN CO. + +OUI:70B3D56FF* + ID_OUI_FROM_DATABASE=AKEO PLUS + +OUI:70B3D589B* + ID_OUI_FROM_DATABASE=ControlWorks, Inc. + +OUI:70B3D568F* + ID_OUI_FROM_DATABASE=PEEK TRAFFIC + +OUI:70B3D55AB* + ID_OUI_FROM_DATABASE=Sea Air and Land Communications Ltd + +OUI:70B3D5CD2* + ID_OUI_FROM_DATABASE=HBH Microwave GmbH + +OUI:70B3D5B23* + ID_OUI_FROM_DATABASE=Supervision Test et Pilotage + +OUI:70B3D5178* + ID_OUI_FROM_DATABASE=Gamber Johnson-LLC + +OUI:70B3D57B6* + ID_OUI_FROM_DATABASE=Amada Miyachi America Inc. + +OUI:70B3D555A* + ID_OUI_FROM_DATABASE=Sontay Ltd. + +OUI:70B3D5CB2* + ID_OUI_FROM_DATABASE=SECLAB + +OUI:70B3D511C* + ID_OUI_FROM_DATABASE=Samriddi Automations Pvt. Ltd. + +OUI:70B3D5AE5* + ID_OUI_FROM_DATABASE=BeatCraft, Inc. + +OUI:70B3D5A91* + ID_OUI_FROM_DATABASE=IDEAL INDUSTRIES Ltd t/a Casella + +OUI:70B3D51DD* + ID_OUI_FROM_DATABASE=RF CREATIONS LTD + +OUI:70B3D50AE* + ID_OUI_FROM_DATABASE=Norsat International Inc. + +OUI:70B3D51DA* + ID_OUI_FROM_DATABASE=Promess Inc. + +OUI:70B3D555B* + ID_OUI_FROM_DATABASE=Procon Electronics Pty Ltd + +OUI:70B3D5461* + ID_OUI_FROM_DATABASE=TESEC Corporation + +OUI:70B3D57FB* + ID_OUI_FROM_DATABASE=db Broadcast Products Ltd + +OUI:70B3D5CED* + ID_OUI_FROM_DATABASE=Advanced Products Corporation Pte Ltd + +OUI:70B3D5DB0* + ID_OUI_FROM_DATABASE=Arnouse Digital Devices Corp + +OUI:70B3D5CCD* + ID_OUI_FROM_DATABASE=Suzhou PowerCore Technology Co.,Ltd. + +OUI:70B3D5163* + ID_OUI_FROM_DATABASE=BHARAT HEAVY ELECTRICALS LIMITED + +OUI:1C8776D* + ID_OUI_FROM_DATABASE=Qivivo + +OUI:1C87764* + ID_OUI_FROM_DATABASE=RDP.RU + +OUI:1C87768* + ID_OUI_FROM_DATABASE=Guangzhou Video-Star Electronics Co.,Ltd. + +OUI:8439BE5* + ID_OUI_FROM_DATABASE=Neat S.r.l. + +OUI:8439BE8* + ID_OUI_FROM_DATABASE=Diamond Products LLC + +OUI:8439BE3* + ID_OUI_FROM_DATABASE=ShenZhen Fudeyu Technology co.,Ltd + +OUI:1C88799* + ID_OUI_FROM_DATABASE=Xingtera China Ltd + +OUI:40A36B5* + ID_OUI_FROM_DATABASE=National Research Council of Canada + +OUI:1C87762* + ID_OUI_FROM_DATABASE=Ibeo Automotive Systems GmbH + +OUI:40A36B6* + ID_OUI_FROM_DATABASE=Bixi Systems Ltd. + +OUI:70886BB* + ID_OUI_FROM_DATABASE=Beijing Strongleader Science & Technology Co., Ltd. + +OUI:800A805* + ID_OUI_FROM_DATABASE=Shenzhen Zidoo Technology Co., Ltd. + +OUI:800A801* + ID_OUI_FROM_DATABASE=Dongguan I-Chime electrinics Co.,Ltd + +OUI:1C21D17* + ID_OUI_FROM_DATABASE=Soundtrack Your Brand Sweden AB + +OUI:DC4427C* + ID_OUI_FROM_DATABASE=Pyrexx Technologies GmbH + +OUI:CC1BE0E* + ID_OUI_FROM_DATABASE=Cassia Networks + +OUI:CC1BE02* + ID_OUI_FROM_DATABASE=i-Trinetech Co.,Ltd. + +OUI:CC1BE05* + ID_OUI_FROM_DATABASE=Earphone Connection, Ubc. + +OUI:A03E6B1* + ID_OUI_FROM_DATABASE=Business Support Consultant Co.,Ltd + +OUI:C88ED19* + ID_OUI_FROM_DATABASE=Focalcrest, Ltd. + +OUI:C88ED14* + ID_OUI_FROM_DATABASE=Comlab AG + +OUI:A03E6BA* + ID_OUI_FROM_DATABASE=Shenzhen Neostra Technology Co.Ltd + +OUI:78C2C0E* + ID_OUI_FROM_DATABASE=Huwomobility + +OUI:78C2C01* + ID_OUI_FROM_DATABASE=XRONOS-INC + +OUI:B437D1C* + ID_OUI_FROM_DATABASE=NANJING PUTIAN TELECOMMUNICATIONS TECHNOLOGY CO.,LTD. + +OUI:B437D17* + ID_OUI_FROM_DATABASE=GE Power Management + +OUI:DC44273* + ID_OUI_FROM_DATABASE=General Microsystems Sdn Bhd + +OUI:78C2C08* + ID_OUI_FROM_DATABASE=Beijing Coilabs technology co.,ltd + +OUI:78C2C05* + ID_OUI_FROM_DATABASE=ShenZhen TuLing Robot CO.,LTD + +OUI:B0C5CAB* + ID_OUI_FROM_DATABASE=RISECOMM (HK) TECHNOLOGY CO. LIMITED + +OUI:B0C5CA2* + ID_OUI_FROM_DATABASE=LOWOTEC GmbH + +OUI:807B855* + ID_OUI_FROM_DATABASE=EFCO + +OUI:807B857* + ID_OUI_FROM_DATABASE=Chendu Ningshui Technology Co.,Ltd + +OUI:807B850* + ID_OUI_FROM_DATABASE=Shiroshita Industrial Co., Ltd. + +OUI:80E4DA3* + ID_OUI_FROM_DATABASE=Beijing Gaokezhongtian Technology Co Ltd + +OUI:2CD1419* + ID_OUI_FROM_DATABASE=Beijing Hexing Chuangxiang Technology Co., Ltd. + +OUI:1CCAE3D* + ID_OUI_FROM_DATABASE=eSight Corporation + +OUI:1CCAE3C* + ID_OUI_FROM_DATABASE=Gahdeung Elecom + +OUI:64FB81A* + ID_OUI_FROM_DATABASE=Bronkhorst High-Tech BV + +OUI:64FB81D* + ID_OUI_FROM_DATABASE=Dongyang unitech.co.ltd + +OUI:64FB816* + ID_OUI_FROM_DATABASE=XIMO Communication Technology Co., Ltd + +OUI:64FB811* + ID_OUI_FROM_DATABASE=Narrative AB + +OUI:549A111* + ID_OUI_FROM_DATABASE=SpearX Inc. + +OUI:549A112* + ID_OUI_FROM_DATABASE=Torrap Design Limited + +OUI:807B85C* + ID_OUI_FROM_DATABASE=Ningbo Plus and Popscreens electronic Technology Co.,LTD + +OUI:1CCAE31* + ID_OUI_FROM_DATABASE=PGA ELECTRONIC + +OUI:1CCAE30* + ID_OUI_FROM_DATABASE=Private + +OUI:80E4DAC* + ID_OUI_FROM_DATABASE=EVER Sp. z o.o. + +OUI:80E4DA7* + ID_OUI_FROM_DATABASE=Shortcut Labs + +OUI:B437D13* + ID_OUI_FROM_DATABASE=DIMTON CO.,LTD. + +OUI:B437D10* + ID_OUI_FROM_DATABASE=Lezyne INC USA + +OUI:74F8DBB* + ID_OUI_FROM_DATABASE=Capwave Technologies Inc + +OUI:885D904* + ID_OUI_FROM_DATABASE=Wuhan Strong Electronics Co., Ltd + +OUI:549A11C* + ID_OUI_FROM_DATABASE=Xi'an Hua Fan Technology Co.,Ltd. + +OUI:549A11B* + ID_OUI_FROM_DATABASE=Elite Silicon Technology, Inc. + +OUI:74F8DB3* + ID_OUI_FROM_DATABASE=InnoTrans Communications + +OUI:885D90D* + ID_OUI_FROM_DATABASE=Hexaglobe + +OUI:885D909* + ID_OUI_FROM_DATABASE=Gigatech R&D Corp. + +OUI:F802782* + ID_OUI_FROM_DATABASE=Innodisk + +OUI:0CEFAFE* + ID_OUI_FROM_DATABASE=Infinisource Inc. + +OUI:90C6822* + ID_OUI_FROM_DATABASE=ekey biometric systems gmbh + +OUI:2C6A6FD* + ID_OUI_FROM_DATABASE=Holjeron + +OUI:2C6A6F9* + ID_OUI_FROM_DATABASE=Logic IO Aps + +OUI:2C6A6FA* + ID_OUI_FROM_DATABASE=Wellntel, Inc. + +OUI:9802D8B* + ID_OUI_FROM_DATABASE=HANSHIN MEDICAL CO., LTD. + +OUI:9802D87* + ID_OUI_FROM_DATABASE=Ormazabal Protection&Automation + +OUI:0CEFAF8* + ID_OUI_FROM_DATABASE=BSX Athletics + +OUI:0CEFAF5* + ID_OUI_FROM_DATABASE=PREMIUM SA + +OUI:0CEFAF4* + ID_OUI_FROM_DATABASE=Sentry360 + +OUI:A44F29C* + ID_OUI_FROM_DATABASE=Shenzhen Huadoo Bright Group Limitied + +OUI:A0BB3E3* + ID_OUI_FROM_DATABASE=WiteRiver Technology LLC + +OUI:28FD80D* + ID_OUI_FROM_DATABASE=Grandway Technology (Shenzhen) Limited + +OUI:28FD80C* + ID_OUI_FROM_DATABASE=Airbus Defence and Space Oy + +OUI:28FD809* + ID_OUI_FROM_DATABASE=JINLITONG INTERNATIONAL CO.,LTD + +OUI:28FD802* + ID_OUI_FROM_DATABASE=Zhixiang Technology Co., Ltd. + +OUI:A0BB3EA* + ID_OUI_FROM_DATABASE=Filo SRL + +OUI:9802D82* + ID_OUI_FROM_DATABASE=United Power Research Technology Corp. + +OUI:A0BB3E8* + ID_OUI_FROM_DATABASE=AutarcTech GmbH + +OUI:A0BB3E4* + ID_OUI_FROM_DATABASE=COMSYS Communications Systems Service GmbH + +OUI:90C682E* + ID_OUI_FROM_DATABASE=Shanghai HuRong Communication Technology Development Co., Ltd. + +OUI:90C6829* + ID_OUI_FROM_DATABASE=ACT + +OUI:F80278E* + ID_OUI_FROM_DATABASE=Lit Technologies + +OUI:F80278B* + ID_OUI_FROM_DATABASE=Rosemount Analytical + +OUI:F802787* + ID_OUI_FROM_DATABASE=BETTINI SRL + +OUI:28FD803* + ID_OUI_FROM_DATABASE=NUUO, Inc. + +OUI:E818635* + ID_OUI_FROM_DATABASE=WETEK ELECTRONICS LIMITED + +OUI:E818632* + ID_OUI_FROM_DATABASE=AVCON Information Technology Co.,Ltd + +OUI:B8D812D* + ID_OUI_FROM_DATABASE=Lam Research + +OUI:B8D8126* + ID_OUI_FROM_DATABASE=Vonger Electronic Technology Co.,Ltd. + +OUI:B8D8124* + ID_OUI_FROM_DATABASE=V5 Technology Corporation + +OUI:74E14A6* + ID_OUI_FROM_DATABASE=Emerging Technology (Holdings) Ltd. + +OUI:74E14A3* + ID_OUI_FROM_DATABASE=emz-Hanauer GmbH & Co. KGaA + +OUI:E4956ED* + ID_OUI_FROM_DATABASE=Shanghai Tieda Telecommunications Equipment Co.,LTD. + +OUI:D022122* + ID_OUI_FROM_DATABASE=RHENAC Systems GmbH + +OUI:E81863D* + ID_OUI_FROM_DATABASE=DIGITAL DYNAMICS, INC. + +OUI:E818639* + ID_OUI_FROM_DATABASE=BSM Wireless Inc. + +OUI:D02212D* + ID_OUI_FROM_DATABASE=SHENZHEN ZHONGXI SECURITY CO.,LTD + +OUI:E4956EA* + ID_OUI_FROM_DATABASE=Red Point Positioning, Corp. + +OUI:E4956E4* + ID_OUI_FROM_DATABASE=Guang Lian Zhi Tong Technology Limited + +OUI:B8D8120* + ID_OUI_FROM_DATABASE=Glamo Inc. + +OUI:74E14AA* + ID_OUI_FROM_DATABASE=AStar Design Service Technologies Co., Ltd. + +OUI:A44F297* + ID_OUI_FROM_DATABASE=Protean Payment + +OUI:A44F290* + ID_OUI_FROM_DATABASE=Dermalog Identification Systems GmbH + +OUI:3C39E7E* + ID_OUI_FROM_DATABASE=MARPOSS SPA + +OUI:3C39E7C* + ID_OUI_FROM_DATABASE=VANSTONE ELECTRONIC (BEIJING)CO,. LTD. + +OUI:3C39E72* + ID_OUI_FROM_DATABASE=HomeWizard B.V. + +OUI:D022126* + ID_OUI_FROM_DATABASE=URANO INDUSTRIA DE BALANCAS E EQUIPAMENTOS LTDA + +OUI:BC6641D* + ID_OUI_FROM_DATABASE=UtilLighting Co.,Ltd. + +OUI:BC66419* + ID_OUI_FROM_DATABASE=Shenzhen General Measure Technology Co., Ltd + +OUI:BC66416* + ID_OUI_FROM_DATABASE=Intuitive Surgical, Inc + +OUI:BC34001* + ID_OUI_FROM_DATABASE=IPLINK Technology Corp + +OUI:A43BFAE* + ID_OUI_FROM_DATABASE=The Magstim Company Ltd. + +OUI:A43BFAA* + ID_OUI_FROM_DATABASE=Plus One Japan Ltd. + +OUI:BC66411* + ID_OUI_FROM_DATABASE=Global China Technology Limited + +OUI:B01F815* + ID_OUI_FROM_DATABASE=SHENZHEN GRID TECHNOLOGY CO.,LTD + +OUI:B01F811* + ID_OUI_FROM_DATABASE=Uvax Concepts + +OUI:F40E11E* + ID_OUI_FROM_DATABASE=Elektronika Naglic d.o.o. + +OUI:F40E112* + ID_OUI_FROM_DATABASE=Axel srl + +OUI:7C70BC9* + ID_OUI_FROM_DATABASE=dogtra + +OUI:7C70BC5* + ID_OUI_FROM_DATABASE=Canary Connect, Inc. + +OUI:141FBA7* + ID_OUI_FROM_DATABASE=Wisnetworks Technologies Co., Ltd. + +OUI:7C70BCE* + ID_OUI_FROM_DATABASE=HOPERUN MMAX DIGITAL PTE. LTD. + +OUI:7C70BC0* + ID_OUI_FROM_DATABASE=Shanghai magcomm communication technology co ltd + +OUI:BC3400B* + ID_OUI_FROM_DATABASE=FARO TECHNOLOGIES, INC. + +OUI:BC34007* + ID_OUI_FROM_DATABASE=Q-PRODUCTS a. s. + +OUI:B01F81D* + ID_OUI_FROM_DATABASE=TAIWAN Anjie Electronics Co.,Ltd. + +OUI:7419F87* + ID_OUI_FROM_DATABASE=Broadanet S.T.M + +OUI:A43BFA7* + ID_OUI_FROM_DATABASE=Deatronic srl + +OUI:A43BFA2* + ID_OUI_FROM_DATABASE=Powell Industries + +OUI:D07650E* + ID_OUI_FROM_DATABASE=Revox Inc. + +OUI:D07650A* + ID_OUI_FROM_DATABASE=InventDesign + +OUI:D076503* + ID_OUI_FROM_DATABASE=TAPKO Technologies GmbH + +OUI:1C87741* + ID_OUI_FROM_DATABASE=SIGFOX + +OUI:1C87743* + ID_OUI_FROM_DATABASE=Silora R&D + +OUI:1C88795* + ID_OUI_FROM_DATABASE=SHENZHENFREELINK ELECTRONIC CO.,LTD + +OUI:1C8879C* + ID_OUI_FROM_DATABASE=Accriva + +OUI:1C87799* + ID_OUI_FROM_DATABASE=Istria soluciones de criptografia, S. A. + +OUI:1C87749* + ID_OUI_FROM_DATABASE=Wide World Trade HK ltd. + +OUI:1C8774B* + ID_OUI_FROM_DATABASE=HABEY USA Inc. + +OUI:1C88794* + ID_OUI_FROM_DATABASE=Ultraflux + +OUI:1C88796* + ID_OUI_FROM_DATABASE=Eolos IT Corp + +OUI:1C8774E* + ID_OUI_FROM_DATABASE=Quest Integrity + +OUI:78CA834* + ID_OUI_FROM_DATABASE=Pinhole (Beijing) Technology Co., Ltd. + +OUI:78CA83E* + ID_OUI_FROM_DATABASE=Konecranes + +OUI:38B8EBB* + ID_OUI_FROM_DATABASE=ExaScaler Inc. + +OUI:38FDFEC* + ID_OUI_FROM_DATABASE=New Garden Co., Ltd. + +OUI:38FDFE5* + ID_OUI_FROM_DATABASE=CaptiveAire Systems Inc. + +OUI:5CF2867* + ID_OUI_FROM_DATABASE=Access IS + +OUI:5CF2862* + ID_OUI_FROM_DATABASE=Shanghai Notion Information Technology CO.,LTD. + +OUI:5CF286C* + ID_OUI_FROM_DATABASE=Sunpet Industries Limited + +OUI:5CF2866* + ID_OUI_FROM_DATABASE=VPInstruments + +OUI:5CF286A* + ID_OUI_FROM_DATABASE=Unfors Raysafe AB + +OUI:7C477C5* + ID_OUI_FROM_DATABASE=Midwest Microwave Solutions + +OUI:5CF286E* + ID_OUI_FROM_DATABASE=Daisen Electronic Industrial Co., Ltd. + +OUI:7C477C1* + ID_OUI_FROM_DATABASE=Photosynth Inc. + +OUI:986D35A* + ID_OUI_FROM_DATABASE=iWave Japan, Inc. + +OUI:986D357* + ID_OUI_FROM_DATABASE=Zhejiang Hanshow Technology Co., Ltd. + +OUI:50FF99C* + ID_OUI_FROM_DATABASE=Goetting KG + +OUI:50FF995* + ID_OUI_FROM_DATABASE=Garrison Technology + +OUI:50FF997* + ID_OUI_FROM_DATABASE=Honeywell International + +OUI:50FF99B* + ID_OUI_FROM_DATABASE=Sichuan Dowlab Electronics Technology Co. Ltd + +OUI:E0B6F55* + ID_OUI_FROM_DATABASE=Shenzhen Civicom Technology Co.,Limited + +OUI:E0B6F50* + ID_OUI_FROM_DATABASE=BeSTAR Corporation + +OUI:E0B6F57* + ID_OUI_FROM_DATABASE=Shenzhen Xrinda Technology Ltd + +OUI:E0B6F5B* + ID_OUI_FROM_DATABASE=Moog Crossbow + +OUI:E0B6F5C* + ID_OUI_FROM_DATABASE=funktel GmbH + +OUI:C47C8D1* + ID_OUI_FROM_DATABASE=LYNX INNOVATION LITIMED + +OUI:C47C8D4* + ID_OUI_FROM_DATABASE=ROBOSTAR + +OUI:C47C8D7* + ID_OUI_FROM_DATABASE=Awiselink Co., Ltd. + +OUI:C47C8D3* + ID_OUI_FROM_DATABASE=Watec Co., Ltd. + +OUI:CCD31E1* + ID_OUI_FROM_DATABASE=Rondo Burgdorf AG + +OUI:CCD31E6* + ID_OUI_FROM_DATABASE=BBPOS International Limited + +OUI:CCD31E3* + ID_OUI_FROM_DATABASE=KEN A/S + +OUI:D0D94F9* + ID_OUI_FROM_DATABASE=Hangzhou xiaoben technology co.,Ltd + +OUI:D0D94F7* + ID_OUI_FROM_DATABASE=Private + +OUI:D0D94F0* + ID_OUI_FROM_DATABASE=Perfant Technology Co., Ltd + +OUI:8C192DD* + ID_OUI_FROM_DATABASE=Pyras Technology Inc. + +OUI:8C192D4* + ID_OUI_FROM_DATABASE=Charmlink Tech(HK) Co.,Limited + +OUI:8C192DE* + ID_OUI_FROM_DATABASE=Elcon AB + +OUI:2836384* + ID_OUI_FROM_DATABASE=Dspread Technology (Beijing) Inc. + +OUI:283638A* + ID_OUI_FROM_DATABASE=Bluekey Pty Ltd + +OUI:CC1BE0F* + ID_OUI_FROM_DATABASE=Private + +OUI:F40E11F* + ID_OUI_FROM_DATABASE=Private + +OUI:D07650F* + ID_OUI_FROM_DATABASE=Private + +OUI:E81863F* + ID_OUI_FROM_DATABASE=Private + +OUI:3C39E7F* + ID_OUI_FROM_DATABASE=Private + +OUI:F80278F* + ID_OUI_FROM_DATABASE=Private + +OUI:9802D8F* + ID_OUI_FROM_DATABASE=Private + +OUI:90C682F* + ID_OUI_FROM_DATABASE=Private + +OUI:283638D* + ID_OUI_FROM_DATABASE=APPEAK Technology System Co.Ltd. + +OUI:1CCAE3F* + ID_OUI_FROM_DATABASE=Private + +OUI:2836389* + ID_OUI_FROM_DATABASE=Shenzhen Zhi Hua Creative Technology Co., Ltd. + +OUI:B0C5CA7* + ID_OUI_FROM_DATABASE=SHENZHEN KTC TECHNOLOGY GROUP + +OUI:283638E* + ID_OUI_FROM_DATABASE=SCA Hygiene Products AB + +OUI:F0ACD73* + ID_OUI_FROM_DATABASE=Med-Pat/Inn-Phone + +OUI:F0ACD75* + ID_OUI_FROM_DATABASE=PAVO TASARIM URETIM TICARET A.S. + +OUI:2836382* + ID_OUI_FROM_DATABASE=SHENZHEN GOSPELL SMARTHOME ELECTRONIC CO., LTD. + +OUI:F0ACD7D* + ID_OUI_FROM_DATABASE=Smart Power Technology Co., Ltd. + +OUI:F0ACD77* + ID_OUI_FROM_DATABASE=Hanju Network Technologies Co. + +OUI:58E8768* + ID_OUI_FROM_DATABASE=Chengdu Vision-Zenith Technology Co.,Ltd + +OUI:58E8766* + ID_OUI_FROM_DATABASE=DivioTec Inc. + +OUI:84E0F4B* + ID_OUI_FROM_DATABASE=Orchard Electronics Co., Ltd. + +OUI:84E0F4C* + ID_OUI_FROM_DATABASE=AIMTRON CORPORATION + +OUI:84E0F45* + ID_OUI_FROM_DATABASE=Hangzhou Nationalchip Science & Technology Co.,Ltd. + +OUI:84E0F41* + ID_OUI_FROM_DATABASE=MedicusTek Inc. + +OUI:70F8E77* + ID_OUI_FROM_DATABASE=NST Technology Limited Co.,Ltd. + +OUI:F81D786* + ID_OUI_FROM_DATABASE=Zengge Co., Limited + +OUI:F81D784* + ID_OUI_FROM_DATABASE=Digital Imaging Technology + +OUI:F81D781* + ID_OUI_FROM_DATABASE=ADTECHNO Inc. + +OUI:383A211* + ID_OUI_FROM_DATABASE=HOBART GmbH + +OUI:383A212* + ID_OUI_FROM_DATABASE=Shenzhen HS Fiber Communication Equipment CO., LTD + +OUI:383A217* + ID_OUI_FROM_DATABASE=Chengdu Krosslan Technology Inc. + +OUI:AC64DDA* + ID_OUI_FROM_DATABASE=Bluewave Global Manufacturing Limited + +OUI:AC64DDB* + ID_OUI_FROM_DATABASE=Groupe Citypassenger Inc + +OUI:4CE173B* + ID_OUI_FROM_DATABASE=Shanghai Ehong Technology Co.,Ltd + +OUI:4CE1737* + ID_OUI_FROM_DATABASE=Ersúles Limited + +OUI:4CE1739* + ID_OUI_FROM_DATABASE=Shenzhen Evolution Dynamics Co., Ltd. + +OUI:1CC0E13* + ID_OUI_FROM_DATABASE=HANGZHOU SOFTEL OPTIC CO., LTD + +OUI:1C8776C* + ID_OUI_FROM_DATABASE=Strone Technology + +OUI:1C87795* + ID_OUI_FROM_DATABASE=BEIDIAN GROUP + +OUI:1C8779B* + ID_OUI_FROM_DATABASE=Beijing Geedeen Technology Co., Ltd + +OUI:1C87794* + ID_OUI_FROM_DATABASE=Novetta + +OUI:1C87792* + ID_OUI_FROM_DATABASE=SMARTMOVT TECHNOLOGY Co., LTD + +OUI:1C87791* + ID_OUI_FROM_DATABASE=A-GEAR COMPANY LIMITED + +OUI:8439BED* + ID_OUI_FROM_DATABASE=Shenzhen Lidaxun Digital Technology Co.,Ltd + +OUI:8439BE9* + ID_OUI_FROM_DATABASE=Guangdong SunMeng Information Technology Co. Ltd. + +OUI:8439BEC* + ID_OUI_FROM_DATABASE=EDC Electronic Design Chemnitz GmbH + +OUI:8439BEA* + ID_OUI_FROM_DATABASE=Emotiq s.r.l. + +OUI:40A36BA* + ID_OUI_FROM_DATABASE=Embrionix Design Inc. + +OUI:8439BE4* + ID_OUI_FROM_DATABASE=Shenzhen Ramos Digital Technology Co,.Ltd. + +OUI:40A36B4* + ID_OUI_FROM_DATABASE=SKS-Kinkel Elektronik GmbH + +OUI:800A803* + ID_OUI_FROM_DATABASE=Beijing VControl Technology Co., Ltd. + +OUI:A03E6BD* + ID_OUI_FROM_DATABASE=Jining SmartCity Infotech Co.Ltd. + +OUI:C88ED1C* + ID_OUI_FROM_DATABASE=Shanghai Bwave Technology Co.,Ltd + +OUI:C88ED1B* + ID_OUI_FROM_DATABASE=Advanced Micro Controls Inc. + +OUI:C88ED16* + ID_OUI_FROM_DATABASE=Shenyang Machine Tool(Group) Research & Design Institute Co., Ltd, Shanghai Branch + +OUI:CC1BE0B* + ID_OUI_FROM_DATABASE=ART&CORE Inc + +OUI:CC1BE07* + ID_OUI_FROM_DATABASE=Sichuan Dianjia network technology Co.Ltd. + +OUI:CC1BE00* + ID_OUI_FROM_DATABASE=MICROTECH SYSTEM + +OUI:DC44270* + ID_OUI_FROM_DATABASE=Suritel + +OUI:B0C5CAE* + ID_OUI_FROM_DATABASE=Audio Elektronik İthalat İhracat San ve Tic A.Ş. + +OUI:B0C5CA9* + ID_OUI_FROM_DATABASE=D&T Inc. + +OUI:C88ED10* + ID_OUI_FROM_DATABASE=AISWORLD PRIVATE LIMITED + +OUI:C88ED12* + ID_OUI_FROM_DATABASE=ROTRONIC AG + +OUI:1C21D16* + ID_OUI_FROM_DATABASE=Wuhan TieChi Detection Technology Co., Ltd. + +OUI:DC44276* + ID_OUI_FROM_DATABASE=EK-TEAM Elektronik- u. Kunststoff-Technik GmbH + +OUI:DC4427A* + ID_OUI_FROM_DATABASE=Shanghai Huahong Integrated Circuit Co.,Ltd + +OUI:A03E6B8* + ID_OUI_FROM_DATABASE=718th Research Institute of CSIC + +OUI:A03E6B3* + ID_OUI_FROM_DATABASE=iLoda Solutions Limited + +OUI:B0C5CA4* + ID_OUI_FROM_DATABASE=shanghai University Ding-Tech software Corp.,ltd + +OUI:78C2C0D* + ID_OUI_FROM_DATABASE=KORF Inc. + +OUI:78C2C0A* + ID_OUI_FROM_DATABASE=Ombitron, Inc. + +OUI:78C2C03* + ID_OUI_FROM_DATABASE=Ningbo Sanxing Electric Co., Ltd. + +OUI:B437D1E* + ID_OUI_FROM_DATABASE=Union Tecnologica Noxium S.L. + +OUI:B437D19* + ID_OUI_FROM_DATABASE=Nanjing yuekong Intelligent Technology + +OUI:885D906* + ID_OUI_FROM_DATABASE=Hi-Profile Achievement (M) Sdn Bhd + +OUI:885D900* + ID_OUI_FROM_DATABASE=FOSHAN HUAGUO OPTICAL CO.,LTD + +OUI:74F8DB8* + ID_OUI_FROM_DATABASE=Songam Syscom Co. LTD. + +OUI:74F8DB6* + ID_OUI_FROM_DATABASE=Shenzhen Melon Electronics Co.,Ltd + +OUI:885D90B* + ID_OUI_FROM_DATABASE=Premier Merchandises Limited + +OUI:B437D15* + ID_OUI_FROM_DATABASE=Stratom, Inc. + +OUI:74F8DBC* + ID_OUI_FROM_DATABASE=TBM CO., LTD. + +OUI:2C6A6F0* + ID_OUI_FROM_DATABASE=Shanghai Shuncom Electronic Technology Co.,Ltd + +OUI:2C6A6F1* + ID_OUI_FROM_DATABASE=ELKO EP, s.r.o. + +OUI:9802D8E* + ID_OUI_FROM_DATABASE=Private + +OUI:549A11D* + ID_OUI_FROM_DATABASE=Hangzhou duotin Technology Co., Ltd. + +OUI:549A11A* + ID_OUI_FROM_DATABASE=VendNovation LLC + +OUI:549A113* + ID_OUI_FROM_DATABASE=Royal Boon Edam International BV + +OUI:549A110* + ID_OUI_FROM_DATABASE=Shenzhen Excera Technology Co.,Ltd. + +OUI:807B85E* + ID_OUI_FROM_DATABASE=Mersen + +OUI:807B859* + ID_OUI_FROM_DATABASE=SMART ELECTRONICS NZ LIMITED + +OUI:1CCAE32* + ID_OUI_FROM_DATABASE=Insigma Inc + +OUI:80E4DAA* + ID_OUI_FROM_DATABASE=Neutronics + +OUI:1CCAE3E* + ID_OUI_FROM_DATABASE=Dabi Atlante S/A Industrias Medico Odontológicas + +OUI:1CCAE3B* + ID_OUI_FROM_DATABASE=Dream Visions Co., LTD + +OUI:1CCAE3A* + ID_OUI_FROM_DATABASE=SIREA + +OUI:2CD1418* + ID_OUI_FROM_DATABASE=Minno LLC + +OUI:90C682B* + ID_OUI_FROM_DATABASE=Lachmann & Rink GmbH + +OUI:80E4DA0* + ID_OUI_FROM_DATABASE=Wheatstone Corporation + +OUI:64FB819* + ID_OUI_FROM_DATABASE=hiQview Corporation + +OUI:64FB814* + ID_OUI_FROM_DATABASE=Pricer AB + +OUI:90C6825* + ID_OUI_FROM_DATABASE=S.A.E.T. S.R.L. + +OUI:90C6827* + ID_OUI_FROM_DATABASE=Cinet Inc + +OUI:90C6820* + ID_OUI_FROM_DATABASE=Shenzhen Lencotion Technology Co.,Ltd + +OUI:2C6A6FC* + ID_OUI_FROM_DATABASE=Sensity Systems + +OUI:2C6A6FB* + ID_OUI_FROM_DATABASE=Schneider Electric Korea + +OUI:2C6A6F8* + ID_OUI_FROM_DATABASE=Milbank Manufacturing Co. + +OUI:807B853* + ID_OUI_FROM_DATABASE=Zhuhai TOP Intelligence Electric Co., Ltd. + +OUI:A0BB3ED* + ID_OUI_FROM_DATABASE=Shenzhen Talent Technology company limited + +OUI:9802D84* + ID_OUI_FROM_DATABASE=Zedi, Inc. + +OUI:28FD808* + ID_OUI_FROM_DATABASE=Jasco Products Company + +OUI:28FD801* + ID_OUI_FROM_DATABASE=Galileo, Inc. + +OUI:2C265F8* + ID_OUI_FROM_DATABASE=Itus Networks, LLC + +OUI:0CEFAF9* + ID_OUI_FROM_DATABASE=Rotel + +OUI:0CEFAF6* + ID_OUI_FROM_DATABASE=Firmware Design AS + +OUI:A44F299* + ID_OUI_FROM_DATABASE=Certi Networks Sdn Bhd + +OUI:A44F293* + ID_OUI_FROM_DATABASE=Comsel System Ltd + +OUI:A44F295* + ID_OUI_FROM_DATABASE=Shanghai KuanYu Industrial Network Equipment Co.,Ltd + +OUI:100723E* + ID_OUI_FROM_DATABASE=First Chair Acoustics Co., Ltd. + +OUI:1007234* + ID_OUI_FROM_DATABASE=Audio Engineering Ltd. + +OUI:2C265F5* + ID_OUI_FROM_DATABASE=Motec GmbH + +OUI:F802785* + ID_OUI_FROM_DATABASE=Electric Objects + +OUI:A0BB3E0* + ID_OUI_FROM_DATABASE=Link Labs + +OUI:28FD80B* + ID_OUI_FROM_DATABASE=Poket Hardware GmbH + +OUI:3C39E73* + ID_OUI_FROM_DATABASE=ELSA Japan Inc. + +OUI:E4956E8* + ID_OUI_FROM_DATABASE=PT.MLWTelecom + +OUI:E4956E7* + ID_OUI_FROM_DATABASE=NationalchipKorea + +OUI:E4956E2* + ID_OUI_FROM_DATABASE=Shanghai Hoping Technology Co., Ltd. + +OUI:E4956E1* + ID_OUI_FROM_DATABASE=Tband srl + +OUI:E818637* + ID_OUI_FROM_DATABASE=Siliconcube + +OUI:E818630* + ID_OUI_FROM_DATABASE=DigiMagus Technology (Shenzhen) Co., Ltd + +OUI:B8D8129* + ID_OUI_FROM_DATABASE=Entotem LTD + +OUI:74E14AC* + ID_OUI_FROM_DATABASE=Wuhan Shenghong Laser Projection Technology Co.,LTD + +OUI:B01F81E* + ID_OUI_FROM_DATABASE=Advanced & Wise Technology Corp. + +OUI:B01F81A* + ID_OUI_FROM_DATABASE=Steffens Systems GmbH + +OUI:D02212A* + ID_OUI_FROM_DATABASE=GNS-GmbH + +OUI:D022129* + ID_OUI_FROM_DATABASE=UAB "SALDA" + +OUI:D022124* + ID_OUI_FROM_DATABASE=Viatron GmbH + +OUI:D022120* + ID_OUI_FROM_DATABASE=Spirit IT B.V. + +OUI:E81863A* + ID_OUI_FROM_DATABASE=JDM Mobile Internet Solution(Shanghai) Co., Ltd. + +OUI:BC66413* + ID_OUI_FROM_DATABASE=Solectria Renewables, LLC + +OUI:58FCDB6* + ID_OUI_FROM_DATABASE=Timex Group USA Inc + +OUI:58FCDB7* + ID_OUI_FROM_DATABASE=Open Roads Consulting, Inc. + +OUI:BC6641B* + ID_OUI_FROM_DATABASE=Sidus Novum Sp. z o. o. + +OUI:74E14A8* + ID_OUI_FROM_DATABASE=aritec gmbh + +OUI:74E14A1* + ID_OUI_FROM_DATABASE=Cerevo Inc. + +OUI:D07650C* + ID_OUI_FROM_DATABASE=Electro-Motive Diesel + +OUI:D076507* + ID_OUI_FROM_DATABASE=ENCORED Technologies, Inc. + +OUI:D076505* + ID_OUI_FROM_DATABASE=Annapurna Labs + +OUI:141FBA6* + ID_OUI_FROM_DATABASE=Thales Communications & Security SAS + +OUI:7C70BC2* + ID_OUI_FROM_DATABASE=Digital Lumens + +OUI:BC3400E* + ID_OUI_FROM_DATABASE=LLD Technology Ltd. + +OUI:BC3400D* + ID_OUI_FROM_DATABASE=Hangzhou Linker Digital Technology Co., Ltd + +OUI:BC34009* + ID_OUI_FROM_DATABASE=Shenzhen PHilorise Technical Limited + +OUI:A43BFA9* + ID_OUI_FROM_DATABASE=SHEN ZHEN PASUN TECH CO.LTD. + +OUI:A43BFA5* + ID_OUI_FROM_DATABASE=BOI Solutions + +OUI:A43BFA1* + ID_OUI_FROM_DATABASE=Beijing Uniwill Science and Technology Co,Ltd + +OUI:D076501* + ID_OUI_FROM_DATABASE=DAIKEN AUTOMACAO LTDA + +OUI:7419F8C* + ID_OUI_FROM_DATABASE=Bach Icon ApS + +OUI:7C70BCC* + ID_OUI_FROM_DATABASE=Lukup Media + +OUI:7C70BC6* + ID_OUI_FROM_DATABASE=Bidgely + +OUI:F40E11B* + ID_OUI_FROM_DATABASE=BRADAR INDUSTRIA SA + +OUI:F40E117* + ID_OUI_FROM_DATABASE=Shenzhen Grandsun Electronic Co.,Ltd. + +OUI:F40E115* + ID_OUI_FROM_DATABASE=E-SONG + +OUI:F40E110* + ID_OUI_FROM_DATABASE=realphone technology co.,ltd + +OUI:BC34004* + ID_OUI_FROM_DATABASE=Dexcel Design Pvt Ltd + +OUI:B01F818* + ID_OUI_FROM_DATABASE=Technion Oy + +OUI:7419F8A* + ID_OUI_FROM_DATABASE=Tanjarine + +OUI:7419F83* + ID_OUI_FROM_DATABASE=Essential Trading Systems Corp + +OUI:7419F80* + ID_OUI_FROM_DATABASE=Marmitek + +OUI:1C8779E* + ID_OUI_FROM_DATABASE=ASSYSTEM France + +OUI:1C88791* + ID_OUI_FROM_DATABASE=ANDRA Sp. z o.o. + +OUI:1C87744* + ID_OUI_FROM_DATABASE=Weber Marking Systems GmbH + +OUI:1C8774C* + ID_OUI_FROM_DATABASE=New Nordic Engineering + +OUI:1C88797* + ID_OUI_FROM_DATABASE=Sensys Networks, Inc. + +OUI:1C8879B* + ID_OUI_FROM_DATABASE=gekartel AG + +OUI:78CA836* + ID_OUI_FROM_DATABASE=Nomiku + +OUI:78CA832* + ID_OUI_FROM_DATABASE=APC + +OUI:78CA838* + ID_OUI_FROM_DATABASE=IHM + +OUI:78CA839* + ID_OUI_FROM_DATABASE=Louroe Electronics + +OUI:78CA83C* + ID_OUI_FROM_DATABASE=Elanview Technology Co.,Ltd + +OUI:38B8EB8* + ID_OUI_FROM_DATABASE=CeeNex Inc + +OUI:38B8EBD* + ID_OUI_FROM_DATABASE=Yellowbrick Data, Inc. + +OUI:38B8EB6* + ID_OUI_FROM_DATABASE=MATRIXSTREAM TECHNOLOGIES, INC. + +OUI:38B8EB2* + ID_OUI_FROM_DATABASE=barox Kommunikation GmbH + +OUI:38B8EBC* + ID_OUI_FROM_DATABASE=Ajax Systems Inc + +OUI:38B8EBE* + ID_OUI_FROM_DATABASE=Wyres SAS + +OUI:38FDFE1* + ID_OUI_FROM_DATABASE=WAYTONE (BEIIJNG) COMMUNICATIONS CO.,LTD + +OUI:38FDFEE* + ID_OUI_FROM_DATABASE=iSmart electronic technology co.,LTD + +OUI:38FDFE8* + ID_OUI_FROM_DATABASE=Indra Navia AS + +OUI:5CF2864* + ID_OUI_FROM_DATABASE=CHIPSEN Co.,Ltd. + +OUI:5CF2868* + ID_OUI_FROM_DATABASE=SHENZHEN HIVT TECHNOLOGY CO.,LTD + +OUI:7C477C9* + ID_OUI_FROM_DATABASE=DaLian Cheering Tech Co.,Ltd + +OUI:7C477C0* + ID_OUI_FROM_DATABASE=BungBungame Inc + +OUI:7C477C4* + ID_OUI_FROM_DATABASE=RLC Electronics Systems + +OUI:7C477CE* + ID_OUI_FROM_DATABASE=I-Convergence.com + +OUI:986D35D* + ID_OUI_FROM_DATABASE=Praesideo B.V. + +OUI:986D358* + ID_OUI_FROM_DATABASE=Beijing 3CAVI Tech Co.,Ltd + +OUI:986D350* + ID_OUI_FROM_DATABASE=Shenzhen MALATA Mobile Communication Co.,LTD + +OUI:986D354* + ID_OUI_FROM_DATABASE=blossom communications corp. + +OUI:50FF998* + ID_OUI_FROM_DATABASE=Dolphin Concepts Limited + +OUI:50FF99D* + ID_OUI_FROM_DATABASE=Shenzhen Haipengxin Electronic Co., Ltd. + +OUI:50FF993* + ID_OUI_FROM_DATABASE=Yongjing Shanghai Electronic Science and Technology + +OUI:50FF992* + ID_OUI_FROM_DATABASE=SHENZHEN KINGVT ELECTRONICS CO.,LTD + +OUI:E0B6F5A* + ID_OUI_FROM_DATABASE=Folksam AB + +OUI:E0B6F54* + ID_OUI_FROM_DATABASE=Agora + +OUI:E0B6F5E* + ID_OUI_FROM_DATABASE=Advatek Lighting Pty Ltd + +OUI:6891D01* + ID_OUI_FROM_DATABASE=Multi Alarm Zrt. + +OUI:6891D0C* + ID_OUI_FROM_DATABASE=Spraying Systems Co. + +OUI:C47C8D8* + ID_OUI_FROM_DATABASE=GETEMED Medizin- und Informationstechnik AG + +OUI:C47C8DB* + ID_OUI_FROM_DATABASE=GC AUTOMATION CO,LTD + +OUI:C47C8DA* + ID_OUI_FROM_DATABASE=Silvus technologies inc + +OUI:CCD31E0* + ID_OUI_FROM_DATABASE=SAMIM Co + +OUI:CCD31EC* + ID_OUI_FROM_DATABASE=Fluidic Energy + +OUI:CCD31E5* + ID_OUI_FROM_DATABASE=NTmore.Co.,Ltd + +OUI:CCD31E9* + ID_OUI_FROM_DATABASE=Siemens AG, MO MLT BG + +OUI:D0D94FB* + ID_OUI_FROM_DATABASE=MAX Smart Home, LLC + +OUI:D0D94F5* + ID_OUI_FROM_DATABASE=Optigo Networks + +OUI:D0D94F2* + ID_OUI_FROM_DATABASE=Teco Image Systems Co., Ltd. + +OUI:8C192D3* + ID_OUI_FROM_DATABASE=Greenfield Technology + +OUI:8C192D1* + ID_OUI_FROM_DATABASE=Shenzhen Huanuo Internet Technology Co.,Ltd + +OUI:D02212F* + ID_OUI_FROM_DATABASE=Private + +OUI:100723F* + ID_OUI_FROM_DATABASE=Private + +OUI:B0C5CAF* + ID_OUI_FROM_DATABASE=Private + +OUI:2836383* + ID_OUI_FROM_DATABASE=Sabinetek + +OUI:2836380* + ID_OUI_FROM_DATABASE=Knowles Electronics LLC + +OUI:8C192D7* + ID_OUI_FROM_DATABASE=SRETT + +OUI:7C70BCF* + ID_OUI_FROM_DATABASE=Private + +OUI:A43BFAF* + ID_OUI_FROM_DATABASE=Private + +OUI:B01F81F* + ID_OUI_FROM_DATABASE=Private + +OUI:0055DAF* + ID_OUI_FROM_DATABASE=Private + +OUI:2836381* + ID_OUI_FROM_DATABASE=Panasonic System Solutions Europe + +OUI:F0ACD7B* + ID_OUI_FROM_DATABASE=Zhejiang Makepower Electronics,Inc. + +OUI:84E0F48* + ID_OUI_FROM_DATABASE=RAY Co.,LTD + +OUI:84E0F49* + ID_OUI_FROM_DATABASE=SHENZHEN HCN.ELECTRONICS CO.,LTD. + +OUI:84E0F4A* + ID_OUI_FROM_DATABASE=iSolution Technologies Co.,Ltd. + +OUI:70F8E74* + ID_OUI_FROM_DATABASE=CLIP Inc. + +OUI:70F8E70* + ID_OUI_FROM_DATABASE=SHENZHEN Xin JiuNing Electronics Co Ltd + +OUI:70F8E79* + ID_OUI_FROM_DATABASE=Kontech Electronics Co., Ltd + +OUI:F81D78C* + ID_OUI_FROM_DATABASE=SHENZHUOYUE TECHNOLOGY.,LTD + +OUI:70F8E71* + ID_OUI_FROM_DATABASE=System Level Solutions (India) Pvt. + +OUI:F81D78A* + ID_OUI_FROM_DATABASE=AVPro Global Holdings LLC + +OUI:383A21B* + ID_OUI_FROM_DATABASE=Pactron + +OUI:383A214* + ID_OUI_FROM_DATABASE=Dongguan Innovation Technology Co Ltd + +OUI:383A21A* + ID_OUI_FROM_DATABASE=Foresight Sports + +OUI:383A218* + ID_OUI_FROM_DATABASE=Alicat Scientific + +OUI:AC64DD1* + ID_OUI_FROM_DATABASE=JSC InfoTeCS + +OUI:383A21E* + ID_OUI_FROM_DATABASE=SDNware technology co.,LTD + +OUI:AC64DD4* + ID_OUI_FROM_DATABASE=8Cups + +OUI:AC64DDC* + ID_OUI_FROM_DATABASE=Beijing Hamigua Technology Co., Ltd. + +OUI:4CE1730* + ID_OUI_FROM_DATABASE=Beijing Sutongwang E-Business Co., Ltd + +OUI:F0ACD72* + ID_OUI_FROM_DATABASE=QUANTUM POWER SYSTEMS + +OUI:F0ACD71* + ID_OUI_FROM_DATABASE=Intenta GmbH + +OUI:58E8769* + ID_OUI_FROM_DATABASE=TEM Mobile Limited + +OUI:58E8765* + ID_OUI_FROM_DATABASE=Broad Air Technology Co., LTD. + +OUI:C0D3913* + ID_OUI_FROM_DATABASE=IXON B.V. + +OUI:C0D3916* + ID_OUI_FROM_DATABASE=Ernitec + +OUI:C0D3912* + ID_OUI_FROM_DATABASE=Hofon Automation Co.,Ltd + +OUI:C0D391B* + ID_OUI_FROM_DATABASE=Private + +OUI:84E0F40* + ID_OUI_FROM_DATABASE=ShenZhen Panrich Technology Limited + +OUI:AC64DD5* + ID_OUI_FROM_DATABASE=SHANGHAI ZTE TECHNOLOGIES CO.,LTD + +OUI:AC64DD8* + ID_OUI_FROM_DATABASE=PFDC ELANCYL + +OUI:AC64DDE* + ID_OUI_FROM_DATABASE=DIGIBIRD TECHNOLOGY CO., LTD. + +OUI:4CE1736* + ID_OUI_FROM_DATABASE=CHINA CNR CORPORATION LIMITED DALIAN ELECTRIC TRACTION R&D CENTER + +OUI:4CE173D* + ID_OUI_FROM_DATABASE=KTC(K-TEL) + +OUI:4CE173E* + ID_OUI_FROM_DATABASE=Plus One Japan Limited + +OUI:1C87765* + ID_OUI_FROM_DATABASE=Zhuhai MYZR Technology Co.,Ltd + +OUI:1C87793* + ID_OUI_FROM_DATABASE=Visual Land Inc. + +OUI:1C87790* + ID_OUI_FROM_DATABASE=Wurm GmbH & Co. KG Elektronische Systeme + +OUI:1C87797* + ID_OUI_FROM_DATABASE=TASC Systems Inc. + +OUI:1C87796* + ID_OUI_FROM_DATABASE=Shenzhen Shouxin Tongda Technology Co.,Ltd + +OUI:1C8779C* + ID_OUI_FROM_DATABASE=AllThingsTalk + +OUI:1C8776A* + ID_OUI_FROM_DATABASE=Jiangsu ETERN COMMUNICATION Co.,ltd + +OUI:40A36B0* + ID_OUI_FROM_DATABASE=Fin Robotics Inc + +OUI:70886B9* + ID_OUI_FROM_DATABASE=Shenzhen Coolhear Information Technology Co., Ltd. + +OUI:70886B4* + ID_OUI_FROM_DATABASE=HORI CO., LTD. + +OUI:70886B5* + ID_OUI_FROM_DATABASE=Chengdu Ophylink Communication Technology Ltd. + +OUI:70886B0* + ID_OUI_FROM_DATABASE=Veracity UK Ltd + +OUI:8439BE2* + ID_OUI_FROM_DATABASE=Cheng Du virtual world Technology Limited. + +OUI:70886BA* + ID_OUI_FROM_DATABASE=RHXTune Technology Co.,Ltd + +OUI:70886BC* + ID_OUI_FROM_DATABASE=MAX4G, Inc. + +OUI:800A804* + ID_OUI_FROM_DATABASE=LLVISION TECHNOLOGY CO.,LTD + +OUI:1C21D14* + ID_OUI_FROM_DATABASE=Scientific-Production Enterprise Dynamics + +OUI:1C21D11* + ID_OUI_FROM_DATABASE=Ognios GmbH + +OUI:DC4427E* + ID_OUI_FROM_DATABASE=VerifEye Technologies + +OUI:DC44278* + ID_OUI_FROM_DATABASE=Wharton Electronics Ltd + +OUI:DC44274* + ID_OUI_FROM_DATABASE=Nex Technologies PTY LTD + +OUI:B0C5CA6* + ID_OUI_FROM_DATABASE=SunTech Medical, Inc. + +OUI:1C21D1E* + ID_OUI_FROM_DATABASE=p2-plus inc. + +OUI:1C21D1B* + ID_OUI_FROM_DATABASE=Global Design Solutions Ltd + +OUI:A03E6B6* + ID_OUI_FROM_DATABASE=Wuhan Rui Ying Tong Network Technology Co., Ltd(China) + +OUI:A03E6B5* + ID_OUI_FROM_DATABASE=Friday Lab, UAB + +OUI:C88ED1E* + ID_OUI_FROM_DATABASE=Aventics GmbH + +OUI:A03E6B0* + ID_OUI_FROM_DATABASE=s&t embedded GmbH + +OUI:C88ED18* + ID_OUI_FROM_DATABASE=Electronic Controls Design, Inc. + +OUI:CC1BE04* + ID_OUI_FROM_DATABASE=Laserworld (Switzerland) AG + +OUI:CC1BE03* + ID_OUI_FROM_DATABASE=Shenzhen Vanstor Technology Co.,Ltd + +OUI:0055DAE* + ID_OUI_FROM_DATABASE=Victorsure Limited + +OUI:0055DA8* + ID_OUI_FROM_DATABASE=BroadSoft, Inc. + +OUI:0055DA4* + ID_OUI_FROM_DATABASE=Datapath Limited + +OUI:0055DA9* + ID_OUI_FROM_DATABASE=Quantum Communication Technology Co., Ltd.,Anhui + +OUI:0055DA3* + ID_OUI_FROM_DATABASE=Novexx Solutions GmbH + +OUI:A03E6BB* + ID_OUI_FROM_DATABASE=KoCoS Messtechnik AG + +OUI:DC44272* + ID_OUI_FROM_DATABASE=Skywave Technology Co,.Ltd. + +OUI:B0C5CAC* + ID_OUI_FROM_DATABASE=XMetrics + +OUI:CC1BE0D* + ID_OUI_FROM_DATABASE=NEWSTAR (HK) ELECTRONIC DEVELOPMENT LIMITED + +OUI:CC1BE09* + ID_OUI_FROM_DATABASE=MobiStor Technology Inc. + +OUI:74F8DBA* + ID_OUI_FROM_DATABASE=Ballard Technology Inc. + +OUI:74F8DB4* + ID_OUI_FROM_DATABASE=WiFi Hotspots, SL + +OUI:74F8DB0* + ID_OUI_FROM_DATABASE=Enercon Technologies + +OUI:885D90E* + ID_OUI_FROM_DATABASE=Unitac Technology Limited + +OUI:885D90C* + ID_OUI_FROM_DATABASE=iRoom GmbH + +OUI:B437D1B* + ID_OUI_FROM_DATABASE=NSI Co., Ltd. + +OUI:B437D16* + ID_OUI_FROM_DATABASE=Yireh Auto Tech Co.,Ltd. + +OUI:B437D12* + ID_OUI_FROM_DATABASE=Fibersystem AB + +OUI:B437D11* + ID_OUI_FROM_DATABASE=Alturna Networks + +OUI:74F8DBE* + ID_OUI_FROM_DATABASE=Bernard Krone Holding GmbH & Co. KG + +OUI:885D908* + ID_OUI_FROM_DATABASE=Creative Sensor Inc. + +OUI:885D902* + ID_OUI_FROM_DATABASE=DAIDONG Industrial System Co., Ltd. + +OUI:B0C5CA1* + ID_OUI_FROM_DATABASE=IVK-SAYANY + +OUI:78C2C0B* + ID_OUI_FROM_DATABASE=Wan Chao An (Beijing) Technology Co., Ltd. + +OUI:78C2C07* + ID_OUI_FROM_DATABASE=Guangzhou Hongcai Stage Equipment co.,ltd + +OUI:549A119* + ID_OUI_FROM_DATABASE=Alfen BV + +OUI:549A116* + ID_OUI_FROM_DATABASE=Orient Direct, Inc. + +OUI:549A115* + ID_OUI_FROM_DATABASE=Elotech Industrieelektronik GmbH + +OUI:78C2C04* + ID_OUI_FROM_DATABASE=Ory Laboratory Co., Ltd. + +OUI:80E4DA8* + ID_OUI_FROM_DATABASE=Krizer international Co,. Ltd. + +OUI:80E4DA4* + ID_OUI_FROM_DATABASE=Beijing Yuantel Technolgy Co.,Ltd-Shenzhen Branch + +OUI:80E4DA2* + ID_OUI_FROM_DATABASE=Thurlby Thandar Instruments LTD + +OUI:2CD141D* + ID_OUI_FROM_DATABASE=Private + +OUI:807B85B* + ID_OUI_FROM_DATABASE=Oliotalo Oy + +OUI:807B856* + ID_OUI_FROM_DATABASE=Quickte Technology Co.,Ltd + +OUI:807B851* + ID_OUI_FROM_DATABASE=Hangzhou Synway Information Engineering Co., Ltd + +OUI:1CCAE38* + ID_OUI_FROM_DATABASE=OxySec S.r.l. + +OUI:1CCAE35* + ID_OUI_FROM_DATABASE=TengFeng + +OUI:80E4DAE* + ID_OUI_FROM_DATABASE=Akenori PTE LTD + +OUI:9802D86* + ID_OUI_FROM_DATABASE=Fritz Kuebler GmbH + +OUI:9802D81* + ID_OUI_FROM_DATABASE=SHENZHEN ATEKO PHOTOELECTRICITY CO LTD + +OUI:A0BB3EB* + ID_OUI_FROM_DATABASE=Beijing Techshino Technology Co., Ltd. + +OUI:A0BB3EE* + ID_OUI_FROM_DATABASE=Messtechnik Sachs GmbH + +OUI:2CD1416* + ID_OUI_FROM_DATABASE=Bowei Technology Company Limited + +OUI:2CD1413* + ID_OUI_FROM_DATABASE=AOptix Technologies, Inc + +OUI:2CD1410* + ID_OUI_FROM_DATABASE=iCIRROUND Inc + +OUI:2C6A6F6* + ID_OUI_FROM_DATABASE=Beep, Inc. + +OUI:2C6A6F2* + ID_OUI_FROM_DATABASE=NanChang LangJie Technology Co.,Ltd + +OUI:9802D8C* + ID_OUI_FROM_DATABASE=AGV spa + +OUI:9802D88* + ID_OUI_FROM_DATABASE=Simplo Technology Co.,LTD + +OUI:64FB81C* + ID_OUI_FROM_DATABASE=Bridgeport Instruments, LLC + +OUI:64FB81B* + ID_OUI_FROM_DATABASE=Sichuan Haige Actec Communication Technology Co.,Ltd. + +OUI:64FB817* + ID_OUI_FROM_DATABASE=Securosys SA + +OUI:90C682D* + ID_OUI_FROM_DATABASE=PowerShield Limited + +OUI:90C6828* + ID_OUI_FROM_DATABASE=Teletek Electronics + +OUI:90C6823* + ID_OUI_FROM_DATABASE=Innovative Electronic Technology + +OUI:64FB810* + ID_OUI_FROM_DATABASE=SHANGHAI SIMCOM LIMITED + +OUI:64FB812* + ID_OUI_FROM_DATABASE=Seven Solutions S.L + +OUI:28FD805* + ID_OUI_FROM_DATABASE=Xiaocong Network Limited + +OUI:2C265FD* + ID_OUI_FROM_DATABASE=E Core Corporation + +OUI:2C265FA* + ID_OUI_FROM_DATABASE=Polara Engineering + +OUI:2C265F7* + ID_OUI_FROM_DATABASE=Coremate Technical Co., Ltd + +OUI:2C265F3* + ID_OUI_FROM_DATABASE=shenzhen Clever Electronic Co., Ltd. + +OUI:2C265F0* + ID_OUI_FROM_DATABASE=XIAMEN VORLINK IOT TECHNOLOGY CO.,LTD. + +OUI:F80278D* + ID_OUI_FROM_DATABASE=Dueton Systems s.r.o. + +OUI:0CEFAFB* + ID_OUI_FROM_DATABASE=Hubei Century Network Technology Co .Ltd + +OUI:A44F291* + ID_OUI_FROM_DATABASE=Olssen B.V. + +OUI:3C39E79* + ID_OUI_FROM_DATABASE=Zone Controls AB + +OUI:3C39E76* + ID_OUI_FROM_DATABASE=RO.VE.R. Laboratories S.p.A + +OUI:100723A* + ID_OUI_FROM_DATABASE=TESSERA TECHNOLOGY INC. + +OUI:1007236* + ID_OUI_FROM_DATABASE=ESTONE TECHNOLOGY INC + +OUI:1007233* + ID_OUI_FROM_DATABASE=Tongfang computer co.Ltd. + +OUI:F802788* + ID_OUI_FROM_DATABASE=EMBUX Technology Co., Ltd. + +OUI:F802783* + ID_OUI_FROM_DATABASE=3Shape Holding A/S + +OUI:F802781* + ID_OUI_FROM_DATABASE=Reason Tecnologia SA + +OUI:1007230* + ID_OUI_FROM_DATABASE=RippleTek Tech Ltd + +OUI:D02212C* + ID_OUI_FROM_DATABASE=Xperio Labs Ltd. + +OUI:0CEFAF1* + ID_OUI_FROM_DATABASE=Goerlitz AG + +OUI:A44F29B* + ID_OUI_FROM_DATABASE=GUANGDONG REAL-DESIGN INTELLIGENT TECHNOLOGY CO.,LTD + +OUI:A44F296* + ID_OUI_FROM_DATABASE=Selektro Power Inc + +OUI:A0BB3E7* + ID_OUI_FROM_DATABASE=SIMTEC Elektronik GmbH + +OUI:A0BB3E2* + ID_OUI_FROM_DATABASE=DirectOut GmbH + +OUI:D022127* + ID_OUI_FROM_DATABASE=Cliptech Industria e Comercio Ltda + +OUI:E81863C* + ID_OUI_FROM_DATABASE=Shenzhen Hipad Telecommunication Technology Co.,Ltd + +OUI:B01F81C* + ID_OUI_FROM_DATABASE=Access Device Integrated Communications Corp. + +OUI:B01F816* + ID_OUI_FROM_DATABASE=COMOTA Co., Ltd. + +OUI:58FCDB9* + ID_OUI_FROM_DATABASE=Hi-Target Surveying Instrument Co., Ltd. + +OUI:58FCDB5* + ID_OUI_FROM_DATABASE=Shenzhen Siecom Communication Technology Development Co.,Ltd. + +OUI:58FCDB2* + ID_OUI_FROM_DATABASE=Beseye Cloud Security Co. Ltd. + +OUI:E4956E5* + ID_OUI_FROM_DATABASE=ELAN SYSTEMS + +OUI:BC6641E* + ID_OUI_FROM_DATABASE=Lucent Trans Electronics Co., Ltd + +OUI:E818634* + ID_OUI_FROM_DATABASE=Guangzhou Tianyi Electronics Co., Ltd + +OUI:E818633* + ID_OUI_FROM_DATABASE=DongGuan Pengxun Electronics Technology Co., Ltd. + +OUI:B8D812E* + ID_OUI_FROM_DATABASE=ZheJiang FangTai Electirc Co., Ltd + +OUI:E4956EC* + ID_OUI_FROM_DATABASE=Shenzhen Arronna Telecom Co.,Ltd + +OUI:E4956EB* + ID_OUI_FROM_DATABASE=iConservo Inc + +OUI:BC66415* + ID_OUI_FROM_DATABASE=Scientific Games + +OUI:BC66410* + ID_OUI_FROM_DATABASE=InSync Technology Ltd + +OUI:58FCDBC* + ID_OUI_FROM_DATABASE=Excenon Mobile Technology Co., Ltd. + +OUI:74E14A4* + ID_OUI_FROM_DATABASE=open joint stock company YUG-SISTEMA plus + +OUI:B8D812A* + ID_OUI_FROM_DATABASE=Kiwigrid GmbH + +OUI:B8D8127* + ID_OUI_FROM_DATABASE=Neuropace Inc. + +OUI:B8D8123* + ID_OUI_FROM_DATABASE=iModesty Technology Corp. + +OUI:74E14AE* + ID_OUI_FROM_DATABASE=Diamond Kinetics + +OUI:A43BFAD* + ID_OUI_FROM_DATABASE=JSC “Component-ASU” + +OUI:A43BFA6* + ID_OUI_FROM_DATABASE=Recognition Systems LLC + +OUI:A43BFA3* + ID_OUI_FROM_DATABASE=Circus World Displays Ltd + +OUI:D076509* + ID_OUI_FROM_DATABASE=Greenwave Scientific + +OUI:BC3400A* + ID_OUI_FROM_DATABASE=AURALIC LIMITED + +OUI:BC34006* + ID_OUI_FROM_DATABASE=Cameron + +OUI:BC34002* + ID_OUI_FROM_DATABASE=LifeSmart + +OUI:7C70BC4* + ID_OUI_FROM_DATABASE=K-Vision Technology (Shanghai), Ltd + +OUI:B01F812* + ID_OUI_FROM_DATABASE=Private + +OUI:F40E11D* + ID_OUI_FROM_DATABASE=DXG Technology Corp. + +OUI:F40E119* + ID_OUI_FROM_DATABASE=Sterna Security + +OUI:141FBA4* + ID_OUI_FROM_DATABASE=BYZERO + +OUI:141FBA0* + ID_OUI_FROM_DATABASE=Shenzhen Mining Technology Co.,Ltd. + +OUI:7C70BCA* + ID_OUI_FROM_DATABASE=Ametek VIS + +OUI:F40E113* + ID_OUI_FROM_DATABASE=Shenzhen headsun technology + +OUI:141FBAE* + ID_OUI_FROM_DATABASE=POS Systema LLC + +OUI:141FBAA* + ID_OUI_FROM_DATABASE=Winsonic Electronics Co., Ltd. + +OUI:7419F88* + ID_OUI_FROM_DATABASE=Quest Payment Systems + +OUI:7419F8E* + ID_OUI_FROM_DATABASE=Volacomm Co., Ltd + +OUI:7419F84* + ID_OUI_FROM_DATABASE=Cloudvue Technologies Corporation + +OUI:1C87742* + ID_OUI_FROM_DATABASE=Nichigaku + +OUI:1C87747* + ID_OUI_FROM_DATABASE=Ing Buero Ziegler + +OUI:1C88792* + ID_OUI_FROM_DATABASE=Airsmart System Co.,Ltd + +OUI:1C8879D* + ID_OUI_FROM_DATABASE=Beijing Raycores Technology Co.,Ltd + +OUI:78CA833* + ID_OUI_FROM_DATABASE=Neofon GmbH + +OUI:78CA831* + ID_OUI_FROM_DATABASE=Excelocity Inc. + +OUI:78CA837* + ID_OUI_FROM_DATABASE=Beijing CarePulse Electronic Technology + +OUI:78CA83D* + ID_OUI_FROM_DATABASE=Hubei Boyuan Zhijia Network Media Co. Ltd. + +OUI:38B8EB3* + ID_OUI_FROM_DATABASE=Aina Wireless Inc + +OUI:38FDFED* + ID_OUI_FROM_DATABASE=FUBA Automotive Electronics GmbH + +OUI:38FDFE7* + ID_OUI_FROM_DATABASE=Rademacher Geraete-Elektronik GmbH + +OUI:38FDFE3* + ID_OUI_FROM_DATABASE=Siemens AG, PG IE R&D + +OUI:38FDFE0* + ID_OUI_FROM_DATABASE=Edge I&D Co., Ltd. + +OUI:5CF286B* + ID_OUI_FROM_DATABASE=Itron UK Limited + +OUI:7C477C2* + ID_OUI_FROM_DATABASE=POWERLAND LIMITED + +OUI:5CF286D* + ID_OUI_FROM_DATABASE=BrightSky, LLC + +OUI:7C477C3* + ID_OUI_FROM_DATABASE=EyeLock LLC + +OUI:7C477CB* + ID_OUI_FROM_DATABASE=Hangzhou Yiyitaidi Information Technology Co., Ltd. + +OUI:986D351* + ID_OUI_FROM_DATABASE=Shenzhen cositea electronics technology co.,LTD + +OUI:986D35C* + ID_OUI_FROM_DATABASE=my-PV GmbH + +OUI:986D35E* + ID_OUI_FROM_DATABASE=BAYCOM OPTO-ELECTRONICS TECHNOLGY CO., LTD. + +OUI:50FF994* + ID_OUI_FROM_DATABASE=IPC Global + +OUI:50FF996* + ID_OUI_FROM_DATABASE=LEGEND WINNER LIMITED + +OUI:50FF99A* + ID_OUI_FROM_DATABASE=metraTec GmbH + +OUI:50FF99E* + ID_OUI_FROM_DATABASE=Informa LLC + +OUI:E0B6F5D* + ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED + +OUI:E0B6F53* + ID_OUI_FROM_DATABASE=Huizhou GISUN Industrial CO. LTD + +OUI:6891D04* + ID_OUI_FROM_DATABASE=G-TECH Instruments Inc. + +OUI:6891D00* + ID_OUI_FROM_DATABASE=Central Railway Manufacturing + +OUI:6891D02* + ID_OUI_FROM_DATABASE=Shenzhen NeaTech Intelligence Technology Co., Ltd. + +OUI:6891D0A* + ID_OUI_FROM_DATABASE=WiseCube + +OUI:6891D0B* + ID_OUI_FROM_DATABASE=Altis Technology + +OUI:C47C8D5* + ID_OUI_FROM_DATABASE=PASCAL Co., Ltd. + +OUI:C47C8D9* + ID_OUI_FROM_DATABASE=Airbus DS - SLC + +OUI:C47C8D2* + ID_OUI_FROM_DATABASE=Star2Star Communications, LLC + +OUI:C47C8D6* + ID_OUI_FROM_DATABASE=HHCC Plant Technology Co.,Ltd. + +OUI:CCD31EB* + ID_OUI_FROM_DATABASE=Elk Products + +OUI:CCD31E2* + ID_OUI_FROM_DATABASE=Neptune Systems + +OUI:CCD31E4* + ID_OUI_FROM_DATABASE=PJG Systementwicklung GmbH + +OUI:CCD31EE* + ID_OUI_FROM_DATABASE=ShenZhenBoryNet Co.,LTD. + +OUI:CCD31E7* + ID_OUI_FROM_DATABASE=Shenzhen Decnta Technology Co.,LTD. + +OUI:D0D94F4* + ID_OUI_FROM_DATABASE=peiker CEE + +OUI:8C192DC* + ID_OUI_FROM_DATABASE=You Zhengcheng co.,ltd + +OUI:8C192D9* + ID_OUI_FROM_DATABASE=ViaWear, Inc. + +OUI:BC3400F* + ID_OUI_FROM_DATABASE=Private + +OUI:7419F8F* + ID_OUI_FROM_DATABASE=Private + +OUI:1C21D1F* + ID_OUI_FROM_DATABASE=Private + +OUI:DC4427F* + ID_OUI_FROM_DATABASE=Private + +OUI:C88ED1F* + ID_OUI_FROM_DATABASE=Private + +OUI:80E4DAF* + ID_OUI_FROM_DATABASE=Private + +OUI:885D90F* + ID_OUI_FROM_DATABASE=Private + +OUI:2836385* + ID_OUI_FROM_DATABASE=CHARGELIB + +OUI:283638B* + ID_OUI_FROM_DATABASE=ShangHai Canall Information Technology Co.,Ltd + +OUI:DC4427D* + ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA + +OUI:F0ACD78* + ID_OUI_FROM_DATABASE=Telefonix Incorporated + +OUI:F0ACD76* + ID_OUI_FROM_DATABASE=Suzhou Pairlink Network Technology + +OUI:F0ACD79* + ID_OUI_FROM_DATABASE=U3storage Technologies Co., Ltd + +OUI:F0ACD7C* + ID_OUI_FROM_DATABASE=Simprints Technology Ltd + +OUI:58E8763* + ID_OUI_FROM_DATABASE=McWong International Inc + +OUI:58E8767* + ID_OUI_FROM_DATABASE=Chronos Technology Ltd. + +OUI:58E876C* + ID_OUI_FROM_DATABASE=KUSTOM SIGNALS INC + +OUI:58E876E* + ID_OUI_FROM_DATABASE=Baoruh Electronic Co., Ltd. + +OUI:58E876D* + ID_OUI_FROM_DATABASE=Xiamen Cacamle Technology Co.,Ltd. + +OUI:C0D3915* + ID_OUI_FROM_DATABASE=WiTagg, Inc + +OUI:C0D391D* + ID_OUI_FROM_DATABASE=REGULUS CO.,LTD. + +OUI:58E8764* + ID_OUI_FROM_DATABASE=PROBIT SRL + +OUI:C0D3911* + ID_OUI_FROM_DATABASE=B9Creations + +OUI:C0D3910* + ID_OUI_FROM_DATABASE=Fuzhou Jinshi Technology Co.,Ltd. + +OUI:C0D391A* + ID_OUI_FROM_DATABASE=Alpha Audiotronics, Inc. + +OUI:84E0F47* + ID_OUI_FROM_DATABASE=Dantherm + +OUI:84E0F4E* + ID_OUI_FROM_DATABASE=Scale-Tec Ltd. + +OUI:70F8E72* + ID_OUI_FROM_DATABASE=VOXX International + +OUI:70F8E7A* + ID_OUI_FROM_DATABASE=TiVACI CORPORATION PTE LTD + +OUI:70F8E76* + ID_OUI_FROM_DATABASE=Flexim Security Oy + +OUI:F81D788* + ID_OUI_FROM_DATABASE=TELEOFIS + +OUI:F81D785* + ID_OUI_FROM_DATABASE=DACONS + +OUI:F81D78B* + ID_OUI_FROM_DATABASE=SigmaConnectivityAB + +OUI:F81D780* + ID_OUI_FROM_DATABASE=Dongguan Shun Hing Plastics Limited + +OUI:F81D78E* + ID_OUI_FROM_DATABASE=GUANGDONG ENOK COMMUNICATION CO., LTD. + +OUI:383A215* + ID_OUI_FROM_DATABASE=OOO NPP Uraltechnologiya + +OUI:383A21C* + ID_OUI_FROM_DATABASE=Mission Embedded GmbH + +OUI:383A219* + ID_OUI_FROM_DATABASE=Skylark Wireless LLC + +OUI:AC64DD2* + ID_OUI_FROM_DATABASE=Shenzhen PuHua Technology Co., Ltd + +OUI:AC64DDD* + ID_OUI_FROM_DATABASE=HMicro Inc + +OUI:AC64DD0* + ID_OUI_FROM_DATABASE=Jia-Teng + +OUI:4CE1731* + ID_OUI_FROM_DATABASE=Datastorm Technologies Inc. + +OUI:4CE1733* + ID_OUI_FROM_DATABASE=outpaceIO + +OUI:4CE173C* + ID_OUI_FROM_DATABASE=REMONDE NETWORK + +OUI:1C8776B* + ID_OUI_FROM_DATABASE=Hekatron Vertriebs GmbH + +OUI:1C8779A* + ID_OUI_FROM_DATABASE=Hangzhou Xiaowen Intelligent Technology Co., Ltd. + +OUI:1C87798* + ID_OUI_FROM_DATABASE=ZHEJIANG ITENAL TECHNOLOGY CO.,LTD + +OUI:1C87763* + ID_OUI_FROM_DATABASE=Unjo AB + +OUI:1C87767* + ID_OUI_FROM_DATABASE=Corporate Systems Engineering + +OUI:1C87769* + ID_OUI_FROM_DATABASE=Tokyo Drawing Ltd. + +OUI:8439BE1* + ID_OUI_FROM_DATABASE=Guangzhou Heygears Technology Ltd + +OUI:40A36B8* + ID_OUI_FROM_DATABASE=SFT Co., Ltd. + +OUI:40A36B1* + ID_OUI_FROM_DATABASE=TW-TeamWare + +OUI:40A36B3* + ID_OUI_FROM_DATABASE=Omnitracs, LLC + +OUI:40A36BC* + ID_OUI_FROM_DATABASE=Onion Corporation + +OUI:40A36BD* + ID_OUI_FROM_DATABASE=FAOD Co.,Ltd. + +OUI:70886B6* + ID_OUI_FROM_DATABASE=Church & Dwight Co., Inc. + +OUI:70886B8* + ID_OUI_FROM_DATABASE=Cable Matters Inc. + +OUI:800A806* + ID_OUI_FROM_DATABASE=Beijing Gooagoo Technical Service Co.,Ltd. + +OUI:800A802* + ID_OUI_FROM_DATABASE=Sumitomo Wiring Systems, Ltd. + +OUI:0055DA5* + ID_OUI_FROM_DATABASE=Nanoleaf + +OUI:0055DA6* + ID_OUI_FROM_DATABASE=OOO DEKATRON + +OUI:0055DA7* + ID_OUI_FROM_DATABASE=LUCISTECHNOLOGIES(SHANGHAI)CO.,LTD + +OUI:800A800* + ID_OUI_FROM_DATABASE=Golana Technology (Shenzhen) Co., Ltd. + +OUI:CC1BE0A* + ID_OUI_FROM_DATABASE=Matter Labs Pty Ltd + +OUI:CC1BE01* + ID_OUI_FROM_DATABASE=Beijing Daotongtianxia Co.Ltd. + +OUI:CC1BE06* + ID_OUI_FROM_DATABASE=IC RealTech + +OUI:A03E6BE* + ID_OUI_FROM_DATABASE=Nanjing zhanyi software technology co., LTD + +OUI:A03E6B2* + ID_OUI_FROM_DATABASE=Videx Electronics S.p.A. + +OUI:A03E6B9* + ID_OUI_FROM_DATABASE=Incogniteam Ltd. + +OUI:B0C5CA8* + ID_OUI_FROM_DATABASE=Astyx GmbH + +OUI:B0C5CAA* + ID_OUI_FROM_DATABASE=TEM Mobile Limited + +OUI:DC4427B* + ID_OUI_FROM_DATABASE=Nautilus Infotech CO., Ltd. + +OUI:DC44277* + ID_OUI_FROM_DATABASE=EcoGuard AB + +OUI:C88ED1D* + ID_OUI_FROM_DATABASE=PHOENIX ENGINEERING CORP. + +OUI:C88ED1A* + ID_OUI_FROM_DATABASE=AP Sensing GmbH + +OUI:C88ED15* + ID_OUI_FROM_DATABASE=Fibergate.Inc + +OUI:1C21D1C* + ID_OUI_FROM_DATABASE=Private + +OUI:C88ED11* + ID_OUI_FROM_DATABASE=German Pipe GmbH + +OUI:1C21D1D* + ID_OUI_FROM_DATABASE=Liscotech System Co., Ltd. + +OUI:1C21D13* + ID_OUI_FROM_DATABASE=Microview Science and Technology Co.,Ltd + +OUI:1C21D12* + ID_OUI_FROM_DATABASE=Varaani Works Oy + +OUI:B0C5CA3* + ID_OUI_FROM_DATABASE=abode systems, inc. + +OUI:78C2C02* + ID_OUI_FROM_DATABASE=RONIX incorporated + +OUI:78C2C00* + ID_OUI_FROM_DATABASE=Shenzhen ELI Technology co.,ltd + +OUI:B437D1D* + ID_OUI_FROM_DATABASE=ZXY Sport Tracking + +OUI:74F8DB7* + ID_OUI_FROM_DATABASE=Wuhan Tianyu Information Industry Co., Ltd. + +OUI:74F8DB2* + ID_OUI_FROM_DATABASE=Shenzhen Ruishi Information Technology Co.,Ltd. + +OUI:885D90A* + ID_OUI_FROM_DATABASE=Shenzhen Speedrun Technologies Co.,Ltd. + +OUI:78C2C09* + ID_OUI_FROM_DATABASE=SES + +OUI:B437D18* + ID_OUI_FROM_DATABASE=eInfochips Limited + +OUI:B437D14* + ID_OUI_FROM_DATABASE=KOMSIS ELEKTRONIK SISTEMLERI SAN. TIC. LTD.STI + +OUI:885D901* + ID_OUI_FROM_DATABASE=ShenZhen Yuyangsheng technology company LTD + +OUI:885D905* + ID_OUI_FROM_DATABASE=Shenzhen JingHanDa Electronics Co.Ltd + +OUI:2CD1414* + ID_OUI_FROM_DATABASE=Shanghai RW ELE&TEC CO.,LTD + +OUI:2CD1415* + ID_OUI_FROM_DATABASE=ZENIC INC. + +OUI:90C682A* + ID_OUI_FROM_DATABASE=Beijing Acorn Networks Corporation + +OUI:90C6826* + ID_OUI_FROM_DATABASE=Nanjing Jiexi Technologies Co., Ltd. + +OUI:64FB81E* + ID_OUI_FROM_DATABASE=ChengDu KeChuang LongXin Sci-tech Co.,Ltd + +OUI:64FB815* + ID_OUI_FROM_DATABASE=Kay Schulze & Karsten Pohle GbR + +OUI:2C6A6FE* + ID_OUI_FROM_DATABASE=EATON FHF Funke + Huster Fernsig GmbH + +OUI:90C6821* + ID_OUI_FROM_DATABASE=Shenzhen Photon Broadband Technology CO., LTD + +OUI:2C6A6F4* + ID_OUI_FROM_DATABASE=TINYCO + +OUI:2C6A6F5* + ID_OUI_FROM_DATABASE=SHEN ZHEN SIS SCIENCE & TECHNOLOGY LTD. + +OUI:1CCAE36* + ID_OUI_FROM_DATABASE=TOKAI RIKA CO., LTD. + +OUI:1CCAE37* + ID_OUI_FROM_DATABASE=Bird Home Automation GmbH + +OUI:80E4DAB* + ID_OUI_FROM_DATABASE=Nanjing LILO Technology Co. Ltd. + +OUI:80E4DAD* + ID_OUI_FROM_DATABASE=Dalian Roiland Technology Co.,Ltd + +OUI:80E4DA6* + ID_OUI_FROM_DATABASE=BroadMedia Co., Ltd. + +OUI:2CD141E* + ID_OUI_FROM_DATABASE=CITA SMART SOLUTIONS LTD + +OUI:549A118* + ID_OUI_FROM_DATABASE=Tite, Inc. + +OUI:549A117* + ID_OUI_FROM_DATABASE=Niveo International BV + +OUI:807B85D* + ID_OUI_FROM_DATABASE=Kaynes Technology India Pvt Ltd + +OUI:807B858* + ID_OUI_FROM_DATABASE=IDair, LLC + +OUI:807B854* + ID_OUI_FROM_DATABASE=Quantel USA, Inc. + +OUI:9802D8A* + ID_OUI_FROM_DATABASE=HySecurity + +OUI:F802786* + ID_OUI_FROM_DATABASE=Witium Co., Ltd + +OUI:0CEFAFD* + ID_OUI_FROM_DATABASE=CJSC «Svyaz Engineering» + +OUI:0CEFAFC* + ID_OUI_FROM_DATABASE=GainStrong Industry Co.,Ltd + +OUI:3C39E71* + ID_OUI_FROM_DATABASE=BEWATEC Kommunikationstechnik GmbH + +OUI:1007239* + ID_OUI_FROM_DATABASE=Wireless input technology Inc. + +OUI:3C39E70* + ID_OUI_FROM_DATABASE=Hannstar Display Corp + +OUI:1007238* + ID_OUI_FROM_DATABASE=Ion Professional Solutions + +OUI:1007237* + ID_OUI_FROM_DATABASE=nanoTech Co., Ltd. + +OUI:D02212E* + ID_OUI_FROM_DATABASE=u::Lux GmbH + +OUI:A44F298* + ID_OUI_FROM_DATABASE=Innovations in Optics, Inc. + +OUI:A44F294* + ID_OUI_FROM_DATABASE=DGC Access AB + +OUI:9802D83* + ID_OUI_FROM_DATABASE=Grammer EiA Electronics nv + +OUI:A0BB3E9* + ID_OUI_FROM_DATABASE=Sandal Plc + +OUI:A0BB3E5* + ID_OUI_FROM_DATABASE=ManTech International Corporation + +OUI:3C39E7A* + ID_OUI_FROM_DATABASE=iiM AG + +OUI:3C39E7B* + ID_OUI_FROM_DATABASE=chipsguide technology Co.,LTD + +OUI:3C39E77* + ID_OUI_FROM_DATABASE=Sensor to Image GmbH + +OUI:3C39E78* + ID_OUI_FROM_DATABASE=Martem AS + +OUI:0CEFAF2* + ID_OUI_FROM_DATABASE=LUMEL S.A. + +OUI:0CEFAF3* + ID_OUI_FROM_DATABASE=Engineering Center ENERGOSERVICE + +OUI:A44F29D* + ID_OUI_FROM_DATABASE=HALLIBURTON + +OUI:28FD804* + ID_OUI_FROM_DATABASE=Digital Signal Corp + +OUI:2C265FB* + ID_OUI_FROM_DATABASE=Rexgen Inc. + +OUI:2C265F6* + ID_OUI_FROM_DATABASE=Appostar Technology Co. Ltd + +OUI:2C265F2* + ID_OUI_FROM_DATABASE=Jiangsu JARI Technology Group Co., LTD + +OUI:2C265F1* + ID_OUI_FROM_DATABASE=Griessbach + +OUI:F80278A* + ID_OUI_FROM_DATABASE=Luxul Technology Inc + +OUI:28FD80E* + ID_OUI_FROM_DATABASE=T-Radio AS + +OUI:74E14A7* + ID_OUI_FROM_DATABASE=APM Technologies (DongGuan) Ltd + +OUI:74E14A2* + ID_OUI_FROM_DATABASE=KLIMAT SOLEC Sp. z o.o. + +OUI:E4956EE* + ID_OUI_FROM_DATABASE=Tacom Projetos Bilhetagem Inteligente ltda + +OUI:D022121* + ID_OUI_FROM_DATABASE=AIM + +OUI:E81863E* + ID_OUI_FROM_DATABASE=Acopian Technical Company + +OUI:E4956E0* + ID_OUI_FROM_DATABASE=SMC Networks, Inc + +OUI:BC6641C* + ID_OUI_FROM_DATABASE=Shenzhen Crave Communication Co.,ltd + +OUI:BC66417* + ID_OUI_FROM_DATABASE=VSN Mobil + +OUI:BC66418* + ID_OUI_FROM_DATABASE=Shenzhen Yaguang communication CO.,LTD + +OUI:D022125* + ID_OUI_FROM_DATABASE=Shanghai Routech Co., Ltd + +OUI:B8D8121* + ID_OUI_FROM_DATABASE=VOTEM + +OUI:74E14AB* + ID_OUI_FROM_DATABASE=Loctek Visual Technology Corp. + +OUI:E4956E9* + ID_OUI_FROM_DATABASE=eZeLink LLC + +OUI:E4956E3* + ID_OUI_FROM_DATABASE=Shanghai DGE Co., Ltd + +OUI:E818636* + ID_OUI_FROM_DATABASE=ARTECH SOLUTION CO.,LTD + +OUI:E818631* + ID_OUI_FROM_DATABASE=clabsys + +OUI:B8D812C* + ID_OUI_FROM_DATABASE=Yuwei Info&Tech Development Co.,Ltd + +OUI:B8D8125* + ID_OUI_FROM_DATABASE=XIAMEN XINDECO LTD. + +OUI:BC66412* + ID_OUI_FROM_DATABASE=Process-Electronic Sp. z o.o. + +OUI:58FCDBE* + ID_OUI_FROM_DATABASE=Applied Device Technologies + +OUI:58FCDBB* + ID_OUI_FROM_DATABASE=SWARCO TRAFFIC SYSTEMS GMBH + +OUI:58FCDBA* + ID_OUI_FROM_DATABASE=Xmodus Systems GmbH + +OUI:58FCDB1* + ID_OUI_FROM_DATABASE=Certis Technology International + +OUI:58FCDB4* + ID_OUI_FROM_DATABASE=Inforce Computing Inc. + +OUI:58FCDB0* + ID_OUI_FROM_DATABASE=Spang Power Electronics + +OUI:141FBAC* + ID_OUI_FROM_DATABASE=Swiss Electronic (Shenzhen) Co., Ltd + +OUI:F40E111* + ID_OUI_FROM_DATABASE=BEIJING DONGJIN AERO-TECH CO., LTD + +OUI:141FBAD* + ID_OUI_FROM_DATABASE=AJIS(DALIAN)co.,LTD + +OUI:141FBA9* + ID_OUI_FROM_DATABASE=Black Moth Technologies + +OUI:141FBA8* + ID_OUI_FROM_DATABASE=Shenzhen CATIC Information Technology Industry Co.,Ltd + +OUI:141FBA3* + ID_OUI_FROM_DATABASE=Private + +OUI:141FBA2* + ID_OUI_FROM_DATABASE=Deutsche Energieversorgung GmbH + +OUI:D07650B* + ID_OUI_FROM_DATABASE=PelKorea + +OUI:D076506* + ID_OUI_FROM_DATABASE=Picobrew LLC + +OUI:7C70BCD* + ID_OUI_FROM_DATABASE=mk-messtechnik GmbH + +OUI:7C70BC8* + ID_OUI_FROM_DATABASE=Mennekes Elektrotechnik GmbH & Co. KG + +OUI:BC34000* + ID_OUI_FROM_DATABASE=Redvision CCTV + +OUI:A43BFAB* + ID_OUI_FROM_DATABASE=ALSTOM Strongwish (Shenzhen) Co., Ltd + +OUI:B01F810* + ID_OUI_FROM_DATABASE=Dalian GigaTec Electronics Co.,Ltd + +OUI:F40E11A* + ID_OUI_FROM_DATABASE=Kodpro Ltd. + +OUI:F40E116* + ID_OUI_FROM_DATABASE=Alpha Design Technologies Pvt Ltd + +OUI:A43BFA8* + ID_OUI_FROM_DATABASE=Alpwise + +OUI:D076502* + ID_OUI_FROM_DATABASE=Happo Solutions Oy + +OUI:7419F8B* + ID_OUI_FROM_DATABASE=IDEXX Laboratories, Inc + +OUI:B01F819* + ID_OUI_FROM_DATABASE=CIDE Interactive + +OUI:B01F814* + ID_OUI_FROM_DATABASE=SHENZHEN YIFANG DIGITAL TECHNOLOGY CO.,LTD. + +OUI:7C70BC1* + ID_OUI_FROM_DATABASE=XD-GE Automation CO.,LTD + +OUI:BC3400C* + ID_OUI_FROM_DATABASE=Parlay Labs dba Highfive + +OUI:BC34008* + ID_OUI_FROM_DATABASE=MATICA TECHNOLOGIES AG + +OUI:7419F86* + ID_OUI_FROM_DATABASE=Baudisch Electronic GmbH + +OUI:7419F82* + ID_OUI_FROM_DATABASE=Symtop Instrument Co. + +OUI:1C88793* + ID_OUI_FROM_DATABASE=Shenzhen Xiaoxi Technology Co., Ltd. + +OUI:1C8879A* + ID_OUI_FROM_DATABASE=ITW-FEG + +OUI:1C8776E* + ID_OUI_FROM_DATABASE=Artis GmbH + +OUI:1C88790* + ID_OUI_FROM_DATABASE=Newps co.,ltd + +OUI:1C87745* + ID_OUI_FROM_DATABASE=Xiaoxinge (Tangshan) Electronic Technology Co., Ltd. + +OUI:1C87748* + ID_OUI_FROM_DATABASE=Surtec Industries, Inc + +OUI:1C8879E* + ID_OUI_FROM_DATABASE=Orion Labs inc + +OUI:78CA83A* + ID_OUI_FROM_DATABASE=Eksagate Elektronik Mühendislik ve Bilgisayar San. Tic. A.Ş. + +OUI:78CA83B* + ID_OUI_FROM_DATABASE=Zhejiang Science Electronic Tech Co., Ltd + +OUI:78CA835* + ID_OUI_FROM_DATABASE=Huatune Technology (Shanghai) Co., Ltd. + +OUI:38B8EB0* + ID_OUI_FROM_DATABASE=Bumjin C&L Co., Ltd. + +OUI:38B8EB5* + ID_OUI_FROM_DATABASE=Dojo-Labs Ltd + +OUI:38B8EB1* + ID_OUI_FROM_DATABASE=1.A Connect GmbH + +OUI:38B8EBA* + ID_OUI_FROM_DATABASE=SECAD SA + +OUI:38B8EB7* + ID_OUI_FROM_DATABASE=Private + +OUI:38FDFE6* + ID_OUI_FROM_DATABASE=Inspero Inc + +OUI:38FDFE2* + ID_OUI_FROM_DATABASE=B.U.G.SST,Inc + +OUI:5CF2869* + ID_OUI_FROM_DATABASE=Shenzhen VST Automotive Electronics Co., LTD + +OUI:7C477C7* + ID_OUI_FROM_DATABASE=BlueSmart Technology Corporation + +OUI:7C477CC* + ID_OUI_FROM_DATABASE=annapurnalabs + +OUI:986D356* + ID_OUI_FROM_DATABASE=Vitronic Dr.-Ing. Stein Bildverarbeitungssysteme GmbH + +OUI:7C477C8* + ID_OUI_FROM_DATABASE=Shenzhen Eunicum Electric Co.,Ltd. + +OUI:986D35B* + ID_OUI_FROM_DATABASE=INTECH + +OUI:986D352* + ID_OUI_FROM_DATABASE=SHENZHEN FISE TECHNOLOGY HOLDING CO.,LTD. + +OUI:50FF990* + ID_OUI_FROM_DATABASE=Simicon + +OUI:50FF991* + ID_OUI_FROM_DATABASE=Coyote Sytem + +OUI:50FF999* + ID_OUI_FROM_DATABASE=Sea Eagle Optoelectronic Information Technology(Tianjin)co,Ltd + +OUI:E0B6F58* + ID_OUI_FROM_DATABASE=Yuneec International(China)Co.,Ltd + +OUI:E0B6F52* + ID_OUI_FROM_DATABASE=Shanghai- British Information Technology Co., Ltd + +OUI:E0B6F56* + ID_OUI_FROM_DATABASE=POMCube Inc. + +OUI:2C265FC* + ID_OUI_FROM_DATABASE=AATON DIGITAL + +OUI:6891D05* + ID_OUI_FROM_DATABASE=NIPK Electron Co. + +OUI:6891D07* + ID_OUI_FROM_DATABASE=Omniimpex GmbH + +OUI:6891D08* + ID_OUI_FROM_DATABASE=solvimus GmbH + +OUI:6891D06* + ID_OUI_FROM_DATABASE=femrice + +OUI:C47C8DC* + ID_OUI_FROM_DATABASE=INOTEC Sicherheitstechnik GmbH + +OUI:C47C8D0* + ID_OUI_FROM_DATABASE=ATI + +OUI:C47C8DD* + ID_OUI_FROM_DATABASE=Anhui GuangXing Linked-Video Communication Technology Co, Ltd. + +OUI:C47C8DE* + ID_OUI_FROM_DATABASE=Labor Strauss Sicherungsanlagenbau GmbH + +OUI:CCD31E8* + ID_OUI_FROM_DATABASE=inoage GmbH + +OUI:CCD31EA* + ID_OUI_FROM_DATABASE=Haishu Technology LIMITED + +OUI:CCD31ED* + ID_OUI_FROM_DATABASE=CUJO LLC + +OUI:D0D94FA* + ID_OUI_FROM_DATABASE=Shenzhen FDC Electuonic Co.,Ltd. + +OUI:D0D94FD* + ID_OUI_FROM_DATABASE=DUKSANMECASYS CO., LTD. + +OUI:D0D94F1* + ID_OUI_FROM_DATABASE=mycable GmbH + +OUI:D0D94F3* + ID_OUI_FROM_DATABASE=Beijing Yiwangxuntong Technology + +OUI:D0D94F8* + ID_OUI_FROM_DATABASE=Apption Labs Limited + +OUI:D0D94F6* + ID_OUI_FROM_DATABASE=Hyundai Autohow + +OUI:8C192D0* + ID_OUI_FROM_DATABASE=Noritsu Precision Co., Ltd. + +OUI:7C477CA* + ID_OUI_FROM_DATABASE=Dspread Technology (Beijing) Inc. + +OUI:1C87760* + ID_OUI_FROM_DATABASE=Dspread Technology (Beijing) Inc. + +OUI:8C192D5* + ID_OUI_FROM_DATABASE=ELCO(TIANJIN)ELECTRONICS CO.,LTD. + +OUI:8C192DA* + ID_OUI_FROM_DATABASE=TeleAlarm SA + +OUI:A03E6BF* + ID_OUI_FROM_DATABASE=Private + +OUI:58FCDBF* + ID_OUI_FROM_DATABASE=Private + +OUI:283638C* + ID_OUI_FROM_DATABASE=Swisson AG + +OUI:E4956EF* + ID_OUI_FROM_DATABASE=Private + +OUI:74F8DBF* + ID_OUI_FROM_DATABASE=Private + +OUI:BC6641F* + ID_OUI_FROM_DATABASE=Private + +OUI:74E14AF* + ID_OUI_FROM_DATABASE=Private + +OUI:B8D812F* + ID_OUI_FROM_DATABASE=Private + +OUI:2C265FF* + ID_OUI_FROM_DATABASE=Private + +OUI:2C6A6FF* + ID_OUI_FROM_DATABASE=Private + +OUI:64FB81F* + ID_OUI_FROM_DATABASE=Private + +OUI:28FD80F* + ID_OUI_FROM_DATABASE=Private + +OUI:A0BB3EF* + ID_OUI_FROM_DATABASE=Private + +OUI:2CD141F* + ID_OUI_FROM_DATABASE=Private + +OUI:2836388* + ID_OUI_FROM_DATABASE=Havells India Limited + +OUI:84E0F44* + ID_OUI_FROM_DATABASE=PetroInTrade + +OUI:84E0F43* + ID_OUI_FROM_DATABASE=ASL Intercom B.V. + +OUI:1C88798* + ID_OUI_FROM_DATABASE=Toshiba Toko Meter Systems Co., LTD. + +OUI:383A210* + ID_OUI_FROM_DATABASE=R3C Information(Shenzhen) Co.,Ltd. + +OUI:4CE1734* + ID_OUI_FROM_DATABASE=Huizhou Dehong Technology Co., Ltd. + +OUI:4CE173A* + ID_OUI_FROM_DATABASE=jvi + +OUI:F0ACD70* + ID_OUI_FROM_DATABASE=Guilin glsun Science and Tech Co.,LTD + +OUI:F0ACD7E* + ID_OUI_FROM_DATABASE=Fiziico Co., Ltd. + +OUI:58E8760* + ID_OUI_FROM_DATABASE=Private + +OUI:58E8761* + ID_OUI_FROM_DATABASE=Beijing Perabytes IS Technology Co., Ltd + +OUI:C0D3914* + ID_OUI_FROM_DATABASE=Vernier Software & Technology + +OUI:C0D3919* + ID_OUI_FROM_DATABASE=xxter bv + +OUI:C0D391E* + ID_OUI_FROM_DATABASE=SAMSARA NETWORKS INC + +OUI:84E0F4D* + ID_OUI_FROM_DATABASE=Logos01 Srl + +OUI:70F8E7E* + ID_OUI_FROM_DATABASE=CUAV + +OUI:70F8E73* + ID_OUI_FROM_DATABASE=Dr. Simon Consulting GmbH + +OUI:70F8E7C* + ID_OUI_FROM_DATABASE=Fixstars Corporation + +OUI:F81D783* + ID_OUI_FROM_DATABASE=SHANGHAI SUN TELECOMMUNICATION CO., LTD. + +OUI:F81D78D* + ID_OUI_FROM_DATABASE=Tofino + +OUI:F81D787* + ID_OUI_FROM_DATABASE=WUHAN GUIDE INFRARED CO.,LTD + +OUI:F81D789* + ID_OUI_FROM_DATABASE=Ophrys Systèmes + +OUI:383A21D* + ID_OUI_FROM_DATABASE=Colooc AB + +OUI:383A213* + ID_OUI_FROM_DATABASE=Shanghai Greatwall Safety System Co.,Ltd + +OUI:AC64DD7* + ID_OUI_FROM_DATABASE=Wittmann Kunststoffgeräte GmbH + +OUI:AC64DD9* + ID_OUI_FROM_DATABASE=Micro Connect Pty Ltd + +OUI:AC64DD3* + ID_OUI_FROM_DATABASE=infypower Co., Ltd + +OUI:1C87740* + ID_OUI_FROM_DATABASE=Philips Personal Health Solutions + +OUI:1C87761* + ID_OUI_FROM_DATABASE=EBS Sp. z o.o. + +OUI:1C8779D* + ID_OUI_FROM_DATABASE=Shenzhen Innovaconn Systems Co.,Ltd + +OUI:1C87766* + ID_OUI_FROM_DATABASE=philandro Software GmbH + +OUI:8439BE0* + ID_OUI_FROM_DATABASE=HINO ENGINEERING, INC + +OUI:40A36BB* + ID_OUI_FROM_DATABASE=Amobile Intelligent Corp. + +OUI:8439BE6* + ID_OUI_FROM_DATABASE=Shenzhen IP3 Century Intelligent Technology Co., Ltd + +OUI:8439BEB* + ID_OUI_FROM_DATABASE=Shenzhen Horn Audio Co.,Ltd. + +OUI:40A36B7* + ID_OUI_FROM_DATABASE=Pella Corporation + +OUI:40A36B2* + ID_OUI_FROM_DATABASE=TOPROOTTechnology Corp. Ltd. + +OUI:40A36B9* + ID_OUI_FROM_DATABASE=PH Technical Labs + +OUI:70886B2* + ID_OUI_FROM_DATABASE=CVnet + +OUI:70886B1* + ID_OUI_FROM_DATABASE=Bitfinder Inc + +OUI:0055DAC* + ID_OUI_FROM_DATABASE=Donguan WideLink Communication Technology Co.,Ltd. + +OUI:0055DAD* + ID_OUI_FROM_DATABASE=Arrow Electronics,Inc. + +OUI:0055DAA* + ID_OUI_FROM_DATABASE=Speechlab + +OUI:0055DAB* + ID_OUI_FROM_DATABASE=Interaxon Inc + +OUI:0055DA2* + ID_OUI_FROM_DATABASE=Beijing Connected Information Technology Co.,Ltd. + +OUI:0055DA0* + ID_OUI_FROM_DATABASE=Shinko Technos co.,ltd. + +OUI:0055DA1* + ID_OUI_FROM_DATABASE=KoolPOS Inc. + +OUI:A03E6B7* + ID_OUI_FROM_DATABASE=SinoGrid Software Systems Inc. + +OUI:C88ED17* + ID_OUI_FROM_DATABASE=Ube, Inc. (dba Plum) + +OUI:A03E6BC* + ID_OUI_FROM_DATABASE=Qunar.com + +OUI:A03E6B4* + ID_OUI_FROM_DATABASE=Shenzhen Nufilo Inc. + +OUI:B0C5CAD* + ID_OUI_FROM_DATABASE=Private + +OUI:C88ED13* + ID_OUI_FROM_DATABASE=Linx Technologies + +OUI:1C21D19* + ID_OUI_FROM_DATABASE=Dynojet Research + +OUI:1C21D1A* + ID_OUI_FROM_DATABASE=LG CNS + +OUI:1C21D18* + ID_OUI_FROM_DATABASE=Reliatronics Inc. + +OUI:1C21D15* + ID_OUI_FROM_DATABASE=B-Scada Inc. + +OUI:1C21D10* + ID_OUI_FROM_DATABASE=Toyo System CO.,LTD. + +OUI:CC1BE0C* + ID_OUI_FROM_DATABASE=Guangzhou Southelectric Power Science Technology Development Co.,Ltd. + +OUI:CC1BE08* + ID_OUI_FROM_DATABASE=MDT technologies GmbH + +OUI:DC44279* + ID_OUI_FROM_DATABASE=Neusoft Corporation + +OUI:DC44275* + ID_OUI_FROM_DATABASE=Century Audio, Inc. + +OUI:DC44271* + ID_OUI_FROM_DATABASE=Tesla Motors, Inc + +OUI:78C2C0C* + ID_OUI_FROM_DATABASE=Shanghai Hanyi Technologies Co,.Ltd. + +OUI:B0C5CA0* + ID_OUI_FROM_DATABASE=EM-Tech + +OUI:74F8DBD* + ID_OUI_FROM_DATABASE=Simon Electric (China) Co.,ltd + +OUI:74F8DB9* + ID_OUI_FROM_DATABASE=Avantree Corporation + +OUI:74F8DB5* + ID_OUI_FROM_DATABASE=Provision-ISR + +OUI:74F8DB1* + ID_OUI_FROM_DATABASE=GHL Advanced Technology GmbH & Co. KG + +OUI:B437D1A* + ID_OUI_FROM_DATABASE=Axiomatic Technologies Corporation + +OUI:B0C5CA5* + ID_OUI_FROM_DATABASE=SYSTOVI + +OUI:549A11E* + ID_OUI_FROM_DATABASE=Beijing HTSmartech Co.,Ltd + +OUI:549A114* + ID_OUI_FROM_DATABASE=eTauro LLC + +OUI:807B85A* + ID_OUI_FROM_DATABASE=Interplan Co., Ltd. + +OUI:90C6824* + ID_OUI_FROM_DATABASE=Neone, Inc. + +OUI:2C6A6F7* + ID_OUI_FROM_DATABASE=SM DSP CO.,LTD. + +OUI:80E4DA5* + ID_OUI_FROM_DATABASE=CAVALRY STORAGE INC + +OUI:80E4DA1* + ID_OUI_FROM_DATABASE=Guangzhou Pinzhong Electronic Technology CO., LTD + +OUI:2CD141C* + ID_OUI_FROM_DATABASE=PIN SHANG LED Co., LTD. + +OUI:2CD141A* + ID_OUI_FROM_DATABASE=Fiberroad Technology Co., Ltd. + +OUI:2CD141B* + ID_OUI_FROM_DATABASE=Resus Industries + +OUI:1CCAE33* + ID_OUI_FROM_DATABASE=Shenzhen Smart Device Technology Co.,LTD + +OUI:1CCAE34* + ID_OUI_FROM_DATABASE=Sunray Medical Apparatus Co.,Ltd. + +OUI:80E4DA9* + ID_OUI_FROM_DATABASE=Elcus + +OUI:64FB813* + ID_OUI_FROM_DATABASE=MOBILUS Inc. + +OUI:1CCAE39* + ID_OUI_FROM_DATABASE=SHIN-YOSHA CORPORATION + +OUI:2CD1417* + ID_OUI_FROM_DATABASE=XiaMen 35.com Technology Co,.Ltd. + +OUI:2CD1412* + ID_OUI_FROM_DATABASE=IntelliLUM + +OUI:2CD1411* + ID_OUI_FROM_DATABASE=Ezee Systems Limited + +OUI:90C682C* + ID_OUI_FROM_DATABASE=Li Seng Technology Ltd. + +OUI:64FB818* + ID_OUI_FROM_DATABASE=NPG Technology S.A. + +OUI:885D907* + ID_OUI_FROM_DATABASE=Schmidt & Co.,(H.K.)Ltd. + +OUI:885D903* + ID_OUI_FROM_DATABASE=CPAC Systems + +OUI:807B852* + ID_OUI_FROM_DATABASE=Phoenix Co.,Ltd. + +OUI:0CEFAF7* + ID_OUI_FROM_DATABASE=Syntrans AB + +OUI:A44F29E* + ID_OUI_FROM_DATABASE=Neotech Systems Pvt. Ltd. + +OUI:2C265F9* + ID_OUI_FROM_DATABASE=Brüel & Kjaer Vibro GmbH + +OUI:2C265F4* + ID_OUI_FROM_DATABASE=GTA Electronics Co., Ltd. + +OUI:F80278C* + ID_OUI_FROM_DATABASE=Technology Research, LLC + +OUI:F802789* + ID_OUI_FROM_DATABASE=Beijing Redcdn Technology, Co., Ltd + +OUI:A44F29A* + ID_OUI_FROM_DATABASE=HTD + +OUI:A44F292* + ID_OUI_FROM_DATABASE=LUCEOR + +OUI:F802784* + ID_OUI_FROM_DATABASE=CLARUS Korea Co., Ltd + +OUI:F802780* + ID_OUI_FROM_DATABASE=Digatron Power Electronics GmbH + +OUI:0CEFAFA* + ID_OUI_FROM_DATABASE=chengdu joyotime Technology Co., Ltd. + +OUI:9802D85* + ID_OUI_FROM_DATABASE=EBI Ltd. + +OUI:9802D80* + ID_OUI_FROM_DATABASE=Stoerk-Tronic, Stoerk GmbH & Co.KG + +OUI:A0BB3EC* + ID_OUI_FROM_DATABASE=Ewig Industries Macao Commercial Offshore Ltd + +OUI:A0BB3E6* + ID_OUI_FROM_DATABASE=Xiamen Kehua Hengsheng Co.,Ltd + +OUI:2C6A6F3* + ID_OUI_FROM_DATABASE=Cloudproject Generation Srl + +OUI:9802D8D* + ID_OUI_FROM_DATABASE=Promicon Elektronik GmbH + Co.KG + +OUI:9802D89* + ID_OUI_FROM_DATABASE=Navroom Beijing, China + +OUI:A0BB3E1* + ID_OUI_FROM_DATABASE=IVision Electronics Co.,Ltd + +OUI:28FD80A* + ID_OUI_FROM_DATABASE=Apollo Digital (Taiwan) Ltd. + +OUI:28FD806* + ID_OUI_FROM_DATABASE=Vigil Monitoring + +OUI:3C39E75* + ID_OUI_FROM_DATABASE=Attrackting AG + +OUI:3C39E74* + ID_OUI_FROM_DATABASE=University of British Columbia + +OUI:28FD807* + ID_OUI_FROM_DATABASE=University of York + +OUI:28FD800* + ID_OUI_FROM_DATABASE=Millcode + +OUI:2C265FE* + ID_OUI_FROM_DATABASE=Hysentel Technology Co., Ltd + +OUI:B8D8128* + ID_OUI_FROM_DATABASE=Visual Productions BV + +OUI:B8D8122* + ID_OUI_FROM_DATABASE=IPM Sales and service Co.,Ltd. + +OUI:74E14AD* + ID_OUI_FROM_DATABASE=Knog Pty Ltd + +OUI:74E14A9* + ID_OUI_FROM_DATABASE=Kanto Aircraft Instrument Co., Ltd. + +OUI:BC66414* + ID_OUI_FROM_DATABASE=ARGUS-SPECTRUM + +OUI:58FCDBD* + ID_OUI_FROM_DATABASE=XIAMEN LEELEN TECHNOLOGY CO.,LTD + +OUI:58FCDB8* + ID_OUI_FROM_DATABASE=Shanghai Qianjin Electronic Equipment Co. Ltd + +OUI:1007231* + ID_OUI_FROM_DATABASE=Beijing Assem Technology Co., ltd + +OUI:D02212B* + ID_OUI_FROM_DATABASE=Schleifenbauer Holding BV + +OUI:D022128* + ID_OUI_FROM_DATABASE=Shenzhen SIC Technology. Co., Ltd. + +OUI:100723B* + ID_OUI_FROM_DATABASE=Fujian Quanzhou Dong Ang Electronics Co., Ltd. + +OUI:100723C* + ID_OUI_FROM_DATABASE=SHENZHEN XINFA ELECTRONIC CO.,LTD + +OUI:1007235* + ID_OUI_FROM_DATABASE=BEIJING SOOALL INFORMATION TECHNOLOGY CO.,LTD + +OUI:1007232* + ID_OUI_FROM_DATABASE=Diginet Control Systems Pty Ltd + +OUI:E4956E6* + ID_OUI_FROM_DATABASE=SHENZHEN JOYETECH ELECTRONICS CO., LTD. + +OUI:B8D812B* + ID_OUI_FROM_DATABASE=Docobo Limited + +OUI:BC6641A* + ID_OUI_FROM_DATABASE=EBlink + +OUI:E81863B* + ID_OUI_FROM_DATABASE=Protek Electronics Group Co.,LTD + +OUI:74E14A5* + ID_OUI_FROM_DATABASE=UTU Oy + +OUI:74E14A0* + ID_OUI_FROM_DATABASE=Altenburger Electronic GmbH + +OUI:F40E11C* + ID_OUI_FROM_DATABASE=NIHON MEGA LOGIC CO.,LTD. + +OUI:F40E118* + ID_OUI_FROM_DATABASE=Zeepro Inc. + +OUI:F40E114* + ID_OUI_FROM_DATABASE=Dayang Technology Development Inc. + +OUI:141FBAB* + ID_OUI_FROM_DATABASE=Newings Communication CO., LTD. + +OUI:141FBA5* + ID_OUI_FROM_DATABASE=Inttelix Brasil Tecnologia e Sistemas Ltda + +OUI:A43BFAC* + ID_OUI_FROM_DATABASE=SHANGHAI XIETONG TECHNOLOGY INC. + +OUI:A43BFA4* + ID_OUI_FROM_DATABASE=Maxon Australia + +OUI:141FBA1* + ID_OUI_FROM_DATABASE=GloQuad + +OUI:7C70BC7* + ID_OUI_FROM_DATABASE=Nomad Digital Ltd. + +OUI:7C70BCB* + ID_OUI_FROM_DATABASE=Tohan Engineering Corporation + +OUI:A43BFA0* + ID_OUI_FROM_DATABASE=Chengdu Territory Technology Co.,Ltd + +OUI:D07650D* + ID_OUI_FROM_DATABASE=tecnotron elekronik gmbh + +OUI:D076508* + ID_OUI_FROM_DATABASE=Accumulate AB + +OUI:D076504* + ID_OUI_FROM_DATABASE=Private + +OUI:58FCDB3* + ID_OUI_FROM_DATABASE=Custom Biogenic Systems + +OUI:B01F81B* + ID_OUI_FROM_DATABASE=Rademacher Geraete-Elektronik GmbH + +OUI:BC34005* + ID_OUI_FROM_DATABASE=NDSL, Inc. + +OUI:BC34003* + ID_OUI_FROM_DATABASE=Altronix Corporation + +OUI:B01F817* + ID_OUI_FROM_DATABASE=Aether Services, Inc. + +OUI:B01F813* + ID_OUI_FROM_DATABASE=Sound United + +OUI:7C70BC3* + ID_OUI_FROM_DATABASE=FLEXIM GmbH + +OUI:7419F89* + ID_OUI_FROM_DATABASE=Princip a.s. + +OUI:7419F85* + ID_OUI_FROM_DATABASE=Starcor Beijing Co.,Limited + +OUI:D076500* + ID_OUI_FROM_DATABASE=CentrAlert, Inc. + +OUI:7419F8D* + ID_OUI_FROM_DATABASE=Ansjer Electronics Co., Ltd. + +OUI:7419F81* + ID_OUI_FROM_DATABASE=Trend-tech Technology Co., Limited + +OUI:1C8774D* + ID_OUI_FROM_DATABASE=CLABER SPA + +OUI:1C87746* + ID_OUI_FROM_DATABASE=Schawbel Technologies LLC + +OUI:1C8774A* + ID_OUI_FROM_DATABASE=Nebbiolo Technologies + +OUI:78CA830* + ID_OUI_FROM_DATABASE=DAINCUBE + +OUI:38B8EB4* + ID_OUI_FROM_DATABASE=UMLOGICS + +OUI:38B8EB9* + ID_OUI_FROM_DATABASE=NHS Sistemas de Energia + +OUI:38FDFEA* + ID_OUI_FROM_DATABASE=Management Service Corporation + +OUI:38FDFEB* + ID_OUI_FROM_DATABASE=Swedish Adrenaline AB + +OUI:38FDFE4* + ID_OUI_FROM_DATABASE=New Telecom Solutions LLC + +OUI:38FDFE9* + ID_OUI_FROM_DATABASE=OOO Group of Industrial Technologies + +OUI:5CF2860* + ID_OUI_FROM_DATABASE=Hangzhou Signwei Electronics Technology Co., Ltd + +OUI:5CF2863* + ID_OUI_FROM_DATABASE=beijing your wonderful control system technology co.,ltd + +OUI:5CF2865* + ID_OUI_FROM_DATABASE=EUROIMMUN Medizinische Labordiagnostika AG + +OUI:5CF2861* + ID_OUI_FROM_DATABASE=iSon Tech + +OUI:7C477C6* + ID_OUI_FROM_DATABASE=Zerosystem LTD.Co + +OUI:7C477CD* + ID_OUI_FROM_DATABASE=Speedifi Inc + +OUI:986D359* + ID_OUI_FROM_DATABASE=Private + +OUI:986D353* + ID_OUI_FROM_DATABASE=DH Mechatronic AG + +OUI:986D355* + ID_OUI_FROM_DATABASE=PDAHL + +OUI:E0B6F59* + ID_OUI_FROM_DATABASE=Motiveprime Consumer Electronics Pvt Ltd + +OUI:E0B6F51* + ID_OUI_FROM_DATABASE=START TODAY CO.,LTD. + +OUI:6891D09* + ID_OUI_FROM_DATABASE=QUANTEX + +OUI:6891D0D* + ID_OUI_FROM_DATABASE=Fuzhou x-speed information technology Co.,Ltd. + +OUI:6891D03* + ID_OUI_FROM_DATABASE=Ambitio LLC + +OUI:6891D0E* + ID_OUI_FROM_DATABASE=Outstanding Technology Co., Ltd. + +OUI:D0D94FC* + ID_OUI_FROM_DATABASE=ARROWAVE TECHNOLOGIES LIMITED + +OUI:2836387* + ID_OUI_FROM_DATABASE=Innovative Technology Ltd + +OUI:800A80F* + ID_OUI_FROM_DATABASE=Private + +OUI:B437D1F* + ID_OUI_FROM_DATABASE=Private + +OUI:0CEFAFF* + ID_OUI_FROM_DATABASE=Private + +OUI:A44F29F* + ID_OUI_FROM_DATABASE=Private + +OUI:78C2C0F* + ID_OUI_FROM_DATABASE=Private + +OUI:141FBAF* + ID_OUI_FROM_DATABASE=Private + +OUI:549A11F* + ID_OUI_FROM_DATABASE=Private + +OUI:807B85F* + ID_OUI_FROM_DATABASE=Private + +OUI:2836386* + ID_OUI_FROM_DATABASE=Georg Neumann GmbH + +OUI:8C192D8* + ID_OUI_FROM_DATABASE=Shenzhen Cylan Technology Co.,Ltd + +OUI:8C192D2* + ID_OUI_FROM_DATABASE=DataRemote Inc. + +OUI:D0D94FE* + ID_OUI_FROM_DATABASE=APPOTRONICS CO., LTD + +OUI:8C192D6* + ID_OUI_FROM_DATABASE=smartHome Partner GmbH + +OUI:8C192DB* + ID_OUI_FROM_DATABASE=Abside Networks, Inc. + +OUI:70F8E7D* + ID_OUI_FROM_DATABASE=System-on-Chip engineering + +OUI:70F8E78* + ID_OUI_FROM_DATABASE=Eclipse Security + +OUI:70F8E75* + ID_OUI_FROM_DATABASE=Beijing Eehuu Technology Co.,Ltd. + +OUI:70F8E7B* + ID_OUI_FROM_DATABASE=Photonfocus AG + +OUI:F81D782* + ID_OUI_FROM_DATABASE=Xperio Labs Limited + +OUI:0CEFAF0* + ID_OUI_FROM_DATABASE=Kenmore + +OUI:78C2C06* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:4CE1732* + ID_OUI_FROM_DATABASE=Lenovo Data Center Group + +OUI:4CE1738* + ID_OUI_FROM_DATABASE=Nanjing Tongke Technology Development Co., LTD + +OUI:F0ACD7A* + ID_OUI_FROM_DATABASE=Groupeer Technologies + +OUI:F0ACD74* + ID_OUI_FROM_DATABASE=Sercomm Corporation. + +OUI:58E876B* + ID_OUI_FROM_DATABASE=annapurnalabs + +OUI:58E876A* + ID_OUI_FROM_DATABASE=SHENZHEN DIGISSIN TECHNOLOGY + +OUI:58E8762* + ID_OUI_FROM_DATABASE=Coala Life AB + +OUI:C0D3917* + ID_OUI_FROM_DATABASE=ALNETz Co.,LTD + +OUI:C0D3918* + ID_OUI_FROM_DATABASE=XENA SECURITY LIMITED + +OUI:C0D391C* + ID_OUI_FROM_DATABASE=Zhinengguo technology company limited + +OUI:84E0F42* + ID_OUI_FROM_DATABASE=Hangzhou Uni-Ubi Co.,Ltd. + +OUI:84E0F46* + ID_OUI_FROM_DATABASE=Liaoning IK'SONYA Science and Technology Co., Ltd. + +OUI:383A216* + ID_OUI_FROM_DATABASE=Shenzhen Smart-core Technology co., Ltd. + +OUI:4CE1735* + ID_OUI_FROM_DATABASE=NewVastek + +OUI:AC64DD6* + ID_OUI_FROM_DATABASE=Kpnetworks Ltd. + +OUI:E043DB* + ID_OUI_FROM_DATABASE=Shenzhen ViewAt Technology Co.,Ltd. + +OUI:2405F5* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:2C3033* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:3CD92B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:9C8E99* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:B499BA* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:1CC1DE* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3C3556* + ID_OUI_FROM_DATABASE=Cognitec Systems GmbH + +OUI:0050BA* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:00179A* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:1CBDB9* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:9094E4* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:28107B* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:1C7EE5* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:C4A81D* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:18622C* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:7C03D8* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:E8F1B0* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:00F871* + ID_OUI_FROM_DATABASE=DGS Denmark A/S + +OUI:20BB76* + ID_OUI_FROM_DATABASE=COL GIOVANNI PAOLO SpA + +OUI:2C228B* + ID_OUI_FROM_DATABASE=CTR SRL + +OUI:348AAE* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:BCEC23* + ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD + +OUI:8CE748* + ID_OUI_FROM_DATABASE=Private + +OUI:F09CE9* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:C413E2* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:AC06C7* + ID_OUI_FROM_DATABASE=ServerNet S.r.l. + +OUI:CC46D6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:48AD08* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2CAB00* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00E0FC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:24DF6A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:009ACD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00CDFE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:38F23E* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:58AC78* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:907F61* + ID_OUI_FROM_DATABASE=Chicony Electronics Co., Ltd. + +OUI:28BC18* + ID_OUI_FROM_DATABASE=SourcingOverseas Co. Ltd + +OUI:807ABF* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:409F87* + ID_OUI_FROM_DATABASE=Jide Technology (Hong Kong) Limited + +OUI:3C5AB4* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:001A11* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:D83C69* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:74AC5F* + ID_OUI_FROM_DATABASE=Qiku Internet Network Scientific (Shenzhen) Co., Ltd. + +OUI:18AF61* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC83A7* + ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD + +OUI:000347* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:001175* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:0013E8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001302* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E4F89C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A402B9* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4C3488* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:000D0B* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:000740* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:0024A5* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:DCFB02* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:F4CE46* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001CC4* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0025B3* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001871* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000BCD* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000E7F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000F20* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:00110A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001321* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001635* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0017A4* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000802* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:90E7C4* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:74A78E* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:D860B0* + ID_OUI_FROM_DATABASE=bioMérieux Italia S.p.A. + +OUI:8038BC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D440F0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:64A651* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E8CD2D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:ACE215* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:EC233D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:78F5FD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:80B686* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:10C61F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:8853D4* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0C37DC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:BC7670* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:24DBAC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:BC3AEA* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:E8BBA8* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:0021E8* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:006057* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:0007D8* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:0012F2* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:001BED* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:002438* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:84742A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:681AB2* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:E005C5* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A0F3C1* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:8C210A* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:EC172F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:EC888F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:14CF92* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:645601* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:14CC20* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:BC4699* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:0C45BA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:847778* + ID_OUI_FROM_DATABASE=Cochlear Limited + +OUI:0453D5* + ID_OUI_FROM_DATABASE=Sysorex Global Holdings + +OUI:CC4463* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C72E7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CCA223* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E8088B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:60E701* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:000883* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:C4346B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:8CDCD4* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3464A9* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D4C9EF* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A45D36* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A0D3C1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:40A8F0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:6C3BE5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:082E5F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:28924A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:10604B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:308D99* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0030C1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:FC3FDB* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:4CA161* + ID_OUI_FROM_DATABASE=Rain Bird Corporation + +OUI:7C6193* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:001217* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:000C41* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:000F66* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:44E08E* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:185933* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:E448C7* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:24767D* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:2CABA4* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:0002C7* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:04766E* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:006B8E* + ID_OUI_FROM_DATABASE=Shanghai Feixun Communication Co.,Ltd. + +OUI:AC853D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:74882A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:78D752* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E0247F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00464B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:707BE8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:548998* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0819A6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3CF808* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B41513* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:283152* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DCD2FC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F8A45F* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:8CBEBE* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:640980* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:98FAE3* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:185936* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:9C99A0* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:0003DD* + ID_OUI_FROM_DATABASE=Comark Interactive Solutions + +OUI:00107B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00906D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090BF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:005080* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E018* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:000C6E* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001BFC* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001E8C* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0015F2* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:002354* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001FC6* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:60182E* + ID_OUI_FROM_DATABASE=ShenZhen Protruly Electronic Ltd co. + +OUI:F4CFE2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:501CBF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:285FDB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:404D8E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:781DBA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001E10* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B0ADAA* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:10CDAE* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:50CD22* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:FCA841* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:3CB15B* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:C8F406* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:2CF4C5* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:7038EE* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:88F031* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:508789* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:381C1A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F40F1B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BC671C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A0ECF9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D46D50* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1CE85D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C47295* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A0554F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:84B802* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BCC493* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001947* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:0022CE* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:F02929* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ECE1A9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C69F6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C08C60* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C0255C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:885A92* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E4C722* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C07BBC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090F2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00173B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00400B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:006009* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:006047* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0006C1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E014* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E01E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACF2C5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:CCC760* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:087402* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:285AEB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:28F076* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:84285A* + ID_OUI_FROM_DATABASE=Saffron Solutions Inc + +OUI:80A1AB* + ID_OUI_FROM_DATABASE=Intellisis + +OUI:44D884* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:EC852F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:286ABA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:705681* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7CD1C3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0DCE2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B065BD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A82066* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC6778* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68967B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:848506* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B4F0AB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:10DDB1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04F7E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34C059* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0D1A9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F82793* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:ACFDEC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D0E140* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F832E4* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:8C7C92* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7831C1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F437B7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:54AE27* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6476BA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:84B153* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:783A84* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2CBE08* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24E314* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0010FF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34BDC8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:54A274* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5897BD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:046C9D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:60FEC5* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00A040* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC3BAF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:786C1C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:041552* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:38484C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:701124* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C86F1D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:685B35* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:380F4A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3010E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04DB56* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:881FA1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04E536* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:109ADD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:40A6D9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7CF05F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A4B197* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C74C2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:403004* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4860BC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:50EAD6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:28E02C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60C547* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7C11BE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:003EE1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68D93C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2CF0EE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:84788B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C94F8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:703EAC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C01ADA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34363B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C81EE7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9CFC01* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:000D93* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001CB3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:64B9E8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34159E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:58B035* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0B479* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:141357* + ID_OUI_FROM_DATABASE=ATP Electronics, Inc. + +OUI:F44B2A* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:3C8CF8* + ID_OUI_FROM_DATABASE=TRENDnet, Inc. + +OUI:78D6B2* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:C04A09* + ID_OUI_FROM_DATABASE=Zhejiang Everbright Communication Equip. Co,. Ltd + +OUI:F00D5C* + ID_OUI_FROM_DATABASE=JinQianMao Technology Co.,Ltd. + +OUI:2C081C* + ID_OUI_FROM_DATABASE=OVH + +OUI:30E090* + ID_OUI_FROM_DATABASE=Linctronix Ltd, + +OUI:70BF3E* + ID_OUI_FROM_DATABASE=Charles River Laboratories + +OUI:D848EE* + ID_OUI_FROM_DATABASE=Hangzhou Xueji Technology Co., Ltd. + +OUI:88947E* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:88C242* + ID_OUI_FROM_DATABASE=Poynt Co. + +OUI:E8343E* + ID_OUI_FROM_DATABASE=Beijing Infosec Technologies Co., LTD. + +OUI:C4ADF1* + ID_OUI_FROM_DATABASE=GOPEACE Inc. + +OUI:58F496* + ID_OUI_FROM_DATABASE=Source Chain + +OUI:80B709* + ID_OUI_FROM_DATABASE=Viptela, Inc + +OUI:1C60DE* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:741865* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:0084ED* + ID_OUI_FROM_DATABASE=Private + +OUI:DCDC07* + ID_OUI_FROM_DATABASE=TRP Systems BV + +OUI:080A4E* + ID_OUI_FROM_DATABASE=Planet Bingo® — 3rd Rock Gaming® + +OUI:0C1A10* + ID_OUI_FROM_DATABASE=Acoustic Stream + +OUI:E4A387* + ID_OUI_FROM_DATABASE=Control Solutions LLC + +OUI:DC82F6* + ID_OUI_FROM_DATABASE=iPort + +OUI:C49E41* + ID_OUI_FROM_DATABASE=G24 Power Limited + +OUI:D03E5C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C8A9FC* + ID_OUI_FROM_DATABASE=Goyoo Networks Inc. + +OUI:C49FF3* + ID_OUI_FROM_DATABASE=Mciao Technologies, Inc. + +OUI:80739F* + ID_OUI_FROM_DATABASE=KYOCERA Corporation + +OUI:7C2BE1* + ID_OUI_FROM_DATABASE=Shenzhen Ferex Electrical Co.,Ltd + +OUI:30FFF6* + ID_OUI_FROM_DATABASE=HangZhou KuoHeng Technology Co.,ltd + +OUI:5853C0* + ID_OUI_FROM_DATABASE=Beijing Guang Runtong Technology Development Company co.,Ltd + +OUI:5031AD* + ID_OUI_FROM_DATABASE=ABB Global Industries and Services Private Limited + +OUI:30A243* + ID_OUI_FROM_DATABASE=Shenzhen Prifox Innovation Technology Co., Ltd. + +OUI:2CA539* + ID_OUI_FROM_DATABASE=Parallel Wireless, Inc + +OUI:FC335F* + ID_OUI_FROM_DATABASE=Polyera + +OUI:FCC233* + ID_OUI_FROM_DATABASE=Private + +OUI:A8C87F* + ID_OUI_FROM_DATABASE=Roqos, Inc. + +OUI:C025A2* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:7853F2* + ID_OUI_FROM_DATABASE=ROXTON Ltd. + +OUI:ACBC32* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:94BBAE* + ID_OUI_FROM_DATABASE=Husqvarna AB + +OUI:AC8995* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:F898B9* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:1C497B* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:2CCF58* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:54FF82* + ID_OUI_FROM_DATABASE=Davit Solution co. + +OUI:D445E8* + ID_OUI_FROM_DATABASE=Jiangxi Hongpai Technology Co., Ltd. + +OUI:847973* + ID_OUI_FROM_DATABASE=Shanghai Baud Data Communication Co.,Ltd. + +OUI:906F18* + ID_OUI_FROM_DATABASE=Private + +OUI:881B99* + ID_OUI_FROM_DATABASE=SHENZHEN XIN FEI JIA ELECTRONIC CO. LTD. + +OUI:681295* + ID_OUI_FROM_DATABASE=Lupine Lighting Systems GmbH + +OUI:649A12* + ID_OUI_FROM_DATABASE=P2 Mobile Technologies Limited + +OUI:E4C2D1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DC3CF6* + ID_OUI_FROM_DATABASE=Atomic Rules LLC + +OUI:3C3178* + ID_OUI_FROM_DATABASE=Qolsys Inc. + +OUI:F4573E* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:083A5C* + ID_OUI_FROM_DATABASE=Junilab, Inc. + +OUI:4CAE31* + ID_OUI_FROM_DATABASE=ShengHai Electronics (Shenzhen) Ltd + +OUI:F0D657* + ID_OUI_FROM_DATABASE=ECHOSENS + +OUI:24693E* + ID_OUI_FROM_DATABASE=innodisk Corporation + +OUI:E48D8C* + ID_OUI_FROM_DATABASE=Routerboard.com + +OUI:C0DC6A* + ID_OUI_FROM_DATABASE=Qingdao Eastsoft Communication Technology Co.,LTD + +OUI:6459F8* + ID_OUI_FROM_DATABASE=Vodafone Omnitel B.V. + +OUI:082CB0* + ID_OUI_FROM_DATABASE=Network Instruments + +OUI:F0AB54* + ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO.,LTD. + +OUI:485073* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:3CA31A* + ID_OUI_FROM_DATABASE=Oilfind International LLC + +OUI:ACFD93* + ID_OUI_FROM_DATABASE=Weifang GoerTek Electronics Co., Ltd. + +OUI:A424DD* + ID_OUI_FROM_DATABASE=Cambrionix Ltd + +OUI:88A2D7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D89A34* + ID_OUI_FROM_DATABASE=Beijing SHENQI Technology Co., Ltd. + +OUI:1CADD1* + ID_OUI_FROM_DATABASE=Bosung Electronics Co., Ltd. + +OUI:24E5AA* + ID_OUI_FROM_DATABASE=Philips Oral Healthcare, Inc. + +OUI:88CBA5* + ID_OUI_FROM_DATABASE=Suzhou Torchstar Intelligent Technology Co.,Ltd + +OUI:046169* + ID_OUI_FROM_DATABASE=MEDIA GLOBAL LINKS CO., LTD. + +OUI:AC562C* + ID_OUI_FROM_DATABASE=LAVA INTERNATIONAL(H.K) LIMITED + +OUI:3CCE15* + ID_OUI_FROM_DATABASE=Mercedes-Benz USA, LLC + +OUI:84DF19* + ID_OUI_FROM_DATABASE=Chuango Security Technology Corporation + +OUI:3C4711* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:245BF0* + ID_OUI_FROM_DATABASE=Liteon, Inc. + +OUI:FCFEC2* + ID_OUI_FROM_DATABASE=Invensys Controls UK Limited + +OUI:E8F2E2* + ID_OUI_FROM_DATABASE=LG Innotek + +OUI:AC676F* + ID_OUI_FROM_DATABASE=Electrocompaniet A.S. + +OUI:4CB82C* + ID_OUI_FROM_DATABASE=Cambridge Mobile Telematics, Inc. + +OUI:F0224E* + ID_OUI_FROM_DATABASE=Esan electronic co. + +OUI:B0411D* + ID_OUI_FROM_DATABASE=ITTIM Technologies + +OUI:7CB25C* + ID_OUI_FROM_DATABASE=Acacia Communications + +OUI:78EB39* + ID_OUI_FROM_DATABASE=Instituto Nacional de Tecnología Industrial + +OUI:7CC95A* + ID_OUI_FROM_DATABASE=EMC + +OUI:ECEED8* + ID_OUI_FROM_DATABASE=ZTLX Network Technology Co.,Ltd + +OUI:F85B9C* + ID_OUI_FROM_DATABASE=SB SYSTEMS Co.,Ltd + +OUI:7CA237* + ID_OUI_FROM_DATABASE=King Slide Technology CO., LTD. + +OUI:300EE3* + ID_OUI_FROM_DATABASE=Aquantia Corporation + +OUI:847303* + ID_OUI_FROM_DATABASE=Letv Mobile and Intelligent Information Technology (Beijing) Corporation Ltd. + +OUI:B0495F* + ID_OUI_FROM_DATABASE=OMRON HEALTHCARE Co., Ltd. + +OUI:BC6E64* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:F44713* + ID_OUI_FROM_DATABASE=Leading Public Performance Co., Ltd. + +OUI:D4522A* + ID_OUI_FROM_DATABASE=TangoWiFi.com + +OUI:B0ECE1* + ID_OUI_FROM_DATABASE=Private + +OUI:407FE0* + ID_OUI_FROM_DATABASE=Glory Star Technics (ShenZhen) Limited + +OUI:BC5C4C* + ID_OUI_FROM_DATABASE=ELECOM CO.,LTD. + +OUI:6C5940* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:6CA75F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C8C50E* + ID_OUI_FROM_DATABASE=Shenzhen Primestone Network Technologies.Co., Ltd. + +OUI:9CBEE0* + ID_OUI_FROM_DATABASE=Biosoundlab Co., Ltd. + +OUI:5C5B35* + ID_OUI_FROM_DATABASE=Mist Systems, Inc. + +OUI:E807BF* + ID_OUI_FROM_DATABASE=SHENZHEN BOOMTECH INDUSTRY CO.,LTD + +OUI:E8162B* + ID_OUI_FROM_DATABASE=IDEO Security Co., Ltd. + +OUI:709F2D* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:5C6B4F* + ID_OUI_FROM_DATABASE=Private + +OUI:ECE2FD* + ID_OUI_FROM_DATABASE=SKG Electric Group(Thailand) Co., Ltd. + +OUI:88E603* + ID_OUI_FROM_DATABASE=Avotek corporation + +OUI:74E28C* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:94F19E* + ID_OUI_FROM_DATABASE=HUIZHOU MAORONG INTELLIGENT TECHNOLOGY CO.,LTD + +OUI:C4924C* + ID_OUI_FROM_DATABASE=KEISOKUKI CENTER CO.,LTD. + +OUI:E4F939* + ID_OUI_FROM_DATABASE=Minxon Hotel Technology INC. + +OUI:38C70A* + ID_OUI_FROM_DATABASE=WiFiSong + +OUI:60E6BC* + ID_OUI_FROM_DATABASE=Sino-Telecom Technology Co.,Ltd. + +OUI:1CA532* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:486EFB* + ID_OUI_FROM_DATABASE=Davit System Technology Co., Ltd. + +OUI:340A22* + ID_OUI_FROM_DATABASE=TOP-ACCESS ELECTRONICS CO LTD + +OUI:B008BF* + ID_OUI_FROM_DATABASE=Vital Connect, Inc. + +OUI:485415* + ID_OUI_FROM_DATABASE=NET RULES TECNOLOGIA EIRELI + +OUI:70C76F* + ID_OUI_FROM_DATABASE=INNO S + +OUI:704E66* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:409B0D* + ID_OUI_FROM_DATABASE=Shenzhen Yourf Kwan Industrial Co., Ltd + +OUI:C40880* + ID_OUI_FROM_DATABASE=Shenzhen UTEPO Tech Co., Ltd. + +OUI:94C038* + ID_OUI_FROM_DATABASE=Tallac Networks + +OUI:801967* + ID_OUI_FROM_DATABASE=Shanghai Reallytek Information Technology Co.,Ltd + +OUI:6836B5* + ID_OUI_FROM_DATABASE=DriveScale, Inc. + +OUI:2CF7F1* + ID_OUI_FROM_DATABASE=Seeed Technology Inc. + +OUI:F88479* + ID_OUI_FROM_DATABASE=Yaojin Technology(Shenzhen)Co.,Ltd + +OUI:4C48DA* + ID_OUI_FROM_DATABASE=Beijing Autelan Technology Co.,Ltd + +OUI:90179B* + ID_OUI_FROM_DATABASE=Nanomegas + +OUI:3077CB* + ID_OUI_FROM_DATABASE=Maike Industry(Shenzhen)CO.,LTD + +OUI:3428F0* + ID_OUI_FROM_DATABASE=ATN International Limited + +OUI:EC3C5A* + ID_OUI_FROM_DATABASE=SHEN ZHEN HENG SHENG HUI DIGITAL TECHNOLOGY CO.,LTD + +OUI:8C0551* + ID_OUI_FROM_DATABASE=Koubachi AG + +OUI:D88466* + ID_OUI_FROM_DATABASE=Extreme Networks + +OUI:E887A3* + ID_OUI_FROM_DATABASE=Loxley Public Company Limited + +OUI:10FACE* + ID_OUI_FROM_DATABASE=Reacheng Communication Technology Co.,Ltd + +OUI:D8CB8A* + ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD. + +OUI:A8D0E3* + ID_OUI_FROM_DATABASE=Systech Electronics Ltd. + +OUI:8463D6* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:78B3B9* + ID_OUI_FROM_DATABASE=ShangHai sunup lighting CO.,LTD + +OUI:F4EE14* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:186571* + ID_OUI_FROM_DATABASE=Top Victory Electronics (Taiwan) Co., Ltd. + +OUI:F8BC41* + ID_OUI_FROM_DATABASE=Rosslare Enterprises Limited + +OUI:8486F3* + ID_OUI_FROM_DATABASE=Greenvity Communications + +OUI:205CFA* + ID_OUI_FROM_DATABASE=Yangzhou ChangLian Network Technology Co,ltd. + +OUI:8C18D9* + ID_OUI_FROM_DATABASE=Shenzhen RF Technology Co., Ltd + +OUI:6099D1* + ID_OUI_FROM_DATABASE=Vuzix / Lenovo + +OUI:34F6D2* + ID_OUI_FROM_DATABASE=Panasonic Taiwan Co.,Ltd. + +OUI:DC2F03* + ID_OUI_FROM_DATABASE=Step forward Group Co., Ltd. + +OUI:582136* + ID_OUI_FROM_DATABASE=KMB systems, s.r.o. + +OUI:00AEFA* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:5CAAFD* + ID_OUI_FROM_DATABASE=Sonos, Inc. + +OUI:8CDF9D* + ID_OUI_FROM_DATABASE=NEC Corporation + +OUI:F8E903* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:F0B052* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:6828F6* + ID_OUI_FROM_DATABASE=Vubiq Networks, Inc. + +OUI:44356F* + ID_OUI_FROM_DATABASE=Neterix + +OUI:742EFC* + ID_OUI_FROM_DATABASE=DirectPacket Research, Inc, + +OUI:20C06D* + ID_OUI_FROM_DATABASE=SHENZHEN SPACETEK TECHNOLOGY CO.,LTD + +OUI:3CB792* + ID_OUI_FROM_DATABASE=Hitachi Maxell, Ltd., Optronics Division + +OUI:7491BD* + ID_OUI_FROM_DATABASE=Four systems Co.,Ltd. + +OUI:D43266* + ID_OUI_FROM_DATABASE=Fike Corporation + +OUI:948E89* + ID_OUI_FROM_DATABASE=INDUSTRIAS UNIDAS SA DE CV + +OUI:9405B6* + ID_OUI_FROM_DATABASE=Liling FullRiver Electronics & Technology Ltd + +OUI:382C4A* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:74547D* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:D48F33* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:1CA2B1* + ID_OUI_FROM_DATABASE=ruwido austria gmbh + +OUI:945493* + ID_OUI_FROM_DATABASE=Rigado, LLC + +OUI:34B7FD* + ID_OUI_FROM_DATABASE=Guangzhou Younghead Electronic Technology Co.,Ltd + +OUI:384B76* + ID_OUI_FROM_DATABASE=AIRTAME ApS + +OUI:1C5216* + ID_OUI_FROM_DATABASE=DONGGUAN HELE ELECTRONICS CO., LTD + +OUI:34029B* + ID_OUI_FROM_DATABASE=CloudBerry Technologies Private Limited + +OUI:70AF25* + ID_OUI_FROM_DATABASE=Nishiyama Industry Co.,LTD. + +OUI:B47C29* + ID_OUI_FROM_DATABASE=Shenzhen Guzidi Technology Co.,Ltd + +OUI:2C1A31* + ID_OUI_FROM_DATABASE=Electronics Company Limited + +OUI:6C198F* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:60C1CB* + ID_OUI_FROM_DATABASE=Fujian Great Power PLC Equipment Co.,Ltd + +OUI:686E48* + ID_OUI_FROM_DATABASE=Prophet Electronic Technology Corp.,Ltd + +OUI:30F7D7* + ID_OUI_FROM_DATABASE=Thread Technology Co., Ltd + +OUI:3808FD* + ID_OUI_FROM_DATABASE=Silca Spa + +OUI:7C2587* + ID_OUI_FROM_DATABASE=chaowifi.com + +OUI:2012D5* + ID_OUI_FROM_DATABASE=Scientech Materials Corporation + +OUI:DC3979* + ID_OUI_FROM_DATABASE=Skyport Systems + +OUI:EC1D7F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:AC11D3* + ID_OUI_FROM_DATABASE=Suzhou HOTEK Video Technology Co. Ltd + +OUI:304225* + ID_OUI_FROM_DATABASE=BURG-WÄCHTER KG + +OUI:1C4840* + ID_OUI_FROM_DATABASE=IMS Messsysteme GmbH + +OUI:F42853* + ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd. + +OUI:3C46D8* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:6C0273* + ID_OUI_FROM_DATABASE=Shenzhen Jin Yun Video Equipment Co., Ltd. + +OUI:2CFAA2* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:F0761C* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:F42833* + ID_OUI_FROM_DATABASE=MMPC Inc. + +OUI:244F1D* + ID_OUI_FROM_DATABASE=iRule LLC + +OUI:BC9CC5* + ID_OUI_FROM_DATABASE=Beijing Huafei Technology Co., Ltd. + +OUI:505065* + ID_OUI_FROM_DATABASE=TAKT Corporation + +OUI:D00AAB* + ID_OUI_FROM_DATABASE=Yokogawa Digital Computer Corporation + +OUI:A4A4D3* + ID_OUI_FROM_DATABASE=Bluebank Communication Technology Co.Ltd + +OUI:74F413* + ID_OUI_FROM_DATABASE=Maxwell Forest + +OUI:34F0CA* + ID_OUI_FROM_DATABASE=Shenzhen Linghangyuan Digital Technology Co.,Ltd. + +OUI:84183A* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:30B5F1* + ID_OUI_FROM_DATABASE=Aitexin Technology Co., Ltd + +OUI:882950* + ID_OUI_FROM_DATABASE=Dalian Netmoon Tech Develop Co.,Ltd + +OUI:08CD9B* + ID_OUI_FROM_DATABASE=samtec automotive electronics & software GmbH + +OUI:28FCF6* + ID_OUI_FROM_DATABASE=Shenzhen Xin KingBrand enterprises Co.,Ltd + +OUI:4C26E7* + ID_OUI_FROM_DATABASE=Welgate Co., Ltd. + +OUI:94D60E* + ID_OUI_FROM_DATABASE=shenzhen yunmao information technologies co., ltd + +OUI:7C6AC3* + ID_OUI_FROM_DATABASE=GatesAir, Inc + +OUI:3CCD5A* + ID_OUI_FROM_DATABASE=Technische Alternative GmbH + +OUI:604826* + ID_OUI_FROM_DATABASE=Newbridge Technologies Int. Ltd. + +OUI:24D13F* + ID_OUI_FROM_DATABASE=MEXUS CO.,LTD + +OUI:702C1F* + ID_OUI_FROM_DATABASE=Wisol + +OUI:9CBD9D* + ID_OUI_FROM_DATABASE=SkyDisk, Inc. + +OUI:74C621* + ID_OUI_FROM_DATABASE=Zhejiang Hite Renewable Energy Co.,LTD + +OUI:44C306* + ID_OUI_FROM_DATABASE=SIFROM Inc. + +OUI:54A31B* + ID_OUI_FROM_DATABASE=Shenzhen Linkworld Technology Co,.LTD + +OUI:5CE7BF* + ID_OUI_FROM_DATABASE=New Singularity International Technical Development Co.,Ltd + +OUI:1CEEE8* + ID_OUI_FROM_DATABASE=Ilshin Elecom + +OUI:6C641A* + ID_OUI_FROM_DATABASE=Penguin Computing + +OUI:E036E3* + ID_OUI_FROM_DATABASE=Stage One International Co., Ltd. + +OUI:34DE34* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:34466F* + ID_OUI_FROM_DATABASE=HiTEM Engineering + +OUI:2C39C1* + ID_OUI_FROM_DATABASE=Ciena Corporation + +OUI:6C2C06* + ID_OUI_FROM_DATABASE=OOO NPP Systemotechnika-NN + +OUI:54EE75* + ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. + +OUI:60812B* + ID_OUI_FROM_DATABASE=Custom Control Concepts + +OUI:F86601* + ID_OUI_FROM_DATABASE=Suzhou Chi-tek information technology Co., Ltd + +OUI:FC4AE9* + ID_OUI_FROM_DATABASE=Castlenet Technology Inc. + +OUI:34E42A* + ID_OUI_FROM_DATABASE=Automatic Bar Controls Inc. + +OUI:B87CF2* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:20A787* + ID_OUI_FROM_DATABASE=Bointec Taiwan Corporation Limited + +OUI:6CAAB3* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:A481EE* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:54C80F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D42122* + ID_OUI_FROM_DATABASE=Sercomm Corporation + +OUI:EC1766* + ID_OUI_FROM_DATABASE=Research Centre Module + +OUI:7CFF62* + ID_OUI_FROM_DATABASE=Huizhou Super Electron Technology Co.,Ltd. + +OUI:A0D12A* + ID_OUI_FROM_DATABASE=AXPRO Technology Inc. + +OUI:30C750* + ID_OUI_FROM_DATABASE=MIC Technology Group + +OUI:442938* + ID_OUI_FROM_DATABASE=NietZsche enterprise Co.Ltd. + +OUI:D881CE* + ID_OUI_FROM_DATABASE=AHN INC. + +OUI:E0D31A* + ID_OUI_FROM_DATABASE=EQUES Technology Co., Limited + +OUI:9C3EAA* + ID_OUI_FROM_DATABASE=EnvyLogic Co.,Ltd. + +OUI:909864* + ID_OUI_FROM_DATABASE=Impex-Sat GmbH&Co KG + +OUI:DCE578* + ID_OUI_FROM_DATABASE=Experimental Factory of Scientific Engineering and Special Design Department + +OUI:949F3F* + ID_OUI_FROM_DATABASE=Optek Digital Technology company limited + +OUI:987770* + ID_OUI_FROM_DATABASE=Pep Digital Technology (Guangzhou) Co., Ltd + +OUI:4411C2* + ID_OUI_FROM_DATABASE=Telegartner Karl Gartner GmbH + +OUI:9451BF* + ID_OUI_FROM_DATABASE=Hyundai ESG + +OUI:4C7F62* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:841766* + ID_OUI_FROM_DATABASE=Weifang GoerTek Electronics Co., Ltd + +OUI:F03FF8* + ID_OUI_FROM_DATABASE=R L Drake + +OUI:B0C554* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:54D163* + ID_OUI_FROM_DATABASE=MAX-TECH,INC + +OUI:E41218* + ID_OUI_FROM_DATABASE=ShenZhen Rapoo Technology Co., Ltd. + +OUI:2C8A72* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:4486C1* + ID_OUI_FROM_DATABASE=Siemens Low Voltage & Products + +OUI:C83168* + ID_OUI_FROM_DATABASE=eZEX corporation + +OUI:F84A73* + ID_OUI_FROM_DATABASE=EUMTECH CO., LTD + +OUI:880F10* + ID_OUI_FROM_DATABASE=Huami Information Technology Co.,Ltd. + +OUI:24336C* + ID_OUI_FROM_DATABASE=Private + +OUI:C46BB4* + ID_OUI_FROM_DATABASE=myIDkey + +OUI:ECE512* + ID_OUI_FROM_DATABASE=tado GmbH + +OUI:30918F* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:FC09F6* + ID_OUI_FROM_DATABASE=GUANGDONG TONZE ELECTRIC CO.,LTD + +OUI:687848* + ID_OUI_FROM_DATABASE=Westunitis Co., Ltd. + +OUI:A8B9B3* + ID_OUI_FROM_DATABASE=ESSYS + +OUI:64B370* + ID_OUI_FROM_DATABASE=PowerComm Solutions LLC + +OUI:D86595* + ID_OUI_FROM_DATABASE=Toy's Myth Inc. + +OUI:D8DD5F* + ID_OUI_FROM_DATABASE=BALMUDA Inc. + +OUI:88D962* + ID_OUI_FROM_DATABASE=Canopus Systems US LLC + +OUI:24C848* + ID_OUI_FROM_DATABASE=mywerk system GmbH + +OUI:2C18AE* + ID_OUI_FROM_DATABASE=Trend Electronics Co., Ltd. + +OUI:E097F2* + ID_OUI_FROM_DATABASE=Atomax Inc. + +OUI:90F3B7* + ID_OUI_FROM_DATABASE=Kirisun Communications Co., Ltd. + +OUI:DCAD9E* + ID_OUI_FROM_DATABASE=GreenPriz + +OUI:B4827B* + ID_OUI_FROM_DATABASE=AKG Acoustics GmbH + +OUI:908C44* + ID_OUI_FROM_DATABASE=H.K ZONGMU TECHNOLOGY CO., LTD. + +OUI:0C473D* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:4C5E0C* + ID_OUI_FROM_DATABASE=Routerboard.com + +OUI:9CF8DB* + ID_OUI_FROM_DATABASE=shenzhen eyunmei technology co,.ltd + +OUI:644214* + ID_OUI_FROM_DATABASE=Swisscom Energy Solutions AG + +OUI:8CCDA2* + ID_OUI_FROM_DATABASE=ACTP, Inc. + +OUI:CC720F* + ID_OUI_FROM_DATABASE=Viscount Systems Inc. + +OUI:906717* + ID_OUI_FROM_DATABASE=Alphion India Private Limited + +OUI:24050F* + ID_OUI_FROM_DATABASE=MTN Electronic Co. Ltd + +OUI:40B6B1* + ID_OUI_FROM_DATABASE=SUNGSAM CO,.Ltd + +OUI:98FF6A* + ID_OUI_FROM_DATABASE=OTEC(Shanghai)Technology Co.,Ltd. + +OUI:AC6BAC* + ID_OUI_FROM_DATABASE=Jenny Science AG + +OUI:707C18* + ID_OUI_FROM_DATABASE=ADATA Technology Co., Ltd + +OUI:FC4B1C* + ID_OUI_FROM_DATABASE=INTERSENSOR S.R.L. + +OUI:1879A2* + ID_OUI_FROM_DATABASE=GMJ ELECTRIC LIMITED + +OUI:E0C86A* + ID_OUI_FROM_DATABASE=SHENZHEN TW-SCIE Co., Ltd + +OUI:80BAE6* + ID_OUI_FROM_DATABASE=Neets + +OUI:3C18A0* + ID_OUI_FROM_DATABASE=Luxshare Precision Industry Co.,Ltd. + +OUI:4CB81C* + ID_OUI_FROM_DATABASE=SAM Electronics GmbH + +OUI:041A04* + ID_OUI_FROM_DATABASE=WaveIP + +OUI:50206B* + ID_OUI_FROM_DATABASE=Emerson Climate Technologies Transportation Solutions + +OUI:C8EE75* + ID_OUI_FROM_DATABASE=Pishion International Co. Ltd + +OUI:CC3429* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:407496* + ID_OUI_FROM_DATABASE=aFUN TECHNOLOGY INC. + +OUI:18C8E7* + ID_OUI_FROM_DATABASE=Shenzhen Hualistone Technology Co.,Ltd + +OUI:3CF748* + ID_OUI_FROM_DATABASE=Shenzhen Linsn Technology Development Co.,Ltd + +OUI:9C039E* + ID_OUI_FROM_DATABASE=Beijing Winchannel Software Technology Co., Ltd + +OUI:F8A963* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:48A2B7* + ID_OUI_FROM_DATABASE=Kodofon JSC + +OUI:443C9C* + ID_OUI_FROM_DATABASE=Pintsch Tiefenbach GmbH + +OUI:F81CE5* + ID_OUI_FROM_DATABASE=Telefonbau Behnke GmbH + +OUI:BC2D98* + ID_OUI_FROM_DATABASE=ThinGlobal LLC + +OUI:7C72E4* + ID_OUI_FROM_DATABASE=Unikey Technologies + +OUI:181BEB* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:CC7498* + ID_OUI_FROM_DATABASE=Filmetrics Inc. + +OUI:7C6AB3* + ID_OUI_FROM_DATABASE=IBC TECHNOLOGIES INC. + +OUI:F0321A* + ID_OUI_FROM_DATABASE=Mita-Teknik A/S + +OUI:4CD7B6* + ID_OUI_FROM_DATABASE=Helmer Scientific + +OUI:746F3D* + ID_OUI_FROM_DATABASE=Contec GmbH + +OUI:483D32* + ID_OUI_FROM_DATABASE=Syscor Controls & Automation + +OUI:9031CD* + ID_OUI_FROM_DATABASE=Onyx Healthcare Inc. + +OUI:A0E453* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:404A18* + ID_OUI_FROM_DATABASE=Addrek Smart Solutions + +OUI:C4C0AE* + ID_OUI_FROM_DATABASE=MIDORI ELECTRONIC CO., LTD. + +OUI:90837A* + ID_OUI_FROM_DATABASE=General Electric Water & Process Technologies + +OUI:089758* + ID_OUI_FROM_DATABASE=Shenzhen Strong Rising Electronics Co.,Ltd DongGuan Subsidiary + +OUI:B424E7* + ID_OUI_FROM_DATABASE=Codetek Technology Co.,Ltd + +OUI:44EE30* + ID_OUI_FROM_DATABASE=Budelmann Elektronik GmbH + +OUI:38DBBB* + ID_OUI_FROM_DATABASE=Sunbow Telecom Co., Ltd. + +OUI:2493CA* + ID_OUI_FROM_DATABASE=Voxtronic Technology Computer-Systeme GmbH + +OUI:688AB5* + ID_OUI_FROM_DATABASE=EDP Servicos + +OUI:407A80* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F06130* + ID_OUI_FROM_DATABASE=Advantage Pharmacy Services, LLC + +OUI:D481CA* + ID_OUI_FROM_DATABASE=iDevices, LLC + +OUI:B898F7* + ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co,Ltd.ShenZhen + +OUI:C0F1C4* + ID_OUI_FROM_DATABASE=Pacidal Corporation Ltd. + +OUI:D858D7* + ID_OUI_FROM_DATABASE=CZ.NIC, z.s.p.o. + +OUI:10B713* + ID_OUI_FROM_DATABASE=Private + +OUI:E8E770* + ID_OUI_FROM_DATABASE=Warp9 Tech Design, Inc. + +OUI:78CA5E* + ID_OUI_FROM_DATABASE=ELNO + +OUI:98FFD0* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:50A054* + ID_OUI_FROM_DATABASE=Actineon + +OUI:48EE86* + ID_OUI_FROM_DATABASE=UTStarcom (China) Co.,Ltd + +OUI:5056A8* + ID_OUI_FROM_DATABASE=Jolla Ltd + +OUI:D09D0A* + ID_OUI_FROM_DATABASE=LINKCOM + +OUI:54FB58* + ID_OUI_FROM_DATABASE=WISEWARE, Lda + +OUI:C0A0BB* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:28A1EB* + ID_OUI_FROM_DATABASE=ETEK TECHNOLOGY (SHENZHEN) CO.,LTD + +OUI:4CCBF5* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:F0F5AE* + ID_OUI_FROM_DATABASE=Adaptrum Inc. + +OUI:F42896* + ID_OUI_FROM_DATABASE=SPECTO PAINEIS ELETRONICOS LTDA + +OUI:1C7B21* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:BC9680* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:9C2840* + ID_OUI_FROM_DATABASE=Discovery Technology,LTD.. + +OUI:F89FB8* + ID_OUI_FROM_DATABASE=YAZAKI Energy System Corporation + +OUI:F037A1* + ID_OUI_FROM_DATABASE=Huike Electronics (SHENZHEN) CO., LTD. + +OUI:6CD1B0* + ID_OUI_FROM_DATABASE=WING SING ELECTRONICS HONG KONG LIMITED + +OUI:A4F522* + ID_OUI_FROM_DATABASE=CHOFU SEISAKUSHO CO.,LTD + +OUI:7CE56B* + ID_OUI_FROM_DATABASE=ESEN Optoelectronics Technology Co.,Ltd. + +OUI:CC4703* + ID_OUI_FROM_DATABASE=Intercon Systems Co., Ltd. + +OUI:5C3327* + ID_OUI_FROM_DATABASE=Spazio Italia srl + +OUI:F85BC9* + ID_OUI_FROM_DATABASE=M-Cube Spa + +OUI:8005DF* + ID_OUI_FROM_DATABASE=Montage Technology Group Limited + +OUI:78E8B6* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:041B94* + ID_OUI_FROM_DATABASE=Host Mobility AB + +OUI:CC2A80* + ID_OUI_FROM_DATABASE=Micro-Biz intelligence solutions Co.,Ltd + +OUI:3859F8* + ID_OUI_FROM_DATABASE=MindMade Sp. z o.o. + +OUI:5C026A* + ID_OUI_FROM_DATABASE=Applied Vision Corporation + +OUI:7CBD06* + ID_OUI_FROM_DATABASE=AE REFUsol + +OUI:94BA56* + ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd. + +OUI:2894AF* + ID_OUI_FROM_DATABASE=Samhwa Telecom + +OUI:740EDB* + ID_OUI_FROM_DATABASE=Optowiz Co., Ltd + +OUI:00A2FF* + ID_OUI_FROM_DATABASE=abatec group AG + +OUI:D095C7* + ID_OUI_FROM_DATABASE=Pantech Co., Ltd. + +OUI:D02C45* + ID_OUI_FROM_DATABASE=littleBits Electronics, Inc. + +OUI:5027C7* + ID_OUI_FROM_DATABASE=TECHNART Co.,Ltd + +OUI:248000* + ID_OUI_FROM_DATABASE=Westcontrol AS + +OUI:F84A7F* + ID_OUI_FROM_DATABASE=Innometriks Inc + +OUI:58639A* + ID_OUI_FROM_DATABASE=TPL SYSTEMES + +OUI:0C9B13* + ID_OUI_FROM_DATABASE=Shanghai Magic Mobile Telecommunication Co.Ltd. + +OUI:3C15EA* + ID_OUI_FROM_DATABASE=TESCOM CO., LTD. + +OUI:B4CCE9* + ID_OUI_FROM_DATABASE=PROSYST + +OUI:34A3BF* + ID_OUI_FROM_DATABASE=Terewave. Inc. + +OUI:B0CE18* + ID_OUI_FROM_DATABASE=Zhejiang shenghui lighting co.,Ltd + +OUI:503CC4* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:286D97* + ID_OUI_FROM_DATABASE=SAMJIN Co., Ltd. + +OUI:ACE42E* + ID_OUI_FROM_DATABASE=SK hynix + +OUI:08EF3B* + ID_OUI_FROM_DATABASE=MCS Logic Inc. + +OUI:806C8B* + ID_OUI_FROM_DATABASE=KAESER KOMPRESSOREN AG + +OUI:048C03* + ID_OUI_FROM_DATABASE=ThinPAD Technology (Shenzhen)CO.,LTD + +OUI:84E629* + ID_OUI_FROM_DATABASE=Bluwan SA + +OUI:34CD6D* + ID_OUI_FROM_DATABASE=CommSky Technologies + +OUI:C47F51* + ID_OUI_FROM_DATABASE=Inventek Systems + +OUI:E8D4E0* + ID_OUI_FROM_DATABASE=Beijing BenyWave Technology Co., Ltd. + +OUI:3889DC* + ID_OUI_FROM_DATABASE=Opticon Sensors Europe B.V. + +OUI:681D64* + ID_OUI_FROM_DATABASE=Sunwave Communications Co., Ltd + +OUI:F4CD90* + ID_OUI_FROM_DATABASE=Vispiron Rotec GmbH + +OUI:E438F2* + ID_OUI_FROM_DATABASE=Advantage Controls + +OUI:24C9A1* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:C8F386* + ID_OUI_FROM_DATABASE=Shenzhen Xiaoniao Technology Co.,Ltd + +OUI:E8CE06* + ID_OUI_FROM_DATABASE=SkyHawke Technologies, LLC. + +OUI:B0808C* + ID_OUI_FROM_DATABASE=Laser Light Engines + +OUI:C419EC* + ID_OUI_FROM_DATABASE=Qualisys AB + +OUI:981094* + ID_OUI_FROM_DATABASE=Shenzhen Vsun communication technology Co.,ltd + +OUI:082719* + ID_OUI_FROM_DATABASE=APS systems/electronic AG + +OUI:D4AC4E* + ID_OUI_FROM_DATABASE=BODi rS, LLC + +OUI:B03850* + ID_OUI_FROM_DATABASE=Nanjing CAS-ZDC IOT SYSTEM CO.,LTD + +OUI:C0DA74* + ID_OUI_FROM_DATABASE=Hangzhou Sunyard Technology Co., Ltd. + +OUI:34A843* + ID_OUI_FROM_DATABASE=KYOCERA Display Corporation + +OUI:6C5779* + ID_OUI_FROM_DATABASE=Aclima, Inc. + +OUI:40BD9E* + ID_OUI_FROM_DATABASE=Physio-Control, Inc + +OUI:581CBD* + ID_OUI_FROM_DATABASE=Affinegy + +OUI:F82BC8* + ID_OUI_FROM_DATABASE=Jiangsu Switter Co., Ltd + +OUI:60C397* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:3065EC* + ID_OUI_FROM_DATABASE=Wistron (ChongQing) + +OUI:5CA3EB* + ID_OUI_FROM_DATABASE=Lokel s.r.o. + +OUI:04DF69* + ID_OUI_FROM_DATABASE=Car Connectivity Consortium + +OUI:28DB81* + ID_OUI_FROM_DATABASE=Shanghai Guao Electronic Technology Co., Ltd + +OUI:9CB793* + ID_OUI_FROM_DATABASE=Creatcomm Technology Inc. + +OUI:A0B100* + ID_OUI_FROM_DATABASE=ShenZhen Cando Electronics Co.,Ltd + +OUI:40560C* + ID_OUI_FROM_DATABASE=In Home Displays Ltd + +OUI:9436E0* + ID_OUI_FROM_DATABASE=Sichuan Bihong Broadcast & Television New Technologies Co.,Ltd + +OUI:D4D50D* + ID_OUI_FROM_DATABASE=Southwest Microwave, Inc + +OUI:B8CD93* + ID_OUI_FROM_DATABASE=Penetek, Inc + +OUI:D8FEE3* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:F8516D* + ID_OUI_FROM_DATABASE=Denwa Technology Corp. + +OUI:1078CE* + ID_OUI_FROM_DATABASE=Hanvit SI, Inc. + +OUI:D8DA52* + ID_OUI_FROM_DATABASE=APATOR S.A. + +OUI:107A86* + ID_OUI_FROM_DATABASE=U&U ENGINEERING INC. + +OUI:980D2E* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:842F75* + ID_OUI_FROM_DATABASE=Innokas Group + +OUI:D4BF7F* + ID_OUI_FROM_DATABASE=UPVEL + +OUI:5061D6* + ID_OUI_FROM_DATABASE=Indu-Sol GmbH + +OUI:68EC62* + ID_OUI_FROM_DATABASE=YODO Technology Corp. Ltd. + +OUI:F07F0C* + ID_OUI_FROM_DATABASE=Leopold Kostal GmbH &Co. KG + +OUI:5C22C4* + ID_OUI_FROM_DATABASE=DAE EUN ELETRONICS CO., LTD + +OUI:08482C* + ID_OUI_FROM_DATABASE=Raycore Taiwan Co., LTD. + +OUI:F4B381* + ID_OUI_FROM_DATABASE=WindowMaster A/S + +OUI:74F102* + ID_OUI_FROM_DATABASE=Beijing HCHCOM Technology Co., Ltd + +OUI:080EA8* + ID_OUI_FROM_DATABASE=Velex s.r.l. + +OUI:0086A0* + ID_OUI_FROM_DATABASE=Private + +OUI:60FE1E* + ID_OUI_FROM_DATABASE=China Palms Telecom.Ltd + +OUI:841E26* + ID_OUI_FROM_DATABASE=KERNEL-I Co.,LTD + +OUI:349D90* + ID_OUI_FROM_DATABASE=Heinzmann GmbH & CO. KG + +OUI:D4016D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:FC1186* + ID_OUI_FROM_DATABASE=Logic3 plc + +OUI:50CD32* + ID_OUI_FROM_DATABASE=NanJing Chaoran Science & Technology Co.,Ltd. + +OUI:683EEC* + ID_OUI_FROM_DATABASE=ERECA + +OUI:44619C* + ID_OUI_FROM_DATABASE=FONsystem co. ltd. + +OUI:BCBAE1* + ID_OUI_FROM_DATABASE=AREC Inc. + +OUI:18FA6F* + ID_OUI_FROM_DATABASE=ISC applied systems corp + +OUI:9C9726* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:880905* + ID_OUI_FROM_DATABASE=MTMCommunications + +OUI:C42628* + ID_OUI_FROM_DATABASE=Airo Wireless + +OUI:745F00* + ID_OUI_FROM_DATABASE=Samsung Semiconductor Inc. + +OUI:541FD5* + ID_OUI_FROM_DATABASE=Advantage Electronics + +OUI:90FF79* + ID_OUI_FROM_DATABASE=Metro Ethernet Forum + +OUI:E08177* + ID_OUI_FROM_DATABASE=GreenBytes, Inc. + +OUI:48F230* + ID_OUI_FROM_DATABASE=Ubizcore Co.,LTD + +OUI:B0C95B* + ID_OUI_FROM_DATABASE=Beijing Symtech CO.,LTD + +OUI:881544* + ID_OUI_FROM_DATABASE=Meraki, Inc. + +OUI:DCA989* + ID_OUI_FROM_DATABASE=MACANDC + +OUI:C05E6F* + ID_OUI_FROM_DATABASE=V. Stonkaus firma Kodinis Raktas + +OUI:6CD146* + ID_OUI_FROM_DATABASE=Smartek d.o.o. + +OUI:E0C2B7* + ID_OUI_FROM_DATABASE=Masimo Corporation + +OUI:F82EDB* + ID_OUI_FROM_DATABASE=RTW GmbH & Co. KG + +OUI:60A44C* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:045FA7* + ID_OUI_FROM_DATABASE=Shenzhen Yichen Technology Development Co.,LTD + +OUI:983F9F* + ID_OUI_FROM_DATABASE=China SSJ (Suzhou) Network Technology Inc. + +OUI:F02329* + ID_OUI_FROM_DATABASE=SHOWA DENKI CO.,LTD. + +OUI:6499A0* + ID_OUI_FROM_DATABASE=AG Elektronik AB + +OUI:A80180* + ID_OUI_FROM_DATABASE=IMAGO Technologies GmbH + +OUI:044CEF* + ID_OUI_FROM_DATABASE=Fujian Sanao Technology Co.,Ltd + +OUI:DC1DD4* + ID_OUI_FROM_DATABASE=Microstep-MIS spol. s r.o. + +OUI:E01877* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:149448* + ID_OUI_FROM_DATABASE=BLU CASTLE S.A. + +OUI:40516C* + ID_OUI_FROM_DATABASE=Grandex International Corporation + +OUI:D0D471* + ID_OUI_FROM_DATABASE=MVTECH co., Ltd + +OUI:34ADE4* + ID_OUI_FROM_DATABASE=Shanghai Chint Power Systems Co., Ltd. + +OUI:1853E0* + ID_OUI_FROM_DATABASE=Hanyang Digitech Co.Ltd + +OUI:C4E032* + ID_OUI_FROM_DATABASE=IEEE 1904.1 Working Group + +OUI:ACDBDA* + ID_OUI_FROM_DATABASE=Shenzhen Geniatech Inc, Ltd + +OUI:A42C08* + ID_OUI_FROM_DATABASE=Masterwork Automodules + +OUI:60B185* + ID_OUI_FROM_DATABASE=ATH system + +OUI:504F94* + ID_OUI_FROM_DATABASE=Loxone Electronics GmbH + +OUI:8C078C* + ID_OUI_FROM_DATABASE=FLOW DATA INC + +OUI:8887DD* + ID_OUI_FROM_DATABASE=DarbeeVision Inc. + +OUI:807B1E* + ID_OUI_FROM_DATABASE=Corsair Components + +OUI:A0E25A* + ID_OUI_FROM_DATABASE=Amicus SK, s.r.o. + +OUI:F87B62* + ID_OUI_FROM_DATABASE=FASTWEL INTERNATIONAL CO., LTD. Taiwan Branch + +OUI:B49842* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:9C9C1D* + ID_OUI_FROM_DATABASE=Starkey Labs Inc. + +OUI:90CC24* + ID_OUI_FROM_DATABASE=Synaptics, Inc + +OUI:AC1702* + ID_OUI_FROM_DATABASE=Fibar Group sp. z o.o. + +OUI:7898FD* + ID_OUI_FROM_DATABASE=Q9 Networks Inc. + +OUI:3C57D5* + ID_OUI_FROM_DATABASE=FiveCo + +OUI:4C2258* + ID_OUI_FROM_DATABASE=cozybit, Inc. + +OUI:10EA59* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:34FA40* + ID_OUI_FROM_DATABASE=Guangzhou Robustel Technologies Co., Limited + +OUI:181725* + ID_OUI_FROM_DATABASE=Cameo Communications, Inc. + +OUI:E82E24* + ID_OUI_FROM_DATABASE=Out of the Fog Research LLC + +OUI:1C52D6* + ID_OUI_FROM_DATABASE=FLAT DISPLAY TECHNOLOGY CORPORATION + +OUI:40270B* + ID_OUI_FROM_DATABASE=Mobileeco Co., Ltd + +OUI:ACE97F* + ID_OUI_FROM_DATABASE=IoT Tech Limited + +OUI:301518* + ID_OUI_FROM_DATABASE=Ubiquitous Communication Co. ltd. + +OUI:101248* + ID_OUI_FROM_DATABASE=ITG, Inc. + +OUI:106FEF* + ID_OUI_FROM_DATABASE=Ad-Sol Nissin Corp + +OUI:A036F0* + ID_OUI_FROM_DATABASE=Comprehensive Power + +OUI:180CAC* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:00DB1E* + ID_OUI_FROM_DATABASE=Albedo Telecom SL + +OUI:74943D* + ID_OUI_FROM_DATABASE=AgJunction + +OUI:080C0B* + ID_OUI_FROM_DATABASE=SysMik GmbH Dresden + +OUI:C8FB26* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:7CC8AB* + ID_OUI_FROM_DATABASE=Acro Associates, Inc. + +OUI:C4DA26* + ID_OUI_FROM_DATABASE=NOBLEX SA + +OUI:1CC316* + ID_OUI_FROM_DATABASE=MileSight Technology Co., Ltd. + +OUI:C4E7BE* + ID_OUI_FROM_DATABASE=SCSpro Co.,Ltd + +OUI:105F49* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:4495FA* + ID_OUI_FROM_DATABASE=Qingdao Santong Digital Technology Co.Ltd + +OUI:60F2EF* + ID_OUI_FROM_DATABASE=VisionVera International Co., Ltd. + +OUI:B01266* + ID_OUI_FROM_DATABASE=Futaba-Kikaku + +OUI:909DE0* + ID_OUI_FROM_DATABASE=Newland Design + Assoc. Inc. + +OUI:64D814* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6CE4CE* + ID_OUI_FROM_DATABASE=Villiger Security Solutions AG + +OUI:30F33A* + ID_OUI_FROM_DATABASE=+plugg srl + +OUI:58CF4B* + ID_OUI_FROM_DATABASE=Lufkin Industries + +OUI:C4393A* + ID_OUI_FROM_DATABASE=SMC Networks Inc + +OUI:C4017C* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:D45C70* + ID_OUI_FROM_DATABASE=Wi-Fi Alliance + +OUI:08EBED* + ID_OUI_FROM_DATABASE=World Elite Technology Co.,LTD + +OUI:60BC4C* + ID_OUI_FROM_DATABASE=EWM Hightec Welding GmbH + +OUI:F41E26* + ID_OUI_FROM_DATABASE=Simon-Kaloi Engineering + +OUI:C44567* + ID_OUI_FROM_DATABASE=SAMBON PRECISON and ELECTRONICS + +OUI:D0738E* + ID_OUI_FROM_DATABASE=DONG OH PRECISION CO., LTD. + +OUI:E8718D* + ID_OUI_FROM_DATABASE=Elsys Equipamentos Eletronicos Ltda + +OUI:3C83B5* + ID_OUI_FROM_DATABASE=Advance Vision Electronics Co. Ltd. + +OUI:808287* + ID_OUI_FROM_DATABASE=ATCOM Technology Co.Ltd. + +OUI:28A192* + ID_OUI_FROM_DATABASE=GERP Solution + +OUI:A08C15* + ID_OUI_FROM_DATABASE=Gerhard D. Wempe KG + +OUI:8CE081* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:485261* + ID_OUI_FROM_DATABASE=SOREEL + +OUI:10FBF0* + ID_OUI_FROM_DATABASE=KangSheng LTD. + +OUI:3C57BD* + ID_OUI_FROM_DATABASE=Kessler Crane Inc. + +OUI:600F77* + ID_OUI_FROM_DATABASE=SilverPlus, Inc + +OUI:6851B7* + ID_OUI_FROM_DATABASE=PowerCloud Systems, Inc. + +OUI:A44E2D* + ID_OUI_FROM_DATABASE=Adaptive Wireless Solutions, LLC + +OUI:3CC12C* + ID_OUI_FROM_DATABASE=AES Corporation + +OUI:0CCDFB* + ID_OUI_FROM_DATABASE=EDIC Systems Inc. + +OUI:2CE2A8* + ID_OUI_FROM_DATABASE=DeviceDesign + +OUI:B49DB4* + ID_OUI_FROM_DATABASE=Axion Technologies Inc. + +OUI:D8182B* + ID_OUI_FROM_DATABASE=Conti Temic Microelectronic GmbH + +OUI:304449* + ID_OUI_FROM_DATABASE=PLATH GmbH + +OUI:94FD2E* + ID_OUI_FROM_DATABASE=Shanghai Uniscope Technologies Co.,Ltd + +OUI:64A341* + ID_OUI_FROM_DATABASE=Wonderlan (Beijing) Technology Co., Ltd. + +OUI:8CAE4C* + ID_OUI_FROM_DATABASE=Plugable Technologies + +OUI:D8D5B9* + ID_OUI_FROM_DATABASE=Rainforest Automation, Inc. + +OUI:C0A0E2* + ID_OUI_FROM_DATABASE=Eden Innovations + +OUI:E8ABFA* + ID_OUI_FROM_DATABASE=Shenzhen Reecam Tech.Ltd. + +OUI:58874C* + ID_OUI_FROM_DATABASE=LITE-ON CLEAN ENERGY TECHNOLOGY CORP. + +OUI:E85BF0* + ID_OUI_FROM_DATABASE=Imaging Diagnostics + +OUI:20DC93* + ID_OUI_FROM_DATABASE=Cheetah Hi-Tech, Inc. + +OUI:7846C4* + ID_OUI_FROM_DATABASE=DAEHAP HYPER-TECH + +OUI:0CD9C1* + ID_OUI_FROM_DATABASE=Visteon Corporation + +OUI:68AB8A* + ID_OUI_FROM_DATABASE=RF IDeas + +OUI:70E24C* + ID_OUI_FROM_DATABASE=SAE IT-systems GmbH & Co. KG + +OUI:88615A* + ID_OUI_FROM_DATABASE=Siano Mobile Silicon Ltd. + +OUI:30215B* + ID_OUI_FROM_DATABASE=Shenzhen Ostar Display Electronic Co.,Ltd + +OUI:DC028E* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:DCB058* + ID_OUI_FROM_DATABASE=Bürkert Werke GmbH + +OUI:641C67* + ID_OUI_FROM_DATABASE=DIGIBRAS INDUSTRIA DO BRASILS/A + +OUI:C8E1A7* + ID_OUI_FROM_DATABASE=Vertu Corporation Limited + +OUI:88D7BC* + ID_OUI_FROM_DATABASE=DEP Company + +OUI:F49466* + ID_OUI_FROM_DATABASE=CountMax, ltd + +OUI:4CAB33* + ID_OUI_FROM_DATABASE=KST technology + +OUI:5CE0F6* + ID_OUI_FROM_DATABASE=NIC.br- Nucleo de Informacao e Coordenacao do Ponto BR + +OUI:00E666* + ID_OUI_FROM_DATABASE=ARIMA Communications Corp. + +OUI:F8E4FB* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:5887E2* + ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd. + +OUI:B4DFFA* + ID_OUI_FROM_DATABASE=Litemax Electronics Inc. + +OUI:48F8B3* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:681CA2* + ID_OUI_FROM_DATABASE=Rosewill Inc. + +OUI:7C092B* + ID_OUI_FROM_DATABASE=Bekey A/S + +OUI:D808F5* + ID_OUI_FROM_DATABASE=Arcadia Networks Co. Ltd. + +OUI:84DF0C* + ID_OUI_FROM_DATABASE=NET2GRID BV + +OUI:3CB87A* + ID_OUI_FROM_DATABASE=Private + +OUI:E425E9* + ID_OUI_FROM_DATABASE=Color-Chip + +OUI:F44848* + ID_OUI_FROM_DATABASE=Amscreen Group Ltd + +OUI:441319* + ID_OUI_FROM_DATABASE=WKK TECHNOLOGY LTD. + +OUI:088F2C* + ID_OUI_FROM_DATABASE=Hills Sound Vision & Lighting + +OUI:3C9F81* + ID_OUI_FROM_DATABASE=Shenzhen CATIC Bit Communications Technology Co.,Ltd + +OUI:18339D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:642216* + ID_OUI_FROM_DATABASE=Shandong Taixin Electronic co.,Ltd + +OUI:D43D7E* + ID_OUI_FROM_DATABASE=Micro-Star Int'l Co, Ltd + +OUI:64517E* + ID_OUI_FROM_DATABASE=LONG BEN (DONGGUAN) ELECTRONIC TECHNOLOGY CO.,LTD. + +OUI:0C57EB* + ID_OUI_FROM_DATABASE=Mueller Systems + +OUI:48282F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:745327* + ID_OUI_FROM_DATABASE=COMMSEN CO., LIMITED + +OUI:E47185* + ID_OUI_FROM_DATABASE=Securifi Ltd + +OUI:881036* + ID_OUI_FROM_DATABASE=Panodic(ShenZhen) Electronics Limted + +OUI:18F87A* + ID_OUI_FROM_DATABASE=i3 International Inc. + +OUI:142DF5* + ID_OUI_FROM_DATABASE=Amphitech + +OUI:C08ADE* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:90F72F* + ID_OUI_FROM_DATABASE=Phillips Machine & Welding Co., Inc. + +OUI:B45570* + ID_OUI_FROM_DATABASE=Borea + +OUI:5C5015* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0CD2B5* + ID_OUI_FROM_DATABASE=Binatone Telecommunication Pvt. Ltd + +OUI:4846F1* + ID_OUI_FROM_DATABASE=Uros Oy + +OUI:1CD40C* + ID_OUI_FROM_DATABASE=Kriwan Industrie-Elektronik GmbH + +OUI:747B7A* + ID_OUI_FROM_DATABASE=ETH Inc. + +OUI:1C7C45* + ID_OUI_FROM_DATABASE=Vitek Industrial Video Products, Inc. + +OUI:C8AE9C* + ID_OUI_FROM_DATABASE=Shanghai TYD Elecronic Technology Co. Ltd + +OUI:A44C11* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:782544* + ID_OUI_FROM_DATABASE=Omnima Limited + +OUI:D4DF57* + ID_OUI_FROM_DATABASE=Alpinion Medical Systems + +OUI:5048EB* + ID_OUI_FROM_DATABASE=BEIJING HAIHEJINSHENG NETWORK TECHNOLOGY CO. LTD. + +OUI:40AC8D* + ID_OUI_FROM_DATABASE=Data Management, Inc. + +OUI:54466B* + ID_OUI_FROM_DATABASE=Shenzhen CZTIC Electronic Technology Co., Ltd + +OUI:1C3477* + ID_OUI_FROM_DATABASE=Innovation Wireless + +OUI:4423AA* + ID_OUI_FROM_DATABASE=Farmage Co., Ltd. + +OUI:A0EF84* + ID_OUI_FROM_DATABASE=Seine Image Int'l Co., Ltd + +OUI:AC7A42* + ID_OUI_FROM_DATABASE=iConnectivity + +OUI:5869F9* + ID_OUI_FROM_DATABASE=Fusion Transactive Ltd. + +OUI:B0C83F* + ID_OUI_FROM_DATABASE=Jiangsu Cynray IOT Co., Ltd. + +OUI:CC14A6* + ID_OUI_FROM_DATABASE=Yichun MyEnergy Domain, Inc + +OUI:98D686* + ID_OUI_FROM_DATABASE=Chyi Lee industry Co., ltd. + +OUI:20443A* + ID_OUI_FROM_DATABASE=Schneider Electric Asia Pacific Ltd + +OUI:28C914* + ID_OUI_FROM_DATABASE=Taimag Corporation + +OUI:4C7897* + ID_OUI_FROM_DATABASE=Arrowhead Alarm Products Ltd + +OUI:AC0A61* + ID_OUI_FROM_DATABASE=Labor S.r.L. + +OUI:B482C5* + ID_OUI_FROM_DATABASE=Relay2, Inc. + +OUI:60D1AA* + ID_OUI_FROM_DATABASE=Vishal Telecommunications Pvt Ltd + +OUI:CCC104* + ID_OUI_FROM_DATABASE=Applied Technical Systems + +OUI:709BA5* + ID_OUI_FROM_DATABASE=Shenzhen Y&D Electronics Co.,LTD. + +OUI:EC42F0* + ID_OUI_FROM_DATABASE=ADL Embedded Solutions, Inc. + +OUI:10BD18* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B0435D* + ID_OUI_FROM_DATABASE=NuLEDs, Inc. + +OUI:A82BD6* + ID_OUI_FROM_DATABASE=Shina System Co., Ltd + +OUI:8CC7AA* + ID_OUI_FROM_DATABASE=Radinet Communications Inc. + +OUI:20014F* + ID_OUI_FROM_DATABASE=Linea Research Ltd + +OUI:80D18B* + ID_OUI_FROM_DATABASE=Hangzhou I'converge Technology Co.,Ltd + +OUI:B4A4B5* + ID_OUI_FROM_DATABASE=Zen Eye Co.,Ltd + +OUI:489153* + ID_OUI_FROM_DATABASE=Weinmann Geräte für Medizin GmbH + Co. KG + +OUI:549D85* + ID_OUI_FROM_DATABASE=EnerAccess inc + +OUI:5CEE79* + ID_OUI_FROM_DATABASE=Global Digitech Co LTD + +OUI:9CE10E* + ID_OUI_FROM_DATABASE=NCTech Ltd + +OUI:28F606* + ID_OUI_FROM_DATABASE=Syes srl + +OUI:A0C3DE* + ID_OUI_FROM_DATABASE=Triton Electronic Systems Ltd. + +OUI:AC3FA4* + ID_OUI_FROM_DATABASE=TAIYO YUDEN CO.,LTD + +OUI:0C130B* + ID_OUI_FROM_DATABASE=Uniqoteq Ltd. + +OUI:14CF8D* + ID_OUI_FROM_DATABASE=OHSUNG ELECTRONICS CO., LTD. + +OUI:808698* + ID_OUI_FROM_DATABASE=Netronics Technologies Inc. + +OUI:2C00F7* + ID_OUI_FROM_DATABASE=XOS + +OUI:809393* + ID_OUI_FROM_DATABASE=Xapt GmbH + +OUI:00DEFB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:90AC3F* + ID_OUI_FROM_DATABASE=BrightSign LLC + +OUI:7CACB2* + ID_OUI_FROM_DATABASE=Bosch Software Innovations GmbH + +OUI:0043FF* + ID_OUI_FROM_DATABASE=KETRON S.R.L. + +OUI:745798* + ID_OUI_FROM_DATABASE=TRUMPF Laser GmbH + Co. KG + +OUI:38E08E* + ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation + +OUI:E4FA1D* + ID_OUI_FROM_DATABASE=PAD Peripheral Advanced Design Inc. + +OUI:4C9E80* + ID_OUI_FROM_DATABASE=KYOKKO ELECTRIC Co., Ltd. + +OUI:A826D9* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:F03A55* + ID_OUI_FROM_DATABASE=Omega Elektronik AS + +OUI:24B88C* + ID_OUI_FROM_DATABASE=Crenus Co.,Ltd. + +OUI:98BC57* + ID_OUI_FROM_DATABASE=SVA TECHNOLOGIES CO.LTD + +OUI:98FE03* + ID_OUI_FROM_DATABASE=Ericsson - North America + +OUI:F0EEBB* + ID_OUI_FROM_DATABASE=VIPAR GmbH + +OUI:54D0ED* + ID_OUI_FROM_DATABASE=AXIM Communications + +OUI:A49005* + ID_OUI_FROM_DATABASE=CHINA GREATWALL COMPUTER SHENZHEN CO.,LTD + +OUI:3055ED* + ID_OUI_FROM_DATABASE=Trex Network LLC + +OUI:D4A02A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0463E0* + ID_OUI_FROM_DATABASE=Nome Oy + +OUI:BCA4E1* + ID_OUI_FROM_DATABASE=Nabto + +OUI:900A3A* + ID_OUI_FROM_DATABASE=PSG Plastic Service GmbH + +OUI:FC5B26* + ID_OUI_FROM_DATABASE=MikroBits + +OUI:5CC213* + ID_OUI_FROM_DATABASE=Fr. Sauter AG + +OUI:581D91* + ID_OUI_FROM_DATABASE=Advanced Mobile Telecom co.,ltd. + +OUI:9CB008* + ID_OUI_FROM_DATABASE=Ubiquitous Computing Technology Corporation + +OUI:00376D* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:E0EF25* + ID_OUI_FROM_DATABASE=Lintes Technology Co., Ltd. + +OUI:CCEED9* + ID_OUI_FROM_DATABASE=VAHLE DETO GmbH + +OUI:645EBE* + ID_OUI_FROM_DATABASE=Yahoo! JAPAN + +OUI:CCC50A* + ID_OUI_FROM_DATABASE=SHENZHEN DAJIAHAO TECHNOLOGY CO.,LTD + +OUI:D01AA7* + ID_OUI_FROM_DATABASE=UniPrint + +OUI:B08E1A* + ID_OUI_FROM_DATABASE=URadio Systems Co., Ltd + +OUI:40605A* + ID_OUI_FROM_DATABASE=Hawkeye Tech Co. Ltd + +OUI:E05DA6* + ID_OUI_FROM_DATABASE=Detlef Fink Elektronik & Softwareentwicklung + +OUI:0C7523* + ID_OUI_FROM_DATABASE=BEIJING GEHUA CATV NETWORK CO.,LTD + +OUI:BC2C55* + ID_OUI_FROM_DATABASE=Bear Flag Design, Inc. + +OUI:04F4BC* + ID_OUI_FROM_DATABASE=Xena Networks + +OUI:608C2B* + ID_OUI_FROM_DATABASE=Hanson Technology + +OUI:EC1120* + ID_OUI_FROM_DATABASE=FloDesign Wind Turbine Corporation + +OUI:D0F73B* + ID_OUI_FROM_DATABASE=Helmut Mauell GmbH + +OUI:C495A2* + ID_OUI_FROM_DATABASE=SHENZHEN WEIJIU INDUSTRY AND TRADE DEVELOPMENT CO., LTD + +OUI:0C9E91* + ID_OUI_FROM_DATABASE=Sankosha Corporation + +OUI:F48771* + ID_OUI_FROM_DATABASE=Infoblox + +OUI:04F021* + ID_OUI_FROM_DATABASE=Compex Systems Pte Ltd + +OUI:8823FE* + ID_OUI_FROM_DATABASE=TTTech Computertechnik AG + +OUI:98AAD7* + ID_OUI_FROM_DATABASE=BLUE WAVE NETWORKING CO LTD + +OUI:20107A* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:502267* + ID_OUI_FROM_DATABASE=PixeLINK + +OUI:9092B4* + ID_OUI_FROM_DATABASE=Diehl BGT Defence GmbH & Co. KG + +OUI:806007* + ID_OUI_FROM_DATABASE=RIM + +OUI:38A851* + ID_OUI_FROM_DATABASE=Moog, Ing + +OUI:90185E* + ID_OUI_FROM_DATABASE=Apex Tool Group GmbH & Co OHG + +OUI:649EF3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34D09B* + ID_OUI_FROM_DATABASE=MobilMAX Technology Inc. + +OUI:087572* + ID_OUI_FROM_DATABASE=Obelux Oy + +OUI:9C1FDD* + ID_OUI_FROM_DATABASE=Accupix Inc. + +OUI:506441* + ID_OUI_FROM_DATABASE=Greenlee + +OUI:80946C* + ID_OUI_FROM_DATABASE=TOKYO RADAR CORPORATION + +OUI:00FA3B* + ID_OUI_FROM_DATABASE=CLOOS ELECTRONIC GMBH + +OUI:28CD1C* + ID_OUI_FROM_DATABASE=Espotel Oy + +OUI:D824BD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D878E5* + ID_OUI_FROM_DATABASE=KUHN SA + +OUI:C49300* + ID_OUI_FROM_DATABASE=8Devices + +OUI:4C3910* + ID_OUI_FROM_DATABASE=Newtek Electronics co., Ltd. + +OUI:5808FA* + ID_OUI_FROM_DATABASE=Fiber Optic & telecommunication INC. + +OUI:7C94B2* + ID_OUI_FROM_DATABASE=Philips Healthcare PCCI + +OUI:200505* + ID_OUI_FROM_DATABASE=RADMAX COMMUNICATION PRIVATE LIMITED + +OUI:5848C0* + ID_OUI_FROM_DATABASE=COFLEC + +OUI:C8F704* + ID_OUI_FROM_DATABASE=Building Block Video + +OUI:C8AF40* + ID_OUI_FROM_DATABASE=marco Systemanalyse und Entwicklung GmbH + +OUI:AC319D* + ID_OUI_FROM_DATABASE=Shenzhen TG-NET Botone Technology Co.,Ltd. + +OUI:08D09F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B81413* + ID_OUI_FROM_DATABASE=Keen High Holding(HK) Ltd. + +OUI:2037BC* + ID_OUI_FROM_DATABASE=Kuipers Electronic Engineering BV + +OUI:A887ED* + ID_OUI_FROM_DATABASE=ARC Wireless LLC + +OUI:983571* + ID_OUI_FROM_DATABASE=Sub10 Systems Ltd + +OUI:B05CE5* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:CC6BF1* + ID_OUI_FROM_DATABASE=Sound Masking Inc. + +OUI:B82CA0* + ID_OUI_FROM_DATABASE=Honeywell HomMed + +OUI:94AE61* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:7CA61D* + ID_OUI_FROM_DATABASE=MHL, LLC + +OUI:5CCEAD* + ID_OUI_FROM_DATABASE=CDYNE Corporation + +OUI:9CA3BA* + ID_OUI_FROM_DATABASE=SAKURA Internet Inc. + +OUI:709756* + ID_OUI_FROM_DATABASE=Happyelectronics Co.,Ltd + +OUI:D4206D* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:1866E3* + ID_OUI_FROM_DATABASE=Veros Systems, Inc. + +OUI:00B338* + ID_OUI_FROM_DATABASE=Kontron Design Manufacturing Services (M) Sdn. Bhd + +OUI:94DE0E* + ID_OUI_FROM_DATABASE=SmartOptics AS + +OUI:A429B7* + ID_OUI_FROM_DATABASE=bluesky + +OUI:7C6B33* + ID_OUI_FROM_DATABASE=Tenyu Tech Co. Ltd. + +OUI:CCB8F1* + ID_OUI_FROM_DATABASE=EAGLE KINGDOM TECHNOLOGIES LIMITED + +OUI:DC2E6A* + ID_OUI_FROM_DATABASE=HCT. Co., Ltd. + +OUI:34255D* + ID_OUI_FROM_DATABASE=Shenzhen Loadcom Technology Co.,Ltd + +OUI:1897FF* + ID_OUI_FROM_DATABASE=TechFaith Wireless Technology Limited + +OUI:8C8E76* + ID_OUI_FROM_DATABASE=taskit GmbH + +OUI:B4D8DE* + ID_OUI_FROM_DATABASE=iota Computing, Inc. + +OUI:54CDA7* + ID_OUI_FROM_DATABASE=Fujian Shenzhou Electronic Co.,Ltd + +OUI:1000FD* + ID_OUI_FROM_DATABASE=LaonPeople + +OUI:603553* + ID_OUI_FROM_DATABASE=Buwon Technology + +OUI:B89BC9* + ID_OUI_FROM_DATABASE=SMC Networks Inc + +OUI:48022A* + ID_OUI_FROM_DATABASE=B-Link Electronic Limited + +OUI:48A6D2* + ID_OUI_FROM_DATABASE=GJsun Optical Science and Tech Co.,Ltd. + +OUI:186D99* + ID_OUI_FROM_DATABASE=Adanis Inc. + +OUI:D44B5E* + ID_OUI_FROM_DATABASE=TAIYO YUDEN CO., LTD. + +OUI:B40C25* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:40BF17* + ID_OUI_FROM_DATABASE=Digistar Telecom. SA + +OUI:E4AFA1* + ID_OUI_FROM_DATABASE=HES-SO + +OUI:58920D* + ID_OUI_FROM_DATABASE=Kinetic Avionics Limited + +OUI:207600* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:84D32A* + ID_OUI_FROM_DATABASE=IEEE 1905.1 + +OUI:F8E7B5* + ID_OUI_FROM_DATABASE=µTech Tecnologia LTDA + +OUI:0462D7* + ID_OUI_FROM_DATABASE=ALSTOM HYDRO FRANCE + +OUI:CCC8D7* + ID_OUI_FROM_DATABASE=CIAS Elettronica srl + +OUI:64AE0C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A446FA* + ID_OUI_FROM_DATABASE=AmTRAN Video Corporation + +OUI:2804E0* + ID_OUI_FROM_DATABASE=FERMAX ELECTRONICA S.A.U. + +OUI:FC01CD* + ID_OUI_FROM_DATABASE=FUNDACION TEKNIKER + +OUI:88E7A6* + ID_OUI_FROM_DATABASE=iKnowledge Integration Corp. + +OUI:98E79A* + ID_OUI_FROM_DATABASE=Foxconn(NanJing) Communication Co.,Ltd. + +OUI:54F5B6* + ID_OUI_FROM_DATABASE=ORIENTAL PACIFIC INTERNATIONAL LIMITED + +OUI:34A55D* + ID_OUI_FROM_DATABASE=TECHNOSOFT INTERNATIONAL SRL + +OUI:D0C282* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:449CB5* + ID_OUI_FROM_DATABASE=Alcomp, Inc + +OUI:24E6BA* + ID_OUI_FROM_DATABASE=JSC Zavod im. Kozitsky + +OUI:8C8A6E* + ID_OUI_FROM_DATABASE=ESTUN AUTOMATION TECHNOLOY CO., LTD + +OUI:E0ED1A* + ID_OUI_FROM_DATABASE=vastriver Technology Co., Ltd + +OUI:C83B45* + ID_OUI_FROM_DATABASE=JRI-Maxant + +OUI:685E6B* + ID_OUI_FROM_DATABASE=PowerRay Co., Ltd. + +OUI:4C32D9* + ID_OUI_FROM_DATABASE=M Rutty Holdings Pty. Ltd. + +OUI:50A733* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:603FC5* + ID_OUI_FROM_DATABASE=COX CO., LTD + +OUI:182B05* + ID_OUI_FROM_DATABASE=8D Technologies + +OUI:54A9D4* + ID_OUI_FROM_DATABASE=Minibar Systems + +OUI:4861A3* + ID_OUI_FROM_DATABASE=Concern Axion JSC + +OUI:D89685* + ID_OUI_FROM_DATABASE=GoPro + +OUI:08A12B* + ID_OUI_FROM_DATABASE=ShenZhen EZL Technology Co., Ltd + +OUI:94319B* + ID_OUI_FROM_DATABASE=Alphatronics BV + +OUI:08FC52* + ID_OUI_FROM_DATABASE=OpenXS BV + +OUI:205B5E* + ID_OUI_FROM_DATABASE=Shenzhen Wonhe Technology Co., Ltd + +OUI:3CC99E* + ID_OUI_FROM_DATABASE=Huiyang Technology Co., Ltd + +OUI:C8A1BA* + ID_OUI_FROM_DATABASE=Neul Ltd + +OUI:AC02EF* + ID_OUI_FROM_DATABASE=Comsis + +OUI:C43A9F* + ID_OUI_FROM_DATABASE=Siconix Inc. + +OUI:0418B6* + ID_OUI_FROM_DATABASE=Private + +OUI:D4024A* + ID_OUI_FROM_DATABASE=Delphian Systems LLC + +OUI:84248D* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:24EC99* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:B8621F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B45CA4* + ID_OUI_FROM_DATABASE=Thing-talk Wireless Communication Technologies Corporation Limited + +OUI:AC8ACD* + ID_OUI_FROM_DATABASE=ROGER D.Wensker, G.Wensker sp.j. + +OUI:984246* + ID_OUI_FROM_DATABASE=SOL INDUSTRY PTE., LTD + +OUI:28A574* + ID_OUI_FROM_DATABASE=Miller Electric Mfg. Co. + +OUI:3826CD* + ID_OUI_FROM_DATABASE=ANDTEK + +OUI:C436DA* + ID_OUI_FROM_DATABASE=Rusteletech Ltd. + +OUI:00FC70* + ID_OUI_FROM_DATABASE=Intrepid Control Systems, Inc. + +OUI:A4EE57* + ID_OUI_FROM_DATABASE=SEIKO EPSON CORPORATION + +OUI:D0AFB6* + ID_OUI_FROM_DATABASE=Linktop Technology Co., LTD + +OUI:444F5E* + ID_OUI_FROM_DATABASE=Pan Studios Co.,Ltd. + +OUI:0C3956* + ID_OUI_FROM_DATABASE=Observator instruments + +OUI:A49981* + ID_OUI_FROM_DATABASE=FuJian Elite Power Tech CO.,LTD. + +OUI:B83A7B* + ID_OUI_FROM_DATABASE=Worldplay (Canada) Inc. + +OUI:783F15* + ID_OUI_FROM_DATABASE=EasySYNC Ltd. + +OUI:88B168* + ID_OUI_FROM_DATABASE=Delta Control GmbH + +OUI:20B399* + ID_OUI_FROM_DATABASE=Enterasys + +OUI:18B79E* + ID_OUI_FROM_DATABASE=Invoxia + +OUI:147411* + ID_OUI_FROM_DATABASE=RIM + +OUI:5C56ED* + ID_OUI_FROM_DATABASE=3pleplay Electronics Private Limited + +OUI:0838A5* + ID_OUI_FROM_DATABASE=Funkwerk plettac electronic GmbH + +OUI:BCCD45* + ID_OUI_FROM_DATABASE=VOISMART + +OUI:78028F* + ID_OUI_FROM_DATABASE=Adaptive Spectrum and Signal Alignment (ASSIA), Inc. + +OUI:D4A425* + ID_OUI_FROM_DATABASE=SMAX Technology Co., Ltd. + +OUI:98F8DB* + ID_OUI_FROM_DATABASE=Marini Impianti Industriali s.r.l. + +OUI:140708* + ID_OUI_FROM_DATABASE=Private + +OUI:24C9DE* + ID_OUI_FROM_DATABASE=Genoray + +OUI:605464* + ID_OUI_FROM_DATABASE=Eyedro Green Solutions Inc. + +OUI:54055F* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:405539* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B8BEBF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:38FEC5* + ID_OUI_FROM_DATABASE=Ellips B.V. + +OUI:24C86E* + ID_OUI_FROM_DATABASE=Chaney Instrument Co. + +OUI:D4D898* + ID_OUI_FROM_DATABASE=Korea CNO Tech Co., Ltd + +OUI:5070E5* + ID_OUI_FROM_DATABASE=He Shan World Fair Electronics Technology Limited + +OUI:28EE2C* + ID_OUI_FROM_DATABASE=Frontline Test Equipment + +OUI:802275* + ID_OUI_FROM_DATABASE=Beijing Beny Wave Technology Co Ltd + +OUI:BC8199* + ID_OUI_FROM_DATABASE=BASIC Co.,Ltd. + +OUI:000726* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co., Ltd. + +OUI:24470E* + ID_OUI_FROM_DATABASE=PentronicAB + +OUI:A4DB2E* + ID_OUI_FROM_DATABASE=Kingspan Environmental Ltd + +OUI:F44EFD* + ID_OUI_FROM_DATABASE=Actions Semiconductor Co.,Ltd.(Cayman Islands) + +OUI:34BCA6* + ID_OUI_FROM_DATABASE=Beijing Ding Qing Technology, Ltd. + +OUI:D4C1FC* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:48DCFB* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:688470* + ID_OUI_FROM_DATABASE=eSSys Co.,Ltd + +OUI:F08BFE* + ID_OUI_FROM_DATABASE=COSTEL.,CO.LTD + +OUI:5435DF* + ID_OUI_FROM_DATABASE=Symeo GmbH + +OUI:F43D80* + ID_OUI_FROM_DATABASE=FAG Industrial Services GmbH + +OUI:D4F0B4* + ID_OUI_FROM_DATABASE=Napco Security Technologies + +OUI:40B3FC* + ID_OUI_FROM_DATABASE=Logital Co. Limited + +OUI:D05FCE* + ID_OUI_FROM_DATABASE=Hitachi Data Systems + +OUI:8C82A8* + ID_OUI_FROM_DATABASE=Insigma Technology Co.,Ltd + +OUI:3C2763* + ID_OUI_FROM_DATABASE=SLE quality engineering GmbH & Co. KG + +OUI:A44B15* + ID_OUI_FROM_DATABASE=Sun Cupid Technology (HK) LTD + +OUI:508ACB* + ID_OUI_FROM_DATABASE=SHENZHEN MAXMADE TECHNOLOGY CO., LTD. + +OUI:7032D5* + ID_OUI_FROM_DATABASE=Athena Wireless Communications Inc + +OUI:7CF0BA* + ID_OUI_FROM_DATABASE=Linkwell Telesystems Pvt Ltd + +OUI:CCC62B* + ID_OUI_FROM_DATABASE=Tri-Systems Corporation + +OUI:ACF97E* + ID_OUI_FROM_DATABASE=ELESYS INC. + +OUI:4C7367* + ID_OUI_FROM_DATABASE=Genius Bytes Software Solutions GmbH + +OUI:DC2B66* + ID_OUI_FROM_DATABASE=InfoBLOCK S.A. de C.V. + +OUI:14F0C5* + ID_OUI_FROM_DATABASE=Xtremio Ltd. + +OUI:C027B9* + ID_OUI_FROM_DATABASE=Beijing National Railway Research & Design Institute of Signal & Communication Co., Ltd. + +OUI:70A41C* + ID_OUI_FROM_DATABASE=Advanced Wireless Dynamics S.L. + +OUI:285132* + ID_OUI_FROM_DATABASE=Shenzhen Prayfly Technology Co.,Ltd + +OUI:4C3B74* + ID_OUI_FROM_DATABASE=VOGTEC(H.K.) Co., Ltd + +OUI:509772* + ID_OUI_FROM_DATABASE=Westinghouse Digital + +OUI:D85D84* + ID_OUI_FROM_DATABASE=CAx soft GmbH + +OUI:78A683* + ID_OUI_FROM_DATABASE=Precidata + +OUI:BC6784* + ID_OUI_FROM_DATABASE=Environics Oy + +OUI:B4E0CD* + ID_OUI_FROM_DATABASE=Fusion-io, Inc + +OUI:50AF73* + ID_OUI_FROM_DATABASE=Shenzhen Bitland Information Technology Co., Ltd. + +OUI:488E42* + ID_OUI_FROM_DATABASE=DIGALOG GmbH + +OUI:286046* + ID_OUI_FROM_DATABASE=Lantech Communications Global, Inc. + +OUI:A424B3* + ID_OUI_FROM_DATABASE=FlatFrog Laboratories AB + +OUI:A4856B* + ID_OUI_FROM_DATABASE=Q Electronics Ltd + +OUI:84EA99* + ID_OUI_FROM_DATABASE=Vieworks + +OUI:DCCBA8* + ID_OUI_FROM_DATABASE=Explora Technologies Inc + +OUI:58EECE* + ID_OUI_FROM_DATABASE=Icon Time Systems + +OUI:A41BC0* + ID_OUI_FROM_DATABASE=Fastec Imaging Corporation + +OUI:E01F0A* + ID_OUI_FROM_DATABASE=Xslent Energy Technologies. LLC + +OUI:F40321* + ID_OUI_FROM_DATABASE=BeNeXt B.V. + +OUI:00B033* + ID_OUI_FROM_DATABASE=OAO Izhevskiy radiozavod + +OUI:707EDE* + ID_OUI_FROM_DATABASE=NASTEC LTD. + +OUI:CCBE71* + ID_OUI_FROM_DATABASE=OptiLogix BV + +OUI:D8B12A* + ID_OUI_FROM_DATABASE=Panasonic Mobile Communications Co., Ltd. + +OUI:7CDD90* + ID_OUI_FROM_DATABASE=Shenzhen Ogemray Technology Co., Ltd. + +OUI:C07E40* + ID_OUI_FROM_DATABASE=SHENZHEN XDK COMMUNICATION EQUIPMENT CO.,LTD + +OUI:E44F29* + ID_OUI_FROM_DATABASE=MA Lighting Technology GmbH + +OUI:6CAB4D* + ID_OUI_FROM_DATABASE=Digital Payment Technologies + +OUI:60DA23* + ID_OUI_FROM_DATABASE=Estech Co.,Ltd + +OUI:28F358* + ID_OUI_FROM_DATABASE=2C - Trifonov & Co + +OUI:304C7E* + ID_OUI_FROM_DATABASE=Panasonic Electric Works Automation Controls Techno Co.,Ltd. + +OUI:64D1A3* + ID_OUI_FROM_DATABASE=Sitecom Europe BV + +OUI:3831AC* + ID_OUI_FROM_DATABASE=WEG + +OUI:2C7ECF* + ID_OUI_FROM_DATABASE=Onzo Ltd + +OUI:10E3C7* + ID_OUI_FROM_DATABASE=Seohwa Telecom + +OUI:E84040* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0C8112* + ID_OUI_FROM_DATABASE=Private + +OUI:7C7D41* + ID_OUI_FROM_DATABASE=Jinmuyu Electronics Co., Ltd. + +OUI:4C1480* + ID_OUI_FROM_DATABASE=NOREGON SYSTEMS, INC + +OUI:60F673* + ID_OUI_FROM_DATABASE=TERUMO CORPORATION + +OUI:E48AD5* + ID_OUI_FROM_DATABASE=RF WINDOW CO., LTD. + +OUI:24F0FF* + ID_OUI_FROM_DATABASE=GHT Co., Ltd. + +OUI:4C07C9* + ID_OUI_FROM_DATABASE=COMPUTER OFFICE Co.,Ltd. + +OUI:40F4EC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2872F0* + ID_OUI_FROM_DATABASE=ATHENA + +OUI:9C807D* + ID_OUI_FROM_DATABASE=SYSCABLE Korea Inc. + +OUI:180B52* + ID_OUI_FROM_DATABASE=Nanotron Technologies GmbH + +OUI:64DE1C* + ID_OUI_FROM_DATABASE=Kingnetic Pte Ltd + +OUI:540496* + ID_OUI_FROM_DATABASE=Gigawave LTD + +OUI:C8C126* + ID_OUI_FROM_DATABASE=ZPM Industria e Comercio Ltda + +OUI:041D10* + ID_OUI_FROM_DATABASE=Dream Ware Inc. + +OUI:88DD79* + ID_OUI_FROM_DATABASE=Voltaire + +OUI:4468AB* + ID_OUI_FROM_DATABASE=JUIN COMPANY, LIMITED + +OUI:902E87* + ID_OUI_FROM_DATABASE=LabJack + +OUI:C8208E* + ID_OUI_FROM_DATABASE=Storagedata + +OUI:00B342* + ID_OUI_FROM_DATABASE=MacroSAN Technologies Co., Ltd. + +OUI:4CB9C8* + ID_OUI_FROM_DATABASE=CONET CO., LTD. + +OUI:0474A1* + ID_OUI_FROM_DATABASE=Aligera Equipamentos Digitais Ltda + +OUI:1064E2* + ID_OUI_FROM_DATABASE=ADFweb.com s.r.l. + +OUI:CC34D7* + ID_OUI_FROM_DATABASE=GEWISS S.P.A. + +OUI:B4CFDB* + ID_OUI_FROM_DATABASE=Shenzhen Jiuzhou Electric Co.,LTD + +OUI:C46354* + ID_OUI_FROM_DATABASE=U-Raku, Inc. + +OUI:20FEDB* + ID_OUI_FROM_DATABASE=M2M Solution S.A.S. + +OUI:405FBE* + ID_OUI_FROM_DATABASE=RIM + +OUI:E05B70* + ID_OUI_FROM_DATABASE=Innovid, Co., Ltd. + +OUI:043604* + ID_OUI_FROM_DATABASE=Gyeyoung I&T + +OUI:34F968* + ID_OUI_FROM_DATABASE=ATEK Products, LLC + +OUI:D0D0FD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:706417* + ID_OUI_FROM_DATABASE=ORBIS TECNOLOGIA ELECTRICA S.A. + +OUI:64FC8C* + ID_OUI_FROM_DATABASE=Zonar Systems + +OUI:28ED58* + ID_OUI_FROM_DATABASE=JAG Jakob AG + +OUI:9873C4* + ID_OUI_FROM_DATABASE=Sage Electronic Engineering LLC + +OUI:B8797E* + ID_OUI_FROM_DATABASE=Secure Meters (UK) Limited + +OUI:2005E8* + ID_OUI_FROM_DATABASE=OOO InProMedia + +OUI:E0D10A* + ID_OUI_FROM_DATABASE=Katoudenkikougyousyo co ltd + +OUI:1C0656* + ID_OUI_FROM_DATABASE=IDY Corporation + +OUI:C44B44* + ID_OUI_FROM_DATABASE=Omniprint Inc. + +OUI:6015C7* + ID_OUI_FROM_DATABASE=IdaTech + +OUI:188ED5* + ID_OUI_FROM_DATABASE=TP Vision Belgium N.V. - innovation site Brugge + +OUI:8CE7B3* + ID_OUI_FROM_DATABASE=Sonardyne International Ltd + +OUI:0034F1* + ID_OUI_FROM_DATABASE=Radicom Research, Inc. + +OUI:A8B0AE* + ID_OUI_FROM_DATABASE=LEONI + +OUI:60893C* + ID_OUI_FROM_DATABASE=Thermo Fisher Scientific P.O.A. + +OUI:5C17D3* + ID_OUI_FROM_DATABASE=LGE + +OUI:347877* + ID_OUI_FROM_DATABASE=O-NET Communications(Shenzhen) Limited + +OUI:70A191* + ID_OUI_FROM_DATABASE=Trendsetter Medical, LLC + +OUI:A49B13* + ID_OUI_FROM_DATABASE=Burroughs Payment Systems, Inc. + +OUI:58BC27* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34D2C4* + ID_OUI_FROM_DATABASE=RENA GmbH Print Systeme + +OUI:E0A670* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:E061B2* + ID_OUI_FROM_DATABASE=HANGZHOU ZENOINTEL TECHNOLOGY CO., LTD + +OUI:4491DB* + ID_OUI_FROM_DATABASE=Shanghai Huaqin Telecom Technology Co.,Ltd + +OUI:14D76E* + ID_OUI_FROM_DATABASE=CONCH ELECTRONIC Co.,Ltd + +OUI:CC6B98* + ID_OUI_FROM_DATABASE=Minetec Wireless Technologies + +OUI:C4CD45* + ID_OUI_FROM_DATABASE=Beijing Boomsense Technology CO.,LTD. + +OUI:D0BB80* + ID_OUI_FROM_DATABASE=SHL Telemedicine International Ltd. + +OUI:1C83B0* + ID_OUI_FROM_DATABASE=Linked IP GmbH + +OUI:F065DD* + ID_OUI_FROM_DATABASE=Primax Electronics Ltd. + +OUI:706582* + ID_OUI_FROM_DATABASE=Suzhou Hanming Technologies Co., Ltd. + +OUI:94C7AF* + ID_OUI_FROM_DATABASE=Raylios Technology + +OUI:6854F5* + ID_OUI_FROM_DATABASE=enLighted Inc + +OUI:008C10* + ID_OUI_FROM_DATABASE=Black Box Corp. + +OUI:20A2E7* + ID_OUI_FROM_DATABASE=Lee-Dickens Ltd + +OUI:8CDD8D* + ID_OUI_FROM_DATABASE=Wifly-City System Inc. + +OUI:EC98C1* + ID_OUI_FROM_DATABASE=Beijing Risbo Network Technology Co.,Ltd + +OUI:ECC38A* + ID_OUI_FROM_DATABASE=Accuenergy (CANADA) Inc + +OUI:D48FAA* + ID_OUI_FROM_DATABASE=Sogecam Industrial, S.A. + +OUI:38A95F* + ID_OUI_FROM_DATABASE=Actifio Inc + +OUI:A0DDE5* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:94A7BC* + ID_OUI_FROM_DATABASE=BodyMedia, Inc. + +OUI:6C9B02* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:84DB2F* + ID_OUI_FROM_DATABASE=Sierra Wireless Inc + +OUI:A45055* + ID_OUI_FROM_DATABASE=busware.de + +OUI:2CD2E7* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:C89383* + ID_OUI_FROM_DATABASE=Embedded Automation, Inc. + +OUI:D49E6D* + ID_OUI_FROM_DATABASE=Wuhan Zhongyuan Huadian Science & Technology Co., + +OUI:94F720* + ID_OUI_FROM_DATABASE=Tianjin Deviser Electronics Instrument Co., Ltd + +OUI:EC2368* + ID_OUI_FROM_DATABASE=IntelliVoice Co.,Ltd. + +OUI:B45253* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:04DD4C* + ID_OUI_FROM_DATABASE=Velocytech + +OUI:B4C810* + ID_OUI_FROM_DATABASE=UMPI Elettronica + +OUI:38580C* + ID_OUI_FROM_DATABASE=Panaccess Systems GmbH + +OUI:24AF54* + ID_OUI_FROM_DATABASE=NEXGEN Mediatech Inc. + +OUI:F0F9F7* + ID_OUI_FROM_DATABASE=IES GmbH & Co. KG + +OUI:CC0CDA* + ID_OUI_FROM_DATABASE=Miljovakt AS + +OUI:C01242* + ID_OUI_FROM_DATABASE=Alpha Security Products + +OUI:90507B* + ID_OUI_FROM_DATABASE=Advanced PANMOBIL Systems GmbH & Co. KG + +OUI:00B5D6* + ID_OUI_FROM_DATABASE=Omnibit Inc. + +OUI:F893F3* + ID_OUI_FROM_DATABASE=VOLANS + +OUI:7C3E9D* + ID_OUI_FROM_DATABASE=PATECH + +OUI:4C60D5* + ID_OUI_FROM_DATABASE=airPointe of New Hampshire + +OUI:D45297* + ID_OUI_FROM_DATABASE=nSTREAMS Technologies, Inc. + +OUI:78EC22* + ID_OUI_FROM_DATABASE=Shanghai Qihui Telecom Technology Co., LTD + +OUI:F8D756* + ID_OUI_FROM_DATABASE=Simm Tronic Limited + +OUI:E087B1* + ID_OUI_FROM_DATABASE=Nata-Info Ltd. + +OUI:A8B1D4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4CBAA3* + ID_OUI_FROM_DATABASE=Bison Electronics Inc. + +OUI:EC7C74* + ID_OUI_FROM_DATABASE=Justone Technologies Co., Ltd. + +OUI:3C1A79* + ID_OUI_FROM_DATABASE=Huayuan Technology CO.,LTD + +OUI:30E48E* + ID_OUI_FROM_DATABASE=Vodafone UK + +OUI:08512E* + ID_OUI_FROM_DATABASE=Orion Diagnostica Oy + +OUI:9CF61A* + ID_OUI_FROM_DATABASE=UTC Fire and Security + +OUI:C802A6* + ID_OUI_FROM_DATABASE=Beijing Newmine Technology + +OUI:C84C75* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:284C53* + ID_OUI_FROM_DATABASE=Intune Networks + +OUI:102D96* + ID_OUI_FROM_DATABASE=Looxcie Inc. + +OUI:3037A6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACEA6A* + ID_OUI_FROM_DATABASE=GENIX INFOCOMM CO., LTD. + +OUI:5C35DA* + ID_OUI_FROM_DATABASE=There Corporation Oy + +OUI:005218* + ID_OUI_FROM_DATABASE=Wuxi Keboda Electron Co.Ltd + +OUI:08F2F4* + ID_OUI_FROM_DATABASE=Net One Partners Co.,Ltd. + +OUI:68EFBD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:183BD2* + ID_OUI_FROM_DATABASE=BYD Precision Manufacture Company Ltd. + +OUI:F45595* + ID_OUI_FROM_DATABASE=HENGBAO Corporation LTD. + +OUI:C08B6F* + ID_OUI_FROM_DATABASE=S I Sistemas Inteligentes Eletrônicos Ltda + +OUI:BCA9D6* + ID_OUI_FROM_DATABASE=Cyber-Rain, Inc. + +OUI:0CDDEF* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:80C63F* + ID_OUI_FROM_DATABASE=Remec Broadband Wireless , LLC + +OUI:F09CBB* + ID_OUI_FROM_DATABASE=RaonThink Inc. + +OUI:FCE23F* + ID_OUI_FROM_DATABASE=CLAY PAKY SPA + +OUI:B0E39D* + ID_OUI_FROM_DATABASE=CAT SYSTEM CO.,LTD. + +OUI:78A6BD* + ID_OUI_FROM_DATABASE=DAEYEON Control&Instrument Co,.Ltd + +OUI:481249* + ID_OUI_FROM_DATABASE=Luxcom Technologies Inc. + +OUI:B43DB2* + ID_OUI_FROM_DATABASE=Degreane Horizon + +OUI:C4823F* + ID_OUI_FROM_DATABASE=Fujian Newland Auto-ID Tech. Co,.Ltd. + +OUI:F4C795* + ID_OUI_FROM_DATABASE=WEY Elektronik AG + +OUI:087695* + ID_OUI_FROM_DATABASE=Auto Industrial Co., Ltd. + +OUI:ACCE8F* + ID_OUI_FROM_DATABASE=HWA YAO TECHNOLOGIES CO., LTD + +OUI:042F56* + ID_OUI_FROM_DATABASE=ATOCS (Shenzhen) LTD + +OUI:084E1C* + ID_OUI_FROM_DATABASE=H2A Systems, LLC + +OUI:A4B121* + ID_OUI_FROM_DATABASE=Arantia 2010 S.L. + +OUI:9889ED* + ID_OUI_FROM_DATABASE=Anadem Information Inc. + +OUI:147373* + ID_OUI_FROM_DATABASE=TUBITAK UEKAE + +OUI:982D56* + ID_OUI_FROM_DATABASE=Resolution Audio + +OUI:00A2DA* + ID_OUI_FROM_DATABASE=INAT GmbH + +OUI:6C3E9C* + ID_OUI_FROM_DATABASE=KE Knestel Elektronik GmbH + +OUI:F89D0D* + ID_OUI_FROM_DATABASE=Control Technology Inc. + +OUI:1010B6* + ID_OUI_FROM_DATABASE=McCain Inc + +OUI:081FF3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5CE286* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:2CCD27* + ID_OUI_FROM_DATABASE=Precor Inc + +OUI:6C5E7A* + ID_OUI_FROM_DATABASE=Ubiquitous Internet Telecom Co., Ltd + +OUI:D828C9* + ID_OUI_FROM_DATABASE=General Electric Consumer and Industrial + +OUI:C86C1E* + ID_OUI_FROM_DATABASE=Display Systems Ltd + +OUI:EC6C9F* + ID_OUI_FROM_DATABASE=Chengdu Volans Technology CO.,LTD + +OUI:CCCC4E* + ID_OUI_FROM_DATABASE=Sun Fountainhead USA. Corp + +OUI:60D30A* + ID_OUI_FROM_DATABASE=Quatius Limited + +OUI:BC9DA5* + ID_OUI_FROM_DATABASE=DASCOM Europe GmbH + +OUI:942E63* + ID_OUI_FROM_DATABASE=Finsécur + +OUI:C8D2C1* + ID_OUI_FROM_DATABASE=Jetlun (Shenzhen) Corporation + +OUI:F0BCC8* + ID_OUI_FROM_DATABASE=MaxID (Pty) Ltd + +OUI:406186* + ID_OUI_FROM_DATABASE=MICRO-STAR INT'L CO.,LTD + +OUI:74E537* + ID_OUI_FROM_DATABASE=RADSPIN + +OUI:7C08D9* + ID_OUI_FROM_DATABASE=Shanghai B-Star Technology Co + +OUI:448E81* + ID_OUI_FROM_DATABASE=VIG + +OUI:2046F9* + ID_OUI_FROM_DATABASE=Advanced Network Devices (dba:AND) + +OUI:681FD8* + ID_OUI_FROM_DATABASE=Advanced Telemetry + +OUI:0C8230* + ID_OUI_FROM_DATABASE=SHENZHEN MAGNUS TECHNOLOGIES CO.,LTD + +OUI:50934F* + ID_OUI_FROM_DATABASE=Gradual Tecnologia Ltda. + +OUI:34EF8B* + ID_OUI_FROM_DATABASE=NTT Communications Corporation + +OUI:38E98C* + ID_OUI_FROM_DATABASE=Reco S.p.A. + +OUI:F02408* + ID_OUI_FROM_DATABASE=Talaris (Sweden) AB + +OUI:A06986* + ID_OUI_FROM_DATABASE=Wellav Technologies Ltd + +OUI:F02FD8* + ID_OUI_FROM_DATABASE=Bi2-Vision + +OUI:C86CB6* + ID_OUI_FROM_DATABASE=Optcom Co., Ltd. + +OUI:C45976* + ID_OUI_FROM_DATABASE=Fugoo Coorporation + +OUI:B0C8AD* + ID_OUI_FROM_DATABASE=People Power Company + +OUI:A870A5* + ID_OUI_FROM_DATABASE=UniComm Inc. + +OUI:80177D* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:E8DAAA* + ID_OUI_FROM_DATABASE=VideoHome Technology Corp. + +OUI:647D81* + ID_OUI_FROM_DATABASE=YOKOTA INDUSTRIAL CO,.LTD + +OUI:8891DD* + ID_OUI_FROM_DATABASE=Racktivity + +OUI:C4198B* + ID_OUI_FROM_DATABASE=Dominion Voting Systems Corporation + +OUI:C83A35* + ID_OUI_FROM_DATABASE=Tenda Technology Co., Ltd. + +OUI:F4ACC1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:584CEE* + ID_OUI_FROM_DATABASE=Digital One Technologies, Limited + +OUI:E064BB* + ID_OUI_FROM_DATABASE=DigiView S.r.l. + +OUI:4C63EB* + ID_OUI_FROM_DATABASE=Application Solutions (Electronics and Vision) Ltd + +OUI:C01E9B* + ID_OUI_FROM_DATABASE=Pixavi AS + +OUI:64168D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:24D2CC* + ID_OUI_FROM_DATABASE=SmartDrive Systems Inc. + +OUI:7C6C8F* + ID_OUI_FROM_DATABASE=AMS NEVE LTD + +OUI:C4E17C* + ID_OUI_FROM_DATABASE=U2S co. + +OUI:A8C222* + ID_OUI_FROM_DATABASE=TM-Research Inc. + +OUI:50252B* + ID_OUI_FROM_DATABASE=Nethra Imaging Incorporated + +OUI:A4DA3F* + ID_OUI_FROM_DATABASE=Bionics Corp. + +OUI:9C4E8E* + ID_OUI_FROM_DATABASE=ALT Systems Ltd + +OUI:448312* + ID_OUI_FROM_DATABASE=Star-Net + +OUI:687924* + ID_OUI_FROM_DATABASE=ELS-GmbH & Co. KG + +OUI:38BB23* + ID_OUI_FROM_DATABASE=OzVision America LLC + +OUI:003A99* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:04C05B* + ID_OUI_FROM_DATABASE=Tigo Energy + +OUI:5C1437* + ID_OUI_FROM_DATABASE=Thyssenkrupp Aufzugswerke GmbH + +OUI:9C55B4* + ID_OUI_FROM_DATABASE=I.S.E. S.r.l. + +OUI:DC2C26* + ID_OUI_FROM_DATABASE=Iton Technology Limited + +OUI:4CC452* + ID_OUI_FROM_DATABASE=Shang Hai Tyd. Electon Technology Ltd. + +OUI:F0C24C* + ID_OUI_FROM_DATABASE=Zhejiang FeiYue Digital Technology Co., Ltd + +OUI:08184C* + ID_OUI_FROM_DATABASE=A. S. Thomas, Inc. + +OUI:5CE223* + ID_OUI_FROM_DATABASE=Delphin Technology AG + +OUI:FC6198* + ID_OUI_FROM_DATABASE=NEC Personal Products, Ltd + +OUI:F871FE* + ID_OUI_FROM_DATABASE=The Goldman Sachs Group, Inc. + +OUI:D8C3FB* + ID_OUI_FROM_DATABASE=DETRACOM + +OUI:201257* + ID_OUI_FROM_DATABASE=Most Lucky Trading Ltd + +OUI:D49C28* + ID_OUI_FROM_DATABASE=JayBird LLC + +OUI:A03A75* + ID_OUI_FROM_DATABASE=PSS Belgium N.V. + +OUI:746B82* + ID_OUI_FROM_DATABASE=MOVEK + +OUI:0C8411* + ID_OUI_FROM_DATABASE=A.O. Smith Water Products + +OUI:F8E968* + ID_OUI_FROM_DATABASE=Egker Kft. + +OUI:E8DFF2* + ID_OUI_FROM_DATABASE=PRF Co., Ltd. + +OUI:006440* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D0E40B* + ID_OUI_FROM_DATABASE=Wearable Inc. + +OUI:AC867E* + ID_OUI_FROM_DATABASE=Create New Technology (HK) Limited Company + +OUI:58F67B* + ID_OUI_FROM_DATABASE=Xia Men UnionCore Technology LTD. + +OUI:A02EF3* + ID_OUI_FROM_DATABASE=United Integrated Services Co., Led. + +OUI:A8CE90* + ID_OUI_FROM_DATABASE=CVC + +OUI:00271F* + ID_OUI_FROM_DATABASE=MIPRO Electronics Co., Ltd + +OUI:00271A* + ID_OUI_FROM_DATABASE=Geenovo Technology Ltd. + +OUI:002714* + ID_OUI_FROM_DATABASE=Grainmustards, Co,ltd. + +OUI:002717* + ID_OUI_FROM_DATABASE=CE Digital(Zhenjiang)Co.,Ltd + +OUI:002708* + ID_OUI_FROM_DATABASE=Nordiag ASA + +OUI:002701* + ID_OUI_FROM_DATABASE=INCOstartec GmbH + +OUI:002702* + ID_OUI_FROM_DATABASE=SolarEdge Technologies + +OUI:0026FB* + ID_OUI_FROM_DATABASE=AirDio Wireless, Inc. + +OUI:0026F5* + ID_OUI_FROM_DATABASE=XRPLUS Inc. + +OUI:002632* + ID_OUI_FROM_DATABASE=Instrumentation Technologies d.d. + +OUI:00262C* + ID_OUI_FROM_DATABASE=IKT Advanced Technologies s.r.o. + +OUI:002626* + ID_OUI_FROM_DATABASE=Geophysical Survey Systems, Inc. + +OUI:00261F* + ID_OUI_FROM_DATABASE=SAE Magnetics (H.K.) Ltd. + +OUI:002620* + ID_OUI_FROM_DATABASE=ISGUS GmbH + +OUI:00261A* + ID_OUI_FROM_DATABASE=Femtocomm System Technology Corp. + +OUI:002613* + ID_OUI_FROM_DATABASE=Engel Axil S.L. + +OUI:00260D* + ID_OUI_FROM_DATABASE=Mercury Systems, Inc. + +OUI:0025D8* + ID_OUI_FROM_DATABASE=KOREA MAINTENANCE + +OUI:0025CC* + ID_OUI_FROM_DATABASE=Mobile Communications Korea Incorporated + +OUI:0025C5* + ID_OUI_FROM_DATABASE=Star Link Communication Pvt. Ltd. + +OUI:0025C6* + ID_OUI_FROM_DATABASE=kasercorp, ltd + +OUI:0025C0* + ID_OUI_FROM_DATABASE=ZillionTV Corporation + +OUI:0025B4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0025B9* + ID_OUI_FROM_DATABASE=Cypress Solutions Inc + +OUI:0025AD* + ID_OUI_FROM_DATABASE=Manufacturing Resources International + +OUI:002600* + ID_OUI_FROM_DATABASE=TEAC Australia Pty Ltd. + +OUI:002607* + ID_OUI_FROM_DATABASE=Enabling Technology Pty Ltd + +OUI:0025FB* + ID_OUI_FROM_DATABASE=Tunstall Healthcare A/S + +OUI:0025FA* + ID_OUI_FROM_DATABASE=J&M Analytik AG + +OUI:0025F6* + ID_OUI_FROM_DATABASE=netTALK.com, Inc. + +OUI:0025EF* + ID_OUI_FROM_DATABASE=I-TEC Co., Ltd. + +OUI:0025E9* + ID_OUI_FROM_DATABASE=i-mate Development, Inc. + +OUI:0025DF* + ID_OUI_FROM_DATABASE=Private + +OUI:002690* + ID_OUI_FROM_DATABASE=I DO IT + +OUI:00268A* + ID_OUI_FROM_DATABASE=Terrier SC Ltd + +OUI:002689* + ID_OUI_FROM_DATABASE=General Dynamics Robotic Systems + +OUI:002684* + ID_OUI_FROM_DATABASE=KISAN SYSTEM + +OUI:002683* + ID_OUI_FROM_DATABASE=Ajoho Enterprise Co., Ltd. + +OUI:00267D* + ID_OUI_FROM_DATABASE=A-Max Technology Macao Commercial Offshore Company Limited + +OUI:002677* + ID_OUI_FROM_DATABASE=DEIF A/S + +OUI:002671* + ID_OUI_FROM_DATABASE=AUTOVISION Co., Ltd + +OUI:00266A* + ID_OUI_FROM_DATABASE=ESSENSIUM NV + +OUI:0026EF* + ID_OUI_FROM_DATABASE=Technology Advancement Group, Inc. + +OUI:0026E9* + ID_OUI_FROM_DATABASE=SP Corp + +OUI:0026DC* + ID_OUI_FROM_DATABASE=Optical Systems Design + +OUI:0026D6* + ID_OUI_FROM_DATABASE=Ningbo Andy Optoelectronic Co., Ltd. + +OUI:0026CF* + ID_OUI_FROM_DATABASE=DEKA R&D + +OUI:0026D0* + ID_OUI_FROM_DATABASE=Semihalf + +OUI:0026CA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0026C9* + ID_OUI_FROM_DATABASE=Proventix Systems, Inc. + +OUI:0026C3* + ID_OUI_FROM_DATABASE=Insightek Corp. + +OUI:002664* + ID_OUI_FROM_DATABASE=Core System Japan + +OUI:002658* + ID_OUI_FROM_DATABASE=T-Platforms (Cyprus) Limited + +OUI:002645* + ID_OUI_FROM_DATABASE=Circontrol S.A. + +OUI:00263F* + ID_OUI_FROM_DATABASE=LIOS Technology GmbH + +OUI:002639* + ID_OUI_FROM_DATABASE=T.M. Electronics, Inc. + +OUI:0026BD* + ID_OUI_FROM_DATABASE=JTEC Card & Communication Co., Ltd. + +OUI:0026B3* + ID_OUI_FROM_DATABASE=Thales Communications Inc + +OUI:0026AD* + ID_OUI_FROM_DATABASE=Arada Systems, Inc. + +OUI:0026A9* + ID_OUI_FROM_DATABASE=Strong Technologies Pty Ltd + +OUI:0026A3* + ID_OUI_FROM_DATABASE=FQ Ingenieria Electronica S.A. + +OUI:00269C* + ID_OUI_FROM_DATABASE=ITUS JAPAN CO. LTD + +OUI:002696* + ID_OUI_FROM_DATABASE=NOOLIX Co., Ltd + +OUI:002484* + ID_OUI_FROM_DATABASE=Bang and Olufsen Medicom a/s + +OUI:002486* + ID_OUI_FROM_DATABASE=DesignArt Networks + +OUI:00247F* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002478* + ID_OUI_FROM_DATABASE=Mag Tech Electronics Co Limited + +OUI:002471* + ID_OUI_FROM_DATABASE=Fusion MultiSystems dba Fusion-io + +OUI:002473* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD + +OUI:002460* + ID_OUI_FROM_DATABASE=Giaval Science Development Co. Ltd. + +OUI:00245B* + ID_OUI_FROM_DATABASE=RAIDON TECHNOLOGY, INC. + +OUI:00244E* + ID_OUI_FROM_DATABASE=RadChips, Inc. + +OUI:002447* + ID_OUI_FROM_DATABASE=Kaztek Systems + +OUI:002442* + ID_OUI_FROM_DATABASE=Axona Limited + +OUI:00243D* + ID_OUI_FROM_DATABASE=Emerson Appliance Motors and Controls + +OUI:002528* + ID_OUI_FROM_DATABASE=Daido Signal Co., Ltd. + +OUI:002523* + ID_OUI_FROM_DATABASE=OCP Inc. + +OUI:00251E* + ID_OUI_FROM_DATABASE=ROTEL TECHNOLOGIES + +OUI:002519* + ID_OUI_FROM_DATABASE=Viaas Inc + +OUI:002514* + ID_OUI_FROM_DATABASE=PC Worth Int'l Co., Ltd. + +OUI:00250D* + ID_OUI_FROM_DATABASE=GZT Telkom-Telmor sp. z o.o. + +OUI:002506* + ID_OUI_FROM_DATABASE=A.I. ANTITACCHEGGIO ITALIA SRL + +OUI:002508* + ID_OUI_FROM_DATABASE=Maquet Cardiopulmonary AG + +OUI:00257A* + ID_OUI_FROM_DATABASE=CAMCO Produktions- und Vertriebs-GmbH für Beschallungs- und Beleuchtungsanlagen + +OUI:00257F* + ID_OUI_FROM_DATABASE=CallTechSolution Co.,Ltd + +OUI:002573* + ID_OUI_FROM_DATABASE=ST Electronics (Info-Security) Pte Ltd + +OUI:00256E* + ID_OUI_FROM_DATABASE=Van Breda B.V. + +OUI:00256D* + ID_OUI_FROM_DATABASE=Broadband Forum + +OUI:002560* + ID_OUI_FROM_DATABASE=Ibridge Networks & Communications Ltd. + +OUI:00255B* + ID_OUI_FROM_DATABASE=CoachComm, LLC + +OUI:0024E2* + ID_OUI_FROM_DATABASE=HASEGAWA ELECTRIC CO.,LTD. + +OUI:0024DB* + ID_OUI_FROM_DATABASE=Alcohol Monitoring Systems + +OUI:0024CF* + ID_OUI_FROM_DATABASE=Inscape Data Corporation + +OUI:0024C8* + ID_OUI_FROM_DATABASE=Broadband Solutions Group + +OUI:0024C3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0024C0* + ID_OUI_FROM_DATABASE=NTI COMODO INC + +OUI:0024B6* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:0024BB* + ID_OUI_FROM_DATABASE=CENTRAL Corporation + +OUI:0024B1* + ID_OUI_FROM_DATABASE=Coulomb Technologies + +OUI:0024AA* + ID_OUI_FROM_DATABASE=Dycor Technologies Ltd. + +OUI:0024A3* + ID_OUI_FROM_DATABASE=Sonim Technologies Inc + +OUI:00249E* + ID_OUI_FROM_DATABASE=ADC-Elektronik GmbH + +OUI:00248B* + ID_OUI_FROM_DATABASE=HYBUS CO., LTD. + +OUI:002492* + ID_OUI_FROM_DATABASE=Motorola, Broadband Solutions Group + +OUI:002497* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002554* + ID_OUI_FROM_DATABASE=Pixel8 Networks + +OUI:00254D* + ID_OUI_FROM_DATABASE=Singapore Technologies Electronics Limited + +OUI:00254E* + ID_OUI_FROM_DATABASE=Vertex Wireless Co., Ltd. + +OUI:002537* + ID_OUI_FROM_DATABASE=Runcom Technologies Ltd. + +OUI:00253E* + ID_OUI_FROM_DATABASE=Sensus Metering Systems + +OUI:002541* + ID_OUI_FROM_DATABASE=Maquet Critical Care AB + +OUI:00252D* + ID_OUI_FROM_DATABASE=Kiryung Electronics + +OUI:0025A6* + ID_OUI_FROM_DATABASE=Central Network Solution Co., Ltd. + +OUI:0025A1* + ID_OUI_FROM_DATABASE=Enalasys + +OUI:00259A* + ID_OUI_FROM_DATABASE=CEStronics GmbH + +OUI:002593* + ID_OUI_FROM_DATABASE=DatNet Informatikai Kft. + +OUI:002594* + ID_OUI_FROM_DATABASE=Eurodesign BG LTD + +OUI:00258E* + ID_OUI_FROM_DATABASE=The Weather Channel + +OUI:00258A* + ID_OUI_FROM_DATABASE=Pole/Zero Corporation + +OUI:002589* + ID_OUI_FROM_DATABASE=Hills Industries Limited + +OUI:002584* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002501* + ID_OUI_FROM_DATABASE=JSC Supertel + +OUI:0024FA* + ID_OUI_FROM_DATABASE=Hilger u. Kern GMBH + +OUI:0024F5* + ID_OUI_FROM_DATABASE=NDS Surgical Imaging + +OUI:0024EE* + ID_OUI_FROM_DATABASE=Wynmax Inc. + +OUI:0024E7* + ID_OUI_FROM_DATABASE=Plaster Networks + +OUI:0023F2* + ID_OUI_FROM_DATABASE=TVLogic + +OUI:0023E8* + ID_OUI_FROM_DATABASE=Demco Corp. + +OUI:0023E1* + ID_OUI_FROM_DATABASE=Cavena Image Products AB + +OUI:0023DC* + ID_OUI_FROM_DATABASE=Benein, Inc + +OUI:0023DB* + ID_OUI_FROM_DATABASE=saxnet gmbh + +OUI:0023C9* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Information Science & Technology Stock CO.,LTD + +OUI:0023CE* + ID_OUI_FROM_DATABASE=KITA DENSHI CORPORATION + +OUI:0023D5* + ID_OUI_FROM_DATABASE=WAREMA electronic GmbH + +OUI:002421* + ID_OUI_FROM_DATABASE=MICRO-STAR INT'L CO., LTD. + +OUI:002414* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002415* + ID_OUI_FROM_DATABASE=Magnetic Autocontrol GmbH + +OUI:00240F* + ID_OUI_FROM_DATABASE=Ishii Tool & Engineering Corporation + +OUI:002408* + ID_OUI_FROM_DATABASE=Pacific Biosciences + +OUI:002402* + ID_OUI_FROM_DATABASE=Op-Tection GmbH + +OUI:0023FC* + ID_OUI_FROM_DATABASE=Ultra Stereo Labs, Inc + +OUI:0023CF* + ID_OUI_FROM_DATABASE=CUMMINS-ALLISON CORP. + +OUI:0023C2* + ID_OUI_FROM_DATABASE=SAMSUNG Electronics. Co. LTD + +OUI:0023B6* + ID_OUI_FROM_DATABASE=SECURITE COMMUNICATIONS / HONEYWELL + +OUI:0023BC* + ID_OUI_FROM_DATABASE=EQ-SYS GmbH + +OUI:0023AA* + ID_OUI_FROM_DATABASE=HFR, Inc. + +OUI:0023A9* + ID_OUI_FROM_DATABASE=Beijing Detianquan Electromechanical Equipment Co., Ltd + +OUI:00233C* + ID_OUI_FROM_DATABASE=Alflex + +OUI:00233B* + ID_OUI_FROM_DATABASE=C-Matic Systems Ltd + +OUI:002335* + ID_OUI_FROM_DATABASE=Linkflex Co.,Ltd + +OUI:00232D* + ID_OUI_FROM_DATABASE=SandForce + +OUI:002328* + ID_OUI_FROM_DATABASE=ALCON TELECOMMUNICATIONS CO., LTD. + +OUI:002321* + ID_OUI_FROM_DATABASE=Avitech International Corp + +OUI:0022F8* + ID_OUI_FROM_DATABASE=PIMA Electronic Systems Ltd. + +OUI:00231F* + ID_OUI_FROM_DATABASE=Guangda Electronic & Telecommunication Technology Development Co., Ltd. + +OUI:0022E6* + ID_OUI_FROM_DATABASE=Intelligent Data + +OUI:0022E0* + ID_OUI_FROM_DATABASE=Atlantic Software Technologies S.r.L. + +OUI:0022DF* + ID_OUI_FROM_DATABASE=TAMUZ Monitors + +OUI:0022DA* + ID_OUI_FROM_DATABASE=ANATEK, LLC + +OUI:0022D3* + ID_OUI_FROM_DATABASE=Hub-Tech + +OUI:0022CD* + ID_OUI_FROM_DATABASE=Ared Technology Co., Ltd. + +OUI:0022C4* + ID_OUI_FROM_DATABASE=epro GmbH + +OUI:0022C9* + ID_OUI_FROM_DATABASE=Lenord, Bauer & Co GmbH + +OUI:0022BF* + ID_OUI_FROM_DATABASE=SieAmp Group of Companies + +OUI:0022B9* + ID_OUI_FROM_DATABASE=Analogix Seminconductor, Inc + +OUI:0022BA* + ID_OUI_FROM_DATABASE=HUTH Elektronik Systeme GmbH + +OUI:00239D* + ID_OUI_FROM_DATABASE=Mapower Electronics Co., Ltd + +OUI:002392* + ID_OUI_FROM_DATABASE=Proteus Industries Inc. + +OUI:00238D* + ID_OUI_FROM_DATABASE=Techno Design Co., Ltd. + +OUI:002388* + ID_OUI_FROM_DATABASE=V.T. Telematica S.p.a. + +OUI:002383* + ID_OUI_FROM_DATABASE=InMage Systems Inc + +OUI:00237C* + ID_OUI_FROM_DATABASE=NEOTION + +OUI:002324* + ID_OUI_FROM_DATABASE=G-PRO COMPUTER + +OUI:002431* + ID_OUI_FROM_DATABASE=Uni-v co.,ltd + +OUI:00241B* + ID_OUI_FROM_DATABASE=iWOW Communications Pte Ltd + +OUI:002422* + ID_OUI_FROM_DATABASE=Knapp Logistik Automation GmbH + +OUI:002427* + ID_OUI_FROM_DATABASE=SSI COMPUTER CORP + +OUI:002373* + ID_OUI_FROM_DATABASE=GridIron Systems, Inc. + +OUI:002367* + ID_OUI_FROM_DATABASE=UniControls a.s. + +OUI:00236E* + ID_OUI_FROM_DATABASE=Burster GmbH & Co KG + +OUI:00236D* + ID_OUI_FROM_DATABASE=ResMed Ltd + +OUI:002360* + ID_OUI_FROM_DATABASE=Lookit Technology Co., Ltd + +OUI:00235B* + ID_OUI_FROM_DATABASE=Gulfstream + +OUI:002316* + ID_OUI_FROM_DATABASE=KISAN ELECTRONICS CO + +OUI:00230F* + ID_OUI_FROM_DATABASE=Hirsch Electronics Corporation + +OUI:00230A* + ID_OUI_FROM_DATABASE=ARBURG GmbH & Co KG + +OUI:002309* + ID_OUI_FROM_DATABASE=Janam Technologies LLC + +OUI:002303* + ID_OUI_FROM_DATABASE=LITE-ON IT Corporation + +OUI:0022F2* + ID_OUI_FROM_DATABASE=SunPower Corp + +OUI:0022ED* + ID_OUI_FROM_DATABASE=TSI Power Corporation + +OUI:00228D* + ID_OUI_FROM_DATABASE=GBS Laboratories LLC + +OUI:002287* + ID_OUI_FROM_DATABASE=Titan Wireless LLC + +OUI:002288* + ID_OUI_FROM_DATABASE=Sagrad, Inc. + +OUI:002281* + ID_OUI_FROM_DATABASE=Daintree Networks Pty + +OUI:00227A* + ID_OUI_FROM_DATABASE=Telecom Design + +OUI:00226B* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:00225D* + ID_OUI_FROM_DATABASE=Digicable Network India Pvt. Ltd. + +OUI:00225C* + ID_OUI_FROM_DATABASE=Multimedia & Communication Technology + +OUI:00216F* + ID_OUI_FROM_DATABASE=SymCom, Inc. + +OUI:002169* + ID_OUI_FROM_DATABASE=Prologix, LLC. + +OUI:002156* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002150* + ID_OUI_FROM_DATABASE=EYEVIEW ELECTRONICS + +OUI:00214A* + ID_OUI_FROM_DATABASE=Pixel Velocity, Inc + +OUI:0021A3* + ID_OUI_FROM_DATABASE=Micromint + +OUI:002199* + ID_OUI_FROM_DATABASE=Vacon Plc + +OUI:002195* + ID_OUI_FROM_DATABASE=GWD Media Limited + +OUI:002194* + ID_OUI_FROM_DATABASE=Ping Communication + +OUI:00218F* + ID_OUI_FROM_DATABASE=Avantgarde Acoustic Lautsprechersysteme GmbH + +OUI:002188* + ID_OUI_FROM_DATABASE=EMC Corporation + +OUI:002182* + ID_OUI_FROM_DATABASE=SandLinks Systems, Ltd. + +OUI:002175* + ID_OUI_FROM_DATABASE=Pacific Satellite International Ltd. + +OUI:00222A* + ID_OUI_FROM_DATABASE=SoundEar A/S + +OUI:00221E* + ID_OUI_FROM_DATABASE=Media Devices Co., Ltd. + +OUI:002225* + ID_OUI_FROM_DATABASE=Thales Avionics Ltd + +OUI:002218* + ID_OUI_FROM_DATABASE=Verivue Inc. + +OUI:002212* + ID_OUI_FROM_DATABASE=CAI Networks, Inc. + +OUI:00220B* + ID_OUI_FROM_DATABASE=National Source Coding Center + +OUI:002205* + ID_OUI_FROM_DATABASE=WeLink Solutions, Inc. + +OUI:002206* + ID_OUI_FROM_DATABASE=Cyberdyne Inc. + +OUI:0022B3* + ID_OUI_FROM_DATABASE=Sei S.p.A. + +OUI:0022AC* + ID_OUI_FROM_DATABASE=Hangzhou Siyuan Tech. Co., Ltd + +OUI:0022A7* + ID_OUI_FROM_DATABASE=Tyco Electronics AMP GmbH + +OUI:0022A0* + ID_OUI_FROM_DATABASE=Delphi Corporation + +OUI:00229A* + ID_OUI_FROM_DATABASE=Lastar, Inc. + +OUI:002299* + ID_OUI_FROM_DATABASE=SeaMicro Inc. + +OUI:002294* + ID_OUI_FROM_DATABASE=Kyocera Corporation + +OUI:0021FA* + ID_OUI_FROM_DATABASE=A4SP Technologies Ltd. + +OUI:0021F4* + ID_OUI_FROM_DATABASE=INRange Systems, Inc + +OUI:0021ED* + ID_OUI_FROM_DATABASE=Telegesis + +OUI:0021E7* + ID_OUI_FROM_DATABASE=Informatics Services Corporation + +OUI:0021DB* + ID_OUI_FROM_DATABASE=Santachi Video Technology (Shenzhen) Co., Ltd. + +OUI:0021E1* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0021D5* + ID_OUI_FROM_DATABASE=X2E GmbH + +OUI:0021DA* + ID_OUI_FROM_DATABASE=Automation Products Group Inc. + +OUI:0021CE* + ID_OUI_FROM_DATABASE=NTC-Metrotek + +OUI:0021C8* + ID_OUI_FROM_DATABASE=LOHUIS Networks + +OUI:0021C2* + ID_OUI_FROM_DATABASE=GL Communications Inc + +OUI:0021BB* + ID_OUI_FROM_DATABASE=Riken Keiki Co., Ltd. + +OUI:0021B5* + ID_OUI_FROM_DATABASE=Galvanic Ltd + +OUI:0021AF* + ID_OUI_FROM_DATABASE=Radio Frequency Systems + +OUI:0021B6* + ID_OUI_FROM_DATABASE=Triacta Power Technologies Inc. + +OUI:0021A9* + ID_OUI_FROM_DATABASE=Mobilink Telecom Co.,Ltd + +OUI:0021A8* + ID_OUI_FROM_DATABASE=Telephonics Corporation + +OUI:00210D* + ID_OUI_FROM_DATABASE=SAMSIN INNOTEC + +OUI:002141* + ID_OUI_FROM_DATABASE=RADLIVE + +OUI:002137* + ID_OUI_FROM_DATABASE=Bay Controls, LLC + +OUI:00212D* + ID_OUI_FROM_DATABASE=SCIMOLEX CORPORATION + +OUI:002133* + ID_OUI_FROM_DATABASE=Building B, Inc + +OUI:002121* + ID_OUI_FROM_DATABASE=VRmagic GmbH + +OUI:002126* + ID_OUI_FROM_DATABASE=Shenzhen Torch Equipment Co., Ltd. + +OUI:002257* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD + +OUI:00224E* + ID_OUI_FROM_DATABASE=SEEnergy Corp. + +OUI:002247* + ID_OUI_FROM_DATABASE=DAC ENGINEERING CO., LTD. + +OUI:00223D* + ID_OUI_FROM_DATABASE=JumpGen Systems, LLC + +OUI:002237* + ID_OUI_FROM_DATABASE=Shinhint Group + +OUI:002238* + ID_OUI_FROM_DATABASE=LOGIPLUS + +OUI:002231* + ID_OUI_FROM_DATABASE=SMT&C Co., Ltd. + +OUI:00222B* + ID_OUI_FROM_DATABASE=Nucomm, Inc. + +OUI:001EF6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001EEA* + ID_OUI_FROM_DATABASE=Sensor Switch, Inc. + +OUI:001EEF* + ID_OUI_FROM_DATABASE=Cantronic International Limited + +OUI:001EDE* + ID_OUI_FROM_DATABASE=BYD COMPANY LIMITED + +OUI:001EE3* + ID_OUI_FROM_DATABASE=T&W Electronics (ShenZhen) Co.,Ltd + +OUI:001EDD* + ID_OUI_FROM_DATABASE=WASKO S.A. + +OUI:001ED9* + ID_OUI_FROM_DATABASE=Mitsubishi Precision Co.,LTd. + +OUI:001ED4* + ID_OUI_FROM_DATABASE=Doble Engineering + +OUI:001ED3* + ID_OUI_FROM_DATABASE=Dot Technology Int'l Co., Ltd. + +OUI:001ECD* + ID_OUI_FROM_DATABASE=KYLAND Technology Co. LTD + +OUI:001EC6* + ID_OUI_FROM_DATABASE=Obvius Holdings LLC + +OUI:001F9D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001FA2* + ID_OUI_FROM_DATABASE=Datron World Communications, Inc. + +OUI:001F91* + ID_OUI_FROM_DATABASE=DBS Lodging Technologies, LLC + +OUI:001F96* + ID_OUI_FROM_DATABASE=APROTECH CO.LTD + +OUI:001F90* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:001F8F* + ID_OUI_FROM_DATABASE=Shanghai Bellmann Digital Source Co.,Ltd. + +OUI:001F85* + ID_OUI_FROM_DATABASE=Apriva ISS, LLC + +OUI:001F87* + ID_OUI_FROM_DATABASE=Skydigital Inc. + +OUI:001F86* + ID_OUI_FROM_DATABASE=digEcor + +OUI:001F80* + ID_OUI_FROM_DATABASE=Lucas Holding bv + +OUI:001F3E* + ID_OUI_FROM_DATABASE=RP-Technik e.K. + +OUI:001F42* + ID_OUI_FROM_DATABASE=Etherstack plc + +OUI:001F41* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:001F39* + ID_OUI_FROM_DATABASE=Construcciones y Auxiliar de Ferrocarriles, S.A. + +OUI:001F2B* + ID_OUI_FROM_DATABASE=Orange Logic + +OUI:001F2C* + ID_OUI_FROM_DATABASE=Starbridge Networks + +OUI:001F26* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001F1A* + ID_OUI_FROM_DATABASE=Prominvest + +OUI:001EC1* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD + +OUI:001EBA* + ID_OUI_FROM_DATABASE=High Density Devices AS + +OUI:001EB3* + ID_OUI_FROM_DATABASE=Primex Wireless + +OUI:001EB4* + ID_OUI_FROM_DATABASE=UNIFAT TECHNOLOGY LTD. + +OUI:001EA8* + ID_OUI_FROM_DATABASE=Datang Mobile Communications Equipment CO.,LTD + +OUI:001E9C* + ID_OUI_FROM_DATABASE=Fidustron INC + +OUI:001E95* + ID_OUI_FROM_DATABASE=SIGMALINK + +OUI:001E96* + ID_OUI_FROM_DATABASE=Sepura Plc + +OUI:001E8B* + ID_OUI_FROM_DATABASE=Infra Access Korea Co., Ltd. + +OUI:001FEF* + ID_OUI_FROM_DATABASE=SHINSEI INDUSTRIES CO.,LTD + +OUI:001FE8* + ID_OUI_FROM_DATABASE=KURUSUGAWA Electronics Industry Inc,. + +OUI:001FDC* + ID_OUI_FROM_DATABASE=Mobile Safe Track Ltd + +OUI:001FD7* + ID_OUI_FROM_DATABASE=TELERAD SA + +OUI:001FCB* + ID_OUI_FROM_DATABASE=NIW Solutions + +OUI:001F77* + ID_OUI_FROM_DATABASE=HEOL DESIGN + +OUI:001F73* + ID_OUI_FROM_DATABASE=Teraview Technology Co., Ltd. + +OUI:001F6D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001F61* + ID_OUI_FROM_DATABASE=Talent Communication Networks Inc. + +OUI:001F66* + ID_OUI_FROM_DATABASE=PLANAR LLC + +OUI:001F5A* + ID_OUI_FROM_DATABASE=Beckwith Electric Co. + +OUI:001F53* + ID_OUI_FROM_DATABASE=GEMAC Gesellschaft für Mikroelektronikanwendung Chemnitz mbH + +OUI:001F4E* + ID_OUI_FROM_DATABASE=ConMed Linvatec + +OUI:001F54* + ID_OUI_FROM_DATABASE=Lorex Technology Inc. + +OUI:001F47* + ID_OUI_FROM_DATABASE=MCS Logic Inc. + +OUI:001FD2* + ID_OUI_FROM_DATABASE=COMMTECH TECHNOLOGY MACAO COMMERCIAL OFFSHORE LTD. + +OUI:001FBF* + ID_OUI_FROM_DATABASE=Fulhua Microelectronics Corp. Taiwan Branch + +OUI:001FAC* + ID_OUI_FROM_DATABASE=Goodmill Systems Ltd + +OUI:00211A* + ID_OUI_FROM_DATABASE=LInTech Corporation + +OUI:002113* + ID_OUI_FROM_DATABASE=Padtec S/A + +OUI:002114* + ID_OUI_FROM_DATABASE=Hylab Technology Inc. + +OUI:00210E* + ID_OUI_FROM_DATABASE=Orpak Systems L.T.D. + +OUI:00210A* + ID_OUI_FROM_DATABASE=byd:sign Corporation + +OUI:002104* + ID_OUI_FROM_DATABASE=Gigaset Communications GmbH + +OUI:001FFB* + ID_OUI_FROM_DATABASE=Green Packet Bhd + +OUI:001FF6* + ID_OUI_FROM_DATABASE=PS Audio International + +OUI:001F19* + ID_OUI_FROM_DATABASE=BEN-RI ELECTRONICA S.A. + +OUI:001F13* + ID_OUI_FROM_DATABASE=S.& A.S. Ltd. + +OUI:001F0F* + ID_OUI_FROM_DATABASE=Select Engineered Systems + +OUI:001EFD* + ID_OUI_FROM_DATABASE=Microbit 2.0 AB + +OUI:001F02* + ID_OUI_FROM_DATABASE=Pixelmetrix Corporation Pte Ltd + +OUI:001EF0* + ID_OUI_FROM_DATABASE=Gigafin Networks + +OUI:001D2C* + ID_OUI_FROM_DATABASE=Wavetrend Technologies (Pty) Limited + +OUI:001D27* + ID_OUI_FROM_DATABASE=NAC-INTERCOM + +OUI:001D18* + ID_OUI_FROM_DATABASE=Power Innovation GmbH + +OUI:001D13* + ID_OUI_FROM_DATABASE=NextGTV + +OUI:001D0C* + ID_OUI_FROM_DATABASE=MobileCompia + +OUI:001D06* + ID_OUI_FROM_DATABASE=HM Electronics, Inc. + +OUI:001D05* + ID_OUI_FROM_DATABASE=Eaton Corporation + +OUI:001E62* + ID_OUI_FROM_DATABASE=Siemon + +OUI:001E5D* + ID_OUI_FROM_DATABASE=Holosys d.o.o. + +OUI:001E56* + ID_OUI_FROM_DATABASE=Bally Wulff Entertainment GmbH + +OUI:001E50* + ID_OUI_FROM_DATABASE=BATTISTONI RESEARCH + +OUI:001E4A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D85* + ID_OUI_FROM_DATABASE=Call Direct Cellular Solutions + +OUI:001D80* + ID_OUI_FROM_DATABASE=Beijing Huahuan Eletronics Co.,Ltd + +OUI:001D68* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:001D6F* + ID_OUI_FROM_DATABASE=Chainzone Technology Co., Ltd + +OUI:001D76* + ID_OUI_FROM_DATABASE=Eyeheight Ltd. + +OUI:001D7B* + ID_OUI_FROM_DATABASE=Ice Energy, Inc. + +OUI:001D75* + ID_OUI_FROM_DATABASE=Radioscape PLC + +OUI:001D63* + ID_OUI_FROM_DATABASE=Miele & Cie. KG + +OUI:001D5C* + ID_OUI_FROM_DATABASE=Tom Communication Industrial Co.,Ltd. + +OUI:001D55* + ID_OUI_FROM_DATABASE=ZANTAZ, Inc + +OUI:001DC8* + ID_OUI_FROM_DATABASE=Navionics Research Inc., dba SCADAmetrics + +OUI:001DC1* + ID_OUI_FROM_DATABASE=Audinate Pty L + +OUI:001DBB* + ID_OUI_FROM_DATABASE=Dynamic System Electronics Corp. + +OUI:001DAB* + ID_OUI_FROM_DATABASE=SwissQual License AG + +OUI:001E86* + ID_OUI_FROM_DATABASE=MEL Co.,Ltd. + +OUI:001E7F* + ID_OUI_FROM_DATABASE=CBM of America + +OUI:001E7A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001E79* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001E6F* + ID_OUI_FROM_DATABASE=Magna-Power Electronics, Inc. + +OUI:001E70* + ID_OUI_FROM_DATABASE=Cobham Defence Communications Ltd + +OUI:001E69* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:001D56* + ID_OUI_FROM_DATABASE=Kramer Electronics Ltd. + +OUI:001D50* + ID_OUI_FROM_DATABASE=SPINETIX SA + +OUI:001D4B* + ID_OUI_FROM_DATABASE=Grid Connect Inc. + +OUI:001D46* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D3F* + ID_OUI_FROM_DATABASE=Mitron Pty Ltd + +OUI:001D39* + ID_OUI_FROM_DATABASE=MOOHADIGITAL CO., LTD + +OUI:001D3A* + ID_OUI_FROM_DATABASE=mh acoustics LLC + +OUI:001D33* + ID_OUI_FROM_DATABASE=Maverick Systems Inc. + +OUI:001E09* + ID_OUI_FROM_DATABASE=ZEFATEK Co.,LTD + +OUI:001E04* + ID_OUI_FROM_DATABASE=Hanson Research Corporation + +OUI:001DF7* + ID_OUI_FROM_DATABASE=R. STAHL Schaltgeräte GmbH + +OUI:001DF8* + ID_OUI_FROM_DATABASE=Webpro Vision Technology Corporation + +OUI:001DF1* + ID_OUI_FROM_DATABASE=Intego Systems, Inc. + +OUI:001DEA* + ID_OUI_FROM_DATABASE=Commtest Instruments Ltd + +OUI:001DDB* + ID_OUI_FROM_DATABASE=C-BEL Corporation + +OUI:001DE5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001DA4* + ID_OUI_FROM_DATABASE=Hangzhou System Technology CO., LTD + +OUI:001D9F* + ID_OUI_FROM_DATABASE=MATT R.P.Traczynscy Sp.J. + +OUI:001D92* + ID_OUI_FROM_DATABASE=MICRO-STAR INT'L CO.,LTD. + +OUI:001D91* + ID_OUI_FROM_DATABASE=Digitize, Inc + +OUI:001D8C* + ID_OUI_FROM_DATABASE=La Crosse Technology LTD + +OUI:001E39* + ID_OUI_FROM_DATABASE=Comsys Communication Ltd. + +OUI:001E34* + ID_OUI_FROM_DATABASE=CryptoMetrics + +OUI:001E2D* + ID_OUI_FROM_DATABASE=STIM + +OUI:001E26* + ID_OUI_FROM_DATABASE=Digifriends Co. Ltd + +OUI:001E1A* + ID_OUI_FROM_DATABASE=Best Source Taiwan Inc. + +OUI:001E14* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001E0A* + ID_OUI_FROM_DATABASE=Syba Tech Limited + +OUI:001C61* + ID_OUI_FROM_DATABASE=Galaxy Microsystems LImited + +OUI:001C55* + ID_OUI_FROM_DATABASE=Shenzhen Kaifa Technology Co. + +OUI:001C5A* + ID_OUI_FROM_DATABASE=Advanced Relay Corporation + +OUI:001C44* + ID_OUI_FROM_DATABASE=Bosch Security Systems BV + +OUI:001C4B* + ID_OUI_FROM_DATABASE=Gener8, Inc. + +OUI:001C38* + ID_OUI_FROM_DATABASE=Bio-Rad Laboratories, Inc. + +OUI:001C3D* + ID_OUI_FROM_DATABASE=WaveStorm + +OUI:001C3F* + ID_OUI_FROM_DATABASE=International Police Technologies, Inc. + +OUI:001C3E* + ID_OUI_FROM_DATABASE=ECKey Corporation + +OUI:001C31* + ID_OUI_FROM_DATABASE=Mobile XP Technology Co., LTD + +OUI:001C2C* + ID_OUI_FROM_DATABASE=Synapse + +OUI:001CF9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001CF3* + ID_OUI_FROM_DATABASE=EVS BROADCAST EQUIPMENT + +OUI:001CF4* + ID_OUI_FROM_DATABASE=Media Technology Systems Inc + +OUI:001CED* + ID_OUI_FROM_DATABASE=ENVIRONNEMENT SA + +OUI:001CE3* + ID_OUI_FROM_DATABASE=Optimedical Systems + +OUI:001CDC* + ID_OUI_FROM_DATABASE=Custom Computer Services, Inc. + +OUI:001CD0* + ID_OUI_FROM_DATABASE=Circleone Co.,Ltd. + +OUI:001BF5* + ID_OUI_FROM_DATABASE=Tellink Sistemas de Telecomunicación S.L. + +OUI:001BF0* + ID_OUI_FROM_DATABASE=Value Platforms Limited + +OUI:001BE8* + ID_OUI_FROM_DATABASE=Ultratronik GmbH + +OUI:001BE1* + ID_OUI_FROM_DATABASE=ViaLogy + +OUI:001BDC* + ID_OUI_FROM_DATABASE=Vencer Co., Ltd. + +OUI:001BD5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001BCE* + ID_OUI_FROM_DATABASE=Measurement Devices Ltd + +OUI:001C94* + ID_OUI_FROM_DATABASE=LI-COR Biosciences + +OUI:001C8E* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:001C8D* + ID_OUI_FROM_DATABASE=Mesa Imaging + +OUI:001C88* + ID_OUI_FROM_DATABASE=TRANSYSTEM INC. + +OUI:001C83* + ID_OUI_FROM_DATABASE=New Level Telecom Co., Ltd. + +OUI:001C7A* + ID_OUI_FROM_DATABASE=Perfectone Netware Company Ltd + +OUI:001C7B* + ID_OUI_FROM_DATABASE=Castlenet Technology Inc. + +OUI:001C79* + ID_OUI_FROM_DATABASE=Cohesive Financial Technologies LLC + +OUI:001C74* + ID_OUI_FROM_DATABASE=Syswan Technologies Inc. + +OUI:001C6D* + ID_OUI_FROM_DATABASE=KYOHRITSU ELECTRONIC INDUSTRY CO., LTD. + +OUI:001C68* + ID_OUI_FROM_DATABASE=Anhui Sun Create Electronics Co., Ltd + +OUI:001CC9* + ID_OUI_FROM_DATABASE=Kaise Electronic Technology Co., Ltd. + +OUI:001CCA* + ID_OUI_FROM_DATABASE=Shanghai Gaozhi Science & Technology Development Co. + +OUI:001CBD* + ID_OUI_FROM_DATABASE=Ezze Mobile Tech., Inc. + +OUI:001CB8* + ID_OUI_FROM_DATABASE=CBC Co., Ltd + +OUI:001CAD* + ID_OUI_FROM_DATABASE=Wuhan Telecommunication Devices Co.,Ltd + +OUI:001CAE* + ID_OUI_FROM_DATABASE=WiChorus, Inc. + +OUI:001CA7* + ID_OUI_FROM_DATABASE=International Quartz Limited + +OUI:001CA0* + ID_OUI_FROM_DATABASE=Production Resource Group, LLC + +OUI:001C9B* + ID_OUI_FROM_DATABASE=FEIG ELECTRONIC GmbH + +OUI:001B69* + ID_OUI_FROM_DATABASE=Equaline Corporation + +OUI:001B64* + ID_OUI_FROM_DATABASE=IsaacLandKorea Co., Ltd, + +OUI:001B5D* + ID_OUI_FROM_DATABASE=Vololink Pty Ltd + +OUI:001B56* + ID_OUI_FROM_DATABASE=Tehuti Networks Ltd. + +OUI:001B51* + ID_OUI_FROM_DATABASE=Vector Technology Corp. + +OUI:001B45* + ID_OUI_FROM_DATABASE=ABB AS, Division Automation Products + +OUI:001B4A* + ID_OUI_FROM_DATABASE=W&W Communications, Inc. + +OUI:001B43* + ID_OUI_FROM_DATABASE=Beijing DG Telecommunications equipment Co.,Ltd + +OUI:001B3E* + ID_OUI_FROM_DATABASE=Curtis, Inc. + +OUI:001B37* + ID_OUI_FROM_DATABASE=Computec Oy + +OUI:001B2B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001BC9* + ID_OUI_FROM_DATABASE=FSN DISPLAY INC + +OUI:001BC2* + ID_OUI_FROM_DATABASE=Integrated Control Technology Limitied + +OUI:001BBC* + ID_OUI_FROM_DATABASE=Silver Peak Systems, Inc. + +OUI:001BBD* + ID_OUI_FROM_DATABASE=FMC Kongsberg Subsea AS + +OUI:001BB3* + ID_OUI_FROM_DATABASE=Condalo GmbH + +OUI:001BB8* + ID_OUI_FROM_DATABASE=BLUEWAY ELECTRONIC CO;LTD + +OUI:001BAC* + ID_OUI_FROM_DATABASE=Curtiss Wright Controls Embedded Computing + +OUI:001BB2* + ID_OUI_FROM_DATABASE=Intellect International NV + +OUI:001BA5* + ID_OUI_FROM_DATABASE=MyungMin Systems, Inc. + +OUI:001BA0* + ID_OUI_FROM_DATABASE=Awox + +OUI:001B99* + ID_OUI_FROM_DATABASE=KS System GmbH + +OUI:001C1B* + ID_OUI_FROM_DATABASE=Hyperstone GmbH + +OUI:001C0F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001C08* + ID_OUI_FROM_DATABASE=Echo360, Inc. + +OUI:001C02* + ID_OUI_FROM_DATABASE=Pano Logic + +OUI:001C01* + ID_OUI_FROM_DATABASE=ABB Oy Drives + +OUI:001C03* + ID_OUI_FROM_DATABASE=Betty TV Technology AG + +OUI:001B92* + ID_OUI_FROM_DATABASE=l-acoustics + +OUI:001B8D* + ID_OUI_FROM_DATABASE=Electronic Computer Systems, Inc. + +OUI:001B88* + ID_OUI_FROM_DATABASE=Divinet Access Technologies Ltd + +OUI:001B83* + ID_OUI_FROM_DATABASE=Finsoft Ltd + +OUI:001B7C* + ID_OUI_FROM_DATABASE=A & R Cambridge + +OUI:001B76* + ID_OUI_FROM_DATABASE=Ripcode, Inc. + +OUI:001B75* + ID_OUI_FROM_DATABASE=Hypermedia Systems + +OUI:001B70* + ID_OUI_FROM_DATABASE=IRI Ubiteq, INC. + +OUI:001A18* + ID_OUI_FROM_DATABASE=Advanced Simulation Technology inc. + +OUI:001A0A* + ID_OUI_FROM_DATABASE=Adaptive Micro-Ware Inc. + +OUI:001A05* + ID_OUI_FROM_DATABASE=OPTIBASE LTD + +OUI:001A03* + ID_OUI_FROM_DATABASE=Angel Electronics Co., Ltd. + +OUI:0019FE* + ID_OUI_FROM_DATABASE=SHENZHEN SEECOMM TECHNOLOGY CO.,LTD. + +OUI:0019F9* + ID_OUI_FROM_DATABASE=TDK-Lambda + +OUI:0019ED* + ID_OUI_FROM_DATABASE=Axesstel Inc. + +OUI:0019F4* + ID_OUI_FROM_DATABASE=Convergens Oy Ltd + +OUI:001A79* + ID_OUI_FROM_DATABASE=TELECOMUNICATION TECHNOLOGIES LTD. + +OUI:001A99* + ID_OUI_FROM_DATABASE=Smarty (HZ) Information Electronics Co., Ltd + +OUI:001A9B* + ID_OUI_FROM_DATABASE=ADEC & Parter AG + +OUI:001A94* + ID_OUI_FROM_DATABASE=Votronic GmbH + +OUI:001A83* + ID_OUI_FROM_DATABASE=Pegasus Technologies Inc. + +OUI:001A7E* + ID_OUI_FROM_DATABASE=LN Srithai Comm Ltd. + +OUI:001AF1* + ID_OUI_FROM_DATABASE=Embedded Artists AB + +OUI:001AF6* + ID_OUI_FROM_DATABASE=Woven Systems, Inc. + +OUI:001AEC* + ID_OUI_FROM_DATABASE=Keumbee Electronics Co.,Ltd. + +OUI:001AE0* + ID_OUI_FROM_DATABASE=Mythology Tech Express Inc. + +OUI:001AE5* + ID_OUI_FROM_DATABASE=Mvox Technologies Inc. + +OUI:001AD2* + ID_OUI_FROM_DATABASE=Eletronica Nitron Ltda + +OUI:001AD9* + ID_OUI_FROM_DATABASE=International Broadband Electric Communications, Inc. + +OUI:001ACB* + ID_OUI_FROM_DATABASE=Autocom Products Ltd + +OUI:001ACD* + ID_OUI_FROM_DATABASE=Tidel Engineering LP + +OUI:001A46* + ID_OUI_FROM_DATABASE=Digital Multimedia Technology Co., Ltd + +OUI:001A3A* + ID_OUI_FROM_DATABASE=Dongahelecomm + +OUI:001A3F* + ID_OUI_FROM_DATABASE=intelbras + +OUI:001A41* + ID_OUI_FROM_DATABASE=INOCOVA Co.,Ltd + +OUI:001A2E* + ID_OUI_FROM_DATABASE=Ziova Coporation + +OUI:001A33* + ID_OUI_FROM_DATABASE=ASI Communications, Inc. + +OUI:001A1D* + ID_OUI_FROM_DATABASE=PChome Online Inc. + +OUI:001A24* + ID_OUI_FROM_DATABASE=Galaxy Telecom Technologies Ltd + +OUI:0019A5* + ID_OUI_FROM_DATABASE=RadarFind Corporation + +OUI:0019AC* + ID_OUI_FROM_DATABASE=GSP SYSTEMS Inc. + +OUI:0019B1* + ID_OUI_FROM_DATABASE=Arrow7 Corporation + +OUI:00199E* + ID_OUI_FROM_DATABASE=Nifty + +OUI:0019A0* + ID_OUI_FROM_DATABASE=NIHON DATA SYSTENS, INC. + +OUI:001994* + ID_OUI_FROM_DATABASE=Jorjin Technologies Inc. + +OUI:00198F* + ID_OUI_FROM_DATABASE=Alcatel Bell N.V. + +OUI:0019E8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0019DA* + ID_OUI_FROM_DATABASE=Welltrans O&E Technology Co. , Ltd. + +OUI:0019DC* + ID_OUI_FROM_DATABASE=ENENSYS Technologies + +OUI:0019C9* + ID_OUI_FROM_DATABASE=S&C ELECTRIC COMPANY + +OUI:0019CE* + ID_OUI_FROM_DATABASE=Progressive Gaming International + +OUI:0019D5* + ID_OUI_FROM_DATABASE=IP Innovations, Inc. + +OUI:0019C4* + ID_OUI_FROM_DATABASE=Infocrypt Inc. + +OUI:0019BF* + ID_OUI_FROM_DATABASE=Citiway technology Co.,ltd + +OUI:0019BD* + ID_OUI_FROM_DATABASE=New Media Life + +OUI:0019B8* + ID_OUI_FROM_DATABASE=Boundary Devices + +OUI:001B26* + ID_OUI_FROM_DATABASE=RON-Telecom ZAO + +OUI:001B1C* + ID_OUI_FROM_DATABASE=Coherent + +OUI:001B1A* + ID_OUI_FROM_DATABASE=e-trees Japan, Inc. + +OUI:001B15* + ID_OUI_FROM_DATABASE=Voxtel, Inc. + +OUI:001B09* + ID_OUI_FROM_DATABASE=Matrix Telecom Pvt. Ltd. + +OUI:001B0E* + ID_OUI_FROM_DATABASE=InoTec GmbH Organisationssysteme + +OUI:001B07* + ID_OUI_FROM_DATABASE=Mendocino Software + +OUI:001B02* + ID_OUI_FROM_DATABASE=ED Co.Ltd + +OUI:001AFB* + ID_OUI_FROM_DATABASE=Joby Inc. + +OUI:001A74* + ID_OUI_FROM_DATABASE=Procare International Co + +OUI:001A6D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A68* + ID_OUI_FROM_DATABASE=Weltec Enterprise Co., Ltd. + +OUI:001A61* + ID_OUI_FROM_DATABASE=PacStar Corp. + +OUI:001A54* + ID_OUI_FROM_DATABASE=Hip Shing Electronics Ltd. + +OUI:001A59* + ID_OUI_FROM_DATABASE=Ircona + +OUI:001A4D* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:001A52* + ID_OUI_FROM_DATABASE=Meshlinx Wireless Inc. + +OUI:001AC6* + ID_OUI_FROM_DATABASE=Micro Control Designs + +OUI:001ABC* + ID_OUI_FROM_DATABASE=U4EA Technologies Ltd + +OUI:001AC1* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:001AB0* + ID_OUI_FROM_DATABASE=Signal Networks Pvt. Ltd., + +OUI:001AB5* + ID_OUI_FROM_DATABASE=Home Network System + +OUI:001AA9* + ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD + +OUI:00183C* + ID_OUI_FROM_DATABASE=Encore Software Limited + +OUI:001841* + ID_OUI_FROM_DATABASE=High Tech Computer Corp + +OUI:001843* + ID_OUI_FROM_DATABASE=Dawevision Ltd + +OUI:001837* + ID_OUI_FROM_DATABASE=Universal ABIT Co., Ltd. + +OUI:001826* + ID_OUI_FROM_DATABASE=Cale Access AB + +OUI:00182B* + ID_OUI_FROM_DATABASE=Softier + +OUI:001818* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00181A* + ID_OUI_FROM_DATABASE=AVerMedia Information Inc. + +OUI:00181F* + ID_OUI_FROM_DATABASE=Palmmicro Communications + +OUI:001804* + ID_OUI_FROM_DATABASE=E-TEK DIGITAL TECHNOLOGY LIMITED + +OUI:001807* + ID_OUI_FROM_DATABASE=Fanstel Corp. + +OUI:00180C* + ID_OUI_FROM_DATABASE=Optelian Access Networks + +OUI:0017FF* + ID_OUI_FROM_DATABASE=PLAYLINE Co.,Ltd. + +OUI:0017F1* + ID_OUI_FROM_DATABASE=Renu Electronics Pvt Ltd + +OUI:0017F3* + ID_OUI_FROM_DATABASE=Harris Corparation + +OUI:0017F8* + ID_OUI_FROM_DATABASE=Motech Industries Inc. + +OUI:0017D4* + ID_OUI_FROM_DATABASE=Monsoon Multimedia, Inc + +OUI:0017D9* + ID_OUI_FROM_DATABASE=AAI Corporation + +OUI:0017E0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001920* + ID_OUI_FROM_DATABASE=KUME electric Co.,Ltd. + +OUI:001925* + ID_OUI_FROM_DATABASE=Intelicis Corporation + +OUI:001912* + ID_OUI_FROM_DATABASE=Welcat Inc + +OUI:001914* + ID_OUI_FROM_DATABASE=Winix Co., Ltd + +OUI:001919* + ID_OUI_FROM_DATABASE=ASTEL Inc. + +OUI:00190D* + ID_OUI_FROM_DATABASE=IEEE 1394c + +OUI:001901* + ID_OUI_FROM_DATABASE=F1MEDIA + +OUI:001906* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018F5* + ID_OUI_FROM_DATABASE=Shenzhen Streaming Video Technology Company Limited + +OUI:0018F7* + ID_OUI_FROM_DATABASE=Kameleon Technologies + +OUI:0018FC* + ID_OUI_FROM_DATABASE=Altec Electronic AG + +OUI:001981* + ID_OUI_FROM_DATABASE=Vivox Inc + +OUI:001983* + ID_OUI_FROM_DATABASE=CCT R&D Limited + +OUI:001975* + ID_OUI_FROM_DATABASE=Beijing Huisen networks technology Inc + +OUI:00197C* + ID_OUI_FROM_DATABASE=Riedel Communications GmbH + +OUI:001970* + ID_OUI_FROM_DATABASE=Z-Com, Inc. + +OUI:001964* + ID_OUI_FROM_DATABASE=Doorking Inc. + +OUI:00195F* + ID_OUI_FROM_DATABASE=Valemount Networks Corporation + +OUI:001953* + ID_OUI_FROM_DATABASE=Chainleader Communications Corp. + +OUI:001958* + ID_OUI_FROM_DATABASE=Bluetooth SIG, Inc. + +OUI:00195A* + ID_OUI_FROM_DATABASE=Jenaer Antriebstechnik GmbH + +OUI:0018F0* + ID_OUI_FROM_DATABASE=JOYTOTO Co., Ltd. + +OUI:0018E9* + ID_OUI_FROM_DATABASE=Numata Corporation + +OUI:0018E4* + ID_OUI_FROM_DATABASE=YIGUANG + +OUI:0018DD* + ID_OUI_FROM_DATABASE=Silicondust Engineering Ltd + +OUI:0018D8* + ID_OUI_FROM_DATABASE=ARCH METER Corporation + +OUI:0018D1* + ID_OUI_FROM_DATABASE=Siemens Home & Office Comm. Devices + +OUI:0018D6* + ID_OUI_FROM_DATABASE=Swirlnet A/S + +OUI:0018CC* + ID_OUI_FROM_DATABASE=AXIOHM SAS + +OUI:0018C7* + ID_OUI_FROM_DATABASE=Real Time Automation + +OUI:00186C* + ID_OUI_FROM_DATABASE=Neonode AB + +OUI:001878* + ID_OUI_FROM_DATABASE=Mackware GmbH + +OUI:001867* + ID_OUI_FROM_DATABASE=Datalogic ADC + +OUI:00185B* + ID_OUI_FROM_DATABASE=Network Chemistry, Inc + +OUI:001862* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:00184F* + ID_OUI_FROM_DATABASE=8 Ways Technology Corp. + +OUI:001854* + ID_OUI_FROM_DATABASE=Argard Co., Ltd + +OUI:001856* + ID_OUI_FROM_DATABASE=EyeFi, Inc + +OUI:001848* + ID_OUI_FROM_DATABASE=Vecima Networks Inc. + +OUI:001945* + ID_OUI_FROM_DATABASE=RF COncepts, LLC + +OUI:00194C* + ID_OUI_FROM_DATABASE=Fujian Stelcom information & Technology CO.,Ltd + +OUI:001940* + ID_OUI_FROM_DATABASE=Rackable Systems + +OUI:001934* + ID_OUI_FROM_DATABASE=TRENDON TOUCH TECHNOLOGY CORP. + +OUI:001939* + ID_OUI_FROM_DATABASE=Gigamips + +OUI:001931* + ID_OUI_FROM_DATABASE=Balluff GmbH + +OUI:0018BB* + ID_OUI_FROM_DATABASE=Eliwell Controls srl + +OUI:0018B9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018B4* + ID_OUI_FROM_DATABASE=Dawon Media Inc. + +OUI:0018AD* + ID_OUI_FROM_DATABASE=NIDEC SANKYO CORPORATION + +OUI:0018A8* + ID_OUI_FROM_DATABASE=AnNeal Technology Inc. + +OUI:00189C* + ID_OUI_FROM_DATABASE=Weldex Corporation + +OUI:0018A1* + ID_OUI_FROM_DATABASE=Tiqit Computers, Inc. + +OUI:001897* + ID_OUI_FROM_DATABASE=JESS-LINK PRODUCTS Co., LTD + +OUI:001892* + ID_OUI_FROM_DATABASE=ads-tec GmbH + +OUI:001890* + ID_OUI_FROM_DATABASE=RadioCOM, s.r.o. + +OUI:001884* + ID_OUI_FROM_DATABASE=Fon Technology S.L. + +OUI:00187D* + ID_OUI_FROM_DATABASE=Armorlink shanghai Co. Ltd + +OUI:00187F* + ID_OUI_FROM_DATABASE=ZODIANET + +OUI:0016D1* + ID_OUI_FROM_DATABASE=ZAT a.s. + +OUI:0016C3* + ID_OUI_FROM_DATABASE=BA Systems Inc + +OUI:0016BE* + ID_OUI_FROM_DATABASE=INFRANET, Inc. + +OUI:0016B7* + ID_OUI_FROM_DATABASE=Seoul Commtech + +OUI:0016B2* + ID_OUI_FROM_DATABASE=DriveCam Inc + +OUI:0016B0* + ID_OUI_FROM_DATABASE=VK Corporation + +OUI:0016AB* + ID_OUI_FROM_DATABASE=Dansensor A/S + +OUI:0016A6* + ID_OUI_FROM_DATABASE=Dovado FZ-LLC + +OUI:0017C8* + ID_OUI_FROM_DATABASE=KYOCERA Document Solutions Inc. + +OUI:0017CF* + ID_OUI_FROM_DATABASE=iMCA-GmbH + +OUI:0017C3* + ID_OUI_FROM_DATABASE=KTF Technologies Inc. + +OUI:0017B7* + ID_OUI_FROM_DATABASE=Tonze Technology Co. + +OUI:0017BC* + ID_OUI_FROM_DATABASE=Touchtunes Music Corporation + +OUI:0017B5* + ID_OUI_FROM_DATABASE=Peerless Systems Corporation + +OUI:001723* + ID_OUI_FROM_DATABASE=Summit Data Communications + +OUI:00171C* + ID_OUI_FROM_DATABASE=NT MicroSystems, Inc. + +OUI:001710* + ID_OUI_FROM_DATABASE=Casa Systems Inc. + +OUI:001715* + ID_OUI_FROM_DATABASE=Qstik + +OUI:001717* + ID_OUI_FROM_DATABASE=Leica Geosystems AG + +OUI:00170B* + ID_OUI_FROM_DATABASE=Contela, Inc. + +OUI:0016FF* + ID_OUI_FROM_DATABASE=Wamin Optocomm Mfg Corp + +OUI:001774* + ID_OUI_FROM_DATABASE=Elesta GmbH + +OUI:001779* + ID_OUI_FROM_DATABASE=QuickTel + +OUI:00177B* + ID_OUI_FROM_DATABASE=Azalea Networks inc + +OUI:001764* + ID_OUI_FROM_DATABASE=ATMedia GmbH + +OUI:001766* + ID_OUI_FROM_DATABASE=Accense Technology, Inc. + +OUI:00175F* + ID_OUI_FROM_DATABASE=XENOLINK Communications Co., Ltd. + +OUI:001751* + ID_OUI_FROM_DATABASE=Online Corporation + +OUI:001753* + ID_OUI_FROM_DATABASE=nFore Technology Inc. + +OUI:001758* + ID_OUI_FROM_DATABASE=ThruVision Ltd + +OUI:001745* + ID_OUI_FROM_DATABASE=INNOTZ CO., Ltd + +OUI:00174C* + ID_OUI_FROM_DATABASE=Millipore + +OUI:00179F* + ID_OUI_FROM_DATABASE=Apricorn + +OUI:0017A9* + ID_OUI_FROM_DATABASE=Sentivision + +OUI:001793* + ID_OUI_FROM_DATABASE=Tigi Corporation + +OUI:00178C* + ID_OUI_FROM_DATABASE=Independent Witness, Inc + +OUI:00178E* + ID_OUI_FROM_DATABASE=Gunnebo Cash Automation AB + +OUI:001780* + ID_OUI_FROM_DATABASE=Applied Biosystems B.V. + +OUI:001787* + ID_OUI_FROM_DATABASE=Brother, Brother & Sons ApS + +OUI:00176B* + ID_OUI_FROM_DATABASE=Kiyon, Inc. + +OUI:00BAC0* + ID_OUI_FROM_DATABASE=Biometric Access Company + +OUI:001673* + ID_OUI_FROM_DATABASE=Bury GmbH & Co. KG + +OUI:001671* + ID_OUI_FROM_DATABASE=Symphox Information Co. + +OUI:001665* + ID_OUI_FROM_DATABASE=Cellon France + +OUI:00166A* + ID_OUI_FROM_DATABASE=TPS + +OUI:00165E* + ID_OUI_FROM_DATABASE=Precision I/O + +OUI:001657* + ID_OUI_FROM_DATABASE=Aegate Ltd + +OUI:001659* + ID_OUI_FROM_DATABASE=Z.M.P. RADWAG + +OUI:001658* + ID_OUI_FROM_DATABASE=Fusiontech Technologies Inc. + +OUI:001652* + ID_OUI_FROM_DATABASE=Hoatech Technologies, Inc. + +OUI:001646* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00164B* + ID_OUI_FROM_DATABASE=Quorion Data Systems GmbH + +OUI:001740* + ID_OUI_FROM_DATABASE=Bluberi Gaming Technologies Inc + +OUI:001736* + ID_OUI_FROM_DATABASE=iiTron Inc. + +OUI:00172F* + ID_OUI_FROM_DATABASE=NeuLion Incorporated + +OUI:001728* + ID_OUI_FROM_DATABASE=Selex Communications + +OUI:00172A* + ID_OUI_FROM_DATABASE=Proware Technology Corp.(By Unifosa) + +OUI:00169A* + ID_OUI_FROM_DATABASE=Quadrics Ltd + +OUI:0016A1* + ID_OUI_FROM_DATABASE=3Leaf Networks + +OUI:001693* + ID_OUI_FROM_DATABASE=PowerLink Technology Inc. + +OUI:001695* + ID_OUI_FROM_DATABASE=AVC Technology (International) Limited + +OUI:00168E* + ID_OUI_FROM_DATABASE=Vimicro corporation + +OUI:001682* + ID_OUI_FROM_DATABASE=Pro Dex, Inc + +OUI:001687* + ID_OUI_FROM_DATABASE=Chubb CSC-Vendor AP + +OUI:00167B* + ID_OUI_FROM_DATABASE=Haver&Boecker + +OUI:0016F3* + ID_OUI_FROM_DATABASE=CAST Information Co., Ltd + +OUI:0016EE* + ID_OUI_FROM_DATABASE=Royaldigital Inc. + +OUI:0016E7* + ID_OUI_FROM_DATABASE=Dynamix Promotions Limited + +OUI:0016E0* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:0016D6* + ID_OUI_FROM_DATABASE=TDA Tech Pty Ltd + +OUI:00151E* + ID_OUI_FROM_DATABASE=Ethernet Powerlink Standardization Group (EPSG) + +OUI:001525* + ID_OUI_FROM_DATABASE=Chamberlain Access Solutions + +OUI:001519* + ID_OUI_FROM_DATABASE=StoreAge Networking Technologies + +OUI:001518* + ID_OUI_FROM_DATABASE=Shenzhen 10MOONS Technology Development CO.,Ltd + +OUI:001514* + ID_OUI_FROM_DATABASE=Hu Zhou NAVA Networks&Electronics Ltd. + +OUI:00150E* + ID_OUI_FROM_DATABASE=OPENBRAIN TECHNOLOGIES CO., LTD. + +OUI:00150F* + ID_OUI_FROM_DATABASE=mingjong + +OUI:00150D* + ID_OUI_FROM_DATABASE=Hoana Medical, Inc. + +OUI:001508* + ID_OUI_FROM_DATABASE=Global Target Enterprise Inc + +OUI:0014FC* + ID_OUI_FROM_DATABASE=Extandon, Inc. + +OUI:001501* + ID_OUI_FROM_DATABASE=LexBox + +OUI:0014F5* + ID_OUI_FROM_DATABASE=OSI Security Devices + +OUI:0014E9* + ID_OUI_FROM_DATABASE=Nortech International + +OUI:0014EE* + ID_OUI_FROM_DATABASE=Western Digital Technologies, Inc. + +OUI:0014DF* + ID_OUI_FROM_DATABASE=HI-P Tech Corporation + +OUI:0014E4* + ID_OUI_FROM_DATABASE=infinias, LLC + +OUI:0014D3* + ID_OUI_FROM_DATABASE=SEPSA + +OUI:0014D8* + ID_OUI_FROM_DATABASE=bio-logic SA + +OUI:0014D2* + ID_OUI_FROM_DATABASE=Kyuden Technosystems Corporation + +OUI:0015E0* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:0015DC* + ID_OUI_FROM_DATABASE=KT&C Co., Ltd. + +OUI:0015D5* + ID_OUI_FROM_DATABASE=NICEVT + +OUI:0015D7* + ID_OUI_FROM_DATABASE=Reti Corporation + +OUI:0015D6* + ID_OUI_FROM_DATABASE=OSLiNK Sp. z o.o. + +OUI:0015C4* + ID_OUI_FROM_DATABASE=FLOVEL CO., LTD. + +OUI:0015C9* + ID_OUI_FROM_DATABASE=Gumstix, Inc + +OUI:0015BD* + ID_OUI_FROM_DATABASE=Group 4 Technology Ltd + +OUI:0015B6* + ID_OUI_FROM_DATABASE=ShinMaywa Industries, Ltd. + +OUI:001581* + ID_OUI_FROM_DATABASE=MAKUS Inc. + +OUI:00156B* + ID_OUI_FROM_DATABASE=Perfisans Networks Corp. + +OUI:001570* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:00155D* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:00155F* + ID_OUI_FROM_DATABASE=GreenPeak Technologies + +OUI:001564* + ID_OUI_FROM_DATABASE=BEHRINGER Spezielle Studiotechnik GmbH + +OUI:00155E* + ID_OUI_FROM_DATABASE=Morgan Stanley + +OUI:001558* + ID_OUI_FROM_DATABASE=FOXCONN + +OUI:001551* + ID_OUI_FROM_DATABASE=RadioPulse Inc. + +OUI:001549* + ID_OUI_FROM_DATABASE=Dixtal Biomedica Ind. Com. Ltda + +OUI:00154C* + ID_OUI_FROM_DATABASE=Saunders Electronics + +OUI:00154A* + ID_OUI_FROM_DATABASE=WANSHIH ELECTRONIC CO., LTD + +OUI:00153D* + ID_OUI_FROM_DATABASE=ELIM PRODUCT CO. + +OUI:001544* + ID_OUI_FROM_DATABASE=coM.s.a.t. AG + +OUI:001531* + ID_OUI_FROM_DATABASE=KOCOM + +OUI:001538* + ID_OUI_FROM_DATABASE=RFID, Inc. + +OUI:00152A* + ID_OUI_FROM_DATABASE=Nokia GmbH + +OUI:00161D* + ID_OUI_FROM_DATABASE=Innovative Wireless Technologies, Inc. + +OUI:00161C* + ID_OUI_FROM_DATABASE=e:cue + +OUI:00160C* + ID_OUI_FROM_DATABASE=LPL DEVELOPMENT S.A. DE C.V + +OUI:001611* + ID_OUI_FROM_DATABASE=Altecon Srl + +OUI:001612* + ID_OUI_FROM_DATABASE=Otsuka Electronics Co., Ltd. + +OUI:001605* + ID_OUI_FROM_DATABASE=YORKVILLE SOUND INC. + +OUI:0015F9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001600* + ID_OUI_FROM_DATABASE=CelleBrite Mobile Synchronization + +OUI:0015ED* + ID_OUI_FROM_DATABASE=Fulcrum Microsystems, Inc. + +OUI:0015E1* + ID_OUI_FROM_DATABASE=Picochip Ltd + +OUI:0015E6* + ID_OUI_FROM_DATABASE=MOBILE TECHNIKA Inc. + +OUI:0015B1* + ID_OUI_FROM_DATABASE=Ambient Corporation + +OUI:0015AC* + ID_OUI_FROM_DATABASE=Capelon AB + +OUI:0015A7* + ID_OUI_FROM_DATABASE=Robatech AG + +OUI:001594* + ID_OUI_FROM_DATABASE=BIXOLON CO.,LTD + +OUI:00158D* + ID_OUI_FROM_DATABASE=Jennic Ltd + +OUI:001588* + ID_OUI_FROM_DATABASE=Salutica Allied Solutions Sdn Bhd + +OUI:0014CC* + ID_OUI_FROM_DATABASE=Zetec, Inc. + +OUI:0014C0* + ID_OUI_FROM_DATABASE=Symstream Technology Group Ltd + +OUI:0014C5* + ID_OUI_FROM_DATABASE=Alive Technologies Pty Ltd + +OUI:0014B9* + ID_OUI_FROM_DATABASE=MSTAR SEMICONDUCTOR + +OUI:0014AF* + ID_OUI_FROM_DATABASE=Datasym POS Inc. + +OUI:0014A8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00163C* + ID_OUI_FROM_DATABASE=Rebox B.V. + +OUI:00162E* + ID_OUI_FROM_DATABASE=Space Shuttle Hi-Tech Co., Ltd. + +OUI:001629* + ID_OUI_FROM_DATABASE=Nivus GmbH + +OUI:001622* + ID_OUI_FROM_DATABASE=BBH SYSTEMS GMBH + +OUI:001616* + ID_OUI_FROM_DATABASE=BROWAN COMMUNICATION INC. + +OUI:00161B* + ID_OUI_FROM_DATABASE=Micronet Corporation + +OUI:00135B* + ID_OUI_FROM_DATABASE=PanelLink Cinema, LLC + +OUI:001362* + ID_OUI_FROM_DATABASE=ShinHeung Precision Co., Ltd. + +OUI:001351* + ID_OUI_FROM_DATABASE=Niles Audio Corporation + +OUI:001345* + ID_OUI_FROM_DATABASE=Eaton Corporation + +OUI:00134A* + ID_OUI_FROM_DATABASE=Engim, Inc. + +OUI:00133E* + ID_OUI_FROM_DATABASE=MetaSwitch + +OUI:00132B* + ID_OUI_FROM_DATABASE=Phoenix Digital + +OUI:001332* + ID_OUI_FROM_DATABASE=Beijing Topsec Network Security Technology Co., Ltd. + +OUI:001337* + ID_OUI_FROM_DATABASE=Orient Power Home Network Ltd. + +OUI:001338* + ID_OUI_FROM_DATABASE=FRESENIUS-VIAL + +OUI:00137A* + ID_OUI_FROM_DATABASE=Netvox Technology Co., Ltd. + +OUI:001381* + ID_OUI_FROM_DATABASE=CHIPS & Systems, Inc. + +OUI:001386* + ID_OUI_FROM_DATABASE=ABB Inc./Totalflow + +OUI:001374* + ID_OUI_FROM_DATABASE=Atheros Communications, Inc. + +OUI:00136E* + ID_OUI_FROM_DATABASE=Techmetro Corp. + +OUI:001373* + ID_OUI_FROM_DATABASE=BLwave Electronics Co., Ltd + +OUI:001367* + ID_OUI_FROM_DATABASE=Narayon. Co., Ltd. + +OUI:001361* + ID_OUI_FROM_DATABASE=Biospace Co., Ltd. + +OUI:001357* + ID_OUI_FROM_DATABASE=Soyal Technology Co., Ltd. + +OUI:001326* + ID_OUI_FROM_DATABASE=ECM Systems Ltd + +OUI:001325* + ID_OUI_FROM_DATABASE=Cortina Systems Inc + +OUI:00131B* + ID_OUI_FROM_DATABASE=BeCell Innovations Corp. + +OUI:00131C* + ID_OUI_FROM_DATABASE=LiteTouch, Inc. + +OUI:001309* + ID_OUI_FROM_DATABASE=Ocean Broadband Networks + +OUI:00130E* + ID_OUI_FROM_DATABASE=Focusrite Audio Engineering Limited + +OUI:0012FC* + ID_OUI_FROM_DATABASE=PLANET System Co.,LTD + +OUI:0012F6* + ID_OUI_FROM_DATABASE=MDK CO.,LTD. + +OUI:0012F1* + ID_OUI_FROM_DATABASE=IFOTEC + +OUI:00143E* + ID_OUI_FROM_DATABASE=AirLink Communications, Inc. + +OUI:001437* + ID_OUI_FROM_DATABASE=GSTeletech Co.,Ltd. + +OUI:001430* + ID_OUI_FROM_DATABASE=ViPowER, Inc + +OUI:00142B* + ID_OUI_FROM_DATABASE=Edata Communication Inc. + +OUI:001424* + ID_OUI_FROM_DATABASE=Merry Electrics CO., LTD. + +OUI:00141F* + ID_OUI_FROM_DATABASE=SunKwang Electronics Co., Ltd + +OUI:00141A* + ID_OUI_FROM_DATABASE=DEICY CORPORATION + +OUI:001413* + ID_OUI_FROM_DATABASE=Trebing & Himstedt Prozeßautomation GmbH & Co. KG + +OUI:001415* + ID_OUI_FROM_DATABASE=Intec Automation inc. + +OUI:001414* + ID_OUI_FROM_DATABASE=Jumpnode Systems LLC. + +OUI:001405* + ID_OUI_FROM_DATABASE=OpenIB, Inc. + +OUI:00140B* + ID_OUI_FROM_DATABASE=FIRST INTERNATIONAL COMPUTER, INC. + +OUI:0013FE* + ID_OUI_FROM_DATABASE=GRANDTEC ELECTRONIC CORP. + +OUI:0013F9* + ID_OUI_FROM_DATABASE=Cavera Systems + +OUI:0013F2* + ID_OUI_FROM_DATABASE=Klas Ltd + +OUI:0013EC* + ID_OUI_FROM_DATABASE=Netsnapper Technologies SARL + +OUI:0013E1* + ID_OUI_FROM_DATABASE=Iprobe AB + +OUI:0013E2* + ID_OUI_FROM_DATABASE=GeoVision Inc. + +OUI:0013D5* + ID_OUI_FROM_DATABASE=RuggedCom + +OUI:0013DC* + ID_OUI_FROM_DATABASE=IBTEK INC. + +OUI:0013D0* + ID_OUI_FROM_DATABASE=t+ Medical Ltd + +OUI:0013CB* + ID_OUI_FROM_DATABASE=Zenitel Norway AS + +OUI:0013C6* + ID_OUI_FROM_DATABASE=OpenGear, Inc + +OUI:0013C5* + ID_OUI_FROM_DATABASE=LIGHTRON FIBER-OPTIC DEVICES INC. + +OUI:0013BB* + ID_OUI_FROM_DATABASE=Smartvue Corporation + +OUI:0013BF* + ID_OUI_FROM_DATABASE=Media System Planning Corp. + +OUI:0013B5* + ID_OUI_FROM_DATABASE=Wavesat + +OUI:0013AE* + ID_OUI_FROM_DATABASE=Radiance Technologies, Inc. + +OUI:0013A2* + ID_OUI_FROM_DATABASE=MaxStream, Inc + +OUI:00139B* + ID_OUI_FROM_DATABASE=ioIMAGE Ltd. + +OUI:00139C* + ID_OUI_FROM_DATABASE=Exavera Technologies, Inc. + +OUI:001396* + ID_OUI_FROM_DATABASE=Acbel Polytech Inc. + +OUI:00138A* + ID_OUI_FROM_DATABASE=QINGDAO GOERTEK ELECTRONICS CO.,LTD. + +OUI:001389* + ID_OUI_FROM_DATABASE=Redes de Telefonía Móvil S.A. + +OUI:00149C* + ID_OUI_FROM_DATABASE=HF Company + +OUI:0014A3* + ID_OUI_FROM_DATABASE=Vitelec BV + +OUI:001497* + ID_OUI_FROM_DATABASE=ZHIYUAN Eletronics co.,ltd. + +OUI:001496* + ID_OUI_FROM_DATABASE=Phonic Corp. + +OUI:001490* + ID_OUI_FROM_DATABASE=ASP Corporation + +OUI:001489* + ID_OUI_FROM_DATABASE=B15402100 - JANDEI, S.L. + +OUI:001484* + ID_OUI_FROM_DATABASE=Cermate Technologies Inc. + +OUI:00147F* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:00147A* + ID_OUI_FROM_DATABASE=Eubus GmbH + +OUI:001473* + ID_OUI_FROM_DATABASE=Bookham Inc + +OUI:001467* + ID_OUI_FROM_DATABASE=ArrowSpan Inc. + +OUI:001460* + ID_OUI_FROM_DATABASE=Kyocera Wireless Corp. + +OUI:00145B* + ID_OUI_FROM_DATABASE=SeekerNet Inc. + +OUI:00145A* + ID_OUI_FROM_DATABASE=Neratec Solutions AG + +OUI:001459* + ID_OUI_FROM_DATABASE=Moram Co., Ltd. + +OUI:001454* + ID_OUI_FROM_DATABASE=Symwave + +OUI:001443* + ID_OUI_FROM_DATABASE=Consultronics Europe Ltd + +OUI:00144A* + ID_OUI_FROM_DATABASE=Taiwan Thick-Film Ind. Corp. + +OUI:0011C4* + ID_OUI_FROM_DATABASE=Terminales de Telecomunicacion Terrestre, S.L. + +OUI:0011C9* + ID_OUI_FROM_DATABASE=MTT Corporation + +OUI:0011BF* + ID_OUI_FROM_DATABASE=AESYS S.p.A. + +OUI:0011B8* + ID_OUI_FROM_DATABASE=Liebherr - Elektronik GmbH + +OUI:0011AC* + ID_OUI_FROM_DATABASE=Simtec Electronics + +OUI:0011B1* + ID_OUI_FROM_DATABASE=BlueExpert Technology Corp. + +OUI:0011B2* + ID_OUI_FROM_DATABASE=2001 Technology Inc. + +OUI:0011A0* + ID_OUI_FROM_DATABASE=Vtech Engineering Canada Ltd + +OUI:0011A5* + ID_OUI_FROM_DATABASE=Fortuna Electronic Corp. + +OUI:001276* + ID_OUI_FROM_DATABASE=CG Power Systems Ireland Limited + +OUI:00126F* + ID_OUI_FROM_DATABASE=Rayson Technology Co., Ltd. + +OUI:001270* + ID_OUI_FROM_DATABASE=NGES Denro Systems + +OUI:00126A* + ID_OUI_FROM_DATABASE=OPTOELECTRONICS Co., Ltd. + +OUI:001263* + ID_OUI_FROM_DATABASE=Data Voice Technologies GmbH + +OUI:00125E* + ID_OUI_FROM_DATABASE=CAEN + +OUI:00125D* + ID_OUI_FROM_DATABASE=CyberNet Inc. + +OUI:001259* + ID_OUI_FROM_DATABASE=THERMO ELECTRON KARLSRUHE + +OUI:001254* + ID_OUI_FROM_DATABASE=Spectra Technologies Holdings Company Ltd + +OUI:001253* + ID_OUI_FROM_DATABASE=AudioDev AB + +OUI:00129D* + ID_OUI_FROM_DATABASE=First International Computer do Brasil + +OUI:001291* + ID_OUI_FROM_DATABASE=KWS Computersysteme GmbH + +OUI:001296* + ID_OUI_FROM_DATABASE=Addlogix + +OUI:00128F* + ID_OUI_FROM_DATABASE=Montilio + +OUI:001282* + ID_OUI_FROM_DATABASE=Qovia + +OUI:001289* + ID_OUI_FROM_DATABASE=Advance Sterilization Products + +OUI:00127D* + ID_OUI_FROM_DATABASE=MobileAria + +OUI:0011F4* + ID_OUI_FROM_DATABASE=woori-net + +OUI:0011EE* + ID_OUI_FROM_DATABASE=Estari, Inc. + +OUI:0011ED* + ID_OUI_FROM_DATABASE=802 Global + +OUI:0011E8* + ID_OUI_FROM_DATABASE=Tixi.Com + +OUI:0011DB* + ID_OUI_FROM_DATABASE=Land-Cellular Corporation + +OUI:0011DC* + ID_OUI_FROM_DATABASE=Glunz & Jensen + +OUI:0011E1* + ID_OUI_FROM_DATABASE=Arcelik A.S + +OUI:0011CE* + ID_OUI_FROM_DATABASE=Ubisense Limited + +OUI:0011D5* + ID_OUI_FROM_DATABASE=Hangzhou Sunyard System Engineering Co.,Ltd. + +OUI:001246* + ID_OUI_FROM_DATABASE=T.O.M TECHNOLOGY INC.. + +OUI:00124D* + ID_OUI_FROM_DATABASE=Inducon BV + +OUI:001241* + ID_OUI_FROM_DATABASE=a2i marketing center + +OUI:00123A* + ID_OUI_FROM_DATABASE=Posystech Inc., Co. + +OUI:001234* + ID_OUI_FROM_DATABASE=Camille Bauer + +OUI:00122A* + ID_OUI_FROM_DATABASE=VTech Telecommunications Ltd. + +OUI:00122E* + ID_OUI_FROM_DATABASE=Signal Technology - AISD + +OUI:001233* + ID_OUI_FROM_DATABASE=JRC TOKKI Co.,Ltd. + +OUI:001199* + ID_OUI_FROM_DATABASE=2wcom Systems GmbH + +OUI:00118F* + ID_OUI_FROM_DATABASE=EUTECH INSTRUMENTS PTE. LTD. + +OUI:001183* + ID_OUI_FROM_DATABASE=Datalogic ADC, Inc. + +OUI:00117C* + ID_OUI_FROM_DATABASE=e-zy.net + +OUI:001176* + ID_OUI_FROM_DATABASE=Intellambda Systems, Inc. + +OUI:0012C0* + ID_OUI_FROM_DATABASE=HotLava Systems, Inc. + +OUI:0012B5* + ID_OUI_FROM_DATABASE=Vialta, Inc. + +OUI:0012BC* + ID_OUI_FROM_DATABASE=Echolab LLC + +OUI:0012B6* + ID_OUI_FROM_DATABASE=Santa Barbara Infrared, Inc. + +OUI:0012B0* + ID_OUI_FROM_DATABASE=Efore Oyj (Plc) + +OUI:0012A4* + ID_OUI_FROM_DATABASE=ThingMagic, LLC + +OUI:0012A9* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:0012A3* + ID_OUI_FROM_DATABASE=Trust International B.V. + +OUI:001224* + ID_OUI_FROM_DATABASE=NexQL Corporation + +OUI:001229* + ID_OUI_FROM_DATABASE=BroadEasy Technologies Co.,Ltd + +OUI:00121D* + ID_OUI_FROM_DATABASE=Netfabric Corporation + +OUI:001211* + ID_OUI_FROM_DATABASE=Protechna Herbst GmbH & Co. KG + +OUI:001218* + ID_OUI_FROM_DATABASE=ARUZE Corporation + +OUI:001205* + ID_OUI_FROM_DATABASE=Terrasat Communications, Inc. + +OUI:00120A* + ID_OUI_FROM_DATABASE=Emerson Climate Technologies GmbH + +OUI:0011FE* + ID_OUI_FROM_DATABASE=Keiyo System Research, Inc. + +OUI:0011F8* + ID_OUI_FROM_DATABASE=AIRAYA Corp + +OUI:0012EC* + ID_OUI_FROM_DATABASE=Movacolor b.v. + +OUI:0012E5* + ID_OUI_FROM_DATABASE=Time America, Inc. + +OUI:0012E0* + ID_OUI_FROM_DATABASE=Codan Limited + +OUI:0012DF* + ID_OUI_FROM_DATABASE=Novomatic AG + +OUI:0012D9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0012C6* + ID_OUI_FROM_DATABASE=TGC America, Inc + +OUI:0012CD* + ID_OUI_FROM_DATABASE=ASEM SpA + +OUI:000FE9* + ID_OUI_FROM_DATABASE=GW TECHNOLOGIES CO.,LTD. + +OUI:000FDD* + ID_OUI_FROM_DATABASE=SORDIN AB + +OUI:000FD6* + ID_OUI_FROM_DATABASE=Sarotech Co., Ltd + +OUI:002654* + ID_OUI_FROM_DATABASE=3Com Corporation + +OUI:000FD0* + ID_OUI_FROM_DATABASE=ASTRI + +OUI:000FCF* + ID_OUI_FROM_DATABASE=DataWind Research + +OUI:000FC3* + ID_OUI_FROM_DATABASE=PalmPalm Technology, Inc. + +OUI:001144* + ID_OUI_FROM_DATABASE=Assurance Technology Corp + +OUI:00113E* + ID_OUI_FROM_DATABASE=JL Corporation + +OUI:001131* + ID_OUI_FROM_DATABASE=UNATECH. CO.,LTD + +OUI:001137* + ID_OUI_FROM_DATABASE=AICHI ELECTRIC CO., LTD. + +OUI:00112D* + ID_OUI_FROM_DATABASE=iPulse Systems + +OUI:111111* + ID_OUI_FROM_DATABASE=Private + +OUI:001123* + ID_OUI_FROM_DATABASE=Appointech, Inc. + +OUI:00111D* + ID_OUI_FROM_DATABASE=Hectrix Limited + +OUI:000F6C* + ID_OUI_FROM_DATABASE=ADDI-DATA GmbH + +OUI:000F6B* + ID_OUI_FROM_DATABASE=GateWare Communications GmbH + +OUI:000F5F* + ID_OUI_FROM_DATABASE=Nicety Technologies Inc. (NTS) + +OUI:000F5A* + ID_OUI_FROM_DATABASE=Peribit Networks + +OUI:000F53* + ID_OUI_FROM_DATABASE=Solarflare Communications Inc + +OUI:000F47* + ID_OUI_FROM_DATABASE=ROBOX SPA + +OUI:000F4C* + ID_OUI_FROM_DATABASE=Elextech INC + +OUI:001170* + ID_OUI_FROM_DATABASE=GSC SRL + +OUI:001169* + ID_OUI_FROM_DATABASE=EMS Satcom + +OUI:001164* + ID_OUI_FROM_DATABASE=ACARD Technology Corp. + +OUI:00115F* + ID_OUI_FROM_DATABASE=ITX Security Co., Ltd. + +OUI:00115A* + ID_OUI_FROM_DATABASE=Ivoclar Vivadent AG + +OUI:001159* + ID_OUI_FROM_DATABASE=MATISSE NETWORKS INC + +OUI:001153* + ID_OUI_FROM_DATABASE=Trident Tek, Inc. + +OUI:001150* + ID_OUI_FROM_DATABASE=Belkin Corporation + +OUI:001151* + ID_OUI_FROM_DATABASE=Mykotronx + +OUI:00114A* + ID_OUI_FROM_DATABASE=KAYABA INDUSTRY Co,.Ltd. + +OUI:001110* + ID_OUI_FROM_DATABASE=Maxanna Technology Co., Ltd. + +OUI:001117* + ID_OUI_FROM_DATABASE=CESNET + +OUI:001104* + ID_OUI_FROM_DATABASE=TELEXY + +OUI:00110B* + ID_OUI_FROM_DATABASE=Franklin Technology Systems + +OUI:001100* + ID_OUI_FROM_DATABASE=Schneider Electric + +OUI:000FFE* + ID_OUI_FROM_DATABASE=G-PRO COMPUTER + +OUI:000FEF* + ID_OUI_FROM_DATABASE=Thales e-Transactions GmbH + +OUI:000FF0* + ID_OUI_FROM_DATABASE=Sunray Co. Ltd. + +OUI:000FF5* + ID_OUI_FROM_DATABASE=GN&S company + +OUI:000FCA* + ID_OUI_FROM_DATABASE=A-JIN TECHLINE CO, LTD + +OUI:000FBD* + ID_OUI_FROM_DATABASE=MRV Communications (Networks) LTD + +OUI:000FBE* + ID_OUI_FROM_DATABASE=e-w/you Inc. + +OUI:000FB7* + ID_OUI_FROM_DATABASE=Cavium + +OUI:000FA4* + ID_OUI_FROM_DATABASE=Sprecher Automation GmbH + +OUI:000FAB* + ID_OUI_FROM_DATABASE=Kyushu Electronics Systems Inc. + +OUI:000F9D* + ID_OUI_FROM_DATABASE=DisplayLink (UK) Ltd + +OUI:000F98* + ID_OUI_FROM_DATABASE=Avamax Co. Ltd. + +OUI:000F8B* + ID_OUI_FROM_DATABASE=Orion MultiSystems Inc + +OUI:000F8C* + ID_OUI_FROM_DATABASE=Gigawavetech Pte Ltd + +OUI:000F91* + ID_OUI_FROM_DATABASE=Aerotelecom Co.,Ltd. + +OUI:000F7E* + ID_OUI_FROM_DATABASE=Ablerex Electronics Co., LTD + +OUI:000F85* + ID_OUI_FROM_DATABASE=ADDO-Japan Corporation + +OUI:000F72* + ID_OUI_FROM_DATABASE=Sandburst + +OUI:000F79* + ID_OUI_FROM_DATABASE=Bluetooth Interest Group Inc. + +OUI:000F19* + ID_OUI_FROM_DATABASE=Boston Scientific + +OUI:000F0D* + ID_OUI_FROM_DATABASE=Hunt Electronic Co., Ltd. + +OUI:000F01* + ID_OUI_FROM_DATABASE=DIGITALKS INC + +OUI:000EFA* + ID_OUI_FROM_DATABASE=Optoway Technology Incorporation + +OUI:000EF3* + ID_OUI_FROM_DATABASE=Smarthome + +OUI:000EEE* + ID_OUI_FROM_DATABASE=Muco Industrie BV + +OUI:000EE7* + ID_OUI_FROM_DATABASE=AAC ELECTRONICS CORP. + +OUI:000F38* + ID_OUI_FROM_DATABASE=Netstar + +OUI:000F40* + ID_OUI_FROM_DATABASE=Optical Internetworking Forum + +OUI:000F33* + ID_OUI_FROM_DATABASE=DUALi Inc. + +OUI:000F2C* + ID_OUI_FROM_DATABASE=Uplogix, Inc. + +OUI:000F26* + ID_OUI_FROM_DATABASE=WorldAccxx LLC + +OUI:000F25* + ID_OUI_FROM_DATABASE=AimValley B.V. + +OUI:000F13* + ID_OUI_FROM_DATABASE=Nisca corporation + +OUI:000F14* + ID_OUI_FROM_DATABASE=Mindray Co., Ltd. + +OUI:000EE1* + ID_OUI_FROM_DATABASE=ExtremeSpeed Inc. + +OUI:000EDB* + ID_OUI_FROM_DATABASE=XiNCOM Corp. + +OUI:000EE2* + ID_OUI_FROM_DATABASE=Custom Engineering + +OUI:000ED5* + ID_OUI_FROM_DATABASE=COPAN Systems Inc. + +OUI:000EC9* + ID_OUI_FROM_DATABASE=YOKO Technology Corp. + +OUI:000ED0* + ID_OUI_FROM_DATABASE=Privaris, Inc. + +OUI:000ED7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000EC4* + ID_OUI_FROM_DATABASE=Iskra Transmission d.d. + +OUI:000EC3* + ID_OUI_FROM_DATABASE=Logic Controls, Inc. + +OUI:000EBD* + ID_OUI_FROM_DATABASE=Burdick, a Quinton Compny + +OUI:000EB1* + ID_OUI_FROM_DATABASE=Newcotech,Ltd + +OUI:000DAA* + ID_OUI_FROM_DATABASE=S.A.Tehnology co.,Ltd. + +OUI:000DA0* + ID_OUI_FROM_DATABASE=NEDAP N.V. + +OUI:000D9F* + ID_OUI_FROM_DATABASE=RF Micro Devices + +OUI:000D9A* + ID_OUI_FROM_DATABASE=INFOTEC LTD + +OUI:000D8D* + ID_OUI_FROM_DATABASE=Prosoft Technology, Inc + +OUI:000D8E* + ID_OUI_FROM_DATABASE=Koden Electronics Co., Ltd. + +OUI:000D84* + ID_OUI_FROM_DATABASE=Makus Inc. + +OUI:000D83* + ID_OUI_FROM_DATABASE=Sanmina-SCI Hungary Ltd. + +OUI:000D76* + ID_OUI_FROM_DATABASE=Hokuto Denshi Co,. Ltd. + +OUI:000D7D* + ID_OUI_FROM_DATABASE=Afco Systems + +OUI:000E51* + ID_OUI_FROM_DATABASE=tecna elettronica srl + +OUI:000E4C* + ID_OUI_FROM_DATABASE=Bermai Inc. + +OUI:000E4B* + ID_OUI_FROM_DATABASE=atrium c and i + +OUI:000E3E* + ID_OUI_FROM_DATABASE=Sun Optronics Inc + +OUI:000E45* + ID_OUI_FROM_DATABASE=Beijing Newtry Electronic Technology Ltd + +OUI:000E39* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E32* + ID_OUI_FROM_DATABASE=Kontron Medical + +OUI:000E2B* + ID_OUI_FROM_DATABASE=Safari Technologies + +OUI:000E2C* + ID_OUI_FROM_DATABASE=Netcodec co. + +OUI:000E1F* + ID_OUI_FROM_DATABASE=TCL Networks Equipment Co., Ltd. + +OUI:000E26* + ID_OUI_FROM_DATABASE=Gincom Technology Corp. + +OUI:000E1A* + ID_OUI_FROM_DATABASE=JPS Communications + +OUI:000E19* + ID_OUI_FROM_DATABASE=LogicaCMG Pty Ltd + +OUI:000E13* + ID_OUI_FROM_DATABASE=Accu-Sort Systems inc. + +OUI:000E0F* + ID_OUI_FROM_DATABASE=ERMME + +OUI:000E05* + ID_OUI_FROM_DATABASE=WIRELESS MATRIX CORP. + +OUI:000E06* + ID_OUI_FROM_DATABASE=Team Simoco Ltd + +OUI:000E0B* + ID_OUI_FROM_DATABASE=Netac Technology Co., Ltd. + +OUI:000DF8* + ID_OUI_FROM_DATABASE=ORGA Kartensysteme GmbH + +OUI:000DFF* + ID_OUI_FROM_DATABASE=CHENMING MOLD INDUSTRY CORP. + +OUI:000DEC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000DF3* + ID_OUI_FROM_DATABASE=Asmax Solutions + +OUI:000DE6* + ID_OUI_FROM_DATABASE=YOUNGBO ENGINEERING CO.,LTD + +OUI:000DE5* + ID_OUI_FROM_DATABASE=Samsung Thales + +OUI:000DE0* + ID_OUI_FROM_DATABASE=ICPDAS Co.,LTD + +OUI:000DD3* + ID_OUI_FROM_DATABASE=SAMWOO Telecommunication Co.,Ltd. + +OUI:000DD4* + ID_OUI_FROM_DATABASE=Symantec Corporation + +OUI:000DD9* + ID_OUI_FROM_DATABASE=Anton Paar GmbH + +OUI:000DCD* + ID_OUI_FROM_DATABASE=GROUPE TXCOM + +OUI:000EAA* + ID_OUI_FROM_DATABASE=Scalent Systems, Inc. + +OUI:000E9E* + ID_OUI_FROM_DATABASE=Topfield Co., Ltd + +OUI:000EA3* + ID_OUI_FROM_DATABASE=CNCR-IT CO.,LTD,HangZhou P.R.CHINA + +OUI:000EA4* + ID_OUI_FROM_DATABASE=Certance Inc. + +OUI:000E92* + ID_OUI_FROM_DATABASE=Open Telecom + +OUI:000E97* + ID_OUI_FROM_DATABASE=Ultracker Technology CO., Inc + +OUI:000E91* + ID_OUI_FROM_DATABASE=Navico Auckland Ltd + +OUI:000E8B* + ID_OUI_FROM_DATABASE=Astarte Technology Co, Ltd. + +OUI:000E84* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000D6A* + ID_OUI_FROM_DATABASE=Redwood Technologies LTD + +OUI:000D71* + ID_OUI_FROM_DATABASE=boca systems + +OUI:000D5E* + ID_OUI_FROM_DATABASE=NEC Personal Products + +OUI:000D63* + ID_OUI_FROM_DATABASE=DENT Instruments, Inc. + +OUI:000D64* + ID_OUI_FROM_DATABASE=COMAG Handels AG + +OUI:000D57* + ID_OUI_FROM_DATABASE=Fujitsu I-Network Systems Limited. + +OUI:000D52* + ID_OUI_FROM_DATABASE=Comart system + +OUI:000D51* + ID_OUI_FROM_DATABASE=DIVR Systems, Inc. + +OUI:000D47* + ID_OUI_FROM_DATABASE=Collex + +OUI:000DC1* + ID_OUI_FROM_DATABASE=SafeWeb Inc + +OUI:000DC6* + ID_OUI_FROM_DATABASE=DigiRose Technology Co., Ltd. + +OUI:000DBA* + ID_OUI_FROM_DATABASE=Océ Document Technologies GmbH + +OUI:000DB4* + ID_OUI_FROM_DATABASE=NETASQ + +OUI:000DB3* + ID_OUI_FROM_DATABASE=SDO Communication Corperation + +OUI:000DAE* + ID_OUI_FROM_DATABASE=SAMSUNG HEAVY INDUSTRIES CO., LTD. + +OUI:000DA6* + ID_OUI_FROM_DATABASE=Universal Switching Corporation + +OUI:000E78* + ID_OUI_FROM_DATABASE=Amtelco + +OUI:000E70* + ID_OUI_FROM_DATABASE=in2 Networks + +OUI:000E6B* + ID_OUI_FROM_DATABASE=Janitza electronics GmbH + +OUI:000E64* + ID_OUI_FROM_DATABASE=Elphel, Inc + +OUI:000E5D* + ID_OUI_FROM_DATABASE=Triple Play Technologies A/S + +OUI:000E5E* + ID_OUI_FROM_DATABASE=Raisecom Technology + +OUI:000E58* + ID_OUI_FROM_DATABASE=Sonos, Inc. + +OUI:000BE2* + ID_OUI_FROM_DATABASE=Lumenera Corporation + +OUI:000BE7* + ID_OUI_FROM_DATABASE=COMFLUX TECHNOLOGY INC. + +OUI:000BD6* + ID_OUI_FROM_DATABASE=Paxton Access Ltd + +OUI:000BD2* + ID_OUI_FROM_DATABASE=Remopro Technology Inc. + +OUI:000BC6* + ID_OUI_FROM_DATABASE=ISAC, Inc. + +OUI:000BCB* + ID_OUI_FROM_DATABASE=Fagor Automation , S. Coop + +OUI:000BBF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000BBA* + ID_OUI_FROM_DATABASE=Harmonic, Inc + +OUI:000BB3* + ID_OUI_FROM_DATABASE=RiT technologies Ltd. + +OUI:000C38* + ID_OUI_FROM_DATABASE=TelcoBridges Inc. + +OUI:000C3F* + ID_OUI_FROM_DATABASE=Cogent Defence & Security Networks, + +OUI:000C30* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000C26* + ID_OUI_FROM_DATABASE=Weintek Labs. Inc. + +OUI:000C2E* + ID_OUI_FROM_DATABASE=Openet information technology(shenzhen) Co., Ltd. + +OUI:000C25* + ID_OUI_FROM_DATABASE=Allied Telesis Labs, Inc. + +OUI:000C1F* + ID_OUI_FROM_DATABASE=Glimmerglass Networks + +OUI:000C24* + ID_OUI_FROM_DATABASE=ANATOR + +OUI:000C1B* + ID_OUI_FROM_DATABASE=ORACOM Co, Ltd. + +OUI:000C19* + ID_OUI_FROM_DATABASE=Telio Communications GmbH + +OUI:000C7A* + ID_OUI_FROM_DATABASE=DaTARIUS Technologies GmbH + +OUI:000C67* + ID_OUI_FROM_DATABASE=OYO ELECTRIC CO.,LTD + +OUI:000C4F* + ID_OUI_FROM_DATABASE=UDTech Japan Corporation + +OUI:000C54* + ID_OUI_FROM_DATABASE=Pedestal Networks, Inc + +OUI:000C5B* + ID_OUI_FROM_DATABASE=HANWANG TECHNOLOGY CO.,LTD + +OUI:000C60* + ID_OUI_FROM_DATABASE=ACM Systems + +OUI:000C62* + ID_OUI_FROM_DATABASE=ABB AB, Cewe-Control + +OUI:000C48* + ID_OUI_FROM_DATABASE=QoStek Corporation + +OUI:000C4D* + ID_OUI_FROM_DATABASE=Curtiss-Wright Controls Avionics & Electronics + +OUI:000C14* + ID_OUI_FROM_DATABASE=Diagnostic Instruments, Inc. + +OUI:000C07* + ID_OUI_FROM_DATABASE=Iftest AG + +OUI:000C06* + ID_OUI_FROM_DATABASE=Nixvue Systems Pte Ltd + +OUI:000C08* + ID_OUI_FROM_DATABASE=HUMEX Technologies Corp. + +OUI:000C0D* + ID_OUI_FROM_DATABASE=Communications & Power Industries / Satcom Division + +OUI:000BF5* + ID_OUI_FROM_DATABASE=Shanghai Sibo Telecom Technology Co.,Ltd + +OUI:000BFA* + ID_OUI_FROM_DATABASE=EXEMYS SRL + +OUI:000C01* + ID_OUI_FROM_DATABASE=Abatron AG + +OUI:000BEE* + ID_OUI_FROM_DATABASE=inc.jet, Incorporated + +OUI:000CE6* + ID_OUI_FROM_DATABASE=Meru Networks Inc + +OUI:000CEB* + ID_OUI_FROM_DATABASE=CNMP Networks, Inc. + +OUI:000CE2* + ID_OUI_FROM_DATABASE=Rolls-Royce + +OUI:000CEC* + ID_OUI_FROM_DATABASE=Spectracom Corp. + +OUI:000CD7* + ID_OUI_FROM_DATABASE=Nallatech Ltd + +OUI:000CDE* + ID_OUI_FROM_DATABASE=ABB STOTZ-KONTAKT GmbH + +OUI:000CD2* + ID_OUI_FROM_DATABASE=Schaffner EMV AG + +OUI:000CD8* + ID_OUI_FROM_DATABASE=M. K. Juchheim GmbH & Co + +OUI:000CC6* + ID_OUI_FROM_DATABASE=Ka-Ro electronics GmbH + +OUI:000CCB* + ID_OUI_FROM_DATABASE=Design Combus Ltd + +OUI:000CC5* + ID_OUI_FROM_DATABASE=Nextlink Co., Ltd. + +OUI:000CB3* + ID_OUI_FROM_DATABASE=ROUND Co.,Ltd. + +OUI:000CB8* + ID_OUI_FROM_DATABASE=MEDION AG + +OUI:000CBF* + ID_OUI_FROM_DATABASE=Holy Stone Ent. Co., Ltd. + +OUI:000A07* + ID_OUI_FROM_DATABASE=WebWayOne Ltd + +OUI:000CA1* + ID_OUI_FROM_DATABASE=SIGMACOM Co., LTD. + +OUI:000CA6* + ID_OUI_FROM_DATABASE=Mintera Corporation + +OUI:000CA8* + ID_OUI_FROM_DATABASE=Garuda Networks Corporation + +OUI:000CAD* + ID_OUI_FROM_DATABASE=BTU International + +OUI:000C95* + ID_OUI_FROM_DATABASE=PrimeNet + +OUI:000C9A* + ID_OUI_FROM_DATABASE=Hitech Electronics Corp. + +OUI:000C8E* + ID_OUI_FROM_DATABASE=Mentor Engineering Inc + +OUI:000C93* + ID_OUI_FROM_DATABASE=Xeline Co., Ltd. + +OUI:000C7F* + ID_OUI_FROM_DATABASE=synertronixx GmbH + +OUI:000C82* + ID_OUI_FROM_DATABASE=NETWORK TECHNOLOGIES INC + +OUI:000C87* + ID_OUI_FROM_DATABASE=AMD + +OUI:000C73* + ID_OUI_FROM_DATABASE=TELSON ELECTRONICS CO., LTD + +OUI:000D1D* + ID_OUI_FROM_DATABASE=HIGH-TEK HARNESS ENT. CO., LTD. + +OUI:000D1E* + ID_OUI_FROM_DATABASE=Control Techniques + +OUI:000D0C* + ID_OUI_FROM_DATABASE=MDI Security Systems + +OUI:000D11* + ID_OUI_FROM_DATABASE=DENTSPLY - Gendex + +OUI:000D05* + ID_OUI_FROM_DATABASE=cybernet manufacturing inc. + +OUI:000CF9* + ID_OUI_FROM_DATABASE=Xylem Water Solutions + +OUI:000CFE* + ID_OUI_FROM_DATABASE=Grand Electronic Co., Ltd + +OUI:000CF2* + ID_OUI_FROM_DATABASE=GAMESA Eólica + +OUI:000D43* + ID_OUI_FROM_DATABASE=DRS Tactical Systems Inc. + +OUI:000D37* + ID_OUI_FROM_DATABASE=WIPLUG + +OUI:000D3E* + ID_OUI_FROM_DATABASE=APLUX Communications Ltd. + +OUI:000D3D* + ID_OUI_FROM_DATABASE=Hammerhead Systems, Inc. + +OUI:000D30* + ID_OUI_FROM_DATABASE=IceFyre Semiconductor + +OUI:000D2B* + ID_OUI_FROM_DATABASE=Racal Instruments + +OUI:000D24* + ID_OUI_FROM_DATABASE=SENTEC E&E CO., LTD. + +OUI:000D18* + ID_OUI_FROM_DATABASE=Mega-Trend Electronics CO., LTD. + +OUI:000BA4* + ID_OUI_FROM_DATABASE=Shiron Satellite Communications Ltd. (1996) + +OUI:000BA9* + ID_OUI_FROM_DATABASE=CloudShield Technologies, Inc. + +OUI:000BA3* + ID_OUI_FROM_DATABASE=Siemens AG, I&S + +OUI:000B91* + ID_OUI_FROM_DATABASE=Aglaia Gesellschaft für Bildverarbeitung und Kommunikation mbH + +OUI:000B96* + ID_OUI_FROM_DATABASE=Innotrac Diagnostics Oy + +OUI:000B9D* + ID_OUI_FROM_DATABASE=TwinMOS Technologies Inc. + +OUI:000B8A* + ID_OUI_FROM_DATABASE=MITEQ Inc. + +OUI:000B7E* + ID_OUI_FROM_DATABASE=SAGINOMIYA Seisakusho Inc. + +OUI:000B83* + ID_OUI_FROM_DATABASE=DATAWATT B.V. + +OUI:000AAD* + ID_OUI_FROM_DATABASE=Stargames Corporation + +OUI:000AB2* + ID_OUI_FROM_DATABASE=Fresnel Wireless Systems + +OUI:000AB4* + ID_OUI_FROM_DATABASE=ETIC Telecommunications + +OUI:000AB9* + ID_OUI_FROM_DATABASE=Astera Technologies Corp. + +OUI:000AA1* + ID_OUI_FROM_DATABASE=V V S Limited + +OUI:000AA6* + ID_OUI_FROM_DATABASE=Hochiki Corporation + +OUI:000A8E* + ID_OUI_FROM_DATABASE=Invacom Ltd + +OUI:000A9F* + ID_OUI_FROM_DATABASE=Pannaway Technologies, Inc. + +OUI:000A99* + ID_OUI_FROM_DATABASE=Calamp Wireless Networks Inc + +OUI:000A93* + ID_OUI_FROM_DATABASE=W2 Networks, Inc. + +OUI:000A7F* + ID_OUI_FROM_DATABASE=Teradon Industries, Inc + +OUI:000A86* + ID_OUI_FROM_DATABASE=Lenze + +OUI:000A8B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B15* + ID_OUI_FROM_DATABASE=Platypus Technology + +OUI:000B10* + ID_OUI_FROM_DATABASE=11wave Technonlogy Co.,Ltd + +OUI:000B09* + ID_OUI_FROM_DATABASE=Ifoundry Systems Singapore + +OUI:000B04* + ID_OUI_FROM_DATABASE=Volktek Corporation + +OUI:000AFD* + ID_OUI_FROM_DATABASE=Kentec Electronics + +OUI:000B02* + ID_OUI_FROM_DATABASE=Dallmeier electronic + +OUI:000AF1* + ID_OUI_FROM_DATABASE=Clarity Design, Inc. + +OUI:000AF6* + ID_OUI_FROM_DATABASE=Emerson Climate Technologies Retail Solutions, Inc. + +OUI:000A0E* + ID_OUI_FROM_DATABASE=Invivo Research Inc. + +OUI:000A13* + ID_OUI_FROM_DATABASE=Honeywell Video Systems + +OUI:000A04* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:0009FD* + ID_OUI_FROM_DATABASE=Ubinetics Limited + +OUI:0009F4* + ID_OUI_FROM_DATABASE=Alcon Laboratories, Inc. + +OUI:0009E7* + ID_OUI_FROM_DATABASE=ADC Techonology + +OUI:0009EE* + ID_OUI_FROM_DATABASE=MEIKYO ELECTRIC CO.,LTD + +OUI:0009F3* + ID_OUI_FROM_DATABASE=WELL Communication Corp. + +OUI:0009E2* + ID_OUI_FROM_DATABASE=Sinbon Electronics Co., Ltd. + +OUI:0009DB* + ID_OUI_FROM_DATABASE=eSpace + +OUI:000B70* + ID_OUI_FROM_DATABASE=Load Technology, Inc. + +OUI:000B72* + ID_OUI_FROM_DATABASE=Lawo AG + +OUI:000B77* + ID_OUI_FROM_DATABASE=Cogent Systems, Inc. + +OUI:000B71* + ID_OUI_FROM_DATABASE=Litchfield Communications Inc. + +OUI:000B5F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B64* + ID_OUI_FROM_DATABASE=Kieback & Peter GmbH & Co KG + +OUI:000B5B* + ID_OUI_FROM_DATABASE=Rincon Research Corporation + +OUI:000B56* + ID_OUI_FROM_DATABASE=Cybernetics + +OUI:000B4E* + ID_OUI_FROM_DATABASE=VertexRSI, General Dynamics SatCOM Technologies, Inc. + +OUI:000B53* + ID_OUI_FROM_DATABASE=INITIUM Co., Ltd. + +OUI:000A35* + ID_OUI_FROM_DATABASE=Xilinx + +OUI:000A3A* + ID_OUI_FROM_DATABASE=J-THREE INTERNATIONAL Holding Co., Ltd. + +OUI:000A3C* + ID_OUI_FROM_DATABASE=Enerpoint Ltd. + +OUI:000A41* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000A48* + ID_OUI_FROM_DATABASE=Albatron Technology + +OUI:000A2E* + ID_OUI_FROM_DATABASE=MAPLE NETWORKS CO., LTD + +OUI:000A26* + ID_OUI_FROM_DATABASE=CEIA S.p.A. + +OUI:000A28* + ID_OUI_FROM_DATABASE=Motorola + +OUI:000A21* + ID_OUI_FROM_DATABASE=Integra Telecom Co. Ltd + +OUI:000A1A* + ID_OUI_FROM_DATABASE=Imerge Ltd + +OUI:000A15* + ID_OUI_FROM_DATABASE=Silicon Data, Inc + +OUI:000B42* + ID_OUI_FROM_DATABASE=commax Co., Ltd. + +OUI:000B47* + ID_OUI_FROM_DATABASE=Advanced Energy + +OUI:000B36* + ID_OUI_FROM_DATABASE=Productivity Systems, Inc. + +OUI:000B28* + ID_OUI_FROM_DATABASE=Quatech Inc. + +OUI:000B2F* + ID_OUI_FROM_DATABASE=bplan GmbH + +OUI:000B1C* + ID_OUI_FROM_DATABASE=SIBCO bv + +OUI:000B21* + ID_OUI_FROM_DATABASE=G-Star Communications Inc. + +OUI:000B23* + ID_OUI_FROM_DATABASE=Siemens Subscriber Networks + +OUI:000A7A* + ID_OUI_FROM_DATABASE=Kyoritsu Electric Co., Ltd. + +OUI:000A6E* + ID_OUI_FROM_DATABASE=Harmonic, Inc + +OUI:000A73* + ID_OUI_FROM_DATABASE=Scientific Atlanta + +OUI:000A60* + ID_OUI_FROM_DATABASE=Autostar Technology Pte Ltd + +OUI:000A67* + ID_OUI_FROM_DATABASE=OngCorp + +OUI:000A6C* + ID_OUI_FROM_DATABASE=Walchem Corporation + +OUI:000A5B* + ID_OUI_FROM_DATABASE=Power-One as + +OUI:000A59* + ID_OUI_FROM_DATABASE=HW server + +OUI:000A54* + ID_OUI_FROM_DATABASE=Laguna Hills, Inc. + +OUI:000A4D* + ID_OUI_FROM_DATABASE=Noritz Corporation + +OUI:000ADF* + ID_OUI_FROM_DATABASE=Gennum Corporation + +OUI:000AD8* + ID_OUI_FROM_DATABASE=IPCserv Technology Corp. + +OUI:000ACC* + ID_OUI_FROM_DATABASE=Winnow Networks, Inc. + +OUI:000AD1* + ID_OUI_FROM_DATABASE=MWS + +OUI:000AD3* + ID_OUI_FROM_DATABASE=INITECH Co., Ltd + +OUI:000AC0* + ID_OUI_FROM_DATABASE=Fuyoh Video Industry CO., LTD. + +OUI:000AC5* + ID_OUI_FROM_DATABASE=Color Kinetics + +OUI:00097B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000982* + ID_OUI_FROM_DATABASE=Loewe Opta GmbH + +OUI:000976* + ID_OUI_FROM_DATABASE=Datasoft ISDN Systems GmbH + +OUI:000969* + ID_OUI_FROM_DATABASE=Meret Optical Communications + +OUI:000963* + ID_OUI_FROM_DATABASE=Dominion Lasercom Inc. + +OUI:00096A* + ID_OUI_FROM_DATABASE=Cloverleaf Communications Inc. + +OUI:00096F* + ID_OUI_FROM_DATABASE=Beijing Zhongqing Elegant Tech. Corp.,Limited + +OUI:00095D* + ID_OUI_FROM_DATABASE=Dialogue Technology Corp. + +OUI:00095F* + ID_OUI_FROM_DATABASE=Telebyte, Inc. + +OUI:000958* + ID_OUI_FROM_DATABASE=INTELNET S.A. + +OUI:00094C* + ID_OUI_FROM_DATABASE=Communication Weaver Co.,Ltd. + +OUI:000951* + ID_OUI_FROM_DATABASE=Apogee Imaging Systems + +OUI:00094B* + ID_OUI_FROM_DATABASE=FillFactory NV + +OUI:0009AE* + ID_OUI_FROM_DATABASE=OKANO ELECTRIC CO.,LTD + +OUI:0009AD* + ID_OUI_FROM_DATABASE=HYUNDAI SYSCOMM, INC. + +OUI:0009B4* + ID_OUI_FROM_DATABASE=KISAN TELECOM CO., LTD. + +OUI:0009A8* + ID_OUI_FROM_DATABASE=Eastmode Pte Ltd + +OUI:00099B* + ID_OUI_FROM_DATABASE=Western Telematic Inc. + +OUI:00099C* + ID_OUI_FROM_DATABASE=Naval Research Laboratory + +OUI:0009A1* + ID_OUI_FROM_DATABASE=Telewise Communications, Inc. + +OUI:000995* + ID_OUI_FROM_DATABASE=Castle Technology Ltd + +OUI:000989* + ID_OUI_FROM_DATABASE=VividLogic Inc. + +OUI:00098E* + ID_OUI_FROM_DATABASE=ipcas GmbH + +OUI:00097C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0009C8* + ID_OUI_FROM_DATABASE=SINAGAWA TSUSHIN KEISOU SERVICE + +OUI:0009CF* + ID_OUI_FROM_DATABASE=iAd GmbH + +OUI:0009D4* + ID_OUI_FROM_DATABASE=Transtech Networks + +OUI:0009BB* + ID_OUI_FROM_DATABASE=MathStar, Inc. + +OUI:0009C0* + ID_OUI_FROM_DATABASE=6WIND + +OUI:000807* + ID_OUI_FROM_DATABASE=Access Devices Limited + +OUI:000801* + ID_OUI_FROM_DATABASE=HighSpeed Surfing Inc. + +OUI:000808* + ID_OUI_FROM_DATABASE=PPT Vision, Inc. + +OUI:0007F7* + ID_OUI_FROM_DATABASE=Galtronics + +OUI:0007FE* + ID_OUI_FROM_DATABASE=Rigaku Corporation + +OUI:0007F8* + ID_OUI_FROM_DATABASE=ITDevices, Inc. + +OUI:0007EB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0007F1* + ID_OUI_FROM_DATABASE=TeraBurst Networks Inc. + +OUI:0007E5* + ID_OUI_FROM_DATABASE=Coup Corporation + +OUI:0007DF* + ID_OUI_FROM_DATABASE=Vbrick Systems Inc. + +OUI:0007DE* + ID_OUI_FROM_DATABASE=eCopilt AB + +OUI:0007CF* + ID_OUI_FROM_DATABASE=Anoto AB + +OUI:0007D2* + ID_OUI_FROM_DATABASE=Logopak Systeme GmbH & Co. KG + +OUI:0008AA* + ID_OUI_FROM_DATABASE=KARAM + +OUI:0008A4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000898* + ID_OUI_FROM_DATABASE=Gigabit Optics Corporation + +OUI:00089D* + ID_OUI_FROM_DATABASE=UHD-Elektronik + +OUI:000890* + ID_OUI_FROM_DATABASE=AVILINKS SA + +OUI:000889* + ID_OUI_FROM_DATABASE=Echostar Technologies Corp + +OUI:000884* + ID_OUI_FROM_DATABASE=Index Braille AB + +OUI:000877* + ID_OUI_FROM_DATABASE=Liebert-Hiross Spa + +OUI:08006B* + ID_OUI_FROM_DATABASE=ACCEL TECHNOLOGIES INC. + +OUI:000871* + ID_OUI_FROM_DATABASE=NORTHDATA Co., Ltd. + +OUI:00087D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000876* + ID_OUI_FROM_DATABASE=SDSystem + +OUI:0008E6* + ID_OUI_FROM_DATABASE=Littlefeet + +OUI:0008D9* + ID_OUI_FROM_DATABASE=Mitadenshi Co.,LTD + +OUI:0008D4* + ID_OUI_FROM_DATABASE=IneoQuest Technologies, Inc + +OUI:0008CD* + ID_OUI_FROM_DATABASE=With-Net Inc + +OUI:0008D3* + ID_OUI_FROM_DATABASE=Hercules Technologies S.A.S. + +OUI:0008C3* + ID_OUI_FROM_DATABASE=Contex A/S + +OUI:0008BD* + ID_OUI_FROM_DATABASE=TEPG-US + +OUI:0008BC* + ID_OUI_FROM_DATABASE=Ilevo AB + +OUI:0008B7* + ID_OUI_FROM_DATABASE=HIT Incorporated + +OUI:0008B0* + ID_OUI_FROM_DATABASE=BKtel communications GmbH + +OUI:00086A* + ID_OUI_FROM_DATABASE=Securiton Gmbh + +OUI:000864* + ID_OUI_FROM_DATABASE=Fasy S.p.A. + +OUI:00085E* + ID_OUI_FROM_DATABASE=PCO AG + +OUI:000851* + ID_OUI_FROM_DATABASE=Canadian Bank Note Company, Ltd. + +OUI:000852* + ID_OUI_FROM_DATABASE=Davolink Co. Inc. + +OUI:000857* + ID_OUI_FROM_DATABASE=Polaris Networks, Inc. + +OUI:00081B* + ID_OUI_FROM_DATABASE=Windigo Systems + +OUI:000822* + ID_OUI_FROM_DATABASE=InPro Comm + +OUI:00082E* + ID_OUI_FROM_DATABASE=Multitone Electronics PLC + +OUI:00081C* + ID_OUI_FROM_DATABASE=@pos.com + +OUI:000828* + ID_OUI_FROM_DATABASE=Koei Engineering Ltd. + +OUI:000816* + ID_OUI_FROM_DATABASE=Bluelon ApS + +OUI:000815* + ID_OUI_FROM_DATABASE=CATS Co., Ltd. + +OUI:00091A* + ID_OUI_FROM_DATABASE=Macat Optics & Electronics Co., Ltd. + +OUI:000919* + ID_OUI_FROM_DATABASE=MDS Gateways + +OUI:000913* + ID_OUI_FROM_DATABASE=SystemK Corporation + +OUI:00090C* + ID_OUI_FROM_DATABASE=Mayekawa Mfg. Co. Ltd. + +OUI:000907* + ID_OUI_FROM_DATABASE=Chrysalis Development + +OUI:000900* + ID_OUI_FROM_DATABASE=TMT + +OUI:0008F8* + ID_OUI_FROM_DATABASE=UTC CCS + +OUI:0008F3* + ID_OUI_FROM_DATABASE=WANY + +OUI:0008EC* + ID_OUI_FROM_DATABASE=Optical Zonu Corporation + +OUI:0008E0* + ID_OUI_FROM_DATABASE=ATO Technology Ltd. + +OUI:0008E5* + ID_OUI_FROM_DATABASE=IDK Corporation + +OUI:000945* + ID_OUI_FROM_DATABASE=Palmmicro Communications Inc + +OUI:00093E* + ID_OUI_FROM_DATABASE=C&I Technologies + +OUI:000932* + ID_OUI_FROM_DATABASE=Omnilux + +OUI:000939* + ID_OUI_FROM_DATABASE=ShibaSoku Co.,Ltd. + +OUI:000926* + ID_OUI_FROM_DATABASE=YODA COMMUNICATIONS, INC. + +OUI:00092B* + ID_OUI_FROM_DATABASE=iQstor Networks, Inc. + +OUI:00092C* + ID_OUI_FROM_DATABASE=Hitpoint Inc. + +OUI:00091F* + ID_OUI_FROM_DATABASE=A&D Co., Ltd. + +OUI:000751* + ID_OUI_FROM_DATABASE=m-u-t AG + +OUI:000750* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000746* + ID_OUI_FROM_DATABASE=TURCK, Inc. + +OUI:00074A* + ID_OUI_FROM_DATABASE=Carl Valentin GmbH + +OUI:00073A* + ID_OUI_FROM_DATABASE=Inventel Systemes + +OUI:000734* + ID_OUI_FROM_DATABASE=ONStor, Inc. + +OUI:000739* + ID_OUI_FROM_DATABASE=Scotty Group Austria Gmbh + +OUI:00072D* + ID_OUI_FROM_DATABASE=CNSystems + +OUI:000727* + ID_OUI_FROM_DATABASE=Zi Corporation (HK) Ltd. + +OUI:000717* + ID_OUI_FROM_DATABASE=Wieland Electric GmbH + +OUI:00071E* + ID_OUI_FROM_DATABASE=Tri-M Engineering / Nupak Dev. Corp. + +OUI:000723* + ID_OUI_FROM_DATABASE=ELCON Systemtechnik GmbH + +OUI:00071D* + ID_OUI_FROM_DATABASE=Satelsa Sistemas Y Aplicaciones De Telecomunicaciones, S.A. + +OUI:000632* + ID_OUI_FROM_DATABASE=Mesco Engineering GmbH + +OUI:000625* + ID_OUI_FROM_DATABASE=The Linksys Group, Inc. + +OUI:00062C* + ID_OUI_FROM_DATABASE=Bivio Networks + +OUI:000624* + ID_OUI_FROM_DATABASE=Gentner Communications Corp. + +OUI:00061B* + ID_OUI_FROM_DATABASE=Notebook Development Lab. Lenovo Japan Ltd. + +OUI:000622* + ID_OUI_FROM_DATABASE=Chung Fu Chen Yeh Enterprise Corp. + +OUI:00061C* + ID_OUI_FROM_DATABASE=Hoshino Metal Industries, Ltd. + +OUI:000621* + ID_OUI_FROM_DATABASE=Hinox, Co., Ltd. + +OUI:00060B* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:000611* + ID_OUI_FROM_DATABASE=Zeus Wireless, Inc. + +OUI:000615* + ID_OUI_FROM_DATABASE=Kimoto Electric Co., Ltd. + +OUI:000605* + ID_OUI_FROM_DATABASE=Inncom International, Inc. + +OUI:0005E3* + ID_OUI_FROM_DATABASE=LightSand Communications, Inc. + +OUI:0005EF* + ID_OUI_FROM_DATABASE=ADOIR Digital Technology + +OUI:0005F6* + ID_OUI_FROM_DATABASE=Young Chang Co. Ltd. + +OUI:0005E9* + ID_OUI_FROM_DATABASE=Unicess Network, Inc. + +OUI:0005F0* + ID_OUI_FROM_DATABASE=SATEC + +OUI:0005FC* + ID_OUI_FROM_DATABASE=Schenck Pegasus Corp. + +OUI:0005E0* + ID_OUI_FROM_DATABASE=Empirix Corp. + +OUI:0005D6* + ID_OUI_FROM_DATABASE=L-3 Linkabit + +OUI:0005C4* + ID_OUI_FROM_DATABASE=Telect, Inc. + +OUI:0005D0* + ID_OUI_FROM_DATABASE=Solinet Systems + +OUI:0005CA* + ID_OUI_FROM_DATABASE=Hitron Technology, Inc. + +OUI:0005BD* + ID_OUI_FROM_DATABASE=ROAX BV + +OUI:0005BE* + ID_OUI_FROM_DATABASE=Kongsberg Seatex AS + +OUI:0005C3* + ID_OUI_FROM_DATABASE=Pacific Instruments, Inc. + +OUI:00059D* + ID_OUI_FROM_DATABASE=Daniel Computing Systems, Inc. + +OUI:000796* + ID_OUI_FROM_DATABASE=LSI Systems, Inc. + +OUI:000790* + ID_OUI_FROM_DATABASE=Tri-M Technologies (s) Limited + +OUI:000784* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000789* + ID_OUI_FROM_DATABASE=DONGWON SYSTEMS + +OUI:000783* + ID_OUI_FROM_DATABASE=SynCom Network, Inc. + +OUI:00078A* + ID_OUI_FROM_DATABASE=Mentor Data System Inc. + +OUI:00077A* + ID_OUI_FROM_DATABASE=Infoware System Co., Ltd. + +OUI:00076D* + ID_OUI_FROM_DATABASE=Flexlight Networks + +OUI:000769* + ID_OUI_FROM_DATABASE=Italiana Macchi SpA + +OUI:000773* + ID_OUI_FROM_DATABASE=Ascom Powerline Communications Ltd. + +OUI:00075D* + ID_OUI_FROM_DATABASE=Celleritas Inc. + +OUI:000763* + ID_OUI_FROM_DATABASE=Sunniwell Cyber Tech. Co., Ltd. + +OUI:000756* + ID_OUI_FROM_DATABASE=Juyoung Telecom + +OUI:0007C9* + ID_OUI_FROM_DATABASE=Technol Seven Co., Ltd. + +OUI:00047B* + ID_OUI_FROM_DATABASE=Schlumberger + +OUI:0007C3* + ID_OUI_FROM_DATABASE=Thomson + +OUI:0007BD* + ID_OUI_FROM_DATABASE=Radionet Ltd. + +OUI:0007B0* + ID_OUI_FROM_DATABASE=Office Details, Inc. + +OUI:0007B7* + ID_OUI_FROM_DATABASE=Samurai Ind. Prods Eletronicos Ltda + +OUI:0007B6* + ID_OUI_FROM_DATABASE=Telecom Technology Ltd. + +OUI:0007A3* + ID_OUI_FROM_DATABASE=Ositis Software, Inc. + +OUI:0007A9* + ID_OUI_FROM_DATABASE=Novasonics + +OUI:0007AC* + ID_OUI_FROM_DATABASE=Eolring + +OUI:00079C* + ID_OUI_FROM_DATABASE=Golden Electronics Technology Co., Ltd. + +OUI:0006AB* + ID_OUI_FROM_DATABASE=W-Link Systems, Inc. + +OUI:0006A5* + ID_OUI_FROM_DATABASE=PINON Corp. + +OUI:0006A1* + ID_OUI_FROM_DATABASE=Celsian Technologies, Inc. + +OUI:000694* + ID_OUI_FROM_DATABASE=Mobillian Corporation + +OUI:00069B* + ID_OUI_FROM_DATABASE=AVT Audio Video Technologies GmbH + +OUI:00068E* + ID_OUI_FROM_DATABASE=HID Corporation + +OUI:000688* + ID_OUI_FROM_DATABASE=Telways Communication Co., Ltd. + +OUI:000682* + ID_OUI_FROM_DATABASE=Convedia + +OUI:000681* + ID_OUI_FROM_DATABASE=Goepel Electronic GmbH + +OUI:000655* + ID_OUI_FROM_DATABASE=Yipee, Inc. + +OUI:00D05F* + ID_OUI_FROM_DATABASE=VALCOM, INC. + +OUI:000674* + ID_OUI_FROM_DATABASE=Spectrum Control, Inc. + +OUI:000661* + ID_OUI_FROM_DATABASE=NIA Home Technologies Corp. + +OUI:000668* + ID_OUI_FROM_DATABASE=Vicon Industries Inc. + +OUI:000667* + ID_OUI_FROM_DATABASE=Tripp Lite + +OUI:00066E* + ID_OUI_FROM_DATABASE=Delta Electronics, Inc. + +OUI:00064E* + ID_OUI_FROM_DATABASE=Broad Net Technology Inc. + +OUI:00064F* + ID_OUI_FROM_DATABASE=PRO-NETS Technology Corporation + +OUI:000642* + ID_OUI_FROM_DATABASE=Genetel Systems Inc. + +OUI:00063E* + ID_OUI_FROM_DATABASE=Opthos Inc. + +OUI:000648* + ID_OUI_FROM_DATABASE=Seedsware, Inc. + +OUI:000638* + ID_OUI_FROM_DATABASE=Sungjin C&C Co., Ltd. + +OUI:00070B* + ID_OUI_FROM_DATABASE=Novabase SGPS, SA + +OUI:000710* + ID_OUI_FROM_DATABASE=Adax, Inc. + +OUI:000700* + ID_OUI_FROM_DATABASE=Zettamedia Korea + +OUI:0006F9* + ID_OUI_FROM_DATABASE=Mitsui Zosen Systems Research Inc. + +OUI:000703* + ID_OUI_FROM_DATABASE=CSEE Transport + +OUI:000706* + ID_OUI_FROM_DATABASE=Sanritz Corporation + +OUI:0006E8* + ID_OUI_FROM_DATABASE=Optical Network Testing, Inc. + +OUI:0006EE* + ID_OUI_FROM_DATABASE=Shenyang Neu-era Information & Technology Stock Co., Ltd + +OUI:0006E2* + ID_OUI_FROM_DATABASE=Ceemax Technology Co., Ltd. + +OUI:0006D8* + ID_OUI_FROM_DATABASE=Maple Optical Systems + +OUI:0006D4* + ID_OUI_FROM_DATABASE=Interactive Objects, Inc. + +OUI:0006CE* + ID_OUI_FROM_DATABASE=DATENO + +OUI:0006B7* + ID_OUI_FROM_DATABASE=TELEM GmbH + +OUI:0006BE* + ID_OUI_FROM_DATABASE=Baumer Optronic GmbH + +OUI:0006B8* + ID_OUI_FROM_DATABASE=Bandspeed Pty Ltd + +OUI:0006BD* + ID_OUI_FROM_DATABASE=BNTECHNOLOGY Co., Ltd. + +OUI:0006C2* + ID_OUI_FROM_DATABASE=Smartmatic Corporation + +OUI:0006C7* + ID_OUI_FROM_DATABASE=RFNET Technologies Pte Ltd (S) + +OUI:0006B1* + ID_OUI_FROM_DATABASE=Sonicwall + +OUI:000475* + ID_OUI_FROM_DATABASE=3 Com Corporation + +OUI:00046F* + ID_OUI_FROM_DATABASE=Digitel S/A Industria Eletronica + +OUI:000468* + ID_OUI_FROM_DATABASE=Vivity, Inc. + +OUI:00045C* + ID_OUI_FROM_DATABASE=Mobiwave Pte Ltd + +OUI:000463* + ID_OUI_FROM_DATABASE=Bosch Security Systems + +OUI:000462* + ID_OUI_FROM_DATABASE=DAKOS Data & Communication Co., Ltd. + +OUI:000455* + ID_OUI_FROM_DATABASE=ANTARA.net + +OUI:000456* + ID_OUI_FROM_DATABASE=Cambium Networks Limited + +OUI:000450* + ID_OUI_FROM_DATABASE=DMD Computers SRL + +OUI:000446* + ID_OUI_FROM_DATABASE=CYZENTECH Co., Ltd. + +OUI:00044B* + ID_OUI_FROM_DATABASE=NVIDIA + +OUI:0005AD* + ID_OUI_FROM_DATABASE=Topspin Communications, Inc. + +OUI:0005B1* + ID_OUI_FROM_DATABASE=ASB Technology BV + +OUI:0005B7* + ID_OUI_FROM_DATABASE=Arbor Technology Corp. + +OUI:0005A3* + ID_OUI_FROM_DATABASE=QEI, Inc. + +OUI:000597* + ID_OUI_FROM_DATABASE=Eagle Traffic Control Systems + +OUI:000591* + ID_OUI_FROM_DATABASE=Active Silicon Ltd + +OUI:00058A* + ID_OUI_FROM_DATABASE=Netcom Co., Ltd. + +OUI:000590* + ID_OUI_FROM_DATABASE=Swissvoice Ltd. + +OUI:00057E* + ID_OUI_FROM_DATABASE=Eckelmann Steuerungstechnik GmbH + +OUI:000578* + ID_OUI_FROM_DATABASE=Private + +OUI:000584* + ID_OUI_FROM_DATABASE=AbsoluteValue Systems, Inc. + +OUI:00052E* + ID_OUI_FROM_DATABASE=Cinta Networks + +OUI:00053A* + ID_OUI_FROM_DATABASE=Willowglen Services Pte Ltd + +OUI:000528* + ID_OUI_FROM_DATABASE=New Focus, Inc. + +OUI:000527* + ID_OUI_FROM_DATABASE=SJ Tek Co. Ltd + +OUI:000521* + ID_OUI_FROM_DATABASE=Control Microsystems + +OUI:000515* + ID_OUI_FROM_DATABASE=Nuark Co., Ltd. + +OUI:00051B* + ID_OUI_FROM_DATABASE=Magic Control Technology Corporation + +OUI:000511* + ID_OUI_FROM_DATABASE=Complementary Technologies Ltd + +OUI:00050B* + ID_OUI_FROM_DATABASE=SICOM Systems, Inc. + +OUI:000501* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000505* + ID_OUI_FROM_DATABASE=Systems Integration Solutions, Inc. + +OUI:000504* + ID_OUI_FROM_DATABASE=Naray Information & Communication Enterprise + +OUI:0004FB* + ID_OUI_FROM_DATABASE=Commtech, Inc. + +OUI:000574* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000567* + ID_OUI_FROM_DATABASE=Etymonic Design, Inc. + +OUI:00056E* + ID_OUI_FROM_DATABASE=National Enhance Technology, Inc. + +OUI:00056D* + ID_OUI_FROM_DATABASE=Pacific Corporation + +OUI:000561* + ID_OUI_FROM_DATABASE=nac Image Technology, Inc. + +OUI:00055B* + ID_OUI_FROM_DATABASE=Charles Industries, Ltd. + +OUI:000554* + ID_OUI_FROM_DATABASE=Rangestar Wireless + +OUI:000555* + ID_OUI_FROM_DATABASE=Japan Cash Machine Co., Ltd. + +OUI:000547* + ID_OUI_FROM_DATABASE=Starent Networks + +OUI:00054E* + ID_OUI_FROM_DATABASE=Philips + +OUI:000540* + ID_OUI_FROM_DATABASE=FAST Corporation + +OUI:000541* + ID_OUI_FROM_DATABASE=Advanced Systems Co., Ltd. + +OUI:000534* + ID_OUI_FROM_DATABASE=Northstar Engineering Ltd. + +OUI:0004F4* + ID_OUI_FROM_DATABASE=Infinite Electronics Inc. + +OUI:0004EE* + ID_OUI_FROM_DATABASE=Lincoln Electric Company + +OUI:0004E8* + ID_OUI_FROM_DATABASE=IER, Inc. + +OUI:008086* + ID_OUI_FROM_DATABASE=COMPUTER GENERATION INC. + +OUI:0004DE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0004E4* + ID_OUI_FROM_DATABASE=Daeryung Ind., Inc. + +OUI:0004D7* + ID_OUI_FROM_DATABASE=Omitec Instrumentation Ltd. + +OUI:0004D8* + ID_OUI_FROM_DATABASE=IPWireless, Inc. + +OUI:0004D2* + ID_OUI_FROM_DATABASE=Adcon Telemetry GmbH + +OUI:0004D1* + ID_OUI_FROM_DATABASE=Drew Technologies, Inc. + +OUI:0004CB* + ID_OUI_FROM_DATABASE=Tdsoft Communication, Ltd. + +OUI:0004BF* + ID_OUI_FROM_DATABASE=VersaLogic Corp. + +OUI:0004C5* + ID_OUI_FROM_DATABASE=ASE Technologies, USA + +OUI:00043F* + ID_OUI_FROM_DATABASE=ESTeem Wireless Modems, Inc + +OUI:000439* + ID_OUI_FROM_DATABASE=Rosco Entertainment Technology, Inc. + +OUI:000433* + ID_OUI_FROM_DATABASE=Cyberboard A/S + +OUI:00042C* + ID_OUI_FROM_DATABASE=Minet, Inc. + +OUI:000427* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000426* + ID_OUI_FROM_DATABASE=Autosys + +OUI:000420* + ID_OUI_FROM_DATABASE=Slim Devices, Inc. + +OUI:000413* + ID_OUI_FROM_DATABASE=SNOM Technology AG + +OUI:000418* + ID_OUI_FROM_DATABASE=Teltronic S.A.U. + +OUI:000412* + ID_OUI_FROM_DATABASE=WaveSmith Networks, Inc. + +OUI:00040C* + ID_OUI_FROM_DATABASE=Kanno Works, Ltd. + +OUI:000370* + ID_OUI_FROM_DATABASE=NXTV, Inc. + +OUI:000405* + ID_OUI_FROM_DATABASE=ACN Technologies + +OUI:000406* + ID_OUI_FROM_DATABASE=Fa. Metabox AG + +OUI:0003FB* + ID_OUI_FROM_DATABASE=ENEGATE Co.,Ltd. + +OUI:0003FC* + ID_OUI_FROM_DATABASE=Intertex Data AB + +OUI:0003EF* + ID_OUI_FROM_DATABASE=Oneline AG + +OUI:0003F6* + ID_OUI_FROM_DATABASE=Allegro Networks, Inc. + +OUI:0003EA* + ID_OUI_FROM_DATABASE=Mega System Technologies, Inc. + +OUI:0003E9* + ID_OUI_FROM_DATABASE=Akara Canada, Inc. + +OUI:0003E4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0003D8* + ID_OUI_FROM_DATABASE=iMPath Networks, Inc. + +OUI:0003D5* + ID_OUI_FROM_DATABASE=Advanced Communications Co., Ltd. + +OUI:0003CC* + ID_OUI_FROM_DATABASE=Momentum Computer, Inc. + +OUI:0003D1* + ID_OUI_FROM_DATABASE=Takaya Corporation + +OUI:0003C5* + ID_OUI_FROM_DATABASE=Mobotix AG + +OUI:0003BE* + ID_OUI_FROM_DATABASE=Netility + +OUI:0003B9* + ID_OUI_FROM_DATABASE=Hualong Telecom Co., Ltd. + +OUI:0003B7* + ID_OUI_FROM_DATABASE=ZACCESS Systems + +OUI:0003B3* + ID_OUI_FROM_DATABASE=IA Link Systems Co., Ltd. + +OUI:0003A7* + ID_OUI_FROM_DATABASE=Unixtar Technology, Inc. + +OUI:0003AE* + ID_OUI_FROM_DATABASE=Allied Advanced Manufacturing Pte, Ltd. + +OUI:0003A0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000398* + ID_OUI_FROM_DATABASE=WISI + +OUI:00039B* + ID_OUI_FROM_DATABASE=NetChip Technology, Inc. + +OUI:000394* + ID_OUI_FROM_DATABASE=Connect One + +OUI:00038D* + ID_OUI_FROM_DATABASE=PCS Revenue Control Systems, Inc. + +OUI:000385* + ID_OUI_FROM_DATABASE=Actelis Networks, Inc. + +OUI:000388* + ID_OUI_FROM_DATABASE=Fastfame Technology Co., Ltd. + +OUI:00037F* + ID_OUI_FROM_DATABASE=Atheros Communications, Inc. + +OUI:0004B8* + ID_OUI_FROM_DATABASE=Kumahira Co., Ltd. + +OUI:0004B2* + ID_OUI_FROM_DATABASE=ESSEGI SRL + +OUI:0004AE* + ID_OUI_FROM_DATABASE=Sullair Corporation + +OUI:0004AB* + ID_OUI_FROM_DATABASE=Comverse Network Systems, Inc. + +OUI:00049F* + ID_OUI_FROM_DATABASE=Freescale Semiconductor + +OUI:0004A4* + ID_OUI_FROM_DATABASE=NetEnabled, Inc. + +OUI:00049E* + ID_OUI_FROM_DATABASE=Wirelink Co., Ltd. + +OUI:000498* + ID_OUI_FROM_DATABASE=Mahi Networks + +OUI:000491* + ID_OUI_FROM_DATABASE=Technovision, Inc. + +OUI:00048C* + ID_OUI_FROM_DATABASE=Nayna Networks, Inc. + +OUI:000492* + ID_OUI_FROM_DATABASE=Hive Internet, Ltd. + +OUI:000485* + ID_OUI_FROM_DATABASE=PicoLight + +OUI:000307* + ID_OUI_FROM_DATABASE=Secure Works, Inc. + +OUI:000300* + ID_OUI_FROM_DATABASE=Barracuda Networks, Inc. + +OUI:0002F8* + ID_OUI_FROM_DATABASE=SEAKR Engineering, Inc. + +OUI:00D024* + ID_OUI_FROM_DATABASE=Cognex Corporation + +OUI:0002F4* + ID_OUI_FROM_DATABASE=PCTEL, Inc. + +OUI:0002FB* + ID_OUI_FROM_DATABASE=Baumuller Aulugen-Systemtechnik GmbH + +OUI:0002E9* + ID_OUI_FROM_DATABASE=CS Systemes De Securite - C3S + +OUI:0002DD* + ID_OUI_FROM_DATABASE=Bromax Communications, Ltd. + +OUI:0002E2* + ID_OUI_FROM_DATABASE=NDC Infared Engineering + +OUI:0002DA* + ID_OUI_FROM_DATABASE=ExiO Communications, Inc. + +OUI:0002D6* + ID_OUI_FROM_DATABASE=NICE Systems + +OUI:0002CA* + ID_OUI_FROM_DATABASE=EndPoints, Inc. + +OUI:0002CF* + ID_OUI_FROM_DATABASE=ZyGate Communications, Inc. + +OUI:0001CD* + ID_OUI_FROM_DATABASE=ARtem + +OUI:0001D2* + ID_OUI_FROM_DATABASE=inXtron, Inc. + +OUI:0001C9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0001C7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0001C2* + ID_OUI_FROM_DATABASE=ARK Research Corp. + +OUI:0001BE* + ID_OUI_FROM_DATABASE=Gigalink Co., Ltd. + +OUI:0001BC* + ID_OUI_FROM_DATABASE=Brains Corporation + +OUI:0001AC* + ID_OUI_FROM_DATABASE=Sitara Networks, Inc. + +OUI:0001A9* + ID_OUI_FROM_DATABASE=BMW AG + +OUI:0001B0* + ID_OUI_FROM_DATABASE=Fulltek Technology Co., Ltd. + +OUI:000179* + ID_OUI_FROM_DATABASE=WIRELESS TECHNOLOGY, INC. + +OUI:000185* + ID_OUI_FROM_DATABASE=Hitachi Aloka Medical, Ltd. + +OUI:00018C* + ID_OUI_FROM_DATABASE=Mega Vision + +OUI:000192* + ID_OUI_FROM_DATABASE=Texas Digital Systems + +OUI:00019E* + ID_OUI_FROM_DATABASE=ESS Technology, Inc. + +OUI:001095* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:00025A* + ID_OUI_FROM_DATABASE=Catena Networks + +OUI:000271* + ID_OUI_FROM_DATABASE=Zhone Technologies + +OUI:00026C* + ID_OUI_FROM_DATABASE=Philips CFT + +OUI:00026A* + ID_OUI_FROM_DATABASE=Cocess Telecom Co., Ltd. + +OUI:000266* + ID_OUI_FROM_DATABASE=Thermalogic Corporation + +OUI:00025F* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000256* + ID_OUI_FROM_DATABASE=Alpha Processor, Inc. + +OUI:000251* + ID_OUI_FROM_DATABASE=Soma Networks, Inc. + +OUI:00024A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00024D* + ID_OUI_FROM_DATABASE=Mannesman Dematic Colby Pty. Ltd. + +OUI:000245* + ID_OUI_FROM_DATABASE=Lampus Co, Ltd. + +OUI:00023E* + ID_OUI_FROM_DATABASE=Selta Telematica S.p.a + +OUI:00023B* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:000237* + ID_OUI_FROM_DATABASE=Cosmo Research Corp. + +OUI:000234* + ID_OUI_FROM_DATABASE=Imperial Technology, Inc. + +OUI:000228* + ID_OUI_FROM_DATABASE=Necsom, Ltd. + +OUI:000224* + ID_OUI_FROM_DATABASE=C-COR + +OUI:00020D* + ID_OUI_FROM_DATABASE=Micronpc.com + +OUI:000220* + ID_OUI_FROM_DATABASE=CANON FINETECH INC. + +OUI:000378* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:00036C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000373* + ID_OUI_FROM_DATABASE=Aselsan A.S + +OUI:000368* + ID_OUI_FROM_DATABASE=Embedone Co., Ltd. + +OUI:000366* + ID_OUI_FROM_DATABASE=ASM Pacific Technology + +OUI:000365* + ID_OUI_FROM_DATABASE=Kira Information & Communications, Ltd. + +OUI:000360* + ID_OUI_FROM_DATABASE=PAC Interactive Technology, Inc. + +OUI:00035D* + ID_OUI_FROM_DATABASE=Bosung Hi-Net Co., Ltd. + +OUI:00031A* + ID_OUI_FROM_DATABASE=Beijing Broad Telecom Ltd., China + +OUI:000359* + ID_OUI_FROM_DATABASE=DigitalSis + +OUI:000354* + ID_OUI_FROM_DATABASE=Fiber Logic Communications + +OUI:000352* + ID_OUI_FROM_DATABASE=Colubris Networks + +OUI:00034E* + ID_OUI_FROM_DATABASE=Pos Data Company, Ltd. + +OUI:0002C3* + ID_OUI_FROM_DATABASE=Arelnet Ltd. + +OUI:0002BE* + ID_OUI_FROM_DATABASE=Totsu Engineering, Inc. + +OUI:0002BA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0002B2* + ID_OUI_FROM_DATABASE=Cablevision + +OUI:0002B5* + ID_OUI_FROM_DATABASE=Avnet, Inc. + +OUI:0002AE* + ID_OUI_FROM_DATABASE=Scannex Electronics Ltd. + +OUI:0002A7* + ID_OUI_FROM_DATABASE=Vivace Networks + +OUI:0002A2* + ID_OUI_FROM_DATABASE=Hilscher GmbH + +OUI:000297* + ID_OUI_FROM_DATABASE=C-COR.net + +OUI:00028E* + ID_OUI_FROM_DATABASE=Rapid 5 Networks, Inc. + +OUI:000293* + ID_OUI_FROM_DATABASE=Solid Data Systems + +OUI:0001FA* + ID_OUI_FROM_DATABASE=HOROSCAS + +OUI:000284* + ID_OUI_FROM_DATABASE=AREVA T&D + +OUI:00027D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00033F* + ID_OUI_FROM_DATABASE=BigBand Networks, Ltd. + +OUI:000336* + ID_OUI_FROM_DATABASE=Zetes Technologies + +OUI:00033B* + ID_OUI_FROM_DATABASE=TAMI Tech Co., Ltd. + +OUI:000328* + ID_OUI_FROM_DATABASE=Mace Group, Inc. + +OUI:00032F* + ID_OUI_FROM_DATABASE=Global Sun Technology, Inc. + +OUI:000320* + ID_OUI_FROM_DATABASE=Xpeed, Inc. + +OUI:000323* + ID_OUI_FROM_DATABASE=Cornet Technology, Inc. + +OUI:00029F* + ID_OUI_FROM_DATABASE=L-3 Communication Aviation Recorders + +OUI:00031F* + ID_OUI_FROM_DATABASE=Condev Ltd. + +OUI:000317* + ID_OUI_FROM_DATABASE=Merlin Systems, Inc. + +OUI:00030E* + ID_OUI_FROM_DATABASE=Core Communications Co., Ltd. + +OUI:000313* + ID_OUI_FROM_DATABASE=Access Media SPA + +OUI:0001A5* + ID_OUI_FROM_DATABASE=Nextcomm, Inc. + +OUI:0001A1* + ID_OUI_FROM_DATABASE=Mag-Tek, Inc. + +OUI:000195* + ID_OUI_FROM_DATABASE=Sena Technologies, Inc. + +OUI:00017D* + ID_OUI_FROM_DATABASE=ThermoQuest + +OUI:000189* + ID_OUI_FROM_DATABASE=Refraction Technology, Inc. + +OUI:00308B* + ID_OUI_FROM_DATABASE=Brix Networks + +OUI:00014F* + ID_OUI_FROM_DATABASE=ADTRAN INC + +OUI:00015A* + ID_OUI_FROM_DATABASE=Digital Video Broadcasting + +OUI:000166* + ID_OUI_FROM_DATABASE=TC GROUP A/S + +OUI:00016D* + ID_OUI_FROM_DATABASE=CarrierComm Inc. + +OUI:00015F* + ID_OUI_FROM_DATABASE=DIGITAL DESIGN GmbH + +OUI:000214* + ID_OUI_FROM_DATABASE=DTVRO + +OUI:000210* + ID_OUI_FROM_DATABASE=Fenecom + +OUI:000208* + ID_OUI_FROM_DATABASE=Unify Networks, Inc. + +OUI:000201* + ID_OUI_FROM_DATABASE=IFM Electronic gmbh + +OUI:0001F5* + ID_OUI_FROM_DATABASE=ERIM S.A. + +OUI:0001FD* + ID_OUI_FROM_DATABASE=Digital Voice Systems, Inc. + +OUI:0001E5* + ID_OUI_FROM_DATABASE=Supernet, Inc. + +OUI:0001E8* + ID_OUI_FROM_DATABASE=Force10 Networks, Inc. + +OUI:0001D9* + ID_OUI_FROM_DATABASE=Sigma, Inc. + +OUI:0001E0* + ID_OUI_FROM_DATABASE=Fast Systems, Inc. + +OUI:0001D5* + ID_OUI_FROM_DATABASE=HAEDONG INFO & COMM CO., LTD + +OUI:000118* + ID_OUI_FROM_DATABASE=EZ Digital Co., Ltd. + +OUI:000124* + ID_OUI_FROM_DATABASE=Acer Incorporated + +OUI:000101* + ID_OUI_FROM_DATABASE=Private + +OUI:000114* + ID_OUI_FROM_DATABASE=KANDA TSUSHIN KOGYO CO., LTD. + +OUI:000111* + ID_OUI_FROM_DATABASE=iDigm Inc. + +OUI:000105* + ID_OUI_FROM_DATABASE=Beckhoff Automation GmbH + +OUI:00029C* + ID_OUI_FROM_DATABASE=3COM + +OUI:00B009* + ID_OUI_FROM_DATABASE=Grass Valley, A Belden Brand + +OUI:00B09D* + ID_OUI_FROM_DATABASE=Point Grey Research Inc. + +OUI:00B094* + ID_OUI_FROM_DATABASE=Alaris, Inc. + +OUI:00B048* + ID_OUI_FROM_DATABASE=Marconi Communications Inc. + +OUI:00B0C7* + ID_OUI_FROM_DATABASE=Tellabs Operations, Inc. + +OUI:003060* + ID_OUI_FROM_DATABASE=Powerfile, Inc. + +OUI:00301C* + ID_OUI_FROM_DATABASE=ALTVATER AIRDATA SYSTEMS + +OUI:003015* + ID_OUI_FROM_DATABASE=CP CLARE CORP. + +OUI:0030E6* + ID_OUI_FROM_DATABASE=Draeger Medical Systems, Inc. + +OUI:003091* + ID_OUI_FROM_DATABASE=TAIWAN FIRST LINE ELEC. CORP. + +OUI:003080* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0030AD* + ID_OUI_FROM_DATABASE=SHANGHAI COMMUNICATION + +OUI:00305B* + ID_OUI_FROM_DATABASE=Toko Inc. + +OUI:003024* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00301F* + ID_OUI_FROM_DATABASE=OPTICAL NETWORKS, INC. + +OUI:0030D9* + ID_OUI_FROM_DATABASE=DATACORE SOFTWARE CORP. + +OUI:00D0FF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003058* + ID_OUI_FROM_DATABASE=API MOTION + +OUI:0030C6* + ID_OUI_FROM_DATABASE=CONTROL SOLUTIONS, INC. + +OUI:003036* + ID_OUI_FROM_DATABASE=RMP ELEKTRONIKSYSTEME GMBH + +OUI:00308A* + ID_OUI_FROM_DATABASE=NICOTRA SISTEMI S.P.A + +OUI:00302C* + ID_OUI_FROM_DATABASE=SYLANTRO SYSTEMS CORPORATION + +OUI:003006* + ID_OUI_FROM_DATABASE=SUPERPOWER COMPUTER + +OUI:003079* + ID_OUI_FROM_DATABASE=CQOS, INC. + +OUI:003059* + ID_OUI_FROM_DATABASE=KONTRON COMPACT COMPUTERS AG + +OUI:0030B9* + ID_OUI_FROM_DATABASE=ECTEL + +OUI:00303A* + ID_OUI_FROM_DATABASE=MAATEL + +OUI:0030A3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003040* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003064* + ID_OUI_FROM_DATABASE=ADLINK TECHNOLOGY, INC. + +OUI:003097* + ID_OUI_FROM_DATABASE=AB Regin + +OUI:0030EB* + ID_OUI_FROM_DATABASE=TURBONET COMMUNICATIONS, INC. + +OUI:0030C8* + ID_OUI_FROM_DATABASE=GAD LINE, LTD. + +OUI:0030C9* + ID_OUI_FROM_DATABASE=LuxN, N + +OUI:00B01E* + ID_OUI_FROM_DATABASE=Rantic Labs, Inc. + +OUI:00B064* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0030A2* + ID_OUI_FROM_DATABASE=Lightner Engineering + +OUI:0030DE* + ID_OUI_FROM_DATABASE=WAGO Kontakttechnik GmbH + +OUI:00309E* + ID_OUI_FROM_DATABASE=WORKBIT CORPORATION. + +OUI:003057* + ID_OUI_FROM_DATABASE=QTelNet, Inc. + +OUI:00305C* + ID_OUI_FROM_DATABASE=SMAR Laboratories Corp. + +OUI:003082* + ID_OUI_FROM_DATABASE=TAIHAN ELECTRIC WIRE CO., LTD. + +OUI:0030AE* + ID_OUI_FROM_DATABASE=Times N System, Inc. + +OUI:00300D* + ID_OUI_FROM_DATABASE=MMC Technology, Inc. + +OUI:003075* + ID_OUI_FROM_DATABASE=ADTECH + +OUI:0030E7* + ID_OUI_FROM_DATABASE=CNF MOBILE SOLUTIONS, INC. + +OUI:003019* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003052* + ID_OUI_FROM_DATABASE=ELASTIC NETWORKS + +OUI:003011* + ID_OUI_FROM_DATABASE=HMS Industrial Networks + +OUI:00304A* + ID_OUI_FROM_DATABASE=Fraunhofer IPMS + +OUI:003014* + ID_OUI_FROM_DATABASE=DIVIO, INC. + +OUI:003029* + ID_OUI_FROM_DATABASE=OPICOM + +OUI:0030BD* + ID_OUI_FROM_DATABASE=BELKIN COMPONENTS + +OUI:0030BA* + ID_OUI_FROM_DATABASE=AC&T SYSTEM CO., LTD. + +OUI:00301D* + ID_OUI_FROM_DATABASE=SKYSTREAM, INC. + +OUI:003049* + ID_OUI_FROM_DATABASE=BRYANT TECHNOLOGY, LTD. + +OUI:003041* + ID_OUI_FROM_DATABASE=SAEJIN T & M CO., LTD. + +OUI:00308C* + ID_OUI_FROM_DATABASE=Quantum Corporation + +OUI:00D04F* + ID_OUI_FROM_DATABASE=BITRONICS, INC. + +OUI:00D0EF* + ID_OUI_FROM_DATABASE=IGT + +OUI:00D022* + ID_OUI_FROM_DATABASE=INCREDIBLE TECHNOLOGIES, INC. + +OUI:00D0C8* + ID_OUI_FROM_DATABASE=Prevas A/S + +OUI:00D052* + ID_OUI_FROM_DATABASE=ASCEND COMMUNICATIONS, INC. + +OUI:00D0B1* + ID_OUI_FROM_DATABASE=OMEGA ELECTRONICS SA + +OUI:00D0C1* + ID_OUI_FROM_DATABASE=HARMONIC DATA SYSTEMS, LTD. + +OUI:00D0F0* + ID_OUI_FROM_DATABASE=CONVISION TECHNOLOGY GMBH + +OUI:00D00E* + ID_OUI_FROM_DATABASE=PLURIS, INC. + +OUI:00D055* + ID_OUI_FROM_DATABASE=KATHREIN-WERKE KG + +OUI:00D095* + ID_OUI_FROM_DATABASE=Alcatel-Lucent, Enterprise Business Group + +OUI:00D000* + ID_OUI_FROM_DATABASE=FERRAN SCIENTIFIC, INC. + +OUI:00D005* + ID_OUI_FROM_DATABASE=ZHS ZEITMANAGEMENTSYSTEME + +OUI:00D019* + ID_OUI_FROM_DATABASE=DAINIPPON SCREEN CORPORATE + +OUI:00D053* + ID_OUI_FROM_DATABASE=CONNECTED SYSTEMS + +OUI:00D097* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00016A* + ID_OUI_FROM_DATABASE=ALITEC + +OUI:000176* + ID_OUI_FROM_DATABASE=Orient Silver Enterprises + +OUI:000158* + ID_OUI_FROM_DATABASE=Electro Industries/Gauge Tech + +OUI:00012D* + ID_OUI_FROM_DATABASE=Komodo Technology + +OUI:000139* + ID_OUI_FROM_DATABASE=Point Multimedia Systems + +OUI:000140* + ID_OUI_FROM_DATABASE=Sendtek Corporation + +OUI:00014C* + ID_OUI_FROM_DATABASE=Berkeley Process Control + +OUI:000135* + ID_OUI_FROM_DATABASE=KDC Corp. + +OUI:00013C* + ID_OUI_FROM_DATABASE=TIW SYSTEMS + +OUI:000148* + ID_OUI_FROM_DATABASE=X-traWeb Inc. + +OUI:000120* + ID_OUI_FROM_DATABASE=OSCILLOQUARTZ S.A. + +OUI:000127* + ID_OUI_FROM_DATABASE=OPEN Networks Pty Ltd + +OUI:00309C* + ID_OUI_FROM_DATABASE=Timing Applications, Inc. + +OUI:003086* + ID_OUI_FROM_DATABASE=Transistor Devices, Inc. + +OUI:0030B5* + ID_OUI_FROM_DATABASE=Tadiran Microwave Networks + +OUI:003070* + ID_OUI_FROM_DATABASE=1Net Corporation + +OUI:003044* + ID_OUI_FROM_DATABASE=CradlePoint, Inc + +OUI:00307E* + ID_OUI_FROM_DATABASE=Redflex Communication Systems + +OUI:00307A* + ID_OUI_FROM_DATABASE=Advanced Technology & Systems + +OUI:0030B7* + ID_OUI_FROM_DATABASE=Teletrol Systems, Inc. + +OUI:0030B3* + ID_OUI_FROM_DATABASE=San Valley Systems, Inc. + +OUI:00303B* + ID_OUI_FROM_DATABASE=PowerCom Technology + +OUI:0030BC* + ID_OUI_FROM_DATABASE=Optronic AG + +OUI:003071* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:009003* + ID_OUI_FROM_DATABASE=APLIO + +OUI:0090D7* + ID_OUI_FROM_DATABASE=NetBoost Corp. + +OUI:009093* + ID_OUI_FROM_DATABASE=NANAO CORPORATION + +OUI:0090B4* + ID_OUI_FROM_DATABASE=WILLOWBROOK TECHNOLOGIES + +OUI:009083* + ID_OUI_FROM_DATABASE=TURBO COMMUNICATION, INC. + +OUI:0090BD* + ID_OUI_FROM_DATABASE=OMNIA COMMUNICATIONS, INC. + +OUI:009094* + ID_OUI_FROM_DATABASE=OSPREY TECHNOLOGIES, INC. + +OUI:0090DD* + ID_OUI_FROM_DATABASE=MIHARU COMMUNICATIONS Inc + +OUI:009028* + ID_OUI_FROM_DATABASE=NIPPON SIGNAL CO., LTD. + +OUI:00908C* + ID_OUI_FROM_DATABASE=ETREND ELECTRONICS, INC. + +OUI:00905D* + ID_OUI_FROM_DATABASE=NETCOM SICHERHEITSTECHNIK GMBH + +OUI:009068* + ID_OUI_FROM_DATABASE=DVT CORP. + +OUI:009030* + ID_OUI_FROM_DATABASE=HONEYWELL-DATING + +OUI:0090D3* + ID_OUI_FROM_DATABASE=GIESECKE & DEVRIENT GmbH + +OUI:005081* + ID_OUI_FROM_DATABASE=MURATA MACHINERY, LTD. + +OUI:0050CB* + ID_OUI_FROM_DATABASE=JETTER + +OUI:00500E* + ID_OUI_FROM_DATABASE=CHROMATIS NETWORKS, INC. + +OUI:0050FD* + ID_OUI_FROM_DATABASE=VISIONCOMM CO., LTD. + +OUI:0050FE* + ID_OUI_FROM_DATABASE=PCTVnet ASA + +OUI:0050AB* + ID_OUI_FROM_DATABASE=NALTEC, Inc. + +OUI:005006* + ID_OUI_FROM_DATABASE=TAC AB + +OUI:0050BF* + ID_OUI_FROM_DATABASE=Metalligence Technology Corp. + +OUI:005089* + ID_OUI_FROM_DATABASE=SAFETY MANAGEMENT SYSTEMS + +OUI:005066* + ID_OUI_FROM_DATABASE=AtecoM GmbH advanced telecomunication modules + +OUI:0050D9* + ID_OUI_FROM_DATABASE=ENGETRON-ENGENHARIA ELETRONICA IND. e COM. LTDA + +OUI:005043* + ID_OUI_FROM_DATABASE=MARVELL SEMICONDUCTOR, INC. + +OUI:005018* + ID_OUI_FROM_DATABASE=AMIT, Inc. + +OUI:005059* + ID_OUI_FROM_DATABASE=iBAHN + +OUI:00506A* + ID_OUI_FROM_DATABASE=EDEVA, INC. + +OUI:00502E* + ID_OUI_FROM_DATABASE=CAMBEX CORPORATION + +OUI:005070* + ID_OUI_FROM_DATABASE=CHAINTECH COMPUTER CO., LTD. + +OUI:00503B* + ID_OUI_FROM_DATABASE=MEDIAFIRE CORPORATION + +OUI:005084* + ID_OUI_FROM_DATABASE=ATL PRODUCTS + +OUI:005055* + ID_OUI_FROM_DATABASE=DOMS A/S + +OUI:00504B* + ID_OUI_FROM_DATABASE=BARCONET N.V. + +OUI:005046* + ID_OUI_FROM_DATABASE=MENICX INTERNATIONAL CO., LTD. + +OUI:00502C* + ID_OUI_FROM_DATABASE=SOYO COMPUTER, INC. + +OUI:005060* + ID_OUI_FROM_DATABASE=TANDBERG TELECOM AS + +OUI:0050DD* + ID_OUI_FROM_DATABASE=SERRA SOLDADURA, S.A. + +OUI:00503F* + ID_OUI_FROM_DATABASE=ANCHOR GAMES + +OUI:0050EE* + ID_OUI_FROM_DATABASE=TEK DIGITEL CORPORATION + +OUI:005004* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:005072* + ID_OUI_FROM_DATABASE=CORVIS CORPORATION + +OUI:005012* + ID_OUI_FROM_DATABASE=CBL - GMBH + +OUI:0050E8* + ID_OUI_FROM_DATABASE=NOMADIX INC. + +OUI:0050F2* + ID_OUI_FROM_DATABASE=MICROSOFT CORP. + +OUI:005052* + ID_OUI_FROM_DATABASE=TIARA NETWORKS, INC. + +OUI:005064* + ID_OUI_FROM_DATABASE=CAE ELECTRONICS + +OUI:0050B4* + ID_OUI_FROM_DATABASE=SATCHWELL CONTROL SYSTEMS, LTD + +OUI:0050B2* + ID_OUI_FROM_DATABASE=BRODEL GmbH + +OUI:00D081* + ID_OUI_FROM_DATABASE=RTD Embedded Technologies, Inc. + +OUI:00D011* + ID_OUI_FROM_DATABASE=PRISM VIDEO, INC. + +OUI:00D09B* + ID_OUI_FROM_DATABASE=SPECTEL LTD. + +OUI:00D031* + ID_OUI_FROM_DATABASE=INDUSTRIAL LOGIC CORPORATION + +OUI:00D021* + ID_OUI_FROM_DATABASE=REGENT ELECTRONICS CORP. + +OUI:00D0DF* + ID_OUI_FROM_DATABASE=KUZUMI ELECTRONICS, INC. + +OUI:00D0B4* + ID_OUI_FROM_DATABASE=KATSUJIMA CO., LTD. + +OUI:00D079* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D0E2* + ID_OUI_FROM_DATABASE=MRT MICRO, INC. + +OUI:00D039* + ID_OUI_FROM_DATABASE=UTILICOM, INC. + +OUI:00504F* + ID_OUI_FROM_DATABASE=OLENCOM ELECTRONICS + +OUI:0050A0* + ID_OUI_FROM_DATABASE=DELTA COMPUTER SYSTEMS, INC. + +OUI:005007* + ID_OUI_FROM_DATABASE=SIEMENS TELECOMMUNICATION SYSTEMS LIMITED + +OUI:005015* + ID_OUI_FROM_DATABASE=BRIGHT STAR ENGINEERING + +OUI:005031* + ID_OUI_FROM_DATABASE=AEROFLEX LABORATORIES, INC. + +OUI:0050DF* + ID_OUI_FROM_DATABASE=AirFiber, Inc. + +OUI:0050F3* + ID_OUI_FROM_DATABASE=GLOBAL NET INFORMATION CO., Ltd. + +OUI:005038* + ID_OUI_FROM_DATABASE=DAIN TELECOM CO., LTD. + +OUI:00D0E1* + ID_OUI_FROM_DATABASE=AVIONITEK ISRAEL INC. + +OUI:00D01B* + ID_OUI_FROM_DATABASE=MIMAKI ENGINEERING CO., LTD. + +OUI:00D06E* + ID_OUI_FROM_DATABASE=TRENDVIEW RECORDERS LTD. + +OUI:00D075* + ID_OUI_FROM_DATABASE=ALARIS MEDICAL SYSTEMS, INC. + +OUI:00509D* + ID_OUI_FROM_DATABASE=THE INDUSTREE B.V. + +OUI:00501E* + ID_OUI_FROM_DATABASE=Grass Valley, A Belden Brand + +OUI:00502B* + ID_OUI_FROM_DATABASE=GENRAD LTD. + +OUI:00500A* + ID_OUI_FROM_DATABASE=IRIS TECHNOLOGIES, INC. + +OUI:00D027* + ID_OUI_FROM_DATABASE=APPLIED AUTOMATION, INC. + +OUI:00D0F1* + ID_OUI_FROM_DATABASE=SEGA ENTERPRISES, LTD. + +OUI:00D009* + ID_OUI_FROM_DATABASE=HSING TECH. ENTERPRISE CO. LTD + +OUI:00D080* + ID_OUI_FROM_DATABASE=EXABYTE CORPORATION + +OUI:00D084* + ID_OUI_FROM_DATABASE=NEXCOMM SYSTEMS, INC. + +OUI:00D0E6* + ID_OUI_FROM_DATABASE=IBOND INC. + +OUI:00D099* + ID_OUI_FROM_DATABASE=Elcard Wireless Systems Oy + +OUI:0090AF* + ID_OUI_FROM_DATABASE=J. MORITA MFG. CORP. + +OUI:009088* + ID_OUI_FROM_DATABASE=BAXALL SECURITY LTD. + +OUI:0090E0* + ID_OUI_FROM_DATABASE=SYSTRAN CORP. + +OUI:00903E* + ID_OUI_FROM_DATABASE=N.V. PHILIPS INDUSTRIAL ACTIVITIES + +OUI:0090B9* + ID_OUI_FROM_DATABASE=BERAN INSTRUMENTS LTD. + +OUI:00901A* + ID_OUI_FROM_DATABASE=UNISPHERE SOLUTIONS + +OUI:009082* + ID_OUI_FROM_DATABASE=FORCE INSTITUTE + +OUI:00906A* + ID_OUI_FROM_DATABASE=TURNSTONE SYSTEMS, INC. + +OUI:0001FE* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:009077* + ID_OUI_FROM_DATABASE=ADVANCED FIBRE COMMUNICATIONS + +OUI:0090B2* + ID_OUI_FROM_DATABASE=AVICI SYSTEMS INC. + +OUI:009095* + ID_OUI_FROM_DATABASE=UNIVERSAL AVIONICS + +OUI:009012* + ID_OUI_FROM_DATABASE=GLOBESPAN SEMICONDUCTOR, INC. + +OUI:0090B6* + ID_OUI_FROM_DATABASE=FIBEX SYSTEMS + +OUI:0090F4* + ID_OUI_FROM_DATABASE=LIGHTNING INSTRUMENTATION + +OUI:00904F* + ID_OUI_FROM_DATABASE=ABB POWER T&D COMPANY, INC. + +OUI:00905A* + ID_OUI_FROM_DATABASE=DEARBORN GROUP, INC. + +OUI:009066* + ID_OUI_FROM_DATABASE=Troika Networks, Inc. + +OUI:00907A* + ID_OUI_FROM_DATABASE=Spectralink, Inc + +OUI:0090F0* + ID_OUI_FROM_DATABASE=Harmonic Video Systems Ltd. + +OUI:001047* + ID_OUI_FROM_DATABASE=ECHO ELETRIC CO. LTD. + +OUI:00100C* + ID_OUI_FROM_DATABASE=ITO CO., LTD. + +OUI:0010D0* + ID_OUI_FROM_DATABASE=WITCOM, LTD. + +OUI:001006* + ID_OUI_FROM_DATABASE=Thales Contact Solutions Ltd. + +OUI:0010D6* + ID_OUI_FROM_DATABASE=Exelis + +OUI:001076* + ID_OUI_FROM_DATABASE=EUREM GmbH + +OUI:00103F* + ID_OUI_FROM_DATABASE=TOLLGRADE COMMUNICATIONS, INC. + +OUI:001034* + ID_OUI_FROM_DATABASE=GNP Computers + +OUI:001012* + ID_OUI_FROM_DATABASE=PROCESSOR SYSTEMS (I) PVT LTD + +OUI:0010C8* + ID_OUI_FROM_DATABASE=COMMUNICATIONS ELECTRONICS SECURITY GROUP + +OUI:0010D1* + ID_OUI_FROM_DATABASE=Top Layer Networks, Inc. + +OUI:0010F0* + ID_OUI_FROM_DATABASE=RITTAL-WERK RUDOLF LOH GmbH & Co. + +OUI:00106A* + ID_OUI_FROM_DATABASE=DIGITAL MICROWAVE CORPORATION + +OUI:001030* + ID_OUI_FROM_DATABASE=EION Inc. + +OUI:0010A4* + ID_OUI_FROM_DATABASE=XIRCOM + +OUI:001050* + ID_OUI_FROM_DATABASE=RION CO., LTD. + +OUI:00109C* + ID_OUI_FROM_DATABASE=M-SYSTEM CO., LTD. + +OUI:001064* + ID_OUI_FROM_DATABASE=DNPG, LLC + +OUI:001020* + ID_OUI_FROM_DATABASE=Hand Held Products Inc + +OUI:00106E* + ID_OUI_FROM_DATABASE=TADIRAN COM. LTD. + +OUI:00105B* + ID_OUI_FROM_DATABASE=NET INSIGHT AB + +OUI:001002* + ID_OUI_FROM_DATABASE=ACTIA + +OUI:0010A0* + ID_OUI_FROM_DATABASE=INNOVEX TECHNOLOGIES, INC. + +OUI:001074* + ID_OUI_FROM_DATABASE=ATEN INTERNATIONAL CO., LTD. + +OUI:001057* + ID_OUI_FROM_DATABASE=Rebel.com, Inc. + +OUI:0010BC* + ID_OUI_FROM_DATABASE=Aastra Telecom + +OUI:001033* + ID_OUI_FROM_DATABASE=ACCESSLAN COMMUNICATIONS, INC. + +OUI:0004AC* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0010B4* + ID_OUI_FROM_DATABASE=ATMOSPHERE NETWORKS + +OUI:0010F9* + ID_OUI_FROM_DATABASE=UNIQUE SYSTEMS, INC. + +OUI:001038* + ID_OUI_FROM_DATABASE=MICRO RESEARCH INSTITUTE, INC. + +OUI:00100A* + ID_OUI_FROM_DATABASE=WILLIAMS COMMUNICATIONS GROUP + +OUI:001080* + ID_OUI_FROM_DATABASE=METAWAVE COMMUNICATIONS + +OUI:0010AB* + ID_OUI_FROM_DATABASE=KOITO ELECTRIC INDUSTRIES, LTD. + +OUI:00903C* + ID_OUI_FROM_DATABASE=ATLANTIC NETWORK SYSTEMS + +OUI:0090CE* + ID_OUI_FROM_DATABASE=TETRA GmbH + +OUI:0090E3* + ID_OUI_FROM_DATABASE=AVEX ELECTRONICS INC. + +OUI:00900B* + ID_OUI_FROM_DATABASE=LANNER ELECTRONICS, INC. + +OUI:0090C8* + ID_OUI_FROM_DATABASE=WAVERIDER COMMUNICATIONS (CANADA) INC. + +OUI:0090B7* + ID_OUI_FROM_DATABASE=DIGITAL LIGHTWAVE, INC. + +OUI:009037* + ID_OUI_FROM_DATABASE=ACUCOMM, INC. + +OUI:009059* + ID_OUI_FROM_DATABASE=TELECOM DEVICE K.K. + +OUI:00E003* + ID_OUI_FROM_DATABASE=NOKIA WIRELESS BUSINESS COMMUN + +OUI:00E0F3* + ID_OUI_FROM_DATABASE=WebSprint Communications, Inc. + +OUI:00E013* + ID_OUI_FROM_DATABASE=EASTERN ELECTRONIC CO., LTD. + +OUI:001063* + ID_OUI_FROM_DATABASE=STARGUIDE DIGITAL NETWORKS + +OUI:0010A7* + ID_OUI_FROM_DATABASE=UNEX TECHNOLOGY CORPORATION + +OUI:001039* + ID_OUI_FROM_DATABASE=Vectron Systems AG + +OUI:0010C3* + ID_OUI_FROM_DATABASE=CSI-CONTROL SYSTEMS + +OUI:00107F* + ID_OUI_FROM_DATABASE=CRESTRON ELECTRONICS, INC. + +OUI:00102C* + ID_OUI_FROM_DATABASE=Lasat Networks A/S + +OUI:0010B7* + ID_OUI_FROM_DATABASE=COYOTE TECHNOLOGIES, LLC + +OUI:006064* + ID_OUI_FROM_DATABASE=NETCOMM LIMITED + +OUI:0060CB* + ID_OUI_FROM_DATABASE=HITACHI ZOSEN CORPORATION + +OUI:006090* + ID_OUI_FROM_DATABASE=Artiza Networks Inc + +OUI:0060A9* + ID_OUI_FROM_DATABASE=GESYTEC MBH + +OUI:0060F2* + ID_OUI_FROM_DATABASE=LASERGRAPHICS, INC. + +OUI:006031* + ID_OUI_FROM_DATABASE=HRK SYSTEMS + +OUI:0060A6* + ID_OUI_FROM_DATABASE=PARTICLE MEASURING SYSTEMS + +OUI:006082* + ID_OUI_FROM_DATABASE=NOVALINK TECHNOLOGIES, INC. + +OUI:006012* + ID_OUI_FROM_DATABASE=POWER COMPUTING CORPORATION + +OUI:00604D* + ID_OUI_FROM_DATABASE=MMC NETWORKS, INC. + +OUI:006048* + ID_OUI_FROM_DATABASE=EMC CORPORATION + +OUI:0060E5* + ID_OUI_FROM_DATABASE=FUJI AUTOMATION CO., LTD. + +OUI:006010* + ID_OUI_FROM_DATABASE=NETWORK MACHINES, INC. + +OUI:006044* + ID_OUI_FROM_DATABASE=LITTON/POLY-SCIENTIFIC + +OUI:00609B* + ID_OUI_FROM_DATABASE=ASTRO-MED, INC. + +OUI:0060BE* + ID_OUI_FROM_DATABASE=WEBTRONICS + +OUI:006052* + ID_OUI_FROM_DATABASE=PERIPHERALS ENTERPRISE CO., Ltd. + +OUI:00E03F* + ID_OUI_FROM_DATABASE=JATON CORPORATION + +OUI:00E0EB* + ID_OUI_FROM_DATABASE=DIGICOM SYSTEMS, INCORPORATED + +OUI:00E00E* + ID_OUI_FROM_DATABASE=AVALON IMAGING SYSTEMS, INC. + +OUI:00E0CD* + ID_OUI_FROM_DATABASE=SAAB SENSIS CORPORATION + +OUI:00E0CB* + ID_OUI_FROM_DATABASE=RESON, INC. + +OUI:00E048* + ID_OUI_FROM_DATABASE=SDL COMMUNICATIONS, INC. + +OUI:00E083* + ID_OUI_FROM_DATABASE=JATO TECHNOLOGIES, INC. + +OUI:00E03D* + ID_OUI_FROM_DATABASE=FOCON ELECTRONIC SYSTEMS A/S + +OUI:00E0FA* + ID_OUI_FROM_DATABASE=TRL TECHNOLOGY, LTD. + +OUI:00E02C* + ID_OUI_FROM_DATABASE=AST COMPUTER + +OUI:00E00B* + ID_OUI_FROM_DATABASE=ROOFTOP COMMUNICATIONS CORP. + +OUI:00E067* + ID_OUI_FROM_DATABASE=eac AUTOMATION-CONSULTING GmbH + +OUI:00E058* + ID_OUI_FROM_DATABASE=PHASE ONE DENMARK A/S + +OUI:00E089* + ID_OUI_FROM_DATABASE=ION Networks, Inc. + +OUI:00E03B* + ID_OUI_FROM_DATABASE=PROMINET CORPORATION + +OUI:006017* + ID_OUI_FROM_DATABASE=TOKIMEC INC. + +OUI:0060E6* + ID_OUI_FROM_DATABASE=SHOMITI SYSTEMS INCORPORATED + +OUI:006053* + ID_OUI_FROM_DATABASE=TOYODA MACHINE WORKS, LTD. + +OUI:0060A0* + ID_OUI_FROM_DATABASE=SWITCHED NETWORK TECHNOLOGIES, INC. + +OUI:006019* + ID_OUI_FROM_DATABASE=Roche Diagnostics + +OUI:006033* + ID_OUI_FROM_DATABASE=ACUITY IMAGING, INC. + +OUI:0060EE* + ID_OUI_FROM_DATABASE=APOLLO + +OUI:006022* + ID_OUI_FROM_DATABASE=VICOM SYSTEMS, INC. + +OUI:006013* + ID_OUI_FROM_DATABASE=NETSTAL MASCHINEN AG + +OUI:0060F4* + ID_OUI_FROM_DATABASE=ADVANCED COMPUTER SOLUTIONS, Inc. + +OUI:006011* + ID_OUI_FROM_DATABASE=CRYSTAL SEMICONDUCTOR CORP. + +OUI:00600E* + ID_OUI_FROM_DATABASE=WAVENET INTERNATIONAL, INC. + +OUI:0060C0* + ID_OUI_FROM_DATABASE=Nera Networks AS + +OUI:00E062* + ID_OUI_FROM_DATABASE=HOST ENGINEERING + +OUI:00E033* + ID_OUI_FROM_DATABASE=E.E.P.D. GmbH + +OUI:00E079* + ID_OUI_FROM_DATABASE=A.T.N.R. + +OUI:00E09C* + ID_OUI_FROM_DATABASE=MII + +OUI:00E075* + ID_OUI_FROM_DATABASE=Verilink Corporation + +OUI:00E07A* + ID_OUI_FROM_DATABASE=MIKRODIDAKT AB + +OUI:00E03E* + ID_OUI_FROM_DATABASE=ALFATECH, INC. + +OUI:00E09A* + ID_OUI_FROM_DATABASE=Positron Inc. + +OUI:0060D7* + ID_OUI_FROM_DATABASE=ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE (EPFL) + +OUI:006087* + ID_OUI_FROM_DATABASE=KANSAI ELECTRIC CO., LTD. + +OUI:00E029* + ID_OUI_FROM_DATABASE=STANDARD MICROSYSTEMS CORP. + +OUI:00606B* + ID_OUI_FROM_DATABASE=Synclayer Inc. + +OUI:006073* + ID_OUI_FROM_DATABASE=REDCREEK COMMUNICATIONS, INC. + +OUI:006039* + ID_OUI_FROM_DATABASE=SanCom Technology, Inc. + +OUI:0060A5* + ID_OUI_FROM_DATABASE=PERFORMANCE TELECOM CORP. + +OUI:0060B3* + ID_OUI_FROM_DATABASE=Z-COM, INC. + +OUI:006089* + ID_OUI_FROM_DATABASE=XATA + +OUI:00603C* + ID_OUI_FROM_DATABASE=HAGIWARA SYS-COM CO., LTD. + +OUI:00602E* + ID_OUI_FROM_DATABASE=CYCLADES CORPORATION + +OUI:006075* + ID_OUI_FROM_DATABASE=PENTEK, INC. + +OUI:00601C* + ID_OUI_FROM_DATABASE=TELXON CORPORATION + +OUI:006016* + ID_OUI_FROM_DATABASE=CLARIION + +OUI:0060AD* + ID_OUI_FROM_DATABASE=MegaChips Corporation + +OUI:0060B6* + ID_OUI_FROM_DATABASE=LAND COMPUTER CO., LTD. + +OUI:006055* + ID_OUI_FROM_DATABASE=CORNELL UNIVERSITY + +OUI:006015* + ID_OUI_FROM_DATABASE=NET2NET CORPORATION + +OUI:00A01D* + ID_OUI_FROM_DATABASE=Red Lion Controls, LP + +OUI:00A071* + ID_OUI_FROM_DATABASE=VIDEO LOTTERY TECHNOLOGIES,INC + +OUI:00A052* + ID_OUI_FROM_DATABASE=STANILITE ELECTRONICS PTY. LTD + +OUI:00A0EA* + ID_OUI_FROM_DATABASE=ETHERCOM CORP. + +OUI:00A02E* + ID_OUI_FROM_DATABASE=BRAND COMMUNICATIONS, LTD. + +OUI:00A0E2* + ID_OUI_FROM_DATABASE=Keisokugiken Corporation + +OUI:00A058* + ID_OUI_FROM_DATABASE=GLORY, LTD. + +OUI:00E093* + ID_OUI_FROM_DATABASE=ACKFIN NETWORKS + +OUI:00E0E3* + ID_OUI_FROM_DATABASE=SK-ELEKTRONIK GMBH + +OUI:00E066* + ID_OUI_FROM_DATABASE=ProMax Systems, Inc. + +OUI:00E0DB* + ID_OUI_FROM_DATABASE=ViaVideo Communications, Inc. + +OUI:00E0DF* + ID_OUI_FROM_DATABASE=KEYMILE GmbH + +OUI:00E00D* + ID_OUI_FROM_DATABASE=RADIANT SYSTEMS + +OUI:00E008* + ID_OUI_FROM_DATABASE=AMAZING CONTROLS! INC. + +OUI:00E086* + ID_OUI_FROM_DATABASE=Emerson Network Power, Avocent Division + +OUI:00E0E1* + ID_OUI_FROM_DATABASE=G2 NETWORKS, INC. + +OUI:00E042* + ID_OUI_FROM_DATABASE=Pacom Systems Ltd. + +OUI:00E08E* + ID_OUI_FROM_DATABASE=UTSTARCOM + +OUI:00E095* + ID_OUI_FROM_DATABASE=ADVANCED-VISION TECHNOLGIES CORP. + +OUI:006006* + ID_OUI_FROM_DATABASE=SOTEC CO., LTD + +OUI:00603D* + ID_OUI_FROM_DATABASE=3CX + +OUI:006029* + ID_OUI_FROM_DATABASE=CARY PERIPHERALS INC. + +OUI:006043* + ID_OUI_FROM_DATABASE=iDirect, INC. + +OUI:0060D1* + ID_OUI_FROM_DATABASE=CASCADE COMMUNICATIONS + +OUI:0060CD* + ID_OUI_FROM_DATABASE=VideoServer, Inc. + +OUI:006094* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0060D9* + ID_OUI_FROM_DATABASE=TRANSYS NETWORKS INC. + +OUI:0060AA* + ID_OUI_FROM_DATABASE=INTELLIGENT DEVICES INC. (IDI) + +OUI:00605A* + ID_OUI_FROM_DATABASE=CELCORE, INC. + +OUI:006065* + ID_OUI_FROM_DATABASE=BERNECKER & RAINER INDUSTRIE-ELEKTRONIC GmbH + +OUI:00E07B* + ID_OUI_FROM_DATABASE=BAY NETWORKS + +OUI:00E077* + ID_OUI_FROM_DATABASE=WEBGEAR, INC. + +OUI:00E0D2* + ID_OUI_FROM_DATABASE=VERSANET COMMUNICATIONS, INC. + +OUI:00E04E* + ID_OUI_FROM_DATABASE=SANYO DENKI CO., LTD. + +OUI:00E0D0* + ID_OUI_FROM_DATABASE=NETSPEED, INC. + +OUI:00E02A* + ID_OUI_FROM_DATABASE=TANDBERG TELEVISION AS + +OUI:00E05B* + ID_OUI_FROM_DATABASE=WEST END SYSTEMS CORP. + +OUI:00E051* + ID_OUI_FROM_DATABASE=TALX CORPORATION + +OUI:00A0F0* + ID_OUI_FROM_DATABASE=TORONTO MICROELECTRONICS INC. + +OUI:00A049* + ID_OUI_FROM_DATABASE=DIGITECH INDUSTRIES, INC. + +OUI:00A027* + ID_OUI_FROM_DATABASE=FIREPOWER SYSTEMS, INC. + +OUI:00A0FF* + ID_OUI_FROM_DATABASE=TELLABS OPERATIONS, INC. + +OUI:00A001* + ID_OUI_FROM_DATABASE=DRS Signal Solutions + +OUI:00A0F1* + ID_OUI_FROM_DATABASE=MTI + +OUI:00A046* + ID_OUI_FROM_DATABASE=SCITEX CORP. LTD. + +OUI:00A0D9* + ID_OUI_FROM_DATABASE=CONVEX COMPUTER CORPORATION + +OUI:00A0B5* + ID_OUI_FROM_DATABASE=3H TECHNOLOGY + +OUI:00A0AC* + ID_OUI_FROM_DATABASE=GILAT SATELLITE NETWORKS, LTD. + +OUI:00A057* + ID_OUI_FROM_DATABASE=LANCOM Systems GmbH + +OUI:00A086* + ID_OUI_FROM_DATABASE=AMBER WAVE SYSTEMS, INC. + +OUI:00A083* + ID_OUI_FROM_DATABASE=ASIMMPHONY TURKEY + +OUI:00A091* + ID_OUI_FROM_DATABASE=APPLICOM INTERNATIONAL + +OUI:00A004* + ID_OUI_FROM_DATABASE=NETPOWER, INC. + +OUI:00A081* + ID_OUI_FROM_DATABASE=ALCATEL DATA NETWORKS + +OUI:00A0D5* + ID_OUI_FROM_DATABASE=SIERRA WIRELESS INC. + +OUI:00200F* + ID_OUI_FROM_DATABASE=EBRAINS Inc + +OUI:0020C7* + ID_OUI_FROM_DATABASE=AKAI Professional M.I. Corp. + +OUI:0020EB* + ID_OUI_FROM_DATABASE=CINCINNATI MICROWAVE, INC. + +OUI:0020E3* + ID_OUI_FROM_DATABASE=MCD KENCOM CORPORATION + +OUI:002013* + ID_OUI_FROM_DATABASE=DIVERSIFIED TECHNOLOGY, INC. + +OUI:0020C1* + ID_OUI_FROM_DATABASE=SAXA, Inc. + +OUI:002087* + ID_OUI_FROM_DATABASE=MEMOTEC, INC. + +OUI:0020F9* + ID_OUI_FROM_DATABASE=PARALINK NETWORKS, INC. + +OUI:00A0F9* + ID_OUI_FROM_DATABASE=BINTEC COMMUNICATIONS GMBH + +OUI:00A0BC* + ID_OUI_FROM_DATABASE=VIASAT, INCORPORATED + +OUI:00A003* + ID_OUI_FROM_DATABASE=Siemens Switzerland Ltd., I B T HVP + +OUI:00A09E* + ID_OUI_FROM_DATABASE=ICTV + +OUI:00A026* + ID_OUI_FROM_DATABASE=TELDAT, S.A. + +OUI:00201A* + ID_OUI_FROM_DATABASE=MRV Communications, Inc. + +OUI:002023* + ID_OUI_FROM_DATABASE=T.C. TECHNOLOGIES PTY. LTD + +OUI:0020F3* + ID_OUI_FROM_DATABASE=RAYNET CORPORATION + +OUI:002039* + ID_OUI_FROM_DATABASE=SCINETS + +OUI:002038* + ID_OUI_FROM_DATABASE=VME MICROSYSTEMS INTERNATIONAL CORPORATION + +OUI:00203E* + ID_OUI_FROM_DATABASE=LogiCan Technologies, Inc. + +OUI:002055* + ID_OUI_FROM_DATABASE=ALTECH CO., LTD. + +OUI:0020D9* + ID_OUI_FROM_DATABASE=PANASONIC TECHNOLOGIES, INC./MIECO-US + +OUI:002080* + ID_OUI_FROM_DATABASE=SYNERGY (UK) LTD. + +OUI:002026* + ID_OUI_FROM_DATABASE=AMKLY SYSTEMS, INC. + +OUI:00203D* + ID_OUI_FROM_DATABASE=Honeywell ECC + +OUI:002019* + ID_OUI_FROM_DATABASE=OHLER GMBH + +OUI:002057* + ID_OUI_FROM_DATABASE=TITZE DATENTECHNIK GmbH + +OUI:0020BE* + ID_OUI_FROM_DATABASE=LAN ACCESS CORP. + +OUI:002022* + ID_OUI_FROM_DATABASE=NMS Communications + +OUI:0020AA* + ID_OUI_FROM_DATABASE=Ericsson Television Limited + +OUI:00208E* + ID_OUI_FROM_DATABASE=CHEVIN SOFTWARE ENG. LTD. + +OUI:00203B* + ID_OUI_FROM_DATABASE=WISDM LTD. + +OUI:002044* + ID_OUI_FROM_DATABASE=GENITECH PTY LTD + +OUI:0020F5* + ID_OUI_FROM_DATABASE=PANDATEL AG + +OUI:002021* + ID_OUI_FROM_DATABASE=ALGORITHMS SOFTWARE PVT. LTD. + +OUI:002074* + ID_OUI_FROM_DATABASE=SUNGWOON SYSTEMS + +OUI:0020CE* + ID_OUI_FROM_DATABASE=LOGICAL DESIGN GROUP, INC. + +OUI:002082* + ID_OUI_FROM_DATABASE=ONEAC CORPORATION + +OUI:0020BF* + ID_OUI_FROM_DATABASE=AEHR TEST SYSTEMS + +OUI:0020F1* + ID_OUI_FROM_DATABASE=ALTOS INDIA LIMITED + +OUI:00205D* + ID_OUI_FROM_DATABASE=NANOMATIC OY + +OUI:0020E1* + ID_OUI_FROM_DATABASE=ALAMAR ELECTRONICS + +OUI:0020CC* + ID_OUI_FROM_DATABASE=DIGITAL SERVICES, LTD. + +OUI:00202C* + ID_OUI_FROM_DATABASE=WELLTRONIX CO., LTD. + +OUI:0020B3* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:00A048* + ID_OUI_FROM_DATABASE=QUESTECH, LTD. + +OUI:00A0C4* + ID_OUI_FROM_DATABASE=CRISTIE ELECTRONICS LTD. + +OUI:00A089* + ID_OUI_FROM_DATABASE=XPOINT TECHNOLOGIES, INC. + +OUI:00A0D1* + ID_OUI_FROM_DATABASE=INVENTEC CORPORATION + +OUI:00A0AE* + ID_OUI_FROM_DATABASE=NUCOM SYSTEMS, INC. + +OUI:00A02B* + ID_OUI_FROM_DATABASE=TRANSITIONS RESEARCH CORP. + +OUI:00A0A1* + ID_OUI_FROM_DATABASE=EPIC DATA INC. + +OUI:00A0C3* + ID_OUI_FROM_DATABASE=UNICOMPUTER GMBH + +OUI:00A042* + ID_OUI_FROM_DATABASE=SPUR PRODUCTS CORP. + +OUI:00C007* + ID_OUI_FROM_DATABASE=PINNACLE DATA SYSTEMS, INC. + +OUI:00C0F8* + ID_OUI_FROM_DATABASE=ABOUT COMPUTING INC. + +OUI:00C06F* + ID_OUI_FROM_DATABASE=KOMATSU LTD. + +OUI:00C08E* + ID_OUI_FROM_DATABASE=NETWORK INFORMATION TECHNOLOGY + +OUI:00C05A* + ID_OUI_FROM_DATABASE=SEMAPHORE COMMUNICATIONS CORP. + +OUI:00C0EB* + ID_OUI_FROM_DATABASE=SEH COMPUTERTECHNIK GMBH + +OUI:00C0C7* + ID_OUI_FROM_DATABASE=SPARKTRUM MICROSYSTEMS, INC. + +OUI:00C0D8* + ID_OUI_FROM_DATABASE=UNIVERSAL DATA SYSTEMS + +OUI:00C068* + ID_OUI_FROM_DATABASE=HME Clear-Com LTD. + +OUI:0040DB* + ID_OUI_FROM_DATABASE=ADVANCED TECHNICAL SOLUTIONS + +OUI:00405B* + ID_OUI_FROM_DATABASE=FUNASSET LIMITED + +OUI:00401B* + ID_OUI_FROM_DATABASE=PRINTER SYSTEMS CORP. + +OUI:0040EB* + ID_OUI_FROM_DATABASE=MARTIN MARIETTA CORPORATION + +OUI:0040CD* + ID_OUI_FROM_DATABASE=TERA MICROSYSTEMS, INC. + +OUI:0040E5* + ID_OUI_FROM_DATABASE=SYBUS CORPORATION + +OUI:0040F9* + ID_OUI_FROM_DATABASE=COMBINET + +OUI:004039* + ID_OUI_FROM_DATABASE=OPTEC DAIICHI DENKO CO., LTD. + +OUI:0040FE* + ID_OUI_FROM_DATABASE=SYMPLEX COMMUNICATIONS + +OUI:0020F0* + ID_OUI_FROM_DATABASE=UNIVERSAL MICROELECTRONICS CO. + +OUI:0020EF* + ID_OUI_FROM_DATABASE=USC CORPORATION + +OUI:002016* + ID_OUI_FROM_DATABASE=SHOWA ELECTRIC WIRE & CABLE CO + +OUI:00201F* + ID_OUI_FROM_DATABASE=BEST POWER TECHNOLOGY, INC. + +OUI:002045* + ID_OUI_FROM_DATABASE=ION Networks, Inc. + +OUI:0020B6* + ID_OUI_FROM_DATABASE=AGILE NETWORKS, INC. + +OUI:00208A* + ID_OUI_FROM_DATABASE=SONIX COMMUNICATIONS, LTD. + +OUI:00204C* + ID_OUI_FROM_DATABASE=MITRON COMPUTER PTE LTD. + +OUI:002002* + ID_OUI_FROM_DATABASE=SERITECH ENTERPRISE CO., LTD. + +OUI:00204B* + ID_OUI_FROM_DATABASE=AUTOCOMPUTER CO., LTD. + +OUI:0020AF* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:002048* + ID_OUI_FROM_DATABASE=Marconi Communications + +OUI:002008* + ID_OUI_FROM_DATABASE=CABLE & COMPUTER TECHNOLOGY + +OUI:00C023* + ID_OUI_FROM_DATABASE=TUTANKHAMON ELECTRONICS + +OUI:00C0F3* + ID_OUI_FROM_DATABASE=NETWORK COMMUNICATIONS CORP. + +OUI:00C043* + ID_OUI_FROM_DATABASE=STRATACOM + +OUI:00C0B3* + ID_OUI_FROM_DATABASE=COMSTAT DATACOMM CORPORATION + +OUI:00C0B5* + ID_OUI_FROM_DATABASE=CORPORATE NETWORK SYSTEMS,INC. + +OUI:00403E* + ID_OUI_FROM_DATABASE=RASTER OPS CORPORATION + +OUI:0040AE* + ID_OUI_FROM_DATABASE=DELTA CONTROLS, INC. + +OUI:0040C6* + ID_OUI_FROM_DATABASE=FIBERNET RESEARCH, INC. + +OUI:004092* + ID_OUI_FROM_DATABASE=ASP COMPUTER PRODUCTS, INC. + +OUI:004054* + ID_OUI_FROM_DATABASE=CONNECTION MACHINES SERVICES + +OUI:0040D8* + ID_OUI_FROM_DATABASE=OCEAN OFFICE AUTOMATION LTD. + +OUI:0040C0* + ID_OUI_FROM_DATABASE=VISTA CONTROLS CORPORATION + +OUI:004088* + ID_OUI_FROM_DATABASE=MOBIUS TECHNOLOGIES, INC. + +OUI:00803B* + ID_OUI_FROM_DATABASE=APT COMMUNICATIONS, INC. + +OUI:0080BA* + ID_OUI_FROM_DATABASE=SPECIALIX (ASIA) PTE, LTD + +OUI:00BB01* + ID_OUI_FROM_DATABASE=OCTOTHORPE CORP. + +OUI:00C01F* + ID_OUI_FROM_DATABASE=S.E.R.C.E.L. + +OUI:00C094* + ID_OUI_FROM_DATABASE=VMX INC. + +OUI:00C075* + ID_OUI_FROM_DATABASE=XANTE CORPORATION + +OUI:00C0F9* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:00C039* + ID_OUI_FROM_DATABASE=Teridian Semiconductor Corporation + +OUI:00C077* + ID_OUI_FROM_DATABASE=DAEWOO TELECOM LTD. + +OUI:00C02F* + ID_OUI_FROM_DATABASE=OKUMA CORPORATION + +OUI:00C0F1* + ID_OUI_FROM_DATABASE=SHINKO ELECTRIC CO., LTD. + +OUI:00C0DE* + ID_OUI_FROM_DATABASE=ZCOMM, INC. + +OUI:0040AF* + ID_OUI_FROM_DATABASE=DIGITAL PRODUCTS, INC. + +OUI:00404F* + ID_OUI_FROM_DATABASE=SPACE & NAVAL WARFARE SYSTEMS + +OUI:00407B* + ID_OUI_FROM_DATABASE=SCIENTIFIC ATLANTA + +OUI:00404E* + ID_OUI_FROM_DATABASE=FLUENT, INC. + +OUI:00C0F7* + ID_OUI_FROM_DATABASE=ENGAGE COMMUNICATION, INC. + +OUI:00C030* + ID_OUI_FROM_DATABASE=INTEGRATED ENGINEERING B. V. + +OUI:00C04A* + ID_OUI_FROM_DATABASE=GROUP 2000 AG + +OUI:00C0A6* + ID_OUI_FROM_DATABASE=EXICOM AUSTRALIA PTY. LTD + +OUI:00C053* + ID_OUI_FROM_DATABASE=Aspect Software Inc. + +OUI:00C0CF* + ID_OUI_FROM_DATABASE=IMATRAN VOIMA OY + +OUI:00C029* + ID_OUI_FROM_DATABASE=Nexans Deutschland GmbH - ANS + +OUI:00C0A4* + ID_OUI_FROM_DATABASE=UNIGRAF OY + +OUI:00C060* + ID_OUI_FROM_DATABASE=ID SCANDINAVIA AS + +OUI:00C082* + ID_OUI_FROM_DATABASE=MOORE PRODUCTS CO. + +OUI:00C008* + ID_OUI_FROM_DATABASE=SECO SRL + +OUI:00C0BB* + ID_OUI_FROM_DATABASE=FORVAL CREATIVE, INC. + +OUI:00C0E0* + ID_OUI_FROM_DATABASE=DSC COMMUNICATION CORP. + +OUI:00C05E* + ID_OUI_FROM_DATABASE=VARI-LITE, INC. + +OUI:00C031* + ID_OUI_FROM_DATABASE=DESIGN RESEARCH SYSTEMS, INC. + +OUI:00C07C* + ID_OUI_FROM_DATABASE=HIGHTECH INFORMATION + +OUI:00C0AE* + ID_OUI_FROM_DATABASE=TOWERCOM CO. INC. DBA PC HOUSE + +OUI:00C0D6* + ID_OUI_FROM_DATABASE=J1 SYSTEMS, INC. + +OUI:00C0AA* + ID_OUI_FROM_DATABASE=SILICON VALLEY COMPUTER + +OUI:00C04E* + ID_OUI_FROM_DATABASE=COMTROL CORPORATION + +OUI:00C00A* + ID_OUI_FROM_DATABASE=MICRO CRAFT + +OUI:00C02A* + ID_OUI_FROM_DATABASE=OHKURA ELECTRIC CO., LTD. + +OUI:00C0F2* + ID_OUI_FROM_DATABASE=TRANSITION NETWORKS + +OUI:00C01D* + ID_OUI_FROM_DATABASE=GRAND JUNCTION NETWORKS, INC. + +OUI:00C0AD* + ID_OUI_FROM_DATABASE=MARBEN COMMUNICATION SYSTEMS + +OUI:00C024* + ID_OUI_FROM_DATABASE=EDEN SISTEMAS DE COMPUTACAO SA + +OUI:00C0E9* + ID_OUI_FROM_DATABASE=OAK SOLUTIONS, LTD. + +OUI:00C0C5* + ID_OUI_FROM_DATABASE=SID INFORMATICA + +OUI:00C001* + ID_OUI_FROM_DATABASE=DIATEK PATIENT MANAGMENT + +OUI:00C07E* + ID_OUI_FROM_DATABASE=KUBOTA CORPORATION ELECTRONIC + +OUI:008012* + ID_OUI_FROM_DATABASE=INTEGRATED MEASUREMENT SYSTEMS + +OUI:008039* + ID_OUI_FROM_DATABASE=ALCATEL STC AUSTRALIA + +OUI:008023* + ID_OUI_FROM_DATABASE=INTEGRATED BUSINESS NETWORKS + +OUI:0080CA* + ID_OUI_FROM_DATABASE=NETCOM RESEARCH INCORPORATED + +OUI:00804D* + ID_OUI_FROM_DATABASE=CYCLONE MICROSYSTEMS, INC. + +OUI:0080D6* + ID_OUI_FROM_DATABASE=NUVOTECH, INC. + +OUI:0080ED* + ID_OUI_FROM_DATABASE=IQ TECHNOLOGIES, INC. + +OUI:0080C1* + ID_OUI_FROM_DATABASE=LANEX CORPORATION + +OUI:008049* + ID_OUI_FROM_DATABASE=NISSIN ELECTRIC CO., LTD. + +OUI:00807C* + ID_OUI_FROM_DATABASE=FIBERCOM, INC. + +OUI:008079* + ID_OUI_FROM_DATABASE=MICROBUS DESIGNS LTD. + +OUI:0080DE* + ID_OUI_FROM_DATABASE=GIPSI S.A. + +OUI:008004* + ID_OUI_FROM_DATABASE=ANTLOW COMMUNICATIONS, LTD. + +OUI:008078* + ID_OUI_FROM_DATABASE=PRACTICAL PERIPHERALS, INC. + +OUI:008040* + ID_OUI_FROM_DATABASE=JOHN FLUKE MANUFACTURING CO. + +OUI:0000F8* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:0080CE* + ID_OUI_FROM_DATABASE=BROADCAST TELEVISION SYSTEMS + +OUI:00801A* + ID_OUI_FROM_DATABASE=BELL ATLANTIC + +OUI:00803F* + ID_OUI_FROM_DATABASE=TATUNG COMPANY + +OUI:0080D4* + ID_OUI_FROM_DATABASE=CHASE RESEARCH LTD. + +OUI:0080CB* + ID_OUI_FROM_DATABASE=FALCO DATA PRODUCTS + +OUI:008075* + ID_OUI_FROM_DATABASE=PARSYTEC GMBH + +OUI:0080EB* + ID_OUI_FROM_DATABASE=COMPCONTROL B.V. + +OUI:008099* + ID_OUI_FROM_DATABASE=Eaton Industries GmbH + +OUI:0080E4* + ID_OUI_FROM_DATABASE=NORTHWEST DIGITAL SYSTEMS, INC + +OUI:008041* + ID_OUI_FROM_DATABASE=VEB KOMBINAT ROBOTRON + +OUI:0080C8* + ID_OUI_FROM_DATABASE=D-LINK SYSTEMS, INC. + +OUI:008036* + ID_OUI_FROM_DATABASE=REFLEX MANUFACTURING SYSTEMS + +OUI:0040F0* + ID_OUI_FROM_DATABASE=MicroBrain,Inc. + +OUI:0040A7* + ID_OUI_FROM_DATABASE=ITAUTEC PHILCO S.A. + +OUI:0040D3* + ID_OUI_FROM_DATABASE=KIMPSION INTERNATIONAL CORP. + +OUI:004065* + ID_OUI_FROM_DATABASE=GTE SPACENET + +OUI:0040CB* + ID_OUI_FROM_DATABASE=LANWAN TECHNOLOGIES + +OUI:004041* + ID_OUI_FROM_DATABASE=FUJIKURA LTD. + +OUI:004053* + ID_OUI_FROM_DATABASE=AMPRO COMPUTERS + +OUI:008032* + ID_OUI_FROM_DATABASE=ACCESS CO., LTD. + +OUI:0080CF* + ID_OUI_FROM_DATABASE=EMBEDDED PERFORMANCE INC. + +OUI:008031* + ID_OUI_FROM_DATABASE=BASYS, CORP. + +OUI:00803A* + ID_OUI_FROM_DATABASE=VARITYPER, INC. + +OUI:00807E* + ID_OUI_FROM_DATABASE=SOUTHERN PACIFIC LTD. + +OUI:008029* + ID_OUI_FROM_DATABASE=EAGLE TECHNOLOGY, INC. + +OUI:00802F* + ID_OUI_FROM_DATABASE=NATIONAL INSTRUMENTS CORP. + +OUI:008051* + ID_OUI_FROM_DATABASE=FIBERMUX + +OUI:0080FD* + ID_OUI_FROM_DATABASE=EXSCEED CORPRATION + +OUI:004008* + ID_OUI_FROM_DATABASE=A PLUS INFO CORPORATION + +OUI:0040E9* + ID_OUI_FROM_DATABASE=ACCORD SYSTEMS, INC. + +OUI:0040B5* + ID_OUI_FROM_DATABASE=VIDEO TECHNOLOGY COMPUTERS LTD + +OUI:004012* + ID_OUI_FROM_DATABASE=WINDATA, INC. + +OUI:00401C* + ID_OUI_FROM_DATABASE=AST RESEARCH, INC. + +OUI:004067* + ID_OUI_FROM_DATABASE=OMNIBYTE CORPORATION + +OUI:004035* + ID_OUI_FROM_DATABASE=OPCOM + +OUI:0040EA* + ID_OUI_FROM_DATABASE=PLAIN TREE SYSTEMS INC + +OUI:0040EF* + ID_OUI_FROM_DATABASE=HYPERCOM, INC. + +OUI:004093* + ID_OUI_FROM_DATABASE=PAXDATA NETWORKS LTD. + +OUI:0040EC* + ID_OUI_FROM_DATABASE=MIKASA SYSTEM ENGINEERING + +OUI:0080B9* + ID_OUI_FROM_DATABASE=ARCHE TECHNOLIGIES INC. + +OUI:0080A7* + ID_OUI_FROM_DATABASE=Honeywell International Inc + +OUI:0040DA* + ID_OUI_FROM_DATABASE=TELSPEC LTD + +OUI:004083* + ID_OUI_FROM_DATABASE=TDA INDUSTRIA DE PRODUTOS + +OUI:0040C8* + ID_OUI_FROM_DATABASE=MILAN TECHNOLOGY CORPORATION + +OUI:0040BC* + ID_OUI_FROM_DATABASE=ALGORITHMICS LTD. + +OUI:00402F* + ID_OUI_FROM_DATABASE=XLNT DESIGNS INC. + +OUI:00405D* + ID_OUI_FROM_DATABASE=STAR-TEK, INC. + +OUI:00405F* + ID_OUI_FROM_DATABASE=AFE COMPUTERS LTD. + +OUI:004043* + ID_OUI_FROM_DATABASE=Nokia Siemens Networks GmbH & Co. KG. + +OUI:00800D* + ID_OUI_FROM_DATABASE=VOSSWINKEL F.U. + +OUI:0080D1* + ID_OUI_FROM_DATABASE=KIMTRON CORPORATION + +OUI:00805D* + ID_OUI_FROM_DATABASE=CANSTAR + +OUI:008094* + ID_OUI_FROM_DATABASE=ALFA LAVAL AUTOMATION AB + +OUI:008047* + ID_OUI_FROM_DATABASE=IN-NET CORP. + +OUI:008064* + ID_OUI_FROM_DATABASE=WYSE TECHNOLOGY LLC + +OUI:0080C5* + ID_OUI_FROM_DATABASE=NOVELLCO DE MEXICO + +OUI:0080AC* + ID_OUI_FROM_DATABASE=IMLOGIX, DIVISION OF GENESYS + +OUI:000052* + ID_OUI_FROM_DATABASE=Intrusion.com, Inc. + +OUI:0000BD* + ID_OUI_FROM_DATABASE=MITSUBISHI CABLE COMPANY + +OUI:000037* + ID_OUI_FROM_DATABASE=OXFORD METRICS LIMITED + +OUI:00003F* + ID_OUI_FROM_DATABASE=SYNTREX, INC. + +OUI:08007C* + ID_OUI_FROM_DATABASE=VITALINK COMMUNICATIONS CORP. + +OUI:080076* + ID_OUI_FROM_DATABASE=PC LAN TECHNOLOGIES + +OUI:080072* + ID_OUI_FROM_DATABASE=XEROX CORP UNIV GRANT PROGRAM + +OUI:080070* + ID_OUI_FROM_DATABASE=MITSUBISHI ELECTRIC CORP. + +OUI:080068* + ID_OUI_FROM_DATABASE=RIDGE COMPUTERS + +OUI:080062* + ID_OUI_FROM_DATABASE=General Dynamics + +OUI:080057* + ID_OUI_FROM_DATABASE=Evans & Sutherland + +OUI:000010* + ID_OUI_FROM_DATABASE=SYTEK INC. + +OUI:000033* + ID_OUI_FROM_DATABASE=EGAN MACHINERY COMPANY + +OUI:000080* + ID_OUI_FROM_DATABASE=CRAY COMMUNICATIONS A/S + +OUI:0000FD* + ID_OUI_FROM_DATABASE=HIGH LEVEL HARDWARE + +OUI:08008C* + ID_OUI_FROM_DATABASE=NETWORK RESEARCH CORPORATION + +OUI:080089* + ID_OUI_FROM_DATABASE=Kinetics + +OUI:080084* + ID_OUI_FROM_DATABASE=TOMEN ELECTRONICS CORP. + +OUI:00000D* + ID_OUI_FROM_DATABASE=FIBRONICS LTD. + +OUI:00004F* + ID_OUI_FROM_DATABASE=LOGICRAFT, INC. + +OUI:000015* + ID_OUI_FROM_DATABASE=DATAPOINT CORPORATION + +OUI:0000C7* + ID_OUI_FROM_DATABASE=ARIX CORPORATION + +OUI:00001C* + ID_OUI_FROM_DATABASE=BELL TECHNOLOGIES + +OUI:00001A* + ID_OUI_FROM_DATABASE=ADVANCED MICRO DEVICES + +OUI:000082* + ID_OUI_FROM_DATABASE=LECTRA SYSTEMES SA + +OUI:0000DA* + ID_OUI_FROM_DATABASE=ATEX + +OUI:0000DB* + ID_OUI_FROM_DATABASE=British Telecommunications plc + +OUI:0000C1* + ID_OUI_FROM_DATABASE=Madge Ltd. + +OUI:0000F6* + ID_OUI_FROM_DATABASE=APPLIED MICROSYSTEMS CORP. + +OUI:080023* + ID_OUI_FROM_DATABASE=Panasonic Communications Co., Ltd. + +OUI:080022* + ID_OUI_FROM_DATABASE=NBI INC. + +OUI:080019* + ID_OUI_FROM_DATABASE=GENERAL ELECTRIC CORPORATION + +OUI:08004D* + ID_OUI_FROM_DATABASE=CORVUS SYSTEMS INC. + +OUI:08003E* + ID_OUI_FROM_DATABASE=CODEX CORPORATION + +OUI:080033* + ID_OUI_FROM_DATABASE=BAUSCH & LOMB + +OUI:08002F* + ID_OUI_FROM_DATABASE=PRIME COMPUTER INC. + +OUI:080032* + ID_OUI_FROM_DATABASE=TIGAN INCORPORATED + +OUI:08002E* + ID_OUI_FROM_DATABASE=METAPHOR COMPUTER SYSTEMS + +OUI:0000D2* + ID_OUI_FROM_DATABASE=SBE, INC. + +OUI:00006B* + ID_OUI_FROM_DATABASE=SILICON GRAPHICS INC./MIPS + +OUI:0000CC* + ID_OUI_FROM_DATABASE=DENSAN CO., LTD. + +OUI:0000CE* + ID_OUI_FROM_DATABASE=MEGADATA CORP. + +OUI:0000EF* + ID_OUI_FROM_DATABASE=KTI + +OUI:00000F* + ID_OUI_FROM_DATABASE=NEXT, INC. + +OUI:0000C6* + ID_OUI_FROM_DATABASE=EON SYSTEMS + +OUI:0000D5* + ID_OUI_FROM_DATABASE=MICROGNOSIS INTERNATIONAL + +OUI:000078* + ID_OUI_FROM_DATABASE=LABTAM LIMITED + +OUI:0000EB* + ID_OUI_FROM_DATABASE=MATSUSHITA COMM. IND. CO. LTD. + +OUI:00009C* + ID_OUI_FROM_DATABASE=ROLM MIL-SPEC COMPUTERS + +OUI:000032* + ID_OUI_FROM_DATABASE=Marconi plc + +OUI:000069* + ID_OUI_FROM_DATABASE=CONCORD COMMUNICATIONS INC + +OUI:00008B* + ID_OUI_FROM_DATABASE=INFOTRON + +OUI:0000BE* + ID_OUI_FROM_DATABASE=THE NTI GROUP + +OUI:00004C* + ID_OUI_FROM_DATABASE=NEC CORPORATION + +OUI:00003B* + ID_OUI_FROM_DATABASE=i Controls, Inc. + +OUI:0000FE* + ID_OUI_FROM_DATABASE=ANNAPOLIS MICRO SYSTEMS + +OUI:080013* + ID_OUI_FROM_DATABASE=Exxon + +OUI:02BB01* + ID_OUI_FROM_DATABASE=OCTOTHORPE CORP. + +OUI:0000A6* + ID_OUI_FROM_DATABASE=NETWORK GENERAL CORPORATION + +OUI:00DD06* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:00DD0B* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:000007* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:080014* + ID_OUI_FROM_DATABASE=EXCELAN + +OUI:08000F* + ID_OUI_FROM_DATABASE=MITEL CORPORATION + +OUI:0000D7* + ID_OUI_FROM_DATABASE=DARTMOUTH COLLEGE + +OUI:00DD00* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:08000A* + ID_OUI_FROM_DATABASE=NESTAR SYSTEMS INCORPORATED + +OUI:08001C* + ID_OUI_FROM_DATABASE=KDD-KOKUSAI DEBNSIN DENWA CO. + +OUI:02AA3C* + ID_OUI_FROM_DATABASE=OLIVETTI TELECOMM SPA (OLTECO) + +OUI:08001D* + ID_OUI_FROM_DATABASE=ABLE COMMUNICATIONS INC. + +OUI:080018* + ID_OUI_FROM_DATABASE=PIRELLI FOCOM NETWORKS + +OUI:080015* + ID_OUI_FROM_DATABASE=STC BUSINESS SYSTEMS + +OUI:00DD03* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:00801F* + ID_OUI_FROM_DATABASE=KRUPP ATLAS ELECTRONIK GMBH + +OUI:00408E* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:00800F* + ID_OUI_FROM_DATABASE=STANDARD MICROSYSTEMS + +OUI:080065* + ID_OUI_FROM_DATABASE=GENRAD INC. + +OUI:002275* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:149182* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:70106F* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:988B5D* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:94FEF4* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:C8CD72* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:E8BE81* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:28FAA0* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:3CA348* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:F42981* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:C4282D* + ID_OUI_FROM_DATABASE=Embedded Intellect Pty Ltd + +OUI:002348* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:B870F4* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:000FB0* + ID_OUI_FROM_DATABASE=COMPAL ELECTRONICS, INC. + +OUI:1C7508* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:8C0EE3* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:3829DD* + ID_OUI_FROM_DATABASE=ONvocal Inc + +OUI:F81897* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:ECF4BB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:D067E5* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:18A99B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8DB88* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:18FB7B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001495* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:74E6E2* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:109836* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:44A842* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:34E6D7* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:000BDB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001143* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00188B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:D4BED9* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:002650* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:00217C* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001FB3* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:640F28* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001AA0* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:002170* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:0026B9* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:A4BADB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001E4F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:5CF9DD* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:907AF1* + ID_OUI_FROM_DATABASE=Wally + +OUI:28101B* + ID_OUI_FROM_DATABASE=MagnaCom + +OUI:00065B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:448723* + ID_OUI_FROM_DATABASE=HOYA SERVICE CORPORATION + +OUI:806C1B* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:A470D6* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:3407FB* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:001B21* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001B77* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:18FF0F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:58A839* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A434D9* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00215D* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001676* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:984FEE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E82AEA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:605718* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C4D987* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B4B676* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:8C705A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:9C4E36* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:541473* + ID_OUI_FROM_DATABASE=Wingtech Group (HongKong)Limited + +OUI:14144B* + ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD + +OUI:001C50* + ID_OUI_FROM_DATABASE=TCL Technoly Electronics (Huizhou) Co., Ltd. + +OUI:00AA01* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:5C36B8* + ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd + +OUI:009027* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:A08869* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00C2C6* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B88A60* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00A0C9* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:7C7A91* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:AC7BA1* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:AC7289* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:606C66* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4C8093* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:BC7737* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A088B4* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00270E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001DE0* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0024D6* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:D8FC93* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E8B1FC* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:186472* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:00246C* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:64D954* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + +OUI:74C63B* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:CC1FC4* + ID_OUI_FROM_DATABASE=InVue + +OUI:A0D37A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:985FD3* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:00D0AC* + ID_OUI_FROM_DATABASE=Commscope, Inc + +OUI:0025D3* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:742F68* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:DC85DE* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:E0B9A5* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:E04136* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + +OUI:E0B2F1* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:0026FC* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:B8616F* + ID_OUI_FROM_DATABASE=Accton Technology Corp + +OUI:0010B5* + ID_OUI_FROM_DATABASE=Accton Technology Corp + +OUI:00A02F* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:6487D7* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:00E098* + ID_OUI_FROM_DATABASE=AboCom + +OUI:F0A225* + ID_OUI_FROM_DATABASE=Private + +OUI:0000B1* + ID_OUI_FROM_DATABASE=Alpha Micro + +OUI:001577* + ID_OUI_FROM_DATABASE=Allied Telesis, Inc. + +OUI:ACE010* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:EC086B* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:2421AB* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:6C23B9* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:58170C* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:B8F934* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:205476* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:303926* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:00EB2D* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:B00594* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:40F02F* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:E8617E* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:28E347* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:18CF5E* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:D0DF9A* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:0013A9* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:00219E* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001E45* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001813* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:002163* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:E839DF* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:00138F* + ID_OUI_FROM_DATABASE=Asiarock Technology Limited + +OUI:2CB05D* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00146C* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:1C69A5* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:003067* + ID_OUI_FROM_DATABASE=BIOSTAR Microtech Int'l Corp. + +OUI:246511* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:002308* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:880355* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:A42B8C* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:04A151* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:28C68E* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:5CDC96* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:504A6E* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:D0D04B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001D00* + ID_OUI_FROM_DATABASE=Brivo Systems, LLC + +OUI:0010E7* + ID_OUI_FROM_DATABASE=Breezecom, Ltd. + +OUI:5C9656* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:7C4CA5* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:902106* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:A4C7DE* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:343759* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:00402A* + ID_OUI_FROM_DATABASE=Canoga Perkins Corporation + +OUI:382DE8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D087E2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:205531* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5440AD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:842E27* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50F0D3* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:84119E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08ECA9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:10D38A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:382DD1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E0CBEE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:64B853* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:988389* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:244B03* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FC8F90* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1816C9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F4428F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:188331* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8455A5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A87C01* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C01173* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BCE63F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B857D8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:94B10A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E458B8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:088C2C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B86CE8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9C65B0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C8A823* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C44202* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D059E4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:64B310* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78ABBB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:000B3B* + ID_OUI_FROM_DATABASE=devolo AG + +OUI:001D20* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:6C38A1* + ID_OUI_FROM_DATABASE=Ubee Interactive Corp. + +OUI:140C76* + ID_OUI_FROM_DATABASE=FREEBOX SAS + +OUI:0024D4* + ID_OUI_FROM_DATABASE=FREEBOX SAS + +OUI:A089E4* + ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd + +OUI:001A9A* + ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd + +OUI:AC3A7A* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:CC6DA0* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:000D4B* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:001999* + ID_OUI_FROM_DATABASE=Fujitsu Technology Solutions GmbH + +OUI:0009E1* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:C477AB* + ID_OUI_FROM_DATABASE=Beijing ASU Tech Co.,Ltd + +OUI:182A7B* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0024F3* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:A45C27* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001DBC* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001F32* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:D8FB5E* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:544408* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:0017B0* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001BEE* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:1886AC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0021FE* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002266* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:DCB3B4* + ID_OUI_FROM_DATABASE=Honeywell Environmental & Combustion Controls (Tianjin) Co., Ltd. + +OUI:C8D10B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:C8979F* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F4F5A5* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:3CC243* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:0015A0* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001A16* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0022FC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002548* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001DFD* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001EA3* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001D98* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00119F* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:18A6F7* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:246968* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:8CA2FD* + ID_OUI_FROM_DATABASE=Starry, Inc. + +OUI:14BB6E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:AC61EA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:38B54D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90A62F* + ID_OUI_FROM_DATABASE=NAVER + +OUI:F4ED5F* + ID_OUI_FROM_DATABASE=SHENZHEN KTC TECHNOLOGY GROUP + +OUI:9476B7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8C1ABF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B47443* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:000BA2* + ID_OUI_FROM_DATABASE=Sumitomo Electric Industries,Ltd + +OUI:30CBF8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:2C4D79* + ID_OUI_FROM_DATABASE=GoerTek Inc. + +OUI:40D357* + ID_OUI_FROM_DATABASE=Ison Technology Co., Ltd. + +OUI:A4F1E8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00351A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00A0B8* + ID_OUI_FROM_DATABASE=NetApp + +OUI:9CD48B* + ID_OUI_FROM_DATABASE=Innolux Technology Europe BV + +OUI:545AA6* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:DCE838* + ID_OUI_FROM_DATABASE=CK Telecom (Shenzhen) Limited + +OUI:00CCFC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2C9662* + ID_OUI_FROM_DATABASE=Invenit BV + +OUI:DC2DCB* + ID_OUI_FROM_DATABASE=Beijing Unis HengYue Technology Co., Ltd. + +OUI:3810D5* + ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH + +OUI:1C5F2B* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:D8803C* + ID_OUI_FROM_DATABASE=Anhui Huami Information Technology Company Limited + +OUI:703C03* + ID_OUI_FROM_DATABASE=RadiAnt Co.,Ltd + +OUI:F0D2F1* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:583277* + ID_OUI_FROM_DATABASE=Reliance Communications LLC + +OUI:CCD3E2* + ID_OUI_FROM_DATABASE=Jiangsu Yinhe Electronics Co.,Ltd. + +OUI:182195* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A88195* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:88ADD2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:008E73* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B805AB* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:9C52F8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:900325* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DC094C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DCEE06* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:AC44F2* + ID_OUI_FROM_DATABASE=YAMAHA CORPORATION + +OUI:508965* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:808C97* + ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD. + +OUI:30B49E* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:349971* + ID_OUI_FROM_DATABASE=Quanta Storage Inc. + +OUI:24615A* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + +OUI:B0E2E5* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:AC0D1B* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A0043E* + ID_OUI_FROM_DATABASE=Parker Hannifin Manufacturing Germany GmbH & Co. KG + +OUI:5CC7D7* + ID_OUI_FROM_DATABASE=AZROAD TECHNOLOGY COMPANY LIMITED + +OUI:001706* + ID_OUI_FROM_DATABASE=Techfaithwireless Communication Technology Limited. + +OUI:30F6B9* + ID_OUI_FROM_DATABASE=Ecocentric Energy + +OUI:1C3ADE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:004268* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00BD82* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:603ECA* + ID_OUI_FROM_DATABASE=Cambridge Medical Robotics Ltd + +OUI:54489C* + ID_OUI_FROM_DATABASE=CDOUBLES ELECTRONICS CO. LTD. + +OUI:54BEF7* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:0C54A5* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:202564* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:600292* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:84002D* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:8019FE* + ID_OUI_FROM_DATABASE=JianLing Technology CO., LTD + +OUI:58605F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001188* + ID_OUI_FROM_DATABASE=Enterasys + +OUI:1078D2* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:001E90* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:002465* + ID_OUI_FROM_DATABASE=Elentec + +OUI:001CD7* + ID_OUI_FROM_DATABASE=Harman/Becker Automotive Systems GmbH + +OUI:0016EC* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:000D87* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:000AE6* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:945089* + ID_OUI_FROM_DATABASE=SimonsVoss Technologies GmbH + +OUI:001F1F* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:0016FA* + ID_OUI_FROM_DATABASE=ECI Telecom Ltd. + +OUI:003A7D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:844076* + ID_OUI_FROM_DATABASE=Drivenets + +OUI:0010E0* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:00144F* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:E80959* + ID_OUI_FROM_DATABASE=Guoguang Electric Co.,Ltd + +OUI:0090AE* + ID_OUI_FROM_DATABASE=ITALTEL S.p.A/RF-UP-I + +OUI:001E33* + ID_OUI_FROM_DATABASE=INVENTEC Corporation + +OUI:001A29* + ID_OUI_FROM_DATABASE=Johnson Outdoors Marine Electronics d/b/a Minnkota + +OUI:001F09* + ID_OUI_FROM_DATABASE=Jastec + +OUI:D0A4B1* + ID_OUI_FROM_DATABASE=Sonifex Ltd. + +OUI:001DB5* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:00239C* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:80711F* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:28C0DA* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:BCAD28* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:28F366* + ID_OUI_FROM_DATABASE=Shenzhen Bilian electronic CO.,LTD + +OUI:8828B3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C4F081* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:801382* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:648788* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:A8D0E5* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0881F4* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:6C3B6B* + ID_OUI_FROM_DATABASE=Routerboard.com + +OUI:7C738B* + ID_OUI_FROM_DATABASE=Cocoon Alarm Ltd + +OUI:000FE2* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:002389* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:3822D6* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:80F62E* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:5866BA* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:0CDA41* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:586AB1* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:741F4A* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:3CCB7C* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:F03404* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:D8E56D* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:90C1C6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70A2B3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4C57CA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68FB7E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:442C05* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:10BEF5* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:7C6AF3* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:C41CFF* + ID_OUI_FROM_DATABASE=Vizio, Inc + +OUI:444450* + ID_OUI_FROM_DATABASE=OttoQ + +OUI:FC55DC* + ID_OUI_FROM_DATABASE=Baltic Latvian Universal Electronics LLC + +OUI:941882* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:000EB6* + ID_OUI_FROM_DATABASE=Riverbed Technology, Inc. + +OUI:D0FCCC* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:045604* + ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co.,Ltd. + +OUI:10BD55* + ID_OUI_FROM_DATABASE=Q-Lab Corporation + +OUI:C449BB* + ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO.,LTD. + +OUI:8C6D50* + ID_OUI_FROM_DATABASE=SHENZHEN MTC CO LTD + +OUI:3C6816* + ID_OUI_FROM_DATABASE=VXi Corporation + +OUI:C0A1A2* + ID_OUI_FROM_DATABASE=MarqMetrix + +OUI:00F663* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:341290* + ID_OUI_FROM_DATABASE=Treeview Co.,Ltd. + +OUI:F40A4A* + ID_OUI_FROM_DATABASE=INDUSNET Communication Technology Co.,LTD + +OUI:E8377A* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:04BF6D* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:00A0C5* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:107BEF* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:C0C976* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:14C913* + ID_OUI_FROM_DATABASE=LG Electronics + +OUI:680715* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A09E1A* + ID_OUI_FROM_DATABASE=Polar Electro Oy + +OUI:3CB6B7* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:D0B2C4* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:FC94E3* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:FC528D* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:D84A87* + ID_OUI_FROM_DATABASE=OI ELECTRIC CO.,LTD + +OUI:BC307D* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:5410EC* + ID_OUI_FROM_DATABASE=Microchip Technology Inc. + +OUI:00262D* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:5CFF35* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:000AE4* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:309BAD* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:001BB1* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:000B6B* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:AC9B0A* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:4813F3* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:74B472* + ID_OUI_FROM_DATABASE=CIESSE + +OUI:483C0C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4C6641* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:C8755B* + ID_OUI_FROM_DATABASE=Quantify Technology Pty. Ltd. + +OUI:1C57D8* + ID_OUI_FROM_DATABASE=Kraftway Corporation PLC + +OUI:002397* + ID_OUI_FROM_DATABASE=Westell Technologies Inc. + +OUI:00600F* + ID_OUI_FROM_DATABASE=Westell Technologies Inc. + +OUI:00E0DD* + ID_OUI_FROM_DATABASE=Zenith Electronics Corporation + +OUI:50CE75* + ID_OUI_FROM_DATABASE=Measy Electronics Co., Ltd. + +OUI:047D7B* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:88124E* + ID_OUI_FROM_DATABASE=Qualcomm Inc. + +OUI:649C81* + ID_OUI_FROM_DATABASE=Qualcomm Inc. + +OUI:001B32* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:001E21* + ID_OUI_FROM_DATABASE=Qisda Corporation + +OUI:0017CA* + ID_OUI_FROM_DATABASE=Qisda Corporation + +OUI:0014D1* + ID_OUI_FROM_DATABASE=TRENDnet, Inc. + +OUI:001C7E* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:001C14* + ID_OUI_FROM_DATABASE=VMware, Inc. + +OUI:90A210* + ID_OUI_FROM_DATABASE=United Telecoms Ltd + +OUI:E02A82* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:001641* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:4C334E* + ID_OUI_FROM_DATABASE=HIGHTECH + +OUI:60E3AC* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:001315* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:001FA7* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:A8E3EE* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:709E29* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:FC0FE6* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:0050C2* + ID_OUI_FROM_DATABASE=IEEE Registration Authority + +OUI:CC79CF* + ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD. + +OUI:544E45* + ID_OUI_FROM_DATABASE=Private + +OUI:6CB9C5* + ID_OUI_FROM_DATABASE=Delta Networks, Inc. + +OUI:7CFC3C* + ID_OUI_FROM_DATABASE=Visteon Corporation + +OUI:58BC8F* + ID_OUI_FROM_DATABASE=Cognitive Systems Corp. + +OUI:54DC1D* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:3CBDD8* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:0C4885* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:88C9D0* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:700514* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:E892A4* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A816B2* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:C4438F* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:2021A5* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:6CD68A* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:001E75* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:0026E2* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:001FE3* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:2C54CF* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:F895C7* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:4888CA* + ID_OUI_FROM_DATABASE=Motorola (Wuhan) Mobility Technologies Communication Co., Ltd. + +OUI:74B57E* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:540955* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:88A6C6* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:000F59* + ID_OUI_FROM_DATABASE=Phonak AG + +OUI:000EF4* + ID_OUI_FROM_DATABASE=Kasda Networks Inc + +OUI:000AEB* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:2C3731* + ID_OUI_FROM_DATABASE=SHENZHEN YIFANG DIGITAL TECHNOLOGY CO.,LTD. + +OUI:001FBA* + ID_OUI_FROM_DATABASE=Boyoung Tech + +OUI:C4047B* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:A42940* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:3C3300* + ID_OUI_FROM_DATABASE=Shenzhen Bilian electronic CO.,LTD + +OUI:20F41B* + ID_OUI_FROM_DATABASE=Shenzhen Bilian electronic CO.,LTD + +OUI:3092F6* + ID_OUI_FROM_DATABASE=SHANGHAI SUNMON COMMUNICATION TECHNOGY CO.,LTD + +OUI:7C2064* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:E4A1E6* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:000B34* + ID_OUI_FROM_DATABASE=ShangHai Broadband Technologies CO.LTD + +OUI:38256B* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:203AEF* + ID_OUI_FROM_DATABASE=Sivantos GmbH + +OUI:001E40* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:80A1D7* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:D8FB68* + ID_OUI_FROM_DATABASE=Cloud Corner Ltd. + +OUI:C09134* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:4CB21C* + ID_OUI_FROM_DATABASE=Maxphotonics Co.,Ltd + +OUI:D8C46A* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:849866* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002162* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000F06* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000342* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00159B* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00140E* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0016CA* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001969* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0019E1* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001A8F* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:E89309* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00D0B2* + ID_OUI_FROM_DATABASE=19514 + +OUI:001988* + ID_OUI_FROM_DATABASE=Wi2Wi, Inc + +OUI:4CFACA* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:2C9D1E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C88D83* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:080087* + ID_OUI_FROM_DATABASE=Xyplex, Inc. + +OUI:00B0B3* + ID_OUI_FROM_DATABASE=XSTREAMIS PLC + +OUI:14825B* + ID_OUI_FROM_DATABASE=Hefei Radio Communication Technology Co., Ltd + +OUI:00549F* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:00562B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:24F57E* + ID_OUI_FROM_DATABASE=HWH CO., LTD. + +OUI:943DC9* + ID_OUI_FROM_DATABASE=Asahi Net, Inc. + +OUI:080028* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0012D2* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D494A1* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:78C5E5* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:847E40* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001832* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:90D7EB* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:BC0DA5* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:7C8EE4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D8543A* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:884AEA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B09122* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:209148* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A0F6FD* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D4F513* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017EC* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E5* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C83E99* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:8C8B83* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D0B5C2* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:84EB18* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:6CECEB* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:985DAD* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:E8EB11* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D43639* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A043DB* + ID_OUI_FROM_DATABASE=Sitael S.p.A. + +OUI:E4BEED* + ID_OUI_FROM_DATABASE=Netcore Technology Inc. + +OUI:84EF18* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:84C1C1* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:A8A648* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:305890* + ID_OUI_FROM_DATABASE=Frontier Silicon Ltd + +OUI:002261* + ID_OUI_FROM_DATABASE=Frontier Silicon Ltd + +OUI:049F81* + ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + +OUI:00808C* + ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + +OUI:C4F5A5* + ID_OUI_FROM_DATABASE=Kumalift Co., Ltd. + +OUI:98F058* + ID_OUI_FROM_DATABASE=Lynxspring, Incl. + +OUI:24E43F* + ID_OUI_FROM_DATABASE=Wenzhou Kunmei Communication Technology Co.,Ltd. + +OUI:A00460* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:946269* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D40598* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:78719C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E0B70A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:C83FB4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:207355* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:900DCB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:14CFE2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015D0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E86D52* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:3C438E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:90B134* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:20E564* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:40B7F3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:94CCB9* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00ACE0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:3C36E4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0000C5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D039B3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:8C7F3B* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:903EAB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:CCA462* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DCD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DCE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0050E3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00080E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00159A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00192C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D40AA9* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:384C90* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:44AAF5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:7085C6* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D0E54D* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:B4F2E8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:FC8E7E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:005094* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002143* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0023EE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:64ED57* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0023A3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:F87B7A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0025F1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001A66* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0018C0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001E46* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001ADE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0023AF* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:240AC4* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:5856E8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E4C1F1* + ID_OUI_FROM_DATABASE=SHENZHEN SPOTMAU INFORMATION TECHNOLIGY CO., Ltd + +OUI:240DC2* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:14DDE5* + ID_OUI_FROM_DATABASE=MPMKVVCL + +OUI:0016DB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5C3C27* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:10D542* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A0821F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C45006* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:88329B* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:BC8CCD* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:400E85* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:EC9BF3* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:F8042E* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:843838* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:54880E* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:BC79AD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:30D6C9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B0DF3A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:805719* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78A873* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:041BBA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08FD0E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08D42B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00E3B2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C81479* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F0728C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:94350A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001FCD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D0DFC7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1C62B8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:18E2C2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F04347* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9CB2B2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:84BE52* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001A8A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002567* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A8F274* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B07870* + ID_OUI_FROM_DATABASE=Wi-NEXT, Inc. + +OUI:001599* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0012FB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7CF854* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8CC8CD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E81132* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A02195* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:840B2D* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:000278* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:F07BCB* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:4C0F6E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:5C6D20* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:90004E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C0F8DA* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:485AB6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:083E8E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F4B7E2* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:4437E6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0016CF* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001C25* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C48E8F* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:184F32* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:441CA8* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:A8474A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:08EDB9* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:7CE9D3* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:E4D53D* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C417FE* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:38B1DB* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00234D* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00234E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00265E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:541379* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:1008B1* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:701DC4* + ID_OUI_FROM_DATABASE=NorthStar Battery Company, LLC + +OUI:801844* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:C80E14* + ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH + +OUI:E0686D* + ID_OUI_FROM_DATABASE=Raybased AB + +OUI:A45385* + ID_OUI_FROM_DATABASE=Weifang GoerTek Electronics Co., Ltd. + +OUI:000678* + ID_OUI_FROM_DATABASE=D&M Holdings Inc. + +OUI:98B039* + ID_OUI_FROM_DATABASE=Nokia + +OUI:84262B* + ID_OUI_FROM_DATABASE=Nokia + +OUI:94E98C* + ID_OUI_FROM_DATABASE=Nokia + +OUI:E48184* + ID_OUI_FROM_DATABASE=Nokia + +OUI:BC8D0E* + ID_OUI_FROM_DATABASE=Nokia + +OUI:B0754D* + ID_OUI_FROM_DATABASE=Nokia + +OUI:BC6B4D* + ID_OUI_FROM_DATABASE=Nokia + +OUI:A47B2C* + ID_OUI_FROM_DATABASE=Nokia + +OUI:00D0F6* + ID_OUI_FROM_DATABASE=Nokia + +OUI:48F8E1* + ID_OUI_FROM_DATABASE=Nokia + +OUI:002341* + ID_OUI_FROM_DATABASE=Vanderbilt International (SWE) AB + +OUI:981333* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8C71F8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:04180F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9463D1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0CDFA4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:CC051B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:68EBAE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:60D0A9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:60A10A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A07591* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D814D6* + ID_OUI_FROM_DATABASE=SURE SYSTEM Co Ltd + +OUI:646184* + ID_OUI_FROM_DATABASE=VELUX + +OUI:001FCC* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:EC01E2* + ID_OUI_FROM_DATABASE=FOXCONN INTERCONNECT TECHNOLOGY + +OUI:00F22C* + ID_OUI_FROM_DATABASE=Shanghai B-star Technology Co.,Ltd. + +OUI:D03DC3* + ID_OUI_FROM_DATABASE=AQ Corporation + +OUI:FCCAC4* + ID_OUI_FROM_DATABASE=LifeHealth, LLC + +OUI:04BA36* + ID_OUI_FROM_DATABASE=Li Seng Technology Ltd + +OUI:DCF090* + ID_OUI_FROM_DATABASE=Private + +OUI:4409B8* + ID_OUI_FROM_DATABASE=Salcomp (Shenzhen) CO., LTD. + +OUI:78888A* + ID_OUI_FROM_DATABASE=CDR Sp. z o.o. Sp. k. + +OUI:F09838* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:18DED7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:EC107B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A01081* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001EAE* + ID_OUI_FROM_DATABASE=Continental Automotive Systems Inc. + +OUI:9CF48E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:FCD848* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8048A5* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:645D92* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:D44165* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:643AB1* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:00010D* + ID_OUI_FROM_DATABASE=Teledyne DALSA Inc. + +OUI:F09FC2* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:0418D6* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:44D9E7* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:48DA96* + ID_OUI_FROM_DATABASE=Eddy Smart Home Solutions Inc. + +OUI:503AA0* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:C025E9* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:50B363* + ID_OUI_FROM_DATABASE=Digitron da Amazonia S/A + +OUI:94B819* + ID_OUI_FROM_DATABASE=Nokia + +OUI:DC0B34* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A4D9A4* + ID_OUI_FROM_DATABASE=neXus ID Solutions AB + +OUI:484D7E* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:8871E5* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:F4B549* + ID_OUI_FROM_DATABASE=Xiamen Yeastar Information Technology Co., Ltd. + +OUI:9C3DCF* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:28EED3* + ID_OUI_FROM_DATABASE=Shenzhen Super D Technology Co., Ltd + +OUI:18F292* + ID_OUI_FROM_DATABASE=Shannon Systems + +OUI:3C3F51* + ID_OUI_FROM_DATABASE=2CRSI + +OUI:F4F524* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:50584F* + ID_OUI_FROM_DATABASE=waytotec,Inc. + +OUI:00A2EE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0C6F9C* + ID_OUI_FROM_DATABASE=Shaw Communications Inc. + +OUI:1801E3* + ID_OUI_FROM_DATABASE=Bittium Wireless Ltd + +OUI:C0AC54* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:40F201* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:C891F9* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:4CFF12* + ID_OUI_FROM_DATABASE=Fuze Entertainment Co., ltd + +OUI:0059AC* + ID_OUI_FROM_DATABASE=KPN. B.V. + +OUI:AC9A22* + ID_OUI_FROM_DATABASE=NXP Semiconductors + +OUI:006037* + ID_OUI_FROM_DATABASE=NXP Semiconductors + +OUI:546009* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:A47733* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:94EB2C* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:28BC56* + ID_OUI_FROM_DATABASE=EMAC, Inc. + +OUI:287CDB* + ID_OUI_FROM_DATABASE=Hefei Toycloud Technology Co.,ltd + +OUI:D0B33F* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:00738D* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:A8CA7B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:ACCF85* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0CD746* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:440010* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2435CC* + ID_OUI_FROM_DATABASE=Zhongshan Scinan Internet of Things Co.,Ltd. + +OUI:2C27D7* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000F3D* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:001195* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:0015E9* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:0CFD37* + ID_OUI_FROM_DATABASE=SUSE Linux GmbH + +OUI:2CFF65* + ID_OUI_FROM_DATABASE=Oki Electric Industry Co., Ltd. + +OUI:001CF0* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:00265A* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:ACF1DF* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:FC7516* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:7C18CD* + ID_OUI_FROM_DATABASE=E-TRON Co.,Ltd. + +OUI:C8665D* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:3897D6* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:C8478C* + ID_OUI_FROM_DATABASE=Beken Corporation + +OUI:E498D6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:606944* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001977* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:4018B1* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:8896B6* + ID_OUI_FROM_DATABASE=Global Fire Equipment S.A. + +OUI:188796* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:AC2A0C* + ID_OUI_FROM_DATABASE=CSR ZHUZHOU INSTITUTE CO.,LTD. + +OUI:F4CA24* + ID_OUI_FROM_DATABASE=FreeBit Co., Ltd. + +OUI:000A57* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:643150* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:002376* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:0007E9* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:B46D83* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E4FAFD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:DC5360* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:780CB8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:484520* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:004026* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:0002A5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A02BB8* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:6CC217* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3863BB* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:CC3E5F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:7446A0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:443192* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:FC15B4* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:EC9A74* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:80C16E* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D07E28* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:7403BD* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:101F74* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001A4B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001F29* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:00215A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000F61* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001185* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001279* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001708* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2832C5* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:EC4D47* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:88CF98* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:6CE3B6* + ID_OUI_FROM_DATABASE=Nera Telecommunications Ltd. + +OUI:942CB3* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:0452F3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:241EEB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F431C3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C4F57C* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:8C7CFF* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:000CDB* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:006069* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:C87B5B* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:98F537* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:001E73* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0019C6* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0015EB* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:A051C6* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:24D921* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:848371* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:7052C5* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:001B4F* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:F0EBD0* + ID_OUI_FROM_DATABASE=Shanghai Feixun Communication Co.,Ltd. + +OUI:D8490B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:888603* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F8E811* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E09796* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:CCCC81* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:101B54* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:7054F5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D07AB5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C40528* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3CDFBD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:14B968* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:80717A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F49FF3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2C5BB8* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:B0AA36* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:784B87* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:28A183* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:5CF8A1* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:6021C0* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:84DBAC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C07009* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E0191D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B8BC1B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:241FA0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:50A72B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C85195* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00F81C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F4559C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:283CE4* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:64A5C3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001D0F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:5C63BF* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:B0487A* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:388345* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:14E6E4* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:647002* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:6466B3* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:6CE873* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:08E84F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:04BD70* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:18C58A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:04C06F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:5C4CA9* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4C5499* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00259E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001882* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D4EA0E* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:B4475E* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:90FB5B* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:14F65A* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:0C1DAF* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:28E31F* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:F0B429* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:00906F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090A6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090AB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7426AC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B000B4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2834A2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:641225* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:544A00* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5067AE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BC16F5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6899CD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F44E05* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0CF5A4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5CFC66* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D0A5A6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:3C5EC3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:64F69D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74A2E6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:204C9E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00112F* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0011D8* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001731* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0018F3* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:485B39* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:F46D04* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:3085A9* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:00900C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001079* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00102F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E08* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:00602F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:006070* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:006083* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00067C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C8D719* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:CC08E0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5855CA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C7B9D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:88C663* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C82A14* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9803D8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C5877* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3451C9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0B9BA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D023DB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B88D12* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B817C2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68A86D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:78A3E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:54781A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:58971E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:CCD539* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:20BBC0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4C4E35* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7CAD74* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:10F311* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:08CC68* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D0C789* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F84F57* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34DBFD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5CA48A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:AC7A4D* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:FC62B9* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:0010A6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E86549* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:84B517* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:046273* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:9C57AD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00223A* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:001839* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:001EE5* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:38C85C* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:F45FD4* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:002306* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:001E3D* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:0019C1* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:BC926B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0050E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:003065* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:000A27* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001451* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0019E3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002312* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002332* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002436* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00254B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0026BB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E80688* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:985AEB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2078F0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:78D75F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0ACCB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:98E0D9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C0CECD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70E72C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D03311* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:847D50* + ID_OUI_FROM_DATABASE=Holley Metering Limited + +OUI:6C4A39* + ID_OUI_FROM_DATABASE=BITA + +OUI:C8B5B7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A8BBCF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90B21F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B8E856* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1499E2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04214C* + ID_OUI_FROM_DATABASE=Insight Energy Ventures LLC + +OUI:B418D1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80006E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60D9C7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C8F650* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1C1AC0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E06678* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5C8D4E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:64A3CB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:44FB42* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F41BA1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3CE072* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E88D28* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CC785F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:AC3C0B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:88CB87* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:EC3586* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0C1F1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F4F951* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:18AF8F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C0F2FB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00F76F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:AC87A3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:48437C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34A395* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9CF387* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A85B78* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:908D6C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C1539* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC4CC4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0CBC9F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A45E60* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:680927* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60FACD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1CABA7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8CFABA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5C95AE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0C97A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC52B7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:14109F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:542696* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D8D1CB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4C8ECC* + ID_OUI_FROM_DATABASE=SILKAN SA + +OUI:3CEF8C* + ID_OUI_FROM_DATABASE=ZHEJIANG DAHUA TECHNOLOGY CO.,LTD. + +OUI:98F428* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:7C5A67* + ID_OUI_FROM_DATABASE=JNC Systems, Inc. + +OUI:C4BBEA* + ID_OUI_FROM_DATABASE=Pakedge Device and Software Inc + +OUI:84100D* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:D88B4C* + ID_OUI_FROM_DATABASE=KingTing Tech. + +OUI:E81363* + ID_OUI_FROM_DATABASE=Comstock RD, Inc. + +OUI:6C9354* + ID_OUI_FROM_DATABASE=Yaojin Technology (Shenzhen) Co., LTD. + +OUI:4054E4* + ID_OUI_FROM_DATABASE=Wearsafe Labs Inc + +OUI:8CE2DA* + ID_OUI_FROM_DATABASE=Circle Media Inc + +OUI:74D7CA* + ID_OUI_FROM_DATABASE=Panasonic Corporation Automotive + +OUI:1CCDE5* + ID_OUI_FROM_DATABASE=Shanghai Wind Technologies Co.,Ltd + +OUI:20896F* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:D494E8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B078F0* + ID_OUI_FROM_DATABASE=Beijing HuaqinWorld Technology Co.,Ltd. + +OUI:3029BE* + ID_OUI_FROM_DATABASE=Shanghai MRDcom Co.,Ltd + +OUI:7011AE* + ID_OUI_FROM_DATABASE=Music Life LTD + +OUI:ECB870* + ID_OUI_FROM_DATABASE=Beijing Heweinet Technology Co.,Ltd. + +OUI:3095E3* + ID_OUI_FROM_DATABASE=SHANGHAI SIMCOM LIMITED + +OUI:401B5F* + ID_OUI_FROM_DATABASE=Weifang GoerTek Electronics Co., Ltd. + +OUI:4040A7* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:54BE53* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:A01E0B* + ID_OUI_FROM_DATABASE=MINIX Technology Limited + +OUI:D48304* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:385F66* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:544E90* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:58FC73* + ID_OUI_FROM_DATABASE=Arria Live Media, Inc. + +OUI:2C1BC8* + ID_OUI_FROM_DATABASE=Hunan Topview Network System CO.,LTD + +OUI:5CADCF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:006D52* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D888CE* + ID_OUI_FROM_DATABASE=RF Technology Pty Ltd + +OUI:D4F4BE* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:B88687* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:68F956* + ID_OUI_FROM_DATABASE=Objetivos y Servicio de Valor Añadido + +OUI:58B633* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:AC60B6* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:F4E926* + ID_OUI_FROM_DATABASE=Tianjin Zanpu Technology Inc. + +OUI:04C23E* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:2CFCE4* + ID_OUI_FROM_DATABASE=CTEK Sweden AB + +OUI:C0B713* + ID_OUI_FROM_DATABASE=Beijing Xiaoyuer Technology Co. Ltd. + +OUI:DCA3AC* + ID_OUI_FROM_DATABASE=RBcloudtech + +OUI:44656A* + ID_OUI_FROM_DATABASE=Mega Video Electronic(HK) Industry Co., Ltd + +OUI:0C9160* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + +OUI:ECA9FA* + ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO.,LTD. + +OUI:300C23* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:445F8C* + ID_OUI_FROM_DATABASE=Intercel Group Limited + +OUI:A48D3B* + ID_OUI_FROM_DATABASE=Vizio, Inc + +OUI:0C756C* + ID_OUI_FROM_DATABASE=Anaren Microwave, Inc. + +OUI:5C5188* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:689AB7* + ID_OUI_FROM_DATABASE=Atelier Vision Corporation + +OUI:640DE6* + ID_OUI_FROM_DATABASE=Petra Systems + +OUI:283713* + ID_OUI_FROM_DATABASE=Shenzhen 3Nod Digital Technology Co., Ltd. + +OUI:7CAB25* + ID_OUI_FROM_DATABASE=MESMO TECHNOLOGY INC. + +OUI:74042B* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication (Wuhan) Company Limited + +OUI:4455B1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A45602* + ID_OUI_FROM_DATABASE=fenglian Technology Co.,Ltd. + +OUI:D06A1F* + ID_OUI_FROM_DATABASE=BSE CO.,LTD. + +OUI:A88038* + ID_OUI_FROM_DATABASE=ShenZhen MovingComm Technology Co., Limited + +OUI:805067* + ID_OUI_FROM_DATABASE=W & D TECHNOLOGY CORPORATION + +OUI:402814* + ID_OUI_FROM_DATABASE=RFI Engineering + +OUI:102C83* + ID_OUI_FROM_DATABASE=XIMEA + +OUI:D468BA* + ID_OUI_FROM_DATABASE=Shenzhen Sundray Technologies Company Limited + +OUI:A47B85* + ID_OUI_FROM_DATABASE=ULTIMEDIA Co Ltd, + +OUI:CC37AB* + ID_OUI_FROM_DATABASE=Edgecore Networks Corportation + +OUI:F80D60* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:E02CB2* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication (Wuhan) Company Limited + +OUI:DC15DB* + ID_OUI_FROM_DATABASE=Ge Ruili Intelligent Technology ( Beijing ) Co., Ltd. + +OUI:30F335* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E89120* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:546172* + ID_OUI_FROM_DATABASE=ZODIAC AEROSPACE SAS + +OUI:54CD10* + ID_OUI_FROM_DATABASE=Panasonic Mobile Communications Co.,Ltd. + +OUI:A4A1E4* + ID_OUI_FROM_DATABASE=Innotube, Inc. + +OUI:706879* + ID_OUI_FROM_DATABASE=Saijo Denki International Co., Ltd. + +OUI:343D98* + ID_OUI_FROM_DATABASE=JinQianMao Technology Co.,Ltd. + +OUI:5804CB* + ID_OUI_FROM_DATABASE=Tianjin Huisun Technology Co.,Ltd. + +OUI:1CB72C* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:40B837* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:287610* + ID_OUI_FROM_DATABASE=IgniteNet + +OUI:68A378* + ID_OUI_FROM_DATABASE=FREEBOX SAS + +OUI:746A3A* + ID_OUI_FROM_DATABASE=Aperi Corporation + +OUI:1844E6* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:A8D409* + ID_OUI_FROM_DATABASE=USA 111 Inc + +OUI:3089D3* + ID_OUI_FROM_DATABASE=HONGKONG UCLOUDLINK NETWORK TECHNOLOGY LIMITED + +OUI:4CB76D* + ID_OUI_FROM_DATABASE=Novi Security + +OUI:906CAC* + ID_OUI_FROM_DATABASE=Fortinet, Inc. + +OUI:00323A* + ID_OUI_FROM_DATABASE=so-logic + +OUI:64DB81* + ID_OUI_FROM_DATABASE=Syszone Co., Ltd. + +OUI:C4BAA3* + ID_OUI_FROM_DATABASE=Beijing Winicssec Technologies Co., Ltd. + +OUI:A013CB* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:20635F* + ID_OUI_FROM_DATABASE=Abeeway + +OUI:E00370* + ID_OUI_FROM_DATABASE=ShenZhen Continental Wireless Technology Co., Ltd. + +OUI:709C8F* + ID_OUI_FROM_DATABASE=Nero AG + +OUI:807459* + ID_OUI_FROM_DATABASE=K's Co.,Ltd. + +OUI:CC9635* + ID_OUI_FROM_DATABASE=LVS Co.,Ltd. + +OUI:700136* + ID_OUI_FROM_DATABASE=FATEK Automation Corporation + +OUI:E03560* + ID_OUI_FROM_DATABASE=Challenger Supply Holdings, LLC + +OUI:0CB5DE* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:04C9D9* + ID_OUI_FROM_DATABASE=EchoStar Technologies Corp + +OUI:E4CE70* + ID_OUI_FROM_DATABASE=Health & Life co., Ltd. + +OUI:EC5A86* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:F87AEF* + ID_OUI_FROM_DATABASE=Rosonix Technology, Inc. + +OUI:C43ABE* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:18B169* + ID_OUI_FROM_DATABASE=Sonicwall + +OUI:D4684D* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:1CC72D* + ID_OUI_FROM_DATABASE=Shenzhen Huapu Digital CO.,Ltd + +OUI:38D82F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C8D779* + ID_OUI_FROM_DATABASE=Qingdao Haier Telecom Co.,Ltd + +OUI:2CA2B4* + ID_OUI_FROM_DATABASE=Fortify Technologies, LLC + +OUI:D87495* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8C873B* + ID_OUI_FROM_DATABASE=Leica Camera AG + +OUI:28E476* + ID_OUI_FROM_DATABASE=Pi-Coral + +OUI:9C685B* + ID_OUI_FROM_DATABASE=Octonion SA + +OUI:ACABBF* + ID_OUI_FROM_DATABASE=AthenTek Inc. + +OUI:5C41E7* + ID_OUI_FROM_DATABASE=Wiatec International Ltd. + +OUI:DC0914* + ID_OUI_FROM_DATABASE=Talk-A-Phone Co. + +OUI:142971* + ID_OUI_FROM_DATABASE=NEMOA ELECTRONICS (HK) CO. LTD + +OUI:B47356* + ID_OUI_FROM_DATABASE=Hangzhou Treebear Networking Co., Ltd. + +OUI:D88D5C* + ID_OUI_FROM_DATABASE=Elentec + +OUI:50ADD5* + ID_OUI_FROM_DATABASE=Dynalec Corporation + +OUI:28D98A* + ID_OUI_FROM_DATABASE=Hangzhou Konke Technology Co.,Ltd. + +OUI:BC4DFB* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:40EACE* + ID_OUI_FROM_DATABASE=FOUNDER BROADBAND NETWORK SERVICE CO.,LTD + +OUI:10C67E* + ID_OUI_FROM_DATABASE=SHENZHEN JUCHIN TECHNOLOGY CO., LTD + +OUI:3C4937* + ID_OUI_FROM_DATABASE=ASSMANN Electronic GmbH + +OUI:904506* + ID_OUI_FROM_DATABASE=Tokyo Boeki Medisys Inc. + +OUI:80A85D* + ID_OUI_FROM_DATABASE=Osterhout Design Group + +OUI:9C6C15* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:EC74BA* + ID_OUI_FROM_DATABASE=Hirschmann Automation and Control GmbH + +OUI:683C7D* + ID_OUI_FROM_DATABASE=Magic Intelligence Technology Limited + +OUI:60128B* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:ECBAFE* + ID_OUI_FROM_DATABASE=GIROPTIC + +OUI:E8447E* + ID_OUI_FROM_DATABASE=Bitdefender SRL + +OUI:84C3E8* + ID_OUI_FROM_DATABASE=Vaillant GmbH + +OUI:B88EC6* + ID_OUI_FROM_DATABASE=Stateless Networks + +OUI:146B72* + ID_OUI_FROM_DATABASE=Shenzhen Fortune Ship Technology Co., Ltd. + +OUI:40A5EF* + ID_OUI_FROM_DATABASE=Shenzhen Four Seas Global Link Network Technology Co., Ltd. + +OUI:7C7A53* + ID_OUI_FROM_DATABASE=Phytrex Technology Corp. + +OUI:4886E8* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:78FC14* + ID_OUI_FROM_DATABASE=B Communications Pty Ltd + +OUI:88E161* + ID_OUI_FROM_DATABASE=Art Beijing Science and Technology Development Co., Ltd. + +OUI:B4A9FE* + ID_OUI_FROM_DATABASE=GHIA Technology (Shenzhen) LTD + +OUI:700FC7* + ID_OUI_FROM_DATABASE=SHENZHEN IKINLOOP TECHNOLOGY CO.,LTD. + +OUI:EC8009* + ID_OUI_FROM_DATABASE=NovaSparks + +OUI:64002D* + ID_OUI_FROM_DATABASE=Powerlinq Co., LTD + +OUI:101218* + ID_OUI_FROM_DATABASE=Korins Inc. + +OUI:B04515* + ID_OUI_FROM_DATABASE=mira fitness,LLC. + +OUI:307512* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:A49D49* + ID_OUI_FROM_DATABASE=Ketra, Inc. + +OUI:C09879* + ID_OUI_FROM_DATABASE=Acer Inc. + +OUI:1C9ECB* + ID_OUI_FROM_DATABASE=Beijing Nari Smartchip Microelectronics Company Limited + +OUI:D48DD9* + ID_OUI_FROM_DATABASE=Meld Technology, Inc + +OUI:2C3796* + ID_OUI_FROM_DATABASE=CYBO CO.,LTD. + +OUI:9470D2* + ID_OUI_FROM_DATABASE=WINFIRM TECHNOLOGY + +OUI:2C2997* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:4CE2F1* + ID_OUI_FROM_DATABASE=sclak srl + +OUI:344DEA* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:908C09* + ID_OUI_FROM_DATABASE=Total Phase + +OUI:1C7E51* + ID_OUI_FROM_DATABASE=3bumen.com + +OUI:60C798* + ID_OUI_FROM_DATABASE=Verifone, Inc. + +OUI:380E7B* + ID_OUI_FROM_DATABASE=V.P.S. Thai Co., Ltd + +OUI:38F33F* + ID_OUI_FROM_DATABASE=TATSUNO CORPORATION + +OUI:28A5EE* + ID_OUI_FROM_DATABASE=Shenzhen SDGI CATV Co., Ltd + +OUI:94CE31* + ID_OUI_FROM_DATABASE=CTS Limited + +OUI:4CBB58* + ID_OUI_FROM_DATABASE=Chicony Electronics Co., Ltd. + +OUI:C40006* + ID_OUI_FROM_DATABASE=Lipi Data Systems Ltd. + +OUI:789CE7* + ID_OUI_FROM_DATABASE=Shenzhen Aikede Technology Co., Ltd + +OUI:5C2ED2* + ID_OUI_FROM_DATABASE=ABC(XiSheng) Electronics Co.,Ltd + +OUI:D8F710* + ID_OUI_FROM_DATABASE=Libre Wireless Technologies Inc. + +OUI:68F728* + ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd + +OUI:DCEC06* + ID_OUI_FROM_DATABASE=Heimi Network Technology Co., Ltd. + +OUI:8870EF* + ID_OUI_FROM_DATABASE=SC Professional Trading Co., Ltd. + +OUI:102F6B* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:ACB74F* + ID_OUI_FROM_DATABASE=METEL s.r.o. + +OUI:CCF538* + ID_OUI_FROM_DATABASE=3isysnetworks + +OUI:04DEDB* + ID_OUI_FROM_DATABASE=Rockport Networks Inc + +OUI:68F06D* + ID_OUI_FROM_DATABASE=ALONG INDUSTRIAL CO., LIMITED + +OUI:54F876* + ID_OUI_FROM_DATABASE=ABB AG + +OUI:4857DD* + ID_OUI_FROM_DATABASE=Facebook + +OUI:84930C* + ID_OUI_FROM_DATABASE=InCoax Networks Europe AB + +OUI:D47B35* + ID_OUI_FROM_DATABASE=NEO Monitors AS + +OUI:D8FB11* + ID_OUI_FROM_DATABASE=AXACORE + +OUI:C8D019* + ID_OUI_FROM_DATABASE=Shanghai Tigercel Communication Technology Co.,Ltd + +OUI:18A958* + ID_OUI_FROM_DATABASE=PROVISION THAI CO., LTD. + +OUI:D8DECE* + ID_OUI_FROM_DATABASE=ISUNG CO.,LTD + +OUI:2053CA* + ID_OUI_FROM_DATABASE=Risk Technology Ltd + +OUI:142BD6* + ID_OUI_FROM_DATABASE=Guangdong Appscomm Co.,Ltd + +OUI:B025AA* + ID_OUI_FROM_DATABASE=Private + +OUI:408256* + ID_OUI_FROM_DATABASE=Continental Automotive GmbH + +OUI:D866EE* + ID_OUI_FROM_DATABASE=BOXIN COMMUNICATION CO.,LTD. + +OUI:3C189F* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:2829CC* + ID_OUI_FROM_DATABASE=Corsa Technology Incorporated + +OUI:FC790B* + ID_OUI_FROM_DATABASE=Hitachi High Technologies America, Inc. + +OUI:28E6E9* + ID_OUI_FROM_DATABASE=SIS Sat Internet Services GmbH + +OUI:BC4E5D* + ID_OUI_FROM_DATABASE=ZhongMiao Technology Co., Ltd. + +OUI:08F728* + ID_OUI_FROM_DATABASE=GLOBO Multimedia Sp. z o.o. Sp.k. + +OUI:70720D* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:8401A7* + ID_OUI_FROM_DATABASE=Greyware Automation Products, Inc + +OUI:C4C9EC* + ID_OUI_FROM_DATABASE=Gugaoo HK Limited + +OUI:F406A5* + ID_OUI_FROM_DATABASE=Hangzhou Bianfeng Networking Technology Co., Ltd. + +OUI:4C3909* + ID_OUI_FROM_DATABASE=HPL Electric & Power Private Limited + +OUI:7CFE4E* + ID_OUI_FROM_DATABASE=Shenzhen Safe vision Technology Co.,LTD + +OUI:54EF92* + ID_OUI_FROM_DATABASE=Shenzhen Elink Technology Co., LTD + +OUI:800E24* + ID_OUI_FROM_DATABASE=ForgetBox + +OUI:FCE186* + ID_OUI_FROM_DATABASE=A3M Co., LTD + +OUI:CCB691* + ID_OUI_FROM_DATABASE=NECMagnusCommunications + +OUI:40167E* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:C89F1D* + ID_OUI_FROM_DATABASE=SHENZHEN COMMUNICATION TECHNOLOGIES CO.,LTD + +OUI:983713* + ID_OUI_FROM_DATABASE=PT.Navicom Indonesia + +OUI:ACA919* + ID_OUI_FROM_DATABASE=TrekStor GmbH + +OUI:84850A* + ID_OUI_FROM_DATABASE=Hella Sonnen- und Wetterschutztechnik GmbH + +OUI:183009* + ID_OUI_FROM_DATABASE=Woojin Industrial Systems Co., Ltd. + +OUI:6081F9* + ID_OUI_FROM_DATABASE=Helium Systems, Inc + +OUI:34C5D0* + ID_OUI_FROM_DATABASE=Hagleitner Hygiene International GmbH + +OUI:74DBD1* + ID_OUI_FROM_DATABASE=Ebay Inc + +OUI:3431C4* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:DC537C* + ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc. + +OUI:A00627* + ID_OUI_FROM_DATABASE=NEXPA System + +OUI:303335* + ID_OUI_FROM_DATABASE=Boosty + +OUI:18D5B6* + ID_OUI_FROM_DATABASE=SMG Holdings LLC + +OUI:C8FF77* + ID_OUI_FROM_DATABASE=Dyson Limited + +OUI:C03D46* + ID_OUI_FROM_DATABASE=Shanghai Mochui Network Technology Co., Ltd + +OUI:DCF110* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:54DF00* + ID_OUI_FROM_DATABASE=Ulterius Technologies, LLC + +OUI:E01D38* + ID_OUI_FROM_DATABASE=Beijing HuaqinWorld Technology Co.,Ltd + +OUI:D80CCF* + ID_OUI_FROM_DATABASE=C.G.V. S.A.S. + +OUI:143DF2* + ID_OUI_FROM_DATABASE=Beijing Shidai Hongyuan Network Communication Co.,Ltd + +OUI:B0D59D* + ID_OUI_FROM_DATABASE=Shenzhen Zowee Technology Co., Ltd + +OUI:C4913A* + ID_OUI_FROM_DATABASE=Shenzhen Sanland Electronic Co., ltd. + +OUI:A46032* + ID_OUI_FROM_DATABASE=MRV Communications (Networks) LTD + +OUI:205A00* + ID_OUI_FROM_DATABASE=Coval + +OUI:0C2026* + ID_OUI_FROM_DATABASE=noax Technologies AG + +OUI:880FB6* + ID_OUI_FROM_DATABASE=Jabil Circuits India Pvt Ltd,-EHTP unit + +OUI:C4626B* + ID_OUI_FROM_DATABASE=ZPT Vigantice + +OUI:74F85D* + ID_OUI_FROM_DATABASE=Berkeley Nucleonics Corp + +OUI:48EE07* + ID_OUI_FROM_DATABASE=Silver Palm Technologies LLC + +OUI:9CFBF1* + ID_OUI_FROM_DATABASE=MESOMATIC GmbH & Co.KG + +OUI:94C014* + ID_OUI_FROM_DATABASE=Sorter Sp. j. Konrad Grzeszczyk MichaA, Ziomek + +OUI:1027BE* + ID_OUI_FROM_DATABASE=TVIP + +OUI:2087AC* + ID_OUI_FROM_DATABASE=AES motomation + +OUI:A824EB* + ID_OUI_FROM_DATABASE=ZAO NPO Introtest + +OUI:447E76* + ID_OUI_FROM_DATABASE=Trek Technology (S) Pte Ltd + +OUI:E8FC60* + ID_OUI_FROM_DATABASE=ELCOM Innovations Private Limited + +OUI:1CFCBB* + ID_OUI_FROM_DATABASE=Realfiction ApS + +OUI:B0EC8F* + ID_OUI_FROM_DATABASE=GMX SAS + +OUI:C40E45* + ID_OUI_FROM_DATABASE=ACK Networks,Inc. + +OUI:5C254C* + ID_OUI_FROM_DATABASE=Avire Global Pte Ltd + +OUI:7C1A03* + ID_OUI_FROM_DATABASE=8Locations Co., Ltd. + +OUI:481842* + ID_OUI_FROM_DATABASE=Shanghai Winaas Co. Equipment Co. Ltd. + +OUI:E817FC* + ID_OUI_FROM_DATABASE=NIFTY Corporation + +OUI:D09C30* + ID_OUI_FROM_DATABASE=Foster Electric Company, Limited + +OUI:78FEE2* + ID_OUI_FROM_DATABASE=Shanghai Diveo Technology Co., Ltd + +OUI:386C9B* + ID_OUI_FROM_DATABASE=Ivy Biomedical + +OUI:E44C6C* + ID_OUI_FROM_DATABASE=Shenzhen Guo Wei Electronic Co,. Ltd. + +OUI:008B43* + ID_OUI_FROM_DATABASE=RFTECH + +OUI:2C957F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:242642* + ID_OUI_FROM_DATABASE=SHARP Corporation. + +OUI:282246* + ID_OUI_FROM_DATABASE=Beijing Sinoix Communication Co., LTD + +OUI:FC1607* + ID_OUI_FROM_DATABASE=Taian Technology(Wuxi) Co.,Ltd. + +OUI:CC89FD* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:E86183* + ID_OUI_FROM_DATABASE=Black Diamond Advanced Technology, LLC + +OUI:C4824E* + ID_OUI_FROM_DATABASE=Changzhou Uchip Electronics Co., LTD. + +OUI:24A87D* + ID_OUI_FROM_DATABASE=Panasonic Automotive Systems Asia Pacific(Thailand)Co.,Ltd. + +OUI:78EC74* + ID_OUI_FROM_DATABASE=Kyland-USA + +OUI:28C825* + ID_OUI_FROM_DATABASE=DellKing Industrial Co., Ltd + +OUI:64E892* + ID_OUI_FROM_DATABASE=Morio Denki Co., Ltd. + +OUI:086DF2* + ID_OUI_FROM_DATABASE=Shenzhen MIMOWAVE Technology Co.,Ltd + +OUI:64EB8C* + ID_OUI_FROM_DATABASE=Seiko Epson Corporation + +OUI:48D0CF* + ID_OUI_FROM_DATABASE=Universal Electronics, Inc. + +OUI:DCC793* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:E03F49* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:D8EE78* + ID_OUI_FROM_DATABASE=Moog Protokraft + +OUI:F4B6E5* + ID_OUI_FROM_DATABASE=TerraSem Co.,Ltd + +OUI:28BB59* + ID_OUI_FROM_DATABASE=RNET Technologies, Inc. + +OUI:7C8D91* + ID_OUI_FROM_DATABASE=Shanghai Hongzhuo Information Technology co.,LTD + +OUI:A881F1* + ID_OUI_FROM_DATABASE=BMEYE B.V. + +OUI:241148* + ID_OUI_FROM_DATABASE=Entropix, LLC + +OUI:30B5C2* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F85C45* + ID_OUI_FROM_DATABASE=IC Nexus Co. Ltd. + +OUI:04DB8A* + ID_OUI_FROM_DATABASE=Suntech International Ltd. + +OUI:083F76* + ID_OUI_FROM_DATABASE=Intellian Technologies, Inc. + +OUI:0CC47A* + ID_OUI_FROM_DATABASE=Super Micro Computer, Inc. + +OUI:D0634D* + ID_OUI_FROM_DATABASE=Meiko Maschinenbau GmbH & Co. KG + +OUI:88C626* + ID_OUI_FROM_DATABASE=Logitech - Ultimate Ears + +OUI:889CA6* + ID_OUI_FROM_DATABASE=BTB Korea INC + +OUI:B0DA00* + ID_OUI_FROM_DATABASE=CERA ELECTRONIQUE + +OUI:447098* + ID_OUI_FROM_DATABASE=MING HONG TECHNOLOGY (SHEN ZHEN) LIMITED + +OUI:00EEBD* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:48B5A7* + ID_OUI_FROM_DATABASE=Glory Horse Industries Ltd. + +OUI:DC5E36* + ID_OUI_FROM_DATABASE=Paterson Technology + +OUI:50E0C7* + ID_OUI_FROM_DATABASE=TurControlSystme AG + +OUI:9CD643* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:28FC51* + ID_OUI_FROM_DATABASE=The Electric Controller and Manufacturing Co., LLC + +OUI:34A5E1* + ID_OUI_FROM_DATABASE=Sensorist ApS + +OUI:A4E9A3* + ID_OUI_FROM_DATABASE=Honest Technology Co., Ltd + +OUI:C4E92F* + ID_OUI_FROM_DATABASE=AB Sciex + +OUI:9C216A* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F862AA* + ID_OUI_FROM_DATABASE=xn systems + +OUI:A4059E* + ID_OUI_FROM_DATABASE=STA Infinity LLP + +OUI:6C15F9* + ID_OUI_FROM_DATABASE=Nautronix Limited + +OUI:680AD7* + ID_OUI_FROM_DATABASE=Yancheng Kecheng Optoelectronic Technology Co., Ltd + +OUI:BC8893* + ID_OUI_FROM_DATABASE=VILLBAU Ltd. + +OUI:643F5F* + ID_OUI_FROM_DATABASE=Exablaze + +OUI:E8F226* + ID_OUI_FROM_DATABASE=MILLSON CUSTOM SOLUTIONS INC. + +OUI:7060DE* + ID_OUI_FROM_DATABASE=LaVision GmbH + +OUI:FCFE77* + ID_OUI_FROM_DATABASE=Hitachi Reftechno, Inc. + +OUI:70533F* + ID_OUI_FROM_DATABASE=Alfa Instrumentos Eletronicos Ltda. + +OUI:448A5B* + ID_OUI_FROM_DATABASE=Micro-Star INT'L CO., LTD. + +OUI:68193F* + ID_OUI_FROM_DATABASE=Digital Airways + +OUI:5CD61F* + ID_OUI_FROM_DATABASE=Qardio, Inc + +OUI:902083* + ID_OUI_FROM_DATABASE=General Engine Management Systems Ltd. + +OUI:14B126* + ID_OUI_FROM_DATABASE=Industrial Software Co + +OUI:C03580* + ID_OUI_FROM_DATABASE=A&R TECH + +OUI:1446E4* + ID_OUI_FROM_DATABASE=AVISTEL + +OUI:907990* + ID_OUI_FROM_DATABASE=Benchmark Electronics Romania SRL + +OUI:C49380* + ID_OUI_FROM_DATABASE=Speedytel technology + +OUI:B4A82B* + ID_OUI_FROM_DATABASE=Histar Digital Electronics Co., Ltd. + +OUI:60A9B0* + ID_OUI_FROM_DATABASE=Merchandising Technologies, Inc + +OUI:007DFA* + ID_OUI_FROM_DATABASE=Volkswagen Group of America + +OUI:6024C1* + ID_OUI_FROM_DATABASE=Jiangsu Zhongxun Electronic Technology Co., Ltd + +OUI:6C5AB5* + ID_OUI_FROM_DATABASE=TCL Technoly Electronics (Huizhou) Co., Ltd. + +OUI:88789C* + ID_OUI_FROM_DATABASE=Game Technologies SA + +OUI:18AA45* + ID_OUI_FROM_DATABASE=Fon Technology + +OUI:549359* + ID_OUI_FROM_DATABASE=SHENZHEN TWOWING TECHNOLOGIES CO.,LTD. + +OUI:284430* + ID_OUI_FROM_DATABASE=GenesisTechnical Systems (UK) Ltd + +OUI:9843DA* + ID_OUI_FROM_DATABASE=INTERTECH + +OUI:285767* + ID_OUI_FROM_DATABASE=Echostar Technologies Corp + +OUI:B07908* + ID_OUI_FROM_DATABASE=Cummings Engineering + +OUI:04CB1D* + ID_OUI_FROM_DATABASE=Traka plc + +OUI:B87AC9* + ID_OUI_FROM_DATABASE=Siemens Ltd. + +OUI:B0989F* + ID_OUI_FROM_DATABASE=LG CNS + +OUI:3C300C* + ID_OUI_FROM_DATABASE=Dewar Electronics Pty Ltd + +OUI:78B5D2* + ID_OUI_FROM_DATABASE=Ever Treasure Industrial Limited + +OUI:A409CB* + ID_OUI_FROM_DATABASE=Alfred Kaercher GmbH & Co KG + +OUI:C445EC* + ID_OUI_FROM_DATABASE=Shanghai Yali Electron Co.,LTD + +OUI:E8611F* + ID_OUI_FROM_DATABASE=Dawning Information Industry Co.,Ltd + +OUI:0CA694* + ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd + +OUI:146080* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:986CF5* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:78491D* + ID_OUI_FROM_DATABASE=The Will-Burt Company + +OUI:74D435* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:840F45* + ID_OUI_FROM_DATABASE=Shanghai GMT Digital Technologies Co., Ltd + +OUI:D8270C* + ID_OUI_FROM_DATABASE=MaxTronic International Co., Ltd. + +OUI:E80410* + ID_OUI_FROM_DATABASE=Private + +OUI:8C088B* + ID_OUI_FROM_DATABASE=Remote Solution + +OUI:A47760* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:24A495* + ID_OUI_FROM_DATABASE=Thales Canada Inc. + +OUI:883612* + ID_OUI_FROM_DATABASE=SRC Computers, LLC + +OUI:E0A198* + ID_OUI_FROM_DATABASE=NOJA Power Switchgear Pty Ltd + +OUI:CC7B35* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:04D437* + ID_OUI_FROM_DATABASE=ZNV + +OUI:CCF407* + ID_OUI_FROM_DATABASE=EUKREA ELECTROMATIQUE SARL + +OUI:BC2BD7* + ID_OUI_FROM_DATABASE=Revogi Innovation Co., Ltd. + +OUI:24ECD6* + ID_OUI_FROM_DATABASE=CSG Science & Technology Co.,Ltd.Hefei + +OUI:102279* + ID_OUI_FROM_DATABASE=ZeroDesktop, Inc. + +OUI:CC4AE1* + ID_OUI_FROM_DATABASE=fourtec -Fourier Technologies + +OUI:A4895B* + ID_OUI_FROM_DATABASE=ARK INFOSOLUTIONS PVT LTD + +OUI:38EC11* + ID_OUI_FROM_DATABASE=Novatek Microelectronics Corp. + +OUI:A8CCC5* + ID_OUI_FROM_DATABASE=Saab AB (publ) + +OUI:988E4A* + ID_OUI_FROM_DATABASE=NOXUS(BEIJING) TECHNOLOGY CO.,LTD + +OUI:1C4158* + ID_OUI_FROM_DATABASE=Gemalto M2M GmbH + +OUI:ACD657* + ID_OUI_FROM_DATABASE=Shaanxi Guolian Digital TV Technology Co., Ltd. + +OUI:541B5D* + ID_OUI_FROM_DATABASE=Techno-Innov + +OUI:78CB33* + ID_OUI_FROM_DATABASE=DHC Software Co.,Ltd + +OUI:507691* + ID_OUI_FROM_DATABASE=Tekpea, Inc. + +OUI:C421C8* + ID_OUI_FROM_DATABASE=KYOCERA Corporation + +OUI:A4C0C7* + ID_OUI_FROM_DATABASE=ShenZhen Hitom Communication Technology Co..LTD + +OUI:EC2257* + ID_OUI_FROM_DATABASE=JiangSu NanJing University Electronic Information Technology Co.,Ltd + +OUI:341A4C* + ID_OUI_FROM_DATABASE=SHENZHEN WEIBU ELECTRONICS CO.,LTD. + +OUI:A09BBD* + ID_OUI_FROM_DATABASE=Total Aviation Solutions Pty Ltd + +OUI:E8481F* + ID_OUI_FROM_DATABASE=Advanced Automotive Antennas + +OUI:18D6CF* + ID_OUI_FROM_DATABASE=Kurth Electronic GmbH + +OUI:E07F88* + ID_OUI_FROM_DATABASE=EVIDENCE Network SIA + +OUI:1C7CC7* + ID_OUI_FROM_DATABASE=Coriant GmbH + +OUI:542CEA* + ID_OUI_FROM_DATABASE=PROTECTRON + +OUI:00C5DB* + ID_OUI_FROM_DATABASE=Datatech Sistemas Digitales Avanzados SL + +OUI:109AB9* + ID_OUI_FROM_DATABASE=Tosibox Oy + +OUI:F842FB* + ID_OUI_FROM_DATABASE=Yasuda Joho Co.,ltd. + +OUI:887398* + ID_OUI_FROM_DATABASE=K2E Tekpoint + +OUI:68EE96* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:FC6018* + ID_OUI_FROM_DATABASE=Zhejiang Kangtai Electric Co., Ltd. + +OUI:303EAD* + ID_OUI_FROM_DATABASE=Sonavox Canada Inc + +OUI:444A65* + ID_OUI_FROM_DATABASE=Silverflare Ltd. + +OUI:50A0BF* + ID_OUI_FROM_DATABASE=Alba Fiber Systems Inc. + +OUI:3C977E* + ID_OUI_FROM_DATABASE=IPS Technology Limited + +OUI:F02405* + ID_OUI_FROM_DATABASE=OPUS High Technology Corporation + +OUI:D8B04C* + ID_OUI_FROM_DATABASE=Jinan USR IOT Technology Co., Ltd. + +OUI:646EEA* + ID_OUI_FROM_DATABASE=Iskratel d.o.o. + +OUI:043D98* + ID_OUI_FROM_DATABASE=ChongQing QingJia Electronics CO.,LTD + +OUI:E8BB3D* + ID_OUI_FROM_DATABASE=Sino Prime-Tech Limited + +OUI:98CDB4* + ID_OUI_FROM_DATABASE=Virident Systems, Inc. + +OUI:54E3B0* + ID_OUI_FROM_DATABASE=JVL Industri Elektronik + +OUI:640B4A* + ID_OUI_FROM_DATABASE=Digital Telecom Technology Limited + +OUI:F42012* + ID_OUI_FROM_DATABASE=Cuciniale GmbH + +OUI:4C21D0* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:18104E* + ID_OUI_FROM_DATABASE=CEDINT-UPM + +OUI:2C7B84* + ID_OUI_FROM_DATABASE=OOO Petr Telegin + +OUI:540536* + ID_OUI_FROM_DATABASE=Vivago Oy + +OUI:2CE6CC* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:E0FAEC* + ID_OUI_FROM_DATABASE=Platan sp. z o.o. sp. k. + +OUI:F08EDB* + ID_OUI_FROM_DATABASE=VeloCloud Networks + +OUI:B8DC87* + ID_OUI_FROM_DATABASE=IAI Corporation + +OUI:7C6FF8* + ID_OUI_FROM_DATABASE=ShenZhen ACTO Digital Video Technology Co.,Ltd. + +OUI:8C4B59* + ID_OUI_FROM_DATABASE=3D Imaging & Simulations Corp + +OUI:A4FB8D* + ID_OUI_FROM_DATABASE=Hangzhou Dunchong Technology Co.Ltd + +OUI:0075E1* + ID_OUI_FROM_DATABASE=Ampt, LLC + +OUI:CC04B4* + ID_OUI_FROM_DATABASE=Select Comfort + +OUI:284FCE* + ID_OUI_FROM_DATABASE=Liaoning Wontel Science and Technology Development Co.,Ltd. + +OUI:0CC81F* + ID_OUI_FROM_DATABASE=Summer Infant, Inc. + +OUI:D86960* + ID_OUI_FROM_DATABASE=Steinsvik + +OUI:442AFF* + ID_OUI_FROM_DATABASE=E3 Technology, Inc. + +OUI:0C9301* + ID_OUI_FROM_DATABASE=PT. Prasimax Inovasi Teknologi + +OUI:60699B* + ID_OUI_FROM_DATABASE=isepos GmbH + +OUI:B830A8* + ID_OUI_FROM_DATABASE=Road-Track Telematics Development + +OUI:542160* + ID_OUI_FROM_DATABASE=Resolution Products + +OUI:88462A* + ID_OUI_FROM_DATABASE=Telechips Inc. + +OUI:A897DC* + ID_OUI_FROM_DATABASE=IBM + +OUI:E8DE27* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:FC229C* + ID_OUI_FROM_DATABASE=Han Kyung I Net Co.,Ltd. + +OUI:148692* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:1832A2* + ID_OUI_FROM_DATABASE=LAON TECHNOLOGY CO., LTD. + +OUI:6854ED* + ID_OUI_FROM_DATABASE=Alcatel-Lucent - Nuage + +OUI:985C93* + ID_OUI_FROM_DATABASE=SBG Systems SAS + +OUI:64E599* + ID_OUI_FROM_DATABASE=EFM Networks + +OUI:F499AC* + ID_OUI_FROM_DATABASE=WEBER Schraubautomaten GmbH + +OUI:8CC7D0* + ID_OUI_FROM_DATABASE=zhejiang ebang communication co.,ltd + +OUI:70820E* + ID_OUI_FROM_DATABASE=as electronics GmbH + +OUI:DC2BCA* + ID_OUI_FROM_DATABASE=Zera GmbH + +OUI:508D6F* + ID_OUI_FROM_DATABASE=CHAHOO Limited + +OUI:68831A* + ID_OUI_FROM_DATABASE=Pandora Mobility Corporation + +OUI:D4223F* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:0868D0* + ID_OUI_FROM_DATABASE=Japan System Design + +OUI:103DEA* + ID_OUI_FROM_DATABASE=HFC Technology (Beijing) Ltd. Co. + +OUI:E8E875* + ID_OUI_FROM_DATABASE=iS5 Communications Inc. + +OUI:2C7B5A* + ID_OUI_FROM_DATABASE=Milper Ltd + +OUI:185AE8* + ID_OUI_FROM_DATABASE=Zenotech.Co.,Ltd + +OUI:E0AEED* + ID_OUI_FROM_DATABASE=LOENK + +OUI:D4EE07* + ID_OUI_FROM_DATABASE=HIWIFI Co., Ltd. + +OUI:908260* + ID_OUI_FROM_DATABASE=IEEE 1904.1 Working Group + +OUI:FCAD0F* + ID_OUI_FROM_DATABASE=QTS NETWORKS + +OUI:984C04* + ID_OUI_FROM_DATABASE=Zhangzhou Keneng Electrical Equipment Co Ltd + +OUI:CC047C* + ID_OUI_FROM_DATABASE=G-WAY Microwave + +OUI:44F849* + ID_OUI_FROM_DATABASE=Union Pacific Railroad + +OUI:1CFA68* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D0BE2C* + ID_OUI_FROM_DATABASE=CNSLink Co., Ltd. + +OUI:281878* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:E457A8* + ID_OUI_FROM_DATABASE=Stuart Manufacturing, Inc. + +OUI:2481AA* + ID_OUI_FROM_DATABASE=KSH International Co., Ltd. + +OUI:789966* + ID_OUI_FROM_DATABASE=Musilab Electronics (DongGuan)Co.,Ltd. + +OUI:EC2C49* + ID_OUI_FROM_DATABASE=University of Tokyo + +OUI:CC5D57* + ID_OUI_FROM_DATABASE=Information System Research Institute,Inc. + +OUI:1C37BF* + ID_OUI_FROM_DATABASE=Cloudium Systems Ltd. + +OUI:249504* + ID_OUI_FROM_DATABASE=SFR + +OUI:308999* + ID_OUI_FROM_DATABASE=Guangdong East Power Co., + +OUI:D4A499* + ID_OUI_FROM_DATABASE=InView Technology Corporation + +OUI:AC4122* + ID_OUI_FROM_DATABASE=Eclipse Electronic Systems Inc. + +OUI:A073FC* + ID_OUI_FROM_DATABASE=Rancore Technologies Private Limited + +OUI:846223* + ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd. + +OUI:A4E991* + ID_OUI_FROM_DATABASE=SISTEMAS AUDIOVISUALES ITELSIS S.L. + +OUI:84F493* + ID_OUI_FROM_DATABASE=OMS spol. s.r.o. + +OUI:386793* + ID_OUI_FROM_DATABASE=Asia Optical Co., Inc. + +OUI:BCD177* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:C8B373* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:983071* + ID_OUI_FROM_DATABASE=DAIKYUNG VASCOM + +OUI:0C0400* + ID_OUI_FROM_DATABASE=Jantar d.o.o. + +OUI:C04301* + ID_OUI_FROM_DATABASE=Epec Oy + +OUI:687CD5* + ID_OUI_FROM_DATABASE=Y Soft Corporation, a.s. + +OUI:E07C62* + ID_OUI_FROM_DATABASE=Whistle Labs, Inc. + +OUI:FC4499* + ID_OUI_FROM_DATABASE=Swarco LEA d.o.o. + +OUI:0C8484* + ID_OUI_FROM_DATABASE=Zenovia Electronics Inc. + +OUI:5CF370* + ID_OUI_FROM_DATABASE=CC&C Technologies, Inc + +OUI:A01C05* + ID_OUI_FROM_DATABASE=NIMAX TELECOM CO.,LTD. + +OUI:F80DEA* + ID_OUI_FROM_DATABASE=ZyCast Technology Inc. + +OUI:1800DB* + ID_OUI_FROM_DATABASE=Fitbit Inc. + +OUI:50A715* + ID_OUI_FROM_DATABASE=Aboundi, Inc. + +OUI:FC35E6* + ID_OUI_FROM_DATABASE=Visteon corp + +OUI:D866C6* + ID_OUI_FROM_DATABASE=Shenzhen Daystar Technology Co.,ltd + +OUI:1836FC* + ID_OUI_FROM_DATABASE=Elecsys International Corporation + +OUI:F48139* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:D40BB9* + ID_OUI_FROM_DATABASE=Solid Semecs bv. + +OUI:748E08* + ID_OUI_FROM_DATABASE=Bestek Corp. + +OUI:B8C855* + ID_OUI_FROM_DATABASE=Shanghai GBCOM Communication Technology Co.,Ltd. + +OUI:C47DFE* + ID_OUI_FROM_DATABASE=A.N. Solutions GmbH + +OUI:E031D0* + ID_OUI_FROM_DATABASE=SZ Telstar CO., LTD + +OUI:70C6AC* + ID_OUI_FROM_DATABASE=Bosch Automotive Aftermarket + +OUI:2C69BA* + ID_OUI_FROM_DATABASE=RF Controls, LLC + +OUI:DC5726* + ID_OUI_FROM_DATABASE=Power-One + +OUI:2C245F* + ID_OUI_FROM_DATABASE=Babolat VS + +OUI:D464F7* + ID_OUI_FROM_DATABASE=CHENGDU USEE DIGITAL TECHNOLOGY CO., LTD + +OUI:A47ACF* + ID_OUI_FROM_DATABASE=VIBICOM COMMUNICATIONS INC. + +OUI:CC3C3F* + ID_OUI_FROM_DATABASE=SA.S.S. Datentechnik AG + +OUI:905692* + ID_OUI_FROM_DATABASE=Autotalks Ltd. + +OUI:0C2AE7* + ID_OUI_FROM_DATABASE=Beijing General Research Institute of Mining and Metallurgy + +OUI:DCD52A* + ID_OUI_FROM_DATABASE=Sunny Heart Limited + +OUI:C4C755* + ID_OUI_FROM_DATABASE=Beijing HuaqinWorld Technology Co.,Ltd + +OUI:9C79AC* + ID_OUI_FROM_DATABASE=Suntec Software(Shanghai) Co., Ltd. + +OUI:F8DFA8* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:ACA430* + ID_OUI_FROM_DATABASE=Peerless AV + +OUI:B4AB2C* + ID_OUI_FROM_DATABASE=MtM Technology Corporation + +OUI:74372F* + ID_OUI_FROM_DATABASE=Tongfang Shenzhen Cloudcomputing Technology Co.,Ltd + +OUI:BC51FE* + ID_OUI_FROM_DATABASE=Swann communications Pty Ltd + +OUI:D40FB2* + ID_OUI_FROM_DATABASE=Applied Micro Electronics AME bv + +OUI:74FE48* + ID_OUI_FROM_DATABASE=ADVANTECH CO., LTD. + +OUI:D0B498* + ID_OUI_FROM_DATABASE=Robert Bosch LLC Automotive Electronics + +OUI:80B95C* + ID_OUI_FROM_DATABASE=ELFTECH Co., Ltd. + +OUI:E85AA7* + ID_OUI_FROM_DATABASE=LLC Emzior + +OUI:242FFA* + ID_OUI_FROM_DATABASE=Toshiba Global Commerce Solutions + +OUI:A0BAB8* + ID_OUI_FROM_DATABASE=Pixon Imaging + +OUI:9CE1D6* + ID_OUI_FROM_DATABASE=Junger Audio-Studiotechnik GmbH + +OUI:E4E409* + ID_OUI_FROM_DATABASE=LEIFHEIT AG + +OUI:004D32* + ID_OUI_FROM_DATABASE=Andon Health Co.,Ltd. + +OUI:C46DF1* + ID_OUI_FROM_DATABASE=DataGravity + +OUI:28D244* + ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology Co., Ltd. + +OUI:ACE87E* + ID_OUI_FROM_DATABASE=Bytemark Computer Consulting Ltd + +OUI:60CDC5* + ID_OUI_FROM_DATABASE=Taiwan Carol Electronics., Ltd + +OUI:60C5A8* + ID_OUI_FROM_DATABASE=Beijing LT Honway Technology Co.,Ltd + +OUI:B4DF3B* + ID_OUI_FROM_DATABASE=Chromlech + +OUI:A46E79* + ID_OUI_FROM_DATABASE=DFT System Co.Ltd + +OUI:94DE80* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:C88A83* + ID_OUI_FROM_DATABASE=Dongguan HuaHong Electronics Co.,Ltd + +OUI:0CC655* + ID_OUI_FROM_DATABASE=Wuxi YSTen Technology Co.,Ltd. + +OUI:D410CF* + ID_OUI_FROM_DATABASE=Huanshun Network Science and Technology Co., Ltd. + +OUI:B80415* + ID_OUI_FROM_DATABASE=Bayan Audio + +OUI:84C8B1* + ID_OUI_FROM_DATABASE=Incognito Software Systems Inc. + +OUI:645A04* + ID_OUI_FROM_DATABASE=Chicony Electronics Co., Ltd. + +OUI:5C89D4* + ID_OUI_FROM_DATABASE=Beijing Banner Electric Co.,Ltd + +OUI:984CD3* + ID_OUI_FROM_DATABASE=Mantis Deposition + +OUI:8C4CDC* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:D063B4* + ID_OUI_FROM_DATABASE=SolidRun Ltd. + +OUI:2C3BFD* + ID_OUI_FROM_DATABASE=Netstor Technology Co., Ltd. + +OUI:F073AE* + ID_OUI_FROM_DATABASE=PEAK-System Technik + +OUI:684CA8* + ID_OUI_FROM_DATABASE=Shenzhen Herotel Tech. Co., Ltd. + +OUI:F4472A* + ID_OUI_FROM_DATABASE=Nanjing Rousing Sci. and Tech. Industrial Co., Ltd + +OUI:185253* + ID_OUI_FROM_DATABASE=Pixord Corporation + +OUI:FCA9B0* + ID_OUI_FROM_DATABASE=MIARTECH (SHANGHAI),INC. + +OUI:80D733* + ID_OUI_FROM_DATABASE=QSR Automations, Inc. + +OUI:8C3330* + ID_OUI_FROM_DATABASE=EmFirst Co., Ltd. + +OUI:8C0C90* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:08E5DA* + ID_OUI_FROM_DATABASE=NANJING FUJITSU COMPUTER PRODUCTS CO.,LTD. + +OUI:5884E4* + ID_OUI_FROM_DATABASE=IP500 Alliance e.V. + +OUI:04E9E5* + ID_OUI_FROM_DATABASE=PJRC.COM, LLC + +OUI:703811* + ID_OUI_FROM_DATABASE=Invensys Rail + +OUI:ACE64B* + ID_OUI_FROM_DATABASE=Shenzhen Baojia Battery Technology Co., Ltd. + +OUI:303294* + ID_OUI_FROM_DATABASE=W-IE-NE-R Plein & Baus GmbH + +OUI:EC473C* + ID_OUI_FROM_DATABASE=Redwire, LLC + +OUI:5481AD* + ID_OUI_FROM_DATABASE=Eagle Research Corporation + +OUI:7C822D* + ID_OUI_FROM_DATABASE=Nortec + +OUI:745FAE* + ID_OUI_FROM_DATABASE=TSL PPL + +OUI:8462A6* + ID_OUI_FROM_DATABASE=EuroCB (Phils), Inc. + +OUI:80FA5B* + ID_OUI_FROM_DATABASE=CLEVO CO. + +OUI:E4F365* + ID_OUI_FROM_DATABASE=Time-O-Matic, Inc. + +OUI:18550F* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:1C9179* + ID_OUI_FROM_DATABASE=Integrated System Technologies Ltd + +OUI:38F597* + ID_OUI_FROM_DATABASE=home2net GmbH + +OUI:386645* + ID_OUI_FROM_DATABASE=OOSIC Technology CO.,Ltd + +OUI:D0DFB2* + ID_OUI_FROM_DATABASE=Genie Networks Limited + +OUI:808B5C* + ID_OUI_FROM_DATABASE=Shenzhen Runhuicheng Technology Co., Ltd + +OUI:04586F* + ID_OUI_FROM_DATABASE=Sichuan Whayer information industry Co.,LTD + +OUI:449B78* + ID_OUI_FROM_DATABASE=The Now Factory + +OUI:D052A8* + ID_OUI_FROM_DATABASE=Physical Graph Corporation + +OUI:34F62D* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:C4EBE3* + ID_OUI_FROM_DATABASE=RRCN SAS + +OUI:4C1A95* + ID_OUI_FROM_DATABASE=Novakon Co., Ltd. + +OUI:C04A00* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:9C3178* + ID_OUI_FROM_DATABASE=Foshan Huadian Intelligent Communications Teachnologies Co.,Ltd + +OUI:48BE2D* + ID_OUI_FROM_DATABASE=Symanitron + +OUI:38E595* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:B86091* + ID_OUI_FROM_DATABASE=Onnet Technologies and Innovations LLC + +OUI:201A06* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:D4CA6E* + ID_OUI_FROM_DATABASE=u-blox AG + +OUI:C011A6* + ID_OUI_FROM_DATABASE=Fort-Telecom ltd. + +OUI:B8DAF1* + ID_OUI_FROM_DATABASE=Strahlenschutz- Entwicklungs- und Ausruestungsgesellschaft mbH + +OUI:1C11E1* + ID_OUI_FROM_DATABASE=Wartsila Finland Oy + +OUI:50465D* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:74BFA1* + ID_OUI_FROM_DATABASE=HYUNTECK + +OUI:F8AA8A* + ID_OUI_FROM_DATABASE=Axview Technology (Shenzhen) Co.,Ltd + +OUI:5894CF* + ID_OUI_FROM_DATABASE=Vertex Standard LMR, Inc. + +OUI:2C5AA3* + ID_OUI_FROM_DATABASE=PROMATE ELECTRONIC CO.LTD + +OUI:B4009C* + ID_OUI_FROM_DATABASE=CableWorld Ltd. + +OUI:803FD6* + ID_OUI_FROM_DATABASE=bytes at work AG + +OUI:645FFF* + ID_OUI_FROM_DATABASE=Nicolet Neuro + +OUI:2829D9* + ID_OUI_FROM_DATABASE=GlobalBeiMing technology (Beijing)Co. Ltd + +OUI:189A67* + ID_OUI_FROM_DATABASE=CSE-Servelec Limited + +OUI:38A5B6* + ID_OUI_FROM_DATABASE=SHENZHEN MEGMEET ELECTRICAL CO.,LTD + +OUI:E43FA2* + ID_OUI_FROM_DATABASE=Wuxi DSP Technologies Inc. + +OUI:00FD4C* + ID_OUI_FROM_DATABASE=NEVATEC + +OUI:6045BD* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:9C54CA* + ID_OUI_FROM_DATABASE=Zhengzhou VCOM Science and Technology Co.,Ltd + +OUI:388AB7* + ID_OUI_FROM_DATABASE=ITC Networks + +OUI:BCC23A* + ID_OUI_FROM_DATABASE=Thomson Video Networks + +OUI:00BF15* + ID_OUI_FROM_DATABASE=Genetec Inc. + +OUI:20F85E* + ID_OUI_FROM_DATABASE=Delta Electronics + +OUI:68CE4E* + ID_OUI_FROM_DATABASE=L-3 Communications Infrared Products + +OUI:68B6FC* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:7C160D* + ID_OUI_FROM_DATABASE=Saia-Burgess Controls AG + +OUI:A4D18F* + ID_OUI_FROM_DATABASE=Shenzhen Skyee Optical Fiber Communication Technology Ltd. + +OUI:0C565C* + ID_OUI_FROM_DATABASE=HyBroad Vision (Hong Kong) Technology Co Ltd + +OUI:647C34* + ID_OUI_FROM_DATABASE=Ubee Interactive Corp. + +OUI:649FF7* + ID_OUI_FROM_DATABASE=Kone OYj + +OUI:4C068A* + ID_OUI_FROM_DATABASE=Basler Electric Company + +OUI:E0A30F* + ID_OUI_FROM_DATABASE=Pevco + +OUI:5C1737* + ID_OUI_FROM_DATABASE=I-View Now, LLC. + +OUI:049C62* + ID_OUI_FROM_DATABASE=BMT Medical Technology s.r.o. + +OUI:C4BA99* + ID_OUI_FROM_DATABASE=I+ME Actia Informatik und Mikro-Elektronik GmbH + +OUI:0C2A69* + ID_OUI_FROM_DATABASE=electric imp, incorporated + +OUI:BC811F* + ID_OUI_FROM_DATABASE=Ingate Systems + +OUI:34E0CF* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:801DAA* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:6C40C6* + ID_OUI_FROM_DATABASE=Nimbus Data Systems, Inc. + +OUI:503F56* + ID_OUI_FROM_DATABASE=Syncmold Enterprise Corp + +OUI:D04CC1* + ID_OUI_FROM_DATABASE=SINTRONES Technology Corp. + +OUI:DC9FA4* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:44C39B* + ID_OUI_FROM_DATABASE=OOO RUBEZH NPO + +OUI:58C232* + ID_OUI_FROM_DATABASE=NEC Corporation + +OUI:D8C691* + ID_OUI_FROM_DATABASE=Hichan Technology Corp. + +OUI:7C02BC* + ID_OUI_FROM_DATABASE=Hansung Electronics Co. LTD + +OUI:1848D8* + ID_OUI_FROM_DATABASE=Fastback Networks + +OUI:702393* + ID_OUI_FROM_DATABASE=fos4X GmbH + +OUI:D8AFF1* + ID_OUI_FROM_DATABASE=Panasonic Appliances Company + +OUI:58ECE1* + ID_OUI_FROM_DATABASE=Newport Corporation + +OUI:14358B* + ID_OUI_FROM_DATABASE=Mediabridge Products, LLC. + +OUI:34996F* + ID_OUI_FROM_DATABASE=VPI Engineering + +OUI:241064* + ID_OUI_FROM_DATABASE=Shenzhen Ecsino Tecnical Co. Ltd + +OUI:10D1DC* + ID_OUI_FROM_DATABASE=INSTAR Deutschland GmbH + +OUI:D8160A* + ID_OUI_FROM_DATABASE=Nippon Electro-Sensory Devices + +OUI:F45433* + ID_OUI_FROM_DATABASE=Rockwell Automation + +OUI:EC9327* + ID_OUI_FROM_DATABASE=MEMMERT GmbH + Co. KG + +OUI:1C43EC* + ID_OUI_FROM_DATABASE=JAPAN CIRCUIT CO.,LTD + +OUI:BC28D6* + ID_OUI_FROM_DATABASE=Rowley Associates Limited + +OUI:F05F5A* + ID_OUI_FROM_DATABASE=Getriebebau NORD GmbH and Co. KG + +OUI:009569* + ID_OUI_FROM_DATABASE=LSD Science and Technology Co.,Ltd. + +OUI:34C803* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:5011EB* + ID_OUI_FROM_DATABASE=SilverNet Ltd + +OUI:5CD41B* + ID_OUI_FROM_DATABASE=UCZOON Technology Co., LTD + +OUI:783CE3* + ID_OUI_FROM_DATABASE=Kai-EE + +OUI:0868EA* + ID_OUI_FROM_DATABASE=EITO ELECTRONICS CO., LTD. + +OUI:5C4A26* + ID_OUI_FROM_DATABASE=Enguity Technology Corp + +OUI:289EDF* + ID_OUI_FROM_DATABASE=Danfoss Turbocor Compressors, Inc + +OUI:50053D* + ID_OUI_FROM_DATABASE=CyWee Group Ltd + +OUI:4C64D9* + ID_OUI_FROM_DATABASE=Guangdong Leawin Group Co., Ltd + +OUI:7CB03E* + ID_OUI_FROM_DATABASE=OSRAM GmbH + +OUI:14B1C8* + ID_OUI_FROM_DATABASE=InfiniWing, Inc. + +OUI:C0493D* + ID_OUI_FROM_DATABASE=MAITRISE TECHNOLOGIQUE + +OUI:34A7BA* + ID_OUI_FROM_DATABASE=Fischer International Systems Corporation + +OUI:ACD364* + ID_OUI_FROM_DATABASE=ABB SPA, ABB SACE DIV. + +OUI:38F8B7* + ID_OUI_FROM_DATABASE=V2COM PARTICIPACOES S.A. + +OUI:B48255* + ID_OUI_FROM_DATABASE=Research Products Corporation + +OUI:2C750F* + ID_OUI_FROM_DATABASE=Shanghai Dongzhou-Lawton Communication Technology Co. Ltd. + +OUI:B40418* + ID_OUI_FROM_DATABASE=Smartchip Integrated Inc. + +OUI:F4EA67* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D0AEEC* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:3C98BF* + ID_OUI_FROM_DATABASE=Quest Controls, Inc. + +OUI:D05785* + ID_OUI_FROM_DATABASE=Pantech Co., Ltd. + +OUI:045C06* + ID_OUI_FROM_DATABASE=Zmodo Technology Corporation + +OUI:504A5E* + ID_OUI_FROM_DATABASE=Masimo Corporation + +OUI:38BF33* + ID_OUI_FROM_DATABASE=NEC CASIO Mobile Communications + +OUI:A041A7* + ID_OUI_FROM_DATABASE=NL Ministry of Defense + +OUI:342F6E* + ID_OUI_FROM_DATABASE=Anywire corporation + +OUI:E86D6E* + ID_OUI_FROM_DATABASE=voestalpine SIGNALING Fareham Ltd. + +OUI:F8D462* + ID_OUI_FROM_DATABASE=Pumatronix Equipamentos Eletronicos Ltda. + +OUI:5453ED* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:940070* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:6C3A84* + ID_OUI_FROM_DATABASE=Shenzhen Aero-Startech. Co.Ltd + +OUI:442B03* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:781C5A* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:E4C6E6* + ID_OUI_FROM_DATABASE=Mophie, LLC + +OUI:502D1D* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BCEA2B* + ID_OUI_FROM_DATABASE=CityCom GmbH + +OUI:944444* + ID_OUI_FROM_DATABASE=LG Innotek + +OUI:E4C806* + ID_OUI_FROM_DATABASE=Ceiec Electric Technology Inc. + +OUI:18B591* + ID_OUI_FROM_DATABASE=I-Storm + +OUI:A45630* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002AAF* + ID_OUI_FROM_DATABASE=LARsys-Automation GmbH + +OUI:60F3DA* + ID_OUI_FROM_DATABASE=Logic Way GmbH + +OUI:A06D09* + ID_OUI_FROM_DATABASE=Intelcan Technosystems Inc. + +OUI:BC1401* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:68D925* + ID_OUI_FROM_DATABASE=ProSys Development Services + +OUI:B41DEF* + ID_OUI_FROM_DATABASE=Internet Laboratories, Inc. + +OUI:284121* + ID_OUI_FROM_DATABASE=OptiSense Network, LLC + +OUI:5057A8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:38458C* + ID_OUI_FROM_DATABASE=MyCloud Technology corporation + +OUI:0C9D56* + ID_OUI_FROM_DATABASE=Consort Controls Ltd + +OUI:3CCE73* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A47C14* + ID_OUI_FROM_DATABASE=ChargeStorm AB + +OUI:F4600D* + ID_OUI_FROM_DATABASE=Panoptic Technology, Inc + +OUI:ACCF23* + ID_OUI_FROM_DATABASE=Hi-flying electronics technology Co.,Ltd + +OUI:C08170* + ID_OUI_FROM_DATABASE=Effigis GeoSolutions + +OUI:78C4AB* + ID_OUI_FROM_DATABASE=Shenzhen Runsil Technology Co.,Ltd + +OUI:709A0B* + ID_OUI_FROM_DATABASE=Italian Institute of Technology + +OUI:240917* + ID_OUI_FROM_DATABASE=Devlin Electronics Limited + +OUI:DC37D2* + ID_OUI_FROM_DATABASE=Hunan HKT Electronic Technology Co., Ltd + +OUI:048B42* + ID_OUI_FROM_DATABASE=Skspruce Technology Limited + +OUI:5076A6* + ID_OUI_FROM_DATABASE=Ecil Informatica Ind. Com. Ltda + +OUI:B431B8* + ID_OUI_FROM_DATABASE=Aviwest + +OUI:241125* + ID_OUI_FROM_DATABASE=Hutek Co., Ltd. + +OUI:0036FE* + ID_OUI_FROM_DATABASE=SuperVision + +OUI:CC187B* + ID_OUI_FROM_DATABASE=Manzanita Systems, Inc. + +OUI:38B12D* + ID_OUI_FROM_DATABASE=Sonotronic Nagel GmbH + +OUI:8020AF* + ID_OUI_FROM_DATABASE=Trade FIDES, a.s. + +OUI:50D274* + ID_OUI_FROM_DATABASE=Steffes Corporation + +OUI:48D54C* + ID_OUI_FROM_DATABASE=Jeda Networks + +OUI:3497FB* + ID_OUI_FROM_DATABASE=ADVANCED RF TECHNOLOGIES INC + +OUI:C46413* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:143AEA* + ID_OUI_FROM_DATABASE=Dynapower Company LLC + +OUI:9CA134* + ID_OUI_FROM_DATABASE=Nike, Inc. + +OUI:B4D8A9* + ID_OUI_FROM_DATABASE=BetterBots + +OUI:7CC8D7* + ID_OUI_FROM_DATABASE=Damalisk + +OUI:0091FA* + ID_OUI_FROM_DATABASE=Synapse Product Development + +OUI:A05AA4* + ID_OUI_FROM_DATABASE=Grand Products Nevada, Inc. + +OUI:24C0B3* + ID_OUI_FROM_DATABASE=RSF + +OUI:E00B28* + ID_OUI_FROM_DATABASE=Inovonics + +OUI:500B32* + ID_OUI_FROM_DATABASE=Foxda Technology Industrial(ShenZhen)Co.,LTD + +OUI:302DE8* + ID_OUI_FROM_DATABASE=JDA, LLC (JDA Systems) + +OUI:70CA9B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2C3F38* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:803F5D* + ID_OUI_FROM_DATABASE=Winstars Technology Ltd + +OUI:780738* + ID_OUI_FROM_DATABASE=Z.U.K. Elzab S.A. + +OUI:640E36* + ID_OUI_FROM_DATABASE=TAZTAG + +OUI:70EE50* + ID_OUI_FROM_DATABASE=Netatmo + +OUI:EC63E5* + ID_OUI_FROM_DATABASE=ePBoard Design LLC + +OUI:60B606* + ID_OUI_FROM_DATABASE=Phorus + +OUI:F4E6D7* + ID_OUI_FROM_DATABASE=Solar Power Technologies, Inc. + +OUI:78DDD6* + ID_OUI_FROM_DATABASE=c-scape + +OUI:984A47* + ID_OUI_FROM_DATABASE=CHG Hospital Beds + +OUI:3C6A7D* + ID_OUI_FROM_DATABASE=Niigata Power Systems Co., Ltd. + +OUI:FC455F* + ID_OUI_FROM_DATABASE=JIANGXI SHANSHUI OPTOELECTRONIC TECHNOLOGY CO.,LTD + +OUI:3C7059* + ID_OUI_FROM_DATABASE=MakerBot Industries + +OUI:F8FE5C* + ID_OUI_FROM_DATABASE=Reciprocal Labs Corp + +OUI:6C9CED* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:94E0D0* + ID_OUI_FROM_DATABASE=HealthStream Taiwan Inc. + +OUI:DCF858* + ID_OUI_FROM_DATABASE=Lorent Networks, Inc. + +OUI:589396* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:A05E6B* + ID_OUI_FROM_DATABASE=MELPER Co., Ltd. + +OUI:30B3A2* + ID_OUI_FROM_DATABASE=Shenzhen Heguang Measurement & Control Technology Co.,Ltd + +OUI:F0007F* + ID_OUI_FROM_DATABASE=Janz - Contadores de Energia, SA + +OUI:CC944A* + ID_OUI_FROM_DATABASE=Pfeiffer Vacuum GmbH + +OUI:0C8525* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BCE59F* + ID_OUI_FROM_DATABASE=WATERWORLD Technology Co.,LTD + +OUI:1C5C55* + ID_OUI_FROM_DATABASE=PRIMA Cinema, Inc + +OUI:082522* + ID_OUI_FROM_DATABASE=ADVANSEE + +OUI:4C2F9D* + ID_OUI_FROM_DATABASE=ICM Controls + +OUI:E467BA* + ID_OUI_FROM_DATABASE=Danish Interpretation Systems A/S + +OUI:BCFE8C* + ID_OUI_FROM_DATABASE=Altronic, LLC + +OUI:24BBC1* + ID_OUI_FROM_DATABASE=Absolute Analysis + +OUI:7CDD11* + ID_OUI_FROM_DATABASE=Chongqing MAS SCI&TECH.Co.,Ltd + +OUI:C43C3C* + ID_OUI_FROM_DATABASE=CYBELEC SA + +OUI:00D632* + ID_OUI_FROM_DATABASE=GE Energy + +OUI:C40ACB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7463DF* + ID_OUI_FROM_DATABASE=VTS GmbH + +OUI:3828EA* + ID_OUI_FROM_DATABASE=Fujian Netcom Technology Co., LTD + +OUI:2CEE26* + ID_OUI_FROM_DATABASE=Petroleum Geo-Services + +OUI:DC3E51* + ID_OUI_FROM_DATABASE=Solberg & Andersen AS + +OUI:D8B90E* + ID_OUI_FROM_DATABASE=Triple Domain Vision Co.,Ltd. + +OUI:7C4B78* + ID_OUI_FROM_DATABASE=Red Sun Synthesis Pte Ltd + +OUI:28D1AF* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:68BC0C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2C9EFC* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:98C845* + ID_OUI_FROM_DATABASE=PacketAccess + +OUI:988217* + ID_OUI_FROM_DATABASE=Disruptive Ltd + +OUI:80FFA8* + ID_OUI_FROM_DATABASE=UNIDIS + +OUI:489BE2* + ID_OUI_FROM_DATABASE=SCI Innovations Ltd + +OUI:B0E50E* + ID_OUI_FROM_DATABASE=NRG SYSTEMS INC + +OUI:4C5FD2* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:E878A1* + ID_OUI_FROM_DATABASE=BEOVIEW INTERCOM DOO + +OUI:3057AC* + ID_OUI_FROM_DATABASE=IRLAB LTD. + +OUI:9002A9* + ID_OUI_FROM_DATABASE=ZHEJIANG DAHUA TECHNOLOGY CO.,LTD + +OUI:28AF0A* + ID_OUI_FROM_DATABASE=Sirius XM Radio Inc + +OUI:2486F4* + ID_OUI_FROM_DATABASE=Ctek, Inc. + +OUI:3CE5B4* + ID_OUI_FROM_DATABASE=KIDASEN INDUSTRIA E COMERCIO DE ANTENAS LTDA + +OUI:A85BF3* + ID_OUI_FROM_DATABASE=Audivo GmbH + +OUI:344F69* + ID_OUI_FROM_DATABASE=EKINOPS SAS + +OUI:C02973* + ID_OUI_FROM_DATABASE=Audyssey Laboratories Inc. + +OUI:30168D* + ID_OUI_FROM_DATABASE=ProLon + +OUI:B451F9* + ID_OUI_FROM_DATABASE=NB Software + +OUI:30688C* + ID_OUI_FROM_DATABASE=Reach Technology Inc. + +OUI:88F488* + ID_OUI_FROM_DATABASE=cellon communications technology(shenzhen)Co.,Ltd. + +OUI:0041B4* + ID_OUI_FROM_DATABASE=Wuxi Zhongxing Optoelectronics Technology Co.,Ltd. + +OUI:D453AF* + ID_OUI_FROM_DATABASE=VIGO System S.A. + +OUI:1CE192* + ID_OUI_FROM_DATABASE=Qisda Corporation + +OUI:20C8B3* + ID_OUI_FROM_DATABASE=SHENZHEN BUL-TECH CO.,LTD. + +OUI:58B0D4* + ID_OUI_FROM_DATABASE=ZuniData Systems Inc. + +OUI:64557F* + ID_OUI_FROM_DATABASE=NSFOCUS Information Technology Co., Ltd. + +OUI:406AAB* + ID_OUI_FROM_DATABASE=RIM + +OUI:248707* + ID_OUI_FROM_DATABASE=SEnergy Corporation + +OUI:EC3F05* + ID_OUI_FROM_DATABASE=Institute 706, The Second Academy China Aerospace Science & Industry Corp + +OUI:C4C19F* + ID_OUI_FROM_DATABASE=National Oilwell Varco Instrumentation, Monitoring, and Optimization (NOV IMO) + +OUI:68CD0F* + ID_OUI_FROM_DATABASE=U Tek Company Limited + +OUI:D4CEB8* + ID_OUI_FROM_DATABASE=Enatel LTD + +OUI:ECF236* + ID_OUI_FROM_DATABASE=NEOMONTANA ELECTRONICS + +OUI:E4A5EF* + ID_OUI_FROM_DATABASE=TRON LINK ELECTRONICS CO., LTD. + +OUI:AC4AFE* + ID_OUI_FROM_DATABASE=Hisense Broadband Multimedia Technology Co.,Ltd. + +OUI:2C1EEA* + ID_OUI_FROM_DATABASE=AERODEV + +OUI:FC6C31* + ID_OUI_FROM_DATABASE=LXinstruments GmbH + +OUI:3C6F45* + ID_OUI_FROM_DATABASE=Fiberpro Inc. + +OUI:B4FC75* + ID_OUI_FROM_DATABASE=SEMA Electronics(HK) CO.,LTD + +OUI:5C16C7* + ID_OUI_FROM_DATABASE=Big Switch Networks + +OUI:B0BF99* + ID_OUI_FROM_DATABASE=WIZITDONGDO + +OUI:147DB3* + ID_OUI_FROM_DATABASE=JOA TELECOM.CO.,LTD + +OUI:3CD16E* + ID_OUI_FROM_DATABASE=Telepower Communication Co., Ltd + +OUI:00077D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1045BE* + ID_OUI_FROM_DATABASE=Norphonic AS + +OUI:A0E295* + ID_OUI_FROM_DATABASE=DAT System Co.,Ltd + +OUI:40F14C* + ID_OUI_FROM_DATABASE=ISE Europe SPRL + +OUI:98293F* + ID_OUI_FROM_DATABASE=Fujian Start Computer Equipment Co.,Ltd + +OUI:70D4F2* + ID_OUI_FROM_DATABASE=RIM + +OUI:9067F3* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:64D912* + ID_OUI_FROM_DATABASE=Solidica, Inc. + +OUI:8C5CA1* + ID_OUI_FROM_DATABASE=d-broad,INC + +OUI:C8F981* + ID_OUI_FROM_DATABASE=Seneca s.r.l. + +OUI:703187* + ID_OUI_FROM_DATABASE=ACX GmbH + +OUI:14307A* + ID_OUI_FROM_DATABASE=Avermetrics + +OUI:8C7EB3* + ID_OUI_FROM_DATABASE=Lytro, Inc. + +OUI:587675* + ID_OUI_FROM_DATABASE=Beijing ECHO Technologies Co.,Ltd + +OUI:78EF4C* + ID_OUI_FROM_DATABASE=Unetconvergence Co., Ltd. + +OUI:E8DA96* + ID_OUI_FROM_DATABASE=Zhuhai Tianrui Electrical Power Tech. Co., Ltd. + +OUI:6CA780* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:04888C* + ID_OUI_FROM_DATABASE=Eifelwerk Butler Systeme GmbH + +OUI:1013EE* + ID_OUI_FROM_DATABASE=Justec International Technology INC. + +OUI:704642* + ID_OUI_FROM_DATABASE=CHYNG HONG ELECTRONIC CO., LTD. + +OUI:78BEB6* + ID_OUI_FROM_DATABASE=Enhanced Vision + +OUI:ECEA03* + ID_OUI_FROM_DATABASE=DARFON LIGHTING CORP + +OUI:C8903E* + ID_OUI_FROM_DATABASE=Pakton Technologies + +OUI:7465D1* + ID_OUI_FROM_DATABASE=Atlinks + +OUI:301A28* + ID_OUI_FROM_DATABASE=Mako Networks Ltd + +OUI:D4945A* + ID_OUI_FROM_DATABASE=COSMO CO., LTD + +OUI:5CF207* + ID_OUI_FROM_DATABASE=Speco Technologies + +OUI:B01B7C* + ID_OUI_FROM_DATABASE=Ontrol A.S. + +OUI:D47B75* + ID_OUI_FROM_DATABASE=HARTING Electronics GmbH + +OUI:70E843* + ID_OUI_FROM_DATABASE=Beijing C&W Optical Communication Technology Co.,Ltd. + +OUI:08ACA5* + ID_OUI_FROM_DATABASE=Benu Video, Inc. + +OUI:D89DB9* + ID_OUI_FROM_DATABASE=eMegatech International Corp. + +OUI:405A9B* + ID_OUI_FROM_DATABASE=ANOVO + +OUI:ACCA54* + ID_OUI_FROM_DATABASE=Telldus Technologies AB + +OUI:CC1EFF* + ID_OUI_FROM_DATABASE=Metrological Group BV + +OUI:941673* + ID_OUI_FROM_DATABASE=Point Core SARL + +OUI:6C5D63* + ID_OUI_FROM_DATABASE=ShenZhen Rapoo Technology Co., Ltd. + +OUI:E4D71D* + ID_OUI_FROM_DATABASE=Oraya Therapeutics + +OUI:C8FE30* + ID_OUI_FROM_DATABASE=Bejing DAYO Mobile Communication Technology Ltd. + +OUI:64B64A* + ID_OUI_FROM_DATABASE=ViVOtech, Inc. + +OUI:DCA7D9* + ID_OUI_FROM_DATABASE=Compressor Controls Corp + +OUI:C455A6* + ID_OUI_FROM_DATABASE=Cadac Holdings Ltd + +OUI:BCBBC9* + ID_OUI_FROM_DATABASE=Kellendonk Elektronik GmbH + +OUI:781DFD* + ID_OUI_FROM_DATABASE=Jabil Inc + +OUI:103711* + ID_OUI_FROM_DATABASE=Simlink AS + +OUI:601199* + ID_OUI_FROM_DATABASE=Siama Systems Inc + +OUI:300B9C* + ID_OUI_FROM_DATABASE=Delta Mobile Systems, Inc. + +OUI:90EA60* + ID_OUI_FROM_DATABASE=SPI Lasers Ltd + +OUI:D46F42* + ID_OUI_FROM_DATABASE=WAXESS USA Inc + +OUI:B0A72A* + ID_OUI_FROM_DATABASE=Ensemble Designs, Inc. + +OUI:50795B* + ID_OUI_FROM_DATABASE=Interexport Telecomunicaciones S.A. + +OUI:E8C229* + ID_OUI_FROM_DATABASE=H-Displays (MSC) Bhd + +OUI:B0BDA1* + ID_OUI_FROM_DATABASE=ZAKLAD ELEKTRONICZNY SIMS + +OUI:8C4435* + ID_OUI_FROM_DATABASE=Shanghai BroadMobi Communication Technology Co., Ltd. + +OUI:24B8D2* + ID_OUI_FROM_DATABASE=Opzoon Technology Co.,Ltd. + +OUI:24CBE7* + ID_OUI_FROM_DATABASE=MYK, Inc. + +OUI:88BFD5* + ID_OUI_FROM_DATABASE=Simple Audio Ltd + +OUI:948B03* + ID_OUI_FROM_DATABASE=EAGET Innovation and Technology Co., Ltd. + +OUI:802DE1* + ID_OUI_FROM_DATABASE=Solarbridge Technologies + +OUI:F081AF* + ID_OUI_FROM_DATABASE=IRZ AUTOMATION TECHNOLOGIES LTD + +OUI:14EB33* + ID_OUI_FROM_DATABASE=BSMediasoft Co., Ltd. + +OUI:AC8674* + ID_OUI_FROM_DATABASE=Open Mesh, Inc. + +OUI:14A9E3* + ID_OUI_FROM_DATABASE=MST CORPORATION + +OUI:589835* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:50D6D7* + ID_OUI_FROM_DATABASE=Takahata Precision + +OUI:B4A5A9* + ID_OUI_FROM_DATABASE=MODI GmbH + +OUI:D09B05* + ID_OUI_FROM_DATABASE=Emtronix + +OUI:98EC65* + ID_OUI_FROM_DATABASE=Cosesy ApS + +OUI:900917* + ID_OUI_FROM_DATABASE=Far-sighted mobile + +OUI:88F077* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:AC4723* + ID_OUI_FROM_DATABASE=Genelec + +OUI:20B7C0* + ID_OUI_FROM_DATABASE=OMICRON electronics GmbH + +OUI:D42C3D* + ID_OUI_FROM_DATABASE=Sky Light Digital Limited + +OUI:806CBC* + ID_OUI_FROM_DATABASE=NET New Electronic Technology GmbH + +OUI:1C184A* + ID_OUI_FROM_DATABASE=ShenZhen RicherLink Technologies Co.,LTD + +OUI:04E662* + ID_OUI_FROM_DATABASE=Acroname Inc. + +OUI:F0BF97* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:C44AD0* + ID_OUI_FROM_DATABASE=FIREFLIES SYSTEMS + +OUI:88E0A0* + ID_OUI_FROM_DATABASE=Shenzhen VisionSTOR Technologies Co., Ltd + +OUI:6879ED* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:9CC0D2* + ID_OUI_FROM_DATABASE=Conductix-Wampfler GmbH + +OUI:408BF6* + ID_OUI_FROM_DATABASE=Shenzhen TCL New Technology Co; Ltd. + +OUI:447E95* + ID_OUI_FROM_DATABASE=Alpha and Omega, Inc + +OUI:E8B748* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:DC16A2* + ID_OUI_FROM_DATABASE=Medtronic Diabetes + +OUI:78CA04* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:2C8BF2* + ID_OUI_FROM_DATABASE=Hitachi Metals America Ltd + +OUI:58F98E* + ID_OUI_FROM_DATABASE=SECUDOS GmbH + +OUI:2826A6* + ID_OUI_FROM_DATABASE=PBR electronics GmbH + +OUI:CC7669* + ID_OUI_FROM_DATABASE=SEETECH + +OUI:E437D7* + ID_OUI_FROM_DATABASE=HENRI DEPAEPE S.A.S. + +OUI:582F42* + ID_OUI_FROM_DATABASE=Universal Electric Corporation + +OUI:AC20AA* + ID_OUI_FROM_DATABASE=DMATEK Co., Ltd. + +OUI:E0A1D7* + ID_OUI_FROM_DATABASE=SFR + +OUI:28852D* + ID_OUI_FROM_DATABASE=Touch Networks + +OUI:F02A61* + ID_OUI_FROM_DATABASE=Waldo Networks, Inc. + +OUI:B8415F* + ID_OUI_FROM_DATABASE=ASP AG + +OUI:2CB69D* + ID_OUI_FROM_DATABASE=RED Digital Cinema + +OUI:988E34* + ID_OUI_FROM_DATABASE=ZHEJIANG BOXSAM ELECTRONIC CO.,LTD + +OUI:D44C24* + ID_OUI_FROM_DATABASE=Vuppalamritha Magnetic Components LTD + +OUI:4CB4EA* + ID_OUI_FROM_DATABASE=HRD (S) PTE., LTD. + +OUI:34BDF9* + ID_OUI_FROM_DATABASE=Shanghai WDK Industrial Co.,Ltd. + +OUI:74CE56* + ID_OUI_FROM_DATABASE=Packet Force Technology Limited Company + +OUI:A89B10* + ID_OUI_FROM_DATABASE=inMotion Ltd. + +OUI:888C19* + ID_OUI_FROM_DATABASE=Brady Corp Asia Pacific Ltd + +OUI:747DB6* + ID_OUI_FROM_DATABASE=Aliwei Communications, Inc + +OUI:B41489* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:AC6F4F* + ID_OUI_FROM_DATABASE=Enspert Inc + +OUI:8886A0* + ID_OUI_FROM_DATABASE=Simton Technologies, Ltd. + +OUI:F0C88C* + ID_OUI_FROM_DATABASE=LeddarTech Inc. + +OUI:68EBC5* + ID_OUI_FROM_DATABASE=Angstrem Telecom + +OUI:448C52* + ID_OUI_FROM_DATABASE=KTIS CO., Ltd + +OUI:686359* + ID_OUI_FROM_DATABASE=Advanced Digital Broadcast SA + +OUI:4018D7* + ID_OUI_FROM_DATABASE=Smartronix, Inc. + +OUI:18922C* + ID_OUI_FROM_DATABASE=Virtual Instruments + +OUI:F80F84* + ID_OUI_FROM_DATABASE=Natural Security SAS + +OUI:EC9ECD* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:303955* + ID_OUI_FROM_DATABASE=Shenzhen Jinhengjia Electronic Co., Ltd. + +OUI:FC5B24* + ID_OUI_FROM_DATABASE=Weibel Scientific A/S + +OUI:34B571* + ID_OUI_FROM_DATABASE=PLDS + +OUI:A862A2* + ID_OUI_FROM_DATABASE=JIWUMEDIA CO., LTD. + +OUI:984E97* + ID_OUI_FROM_DATABASE=Starlight Marketing (H. K.) Ltd. + +OUI:7C6ADB* + ID_OUI_FROM_DATABASE=SafeTone Technology Co.,Ltd + +OUI:EC986C* + ID_OUI_FROM_DATABASE=Lufft Mess- und Regeltechnik GmbH + +OUI:B0518E* + ID_OUI_FROM_DATABASE=Holl technology CO.Ltd. + +OUI:DCDECA* + ID_OUI_FROM_DATABASE=Akyllor + +OUI:A071A9* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:8065E9* + ID_OUI_FROM_DATABASE=BenQ Corporation + +OUI:845DD7* + ID_OUI_FROM_DATABASE=Shenzhen Netcom Electronics Co.,Ltd + +OUI:447DA5* + ID_OUI_FROM_DATABASE=VTION INFORMATION TECHNOLOGY (FUJIAN) CO.,LTD + +OUI:0CCDD3* + ID_OUI_FROM_DATABASE=EASTRIVER TECHNOLOGY CO., LTD. + +OUI:B8E589* + ID_OUI_FROM_DATABASE=Payter BV + +OUI:C89C1D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:503DE5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:801440* + ID_OUI_FROM_DATABASE=Sunlit System Technology Corp + +OUI:948D50* + ID_OUI_FROM_DATABASE=Beamex Oy Ab + +OUI:94E226* + ID_OUI_FROM_DATABASE=D. ORtiz Consulting, LLC + +OUI:E8E732* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:386E21* + ID_OUI_FROM_DATABASE=Wasion Group Ltd. + +OUI:D8C99D* + ID_OUI_FROM_DATABASE=EA DISPLAY LIMITED + +OUI:CCFC6D* + ID_OUI_FROM_DATABASE=RIZ TRANSMITTERS + +OUI:AC80D6* + ID_OUI_FROM_DATABASE=Hexatronic AB + +OUI:9CF938* + ID_OUI_FROM_DATABASE=AREVA NP GmbH + +OUI:500E6D* + ID_OUI_FROM_DATABASE=TrafficCast International + +OUI:1CFEA7* + ID_OUI_FROM_DATABASE=IDentytech Solutins Ltd. + +OUI:D0B53D* + ID_OUI_FROM_DATABASE=SEPRO ROBOTIQUE + +OUI:A0DE05* + ID_OUI_FROM_DATABASE=JSC Irbis-T + +OUI:8895B9* + ID_OUI_FROM_DATABASE=Unified Packet Systems Crop + +OUI:78593E* + ID_OUI_FROM_DATABASE=RAFI GmbH & Co.KG + +OUI:684352* + ID_OUI_FROM_DATABASE=Bhuu Limited + +OUI:3CC0C6* + ID_OUI_FROM_DATABASE=d&b audiotechnik GmbH + +OUI:F8DAF4* + ID_OUI_FROM_DATABASE=Taishan Online Technology Co., Ltd. + +OUI:D8E3AE* + ID_OUI_FROM_DATABASE=CIRTEC MEDICAL SYSTEMS + +OUI:A83944* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:FC1FC0* + ID_OUI_FROM_DATABASE=EURECAM + +OUI:4891F6* + ID_OUI_FROM_DATABASE=Shenzhen Reach software technology CO.,LTD + +OUI:EC14F6* + ID_OUI_FROM_DATABASE=BioControl AS + +OUI:B8D06F* + ID_OUI_FROM_DATABASE=GUANGZHOU HKUST FOK YING TUNG RESEARCH INSTITUTE + +OUI:B4C44E* + ID_OUI_FROM_DATABASE=VXL eTech Pvt Ltd + +OUI:F0933A* + ID_OUI_FROM_DATABASE=NxtConect + +OUI:6052D0* + ID_OUI_FROM_DATABASE=FACTS Engineering + +OUI:8C278A* + ID_OUI_FROM_DATABASE=Vocollect Inc + +OUI:FCAF6A* + ID_OUI_FROM_DATABASE=Qulsar Inc + +OUI:ECE555* + ID_OUI_FROM_DATABASE=Hirschmann Automation + +OUI:DCD0F7* + ID_OUI_FROM_DATABASE=Bentek Systems Ltd. + +OUI:D0574C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:8818AE* + ID_OUI_FROM_DATABASE=Tamron Co., Ltd + +OUI:20D607* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:58DB8D* + ID_OUI_FROM_DATABASE=Fast Co., Ltd. + +OUI:18EF63* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:CCCE40* + ID_OUI_FROM_DATABASE=Janteq Corp + +OUI:8C4DEA* + ID_OUI_FROM_DATABASE=Cerio Corporation + +OUI:ECFAAA* + ID_OUI_FROM_DATABASE=The IMS Company + +OUI:CC55AD* + ID_OUI_FROM_DATABASE=RIM + +OUI:F0F7B3* + ID_OUI_FROM_DATABASE=Phorm + +OUI:E8757F* + ID_OUI_FROM_DATABASE=FIRS Technologies(Shenzhen) Co., Ltd + +OUI:C83EA7* + ID_OUI_FROM_DATABASE=KUNBUS GmbH + +OUI:A8D3C8* + ID_OUI_FROM_DATABASE=Wachendorff Elektronik GmbH & Co. KG + +OUI:E0CF2D* + ID_OUI_FROM_DATABASE=Gemintek Corporation + +OUI:68BDAB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:9CADEF* + ID_OUI_FROM_DATABASE=Obihai Technology, Inc. + +OUI:D08999* + ID_OUI_FROM_DATABASE=APCON, Inc. + +OUI:4454C0* + ID_OUI_FROM_DATABASE=Thompson Aerospace + +OUI:B4A4E3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:90903C* + ID_OUI_FROM_DATABASE=TRISON TECHNOLOGY CORPORATION + +OUI:94DD3F* + ID_OUI_FROM_DATABASE=A+V Link Technologies, Corp. + +OUI:C8EE08* + ID_OUI_FROM_DATABASE=TANGTOP TECHNOLOGY CO.,LTD + +OUI:7472F2* + ID_OUI_FROM_DATABASE=Chipsip Technology Co., Ltd. + +OUI:5CD998* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:D46CDA* + ID_OUI_FROM_DATABASE=CSM GmbH + +OUI:C4F464* + ID_OUI_FROM_DATABASE=Spica international + +OUI:74911A* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:544A05* + ID_OUI_FROM_DATABASE=wenglor sensoric gmbh + +OUI:5CCA32* + ID_OUI_FROM_DATABASE=Theben AG + +OUI:84C7A9* + ID_OUI_FROM_DATABASE=C3PO S.A. + +OUI:F8AC6D* + ID_OUI_FROM_DATABASE=Deltenna Ltd + +OUI:641084* + ID_OUI_FROM_DATABASE=HEXIUM Technical Development Co., Ltd. + +OUI:C416FA* + ID_OUI_FROM_DATABASE=Prysm Inc + +OUI:E0C286* + ID_OUI_FROM_DATABASE=Aisai Communication Technology Co., Ltd. + +OUI:D84B2A* + ID_OUI_FROM_DATABASE=Cognitas Technologies, Inc. + +OUI:684B88* + ID_OUI_FROM_DATABASE=Galtronics Telemetry Inc. + +OUI:842914* + ID_OUI_FROM_DATABASE=EMPORIA TELECOM Produktions- und VertriebsgesmbH & Co KG + +OUI:4C8B55* + ID_OUI_FROM_DATABASE=Grupo Digicon + +OUI:04A3F3* + ID_OUI_FROM_DATABASE=Emicon + +OUI:F866F2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C55E7* + ID_OUI_FROM_DATABASE=YSI, Inc. + +OUI:C02BFC* + ID_OUI_FROM_DATABASE=iNES. applied informatics GmbH + +OUI:AC34CB* + ID_OUI_FROM_DATABASE=Shanhai GBCOM Communication Technology Co. Ltd + +OUI:D4A928* + ID_OUI_FROM_DATABASE=GreenWave Reality Inc + +OUI:9CFFBE* + ID_OUI_FROM_DATABASE=OTSL Inc. + +OUI:2CD1DA* + ID_OUI_FROM_DATABASE=Sanjole, Inc. + +OUI:100E2B* + ID_OUI_FROM_DATABASE=NEC CASIO Mobile Communications + +OUI:445EF3* + ID_OUI_FROM_DATABASE=Tonalite Holding B.V. + +OUI:100C24* + ID_OUI_FROM_DATABASE=pomdevices, LLC + +OUI:58F6BF* + ID_OUI_FROM_DATABASE=Kyoto University + +OUI:7CED8D* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:54FDBF* + ID_OUI_FROM_DATABASE=Scheidt & Bachmann GmbH + +OUI:B40EDC* + ID_OUI_FROM_DATABASE=LG-Ericsson Co.,Ltd. + +OUI:A4D1D1* + ID_OUI_FROM_DATABASE=ECOtality North America + +OUI:C8D5FE* + ID_OUI_FROM_DATABASE=Shenzhen Zowee Technology Co., Ltd + +OUI:C49313* + ID_OUI_FROM_DATABASE=100fio networks technology llc + +OUI:A4A80F* + ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd. + +OUI:B8921D* + ID_OUI_FROM_DATABASE=BG T&A + +OUI:48FCB8* + ID_OUI_FROM_DATABASE=Woodstream Corporation + +OUI:548922* + ID_OUI_FROM_DATABASE=Zelfy Inc + +OUI:F8C091* + ID_OUI_FROM_DATABASE=Highgates Technology + +OUI:6C5CDE* + ID_OUI_FROM_DATABASE=SunReports, Inc. + +OUI:241F2C* + ID_OUI_FROM_DATABASE=Calsys, Inc. + +OUI:284846* + ID_OUI_FROM_DATABASE=GridCentric Inc. + +OUI:58B9E1* + ID_OUI_FROM_DATABASE=Crystalfontz America, Inc. + +OUI:646707* + ID_OUI_FROM_DATABASE=Beijing Omnific Technology, Ltd. + +OUI:D4000D* + ID_OUI_FROM_DATABASE=Phoenix Broadband Technologies, LLC. + +OUI:E87AF3* + ID_OUI_FROM_DATABASE=S5 Tech S.r.l. + +OUI:40C7C9* + ID_OUI_FROM_DATABASE=Naviit Inc. + +OUI:A0A763* + ID_OUI_FROM_DATABASE=Polytron Vertrieb GmbH + +OUI:D496DF* + ID_OUI_FROM_DATABASE=SUNGJIN C&T CO.,LTD + +OUI:D07DE5* + ID_OUI_FROM_DATABASE=Forward Pay Systems, Inc. + +OUI:7CEF18* + ID_OUI_FROM_DATABASE=Creative Product Design Pty. Ltd. + +OUI:FCD4F6* + ID_OUI_FROM_DATABASE=Messana Air.Ray Conditioning s.r.l. + +OUI:0CD696* + ID_OUI_FROM_DATABASE=Amimon Ltd + +OUI:B43741* + ID_OUI_FROM_DATABASE=Consert, Inc. + +OUI:F8FB2F* + ID_OUI_FROM_DATABASE=Santur Corporation + +OUI:2CCD43* + ID_OUI_FROM_DATABASE=Summit Technology Group + +OUI:6C8D65* + ID_OUI_FROM_DATABASE=Wireless Glue Networks, Inc. + +OUI:CCFCB1* + ID_OUI_FROM_DATABASE=Wireless Technology, Inc. + +OUI:CC5C75* + ID_OUI_FROM_DATABASE=Weightech Com. Imp. Exp. Equip. Pesagem Ltda + +OUI:A098ED* + ID_OUI_FROM_DATABASE=Shandong Intelligent Optical Communication Development Co., Ltd. + +OUI:34C69A* + ID_OUI_FROM_DATABASE=Enecsys Ltd + +OUI:502A8B* + ID_OUI_FROM_DATABASE=Telekom Research and Development Sdn Bhd + +OUI:F88DEF* + ID_OUI_FROM_DATABASE=Tenebraex + +OUI:EC43E6* + ID_OUI_FROM_DATABASE=AWCER Ltd. + +OUI:F0EC39* + ID_OUI_FROM_DATABASE=Essec + +OUI:5849BA* + ID_OUI_FROM_DATABASE=Chitai Electronic Corp. + +OUI:181714* + ID_OUI_FROM_DATABASE=DAEWOOIS + +OUI:80B289* + ID_OUI_FROM_DATABASE=Forworld Electronics Ltd. + +OUI:14A62C* + ID_OUI_FROM_DATABASE=S.M. Dezac S.A. + +OUI:A8F470* + ID_OUI_FROM_DATABASE=Fujian Newland Communication Science Technologies Co.,Ltd. + +OUI:DC1D9F* + ID_OUI_FROM_DATABASE=U & B tech + +OUI:081651* + ID_OUI_FROM_DATABASE=SHENZHEN SEA STAR TECHNOLOGY CO.,LTD + +OUI:DC49C9* + ID_OUI_FROM_DATABASE=CASCO SIGNAL LTD + +OUI:B09134* + ID_OUI_FROM_DATABASE=Taleo + +OUI:A863DF* + ID_OUI_FROM_DATABASE=DISPLAIRE CORPORATION + +OUI:104369* + ID_OUI_FROM_DATABASE=Soundmax Electronic Limited + +OUI:C06C0F* + ID_OUI_FROM_DATABASE=Dobbs Stanford + +OUI:5475D0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BC6A16* + ID_OUI_FROM_DATABASE=tdvine + +OUI:C8EF2E* + ID_OUI_FROM_DATABASE=Beijing Gefei Tech. Co., Ltd + +OUI:98DCD9* + ID_OUI_FROM_DATABASE=UNITEC Co., Ltd. + +OUI:30525A* + ID_OUI_FROM_DATABASE=NST Co., LTD + +OUI:6089B7* + ID_OUI_FROM_DATABASE=KAEL MÜHENDİSLİK ELEKTRONİK TİCARET SANAYİ LİMİTED ŞİRKETİ + +OUI:2CA780* + ID_OUI_FROM_DATABASE=True Technologies Inc. + +OUI:545FA9* + ID_OUI_FROM_DATABASE=Teracom Limited + +OUI:ECC882* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A0B9ED* + ID_OUI_FROM_DATABASE=Skytap + +OUI:502DF4* + ID_OUI_FROM_DATABASE=Phytec Messtechnik GmbH + +OUI:38E8DF* + ID_OUI_FROM_DATABASE=b gmbh medien + datenbanken + +OUI:10189E* + ID_OUI_FROM_DATABASE=Elmo Motion Control + +OUI:88FD15* + ID_OUI_FROM_DATABASE=LINEEYE CO., LTD + +OUI:10445A* + ID_OUI_FROM_DATABASE=Shaanxi Hitech Electronic Co., LTD + +OUI:60B3C4* + ID_OUI_FROM_DATABASE=Elber Srl + +OUI:04C880* + ID_OUI_FROM_DATABASE=Samtec Inc + +OUI:884B39* + ID_OUI_FROM_DATABASE=Siemens AG, Healthcare Sector + +OUI:44C233* + ID_OUI_FROM_DATABASE=Guangzhou Comet Technology Development Co.Ltd + +OUI:B482FE* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:307C30* + ID_OUI_FROM_DATABASE=RIM + +OUI:BC4E3C* + ID_OUI_FROM_DATABASE=CORE STAFF CO., LTD. + +OUI:80BAAC* + ID_OUI_FROM_DATABASE=TeleAdapt Ltd + +OUI:FC4463* + ID_OUI_FROM_DATABASE=Universal Audio, Inc + +OUI:F06853* + ID_OUI_FROM_DATABASE=Integrated Corporation + +OUI:10E6AE* + ID_OUI_FROM_DATABASE=Source Technologies, LLC + +OUI:A4ADB8* + ID_OUI_FROM_DATABASE=Vitec Group, Camera Dynamics Ltd + +OUI:90A2DA* + ID_OUI_FROM_DATABASE=GHEO SA + +OUI:C41ECE* + ID_OUI_FROM_DATABASE=HMI Sources Ltd. + +OUI:BCD5B6* + ID_OUI_FROM_DATABASE=d2d technologies + +OUI:1C8F8A* + ID_OUI_FROM_DATABASE=Phase Motion Control SpA + +OUI:A4B1EE* + ID_OUI_FROM_DATABASE=H. ZANDER GmbH & Co. KG + +OUI:486FD2* + ID_OUI_FROM_DATABASE=StorSimple Inc + +OUI:D4F143* + ID_OUI_FROM_DATABASE=IPROAD.,Inc + +OUI:CC5459* + ID_OUI_FROM_DATABASE=OnTime Networks AS + +OUI:3CB17F* + ID_OUI_FROM_DATABASE=Wattwatchers Pty Ld + +OUI:00DB45* + ID_OUI_FROM_DATABASE=THAMWAY CO.,LTD. + +OUI:A0231B* + ID_OUI_FROM_DATABASE=TeleComp R&D Corp. + +OUI:94C4E9* + ID_OUI_FROM_DATABASE=PowerLayer Microsystems HongKong Limited + +OUI:8843E1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B4ED19* + ID_OUI_FROM_DATABASE=Pie Digital, Inc. + +OUI:888717* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:E0271A* + ID_OUI_FROM_DATABASE=TTC Next-generation Home Network System WG + +OUI:84C727* + ID_OUI_FROM_DATABASE=Gnodal Ltd + +OUI:E4AB46* + ID_OUI_FROM_DATABASE=UAB Selteka + +OUI:D479C3* + ID_OUI_FROM_DATABASE=Cameronet GmbH & Co. KG + +OUI:945B7E* + ID_OUI_FROM_DATABASE=TRILOBIT LTDA. + +OUI:E85B5B* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:20D906* + ID_OUI_FROM_DATABASE=Iota, Inc. + +OUI:404022* + ID_OUI_FROM_DATABASE=ZIV + +OUI:74F726* + ID_OUI_FROM_DATABASE=Neuron Robotics + +OUI:18FC9F* + ID_OUI_FROM_DATABASE=Changhe Electronics Co., Ltd. + +OUI:A438FC* + ID_OUI_FROM_DATABASE=Plastic Logic + +OUI:601D0F* + ID_OUI_FROM_DATABASE=Midnite Solar + +OUI:50A6E3* + ID_OUI_FROM_DATABASE=David Clark Company + +OUI:549A16* + ID_OUI_FROM_DATABASE=Uzushio Electric Co.,Ltd. + +OUI:4001C6* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD + +OUI:608D17* + ID_OUI_FROM_DATABASE=Sentrus Government Systems Division, Inc + +OUI:80912A* + ID_OUI_FROM_DATABASE=Lih Rong electronic Enterprise Co., Ltd. + +OUI:8038FD* + ID_OUI_FROM_DATABASE=LeapFrog Enterprises, Inc. + +OUI:7072CF* + ID_OUI_FROM_DATABASE=EdgeCore Networks + +OUI:803B9A* + ID_OUI_FROM_DATABASE=ghe-ces electronic ag + +OUI:9CCD82* + ID_OUI_FROM_DATABASE=CHENG UEI PRECISION INDUSTRY CO.,LTD + +OUI:C8AACC* + ID_OUI_FROM_DATABASE=Private + +OUI:003D41* + ID_OUI_FROM_DATABASE=Hatteland Computer AS + +OUI:087618* + ID_OUI_FROM_DATABASE=ViE Technologies Sdn. Bhd. + +OUI:A4AD00* + ID_OUI_FROM_DATABASE=Ragsdale Technology + +OUI:2C1984* + ID_OUI_FROM_DATABASE=IDN Telecom, Inc. + +OUI:3863F6* + ID_OUI_FROM_DATABASE=3NOD MULTIMEDIA(SHENZHEN)CO.,LTD + +OUI:DCE2AC* + ID_OUI_FROM_DATABASE=Lumens Digital Optics Inc. + +OUI:98D88C* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:C8873B* + ID_OUI_FROM_DATABASE=Net Optics + +OUI:B0E97E* + ID_OUI_FROM_DATABASE=Advanced Micro Peripherals + +OUI:D44CA7* + ID_OUI_FROM_DATABASE=Informtekhnika & Communication, LLC + +OUI:202CB7* + ID_OUI_FROM_DATABASE=Kong Yue Electronics & Information Industry (Xinhui) Ltd. + +OUI:68CC9C* + ID_OUI_FROM_DATABASE=Mine Site Technologies + +OUI:04B466* + ID_OUI_FROM_DATABASE=BSP Co., Ltd. + +OUI:E41F13* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:00271B* + ID_OUI_FROM_DATABASE=Alec Sicherheitssysteme GmbH + +OUI:002718* + ID_OUI_FROM_DATABASE=Suzhou NEW SEAUNION Video Technology Co.,Ltd + +OUI:00270C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00270B* + ID_OUI_FROM_DATABASE=Adura Technologies + +OUI:002705* + ID_OUI_FROM_DATABASE=Sectronic + +OUI:002706* + ID_OUI_FROM_DATABASE=YOISYS + +OUI:0026F9* + ID_OUI_FROM_DATABASE=S.E.M. srl + +OUI:0026F3* + ID_OUI_FROM_DATABASE=SMC Networks + +OUI:688540* + ID_OUI_FROM_DATABASE=IGI Mobile, Inc. + +OUI:6465C0* + ID_OUI_FROM_DATABASE=Nuvon, Inc + +OUI:F0DE71* + ID_OUI_FROM_DATABASE=Shanghai EDO Technologies Co.,Ltd. + +OUI:28FBD3* + ID_OUI_FROM_DATABASE=Ragentek Technology Group + +OUI:7C1EB3* + ID_OUI_FROM_DATABASE=2N TELEKOMUNIKACE a.s. + +OUI:146E0A* + ID_OUI_FROM_DATABASE=Private + +OUI:1045F8* + ID_OUI_FROM_DATABASE=LNT-Automation GmbH + +OUI:644F74* + ID_OUI_FROM_DATABASE=LENUS Co., Ltd. + +OUI:787F62* + ID_OUI_FROM_DATABASE=GiK mbH + +OUI:D4AAFF* + ID_OUI_FROM_DATABASE=MICRO WORLD + +OUI:C4FCE4* + ID_OUI_FROM_DATABASE=DishTV NZ Ltd + +OUI:0CD7C2* + ID_OUI_FROM_DATABASE=Axium Technologies, Inc. + +OUI:40F52E* + ID_OUI_FROM_DATABASE=Leica Microsystems (Schweiz) AG + +OUI:C02250* + ID_OUI_FROM_DATABASE=Private + +OUI:64BC11* + ID_OUI_FROM_DATABASE=CombiQ AB + +OUI:4097D1* + ID_OUI_FROM_DATABASE=BK Electronics cc + +OUI:68AAD2* + ID_OUI_FROM_DATABASE=DATECS LTD., + +OUI:0026EC* + ID_OUI_FROM_DATABASE=Legrand Home Systems, Inc + +OUI:0026E6* + ID_OUI_FROM_DATABASE=Visionhitech Co., Ltd. + +OUI:0026E0* + ID_OUI_FROM_DATABASE=ASITEQ + +OUI:0026DA* + ID_OUI_FROM_DATABASE=Universal Media Corporation /Slovakia/ s.r.o. + +OUI:0026D3* + ID_OUI_FROM_DATABASE=Zeno Information System + +OUI:0026D4* + ID_OUI_FROM_DATABASE=IRCA SpA + +OUI:0026CD* + ID_OUI_FROM_DATABASE=PurpleComm, Inc. + +OUI:10880F* + ID_OUI_FROM_DATABASE=Daruma Telecomunicações e Informática S.A. + +OUI:4C4B68* + ID_OUI_FROM_DATABASE=Mobile Device, Inc. + +OUI:94BA31* + ID_OUI_FROM_DATABASE=Visiontec da Amazônia Ltda. + +OUI:F45FF7* + ID_OUI_FROM_DATABASE=DQ Technology Inc. + +OUI:60F13D* + ID_OUI_FROM_DATABASE=JABLOCOM s.r.o. + +OUI:0CEF7C* + ID_OUI_FROM_DATABASE=AnaCom Inc + +OUI:E08FEC* + ID_OUI_FROM_DATABASE=REPOTEC CO., LTD. + +OUI:D0D286* + ID_OUI_FROM_DATABASE=Beckman Coulter K.K. + +OUI:1C0FCF* + ID_OUI_FROM_DATABASE=Sypro Optics GmbH + +OUI:0025AB* + ID_OUI_FROM_DATABASE=AIO LCD PC BU / TPV + +OUI:0025A4* + ID_OUI_FROM_DATABASE=EuroDesign embedded technologies GmbH + +OUI:00259D* + ID_OUI_FROM_DATABASE=Private + +OUI:002598* + ID_OUI_FROM_DATABASE=Zhong Shan City Litai Electronic Industrial Co. Ltd + +OUI:002591* + ID_OUI_FROM_DATABASE=NEXTEK, Inc. + +OUI:00258C* + ID_OUI_FROM_DATABASE=ESUS ELEKTRONIK SAN. VE DIS. TIC. LTD. STI. + +OUI:002587* + ID_OUI_FROM_DATABASE=Vitality, Inc. + +OUI:002581* + ID_OUI_FROM_DATABASE=x-star networks Inc. + +OUI:002582* + ID_OUI_FROM_DATABASE=Maksat Technologies (P) Ltd + +OUI:002578* + ID_OUI_FROM_DATABASE=JSC Concern Sozvezdie + +OUI:00257D* + ID_OUI_FROM_DATABASE=PointRed Telecom Private Ltd. + +OUI:002577* + ID_OUI_FROM_DATABASE=D-BOX Technologies + +OUI:002571* + ID_OUI_FROM_DATABASE=Zhejiang Tianle Digital Electric Co.,Ltd + +OUI:00256A* + ID_OUI_FROM_DATABASE=inIT - Institut Industrial IT + +OUI:002565* + ID_OUI_FROM_DATABASE=Vizimax Inc. + +OUI:00255E* + ID_OUI_FROM_DATABASE=Shanghai Dare Technologies Co.,Ltd. + +OUI:002558* + ID_OUI_FROM_DATABASE=MPEDIA + +OUI:002635* + ID_OUI_FROM_DATABASE=Bluetechnix GmbH + +OUI:00262F* + ID_OUI_FROM_DATABASE=HAMAMATSU TOA ELECTRONICS + +OUI:002623* + ID_OUI_FROM_DATABASE=JRD Communication Inc + +OUI:002628* + ID_OUI_FROM_DATABASE=companytec automação e controle ltda. + +OUI:00261C* + ID_OUI_FROM_DATABASE=NEOVIA INC. + +OUI:002615* + ID_OUI_FROM_DATABASE=Teracom Limited + +OUI:002616* + ID_OUI_FROM_DATABASE=Rosemount Inc. + +OUI:002610* + ID_OUI_FROM_DATABASE=Apacewave Technologies + +OUI:002609* + ID_OUI_FROM_DATABASE=Phyllis Co., Ltd. + +OUI:00268C* + ID_OUI_FROM_DATABASE=StarLeaf Ltd. + +OUI:002692* + ID_OUI_FROM_DATABASE=Mitsubishi Electric Co. + +OUI:002686* + ID_OUI_FROM_DATABASE=Quantenna Communcations, Inc. + +OUI:002680* + ID_OUI_FROM_DATABASE=SIL3 Pty.Ltd + +OUI:00267F* + ID_OUI_FROM_DATABASE=Zenterio AB + +OUI:00267A* + ID_OUI_FROM_DATABASE=wuhan hongxin telecommunication technologies co.,ltd + +OUI:002679* + ID_OUI_FROM_DATABASE=Euphonic Technologies, Inc. + +OUI:002673* + ID_OUI_FROM_DATABASE=RICOH COMPANY,LTD. + +OUI:00266D* + ID_OUI_FROM_DATABASE=MobileAccess Networks + +OUI:0025D6* + ID_OUI_FROM_DATABASE=The Kroger Co. + +OUI:0025CA* + ID_OUI_FROM_DATABASE=LS Research, LLC + +OUI:0025BE* + ID_OUI_FROM_DATABASE=Tektrap Systems Inc. + +OUI:0025BD* + ID_OUI_FROM_DATABASE=Italdata Ingegneria dell'Idea S.p.A. + +OUI:0025B7* + ID_OUI_FROM_DATABASE=Costar electronics, inc., + +OUI:0025B0* + ID_OUI_FROM_DATABASE=Schmartz Inc + +OUI:002546* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002545* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002535* + ID_OUI_FROM_DATABASE=Minimax GmbH & Co KG + +OUI:002532* + ID_OUI_FROM_DATABASE=Digital Recorders + +OUI:00252B* + ID_OUI_FROM_DATABASE=Stirling Energy Systems + +OUI:0025FD* + ID_OUI_FROM_DATABASE=OBR Centrum Techniki Morskiej S.A. + +OUI:002603* + ID_OUI_FROM_DATABASE=Shenzhen Wistar Technology Co., Ltd + +OUI:0025F3* + ID_OUI_FROM_DATABASE=Nordwestdeutsche Zählerrevision + +OUI:0025EC* + ID_OUI_FROM_DATABASE=Humanware + +OUI:0025E2* + ID_OUI_FROM_DATABASE=Everspring Industry Co., Ltd. + +OUI:0025DD* + ID_OUI_FROM_DATABASE=SUNNYTEK INFORMATION CO., LTD. + +OUI:002667* + ID_OUI_FROM_DATABASE=CARECOM CO.,LTD. + +OUI:002660* + ID_OUI_FROM_DATABASE=Logiways + +OUI:002656* + ID_OUI_FROM_DATABASE=Sansonic Electronics USA + +OUI:002653* + ID_OUI_FROM_DATABASE=DaySequerra Corporation + +OUI:00264C* + ID_OUI_FROM_DATABASE=Shanghai DigiVision Technology Co., Ltd. + +OUI:002647* + ID_OUI_FROM_DATABASE=WFE TECHNOLOGY CORP. + +OUI:00263B* + ID_OUI_FROM_DATABASE=Onbnetech + +OUI:0026C1* + ID_OUI_FROM_DATABASE=ARTRAY CO., LTD. + +OUI:0026B5* + ID_OUI_FROM_DATABASE=ICOMM Tele Ltd + +OUI:0026AF* + ID_OUI_FROM_DATABASE=Duelco A/S + +OUI:0026AB* + ID_OUI_FROM_DATABASE=SEIKO EPSON CORPORATION + +OUI:0026A5* + ID_OUI_FROM_DATABASE=MICROROBOT.CO.,LTD + +OUI:00269F* + ID_OUI_FROM_DATABASE=Private + +OUI:002699* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002489* + ID_OUI_FROM_DATABASE=Vodafone Omnitel N.V. + +OUI:00248E* + ID_OUI_FROM_DATABASE=Infoware ZRt. + +OUI:002482* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:002476* + ID_OUI_FROM_DATABASE=TAP.tv + +OUI:00246F* + ID_OUI_FROM_DATABASE=Onda Communication spa + +OUI:00246A* + ID_OUI_FROM_DATABASE=Solid Year Co., Ltd. + +OUI:0023FA* + ID_OUI_FROM_DATABASE=RG Nets, Inc. + +OUI:0023FF* + ID_OUI_FROM_DATABASE=Beijing HTTC Technology Ltd. + +OUI:0023F4* + ID_OUI_FROM_DATABASE=Masternaut + +OUI:0023EA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0023E4* + ID_OUI_FROM_DATABASE=IPnect co. ltd. + +OUI:0023DE* + ID_OUI_FROM_DATABASE=Ansync Inc. + +OUI:0023D1* + ID_OUI_FROM_DATABASE=TRG + +OUI:0023CB* + ID_OUI_FROM_DATABASE=Shenzhen Full-join Technology Co.,Ltd + +OUI:0023D2* + ID_OUI_FROM_DATABASE=Inhand Electronics, Inc. + +OUI:0024B4* + ID_OUI_FROM_DATABASE=ESCATRONIC GmbH + +OUI:0024AF* + ID_OUI_FROM_DATABASE=EchoStar Technologies + +OUI:0024AD* + ID_OUI_FROM_DATABASE=Adolf Thies Gmbh & Co. KG + +OUI:00249C* + ID_OUI_FROM_DATABASE=Bimeng Comunication System Co. Ltd + +OUI:002526* + ID_OUI_FROM_DATABASE=Genuine Technologies Co., Ltd. + +OUI:002525* + ID_OUI_FROM_DATABASE=CTERA Networks Ltd. + +OUI:002520* + ID_OUI_FROM_DATABASE=SMA Railway Technology GmbH + +OUI:00251B* + ID_OUI_FROM_DATABASE=Philips CareServant + +OUI:002516* + ID_OUI_FROM_DATABASE=Integrated Design Tools, Inc. + +OUI:00250F* + ID_OUI_FROM_DATABASE=On-Ramp Wireless, Inc. + +OUI:002503* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:00250A* + ID_OUI_FROM_DATABASE=Security Expert Co. Ltd + +OUI:0024DD* + ID_OUI_FROM_DATABASE=Centrak, Inc. + +OUI:0024D8* + ID_OUI_FROM_DATABASE=IlSung Precision + +OUI:0024CC* + ID_OUI_FROM_DATABASE=Fascinations Toys and Gifts, Inc. + +OUI:0024D1* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:0024CA* + ID_OUI_FROM_DATABASE=Tobii Technology AB + +OUI:0024C5* + ID_OUI_FROM_DATABASE=Meridian Audio Limited + +OUI:0024B9* + ID_OUI_FROM_DATABASE=Wuhan Higheasy Electronic Technology Development Co.Ltd + +OUI:002425* + ID_OUI_FROM_DATABASE=Shenzhenshi chuangzhicheng Technology Co.,Ltd + +OUI:002419* + ID_OUI_FROM_DATABASE=Private + +OUI:002412* + ID_OUI_FROM_DATABASE=Benign Technologies Co, Ltd. + +OUI:00240C* + ID_OUI_FROM_DATABASE=DELEC GmbH + +OUI:002406* + ID_OUI_FROM_DATABASE=Pointmobile + +OUI:0023F9* + ID_OUI_FROM_DATABASE=Double-Take Software, INC. + +OUI:002463* + ID_OUI_FROM_DATABASE=Phybridge Inc + +OUI:002459* + ID_OUI_FROM_DATABASE=ABB Automation products GmbH + +OUI:00245E* + ID_OUI_FROM_DATABASE=Hivision Co.,ltd + +OUI:002451* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00244C* + ID_OUI_FROM_DATABASE=Solartron Metrology Ltd + +OUI:002445* + ID_OUI_FROM_DATABASE=CommScope Canada Inc. + +OUI:00243F* + ID_OUI_FROM_DATABASE=Storwize, Inc. + +OUI:002440* + ID_OUI_FROM_DATABASE=Halo Monitoring, Inc. + +OUI:00243B* + ID_OUI_FROM_DATABASE=CSSI (S) Pte Ltd + +OUI:0024FC* + ID_OUI_FROM_DATABASE=QuoPin Co., Ltd. + +OUI:0024F7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0024F0* + ID_OUI_FROM_DATABASE=Seanodes + +OUI:0024EB* + ID_OUI_FROM_DATABASE=ClearPath Networks, Inc. + +OUI:0024E4* + ID_OUI_FROM_DATABASE=Withings + +OUI:002435* + ID_OUI_FROM_DATABASE=WIDE CORPORATION + +OUI:00242F* + ID_OUI_FROM_DATABASE=Micron + +OUI:00241F* + ID_OUI_FROM_DATABASE=DCT-Delta GmbH + +OUI:0023C5* + ID_OUI_FROM_DATABASE=Radiation Safety and Control Services Inc + +OUI:0023C4* + ID_OUI_FROM_DATABASE=Lux Lumen + +OUI:0023B8* + ID_OUI_FROM_DATABASE=Sichuan Jiuzhou Electronic Technology Co.,Ltd + +OUI:0023BF* + ID_OUI_FROM_DATABASE=Mainpine, Inc. + +OUI:0023B2* + ID_OUI_FROM_DATABASE=Intelligent Mechatronic Systems Inc + +OUI:0023AC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0023A0* + ID_OUI_FROM_DATABASE=Hana CNS Co., LTD. + +OUI:0023A5* + ID_OUI_FROM_DATABASE=SageTV, LLC + +OUI:0022B6* + ID_OUI_FROM_DATABASE=Superflow Technologies Group + +OUI:0022A3* + ID_OUI_FROM_DATABASE=California Eastern Laboratories + +OUI:00229E* + ID_OUI_FROM_DATABASE=Social Aid Research Co., Ltd. + +OUI:002291* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002292* + ID_OUI_FROM_DATABASE=Cinetal + +OUI:002297* + ID_OUI_FROM_DATABASE=XMOS Semiconductor + +OUI:00228B* + ID_OUI_FROM_DATABASE=Kensington Computer Products Group + +OUI:002284* + ID_OUI_FROM_DATABASE=DESAY A&V SCIENCE AND TECHNOLOGY CO.,LTD + +OUI:00227F* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:002277* + ID_OUI_FROM_DATABASE=NEC Australia Pty Ltd + +OUI:00226D* + ID_OUI_FROM_DATABASE=Shenzhen GIEC Electronics Co., Ltd. + +OUI:002263* + ID_OUI_FROM_DATABASE=Koos Technical Services, Inc. + +OUI:002267* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002259* + ID_OUI_FROM_DATABASE=Guangzhou New Postcom Equipment Co.,Ltd. + +OUI:0022E4* + ID_OUI_FROM_DATABASE=APASS TECHNOLOGY CO., LTD. + +OUI:0022DD* + ID_OUI_FROM_DATABASE=Protecta Electronics Ltd + +OUI:0022D8* + ID_OUI_FROM_DATABASE=Shenzhen GST Security and Safety Technology Limited + +OUI:0022D1* + ID_OUI_FROM_DATABASE=Albrecht Jung GmbH & Co. KG + +OUI:0022C3* + ID_OUI_FROM_DATABASE=Zeeport Technology Inc. + +OUI:0022C7* + ID_OUI_FROM_DATABASE=SEGGER Microcontroller GmbH & Co. KG + +OUI:0022BD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002344* + ID_OUI_FROM_DATABASE=Objective Interface Systems, Inc. + +OUI:002343* + ID_OUI_FROM_DATABASE=TEM AG + +OUI:002337* + ID_OUI_FROM_DATABASE=Global Star Solutions ULC + +OUI:00232B* + ID_OUI_FROM_DATABASE=IRD A/S + +OUI:00231C* + ID_OUI_FROM_DATABASE=Fourier Systems Ltd. + +OUI:00231B* + ID_OUI_FROM_DATABASE=Danaher Motion - Kollmorgen + +OUI:00239F* + ID_OUI_FROM_DATABASE=Institut für Prüftechnik + +OUI:002393* + ID_OUI_FROM_DATABASE=AJINEXTEK + +OUI:00238F* + ID_OUI_FROM_DATABASE=NIDEC COPAL CORPORATION + +OUI:002385* + ID_OUI_FROM_DATABASE=ANTIPODE + +OUI:00237E* + ID_OUI_FROM_DATABASE=ELSTER GMBH + +OUI:00237F* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:002379* + ID_OUI_FROM_DATABASE=Union Business Machines Co. Ltd. + +OUI:002253* + ID_OUI_FROM_DATABASE=Entorian Technologies + +OUI:002250* + ID_OUI_FROM_DATABASE=Point Six Wireless, LLC + +OUI:002249* + ID_OUI_FROM_DATABASE=HOME MULTIENERGY SL + +OUI:00224A* + ID_OUI_FROM_DATABASE=RAYLASE AG + +OUI:002240* + ID_OUI_FROM_DATABASE=Universal Telecom S/A + +OUI:00222D* + ID_OUI_FROM_DATABASE=SMC Networks Inc. + +OUI:00222E* + ID_OUI_FROM_DATABASE=maintech GmbH + +OUI:002364* + ID_OUI_FROM_DATABASE=Power Instruments Pte Ltd + +OUI:002369* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:002370* + ID_OUI_FROM_DATABASE=Snell + +OUI:00235D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002356* + ID_OUI_FROM_DATABASE=Packet Forensics LLC + +OUI:00234A* + ID_OUI_FROM_DATABASE=Private + +OUI:002313* + ID_OUI_FROM_DATABASE=Qool Technologies Ltd. + +OUI:00230D* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002301* + ID_OUI_FROM_DATABASE=Witron Technology Limited + +OUI:0022F7* + ID_OUI_FROM_DATABASE=Conceptronic + +OUI:0022EA* + ID_OUI_FROM_DATABASE=Rustelcom Inc. + +OUI:0022F0* + ID_OUI_FROM_DATABASE=3 Greens Aviation Limited + +OUI:0022E9* + ID_OUI_FROM_DATABASE=ProVision Communications + +OUI:00211C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002117* + ID_OUI_FROM_DATABASE=Tellord + +OUI:002110* + ID_OUI_FROM_DATABASE=Clearbox Systems + +OUI:002106* + ID_OUI_FROM_DATABASE=RIM Testing Services + +OUI:001FFF* + ID_OUI_FROM_DATABASE=Respironics, Inc. + +OUI:001FFE* + ID_OUI_FROM_DATABASE=HPN Supply Chain + +OUI:001FF8* + ID_OUI_FROM_DATABASE=Siemens AG, Sector Industry, Drive Technologies, Motion Control Systems + +OUI:001FFD* + ID_OUI_FROM_DATABASE=Indigo Mobile Technologies Corp. + +OUI:002221* + ID_OUI_FROM_DATABASE=ITOH DENKI CO,LTD. + +OUI:00221B* + ID_OUI_FROM_DATABASE=Morega Systems + +OUI:002220* + ID_OUI_FROM_DATABASE=Mitac Technology Corp + +OUI:002227* + ID_OUI_FROM_DATABASE=uv-electronic GmbH + +OUI:002214* + ID_OUI_FROM_DATABASE=RINNAI KOREA + +OUI:00220E* + ID_OUI_FROM_DATABASE=Indigo Security Co., Ltd. + +OUI:002208* + ID_OUI_FROM_DATABASE=Certicom Corp + +OUI:002201* + ID_OUI_FROM_DATABASE=Aksys Networks Inc + +OUI:0021F7* + ID_OUI_FROM_DATABASE=HPN Supply Chain + +OUI:0021A0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00219C* + ID_OUI_FROM_DATABASE=Honeywld Technology Corp. + +OUI:002192* + ID_OUI_FROM_DATABASE=Baoding Galaxy Electronic Technology Co.,Ltd + +OUI:00218C* + ID_OUI_FROM_DATABASE=TopControl GMBH + +OUI:00217F* + ID_OUI_FROM_DATABASE=Intraco Technology Pte Ltd + +OUI:00217A* + ID_OUI_FROM_DATABASE=Sejin Electron, Inc. + +OUI:002179* + ID_OUI_FROM_DATABASE=IOGEAR, Inc. + +OUI:002173* + ID_OUI_FROM_DATABASE=Ion Torrent Systems, Inc. + +OUI:001FC3* + ID_OUI_FROM_DATABASE=SmartSynch, Inc + +OUI:001FC8* + ID_OUI_FROM_DATABASE=Up-Today Industrial Co., Ltd. + +OUI:001FC1* + ID_OUI_FROM_DATABASE=Hanlong Technology Co.,LTD + +OUI:001FC2* + ID_OUI_FROM_DATABASE=Jow Tong Technology Co Ltd + +OUI:001FBC* + ID_OUI_FROM_DATABASE=EVGA Corporation + +OUI:001FB0* + ID_OUI_FROM_DATABASE=TimeIPS, Inc. + +OUI:001FB5* + ID_OUI_FROM_DATABASE=I/O Interconnect Inc. + +OUI:001FA9* + ID_OUI_FROM_DATABASE=Atlanta DTH, Inc. + +OUI:0021F1* + ID_OUI_FROM_DATABASE=Tutus Data AB + +OUI:0021F2* + ID_OUI_FROM_DATABASE=EASY3CALL Technology Limited + +OUI:0021EB* + ID_OUI_FROM_DATABASE=ESP SYSTEMS, LLC + +OUI:0021E5* + ID_OUI_FROM_DATABASE=Display Solution AG + +OUI:0021E4* + ID_OUI_FROM_DATABASE=I-WIN + +OUI:0021DF* + ID_OUI_FROM_DATABASE=Martin Christ GmbH + +OUI:0021D8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0021CC* + ID_OUI_FROM_DATABASE=Flextronics International + +OUI:001FF1* + ID_OUI_FROM_DATABASE=Paradox Hellas S.A. + +OUI:001FEC* + ID_OUI_FROM_DATABASE=Synapse Électronique + +OUI:001FE5* + ID_OUI_FROM_DATABASE=In-Circuit GmbH + +OUI:001FD9* + ID_OUI_FROM_DATABASE=RSD Communications Ltd + +OUI:001FD4* + ID_OUI_FROM_DATABASE=4IPNET, INC. + +OUI:001FCF* + ID_OUI_FROM_DATABASE=MSI Technology GmbH + +OUI:00213F* + ID_OUI_FROM_DATABASE=A-Team Technology Ltd. + +OUI:002139* + ID_OUI_FROM_DATABASE=Escherlogic Inc. + +OUI:002134* + ID_OUI_FROM_DATABASE=Brandywine Communications + +OUI:00212F* + ID_OUI_FROM_DATABASE=Phoebe Micro Inc. + +OUI:002129* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:00212A* + ID_OUI_FROM_DATABASE=Audiovox Corporation + +OUI:002123* + ID_OUI_FROM_DATABASE=Aerosat Avionics + +OUI:00216D* + ID_OUI_FROM_DATABASE=Soltech Co., Ltd. + +OUI:00216C* + ID_OUI_FROM_DATABASE=ODVA + +OUI:002167* + ID_OUI_FROM_DATABASE=HWA JIN T&I Corp. + +OUI:002160* + ID_OUI_FROM_DATABASE=Hidea Solutions Co. Ltd. + +OUI:002154* + ID_OUI_FROM_DATABASE=D-TACQ Solutions Ltd + +OUI:00214D* + ID_OUI_FROM_DATABASE=Guangzhou Skytone Transmission Technology Com. Ltd. + +OUI:002148* + ID_OUI_FROM_DATABASE=Kaco Solar Korea + +OUI:0021C5* + ID_OUI_FROM_DATABASE=3DSP Corp + +OUI:0021BF* + ID_OUI_FROM_DATABASE=Hitachi High-Tech Control Systems Corporation + +OUI:0021C0* + ID_OUI_FROM_DATABASE=Mobile Appliance, Inc. + +OUI:0021B9* + ID_OUI_FROM_DATABASE=Universal Devices Inc. + +OUI:0021B3* + ID_OUI_FROM_DATABASE=Ross Controls + +OUI:0021B2* + ID_OUI_FROM_DATABASE=Fiberblaze A/S + +OUI:0021AD* + ID_OUI_FROM_DATABASE=Nordic ID Oy + +OUI:0021A6* + ID_OUI_FROM_DATABASE=Videotec Spa + +OUI:001F11* + ID_OUI_FROM_DATABASE=OPENMOKO, INC. + +OUI:001F0B* + ID_OUI_FROM_DATABASE=Federal State Unitary Enterprise Industrial UnionElectropribor + +OUI:001EFF* + ID_OUI_FROM_DATABASE=Mueller-Elektronik GmbH & Co. KG + +OUI:001F06* + ID_OUI_FROM_DATABASE=Integrated Dispatch Solutions + +OUI:001F05* + ID_OUI_FROM_DATABASE=iTAS Technology Corp. + +OUI:001EF3* + ID_OUI_FROM_DATABASE=From2 + +OUI:001EF8* + ID_OUI_FROM_DATABASE=Emfinity Inc. + +OUI:001F7A* + ID_OUI_FROM_DATABASE=WiWide Inc. + +OUI:001F70* + ID_OUI_FROM_DATABASE=Botik Technologies LTD + +OUI:001F75* + ID_OUI_FROM_DATABASE=GiBahn Media + +OUI:001F64* + ID_OUI_FROM_DATABASE=Beijing Autelan Technology Inc. + +OUI:001F5E* + ID_OUI_FROM_DATABASE=Dyna Technology Co.,Ltd. + +OUI:001F58* + ID_OUI_FROM_DATABASE=EMH Energiemesstechnik GmbH + +OUI:001F4C* + ID_OUI_FROM_DATABASE=Roseman Engineering Ltd + +OUI:001F51* + ID_OUI_FROM_DATABASE=HD Communications Corp + +OUI:001F4B* + ID_OUI_FROM_DATABASE=Lineage Power + +OUI:001F9F* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:001F93* + ID_OUI_FROM_DATABASE=Xiotech Corporation + +OUI:001F98* + ID_OUI_FROM_DATABASE=DAIICHI-DENTSU LTD. + +OUI:001F8C* + ID_OUI_FROM_DATABASE=CCS Inc. + +OUI:001F8A* + ID_OUI_FROM_DATABASE=Ellion Digital Inc. + +OUI:001F83* + ID_OUI_FROM_DATABASE=Teleplan Technology Services Sdn Bhd + +OUI:001E30* + ID_OUI_FROM_DATABASE=Shireen Inc + +OUI:001E2B* + ID_OUI_FROM_DATABASE=Radio Systems Design, Inc. + +OUI:001E24* + ID_OUI_FROM_DATABASE=Zhejiang Bell Technology Co.,ltd + +OUI:001E18* + ID_OUI_FROM_DATABASE=Radio Activity srl + +OUI:001E1D* + ID_OUI_FROM_DATABASE=East Coast Datacom, Inc. + +OUI:001E1E* + ID_OUI_FROM_DATABASE=Honeywell Life Safety + +OUI:001E13* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001E0E* + ID_OUI_FROM_DATABASE=MAXI VIEW HOLDINGS LIMITED + +OUI:001E60* + ID_OUI_FROM_DATABASE=Digital Lighting Systems, Inc + +OUI:001E59* + ID_OUI_FROM_DATABASE=Silicon Turnkey Express, LLC + +OUI:001E54* + ID_OUI_FROM_DATABASE=TOYO ELECTRIC Corporation + +OUI:001E4D* + ID_OUI_FROM_DATABASE=Welkin Sciences, LLC + +OUI:001E48* + ID_OUI_FROM_DATABASE=Wi-Links + +OUI:001E43* + ID_OUI_FROM_DATABASE=AISIN AW CO.,LTD. + +OUI:001E3E* + ID_OUI_FROM_DATABASE=KMW Inc. + +OUI:001EC3* + ID_OUI_FROM_DATABASE=Kozio, Inc. + +OUI:001EBC* + ID_OUI_FROM_DATABASE=WINTECH AUTOMATION CO.,LTD. + +OUI:001EB7* + ID_OUI_FROM_DATABASE=TBTech, Co., Ltd. + +OUI:001EB0* + ID_OUI_FROM_DATABASE=ImesD Electronica S.L. + +OUI:001EA5* + ID_OUI_FROM_DATABASE=ROBOTOUS, Inc. + +OUI:001EAB* + ID_OUI_FROM_DATABASE=TeleWell Oy + +OUI:001E9E* + ID_OUI_FROM_DATABASE=ddm hopt + schuler Gmbh + Co. KG + +OUI:001E99* + ID_OUI_FROM_DATABASE=Vantanol Industrial Corporation + +OUI:001F36* + ID_OUI_FROM_DATABASE=Bellwin Information Co. Ltd., + +OUI:001F35* + ID_OUI_FROM_DATABASE=AIR802 LLC + +OUI:001F30* + ID_OUI_FROM_DATABASE=Travelping + +OUI:001F23* + ID_OUI_FROM_DATABASE=Interacoustics + +OUI:001F24* + ID_OUI_FROM_DATABASE=DIGITVIEW TECHNOLOGY CO., LTD. + +OUI:001F1D* + ID_OUI_FROM_DATABASE=Atlas Material Testing Technology LLC + +OUI:001E92* + ID_OUI_FROM_DATABASE=JEULIN S.A. + +OUI:001E89* + ID_OUI_FROM_DATABASE=CRFS Limited + +OUI:001E84* + ID_OUI_FROM_DATABASE=Pika Technologies Inc. + +OUI:001E83* + ID_OUI_FROM_DATABASE=LAN/MAN Standards Association (LMSC) + +OUI:001E6C* + ID_OUI_FROM_DATABASE=Opaque Systems + +OUI:001EE6* + ID_OUI_FROM_DATABASE=Shenzhen Advanced Video Info-Tech Co., Ltd. + +OUI:001EE0* + ID_OUI_FROM_DATABASE=Urmet Domus SpA + +OUI:001EDB* + ID_OUI_FROM_DATABASE=Giken Trastem Co., Ltd. + +OUI:001ED6* + ID_OUI_FROM_DATABASE=Alentec & Orion AB + +OUI:001ECF* + ID_OUI_FROM_DATABASE=PHILIPS ELECTRONICS UK LTD + +OUI:001C96* + ID_OUI_FROM_DATABASE=Linkwise Technology Pte Ltd + +OUI:001C91* + ID_OUI_FROM_DATABASE=Gefen Inc. + +OUI:001C8A* + ID_OUI_FROM_DATABASE=Cirrascale Corporation + +OUI:001C84* + ID_OUI_FROM_DATABASE=STL Solution Co.,Ltd. + +OUI:001C80* + ID_OUI_FROM_DATABASE=New Business Division/Rhea-Information CO., LTD. + +OUI:001C76* + ID_OUI_FROM_DATABASE=The Wandsworth Group Ltd + +OUI:001C6F* + ID_OUI_FROM_DATABASE=Emfit Ltd + +OUI:001C71* + ID_OUI_FROM_DATABASE=Emergent Electronics + +OUI:001C70* + ID_OUI_FROM_DATABASE=NOVACOMM LTDA + +OUI:001C6A* + ID_OUI_FROM_DATABASE=Weiss Engineering Ltd. + +OUI:001D59* + ID_OUI_FROM_DATABASE=Mitra Energy & Infrastructure + +OUI:001D52* + ID_OUI_FROM_DATABASE=Defzone B.V. + +OUI:001D4C* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:001D48* + ID_OUI_FROM_DATABASE=Sensor-Technik Wiedemann GmbH + +OUI:001D41* + ID_OUI_FROM_DATABASE=Hardy Instruments + +OUI:001D3C* + ID_OUI_FROM_DATABASE=Muscle Corporation + +OUI:001D30* + ID_OUI_FROM_DATABASE=YX Wireless S.A. + +OUI:001D35* + ID_OUI_FROM_DATABASE=Viconics Electronics Inc. + +OUI:001D2F* + ID_OUI_FROM_DATABASE=QuantumVision Corporation + +OUI:001CD3* + ID_OUI_FROM_DATABASE=ZP Engineering SEL + +OUI:001CCE* + ID_OUI_FROM_DATABASE=By Techdesign + +OUI:001CC7* + ID_OUI_FROM_DATABASE=Rembrandt Technologies, LLC d/b/a REMSTREAM + +OUI:001CC2* + ID_OUI_FROM_DATABASE=Part II Research, Inc. + +OUI:001CBB* + ID_OUI_FROM_DATABASE=MusicianLink + +OUI:001CB1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001CB7* + ID_OUI_FROM_DATABASE=USC DigiArk Corporation + +OUI:001CA3* + ID_OUI_FROM_DATABASE=Terra + +OUI:001CA5* + ID_OUI_FROM_DATABASE=Zygo Corporation + +OUI:001CAA* + ID_OUI_FROM_DATABASE=Bellon Pty Ltd + +OUI:001C9D* + ID_OUI_FROM_DATABASE=Liecthi AG + +OUI:001DCA* + ID_OUI_FROM_DATABASE=PAV Electronics Limited + +OUI:001DC4* + ID_OUI_FROM_DATABASE=AIOI Systems Co., Ltd. + +OUI:001DC3* + ID_OUI_FROM_DATABASE=RIKOR TV, Ltd + +OUI:001DB1* + ID_OUI_FROM_DATABASE=Crescendo Networks + +OUI:001DB2* + ID_OUI_FROM_DATABASE=HOKKAIDO ELECTRIC ENGINEERING CO.,LTD. + +OUI:001DB7* + ID_OUI_FROM_DATABASE=Tendril Networks, Inc. + +OUI:001DAD* + ID_OUI_FROM_DATABASE=Sinotech Engineering Consultants, Inc. Geotechnical Enginee + +OUI:001DA8* + ID_OUI_FROM_DATABASE=Takahata Electronics Co.,Ltd + +OUI:001DA7* + ID_OUI_FROM_DATABASE=Seamless Internet + +OUI:001DA1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D9A* + ID_OUI_FROM_DATABASE=GODEX INTERNATIONAL CO., LTD + +OUI:001D95* + ID_OUI_FROM_DATABASE=Flash, Inc. + +OUI:001D8E* + ID_OUI_FROM_DATABASE=Alereon, Inc. + +OUI:001D87* + ID_OUI_FROM_DATABASE=VigTech Labs Sdn Bhd + +OUI:001D88* + ID_OUI_FROM_DATABASE=Clearwire + +OUI:001D7E* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:001D7D* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:001D6C* + ID_OUI_FROM_DATABASE=ClariPhy Communications, Inc. + +OUI:001D71* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D78* + ID_OUI_FROM_DATABASE=Invengo Information Technology Co.,Ltd + +OUI:001D65* + ID_OUI_FROM_DATABASE=Microwave Radio Communications + +OUI:001D5E* + ID_OUI_FROM_DATABASE=COMING MEDIA CORP. + +OUI:001D29* + ID_OUI_FROM_DATABASE=Doro AB + +OUI:001D22* + ID_OUI_FROM_DATABASE=Foss Analytical A/S + +OUI:001D1D* + ID_OUI_FROM_DATABASE=Inter-M Corporation + +OUI:001D16* + ID_OUI_FROM_DATABASE=SFR + +OUI:001D10* + ID_OUI_FROM_DATABASE=LightHaus Logic, Inc. + +OUI:001D0A* + ID_OUI_FROM_DATABASE=Davis Instruments, Inc. + +OUI:001D03* + ID_OUI_FROM_DATABASE=Design Solutions Inc. + +OUI:001CFE* + ID_OUI_FROM_DATABASE=Quartics Inc + +OUI:001CF7* + ID_OUI_FROM_DATABASE=AudioScience + +OUI:001CE6* + ID_OUI_FROM_DATABASE=INNES + +OUI:001CE1* + ID_OUI_FROM_DATABASE=INDRA SISTEMAS, S.A. + +OUI:001CDA* + ID_OUI_FROM_DATABASE=Exegin Technologies Limited + +OUI:001E07* + ID_OUI_FROM_DATABASE=Winy Technology Co., Ltd. + +OUI:001E02* + ID_OUI_FROM_DATABASE=Sougou Keikaku Kougyou Co.,Ltd. + +OUI:001E01* + ID_OUI_FROM_DATABASE=Renesas Technology Sales Co., Ltd. + +OUI:001DFB* + ID_OUI_FROM_DATABASE=NETCLEUS Systems Corporation + +OUI:001DF4* + ID_OUI_FROM_DATABASE=Magellan Technology Pty Limited + +OUI:001DEF* + ID_OUI_FROM_DATABASE=TRIMM, INC. + +OUI:001DE8* + ID_OUI_FROM_DATABASE=Nikko Denki Tsushin Corporation(NDTC) + +OUI:001DE3* + ID_OUI_FROM_DATABASE=Intuicom + +OUI:001DDD* + ID_OUI_FROM_DATABASE=DAT H.K. LIMITED + +OUI:001AF8* + ID_OUI_FROM_DATABASE=Copley Controls Corporation + +OUI:001AF3* + ID_OUI_FROM_DATABASE=Samyoung Electronics + +OUI:001AEE* + ID_OUI_FROM_DATABASE=Shenztech Ltd + +OUI:001AE2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001AE7* + ID_OUI_FROM_DATABASE=Aztek Networks, Inc. + +OUI:001AD4* + ID_OUI_FROM_DATABASE=iPOX Technology Co., Ltd. + +OUI:001AD6* + ID_OUI_FROM_DATABASE=JIAGNSU AETNA ELECTRIC CO.,LTD + +OUI:001B97* + ID_OUI_FROM_DATABASE=Violin Technologies + +OUI:001B9C* + ID_OUI_FROM_DATABASE=SATEL sp. z o.o. + +OUI:001B90* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B86* + ID_OUI_FROM_DATABASE=Bosch Access Systems GmbH + +OUI:001B8B* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:001B7F* + ID_OUI_FROM_DATABASE=TMN Technologies Telecomunicacoes Ltda + +OUI:001B81* + ID_OUI_FROM_DATABASE=DATAQ Instruments, Inc. + +OUI:001B80* + ID_OUI_FROM_DATABASE=LORD Corporation + +OUI:001B73* + ID_OUI_FROM_DATABASE=DTL Broadcast Ltd + +OUI:001B6E* + ID_OUI_FROM_DATABASE=Anue Systems, Inc. + +OUI:001B67* + ID_OUI_FROM_DATABASE=Cisco Systems Inc + +OUI:001B60* + ID_OUI_FROM_DATABASE=NAVIGON AG + +OUI:001B54* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B48* + ID_OUI_FROM_DATABASE=Shenzhen Lantech Electronics Co., Ltd. + +OUI:001B4D* + ID_OUI_FROM_DATABASE=Areca Technology Corporation + +OUI:001B41* + ID_OUI_FROM_DATABASE=General Infinity Co.,Ltd. + +OUI:001B3C* + ID_OUI_FROM_DATABASE=Software Technologies Group,Inc. + +OUI:001B35* + ID_OUI_FROM_DATABASE=ChongQing JINOU Science & Technology Development CO.,Ltd + +OUI:001B2E* + ID_OUI_FROM_DATABASE=Sinkyo Electron Inc + +OUI:001B30* + ID_OUI_FROM_DATABASE=Solitech Inc. + +OUI:001BC7* + ID_OUI_FROM_DATABASE=StarVedia Technology Inc. + +OUI:001BC6* + ID_OUI_FROM_DATABASE=Strato Rechenzentrum AG + +OUI:001BBB* + ID_OUI_FROM_DATABASE=RFTech Co.,Ltd + +OUI:001BB6* + ID_OUI_FROM_DATABASE=Bird Electronic Corp. + +OUI:001BAA* + ID_OUI_FROM_DATABASE=XenICs nv + +OUI:001BA3* + ID_OUI_FROM_DATABASE=Flexit Group GmbH + +OUI:001C63* + ID_OUI_FROM_DATABASE=TRUEN + +OUI:001C57* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001C5E* + ID_OUI_FROM_DATABASE=ASTON France + +OUI:001C46* + ID_OUI_FROM_DATABASE=QTUM + +OUI:001C3A* + ID_OUI_FROM_DATABASE=Element Labs, Inc. + +OUI:001C41* + ID_OUI_FROM_DATABASE=scemtec Transponder Technology GmbH + +OUI:001C34* + ID_OUI_FROM_DATABASE=HUEY CHIAO INTERNATIONAL CO., LTD. + +OUI:001C33* + ID_OUI_FROM_DATABASE=Sutron + +OUI:001BF7* + ID_OUI_FROM_DATABASE=Lund IP Products AB + +OUI:001BF9* + ID_OUI_FROM_DATABASE=Intellitect Water Ltd + +OUI:001BF8* + ID_OUI_FROM_DATABASE=Digitrax Inc. + +OUI:001BF2* + ID_OUI_FROM_DATABASE=KWORLD COMPUTER CO., LTD + +OUI:001BEB* + ID_OUI_FROM_DATABASE=DMP Electronics INC. + +OUI:001BE6* + ID_OUI_FROM_DATABASE=VR AG + +OUI:001BDF* + ID_OUI_FROM_DATABASE=Iskra Sistemi d.d. + +OUI:001BD3* + ID_OUI_FROM_DATABASE=Panasonic Corp. AVC Company + +OUI:001BD8* + ID_OUI_FROM_DATABASE=DVTel LTD + +OUI:001BCC* + ID_OUI_FROM_DATABASE=KINGTEK CCTV ALLIANCE CO., LTD. + +OUI:001AC8* + ID_OUI_FROM_DATABASE=ISL (Instrumentation Scientifique de Laboratoire) + +OUI:001ACF* + ID_OUI_FROM_DATABASE=C.T. ELETTRONICA + +OUI:001AC3* + ID_OUI_FROM_DATABASE=Scientific-Atlanta, Inc + +OUI:001AB9* + ID_OUI_FROM_DATABASE=PMC + +OUI:001ABE* + ID_OUI_FROM_DATABASE=COMPUTER HI-TECH INC. + +OUI:001AAB* + ID_OUI_FROM_DATABASE=eWings s.r.l. + +OUI:001AB2* + ID_OUI_FROM_DATABASE=Cyber Solutions Inc. + +OUI:001AB7* + ID_OUI_FROM_DATABASE=Ethos Networks LTD. + +OUI:001C2E* + ID_OUI_FROM_DATABASE=HPN Supply Chain + +OUI:001C27* + ID_OUI_FROM_DATABASE=Sunell Electronics Co. + +OUI:001C22* + ID_OUI_FROM_DATABASE=Aeris Elettronica s.r.l. + +OUI:001C1D* + ID_OUI_FROM_DATABASE=CHENZHOU GOSPELL DIGITAL TECHNOLOGY CO.,LTD + +OUI:001C18* + ID_OUI_FROM_DATABASE=Sicert S.r.L. + +OUI:001C0A* + ID_OUI_FROM_DATABASE=Shenzhen AEE Technology Co.,Ltd. + +OUI:001C05* + ID_OUI_FROM_DATABASE=Nonin Medical Inc. + +OUI:001BFE* + ID_OUI_FROM_DATABASE=Zavio Inc. + +OUI:001B29* + ID_OUI_FROM_DATABASE=Avantis.Co.,Ltd + +OUI:001B23* + ID_OUI_FROM_DATABASE=SimpleComTools + +OUI:001B1E* + ID_OUI_FROM_DATABASE=HART Communication Foundation + +OUI:001B12* + ID_OUI_FROM_DATABASE=Apprion + +OUI:001B17* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:001B0B* + ID_OUI_FROM_DATABASE=Phidgets Inc. + +OUI:001B10* + ID_OUI_FROM_DATABASE=ShenZhen Kang Hui Technology Co.,ltd + +OUI:001B04* + ID_OUI_FROM_DATABASE=Affinity International S.p.a + +OUI:001AFF* + ID_OUI_FROM_DATABASE=Wizyoung Tech. + +OUI:001AFD* + ID_OUI_FROM_DATABASE=EVOLIS + +OUI:00191C* + ID_OUI_FROM_DATABASE=Sensicast Systems + +OUI:00191E* + ID_OUI_FROM_DATABASE=Beyondwiz Co., Ltd. + +OUI:001923* + ID_OUI_FROM_DATABASE=Phonex Korea Co., LTD. + +OUI:00192A* + ID_OUI_FROM_DATABASE=Antiope Associates + +OUI:001910* + ID_OUI_FROM_DATABASE=Knick Elektronische Messgeraete GmbH & Co. KG + +OUI:001917* + ID_OUI_FROM_DATABASE=Posiflex Inc. + +OUI:001909* + ID_OUI_FROM_DATABASE=DEVI - Danfoss A/S + +OUI:00190B* + ID_OUI_FROM_DATABASE=Southern Vision Systems, Inc. + +OUI:001904* + ID_OUI_FROM_DATABASE=WB Electronics Sp. z o.o. + +OUI:0018FF* + ID_OUI_FROM_DATABASE=PowerQuattro Co. + +OUI:0018FA* + ID_OUI_FROM_DATABASE=Yushin Precision Equipment Co.,Ltd. + +OUI:001955* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00194E* + ID_OUI_FROM_DATABASE=Ultra Electronics - TCS (Tactical Communication Systems) + +OUI:001950* + ID_OUI_FROM_DATABASE=Harman Multimedia + +OUI:001949* + ID_OUI_FROM_DATABASE=TENTEL COMTECH CO., LTD. + +OUI:001942* + ID_OUI_FROM_DATABASE=ON SOFTWARE INTERNATIONAL LIMITED + +OUI:00193D* + ID_OUI_FROM_DATABASE=GMC Guardian Mobility Corp. + +OUI:001936* + ID_OUI_FROM_DATABASE=STERLITE OPTICAL TECHNOLOGIES LIMITED + +OUI:00193B* + ID_OUI_FROM_DATABASE=Wilibox Deliberant Group LLC + +OUI:00192F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A20* + ID_OUI_FROM_DATABASE=CMOTECH Co. Ltd. + +OUI:001A22* + ID_OUI_FROM_DATABASE=eQ-3 Entwicklung GmbH + +OUI:001A14* + ID_OUI_FROM_DATABASE=Xin Hua Control Engineering Co.,Ltd. + +OUI:001A0D* + ID_OUI_FROM_DATABASE=HandHeld entertainment, Inc. + +OUI:001A0F* + ID_OUI_FROM_DATABASE=Sistemas Avanzados de Control, S.A. + +OUI:001A08* + ID_OUI_FROM_DATABASE=Simoco Ltd. + +OUI:001A01* + ID_OUI_FROM_DATABASE=Smiths Medical + +OUI:0019FC* + ID_OUI_FROM_DATABASE=PT. Ufoakses Sukses Luarbiasa + +OUI:0019EF* + ID_OUI_FROM_DATABASE=SHENZHEN LINNKING ELECTRONICS CO.,LTD + +OUI:0019F1* + ID_OUI_FROM_DATABASE=Star Communication Network Technology Co.,Ltd + +OUI:0019F6* + ID_OUI_FROM_DATABASE=Acconet (PTE) Ltd + +OUI:001A76* + ID_OUI_FROM_DATABASE=SDT information Technology Co.,LTD. + +OUI:001A6F* + ID_OUI_FROM_DATABASE=MI.TEL s.r.l. + +OUI:001A6A* + ID_OUI_FROM_DATABASE=Tranzas, Inc. + +OUI:001A63* + ID_OUI_FROM_DATABASE=Elster Solutions, LLC, + +OUI:001A5E* + ID_OUI_FROM_DATABASE=Thincom Technology Co.,Ltd + +OUI:001A57* + ID_OUI_FROM_DATABASE=Matrix Design Group, LLC + +OUI:001A5C* + ID_OUI_FROM_DATABASE=Euchner GmbH+Co. KG + +OUI:001A50* + ID_OUI_FROM_DATABASE=PheeNet Technology Corp. + +OUI:001A9D* + ID_OUI_FROM_DATABASE=Skipper Wireless, Inc. + +OUI:001AA2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A91* + ID_OUI_FROM_DATABASE=FusionDynamic Ltd. + +OUI:001A96* + ID_OUI_FROM_DATABASE=ECLER S.A. + +OUI:001A90* + ID_OUI_FROM_DATABASE=Trópico Sistemas e Telecomunicações da Amazônia LTDA. + +OUI:001A8C* + ID_OUI_FROM_DATABASE=Sophos Ltd + +OUI:001A85* + ID_OUI_FROM_DATABASE=NV Michel Van de Wiele + +OUI:001A87* + ID_OUI_FROM_DATABASE=Canhold International Limited + +OUI:001A86* + ID_OUI_FROM_DATABASE=AdvancedIO Systems Inc + +OUI:0019B5* + ID_OUI_FROM_DATABASE=Famar Fueguina S.A. + +OUI:0019BA* + ID_OUI_FROM_DATABASE=Paradox Security Systems Ltd + +OUI:0019A2* + ID_OUI_FROM_DATABASE=ORDYN TECHNOLOGIES + +OUI:0019AE* + ID_OUI_FROM_DATABASE=Hopling Technologies b.v. + +OUI:0019A7* + ID_OUI_FROM_DATABASE=ITU-T + +OUI:001996* + ID_OUI_FROM_DATABASE=TurboChef Technologies Inc. + +OUI:00199B* + ID_OUI_FROM_DATABASE=Diversified Technical Systems, Inc. + +OUI:001991* + ID_OUI_FROM_DATABASE=avinfo + +OUI:00198A* + ID_OUI_FROM_DATABASE=Northrop Grumman Systems Corp. + +OUI:00198C* + ID_OUI_FROM_DATABASE=iXSea + +OUI:001985* + ID_OUI_FROM_DATABASE=IT Watchdogs, Inc + +OUI:00196B* + ID_OUI_FROM_DATABASE=Danpex Corporation + +OUI:001966* + ID_OUI_FROM_DATABASE=Asiarock Technology Limited + +OUI:00195C* + ID_OUI_FROM_DATABASE=Innotech Corporation + +OUI:001961* + ID_OUI_FROM_DATABASE=Blaupunkt Embedded Systems GmbH + +OUI:0019DE* + ID_OUI_FROM_DATABASE=MOBITEK + +OUI:0019EA* + ID_OUI_FROM_DATABASE=TeraMage Technologies Co., Ltd. + +OUI:0019D0* + ID_OUI_FROM_DATABASE=Cathexis + +OUI:0019D7* + ID_OUI_FROM_DATABASE=FORTUNETEK CO., LTD + +OUI:0019B3* + ID_OUI_FROM_DATABASE=Stanford Research Systems + +OUI:001A44* + ID_OUI_FROM_DATABASE=JWTrading Co., Ltd + +OUI:001A49* + ID_OUI_FROM_DATABASE=Micro Vision Co.,LTD + +OUI:001A3D* + ID_OUI_FROM_DATABASE=Ajin Vision Co.,Ltd + +OUI:001A31* + ID_OUI_FROM_DATABASE=SCAN COIN Industries AB + +OUI:001A38* + ID_OUI_FROM_DATABASE=Sanmina-SCI + +OUI:001A2C* + ID_OUI_FROM_DATABASE=SATEC Co.,LTD + +OUI:001A27* + ID_OUI_FROM_DATABASE=Ubistar + +OUI:0017AE* + ID_OUI_FROM_DATABASE=GAI-Tronics + +OUI:0017A2* + ID_OUI_FROM_DATABASE=Camrivox Ltd. + +OUI:0017A7* + ID_OUI_FROM_DATABASE=Mobile Computing Promotion Consortium + +OUI:00179D* + ID_OUI_FROM_DATABASE=Kelman Limited + +OUI:001791* + ID_OUI_FROM_DATABASE=LinTech GmbH + +OUI:001796* + ID_OUI_FROM_DATABASE=Rittmeyer AG + +OUI:001798* + ID_OUI_FROM_DATABASE=Azonic Technology Co., LTD + +OUI:00178A* + ID_OUI_FROM_DATABASE=DARTS TECHNOLOGIES CORP. + +OUI:00177E* + ID_OUI_FROM_DATABASE=Meshcom Technologies Inc. + +OUI:001785* + ID_OUI_FROM_DATABASE=Sparr Electronics Ltd + +OUI:001809* + ID_OUI_FROM_DATABASE=CRESYN + +OUI:00180E* + ID_OUI_FROM_DATABASE=Avega Systems + +OUI:001810* + ID_OUI_FROM_DATABASE=IPTrade S.A. + +OUI:0017F6* + ID_OUI_FROM_DATABASE=Pyramid Meriden Inc. + +OUI:0017FB* + ID_OUI_FROM_DATABASE=FA + +OUI:0017FD* + ID_OUI_FROM_DATABASE=Amulet Hotkey + +OUI:0017EF* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0017D7* + ID_OUI_FROM_DATABASE=ION Geophysical Corporation Inc. + +OUI:0017DC* + ID_OUI_FROM_DATABASE=DAEMYUNG ZERO1 + +OUI:0017DE* + ID_OUI_FROM_DATABASE=Advantage Six Ltd + +OUI:0018C3* + ID_OUI_FROM_DATABASE=CS Corporation + +OUI:0018CA* + ID_OUI_FROM_DATABASE=Viprinet GmbH + +OUI:0018BE* + ID_OUI_FROM_DATABASE=ANSA Corporation + +OUI:0018B2* + ID_OUI_FROM_DATABASE=ADEUNIS RF + +OUI:0018B7* + ID_OUI_FROM_DATABASE=D3 LED, LLC + +OUI:0018AB* + ID_OUI_FROM_DATABASE=BEIJING LHWT MICROELECTRONICS INC. + +OUI:0018A6* + ID_OUI_FROM_DATABASE=Persistent Systems, LLC + +OUI:001895* + ID_OUI_FROM_DATABASE=Hansun Technologies Inc. + +OUI:00189A* + ID_OUI_FROM_DATABASE=HANA Micron Inc. + +OUI:0018E7* + ID_OUI_FROM_DATABASE=Cameo Communications, INC. + +OUI:0018EE* + ID_OUI_FROM_DATABASE=Videology Imaging Solutions, Inc. + +OUI:0018E2* + ID_OUI_FROM_DATABASE=Topdata Sistemas de Automacao Ltda + +OUI:0018DB* + ID_OUI_FROM_DATABASE=EPL Technology Ltd + +OUI:0018E0* + ID_OUI_FROM_DATABASE=ANAVEO + +OUI:0018CF* + ID_OUI_FROM_DATABASE=Baldor Electric Company + +OUI:0018D4* + ID_OUI_FROM_DATABASE=Unified Display Interface SIG + +OUI:00184A* + ID_OUI_FROM_DATABASE=Catcher, Inc. + +OUI:00184C* + ID_OUI_FROM_DATABASE=Bogen Communications + +OUI:001845* + ID_OUI_FROM_DATABASE=Pulsar-Telecom LLC. + +OUI:00183E* + ID_OUI_FROM_DATABASE=Digilent, Inc + +OUI:001828* + ID_OUI_FROM_DATABASE=e2v technologies (UK) ltd. + +OUI:00182D* + ID_OUI_FROM_DATABASE=Artec Design + +OUI:001821* + ID_OUI_FROM_DATABASE=SINDORICOH + +OUI:001815* + ID_OUI_FROM_DATABASE=GZ Technologies, Inc. + +OUI:00181C* + ID_OUI_FROM_DATABASE=Exterity Limited + +OUI:001772* + ID_OUI_FROM_DATABASE=ASTRO Strobel Kommunikationssysteme GmbH + +OUI:001777* + ID_OUI_FROM_DATABASE=Obsidian Research Corporation + +OUI:00176E* + ID_OUI_FROM_DATABASE=DUCATI SISTEMI + +OUI:001762* + ID_OUI_FROM_DATABASE=Solar Technology, Inc. + +OUI:001769* + ID_OUI_FROM_DATABASE=Cymphonix Corp + +OUI:00175D* + ID_OUI_FROM_DATABASE=Dongseo system. + +OUI:00175B* + ID_OUI_FROM_DATABASE=ACS Solutions Switzerland Ltd. + +OUI:001756* + ID_OUI_FROM_DATABASE=Vinci Labs Oy + +OUI:00174F* + ID_OUI_FROM_DATABASE=iCatch Inc. + +OUI:0017CD* + ID_OUI_FROM_DATABASE=CEC Wireless R&D Ltd. + +OUI:0017D2* + ID_OUI_FROM_DATABASE=THINLINX PTY LTD + +OUI:0017C6* + ID_OUI_FROM_DATABASE=Cross Match Technologies Inc + +OUI:0017BA* + ID_OUI_FROM_DATABASE=SEDO CO., LTD. + +OUI:0017BF* + ID_OUI_FROM_DATABASE=Coherent Research Limited + +OUI:0017C1* + ID_OUI_FROM_DATABASE=CM Precision Technology LTD. + +OUI:0017B3* + ID_OUI_FROM_DATABASE=Aftek Infosys Limited + +OUI:00186A* + ID_OUI_FROM_DATABASE=Global Link Digital Technology Co,.LTD + +OUI:00186F* + ID_OUI_FROM_DATABASE=Setha Industria Eletronica LTDA + +OUI:001876* + ID_OUI_FROM_DATABASE=WowWee Ltd. + +OUI:001869* + ID_OUI_FROM_DATABASE=KINGJIM + +OUI:001864* + ID_OUI_FROM_DATABASE=Eaton Corporation + +OUI:00185D* + ID_OUI_FROM_DATABASE=TAIGUEN TECHNOLOGY (SHEN-ZHEN) CO., LTD. + +OUI:001851* + ID_OUI_FROM_DATABASE=SWsoft + +OUI:001858* + ID_OUI_FROM_DATABASE=TagMaster AB + +OUI:00189F* + ID_OUI_FROM_DATABASE=Lenntek Corporation + +OUI:00188E* + ID_OUI_FROM_DATABASE=Ekahau, Inc. + +OUI:001887* + ID_OUI_FROM_DATABASE=Metasystem SpA + +OUI:001889* + ID_OUI_FROM_DATABASE=WinNet Solutions Limited + +OUI:00187B* + ID_OUI_FROM_DATABASE=4NSYS Co. Ltd. + +OUI:001661* + ID_OUI_FROM_DATABASE=Novatium Solutions (P) Ltd + +OUI:001663* + ID_OUI_FROM_DATABASE=KBT Mobile + +OUI:001668* + ID_OUI_FROM_DATABASE=Eishin Electronics + +OUI:001662* + ID_OUI_FROM_DATABASE=Liyuh Technology Ltd. + +OUI:00165C* + ID_OUI_FROM_DATABASE=Trackflow Ltd + +OUI:001655* + ID_OUI_FROM_DATABASE=FUHO TECHNOLOGY Co., LTD + +OUI:0015E4* + ID_OUI_FROM_DATABASE=Zimmer Elektromedizin + +OUI:0015DA* + ID_OUI_FROM_DATABASE=IRITEL A.D. + +OUI:0015DF* + ID_OUI_FROM_DATABASE=Clivet S.p.A. + +OUI:0015D3* + ID_OUI_FROM_DATABASE=Pantech&Curitel Communications, Inc. + +OUI:0015C7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0015C0* + ID_OUI_FROM_DATABASE=DIGITAL TELEMEDIA CO.,LTD. + +OUI:0015BA* + ID_OUI_FROM_DATABASE=iba AG + +OUI:00174A* + ID_OUI_FROM_DATABASE=SOCOMEC + +OUI:001743* + ID_OUI_FROM_DATABASE=Deck Srl + +OUI:00173D* + ID_OUI_FROM_DATABASE=Neology + +OUI:00173E* + ID_OUI_FROM_DATABASE=LeucotronEquipamentos Ltda. + +OUI:001738* + ID_OUI_FROM_DATABASE=International Business Machines + +OUI:00172C* + ID_OUI_FROM_DATABASE=TAEJIN INFOTECH + +OUI:001720* + ID_OUI_FROM_DATABASE=Image Sensing Systems, Inc. + +OUI:001725* + ID_OUI_FROM_DATABASE=Liquid Computing + +OUI:001701* + ID_OUI_FROM_DATABASE=KDE, Inc. + +OUI:001703* + ID_OUI_FROM_DATABASE=MOSDAN Internation Co.,Ltd + +OUI:0016FC* + ID_OUI_FROM_DATABASE=TOHKEN CO.,LTD. + +OUI:0016F0* + ID_OUI_FROM_DATABASE=Dell + +OUI:0016F5* + ID_OUI_FROM_DATABASE=Dalian Golden Hualu Digital Technology Co.,Ltd + +OUI:0016E9* + ID_OUI_FROM_DATABASE=Tiba Medical Inc + +OUI:0016E4* + ID_OUI_FROM_DATABASE=VANGUARD SECURITY ENGINEERING CORP. + +OUI:0016DD* + ID_OUI_FROM_DATABASE=Gigabeam Corporation + +OUI:0016E2* + ID_OUI_FROM_DATABASE=American Fibertek, Inc. + +OUI:0016D8* + ID_OUI_FROM_DATABASE=Senea AB + +OUI:00169C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00169E* + ID_OUI_FROM_DATABASE=TV One Ltd + +OUI:0016A3* + ID_OUI_FROM_DATABASE=Ingeteam Transmission&Distribution, S.A. + +OUI:001690* + ID_OUI_FROM_DATABASE=J-TEK INCORPORATION + +OUI:001697* + ID_OUI_FROM_DATABASE=NEC Corporation + +OUI:001689* + ID_OUI_FROM_DATABASE=Pilkor Electronics Co., Ltd + +OUI:00168B* + ID_OUI_FROM_DATABASE=Paralan Corporation + +OUI:001684* + ID_OUI_FROM_DATABASE=Donjin Co.,Ltd. + +OUI:00167D* + ID_OUI_FROM_DATABASE=Sky-Line Information Co., Ltd. + +OUI:001678* + ID_OUI_FROM_DATABASE=SHENZHEN BAOAN GAOKE ELECTRONICS CO., LTD + +OUI:001649* + ID_OUI_FROM_DATABASE=SetOne GmbH + +OUI:00163F* + ID_OUI_FROM_DATABASE=CReTE SYSTEMS Inc. + +OUI:001638* + ID_OUI_FROM_DATABASE=TECOM Co., Ltd. + +OUI:001633* + ID_OUI_FROM_DATABASE=Oxford Diagnostics Ltd. + +OUI:00162C* + ID_OUI_FROM_DATABASE=Xanboo + +OUI:001627* + ID_OUI_FROM_DATABASE=embedded-logic DESIGN AND MORE GmbH + +OUI:001619* + ID_OUI_FROM_DATABASE=Lancelan Technologies S.L. + +OUI:001614* + ID_OUI_FROM_DATABASE=Picosecond Pulse Labs + +OUI:001719* + ID_OUI_FROM_DATABASE=Audiocodes USA, Inc + +OUI:00171E* + ID_OUI_FROM_DATABASE=Theo Benning GmbH & Co. KG + +OUI:001712* + ID_OUI_FROM_DATABASE=ISCO International + +OUI:00170D* + ID_OUI_FROM_DATABASE=Dust Networks Inc. + +OUI:00160F* + ID_OUI_FROM_DATABASE=BADGER METER INC + +OUI:00160A* + ID_OUI_FROM_DATABASE=SWEEX Europe BV + +OUI:001603* + ID_OUI_FROM_DATABASE=COOLKSKY Co., LTD + +OUI:0015FC* + ID_OUI_FROM_DATABASE=Littelfuse Startco + +OUI:0015F7* + ID_OUI_FROM_DATABASE=Wintecronics Ltd. + +OUI:0015F0* + ID_OUI_FROM_DATABASE=EGO BV + +OUI:0015EA* + ID_OUI_FROM_DATABASE=Tellumat (Pty) Ltd + +OUI:0016C5* + ID_OUI_FROM_DATABASE=Shenzhen Xing Feng Industry Co.,Ltd + +OUI:0016C7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0016CC* + ID_OUI_FROM_DATABASE=Xcute Mobile Corp. + +OUI:0016C0* + ID_OUI_FROM_DATABASE=Semtech Corporation + +OUI:0016B4* + ID_OUI_FROM_DATABASE=Private + +OUI:0016A8* + ID_OUI_FROM_DATABASE=CWT CO., LTD. + +OUI:0016AD* + ID_OUI_FROM_DATABASE=BT-Links Company Limited + +OUI:001553* + ID_OUI_FROM_DATABASE=Cytyc Corporation + +OUI:001555* + ID_OUI_FROM_DATABASE=DFM GmbH + +OUI:00154E* + ID_OUI_FROM_DATABASE=IEC + +OUI:001547* + ID_OUI_FROM_DATABASE=AiZen Solutions Inc. + +OUI:001542* + ID_OUI_FROM_DATABASE=MICROHARD S.R.L. + +OUI:00153B* + ID_OUI_FROM_DATABASE=EMH metering GmbH & Co. KG + +OUI:001534* + ID_OUI_FROM_DATABASE=A Beltrónica-Companhia de Comunicações, Lda + +OUI:001440* + ID_OUI_FROM_DATABASE=ATOMIC Corporation + +OUI:001439* + ID_OUI_FROM_DATABASE=Blonder Tongue Laboratories, Inc. + +OUI:001434* + ID_OUI_FROM_DATABASE=Keri Systems, Inc + +OUI:00142D* + ID_OUI_FROM_DATABASE=Toradex AG + +OUI:001426* + ID_OUI_FROM_DATABASE=NL Technology + +OUI:001421* + ID_OUI_FROM_DATABASE=Total Wireless Technologies Pte. Ltd. + +OUI:00141C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001583* + ID_OUI_FROM_DATABASE=IVT corporation + +OUI:00157E* + ID_OUI_FROM_DATABASE=Weidmüller Interface GmbH & Co. KG + +OUI:001579* + ID_OUI_FROM_DATABASE=Lunatone Industrielle Elektronik GmbH + +OUI:001574* + ID_OUI_FROM_DATABASE=Horizon Semiconductors Ltd. + +OUI:001566* + ID_OUI_FROM_DATABASE=A-First Technology Co., Ltd. + +OUI:001561* + ID_OUI_FROM_DATABASE=JJPlus Corporation + +OUI:00155A* + ID_OUI_FROM_DATABASE=DAINIPPON PHARMACEUTICAL CO., LTD. + +OUI:001554* + ID_OUI_FROM_DATABASE=Atalum Wireless S.A. + +OUI:001528* + ID_OUI_FROM_DATABASE=Beacon Medical Products LLC d.b.a. BeaconMedaes + +OUI:001521* + ID_OUI_FROM_DATABASE=Horoquartz + +OUI:001523* + ID_OUI_FROM_DATABASE=Meteor Communications Corporation + +OUI:001522* + ID_OUI_FROM_DATABASE=Dea Security + +OUI:00151C* + ID_OUI_FROM_DATABASE=LENECO + +OUI:001512* + ID_OUI_FROM_DATABASE=Zurich University of Applied Sciences + +OUI:00150B* + ID_OUI_FROM_DATABASE=SAGE INFOTECH LTD. + +OUI:001506* + ID_OUI_FROM_DATABASE=Neo Photonics + +OUI:0014FF* + ID_OUI_FROM_DATABASE=Precise Automation, Inc. + +OUI:0014F8* + ID_OUI_FROM_DATABASE=Scientific Atlanta + +OUI:0014F3* + ID_OUI_FROM_DATABASE=ViXS Systems Inc + +OUI:0014E7* + ID_OUI_FROM_DATABASE=Stolinx,. Inc + +OUI:0014EC* + ID_OUI_FROM_DATABASE=Acro Telecom + +OUI:0014E2* + ID_OUI_FROM_DATABASE=datacom systems inc. + +OUI:0014D6* + ID_OUI_FROM_DATABASE=Jeongmin Electronics Co.,Ltd. + +OUI:0014DB* + ID_OUI_FROM_DATABASE=Elma Trenew Electronic GmbH + +OUI:0014DD* + ID_OUI_FROM_DATABASE=Covergence Inc. + +OUI:0014DC* + ID_OUI_FROM_DATABASE=Communication System Design & Manufacturing (CSDM) + +OUI:0014CF* + ID_OUI_FROM_DATABASE=INVISIO Communications + +OUI:0014CA* + ID_OUI_FROM_DATABASE=Key Radio Systems Limited + +OUI:0014C3* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:0014BC* + ID_OUI_FROM_DATABASE=SYNECTIC TELECOM EXPORTS PVT. LTD. + +OUI:0014B7* + ID_OUI_FROM_DATABASE=AR Infotek Inc. + +OUI:0014AD* + ID_OUI_FROM_DATABASE=Gassner Wiege- und Meßtechnik GmbH + +OUI:0014B2* + ID_OUI_FROM_DATABASE=mCubelogics Corporation + +OUI:0014A6* + ID_OUI_FROM_DATABASE=Teranetics, Inc. + +OUI:00149F* + ID_OUI_FROM_DATABASE=System and Chips, Inc. + +OUI:0014A1* + ID_OUI_FROM_DATABASE=Synchronous Communication Corp + +OUI:001470* + ID_OUI_FROM_DATABASE=Prokom Software SA + +OUI:001469* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001462* + ID_OUI_FROM_DATABASE=Digiwell Technology, inc + +OUI:00145D* + ID_OUI_FROM_DATABASE=WJ Communications, Inc. + +OUI:001450* + ID_OUI_FROM_DATABASE=Heim Systems GmbH + +OUI:001456* + ID_OUI_FROM_DATABASE=Edge Products + +OUI:00144C* + ID_OUI_FROM_DATABASE=General Meters Corp. + +OUI:001445* + ID_OUI_FROM_DATABASE=Telefon-Gradnja d.o.o. + +OUI:001447* + ID_OUI_FROM_DATABASE=BOAZ Inc. + +OUI:001446* + ID_OUI_FROM_DATABASE=SuperVision Solutions LLC + +OUI:0015B3* + ID_OUI_FROM_DATABASE=Caretech AB + +OUI:0015A9* + ID_OUI_FROM_DATABASE=KWANG WOO I&C CO.,LTD + +OUI:00159D* + ID_OUI_FROM_DATABASE=Tripp Lite + +OUI:001591* + ID_OUI_FROM_DATABASE=RLW Inc. + +OUI:00158A* + ID_OUI_FROM_DATABASE=SURECOM Technology Corp. + +OUI:00158F* + ID_OUI_FROM_DATABASE=NTT Advanced Technology Corporation + +OUI:001590* + ID_OUI_FROM_DATABASE=Hectronic GmbH + +OUI:0014A0* + ID_OUI_FROM_DATABASE=Accsense, Inc. + +OUI:001493* + ID_OUI_FROM_DATABASE=Systimax Solutions + +OUI:00148E* + ID_OUI_FROM_DATABASE=Tele Power Inc. + +OUI:001487* + ID_OUI_FROM_DATABASE=American Technology Integrators + +OUI:001482* + ID_OUI_FROM_DATABASE=Aurora Networks + +OUI:001481* + ID_OUI_FROM_DATABASE=Multilink Inc + +OUI:00147C* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:001475* + ID_OUI_FROM_DATABASE=Wiline Networks, Inc. + +OUI:0012E7* + ID_OUI_FROM_DATABASE=Projectek Networking Electronics Corp. + +OUI:0012E8* + ID_OUI_FROM_DATABASE=Fraunhofer IMS + +OUI:0012DB* + ID_OUI_FROM_DATABASE=ZIEHL industrie-elektronik GmbH + Co KG + +OUI:0012E2* + ID_OUI_FROM_DATABASE=ALAXALA Networks Corporation + +OUI:0012D6* + ID_OUI_FROM_DATABASE=Jiangsu Yitong High-Tech Co.,Ltd + +OUI:0012D5* + ID_OUI_FROM_DATABASE=Motion Reality Inc. + +OUI:0012C3* + ID_OUI_FROM_DATABASE=WIT S.A. + +OUI:0013E5* + ID_OUI_FROM_DATABASE=TENOSYS, INC. + +OUI:0013EA* + ID_OUI_FROM_DATABASE=Kamstrup A/S + +OUI:0013DE* + ID_OUI_FROM_DATABASE=Adapt4, LLC + +OUI:0013D7* + ID_OUI_FROM_DATABASE=SPIDCOM Technologies SA + +OUI:0013D8* + ID_OUI_FROM_DATABASE=Princeton Instruments + +OUI:0013CF* + ID_OUI_FROM_DATABASE=4Access Communications + +OUI:0013D2* + ID_OUI_FROM_DATABASE=PAGE IBERICA, S.A. + +OUI:0013C9* + ID_OUI_FROM_DATABASE=Beyond Achieve Enterprises Ltd. + +OUI:0013C2* + ID_OUI_FROM_DATABASE=WACOM Co.,Ltd + +OUI:0013BD* + ID_OUI_FROM_DATABASE=HYMATOM SA + +OUI:0013B8* + ID_OUI_FROM_DATABASE=RyCo Electronic Systems Limited + +OUI:00134E* + ID_OUI_FROM_DATABASE=Valox Systems, Inc. + +OUI:001353* + ID_OUI_FROM_DATABASE=HYDAC Filtertechnik GMBH + +OUI:00134D* + ID_OUI_FROM_DATABASE=Inepro BV + +OUI:001347* + ID_OUI_FROM_DATABASE=Red Lion Controls, LP + +OUI:00133B* + ID_OUI_FROM_DATABASE=Speed Dragon Multimedia Limited + +OUI:001340* + ID_OUI_FROM_DATABASE=AD.EL s.r.l. + +OUI:00132E* + ID_OUI_FROM_DATABASE=ITian Coporation + +OUI:001328* + ID_OUI_FROM_DATABASE=Westech Korea Inc., + +OUI:00132D* + ID_OUI_FROM_DATABASE=iWise Communications + +OUI:001334* + ID_OUI_FROM_DATABASE=Arkados, Inc. + +OUI:0013B3* + ID_OUI_FROM_DATABASE=Ecom Communications Technology Co., Ltd. + +OUI:0013AC* + ID_OUI_FROM_DATABASE=Sunmyung Electronics Co., LTD + +OUI:0013A6* + ID_OUI_FROM_DATABASE=Extricom Ltd + +OUI:0013A5* + ID_OUI_FROM_DATABASE=General Solutions, LTD. + +OUI:0013A0* + ID_OUI_FROM_DATABASE=ALGOSYSTEM Co., Ltd. + +OUI:001399* + ID_OUI_FROM_DATABASE=STAC Corporation. + +OUI:001393* + ID_OUI_FROM_DATABASE=Panta Systems, Inc. + +OUI:001394* + ID_OUI_FROM_DATABASE=Infohand Co.,Ltd + +OUI:00138D* + ID_OUI_FROM_DATABASE=Kinghold + +OUI:0012C8* + ID_OUI_FROM_DATABASE=Perfect tech + +OUI:0012B9* + ID_OUI_FROM_DATABASE=Fusion Digital Technology + +OUI:0012BE* + ID_OUI_FROM_DATABASE=Astek Corporation + +OUI:0012AC* + ID_OUI_FROM_DATABASE=ONTIMETEK INC. + +OUI:0012AB* + ID_OUI_FROM_DATABASE=WiLife, Inc. + +OUI:0012B2* + ID_OUI_FROM_DATABASE=AVOLITES LTD. + +OUI:0012A6* + ID_OUI_FROM_DATABASE=Dolby Australia + +OUI:001378* + ID_OUI_FROM_DATABASE=Qsan Technology, Inc. + +OUI:00137D* + ID_OUI_FROM_DATABASE=Dynalab, Inc. + +OUI:001384* + ID_OUI_FROM_DATABASE=Advanced Motion Controls + +OUI:00137E* + ID_OUI_FROM_DATABASE=CorEdge Networks, Inc. + +OUI:00136C* + ID_OUI_FROM_DATABASE=TomTom + +OUI:00136B* + ID_OUI_FROM_DATABASE=E-TEC + +OUI:001359* + ID_OUI_FROM_DATABASE=ProTelevision Technologies A/S + +OUI:00135E* + ID_OUI_FROM_DATABASE=EAB/RWI/K + +OUI:00129F* + ID_OUI_FROM_DATABASE=RAE Systems + +OUI:001299* + ID_OUI_FROM_DATABASE=Ktech Telecommunications Inc + +OUI:00129A* + ID_OUI_FROM_DATABASE=IRT Electronics Pty Ltd + +OUI:00128C* + ID_OUI_FROM_DATABASE=Woodward Governor + +OUI:001293* + ID_OUI_FROM_DATABASE=GE Energy + +OUI:001287* + ID_OUI_FROM_DATABASE=Digital Everywhere Unterhaltungselektronik GmbH + +OUI:001280* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00131E* + ID_OUI_FROM_DATABASE=Peiker acustic GmbH & Co. KG + +OUI:001323* + ID_OUI_FROM_DATABASE=Cap Co., Ltd. + +OUI:00130B* + ID_OUI_FROM_DATABASE=Mextal B.V. + +OUI:001312* + ID_OUI_FROM_DATABASE=Amedia Networks Inc. + +OUI:0012F8* + ID_OUI_FROM_DATABASE=WNI Resources, LLC + +OUI:0012FF* + ID_OUI_FROM_DATABASE=Lely Industries N.V. + +OUI:001304* + ID_OUI_FROM_DATABASE=Flaircomm Technologies Co. LTD + +OUI:001410* + ID_OUI_FROM_DATABASE=Suzhou Keda Technology CO.,Ltd + +OUI:001417* + ID_OUI_FROM_DATABASE=RSE Informations Technologie GmbH + +OUI:001408* + ID_OUI_FROM_DATABASE=Eka Systems Inc. + +OUI:001402* + ID_OUI_FROM_DATABASE=kk-electronic a/s + +OUI:001401* + ID_OUI_FROM_DATABASE=Rivertree Networks Corp. + +OUI:0013FB* + ID_OUI_FROM_DATABASE=RKC INSTRUMENT INC. + +OUI:0013F4* + ID_OUI_FROM_DATABASE=Psitek (Pty) Ltd + +OUI:0013EF* + ID_OUI_FROM_DATABASE=Kingjon Digital Technology Co.,Ltd + +OUI:0011F7* + ID_OUI_FROM_DATABASE=Shenzhen Forward Industry Co., Ltd + +OUI:0011F2* + ID_OUI_FROM_DATABASE=Institute of Network Technologies + +OUI:0011EB* + ID_OUI_FROM_DATABASE=Innovative Integration + +OUI:0011E6* + ID_OUI_FROM_DATABASE=Scientific Atlanta + +OUI:0011E5* + ID_OUI_FROM_DATABASE=KCodes Corporation + +OUI:0011DF* + ID_OUI_FROM_DATABASE=Current Energy + +OUI:0011D3* + ID_OUI_FROM_DATABASE=NextGenTel Holding ASA + +OUI:00110E* + ID_OUI_FROM_DATABASE=Tsurusaki Sealand Transportation Co. Ltd. + +OUI:001115* + ID_OUI_FROM_DATABASE=EPIN Technologies, Inc. + +OUI:001114* + ID_OUI_FROM_DATABASE=EverFocus Electronics Corp. + +OUI:001107* + ID_OUI_FROM_DATABASE=RGB Networks Inc. + +OUI:001108* + ID_OUI_FROM_DATABASE=Orbital Data Corporation + +OUI:001102* + ID_OUI_FROM_DATABASE=Aurora Multimedia Corp. + +OUI:000FFC* + ID_OUI_FROM_DATABASE=Merit Li-Lin Ent. + +OUI:000FDA* + ID_OUI_FROM_DATABASE=YAZAKI CORPORATION + +OUI:000FF3* + ID_OUI_FROM_DATABASE=Jung Myoung Communications&Technology + +OUI:0011A2* + ID_OUI_FROM_DATABASE=Manufacturing Technology Inc + +OUI:00119B* + ID_OUI_FROM_DATABASE=Telesynergy Research Inc. + +OUI:00118C* + ID_OUI_FROM_DATABASE=Missouri Department of Transportation + +OUI:001191* + ID_OUI_FROM_DATABASE=CTS-Clima Temperatur Systeme GmbH + +OUI:00118B* + ID_OUI_FROM_DATABASE=Alcatel-Lucent, Enterprise Business Group + +OUI:001196* + ID_OUI_FROM_DATABASE=Actuality Systems, Inc. + +OUI:00117E* + ID_OUI_FROM_DATABASE=Progeny, A division of Midmark Corp + +OUI:001179* + ID_OUI_FROM_DATABASE=Singular Technology Co. Ltd. + +OUI:001172* + ID_OUI_FROM_DATABASE=COTRON CORPORATION + +OUI:001166* + ID_OUI_FROM_DATABASE=Taelim Electronics Co., Ltd. + +OUI:00116B* + ID_OUI_FROM_DATABASE=Digital Data Communications Asia Co.,Ltd + +OUI:00116C* + ID_OUI_FROM_DATABASE=Nanwang Multimedia Inc.,Ltd + +OUI:001162* + ID_OUI_FROM_DATABASE=STAR MICRONICS CO.,LTD. + +OUI:001161* + ID_OUI_FROM_DATABASE=NetStreams, LLC + +OUI:001155* + ID_OUI_FROM_DATABASE=Sevis Systems + +OUI:00115C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001147* + ID_OUI_FROM_DATABASE=Secom-Industry co.LTD. + +OUI:00114C* + ID_OUI_FROM_DATABASE=caffeina applied research ltd. + +OUI:001274* + ID_OUI_FROM_DATABASE=NIT lab + +OUI:00127A* + ID_OUI_FROM_DATABASE=Sanyu Industry Co.,Ltd. + +OUI:00126D* + ID_OUI_FROM_DATABASE=University of California, Berkeley + +OUI:001268* + ID_OUI_FROM_DATABASE=IPS d.o.o. + +OUI:001267* + ID_OUI_FROM_DATABASE=Panasonic Corporation + +OUI:001261* + ID_OUI_FROM_DATABASE=Adaptix, Inc + +OUI:001257* + ID_OUI_FROM_DATABASE=LeapComm Communication Technologies Inc. + +OUI:001222* + ID_OUI_FROM_DATABASE=Skardin (UK) Ltd + +OUI:001227* + ID_OUI_FROM_DATABASE=Franklin Electric Co., Inc. + +OUI:00121B* + ID_OUI_FROM_DATABASE=Sound Devices, LLC + +OUI:001221* + ID_OUI_FROM_DATABASE=B.Braun Melsungen AG + +OUI:001214* + ID_OUI_FROM_DATABASE=Koenig & Bauer AG + +OUI:00120F* + ID_OUI_FROM_DATABASE=IEEE 802.3 + +OUI:001208* + ID_OUI_FROM_DATABASE=Gantner Instruments GmbH + +OUI:001201* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001202* + ID_OUI_FROM_DATABASE=Decrane Aerospace - Audio International Inc. + +OUI:0011C7* + ID_OUI_FROM_DATABASE=Raymarine UK Ltd + +OUI:0011CC* + ID_OUI_FROM_DATABASE=Guangzhou Jinpeng Group Co.,Ltd. + +OUI:0011B5* + ID_OUI_FROM_DATABASE=Shenzhen Powercom Co.,Ltd + +OUI:0011BA* + ID_OUI_FROM_DATABASE=Elexol Pty Ltd + +OUI:0011C1* + ID_OUI_FROM_DATABASE=4P MOBILE DATA PROCESSING + +OUI:0011A8* + ID_OUI_FROM_DATABASE=Quest Technologies + +OUI:0011A7* + ID_OUI_FROM_DATABASE=Infilco Degremont Inc. + +OUI:001250* + ID_OUI_FROM_DATABASE=Tokyo Aircaft Instrument Co., Ltd. + +OUI:00124B* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001244* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001238* + ID_OUI_FROM_DATABASE=SetaBox Technology Co., Ltd. + +OUI:00123D* + ID_OUI_FROM_DATABASE=GES Co, Ltd + +OUI:00123E* + ID_OUI_FROM_DATABASE=ERUNE technology Co., Ltd. + +OUI:00122C* + ID_OUI_FROM_DATABASE=Soenen Controls N.V. + +OUI:001231* + ID_OUI_FROM_DATABASE=Motion Control Systems, Inc. + +OUI:001146* + ID_OUI_FROM_DATABASE=Telecard-Pribor Ltd + +OUI:001140* + ID_OUI_FROM_DATABASE=Nanometrics Inc. + +OUI:001139* + ID_OUI_FROM_DATABASE=STOEBER ANTRIEBSTECHNIK GmbH + Co. KG. + +OUI:00113A* + ID_OUI_FROM_DATABASE=SHINBORAM + +OUI:001134* + ID_OUI_FROM_DATABASE=MediaCell, Inc. + +OUI:001127* + ID_OUI_FROM_DATABASE=TASI, Inc + +OUI:00112A* + ID_OUI_FROM_DATABASE=Niko NV + +OUI:001121* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000EBB* + ID_OUI_FROM_DATABASE=Everbee Networks + +OUI:000EB4* + ID_OUI_FROM_DATABASE=GUANGZHOU GAOKE COMMUNICATIONS TECHNOLOGY CO.LTD. + +OUI:000EAE* + ID_OUI_FROM_DATABASE=GAWELL TECHNOLOGIES CORP. + +OUI:000EA8* + ID_OUI_FROM_DATABASE=United Technologists Europe Limited + +OUI:000EAD* + ID_OUI_FROM_DATABASE=Metanoia Technologies, Inc. + +OUI:000EA1* + ID_OUI_FROM_DATABASE=Formosa Teletek Corporation + +OUI:000E9C* + ID_OUI_FROM_DATABASE=Benchmark Electronics + +OUI:000E9B* + ID_OUI_FROM_DATABASE=Ambit Microsystems Corporation + +OUI:000E8E* + ID_OUI_FROM_DATABASE=SparkLAN Communications, Inc. + +OUI:000E95* + ID_OUI_FROM_DATABASE=Fujiya Denki Seisakusho Co.,Ltd. + +OUI:000FC1* + ID_OUI_FROM_DATABASE=WAVE Corporation + +OUI:000FC8* + ID_OUI_FROM_DATABASE=Chantry Networks + +OUI:000FC7* + ID_OUI_FROM_DATABASE=Dionica R&D Ltd. + +OUI:000FBA* + ID_OUI_FROM_DATABASE=Tevebox AB + +OUI:000FA7* + ID_OUI_FROM_DATABASE=Raptor Networks Technology + +OUI:000FAE* + ID_OUI_FROM_DATABASE=E2O Communications + +OUI:000FA8* + ID_OUI_FROM_DATABASE=Photometrics, Inc. + +OUI:000F9A* + ID_OUI_FROM_DATABASE=Synchrony, Inc. + +OUI:000FA2* + ID_OUI_FROM_DATABASE=2xWireless + +OUI:000E89* + ID_OUI_FROM_DATABASE=CLEMATIC + +OUI:000E82* + ID_OUI_FROM_DATABASE=Commtech Wireless + +OUI:000E7C* + ID_OUI_FROM_DATABASE=Televes S.A. + +OUI:000E76* + ID_OUI_FROM_DATABASE=GEMSOC INNOVISION INC. + +OUI:000E6E* + ID_OUI_FROM_DATABASE=MAT S.A. (Mircrelec Advanced Technology) + +OUI:000E72* + ID_OUI_FROM_DATABASE=CTS electronics + +OUI:000E68* + ID_OUI_FROM_DATABASE=E-TOP Network Technology Inc. + +OUI:000E67* + ID_OUI_FROM_DATABASE=Eltis Microelectronics Ltd. + +OUI:000FE7* + ID_OUI_FROM_DATABASE=Lutron Electronics Co., Inc. + +OUI:000FEC* + ID_OUI_FROM_DATABASE=ARKUS Inc. + +OUI:000FE0* + ID_OUI_FROM_DATABASE=NComputing Co.,Ltd. + +OUI:000FD4* + ID_OUI_FROM_DATABASE=Soundcraft + +OUI:000FD9* + ID_OUI_FROM_DATABASE=FlexDSL Telecommunications AG + +OUI:000EEA* + ID_OUI_FROM_DATABASE=Shadong Luneng Jicheng Electronics,Co.,Ltd + +OUI:000EDD* + ID_OUI_FROM_DATABASE=SHURE INCORPORATED + +OUI:000EE4* + ID_OUI_FROM_DATABASE=BOE TECHNOLOGY GROUP CO.,LTD + +OUI:000ED8* + ID_OUI_FROM_DATABASE=Positron Access Solutions Corp + +OUI:000ECD* + ID_OUI_FROM_DATABASE=SKOV A/S + +OUI:000ECE* + ID_OUI_FROM_DATABASE=S.I.T.T.I. S.p.A. + +OUI:000ED3* + ID_OUI_FROM_DATABASE=Epicenter, Inc. + +OUI:000EC7* + ID_OUI_FROM_DATABASE=Motorola Korea + +OUI:000F93* + ID_OUI_FROM_DATABASE=Landis+Gyr Ltd. + +OUI:000F94* + ID_OUI_FROM_DATABASE=Genexis BV + +OUI:000F8E* + ID_OUI_FROM_DATABASE=DONGYANG TELECOM CO.,LTD. + +OUI:000F87* + ID_OUI_FROM_DATABASE=Maxcess International + +OUI:000F82* + ID_OUI_FROM_DATABASE=Mortara Instrument, Inc. + +OUI:000F81* + ID_OUI_FROM_DATABASE=PAL Pacific Inc. + +OUI:000F74* + ID_OUI_FROM_DATABASE=Qamcom Technology AB + +OUI:000F7B* + ID_OUI_FROM_DATABASE=Arce Sistemas, S.A. + +OUI:000F68* + ID_OUI_FROM_DATABASE=Vavic Network Technology, Inc. + +OUI:000F6F* + ID_OUI_FROM_DATABASE=FTA Communication Technologies + +OUI:000F62* + ID_OUI_FROM_DATABASE=Alcatel Bell Space N.V. + +OUI:000F5C* + ID_OUI_FROM_DATABASE=Day One Digital Media Limited + +OUI:000F55* + ID_OUI_FROM_DATABASE=Datawire Communication Networks Inc. + +OUI:000F49* + ID_OUI_FROM_DATABASE=Northover Solutions Limited + +OUI:000F50* + ID_OUI_FROM_DATABASE=StreamScale Limited + +OUI:000F42* + ID_OUI_FROM_DATABASE=Xalyo Systems + +OUI:000F1C* + ID_OUI_FROM_DATABASE=DigitAll World Co., Ltd + +OUI:000F0A* + ID_OUI_FROM_DATABASE=Clear Edge Networks + +OUI:000F09* + ID_OUI_FROM_DATABASE=Private + +OUI:000F03* + ID_OUI_FROM_DATABASE=COM&C CO., LTD + +OUI:000EF7* + ID_OUI_FROM_DATABASE=Vulcan Portals Inc + +OUI:000EFC* + ID_OUI_FROM_DATABASE=JTAG Technologies B.V. + +OUI:000EE9* + ID_OUI_FROM_DATABASE=WayTech Development, Inc. + +OUI:000EF0* + ID_OUI_FROM_DATABASE=Festo AG & Co. KG + +OUI:000F4F* + ID_OUI_FROM_DATABASE=Cadmus Technology Ltd + +OUI:000F35* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000F2E* + ID_OUI_FROM_DATABASE=Megapower International Corp. + +OUI:000F29* + ID_OUI_FROM_DATABASE=Augmentix Corporation + +OUI:000F22* + ID_OUI_FROM_DATABASE=Helius, Inc. + +OUI:000F0F* + ID_OUI_FROM_DATABASE=Real ID Technology Co., Ltd. + +OUI:000F16* + ID_OUI_FROM_DATABASE=JAY HOW TECHNOLOGY CO., + +OUI:000F1B* + ID_OUI_FROM_DATABASE=Ego Systems Inc. + +OUI:000D74* + ID_OUI_FROM_DATABASE=Sand Network Systems, Inc. + +OUI:000D7B* + ID_OUI_FROM_DATABASE=Consensys Computers Inc. + +OUI:000D6E* + ID_OUI_FROM_DATABASE=K-Patents Oy + +OUI:000D68* + ID_OUI_FROM_DATABASE=Vinci Systems, Inc. + +OUI:000D6D* + ID_OUI_FROM_DATABASE=K-Tech Devices Corp. + +OUI:000D5B* + ID_OUI_FROM_DATABASE=Smart Empire Investments Limited + +OUI:000D5C* + ID_OUI_FROM_DATABASE=Robert Bosch GmbH, VT-ATMO + +OUI:000D61* + ID_OUI_FROM_DATABASE=Giga-Byte Technology Co., Ltd. + +OUI:000D55* + ID_OUI_FROM_DATABASE=SANYCOM Technology Co.,Ltd + +OUI:000D49* + ID_OUI_FROM_DATABASE=Triton Systems of Delaware, Inc. + +OUI:000D4E* + ID_OUI_FROM_DATABASE=NDR Co.,LTD. + +OUI:000E5B* + ID_OUI_FROM_DATABASE=ParkerVision - Direct2Data + +OUI:000E55* + ID_OUI_FROM_DATABASE=AUVITRAN + +OUI:000E56* + ID_OUI_FROM_DATABASE=4G Systems GmbH & Co. KG + +OUI:000E4F* + ID_OUI_FROM_DATABASE=Trajet GmbH + +OUI:000E48* + ID_OUI_FROM_DATABASE=Lipman TransAction Solutions + +OUI:000E43* + ID_OUI_FROM_DATABASE=G-Tek Electronics Sdn. Bhd. + +OUI:000E34* + ID_OUI_FROM_DATABASE=NexGen City, LP + +OUI:000E3B* + ID_OUI_FROM_DATABASE=Hawking Technologies, Inc. + +OUI:000E2F* + ID_OUI_FROM_DATABASE=Roche Diagnostics GmbH + +OUI:000DFB* + ID_OUI_FROM_DATABASE=Komax AG + +OUI:000DE9* + ID_OUI_FROM_DATABASE=Napatech Aps + +OUI:000DEE* + ID_OUI_FROM_DATABASE=Andrew RF Power Amplifier Group + +OUI:000DE2* + ID_OUI_FROM_DATABASE=CMZ Sistemi Elettronici + +OUI:000DDC* + ID_OUI_FROM_DATABASE=VAC + +OUI:000DD6* + ID_OUI_FROM_DATABASE=ITI LTD + +OUI:000DDB* + ID_OUI_FROM_DATABASE=AIRWAVE TECHNOLOGIES INC. + +OUI:000DCA* + ID_OUI_FROM_DATABASE=Tait Electronics + +OUI:000DCF* + ID_OUI_FROM_DATABASE=Cidra Corp. + +OUI:000E28* + ID_OUI_FROM_DATABASE=Dynamic Ratings P/L + +OUI:000E22* + ID_OUI_FROM_DATABASE=Private + +OUI:000E21* + ID_OUI_FROM_DATABASE=MTU Friedrichshafen GmbH + +OUI:000E15* + ID_OUI_FROM_DATABASE=Tadlys LTD + +OUI:000E1C* + ID_OUI_FROM_DATABASE=Hach Company + +OUI:000E0D* + ID_OUI_FROM_DATABASE=Hesch Schröder GmbH + +OUI:000E10* + ID_OUI_FROM_DATABASE=C-guys, Inc. + +OUI:000DF5* + ID_OUI_FROM_DATABASE=Teletronics International Inc. + +OUI:000DFC* + ID_OUI_FROM_DATABASE=ITFOR Inc. + +OUI:000E01* + ID_OUI_FROM_DATABASE=ASIP Technologies Inc. + +OUI:000CF0* + ID_OUI_FROM_DATABASE=M & N GmbH + +OUI:000CF5* + ID_OUI_FROM_DATABASE=InfoExpress + +OUI:000CE0* + ID_OUI_FROM_DATABASE=Trek Diagnostics Inc. + +OUI:000CE4* + ID_OUI_FROM_DATABASE=NeuroCom International, Inc. + +OUI:000CE9* + ID_OUI_FROM_DATABASE=BLOOMBERG L.P. + +OUI:000CCE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000CD4* + ID_OUI_FROM_DATABASE=Positron Public Safety Systems inc. + +OUI:000CCD* + ID_OUI_FROM_DATABASE=IEC - TC57 + +OUI:000D15* + ID_OUI_FROM_DATABASE=Voipac s.r.o. + +OUI:000D16* + ID_OUI_FROM_DATABASE=UHS Systems Pty Ltd + +OUI:000D1B* + ID_OUI_FROM_DATABASE=Kyoto Electronics Manufacturing Co., Ltd. + +OUI:000D0F* + ID_OUI_FROM_DATABASE=Finlux Ltd + +OUI:000D03* + ID_OUI_FROM_DATABASE=Matrics, Inc. + +OUI:000D08* + ID_OUI_FROM_DATABASE=AboveCable, Inc. + +OUI:000CFC* + ID_OUI_FROM_DATABASE=S2io Technologies Corp + +OUI:000CF6* + ID_OUI_FROM_DATABASE=Sitecom Europe BV + +OUI:000DA3* + ID_OUI_FROM_DATABASE=Emerging Technologies Limited + +OUI:000D9C* + ID_OUI_FROM_DATABASE=Elan GmbH & Co KG + +OUI:000D96* + ID_OUI_FROM_DATABASE=Vtera Technology Inc. + +OUI:000D95* + ID_OUI_FROM_DATABASE=Opti-cell, Inc. + +OUI:000D90* + ID_OUI_FROM_DATABASE=Factum Electronics AB + +OUI:000D89* + ID_OUI_FROM_DATABASE=Bils Technology Inc + +OUI:000D80* + ID_OUI_FROM_DATABASE=Online Development Inc + +OUI:000DC9* + ID_OUI_FROM_DATABASE=THALES Elektronik Systeme GmbH + +OUI:000DC3* + ID_OUI_FROM_DATABASE=First Communication, Inc. + +OUI:000DBC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000DB7* + ID_OUI_FROM_DATABASE=SANKO ELECTRIC CO,.LTD + +OUI:000DB0* + ID_OUI_FROM_DATABASE=Olym-tech Co.,Ltd. + +OUI:000DA8* + ID_OUI_FROM_DATABASE=Teletronics Technology Corporation + +OUI:000D41* + ID_OUI_FROM_DATABASE=Siemens AG ICM MP UC RD IT KLF1 + +OUI:000D3A* + ID_OUI_FROM_DATABASE=Microsoft Corp. + +OUI:000D35* + ID_OUI_FROM_DATABASE=PAC International Ltd + +OUI:000D2E* + ID_OUI_FROM_DATABASE=Matsushita Avionics Systems Corporation + +OUI:000D28* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000D22* + ID_OUI_FROM_DATABASE=Unitronics LTD + +OUI:000D27* + ID_OUI_FROM_DATABASE=MICROPLEX Printware AG + +OUI:000C21* + ID_OUI_FROM_DATABASE=Faculty of Science and Technology, Keio University + +OUI:000C11* + ID_OUI_FROM_DATABASE=NIPPON DEMPA CO.,LTD. + +OUI:000C10* + ID_OUI_FROM_DATABASE=PNI Corporation + +OUI:000C12* + ID_OUI_FROM_DATABASE=Micro-Optronic-Messtechnik GmbH + +OUI:000C17* + ID_OUI_FROM_DATABASE=AJA Video Systems Inc + +OUI:000C04* + ID_OUI_FROM_DATABASE=Tecnova + +OUI:000C0B* + ID_OUI_FROM_DATABASE=Broadbus Technologies + +OUI:000BF8* + ID_OUI_FROM_DATABASE=Infinera + +OUI:000BFF* + ID_OUI_FROM_DATABASE=Berkeley Camera Engineering + +OUI:000BEC* + ID_OUI_FROM_DATABASE=NIPPON ELECTRIC INSTRUMENT, INC. + +OUI:000BB8* + ID_OUI_FROM_DATABASE=Kihoku Electronic Co. + +OUI:000BBD* + ID_OUI_FROM_DATABASE=Connexionz Limited + +OUI:000BAD* + ID_OUI_FROM_DATABASE=PC-PoS Inc. + +OUI:000BA0* + ID_OUI_FROM_DATABASE=T&L Information Inc. + +OUI:000BA7* + ID_OUI_FROM_DATABASE=Maranti Networks + +OUI:000BAC* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:000B93* + ID_OUI_FROM_DATABASE=Ritter Elektronik + +OUI:000B98* + ID_OUI_FROM_DATABASE=NiceTechVision + +OUI:000B9B* + ID_OUI_FROM_DATABASE=Sirius System Co, Ltd. + +OUI:000B8C* + ID_OUI_FROM_DATABASE=Flextronics + +OUI:000BF1* + ID_OUI_FROM_DATABASE=LAP Laser Applikations + +OUI:000BDF* + ID_OUI_FROM_DATABASE=Shenzhen RouterD Networks Limited + +OUI:000BDE* + ID_OUI_FROM_DATABASE=TELDIX GmbH + +OUI:000BE0* + ID_OUI_FROM_DATABASE=SercoNet Ltd. + +OUI:000BE5* + ID_OUI_FROM_DATABASE=HIMS International Corporation + +OUI:000BD9* + ID_OUI_FROM_DATABASE=General Hydrogen + +OUI:000BAE* + ID_OUI_FROM_DATABASE=Vitals System Inc. + +OUI:000BD0* + ID_OUI_FROM_DATABASE=XiMeta Technology Americas Inc. + +OUI:000BD5* + ID_OUI_FROM_DATABASE=Nvergence, Inc. + +OUI:000BC4* + ID_OUI_FROM_DATABASE=BIOTRONIK GmbH & Co + +OUI:000BC9* + ID_OUI_FROM_DATABASE=Electroline Equipment + +OUI:000BB1* + ID_OUI_FROM_DATABASE=Super Star Technology Co., Ltd. + +OUI:000BB6* + ID_OUI_FROM_DATABASE=Metalligence Technology Corp. + +OUI:000B79* + ID_OUI_FROM_DATABASE=X-COM, Inc. + +OUI:000B80* + ID_OUI_FROM_DATABASE=Lycium Networks + +OUI:000B87* + ID_OUI_FROM_DATABASE=American Reliance Inc. + +OUI:000B6D* + ID_OUI_FROM_DATABASE=SOLECTRON JAPAN NAKANIIDA + +OUI:000B74* + ID_OUI_FROM_DATABASE=Kingwave Technology Co., Ltd. + +OUI:000B67* + ID_OUI_FROM_DATABASE=Topview Technology Corporation + +OUI:000B61* + ID_OUI_FROM_DATABASE=Friedrich Lütze GmbH & Co. KG + +OUI:000B66* + ID_OUI_FROM_DATABASE=Teralink Communications + +OUI:000B68* + ID_OUI_FROM_DATABASE=Addvalue Communications Pte Ltd + +OUI:000B58* + ID_OUI_FROM_DATABASE=Astronautics C.A LTD + +OUI:000B50* + ID_OUI_FROM_DATABASE=Oxygnet + +OUI:000B44* + ID_OUI_FROM_DATABASE=Concord IDea Corp. + +OUI:000B49* + ID_OUI_FROM_DATABASE=RF-Link System Inc. + +OUI:000B4B* + ID_OUI_FROM_DATABASE=VISIOWAVE SA + +OUI:000B31* + ID_OUI_FROM_DATABASE=Yantai ZhiYang Scientific and technology industry CO., LTD + +OUI:000B3D* + ID_OUI_FROM_DATABASE=CONTAL OK Ltd. + +OUI:000B38* + ID_OUI_FROM_DATABASE=Knürr GmbH + +OUI:000B2A* + ID_OUI_FROM_DATABASE=HOWTEL Co., Ltd. + +OUI:000B2C* + ID_OUI_FROM_DATABASE=Eiki Industrial Co. Ltd. + +OUI:000C97* + ID_OUI_FROM_DATABASE=NV ADB TTV Technologies SA + +OUI:000C9C* + ID_OUI_FROM_DATABASE=Chongho information & communications + +OUI:000C9E* + ID_OUI_FROM_DATABASE=MemoryLink Corp. + +OUI:000C89* + ID_OUI_FROM_DATABASE=AC Electric Vehicles, Ltd. + +OUI:000C8B* + ID_OUI_FROM_DATABASE=Connect Tech Inc + +OUI:000C90* + ID_OUI_FROM_DATABASE=Octasic Inc. + +OUI:000C84* + ID_OUI_FROM_DATABASE=Eazix, Inc. + +OUI:000C75* + ID_OUI_FROM_DATABASE=Oriental integrated electronics. LTD + +OUI:000C77* + ID_OUI_FROM_DATABASE=Life Racing Ltd + +OUI:000C7C* + ID_OUI_FROM_DATABASE=Internet Information Image Inc. + +OUI:000C43* + ID_OUI_FROM_DATABASE=Ralink Technology, Corp. + +OUI:000C45* + ID_OUI_FROM_DATABASE=Animation Technologies Inc. + +OUI:000C3C* + ID_OUI_FROM_DATABASE=MediaChorus, Inc. + +OUI:000C32* + ID_OUI_FROM_DATABASE=Avionic Design Development GmbH + +OUI:000C35* + ID_OUI_FROM_DATABASE=KaVo Dental GmbH & Co. KG + +OUI:000C2B* + ID_OUI_FROM_DATABASE=ELIAS Technology, Inc. + +OUI:000C28* + ID_OUI_FROM_DATABASE=RIFATRON + +OUI:000C1C* + ID_OUI_FROM_DATABASE=MicroWeb Co., Ltd. + +OUI:000C64* + ID_OUI_FROM_DATABASE=X2 MSA Group + +OUI:000C69* + ID_OUI_FROM_DATABASE=National Radio Astronomy Observatory + +OUI:000C70* + ID_OUI_FROM_DATABASE=ACC GmbH + +OUI:000C51* + ID_OUI_FROM_DATABASE=Scientific Technologies Inc. + +OUI:000C56* + ID_OUI_FROM_DATABASE=Megatel Computer (1986) Corp. + +OUI:000C58* + ID_OUI_FROM_DATABASE=M&S Systems + +OUI:000C5D* + ID_OUI_FROM_DATABASE=CHIC TECHNOLOGY (CHINA) CORP. + +OUI:000C4A* + ID_OUI_FROM_DATABASE=Cygnus Microsystems (P) Limited + +OUI:000CC8* + ID_OUI_FROM_DATABASE=Xytronix Research & Design, Inc. + +OUI:000CBB* + ID_OUI_FROM_DATABASE=ISKRAEMECO + +OUI:000CB5* + ID_OUI_FROM_DATABASE=Premier Technolgies, Inc + +OUI:000CBC* + ID_OUI_FROM_DATABASE=Iscutum + +OUI:000CA3* + ID_OUI_FROM_DATABASE=Rancho Technology, Inc. + +OUI:000CAA* + ID_OUI_FROM_DATABASE=Cubic Transportation Systems Inc + +OUI:000A38* + ID_OUI_FROM_DATABASE=Apani Networks + +OUI:000A3F* + ID_OUI_FROM_DATABASE=Data East Corporation + +OUI:000A44* + ID_OUI_FROM_DATABASE=Avery Dennison Deutschland GmbH + +OUI:000A46* + ID_OUI_FROM_DATABASE=ARO WELDING TECHNOLOGIES SAS + +OUI:000A33* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:000A31* + ID_OUI_FROM_DATABASE=HCV Consulting + +OUI:000A2C* + ID_OUI_FROM_DATABASE=Active Tchnology Corporation + +OUI:004252* + ID_OUI_FROM_DATABASE=RLX Technologies + +OUI:000A2A* + ID_OUI_FROM_DATABASE=QSI Systems Inc. + +OUI:000A1E* + ID_OUI_FROM_DATABASE=Red-M Products Limited + +OUI:000A23* + ID_OUI_FROM_DATABASE=Parama Networks Inc + +OUI:000A17* + ID_OUI_FROM_DATABASE=NESTAR COMMUNICATIONS, INC + +OUI:000A1C* + ID_OUI_FROM_DATABASE=Bridge Information Co., Ltd. + +OUI:000B19* + ID_OUI_FROM_DATABASE=Vernier Networks, Inc. + +OUI:000B1E* + ID_OUI_FROM_DATABASE=KAPPA opto-electronics GmbH + +OUI:000B25* + ID_OUI_FROM_DATABASE=Aeluros + +OUI:000B17* + ID_OUI_FROM_DATABASE=MKS Instruments + +OUI:000B12* + ID_OUI_FROM_DATABASE=NURI Telecom Co., Ltd. + +OUI:000B0B* + ID_OUI_FROM_DATABASE=Corrent Corporation + +OUI:000AFA* + ID_OUI_FROM_DATABASE=Traverse Technologies Australia + +OUI:000AFF* + ID_OUI_FROM_DATABASE=Kilchherr Elektronik AG + +OUI:000AF3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000AF8* + ID_OUI_FROM_DATABASE=American Telecare Inc. + +OUI:000AEE* + ID_OUI_FROM_DATABASE=GCD Hard- & Software GmbH + +OUI:000A06* + ID_OUI_FROM_DATABASE=Teledex LLC + +OUI:000A09* + ID_OUI_FROM_DATABASE=TaraCom Integrated Products, Inc. + +OUI:000A0B* + ID_OUI_FROM_DATABASE=Sealevel Systems, Inc. + +OUI:000A10* + ID_OUI_FROM_DATABASE=FAST media integrations AG + +OUI:0009F7* + ID_OUI_FROM_DATABASE=SED, a division of Calian + +OUI:000A01* + ID_OUI_FROM_DATABASE=SOHOware, Inc. + +OUI:0009E9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0009F0* + ID_OUI_FROM_DATABASE=Shimizu Technology Inc. + +OUI:0009EA* + ID_OUI_FROM_DATABASE=YEM Inc. + +OUI:0009E4* + ID_OUI_FROM_DATABASE=K Tech Infosystem Inc. + +OUI:0009D8* + ID_OUI_FROM_DATABASE=Fält Communications AB + +OUI:0009DD* + ID_OUI_FROM_DATABASE=Mavin Technology Inc. + +OUI:0009B1* + ID_OUI_FROM_DATABASE=Kanematsu Electronics, Ltd. + +OUI:0009A3* + ID_OUI_FROM_DATABASE=Leadfly Techologies Corp. Ltd. + +OUI:0009AA* + ID_OUI_FROM_DATABASE=Data Comm for Business, Inc. + +OUI:0009A4* + ID_OUI_FROM_DATABASE=HARTEC Corporation + +OUI:00099E* + ID_OUI_FROM_DATABASE=Testech, Inc. + +OUI:000992* + ID_OUI_FROM_DATABASE=InterEpoch Technology,INC. + +OUI:000991* + ID_OUI_FROM_DATABASE=GE Fanuc Automation Manufacturing, Inc. + +OUI:00098B* + ID_OUI_FROM_DATABASE=Entropic Communications, Inc. + +OUI:000AB0* + ID_OUI_FROM_DATABASE=LOYTEC electronics GmbH + +OUI:000AB7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000AA4* + ID_OUI_FROM_DATABASE=SHANGHAI SURVEILLANCE TECHNOLOGY CO,LTD + +OUI:000AA9* + ID_OUI_FROM_DATABASE=Brooks Automation GmbH + +OUI:000A91* + ID_OUI_FROM_DATABASE=HemoCue AB + +OUI:000A9D* + ID_OUI_FROM_DATABASE=King Young Technology Co. Ltd. + +OUI:000A8C* + ID_OUI_FROM_DATABASE=Guardware Systems Ltd. + +OUI:000A97* + ID_OUI_FROM_DATABASE=SONICblue, Inc. + +OUI:000A7D* + ID_OUI_FROM_DATABASE=Valo, Inc. + +OUI:000A84* + ID_OUI_FROM_DATABASE=Rainsun Enterprise Co., Ltd. + +OUI:000A89* + ID_OUI_FROM_DATABASE=Creval Systems, Inc. + +OUI:0009D7* + ID_OUI_FROM_DATABASE=DC Security Products + +OUI:0009CA* + ID_OUI_FROM_DATABASE=iMaxNetworks(Shenzhen)Limited. + +OUI:0009D1* + ID_OUI_FROM_DATABASE=SERANOA NETWORKS INC + +OUI:0009C5* + ID_OUI_FROM_DATABASE=KINGENE Technology Corporation + +OUI:0009BD* + ID_OUI_FROM_DATABASE=Epygi Technologies, Ltd. + +OUI:0009B6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00097F* + ID_OUI_FROM_DATABASE=Vsecure 2000 LTD. + +OUI:000984* + ID_OUI_FROM_DATABASE=MyCasa Network Inc. + +OUI:000971* + ID_OUI_FROM_DATABASE=Time Management, Inc. + +OUI:000978* + ID_OUI_FROM_DATABASE=AIJI System Co., Ltd. + +OUI:000972* + ID_OUI_FROM_DATABASE=Securebase,Inc + +OUI:00096C* + ID_OUI_FROM_DATABASE=Imedia Semiconductor Corp. + +OUI:000965* + ID_OUI_FROM_DATABASE=HyunJu Computer Co., Ltd. + +OUI:000960* + ID_OUI_FROM_DATABASE=YOZAN Inc. + +OUI:000956* + ID_OUI_FROM_DATABASE=Network Systems Group, Ltd. (NSG) + +OUI:000955* + ID_OUI_FROM_DATABASE=Young Generation International Corp. + +OUI:000AE9* + ID_OUI_FROM_DATABASE=AirVast Technology Inc. + +OUI:000ADB* + ID_OUI_FROM_DATABASE=SkyPilot Network, Inc + +OUI:000ADD* + ID_OUI_FROM_DATABASE=Allworx Corp. + +OUI:000AE2* + ID_OUI_FROM_DATABASE=Binatone Electronics International, Ltd + +OUI:000ACA* + ID_OUI_FROM_DATABASE=YOKOYAMA SHOKAI CO.,Ltd. + +OUI:000ACF* + ID_OUI_FROM_DATABASE=PROVIDEO Multimedia Co. Ltd. + +OUI:000AD6* + ID_OUI_FROM_DATABASE=BeamReach Networks + +OUI:000ABC* + ID_OUI_FROM_DATABASE=Seabridge Ltd. + +OUI:000ABE* + ID_OUI_FROM_DATABASE=OPNET Technologies CO., LTD. + +OUI:000AC3* + ID_OUI_FROM_DATABASE=eM Technics Co., Ltd. + +OUI:000A78* + ID_OUI_FROM_DATABASE=OLITEC + +OUI:000A71* + ID_OUI_FROM_DATABASE=Avrio Technologies, Inc + +OUI:000A76* + ID_OUI_FROM_DATABASE=Beida Jade Bird Huaguang Technology Co.,Ltd + +OUI:000A63* + ID_OUI_FROM_DATABASE=DHD GmbH + +OUI:000A65* + ID_OUI_FROM_DATABASE=GentechMedia.co.,ltd. + +OUI:000A6A* + ID_OUI_FROM_DATABASE=SVM Microwaves s.r.o. + +OUI:000A5E* + ID_OUI_FROM_DATABASE=3COM Corporation + +OUI:000A52* + ID_OUI_FROM_DATABASE=AsiaRF Ltd. + +OUI:000A4B* + ID_OUI_FROM_DATABASE=DataPower Technology, Inc. + +OUI:00075A* + ID_OUI_FROM_DATABASE=Air Products and Chemicals, Inc. + +OUI:000754* + ID_OUI_FROM_DATABASE=Xyterra Computing, Inc. + +OUI:00074E* + ID_OUI_FROM_DATABASE=IPFRONT Inc + +OUI:00074D* + ID_OUI_FROM_DATABASE=Zebra Technologies Corp. + +OUI:000742* + ID_OUI_FROM_DATABASE=Ormazabal + +OUI:000748* + ID_OUI_FROM_DATABASE=The Imaging Source Europe + +OUI:000736* + ID_OUI_FROM_DATABASE=Data Video Technologies Co., Ltd. + +OUI:00073D* + ID_OUI_FROM_DATABASE=Nanjing Postel Telecommunications Co., Ltd. + +OUI:00073C* + ID_OUI_FROM_DATABASE=Telecom Design + +OUI:00072A* + ID_OUI_FROM_DATABASE=Innovance Networks + +OUI:00072F* + ID_OUI_FROM_DATABASE=Intransa, Inc. + +OUI:000730* + ID_OUI_FROM_DATABASE=Hutchison OPTEL Telecom Technology Co., Ltd. + +OUI:000725* + ID_OUI_FROM_DATABASE=Bematech International Corp. + +OUI:000818* + ID_OUI_FROM_DATABASE=Pixelworks, Inc. + +OUI:000812* + ID_OUI_FROM_DATABASE=GM-2 Corporation + +OUI:000811* + ID_OUI_FROM_DATABASE=VOIX Corporation + +OUI:00080B* + ID_OUI_FROM_DATABASE=Birka BPA Informationssystem AB + +OUI:000805* + ID_OUI_FROM_DATABASE=Techno-Holon Corporation + +OUI:00080C* + ID_OUI_FROM_DATABASE=VDA Elettronica spa + +OUI:0007FB* + ID_OUI_FROM_DATABASE=Giga Stream UMTS Technologies GmbH + +OUI:0007F5* + ID_OUI_FROM_DATABASE=Bridgeco Co AG + +OUI:0007E8* + ID_OUI_FROM_DATABASE=EdgeWave + +OUI:0007EF* + ID_OUI_FROM_DATABASE=Lockheed Martin Tactical Systems + +OUI:0007E2* + ID_OUI_FROM_DATABASE=Bitworks, Inc. + +OUI:0007D6* + ID_OUI_FROM_DATABASE=Commil Ltd. + +OUI:0007DC* + ID_OUI_FROM_DATABASE=Atek Co, Ltd. + +OUI:000923* + ID_OUI_FROM_DATABASE=Heaman System Co., Ltd + +OUI:00091D* + ID_OUI_FROM_DATABASE=Proteam Computer Corporation + +OUI:000924* + ID_OUI_FROM_DATABASE=Telebau GmbH + +OUI:000911* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000916* + ID_OUI_FROM_DATABASE=Listman Home Technologies, Inc. + +OUI:00090A* + ID_OUI_FROM_DATABASE=SnedFar Technology Co., Ltd. + +OUI:000904* + ID_OUI_FROM_DATABASE=MONDIAL electronic + +OUI:000903* + ID_OUI_FROM_DATABASE=Panasas, Inc + +OUI:0008FE* + ID_OUI_FROM_DATABASE=UNIK C&C Co.,Ltd. + +OUI:0008FA* + ID_OUI_FROM_DATABASE=Karl E.Brinkmann GmbH + +OUI:0008EE* + ID_OUI_FROM_DATABASE=Logic Product Development + +OUI:0008F0* + ID_OUI_FROM_DATABASE=Next Generation Systems, Inc. + +OUI:000948* + ID_OUI_FROM_DATABASE=Vista Control Systems, Corp. + +OUI:00094F* + ID_OUI_FROM_DATABASE=elmegt GmbH & Co. KG + +OUI:000943* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00093C* + ID_OUI_FROM_DATABASE=Jacques Technologies P/L + +OUI:000936* + ID_OUI_FROM_DATABASE=Ipetronik GmbH & Co. KG + +OUI:000935* + ID_OUI_FROM_DATABASE=Sandvine Incorporated + +OUI:000929* + ID_OUI_FROM_DATABASE=Sanyo Industries (UK) Limited + +OUI:000930* + ID_OUI_FROM_DATABASE=AeroConcierge Inc. + +OUI:0008E9* + ID_OUI_FROM_DATABASE=NextGig + +OUI:0008DC* + ID_OUI_FROM_DATABASE=Wiznet + +OUI:0008E2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0008DB* + ID_OUI_FROM_DATABASE=Corrigent Systems + +OUI:0008D6* + ID_OUI_FROM_DATABASE=HASSNET Inc. + +OUI:0008CF* + ID_OUI_FROM_DATABASE=Nippon Koei Power Systems Co., Ltd. + +OUI:0008C0* + ID_OUI_FROM_DATABASE=ASA SYSTEMS + +OUI:0008C5* + ID_OUI_FROM_DATABASE=Liontech Co., Ltd. + +OUI:0008C9* + ID_OUI_FROM_DATABASE=TechniSat Digital GmbH + +OUI:0008CA* + ID_OUI_FROM_DATABASE=TwinHan Technology Co.,Ltd + +OUI:0008BF* + ID_OUI_FROM_DATABASE=Aptus Elektronik AB + +OUI:0008B3* + ID_OUI_FROM_DATABASE=Fastwel + +OUI:0008B2* + ID_OUI_FROM_DATABASE=SHENZHEN COMPASS TECHNOLOGY DEVELOPMENT CO.,LTD + +OUI:0008A6* + ID_OUI_FROM_DATABASE=Multiware & Image Co., Ltd. + +OUI:0008AD* + ID_OUI_FROM_DATABASE=Toyo-Linx Co., Ltd. + +OUI:00089A* + ID_OUI_FROM_DATABASE=Alcatel Microelectronics + +OUI:0008A0* + ID_OUI_FROM_DATABASE=Stotz Feinmesstechnik GmbH + +OUI:000892* + ID_OUI_FROM_DATABASE=EM Solutions + +OUI:000896* + ID_OUI_FROM_DATABASE=Printronix, Inc. + +OUI:00088C* + ID_OUI_FROM_DATABASE=Quanta Network Systems Inc. + +OUI:000886* + ID_OUI_FROM_DATABASE=Hansung Teliann, Inc. + +OUI:000873* + ID_OUI_FROM_DATABASE=DapTechnology B.V. + +OUI:00087A* + ID_OUI_FROM_DATABASE=Wipotec GmbH + +OUI:00087F* + ID_OUI_FROM_DATABASE=SPAUN electronic GmbH & Co. KG + +OUI:02608C* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:0007D0* + ID_OUI_FROM_DATABASE=Automat Engenharia de Automação Ltda. + +OUI:0007CD* + ID_OUI_FROM_DATABASE=Kumoh Electronic Co, Ltd + +OUI:0007C7* + ID_OUI_FROM_DATABASE=Synectics Systems Limited + +OUI:00047D* + ID_OUI_FROM_DATABASE=Pelco + +OUI:00047E* + ID_OUI_FROM_DATABASE=Siqura B.V. + +OUI:0007C1* + ID_OUI_FROM_DATABASE=Overture Networks, Inc. + +OUI:0007C0* + ID_OUI_FROM_DATABASE=NetZerver Inc. + +OUI:0007AE* + ID_OUI_FROM_DATABASE=Britestream Networks, Inc. + +OUI:0007B4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00079A* + ID_OUI_FROM_DATABASE=Verint Systems Inc + +OUI:0007A0* + ID_OUI_FROM_DATABASE=e-Watch Inc. + +OUI:000794* + ID_OUI_FROM_DATABASE=Simple Devices, Inc. + +OUI:000793* + ID_OUI_FROM_DATABASE=Shin Satellite Public Company Limited + +OUI:00078D* + ID_OUI_FROM_DATABASE=NetEngines Ltd. + +OUI:00078E* + ID_OUI_FROM_DATABASE=Garz & Friche GmbH + +OUI:000781* + ID_OUI_FROM_DATABASE=Itron Inc. + +OUI:000787* + ID_OUI_FROM_DATABASE=Idea System Co., Ltd. + +OUI:000777* + ID_OUI_FROM_DATABASE=Motah Ltd. + +OUI:000771* + ID_OUI_FROM_DATABASE=Embedded System Corporation + +OUI:00075B* + ID_OUI_FROM_DATABASE=Gibson Guitars + +OUI:000760* + ID_OUI_FROM_DATABASE=TOMIS Information & Telecom Corp. + +OUI:000767* + ID_OUI_FROM_DATABASE=Yuxing Electronics Company Limited + +OUI:000879* + ID_OUI_FROM_DATABASE=CEM Corporation + +OUI:00086C* + ID_OUI_FROM_DATABASE=Plasmon LMS + +OUI:00086D* + ID_OUI_FROM_DATABASE=Missouri FreeNet + +OUI:000867* + ID_OUI_FROM_DATABASE=Uptime Devices + +OUI:000860* + ID_OUI_FROM_DATABASE=LodgeNet Entertainment Corp. + +OUI:000854* + ID_OUI_FROM_DATABASE=Netronix, Inc. + +OUI:00085A* + ID_OUI_FROM_DATABASE=IntiGate Inc. + +OUI:00081E* + ID_OUI_FROM_DATABASE=Repeatit AB + +OUI:00082B* + ID_OUI_FROM_DATABASE=Wooksung Electronics, Inc. + +OUI:000824* + ID_OUI_FROM_DATABASE=Nuance Document Imaging + +OUI:0005BA* + ID_OUI_FROM_DATABASE=Area Netwoeks, Inc. + +OUI:0005B9* + ID_OUI_FROM_DATABASE=Airvana, Inc. + +OUI:0005C0* + ID_OUI_FROM_DATABASE=Digital Network Alacarte Co., Ltd. + +OUI:000599* + ID_OUI_FROM_DATABASE=DRS Test and Energy Management or DRS-TEM + +OUI:0005A0* + ID_OUI_FROM_DATABASE=MOBILINE Kft. + +OUI:0005A9* + ID_OUI_FROM_DATABASE=Princeton Networks, Inc. + +OUI:0005AA* + ID_OUI_FROM_DATABASE=Moore Industries International Inc. + +OUI:0005AF* + ID_OUI_FROM_DATABASE=InnoScan Computing A/S + +OUI:0005B3* + ID_OUI_FROM_DATABASE=Asahi-Engineering Co., Ltd. + +OUI:00059F* + ID_OUI_FROM_DATABASE=Yotta Networks, Inc. + +OUI:0005A6* + ID_OUI_FROM_DATABASE=Extron Electronics + +OUI:0005B4* + ID_OUI_FROM_DATABASE=Aceex Corporation + +OUI:00058D* + ID_OUI_FROM_DATABASE=Lynx Photonic Networks, Inc. + +OUI:000587* + ID_OUI_FROM_DATABASE=Locus, Incorporated + +OUI:000593* + ID_OUI_FROM_DATABASE=Grammar Engine Inc. + +OUI:000586* + ID_OUI_FROM_DATABASE=Lucent Technologies + +OUI:00057A* + ID_OUI_FROM_DATABASE=Overture Networks + +OUI:00063C* + ID_OUI_FROM_DATABASE=Intrinsyc Software International Inc. + +OUI:00062F* + ID_OUI_FROM_DATABASE=Pivotech Systems Inc. + +OUI:000636* + ID_OUI_FROM_DATABASE=Jedai Broadband Networks + +OUI:000635* + ID_OUI_FROM_DATABASE=PacketAir Networks, Inc. + +OUI:000628* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00061F* + ID_OUI_FROM_DATABASE=Vision Components GmbH + +OUI:000619* + ID_OUI_FROM_DATABASE=Connection Technology Systems + +OUI:00060D* + ID_OUI_FROM_DATABASE=Wave7 Optics + +OUI:000613* + ID_OUI_FROM_DATABASE=Kawasaki Microelectronics Incorporated + +OUI:00060E* + ID_OUI_FROM_DATABASE=IGYS Systems, Inc. + +OUI:0005EC* + ID_OUI_FROM_DATABASE=Mosaic Systems Inc. + +OUI:0005D3* + ID_OUI_FROM_DATABASE=eProduction Solutions, Inc. + +OUI:000608* + ID_OUI_FROM_DATABASE=At-Sky SAS + +OUI:000607* + ID_OUI_FROM_DATABASE=Omni Directional Control Technology Inc. + +OUI:0005E6* + ID_OUI_FROM_DATABASE=Egenera, Inc. + +OUI:000580* + ID_OUI_FROM_DATABASE=FibroLAN Ltd. + +OUI:000576* + ID_OUI_FROM_DATABASE=NSM Technology Ltd. + +OUI:000570* + ID_OUI_FROM_DATABASE=Baydel Ltd. + +OUI:00056A* + ID_OUI_FROM_DATABASE=Heuft Systemtechnik GmbH + +OUI:000563* + ID_OUI_FROM_DATABASE=J-Works, Inc. + +OUI:00055D* + ID_OUI_FROM_DATABASE=D-LINK SYSTEMS, INC. + +OUI:000564* + ID_OUI_FROM_DATABASE=Tsinghua Bitway Co., Ltd. + +OUI:000557* + ID_OUI_FROM_DATABASE=Agile TV Corporation + +OUI:000551* + ID_OUI_FROM_DATABASE=F & S Elektronik Systeme GmbH + +OUI:00054B* + ID_OUI_FROM_DATABASE=Eaton Automation AG + +OUI:00054A* + ID_OUI_FROM_DATABASE=Ario Data Networks, Inc. + +OUI:000544* + ID_OUI_FROM_DATABASE=Valley Technologies, Inc. + +OUI:00053E* + ID_OUI_FROM_DATABASE=KID Systeme GmbH + +OUI:000531* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000538* + ID_OUI_FROM_DATABASE=Merilus, Inc. + +OUI:000532* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000525* + ID_OUI_FROM_DATABASE=Puretek Industrial Co., Ltd. + +OUI:00052B* + ID_OUI_FROM_DATABASE=HORIBA, Ltd. + +OUI:00051F* + ID_OUI_FROM_DATABASE=Taijin Media Co., Ltd. + +OUI:000519* + ID_OUI_FROM_DATABASE=Siemens Building Technologies AG, + +OUI:000518* + ID_OUI_FROM_DATABASE=Jupiters Technology + +OUI:00050E* + ID_OUI_FROM_DATABASE=3ware, Inc. + +OUI:00050F* + ID_OUI_FROM_DATABASE=Tanaka S/S Ltd. + +OUI:000508* + ID_OUI_FROM_DATABASE=Inetcam, Inc. + +OUI:0004FE* + ID_OUI_FROM_DATABASE=Pelago Networks + +OUI:000671* + ID_OUI_FROM_DATABASE=Softing AG + +OUI:000672* + ID_OUI_FROM_DATABASE=Netezza + +OUI:00067B* + ID_OUI_FROM_DATABASE=Toplink C&C Corporation + +OUI:000665* + ID_OUI_FROM_DATABASE=Sunny Giken, Inc. + +OUI:00066B* + ID_OUI_FROM_DATABASE=Sysmex Corporation + +OUI:000652* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000659* + ID_OUI_FROM_DATABASE=EAL (Apeldoorn) B.V. + +OUI:000658* + ID_OUI_FROM_DATABASE=Helmut Fischer GmbH Institut für Elektronik und Messtechnik + +OUI:000646* + ID_OUI_FROM_DATABASE=ShenZhen XunBao Network Technology Co Ltd + +OUI:000640* + ID_OUI_FROM_DATABASE=White Rock Networks + +OUI:00064C* + ID_OUI_FROM_DATABASE=Invicta Networks, Inc. + +OUI:0006B5* + ID_OUI_FROM_DATABASE=Source Photonics, Inc. + +OUI:0006A8* + ID_OUI_FROM_DATABASE=KC Technology, Inc. + +OUI:00069E* + ID_OUI_FROM_DATABASE=UNIQA, Inc. + +OUI:000698* + ID_OUI_FROM_DATABASE=egnite GmbH + +OUI:000692* + ID_OUI_FROM_DATABASE=Intruvert Networks, Inc. + +OUI:00068C* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:000685* + ID_OUI_FROM_DATABASE=NetNearU Corporation + +OUI:00068B* + ID_OUI_FROM_DATABASE=AirRunner Technologies, Inc. + +OUI:000686* + ID_OUI_FROM_DATABASE=ZARDCOM Co., Ltd. + +OUI:00067F* + ID_OUI_FROM_DATABASE=Digeo, Inc. + +OUI:0006DE* + ID_OUI_FROM_DATABASE=Flash Technology + +OUI:0006E4* + ID_OUI_FROM_DATABASE=Citel Technologies Ltd. + +OUI:0006D1* + ID_OUI_FROM_DATABASE=Tahoe Networks, Inc. + +OUI:0006DA* + ID_OUI_FROM_DATABASE=ITRAN Communications Ltd. + +OUI:0006CB* + ID_OUI_FROM_DATABASE=Jotron Electronics A/S + +OUI:0006CC* + ID_OUI_FROM_DATABASE=JMI Electronics Co., Ltd. + +OUI:0006BB* + ID_OUI_FROM_DATABASE=ATI Technologies Inc. + +OUI:0006C5* + ID_OUI_FROM_DATABASE=INNOVI Technologies Limited + +OUI:0006AF* + ID_OUI_FROM_DATABASE=Xalted Networks + +OUI:000719* + ID_OUI_FROM_DATABASE=Mobiis Co., Ltd. + +OUI:000720* + ID_OUI_FROM_DATABASE=Trutzschler GmbH & Co. KG + +OUI:000713* + ID_OUI_FROM_DATABASE=IP One, Inc. + +OUI:00070D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000714* + ID_OUI_FROM_DATABASE=Brightcom + +OUI:0006F1* + ID_OUI_FROM_DATABASE=Optillion + +OUI:0006F0* + ID_OUI_FROM_DATABASE=Digeo, Inc. + +OUI:0006FB* + ID_OUI_FROM_DATABASE=Hitachi Printing Solutions, Ltd. + +OUI:0006EB* + ID_OUI_FROM_DATABASE=Global Data + +OUI:0005F2* + ID_OUI_FROM_DATABASE=Power R, Inc. + +OUI:0005FE* + ID_OUI_FROM_DATABASE=Traficon N.V. + +OUI:0005E5* + ID_OUI_FROM_DATABASE=Renishaw PLC + +OUI:0005F8* + ID_OUI_FROM_DATABASE=Real Time Access, Inc. + +OUI:0005FF* + ID_OUI_FROM_DATABASE=SNS Solutions, Inc. + +OUI:0005DD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0005D9* + ID_OUI_FROM_DATABASE=Techno Valley, Inc. + +OUI:0005C6* + ID_OUI_FROM_DATABASE=Triz Communications + +OUI:0005CC* + ID_OUI_FROM_DATABASE=Sumtel Communications, Inc. + +OUI:00044C* + ID_OUI_FROM_DATABASE=JENOPTIK + +OUI:000448* + ID_OUI_FROM_DATABASE=Polaroid Corporation + +OUI:00043C* + ID_OUI_FROM_DATABASE=SONOS Co., Ltd. + +OUI:000441* + ID_OUI_FROM_DATABASE=Half Dome Systems, Inc. + +OUI:00042F* + ID_OUI_FROM_DATABASE=International Communications Products, Inc. + +OUI:000429* + ID_OUI_FROM_DATABASE=Pixord Corporation + +OUI:000422* + ID_OUI_FROM_DATABASE=Gordon Kapes, Inc. + +OUI:00041C* + ID_OUI_FROM_DATABASE=ipDialog, Inc. + +OUI:00041D* + ID_OUI_FROM_DATABASE=Corega of America + +OUI:000416* + ID_OUI_FROM_DATABASE=Parks S/A Comunicacoes Digitais + +OUI:000410* + ID_OUI_FROM_DATABASE=Spinnaker Networks, Inc. + +OUI:00040F* + ID_OUI_FROM_DATABASE=Asus Network Technologies, Inc. + +OUI:00040A* + ID_OUI_FROM_DATABASE=Sage Systems + +OUI:000403* + ID_OUI_FROM_DATABASE=Nexsi Corporation + +OUI:0004F8* + ID_OUI_FROM_DATABASE=QUALICABLE TV Industria E Com., Ltda + +OUI:0004F2* + ID_OUI_FROM_DATABASE=Polycom + +OUI:0004EB* + ID_OUI_FROM_DATABASE=Paxonet Communications, Inc. + +OUI:0004EC* + ID_OUI_FROM_DATABASE=Memobox SA + +OUI:0004E6* + ID_OUI_FROM_DATABASE=Banyan Network Private Limited + +OUI:0004E1* + ID_OUI_FROM_DATABASE=Infinior Microsystems + +OUI:0004DB* + ID_OUI_FROM_DATABASE=Tellus Group Corp. + +OUI:0004E2* + ID_OUI_FROM_DATABASE=SMC Networks, Inc. + +OUI:0004D5* + ID_OUI_FROM_DATABASE=Hitachi Information & Communication Engineering, Ltd. + +OUI:0004CF* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:0004C9* + ID_OUI_FROM_DATABASE=Micro Electron Co., Ltd. + +OUI:000487* + ID_OUI_FROM_DATABASE=Cogency Semiconductor, Inc. + +OUI:000482* + ID_OUI_FROM_DATABASE=Medialogic Corp. + +OUI:000478* + ID_OUI_FROM_DATABASE=G. Star Technology Corporation + +OUI:000471* + ID_OUI_FROM_DATABASE=IPrad + +OUI:00046B* + ID_OUI_FROM_DATABASE=Palm Wireless, Inc. + +OUI:000465* + ID_OUI_FROM_DATABASE=i.s.t isdn-support technik GmbH + +OUI:000459* + ID_OUI_FROM_DATABASE=Veristar Corporation + +OUI:00045E* + ID_OUI_FROM_DATABASE=PolyTrax Information Technology AG + +OUI:000458* + ID_OUI_FROM_DATABASE=Fusion X Co., Ltd. + +OUI:000452* + ID_OUI_FROM_DATABASE=RocketLogix, Inc. + +OUI:000442* + ID_OUI_FROM_DATABASE=NACT + +OUI:0003F9* + ID_OUI_FROM_DATABASE=Pleiades Communications, Inc. + +OUI:0003E2* + ID_OUI_FROM_DATABASE=Comspace Corporation + +OUI:0003F4* + ID_OUI_FROM_DATABASE=NetBurner + +OUI:0003F3* + ID_OUI_FROM_DATABASE=Dazzle Multimedia, Inc. + +OUI:0003ED* + ID_OUI_FROM_DATABASE=Shinkawa Electric Co., Ltd. + +OUI:0003E7* + ID_OUI_FROM_DATABASE=Logostek Co. Ltd. + +OUI:0003DF* + ID_OUI_FROM_DATABASE=Desana Systems + +OUI:0003DB* + ID_OUI_FROM_DATABASE=Apogee Electronics Corp. + +OUI:0003D6* + ID_OUI_FROM_DATABASE=RADVision, Ltd. + +OUI:0003CF* + ID_OUI_FROM_DATABASE=Muxcom, Inc. + +OUI:0003C8* + ID_OUI_FROM_DATABASE=CML Emergency Services + +OUI:0003C3* + ID_OUI_FROM_DATABASE=Micronik Multimedia + +OUI:0003C0* + ID_OUI_FROM_DATABASE=RFTNC Co., Ltd. + +OUI:0003BC* + ID_OUI_FROM_DATABASE=COT GmbH + +OUI:0003B1* + ID_OUI_FROM_DATABASE=Hospira Inc. + +OUI:0003A5* + ID_OUI_FROM_DATABASE=Medea Corporation + +OUI:0003AA* + ID_OUI_FROM_DATABASE=Watlow + +OUI:0003A2* + ID_OUI_FROM_DATABASE=Catapult Communications + +OUI:000397* + ID_OUI_FROM_DATABASE=Watchfront Limited + +OUI:00039E* + ID_OUI_FROM_DATABASE=Tera System Co., Ltd. + +OUI:000392* + ID_OUI_FROM_DATABASE=Hyundai Teletek Co., Ltd. + +OUI:00038F* + ID_OUI_FROM_DATABASE=Weinschel Corporation + +OUI:00038B* + ID_OUI_FROM_DATABASE=PLUS-ONE I&T, Inc. + +OUI:000386* + ID_OUI_FROM_DATABASE=Ho Net, Inc. + +OUI:00037D* + ID_OUI_FROM_DATABASE=Stellcom + +OUI:000382* + ID_OUI_FROM_DATABASE=A-One Co., Ltd. + +OUI:00037A* + ID_OUI_FROM_DATABASE=Taiyo Yuden Co., Ltd. + +OUI:000376* + ID_OUI_FROM_DATABASE=Graphtec Technology, Inc. + +OUI:000369* + ID_OUI_FROM_DATABASE=Nippon Antenna Co., Ltd. + +OUI:00036F* + ID_OUI_FROM_DATABASE=Telsey SPA + +OUI:000363* + ID_OUI_FROM_DATABASE=Miraesys Co., Ltd. + +OUI:00035E* + ID_OUI_FROM_DATABASE=Metropolitan Area Networks, Inc. + +OUI:000357* + ID_OUI_FROM_DATABASE=Intervoice-Brite, Inc. + +OUI:00034C* + ID_OUI_FROM_DATABASE=Shanghai DigiVision Technology Co., Ltd. + +OUI:000351* + ID_OUI_FROM_DATABASE=Diebold, Inc. + +OUI:000311* + ID_OUI_FROM_DATABASE=Micro Technology Co., Ltd. + +OUI:00030A* + ID_OUI_FROM_DATABASE=Argus Technologies + +OUI:000302* + ID_OUI_FROM_DATABASE=Charles Industries, Ltd. + +OUI:000305* + ID_OUI_FROM_DATABASE=MSC Vertriebs GmbH + +OUI:0002FE* + ID_OUI_FROM_DATABASE=Viditec, Inc. + +OUI:0002F2* + ID_OUI_FROM_DATABASE=eDevice, Inc. + +OUI:0002F7* + ID_OUI_FROM_DATABASE=ARM + +OUI:0002EC* + ID_OUI_FROM_DATABASE=Maschoff Design Engineering + +OUI:0002E4* + ID_OUI_FROM_DATABASE=JC HYUN Systems, Inc. + +OUI:0002E7* + ID_OUI_FROM_DATABASE=CAB GmbH & Co KG + +OUI:0002E0* + ID_OUI_FROM_DATABASE=ETAS GmbH + +OUI:0002D9* + ID_OUI_FROM_DATABASE=Reliable Controls + +OUI:0002D4* + ID_OUI_FROM_DATABASE=PDA Peripherals, Inc. + +OUI:0002D1* + ID_OUI_FROM_DATABASE=Vivotek, Inc. + +OUI:0002CD* + ID_OUI_FROM_DATABASE=TeleDream, Inc. + +OUI:000349* + ID_OUI_FROM_DATABASE=Vidicode Datacommunicatie B.V. + +OUI:000340* + ID_OUI_FROM_DATABASE=Floware Wireless Systems, Ltd. + +OUI:008037* + ID_OUI_FROM_DATABASE=Ericsson Group + +OUI:000332* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000339* + ID_OUI_FROM_DATABASE=Eurologic Systems, Ltd. + +OUI:00032A* + ID_OUI_FROM_DATABASE=UniData Communication Systems, Inc. + +OUI:00032D* + ID_OUI_FROM_DATABASE=IBASE Technology, Inc. + +OUI:000326* + ID_OUI_FROM_DATABASE=Iwasaki Information Systems Co., Ltd. + +OUI:00031D* + ID_OUI_FROM_DATABASE=Taiwan Commate Computer, Inc. + +OUI:000318* + ID_OUI_FROM_DATABASE=Cyras Systems, Inc. + +OUI:0004C2* + ID_OUI_FROM_DATABASE=Magnipix, Inc. + +OUI:0004B6* + ID_OUI_FROM_DATABASE=Stratex Networks, Inc. + +OUI:0004BC* + ID_OUI_FROM_DATABASE=Giantec, Inc. + +OUI:0004B0* + ID_OUI_FROM_DATABASE=ELESIGN Co., Ltd. + +OUI:0004A9* + ID_OUI_FROM_DATABASE=SandStream Technologies, Inc. + +OUI:0004A8* + ID_OUI_FROM_DATABASE=Broadmax Technologies, Inc. + +OUI:0004A2* + ID_OUI_FROM_DATABASE=L.S.I. Japan Co., Ltd. + +OUI:00049B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00049C* + ID_OUI_FROM_DATABASE=Surgient Networks, Inc. + +OUI:000496* + ID_OUI_FROM_DATABASE=Extreme Networks + +OUI:00048F* + ID_OUI_FROM_DATABASE=TD Systems Corporation + +OUI:000488* + ID_OUI_FROM_DATABASE=Eurotherm Controls + +OUI:000281* + ID_OUI_FROM_DATABASE=Madge Ltd. + +OUI:009064* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:00027F* + ID_OUI_FROM_DATABASE=ask-technologies.com + +OUI:00027A* + ID_OUI_FROM_DATABASE=IOI Technology Corporation + +OUI:000273* + ID_OUI_FROM_DATABASE=Coriolis Networks + +OUI:00026E* + ID_OUI_FROM_DATABASE=NeGeN Access, Inc. + +OUI:000263* + ID_OUI_FROM_DATABASE=UPS Manufacturing SRL + +OUI:00025C* + ID_OUI_FROM_DATABASE=SCI Systems (Kunshan) Co., Ltd. + +OUI:000253* + ID_OUI_FROM_DATABASE=Televideo, Inc. + +OUI:00024C* + ID_OUI_FROM_DATABASE=SiByte, Inc. + +OUI:00024E* + ID_OUI_FROM_DATABASE=Datacard Group + +OUI:00012F* + ID_OUI_FROM_DATABASE=Twinhead International Corp + +OUI:00023C* + ID_OUI_FROM_DATABASE=Creative Technology, Ltd. + +OUI:000240* + ID_OUI_FROM_DATABASE=Seedek Co., Ltd. + +OUI:000247* + ID_OUI_FROM_DATABASE=Great Dragon Information Technology (Group) Co., Ltd. + +OUI:000243* + ID_OUI_FROM_DATABASE=Raysis Co., Ltd. + +OUI:000239* + ID_OUI_FROM_DATABASE=Visicom + +OUI:000236* + ID_OUI_FROM_DATABASE=INIT GmbH + +OUI:000231* + ID_OUI_FROM_DATABASE=Ingersoll-Rand + +OUI:00022A* + ID_OUI_FROM_DATABASE=Asound Electronic + +OUI:00022D* + ID_OUI_FROM_DATABASE=Agere Systems + +OUI:000219* + ID_OUI_FROM_DATABASE=Paralon Technologies + +OUI:000186* + ID_OUI_FROM_DATABASE=Uwe Disch + +OUI:00017B* + ID_OUI_FROM_DATABASE=Heidelberger Druckmaschinen AG + +OUI:000182* + ID_OUI_FROM_DATABASE=DICA TECHNOLOGIES AG + +OUI:00018E* + ID_OUI_FROM_DATABASE=Logitec Corporation + +OUI:00019B* + ID_OUI_FROM_DATABASE=Kyoto Microcomputer Co., Ltd. + +OUI:000194* + ID_OUI_FROM_DATABASE=Capital Equipment Corporation + +OUI:000197* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0001A3* + ID_OUI_FROM_DATABASE=GENESYS LOGIC, INC. + +OUI:00014E* + ID_OUI_FROM_DATABASE=WIN Enterprises, Inc. + +OUI:0030AC* + ID_OUI_FROM_DATABASE=Systeme Lauer GmbH & Co., Ltd. + +OUI:00013E* + ID_OUI_FROM_DATABASE=Ascom Tateco AB + +OUI:000145* + ID_OUI_FROM_DATABASE=WINSYSTEMS, INC. + +OUI:000126* + ID_OUI_FROM_DATABASE=PAC Labs + +OUI:00011A* + ID_OUI_FROM_DATABASE=Hoffmann und Burmeister GbR + +OUI:00011D* + ID_OUI_FROM_DATABASE=Centillium Communications + +OUI:000129* + ID_OUI_FROM_DATABASE=DFI Inc. + +OUI:000107* + ID_OUI_FROM_DATABASE=Leiser GmbH + +OUI:00010E* + ID_OUI_FROM_DATABASE=Bri-Link Technologies Co., Ltd + +OUI:000116* + ID_OUI_FROM_DATABASE=Netspect Technologies, Inc. + +OUI:000103* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:00062B* + ID_OUI_FROM_DATABASE=INTRASERVER TECHNOLOGY + +OUI:0002C1* + ID_OUI_FROM_DATABASE=Innovative Electronic Designs, Inc. + +OUI:0002C8* + ID_OUI_FROM_DATABASE=Technocom Communications Technology (pte) Ltd + +OUI:0002A9* + ID_OUI_FROM_DATABASE=RACOM, s.r.o. + +OUI:0002B8* + ID_OUI_FROM_DATABASE=WHI KONSULT AB + +OUI:0002AC* + ID_OUI_FROM_DATABASE=3PAR data + +OUI:0002B1* + ID_OUI_FROM_DATABASE=Anritsu, Ltd. + +OUI:00029A* + ID_OUI_FROM_DATABASE=Storage Apps + +OUI:0002A0* + ID_OUI_FROM_DATABASE=Flatstack Ltd. + +OUI:000295* + ID_OUI_FROM_DATABASE=IP.Access Limited + +OUI:000294* + ID_OUI_FROM_DATABASE=Tokyo Sokushin Co., Ltd. + +OUI:000290* + ID_OUI_FROM_DATABASE=Woorigisool, Inc. + +OUI:000286* + ID_OUI_FROM_DATABASE=Occam Networks + +OUI:00028B* + ID_OUI_FROM_DATABASE=VDSL Systems OY + +OUI:000222* + ID_OUI_FROM_DATABASE=Chromisys, Inc. + +OUI:00021D* + ID_OUI_FROM_DATABASE=Data General Communication Ltd. + +OUI:00020A* + ID_OUI_FROM_DATABASE=Gefran Spa + +OUI:000216* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000206* + ID_OUI_FROM_DATABASE=Telital R&D Denmark A/S + +OUI:000203* + ID_OUI_FROM_DATABASE=Woonsang Telecom, Inc. + +OUI:0001F7* + ID_OUI_FROM_DATABASE=Image Display Systems, Inc. + +OUI:0001EE* + ID_OUI_FROM_DATABASE=Comtrol Europe, Ltd. + +OUI:0001E2* + ID_OUI_FROM_DATABASE=Ando Electric Corporation + +OUI:0001F1* + ID_OUI_FROM_DATABASE=Innovative Concepts, Inc. + +OUI:00B06D* + ID_OUI_FROM_DATABASE=Jones Futurex Inc. + +OUI:0030FE* + ID_OUI_FROM_DATABASE=DSA GmbH + +OUI:00305E* + ID_OUI_FROM_DATABASE=Abelko Innovation + +OUI:00301E* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:00304D* + ID_OUI_FROM_DATABASE=ESI + +OUI:003046* + ID_OUI_FROM_DATABASE=Controlled Electronic Manageme + +OUI:00307B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0001D6* + ID_OUI_FROM_DATABASE=manroland AG + +OUI:0001DB* + ID_OUI_FROM_DATABASE=Freecom Technologies GmbH + +OUI:0001DE* + ID_OUI_FROM_DATABASE=Trango Systems, Inc. + +OUI:0001CF* + ID_OUI_FROM_DATABASE=Alpha Data Parallel Systems, Ltd. + +OUI:0001CB* + ID_OUI_FROM_DATABASE=EVR + +OUI:0001C4* + ID_OUI_FROM_DATABASE=NeoWave, Inc. + +OUI:0001C0* + ID_OUI_FROM_DATABASE=CompuLab, Ltd. + +OUI:0001B9* + ID_OUI_FROM_DATABASE=SKF Condition Monitoring + +OUI:0001B5* + ID_OUI_FROM_DATABASE=Turin Networks, Inc. + +OUI:00017F* + ID_OUI_FROM_DATABASE=Experience Music Project + +OUI:00016C* + ID_OUI_FROM_DATABASE=FOXCONN + +OUI:000173* + ID_OUI_FROM_DATABASE=AMCC + +OUI:00015C* + ID_OUI_FROM_DATABASE=CADANT INC. + +OUI:000163* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00010A* + ID_OUI_FROM_DATABASE=CIS TECHNOLOGY INC. + +OUI:00016F* + ID_OUI_FROM_DATABASE=Inkel Corp. + +OUI:000155* + ID_OUI_FROM_DATABASE=Promise Technology, Inc. + +OUI:000151* + ID_OUI_FROM_DATABASE=Ensemble Communications + +OUI:000142* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000132* + ID_OUI_FROM_DATABASE=Dranetz - BMI + +OUI:00D07D* + ID_OUI_FROM_DATABASE=COSINE COMMUNICATIONS + +OUI:00D0CA* + ID_OUI_FROM_DATABASE=Intrinsyc Software International Inc. + +OUI:00D058* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D067* + ID_OUI_FROM_DATABASE=CAMPIO COMMUNICATIONS + +OUI:00D023* + ID_OUI_FROM_DATABASE=INFORTREND TECHNOLOGY, INC. + +OUI:00D02A* + ID_OUI_FROM_DATABASE=Voxent Systems Ltd. + +OUI:00D068* + ID_OUI_FROM_DATABASE=IWILL CORPORATION + +OUI:00D09D* + ID_OUI_FROM_DATABASE=VERIS INDUSTRIES + +OUI:00D09A* + ID_OUI_FROM_DATABASE=FILANET CORPORATION + +OUI:00D00A* + ID_OUI_FROM_DATABASE=LANACCESS TELECOM S.A. + +OUI:00D04A* + ID_OUI_FROM_DATABASE=PRESENCE TECHNOLOGY GMBH + +OUI:00D0C3* + ID_OUI_FROM_DATABASE=VIVID TECHNOLOGY PTE, LTD. + +OUI:00D0F8* + ID_OUI_FROM_DATABASE=FUJIAN STAR TERMINAL + +OUI:00D096* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:00D003* + ID_OUI_FROM_DATABASE=COMDA ENTERPRISES CORP. + +OUI:00D029* + ID_OUI_FROM_DATABASE=WAKEFERN FOOD CORPORATION + +OUI:00D0F5* + ID_OUI_FROM_DATABASE=ORANGE MICRO, INC. + +OUI:00D0F7* + ID_OUI_FROM_DATABASE=NEXT NETS CORPORATION + +OUI:00D078* + ID_OUI_FROM_DATABASE=Eltex of Sweden AB + +OUI:00D0AF* + ID_OUI_FROM_DATABASE=CUTLER-HAMMER, INC. + +OUI:00D026* + ID_OUI_FROM_DATABASE=HIRSCHMANN AUSTRIA GMBH + +OUI:00D010* + ID_OUI_FROM_DATABASE=CONVERGENT NETWORKS, INC. + +OUI:00D074* + ID_OUI_FROM_DATABASE=TAQUA SYSTEMS, INC. + +OUI:00D0D5* + ID_OUI_FROM_DATABASE=GRUNDIG AG + +OUI:00D034* + ID_OUI_FROM_DATABASE=ORMEC SYSTEMS CORP. + +OUI:00D08C* + ID_OUI_FROM_DATABASE=GENOA TECHNOLOGY, INC. + +OUI:00D059* + ID_OUI_FROM_DATABASE=AMBIT MICROSYSTEMS CORP. + +OUI:005020* + ID_OUI_FROM_DATABASE=MEDIASTAR CO., LTD. + +OUI:00503E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D02B* + ID_OUI_FROM_DATABASE=JETCELL, INC. + +OUI:005017* + ID_OUI_FROM_DATABASE=RSR S.R.L. + +OUI:00D0CC* + ID_OUI_FROM_DATABASE=TECHNOLOGIES LYRE INC. + +OUI:00506D* + ID_OUI_FROM_DATABASE=VIDEOJET SYSTEMS + +OUI:005077* + ID_OUI_FROM_DATABASE=PROLIFIC TECHNOLOGY, INC. + +OUI:0050D4* + ID_OUI_FROM_DATABASE=JOOHONG INFORMATION & + +OUI:00505E* + ID_OUI_FROM_DATABASE=DIGITEK MICROLOGIC S.A. + +OUI:0050E7* + ID_OUI_FROM_DATABASE=PARADISE INNOVATIONS (ASIA) + +OUI:0050B9* + ID_OUI_FROM_DATABASE=XITRON TECHNOLOGIES, INC. + +OUI:00D049* + ID_OUI_FROM_DATABASE=IMPRESSTEK CO., LTD. + +OUI:00D04D* + ID_OUI_FROM_DATABASE=DIV OF RESEARCH & STATISTICS + +OUI:00D035* + ID_OUI_FROM_DATABASE=BEHAVIOR TECH. COMPUTER CORP. + +OUI:00D02D* + ID_OUI_FROM_DATABASE=ADEMCO + +OUI:00D07C* + ID_OUI_FROM_DATABASE=KOYO ELECTRONICS INC. CO.,LTD. + +OUI:00D05B* + ID_OUI_FROM_DATABASE=ACROLOOP MOTION CONTROL + +OUI:00D0C6* + ID_OUI_FROM_DATABASE=THOMAS & BETTS CORP. + +OUI:00D02E* + ID_OUI_FROM_DATABASE=COMMUNICATION AUTOMATION CORP. + +OUI:00D0DA* + ID_OUI_FROM_DATABASE=TAICOM DATA SYSTEMS CO., LTD. + +OUI:00D0E8* + ID_OUI_FROM_DATABASE=MAC SYSTEM CO., LTD. + +OUI:00D03C* + ID_OUI_FROM_DATABASE=Vieo, Inc. + +OUI:00D09F* + ID_OUI_FROM_DATABASE=NOVTEK TEST SYSTEMS + +OUI:00D07E* + ID_OUI_FROM_DATABASE=KEYCORP LTD. + +OUI:00D0EA* + ID_OUI_FROM_DATABASE=NEXTONE COMMUNICATIONS, INC. + +OUI:00D020* + ID_OUI_FROM_DATABASE=AIM SYSTEM, INC. + +OUI:00D064* + ID_OUI_FROM_DATABASE=MULTITEL + +OUI:00D072* + ID_OUI_FROM_DATABASE=BROADLOGIC + +OUI:00309B* + ID_OUI_FROM_DATABASE=Smartware + +OUI:0030AF* + ID_OUI_FROM_DATABASE=Honeywell GmbH + +OUI:003074* + ID_OUI_FROM_DATABASE=EQUIINET LTD. + +OUI:003090* + ID_OUI_FROM_DATABASE=CYRA TECHNOLOGIES, INC. + +OUI:003030* + ID_OUI_FROM_DATABASE=HARMONIX CORPORATION + +OUI:00307C* + ID_OUI_FROM_DATABASE=ADID SA + +OUI:003063* + ID_OUI_FROM_DATABASE=SANTERA SYSTEMS, INC. + +OUI:00309F* + ID_OUI_FROM_DATABASE=AMBER NETWORKS + +OUI:0030A8* + ID_OUI_FROM_DATABASE=OL'E COMMUNICATIONS, INC. + +OUI:00304C* + ID_OUI_FROM_DATABASE=APPIAN COMMUNICATIONS, INC. + +OUI:0030EF* + ID_OUI_FROM_DATABASE=NEON TECHNOLOGY, INC. + +OUI:00306F* + ID_OUI_FROM_DATABASE=SEYEON TECH. CO., LTD. + +OUI:003031* + ID_OUI_FROM_DATABASE=LIGHTWAVE COMMUNICATIONS, INC. + +OUI:003035* + ID_OUI_FROM_DATABASE=Corning Incorporated + +OUI:00302B* + ID_OUI_FROM_DATABASE=INALP NETWORKS, INC. + +OUI:00305F* + ID_OUI_FROM_DATABASE=Hasselblad + +OUI:00302D* + ID_OUI_FROM_DATABASE=QUANTUM BRIDGE COMMUNICATIONS + +OUI:003025* + ID_OUI_FROM_DATABASE=CHECKOUT COMPUTER SYSTEMS, LTD + +OUI:00D01F* + ID_OUI_FROM_DATABASE=Senetas Security + +OUI:003012* + ID_OUI_FROM_DATABASE=DIGITAL ENGINEERING LTD. + +OUI:003077* + ID_OUI_FROM_DATABASE=ONPREM NETWORKS + +OUI:0030D4* + ID_OUI_FROM_DATABASE=AAE Systems, Inc. + +OUI:00D00F* + ID_OUI_FROM_DATABASE=SPEECH DESIGN GMBH + +OUI:00D0CF* + ID_OUI_FROM_DATABASE=MORETON BAY + +OUI:00D073* + ID_OUI_FROM_DATABASE=ACN ADVANCED COMMUNICATIONS + +OUI:00D030* + ID_OUI_FROM_DATABASE=Safetran Systems Corp + +OUI:00D057* + ID_OUI_FROM_DATABASE=ULTRAK, INC. + +OUI:00D03B* + ID_OUI_FROM_DATABASE=VISION PRODUCTS PTY. LTD. + +OUI:00D0BF* + ID_OUI_FROM_DATABASE=PIVOTAL TECHNOLOGIES + +OUI:00D050* + ID_OUI_FROM_DATABASE=ISKRATEL + +OUI:00D0CB* + ID_OUI_FROM_DATABASE=DASAN CO., LTD. + +OUI:00D0D3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D08E* + ID_OUI_FROM_DATABASE=Grass Valley, A Belden Brand + +OUI:00D0A3* + ID_OUI_FROM_DATABASE=VOCAL DATA, INC. + +OUI:00D0E0* + ID_OUI_FROM_DATABASE=DOOIN ELECTRONICS CO. + +OUI:003054* + ID_OUI_FROM_DATABASE=CASTLENET TECHNOLOGY, INC. + +OUI:003039* + ID_OUI_FROM_DATABASE=SOFTBOOK PRESS + +OUI:003017* + ID_OUI_FROM_DATABASE=BlueArc UK Ltd + +OUI:003076* + ID_OUI_FROM_DATABASE=Akamba Corporation + +OUI:00305D* + ID_OUI_FROM_DATABASE=DIGITRA SYSTEMS, INC. + +OUI:0030F7* + ID_OUI_FROM_DATABASE=RAMIX INC. + +OUI:003033* + ID_OUI_FROM_DATABASE=ORIENT TELECOM CO., LTD. + +OUI:003083* + ID_OUI_FROM_DATABASE=Ivron Systems + +OUI:003007* + ID_OUI_FROM_DATABASE=OPTI, INC. + +OUI:0030DD* + ID_OUI_FROM_DATABASE=INDIGITA CORPORATION + +OUI:0030F2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003020* + ID_OUI_FROM_DATABASE=TSI, Inc.. + +OUI:003089* + ID_OUI_FROM_DATABASE=Spectrapoint Wireless, LLC + +OUI:003022* + ID_OUI_FROM_DATABASE=Fong Kai Industrial Co., Ltd. + +OUI:0030F8* + ID_OUI_FROM_DATABASE=Dynapro Systems, Inc. + +OUI:0030C2* + ID_OUI_FROM_DATABASE=COMONE + +OUI:003056* + ID_OUI_FROM_DATABASE=Beck IPC GmbH + +OUI:0030D2* + ID_OUI_FROM_DATABASE=WIN TECHNOLOGIES, CO., LTD. + +OUI:003050* + ID_OUI_FROM_DATABASE=Versa Technology + +OUI:0030B8* + ID_OUI_FROM_DATABASE=RiverDelta Networks + +OUI:00904D* + ID_OUI_FROM_DATABASE=SPEC S.A. + +OUI:009079* + ID_OUI_FROM_DATABASE=ClearOne, Inc. + +OUI:00908F* + ID_OUI_FROM_DATABASE=AUDIO CODES LTD. + +OUI:0090D5* + ID_OUI_FROM_DATABASE=EUPHONIX, INC. + +OUI:0090A7* + ID_OUI_FROM_DATABASE=CLIENTEC CORPORATION + +OUI:00907F* + ID_OUI_FROM_DATABASE=WatchGuard Technologies, Inc. + +OUI:00907E* + ID_OUI_FROM_DATABASE=VETRONIX CORP. + +OUI:00902F* + ID_OUI_FROM_DATABASE=NETCORE SYSTEMS, INC. + +OUI:00900D* + ID_OUI_FROM_DATABASE=Overland Storage Inc. + +OUI:009044* + ID_OUI_FROM_DATABASE=ASSURED DIGITAL, INC. + +OUI:009078* + ID_OUI_FROM_DATABASE=MER TELEMANAGEMENT SOLUTIONS, LTD. + +OUI:009009* + ID_OUI_FROM_DATABASE=I Controls, Inc. + +OUI:009015* + ID_OUI_FROM_DATABASE=CENTIGRAM COMMUNICATIONS CORP. + +OUI:0090F3* + ID_OUI_FROM_DATABASE=ASPECT COMMUNICATIONS + +OUI:0090A8* + ID_OUI_FROM_DATABASE=NineTiles Networks, Ltd. + +OUI:00507A* + ID_OUI_FROM_DATABASE=XPEED, INC. + +OUI:005002* + ID_OUI_FROM_DATABASE=OMNISEC AG + +OUI:00508D* + ID_OUI_FROM_DATABASE=ABIT COMPUTER CORPORATION + +OUI:0050CD* + ID_OUI_FROM_DATABASE=DIGIANSWER A/S + +OUI:0050C5* + ID_OUI_FROM_DATABASE=ADS Technologies, Inc + +OUI:00502F* + ID_OUI_FROM_DATABASE=TollBridge Technologies, Inc. + +OUI:005028* + ID_OUI_FROM_DATABASE=AVAL COMMUNICATIONS + +OUI:00505B* + ID_OUI_FROM_DATABASE=KAWASAKI LSI U.S.A., INC. + +OUI:0050F8* + ID_OUI_FROM_DATABASE=ENTREGA TECHNOLOGIES, INC. + +OUI:00506F* + ID_OUI_FROM_DATABASE=G-CONNECT + +OUI:0050CC* + ID_OUI_FROM_DATABASE=XYRATEX + +OUI:0050D5* + ID_OUI_FROM_DATABASE=AD SYSTEMS CORP. + +OUI:0050AA* + ID_OUI_FROM_DATABASE=KONICA MINOLTA HOLDINGS, INC. + +OUI:00509C* + ID_OUI_FROM_DATABASE=BETA RESEARCH + +OUI:005027* + ID_OUI_FROM_DATABASE=GENICOM CORPORATION + +OUI:005010* + ID_OUI_FROM_DATABASE=NovaNET Learning, Inc. + +OUI:00509E* + ID_OUI_FROM_DATABASE=Les Technologies SoftAcoustik Inc. + +OUI:00505F* + ID_OUI_FROM_DATABASE=BRAND INNOVATORS + +OUI:005095* + ID_OUI_FROM_DATABASE=PERACOM NETWORKS + +OUI:005026* + ID_OUI_FROM_DATABASE=COSYSTEMS, INC. + +OUI:0050EF* + ID_OUI_FROM_DATABASE=SPE Systemhaus GmbH + +OUI:005093* + ID_OUI_FROM_DATABASE=BOEING + +OUI:0050D8* + ID_OUI_FROM_DATABASE=UNICORN COMPUTER CORP. + +OUI:009034* + ID_OUI_FROM_DATABASE=IMAGIC, INC. + +OUI:009073* + ID_OUI_FROM_DATABASE=GAIO TECHNOLOGY + +OUI:0090C9* + ID_OUI_FROM_DATABASE=DPAC Technologies + +OUI:0090E7* + ID_OUI_FROM_DATABASE=HORSCH ELEKTRONIK AG + +OUI:009001* + ID_OUI_FROM_DATABASE=NISHIMU ELECTRONICS INDUSTRIES CO., LTD. + +OUI:0090FB* + ID_OUI_FROM_DATABASE=PORTWELL, INC. + +OUI:009070* + ID_OUI_FROM_DATABASE=NEO NETWORKS, INC. + +OUI:0090EF* + ID_OUI_FROM_DATABASE=INTEGRIX, INC. + +OUI:0090B0* + ID_OUI_FROM_DATABASE=VADEM + +OUI:0090D1* + ID_OUI_FROM_DATABASE=LEICHU ENTERPRISE CO., LTD. + +OUI:0050D7* + ID_OUI_FROM_DATABASE=TELSTRAT + +OUI:0050F1* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:00501B* + ID_OUI_FROM_DATABASE=ABL CANADA, INC. + +OUI:005036* + ID_OUI_FROM_DATABASE=NETCAM, LTD. + +OUI:0050C9* + ID_OUI_FROM_DATABASE=MASPRO DENKOH CORP. + +OUI:005009* + ID_OUI_FROM_DATABASE=PHILIPS BROADBAND NETWORKS + +OUI:0050C4* + ID_OUI_FROM_DATABASE=IMD + +OUI:0050A3* + ID_OUI_FROM_DATABASE=TransMedia Communications, Inc. + +OUI:005099* + ID_OUI_FROM_DATABASE=3COM EUROPE, LTD. + +OUI:0050A4* + ID_OUI_FROM_DATABASE=IO TECH, INC. + +OUI:0050B3* + ID_OUI_FROM_DATABASE=VOICEBOARD CORPORATION + +OUI:0050B7* + ID_OUI_FROM_DATABASE=BOSER TECHNOLOGY CO., LTD. + +OUI:00908D* + ID_OUI_FROM_DATABASE=VICKERS ELECTRONICS SYSTEMS + +OUI:009042* + ID_OUI_FROM_DATABASE=ECCS, Inc. + +OUI:009051* + ID_OUI_FROM_DATABASE=ULTIMATE TECHNOLOGY CORP. + +OUI:0090FF* + ID_OUI_FROM_DATABASE=TELLUS TECHNOLOGY INC. + +OUI:009018* + ID_OUI_FROM_DATABASE=ITO ELECTRIC INDUSTRY CO, LTD. + +OUI:009002* + ID_OUI_FROM_DATABASE=ALLGON AB + +OUI:009016* + ID_OUI_FROM_DATABASE=ZAC + +OUI:009005* + ID_OUI_FROM_DATABASE=PROTECH SYSTEMS CO., LTD. + +OUI:00901E* + ID_OUI_FROM_DATABASE=Selesta Ingegneria S.p.A. + +OUI:009090* + ID_OUI_FROM_DATABASE=I-BUS + +OUI:0090AA* + ID_OUI_FROM_DATABASE=INDIGO ACTIVE VISION SYSTEMS LIMITED + +OUI:00903A* + ID_OUI_FROM_DATABASE=NIHON MEDIA TOOL INC. + +OUI:009055* + ID_OUI_FROM_DATABASE=PARKER HANNIFIN CORPORATION COMPUMOTOR DIVISION + +OUI:00909F* + ID_OUI_FROM_DATABASE=DIGI-DATA CORPORATION + +OUI:0090E4* + ID_OUI_FROM_DATABASE=NEC AMERICA, INC. + +OUI:009013* + ID_OUI_FROM_DATABASE=SAMSAN CORP. + +OUI:009004* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:0090E1* + ID_OUI_FROM_DATABASE=TELENA S.P.A. + +OUI:00504A* + ID_OUI_FROM_DATABASE=ELTECO A.S. + +OUI:00504C* + ID_OUI_FROM_DATABASE=Galil Motion Control + +OUI:005021* + ID_OUI_FROM_DATABASE=EIS INTERNATIONAL, INC. + +OUI:00506E* + ID_OUI_FROM_DATABASE=CORDER ENGINEERING CORPORATION + +OUI:00507E* + ID_OUI_FROM_DATABASE=NEWER TECHNOLOGY + +OUI:0050E6* + ID_OUI_FROM_DATABASE=HAKUSAN CORPORATION + +OUI:0050AE* + ID_OUI_FROM_DATABASE=FDK Co., Ltd + +OUI:00109D* + ID_OUI_FROM_DATABASE=CLARINET SYSTEMS, INC. + +OUI:0010D2* + ID_OUI_FROM_DATABASE=NITTO TSUSHINKI CO., LTD + +OUI:001045* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00106B* + ID_OUI_FROM_DATABASE=SONUS NETWORKS, INC. + +OUI:0010EC* + ID_OUI_FROM_DATABASE=RPCG, LLC + +OUI:001092* + ID_OUI_FROM_DATABASE=NETCORE INC. + +OUI:0010E2* + ID_OUI_FROM_DATABASE=ArrayComm, Inc. + +OUI:001071* + ID_OUI_FROM_DATABASE=ADVANET INC. + +OUI:001069* + ID_OUI_FROM_DATABASE=HELIOSS COMMUNICATIONS, INC. + +OUI:0010FD* + ID_OUI_FROM_DATABASE=COCOM A/S + +OUI:0010AC* + ID_OUI_FROM_DATABASE=IMCI TECHNOLOGIES + +OUI:0010EF* + ID_OUI_FROM_DATABASE=DBTEL INCORPORATED + +OUI:001017* + ID_OUI_FROM_DATABASE=Bosch Access Systems GmbH + +OUI:001024* + ID_OUI_FROM_DATABASE=NAGOYA ELECTRIC WORKS CO., LTD + +OUI:0010DD* + ID_OUI_FROM_DATABASE=ENABLE SEMICONDUCTOR, INC. + +OUI:0010C9* + ID_OUI_FROM_DATABASE=MITSUBISHI ELECTRONICS LOGISTIC SUPPORT CO. + +OUI:001085* + ID_OUI_FROM_DATABASE=POLARIS COMMUNICATIONS, INC. + +OUI:001044* + ID_OUI_FROM_DATABASE=InnoLabs Corporation + +OUI:001056* + ID_OUI_FROM_DATABASE=SODICK CO., LTD. + +OUI:001099* + ID_OUI_FROM_DATABASE=InnoMedia, Inc. + +OUI:001061* + ID_OUI_FROM_DATABASE=HOSTLINK CORP. + +OUI:001093* + ID_OUI_FROM_DATABASE=CMS COMPUTERS, LTD. + +OUI:0010CD* + ID_OUI_FROM_DATABASE=INTERFACE CONCEPT + +OUI:0010F3* + ID_OUI_FROM_DATABASE=Nexcom International Co., Ltd. + +OUI:001005* + ID_OUI_FROM_DATABASE=UEC COMMERCIAL + +OUI:001066* + ID_OUI_FROM_DATABASE=ADVANCED CONTROL SYSTEMS, INC. + +OUI:0010E4* + ID_OUI_FROM_DATABASE=NSI CORPORATION + +OUI:001062* + ID_OUI_FROM_DATABASE=NX SERVER, ILNC. + +OUI:0010B9* + ID_OUI_FROM_DATABASE=MAXTOR CORP. + +OUI:00108B* + ID_OUI_FROM_DATABASE=LASERANIMATION SOLLINGER GMBH + +OUI:00105C* + ID_OUI_FROM_DATABASE=QUANTUM DESIGNS (H.K.) LTD. + +OUI:001042* + ID_OUI_FROM_DATABASE=Alacritech, Inc. + +OUI:001060* + ID_OUI_FROM_DATABASE=BILLIONTON SYSTEMS, INC. + +OUI:0010DE* + ID_OUI_FROM_DATABASE=INTERNATIONAL DATACASTING CORPORATION + +OUI:00105D* + ID_OUI_FROM_DATABASE=Draeger Medical + +OUI:0010E1* + ID_OUI_FROM_DATABASE=S.I. TECH, INC. + +OUI:001091* + ID_OUI_FROM_DATABASE=NO WIRES NEEDED BV + +OUI:0010F5* + ID_OUI_FROM_DATABASE=AMHERST SYSTEMS, INC. + +OUI:001090* + ID_OUI_FROM_DATABASE=CIMETRICS, INC. + +OUI:001070* + ID_OUI_FROM_DATABASE=CARADON TREND LTD. + +OUI:0010BA* + ID_OUI_FROM_DATABASE=MARTINHO-DAVIS SYSTEMS, INC. + +OUI:00107C* + ID_OUI_FROM_DATABASE=P-COM, INC. + +OUI:0010AE* + ID_OUI_FROM_DATABASE=SHINKO ELECTRIC INDUSTRIES CO. + +OUI:001040* + ID_OUI_FROM_DATABASE=INTERMEC CORPORATION + +OUI:0010B0* + ID_OUI_FROM_DATABASE=MERIDIAN TECHNOLOGY CORP. + +OUI:001077* + ID_OUI_FROM_DATABASE=SAF DRIVE SYSTEMS, LTD. + +OUI:0010F4* + ID_OUI_FROM_DATABASE=Vertical Communications + +OUI:001065* + ID_OUI_FROM_DATABASE=RADYNE CORPORATION + +OUI:00104A* + ID_OUI_FROM_DATABASE=The Parvus Corporation + +OUI:0010B3* + ID_OUI_FROM_DATABASE=NOKIA MULTIMEDIA TERMINALS + +OUI:001037* + ID_OUI_FROM_DATABASE=CYQ've Technology Co., Ltd. + +OUI:001051* + ID_OUI_FROM_DATABASE=CMICRO CORPORATION + +OUI:0010DC* + ID_OUI_FROM_DATABASE=MICRO-STAR INTERNATIONAL CO., LTD. + +OUI:0010EE* + ID_OUI_FROM_DATABASE=CTI PRODUCTS, INC. + +OUI:00101B* + ID_OUI_FROM_DATABASE=CORNET TECHNOLOGY, INC. + +OUI:001032* + ID_OUI_FROM_DATABASE=ALTA TECHNOLOGY + +OUI:001025* + ID_OUI_FROM_DATABASE=Grayhill, Inc + +OUI:001009* + ID_OUI_FROM_DATABASE=HORO QUARTZ + +OUI:0010F8* + ID_OUI_FROM_DATABASE=TEXIO TECHNOLOGY CORPORATION + +OUI:00104D* + ID_OUI_FROM_DATABASE=SURTEC INDUSTRIES, INC. + +OUI:00E0E0* + ID_OUI_FROM_DATABASE=SI ELECTRONICS, LTD. + +OUI:00E0D1* + ID_OUI_FROM_DATABASE=TELSIS LIMITED + +OUI:00E005* + ID_OUI_FROM_DATABASE=TECHNICAL CORP. + +OUI:00E072* + ID_OUI_FROM_DATABASE=LYNK + +OUI:00E0C1* + ID_OUI_FROM_DATABASE=MEMOREX TELEX JAPAN, LTD. + +OUI:00E0AD* + ID_OUI_FROM_DATABASE=EES TECHNOLOGY, LTD. + +OUI:00E025* + ID_OUI_FROM_DATABASE=dit Co., Ltd. + +OUI:00E0B1* + ID_OUI_FROM_DATABASE=Alcatel-Lucent, Enterprise Business Group + +OUI:00E0E4* + ID_OUI_FROM_DATABASE=FANUC ROBOTICS NORTH AMERICA, Inc. + +OUI:00E031* + ID_OUI_FROM_DATABASE=HAGIWARA ELECTRIC CO., LTD. + +OUI:00E0A5* + ID_OUI_FROM_DATABASE=ComCore Semiconductor, Inc. + +OUI:00E044* + ID_OUI_FROM_DATABASE=LSICS CORPORATION + +OUI:00E05D* + ID_OUI_FROM_DATABASE=UNITEC CO., LTD. + +OUI:00E0B3* + ID_OUI_FROM_DATABASE=EtherWAN Systems, Inc. + +OUI:00E053* + ID_OUI_FROM_DATABASE=CELLPORT LABS, INC. + +OUI:00E07D* + ID_OUI_FROM_DATABASE=NETRONIX, INC. + +OUI:00E0ED* + ID_OUI_FROM_DATABASE=SILICOM, LTD. + +OUI:00E0B4* + ID_OUI_FROM_DATABASE=TECHNO SCOPE CO., LTD. + +OUI:00E0C6* + ID_OUI_FROM_DATABASE=LINK2IT, L.L.C. + +OUI:00E06D* + ID_OUI_FROM_DATABASE=COMPUWARE CORPORATION + +OUI:00E074* + ID_OUI_FROM_DATABASE=TIERNAN COMMUNICATIONS, INC. + +OUI:00E059* + ID_OUI_FROM_DATABASE=CONTROLLED ENVIRONMENTS, LTD. + +OUI:00E006* + ID_OUI_FROM_DATABASE=SILICON INTEGRATED SYS. CORP. + +OUI:00E0F8* + ID_OUI_FROM_DATABASE=DICNA CONTROL AB + +OUI:00E004* + ID_OUI_FROM_DATABASE=PMC-SIERRA, INC. + +OUI:00E0DE* + ID_OUI_FROM_DATABASE=DATAX NV + +OUI:00E078* + ID_OUI_FROM_DATABASE=BERKELEY NETWORKS + +OUI:00E041* + ID_OUI_FROM_DATABASE=CSPI + +OUI:00E0E2* + ID_OUI_FROM_DATABASE=INNOVA CORP. + +OUI:00E009* + ID_OUI_FROM_DATABASE=MARATHON TECHNOLOGIES CORP. + +OUI:00E02F* + ID_OUI_FROM_DATABASE=MCNS HOLDINGS, L.P. + +OUI:00E04C* + ID_OUI_FROM_DATABASE=REALTEK SEMICONDUCTOR CORP. + +OUI:00E047* + ID_OUI_FROM_DATABASE=InFocus Corporation + +OUI:00E092* + ID_OUI_FROM_DATABASE=ADMTEK INCORPORATED + +OUI:00E0FF* + ID_OUI_FROM_DATABASE=SECURITY DYNAMICS TECHNOLOGIES, Inc. + +OUI:08BBCC* + ID_OUI_FROM_DATABASE=AK-NORD EDV VERTRIEBSGES. mbH + +OUI:0060B2* + ID_OUI_FROM_DATABASE=PROCESS CONTROL CORP. + +OUI:006004* + ID_OUI_FROM_DATABASE=COMPUTADORES MODULARES SA + +OUI:006000* + ID_OUI_FROM_DATABASE=XYCOM INC. + +OUI:00A019* + ID_OUI_FROM_DATABASE=NEBULA CONSULTANTS, INC. + +OUI:00A0ED* + ID_OUI_FROM_DATABASE=Brooks Automation, Inc. + +OUI:00A0A9* + ID_OUI_FROM_DATABASE=NAVTEL COMMUNICATIONS INC. + +OUI:00A0E1* + ID_OUI_FROM_DATABASE=WESTPORT RESEARCH ASSOCIATES, INC. + +OUI:00A0D6* + ID_OUI_FROM_DATABASE=SBE, Inc. + +OUI:00A05E* + ID_OUI_FROM_DATABASE=MYRIAD LOGIC INC. + +OUI:00A078* + ID_OUI_FROM_DATABASE=Marconi Communications + +OUI:00A00B* + ID_OUI_FROM_DATABASE=COMPUTEX CO., LTD. + +OUI:00A09A* + ID_OUI_FROM_DATABASE=NIHON KOHDEN AMERICA + +OUI:00A095* + ID_OUI_FROM_DATABASE=ACACIA NETWORKS, INC. + +OUI:00A0F2* + ID_OUI_FROM_DATABASE=INFOTEK COMMUNICATIONS, INC. + +OUI:00A0EF* + ID_OUI_FROM_DATABASE=LUCIDATA LTD. + +OUI:00A03F* + ID_OUI_FROM_DATABASE=COMPUTER SOCIETY MICROPROCESSOR & MICROPROCESSOR STANDARDS C + +OUI:00A067* + ID_OUI_FROM_DATABASE=NETWORK SERVICES GROUP + +OUI:00A0A7* + ID_OUI_FROM_DATABASE=VORAX CORPORATION + +OUI:00A02D* + ID_OUI_FROM_DATABASE=1394 Trade Association + +OUI:00A0E6* + ID_OUI_FROM_DATABASE=DIALOGIC CORPORATION + +OUI:00A04A* + ID_OUI_FROM_DATABASE=NISSHIN ELECTRIC CO., LTD. + +OUI:00A05B* + ID_OUI_FROM_DATABASE=MARQUIP, INC. + +OUI:00A08D* + ID_OUI_FROM_DATABASE=JACOMO CORPORATION + +OUI:00A06F* + ID_OUI_FROM_DATABASE=THE APPCON GROUP, INC. + +OUI:00A08E* + ID_OUI_FROM_DATABASE=Check Point Software Technologies + +OUI:00E0AA* + ID_OUI_FROM_DATABASE=ELECTROSONIC LTD. + +OUI:00E085* + ID_OUI_FROM_DATABASE=GLOBAL MAINTECH, INC. + +OUI:00E05A* + ID_OUI_FROM_DATABASE=GALEA NETWORK SECURITY + +OUI:00E022* + ID_OUI_FROM_DATABASE=Analog Devices Inc. + +OUI:00E0E7* + ID_OUI_FROM_DATABASE=RAYTHEON E-SYSTEMS, INC. + +OUI:00E00C* + ID_OUI_FROM_DATABASE=MOTOROLA + +OUI:00E04A* + ID_OUI_FROM_DATABASE=ZX Technologies, Inc + +OUI:00E00A* + ID_OUI_FROM_DATABASE=DIBA, INC. + +OUI:00E0B9* + ID_OUI_FROM_DATABASE=BYAS SYSTEMS + +OUI:00E054* + ID_OUI_FROM_DATABASE=KODAI HITEC CO., LTD. + +OUI:00E0AF* + ID_OUI_FROM_DATABASE=GENERAL DYNAMICS INFORMATION SYSTEMS + +OUI:00605B* + ID_OUI_FROM_DATABASE=IntraServer Technology, Inc. + +OUI:00604B* + ID_OUI_FROM_DATABASE=Safe-com GmbH & Co. KG + +OUI:00A0CD* + ID_OUI_FROM_DATABASE=DR. JOHANNES HEIDENHAIN GmbH + +OUI:00A0DA* + ID_OUI_FROM_DATABASE=INTEGRATED SYSTEMS Technology, Inc. + +OUI:00A03C* + ID_OUI_FROM_DATABASE=EG&G NUCLEAR INSTRUMENTS + +OUI:00A038* + ID_OUI_FROM_DATABASE=EMAIL ELECTRONICS + +OUI:00A0BE* + ID_OUI_FROM_DATABASE=INTEGRATED CIRCUIT SYSTEMS, INC. COMMUNICATIONS GROUP + +OUI:00605D* + ID_OUI_FROM_DATABASE=SCANIVALVE CORP. + +OUI:0060E4* + ID_OUI_FROM_DATABASE=COMPUSERVE, INC. + +OUI:00600A* + ID_OUI_FROM_DATABASE=SORD COMPUTER CORPORATION + +OUI:0060C4* + ID_OUI_FROM_DATABASE=SOLITON SYSTEMS K.K. + +OUI:0060C8* + ID_OUI_FROM_DATABASE=KUKA WELDING SYSTEMS & ROBOTS + +OUI:006030* + ID_OUI_FROM_DATABASE=VILLAGE TRONIC ENTWICKLUNG + +OUI:0060E7* + ID_OUI_FROM_DATABASE=RANDATA + +OUI:00602A* + ID_OUI_FROM_DATABASE=SYMICRON COMPUTER COMMUNICATIONS, LTD. + +OUI:00601E* + ID_OUI_FROM_DATABASE=SOFTLAB, INC. + +OUI:0060F8* + ID_OUI_FROM_DATABASE=Loran International Technologies Inc. + +OUI:006088* + ID_OUI_FROM_DATABASE=WHITE MOUNTAIN DSP, INC. + +OUI:00609A* + ID_OUI_FROM_DATABASE=NJK TECHNO CO. + +OUI:0060CC* + ID_OUI_FROM_DATABASE=EMTRAK, INCORPORATED + +OUI:006036* + ID_OUI_FROM_DATABASE=AIT Austrian Institute of Technology GmbH + +OUI:0060B9* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd + +OUI:0060CE* + ID_OUI_FROM_DATABASE=ACCLAIM COMMUNICATIONS + +OUI:0060F5* + ID_OUI_FROM_DATABASE=ICON WEST, INC. + +OUI:0060A4* + ID_OUI_FROM_DATABASE=GEW Technologies (PTY)Ltd + +OUI:0060CA* + ID_OUI_FROM_DATABASE=HARMONIC SYSTEMS INCORPORATED + +OUI:006024* + ID_OUI_FROM_DATABASE=GRADIENT TECHNOLOGIES, INC. + +OUI:0060FB* + ID_OUI_FROM_DATABASE=PACKETEER, INC. + +OUI:0060BC* + ID_OUI_FROM_DATABASE=KeunYoung Electronics & Communication Co., Ltd. + +OUI:0060B8* + ID_OUI_FROM_DATABASE=CORELIS Inc. + +OUI:0060FE* + ID_OUI_FROM_DATABASE=LYNX SYSTEM DEVELOPERS, INC. + +OUI:006001* + ID_OUI_FROM_DATABASE=InnoSys, Inc. + +OUI:00607D* + ID_OUI_FROM_DATABASE=SENTIENT NETWORKS INC. + +OUI:00606E* + ID_OUI_FROM_DATABASE=DAVICOM SEMICONDUCTOR, INC. + +OUI:00607E* + ID_OUI_FROM_DATABASE=GIGALABS, INC. + +OUI:0060CF* + ID_OUI_FROM_DATABASE=ALTEON NETWORKS, INC. + +OUI:006026* + ID_OUI_FROM_DATABASE=VIKING Modular Solutions + +OUI:006003* + ID_OUI_FROM_DATABASE=TERAOKA WEIGH SYSTEM PTE, LTD. + +OUI:006059* + ID_OUI_FROM_DATABASE=TECHNICAL COMMUNICATIONS CORP. + +OUI:006066* + ID_OUI_FROM_DATABASE=LACROIX Trafic + +OUI:0060DA* + ID_OUI_FROM_DATABASE=Red Lion Controls, LP + +OUI:006042* + ID_OUI_FROM_DATABASE=TKS (USA), INC. + +OUI:00A023* + ID_OUI_FROM_DATABASE=APPLIED CREATIVE TECHNOLOGY, INC. + +OUI:00A00F* + ID_OUI_FROM_DATABASE=Broadband Technologies + +OUI:00A032* + ID_OUI_FROM_DATABASE=GES SINGAPORE PTE. LTD. + +OUI:002034* + ID_OUI_FROM_DATABASE=ROTEC INDUSTRIEAUTOMATION GMBH + +OUI:0020B2* + ID_OUI_FROM_DATABASE=GKD Gesellschaft Fur Kommunikation Und Datentechnik + +OUI:002004* + ID_OUI_FROM_DATABASE=YAMATAKE-HONEYWELL CO., LTD. + +OUI:0020FE* + ID_OUI_FROM_DATABASE=TOPWARE INC. / GRAND COMPUTER + +OUI:002073* + ID_OUI_FROM_DATABASE=FUSION SYSTEMS CORPORATION + +OUI:00207A* + ID_OUI_FROM_DATABASE=WiSE Communications, Inc. + +OUI:00205C* + ID_OUI_FROM_DATABASE=InterNet Systems of Florida, Inc. + +OUI:00207E* + ID_OUI_FROM_DATABASE=FINECOM CO., LTD. + +OUI:00205A* + ID_OUI_FROM_DATABASE=COMPUTER IDENTICS + +OUI:0020E4* + ID_OUI_FROM_DATABASE=HSING TECH ENTERPRISE CO., LTD + +OUI:00A000* + ID_OUI_FROM_DATABASE=CENTILLION NETWORKS, INC. + +OUI:00A07B* + ID_OUI_FROM_DATABASE=DAWN COMPUTER INCORPORATION + +OUI:00A05C* + ID_OUI_FROM_DATABASE=INVENTORY CONVERSION, INC./ + +OUI:00206F* + ID_OUI_FROM_DATABASE=FLOWPOINT CORPORATION + +OUI:0020DF* + ID_OUI_FROM_DATABASE=KYOSAN ELECTRIC MFG. CO., LTD. + +OUI:002010* + ID_OUI_FROM_DATABASE=JEOL SYSTEM TECHNOLOGY CO. LTD + +OUI:002020* + ID_OUI_FROM_DATABASE=MEGATRON COMPUTER INDUSTRIES PTY, LTD. + +OUI:002037* + ID_OUI_FROM_DATABASE=SEAGATE TECHNOLOGY + +OUI:0020A0* + ID_OUI_FROM_DATABASE=OA LABORATORY CO., LTD. + +OUI:00C0A3* + ID_OUI_FROM_DATABASE=DUAL ENTERPRISES CORPORATION + +OUI:0070B0* + ID_OUI_FROM_DATABASE=M/A-COM INC. COMPANIES + +OUI:009D8E* + ID_OUI_FROM_DATABASE=CARDIAC RECORDERS, INC. + +OUI:006086* + ID_OUI_FROM_DATABASE=LOGIC REPLACEMENT TECH. LTD. + +OUI:001C7C* + ID_OUI_FROM_DATABASE=PERQ SYSTEMS CORPORATION + +OUI:00C059* + ID_OUI_FROM_DATABASE=DENSO CORPORATION + +OUI:00C0A9* + ID_OUI_FROM_DATABASE=BARRON MCCANN LTD. + +OUI:00C069* + ID_OUI_FROM_DATABASE=Axxcelera Broadband Wireless + +OUI:00C019* + ID_OUI_FROM_DATABASE=LEAP TECHNOLOGY, INC. + +OUI:00A062* + ID_OUI_FROM_DATABASE=AES PRODATA + +OUI:00A008* + ID_OUI_FROM_DATABASE=NETCORP + +OUI:00A01B* + ID_OUI_FROM_DATABASE=PREMISYS COMMUNICATIONS, INC. + +OUI:00A04B* + ID_OUI_FROM_DATABASE=TFL LAN INC. + +OUI:00A015* + ID_OUI_FROM_DATABASE=WYLE + +OUI:00A011* + ID_OUI_FROM_DATABASE=MUTOH INDUSTRIES LTD. + +OUI:00A0B6* + ID_OUI_FROM_DATABASE=SANRITZ AUTOMATION CO., LTD. + +OUI:00A0DD* + ID_OUI_FROM_DATABASE=AZONIX CORPORATION + +OUI:00A00A* + ID_OUI_FROM_DATABASE=Airspan + +OUI:00A03B* + ID_OUI_FROM_DATABASE=TOSHIN ELECTRIC CO., LTD. + +OUI:00A0F3* + ID_OUI_FROM_DATABASE=STAUBLI + +OUI:00A097* + ID_OUI_FROM_DATABASE=JC INFORMATION SYSTEMS + +OUI:00A082* + ID_OUI_FROM_DATABASE=NKT ELEKTRONIK A/S + +OUI:00A072* + ID_OUI_FROM_DATABASE=OVATION SYSTEMS LTD. + +OUI:00A0B2* + ID_OUI_FROM_DATABASE=SHIMA SEIKI + +OUI:00A0E5* + ID_OUI_FROM_DATABASE=NHC COMMUNICATIONS + +OUI:00A0D3* + ID_OUI_FROM_DATABASE=INSTEM COMPUTER SYSTEMS, LTD. + +OUI:00A0BA* + ID_OUI_FROM_DATABASE=PATTON ELECTRONICS CO. + +OUI:00A0B4* + ID_OUI_FROM_DATABASE=TEXAS MICROSYSTEMS, INC. + +OUI:00A0AF* + ID_OUI_FROM_DATABASE=WMS INDUSTRIES + +OUI:00A0FE* + ID_OUI_FROM_DATABASE=BOSTON TECHNOLOGY, INC. + +OUI:00202F* + ID_OUI_FROM_DATABASE=ZETA COMMUNICATIONS, LTD. + +OUI:002060* + ID_OUI_FROM_DATABASE=ALCATEL ITALIA S.p.A. + +OUI:00209A* + ID_OUI_FROM_DATABASE=THE 3DO COMPANY + +OUI:00205E* + ID_OUI_FROM_DATABASE=CASTLE ROCK, INC. + +OUI:00207C* + ID_OUI_FROM_DATABASE=AUTEC GMBH + +OUI:002075* + ID_OUI_FROM_DATABASE=MOTOROLA COMMUNICATION ISRAEL + +OUI:002015* + ID_OUI_FROM_DATABASE=ACTIS COMPUTER SA + +OUI:0020E9* + ID_OUI_FROM_DATABASE=DANTEL + +OUI:00204A* + ID_OUI_FROM_DATABASE=PRONET GMBH + +OUI:002029* + ID_OUI_FROM_DATABASE=TELEPROCESSING PRODUCTS, INC. + +OUI:002051* + ID_OUI_FROM_DATABASE=Verilink Corporation + +OUI:0020A1* + ID_OUI_FROM_DATABASE=DOVATRON + +OUI:002024* + ID_OUI_FROM_DATABASE=PACIFIC COMMUNICATION SCIENCES + +OUI:00209D* + ID_OUI_FROM_DATABASE=LIPPERT AUTOMATIONSTECHNIK + +OUI:002041* + ID_OUI_FROM_DATABASE=DATA NET + +OUI:002076* + ID_OUI_FROM_DATABASE=REUDO CORPORATION + +OUI:00206E* + ID_OUI_FROM_DATABASE=XACT, INC. + +OUI:0020CA* + ID_OUI_FROM_DATABASE=DIGITAL OCEAN + +OUI:002085* + ID_OUI_FROM_DATABASE=Eaton Corporation + +OUI:0020CD* + ID_OUI_FROM_DATABASE=HYBRID NETWORKS, INC. + +OUI:0020E7* + ID_OUI_FROM_DATABASE=B&W NUCLEAR SERVICE COMPANY + +OUI:0020AC* + ID_OUI_FROM_DATABASE=INTERFLEX DATENSYSTEME GMBH + +OUI:0020F6* + ID_OUI_FROM_DATABASE=NET TEK AND KARLNET, INC. + +OUI:0020D3* + ID_OUI_FROM_DATABASE=OST (OUEST STANDARD TELEMATIQU + +OUI:0020D8* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002017* + ID_OUI_FROM_DATABASE=ORBOTECH + +OUI:002025* + ID_OUI_FROM_DATABASE=CONTROL TECHNOLOGY, INC. + +OUI:00C08B* + ID_OUI_FROM_DATABASE=RISQ MODULAR SYSTEMS, INC. + +OUI:00C0CD* + ID_OUI_FROM_DATABASE=COMELTA, S.A. + +OUI:00C04B* + ID_OUI_FROM_DATABASE=CREATIVE MICROSYSTEMS + +OUI:00C0A1* + ID_OUI_FROM_DATABASE=TOKYO DENSHI SEKEI CO. + +OUI:00C03E* + ID_OUI_FROM_DATABASE=FA. GEBR. HELLER GMBH + +OUI:00C0E1* + ID_OUI_FROM_DATABASE=SONIC SOLUTIONS + +OUI:00C047* + ID_OUI_FROM_DATABASE=UNIMICRO SYSTEMS, INC. + +OUI:00C046* + ID_OUI_FROM_DATABASE=Blue Chip Technology Ltd + +OUI:00C00D* + ID_OUI_FROM_DATABASE=ADVANCED LOGIC RESEARCH, INC. + +OUI:00C0FA* + ID_OUI_FROM_DATABASE=CANARY COMMUNICATIONS, INC. + +OUI:00C0B7* + ID_OUI_FROM_DATABASE=AMERICAN POWER CONVERSION CORP + +OUI:00C0BA* + ID_OUI_FROM_DATABASE=NETVANTAGE + +OUI:00C0B6* + ID_OUI_FROM_DATABASE=Overland Storage, Inc. + +OUI:00C048* + ID_OUI_FROM_DATABASE=BAY TECHNICAL ASSOCIATES + +OUI:00C03F* + ID_OUI_FROM_DATABASE=STORES AUTOMATED SYSTEMS, INC. + +OUI:00C00E* + ID_OUI_FROM_DATABASE=PSITECH, INC. + +OUI:00C036* + ID_OUI_FROM_DATABASE=RAYTECH ELECTRONIC CORP. + +OUI:00C009* + ID_OUI_FROM_DATABASE=KT TECHNOLOGY (S) PTE LTD + +OUI:00C0EA* + ID_OUI_FROM_DATABASE=ARRAY TECHNOLOGY LTD. + +OUI:00C03A* + ID_OUI_FROM_DATABASE=MEN-MIKRO ELEKTRONIK GMBH + +OUI:00C040* + ID_OUI_FROM_DATABASE=ECCI + +OUI:00C04C* + ID_OUI_FROM_DATABASE=DEPARTMENT OF FOREIGN AFFAIRS + +OUI:00C01C* + ID_OUI_FROM_DATABASE=INTERLINK COMMUNICATIONS LTD. + +OUI:00C086* + ID_OUI_FROM_DATABASE=THE LYNK CORPORATION + +OUI:00C08D* + ID_OUI_FROM_DATABASE=TRONIX PRODUCT DEVELOPMENT + +OUI:00C0A2* + ID_OUI_FROM_DATABASE=INTERMEDIUM A/S + +OUI:00C070* + ID_OUI_FROM_DATABASE=SECTRA SECURE-TRANSMISSION AB + +OUI:00C057* + ID_OUI_FROM_DATABASE=MYCO ELECTRONICS + +OUI:00C0DF* + ID_OUI_FROM_DATABASE=KYE Systems Corp. + +OUI:00C0F6* + ID_OUI_FROM_DATABASE=CELAN TECHNOLOGY INC. + +OUI:00C08F* + ID_OUI_FROM_DATABASE=Panasonic Electric Works Co., Ltd. + +OUI:00C012* + ID_OUI_FROM_DATABASE=NETSPAN CORPORATION + +OUI:00C0C4* + ID_OUI_FROM_DATABASE=COMPUTER OPERATIONAL + +OUI:00C0C2* + ID_OUI_FROM_DATABASE=INFINITE NETWORKS LTD. + +OUI:00C0D3* + ID_OUI_FROM_DATABASE=OLYMPUS IMAGE SYSTEMS, INC. + +OUI:00C0B0* + ID_OUI_FROM_DATABASE=GCC TECHNOLOGIES,INC. + +OUI:00C0F4* + ID_OUI_FROM_DATABASE=INTERLINK SYSTEM CO., LTD. + +OUI:00C0E2* + ID_OUI_FROM_DATABASE=CALCOMP, INC. + +OUI:00C0CA* + ID_OUI_FROM_DATABASE=ALFA, INC. + +OUI:00C07B* + ID_OUI_FROM_DATABASE=ASCEND COMMUNICATIONS, INC. + +OUI:00C052* + ID_OUI_FROM_DATABASE=BURR-BROWN + +OUI:00C0BE* + ID_OUI_FROM_DATABASE=ALCATEL - SEL + +OUI:00408F* + ID_OUI_FROM_DATABASE=WM-DATA MINFO AB + +OUI:0040B7* + ID_OUI_FROM_DATABASE=STEALTH COMPUTER SYSTEMS + +OUI:004057* + ID_OUI_FROM_DATABASE=LOCKHEED - SANDERS + +OUI:004017* + ID_OUI_FROM_DATABASE=Silex Technology America + +OUI:004087* + ID_OUI_FROM_DATABASE=UBITREX CORPORATION + +OUI:00400E* + ID_OUI_FROM_DATABASE=MEMOTEC, INC. + +OUI:00C09E* + ID_OUI_FROM_DATABASE=CACHE COMPUTERS, INC. + +OUI:00C093* + ID_OUI_FROM_DATABASE=ALTA RESEARCH CORP. + +OUI:00C034* + ID_OUI_FROM_DATABASE=TRANSACTION NETWORK + +OUI:004034* + ID_OUI_FROM_DATABASE=BUSTEK CORPORATION + +OUI:004097* + ID_OUI_FROM_DATABASE=DATEX DIVISION OF + +OUI:00401E* + ID_OUI_FROM_DATABASE=ICC + +OUI:00407C* + ID_OUI_FROM_DATABASE=QUME CORPORATION + +OUI:004060* + ID_OUI_FROM_DATABASE=COMENDEC LTD + +OUI:004056* + ID_OUI_FROM_DATABASE=MCM JAPAN LTD. + +OUI:004095* + ID_OUI_FROM_DATABASE=R.P.T. INTERGROUPS INT'L LTD. + +OUI:0040C3* + ID_OUI_FROM_DATABASE=FISCHER AND PORTER CO. + +OUI:0040F1* + ID_OUI_FROM_DATABASE=CHUO ELECTRONICS CO., LTD. + +OUI:004061* + ID_OUI_FROM_DATABASE=DATATECH ENTERPRISES CO., LTD. + +OUI:00408B* + ID_OUI_FROM_DATABASE=RAYLAN CORPORATION + +OUI:004020* + ID_OUI_FROM_DATABASE=CommScope Inc + +OUI:00406E* + ID_OUI_FROM_DATABASE=COROLLARY, INC. + +OUI:004066* + ID_OUI_FROM_DATABASE=Hitachi Metals, Ltd. + +OUI:004016* + ID_OUI_FROM_DATABASE=ADC - Global Connectivity Solutions Division + +OUI:004086* + ID_OUI_FROM_DATABASE=MICHELS & KLEBERHOFF COMPUTER + +OUI:0040DC* + ID_OUI_FROM_DATABASE=TRITEC ELECTRONIC GMBH + +OUI:004074* + ID_OUI_FROM_DATABASE=CABLE AND WIRELESS + +OUI:004084* + ID_OUI_FROM_DATABASE=HONEYWELL ACS + +OUI:0040B8* + ID_OUI_FROM_DATABASE=IDEA ASSOCIATES + +OUI:004058* + ID_OUI_FROM_DATABASE=KRONOS, INC. + +OUI:0040A8* + ID_OUI_FROM_DATABASE=IMF INTERNATIONAL LTD. + +OUI:0080BB* + ID_OUI_FROM_DATABASE=HUGHES LAN SYSTEMS + +OUI:00C0A0* + ID_OUI_FROM_DATABASE=ADVANCE MICRO RESEARCH, INC. + +OUI:00C0D7* + ID_OUI_FROM_DATABASE=TAIWAN TRADING CENTER DBA + +OUI:00C037* + ID_OUI_FROM_DATABASE=DYNATEM + +OUI:00C05F* + ID_OUI_FROM_DATABASE=FINE-PAL COMPANY LIMITED + +OUI:0040CE* + ID_OUI_FROM_DATABASE=NET-SOURCE, INC. + +OUI:004080* + ID_OUI_FROM_DATABASE=ATHENIX CORPORATION + +OUI:0040BB* + ID_OUI_FROM_DATABASE=GOLDSTAR CABLE CO., LTD. + +OUI:0040B1* + ID_OUI_FROM_DATABASE=CODONICS INC. + +OUI:00402E* + ID_OUI_FROM_DATABASE=PRECISION SOFTWARE, INC. + +OUI:00C0CE* + ID_OUI_FROM_DATABASE=CEI SYSTEMS & ENGINEERING PTE + +OUI:00409B* + ID_OUI_FROM_DATABASE=HAL COMPUTER SYSTEMS INC. + +OUI:004073* + ID_OUI_FROM_DATABASE=BASS ASSOCIATES + +OUI:10005A* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:004005* + ID_OUI_FROM_DATABASE=ANI COMMUNICATIONS INC. + +OUI:004099* + ID_OUI_FROM_DATABASE=NEWGEN SYSTEMS CORP. + +OUI:0040E1* + ID_OUI_FROM_DATABASE=MARNER INTERNATIONAL, INC. + +OUI:0080DD* + ID_OUI_FROM_DATABASE=GMX INC/GIMIX + +OUI:0080B7* + ID_OUI_FROM_DATABASE=STELLAR COMPUTER + +OUI:008002* + ID_OUI_FROM_DATABASE=SATELCOM (UK) LTD + +OUI:00805C* + ID_OUI_FROM_DATABASE=AGILIS CORPORATION + +OUI:0080E7* + ID_OUI_FROM_DATABASE=LYNWOOD SCIENTIFIC DEV. LTD. + +OUI:008070* + ID_OUI_FROM_DATABASE=COMPUTADORAS MICRON + +OUI:00808F* + ID_OUI_FROM_DATABASE=C. ITOH ELECTRONICS, INC. + +OUI:000091* + ID_OUI_FROM_DATABASE=ANRITSU CORPORATION + +OUI:000094* + ID_OUI_FROM_DATABASE=ASANTE TECHNOLOGIES + +OUI:000090* + ID_OUI_FROM_DATABASE=MICROCOM + +OUI:000047* + ID_OUI_FROM_DATABASE=NICOLET INSTRUMENTS CORP. + +OUI:0000FB* + ID_OUI_FROM_DATABASE=RECHNER ZUR KOMMUNIKATION + +OUI:0000A3* + ID_OUI_FROM_DATABASE=NETWORK APPLICATION TECHNOLOGY + +OUI:00008F* + ID_OUI_FROM_DATABASE=Raytheon + +OUI:00007E* + ID_OUI_FROM_DATABASE=CLUSTRIX CORPORATION + +OUI:00000A* + ID_OUI_FROM_DATABASE=OMRON TATEISI ELECTRONICS CO. + +OUI:000063* + ID_OUI_FROM_DATABASE=BARCO CONTROL ROOMS GMBH + +OUI:00004E* + ID_OUI_FROM_DATABASE=AMPEX CORPORATION + +OUI:0000C2* + ID_OUI_FROM_DATABASE=INFORMATION PRESENTATION TECH. + +OUI:000034* + ID_OUI_FROM_DATABASE=NETWORK RESOURCES CORPORATION + +OUI:000049* + ID_OUI_FROM_DATABASE=APRICOT COMPUTERS, LTD + +OUI:0000E2* + ID_OUI_FROM_DATABASE=ACER TECHNOLOGIES CORP. + +OUI:000097* + ID_OUI_FROM_DATABASE=EMC Corporation + +OUI:0000D4* + ID_OUI_FROM_DATABASE=PURE DATA LTD. + +OUI:0000E1* + ID_OUI_FROM_DATABASE=GRID SYSTEMS + +OUI:000044* + ID_OUI_FROM_DATABASE=CASTELLE CORPORATION + +OUI:000027* + ID_OUI_FROM_DATABASE=JAPAN RADIO COMPANY + +OUI:004049* + ID_OUI_FROM_DATABASE=Roche Diagnostics International Ltd. + +OUI:004029* + ID_OUI_FROM_DATABASE=Compex + +OUI:008038* + ID_OUI_FROM_DATABASE=DATA RESEARCH & APPLICATIONS + +OUI:008090* + ID_OUI_FROM_DATABASE=MICROTEK INTERNATIONAL, INC. + +OUI:0080C3* + ID_OUI_FROM_DATABASE=BICC INFORMATION SYSTEMS & SVC + +OUI:00805A* + ID_OUI_FROM_DATABASE=TULIP COMPUTERS INTERNAT'L B.V + +OUI:0080F0* + ID_OUI_FROM_DATABASE=Panasonic Communications Co., Ltd. + +OUI:008043* + ID_OUI_FROM_DATABASE=NETWORLD, INC. + +OUI:0080B0* + ID_OUI_FROM_DATABASE=ADVANCED INFORMATION + +OUI:008066* + ID_OUI_FROM_DATABASE=ARCOM CONTROL SYSTEMS, LTD. + +OUI:004051* + ID_OUI_FROM_DATABASE=GRACILIS, INC. + +OUI:004064* + ID_OUI_FROM_DATABASE=KLA INSTRUMENTS CORPORATION + +OUI:004028* + ID_OUI_FROM_DATABASE=NETCOMM LIMITED + +OUI:004013* + ID_OUI_FROM_DATABASE=NTT DATA COMM. SYSTEMS CORP. + +OUI:0040A0* + ID_OUI_FROM_DATABASE=GOLDSTAR CO., LTD. + +OUI:0040B2* + ID_OUI_FROM_DATABASE=SYSTEMFORSCHUNG + +OUI:004071* + ID_OUI_FROM_DATABASE=ATM COMPUTER GMBH + +OUI:0080BF* + ID_OUI_FROM_DATABASE=TAKAOKA ELECTRIC MFG. CO. LTD. + +OUI:0080F6* + ID_OUI_FROM_DATABASE=SYNERGY MICROSYSTEMS + +OUI:000058* + ID_OUI_FROM_DATABASE=RACORE COMPUTER PRODUCTS INC. + +OUI:000050* + ID_OUI_FROM_DATABASE=RADISYS CORPORATION + +OUI:008082* + ID_OUI_FROM_DATABASE=PEP MODULAR COMPUTERS GMBH + +OUI:008096* + ID_OUI_FROM_DATABASE=HUMAN DESIGNED SYSTEMS, INC. + +OUI:0080D5* + ID_OUI_FROM_DATABASE=CADRE TECHNOLOGIES + +OUI:00803E* + ID_OUI_FROM_DATABASE=SYNERNETICS + +OUI:00809A* + ID_OUI_FROM_DATABASE=NOVUS NETWORKS LTD + +OUI:0080B3* + ID_OUI_FROM_DATABASE=AVAL DATA CORPORATION + +OUI:0080A3* + ID_OUI_FROM_DATABASE=Lantronix + +OUI:00803C* + ID_OUI_FROM_DATABASE=TVS ELECTRONICS LTD + +OUI:008061* + ID_OUI_FROM_DATABASE=LITTON SYSTEMS, INC. + +OUI:0080AD* + ID_OUI_FROM_DATABASE=CNET TECHNOLOGY, INC. + +OUI:008081* + ID_OUI_FROM_DATABASE=KENDALL SQUARE RESEARCH CORP. + +OUI:008019* + ID_OUI_FROM_DATABASE=DAYNA COMMUNICATIONS, INC. + +OUI:00808B* + ID_OUI_FROM_DATABASE=DACOLL LIMITED + +OUI:008097* + ID_OUI_FROM_DATABASE=CENTRALP AUTOMATISMES + +OUI:0080FC* + ID_OUI_FROM_DATABASE=AVATAR CORPORATION + +OUI:008076* + ID_OUI_FROM_DATABASE=MCNC + +OUI:008080* + ID_OUI_FROM_DATABASE=DATAMEDIA CORPORATION + +OUI:0000E6* + ID_OUI_FROM_DATABASE=APTOR PRODUITS DE COMM INDUST + +OUI:000084* + ID_OUI_FROM_DATABASE=SUPERNET + +OUI:0000FF* + ID_OUI_FROM_DATABASE=CAMTEC ELECTRONICS LTD. + +OUI:00007B* + ID_OUI_FROM_DATABASE=RESEARCH MACHINES + +OUI:000056* + ID_OUI_FROM_DATABASE=DR. B. STRUCK + +OUI:0000BB* + ID_OUI_FROM_DATABASE=TRI-DATA + +OUI:080025* + ID_OUI_FROM_DATABASE=CONTROL DATA + +OUI:080020* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:027001* + ID_OUI_FROM_DATABASE=RACAL-DATACOM + +OUI:080006* + ID_OUI_FROM_DATABASE=SIEMENS AG + +OUI:08007E* + ID_OUI_FROM_DATABASE=AMALGAMATED WIRELESS(AUS) LTD + +OUI:080075* + ID_OUI_FROM_DATABASE=DANSK DATA ELECTRONIK + +OUI:080073* + ID_OUI_FROM_DATABASE=TECMAR INC. + +OUI:080069* + ID_OUI_FROM_DATABASE=SILICON GRAPHICS INC. + +OUI:080061* + ID_OUI_FROM_DATABASE=JAROGATE LTD. + +OUI:08005D* + ID_OUI_FROM_DATABASE=GOULD INC. + +OUI:08004E* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:08004A* + ID_OUI_FROM_DATABASE=BANYAN SYSTEMS INC. + +OUI:08004C* + ID_OUI_FROM_DATABASE=HYDRA COMPUTER SYSTEMS INC. + +OUI:080043* + ID_OUI_FROM_DATABASE=PIXEL COMPUTER INC. + +OUI:08003A* + ID_OUI_FROM_DATABASE=ORCATECH INC. + +OUI:080035* + ID_OUI_FROM_DATABASE=MICROFIVE CORPORATION + +OUI:080036* + ID_OUI_FROM_DATABASE=INTERGRAPH CORPORATION + +OUI:08002D* + ID_OUI_FROM_DATABASE=LAN-TEC INC. + +OUI:000025* + ID_OUI_FROM_DATABASE=RAMTEK CORP. + +OUI:00003A* + ID_OUI_FROM_DATABASE=CHYRON CORPORATION + +OUI:000077* + ID_OUI_FROM_DATABASE=INTERPHASE CORPORATION + +OUI:000096* + ID_OUI_FROM_DATABASE=MARCONI ELECTRONICS LTD. + +OUI:000076* + ID_OUI_FROM_DATABASE=ABEKAS VIDEO SYSTEM + +OUI:0000EA* + ID_OUI_FROM_DATABASE=UPNOD AB + +OUI:000074* + ID_OUI_FROM_DATABASE=RICOH COMPANY LTD. + +OUI:00006A* + ID_OUI_FROM_DATABASE=COMPUTER CONSOLES INC. + +OUI:0000C4* + ID_OUI_FROM_DATABASE=WATERS DIV. OF MILLIPORE + +OUI:000006* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:0001C8* + ID_OUI_FROM_DATABASE=THOMAS CONRAD CORP. + +OUI:00DD0E* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:08008D* + ID_OUI_FROM_DATABASE=XYVISION INC. + +OUI:080059* + ID_OUI_FROM_DATABASE=A/S MYCRON + +OUI:021C7C* + ID_OUI_FROM_DATABASE=PERQ SYSTEMS CORPORATION + +OUI:100000* + ID_OUI_FROM_DATABASE=Private + +OUI:080004* + ID_OUI_FROM_DATABASE=CROMEMCO INCORPORATED + +OUI:00DD07* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:00003E* + ID_OUI_FROM_DATABASE=SIMPACT + +OUI:04E0C4* + ID_OUI_FROM_DATABASE=TRIUMPH-ADLER AG + +OUI:040AE0* + ID_OUI_FROM_DATABASE=XMIT AG COMPUTER NETWORKS + +OUI:080016* + ID_OUI_FROM_DATABASE=BARRISTER INFO SYS CORP + +OUI:080012* + ID_OUI_FROM_DATABASE=BELL ATLANTIC INTEGRATED SYST. + +OUI:0001C8* + ID_OUI_FROM_DATABASE=CONRAD CORP. + +OUI:0000F9* + ID_OUI_FROM_DATABASE=QUOTRON SYSTEMS INC. + +OUI:0000BF* + ID_OUI_FROM_DATABASE=SYMMETRIC COMPUTER SYSTEMS + +OUI:000085* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:000028* + ID_OUI_FROM_DATABASE=PRODIGY SYSTEMS CORPORATION + +OUI:000012* + ID_OUI_FROM_DATABASE=INFORMATION TECHNOLOGY LIMITED + +OUI:080085* + ID_OUI_FROM_DATABASE=ELXSI + +OUI:00005B* + ID_OUI_FROM_DATABASE=ELTEC ELEKTRONIK AG + +OUI:000054* + ID_OUI_FROM_DATABASE=Schneider Electric + +OUI:0000A9* + ID_OUI_FROM_DATABASE=NETWORK SYSTEMS CORP. + +OUI:000059* + ID_OUI_FROM_DATABASE=Hellige GMBH + +OUI:000099* + ID_OUI_FROM_DATABASE=MTX, INC. + +OUI:0000E9* + ID_OUI_FROM_DATABASE=ISICAD, INC. + +OUI:08003F* + ID_OUI_FROM_DATABASE=FRED KOSCHARA ENTERPRISES + +OUI:080002* + ID_OUI_FROM_DATABASE=BRIDGE COMMUNICATIONS INC. + +OUI:08008B* + ID_OUI_FROM_DATABASE=PYRAMID TECHNOLOGY CORP. + +OUI:000002* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:84F6FA* + ID_OUI_FROM_DATABASE=Miovision Technologies Incorporated + +OUI:CC3B3E* + ID_OUI_FROM_DATABASE=Lester Electrical + +OUI:C05627* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:88074B* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:4065A3* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:00789E* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:44E9DD* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:10F681* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:B888E3* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:002622* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:001EEC* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:DC0EA1* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:FC4596* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:208984* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:247C4C* + ID_OUI_FROM_DATABASE=Herman Miller + +OUI:180373* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8B156* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:1C4024* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8BC12* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001B5B* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:002456* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:002351* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:00253C* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:0022A4* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:C0830A* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:D0431E* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:246E96* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:204747* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:4C7625* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B8AC6F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001EC9* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:E09861* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:F4F1E1* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:74C99A* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:3C197D* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:60BEB5* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:7845C4* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B4E1C4* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:D86C02* + ID_OUI_FROM_DATABASE=Huaqin Telecom Technology Co.,Ltd + +OUI:0019D2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:7C5CF8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001E67* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001F3C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0022FA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001517* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00166F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A44E31* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:6C8814* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:F81654* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:3413E8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:34E6AD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:FCF8AE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:648099* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:002314* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4025C2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:8CA982* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:D07E35* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:685D43* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:90E2BA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0026C7* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:8086F2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:78FF57* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:20934D* + ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD + +OUI:00AA00* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:6CF37F* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:605BB4* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:9C0E4A* + ID_OUI_FROM_DATABASE=Shenzhen Vastking Electronic Co.,Ltd. + +OUI:ACE5F0* + ID_OUI_FROM_DATABASE=Doppler Labs + +OUI:00F28B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5414FD* + ID_OUI_FROM_DATABASE=Orbbec 3D Technology International + +OUI:1C4BD6* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:94DBC9* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:40E230* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:00006E* + ID_OUI_FROM_DATABASE=Artisoft Inc. + +OUI:A0F459* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:0C6AE6* + ID_OUI_FROM_DATABASE=Stanley Security Solutions + +OUI:E874E6* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:00247B* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:689C5E* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:0012CF* + ID_OUI_FROM_DATABASE=Accton Technology Corp + +OUI:0030D3* + ID_OUI_FROM_DATABASE=Agilent Technologies, Inc. + +OUI:38229D* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:002233* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:D4D184* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:34C3D2* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:38E3C5* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + +OUI:D0E44A* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:9433DD* + ID_OUI_FROM_DATABASE=Taco Inc + +OUI:948815* + ID_OUI_FROM_DATABASE=Infinique Worldwide Inc + +OUI:3010B3* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:001802* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:ECCD6D* + ID_OUI_FROM_DATABASE=Allied Telesis, Inc. + +OUI:74C246* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:F0272D* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:00225F* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:983B16* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:402BA1* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:0025E7* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:D05162* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:94CE2C* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001A80* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:0024BE* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:001620* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:0012EE* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:20689D* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:446D57* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:44EE02* + ID_OUI_FROM_DATABASE=MTI Ltd. + +OUI:0026B6* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:B4EEB4* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:FCB4E6* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:F05C19* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:C43DC7* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:000FB5* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00095B* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:F87394* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:70AAB2* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:0026FF* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:406F2A* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:002557* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:0024FE* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:745AAA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:7C1CF1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C0FFD4* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:405D82* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:803773* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00264D* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:74A528* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:30A220* + ID_OUI_FROM_DATABASE=ARG Telecom + +OUI:783E53* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:4CF2BF* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:70D931* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:00E063* + ID_OUI_FROM_DATABASE=Cabletron Systems, Inc. + +OUI:E01D3B* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:D476EA* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0040FB* + ID_OUI_FROM_DATABASE=CASCADE COMMUNICATIONS + +OUI:F05A09* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:503275* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:28CC01* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B46293* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:04FE31* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:845181* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D831CF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F8D0BD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FCC734* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E4B021* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B0EC71* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3CBBFD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:24F5AA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:2CAE2B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C488E5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7C9122* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E8B4C8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:18895B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E0DB10* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E09971* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:6077E2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:680571* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:6C2F2C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5056BF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:000136* + ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. + +OUI:F88E85* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:300D43* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:6C2779* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:607EDD* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:F88096* + ID_OUI_FROM_DATABASE=Elsys Equipamentos Eletrônicos Ltda + +OUI:E0B9E5* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:0CBF15* + ID_OUI_FROM_DATABASE=Genetec Inc. + +OUI:000B5D* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:F4CAE5* + ID_OUI_FROM_DATABASE=FREEBOX SAS + +OUI:002100* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:002147* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0022AA* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0022D7* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:002331* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:00241E* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:78A2A0* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001B7A* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:40F407* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:B8AE6E* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:60A8FE* + ID_OUI_FROM_DATABASE=Nokia + +OUI:546751* + ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc. + +OUI:84BA3B* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:0018C5* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:80501B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:347E39* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:A87E33* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00247D* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001BAF* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001C35* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001CD4* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001979* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:9C1874* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0021FC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001F5D* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0025CF* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0025D0* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001FDE* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:907282* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:006CFD* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:1C234F* + ID_OUI_FROM_DATABASE=EDMI Europe Ltd + +OUI:A444D1* + ID_OUI_FROM_DATABASE=Wingtech Group (HongKong)Limited + +OUI:1C9E46* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:005058* + ID_OUI_FROM_DATABASE=Sangoma Technologies + +OUI:3482DE* + ID_OUI_FROM_DATABASE=Kiio Inc + +OUI:0008F6* + ID_OUI_FROM_DATABASE=Sumitomo Electric Industries,Ltd + +OUI:00005F* + ID_OUI_FROM_DATABASE=Sumitomo Electric Industries,Ltd + +OUI:A0C589* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:74BFB7* + ID_OUI_FROM_DATABASE=Nusoft Corporation + +OUI:50DA00* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:9C2A83* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E45D75* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3CBEE1* + ID_OUI_FROM_DATABASE=NIKON CORPORATION + +OUI:047E4A* + ID_OUI_FROM_DATABASE=moobox CO., Ltd. + +OUI:F01B6C* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:E0C767* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2C09CB* + ID_OUI_FROM_DATABASE=COBS AB + +OUI:60ACC8* + ID_OUI_FROM_DATABASE=KunTeng Inc. + +OUI:0404EA* + ID_OUI_FROM_DATABASE=Valens Semiconductor Ltd. + +OUI:800DD7* + ID_OUI_FROM_DATABASE=Latticework, Inc + +OUI:402E28* + ID_OUI_FROM_DATABASE=MiXTelematics + +OUI:18C501* + ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT + +OUI:546D52* + ID_OUI_FROM_DATABASE=TOPVIEW OPTRONICS CORP. + +OUI:CCB3AB* + ID_OUI_FROM_DATABASE=shenzhen Biocare Bio-Medical Equipment Co.,Ltd. + +OUI:E4B318* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00C88B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A85EE4* + ID_OUI_FROM_DATABASE=12Sided Technology, LLC + +OUI:000CC1* + ID_OUI_FROM_DATABASE=Eaton Corporation + +OUI:0090F9* + ID_OUI_FROM_DATABASE=Imagine Communications + +OUI:04C103* + ID_OUI_FROM_DATABASE=Clover Network, Inc. + +OUI:F877B8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1C553A* + ID_OUI_FROM_DATABASE=QianGua Corp. + +OUI:E4A7A0* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E4FAED* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:789682* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:F02745* + ID_OUI_FROM_DATABASE=F-Secure Corporation + +OUI:54D0B4* + ID_OUI_FROM_DATABASE=Xiamen Four-Faith Communication Technology Co.,Ltd + +OUI:D017C2* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:10DA43* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:001625* + ID_OUI_FROM_DATABASE=Impinj, Inc. + +OUI:60EE5C* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:58D67A* + ID_OUI_FROM_DATABASE=TCPlink + +OUI:00A0DE* + ID_OUI_FROM_DATABASE=YAMAHA CORPORATION + +OUI:081F71* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:2C2D48* + ID_OUI_FROM_DATABASE=bct electronic GesmbH + +OUI:E4A471* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:60B617* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:18A3E8* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:741E93* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:00A0F4* + ID_OUI_FROM_DATABASE=GE + +OUI:00CAE5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4883C7* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:7050AF* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:F4EF9E* + ID_OUI_FROM_DATABASE=SGSG SCIENCE & TECHNOLOGY CO. LTD + +OUI:DC9C9F* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:0CBF3F* + ID_OUI_FROM_DATABASE=Shenzhen Lencotion Technology Co.,Ltd + +OUI:84FEDC* + ID_OUI_FROM_DATABASE=Borqs Beijing Ltd. + +OUI:F03E90* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:D8D723* + ID_OUI_FROM_DATABASE=IDS, Inc + +OUI:703A0E* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:7054D2* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:7C0507* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:C07CD1* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:94DBDA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:384C4F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E4A8B6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:244C07* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E840F2* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:F0D1B8* + ID_OUI_FROM_DATABASE=LEDVANCE + +OUI:60B387* + ID_OUI_FROM_DATABASE=Synergics Technologies GmbH + +OUI:7085C2* + ID_OUI_FROM_DATABASE=ASRock Incorporation + +OUI:C825E1* + ID_OUI_FROM_DATABASE=Lemobile Information Technology (Beijing) Co., Ltd + +OUI:0022B1* + ID_OUI_FROM_DATABASE=Elbit Systems Ltd. + +OUI:0000B4* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:00065F* + ID_OUI_FROM_DATABASE=ECI Telecom Ltd. + +OUI:001F45* + ID_OUI_FROM_DATABASE=Enterasys + +OUI:0090FA* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:50C971* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:001D82* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:001317* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:749781* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B4B15A* + ID_OUI_FROM_DATABASE=Siemens AG Energy Management Division + +OUI:A8D828* + ID_OUI_FROM_DATABASE=Ascensia Diabetes Care + +OUI:FCBC9C* + ID_OUI_FROM_DATABASE=Vimar Spa + +OUI:149ECF* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:AC620D* + ID_OUI_FROM_DATABASE=Jabil Circuit(Wuxi) Co.,Ltd + +OUI:008CFA* + ID_OUI_FROM_DATABASE=INVENTEC Corporation + +OUI:0008B9* + ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD. + +OUI:C83F26* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:00E0E6* + ID_OUI_FROM_DATABASE=INCAA Computers + +OUI:5C5EAB* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:7819F7* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:2C2172* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:88E0F3* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:4C9614* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:3C8AB0* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:B0C69A* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:009069* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:204E71* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:F4B52F* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:88A25E* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:001BC0* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:F49EEF* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + +OUI:F4911E* + ID_OUI_FROM_DATABASE=ZHUHAI EWPE INFORMATION TECHNOLOGY INC + +OUI:94FE22* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F823B2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DCD916* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:002552* + ID_OUI_FROM_DATABASE=VXi Corporation + +OUI:006CBC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:DC3752* + ID_OUI_FROM_DATABASE=GE + +OUI:B4D5BD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:7CB0C2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:98AA3C* + ID_OUI_FROM_DATABASE=Will i-tech Co., Ltd. + +OUI:449F7F* + ID_OUI_FROM_DATABASE=DataCore Software Corporation + +OUI:0011FC* + ID_OUI_FROM_DATABASE=HARTING Electronics GmbH + +OUI:5CDD70* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:24BF74* + ID_OUI_FROM_DATABASE=Private + +OUI:B8E779* + ID_OUI_FROM_DATABASE=9Solutions Oy + +OUI:240A11* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:C84544* + ID_OUI_FROM_DATABASE=Asia Pacific CIS (Wuxi) Co, Ltd + +OUI:E8A7F2* + ID_OUI_FROM_DATABASE=sTraffic + +OUI:D8209F* + ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH + +OUI:CC500A* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:A860B6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24F094* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90B0ED* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C4B301* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E05F45* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:483B38* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E47B3F* + ID_OUI_FROM_DATABASE=BEIJING CO-CLOUD TECHNOLOGY LTD. + +OUI:A0415E* + ID_OUI_FROM_DATABASE=Opsens Solution Inc. + +OUI:1C6E76* + ID_OUI_FROM_DATABASE=Quarion Technology Inc + +OUI:000AAB* + ID_OUI_FROM_DATABASE=Toyota Technical Development Corporation + +OUI:44D1FA* + ID_OUI_FROM_DATABASE=Shenzhen Yunlink Technology Co., Ltd + +OUI:08C021* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:48435A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9CE374* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:6C0EE6* + ID_OUI_FROM_DATABASE=Chengdu Xiyida Electronic Technology Co,.Ltd + +OUI:78FFCA* + ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED + +OUI:F03EBF* + ID_OUI_FROM_DATABASE=GOGORO TAIWAN LIMITED + +OUI:50AB3E* + ID_OUI_FROM_DATABASE=Qibixx AG + +OUI:A8BB50* + ID_OUI_FROM_DATABASE=WiZ IoT Company Limited + +OUI:005F86* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E46251* + ID_OUI_FROM_DATABASE=HAO CHENG GROUP LIMITED + +OUI:8850DD* + ID_OUI_FROM_DATABASE=Infiniband Trade Association + +OUI:DC7834* + ID_OUI_FROM_DATABASE=LOGICOM SA + +OUI:54F201* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A06090* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3876CA* + ID_OUI_FROM_DATABASE=Shenzhen Smart Intelligent Technology Co.Ltd + +OUI:D0577B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B824F0* + ID_OUI_FROM_DATABASE=SOYO Technology Development Co., Ltd. + +OUI:B456B9* + ID_OUI_FROM_DATABASE=Teraspek Technologies Co.,Ltd + +OUI:A009ED* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:68B35E* + ID_OUI_FROM_DATABASE=Shenzhen Neostra Technology Co.Ltd + +OUI:24E271* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:BC6010* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:AC3743* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:603197* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:0019CB* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:FCF528* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:588BF3* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:EC43F6* + ID_OUI_FROM_DATABASE=5420 + +OUI:D8B02E* + ID_OUI_FROM_DATABASE=Guangzhou Zonerich Business Machine Co., LTD. + +OUI:DC1AC5* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:849D64* + ID_OUI_FROM_DATABASE=SMC Corporation + +OUI:A020A6* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:88F7C7* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:08952A* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:C4BB4C* + ID_OUI_FROM_DATABASE=Zebra Information Tech Co. Ltd + +OUI:8C04FF* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:104FA8* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:6C25B9* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:486B2C* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:00001F* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:0016D3* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:001F16* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:BC307E* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:00C0AB* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:0010CA* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:0C2576* + ID_OUI_FROM_DATABASE=LONGCHEER TELECOMMUNICATION LIMITED + +OUI:0007A6* + ID_OUI_FROM_DATABASE=Leviton Manufacturing Co., Inc. + +OUI:208756* + ID_OUI_FROM_DATABASE=SIEMENS AG + +OUI:B08900* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:640DCE* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:00A085* + ID_OUI_FROM_DATABASE=Private + +OUI:ACDE48* + ID_OUI_FROM_DATABASE=Private + +OUI:6063F9* + ID_OUI_FROM_DATABASE=Ciholas, Inc. + +OUI:F0421C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C0E42D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:18D6C7* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:B8BB23* + ID_OUI_FROM_DATABASE=Guangdong Nufront CSC Co., Ltd + +OUI:EC26FB* + ID_OUI_FROM_DATABASE=TECC CO.,LTD. + +OUI:10683F* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A039F7* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:64BC0C* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:0090CC* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:E09DB8* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:64899A* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:58A2B5* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:74A722* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:001F6B* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:903AE6* + ID_OUI_FROM_DATABASE=PARROT SA + +OUI:00E00F* + ID_OUI_FROM_DATABASE=Shanghai Baud Data Communication Co.,Ltd. + +OUI:3C404F* + ID_OUI_FROM_DATABASE=GUANGDONG PISEN ELECTRONICS CO.,LTD + +OUI:00233E* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:6CBEE9* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:0080F7* + ID_OUI_FROM_DATABASE=Zenith Electronics Corporation + +OUI:00C095* + ID_OUI_FROM_DATABASE=ZNYX Networks, Inc. + +OUI:60EB69* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:C80AA9* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:00238B* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:0007BA* + ID_OUI_FROM_DATABASE=UTStarcom Inc + +OUI:4439C4* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:70F395* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:001E37* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:002713* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:002186* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:8CFDF0* + ID_OUI_FROM_DATABASE=Qualcomm Inc. + +OUI:000031* + ID_OUI_FROM_DATABASE=QPSX COMMUNICATIONS, LTD. + +OUI:000E7B* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:B86B23* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:000C29* + ID_OUI_FROM_DATABASE=VMware, Inc. + +OUI:005056* + ID_OUI_FROM_DATABASE=VMware, Inc. + +OUI:001C4D* + ID_OUI_FROM_DATABASE=Aplix IP Holdings Corporation + +OUI:D0052A* + ID_OUI_FROM_DATABASE=Arcadyan Corporation + +OUI:F485C6* + ID_OUI_FROM_DATABASE=FDT Technologies + +OUI:BC60A7* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:08D833* + ID_OUI_FROM_DATABASE=Shenzhen RF Technology Co., Ltd + +OUI:94D469* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:385610* + ID_OUI_FROM_DATABASE=CANDY HOUSE, Inc. + +OUI:20F543* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + +OUI:685388* + ID_OUI_FROM_DATABASE=P&S Technology + +OUI:54A619* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:1880F5* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:24DBED* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:AC3613* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1449E0* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:C0BDD1* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:E8508B* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:F025B7* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:C8BA94* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:EC1F72* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:9852B1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1489FD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:CCFE3C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:789ED0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E440E2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1CAF05* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E492FB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:247F20* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:0073E0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC4486* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:380B40* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8C0D76* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:005A13* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:002490* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0023D7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FCA13E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A00798* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:945103* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C819F7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:2C4401* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08C6B3* + ID_OUI_FROM_DATABASE=QTECH LLC + +OUI:64DAA0* + ID_OUI_FROM_DATABASE=Robert Bosch Smart Home GmbH + +OUI:14B837* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:8056F2* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:70188B* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:3C77E6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0C84DC* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:844BF5* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:E006E6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:60F494* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:A41731* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C0143D* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:642737* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:60D819* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:6474F6* + ID_OUI_FROM_DATABASE=Shooter Detection Systems + +OUI:604BAA* + ID_OUI_FROM_DATABASE=Private + +OUI:CC7314* + ID_OUI_FROM_DATABASE=HONG KONG WHEATEK TECHNOLOGY LIMITED + +OUI:C0CB38* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:98E7F4* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D42C44* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D842E2* + ID_OUI_FROM_DATABASE=Canary Connect, Inc. + +OUI:500959* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:143365* + ID_OUI_FROM_DATABASE=TEM Mobile Limited + +OUI:205D47* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:C0F945* + ID_OUI_FROM_DATABASE=Toshiba Toko Meter Systems Co., LTD. + +OUI:ACAB2E* + ID_OUI_FROM_DATABASE=Beijing LasNubes Technology Co., Ltd. + +OUI:10E878* + ID_OUI_FROM_DATABASE=Nokia + +OUI:48F7F1* + ID_OUI_FROM_DATABASE=Nokia + +OUI:4CC94F* + ID_OUI_FROM_DATABASE=Nokia + +OUI:1CEA1B* + ID_OUI_FROM_DATABASE=Nokia + +OUI:B4F81E* + ID_OUI_FROM_DATABASE=Kinova + +OUI:A46011* + ID_OUI_FROM_DATABASE=VeriFone Inc. + +OUI:28CA09* + ID_OUI_FROM_DATABASE=ThyssenKrupp Elevators (Shanghai) Co.,Ltd + +OUI:E0B94D* + ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD + +OUI:D8380D* + ID_OUI_FROM_DATABASE=SHENZHEN IP-COM Network Co.,Ltd + +OUI:A4C64F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C83DD4* + ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. + +OUI:487B6B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3087D9* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:A8E705* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:9C62AB* + ID_OUI_FROM_DATABASE=Sumavision Technologies Co.,Ltd + +OUI:487A55* + ID_OUI_FROM_DATABASE=ALE International + +OUI:000435* + ID_OUI_FROM_DATABASE=InfiNet LLC + +OUI:BC39D9* + ID_OUI_FROM_DATABASE=Z-TEC + +OUI:88E87F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B853AC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B04BBF* + ID_OUI_FROM_DATABASE=PT HAN SUNG ELECTORONICS INDONESIA + +OUI:0060D6* + ID_OUI_FROM_DATABASE=NovAtel Inc. + +OUI:2C3361* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:78B84B* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:40F420* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:9C6121* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:8C8ABB* + ID_OUI_FROM_DATABASE=Beijing Orient View Technology Co., Ltd. + +OUI:88366C* + ID_OUI_FROM_DATABASE=EFM Networks + +OUI:F074E4* + ID_OUI_FROM_DATABASE=Thundercomm Technology Co., Ltd + +OUI:A0722C* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:FCECDA* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:E07C13* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:58696C* + ID_OUI_FROM_DATABASE=Ruijie Networks Co.,LTD. + +OUI:001972* + ID_OUI_FROM_DATABASE=Plexus (Xiamen) Co.,ltd. + +OUI:6488FF* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:005979* + ID_OUI_FROM_DATABASE=Networked Energy Services + +OUI:000997* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000E62* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000EC0* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000FCD* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0004DC* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:02E6D3* + ID_OUI_FROM_DATABASE=NIXDORF COMPUTER CORP. + +OUI:0016B9* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:0024A8* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:CC3ADF* + ID_OUI_FROM_DATABASE=Private + +OUI:141F78* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:006F64* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:DC6672* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0025C3* + ID_OUI_FROM_DATABASE=21168 + +OUI:001365* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001ECA* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001D42* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001CEB* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002363* + ID_OUI_FROM_DATABASE=Zhuhai Raysharp Technology Co.,Ltd + +OUI:D03742* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:001CFD* + ID_OUI_FROM_DATABASE=Universal Electronics, Inc. + +OUI:080051* + ID_OUI_FROM_DATABASE=ExperData + +OUI:0080C7* + ID_OUI_FROM_DATABASE=XIRCOM + +OUI:049FCA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C81FBE* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:203DB2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:48D539* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:10E68F* + ID_OUI_FROM_DATABASE=KWANGSUNG ELECTRONICS KOREA CO.,LTD. + +OUI:1899F5* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:E41D2D* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:B80018* + ID_OUI_FROM_DATABASE=Htel + +OUI:0081C4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E8FD90* + ID_OUI_FROM_DATABASE=Turbostor + +OUI:0017EA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E3* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001834* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:00182F* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:78DEE4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B8FFFE* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:E0D7BA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:405FC2* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:8030DC* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:CC78AB* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A4D578* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:544A16* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D8DDFD* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:20CD39* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:987BF3* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:247189* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:EC1127* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:F0C77F* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:F45EAB* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001783* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A81B6A* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:9884E3* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:38D269* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C8FD19* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:508CB1* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:04BBF9* + ID_OUI_FROM_DATABASE=Pavilion Data Systems Inc + +OUI:B0F893* + ID_OUI_FROM_DATABASE=Shanghai MXCHIP Information Technology Co., Ltd. + +OUI:00C017* + ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + +OUI:D49B5C* + ID_OUI_FROM_DATABASE=Chongqing Miedu Technology Co., Ltd. + +OUI:EC8CA2* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:C411E0* + ID_OUI_FROM_DATABASE=Bull Group Co., Ltd + +OUI:90842B* + ID_OUI_FROM_DATABASE=LEGO System A/S + +OUI:84C7EA* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:8C6102* + ID_OUI_FROM_DATABASE=Beijing Baofengmojing Technologies Co., Ltd + +OUI:1005B1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:10868C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:1C1B68* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:44E137* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E83381* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:8461A0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0CF893* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:14ABF0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:ACB313* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0026D9* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:28C87A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:54E2E0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A055DE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A0C562* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:FC6FB7* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001A1B* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00149A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001371* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DBE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001E5A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001D6B* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001CC1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001C11* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001F7E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002495* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:2C9E5F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:C8AA21* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:341FE4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:400D10* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001596* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015A2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001311* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015CE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002040* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0011AE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:000F9F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:306023* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD6* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:601971* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0000CA* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001ADB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002375* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0024A1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A4ED4E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002642* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:000B06* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00152F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00111A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001626* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0018A4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00D037* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:FC9114* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:1C25E1* + ID_OUI_FROM_DATABASE=China Mobile IOT Company Limited + +OUI:C0F636* + ID_OUI_FROM_DATABASE=Hangzhou Kuaiyue Technologies, Ltd. + +OUI:F0038C* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:B45D50* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:742344* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:001E7D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3C6200* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0024E9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002399* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E4E0C5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E8039A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C4731E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78D6F0* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:B407F9* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:40B89A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:A8A795* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:8096CA* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:9CD21E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:D87988* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00242B* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00242C* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:945330* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:EC0EC4* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:7429AF* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:346895* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:A86BAD* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:D80F99* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:78DD08* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00197E* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:A0AB1B* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:5C4979* + ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH + +OUI:086A0A* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:101250* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:8C7712* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:2013E0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0007AB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0021D2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC4760* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D0176A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F0D9B2* + ID_OUI_FROM_DATABASE=EXO S.A. + +OUI:2CBABA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:24920E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:40D3AE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:802AA8* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:00156D* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:787D48* + ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED + +OUI:D46E0E* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:049790* + ID_OUI_FROM_DATABASE=Lartech telecom LLC + +OUI:8CEA1B* + ID_OUI_FROM_DATABASE=Edgecore Networks Corporation + +OUI:001650* + ID_OUI_FROM_DATABASE=Kratos EPD + +OUI:58E16C* + ID_OUI_FROM_DATABASE=Ying Hua Information Technology (Shanghai)Co., LTD + +OUI:5846E1* + ID_OUI_FROM_DATABASE=Baxter International Inc + +OUI:00D0BD* + ID_OUI_FROM_DATABASE=Lattice Semiconductor Corp. (LPA) + +OUI:F08261* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:D084B0* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:00FEC8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0030C5* + ID_OUI_FROM_DATABASE=CADENCE DESIGN SYSTEMS, INC. + +OUI:EC2280* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:047863* + ID_OUI_FROM_DATABASE=Shanghai MXCHIP Information Technology Co., Ltd. + +OUI:24BA13* + ID_OUI_FROM_DATABASE=RISO KAGAKU CORPORATION + +OUI:24DA11* + ID_OUI_FROM_DATABASE=NO NDA Inc + +OUI:70CA4D* + ID_OUI_FROM_DATABASE=Shenzhen lnovance Technology Co.,Ltd. + +OUI:DCC0EB* + ID_OUI_FROM_DATABASE=ASSA ABLOY CÔTE PICARDE + +OUI:001735* + ID_OUI_FROM_DATABASE=Intel Wireless Network Group + +OUI:9CDFB1* + ID_OUI_FROM_DATABASE=Shenzhen Crave Communication Co., LTD + +OUI:5CF938* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3871DE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC5436* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0CC731* + ID_OUI_FROM_DATABASE=Currant, Inc. + +OUI:00142F* + ID_OUI_FROM_DATABASE=Savvius + +OUI:2CDDA3* + ID_OUI_FROM_DATABASE=Point Grey Research Inc. + +OUI:24FD5B* + ID_OUI_FROM_DATABASE=SmartThings, Inc. + +OUI:2876CD* + ID_OUI_FROM_DATABASE=Funshion Online Technologies Co.,Ltd + +OUI:F4F5D8* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:F4F5E8* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:F88FCA* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:BCD1D3* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:BC4434* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:0041D2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4CFB45* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A4BA76* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:78E3B5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:984BE1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:68B599* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0C47C9* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:14D64D* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:C8BE19* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:BCF685* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:CCB255* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:84C9B2* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:DCD321* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:CC4EEC* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:DC330D* + ID_OUI_FROM_DATABASE=Qingdao Haier Telecom Co.,Ltd + +OUI:0080E1* + ID_OUI_FROM_DATABASE=STMicroelectronics SRL + +OUI:58DC6D* + ID_OUI_FROM_DATABASE=Exceptional Innovation, Inc. + +OUI:00092D* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:F8DB7F* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:E899C4* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:7CB15D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:18686A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0C0535* + ID_OUI_FROM_DATABASE=Juniper Systems + +OUI:8CF228* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:08EA44* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:78F882* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:8851FB* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:AC162D* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A0B3CC* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:E4115B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:C8CBB8* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:9457A5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0001E7* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:080009* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0080A0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D48564* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3C4A92* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:780AC7* + ID_OUI_FROM_DATABASE=Baofeng TV Co., Ltd. + +OUI:001D73* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:001601* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:106F3F* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:8857EE* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:009C02* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:78E7D1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001B78* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001E0B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C6E85* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00D0B7* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:0002B3* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:001111* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:001320* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0012F0* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:9049FA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C8348E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00508B* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:784859* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:1458D0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:5065F3* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A0481C* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:A01D48* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:94B2CC* + ID_OUI_FROM_DATABASE=PIONEER CORPORATION + +OUI:887F03* + ID_OUI_FROM_DATABASE=Comper Technology Investment Limited + +OUI:E06066* + ID_OUI_FROM_DATABASE=Sercomm Corporation + +OUI:0019E0* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:0023CD* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:002719* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:40169F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:940C6D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:74EA3A* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:90F652* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:10FEED* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:C46E1F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:50FA84* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F483CD* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:882593* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:808917* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:5C899A* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A81B5A* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:E422A5* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:1C994C* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:F02765* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:D4970B* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:F48B32* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:20A783* + ID_OUI_FROM_DATABASE=miControl GmbH + +OUI:005053* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00500F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:048A15* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:44322A* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:FC8399* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:00040D* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:D842AC* + ID_OUI_FROM_DATABASE=Shanghai Feixun Communication Co.,Ltd. + +OUI:34CDBE* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D46AA8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:5439DF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4846FB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:200BC7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:104780* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:88308A* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:44A7CF* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:0013E0* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:748EF8* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:00E052* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:000480* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:000088* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:344B50* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:FCC897* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:9CD24B* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C864C7* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:D0154A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:88E3AB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00664B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:68A0F6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:5CF96A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B43052* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:88CEFA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:582AF7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F48E92* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:40CBA8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:087A4C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D46E5C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2469A5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C8D15E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F83DFF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:308730* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:002568* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D47856* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:C057BC* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:38BB3C* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:E45D52* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:A4251B* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:6CA849* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:30D17E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9C28EF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:7C6097* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:60DE44* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3400A3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:643E8C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:00C610* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70DEE2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:182032* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6CC26B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1040F3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:FC253F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:183451* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C0847A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:64200C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:74E1B6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C771A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00F4B9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C8334B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B8F6B1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C09F42* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:189EFC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C3E6D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0016FE* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:0498F3* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:38C096* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:E0750A* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:B05947* + ID_OUI_FROM_DATABASE=Shenzhen Qihu Intelligent Technology Company Limited + +OUI:00E04F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001011* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0010F6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:80E01D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:80E86F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E4AA5D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000389* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:0CE0E4* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:B0AA77* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:78BAF9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0016B6* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:0018F8* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:00252E* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:A4A24A* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:602AD0* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:001BFB* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:00E08F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:203A07* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34A84E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E4D3F1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1CE6C7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E02F6D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:8478AC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4403A7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6886A7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B4E9B0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000832* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B0FAEB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:500604* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:70105C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7CFADF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:101C0C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001124* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001D4F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001E52* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001F5B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001FF3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0021E9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00236C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002500* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60FB42* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:14DAE9* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:3C08F6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D072DC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:28C7CE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6CFA89* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:58F39C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:346288* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:881DFC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F81EDF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90840D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D8A25E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C8BCC8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:28E7CF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D89E3F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:040CCE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A4D1D2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:406C8F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C067AF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:64E950* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:189C5D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000EA6* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0013D4* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:002618* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:00248C* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0050A2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0050F0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00905F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00902B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00100B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00100D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001014* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:649ABE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:94E96A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:AC293A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:10417F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7014A6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A8667F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D02598* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CC29F5* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C709F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C3E9F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34E2FD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:609217* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8863DF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80E650* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:006171* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90FD61* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5C97F3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C4008* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24A074* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F02475* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:20A2E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5CF5DA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D4B8FF* + ID_OUI_FROM_DATABASE=Home Control Singapore Pte Ltd + +OUI:28E14C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:54E43A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C8E0EB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A88808* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:907240* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C4DE9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D89695* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0C3021* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0F61C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B03495* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:848E0C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C2DAA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:444C0C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:84FCFE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E48B7F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5C969D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A8FAD8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:949426* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0F5C6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:AC6462* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C08488* + ID_OUI_FROM_DATABASE=Finis Inc + +OUI:68E8EB* + ID_OUI_FROM_DATABASE=Linktel Technologies Co.,Ltd + +OUI:20C3A4* + ID_OUI_FROM_DATABASE=RetailNext + +OUI:780541* + ID_OUI_FROM_DATABASE=Queclink Wireless Solutions Co., Ltd + +OUI:C02DEE* + ID_OUI_FROM_DATABASE=Cuff + +OUI:54A3FA* + ID_OUI_FROM_DATABASE=BQT Solutions (Australia)Pty Ltd + +OUI:9023EC* + ID_OUI_FROM_DATABASE=Availink, Inc. + +OUI:7467F7* + ID_OUI_FROM_DATABASE=Zebra Technologoes + +OUI:3891D5* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:90DFFB* + ID_OUI_FROM_DATABASE=HOMERIDER SYSTEMS + +OUI:3C831E* + ID_OUI_FROM_DATABASE=CKD Corporation + +OUI:381C23* + ID_OUI_FROM_DATABASE=Hilan Technology CO.,LTD + +OUI:E03676* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3CB72B* + ID_OUI_FROM_DATABASE=PLUMgrid Inc + +OUI:243184* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:24DA9B* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:3052CB* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:B8B2EB* + ID_OUI_FROM_DATABASE=Googol Technology (HK) Limited + +OUI:C40049* + ID_OUI_FROM_DATABASE=Kamama + +OUI:50A9DE* + ID_OUI_FROM_DATABASE=Smartcom - Bulgaria AD + +OUI:8809AF* + ID_OUI_FROM_DATABASE=Masimo Corp. + +OUI:E8DED6* + ID_OUI_FROM_DATABASE=Intrising Networks, Inc. + +OUI:B844D9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC2B2A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C10D4* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:089B4B* + ID_OUI_FROM_DATABASE=iKuai Networks + +OUI:3C7873* + ID_OUI_FROM_DATABASE=Airsonics + +OUI:BC5FF6* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:C8F9C8* + ID_OUI_FROM_DATABASE=NewSharp Technology(SuZhou)Co,Ltd + +OUI:3C5CC3* + ID_OUI_FROM_DATABASE=Shenzhen First Blue Chip Technology Ltd + +OUI:A8741D* + ID_OUI_FROM_DATABASE=PHOENIX CONTACT Electronics GmbH + +OUI:A4C138* + ID_OUI_FROM_DATABASE=Telink Semiconductor (Taipei) Co. Ltd. + +OUI:D8EFCD* + ID_OUI_FROM_DATABASE=Nokia + +OUI:EC0133* + ID_OUI_FROM_DATABASE=TRINUS SYSTEMS INC. + +OUI:1C56FE* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:7CA23E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:501AA5* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:F09A51* + ID_OUI_FROM_DATABASE=Shanghai Viroyal Electronic Technology Company Limited + +OUI:9870E8* + ID_OUI_FROM_DATABASE=INNATECH SDN BHD + +OUI:50DF95* + ID_OUI_FROM_DATABASE=Lytx + +OUI:584925* + ID_OUI_FROM_DATABASE=E3 Enterprise + +OUI:94F278* + ID_OUI_FROM_DATABASE=Elma Electronic + +OUI:E8BDD1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3481F4* + ID_OUI_FROM_DATABASE=SST Taiwan Ltd. + +OUI:F4B8A7* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:58F102* + ID_OUI_FROM_DATABASE=BLU Products Inc. + +OUI:B869C2* + ID_OUI_FROM_DATABASE=Sunitec Enterprise Co., Ltd. + +OUI:2CC548* + ID_OUI_FROM_DATABASE=IAdea Corporation + +OUI:307CB2* + ID_OUI_FROM_DATABASE=ANOV FRANCE + +OUI:90D8F3* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:444CA8* + ID_OUI_FROM_DATABASE=Arista Networks + +OUI:FCE33C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:BC6A2F* + ID_OUI_FROM_DATABASE=Henge Docks LLC + +OUI:E4907E* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:48066A* + ID_OUI_FROM_DATABASE=Tempered Networks, Inc. + +OUI:1CF03E* + ID_OUI_FROM_DATABASE=Wearhaus Inc. + +OUI:DCDB70* + ID_OUI_FROM_DATABASE=Tonfunk Systementwicklung und Service GmbH + +OUI:C47D46* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:68EDA4* + ID_OUI_FROM_DATABASE=Shenzhen Seavo Technology Co.,Ltd + +OUI:B899B0* + ID_OUI_FROM_DATABASE=Cohere Technologies + +OUI:2CC5D3* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:80C5E6* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:D85DEF* + ID_OUI_FROM_DATABASE=Busch-Jaeger Elektro GmbH + +OUI:10DF8B* + ID_OUI_FROM_DATABASE=Shenzhen CareDear Communication Technology Co.,Ltd + +OUI:00A784* + ID_OUI_FROM_DATABASE=ITX security + +OUI:800184* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:38FACA* + ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd + +OUI:44C69B* + ID_OUI_FROM_DATABASE=Wuhan Feng Tian Information Network CO.,LTD + +OUI:C02567* + ID_OUI_FROM_DATABASE=Nexxt Solutions + +OUI:B46D35* + ID_OUI_FROM_DATABASE=Dalian Seasky Automation Co;Ltd + +OUI:B89ACD* + ID_OUI_FROM_DATABASE=ELITE OPTOELECTRONIC(ASIA)CO.,LTD + +OUI:241C04* + ID_OUI_FROM_DATABASE=SHENZHEN JEHE TECHNOLOGY DEVELOPMENT CO., LTD. + +OUI:F8CFC5* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:BCF811* + ID_OUI_FROM_DATABASE=Xiamen DNAKE Technology Co.,Ltd + +OUI:A8827F* + ID_OUI_FROM_DATABASE=CIBN Oriental Network(Beijing) CO.,Ltd + +OUI:609C9F* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:900A39* + ID_OUI_FROM_DATABASE=Wiio, Inc. + +OUI:C4693E* + ID_OUI_FROM_DATABASE=Turbulence Design Inc. + +OUI:1C8341* + ID_OUI_FROM_DATABASE=Hefei Bitland Information Technology Co.Ltd + +OUI:4011DC* + ID_OUI_FROM_DATABASE=Sonance + +OUI:249EAB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:DC56E6* + ID_OUI_FROM_DATABASE=Shenzhen Bococom Technology Co.,LTD + +OUI:5CA178* + ID_OUI_FROM_DATABASE=TableTop Media (dba Ziosk) + +OUI:702A7D* + ID_OUI_FROM_DATABASE=EpSpot AB + +OUI:B8B3DC* + ID_OUI_FROM_DATABASE=DEREK (SHAOGUAN) LIMITED + +OUI:6C1E70* + ID_OUI_FROM_DATABASE=Guangzhou YBDS IT Co.,Ltd + +OUI:C8E130* + ID_OUI_FROM_DATABASE=Milkyway Group Ltd + +OUI:8833BE* + ID_OUI_FROM_DATABASE=Ivenix, Inc. + +OUI:34CC28* + ID_OUI_FROM_DATABASE=Nexpring Co. LTD., + +OUI:144146* + ID_OUI_FROM_DATABASE=Honeywell (China) Co., LTD + +OUI:F41563* + ID_OUI_FROM_DATABASE=F5 Networks, Inc. + +OUI:C4EA1D* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:20E407* + ID_OUI_FROM_DATABASE=Spark srl + +OUI:887384* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:584704* + ID_OUI_FROM_DATABASE=Shenzhen Webridge Technology Co.,Ltd + +OUI:749CE3* + ID_OUI_FROM_DATABASE=Art2Wave Canada Inc. + +OUI:B856BD* + ID_OUI_FROM_DATABASE=ITT LLC + +OUI:107873* + ID_OUI_FROM_DATABASE=Shenzhen Jinkeyi Communication Co., Ltd. + +OUI:D45556* + ID_OUI_FROM_DATABASE=Fiber Mountain Inc. + +OUI:F01E34* + ID_OUI_FROM_DATABASE=ORICO Technologies Co., Ltd + +OUI:74A063* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A89008* + ID_OUI_FROM_DATABASE=Beijing Yuecheng Technology Co. Ltd. + +OUI:183864* + ID_OUI_FROM_DATABASE=CAP-TECH INTERNATIONAL CO., LTD. + +OUI:08D34B* + ID_OUI_FROM_DATABASE=Techman Electronics (Changshu) Co., Ltd. + +OUI:C808E9* + ID_OUI_FROM_DATABASE=LG Electronics + +OUI:78ACBF* + ID_OUI_FROM_DATABASE=Igneous Systems + +OUI:206274* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:5CCCFF* + ID_OUI_FROM_DATABASE=Techroutes Network Pvt Ltd + +OUI:844BB7* + ID_OUI_FROM_DATABASE=Beijing Sankuai Online Technology Co.,Ltd + +OUI:148F21* + ID_OUI_FROM_DATABASE=Garmin International + +OUI:3C6A9D* + ID_OUI_FROM_DATABASE=Dexatek Technology LTD. + +OUI:14893E* + ID_OUI_FROM_DATABASE=VIXTEL TECHNOLOGIES LIMTED + +OUI:60F189* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:74A34A* + ID_OUI_FROM_DATABASE=ZIMI CORPORATION + +OUI:98F5A9* + ID_OUI_FROM_DATABASE=OHSUNG ELECTRONICS CO.,LTD. + +OUI:D89341* + ID_OUI_FROM_DATABASE=General Electric Global Research + +OUI:F4645D* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:30D587* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1436C6* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:04C09C* + ID_OUI_FROM_DATABASE=Tellabs Inc. + +OUI:844464* + ID_OUI_FROM_DATABASE=ServerU Inc + +OUI:589B0B* + ID_OUI_FROM_DATABASE=Shineway Technologies, Inc. + +OUI:A48CDB* + ID_OUI_FROM_DATABASE=Lenovo + +OUI:4062B6* + ID_OUI_FROM_DATABASE=Tele system communication + +OUI:3C2C94* + ID_OUI_FROM_DATABASE=杭州德澜科技有限公司(HangZhou Delan Technology Co.,Ltd) + +OUI:78312B* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C035C5* + ID_OUI_FROM_DATABASE=Prosoft Systems LTD + +OUI:F8B2F3* + ID_OUI_FROM_DATABASE=GUANGZHOU BOSMA TECHNOLOGY CO.,LTD + +OUI:1C7D22* + ID_OUI_FROM_DATABASE=Fuji Xerox Co., Ltd. + +OUI:7C11CD* + ID_OUI_FROM_DATABASE=QianTang Technology + +OUI:0492EE* + ID_OUI_FROM_DATABASE=iway AG + +OUI:F02A23* + ID_OUI_FROM_DATABASE=Creative Next Design + +OUI:8C9109* + ID_OUI_FROM_DATABASE=Toyoshima Electric Technoeogy(Suzhou) Co.,Ltd. + +OUI:307350* + ID_OUI_FROM_DATABASE=Inpeco SA + +OUI:E8CC18* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:B09137* + ID_OUI_FROM_DATABASE=ISis ImageStream Internet Solutions, Inc + +OUI:3C1E13* + ID_OUI_FROM_DATABASE=HANGZHOU SUNRISE TECHNOLOGY CO., LTD + +OUI:B4A828* + ID_OUI_FROM_DATABASE=Shenzhen Concox Information Technology Co., Ltd + +OUI:A41242* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:404EEB* + ID_OUI_FROM_DATABASE=Higher Way Electronic Co., Ltd. + +OUI:50BD5F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:147590* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:ECB907* + ID_OUI_FROM_DATABASE=CloudGenix Inc + +OUI:5CF9F0* + ID_OUI_FROM_DATABASE=Atomos Engineering P/L + +OUI:FCDBB3* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:B8186F* + ID_OUI_FROM_DATABASE=ORIENTAL MOTOR CO., LTD. + +OUI:1C9C26* + ID_OUI_FROM_DATABASE=Zoovel Technologies + +OUI:9C3583* + ID_OUI_FROM_DATABASE=Nipro Diagnostics, Inc + +OUI:C456FE* + ID_OUI_FROM_DATABASE=Lava International Ltd. + +OUI:B89BE4* + ID_OUI_FROM_DATABASE=ABB Power Systems Power Generation + +OUI:C0EEFB* + ID_OUI_FROM_DATABASE=OnePlus Tech (Shenzhen) Ltd + +OUI:108A1B* + ID_OUI_FROM_DATABASE=RAONIX Inc. + +OUI:8CF813* + ID_OUI_FROM_DATABASE=ORANGE POLSKA + +OUI:B8F317* + ID_OUI_FROM_DATABASE=iSun Smasher Communications Private Limited + +OUI:2442BC* + ID_OUI_FROM_DATABASE=Alinco,incorporated + +OUI:C401CE* + ID_OUI_FROM_DATABASE=PRESITION (2000) CO., LTD. + +OUI:D01242* + ID_OUI_FROM_DATABASE=BIOS Corporation + +OUI:50F43C* + ID_OUI_FROM_DATABASE=Leeo Inc + +OUI:B43934* + ID_OUI_FROM_DATABASE=Pen Generations, Inc. + +OUI:DCC622* + ID_OUI_FROM_DATABASE=BUHEUNG SYSTEM + +OUI:5C2BF5* + ID_OUI_FROM_DATABASE=Vivint + +OUI:D062A0* + ID_OUI_FROM_DATABASE=China Essence Technology (Zhumadian) Co., Ltd. + +OUI:CC10A3* + ID_OUI_FROM_DATABASE=Beijing Nan Bao Technology Co., Ltd. + +OUI:2CA30E* + ID_OUI_FROM_DATABASE=POWER DRAGON DEVELOPMENT LIMITED + +OUI:4CF5A0* + ID_OUI_FROM_DATABASE=Scalable Network Technologies Inc + +OUI:084656* + ID_OUI_FROM_DATABASE=VEO-LABS + +OUI:4488CB* + ID_OUI_FROM_DATABASE=Camco Technologies NV + +OUI:5014B5* + ID_OUI_FROM_DATABASE=Richfit Information Technology Co., Ltd + +OUI:CC3080* + ID_OUI_FROM_DATABASE=VAIO Corporation + +OUI:F82441* + ID_OUI_FROM_DATABASE=Yeelink + +OUI:6CBFB5* + ID_OUI_FROM_DATABASE=Noon Technology Co., Ltd + +OUI:489D18* + ID_OUI_FROM_DATABASE=Flashbay Limited + +OUI:8CB094* + ID_OUI_FROM_DATABASE=Airtech I&C Co., Ltd + +OUI:70F196* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:6C6EFE* + ID_OUI_FROM_DATABASE=Core Logic Inc. + +OUI:E4C62B* + ID_OUI_FROM_DATABASE=Airware + +OUI:80F8EB* + ID_OUI_FROM_DATABASE=RayTight + +OUI:94B40F* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:4C2C83* + ID_OUI_FROM_DATABASE=Zhejiang KaNong Network Technology Co.,Ltd. + +OUI:BCC342* + ID_OUI_FROM_DATABASE=Panasonic System Networks Co., Ltd. + +OUI:E89606* + ID_OUI_FROM_DATABASE=testo Instruments (Shenzhen) Co., Ltd. + +OUI:CC3F1D* + ID_OUI_FROM_DATABASE=Intesis Software SL + +OUI:902181* + ID_OUI_FROM_DATABASE=Shanghai Huaqin Telecom Technology Co.,Ltd + +OUI:600417* + ID_OUI_FROM_DATABASE=POSBANK CO.,LTD + +OUI:A44AD3* + ID_OUI_FROM_DATABASE=ST Electronics(Shanghai) Co.,Ltd + +OUI:2497ED* + ID_OUI_FROM_DATABASE=Techvision Intelligent Technology Limited + +OUI:104E07* + ID_OUI_FROM_DATABASE=Shanghai Genvision Industries Co.,Ltd + +OUI:4C11BF* + ID_OUI_FROM_DATABASE=ZHEJIANG DAHUA TECHNOLOGY CO.,LTD. + +OUI:FCD5D9* + ID_OUI_FROM_DATABASE=Shenzhen SDMC Technology Co., Ltd. + +OUI:007532* + ID_OUI_FROM_DATABASE=INID BV + +OUI:A002DC* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:907EBA* + ID_OUI_FROM_DATABASE=UTEK TECHNOLOGY (SHENZHEN) CO.,LTD + +OUI:488244* + ID_OUI_FROM_DATABASE=Life Fitness / Div. of Brunswick + +OUI:A8F7E0* + ID_OUI_FROM_DATABASE=PLANET Technology Corporation + +OUI:2C5BE1* + ID_OUI_FROM_DATABASE=Centripetal Networks, Inc + +OUI:D87EB1* + ID_OUI_FROM_DATABASE=x.o.ware, inc. + +OUI:4045DA* + ID_OUI_FROM_DATABASE=Spreadtrum Communications (Shanghai) Co., Ltd. + +OUI:98BE94* + ID_OUI_FROM_DATABASE=IBM + +OUI:D4B43E* + ID_OUI_FROM_DATABASE=Messcomp Datentechnik GmbH + +OUI:A8E539* + ID_OUI_FROM_DATABASE=Moimstone Co.,Ltd + +OUI:98F170* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:04C991* + ID_OUI_FROM_DATABASE=Phistek INC. + +OUI:581F67* + ID_OUI_FROM_DATABASE=Open-m technology limited + +OUI:BC25F0* + ID_OUI_FROM_DATABASE=3D Display Technologies Co., Ltd. + +OUI:7CE524* + ID_OUI_FROM_DATABASE=Quirky, Inc. + +OUI:D85DFB* + ID_OUI_FROM_DATABASE=Private + +OUI:7CC4EF* + ID_OUI_FROM_DATABASE=Devialet + +OUI:94AEE3* + ID_OUI_FROM_DATABASE=Belden Hirschmann Industries (Suzhou) Ltd. + +OUI:44666E* + ID_OUI_FROM_DATABASE=IP-LINE + +OUI:705B2E* + ID_OUI_FROM_DATABASE=M2Communication Inc. + +OUI:0C8C8F* + ID_OUI_FROM_DATABASE=Kamo Technology Limited + +OUI:F4FD2B* + ID_OUI_FROM_DATABASE=ZOYI Company + +OUI:FCAA14* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:50FEF2* + ID_OUI_FROM_DATABASE=Sify Technologies Ltd + +OUI:3CD9CE* + ID_OUI_FROM_DATABASE=Eclipse WiFi + +OUI:C80210* + ID_OUI_FROM_DATABASE=LG Innotek + +OUI:702DD1* + ID_OUI_FROM_DATABASE=Newings Communication CO., LTD. + +OUI:44746C* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:F4F646* + ID_OUI_FROM_DATABASE=Dediprog Technology Co. Ltd. + +OUI:ECD9D1* + ID_OUI_FROM_DATABASE=Shenzhen TG-NET Botone Technology Co.,Ltd. + +OUI:748F4D* + ID_OUI_FROM_DATABASE=MEN Mikro Elektronik GmbH + +OUI:A47E39* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0C63FC* + ID_OUI_FROM_DATABASE=Nanjing Signway Technology Co., Ltd + +OUI:ACA9A0* + ID_OUI_FROM_DATABASE=Audioengine, Ltd. + +OUI:A8A668* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:60E327* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:E4D332* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A0DA92* + ID_OUI_FROM_DATABASE=Nanjing Glarun Atten Technology Co. Ltd. + +OUI:6828BA* + ID_OUI_FROM_DATABASE=Dejai + +OUI:48D18E* + ID_OUI_FROM_DATABASE=Metis Communication Co.,Ltd + +OUI:A49F85* + ID_OUI_FROM_DATABASE=Lyve Minds, Inc + +OUI:7CD30A* + ID_OUI_FROM_DATABASE=INVENTEC Corporation + +OUI:3481C4* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:885BDD* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:085700* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:888914* + ID_OUI_FROM_DATABASE=All Components Incorporated + +OUI:D8150D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A06518* + ID_OUI_FROM_DATABASE=VNPT TECHNOLOGY + +OUI:748F1B* + ID_OUI_FROM_DATABASE=MasterImage 3D + +OUI:F03A4B* + ID_OUI_FROM_DATABASE=Bloombase, Inc. + +OUI:D82A15* + ID_OUI_FROM_DATABASE=Leitner SpA + +OUI:C4291D* + ID_OUI_FROM_DATABASE=KLEMSAN ELEKTRIK ELEKTRONIK SAN.VE TIC.AS. + +OUI:704E01* + ID_OUI_FROM_DATABASE=KWANGWON TECH CO., LTD. + +OUI:848433* + ID_OUI_FROM_DATABASE=Paradox Engineering SA + +OUI:D4319D* + ID_OUI_FROM_DATABASE=Sinwatec + +OUI:DC052F* + ID_OUI_FROM_DATABASE=National Products Inc. + +OUI:CC398C* + ID_OUI_FROM_DATABASE=Shiningtek + +OUI:6C5F1C* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:B42C92* + ID_OUI_FROM_DATABASE=Zhejiang Weirong Electronic Co., Ltd + +OUI:FC1349* + ID_OUI_FROM_DATABASE=Global Apps Corp. + +OUI:8C41F2* + ID_OUI_FROM_DATABASE=RDA Technologies Ltd. + +OUI:FC07A0* + ID_OUI_FROM_DATABASE=LRE Medical GmbH + +OUI:AC02CA* + ID_OUI_FROM_DATABASE=HI Solutions, Inc. + +OUI:F490CA* + ID_OUI_FROM_DATABASE=Tensorcom + +OUI:2C534A* + ID_OUI_FROM_DATABASE=Shenzhen Winyao Electronic Limited + +OUI:CC856C* + ID_OUI_FROM_DATABASE=SHENZHEN MDK DIGITAL TECHNOLOGY CO.,LTD + +OUI:60FFDD* + ID_OUI_FROM_DATABASE=C.E. ELECTRONICS, INC + +OUI:FCBBA1* + ID_OUI_FROM_DATABASE=Shenzhen Minicreate Technology Co.,Ltd + +OUI:50B695* + ID_OUI_FROM_DATABASE=Micropoint Biotechnologies,Inc. + +OUI:B48547* + ID_OUI_FROM_DATABASE=Amptown System Company GmbH + +OUI:3C25D7* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:1889DF* + ID_OUI_FROM_DATABASE=CerebrEX Inc. + +OUI:30A8DB* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:CC9F35* + ID_OUI_FROM_DATABASE=Transbit Sp. z o.o. + +OUI:407875* + ID_OUI_FROM_DATABASE=IMBEL - Industria de Material Belico do Brasil + +OUI:0C4F5A* + ID_OUI_FROM_DATABASE=ASA-RT s.r.l. + +OUI:B4B542* + ID_OUI_FROM_DATABASE=Hubbell Power Systems, Inc. + +OUI:54CDEE* + ID_OUI_FROM_DATABASE=ShenZhen Apexis Electronic Co.,Ltd + +OUI:F8F005* + ID_OUI_FROM_DATABASE=Newport Media Inc. + +OUI:98C0EB* + ID_OUI_FROM_DATABASE=Global Regency Ltd + +OUI:D4224E* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:28DEF6* + ID_OUI_FROM_DATABASE=bioMerieux Inc. + +OUI:88E8F8* + ID_OUI_FROM_DATABASE=YONG TAI ELECTRONIC (DONGGUAN) LTD. + +OUI:2C073C* + ID_OUI_FROM_DATABASE=DEVLINE LIMITED + +OUI:7CE4AA* + ID_OUI_FROM_DATABASE=Private + +OUI:1820A6* + ID_OUI_FROM_DATABASE=Sage Co., Ltd. + +OUI:BCF61C* + ID_OUI_FROM_DATABASE=Geomodeling Wuxi Technology Co. Ltd. + +OUI:083F3E* + ID_OUI_FROM_DATABASE=WSH GmbH + +OUI:6C09D6* + ID_OUI_FROM_DATABASE=Digiquest Electronics LTD + +OUI:8C569D* + ID_OUI_FROM_DATABASE=Imaging Solutions Group + +OUI:A43A69* + ID_OUI_FROM_DATABASE=Vers Inc + +OUI:387B47* + ID_OUI_FROM_DATABASE=AKELA, Inc. + +OUI:7CCD11* + ID_OUI_FROM_DATABASE=MS-Magnet + +OUI:94FBB2* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:4CE1BB* + ID_OUI_FROM_DATABASE=Zhuhai HiFocus Technology Co., Ltd. + +OUI:8CDE99* + ID_OUI_FROM_DATABASE=Comlab Inc. + +OUI:2C9AA4* + ID_OUI_FROM_DATABASE=NGI SpA + +OUI:B46698* + ID_OUI_FROM_DATABASE=Zealabs srl + +OUI:283B96* + ID_OUI_FROM_DATABASE=Cool Control LTD + +OUI:80D433* + ID_OUI_FROM_DATABASE=LzLabs GmbH + +OUI:085AE0* + ID_OUI_FROM_DATABASE=Recovision Technology Co., Ltd. + +OUI:BCEE7B* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:FC09D8* + ID_OUI_FROM_DATABASE=ACTEON Group + +OUI:0C1262* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:687CC8* + ID_OUI_FROM_DATABASE=Measurement Systems S. de R.L. + +OUI:F015A0* + ID_OUI_FROM_DATABASE=KyungDong One Co., Ltd. + +OUI:ECF72B* + ID_OUI_FROM_DATABASE=HD DIGITAL TECH CO., LTD. + +OUI:D8B6D6* + ID_OUI_FROM_DATABASE=Blu Tether Limited + +OUI:847207* + ID_OUI_FROM_DATABASE=I&C Technology + +OUI:E0AEB2* + ID_OUI_FROM_DATABASE=Bender GmbH & Co.KG + +OUI:2C553C* + ID_OUI_FROM_DATABASE=Gainspeed, Inc. + +OUI:B43E3B* + ID_OUI_FROM_DATABASE=Viableware, Inc + +OUI:F854AF* + ID_OUI_FROM_DATABASE=ECI Telecom Ltd. + +OUI:2464EF* + ID_OUI_FROM_DATABASE=CYG SUNRI CO.,LTD. + +OUI:50B888* + ID_OUI_FROM_DATABASE=wi2be Tecnologia S/A + +OUI:B8C1A2* + ID_OUI_FROM_DATABASE=Dragon Path Technologies Co., Limited + +OUI:50ED78* + ID_OUI_FROM_DATABASE=Changzhou Yongse Infotech Co.,Ltd + +OUI:8CB7F7* + ID_OUI_FROM_DATABASE=Shenzhen UniStrong Science & Technology Co., Ltd + +OUI:085240* + ID_OUI_FROM_DATABASE=EbV Elektronikbau- und Vertriebs GmbH + +OUI:80F25E* + ID_OUI_FROM_DATABASE=Kyynel + +OUI:844F03* + ID_OUI_FROM_DATABASE=Ablelink Electronics Ltd + +OUI:94B9B4* + ID_OUI_FROM_DATABASE=Aptos Technology + +OUI:D0B523* + ID_OUI_FROM_DATABASE=Bestcare Cloucal Corp. + +OUI:783D5B* + ID_OUI_FROM_DATABASE=TELNET Redes Inteligentes S.A. + +OUI:D0C42F* + ID_OUI_FROM_DATABASE=Tamagawa Seiki Co.,Ltd. + +OUI:5CFFFF* + ID_OUI_FROM_DATABASE=Shenzhen Kezhonglong Optoelectronic Technology Co., Ltd + +OUI:F0D3A7* + ID_OUI_FROM_DATABASE=CobaltRay Co., Ltd + +OUI:847616* + ID_OUI_FROM_DATABASE=Addat s.r.o. + +OUI:D46867* + ID_OUI_FROM_DATABASE=Neoventus Design Group + +OUI:68692E* + ID_OUI_FROM_DATABASE=Zycoo Co.,Ltd + +OUI:A875E2* + ID_OUI_FROM_DATABASE=Aventura Technologies, Inc. + +OUI:38BF2F* + ID_OUI_FROM_DATABASE=Espec Corp. + +OUI:182012* + ID_OUI_FROM_DATABASE=Aztech Associates Inc. + +OUI:C0F991* + ID_OUI_FROM_DATABASE=GME Standard Communications P/L + +OUI:14EDA5* + ID_OUI_FROM_DATABASE=Wächter GmbH Sicherheitssysteme + +OUI:E056F4* + ID_OUI_FROM_DATABASE=AxesNetwork Solutions inc. + +OUI:385AA8* + ID_OUI_FROM_DATABASE=Beijing Zhongdun Security Technology Development Co. + +OUI:FC3FAB* + ID_OUI_FROM_DATABASE=Henan Lanxin Technology Co., Ltd + +OUI:F8FF5F* + ID_OUI_FROM_DATABASE=Shenzhen Communication Technology Co.,Ltd + +OUI:DCC422* + ID_OUI_FROM_DATABASE=Systembase Limited + +OUI:F4BD7C* + ID_OUI_FROM_DATABASE=Chengdu jinshi communication Co., LTD + +OUI:C8F36B* + ID_OUI_FROM_DATABASE=Yamato Scale Co.,Ltd. + +OUI:6C90B1* + ID_OUI_FROM_DATABASE=SanLogic Inc + +OUI:845C93* + ID_OUI_FROM_DATABASE=Chabrier Services + +OUI:D44C9C* + ID_OUI_FROM_DATABASE=Shenzhen YOOBAO Technology Co.Ltd + +OUI:A88D7B* + ID_OUI_FROM_DATABASE=SunDroid Global limited. + +OUI:A03B1B* + ID_OUI_FROM_DATABASE=Inspire Tech + +OUI:3C6E63* + ID_OUI_FROM_DATABASE=Mitron OY + +OUI:502E5C* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:20D21F* + ID_OUI_FROM_DATABASE=Wincal Technology Corp. + +OUI:FC1E16* + ID_OUI_FROM_DATABASE=IPEVO corp + +OUI:6C4B7F* + ID_OUI_FROM_DATABASE=Vossloh-Schwabe Deutschland GmbH + +OUI:0CCB8D* + ID_OUI_FROM_DATABASE=ASCO Numatics GmbH + +OUI:2847AA* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:682DDC* + ID_OUI_FROM_DATABASE=Wuhan Changjiang Electro-Communication Equipment CO.,LTD + +OUI:1C63B7* + ID_OUI_FROM_DATABASE=OpenProducts 237 AB + +OUI:A0A23C* + ID_OUI_FROM_DATABASE=GPMS + +OUI:708D09* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:FCE1D9* + ID_OUI_FROM_DATABASE=Stable Imaging Solutions LLC + +OUI:38B74D* + ID_OUI_FROM_DATABASE=Fijowave Limited + +OUI:A0E5E9* + ID_OUI_FROM_DATABASE=enimai Inc + +OUI:9CBB98* + ID_OUI_FROM_DATABASE=Shen Zhen RND Electronic Co.,LTD + +OUI:345C40* + ID_OUI_FROM_DATABASE=Cargt Holdings LLC + +OUI:34885D* + ID_OUI_FROM_DATABASE=Logitech Far East + +OUI:6064A1* + ID_OUI_FROM_DATABASE=RADiflow Ltd. + +OUI:8079AE* + ID_OUI_FROM_DATABASE=ShanDong Tecsunrise Co.,Ltd + +OUI:2C7155* + ID_OUI_FROM_DATABASE=HiveMotion + +OUI:909916* + ID_OUI_FROM_DATABASE=ELVEES NeoTek OJSC + +OUI:FC1BFF* + ID_OUI_FROM_DATABASE=V-ZUG AG + +OUI:AC5036* + ID_OUI_FROM_DATABASE=Pi-Coral Inc + +OUI:FC019E* + ID_OUI_FROM_DATABASE=VIEVU + +OUI:F45F69* + ID_OUI_FROM_DATABASE=Matsufu Electronics distribution Company + +OUI:F4A294* + ID_OUI_FROM_DATABASE=EAGLE WORLD DEVELOPMENT CO., LIMITED + +OUI:2CCD69* + ID_OUI_FROM_DATABASE=Aqavi.com + +OUI:947C3E* + ID_OUI_FROM_DATABASE=Polewall Norge AS + +OUI:E0D1E6* + ID_OUI_FROM_DATABASE=Aliph dba Jawbone + +OUI:28C671* + ID_OUI_FROM_DATABASE=Yota Devices OY + +OUI:DC1792* + ID_OUI_FROM_DATABASE=Captivate Network + +OUI:7C8306* + ID_OUI_FROM_DATABASE=Glen Dimplex Nordic as + +OUI:84253F* + ID_OUI_FROM_DATABASE=Silex Technology, Inc + +OUI:907A0A* + ID_OUI_FROM_DATABASE=Gebr. Bode GmbH & Co KG + +OUI:306112* + ID_OUI_FROM_DATABASE=PAV GmbH + +OUI:A0C6EC* + ID_OUI_FROM_DATABASE=ShenZhen ANYK Technology Co.,LTD + +OUI:C80258* + ID_OUI_FROM_DATABASE=ITW GSE ApS + +OUI:1001CA* + ID_OUI_FROM_DATABASE=Ashley Butterworth + +OUI:246AAB* + ID_OUI_FROM_DATABASE=IT-IS International + +OUI:28F532* + ID_OUI_FROM_DATABASE=ADD-Engineering BV + +OUI:FC4BBC* + ID_OUI_FROM_DATABASE=Sunplus Technology Co., Ltd. + +OUI:142D8B* + ID_OUI_FROM_DATABASE=Incipio Technologies, Inc + +OUI:CCE8AC* + ID_OUI_FROM_DATABASE=SOYEA Technology Co.,Ltd. + +OUI:78D38D* + ID_OUI_FROM_DATABASE=HONGKONG YUNLINK TECHNOLOGY LIMITED + +OUI:1C48F9* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:744BE9* + ID_OUI_FROM_DATABASE=EXPLORER HYPERTECH CO.,LTD + +OUI:B836D8* + ID_OUI_FROM_DATABASE=Videoswitch + +OUI:F835DD* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:0CF019* + ID_OUI_FROM_DATABASE=Malgn Technology Co., Ltd. + +OUI:D46A91* + ID_OUI_FROM_DATABASE=Snap AV + +OUI:E8519D* + ID_OUI_FROM_DATABASE=Yeonhab Precision Co.,LTD + +OUI:00B78D* + ID_OUI_FROM_DATABASE=Nanjing Shining Electric Automation Co., Ltd + +OUI:68E166* + ID_OUI_FROM_DATABASE=Private + +OUI:60FEF9* + ID_OUI_FROM_DATABASE=Thomas & Betts + +OUI:78FE41* + ID_OUI_FROM_DATABASE=Socus networks + +OUI:083571* + ID_OUI_FROM_DATABASE=CASwell INC. + +OUI:DCF755* + ID_OUI_FROM_DATABASE=SITRONIK + +OUI:ACCA8E* + ID_OUI_FROM_DATABASE=ODA Technologies + +OUI:6405BE* + ID_OUI_FROM_DATABASE=NEW LIGHT LED + +OUI:E03E4A* + ID_OUI_FROM_DATABASE=Cavanagh Group International + +OUI:6CB350* + ID_OUI_FROM_DATABASE=Anhui comhigher tech co.,ltd + +OUI:A42305* + ID_OUI_FROM_DATABASE=Open Networking Laboratory + +OUI:1C86AD* + ID_OUI_FROM_DATABASE=MCT CO., LTD. + +OUI:28D93E* + ID_OUI_FROM_DATABASE=Telecor Inc. + +OUI:882364* + ID_OUI_FROM_DATABASE=Watchnet DVR Inc + +OUI:A05B21* + ID_OUI_FROM_DATABASE=ENVINET GmbH + +OUI:50B8A2* + ID_OUI_FROM_DATABASE=ImTech Technologies LLC, + +OUI:A41566* + ID_OUI_FROM_DATABASE=Wei Fang Goertek Electronics Co.,Ltd + +OUI:B04C05* + ID_OUI_FROM_DATABASE=Fresenius Medical Care Deutschland GmbH + +OUI:A0EC80* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:9046B7* + ID_OUI_FROM_DATABASE=Vadaro Pte Ltd + +OUI:1C08C1* + ID_OUI_FROM_DATABASE=Lg Innotek + +OUI:201D03* + ID_OUI_FROM_DATABASE=Elatec GmbH + +OUI:C06C6D* + ID_OUI_FROM_DATABASE=MagneMotion, Inc. + +OUI:74CA25* + ID_OUI_FROM_DATABASE=Calxeda, Inc. + +OUI:CCBD35* + ID_OUI_FROM_DATABASE=Steinel GmbH + +OUI:788DF7* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:6CECA1* + ID_OUI_FROM_DATABASE=SHENZHEN CLOU ELECTRONICS CO. LTD. + +OUI:D862DB* + ID_OUI_FROM_DATABASE=Eno Inc. + +OUI:68DB67* + ID_OUI_FROM_DATABASE=Nantong Coship Electronics Co., Ltd + +OUI:BC261D* + ID_OUI_FROM_DATABASE=HONG KONG TECON TECHNOLOGY + +OUI:888964* + ID_OUI_FROM_DATABASE=GSI Electronics Inc. + +OUI:4C82CF* + ID_OUI_FROM_DATABASE=Echostar Technologies + +OUI:9CA577* + ID_OUI_FROM_DATABASE=Osorno Enterprises Inc. + +OUI:C0C3B6* + ID_OUI_FROM_DATABASE=Automatic Systems + +OUI:A8294C* + ID_OUI_FROM_DATABASE=Precision Optical Transceivers, Inc. + +OUI:D0EB03* + ID_OUI_FROM_DATABASE=Zhehua technology limited + +OUI:A0861D* + ID_OUI_FROM_DATABASE=Chengdu Fuhuaxin Technology co.,Ltd + +OUI:9498A2* + ID_OUI_FROM_DATABASE=Shanghai LISTEN TECH.LTD + +OUI:2CB693* + ID_OUI_FROM_DATABASE=Radware + +OUI:88685C* + ID_OUI_FROM_DATABASE=Shenzhen ChuangDao & Perpetual Eternal Technology Co.,Ltd + +OUI:B4FE8C* + ID_OUI_FROM_DATABASE=Centro Sicurezza Italia SpA + +OUI:D82916* + ID_OUI_FROM_DATABASE=Ascent Communication Technology + +OUI:6472D8* + ID_OUI_FROM_DATABASE=GooWi Technology Co.,Limited + +OUI:84ACA4* + ID_OUI_FROM_DATABASE=Beijing Novel Super Digital TV Technology Co., Ltd + +OUI:3C6FF7* + ID_OUI_FROM_DATABASE=EnTek Systems, Inc. + +OUI:B838CA* + ID_OUI_FROM_DATABASE=Kyokko Tsushin System CO.,LTD + +OUI:380FE4* + ID_OUI_FROM_DATABASE=Dedicated Network Partners Oy + +OUI:847A88* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:5461EA* + ID_OUI_FROM_DATABASE=Zaplox AB + +OUI:78324F* + ID_OUI_FROM_DATABASE=Millennium Group, Inc. + +OUI:F05DC8* + ID_OUI_FROM_DATABASE=Duracell Powermat + +OUI:48F925* + ID_OUI_FROM_DATABASE=Maestronic + +OUI:C0885B* + ID_OUI_FROM_DATABASE=SnD Tech Co., Ltd. + +OUI:64C667* + ID_OUI_FROM_DATABASE=Barnes&Noble + +OUI:C47DCC* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:64535D* + ID_OUI_FROM_DATABASE=Frauscher Sensortechnik + +OUI:105F06* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:841715* + ID_OUI_FROM_DATABASE=GP Electronics (HK) Ltd. + +OUI:087999* + ID_OUI_FROM_DATABASE=AIM GmbH + +OUI:84C2E4* + ID_OUI_FROM_DATABASE=Jiangsu Qinheng Co., Ltd. + +OUI:C0B8B1* + ID_OUI_FROM_DATABASE=BitBox Ltd + +OUI:0C722C* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:B01408* + ID_OUI_FROM_DATABASE=LIGHTSPEED INTERNATIONAL CO. + +OUI:F8FEA8* + ID_OUI_FROM_DATABASE=Technico Japan Corporation + +OUI:A8154D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D05099* + ID_OUI_FROM_DATABASE=ASRock Incorporation + +OUI:78A106* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A49EDB* + ID_OUI_FROM_DATABASE=AutoCrib, Inc. + +OUI:282CB2* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D43A65* + ID_OUI_FROM_DATABASE=IGRS Engineering Lab Ltd. + +OUI:10B9FE* + ID_OUI_FROM_DATABASE=Lika srl + +OUI:D42751* + ID_OUI_FROM_DATABASE=Infopia Co., Ltd + +OUI:A895B0* + ID_OUI_FROM_DATABASE=Aker Subsea Ltd + +OUI:5C20D0* + ID_OUI_FROM_DATABASE=Asoni Communication Co., Ltd. + +OUI:E0C3F3* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:104D77* + ID_OUI_FROM_DATABASE=Innovative Computer Engineering + +OUI:3C081E* + ID_OUI_FROM_DATABASE=Beijing Yupont Electric Power Technology Co.,Ltd + +OUI:7CA15D* + ID_OUI_FROM_DATABASE=GN ReSound A/S + +OUI:B4DD15* + ID_OUI_FROM_DATABASE=ControlThings Oy Ab + +OUI:3C86A8* + ID_OUI_FROM_DATABASE=Sangshin elecom .co,, LTD + +OUI:FCDD55* + ID_OUI_FROM_DATABASE=Shenzhen WeWins wireless Co.,Ltd + +OUI:CC0DEC* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:68B094* + ID_OUI_FROM_DATABASE=INESA ELECTRON CO.,LTD + +OUI:40E730* + ID_OUI_FROM_DATABASE=DEY Storage Systems, Inc. + +OUI:A8D236* + ID_OUI_FROM_DATABASE=Lightware Visual Engineering + +OUI:6C8686* + ID_OUI_FROM_DATABASE=Technonia + +OUI:84E714* + ID_OUI_FROM_DATABASE=Liang Herng Enterprise,Co.Ltd. + +OUI:303D08* + ID_OUI_FROM_DATABASE=GLINTT TES S.A. + +OUI:9C541C* + ID_OUI_FROM_DATABASE=Shenzhen My-power Technology Co.,Ltd + +OUI:E496AE* + ID_OUI_FROM_DATABASE=ALTOGRAPHICS Inc. + +OUI:F80BD0* + ID_OUI_FROM_DATABASE=Datang Telecom communication terminal (Tianjin) Co., Ltd. + +OUI:48B9C2* + ID_OUI_FROM_DATABASE=Teletics Inc. + +OUI:D046DC* + ID_OUI_FROM_DATABASE=Southwest Research Institute + +OUI:046E49* + ID_OUI_FROM_DATABASE=TaiYear Electronic Technology (Suzhou) Co., Ltd + +OUI:08606E* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:BC39A6* + ID_OUI_FROM_DATABASE=CSUN System Technology Co.,LTD + +OUI:ECB541* + ID_OUI_FROM_DATABASE=SHINANO E and E Co.Ltd. + +OUI:D40057* + ID_OUI_FROM_DATABASE=MC Technologies GmbH + +OUI:48B8DE* + ID_OUI_FROM_DATABASE=HOMEWINS TECHNOLOGY CO.,LTD. + +OUI:1065CF* + ID_OUI_FROM_DATABASE=IQSIM + +OUI:B877C3* + ID_OUI_FROM_DATABASE=Decagon Devices, Inc. + +OUI:849DC5* + ID_OUI_FROM_DATABASE=Centera Photonics Inc. + +OUI:580943* + ID_OUI_FROM_DATABASE=Private + +OUI:547FA8* + ID_OUI_FROM_DATABASE=TELCO systems, s.r.o. + +OUI:5474E6* + ID_OUI_FROM_DATABASE=Webtech Wireless + +OUI:AC5D10* + ID_OUI_FROM_DATABASE=Pace Americas + +OUI:88F490* + ID_OUI_FROM_DATABASE=Jetmobile Pte Ltd + +OUI:E8A364* + ID_OUI_FROM_DATABASE=Signal Path International / Peachtree Audio + +OUI:D0D6CC* + ID_OUI_FROM_DATABASE=Wintop + +OUI:101D51* + ID_OUI_FROM_DATABASE=ON-Q LLC dba ON-Q Mesh Networks + +OUI:34C99D* + ID_OUI_FROM_DATABASE=EIDOLON COMMUNICATIONS TECHNOLOGY CO. LTD. + +OUI:8C4AEE* + ID_OUI_FROM_DATABASE=GIGA TMS INC + +OUI:F46DE2* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:04F8C2* + ID_OUI_FROM_DATABASE=Flaircomm Microelectronics, Inc. + +OUI:0C93FB* + ID_OUI_FROM_DATABASE=BNS Solutions + +OUI:38B5BD* + ID_OUI_FROM_DATABASE=E.G.O. Elektro-Ger + +OUI:B85AF7* + ID_OUI_FROM_DATABASE=Ouya, Inc + +OUI:E0D9A2* + ID_OUI_FROM_DATABASE=Hippih aps + +OUI:F0F669* + ID_OUI_FROM_DATABASE=Motion Analysis Corporation + +OUI:F0219D* + ID_OUI_FROM_DATABASE=Cal-Comp Electronics & Communications Company Ltd. + +OUI:F8D7BF* + ID_OUI_FROM_DATABASE=REV Ritter GmbH + +OUI:00B56D* + ID_OUI_FROM_DATABASE=David Electronics Co., LTD. + +OUI:B461FF* + ID_OUI_FROM_DATABASE=Lumigon A/S + +OUI:9038DF* + ID_OUI_FROM_DATABASE=Changzhou Tiannengbo System Co. Ltd. + +OUI:CC593E* + ID_OUI_FROM_DATABASE=TOUMAZ LTD + +OUI:AC8D14* + ID_OUI_FROM_DATABASE=Smartrove Inc + +OUI:18673F* + ID_OUI_FROM_DATABASE=Hanover Displays Limited + +OUI:A00ABF* + ID_OUI_FROM_DATABASE=Wieson Technologies Co., Ltd. + +OUI:2091D9* + ID_OUI_FROM_DATABASE=I'M SPA + +OUI:744D79* + ID_OUI_FROM_DATABASE=Arrive Systems Inc. + +OUI:C83D97* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:38192F* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:141BF0* + ID_OUI_FROM_DATABASE=Intellimedia Systems Ltd + +OUI:E45614* + ID_OUI_FROM_DATABASE=Suttle Apparatus + +OUI:842BBC* + ID_OUI_FROM_DATABASE=Modelleisenbahn GmbH + +OUI:E856D6* + ID_OUI_FROM_DATABASE=NCTech Ltd + +OUI:4088E0* + ID_OUI_FROM_DATABASE=Beijing Ereneben Information Technology Limited Shenzhen Branch + +OUI:1CF4CA* + ID_OUI_FROM_DATABASE=Private + +OUI:F490EA* + ID_OUI_FROM_DATABASE=Deciso B.V. + +OUI:942197* + ID_OUI_FROM_DATABASE=Stalmart Technology Limited + +OUI:AC9403* + ID_OUI_FROM_DATABASE=Envision Peripherals Inc + +OUI:A865B2* + ID_OUI_FROM_DATABASE=DONGGUAN YISHANG ELECTRONIC TECHNOLOGY CO., LIMITED + +OUI:60B982* + ID_OUI_FROM_DATABASE=RO.VE.R. Laboratories S.p.A. + +OUI:B46238* + ID_OUI_FROM_DATABASE=Exablox + +OUI:40704A* + ID_OUI_FROM_DATABASE=Power Idea Technology Limited + +OUI:A40BED* + ID_OUI_FROM_DATABASE=Carry Technology Co.,Ltd + +OUI:0CD996* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D82DE1* + ID_OUI_FROM_DATABASE=Tricascade Inc. + +OUI:C438D3* + ID_OUI_FROM_DATABASE=TAGATEC CO.,LTD + +OUI:547398* + ID_OUI_FROM_DATABASE=Toyo Electronics Corporation + +OUI:E0AAB0* + ID_OUI_FROM_DATABASE=GENERAL VISION ELECTRONICS CO. LTD. + +OUI:68B43A* + ID_OUI_FROM_DATABASE=WaterFurnace International, Inc. + +OUI:543968* + ID_OUI_FROM_DATABASE=Edgewater Networks Inc + +OUI:985E1B* + ID_OUI_FROM_DATABASE=ConversDigital Co., Ltd. + +OUI:B8B7D7* + ID_OUI_FROM_DATABASE=2GIG Technologies + +OUI:1048B1* + ID_OUI_FROM_DATABASE=Beijing Duokan Technology Limited + +OUI:005D03* + ID_OUI_FROM_DATABASE=Xilinx, Inc + +OUI:24EE3A* + ID_OUI_FROM_DATABASE=Chengdu Yingji Electronic Hi-tech Co Ltd + +OUI:F82285* + ID_OUI_FROM_DATABASE=Cypress Technology CO., LTD. + +OUI:8482F4* + ID_OUI_FROM_DATABASE=Beijing Huasun Unicreate Technology Co., Ltd + +OUI:0CC47E* + ID_OUI_FROM_DATABASE=EUCAST Co., Ltd. + +OUI:CCE798* + ID_OUI_FROM_DATABASE=My Social Stuff + +OUI:50724D* + ID_OUI_FROM_DATABASE=BEG Brueck Electronic GmbH + +OUI:B898B0* + ID_OUI_FROM_DATABASE=Atlona Inc. + +OUI:2C625A* + ID_OUI_FROM_DATABASE=Finest Security Systems Co., Ltd + +OUI:2074CF* + ID_OUI_FROM_DATABASE=Shenzhen Voxtech Co.,Ltd + +OUI:ACBD0B* + ID_OUI_FROM_DATABASE=IMAC CO.,LTD + +OUI:D8D27C* + ID_OUI_FROM_DATABASE=JEMA ENERGY, SA + +OUI:10F3DB* + ID_OUI_FROM_DATABASE=Gridco Systems, Inc. + +OUI:B01203* + ID_OUI_FROM_DATABASE=Dynamics Hong Kong Limited + +OUI:7093F8* + ID_OUI_FROM_DATABASE=Space Monkey, Inc. + +OUI:305D38* + ID_OUI_FROM_DATABASE=Beissbarth + +OUI:FCD6BD* + ID_OUI_FROM_DATABASE=Robert Bosch GmbH + +OUI:044A50* + ID_OUI_FROM_DATABASE=Ramaxel Technology (Shenzhen) limited company + +OUI:A4466B* + ID_OUI_FROM_DATABASE=EOC Technology + +OUI:3CF392* + ID_OUI_FROM_DATABASE=Virtualtek. Co. Ltd + +OUI:889676* + ID_OUI_FROM_DATABASE=TTC MARCONI s.r.o. + +OUI:149FE8* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:70B599* + ID_OUI_FROM_DATABASE=Embedded Technologies s.r.o. + +OUI:EC4C4D* + ID_OUI_FROM_DATABASE=ZAO NPK RoTeK + +OUI:E8D483* + ID_OUI_FROM_DATABASE=ULTIMATE Europe Transportation Equipment GmbH + +OUI:ACD9D6* + ID_OUI_FROM_DATABASE=tci GmbH + +OUI:7493A4* + ID_OUI_FROM_DATABASE=Zebra Technologies Corp. + +OUI:9C0DAC* + ID_OUI_FROM_DATABASE=Tymphany HK Limited + +OUI:8CD3A2* + ID_OUI_FROM_DATABASE=VisSim AS + +OUI:647657* + ID_OUI_FROM_DATABASE=Innovative Security Designs + +OUI:60455E* + ID_OUI_FROM_DATABASE=Liptel s.r.o. + +OUI:944A09* + ID_OUI_FROM_DATABASE=BitWise Controls + +OUI:E8102E* + ID_OUI_FROM_DATABASE=Really Simple Software, Inc + +OUI:D48CB5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D41E35* + ID_OUI_FROM_DATABASE=TOHO Electronics INC. + +OUI:700BC0* + ID_OUI_FROM_DATABASE=Dewav Technology Company + +OUI:2CD444* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:EC1A59* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:60CBFB* + ID_OUI_FROM_DATABASE=AirScape Inc. + +OUI:4C5427* + ID_OUI_FROM_DATABASE=Linepro Sp. z o.o. + +OUI:3CEAFB* + ID_OUI_FROM_DATABASE=NSE AG + +OUI:3476C5* + ID_OUI_FROM_DATABASE=I-O DATA DEVICE, INC. + +OUI:407074* + ID_OUI_FROM_DATABASE=Life Technology (China) Co., Ltd + +OUI:58BFEA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C386C* + ID_OUI_FROM_DATABASE=Real Time Logic + +OUI:D8AF3B* + ID_OUI_FROM_DATABASE=Hangzhou Bigbright Integrated communications system Co.,Ltd + +OUI:78D34F* + ID_OUI_FROM_DATABASE=Pace-O-Matic, Inc. + +OUI:784405* + ID_OUI_FROM_DATABASE=FUJITU(HONG KONG) ELECTRONIC Co.,LTD. + +OUI:C03F2A* + ID_OUI_FROM_DATABASE=Biscotti, Inc. + +OUI:44B382* + ID_OUI_FROM_DATABASE=Kuang-chi Institute of Advanced Technology + +OUI:D80DE3* + ID_OUI_FROM_DATABASE=FXI TECHNOLOGIES AS + +OUI:1CE165* + ID_OUI_FROM_DATABASE=Marshal Corporation + +OUI:0CC0C0* + ID_OUI_FROM_DATABASE=MAGNETI MARELLI SISTEMAS ELECTRONICOS MEXICO + +OUI:AC40EA* + ID_OUI_FROM_DATABASE=C&T Solution Inc. + +OUI:BC8B55* + ID_OUI_FROM_DATABASE=NPP ELIKS America Inc. DBA T&M Atlantic + +OUI:202598* + ID_OUI_FROM_DATABASE=Teleview + +OUI:844915* + ID_OUI_FROM_DATABASE=vArmour Networks, Inc. + +OUI:A04CC1* + ID_OUI_FROM_DATABASE=Helixtech Corp. + +OUI:1CB243* + ID_OUI_FROM_DATABASE=TDC A/S + +OUI:1C51B5* + ID_OUI_FROM_DATABASE=Techaya LTD + +OUI:80DB31* + ID_OUI_FROM_DATABASE=Power Quotient International Co., Ltd. + +OUI:AC0142* + ID_OUI_FROM_DATABASE=Uriel Technologies SIA + +OUI:A007B6* + ID_OUI_FROM_DATABASE=Advanced Technical Support, Inc. + +OUI:542A9C* + ID_OUI_FROM_DATABASE=LSY Defense, LLC. + +OUI:F89955* + ID_OUI_FROM_DATABASE=Fortress Technology Inc + +OUI:B827EB* + ID_OUI_FROM_DATABASE=Raspberry Pi Foundation + +OUI:E88DF5* + ID_OUI_FROM_DATABASE=ZNYX Networks, Inc. + +OUI:48EA63* + ID_OUI_FROM_DATABASE=Zhejiang Uniview Technologies Co., Ltd. + +OUI:0CE5D3* + ID_OUI_FROM_DATABASE=DH electronics GmbH + +OUI:C47130* + ID_OUI_FROM_DATABASE=Fon Technology S.L. + +OUI:48D7FF* + ID_OUI_FROM_DATABASE=BLANKOM Antennentechnik GmbH + +OUI:F47F35* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A0F419* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BCC168* + ID_OUI_FROM_DATABASE=DinBox Sverige AB + +OUI:6CAE8B* + ID_OUI_FROM_DATABASE=IBM Corporation + +OUI:A4F7D0* + ID_OUI_FROM_DATABASE=LAN Accessories Co., Ltd. + +OUI:D4EC0C* + ID_OUI_FROM_DATABASE=Harley-Davidson Motor Company + +OUI:6CA96F* + ID_OUI_FROM_DATABASE=TransPacket AS + +OUI:48ED80* + ID_OUI_FROM_DATABASE=daesung eltec + +OUI:A086EC* + ID_OUI_FROM_DATABASE=SAEHAN HITEC Co., Ltd + +OUI:BC4B79* + ID_OUI_FROM_DATABASE=SensingTek + +OUI:2818FD* + ID_OUI_FROM_DATABASE=Aditya Infotech Ltd. + +OUI:E42C56* + ID_OUI_FROM_DATABASE=Lilee Systems, Ltd. + +OUI:50008C* + ID_OUI_FROM_DATABASE=Hong Kong Telecommunications (HKT) Limited + +OUI:DCA8CF* + ID_OUI_FROM_DATABASE=New Spin Golf, LLC. + +OUI:34BA9A* + ID_OUI_FROM_DATABASE=Asiatelco Technologies Co. + +OUI:642DB7* + ID_OUI_FROM_DATABASE=SEUNGIL ELECTRONICS + +OUI:008DDA* + ID_OUI_FROM_DATABASE=Link One Co., Ltd. + +OUI:08B4CF* + ID_OUI_FROM_DATABASE=Abicom International + +OUI:445F7A* + ID_OUI_FROM_DATABASE=Shihlin Electric & Engineering Corp. + +OUI:28BA18* + ID_OUI_FROM_DATABASE=NextNav, LLC + +OUI:2C36F8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:AC3D05* + ID_OUI_FROM_DATABASE=Instorescreen Aisa + +OUI:F48E09* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:D443A8* + ID_OUI_FROM_DATABASE=Changzhou Haojie Electric Co., Ltd. + +OUI:BCB852* + ID_OUI_FROM_DATABASE=Cybera, Inc. + +OUI:70D6B6* + ID_OUI_FROM_DATABASE=Metrum Technologies + +OUI:28D576* + ID_OUI_FROM_DATABASE=Premier Wireless, Inc. + +OUI:6CE907* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:94DF58* + ID_OUI_FROM_DATABASE=IJ Electron CO.,Ltd. + +OUI:8C0CA3* + ID_OUI_FROM_DATABASE=Amper + +OUI:28940F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5CEB4E* + ID_OUI_FROM_DATABASE=R. STAHL HMI Systems GmbH + +OUI:B8DAF7* + ID_OUI_FROM_DATABASE=Advanced Photonics, Inc. + +OUI:2C36A0* + ID_OUI_FROM_DATABASE=Capisco Limited + +OUI:800A06* + ID_OUI_FROM_DATABASE=COMTEC co.,ltd + +OUI:20FABB* + ID_OUI_FROM_DATABASE=Cambridge Executive Limited + +OUI:1C0B52* + ID_OUI_FROM_DATABASE=EPICOM S.A + +OUI:747E2D* + ID_OUI_FROM_DATABASE=Beijing Thomson CITIC Digital Technology Co. LTD. + +OUI:E80C75* + ID_OUI_FROM_DATABASE=Syncbak, Inc. + +OUI:18D66A* + ID_OUI_FROM_DATABASE=Inmarsat + +OUI:C85645* + ID_OUI_FROM_DATABASE=Intermas France + +OUI:8C604F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74FF7D* + ID_OUI_FROM_DATABASE=Wren Sound Systems, LLC + +OUI:30B216* + ID_OUI_FROM_DATABASE=Hytec Geraetebau GmbH + +OUI:34FC6F* + ID_OUI_FROM_DATABASE=ALCEA + +OUI:C0B357* + ID_OUI_FROM_DATABASE=Yoshiki Electronics Industry Ltd. + +OUI:D8BF4C* + ID_OUI_FROM_DATABASE=Victory Concept Electronics Limited + +OUI:C0DF77* + ID_OUI_FROM_DATABASE=Conrad Electronic SE + +OUI:C86000* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:645299* + ID_OUI_FROM_DATABASE=The Chamberlain Group, Inc + +OUI:BC125E* + ID_OUI_FROM_DATABASE=Beijing WisVideo INC. + +OUI:C80718* + ID_OUI_FROM_DATABASE=TDSi + +OUI:B4944E* + ID_OUI_FROM_DATABASE=WeTelecom Co., Ltd. + +OUI:345B11* + ID_OUI_FROM_DATABASE=EVI HEAT AB + +OUI:988BAD* + ID_OUI_FROM_DATABASE=Corintech Ltd. + +OUI:4050E0* + ID_OUI_FROM_DATABASE=Milton Security Group LLC + +OUI:C87CBC* + ID_OUI_FROM_DATABASE=Valink Co., Ltd. + +OUI:409FC7* + ID_OUI_FROM_DATABASE=BAEKCHUN I&C Co., Ltd. + +OUI:C87D77* + ID_OUI_FROM_DATABASE=Shenzhen Kingtech Communication Equipment Co.,Ltd + +OUI:A078BA* + ID_OUI_FROM_DATABASE=Pantech Co., Ltd. + +OUI:D4507A* + ID_OUI_FROM_DATABASE=CEIVA Logic, Inc + +OUI:9CC7D1* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:00B9F6* + ID_OUI_FROM_DATABASE=Shenzhen Super Rich Electronics Co.,Ltd + +OUI:9C5C8D* + ID_OUI_FROM_DATABASE=FIREMAX INDÚSTRIA E COMÉRCIO DE PRODUTOS ELETRÔNICOS LTDA + +OUI:E01E07* + ID_OUI_FROM_DATABASE=Anite Telecoms US. Inc + +OUI:B06CBF* + ID_OUI_FROM_DATABASE=3ality Digital Systems GmbH + +OUI:20AA4B* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:080D84* + ID_OUI_FROM_DATABASE=GECO, Inc. + +OUI:88E712* + ID_OUI_FROM_DATABASE=Whirlpool Corporation + +OUI:644BF0* + ID_OUI_FROM_DATABASE=CalDigit, Inc + +OUI:2838CF* + ID_OUI_FROM_DATABASE=Gen2wave + +OUI:50FC30* + ID_OUI_FROM_DATABASE=Treehouse Labs + +OUI:70704C* + ID_OUI_FROM_DATABASE=Purple Communications, Inc + +OUI:F47ACC* + ID_OUI_FROM_DATABASE=SolidFire, Inc. + +OUI:24BC82* + ID_OUI_FROM_DATABASE=Dali Wireless, Inc. + +OUI:64C5AA* + ID_OUI_FROM_DATABASE=South African Broadcasting Corporation + +OUI:64ED62* + ID_OUI_FROM_DATABASE=WOORI SYSTEMS Co., Ltd + +OUI:C4237A* + ID_OUI_FROM_DATABASE=WhizNets Inc. + +OUI:8430E5* + ID_OUI_FROM_DATABASE=SkyHawke Technologies, LLC + +OUI:2C002C* + ID_OUI_FROM_DATABASE=UNOWHY + +OUI:0481AE* + ID_OUI_FROM_DATABASE=Clack Corporation + +OUI:C09132* + ID_OUI_FROM_DATABASE=Patriot Memory + +OUI:A898C6* + ID_OUI_FROM_DATABASE=Shinbo Co., Ltd. + +OUI:006BA0* + ID_OUI_FROM_DATABASE=SHENZHEN UNIVERSAL INTELLISYS PTE LTD + +OUI:502690* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:B4211D* + ID_OUI_FROM_DATABASE=Beijing GuangXin Technology Co., Ltd + +OUI:E039D7* + ID_OUI_FROM_DATABASE=Plexxi, Inc. + +OUI:FC946C* + ID_OUI_FROM_DATABASE=UBIVELOX + +OUI:38DE60* + ID_OUI_FROM_DATABASE=Mohlenhoff GmbH + +OUI:2839E7* + ID_OUI_FROM_DATABASE=Preceno Technology Pte.Ltd. + +OUI:28D997* + ID_OUI_FROM_DATABASE=Yuduan Mobile Co., Ltd. + +OUI:886B76* + ID_OUI_FROM_DATABASE=CHINA HOPEFUL GROUP HOPEFUL ELECTRIC CO.,LTD + +OUI:A0CF5B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:18C451* + ID_OUI_FROM_DATABASE=Tucson Embedded Systems + +OUI:582EFE* + ID_OUI_FROM_DATABASE=Lighting Science Group + +OUI:F8D3A9* + ID_OUI_FROM_DATABASE=AXAN Networks + +OUI:5CD4AB* + ID_OUI_FROM_DATABASE=Zektor + +OUI:F8462D* + ID_OUI_FROM_DATABASE=SYNTEC Incorporation + +OUI:58677F* + ID_OUI_FROM_DATABASE=Clare Controls Inc. + +OUI:CCA374* + ID_OUI_FROM_DATABASE=Guangdong Guanglian Electronic Technology Co.Ltd + +OUI:50F61A* + ID_OUI_FROM_DATABASE=Kunshan JADE Technologies co., Ltd. + +OUI:20BBC6* + ID_OUI_FROM_DATABASE=Jabil Circuit Hungary Ltd. + +OUI:2C9717* + ID_OUI_FROM_DATABASE=I.C.Y. B.V. + +OUI:64E84F* + ID_OUI_FROM_DATABASE=Serialway Communication Technology Co. Ltd + +OUI:941D1C* + ID_OUI_FROM_DATABASE=TLab West Systems AB + +OUI:40667A* + ID_OUI_FROM_DATABASE=mediola - connected living AG + +OUI:64808B* + ID_OUI_FROM_DATABASE=VG Controls, Inc. + +OUI:7C6B52* + ID_OUI_FROM_DATABASE=Tigaro Wireless + +OUI:48C1AC* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:046D42* + ID_OUI_FROM_DATABASE=Bryston Ltd. + +OUI:D0CF5E* + ID_OUI_FROM_DATABASE=Energy Micro AS + +OUI:644D70* + ID_OUI_FROM_DATABASE=dSPACE GmbH + +OUI:807693* + ID_OUI_FROM_DATABASE=Newag SA + +OUI:FC1794* + ID_OUI_FROM_DATABASE=InterCreative Co., Ltd + +OUI:181420* + ID_OUI_FROM_DATABASE=TEB SAS + +OUI:D03110* + ID_OUI_FROM_DATABASE=Ingenic Semiconductor Co.,Ltd + +OUI:AC81F3* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:94C6EB* + ID_OUI_FROM_DATABASE=NOVA electronics, Inc. + +OUI:10F9EE* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:80971B* + ID_OUI_FROM_DATABASE=Altenergy Power System,Inc. + +OUI:1071F9* + ID_OUI_FROM_DATABASE=Cloud Telecomputers, LLC + +OUI:C47B2F* + ID_OUI_FROM_DATABASE=Beijing JoinHope Image Technology Ltd. + +OUI:18F650* + ID_OUI_FROM_DATABASE=Multimedia Pacific Limited + +OUI:704AAE* + ID_OUI_FROM_DATABASE=Xstream Flow (Pty) Ltd + +OUI:9C934E* + ID_OUI_FROM_DATABASE=Xerox Corporation + +OUI:3C26D5* + ID_OUI_FROM_DATABASE=Sotera Wireless + +OUI:FC2E2D* + ID_OUI_FROM_DATABASE=Lorom Industrial Co.LTD. + +OUI:E84E06* + ID_OUI_FROM_DATABASE=EDUP INTERNATIONAL (HK) CO., LTD + +OUI:B4C799* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:70B921* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:948FEE* + ID_OUI_FROM_DATABASE=Hughes Telematics, Inc. + +OUI:E8C320* + ID_OUI_FROM_DATABASE=Austco Communication Systems Pty Ltd + +OUI:D8973B* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:008D4E* + ID_OUI_FROM_DATABASE=CJSC NII STT + +OUI:10C586* + ID_OUI_FROM_DATABASE=BIO SOUND LAB CO., LTD. + +OUI:E8BA70* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6473E2* + ID_OUI_FROM_DATABASE=Arbiter Systems, Inc. + +OUI:00A1DE* + ID_OUI_FROM_DATABASE=ShenZhen ShiHua Technology CO.,LTD + +OUI:04E1C8* + ID_OUI_FROM_DATABASE=IMS Soluções em Energia Ltda. + +OUI:E4DD79* + ID_OUI_FROM_DATABASE=En-Vision America, Inc. + +OUI:60190C* + ID_OUI_FROM_DATABASE=RRAMAC + +OUI:34A709* + ID_OUI_FROM_DATABASE=Trevil srl + +OUI:F80332* + ID_OUI_FROM_DATABASE=Khomp + +OUI:C40F09* + ID_OUI_FROM_DATABASE=Hermes electronic GmbH + +OUI:908D1D* + ID_OUI_FROM_DATABASE=GH Technologies + +OUI:CCB55A* + ID_OUI_FROM_DATABASE=Fraunhofer ITWM + +OUI:587521* + ID_OUI_FROM_DATABASE=CJSC RTSoft + +OUI:64D989* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:44D3CA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:24DAB6* + ID_OUI_FROM_DATABASE=Sistemas de Gestión Energética S.A. de C.V + +OUI:B8F5E7* + ID_OUI_FROM_DATABASE=WayTools, LLC + +OUI:148A70* + ID_OUI_FROM_DATABASE=ADS GmbH + +OUI:FC0012* + ID_OUI_FROM_DATABASE=Toshiba Samsung Storage Technolgoy Korea Corporation + +OUI:F44450* + ID_OUI_FROM_DATABASE=BND Co., Ltd. + +OUI:644346* + ID_OUI_FROM_DATABASE=GuangDong Quick Network Computer CO.,LTD + +OUI:FCE892* + ID_OUI_FROM_DATABASE=Hangzhou Lancable Technology Co.,Ltd + +OUI:B8B42E* + ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co,Ltd.ShenZhen + +OUI:A84041* + ID_OUI_FROM_DATABASE=Dragino Technology Co., Limited + +OUI:DCF05D* + ID_OUI_FROM_DATABASE=Letta Teknoloji + +OUI:D05A0F* + ID_OUI_FROM_DATABASE=I-BT DIGITAL CO.,LTD + +OUI:7CDD20* + ID_OUI_FROM_DATABASE=IOXOS Technologies S.A. + +OUI:A0E9DB* + ID_OUI_FROM_DATABASE=Ningbo FreeWings Technologies Co.,Ltd + +OUI:9C7BD2* + ID_OUI_FROM_DATABASE=NEOLAB Convergence + +OUI:900D66* + ID_OUI_FROM_DATABASE=Digimore Electronics Co., Ltd + +OUI:48C862* + ID_OUI_FROM_DATABASE=Simo Wireless,Inc. + +OUI:0CF3EE* + ID_OUI_FROM_DATABASE=EM Microelectronic + +OUI:F0C27C* + ID_OUI_FROM_DATABASE=Mianyang Netop Telecom Equipment Co.,Ltd. + +OUI:BC35E5* + ID_OUI_FROM_DATABASE=Hydro Systems Company + +OUI:283410* + ID_OUI_FROM_DATABASE=Enigma Diagnostics Limited + +OUI:28CCFF* + ID_OUI_FROM_DATABASE=Corporacion Empresarial Altra SL + +OUI:14B73D* + ID_OUI_FROM_DATABASE=ARCHEAN Technologies + +OUI:A433D1* + ID_OUI_FROM_DATABASE=Fibrlink Communications Co.,Ltd. + +OUI:84DE3D* + ID_OUI_FROM_DATABASE=Crystal Vision Ltd + +OUI:B4AA4D* + ID_OUI_FROM_DATABASE=Ensequence, Inc. + +OUI:040A83* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:B42A39* + ID_OUI_FROM_DATABASE=ORBIT MERRET, spol. s r. o. + +OUI:B80B9D* + ID_OUI_FROM_DATABASE=ROPEX Industrie-Elektronik GmbH + +OUI:18AEBB* + ID_OUI_FROM_DATABASE=Siemens Convergence Creators GmbH&Co.KG + +OUI:3891FB* + ID_OUI_FROM_DATABASE=Xenox Holding BV + +OUI:50FAAB* + ID_OUI_FROM_DATABASE=L-tek d.o.o. + +OUI:A8E018* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:44AAE8* + ID_OUI_FROM_DATABASE=Nanotec Electronic GmbH & Co. KG + +OUI:D8DF0D* + ID_OUI_FROM_DATABASE=beroNet GmbH + +OUI:D8C068* + ID_OUI_FROM_DATABASE=Netgenetech.co.,ltd. + +OUI:50E549* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:A8FCB7* + ID_OUI_FROM_DATABASE=Consolidated Resource Imaging + +OUI:F87B8C* + ID_OUI_FROM_DATABASE=Amped Wireless + +OUI:44D2CA* + ID_OUI_FROM_DATABASE=Anvia TV Oy + +OUI:4C1A3A* + ID_OUI_FROM_DATABASE=PRIMA Research And Production Enterprise Ltd. + +OUI:AC0613* + ID_OUI_FROM_DATABASE=Senselogix Ltd + +OUI:CCF67A* + ID_OUI_FROM_DATABASE=Ayecka Communication Systems LTD + +OUI:00BB8E* + ID_OUI_FROM_DATABASE=HME Co., Ltd. + +OUI:C0A26D* + ID_OUI_FROM_DATABASE=Abbott Point of Care + +OUI:205B2A* + ID_OUI_FROM_DATABASE=Private + +OUI:18B430* + ID_OUI_FROM_DATABASE=Nest Labs Inc. + +OUI:F8769B* + ID_OUI_FROM_DATABASE=Neopis Co., Ltd. + +OUI:08E672* + ID_OUI_FROM_DATABASE=JEBSEE ELECTRONICS CO.,LTD. + +OUI:58E476* + ID_OUI_FROM_DATABASE=CENTRON COMMUNICATIONS TECHNOLOGIES FUJIAN CO.,LTD + +OUI:B435F7* + ID_OUI_FROM_DATABASE=Zhejiang Pearmain Electronics Co.ltd. + +OUI:0C6E4F* + ID_OUI_FROM_DATABASE=PrimeVOLT Co., Ltd. + +OUI:685B36* + ID_OUI_FROM_DATABASE=POWERTECH INDUSTRIAL CO., LTD. + +OUI:983000* + ID_OUI_FROM_DATABASE=Beijing KEMACOM Technologies Co., Ltd. + +OUI:F81D93* + ID_OUI_FROM_DATABASE=Longdhua(Beijing) Controls Technology Co.,Ltd + +OUI:D0EB9E* + ID_OUI_FROM_DATABASE=Seowoo Inc. + +OUI:8C5FDF* + ID_OUI_FROM_DATABASE=Beijing Railway Signal Factory + +OUI:586D8F* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:14C21D* + ID_OUI_FROM_DATABASE=Sabtech Industries + +OUI:74B00C* + ID_OUI_FROM_DATABASE=Network Video Technologies, Inc + +OUI:C88439* + ID_OUI_FROM_DATABASE=Sunrise Technologies + +OUI:44E4D9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0054AF* + ID_OUI_FROM_DATABASE=Continental Automotive Systems Inc. + +OUI:EC7D9D* + ID_OUI_FROM_DATABASE=MEI + +OUI:9C95F8* + ID_OUI_FROM_DATABASE=SmartDoor Systems, LLC + +OUI:D075BE* + ID_OUI_FROM_DATABASE=Reno A&E + +OUI:7C6C39* + ID_OUI_FROM_DATABASE=PIXSYS SRL + +OUI:9C5D95* + ID_OUI_FROM_DATABASE=VTC Electronics Corp. + +OUI:DC05ED* + ID_OUI_FROM_DATABASE=Nabtesco Corporation + +OUI:FC8329* + ID_OUI_FROM_DATABASE=Trei technics + +OUI:94E848* + ID_OUI_FROM_DATABASE=FYLDE MICRO LTD + +OUI:AC5E8C* + ID_OUI_FROM_DATABASE=Utillink + +OUI:CC7EE7* + ID_OUI_FROM_DATABASE=Panasonic AVC Networks Company + +OUI:BC99BC* + ID_OUI_FROM_DATABASE=FonSee Technology Inc. + +OUI:986022* + ID_OUI_FROM_DATABASE=EMW Co., Ltd. + +OUI:80B32A* + ID_OUI_FROM_DATABASE=Alstom Grid + +OUI:803457* + ID_OUI_FROM_DATABASE=OT Systems Limited + +OUI:B83D4E* + ID_OUI_FROM_DATABASE=Shenzhen Cultraview Digital Technology Co.,Ltd Shanghai Branch + +OUI:CCF3A5* + ID_OUI_FROM_DATABASE=Chi Mei Communication Systems, Inc + +OUI:C4242E* + ID_OUI_FROM_DATABASE=Galvanic Applied Sciences Inc + +OUI:6400F1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:04C5A4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:3CA72B* + ID_OUI_FROM_DATABASE=MRV Communications (Networks) LTD + +OUI:584C19* + ID_OUI_FROM_DATABASE=Chongqing Guohong Technology Development Company Limited + +OUI:D0A311* + ID_OUI_FROM_DATABASE=Neuberger Gebäudeautomation GmbH + +OUI:10A13B* + ID_OUI_FROM_DATABASE=FUJIKURA RUBBER LTD. + +OUI:F4E142* + ID_OUI_FROM_DATABASE=Delta Elektronika BV + +OUI:F00248* + ID_OUI_FROM_DATABASE=SmarteBuilding + +OUI:2CDD0C* + ID_OUI_FROM_DATABASE=Discovergy GmbH + +OUI:40B2C8* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:486B91* + ID_OUI_FROM_DATABASE=Fleetwood Group Inc. + +OUI:F43814* + ID_OUI_FROM_DATABASE=Shanghai Howell Electronic Co.,Ltd + +OUI:20AA25* + ID_OUI_FROM_DATABASE=IP-NET LLC + +OUI:ECBBAE* + ID_OUI_FROM_DATABASE=Digivoice Tecnologia em Eletronica Ltda + +OUI:DC2008* + ID_OUI_FROM_DATABASE=ASD Electronics Ltd + +OUI:088DC8* + ID_OUI_FROM_DATABASE=Ryowa Electronics Co.,Ltd + +OUI:D491AF* + ID_OUI_FROM_DATABASE=Electroacustica General Iberica, S.A. + +OUI:1CDF0F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34DF2A* + ID_OUI_FROM_DATABASE=Fujikon Industrial Co.,Limited + +OUI:C88447* + ID_OUI_FROM_DATABASE=Beautiful Enterprise Co., Ltd + +OUI:C88B47* + ID_OUI_FROM_DATABASE=Nolangroup S.P.A con Socio Unico + +OUI:24BA30* + ID_OUI_FROM_DATABASE=Technical Consumer Products, Inc. + +OUI:74D675* + ID_OUI_FROM_DATABASE=WYMA Tecnologia + +OUI:D01CBB* + ID_OUI_FROM_DATABASE=Beijing Ctimes Digital Technology Co., Ltd. + +OUI:9481A4* + ID_OUI_FROM_DATABASE=Azuray Technologies + +OUI:BCE09D* + ID_OUI_FROM_DATABASE=Eoslink + +OUI:346F92* + ID_OUI_FROM_DATABASE=White Rodgers Division + +OUI:8CDB25* + ID_OUI_FROM_DATABASE=ESG Solutions + +OUI:641A22* + ID_OUI_FROM_DATABASE=Heliospectra AB + +OUI:30142D* + ID_OUI_FROM_DATABASE=Piciorgros GmbH + +OUI:E441E6* + ID_OUI_FROM_DATABASE=Ottec Technology GmbH + +OUI:10E2D5* + ID_OUI_FROM_DATABASE=Qi Hardware Inc. + +OUI:7CDA84* + ID_OUI_FROM_DATABASE=Dongnian Networks Inc. + +OUI:A036FA* + ID_OUI_FROM_DATABASE=Ettus Research LLC + +OUI:EC836C* + ID_OUI_FROM_DATABASE=RM Tech Co., Ltd. + +OUI:C0C520* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:6083B2* + ID_OUI_FROM_DATABASE=GkWare e.K. + +OUI:80D019* + ID_OUI_FROM_DATABASE=Embed, Inc + +OUI:D41296* + ID_OUI_FROM_DATABASE=Anobit Technologies Ltd. + +OUI:B8FF6F* + ID_OUI_FROM_DATABASE=Shanghai Typrotech Technology Co.Ltd + +OUI:DC9C52* + ID_OUI_FROM_DATABASE=Sapphire Technology Limited. + +OUI:68122D* + ID_OUI_FROM_DATABASE=Special Instrument Development Co., Ltd. + +OUI:649B24* + ID_OUI_FROM_DATABASE=V Technology Co., Ltd. + +OUI:0475F5* + ID_OUI_FROM_DATABASE=CSST + +OUI:BC20BA* + ID_OUI_FROM_DATABASE=Inspur (Shandong) Electronic Information Co., Ltd + +OUI:249442* + ID_OUI_FROM_DATABASE=OPEN ROAD SOLUTIONS , INC. + +OUI:E0F379* + ID_OUI_FROM_DATABASE=Vaddio + +OUI:B09AE2* + ID_OUI_FROM_DATABASE=STEMMER IMAGING GmbH + +OUI:CCD811* + ID_OUI_FROM_DATABASE=Aiconn Technology Corporation + +OUI:78D004* + ID_OUI_FROM_DATABASE=Neousys Technology Inc. + +OUI:78A051* + ID_OUI_FROM_DATABASE=iiNet Labs Pty Ltd + +OUI:58A76F* + ID_OUI_FROM_DATABASE=iD corporation + +OUI:44599F* + ID_OUI_FROM_DATABASE=Criticare Systems, Inc + +OUI:3C2F3A* + ID_OUI_FROM_DATABASE=SFORZATO Corp. + +OUI:EC9233* + ID_OUI_FROM_DATABASE=Eddyfi NDT Inc + +OUI:ECE90B* + ID_OUI_FROM_DATABASE=SISTEMA SOLUCOES ELETRONICAS LTDA - EASYTECH + +OUI:A08C9B* + ID_OUI_FROM_DATABASE=Xtreme Technologies Corp + +OUI:607688* + ID_OUI_FROM_DATABASE=Velodyne + +OUI:980EE4* + ID_OUI_FROM_DATABASE=Private + +OUI:E828D5* + ID_OUI_FROM_DATABASE=Cots Technology + +OUI:08D5C0* + ID_OUI_FROM_DATABASE=Seers Technology Co., Ltd + +OUI:8CB64F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6C33A9* + ID_OUI_FROM_DATABASE=Magicjack LP + +OUI:08B7EC* + ID_OUI_FROM_DATABASE=Wireless Seismic + +OUI:BC71C1* + ID_OUI_FROM_DATABASE=XTrillion, Inc. + +OUI:0C469D* + ID_OUI_FROM_DATABASE=MS Sedco + +OUI:E0E8E8* + ID_OUI_FROM_DATABASE=Olive Telecommunication Pvt. Ltd + +OUI:0C3C65* + ID_OUI_FROM_DATABASE=Dome Imaging Inc + +OUI:942053* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:D49C8E* + ID_OUI_FROM_DATABASE=University of FUKUI + +OUI:2CB0DF* + ID_OUI_FROM_DATABASE=Soliton Technologies Pvt Ltd + +OUI:5CF3FC* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:D43D67* + ID_OUI_FROM_DATABASE=Carma Industries Inc. + +OUI:00BD27* + ID_OUI_FROM_DATABASE=Exar Corp. + +OUI:C8A729* + ID_OUI_FROM_DATABASE=SYStronics Co., Ltd. + +OUI:6C9CE9* + ID_OUI_FROM_DATABASE=Nimble Storage + +OUI:700258* + ID_OUI_FROM_DATABASE=01DB-METRAVIB + +OUI:20FDF1* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD + +OUI:389592* + ID_OUI_FROM_DATABASE=Beijing Tendyron Corporation + +OUI:705EAA* + ID_OUI_FROM_DATABASE=Action Target, Inc. + +OUI:0C8D98* + ID_OUI_FROM_DATABASE=TOP EIGHT IND CORP + +OUI:30493B* + ID_OUI_FROM_DATABASE=Nanjing Z-Com Wireless Co.,Ltd + +OUI:68DB96* + ID_OUI_FROM_DATABASE=OPWILL Technologies CO .,LTD + +OUI:00F860* + ID_OUI_FROM_DATABASE=PT. Panggung Electric Citrabuana + +OUI:FCEDB9* + ID_OUI_FROM_DATABASE=Arrayent + +OUI:44ED57* + ID_OUI_FROM_DATABASE=Longicorn, inc. + +OUI:C8A1B6* + ID_OUI_FROM_DATABASE=Shenzhen Longway Technologies Co., Ltd + +OUI:641E81* + ID_OUI_FROM_DATABASE=Dowslake Microsystems + +OUI:88ACC1* + ID_OUI_FROM_DATABASE=Generiton Co., Ltd. + +OUI:785712* + ID_OUI_FROM_DATABASE=Mobile Integration Workgroup + +OUI:380A0A* + ID_OUI_FROM_DATABASE=Sky-City Communication and Electronics Limited Company + +OUI:141BBD* + ID_OUI_FROM_DATABASE=Volex Inc. + +OUI:78C6BB* + ID_OUI_FROM_DATABASE=Innovasic, Inc. + +OUI:DC4EDE* + ID_OUI_FROM_DATABASE=SHINYEI TECHNOLOGY CO., LTD. + +OUI:888B5D* + ID_OUI_FROM_DATABASE=Storage Appliance Corporation + +OUI:F0F842* + ID_OUI_FROM_DATABASE=KEEBOX, Inc. + +OUI:78A714* + ID_OUI_FROM_DATABASE=Amphenol + +OUI:F450EB* + ID_OUI_FROM_DATABASE=Telechips Inc + +OUI:988EDD* + ID_OUI_FROM_DATABASE=TE Connectivity Limerick + +OUI:98FC11* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:180C77* + ID_OUI_FROM_DATABASE=Westinghouse Electric Company, LLC + +OUI:ACA016* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E4AD7D* + ID_OUI_FROM_DATABASE=SCL Elements + +OUI:40D40E* + ID_OUI_FROM_DATABASE=Biodata Ltd + +OUI:7C051E* + ID_OUI_FROM_DATABASE=RAFAEL LTD. + +OUI:58570D* + ID_OUI_FROM_DATABASE=Danfoss Solar Inverters + +OUI:0C826A* + ID_OUI_FROM_DATABASE=Wuhan Huagong Genuine Optics Technology Co., Ltd + +OUI:5C0E8B* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:38C7BA* + ID_OUI_FROM_DATABASE=CS Services Co.,Ltd. + +OUI:70D57E* + ID_OUI_FROM_DATABASE=Scalar Corporation + +OUI:7866AE* + ID_OUI_FROM_DATABASE=ZTEC Instruments, Inc. + +OUI:78818F* + ID_OUI_FROM_DATABASE=Server Racks Australia Pty Ltd + +OUI:E0589E* + ID_OUI_FROM_DATABASE=Laerdal Medical + +OUI:44D63D* + ID_OUI_FROM_DATABASE=Talari Networks + +OUI:58FD20* + ID_OUI_FROM_DATABASE=Bravida Sakerhet AB + +OUI:9835B8* + ID_OUI_FROM_DATABASE=Assembled Products Corporation + +OUI:240B2A* + ID_OUI_FROM_DATABASE=Viettel Group + +OUI:68E41F* + ID_OUI_FROM_DATABASE=Unglaube Identech GmbH + +OUI:84F64C* + ID_OUI_FROM_DATABASE=Cross Point BV + +OUI:90513F* + ID_OUI_FROM_DATABASE=Elettronica Santerno SpA + +OUI:7CA29B* + ID_OUI_FROM_DATABASE=D.SignT GmbH & Co. KG + +OUI:34AAEE* + ID_OUI_FROM_DATABASE=Mikrovisatos Servisas UAB + +OUI:A40CC3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34E0D7* + ID_OUI_FROM_DATABASE=DONGGUAN QISHENG ELECTRONICS INDUSTRIAL CO., LTD + +OUI:40520D* + ID_OUI_FROM_DATABASE=Pico Technology + +OUI:543131* + ID_OUI_FROM_DATABASE=Raster Vision Ltd + +OUI:90E0F0* + ID_OUI_FROM_DATABASE=IEEE 1722a Working Group + +OUI:1C6F65* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:F0AD4E* + ID_OUI_FROM_DATABASE=Globalscale Technologies, Inc. + +OUI:903D5A* + ID_OUI_FROM_DATABASE=Shenzhen Wision Technology Holding Limited + +OUI:609AA4* + ID_OUI_FROM_DATABASE=GVI SECURITY INC. + +OUI:F0ED1E* + ID_OUI_FROM_DATABASE=Bilkon Bilgisayar Kontrollu Cih. Im.Ltd. + +OUI:24A937* + ID_OUI_FROM_DATABASE=PURE Storage + +OUI:348302* + ID_OUI_FROM_DATABASE=iFORCOM Co., Ltd + +OUI:949C55* + ID_OUI_FROM_DATABASE=Alta Data Technologies + +OUI:389F83* + ID_OUI_FROM_DATABASE=OTN Systems N.V. + +OUI:8C541D* + ID_OUI_FROM_DATABASE=LGE + +OUI:601283* + ID_OUI_FROM_DATABASE=Soluciones Tecnologicas para la Salud y el Bienestar SA + +OUI:003A9D* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:905446* + ID_OUI_FROM_DATABASE=TES ELECTRONIC SOLUTIONS + +OUI:DC7B94* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:68234B* + ID_OUI_FROM_DATABASE=Nihon Dengyo Kousaku + +OUI:18422F* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:A4BE61* + ID_OUI_FROM_DATABASE=EutroVision System, Inc. + +OUI:E06290* + ID_OUI_FROM_DATABASE=Jinan Jovision Science & Technology Co., Ltd. + +OUI:A01859* + ID_OUI_FROM_DATABASE=Shenzhen Yidashi Electronics Co Ltd + +OUI:042234* + ID_OUI_FROM_DATABASE=Wireless Standard Extensions + +OUI:7812B8* + ID_OUI_FROM_DATABASE=ORANTEK LIMITED + +OUI:F0B6EB* + ID_OUI_FROM_DATABASE=Poslab Technology Co., Ltd. + +OUI:FCCCE4* + ID_OUI_FROM_DATABASE=Ascon Ltd. + +OUI:34862A* + ID_OUI_FROM_DATABASE=Heinz Lackmann GmbH & Co KG + +OUI:842141* + ID_OUI_FROM_DATABASE=Shenzhen Ginwave Technologies Ltd. + +OUI:B4ED54* + ID_OUI_FROM_DATABASE=Wohler Technologies + +OUI:544249* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:24DBAD* + ID_OUI_FROM_DATABASE=ShopperTrak RCT Corporation + +OUI:CC69B0* + ID_OUI_FROM_DATABASE=Global Traffic Technologies, LLC + +OUI:2872C5* + ID_OUI_FROM_DATABASE=Smartmatic Corp + +OUI:B8A3E0* + ID_OUI_FROM_DATABASE=BenRui Technology Co.,Ltd + +OUI:B8F732* + ID_OUI_FROM_DATABASE=Aryaka Networks Inc + +OUI:70828E* + ID_OUI_FROM_DATABASE=OleumTech Corporation + +OUI:502A7E* + ID_OUI_FROM_DATABASE=Smart electronic GmbH + +OUI:F0264C* + ID_OUI_FROM_DATABASE=Dr. Sigrist AG + +OUI:3C1CBE* + ID_OUI_FROM_DATABASE=JADAK LLC + +OUI:A8995C* + ID_OUI_FROM_DATABASE=aizo ag + +OUI:F445ED* + ID_OUI_FROM_DATABASE=Portable Innovation Technology Ltd. + +OUI:6C32DE* + ID_OUI_FROM_DATABASE=Indieon Technologies Pvt. Ltd. + +OUI:FCCF62* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:B09074* + ID_OUI_FROM_DATABASE=Fulan Electronics Limited + +OUI:2CA835* + ID_OUI_FROM_DATABASE=RIM + +OUI:94F692* + ID_OUI_FROM_DATABASE=Geminico co.,Ltd. + +OUI:8C736E* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:30EFD1* + ID_OUI_FROM_DATABASE=Alstom Strongwish (Shenzhen) Co., Ltd. + +OUI:C835B8* + ID_OUI_FROM_DATABASE=Ericsson, EAB/RWI/K + +OUI:243C20* + ID_OUI_FROM_DATABASE=Dynamode Group + +OUI:70D5E7* + ID_OUI_FROM_DATABASE=Wellcore Corporation + +OUI:3CF72A* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:FCE192* + ID_OUI_FROM_DATABASE=Sichuan Jinwangtong Electronic Science&Technology Co,.Ltd + +OUI:F8912A* + ID_OUI_FROM_DATABASE=GLP German Light Products GmbH + +OUI:E02630* + ID_OUI_FROM_DATABASE=Intrigue Technologies, Inc. + +OUI:8C9236* + ID_OUI_FROM_DATABASE=Aus.Linx Technology Co., Ltd. + +OUI:4012E4* + ID_OUI_FROM_DATABASE=Compass-EOS + +OUI:F8DC7A* + ID_OUI_FROM_DATABASE=Variscite LTD + +OUI:003A9C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E8E776* + ID_OUI_FROM_DATABASE=Shenzhen Kootion Technology Co., Ltd + +OUI:702F97* + ID_OUI_FROM_DATABASE=Aava Mobile Oy + +OUI:9018AE* + ID_OUI_FROM_DATABASE=Shanghai Meridian Technologies, Co. Ltd. + +OUI:0494A1* + ID_OUI_FROM_DATABASE=CATCH THE WIND INC + +OUI:2C3427* + ID_OUI_FROM_DATABASE=ERCO & GENER + +OUI:B42CBE* + ID_OUI_FROM_DATABASE=Direct Payment Solutions Limited + +OUI:F47626* + ID_OUI_FROM_DATABASE=Viltechmeda UAB + +OUI:EC4476* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:9CEBE8* + ID_OUI_FROM_DATABASE=BizLink (Kunshan) Co.,Ltd + +OUI:88ED1C* + ID_OUI_FROM_DATABASE=Cudo Communication Co., Ltd. + +OUI:B05B1F* + ID_OUI_FROM_DATABASE=THERMO FISHER SCIENTIFIC S.P.A. + +OUI:743256* + ID_OUI_FROM_DATABASE=NT-ware Systemprg GmbH + +OUI:003AAF* + ID_OUI_FROM_DATABASE=BlueBit Ltd. + +OUI:C0BAE6* + ID_OUI_FROM_DATABASE=Application Solutions (Electronics and Vision) Ltd + +OUI:20BFDB* + ID_OUI_FROM_DATABASE=DVL + +OUI:889821* + ID_OUI_FROM_DATABASE=TERAON + +OUI:CC5076* + ID_OUI_FROM_DATABASE=Ocom Communications, Inc. + +OUI:705812* + ID_OUI_FROM_DATABASE=Panasonic AVC Networks Company + +OUI:7C2CF3* + ID_OUI_FROM_DATABASE=Secure Electrans Ltd + +OUI:304174* + ID_OUI_FROM_DATABASE=ALTEC LANSING LLC + +OUI:7830E1* + ID_OUI_FROM_DATABASE=UltraClenz, LLC + +OUI:FCFBFB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1C129D* + ID_OUI_FROM_DATABASE=IEEE PES PSRC/SUB + +OUI:B40832* + ID_OUI_FROM_DATABASE=TC Communications + +OUI:002720* + ID_OUI_FROM_DATABASE=NEW-SOL COM + +OUI:00271C* + ID_OUI_FROM_DATABASE=MERCURY CORPORATION + +OUI:002712* + ID_OUI_FROM_DATABASE=MaxVision LLC + +OUI:00270F* + ID_OUI_FROM_DATABASE=Envisionnovation Inc + +OUI:0026D7* + ID_OUI_FROM_DATABASE=KM Electornic Technology Co., Ltd. + +OUI:0026D1* + ID_OUI_FROM_DATABASE=S Squared Innovations Inc. + +OUI:0026CB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0026C4* + ID_OUI_FROM_DATABASE=Cadmos microsystems S.r.l. + +OUI:0026BE* + ID_OUI_FROM_DATABASE=Schoonderbeek Elektronica Systemen B.V. + +OUI:0026B2* + ID_OUI_FROM_DATABASE=Setrix GmbH + +OUI:0026AC* + ID_OUI_FROM_DATABASE=Shanghai LUSTER Teraband photonic Co., Ltd. + +OUI:0026B1* + ID_OUI_FROM_DATABASE=Navis Auto Motive Systems, Inc. + +OUI:0026A8* + ID_OUI_FROM_DATABASE=DAEHAP HYPER-TECH + +OUI:0026A7* + ID_OUI_FROM_DATABASE=CONNECT SRL + +OUI:0026A1* + ID_OUI_FROM_DATABASE=Megger + +OUI:0026A2* + ID_OUI_FROM_DATABASE=Instrumentation Technology Systems + +OUI:00269B* + ID_OUI_FROM_DATABASE=SOKRAT Ltd. + +OUI:002695* + ID_OUI_FROM_DATABASE=ZT Group Int'l Inc + +OUI:00268F* + ID_OUI_FROM_DATABASE=MTA SpA + +OUI:6C8CDB* + ID_OUI_FROM_DATABASE=Otus Technologies Ltd + +OUI:B4417A* + ID_OUI_FROM_DATABASE=ShenZhen Gongjin Electronics Co.,Ltd + +OUI:401597* + ID_OUI_FROM_DATABASE=Protect America, Inc. + +OUI:60391F* + ID_OUI_FROM_DATABASE=ABB Ltd + +OUI:A07332* + ID_OUI_FROM_DATABASE=Cashmaster International Limited + +OUI:7C7BE4* + ID_OUI_FROM_DATABASE=Z'SEDAI KENKYUSHO CORPORATION + +OUI:40EF4C* + ID_OUI_FROM_DATABASE=Fihonest communication co.,Ltd + +OUI:24CF21* + ID_OUI_FROM_DATABASE=Shenzhen State Micro Technology Co., Ltd + +OUI:04B3B6* + ID_OUI_FROM_DATABASE=Seamap (UK) Ltd + +OUI:10BAA5* + ID_OUI_FROM_DATABASE=GANA I&C CO., LTD + +OUI:586ED6* + ID_OUI_FROM_DATABASE=Private + +OUI:E09153* + ID_OUI_FROM_DATABASE=XAVi Technologies Corp. + +OUI:CC0080* + ID_OUI_FROM_DATABASE=BETTINI SRL + +OUI:644BC3* + ID_OUI_FROM_DATABASE=Shanghai WOASiS Telecommunications Ltd., Co. + +OUI:0CE709* + ID_OUI_FROM_DATABASE=Fox Crypto B.V. + +OUI:002703* + ID_OUI_FROM_DATABASE=Testech Electronics Pte Ltd + +OUI:0026FD* + ID_OUI_FROM_DATABASE=Interactive Intelligence + +OUI:0026F6* + ID_OUI_FROM_DATABASE=Military Communication Institute + +OUI:0026F0* + ID_OUI_FROM_DATABASE=cTrixs International GmbH. + +OUI:0026EA* + ID_OUI_FROM_DATABASE=Cheerchip Electronic Technology (ShangHai) Co., Ltd. + +OUI:0026E3* + ID_OUI_FROM_DATABASE=DTI + +OUI:0026DD* + ID_OUI_FROM_DATABASE=Fival Science & Technology Co.,Ltd. + +OUI:0026DE* + ID_OUI_FROM_DATABASE=FDI MATELEC + +OUI:54B620* + ID_OUI_FROM_DATABASE=SUHDOL E&C Co.Ltd. + +OUI:C4AAA1* + ID_OUI_FROM_DATABASE=SUMMIT DEVELOPMENT, spol.s r.o. + +OUI:78C40E* + ID_OUI_FROM_DATABASE=H&D Wireless + +OUI:9C5B96* + ID_OUI_FROM_DATABASE=NMR Corporation + +OUI:E4FFDD* + ID_OUI_FROM_DATABASE=ELECTRON INDIA + +OUI:F852DF* + ID_OUI_FROM_DATABASE=VNL Europe AB + +OUI:1CF061* + ID_OUI_FROM_DATABASE=SCAPS GmbH + +OUI:A893E6* + ID_OUI_FROM_DATABASE=JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LTD + +OUI:00267C* + ID_OUI_FROM_DATABASE=Metz-Werke GmbH & Co KG + +OUI:002676* + ID_OUI_FROM_DATABASE=COMMidt AS + +OUI:00266F* + ID_OUI_FROM_DATABASE=Coordiwise Technology Corp. + +OUI:002670* + ID_OUI_FROM_DATABASE=Cinch Connectors + +OUI:002663* + ID_OUI_FROM_DATABASE=Shenzhen Huitaiwei Tech. Ltd, co. + +OUI:0025CD* + ID_OUI_FROM_DATABASE=Skylane Optics + +OUI:0025C8* + ID_OUI_FROM_DATABASE=S-Access GmbH + +OUI:0025C7* + ID_OUI_FROM_DATABASE=altek Corporation + +OUI:0025C1* + ID_OUI_FROM_DATABASE=Nawoo Korea Corp. + +OUI:0025BA* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:0025B5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0025AE* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:0025A8* + ID_OUI_FROM_DATABASE=Kontron (BeiJing) Technology Co.,Ltd + +OUI:0025A7* + ID_OUI_FROM_DATABASE=Comverge, Inc. + +OUI:00262B* + ID_OUI_FROM_DATABASE=Wongs Electronics Co. Ltd. + +OUI:002625* + ID_OUI_FROM_DATABASE=MediaSputnik + +OUI:00261E* + ID_OUI_FROM_DATABASE=QINGBANG ELEC(SZ) CO., LTD + +OUI:002619* + ID_OUI_FROM_DATABASE=FRC + +OUI:002612* + ID_OUI_FROM_DATABASE=Space Exploration Technologies + +OUI:00260B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00260C* + ID_OUI_FROM_DATABASE=Dataram + +OUI:0025FF* + ID_OUI_FROM_DATABASE=CreNova Multimedia Co., Ltd + +OUI:002606* + ID_OUI_FROM_DATABASE=RAUMFELD GmbH + +OUI:0025F9* + ID_OUI_FROM_DATABASE=GMK electronic design GmbH + +OUI:0025A2* + ID_OUI_FROM_DATABASE=Alta Definicion LINCEO S.L. + +OUI:002596* + ID_OUI_FROM_DATABASE=GIGAVISION srl + +OUI:00259B* + ID_OUI_FROM_DATABASE=Beijing PKUNITY Microsystems Technology Co., Ltd + +OUI:002595* + ID_OUI_FROM_DATABASE=Northwest Signal Supply, Inc + +OUI:00258F* + ID_OUI_FROM_DATABASE=Trident Microsystems, Inc. + +OUI:002585* + ID_OUI_FROM_DATABASE=KOKUYO S&T Co., Ltd. + +OUI:00257B* + ID_OUI_FROM_DATABASE=STJ ELECTRONICS PVT LTD + +OUI:002574* + ID_OUI_FROM_DATABASE=KUNIMI MEDIA DEVICE Co., Ltd. + +OUI:00264F* + ID_OUI_FROM_DATABASE=Krüger &Gothe GmbH + +OUI:002648* + ID_OUI_FROM_DATABASE=Emitech Corp. + +OUI:002644* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:00263E* + ID_OUI_FROM_DATABASE=Trapeze Networks + +OUI:002638* + ID_OUI_FROM_DATABASE=Xia Men Joyatech Co., Ltd. + +OUI:00263D* + ID_OUI_FROM_DATABASE=MIA Corporation + +OUI:002631* + ID_OUI_FROM_DATABASE=COMMTACT LTD + +OUI:00256F* + ID_OUI_FROM_DATABASE=Dantherm Power + +OUI:002562* + ID_OUI_FROM_DATABASE=interbro Co. Ltd. + +OUI:00255C* + ID_OUI_FROM_DATABASE=NEC Corporation + +OUI:00254F* + ID_OUI_FROM_DATABASE=ELETTROLAB Srl + +OUI:002518* + ID_OUI_FROM_DATABASE=Power PLUS Communications AG + +OUI:002513* + ID_OUI_FROM_DATABASE=CXP DIGITAL BV + +OUI:00250C* + ID_OUI_FROM_DATABASE=Enertrac + +OUI:002505* + ID_OUI_FROM_DATABASE=eks Engel GmbH & Co. KG + +OUI:0024F9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0024F2* + ID_OUI_FROM_DATABASE=Uniphone Telecommunication Co., Ltd. + +OUI:0024ED* + ID_OUI_FROM_DATABASE=YT Elec. Co,.Ltd. + +OUI:0024E6* + ID_OUI_FROM_DATABASE=In Motion Technology Inc. + +OUI:0024E1* + ID_OUI_FROM_DATABASE=Convey Computer Corp. + +OUI:0024DF* + ID_OUI_FROM_DATABASE=Digitalbox Europe GmbH + +OUI:0024DA* + ID_OUI_FROM_DATABASE=Innovar Systems Limited + +OUI:002549* + ID_OUI_FROM_DATABASE=Jeorich Tech. Co.,Ltd. + +OUI:002538* + ID_OUI_FROM_DATABASE=Samsung Electronics Co., Ltd., Memory Division + +OUI:002542* + ID_OUI_FROM_DATABASE=Pittasoft + +OUI:002530* + ID_OUI_FROM_DATABASE=Aetas Systems Inc. + +OUI:002529* + ID_OUI_FROM_DATABASE=COMELIT GROUP S.P.A + +OUI:002522* + ID_OUI_FROM_DATABASE=ASRock Incorporation + +OUI:00251D* + ID_OUI_FROM_DATABASE=DSA Encore, LLC + +OUI:0025F5* + ID_OUI_FROM_DATABASE=DVS Korea, Co., Ltd + +OUI:0025F0* + ID_OUI_FROM_DATABASE=Suga Electronics Limited + +OUI:0025EA* + ID_OUI_FROM_DATABASE=Iphion BV + +OUI:0025E4* + ID_OUI_FROM_DATABASE=OMNI-WiFi, LLC + +OUI:0025E0* + ID_OUI_FROM_DATABASE=CeedTec Sdn Bhd + +OUI:0025DA* + ID_OUI_FROM_DATABASE=Secura Key + +OUI:0025D9* + ID_OUI_FROM_DATABASE=DataFab Systems Inc. + +OUI:002410* + ID_OUI_FROM_DATABASE=NUETEQ Technology,Inc. + +OUI:002409* + ID_OUI_FROM_DATABASE=The Toro Company + +OUI:0023F7* + ID_OUI_FROM_DATABASE=Private + +OUI:0023FD* + ID_OUI_FROM_DATABASE=AFT Atlas Fahrzeugtechnik GmbH + +OUI:0023F6* + ID_OUI_FROM_DATABASE=Softwell Technology Co., Ltd. + +OUI:0023EC* + ID_OUI_FROM_DATABASE=Algorithmix GmbH + +OUI:0023E7* + ID_OUI_FROM_DATABASE=Hinke A/S + +OUI:002387* + ID_OUI_FROM_DATABASE=ThinkFlood, Inc. + +OUI:002381* + ID_OUI_FROM_DATABASE=Lengda Technology(Xiamen) Co.,Ltd. + +OUI:00237B* + ID_OUI_FROM_DATABASE=WHDI LLC + +OUI:002372* + ID_OUI_FROM_DATABASE=MORE STAR INDUSTRIAL GROUP LIMITED + +OUI:0024CE* + ID_OUI_FROM_DATABASE=Exeltech Inc + +OUI:0024D3* + ID_OUI_FROM_DATABASE=QUALICA Inc. + +OUI:0024C7* + ID_OUI_FROM_DATABASE=Mobilarm Ltd + +OUI:0024C2* + ID_OUI_FROM_DATABASE=Asumo Co.,Ltd. + +OUI:0024BC* + ID_OUI_FROM_DATABASE=HuRob Co.,Ltd + +OUI:0024B7* + ID_OUI_FROM_DATABASE=GridPoint, Inc. + +OUI:0024AB* + ID_OUI_FROM_DATABASE=A7 Engineering, Inc. + +OUI:0024A6* + ID_OUI_FROM_DATABASE=TELESTAR DIGITAL GmbH + +OUI:00249A* + ID_OUI_FROM_DATABASE=Beijing Zhongchuang Telecommunication Test Co., Ltd. + +OUI:00249F* + ID_OUI_FROM_DATABASE=RIM Testing Services + +OUI:002487* + ID_OUI_FROM_DATABASE=Blackboard Inc. + +OUI:002498* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002485* + ID_OUI_FROM_DATABASE=ConteXtream Ltd + +OUI:002480* + ID_OUI_FROM_DATABASE=Meteocontrol GmbH + +OUI:002448* + ID_OUI_FROM_DATABASE=SpiderCloud Wireless, Inc + +OUI:00244A* + ID_OUI_FROM_DATABASE=Voyant International + +OUI:002449* + ID_OUI_FROM_DATABASE=Shen Zhen Lite Star Electronics Technology Co., Ltd + +OUI:002443* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002439* + ID_OUI_FROM_DATABASE=Digital Barriers Advanced Technologies + +OUI:002479* + ID_OUI_FROM_DATABASE=Optec Displays, Inc. + +OUI:00246D* + ID_OUI_FROM_DATABASE=Weinzierl Engineering GmbH + +OUI:002474* + ID_OUI_FROM_DATABASE=Autronica Fire And Securirty + +OUI:002468* + ID_OUI_FROM_DATABASE=Sumavision Technologies Co.,Ltd + +OUI:002466* + ID_OUI_FROM_DATABASE=Unitron nv + +OUI:002461* + ID_OUI_FROM_DATABASE=Shin Wang Tech. + +OUI:00245C* + ID_OUI_FROM_DATABASE=Design-Com Technologies Pty. Ltd. + +OUI:00244F* + ID_OUI_FROM_DATABASE=Asantron Technologies Ltd. + +OUI:0023BB* + ID_OUI_FROM_DATABASE=Schmitt Industries + +OUI:0023BA* + ID_OUI_FROM_DATABASE=Chroma + +OUI:0023B5* + ID_OUI_FROM_DATABASE=ORTANA LTD + +OUI:0023A8* + ID_OUI_FROM_DATABASE=Marshall Electronics + +OUI:00239B* + ID_OUI_FROM_DATABASE=Elster Solutions, LLC + +OUI:002396* + ID_OUI_FROM_DATABASE=ANDES TECHNOLOGY CORPORATION + +OUI:002391* + ID_OUI_FROM_DATABASE=Maxian + +OUI:00238C* + ID_OUI_FROM_DATABASE=Private + +OUI:002432* + ID_OUI_FROM_DATABASE=Neostar Technology Co.,LTD + +OUI:002429* + ID_OUI_FROM_DATABASE=MK MASTER INC. + +OUI:00241C* + ID_OUI_FROM_DATABASE=FuGang Electronic (DG) Co.,Ltd + +OUI:002428* + ID_OUI_FROM_DATABASE=EnergyICT + +OUI:002416* + ID_OUI_FROM_DATABASE=Any Use + +OUI:0023E0* + ID_OUI_FROM_DATABASE=INO Therapeutics LLC + +OUI:0023DA* + ID_OUI_FROM_DATABASE=Industrial Computer Source (Deutschland)GmbH + +OUI:0023C8* + ID_OUI_FROM_DATABASE=TEAM-R + +OUI:0023C7* + ID_OUI_FROM_DATABASE=AVSystem + +OUI:0023C1* + ID_OUI_FROM_DATABASE=Securitas Direct AB + +OUI:0021DC* + ID_OUI_FROM_DATABASE=TECNOALARM S.r.l. + +OUI:0021D6* + ID_OUI_FROM_DATABASE=LXI Consortium + +OUI:0021CF* + ID_OUI_FROM_DATABASE=The Crypto Group + +OUI:0021C9* + ID_OUI_FROM_DATABASE=Wavecom Asia Pacific Limited + +OUI:0021CA* + ID_OUI_FROM_DATABASE=ART System Co., Ltd. + +OUI:0021C3* + ID_OUI_FROM_DATABASE=CORNELL Communications, Inc. + +OUI:002334* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00232E* + ID_OUI_FROM_DATABASE=Kedah Electronics Engineering, LLC + +OUI:002329* + ID_OUI_FROM_DATABASE=DDRdrive LLC + +OUI:002322* + ID_OUI_FROM_DATABASE=KISS Teknical Solutions, Inc. + +OUI:002325* + ID_OUI_FROM_DATABASE=IOLAN Holding + +OUI:002319* + ID_OUI_FROM_DATABASE=Sielox LLC + +OUI:002270* + ID_OUI_FROM_DATABASE=ABK North America, LLC + +OUI:002317* + ID_OUI_FROM_DATABASE=Lasercraft Inc + +OUI:002310* + ID_OUI_FROM_DATABASE=LNC Technology Co., Ltd. + +OUI:002273* + ID_OUI_FROM_DATABASE=Techway + +OUI:002274* + ID_OUI_FROM_DATABASE=FamilyPhone AB + +OUI:00226F* + ID_OUI_FROM_DATABASE=3onedata Technology Co. Ltd. + +OUI:00226A* + ID_OUI_FROM_DATABASE=Honeywell + +OUI:002260* + ID_OUI_FROM_DATABASE=AFREEY Inc. + +OUI:00225B* + ID_OUI_FROM_DATABASE=Teradici Corporation + +OUI:002256* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002255* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00224D* + ID_OUI_FROM_DATABASE=MITAC INTERNATIONAL CORP. + +OUI:002252* + ID_OUI_FROM_DATABASE=ZOLL Lifecor Corporation + +OUI:002246* + ID_OUI_FROM_DATABASE=Evoc Intelligent Technology Co.,Ltd. + +OUI:002366* + ID_OUI_FROM_DATABASE=Beijing Siasun Electronic System Co.,Ltd. + +OUI:00236B* + ID_OUI_FROM_DATABASE=Xembedded, Inc. + +OUI:002359* + ID_OUI_FROM_DATABASE=Benchmark Electronics ( Thailand ) Public Company Limited + +OUI:00235F* + ID_OUI_FROM_DATABASE=Silicon Micro Sensors GmbH + +OUI:002353* + ID_OUI_FROM_DATABASE=F E T Elettronica snc + +OUI:00234C* + ID_OUI_FROM_DATABASE=KTC AB + +OUI:002304* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0022F3* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:0022EE* + ID_OUI_FROM_DATABASE=Algo Communication Products Ltd + +OUI:0022E7* + ID_OUI_FROM_DATABASE=WPS Parking Systems + +OUI:0022E1* + ID_OUI_FROM_DATABASE=ZORT Labs, LLC. + +OUI:0022E2* + ID_OUI_FROM_DATABASE=WABTEC Transit Division + +OUI:0022DB* + ID_OUI_FROM_DATABASE=Translogic Corporation + +OUI:0022A1* + ID_OUI_FROM_DATABASE=Huawei Symantec Technologies Co.,Ltd. + +OUI:00229B* + ID_OUI_FROM_DATABASE=AverLogic Technologies, Inc. + +OUI:00229C* + ID_OUI_FROM_DATABASE=Verismo Networks Inc + +OUI:002295* + ID_OUI_FROM_DATABASE=SGM Technology for lighting spa + +OUI:00228E* + ID_OUI_FROM_DATABASE=TV-NUMERIC + +OUI:002289* + ID_OUI_FROM_DATABASE=Optosecurity Inc. + +OUI:002282* + ID_OUI_FROM_DATABASE=8086 Consultancy + +OUI:00227C* + ID_OUI_FROM_DATABASE=Woori SMT Co.,ltd + +OUI:002279* + ID_OUI_FROM_DATABASE=Nippon Conlux Co., Ltd. + +OUI:00223C* + ID_OUI_FROM_DATABASE=RATIO Entwicklungen GmbH + +OUI:002236* + ID_OUI_FROM_DATABASE=VECTOR SP. Z O.O. + +OUI:002230* + ID_OUI_FROM_DATABASE=FutureLogic Inc. + +OUI:002229* + ID_OUI_FROM_DATABASE=Compumedics Ltd + +OUI:00221D* + ID_OUI_FROM_DATABASE=Freegene Technology LTD + +OUI:002224* + ID_OUI_FROM_DATABASE=Good Will Instrument Co., Ltd. + +OUI:002223* + ID_OUI_FROM_DATABASE=TimeKeeping Systems, Inc. + +OUI:002216* + ID_OUI_FROM_DATABASE=SHIBAURA VENDING MACHINE CORPORATION + +OUI:002217* + ID_OUI_FROM_DATABASE=Neat Electronics + +OUI:002211* + ID_OUI_FROM_DATABASE=Rohati Systems + +OUI:00220A* + ID_OUI_FROM_DATABASE=OnLive, Inc + +OUI:002204* + ID_OUI_FROM_DATABASE=KORATEK + +OUI:0021FF* + ID_OUI_FROM_DATABASE=Cyfrowy Polsat SA + +OUI:0021F5* + ID_OUI_FROM_DATABASE=Western Engravers Supply, Inc. + +OUI:0021EF* + ID_OUI_FROM_DATABASE=Kapsys + +OUI:0021EE* + ID_OUI_FROM_DATABASE=Full Spectrum Inc. + +OUI:0022D4* + ID_OUI_FROM_DATABASE=ComWorth Co., Ltd. + +OUI:0022CA* + ID_OUI_FROM_DATABASE=Anviz Biometric Tech. Co., Ltd. + +OUI:0022C5* + ID_OUI_FROM_DATABASE=INFORSON Co,Ltd. + +OUI:0022C0* + ID_OUI_FROM_DATABASE=Shenzhen Forcelink Electronic Co, Ltd + +OUI:0022BB* + ID_OUI_FROM_DATABASE=beyerdynamic GmbH & Co. KG + +OUI:0022AE* + ID_OUI_FROM_DATABASE=Mattel Inc. + +OUI:0022AD* + ID_OUI_FROM_DATABASE=TELESIS TECHNOLOGIES, INC. + +OUI:0022A8* + ID_OUI_FROM_DATABASE=Ouman Oy + +OUI:002132* + ID_OUI_FROM_DATABASE=Masterclock, Inc. + +OUI:00212C* + ID_OUI_FROM_DATABASE=SemIndia System Private Limited + +OUI:002131* + ID_OUI_FROM_DATABASE=Blynke Inc. + +OUI:00211F* + ID_OUI_FROM_DATABASE=SHINSUNG DELTATECH CO.,LTD. + +OUI:002120* + ID_OUI_FROM_DATABASE=Sequel Technologies + +OUI:002125* + ID_OUI_FROM_DATABASE=KUK JE TONG SHIN Co.,LTD + +OUI:002112* + ID_OUI_FROM_DATABASE=WISCOM SYSTEM CO.,LTD + +OUI:001FB9* + ID_OUI_FROM_DATABASE=Paltronics + +OUI:001FB7* + ID_OUI_FROM_DATABASE=WiMate Technologies Corp. + +OUI:001FB8* + ID_OUI_FROM_DATABASE=Universal Remote Control, Inc. + +OUI:001FB2* + ID_OUI_FROM_DATABASE=Sontheim Industrie Elektronik GmbH + +OUI:001FAB* + ID_OUI_FROM_DATABASE=I.S HIGH TECH.INC + +OUI:001FA6* + ID_OUI_FROM_DATABASE=Stilo srl + +OUI:001FA1* + ID_OUI_FROM_DATABASE=Gtran Inc + +OUI:001F9C* + ID_OUI_FROM_DATABASE=LEDCO + +OUI:00215E* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:002151* + ID_OUI_FROM_DATABASE=Millinet Co., Ltd. + +OUI:002152* + ID_OUI_FROM_DATABASE=General Satellite Research & Development Limited + +OUI:002157* + ID_OUI_FROM_DATABASE=National Datacast, Inc. + +OUI:00214B* + ID_OUI_FROM_DATABASE=Shenzhen HAMP Science & Technology Co.,Ltd + +OUI:002145* + ID_OUI_FROM_DATABASE=Semptian Technologies Ltd. + +OUI:002144* + ID_OUI_FROM_DATABASE=SS Telecoms + +OUI:00213C* + ID_OUI_FROM_DATABASE=AliphCom + +OUI:00213B* + ID_OUI_FROM_DATABASE=Berkshire Products, Inc + +OUI:002190* + ID_OUI_FROM_DATABASE=Goliath Solutions + +OUI:002189* + ID_OUI_FROM_DATABASE=AppTech, Inc. + +OUI:002184* + ID_OUI_FROM_DATABASE=POWERSOFT SRL + +OUI:002183* + ID_OUI_FROM_DATABASE=VATECH HYDRO + +OUI:00217D* + ID_OUI_FROM_DATABASE=PYXIS S.R.L. + +OUI:002177* + ID_OUI_FROM_DATABASE=W. L. Gore & Associates + +OUI:002176* + ID_OUI_FROM_DATABASE=YMax Telecom Ltd. + +OUI:002171* + ID_OUI_FROM_DATABASE=Wesung TNC Co., Ltd. + +OUI:002164* + ID_OUI_FROM_DATABASE=Special Design Bureau for Seismic Instrumentation + +OUI:002103* + ID_OUI_FROM_DATABASE=GHI Electronics, LLC + +OUI:001FFA* + ID_OUI_FROM_DATABASE=Coretree, Co, Ltd + +OUI:001FF5* + ID_OUI_FROM_DATABASE=Kongsberg Defence & Aerospace + +OUI:001FF4* + ID_OUI_FROM_DATABASE=Power Monitors, Inc. + +OUI:001FEE* + ID_OUI_FROM_DATABASE=ubisys technologies GmbH + +OUI:001FE7* + ID_OUI_FROM_DATABASE=Simet + +OUI:001FDB* + ID_OUI_FROM_DATABASE=Network Supply Corp., + +OUI:001FD1* + ID_OUI_FROM_DATABASE=OPTEX CO.,LTD. + +OUI:001FCA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001FBE* + ID_OUI_FROM_DATABASE=Shenzhen Mopnet Industrial Co.,Ltd + +OUI:001F62* + ID_OUI_FROM_DATABASE=JSC Stilsoft + +OUI:001F67* + ID_OUI_FROM_DATABASE=Hitachi,Ltd. + +OUI:001F55* + ID_OUI_FROM_DATABASE=Honeywell Security (China) Co., Ltd. + +OUI:001F56* + ID_OUI_FROM_DATABASE=DIGITAL FORECAST + +OUI:001F4F* + ID_OUI_FROM_DATABASE=Thinkware Co. Ltd. + +OUI:001F48* + ID_OUI_FROM_DATABASE=Mojix Inc. + +OUI:001F43* + ID_OUI_FROM_DATABASE=ENTES ELEKTRONIK + +OUI:001F8E* + ID_OUI_FROM_DATABASE=Metris USA Inc. + +OUI:001F88* + ID_OUI_FROM_DATABASE=FMS Force Measuring Systems AG + +OUI:001F81* + ID_OUI_FROM_DATABASE=Accel Semiconductor Corp + +OUI:001B58* + ID_OUI_FROM_DATABASE=ACE CAD Enterprise Co., Ltd. + +OUI:001F78* + ID_OUI_FROM_DATABASE=Blue Fox Porini Textile + +OUI:001F6E* + ID_OUI_FROM_DATABASE=Vtech Engineering Corporation + +OUI:001F68* + ID_OUI_FROM_DATABASE=Martinsson Elektronik AB + +OUI:0021BC* + ID_OUI_FROM_DATABASE=ZALA COMPUTER + +OUI:0021B7* + ID_OUI_FROM_DATABASE=Lexmark International Inc. + +OUI:0021B0* + ID_OUI_FROM_DATABASE=Tyco Telecommunications + +OUI:0021A4* + ID_OUI_FROM_DATABASE=Dbii Networks + +OUI:00219A* + ID_OUI_FROM_DATABASE=Cambridge Visual Networks Ltd + +OUI:002196* + ID_OUI_FROM_DATABASE=Telsey S.p.A. + +OUI:001E4B* + ID_OUI_FROM_DATABASE=City Theatrical + +OUI:001E47* + ID_OUI_FROM_DATABASE=PT. Hariff Daya Tunggal Engineering + +OUI:001E41* + ID_OUI_FROM_DATABASE=Microwave Communication & Component, Inc. + +OUI:001E2E* + ID_OUI_FROM_DATABASE=SIRTI S.p.A. + +OUI:001E27* + ID_OUI_FROM_DATABASE=SBN TECH Co.,Ltd. + +OUI:001E28* + ID_OUI_FROM_DATABASE=Lumexis Corporation + +OUI:001DF2* + ID_OUI_FROM_DATABASE=Netflix, Inc. + +OUI:001DEB* + ID_OUI_FROM_DATABASE=DINEC International + +OUI:001DEC* + ID_OUI_FROM_DATABASE=Marusys + +OUI:001DE6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001DDA* + ID_OUI_FROM_DATABASE=Mikroelektronika spol. s r. o. + +OUI:001DDF* + ID_OUI_FROM_DATABASE=Sunitec Enterprise Co., Ltd. + +OUI:001DCC* + ID_OUI_FROM_DATABASE=Hetra Secure Solutions + +OUI:001DC7* + ID_OUI_FROM_DATABASE=L-3 Communications Geneva Aerospace + +OUI:001DC0* + ID_OUI_FROM_DATABASE=Enphase Energy + +OUI:001ED8* + ID_OUI_FROM_DATABASE=Digital United Inc. + +OUI:001ED2* + ID_OUI_FROM_DATABASE=Ray Shine Video Technology Inc + +OUI:001ED1* + ID_OUI_FROM_DATABASE=Keyprocessor B.V. + +OUI:001ECC* + ID_OUI_FROM_DATABASE=CDVI + +OUI:001EC5* + ID_OUI_FROM_DATABASE=Middle Atlantic Products Inc + +OUI:001EBF* + ID_OUI_FROM_DATABASE=Haas Automation Inc. + +OUI:001EB9* + ID_OUI_FROM_DATABASE=Sing Fai Technology Limited + +OUI:001EB2* + ID_OUI_FROM_DATABASE=LG innotek + +OUI:001F2E* + ID_OUI_FROM_DATABASE=Triangle Research Int'l Pte Ltd + +OUI:001F2D* + ID_OUI_FROM_DATABASE=Electro-Optical Imaging, Inc. + +OUI:001F27* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001F14* + ID_OUI_FROM_DATABASE=NexG + +OUI:001F1B* + ID_OUI_FROM_DATABASE=RoyalTek Company Ltd. + +OUI:001F0D* + ID_OUI_FROM_DATABASE=L3 Communications - Telemetry West + +OUI:001F0E* + ID_OUI_FROM_DATABASE=Japan Kyastem Co., Ltd + +OUI:001E22* + ID_OUI_FROM_DATABASE=ARVOO Imaging Products BV + +OUI:001E1B* + ID_OUI_FROM_DATABASE=Digital Stream Technology, Inc. + +OUI:001E16* + ID_OUI_FROM_DATABASE=Keytronix + +OUI:001E15* + ID_OUI_FROM_DATABASE=Beech Hill Electronics + +OUI:001E11* + ID_OUI_FROM_DATABASE=ELELUX INTERNATIONAL LTD + +OUI:001E05* + ID_OUI_FROM_DATABASE=Xseed Technologies & Computing + +OUI:001E0C* + ID_OUI_FROM_DATABASE=Sherwood Information Partners, Inc. + +OUI:001DFE* + ID_OUI_FROM_DATABASE=Palm, Inc + +OUI:001DF9* + ID_OUI_FROM_DATABASE=Cybiotronics (Far East) Limited + +OUI:001EAD* + ID_OUI_FROM_DATABASE=Wingtech Group Limited + +OUI:001EA2* + ID_OUI_FROM_DATABASE=Symx Systems, Inc. + +OUI:001EA7* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:001EA1* + ID_OUI_FROM_DATABASE=Brunata a/s + +OUI:001E9B* + ID_OUI_FROM_DATABASE=San-Eisha, Ltd. + +OUI:001E94* + ID_OUI_FROM_DATABASE=SUPERCOM TECHNOLOGY CORPORATION + +OUI:001E8F* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:001E87* + ID_OUI_FROM_DATABASE=Realease Limited + +OUI:001E80* + ID_OUI_FROM_DATABASE=Last Mile Ltd. + +OUI:001EFC* + ID_OUI_FROM_DATABASE=JSC MASSA-K + +OUI:001F08* + ID_OUI_FROM_DATABASE=RISCO LTD + +OUI:001EF5* + ID_OUI_FROM_DATABASE=Hitek Automated Inc. + +OUI:001EFB* + ID_OUI_FROM_DATABASE=Trio Motion Technology Ltd + +OUI:001EE9* + ID_OUI_FROM_DATABASE=Stoneridge Electronics AB + +OUI:001EEE* + ID_OUI_FROM_DATABASE=ETL Systems Ltd + +OUI:001E7B* + ID_OUI_FROM_DATABASE=R.I.CO. S.r.l. + +OUI:001E76* + ID_OUI_FROM_DATABASE=Thermo Fisher Scientific + +OUI:001E6A* + ID_OUI_FROM_DATABASE=Beijing Bluexon Technology Co.,Ltd + +OUI:001E71* + ID_OUI_FROM_DATABASE=MIrcom Group of Companies + +OUI:001E63* + ID_OUI_FROM_DATABASE=Vibro-Meter SA + +OUI:001E5E* + ID_OUI_FROM_DATABASE=COmputime Ltd. + +OUI:001E57* + ID_OUI_FROM_DATABASE=ALCOMA, spol. s r.o. + +OUI:001E51* + ID_OUI_FROM_DATABASE=Converter Industry Srl + +OUI:001DB9* + ID_OUI_FROM_DATABASE=Wellspring Wireless + +OUI:001DB4* + ID_OUI_FROM_DATABASE=KUMHO ENG CO.,LTD + +OUI:001D9E* + ID_OUI_FROM_DATABASE=AXION TECHNOLOGIES + +OUI:001DA3* + ID_OUI_FROM_DATABASE=SabiOso + +OUI:001D9D* + ID_OUI_FROM_DATABASE=ARTJOY INTERNATIONAL LIMITED + +OUI:001D45* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D3E* + ID_OUI_FROM_DATABASE=SAKA TECHNO SCIENCE CO.,LTD + +OUI:001D37* + ID_OUI_FROM_DATABASE=Thales-Panda Transportation System + +OUI:001D38* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:001D32* + ID_OUI_FROM_DATABASE=Longkay Communication & Technology (Shanghai) Co. Ltd + +OUI:001D2B* + ID_OUI_FROM_DATABASE=Wuhan Pont Technology CO. , LTD + +OUI:001D1F* + ID_OUI_FROM_DATABASE=Siauliu Tauro Televizoriai, JSC + +OUI:001D26* + ID_OUI_FROM_DATABASE=Rockridgesound Technology Co. + +OUI:001D1A* + ID_OUI_FROM_DATABASE=OvisLink S.A. + +OUI:001D7A* + ID_OUI_FROM_DATABASE=Wideband Semiconductor, Inc. + +OUI:001D74* + ID_OUI_FROM_DATABASE=Tianjin China-Silicon Microelectronics Co., Ltd. + +OUI:001D62* + ID_OUI_FROM_DATABASE=InPhase Technologies + +OUI:001D61* + ID_OUI_FROM_DATABASE=BIJ Corporation + +OUI:001D5B* + ID_OUI_FROM_DATABASE=Tecvan Informática Ltda + +OUI:001D54* + ID_OUI_FROM_DATABASE=Sunnic Technology & Merchandise INC. + +OUI:001D4A* + ID_OUI_FROM_DATABASE=Carestream Health, Inc. + +OUI:001CE8* + ID_OUI_FROM_DATABASE=Cummins Inc + +OUI:001CE4* + ID_OUI_FROM_DATABASE=EleSy JSC + +OUI:001CDD* + ID_OUI_FROM_DATABASE=COWBELL ENGINEERING CO., LTD. + +OUI:001CDE* + ID_OUI_FROM_DATABASE=Interactive Multimedia eXchange Inc. + +OUI:001CD8* + ID_OUI_FROM_DATABASE=BlueAnt Wireless + +OUI:001CD1* + ID_OUI_FROM_DATABASE=Waves Audio LTD + +OUI:001CCB* + ID_OUI_FROM_DATABASE=Forth Corporation Public Company Limited + +OUI:001CC5* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:001D14* + ID_OUI_FROM_DATABASE=SPERADTONE INFORMATION TECHNOLOGY LIMITED + +OUI:001D07* + ID_OUI_FROM_DATABASE=Shenzhen Sang Fei Consumer Communications Co.,Ltd + +OUI:001D01* + ID_OUI_FROM_DATABASE=Neptune Digital + +OUI:001CFA* + ID_OUI_FROM_DATABASE=Alarm.com + +OUI:001CEE* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:001CF5* + ID_OUI_FROM_DATABASE=Wiseblue Technology Limited + +OUI:001CB9* + ID_OUI_FROM_DATABASE=KWANG SUNG ELECTRONICS CO., LTD. + +OUI:001CAF* + ID_OUI_FROM_DATABASE=Plato Networks Inc. + +OUI:001CB4* + ID_OUI_FROM_DATABASE=Iridium Satellite LLC + +OUI:001C9F* + ID_OUI_FROM_DATABASE=Razorstream, LLC + +OUI:001C99* + ID_OUI_FROM_DATABASE=Shunra Software Ltd. + +OUI:001C8C* + ID_OUI_FROM_DATABASE=DIAL TECHNOLOGY LTD. + +OUI:001C93* + ID_OUI_FROM_DATABASE=ExaDigm Inc + +OUI:001C87* + ID_OUI_FROM_DATABASE=Uriver Inc. + +OUI:001C82* + ID_OUI_FROM_DATABASE=Genew Technologies + +OUI:001C1A* + ID_OUI_FROM_DATABASE=Thomas Instrumentation, Inc + +OUI:001C0E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001C13* + ID_OUI_FROM_DATABASE=OPTSYS TECHNOLOGY CO., LTD. + +OUI:001C07* + ID_OUI_FROM_DATABASE=Cwlinux Limited + +OUI:001C00* + ID_OUI_FROM_DATABASE=Entry Point, LLC + +OUI:001BF4* + ID_OUI_FROM_DATABASE=KENWIN INDUSTRIAL(HK) LTD. + +OUI:001BEF* + ID_OUI_FROM_DATABASE=Blossoms Digital Technology Co.,Ltd. + +OUI:001BE2* + ID_OUI_FROM_DATABASE=AhnLab,Inc. + +OUI:001C7D* + ID_OUI_FROM_DATABASE=Excelpoint Manufacturing Pte Ltd + +OUI:001C73* + ID_OUI_FROM_DATABASE=Arista Networks, Inc. + +OUI:001C78* + ID_OUI_FROM_DATABASE=WYPLAY SAS + +OUI:001C65* + ID_OUI_FROM_DATABASE=JoeScan, Inc. + +OUI:001C67* + ID_OUI_FROM_DATABASE=Pumpkin Networks, Inc. + +OUI:001C66* + ID_OUI_FROM_DATABASE=UCAMP CO.,LTD + +OUI:001C60* + ID_OUI_FROM_DATABASE=CSP Frontier Technologies,Inc. + +OUI:001C54* + ID_OUI_FROM_DATABASE=Hillstone Networks Inc + +OUI:001C59* + ID_OUI_FROM_DATABASE=DEVON IT + +OUI:001C4F* + ID_OUI_FROM_DATABASE=MACAB AB + +OUI:001C37* + ID_OUI_FROM_DATABASE=Callpod, Inc. + +OUI:001C3C* + ID_OUI_FROM_DATABASE=Seon Design Inc. + +OUI:001C30* + ID_OUI_FROM_DATABASE=Mode Lighting (UK ) Ltd. + +OUI:001C2B* + ID_OUI_FROM_DATABASE=Alertme.com Limited + +OUI:001C2A* + ID_OUI_FROM_DATABASE=Envisacor Technologies Inc. + +OUI:001C29* + ID_OUI_FROM_DATABASE=CORE DIGITAL ELECTRONICS CO., LTD + +OUI:001C24* + ID_OUI_FROM_DATABASE=Formosa Wireless Systems Corp. + +OUI:001C1F* + ID_OUI_FROM_DATABASE=Quest Retail Technology Pty Ltd + +OUI:001D97* + ID_OUI_FROM_DATABASE=Alertus Technologies LLC + +OUI:001D90* + ID_OUI_FROM_DATABASE=EMCO Flow Systems + +OUI:001D84* + ID_OUI_FROM_DATABASE=Gateway, Inc. + +OUI:001D67* + ID_OUI_FROM_DATABASE=AMEC + +OUI:001A93* + ID_OUI_FROM_DATABASE=ERCO Leuchten GmbH + +OUI:001A98* + ID_OUI_FROM_DATABASE=Asotel Communication Limited Taiwan Branch + +OUI:001A8E* + ID_OUI_FROM_DATABASE=3Way Networks Ltd + +OUI:001A7D* + ID_OUI_FROM_DATABASE=cyber-blue(HK)Ltd + +OUI:001A82* + ID_OUI_FROM_DATABASE=PROBA Building Automation Co.,LTD + +OUI:001A7C* + ID_OUI_FROM_DATABASE=Hirschmann Multimedia B.V. + +OUI:001A78* + ID_OUI_FROM_DATABASE=ubtos + +OUI:001A7B* + ID_OUI_FROM_DATABASE=Teleco, Inc. + +OUI:001A71* + ID_OUI_FROM_DATABASE=Diostech Co., Ltd. + +OUI:001A6C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A65* + ID_OUI_FROM_DATABASE=Seluxit + +OUI:001B7D* + ID_OUI_FROM_DATABASE=CXR Anderson Jacobson + +OUI:001B71* + ID_OUI_FROM_DATABASE=Telular Corp. + +OUI:001B6A* + ID_OUI_FROM_DATABASE=Powerwave Technologies Sweden AB + +OUI:001B65* + ID_OUI_FROM_DATABASE=China Gridcom Co., Ltd + +OUI:001B5E* + ID_OUI_FROM_DATABASE=BPL Limited + +OUI:001B57* + ID_OUI_FROM_DATABASE=SEMINDIA SYSTEMS PRIVATE LIMITED + +OUI:001B46* + ID_OUI_FROM_DATABASE=Blueone Technology Co.,Ltd + +OUI:001B4B* + ID_OUI_FROM_DATABASE=SANION Co., Ltd. + +OUI:001BAD* + ID_OUI_FROM_DATABASE=iControl Incorporated + +OUI:001BA6* + ID_OUI_FROM_DATABASE=intotech inc. + +OUI:001BA1* + ID_OUI_FROM_DATABASE=Åmic AB + +OUI:001B93* + ID_OUI_FROM_DATABASE=JC Decaux SA DNT + +OUI:001B95* + ID_OUI_FROM_DATABASE=VIDEO SYSTEMS SRL + +OUI:001B9A* + ID_OUI_FROM_DATABASE=Apollo Fire Detectors Ltd + +OUI:001B94* + ID_OUI_FROM_DATABASE=T.E.M.A. S.p.A. + +OUI:001B8E* + ID_OUI_FROM_DATABASE=Hulu Sweden AB + +OUI:001B89* + ID_OUI_FROM_DATABASE=EMZA Visual Sense Ltd. + +OUI:001B8A* + ID_OUI_FROM_DATABASE=2M Electronic A/S + +OUI:001B84* + ID_OUI_FROM_DATABASE=Scan Engineering Telecom + +OUI:001BD1* + ID_OUI_FROM_DATABASE=SOGESTMATIC + +OUI:001BD6* + ID_OUI_FROM_DATABASE=Kelvin Hughes Ltd + +OUI:001BCF* + ID_OUI_FROM_DATABASE=Dataupia Corporation + +OUI:001BD0* + ID_OUI_FROM_DATABASE=IDENTEC SOLUTIONS + +OUI:001BCA* + ID_OUI_FROM_DATABASE=Beijing Run Technology LTD. Company + +OUI:001BC3* + ID_OUI_FROM_DATABASE=Mobisolution Co.,Ltd + +OUI:001BBE* + ID_OUI_FROM_DATABASE=ICOP Digital + +OUI:001BB4* + ID_OUI_FROM_DATABASE=Airvod Limited + +OUI:001B14* + ID_OUI_FROM_DATABASE=Carex Lighting Equipment Factory + +OUI:001B0D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B06* + ID_OUI_FROM_DATABASE=Ateliers R. LAUMONIER + +OUI:001B08* + ID_OUI_FROM_DATABASE=Danfoss Drives A/S + +OUI:001B01* + ID_OUI_FROM_DATABASE=Applied Radio Technologies + +OUI:001AF5* + ID_OUI_FROM_DATABASE=PENTAONE. CO., LTD. + +OUI:001AFA* + ID_OUI_FROM_DATABASE=Welch Allyn, Inc. + +OUI:001AE4* + ID_OUI_FROM_DATABASE=Medicis Technologies Corporation + +OUI:001ADD* + ID_OUI_FROM_DATABASE=PePWave Ltd + +OUI:001AD1* + ID_OUI_FROM_DATABASE=FARGO CO., LTD. + +OUI:001AD8* + ID_OUI_FROM_DATABASE=AlsterAero GmbH + +OUI:001ACA* + ID_OUI_FROM_DATABASE=Tilera Corporation + +OUI:001ACC* + ID_OUI_FROM_DATABASE=Celestial Semiconductor, Ltd + +OUI:001AC5* + ID_OUI_FROM_DATABASE=BreakingPoint Systems, Inc. + +OUI:001ABB* + ID_OUI_FROM_DATABASE=Fontal Technology Incorporation + +OUI:001AC0* + ID_OUI_FROM_DATABASE=JOYBIEN TECHNOLOGIES CO., LTD. + +OUI:001A60* + ID_OUI_FROM_DATABASE=Wave Electronics Co.,Ltd. + +OUI:001A55* + ID_OUI_FROM_DATABASE=ACA-Digital Corporation + +OUI:001A5A* + ID_OUI_FROM_DATABASE=Korea Electric Power Data Network (KDN) Co., Ltd + +OUI:001A4E* + ID_OUI_FROM_DATABASE=NTI AG / LinMot + +OUI:001A53* + ID_OUI_FROM_DATABASE=Zylaya + +OUI:001A42* + ID_OUI_FROM_DATABASE=Techcity Technology co., Ltd. + +OUI:001A47* + ID_OUI_FROM_DATABASE=Agami Systems, Inc. + +OUI:001A3B* + ID_OUI_FROM_DATABASE=Doah Elecom Inc. + +OUI:001B3F* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:001B3A* + ID_OUI_FROM_DATABASE=SIMS Corp. + +OUI:001B2C* + ID_OUI_FROM_DATABASE=ATRON electronic GmbH + +OUI:001B27* + ID_OUI_FROM_DATABASE=Merlin CSI + +OUI:001B20* + ID_OUI_FROM_DATABASE=TPine Technology + +OUI:001B19* + ID_OUI_FROM_DATABASE=IEEE I&M Society TC9 + +OUI:001AB4* + ID_OUI_FROM_DATABASE=FFEI Ltd. + +OUI:001AAF* + ID_OUI_FROM_DATABASE=BLUSENS TECHNOLOGY + +OUI:001AA8* + ID_OUI_FROM_DATABASE=Mamiya Digital Imaging Co., Ltd. + +OUI:001A9F* + ID_OUI_FROM_DATABASE=A-Link Ltd + +OUI:001AA6* + ID_OUI_FROM_DATABASE=Telefunken Radio Communication Systems GmbH &CO.KG + +OUI:00193F* + ID_OUI_FROM_DATABASE=RDI technology(Shenzhen) Co.,LTD + +OUI:001933* + ID_OUI_FROM_DATABASE=Strix Systems, Inc. + +OUI:001938* + ID_OUI_FROM_DATABASE=UMB Communications Co., Ltd. + +OUI:00192D* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:001926* + ID_OUI_FROM_DATABASE=BitsGen Co., Ltd. + +OUI:001928* + ID_OUI_FROM_DATABASE=Siemens AG, Transportation Systems + +OUI:00190E* + ID_OUI_FROM_DATABASE=Atech Technology Co., Ltd. + +OUI:001913* + ID_OUI_FROM_DATABASE=Chuang-Yi Network Equipment Co.Ltd. + +OUI:001915* + ID_OUI_FROM_DATABASE=TECOM Co., Ltd. + +OUI:00191A* + ID_OUI_FROM_DATABASE=IRLINK + +OUI:001993* + ID_OUI_FROM_DATABASE=Changshu Switchgear MFG. Co.,Ltd. (Former Changshu Switchgea + +OUI:001998* + ID_OUI_FROM_DATABASE=SATO CORPORATION + +OUI:00198E* + ID_OUI_FROM_DATABASE=Oticon A/S + +OUI:001980* + ID_OUI_FROM_DATABASE=Gridpoint Systems + +OUI:001987* + ID_OUI_FROM_DATABASE=Panasonic Mobile Communications Co., Ltd. + +OUI:00197B* + ID_OUI_FROM_DATABASE=Picotest Corp. + +OUI:001968* + ID_OUI_FROM_DATABASE=Digital Video Networks(Shanghai) CO. LTD. + +OUI:00196D* + ID_OUI_FROM_DATABASE=Raybit Systems Korea, Inc + +OUI:00196F* + ID_OUI_FROM_DATABASE=SensoPart GmbH + +OUI:001952* + ID_OUI_FROM_DATABASE=ACOGITO Co., Ltd + +OUI:001957* + ID_OUI_FROM_DATABASE=Saafnet Canada Inc. + +OUI:001946* + ID_OUI_FROM_DATABASE=Cianet Industria e Comercio S/A + +OUI:001944* + ID_OUI_FROM_DATABASE=Fossil Partners, L.P. + +OUI:001A2F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A36* + ID_OUI_FROM_DATABASE=Aipermon GmbH & Co. KG + +OUI:001A25* + ID_OUI_FROM_DATABASE=DELTA DORE + +OUI:001A17* + ID_OUI_FROM_DATABASE=Teak Technologies, Inc. + +OUI:001A19* + ID_OUI_FROM_DATABASE=Computer Engineering Limited + +OUI:001A12* + ID_OUI_FROM_DATABASE=Essilor + +OUI:001A0B* + ID_OUI_FROM_DATABASE=BONA TECHNOLOGY INC. + +OUI:001A06* + ID_OUI_FROM_DATABASE=OpVista, Inc. + +OUI:0018CD* + ID_OUI_FROM_DATABASE=Erae Electronics Industry Co., Ltd + +OUI:0018D2* + ID_OUI_FROM_DATABASE=High-Gain Antennas LLC + +OUI:0018D9* + ID_OUI_FROM_DATABASE=Santosha Internatonal, Inc + +OUI:0018C1* + ID_OUI_FROM_DATABASE=Almitec Informática e Comércio + +OUI:0018C8* + ID_OUI_FROM_DATABASE=ISONAS Inc. + +OUI:0018BC* + ID_OUI_FROM_DATABASE=ZAO NVP Bolid + +OUI:0018B5* + ID_OUI_FROM_DATABASE=Magna Carta + +OUI:0018AE* + ID_OUI_FROM_DATABASE=TVT CO.,LTD + +OUI:001902* + ID_OUI_FROM_DATABASE=Cambridge Consultants Ltd + +OUI:001907* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018FD* + ID_OUI_FROM_DATABASE=Optimal Technologies International Inc. + +OUI:0018F1* + ID_OUI_FROM_DATABASE=Chunichi Denshi Co.,LTD. + +OUI:0018EA* + ID_OUI_FROM_DATABASE=Alltec GmbH + +OUI:0018EC* + ID_OUI_FROM_DATABASE=Welding Technology Corporation + +OUI:0018E5* + ID_OUI_FROM_DATABASE=Adhoco AG + +OUI:0018A2* + ID_OUI_FROM_DATABASE=XIP Technology AB + +OUI:0018A9* + ID_OUI_FROM_DATABASE=Ethernet Direct Corporation + +OUI:00189D* + ID_OUI_FROM_DATABASE=Navcast Inc. + +OUI:001893* + ID_OUI_FROM_DATABASE=SHENZHEN PHOTON BROADBAND TECHNOLOGY CO.,LTD + +OUI:001898* + ID_OUI_FROM_DATABASE=KINGSTATE ELECTRONICS CORPORATION + +OUI:001891* + ID_OUI_FROM_DATABASE=Zhongshan General K-mate Electronics Co., Ltd + +OUI:001885* + ID_OUI_FROM_DATABASE=Avigilon Corporation + +OUI:00188C* + ID_OUI_FROM_DATABASE=Mobile Action Technology Inc. + +OUI:0019C8* + ID_OUI_FROM_DATABASE=AnyDATA Corporation + +OUI:0019C3* + ID_OUI_FROM_DATABASE=Qualitrol + +OUI:0019BE* + ID_OUI_FROM_DATABASE=Altai Technologies Limited + +OUI:0019BC* + ID_OUI_FROM_DATABASE=ELECTRO CHANCE SRL + +OUI:0019A4* + ID_OUI_FROM_DATABASE=Austar Technology (hang zhou) Co.,Ltd + +OUI:0019A9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0019AB* + ID_OUI_FROM_DATABASE=Raycom CO ., LTD + +OUI:0019B0* + ID_OUI_FROM_DATABASE=HanYang System + +OUI:0019FA* + ID_OUI_FROM_DATABASE=Cable Vision Electronics CO., LTD. + +OUI:0019FF* + ID_OUI_FROM_DATABASE=Finnzymes + +OUI:0019EC* + ID_OUI_FROM_DATABASE=Sagamore Systems, Inc. + +OUI:0019F3* + ID_OUI_FROM_DATABASE=Cetis, Inc + +OUI:0019F8* + ID_OUI_FROM_DATABASE=Embedded Systems Design, Inc. + +OUI:0019E5* + ID_OUI_FROM_DATABASE=Lynx Studio Technology, Inc. + +OUI:0019E7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0019CD* + ID_OUI_FROM_DATABASE=Chengdu ethercom information technology Ltd. + +OUI:0019D4* + ID_OUI_FROM_DATABASE=ICX Technologies + +OUI:0019D9* + ID_OUI_FROM_DATABASE=Zeutschel GmbH + +OUI:001823* + ID_OUI_FROM_DATABASE=Delta Electronics, Inc. + +OUI:001817* + ID_OUI_FROM_DATABASE=D. E. Shaw Research, LLC + +OUI:00181E* + ID_OUI_FROM_DATABASE=GDX Technologies Ltd. + +OUI:001812* + ID_OUI_FROM_DATABASE=Beijing Xinwei Telecom Technology Co., Ltd. + +OUI:001806* + ID_OUI_FROM_DATABASE=Hokkei Industries Co., Ltd. + +OUI:00180B* + ID_OUI_FROM_DATABASE=Brilliant Telecommunications + +OUI:001805* + ID_OUI_FROM_DATABASE=Beijing InHand Networking Technology Co.,Ltd. + +OUI:0017B8* + ID_OUI_FROM_DATABASE=NOVATRON CO., LTD. + +OUI:0017BD* + ID_OUI_FROM_DATABASE=Tibetsystem + +OUI:0017B1* + ID_OUI_FROM_DATABASE=ACIST Medical Systems, Inc. + +OUI:0017AA* + ID_OUI_FROM_DATABASE=elab-experience inc. + +OUI:0017AC* + ID_OUI_FROM_DATABASE=O'Neil Product Development Inc. + +OUI:0017A5* + ID_OUI_FROM_DATABASE=Ralink Technology Corp + +OUI:0017A0* + ID_OUI_FROM_DATABASE=RoboTech srl + +OUI:00179B* + ID_OUI_FROM_DATABASE=Chant Sincere CO., LTD. + +OUI:00170F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001705* + ID_OUI_FROM_DATABASE=Methode Electronics + +OUI:00170A* + ID_OUI_FROM_DATABASE=INEW DIGITAL COMPANY + +OUI:0016F9* + ID_OUI_FROM_DATABASE=CETRTA POT, d.o.o., Kranj + +OUI:0016F7* + ID_OUI_FROM_DATABASE=L-3 Communications, Aviation Recorders + +OUI:0016E6* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:00178F* + ID_OUI_FROM_DATABASE=NINGBO YIDONG ELECTRONIC CO.,LTD. + +OUI:001794* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00178D* + ID_OUI_FROM_DATABASE=Checkpoint Systems, Inc. + +OUI:00177C* + ID_OUI_FROM_DATABASE=Smartlink Network Systems Limited + +OUI:001781* + ID_OUI_FROM_DATABASE=Greystone Data System, Inc. + +OUI:001788* + ID_OUI_FROM_DATABASE=Philips Lighting BV + +OUI:00176C* + ID_OUI_FROM_DATABASE=Pivot3, Inc. + +OUI:001770* + ID_OUI_FROM_DATABASE=Arti Industrial Electronics Ltd. + +OUI:001775* + ID_OUI_FROM_DATABASE=TTE Germany GmbH + +OUI:001760* + ID_OUI_FROM_DATABASE=Naito Densei Machida MFG.CO.,LTD + +OUI:001767* + ID_OUI_FROM_DATABASE=Earforce AS + +OUI:00185A* + ID_OUI_FROM_DATABASE=uControl, Inc. + +OUI:00185F* + ID_OUI_FROM_DATABASE=TAC Inc. + +OUI:001861* + ID_OUI_FROM_DATABASE=Ooma, Inc. + +OUI:001866* + ID_OUI_FROM_DATABASE=Leutron Vision + +OUI:001853* + ID_OUI_FROM_DATABASE=Atera Networks LTD. + +OUI:00184E* + ID_OUI_FROM_DATABASE=Lianhe Technologies, Inc. + +OUI:001847* + ID_OUI_FROM_DATABASE=AceNet Technology Inc. + +OUI:00183B* + ID_OUI_FROM_DATABASE=CENITS Co., Ltd. + +OUI:001840* + ID_OUI_FROM_DATABASE=3 Phoenix, Inc. + +OUI:001842* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001825* + ID_OUI_FROM_DATABASE=Private + +OUI:00182A* + ID_OUI_FROM_DATABASE=Taiwan Video & Monitor + +OUI:001836* + ID_OUI_FROM_DATABASE=Reliance Electric Limited + +OUI:001759* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001754* + ID_OUI_FROM_DATABASE=Arkino HiTOP Corporation Limited + +OUI:001746* + ID_OUI_FROM_DATABASE=Freedom9 Inc. + +OUI:001748* + ID_OUI_FROM_DATABASE=Neokoros Brasil Ltda + +OUI:00174D* + ID_OUI_FROM_DATABASE=DYNAMIC NETWORK FACTORY, INC. + +OUI:001741* + ID_OUI_FROM_DATABASE=DEFIDEV + +OUI:001733* + ID_OUI_FROM_DATABASE=SFR + +OUI:00173A* + ID_OUI_FROM_DATABASE=Reach Systems Inc. + +OUI:00172E* + ID_OUI_FROM_DATABASE=FXC Inc. + +OUI:001727* + ID_OUI_FROM_DATABASE=Thermo Ramsey Italia s.r.l. + +OUI:001722* + ID_OUI_FROM_DATABASE=Hanazeder Electronic GmbH + +OUI:00171B* + ID_OUI_FROM_DATABASE=Innovation Lab Corp. + +OUI:001714* + ID_OUI_FROM_DATABASE=BR Controls Nederland bv + +OUI:001716* + ID_OUI_FROM_DATABASE=Qno Technology Inc. + +OUI:0017F4* + ID_OUI_FROM_DATABASE=ZERON ALLIANCE + +OUI:0017F9* + ID_OUI_FROM_DATABASE=Forcom Sp. z o.o. + +OUI:001800* + ID_OUI_FROM_DATABASE=UNIGRAND LTD + +OUI:0017ED* + ID_OUI_FROM_DATABASE=WooJooIT Ltd. + +OUI:0017DA* + ID_OUI_FROM_DATABASE=Spans Logic + +OUI:0017E1* + ID_OUI_FROM_DATABASE=DACOS Technologies Co., Ltd. + +OUI:0017D0* + ID_OUI_FROM_DATABASE=Opticom Communications, LLC + +OUI:0017C4* + ID_OUI_FROM_DATABASE=Quanta Microsystems, INC. + +OUI:001880* + ID_OUI_FROM_DATABASE=Maxim Integrated Products + +OUI:00186D* + ID_OUI_FROM_DATABASE=Zhenjiang Sapphire Electronic Industry CO. + +OUI:001872* + ID_OUI_FROM_DATABASE=Expertise Engineering + +OUI:001874* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001879* + ID_OUI_FROM_DATABASE=dSys + +OUI:001686* + ID_OUI_FROM_DATABASE=Karl Storz Imaging + +OUI:00167F* + ID_OUI_FROM_DATABASE=Bluebird Soft Inc. + +OUI:001681* + ID_OUI_FROM_DATABASE=Vector Informatik GmbH + +OUI:001674* + ID_OUI_FROM_DATABASE=EuroCB (Phils.), Inc. + +OUI:001672* + ID_OUI_FROM_DATABASE=Zenway enterprise ltd + +OUI:001666* + ID_OUI_FROM_DATABASE=Quantier Communication Inc. + +OUI:00165F* + ID_OUI_FROM_DATABASE=Fairmount Automation + +OUI:0016AA* + ID_OUI_FROM_DATABASE=Kei Communication Technology Inc. + +OUI:0016AF* + ID_OUI_FROM_DATABASE=Shenzhen Union Networks Equipment Co.,Ltd. + +OUI:0016A5* + ID_OUI_FROM_DATABASE=Tandberg Storage ASA + +OUI:001699* + ID_OUI_FROM_DATABASE=Tonic DVB Marketing Ltd + +OUI:0016A0* + ID_OUI_FROM_DATABASE=Auto-Maskin + +OUI:001692* + ID_OUI_FROM_DATABASE=Scientific-Atlanta, Inc. + +OUI:001694* + ID_OUI_FROM_DATABASE=Sennheiser Communications A/S + +OUI:00168D* + ID_OUI_FROM_DATABASE=KORWIN CO., Ltd. + +OUI:00165A* + ID_OUI_FROM_DATABASE=Harman Specialty Group + +OUI:001653* + ID_OUI_FROM_DATABASE=LEGO System A/S IE Electronics Division + +OUI:00164C* + ID_OUI_FROM_DATABASE=PLANET INT Co., Ltd + +OUI:001647* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001642* + ID_OUI_FROM_DATABASE=Pangolin + +OUI:00163D* + ID_OUI_FROM_DATABASE=Tsinghua Tongfang Legend Silicon Tech. Co., Ltd. + +OUI:001631* + ID_OUI_FROM_DATABASE=Xteam + +OUI:00162F* + ID_OUI_FROM_DATABASE=Geutebrück GmbH + +OUI:001630* + ID_OUI_FROM_DATABASE=Vativ Technologies + +OUI:0015F5* + ID_OUI_FROM_DATABASE=Sustainable Energy Systems + +OUI:0015F4* + ID_OUI_FROM_DATABASE=Eventide + +OUI:0015EE* + ID_OUI_FROM_DATABASE=Omnex Control Systems + +OUI:0015F3* + ID_OUI_FROM_DATABASE=PELTOR AB + +OUI:0015E7* + ID_OUI_FROM_DATABASE=Quantec Tontechnik + +OUI:0015E2* + ID_OUI_FROM_DATABASE=Dr.Ing. Herbert Knauer GmbH + +OUI:0015DD* + ID_OUI_FROM_DATABASE=IP Control Systems Ltd. + +OUI:0015D8* + ID_OUI_FROM_DATABASE=Interlink Electronics + +OUI:0015CA* + ID_OUI_FROM_DATABASE=TeraRecon, Inc. + +OUI:001598* + ID_OUI_FROM_DATABASE=Kolektor group + +OUI:001593* + ID_OUI_FROM_DATABASE=U4EA Technologies Inc. + +OUI:00158C* + ID_OUI_FROM_DATABASE=Liab ApS + +OUI:001586* + ID_OUI_FROM_DATABASE=Xiamen Overseas Chinese Electronic Co., Ltd. + +OUI:001585* + ID_OUI_FROM_DATABASE=Aonvision Technolopy Corp. + +OUI:001587* + ID_OUI_FROM_DATABASE=Takenaka Seisakusho Co.,Ltd + +OUI:001580* + ID_OUI_FROM_DATABASE=U-WAY CORPORATION + +OUI:00157B* + ID_OUI_FROM_DATABASE=Leuze electronic GmbH + Co. KG + +OUI:001576* + ID_OUI_FROM_DATABASE=LABiTec - Labor Biomedical Technologies GmbH + +OUI:00156A* + ID_OUI_FROM_DATABASE=DG2L Technologies Pvt. Ltd. + +OUI:00156F* + ID_OUI_FROM_DATABASE=Xiranet Communications GmbH + +OUI:0016DF* + ID_OUI_FROM_DATABASE=Lundinova AB + +OUI:0016DA* + ID_OUI_FROM_DATABASE=Futronic Technology Co. Ltd. + +OUI:0016D5* + ID_OUI_FROM_DATABASE=Synccom Co., Ltd + +OUI:0016C9* + ID_OUI_FROM_DATABASE=NAT Seattle, Inc. + +OUI:0016D0* + ID_OUI_FROM_DATABASE=ATech elektronika d.o.o. + +OUI:0016BD* + ID_OUI_FROM_DATABASE=ATI Industrial Automation + +OUI:0016C2* + ID_OUI_FROM_DATABASE=Avtec Systems Inc + +OUI:0016BB* + ID_OUI_FROM_DATABASE=Law-Chain Computer Technology Co Ltd + +OUI:00162A* + ID_OUI_FROM_DATABASE=Antik computers & communications s.r.o. + +OUI:001623* + ID_OUI_FROM_DATABASE=Interval Media + +OUI:001617* + ID_OUI_FROM_DATABASE=MSI + +OUI:00161E* + ID_OUI_FROM_DATABASE=Woojinnet + +OUI:00160D* + ID_OUI_FROM_DATABASE=Be Here Corporation + +OUI:001606* + ID_OUI_FROM_DATABASE=Ideal Industries + +OUI:0015FA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001563* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001557* + ID_OUI_FROM_DATABASE=Olivetti + +OUI:00155C* + ID_OUI_FROM_DATABASE=Dresser Wayne + +OUI:00154B* + ID_OUI_FROM_DATABASE=Wonde Proud Technology Co., Ltd + +OUI:001550* + ID_OUI_FROM_DATABASE=Nits Technology Inc + +OUI:001545* + ID_OUI_FROM_DATABASE=SEECODE Co., Ltd. + +OUI:00153E* + ID_OUI_FROM_DATABASE=Q-Matic Sweden AB + +OUI:0015BC* + ID_OUI_FROM_DATABASE=Develco + +OUI:0015B5* + ID_OUI_FROM_DATABASE=CI Network Corp. + +OUI:0015B0* + ID_OUI_FROM_DATABASE=AUTOTELENET CO.,LTD + +OUI:0015AB* + ID_OUI_FROM_DATABASE=PRO CO SOUND INC + +OUI:0015A6* + ID_OUI_FROM_DATABASE=Digital Electronics Products Ltd. + +OUI:00159F* + ID_OUI_FROM_DATABASE=Terascala, Inc. + +OUI:001532* + ID_OUI_FROM_DATABASE=Consumer Technologies Group, LLC + +OUI:001539* + ID_OUI_FROM_DATABASE=Technodrive srl + +OUI:00152B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00152D* + ID_OUI_FROM_DATABASE=TenX Networks, LLC + +OUI:00152C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00151F* + ID_OUI_FROM_DATABASE=Multivision Intelligent Surveillance (Hong Kong) Ltd + +OUI:001526* + ID_OUI_FROM_DATABASE=Remote Technologies Inc + +OUI:00151A* + ID_OUI_FROM_DATABASE=Hunter Engineering Company + +OUI:001515* + ID_OUI_FROM_DATABASE=Leipold+Co.GmbH + +OUI:001510* + ID_OUI_FROM_DATABASE=Techsphere Co., Ltd + +OUI:001453* + ID_OUI_FROM_DATABASE=ADVANTECH TECHNOLOGIES CO.,LTD + +OUI:00144E* + ID_OUI_FROM_DATABASE=SRISA + +OUI:001442* + ID_OUI_FROM_DATABASE=ATTO CORPORATION + +OUI:001449* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:00143D* + ID_OUI_FROM_DATABASE=Aevoe Inc. + +OUI:00143C* + ID_OUI_FROM_DATABASE=Rheinmetall Canada Inc. + +OUI:00143B* + ID_OUI_FROM_DATABASE=Sensovation AG + +OUI:001436* + ID_OUI_FROM_DATABASE=Qwerty Elektronik AB + +OUI:0014AB* + ID_OUI_FROM_DATABASE=Senhai Electronic Technology Co., Ltd. + +OUI:0014B0* + ID_OUI_FROM_DATABASE=Naeil Community + +OUI:0014A9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0014AA* + ID_OUI_FROM_DATABASE=Ashly Audio, Inc. + +OUI:00149D* + ID_OUI_FROM_DATABASE=Sound ID Inc. + +OUI:001498* + ID_OUI_FROM_DATABASE=Viking Design Technology + +OUI:00148A* + ID_OUI_FROM_DATABASE=Elin Ebg Traction Gmbh + +OUI:001491* + ID_OUI_FROM_DATABASE=Daniels Electronics Ltd. dbo Codan Rado Communications + +OUI:001485* + ID_OUI_FROM_DATABASE=Giga-Byte + +OUI:00147E* + ID_OUI_FROM_DATABASE=InnerWireless + +OUI:001477* + ID_OUI_FROM_DATABASE=Nertec Inc. + +OUI:001472* + ID_OUI_FROM_DATABASE=China Broadband Wireless IP Standard Group + +OUI:001466* + ID_OUI_FROM_DATABASE=Kleinhenz Elektronik GmbH + +OUI:00146B* + ID_OUI_FROM_DATABASE=Anagran, Inc. + +OUI:00145F* + ID_OUI_FROM_DATABASE=ADITEC CO. LTD + +OUI:001458* + ID_OUI_FROM_DATABASE=HS Automatic ApS + +OUI:0014E6* + ID_OUI_FROM_DATABASE=AIM Infrarotmodule GmbH + +OUI:0014E0* + ID_OUI_FROM_DATABASE=LET'S Corporation + +OUI:0014D4* + ID_OUI_FROM_DATABASE=K Technology Corporation + +OUI:0014D9* + ID_OUI_FROM_DATABASE=IP Fabrics, Inc. + +OUI:0014CD* + ID_OUI_FROM_DATABASE=DigitalZone Co., Ltd. + +OUI:0014C1* + ID_OUI_FROM_DATABASE=U.S. Robotics Corporation + +OUI:0014C6* + ID_OUI_FROM_DATABASE=Quixant Ltd + +OUI:0014BA* + ID_OUI_FROM_DATABASE=Carvers SA de CV + +OUI:0014B5* + ID_OUI_FROM_DATABASE=PHYSIOMETRIX,INC + +OUI:0013C7* + ID_OUI_FROM_DATABASE=IONOS Co.,Ltd. + +OUI:0013C0* + ID_OUI_FROM_DATABASE=Trix Tecnologia Ltda. + +OUI:0013B6* + ID_OUI_FROM_DATABASE=Sling Media, Inc. + +OUI:0013AF* + ID_OUI_FROM_DATABASE=NUMA Technology,Inc. + +OUI:0013B0* + ID_OUI_FROM_DATABASE=Jablotron + +OUI:0013AA* + ID_OUI_FROM_DATABASE=ALS & TEC Ltd. + +OUI:0013A3* + ID_OUI_FROM_DATABASE=Siemens Com CPE Devices + +OUI:00139E* + ID_OUI_FROM_DATABASE=Ciara Technologies Inc. + +OUI:001502* + ID_OUI_FROM_DATABASE=BETA tech + +OUI:001509* + ID_OUI_FROM_DATABASE=Plus Technology Co., Ltd + +OUI:0014FD* + ID_OUI_FROM_DATABASE=Thecus Technology Corp. + +OUI:0014EF* + ID_OUI_FROM_DATABASE=TZero Technologies, Inc. + +OUI:0014F1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0014F0* + ID_OUI_FROM_DATABASE=Business Security OL AB + +OUI:0014EA* + ID_OUI_FROM_DATABASE=S Digm Inc. (Safe Paradigm Inc.) + +OUI:0014E5* + ID_OUI_FROM_DATABASE=Alticast + +OUI:001423* + ID_OUI_FROM_DATABASE=J-S Co. NEUROCOM + +OUI:001419* + ID_OUI_FROM_DATABASE=SIDSA + +OUI:001412* + ID_OUI_FROM_DATABASE=S-TEC electronics AG + +OUI:001409* + ID_OUI_FROM_DATABASE=MAGNETI MARELLI S.E. S.p.A. + +OUI:00140A* + ID_OUI_FROM_DATABASE=WEPIO Co., Ltd. + +OUI:0013FD* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0013F8* + ID_OUI_FROM_DATABASE=Dex Security Solutions + +OUI:0013F1* + ID_OUI_FROM_DATABASE=AMOD Technology Co., Ltd. + +OUI:0013F7* + ID_OUI_FROM_DATABASE=SMC Networks, Inc. + +OUI:0013E7* + ID_OUI_FROM_DATABASE=Halcro + +OUI:0013DB* + ID_OUI_FROM_DATABASE=SHOEI Electric Co.,Ltd + +OUI:0013CC* + ID_OUI_FROM_DATABASE=Tall Maple Systems + +OUI:001284* + ID_OUI_FROM_DATABASE=Lab33 Srl + +OUI:00127E* + ID_OUI_FROM_DATABASE=Digital Lifestyles Group, Inc. + +OUI:001277* + ID_OUI_FROM_DATABASE=Korenix Technologies Co., Ltd. + +OUI:001272* + ID_OUI_FROM_DATABASE=Redux Communications Ltd. + +OUI:001271* + ID_OUI_FROM_DATABASE=Measurement Computing Corp + +OUI:00126B* + ID_OUI_FROM_DATABASE=Ascalade Communications Limited + +OUI:001264* + ID_OUI_FROM_DATABASE=daum electronic gmbh + +OUI:00125A* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:00125F* + ID_OUI_FROM_DATABASE=AWIND Inc. + +OUI:001255* + ID_OUI_FROM_DATABASE=NetEffect Incorporated + +OUI:00124E* + ID_OUI_FROM_DATABASE=XAC AUTOMATION CORP. + +OUI:001248* + ID_OUI_FROM_DATABASE=EMC Corporation (Kashya) + +OUI:001242* + ID_OUI_FROM_DATABASE=Millennial Net + +OUI:001236* + ID_OUI_FROM_DATABASE=ConSentry Networks + +OUI:00123B* + ID_OUI_FROM_DATABASE=KeRo Systems ApS + +OUI:001368* + ID_OUI_FROM_DATABASE=Saab Danmark A/S + +OUI:00135C* + ID_OUI_FROM_DATABASE=OnSite Systems, Inc. + +OUI:001355* + ID_OUI_FROM_DATABASE=TOMEN Cyber-business Solutions, Inc. + +OUI:001356* + ID_OUI_FROM_DATABASE=FLIR Radiation Inc + +OUI:001350* + ID_OUI_FROM_DATABASE=Silver Spring Networks, Inc + +OUI:001344* + ID_OUI_FROM_DATABASE=Fargo Electronics Inc. + +OUI:001343* + ID_OUI_FROM_DATABASE=Matsushita Electronic Components (Europe) GmbH + +OUI:00133D* + ID_OUI_FROM_DATABASE=Micro Memory Curtiss Wright Co + +OUI:00139D* + ID_OUI_FROM_DATABASE=Marvell Hispana S.L. + +OUI:00138B* + ID_OUI_FROM_DATABASE=Phantom Technologies LLC + +OUI:001390* + ID_OUI_FROM_DATABASE=Termtek Computer Co., Ltd + +OUI:001376* + ID_OUI_FROM_DATABASE=Tabor Electronics Ltd. + +OUI:00137B* + ID_OUI_FROM_DATABASE=Movon Corporation + +OUI:001382* + ID_OUI_FROM_DATABASE=Cetacea Networks Corporation + +OUI:001387* + ID_OUI_FROM_DATABASE=27M Technologies AB + +OUI:00136F* + ID_OUI_FROM_DATABASE=PacketMotion, Inc. + +OUI:001375* + ID_OUI_FROM_DATABASE=American Security Products Co. + +OUI:001363* + ID_OUI_FROM_DATABASE=Verascape, Inc. + +OUI:0012FA* + ID_OUI_FROM_DATABASE=THX LTD + +OUI:001301* + ID_OUI_FROM_DATABASE=IronGate S.L. + +OUI:001307* + ID_OUI_FROM_DATABASE=Paravirtual Corporation + +OUI:0012F5* + ID_OUI_FROM_DATABASE=Imarda New Zealand Limited + +OUI:0012EB* + ID_OUI_FROM_DATABASE=PDH Solutions, LLC + +OUI:0012DE* + ID_OUI_FROM_DATABASE=Radio Components Sweden AB + +OUI:0012DD* + ID_OUI_FROM_DATABASE=Shengqu Information Technology (Shanghai) Co., Ltd. + +OUI:0012E4* + ID_OUI_FROM_DATABASE=ZIEHL industrie-electronik GmbH + Co KG + +OUI:0012AF* + ID_OUI_FROM_DATABASE=ELPRO Technologies + +OUI:0012A8* + ID_OUI_FROM_DATABASE=intec GmbH + +OUI:0012A2* + ID_OUI_FROM_DATABASE=VITA + +OUI:0012A1* + ID_OUI_FROM_DATABASE=BluePacket Communications Co., Ltd. + +OUI:00129C* + ID_OUI_FROM_DATABASE=Yulinet + +OUI:001290* + ID_OUI_FROM_DATABASE=KYOWA Electric & Machinery Corp. + +OUI:001295* + ID_OUI_FROM_DATABASE=Aiware Inc. + +OUI:00132A* + ID_OUI_FROM_DATABASE=Sitronics Telecom Solutions + +OUI:001331* + ID_OUI_FROM_DATABASE=CellPoint Connect + +OUI:001336* + ID_OUI_FROM_DATABASE=Tianjin 712 Communication Broadcasting co., ltd. + +OUI:001324* + ID_OUI_FROM_DATABASE=Schneider Electric Ultra Terminal + +OUI:001314* + ID_OUI_FROM_DATABASE=Asiamajor Inc. + +OUI:001319* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00131A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00130D* + ID_OUI_FROM_DATABASE=GALILEO AVIONICA + +OUI:001308* + ID_OUI_FROM_DATABASE=Nuvera Fuel Cells + +OUI:00122F* + ID_OUI_FROM_DATABASE=Sanei Electric Inc. + +OUI:001235* + ID_OUI_FROM_DATABASE=Andrew Corporation + +OUI:00122B* + ID_OUI_FROM_DATABASE=Virbiage Pty Ltd + +OUI:001212* + ID_OUI_FROM_DATABASE=PLUS Corporation + +OUI:001219* + ID_OUI_FROM_DATABASE=Ahead Communication Systems Inc + +OUI:0012D8* + ID_OUI_FROM_DATABASE=International Games System Co., Ltd. + +OUI:0012CB* + ID_OUI_FROM_DATABASE=CSS Inc. + +OUI:0012C5* + ID_OUI_FROM_DATABASE=V-Show Technology (China) Co.,Ltd + +OUI:0012CC* + ID_OUI_FROM_DATABASE=Bitatek CO., LTD + +OUI:0012B4* + ID_OUI_FROM_DATABASE=Work Microwave GmbH + +OUI:0012BB* + ID_OUI_FROM_DATABASE=Telecommunications Industry Association TR-41 Committee + +OUI:001206* + ID_OUI_FROM_DATABASE=iQuest (NZ) Ltd + +OUI:00120B* + ID_OUI_FROM_DATABASE=Chinasys Technologies Limited + +OUI:00120C* + ID_OUI_FROM_DATABASE=CE-Infosys Pte Ltd + +OUI:0011FF* + ID_OUI_FROM_DATABASE=Digitro Tecnologia Ltda + +OUI:0011FA* + ID_OUI_FROM_DATABASE=Rane Corporation + +OUI:0011F0* + ID_OUI_FROM_DATABASE=Wideful Limited + +OUI:0011EF* + ID_OUI_FROM_DATABASE=Conitec Datensysteme GmbH + +OUI:0011E9* + ID_OUI_FROM_DATABASE=STARNEX CO., LTD. + +OUI:001187* + ID_OUI_FROM_DATABASE=Category Solutions, Inc + +OUI:001182* + ID_OUI_FROM_DATABASE=IMI Norgren Ltd + +OUI:001181* + ID_OUI_FROM_DATABASE=InterEnergy Co.Ltd, + +OUI:00117B* + ID_OUI_FROM_DATABASE=Büchi Labortechnik AG + +OUI:00116F* + ID_OUI_FROM_DATABASE=Netforyou Co., LTD. + +OUI:001168* + ID_OUI_FROM_DATABASE=HomeLogic LLC + +OUI:00115E* + ID_OUI_FROM_DATABASE=ProMinent Dosiertechnik GmbH + +OUI:001157* + ID_OUI_FROM_DATABASE=Oki Electric Industry Co., Ltd. + +OUI:000FB2* + ID_OUI_FROM_DATABASE=Broadband Pacenet (India) Pvt. Ltd. + +OUI:000FA5* + ID_OUI_FROM_DATABASE=BWA Technology GmbH + +OUI:000FB1* + ID_OUI_FROM_DATABASE=Cognio Inc. + +OUI:000FAC* + ID_OUI_FROM_DATABASE=IEEE 802.11 + +OUI:000F9C* + ID_OUI_FROM_DATABASE=Panduit Corp + +OUI:000FA0* + ID_OUI_FROM_DATABASE=CANON KOREA BUSINESS SOLUTIONS INC. + +OUI:000F97* + ID_OUI_FROM_DATABASE=Avanex Corporation + +OUI:000F8A* + ID_OUI_FROM_DATABASE=WideView + +OUI:000F89* + ID_OUI_FROM_DATABASE=Winnertec System Co., Ltd. + +OUI:000F90* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000FD7* + ID_OUI_FROM_DATABASE=Harman Music Group + +OUI:000FD1* + ID_OUI_FROM_DATABASE=Applied Wireless Identifications Group, Inc. + +OUI:000FD2* + ID_OUI_FROM_DATABASE=EWA Technologies, Inc. + +OUI:000FC4* + ID_OUI_FROM_DATABASE=NST co.,LTD. + +OUI:000FCB* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:000FBF* + ID_OUI_FROM_DATABASE=DGT Sp. z o.o. + +OUI:000FB8* + ID_OUI_FROM_DATABASE=CallURL Inc. + +OUI:0011DD* + ID_OUI_FROM_DATABASE=FROMUS TEC. Co., Ltd. + +OUI:0011E2* + ID_OUI_FROM_DATABASE=Hua Jung Components Co., Ltd. + +OUI:0011CF* + ID_OUI_FROM_DATABASE=Thrane & Thrane A/S + +OUI:0011D6* + ID_OUI_FROM_DATABASE=HandEra, Inc. + +OUI:0011D0* + ID_OUI_FROM_DATABASE=Tandberg Data ASA + +OUI:0011CA* + ID_OUI_FROM_DATABASE=Long Range Systems, Inc. + +OUI:0011C3* + ID_OUI_FROM_DATABASE=Transceiving System Technology Corporation + +OUI:0011B7* + ID_OUI_FROM_DATABASE=Octalix B.V. + +OUI:0011BE* + ID_OUI_FROM_DATABASE=AGP Telecom Co. Ltd + +OUI:0011BD* + ID_OUI_FROM_DATABASE=Bombardier Transportation + +OUI:001105* + ID_OUI_FROM_DATABASE=Sunplus Technology Co., Ltd. + +OUI:00110C* + ID_OUI_FROM_DATABASE=Atmark Techno, Inc. + +OUI:000FF9* + ID_OUI_FROM_DATABASE=Valcretec, Inc. + +OUI:000FFA* + ID_OUI_FROM_DATABASE=Optinel Systems, Inc. + +OUI:000FFF* + ID_OUI_FROM_DATABASE=Control4 + +OUI:000FF1* + ID_OUI_FROM_DATABASE=nex-G Systems Pte.Ltd + +OUI:000FE4* + ID_OUI_FROM_DATABASE=Pantech Co.,Ltd + +OUI:000FEA* + ID_OUI_FROM_DATABASE=Giga-Byte Technology Co.,LTD. + +OUI:000FE3* + ID_OUI_FROM_DATABASE=Damm Cellular Systems A/S + +OUI:0011AB* + ID_OUI_FROM_DATABASE=TRUSTABLE TECHNOLOGY CO.,LTD. + +OUI:0011B0* + ID_OUI_FROM_DATABASE=Fortelink Inc. + +OUI:0011A4* + ID_OUI_FROM_DATABASE=JStream Technologies Inc. + +OUI:001198* + ID_OUI_FROM_DATABASE=Prism Media Products Limited + +OUI:00119D* + ID_OUI_FROM_DATABASE=Diginfo Technology Corporation + +OUI:00119E* + ID_OUI_FROM_DATABASE=Solectron Brazil + +OUI:00118E* + ID_OUI_FROM_DATABASE=Halytech Mace + +OUI:001193* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001152* + ID_OUI_FROM_DATABASE=Eidsvoll Electronics AS + +OUI:00114F* + ID_OUI_FROM_DATABASE=US Digital Television, Inc + +OUI:001149* + ID_OUI_FROM_DATABASE=Proliphix Inc. + +OUI:001142* + ID_OUI_FROM_DATABASE=e-SMARTCOM INC. + +OUI:00113D* + ID_OUI_FROM_DATABASE=KN SOLTEC CO.,LTD. + +OUI:00113C* + ID_OUI_FROM_DATABASE=Micronas GmbH + +OUI:001136* + ID_OUI_FROM_DATABASE=Goodrich Sensor Systems + +OUI:00112C* + ID_OUI_FROM_DATABASE=IZT GmbH + +OUI:001130* + ID_OUI_FROM_DATABASE=Allied Telesis (Hong Kong) Ltd. + +OUI:00111E* + ID_OUI_FROM_DATABASE=EPSG (Ethernet Powerlink Standardization Group) + +OUI:00111F* + ID_OUI_FROM_DATABASE=Doremi Labs, Inc. + +OUI:001112* + ID_OUI_FROM_DATABASE=Honeywell CMSS + +OUI:001118* + ID_OUI_FROM_DATABASE=BLX IC Design Corp., Ltd. + +OUI:000F58* + ID_OUI_FROM_DATABASE=Adder Technology Limited + +OUI:000F52* + ID_OUI_FROM_DATABASE=YORK Refrigeration, Marine & Controls + +OUI:000F57* + ID_OUI_FROM_DATABASE=CABLELOGIC Co., Ltd. + +OUI:000F45* + ID_OUI_FROM_DATABASE=Stretch, Inc. + +OUI:000F46* + ID_OUI_FROM_DATABASE=SINAR AG + +OUI:000F4B* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:000F37* + ID_OUI_FROM_DATABASE=Xambala Incorporated + +OUI:000F3F* + ID_OUI_FROM_DATABASE=Big Bear Networks + +OUI:000F3B* + ID_OUI_FROM_DATABASE=Fuji System Machines Co., Ltd. + +OUI:000F31* + ID_OUI_FROM_DATABASE=Allied Vision Technologies Canada Inc + +OUI:000F32* + ID_OUI_FROM_DATABASE=Lootom Telcovideo Network Wuxi Co Ltd + +OUI:000F2B* + ID_OUI_FROM_DATABASE=GREENBELL SYSTEMS + +OUI:000E98* + ID_OUI_FROM_DATABASE=HME Clear-Com LTD. + +OUI:000E93* + ID_OUI_FROM_DATABASE=Milénio 3 Sistemas Electrónicos, Lda. + +OUI:000E8C* + ID_OUI_FROM_DATABASE=Siemens AG A&D ET + +OUI:000E86* + ID_OUI_FROM_DATABASE=Alcatel North America + +OUI:000E80* + ID_OUI_FROM_DATABASE=Thomson Technology Inc + +OUI:000E85* + ID_OUI_FROM_DATABASE=Catalyst Enterprises, Inc. + +OUI:000E74* + ID_OUI_FROM_DATABASE=Solar Telecom. Tech + +OUI:000E79* + ID_OUI_FROM_DATABASE=Ample Communications Inc. + +OUI:000F24* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000F12* + ID_OUI_FROM_DATABASE=Panasonic Europe Ltd. + +OUI:000F18* + ID_OUI_FROM_DATABASE=Industrial Control Systems + +OUI:000F11* + ID_OUI_FROM_DATABASE=Prodrive B.V. + +OUI:000F0C* + ID_OUI_FROM_DATABASE=SYNCHRONIC ENGINEERING + +OUI:000EFF* + ID_OUI_FROM_DATABASE=Megasolution,Inc. + +OUI:000F00* + ID_OUI_FROM_DATABASE=Legra Systems, Inc. + +OUI:000F05* + ID_OUI_FROM_DATABASE=3B SYSTEM INC. + +OUI:000F7D* + ID_OUI_FROM_DATABASE=Xirrus + +OUI:000F84* + ID_OUI_FROM_DATABASE=Astute Networks, Inc. + +OUI:000F77* + ID_OUI_FROM_DATABASE=DENTUM CO.,LTD + +OUI:000F71* + ID_OUI_FROM_DATABASE=Sanmei Electronics Co.,Ltd + +OUI:000F78* + ID_OUI_FROM_DATABASE=Datacap Systems Inc + +OUI:000F65* + ID_OUI_FROM_DATABASE=icube Corp. + +OUI:000F5E* + ID_OUI_FROM_DATABASE=Veo + +OUI:000E71* + ID_OUI_FROM_DATABASE=Gemstar Technology Development Ltd. + +OUI:000E6C* + ID_OUI_FROM_DATABASE=Device Drivers Limited + +OUI:000E65* + ID_OUI_FROM_DATABASE=TransCore + +OUI:000E5F* + ID_OUI_FROM_DATABASE=activ-net GmbH & Co. KG + +OUI:000E60* + ID_OUI_FROM_DATABASE=360SUN Digital Broadband Corporation + +OUI:000E52* + ID_OUI_FROM_DATABASE=Optium Corporation + +OUI:000E46* + ID_OUI_FROM_DATABASE=Niigata Seimitsu Co.,Ltd. + +OUI:000E4D* + ID_OUI_FROM_DATABASE=Numesa Inc. + +OUI:000E3F* + ID_OUI_FROM_DATABASE=Soronti, Inc. + +OUI:000EC5* + ID_OUI_FROM_DATABASE=Digital Multitools Inc + +OUI:000EB8* + ID_OUI_FROM_DATABASE=Iiga co.,Ltd + +OUI:000EB7* + ID_OUI_FROM_DATABASE=Knovative, Inc. + +OUI:000EBE* + ID_OUI_FROM_DATABASE=B&B Electronics Manufacturing Co. + +OUI:000EB2* + ID_OUI_FROM_DATABASE=Micro-Research Finland Oy + +OUI:000EAB* + ID_OUI_FROM_DATABASE=Cray Inc + +OUI:000EA5* + ID_OUI_FROM_DATABASE=BLIP Systems + +OUI:000E9F* + ID_OUI_FROM_DATABASE=TEMIC SDS GmbH + +OUI:000E0A* + ID_OUI_FROM_DATABASE=SAKUMA DESIGN OFFICE + +OUI:000E12* + ID_OUI_FROM_DATABASE=Adaptive Micro Systems Inc. + +OUI:000E04* + ID_OUI_FROM_DATABASE=CMA/Microdialysis AB + +OUI:000DF7* + ID_OUI_FROM_DATABASE=Space Dynamics Lab + +OUI:000DFE* + ID_OUI_FROM_DATABASE=Hauppauge Computer Works, Inc. + +OUI:000DF1* + ID_OUI_FROM_DATABASE=IONIX INC. + +OUI:000DEB* + ID_OUI_FROM_DATABASE=CompXs Limited + +OUI:000DF2* + ID_OUI_FROM_DATABASE=Private + +OUI:000DE4* + ID_OUI_FROM_DATABASE=DIGINICS, Inc. + +OUI:000EF9* + ID_OUI_FROM_DATABASE=REA Elektronik GmbH + +OUI:000EF2* + ID_OUI_FROM_DATABASE=Infinico Corporation + +OUI:000EE0* + ID_OUI_FROM_DATABASE=Mcharge + +OUI:000EDF* + ID_OUI_FROM_DATABASE=PLX Technology + +OUI:000EE6* + ID_OUI_FROM_DATABASE=Adimos Systems LTD + +OUI:000ECA* + ID_OUI_FROM_DATABASE=WTSS Inc + +OUI:000ED1* + ID_OUI_FROM_DATABASE=Osaka Micro Computer. + +OUI:000EDA* + ID_OUI_FROM_DATABASE=C-TECH UNITED CORP. + +OUI:000ED6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E37* + ID_OUI_FROM_DATABASE=Harms & Wende GmbH & Co.KG + +OUI:000E38* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E31* + ID_OUI_FROM_DATABASE=Olympus Soft Imaging Solutions GmbH + +OUI:000E2A* + ID_OUI_FROM_DATABASE=Private + +OUI:000E25* + ID_OUI_FROM_DATABASE=Hannae Technology Co., Ltd + +OUI:000E18* + ID_OUI_FROM_DATABASE=MyA Technology + +OUI:000E17* + ID_OUI_FROM_DATABASE=Private + +OUI:000E0E* + ID_OUI_FROM_DATABASE=ESA elettronica S.P.A. + +OUI:000C7E* + ID_OUI_FROM_DATABASE=Tellium Incorporated + +OUI:000C86* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000C81* + ID_OUI_FROM_DATABASE=Schneider Electric (Australia) + +OUI:000C72* + ID_OUI_FROM_DATABASE=Tempearl Industrial Co., Ltd. + +OUI:000C79* + ID_OUI_FROM_DATABASE=Extel Communications P/L + +OUI:000C66* + ID_OUI_FROM_DATABASE=Pronto Networks Inc + +OUI:000C6B* + ID_OUI_FROM_DATABASE=Kurz Industrie-Elektronik GmbH + +OUI:000C6D* + ID_OUI_FROM_DATABASE=Edwards Ltd. + +OUI:000DDF* + ID_OUI_FROM_DATABASE=Japan Image & Network Inc. + +OUI:000DD2* + ID_OUI_FROM_DATABASE=Simrad Optronics ASA + +OUI:000DD1* + ID_OUI_FROM_DATABASE=Stryker Corporation + +OUI:000DD8* + ID_OUI_FROM_DATABASE=BBN + +OUI:000DCC* + ID_OUI_FROM_DATABASE=NEOSMART Corp. + +OUI:000DBF* + ID_OUI_FROM_DATABASE=TekTone Sound & Signal Mfg., Inc. + +OUI:000DC0* + ID_OUI_FROM_DATABASE=Spagat AS + +OUI:000DC5* + ID_OUI_FROM_DATABASE=EchoStar Global B.V. + +OUI:000DB9* + ID_OUI_FROM_DATABASE=PC Engines GmbH + +OUI:000D8C* + ID_OUI_FROM_DATABASE=Shanghai Wedone Digital Ltd. CO. + +OUI:000D8B* + ID_OUI_FROM_DATABASE=T&D Corporation + +OUI:000D85* + ID_OUI_FROM_DATABASE=Tapwave, Inc. + +OUI:000D86* + ID_OUI_FROM_DATABASE=Huber + Suhner AG + +OUI:000D7E* + ID_OUI_FROM_DATABASE=Axiowave Networks, Inc. + +OUI:000D78* + ID_OUI_FROM_DATABASE=Engineering & Security + +OUI:000D77* + ID_OUI_FROM_DATABASE=FalconStor Software + +OUI:000D6B* + ID_OUI_FROM_DATABASE=Mita-Teknik A/S + +OUI:000D65* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000D5F* + ID_OUI_FROM_DATABASE=Minds Inc + +OUI:000D66* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000CB1* + ID_OUI_FROM_DATABASE=Salland Engineering (Europe) BV + +OUI:000CB7* + ID_OUI_FROM_DATABASE=Nanjing Huazhuo Electronics Co., Ltd. + +OUI:000CBE* + ID_OUI_FROM_DATABASE=Innominate Security Technologies AG + +OUI:000CC3* + ID_OUI_FROM_DATABASE=BeWAN systems + +OUI:000CB2* + ID_OUI_FROM_DATABASE=UNION co., ltd. + +OUI:000CA5* + ID_OUI_FROM_DATABASE=Naman NZ LTd + +OUI:000CAC* + ID_OUI_FROM_DATABASE=Citizen Watch Co., Ltd. + +OUI:000C94* + ID_OUI_FROM_DATABASE=United Electronic Industries, Inc. (EUI) + +OUI:000C99* + ID_OUI_FROM_DATABASE=HITEL LINK Co.,Ltd + +OUI:000CA0* + ID_OUI_FROM_DATABASE=StorCase Technology, Inc. + +OUI:000C8D* + ID_OUI_FROM_DATABASE=MATRIX VISION GmbH + +OUI:000C92* + ID_OUI_FROM_DATABASE=WolfVision Gmbh + +OUI:000D32* + ID_OUI_FROM_DATABASE=DispenseSource, Inc. + +OUI:000D31* + ID_OUI_FROM_DATABASE=Compellent Technologies, Inc. + +OUI:000D2C* + ID_OUI_FROM_DATABASE=Patapsco Designs Ltd + +OUI:000D25* + ID_OUI_FROM_DATABASE=SANDEN CORPORATION + +OUI:000D1F* + ID_OUI_FROM_DATABASE=AV Digital + +OUI:000D19* + ID_OUI_FROM_DATABASE=ROBE Show lighting + +OUI:000D20* + ID_OUI_FROM_DATABASE=ASAHIKASEI TECHNOSYSTEM CO.,LTD. + +OUI:000D0D* + ID_OUI_FROM_DATABASE=ITSupported, LLC + +OUI:000D12* + ID_OUI_FROM_DATABASE=AXELL Corporation + +OUI:000DB2* + ID_OUI_FROM_DATABASE=Ammasso, Inc. + +OUI:000DAD* + ID_OUI_FROM_DATABASE=Dataprobe, Inc. + +OUI:000D9E* + ID_OUI_FROM_DATABASE=TOKUDEN OHIZUMI SEISAKUSYO Co.,Ltd. + +OUI:000DA5* + ID_OUI_FROM_DATABASE=Fabric7 Systems, Inc + +OUI:000D99* + ID_OUI_FROM_DATABASE=Orbital Sciences Corp.; Launch Systems Group + +OUI:000D58* + ID_OUI_FROM_DATABASE=Private + +OUI:000D4C* + ID_OUI_FROM_DATABASE=Outline Electronics Ltd. + +OUI:000D53* + ID_OUI_FROM_DATABASE=Beijing 5w Communication Corp. + +OUI:000D3F* + ID_OUI_FROM_DATABASE=VTI Instruments Corporation + +OUI:000D44* + ID_OUI_FROM_DATABASE=Audio BU - Logitech + +OUI:000D38* + ID_OUI_FROM_DATABASE=NISSIN INC. + +OUI:000CD1* + ID_OUI_FROM_DATABASE=SFOM Technology Corp. + +OUI:000CD6* + ID_OUI_FROM_DATABASE=PARTNER TECH + +OUI:000CDD* + ID_OUI_FROM_DATABASE=AOS technologies AG + +OUI:000CCA* + ID_OUI_FROM_DATABASE=HGST a Western Digital Company + +OUI:000CC4* + ID_OUI_FROM_DATABASE=Tiptel AG + +OUI:000D00* + ID_OUI_FROM_DATABASE=Seaway Networks Inc. + +OUI:000D06* + ID_OUI_FROM_DATABASE=Compulogic Limited + +OUI:000CFA* + ID_OUI_FROM_DATABASE=Digital Systems Corp + +OUI:000CFF* + ID_OUI_FROM_DATABASE=MRO-TEK LIMITED + +OUI:000CED* + ID_OUI_FROM_DATABASE=Real Digital Media + +OUI:000CEE* + ID_OUI_FROM_DATABASE=jp-embedded + +OUI:000CF3* + ID_OUI_FROM_DATABASE=CALL IMAGE SA + +OUI:000CE7* + ID_OUI_FROM_DATABASE=MediaTek Inc. + +OUI:000CE3* + ID_OUI_FROM_DATABASE=Option International N.V. + +OUI:000B01* + ID_OUI_FROM_DATABASE=DAIICHI ELECTRONICS CO., LTD. + +OUI:000AF0* + ID_OUI_FROM_DATABASE=SHIN-OH ELECTRONICS CO., LTD. R&D + +OUI:000AF5* + ID_OUI_FROM_DATABASE=Airgo Networks, Inc. + +OUI:000AEC* + ID_OUI_FROM_DATABASE=Koatsu Gas Kogyo Co., Ltd. + +OUI:000AE5* + ID_OUI_FROM_DATABASE=ScottCare Corporation + +OUI:000AE7* + ID_OUI_FROM_DATABASE=ELIOP S.A. + +OUI:000AE0* + ID_OUI_FROM_DATABASE=Fujitsu Softek + +OUI:000AC8* + ID_OUI_FROM_DATABASE=ZPSYS CO.,LTD. (Planning&Management) + +OUI:000ACD* + ID_OUI_FROM_DATABASE=Sunrich Technology Limited + +OUI:000AD4* + ID_OUI_FROM_DATABASE=CoreBell Systems Inc. + +OUI:000B5E* + ID_OUI_FROM_DATABASE=Audio Engineering Society Inc. + +OUI:000B63* + ID_OUI_FROM_DATABASE=Kaleidescape + +OUI:000B55* + ID_OUI_FROM_DATABASE=ADInstruments + +OUI:000B5A* + ID_OUI_FROM_DATABASE=HyperEdge + +OUI:000B52* + ID_OUI_FROM_DATABASE=JOYMAX ELECTRONICS CO. LTD. + +OUI:000B4D* + ID_OUI_FROM_DATABASE=Emuzed + +OUI:000B41* + ID_OUI_FROM_DATABASE=Ing. Büro Dr. Beutlhauser + +OUI:000B46* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B33* + ID_OUI_FROM_DATABASE=Vivato Technologies + +OUI:000B3A* + ID_OUI_FROM_DATABASE=QuStream Corporation + +OUI:000B3F* + ID_OUI_FROM_DATABASE=Anthology Solutions Inc. + +OUI:000B95* + ID_OUI_FROM_DATABASE=eBet Gaming Systems Pty Ltd + +OUI:000B8F* + ID_OUI_FROM_DATABASE=AKITA ELECTRONICS SYSTEMS CO.,LTD. + +OUI:000B89* + ID_OUI_FROM_DATABASE=Top Global Technology, Ltd. + +OUI:000B8E* + ID_OUI_FROM_DATABASE=Ascent Corporation + +OUI:000B90* + ID_OUI_FROM_DATABASE=ADVA Optical Networking Ltd. + +OUI:000B7D* + ID_OUI_FROM_DATABASE=SOLOMON EXTREME INTERNATIONAL LTD. + +OUI:000B82* + ID_OUI_FROM_DATABASE=Grandstream Networks, Inc. + +OUI:000B6F* + ID_OUI_FROM_DATABASE=Media Streaming Networks Inc + +OUI:000B76* + ID_OUI_FROM_DATABASE=ET&T Technology Co. Ltd. + +OUI:000AC1* + ID_OUI_FROM_DATABASE=Futuretel + +OUI:000AC6* + ID_OUI_FROM_DATABASE=Overture Networks. + +OUI:000AAE* + ID_OUI_FROM_DATABASE=Rosemount Process Analytical + +OUI:000AB3* + ID_OUI_FROM_DATABASE=Fa. GIRA + +OUI:000AB5* + ID_OUI_FROM_DATABASE=Digital Electronic Network + +OUI:000ABA* + ID_OUI_FROM_DATABASE=Arcon Technology Limited + +OUI:000AA2* + ID_OUI_FROM_DATABASE=SYSTEK INC. + +OUI:000AA7* + ID_OUI_FROM_DATABASE=FEI Electron Optics + +OUI:000A8F* + ID_OUI_FROM_DATABASE=Aska International Inc. + +OUI:000A94* + ID_OUI_FROM_DATABASE=ShangHai cellink CO., LTD + +OUI:000C4E* + ID_OUI_FROM_DATABASE=Winbest Technology CO,LT + +OUI:000C53* + ID_OUI_FROM_DATABASE=Private + +OUI:000C5A* + ID_OUI_FROM_DATABASE=IBSmm Embedded Electronics Consulting + +OUI:000C5F* + ID_OUI_FROM_DATABASE=Avtec, Inc. + +OUI:000C47* + ID_OUI_FROM_DATABASE=SK Teletech(R&D Planning Team) + +OUI:000C4C* + ID_OUI_FROM_DATABASE=Arcor AG&Co. + +OUI:000C3E* + ID_OUI_FROM_DATABASE=Crest Audio + +OUI:000C37* + ID_OUI_FROM_DATABASE=Geomation, Inc. + +OUI:000C2D* + ID_OUI_FROM_DATABASE=FullWave Technology Co., Ltd. + +OUI:000C1A* + ID_OUI_FROM_DATABASE=Quest Technical Solutions Inc. + +OUI:000C1E* + ID_OUI_FROM_DATABASE=Global Cache + +OUI:000C23* + ID_OUI_FROM_DATABASE=Beijing Lanchuan Tech. Co., Ltd. + +OUI:000C0E* + ID_OUI_FROM_DATABASE=XtremeSpectrum, Inc. + +OUI:000C15* + ID_OUI_FROM_DATABASE=CyberPower Systems, Inc. + +OUI:000C09* + ID_OUI_FROM_DATABASE=Hitachi IE Systems Co., Ltd + +OUI:000BD3* + ID_OUI_FROM_DATABASE=cd3o + +OUI:000BC7* + ID_OUI_FROM_DATABASE=ICET S.p.A. + +OUI:000BCE* + ID_OUI_FROM_DATABASE=Free2move AB + +OUI:000BC2* + ID_OUI_FROM_DATABASE=Corinex Communication Corp. + +OUI:000BBB* + ID_OUI_FROM_DATABASE=Etin Systems Co., Ltd + +OUI:000BC0* + ID_OUI_FROM_DATABASE=China IWNComm Co., Ltd. + +OUI:000BAF* + ID_OUI_FROM_DATABASE=WOOJU COMMUNICATIONS Co,.Ltd + +OUI:000BB4* + ID_OUI_FROM_DATABASE=RDC Semiconductor Inc., + +OUI:000BA5* + ID_OUI_FROM_DATABASE=Quasar Cipta Mandiri, PT + +OUI:000BAA* + ID_OUI_FROM_DATABASE=Aiphone co.,Ltd + +OUI:000B9E* + ID_OUI_FROM_DATABASE=Yasing Technology Corp. + +OUI:000B27* + ID_OUI_FROM_DATABASE=Scion Corporation + +OUI:000B2E* + ID_OUI_FROM_DATABASE=Cal-Comp Electronics (Thailand) Public Company Limited Taipe + +OUI:000B1B* + ID_OUI_FROM_DATABASE=Systronix, Inc. + +OUI:000B20* + ID_OUI_FROM_DATABASE=Hirata corporation + +OUI:000B22* + ID_OUI_FROM_DATABASE=Environmental Systems and Services + +OUI:000B14* + ID_OUI_FROM_DATABASE=ViewSonic Corporation + +OUI:000B0D* + ID_OUI_FROM_DATABASE=Air2U, Inc. + +OUI:000B0F* + ID_OUI_FROM_DATABASE=Bosch Rexroth + +OUI:000B08* + ID_OUI_FROM_DATABASE=Pillar Data Systems + +OUI:000AFC* + ID_OUI_FROM_DATABASE=Core Tec Communications, LLC + +OUI:000BF6* + ID_OUI_FROM_DATABASE=Nitgen Co., Ltd + +OUI:000BFB* + ID_OUI_FROM_DATABASE=D-NET International Corporation + +OUI:000C02* + ID_OUI_FROM_DATABASE=ABB Oy + +OUI:000BEA* + ID_OUI_FROM_DATABASE=Zultys Technologies + +OUI:000BEF* + ID_OUI_FROM_DATABASE=Code Corporation + +OUI:000BE3* + ID_OUI_FROM_DATABASE=Key Stream Co., Ltd. + +OUI:000BE8* + ID_OUI_FROM_DATABASE=AOIP + +OUI:000BE9* + ID_OUI_FROM_DATABASE=Actel Corporation + +OUI:000BD7* + ID_OUI_FROM_DATABASE=DORMA Time + Access GmbH + +OUI:000BDC* + ID_OUI_FROM_DATABASE=AKCP + +OUI:000994* + ID_OUI_FROM_DATABASE=Cronyx Engineering + +OUI:000999* + ID_OUI_FROM_DATABASE=CP GEORGES RENAULT + +OUI:000987* + ID_OUI_FROM_DATABASE=NISHI NIPPON ELECTRIC WIRE & CABLE CO.,LTD. + +OUI:000988* + ID_OUI_FROM_DATABASE=Nudian Electron Co., Ltd. + +OUI:00098D* + ID_OUI_FROM_DATABASE=Velocity Semiconductor + +OUI:000981* + ID_OUI_FROM_DATABASE=Newport Networks + +OUI:000975* + ID_OUI_FROM_DATABASE=fSONA Communications Corporation + +OUI:00097A* + ID_OUI_FROM_DATABASE=Louis Design Labs. + +OUI:000968* + ID_OUI_FROM_DATABASE=TECHNOVENTURE, INC. + +OUI:000962* + ID_OUI_FROM_DATABASE=Sonitor Technologies AS + +OUI:000A9B* + ID_OUI_FROM_DATABASE=TB Group Inc + +OUI:000A9A* + ID_OUI_FROM_DATABASE=Aiptek International Inc + +OUI:000A80* + ID_OUI_FROM_DATABASE=Telkonet Inc. + +OUI:000A82* + ID_OUI_FROM_DATABASE=TATSUTA SYSTEM ELECTRONICS CO.,LTD. + +OUI:000A87* + ID_OUI_FROM_DATABASE=Integrated Micromachines Inc. + +OUI:000A7B* + ID_OUI_FROM_DATABASE=Cornelius Consult + +OUI:000A6D* + ID_OUI_FROM_DATABASE=EKS Elektronikservice GmbH + +OUI:000A6F* + ID_OUI_FROM_DATABASE=ZyFLEX Technologies Inc + +OUI:000A74* + ID_OUI_FROM_DATABASE=Manticom Networks Inc. + +OUI:000A61* + ID_OUI_FROM_DATABASE=Cellinx Systems Inc. + +OUI:0009C3* + ID_OUI_FROM_DATABASE=NETAS + +OUI:0009B9* + ID_OUI_FROM_DATABASE=Action Imaging Solutions + +OUI:0009BA* + ID_OUI_FROM_DATABASE=MAKU Informationstechik GmbH + +OUI:0009AC* + ID_OUI_FROM_DATABASE=LANVOICE + +OUI:0009B3* + ID_OUI_FROM_DATABASE=MCM Systems Ltd + +OUI:0009A7* + ID_OUI_FROM_DATABASE=Bang & Olufsen A/S + +OUI:00099A* + ID_OUI_FROM_DATABASE=ELMO COMPANY, LIMITED + +OUI:0009A0* + ID_OUI_FROM_DATABASE=Microtechno Corporation + +OUI:0009ED* + ID_OUI_FROM_DATABASE=CipherOptics + +OUI:0009F2* + ID_OUI_FROM_DATABASE=Cohu, Inc., Electronics Division + +OUI:0009E6* + ID_OUI_FROM_DATABASE=Cyber Switching Inc. + +OUI:0009E0* + ID_OUI_FROM_DATABASE=XEMICS S.A. + +OUI:0009DA* + ID_OUI_FROM_DATABASE=Control Module Inc. + +OUI:0009DF* + ID_OUI_FROM_DATABASE=Vestel Komunikasyon Sanayi ve Ticaret A.S. + +OUI:0009CD* + ID_OUI_FROM_DATABASE=HUDSON SOFT CO.,LTD. + +OUI:0009C7* + ID_OUI_FROM_DATABASE=Movistec + +OUI:0009CE* + ID_OUI_FROM_DATABASE=SpaceBridge Semiconductor Corp. + +OUI:0009D3* + ID_OUI_FROM_DATABASE=Western DataCom Co., Inc. + +OUI:000901* + ID_OUI_FROM_DATABASE=Shenzhen Shixuntong Information & Technoligy Co + +OUI:0008FC* + ID_OUI_FROM_DATABASE=Gigaphoton Inc. + +OUI:0008F9* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:0008F4* + ID_OUI_FROM_DATABASE=Bluetake Technology Co., Ltd. + +OUI:0008EB* + ID_OUI_FROM_DATABASE=ROMWin Co.,Ltd. + +OUI:0008E4* + ID_OUI_FROM_DATABASE=Envenergy Inc + +OUI:0008DF* + ID_OUI_FROM_DATABASE=Alistel Inc. + +OUI:0008D8* + ID_OUI_FROM_DATABASE=Dowkey Microwave + +OUI:0008D2* + ID_OUI_FROM_DATABASE=ZOOM Networks Inc. + +OUI:0008CC* + ID_OUI_FROM_DATABASE=Remotec, Inc. + +OUI:0008D1* + ID_OUI_FROM_DATABASE=KAREL INC. + +OUI:000967* + ID_OUI_FROM_DATABASE=Tachyon, Inc + +OUI:00096E* + ID_OUI_FROM_DATABASE=GIANT ELECTRONICS LTD. + +OUI:00095E* + ID_OUI_FROM_DATABASE=Masstech Group Inc. + +OUI:000959* + ID_OUI_FROM_DATABASE=Sitecsoft + +OUI:00094D* + ID_OUI_FROM_DATABASE=Braintree Communications Pty Ltd + +OUI:000952* + ID_OUI_FROM_DATABASE=Auerswald GmbH & Co. KG + +OUI:000946* + ID_OUI_FROM_DATABASE=Cluster Labs GmbH + +OUI:000940* + ID_OUI_FROM_DATABASE=AGFEO GmbH & Co. KG + +OUI:00093F* + ID_OUI_FROM_DATABASE=Double-Win Enterpirse CO., LTD + +OUI:00093A* + ID_OUI_FROM_DATABASE=Molex Fiber Optics + +OUI:000933* + ID_OUI_FROM_DATABASE=Ophit Co.Ltd. + +OUI:000A5C* + ID_OUI_FROM_DATABASE=Carel s.p.a. + +OUI:000A50* + ID_OUI_FROM_DATABASE=REMOTEK CORPORATION + +OUI:000A55* + ID_OUI_FROM_DATABASE=MARKEM Corporation + +OUI:000A4E* + ID_OUI_FROM_DATABASE=UNITEK Electronics INC. + +OUI:000A42* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000A49* + ID_OUI_FROM_DATABASE=F5 Networks, Inc. + +OUI:000A36* + ID_OUI_FROM_DATABASE=Synelec Telecom Multimedia + +OUI:000A3B* + ID_OUI_FROM_DATABASE=GCT Semiconductor, Inc + +OUI:000A3D* + ID_OUI_FROM_DATABASE=Elo Sistemas Eletronicos S.A. + +OUI:000A2F* + ID_OUI_FROM_DATABASE=Artnix Inc. + +OUI:000927* + ID_OUI_FROM_DATABASE=TOYOKEIKI CO.,LTD. + +OUI:00092E* + ID_OUI_FROM_DATABASE=B&Tech System Inc. + +OUI:000920* + ID_OUI_FROM_DATABASE=EpoX COMPUTER CO.,LTD. + +OUI:00091B* + ID_OUI_FROM_DATABASE=Digital Generation Inc. + +OUI:000914* + ID_OUI_FROM_DATABASE=COMPUTROLS INC. + +OUI:00090E* + ID_OUI_FROM_DATABASE=Helix Technology Inc. + +OUI:000908* + ID_OUI_FROM_DATABASE=VTech Technology Corp. + +OUI:00090D* + ID_OUI_FROM_DATABASE=LEADER ELECTRONICS CORP. + +OUI:000A20* + ID_OUI_FROM_DATABASE=SVA Networks, Inc. + +OUI:000A25* + ID_OUI_FROM_DATABASE=CERAGON NETWORKS + +OUI:000A14* + ID_OUI_FROM_DATABASE=TECO a.s. + +OUI:000A19* + ID_OUI_FROM_DATABASE=Valere Power, Inc. + +OUI:000A0D* + ID_OUI_FROM_DATABASE=FCI Deutschland GmbH + +OUI:000A12* + ID_OUI_FROM_DATABASE=Azylex Technology, Inc + +OUI:0009F9* + ID_OUI_FROM_DATABASE=ART JAPAN CO., LTD. + +OUI:0009FC* + ID_OUI_FROM_DATABASE=IPFLEX Inc. + +OUI:000A03* + ID_OUI_FROM_DATABASE=ENDESA SERVICIOS, S.L. + +OUI:000705* + ID_OUI_FROM_DATABASE=Endress & Hauser GmbH & Co + +OUI:0006F8* + ID_OUI_FROM_DATABASE=The Boeing Company + +OUI:0006FF* + ID_OUI_FROM_DATABASE=Sheba Systems Co., Ltd. + +OUI:0006FD* + ID_OUI_FROM_DATABASE=Comjet Information Systems Corp. + +OUI:0006E7* + ID_OUI_FROM_DATABASE=Bit Blitz Communications Inc. + +OUI:0006ED* + ID_OUI_FROM_DATABASE=Inara Networks + +OUI:0006DC* + ID_OUI_FROM_DATABASE=Syabas Technology (Amquest) + +OUI:0006E1* + ID_OUI_FROM_DATABASE=Techno Trade s.a + +OUI:0006E6* + ID_OUI_FROM_DATABASE=DongYang Telecom Co., Ltd. + +OUI:0006CF* + ID_OUI_FROM_DATABASE=Thales Avionics In-Flight Systems, LLC + +OUI:0006D6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0006D5* + ID_OUI_FROM_DATABASE=Diamond Systems Corp. + +OUI:0006C9* + ID_OUI_FROM_DATABASE=Technical Marketing Research, Inc. + +OUI:0007B1* + ID_OUI_FROM_DATABASE=Equator Technologies + +OUI:0007B8* + ID_OUI_FROM_DATABASE=Corvalent Corporation + +OUI:0007B2* + ID_OUI_FROM_DATABASE=Transaccess S.A. + +OUI:0007A4* + ID_OUI_FROM_DATABASE=GN Netcom Ltd. + +OUI:0007AA* + ID_OUI_FROM_DATABASE=Quantum Data Inc. + +OUI:00079D* + ID_OUI_FROM_DATABASE=Musashi Co., Ltd. + +OUI:00079E* + ID_OUI_FROM_DATABASE=Ilinx Co., Ltd. + +OUI:000774* + ID_OUI_FROM_DATABASE=GuangZhou Thinker Technology Co. Ltd. + +OUI:000791* + ID_OUI_FROM_DATABASE=International Data Communications, Inc. + +OUI:000798* + ID_OUI_FROM_DATABASE=Selea SRL + +OUI:000797* + ID_OUI_FROM_DATABASE=Netpower Co., Ltd. + +OUI:00078B* + ID_OUI_FROM_DATABASE=Wegener Communications, Inc. + +OUI:000785* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00077B* + ID_OUI_FROM_DATABASE=Millimetrix Broadband Networks + +OUI:000856* + ID_OUI_FROM_DATABASE=Gamatronic Electronic Industries Ltd. + +OUI:00082D* + ID_OUI_FROM_DATABASE=Indus Teqsite Private Limited + +OUI:000821* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000814* + ID_OUI_FROM_DATABASE=TIL Technologies + +OUI:00081A* + ID_OUI_FROM_DATABASE=Sanrad Intelligence Storage Communications (2000) Ltd. + +OUI:00080F* + ID_OUI_FROM_DATABASE=Proximion Fiber Optics AB + +OUI:000809* + ID_OUI_FROM_DATABASE=Systemonic AG + +OUI:000803* + ID_OUI_FROM_DATABASE=Cos Tron + +OUI:0007FF* + ID_OUI_FROM_DATABASE=Gluon Networks + +OUI:0007F9* + ID_OUI_FROM_DATABASE=Sensaphone + +OUI:000894* + ID_OUI_FROM_DATABASE=InnoVISION Multimedia Ltd. + +OUI:00088F* + ID_OUI_FROM_DATABASE=ADVANCED DIGITAL TECHNOLOGY + +OUI:000888* + ID_OUI_FROM_DATABASE=OULLIM Information Technology Inc,. + +OUI:000882* + ID_OUI_FROM_DATABASE=SIGMA CORPORATION + +OUI:00087C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000875* + ID_OUI_FROM_DATABASE=Acorp Electronics Corp. + +OUI:000870* + ID_OUI_FROM_DATABASE=Rasvia Systems, Inc. + +OUI:00086F* + ID_OUI_FROM_DATABASE=Resources Computer Network Ltd. + +OUI:000869* + ID_OUI_FROM_DATABASE=Command-e Technology Co.,Ltd. + +OUI:000863* + ID_OUI_FROM_DATABASE=Entrisphere Inc. + +OUI:00085D* + ID_OUI_FROM_DATABASE=Aastra + +OUI:000862* + ID_OUI_FROM_DATABASE=NEC Eluminant Technologies, Inc. + +OUI:000850* + ID_OUI_FROM_DATABASE=Arizona Instrument Corp. + +OUI:000738* + ID_OUI_FROM_DATABASE=Young Technology Co., Ltd. + +OUI:00073F* + ID_OUI_FROM_DATABASE=Woojyun Systec Co., Ltd. + +OUI:00072C* + ID_OUI_FROM_DATABASE=Fabricom + +OUI:000733* + ID_OUI_FROM_DATABASE=DANCONTROL Engineering + +OUI:000732* + ID_OUI_FROM_DATABASE=AAEON Technology Inc. + +OUI:000716* + ID_OUI_FROM_DATABASE=J & S Marine Ltd. + +OUI:00071B* + ID_OUI_FROM_DATABASE=CDVI Americas Ltd + +OUI:000722* + ID_OUI_FROM_DATABASE=The Nielsen Company + +OUI:00071C* + ID_OUI_FROM_DATABASE=AT&T Fixed Wireless Services + +OUI:00070A* + ID_OUI_FROM_DATABASE=Unicom Automation Co., Ltd. + +OUI:00070F* + ID_OUI_FROM_DATABASE=Fujant, Inc. + +OUI:000709* + ID_OUI_FROM_DATABASE=Westerstrand Urfabrik AB + +OUI:000702* + ID_OUI_FROM_DATABASE=Varian Medical Systems + +OUI:0006F3* + ID_OUI_FROM_DATABASE=AcceLight Networks + +OUI:0006C3* + ID_OUI_FROM_DATABASE=Schindler Elevator Ltd. + +OUI:0006C8* + ID_OUI_FROM_DATABASE=Sumitomo Metal Micro Devices, Inc. + +OUI:0006BF* + ID_OUI_FROM_DATABASE=Accella Technologies Co., Ltd. + +OUI:0006B9* + ID_OUI_FROM_DATABASE=A5TEK Corp. + +OUI:0006B2* + ID_OUI_FROM_DATABASE=Linxtek Co. + +OUI:0006AC* + ID_OUI_FROM_DATABASE=Intersoft Co. + +OUI:0006A6* + ID_OUI_FROM_DATABASE=Artistic Licence Engineering Ltd + +OUI:0006A2* + ID_OUI_FROM_DATABASE=Microtune, Inc. + +OUI:000695* + ID_OUI_FROM_DATABASE=Ensure Technologies, Inc. + +OUI:00069C* + ID_OUI_FROM_DATABASE=Transmode Systems AB + +OUI:000696* + ID_OUI_FROM_DATABASE=Advent Networks + +OUI:0007F3* + ID_OUI_FROM_DATABASE=Thinkengine Networks + +OUI:0007EC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0007F2* + ID_OUI_FROM_DATABASE=IOA Corporation + +OUI:0007E6* + ID_OUI_FROM_DATABASE=edgeflow Canada Inc. + +OUI:0007E0* + ID_OUI_FROM_DATABASE=Palm Inc. + +OUI:0007D9* + ID_OUI_FROM_DATABASE=Splicecom + +OUI:0007DA* + ID_OUI_FROM_DATABASE=Neuro Telecom Co., Ltd. + +OUI:0007D3* + ID_OUI_FROM_DATABASE=SPGPrints B.V. + +OUI:0007CA* + ID_OUI_FROM_DATABASE=Creatix Polymedia Ges Fur Kommunikaitonssysteme + +OUI:0007C4* + ID_OUI_FROM_DATABASE=JEAN Co. Ltd. + +OUI:0007BE* + ID_OUI_FROM_DATABASE=DataLogic SpA + +OUI:00077E* + ID_OUI_FROM_DATABASE=Elrest GmbH + +OUI:00076F* + ID_OUI_FROM_DATABASE=Synoptics Limited + +OUI:00076E* + ID_OUI_FROM_DATABASE=Sinetica Corporation Limited + +OUI:00076A* + ID_OUI_FROM_DATABASE=NEXTEYE Co., Ltd. + +OUI:00075E* + ID_OUI_FROM_DATABASE=Ametek Power Instruments + +OUI:000765* + ID_OUI_FROM_DATABASE=Jade Quantum Technologies, Inc. + +OUI:000764* + ID_OUI_FROM_DATABASE=YoungWoo Telecom Co. Ltd. + +OUI:000757* + ID_OUI_FROM_DATABASE=Topcall International AG + +OUI:000758* + ID_OUI_FROM_DATABASE=Dragonwave + +OUI:000752* + ID_OUI_FROM_DATABASE=Rhythm Watch Co., Ltd. + +OUI:00074B* + ID_OUI_FROM_DATABASE=Daihen Corporation + +OUI:000745* + ID_OUI_FROM_DATABASE=Radlan Computer Communications Ltd. + +OUI:0008C2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0008BB* + ID_OUI_FROM_DATABASE=NetExcell + +OUI:0008B5* + ID_OUI_FROM_DATABASE=TAI GUEN ENTERPRISE CO., LTD + +OUI:0008B6* + ID_OUI_FROM_DATABASE=RouteFree, Inc. + +OUI:0008AF* + ID_OUI_FROM_DATABASE=Novatec Corporation + +OUI:0008A9* + ID_OUI_FROM_DATABASE=SangSang Technology, Inc. + +OUI:0008A8* + ID_OUI_FROM_DATABASE=Systec Co., Ltd. + +OUI:0008A3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00089C* + ID_OUI_FROM_DATABASE=Elecs Industry Co., Ltd. + +OUI:000690* + ID_OUI_FROM_DATABASE=Euracom Communication GmbH + +OUI:00068F* + ID_OUI_FROM_DATABASE=Telemonitor, Inc. + +OUI:000689* + ID_OUI_FROM_DATABASE=yLez Technologies Pte Ltd + +OUI:000683* + ID_OUI_FROM_DATABASE=Bravara Communications, Inc. + +OUI:00D0B9* + ID_OUI_FROM_DATABASE=MICROTEK INTERNATIONAL, INC. + +OUI:00067D* + ID_OUI_FROM_DATABASE=Takasago Ltd. + +OUI:000675* + ID_OUI_FROM_DATABASE=Banderacom, Inc. + +OUI:000679* + ID_OUI_FROM_DATABASE=Konami Corporation + +OUI:000663* + ID_OUI_FROM_DATABASE=Human Technology Co., Ltd. + +OUI:00066F* + ID_OUI_FROM_DATABASE=Korea Data Systems + +OUI:000662* + ID_OUI_FROM_DATABASE=MBM Technology Ltd. + +OUI:000669* + ID_OUI_FROM_DATABASE=Datasound Laboratories Ltd + +OUI:00055A* + ID_OUI_FROM_DATABASE=Power Dsine Ltd. + +OUI:00065C* + ID_OUI_FROM_DATABASE=Malachite Technologies, Inc. + +OUI:000610* + ID_OUI_FROM_DATABASE=Abeona Networks Inc + +OUI:000616* + ID_OUI_FROM_DATABASE=Tel Net Co., Ltd. + +OUI:00060A* + ID_OUI_FROM_DATABASE=Blue2space + +OUI:000604* + ID_OUI_FROM_DATABASE=@Track Communications, Inc. + +OUI:00CBBD* + ID_OUI_FROM_DATABASE=Cambridge Broadband Networks Ltd. + +OUI:000603* + ID_OUI_FROM_DATABASE=Baker Hughes Inc. + +OUI:A06A00* + ID_OUI_FROM_DATABASE=Verilink Corporation + +OUI:0005F5* + ID_OUI_FROM_DATABASE=Geospace Technologies + +OUI:000601* + ID_OUI_FROM_DATABASE=Otanikeiki Co., Ltd. + +OUI:0005E8* + ID_OUI_FROM_DATABASE=TurboWave, Inc. + +OUI:0005F4* + ID_OUI_FROM_DATABASE=System Base Co., Ltd. + +OUI:0005FB* + ID_OUI_FROM_DATABASE=ShareGate, Inc. + +OUI:0005DB* + ID_OUI_FROM_DATABASE=PSI Nentec GmbH + +OUI:0005DF* + ID_OUI_FROM_DATABASE=Electronic Innovation, Inc. + +OUI:0005CF* + ID_OUI_FROM_DATABASE=Thunder River Technologies, Inc. + +OUI:0005C9* + ID_OUI_FROM_DATABASE=LG Innotek Co., Ltd. + +OUI:0005D5* + ID_OUI_FROM_DATABASE=Speedcom Wireless + +OUI:0005BC* + ID_OUI_FROM_DATABASE=Resource Data Management Ltd + +OUI:0005C2* + ID_OUI_FROM_DATABASE=Soronti, Inc. + +OUI:0005B0* + ID_OUI_FROM_DATABASE=Korea Computer Technology Co., Ltd. + +OUI:00059C* + ID_OUI_FROM_DATABASE=Kleinknecht GmbH, Ing. Büro + +OUI:0005B6* + ID_OUI_FROM_DATABASE=INSYS Microelectronics GmbH + +OUI:0005A2* + ID_OUI_FROM_DATABASE=CELOX Networks + +OUI:0005AC* + ID_OUI_FROM_DATABASE=Northern Digital, Inc. + +OUI:0004E5* + ID_OUI_FROM_DATABASE=Glonet Systems, Inc. + +OUI:0004D9* + ID_OUI_FROM_DATABASE=Titan Electronics, Inc. + +OUI:0004D3* + ID_OUI_FROM_DATABASE=Toyokeiki Co., Ltd. + +OUI:0004CC* + ID_OUI_FROM_DATABASE=Peek Traffic B.V. + +OUI:0004C0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0004B9* + ID_OUI_FROM_DATABASE=S.I. Soubou, Inc. + +OUI:0004BA* + ID_OUI_FROM_DATABASE=KDD Media Will Corporation + +OUI:0004AF* + ID_OUI_FROM_DATABASE=Digital Fountain, Inc. + +OUI:0004B4* + ID_OUI_FROM_DATABASE=CIAC + +OUI:0004B3* + ID_OUI_FROM_DATABASE=Videotek, Inc. + +OUI:0004A6* + ID_OUI_FROM_DATABASE=SAF Tehnika Ltd. + +OUI:0004A0* + ID_OUI_FROM_DATABASE=Verity Instruments, Inc. + +OUI:00050C* + ID_OUI_FROM_DATABASE=Network Photonics, Inc. + +OUI:000512* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:000506* + ID_OUI_FROM_DATABASE=Reddo Networks AB + +OUI:0004FC* + ID_OUI_FROM_DATABASE=Stratus Computer (DE), Inc. + +OUI:0004F6* + ID_OUI_FROM_DATABASE=Amphus + +OUI:0004F5* + ID_OUI_FROM_DATABASE=SnowShore Networks, Inc. + +OUI:0004E9* + ID_OUI_FROM_DATABASE=Infiniswitch Corporation + +OUI:0004F0* + ID_OUI_FROM_DATABASE=International Computers, Ltd + +OUI:0004EF* + ID_OUI_FROM_DATABASE=Polestar Corp. + +OUI:0004DF* + ID_OUI_FROM_DATABASE=Teracom Telematica Ltda. + +OUI:000553* + ID_OUI_FROM_DATABASE=DVC Company, Inc. + +OUI:000548* + ID_OUI_FROM_DATABASE=Disco Corporation + +OUI:00054D* + ID_OUI_FROM_DATABASE=Brans Technologies, Inc. + +OUI:000542* + ID_OUI_FROM_DATABASE=Otari, Inc. + +OUI:00053C* + ID_OUI_FROM_DATABASE=XIRCOM + +OUI:00052F* + ID_OUI_FROM_DATABASE=Leviton Network Solutions + +OUI:00053B* + ID_OUI_FROM_DATABASE=Harbour Networks Ltd., Co. Beijing + +OUI:000535* + ID_OUI_FROM_DATABASE=Chip PC Ltd. + +OUI:000529* + ID_OUI_FROM_DATABASE=Shanghai Broadan Communication Technology Co., Ltd + +OUI:000523* + ID_OUI_FROM_DATABASE=AVL List GmbH + +OUI:000522* + ID_OUI_FROM_DATABASE=LEA*D Corporation, Inc. + +OUI:00051C* + ID_OUI_FROM_DATABASE=Xnet Technology Corp. + +OUI:000516* + ID_OUI_FROM_DATABASE=SMART Modular Technologies + +OUI:000650* + ID_OUI_FROM_DATABASE=Tiburon Networks, Inc. + +OUI:000656* + ID_OUI_FROM_DATABASE=Tactel AB + +OUI:00062D* + ID_OUI_FROM_DATABASE=TouchStar Technologies, L.L.C. + +OUI:000649* + ID_OUI_FROM_DATABASE=3M Deutschland GmbH + +OUI:000643* + ID_OUI_FROM_DATABASE=SONO Computer Co., Ltd. + +OUI:00064A* + ID_OUI_FROM_DATABASE=Honeywell Co., Ltd. (KOREA) + +OUI:00063F* + ID_OUI_FROM_DATABASE=Everex Communications Inc. + +OUI:000639* + ID_OUI_FROM_DATABASE=Newtec + +OUI:000633* + ID_OUI_FROM_DATABASE=Cross Match Technologies GmbH + +OUI:000626* + ID_OUI_FROM_DATABASE=MWE GmbH + +OUI:00061D* + ID_OUI_FROM_DATABASE=MIP Telecom, Inc. + +OUI:000623* + ID_OUI_FROM_DATABASE=MGE UPS Systems France + +OUI:000589* + ID_OUI_FROM_DATABASE=National Datacomputer + +OUI:000595* + ID_OUI_FROM_DATABASE=Alesis Corporation + +OUI:00058F* + ID_OUI_FROM_DATABASE=CLCsoft co. + +OUI:000596* + ID_OUI_FROM_DATABASE=Genotech Co., Ltd. + +OUI:00057D* + ID_OUI_FROM_DATABASE=Sun Communications, Inc. + +OUI:00057C* + ID_OUI_FROM_DATABASE=RCO Security AB + +OUI:000583* + ID_OUI_FROM_DATABASE=ImageCom Limited + +OUI:000573* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000572* + ID_OUI_FROM_DATABASE=Deonet Co., Ltd. + +OUI:00056C* + ID_OUI_FROM_DATABASE=Hung Chang Co., Ltd. + +OUI:000566* + ID_OUI_FROM_DATABASE=Secui.com Corporation + +OUI:000560* + ID_OUI_FROM_DATABASE=LEADER COMM.CO., LTD + +OUI:000559* + ID_OUI_FROM_DATABASE=Intracom S.A. + +OUI:0004A5* + ID_OUI_FROM_DATABASE=Barco Projection Systems NV + +OUI:000499* + ID_OUI_FROM_DATABASE=Chino Corporation + +OUI:00048D* + ID_OUI_FROM_DATABASE=Teo Technologies, Inc + +OUI:000493* + ID_OUI_FROM_DATABASE=Tsinghua Unisplendour Co., Ltd. + +OUI:000484* + ID_OUI_FROM_DATABASE=Amann GmbH + +OUI:00048A* + ID_OUI_FROM_DATABASE=Temia Vertriebs GmbH + +OUI:00047A* + ID_OUI_FROM_DATABASE=AXXESSIT ASA + +OUI:000474* + ID_OUI_FROM_DATABASE=LEGRAND + +OUI:00046E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000473* + ID_OUI_FROM_DATABASE=Photonex Corporation + +OUI:000467* + ID_OUI_FROM_DATABASE=Wuhan Research Institute of MII + +OUI:000461* + ID_OUI_FROM_DATABASE=EPOX Computer Co., Ltd. + +OUI:0003D9* + ID_OUI_FROM_DATABASE=Secheron SA + +OUI:0003D2* + ID_OUI_FROM_DATABASE=Crossbeam Systems, Inc. + +OUI:0003CD* + ID_OUI_FROM_DATABASE=Clovertech, Inc. + +OUI:0003CA* + ID_OUI_FROM_DATABASE=MTS Systems Corp. + +OUI:0003C6* + ID_OUI_FROM_DATABASE=ICUE Systems, Inc. + +OUI:0003BF* + ID_OUI_FROM_DATABASE=Centerpoint Broadband Technologies, Inc. + +OUI:0003BA* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:0003AF* + ID_OUI_FROM_DATABASE=Paragea Communications + +OUI:0003B4* + ID_OUI_FROM_DATABASE=Macrotek International Corp. + +OUI:0003AC* + ID_OUI_FROM_DATABASE=Fronius Schweissmaschinen + +OUI:0003A8* + ID_OUI_FROM_DATABASE=IDOT Computers, Inc. + +OUI:0003A1* + ID_OUI_FROM_DATABASE=HIPER Information & Communication, Inc. + +OUI:000399* + ID_OUI_FROM_DATABASE=Dongju Informations & Communications Co., Ltd. + +OUI:00039C* + ID_OUI_FROM_DATABASE=OptiMight Communications, Inc. + +OUI:000390* + ID_OUI_FROM_DATABASE=Digital Video Communications, Inc. + +OUI:000395* + ID_OUI_FROM_DATABASE=California Amplifier + +OUI:000380* + ID_OUI_FROM_DATABASE=SSH Communications Security Corp. + +OUI:000374* + ID_OUI_FROM_DATABASE=Control Microsystems + +OUI:0002F0* + ID_OUI_FROM_DATABASE=AME Optimedia Technology Co., Ltd. + +OUI:000379* + ID_OUI_FROM_DATABASE=Proscend Communications, Inc. + +OUI:000371* + ID_OUI_FROM_DATABASE=Acomz Networks Corp. + +OUI:00036D* + ID_OUI_FROM_DATABASE=Runtop, Inc. + +OUI:0002E3* + ID_OUI_FROM_DATABASE=LITE-ON Communications, Inc. + +OUI:0002DE* + ID_OUI_FROM_DATABASE=Astrodesign, Inc. + +OUI:0002DB* + ID_OUI_FROM_DATABASE=NETSEC + +OUI:0002D7* + ID_OUI_FROM_DATABASE=EMPEG Ltd + +OUI:0002D2* + ID_OUI_FROM_DATABASE=Workstation AG + +OUI:000223* + ID_OUI_FROM_DATABASE=ClickTV + +OUI:0002CB* + ID_OUI_FROM_DATABASE=TriState Ltd. + +OUI:0002C4* + ID_OUI_FROM_DATABASE=Vector International BVBA + +OUI:0002BF* + ID_OUI_FROM_DATABASE=dotRocket, Inc. + +OUI:0002BB* + ID_OUI_FROM_DATABASE=Continuous Computing Corp + +OUI:0002BC* + ID_OUI_FROM_DATABASE=LVL 7 Systems, Inc. + +OUI:0002B6* + ID_OUI_FROM_DATABASE=Acrosser Technology Co., Ltd. + +OUI:0002AF* + ID_OUI_FROM_DATABASE=TeleCruz Technology, Inc. + +OUI:0002AA* + ID_OUI_FROM_DATABASE=PLcom Co., Ltd. + +OUI:00045B* + ID_OUI_FROM_DATABASE=Techsan Electronics Co., Ltd. + +OUI:00044E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00044F* + ID_OUI_FROM_DATABASE=Schubert System Elektronik Gmbh + +OUI:000454* + ID_OUI_FROM_DATABASE=Quadriga UK + +OUI:000445* + ID_OUI_FROM_DATABASE=LMS Skalar Instruments GmbH + +OUI:00044A* + ID_OUI_FROM_DATABASE=iPolicy Networks, Inc. + +OUI:000444* + ID_OUI_FROM_DATABASE=Western Multiplex Corporation + +OUI:00043E* + ID_OUI_FROM_DATABASE=Telencomm + +OUI:000432* + ID_OUI_FROM_DATABASE=Voyetra Turtle Beach, Inc. + +OUI:000437* + ID_OUI_FROM_DATABASE=Powin Information Technology, Inc. + +OUI:00042B* + ID_OUI_FROM_DATABASE=IT Access Co., Ltd. + +OUI:000361* + ID_OUI_FROM_DATABASE=Widcomm, Inc. + +OUI:00035A* + ID_OUI_FROM_DATABASE=Photron Limited + +OUI:000355* + ID_OUI_FROM_DATABASE=TeraBeam Internet Systems + +OUI:000353* + ID_OUI_FROM_DATABASE=Mitac, Inc. + +OUI:00034F* + ID_OUI_FROM_DATABASE=Sur-Gard Security + +OUI:00034A* + ID_OUI_FROM_DATABASE=RIAS Corporation + +OUI:000346* + ID_OUI_FROM_DATABASE=Hitachi Kokusai Electric, Inc. + +OUI:000344* + ID_OUI_FROM_DATABASE=Tietech.Co., Ltd. + +OUI:000343* + ID_OUI_FROM_DATABASE=Martin Professional A/S + +OUI:000334* + ID_OUI_FROM_DATABASE=Newport Electronics + +OUI:000337* + ID_OUI_FROM_DATABASE=Vaone, Inc. + +OUI:00033C* + ID_OUI_FROM_DATABASE=Daiden Co., Ltd. + +OUI:000329* + ID_OUI_FROM_DATABASE=F3, Inc. + +OUI:000330* + ID_OUI_FROM_DATABASE=Imagenics, Co., Ltd. + +OUI:000321* + ID_OUI_FROM_DATABASE=Reco Research Co., Ltd. + +OUI:000324* + ID_OUI_FROM_DATABASE=SANYO Consumer Electronics Co., Ltd. + +OUI:00031B* + ID_OUI_FROM_DATABASE=Cellvision Systems, Inc. + +OUI:0001A8* + ID_OUI_FROM_DATABASE=Welltech Computer Co., Ltd. + +OUI:00030F* + ID_OUI_FROM_DATABASE=Digital China (Shanghai) Networks Ltd. + +OUI:000314* + ID_OUI_FROM_DATABASE=Teleware Network Systems + +OUI:00030C* + ID_OUI_FROM_DATABASE=Telesoft Technologies Ltd. + +OUI:000308* + ID_OUI_FROM_DATABASE=AM Communications, Inc. + +OUI:0002FC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000301* + ID_OUI_FROM_DATABASE=EXFO + +OUI:0002F9* + ID_OUI_FROM_DATABASE=MIMOS Berhad + +OUI:0002F5* + ID_OUI_FROM_DATABASE=VIVE Synergies, Inc. + +OUI:0002EA* + ID_OUI_FROM_DATABASE=Focus Enhancements + +OUI:000269* + ID_OUI_FROM_DATABASE=Nadatel Co., Ltd + +OUI:000265* + ID_OUI_FROM_DATABASE=Virditech Co. Ltd. + +OUI:00025E* + ID_OUI_FROM_DATABASE=High Technology Ltd + +OUI:000261* + ID_OUI_FROM_DATABASE=Tilgin AB + +OUI:000259* + ID_OUI_FROM_DATABASE=Tsann Kuen China (Shanghai)Enterprise Co., Ltd. IT Group + +OUI:000255* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:000249* + ID_OUI_FROM_DATABASE=Aviv Infocom Co, Ltd. + +OUI:000250* + ID_OUI_FROM_DATABASE=Geyser Networks, Inc. + +OUI:000242* + ID_OUI_FROM_DATABASE=Videoframe Systems + +OUI:000244* + ID_OUI_FROM_DATABASE=SURECOM Technology Co. + +OUI:00022C* + ID_OUI_FROM_DATABASE=ABB Bomem, Inc. + +OUI:00023A* + ID_OUI_FROM_DATABASE=ZSK Stickmaschinen GmbH + +OUI:000425* + ID_OUI_FROM_DATABASE=Atmel Corporation + +OUI:000419* + ID_OUI_FROM_DATABASE=Fibercycle Networks, Inc. + +OUI:00041A* + ID_OUI_FROM_DATABASE=Ines Test and Measurement GmbH & CoKG + +OUI:000414* + ID_OUI_FROM_DATABASE=Umezawa Musen Denki Co., Ltd. + +OUI:000407* + ID_OUI_FROM_DATABASE=Topcon Positioning Systems, Inc. + +OUI:0003F7* + ID_OUI_FROM_DATABASE=Plast-Control GmbH + +OUI:0003FE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0003FD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000401* + ID_OUI_FROM_DATABASE=Osaki Electric Co., Ltd. + +OUI:0003F0* + ID_OUI_FROM_DATABASE=Redfern Broadband Networks + +OUI:0003EB* + ID_OUI_FROM_DATABASE=Atrica + +OUI:0003E5* + ID_OUI_FROM_DATABASE=Hermstedt SG + +OUI:0002A3* + ID_OUI_FROM_DATABASE=ABB Switzerland Ltd, Power Systems + +OUI:000298* + ID_OUI_FROM_DATABASE=Broadframe Corporation + +OUI:000292* + ID_OUI_FROM_DATABASE=Logic Innovations, Inc. + +OUI:00028D* + ID_OUI_FROM_DATABASE=Movita Technologies, Inc. + +OUI:000283* + ID_OUI_FROM_DATABASE=Spectrum Controls, Inc. + +OUI:000277* + ID_OUI_FROM_DATABASE=Cash Systemes Industrie + +OUI:00027C* + ID_OUI_FROM_DATABASE=Trilithic, Inc. + +OUI:000275* + ID_OUI_FROM_DATABASE=SMART Technologies, Inc. + +OUI:000270* + ID_OUI_FROM_DATABASE=Crewave Co., Ltd. + +OUI:000104* + ID_OUI_FROM_DATABASE=DVICO Co., Ltd. + +OUI:000110* + ID_OUI_FROM_DATABASE=Gotham Networks + +OUI:00010C* + ID_OUI_FROM_DATABASE=System Talks Inc. + +OUI:000113* + ID_OUI_FROM_DATABASE=OLYMPUS CORPORATION + +OUI:000100* + ID_OUI_FROM_DATABASE=EQUIP'TRANS + +OUI:00B0AC* + ID_OUI_FROM_DATABASE=SIAE-Microelettronica S.p.A. + +OUI:00B017* + ID_OUI_FROM_DATABASE=InfoGear Technology Corp. + +OUI:0030F0* + ID_OUI_FROM_DATABASE=Uniform Industrial Corp. + +OUI:00B080* + ID_OUI_FROM_DATABASE=Mannesmann Ipulsys B.V. + +OUI:00B09A* + ID_OUI_FROM_DATABASE=Morrow Technologies Corp. + +OUI:00B091* + ID_OUI_FROM_DATABASE=Transmeta Corp. + +OUI:0030BE* + ID_OUI_FROM_DATABASE=City-Net Technology, Inc. + +OUI:000233* + ID_OUI_FROM_DATABASE=Mantra Communications, Inc. + +OUI:00022F* + ID_OUI_FROM_DATABASE=P-Cube, Ltd. + +OUI:000227* + ID_OUI_FROM_DATABASE=ESD Electronic System Design GmbH + +OUI:00021F* + ID_OUI_FROM_DATABASE=Aculab PLC + +OUI:00021B* + ID_OUI_FROM_DATABASE=Kollmorgen-Servotronix + +OUI:00020C* + ID_OUI_FROM_DATABASE=Metro-Optix + +OUI:000218* + ID_OUI_FROM_DATABASE=Advanced Scientific Corp + +OUI:000213* + ID_OUI_FROM_DATABASE=S.D.E.L. + +OUI:00020F* + ID_OUI_FROM_DATABASE=AATR + +OUI:0001F9* + ID_OUI_FROM_DATABASE=TeraGlobal Communications Corp. + +OUI:000200* + ID_OUI_FROM_DATABASE=Net & Sys Co., Ltd. + +OUI:0001FC* + ID_OUI_FROM_DATABASE=Keyence Corporation + +OUI:0001F3* + ID_OUI_FROM_DATABASE=QPS, Inc. + +OUI:0001E4* + ID_OUI_FROM_DATABASE=Sitera, Inc. + +OUI:0001EB* + ID_OUI_FROM_DATABASE=C-COM Corporation + +OUI:0001F0* + ID_OUI_FROM_DATABASE=Tridium, Inc. + +OUI:0001D4* + ID_OUI_FROM_DATABASE=Leisure Time, Inc. + +OUI:0001D8* + ID_OUI_FROM_DATABASE=Teltronics, Inc. + +OUI:0001C6* + ID_OUI_FROM_DATABASE=Quarry Technologies + +OUI:0001CC* + ID_OUI_FROM_DATABASE=Japan Total Design Communication Co., Ltd. + +OUI:0001D1* + ID_OUI_FROM_DATABASE=CoNet Communications, Inc. + +OUI:0001B3* + ID_OUI_FROM_DATABASE=Precision Electronic Manufacturing + +OUI:000160* + ID_OUI_FROM_DATABASE=ELMEX Co., LTD. + +OUI:00015E* + ID_OUI_FROM_DATABASE=BEST TECHNOLOGY CO., LTD. + +OUI:000162* + ID_OUI_FROM_DATABASE=Cygnet Technologies, Inc. + +OUI:000169* + ID_OUI_FROM_DATABASE=Celestix Networks Pte Ltd. + +OUI:000175* + ID_OUI_FROM_DATABASE=Radiant Communications Corp. + +OUI:000159* + ID_OUI_FROM_DATABASE=S1 Corporation + +OUI:000165* + ID_OUI_FROM_DATABASE=AirSwitch Corporation + +OUI:000171* + ID_OUI_FROM_DATABASE=Allied Data Technologies + +OUI:000157* + ID_OUI_FROM_DATABASE=SYSWAVE CO., LTD + +OUI:000153* + ID_OUI_FROM_DATABASE=ARCHTEK TELECOM CORPORATION + +OUI:000144* + ID_OUI_FROM_DATABASE=EMC Corporation + +OUI:003038* + ID_OUI_FROM_DATABASE=XCP, INC. + +OUI:0030DB* + ID_OUI_FROM_DATABASE=Mindready Solutions, Inc. + +OUI:00306A* + ID_OUI_FROM_DATABASE=PENTA MEDIA CO., LTD. + +OUI:003021* + ID_OUI_FROM_DATABASE=HSING TECH. ENTERPRISE CO.,LTD + +OUI:0030EA* + ID_OUI_FROM_DATABASE=TeraForce Technology Corporation + +OUI:0030F4* + ID_OUI_FROM_DATABASE=STARDOT TECHNOLOGIES + +OUI:003087* + ID_OUI_FROM_DATABASE=VEGA GRIESHABER KG + +OUI:003000* + ID_OUI_FROM_DATABASE=ALLWELL TECHNOLOGY CORP. + +OUI:003034* + ID_OUI_FROM_DATABASE=SET ENGINEERING + +OUI:00308D* + ID_OUI_FROM_DATABASE=Pinnacle Systems, Inc. + +OUI:00304B* + ID_OUI_FROM_DATABASE=ORBACOM SYSTEMS, INC. + +OUI:0030FA* + ID_OUI_FROM_DATABASE=TELICA, INC. + +OUI:0001B1* + ID_OUI_FROM_DATABASE=General Bandwidth + +OUI:0001BB* + ID_OUI_FROM_DATABASE=Frequentis + +OUI:0001B7* + ID_OUI_FROM_DATABASE=Centos, Inc. + +OUI:0001AF* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:0001AB* + ID_OUI_FROM_DATABASE=Main Street Networks + +OUI:000191* + ID_OUI_FROM_DATABASE=SYRED Data Systems + +OUI:00019D* + ID_OUI_FROM_DATABASE=E-Control Systems, Inc. + +OUI:0001A4* + ID_OUI_FROM_DATABASE=Microlink Corporation + +OUI:000199* + ID_OUI_FROM_DATABASE=HeiSei Electronics + +OUI:0001A0* + ID_OUI_FROM_DATABASE=Infinilink Corporation + +OUI:00017C* + ID_OUI_FROM_DATABASE=AG-E GmbH + +OUI:000188* + ID_OUI_FROM_DATABASE=LXCO Technologies ag + +OUI:000178* + ID_OUI_FROM_DATABASE=MARGI Systems, Inc. + +OUI:00018B* + ID_OUI_FROM_DATABASE=NetLinks Co., Ltd. + +OUI:0030F5* + ID_OUI_FROM_DATABASE=Wild Lab. Ltd. + +OUI:000184* + ID_OUI_FROM_DATABASE=SIEB & MEYER AG + +OUI:00303E* + ID_OUI_FROM_DATABASE=Radcom Ltd. + +OUI:0030D7* + ID_OUI_FROM_DATABASE=Innovative Systems, L.L.C. + +OUI:0030FC* + ID_OUI_FROM_DATABASE=Terawave Communications, Inc. + +OUI:00300F* + ID_OUI_FROM_DATABASE=IMT - Information Management T + +OUI:003004* + ID_OUI_FROM_DATABASE=LEADTEK RESEARCH INC. + +OUI:003018* + ID_OUI_FROM_DATABASE=Jetway Information Co., Ltd. + +OUI:003088* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:0030CA* + ID_OUI_FROM_DATABASE=Discovery Com + +OUI:00304F* + ID_OUI_FROM_DATABASE=PLANET Technology Corporation + +OUI:00014B* + ID_OUI_FROM_DATABASE=Ennovate Networks, Inc. + +OUI:00012C* + ID_OUI_FROM_DATABASE=Aravox Technologies, Inc. + +OUI:000134* + ID_OUI_FROM_DATABASE=Selectron Systems AG + +OUI:00013B* + ID_OUI_FROM_DATABASE=BNA SYSTEMS + +OUI:000147* + ID_OUI_FROM_DATABASE=Zhone Technologies + +OUI:00012B* + ID_OUI_FROM_DATABASE=TELENET Co., Ltd. + +OUI:00011C* + ID_OUI_FROM_DATABASE=Universal Talkware Corporation + +OUI:000123* + ID_OUI_FROM_DATABASE=DIGITAL ELECTRONICS CORP. + +OUI:00011F* + ID_OUI_FROM_DATABASE=RC Networks, Inc. + +OUI:003045* + ID_OUI_FROM_DATABASE=Village Networks, Inc. (VNI) + +OUI:0030BB* + ID_OUI_FROM_DATABASE=CacheFlow, Inc. + +OUI:003053* + ID_OUI_FROM_DATABASE=Basler AG + +OUI:003072* + ID_OUI_FROM_DATABASE=Intellibyte Inc. + +OUI:0030B1* + ID_OUI_FROM_DATABASE=TrunkNet + +OUI:0030A7* + ID_OUI_FROM_DATABASE=SCHWEITZER ENGINEERING + +OUI:00D086* + ID_OUI_FROM_DATABASE=FOVEON, INC. + +OUI:00D05A* + ID_OUI_FROM_DATABASE=SYMBIONICS, LTD. + +OUI:00D01A* + ID_OUI_FROM_DATABASE=URMET TLC S.P.A. + +OUI:00D0F3* + ID_OUI_FROM_DATABASE=SOLARI DI UDINE SPA + +OUI:00D089* + ID_OUI_FROM_DATABASE=DYNACOLOR, INC. + +OUI:00D08D* + ID_OUI_FROM_DATABASE=PHOENIX GROUP, INC. + +OUI:00D09C* + ID_OUI_FROM_DATABASE=KAPADIA COMMUNICATIONS + +OUI:00D0FE* + ID_OUI_FROM_DATABASE=ASTRAL POINT + +OUI:00D0DC* + ID_OUI_FROM_DATABASE=MODULAR MINING SYSTEMS, INC. + +OUI:00D062* + ID_OUI_FROM_DATABASE=DIGIGRAM + +OUI:00D0A7* + ID_OUI_FROM_DATABASE=TOKYO SOKKI KENKYUJO CO., LTD. + +OUI:00D032* + ID_OUI_FROM_DATABASE=YANO ELECTRIC CO., LTD. + +OUI:00D054* + ID_OUI_FROM_DATABASE=SAS INSTITUTE INC. + +OUI:00D0EB* + ID_OUI_FROM_DATABASE=LIGHTERA NETWORKS, INC. + +OUI:00D01E* + ID_OUI_FROM_DATABASE=PINGTEL CORP. + +OUI:00D0A9* + ID_OUI_FROM_DATABASE=SHINANO KENSHI CO., LTD. + +OUI:0030E9* + ID_OUI_FROM_DATABASE=GMA COMMUNICATION MANUFACT'G + +OUI:003027* + ID_OUI_FROM_DATABASE=KERBANGO, INC. + +OUI:0030F6* + ID_OUI_FROM_DATABASE=SECURELOGIX CORPORATION + +OUI:0030B6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0030B2* + ID_OUI_FROM_DATABASE=L-3 Sonoma EO + +OUI:0030D6* + ID_OUI_FROM_DATABASE=MSC VERTRIEBS GMBH + +OUI:003008* + ID_OUI_FROM_DATABASE=AVIO DIGITAL, INC. + +OUI:00306D* + ID_OUI_FROM_DATABASE=LUCENT TECHNOLOGIES + +OUI:0030E4* + ID_OUI_FROM_DATABASE=CHIYODA SYSTEM RIKEN + +OUI:00301A* + ID_OUI_FROM_DATABASE=SMARTBRIDGES PTE. LTD. + +OUI:0030CD* + ID_OUI_FROM_DATABASE=CONEXANT SYSTEMS, INC. + +OUI:003001* + ID_OUI_FROM_DATABASE=SMP + +OUI:0030E1* + ID_OUI_FROM_DATABASE=Network Equipment Technologies, Inc. + +OUI:0050A7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D0EE* + ID_OUI_FROM_DATABASE=DICTAPHONE CORPORATION + +OUI:00D0B8* + ID_OUI_FROM_DATABASE=Iomega Corporation + +OUI:005045* + ID_OUI_FROM_DATABASE=RIOWORKS SOLUTIONS, INC. + +OUI:00507C* + ID_OUI_FROM_DATABASE=VIDEOCON AG + +OUI:005065* + ID_OUI_FROM_DATABASE=TDK-Lambda Corporation + +OUI:0050C7* + ID_OUI_FROM_DATABASE=Private + +OUI:0050F4* + ID_OUI_FROM_DATABASE=SIGMATEK GMBH & CO. KG + +OUI:005076* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:005075* + ID_OUI_FROM_DATABASE=KESTREL SOLUTIONS + +OUI:005090* + ID_OUI_FROM_DATABASE=DCTRI + +OUI:0050ED* + ID_OUI_FROM_DATABASE=ANDA NETWORKS + +OUI:005096* + ID_OUI_FROM_DATABASE=SALIX TECHNOLOGIES, INC. + +OUI:00509B* + ID_OUI_FROM_DATABASE=SWITCHCORE AB + +OUI:0050A9* + ID_OUI_FROM_DATABASE=MOLDAT WIRELESS TECHNOLGIES + +OUI:00503C* + ID_OUI_FROM_DATABASE=TSINGHUA NOVEL ELECTRONICS + +OUI:005030* + ID_OUI_FROM_DATABASE=FUTURE PLUS SYSTEMS + +OUI:005037* + ID_OUI_FROM_DATABASE=KOGA ELECTRONICS CO. + +OUI:00501F* + ID_OUI_FROM_DATABASE=MRG SYSTEMS, LTD. + +OUI:005092* + ID_OUI_FROM_DATABASE=Rigaku Corporation Osaka Plant + +OUI:00501C* + ID_OUI_FROM_DATABASE=JATOM SYSTEMS, INC. + +OUI:00505C* + ID_OUI_FROM_DATABASE=TUNDO CORPORATION + +OUI:005068* + ID_OUI_FROM_DATABASE=ELECTRONIC INDUSTRIES ASSOCIATION + +OUI:00501A* + ID_OUI_FROM_DATABASE=IQinVision + +OUI:005063* + ID_OUI_FROM_DATABASE=OY COMSEL SYSTEM AB + +OUI:0050DE* + ID_OUI_FROM_DATABASE=SIGNUM SYSTEMS CORP. + +OUI:00507B* + ID_OUI_FROM_DATABASE=MERLOT COMMUNICATIONS + +OUI:005078* + ID_OUI_FROM_DATABASE=MEGATON HOUSE, LTD. + +OUI:00508F* + ID_OUI_FROM_DATABASE=ASITA TECHNOLOGIES INT'L LTD. + +OUI:005057* + ID_OUI_FROM_DATABASE=BROADBAND ACCESS SYSTEMS + +OUI:005087* + ID_OUI_FROM_DATABASE=TERASAKI ELECTRIC CO., LTD. + +OUI:00D03E* + ID_OUI_FROM_DATABASE=ROCKETCHIPS, INC. + +OUI:00D03F* + ID_OUI_FROM_DATABASE=AMERICAN COMMUNICATION + +OUI:00D033* + ID_OUI_FROM_DATABASE=DALIAN DAXIAN NETWORK + +OUI:00D0CE* + ID_OUI_FROM_DATABASE=ASYST ELECTRONIC + +OUI:00D090* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D0B6* + ID_OUI_FROM_DATABASE=CRESCENT NETWORKS, INC. + +OUI:00D0D2* + ID_OUI_FROM_DATABASE=EPILOG CORPORATION + +OUI:0050B6* + ID_OUI_FROM_DATABASE=GOOD WAY IND. CO., LTD. + +OUI:0050FF* + ID_OUI_FROM_DATABASE=HAKKO ELECTRONICS CO., LTD. + +OUI:005032* + ID_OUI_FROM_DATABASE=PICAZO COMMUNICATIONS, INC. + +OUI:0050DA* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:0050F9* + ID_OUI_FROM_DATABASE=Sensormatic Electronics LLC + +OUI:0050F6* + ID_OUI_FROM_DATABASE=PAN-INTERNATIONAL INDUSTRIAL CORP. + +OUI:00506C* + ID_OUI_FROM_DATABASE=Beijer Electronics Products AB + +OUI:0050A5* + ID_OUI_FROM_DATABASE=CAPITOL BUSINESS SYSTEMS, LTD. + +OUI:005000* + ID_OUI_FROM_DATABASE=NEXO COMMUNICATIONS, INC. + +OUI:00D071* + ID_OUI_FROM_DATABASE=ECHELON CORP. + +OUI:00D066* + ID_OUI_FROM_DATABASE=WINTRISS ENGINEERING CORP. + +OUI:00D06F* + ID_OUI_FROM_DATABASE=KMC CONTROLS + +OUI:00D04B* + ID_OUI_FROM_DATABASE=LA CIE GROUP S.A. + +OUI:00D060* + ID_OUI_FROM_DATABASE=Panasonic Europe Ltd. + +OUI:00D002* + ID_OUI_FROM_DATABASE=DITECH CORPORATION + +OUI:00D0A6* + ID_OUI_FROM_DATABASE=LANBIRD TECHNOLOGY CO., LTD. + +OUI:00D0DE* + ID_OUI_FROM_DATABASE=PHILIPS MULTIMEDIA NETWORK + +OUI:00D083* + ID_OUI_FROM_DATABASE=INVERTEX, INC. + +OUI:00D038* + ID_OUI_FROM_DATABASE=FIVEMERE, LTD. + +OUI:00D00C* + ID_OUI_FROM_DATABASE=SNIJDER MICRO SYSTEMS + +OUI:00D0F2* + ID_OUI_FROM_DATABASE=MONTEREY NETWORKS + +OUI:00D07B* + ID_OUI_FROM_DATABASE=COMCAM INTERNATIONAL INC + +OUI:00D05D* + ID_OUI_FROM_DATABASE=INTELLIWORXX, INC. + +OUI:00D00D* + ID_OUI_FROM_DATABASE=MICROMERITICS INSTRUMENT + +OUI:00D04C* + ID_OUI_FROM_DATABASE=EUROTEL TELECOM LTD. + +OUI:00D0FD* + ID_OUI_FROM_DATABASE=OPTIMA TELE.COM, INC. + +OUI:0030D8* + ID_OUI_FROM_DATABASE=SITEK + +OUI:003062* + ID_OUI_FROM_DATABASE=IP Video Networks Inc + +OUI:003081* + ID_OUI_FROM_DATABASE=ALTOS C&C + +OUI:00D0B0* + ID_OUI_FROM_DATABASE=BITSWITCH LTD. + +OUI:00D044* + ID_OUI_FROM_DATABASE=ALIDIAN NETWORKS, INC. + +OUI:00D004* + ID_OUI_FROM_DATABASE=PENTACOM LTD. + +OUI:00D045* + ID_OUI_FROM_DATABASE=KVASER AB + +OUI:00D0D0* + ID_OUI_FROM_DATABASE=ZHONGXING TELECOM LTD. + +OUI:00902C* + ID_OUI_FROM_DATABASE=DATA & CONTROL EQUIPMENT LTD. + +OUI:009049* + ID_OUI_FROM_DATABASE=ENTRIDIA CORPORATION + +OUI:009043* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:009076* + ID_OUI_FROM_DATABASE=FMT AIRCRAFT GATE SUPPORT SYSTEMS AB + +OUI:009017* + ID_OUI_FROM_DATABASE=Zypcom, Inc + +OUI:00907B* + ID_OUI_FROM_DATABASE=E-TECH, INC. + +OUI:00102A* + ID_OUI_FROM_DATABASE=ZF MICROSYSTEMS, INC. + +OUI:00107D* + ID_OUI_FROM_DATABASE=AURORA COMMUNICATIONS, LTD. + +OUI:00101C* + ID_OUI_FROM_DATABASE=OHM TECHNOLOGIES INTL, LLC + +OUI:00106C* + ID_OUI_FROM_DATABASE=EDNT GmbH + +OUI:0010D4* + ID_OUI_FROM_DATABASE=STORAGE COMPUTER CORPORATION + +OUI:0010BF* + ID_OUI_FROM_DATABASE=InterAir Wireless + +OUI:001036* + ID_OUI_FROM_DATABASE=INTER-TEL INTEGRATED SYSTEMS + +OUI:001026* + ID_OUI_FROM_DATABASE=ACCELERATED NETWORKS, INC. + +OUI:00104B* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:000629* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:001004* + ID_OUI_FROM_DATABASE=THE BRANTLEY COILE COMPANY,INC + +OUI:00103A* + ID_OUI_FROM_DATABASE=DIAMOND NETWORK TECH + +OUI:0010D8* + ID_OUI_FROM_DATABASE=CALISTA + +OUI:001031* + ID_OUI_FROM_DATABASE=OBJECTIVE COMMUNICATIONS, INC. + +OUI:00107E* + ID_OUI_FROM_DATABASE=BACHMANN ELECTRONIC GmbH + +OUI:0010C0* + ID_OUI_FROM_DATABASE=ARMA, Inc. + +OUI:001016* + ID_OUI_FROM_DATABASE=T.SQWARE + +OUI:00103D* + ID_OUI_FROM_DATABASE=PHASECOM, LTD. + +OUI:0010C2* + ID_OUI_FROM_DATABASE=WILLNET, INC. + +OUI:00107A* + ID_OUI_FROM_DATABASE=AmbiCom, Inc. + +OUI:0010C4* + ID_OUI_FROM_DATABASE=MEDIA GLOBAL LINKS CO., LTD. + +OUI:0010EB* + ID_OUI_FROM_DATABASE=SELSIUS SYSTEMS, INC. + +OUI:0010FE* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:00102E* + ID_OUI_FROM_DATABASE=NETWORK SYSTEMS & TECHNOLOGIES PVT. LTD. + +OUI:00103E* + ID_OUI_FROM_DATABASE=NETSCHOOLS CORPORATION + +OUI:001049* + ID_OUI_FROM_DATABASE=ShoreTel, Inc + +OUI:00105E* + ID_OUI_FROM_DATABASE=Spirent plc, Service Assurance Broadband + +OUI:005088* + ID_OUI_FROM_DATABASE=AMANO CORPORATION + +OUI:0050A8* + ID_OUI_FROM_DATABASE=OpenCon Systems, Inc. + +OUI:005062* + ID_OUI_FROM_DATABASE=KOUWELL ELECTRONICS CORP. ** + +OUI:0050B1* + ID_OUI_FROM_DATABASE=GIDDINGS & LEWIS + +OUI:00500C* + ID_OUI_FROM_DATABASE=e-Tek Labs, Inc. + +OUI:005091* + ID_OUI_FROM_DATABASE=NETACCESS, INC. + +OUI:005097* + ID_OUI_FROM_DATABASE=MMC-EMBEDDED COMPUTERTECHNIK GmbH + +OUI:0050AF* + ID_OUI_FROM_DATABASE=INTERGON, INC. + +OUI:0050EB* + ID_OUI_FROM_DATABASE=ALPHA-TOP CORPORATION + +OUI:0050BC* + ID_OUI_FROM_DATABASE=HAMMER STORAGE SOLUTIONS + +OUI:0090C3* + ID_OUI_FROM_DATABASE=TOPIC SEMICONDUCTOR CORP. + +OUI:0090EC* + ID_OUI_FROM_DATABASE=PYRESCOM + +OUI:00903B* + ID_OUI_FROM_DATABASE=TriEMS Research Lab, Inc. + +OUI:009074* + ID_OUI_FROM_DATABASE=ARGON NETWORKS, INC. + +OUI:0090C1* + ID_OUI_FROM_DATABASE=Peco II, Inc. + +OUI:0010D3* + ID_OUI_FROM_DATABASE=GRIPS ELECTRONIC GMBH + +OUI:0010ED* + ID_OUI_FROM_DATABASE=SUNDANCE TECHNOLOGY, INC. + +OUI:001023* + ID_OUI_FROM_DATABASE=Network Equipment Technologies + +OUI:00104E* + ID_OUI_FROM_DATABASE=CEOLOGIC + +OUI:0010FB* + ID_OUI_FROM_DATABASE=ZIDA TECHNOLOGIES LIMITED + +OUI:0010AD* + ID_OUI_FROM_DATABASE=SOFTRONICS USB, INC. + +OUI:0010D5* + ID_OUI_FROM_DATABASE=IMASDE CANARIAS, S.A. + +OUI:0010E5* + ID_OUI_FROM_DATABASE=SOLECTRON TEXAS + +OUI:00909D* + ID_OUI_FROM_DATABASE=NovaTech Process Solutions, LLC + +OUI:009038* + ID_OUI_FROM_DATABASE=FOUNTAIN TECHNOLOGIES, INC. + +OUI:0090C5* + ID_OUI_FROM_DATABASE=INTERNET MAGIC, INC. + +OUI:0090AD* + ID_OUI_FROM_DATABASE=ASPECT ELECTRONICS, INC. + +OUI:009097* + ID_OUI_FROM_DATABASE=Sycamore Networks + +OUI:009008* + ID_OUI_FROM_DATABASE=HanA Systems Inc. + +OUI:0090D4* + ID_OUI_FROM_DATABASE=BindView Development Corp. + +OUI:009089* + ID_OUI_FROM_DATABASE=SOFTCOM MICROSYSTEMS, INC. + +OUI:0090C4* + ID_OUI_FROM_DATABASE=JAVELIN SYSTEMS, INC. + +OUI:009014* + ID_OUI_FROM_DATABASE=ROTORK INSTRUMENTS, LTD. + +OUI:0090B5* + ID_OUI_FROM_DATABASE=NIKON CORPORATION + +OUI:0090C6* + ID_OUI_FROM_DATABASE=OPTIM SYSTEMS, INC. + +OUI:00909B* + ID_OUI_FROM_DATABASE=MARKEM-IMAJE + +OUI:00905B* + ID_OUI_FROM_DATABASE=RAYMOND AND LAE ENGINEERING + +OUI:0090E8* + ID_OUI_FROM_DATABASE=MOXA TECHNOLOGIES CORP., LTD. + +OUI:0090A1* + ID_OUI_FROM_DATABASE=Flying Pig Systems/High End Systems Inc. + +OUI:0090FD* + ID_OUI_FROM_DATABASE=CopperCom, Inc. + +OUI:0090AC* + ID_OUI_FROM_DATABASE=OPTIVISION, INC. + +OUI:00902A* + ID_OUI_FROM_DATABASE=COMMUNICATION DEVICES, INC. + +OUI:009098* + ID_OUI_FROM_DATABASE=SBC DESIGNS, INC. + +OUI:0090CF* + ID_OUI_FROM_DATABASE=NORTEL + +OUI:00900F* + ID_OUI_FROM_DATABASE=KAWASAKI HEAVY INDUSTRIES, LTD + +OUI:009036* + ID_OUI_FROM_DATABASE=ens, inc. + +OUI:0090E9* + ID_OUI_FROM_DATABASE=JANZ COMPUTER AG + +OUI:009032* + ID_OUI_FROM_DATABASE=PELCOMBE GROUP LTD. + +OUI:0090B8* + ID_OUI_FROM_DATABASE=ROHDE & SCHWARZ GMBH & CO. KG + +OUI:0090BE* + ID_OUI_FROM_DATABASE=IBC/INTEGRATED BUSINESS COMPUTERS + +OUI:009062* + ID_OUI_FROM_DATABASE=ICP VORTEX COMPUTERSYSTEME GmbH + +OUI:00108F* + ID_OUI_FROM_DATABASE=RAPTOR SYSTEMS + +OUI:001089* + ID_OUI_FROM_DATABASE=WebSonic + +OUI:001086* + ID_OUI_FROM_DATABASE=ATTO Technology, Inc. + +OUI:001027* + ID_OUI_FROM_DATABASE=L-3 COMMUNICATIONS EAST + +OUI:0010B8* + ID_OUI_FROM_DATABASE=ISHIGAKI COMPUTER SYSTEM CO. + +OUI:00104C* + ID_OUI_FROM_DATABASE=Teledyne LeCroy, Inc + +OUI:001001* + ID_OUI_FROM_DATABASE=Citel + +OUI:0010CF* + ID_OUI_FROM_DATABASE=FIBERLANE COMMUNICATIONS + +OUI:001068* + ID_OUI_FROM_DATABASE=COMOS TELECOM + +OUI:001067* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:0010F1* + ID_OUI_FROM_DATABASE=I-O CORPORATION + +OUI:001073* + ID_OUI_FROM_DATABASE=TECHNOBOX, INC. + +OUI:00E0C0* + ID_OUI_FROM_DATABASE=SEIWA ELECTRIC MFG. CO., LTD. + +OUI:00E046* + ID_OUI_FROM_DATABASE=BENTLY NEVADA CORP. + +OUI:00E015* + ID_OUI_FROM_DATABASE=HEIWA CORPORATION + +OUI:00E065* + ID_OUI_FROM_DATABASE=OPTICAL ACCESS INTERNATIONAL + +OUI:00E069* + ID_OUI_FROM_DATABASE=JAYCOR + +OUI:00E05C* + ID_OUI_FROM_DATABASE=Panasonic Healthcare Co., Ltd. + +OUI:00E087* + ID_OUI_FROM_DATABASE=LeCroy - Networking Productions Division + +OUI:00E049* + ID_OUI_FROM_DATABASE=MICROWI ELECTRONIC GmbH + +OUI:00E050* + ID_OUI_FROM_DATABASE=EXECUTONE INFORMATION SYSTEMS, INC. + +OUI:00E064* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRONICS + +OUI:00E012* + ID_OUI_FROM_DATABASE=PLUTO TECHNOLOGIES INTERNATIONAL INC. + +OUI:00E0D8* + ID_OUI_FROM_DATABASE=LANBit Computer, Inc. + +OUI:00E02D* + ID_OUI_FROM_DATABASE=InnoMediaLogic, Inc. + +OUI:00E0A9* + ID_OUI_FROM_DATABASE=FUNAI ELECTRIC CO., LTD. + +OUI:00E035* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:00E060* + ID_OUI_FROM_DATABASE=SHERWOOD + +OUI:00E0A2* + ID_OUI_FROM_DATABASE=MICROSLATE INC. + +OUI:00E06C* + ID_OUI_FROM_DATABASE=Ultra Electronics Limited (AEP Networks) + +OUI:00E0CE* + ID_OUI_FROM_DATABASE=ARN + +OUI:00E05F* + ID_OUI_FROM_DATABASE=e-Net, Inc. + +OUI:00E02B* + ID_OUI_FROM_DATABASE=EXTREME NETWORKS + +OUI:00E0C7* + ID_OUI_FROM_DATABASE=EUROTECH SRL + +OUI:00E0C4* + ID_OUI_FROM_DATABASE=HORNER ELECTRIC, INC. + +OUI:00E04D* + ID_OUI_FROM_DATABASE=INTERNET INITIATIVE JAPAN, INC + +OUI:00607F* + ID_OUI_FROM_DATABASE=AURORA TECHNOLOGIES, INC. + +OUI:00E039* + ID_OUI_FROM_DATABASE=PARADYNE CORP. + +OUI:006091* + ID_OUI_FROM_DATABASE=FIRST PACIFIC NETWORKS, INC. + +OUI:006002* + ID_OUI_FROM_DATABASE=SCREEN SUBTITLING SYSTEMS, LTD + +OUI:006061* + ID_OUI_FROM_DATABASE=WHISTLE COMMUNICATIONS CORP. + +OUI:0060BD* + ID_OUI_FROM_DATABASE=HUBBELL-PULSECOM + +OUI:00E0A1* + ID_OUI_FROM_DATABASE=HIMA PAUL HILDEBRANDT GmbH Co. KG + +OUI:00E028* + ID_OUI_FROM_DATABASE=APTIX CORPORATION + +OUI:00E0F2* + ID_OUI_FROM_DATABASE=ARLOTTO COMNET, INC. + +OUI:00E020* + ID_OUI_FROM_DATABASE=TECNOMEN OY + +OUI:00E0C5* + ID_OUI_FROM_DATABASE=BCOM ELECTRONICS INC. + +OUI:00E0EE* + ID_OUI_FROM_DATABASE=MAREL HF + +OUI:00E0AC* + ID_OUI_FROM_DATABASE=MIDSCO, INC. + +OUI:00E002* + ID_OUI_FROM_DATABASE=CROSSROADS SYSTEMS, INC. + +OUI:00E057* + ID_OUI_FROM_DATABASE=HAN MICROTELECOM. CO., LTD. + +OUI:00E0F0* + ID_OUI_FROM_DATABASE=ABLER TECHNOLOGY, INC. + +OUI:00E0B7* + ID_OUI_FROM_DATABASE=PI GROUP, LTD. + +OUI:0010B1* + ID_OUI_FROM_DATABASE=FOR-A CO., LTD. + +OUI:001041* + ID_OUI_FROM_DATABASE=BRISTOL BABCOCK, INC. + +OUI:0010F7* + ID_OUI_FROM_DATABASE=IRIICHI TECHNOLOGIES Inc. + +OUI:0010E6* + ID_OUI_FROM_DATABASE=APPLIED INTELLIGENT SYSTEMS, INC. + +OUI:00101E* + ID_OUI_FROM_DATABASE=MATSUSHITA ELECTRONIC INSTRUMENTS CORP. + +OUI:0010F2* + ID_OUI_FROM_DATABASE=ANTEC + +OUI:0010BE* + ID_OUI_FROM_DATABASE=MARCH NETWORKS CORPORATION + +OUI:006058* + ID_OUI_FROM_DATABASE=COPPER MOUNTAIN COMMUNICATIONS, INC. + +OUI:00601B* + ID_OUI_FROM_DATABASE=MESA ELECTRONICS + +OUI:0060FF* + ID_OUI_FROM_DATABASE=QuVis, Inc. + +OUI:006056* + ID_OUI_FROM_DATABASE=NETWORK TOOLS, INC. + +OUI:0060D8* + ID_OUI_FROM_DATABASE=ELMIC SYSTEMS, INC. + +OUI:00607A* + ID_OUI_FROM_DATABASE=DVS GMBH + +OUI:006097* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:0060E3* + ID_OUI_FROM_DATABASE=ARBIN INSTRUMENTS + +OUI:00E0FD* + ID_OUI_FROM_DATABASE=A-TREND TECHNOLOGY CO., LTD. + +OUI:00E0FB* + ID_OUI_FROM_DATABASE=LEIGHTRONIX, INC. + +OUI:00E0D3* + ID_OUI_FROM_DATABASE=DATENTECHNIK GmbH + +OUI:00E05E* + ID_OUI_FROM_DATABASE=JAPAN AVIATION ELECTRONICS INDUSTRY, LTD. + +OUI:00E0E5* + ID_OUI_FROM_DATABASE=CINCO NETWORKS, INC. + +OUI:00A0FD* + ID_OUI_FROM_DATABASE=SCITEX DIGITAL PRINTING, INC. + +OUI:00A0F5* + ID_OUI_FROM_DATABASE=RADGUARD LTD. + +OUI:00A022* + ID_OUI_FROM_DATABASE=CENTRE FOR DEVELOPMENT OF ADVANCED COMPUTING + +OUI:00A087* + ID_OUI_FROM_DATABASE=Microsemi Corporation + +OUI:00A007* + ID_OUI_FROM_DATABASE=APEXX TECHNOLOGY, INC. + +OUI:00A066* + ID_OUI_FROM_DATABASE=ISA CO., LTD. + +OUI:00A0AB* + ID_OUI_FROM_DATABASE=NETCS INFORMATIONSTECHNIK GMBH + +OUI:00A0D8* + ID_OUI_FROM_DATABASE=SPECTRA - TEK + +OUI:00A01A* + ID_OUI_FROM_DATABASE=BINAR ELEKTRONIK AB + +OUI:00A0E8* + ID_OUI_FROM_DATABASE=REUTERS HOLDINGS PLC + +OUI:00A076* + ID_OUI_FROM_DATABASE=CARDWARE LAB, INC. + +OUI:00A0A3* + ID_OUI_FROM_DATABASE=RELIABLE POWER METERS + +OUI:00A055* + ID_OUI_FROM_DATABASE=Data Device Corporation + +OUI:00A065* + ID_OUI_FROM_DATABASE=Symantec Corporation + +OUI:00A044* + ID_OUI_FROM_DATABASE=NTT IT CO., LTD. + +OUI:006008* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:0060EF* + ID_OUI_FROM_DATABASE=FLYTECH TECHNOLOGY CO., LTD. + +OUI:006098* + ID_OUI_FROM_DATABASE=HT COMMUNICATIONS + +OUI:0060F7* + ID_OUI_FROM_DATABASE=DATAFUSION SYSTEMS + +OUI:0060DE* + ID_OUI_FROM_DATABASE=Kayser-Threde GmbH + +OUI:0060D0* + ID_OUI_FROM_DATABASE=SNMP RESEARCH INCORPORATED + +OUI:006079* + ID_OUI_FROM_DATABASE=Mainstream Data, Inc. + +OUI:006020* + ID_OUI_FROM_DATABASE=PIVOTAL NETWORKING, INC. + +OUI:0005A8* + ID_OUI_FROM_DATABASE=WYLE ELECTRONICS + +OUI:0060B7* + ID_OUI_FROM_DATABASE=CHANNELMATIC, INC. + +OUI:0060A3* + ID_OUI_FROM_DATABASE=CONTINUUM TECHNOLOGY CORP. + +OUI:006050* + ID_OUI_FROM_DATABASE=INTERNIX INC. + +OUI:0060E0* + ID_OUI_FROM_DATABASE=AXIOM TECHNOLOGY CO., LTD. + +OUI:0060A8* + ID_OUI_FROM_DATABASE=TIDOMAT AB + +OUI:00A056* + ID_OUI_FROM_DATABASE=MICROPROSS + +OUI:00A051* + ID_OUI_FROM_DATABASE=ANGIA COMMUNICATIONS. INC. + +OUI:00A0A6* + ID_OUI_FROM_DATABASE=M.I. SYSTEMS, K.K. + +OUI:00A05F* + ID_OUI_FROM_DATABASE=BTG Electronics Design BV + +OUI:00A094* + ID_OUI_FROM_DATABASE=COMSAT CORPORATION + +OUI:00A010* + ID_OUI_FROM_DATABASE=SYSLOGIC DATENTECHNIK AG + +OUI:00A063* + ID_OUI_FROM_DATABASE=JRL SYSTEMS, INC. + +OUI:00A08F* + ID_OUI_FROM_DATABASE=DESKNET SYSTEMS, INC. + +OUI:00A0CC* + ID_OUI_FROM_DATABASE=LITE-ON COMMUNICATIONS, INC. + +OUI:00A090* + ID_OUI_FROM_DATABASE=TimeStep Corporation + +OUI:00A0F7* + ID_OUI_FROM_DATABASE=V.I COMPUTER CORP. + +OUI:00A09C* + ID_OUI_FROM_DATABASE=Xyplex, Inc. + +OUI:00A092* + ID_OUI_FROM_DATABASE=H. BOLLMANN MANUFACTURERS, LTD + +OUI:00A04D* + ID_OUI_FROM_DATABASE=EDA INSTRUMENTS, INC. + +OUI:00A0DB* + ID_OUI_FROM_DATABASE=FISHER & PAYKEL PRODUCTION + +OUI:00A0A5* + ID_OUI_FROM_DATABASE=TEKNOR MICROSYSTEME, INC. + +OUI:00A018* + ID_OUI_FROM_DATABASE=CREATIVE CONTROLLERS, INC. + +OUI:00A09F* + ID_OUI_FROM_DATABASE=COMMVISION CORP. + +OUI:00A06B* + ID_OUI_FROM_DATABASE=DMS DORSCH MIKROSYSTEM GMBH + +OUI:006051* + ID_OUI_FROM_DATABASE=QUALITY SEMICONDUCTOR + +OUI:00605E* + ID_OUI_FROM_DATABASE=LIBERTY TECHNOLOGY NETWORKING + +OUI:0060C6* + ID_OUI_FROM_DATABASE=DCS AG + +OUI:00609E* + ID_OUI_FROM_DATABASE=ASC X3 - INFORMATION TECHNOLOGY STANDARDS SECRETARIATS + +OUI:006084* + ID_OUI_FROM_DATABASE=DIGITAL VIDEO + +OUI:00602D* + ID_OUI_FROM_DATABASE=ALERTON TECHNOLOGIES, INC. + +OUI:006093* + ID_OUI_FROM_DATABASE=VARIAN + +OUI:0060E2* + ID_OUI_FROM_DATABASE=QUEST ENGINEERING & DEVELOPMENT + +OUI:00A039* + ID_OUI_FROM_DATABASE=ROSS TECHNOLOGY, INC. + +OUI:00A06D* + ID_OUI_FROM_DATABASE=MANNESMANN TALLY CORPORATION + +OUI:00608E* + ID_OUI_FROM_DATABASE=HE ELECTRONICS, TECHNOLOGIE & SYSTEMTECHNIK GmbH + +OUI:0060F0* + ID_OUI_FROM_DATABASE=JOHNSON & JOHNSON MEDICAL, INC + +OUI:0060D2* + ID_OUI_FROM_DATABASE=LUCENT TECHNOLOGIES TAIWAN TELECOMMUNICATIONS CO., LTD. + +OUI:006077* + ID_OUI_FROM_DATABASE=PRISA NETWORKS + +OUI:0060AB* + ID_OUI_FROM_DATABASE=LARSCOM INCORPORATED + +OUI:0060E9* + ID_OUI_FROM_DATABASE=ATOP TECHNOLOGIES, INC. + +OUI:00608B* + ID_OUI_FROM_DATABASE=ConferTech International + +OUI:0060C3* + ID_OUI_FROM_DATABASE=NETVISION CORPORATION + +OUI:00A0A0* + ID_OUI_FROM_DATABASE=COMPACT DATA, LTD. + +OUI:00A024* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:00A08B* + ID_OUI_FROM_DATABASE=ASTON ELECTRONIC DESIGNS LTD. + +OUI:00A0AA* + ID_OUI_FROM_DATABASE=SPACELABS MEDICAL + +OUI:00A04F* + ID_OUI_FROM_DATABASE=AMERITEC CORP. + +OUI:00A073* + ID_OUI_FROM_DATABASE=COM21, INC. + +OUI:00A084* + ID_OUI_FROM_DATABASE=Dataplex Pty Ltd + +OUI:00A034* + ID_OUI_FROM_DATABASE=AXEL + +OUI:00C0BC* + ID_OUI_FROM_DATABASE=TELECOM AUSTRALIA/CSSC + +OUI:00C0EF* + ID_OUI_FROM_DATABASE=ABIT CORPORATION + +OUI:00C03C* + ID_OUI_FROM_DATABASE=TOWER TECH S.R.L. + +OUI:00C061* + ID_OUI_FROM_DATABASE=SOLECTEK CORPORATION + +OUI:00C074* + ID_OUI_FROM_DATABASE=TOYODA AUTOMATIC LOOM + +OUI:00C07F* + ID_OUI_FROM_DATABASE=NUPON COMPUTING CORP. + +OUI:00C027* + ID_OUI_FROM_DATABASE=CIPHER SYSTEMS, INC. + +OUI:00C025* + ID_OUI_FROM_DATABASE=DATAPRODUCTS CORPORATION + +OUI:00C022* + ID_OUI_FROM_DATABASE=LASERMASTER TECHNOLOGIES, INC. + +OUI:00C0E6* + ID_OUI_FROM_DATABASE=Verilink Corporation + +OUI:00C05C* + ID_OUI_FROM_DATABASE=ELONEX PLC + +OUI:00C0C1* + ID_OUI_FROM_DATABASE=QUAD/GRAPHICS, INC. + +OUI:00C091* + ID_OUI_FROM_DATABASE=JABIL CIRCUIT, INC. + +OUI:00C002* + ID_OUI_FROM_DATABASE=SERCOMM CORPORATION + +OUI:00C0F5* + ID_OUI_FROM_DATABASE=METACOMP, INC. + +OUI:00C042* + ID_OUI_FROM_DATABASE=DATALUX CORP. + +OUI:00C089* + ID_OUI_FROM_DATABASE=TELINDUS DISTRIBUTION + +OUI:00C09D* + ID_OUI_FROM_DATABASE=DISTRIBUTED SYSTEMS INT'L, INC + +OUI:00C0A5* + ID_OUI_FROM_DATABASE=DICKENS DATA SYSTEMS + +OUI:00C0E3* + ID_OUI_FROM_DATABASE=OSITECH COMMUNICATIONS, INC. + +OUI:00C071* + ID_OUI_FROM_DATABASE=AREANEX COMMUNICATIONS, INC. + +OUI:00C0AF* + ID_OUI_FROM_DATABASE=TEKLOGIX INC. + +OUI:00209F* + ID_OUI_FROM_DATABASE=MERCURY COMPUTER SYSTEMS, INC. + +OUI:0020B7* + ID_OUI_FROM_DATABASE=NAMAQUA COMPUTERWARE + +OUI:00201B* + ID_OUI_FROM_DATABASE=NORTHERN TELECOM/NETWORK + +OUI:0020C0* + ID_OUI_FROM_DATABASE=PULSE ELECTRONICS, INC. + +OUI:00208D* + ID_OUI_FROM_DATABASE=CMD TECHNOLOGY + +OUI:0020DD* + ID_OUI_FROM_DATABASE=Cybertec Pty Ltd + +OUI:0020BD* + ID_OUI_FROM_DATABASE=NIOBRARA R & D CORPORATION + +OUI:0020E6* + ID_OUI_FROM_DATABASE=LIDKOPING MACHINE TOOLS AB + +OUI:002047* + ID_OUI_FROM_DATABASE=STEINBRECHER CORP. + +OUI:0020B5* + ID_OUI_FROM_DATABASE=YASKAWA ELECTRIC CORPORATION + +OUI:002072* + ID_OUI_FROM_DATABASE=WORKLINK INNOVATIONS + +OUI:0020B8* + ID_OUI_FROM_DATABASE=PRIME OPTION, INC. + +OUI:002092* + ID_OUI_FROM_DATABASE=CHESS ENGINEERING B.V. + +OUI:0020B9* + ID_OUI_FROM_DATABASE=METRICOM, INC. + +OUI:00206B* + ID_OUI_FROM_DATABASE=KONICA MINOLTA HOLDINGS, INC. + +OUI:0020FC* + ID_OUI_FROM_DATABASE=MATROX + +OUI:00C003* + ID_OUI_FROM_DATABASE=GLOBALNET COMMUNICATIONS + +OUI:00C0C3* + ID_OUI_FROM_DATABASE=ACUSON COMPUTED SONOGRAPHY + +OUI:00C04D* + ID_OUI_FROM_DATABASE=MITEC, INC. + +OUI:00C055* + ID_OUI_FROM_DATABASE=MODULAR COMPUTING TECHNOLOGIES + +OUI:00C067* + ID_OUI_FROM_DATABASE=UNITED BARCODE INDUSTRIES + +OUI:00C0B4* + ID_OUI_FROM_DATABASE=MYSON TECHNOLOGY, INC. + +OUI:00C080* + ID_OUI_FROM_DATABASE=NETSTAR, INC. + +OUI:00C015* + ID_OUI_FROM_DATABASE=NEW MEDIA CORPORATION + +OUI:0070B3* + ID_OUI_FROM_DATABASE=DATA RECALL LTD. + +OUI:00E6D3* + ID_OUI_FROM_DATABASE=NIXDORF COMPUTER CORP. + +OUI:00C083* + ID_OUI_FROM_DATABASE=TRACE MOUNTAIN PRODUCTS, INC. + +OUI:00C005* + ID_OUI_FROM_DATABASE=LIVINGSTON ENTERPRISES, INC. + +OUI:00C064* + ID_OUI_FROM_DATABASE=GENERAL DATACOMM IND. INC. + +OUI:00C0C8* + ID_OUI_FROM_DATABASE=MICRO BYTE PTY. LTD. + +OUI:00C090* + ID_OUI_FROM_DATABASE=PRAIM S.R.L. + +OUI:00C011* + ID_OUI_FROM_DATABASE=INTERACTIVE COMPUTING DEVICES + +OUI:00C0FD* + ID_OUI_FROM_DATABASE=PROSUM + +OUI:00C041* + ID_OUI_FROM_DATABASE=DIGITAL TRANSMISSION SYSTEMS + +OUI:00C00F* + ID_OUI_FROM_DATABASE=QUANTUM SOFTWARE SYSTEMS LTD. + +OUI:00C076* + ID_OUI_FROM_DATABASE=I-DATA INTERNATIONAL A-S + +OUI:00C0C6* + ID_OUI_FROM_DATABASE=PERSONAL MEDIA CORP. + +OUI:00C03B* + ID_OUI_FROM_DATABASE=MULTIACCESS COMPUTING CORP. + +OUI:0020F4* + ID_OUI_FROM_DATABASE=SPECTRIX CORPORATION + +OUI:00204E* + ID_OUI_FROM_DATABASE=NETWORK SECURITY SYSTEMS, INC. + +OUI:002027* + ID_OUI_FROM_DATABASE=MING FORTUNE INDUSTRY CO., LTD + +OUI:0020ED* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO., LTD. + +OUI:00200E* + ID_OUI_FROM_DATABASE=SATELLITE TECHNOLOGY MGMT, INC + +OUI:002096* + ID_OUI_FROM_DATABASE=Invensys + +OUI:0020BB* + ID_OUI_FROM_DATABASE=ZAX CORPORATION + +OUI:00204D* + ID_OUI_FROM_DATABASE=INOVIS GMBH + +OUI:002089* + ID_OUI_FROM_DATABASE=T3PLUS NETWORKING, INC. + +OUI:00205F* + ID_OUI_FROM_DATABASE=GAMMADATA COMPUTER GMBH + +OUI:002035* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0020E2* + ID_OUI_FROM_DATABASE=INFORMATION RESOURCE ENGINEERING + +OUI:002058* + ID_OUI_FROM_DATABASE=ALLIED SIGNAL INC. + +OUI:002081* + ID_OUI_FROM_DATABASE=TITAN ELECTRONICS + +OUI:00201D* + ID_OUI_FROM_DATABASE=KATANA PRODUCTS + +OUI:0020CF* + ID_OUI_FROM_DATABASE=TEST & MEASUREMENT SYSTEMS INC + +OUI:002043* + ID_OUI_FROM_DATABASE=NEURON COMPANY LIMITED + +OUI:002018* + ID_OUI_FROM_DATABASE=CIS TECHNOLOGY INC. + +OUI:002031* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:0020DE* + ID_OUI_FROM_DATABASE=JAPAN DIGITAL LABORAT'Y CO.LTD + +OUI:0020F7* + ID_OUI_FROM_DATABASE=CYBERDATA CORPORATION + +OUI:0020EE* + ID_OUI_FROM_DATABASE=GTECH CORPORATION + +OUI:00208C* + ID_OUI_FROM_DATABASE=GALAXY NETWORKS, INC. + +OUI:002063* + ID_OUI_FROM_DATABASE=WIPRO INFOTECH LTD. + +OUI:0020DC* + ID_OUI_FROM_DATABASE=DENSITRON TAIWAN LTD. + +OUI:002078* + ID_OUI_FROM_DATABASE=RUNTOP, INC. + +OUI:002042* + ID_OUI_FROM_DATABASE=DATAMETRICS CORP. + +OUI:0020F8* + ID_OUI_FROM_DATABASE=CARRERA COMPUTERS, INC. + +OUI:00200C* + ID_OUI_FROM_DATABASE=ADASTRA SYSTEMS CORP. + +OUI:0020C4* + ID_OUI_FROM_DATABASE=INET,INC. + +OUI:00C099* + ID_OUI_FROM_DATABASE=YOSHIKI INDUSTRIAL CO.,LTD. + +OUI:00C0FC* + ID_OUI_FROM_DATABASE=ELASTIC REALITY, INC. + +OUI:00C0D0* + ID_OUI_FROM_DATABASE=RATOC SYSTEM INC. + +OUI:00C07A* + ID_OUI_FROM_DATABASE=PRIVA B.V. + +OUI:000701* + ID_OUI_FROM_DATABASE=RACAL-DATACOM + +OUI:00C09C* + ID_OUI_FROM_DATABASE=HIOKI E.E. CORPORATION + +OUI:00C004* + ID_OUI_FROM_DATABASE=JAPAN BUSINESS COMPUTER CO.LTD + +OUI:00C062* + ID_OUI_FROM_DATABASE=IMPULSE TECHNOLOGY + +OUI:000267* + ID_OUI_FROM_DATABASE=NODE RUNNER, INC. + +OUI:002064* + ID_OUI_FROM_DATABASE=PROTEC MICROSYSTEMS, INC. + +OUI:002032* + ID_OUI_FROM_DATABASE=ALCATEL TAISEL + +OUI:00207F* + ID_OUI_FROM_DATABASE=KYOEI SANGYO CO., LTD. + +OUI:002077* + ID_OUI_FROM_DATABASE=KARDIOS SYSTEMS CORP. + +OUI:002068* + ID_OUI_FROM_DATABASE=ISDYNE + +OUI:00202A* + ID_OUI_FROM_DATABASE=N.V. DZINE + +OUI:008006* + ID_OUI_FROM_DATABASE=COMPUADD CORPORATION + +OUI:0080EF* + ID_OUI_FROM_DATABASE=RATIONAL + +OUI:0080C4* + ID_OUI_FROM_DATABASE=DOCUMENT TECHNOLOGIES, INC. + +OUI:008095* + ID_OUI_FROM_DATABASE=BASIC MERTON HANDELSGES.M.B.H. + +OUI:008053* + ID_OUI_FROM_DATABASE=INTELLICOM, INC. + +OUI:008026* + ID_OUI_FROM_DATABASE=NETWORK PRODUCTS CORPORATION + +OUI:0080FE* + ID_OUI_FROM_DATABASE=AZURE TECHNOLOGIES, INC. + +OUI:008028* + ID_OUI_FROM_DATABASE=TRADPOST (HK) LTD + +OUI:0080B6* + ID_OUI_FROM_DATABASE=THEMIS COMPUTER + +OUI:0080C0* + ID_OUI_FROM_DATABASE=PENRIL DATACOMM + +OUI:0080F5* + ID_OUI_FROM_DATABASE=Quantel Ltd + +OUI:00401D* + ID_OUI_FROM_DATABASE=INVISIBLE SOFTWARE, INC. + +OUI:0040BD* + ID_OUI_FROM_DATABASE=STARLIGHT NETWORKS, INC. + +OUI:00406D* + ID_OUI_FROM_DATABASE=LANCO, INC. + +OUI:00404D* + ID_OUI_FROM_DATABASE=TELECOMMUNICATIONS TECHNIQUES + +OUI:0040A5* + ID_OUI_FROM_DATABASE=CLINICOMP INTL. + +OUI:004059* + ID_OUI_FROM_DATABASE=YOSHIDA KOGYO K. K. + +OUI:004021* + ID_OUI_FROM_DATABASE=RASTER GRAPHICS + +OUI:004081* + ID_OUI_FROM_DATABASE=MANNESMANN SCANGRAPHIC GMBH + +OUI:00806C* + ID_OUI_FROM_DATABASE=CEGELEC PROJECTS LTD + +OUI:00404A* + ID_OUI_FROM_DATABASE=WEST AUSTRALIAN DEPARTMENT + +OUI:00400A* + ID_OUI_FROM_DATABASE=PIVOTAL TECHNOLOGIES, INC. + +OUI:004032* + ID_OUI_FROM_DATABASE=DIGITAL COMMUNICATIONS + +OUI:004042* + ID_OUI_FROM_DATABASE=N.A.T. GMBH + +OUI:0040C2* + ID_OUI_FROM_DATABASE=APPLIED COMPUTING DEVICES + +OUI:00403C* + ID_OUI_FROM_DATABASE=FORKS, INC. + +OUI:0040C4* + ID_OUI_FROM_DATABASE=KINKEI SYSTEM CORPORATION + +OUI:0040D1* + ID_OUI_FROM_DATABASE=FUKUDA DENSHI CO., LTD. + +OUI:004024* + ID_OUI_FROM_DATABASE=COMPAC INC. + +OUI:0040B6* + ID_OUI_FROM_DATABASE=COMPUTERM CORPORATION + +OUI:00403F* + ID_OUI_FROM_DATABASE=SSANGYONG COMPUTER SYSTEMS + +OUI:004003* + ID_OUI_FROM_DATABASE=Emerson Process Management Power & Water Solutions, Inc. + +OUI:004090* + ID_OUI_FROM_DATABASE=ANSEL COMMUNICATIONS + +OUI:00409A* + ID_OUI_FROM_DATABASE=NETWORK EXPRESS, INC. + +OUI:0040DE* + ID_OUI_FROM_DATABASE=Elsag Datamat spa + +OUI:004063* + ID_OUI_FROM_DATABASE=VIA TECHNOLOGIES, INC. + +OUI:00406C* + ID_OUI_FROM_DATABASE=COPERNIQUE + +OUI:0040DF* + ID_OUI_FROM_DATABASE=DIGALOG SYSTEMS, INC. + +OUI:004015* + ID_OUI_FROM_DATABASE=ASCOM INFRASYS AG + +OUI:008056* + ID_OUI_FROM_DATABASE=SPHINX Electronics GmbH & Co KG + +OUI:008060* + ID_OUI_FROM_DATABASE=NETWORK INTERFACE CORPORATION + +OUI:00805E* + ID_OUI_FROM_DATABASE=LSI LOGIC CORPORATION + +OUI:008093* + ID_OUI_FROM_DATABASE=XYRON CORPORATION + +OUI:00C05D* + ID_OUI_FROM_DATABASE=L&N TECHNOLOGIES + +OUI:00C0E4* + ID_OUI_FROM_DATABASE=SIEMENS BUILDING + +OUI:00C01B* + ID_OUI_FROM_DATABASE=SOCKET COMMUNICATIONS, INC. + +OUI:00C06E* + ID_OUI_FROM_DATABASE=HAFT TECHNOLOGY, INC. + +OUI:00406F* + ID_OUI_FROM_DATABASE=SYNC RESEARCH INC. + +OUI:00401F* + ID_OUI_FROM_DATABASE=COLORGRAPH LTD + +OUI:0040CF* + ID_OUI_FROM_DATABASE=STRAWBERRY TREE, INC. + +OUI:0040F7* + ID_OUI_FROM_DATABASE=Polaroid Corporation + +OUI:004037* + ID_OUI_FROM_DATABASE=SEA-ILAN, INC. + +OUI:0040CC* + ID_OUI_FROM_DATABASE=SILCOM MANUF'G TECHNOLOGY INC. + +OUI:004052* + ID_OUI_FROM_DATABASE=STAR TECHNOLOGIES, INC. + +OUI:00407A* + ID_OUI_FROM_DATABASE=SOCIETE D'EXPLOITATION DU CNIT + +OUI:004089* + ID_OUI_FROM_DATABASE=MEIDENSHA CORPORATION + +OUI:00405A* + ID_OUI_FROM_DATABASE=GOLDSTAR INFORMATION & COMM. + +OUI:00404C* + ID_OUI_FROM_DATABASE=HYPERTEC PTY LTD. + +OUI:00C0EE* + ID_OUI_FROM_DATABASE=KYOCERA CORPORATION + +OUI:00C0CB* + ID_OUI_FROM_DATABASE=CONTROL TECHNOLOGY CORPORATION + +OUI:00C09A* + ID_OUI_FROM_DATABASE=PHOTONICS CORPORATION + +OUI:00C01A* + ID_OUI_FROM_DATABASE=COROMETRICS MEDICAL SYSTEMS + +OUI:00404B* + ID_OUI_FROM_DATABASE=MAPLE COMPUTER SYSTEMS + +OUI:004055* + ID_OUI_FROM_DATABASE=METRONIX GMBH + +OUI:004045* + ID_OUI_FROM_DATABASE=TWINHEAD CORPORATION + +OUI:00409D* + ID_OUI_FROM_DATABASE=DIGIBOARD, INC. + +OUI:00401A* + ID_OUI_FROM_DATABASE=FUJI ELECTRIC CO., LTD. + +OUI:0040B9* + ID_OUI_FROM_DATABASE=MACQ ELECTRONIQUE SA + +OUI:0040C7* + ID_OUI_FROM_DATABASE=RUBY TECH CORPORATION + +OUI:004004* + ID_OUI_FROM_DATABASE=ICM CO. LTD. + +OUI:004070* + ID_OUI_FROM_DATABASE=INTERWARE CO., LTD. + +OUI:008057* + ID_OUI_FROM_DATABASE=ADSOFT, LTD. + +OUI:00807A* + ID_OUI_FROM_DATABASE=AITECH SYSTEMS LTD. + +OUI:0080AA* + ID_OUI_FROM_DATABASE=MAXPEED + +OUI:00C0E7* + ID_OUI_FROM_DATABASE=FIBERDATA AB + +OUI:00800A* + ID_OUI_FROM_DATABASE=JAPAN COMPUTER CORP. + +OUI:00806E* + ID_OUI_FROM_DATABASE=NIPPON STEEL CORPORATION + +OUI:008010* + ID_OUI_FROM_DATABASE=COMMODORE INTERNATIONAL + +OUI:0080DA* + ID_OUI_FROM_DATABASE=Bruel & Kjaer Sound & Vibration Measurement A/S + +OUI:0080BC* + ID_OUI_FROM_DATABASE=HITACHI ENGINEERING CO., LTD + +OUI:008000* + ID_OUI_FROM_DATABASE=MULTITECH SYSTEMS, INC. + +OUI:0080A1* + ID_OUI_FROM_DATABASE=MICROTEST, INC. + +OUI:0080D0* + ID_OUI_FROM_DATABASE=COMPUTER PERIPHERALS, INC. + +OUI:00807D* + ID_OUI_FROM_DATABASE=EQUINOX SYSTEMS INC. + +OUI:008063* + ID_OUI_FROM_DATABASE=Hirschmann Automation and Control GmbH + +OUI:00608C* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:00804E* + ID_OUI_FROM_DATABASE=APEX COMPUTER COMPANY + +OUI:00800E* + ID_OUI_FROM_DATABASE=ATLANTIX CORPORATION + +OUI:00806F* + ID_OUI_FROM_DATABASE=ONELAN LTD. + +OUI:008098* + ID_OUI_FROM_DATABASE=TDK CORPORATION + +OUI:00809C* + ID_OUI_FROM_DATABASE=LUXCOM, INC. + +OUI:008065* + ID_OUI_FROM_DATABASE=CYBERGRAPHIC SYSTEMS PTY LTD. + +OUI:008016* + ID_OUI_FROM_DATABASE=WANDEL AND GOLTERMANN + +OUI:0080E6* + ID_OUI_FROM_DATABASE=PEER NETWORKS, INC. + +OUI:0080A2* + ID_OUI_FROM_DATABASE=CREATIVE ELECTRONIC SYSTEMS + +OUI:0080E0* + ID_OUI_FROM_DATABASE=XTP SYSTEMS, INC. + +OUI:008050* + ID_OUI_FROM_DATABASE=ZIATECH CORPORATION + +OUI:0000E0* + ID_OUI_FROM_DATABASE=QUADRAM CORP. + +OUI:000057* + ID_OUI_FROM_DATABASE=SCITEX CORPORATION LTD. + +OUI:0000D6* + ID_OUI_FROM_DATABASE=PUNCH LINE HOLDING + +OUI:0000C8* + ID_OUI_FROM_DATABASE=ALTOS COMPUTER SYSTEMS + +OUI:000098* + ID_OUI_FROM_DATABASE=CROSSCOMM CORPORATION + +OUI:00007D* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:0000A2* + ID_OUI_FROM_DATABASE=Bay Networks + +OUI:000038* + ID_OUI_FROM_DATABASE=CSS LABS + +OUI:000061* + ID_OUI_FROM_DATABASE=GATEWAY COMMUNICATIONS + +OUI:000043* + ID_OUI_FROM_DATABASE=MICRO TECHNOLOGY + +OUI:0000E7* + ID_OUI_FROM_DATABASE=Star Gate Technologies + +OUI:0000F3* + ID_OUI_FROM_DATABASE=GANDALF DATA LIMITED + +OUI:000064* + ID_OUI_FROM_DATABASE=Yokogawa Electric Corporation + +OUI:00002C* + ID_OUI_FROM_DATABASE=AUTOTOTE LIMITED + +OUI:00002A* + ID_OUI_FROM_DATABASE=TRW - SEDD/INP + +OUI:0000F1* + ID_OUI_FROM_DATABASE=MAGNA COMPUTER CORPORATION + +OUI:000083* + ID_OUI_FROM_DATABASE=TADPOLE TECHNOLOGY PLC + +OUI:000020* + ID_OUI_FROM_DATABASE=DATAINDUSTRIER DIAB AB + +OUI:00007A* + ID_OUI_FROM_DATABASE=DANA COMPUTER INC. + +OUI:00007C* + ID_OUI_FROM_DATABASE=AMPERE INCORPORATED + +OUI:00008A* + ID_OUI_FROM_DATABASE=DATAHOUSE INFORMATION SYSTEMS + +OUI:000068* + ID_OUI_FROM_DATABASE=ROSEMOUNT CONTROLS + +OUI:0000A8* + ID_OUI_FROM_DATABASE=STRATUS COMPUTER INC. + +OUI:0000DF* + ID_OUI_FROM_DATABASE=BELL & HOWELL PUB SYS DIV + +OUI:000062* + ID_OUI_FROM_DATABASE=BULL HN INFORMATION SYSTEMS + +OUI:0000AD* + ID_OUI_FROM_DATABASE=BRUKER INSTRUMENTS INC. + +OUI:0000D0* + ID_OUI_FROM_DATABASE=DEVELCON ELECTRONICS LTD. + +OUI:000093* + ID_OUI_FROM_DATABASE=PROTEON INC. + +OUI:008008* + ID_OUI_FROM_DATABASE=DYNATECH COMPUTER SYSTEMS + +OUI:0080FF* + ID_OUI_FROM_DATABASE=SOC. DE TELEINFORMATIQUE RTC + +OUI:000070* + ID_OUI_FROM_DATABASE=HCL LIMITED + +OUI:00008E* + ID_OUI_FROM_DATABASE=SOLBOURNE COMPUTER, INC. + +OUI:0000DC* + ID_OUI_FROM_DATABASE=HAYES MICROCOMPUTER PRODUCTS + +OUI:000024* + ID_OUI_FROM_DATABASE=CONNECT AS + +OUI:000048* + ID_OUI_FROM_DATABASE=SEIKO EPSON CORPORATION + +OUI:008030* + ID_OUI_FROM_DATABASE=NEXUS ELECTRONICS + +OUI:008022* + ID_OUI_FROM_DATABASE=SCAN-OPTICS + +OUI:000041* + ID_OUI_FROM_DATABASE=ICE CORPORATION + +OUI:00001E* + ID_OUI_FROM_DATABASE=TELSIST INDUSTRIA ELECTRONICA + +OUI:00807B* + ID_OUI_FROM_DATABASE=ARTEL COMMUNICATIONS CORP. + +OUI:00802E* + ID_OUI_FROM_DATABASE=CASTLE ROCK COMPUTING + +OUI:0080F9* + ID_OUI_FROM_DATABASE=HEURIKON CORPORATION + +OUI:008005* + ID_OUI_FROM_DATABASE=CACTUS COMPUTER INC. + +OUI:00801D* + ID_OUI_FROM_DATABASE=INTEGRATED INFERENCE MACHINES + +OUI:008015* + ID_OUI_FROM_DATABASE=SEIKO SYSTEMS, INC. + +OUI:008034* + ID_OUI_FROM_DATABASE=SMT GOUPIL + +OUI:0080C9* + ID_OUI_FROM_DATABASE=ALBERTA MICROELECTRONIC CENTRE + +OUI:00800B* + ID_OUI_FROM_DATABASE=CSK CORPORATION + +OUI:000016* + ID_OUI_FROM_DATABASE=DU PONT PIXEL SYSTEMS . + +OUI:00005C* + ID_OUI_FROM_DATABASE=TELEMATICS INTERNATIONAL INC. + +OUI:0000AC* + ID_OUI_FROM_DATABASE=CONWARE COMPUTER CONSULTING + +OUI:0000F2* + ID_OUI_FROM_DATABASE=SPIDER COMMUNICATIONS + +OUI:000030* + ID_OUI_FROM_DATABASE=VG LABORATORY SYSTEMS LTD + +OUI:000035* + ID_OUI_FROM_DATABASE=SPECTRAGRAPHICS CORPORATION + +OUI:020701* + ID_OUI_FROM_DATABASE=RACAL-DATACOM + +OUI:080011* + ID_OUI_FROM_DATABASE=TEKTRONIX INC. + +OUI:080040* + ID_OUI_FROM_DATABASE=FERRANTI COMPUTER SYS. LIMITED + +OUI:08003B* + ID_OUI_FROM_DATABASE=TORUS SYSTEMS LIMITED + +OUI:08003D* + ID_OUI_FROM_DATABASE=CADNETIX CORPORATIONS + +OUI:080039* + ID_OUI_FROM_DATABASE=SPIDER SYSTEMS LIMITED + +OUI:080030* + ID_OUI_FROM_DATABASE=NETWORK RESEARCH CORPORATION + +OUI:080027* + ID_OUI_FROM_DATABASE=Cadmus Computer Systems + +OUI:00009B* + ID_OUI_FROM_DATABASE=INFORMATION INTERNATIONAL, INC + +OUI:00DD0F* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:000001* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:080021* + ID_OUI_FROM_DATABASE=3M COMPANY + +OUI:AA0004* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:08000C* + ID_OUI_FROM_DATABASE=MIKLYN DEVELOPMENT CO. + +OUI:00DD08* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:0000A0* + ID_OUI_FROM_DATABASE=SANYO Electric Co., Ltd. + +OUI:08007F* + ID_OUI_FROM_DATABASE=CARNEGIE-MELLON UNIVERSITY + +OUI:080082* + ID_OUI_FROM_DATABASE=VERITAS SOFTWARE + +OUI:08007B* + ID_OUI_FROM_DATABASE=SANYO ELECTRIC CO. LTD. + +OUI:00DD0C* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:000005* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:0000AA* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:00406B* + ID_OUI_FROM_DATABASE=SYSGEN + +OUI:AA0001* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:080001* + ID_OUI_FROM_DATABASE=COMPUTERVISION CORPORATION + +OUI:000053* + ID_OUI_FROM_DATABASE=COMPUCORP + +OUI:08004B* + ID_OUI_FROM_DATABASE=Planning Research Corp. + +OUI:080003* + ID_OUI_FROM_DATABASE=ADVANCED COMPUTER COMM. + +OUI:080074* + ID_OUI_FROM_DATABASE=CASIO COMPUTER CO. LTD. + +OUI:08005E* + ID_OUI_FROM_DATABASE=COUNTERPOINT COMPUTER INC. + +OUI:08005A* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:080056* + ID_OUI_FROM_DATABASE=STANFORD LINEAR ACCEL. CENTER + +OUI:080053* + ID_OUI_FROM_DATABASE=MIDDLE EAST TECH. UNIVERSITY + +OUI:08004F* + ID_OUI_FROM_DATABASE=CYGNET SYSTEMS + +OUI:F8E71E* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:00194B* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:001F95* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:000E59* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:A01B29* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:90013B* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:ECDF3A* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:E45AA2* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:00235A* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:001B38* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:E46F13* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:DC6DCD* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:94C150* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:60FE20* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:989096* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B82A72* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00D09E* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:000D72* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:000F1F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:14FEB5* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:0015C5* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:D4AE52* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B0E754* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:B8E625* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:549F35* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:64006A* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B4E10F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:0023AE* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:9CD917* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:9068C3* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:408805* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:A4A1C2* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:348446* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:AC2B6E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:F8F1B6* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:00216A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001E64* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0016EB* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0018DE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:681729* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:5C514F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B808CF* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C8F733* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4851B7* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:5CC5D4* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:7CCCB8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:F40669* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:3CA9F4* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:28B2BD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:08D40C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:843A4B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0CD292* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:78929C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:6805CA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:ACA31E* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:9C1C12* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:001A1E* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:28C2DD* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:84D47E* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:A85840* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:002243* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:74F06D* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:44D832* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:781881* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:B0EE45* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:240A64* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:D0E782* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:0C4C39* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + +OUI:002423* + ID_OUI_FROM_DATABASE=AzureWave Technologies (Shanghai) Inc. + +OUI:A81D16* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:38A53C* + ID_OUI_FROM_DATABASE=COMECER Netherlands + +OUI:001D8B* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:A4526F* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:581243* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:0026B8* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:0030F1* + ID_OUI_FROM_DATABASE=Accton Technology Corp + +OUI:001974* + ID_OUI_FROM_DATABASE=16063 + +OUI:ECF00E* + ID_OUI_FROM_DATABASE=AboCom + +OUI:3039F2* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:000827* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:9097D5* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:18FE34* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:54F6C5* + ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD + +OUI:28EF01* + ID_OUI_FROM_DATABASE=Private + +OUI:5C338E* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:001AEB* + ID_OUI_FROM_DATABASE=Allied Telesis R&D Center K.K. + +OUI:747548* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:A43111* + ID_OUI_FROM_DATABASE=ZIV + +OUI:5C93A2* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:E8C74F* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:E8F724* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:701A04* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:48D224* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:2CD05A* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:74E543* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:A4DB30* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:B8EE65* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:001DBA* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:000AD9* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:000FDE* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001EDC* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001963* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001B59* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:78843C* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:0023F1* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:3017C8* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:18002D* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:04E676* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:0022F4* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:080046* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:000D92* + ID_OUI_FROM_DATABASE=ARIMA Communications Corp. + +OUI:009096* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:0011F5* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:DCD87C* + ID_OUI_FROM_DATABASE=Beijing Jingdong Century Trading Co., LTD. + +OUI:001C4A* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:000B6A* + ID_OUI_FROM_DATABASE=Asiarock Technology Limited + +OUI:40BA61* + ID_OUI_FROM_DATABASE=ARIMA Communications Corp. + +OUI:841B5E* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:204E7F* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:A021B7* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:0024B2* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:C03F0E* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:001F33* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:1883BF* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:9C80DF* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:001CCC* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:94EBCD* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:644FB0* + ID_OUI_FROM_DATABASE=Hyunjin.com + +OUI:001A2A* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:001D19* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:88252C* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:A4E4B8* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:58671A* + ID_OUI_FROM_DATABASE=Barnes&Noble + +OUI:BC0543* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:002675* + ID_OUI_FROM_DATABASE=Aztech Electronics Pte Ltd + +OUI:001F3F* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:506A03* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:6CB0CE* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:100D7F* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:0020D6* + ID_OUI_FROM_DATABASE=Breezecom, Ltd. + +OUI:001018* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:001BE9* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:008077* + ID_OUI_FROM_DATABASE=Brother industries, LTD. + +OUI:029D8E* + ID_OUI_FROM_DATABASE=CARDIAC RECORDERS, INC. + +OUI:FC2F40* + ID_OUI_FROM_DATABASE=Calxeda, Inc. + +OUI:0026E4* + ID_OUI_FROM_DATABASE=Canal + + +OUI:389496* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0CB319* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08EE8B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:84A466* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:981DFA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FCF136* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0C8910* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:54FA3E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A89FBA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FC1910* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:083D88* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5C2E59* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:646CB2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F884F2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:14B484* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:608F5C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:4CBCA5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78595E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B0D09C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:4CA56D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A48431* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E4F8EF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1432D1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E458E7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8CBFA6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7840E4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9000DB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:183A2D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08373D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50F520* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A4EBD3* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:28987B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1867B0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F40E22* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9C3AAF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BCF2AF* + ID_OUI_FROM_DATABASE=devolo AG + +OUI:0270B3* + ID_OUI_FROM_DATABASE=DATA RECALL LTD. + +OUI:000FF6* + ID_OUI_FROM_DATABASE=DARFON LIGHTING CORP + +OUI:702559* + ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. + +OUI:0090D6* + ID_OUI_FROM_DATABASE=Crystal Group, Inc. + +OUI:001DAA* + ID_OUI_FROM_DATABASE=DrayTek Corp. + +OUI:02CF1C* + ID_OUI_FROM_DATABASE=Communication Machinery Corporation + +OUI:0C75BD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:38F0C8* + ID_OUI_FROM_DATABASE=Livestream + +OUI:0C1167* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001982* + ID_OUI_FROM_DATABASE=SmarDTV + +OUI:10C6FC* + ID_OUI_FROM_DATABASE=Garmin International + +OUI:00E000* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:00000E* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:002326* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:0007CB* + ID_OUI_FROM_DATABASE=FREEBOX SAS + +OUI:3C591E* + ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd + +OUI:002682* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:001A73* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:00904B* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:D86BF7* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:A4C0E1* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:34AF2C* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:8CCDE8* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:9CE635* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:600194* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:F44D17* + ID_OUI_FROM_DATABASE=GOLDCARD HIGH-TECH CO.,LTD. + +OUI:001E35* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001FC5* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0021BD* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:002709* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:E84ECE* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0009BF* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001AE9* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001CBE* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:002403* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002265* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0019B7* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002404* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0002EE* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001C9A* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001F01* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:000EED* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001E3A* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001A89* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0021AA* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002669* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0022FD* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002109* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002108* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001D6E* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001B33* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:ECF35B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:EC9B5B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BCC6DB* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:B83241* + ID_OUI_FROM_DATABASE=Wuhan Tianyu Information Industry Co., Ltd. + +OUI:9897D1* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + +OUI:94C960* + ID_OUI_FROM_DATABASE=Zhongshan B&T technology.co.,ltd + +OUI:001479* + ID_OUI_FROM_DATABASE=NEC Magnus Communications,Ltd. + +OUI:9C4FDA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1C5CF2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0821EF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A0CBFD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:34145F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B462AD* + ID_OUI_FROM_DATABASE=Elysia Germany GmbH + +OUI:747818* + ID_OUI_FROM_DATABASE=Jurumani Solutions + +OUI:803896* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:80D160* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:686E23* + ID_OUI_FROM_DATABASE=Wi3 Inc. + +OUI:B8A175* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:0080E5* + ID_OUI_FROM_DATABASE=NetApp + +OUI:E49A79* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:28A02B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B44BD2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002340* + ID_OUI_FROM_DATABASE=MiXTelematics + +OUI:B48B19* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00AF1F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4CCC6A* + ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD. + +OUI:985BB0* + ID_OUI_FROM_DATABASE=KMDATA INC. + +OUI:6C8FB5* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:245EBE* + ID_OUI_FROM_DATABASE=QNAP Systems, Inc. + +OUI:A89352* + ID_OUI_FROM_DATABASE=SHANGHAI ZHONGMI COMMUNICATION TECHNOLOGY CO.,LTD + +OUI:AC5F3E* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:B07FB9* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:70661B* + ID_OUI_FROM_DATABASE=Sonova AG + +OUI:1C98EC* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:9C9D5D* + ID_OUI_FROM_DATABASE=Raden Inc + +OUI:E8FD72* + ID_OUI_FROM_DATABASE=SHANGHAI LINGUO TECHNOLOGY CO., LTD. + +OUI:98BB1E* + ID_OUI_FROM_DATABASE=BYD Precision Manufacture Company Ltd. + +OUI:EC438B* + ID_OUI_FROM_DATABASE=YAPTV + +OUI:1866DA* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:981FB1* + ID_OUI_FROM_DATABASE=Shenzhen Lemon Network Technology Co.,Ltd + +OUI:CCB11A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:40476A* + ID_OUI_FROM_DATABASE=AG Acquisition Corp. d.b.a. ASTRO Gaming + +OUI:A4BF01* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:509EA7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:DCCF96* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0004C6* + ID_OUI_FROM_DATABASE=YAMAHA MOTOR CO.,LTD + +OUI:14D11F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:54511B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:68536C* + ID_OUI_FROM_DATABASE=SPnS Co.,Ltd + +OUI:64CC2E* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:005BA1* + ID_OUI_FROM_DATABASE=shanghai huayuan chuangxin software CO., LTD. + +OUI:B07E70* + ID_OUI_FROM_DATABASE=Zadara Storage Ltd. + +OUI:405EE1* + ID_OUI_FROM_DATABASE=Shenzhen H&T Intelligent Control Co.,Ltd. + +OUI:10F005* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:BC9889* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:E42F26* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:344B3D* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:FCF647* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:1088CE* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:D463FE* + ID_OUI_FROM_DATABASE=Arcadyan Corporation + +OUI:9466E7* + ID_OUI_FROM_DATABASE=WOM Engineering + +OUI:F8A188* + ID_OUI_FROM_DATABASE=LED Roadway Lighting + +OUI:001174* + ID_OUI_FROM_DATABASE=Mojo Networks, Inc. + +OUI:BC15AC* + ID_OUI_FROM_DATABASE=Vodafone Italia S.p.A. + +OUI:140C5B* + ID_OUI_FROM_DATABASE=PLNetworks + +OUI:D0B0CD* + ID_OUI_FROM_DATABASE=Moen + +OUI:0071C2* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:DCFE07* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:E47E66* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9C741A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:EC93ED* + ID_OUI_FROM_DATABASE=DDoS-Guard LTD + +OUI:4C72B9* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:F462D0* + ID_OUI_FROM_DATABASE=Not for Radio, LLC + +OUI:94513D* + ID_OUI_FROM_DATABASE=iSmart Alarm, Inc. + +OUI:C89CDC* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:002511* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:000E03* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:001BB9* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:001921* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:00142A* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:0001F4* + ID_OUI_FROM_DATABASE=Enterasys + +OUI:487ADA* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:1C7370* + ID_OUI_FROM_DATABASE=Neotech + +OUI:0050FC* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:200A5E* + ID_OUI_FROM_DATABASE=Xiangshan Giant Eagle Technology Developing Co., Ltd. + +OUI:30E37A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4CA003* + ID_OUI_FROM_DATABASE=T-21 Technologies LLC + +OUI:F0EE58* + ID_OUI_FROM_DATABASE=PACE Telematics GmbH + +OUI:A08CFD* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:4000E0* + ID_OUI_FROM_DATABASE=Derek(Shaoguan)Limited + +OUI:001397* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:00A0A4* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:A4E597* + ID_OUI_FROM_DATABASE=Gessler GmbH + +OUI:0024F4* + ID_OUI_FROM_DATABASE=Kaminario, Ltd. + +OUI:001D08* + ID_OUI_FROM_DATABASE=Jiangsu Yinhe Electronics Co.,Ltd. + +OUI:0018D7* + ID_OUI_FROM_DATABASE=JAVAD GNSS, Inc. + +OUI:001C6C* + ID_OUI_FROM_DATABASE=30805 + +OUI:00A0B0* + ID_OUI_FROM_DATABASE=I-O DATA DEVICE, INC. + +OUI:00E0CF* + ID_OUI_FROM_DATABASE=INTEGRATED DEVICE + +OUI:547F54* + ID_OUI_FROM_DATABASE=INGENICO + +OUI:48C049* + ID_OUI_FROM_DATABASE=Broad Telecom SA + +OUI:DC38E1* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:40A677* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0C8610* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:EC3EF7* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0014F6* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:00121E* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0010DB* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:307C5E* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:841888* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:40B4F0* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:002688* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0017CB* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:E0A3AC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E00EDA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6C2483* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:848319* + ID_OUI_FROM_DATABASE=Hangzhou Zero Zero Technology Co., Ltd. + +OUI:001F20* + ID_OUI_FROM_DATABASE=Logitech Europe SA + +OUI:882012* + ID_OUI_FROM_DATABASE=LMI Technologies + +OUI:002382* + ID_OUI_FROM_DATABASE=Lih Rong electronic Enterprise Co., Ltd. + +OUI:88795B* + ID_OUI_FROM_DATABASE=Konka Group Co., Ltd. + +OUI:001A34* + ID_OUI_FROM_DATABASE=Konka Group Co., Ltd. + +OUI:20A90E* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:8C99E6* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:745C9F* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:0CBD51* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:E42D02* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:3CE5A6* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:3C8C40* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:B04519* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:A81559* + ID_OUI_FROM_DATABASE=Breathometer, Inc. + +OUI:ECADB8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9801A7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2CF0A2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C09727* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:2C5A8D* + ID_OUI_FROM_DATABASE=SYSTRONIK Elektronik u. Systemtechnik GmbH + +OUI:B8BBAF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:60C5AD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8C897A* + ID_OUI_FROM_DATABASE=AUGTEK + +OUI:54EDA3* + ID_OUI_FROM_DATABASE=Navdy, Inc. + +OUI:046565* + ID_OUI_FROM_DATABASE=Testop + +OUI:042758* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3C92DC* + ID_OUI_FROM_DATABASE=Octopod Technology Co. Ltd. + +OUI:74CC39* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:6038E0* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:F0FDA0* + ID_OUI_FROM_DATABASE=Acurix Networks Pty Ltd + +OUI:1CB9C4* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:3876D1* + ID_OUI_FROM_DATABASE=Euronda SpA + +OUI:C48F07* + ID_OUI_FROM_DATABASE=Shenzhen Yihao Hulian Science and Technology Co., Ltd. + +OUI:009E1E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002550* + ID_OUI_FROM_DATABASE=Riverbed Technology, Inc. + +OUI:D85B2A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:ACC33A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F45B73* + ID_OUI_FROM_DATABASE=Wanjiaan Interconnected Technology Co., Ltd + +OUI:0021E2* + ID_OUI_FROM_DATABASE=visago Systems & Controls GmbH & Co. KG + +OUI:28F10E* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:C4A366* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0014B4* + ID_OUI_FROM_DATABASE=General Dynamics United Kingdom Ltd + +OUI:A0B437* + ID_OUI_FROM_DATABASE=GD Mission Systems + +OUI:5052D2* + ID_OUI_FROM_DATABASE=Hangzhou Telin Technologies Co., Limited + +OUI:1CD6BD* + ID_OUI_FROM_DATABASE=LEEDARSON LIGHTING CO., LTD. + +OUI:9CDD1F* + ID_OUI_FROM_DATABASE=Intelligent Steward Co.,Ltd + +OUI:00EBD5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1C7B23* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:1C740D* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:001349* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:404A03* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:CC5D4E* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:A0E4CB* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:90CF7D* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:F8F082* + ID_OUI_FROM_DATABASE=NAG LLC + +OUI:40F413* + ID_OUI_FROM_DATABASE=Rubezh + +OUI:2C094D* + ID_OUI_FROM_DATABASE=Raptor Engineering, LLC + +OUI:B0E235* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:88797E* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:40C729* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:AC040B* + ID_OUI_FROM_DATABASE=Peloton Interactive, Inc + +OUI:006074* + ID_OUI_FROM_DATABASE=QSC LLC + +OUI:34ED0B* + ID_OUI_FROM_DATABASE=Shanghai XZ-COM.CO.,Ltd. + +OUI:0010C1* + ID_OUI_FROM_DATABASE=OI ELECTRIC CO.,LTD + +OUI:4432C8* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:E0885D* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:802994* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:206A8A* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:F0DEF1* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:F80F41* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:94DF4E* + ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. + +OUI:48A9D2* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:683E34* + ID_OUI_FROM_DATABASE=MEIZU Technology Co., Ltd. + +OUI:001EC0* + ID_OUI_FROM_DATABASE=Microchip Technology Inc. + +OUI:3C0771* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:D8D43C* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:00A012* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:94611E* + ID_OUI_FROM_DATABASE=Wata Electronics Co.,Ltd. + +OUI:0025D4* + ID_OUI_FROM_DATABASE=General Dynamics Mission Systems + +OUI:5CA86A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C8778B* + ID_OUI_FROM_DATABASE=Themis Computer + +OUI:000A68* + ID_OUI_FROM_DATABASE=Solarflare Communications Inc + +OUI:0CD502* + ID_OUI_FROM_DATABASE=Westell Technologies Inc. + +OUI:001636* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:00C09F* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:54AB3A* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:089E01* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:00199D* + ID_OUI_FROM_DATABASE=Vizio, Inc + +OUI:6C0B84* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:E4509A* + ID_OUI_FROM_DATABASE=HW Communications Ltd + +OUI:702900* + ID_OUI_FROM_DATABASE=Shenzhen ChipTrip Technology Co,Ltd + +OUI:204C03* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:90F052* + ID_OUI_FROM_DATABASE=MEIZU Technology Co., Ltd. + +OUI:000E1E* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:D8EB97* + ID_OUI_FROM_DATABASE=TRENDnet, Inc. + +OUI:146102* + ID_OUI_FROM_DATABASE=Alpine Electronics, Inc. + +OUI:9003B7* + ID_OUI_FROM_DATABASE=PARROT SA + +OUI:0CFE45* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:F8D0AC* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:00D9D1* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:00041F* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:001D0D* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:7CC709* + ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD. + +OUI:D455BE* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:8CA6DF* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:00E091* + ID_OUI_FROM_DATABASE=LG Electronics + +OUI:6CD032* + ID_OUI_FROM_DATABASE=LG Electronics + +OUI:C041F6* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:404AD4* + ID_OUI_FROM_DATABASE=Widex A/S + +OUI:0021FB* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:8C3AE3* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:30766F* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:F80CF3* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:0022CF* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:A84E3F* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:00A742* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6CA858* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:001478* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:00167A* + ID_OUI_FROM_DATABASE=Skyworth Overseas Development Ltd. + +OUI:28BE03* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:F4C613* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:D826B9* + ID_OUI_FROM_DATABASE=Guangdong Coagent Electronics S&T Co.,Ltd. + +OUI:FCB0C4* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:24AF4A* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:001AF0* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:AC9CE4* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:D84710* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:000E40* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001158* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0011F9* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000F6A* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001283* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000438* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002347* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:002561* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:008058* + ID_OUI_FROM_DATABASE=PRINTER SYSTEMS CORP. + +OUI:00140D* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001765* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0018B0* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001B25* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001DAF* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00166D* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:0016F2* + ID_OUI_FROM_DATABASE=Dmobile System Co., Ltd. + +OUI:000138* + ID_OUI_FROM_DATABASE=XAVi Technologies Corp. + +OUI:3C9157* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:0000D8* + ID_OUI_FROM_DATABASE=Novell, Inc. + +OUI:001087* + ID_OUI_FROM_DATABASE=XSTREAMIS PLC + +OUI:7C0623* + ID_OUI_FROM_DATABASE=Ultra Electronics Sonar System Division + +OUI:002555* + ID_OUI_FROM_DATABASE=Visonic Technologies 1993 Ltd. + +OUI:009058* + ID_OUI_FROM_DATABASE=Ultra Electronics Limited (AEP Networks) + +OUI:48FD8E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:244427* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B4A984* + ID_OUI_FROM_DATABASE=Symantec Corporation + +OUI:34074F* + ID_OUI_FROM_DATABASE=AccelStor, Inc. + +OUI:248A07* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:00258B* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:3C2DB7* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0023D4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001831* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D08CB5* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B4EED4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:CC8CE3* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:102EAF* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:647BD4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E8* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E6* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B0B448* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:505663* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:3C7DB1* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:40984E* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0012D1* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:88C255* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:E0C79D* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:9059AF* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B4994C* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:70FF76* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:507224* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:440444* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:506583* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:BC282C* + ID_OUI_FROM_DATABASE=e-Smart Systems Pvt. Ltd + +OUI:546C0E* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:F85C4D* + ID_OUI_FROM_DATABASE=NOKIA + +OUI:D013FD* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:D8E72B* + ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + +OUI:04FEA1* + ID_OUI_FROM_DATABASE=Fihonest communication co.,Ltd + +OUI:2CAC44* + ID_OUI_FROM_DATABASE=CONEXTOP + +OUI:A8BD27* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:981E0F* + ID_OUI_FROM_DATABASE=Jeelan (Shanghai Jeelan Technology Information Inc + +OUI:548CA0* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:707E43* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:1C1448* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A47AA4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001AAD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E83EFC* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E8892C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:3CDFA9* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:8C09F4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:083E0C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D404CD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:203D66* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:6455B1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:C005C2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001225* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00128A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0003E0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015D1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E46449* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:745612* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:74EAE8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A811FC* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:044E5A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:94E8C5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:F8A097* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:BC644B* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:347A60* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0023ED* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002395* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0022B4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002136* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0024C1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:3C754A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:40FC89* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002493* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00195E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001404* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001BDD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0023A2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001E8D* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00230B* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001B52* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:84E058* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:003676* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001CA8* + ID_OUI_FROM_DATABASE=AirTies Wireless Networks + +OUI:001EE2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001C43* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001D25* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3C5A37* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:549B12* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3C8BFE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00265D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D4E8B2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0017D5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001247* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78521A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E4121D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:684898* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F409D8* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:B479A7* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:18D276* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:1C66AA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:58C38B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0808C2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B0C4E7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D890E8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:34AA8B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002339* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D487D8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:184617* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5001BB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:380A94* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D857EF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:24C696* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:181EB0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:20D390* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:343111* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:34BE00* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50CCF8* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:980C82* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:002119* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:7825AD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F4D9FB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0017C9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00166B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00166C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E47CF9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002454* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5C0A5B* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:90187C* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:FC1F19* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:20D5BF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:30CDA7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00749C* + ID_OUI_FROM_DATABASE=RUIJIE NETWORKS CO., LTD. + +OUI:300ED5* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:D02788* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0014A4* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0016CE* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001DD9* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001FE2* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:48E244* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:30F772* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:90489A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:543530* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C03896* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:2C337A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:ACD1B8* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:9439E5* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:506313* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:78E400* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:40490F* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:28565A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001F3A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:002269* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:5C8613* + ID_OUI_FROM_DATABASE=Beijing Zhoenet Technology Co., Ltd + +OUI:8C7CB5* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:EC55F9* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C8B21E* + ID_OUI_FROM_DATABASE=CHIPSEA TECHNOLOGIES (SHENZHEN) CORP. + +OUI:B072BF* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:600B03* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:503F98* + ID_OUI_FROM_DATABASE=CMITECH + +OUI:C09F05* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:AC63BE* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:38521A* + ID_OUI_FROM_DATABASE=Nokia + +OUI:A41437* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:884CCF* + ID_OUI_FROM_DATABASE=Pulzze Systems, Inc + +OUI:84DBFC* + ID_OUI_FROM_DATABASE=Nokia + +OUI:143E60* + ID_OUI_FROM_DATABASE=Nokia + +OUI:D4E33F* + ID_OUI_FROM_DATABASE=Nokia + +OUI:00233A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C87E75* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5454CF* + ID_OUI_FROM_DATABASE=PROBEDIGITAL CO.,LTD + +OUI:F0D5BF* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:748A69* + ID_OUI_FROM_DATABASE=Korea Image Technology Co., Ltd + +OUI:1C9D3E* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:30B64F* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:DC0D30* + ID_OUI_FROM_DATABASE=Shenzhen Feasycom Technology Co., Ltd. + +OUI:008731* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B4EFFA* + ID_OUI_FROM_DATABASE=Lemobile Information Technology (Beijing) Co., Ltd. + +OUI:0005EE* + ID_OUI_FROM_DATABASE=Vanderbilt International (SWE) AB + +OUI:9495A0* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:CCFD17* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:4CF95D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:8421F1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:707990* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:38D547* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:248894* + ID_OUI_FROM_DATABASE=shenzhen lensun Communication Technology LTD + +OUI:60A4D0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3C8BCD* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:E43ED7* + ID_OUI_FROM_DATABASE=Arcadyan Corporation + +OUI:38A4ED* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:00B0CE* + ID_OUI_FROM_DATABASE=Viveris Technologies + +OUI:E00DB9* + ID_OUI_FROM_DATABASE=Cree, Inc. + +OUI:40FE0D* + ID_OUI_FROM_DATABASE=MAXIO + +OUI:609AC1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F07960* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C8BA0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9840BB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:CC2D83* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:4C3275* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E04FBD* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:ACE77B* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:00B0E1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0006F4* + ID_OUI_FROM_DATABASE=Prime Electronics & Satellitics Inc. + +OUI:24A43C* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:28EE52* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D4E90B* + ID_OUI_FROM_DATABASE=CVT CO.,LTD + +OUI:788A20* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:905C44* + ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc. + +OUI:FC372B* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:0CD86C* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:8C60E7* + ID_OUI_FROM_DATABASE=MPGIO CO.,LTD + +OUI:2C0E3D* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:24C44A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B83A9D* + ID_OUI_FROM_DATABASE=Alarm.com + +OUI:00BBC1* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:2CC260* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:1C14B3* + ID_OUI_FROM_DATABASE=Airwire Technologies + +OUI:407183* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0059DC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:14612F* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:D86CE9* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:3C81D8* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:2CE412* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:181E78* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:0037B7* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:0014BF* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:6C8DC1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:38CADA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C579B* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:B436A9* + ID_OUI_FROM_DATABASE=Fibocom Wireless Inc. + +OUI:6416F0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:48DB50* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2400BA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:68DBCA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:044BED* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3CBB73* + ID_OUI_FROM_DATABASE=Shenzhen Xinguodu Technology Co., Ltd. + +OUI:3CCF5B* + ID_OUI_FROM_DATABASE=ICOMM HK LIMITED + +OUI:F40304* + ID_OUI_FROM_DATABASE=Google, Inc. + +OUI:78ACC0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3C9066* + ID_OUI_FROM_DATABASE=SmartRG, Inc. + +OUI:00195B* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:000D88* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:001346* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:205532* + ID_OUI_FROM_DATABASE=Gotech International Technology Limited + +OUI:002401* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:1CAFF7* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:B8A386* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:C8D3A3* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:4419B6* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:C056E3* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:C8E7D8* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:E01C41* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:D854A2* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:9CEFD5* + ID_OUI_FROM_DATABASE=Panda Wireless, Inc. + +OUI:C02C7A* + ID_OUI_FROM_DATABASE=Shenzhen Horn Audio Co.,Ltd. + +OUI:88B8D0* + ID_OUI_FROM_DATABASE=Dongguan Koppo Electronic Co.,Ltd + +OUI:38E7D8* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:D8B377* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:B4CEF6* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:D40B1A* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:A08D16* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:601888* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8002DF* + ID_OUI_FROM_DATABASE=ORA Inc. + +OUI:D8FC38* + ID_OUI_FROM_DATABASE=Giantec Semiconductor Inc + +OUI:2C6798* + ID_OUI_FROM_DATABASE=InTalTech Ltd. + +OUI:D0BF9C* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:B05ADA* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001083* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0001E6* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:C44044* + ID_OUI_FROM_DATABASE=RackTop Systems Inc. + +OUI:3898D8* + ID_OUI_FROM_DATABASE=MERITECH CO.,LTD + +OUI:C8675E* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:000CF1* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:000E0C* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:BC0F64* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:6CA100* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:94659C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:1002B5* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A468BC* + ID_OUI_FROM_DATABASE=Private + +OUI:441EA1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D8D385* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:18A905* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:00237D* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:002655* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:001560* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:288023* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:645106* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:5CB901* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:DC4A3E* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C59E5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:9CB654* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:38EAA7* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:E83935* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:08EB74* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:6CB56B* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:940937* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:403DEC* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:E84DD0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D81FCC* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:140467* + ID_OUI_FROM_DATABASE=SNK Technologies Co.,Ltd. + +OUI:EC5F23* + ID_OUI_FROM_DATABASE=Qinghai Kimascend Electronics Technology Co. Ltd. + +OUI:047D50* + ID_OUI_FROM_DATABASE=Shenzhen Kang Ying Technology Co.Ltd. + +OUI:54EFFE* + ID_OUI_FROM_DATABASE=Fullpower Technologies, Inc. + +OUI:EC52DC* + ID_OUI_FROM_DATABASE=WORLD MEDIA AND TECHNOLOGY Corp. + +OUI:A4D18C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CC25EF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:240995* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:247F3C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:1C8E5C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:94772B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F4E3FB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:04021F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0034FE* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D02DB3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:086361* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F80113* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:70723C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:5C7D5E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4C8BEF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:20F3A3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:ACE87B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:688F84* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:ACF7F3* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:889471* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:CC4E24* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:50EB1A* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:0027F8* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:000533* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:0060DF* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:4CAC0A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:0026ED* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:002293* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:FCD733* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:10A5D0* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:D4C9B2* + ID_OUI_FROM_DATABASE=Quanergy Systems Inc + +OUI:E4CE02* + ID_OUI_FROM_DATABASE=WyreStorm Technologies Ltd + +OUI:2002AF* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:0026E8* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:ECCB30* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:786A89* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2008ED* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:509F27* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:CC96A0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:54A51B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F4C714* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:286ED4* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A01290* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:F81547* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:506184* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:BCADAB* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:B4A95A* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:3C3A73* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:04F938* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:FC48EF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:80FB06* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D4B110* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:CC53B5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:002127* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:54E6FC* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D85D4C* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F81A67* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F0F336* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:44B32D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F07816* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001310* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:0023BE* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:54D46F* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:24374C* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:BCC810* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:484487* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:445829* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:481D70* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:00214F* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:00E036* + ID_OUI_FROM_DATABASE=PIONEER CORPORATION + +OUI:E0AE5E* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:34C731* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:60380E* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:64D4BD* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:00000C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:004096* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:30F70D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B07D47* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:D8B190* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F0B2E5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:188B9D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:38ED18* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ECBD1D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:DCCEC1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:84B261* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:009EC8* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:7C1DD9* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:A086C6* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:584498* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:70E422* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0050BD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:009086* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:005054* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:3C0E23* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:90E6BA* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:BCAEC5* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:10BF48* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:A80C0D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B83861* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6C9989* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:580A20* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0050D1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00500B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:005073* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00603E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E034* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001868* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:887556* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:60735C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:FC9947* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7CC537* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70CD60* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24AB81* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:581FAA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A46706* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3C0754* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E4CE8F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E8040B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B8C75D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:403CFC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:286AB8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7CC3A1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00E16D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F8C288* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E0ACF1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:FC5B39* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:346F90* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E0D173* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74A02F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:547C69* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:689CE2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:40A6E8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B8782E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:000502* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0010FA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:000393* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0016CB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0017F2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001B63* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:001EC2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002608* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7C6D62* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:40D32D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D83062* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C42C03* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:6C2056* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BC1665* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:44ADD9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0C2724* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:6C416A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F872EA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0C6803* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:789F70* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC3714* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:40331A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:94F6A3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D81D72* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70ECE4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:38C986* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:FCFC48* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2857BE* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:50D59C* + ID_OUI_FROM_DATABASE=Thai Habel Industrial Co., Ltd. + +OUI:FCA386* + ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD + +OUI:F0F249* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:A4C361* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:AC7F3E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:280B5C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:90B931* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24A2E1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80EA96* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:600308* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04F13E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:54724F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:48746E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3CAB8E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7C6DF8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:48D705* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3CD0F8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:98D6BB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4CB199* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:64E682* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:804971* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:98FE94* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D8004D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:98B8E3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80929F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:885395* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C04EB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:78FD94* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C88550* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D4F46F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:787E61* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60F81D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4C7C5F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:48E9F1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:FCE998* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F099BF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68644B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A8968A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:4C8D79* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:207D74* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F4F15A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:042665* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2CB43A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:689C70* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:087045* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CCE0C3* + ID_OUI_FROM_DATABASE=Mangstor, Inc. + +OUI:84A423* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:346987* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:58685D* + ID_OUI_FROM_DATABASE=Tempo Australia Pty Ltd + +OUI:789C85* + ID_OUI_FROM_DATABASE=August Home, Inc. + +OUI:FCCF43* + ID_OUI_FROM_DATABASE=HUIZHOU CITY HUIYANG DISTRICT MEISIQI INDUSTRY DEVELOPMENT CO,.LTD + +OUI:5882A8* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:B4EF04* + ID_OUI_FROM_DATABASE=DAIHAN Scientific Co., Ltd. + +OUI:049645* + ID_OUI_FROM_DATABASE=WUXI SKY CHIP INTERCONNECTION TECHNOLOGY CO.,LTD. + +OUI:5CE3B6* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:9C88AD* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:C8C2C6* + ID_OUI_FROM_DATABASE=Shanghai Airm2m Communication Technology Co., Ltd + +OUI:EC64E7* + ID_OUI_FROM_DATABASE=MOCACARE Corporation + +OUI:D07C2D* + ID_OUI_FROM_DATABASE=Leie IOT technology Co., Ltd + +OUI:40862E* + ID_OUI_FROM_DATABASE=JDM MOBILE INTERNET SOLUTION CO., LTD. + +OUI:EC388F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:BC9C31* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:90C99B* + ID_OUI_FROM_DATABASE=Recore Systems + +OUI:5CB559* + ID_OUI_FROM_DATABASE=CNEX Labs + +OUI:5CCF7F* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:380546* + ID_OUI_FROM_DATABASE=Foctek Photonics, Inc. + +OUI:6858C5* + ID_OUI_FROM_DATABASE=ZF TRW Automotive + +OUI:044169* + ID_OUI_FROM_DATABASE=GoPro + +OUI:ACC51B* + ID_OUI_FROM_DATABASE=Zhuhai Pantum Electronics Co., Ltd. + +OUI:4473D6* + ID_OUI_FROM_DATABASE=Logitech + +OUI:E80734* + ID_OUI_FROM_DATABASE=Champion Optical Network Engineering, LLC + +OUI:6CEBB2* + ID_OUI_FROM_DATABASE=Dongguan Sen DongLv Electronics Co.,Ltd + +OUI:A03299* + ID_OUI_FROM_DATABASE=Lenovo (Beijing) Co., Ltd. + +OUI:A845CD* + ID_OUI_FROM_DATABASE=Siselectron Technology LTD. + +OUI:D0C193* + ID_OUI_FROM_DATABASE=SKYBELL, INC + +OUI:209BCD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0B0E7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CC20E8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E435C8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:38FF36* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:D47208* + ID_OUI_FROM_DATABASE=Bragi GmbH + +OUI:489A42* + ID_OUI_FROM_DATABASE=Technomate Ltd + +OUI:B49D0B* + ID_OUI_FROM_DATABASE=BQ + +OUI:98CB27* + ID_OUI_FROM_DATABASE=Galore Networks Pvt. Ltd. + +OUI:30D32D* + ID_OUI_FROM_DATABASE=devolo AG + +OUI:CC794A* + ID_OUI_FROM_DATABASE=BLU Products Inc. + +OUI:60FD56* + ID_OUI_FROM_DATABASE=WOORISYSTEMS CO., Ltd + +OUI:483974* + ID_OUI_FROM_DATABASE=Proware Technologies Co., Ltd. + +OUI:E855B4* + ID_OUI_FROM_DATABASE=SAI Technology Inc. + +OUI:9CA69D* + ID_OUI_FROM_DATABASE=Whaley Technology Co.Ltd + +OUI:342606* + ID_OUI_FROM_DATABASE=CarePredict, Inc. + +OUI:B4AE2B* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:80EB77* + ID_OUI_FROM_DATABASE=Wistron Corporation + +OUI:B88981* + ID_OUI_FROM_DATABASE=Chengdu InnoThings Technology Co., Ltd. + +OUI:B4293D* + ID_OUI_FROM_DATABASE=Shenzhen Urovo Technology Co.,Ltd. + +OUI:906FA9* + ID_OUI_FROM_DATABASE=NANJING PUTIAN TELECOMMUNICATIONS TECHNOLOGY CO.,LTD. + +OUI:14B370* + ID_OUI_FROM_DATABASE=Gigaset Digital Technology (Shenzhen) Co., Ltd. + +OUI:FC2FEF* + ID_OUI_FROM_DATABASE=UTT Technologies Co., Ltd. + +OUI:EC21E5* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:44FDA3* + ID_OUI_FROM_DATABASE=Everysight LTD. + +OUI:84D4C8* + ID_OUI_FROM_DATABASE=Widex A/S + +OUI:247260* + ID_OUI_FROM_DATABASE=IOTTECH Corp + +OUI:44975A* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:584822* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:F8BF09* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B4B265* + ID_OUI_FROM_DATABASE=DAEHO I&T + +OUI:081FEB* + ID_OUI_FROM_DATABASE=BinCube + +OUI:785F4C* + ID_OUI_FROM_DATABASE=Argox Information Co., Ltd. + +OUI:5870C6* + ID_OUI_FROM_DATABASE=Shanghai Xiaoyi Technology Co., Ltd. + +OUI:803B2A* + ID_OUI_FROM_DATABASE=ABB Xiamen Low Voltage Equipment Co.,Ltd. + +OUI:A0A65C* + ID_OUI_FROM_DATABASE=Supercomputing Systems AG + +OUI:5CB395* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C412F5* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:44F436* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:349B5B* + ID_OUI_FROM_DATABASE=Maquet GmbH + +OUI:E861BE* + ID_OUI_FROM_DATABASE=Melec Inc. + +OUI:54B80A* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:D8ADDD* + ID_OUI_FROM_DATABASE=Sonavation, Inc. + +OUI:C09A71* + ID_OUI_FROM_DATABASE=XIAMEN MEITU MOBILE TECHNOLOGY CO.LTD + +OUI:340B40* + ID_OUI_FROM_DATABASE=MIOS ELETTRONICA SRL + +OUI:944A0C* + ID_OUI_FROM_DATABASE=Sercomm Corporation + +OUI:D02516* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:D05C7A* + ID_OUI_FROM_DATABASE=Sartura d.o.o. + +OUI:9C37F4* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:5CEB68* + ID_OUI_FROM_DATABASE=Cheerstar Technology Co., Ltd + +OUI:F46A92* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:14AEDB* + ID_OUI_FROM_DATABASE=VTech Telecommunications Ltd. + +OUI:EC4F82* + ID_OUI_FROM_DATABASE=Calix Inc. + +OUI:B8C3BF* + ID_OUI_FROM_DATABASE=Henan Chengshi NetWork Technology Co.,Ltd + +OUI:C0EE40* + ID_OUI_FROM_DATABASE=Laird Technologies + +OUI:F0182B* + ID_OUI_FROM_DATABASE=LG Chem + +OUI:CC5FBF* + ID_OUI_FROM_DATABASE=Topwise 3G Communication Co., Ltd. + +OUI:14DDA9* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:485D36* + ID_OUI_FROM_DATABASE=Verizon + +OUI:EC60E0* + ID_OUI_FROM_DATABASE=AVI-ON LABS + +OUI:145A83* + ID_OUI_FROM_DATABASE=Logi-D inc + +OUI:4CEEB0* + ID_OUI_FROM_DATABASE=SHC Netzwerktechnik GmbH + +OUI:188EF9* + ID_OUI_FROM_DATABASE=G2C Co. Ltd. + +OUI:809FAB* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:D00492* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:F4E9D4* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:1422DB* + ID_OUI_FROM_DATABASE=eero inc. + +OUI:0C413E* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:007E56* + ID_OUI_FROM_DATABASE=China Dragon Technology Limited + +OUI:086266* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:346C0F* + ID_OUI_FROM_DATABASE=Pramod Telecom Pvt. Ltd + +OUI:3C912B* + ID_OUI_FROM_DATABASE=Vexata Inc + +OUI:54369B* + ID_OUI_FROM_DATABASE=1Verge Internet Technology (Beijing) Co., Ltd. + +OUI:E4FED9* + ID_OUI_FROM_DATABASE=EDMI Europe Ltd + +OUI:2852E0* + ID_OUI_FROM_DATABASE=Layon international Electronic & Telecom Co.,Ltd + +OUI:E48501* + ID_OUI_FROM_DATABASE=Geberit International AG + +OUI:1C3947* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:2CAD13* + ID_OUI_FROM_DATABASE=SHENZHEN ZHILU TECHNOLOGY CO.,LTD + +OUI:68B983* + ID_OUI_FROM_DATABASE=b-plus GmbH + +OUI:BC74D7* + ID_OUI_FROM_DATABASE=HangZhou JuRu Technology CO.,LTD + +OUI:E88E60* + ID_OUI_FROM_DATABASE=NSD Corporation + +OUI:545146* + ID_OUI_FROM_DATABASE=AMG Systems Ltd. + +OUI:84DDB7* + ID_OUI_FROM_DATABASE=Cilag GmbH International + +OUI:78EB14* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:D05BA8* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8CE78C* + ID_OUI_FROM_DATABASE=DK Networks + +OUI:E4BAD9* + ID_OUI_FROM_DATABASE=360 Fly Inc. + +OUI:7C3CB6* + ID_OUI_FROM_DATABASE=Shenzhen Homecare Technology Co.,Ltd. + +OUI:BCE767* + ID_OUI_FROM_DATABASE=Quanzhou TDX Electronics Co., Ltd + +OUI:6CA7FA* + ID_OUI_FROM_DATABASE=YOUNGBO ENGINEERING INC. + +OUI:D0929E* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:F4032F* + ID_OUI_FROM_DATABASE=Reduxio Systems + +OUI:84CFBF* + ID_OUI_FROM_DATABASE=Fairphone + +OUI:AC9E17* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:ACC73F* + ID_OUI_FROM_DATABASE=VITSMO CO., LTD. + +OUI:18BDAD* + ID_OUI_FROM_DATABASE=L-TECH CORPORATION + +OUI:44D244* + ID_OUI_FROM_DATABASE=Seiko Epson Corporation + +OUI:10C07C* + ID_OUI_FROM_DATABASE=Blu-ray Disc Association + +OUI:B87879* + ID_OUI_FROM_DATABASE=Roche Diagnostics GmbH + +OUI:4480EB* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:D06F4A* + ID_OUI_FROM_DATABASE=TOPWELL INTERNATIONAL HOLDINGS LIMITED + +OUI:BC54F9* + ID_OUI_FROM_DATABASE=Drogoo Technology Co., Ltd. + +OUI:349E34* + ID_OUI_FROM_DATABASE=Evervictory Electronic Co.Ltd + +OUI:A0C2DE* + ID_OUI_FROM_DATABASE=Costar Video Systems + +OUI:3809A4* + ID_OUI_FROM_DATABASE=Firefly Integrations + +OUI:00A509* + ID_OUI_FROM_DATABASE=WigWag Inc. + +OUI:A86405* + ID_OUI_FROM_DATABASE=nimbus 9, Inc + +OUI:7076FF* + ID_OUI_FROM_DATABASE=KERLINK + +OUI:68F0BC* + ID_OUI_FROM_DATABASE=Shenzhen LiWiFi Technology Co., Ltd + +OUI:BCD165* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:4CA928* + ID_OUI_FROM_DATABASE=Insensi + +OUI:2884FA* + ID_OUI_FROM_DATABASE=SHARP Corporation + +OUI:3C1E04* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:E0FFF7* + ID_OUI_FROM_DATABASE=Softiron Inc. + +OUI:DC60A1* + ID_OUI_FROM_DATABASE=Teledyne DALSA Professional Imaging + +OUI:78E980* + ID_OUI_FROM_DATABASE=RainUs Co.,Ltd + +OUI:7C8274* + ID_OUI_FROM_DATABASE=Shenzhen Hikeen Technology CO.,LTD + +OUI:B40566* + ID_OUI_FROM_DATABASE=SP Best Corporation Co., LTD. + +OUI:70AD54* + ID_OUI_FROM_DATABASE=Malvern Instruments Ltd + +OUI:DCE026* + ID_OUI_FROM_DATABASE=Patrol Tag, Inc + +OUI:EC3C88* + ID_OUI_FROM_DATABASE=MCNEX Co.,Ltd. + +OUI:F07959* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:E08E3C* + ID_OUI_FROM_DATABASE=Aztech Electronics Pte Ltd + +OUI:78A351* + ID_OUI_FROM_DATABASE=SHENZHEN ZHIBOTONG ELECTRONICS CO.,LTD + +OUI:94E2FD* + ID_OUI_FROM_DATABASE=Boge Kompressoren OTTO Boge GmbH & Co. KG + +OUI:E4695A* + ID_OUI_FROM_DATABASE=Dictum Health, Inc. + +OUI:D46132* + ID_OUI_FROM_DATABASE=Pro Concept Manufacturer Co.,Ltd. + +OUI:54A050* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:841826* + ID_OUI_FROM_DATABASE=Osram GmbH + +OUI:14F893* + ID_OUI_FROM_DATABASE=Wuhan FiberHome Digital Technology Co.,Ltd. + +OUI:9816EC* + ID_OUI_FROM_DATABASE=IC Intracom + +OUI:DCDA4F* + ID_OUI_FROM_DATABASE=GETCK TECHNOLOGY, INC + +OUI:30FAB7* + ID_OUI_FROM_DATABASE=Tunai Creative + +OUI:0809B6* + ID_OUI_FROM_DATABASE=Masimo Corp + +OUI:14EDE4* + ID_OUI_FROM_DATABASE=Kaiam Corporation + +OUI:3438AF* + ID_OUI_FROM_DATABASE=Inlab Software GmbH + +OUI:049B9C* + ID_OUI_FROM_DATABASE=Eadingcore Intelligent Technology Co., Ltd. + +OUI:842690* + ID_OUI_FROM_DATABASE=BEIJING THOUGHT SCIENCE CO.,LTD. + +OUI:B84FD5* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:587BE9* + ID_OUI_FROM_DATABASE=AirPro Technology India Pvt. Ltd + +OUI:FC1D84* + ID_OUI_FROM_DATABASE=Autobase + +OUI:4CE933* + ID_OUI_FROM_DATABASE=RailComm, LLC + +OUI:6050C1* + ID_OUI_FROM_DATABASE=Kinetek Sports + +OUI:003560* + ID_OUI_FROM_DATABASE=Rosen Aviation + +OUI:EC59E7* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:08EFAB* + ID_OUI_FROM_DATABASE=SAYME WIRELESS SENSOR NETWORK + +OUI:C81B6B* + ID_OUI_FROM_DATABASE=Innova Security + +OUI:5C966A* + ID_OUI_FROM_DATABASE=RTNET + +OUI:2C5089* + ID_OUI_FROM_DATABASE=Shenzhen Kaixuan Visual Technology Co.,Limited + +OUI:EC13B2* + ID_OUI_FROM_DATABASE=Netonix + +OUI:74BADB* + ID_OUI_FROM_DATABASE=Longconn Electornics(shenzhen)Co.,Ltd + +OUI:4C7403* + ID_OUI_FROM_DATABASE=BQ + +OUI:5876C5* + ID_OUI_FROM_DATABASE=DIGI I'S LTD + +OUI:00A2F5* + ID_OUI_FROM_DATABASE=Guangzhou Yuanyun Network Technology Co.,Ltd + +OUI:70FC8C* + ID_OUI_FROM_DATABASE=OneAccess SA + +OUI:902CC7* + ID_OUI_FROM_DATABASE=C-MAX Asia Limited + +OUI:1C965A* + ID_OUI_FROM_DATABASE=Weifang goertek Electronics CO.,LTD + +OUI:188219* + ID_OUI_FROM_DATABASE=Alibaba Cloud Computing Ltd. + +OUI:B41780* + ID_OUI_FROM_DATABASE=DTI Group Ltd + +OUI:D437D7* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:AC3870* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:80EACA* + ID_OUI_FROM_DATABASE=Dialog Semiconductor Hellas SA + +OUI:4CBC42* + ID_OUI_FROM_DATABASE=Shenzhen Hangsheng Electronics Co.,Ltd. + +OUI:987E46* + ID_OUI_FROM_DATABASE=Emizon Networks Limited + +OUI:8432EA* + ID_OUI_FROM_DATABASE=ANHUI WANZTEN P&T CO., LTD + +OUI:90B686* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:4C6E6E* + ID_OUI_FROM_DATABASE=Comnect Technology CO.,LTD + +OUI:F4DD9E* + ID_OUI_FROM_DATABASE=GoPro + +OUI:40B3CD* + ID_OUI_FROM_DATABASE=Chiyoda Electronics Co.,Ltd. + +OUI:3451AA* + ID_OUI_FROM_DATABASE=JID GLOBAL + +OUI:04572F* + ID_OUI_FROM_DATABASE=Sertel Electronics UK Ltd + +OUI:08B2A3* + ID_OUI_FROM_DATABASE=Cynny Italia S.r.L. + +OUI:D8977C* + ID_OUI_FROM_DATABASE=Grey Innovation + +OUI:80AD67* + ID_OUI_FROM_DATABASE=Kasda Networks Inc + +OUI:30595B* + ID_OUI_FROM_DATABASE=streamnow AG + +OUI:B8AD3E* + ID_OUI_FROM_DATABASE=BLUECOM + +OUI:10C37B* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:48D855* + ID_OUI_FROM_DATABASE=Telvent + +OUI:284ED7* + ID_OUI_FROM_DATABASE=OutSmart Power Systems, Inc. + +OUI:5C5BC2* + ID_OUI_FROM_DATABASE=YIK Corporation + +OUI:EC8A4C* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8014A8* + ID_OUI_FROM_DATABASE=Guangzhou V-SOLUTION Electronic Technology Co., Ltd. + +OUI:908C63* + ID_OUI_FROM_DATABASE=GZ Weedong Networks Technology Co. , Ltd + +OUI:B49EAC* + ID_OUI_FROM_DATABASE=Imagik Int'l Corp + +OUI:C8E42F* + ID_OUI_FROM_DATABASE=Technical Research Design and Development + +OUI:FC2325* + ID_OUI_FROM_DATABASE=EosTek (Shenzhen) Co., Ltd. + +OUI:A81374* + ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company + +OUI:4C83DE* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:5CB6CC* + ID_OUI_FROM_DATABASE=NovaComm Technologies Inc. + +OUI:B4AE6F* + ID_OUI_FROM_DATABASE=Circle Reliance, Inc DBA Cranberry Networks + +OUI:B89919* + ID_OUI_FROM_DATABASE=7signal Solutions, Inc + +OUI:90DA6A* + ID_OUI_FROM_DATABASE=FOCUS H&S Co., Ltd. + +OUI:A45DA1* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:A43D78* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:E8EF89* + ID_OUI_FROM_DATABASE=OPMEX Tech. + +OUI:F4C447* + ID_OUI_FROM_DATABASE=Coagent International Enterprise Limited + +OUI:08DF1F* + ID_OUI_FROM_DATABASE=Bose Corporation + +OUI:542AA2* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:84948C* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:CCA0E5* + ID_OUI_FROM_DATABASE=DZG Metering GmbH + +OUI:3059B7* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:0874F6* + ID_OUI_FROM_DATABASE=Winterhalter Gastronom GmbH + +OUI:FCC2DE* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:1C1CFD* + ID_OUI_FROM_DATABASE=Dalian Hi-Think Computer Technology, Corp + +OUI:7062B8* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:B875C0* + ID_OUI_FROM_DATABASE=PayPal, Inc. + +OUI:E47FB2* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:38262B* + ID_OUI_FROM_DATABASE=UTran Technology + +OUI:20ED74* + ID_OUI_FROM_DATABASE=Ability enterprise co.,Ltd. + +OUI:7824AF* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:0CAC05* + ID_OUI_FROM_DATABASE=Unitend Technologies Inc. + +OUI:B4B859* + ID_OUI_FROM_DATABASE=Texa Spa + +OUI:045C8E* + ID_OUI_FROM_DATABASE=gosund GROUP CO.,LTD + +OUI:54B753* + ID_OUI_FROM_DATABASE=Hunan Fenghui Yinjia Science And Technology Co.,Ltd + +OUI:4826E8* + ID_OUI_FROM_DATABASE=Tek-Air Systems, Inc. + +OUI:A012DB* + ID_OUI_FROM_DATABASE=TABUCHI ELECTRIC CO.,LTD + +OUI:ACB859* + ID_OUI_FROM_DATABASE=Uniband Electronic Corp, + +OUI:100F18* + ID_OUI_FROM_DATABASE=Fu Gang Electronic(KunShan)CO.,LTD + +OUI:C8D590* + ID_OUI_FROM_DATABASE=FLIGHT DATA SYSTEMS + +OUI:709383* + ID_OUI_FROM_DATABASE=Intelligent Optical Network High Tech CO.,LTD. + +OUI:6047D4* + ID_OUI_FROM_DATABASE=FORICS Electronic Technology Co., Ltd. + +OUI:C09D26* + ID_OUI_FROM_DATABASE=Topicon HK Lmd. + +OUI:B061C7* + ID_OUI_FROM_DATABASE=Ericsson-LG Enterprise + +OUI:B05706* + ID_OUI_FROM_DATABASE=Vallox Oy + +OUI:C8D429* + ID_OUI_FROM_DATABASE=Muehlbauer AG + +OUI:20EAC7* + ID_OUI_FROM_DATABASE=SHENZHEN RIOPINE ELECTRONICS CO., LTD + +OUI:80618F* + ID_OUI_FROM_DATABASE=Shenzhen sangfei consumer communications co.,ltd + +OUI:5CF50D* + ID_OUI_FROM_DATABASE=Institute of microelectronic applications + +OUI:10DEE4* + ID_OUI_FROM_DATABASE=automationNEXT GmbH + +OUI:444891* + ID_OUI_FROM_DATABASE=HDMI Licensing, LLC + +OUI:FC923B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:38F708* + ID_OUI_FROM_DATABASE=National Resource Management, Inc. + +OUI:C4C919* + ID_OUI_FROM_DATABASE=Energy Imports Ltd + +OUI:88A73C* + ID_OUI_FROM_DATABASE=Ragentek Technology Group + +OUI:B0D7C5* + ID_OUI_FROM_DATABASE=Logipix Ltd + +OUI:38C9A9* + ID_OUI_FROM_DATABASE=SMART High Reliability Solutions, Inc. + +OUI:BC1A67* + ID_OUI_FROM_DATABASE=YF Technology Co., Ltd + +OUI:B024F3* + ID_OUI_FROM_DATABASE=Progeny Systems + +OUI:8C4DB9* + ID_OUI_FROM_DATABASE=Unmonday Ltd + +OUI:D87CDD* + ID_OUI_FROM_DATABASE=SANIX INCORPORATED + +OUI:F8A2B4* + ID_OUI_FROM_DATABASE=RHEWA-WAAGENFABRIK August Freudewald GmbH &Co. KG + +OUI:84FE9E* + ID_OUI_FROM_DATABASE=RTC Industries, Inc. + +OUI:403067* + ID_OUI_FROM_DATABASE=Conlog (Pty) Ltd + +OUI:98DA92* + ID_OUI_FROM_DATABASE=Vuzix Corporation + +OUI:5C2AEF* + ID_OUI_FROM_DATABASE=Open Access Pty Ltd + +OUI:E40439* + ID_OUI_FROM_DATABASE=TomTom Software Ltd + +OUI:90AE1B* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:441E91* + ID_OUI_FROM_DATABASE=ARVIDA Intelligent Electronics Technology Co.,Ltd. + +OUI:6C14F7* + ID_OUI_FROM_DATABASE=Erhardt+Leimer GmbH + +OUI:CC07E4* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:B4430D* + ID_OUI_FROM_DATABASE=Broadlink Pty Ltd + +OUI:A4BBAF* + ID_OUI_FROM_DATABASE=Lime Instruments + +OUI:7CE1FF* + ID_OUI_FROM_DATABASE=Computer Performance, Inc. DBA Digital Loggers, Inc. + +OUI:D069D0* + ID_OUI_FROM_DATABASE=Verto Medical Solutions, LLC + +OUI:ACE069* + ID_OUI_FROM_DATABASE=ISAAC Instruments + +OUI:E8EA6A* + ID_OUI_FROM_DATABASE=StarTech.com + +OUI:C4E984* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:8059FD* + ID_OUI_FROM_DATABASE=Noviga + +OUI:18FF2E* + ID_OUI_FROM_DATABASE=Shenzhen Rui Ying Da Technology Co., Ltd + +OUI:1CAB01* + ID_OUI_FROM_DATABASE=Innovolt + +OUI:68856A* + ID_OUI_FROM_DATABASE=OuterLink Corporation + +OUI:30F42F* + ID_OUI_FROM_DATABASE=ESP + +OUI:746A8F* + ID_OUI_FROM_DATABASE=VS Vision Systems GmbH + +OUI:B068B6* + ID_OUI_FROM_DATABASE=Hangzhou OYE Technology Co. Ltd + +OUI:9C65F9* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:487604* + ID_OUI_FROM_DATABASE=Private + +OUI:D057A1* + ID_OUI_FROM_DATABASE=Werma Signaltechnik GmbH & Co. KG + +OUI:3C89A6* + ID_OUI_FROM_DATABASE=KAPELSE + +OUI:90F1B0* + ID_OUI_FROM_DATABASE=Hangzhou Anheng Info&Tech CO.,LTD + +OUI:9C86DA* + ID_OUI_FROM_DATABASE=Phoenix Geophysics Ltd. + +OUI:48FEEA* + ID_OUI_FROM_DATABASE=HOMA B.V. + +OUI:10DDF4* + ID_OUI_FROM_DATABASE=Maxway Electronics CO.,LTD + +OUI:080371* + ID_OUI_FROM_DATABASE=KRG CORPORATE + +OUI:ACC595* + ID_OUI_FROM_DATABASE=Graphite Systems + +OUI:3413A8* + ID_OUI_FROM_DATABASE=Mediplan Limited + +OUI:4CD9C4* + ID_OUI_FROM_DATABASE=Magneti Marelli Automotive Electronics (Guangzhou) Co. Ltd + +OUI:743ECB* + ID_OUI_FROM_DATABASE=Gentrice tech + +OUI:7071B3* + ID_OUI_FROM_DATABASE=Brain Corporation + +OUI:208986* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:3CD4D6* + ID_OUI_FROM_DATABASE=WirelessWERX, Inc + +OUI:64E625* + ID_OUI_FROM_DATABASE=Woxu Wireless Co., Ltd + +OUI:7C444C* + ID_OUI_FROM_DATABASE=Entertainment Solutions, S.L. + +OUI:501AC5* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:609620* + ID_OUI_FROM_DATABASE=Private + +OUI:F8572E* + ID_OUI_FROM_DATABASE=Core Brands, LLC + +OUI:E0E631* + ID_OUI_FROM_DATABASE=SNB TECHNOLOGIES LIMITED + +OUI:20C60D* + ID_OUI_FROM_DATABASE=Shanghai annijie Information technology Co.,LTD + +OUI:7C9763* + ID_OUI_FROM_DATABASE=Openmatics s.r.o. + +OUI:0444A1* + ID_OUI_FROM_DATABASE=TELECON GALICIA,S.A. + +OUI:84569C* + ID_OUI_FROM_DATABASE=Coho Data, Inc., + +OUI:78AE0C* + ID_OUI_FROM_DATABASE=Far South Networks + +OUI:38CA97* + ID_OUI_FROM_DATABASE=Contour Design LLC + +OUI:84A783* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:2C5D93* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:1CC11A* + ID_OUI_FROM_DATABASE=Wavetronix + +OUI:4CF02E* + ID_OUI_FROM_DATABASE=Vifa Denmark A/S + +OUI:3051F8* + ID_OUI_FROM_DATABASE=BYK-Gardner GmbH + +OUI:94C3E4* + ID_OUI_FROM_DATABASE=SCA Schucker Gmbh & Co KG + +OUI:FC19D0* + ID_OUI_FROM_DATABASE=Cloud Vision Networks Technology Co.,Ltd. + +OUI:20E791* + ID_OUI_FROM_DATABASE=Siemens Healthcare Diagnostics, Inc + +OUI:68764F* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:D4D919* + ID_OUI_FROM_DATABASE=GoPro + +OUI:50C9A0* + ID_OUI_FROM_DATABASE=SKIPPER Electronics AS + +OUI:A49F89* + ID_OUI_FROM_DATABASE=Shanghai Rui Rui Communication Technology Co.Ltd. + +OUI:D850E6* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:94103E* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:B4750E* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:346178* + ID_OUI_FROM_DATABASE=The Boeing Company + +OUI:187ED5* + ID_OUI_FROM_DATABASE=shenzhen kaism technology Co. Ltd + +OUI:841B38* + ID_OUI_FROM_DATABASE=Shenzhen Excelsecu Data Technology Co.,Ltd + +OUI:EC2AF0* + ID_OUI_FROM_DATABASE=Ypsomed AG + +OUI:044F8B* + ID_OUI_FROM_DATABASE=Adapteva, Inc. + +OUI:9CE7BD* + ID_OUI_FROM_DATABASE=Winduskorea co., Ltd + +OUI:A0BF50* + ID_OUI_FROM_DATABASE=S.C. ADD-PRODUCTION S.R.L. + +OUI:7CB733* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:705957* + ID_OUI_FROM_DATABASE=Medallion Instrumentation Systems + +OUI:6C8366* + ID_OUI_FROM_DATABASE=Nanjing SAC Power Grid Automation Co., Ltd. + +OUI:88576D* + ID_OUI_FROM_DATABASE=XTA Electronics Ltd + +OUI:F83D4E* + ID_OUI_FROM_DATABASE=Softlink Automation System Co., Ltd + +OUI:FCD817* + ID_OUI_FROM_DATABASE=Beijing Hesun Technologies Co.Ltd. + +OUI:909F43* + ID_OUI_FROM_DATABASE=Accutron Instruments Inc. + +OUI:50C006* + ID_OUI_FROM_DATABASE=Carmanah Signs + +OUI:98FB12* + ID_OUI_FROM_DATABASE=Grand Electronics (HK) Ltd + +OUI:3C1040* + ID_OUI_FROM_DATABASE=daesung network + +OUI:B04545* + ID_OUI_FROM_DATABASE=YACOUB Automation GmbH + +OUI:701D7F* + ID_OUI_FROM_DATABASE=Comtech Technology Co., Ltd. + +OUI:60DB2A* + ID_OUI_FROM_DATABASE=HNS + +OUI:7CBF88* + ID_OUI_FROM_DATABASE=Mobilicom LTD + +OUI:90028A* + ID_OUI_FROM_DATABASE=Shenzhen Shidean Legrand Electronic Products Co.,Ltd + +OUI:90356E* + ID_OUI_FROM_DATABASE=Vodafone Omnitel N.V. + +OUI:3CCA87* + ID_OUI_FROM_DATABASE=Iders Incorporated + +OUI:08CA45* + ID_OUI_FROM_DATABASE=Toyou Feiji Electronics Co., Ltd. + +OUI:9CA9E4* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:E47723* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:C098E5* + ID_OUI_FROM_DATABASE=University of Michigan + +OUI:B8DF6B* + ID_OUI_FROM_DATABASE=SpotCam Co., Ltd. + +OUI:742B62* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:58BDF9* + ID_OUI_FROM_DATABASE=Sigrand + +OUI:344F3F* + ID_OUI_FROM_DATABASE=IO-Power Technology Co., Ltd. + +OUI:C0C687* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:142BD2* + ID_OUI_FROM_DATABASE=Armtel Ltd. + +OUI:54A54B* + ID_OUI_FROM_DATABASE=NSC Communications Siberia Ltd + +OUI:BC2B6B* + ID_OUI_FROM_DATABASE=Beijing Haier IC Design Co.,Ltd + +OUI:642184* + ID_OUI_FROM_DATABASE=Nippon Denki Kagaku Co.,LTD + +OUI:EC3E09* + ID_OUI_FROM_DATABASE=PERFORMANCE DESIGNED PRODUCTS, LLC + +OUI:EC219F* + ID_OUI_FROM_DATABASE=VidaBox LLC + +OUI:98D331* + ID_OUI_FROM_DATABASE=Shenzhen Bolutek Technology Co.,Ltd. + +OUI:3C1A57* + ID_OUI_FROM_DATABASE=Cardiopulmonary Corp + +OUI:6CF97C* + ID_OUI_FROM_DATABASE=Nanoptix Inc. + +OUI:58E02C* + ID_OUI_FROM_DATABASE=Micro Technic A/S + +OUI:E481B3* + ID_OUI_FROM_DATABASE=Shenzhen ACT Industrial Co.,Ltd. + +OUI:E4F3E3* + ID_OUI_FROM_DATABASE=Shanghai iComhome Co.,Ltd. + +OUI:04CF25* + ID_OUI_FROM_DATABASE=MANYCOLORS, INC. + +OUI:D41090* + ID_OUI_FROM_DATABASE=iNFORM Systems AG + +OUI:3495DB* + ID_OUI_FROM_DATABASE=Logitec Corporation + +OUI:88142B* + ID_OUI_FROM_DATABASE=Protonic Holland + +OUI:B8241A* + ID_OUI_FROM_DATABASE=SWEDA INFORMATICA LTDA + +OUI:3806B4* + ID_OUI_FROM_DATABASE=A.D.C. GmbH + +OUI:341B22* + ID_OUI_FROM_DATABASE=Grandbeing Technology Co., Ltd + +OUI:B4346C* + ID_OUI_FROM_DATABASE=MATSUNICHI DIGITAL TECHNOLOGY (HONG KONG) LIMITED + +OUI:9C1465* + ID_OUI_FROM_DATABASE=Edata Elektronik San. ve Tic. A.Ş. + +OUI:587A4D* + ID_OUI_FROM_DATABASE=Stonesoft Corporation + +OUI:E89218* + ID_OUI_FROM_DATABASE=Arcontia International AB + +OUI:58F387* + ID_OUI_FROM_DATABASE=HCCP + +OUI:B0793C* + ID_OUI_FROM_DATABASE=Revolv Inc + +OUI:20CEC4* + ID_OUI_FROM_DATABASE=Peraso Technologies + +OUI:04848A* + ID_OUI_FROM_DATABASE=7INOVA TECHNOLOGY LIMITED + +OUI:20C6EB* + ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company + +OUI:700FEC* + ID_OUI_FROM_DATABASE=Poindus Systems Corp. + +OUI:78D5B5* + ID_OUI_FROM_DATABASE=NAVIELEKTRO KY + +OUI:E067B3* + ID_OUI_FROM_DATABASE=C-Data Technology Co., Ltd + +OUI:B887A8* + ID_OUI_FROM_DATABASE=Step Ahead Innovations Inc. + +OUI:140D4F* + ID_OUI_FROM_DATABASE=Flextronics International + +OUI:B847C6* + ID_OUI_FROM_DATABASE=SanJet Technology Corp. + +OUI:4CDF3D* + ID_OUI_FROM_DATABASE=TEAM ENGINEERS ADVANCE TECHNOLOGIES INDIA PVT LTD + +OUI:70F176* + ID_OUI_FROM_DATABASE=Data Modul AG + +OUI:205721* + ID_OUI_FROM_DATABASE=Salix Technology CO., Ltd. + +OUI:704CED* + ID_OUI_FROM_DATABASE=TMRG, Inc. + +OUI:E8516E* + ID_OUI_FROM_DATABASE=TSMART Inc. + +OUI:7C1AFC* + ID_OUI_FROM_DATABASE=Dalian Co-Edifice Video Technology Co., Ltd + +OUI:C034B4* + ID_OUI_FROM_DATABASE=Gigastone Corporation + +OUI:74ADB7* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + +OUI:DC6F00* + ID_OUI_FROM_DATABASE=Livescribe, Inc. + +OUI:D0737F* + ID_OUI_FROM_DATABASE=Mini-Circuits + +OUI:A4D094* + ID_OUI_FROM_DATABASE=Erwin Peters Systemtechnik GmbH + +OUI:0488E2* + ID_OUI_FROM_DATABASE=Beats Electronics LLC + +OUI:D00EA4* + ID_OUI_FROM_DATABASE=Porsche Cars North America + +OUI:F415FD* + ID_OUI_FROM_DATABASE=Shanghai Pateo Electronic Equipment Manufacturing Co., Ltd. + +OUI:2C9464* + ID_OUI_FROM_DATABASE=Cincoze Co., Ltd. + +OUI:B050BC* + ID_OUI_FROM_DATABASE=SHENZHEN BASICOM ELECTRONIC CO.,LTD. + +OUI:DC7014* + ID_OUI_FROM_DATABASE=Private + +OUI:40BC73* + ID_OUI_FROM_DATABASE=Cronoplast S.L. + +OUI:78303B* + ID_OUI_FROM_DATABASE=Stephen Technologies Co.,Limited + +OUI:78F5E5* + ID_OUI_FROM_DATABASE=BEGA Gantenbrink-Leuchten KG + +OUI:804B20* + ID_OUI_FROM_DATABASE=Ventilation Control + +OUI:4007C0* + ID_OUI_FROM_DATABASE=Railtec Systems GmbH + +OUI:94B8C5* + ID_OUI_FROM_DATABASE=RuggedCom Inc. + +OUI:8C3C07* + ID_OUI_FROM_DATABASE=Skiva Technologies, Inc. + +OUI:784B08* + ID_OUI_FROM_DATABASE=f.robotics acquisitions ltd + +OUI:0C2D89* + ID_OUI_FROM_DATABASE=QiiQ Communications Inc. + +OUI:604A1C* + ID_OUI_FROM_DATABASE=SUYIN Corporation + +OUI:A4D3B5* + ID_OUI_FROM_DATABASE=GLITEL Stropkov, s.r.o. + +OUI:A4F3C1* + ID_OUI_FROM_DATABASE=Open Source Robotics Foundation, Inc. + +OUI:6C8B2F* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B863BC* + ID_OUI_FROM_DATABASE=ROBOTIS, Co, Ltd + +OUI:C8DDC9* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:CC1AFA* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:8C5AF0* + ID_OUI_FROM_DATABASE=Exeltech Solar Products + +OUI:F8DADF* + ID_OUI_FROM_DATABASE=EcoTech, Inc. + +OUI:30AE7B* + ID_OUI_FROM_DATABASE=Deqing Dusun Electron CO., LTD + +OUI:1441E2* + ID_OUI_FROM_DATABASE=Monaco Enterprises, Inc. + +OUI:F07765* + ID_OUI_FROM_DATABASE=Sourcefire, Inc + +OUI:E4F7A1* + ID_OUI_FROM_DATABASE=Datafox GmbH + +OUI:601E02* + ID_OUI_FROM_DATABASE=EltexAlatau + +OUI:E47D5A* + ID_OUI_FROM_DATABASE=Beijing Hanbang Technology Corp. + +OUI:4C6255* + ID_OUI_FROM_DATABASE=SANMINA-SCI SYSTEM DE MEXICO S.A. DE C.V. + +OUI:381766* + ID_OUI_FROM_DATABASE=PROMZAKAZ LTD. + +OUI:204C6D* + ID_OUI_FROM_DATABASE=Hugo Brennenstuhl Gmbh & Co. KG. + +OUI:DC825B* + ID_OUI_FROM_DATABASE=JANUS, spol. s r.o. + +OUI:B08807* + ID_OUI_FROM_DATABASE=Strata Worldwide + +OUI:74D02B* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:A4E0E6* + ID_OUI_FROM_DATABASE=FILIZOLA S.A. PESAGEM E AUTOMACAO + +OUI:60E00E* + ID_OUI_FROM_DATABASE=SHINSEI ELECTRONICS CO LTD + +OUI:30D46A* + ID_OUI_FROM_DATABASE=Autosales Incorporated + +OUI:30AABD* + ID_OUI_FROM_DATABASE=Shanghai Reallytek Information Technology Co.,Ltd + +OUI:A4B818* + ID_OUI_FROM_DATABASE=PENTA Gesellschaft für elektronische Industriedatenverarbeitung mbH + +OUI:106682* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:102831* + ID_OUI_FROM_DATABASE=Morion Inc. + +OUI:D81EDE* + ID_OUI_FROM_DATABASE=B&W Group Ltd + +OUI:6897E8* + ID_OUI_FROM_DATABASE=Society of Motion Picture & Television Engineers + +OUI:24EA40* + ID_OUI_FROM_DATABASE=Systeme Helmholz GmbH + +OUI:FC58FA* + ID_OUI_FROM_DATABASE=Shen Zhen Shi Xin Zhong Xin Technology Co.,Ltd. + +OUI:60601F* + ID_OUI_FROM_DATABASE=SZ DJI TECHNOLOGY CO.,LTD + +OUI:E0C6B3* + ID_OUI_FROM_DATABASE=MilDef AB + +OUI:FCDB96* + ID_OUI_FROM_DATABASE=ENERVALLEY CO., LTD + +OUI:FC8B97* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:882E5A* + ID_OUI_FROM_DATABASE=storONE + +OUI:D429EA* + ID_OUI_FROM_DATABASE=Zimory GmbH + +OUI:C80E95* + ID_OUI_FROM_DATABASE=OmniLync Inc. + +OUI:50ABBF* + ID_OUI_FROM_DATABASE=Hoseo Telecom + +OUI:C8EEA6* + ID_OUI_FROM_DATABASE=Shenzhen SHX Technology Co., Ltd + +OUI:28CBEB* + ID_OUI_FROM_DATABASE=One + +OUI:18E8DD* + ID_OUI_FROM_DATABASE=MODULETEK + +OUI:4CCC34* + ID_OUI_FROM_DATABASE=Motorola Solutions Inc. + +OUI:F084C9* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:E894F6* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:94ACCA* + ID_OUI_FROM_DATABASE=trivum technologies GmbH + +OUI:7CD844* + ID_OUI_FROM_DATABASE=Enmotus Inc + +OUI:F4C6D7* + ID_OUI_FROM_DATABASE=blackned GmbH + +OUI:68A40E* + ID_OUI_FROM_DATABASE=BSH Bosch and Siemens Home Appliances GmbH + +OUI:4CCA53* + ID_OUI_FROM_DATABASE=Skyera, Inc. + +OUI:081DFB* + ID_OUI_FROM_DATABASE=Shanghai Mexon Communication Technology Co.,Ltd + +OUI:D0CDE1* + ID_OUI_FROM_DATABASE=Scientech Electronics + +OUI:94756E* + ID_OUI_FROM_DATABASE=QinetiQ North America + +OUI:543D37* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:0C5521* + ID_OUI_FROM_DATABASE=Axiros GmbH + +OUI:A4D856* + ID_OUI_FROM_DATABASE=Gimbal, Inc + +OUI:10A743* + ID_OUI_FROM_DATABASE=SK Mtek Limited + +OUI:E4A7FD* + ID_OUI_FROM_DATABASE=Cellco Partnership + +OUI:24F2DD* + ID_OUI_FROM_DATABASE=Radiant Zemax LLC + +OUI:80CF41* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:7C9A9B* + ID_OUI_FROM_DATABASE=VSE valencia smart energy + +OUI:A845E9* + ID_OUI_FROM_DATABASE=Firich Enterprises CO., LTD. + +OUI:78995C* + ID_OUI_FROM_DATABASE=Nationz Technologies Inc + +OUI:8CC5E1* + ID_OUI_FROM_DATABASE=ShenZhen Konka Telecommunication Technology Co.,Ltd + +OUI:6CB311* + ID_OUI_FROM_DATABASE=Shenzhen Lianrui Electronics Co.,Ltd + +OUI:54115F* + ID_OUI_FROM_DATABASE=Atamo Pty Ltd + +OUI:2411D0* + ID_OUI_FROM_DATABASE=Chongqing Ehs Science and Technology Development Co.,Ltd. + +OUI:6C9AC9* + ID_OUI_FROM_DATABASE=Valentine Research, Inc. + +OUI:10F49A* + ID_OUI_FROM_DATABASE=T3 Innovation + +OUI:5865E6* + ID_OUI_FROM_DATABASE=INFOMARK CO., LTD. + +OUI:60BD91* + ID_OUI_FROM_DATABASE=Move Innovation + +OUI:98473C* + ID_OUI_FROM_DATABASE=SHANGHAI SUNMON COMMUNICATION TECHNOGY CO.,LTD + +OUI:CC4BFB* + ID_OUI_FROM_DATABASE=Hellberg Safety AB + +OUI:ACA22C* + ID_OUI_FROM_DATABASE=Baycity Technologies Ltd + +OUI:6CADEF* + ID_OUI_FROM_DATABASE=KZ Broadband Technologies, Ltd. + +OUI:044BFF* + ID_OUI_FROM_DATABASE=GuangZhou Hedy Digital Technology Co., Ltd + +OUI:949BFD* + ID_OUI_FROM_DATABASE=Trans New Technology, Inc. + +OUI:E4EEFD* + ID_OUI_FROM_DATABASE=MR&D Manufacturing + +OUI:105CBF* + ID_OUI_FROM_DATABASE=DuroByte Inc + +OUI:88A3CC* + ID_OUI_FROM_DATABASE=Amatis Controls + +OUI:EC89F5* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:083AB8* + ID_OUI_FROM_DATABASE=Shinoda Plasma Co., Ltd. + +OUI:A0DD97* + ID_OUI_FROM_DATABASE=PolarLink Technologies, Ltd + +OUI:E05597* + ID_OUI_FROM_DATABASE=Emergent Vision Technologies Inc. + +OUI:A01917* + ID_OUI_FROM_DATABASE=Bertel S.p.a. + +OUI:FC9FAE* + ID_OUI_FROM_DATABASE=Fidus Systems Inc + +OUI:FC0647* + ID_OUI_FROM_DATABASE=Cortland Research, LLC + +OUI:20918A* + ID_OUI_FROM_DATABASE=PROFALUX + +OUI:7C438F* + ID_OUI_FROM_DATABASE=E-Band Communications Corp. + +OUI:FC626E* + ID_OUI_FROM_DATABASE=Beijing MDC Telecom + +OUI:C0B339* + ID_OUI_FROM_DATABASE=Comigo Ltd. + +OUI:DCC0DB* + ID_OUI_FROM_DATABASE=Shenzhen Kaiboer Technology Co., Ltd. + +OUI:7076DD* + ID_OUI_FROM_DATABASE=Oxyguard International A/S + +OUI:E89AFF* + ID_OUI_FROM_DATABASE=Fujian Landi Commercial Equipment Co.,Ltd + +OUI:683B1E* + ID_OUI_FROM_DATABASE=Countwise LTD + +OUI:D4136F* + ID_OUI_FROM_DATABASE=Asia Pacific Brands + +OUI:A0A130* + ID_OUI_FROM_DATABASE=DLI Taiwan Branch office + +OUI:ECE915* + ID_OUI_FROM_DATABASE=STI Ltd + +OUI:A81FAF* + ID_OUI_FROM_DATABASE=KRYPTON POLSKA + +OUI:087BAA* + ID_OUI_FROM_DATABASE=SVYAZKOMPLEKTSERVICE, LLC + +OUI:2C26C5* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:BC629F* + ID_OUI_FROM_DATABASE=Telenet Systems P. Ltd. + +OUI:B47F5E* + ID_OUI_FROM_DATABASE=Foresight Manufacture (S) Pte Ltd + +OUI:785517* + ID_OUI_FROM_DATABASE=SankyuElectronics + +OUI:848E96* + ID_OUI_FROM_DATABASE=Embertec Pty Ltd + +OUI:CC3A61* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:A00363* + ID_OUI_FROM_DATABASE=Robert Bosch Healthcare GmbH + +OUI:F0F644* + ID_OUI_FROM_DATABASE=Whitesky Science & Technology Co.,Ltd. + +OUI:30D357* + ID_OUI_FROM_DATABASE=Logosol, Inc. + +OUI:2C441B* + ID_OUI_FROM_DATABASE=Spectrum Medical Limited + +OUI:1C5A6B* + ID_OUI_FROM_DATABASE=Philips Electronics Nederland BV + +OUI:A875D6* + ID_OUI_FROM_DATABASE=FreeTek International Co., Ltd. + +OUI:58EB14* + ID_OUI_FROM_DATABASE=Proteus Digital Health + +OUI:789F87* + ID_OUI_FROM_DATABASE=Siemens AG I IA PP PRM + +OUI:7C0A50* + ID_OUI_FROM_DATABASE=J-MEX Inc. + +OUI:40F2E9* + ID_OUI_FROM_DATABASE=IBM + +OUI:9C0473* + ID_OUI_FROM_DATABASE=Tecmobile (International) Ltd. + +OUI:CC262D* + ID_OUI_FROM_DATABASE=Verifi, LLC + +OUI:3C8AE5* + ID_OUI_FROM_DATABASE=Tensun Information Technology(Hangzhou) Co.,LTD + +OUI:7CB232* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + +OUI:54DF63* + ID_OUI_FROM_DATABASE=Intrakey technologies GmbH + +OUI:7C0187* + ID_OUI_FROM_DATABASE=Curtis Instruments, Inc. + +OUI:388EE7* + ID_OUI_FROM_DATABASE=Fanhattan LLC + +OUI:54F666* + ID_OUI_FROM_DATABASE=Berthold Technologies GmbH and Co.KG + +OUI:802FDE* + ID_OUI_FROM_DATABASE=Zurich Instruments AG + +OUI:08AF78* + ID_OUI_FROM_DATABASE=Totus Solutions, Inc. + +OUI:5C38E0* + ID_OUI_FROM_DATABASE=Shanghai Super Electronics Technology Co.,LTD + +OUI:A0E534* + ID_OUI_FROM_DATABASE=Stratec Biomedical AG + +OUI:2891D0* + ID_OUI_FROM_DATABASE=Stage Tec Entwicklungsgesellschaft für professionelle Audiotechnik mbH + +OUI:98291D* + ID_OUI_FROM_DATABASE=Jaguar de Mexico, SA de CV + +OUI:18863A* + ID_OUI_FROM_DATABASE=DIGITAL ART SYSTEM + +OUI:F4B72A* + ID_OUI_FROM_DATABASE=TIME INTERCONNECT LTD + +OUI:34D7B4* + ID_OUI_FROM_DATABASE=Tributary Systems, Inc. + +OUI:F40F9B* + ID_OUI_FROM_DATABASE=WAVELINK + +OUI:144319* + ID_OUI_FROM_DATABASE=Creative&Link Technology Limited + +OUI:64F50E* + ID_OUI_FROM_DATABASE=Kinion Technology Company Limited + +OUI:28A186* + ID_OUI_FROM_DATABASE=enblink + +OUI:1C9492* + ID_OUI_FROM_DATABASE=RUAG Schweiz AG + +OUI:24694A* + ID_OUI_FROM_DATABASE=Jasmine Systems Inc. + +OUI:C8C791* + ID_OUI_FROM_DATABASE=Zero1.tv GmbH + +OUI:60748D* + ID_OUI_FROM_DATABASE=Atmaca Elektronik + +OUI:78D129* + ID_OUI_FROM_DATABASE=Vicos + +OUI:78AB60* + ID_OUI_FROM_DATABASE=ABB Australia + +OUI:289A4B* + ID_OUI_FROM_DATABASE=SteelSeries ApS + +OUI:0CC66A* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:3078C2* + ID_OUI_FROM_DATABASE=Innowireless, Co. Ltd. + +OUI:7CFE28* + ID_OUI_FROM_DATABASE=Salutron Inc. + +OUI:109FA9* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:C0A364* + ID_OUI_FROM_DATABASE=3D Systems Massachusetts + +OUI:98A7B0* + ID_OUI_FROM_DATABASE=MCST ZAO + +OUI:88DC96* + ID_OUI_FROM_DATABASE=SENAO Networks, Inc. + +OUI:C455C2* + ID_OUI_FROM_DATABASE=Bach-Simpson + +OUI:ECA29B* + ID_OUI_FROM_DATABASE=Kemppi Oy + +OUI:04CE14* + ID_OUI_FROM_DATABASE=Wilocity LTD. + +OUI:802AFA* + ID_OUI_FROM_DATABASE=Germaneers GmbH + +OUI:1C8464* + ID_OUI_FROM_DATABASE=FORMOSA WIRELESS COMMUNICATION CORP. + +OUI:D867D9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B4218A* + ID_OUI_FROM_DATABASE=Dog Hunter LLC + +OUI:F8A03D* + ID_OUI_FROM_DATABASE=Dinstar Technologies Co., Ltd. + +OUI:D08CFF* + ID_OUI_FROM_DATABASE=UPWIS AB + +OUI:9C066E* + ID_OUI_FROM_DATABASE=Hytera Communications Corporation Limited + +OUI:746A89* + ID_OUI_FROM_DATABASE=Rezolt Corporation + +OUI:68D1FD* + ID_OUI_FROM_DATABASE=Shenzhen Trimax Technology Co.,Ltd + +OUI:241B13* + ID_OUI_FROM_DATABASE=Shanghai Nutshell Electronic Co., Ltd. + +OUI:B43564* + ID_OUI_FROM_DATABASE=Fujian Tian Cheng Electron Science & Technical Development Co.,Ltd. + +OUI:54D1B0* + ID_OUI_FROM_DATABASE=Universal Laser Systems, Inc + +OUI:A497BB* + ID_OUI_FROM_DATABASE=Hitachi Industrial Equipment Systems Co.,Ltd + +OUI:FC52CE* + ID_OUI_FROM_DATABASE=Control iD + +OUI:E804F3* + ID_OUI_FROM_DATABASE=Throughtek Co., Ltd. + +OUI:B85810* + ID_OUI_FROM_DATABASE=NUMERA, INC. + +OUI:2CAB25* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:AC6E1A* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co.,Ltd + +OUI:9886B1* + ID_OUI_FROM_DATABASE=Flyaudio corporation (China) + +OUI:28B3AB* + ID_OUI_FROM_DATABASE=Genmark Automation + +OUI:44E8A5* + ID_OUI_FROM_DATABASE=Myreka Technologies Sdn. Bhd. + +OUI:AC14D2* + ID_OUI_FROM_DATABASE=wi-daq, inc. + +OUI:9C4CAE* + ID_OUI_FROM_DATABASE=Mesa Labs + +OUI:7CD9FE* + ID_OUI_FROM_DATABASE=New Cosmos Electric Co., Ltd. + +OUI:E49069* + ID_OUI_FROM_DATABASE=Rockwell Automation + +OUI:B48910* + ID_OUI_FROM_DATABASE=Coster T.E. S.P.A. + +OUI:A4B1E9* + ID_OUI_FROM_DATABASE=Technicolor + +OUI:30AEF6* + ID_OUI_FROM_DATABASE=Radio Mobile Access + +OUI:58343B* + ID_OUI_FROM_DATABASE=Glovast Technology Ltd. + +OUI:54A04F* + ID_OUI_FROM_DATABASE=t-mac Technologies Ltd + +OUI:E44F5F* + ID_OUI_FROM_DATABASE=EDS Elektronik Destek San.Tic.Ltd.Sti + +OUI:08B738* + ID_OUI_FROM_DATABASE=Lite-On Technogy Corp. + +OUI:9C6650* + ID_OUI_FROM_DATABASE=Glodio Technolies Co.,Ltd Tianjin Branch + +OUI:503955* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:90CF6F* + ID_OUI_FROM_DATABASE=Dlogixs Co Ltd + +OUI:68AF13* + ID_OUI_FROM_DATABASE=Futura Mobility + +OUI:B82410* + ID_OUI_FROM_DATABASE=Magneti Marelli Slovakia s.r.o. + +OUI:A8EF26* + ID_OUI_FROM_DATABASE=Tritonwave + +OUI:F0D3E7* + ID_OUI_FROM_DATABASE=Sensometrix SA + +OUI:7CC8D0* + ID_OUI_FROM_DATABASE=TIANJIN YAAN TECHNOLOGY CO., LTD. + +OUI:88E917* + ID_OUI_FROM_DATABASE=Tamaggo + +OUI:80AAA4* + ID_OUI_FROM_DATABASE=USAG + +OUI:5C2479* + ID_OUI_FROM_DATABASE=Baltech AG + +OUI:E8CBA1* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F85F2A* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:286094* + ID_OUI_FROM_DATABASE=CAPELEC + +OUI:60E956* + ID_OUI_FROM_DATABASE=Ayla Networks, Inc + +OUI:287184* + ID_OUI_FROM_DATABASE=Spire Payments + +OUI:1CB094* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:FC5090* + ID_OUI_FROM_DATABASE=SIMEX Sp. z o.o. + +OUI:209BA5* + ID_OUI_FROM_DATABASE=JIAXING GLEAD Electronics Co.,Ltd + +OUI:60843B* + ID_OUI_FROM_DATABASE=Soladigm, Inc. + +OUI:508C77* + ID_OUI_FROM_DATABASE=DIRMEIER Schanktechnik GmbH &Co KG + +OUI:6089B1* + ID_OUI_FROM_DATABASE=Key Digital Systems + +OUI:080CC9* + ID_OUI_FROM_DATABASE=Mission Technology Group, dba Magma + +OUI:A0F450* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:44D15E* + ID_OUI_FROM_DATABASE=Shanghai Kingto Information Technology Ltd + +OUI:545EBD* + ID_OUI_FROM_DATABASE=NL Technologies + +OUI:C8BBD3* + ID_OUI_FROM_DATABASE=Embrane + +OUI:ECD19A* + ID_OUI_FROM_DATABASE=Zhuhai Liming Industries Co., Ltd + +OUI:346E8A* + ID_OUI_FROM_DATABASE=Ecosense + +OUI:ACEE3B* + ID_OUI_FROM_DATABASE=6harmonics Inc + +OUI:681605* + ID_OUI_FROM_DATABASE=Systems And Electronic Development FZCO + +OUI:04F17D* + ID_OUI_FROM_DATABASE=Tarana Wireless + +OUI:A0DC04* + ID_OUI_FROM_DATABASE=Becker-Antriebe GmbH + +OUI:8CC121* + ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company + +OUI:2CBE97* + ID_OUI_FROM_DATABASE=Ingenieurbuero Bickele und Buehler GmbH + +OUI:045A95* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:B40E96* + ID_OUI_FROM_DATABASE=HERAN + +OUI:0CAF5A* + ID_OUI_FROM_DATABASE=GENUS POWER INFRASTRUCTURES LIMITED + +OUI:D0699E* + ID_OUI_FROM_DATABASE=LUMINEX Lighting Control Equipment + +OUI:64AE88* + ID_OUI_FROM_DATABASE=Polytec GmbH + +OUI:2C542D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:709E86* + ID_OUI_FROM_DATABASE=X6D Limited + +OUI:946124* + ID_OUI_FROM_DATABASE=Pason Systems + +OUI:DC309C* + ID_OUI_FROM_DATABASE=Heyrex Limited + +OUI:E81324* + ID_OUI_FROM_DATABASE=GuangZhou Bonsoninfo System CO.,LTD + +OUI:0036F8* + ID_OUI_FROM_DATABASE=Conti Temic microelectronic GmbH + +OUI:443839* + ID_OUI_FROM_DATABASE=Cumulus Networks, inc + +OUI:20F002* + ID_OUI_FROM_DATABASE=MTData Developments Pty. Ltd. + +OUI:CC912B* + ID_OUI_FROM_DATABASE=TE Connectivity Touch Solutions + +OUI:785262* + ID_OUI_FROM_DATABASE=Shenzhen Hojy Software Co., Ltd. + +OUI:40336C* + ID_OUI_FROM_DATABASE=Godrej & Boyce Mfg. co. ltd + +OUI:FC1D59* + ID_OUI_FROM_DATABASE=I Smart Cities HK Ltd + +OUI:EC0ED6* + ID_OUI_FROM_DATABASE=ITECH INSTRUMENTS SAS + +OUI:D0D212* + ID_OUI_FROM_DATABASE=K2NET Co.,Ltd. + +OUI:9C8EDC* + ID_OUI_FROM_DATABASE=Teracom Limited + +OUI:146A0B* + ID_OUI_FROM_DATABASE=Cypress Electronics Limited + +OUI:B0750C* + ID_OUI_FROM_DATABASE=QA Cafe + +OUI:B4E1EB* + ID_OUI_FROM_DATABASE=Private + +OUI:FC2A54* + ID_OUI_FROM_DATABASE=Connected Data, Inc. + +OUI:A090DE* + ID_OUI_FROM_DATABASE=VEEDIMS,LLC + +OUI:AC1461* + ID_OUI_FROM_DATABASE=ATAW Co., Ltd. + +OUI:508A42* + ID_OUI_FROM_DATABASE=Uptmate Technology Co., LTD + +OUI:8C57FD* + ID_OUI_FROM_DATABASE=LVX Western + +OUI:002A6A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B88F14* + ID_OUI_FROM_DATABASE=Analytica GmbH + +OUI:94FAE8* + ID_OUI_FROM_DATABASE=Shenzhen Eycom Technology Co., Ltd + +OUI:3CA315* + ID_OUI_FROM_DATABASE=Bless Information & Communications Co., Ltd + +OUI:F8DB4C* + ID_OUI_FROM_DATABASE=PNY Technologies, INC. + +OUI:F83094* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Telecom Limited + +OUI:2817CE* + ID_OUI_FROM_DATABASE=Omnisense Ltd + +OUI:28E608* + ID_OUI_FROM_DATABASE=Tokheim + +OUI:E477D4* + ID_OUI_FROM_DATABASE=Minrray Industry Co.,Ltd + +OUI:A4B980* + ID_OUI_FROM_DATABASE=Parking BOXX Inc. + +OUI:002D76* + ID_OUI_FROM_DATABASE=TITECH GmbH + +OUI:78A183* + ID_OUI_FROM_DATABASE=Advidia + +OUI:F85063* + ID_OUI_FROM_DATABASE=Verathon + +OUI:400E67* + ID_OUI_FROM_DATABASE=Tremol Ltd. + +OUI:901B0E* + ID_OUI_FROM_DATABASE=Fujitsu Technology Solutions GmbH + +OUI:5C6F4F* + ID_OUI_FROM_DATABASE=S.A. SISTEL + +OUI:B058C4* + ID_OUI_FROM_DATABASE=Broadcast Microwave Services, Inc + +OUI:B820E7* + ID_OUI_FROM_DATABASE=Guangzhou Horizontal Information & Network Integration Co. Ltd + +OUI:98588A* + ID_OUI_FROM_DATABASE=SYSGRATION Ltd. + +OUI:842B50* + ID_OUI_FROM_DATABASE=Huria Co.,Ltd. + +OUI:0C5A19* + ID_OUI_FROM_DATABASE=Axtion Sdn Bhd + +OUI:A00CA1* + ID_OUI_FROM_DATABASE=SKTB SKiT + +OUI:E09579* + ID_OUI_FROM_DATABASE=ORTHOsoft inc, d/b/a Zimmer CAS + +OUI:307ECB* + ID_OUI_FROM_DATABASE=SFR + +OUI:90A783* + ID_OUI_FROM_DATABASE=JSW PACIFIC CORPORATION + +OUI:000830* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:CCEF48* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:78A5DD* + ID_OUI_FROM_DATABASE=Shenzhen Smarteye Digital Electronics Co., Ltd + +OUI:28B0CC* + ID_OUI_FROM_DATABASE=Xenya d.o.o. + +OUI:ECE744* + ID_OUI_FROM_DATABASE=Omntec mfg. inc + +OUI:80427C* + ID_OUI_FROM_DATABASE=Adolf Tedsen GmbH & Co. KG + +OUI:F8F7D3* + ID_OUI_FROM_DATABASE=International Communications Corporation + +OUI:B89AED* + ID_OUI_FROM_DATABASE=OceanServer Technology, Inc + +OUI:E455EA* + ID_OUI_FROM_DATABASE=Dedicated Computing + +OUI:00FC58* + ID_OUI_FROM_DATABASE=WebSilicon Ltd. + +OUI:64A0E7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:18E80F* + ID_OUI_FROM_DATABASE=Viking Electronics Inc. + +OUI:EC6264* + ID_OUI_FROM_DATABASE=Global411 Internet Services, LLC + +OUI:00F051* + ID_OUI_FROM_DATABASE=KWB Gmbh + +OUI:F0DEB9* + ID_OUI_FROM_DATABASE=ShangHai Y&Y Electronics Co., Ltd + +OUI:AC54EC* + ID_OUI_FROM_DATABASE=IEEE P1823 Standards Working Group + +OUI:C8292A* + ID_OUI_FROM_DATABASE=Barun Electronics + +OUI:E0DADC* + ID_OUI_FROM_DATABASE=JVC KENWOOD Corporation + +OUI:C894D2* + ID_OUI_FROM_DATABASE=Jiangsu Datang Electronic Products Co., Ltd + +OUI:A0423F* + ID_OUI_FROM_DATABASE=Tyan Computer Corp + +OUI:5C18B5* + ID_OUI_FROM_DATABASE=Talon Communications + +OUI:78BAD0* + ID_OUI_FROM_DATABASE=Shinybow Technology Co. Ltd. + +OUI:306CBE* + ID_OUI_FROM_DATABASE=Skymotion Technology (HK) Limited + +OUI:40D559* + ID_OUI_FROM_DATABASE=MICRO S.E.R.I. + +OUI:F82F5B* + ID_OUI_FROM_DATABASE=eGauge Systems LLC + +OUI:3499D7* + ID_OUI_FROM_DATABASE=Universal Flow Monitors, Inc. + +OUI:7C336E* + ID_OUI_FROM_DATABASE=MEG Electronics Inc. + +OUI:D4D249* + ID_OUI_FROM_DATABASE=Power Ethernet + +OUI:10C2BA* + ID_OUI_FROM_DATABASE=UTT Co., Ltd. + +OUI:F0DA7C* + ID_OUI_FROM_DATABASE=RLH INDUSTRIES,INC. + +OUI:40984C* + ID_OUI_FROM_DATABASE=Casacom Solutions AG + +OUI:B8975A* + ID_OUI_FROM_DATABASE=BIOSTAR Microtech Int'l Corp. + +OUI:4833DD* + ID_OUI_FROM_DATABASE=ZENNIO AVANCE Y TECNOLOGIA, S.L. + +OUI:D4D748* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:9CCAD9* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F8313E* + ID_OUI_FROM_DATABASE=endeavour GmbH + +OUI:10FC54* + ID_OUI_FROM_DATABASE=Shany Electronic Co., Ltd. + +OUI:D4CA6D* + ID_OUI_FROM_DATABASE=Routerboard.com + +OUI:D8E743* + ID_OUI_FROM_DATABASE=Wush, Inc + +OUI:908FCF* + ID_OUI_FROM_DATABASE=UNO System Co., Ltd + +OUI:903CAE* + ID_OUI_FROM_DATABASE=Yunnan KSEC Digital Technology Co.,Ltd. + +OUI:000831* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F0620D* + ID_OUI_FROM_DATABASE=Shenzhen Egreat Tech Corp.,Ltd + +OUI:843611* + ID_OUI_FROM_DATABASE=hyungseul publishing networks + +OUI:B8FD32* + ID_OUI_FROM_DATABASE=Zhejiang ROICX Microelectronics + +OUI:D8052E* + ID_OUI_FROM_DATABASE=Skyviia Corporation + +OUI:F83553* + ID_OUI_FROM_DATABASE=Magenta Research Ltd. + +OUI:DC3C2E* + ID_OUI_FROM_DATABASE=Manufacturing System Insights, Inc. + +OUI:40BC8B* + ID_OUI_FROM_DATABASE=itelio GmbH + +OUI:88C36E* + ID_OUI_FROM_DATABASE=Beijing Ereneben lnformation Technology Limited + +OUI:8CDE52* + ID_OUI_FROM_DATABASE=ISSC Technologies Corp. + +OUI:A8776F* + ID_OUI_FROM_DATABASE=Zonoff + +OUI:902B34* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:48E1AF* + ID_OUI_FROM_DATABASE=Vity + +OUI:245FDF* + ID_OUI_FROM_DATABASE=KYOCERA Corporation + +OUI:C0A0DE* + ID_OUI_FROM_DATABASE=Multi Touch Oy + +OUI:943AF0* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:B826D4* + ID_OUI_FROM_DATABASE=Furukawa Industrial S.A. Produtos Elétricos + +OUI:14E4EC* + ID_OUI_FROM_DATABASE=mLogic LLC + +OUI:FC0A81* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:AC0DFE* + ID_OUI_FROM_DATABASE=Ekon GmbH - myGEKKO + +OUI:005CB1* + ID_OUI_FROM_DATABASE=Gospell DIGITAL TECHNOLOGY CO., LTD + +OUI:186751* + ID_OUI_FROM_DATABASE=KOMEG Industrielle Messtechnik GmbH + +OUI:B467E9* + ID_OUI_FROM_DATABASE=Qingdao GoerTek Technology Co., Ltd. + +OUI:B49EE6* + ID_OUI_FROM_DATABASE=SHENZHEN TECHNOLOGY CO LTD + +OUI:7041B7* + ID_OUI_FROM_DATABASE=Edwards Lifesciences LLC + +OUI:A849A5* + ID_OUI_FROM_DATABASE=Lisantech Co., Ltd. + +OUI:94DB49* + ID_OUI_FROM_DATABASE=SITCORP + +OUI:8CD17B* + ID_OUI_FROM_DATABASE=CG Mobile + +OUI:144978* + ID_OUI_FROM_DATABASE=Digital Control Incorporated + +OUI:FC8FC4* + ID_OUI_FROM_DATABASE=Intelligent Technology Inc. + +OUI:F04A2B* + ID_OUI_FROM_DATABASE=PYRAMID Computer GmbH + +OUI:CC9093* + ID_OUI_FROM_DATABASE=Hansong Tehnologies + +OUI:78F7D0* + ID_OUI_FROM_DATABASE=Silverbrook Research + +OUI:F04B6A* + ID_OUI_FROM_DATABASE=Scientific Production Association Siberian Arsenal, Ltd. + +OUI:30DE86* + ID_OUI_FROM_DATABASE=Cedac Software S.r.l. + +OUI:F013C3* + ID_OUI_FROM_DATABASE=SHENZHEN FENDA TECHNOLOGY CO., LTD + +OUI:CCE7DF* + ID_OUI_FROM_DATABASE=American Magnetics, Inc. + +OUI:E44E18* + ID_OUI_FROM_DATABASE=Gardasoft VisionLimited + +OUI:D41C1C* + ID_OUI_FROM_DATABASE=RCF S.P.A. + +OUI:8C94CF* + ID_OUI_FROM_DATABASE=Encell Technology, Inc. + +OUI:149090* + ID_OUI_FROM_DATABASE=KongTop industrial(shen zhen)CO.,LTD + +OUI:CCF8F0* + ID_OUI_FROM_DATABASE=Xi'an HISU Multimedia Technology Co.,Ltd. + +OUI:30F9ED* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:28C718* + ID_OUI_FROM_DATABASE=Altierre + +OUI:2046A1* + ID_OUI_FROM_DATABASE=VECOW Co., Ltd + +OUI:8C271D* + ID_OUI_FROM_DATABASE=QuantHouse + +OUI:9C8BF1* + ID_OUI_FROM_DATABASE=The Warehouse Limited + +OUI:147DC5* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:944696* + ID_OUI_FROM_DATABASE=BaudTec Corporation + +OUI:90342B* + ID_OUI_FROM_DATABASE=Gatekeeper Systems, Inc. + +OUI:D45251* + ID_OUI_FROM_DATABASE=IBT Ingenieurbureau Broennimann Thun + +OUI:3071B2* + ID_OUI_FROM_DATABASE=Hangzhou Prevail Optoelectronic Equipment Co.,LTD. + +OUI:B82ADC* + ID_OUI_FROM_DATABASE=EFR Europäische Funk-Rundsteuerung GmbH + +OUI:B09BD4* + ID_OUI_FROM_DATABASE=GNH Software India Private Limited + +OUI:7CF429* + ID_OUI_FROM_DATABASE=NUUO Inc. + +OUI:B8CDA7* + ID_OUI_FROM_DATABASE=Maxeler Technologies Ltd. + +OUI:F49461* + ID_OUI_FROM_DATABASE=NexGen Storage + +OUI:804731* + ID_OUI_FROM_DATABASE=Packet Design, Inc. + +OUI:ACCB09* + ID_OUI_FROM_DATABASE=Hefcom Metering (Pty) Ltd + +OUI:10EED9* + ID_OUI_FROM_DATABASE=Canoga Perkins Corporation + +OUI:240BB1* + ID_OUI_FROM_DATABASE=KOSTAL Industrie Elektrik GmbH + +OUI:20EEC6* + ID_OUI_FROM_DATABASE=Elefirst Science & Tech Co ., ltd + +OUI:807A7F* + ID_OUI_FROM_DATABASE=ABB Genway Xiamen Electrical Equipment CO., LTD + +OUI:14373B* + ID_OUI_FROM_DATABASE=PROCOM Systems + +OUI:B81999* + ID_OUI_FROM_DATABASE=Nesys + +OUI:4C5585* + ID_OUI_FROM_DATABASE=Hamilton Systems + +OUI:8CCF5C* + ID_OUI_FROM_DATABASE=BEFEGA GmbH + +OUI:A0133B* + ID_OUI_FROM_DATABASE=HiTi Digital, Inc. + +OUI:448E12* + ID_OUI_FROM_DATABASE=DT Research, Inc. + +OUI:9C5711* + ID_OUI_FROM_DATABASE=Feitian Xunda(Beijing) Aeronautical Information Technology Co., Ltd. + +OUI:18AD4D* + ID_OUI_FROM_DATABASE=Polostar Technology Corporation + +OUI:4CA74B* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:549478* + ID_OUI_FROM_DATABASE=Silvershore Technology Partners + +OUI:F4B164* + ID_OUI_FROM_DATABASE=Lightning Telecommunications Technology Co. Ltd + +OUI:0CFC83* + ID_OUI_FROM_DATABASE=Airoha Technology Corp., + +OUI:0C51F7* + ID_OUI_FROM_DATABASE=CHAUVIN ARNOUX + +OUI:70B035* + ID_OUI_FROM_DATABASE=Shenzhen Zowee Technology Co., Ltd + +OUI:708105* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00082F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:542018* + ID_OUI_FROM_DATABASE=Tely Labs + +OUI:581FEF* + ID_OUI_FROM_DATABASE=Tuttnaer LTD + +OUI:F8F25A* + ID_OUI_FROM_DATABASE=G-Lab GmbH + +OUI:BC779F* + ID_OUI_FROM_DATABASE=SBM Co., Ltd. + +OUI:C058A7* + ID_OUI_FROM_DATABASE=Pico Systems Co., Ltd. + +OUI:04D783* + ID_OUI_FROM_DATABASE=Y&H E&C Co.,LTD. + +OUI:00E175* + ID_OUI_FROM_DATABASE=AK-Systems Ltd + +OUI:843F4E* + ID_OUI_FROM_DATABASE=Tri-Tech Manufacturing, Inc. + +OUI:C83232* + ID_OUI_FROM_DATABASE=Hunting Innova + +OUI:D059C3* + ID_OUI_FROM_DATABASE=CeraMicro Technology Corporation + +OUI:EC9681* + ID_OUI_FROM_DATABASE=2276427 Ontario Inc + +OUI:B8288B* + ID_OUI_FROM_DATABASE=Parker Hannifin Manufacturing (UK) Ltd + +OUI:5835D9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:802E14* + ID_OUI_FROM_DATABASE=azeti Networks AG + +OUI:E8944C* + ID_OUI_FROM_DATABASE=Cogent Healthcare Systems Ltd + +OUI:68F895* + ID_OUI_FROM_DATABASE=Redflow Limited + +OUI:A88792* + ID_OUI_FROM_DATABASE=Broadband Antenna Tracking Systems + +OUI:901900* + ID_OUI_FROM_DATABASE=SCS SA + +OUI:AC932F* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:1435B3* + ID_OUI_FROM_DATABASE=Future Designs, Inc. + +OUI:FCF1CD* + ID_OUI_FROM_DATABASE=OPTEX-FA CO.,LTD. + +OUI:B03829* + ID_OUI_FROM_DATABASE=Siliconware Precision Industries Co., Ltd. + +OUI:BC0F2B* + ID_OUI_FROM_DATABASE=FORTUNE TECHGROUP CO.,LTD + +OUI:8CF9C9* + ID_OUI_FROM_DATABASE=MESADA Technology Co.,Ltd. + +OUI:E42AD3* + ID_OUI_FROM_DATABASE=Magneti Marelli S.p.A. Powertrain + +OUI:FC10BD* + ID_OUI_FROM_DATABASE=Control Sistematizado S.A. + +OUI:443719* + ID_OUI_FROM_DATABASE=2 Save Energy Ltd + +OUI:E83EB6* + ID_OUI_FROM_DATABASE=RIM + +OUI:94FD1D* + ID_OUI_FROM_DATABASE=WhereWhen Corp + +OUI:0CE82F* + ID_OUI_FROM_DATABASE=Bonfiglioli Vectron GmbH + +OUI:C0626B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74D0DC* + ID_OUI_FROM_DATABASE=ERICSSON AB + +OUI:B4B88D* + ID_OUI_FROM_DATABASE=Thuh Company + +OUI:60F59C* + ID_OUI_FROM_DATABASE=CRU-Dataport + +OUI:C4108A* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:4C73A5* + ID_OUI_FROM_DATABASE=KOVE + +OUI:F86971* + ID_OUI_FROM_DATABASE=Seibu Electric Co., + +OUI:44AA27* + ID_OUI_FROM_DATABASE=udworks Co., Ltd. + +OUI:6CAD3F* + ID_OUI_FROM_DATABASE=Hubbell Building Automation, Inc. + +OUI:8427CE* + ID_OUI_FROM_DATABASE=Corporation of the Presiding Bishop of The Church of Jesus Christ of Latter-day Saints + +OUI:D428B2* + ID_OUI_FROM_DATABASE=ioBridge, Inc. + +OUI:90B8D0* + ID_OUI_FROM_DATABASE=Joyent, Inc. + +OUI:909060* + ID_OUI_FROM_DATABASE=RSI VIDEO TECHNOLOGIES + +OUI:281471* + ID_OUI_FROM_DATABASE=Lantis co., LTD. + +OUI:1407E0* + ID_OUI_FROM_DATABASE=Abrantix AG + +OUI:DCCF94* + ID_OUI_FROM_DATABASE=Beijing Rongcheng Hutong Technology Co., Ltd. + +OUI:18E288* + ID_OUI_FROM_DATABASE=STT Condigi + +OUI:68876B* + ID_OUI_FROM_DATABASE=INQ Mobile Limited + +OUI:9866EA* + ID_OUI_FROM_DATABASE=Industrial Control Communications, Inc. + +OUI:F4A52A* + ID_OUI_FROM_DATABASE=Hawa Technologies Inc + +OUI:90CF15* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:B8D49D* + ID_OUI_FROM_DATABASE=M Seven System Ltd. + +OUI:B0A10A* + ID_OUI_FROM_DATABASE=Pivotal Systems Corporation + +OUI:48F47D* + ID_OUI_FROM_DATABASE=TechVision Holding Internation Limited + +OUI:6C391D* + ID_OUI_FROM_DATABASE=Beijing ZhongHuaHun Network Information center + +OUI:64D241* + ID_OUI_FROM_DATABASE=Keith & Koep GmbH + +OUI:101212* + ID_OUI_FROM_DATABASE=Vivo International Corporation Pty Ltd + +OUI:5087B8* + ID_OUI_FROM_DATABASE=Nuvyyo Inc + +OUI:E41289* + ID_OUI_FROM_DATABASE=topsystem Systemhaus GmbH + +OUI:A4134E* + ID_OUI_FROM_DATABASE=Luxul + +OUI:B09928* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:8C11CB* + ID_OUI_FROM_DATABASE=ABUS Security-Center GmbH & Co. KG + +OUI:806459* + ID_OUI_FROM_DATABASE=Nimbus Inc. + +OUI:A45A1C* + ID_OUI_FROM_DATABASE=smart-electronic GmbH + +OUI:8C89A5* + ID_OUI_FROM_DATABASE=Micro-Star INT'L CO., LTD + +OUI:3C672C* + ID_OUI_FROM_DATABASE=Sciovid Inc. + +OUI:18D071* + ID_OUI_FROM_DATABASE=DASAN CO., LTD. + +OUI:38D135* + ID_OUI_FROM_DATABASE=EasyIO Corporation Sdn. Bhd. + +OUI:184E94* + ID_OUI_FROM_DATABASE=MESSOA TECHNOLOGIES INC. + +OUI:94D93C* + ID_OUI_FROM_DATABASE=ENELPS + +OUI:DC9B1E* + ID_OUI_FROM_DATABASE=Intercom, Inc. + +OUI:5C7757* + ID_OUI_FROM_DATABASE=Haivision Network Video + +OUI:E8B4AE* + ID_OUI_FROM_DATABASE=Shenzhen C&D Electronics Co.,Ltd + +OUI:C45600* + ID_OUI_FROM_DATABASE=Galleon Embedded Computing + +OUI:E42FF6* + ID_OUI_FROM_DATABASE=Unicore communication Inc. + +OUI:B8F4D0* + ID_OUI_FROM_DATABASE=Herrmann Ultraschalltechnik GmbH & Co. Kg + +OUI:B4F323* + ID_OUI_FROM_DATABASE=PETATEL INC. + +OUI:C81E8E* + ID_OUI_FROM_DATABASE=ADV Security (S) Pte Ltd + +OUI:ACCABA* + ID_OUI_FROM_DATABASE=Midokura Co., Ltd. + +OUI:9C417C* + ID_OUI_FROM_DATABASE=Hame Technology Co., Limited + +OUI:10768A* + ID_OUI_FROM_DATABASE=EoCell + +OUI:044665* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:D0131E* + ID_OUI_FROM_DATABASE=Sunrex Technology Corp + +OUI:380197* + ID_OUI_FROM_DATABASE=TSST Global,Inc + +OUI:B40142* + ID_OUI_FROM_DATABASE=GCI Science & Technology Co.,LTD + +OUI:846EB1* + ID_OUI_FROM_DATABASE=Park Assist LLC + +OUI:6C504D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C0C1C0* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:1CBD0E* + ID_OUI_FROM_DATABASE=Amplified Engineering Pty Ltd + +OUI:F0A764* + ID_OUI_FROM_DATABASE=GST Co., Ltd. + +OUI:A0F217* + ID_OUI_FROM_DATABASE=GE Medical System(China) Co., Ltd. + +OUI:643409* + ID_OUI_FROM_DATABASE=BITwave Pte Ltd + +OUI:20D5AB* + ID_OUI_FROM_DATABASE=Korea Infocom Co.,Ltd. + +OUI:F05849* + ID_OUI_FROM_DATABASE=CareView Communications + +OUI:BC15A6* + ID_OUI_FROM_DATABASE=Taiwan Jantek Electronics,Ltd. + +OUI:241A8C* + ID_OUI_FROM_DATABASE=Squarehead Technology AS + +OUI:1083D2* + ID_OUI_FROM_DATABASE=Microseven Systems, LLC + +OUI:F05D89* + ID_OUI_FROM_DATABASE=Dycon Limited + +OUI:AC02CF* + ID_OUI_FROM_DATABASE=RW Tecnologia Industria e Comercio Ltda + +OUI:9067B5* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:40987B* + ID_OUI_FROM_DATABASE=Aisino Corporation + +OUI:6C2E33* + ID_OUI_FROM_DATABASE=Accelink Technologies Co.,Ltd. + +OUI:4CEDDE* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:E8E08F* + ID_OUI_FROM_DATABASE=GRAVOTECH MARKING SAS + +OUI:78B6C1* + ID_OUI_FROM_DATABASE=AOBO Telecom Co.,Ltd + +OUI:B8BA68* + ID_OUI_FROM_DATABASE=Xi'an Jizhong Digital Communication Co.,Ltd + +OUI:BC38D2* + ID_OUI_FROM_DATABASE=Pandachip Limited + +OUI:14EE9D* + ID_OUI_FROM_DATABASE=AirNav Systems LLC + +OUI:48174C* + ID_OUI_FROM_DATABASE=MicroPower technologies + +OUI:F81037* + ID_OUI_FROM_DATABASE=Atopia Systems, LP + +OUI:64F987* + ID_OUI_FROM_DATABASE=Avvasi Inc. + +OUI:3C7437* + ID_OUI_FROM_DATABASE=RIM + +OUI:04209A* + ID_OUI_FROM_DATABASE=Panasonic AVC Networks Company + +OUI:64DC01* + ID_OUI_FROM_DATABASE=Static Systems Group PLC + +OUI:1CF5E7* + ID_OUI_FROM_DATABASE=Turtle Industry Co., Ltd. + +OUI:9C4A7B* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:2C8065* + ID_OUI_FROM_DATABASE=HARTING Inc. of North America + +OUI:F8F014* + ID_OUI_FROM_DATABASE=RackWare Inc. + +OUI:E41C4B* + ID_OUI_FROM_DATABASE=V2 TECHNOLOGY, INC. + +OUI:E0143E* + ID_OUI_FROM_DATABASE=Modoosis Inc. + +OUI:5C6984* + ID_OUI_FROM_DATABASE=NUVICO + +OUI:204AAA* + ID_OUI_FROM_DATABASE=Hanscan Spain S.A. + +OUI:F02572* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:8091C0* + ID_OUI_FROM_DATABASE=AgileMesh, Inc. + +OUI:0CF0B4* + ID_OUI_FROM_DATABASE=Globalsat International Technology Ltd + +OUI:BCC61A* + ID_OUI_FROM_DATABASE=SPECTRA EMBEDDED SYSTEMS + +OUI:48DF1C* + ID_OUI_FROM_DATABASE=Wuhan NEC Fibre Optic Communications industry Co. Ltd + +OUI:D0D3FC* + ID_OUI_FROM_DATABASE=Mios, Ltd. + +OUI:989449* + ID_OUI_FROM_DATABASE=Skyworth Wireless Technology Ltd. + +OUI:C8DF7C* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F8C678* + ID_OUI_FROM_DATABASE=Carefusion + +OUI:FC3598* + ID_OUI_FROM_DATABASE=Favite Inc. + +OUI:A0AAFD* + ID_OUI_FROM_DATABASE=EraThink Technologies Corp. + +OUI:801F02* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:E03E7D* + ID_OUI_FROM_DATABASE=data-complex GmbH + +OUI:A4E32E* + ID_OUI_FROM_DATABASE=Silicon & Software Systems Ltd. + +OUI:1C19DE* + ID_OUI_FROM_DATABASE=eyevis GmbH + +OUI:DC07C1* + ID_OUI_FROM_DATABASE=HangZhou QiYang Technology Co.,Ltd. + +OUI:D8FE8F* + ID_OUI_FROM_DATABASE=IDFone Co., Ltd. + +OUI:0006F6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACAB8D* + ID_OUI_FROM_DATABASE=Lyngso Marine A/S + +OUI:181456* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:E8995A* + ID_OUI_FROM_DATABASE=PiiGAB, Processinformation i Goteborg AB + +OUI:D4E32C* + ID_OUI_FROM_DATABASE=S. Siedle & Sohne + +OUI:68DCE8* + ID_OUI_FROM_DATABASE=PacketStorm Communications + +OUI:78223D* + ID_OUI_FROM_DATABASE=Affirmed Networks + +OUI:60C980* + ID_OUI_FROM_DATABASE=Trymus + +OUI:94CDAC* + ID_OUI_FROM_DATABASE=Creowave Oy + +OUI:F4DCDA* + ID_OUI_FROM_DATABASE=Zhuhai Jiahe Communication Technology Co., limited + +OUI:100D32* + ID_OUI_FROM_DATABASE=Embedian, Inc. + +OUI:D82986* + ID_OUI_FROM_DATABASE=Best Wish Technology LTD + +OUI:C03B8F* + ID_OUI_FROM_DATABASE=Minicom Digital Signage + +OUI:A4218A* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:6C0460* + ID_OUI_FROM_DATABASE=RBH Access Technologies Inc. + +OUI:5C864A* + ID_OUI_FROM_DATABASE=Secret Labs LLC + +OUI:B8BA72* + ID_OUI_FROM_DATABASE=Cynove + +OUI:C00D7E* + ID_OUI_FROM_DATABASE=Additech, Inc. + +OUI:68784C* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:6C626D* + ID_OUI_FROM_DATABASE=Micro-Star INT'L CO., LTD + +OUI:8841C1* + ID_OUI_FROM_DATABASE=ORBISAT DA AMAZONIA IND E AEROL SA + +OUI:18B209* + ID_OUI_FROM_DATABASE=Torrey Pines Logic, Inc + +OUI:3018CF* + ID_OUI_FROM_DATABASE=DEOS control systems GmbH + +OUI:4CF737* + ID_OUI_FROM_DATABASE=SamJi Electronics Co., Ltd + +OUI:40406B* + ID_OUI_FROM_DATABASE=Icomera + +OUI:1880CE* + ID_OUI_FROM_DATABASE=Barberry Solutions Ltd + +OUI:CC43E3* + ID_OUI_FROM_DATABASE=Trump s.a. + +OUI:6C22AB* + ID_OUI_FROM_DATABASE=Ainsworth Game Technology + +OUI:3C106F* + ID_OUI_FROM_DATABASE=ALBAHITH TECHNOLOGIES + +OUI:7CE044* + ID_OUI_FROM_DATABASE=NEON Inc + +OUI:64D02D* + ID_OUI_FROM_DATABASE=Next Generation Integration (NGI) + +OUI:A04041* + ID_OUI_FROM_DATABASE=SAMWONFA Co.,Ltd. + +OUI:788C54* + ID_OUI_FROM_DATABASE=Eltek Technologies LTD + +OUI:9411DA* + ID_OUI_FROM_DATABASE=ITF Fröschl GmbH + +OUI:10E8EE* + ID_OUI_FROM_DATABASE=PhaseSpace + +OUI:A47C1F* + ID_OUI_FROM_DATABASE=Cobham plc + +OUI:8C1F94* + ID_OUI_FROM_DATABASE=RF Surgical System Inc. + +OUI:74A4A7* + ID_OUI_FROM_DATABASE=QRS Music Technologies, Inc. + +OUI:8039E5* + ID_OUI_FROM_DATABASE=PATLITE CORPORATION + +OUI:BCFFAC* + ID_OUI_FROM_DATABASE=TOPCON CORPORATION + +OUI:602A54* + ID_OUI_FROM_DATABASE=CardioTek B.V. + +OUI:1C3DE7* + ID_OUI_FROM_DATABASE=Sigma Koki Co.,Ltd. + +OUI:482CEA* + ID_OUI_FROM_DATABASE=Motorola Inc Business Light Radios + +OUI:70E139* + ID_OUI_FROM_DATABASE=3view Ltd + +OUI:AC6123* + ID_OUI_FROM_DATABASE=Drivven, Inc. + +OUI:3C04BF* + ID_OUI_FROM_DATABASE=PRAVIS SYSTEMS Co.Ltd., + +OUI:443D21* + ID_OUI_FROM_DATABASE=Nuvolt + +OUI:749050* + ID_OUI_FROM_DATABASE=Renesas Electronics Corporation + +OUI:7CBB6F* + ID_OUI_FROM_DATABASE=Cosco Electronics Co., Ltd. + +OUI:D466A8* + ID_OUI_FROM_DATABASE=Riedo Networks GmbH + +OUI:98E165* + ID_OUI_FROM_DATABASE=Accutome + +OUI:EC66D1* + ID_OUI_FROM_DATABASE=B&W Group LTD + +OUI:385FC3* + ID_OUI_FROM_DATABASE=Yu Jeong System, Co.Ltd + +OUI:94857A* + ID_OUI_FROM_DATABASE=Evantage Industries Corp + +OUI:4451DB* + ID_OUI_FROM_DATABASE=Raytheon BBN Technologies + +OUI:64995D* + ID_OUI_FROM_DATABASE=LGE + +OUI:585076* + ID_OUI_FROM_DATABASE=Linear Equipamentos Eletronicos SA + +OUI:4083DE* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:8897DF* + ID_OUI_FROM_DATABASE=Entrypass Corporation Sdn. Bhd. + +OUI:0C15C5* + ID_OUI_FROM_DATABASE=SDTEC Co., Ltd. + +OUI:9803A0* + ID_OUI_FROM_DATABASE=ABB n.v. Power Quality Products + +OUI:DCFAD5* + ID_OUI_FROM_DATABASE=STRONG Ges.m.b.H. + +OUI:D84606* + ID_OUI_FROM_DATABASE=Silicon Valley Global Marketing + +OUI:689234* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:D0E347* + ID_OUI_FROM_DATABASE=Yoga + +OUI:84A991* + ID_OUI_FROM_DATABASE=Cyber Trans Japan Co.,Ltd. + +OUI:D81C14* + ID_OUI_FROM_DATABASE=Compacta International, Ltd. + +OUI:9088A2* + ID_OUI_FROM_DATABASE=IONICS TECHNOLOGY ME LTDA + +OUI:B0B8D5* + ID_OUI_FROM_DATABASE=Nanjing Nengrui Auto Equipment CO.,Ltd + +OUI:8497B8* + ID_OUI_FROM_DATABASE=Memjet Inc. + +OUI:A8556A* + ID_OUI_FROM_DATABASE=Pocketnet Technology Inc. + +OUI:B081D8* + ID_OUI_FROM_DATABASE=I-sys Corp + +OUI:206AFF* + ID_OUI_FROM_DATABASE=Atlas Elektronik UK Limited + +OUI:EC542E* + ID_OUI_FROM_DATABASE=Shanghai XiMei Electronic Technology Co. Ltd + +OUI:B88E3A* + ID_OUI_FROM_DATABASE=Infinite Technologies JLT + +OUI:74BE08* + ID_OUI_FROM_DATABASE=ATEK Products, LLC + +OUI:E0EE1B* + ID_OUI_FROM_DATABASE=Panasonic Automotive Systems Company of America + +OUI:E80C38* + ID_OUI_FROM_DATABASE=DAEYOUNG INFORMATION SYSTEM CO., LTD + +OUI:68597F* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:2C3068* + ID_OUI_FROM_DATABASE=Pantech Co.,Ltd + +OUI:5C4058* + ID_OUI_FROM_DATABASE=Jefferson Audio Video Systems, Inc. + +OUI:64317E* + ID_OUI_FROM_DATABASE=Dexin Corporation + +OUI:AC9B84* + ID_OUI_FROM_DATABASE=Smak Tecnologia e Automacao + +OUI:4C022E* + ID_OUI_FROM_DATABASE=CMR KOREA CO., LTD + +OUI:24A42C* + ID_OUI_FROM_DATABASE=KOUKAAM a.s. + +OUI:34F39B* + ID_OUI_FROM_DATABASE=WizLAN Ltd. + +OUI:74B9EB* + ID_OUI_FROM_DATABASE=JinQianMao Technology Co.,Ltd. + +OUI:244597* + ID_OUI_FROM_DATABASE=GEMUE Gebr. Mueller Apparatebau + +OUI:30694B* + ID_OUI_FROM_DATABASE=RIM + +OUI:AC5135* + ID_OUI_FROM_DATABASE=MPI TECH + +OUI:E4EC10* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:00D38D* + ID_OUI_FROM_DATABASE=Hotel Technology Next Generation + +OUI:3C6278* + ID_OUI_FROM_DATABASE=SHENZHEN JETNET TECHNOLOGY CO.,LTD. + +OUI:8081A5* + ID_OUI_FROM_DATABASE=TONGQING COMMUNICATION EQUIPMENT (SHENZHEN) Co.,Ltd + +OUI:EC8EAD* + ID_OUI_FROM_DATABASE=DLX + +OUI:ECDE3D* + ID_OUI_FROM_DATABASE=Lamprey Networks, Inc. + +OUI:04FE7F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E8056D* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00D11C* + ID_OUI_FROM_DATABASE=ACETEL + +OUI:1056CA* + ID_OUI_FROM_DATABASE=Peplink International Ltd. + +OUI:44A689* + ID_OUI_FROM_DATABASE=PROMAX ELECTRONICA SA + +OUI:10CCDB* + ID_OUI_FROM_DATABASE=AXIMUM PRODUITS ELECTRONIQUES + +OUI:6C92BF* + ID_OUI_FROM_DATABASE=Inspur Electronic Information Industry Co.,Ltd. + +OUI:E01CEE* + ID_OUI_FROM_DATABASE=Bravo Tech, Inc. + +OUI:3C1915* + ID_OUI_FROM_DATABASE=GFI Chrono Time + +OUI:EC5C69* + ID_OUI_FROM_DATABASE=MITSUBISHI HEAVY INDUSTRIES MECHATRONICS SYSTEMS,LTD. + +OUI:04E548* + ID_OUI_FROM_DATABASE=Cohda Wireless Pty Ltd + +OUI:0C1DC2* + ID_OUI_FROM_DATABASE=SeAH Networks + +OUI:28CD4C* + ID_OUI_FROM_DATABASE=Individual Computers GmbH + +OUI:8C53F7* + ID_OUI_FROM_DATABASE=A&D ENGINEERING CO., LTD. + +OUI:781185* + ID_OUI_FROM_DATABASE=NBS Payment Solutions Inc. + +OUI:2893FE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:10B7F6* + ID_OUI_FROM_DATABASE=Plastoform Industries Ltd. + +OUI:2059A0* + ID_OUI_FROM_DATABASE=Paragon Technologies Inc. + +OUI:487119* + ID_OUI_FROM_DATABASE=SGB GROUP LTD. + +OUI:E0ABFE* + ID_OUI_FROM_DATABASE=Orb Networks, Inc. + +OUI:CCEA1C* + ID_OUI_FROM_DATABASE=DCONWORKS Co., Ltd + +OUI:ACE348* + ID_OUI_FROM_DATABASE=MadgeTech, Inc + +OUI:687F74* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:CCB888* + ID_OUI_FROM_DATABASE=AnB Securite s.a. + +OUI:CC2218* + ID_OUI_FROM_DATABASE=InnoDigital Co., Ltd. + +OUI:B86491* + ID_OUI_FROM_DATABASE=CK Telecom Ltd + +OUI:80C862* + ID_OUI_FROM_DATABASE=Openpeak, Inc + +OUI:E43593* + ID_OUI_FROM_DATABASE=Hangzhou GoTo technology Co.Ltd + +OUI:E0BC43* + ID_OUI_FROM_DATABASE=C2 Microsystems, Inc. + +OUI:7884EE* + ID_OUI_FROM_DATABASE=INDRA ESPACIO S.A. + +OUI:2C3F3E* + ID_OUI_FROM_DATABASE=Alge-Timing GmbH + +OUI:C0CFA3* + ID_OUI_FROM_DATABASE=Creative Electronics & Software, Inc. + +OUI:D4823E* + ID_OUI_FROM_DATABASE=Argosy Technologies, Ltd. + +OUI:844823* + ID_OUI_FROM_DATABASE=WOXTER TECHNOLOGY Co. Ltd + +OUI:D0F0DB* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:7C1476* + ID_OUI_FROM_DATABASE=Damall Technologies SAS + +OUI:D05875* + ID_OUI_FROM_DATABASE=Active Control Technology Inc. + +OUI:D81BFE* + ID_OUI_FROM_DATABASE=TWINLINX CORPORATION + +OUI:D46CBF* + ID_OUI_FROM_DATABASE=Goodrich ISR + +OUI:5C57C8* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:4CC602* + ID_OUI_FROM_DATABASE=Radios, Inc. + +OUI:3C05AB* + ID_OUI_FROM_DATABASE=Product Creation Studio + +OUI:3C39C3* + ID_OUI_FROM_DATABASE=JW Electronics Co., Ltd. + +OUI:547FEE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A4C2AB* + ID_OUI_FROM_DATABASE=Hangzhou LEAD-IT Information & Technology Co.,Ltd + +OUI:48AA5D* + ID_OUI_FROM_DATABASE=Store Electronic Systems + +OUI:1062C9* + ID_OUI_FROM_DATABASE=Adatis GmbH & Co. KG + +OUI:D8AE90* + ID_OUI_FROM_DATABASE=Itibia Technologies + +OUI:904716* + ID_OUI_FROM_DATABASE=RORZE CORPORATION + +OUI:28E794* + ID_OUI_FROM_DATABASE=Microtime Computer Inc. + +OUI:8894F9* + ID_OUI_FROM_DATABASE=Gemicom Technology, Inc. + +OUI:0CA42A* + ID_OUI_FROM_DATABASE=OB Telecom Electronic Technology Co., Ltd + +OUI:5850E6* + ID_OUI_FROM_DATABASE=Best Buy Corporation + +OUI:AC9A96* + ID_OUI_FROM_DATABASE=Lantiq Deutschland GmbH + +OUI:E86CDA* + ID_OUI_FROM_DATABASE=Supercomputers and Neurocomputers Research Center + +OUI:24B6B8* + ID_OUI_FROM_DATABASE=FRIEM SPA + +OUI:F86ECF* + ID_OUI_FROM_DATABASE=Arcx Inc + +OUI:8C8401* + ID_OUI_FROM_DATABASE=Private + +OUI:6C7039* + ID_OUI_FROM_DATABASE=Novar GmbH + +OUI:A4561B* + ID_OUI_FROM_DATABASE=MCOT Corporation + +OUI:80EE73* + ID_OUI_FROM_DATABASE=Shuttle Inc. + +OUI:10C73F* + ID_OUI_FROM_DATABASE=Midas Klark Teknik Ltd + +OUI:408A9A* + ID_OUI_FROM_DATABASE=TITENG CO., Ltd. + +OUI:702B1D* + ID_OUI_FROM_DATABASE=E-Domus International Limited + +OUI:F077D0* + ID_OUI_FROM_DATABASE=Xcellen + +OUI:785C72* + ID_OUI_FROM_DATABASE=Hioso Technology Co., Ltd. + +OUI:94236E* + ID_OUI_FROM_DATABASE=Shenzhen Junlan Electronic Ltd + +OUI:88BA7F* + ID_OUI_FROM_DATABASE=Qfiednet Co., Ltd. + +OUI:E02636* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:4456B7* + ID_OUI_FROM_DATABASE=Spawn Labs, Inc + +OUI:A09805* + ID_OUI_FROM_DATABASE=OpenVox Communication Co Ltd + +OUI:00271D* + ID_OUI_FROM_DATABASE=Comba Telecom Systems (China) Ltd. + +OUI:002721* + ID_OUI_FROM_DATABASE=Shenzhen Baoan Fenda Industrial Co., Ltd + +OUI:A09A5A* + ID_OUI_FROM_DATABASE=Time Domain + +OUI:64A837* + ID_OUI_FROM_DATABASE=Juni Korea Co., Ltd + +OUI:B4B5AF* + ID_OUI_FROM_DATABASE=Minsung Electronics + +OUI:044FAA* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:44568D* + ID_OUI_FROM_DATABASE=PNC Technologies Co., Ltd. + +OUI:ACD180* + ID_OUI_FROM_DATABASE=Crexendo Business Solutions, Inc. + +OUI:AC8317* + ID_OUI_FROM_DATABASE=Shenzhen Furtunetel Communication Co., Ltd + +OUI:E80B13* + ID_OUI_FROM_DATABASE=Akib Systems Taiwan, INC + +OUI:44C9A2* + ID_OUI_FROM_DATABASE=Greenwald Industries + +OUI:646E6C* + ID_OUI_FROM_DATABASE=Radio Datacom LLC + +OUI:E4751E* + ID_OUI_FROM_DATABASE=Getinge Sterilization AB + +OUI:F8811A* + ID_OUI_FROM_DATABASE=OVERKIZ + +OUI:042BBB* + ID_OUI_FROM_DATABASE=PicoCELA, Inc. + +OUI:FC0877* + ID_OUI_FROM_DATABASE=Prentke Romich Company + +OUI:ECD00E* + ID_OUI_FROM_DATABASE=MiraeRecognition Co., Ltd. + +OUI:747E1A* + ID_OUI_FROM_DATABASE=Red Embedded Design Limited + +OUI:C47D4F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4C9EE4* + ID_OUI_FROM_DATABASE=Hanyang Navicom Co.,Ltd. + +OUI:3CDF1E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BCB181* + ID_OUI_FROM_DATABASE=SHARP CORPORATION + +OUI:78B81A* + ID_OUI_FROM_DATABASE=INTER SALES A/S + +OUI:78192E* + ID_OUI_FROM_DATABASE=NASCENT Technology + +OUI:2C0623* + ID_OUI_FROM_DATABASE=Win Leader Inc. + +OUI:C82E94* + ID_OUI_FROM_DATABASE=Halfa Enterprise Co., Ltd. + +OUI:0C2755* + ID_OUI_FROM_DATABASE=Valuable Techologies Limited + +OUI:C038F9* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:F46349* + ID_OUI_FROM_DATABASE=Diffon Corporation + +OUI:5C8778* + ID_OUI_FROM_DATABASE=Cybertelbridge co.,ltd + +OUI:9C5E73* + ID_OUI_FROM_DATABASE=Calibre UK LTD + +OUI:F06281* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:003A9B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2C9127* + ID_OUI_FROM_DATABASE=Eintechno Corporation + +OUI:C09C92* + ID_OUI_FROM_DATABASE=COBY + +OUI:849000* + ID_OUI_FROM_DATABASE=Arnold & Richter Cine Technik + +OUI:C87248* + ID_OUI_FROM_DATABASE=Aplicom Oy + +OUI:74D850* + ID_OUI_FROM_DATABASE=Evrisko Systems + +OUI:6CAC60* + ID_OUI_FROM_DATABASE=Venetex Corp + +OUI:DC0265* + ID_OUI_FROM_DATABASE=Meditech Kft + +OUI:986DC8* + ID_OUI_FROM_DATABASE=TOSHIBA MITSUBISHI-ELECTRIC INDUSTRIAL SYSTEMS CORPORATION + +OUI:68A1B7* + ID_OUI_FROM_DATABASE=Honghao Mingchuan Technology (Beijing) CO.,Ltd. + +OUI:7CCFCF* + ID_OUI_FROM_DATABASE=Shanghai SEARI Intelligent System Co., Ltd + +OUI:EC3091* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:3032D4* + ID_OUI_FROM_DATABASE=Hanilstm Co., Ltd. + +OUI:0026EE* + ID_OUI_FROM_DATABASE=TKM GmbH + +OUI:0026E7* + ID_OUI_FROM_DATABASE=Shanghai ONLAN Communication Tech. Co., Ltd. + +OUI:0026E1* + ID_OUI_FROM_DATABASE=Stanford University, OpenFlow Group + +OUI:0026DB* + ID_OUI_FROM_DATABASE=Ionics EMS Inc. + +OUI:0026CE* + ID_OUI_FROM_DATABASE=Kozumi USA Corp. + +OUI:0026D5* + ID_OUI_FROM_DATABASE=Ory Solucoes em Comercio de Informatica Ltda. + +OUI:0026C8* + ID_OUI_FROM_DATABASE=System Sensor + +OUI:002711* + ID_OUI_FROM_DATABASE=LanPro Inc + +OUI:00270D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002707* + ID_OUI_FROM_DATABASE=Lift Complex DS, JSC + +OUI:002700* + ID_OUI_FROM_DATABASE=Shenzhen Siglent Technology Co., Ltd. + +OUI:0026FA* + ID_OUI_FROM_DATABASE=BandRich Inc. + +OUI:0026F4* + ID_OUI_FROM_DATABASE=Nesslab + +OUI:0025D7* + ID_OUI_FROM_DATABASE=CEDO + +OUI:0025D2* + ID_OUI_FROM_DATABASE=InpegVision Co., Ltd + +OUI:0025D1* + ID_OUI_FROM_DATABASE=Eastern Asia Technology Limited + +OUI:0025CB* + ID_OUI_FROM_DATABASE=Reiner SCT + +OUI:0025C4* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:0025BF* + ID_OUI_FROM_DATABASE=Wireless Cables Inc. + +OUI:0025B1* + ID_OUI_FROM_DATABASE=Maya-Creation Corporation + +OUI:0025B8* + ID_OUI_FROM_DATABASE=Agile Communications, Inc. + +OUI:0025B2* + ID_OUI_FROM_DATABASE=MBDA Deutschland GmbH + +OUI:0025AC* + ID_OUI_FROM_DATABASE=I-Tech corporation + +OUI:0026C2* + ID_OUI_FROM_DATABASE=SCDI Co. LTD + +OUI:0026BC* + ID_OUI_FROM_DATABASE=General Jack Technology Ltd. + +OUI:0026B4* + ID_OUI_FROM_DATABASE=Ford Motor Company + +OUI:0026AE* + ID_OUI_FROM_DATABASE=Wireless Measurement Ltd + +OUI:0026AA* + ID_OUI_FROM_DATABASE=Kenmec Mechanical Engineering Co., Ltd. + +OUI:0026A4* + ID_OUI_FROM_DATABASE=Novus Produtos Eletronicos Ltda + +OUI:002698* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00269D* + ID_OUI_FROM_DATABASE=M2Mnet Co., Ltd. + +OUI:00268B* + ID_OUI_FROM_DATABASE=Guangzhou Escene Computer Technology Limited + +OUI:002685* + ID_OUI_FROM_DATABASE=Digital Innovation + +OUI:002678* + ID_OUI_FROM_DATABASE=Logic Instrument SA + +OUI:002672* + ID_OUI_FROM_DATABASE=AAMP of America + +OUI:00266B* + ID_OUI_FROM_DATABASE=SHINE UNION ENTERPRISE LIMITED + +OUI:002666* + ID_OUI_FROM_DATABASE=EFM Networks + +OUI:002665* + ID_OUI_FROM_DATABASE=ProtectedLogic Corporation + +OUI:002651* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002652* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002646* + ID_OUI_FROM_DATABASE=SHENYANG TONGFANG MULTIMEDIA TECHNOLOGY COMPANY LIMITED + +OUI:002640* + ID_OUI_FROM_DATABASE=Baustem Broadband Technologies, Ltd. + +OUI:00263A* + ID_OUI_FROM_DATABASE=Digitec Systems + +OUI:002634* + ID_OUI_FROM_DATABASE=Infineta Systems, Inc + +OUI:002633* + ID_OUI_FROM_DATABASE=MIR - Medical International Research + +OUI:00262E* + ID_OUI_FROM_DATABASE=Chengdu Jiuzhou Electronic Technology Inc + +OUI:002627* + ID_OUI_FROM_DATABASE=Truesell + +OUI:002621* + ID_OUI_FROM_DATABASE=InteliCloud Technology Inc. + +OUI:00261B* + ID_OUI_FROM_DATABASE=LAUREL BANK MACHINES CO., LTD. + +OUI:002614* + ID_OUI_FROM_DATABASE=KTNF + +OUI:00260E* + ID_OUI_FROM_DATABASE=Ablaze Systems, LLC + +OUI:002602* + ID_OUI_FROM_DATABASE=SMART Temps LLC + +OUI:002601* + ID_OUI_FROM_DATABASE=Cutera Inc + +OUI:0025F7* + ID_OUI_FROM_DATABASE=Ansaldo STS USA + +OUI:0025FC* + ID_OUI_FROM_DATABASE=ENDA ENDUSTRIYEL ELEKTRONIK LTD. STI. + +OUI:0025ED* + ID_OUI_FROM_DATABASE=NuVo Technologies LLC + +OUI:0025EE* + ID_OUI_FROM_DATABASE=Avtex Ltd + +OUI:0025E8* + ID_OUI_FROM_DATABASE=Idaho Technology + +OUI:0025E3* + ID_OUI_FROM_DATABASE=Hanshinit Inc. + +OUI:0025DE* + ID_OUI_FROM_DATABASE=Probits Co., LTD. + +OUI:002579* + ID_OUI_FROM_DATABASE=J & F Labs + +OUI:00257E* + ID_OUI_FROM_DATABASE=NEW POS Technology Limited + +OUI:002572* + ID_OUI_FROM_DATABASE=Nemo-Q International AB + +OUI:00256B* + ID_OUI_FROM_DATABASE=ATENIX E.E. s.r.l. + +OUI:00256C* + ID_OUI_FROM_DATABASE=Azimut Production Association JSC + +OUI:00255F* + ID_OUI_FROM_DATABASE=SenTec AG + +OUI:00255A* + ID_OUI_FROM_DATABASE=Tantalus Systems Corp. + +OUI:002559* + ID_OUI_FROM_DATABASE=Syphan Technologies Ltd + +OUI:0025A5* + ID_OUI_FROM_DATABASE=Walnut Media Network + +OUI:00259F* + ID_OUI_FROM_DATABASE=TechnoDigital Technologies GmbH + +OUI:002599* + ID_OUI_FROM_DATABASE=Hedon e.d. B.V. + +OUI:002592* + ID_OUI_FROM_DATABASE=Guangzhou Shirui Electronic Co., Ltd + +OUI:00258D* + ID_OUI_FROM_DATABASE=Haier + +OUI:002588* + ID_OUI_FROM_DATABASE=Genie Industries, Inc. + +OUI:002583* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00254C* + ID_OUI_FROM_DATABASE=Videon Central, Inc. + +OUI:002536* + ID_OUI_FROM_DATABASE=Oki Electric Industry Co., Ltd. + +OUI:00253D* + ID_OUI_FROM_DATABASE=DRS Consolidated Controls + +OUI:002540* + ID_OUI_FROM_DATABASE=Quasar Technologies, Inc. + +OUI:002533* + ID_OUI_FROM_DATABASE=WITTENSTEIN AG + +OUI:00252C* + ID_OUI_FROM_DATABASE=Entourage Systems, Inc. + +OUI:002502* + ID_OUI_FROM_DATABASE=NaturalPoint + +OUI:0024FB* + ID_OUI_FROM_DATABASE=Private + +OUI:0024F6* + ID_OUI_FROM_DATABASE=MIYOSHI ELECTRONICS CORPORATION + +OUI:0024EA* + ID_OUI_FROM_DATABASE=iris-GmbH infrared & intelligent sensors + +OUI:0024E3* + ID_OUI_FROM_DATABASE=CAO Group + +OUI:002527* + ID_OUI_FROM_DATABASE=Bitrode Corp. + +OUI:002524* + ID_OUI_FROM_DATABASE=Lightcomm Technology Co., Ltd + +OUI:00251F* + ID_OUI_FROM_DATABASE=ZYNUS VISION INC. + +OUI:00251A* + ID_OUI_FROM_DATABASE=Psiber Data Systems Inc. + +OUI:002515* + ID_OUI_FROM_DATABASE=SFR + +OUI:00250E* + ID_OUI_FROM_DATABASE=gt german telematics gmbh + +OUI:002507* + ID_OUI_FROM_DATABASE=ASTAK Inc. + +OUI:002509* + ID_OUI_FROM_DATABASE=SHARETRONIC Group LTD + +OUI:002437* + ID_OUI_FROM_DATABASE=Motorola - BSG + +OUI:00243C* + ID_OUI_FROM_DATABASE=S.A.A.A. + +OUI:002430* + ID_OUI_FROM_DATABASE=Ruby Tech Corp. + +OUI:0023FB* + ID_OUI_FROM_DATABASE=IP Datatel, LLC. + +OUI:0023F3* + ID_OUI_FROM_DATABASE=Glocom, Inc. + +OUI:0023EF* + ID_OUI_FROM_DATABASE=Zuend Systemtechnik AG + +OUI:0023E9* + ID_OUI_FROM_DATABASE=F5 Networks, Inc. + +OUI:0023E3* + ID_OUI_FROM_DATABASE=Microtronic AG + +OUI:0023E2* + ID_OUI_FROM_DATABASE=SEA Signalisation + +OUI:0023DD* + ID_OUI_FROM_DATABASE=ELGIN S.A. + +OUI:0023D0* + ID_OUI_FROM_DATABASE=Uniloc USA Inc. + +OUI:0023CA* + ID_OUI_FROM_DATABASE=Behind The Set, LLC + +OUI:0024B0* + ID_OUI_FROM_DATABASE=ESAB AB + +OUI:0024A9* + ID_OUI_FROM_DATABASE=Ag Leader Technology + +OUI:0024A2* + ID_OUI_FROM_DATABASE=Hong Kong Middleware Technology Limited + +OUI:0024A4* + ID_OUI_FROM_DATABASE=Siklu Communication + +OUI:00249D* + ID_OUI_FROM_DATABASE=NES Technology Inc. + +OUI:00248A* + ID_OUI_FROM_DATABASE=Kaga Electronics Co., Ltd. + +OUI:00248F* + ID_OUI_FROM_DATABASE=DO-MONIX + +OUI:002496* + ID_OUI_FROM_DATABASE=Ginzinger electronic systems + +OUI:002477* + ID_OUI_FROM_DATABASE=Tibbo Technology + +OUI:002470* + ID_OUI_FROM_DATABASE=AUROTECH ultrasound AS. + +OUI:002472* + ID_OUI_FROM_DATABASE=ReDriven Power Inc. + +OUI:00246B* + ID_OUI_FROM_DATABASE=Covia, Inc. + +OUI:002464* + ID_OUI_FROM_DATABASE=Bridge Technologies Co AS + +OUI:00245F* + ID_OUI_FROM_DATABASE=Vine Telecom CO.,Ltd. + +OUI:002420* + ID_OUI_FROM_DATABASE=NetUP Inc. + +OUI:002426* + ID_OUI_FROM_DATABASE=NOHMI BOSAI LTD. + +OUI:00241A* + ID_OUI_FROM_DATABASE=Red Beetle Inc. + +OUI:002413* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00240D* + ID_OUI_FROM_DATABASE=OnePath Networks LTD. + +OUI:00240E* + ID_OUI_FROM_DATABASE=Inventec Besta Co., Ltd. + +OUI:002407* + ID_OUI_FROM_DATABASE=TELEM SAS + +OUI:002400* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0024D0* + ID_OUI_FROM_DATABASE=Shenzhen SOGOOD Industry CO.,LTD. + +OUI:0024D5* + ID_OUI_FROM_DATABASE=Winward Industrial Limited + +OUI:0024C9* + ID_OUI_FROM_DATABASE=Broadband Solutions Group + +OUI:0024C4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0024BF* + ID_OUI_FROM_DATABASE=CIAT + +OUI:0024B5* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00245A* + ID_OUI_FROM_DATABASE=Nanjing Panda Electronics Company Limited + +OUI:002453* + ID_OUI_FROM_DATABASE=Initra d.o.o. + +OUI:00244D* + ID_OUI_FROM_DATABASE=Hokkaido Electronics Corporation + +OUI:002452* + ID_OUI_FROM_DATABASE=Silicon Software GmbH + +OUI:002446* + ID_OUI_FROM_DATABASE=MMB Research Inc. + +OUI:002441* + ID_OUI_FROM_DATABASE=Wanzl Metallwarenfabrik GmbH + +OUI:002368* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:00236F* + ID_OUI_FROM_DATABASE=DAQ System + +OUI:002362* + ID_OUI_FROM_DATABASE=Goldline Controls + +OUI:002361* + ID_OUI_FROM_DATABASE=Unigen Corporation + +OUI:00235C* + ID_OUI_FROM_DATABASE=Aprius, Inc. + +OUI:002355* + ID_OUI_FROM_DATABASE=Kinco Automation(Shanghai) Ltd. + +OUI:00234F* + ID_OUI_FROM_DATABASE=Luminous Power Technologies Pvt. Ltd. + +OUI:002350* + ID_OUI_FROM_DATABASE=LynTec + +OUI:002349* + ID_OUI_FROM_DATABASE=Helmholtz Centre Berlin for Material and Energy + +OUI:002244* + ID_OUI_FROM_DATABASE=Chengdu Linkon Communications Device Co., Ltd + +OUI:00224F* + ID_OUI_FROM_DATABASE=Byzoro Networks Ltd. + +OUI:002248* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:00223E* + ID_OUI_FROM_DATABASE=IRTrans GmbH + +OUI:002239* + ID_OUI_FROM_DATABASE=Indiana Life Sciences Incorporated + +OUI:002232* + ID_OUI_FROM_DATABASE=Design Design Technology Ltd + +OUI:00222C* + ID_OUI_FROM_DATABASE=Ceton Corp + +OUI:00230E* + ID_OUI_FROM_DATABASE=Gorba AG + +OUI:002307* + ID_OUI_FROM_DATABASE=FUTURE INNOVATION TECH CO.,LTD + +OUI:002302* + ID_OUI_FROM_DATABASE=Cobalt Digital, Inc. + +OUI:0022EB* + ID_OUI_FROM_DATABASE=Data Respons A/S + +OUI:0022EC* + ID_OUI_FROM_DATABASE=IDEALBT TECHNOLOGY CORPORATION + +OUI:0022F1* + ID_OUI_FROM_DATABASE=Private + +OUI:00239E* + ID_OUI_FROM_DATABASE=Jiangsu Lemote Technology Corporation Limited + +OUI:002398* + ID_OUI_FROM_DATABASE=Vutlan sro + +OUI:00238A* + ID_OUI_FROM_DATABASE=Ciena Corporation + +OUI:002384* + ID_OUI_FROM_DATABASE=GGH Engineering s.r.l. + +OUI:002342* + ID_OUI_FROM_DATABASE=Coffee Equipment Company + +OUI:002336* + ID_OUI_FROM_DATABASE=METEL s.r.o. + +OUI:00233D* + ID_OUI_FROM_DATABASE=Novero holding B.V. + +OUI:002330* + ID_OUI_FROM_DATABASE=DIZIPIA, INC. + +OUI:00232C* + ID_OUI_FROM_DATABASE=Senticare + +OUI:002320* + ID_OUI_FROM_DATABASE=Nicira Networks + +OUI:00231D* + ID_OUI_FROM_DATABASE=Deltacom Electronics Ltd + +OUI:00231E* + ID_OUI_FROM_DATABASE=Cezzer Multimedia Technologies + +OUI:0022B8* + ID_OUI_FROM_DATABASE=Norcott + +OUI:0022B7* + ID_OUI_FROM_DATABASE=GSS Grundig SAT-Systems GmbH + +OUI:0022B2* + ID_OUI_FROM_DATABASE=4RF Communications Ltd + +OUI:0022AB* + ID_OUI_FROM_DATABASE=Shenzhen Turbosight Technology Ltd + +OUI:0022A6* + ID_OUI_FROM_DATABASE=Sony Computer Entertainment America + +OUI:00229F* + ID_OUI_FROM_DATABASE=Sensys Traffic AB + +OUI:0022E5* + ID_OUI_FROM_DATABASE=Fisher-Rosemount Systems Inc. + +OUI:0022DE* + ID_OUI_FROM_DATABASE=OPPO Digital, Inc. + +OUI:0022D9* + ID_OUI_FROM_DATABASE=Fortex Industrial Ltd. + +OUI:0022D2* + ID_OUI_FROM_DATABASE=All Earth Comércio de Eletrônicos LTDA. + +OUI:0022CC* + ID_OUI_FROM_DATABASE=SciLog, Inc. + +OUI:0022C8* + ID_OUI_FROM_DATABASE=Applied Instruments B.V. + +OUI:0022BE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00228C* + ID_OUI_FROM_DATABASE=Photon Europe GmbH + +OUI:002286* + ID_OUI_FROM_DATABASE=ASTRON + +OUI:002285* + ID_OUI_FROM_DATABASE=NOMUS COMM SYSTEMS + +OUI:002280* + ID_OUI_FROM_DATABASE=A2B Electronics AB + +OUI:002276* + ID_OUI_FROM_DATABASE=Triple EYE B.V. + +OUI:00227B* + ID_OUI_FROM_DATABASE=Apogee Labs, Inc. + +OUI:002262* + ID_OUI_FROM_DATABASE=BEP Marine + +OUI:00226C* + ID_OUI_FROM_DATABASE=LinkSprite Technologies, Inc. + +OUI:00225E* + ID_OUI_FROM_DATABASE=Uwin Technologies Co.,LTD + +OUI:002258* + ID_OUI_FROM_DATABASE=Taiyo Yuden Co., Ltd. + +OUI:0023C3* + ID_OUI_FROM_DATABASE=LogMeIn, Inc. + +OUI:0023BD* + ID_OUI_FROM_DATABASE=Digital Ally, Inc. + +OUI:0023B7* + ID_OUI_FROM_DATABASE=Q-Light Co., Ltd. + +OUI:0023B1* + ID_OUI_FROM_DATABASE=Longcheer Technology (Singapore) Pte Ltd + +OUI:0023B0* + ID_OUI_FROM_DATABASE=COMXION Technology Inc. + +OUI:0023AB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0023A4* + ID_OUI_FROM_DATABASE=New Concepts Development Corp. + +OUI:001FC0* + ID_OUI_FROM_DATABASE=Control Express Finland Oy + +OUI:001FBB* + ID_OUI_FROM_DATABASE=Xenatech Co.,LTD + +OUI:001FB4* + ID_OUI_FROM_DATABASE=SmartShare Systems + +OUI:001FAD* + ID_OUI_FROM_DATABASE=Brown Innovations, Inc + +OUI:001FAF* + ID_OUI_FROM_DATABASE=NextIO, Inc. + +OUI:001FAE* + ID_OUI_FROM_DATABASE=Blick South Africa (Pty) Ltd + +OUI:001FA8* + ID_OUI_FROM_DATABASE=Smart Energy Instruments Inc. + +OUI:001FA3* + ID_OUI_FROM_DATABASE=T&W Electronics(Shenzhen)Co.,Ltd. + +OUI:002142* + ID_OUI_FROM_DATABASE=Advanced Control Systems doo + +OUI:002140* + ID_OUI_FROM_DATABASE=EN Technologies Inc. + +OUI:002138* + ID_OUI_FROM_DATABASE=Cepheid + +OUI:00212E* + ID_OUI_FROM_DATABASE=dresden-elektronik + +OUI:002128* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:002122* + ID_OUI_FROM_DATABASE=Chip-pro Ltd. + +OUI:00211B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002115* + ID_OUI_FROM_DATABASE=PHYWE Systeme GmbH & Co. KG + +OUI:002116* + ID_OUI_FROM_DATABASE=Transcon Electronic Systems, spol. s r. o. + +OUI:00210F* + ID_OUI_FROM_DATABASE=Cernium Corp + +OUI:00210B* + ID_OUI_FROM_DATABASE=GEMINI TRAZE RFID PVT. LTD. + +OUI:00210C* + ID_OUI_FROM_DATABASE=Cymtec Systems, Inc. + +OUI:001FFC* + ID_OUI_FROM_DATABASE=Riccius+Sohn GmbH + +OUI:001FF7* + ID_OUI_FROM_DATABASE=Nakajima All Precision Co., Ltd. + +OUI:00216E* + ID_OUI_FROM_DATABASE=Function ATI (Huizhou) Telecommunications Co., Ltd. + +OUI:002168* + ID_OUI_FROM_DATABASE=iVeia, LLC + +OUI:002161* + ID_OUI_FROM_DATABASE=Yournet Inc. + +OUI:002155* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00214E* + ID_OUI_FROM_DATABASE=GS Yuasa Power Supply Ltd. + +OUI:002149* + ID_OUI_FROM_DATABASE=China Daheng Group ,Inc. + +OUI:001FF0* + ID_OUI_FROM_DATABASE=Audio Partnership + +OUI:001FE9* + ID_OUI_FROM_DATABASE=Printrex, Inc. + +OUI:001FEB* + ID_OUI_FROM_DATABASE=Trio Datacom Pty Ltd + +OUI:001FEA* + ID_OUI_FROM_DATABASE=Applied Media Technologies Corporation + +OUI:001FDD* + ID_OUI_FROM_DATABASE=GDI LLC + +OUI:001FD8* + ID_OUI_FROM_DATABASE=A-TRUST COMPUTER CORPORATION + +OUI:001FD3* + ID_OUI_FROM_DATABASE=RIVA Networks Inc. + +OUI:001FCE* + ID_OUI_FROM_DATABASE=QTECH LLC + +OUI:00219D* + ID_OUI_FROM_DATABASE=Adesys BV + +OUI:0021A1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002198* + ID_OUI_FROM_DATABASE=Thai Radio Co, LTD + +OUI:002193* + ID_OUI_FROM_DATABASE=Videofon MV + +OUI:00218D* + ID_OUI_FROM_DATABASE=AP Router Ind. Eletronica LTDA + +OUI:00218E* + ID_OUI_FROM_DATABASE=MEKICS CO., LTD. + +OUI:002187* + ID_OUI_FROM_DATABASE=Imacs GmbH + +OUI:002181* + ID_OUI_FROM_DATABASE=Si2 Microsystems Limited + +OUI:00217B* + ID_OUI_FROM_DATABASE=Bastec AB + +OUI:002174* + ID_OUI_FROM_DATABASE=AvaLAN Wireless + +OUI:0021F8* + ID_OUI_FROM_DATABASE=Enseo, Inc. + +OUI:0021F3* + ID_OUI_FROM_DATABASE=Si14 SpA + +OUI:0021EC* + ID_OUI_FROM_DATABASE=Solutronic GmbH + +OUI:0021E6* + ID_OUI_FROM_DATABASE=Starlight Video Limited + +OUI:0021E0* + ID_OUI_FROM_DATABASE=CommAgility Ltd + +OUI:0021D3* + ID_OUI_FROM_DATABASE=BOCOM SECURITY(ASIA PACIFIC) LIMITED + +OUI:0021D4* + ID_OUI_FROM_DATABASE=Vollmer Werke GmbH + +OUI:0021D9* + ID_OUI_FROM_DATABASE=SEKONIC CORPORATION + +OUI:0021CD* + ID_OUI_FROM_DATABASE=LiveTV + +OUI:0021C7* + ID_OUI_FROM_DATABASE=Russound + +OUI:0021C6* + ID_OUI_FROM_DATABASE=CSJ Global, Inc. + +OUI:0021C1* + ID_OUI_FROM_DATABASE=ABB Oy / Medium Voltage Products + +OUI:0021B4* + ID_OUI_FROM_DATABASE=APRO MEDIA CO., LTD + +OUI:0021AE* + ID_OUI_FROM_DATABASE=ALCATEL-LUCENT FRANCE - WTD + +OUI:0021A2* + ID_OUI_FROM_DATABASE=EKE-Electronics Ltd. + +OUI:0021A7* + ID_OUI_FROM_DATABASE=Hantle System Co., Ltd. + +OUI:00221F* + ID_OUI_FROM_DATABASE=eSang Technologies Co., Ltd. + +OUI:002226* + ID_OUI_FROM_DATABASE=Avaak, Inc. + +OUI:00221A* + ID_OUI_FROM_DATABASE=Audio Precision + +OUI:002213* + ID_OUI_FROM_DATABASE=PCI CORPORATION + +OUI:00220D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00220C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002207* + ID_OUI_FROM_DATABASE=Inteno Broadband Technology AB + +OUI:002202* + ID_OUI_FROM_DATABASE=Excito Elektronik i Skåne AB + +OUI:0021F9* + ID_OUI_FROM_DATABASE=WIRECOM Technologies + +OUI:001F40* + ID_OUI_FROM_DATABASE=Speakercraft Inc. + +OUI:001F38* + ID_OUI_FROM_DATABASE=POSITRON + +OUI:001F3D* + ID_OUI_FROM_DATABASE=Qbit GmbH + +OUI:001F37* + ID_OUI_FROM_DATABASE=Genesis I&C + +OUI:001F2A* + ID_OUI_FROM_DATABASE=ACCM + +OUI:001F31* + ID_OUI_FROM_DATABASE=Radiocomp + +OUI:001F25* + ID_OUI_FROM_DATABASE=MBS GmbH + +OUI:001F1E* + ID_OUI_FROM_DATABASE=Astec Technology Co., Ltd + +OUI:001F17* + ID_OUI_FROM_DATABASE=IDX Company, Ltd. + +OUI:001F18* + ID_OUI_FROM_DATABASE=Hakusan.Mfg.Co,.Ltd + +OUI:001E61* + ID_OUI_FROM_DATABASE=ITEC GmbH + +OUI:001E5C* + ID_OUI_FROM_DATABASE=RB GeneralEkonomik + +OUI:001E5B* + ID_OUI_FROM_DATABASE=Unitron Company, Inc. + +OUI:001E55* + ID_OUI_FROM_DATABASE=COWON SYSTEMS,Inc. + +OUI:001E4E* + ID_OUI_FROM_DATABASE=DAKO EDV-Ingenieur- und Systemhaus GmbH + +OUI:001E49* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001E44* + ID_OUI_FROM_DATABASE=SANTEC + +OUI:001E3F* + ID_OUI_FROM_DATABASE=TrellisWare Technologies, Inc. + +OUI:001E38* + ID_OUI_FROM_DATABASE=Bluecard Software Technology Co., Ltd. + +OUI:001E31* + ID_OUI_FROM_DATABASE=INFOMARK CO.,LTD. + +OUI:001E32* + ID_OUI_FROM_DATABASE=Zensys + +OUI:001E2C* + ID_OUI_FROM_DATABASE=CyVerse Corporation + +OUI:001E20* + ID_OUI_FROM_DATABASE=Intertain Inc. + +OUI:001E19* + ID_OUI_FROM_DATABASE=GTRI + +OUI:001E0F* + ID_OUI_FROM_DATABASE=Briot International + +OUI:001EE4* + ID_OUI_FROM_DATABASE=ACS Solutions France + +OUI:001EEB* + ID_OUI_FROM_DATABASE=Talk-A-Phone Co. + +OUI:001EDF* + ID_OUI_FROM_DATABASE=Master Industrialization Center Kista + +OUI:001EDA* + ID_OUI_FROM_DATABASE=Wesemann Elektrotechniek B.V. + +OUI:001ED5* + ID_OUI_FROM_DATABASE=Tekon-Automatics + +OUI:001ECE* + ID_OUI_FROM_DATABASE=BISA Technologies (Hong Kong) Limited + +OUI:001EC8* + ID_OUI_FROM_DATABASE=Rapid Mobile (Pty) Ltd + +OUI:001EBB* + ID_OUI_FROM_DATABASE=BLUELIGHT TECHNOLOGY INC. + +OUI:001EB6* + ID_OUI_FROM_DATABASE=TAG Heuer SA + +OUI:001EB5* + ID_OUI_FROM_DATABASE=Ever Sparkle Technologies Ltd + +OUI:001EAF* + ID_OUI_FROM_DATABASE=Ophir Optronics Ltd + +OUI:001EAA* + ID_OUI_FROM_DATABASE=E-Senza Technologies GmbH + +OUI:001E9D* + ID_OUI_FROM_DATABASE=Recall Technologies, Inc. + +OUI:001E98* + ID_OUI_FROM_DATABASE=GreenLine Communications + +OUI:001E97* + ID_OUI_FROM_DATABASE=Medium Link System Technology CO., LTD, + +OUI:001E91* + ID_OUI_FROM_DATABASE=KIMIN Electronic Co., Ltd. + +OUI:001E8A* + ID_OUI_FROM_DATABASE=eCopy, Inc + +OUI:001E85* + ID_OUI_FROM_DATABASE=Lagotek Corporation + +OUI:001E78* + ID_OUI_FROM_DATABASE=Owitek Technology Ltd., + +OUI:001E6D* + ID_OUI_FROM_DATABASE=IT R&D Center + +OUI:001E6E* + ID_OUI_FROM_DATABASE=Shenzhen First Mile Communications Ltd + +OUI:001F71* + ID_OUI_FROM_DATABASE=xG Technology, Inc. + +OUI:001F72* + ID_OUI_FROM_DATABASE=QingDao Hiphone Technology Co,.Ltd + +OUI:001F76* + ID_OUI_FROM_DATABASE=AirLogic Systems Inc. + +OUI:001F6C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001F60* + ID_OUI_FROM_DATABASE=COMPASS SYSTEMS CORP. + +OUI:001F65* + ID_OUI_FROM_DATABASE=KOREA ELECTRIC TERMINAL CO., LTD. + +OUI:001F5F* + ID_OUI_FROM_DATABASE=Blatand GmbH + +OUI:001F59* + ID_OUI_FROM_DATABASE=Kronback Tracers + +OUI:001F4D* + ID_OUI_FROM_DATABASE=Segnetics LLC + +OUI:001F52* + ID_OUI_FROM_DATABASE=UVT Unternehmensberatung fur Verkehr und Technik GmbH + +OUI:001F03* + ID_OUI_FROM_DATABASE=NUM AG + +OUI:001EFE* + ID_OUI_FROM_DATABASE=LEVEL s.r.o. + +OUI:001F04* + ID_OUI_FROM_DATABASE=Granch Ltd. + +OUI:001EF2* + ID_OUI_FROM_DATABASE=Micro Motion Inc + +OUI:001EF7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001EF1* + ID_OUI_FROM_DATABASE=Servimat + +OUI:001F9E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001F92* + ID_OUI_FROM_DATABASE=VideoIQ, Inc. + +OUI:001F97* + ID_OUI_FROM_DATABASE=BERTANA srl + +OUI:001F8B* + ID_OUI_FROM_DATABASE=Cache IQ + +OUI:001F84* + ID_OUI_FROM_DATABASE=Gigle Semiconductor + +OUI:001F7F* + ID_OUI_FROM_DATABASE=Phabrix Limited + +OUI:001CFF* + ID_OUI_FROM_DATABASE=Napera Networks Inc + +OUI:001CF8* + ID_OUI_FROM_DATABASE=Parade Technologies, Ltd. + +OUI:001CF1* + ID_OUI_FROM_DATABASE=SUPoX Technology Co. , LTD. + +OUI:001CF2* + ID_OUI_FROM_DATABASE=Tenlon Technology Co.,Ltd. + +OUI:001CEC* + ID_OUI_FROM_DATABASE=Mobilesoft (Aust.) Pty Ltd + +OUI:001CE7* + ID_OUI_FROM_DATABASE=Rocon PLC Research Centre + +OUI:001CE2* + ID_OUI_FROM_DATABASE=Attero Tech, LLC. + +OUI:001CDB* + ID_OUI_FROM_DATABASE=CARPOINT CO.,LTD + +OUI:001CD5* + ID_OUI_FROM_DATABASE=ZeeVee, Inc. + +OUI:001CCF* + ID_OUI_FROM_DATABASE=LIMETEK + +OUI:001E08* + ID_OUI_FROM_DATABASE=Centec Networks Inc + +OUI:001E03* + ID_OUI_FROM_DATABASE=LiComm Co., Ltd. + +OUI:001DFC* + ID_OUI_FROM_DATABASE=KSIC + +OUI:001DF5* + ID_OUI_FROM_DATABASE=Sunshine Co,LTD + +OUI:001DF0* + ID_OUI_FROM_DATABASE=Vidient Systems, Inc. + +OUI:001DDC* + ID_OUI_FROM_DATABASE=HangZhou DeChangLong Tech&Info Co.,Ltd + +OUI:001DE4* + ID_OUI_FROM_DATABASE=Visioneered Image Systems + +OUI:001DE2* + ID_OUI_FROM_DATABASE=Radionor Communications + +OUI:001CC8* + ID_OUI_FROM_DATABASE=INDUSTRONIC Industrie-Electronic GmbH & Co. KG + +OUI:001CBC* + ID_OUI_FROM_DATABASE=CastGrabber, LLC + +OUI:001CB2* + ID_OUI_FROM_DATABASE=BPT SPA + +OUI:001CA6* + ID_OUI_FROM_DATABASE=Win4NET + +OUI:001CAB* + ID_OUI_FROM_DATABASE=Meyer Sound Laboratories, Inc. + +OUI:001CAC* + ID_OUI_FROM_DATABASE=Qniq Technology Corp. + +OUI:001CA1* + ID_OUI_FROM_DATABASE=AKAMAI TECHNOLOGIES, INC. + +OUI:001C95* + ID_OUI_FROM_DATABASE=Opticomm Corporation + +OUI:001C90* + ID_OUI_FROM_DATABASE=Empacket Corporation + +OUI:001C8F* + ID_OUI_FROM_DATABASE=Advanced Electronic Design, Inc. + +OUI:001C89* + ID_OUI_FROM_DATABASE=Force Communications, Inc. + +OUI:001C7F* + ID_OUI_FROM_DATABASE=Check Point Software Technologies + +OUI:001C75* + ID_OUI_FROM_DATABASE=Segnet Ltd. + +OUI:001C6E* + ID_OUI_FROM_DATABASE=Newbury Networks, Inc. + +OUI:001C69* + ID_OUI_FROM_DATABASE=Packet Vision Ltd + +OUI:001DA5* + ID_OUI_FROM_DATABASE=WB Electronics + +OUI:001DA6* + ID_OUI_FROM_DATABASE=Media Numerics Limited + +OUI:001DA0* + ID_OUI_FROM_DATABASE=Heng Yu Electronic Manufacturing Company Limited + +OUI:001D99* + ID_OUI_FROM_DATABASE=Cyan Optic, Inc. + +OUI:001D94* + ID_OUI_FROM_DATABASE=Climax Technology Co., Ltd + +OUI:001D93* + ID_OUI_FROM_DATABASE=Modacom + +OUI:001D8D* + ID_OUI_FROM_DATABASE=Raytek GmbH + +OUI:001D86* + ID_OUI_FROM_DATABASE=Shinwa Industries(China) Ltd. + +OUI:001DC9* + ID_OUI_FROM_DATABASE=GainSpan Corp. + +OUI:001DC2* + ID_OUI_FROM_DATABASE=XORTEC OY + +OUI:001DBD* + ID_OUI_FROM_DATABASE=Versamed Inc. + +OUI:001DB6* + ID_OUI_FROM_DATABASE=BestComm Networks, Inc. + +OUI:001DB0* + ID_OUI_FROM_DATABASE=FuJian HengTong Information Technology Co.,Ltd + +OUI:001DAC* + ID_OUI_FROM_DATABASE=Gigamon Systems LLC + +OUI:001D81* + ID_OUI_FROM_DATABASE=GUANGZHOU GATEWAY ELECTRONICS CO., LTD + +OUI:001D69* + ID_OUI_FROM_DATABASE=Knorr-Bremse IT-Services GmbH + +OUI:001D70* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D77* + ID_OUI_FROM_DATABASE=NSGate + +OUI:001D7C* + ID_OUI_FROM_DATABASE=ABE Elettronica S.p.A. + +OUI:001D64* + ID_OUI_FROM_DATABASE=Adam Communications Systems Int Ltd + +OUI:001D5D* + ID_OUI_FROM_DATABASE=Control Dynamics Pty. Ltd. + +OUI:001D2E* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:001D21* + ID_OUI_FROM_DATABASE=Alcad SL + +OUI:001D1C* + ID_OUI_FROM_DATABASE=Gennet s.a. + +OUI:001D17* + ID_OUI_FROM_DATABASE=Digital Sky Corporation + +OUI:001D12* + ID_OUI_FROM_DATABASE=ROHM CO., LTD. + +OUI:001D11* + ID_OUI_FROM_DATABASE=Analogue & Micro Ltd + +OUI:001D0B* + ID_OUI_FROM_DATABASE=Power Standards Lab + +OUI:001D04* + ID_OUI_FROM_DATABASE=Zipit Wireless, Inc. + +OUI:001D58* + ID_OUI_FROM_DATABASE=CQ Inc + +OUI:001D57* + ID_OUI_FROM_DATABASE=CAETEC Messtechnik + +OUI:001D51* + ID_OUI_FROM_DATABASE=Babcock & Wilcox Power Generation Group, Inc + +OUI:001D47* + ID_OUI_FROM_DATABASE=Covote GmbH & Co KG + +OUI:001D40* + ID_OUI_FROM_DATABASE=Intel – GE Care Innovations LLC + +OUI:001D34* + ID_OUI_FROM_DATABASE=SYRIS Technology Corp + +OUI:001D2D* + ID_OUI_FROM_DATABASE=Pylone, Inc. + +OUI:001B2A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B1D* + ID_OUI_FROM_DATABASE=Phoenix International Co., Ltd + +OUI:001B22* + ID_OUI_FROM_DATABASE=Palit Microsystems ( H.K.) Ltd. + +OUI:001B1B* + ID_OUI_FROM_DATABASE=Siemens AG, + +OUI:001B16* + ID_OUI_FROM_DATABASE=Celtro Ltd. + +OUI:001B0A* + ID_OUI_FROM_DATABASE=Intelligent Distributed Controls Ltd + +OUI:001B0F* + ID_OUI_FROM_DATABASE=Petratec + +OUI:001AFE* + ID_OUI_FROM_DATABASE=SOFACREAL + +OUI:001B03* + ID_OUI_FROM_DATABASE=Action Technology (SZ) Co., Ltd + +OUI:001B68* + ID_OUI_FROM_DATABASE=Modnnet Co., Ltd + +OUI:001B62* + ID_OUI_FROM_DATABASE=JHT Optoelectronics Co.,Ltd. + +OUI:001B61* + ID_OUI_FROM_DATABASE=Digital Acoustics, LLC + +OUI:001B5C* + ID_OUI_FROM_DATABASE=Azuretec Co., Ltd. + +OUI:001B55* + ID_OUI_FROM_DATABASE=Hurco Automation Ltd. + +OUI:001B50* + ID_OUI_FROM_DATABASE=Nizhny Novgorod Factory named after M.Frunze, FSUE (NZiF) + +OUI:001B44* + ID_OUI_FROM_DATABASE=SanDisk Corporation + +OUI:001B49* + ID_OUI_FROM_DATABASE=Roberts Radio limited + +OUI:001B42* + ID_OUI_FROM_DATABASE=Wise & Blue + +OUI:001B3D* + ID_OUI_FROM_DATABASE=EuroTel Spa + +OUI:001B36* + ID_OUI_FROM_DATABASE=Tsubata Engineering Co.,Ltd. (Head Office) + +OUI:001B31* + ID_OUI_FROM_DATABASE=Neural Image. Co. Ltd. + +OUI:001C56* + ID_OUI_FROM_DATABASE=Pado Systems, Inc. + +OUI:001C5B* + ID_OUI_FROM_DATABASE=Chubb Electronic Security Systems Ltd + +OUI:001C5D* + ID_OUI_FROM_DATABASE=Leica Microsystems + +OUI:001C5C* + ID_OUI_FROM_DATABASE=Integrated Medical Systems, Inc. + +OUI:001C51* + ID_OUI_FROM_DATABASE=Celeno Communications + +OUI:001C52* + ID_OUI_FROM_DATABASE=VISIONEE SRL + +OUI:001C45* + ID_OUI_FROM_DATABASE=Chenbro Micom Co., Ltd. + +OUI:001C4C* + ID_OUI_FROM_DATABASE=Petrotest Instruments + +OUI:001C39* + ID_OUI_FROM_DATABASE=S Netsystems Inc. + +OUI:001C40* + ID_OUI_FROM_DATABASE=VDG-Security bv + +OUI:001C32* + ID_OUI_FROM_DATABASE=Telian Corporation + +OUI:001AC7* + ID_OUI_FROM_DATABASE=UNIPOINT + +OUI:001AC2* + ID_OUI_FROM_DATABASE=YEC Co.,Ltd. + +OUI:001AB8* + ID_OUI_FROM_DATABASE=Anseri Corporation + +OUI:001ABD* + ID_OUI_FROM_DATABASE=Impatica Inc. + +OUI:001AB1* + ID_OUI_FROM_DATABASE=Asia Pacific Satellite Industries Co., Ltd. + +OUI:001B8C* + ID_OUI_FROM_DATABASE=JMicron Technology Corp. + +OUI:001B91* + ID_OUI_FROM_DATABASE=EFKON AG + +OUI:001B87* + ID_OUI_FROM_DATABASE=Deepsound Tech. Co., Ltd + +OUI:001B82* + ID_OUI_FROM_DATABASE=Taiwan Semiconductor Co., Ltd. + +OUI:001B7B* + ID_OUI_FROM_DATABASE=The Tintometer Ltd + +OUI:001B74* + ID_OUI_FROM_DATABASE=MiraLink Corporation + +OUI:001B6F* + ID_OUI_FROM_DATABASE=Teletrak Ltd + +OUI:001AFC* + ID_OUI_FROM_DATABASE=ModusLink Corporation + +OUI:001AF2* + ID_OUI_FROM_DATABASE=Dynavisions Schweiz AG + +OUI:001AF7* + ID_OUI_FROM_DATABASE=dataschalt e+a GmbH + +OUI:001AED* + ID_OUI_FROM_DATABASE=INCOTEC GmbH + +OUI:001ADF* + ID_OUI_FROM_DATABASE=Interactivetv Pty Limited + +OUI:001AE1* + ID_OUI_FROM_DATABASE=EDGE ACCESS INC + +OUI:001AE6* + ID_OUI_FROM_DATABASE=Atlanta Advanced Communications Holdings Limited + +OUI:001AD3* + ID_OUI_FROM_DATABASE=Vamp Ltd. + +OUI:001ADA* + ID_OUI_FROM_DATABASE=Biz-2-Me Inc. + +OUI:001ACE* + ID_OUI_FROM_DATABASE=YUPITERU CORPORATION + +OUI:001BC8* + ID_OUI_FROM_DATABASE=MIURA CO.,LTD + +OUI:001BC1* + ID_OUI_FROM_DATABASE=HOLUX Technology, Inc. + +OUI:001BB7* + ID_OUI_FROM_DATABASE=Alta Heights Technology Corp. + +OUI:001BAB* + ID_OUI_FROM_DATABASE=Telchemy, Incorporated + +OUI:001BB0* + ID_OUI_FROM_DATABASE=BHARAT ELECTRONICS + +OUI:001BA4* + ID_OUI_FROM_DATABASE=S.A.E Afikim + +OUI:001B9F* + ID_OUI_FROM_DATABASE=Calyptech Pty Ltd + +OUI:001B9D* + ID_OUI_FROM_DATABASE=Novus Security Sp. z o.o. + +OUI:001BF6* + ID_OUI_FROM_DATABASE=CONWISE Technology Corporation Ltd. + +OUI:001BF1* + ID_OUI_FROM_DATABASE=Nanjing SilverNet Software Co., Ltd. + +OUI:001BEC* + ID_OUI_FROM_DATABASE=Netio Technologies Co., Ltd + +OUI:001BE7* + ID_OUI_FROM_DATABASE=Postek Electronics Co., Ltd. + +OUI:001BE0* + ID_OUI_FROM_DATABASE=TELENOT ELECTRONIC GmbH + +OUI:001BD9* + ID_OUI_FROM_DATABASE=Edgewater Computer Systems + +OUI:001BDB* + ID_OUI_FROM_DATABASE=Valeo VECS + +OUI:001BD4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001BCD* + ID_OUI_FROM_DATABASE=DAVISCOMMS (S) PTE LTD + +OUI:001C2D* + ID_OUI_FROM_DATABASE=FlexRadio Systems + +OUI:001C1C* + ID_OUI_FROM_DATABASE=Center Communication Systems GmbH + +OUI:001C21* + ID_OUI_FROM_DATABASE=Nucsafe Inc. + +OUI:001C20* + ID_OUI_FROM_DATABASE=CLB Benelux + +OUI:001C15* + ID_OUI_FROM_DATABASE=iPhotonix LLC + +OUI:001C16* + ID_OUI_FROM_DATABASE=ThyssenKrupp Elevator + +OUI:001C10* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:001C09* + ID_OUI_FROM_DATABASE=SAE Electronic Co.,Ltd. + +OUI:001C04* + ID_OUI_FROM_DATABASE=Airgain, Inc. + +OUI:001BFD* + ID_OUI_FROM_DATABASE=Dignsys Inc. + +OUI:00192B* + ID_OUI_FROM_DATABASE=Aclara RF Systems Inc. + +OUI:001930* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00191F* + ID_OUI_FROM_DATABASE=Microlink communications Inc. + +OUI:001924* + ID_OUI_FROM_DATABASE=LBNL Engineering + +OUI:001911* + ID_OUI_FROM_DATABASE=Just In Mobile Information Technologies (Shanghai) Co., Ltd. + +OUI:001918* + ID_OUI_FROM_DATABASE=Interactive Wear AG + +OUI:00190C* + ID_OUI_FROM_DATABASE=Encore Electronics, Inc. + +OUI:001900* + ID_OUI_FROM_DATABASE=Intelliverese - DBA Voicecom + +OUI:001905* + ID_OUI_FROM_DATABASE=SCHRACK Seconet AG + +OUI:0018F4* + ID_OUI_FROM_DATABASE=EO TECHNICS Co., Ltd. + +OUI:0018F6* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:0018FB* + ID_OUI_FROM_DATABASE=Compro Technology + +OUI:0019EE* + ID_OUI_FROM_DATABASE=CARLO GAVAZZI CONTROLS SPA-Controls Division + +OUI:0019F0* + ID_OUI_FROM_DATABASE=UNIONMAN TECHNOLOGY CO.,LTD + +OUI:0019F5* + ID_OUI_FROM_DATABASE=Imagination Technologies Ltd + +OUI:0019E9* + ID_OUI_FROM_DATABASE=S-Information Technolgy, Co., Ltd. + +OUI:0019DB* + ID_OUI_FROM_DATABASE=MICRO-STAR INTERNATIONAL CO., LTD. + +OUI:0019DD* + ID_OUI_FROM_DATABASE=FEI-Zyfer, Inc. + +OUI:0019CA* + ID_OUI_FROM_DATABASE=Broadata Communications, Inc + +OUI:0019CF* + ID_OUI_FROM_DATABASE=SALICRU, S.A. + +OUI:0019D6* + ID_OUI_FROM_DATABASE=LS Cable and System Ltd. + +OUI:0019B4* + ID_OUI_FROM_DATABASE=Intellio Ltd + +OUI:001A6E* + ID_OUI_FROM_DATABASE=Impro Technologies + +OUI:001A67* + ID_OUI_FROM_DATABASE=Infinite QL Sdn Bhd + +OUI:001A69* + ID_OUI_FROM_DATABASE=Wuhan Yangtze Optical Technology CO.,Ltd. + +OUI:001A62* + ID_OUI_FROM_DATABASE=Data Robotics, Incorporated + +OUI:001A58* + ID_OUI_FROM_DATABASE=CCV Deutschland GmbH - Celectronic eHealth Div. + +OUI:001A5D* + ID_OUI_FROM_DATABASE=Mobinnova Corp. + +OUI:001A4C* + ID_OUI_FROM_DATABASE=Crossbow Technology, Inc + +OUI:001A51* + ID_OUI_FROM_DATABASE=Alfred Mann Foundation + +OUI:001AAA* + ID_OUI_FROM_DATABASE=Analogic Corp. + +OUI:001AA1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A9C* + ID_OUI_FROM_DATABASE=RightHand Technologies, Inc. + +OUI:001A8B* + ID_OUI_FROM_DATABASE=CHUNIL ELECTRIC IND., CO. + +OUI:001A95* + ID_OUI_FROM_DATABASE=Hisense Mobile Communications Technoligy Co.,Ltd. + +OUI:001A84* + ID_OUI_FROM_DATABASE=V One Multimedia Pte Ltd + +OUI:0019A1* + ID_OUI_FROM_DATABASE=LG INFORMATION & COMM. + +OUI:0019AD* + ID_OUI_FROM_DATABASE=BOBST SA + +OUI:0019B2* + ID_OUI_FROM_DATABASE=XYnetsoft Co.,Ltd + +OUI:00199A* + ID_OUI_FROM_DATABASE=EDO-EVI + +OUI:00199F* + ID_OUI_FROM_DATABASE=DKT A/S + +OUI:001995* + ID_OUI_FROM_DATABASE=Jurong Hi-Tech (Suzhou)Co.ltd + +OUI:001990* + ID_OUI_FROM_DATABASE=ELM DATA Co., Ltd. + +OUI:001989* + ID_OUI_FROM_DATABASE=Sonitrol Corporation + +OUI:001A3E* + ID_OUI_FROM_DATABASE=Faster Technology LLC + +OUI:001A40* + ID_OUI_FROM_DATABASE=A-FOUR TECH CO., LTD. + +OUI:001A2D* + ID_OUI_FROM_DATABASE=The Navvo Group + +OUI:001A32* + ID_OUI_FROM_DATABASE=ACTIVA MULTIMEDIA + +OUI:001A28* + ID_OUI_FROM_DATABASE=ASWT Co., LTD. Taiwan Branch H.K. + +OUI:001A1C* + ID_OUI_FROM_DATABASE=GT&T Engineering Pte Ltd + +OUI:001A23* + ID_OUI_FROM_DATABASE=Ice Qube, Inc + +OUI:001A15* + ID_OUI_FROM_DATABASE=gemalto e-Payment + +OUI:001A10* + ID_OUI_FROM_DATABASE=LUCENT TRANS ELECTRONICS CO.,LTD + +OUI:001A09* + ID_OUI_FROM_DATABASE=Wayfarer Transit Systems Ltd + +OUI:001A02* + ID_OUI_FROM_DATABASE=SECURE CARE PRODUCTS, INC + +OUI:001A04* + ID_OUI_FROM_DATABASE=Interay Solutions BV + +OUI:001984* + ID_OUI_FROM_DATABASE=ESTIC Corporation + +OUI:001976* + ID_OUI_FROM_DATABASE=Xipher Technologies, LLC + +OUI:001978* + ID_OUI_FROM_DATABASE=Datum Systems, Inc. + +OUI:00196A* + ID_OUI_FROM_DATABASE=MikroM GmbH + +OUI:001971* + ID_OUI_FROM_DATABASE=Guangzhou Unicomp Technology Co.,Ltd + +OUI:001965* + ID_OUI_FROM_DATABASE=YuHua TelTech (ShangHai) Co., Ltd. + +OUI:001960* + ID_OUI_FROM_DATABASE=DoCoMo Systems, Inc. + +OUI:001954* + ID_OUI_FROM_DATABASE=Leaf Corporation. + +OUI:001959* + ID_OUI_FROM_DATABASE=Staccato Communications Inc. + +OUI:00194D* + ID_OUI_FROM_DATABASE=Avago Technologies Sdn Bhd + +OUI:001948* + ID_OUI_FROM_DATABASE=AireSpider Networks + +OUI:001941* + ID_OUI_FROM_DATABASE=Pitney Bowes, Inc + +OUI:001935* + ID_OUI_FROM_DATABASE=DUERR DENTAL AG + +OUI:00193A* + ID_OUI_FROM_DATABASE=OESOLUTIONS + +OUI:00193C* + ID_OUI_FROM_DATABASE=HighPoint Technologies Incorporated + +OUI:001773* + ID_OUI_FROM_DATABASE=Laketune Technologies Co. Ltd + +OUI:001778* + ID_OUI_FROM_DATABASE=Central Music Co. + +OUI:00177A* + ID_OUI_FROM_DATABASE=ASSA ABLOY AB + +OUI:00176F* + ID_OUI_FROM_DATABASE=PAX Computer Technology(Shenzhen) Ltd. + +OUI:00176A* + ID_OUI_FROM_DATABASE=Avago Technologies + +OUI:001763* + ID_OUI_FROM_DATABASE=Essentia S.p.A. + +OUI:00175E* + ID_OUI_FROM_DATABASE=Zed-3 + +OUI:001750* + ID_OUI_FROM_DATABASE=GSI Group, MicroE Systems + +OUI:001752* + ID_OUI_FROM_DATABASE=DAGS, Inc + +OUI:001757* + ID_OUI_FROM_DATABASE=RIX TECHNOLOGY LIMITED + +OUI:00183D* + ID_OUI_FROM_DATABASE=Vertex Link Corporation + +OUI:001844* + ID_OUI_FROM_DATABASE=Heads Up Technologies, Inc. + +OUI:001838* + ID_OUI_FROM_DATABASE=PanAccess Communications,Inc. + +OUI:001827* + ID_OUI_FROM_DATABASE=NEC UNIFIED SOLUTIONS NEDERLAND B.V. + +OUI:00182C* + ID_OUI_FROM_DATABASE=Ascend Networks, Inc. + +OUI:00181B* + ID_OUI_FROM_DATABASE=TaiJin Metal Co., Ltd. + +OUI:001814* + ID_OUI_FROM_DATABASE=Mitutoyo Corporation + +OUI:001819* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001820* + ID_OUI_FROM_DATABASE=w5networks + +OUI:001808* + ID_OUI_FROM_DATABASE=SightLogix, Inc. + +OUI:00180D* + ID_OUI_FROM_DATABASE=Terabytes Server Storage Tech Corp + +OUI:001803* + ID_OUI_FROM_DATABASE=ArcSoft Shanghai Co. LTD + +OUI:0017F0* + ID_OUI_FROM_DATABASE=SZCOM Broadband Network Technology Co.,Ltd + +OUI:0017F7* + ID_OUI_FROM_DATABASE=CEM Solutions Pvt Ltd + +OUI:0017FE* + ID_OUI_FROM_DATABASE=TALOS SYSTEM INC. + +OUI:0017D8* + ID_OUI_FROM_DATABASE=Magnum Semiconductor, Inc. + +OUI:0017DD* + ID_OUI_FROM_DATABASE=Clipsal Australia + +OUI:0017DF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018C6* + ID_OUI_FROM_DATABASE=OPW Fuel Management Systems + +OUI:0018CB* + ID_OUI_FROM_DATABASE=Tecobest Technology Limited + +OUI:0018BF* + ID_OUI_FROM_DATABASE=Essence Technology Solution, Inc. + +OUI:0018BA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018B8* + ID_OUI_FROM_DATABASE=New Voice International AG + +OUI:0018B3* + ID_OUI_FROM_DATABASE=TEC WizHome Co., Ltd. + +OUI:0018AC* + ID_OUI_FROM_DATABASE=Shanghai Jiao Da HISYS Technology Co. Ltd. + +OUI:0018A5* + ID_OUI_FROM_DATABASE=ADigit Technologies Corp. + +OUI:0018A7* + ID_OUI_FROM_DATABASE=Yoggie Security Systems LTD. + +OUI:001896* + ID_OUI_FROM_DATABASE=Great Well Electronic LTD + +OUI:00189B* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:00179E* + ID_OUI_FROM_DATABASE=Sirit Inc + +OUI:0017A3* + ID_OUI_FROM_DATABASE=MIX s.r.l. + +OUI:0017A8* + ID_OUI_FROM_DATABASE=EDM Corporation + +OUI:001792* + ID_OUI_FROM_DATABASE=Falcom Wireless Comunications Gmbh + +OUI:001797* + ID_OUI_FROM_DATABASE=Telsy Elettronica S.p.A. + +OUI:001799* + ID_OUI_FROM_DATABASE=SmarTire Systems Inc. + +OUI:00178B* + ID_OUI_FROM_DATABASE=Teledyne Technologies Incorporated + +OUI:00177F* + ID_OUI_FROM_DATABASE=Worldsmart Retech + +OUI:001786* + ID_OUI_FROM_DATABASE=wisembed + +OUI:001877* + ID_OUI_FROM_DATABASE=Amplex A/S + +OUI:00186B* + ID_OUI_FROM_DATABASE=Sambu Communics CO., LTD. + +OUI:001870* + ID_OUI_FROM_DATABASE=E28 Shanghai Limited + +OUI:001863* + ID_OUI_FROM_DATABASE=Veritech Electronics Limited + +OUI:001850* + ID_OUI_FROM_DATABASE=Secfone Kft + +OUI:001855* + ID_OUI_FROM_DATABASE=Aeromaritime Systembau GmbH + +OUI:001857* + ID_OUI_FROM_DATABASE=Unilever R&D + +OUI:001849* + ID_OUI_FROM_DATABASE=Pigeon Point Systems LLC + +OUI:0017C7* + ID_OUI_FROM_DATABASE=MARA Systems Consulting AB + +OUI:0017CE* + ID_OUI_FROM_DATABASE=Screen Service Spa + +OUI:0017D3* + ID_OUI_FROM_DATABASE=Etymotic Research, Inc. + +OUI:0017BB* + ID_OUI_FROM_DATABASE=Syrinx Industrial Electronics + +OUI:0017B4* + ID_OUI_FROM_DATABASE=Remote Security Systems, LLC + +OUI:0017B6* + ID_OUI_FROM_DATABASE=Aquantia + +OUI:0017AF* + ID_OUI_FROM_DATABASE=Enermet + +OUI:0018E8* + ID_OUI_FROM_DATABASE=Hacetron Corporation + +OUI:0018EF* + ID_OUI_FROM_DATABASE=Escape Communications, Inc. + +OUI:0018E3* + ID_OUI_FROM_DATABASE=Visualgate Systems, Inc. + +OUI:0018DC* + ID_OUI_FROM_DATABASE=Prostar Co., Ltd. + +OUI:0018E1* + ID_OUI_FROM_DATABASE=Verkerk Service Systemen + +OUI:0018D0* + ID_OUI_FROM_DATABASE=AtRoad, A Trimble Company + +OUI:0018D5* + ID_OUI_FROM_DATABASE=REIGNCOM + +OUI:0018A0* + ID_OUI_FROM_DATABASE=Cierma Ascenseurs + +OUI:001883* + ID_OUI_FROM_DATABASE=FORMOSA21 INC. + +OUI:00188A* + ID_OUI_FROM_DATABASE=Infinova LLC + +OUI:00188F* + ID_OUI_FROM_DATABASE=Montgomery Technology, Inc. + +OUI:00187C* + ID_OUI_FROM_DATABASE=INTERCROSS, LLC + +OUI:00187E* + ID_OUI_FROM_DATABASE=RGB Spectrum + +OUI:00164A* + ID_OUI_FROM_DATABASE=Vibration Technology Limited + +OUI:001644* + ID_OUI_FROM_DATABASE=LITE-ON Technology Corp. + +OUI:001645* + ID_OUI_FROM_DATABASE=Power Distribution, Inc. + +OUI:00163B* + ID_OUI_FROM_DATABASE=VertexRSI/General Dynamics + +OUI:001640* + ID_OUI_FROM_DATABASE=Asmobile Communication Inc. + +OUI:00163A* + ID_OUI_FROM_DATABASE=YVES TECHNOLOGY CO., LTD. + +OUI:001634* + ID_OUI_FROM_DATABASE=Mathtech, Inc. + +OUI:00162D* + ID_OUI_FROM_DATABASE=STNet Co., Ltd. + +OUI:001621* + ID_OUI_FROM_DATABASE=Colorado Vnet + +OUI:00161A* + ID_OUI_FROM_DATABASE=Dametric AB + +OUI:001615* + ID_OUI_FROM_DATABASE=Nittan Company, Limited + +OUI:0016C4* + ID_OUI_FROM_DATABASE=SiRF Technology, Inc. + +OUI:0016C6* + ID_OUI_FROM_DATABASE=North Atlantic Industries + +OUI:0016D2* + ID_OUI_FROM_DATABASE=Caspian + +OUI:0016BF* + ID_OUI_FROM_DATABASE=PaloDEx Group Oy + +OUI:0016B3* + ID_OUI_FROM_DATABASE=Photonicbridges (China) Co., Ltd. + +OUI:0016AC* + ID_OUI_FROM_DATABASE=Toho Technology Corp. + +OUI:0016B1* + ID_OUI_FROM_DATABASE=KBS + +OUI:0016A7* + ID_OUI_FROM_DATABASE=AWETA G&P + +OUI:001724* + ID_OUI_FROM_DATABASE=Studer Professional Audio GmbH + +OUI:001718* + ID_OUI_FROM_DATABASE=Vansco Electronics Oy + +OUI:00171D* + ID_OUI_FROM_DATABASE=DIGIT + +OUI:001711* + ID_OUI_FROM_DATABASE=GE Healthcare Bio-Sciences AB + +OUI:00170C* + ID_OUI_FROM_DATABASE=Twig Com Ltd. + +OUI:001707* + ID_OUI_FROM_DATABASE=InGrid, Inc + +OUI:001702* + ID_OUI_FROM_DATABASE=Osung Midicom Co., Ltd + +OUI:001744* + ID_OUI_FROM_DATABASE=Araneo Ltd. + +OUI:00173C* + ID_OUI_FROM_DATABASE=Extreme Engineering Solutions + +OUI:001737* + ID_OUI_FROM_DATABASE=Industrie Dial Face S.p.A. + +OUI:00172B* + ID_OUI_FROM_DATABASE=Global Technologies Inc. + +OUI:001730* + ID_OUI_FROM_DATABASE=Automation Electronics + +OUI:001729* + ID_OUI_FROM_DATABASE=Ubicod Co.LTD + +OUI:00169B* + ID_OUI_FROM_DATABASE=Alstom Transport + +OUI:0016A2* + ID_OUI_FROM_DATABASE=CentraLite Systems, Inc. + +OUI:001696* + ID_OUI_FROM_DATABASE=QDI Technology (H.K.) Limited + +OUI:001688* + ID_OUI_FROM_DATABASE=ServerEngines LLC + +OUI:00168A* + ID_OUI_FROM_DATABASE=id-Confirm Inc + +OUI:001683* + ID_OUI_FROM_DATABASE=WEBIO International Co.,.Ltd. + +OUI:00167C* + ID_OUI_FROM_DATABASE=iRex Technologies BV + +OUI:001610* + ID_OUI_FROM_DATABASE=Carina Technology + +OUI:00160B* + ID_OUI_FROM_DATABASE=TVWorks LLC + +OUI:001604* + ID_OUI_FROM_DATABASE=Sigpro + +OUI:0015FE* + ID_OUI_FROM_DATABASE=SCHILLING ROBOTICS LLC + +OUI:0015FD* + ID_OUI_FROM_DATABASE=Complete Media Systems + +OUI:0015F8* + ID_OUI_FROM_DATABASE=Kingtronics Industrial Co. Ltd. + +OUI:0015EC* + ID_OUI_FROM_DATABASE=Boca Devices LLC + +OUI:0015F1* + ID_OUI_FROM_DATABASE=KYLINK Communications Corp. + +OUI:001677* + ID_OUI_FROM_DATABASE=Bihl + Wiedemann GmbH + +OUI:001670* + ID_OUI_FROM_DATABASE=SKNET Corporation + +OUI:001664* + ID_OUI_FROM_DATABASE=Prod-El SpA + +OUI:001669* + ID_OUI_FROM_DATABASE=MRV Communication (Networks) LTD + +OUI:00165D* + ID_OUI_FROM_DATABASE=AirDefense, Inc. + +OUI:001651* + ID_OUI_FROM_DATABASE=Exeo Systems + +OUI:0015E5* + ID_OUI_FROM_DATABASE=Cheertek Inc. + +OUI:0015DB* + ID_OUI_FROM_DATABASE=Canesta Inc. + +OUI:0015D4* + ID_OUI_FROM_DATABASE=Emitor AB + +OUI:0015C8* + ID_OUI_FROM_DATABASE=FlexiPanel Ltd + +OUI:0015C3* + ID_OUI_FROM_DATABASE=Ruf Telematik AG + +OUI:0015C2* + ID_OUI_FROM_DATABASE=3M Germany + +OUI:0015BE* + ID_OUI_FROM_DATABASE=Iqua Ltd. + +OUI:0016EF* + ID_OUI_FROM_DATABASE=Koko Fitness, Inc. + +OUI:0016F4* + ID_OUI_FROM_DATABASE=Eidicom Co., Ltd. + +OUI:0016E8* + ID_OUI_FROM_DATABASE=Sigma Designs, Inc. + +OUI:0016ED* + ID_OUI_FROM_DATABASE=Digital Safety Technologies, Inc + +OUI:0016DC* + ID_OUI_FROM_DATABASE=ARCHOS + +OUI:0016E1* + ID_OUI_FROM_DATABASE=SiliconStor, Inc. + +OUI:0016D7* + ID_OUI_FROM_DATABASE=Sunways AG + +OUI:0014CB* + ID_OUI_FROM_DATABASE=LifeSync Corporation + +OUI:0014D0* + ID_OUI_FROM_DATABASE=BTI Systems Inc. + +OUI:0014C4* + ID_OUI_FROM_DATABASE=Vitelcom Mobile Technology + +OUI:0014BE* + ID_OUI_FROM_DATABASE=Wink communication technology CO.LTD + +OUI:0014BD* + ID_OUI_FROM_DATABASE=incNETWORKS, Inc + +OUI:0014B8* + ID_OUI_FROM_DATABASE=Hill-Rom + +OUI:0014AE* + ID_OUI_FROM_DATABASE=Wizlogics Co., Ltd. + +OUI:0014B3* + ID_OUI_FROM_DATABASE=CoreStar International Corp + +OUI:00149B* + ID_OUI_FROM_DATABASE=Nokota Communications, LLC + +OUI:00143F* + ID_OUI_FROM_DATABASE=Hotway Technology Corporation + +OUI:001431* + ID_OUI_FROM_DATABASE=PDL Electronics Ltd + +OUI:001433* + ID_OUI_FROM_DATABASE=Empower Technologies(Canada) Inc. + +OUI:001432* + ID_OUI_FROM_DATABASE=Tarallax Wireless, Inc. + +OUI:00142C* + ID_OUI_FROM_DATABASE=Koncept International, Inc. + +OUI:001425* + ID_OUI_FROM_DATABASE=Galactic Computing Corp. + +OUI:001420* + ID_OUI_FROM_DATABASE=G-Links networking company + +OUI:00141B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00146D* + ID_OUI_FROM_DATABASE=RF Technologies + +OUI:00146F* + ID_OUI_FROM_DATABASE=Kohler Co + +OUI:00146E* + ID_OUI_FROM_DATABASE=H. Stoll GmbH & Co. KG + +OUI:001468* + ID_OUI_FROM_DATABASE=CelPlan International, Inc. + +OUI:001461* + ID_OUI_FROM_DATABASE=CORONA CORPORATION + +OUI:00145C* + ID_OUI_FROM_DATABASE=Intronics B.V. + +OUI:001455* + ID_OUI_FROM_DATABASE=Coder Electronics Corporation + +OUI:001444* + ID_OUI_FROM_DATABASE=Grundfos Holding + +OUI:00144B* + ID_OUI_FROM_DATABASE=Hifn, Inc. + +OUI:001589* + ID_OUI_FROM_DATABASE=D-MAX Technology Co.,Ltd + +OUI:001582* + ID_OUI_FROM_DATABASE=Pulse Eight Limited + +OUI:00157C* + ID_OUI_FROM_DATABASE=Dave Networks, Inc. + +OUI:001578* + ID_OUI_FROM_DATABASE=Audio / Video Innovations + +OUI:001573* + ID_OUI_FROM_DATABASE=NewSoft Technology Corporation + +OUI:00156C* + ID_OUI_FROM_DATABASE=SANE SYSTEM CO., LTD + +OUI:001571* + ID_OUI_FROM_DATABASE=Nolan Systems + +OUI:001572* + ID_OUI_FROM_DATABASE=Red-Lemon + +OUI:001565* + ID_OUI_FROM_DATABASE=XIAMEN YEALINK NETWORK TECHNOLOGY CO.,LTD + +OUI:001559* + ID_OUI_FROM_DATABASE=Securaplane Technologies, Inc. + +OUI:0014A2* + ID_OUI_FROM_DATABASE=Core Micro Systems Inc. + +OUI:001494* + ID_OUI_FROM_DATABASE=ESU AG + +OUI:00148F* + ID_OUI_FROM_DATABASE=Protronic (Far East) Ltd. + +OUI:001488* + ID_OUI_FROM_DATABASE=Akorri + +OUI:001483* + ID_OUI_FROM_DATABASE=eXS Inc. + +OUI:001480* + ID_OUI_FROM_DATABASE=Hitachi-LG Data Storage Korea, Inc + +OUI:00147B* + ID_OUI_FROM_DATABASE=Iteris, Inc. + +OUI:001474* + ID_OUI_FROM_DATABASE=K40 Electronics + +OUI:0015B8* + ID_OUI_FROM_DATABASE=Tahoe + +OUI:0015B2* + ID_OUI_FROM_DATABASE=Advanced Industrial Computer, Inc. + +OUI:0015AE* + ID_OUI_FROM_DATABASE=kyung il + +OUI:0015AD* + ID_OUI_FROM_DATABASE=Accedian Networks + +OUI:00E0A8* + ID_OUI_FROM_DATABASE=SAT GmbH & Co. + +OUI:0015A1* + ID_OUI_FROM_DATABASE=ECA-SINTERS + +OUI:00159C* + ID_OUI_FROM_DATABASE=B-KYUNG SYSTEM Co.,Ltd. + +OUI:001595* + ID_OUI_FROM_DATABASE=Quester Tangent Corporation + +OUI:00158E* + ID_OUI_FROM_DATABASE=Plustek.INC + +OUI:001552* + ID_OUI_FROM_DATABASE=Wi-Gear Inc. + +OUI:001548* + ID_OUI_FROM_DATABASE=CUBE TECHNOLOGIES + +OUI:00154D* + ID_OUI_FROM_DATABASE=Netronome Systems, Inc. + +OUI:00153C* + ID_OUI_FROM_DATABASE=Kprotech Co., Ltd. + +OUI:001543* + ID_OUI_FROM_DATABASE=Aberdeen Test Center + +OUI:001535* + ID_OUI_FROM_DATABASE=OTE Spa + +OUI:001537* + ID_OUI_FROM_DATABASE=Ventus Networks + +OUI:001536* + ID_OUI_FROM_DATABASE=Powertech co.,Ltd + +OUI:001530* + ID_OUI_FROM_DATABASE=EMC Corporation + +OUI:001529* + ID_OUI_FROM_DATABASE=N3 Corporation + +OUI:0014F9* + ID_OUI_FROM_DATABASE=Vantage Controls + +OUI:0014FB* + ID_OUI_FROM_DATABASE=Technical Solutions Inc. + +OUI:0014FA* + ID_OUI_FROM_DATABASE=AsGa S.A. + +OUI:0014F4* + ID_OUI_FROM_DATABASE=DekTec Digital Video B.V. + +OUI:0014ED* + ID_OUI_FROM_DATABASE=Airak, Inc. + +OUI:0014DE* + ID_OUI_FROM_DATABASE=Sage Instruments Inc. + +OUI:0014E3* + ID_OUI_FROM_DATABASE=mm-lab GmbH + +OUI:0014D7* + ID_OUI_FROM_DATABASE=Datastore Technology Corp + +OUI:001524* + ID_OUI_FROM_DATABASE=Numatics, Inc. + +OUI:00151D* + ID_OUI_FROM_DATABASE=M2I CORPORATION + +OUI:001513* + ID_OUI_FROM_DATABASE=EFS sas + +OUI:001507* + ID_OUI_FROM_DATABASE=Renaissance Learning Inc + +OUI:00129E* + ID_OUI_FROM_DATABASE=Surf Communications Inc. + +OUI:001297* + ID_OUI_FROM_DATABASE=O2Micro, Inc. + +OUI:001298* + ID_OUI_FROM_DATABASE=MICO ELECTRIC(SHENZHEN) LIMITED + +OUI:00128D* + ID_OUI_FROM_DATABASE=STB Datenservice GmbH + +OUI:00128E* + ID_OUI_FROM_DATABASE=Q-Free ASA + +OUI:001292* + ID_OUI_FROM_DATABASE=Griffin Technology + +OUI:00127C* + ID_OUI_FROM_DATABASE=SWEGON AB + +OUI:001281* + ID_OUI_FROM_DATABASE=March Networks S.p.A. + +OUI:00127B* + ID_OUI_FROM_DATABASE=VIA Networking Technologies, Inc. + +OUI:001327* + ID_OUI_FROM_DATABASE=Data Acquisitions limited + +OUI:00131D* + ID_OUI_FROM_DATABASE=Scanvaegt International A/S + +OUI:001322* + ID_OUI_FROM_DATABASE=DAQ Electronics, Inc. + +OUI:001316* + ID_OUI_FROM_DATABASE=L-S-B Broadcast Technologies GmbH + +OUI:00130F* + ID_OUI_FROM_DATABASE=EGEMEN Bilgisayar Muh San ve Tic LTD STI + +OUI:0012F7* + ID_OUI_FROM_DATABASE=Xiamen Xinglian Electronics Co., Ltd. + +OUI:0012FE* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:001303* + ID_OUI_FROM_DATABASE=GateConnect + +OUI:0012FD* + ID_OUI_FROM_DATABASE=OPTIMUS IC S.A. + +OUI:00140F* + ID_OUI_FROM_DATABASE=Federal State Unitary Enterprise Leningrad R&D Institute of + +OUI:001416* + ID_OUI_FROM_DATABASE=Scosche Industries, Inc. + +OUI:001406* + ID_OUI_FROM_DATABASE=Go Networks + +OUI:001407* + ID_OUI_FROM_DATABASE=Sperian Protection Instrumentation + +OUI:00140C* + ID_OUI_FROM_DATABASE=GKB CCTV CO., LTD. + +OUI:0013FF* + ID_OUI_FROM_DATABASE=Dage-MTI of MC, Inc. + +OUI:001400* + ID_OUI_FROM_DATABASE=MINERVA KOREA CO., LTD + +OUI:0013FA* + ID_OUI_FROM_DATABASE=LifeSize Communications, Inc + +OUI:0013F3* + ID_OUI_FROM_DATABASE=Giga-byte Communications Inc. + +OUI:0013EE* + ID_OUI_FROM_DATABASE=JBX Designs Inc. + +OUI:0013ED* + ID_OUI_FROM_DATABASE=PSIA + +OUI:00135A* + ID_OUI_FROM_DATABASE=Project T&E Limited + +OUI:00135F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001360* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001352* + ID_OUI_FROM_DATABASE=Naztec, Inc. + +OUI:00134B* + ID_OUI_FROM_DATABASE=ToGoldenNet Technology Inc. + +OUI:00134C* + ID_OUI_FROM_DATABASE=YDT Technology International + +OUI:00133A* + ID_OUI_FROM_DATABASE=VadaTech Inc. + +OUI:00133F* + ID_OUI_FROM_DATABASE=Eppendorf Instrumente GmbH + +OUI:00132C* + ID_OUI_FROM_DATABASE=MAZ Brandenburg GmbH + +OUI:001339* + ID_OUI_FROM_DATABASE=CCV Deutschland GmbH + +OUI:0013AD* + ID_OUI_FROM_DATABASE=Sendo Ltd + +OUI:0013B4* + ID_OUI_FROM_DATABASE=Appear TV + +OUI:0013A8* + ID_OUI_FROM_DATABASE=Tanisys Technology + +OUI:0013A7* + ID_OUI_FROM_DATABASE=BATTELLE MEMORIAL INSTITUTE + +OUI:0013A1* + ID_OUI_FROM_DATABASE=Crow Electronic Engeneering + +OUI:00139A* + ID_OUI_FROM_DATABASE=K-ubique ID Corp. + +OUI:001395* + ID_OUI_FROM_DATABASE=congatec AG + +OUI:00138E* + ID_OUI_FROM_DATABASE=FOAB Elektronik AB + +OUI:001388* + ID_OUI_FROM_DATABASE=WiMedia Alliance + +OUI:0013E4* + ID_OUI_FROM_DATABASE=YANGJAE SYSTEMS CORP. + +OUI:0013E9* + ID_OUI_FROM_DATABASE=VeriWave, Inc. + +OUI:0013E3* + ID_OUI_FROM_DATABASE=CoVi Technologies, Inc. + +OUI:0013DD* + ID_OUI_FROM_DATABASE=Abbott Diagnostics + +OUI:0013D6* + ID_OUI_FROM_DATABASE=TII NETWORK TECHNOLOGIES, INC. + +OUI:0013D1* + ID_OUI_FROM_DATABASE=KIRK telecom A/S + +OUI:0013CA* + ID_OUI_FROM_DATABASE=Pico Digital + +OUI:0013C3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0013C4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0013BA* + ID_OUI_FROM_DATABASE=ReadyLinks Inc + +OUI:0013BE* + ID_OUI_FROM_DATABASE=Virtual Conexions + +OUI:0013B9* + ID_OUI_FROM_DATABASE=BM SPA + +OUI:0012F3* + ID_OUI_FROM_DATABASE=connectBlue AB + +OUI:0012ED* + ID_OUI_FROM_DATABASE=AVG Advanced Technologies + +OUI:0012E6* + ID_OUI_FROM_DATABASE=SPECTEC COMPUTER CO., LTD. + +OUI:0012E1* + ID_OUI_FROM_DATABASE=Alliant Networks, Inc + +OUI:0012D3* + ID_OUI_FROM_DATABASE=Zetta Systems, Inc. + +OUI:0012DA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0012D4* + ID_OUI_FROM_DATABASE=Princeton Technology, Ltd + +OUI:0012C7* + ID_OUI_FROM_DATABASE=SECURAY Technologies Ltd.Co. + +OUI:0012CE* + ID_OUI_FROM_DATABASE=Advanced Cybernetics Group + +OUI:0012C2* + ID_OUI_FROM_DATABASE=Apex Electronics Factory + +OUI:0012C1* + ID_OUI_FROM_DATABASE=Check Point Software Technologies + +OUI:0012B8* + ID_OUI_FROM_DATABASE=G2 Microsystems + +OUI:0012BD* + ID_OUI_FROM_DATABASE=Avantec Manufacturing Limited + +OUI:0012B7* + ID_OUI_FROM_DATABASE=PTW Freiburg + +OUI:0012B1* + ID_OUI_FROM_DATABASE=Dai Nippon Printing Co., Ltd + +OUI:0012A5* + ID_OUI_FROM_DATABASE=Stargen, Inc. + +OUI:0012AA* + ID_OUI_FROM_DATABASE=IEE, Inc. + +OUI:001379* + ID_OUI_FROM_DATABASE=PONDER INFORMATION INDUSTRIES LTD. + +OUI:001380* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001385* + ID_OUI_FROM_DATABASE=Add-On Technology Co., LTD. + +OUI:00137F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00136D* + ID_OUI_FROM_DATABASE=Tentaculus AB + +OUI:001366* + ID_OUI_FROM_DATABASE=Neturity Technologies Inc. + +OUI:001258* + ID_OUI_FROM_DATABASE=Activis Polska + +OUI:001251* + ID_OUI_FROM_DATABASE=SILINK + +OUI:001252* + ID_OUI_FROM_DATABASE=Citronix, LLC + +OUI:001245* + ID_OUI_FROM_DATABASE=Zellweger Analytics, Inc. + +OUI:00124C* + ID_OUI_FROM_DATABASE=BBWM Corporation + +OUI:001239* + ID_OUI_FROM_DATABASE=S Net Systems Inc. + +OUI:001240* + ID_OUI_FROM_DATABASE=AMOI ELECTRONICS CO.,LTD + +OUI:00122D* + ID_OUI_FROM_DATABASE=SiNett Corporation + +OUI:001232* + ID_OUI_FROM_DATABASE=LeWiz Communications Inc. + +OUI:0011C5* + ID_OUI_FROM_DATABASE=TEN Technology + +OUI:0011C8* + ID_OUI_FROM_DATABASE=Powercom Co., Ltd. + +OUI:0011CD* + ID_OUI_FROM_DATABASE=Axsun Technologies + +OUI:0011C6* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:0011B4* + ID_OUI_FROM_DATABASE=Westermo Teleindustri AB + +OUI:0011B9* + ID_OUI_FROM_DATABASE=Inner Range Pty. Ltd. + +OUI:0011C0* + ID_OUI_FROM_DATABASE=Aday Technology Inc + +OUI:0011B3* + ID_OUI_FROM_DATABASE=YOSHIMIYA CO.,LTD. + +OUI:0011AD* + ID_OUI_FROM_DATABASE=Shanghai Ruijie Technology + +OUI:001138* + ID_OUI_FROM_DATABASE=TAISHIN CO., LTD. + +OUI:00113F* + ID_OUI_FROM_DATABASE=Alcatel DI + +OUI:001133* + ID_OUI_FROM_DATABASE=Siemens Austria SIMEA + +OUI:001132* + ID_OUI_FROM_DATABASE=Synology Incorporated + +OUI:001129* + ID_OUI_FROM_DATABASE=Paradise Datacom Ltd. + +OUI:00112E* + ID_OUI_FROM_DATABASE=CEICOM + +OUI:001128* + ID_OUI_FROM_DATABASE=Streamit + +OUI:00111B* + ID_OUI_FROM_DATABASE=Targa Systems Div L-3 Communications Canada + +OUI:001122* + ID_OUI_FROM_DATABASE=CIMSYS Inc + +OUI:001171* + ID_OUI_FROM_DATABASE=DEXTER Communications, Inc. + +OUI:00116A* + ID_OUI_FROM_DATABASE=Domo Ltd + +OUI:001160* + ID_OUI_FROM_DATABASE=ARTDIO Company Co., LTD + +OUI:001154* + ID_OUI_FROM_DATABASE=Webpro Technologies Inc. + +OUI:00114B* + ID_OUI_FROM_DATABASE=Francotyp-Postalia GmbH + +OUI:001145* + ID_OUI_FROM_DATABASE=ValuePoint Networks + +OUI:0011A1* + ID_OUI_FROM_DATABASE=VISION NETWARE CO.,LTD + +OUI:0011A6* + ID_OUI_FROM_DATABASE=Sypixx Networks + +OUI:00119A* + ID_OUI_FROM_DATABASE=Alkeria srl + +OUI:001190* + ID_OUI_FROM_DATABASE=Digital Design Corporation + +OUI:00118A* + ID_OUI_FROM_DATABASE=Viewtran Technology Limited + +OUI:001194* + ID_OUI_FROM_DATABASE=Chi Mei Communication Systems, Inc. + +OUI:001189* + ID_OUI_FROM_DATABASE=Aerotech Inc + +OUI:001184* + ID_OUI_FROM_DATABASE=Humo Laboratory,Ltd. + +OUI:00117D* + ID_OUI_FROM_DATABASE=ZMD America, Inc. + +OUI:001178* + ID_OUI_FROM_DATABASE=Chiron Technology Ltd + +OUI:001177* + ID_OUI_FROM_DATABASE=Coaxial Networks, Inc. + +OUI:001223* + ID_OUI_FROM_DATABASE=Pixim + +OUI:001228* + ID_OUI_FROM_DATABASE=Data Ltd. + +OUI:001210* + ID_OUI_FROM_DATABASE=WideRay Corp + +OUI:001215* + ID_OUI_FROM_DATABASE=iStor Networks, Inc. + +OUI:001216* + ID_OUI_FROM_DATABASE=ICP Internet Communication Payment AG + +OUI:001209* + ID_OUI_FROM_DATABASE=Fastrax Ltd + +OUI:001204* + ID_OUI_FROM_DATABASE=u10 Networks, Inc. + +OUI:0011FD* + ID_OUI_FROM_DATABASE=KORG INC. + +OUI:001203* + ID_OUI_FROM_DATABASE=ActivNetworks + +OUI:0011F3* + ID_OUI_FROM_DATABASE=NeoMedia Europe AG + +OUI:0011E7* + ID_OUI_FROM_DATABASE=WORLDSAT - Texas de France + +OUI:0011EC* + ID_OUI_FROM_DATABASE=AVIX INC. + +OUI:0011E0* + ID_OUI_FROM_DATABASE=U-MEDIA Communications, Inc. + +OUI:0011DA* + ID_OUI_FROM_DATABASE=Vivaas Technology Inc. + +OUI:0011D4* + ID_OUI_FROM_DATABASE=NetEnrich, Inc + +OUI:0011D9* + ID_OUI_FROM_DATABASE=TiVo + +OUI:00111C* + ID_OUI_FROM_DATABASE=Pleora Technologies Inc. + +OUI:00110F* + ID_OUI_FROM_DATABASE=netplat,Inc. + +OUI:001116* + ID_OUI_FROM_DATABASE=COTEAU VERT CO., LTD. + +OUI:001109* + ID_OUI_FROM_DATABASE=Micro-Star International + +OUI:001103* + ID_OUI_FROM_DATABASE=kawamura electric inc. + +OUI:000FFD* + ID_OUI_FROM_DATABASE=Glorytek Network Inc. + +OUI:000FEE* + ID_OUI_FROM_DATABASE=XTec, Incorporated + +OUI:000FF4* + ID_OUI_FROM_DATABASE=Guntermann & Drunck GmbH + +OUI:001275* + ID_OUI_FROM_DATABASE=Sentilla Corporation + +OUI:00126E* + ID_OUI_FROM_DATABASE=Seidel Elektronik GmbH Nfg.KG + +OUI:001269* + ID_OUI_FROM_DATABASE=Value Electronics + +OUI:00125C* + ID_OUI_FROM_DATABASE=Green Hills Software, Inc. + +OUI:000F15* + ID_OUI_FROM_DATABASE=Kjaerulff1 A/S + +OUI:000F1A* + ID_OUI_FROM_DATABASE=Gaming Support B.V. + +OUI:000F0E* + ID_OUI_FROM_DATABASE=WaveSplitter Technologies, Inc. + +OUI:000F08* + ID_OUI_FROM_DATABASE=Indagon Oy + +OUI:000F07* + ID_OUI_FROM_DATABASE=Mangrove Systems, Inc. + +OUI:000F02* + ID_OUI_FROM_DATABASE=Digicube Technology Co., Ltd + +OUI:000EFB* + ID_OUI_FROM_DATABASE=Macey Enterprises + +OUI:000EF5* + ID_OUI_FROM_DATABASE=iPAC Technology Co., Ltd. + +OUI:000EF6* + ID_OUI_FROM_DATABASE=E-TEN Information Systems Co., Ltd. + +OUI:000E8A* + ID_OUI_FROM_DATABASE=Avara Technologies Pty. Ltd. + +OUI:000E83* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E73* + ID_OUI_FROM_DATABASE=Tpack A/S + +OUI:000E7D* + ID_OUI_FROM_DATABASE=Electronics Line 3000 Ltd. + +OUI:000E77* + ID_OUI_FROM_DATABASE=Decru, Inc. + +OUI:000E7E* + ID_OUI_FROM_DATABASE=ionSign Oy + +OUI:000E6F* + ID_OUI_FROM_DATABASE=IRIS Corporation Berhad + +OUI:000E6A* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:000E69* + ID_OUI_FROM_DATABASE=China Electric Power Research Institute + +OUI:000E63* + ID_OUI_FROM_DATABASE=Lemke Diagnostics GmbH + +OUI:000EBC* + ID_OUI_FROM_DATABASE=Paragon Fidelity GmbH + +OUI:000EB0* + ID_OUI_FROM_DATABASE=Solutions Radio BV + +OUI:000EB5* + ID_OUI_FROM_DATABASE=Ecastle Electronics Co., Ltd. + +OUI:000EAF* + ID_OUI_FROM_DATABASE=CASTEL + +OUI:000EA9* + ID_OUI_FROM_DATABASE=Shanghai Xun Shi Communications Equipment Ltd. Co. + +OUI:000E9D* + ID_OUI_FROM_DATABASE=Tiscali UK Ltd + +OUI:000EA2* + ID_OUI_FROM_DATABASE=McAfee, Inc + +OUI:000E90* + ID_OUI_FROM_DATABASE=PONICO CORP. + +OUI:000E8F* + ID_OUI_FROM_DATABASE=Sercomm Corp. + +OUI:000E96* + ID_OUI_FROM_DATABASE=Cubic Defense Applications, Inc. + +OUI:000F4E* + ID_OUI_FROM_DATABASE=Cellink + +OUI:000F41* + ID_OUI_FROM_DATABASE=Zipher Ltd + +OUI:000F48* + ID_OUI_FROM_DATABASE=Polypix Inc. + +OUI:000F4D* + ID_OUI_FROM_DATABASE=TalkSwitch + +OUI:000F39* + ID_OUI_FROM_DATABASE=IRIS SENSORS + +OUI:000F3C* + ID_OUI_FROM_DATABASE=Endeleo Limited + +OUI:000F34* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000F2D* + ID_OUI_FROM_DATABASE=CHUNG-HSIN ELECTRIC & MACHINERY MFG.CORP. + +OUI:000F27* + ID_OUI_FROM_DATABASE=TEAL Electronics, Inc. + +OUI:000F28* + ID_OUI_FROM_DATABASE=Itronix Corporation + +OUI:000F21* + ID_OUI_FROM_DATABASE=Scientific Atlanta, Inc + +OUI:000EEF* + ID_OUI_FROM_DATABASE=Private + +OUI:000EDC* + ID_OUI_FROM_DATABASE=Tellion INC. + +OUI:000EE3* + ID_OUI_FROM_DATABASE=Chiyu Technology Co.,Ltd + +OUI:000EC8* + ID_OUI_FROM_DATABASE=Zoran Corporation + +OUI:000ECF* + ID_OUI_FROM_DATABASE=PROFIBUS Nutzerorganisation e.V. + +OUI:000ED4* + ID_OUI_FROM_DATABASE=CRESITT INDUSTRIE + +OUI:000EC2* + ID_OUI_FROM_DATABASE=Lowrance Electronics, Inc. + +OUI:000EC1* + ID_OUI_FROM_DATABASE=MYNAH Technologies + +OUI:000F92* + ID_OUI_FROM_DATABASE=Microhard Systems Inc. + +OUI:000F99* + ID_OUI_FROM_DATABASE=APAC opto Electronics Inc. + +OUI:000F8D* + ID_OUI_FROM_DATABASE=FAST TV-Server AG + +OUI:000F80* + ID_OUI_FROM_DATABASE=Trinity Security Systems,Inc. + +OUI:000F7F* + ID_OUI_FROM_DATABASE=UBSTORAGE Co.,Ltd. + +OUI:000FC2* + ID_OUI_FROM_DATABASE=Uniwell Corporation + +OUI:000FC9* + ID_OUI_FROM_DATABASE=Allnet GmbH + +OUI:000FBC* + ID_OUI_FROM_DATABASE=Onkey Technologies, Inc. + +OUI:000FBB* + ID_OUI_FROM_DATABASE=Nokia Siemens Networks GmbH & Co. KG. + +OUI:000FB6* + ID_OUI_FROM_DATABASE=Europlex Technologies + +OUI:000FA9* + ID_OUI_FROM_DATABASE=PC Fabrik + +OUI:000FAA* + ID_OUI_FROM_DATABASE=Nexus Technologies + +OUI:000FAF* + ID_OUI_FROM_DATABASE=Dialog Inc. + +OUI:000FE8* + ID_OUI_FROM_DATABASE=Lobos, Inc. + +OUI:000FED* + ID_OUI_FROM_DATABASE=Anam Electronics Co., Ltd + +OUI:000FDC* + ID_OUI_FROM_DATABASE=Ueda Japan Radio Co., Ltd. + +OUI:000FE1* + ID_OUI_FROM_DATABASE=ID DIGITAL CORPORATION + +OUI:000FD5* + ID_OUI_FROM_DATABASE=Schwechat - RISE + +OUI:000FCE* + ID_OUI_FROM_DATABASE=Kikusui Electronics Corp. + +OUI:000F73* + ID_OUI_FROM_DATABASE=RS Automation Co., Ltd + +OUI:000F7A* + ID_OUI_FROM_DATABASE=BeiJing NuQX Technology CO.,LTD + +OUI:000F6D* + ID_OUI_FROM_DATABASE=Midas Engineering + +OUI:000F67* + ID_OUI_FROM_DATABASE=West Instruments + +OUI:000F6E* + ID_OUI_FROM_DATABASE=BBox + +OUI:000F60* + ID_OUI_FROM_DATABASE=Lifetron Co.,Ltd + +OUI:000F5B* + ID_OUI_FROM_DATABASE=Delta Information Systems, Inc. + +OUI:000F54* + ID_OUI_FROM_DATABASE=Entrelogic Corporation + +OUI:000D75* + ID_OUI_FROM_DATABASE=Kobian Pte Ltd - Taiwan Branch + +OUI:000D7C* + ID_OUI_FROM_DATABASE=Codian Ltd + +OUI:000D6F* + ID_OUI_FROM_DATABASE=Ember Corporation + +OUI:000D69* + ID_OUI_FROM_DATABASE=TMT&D Corporation + +OUI:000D70* + ID_OUI_FROM_DATABASE=Datamax Corporation + +OUI:000D5D* + ID_OUI_FROM_DATABASE=Raritan Computer, Inc + +OUI:000D62* + ID_OUI_FROM_DATABASE=Funkwerk Dabendorf GmbH + +OUI:000D50* + ID_OUI_FROM_DATABASE=Galazar Networks + +OUI:000D4A* + ID_OUI_FROM_DATABASE=Steag ETA-Optik + +OUI:000DAB* + ID_OUI_FROM_DATABASE=Parker Hannifin GmbH Electromechanical Division Europe + +OUI:000DA7* + ID_OUI_FROM_DATABASE=Private + +OUI:000DA1* + ID_OUI_FROM_DATABASE=MIRAE ITS Co.,LTD. + +OUI:000DA2* + ID_OUI_FROM_DATABASE=Infrant Technologies, Inc. + +OUI:000D9B* + ID_OUI_FROM_DATABASE=Heraeus Electro-Nite International N.V. + +OUI:000D8F* + ID_OUI_FROM_DATABASE=King Tsushin Kogyo Co., LTD. + +OUI:000D94* + ID_OUI_FROM_DATABASE=AFAR Communications,Inc + +OUI:000D82* + ID_OUI_FROM_DATABASE=PHS srl + +OUI:000D81* + ID_OUI_FROM_DATABASE=Pepperl+Fuchs GmbH + +OUI:000DCE* + ID_OUI_FROM_DATABASE=Dynavac Technology Pte Ltd + +OUI:000DC8* + ID_OUI_FROM_DATABASE=AirMagnet, Inc + +OUI:000DC2* + ID_OUI_FROM_DATABASE=Private + +OUI:000DC7* + ID_OUI_FROM_DATABASE=COSMIC ENGINEERING INC. + +OUI:000DBB* + ID_OUI_FROM_DATABASE=Nippon Dentsu Co.,Ltd. + +OUI:000DB5* + ID_OUI_FROM_DATABASE=GLOBALSAT TECHNOLOGY CORPORATION + +OUI:000DAF* + ID_OUI_FROM_DATABASE=Plexus Corp (UK) Ltd + +OUI:000D29* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000D23* + ID_OUI_FROM_DATABASE=Smart Solution, Inc + +OUI:000D17* + ID_OUI_FROM_DATABASE=Turbo Networks Co.Ltd + +OUI:000D1C* + ID_OUI_FROM_DATABASE=Amesys Defense + +OUI:000D0A* + ID_OUI_FROM_DATABASE=Projectiondesign as + +OUI:000D09* + ID_OUI_FROM_DATABASE=Yuehua(Zhuhai) Electronic CO. LTD + +OUI:000D10* + ID_OUI_FROM_DATABASE=Embedtronics Oy + +OUI:000D04* + ID_OUI_FROM_DATABASE=Foxboro Eckardt Development GmbH + +OUI:000CFD* + ID_OUI_FROM_DATABASE=Hyundai ImageQuest Co.,Ltd. + +OUI:000D4F* + ID_OUI_FROM_DATABASE=Kenwood Corporation + +OUI:000D46* + ID_OUI_FROM_DATABASE=Parker SSD Drives + +OUI:000D42* + ID_OUI_FROM_DATABASE=Newbest Development Limited + +OUI:000D3C* + ID_OUI_FROM_DATABASE=i.Tech Dynamic Ltd + +OUI:000D36* + ID_OUI_FROM_DATABASE=Wu Han Routon Electronic Co., Ltd + +OUI:000D3B* + ID_OUI_FROM_DATABASE=Microelectronics Technology Inc. + +OUI:000D2A* + ID_OUI_FROM_DATABASE=Scanmatic AS + +OUI:000D2F* + ID_OUI_FROM_DATABASE=AIN Comm.Tech.Co., LTD + +OUI:000DFA* + ID_OUI_FROM_DATABASE=Micro Control Systems Ltd. + +OUI:000DF4* + ID_OUI_FROM_DATABASE=Watertek Co. + +OUI:000DF9* + ID_OUI_FROM_DATABASE=NDS Limited + +OUI:000E00* + ID_OUI_FROM_DATABASE=Atrie + +OUI:000DE7* + ID_OUI_FROM_DATABASE=Snap-on OEM Group + +OUI:000DE8* + ID_OUI_FROM_DATABASE=Nasaco Electronics Pte. Ltd + +OUI:000DED* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000DE1* + ID_OUI_FROM_DATABASE=Control Products, Inc. + +OUI:000DD5* + ID_OUI_FROM_DATABASE=O'RITE TECHNOLOGY CO.,LTD + +OUI:000DDA* + ID_OUI_FROM_DATABASE=ALLIED TELESIS K.K. + +OUI:000E20* + ID_OUI_FROM_DATABASE=ACCESS Systems Americas, Inc. + +OUI:000E27* + ID_OUI_FROM_DATABASE=Crere Networks, Inc. + +OUI:000E14* + ID_OUI_FROM_DATABASE=Visionary Solutions, Inc. + +OUI:000E1B* + ID_OUI_FROM_DATABASE=IAV GmbH + +OUI:000E57* + ID_OUI_FROM_DATABASE=Iworld Networking, Inc. + +OUI:000E50* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:000E4A* + ID_OUI_FROM_DATABASE=Changchun Huayu WEBPAD Co.,LTD + +OUI:000E49* + ID_OUI_FROM_DATABASE=Forsway Scandinavia AB + +OUI:000E3D* + ID_OUI_FROM_DATABASE=Televic N.V. + +OUI:000E44* + ID_OUI_FROM_DATABASE=Digital 5, Inc. + +OUI:000E33* + ID_OUI_FROM_DATABASE=Shuko Electronics Co.,Ltd + +OUI:000E3A* + ID_OUI_FROM_DATABASE=Cirrus Logic + +OUI:000E2D* + ID_OUI_FROM_DATABASE=Hyundai Digital Technology Co.,Ltd. + +OUI:000CEA* + ID_OUI_FROM_DATABASE=aphona Kommunikationssysteme + +OUI:000CD9* + ID_OUI_FROM_DATABASE=Itcare Co., Ltd + +OUI:000CD3* + ID_OUI_FROM_DATABASE=Prettl Elektronik Radeberg GmbH + +OUI:000CDA* + ID_OUI_FROM_DATABASE=FreeHand Systems, Inc. + +OUI:000CDF* + ID_OUI_FROM_DATABASE=PULNiX America, Inc + +OUI:000CC7* + ID_OUI_FROM_DATABASE=Intelligent Computer Solutions Inc. + +OUI:000CCC* + ID_OUI_FROM_DATABASE=Aeroscout Ltd. + +OUI:000C13* + ID_OUI_FROM_DATABASE=MediaQ + +OUI:000C05* + ID_OUI_FROM_DATABASE=RPA Reserch Co., Ltd. + +OUI:000C0C* + ID_OUI_FROM_DATABASE=APPRO TECHNOLOGY INC. + +OUI:000BF4* + ID_OUI_FROM_DATABASE=Private + +OUI:000BF9* + ID_OUI_FROM_DATABASE=Gemstone Communications, Inc. + +OUI:000C00* + ID_OUI_FROM_DATABASE=BEB Industrie-Elektronik AG + +OUI:000BF3* + ID_OUI_FROM_DATABASE=BAE SYSTEMS + +OUI:000C63* + ID_OUI_FROM_DATABASE=Zenith Electronics Corporation + +OUI:000C68* + ID_OUI_FROM_DATABASE=SigmaTel, Inc. + +OUI:000C6F* + ID_OUI_FROM_DATABASE=Amtek system co.,LTD. + +OUI:000C50* + ID_OUI_FROM_DATABASE=Seagate Technology + +OUI:000C55* + ID_OUI_FROM_DATABASE=Microlink Communications Inc. + +OUI:000C5C* + ID_OUI_FROM_DATABASE=GTN Systems B.V. + +OUI:000C61* + ID_OUI_FROM_DATABASE=AC Tech corporation DBA Advanced Digital + +OUI:000CBA* + ID_OUI_FROM_DATABASE=Jamex, Inc. + +OUI:000CB9* + ID_OUI_FROM_DATABASE=LEA + +OUI:000CC0* + ID_OUI_FROM_DATABASE=Genera Oy + +OUI:000CB4* + ID_OUI_FROM_DATABASE=AutoCell Laboratories, Inc. + +OUI:000C34* + ID_OUI_FROM_DATABASE=Vixen Co., Ltd. + +OUI:000CA2* + ID_OUI_FROM_DATABASE=Harmonic Video Network + +OUI:000CA7* + ID_OUI_FROM_DATABASE=Metro (Suzhou) Technologies Co., Ltd. + +OUI:000CA9* + ID_OUI_FROM_DATABASE=Ebtron Inc. + +OUI:000CAE* + ID_OUI_FROM_DATABASE=Ailocom Oy + +OUI:000C42* + ID_OUI_FROM_DATABASE=Routerboard.com + +OUI:000C44* + ID_OUI_FROM_DATABASE=Automated Interfaces, Inc. + +OUI:000C39* + ID_OUI_FROM_DATABASE=Sentinel Wireless Inc. + +OUI:000C3B* + ID_OUI_FROM_DATABASE=Orion Electric Co., Ltd. + +OUI:000C40* + ID_OUI_FROM_DATABASE=Altech Controls + +OUI:000C3A* + ID_OUI_FROM_DATABASE=Oxance + +OUI:000C2F* + ID_OUI_FROM_DATABASE=SeorimTechnology Co.,Ltd. + +OUI:000C31* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000C2A* + ID_OUI_FROM_DATABASE=OCTTEL Communication Co., Ltd. + +OUI:000C27* + ID_OUI_FROM_DATABASE=Sammy Corporation + +OUI:000C18* + ID_OUI_FROM_DATABASE=Zenisu Keisoku Inc. + +OUI:000C20* + ID_OUI_FROM_DATABASE=Fi WIn, Inc. + +OUI:000BED* + ID_OUI_FROM_DATABASE=ELM Inc. + +OUI:000BF2* + ID_OUI_FROM_DATABASE=Chih-Kan Technology Co., Ltd. + +OUI:000BE1* + ID_OUI_FROM_DATABASE=Nokia NET Product Operations + +OUI:000BE6* + ID_OUI_FROM_DATABASE=Datel Electronics + +OUI:000BDA* + ID_OUI_FROM_DATABASE=EyeCross Co.,Inc. + +OUI:000BD1* + ID_OUI_FROM_DATABASE=Aeronix, Inc. + +OUI:000BC5* + ID_OUI_FROM_DATABASE=SMC Networks, Inc. + +OUI:000BCC* + ID_OUI_FROM_DATABASE=JUSAN, S.A. + +OUI:000BB9* + ID_OUI_FROM_DATABASE=Imsys AB + +OUI:000BBE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000BB2* + ID_OUI_FROM_DATABASE=SMALLBIG TECHNOLOGY + +OUI:000BB7* + ID_OUI_FROM_DATABASE=Micro Systems Co.,Ltd. + +OUI:000C96* + ID_OUI_FROM_DATABASE=OQO, Inc. + +OUI:000C9B* + ID_OUI_FROM_DATABASE=EE Solutions, Inc + +OUI:000C8A* + ID_OUI_FROM_DATABASE=Bose Corporation + +OUI:000C8F* + ID_OUI_FROM_DATABASE=Nergal s.r.l. + +OUI:000C83* + ID_OUI_FROM_DATABASE=Logical Solutions + +OUI:000C88* + ID_OUI_FROM_DATABASE=Apache Micro Peripherals, Inc. + +OUI:000C74* + ID_OUI_FROM_DATABASE=RIVERTEC CORPORATION + +OUI:000C76* + ID_OUI_FROM_DATABASE=MICRO-STAR INTERNATIONAL CO., LTD. + +OUI:000C7B* + ID_OUI_FROM_DATABASE=ALPHA PROJECT Co.,Ltd. + +OUI:000B85* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B7F* + ID_OUI_FROM_DATABASE=Align Engineering LLC + +OUI:000B84* + ID_OUI_FROM_DATABASE=BODET + +OUI:000B73* + ID_OUI_FROM_DATABASE=Kodeos Communications + +OUI:000B78* + ID_OUI_FROM_DATABASE=TAIFATECH INC. + +OUI:000B6C* + ID_OUI_FROM_DATABASE=Sychip Inc. + +OUI:000B60* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B65* + ID_OUI_FROM_DATABASE=Sy.A.C. srl + +OUI:000B57* + ID_OUI_FROM_DATABASE=Silicon Laboratories + +OUI:000B5C* + ID_OUI_FROM_DATABASE=Newtech Co.,Ltd + +OUI:000B4F* + ID_OUI_FROM_DATABASE=Verifone, INC. + +OUI:000B43* + ID_OUI_FROM_DATABASE=Microscan Systems, Inc. + +OUI:000B48* + ID_OUI_FROM_DATABASE=sofrel + +OUI:000B4A* + ID_OUI_FROM_DATABASE=Visimetrics (UK) Ltd + +OUI:000B35* + ID_OUI_FROM_DATABASE=Quad Bit System co., Ltd. + +OUI:000B37* + ID_OUI_FROM_DATABASE=MANUFACTURE DES MONTRES ROLEX SA + +OUI:000B3C* + ID_OUI_FROM_DATABASE=Cygnal Integrated Products, Inc. + +OUI:000B29* + ID_OUI_FROM_DATABASE=LS(LG) Industrial Systems co.,Ltd + +OUI:000B30* + ID_OUI_FROM_DATABASE=Beijing Gongye Science & Technology Co.,Ltd + +OUI:000BA1* + ID_OUI_FROM_DATABASE=SYSCOM Ltd. + +OUI:000BA8* + ID_OUI_FROM_DATABASE=HANBACK ELECTRONICS CO., LTD. + +OUI:000B92* + ID_OUI_FROM_DATABASE=Ascom Danmark A/S + +OUI:000B97* + ID_OUI_FROM_DATABASE=Matsushita Electric Industrial Co.,Ltd. + +OUI:000B9C* + ID_OUI_FROM_DATABASE=TriBeam Technologies, Inc. + +OUI:000B8B* + ID_OUI_FROM_DATABASE=KERAJET, S.A. + +OUI:0009D6* + ID_OUI_FROM_DATABASE=KNC One GmbH + +OUI:0009D5* + ID_OUI_FROM_DATABASE=Signal Communication, Inc. + +OUI:0009DC* + ID_OUI_FROM_DATABASE=Galaxis Technology AG + +OUI:0009C9* + ID_OUI_FROM_DATABASE=BlueWINC Co., Ltd. + +OUI:0009D0* + ID_OUI_FROM_DATABASE=Solacom Technologies Inc. + +OUI:0009BC* + ID_OUI_FROM_DATABASE=Digital Safety Technologies, Inc + +OUI:0009C1* + ID_OUI_FROM_DATABASE=PROCES-DATA A/S + +OUI:0009C4* + ID_OUI_FROM_DATABASE=Medicore Co., Ltd + +OUI:00098F* + ID_OUI_FROM_DATABASE=Cetacean Networks + +OUI:00097D* + ID_OUI_FROM_DATABASE=SecWell Networks Oy + +OUI:00097E* + ID_OUI_FROM_DATABASE=IMI TECHNOLOGY CO., LTD + +OUI:000983* + ID_OUI_FROM_DATABASE=GlobalTop Technology, Inc. + +OUI:000970* + ID_OUI_FROM_DATABASE=Vibration Research Corporation + +OUI:000977* + ID_OUI_FROM_DATABASE=Brunner Elektronik AG + +OUI:000964* + ID_OUI_FROM_DATABASE=Hi-Techniques, Inc. + +OUI:00096B* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:000957* + ID_OUI_FROM_DATABASE=Supercaller, Inc. + +OUI:00095C* + ID_OUI_FROM_DATABASE=Philips Medical Systems - Cardiac and Monitoring Systems (CM + +OUI:000AE3* + ID_OUI_FROM_DATABASE=YANG MEI TECHNOLOGY CO., LTD + +OUI:000AEA* + ID_OUI_FROM_DATABASE=ADAM ELEKTRONIK LTD. ŞTI + +OUI:000ADE* + ID_OUI_FROM_DATABASE=Happy Communication Co., Ltd. + +OUI:000AD7* + ID_OUI_FROM_DATABASE=Origin ELECTRIC CO.,LTD. + +OUI:000ACB* + ID_OUI_FROM_DATABASE=XPAK MSA Group + +OUI:000AD0* + ID_OUI_FROM_DATABASE=Niigata Develoment Center, F.I.T. Co., Ltd. + +OUI:000AD2* + ID_OUI_FROM_DATABASE=JEPICO Corporation + +OUI:000ABD* + ID_OUI_FROM_DATABASE=Rupprecht & Patashnick Co. + +OUI:000ABF* + ID_OUI_FROM_DATABASE=HIROTA SS + +OUI:000AC4* + ID_OUI_FROM_DATABASE=Daewoo Teletech Co., Ltd + +OUI:000AAC* + ID_OUI_FROM_DATABASE=TerraTec Electronic GmbH + +OUI:000AB1* + ID_OUI_FROM_DATABASE=GENETEC Corporation + +OUI:000AB8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000AA5* + ID_OUI_FROM_DATABASE=MAXLINK INDUSTRIES LIMITED + +OUI:000A8D* + ID_OUI_FROM_DATABASE=EUROTHERM LIMITED + +OUI:000A9E* + ID_OUI_FROM_DATABASE=BroadWeb Corportation + +OUI:000AA0* + ID_OUI_FROM_DATABASE=Cedar Point Communications + +OUI:000A98* + ID_OUI_FROM_DATABASE=M+F Gwinner GmbH & Co + +OUI:000A92* + ID_OUI_FROM_DATABASE=Presonus Corporation + +OUI:000A7E* + ID_OUI_FROM_DATABASE=The Advantage Group + +OUI:000A85* + ID_OUI_FROM_DATABASE=PLAT'C2,Inc + +OUI:000A8A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0009B5* + ID_OUI_FROM_DATABASE=3J Tech. Co., Ltd. + +OUI:0009AF* + ID_OUI_FROM_DATABASE=e-generis + +OUI:0009B0* + ID_OUI_FROM_DATABASE=Onkyo Corporation + +OUI:0009A9* + ID_OUI_FROM_DATABASE=Ikanos Communications + +OUI:00099D* + ID_OUI_FROM_DATABASE=Haliplex Communications + +OUI:0009A2* + ID_OUI_FROM_DATABASE=Interface Co., Ltd. + +OUI:000990* + ID_OUI_FROM_DATABASE=ACKSYS Communications & systems + +OUI:000996* + ID_OUI_FROM_DATABASE=RDI + +OUI:00098A* + ID_OUI_FROM_DATABASE=EqualLogic Inc + +OUI:000A77* + ID_OUI_FROM_DATABASE=Bluewire Technologies LLC + +OUI:000A79* + ID_OUI_FROM_DATABASE=corega K.K + +OUI:000A72* + ID_OUI_FROM_DATABASE=STEC, INC. + +OUI:000A5F* + ID_OUI_FROM_DATABASE=almedio inc. + +OUI:000A66* + ID_OUI_FROM_DATABASE=MITSUBISHI ELECTRIC SYSTEM & SERVICE CO.,LTD. + +OUI:000A6B* + ID_OUI_FROM_DATABASE=Tadiran Telecom Business Systems LTD + +OUI:000A5A* + ID_OUI_FROM_DATABASE=GreenNET Technologies Co.,Ltd. + +OUI:000A53* + ID_OUI_FROM_DATABASE=Intronics, Incorporated + +OUI:000A58* + ID_OUI_FROM_DATABASE=Freyer & Siegel Elektronik GmbH & Co. KG + +OUI:000A4C* + ID_OUI_FROM_DATABASE=Molecular Devices Corporation + +OUI:000B24* + ID_OUI_FROM_DATABASE=AirLogic + +OUI:000B1D* + ID_OUI_FROM_DATABASE=LayerZero Power Systems, Inc. + +OUI:000B16* + ID_OUI_FROM_DATABASE=Communication Machinery Corporation + +OUI:000B18* + ID_OUI_FROM_DATABASE=Private + +OUI:000B11* + ID_OUI_FROM_DATABASE=HIMEJI ABC TRADING CO.,LTD. + +OUI:000B0A* + ID_OUI_FROM_DATABASE=dBm Optics + +OUI:000B05* + ID_OUI_FROM_DATABASE=Pacific Broadband Networks + +OUI:000AFE* + ID_OUI_FROM_DATABASE=NovaPal Ltd + +OUI:000B03* + ID_OUI_FROM_DATABASE=Taekwang Industrial Co., Ltd + +OUI:000AEF* + ID_OUI_FROM_DATABASE=OTRUM ASA + +OUI:000AF2* + ID_OUI_FROM_DATABASE=NeoAxiom Corp. + +OUI:000A05* + ID_OUI_FROM_DATABASE=Widax Corp. + +OUI:000A0A* + ID_OUI_FROM_DATABASE=SUNIX Co., Ltd. + +OUI:000A0F* + ID_OUI_FROM_DATABASE=Ilryung Telesys, Inc + +OUI:0009FF* + ID_OUI_FROM_DATABASE=X.net 2000 GmbH + +OUI:0009FE* + ID_OUI_FROM_DATABASE=Daisy Technologies, Inc. + +OUI:000A00* + ID_OUI_FROM_DATABASE=Mediatek Corp. + +OUI:0009F6* + ID_OUI_FROM_DATABASE=Shenzhen Eastern Digital Tech Ltd. + +OUI:0009F5* + ID_OUI_FROM_DATABASE=Emerson Network Power Co.,Ltd + +OUI:0009E8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0009EF* + ID_OUI_FROM_DATABASE=Vocera Communications + +OUI:0009E3* + ID_OUI_FROM_DATABASE=Angel Iglesias S.A. + +OUI:000A39* + ID_OUI_FROM_DATABASE=LoPA Information Technology + +OUI:000A40* + ID_OUI_FROM_DATABASE=Crown Audio -- Harmanm International + +OUI:000A45* + ID_OUI_FROM_DATABASE=Audio-Technica Corp. + +OUI:000A47* + ID_OUI_FROM_DATABASE=Allied Vision Technologies + +OUI:000A34* + ID_OUI_FROM_DATABASE=Identicard Systems Incorporated + +OUI:000A2D* + ID_OUI_FROM_DATABASE=Cabot Communications Limited + +OUI:000A22* + ID_OUI_FROM_DATABASE=Amperion Inc + +OUI:000A16* + ID_OUI_FROM_DATABASE=Lassen Research + +OUI:000A1B* + ID_OUI_FROM_DATABASE=Stream Labs + +OUI:000878* + ID_OUI_FROM_DATABASE=Benchmark Storage Innovations + +OUI:000872* + ID_OUI_FROM_DATABASE=Sorenson Communications + +OUI:00087E* + ID_OUI_FROM_DATABASE=Bon Electro-Telecom Inc. + +OUI:00086B* + ID_OUI_FROM_DATABASE=MIPSYS + +OUI:000865* + ID_OUI_FROM_DATABASE=JASCOM CO., LTD + +OUI:000866* + ID_OUI_FROM_DATABASE=DSX Access Systems, Inc. + +OUI:00085F* + ID_OUI_FROM_DATABASE=Picanol N.V. + +OUI:000859* + ID_OUI_FROM_DATABASE=ShenZhen Unitone Electronics Co., Ltd. + +OUI:000853* + ID_OUI_FROM_DATABASE=Schleicher GmbH & Co. Relaiswerke KG + +OUI:000858* + ID_OUI_FROM_DATABASE=Novatechnology Inc. + +OUI:00081D* + ID_OUI_FROM_DATABASE=Ipsil, Incorporated + +OUI:000829* + ID_OUI_FROM_DATABASE=Aval Nagasaki Corporation + +OUI:000823* + ID_OUI_FROM_DATABASE=Texa Corp. + +OUI:00082A* + ID_OUI_FROM_DATABASE=Powerwallz Network Security + +OUI:000817* + ID_OUI_FROM_DATABASE=EmergeCore Networks LLC + +OUI:00091E* + ID_OUI_FROM_DATABASE=Firstech Technology Corp. + +OUI:000925* + ID_OUI_FROM_DATABASE=VSN Systemen BV + +OUI:000918* + ID_OUI_FROM_DATABASE=SAMSUNG TECHWIN CO.,LTD + +OUI:000917* + ID_OUI_FROM_DATABASE=WEM Technology Inc + +OUI:000912* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00090B* + ID_OUI_FROM_DATABASE=MTL Instruments PLC + +OUI:000905* + ID_OUI_FROM_DATABASE=iTEC Technologies Ltd. + +OUI:0008FF* + ID_OUI_FROM_DATABASE=Trilogy Communications Ltd + +OUI:000906* + ID_OUI_FROM_DATABASE=Esteem Networks + +OUI:0008FB* + ID_OUI_FROM_DATABASE=SonoSite, Inc. + +OUI:0008F2* + ID_OUI_FROM_DATABASE=C&S Technology + +OUI:0008F7* + ID_OUI_FROM_DATABASE=Hitachi Ltd, Semiconductor & Integrated Circuits Gr + +OUI:0008ED* + ID_OUI_FROM_DATABASE=ST&T Instrument Corp. + +OUI:0007D1* + ID_OUI_FROM_DATABASE=Spectrum Signal Processing Inc. + +OUI:0007CE* + ID_OUI_FROM_DATABASE=Cabletime Limited + +OUI:0007C8* + ID_OUI_FROM_DATABASE=Brain21, Inc. + +OUI:0007BC* + ID_OUI_FROM_DATABASE=Identix Inc. + +OUI:00047C* + ID_OUI_FROM_DATABASE=Skidata AG + +OUI:0007BB* + ID_OUI_FROM_DATABASE=Candera Inc. + +OUI:0007C2* + ID_OUI_FROM_DATABASE=Netsys Telecom + +OUI:0007B5* + ID_OUI_FROM_DATABASE=Any One Wireless Ltd. + +OUI:0007AF* + ID_OUI_FROM_DATABASE=Red Lion Controls, LP + +OUI:0007A2* + ID_OUI_FROM_DATABASE=Opteon Corporation + +OUI:0007A7* + ID_OUI_FROM_DATABASE=A-Z Inc. + +OUI:0007A1* + ID_OUI_FROM_DATABASE=VIASYS Healthcare GmbH + +OUI:0007A8* + ID_OUI_FROM_DATABASE=Haier Group Technologies Ltd. + +OUI:00094A* + ID_OUI_FROM_DATABASE=Homenet Communications + +OUI:000949* + ID_OUI_FROM_DATABASE=Glyph Technologies Inc. + +OUI:000950* + ID_OUI_FROM_DATABASE=Independent Storage Corporation + +OUI:000944* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00093D* + ID_OUI_FROM_DATABASE=Newisys,Inc. + +OUI:000937* + ID_OUI_FROM_DATABASE=Inventec Appliance Corp + +OUI:000931* + ID_OUI_FROM_DATABASE=Future Internet, Inc. + +OUI:000938* + ID_OUI_FROM_DATABASE=Allot Communications + +OUI:00092A* + ID_OUI_FROM_DATABASE=MYTECS Co.,Ltd. + +OUI:0008B1* + ID_OUI_FROM_DATABASE=ProQuent Systems + +OUI:0008AB* + ID_OUI_FROM_DATABASE=EnerLinx.com, Inc. + +OUI:0008AC* + ID_OUI_FROM_DATABASE=Eltromat GmbH + +OUI:0008A5* + ID_OUI_FROM_DATABASE=Peninsula Systems Inc. + +OUI:000899* + ID_OUI_FROM_DATABASE=Netbind, Inc. + +OUI:00089E* + ID_OUI_FROM_DATABASE=Beijing Enter-Net co.LTD + +OUI:000895* + ID_OUI_FROM_DATABASE=DIRC Technologie GmbH & Co.KG + +OUI:000891* + ID_OUI_FROM_DATABASE=Lyan Inc. + +OUI:00088B* + ID_OUI_FROM_DATABASE=Tropic Networks Inc. + +OUI:00088A* + ID_OUI_FROM_DATABASE=Minds@Work + +OUI:000885* + ID_OUI_FROM_DATABASE=EMS Dr. Thomas Wünsche + +OUI:0008E8* + ID_OUI_FROM_DATABASE=Excel Master Ltd. + +OUI:0008E7* + ID_OUI_FROM_DATABASE=SHI ControlSystems,Ltd. + +OUI:0008E1* + ID_OUI_FROM_DATABASE=Barix AG + +OUI:0008DA* + ID_OUI_FROM_DATABASE=SofaWare Technologies Ltd. + +OUI:0008D5* + ID_OUI_FROM_DATABASE=Vanguard Networks Solutions, LLC + +OUI:0008CE* + ID_OUI_FROM_DATABASE=IPMobileNet Inc. + +OUI:0008C8* + ID_OUI_FROM_DATABASE=Soneticom, Inc. + +OUI:0008C4* + ID_OUI_FROM_DATABASE=Hikari Co.,Ltd. + +OUI:0008BE* + ID_OUI_FROM_DATABASE=XENPAK MSA Group + +OUI:0008B8* + ID_OUI_FROM_DATABASE=E.F. Johnson + +OUI:00079B* + ID_OUI_FROM_DATABASE=Aurora Networks + +OUI:00078F* + ID_OUI_FROM_DATABASE=Emkay Innovative Products + +OUI:000788* + ID_OUI_FROM_DATABASE=Clipcomm, Inc. + +OUI:000779* + ID_OUI_FROM_DATABASE=Sungil Telecom Co., Ltd. + +OUI:000778* + ID_OUI_FROM_DATABASE=GERSTEL GmbH & Co. KG + +OUI:00076C* + ID_OUI_FROM_DATABASE=Daehanet, Inc. + +OUI:00075C* + ID_OUI_FROM_DATABASE=Eastman Kodak Company + +OUI:000768* + ID_OUI_FROM_DATABASE=Danfoss A/S + +OUI:000762* + ID_OUI_FROM_DATABASE=Group Sense Limited + +OUI:000755* + ID_OUI_FROM_DATABASE=Lafon + +OUI:00074F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000741* + ID_OUI_FROM_DATABASE=Sierra Automated Systems + +OUI:000749* + ID_OUI_FROM_DATABASE=CENiX Inc. + +OUI:000735* + ID_OUI_FROM_DATABASE=Flarion Technologies, Inc. + +OUI:00073B* + ID_OUI_FROM_DATABASE=Tenovis GmbH & Co KG + +OUI:000729* + ID_OUI_FROM_DATABASE=Kistler Instrumente AG + +OUI:00072E* + ID_OUI_FROM_DATABASE=North Node AB + +OUI:000728* + ID_OUI_FROM_DATABASE=Neo Telecom + +OUI:000718* + ID_OUI_FROM_DATABASE=iCanTek Co., Ltd. + +OUI:000806* + ID_OUI_FROM_DATABASE=Raonet Systems, Inc. + +OUI:0007FD* + ID_OUI_FROM_DATABASE=LANergy Ltd. + +OUI:0007F6* + ID_OUI_FROM_DATABASE=Qqest Software Systems + +OUI:0007FC* + ID_OUI_FROM_DATABASE=Adept Systems Inc. + +OUI:0007EA* + ID_OUI_FROM_DATABASE=Massana, Inc. + +OUI:0007F0* + ID_OUI_FROM_DATABASE=LogiSync LLC + +OUI:0007E3* + ID_OUI_FROM_DATABASE=Navcom Technology, Inc. + +OUI:0007E4* + ID_OUI_FROM_DATABASE=SoftRadio Co., Ltd. + +OUI:0007DD* + ID_OUI_FROM_DATABASE=Cradle Technologies + +OUI:0007D7* + ID_OUI_FROM_DATABASE=Caporis Networks AG + +OUI:0006E3* + ID_OUI_FROM_DATABASE=Quantitative Imaging Corporation + +OUI:0006DD* + ID_OUI_FROM_DATABASE=AT & T Laboratories - Cambridge Ltd + +OUI:0006A4* + ID_OUI_FROM_DATABASE=INNOWELL Corp. + +OUI:0006D3* + ID_OUI_FROM_DATABASE=Alpha Telecom, Inc. U.S.A. + +OUI:0006D2* + ID_OUI_FROM_DATABASE=Tundra Semiconductor Corp. + +OUI:000647* + ID_OUI_FROM_DATABASE=Etrali S.A. + +OUI:0006D9* + ID_OUI_FROM_DATABASE=IPM-Net S.p.A. + +OUI:0005EA* + ID_OUI_FROM_DATABASE=Rednix + +OUI:0006CD* + ID_OUI_FROM_DATABASE=Leaf Imaging Ltd. + +OUI:0006BC* + ID_OUI_FROM_DATABASE=Macrolink, Inc. + +OUI:0006C6* + ID_OUI_FROM_DATABASE=lesswire AG + +OUI:000654* + ID_OUI_FROM_DATABASE=Winpresa Building Automation Technologies GmbH + +OUI:0006B6* + ID_OUI_FROM_DATABASE=Nir-Or Israel Ltd. + +OUI:0006B0* + ID_OUI_FROM_DATABASE=Comtech EF Data Corp. + +OUI:00071F* + ID_OUI_FROM_DATABASE=European Systems Integration + +OUI:000724* + ID_OUI_FROM_DATABASE=Telemax Co., Ltd. + +OUI:000707* + ID_OUI_FROM_DATABASE=Interalia Inc. + +OUI:00070C* + ID_OUI_FROM_DATABASE=SVA-Intrusion.com Co. Ltd. + +OUI:000711* + ID_OUI_FROM_DATABASE=Acterna + +OUI:000712* + ID_OUI_FROM_DATABASE=JAL Information Technology + +OUI:0006FA* + ID_OUI_FROM_DATABASE=IP SQUARE Co, Ltd. + +OUI:0006EF* + ID_OUI_FROM_DATABASE=Maxxan Systems, Inc. + +OUI:0006EA* + ID_OUI_FROM_DATABASE=ELZET80 Mikrocomputer GmbH&Co. KG + +OUI:0006E9* + ID_OUI_FROM_DATABASE=Intime Corp. + +OUI:0005EB* + ID_OUI_FROM_DATABASE=Blue Ridge Networks, Inc. + +OUI:0005F7* + ID_OUI_FROM_DATABASE=Analog Devices, Inc. + +OUI:0005E4* + ID_OUI_FROM_DATABASE=Red Lion Controls Inc. + +OUI:0005F1* + ID_OUI_FROM_DATABASE=Vrcom, Inc. + +OUI:0005FD* + ID_OUI_FROM_DATABASE=PacketLight Networks Ltd. + +OUI:0005E2* + ID_OUI_FROM_DATABASE=Creativ Network Technologies + +OUI:0005DC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0005E1* + ID_OUI_FROM_DATABASE=Trellis Photonics, Ltd. + +OUI:0005D8* + ID_OUI_FROM_DATABASE=Arescom, Inc. + +OUI:0005D7* + ID_OUI_FROM_DATABASE=Vista Imaging, Inc. + +OUI:0005C5* + ID_OUI_FROM_DATABASE=Flaga HF + +OUI:0005D1* + ID_OUI_FROM_DATABASE=Metavector Technologies + +OUI:0005D2* + ID_OUI_FROM_DATABASE=DAP Technologies + +OUI:0005CB* + ID_OUI_FROM_DATABASE=ROIS Technologies, Inc. + +OUI:00057F* + ID_OUI_FROM_DATABASE=Acqis Technology + +OUI:000579* + ID_OUI_FROM_DATABASE=Universal Control Solution Corp. + +OUI:000575* + ID_OUI_FROM_DATABASE=CDS-Electronics BV + +OUI:00056F* + ID_OUI_FROM_DATABASE=Innomedia Technologies Pvt. Ltd. + +OUI:000568* + ID_OUI_FROM_DATABASE=Piltofish Networks AB + +OUI:000562* + ID_OUI_FROM_DATABASE=Digital View Limited + +OUI:00055C* + ID_OUI_FROM_DATABASE=Kowa Company, Ltd. + +OUI:000556* + ID_OUI_FROM_DATABASE=360 Systems + +OUI:000550* + ID_OUI_FROM_DATABASE=Vcomms Connect Limited + +OUI:000545* + ID_OUI_FROM_DATABASE=Internet Photonics + +OUI:00053F* + ID_OUI_FROM_DATABASE=VisionTek, Inc. + +OUI:000546* + ID_OUI_FROM_DATABASE=KDDI Network & Solultions Inc. + +OUI:0006AA* + ID_OUI_FROM_DATABASE=VT Miltope + +OUI:0006A9* + ID_OUI_FROM_DATABASE=Universal Instruments Corp. + +OUI:0006A0* + ID_OUI_FROM_DATABASE=Mx Imaging + +OUI:00069F* + ID_OUI_FROM_DATABASE=Kuokoa Networks + +OUI:000699* + ID_OUI_FROM_DATABASE=Vida Design Co. + +OUI:000693* + ID_OUI_FROM_DATABASE=Flexus Computer Technology, Inc. + +OUI:00069A* + ID_OUI_FROM_DATABASE=e & Tel + +OUI:00068D* + ID_OUI_FROM_DATABASE=SEPATON, Inc. + +OUI:000687* + ID_OUI_FROM_DATABASE=Omnitron Systems Technology, Inc. + +OUI:000680* + ID_OUI_FROM_DATABASE=Card Access, Inc. + +OUI:000539* + ID_OUI_FROM_DATABASE=A Brand New World in Sweden AB + +OUI:000526* + ID_OUI_FROM_DATABASE=IPAS GmbH + +OUI:00052D* + ID_OUI_FROM_DATABASE=Zoltrix International Limited + +OUI:00052C* + ID_OUI_FROM_DATABASE=Supreme Magic Corporation + +OUI:000520* + ID_OUI_FROM_DATABASE=Smartronix, Inc. + +OUI:00051A* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:000510* + ID_OUI_FROM_DATABASE=Infinite Shanghai Communication Terminals Ltd. + +OUI:000514* + ID_OUI_FROM_DATABASE=KDT Systems Co., Ltd. + +OUI:000509* + ID_OUI_FROM_DATABASE=AVOC Nishimura Ltd. + +OUI:000503* + ID_OUI_FROM_DATABASE=ICONAG + +OUI:00050A* + ID_OUI_FROM_DATABASE=ICS Spa + +OUI:0004FF* + ID_OUI_FROM_DATABASE=Acronet Co., Ltd. + +OUI:000500* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000641* + ID_OUI_FROM_DATABASE=ITCN + +OUI:00063D* + ID_OUI_FROM_DATABASE=Microwave Data Systems Inc. + +OUI:000631* + ID_OUI_FROM_DATABASE=Calix + +OUI:000630* + ID_OUI_FROM_DATABASE=Adtranz Sweden + +OUI:000637* + ID_OUI_FROM_DATABASE=Toptrend-Meta Information (ShenZhen) Inc. + +OUI:000620* + ID_OUI_FROM_DATABASE=Serial System Ltd. + +OUI:00061A* + ID_OUI_FROM_DATABASE=Zetari Inc. + +OUI:00060C* + ID_OUI_FROM_DATABASE=Melco Industries, Inc. + +OUI:000614* + ID_OUI_FROM_DATABASE=Prism Holdings + +OUI:000606* + ID_OUI_FROM_DATABASE=RapidWAN, Inc. + +OUI:000677* + ID_OUI_FROM_DATABASE=SICK AG + +OUI:000673* + ID_OUI_FROM_DATABASE=TKH Security Solutions USA + +OUI:000666* + ID_OUI_FROM_DATABASE=Roving Networks + +OUI:00066D* + ID_OUI_FROM_DATABASE=Compuprint S.P.A. + +OUI:00066C* + ID_OUI_FROM_DATABASE=Robinson Corporation + +OUI:000653* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00065A* + ID_OUI_FROM_DATABASE=Strix Systems + +OUI:00064D* + ID_OUI_FROM_DATABASE=Sencore + +OUI:000660* + ID_OUI_FROM_DATABASE=NADEX Co., Ltd. + +OUI:0005B8* + ID_OUI_FROM_DATABASE=Electronic Design Associates, Inc. + +OUI:0005BF* + ID_OUI_FROM_DATABASE=JustEzy Technology, Inc. + +OUI:0005AE* + ID_OUI_FROM_DATABASE=Mediaport USA + +OUI:0005B2* + ID_OUI_FROM_DATABASE=Medison Co., Ltd. + +OUI:00059E* + ID_OUI_FROM_DATABASE=Zinwell Corporation + +OUI:0005A5* + ID_OUI_FROM_DATABASE=KOTT + +OUI:000598* + ID_OUI_FROM_DATABASE=CRONOS S.r.l. + +OUI:0005A4* + ID_OUI_FROM_DATABASE=Lucid Voice Ltd. + +OUI:000592* + ID_OUI_FROM_DATABASE=Pultek Corp. + +OUI:00058B* + ID_OUI_FROM_DATABASE=IPmental, Inc. + +OUI:00058C* + ID_OUI_FROM_DATABASE=Opentech Inc. + +OUI:00037E* + ID_OUI_FROM_DATABASE=PORTech Communications, Inc. + +OUI:000383* + ID_OUI_FROM_DATABASE=Metera Networks, Inc. + +OUI:000377* + ID_OUI_FROM_DATABASE=Gigabit Wireless + +OUI:00037B* + ID_OUI_FROM_DATABASE=IDEC IZUMI Corporation + +OUI:00036B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000372* + ID_OUI_FROM_DATABASE=ULAN + +OUI:000367* + ID_OUI_FROM_DATABASE=Jasmine Networks, Inc. + +OUI:00036A* + ID_OUI_FROM_DATABASE=Mainnet, Ltd. + +OUI:000364* + ID_OUI_FROM_DATABASE=Scenix Semiconductor, Inc. + +OUI:00035F* + ID_OUI_FROM_DATABASE=Prüftechnik Condition Monitoring GmbH & Co. KG + +OUI:00035C* + ID_OUI_FROM_DATABASE=Saint Song Corp. + +OUI:00034D* + ID_OUI_FROM_DATABASE=Chiaro Networks, Ltd. + +OUI:0003FA* + ID_OUI_FROM_DATABASE=TiMetra Networks + +OUI:0003F5* + ID_OUI_FROM_DATABASE=Chip2Chip + +OUI:0003EE* + ID_OUI_FROM_DATABASE=MKNet Corporation + +OUI:0003E8* + ID_OUI_FROM_DATABASE=Wavelength Digital Limited + +OUI:0003E3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0003DC* + ID_OUI_FROM_DATABASE=Lexar Media, Inc. + +OUI:0003D7* + ID_OUI_FROM_DATABASE=NextNet Wireless, Inc. + +OUI:0003D4* + ID_OUI_FROM_DATABASE=Alloptic, Inc. + +OUI:00030B* + ID_OUI_FROM_DATABASE=Hunter Technology, Inc. + +OUI:0003D0* + ID_OUI_FROM_DATABASE=KOANKEISO Co., Ltd. + +OUI:0003C9* + ID_OUI_FROM_DATABASE=TECOM Co., Ltd. + +OUI:0003C4* + ID_OUI_FROM_DATABASE=Tomra Systems ASA + +OUI:0004FA* + ID_OUI_FROM_DATABASE=NBS Technologies Inc. + +OUI:0004F9* + ID_OUI_FROM_DATABASE=Xtera Communications, Inc. + +OUI:0004F3* + ID_OUI_FROM_DATABASE=FS FORTH-SYSTEME GmbH + +OUI:0004E7* + ID_OUI_FROM_DATABASE=Lightpointe Communications, Inc + +OUI:0004ED* + ID_OUI_FROM_DATABASE=Billion Electric Co., Ltd. + +OUI:0004DD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0004D6* + ID_OUI_FROM_DATABASE=Takagi Industrial Co., Ltd. + +OUI:0004D0* + ID_OUI_FROM_DATABASE=Softlink s.r.o. + +OUI:0004CA* + ID_OUI_FROM_DATABASE=FreeMs Corp. + +OUI:0004BE* + ID_OUI_FROM_DATABASE=OptXCon, Inc. + +OUI:0004C3* + ID_OUI_FROM_DATABASE=CASTOR Informatique + +OUI:0004C4* + ID_OUI_FROM_DATABASE=Allen & Heath Limited + +OUI:0004B7* + ID_OUI_FROM_DATABASE=AMB i.t. Holding + +OUI:0004B1* + ID_OUI_FROM_DATABASE=Signal Technology, Inc. + +OUI:0004AD* + ID_OUI_FROM_DATABASE=Malibu Networks + +OUI:0004AA* + ID_OUI_FROM_DATABASE=Jetstream Communications + +OUI:00049D* + ID_OUI_FROM_DATABASE=Ipanema Technologies + +OUI:000497* + ID_OUI_FROM_DATABASE=MacroSystem Digital Video AG + +OUI:000490* + ID_OUI_FROM_DATABASE=Optical Access + +OUI:00048B* + ID_OUI_FROM_DATABASE=Poscon Corporation + +OUI:000341* + ID_OUI_FROM_DATABASE=Axon Digital Design + +OUI:00033E* + ID_OUI_FROM_DATABASE=Tateyama System Laboratory Co., Ltd. + +OUI:00033A* + ID_OUI_FROM_DATABASE=Silicon Wave, Inc. + +OUI:000333* + ID_OUI_FROM_DATABASE=Digitel Co., Ltd. + +OUI:00032B* + ID_OUI_FROM_DATABASE=GAI Datenfunksysteme GmbH + +OUI:000327* + ID_OUI_FROM_DATABASE=ACT'L + +OUI:00032E* + ID_OUI_FROM_DATABASE=Scope Information Management, Ltd. + +OUI:000322* + ID_OUI_FROM_DATABASE=IDIS Co., Ltd. + +OUI:00031E* + ID_OUI_FROM_DATABASE=Optranet, Inc. + +OUI:00B052* + ID_OUI_FROM_DATABASE=Atheros Communications + +OUI:000319* + ID_OUI_FROM_DATABASE=Infineon AG + +OUI:000316* + ID_OUI_FROM_DATABASE=Nobell Communications, Inc. + +OUI:000312* + ID_OUI_FROM_DATABASE=TR-Systemtechnik GmbH + +OUI:000447* + ID_OUI_FROM_DATABASE=Acrowave Systems Co., Ltd. + +OUI:00043B* + ID_OUI_FROM_DATABASE=Lava Computer Mfg., Inc. + +OUI:000440* + ID_OUI_FROM_DATABASE=cyberPIXIE, Inc. + +OUI:00043A* + ID_OUI_FROM_DATABASE=Intelligent Telecommunications, Inc. + +OUI:000434* + ID_OUI_FROM_DATABASE=Accelent Systems, Inc. + +OUI:00042D* + ID_OUI_FROM_DATABASE=Sarian Systems, Ltd. + +OUI:00042E* + ID_OUI_FROM_DATABASE=Netous Technologies, Ltd. + +OUI:000428* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000421* + ID_OUI_FROM_DATABASE=Ocular Networks + +OUI:000417* + ID_OUI_FROM_DATABASE=ELAU AG + +OUI:000411* + ID_OUI_FROM_DATABASE=Inkra Networks, Inc. + +OUI:00040B* + ID_OUI_FROM_DATABASE=3COM EUROPE LTD. + +OUI:000404* + ID_OUI_FROM_DATABASE=Makino Milling Machine Co., Ltd. + +OUI:000481* + ID_OUI_FROM_DATABASE=Econolite Control Products, Inc. + +OUI:000486* + ID_OUI_FROM_DATABASE=ITTC, University of Kansas + +OUI:000477* + ID_OUI_FROM_DATABASE=Scalant Systems, Inc. + +OUI:000476* + ID_OUI_FROM_DATABASE=3 Com Corporation + +OUI:000469* + ID_OUI_FROM_DATABASE=Innocom, Inc. + +OUI:000470* + ID_OUI_FROM_DATABASE=ipUnplugged AB + +OUI:00046A* + ID_OUI_FROM_DATABASE=Navini Networks + +OUI:000464* + ID_OUI_FROM_DATABASE=Pulse-Link Inc + +OUI:00045D* + ID_OUI_FROM_DATABASE=BEKA Elektronik + +OUI:000457* + ID_OUI_FROM_DATABASE=Universal Access Technology, Inc. + +OUI:000451* + ID_OUI_FROM_DATABASE=Medrad, Inc. + +OUI:0003C1* + ID_OUI_FROM_DATABASE=Packet Dynamics Ltd + +OUI:0003BD* + ID_OUI_FROM_DATABASE=OmniCluster Technologies, Inc. + +OUI:0003B8* + ID_OUI_FROM_DATABASE=NetKit Solutions, LLC + +OUI:0003B6* + ID_OUI_FROM_DATABASE=QSI Corporation + +OUI:0003A6* + ID_OUI_FROM_DATABASE=Traxit Technology, Inc. + +OUI:0003AB* + ID_OUI_FROM_DATABASE=Bridge Information Systems + +OUI:0003A3* + ID_OUI_FROM_DATABASE=MAVIX, Ltd. + +OUI:00039F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00039A* + ID_OUI_FROM_DATABASE=SiConnect + +OUI:00038C* + ID_OUI_FROM_DATABASE=Total Impact + +OUI:000384* + ID_OUI_FROM_DATABASE=AETA + +OUI:000387* + ID_OUI_FROM_DATABASE=Blaze Network Products + +OUI:000306* + ID_OUI_FROM_DATABASE=Fusion In Tech Co., Ltd. + +OUI:000303* + ID_OUI_FROM_DATABASE=JAMA Electronics Co., Ltd. + +OUI:0002FF* + ID_OUI_FROM_DATABASE=Handan BroadInfoCom + +OUI:0002F3* + ID_OUI_FROM_DATABASE=Media Serve Co., Ltd. + +OUI:0002FA* + ID_OUI_FROM_DATABASE=DX Antenna Co., Ltd. + +OUI:0002ED* + ID_OUI_FROM_DATABASE=DXO Telecom Co., Ltd. + +OUI:0002E5* + ID_OUI_FROM_DATABASE=Timeware Ltd. + +OUI:0002E8* + ID_OUI_FROM_DATABASE=E.D.&A. + +OUI:0002DC* + ID_OUI_FROM_DATABASE=Fujitsu General Limited + +OUI:0002E1* + ID_OUI_FROM_DATABASE=Integrated Network Corporation + +OUI:0002D5* + ID_OUI_FROM_DATABASE=ACR + +OUI:0002CE* + ID_OUI_FROM_DATABASE=FoxJet, Inc. + +OUI:00B0DB* + ID_OUI_FROM_DATABASE=Nextcell, Inc. + +OUI:00B08E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00B01C* + ID_OUI_FROM_DATABASE=Westport Technologies + +OUI:00B02D* + ID_OUI_FROM_DATABASE=ViaGate Technologies, Inc. + +OUI:00B03B* + ID_OUI_FROM_DATABASE=HiQ Networks + +OUI:0030A9* + ID_OUI_FROM_DATABASE=Netiverse, Inc. + +OUI:00B0F0* + ID_OUI_FROM_DATABASE=CALY NETWORKS + +OUI:00B086* + ID_OUI_FROM_DATABASE=LocSoft Limited + +OUI:0030C4* + ID_OUI_FROM_DATABASE=Canon Imaging Systems Inc. + +OUI:00309D* + ID_OUI_FROM_DATABASE=Nimble Microsystems, Inc. + +OUI:003037* + ID_OUI_FROM_DATABASE=Packard Bell Nec Services + +OUI:00302E* + ID_OUI_FROM_DATABASE=Hoft & Wessel AG + +OUI:00301B* + ID_OUI_FROM_DATABASE=SHUTTLE, INC. + +OUI:003028* + ID_OUI_FROM_DATABASE=FASE Saldatura srl + +OUI:0030FB* + ID_OUI_FROM_DATABASE=AZS Technology AG + +OUI:003048* + ID_OUI_FROM_DATABASE=Supermicro Computer, Inc. + +OUI:0001DA* + ID_OUI_FROM_DATABASE=WINCOMM Corporation + +OUI:0001E1* + ID_OUI_FROM_DATABASE=Kinpo Electronics, Inc. + +OUI:0001DD* + ID_OUI_FROM_DATABASE=Avail Networks + +OUI:0001CE* + ID_OUI_FROM_DATABASE=Custom Micro Products, Ltd. + +OUI:0001CA* + ID_OUI_FROM_DATABASE=Geocast Network Systems, Inc. + +OUI:0001B8* + ID_OUI_FROM_DATABASE=Netsensity, Inc. + +OUI:0001BD* + ID_OUI_FROM_DATABASE=Peterson Electro-Musical Products, Inc. + +OUI:0001B4* + ID_OUI_FROM_DATABASE=Wayport, Inc. + +OUI:0001C3* + ID_OUI_FROM_DATABASE=Acromag, Inc. + +OUI:0001BF* + ID_OUI_FROM_DATABASE=Teleforce Co., Ltd. + +OUI:0001AD* + ID_OUI_FROM_DATABASE=Coach Master International d.b.a. CMI Worldwide, Inc. + +OUI:00017E* + ID_OUI_FROM_DATABASE=ADTEK System Science Co., Ltd. + +OUI:00018A* + ID_OUI_FROM_DATABASE=ROI COMPUTER AG + +OUI:000119* + ID_OUI_FROM_DATABASE=RTUnet (Australia) + +OUI:000125* + ID_OUI_FROM_DATABASE=YAESU MUSEN CO., LTD. + +OUI:000121* + ID_OUI_FROM_DATABASE=Watchguard Technologies, Inc. + +OUI:000128* + ID_OUI_FROM_DATABASE=EnjoyWeb, Inc. + +OUI:000106* + ID_OUI_FROM_DATABASE=Tews Datentechnik GmbH + +OUI:000112* + ID_OUI_FROM_DATABASE=Shark Multimedia Inc. + +OUI:000102* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:000115* + ID_OUI_FROM_DATABASE=EXTRATECH CORPORATION + +OUI:000109* + ID_OUI_FROM_DATABASE=Nagano Japan Radio Co., Ltd. + +OUI:081443* + ID_OUI_FROM_DATABASE=UNIBRAIN S.A. + +OUI:00B0F5* + ID_OUI_FROM_DATABASE=NetWorth Technologies, Inc. + +OUI:00B019* + ID_OUI_FROM_DATABASE=UTC CCS + +OUI:00B02A* + ID_OUI_FROM_DATABASE=ORSYS GmbH + +OUI:00B0AE* + ID_OUI_FROM_DATABASE=Symmetricom + +OUI:000181* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00018D* + ID_OUI_FROM_DATABASE=AudeSi Technologies + +OUI:00019A* + ID_OUI_FROM_DATABASE=LEUNIG GmbH + +OUI:000193* + ID_OUI_FROM_DATABASE=Hanbyul Telecom Co., Ltd. + +OUI:0001A2* + ID_OUI_FROM_DATABASE=Logical Co., Ltd. + +OUI:000196* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0001A6* + ID_OUI_FROM_DATABASE=Scientific-Atlanta Arcodan A/S + +OUI:000172* + ID_OUI_FROM_DATABASE=TechnoLand Co., LTD. + +OUI:00303F* + ID_OUI_FROM_DATABASE=TurboComm Tech Inc. + +OUI:003073* + ID_OUI_FROM_DATABASE=International Microsystems, In + +OUI:00014D* + ID_OUI_FROM_DATABASE=Shin Kin Enterprises Co., Ltd + +OUI:00016B* + ID_OUI_FROM_DATABASE=LightChip, Inc. + +OUI:000167* + ID_OUI_FROM_DATABASE=HIOKI E.E. CORPORATION + +OUI:000215* + ID_OUI_FROM_DATABASE=Cotas Computer Technology A/B + +OUI:000211* + ID_OUI_FROM_DATABASE=Nature Worldwide Technology Corp. + +OUI:000209* + ID_OUI_FROM_DATABASE=Shenzhen SED Information Technology Co., Ltd. + +OUI:000205* + ID_OUI_FROM_DATABASE=Hitachi Denshi, Ltd. + +OUI:000202* + ID_OUI_FROM_DATABASE=Amino Communications, Ltd. + +OUI:0001F6* + ID_OUI_FROM_DATABASE=Association of Musical Electronics Industry + +OUI:0001ED* + ID_OUI_FROM_DATABASE=SETA Corp. + +OUI:0001E9* + ID_OUI_FROM_DATABASE=Litton Marine Systems B.V. + +OUI:0002C6* + ID_OUI_FROM_DATABASE=Data Track Technology PLC + +OUI:0002C2* + ID_OUI_FROM_DATABASE=Net Vision Telecom + +OUI:0002B9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0002B4* + ID_OUI_FROM_DATABASE=DAPHNE + +OUI:0002AD* + ID_OUI_FROM_DATABASE=HOYA Corporation + +OUI:0002A6* + ID_OUI_FROM_DATABASE=Effinet Systems Co., Ltd. + +OUI:0002A1* + ID_OUI_FROM_DATABASE=World Wide Packets + +OUI:00029B* + ID_OUI_FROM_DATABASE=Kreatel Communications AB + +OUI:00029E* + ID_OUI_FROM_DATABASE=Information Equipment Co., Ltd. + +OUI:000296* + ID_OUI_FROM_DATABASE=Lectron Co,. Ltd. + +OUI:00028F* + ID_OUI_FROM_DATABASE=Globetek, Inc. + +OUI:000289* + ID_OUI_FROM_DATABASE=DNE Technologies + +OUI:000285* + ID_OUI_FROM_DATABASE=Riverstone Networks + +OUI:00027E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000280* + ID_OUI_FROM_DATABASE=Mu Net, Inc. + +OUI:000279* + ID_OUI_FROM_DATABASE=Control Applications, Ltd. + +OUI:000272* + ID_OUI_FROM_DATABASE=CC&C Technologies, Inc. + +OUI:00026B* + ID_OUI_FROM_DATABASE=BCM Computers Co., Ltd. + +OUI:00026D* + ID_OUI_FROM_DATABASE=Adept Telecom + +OUI:000262* + ID_OUI_FROM_DATABASE=Soyo Group Soyo Com Tech Co., Ltd + +OUI:000260* + ID_OUI_FROM_DATABASE=Accordion Networks, Inc. + +OUI:00025B* + ID_OUI_FROM_DATABASE=Cambridge Silicon Radio + +OUI:000087* + ID_OUI_FROM_DATABASE=HITACHI, LTD. + +OUI:000252* + ID_OUI_FROM_DATABASE=Carrier Corporation + +OUI:00024B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000246* + ID_OUI_FROM_DATABASE=All-Win Tech Co., Ltd. + +OUI:00017A* + ID_OUI_FROM_DATABASE=Chengdu Maipu Electric Industrial Co., Ltd. + +OUI:000235* + ID_OUI_FROM_DATABASE=Paragon Networks International + +OUI:000238* + ID_OUI_FROM_DATABASE=Serome Technology, Inc. + +OUI:000230* + ID_OUI_FROM_DATABASE=Intersoft Electronics + +OUI:000229* + ID_OUI_FROM_DATABASE=Adtec Corporation + +OUI:000225* + ID_OUI_FROM_DATABASE=One Stop Systems + +OUI:00021C* + ID_OUI_FROM_DATABASE=Network Elements, Inc. + +OUI:000221* + ID_OUI_FROM_DATABASE=DSP Application, Ltd. + +OUI:00016E* + ID_OUI_FROM_DATABASE=Conklin Corporation + +OUI:00015B* + ID_OUI_FROM_DATABASE=ITALTEL S.p.A/RF-UP-I + +OUI:000154* + ID_OUI_FROM_DATABASE=G3M Corporation + +OUI:000150* + ID_OUI_FROM_DATABASE=GILAT COMMUNICATIONS, LTD. + +OUI:00012E* + ID_OUI_FROM_DATABASE=PC Partner Ltd. + +OUI:00013A* + ID_OUI_FROM_DATABASE=SHELCAD COMMUNICATIONS, LTD. + +OUI:000141* + ID_OUI_FROM_DATABASE=CABLE PRINT + +OUI:000131* + ID_OUI_FROM_DATABASE=Bosch Security Systems, Inc. + +OUI:00013D* + ID_OUI_FROM_DATABASE=RiscStation Ltd. + +OUI:000149* + ID_OUI_FROM_DATABASE=T.D.T. Transfer Data Test GmbH + +OUI:00D047* + ID_OUI_FROM_DATABASE=XN TECHNOLOGIES + +OUI:00D018* + ID_OUI_FROM_DATABASE=QWES. COM, INC. + +OUI:00D048* + ID_OUI_FROM_DATABASE=ECTON, INC. + +OUI:00D028* + ID_OUI_FROM_DATABASE=Harmonic, Inc + +OUI:00D02F* + ID_OUI_FROM_DATABASE=VLSI TECHNOLOGY INC. + +OUI:00D025* + ID_OUI_FROM_DATABASE=XROSSTECH, INC. + +OUI:00D085* + ID_OUI_FROM_DATABASE=OTIS ELEVATOR COMPANY + +OUI:00D077* + ID_OUI_FROM_DATABASE=LUCENT TECHNOLOGIES + +OUI:00D093* + ID_OUI_FROM_DATABASE=TQ - COMPONENTS GMBH + +OUI:00D013* + ID_OUI_FROM_DATABASE=PRIMEX AEROSPACE COMPANY + +OUI:00D056* + ID_OUI_FROM_DATABASE=SOMAT CORPORATION + +OUI:00D017* + ID_OUI_FROM_DATABASE=SYNTECH INFORMATION CO., LTD. + +OUI:00D036* + ID_OUI_FROM_DATABASE=TECHNOLOGY ATLANTA CORP. + +OUI:00D0D6* + ID_OUI_FROM_DATABASE=AETHRA TELECOMUNICAZIONI + +OUI:003078* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003003* + ID_OUI_FROM_DATABASE=Phasys Ltd. + +OUI:0030D5* + ID_OUI_FROM_DATABASE=DResearch GmbH + +OUI:0030CE* + ID_OUI_FROM_DATABASE=Zaffire + +OUI:003095* + ID_OUI_FROM_DATABASE=Procomp Informatics, Ltd. + +OUI:003055* + ID_OUI_FROM_DATABASE=Renesas Technology America, Inc. + +OUI:0030B0* + ID_OUI_FROM_DATABASE=Convergenet Technologies + +OUI:0030CC* + ID_OUI_FROM_DATABASE=Tenor Networks, Inc. + +OUI:003013* + ID_OUI_FROM_DATABASE=NEC Corporation + +OUI:003061* + ID_OUI_FROM_DATABASE=MobyTEL + +OUI:00D0AB* + ID_OUI_FROM_DATABASE=DELTAKABEL TELECOM CV + +OUI:00D0A8* + ID_OUI_FROM_DATABASE=NETWORK ENGINES, INC. + +OUI:00D01C* + ID_OUI_FROM_DATABASE=SBS TECHNOLOGIES, + +OUI:00D0C0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D051* + ID_OUI_FROM_DATABASE=O2 MICRO, INC. + +OUI:00D06D* + ID_OUI_FROM_DATABASE=ACRISON, INC. + +OUI:0050A1* + ID_OUI_FROM_DATABASE=CARLO GAVAZZI, INC. + +OUI:00D06C* + ID_OUI_FROM_DATABASE=SHAREWAVE, INC. + +OUI:00D03A* + ID_OUI_FROM_DATABASE=ZONEWORX, INC. + +OUI:0050C1* + ID_OUI_FROM_DATABASE=GEMFLEX NETWORKS, LTD. + +OUI:0050FB* + ID_OUI_FROM_DATABASE=VSK ELECTRONICS + +OUI:005033* + ID_OUI_FROM_DATABASE=MAYAN NETWORKS + +OUI:0030A0* + ID_OUI_FROM_DATABASE=TYCO SUBMARINE SYSTEMS, LTD. + +OUI:0030CB* + ID_OUI_FROM_DATABASE=OMNI FLOW COMPUTERS, INC. + +OUI:00306B* + ID_OUI_FROM_DATABASE=CMOS SYSTEMS, INC. + +OUI:003068* + ID_OUI_FROM_DATABASE=CYBERNETICS TECH. CO., LTD. + +OUI:0030E3* + ID_OUI_FROM_DATABASE=SEDONA NETWORKS CORP. + +OUI:00D007* + ID_OUI_FROM_DATABASE=MIC ASSOCIATES, INC. + +OUI:00D07F* + ID_OUI_FROM_DATABASE=STRATEGY & TECHNOLOGY, LIMITED + +OUI:003085* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003026* + ID_OUI_FROM_DATABASE=HeiTel Digital Video GmbH + +OUI:0030A6* + ID_OUI_FROM_DATABASE=VIANET TECHNOLOGIES, LTD. + +OUI:003047* + ID_OUI_FROM_DATABASE=NISSEI ELECTRIC CO., LTD. + +OUI:00D0FC* + ID_OUI_FROM_DATABASE=GRANITE MICROSYSTEMS + +OUI:00D042* + ID_OUI_FROM_DATABASE=MAHLO GMBH & CO. UG + +OUI:00D046* + ID_OUI_FROM_DATABASE=DOLBY LABORATORIES, INC. + +OUI:00D0BA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D0BC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D0D8* + ID_OUI_FROM_DATABASE=3Com Corporation + +OUI:00D06B* + ID_OUI_FROM_DATABASE=SR TELECOM INC. + +OUI:0030AA* + ID_OUI_FROM_DATABASE=AXUS MICROSYSTEMS, INC. + +OUI:003043* + ID_OUI_FROM_DATABASE=IDREAM TECHNOLOGIES, PTE. LTD. + +OUI:003010* + ID_OUI_FROM_DATABASE=VISIONETICS INTERNATIONAL + +OUI:003096* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:003084* + ID_OUI_FROM_DATABASE=ALLIED TELESYN INTERNAIONAL + +OUI:0030CF* + ID_OUI_FROM_DATABASE=TWO TECHNOLOGIES, INC. + +OUI:00D0E3* + ID_OUI_FROM_DATABASE=ELE-CHEM ENGINEERING CO., LTD. + +OUI:00D0ED* + ID_OUI_FROM_DATABASE=XIOX + +OUI:00D0C2* + ID_OUI_FROM_DATABASE=BALTHAZAR TECHNOLOGY AB + +OUI:00D0FB* + ID_OUI_FROM_DATABASE=TEK MICROSYSTEMS, INCORPORATED + +OUI:00D082* + ID_OUI_FROM_DATABASE=IOWAVE INC. + +OUI:00D0AD* + ID_OUI_FROM_DATABASE=TL INDUSTRIES + +OUI:00D0DB* + ID_OUI_FROM_DATABASE=MCQUAY INTERNATIONAL + +OUI:00D06A* + ID_OUI_FROM_DATABASE=LINKUP SYSTEMS CORPORATION + +OUI:00D065* + ID_OUI_FROM_DATABASE=TOKO ELECTRIC + +OUI:00D08F* + ID_OUI_FROM_DATABASE=ARDENT TECHNOLOGIES, INC. + +OUI:00D0E7* + ID_OUI_FROM_DATABASE=VCON TELECOMMUNICATION LTD. + +OUI:00D087* + ID_OUI_FROM_DATABASE=MICROFIRST INC. + +OUI:00D008* + ID_OUI_FROM_DATABASE=MACTELL CORPORATION + +OUI:003005* + ID_OUI_FROM_DATABASE=Fujitsu Siemens Computers + +OUI:00304E* + ID_OUI_FROM_DATABASE=BUSTEC PRODUCTION LTD. + +OUI:0030E0* + ID_OUI_FROM_DATABASE=OXFORD SEMICONDUCTOR LTD. + +OUI:0030A1* + ID_OUI_FROM_DATABASE=WEBGATE Inc. + +OUI:00303D* + ID_OUI_FROM_DATABASE=IVA CORPORATION + +OUI:0030C3* + ID_OUI_FROM_DATABASE=FLUECKIGER ELEKTRONIK AG + +OUI:009047* + ID_OUI_FROM_DATABASE=GIGA FAST E. LTD. + +OUI:0090CB* + ID_OUI_FROM_DATABASE=Wireless OnLine, Inc. + +OUI:00903F* + ID_OUI_FROM_DATABASE=AZTEC RADIOMEDIA + +OUI:001043* + ID_OUI_FROM_DATABASE=A2 CORPORATION + +OUI:00108D* + ID_OUI_FROM_DATABASE=Johnson Controls, Inc. + +OUI:00108E* + ID_OUI_FROM_DATABASE=HUGH SYMONS CONCEPT Technologies Ltd. + +OUI:001052* + ID_OUI_FROM_DATABASE=METTLER-TOLEDO (ALBSTADT) GMBH + +OUI:00100E* + ID_OUI_FROM_DATABASE=MICRO LINEAR COPORATION + +OUI:0010D7* + ID_OUI_FROM_DATABASE=ARGOSY RESEARCH INC. + +OUI:001059* + ID_OUI_FROM_DATABASE=DIABLO RESEARCH CO. LLC + +OUI:0010B6* + ID_OUI_FROM_DATABASE=ENTRATA COMMUNICATIONS CORP. + +OUI:001019* + ID_OUI_FROM_DATABASE=SIRONA DENTAL SYSTEMS GmbH & Co. KG + +OUI:001013* + ID_OUI_FROM_DATABASE=Kontron America, Inc. + +OUI:0090A4* + ID_OUI_FROM_DATABASE=ALTIGA NETWORKS + +OUI:00906C* + ID_OUI_FROM_DATABASE=Sartorius Hamburg GmbH + +OUI:0090FC* + ID_OUI_FROM_DATABASE=NETWORK COMPUTING DEVICES + +OUI:0090A3* + ID_OUI_FROM_DATABASE=Corecess Inc. + +OUI:009022* + ID_OUI_FROM_DATABASE=IVEX + +OUI:0090A5* + ID_OUI_FROM_DATABASE=SPECTRA LOGIC + +OUI:0090BA* + ID_OUI_FROM_DATABASE=VALID NETWORKS, INC. + +OUI:0090EE* + ID_OUI_FROM_DATABASE=PERSONAL COMMUNICATIONS TECHNOLOGIES + +OUI:0090CD* + ID_OUI_FROM_DATABASE=ENT-EMPRESA NACIONAL DE TELECOMMUNICACOES, S.A. + +OUI:0090D0* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:009075* + ID_OUI_FROM_DATABASE=NEC DO BRASIL S.A. + +OUI:00902E* + ID_OUI_FROM_DATABASE=NAMCO LIMITED + +OUI:0090A0* + ID_OUI_FROM_DATABASE=8X8 INC. + +OUI:00907C* + ID_OUI_FROM_DATABASE=DIGITALCAST, INC. + +OUI:0090DF* + ID_OUI_FROM_DATABASE=MITSUBISHI CHEMICAL AMERICA, INC. + +OUI:009023* + ID_OUI_FROM_DATABASE=ZILOG INC. + +OUI:00908A* + ID_OUI_FROM_DATABASE=BAYLY COMMUNICATIONS, INC. + +OUI:009063* + ID_OUI_FROM_DATABASE=COHERENT COMMUNICATIONS SYSTEMS CORPORATION + +OUI:009041* + ID_OUI_FROM_DATABASE=APPLIED DIGITAL ACCESS + +OUI:0090D8* + ID_OUI_FROM_DATABASE=WHITECROSS SYSTEMS + +OUI:009011* + ID_OUI_FROM_DATABASE=WAVTrace, Inc. + +OUI:009040* + ID_OUI_FROM_DATABASE=Siemens Network Convergence LLC + +OUI:0090C7* + ID_OUI_FROM_DATABASE=ICOM INC. + +OUI:009035* + ID_OUI_FROM_DATABASE=ALPHA TELECOM, INC. + +OUI:009087* + ID_OUI_FROM_DATABASE=ITIS + +OUI:00906E* + ID_OUI_FROM_DATABASE=PRAXON, INC. + +OUI:009039* + ID_OUI_FROM_DATABASE=SHASTA NETWORKS + +OUI:00909A* + ID_OUI_FROM_DATABASE=ONE WORLD SYSTEMS, INC. + +OUI:009053* + ID_OUI_FROM_DATABASE=DAEWOO ELECTRONICS CO., LTD. + +OUI:00909E* + ID_OUI_FROM_DATABASE=Critical IO, LLC + +OUI:0090C2* + ID_OUI_FROM_DATABASE=JK microsystems, Inc. + +OUI:009091* + ID_OUI_FROM_DATABASE=DigitalScape, Inc. + +OUI:0090ED* + ID_OUI_FROM_DATABASE=CENTRAL SYSTEM RESEARCH CO., LTD. + +OUI:00901B* + ID_OUI_FROM_DATABASE=DIGITAL CONTROLS + +OUI:00905C* + ID_OUI_FROM_DATABASE=EDMI + +OUI:0090D2* + ID_OUI_FROM_DATABASE=ARTEL VIDEO SYSTEMS + +OUI:00508C* + ID_OUI_FROM_DATABASE=RSI SYSTEMS + +OUI:00502D* + ID_OUI_FROM_DATABASE=ACCEL, INC. + +OUI:0050B8* + ID_OUI_FROM_DATABASE=INOVA COMPUTERS GMBH & CO. KG + +OUI:00503A* + ID_OUI_FROM_DATABASE=DATONG ELECTRONICS LTD. + +OUI:00508E* + ID_OUI_FROM_DATABASE=OPTIMATION, INC. + +OUI:0050BB* + ID_OUI_FROM_DATABASE=CMS TECHNOLOGIES + +OUI:005051* + ID_OUI_FROM_DATABASE=IWATSU ELECTRIC CO., LTD. + +OUI:0050BE* + ID_OUI_FROM_DATABASE=FAST MULTIMEDIA AG + +OUI:0050AD* + ID_OUI_FROM_DATABASE=CommUnique Wireless Corp. + +OUI:005016* + ID_OUI_FROM_DATABASE=SST/WOODHEAD INDUSTRIES + +OUI:005003* + ID_OUI_FROM_DATABASE=Xrite Inc + +OUI:005023* + ID_OUI_FROM_DATABASE=PG DESIGN ELECTRONICS, INC. + +OUI:005039* + ID_OUI_FROM_DATABASE=MARINER NETWORKS + +OUI:00505A* + ID_OUI_FROM_DATABASE=NETWORK ALCHEMY, INC. + +OUI:005071* + ID_OUI_FROM_DATABASE=AIWA CO., LTD. + +OUI:009071* + ID_OUI_FROM_DATABASE=Applied Innovation Inc. + +OUI:009031* + ID_OUI_FROM_DATABASE=MYSTICOM, LTD. + +OUI:00901F* + ID_OUI_FROM_DATABASE=ADTEC PRODUCTIONS, INC. + +OUI:009081* + ID_OUI_FROM_DATABASE=ALOHA NETWORKS, INC. + +OUI:0090B3* + ID_OUI_FROM_DATABASE=AGRANAT SYSTEMS + +OUI:00500D* + ID_OUI_FROM_DATABASE=SATORI ELECTORIC CO., LTD. + +OUI:0050EC* + ID_OUI_FROM_DATABASE=OLICOM A/S + +OUI:005083* + ID_OUI_FROM_DATABASE=GILBARCO, INC. + +OUI:0050CF* + ID_OUI_FROM_DATABASE=VANLINK COMMUNICATION TECHNOLOGY RESEARCH INSTITUTE + +OUI:005008* + ID_OUI_FROM_DATABASE=TIVA MICROCOMPUTER CORP. (TMC) + +OUI:005001* + ID_OUI_FROM_DATABASE=YAMASHITA SYSTEMS CORP. + +OUI:0050B5* + ID_OUI_FROM_DATABASE=FICHET-BAUCHE + +OUI:0050B0* + ID_OUI_FROM_DATABASE=TECHNOLOGY ATLANTA CORPORATION + +OUI:00504E* + ID_OUI_FROM_DATABASE=SIERRA MONITOR CORP. + +OUI:00504D* + ID_OUI_FROM_DATABASE=Tokyo Electron Device Limited + +OUI:0050F7* + ID_OUI_FROM_DATABASE=VENTURE MANUFACTURING (SINGAPORE) LTD. + +OUI:005029* + ID_OUI_FROM_DATABASE=1394 PRINTER WORKING GROUP + +OUI:00E08D* + ID_OUI_FROM_DATABASE=PRESSURE SYSTEMS, INC. + +OUI:00E040* + ID_OUI_FROM_DATABASE=DeskStation Technology, Inc. + +OUI:00E0D6* + ID_OUI_FROM_DATABASE=COMPUTER & COMMUNICATION RESEARCH LAB. + +OUI:00E07E* + ID_OUI_FROM_DATABASE=WALT DISNEY IMAGINEERING + +OUI:00E094* + ID_OUI_FROM_DATABASE=OSAI SRL + +OUI:00E032* + ID_OUI_FROM_DATABASE=MISYS FINANCIAL SYSTEMS, LTD. + +OUI:00E06B* + ID_OUI_FROM_DATABASE=W&G SPECIAL PRODUCTS + +OUI:00E01C* + ID_OUI_FROM_DATABASE=Cradlepoint, Inc + +OUI:00E076* + ID_OUI_FROM_DATABASE=DEVELOPMENT CONCEPTS, INC. + +OUI:00E0A7* + ID_OUI_FROM_DATABASE=IPC INFORMATION SYSTEMS, INC. + +OUI:00E0A4* + ID_OUI_FROM_DATABASE=ESAOTE S.p.A. + +OUI:00E080* + ID_OUI_FROM_DATABASE=CONTROL RESOURCES CORPORATION + +OUI:00E0CC* + ID_OUI_FROM_DATABASE=HERO SYSTEMS, LTD. + +OUI:00E099* + ID_OUI_FROM_DATABASE=SAMSON AG + +OUI:0010E9* + ID_OUI_FROM_DATABASE=RAIDTEC LTD. + +OUI:001003* + ID_OUI_FROM_DATABASE=IMATRON, INC. + +OUI:00105A* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:0010A9* + ID_OUI_FROM_DATABASE=ADHOC TECHNOLOGIES + +OUI:000400* + ID_OUI_FROM_DATABASE=LEXMARK INTERNATIONAL, INC. + +OUI:00101A* + ID_OUI_FROM_DATABASE=PictureTel Corp. + +OUI:001097* + ID_OUI_FROM_DATABASE=WinNet Metropolitan Communications Systems, Inc. + +OUI:00106F* + ID_OUI_FROM_DATABASE=TRENTON TECHNOLOGY INC. + +OUI:0010DA* + ID_OUI_FROM_DATABASE=Kollmorgen Corp + +OUI:0010DF* + ID_OUI_FROM_DATABASE=RISE COMPUTER INC. + +OUI:00109E* + ID_OUI_FROM_DATABASE=AWARE, INC. + +OUI:001072* + ID_OUI_FROM_DATABASE=GVN TECHNOLOGIES, INC. + +OUI:00E019* + ID_OUI_FROM_DATABASE=ING. GIORDANO ELETTRONICA + +OUI:00E0D7* + ID_OUI_FROM_DATABASE=SUNSHINE ELECTRONICS, INC. + +OUI:00E0DA* + ID_OUI_FROM_DATABASE=Alcatel North America ESD + +OUI:00E068* + ID_OUI_FROM_DATABASE=MERRIMAC SYSTEMS INC. + +OUI:00E01D* + ID_OUI_FROM_DATABASE=WebTV NETWORKS, INC. + +OUI:00E01F* + ID_OUI_FROM_DATABASE=AVIDIA Systems, Inc. + +OUI:00E056* + ID_OUI_FROM_DATABASE=HOLONTECH CORPORATION + +OUI:00E0C9* + ID_OUI_FROM_DATABASE=AutomatedLogic Corporation + +OUI:00E030* + ID_OUI_FROM_DATABASE=MELITA INTERNATIONAL CORP. + +OUI:00E0BA* + ID_OUI_FROM_DATABASE=BERGHOF AUTOMATIONSTECHNIK GmbH + +OUI:00E0B2* + ID_OUI_FROM_DATABASE=TELMAX COMMUNICATIONS CORP. + +OUI:00E0EF* + ID_OUI_FROM_DATABASE=DIONEX + +OUI:00E0BD* + ID_OUI_FROM_DATABASE=INTERFACE SYSTEMS, INC. + +OUI:00E071* + ID_OUI_FROM_DATABASE=EPIS MICROCOMPUTER + +OUI:00E0A6* + ID_OUI_FROM_DATABASE=TELOGY NETWORKS, INC. + +OUI:00E026* + ID_OUI_FROM_DATABASE=Redlake MASD LLC + +OUI:00E0B8* + ID_OUI_FROM_DATABASE=GATEWAY 2000 + +OUI:00E088* + ID_OUI_FROM_DATABASE=LTX-Credence CORPORATION + +OUI:00E07C* + ID_OUI_FROM_DATABASE=METTLER-TOLEDO, INC. + +OUI:00E08C* + ID_OUI_FROM_DATABASE=NEOPARADIGM LABS, INC. + +OUI:00E061* + ID_OUI_FROM_DATABASE=EdgePoint Networks, Inc. + +OUI:00E06E* + ID_OUI_FROM_DATABASE=FAR SYSTEMS S.p.A. + +OUI:00E01B* + ID_OUI_FROM_DATABASE=SPHERE COMMUNICATIONS, INC. + +OUI:00E0AE* + ID_OUI_FROM_DATABASE=XAQTI CORPORATION + +OUI:00E0C8* + ID_OUI_FROM_DATABASE=VIRTUAL ACCESS, LTD. + +OUI:00101D* + ID_OUI_FROM_DATABASE=WINBOND ELECTRONICS CORP. + +OUI:00105F* + ID_OUI_FROM_DATABASE=ZODIAC DATA SYSTEMS + +OUI:0010CB* + ID_OUI_FROM_DATABASE=FACIT K.K. + +OUI:00108C* + ID_OUI_FROM_DATABASE=FUJITSU TELECOMMUNICATIONS EUROPE, LTD. + +OUI:001075* + ID_OUI_FROM_DATABASE=Segate Technology LLC + +OUI:001058* + ID_OUI_FROM_DATABASE=ArrowPoint Communications + +OUI:0010A8* + ID_OUI_FROM_DATABASE=RELIANCE COMPUTER CORP. + +OUI:0010AA* + ID_OUI_FROM_DATABASE=MEDIA4, INC. + +OUI:0010E8* + ID_OUI_FROM_DATABASE=TELOCITY, INCORPORATED + +OUI:001010* + ID_OUI_FROM_DATABASE=INITIO CORPORATION + +OUI:00E007* + ID_OUI_FROM_DATABASE=Avaya ECS Ltd + +OUI:001022* + ID_OUI_FROM_DATABASE=SatCom Media Corporation + +OUI:0010C7* + ID_OUI_FROM_DATABASE=DATA TRANSMISSION NETWORK + +OUI:001098* + ID_OUI_FROM_DATABASE=STARNET TECHNOLOGIES, INC. + +OUI:001096* + ID_OUI_FROM_DATABASE=TRACEWELL SYSTEMS, INC. + +OUI:001082* + ID_OUI_FROM_DATABASE=JNA TELECOMMUNICATIONS LIMITED + +OUI:001021* + ID_OUI_FROM_DATABASE=ENCANTO NETWORKS, INC. + +OUI:0010CE* + ID_OUI_FROM_DATABASE=VOLAMP, LTD. + +OUI:0010B2* + ID_OUI_FROM_DATABASE=COACTIVE AESTHETICS + +OUI:00109A* + ID_OUI_FROM_DATABASE=NETLINE + +OUI:0010EA* + ID_OUI_FROM_DATABASE=ADEPT TECHNOLOGY + +OUI:0010BD* + ID_OUI_FROM_DATABASE=THE TELECOMMUNICATION TECHNOLOGY COMMITTEE (TTC) + +OUI:006099* + ID_OUI_FROM_DATABASE=SBE, Inc. + +OUI:0060FD* + ID_OUI_FROM_DATABASE=NetICs, Inc. + +OUI:0060B5* + ID_OUI_FROM_DATABASE=KEBA GmbH + +OUI:006027* + ID_OUI_FROM_DATABASE=Superior Modular Products + +OUI:0060C1* + ID_OUI_FROM_DATABASE=WaveSpan Corporation + +OUI:006041* + ID_OUI_FROM_DATABASE=Yokogawa Electric Corporation + +OUI:006005* + ID_OUI_FROM_DATABASE=FEEDBACK DATA LTD. + +OUI:00607B* + ID_OUI_FROM_DATABASE=FORE SYSTEMS, INC. + +OUI:00609C* + ID_OUI_FROM_DATABASE=Perkin-Elmer Incorporated + +OUI:006007* + ID_OUI_FROM_DATABASE=ACRES GAMING, INC. + +OUI:006035* + ID_OUI_FROM_DATABASE=DALLAS SEMICONDUCTOR, INC. + +OUI:0060F1* + ID_OUI_FROM_DATABASE=EXP COMPUTER, INC. + +OUI:006040* + ID_OUI_FROM_DATABASE=NETRO CORP. + +OUI:006034* + ID_OUI_FROM_DATABASE=ROBERT BOSCH GmbH + +OUI:0060BA* + ID_OUI_FROM_DATABASE=SAHARA NETWORKS, INC. + +OUI:006096* + ID_OUI_FROM_DATABASE=T.S. MICROTECH INC. + +OUI:00603A* + ID_OUI_FROM_DATABASE=QUICK CONTROLS LTD. + +OUI:0060AC* + ID_OUI_FROM_DATABASE=RESILIENCE CORPORATION + +OUI:0060EB* + ID_OUI_FROM_DATABASE=FOURTHTRACK SYSTEMS + +OUI:00606D* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORP. + +OUI:006014* + ID_OUI_FROM_DATABASE=EDEC CO., LTD. + +OUI:0060E1* + ID_OUI_FROM_DATABASE=ORCKIT COMMUNICATIONS LTD. + +OUI:006062* + ID_OUI_FROM_DATABASE=TELESYNC, INC. + +OUI:006038* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:006095* + ID_OUI_FROM_DATABASE=ACCU-TIME SYSTEMS, INC. + +OUI:00A016* + ID_OUI_FROM_DATABASE=MICROPOLIS CORP. + +OUI:00A01C* + ID_OUI_FROM_DATABASE=NASCENT NETWORKS CORPORATION + +OUI:00A0FC* + ID_OUI_FROM_DATABASE=IMAGE SCIENCES, INC. + +OUI:00A0B7* + ID_OUI_FROM_DATABASE=CORDANT, INC. + +OUI:00A037* + ID_OUI_FROM_DATABASE=Mindray DS USA, Inc. + +OUI:00A04C* + ID_OUI_FROM_DATABASE=INNOVATIVE SYSTEMS & TECHNOLOGIES, INC. + +OUI:00A0E9* + ID_OUI_FROM_DATABASE=ELECTRONIC RETAILING SYSTEMS INTERNATIONAL + +OUI:006078* + ID_OUI_FROM_DATABASE=POWER MEASUREMENT LTD. + +OUI:00600D* + ID_OUI_FROM_DATABASE=Digital Logic GmbH + +OUI:00608A* + ID_OUI_FROM_DATABASE=CITADEL COMPUTER + +OUI:00A05D* + ID_OUI_FROM_DATABASE=CS COMPUTER SYSTEME GmbH + +OUI:00A0BD* + ID_OUI_FROM_DATABASE=I-TECH CORP. + +OUI:00A0B9* + ID_OUI_FROM_DATABASE=EAGLE TECHNOLOGY, INC. + +OUI:00A069* + ID_OUI_FROM_DATABASE=Symmetricom, Inc. + +OUI:00A07A* + ID_OUI_FROM_DATABASE=ADVANCED PERIPHERALS TECHNOLOGIES, INC. + +OUI:00A04E* + ID_OUI_FROM_DATABASE=VOELKER TECHNOLOGIES, INC. + +OUI:00A05A* + ID_OUI_FROM_DATABASE=KOFAX IMAGE PRODUCTS + +OUI:00A093* + ID_OUI_FROM_DATABASE=B/E AEROSPACE, Inc. + +OUI:00A0BF* + ID_OUI_FROM_DATABASE=WIRELESS DATA GROUP MOTOROLA + +OUI:00609F* + ID_OUI_FROM_DATABASE=PHAST CORPORATION + +OUI:006067* + ID_OUI_FROM_DATABASE=ACER NETXUS INC. + +OUI:00600C* + ID_OUI_FROM_DATABASE=Eurotech Inc. + +OUI:006025* + ID_OUI_FROM_DATABASE=ACTIVE IMAGING PLC + +OUI:006071* + ID_OUI_FROM_DATABASE=MIDAS LAB, INC. + +OUI:0060A7* + ID_OUI_FROM_DATABASE=MICROSENS GmbH & CO. KG + +OUI:0060FC* + ID_OUI_FROM_DATABASE=CONSERVATION THROUGH INNOVATION LTD. + +OUI:0060D4* + ID_OUI_FROM_DATABASE=ELDAT COMMUNICATION LTD. + +OUI:006085* + ID_OUI_FROM_DATABASE=Storage Concepts + +OUI:0060D3* + ID_OUI_FROM_DATABASE=AT&T + +OUI:006018* + ID_OUI_FROM_DATABASE=STELLAR ONE CORPORATION + +OUI:00602B* + ID_OUI_FROM_DATABASE=PEAK AUDIO + +OUI:00606F* + ID_OUI_FROM_DATABASE=CLARION CORPORATION OF AMERICA + +OUI:0060ED* + ID_OUI_FROM_DATABASE=RICARDO TEST AUTOMATION LTD. + +OUI:0060F6* + ID_OUI_FROM_DATABASE=NEXTEST COMMUNICATIONS PRODUCTS, INC. + +OUI:0060DD* + ID_OUI_FROM_DATABASE=MYRICOM, INC. + +OUI:006092* + ID_OUI_FROM_DATABASE=MICRO/SYS, INC. + +OUI:006080* + ID_OUI_FROM_DATABASE=MICROTRONIX DATACOM LTD. + +OUI:006068* + ID_OUI_FROM_DATABASE=Dialogic Corporation + +OUI:0060DB* + ID_OUI_FROM_DATABASE=NTP ELEKTRONIK A/S + +OUI:00A002* + ID_OUI_FROM_DATABASE=LEEDS & NORTHRUP AUSTRALIA PTY LTD + +OUI:00A0E4* + ID_OUI_FROM_DATABASE=OPTIQUEST + +OUI:00A01F* + ID_OUI_FROM_DATABASE=TRICORD SYSTEMS, INC. + +OUI:00A0C0* + ID_OUI_FROM_DATABASE=DIGITAL LINK CORP. + +OUI:00A043* + ID_OUI_FROM_DATABASE=AMERICAN TECHNOLOGY LABS, INC. + +OUI:00A047* + ID_OUI_FROM_DATABASE=INTEGRATED FITNESS CORP. + +OUI:00A07C* + ID_OUI_FROM_DATABASE=TONYANG NYLON CO., LTD. + +OUI:00A0EC* + ID_OUI_FROM_DATABASE=TRANSMITTON LTD. + +OUI:00A07E* + ID_OUI_FROM_DATABASE=AVID TECHNOLOGY, INC. + +OUI:00A035* + ID_OUI_FROM_DATABASE=CYLINK CORPORATION + +OUI:00A028* + ID_OUI_FROM_DATABASE=CONNER PERIPHERALS + +OUI:00A0C7* + ID_OUI_FROM_DATABASE=TADIRAN TELECOMMUNICATIONS + +OUI:00E0BE* + ID_OUI_FROM_DATABASE=GENROCO INTERNATIONAL, INC. + +OUI:00E010* + ID_OUI_FROM_DATABASE=HESS SB-AUTOMATENBAU GmbH + +OUI:00E0E9* + ID_OUI_FROM_DATABASE=DATA LABS, INC. + +OUI:00E0A0* + ID_OUI_FROM_DATABASE=WILTRON CO. + +OUI:00E024* + ID_OUI_FROM_DATABASE=GADZOOX NETWORKS + +OUI:00E017* + ID_OUI_FROM_DATABASE=EXXACT GmbH + +OUI:00603B* + ID_OUI_FROM_DATABASE=AMTEC spa + +OUI:0020E5* + ID_OUI_FROM_DATABASE=APEX DATA, INC. + +OUI:00207D* + ID_OUI_FROM_DATABASE=ADVANCED COMPUTER APPLICATIONS + +OUI:0020D0* + ID_OUI_FROM_DATABASE=VERSALYNX CORPORATION + +OUI:00206C* + ID_OUI_FROM_DATABASE=EVERGREEN TECHNOLOGY CORP. + +OUI:002012* + ID_OUI_FROM_DATABASE=CAMTRONICS MEDICAL SYSTEMS + +OUI:00200B* + ID_OUI_FROM_DATABASE=OCTAGON SYSTEMS CORP. + +OUI:00209E* + ID_OUI_FROM_DATABASE=BROWN'S OPERATING SYSTEM SERVICES, LTD. + +OUI:0020D7* + ID_OUI_FROM_DATABASE=JAPAN MINICOMPUTER SYSTEMS CO., Ltd. + +OUI:0020FB* + ID_OUI_FROM_DATABASE=OCTEL COMMUNICATIONS CORP. + +OUI:0020B1* + ID_OUI_FROM_DATABASE=COMTECH RESEARCH INC. + +OUI:002033* + ID_OUI_FROM_DATABASE=SYNAPSE TECHNOLOGIES, INC. + +OUI:002099* + ID_OUI_FROM_DATABASE=BON ELECTRIC CO., LTD. + +OUI:0020AE* + ID_OUI_FROM_DATABASE=ORNET DATA COMMUNICATION TECH. + +OUI:0020EA* + ID_OUI_FROM_DATABASE=EFFICIENT NETWORKS, INC. + +OUI:0020FF* + ID_OUI_FROM_DATABASE=SYMMETRICAL TECHNOLOGIES + +OUI:00208B* + ID_OUI_FROM_DATABASE=LAPIS TECHNOLOGIES, INC. + +OUI:002069* + ID_OUI_FROM_DATABASE=ISDN SYSTEMS CORPORATION + +OUI:0020BA* + ID_OUI_FROM_DATABASE=CENTER FOR HIGH PERFORMANCE + +OUI:002006* + ID_OUI_FROM_DATABASE=GARRETT COMMUNICATIONS, INC. + +OUI:00A0A2* + ID_OUI_FROM_DATABASE=DIGICOM S.P.A. + +OUI:00A054* + ID_OUI_FROM_DATABASE=Private + +OUI:00A030* + ID_OUI_FROM_DATABASE=CAPTOR NV/SA + +OUI:00A0B1* + ID_OUI_FROM_DATABASE=FIRST VIRTUAL CORPORATION + +OUI:0020CB* + ID_OUI_FROM_DATABASE=PRETEC ELECTRONICS CORP. + +OUI:0020AB* + ID_OUI_FROM_DATABASE=MICRO INDUSTRIES CORP. + +OUI:00202D* + ID_OUI_FROM_DATABASE=TAIYO CORPORATION + +OUI:00A088* + ID_OUI_FROM_DATABASE=ESSENTIAL COMMUNICATIONS + +OUI:00A0FA* + ID_OUI_FROM_DATABASE=Marconi Communication GmbH + +OUI:00A014* + ID_OUI_FROM_DATABASE=CSIR + +OUI:00A064* + ID_OUI_FROM_DATABASE=KVB/ANALECT + +OUI:00A07F* + ID_OUI_FROM_DATABASE=GSM-SYNTEL, LTD. + +OUI:00A03E* + ID_OUI_FROM_DATABASE=ATM FORUM + +OUI:00A050* + ID_OUI_FROM_DATABASE=CYPRESS SEMICONDUCTOR + +OUI:00A098* + ID_OUI_FROM_DATABASE=NetApp + +OUI:00A021* + ID_OUI_FROM_DATABASE=General Dynamics + +OUI:00A0A8* + ID_OUI_FROM_DATABASE=RENEX CORPORATION + +OUI:002049* + ID_OUI_FROM_DATABASE=COMTRON, INC. + +OUI:002050* + ID_OUI_FROM_DATABASE=KOREA COMPUTER INC. + +OUI:00203C* + ID_OUI_FROM_DATABASE=EUROTIME AB + +OUI:002028* + ID_OUI_FROM_DATABASE=WEST EGG SYSTEMS, INC. + +OUI:002014* + ID_OUI_FROM_DATABASE=GLOBAL VIEW CO., LTD. + +OUI:002053* + ID_OUI_FROM_DATABASE=HUNTSVILLE MICROSYSTEMS, INC. + +OUI:002001* + ID_OUI_FROM_DATABASE=DSP SOLUTIONS, INC. + +OUI:00209C* + ID_OUI_FROM_DATABASE=PRIMARY ACCESS CORP. + +OUI:0020C5* + ID_OUI_FROM_DATABASE=EAGLE TECHNOLOGY + +OUI:002009* + ID_OUI_FROM_DATABASE=PACKARD BELL ELEC., INC. + +OUI:002095* + ID_OUI_FROM_DATABASE=RIVA ELECTRONICS + +OUI:00203F* + ID_OUI_FROM_DATABASE=JUKI CORPORATION + +OUI:00C014* + ID_OUI_FROM_DATABASE=TELEMATICS CALABASAS INT'L,INC + +OUI:00C045* + ID_OUI_FROM_DATABASE=ISOLATION SYSTEMS, LTD. + +OUI:00C000* + ID_OUI_FROM_DATABASE=LANOPTICS, LTD. + +OUI:00AA3C* + ID_OUI_FROM_DATABASE=OLIVETTI TELECOM SPA (OLTECO) + +OUI:00C079* + ID_OUI_FROM_DATABASE=FONSYS CO.,LTD. + +OUI:002011* + ID_OUI_FROM_DATABASE=CANOPUS CO., LTD. + +OUI:00C00B* + ID_OUI_FROM_DATABASE=NORCONTROL A.S. + +OUI:00C0C0* + ID_OUI_FROM_DATABASE=SHORE MICROSYSTEMS, INC. + +OUI:00C00C* + ID_OUI_FROM_DATABASE=RELIA TECHNOLGIES + +OUI:00A0E7* + ID_OUI_FROM_DATABASE=CENTRAL DATA CORPORATION + +OUI:00A068* + ID_OUI_FROM_DATABASE=BHP LIMITED + +OUI:00A0B3* + ID_OUI_FROM_DATABASE=ZYKRONIX + +OUI:00A06E* + ID_OUI_FROM_DATABASE=AUSTRON, INC. + +OUI:00A0BB* + ID_OUI_FROM_DATABASE=HILAN GMBH + +OUI:00A0C8* + ID_OUI_FROM_DATABASE=ADTRAN INC. + +OUI:00A017* + ID_OUI_FROM_DATABASE=J B M CORPORATION + +OUI:0020D5* + ID_OUI_FROM_DATABASE=VIPA GMBH + +OUI:002079* + ID_OUI_FROM_DATABASE=MIKRON GMBH + +OUI:0020FA* + ID_OUI_FROM_DATABASE=GDE SYSTEMS, INC. + +OUI:002007* + ID_OUI_FROM_DATABASE=SFA, INC. + +OUI:002062* + ID_OUI_FROM_DATABASE=SCORPION LOGIC, LTD. + +OUI:00200A* + ID_OUI_FROM_DATABASE=SOURCE-COMM CORP. + +OUI:002000* + ID_OUI_FROM_DATABASE=LEXMARK INTERNATIONAL, INC. + +OUI:002003* + ID_OUI_FROM_DATABASE=PIXEL POWER LTD. + +OUI:0020B4* + ID_OUI_FROM_DATABASE=TERMA ELEKTRONIK AS + +OUI:00205B* + ID_OUI_FROM_DATABASE=Kentrox, LLC + +OUI:002030* + ID_OUI_FROM_DATABASE=ANALOG & DIGITAL SYSTEMS + +OUI:0020A8* + ID_OUI_FROM_DATABASE=SAST TECHNOLOGY CORP. + +OUI:002066* + ID_OUI_FROM_DATABASE=GENERAL MAGIC, INC. + +OUI:002036* + ID_OUI_FROM_DATABASE=BMC SOFTWARE + +OUI:0040BE* + ID_OUI_FROM_DATABASE=BOEING DEFENSE & SPACE + +OUI:004036* + ID_OUI_FROM_DATABASE=Zoom Telephonics, Inc + +OUI:004046* + ID_OUI_FROM_DATABASE=UDC RESEARCH LIMITED + +OUI:00406A* + ID_OUI_FROM_DATABASE=KENTEK INFORMATION SYSTEMS,INC + +OUI:0040F2* + ID_OUI_FROM_DATABASE=JANICH & KLASS COMPUTERTECHNIK + +OUI:004082* + ID_OUI_FROM_DATABASE=LABORATORY EQUIPMENT CORP. + +OUI:004022* + ID_OUI_FROM_DATABASE=KLEVER COMPUTERS, INC. + +OUI:0040A2* + ID_OUI_FROM_DATABASE=KINGSTAR TECHNOLOGY INC. + +OUI:0040B4* + ID_OUI_FROM_DATABASE=NEXTCOM K.K. + +OUI:0040D4* + ID_OUI_FROM_DATABASE=GAGE TALKER CORP. + +OUI:004038* + ID_OUI_FROM_DATABASE=TALENT ELECTRIC INCORPORATED + +OUI:004018* + ID_OUI_FROM_DATABASE=ADOBE SYSTEMS, INC. + +OUI:0040B0* + ID_OUI_FROM_DATABASE=BYTEX CORPORATION, ENGINEERING + +OUI:004040* + ID_OUI_FROM_DATABASE=RING ACCESS, INC. + +OUI:0080D7* + ID_OUI_FROM_DATABASE=Fantum Engineering + +OUI:0080D9* + ID_OUI_FROM_DATABASE=EMK Elektronik GmbH & Co. KG + +OUI:00806A* + ID_OUI_FROM_DATABASE=ERI (EMPAC RESEARCH INC.) + +OUI:00403B* + ID_OUI_FROM_DATABASE=SYNERJET INTERNATIONAL CORP. + +OUI:0040AB* + ID_OUI_FROM_DATABASE=ROLAND DG CORPORATION + +OUI:0040D5* + ID_OUI_FROM_DATABASE=Sartorius Mechatronics T&H GmbH + +OUI:004027* + ID_OUI_FROM_DATABASE=SMC MASSACHUSETTS, INC. + +OUI:00409C* + ID_OUI_FROM_DATABASE=TRANSWARE + +OUI:00405C* + ID_OUI_FROM_DATABASE=FUTURE SYSTEMS, INC. + +OUI:00008C* + ID_OUI_FROM_DATABASE=Alloy Computer Products (Australia) Pty Ltd + +OUI:004000* + ID_OUI_FROM_DATABASE=PCI COMPONENTES DA AMZONIA LTD + +OUI:0040C5* + ID_OUI_FROM_DATABASE=MICOM COMMUNICATIONS INC. + +OUI:0040AA* + ID_OUI_FROM_DATABASE=Metso Automation + +OUI:004023* + ID_OUI_FROM_DATABASE=LOGIC CORPORATION + +OUI:0040A4* + ID_OUI_FROM_DATABASE=ROSE ELECTRONICS + +OUI:004048* + ID_OUI_FROM_DATABASE=SMD INFORMATICA S.A. + +OUI:004025* + ID_OUI_FROM_DATABASE=MOLECULAR DYNAMICS + +OUI:004010* + ID_OUI_FROM_DATABASE=SONIC SYSTEMS, INC. + +OUI:0040CA* + ID_OUI_FROM_DATABASE=FIRST INTERNAT'L COMPUTER, INC + +OUI:004050* + ID_OUI_FROM_DATABASE=IRONICS, INCORPORATED + +OUI:00402B* + ID_OUI_FROM_DATABASE=TRIGEM COMPUTER, INC. + +OUI:00C08C* + ID_OUI_FROM_DATABASE=PERFORMANCE TECHNOLOGIES, INC. + +OUI:00C02B* + ID_OUI_FROM_DATABASE=GERLOFF GESELLSCHAFT FUR + +OUI:00C0A7* + ID_OUI_FROM_DATABASE=SEEL LTD. + +OUI:0040B3* + ID_OUI_FROM_DATABASE=ParTech Inc. + +OUI:00407D* + ID_OUI_FROM_DATABASE=EXTENSION TECHNOLOGY CORP. + +OUI:004079* + ID_OUI_FROM_DATABASE=JUKO MANUFACTURE COMPANY, LTD. + +OUI:0040D9* + ID_OUI_FROM_DATABASE=AMERICAN MEGATRENDS INC. + +OUI:004011* + ID_OUI_FROM_DATABASE=ANDOVER CONTROLS CORPORATION + +OUI:0040C1* + ID_OUI_FROM_DATABASE=BIZERBA-WERKE WILHEIM KRAUT + +OUI:00C06B* + ID_OUI_FROM_DATABASE=OSI PLUS CORPORATION + +OUI:00C06A* + ID_OUI_FROM_DATABASE=ZAHNER-ELEKTRIK GMBH & CO. KG + +OUI:00C097* + ID_OUI_FROM_DATABASE=ARCHIPEL SA + +OUI:00C072* + ID_OUI_FROM_DATABASE=KNX LTD. + +OUI:00C0EC* + ID_OUI_FROM_DATABASE=DAUPHIN TECHNOLOGY + +OUI:00C066* + ID_OUI_FROM_DATABASE=DOCUPOINT, INC. + +OUI:00C028* + ID_OUI_FROM_DATABASE=JASCO CORPORATION + +OUI:00C0DC* + ID_OUI_FROM_DATABASE=EOS TECHNOLOGIES, INC. + +OUI:00C02D* + ID_OUI_FROM_DATABASE=FUJI PHOTO FILM CO., LTD. + +OUI:00C0BD* + ID_OUI_FROM_DATABASE=INEX TECHNOLOGIES, INC. + +OUI:00C054* + ID_OUI_FROM_DATABASE=NETWORK PERIPHERALS, LTD. + +OUI:00C0D5* + ID_OUI_FROM_DATABASE=Werbeagentur Jürgen Siebert + +OUI:00C044* + ID_OUI_FROM_DATABASE=EMCOM CORPORATION + +OUI:00C050* + ID_OUI_FROM_DATABASE=TOYO DENKI SEIZO K.K. + +OUI:00408A* + ID_OUI_FROM_DATABASE=TPS TELEPROCESSING SYS. GMBH + +OUI:0040FD* + ID_OUI_FROM_DATABASE=LXE + +OUI:00403D* + ID_OUI_FROM_DATABASE=Teradata Corporation + +OUI:0040E0* + ID_OUI_FROM_DATABASE=ATOMWIDE LTD. + +OUI:00408C* + ID_OUI_FROM_DATABASE=AXIS COMMUNICATIONS AB + +OUI:004068* + ID_OUI_FROM_DATABASE=EXTENDED SYSTEMS + +OUI:0040BA* + ID_OUI_FROM_DATABASE=ALLIANT COMPUTER SYSTEMS CORP. + +OUI:004069* + ID_OUI_FROM_DATABASE=LEMCOM SYSTEMS, INC. + +OUI:0040F8* + ID_OUI_FROM_DATABASE=SYSTEMHAUS DISCOM + +OUI:004077* + ID_OUI_FROM_DATABASE=MAXTON TECHNOLOGY CORPORATION + +OUI:0040E7* + ID_OUI_FROM_DATABASE=ARNOS INSTRUMENTS & COMPUTER + +OUI:0040AC* + ID_OUI_FROM_DATABASE=SUPER WORKSTATION, INC. + +OUI:00C0AC* + ID_OUI_FROM_DATABASE=GAMBIT COMPUTER COMMUNICATIONS + +OUI:00C02C* + ID_OUI_FROM_DATABASE=CENTRUM COMMUNICATIONS, INC. + +OUI:00C0ED* + ID_OUI_FROM_DATABASE=US ARMY ELECTRONIC + +OUI:00C0D1* + ID_OUI_FROM_DATABASE=COMTREE TECHNOLOGY CORPORATION + +OUI:00C0D2* + ID_OUI_FROM_DATABASE=SYNTELLECT, INC. + +OUI:00C0FB* + ID_OUI_FROM_DATABASE=ADVANCED TECHNOLOGY LABS + +OUI:00C092* + ID_OUI_FROM_DATABASE=MENNEN MEDICAL INC. + +OUI:00C06C* + ID_OUI_FROM_DATABASE=SVEC COMPUTER CORP. + +OUI:00C02E* + ID_OUI_FROM_DATABASE=NETWIZ + +OUI:00C05B* + ID_OUI_FROM_DATABASE=NETWORKS NORTHWEST, INC. + +OUI:00C0BF* + ID_OUI_FROM_DATABASE=TECHNOLOGY CONCEPTS, LTD. + +OUI:00C0C9* + ID_OUI_FROM_DATABASE=ELSAG BAILEY PROCESS + +OUI:00809D* + ID_OUI_FROM_DATABASE=Commscraft Ltd. + +OUI:008017* + ID_OUI_FROM_DATABASE=PFU LIMITED + +OUI:0080F8* + ID_OUI_FROM_DATABASE=MIZAR, INC. + +OUI:008024* + ID_OUI_FROM_DATABASE=KALPANA, INC. + +OUI:008074* + ID_OUI_FROM_DATABASE=FISHER CONTROLS + +OUI:008021* + ID_OUI_FROM_DATABASE=Alcatel Canada Inc. + +OUI:000055* + ID_OUI_FROM_DATABASE=COMMISSARIAT A L`ENERGIE ATOM. + +OUI:000086* + ID_OUI_FROM_DATABASE=MEGAHERTZ CORPORATION + +OUI:000092* + ID_OUI_FROM_DATABASE=COGENT DATA TECHNOLOGIES + +OUI:008068* + ID_OUI_FROM_DATABASE=YAMATECH SCIENTIFIC LTD. + +OUI:0080F2* + ID_OUI_FROM_DATABASE=RAYCOM SYSTEMS INC + +OUI:0080EA* + ID_OUI_FROM_DATABASE=ADVA Optical Networking Ltd. + +OUI:008025* + ID_OUI_FROM_DATABASE=STOLLMANN GMBH + +OUI:000067* + ID_OUI_FROM_DATABASE=SOFT * RITE, INC. + +OUI:0000E8* + ID_OUI_FROM_DATABASE=ACCTON TECHNOLOGY CORP. + +OUI:0000B2* + ID_OUI_FROM_DATABASE=TELEVIDEO SYSTEMS, INC. + +OUI:0000EE* + ID_OUI_FROM_DATABASE=NETWORK DESIGNERS, LTD. + +OUI:000089* + ID_OUI_FROM_DATABASE=CAYMAN SYSTEMS INC. + +OUI:000021* + ID_OUI_FROM_DATABASE=SUREMAN COMP. & COMMUN. CORP. + +OUI:0000CF* + ID_OUI_FROM_DATABASE=HAYES MICROCOMPUTER PRODUCTS + +OUI:0000A4* + ID_OUI_FROM_DATABASE=ACORN COMPUTERS LIMITED + +OUI:000018* + ID_OUI_FROM_DATABASE=WEBSTER COMPUTER CORPORATION + +OUI:008033* + ID_OUI_FROM_DATABASE=EMS Aviation, Inc. + +OUI:008052* + ID_OUI_FROM_DATABASE=TECHNICALLY ELITE CONCEPTS + +OUI:00804F* + ID_OUI_FROM_DATABASE=DAIKIN INDUSTRIES, LTD. + +OUI:00806D* + ID_OUI_FROM_DATABASE=CENTURY SYSTEMS CORP. + +OUI:00802D* + ID_OUI_FROM_DATABASE=XYLOGICS INC + +OUI:008048* + ID_OUI_FROM_DATABASE=COMPEX INCORPORATED + +OUI:008085* + ID_OUI_FROM_DATABASE=H-THREE SYSTEMS CORPORATION + +OUI:008014* + ID_OUI_FROM_DATABASE=ESPRIT SYSTEMS + +OUI:0080B4* + ID_OUI_FROM_DATABASE=SOPHIA SYSTEMS + +OUI:00807F* + ID_OUI_FROM_DATABASE=DY-4 INCORPORATED + +OUI:0000E4* + ID_OUI_FROM_DATABASE=IN2 GROUPE INTERTECHNIQUE + +OUI:000079* + ID_OUI_FROM_DATABASE=NETWORTH INCORPORATED + +OUI:000075* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:004009* + ID_OUI_FROM_DATABASE=TACHIBANA TECTRON CO., LTD. + +OUI:00409E* + ID_OUI_FROM_DATABASE=CONCURRENT TECHNOLOGIES LTD. + +OUI:008092* + ID_OUI_FROM_DATABASE=Silex Technology, Inc. + +OUI:008011* + ID_OUI_FROM_DATABASE=DIGITAL SYSTEMS INT'L. INC. + +OUI:008044* + ID_OUI_FROM_DATABASE=SYSTECH COMPUTER CORP. + +OUI:00808A* + ID_OUI_FROM_DATABASE=SUMMIT MICROSYSTEMS CORP. + +OUI:0080E3* + ID_OUI_FROM_DATABASE=CORAL NETWORK CORPORATION + +OUI:008072* + ID_OUI_FROM_DATABASE=MICROPLEX SYSTEMS LTD. + +OUI:008054* + ID_OUI_FROM_DATABASE=FRONTIER TECHNOLOGIES CORP. + +OUI:0080AE* + ID_OUI_FROM_DATABASE=HUGHES NETWORK SYSTEMS + +OUI:0080AF* + ID_OUI_FROM_DATABASE=ALLUMER CO., LTD. + +OUI:0080EC* + ID_OUI_FROM_DATABASE=SUPERCOMPUTING SOLUTIONS, INC. + +OUI:0080A4* + ID_OUI_FROM_DATABASE=LIBERTY ELECTRONICS + +OUI:008073* + ID_OUI_FROM_DATABASE=DWB ASSOCIATES + +OUI:00802B* + ID_OUI_FROM_DATABASE=INTEGRATED MARKETING CO + +OUI:0080BE* + ID_OUI_FROM_DATABASE=ARIES RESEARCH + +OUI:008027* + ID_OUI_FROM_DATABASE=ADAPTIVE SYSTEMS, INC. + +OUI:0080E2* + ID_OUI_FROM_DATABASE=T.D.I. CO., LTD. + +OUI:0040EE* + ID_OUI_FROM_DATABASE=OPTIMEM + +OUI:00405E* + ID_OUI_FROM_DATABASE=NORTH HILLS ISRAEL + +OUI:004072* + ID_OUI_FROM_DATABASE=Applied Innovation Inc. + +OUI:004031* + ID_OUI_FROM_DATABASE=KOKUSAI ELECTRIC CO., LTD + +OUI:00400C* + ID_OUI_FROM_DATABASE=GENERAL MICRO SYSTEMS, INC. + +OUI:0040E6* + ID_OUI_FROM_DATABASE=C.A.E.N. + +OUI:0040FC* + ID_OUI_FROM_DATABASE=IBR COMPUTER TECHNIK GMBH + +OUI:004001* + ID_OUI_FROM_DATABASE=Zero One Technology Co. Ltd. + +OUI:004002* + ID_OUI_FROM_DATABASE=PERLE SYSTEMS LIMITED + +OUI:0080DB* + ID_OUI_FROM_DATABASE=GRAPHON CORPORATION + +OUI:0080B1* + ID_OUI_FROM_DATABASE=SOFTCOM A/S + +OUI:0080D8* + ID_OUI_FROM_DATABASE=NETWORK PERIPHERALS INC. + +OUI:0080AB* + ID_OUI_FROM_DATABASE=DUKANE NETWORK INTEGRATION + +OUI:00809B* + ID_OUI_FROM_DATABASE=JUSTSYSTEM CORPORATION + +OUI:008089* + ID_OUI_FROM_DATABASE=TECNETICS (PTY) LTD. + +OUI:000039* + ID_OUI_FROM_DATABASE=TOSHIBA CORPORATION + +OUI:0000CB* + ID_OUI_FROM_DATABASE=COMPU-SHACK ELECTRONIC GMBH + +OUI:0000D1* + ID_OUI_FROM_DATABASE=ADAPTEC INCORPORATED + +OUI:0000B6* + ID_OUI_FROM_DATABASE=MICRO-MATIC RESEARCH + +OUI:000066* + ID_OUI_FROM_DATABASE=TALARIS SYSTEMS, INC. + +OUI:000014* + ID_OUI_FROM_DATABASE=NETRONIX + +OUI:000072* + ID_OUI_FROM_DATABASE=MINIWARE TECHNOLOGY + +OUI:0000AB* + ID_OUI_FROM_DATABASE=LOGIC MODELING CORPORATION + +OUI:000029* + ID_OUI_FROM_DATABASE=IMC NETWORKS CORP. + +OUI:0080CD* + ID_OUI_FROM_DATABASE=MICRONICS COMPUTER, INC. + +OUI:008083* + ID_OUI_FROM_DATABASE=AMDAHL + +OUI:008003* + ID_OUI_FROM_DATABASE=HYTEC ELECTRONICS LTD. + +OUI:00801B* + ID_OUI_FROM_DATABASE=KODIAK TECHNOLOGY + +OUI:0080CC* + ID_OUI_FROM_DATABASE=MICROWAVE BYPASS SYSTEMS + +OUI:080079* + ID_OUI_FROM_DATABASE=THE DROID WORKS + +OUI:080077* + ID_OUI_FROM_DATABASE=TSL COMMUNICATIONS LTD. + +OUI:080071* + ID_OUI_FROM_DATABASE=MATRA (DSIE) + +OUI:08006A* + ID_OUI_FROM_DATABASE=ATT BELL LABORATORIES + +OUI:08005F* + ID_OUI_FROM_DATABASE=SABER TECHNOLOGY CORP. + +OUI:08005C* + ID_OUI_FROM_DATABASE=FOUR PHASE SYSTEMS + +OUI:08005B* + ID_OUI_FROM_DATABASE=VTA TECHNOLOGIES INC. + +OUI:080058* + ID_OUI_FROM_DATABASE=SYSTEMS CONCEPTS + +OUI:080050* + ID_OUI_FROM_DATABASE=DAISY SYSTEMS CORP. + +OUI:080052* + ID_OUI_FROM_DATABASE=INSYSTEC + +OUI:080047* + ID_OUI_FROM_DATABASE=SEQUENT COMPUTER SYSTEMS INC. + +OUI:080045* + ID_OUI_FROM_DATABASE=CONCURRENT COMPUTER CORP. + +OUI:080044* + ID_OUI_FROM_DATABASE=DAVID SYSTEMS INC. + +OUI:080041* + ID_OUI_FROM_DATABASE=RACAL-MILGO INFORMATION SYS.. + +OUI:080038* + ID_OUI_FROM_DATABASE=BULL S.A.S. + +OUI:08003C* + ID_OUI_FROM_DATABASE=SCHLUMBERGER WELL SERVICES + +OUI:080034* + ID_OUI_FROM_DATABASE=FILENET CORPORATION + +OUI:08002C* + ID_OUI_FROM_DATABASE=BRITTON LEE INC. + +OUI:0000B9* + ID_OUI_FROM_DATABASE=MCDONNELL DOUGLAS COMPUTER SYS + +OUI:00002D* + ID_OUI_FROM_DATABASE=CHROMATICS INC + +OUI:00004A* + ID_OUI_FROM_DATABASE=ADC CODENOLL TECHNOLOGY CORP. + +OUI:0000C0* + ID_OUI_FROM_DATABASE=WESTERN DIGITAL CORPORATION + +OUI:000040* + ID_OUI_FROM_DATABASE=APPLICON, INC. + +OUI:00005D* + ID_OUI_FROM_DATABASE=CS TELECOM + +OUI:08008E* + ID_OUI_FROM_DATABASE=Tandem Computers + +OUI:080086* + ID_OUI_FROM_DATABASE=KONICA MINOLTA HOLDINGS, INC. + +OUI:080083* + ID_OUI_FROM_DATABASE=Seiko Instruments Inc. + +OUI:080080* + ID_OUI_FROM_DATABASE=AES DATA INC. + +OUI:080030* + ID_OUI_FROM_DATABASE=ROYAL MELBOURNE INST OF TECH + +OUI:080064* + ID_OUI_FROM_DATABASE=Sitasys AG + +OUI:00DD09* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:08008A* + ID_OUI_FROM_DATABASE=PerfTech, Inc. + +OUI:00DD04* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:080066* + ID_OUI_FROM_DATABASE=AGFA CORPORATION + +OUI:08001A* + ID_OUI_FROM_DATABASE=TIARA/ 10NET + +OUI:080090* + ID_OUI_FROM_DATABASE=SONOMA SYSTEMS + +OUI:08000B* + ID_OUI_FROM_DATABASE=UNISYS CORPORATION + +OUI:080017* + ID_OUI_FROM_DATABASE=NATIONAL SEMICONDUCTOR + +OUI:00005E* + ID_OUI_FROM_DATABASE=ICANN, IANA Department + +OUI:0000AF* + ID_OUI_FROM_DATABASE=Canberra Industries, Inc. + +OUI:0000EC* + ID_OUI_FROM_DATABASE=MICROPROCESS + +OUI:00009E* + ID_OUI_FROM_DATABASE=MARLI S.A. + +OUI:000042* + ID_OUI_FROM_DATABASE=METIER MANAGEMENT SYSTEMS LTD. + +OUI:00008D* + ID_OUI_FROM_DATABASE=Cryptek Inc. + +OUI:000065* + ID_OUI_FROM_DATABASE=Network General Corporation + +OUI:00004D* + ID_OUI_FROM_DATABASE=DCI CORPORATION + +OUI:080024* + ID_OUI_FROM_DATABASE=10NET COMMUNICATIONS/DCA + +OUI:08001E* + ID_OUI_FROM_DATABASE=APOLLO COMPUTER INC. + +OUI:08001B* + ID_OUI_FROM_DATABASE=EMC Corporation + +OUI:00DD0D* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:AA0002* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:080005* + ID_OUI_FROM_DATABASE=SYMBOLICS INC. + +OUI:000000* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:0040D6* + ID_OUI_FROM_DATABASE=LOCAMATION B.V. + +OUI:800010* + ID_OUI_FROM_DATABASE=ATT BELL LABORATORIES + +OUI:AA0003* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:080008* + ID_OUI_FROM_DATABASE=BOLT BERANEK AND NEWMAN INC. + +OUI:08000E* + ID_OUI_FROM_DATABASE=NCR CORPORATION + +OUI:00006F* + ID_OUI_FROM_DATABASE=Madge Ltd. + +OUI:00005A* + ID_OUI_FROM_DATABASE=SysKonnect GmbH + +OUI:000023* + ID_OUI_FROM_DATABASE=ABB INDUSTRIAL SYSTEMS AB + +OUI:000045* + ID_OUI_FROM_DATABASE=FORD AEROSPACE & COMM. CORP. + +OUI:0000BC* + ID_OUI_FROM_DATABASE=Rockwell Automation + +OUI:0000C3* + ID_OUI_FROM_DATABASE=HARRIS CORP COMPUTER SYS DIV + +OUI:000004* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:000009* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:00003D* + ID_OUI_FROM_DATABASE=UNISYS + +OUI:F82C18* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:00173F* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:388602* + ID_OUI_FROM_DATABASE=Flexoptix GmbH + +OUI:F4EB38* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:001E74* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:00604C* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:002691* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:C0D044* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:6C2E85* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:CC33BB* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:681590* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:5464D9* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:00023F* + ID_OUI_FROM_DATABASE=COMPAL ELECTRONICS, INC. + +OUI:0080C2* + ID_OUI_FROM_DATABASE=IEEE 802.1 + +OUI:C46699* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:383BC8* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:DC7FA4* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001288* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001EC7* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:28162E* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:3CEA4F* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:848F69* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:90B11C* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8CAB8* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:24B6FD* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:000D56* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00123F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001372* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:74867A* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:3417EB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:EC8892* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:B07994* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:141AA3* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:CCC3EA* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:34BB26* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:40786A* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:0019B9* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:002219* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00B0D0* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:5C260A* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B083FE* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:141877* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:0024E8* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:A48E0A* + ID_OUI_FROM_DATABASE=DeLaval International AB + +OUI:00215C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:002315* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001500* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:104A7D* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A4C494* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:902E1C* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:3CFDFE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B8BF83* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001DE1* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0022FB* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:081196* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:6036DD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A0369F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:502DA2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4C79BA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:4CEB42* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:606720* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:84A6C8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:5891CF* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:88532E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0024D7* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C40938* + ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD + +OUI:00AA02* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:5CD2E4* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:04BD88* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:000B86* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:8896F2* + ID_OUI_FROM_DATABASE=Valeo Schalter und Sensoren GmbH + +OUI:80A589* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:0CCC26* + ID_OUI_FROM_DATABASE=Airenetworks + +OUI:4CB0E8* + ID_OUI_FROM_DATABASE=Beijing RongZhi xinghua technology co., LTD + +OUI:4C14A3* + ID_OUI_FROM_DATABASE=TCL Technoly Electronics (Huizhou) Co., Ltd. + +OUI:F48E38* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:D887D5* + ID_OUI_FROM_DATABASE=Leadcore Technology CO.,LTD + +OUI:00DA55* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:80D21D* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:705A0F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:586356* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:B046FC* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + +OUI:08A95A* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:6CADF8* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:54271E* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:008C54* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:F0842F* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:8CB864* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:0020E0* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:0004E3* + ID_OUI_FROM_DATABASE=Accton Technology Corp + +OUI:409558* + ID_OUI_FROM_DATABASE=Aisino Corporation + +OUI:00D0C9* + ID_OUI_FROM_DATABASE=ADVANTECH CO., LTD. + +OUI:002553* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:00238E* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:001CA2* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:0017C2* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:D0D412* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:000FA3* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:001D6A* + ID_OUI_FROM_DATABASE=Alpha Networks Inc. + +OUI:0000F4* + ID_OUI_FROM_DATABASE=Allied Telesis, Inc. + +OUI:10AE60* + ID_OUI_FROM_DATABASE=Private + +OUI:F04F7C* + ID_OUI_FROM_DATABASE=Private + +OUI:70F1A1* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:6CFAA7* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:0024EF* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:6C0E0D* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:B4527D* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:E063E5* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:000E07* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001A75* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:0016B8* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001D28* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:001FE4* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:002298* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:24FD52* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:2016D8* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:9CB70D* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:1C659D* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:001B9E* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:E0CA94* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:C0D962* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:00150C* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:744401* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:E091F5* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:001B2F* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00223F* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:E0469A* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:F40B93* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:68ED43* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:34BB1F* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:489D24* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:000F86* + ID_OUI_FROM_DATABASE=BlackBerry RTS + +OUI:001333* + ID_OUI_FROM_DATABASE=BaudTec Corporation + +OUI:507E5D* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:849CA6* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:1CC63C* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:C02506* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:0896D7* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:008EF2* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:4494FC* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:20E52A* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:9CD36D* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:C40415* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:08BD43* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:4C09D4* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:DC446D* + ID_OUI_FROM_DATABASE=Allwinner Technology Co., Ltd + +OUI:BC620E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:78F557* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E02861* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C4473F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:000AF7* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:000DB6* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:18C086* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:C03E0F* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:0020D4* + ID_OUI_FROM_DATABASE=Cabletron Systems, Inc. + +OUI:00001D* + ID_OUI_FROM_DATABASE=Cabletron Systems, Inc. + +OUI:0060BB* + ID_OUI_FROM_DATABASE=Cabletron Systems, Inc. + +OUI:D0542D* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:001FC7* + ID_OUI_FROM_DATABASE=Casio Hitachi Mobile Communications Co., Ltd. + +OUI:ACEE9E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C08997* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:2827BF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F05B7B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7CF90E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:AC5A14* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B0C559* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BCD11F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A0B4A5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:80656D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:48137E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E83A12* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9C0298* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:6C8336* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B8C68E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:74458A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A49A58* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B4EF39* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:14A364* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3CA10D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:206E9C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:183F47* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0C715D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0C1420* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A80600* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:6CF373* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:90F1AA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C4576E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78BDBC* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3872C0* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:F4068D* + ID_OUI_FROM_DATABASE=devolo AG + +OUI:000BCA* + ID_OUI_FROM_DATABASE=DATAVAN TC + +OUI:00507F* + ID_OUI_FROM_DATABASE=DrayTek Corp. + +OUI:3C8970* + ID_OUI_FROM_DATABASE=Neosfar + +OUI:C43655* + ID_OUI_FROM_DATABASE=Shenzhen Fenglian Technology Co., Ltd. + +OUI:78CB68* + ID_OUI_FROM_DATABASE=DAEHAP HYPER-TECH + +OUI:001A7F* + ID_OUI_FROM_DATABASE=GCI Science & Technology Co.,LTD + +OUI:00054F* + ID_OUI_FROM_DATABASE=Private + +OUI:D04D2C* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:E00C7F* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:58BDA3* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0025A0* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:002659* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:8C56C5* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:CC9E00* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001656* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:00191D* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0019FD* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001EA9* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:A84481* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:8844F6* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:A87B39* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:14C126* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:4C2578* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:001EA4* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001262* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00174B* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002547* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001DE9* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001D3B* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0014A7* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001CD6* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:D099D5* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:DC0077* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:0060DC* + ID_OUI_FROM_DATABASE=NEC Magnus Communications,Ltd. + +OUI:9CAED3* + ID_OUI_FROM_DATABASE=Seiko Epson Corporation + +OUI:F45C89* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C3C4A* + ID_OUI_FROM_DATABASE=NAKAYO TELECOMMUNICATIONS,INC + +OUI:0021FD* + ID_OUI_FROM_DATABASE=LACROIX TRAFFIC S.A.U + +OUI:4CB44A* + ID_OUI_FROM_DATABASE=NANOWAVE Technologies Inc. + +OUI:78C3E9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9C5C8E* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:70884D* + ID_OUI_FROM_DATABASE=JAPAN RADIO CO., LTD. + +OUI:102AB3* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:4C55CC* + ID_OUI_FROM_DATABASE=Zentri Pty Ltd + +OUI:BCEC5D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC415F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:30636B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:84683E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C88722* + ID_OUI_FROM_DATABASE=Lumenpulse + +OUI:FC1A11* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:30A9DE* + ID_OUI_FROM_DATABASE=LG Innotek + +OUI:E0CDFD* + ID_OUI_FROM_DATABASE=Beijing E3Control Technology Co, LTD + +OUI:208B37* + ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd + +OUI:08BE77* + ID_OUI_FROM_DATABASE=Green Electronics + +OUI:280C28* + ID_OUI_FROM_DATABASE=Unigen DataStorage Corporation + +OUI:980CA5* + ID_OUI_FROM_DATABASE=Motorola (Wuhan) Mobility Technologies Communication Co., Ltd. + +OUI:1CC035* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:34543C* + ID_OUI_FROM_DATABASE=TAKAOKA TOKO CO.,LTD. + +OUI:D49524* + ID_OUI_FROM_DATABASE=Clover Network, Inc. + +OUI:0034DA* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:9046A2* + ID_OUI_FROM_DATABASE=Tedipay UK Ltd + +OUI:6479A7* + ID_OUI_FROM_DATABASE=Phison Electronics Corp. + +OUI:C83870* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:288335* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:44783E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:202D07* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0452C7* + ID_OUI_FROM_DATABASE=Bose Corporation + +OUI:D4612E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:1C6758* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E85659* + ID_OUI_FROM_DATABASE=Advanced-Connectek Inc. + +OUI:34E70B* + ID_OUI_FROM_DATABASE=Beijing HAN Networks Co., Ltd + +OUI:8801F2* + ID_OUI_FROM_DATABASE=Vitec System Engineering Inc. + +OUI:FC084A* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:D4AD2D* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:48555F* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:847BEB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8C96C* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:34BF90* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:D467E7* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:04C1B9* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:689361* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:A082AC* + ID_OUI_FROM_DATABASE=Linear DMS Solutions Sdn. Bhd. + +OUI:002697* + ID_OUI_FROM_DATABASE=Alpha Technologies Inc. + +OUI:4CB8B5* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:1CABC0* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:84E323* + ID_OUI_FROM_DATABASE=Green Wave Telecommunication SDN BHD + +OUI:44650D* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:D897BA* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:7071BC* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:E06995* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:54D9E4* + ID_OUI_FROM_DATABASE=BRILLIANTTS CO., LTD + +OUI:E4F3F5* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:00089F* + ID_OUI_FROM_DATABASE=EFM Networks + +OUI:00185C* + ID_OUI_FROM_DATABASE=EDSLAB Technologies + +OUI:000E2E* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:00020E* + ID_OUI_FROM_DATABASE=ECI Telecom Ltd. + +OUI:00115B* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:000795* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:B8AEED* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:C03FD5* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:7427EA* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:0000C9* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:001A45* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:00168F* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:083FBC* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:042AE2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1C1B0D* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:903809* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:00104F* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:000782* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:E42F56* + ID_OUI_FROM_DATABASE=OptoMET GmbH + +OUI:00A045* + ID_OUI_FROM_DATABASE=PHOENIX CONTACT Electronics GmbH + +OUI:00266C* + ID_OUI_FROM_DATABASE=INVENTEC Corporation + +OUI:001E25* + ID_OUI_FROM_DATABASE=INTEK DIGITAL + +OUI:A0B662* + ID_OUI_FROM_DATABASE=Acutvista Innovation Co., Ltd. + +OUI:00C0F0* + ID_OUI_FROM_DATABASE=Kingston Technology Company, Inc. + +OUI:4C8FA5* + ID_OUI_FROM_DATABASE=Jastec + +OUI:000C49* + ID_OUI_FROM_DATABASE=Dangaard Telecom Denmark A/S + +OUI:CCE17F* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:44F477* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:5C4527* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:F01C2D* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:F8C001* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:78FE3D* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:54E032* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:3C6104* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:BC7574* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:20A680* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0019E2* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:001F12* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:0024DC* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:50C58D* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:000585* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:003146* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:80ACAC* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:50DD4F* + ID_OUI_FROM_DATABASE=Automation Components, Inc + +OUI:904D4A* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:7C79E8* + ID_OUI_FROM_DATABASE=PayRange Inc. + +OUI:540593* + ID_OUI_FROM_DATABASE=WOORI ELEC Co.,Ltd + +OUI:A067BE* + ID_OUI_FROM_DATABASE=Sicon srl + +OUI:C4CAD9* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:74258A* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:70F96D* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:00260F* + ID_OUI_FROM_DATABASE=Linn Products Ltd + +OUI:F845AD* + ID_OUI_FROM_DATABASE=Konka Group Co., Ltd. + +OUI:000358* + ID_OUI_FROM_DATABASE=Hanyang Digitech Co.Ltd + +OUI:000761* + ID_OUI_FROM_DATABASE=29530 + +OUI:60512C* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:905F2E* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:4C0B3A* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:C02FF1* + ID_OUI_FROM_DATABASE=Volta Networks + +OUI:4882F2* + ID_OUI_FROM_DATABASE=Appel Elektronik GmbH + +OUI:0C5101* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:086D41* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04D3CF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:30C82A* + ID_OUI_FROM_DATABASE=WI-BIZ srl + +OUI:0062EC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0C8A87* + ID_OUI_FROM_DATABASE=AgLogica Holdings, Inc + +OUI:34A2A2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:20F17C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:34B354* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:749D8F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:346AC2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:50F5DA* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:8CD2E9* + ID_OUI_FROM_DATABASE=NIPPON SMT Co.Ltd + +OUI:C83DFC* + ID_OUI_FROM_DATABASE=Pioneer DJ Corporation + +OUI:0016FB* + ID_OUI_FROM_DATABASE=SHENZHEN MTC CO LTD + +OUI:381DD9* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:6C9522* + ID_OUI_FROM_DATABASE=Scalys + +OUI:8C59C3* + ID_OUI_FROM_DATABASE=ADB Italia + +OUI:60C0BF* + ID_OUI_FROM_DATABASE=ON Semiconductor + +OUI:98398E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:348A7B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC765E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E0A8B8* + ID_OUI_FROM_DATABASE=Le Shi Zhi Xin Electronic Technology (Tianjin) Limited + +OUI:B88198* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E4FB8F* + ID_OUI_FROM_DATABASE=MOBIWIRE MOBILES (NINGBO) CO.,LTD + +OUI:78009E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C8AFE3* + ID_OUI_FROM_DATABASE=Hefei Radio Communication Technology Co., Ltd + +OUI:7C3548* + ID_OUI_FROM_DATABASE=Transcend Information + +OUI:E83A97* + ID_OUI_FROM_DATABASE=Toshiba Corporation + +OUI:9C8ECD* + ID_OUI_FROM_DATABASE=Amcrest Technologies + +OUI:282536* + ID_OUI_FROM_DATABASE=SHENZHEN HOLATEK CO.,LTD + +OUI:FCA89A* + ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd + +OUI:B8F8BE* + ID_OUI_FROM_DATABASE=BLUECOM + +OUI:6073BC* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:90EED9* + ID_OUI_FROM_DATABASE=UNIVERSAL DE DESARROLLOS ELECTRÓNICOS, SA + +OUI:043110* + ID_OUI_FROM_DATABASE=Inspur Group Co., Ltd. + +OUI:00215B* + ID_OUI_FROM_DATABASE=SenseAnywhere + +OUI:C816BD* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:587E61* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:340AFF* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:F85A00* + ID_OUI_FROM_DATABASE=Sanford LP + +OUI:5067F0* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:C86C87* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:D8E0B8* + ID_OUI_FROM_DATABASE=BULAT LLC + +OUI:68C44D* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:48FCB6* + ID_OUI_FROM_DATABASE=LAVA INTERNATIONAL(H.K) LIMITED + +OUI:CC3540* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:C42795* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:58238C* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:705A9E* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:80C6AB* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:90A4DE* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:3C970E* + ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. + +OUI:30144A* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:A854B2* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:38BC1A* + ID_OUI_FROM_DATABASE=MEIZU Technology Co., Ltd. + +OUI:0004A3* + ID_OUI_FROM_DATABASE=Microchip Technology Inc. + +OUI:98CF53* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:F4CB52* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:446EE5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:2C282D* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:80414E* + ID_OUI_FROM_DATABASE=BBK EDUCATIONAL ELECTRONICS CORP.,LTD. + +OUI:8C7716* + ID_OUI_FROM_DATABASE=LONGCHEER TELECOMMUNICATION LIMITED + +OUI:000A08* + ID_OUI_FROM_DATABASE=Alpine Electronics, Inc. + +OUI:A0143D* + ID_OUI_FROM_DATABASE=PARROT SA + +OUI:00267E* + ID_OUI_FROM_DATABASE=PARROT SA + +OUI:00121C* + ID_OUI_FROM_DATABASE=PARROT SA + +OUI:B85510* + ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd. + +OUI:000EE8* + ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd. + +OUI:001165* + ID_OUI_FROM_DATABASE=ZNYX Networks, Inc. + +OUI:0060D5* + ID_OUI_FROM_DATABASE=AMADA MIYACHI Co., Ltd + +OUI:000FDB* + ID_OUI_FROM_DATABASE=Westell Technologies Inc. + +OUI:D404FF* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:C45444* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:00269E* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:683563* + ID_OUI_FROM_DATABASE=SHENZHEN LIOWN ELECTRONICS CO.,LTD. + +OUI:0003B2* + ID_OUI_FROM_DATABASE=Radware + +OUI:2C600C* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:001E68* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:00A09B* + ID_OUI_FROM_DATABASE=QPSX COMMUNICATIONS, LTD. + +OUI:00E08B* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:00080D* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:0015B7* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:000569* + ID_OUI_FROM_DATABASE=VMware, Inc. + +OUI:0008F1* + ID_OUI_FROM_DATABASE=Voltaire + +OUI:001BDA* + ID_OUI_FROM_DATABASE=UTStarcom Inc + +OUI:FC4DD4* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:402CF4* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:0010C6* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:00247E* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:001639* + ID_OUI_FROM_DATABASE=Ubiquam Co., Ltd. + +OUI:183919* + ID_OUI_FROM_DATABASE=Unicoi Systems + +OUI:90A46A* + ID_OUI_FROM_DATABASE=SISNET CO., LTD + +OUI:14E7C8* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:280DFC* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:0015C1* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:0019C5* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:ACA213* + ID_OUI_FROM_DATABASE=Shenzhen Bilian electronic CO.,LTD + +OUI:38F8CA* + ID_OUI_FROM_DATABASE=OWIN Inc. + +OUI:54D272* + ID_OUI_FROM_DATABASE=Nuki Home Solutions GmbH + +OUI:9CA3A9* + ID_OUI_FROM_DATABASE=Guangzhou Juan Optical and Electronical Tech Joint Stock Co., Ltd + +OUI:1100AA* + ID_OUI_FROM_DATABASE=Private + +OUI:002067* + ID_OUI_FROM_DATABASE=Private + +OUI:9893CC* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:3CCD93* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:583F54* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:001C62* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:002483* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:E417D8* + ID_OUI_FROM_DATABASE=8BITDO TECHNOLOGY HK LIMITED + +OUI:40B0FA* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A09169* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:286C07* + ID_OUI_FROM_DATABASE=XIAOMI Electronics,CO.,LTD + +OUI:84D931* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:34FCEF* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:485929* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:505527* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:98D6F7* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:A8922C* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:44DC91* + ID_OUI_FROM_DATABASE=PLANEX COMMUNICATIONS INC. + +OUI:9CD332* + ID_OUI_FROM_DATABASE=PLC Technology Ltd + +OUI:94D723* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:A89DD2* + ID_OUI_FROM_DATABASE=Shanghai DareGlobal Technologies Co.,Ltd + +OUI:184A6F* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:A0F3E4* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:002105* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:000772* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:F06BCA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3423BA* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:D022BE* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:D02544* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) + +OUI:BC20A4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:14F42A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC851F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B85E7B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C462EA* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0023D6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002491* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001B98* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:44F459* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:34C3AC* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:94D771* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:4C3C16* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9401C2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B43A28* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:A8C83A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:849FB5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D0C1B1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F008F1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:782079* + ID_OUI_FROM_DATABASE=ID Tech + +OUI:98234E* + ID_OUI_FROM_DATABASE=Micromedia AG + +OUI:E80036* + ID_OUI_FROM_DATABASE=Befs co,. ltd + +OUI:24590B* + ID_OUI_FROM_DATABASE=White Sky Inc. Limited + +OUI:10C60C* + ID_OUI_FROM_DATABASE=Domino UK Ltd + +OUI:3842A6* + ID_OUI_FROM_DATABASE=Ingenieurbuero Stahlkopf + +OUI:E866C4* + ID_OUI_FROM_DATABASE=Diamanti + +OUI:78471D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:3816D1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:004A77* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:D48890* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002566* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00265F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001628* + ID_OUI_FROM_DATABASE=Magicard Ltd + +OUI:E4C801* + ID_OUI_FROM_DATABASE=BLU Products Inc + +OUI:00A6CA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:9C7DA3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F02FA7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:883FD3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A04E01* + ID_OUI_FROM_DATABASE=CENTRAL ENGINEERING co.,ltd. + +OUI:245CBF* + ID_OUI_FROM_DATABASE=NCSE + +OUI:84CD62* + ID_OUI_FROM_DATABASE=ShenZhen IDWELL Technology CO.,Ltd + +OUI:DC9FDB* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:B0958E* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:001A39* + ID_OUI_FROM_DATABASE=Merten GmbH&CoKG + +OUI:007B18* + ID_OUI_FROM_DATABASE=SENTRY Co., LTD. + +OUI:144D67* + ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd. + +OUI:34F39A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:20A8B9* + ID_OUI_FROM_DATABASE=Siemens + +OUI:C81B5C* + ID_OUI_FROM_DATABASE=BCTech + +OUI:3C2AF4* + ID_OUI_FROM_DATABASE=Brother Industries, LTD. + +OUI:20719E* + ID_OUI_FROM_DATABASE=SF Technology Co.,Ltd + +OUI:E0DDC0* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:982F3C* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + +OUI:380DD4* + ID_OUI_FROM_DATABASE=Primax Electronics Ltd. + +OUI:98FDB4* + ID_OUI_FROM_DATABASE=Primax Electronics Ltd. + +OUI:00157D* + ID_OUI_FROM_DATABASE=POSDATA + +OUI:F8E61A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:888322* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:84B541* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:18DC56* + ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd + +OUI:001F46* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001F0A* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00130A* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001E7E* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001C9C* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000CF8* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:000CF7* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001E1F* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001C17* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00182E* + ID_OUI_FROM_DATABASE=XStreamHD + +OUI:50016B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:58986F* + ID_OUI_FROM_DATABASE=Revolution Display + +OUI:28AC67* + ID_OUI_FROM_DATABASE=Mach Power, Rappresentanze Internazionali s.r.l. + +OUI:B0B28F* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:DC1A01* + ID_OUI_FROM_DATABASE=Ecoliv Technology ( Shenzhen ) Ltd. + +OUI:7CFE90* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:0002C9* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:D05FB8* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C4BE84* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:78A504* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:7C669D* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D03972* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:E0E5CF* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:7CEC79* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:74D6EA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017EB* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:883314* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:84DD20* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:1C4593* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:5C6B32* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E4* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D03761* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0024BA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0022A5* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0021BA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001833* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D8952F* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:649C8E* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:F4FC32* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:74DAEA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:04A316* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:98072D* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001AB6* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C8A030* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:34B1F7* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C4EDBA* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A40DBC* + ID_OUI_FROM_DATABASE=Xiamen Intretech Inc. + +OUI:EC8EAE* + ID_OUI_FROM_DATABASE=Nagravision SA + +OUI:606405* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:708BCD* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001A21* + ID_OUI_FROM_DATABASE=Brookhuis Applied Technologies BV + +OUI:00A00E* + ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + +OUI:1C330E* + ID_OUI_FROM_DATABASE=PernixData + +OUI:345760* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + +OUI:343DC4* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:6CEFC6* + ID_OUI_FROM_DATABASE=SHENZHEN TWOWING TECHNOLOGIES CO.,LTD. + +OUI:986B3D* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:CC65AD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:789684* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:90C792* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015CF* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:386BBB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00E06F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0004BD* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:5C571A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DCF* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E8ED05* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:901ACA* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002A10* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74E7C6* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:74F612* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:DC4517* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:F80BBE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:6CC1D2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:145BD1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:B077AC* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:B81619* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:A41588* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:38700C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:FC51A4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:287AEE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:641269* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001CC3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:14D4FE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:70B14E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D82522* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:707630* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:000CE5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:000E5C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015A8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001700* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0019A6* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0014E8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002180* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0026BA* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002641* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002374* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0025F2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0019C0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:886AB1* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:44D6E1* + ID_OUI_FROM_DATABASE=Snuza International Pty. Ltd. + +OUI:0015B9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001DF6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:ECE09B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:606BBD* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0000F0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:4844F7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:DC7144* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:A00BBA* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:1C5A3E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F47B5E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C44619* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F0F002* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:889FFA* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:5CAC4C* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:18F46A* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:3859F9* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:BC8556* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:9C2A70* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F82FA8* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0CEEE6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0C6076* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:90FBA6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00197D* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001C26* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:9CAD97* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:2C8158* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:142D27* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:843DC6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:407C7D* + ID_OUI_FROM_DATABASE=Nokia + +OUI:BC52B4* + ID_OUI_FROM_DATABASE=Nokia + +OUI:FC2FAA* + ID_OUI_FROM_DATABASE=Nokia + +OUI:903AA0* + ID_OUI_FROM_DATABASE=Nokia + +OUI:702526* + ID_OUI_FROM_DATABASE=Nokia + +OUI:38F7B2* + ID_OUI_FROM_DATABASE=SEOJUN ELECTRIC + +OUI:7802B7* + ID_OUI_FROM_DATABASE=ShenZhen Ultra Easy Technology CO.,LTD + +OUI:88AD43* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:E4186B* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:6C71BD* + ID_OUI_FROM_DATABASE=EZELINK TELECOM + +OUI:842519* + ID_OUI_FROM_DATABASE=Samsung Electronics + +OUI:88DEA9* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:FC83C6* + ID_OUI_FROM_DATABASE=N-Radio Technologies Co., Ltd. + +OUI:B4E782* + ID_OUI_FROM_DATABASE=Vivalnk + +OUI:008701* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:FC4203* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1C232C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08010F* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:CCA260* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:0015FF* + ID_OUI_FROM_DATABASE=Novatel Wireless Solutions, Inc. + +OUI:203CAE* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:748D08* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00D78F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A03BE3* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:18E29F* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:886B0F* + ID_OUI_FROM_DATABASE=Bluegiga Technologies OY + +OUI:001438* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:98541B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:CC61E5* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:404E36* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:9CB206* + ID_OUI_FROM_DATABASE=PROCENTEC + +OUI:1C40E8* + ID_OUI_FROM_DATABASE=SHENZHEN PROGRESS&WIN TECHNOLOGY CO.,LTD + +OUI:C8D3FF* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C3996* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:0054BD* + ID_OUI_FROM_DATABASE=Swelaser AB + +OUI:0057D2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:3C6716* + ID_OUI_FROM_DATABASE=Lily Robotics + +OUI:806AB0* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:A0F895* + ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. + +OUI:0078CD* + ID_OUI_FROM_DATABASE=Ignition Design Labs + +OUI:28ED6A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:34AB37* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60A37D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0056CD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7081EB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:086698* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002926* + ID_OUI_FROM_DATABASE=Applied Optoelectronics, Inc Taiwan Branch + +OUI:2CFD37* + ID_OUI_FROM_DATABASE=Blue Calypso, Inc. + +OUI:0C6127* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:001B11* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:001E58* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:002191* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:0022B0* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:F07D68* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:78542E* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:3CDD89* + ID_OUI_FROM_DATABASE=SOMO HOLDINGS & TECH. CO.,LTD. + +OUI:2C56DC* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:B8AF67* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:188B45* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B0C090* + ID_OUI_FROM_DATABASE=Chicony Electronics Co., Ltd. + +OUI:1CA770* + ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD + +OUI:C42F90* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + +OUI:9C5D12* + ID_OUI_FROM_DATABASE=Aerohive Networks Inc. + +OUI:A42BB0* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:4CE676* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:B0C745* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:CCE1D5* + ID_OUI_FROM_DATABASE=BUFFALO.INC + +OUI:B8FC9A* + ID_OUI_FROM_DATABASE=Le Shi Zhi Xin Electronic Technology (Tianjin) Limited + +OUI:2C4138* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C768A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0018FE* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0019BB* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:002264* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:002481* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000D9D* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0014C2* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:788B77* + ID_OUI_FROM_DATABASE=Standar Telecom + +OUI:84ACFB* + ID_OUI_FROM_DATABASE=Crouzet Automatismes + +OUI:34BA75* + ID_OUI_FROM_DATABASE=Tembo Systems, Inc. + +OUI:9486CD* + ID_OUI_FROM_DATABASE=SEOUL ELECTRONICS&TELECOM + +OUI:94ABDE* + ID_OUI_FROM_DATABASE=OMX Technology - FZE + +OUI:000E35* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:00207B* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:0013CE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:801934* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B8B81E* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:185E0F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C80E77* + ID_OUI_FROM_DATABASE=Le Shi Zhi Xin Electronic Technology (Tianjin) Limited + +OUI:843497* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:ECB1D7* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:3CA82A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:480FCF* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:5820B1* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C233A* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000EB3* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0004EA* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:00306E* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0060B0* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:24BE05* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:000423* + ID_OUI_FROM_DATABASE=Intel Corporation + +OUI:0008C7* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:0010E3* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:00805F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:BCEAFA* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:5C8A38* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:D89D67* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:2C44FD* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:F0921C* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:B4B52F* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:902155* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:64A769* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:BCCFCC* + ID_OUI_FROM_DATABASE=HTC Corporation + +OUI:B0F1A3* + ID_OUI_FROM_DATABASE=Fengfan (BeiJing) Technology Co., Ltd. + +OUI:7C7D3D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4482E5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:542758* + ID_OUI_FROM_DATABASE=Motorola (Wuhan) Mobility Technologies Communication Co., Ltd. + +OUI:4CD08A* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:20906F* + ID_OUI_FROM_DATABASE=Shenzhen Tencent Computer System Co., Ltd. + +OUI:1C7839* + ID_OUI_FROM_DATABASE=Shenzhen Tencent Computer System Co., Ltd. + +OUI:D837BE* + ID_OUI_FROM_DATABASE=Shanghai Gongjing Telecom Technology Co,LTD + +OUI:A4516F* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:FC64BA* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:246081* + ID_OUI_FROM_DATABASE=razberi technologies + +OUI:8CAB8E* + ID_OUI_FROM_DATABASE=Shanghai Feixun Communication Co.,Ltd. + +OUI:9060F1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:EC26CA* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:A09347* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:2C088C* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + +OUI:40F308* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:5CDAD4* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:000E6D* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:B05B67* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:38F889* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F4DCF9* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:904E2B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0C96BF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9CC172* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0014C9* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:00010F* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:080088* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:00051E* + ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc. + +OUI:384608* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B4B362* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B075D5* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:08181A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:002512* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:CCF954* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:703018* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:B0A37E* + ID_OUI_FROM_DATABASE=Qingdao Haier Telecom Co.,Ltd + +OUI:70A8E3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:F84ABF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4CB16C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:4C1FCC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:486276* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:AC4E91* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:E468A3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:80D09B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:581F28* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:8C34FD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:90671C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:587F66* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:BC25E0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C4072F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0CD6BD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A49947* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:346BD3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:1C1D67* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:84A8E4* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:202BC1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:3475C7* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:6CFA58* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:64A7DD* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:646A52* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:F873A2* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:64C354* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:B4B017* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:581626* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:741BB2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002586* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F8D111* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:F4EC38* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:20DCE6* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:1C6E4C* + ID_OUI_FROM_DATABASE=Logistic Service & Engineering Co.,Ltd + +OUI:00101F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001054* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:DCEB94* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5C838F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:AC7E8A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:382056* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:28CFE9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00502A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:005014* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090D9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:009092* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001029* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001007* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00605C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E0F7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E0B0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E0FE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E0A3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00E0F9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001BD7* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:105172* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9017AC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:94049C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C46AB7* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:68DFDD* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:64B473* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:7451BA* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:3480B3* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:5006AB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0050E2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:005050* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:009021* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0090B1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00023D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:18E728* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2C3ECF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1005CA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1CDEA7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:1C6A7A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:CCD8C1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C0ECE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F09E63* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F07F06* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:84802D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E0899D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A89D21* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:BCF1F2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C80084* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A0F849* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:88908D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:A46C2A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0021BE* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:7CB21B* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:002643* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:002433* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:745E1C* + ID_OUI_FROM_DATABASE=PIONEER CORPORATION + +OUI:0006F5* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:0006F7* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:000704* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:1C1D86* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A92* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:001D60* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:002215* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:20CF30* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:E0CB4E* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:1C872C* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:C4143C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:2401C7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:04DAD2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F41FC2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:4C0082* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:DCA5F4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C95F3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:5017FF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E8EDF3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:78DA6E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:24E9B3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E425E7* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:080007* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:000A95* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002241* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0023DF* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0025BC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:00264A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0026B0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:041E64* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D49A20* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9027E4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:60334B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A43135* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C35EB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:507A55* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A0999B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:24240E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:903C92* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:341298* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C293F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:488AD2* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + +OUI:A88E24* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E8802E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68AE20* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0B52D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80BE05* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D8BB2C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D04F7E* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:2C1F23* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:549F13* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B8098A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0DBE2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:18EE69* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:748114* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:18F643* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D0A637* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A01828* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D0034B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:5C5948* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:78CA39* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:18E7F4* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B8FF61* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC2B61* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1093E9* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:442A60* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E0F847* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:145A05* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:28CFDA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:148FC6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:283737* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:045453* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0CBA1* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:C06394* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C006D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:B09FBA* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC86D8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:8C2937* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:DC9B9C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:98F0AB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F0DBF8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:ACCF5C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3C15C2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:04489A* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:D8CF9C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:30F7C5* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:008865* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:40B395* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:3090AB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1CE62B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A0EDCD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A886DD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:54EAA8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E4C63D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:843835* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:7073CB* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C207B* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:842999* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:74E2F5* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:20C9D0* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1402EC* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:707938* + ID_OUI_FROM_DATABASE=Wuxi Zhanrui Electronic Technology Co.,LTD + +OUI:646A74* + ID_OUI_FROM_DATABASE=AUTH-SERVERS, LLC + +OUI:34C9F0* + ID_OUI_FROM_DATABASE=LM Technologies Ltd + +OUI:E034E4* + ID_OUI_FROM_DATABASE=Feit Electric Company, Inc. + +OUI:98E848* + ID_OUI_FROM_DATABASE=Axiim + +OUI:A0F9E0* + ID_OUI_FROM_DATABASE=VIVATEL COMPANY LIMITED + +OUI:F8C372* + ID_OUI_FROM_DATABASE=TSUZUKI DENKI + +OUI:908D78* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:A4CC32* + ID_OUI_FROM_DATABASE=Inficomm Co., Ltd + +OUI:582BDB* + ID_OUI_FROM_DATABASE=Pax AB + +OUI:D00F6D* + ID_OUI_FROM_DATABASE=T&W Electronics Company + +OUI:48BF74* + ID_OUI_FROM_DATABASE=Baicells Technologies Co.,LTD + +OUI:38F557* + ID_OUI_FROM_DATABASE=JOLATA, INC. + +OUI:280E8B* + ID_OUI_FROM_DATABASE=Beijing Spirit Technology Development Co., Ltd. + +OUI:F44D30* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:DC9A8E* + ID_OUI_FROM_DATABASE=Nanjing Cocomm electronics co., LTD + +OUI:C4EF70* + ID_OUI_FROM_DATABASE=Home Skinovations + +OUI:B813E9* + ID_OUI_FROM_DATABASE=Trace Live Network + +OUI:746F19* + ID_OUI_FROM_DATABASE=ICARVISIONS (SHENZHEN) TECHNOLOGY CO., LTD. + +OUI:7C7176* + ID_OUI_FROM_DATABASE=Wuxi iData Technology Company Ltd. + +OUI:7C0191* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:70480F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A4B805* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:587F57* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80D605* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:68A828* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:988744* + ID_OUI_FROM_DATABASE=Wuxi Hongda Science and Technology Co.,LTD + +OUI:C869CD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:BC6C21* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C8DD3* + ID_OUI_FROM_DATABASE=Leonton Technologies + +OUI:246C8A* + ID_OUI_FROM_DATABASE=YUKAI Engineering + +OUI:A43831* + ID_OUI_FROM_DATABASE=RF elements s.r.o. + +OUI:D0BAE4* + ID_OUI_FROM_DATABASE=Shanghai MXCHIP Information Technology Co., Ltd. + +OUI:A4DCBE* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:10CC1B* + ID_OUI_FROM_DATABASE=Liverock technologies,INC + +OUI:48B620* + ID_OUI_FROM_DATABASE=ROLI Ltd. + +OUI:20D160* + ID_OUI_FROM_DATABASE=Private + +OUI:382187* + ID_OUI_FROM_DATABASE=Midea Group Co., Ltd. + +OUI:305A3A* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:A87285* + ID_OUI_FROM_DATABASE=IDT, INC. + +OUI:AC1FD7* + ID_OUI_FROM_DATABASE=Real Vision Technology Co.,Ltd. + +OUI:C8A2CE* + ID_OUI_FROM_DATABASE=Oasis Media Systems LLC + +OUI:A4DEC9* + ID_OUI_FROM_DATABASE=QLove Mobile Intelligence Information Technology (W.H.) Co. Ltd. + +OUI:A4A6A9* + ID_OUI_FROM_DATABASE=Private + +OUI:0469F8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C7A03* + ID_OUI_FROM_DATABASE=Ciena Corporation + +OUI:380AAB* + ID_OUI_FROM_DATABASE=Formlabs + +OUI:F41535* + ID_OUI_FROM_DATABASE=SPON Communication Technology Co.,Ltd + +OUI:E41A2C* + ID_OUI_FROM_DATABASE=ZPE Systems, Inc. + +OUI:A815D6* + ID_OUI_FROM_DATABASE=Shenzhen Meione Technology CO., LTD + +OUI:D09380* + ID_OUI_FROM_DATABASE=Ducere Technologies Pvt. Ltd. + +OUI:84A788* + ID_OUI_FROM_DATABASE=Perples + +OUI:6889C1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:845B12* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:143EBF* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:041E7A* + ID_OUI_FROM_DATABASE=DSPWorks + +OUI:38B725* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:4CC681* + ID_OUI_FROM_DATABASE=Shenzhen Aisat Electronic Co., Ltd. + +OUI:28B9D9* + ID_OUI_FROM_DATABASE=Radisys Corporation + +OUI:E0553D* + ID_OUI_FROM_DATABASE=Cisco Meraki + +OUI:0894EF* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:E0319E* + ID_OUI_FROM_DATABASE=Valve Corporation + +OUI:E4A32F* + ID_OUI_FROM_DATABASE=Shanghai Artimen Technology Co., Ltd. + +OUI:D47BB0* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:5045F7* + ID_OUI_FROM_DATABASE=Liuhe Intelligence Technology Ltd. + +OUI:20F510* + ID_OUI_FROM_DATABASE=Codex Digital Limited + +OUI:949F3E* + ID_OUI_FROM_DATABASE=Sonos, Inc. + +OUI:788E33* + ID_OUI_FROM_DATABASE=Jiangsu SEUIC Technology Co.,Ltd + +OUI:E01AEA* + ID_OUI_FROM_DATABASE=Allied Telesis, Inc. + +OUI:340CED* + ID_OUI_FROM_DATABASE=Moduel AB + +OUI:507B9D* + ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd + +OUI:6C7220* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:F02624* + ID_OUI_FROM_DATABASE=WAFA TECHNOLOGIES CO., LTD. + +OUI:F8F464* + ID_OUI_FROM_DATABASE=Rawe Electonic GmbH + +OUI:F4672D* + ID_OUI_FROM_DATABASE=ShenZhen Topstar Technology Company + +OUI:382B78* + ID_OUI_FROM_DATABASE=ECO PLUGS ENTERPRISE CO., LTD + +OUI:BCEB5F* + ID_OUI_FROM_DATABASE=Fujian Beifeng Telecom Technology Co., Ltd. + +OUI:800B51* + ID_OUI_FROM_DATABASE=Chengdu XGimi Technology Co.,Ltd + +OUI:00FC8D* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:1CC586* + ID_OUI_FROM_DATABASE=Absolute Acoustics + +OUI:E076D0* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:24B0A9* + ID_OUI_FROM_DATABASE=Shanghai Mobiletek Communication Ltd. + +OUI:64167F* + ID_OUI_FROM_DATABASE=Polycom + +OUI:54E2C8* + ID_OUI_FROM_DATABASE=Dongguan Aoyuan Electronics Technology Co., Ltd + +OUI:20D75A* + ID_OUI_FROM_DATABASE=Posh Mobile Limited + +OUI:88D37B* + ID_OUI_FROM_DATABASE=FirmTek, LLC + +OUI:10AF78* + ID_OUI_FROM_DATABASE=Shenzhen ATUE Technology Co., Ltd + +OUI:B0966C* + ID_OUI_FROM_DATABASE=Lanbowan Technology Ltd. + +OUI:A408EA* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + +OUI:D4F9A1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:9CB6D0* + ID_OUI_FROM_DATABASE=Rivet Networks + +OUI:D0C0BF* + ID_OUI_FROM_DATABASE=Actions Microelectronics Co., Ltd + +OUI:94F665* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:E04B45* + ID_OUI_FROM_DATABASE=Hi-P Electronics Pte Ltd + +OUI:6C4598* + ID_OUI_FROM_DATABASE=Antex Electronic Corp. + +OUI:94A7B7* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:3C8375* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:C8458F* + ID_OUI_FROM_DATABASE=Wyler AG + +OUI:149A10* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:FC9AFA* + ID_OUI_FROM_DATABASE=Motus Global Inc. + +OUI:5CB43E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:FCE1FB* + ID_OUI_FROM_DATABASE=Array Networks + +OUI:54E140* + ID_OUI_FROM_DATABASE=INGENICO + +OUI:14157C* + ID_OUI_FROM_DATABASE=TOKYO COSMOS ELECTRIC CO.,LTD. + +OUI:408D5C* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:6CE01E* + ID_OUI_FROM_DATABASE=Modcam AB + +OUI:E8F2E3* + ID_OUI_FROM_DATABASE=Starcor Beijing Co.,Limited + +OUI:D048F3* + ID_OUI_FROM_DATABASE=DATTUS Inc + +OUI:CC19A8* + ID_OUI_FROM_DATABASE=PT Inovação e Sistemas SA + +OUI:6C4418* + ID_OUI_FROM_DATABASE=Zappware + +OUI:44962B* + ID_OUI_FROM_DATABASE=Aidon Oy + +OUI:D4D7A9* + ID_OUI_FROM_DATABASE=Shanghai Kaixiang Info Tech LTD + +OUI:185D9A* + ID_OUI_FROM_DATABASE=BobjGear LLC + +OUI:884157* + ID_OUI_FROM_DATABASE=Shenzhen Atsmart Technology Co.,Ltd. + +OUI:3CDA2A* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:747336* + ID_OUI_FROM_DATABASE=MICRODIGTAL Inc + +OUI:0CE725* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:6C2E72* + ID_OUI_FROM_DATABASE=B&B EXPORTING LIMITED + +OUI:FC3288* + ID_OUI_FROM_DATABASE=CELOT Wireless Co., Ltd + +OUI:BCB308* + ID_OUI_FROM_DATABASE=HONGKONG RAGENTEK COMMUNICATION TECHNOLOGY CO.,LIMITED + +OUI:445ECD* + ID_OUI_FROM_DATABASE=Razer Inc + +OUI:749637* + ID_OUI_FROM_DATABASE=Todaair Electronic Co., Ltd + +OUI:2031EB* + ID_OUI_FROM_DATABASE=HDSN + +OUI:C0335E* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:ACCAAB* + ID_OUI_FROM_DATABASE=Virtual Electric Inc + +OUI:241B44* + ID_OUI_FROM_DATABASE=Hangzhou Tuners Electronics Co., Ltd + +OUI:90C35F* + ID_OUI_FROM_DATABASE=Nanjing Jiahao Technology Co., Ltd. + +OUI:18F145* + ID_OUI_FROM_DATABASE=NetComm Wireless Limited + +OUI:4CA515* + ID_OUI_FROM_DATABASE=Baikal Electronics JSC + +OUI:9CE230* + ID_OUI_FROM_DATABASE=JULONG CO,.LTD. + +OUI:34873D* + ID_OUI_FROM_DATABASE=Quectel Wireless Solution Co.,Ltd. + +OUI:186882* + ID_OUI_FROM_DATABASE=Beward R&D Co., Ltd. + +OUI:344CA4* + ID_OUI_FROM_DATABASE=amazipoint technology Ltd. + +OUI:A8F038* + ID_OUI_FROM_DATABASE=SHEN ZHEN SHI JIN HUA TAI ELECTRONICS CO.,LTD + +OUI:74E277* + ID_OUI_FROM_DATABASE=Vizmonet Pte Ltd + +OUI:10A659* + ID_OUI_FROM_DATABASE=Mobile Create Co.,Ltd. + +OUI:58856E* + ID_OUI_FROM_DATABASE=QSC AG + +OUI:FCAFAC* + ID_OUI_FROM_DATABASE=Socionext Inc. + +OUI:F8C397* + ID_OUI_FROM_DATABASE=NZXT Corp. Ltd. + +OUI:C4366C* + ID_OUI_FROM_DATABASE=LG Innotek + +OUI:60D9A0* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:5C3B35* + ID_OUI_FROM_DATABASE=Gehirn Inc. + +OUI:5CF7C3* + ID_OUI_FROM_DATABASE=SYNTECH (HK) TECHNOLOGY LIMITED + +OUI:3CC2E1* + ID_OUI_FROM_DATABASE=XINHUA CONTROL ENGINEERING CO.,LTD + +OUI:7C534A* + ID_OUI_FROM_DATABASE=Metamako + +OUI:9C3066* + ID_OUI_FROM_DATABASE=RWE Effizienz GmbH + +OUI:FCA22A* + ID_OUI_FROM_DATABASE=PT. Callysta Multi Engineering + +OUI:247656* + ID_OUI_FROM_DATABASE=Shanghai Net Miles Fiber Optics Technology Co., LTD. + +OUI:A0ADA1* + ID_OUI_FROM_DATABASE=JMR Electronics, Inc + +OUI:601970* + ID_OUI_FROM_DATABASE=HUIZHOU QIAOXING ELECTRONICS TECHNOLOGY CO., LTD. + +OUI:887033* + ID_OUI_FROM_DATABASE=Hangzhou Silan Microelectronic Inc + +OUI:8C7967* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:D083D4* + ID_OUI_FROM_DATABASE=XTel ApS + +OUI:78F944* + ID_OUI_FROM_DATABASE=Private + +OUI:CCA4AF* + ID_OUI_FROM_DATABASE=Shenzhen Sowell Technology Co., LTD + +OUI:84F129* + ID_OUI_FROM_DATABASE=Metrascale Inc. + +OUI:2028BC* + ID_OUI_FROM_DATABASE=Visionscape Co,. Ltd. + +OUI:B8F080* + ID_OUI_FROM_DATABASE=SPS, INC. + +OUI:7858F3* + ID_OUI_FROM_DATABASE=Vachen Co.,Ltd + +OUI:FCDC4A* + ID_OUI_FROM_DATABASE=G-Wearables Corp. + +OUI:F42C56* + ID_OUI_FROM_DATABASE=SENOR TECH CO LTD + +OUI:50502A* + ID_OUI_FROM_DATABASE=Egardia + +OUI:48EE0C* + ID_OUI_FROM_DATABASE=D-Link International + +OUI:48C093* + ID_OUI_FROM_DATABASE=Xirrus, Inc. + +OUI:3C1A0F* + ID_OUI_FROM_DATABASE=ClearSky Data + +OUI:ACB57D* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:DCE1AD* + ID_OUI_FROM_DATABASE=Shenzhen Wintop Photoelectric Technology Co., Ltd + +OUI:900CB4* + ID_OUI_FROM_DATABASE=Alinket Electronic Technology Co., Ltd + +OUI:883B8B* + ID_OUI_FROM_DATABASE=Cheering Connection Co. Ltd. + +OUI:94D417* + ID_OUI_FROM_DATABASE=GPI KOREA INC. + +OUI:D855A3* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:70DA9C* + ID_OUI_FROM_DATABASE=TECSEN + +OUI:6CF5E8* + ID_OUI_FROM_DATABASE=Mooredoll Inc. + +OUI:70FF5C* + ID_OUI_FROM_DATABASE=Cheerzing Communication(Xiamen)Technology Co.,Ltd + +OUI:E0107F* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:08115E* + ID_OUI_FROM_DATABASE=Bitel Co., Ltd. + +OUI:44CE7D* + ID_OUI_FROM_DATABASE=SFR + +OUI:0881BC* + ID_OUI_FROM_DATABASE=HongKong Ipro Technology Co., Limited + +OUI:4C16F1* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:800902* + ID_OUI_FROM_DATABASE=Keysight Technologies, Inc. + +OUI:6872DC* + ID_OUI_FROM_DATABASE=CETORY.TV Company Limited + +OUI:D8B6B7* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:0499E6* + ID_OUI_FROM_DATABASE=Shenzhen Yoostar Technology Co., Ltd + +OUI:94BF95* + ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd + +OUI:FC9FE1* + ID_OUI_FROM_DATABASE=CONWIN.Tech. Ltd + +OUI:90203A* + ID_OUI_FROM_DATABASE=BYD Precision Manufacture Co.,Ltd + +OUI:A81B5D* + ID_OUI_FROM_DATABASE=Foxtel Management Pty Ltd + +OUI:B8BD79* + ID_OUI_FROM_DATABASE=TrendPoint Systems + +OUI:2C010B* + ID_OUI_FROM_DATABASE=NASCENT Technology, LLC - RemKon + +OUI:D4EC86* + ID_OUI_FROM_DATABASE=LinkedHope Intelligent Technologies Co., Ltd + +OUI:20A99B* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:6C7660* + ID_OUI_FROM_DATABASE=KYOCERA Corporation + +OUI:A0A3E2* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:54098D* + ID_OUI_FROM_DATABASE=deister electronic GmbH + +OUI:F0FE6B* + ID_OUI_FROM_DATABASE=Shanghai High-Flying Electronics Technology Co., Ltd + +OUI:3CAE69* + ID_OUI_FROM_DATABASE=ESA Elektroschaltanlagen Grimma GmbH + +OUI:00F3DB* + ID_OUI_FROM_DATABASE=WOO Sports + +OUI:08A5C8* + ID_OUI_FROM_DATABASE=Sunnovo International Limited + +OUI:848EDF* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:CCBDD3* + ID_OUI_FROM_DATABASE=Ultimaker B.V. + +OUI:50294D* + ID_OUI_FROM_DATABASE=NANJING IOT SENSOR TECHNOLOGY CO,LTD + +OUI:0CCFD1* + ID_OUI_FROM_DATABASE=SPRINGWAVE Co., Ltd + +OUI:58108C* + ID_OUI_FROM_DATABASE=Intelbras + +OUI:187117* + ID_OUI_FROM_DATABASE=eta plus electronic gmbh + +OUI:7CB177* + ID_OUI_FROM_DATABASE=Satelco AG + +OUI:8C5D60* + ID_OUI_FROM_DATABASE=UCI Corporation Co.,Ltd. + +OUI:104B46* + ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation + +OUI:4C0BBE* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:08EB29* + ID_OUI_FROM_DATABASE=Jiangsu Huitong Group Co.,Ltd. + +OUI:E48C0F* + ID_OUI_FROM_DATABASE=Discovery Insure + +OUI:587FB7* + ID_OUI_FROM_DATABASE=SONAR INDUSTRIAL CO., LTD. + +OUI:E42354* + ID_OUI_FROM_DATABASE=SHENZHEN FUZHI SOFTWARE TECHNOLOGY CO.,LTD + +OUI:207693* + ID_OUI_FROM_DATABASE=Lenovo (Beijing) Limited. + +OUI:C4BD6A* + ID_OUI_FROM_DATABASE=SKF GmbH + +OUI:14488B* + ID_OUI_FROM_DATABASE=Shenzhen Doov Technology Co.,Ltd + +OUI:603696* + ID_OUI_FROM_DATABASE=The Sapling Company + +OUI:54FFCF* + ID_OUI_FROM_DATABASE=Mopria Alliance + +OUI:BCBC46* + ID_OUI_FROM_DATABASE=SKS Welding Systems GmbH + +OUI:A8D88A* + ID_OUI_FROM_DATABASE=Wyconn + +OUI:00E6E8* + ID_OUI_FROM_DATABASE=Netzin Technology Corporation,.Ltd. + +OUI:64B21D* + ID_OUI_FROM_DATABASE=Chengdu Phycom Tech Co., Ltd. + +OUI:88708C* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:F03D29* + ID_OUI_FROM_DATABASE=Actility + +OUI:909F33* + ID_OUI_FROM_DATABASE=EFM Networks + +OUI:849681* + ID_OUI_FROM_DATABASE=Cathay Communication Co.,Ltd + +OUI:A056B2* + ID_OUI_FROM_DATABASE=Harman/Becker Automotive Systems GmbH + +OUI:40C62A* + ID_OUI_FROM_DATABASE=Shanghai Jing Ren Electronic Technology Co., Ltd. + +OUI:E8150E* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:F4D032* + ID_OUI_FROM_DATABASE=Yunnan Ideal Information&Technology.,Ltd + +OUI:44A6E5* + ID_OUI_FROM_DATABASE=THINKING TECHNOLOGY CO.,LTD + +OUI:A8329A* + ID_OUI_FROM_DATABASE=Digicom Futuristic Technologies Ltd. + +OUI:B40AC6* + ID_OUI_FROM_DATABASE=DEXON Systems Ltd. + +OUI:480C49* + ID_OUI_FROM_DATABASE=NAKAYO TELECOMMUNICATIONS,INC + +OUI:5CB8CB* + ID_OUI_FROM_DATABASE=Allis Communications + +OUI:E85D6B* + ID_OUI_FROM_DATABASE=Luminate Wireless + +OUI:8C3357* + ID_OUI_FROM_DATABASE=HiteVision Digital Media Technology Co.,Ltd. + +OUI:506787* + ID_OUI_FROM_DATABASE=iTellus + +OUI:F4D261* + ID_OUI_FROM_DATABASE=SEMOCON Co., Ltd + +OUI:D05AF1* + ID_OUI_FROM_DATABASE=Shenzhen Pulier Tech CO.,Ltd + +OUI:481A84* + ID_OUI_FROM_DATABASE=Pointer Telocation Ltd + +OUI:E4F4C6* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:DC663A* + ID_OUI_FROM_DATABASE=Apacer Technology Inc. + +OUI:B009D3* + ID_OUI_FROM_DATABASE=Avizia + +OUI:3CAA3F* + ID_OUI_FROM_DATABASE=iKey, Ltd. + +OUI:0C383E* + ID_OUI_FROM_DATABASE=Fanvil Technology Co., Ltd. + +OUI:60CDA9* + ID_OUI_FROM_DATABASE=Abloomy + +OUI:B40B44* + ID_OUI_FROM_DATABASE=Smartisan Technology Co., Ltd. + +OUI:A0FC6E* + ID_OUI_FROM_DATABASE=Telegrafia a.s. + +OUI:44D4E0* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:D0FA1D* + ID_OUI_FROM_DATABASE=Qihoo 360 Technology Co.,Ltd + +OUI:046785* + ID_OUI_FROM_DATABASE=scemtec Hard- und Software fuer Mess- und Steuerungstechnik GmbH + +OUI:FC6DC0* + ID_OUI_FROM_DATABASE=BME CORPORATION + +OUI:784561* + ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. + +OUI:D896E0* + ID_OUI_FROM_DATABASE=Alibaba Cloud Computing Ltd. + +OUI:300D2A* + ID_OUI_FROM_DATABASE=Zhejiang Wellcom Technology Co.,Ltd. + +OUI:64EAC5* + ID_OUI_FROM_DATABASE=SiboTech Automation Co., Ltd. + +OUI:74DA38* + ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. + +OUI:F4F26D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:5C1515* + ID_OUI_FROM_DATABASE=ADVAN + +OUI:D0A0D6* + ID_OUI_FROM_DATABASE=Chengdu TD Tech Ltd. + +OUI:8CBF9D* + ID_OUI_FROM_DATABASE=Shanghai Xinyou Information Technology Ltd. Co. + +OUI:D49398* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:78D66F* + ID_OUI_FROM_DATABASE=Aristocrat Technologies Australia Pty. Ltd. + +OUI:50C7BF* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:C06118* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:D0C7C0* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:209AE9* + ID_OUI_FROM_DATABASE=Volacomm Co., Ltd + +OUI:345D10* + ID_OUI_FROM_DATABASE=Wytek + +OUI:58E326* + ID_OUI_FROM_DATABASE=Compass Technologies Inc. + +OUI:848DC7* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:A8BD3A* + ID_OUI_FROM_DATABASE=UNIONMAN TECHNOLOGY CO.,LTD + +OUI:C44E1F* + ID_OUI_FROM_DATABASE=BlueN + +OUI:CCA614* + ID_OUI_FROM_DATABASE=AIFA TECHNOLOGY CORP. + +OUI:B0869E* + ID_OUI_FROM_DATABASE=Chloride S.r.L + +OUI:344F5C* + ID_OUI_FROM_DATABASE=R&M AG + +OUI:A46CC1* + ID_OUI_FROM_DATABASE=LTi REEnergy GmbH + +OUI:90DB46* + ID_OUI_FROM_DATABASE=E-LEAD ELECTRONIC CO., LTD + +OUI:D42F23* + ID_OUI_FROM_DATABASE=Akenori PTE Ltd + +OUI:286336* + ID_OUI_FROM_DATABASE=Siemens AG - Industrial Automation - EWA + +OUI:38F098* + ID_OUI_FROM_DATABASE=Vapor Stone Rail Systems + +OUI:400107* + ID_OUI_FROM_DATABASE=Arista Corp + +OUI:4C8B30* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:0805CD* + ID_OUI_FROM_DATABASE=DongGuang EnMai Electronic Product Co.Ltd. + +OUI:0092FA* + ID_OUI_FROM_DATABASE=SHENZHEN WISKY TECHNOLOGY CO.,LTD + +OUI:4CF45B* + ID_OUI_FROM_DATABASE=Blue Clover Devices + +OUI:B06971* + ID_OUI_FROM_DATABASE=DEI Sales, Inc. + +OUI:58493B* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:580528* + ID_OUI_FROM_DATABASE=LABRIS NETWORKS + +OUI:28656B* + ID_OUI_FROM_DATABASE=Keystone Microtech Corporation + +OUI:EC2E4E* + ID_OUI_FROM_DATABASE=HITACHI-LG DATA STORAGE INC + +OUI:505800* + ID_OUI_FROM_DATABASE=WyTec International, Inc. + +OUI:78923E* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:D4CFF9* + ID_OUI_FROM_DATABASE=Shenzhen Sen5 Technology Co., Ltd. + +OUI:D8492F* + ID_OUI_FROM_DATABASE=CANON INC. + +OUI:D46761* + ID_OUI_FROM_DATABASE=SAHAB TECHNOLOGY + +OUI:145645* + ID_OUI_FROM_DATABASE=Savitech Corp. + +OUI:D4E08E* + ID_OUI_FROM_DATABASE=ValueHD Corporation + +OUI:70305D* + ID_OUI_FROM_DATABASE=Ubiquoss Inc + +OUI:5850AB* + ID_OUI_FROM_DATABASE=TLS Corporation + +OUI:90DFB7* + ID_OUI_FROM_DATABASE=s.m.s smart microwave sensors GmbH + +OUI:B843E4* + ID_OUI_FROM_DATABASE=Vlatacom + +OUI:8425A4* + ID_OUI_FROM_DATABASE=Tariox Limited + +OUI:E07F53* + ID_OUI_FROM_DATABASE=TECHBOARD SRL + +OUI:4C0DEE* + ID_OUI_FROM_DATABASE=JABIL CIRCUIT (SHANGHAI) LTD. + +OUI:A07771* + ID_OUI_FROM_DATABASE=Vialis BV + +OUI:D0BD01* + ID_OUI_FROM_DATABASE=DS International + +OUI:C0C569* + ID_OUI_FROM_DATABASE=SHANGHAI LYNUC CNC TECHNOLOGY CO.,LTD + +OUI:200E95* + ID_OUI_FROM_DATABASE=IEC – TC9 WG43 + +OUI:E0DB88* + ID_OUI_FROM_DATABASE=Open Standard Digital-IF Interface for SATCOM Systems + +OUI:D86194* + ID_OUI_FROM_DATABASE=Objetivos y Sevicios de Valor Añadido + +OUI:589CFC* + ID_OUI_FROM_DATABASE=FreeBSD Foundation + +OUI:602103* + ID_OUI_FROM_DATABASE=STCUBE.INC + +OUI:085DDD* + ID_OUI_FROM_DATABASE=Mercury Corporation + +OUI:98349D* + ID_OUI_FROM_DATABASE=Krauss Maffei Technologies GmbH + +OUI:18CC23* + ID_OUI_FROM_DATABASE=Philio Technology Corporation + +OUI:648D9E* + ID_OUI_FROM_DATABASE=IVT Electronic Co.,Ltd + +OUI:CC95D7* + ID_OUI_FROM_DATABASE=Vizio, Inc + +OUI:749C52* + ID_OUI_FROM_DATABASE=Huizhou Desay SV Automotive Co., Ltd. + +OUI:C0F79D* + ID_OUI_FROM_DATABASE=Powercode + +OUI:3C0C48* + ID_OUI_FROM_DATABASE=Servergy, Inc. + +OUI:68D247* + ID_OUI_FROM_DATABASE=Portalis LC + +OUI:FC27A2* + ID_OUI_FROM_DATABASE=TRANS ELECTRIC CO., LTD. + +OUI:14C089* + ID_OUI_FROM_DATABASE=DUNE HD LTD + +OUI:F08A28* + ID_OUI_FROM_DATABASE=JIANGSU HENGSION ELECTRONIC S and T CO.,LTD + +OUI:A8574E* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:DC3EF8* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:706173* + ID_OUI_FROM_DATABASE=Calantec GmbH + +OUI:50C271* + ID_OUI_FROM_DATABASE=SECURETECH INC + +OUI:7C49B9* + ID_OUI_FROM_DATABASE=Plexus Manufacturing Sdn Bhd + +OUI:184462* + ID_OUI_FROM_DATABASE=Riava Networks, Inc. + +OUI:9C443D* + ID_OUI_FROM_DATABASE=CHENGDU XUGUANG TECHNOLOGY CO, LTD + +OUI:74A4B5* + ID_OUI_FROM_DATABASE=Powerleader Science and Technology Co. Ltd. + +OUI:BC4100* + ID_OUI_FROM_DATABASE=CODACO ELECTRONIC s.r.o. + +OUI:7CCD3C* + ID_OUI_FROM_DATABASE=Guangzhou Juzing Technology Co., Ltd + +OUI:10B26B* + ID_OUI_FROM_DATABASE=base Co.,Ltd. + +OUI:DCCEBC* + ID_OUI_FROM_DATABASE=Shenzhen JSR Technology Co.,Ltd. + +OUI:9486D4* + ID_OUI_FROM_DATABASE=Surveillance Pro Corporation + +OUI:F89550* + ID_OUI_FROM_DATABASE=Proton Products Chengdu Ltd + +OUI:447BC4* + ID_OUI_FROM_DATABASE=DualShine Technology(SZ)Co.,Ltd + +OUI:542F89* + ID_OUI_FROM_DATABASE=Euclid Laboratories, Inc. + +OUI:48B977* + ID_OUI_FROM_DATABASE=PulseOn Oy + +OUI:AC2DA3* + ID_OUI_FROM_DATABASE=TXTR GmbH + +OUI:C8F68D* + ID_OUI_FROM_DATABASE=S.E.TECHNOLOGIES LIMITED + +OUI:BC14EF* + ID_OUI_FROM_DATABASE=ITON Technology Limited + +OUI:14F28E* + ID_OUI_FROM_DATABASE=ShenYang ZhongKe-Allwin Technology Co.LTD + +OUI:C064C6* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:9C44A6* + ID_OUI_FROM_DATABASE=SwiftTest, Inc. + +OUI:44C4A9* + ID_OUI_FROM_DATABASE=Opticom Communication, LLC + +OUI:6C3C53* + ID_OUI_FROM_DATABASE=SoundHawk Corp + +OUI:64BABD* + ID_OUI_FROM_DATABASE=SDJ Technologies, Inc. + +OUI:889166* + ID_OUI_FROM_DATABASE=Viewcooper Corp. + +OUI:103378* + ID_OUI_FROM_DATABASE=FLECTRON Co., LTD + +OUI:DC0575* + ID_OUI_FROM_DATABASE=SIEMENS ENERGY AUTOMATION + +OUI:5C1193* + ID_OUI_FROM_DATABASE=Seal One AG + +OUI:B4527E* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:50E14A* + ID_OUI_FROM_DATABASE=Private + +OUI:68FCB3* + ID_OUI_FROM_DATABASE=Next Level Security Systems, Inc. + +OUI:70305E* + ID_OUI_FROM_DATABASE=Nanjing Zhongke Menglian Information Technology Co.,LTD + +OUI:9C8888* + ID_OUI_FROM_DATABASE=Simac Techniek NV + +OUI:180C14* + ID_OUI_FROM_DATABASE=iSonea Limited + +OUI:8CAE89* + ID_OUI_FROM_DATABASE=Y-cam Solutions Ltd + +OUI:58B961* + ID_OUI_FROM_DATABASE=SOLEM Electronique + +OUI:F46ABC* + ID_OUI_FROM_DATABASE=Adonit Corp. Ltd. + +OUI:20180E* + ID_OUI_FROM_DATABASE=Shenzhen Sunchip Technology Co., Ltd + +OUI:80B219* + ID_OUI_FROM_DATABASE=ELEKTRON TECHNOLOGY UK LIMITED + +OUI:D08A55* + ID_OUI_FROM_DATABASE=Skullcandy + +OUI:C4D655* + ID_OUI_FROM_DATABASE=Tercel technology co.,ltd + +OUI:9CA10A* + ID_OUI_FROM_DATABASE=SCLE SFE + +OUI:78D99F* + ID_OUI_FROM_DATABASE=NuCom HK Ltd. + +OUI:44C56F* + ID_OUI_FROM_DATABASE=NGN Easy Satfinder (Tianjin) Electronic Co., Ltd + +OUI:2C5A05* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:848336* + ID_OUI_FROM_DATABASE=Newrun + +OUI:EC71DB* + ID_OUI_FROM_DATABASE=Shenzhen Baichuan Digital Technology Co., Ltd. + +OUI:B8266C* + ID_OUI_FROM_DATABASE=ANOV France + +OUI:284D92* + ID_OUI_FROM_DATABASE=Luminator + +OUI:1C4BB9* + ID_OUI_FROM_DATABASE=SMG ENTERPRISE, LLC + +OUI:0C5CD8* + ID_OUI_FROM_DATABASE=DOLI Elektronik GmbH + +OUI:2C5FF3* + ID_OUI_FROM_DATABASE=Pertronic Industries + +OUI:E0AF4B* + ID_OUI_FROM_DATABASE=Pluribus Networks, Inc. + +OUI:C85663* + ID_OUI_FROM_DATABASE=Sunflex Europe GmbH + +OUI:88FED6* + ID_OUI_FROM_DATABASE=ShangHai WangYong Software Co., Ltd. + +OUI:600347* + ID_OUI_FROM_DATABASE=Billion Electric Co. Ltd. + +OUI:084027* + ID_OUI_FROM_DATABASE=Gridstore Inc. + +OUI:7C2048* + ID_OUI_FROM_DATABASE=KoamTac + +OUI:705986* + ID_OUI_FROM_DATABASE=OOO TTV + +OUI:20DF3F* + ID_OUI_FROM_DATABASE=Nanjing SAC Power Grid Automation Co., Ltd. + +OUI:30786B* + ID_OUI_FROM_DATABASE=TIANJIN Golden Pentagon Electronics Co., Ltd. + +OUI:4CD637* + ID_OUI_FROM_DATABASE=Qsono Electronics Co., Ltd + +OUI:8CF945* + ID_OUI_FROM_DATABASE=Power Automation pte Ltd + +OUI:2C922C* + ID_OUI_FROM_DATABASE=Kishu Giken Kogyou Company Ltd,. + +OUI:509871* + ID_OUI_FROM_DATABASE=Inventum Technologies Private Limited + +OUI:384233* + ID_OUI_FROM_DATABASE=Wildeboer Bauteile GmbH + +OUI:9440A2* + ID_OUI_FROM_DATABASE=Anywave Communication Technologies, Inc. + +OUI:7CB77B* + ID_OUI_FROM_DATABASE=Paradigm Electronics Inc + +OUI:28A241* + ID_OUI_FROM_DATABASE=exlar corp + +OUI:9876B6* + ID_OUI_FROM_DATABASE=Adafruit + +OUI:AC220B* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:88354C* + ID_OUI_FROM_DATABASE=Transics + +OUI:709BFC* + ID_OUI_FROM_DATABASE=Bryton Inc. + +OUI:D82D9B* + ID_OUI_FROM_DATABASE=Shenzhen G.Credit Communication Technology Co., Ltd + +OUI:94BF1E* + ID_OUI_FROM_DATABASE=eflow Inc. / Smart Device Planning and Development Division + +OUI:C0A39E* + ID_OUI_FROM_DATABASE=EarthCam, Inc. + +OUI:088E4F* + ID_OUI_FROM_DATABASE=SF Software Solutions + +OUI:E8EADA* + ID_OUI_FROM_DATABASE=Denkovi Assembly Electroncs LTD + +OUI:DCAE04* + ID_OUI_FROM_DATABASE=CELOXICA Ltd + +OUI:5422F8* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:486E73* + ID_OUI_FROM_DATABASE=Pica8, Inc. + +OUI:A0CEC8* + ID_OUI_FROM_DATABASE=CE LINK LIMITED + +OUI:907A28* + ID_OUI_FROM_DATABASE=Beijing Morncloud Information And Technology Co. Ltd. + +OUI:CCD29B* + ID_OUI_FROM_DATABASE=Shenzhen Bopengfa Elec&Technology CO.,Ltd + +OUI:9C4EBF* + ID_OUI_FROM_DATABASE=BoxCast + +OUI:34A68C* + ID_OUI_FROM_DATABASE=Shine Profit Development Limited + +OUI:78DAB3* + ID_OUI_FROM_DATABASE=GBO Technology + +OUI:80BBEB* + ID_OUI_FROM_DATABASE=Satmap Systems Ltd + +OUI:949FB4* + ID_OUI_FROM_DATABASE=ChengDu JiaFaAnTai Technology Co.,Ltd + +OUI:406826* + ID_OUI_FROM_DATABASE=Thales UK Limited + +OUI:5C15E1* + ID_OUI_FROM_DATABASE=AIDC TECHNOLOGY (S) PTE LTD + +OUI:048D38* + ID_OUI_FROM_DATABASE=Netcore Technology Inc. + +OUI:1C4AF7* + ID_OUI_FROM_DATABASE=AMON INC + +OUI:985D46* + ID_OUI_FROM_DATABASE=PeopleNet Communication + +OUI:446755* + ID_OUI_FROM_DATABASE=Orbit Irrigation + +OUI:789F4C* + ID_OUI_FROM_DATABASE=HOERBIGER Elektronik GmbH + +OUI:98F8C1* + ID_OUI_FROM_DATABASE=IDT Technology Limited + +OUI:F47A4E* + ID_OUI_FROM_DATABASE=Woojeon&Handan + +OUI:44700B* + ID_OUI_FROM_DATABASE=IFFU + +OUI:8C2F39* + ID_OUI_FROM_DATABASE=IBA Dosimetry GmbH + +OUI:B8F828* + ID_OUI_FROM_DATABASE=Changshu Gaoshida Optoelectronic Technology Co. Ltd. + +OUI:58468F* + ID_OUI_FROM_DATABASE=Koncar Electronics and Informatics + +OUI:746630* + ID_OUI_FROM_DATABASE=T:mi Ytti + +OUI:B0FEBD* + ID_OUI_FROM_DATABASE=Private + +OUI:940BD5* + ID_OUI_FROM_DATABASE=Himax Technologies, Inc + +OUI:30055C* + ID_OUI_FROM_DATABASE=Brother industries, LTD. + +OUI:0C8268* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:B01743* + ID_OUI_FROM_DATABASE=EDISON GLOBAL CIRCUITS LLC + +OUI:90DA4E* + ID_OUI_FROM_DATABASE=AVANU + +OUI:7038B4* + ID_OUI_FROM_DATABASE=Low Tech Solutions + +OUI:AC1826* + ID_OUI_FROM_DATABASE=SEIKO EPSON CORPORATION + +OUI:4C804F* + ID_OUI_FROM_DATABASE=Armstrong Monitoring Corp + +OUI:901D27* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:7CD762* + ID_OUI_FROM_DATABASE=Freestyle Technology Pty Ltd + +OUI:D073D5* + ID_OUI_FROM_DATABASE=LIFI LABS MANAGEMENT PTY LTD + +OUI:B8C46F* + ID_OUI_FROM_DATABASE=PRIMMCON INDUSTRIES INC + +OUI:505AC6* + ID_OUI_FROM_DATABASE=GUANGDONG SUPER TELECOM CO.,LTD. + +OUI:38A86B* + ID_OUI_FROM_DATABASE=Orga BV + +OUI:141330* + ID_OUI_FROM_DATABASE=Anakreon UK LLP + +OUI:0CF405* + ID_OUI_FROM_DATABASE=Beijing Signalway Technologies Co.,Ltd + +OUI:1C76CA* + ID_OUI_FROM_DATABASE=Terasic Technologies Inc. + +OUI:0C1105* + ID_OUI_FROM_DATABASE=Ringslink (Xiamen) Network Communication Technologies Co., Ltd + +OUI:945047* + ID_OUI_FROM_DATABASE=Rechnerbetriebsgruppe + +OUI:D8DCE9* + ID_OUI_FROM_DATABASE=Kunshan Erlab ductless filtration system Co.,Ltd + +OUI:54112F* + ID_OUI_FROM_DATABASE=Sulzer Pump Solutions Finland Oy + +OUI:E0DCA0* + ID_OUI_FROM_DATABASE=Siemens Electrical Apparatus Ltd., Suzhou Chengdu Branch + +OUI:4C55B8* + ID_OUI_FROM_DATABASE=Turkcell Teknoloji + +OUI:088039* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:2C72C3* + ID_OUI_FROM_DATABASE=Soundmatters + +OUI:84E4D9* + ID_OUI_FROM_DATABASE=Shenzhen NEED technology Ltd. + +OUI:C44838* + ID_OUI_FROM_DATABASE=Satcom Direct, Inc. + +OUI:545414* + ID_OUI_FROM_DATABASE=Digital RF Corea, Inc + +OUI:24EB65* + ID_OUI_FROM_DATABASE=SAET I.S. S.r.l. + +OUI:D0F27F* + ID_OUI_FROM_DATABASE=SteadyServ Technoligies, LLC + +OUI:DC647C* + ID_OUI_FROM_DATABASE=C.R.S. iiMotion GmbH + +OUI:188410* + ID_OUI_FROM_DATABASE=CoreTrust Inc. + +OUI:A08A87* + ID_OUI_FROM_DATABASE=HuiZhou KaiYue Electronic Co.,Ltd + +OUI:04BFA8* + ID_OUI_FROM_DATABASE=ISB Corporation + +OUI:5C8486* + ID_OUI_FROM_DATABASE=Brightsource Industries Israel LTD + +OUI:28CD9C* + ID_OUI_FROM_DATABASE=Shenzhen Dynamax Software Development Co.,Ltd. + +OUI:E0EDC7* + ID_OUI_FROM_DATABASE=Shenzhen Friendcom Technology Development Co., Ltd + +OUI:2CF203* + ID_OUI_FROM_DATABASE=EMKO ELEKTRONIK SAN VE TIC AS + +OUI:246278* + ID_OUI_FROM_DATABASE=sysmocom - systems for mobile communications GmbH + +OUI:F45842* + ID_OUI_FROM_DATABASE=Boxx TV Ltd + +OUI:A861AA* + ID_OUI_FROM_DATABASE=Cloudview Limited + +OUI:C89346* + ID_OUI_FROM_DATABASE=MXCHIP Company Limited + +OUI:F0F260* + ID_OUI_FROM_DATABASE=Mobitec AB + +OUI:1423D7* + ID_OUI_FROM_DATABASE=EUTRONIX CO., LTD. + +OUI:3CFB96* + ID_OUI_FROM_DATABASE=Emcraft Systems LLC + +OUI:081F3F* + ID_OUI_FROM_DATABASE=WondaLink Inc. + +OUI:DC6F08* + ID_OUI_FROM_DATABASE=Bay Storage Technology + +OUI:E492E7* + ID_OUI_FROM_DATABASE=Gridlink Tech. Co.,Ltd. + +OUI:60BB0C* + ID_OUI_FROM_DATABASE=Beijing HuaqinWorld Technology Co,Ltd + +OUI:70E027* + ID_OUI_FROM_DATABASE=HONGYU COMMUNICATION TECHNOLOGY LIMITED + +OUI:E880D8* + ID_OUI_FROM_DATABASE=GNTEK Electronics Co.,Ltd. + +OUI:188857* + ID_OUI_FROM_DATABASE=Beijing Jinhong Xi-Dian Information Technology Corp. + +OUI:287994* + ID_OUI_FROM_DATABASE=Realplay Digital Technology(Shenzhen) Co.,Ltd + +OUI:105C3B* + ID_OUI_FROM_DATABASE=Perma-Pipe, Inc. + +OUI:40C4D6* + ID_OUI_FROM_DATABASE=ChongQing Camyu Technology Development Co.,Ltd. + +OUI:A0EB76* + ID_OUI_FROM_DATABASE=AirCUVE Inc. + +OUI:6C6126* + ID_OUI_FROM_DATABASE=Rinicom Holdings + +OUI:C04DF7* + ID_OUI_FROM_DATABASE=SERELEC + +OUI:ECD040* + ID_OUI_FROM_DATABASE=GEA Farm Technologies GmbH + +OUI:005907* + ID_OUI_FROM_DATABASE=LenovoEMC Products USA, LLC + +OUI:78B3CE* + ID_OUI_FROM_DATABASE=Elo touch solutions + +OUI:A8FB70* + ID_OUI_FROM_DATABASE=WiseSec L.t.d + +OUI:30F31D* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:E4776B* + ID_OUI_FROM_DATABASE=AARTESYS AG + +OUI:5C335C* + ID_OUI_FROM_DATABASE=Swissphone Telecom AG + +OUI:A4FCCE* + ID_OUI_FROM_DATABASE=Security Expert Ltd. + +OUI:E0CEC3* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:5C43D2* + ID_OUI_FROM_DATABASE=HAZEMEYER + +OUI:D819CE* + ID_OUI_FROM_DATABASE=Telesquare + +OUI:D809C3* + ID_OUI_FROM_DATABASE=Cercacor Labs + +OUI:84ED33* + ID_OUI_FROM_DATABASE=BBMC Co.,Ltd + +OUI:681E8B* + ID_OUI_FROM_DATABASE=InfoSight Corporation + +OUI:C044E3* + ID_OUI_FROM_DATABASE=Shenzhen Sinkna Electronics Co., LTD + +OUI:08F1B7* + ID_OUI_FROM_DATABASE=Towerstream Corpration + +OUI:20858C* + ID_OUI_FROM_DATABASE=Assa + +OUI:187A93* + ID_OUI_FROM_DATABASE=AMICCOM Electronics Corporation + +OUI:94C962* + ID_OUI_FROM_DATABASE=Teseq AG + +OUI:384369* + ID_OUI_FROM_DATABASE=Patrol Products Consortium LLC + +OUI:D08B7E* + ID_OUI_FROM_DATABASE=Passif Semiconductor + +OUI:6886E7* + ID_OUI_FROM_DATABASE=Orbotix, Inc. + +OUI:2CE871* + ID_OUI_FROM_DATABASE=Alert Metalguard ApS + +OUI:58D071* + ID_OUI_FROM_DATABASE=BW Broadcast + +OUI:C0A0C7* + ID_OUI_FROM_DATABASE=FAIRFIELD INDUSTRIES + +OUI:98208E* + ID_OUI_FROM_DATABASE=Definium Technologies + +OUI:704AE4* + ID_OUI_FROM_DATABASE=Rinstrum Pty Ltd + +OUI:68B8D9* + ID_OUI_FROM_DATABASE=Act KDE, Inc. + +OUI:F84897* + ID_OUI_FROM_DATABASE=Hitachi, Ltd. + +OUI:74E424* + ID_OUI_FROM_DATABASE=APISTE CORPORATION + +OUI:58D6D3* + ID_OUI_FROM_DATABASE=Dairy Cheq Inc + +OUI:68FB95* + ID_OUI_FROM_DATABASE=Generalplus Technology Inc. + +OUI:E4C146* + ID_OUI_FROM_DATABASE=Objetivos y Servicios de Valor A + +OUI:D4BF2D* + ID_OUI_FROM_DATABASE=SE Controls Asia Pacific Ltd + +OUI:C45DD8* + ID_OUI_FROM_DATABASE=HDMI Forum + +OUI:C44EAC* + ID_OUI_FROM_DATABASE=Shenzhen Shiningworth Technology Co., Ltd. + +OUI:C458C2* + ID_OUI_FROM_DATABASE=Shenzhen TATFOOK Technology Co., Ltd. + +OUI:44184F* + ID_OUI_FROM_DATABASE=Fitview + +OUI:8C76C1* + ID_OUI_FROM_DATABASE=Goden Tech Limited + +OUI:DC2A14* + ID_OUI_FROM_DATABASE=Shanghai Longjing Technology Co. + +OUI:0C191F* + ID_OUI_FROM_DATABASE=Inform Electronik + +OUI:080FFA* + ID_OUI_FROM_DATABASE=KSP INC. + +OUI:ECFC55* + ID_OUI_FROM_DATABASE=A. Eberle GmbH & Co. KG + +OUI:0C8CDC* + ID_OUI_FROM_DATABASE=Suunto Oy + +OUI:20B5C6* + ID_OUI_FROM_DATABASE=Mimosa Networks + +OUI:AC3CB4* + ID_OUI_FROM_DATABASE=Nilan A/S + +OUI:A830AD* + ID_OUI_FROM_DATABASE=Wei Fang Goertek Electronics Co.,Ltd + +OUI:8007A2* + ID_OUI_FROM_DATABASE=Esson Technology Inc. + +OUI:2C3557* + ID_OUI_FROM_DATABASE=ELLIY Power CO..Ltd + +OUI:6C5A34* + ID_OUI_FROM_DATABASE=Shenzhen Haitianxiong Electronic Co., Ltd. + +OUI:485A3F* + ID_OUI_FROM_DATABASE=WISOL + +OUI:70F1E5* + ID_OUI_FROM_DATABASE=Xetawave LLC + +OUI:C0AA68* + ID_OUI_FROM_DATABASE=OSASI Technos Inc. + +OUI:B829F7* + ID_OUI_FROM_DATABASE=Blaster Tech + +OUI:00C14F* + ID_OUI_FROM_DATABASE=DDL Co,.ltd. + +OUI:5CE0CA* + ID_OUI_FROM_DATABASE=FeiTian United (Beijing) System Technology Co., Ltd. + +OUI:9C9811* + ID_OUI_FROM_DATABASE=Guangzhou Sunrise Electronics Development Co., Ltd + +OUI:A0FE91* + ID_OUI_FROM_DATABASE=AVAT Automation GmbH + +OUI:5809E5* + ID_OUI_FROM_DATABASE=Kivic Inc. + +OUI:74ECF1* + ID_OUI_FROM_DATABASE=Acumen + +OUI:6815D3* + ID_OUI_FROM_DATABASE=Zaklady Elektroniki i Mechaniki Precyzyjnej R&G S.A. + +OUI:601929* + ID_OUI_FROM_DATABASE=VOLTRONIC POWER TECHNOLOGY(SHENZHEN) CORP. + +OUI:C0BD42* + ID_OUI_FROM_DATABASE=ZPA Smart Energy a.s. + +OUI:48B253* + ID_OUI_FROM_DATABASE=Marketaxess Corporation + +OUI:60D2B9* + ID_OUI_FROM_DATABASE=REALAND BIO CO., LTD. + +OUI:2067B1* + ID_OUI_FROM_DATABASE=Pluto inc. + +OUI:087D21* + ID_OUI_FROM_DATABASE=Altasec technology corporation + +OUI:30FD11* + ID_OUI_FROM_DATABASE=MACROTECH (USA) INC. + +OUI:F8051C* + ID_OUI_FROM_DATABASE=DRS Imaging and Targeting Solutions + +OUI:6032F0* + ID_OUI_FROM_DATABASE=Mplus technology + +OUI:749975* + ID_OUI_FROM_DATABASE=IBM Corporation + +OUI:0CDCCC* + ID_OUI_FROM_DATABASE=Inala Technologies + +OUI:F0ACA4* + ID_OUI_FROM_DATABASE=HBC-radiomatic + +OUI:14DB85* + ID_OUI_FROM_DATABASE=S NET MEDIA + +OUI:D493A0* + ID_OUI_FROM_DATABASE=Fidelix Oy + +OUI:AC7236* + ID_OUI_FROM_DATABASE=Lexking Technology Co., Ltd. + +OUI:CCB3F8* + ID_OUI_FROM_DATABASE=FUJITSU ISOTEC LIMITED + +OUI:3CD7DA* + ID_OUI_FROM_DATABASE=SK Mtek microelectronics(shenzhen)limited + +OUI:E86D54* + ID_OUI_FROM_DATABASE=Digit Mobile Inc + +OUI:9857D3* + ID_OUI_FROM_DATABASE=HON HAI-CCPBG PRECISION IND.CO.,LTD. + +OUI:9C8D1A* + ID_OUI_FROM_DATABASE=INTEG process group inc + +OUI:742D0A* + ID_OUI_FROM_DATABASE=Norfolk Elektronik AG + +OUI:480362* + ID_OUI_FROM_DATABASE=DESAY ELECTRONICS(HUIZHOU)CO.,LTD + +OUI:B0358D* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:0CF361* + ID_OUI_FROM_DATABASE=Java Information + +OUI:34BDFA* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:8CEEC6* + ID_OUI_FROM_DATABASE=Precepscion Pty. Ltd. + +OUI:ECD950* + ID_OUI_FROM_DATABASE=IRT SA + +OUI:74273C* + ID_OUI_FROM_DATABASE=ChangYang Technology (Nanjing) Co., LTD + +OUI:087CBE* + ID_OUI_FROM_DATABASE=Quintic Corp. + +OUI:C4AD21* + ID_OUI_FROM_DATABASE=MEDIAEDGE Corporation + +OUI:DCBF90* + ID_OUI_FROM_DATABASE=HUIZHOU QIAOXING TELECOMMUNICATION INDUSTRY CO.,LTD. + +OUI:E0F5CA* + ID_OUI_FROM_DATABASE=CHENG UEI PRECISION INDUSTRY CO.,LTD. + +OUI:1C5C60* + ID_OUI_FROM_DATABASE=Shenzhen Belzon Technology Co.,LTD. + +OUI:2CEDEB* + ID_OUI_FROM_DATABASE=Alpheus Digital Company Limited + +OUI:381C4A* + ID_OUI_FROM_DATABASE=SIMCom Wireless Solutions Co.,Ltd. + +OUI:C8DE51* + ID_OUI_FROM_DATABASE=Integra Networks, Inc. + +OUI:901EDD* + ID_OUI_FROM_DATABASE=GREAT COMPUTER CORPORATION + +OUI:2C6289* + ID_OUI_FROM_DATABASE=Regenersis (Glenrothes) Ltd + +OUI:F093C5* + ID_OUI_FROM_DATABASE=Garland Technology + +OUI:4C09B4* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B8B94E* + ID_OUI_FROM_DATABASE=Shenzhen iBaby Labs, Inc. + +OUI:00F403* + ID_OUI_FROM_DATABASE=Orbis Systems Oy + +OUI:ACC698* + ID_OUI_FROM_DATABASE=Kohzu Precision Co., Ltd. + +OUI:907025* + ID_OUI_FROM_DATABASE=Garea Microsys Co.,Ltd. + +OUI:502ECE* + ID_OUI_FROM_DATABASE=Asahi Electronics Co.,Ltd + +OUI:440CFD* + ID_OUI_FROM_DATABASE=NetMan Co., Ltd. + +OUI:7CEBEA* + ID_OUI_FROM_DATABASE=ASCT + +OUI:085B0E* + ID_OUI_FROM_DATABASE=Fortinet, Inc. + +OUI:4C0FC7* + ID_OUI_FROM_DATABASE=Earda Electronics Co.,Ltd + +OUI:64C944* + ID_OUI_FROM_DATABASE=LARK Technologies, Inc + +OUI:6869F2* + ID_OUI_FROM_DATABASE=ComAp s.r.o. + +OUI:B889CA* + ID_OUI_FROM_DATABASE=ILJIN ELECTRIC Co., Ltd. + +OUI:B85AFE* + ID_OUI_FROM_DATABASE=Handaer Communication Technology (Beijing) Co., Ltd + +OUI:604616* + ID_OUI_FROM_DATABASE=XIAMEN VANN INTELLIGENT CO., LTD + +OUI:ECD925* + ID_OUI_FROM_DATABASE=RAMI + +OUI:049F06* + ID_OUI_FROM_DATABASE=Smobile Co., Ltd. + +OUI:D806D1* + ID_OUI_FROM_DATABASE=Honeywell Fire System (Shanghai) Co,. Ltd. + +OUI:8C6AE4* + ID_OUI_FROM_DATABASE=Viogem Limited + +OUI:20C1AF* + ID_OUI_FROM_DATABASE=i Wit Digital Co., Limited + +OUI:D88A3B* + ID_OUI_FROM_DATABASE=UNIT-EM + +OUI:BCD940* + ID_OUI_FROM_DATABASE=ASR Co,.Ltd. + +OUI:EC4993* + ID_OUI_FROM_DATABASE=Qihan Technology Co., Ltd + +OUI:B0ACFA* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:1C959F* + ID_OUI_FROM_DATABASE=Veethree Electronics And Marine LLC + +OUI:18D949* + ID_OUI_FROM_DATABASE=Qvis Labs, LLC + +OUI:646223* + ID_OUI_FROM_DATABASE=Cellient Co., Ltd. + +OUI:ACF0B2* + ID_OUI_FROM_DATABASE=Becker Electronics Taiwan Ltd. + +OUI:10A932* + ID_OUI_FROM_DATABASE=Beijing Cyber Cloud Technology Co. ,Ltd. + +OUI:C47BA3* + ID_OUI_FROM_DATABASE=NAVIS Inc. + +OUI:A81758* + ID_OUI_FROM_DATABASE=Elektronik System i Umeå AB + +OUI:44348F* + ID_OUI_FROM_DATABASE=MXT INDUSTRIAL LTDA + +OUI:9C0111* + ID_OUI_FROM_DATABASE=Shenzhen Newabel Electronic Co., Ltd. + +OUI:0CA138* + ID_OUI_FROM_DATABASE=Blinq Wireless Inc. + +OUI:348137* + ID_OUI_FROM_DATABASE=UNICARD SA + +OUI:64F242* + ID_OUI_FROM_DATABASE=Gerdes Aktiengesellschaft + +OUI:60F281* + ID_OUI_FROM_DATABASE=TRANWO TECHNOLOGY CO., LTD. + +OUI:B0E892* + ID_OUI_FROM_DATABASE=SEIKO EPSON CORPORATION + +OUI:642400* + ID_OUI_FROM_DATABASE=Xorcom Ltd. + +OUI:4CAA16* + ID_OUI_FROM_DATABASE=AzureWave Technologies (Shanghai) Inc. + +OUI:1C6BCA* + ID_OUI_FROM_DATABASE=Mitsunami Co., Ltd. + +OUI:08379C* + ID_OUI_FROM_DATABASE=Topaz Co. LTD. + +OUI:E83EFB* + ID_OUI_FROM_DATABASE=GEODESIC LTD. + +OUI:4016FA* + ID_OUI_FROM_DATABASE=EKM Metering + +OUI:3C363D* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BC0200* + ID_OUI_FROM_DATABASE=Stewart Audio + +OUI:1C973D* + ID_OUI_FROM_DATABASE=PRICOM Design + +OUI:F00786* + ID_OUI_FROM_DATABASE=Shandong Bittel Electronics Co., Ltd + +OUI:885C47* + ID_OUI_FROM_DATABASE=Alcatel Lucent + +OUI:E0F9BE* + ID_OUI_FROM_DATABASE=Cloudena Corp. + +OUI:3CC1F6* + ID_OUI_FROM_DATABASE=Melange Systems Pvt. Ltd. + +OUI:54E63F* + ID_OUI_FROM_DATABASE=ShenZhen LingKeWeiEr Technology Co., Ltd. + +OUI:F88C1C* + ID_OUI_FROM_DATABASE=KAISHUN ELECTRONIC TECHNOLOGY CO., LTD. BEIJING + +OUI:940149* + ID_OUI_FROM_DATABASE=AutoHotBox + +OUI:C035BD* + ID_OUI_FROM_DATABASE=Velocytech Aps + +OUI:F897CF* + ID_OUI_FROM_DATABASE=DAESHIN-INFORMATION TECHNOLOGY CO., LTD. + +OUI:383F10* + ID_OUI_FROM_DATABASE=DBL Technology Ltd. + +OUI:8C6878* + ID_OUI_FROM_DATABASE=Nortek-AS + +OUI:8016B7* + ID_OUI_FROM_DATABASE=Brunel University + +OUI:9C611D* + ID_OUI_FROM_DATABASE=Omni-ID USA, Inc. + +OUI:78BEBD* + ID_OUI_FROM_DATABASE=STULZ GmbH + +OUI:3C9174* + ID_OUI_FROM_DATABASE=ALONG COMMUNICATION TECHNOLOGY + +OUI:B8E937* + ID_OUI_FROM_DATABASE=Sonos, Inc. + +OUI:E8D0FA* + ID_OUI_FROM_DATABASE=MKS Instruments Deutschland GmbH + +OUI:98262A* + ID_OUI_FROM_DATABASE=Applied Research Associates, Inc + +OUI:B0D2F5* + ID_OUI_FROM_DATABASE=Vello Systems, Inc. + +OUI:C89F42* + ID_OUI_FROM_DATABASE=VDII Innovation AB + +OUI:A41875* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:640E94* + ID_OUI_FROM_DATABASE=Pluribus Networks, Inc. + +OUI:6CE983* + ID_OUI_FROM_DATABASE=Gastron Co., LTD. + +OUI:0CB4EF* + ID_OUI_FROM_DATABASE=Digience Co.,Ltd. + +OUI:D0DB32* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:609084* + ID_OUI_FROM_DATABASE=DSSD Inc + +OUI:A4E731* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:0808EA* + ID_OUI_FROM_DATABASE=AMSC + +OUI:C05E79* + ID_OUI_FROM_DATABASE=SHENZHEN HUAXUN ARK TECHNOLOGIES CO.,LTD + +OUI:A4934C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:E85484* + ID_OUI_FROM_DATABASE=NEO Information Systems Co., Ltd. + +OUI:74AE76* + ID_OUI_FROM_DATABASE=iNovo Broadband, Inc. + +OUI:60B933* + ID_OUI_FROM_DATABASE=Deutron Electronics Corp. + +OUI:38EE9D* + ID_OUI_FROM_DATABASE=Anedo Ltd. + +OUI:80CEB1* + ID_OUI_FROM_DATABASE=Theissen Training Systems GmbH + +OUI:3C3888* + ID_OUI_FROM_DATABASE=ConnectQuest, llc + +OUI:08BE09* + ID_OUI_FROM_DATABASE=Astrol Electronic AG + +OUI:D8B8F6* + ID_OUI_FROM_DATABASE=Nantworks + +OUI:6044F5* + ID_OUI_FROM_DATABASE=Easy Digital Ltd. + +OUI:AC51EE* + ID_OUI_FROM_DATABASE=Cambridge Communication Systems Ltd + +OUI:10E4AF* + ID_OUI_FROM_DATABASE=APR, LLC + +OUI:B0BD6D* + ID_OUI_FROM_DATABASE=Echostreams Innovative Solutions + +OUI:F0D14F* + ID_OUI_FROM_DATABASE=LINEAR LLC + +OUI:AC3D75* + ID_OUI_FROM_DATABASE=HANGZHOU ZHIWAY TECHNOLOGIES CO.,LTD. + +OUI:141A51* + ID_OUI_FROM_DATABASE=Treetech Sistemas Digitais + +OUI:845787* + ID_OUI_FROM_DATABASE=DVR C&C Co., Ltd. + +OUI:F436E1* + ID_OUI_FROM_DATABASE=Abilis Systems SARL + +OUI:587FC8* + ID_OUI_FROM_DATABASE=S2M + +OUI:C49805* + ID_OUI_FROM_DATABASE=Minieum Networks, Inc + +OUI:90F4C1* + ID_OUI_FROM_DATABASE=Rand McNally + +OUI:18193F* + ID_OUI_FROM_DATABASE=Tamtron Oy + +OUI:F8F7FF* + ID_OUI_FROM_DATABASE=SYN-TECH SYSTEMS INC + +OUI:F473CA* + ID_OUI_FROM_DATABASE=Conversion Sound Inc. + +OUI:00E8AB* + ID_OUI_FROM_DATABASE=Meggitt Training Systems, Inc. + +OUI:18421D* + ID_OUI_FROM_DATABASE=Private + +OUI:78617C* + ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO.,LTD + +OUI:C401B1* + ID_OUI_FROM_DATABASE=SeekTech INC + +OUI:1C5FFF* + ID_OUI_FROM_DATABASE=Beijing Ereneben Information Technology Co.,Ltd Shenzhen Branch + +OUI:C0C946* + ID_OUI_FROM_DATABASE=MITSUYA LABORATORIES INC. + +OUI:ACC2EC* + ID_OUI_FROM_DATABASE=CLT INT'L IND. CORP. + +OUI:702F4B* + ID_OUI_FROM_DATABASE=PolyVision Inc. + +OUI:741489* + ID_OUI_FROM_DATABASE=SRT Wireless + +OUI:94CA0F* + ID_OUI_FROM_DATABASE=Honeywell Analytics + +OUI:848D84* + ID_OUI_FROM_DATABASE=Rajant Corporation + +OUI:D8337F* + ID_OUI_FROM_DATABASE=Office FA.com Co.,Ltd. + +OUI:7CEF8A* + ID_OUI_FROM_DATABASE=Inhon International Ltd. + +OUI:84AF1F* + ID_OUI_FROM_DATABASE=Beat System Service Co,. Ltd. + +OUI:100D2F* + ID_OUI_FROM_DATABASE=Online Security Pty. Ltd. + +OUI:408B07* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:980284* + ID_OUI_FROM_DATABASE=Theobroma Systems GmbH + +OUI:E03C5B* + ID_OUI_FROM_DATABASE=SHENZHEN JIAXINJIE ELECTRON CO.,LTD + +OUI:645563* + ID_OUI_FROM_DATABASE=Intelight Inc. + +OUI:C467B5* + ID_OUI_FROM_DATABASE=Libratone A/S + +OUI:A4EF52* + ID_OUI_FROM_DATABASE=Telewave Co., Ltd. + +OUI:F4044C* + ID_OUI_FROM_DATABASE=ValenceTech Limited + +OUI:1CBBA8* + ID_OUI_FROM_DATABASE=OJSC Ufimskiy Zavod Promsvyaz + +OUI:506028* + ID_OUI_FROM_DATABASE=Xirrus Inc. + +OUI:24B657* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:940B2D* + ID_OUI_FROM_DATABASE=NetView Technologies(Shenzhen) Co., Ltd + +OUI:306E5C* + ID_OUI_FROM_DATABASE=Validus Technologies + +OUI:E843B6* + ID_OUI_FROM_DATABASE=QNAP Systems, Inc. + +OUI:5CC9D3* + ID_OUI_FROM_DATABASE=PALLADIUM ENERGY ELETRONICA DA AMAZONIA LTDA + +OUI:407B1B* + ID_OUI_FROM_DATABASE=Mettle Networks Inc. + +OUI:64E161* + ID_OUI_FROM_DATABASE=DEP Corp. + +OUI:C8A620* + ID_OUI_FROM_DATABASE=Nebula, Inc + +OUI:989080* + ID_OUI_FROM_DATABASE=Linkpower Network System Inc Ltd. + +OUI:0064A6* + ID_OUI_FROM_DATABASE=Maquet CardioVascular + +OUI:3C4E47* + ID_OUI_FROM_DATABASE=Etronic A/S + +OUI:C8F9F9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:F0F755* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:B01C91* + ID_OUI_FROM_DATABASE=Elim Co + +OUI:0CA2F4* + ID_OUI_FROM_DATABASE=Chameleon Technology (UK) Limited + +OUI:846AED* + ID_OUI_FROM_DATABASE=Wireless Tsukamoto.,co.LTD + +OUI:D8E952* + ID_OUI_FROM_DATABASE=KEOPSYS + +OUI:3CB9A6* + ID_OUI_FROM_DATABASE=Belden Deutschland GmbH + +OUI:3440B5* + ID_OUI_FROM_DATABASE=IBM + +OUI:90D74F* + ID_OUI_FROM_DATABASE=Bookeen + +OUI:905682* + ID_OUI_FROM_DATABASE=Lenbrook Industries Limited + +OUI:CC6DEF* + ID_OUI_FROM_DATABASE=TJK Tietolaite Oy + +OUI:3CE624* + ID_OUI_FROM_DATABASE=LG Display + +OUI:D8F0F2* + ID_OUI_FROM_DATABASE=Zeebo Inc + +OUI:B0CF4D* + ID_OUI_FROM_DATABASE=MI-Zone Technology Ireland + +OUI:143605* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:B87424* + ID_OUI_FROM_DATABASE=Viessmann Elektronik GmbH + +OUI:C81AFE* + ID_OUI_FROM_DATABASE=DLOGIC GmbH + +OUI:9C53CD* + ID_OUI_FROM_DATABASE=ENGICAM s.r.l. + +OUI:DCC101* + ID_OUI_FROM_DATABASE=SOLiD Technologies, Inc. + +OUI:AC6FBB* + ID_OUI_FROM_DATABASE=TATUNG Technology Inc. + +OUI:1803FA* + ID_OUI_FROM_DATABASE=IBT Interfaces + +OUI:608645* + ID_OUI_FROM_DATABASE=Avery Weigh-Tronix, LLC + +OUI:541DFB* + ID_OUI_FROM_DATABASE=Freestyle Energy Ltd + +OUI:9CF67D* + ID_OUI_FROM_DATABASE=Ricardo Prague, s.r.o. + +OUI:A0E201* + ID_OUI_FROM_DATABASE=AVTrace Ltd.(China) + +OUI:04EE91* + ID_OUI_FROM_DATABASE=x-fabric GmbH + +OUI:183825* + ID_OUI_FROM_DATABASE=Wuhan Lingjiu High-tech Co.,Ltd. + +OUI:5404A6* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:F83376* + ID_OUI_FROM_DATABASE=Good Mind Innovation Co., Ltd. + +OUI:C46044* + ID_OUI_FROM_DATABASE=Everex Electronics Limited + +OUI:645422* + ID_OUI_FROM_DATABASE=Equinox Payments + +OUI:D412BB* + ID_OUI_FROM_DATABASE=Quadrant Components Inc. Ltd + +OUI:40E793* + ID_OUI_FROM_DATABASE=Shenzhen Siviton Technology Co.,Ltd + +OUI:2C67FB* + ID_OUI_FROM_DATABASE=ShenZhen Zhengjili Electronics Co., LTD + +OUI:D89760* + ID_OUI_FROM_DATABASE=C2 Development, Inc. + +OUI:1CB17F* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:942E17* + ID_OUI_FROM_DATABASE=Schneider Electric Canada Inc + +OUI:B89674* + ID_OUI_FROM_DATABASE=AllDSP GmbH & Co. KG + +OUI:6CA682* + ID_OUI_FROM_DATABASE=EDAM information & communications + +OUI:48A22D* + ID_OUI_FROM_DATABASE=Shenzhen Huaxuchang Telecom Technology Co.,Ltd + +OUI:50ED94* + ID_OUI_FROM_DATABASE=EGATEL SL + +OUI:B87447* + ID_OUI_FROM_DATABASE=Convergence Technologies + +OUI:70A66A* + ID_OUI_FROM_DATABASE=Prox Dynamics AS + +OUI:DC175A* + ID_OUI_FROM_DATABASE=Hitachi High-Technologies Corporation + +OUI:5C076F* + ID_OUI_FROM_DATABASE=Thought Creator + +OUI:3C0FC1* + ID_OUI_FROM_DATABASE=KBC Networks + +OUI:58E636* + ID_OUI_FROM_DATABASE=EVRsafe Technologies + +OUI:90D11B* + ID_OUI_FROM_DATABASE=Palomar Medical Technologies + +OUI:CC60BB* + ID_OUI_FROM_DATABASE=Empower RF Systems + +OUI:24497B* + ID_OUI_FROM_DATABASE=Innovative Converged Devices Inc + +OUI:ECBD09* + ID_OUI_FROM_DATABASE=FUSION Electronics Ltd + +OUI:54847B* + ID_OUI_FROM_DATABASE=Digital Devices GmbH + +OUI:705CAD* + ID_OUI_FROM_DATABASE=Konami Gaming Inc + +OUI:788973* + ID_OUI_FROM_DATABASE=CMC + +OUI:DCCE41* + ID_OUI_FROM_DATABASE=FE GLOBAL HONG KONG LIMITED + +OUI:4C774F* + ID_OUI_FROM_DATABASE=Embedded Wireless Labs + +OUI:203706* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:7C4C58* + ID_OUI_FROM_DATABASE=Scale Computing, Inc. + +OUI:FCC23D* + ID_OUI_FROM_DATABASE=Atmel Corporation + +OUI:7C1E52* + ID_OUI_FROM_DATABASE=Microsoft + +OUI:DCB4C4* + ID_OUI_FROM_DATABASE=Microsoft XCG + +OUI:74FDA0* + ID_OUI_FROM_DATABASE=Compupal (Group) Corporation + +OUI:C029F3* + ID_OUI_FROM_DATABASE=XySystem + +OUI:48F317* + ID_OUI_FROM_DATABASE=Private + +OUI:B07D62* + ID_OUI_FROM_DATABASE=Dipl.-Ing. H. Horstmann GmbH + +OUI:68974B* + ID_OUI_FROM_DATABASE=Shenzhen Costar Electronics Co. Ltd. + +OUI:B8BB6D* + ID_OUI_FROM_DATABASE=ENERES Co.,Ltd. + +OUI:645DD7* + ID_OUI_FROM_DATABASE=Shenzhen Lifesense Medical Electronics Co., Ltd. + +OUI:D45AB2* + ID_OUI_FROM_DATABASE=Galleon Systems + +OUI:C40142* + ID_OUI_FROM_DATABASE=MaxMedia Technology Limited + +OUI:A06E50* + ID_OUI_FROM_DATABASE=Nanotek Elektronik Sistemler Ltd. Sti. + +OUI:182C91* + ID_OUI_FROM_DATABASE=Concept Development, Inc. + +OUI:EC4670* + ID_OUI_FROM_DATABASE=Meinberg Funkuhren GmbH & Co. KG + +OUI:B40B7A* + ID_OUI_FROM_DATABASE=Brusa Elektronik AG + +OUI:BC764E* + ID_OUI_FROM_DATABASE=Rackspace US, Inc. + +OUI:C4EEAE* + ID_OUI_FROM_DATABASE=VSS Monitoring + +OUI:2437EF* + ID_OUI_FROM_DATABASE=EMC Electronic Media Communication SA + +OUI:D4F63F* + ID_OUI_FROM_DATABASE=IEA S.R.L. + +OUI:4C0289* + ID_OUI_FROM_DATABASE=LEX COMPUTECH CO., LTD + +OUI:C0E54E* + ID_OUI_FROM_DATABASE=DENX Computer Systems GmbH + +OUI:E435FB* + ID_OUI_FROM_DATABASE=Sabre Technology (Hull) Ltd + +OUI:00CD90* + ID_OUI_FROM_DATABASE=MAS Elektronik AG + +OUI:A8BD1A* + ID_OUI_FROM_DATABASE=Honey Bee (Hong Kong) Limited + +OUI:ACCC8E* + ID_OUI_FROM_DATABASE=Axis Communications AB + +OUI:187C81* + ID_OUI_FROM_DATABASE=Valeo Vision Systems + +OUI:DC1EA3* + ID_OUI_FROM_DATABASE=Accensus LLC + +OUI:A40130* + ID_OUI_FROM_DATABASE=ABIsystems Co., LTD + +OUI:68F125* + ID_OUI_FROM_DATABASE=Data Controls Inc. + +OUI:706F81* + ID_OUI_FROM_DATABASE=Private + +OUI:30E4DB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:742B0F* + ID_OUI_FROM_DATABASE=Infinidat Ltd. + +OUI:280CB8* + ID_OUI_FROM_DATABASE=Mikrosay Yazilim ve Elektronik A.S. + +OUI:A06CEC* + ID_OUI_FROM_DATABASE=RIM + +OUI:443EB2* + ID_OUI_FROM_DATABASE=DEOTRON Co., LTD. + +OUI:8CB82C* + ID_OUI_FROM_DATABASE=IPitomy Communications + +OUI:807DE3* + ID_OUI_FROM_DATABASE=Chongqing Sichuan Instrument Microcircuit Co.LTD. + +OUI:1C8E8E* + ID_OUI_FROM_DATABASE=DB Communication & Systems Co., ltd. + +OUI:F0022B* + ID_OUI_FROM_DATABASE=Chrontel + +OUI:007F28* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:0C924E* + ID_OUI_FROM_DATABASE=Rice Lake Weighing Systems + +OUI:40040C* + ID_OUI_FROM_DATABASE=A&T + +OUI:A0165C* + ID_OUI_FROM_DATABASE=Triteka LTD + +OUI:90B97D* + ID_OUI_FROM_DATABASE=Johnson Outdoors Marine Electronics d/b/a Minnkota + +OUI:8821E3* + ID_OUI_FROM_DATABASE=Nebusens, S.L. + +OUI:B0F1BC* + ID_OUI_FROM_DATABASE=Dhemax Ingenieros Ltda + +OUI:3C096D* + ID_OUI_FROM_DATABASE=Powerhouse Dynamics + +OUI:CC501C* + ID_OUI_FROM_DATABASE=KVH Industries, Inc. + +OUI:AC6FD9* + ID_OUI_FROM_DATABASE=Valueplus Inc. + +OUI:A4E391* + ID_OUI_FROM_DATABASE=DENY FONTAINE + +OUI:04A82A* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:48D8FE* + ID_OUI_FROM_DATABASE=ClarIDy Solutions, Inc. + +OUI:70B265* + ID_OUI_FROM_DATABASE=Hiltron s.r.l. + +OUI:84D9C8* + ID_OUI_FROM_DATABASE=Unipattern Co., + +OUI:1C955D* + ID_OUI_FROM_DATABASE=I-LAX ELECTRONICS INC. + +OUI:94AAB8* + ID_OUI_FROM_DATABASE=Joview(Beijing) Technology Co. Ltd. + +OUI:18B3BA* + ID_OUI_FROM_DATABASE=Netlogic AB + +OUI:F43E9D* + ID_OUI_FROM_DATABASE=Benu Networks, Inc. + +OUI:6469BC* + ID_OUI_FROM_DATABASE=Hytera Communications Co .,ltd + +OUI:64094C* + ID_OUI_FROM_DATABASE=Beijing Superbee Wireless Technology Co.,Ltd + +OUI:F0AE51* + ID_OUI_FROM_DATABASE=Xi3 Corp + +OUI:782EEF* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:78510C* + ID_OUI_FROM_DATABASE=LiveU Ltd. + +OUI:306118* + ID_OUI_FROM_DATABASE=Paradom Inc. + +OUI:C84529* + ID_OUI_FROM_DATABASE=IMK Networks Co.,Ltd + +OUI:A88CEE* + ID_OUI_FROM_DATABASE=MicroMade Galka i Drozdz sp.j. + +OUI:204005* + ID_OUI_FROM_DATABASE=feno GmbH + +OUI:6C81FE* + ID_OUI_FROM_DATABASE=Mitsuba Corporation + +OUI:E8F928* + ID_OUI_FROM_DATABASE=RFTECH SRL + +OUI:703AD8* + ID_OUI_FROM_DATABASE=Shenzhen Afoundry Electronic Co., Ltd + +OUI:4C98EF* + ID_OUI_FROM_DATABASE=Zeo + +OUI:DCA6BD* + ID_OUI_FROM_DATABASE=Beijing Lanbo Technology Co., Ltd. + +OUI:58E808* + ID_OUI_FROM_DATABASE=AUTONICS CORPORATION + +OUI:B8C716* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:8058C5* + ID_OUI_FROM_DATABASE=NovaTec Kommunikationstechnik GmbH + +OUI:C0EAE4* + ID_OUI_FROM_DATABASE=Sonicwall + +OUI:F8A9DE* + ID_OUI_FROM_DATABASE=PUISSANCE PLUS + +OUI:D4F027* + ID_OUI_FROM_DATABASE=Navetas Energy Management + +OUI:5C0CBB* + ID_OUI_FROM_DATABASE=CELIZION Inc. + +OUI:B8871E* + ID_OUI_FROM_DATABASE=Good Mind Industries Co., Ltd. + +OUI:F8EA0A* + ID_OUI_FROM_DATABASE=Dipl.-Math. Michael Rauch + +OUI:BC5FF4* + ID_OUI_FROM_DATABASE=ASRock Incorporation + +OUI:A4B36A* + ID_OUI_FROM_DATABASE=JSC SDO Chromatec + +OUI:905F8D* + ID_OUI_FROM_DATABASE=modas GmbH + +OUI:E0C922* + ID_OUI_FROM_DATABASE=Jireh Energy Tech., Ltd. + +OUI:28401A* + ID_OUI_FROM_DATABASE=C8 MediSensors, Inc. + +OUI:DC3C84* + ID_OUI_FROM_DATABASE=Ticom Geomatics, Inc. + +OUI:E8CC32* + ID_OUI_FROM_DATABASE=Micronet LTD + +OUI:9C6ABE* + ID_OUI_FROM_DATABASE=QEES ApS. + +OUI:3429EA* + ID_OUI_FROM_DATABASE=MCD ELECTRONICS SP. Z O.O. + +OUI:D43AE9* + ID_OUI_FROM_DATABASE=DONGGUAN ipt INDUSTRIAL CO., LTD + +OUI:ACC935* + ID_OUI_FROM_DATABASE=Ness Corporation + +OUI:7C4A82* + ID_OUI_FROM_DATABASE=Portsmith LLC + +OUI:2C0033* + ID_OUI_FROM_DATABASE=EControls, LLC + +OUI:E0F211* + ID_OUI_FROM_DATABASE=Digitalwatt + +OUI:0432F4* + ID_OUI_FROM_DATABASE=Partron + +OUI:AC199F* + ID_OUI_FROM_DATABASE=SUNGROW POWER SUPPLY CO.,LTD. + +OUI:1CAA07* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:308CFB* + ID_OUI_FROM_DATABASE=Dropcam + +OUI:CCF841* + ID_OUI_FROM_DATABASE=Lumewave + +OUI:701404* + ID_OUI_FROM_DATABASE=Limited Liability Company + +OUI:1C35F1* + ID_OUI_FROM_DATABASE=NEW Lift Neue Elektronische Wege Steuerungsbau GmbH + +OUI:CCD9E9* + ID_OUI_FROM_DATABASE=SCR Engineers Ltd. + +OUI:F0DB30* + ID_OUI_FROM_DATABASE=Yottabyte + +OUI:9C31B6* + ID_OUI_FROM_DATABASE=Kulite Semiconductor Products Inc + +OUI:5C6A7D* + ID_OUI_FROM_DATABASE=KENTKART EGE ELEKTRONIK SAN. VE TIC. LTD. STI. + +OUI:04FF51* + ID_OUI_FROM_DATABASE=NOVAMEDIA INNOVISION SP. Z O.O. + +OUI:FCD4F2* + ID_OUI_FROM_DATABASE=The Coca Cola Company + +OUI:C471FE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:340804* + ID_OUI_FROM_DATABASE=D-Link Corporation + +OUI:B44CC2* + ID_OUI_FROM_DATABASE=NR ELECTRIC CO., LTD + +OUI:084EBF* + ID_OUI_FROM_DATABASE=Broad Net Mux Corporation + +OUI:48CB6E* + ID_OUI_FROM_DATABASE=Cello Electronics (UK) Ltd + +OUI:EC3BF0* + ID_OUI_FROM_DATABASE=NovelSat + +OUI:A86A6F* + ID_OUI_FROM_DATABASE=RIM + +OUI:4022ED* + ID_OUI_FROM_DATABASE=Digital Projection Ltd + +OUI:0817F4* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:C4D489* + ID_OUI_FROM_DATABASE=JiangSu Joyque Information Industry Co.,Ltd + +OUI:1C7C11* + ID_OUI_FROM_DATABASE=EID + +OUI:F43E61* + ID_OUI_FROM_DATABASE=Shenzhen Gongjin Electronics Co., Ltd + +OUI:B0B32B* + ID_OUI_FROM_DATABASE=Slican Sp. z o.o. + +OUI:5842E4* + ID_OUI_FROM_DATABASE=Baxter International Inc + +OUI:8CA048* + ID_OUI_FROM_DATABASE=Beijing NeTopChip Technology Co.,LTD + +OUI:804F58* + ID_OUI_FROM_DATABASE=ThinkEco, Inc. + +OUI:B06563* + ID_OUI_FROM_DATABASE=Shanghai Railway Communication Factory + +OUI:349A0D* + ID_OUI_FROM_DATABASE=ZBD Displays Ltd + +OUI:A0B5DA* + ID_OUI_FROM_DATABASE=HongKong THTF Co., Ltd + +OUI:CCCD64* + ID_OUI_FROM_DATABASE=SM-Electronic GmbH + +OUI:E82877* + ID_OUI_FROM_DATABASE=TMY Co., Ltd. + +OUI:AC8112* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:6CA906* + ID_OUI_FROM_DATABASE=Telefield Ltd + +OUI:3C02B1* + ID_OUI_FROM_DATABASE=Creation Technologies LP + +OUI:E46C21* + ID_OUI_FROM_DATABASE=messMa GmbH + +OUI:0470BC* + ID_OUI_FROM_DATABASE=Globalstar Inc. + +OUI:E05FB9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:081735* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:20FECD* + ID_OUI_FROM_DATABASE=System In Frontier Inc. + +OUI:94D019* + ID_OUI_FROM_DATABASE=Cydle Corp. + +OUI:2CA157* + ID_OUI_FROM_DATABASE=acromate, Inc. + +OUI:70DDA1* + ID_OUI_FROM_DATABASE=Tellabs + +OUI:30EB25* + ID_OUI_FROM_DATABASE=INTEK DIGITAL + +OUI:BC3E13* + ID_OUI_FROM_DATABASE=Accordance Systems Inc. + +OUI:0455CA* + ID_OUI_FROM_DATABASE=BriView (Xiamen) Corp. + +OUI:D45D42* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BC2846* + ID_OUI_FROM_DATABASE=NextBIT Computing Pvt. Ltd. + +OUI:4425BB* + ID_OUI_FROM_DATABASE=Bamboo Entertainment Corporation + +OUI:B8A8AF* + ID_OUI_FROM_DATABASE=Logic S.p.A. + +OUI:648125* + ID_OUI_FROM_DATABASE=Alphatron Marine BV + +OUI:042605* + ID_OUI_FROM_DATABASE=GFR Gesellschaft für Regelungstechnik und Energieeinsparung mbH + +OUI:9C645E* + ID_OUI_FROM_DATABASE=Harman Consumer Group + +OUI:78CD8E* + ID_OUI_FROM_DATABASE=SMC Networks Inc + +OUI:5C9AD8* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:144C1A* + ID_OUI_FROM_DATABASE=Max Communication GmbH + +OUI:FCE557* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:BC6E76* + ID_OUI_FROM_DATABASE=Green Energy Options Ltd + +OUI:108CCF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:74E06E* + ID_OUI_FROM_DATABASE=Ergophone GmbH + +OUI:18AF9F* + ID_OUI_FROM_DATABASE=DIGITRONIC Automationsanlagen GmbH + +OUI:EC4644* + ID_OUI_FROM_DATABASE=TTK SAS + +OUI:DCD87F* + ID_OUI_FROM_DATABASE=Shenzhen JoinCyber Telecom Equipment Ltd + +OUI:B08991* + ID_OUI_FROM_DATABASE=LGE + +OUI:44DCCB* + ID_OUI_FROM_DATABASE=SEMINDIA SYSTEMS PVT LTD + +OUI:90D92C* + ID_OUI_FROM_DATABASE=HUG-WITSCHI AG + +OUI:B428F1* + ID_OUI_FROM_DATABASE=E-Prime Co., Ltd. + +OUI:B4749F* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:AC2FA8* + ID_OUI_FROM_DATABASE=Humannix Co.,Ltd. + +OUI:7C4AA8* + ID_OUI_FROM_DATABASE=MindTree Wireless PVT Ltd + +OUI:C8A70A* + ID_OUI_FROM_DATABASE=Verizon Business + +OUI:304EC3* + ID_OUI_FROM_DATABASE=Tianjin Techua Technology Co., Ltd. + +OUI:BC4377* + ID_OUI_FROM_DATABASE=Hang Zhou Huite Technology Co.,ltd. + +OUI:A81B18* + ID_OUI_FROM_DATABASE=XTS CORP + +OUI:04E2F8* + ID_OUI_FROM_DATABASE=AEP Ticketing solutions srl + +OUI:8C5105* + ID_OUI_FROM_DATABASE=Shenzhen ireadygo Information Technology CO.,LTD. + +OUI:28E297* + ID_OUI_FROM_DATABASE=Shanghai InfoTM Microelectronics Co.,Ltd. + +OUI:D093F8* + ID_OUI_FROM_DATABASE=Stonestreet One LLC + +OUI:1C334D* + ID_OUI_FROM_DATABASE=ITS Telecom + +OUI:609E64* + ID_OUI_FROM_DATABASE=Vivonic GmbH + +OUI:D44F80* + ID_OUI_FROM_DATABASE=Kemper Digital GmbH + +OUI:34684A* + ID_OUI_FROM_DATABASE=Teraworks Co., Ltd. + +OUI:0CC6AC* + ID_OUI_FROM_DATABASE=DAGS + +OUI:D82A7E* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:5CBD9E* + ID_OUI_FROM_DATABASE=HONGKONG MIRACLE EAGLE TECHNOLOGY(GROUP) LIMITED + +OUI:743889* + ID_OUI_FROM_DATABASE=ANNAX Anzeigesysteme GmbH + +OUI:647FDA* + ID_OUI_FROM_DATABASE=TEKTELIC Communications Inc. + +OUI:90610C* + ID_OUI_FROM_DATABASE=Fida International (S) Pte Ltd + +OUI:3C5F01* + ID_OUI_FROM_DATABASE=Synerchip Co., Ltd. + +OUI:708B78* + ID_OUI_FROM_DATABASE=citygrow technology co., ltd + +OUI:74CD0C* + ID_OUI_FROM_DATABASE=Smith Myers Communications Ltd. + +OUI:B8EE79* + ID_OUI_FROM_DATABASE=YWire Technologies, Inc. + +OUI:40C245* + ID_OUI_FROM_DATABASE=Shenzhen Hexicom Technology Co., Ltd. + +OUI:7076F0* + ID_OUI_FROM_DATABASE=LevelOne Communications (India) Private Limited + +OUI:48C8B6* + ID_OUI_FROM_DATABASE=SysTec GmbH + +OUI:303855* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:9C4563* + ID_OUI_FROM_DATABASE=DIMEP Sistemas + +OUI:E42771* + ID_OUI_FROM_DATABASE=Smartlabs + +OUI:C4EEF5* + ID_OUI_FROM_DATABASE=Oclaro, Inc. + +OUI:0876FF* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:401D59* + ID_OUI_FROM_DATABASE=Biometric Associates, LP + +OUI:4C2C80* + ID_OUI_FROM_DATABASE=Beijing Skyway Technologies Co.,Ltd + +OUI:08D29A* + ID_OUI_FROM_DATABASE=Proformatique + +OUI:90D852* + ID_OUI_FROM_DATABASE=Comtec Co., Ltd. + +OUI:28061E* + ID_OUI_FROM_DATABASE=NINGBO GLOBAL USEFUL ELECTRIC CO.,LTD + +OUI:4037AD* + ID_OUI_FROM_DATABASE=Macro Image Technology, Inc. + +OUI:64E8E6* + ID_OUI_FROM_DATABASE=global moisture management system + +OUI:34A183* + ID_OUI_FROM_DATABASE=AWare, Inc + +OUI:740ABC* + ID_OUI_FROM_DATABASE=JSJS Designs (Europe) Limited + +OUI:588D09* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:342109* + ID_OUI_FROM_DATABASE=Jensen Scandinavia AS + +OUI:08FAE0* + ID_OUI_FROM_DATABASE=Fohhn Audio AG + +OUI:506F9A* + ID_OUI_FROM_DATABASE=Wi-Fi Alliance + +OUI:7CF098* + ID_OUI_FROM_DATABASE=Bee Beans Technologies, Inc. + +OUI:9C7514* + ID_OUI_FROM_DATABASE=Wildix srl + +OUI:BC7DD1* + ID_OUI_FROM_DATABASE=Radio Data Comms + +OUI:28068D* + ID_OUI_FROM_DATABASE=ITL, LLC + +OUI:F0D767* + ID_OUI_FROM_DATABASE=Axema Passagekontroll AB + +OUI:A4AE9A* + ID_OUI_FROM_DATABASE=Maestro Wireless Solutions ltd. + +OUI:5CD135* + ID_OUI_FROM_DATABASE=Xtreme Power Systems + +OUI:9C28BF* + ID_OUI_FROM_DATABASE=Continental Automotive Czech Republic s.r.o. + +OUI:206FEC* + ID_OUI_FROM_DATABASE=Braemac CA LLC + +OUI:64A232* + ID_OUI_FROM_DATABASE=OOO Samlight + +OUI:A082C7* + ID_OUI_FROM_DATABASE=P.T.I Co.,LTD + +OUI:F41F0B* + ID_OUI_FROM_DATABASE=YAMABISHI Corporation + +OUI:447C7F* + ID_OUI_FROM_DATABASE=Innolight Technology Corporation + +OUI:FC75E6* + ID_OUI_FROM_DATABASE=Handreamnet + +OUI:20B0F7* + ID_OUI_FROM_DATABASE=Enclustra GmbH + +OUI:4013D9* + ID_OUI_FROM_DATABASE=Global ES + +OUI:F4DC4D* + ID_OUI_FROM_DATABASE=Beijing CCD Digital Technology Co., Ltd + +OUI:F8B599* + ID_OUI_FROM_DATABASE=Guangzhou CHNAVS Digital Technology Co.,Ltd + +OUI:7C3920* + ID_OUI_FROM_DATABASE=SSOMA SECURITY + +OUI:9C77AA* + ID_OUI_FROM_DATABASE=NADASNV + +OUI:D8B6C1* + ID_OUI_FROM_DATABASE=NetworkAccountant, Inc. + +OUI:58D08F* + ID_OUI_FROM_DATABASE=IEEE 1904.1 Working Group + +OUI:3C99F7* + ID_OUI_FROM_DATABASE=Lansentechnology AB + +OUI:94E711* + ID_OUI_FROM_DATABASE=Xirka Dama Persada PT + +OUI:507D02* + ID_OUI_FROM_DATABASE=BIODIT + +OUI:F44227* + ID_OUI_FROM_DATABASE=S & S Research Inc. + +OUI:D4CBAF* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:CC09C8* + ID_OUI_FROM_DATABASE=IMAQLIQ LTD + +OUI:C4B512* + ID_OUI_FROM_DATABASE=General Electric Digital Energy + +OUI:E02538* + ID_OUI_FROM_DATABASE=Titan Pet Products + +OUI:CC7A30* + ID_OUI_FROM_DATABASE=CMAX Wireless Co., Ltd. + +OUI:D8760A* + ID_OUI_FROM_DATABASE=Escort, Inc. + +OUI:6063FD* + ID_OUI_FROM_DATABASE=Transcend Communication Beijing Co.,Ltd. + +OUI:E08A7E* + ID_OUI_FROM_DATABASE=Exponent + +OUI:80C6CA* + ID_OUI_FROM_DATABASE=Endian s.r.l. + +OUI:F8DAE2* + ID_OUI_FROM_DATABASE=Beta LaserMike + +OUI:E80462* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:70B08C* + ID_OUI_FROM_DATABASE=Shenou Communication Equipment Co.,Ltd + +OUI:F0E5C3* + ID_OUI_FROM_DATABASE=Drägerwerk AG & Co. KG aA + +OUI:446132* + ID_OUI_FROM_DATABASE=ecobee inc + +OUI:A4B2A7* + ID_OUI_FROM_DATABASE=Adaxys Solutions AG + +OUI:F455E0* + ID_OUI_FROM_DATABASE=Niceway CNC Technology Co.,Ltd.Hunan Province + +OUI:AC4FFC* + ID_OUI_FROM_DATABASE=SVS-VISTEK GmbH + +OUI:FC7CE7* + ID_OUI_FROM_DATABASE=FCI USA LLC + +OUI:145412* + ID_OUI_FROM_DATABASE=Entis Co., Ltd. + +OUI:807D1B* + ID_OUI_FROM_DATABASE=Neosystem Co. Ltd. + +OUI:14FEAF* + ID_OUI_FROM_DATABASE=SAGITTAR LIMITED + +OUI:7CB542* + ID_OUI_FROM_DATABASE=ACES Technology + +OUI:40CD3A* + ID_OUI_FROM_DATABASE=Z3 Technology + +OUI:045D56* + ID_OUI_FROM_DATABASE=camtron industrial inc. + +OUI:AC83F0* + ID_OUI_FROM_DATABASE=ImmediaTV Corporation + +OUI:6CE0B0* + ID_OUI_FROM_DATABASE=SOUND4 + +OUI:00336C* + ID_OUI_FROM_DATABASE=SynapSense Corporation + +OUI:E446BD* + ID_OUI_FROM_DATABASE=C&C TECHNIC TAIWAN CO., LTD. + +OUI:7415E2* + ID_OUI_FROM_DATABASE=Tri-Sen Systems Corporation + +OUI:F0BDF1* + ID_OUI_FROM_DATABASE=Sipod Inc. + +OUI:288915* + ID_OUI_FROM_DATABASE=CashGuard Sverige AB + +OUI:180675* + ID_OUI_FROM_DATABASE=DILAX Intelcom GmbH + +OUI:40618E* + ID_OUI_FROM_DATABASE=Stella-Green Co + +OUI:9C4E20* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:408493* + ID_OUI_FROM_DATABASE=Clavister AB + +OUI:1C3A4F* + ID_OUI_FROM_DATABASE=AccuSpec Electronics, LLC + +OUI:58E747* + ID_OUI_FROM_DATABASE=Deltanet AG + +OUI:D87533* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:ECFE7E* + ID_OUI_FROM_DATABASE=BlueRadios, Inc. + +OUI:7C6F06* + ID_OUI_FROM_DATABASE=Caterpillar Trimble Control Technologies + +OUI:7C7673* + ID_OUI_FROM_DATABASE=ENMAS GmbH + +OUI:6C6F18* + ID_OUI_FROM_DATABASE=Stereotaxis, Inc. + +OUI:003532* + ID_OUI_FROM_DATABASE=Electro-Metrics Corporation + +OUI:44376F* + ID_OUI_FROM_DATABASE=Young Electric Sign Co + +OUI:8C640B* + ID_OUI_FROM_DATABASE=Beyond Devices d.o.o. + +OUI:F04335* + ID_OUI_FROM_DATABASE=DVN(Shanghai)Ltd. + +OUI:A479E4* + ID_OUI_FROM_DATABASE=KLINFO Corp + +OUI:003CC5* + ID_OUI_FROM_DATABASE=WONWOO Engineering Co., Ltd + +OUI:E85E53* + ID_OUI_FROM_DATABASE=Infratec Datentechnik GmbH + +OUI:C848F5* + ID_OUI_FROM_DATABASE=MEDISON Xray Co., Ltd + +OUI:1C17D3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACBE75* + ID_OUI_FROM_DATABASE=Ufine Technologies Co.,Ltd. + +OUI:D87157* + ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd. + +OUI:806629* + ID_OUI_FROM_DATABASE=Prescope Technologies CO.,LTD. + +OUI:90F278* + ID_OUI_FROM_DATABASE=Radius Gateway + +OUI:68CA00* + ID_OUI_FROM_DATABASE=Octopus Systems Limited + +OUI:4C3089* + ID_OUI_FROM_DATABASE=Thales Transportation Systems GmbH + +OUI:0C7D7C* + ID_OUI_FROM_DATABASE=Kexiang Information Technology Co, Ltd. + +OUI:70D880* + ID_OUI_FROM_DATABASE=Upos System sp. z o.o. + +OUI:0CC9C6* + ID_OUI_FROM_DATABASE=Samwin Hong Kong Limited + +OUI:B45861* + ID_OUI_FROM_DATABASE=CRemote, LLC + +OUI:AC6706* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:B8653B* + ID_OUI_FROM_DATABASE=Bolymin, Inc. + +OUI:B0973A* + ID_OUI_FROM_DATABASE=E-Fuel Corporation + +OUI:A05DC1* + ID_OUI_FROM_DATABASE=TMCT Co., LTD. + +OUI:E0CA4D* + ID_OUI_FROM_DATABASE=Shenzhen Unistar Communication Co.,LTD + +OUI:E497F0* + ID_OUI_FROM_DATABASE=Shanghai VLC Technologies Ltd. Co. + +OUI:204E6B* + ID_OUI_FROM_DATABASE=Axxana(israel) ltd + +OUI:50F003* + ID_OUI_FROM_DATABASE=Open Stack, Inc. + +OUI:0C17F1* + ID_OUI_FROM_DATABASE=TELECSYS + +OUI:98BC99* + ID_OUI_FROM_DATABASE=Edeltech Co.,Ltd. + +OUI:E8E1E2* + ID_OUI_FROM_DATABASE=Energotest + +OUI:FC683E* + ID_OUI_FROM_DATABASE=Directed Perception, Inc + +OUI:6C1811* + ID_OUI_FROM_DATABASE=Decatur Electronics + +OUI:94592D* + ID_OUI_FROM_DATABASE=EKE Building Technology Systems Ltd + +OUI:9CC077* + ID_OUI_FROM_DATABASE=PrintCounts, LLC + +OUI:A85BB0* + ID_OUI_FROM_DATABASE=Shenzhen Dehoo Technology Co.,Ltd + +OUI:089F97* + ID_OUI_FROM_DATABASE=LEROY AUTOMATION + +OUI:4C5DCD* + ID_OUI_FROM_DATABASE=Oy Finnish Electric Vehicle Technologies Ltd + +OUI:10090C* + ID_OUI_FROM_DATABASE=Janome Sewing Machine Co., Ltd. + +OUI:ECB106* + ID_OUI_FROM_DATABASE=Acuro Networks, Inc + +OUI:7C2E0D* + ID_OUI_FROM_DATABASE=Blackmagic Design + +OUI:08F6F8* + ID_OUI_FROM_DATABASE=GET Engineering + +OUI:6CDC6A* + ID_OUI_FROM_DATABASE=Promethean Limited + +OUI:9055AE* + ID_OUI_FROM_DATABASE=Ericsson, EAB/RWI/K + +OUI:2C3A28* + ID_OUI_FROM_DATABASE=Fagor Electrónica + +OUI:90A7C1* + ID_OUI_FROM_DATABASE=Pakedge Device and Software Inc. + +OUI:80F593* + ID_OUI_FROM_DATABASE=IRCO Sistemas de Telecomunicación S.A. + +OUI:6CFDB9* + ID_OUI_FROM_DATABASE=Proware Technologies Co Ltd. + +OUI:6CFFBE* + ID_OUI_FROM_DATABASE=MPB Communications Inc. + +OUI:583CC6* + ID_OUI_FROM_DATABASE=Omneality Ltd. + +OUI:0097FF* + ID_OUI_FROM_DATABASE=Heimann Sensor GmbH + +OUI:34BA51* + ID_OUI_FROM_DATABASE=Se-Kure Controls, Inc. + +OUI:44A8C2* + ID_OUI_FROM_DATABASE=SEWOO TECH CO., LTD + +OUI:8CD628* + ID_OUI_FROM_DATABASE=Ikor Metering + +OUI:481BD2* + ID_OUI_FROM_DATABASE=Intron Scientific co., ltd. + +OUI:009363* + ID_OUI_FROM_DATABASE=Uni-Link Technology Co., Ltd. + +OUI:64DB18* + ID_OUI_FROM_DATABASE=OpenPattern + +OUI:580556* + ID_OUI_FROM_DATABASE=Elettronica GF S.r.L. + +OUI:88B627* + ID_OUI_FROM_DATABASE=Gembird Europe BV + +OUI:D41F0C* + ID_OUI_FROM_DATABASE=JAI Oy + +OUI:3C4C69* + ID_OUI_FROM_DATABASE=Infinity System S.L. + +OUI:44E49A* + ID_OUI_FROM_DATABASE=OMNITRONICS PTY LTD + +OUI:74F07D* + ID_OUI_FROM_DATABASE=BnCOM Co.,Ltd + +OUI:1065A3* + ID_OUI_FROM_DATABASE=Core Brands LLC + +OUI:20415A* + ID_OUI_FROM_DATABASE=Smarteh d.o.o. + +OUI:703C39* + ID_OUI_FROM_DATABASE=SEAWING Kft + +OUI:14A86B* + ID_OUI_FROM_DATABASE=ShenZhen Telacom Science&Technology Co., Ltd + +OUI:0CC3A7* + ID_OUI_FROM_DATABASE=Meritec + +OUI:4C322D* + ID_OUI_FROM_DATABASE=TELEDATA NETWORKS + +OUI:B8B1C7* + ID_OUI_FROM_DATABASE=BT&COM CO.,LTD + +OUI:A0BFA5* + ID_OUI_FROM_DATABASE=CORESYS + +OUI:D411D6* + ID_OUI_FROM_DATABASE=ShotSpotter, Inc. + +OUI:7CCB0D* + ID_OUI_FROM_DATABASE=Antaira Technologies, LLC + +OUI:ECE9F8* + ID_OUI_FROM_DATABASE=Guang Zhou TRI-SUN Electronics Technology Co., Ltd + +OUI:9CAFCA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:34CE94* + ID_OUI_FROM_DATABASE=Parsec (Pty) Ltd + +OUI:ACE9AA* + ID_OUI_FROM_DATABASE=Hay Systems Ltd + +OUI:082AD0* + ID_OUI_FROM_DATABASE=SRD Innovations Inc. + +OUI:24828A* + ID_OUI_FROM_DATABASE=Prowave Technologies Ltd. + +OUI:6C0F6A* + ID_OUI_FROM_DATABASE=JDC Tech Co., Ltd. + +OUI:6CF049* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:D4C766* + ID_OUI_FROM_DATABASE=Acentic GmbH + +OUI:48EB30* + ID_OUI_FROM_DATABASE=ETERNA TECHNOLOGY, INC. + +OUI:207C8F* + ID_OUI_FROM_DATABASE=Quanta Microsystems,Inc. + +OUI:F8472D* + ID_OUI_FROM_DATABASE=X2gen Digital Corp. Ltd + +OUI:8C598B* + ID_OUI_FROM_DATABASE=C Technologies AB + +OUI:64F970* + ID_OUI_FROM_DATABASE=Kenade Electronics Technology Co.,LTD. + +OUI:A04025* + ID_OUI_FROM_DATABASE=Actioncable, Inc. + +OUI:78998F* + ID_OUI_FROM_DATABASE=MEDILINE ITALIA SRL + +OUI:40ECF8* + ID_OUI_FROM_DATABASE=Siemens AG + +OUI:F04BF2* + ID_OUI_FROM_DATABASE=JTECH Communications, Inc. + +OUI:A8CB95* + ID_OUI_FROM_DATABASE=EAST BEST CO., LTD. + +OUI:C8D1D1* + ID_OUI_FROM_DATABASE=AGAiT Technology Corporation + +OUI:3CF52C* + ID_OUI_FROM_DATABASE=DSPECIALISTS GmbH + +OUI:040EC2* + ID_OUI_FROM_DATABASE=ViewSonic Mobile China Limited + +OUI:5403F5* + ID_OUI_FROM_DATABASE=EBN Technology Corp. + +OUI:7C2F80* + ID_OUI_FROM_DATABASE=Gigaset Communications GmbH + +OUI:446C24* + ID_OUI_FROM_DATABASE=Reallin Electronic Co.,Ltd + +OUI:A0593A* + ID_OUI_FROM_DATABASE=V.D.S. Video Display Systems srl + +OUI:A8F94B* + ID_OUI_FROM_DATABASE=Eltex Enterprise Ltd. + +OUI:906DC8* + ID_OUI_FROM_DATABASE=DLG Automação Industrial Ltda + +OUI:48343D* + ID_OUI_FROM_DATABASE=IEP GmbH + +OUI:C8C13C* + ID_OUI_FROM_DATABASE=RuggedTek Hangzhou Co., Ltd + +OUI:609F9D* + ID_OUI_FROM_DATABASE=CloudSwitch + +OUI:0CE936* + ID_OUI_FROM_DATABASE=ELIMOS srl + +OUI:A4DE50* + ID_OUI_FROM_DATABASE=Total Walther GmbH + +OUI:E8A4C1* + ID_OUI_FROM_DATABASE=Deep Sea Electronics PLC + +OUI:701AED* + ID_OUI_FROM_DATABASE=ADVAS CO., LTD. + +OUI:64C6AF* + ID_OUI_FROM_DATABASE=AXERRA Networks Ltd + +OUI:D8D67E* + ID_OUI_FROM_DATABASE=GSK CNC EQUIPMENT CO.,LTD + +OUI:A4E7E4* + ID_OUI_FROM_DATABASE=Connex GmbH + +OUI:AC583B* + ID_OUI_FROM_DATABASE=Human Assembler, Inc. + +OUI:A05DE7* + ID_OUI_FROM_DATABASE=DIRECTV, Inc. + +OUI:10CA81* + ID_OUI_FROM_DATABASE=PRECIA + +OUI:003A98* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:705AB6* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:003A9A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACBEB6* + ID_OUI_FROM_DATABASE=Visualedge Technology Co., Ltd. + +OUI:40A6A4* + ID_OUI_FROM_DATABASE=PassivSystems Ltd + +OUI:903D6B* + ID_OUI_FROM_DATABASE=Zicon Technology Corp. + +OUI:7C3BD5* + ID_OUI_FROM_DATABASE=Imago Group + +OUI:B894D2* + ID_OUI_FROM_DATABASE=Retail Innovation HTT AB + +OUI:DCE71C* + ID_OUI_FROM_DATABASE=AUG Elektronik GmbH + +OUI:88A5BD* + ID_OUI_FROM_DATABASE=QPCOM INC. + +OUI:DC3350* + ID_OUI_FROM_DATABASE=TechSAT GmbH + +OUI:00271E* + ID_OUI_FROM_DATABASE=Xagyl Communications + +OUI:002716* + ID_OUI_FROM_DATABASE=Adachi-Syokai Co., Ltd. + +OUI:002715* + ID_OUI_FROM_DATABASE=Rebound Telecom. Co., Ltd + +OUI:00270A* + ID_OUI_FROM_DATABASE=IEE S.A. + +OUI:002674* + ID_OUI_FROM_DATABASE=Electronic Solutions, Inc. + +OUI:00266E* + ID_OUI_FROM_DATABASE=Nissho-denki Co.,LTD. + +OUI:00265B* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + +OUI:002661* + ID_OUI_FROM_DATABASE=Irumtek Co., Ltd. + +OUI:002657* + ID_OUI_FROM_DATABASE=OOO NPP EKRA + +OUI:00264E* + ID_OUI_FROM_DATABASE=Rail & Road Protec GmbH + +OUI:0025E6* + ID_OUI_FROM_DATABASE=Belgian Monitoring Systems bvba + +OUI:0025E1* + ID_OUI_FROM_DATABASE=SHANGHAI SEEYOO ELECTRONIC & TECHNOLOGY CO., LTD + +OUI:0025DB* + ID_OUI_FROM_DATABASE=ATI Electronics(Shenzhen) Co., LTD + +OUI:0025D5* + ID_OUI_FROM_DATABASE=Robonica (Pty) Ltd + +OUI:0025C9* + ID_OUI_FROM_DATABASE=SHENZHEN HUAPU DIGITAL CO., LTD + +OUI:0025CE* + ID_OUI_FROM_DATABASE=InnerSpace + +OUI:0025C2* + ID_OUI_FROM_DATABASE=RingBell Co.,Ltd. + +OUI:0026A0* + ID_OUI_FROM_DATABASE=moblic + +OUI:00269A* + ID_OUI_FROM_DATABASE=Carina System Co., Ltd. + +OUI:002694* + ID_OUI_FROM_DATABASE=Senscient Ltd + +OUI:002693* + ID_OUI_FROM_DATABASE=QVidium Technologies, Inc. + +OUI:00268D* + ID_OUI_FROM_DATABASE=CellTel S.p.A. + +OUI:00268E* + ID_OUI_FROM_DATABASE=Alta Solutions, Inc. + +OUI:002687* + ID_OUI_FROM_DATABASE=corega K.K + +OUI:002681* + ID_OUI_FROM_DATABASE=Interspiro AB + +OUI:00267B* + ID_OUI_FROM_DATABASE=GSI Helmholtzzentrum für Schwerionenforschung GmbH + +OUI:0025BB* + ID_OUI_FROM_DATABASE=INNERINT Co., Ltd. + +OUI:0025B6* + ID_OUI_FROM_DATABASE=Telecom FM + +OUI:0025AF* + ID_OUI_FROM_DATABASE=COMFILE Technology + +OUI:0025AA* + ID_OUI_FROM_DATABASE=Beijing Soul Technology Co.,Ltd. + +OUI:0025A9* + ID_OUI_FROM_DATABASE=Shanghai Embedway Information Technologies Co.,Ltd + +OUI:0025A3* + ID_OUI_FROM_DATABASE=Trimax Wireless, Inc. + +OUI:00259C* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:002590* + ID_OUI_FROM_DATABASE=Super Micro Computer, Inc. + +OUI:002580* + ID_OUI_FROM_DATABASE=Equipson S.A. + +OUI:00257C* + ID_OUI_FROM_DATABASE=Huachentel Technology Development Co., Ltd + +OUI:002575* + ID_OUI_FROM_DATABASE=FiberPlex Technologies, LLC + +OUI:002576* + ID_OUI_FROM_DATABASE=NELI TECHNOLOGIES + +OUI:002570* + ID_OUI_FROM_DATABASE=Eastern Communications Company Limited + +OUI:002563* + ID_OUI_FROM_DATABASE=Luxtera Inc + +OUI:002704* + ID_OUI_FROM_DATABASE=Accelerated Concepts, Inc + +OUI:0026FE* + ID_OUI_FROM_DATABASE=MKD Technology Inc. + +OUI:0026F8* + ID_OUI_FROM_DATABASE=Golden Highway Industry Development Co., Ltd. + +OUI:0026EB* + ID_OUI_FROM_DATABASE=Advanced Spectrum Technology Co., Ltd. + +OUI:0026E5* + ID_OUI_FROM_DATABASE=AEG Power Solutions + +OUI:0026DF* + ID_OUI_FROM_DATABASE=TaiDoc Technology Corp. + +OUI:0026D8* + ID_OUI_FROM_DATABASE=Magic Point Inc. + +OUI:0026D2* + ID_OUI_FROM_DATABASE=Pcube Systems, Inc. + +OUI:0026C5* + ID_OUI_FROM_DATABASE=Guangdong Gosun Telecommunications Co.,Ltd + +OUI:0026C0* + ID_OUI_FROM_DATABASE=EnergyHub + +OUI:0026BF* + ID_OUI_FROM_DATABASE=ShenZhen Temobi Science&Tech Development Co.,Ltd + +OUI:0026B7* + ID_OUI_FROM_DATABASE=Kingston Technology Company, Inc. + +OUI:0026A6* + ID_OUI_FROM_DATABASE=TRIXELL + +OUI:00263C* + ID_OUI_FROM_DATABASE=Bachmann Technology GmbH & Co. KG + +OUI:002630* + ID_OUI_FROM_DATABASE=ACOREL S.A.S + +OUI:002629* + ID_OUI_FROM_DATABASE=Juphoon System Software Inc. + +OUI:00262A* + ID_OUI_FROM_DATABASE=Proxense, LLC + +OUI:002624* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:00261D* + ID_OUI_FROM_DATABASE=COP SECURITY SYSTEM CORP. + +OUI:002611* + ID_OUI_FROM_DATABASE=Licera AB + +OUI:002617* + ID_OUI_FROM_DATABASE=OEM Worldwide + +OUI:00260A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0025FE* + ID_OUI_FROM_DATABASE=Pilot Electronics Corporation + +OUI:002605* + ID_OUI_FROM_DATABASE=CC Systems AB + +OUI:002604* + ID_OUI_FROM_DATABASE=Audio Processing Technology Ltd + +OUI:0025F4* + ID_OUI_FROM_DATABASE=KoCo Connector AG + +OUI:0025EB* + ID_OUI_FROM_DATABASE=Reutech Radar Systems (PTY) Ltd + +OUI:00242A* + ID_OUI_FROM_DATABASE=Hittite Microwave Corporation + +OUI:00241D* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:002424* + ID_OUI_FROM_DATABASE=Axis Network Technology + +OUI:002417* + ID_OUI_FROM_DATABASE=Thomson Telecom Belgium + +OUI:002418* + ID_OUI_FROM_DATABASE=Nextwave Semiconductor + +OUI:002411* + ID_OUI_FROM_DATABASE=PharmaSmart LLC + +OUI:00240B* + ID_OUI_FROM_DATABASE=Virtual Computer Inc. + +OUI:00240A* + ID_OUI_FROM_DATABASE=US Beverage Net + +OUI:0024B8* + ID_OUI_FROM_DATABASE=free alliance sdn bhd + +OUI:0024BD* + ID_OUI_FROM_DATABASE=Hainzl Industriesysteme GmbH + +OUI:0024B3* + ID_OUI_FROM_DATABASE=Graf-Syteco GmbH & Co. KG + +OUI:0024AE* + ID_OUI_FROM_DATABASE=Morpho + +OUI:0024A7* + ID_OUI_FROM_DATABASE=Advanced Video Communications Inc. + +OUI:0024AC* + ID_OUI_FROM_DATABASE=Hangzhou DPtech Technologies Co., Ltd. + +OUI:00255D* + ID_OUI_FROM_DATABASE=Morningstar Corporation + +OUI:002551* + ID_OUI_FROM_DATABASE=SE-Elektronic GmbH + +OUI:00254A* + ID_OUI_FROM_DATABASE=RingCube Technologies, Inc. + +OUI:002543* + ID_OUI_FROM_DATABASE=MONEYTECH + +OUI:002544* + ID_OUI_FROM_DATABASE=LoJack Corporation + +OUI:002539* + ID_OUI_FROM_DATABASE=IfTA GmbH + +OUI:00253B* + ID_OUI_FROM_DATABASE=din Dietmar Nocker Facilitymanagement GmbH + +OUI:00250B* + ID_OUI_FROM_DATABASE=CENTROFACTOR INC + +OUI:002504* + ID_OUI_FROM_DATABASE=Valiant Communications Limited + +OUI:0024FD* + ID_OUI_FROM_DATABASE=Accedian Networks Inc + +OUI:0024F8* + ID_OUI_FROM_DATABASE=Technical Solutions Company Ltd. + +OUI:0024F1* + ID_OUI_FROM_DATABASE=Shenzhen Fanhai Sanjiang Electronics Co., Ltd. + +OUI:0024EC* + ID_OUI_FROM_DATABASE=United Information Technology Co.,Ltd. + +OUI:00249B* + ID_OUI_FROM_DATABASE=Action Star Enterprise Co., Ltd. + +OUI:002499* + ID_OUI_FROM_DATABASE=Aquila Technologies + +OUI:002488* + ID_OUI_FROM_DATABASE=Centre For Development Of Telematics + +OUI:002494* + ID_OUI_FROM_DATABASE=Shenzhen Baoxin Tech CO., Ltd. + +OUI:00247A* + ID_OUI_FROM_DATABASE=FU YI CHENG Technology Co., Ltd. + +OUI:002475* + ID_OUI_FROM_DATABASE=Compass System(Embedded Dept.) + +OUI:00246E* + ID_OUI_FROM_DATABASE=Phihong USA Corp. + +OUI:002467* + ID_OUI_FROM_DATABASE=AOC International (Europe) GmbH + +OUI:002469* + ID_OUI_FROM_DATABASE=Smart Doorphones + +OUI:002462* + ID_OUI_FROM_DATABASE=Rayzone Corporation + +OUI:002458* + ID_OUI_FROM_DATABASE=PA Bastion CC + +OUI:00245D* + ID_OUI_FROM_DATABASE=Terberg besturingstechniek B.V. + +OUI:002455* + ID_OUI_FROM_DATABASE=MuLogic BV + +OUI:002450* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00244B* + ID_OUI_FROM_DATABASE=PERCEPTRON INC + +OUI:00253A* + ID_OUI_FROM_DATABASE=CEVA, Ltd. + +OUI:002531* + ID_OUI_FROM_DATABASE=Cloud Engines, Inc. + +OUI:00252F* + ID_OUI_FROM_DATABASE=Energy, Inc. + +OUI:00252A* + ID_OUI_FROM_DATABASE=Chengdu GeeYa Technology Co.,LTD + +OUI:002521* + ID_OUI_FROM_DATABASE=Logitek Electronic Systems, Inc. + +OUI:00251C* + ID_OUI_FROM_DATABASE=EDT + +OUI:002517* + ID_OUI_FROM_DATABASE=Venntis, LLC + +OUI:002510* + ID_OUI_FROM_DATABASE=Pico-Tesla Magnetic Therapies + +OUI:0024E5* + ID_OUI_FROM_DATABASE=Seer Technology, Inc + +OUI:0024E0* + ID_OUI_FROM_DATABASE=DS Tech, LLC + +OUI:0024DE* + ID_OUI_FROM_DATABASE=GLOBAL Technology Inc. + +OUI:0024D9* + ID_OUI_FROM_DATABASE=BICOM, Inc. + +OUI:0024CB* + ID_OUI_FROM_DATABASE=Autonet Mobile + +OUI:0024CD* + ID_OUI_FROM_DATABASE=Willow Garage, Inc. + +OUI:0024C6* + ID_OUI_FROM_DATABASE=Hager Electro SAS + +OUI:00243A* + ID_OUI_FROM_DATABASE=Ludl Electronic Products + +OUI:002434* + ID_OUI_FROM_DATABASE=Lectrosonics, Inc. + +OUI:00242E* + ID_OUI_FROM_DATABASE=Datastrip Inc. + +OUI:002296* + ID_OUI_FROM_DATABASE=LinoWave Corporation + +OUI:00228F* + ID_OUI_FROM_DATABASE=CNRS + +OUI:002290* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00228A* + ID_OUI_FROM_DATABASE=Teratronik elektronische systeme gmbh + +OUI:00227E* + ID_OUI_FROM_DATABASE=Chengdu 30Kaitian Communication Industry Co.Ltd + +OUI:00227D* + ID_OUI_FROM_DATABASE=YE DATA INC. + +OUI:002278* + ID_OUI_FROM_DATABASE=Shenzhen Tongfang Multimedia Technology Co.,Ltd. + +OUI:002272* + ID_OUI_FROM_DATABASE=American Micro-Fuel Device Corp. + +OUI:002271* + ID_OUI_FROM_DATABASE=Jäger Computergesteuerte Meßtechnik GmbH. + +OUI:00226E* + ID_OUI_FROM_DATABASE=Gowell Electronic Limited + +OUI:002358* + ID_OUI_FROM_DATABASE=SYSTEL SA + +OUI:002357* + ID_OUI_FROM_DATABASE=Pitronot Technologies and Engineering P.T.E. Ltd. + +OUI:002352* + ID_OUI_FROM_DATABASE=DATASENSOR S.p.A. + +OUI:00234B* + ID_OUI_FROM_DATABASE=Inyuan Technology Inc. + +OUI:002346* + ID_OUI_FROM_DATABASE=Vestac + +OUI:00233F* + ID_OUI_FROM_DATABASE=Purechoice Inc + +OUI:002338* + ID_OUI_FROM_DATABASE=OJ-Electronics A/S + +OUI:002333* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00232F* + ID_OUI_FROM_DATABASE=Advanced Card Systems Ltd. + +OUI:00232A* + ID_OUI_FROM_DATABASE=eonas IT-Beratung und -Entwicklung GmbH + +OUI:0022C1* + ID_OUI_FROM_DATABASE=Active Storage Inc. + +OUI:0022C2* + ID_OUI_FROM_DATABASE=Proview Eletrônica do Brasil LTDA + +OUI:0022BC* + ID_OUI_FROM_DATABASE=JDSU France SAS + +OUI:0022B5* + ID_OUI_FROM_DATABASE=NOVITA + +OUI:0022AF* + ID_OUI_FROM_DATABASE=Safety Vision + +OUI:0022A2* + ID_OUI_FROM_DATABASE=Xtramus Technologies + +OUI:00229D* + ID_OUI_FROM_DATABASE=PYUNG-HWA IND.CO.,LTD + +OUI:002327* + ID_OUI_FROM_DATABASE=Shouyo Electronics CO., LTD + +OUI:002323* + ID_OUI_FROM_DATABASE=Zylin AS + +OUI:00231A* + ID_OUI_FROM_DATABASE=ITF Co., Ltd. + +OUI:002311* + ID_OUI_FROM_DATABASE=Gloscom Co., Ltd. + +OUI:00230C* + ID_OUI_FROM_DATABASE=CLOVER ELECTRONICS CO.,LTD. + +OUI:002305* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0022FF* + ID_OUI_FROM_DATABASE=NIVIS LLC + +OUI:0022FE* + ID_OUI_FROM_DATABASE=Advanced Illumination + +OUI:002300* + ID_OUI_FROM_DATABASE=Cayee Computer Ltd. + +OUI:0022F6* + ID_OUI_FROM_DATABASE=Syracuse Research Corporation + +OUI:0022F9* + ID_OUI_FROM_DATABASE=Pollin Electronic GmbH + +OUI:0023AD* + ID_OUI_FROM_DATABASE=Xmark Corporation + +OUI:0023A7* + ID_OUI_FROM_DATABASE=Redpine Signals, Inc. + +OUI:0023A1* + ID_OUI_FROM_DATABASE=Trend Electronics Ltd + +OUI:0023A6* + ID_OUI_FROM_DATABASE=E-Mon + +OUI:00239A* + ID_OUI_FROM_DATABASE=EasyData Hardware GmbH + +OUI:002394* + ID_OUI_FROM_DATABASE=Samjeon + +OUI:002390* + ID_OUI_FROM_DATABASE=Algolware Corporation + +OUI:002386* + ID_OUI_FROM_DATABASE=Tour & Andersson AB + +OUI:002405* + ID_OUI_FROM_DATABASE=Dilog Nordic AB + +OUI:0023F5* + ID_OUI_FROM_DATABASE=WILO SE + +OUI:0023FE* + ID_OUI_FROM_DATABASE=Biodevices, SA + +OUI:0023F0* + ID_OUI_FROM_DATABASE=Shanghai Jinghan Weighing Apparatus Co. Ltd. + +OUI:0023EB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0023E5* + ID_OUI_FROM_DATABASE=IPaXiom Networks + +OUI:0023E6* + ID_OUI_FROM_DATABASE=Pirkus, Inc. + +OUI:0023D9* + ID_OUI_FROM_DATABASE=Banner Engineering + +OUI:0023D3* + ID_OUI_FROM_DATABASE=AirLink WiFi Networking Corp. + +OUI:0023D8* + ID_OUI_FROM_DATABASE=Ball-It Oy + +OUI:0023C6* + ID_OUI_FROM_DATABASE=SMC Corporation + +OUI:0023C0* + ID_OUI_FROM_DATABASE=Broadway Networks + +OUI:0023B3* + ID_OUI_FROM_DATABASE=Lyyn AB + +OUI:0022F5* + ID_OUI_FROM_DATABASE=Advanced Realtime Tracking GmbH + +OUI:0022EF* + ID_OUI_FROM_DATABASE=iWDL Technologies + +OUI:0022E8* + ID_OUI_FROM_DATABASE=Applition Co., Ltd. + +OUI:0022E3* + ID_OUI_FROM_DATABASE=Amerigon + +OUI:0022D5* + ID_OUI_FROM_DATABASE=Eaton Corp. Electrical Group Data Center Solutions - Pulizzi + +OUI:0022DC* + ID_OUI_FROM_DATABASE=Vigil Health Solutions Inc. + +OUI:0022D6* + ID_OUI_FROM_DATABASE=Cypak AB + +OUI:0022D0* + ID_OUI_FROM_DATABASE=Polar Electro Oy + +OUI:0022CB* + ID_OUI_FROM_DATABASE=IONODES Inc. + +OUI:0022C6* + ID_OUI_FROM_DATABASE=Sutus Inc + +OUI:002380* + ID_OUI_FROM_DATABASE=Nanoteq + +OUI:00237A* + ID_OUI_FROM_DATABASE=RIM + +OUI:002377* + ID_OUI_FROM_DATABASE=Isotek Electronics Ltd + +OUI:002371* + ID_OUI_FROM_DATABASE=SOAM Systel + +OUI:002365* + ID_OUI_FROM_DATABASE=ELKA-Elektronik GmbH + +OUI:00236A* + ID_OUI_FROM_DATABASE=SmartRG Inc + +OUI:00235E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00225A* + ID_OUI_FROM_DATABASE=Garde Security AB + +OUI:002254* + ID_OUI_FROM_DATABASE=Bigelow Aerospace + +OUI:002251* + ID_OUI_FROM_DATABASE=Lumasense Technologies + +OUI:00224B* + ID_OUI_FROM_DATABASE=AIRTECH TECHNOLOGIES, INC. + +OUI:002245* + ID_OUI_FROM_DATABASE=Leine & Linde AB + +OUI:002242* + ID_OUI_FROM_DATABASE=Alacron Inc. + +OUI:00223B* + ID_OUI_FROM_DATABASE=Communication Networks, LLC + +OUI:002146* + ID_OUI_FROM_DATABASE=Sanmina-SCI + +OUI:00213D* + ID_OUI_FROM_DATABASE=Cermetek Microelectronics, Inc. + +OUI:00213E* + ID_OUI_FROM_DATABASE=TomTom + +OUI:002135* + ID_OUI_FROM_DATABASE=ALCATEL-LUCENT + +OUI:00213A* + ID_OUI_FROM_DATABASE=Winchester Systems Inc. + +OUI:002130* + ID_OUI_FROM_DATABASE=Keico Hightech Inc. + +OUI:00217E* + ID_OUI_FROM_DATABASE=Telit Communication s.p.a + +OUI:002178* + ID_OUI_FROM_DATABASE=Matuschek Messtechnik GmbH + +OUI:002172* + ID_OUI_FROM_DATABASE=Seoultek Valley + +OUI:002166* + ID_OUI_FROM_DATABASE=NovAtel Inc. + +OUI:002165* + ID_OUI_FROM_DATABASE=Presstek Inc. + +OUI:00215F* + ID_OUI_FROM_DATABASE=IHSE GmbH + +OUI:002153* + ID_OUI_FROM_DATABASE=SeaMicro Inc. + +OUI:002158* + ID_OUI_FROM_DATABASE=Style Flying Technology Co. + +OUI:0021AC* + ID_OUI_FROM_DATABASE=Infrared Integrated Systems Ltd + +OUI:0021A5* + ID_OUI_FROM_DATABASE=ERLPhase Power Technologies Ltd. + +OUI:00219F* + ID_OUI_FROM_DATABASE=SATEL OY + +OUI:00218A* + ID_OUI_FROM_DATABASE=Electronic Design and Manufacturing Company + +OUI:00218B* + ID_OUI_FROM_DATABASE=Wescon Technology, Inc. + +OUI:002185* + ID_OUI_FROM_DATABASE=MICRO-STAR INT'L CO.,LTD. + +OUI:001FF9* + ID_OUI_FROM_DATABASE=Advanced Knowledge Associates + +OUI:001FF2* + ID_OUI_FROM_DATABASE=VIA Technologies, Inc. + +OUI:001FED* + ID_OUI_FROM_DATABASE=Tecan Systems Inc. + +OUI:001FE6* + ID_OUI_FROM_DATABASE=Alphion Corporation + +OUI:001FE0* + ID_OUI_FROM_DATABASE=EdgeVelocity Corp + +OUI:001FDA* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:002209* + ID_OUI_FROM_DATABASE=Omron Healthcare Co., Ltd + +OUI:002203* + ID_OUI_FROM_DATABASE=Glensound Electronics Ltd + +OUI:002200* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0021F6* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:0021F0* + ID_OUI_FROM_DATABASE=EW3 Technologies LLC + +OUI:0021EA* + ID_OUI_FROM_DATABASE=Bystronic Laser AG + +OUI:0021E3* + ID_OUI_FROM_DATABASE=SerialTek LLC + +OUI:0021DE* + ID_OUI_FROM_DATABASE=Firepro Wireless + +OUI:0021DD* + ID_OUI_FROM_DATABASE=Northstar Systems Corp + +OUI:0021D7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:002235* + ID_OUI_FROM_DATABASE=Strukton Systems bv + +OUI:002234* + ID_OUI_FROM_DATABASE=Corventis Inc. + +OUI:00222F* + ID_OUI_FROM_DATABASE=Open Grid Computing, Inc. + +OUI:002228* + ID_OUI_FROM_DATABASE=Breeze Innovations Ltd. + +OUI:002222* + ID_OUI_FROM_DATABASE=Schaffner Deutschland GmbH + +OUI:00221C* + ID_OUI_FROM_DATABASE=Private + +OUI:00220F* + ID_OUI_FROM_DATABASE=MoCA (Multimedia over Coax Alliance) + +OUI:00212B* + ID_OUI_FROM_DATABASE=MSA Auer + +OUI:00211D* + ID_OUI_FROM_DATABASE=Dataline AB + +OUI:002124* + ID_OUI_FROM_DATABASE=Optos Plc + +OUI:002118* + ID_OUI_FROM_DATABASE=Athena Tech, Inc. + +OUI:002111* + ID_OUI_FROM_DATABASE=Uniphone Inc. + +OUI:002107* + ID_OUI_FROM_DATABASE=Seowonintech Co Ltd. + +OUI:002101* + ID_OUI_FROM_DATABASE=Aplicaciones Electronicas Quasar (AEQ) + +OUI:002102* + ID_OUI_FROM_DATABASE=UpdateLogic Inc. + +OUI:0021D0* + ID_OUI_FROM_DATABASE=Global Display Solutions Spa + +OUI:0021CB* + ID_OUI_FROM_DATABASE=SMS TECNOLOGIA ELETRONICA LTDA + +OUI:0021C4* + ID_OUI_FROM_DATABASE=Consilium AB + +OUI:0021B8* + ID_OUI_FROM_DATABASE=Inphi Corporation + +OUI:0021B1* + ID_OUI_FROM_DATABASE=DIGITAL SOLUTIONS LTD + +OUI:001F82* + ID_OUI_FROM_DATABASE=Cal-Comp Electronics & Communications Co., Ltd + +OUI:001F7D* + ID_OUI_FROM_DATABASE=embedded wireless GmbH + +OUI:001F7B* + ID_OUI_FROM_DATABASE=TechNexion Ltd. + +OUI:001F7C* + ID_OUI_FROM_DATABASE=Witelcom AS + +OUI:001F79* + ID_OUI_FROM_DATABASE=Lodam Electronics A/S + +OUI:001F74* + ID_OUI_FROM_DATABASE=Eigen Development + +OUI:001F6F* + ID_OUI_FROM_DATABASE=Fujian Sunnada Communication Co.,Ltd. + +OUI:001F63* + ID_OUI_FROM_DATABASE=JSC Goodwin-Europa + +OUI:001F6A* + ID_OUI_FROM_DATABASE=PacketFlux Technologies, Inc. + +OUI:001F69* + ID_OUI_FROM_DATABASE=Pingood Technology Co., Ltd. + +OUI:001F57* + ID_OUI_FROM_DATABASE=Phonik Innovation Co.,LTD + +OUI:001F21* + ID_OUI_FROM_DATABASE=Inner Mongolia Yin An Science & Technology Development Co.,L + +OUI:001F22* + ID_OUI_FROM_DATABASE=Source Photonics, Inc. + +OUI:001F1C* + ID_OUI_FROM_DATABASE=KOBISHI ELECTRIC Co.,Ltd. + +OUI:001F15* + ID_OUI_FROM_DATABASE=Bioscrypt Inc + +OUI:001F10* + ID_OUI_FROM_DATABASE=TOLEDO DO BRASIL INDUSTRIA DE BALANCAS LTDA + +OUI:001F0C* + ID_OUI_FROM_DATABASE=Intelligent Digital Services GmbH + +OUI:001F07* + ID_OUI_FROM_DATABASE=AZTEQ Mobile + +OUI:001FAA* + ID_OUI_FROM_DATABASE=Taseon, Inc. + +OUI:001FA5* + ID_OUI_FROM_DATABASE=Blue-White Industries + +OUI:001FA4* + ID_OUI_FROM_DATABASE=ShenZhen Gongjin Electronics Co.,Ltd + +OUI:001FA0* + ID_OUI_FROM_DATABASE=A10 Networks + +OUI:001F99* + ID_OUI_FROM_DATABASE=SERONICS co.ltd + +OUI:001F9B* + ID_OUI_FROM_DATABASE=POSBRO + +OUI:001F94* + ID_OUI_FROM_DATABASE=Lascar Electronics Ltd + +OUI:001F8D* + ID_OUI_FROM_DATABASE=Ingenieurbuero Stark GmbH und Ko. KG + +OUI:001F89* + ID_OUI_FROM_DATABASE=Signalion GmbH + +OUI:001ED0* + ID_OUI_FROM_DATABASE=Ingespace + +OUI:001ECB* + ID_OUI_FROM_DATABASE=RPC Energoautomatika Ltd + +OUI:001EC4* + ID_OUI_FROM_DATABASE=Celio Corp + +OUI:001EBE* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001EBD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001EB8* + ID_OUI_FROM_DATABASE=Fortis, Inc. + +OUI:001EB1* + ID_OUI_FROM_DATABASE=Cryptsoft Pty Ltd + +OUI:001EA6* + ID_OUI_FROM_DATABASE=Best IT World (India) Pvt. Ltd. + +OUI:001EAC* + ID_OUI_FROM_DATABASE=Armadeus Systems + +OUI:001E9F* + ID_OUI_FROM_DATABASE=Visioneering Systems, Inc. + +OUI:001EA0* + ID_OUI_FROM_DATABASE=XLN-t + +OUI:001EF4* + ID_OUI_FROM_DATABASE=L-3 Communications Display Systems + +OUI:001EF9* + ID_OUI_FROM_DATABASE=Pascom Kommunikations systeme GmbH. + +OUI:001EFA* + ID_OUI_FROM_DATABASE=PROTEI Ltd. + +OUI:001EE8* + ID_OUI_FROM_DATABASE=Mytek + +OUI:001EED* + ID_OUI_FROM_DATABASE=Adventiq Ltd. + +OUI:001EE7* + ID_OUI_FROM_DATABASE=Epic Systems Inc + +OUI:001ED7* + ID_OUI_FROM_DATABASE=H-Stream Wireless, Inc. + +OUI:001E6B* + ID_OUI_FROM_DATABASE=Cisco SPVTG + +OUI:001E72* + ID_OUI_FROM_DATABASE=PCS + +OUI:001E66* + ID_OUI_FROM_DATABASE=RESOL Elektronische Regelungen GmbH + +OUI:001E5F* + ID_OUI_FROM_DATABASE=KwikByte, LLC + +OUI:001E53* + ID_OUI_FROM_DATABASE=Further Tech Co., LTD + +OUI:001E9A* + ID_OUI_FROM_DATABASE=HAMILTON Bonaduz AG + +OUI:001E93* + ID_OUI_FROM_DATABASE=CiriTech Systems Inc + +OUI:001E8E* + ID_OUI_FROM_DATABASE=Hunkeler AG + +OUI:001E88* + ID_OUI_FROM_DATABASE=ANDOR SYSTEM SUPPORT CO., LTD. + +OUI:001E82* + ID_OUI_FROM_DATABASE=SanDisk Corporation + +OUI:001E81* + ID_OUI_FROM_DATABASE=CNB Technology Inc. + +OUI:001E7C* + ID_OUI_FROM_DATABASE=Taiwick Limited + +OUI:001E77* + ID_OUI_FROM_DATABASE=Air2App + +OUI:001F50* + ID_OUI_FROM_DATABASE=Swissdis AG + +OUI:001F49* + ID_OUI_FROM_DATABASE=Manhattan TV Ltd + +OUI:001F4A* + ID_OUI_FROM_DATABASE=Albentia Systems S.A. + +OUI:001F44* + ID_OUI_FROM_DATABASE=GE Transportation Systems + +OUI:001F2F* + ID_OUI_FROM_DATABASE=Berker GmbH & Co. KG + +OUI:001F34* + ID_OUI_FROM_DATABASE=Lung Hwa Electronics Co., Ltd. + +OUI:001F28* + ID_OUI_FROM_DATABASE=HPN Supply Chain + +OUI:001FD5* + ID_OUI_FROM_DATABASE=MICRORISC s.r.o. + +OUI:001FD6* + ID_OUI_FROM_DATABASE=Shenzhen Allywll + +OUI:001FD0* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + +OUI:001FC9* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001FBD* + ID_OUI_FROM_DATABASE=Kyocera Wireless Corp. + +OUI:001FB1* + ID_OUI_FROM_DATABASE=Cybertech Inc. + +OUI:001FB6* + ID_OUI_FROM_DATABASE=Chi Lin Technology Co., Ltd. + +OUI:001D02* + ID_OUI_FROM_DATABASE=Cybertech Telecom Development + +OUI:001CF6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001CEA* + ID_OUI_FROM_DATABASE=Scientific-Atlanta, Inc + +OUI:001CE9* + ID_OUI_FROM_DATABASE=Galaxy Technology Limited + +OUI:001CE5* + ID_OUI_FROM_DATABASE=MBS Electronic Systems GmbH + +OUI:001CE0* + ID_OUI_FROM_DATABASE=DASAN TPS + +OUI:001CD9* + ID_OUI_FROM_DATABASE=GlobalTop Technology Inc. + +OUI:001CD2* + ID_OUI_FROM_DATABASE=King Champion (Hong Kong) Limited + +OUI:001CCD* + ID_OUI_FROM_DATABASE=Alektrona Corporation + +OUI:001CC6* + ID_OUI_FROM_DATABASE=ProStor Systems + +OUI:001CBA* + ID_OUI_FROM_DATABASE=VerScient, Inc. + +OUI:001CB0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001CB5* + ID_OUI_FROM_DATABASE=Neihua Network Technology Co.,LTD.(NHN) + +OUI:001CB6* + ID_OUI_FROM_DATABASE=Duzon CNT Co., Ltd. + +OUI:001CA9* + ID_OUI_FROM_DATABASE=Audiomatica Srl + +OUI:001D5F* + ID_OUI_FROM_DATABASE=OverSpeed SARL + +OUI:001D53* + ID_OUI_FROM_DATABASE=S&O Electronics (Malaysia) Sdn. Bhd. + +OUI:001D4E* + ID_OUI_FROM_DATABASE=TCM Mobile LLC + +OUI:001D4D* + ID_OUI_FROM_DATABASE=Adaptive Recognition Hungary, Inc + +OUI:001D49* + ID_OUI_FROM_DATABASE=Innovation Wireless Inc. + +OUI:001D44* + ID_OUI_FROM_DATABASE=KROHNE Messtechnik GmbH + +OUI:001D3D* + ID_OUI_FROM_DATABASE=Avidyne Corporation + +OUI:001D43* + ID_OUI_FROM_DATABASE=Shenzhen G-link Digital Technology Co., Ltd. + +OUI:001E17* + ID_OUI_FROM_DATABASE=STN BV + +OUI:001E1C* + ID_OUI_FROM_DATABASE=SWS Australia Pty Limited + +OUI:001E12* + ID_OUI_FROM_DATABASE=Ecolab + +OUI:001E0D* + ID_OUI_FROM_DATABASE=Micran Ltd. + +OUI:001E06* + ID_OUI_FROM_DATABASE=WIBRAIN + +OUI:001DFF* + ID_OUI_FROM_DATABASE=Network Critical Solutions Ltd + +OUI:001E00* + ID_OUI_FROM_DATABASE=Shantou Institute of Ultrasonic Instruments + +OUI:001DFA* + ID_OUI_FROM_DATABASE=Fujian LANDI Commercial Equipment Co.,Ltd + +OUI:001DF3* + ID_OUI_FROM_DATABASE=SBS Science & Technology Co., Ltd + +OUI:001DEE* + ID_OUI_FROM_DATABASE=NEXTVISION SISTEMAS DIGITAIS DE TELEVISÃO LTDA. + +OUI:001DED* + ID_OUI_FROM_DATABASE=Grid Net, Inc. + +OUI:001DDE* + ID_OUI_FROM_DATABASE=Zhejiang Broadcast&Television Technology Co.,Ltd. + +OUI:001DE7* + ID_OUI_FROM_DATABASE=Marine Sonic Technology, Ltd. + +OUI:001DD7* + ID_OUI_FROM_DATABASE=Algolith + +OUI:001DD8* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:001DCB* + ID_OUI_FROM_DATABASE=Exéns Development Oy + +OUI:001DC6* + ID_OUI_FROM_DATABASE=SNR Inc. + +OUI:001DC5* + ID_OUI_FROM_DATABASE=Beijing Jiaxun Feihong Electricial Co., Ltd. + +OUI:001DBF* + ID_OUI_FROM_DATABASE=Radiient Technologies, Inc. + +OUI:001DB8* + ID_OUI_FROM_DATABASE=Intoto Inc. + +OUI:001D36* + ID_OUI_FROM_DATABASE=ELECTRONICS CORPORATION OF INDIA LIMITED + +OUI:001D31* + ID_OUI_FROM_DATABASE=HIGHPRO INTERNATIONAL R&D CO,.LTD. + +OUI:001D2A* + ID_OUI_FROM_DATABASE=SHENZHEN BUL-TECH CO.,LTD. + +OUI:001D23* + ID_OUI_FROM_DATABASE=SENSUS + +OUI:001D24* + ID_OUI_FROM_DATABASE=Aclara Power-Line Systems Inc. + +OUI:001D1B* + ID_OUI_FROM_DATABASE=Sangean Electronics Inc. + +OUI:001D1E* + ID_OUI_FROM_DATABASE=KYUSHU TEN CO.,LTD + +OUI:001D15* + ID_OUI_FROM_DATABASE=Shenzhen Dolphin Electronic Co., Ltd + +OUI:001D0E* + ID_OUI_FROM_DATABASE=Agapha Technology co., Ltd. + +OUI:001DB3* + ID_OUI_FROM_DATABASE=HPN Supply Chain + +OUI:001DAE* + ID_OUI_FROM_DATABASE=CHANG TSENG TECHNOLOGY CO., LTD + +OUI:001DA9* + ID_OUI_FROM_DATABASE=Castles Technology, Co., LTD + +OUI:001DA2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001D9C* + ID_OUI_FROM_DATABASE=Rockwell Automation + +OUI:001D9B* + ID_OUI_FROM_DATABASE=Hokuyo Automatic Co., Ltd. + +OUI:001D96* + ID_OUI_FROM_DATABASE=WatchGuard Video + +OUI:001D8F* + ID_OUI_FROM_DATABASE=PureWave Networks + +OUI:001D8A* + ID_OUI_FROM_DATABASE=TechTrex Inc + +OUI:001D89* + ID_OUI_FROM_DATABASE=VaultStor Corporation + +OUI:001D7F* + ID_OUI_FROM_DATABASE=Tekron International Ltd + +OUI:001D83* + ID_OUI_FROM_DATABASE=Emitech Corporation + +OUI:001D79* + ID_OUI_FROM_DATABASE=SIGNAMAX LLC + +OUI:001D66* + ID_OUI_FROM_DATABASE=Hyundai Telecom + +OUI:001D6D* + ID_OUI_FROM_DATABASE=Confidant International LLC + +OUI:001E42* + ID_OUI_FROM_DATABASE=Teltonika + +OUI:001E3C* + ID_OUI_FROM_DATABASE=Lyngbox Media AB + +OUI:001E2F* + ID_OUI_FROM_DATABASE=DiMoto Pty Ltd + +OUI:001E36* + ID_OUI_FROM_DATABASE=IPTE + +OUI:001E29* + ID_OUI_FROM_DATABASE=Hypertherm Inc + +OUI:001E23* + ID_OUI_FROM_DATABASE=Electronic Educational Devices, Inc + +OUI:001C0C* + ID_OUI_FROM_DATABASE=TANITA Corporation + +OUI:001C06* + ID_OUI_FROM_DATABASE=Siemens Numerical Control Ltd., Nanjing + +OUI:001BFF* + ID_OUI_FROM_DATABASE=Millennia Media inc. + +OUI:001BFA* + ID_OUI_FROM_DATABASE=G.i.N. mbH + +OUI:001BE3* + ID_OUI_FROM_DATABASE=Health Hero Network, Inc. + +OUI:001BE5* + ID_OUI_FROM_DATABASE=802automation Limited + +OUI:001BE4* + ID_OUI_FROM_DATABASE=TOWNET SRL + +OUI:001BDE* + ID_OUI_FROM_DATABASE=Renkus-Heinz, Inc. + +OUI:001BD2* + ID_OUI_FROM_DATABASE=ULTRA-X ASIA PACIFIC Inc. + +OUI:001C6B* + ID_OUI_FROM_DATABASE=COVAX Co. Ltd + +OUI:001C64* + ID_OUI_FROM_DATABASE=Landis+Gyr + +OUI:001C5F* + ID_OUI_FROM_DATABASE=Winland Electronics, Inc. + +OUI:001C53* + ID_OUI_FROM_DATABASE=Synergy Lighting Controls + +OUI:001C58* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001C4E* + ID_OUI_FROM_DATABASE=TASA International Limited + +OUI:001C47* + ID_OUI_FROM_DATABASE=Hangzhou Hollysys Automation Co., Ltd + +OUI:001C49* + ID_OUI_FROM_DATABASE=Zoltan Technology Inc. + +OUI:001C48* + ID_OUI_FROM_DATABASE=WiDeFi, Inc. + +OUI:001C3B* + ID_OUI_FROM_DATABASE=AmRoad Technology Inc. + +OUI:001C42* + ID_OUI_FROM_DATABASE=Parallels, Inc. + +OUI:001B72* + ID_OUI_FROM_DATABASE=Sicep s.p.a. + +OUI:001B6D* + ID_OUI_FROM_DATABASE=Midtronics, Inc. + +OUI:001B6B* + ID_OUI_FROM_DATABASE=Swyx Solutions AG + +OUI:001B6C* + ID_OUI_FROM_DATABASE=LookX Digital Media BV + +OUI:001B66* + ID_OUI_FROM_DATABASE=Sennheiser electronic GmbH & Co. KG + +OUI:001B5F* + ID_OUI_FROM_DATABASE=Alien Technology + +OUI:001B5A* + ID_OUI_FROM_DATABASE=Apollo Imaging Technologies, Inc. + +OUI:001B53* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B47* + ID_OUI_FROM_DATABASE=Futarque A/S + +OUI:001B4C* + ID_OUI_FROM_DATABASE=Signtech + +OUI:001B4E* + ID_OUI_FROM_DATABASE=Navman New Zealand + +OUI:001B40* + ID_OUI_FROM_DATABASE=Network Automation mxc AB + +OUI:001C9E* + ID_OUI_FROM_DATABASE=Dualtech IT AB + +OUI:001C97* + ID_OUI_FROM_DATABASE=Enzytek Technology Inc., + +OUI:001C98* + ID_OUI_FROM_DATABASE=LUCKY TECHNOLOGY (HK) COMPANY LIMITED + +OUI:001C92* + ID_OUI_FROM_DATABASE=Tervela + +OUI:001C8B* + ID_OUI_FROM_DATABASE=MJ Innovations Ltd. + +OUI:001C86* + ID_OUI_FROM_DATABASE=Cranite Systems, Inc. + +OUI:001C85* + ID_OUI_FROM_DATABASE=Eunicorn + +OUI:001C81* + ID_OUI_FROM_DATABASE=NextGen Venturi LTD + +OUI:001C72* + ID_OUI_FROM_DATABASE=Mayer & Cie GmbH & Co KG + +OUI:001C77* + ID_OUI_FROM_DATABASE=Prodys + +OUI:001B34* + ID_OUI_FROM_DATABASE=Focus System Inc. + +OUI:001B39* + ID_OUI_FROM_DATABASE=Proxicast + +OUI:001B3B* + ID_OUI_FROM_DATABASE=Yi-Qing CO., LTD + +OUI:001B28* + ID_OUI_FROM_DATABASE=POLYGON, JSC + +OUI:001B2D* + ID_OUI_FROM_DATABASE=Med-Eng Systems Inc. + +OUI:001B1F* + ID_OUI_FROM_DATABASE=DELTA - Danish Electronics, Light & Acoustics + +OUI:001B18* + ID_OUI_FROM_DATABASE=Tsuken Electric Ind. Co.,Ltd + +OUI:001B13* + ID_OUI_FROM_DATABASE=Icron Technologies Corporation + +OUI:001B0C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001BA7* + ID_OUI_FROM_DATABASE=Lorica Solutions + +OUI:001BA2* + ID_OUI_FROM_DATABASE=IDS Imaging Development Systems GmbH + +OUI:001B96* + ID_OUI_FROM_DATABASE=General Sensing + +OUI:001B9B* + ID_OUI_FROM_DATABASE=Hose-McCann Communications + +OUI:001B8F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001B85* + ID_OUI_FROM_DATABASE=MAN Diesel SE + +OUI:001B7E* + ID_OUI_FROM_DATABASE=Beckmann GmbH + +OUI:001B79* + ID_OUI_FROM_DATABASE=FAIVELEY TRANSPORT + +OUI:001C36* + ID_OUI_FROM_DATABASE=iNEWiT NV + +OUI:001C2F* + ID_OUI_FROM_DATABASE=Pfister GmbH + +OUI:001C28* + ID_OUI_FROM_DATABASE=Sphairon Technologies GmbH + +OUI:001C1E* + ID_OUI_FROM_DATABASE=emtrion GmbH + +OUI:001C19* + ID_OUI_FROM_DATABASE=secunet Security Networks AG + +OUI:001C0B* + ID_OUI_FROM_DATABASE=SmartAnt Telecom + +OUI:001C0D* + ID_OUI_FROM_DATABASE=G-Technology, Inc. + +OUI:001BCB* + ID_OUI_FROM_DATABASE=PEMPEK SYSTEMS PTY LTD + +OUI:001BC4* + ID_OUI_FROM_DATABASE=Ultratec, Inc. + +OUI:001BB5* + ID_OUI_FROM_DATABASE=ZF Electronics GmbH + +OUI:001BAE* + ID_OUI_FROM_DATABASE=Micro Control Systems, Inc + +OUI:001BA8* + ID_OUI_FROM_DATABASE=UBI&MOBI,.Inc + +OUI:001B05* + ID_OUI_FROM_DATABASE=YMC AG + +OUI:001B00* + ID_OUI_FROM_DATABASE=Neopost Technologies + +OUI:001AF9* + ID_OUI_FROM_DATABASE=AeroVIronment (AV Inc) + +OUI:001AEF* + ID_OUI_FROM_DATABASE=Loopcomm Technology, Inc. + +OUI:001AE3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001AEA* + ID_OUI_FROM_DATABASE=Radio Terminal Systems Pty Ltd + +OUI:001A26* + ID_OUI_FROM_DATABASE=Deltanode Solutions AB + +OUI:001A2B* + ID_OUI_FROM_DATABASE=Ayecom Technology Co., Ltd. + +OUI:001A1F* + ID_OUI_FROM_DATABASE=Coastal Environmental Systems + +OUI:001A1A* + ID_OUI_FROM_DATABASE=Gentex Corporation/Electro-Acoustic Products + +OUI:001A13* + ID_OUI_FROM_DATABASE=Wanlida Group Co., LTD + +OUI:001A0E* + ID_OUI_FROM_DATABASE=Cheng Uei Precision Industry Co.,Ltd + +OUI:001A0C* + ID_OUI_FROM_DATABASE=Swe-Dish Satellite Systems AB + +OUI:001A07* + ID_OUI_FROM_DATABASE=Arecont Vision + +OUI:001A00* + ID_OUI_FROM_DATABASE=MATRIX INC. + +OUI:001AD0* + ID_OUI_FROM_DATABASE=Albis Technologies AG + +OUI:001AD5* + ID_OUI_FROM_DATABASE=KMC CHAIN INDUSTRIAL CO., LTD. + +OUI:001AD7* + ID_OUI_FROM_DATABASE=Christie Digital Systems, Inc. + +OUI:001AC9* + ID_OUI_FROM_DATABASE=SUZUKEN CO.,LTD + +OUI:001ABA* + ID_OUI_FROM_DATABASE=Caton Overseas Limited + +OUI:001ABF* + ID_OUI_FROM_DATABASE=TRUMPF Laser Marking Systems AG + +OUI:001A81* + ID_OUI_FROM_DATABASE=Zelax + +OUI:001A88* + ID_OUI_FROM_DATABASE=Venergy,Co,Ltd + +OUI:001A7A* + ID_OUI_FROM_DATABASE=Lismore Instruments Limited + +OUI:001A70* + ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC + +OUI:001A72* + ID_OUI_FROM_DATABASE=Mosart Semiconductor Corp. + +OUI:001A64* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:001A56* + ID_OUI_FROM_DATABASE=ViewTel Co,. Ltd. + +OUI:001A5B* + ID_OUI_FROM_DATABASE=NetCare Service Co., Ltd. + +OUI:001A5F* + ID_OUI_FROM_DATABASE=KitWorks.fi Ltd. + +OUI:0019B6* + ID_OUI_FROM_DATABASE=Euro Emme s.r.l. + +OUI:0019A3* + ID_OUI_FROM_DATABASE=asteel electronique atlantique + +OUI:0019A8* + ID_OUI_FROM_DATABASE=WiQuest Communications + +OUI:0019AA* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0019AF* + ID_OUI_FROM_DATABASE=Rigol Technologies, Inc. + +OUI:001992* + ID_OUI_FROM_DATABASE=ADTRAN INC. + +OUI:001997* + ID_OUI_FROM_DATABASE=Soft Device Sdn Bhd + +OUI:00199C* + ID_OUI_FROM_DATABASE=CTRING + +OUI:001A43* + ID_OUI_FROM_DATABASE=Logical Link Communications + +OUI:001A48* + ID_OUI_FROM_DATABASE=Takacom Corporation + +OUI:001A4A* + ID_OUI_FROM_DATABASE=Qumranet Inc. + +OUI:001A3C* + ID_OUI_FROM_DATABASE=Technowave Ltd. + +OUI:001A30* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001A35* + ID_OUI_FROM_DATABASE=BARTEC GmbH + +OUI:001A37* + ID_OUI_FROM_DATABASE=Lear Corporation + +OUI:0019F2* + ID_OUI_FROM_DATABASE=Teradyne K.K. + +OUI:0019F7* + ID_OUI_FROM_DATABASE=Onset Computer Corporation + +OUI:0019DF* + ID_OUI_FROM_DATABASE=Thomson Inc. + +OUI:0019E6* + ID_OUI_FROM_DATABASE=TOYO MEDIC CO.,LTD. + +OUI:0019EB* + ID_OUI_FROM_DATABASE=Pyronix Ltd + +OUI:0019CC* + ID_OUI_FROM_DATABASE=RCG (HK) Ltd + +OUI:0019D3* + ID_OUI_FROM_DATABASE=TRAK Microwave + +OUI:0019D8* + ID_OUI_FROM_DATABASE=MAXFOR + +OUI:0019C2* + ID_OUI_FROM_DATABASE=Equustek Solutions, Inc. + +OUI:00198B* + ID_OUI_FROM_DATABASE=Novera Optics Korea, Inc. + +OUI:00198D* + ID_OUI_FROM_DATABASE=Ocean Optics, Inc. + +OUI:00197F* + ID_OUI_FROM_DATABASE=PLANTRONICS, INC. + +OUI:001986* + ID_OUI_FROM_DATABASE=Cheng Hongjian + +OUI:001973* + ID_OUI_FROM_DATABASE=Zeugma Systems + +OUI:00197A* + ID_OUI_FROM_DATABASE=MAZeT GmbH + +OUI:001967* + ID_OUI_FROM_DATABASE=TELDAT Sp.J. + +OUI:00196C* + ID_OUI_FROM_DATABASE=ETROVISION TECHNOLOGY + +OUI:00196E* + ID_OUI_FROM_DATABASE=Metacom (Pty) Ltd. + +OUI:001AAC* + ID_OUI_FROM_DATABASE=Corelatus AB + +OUI:001AAE* + ID_OUI_FROM_DATABASE=Savant Systems LLC + +OUI:001AB3* + ID_OUI_FROM_DATABASE=VISIONITE INC. + +OUI:001AA7* + ID_OUI_FROM_DATABASE=Torian Wireless + +OUI:001A9E* + ID_OUI_FROM_DATABASE=ICON Digital International Limited + +OUI:001AA3* + ID_OUI_FROM_DATABASE=DELORME + +OUI:001AA5* + ID_OUI_FROM_DATABASE=BRN Phoenix + +OUI:001AA4* + ID_OUI_FROM_DATABASE=Future University-Hakodate + +OUI:001A97* + ID_OUI_FROM_DATABASE=fitivision technology Inc. + +OUI:001A8D* + ID_OUI_FROM_DATABASE=AVECS Bergen GmbH + +OUI:001962* + ID_OUI_FROM_DATABASE=Commerciant, LP + +OUI:00195D* + ID_OUI_FROM_DATABASE=ShenZhen XinHuaTong Opto Electronics Co.,Ltd + +OUI:001951* + ID_OUI_FROM_DATABASE=NETCONS, s.r.o. + +OUI:001956* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00194A* + ID_OUI_FROM_DATABASE=TESTO AG + +OUI:001943* + ID_OUI_FROM_DATABASE=Belden + +OUI:001873* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001875* + ID_OUI_FROM_DATABASE=AnaCise Testnology Pte Ltd + +OUI:00187A* + ID_OUI_FROM_DATABASE=Wiremold + +OUI:00186E* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:00185E* + ID_OUI_FROM_DATABASE=Nexterm Inc. + +OUI:001860* + ID_OUI_FROM_DATABASE=SIM Technology Group Shanghai Simcom Ltd., + +OUI:001865* + ID_OUI_FROM_DATABASE=Siemens Healthcare Diagnostics Manufacturing Ltd + +OUI:001903* + ID_OUI_FROM_DATABASE=Bigfoot Networks Inc + +OUI:0018F9* + ID_OUI_FROM_DATABASE=VVOND, Inc. + +OUI:0018F2* + ID_OUI_FROM_DATABASE=Beijing Tianyu Communication Equipment Co., Ltd + +OUI:0018EB* + ID_OUI_FROM_DATABASE=Blue Zen Enterprises Private Limited + +OUI:0018ED* + ID_OUI_FROM_DATABASE=Accutech Ultrasystems Co., Ltd. + +OUI:0018E6* + ID_OUI_FROM_DATABASE=Computer Hardware Design SIA + +OUI:0018DF* + ID_OUI_FROM_DATABASE=The Morey Corporation + +OUI:001937* + ID_OUI_FROM_DATABASE=CommerceGuard AB + +OUI:00192E* + ID_OUI_FROM_DATABASE=Spectral Instruments, Inc. + +OUI:001932* + ID_OUI_FROM_DATABASE=Gude Analog- und Digialsysteme GmbH + +OUI:001922* + ID_OUI_FROM_DATABASE=CM Comandos Lineares + +OUI:001927* + ID_OUI_FROM_DATABASE=ImCoSys Ltd + +OUI:001929* + ID_OUI_FROM_DATABASE=2M2B Montadora de Maquinas Bahia Brasil LTDA + +OUI:00190F* + ID_OUI_FROM_DATABASE=Advansus Corp. + +OUI:001916* + ID_OUI_FROM_DATABASE=PayTec AG + +OUI:00191B* + ID_OUI_FROM_DATABASE=Sputnik Engineering AG + +OUI:001908* + ID_OUI_FROM_DATABASE=Duaxes Corporation + +OUI:00190A* + ID_OUI_FROM_DATABASE=HASWARE INC. + +OUI:0017D6* + ID_OUI_FROM_DATABASE=Bluechips Microhouse Co.,Ltd. + +OUI:0017DB* + ID_OUI_FROM_DATABASE=CANKO TECHNOLOGIES INC. + +OUI:0017CC* + ID_OUI_FROM_DATABASE=Alcatel-Lucent + +OUI:0017C5* + ID_OUI_FROM_DATABASE=SonicWALL + +OUI:0017B9* + ID_OUI_FROM_DATABASE=Gambro Lundia AB + +OUI:0017BE* + ID_OUI_FROM_DATABASE=Tratec Telecom B.V. + +OUI:0017C0* + ID_OUI_FROM_DATABASE=PureTech Systems, Inc. + +OUI:001852* + ID_OUI_FROM_DATABASE=StorLink Semiconductors, Inc. + +OUI:001859* + ID_OUI_FROM_DATABASE=Strawberry Linux Co.,Ltd. + +OUI:00184B* + ID_OUI_FROM_DATABASE=Las Vegas Gaming, Inc. + +OUI:001846* + ID_OUI_FROM_DATABASE=Crypto S.A. + +OUI:001829* + ID_OUI_FROM_DATABASE=Gatsometer + +OUI:001835* + ID_OUI_FROM_DATABASE=Thoratec / ITC + +OUI:001824* + ID_OUI_FROM_DATABASE=Kimaldi Electronics, S.L. + +OUI:001822* + ID_OUI_FROM_DATABASE=CEC TELECOM CO.,LTD. + +OUI:0017B2* + ID_OUI_FROM_DATABASE=SK Telesys + +OUI:0017AD* + ID_OUI_FROM_DATABASE=AceNet Corporation + +OUI:0017A6* + ID_OUI_FROM_DATABASE=YOSIN ELECTRONICS CO., LTD. + +OUI:0017A1* + ID_OUI_FROM_DATABASE=3soft inc. + +OUI:00179C* + ID_OUI_FROM_DATABASE=DEPRAG SCHULZ GMBH u. CO. + +OUI:001790* + ID_OUI_FROM_DATABASE=HYUNDAI DIGITECH Co, Ltd. + +OUI:001795* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0018CE* + ID_OUI_FROM_DATABASE=Dreamtech Co., Ltd + +OUI:0018D3* + ID_OUI_FROM_DATABASE=TEAMCAST + +OUI:0018C2* + ID_OUI_FROM_DATABASE=Firetide, Inc + +OUI:0018C4* + ID_OUI_FROM_DATABASE=Raba Technologies LLC + +OUI:0018C9* + ID_OUI_FROM_DATABASE=EOps Technology Limited + +OUI:0018BD* + ID_OUI_FROM_DATABASE=SHENZHEN DVBWORLD TECHNOLOGY CO., LTD. + +OUI:0018B1* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:0018B6* + ID_OUI_FROM_DATABASE=S3C, Inc. + +OUI:0018A3* + ID_OUI_FROM_DATABASE=ZIPPY TECHNOLOGY CORP. + +OUI:0018AA* + ID_OUI_FROM_DATABASE=Protec Fire Detection plc + +OUI:001816* + ID_OUI_FROM_DATABASE=Ubixon Co., Ltd. + +OUI:00181D* + ID_OUI_FROM_DATABASE=ASIA ELECTRONICS CO.,LTD + +OUI:001811* + ID_OUI_FROM_DATABASE=Neuros Technology International, LLC. + +OUI:00180A* + ID_OUI_FROM_DATABASE=Meraki, Inc. + +OUI:001801* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:0017F5* + ID_OUI_FROM_DATABASE=LIG NEOPTEK + +OUI:0017FA* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:0017FC* + ID_OUI_FROM_DATABASE=Suprema Inc. + +OUI:00189E* + ID_OUI_FROM_DATABASE=OMNIKEY GmbH. + +OUI:001894* + ID_OUI_FROM_DATABASE=NPCore, Inc. + +OUI:001899* + ID_OUI_FROM_DATABASE=ShenZhen jieshun Science&Technology Industry CO,LTD. + +OUI:001886* + ID_OUI_FROM_DATABASE=EL-TECH, INC. + +OUI:001888* + ID_OUI_FROM_DATABASE=GOTIVE a.s. + +OUI:001881* + ID_OUI_FROM_DATABASE=Buyang Electronics Industrial Co., Ltd + +OUI:0016D4* + ID_OUI_FROM_DATABASE=Compal Communications, Inc. + +OUI:0016D9* + ID_OUI_FROM_DATABASE=NINGBO BIRD CO.,LTD. + +OUI:0016C8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0016CD* + ID_OUI_FROM_DATABASE=HIJI HIGH-TECH CO., LTD. + +OUI:0016C1* + ID_OUI_FROM_DATABASE=Eleksen Ltd + +OUI:0016BA* + ID_OUI_FROM_DATABASE=WEATHERNEWS INC. + +OUI:00164F* + ID_OUI_FROM_DATABASE=World Ethnic Broadcastin Inc. + +OUI:001648* + ID_OUI_FROM_DATABASE=SSD Company Limited + +OUI:001643* + ID_OUI_FROM_DATABASE=Sunhillo Corporation + +OUI:00163E* + ID_OUI_FROM_DATABASE=Xensource, Inc. + +OUI:001637* + ID_OUI_FROM_DATABASE=CITEL SpA + +OUI:00162B* + ID_OUI_FROM_DATABASE=Togami Electric Mfg.co.,Ltd. + +OUI:001755* + ID_OUI_FROM_DATABASE=GE Security + +OUI:001747* + ID_OUI_FROM_DATABASE=Trimble + +OUI:001749* + ID_OUI_FROM_DATABASE=HYUNDAE YONG-O-SA CO.,LTD + +OUI:00174E* + ID_OUI_FROM_DATABASE=Parama-tech Co.,Ltd. + +OUI:001732* + ID_OUI_FROM_DATABASE=Science-Technical Center RISSA + +OUI:001734* + ID_OUI_FROM_DATABASE=ADC Telecommunications + +OUI:001739* + ID_OUI_FROM_DATABASE=Bright Headphone Electronics Company + +OUI:00172D* + ID_OUI_FROM_DATABASE=Axcen Photonics Corporation + +OUI:001624* + ID_OUI_FROM_DATABASE=Teneros, Inc. + +OUI:001613* + ID_OUI_FROM_DATABASE=LibreStream Technologies Inc. + +OUI:001618* + ID_OUI_FROM_DATABASE=HIVION Co., Ltd. + +OUI:00161F* + ID_OUI_FROM_DATABASE=SUNWAVETEC Co., Ltd. + +OUI:00160E* + ID_OUI_FROM_DATABASE=Optica Technologies Inc. + +OUI:001607* + ID_OUI_FROM_DATABASE=Curves International Inc. + +OUI:001609* + ID_OUI_FROM_DATABASE=Unitech electronics co., ltd. + +OUI:001608* + ID_OUI_FROM_DATABASE=Sequans Communications + +OUI:001602* + ID_OUI_FROM_DATABASE=CEYON TECHNOLOGY CO.,LTD. + +OUI:0015FB* + ID_OUI_FROM_DATABASE=setex schermuly textile computer gmbh + +OUI:0015F6* + ID_OUI_FROM_DATABASE=SCIENCE AND ENGINEERING SERVICES, INC. + +OUI:001782* + ID_OUI_FROM_DATABASE=LoBenn Inc. + +OUI:001789* + ID_OUI_FROM_DATABASE=Zenitron Corporation + +OUI:00176D* + ID_OUI_FROM_DATABASE=CORE CORPORATION + +OUI:001771* + ID_OUI_FROM_DATABASE=APD Communications Ltd + +OUI:001776* + ID_OUI_FROM_DATABASE=Meso Scale Diagnostics, LLC + +OUI:001761* + ID_OUI_FROM_DATABASE=Private + +OUI:001768* + ID_OUI_FROM_DATABASE=Zinwave Ltd + +OUI:00175C* + ID_OUI_FROM_DATABASE=SHARP CORPORATION + +OUI:00175A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001709* + ID_OUI_FROM_DATABASE=Exalt Communications + +OUI:001704* + ID_OUI_FROM_DATABASE=Shinco Electronics Group Co.,Ltd + +OUI:0016FD* + ID_OUI_FROM_DATABASE=Jaty Electronics + +OUI:0016F1* + ID_OUI_FROM_DATABASE=OmniSense, LLC + +OUI:0016F6* + ID_OUI_FROM_DATABASE=Video Products Group + +OUI:0016F8* + ID_OUI_FROM_DATABASE=AVIQTECH TECHNOLOGY CO., LTD. + +OUI:0016E5* + ID_OUI_FROM_DATABASE=FORDLEY DEVELOPMENT LIMITED + +OUI:0016DE* + ID_OUI_FROM_DATABASE=FAST Inc + +OUI:00167E* + ID_OUI_FROM_DATABASE=DIBOSS.CO.,LTD + +OUI:001680* + ID_OUI_FROM_DATABASE=Bally Gaming + Systems + +OUI:001679* + ID_OUI_FROM_DATABASE=eOn Communications + +OUI:00166E* + ID_OUI_FROM_DATABASE=Arbitron Inc. + +OUI:001667* + ID_OUI_FROM_DATABASE=A-TEC Subsystem INC. + +OUI:00165B* + ID_OUI_FROM_DATABASE=Grip Audio + +OUI:001654* + ID_OUI_FROM_DATABASE=Flex-P Industries Sdn. Bhd. + +OUI:001721* + ID_OUI_FROM_DATABASE=FITRE S.p.A. + +OUI:001726* + ID_OUI_FROM_DATABASE=m2c Electronic Technology Ltd. + +OUI:00171A* + ID_OUI_FROM_DATABASE=Winegard Company + +OUI:00171F* + ID_OUI_FROM_DATABASE=IMV Corporation + +OUI:001713* + ID_OUI_FROM_DATABASE=Tiger NetCom + +OUI:00170E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0016A9* + ID_OUI_FROM_DATABASE=2EI + +OUI:0016AE* + ID_OUI_FROM_DATABASE=INVENTEL + +OUI:00169D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00169F* + ID_OUI_FROM_DATABASE=Vimtron Electronics Co., Ltd. + +OUI:0016A4* + ID_OUI_FROM_DATABASE=Ezurio Ltd + +OUI:001691* + ID_OUI_FROM_DATABASE=Moser-Baer AG + +OUI:001698* + ID_OUI_FROM_DATABASE=T&A Mobile Phones + +OUI:00168C* + ID_OUI_FROM_DATABASE=DSL Partner AS + +OUI:001685* + ID_OUI_FROM_DATABASE=Elisa Oyj + +OUI:0015EF* + ID_OUI_FROM_DATABASE=NEC TOKIN Corporation + +OUI:0015E3* + ID_OUI_FROM_DATABASE=Dream Technologies Corporation + +OUI:0015D9* + ID_OUI_FROM_DATABASE=PKC Electronics Oy + +OUI:0015D2* + ID_OUI_FROM_DATABASE=Xantech Corporation + +OUI:0015CC* + ID_OUI_FROM_DATABASE=UQUEST, LTD. + +OUI:0015CB* + ID_OUI_FROM_DATABASE=Surf Communication Solutions Ltd. + +OUI:0015CD* + ID_OUI_FROM_DATABASE=Exartech International Corp. + +OUI:0015C6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0015BB* + ID_OUI_FROM_DATABASE=SMA Solar Technology AG + +OUI:0014D5* + ID_OUI_FROM_DATABASE=Datang Telecom Technology CO. , LCD,Optical Communication Br + +OUI:0014DA* + ID_OUI_FROM_DATABASE=Huntleigh Healthcare + +OUI:0014CE* + ID_OUI_FROM_DATABASE=NF CORPORATION + +OUI:0014C8* + ID_OUI_FROM_DATABASE=Contemporary Research Corp + +OUI:0014BB* + ID_OUI_FROM_DATABASE=Open Interface North America + +OUI:0014B6* + ID_OUI_FROM_DATABASE=Enswer Technology Inc. + +OUI:0014AC* + ID_OUI_FROM_DATABASE=Bountiful WiFi + +OUI:0014B1* + ID_OUI_FROM_DATABASE=Axell Wireless Limited + +OUI:001476* + ID_OUI_FROM_DATABASE=MultiCom Industries Limited + +OUI:001471* + ID_OUI_FROM_DATABASE=Eastern Asia Technology Limited + +OUI:00146A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001463* + ID_OUI_FROM_DATABASE=IDCS N.V. + +OUI:001465* + ID_OUI_FROM_DATABASE=Novo Nordisk A/S + +OUI:001464* + ID_OUI_FROM_DATABASE=Cryptosoft + +OUI:00145E* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:001457* + ID_OUI_FROM_DATABASE=T-VIPS AS + +OUI:001452* + ID_OUI_FROM_DATABASE=CALCULEX,INC. + +OUI:001592* + ID_OUI_FROM_DATABASE=Facom UK Ltd (Melksham) + +OUI:00158B* + ID_OUI_FROM_DATABASE=Park Air Systems Ltd + +OUI:001584* + ID_OUI_FROM_DATABASE=Schenck Process GmbH + +OUI:00157F* + ID_OUI_FROM_DATABASE=ChuanG International Holding CO.,LTD. + +OUI:00157A* + ID_OUI_FROM_DATABASE=Telefin S.p.A. + +OUI:001575* + ID_OUI_FROM_DATABASE=Nevis Networks Inc. + +OUI:00156E* + ID_OUI_FROM_DATABASE=A. W. Communication Systems Ltd + +OUI:001567* + ID_OUI_FROM_DATABASE=RADWIN Inc. + +OUI:001569* + ID_OUI_FROM_DATABASE=PECO II, Inc. + +OUI:001568* + ID_OUI_FROM_DATABASE=Dilithium Networks + +OUI:001562* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001503* + ID_OUI_FROM_DATABASE=PROFIcomms s.r.o. + +OUI:001505* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:001504* + ID_OUI_FROM_DATABASE=GAME PLUS CO., LTD. + +OUI:0014FE* + ID_OUI_FROM_DATABASE=Artech Electronics + +OUI:0014F7* + ID_OUI_FROM_DATABASE=CREVIS Co., LTD + +OUI:0014F2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0014EB* + ID_OUI_FROM_DATABASE=AwarePoint Corporation + +OUI:0014E1* + ID_OUI_FROM_DATABASE=Data Display AG + +OUI:00155B* + ID_OUI_FROM_DATABASE=Sampo Corporation + +OUI:00154F* + ID_OUI_FROM_DATABASE=one RF Technology + +OUI:001546* + ID_OUI_FROM_DATABASE=ITG Worldwide Sdn Bhd + +OUI:00153F* + ID_OUI_FROM_DATABASE=Alcatel Alenia Space Italia + +OUI:001541* + ID_OUI_FROM_DATABASE=StrataLight Communications, Inc. + +OUI:00153A* + ID_OUI_FROM_DATABASE=Shenzhen Syscan Technology Co.,Ltd. + +OUI:0015BF* + ID_OUI_FROM_DATABASE=technicob + +OUI:0015B4* + ID_OUI_FROM_DATABASE=Polymap Wireless LLC + +OUI:0015AA* + ID_OUI_FROM_DATABASE=Rextechnik International Co., + +OUI:0015A5* + ID_OUI_FROM_DATABASE=DCI Co., Ltd. + +OUI:00159E* + ID_OUI_FROM_DATABASE=Mad Catz Interactive Inc + +OUI:001597* + ID_OUI_FROM_DATABASE=AETA AUDIO SYSTEMS + +OUI:00149E* + ID_OUI_FROM_DATABASE=UbONE Co., Ltd + +OUI:001499* + ID_OUI_FROM_DATABASE=Helicomm Inc + +OUI:001492* + ID_OUI_FROM_DATABASE=Liteon, Mobile Media Solution SBU + +OUI:00148B* + ID_OUI_FROM_DATABASE=Globo Electronic GmbH & Co. KG + +OUI:00148D* + ID_OUI_FROM_DATABASE=Cubic Defense Simulation Systems + +OUI:001486* + ID_OUI_FROM_DATABASE=Echo Digital Audio Corporation + +OUI:00147D* + ID_OUI_FROM_DATABASE=Aeon Digital International + +OUI:001533* + ID_OUI_FROM_DATABASE=NADAM.CO.,LTD + +OUI:00152E* + ID_OUI_FROM_DATABASE=PacketHop, Inc. + +OUI:001527* + ID_OUI_FROM_DATABASE=Balboa Instruments + +OUI:001520* + ID_OUI_FROM_DATABASE=Radiocrafts AS + +OUI:00151B* + ID_OUI_FROM_DATABASE=Isilon Systems Inc. + +OUI:001516* + ID_OUI_FROM_DATABASE=URIEL SYSTEMS INC. + +OUI:001511* + ID_OUI_FROM_DATABASE=Data Center Systems + +OUI:00150A* + ID_OUI_FROM_DATABASE=Sonoa Systems, Inc + +OUI:00131F* + ID_OUI_FROM_DATABASE=NxtPhase T&D, Corp. + +OUI:001318* + ID_OUI_FROM_DATABASE=DGSTATION Co., Ltd. + +OUI:00130C* + ID_OUI_FROM_DATABASE=HF System Corporation + +OUI:001313* + ID_OUI_FROM_DATABASE=GuangZhou Post & Telecom Equipment ltd + +OUI:001354* + ID_OUI_FROM_DATABASE=Zcomax Technologies, Inc. + +OUI:001358* + ID_OUI_FROM_DATABASE=Realm Systems, Inc. + +OUI:00135D* + ID_OUI_FROM_DATABASE=NTTPC Communications, Inc. + +OUI:00134F* + ID_OUI_FROM_DATABASE=Tranzeo Wireless Technologies Inc. + +OUI:001348* + ID_OUI_FROM_DATABASE=Artila Electronics Co., Ltd. + +OUI:001342* + ID_OUI_FROM_DATABASE=Vision Research, Inc. + +OUI:00133C* + ID_OUI_FROM_DATABASE=QUINTRON SYSTEMS INC. + +OUI:001341* + ID_OUI_FROM_DATABASE=Shandong New Beiyang Information Technology Co.,Ltd + +OUI:001329* + ID_OUI_FROM_DATABASE=VSST Co., LTD + +OUI:001330* + ID_OUI_FROM_DATABASE=EURO PROTECTION SURVEILLANCE + +OUI:001335* + ID_OUI_FROM_DATABASE=VS Industry Berhad + +OUI:00132F* + ID_OUI_FROM_DATABASE=Interactek + +OUI:0012C4* + ID_OUI_FROM_DATABASE=Viseon, Inc. + +OUI:0012D0* + ID_OUI_FROM_DATABASE=Gossen-Metrawatt-GmbH + +OUI:0012CA* + ID_OUI_FROM_DATABASE=Mechatronic Brick Aps + +OUI:0012BA* + ID_OUI_FROM_DATABASE=FSI Systems, Inc. + +OUI:0012AE* + ID_OUI_FROM_DATABASE=HLS HARD-LINE Solutions Inc. + +OUI:0012B3* + ID_OUI_FROM_DATABASE=Advance Wireless Technology Corp. + +OUI:0012AD* + ID_OUI_FROM_DATABASE=IDS GmbH + +OUI:00144D* + ID_OUI_FROM_DATABASE=Intelligent Systems + +OUI:001441* + ID_OUI_FROM_DATABASE=Innovation Sound Technology Co., LTD. + +OUI:001448* + ID_OUI_FROM_DATABASE=Inventec Multimedia & Telecom Corporation + +OUI:00143A* + ID_OUI_FROM_DATABASE=RAYTALK INTERNATIONAL SRL + +OUI:001435* + ID_OUI_FROM_DATABASE=CityCom Corp. + +OUI:00142E* + ID_OUI_FROM_DATABASE=77 Elektronika Kft. + +OUI:001429* + ID_OUI_FROM_DATABASE=V Center Technologies Co., Ltd. + +OUI:001427* + ID_OUI_FROM_DATABASE=JazzMutant + +OUI:00141E* + ID_OUI_FROM_DATABASE=P.A. Semi, Inc. + +OUI:0012F9* + ID_OUI_FROM_DATABASE=URYU SEISAKU, LTD. + +OUI:001300* + ID_OUI_FROM_DATABASE=IT-FACTORY, INC. + +OUI:001305* + ID_OUI_FROM_DATABASE=Epicom, Inc. + +OUI:001306* + ID_OUI_FROM_DATABASE=Always On Wireless + +OUI:0012F4* + ID_OUI_FROM_DATABASE=Belco International Co.,Ltd. + +OUI:0012EF* + ID_OUI_FROM_DATABASE=OneAccess SA + +OUI:0012EA* + ID_OUI_FROM_DATABASE=Trane + +OUI:0012E9* + ID_OUI_FROM_DATABASE=Abbey Systems Ltd + +OUI:0012DC* + ID_OUI_FROM_DATABASE=SunCorp Industrial Limited + +OUI:0012E3* + ID_OUI_FROM_DATABASE=Agat-RT, Ltd. + +OUI:0012D7* + ID_OUI_FROM_DATABASE=Invento Networks, Inc. + +OUI:0013F0* + ID_OUI_FROM_DATABASE=Wavefront Semiconductor + +OUI:0013EB* + ID_OUI_FROM_DATABASE=Sysmaster Corporation + +OUI:0013E6* + ID_OUI_FROM_DATABASE=Technolution + +OUI:0013DF* + ID_OUI_FROM_DATABASE=Ryvor Corp. + +OUI:0013D9* + ID_OUI_FROM_DATABASE=Matrix Product Development, Inc. + +OUI:0013DA* + ID_OUI_FROM_DATABASE=Diskware Co., Ltd + +OUI:0013CD* + ID_OUI_FROM_DATABASE=MTI co. LTD + +OUI:0013D3* + ID_OUI_FROM_DATABASE=MICRO-STAR INTERNATIONAL CO., LTD. + +OUI:0013C1* + ID_OUI_FROM_DATABASE=Asoka USA Corporation + +OUI:0013BC* + ID_OUI_FROM_DATABASE=Artimi Ltd + +OUI:0013B7* + ID_OUI_FROM_DATABASE=Scantech ID + +OUI:0013AB* + ID_OUI_FROM_DATABASE=Telemotive AG + +OUI:0013B2* + ID_OUI_FROM_DATABASE=Carallon Limited + +OUI:0013B1* + ID_OUI_FROM_DATABASE=Intelligent Control Systems (Asia) Pte Ltd + +OUI:0013A4* + ID_OUI_FROM_DATABASE=KeyEye Communications + +OUI:00139F* + ID_OUI_FROM_DATABASE=Electronics Design Services, Co., Ltd. + +OUI:001398* + ID_OUI_FROM_DATABASE=TrafficSim Co.,Ltd + +OUI:001392* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:00138C* + ID_OUI_FROM_DATABASE=Kumyoung.Co.Ltd + +OUI:001391* + ID_OUI_FROM_DATABASE=OUEN CO.,LTD. + +OUI:00137C* + ID_OUI_FROM_DATABASE=Kaicom co., Ltd. + +OUI:001383* + ID_OUI_FROM_DATABASE=Application Technologies and Engineering Research Laboratory + +OUI:001364* + ID_OUI_FROM_DATABASE=Paradigm Technology Inc.. + +OUI:001369* + ID_OUI_FROM_DATABASE=Honda Electron Co., LED. + +OUI:00136A* + ID_OUI_FROM_DATABASE=Hach Lange Sarl + +OUI:001418* + ID_OUI_FROM_DATABASE=C4Line + +OUI:00141D* + ID_OUI_FROM_DATABASE=LTi DRIVES GmbH + +OUI:001411* + ID_OUI_FROM_DATABASE=Deutschmann Automation GmbH & Co. KG + +OUI:004501* + ID_OUI_FROM_DATABASE=Versus Technology, Inc. + +OUI:001403* + ID_OUI_FROM_DATABASE=Renasis, LLC + +OUI:0013FC* + ID_OUI_FROM_DATABASE=SiCortex, Inc + +OUI:0013F5* + ID_OUI_FROM_DATABASE=Akimbi Systems + +OUI:0013F6* + ID_OUI_FROM_DATABASE=Cintech + +OUI:001286* + ID_OUI_FROM_DATABASE=ENDEVCO CORP + +OUI:00127F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001278* + ID_OUI_FROM_DATABASE=International Bar Code + +OUI:001273* + ID_OUI_FROM_DATABASE=Stoke Inc + +OUI:001266* + ID_OUI_FROM_DATABASE=Swisscom Hospitality Services SA + +OUI:001265* + ID_OUI_FROM_DATABASE=Enerdyne Technologies, Inc. + +OUI:00125B* + ID_OUI_FROM_DATABASE=KAIMEI ELECTRONI + +OUI:0011D2* + ID_OUI_FROM_DATABASE=Perception Digital Ltd + +OUI:0011D7* + ID_OUI_FROM_DATABASE=eWerks Inc + +OUI:0011D1* + ID_OUI_FROM_DATABASE=Soft Imaging System GmbH + +OUI:0011C2* + ID_OUI_FROM_DATABASE=United Fiber Optic Communication + +OUI:0011CB* + ID_OUI_FROM_DATABASE=Jacobsons AB + +OUI:0011BB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0011BC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0011AA* + ID_OUI_FROM_DATABASE=Uniclass Technology, Co., LTD + +OUI:0011AF* + ID_OUI_FROM_DATABASE=Medialink-i,Inc + +OUI:001200* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0011FB* + ID_OUI_FROM_DATABASE=Heidelberg Engineering GmbH + +OUI:0011F6* + ID_OUI_FROM_DATABASE=Asia Pacific Microsystems , Inc. + +OUI:0011F1* + ID_OUI_FROM_DATABASE=QinetiQ Ltd + +OUI:0011EA* + ID_OUI_FROM_DATABASE=IWICS Inc. + +OUI:0011E3* + ID_OUI_FROM_DATABASE=Thomson, Inc. + +OUI:0011DE* + ID_OUI_FROM_DATABASE=EURILOGIC + +OUI:0011E4* + ID_OUI_FROM_DATABASE=Danelec Electronics A/S + +OUI:001230* + ID_OUI_FROM_DATABASE=Picaso Infocommunication CO., LTD. + +OUI:001226* + ID_OUI_FROM_DATABASE=Japan Direx Corporation + +OUI:001220* + ID_OUI_FROM_DATABASE=Cadco Systems + +OUI:00121A* + ID_OUI_FROM_DATABASE=Techno Soft Systemnics Inc. + +OUI:00121F* + ID_OUI_FROM_DATABASE=Harding Instruments + +OUI:001213* + ID_OUI_FROM_DATABASE=Metrohm AG + +OUI:00120D* + ID_OUI_FROM_DATABASE=Advanced Telecommunication Technologies, Inc. + +OUI:001207* + ID_OUI_FROM_DATABASE=Head Strong International Limited + +OUI:00120E* + ID_OUI_FROM_DATABASE=AboCom + +OUI:00117A* + ID_OUI_FROM_DATABASE=Singim International Corp. + +OUI:001173* + ID_OUI_FROM_DATABASE=SMART Storage Systems + +OUI:001167* + ID_OUI_FROM_DATABASE=Integrated System Solution Corp. + +OUI:00116D* + ID_OUI_FROM_DATABASE=American Time and Signal + +OUI:001163* + ID_OUI_FROM_DATABASE=SYSTEM SPA DEPT. ELECTRONICS + +OUI:001156* + ID_OUI_FROM_DATABASE=Pharos Systems NZ + +OUI:00115D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0012A7* + ID_OUI_FROM_DATABASE=ISR TECHNOLOGIES Inc + +OUI:0012A0* + ID_OUI_FROM_DATABASE=NeoMeridian Sdn Bhd + +OUI:00129B* + ID_OUI_FROM_DATABASE=E2S Electronic Engineering Solutions, S.L. + +OUI:001294* + ID_OUI_FROM_DATABASE=SUMITOMO ELECTRIC DEVICE INNOVATIONS, INC + +OUI:00128B* + ID_OUI_FROM_DATABASE=Sensory Networks Inc + +OUI:001285* + ID_OUI_FROM_DATABASE=Gizmondo Europe Ltd + +OUI:0011A9* + ID_OUI_FROM_DATABASE=MOIMSTONE Co., LTD + +OUI:0011A3* + ID_OUI_FROM_DATABASE=LanReady Technologies Inc. + +OUI:001197* + ID_OUI_FROM_DATABASE=Monitoring Technologies Limited + +OUI:00119C* + ID_OUI_FROM_DATABASE=EP&T Energy + +OUI:00118D* + ID_OUI_FROM_DATABASE=Hanchang System Corp. + +OUI:001192* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001186* + ID_OUI_FROM_DATABASE=Prime Systems, Inc. + +OUI:00117F* + ID_OUI_FROM_DATABASE=Neotune Information Technology Corporation,.LTD + +OUI:001260* + ID_OUI_FROM_DATABASE=Stanton Magnetics,inc. + +OUI:001256* + ID_OUI_FROM_DATABASE=LG INFORMATION & COMM. + +OUI:00124F* + ID_OUI_FROM_DATABASE=Pentair Thermal Management + +OUI:00124A* + ID_OUI_FROM_DATABASE=Dedicated Devices, Inc. + +OUI:001249* + ID_OUI_FROM_DATABASE=Delta Elettronica S.p.A. + +OUI:001243* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00123C* + ID_OUI_FROM_DATABASE=Second Rule LLC + +OUI:001148* + ID_OUI_FROM_DATABASE=Prolon Control Systems + +OUI:00114D* + ID_OUI_FROM_DATABASE=Atsumi Electric Co.,LTD. + +OUI:00114E* + ID_OUI_FROM_DATABASE=690885 Ontario Inc. + +OUI:001141* + ID_OUI_FROM_DATABASE=GoodMan Corporation + +OUI:00113B* + ID_OUI_FROM_DATABASE=Micronet Communications Inc. + +OUI:001135* + ID_OUI_FROM_DATABASE=Grandeye Ltd + +OUI:001126* + ID_OUI_FROM_DATABASE=Venstar Inc. + +OUI:000EB9* + ID_OUI_FROM_DATABASE=HASHIMOTO Electronics Industry Co.,Ltd. + +OUI:000EBA* + ID_OUI_FROM_DATABASE=HANMI SEMICONDUCTOR CO., LTD. + +OUI:000EAC* + ID_OUI_FROM_DATABASE=MINTRON ENTERPRISE CO., LTD. + +OUI:000EA0* + ID_OUI_FROM_DATABASE=NetKlass Technology Inc. + +OUI:000EA7* + ID_OUI_FROM_DATABASE=Endace Technology + +OUI:000E9A* + ID_OUI_FROM_DATABASE=BOE TECHNOLOGY GROUP CO.,LTD + +OUI:000E99* + ID_OUI_FROM_DATABASE=Spectrum Digital, Inc + +OUI:00112B* + ID_OUI_FROM_DATABASE=NetModule AG + +OUI:001120* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:001125* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:001119* + ID_OUI_FROM_DATABASE=Solteras, Inc. + +OUI:001113* + ID_OUI_FROM_DATABASE=Fraunhofer FOKUS + +OUI:001106* + ID_OUI_FROM_DATABASE=Siemens NV (Belgium) + +OUI:00110D* + ID_OUI_FROM_DATABASE=SANBlaze Technology, Inc. + +OUI:001101* + ID_OUI_FROM_DATABASE=CET Technologies Pte Ltd + +OUI:000FB3* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:000FA6* + ID_OUI_FROM_DATABASE=S2 Security Corporation + +OUI:000FAD* + ID_OUI_FROM_DATABASE=FMN communications GmbH + +OUI:000F9B* + ID_OUI_FROM_DATABASE=Ross Video Limited + +OUI:000F9E* + ID_OUI_FROM_DATABASE=Murrelektronik GmbH + +OUI:000FA1* + ID_OUI_FROM_DATABASE=Gigabit Systems Inc. + +OUI:000F95* + ID_OUI_FROM_DATABASE=ELECOM Co.,LTD Laneed Division + +OUI:000F96* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:000F8F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000F88* + ID_OUI_FROM_DATABASE=AMETEK, Inc. + +OUI:000F83* + ID_OUI_FROM_DATABASE=Brainium Technologies Inc. + +OUI:000F51* + ID_OUI_FROM_DATABASE=Azul Systems, Inc. + +OUI:000F44* + ID_OUI_FROM_DATABASE=Tivella Inc. + +OUI:000F43* + ID_OUI_FROM_DATABASE=Wasabi Systems Inc. + +OUI:000F4A* + ID_OUI_FROM_DATABASE=Kyushu-kyohan co.,ltd + +OUI:000F3E* + ID_OUI_FROM_DATABASE=CardioNet, Inc + +OUI:000F3A* + ID_OUI_FROM_DATABASE=HISHARP + +OUI:000F30* + ID_OUI_FROM_DATABASE=Raza Microelectronics Inc + +OUI:000F2F* + ID_OUI_FROM_DATABASE=W-LINX TECHNOLOGY CO., LTD. + +OUI:000F36* + ID_OUI_FROM_DATABASE=Accurate Techhnologies, Inc. + +OUI:000F2A* + ID_OUI_FROM_DATABASE=Cableware Electronics + +OUI:000F76* + ID_OUI_FROM_DATABASE=Digital Keystone, Inc. + +OUI:000F70* + ID_OUI_FROM_DATABASE=Wintec Industries, inc. + +OUI:000F75* + ID_OUI_FROM_DATABASE=First Silicon Solutions + +OUI:000F7C* + ID_OUI_FROM_DATABASE=ACTi Corporation + +OUI:000F69* + ID_OUI_FROM_DATABASE=SEW Eurodrive GmbH & Co. KG + +OUI:000F63* + ID_OUI_FROM_DATABASE=Obzerv Technologies + +OUI:000F64* + ID_OUI_FROM_DATABASE=D&R Electronica Weesp BV + +OUI:000F5D* + ID_OUI_FROM_DATABASE=Genexis BV + +OUI:000F56* + ID_OUI_FROM_DATABASE=Continuum Photonics Inc + +OUI:000EEB* + ID_OUI_FROM_DATABASE=Sandmartin(zhong shan)Electronics Co.,Ltd + +OUI:000EEC* + ID_OUI_FROM_DATABASE=Orban + +OUI:000EF1* + ID_OUI_FROM_DATABASE=EZQUEST INC. + +OUI:000EDE* + ID_OUI_FROM_DATABASE=REMEC, Inc. + +OUI:000EE5* + ID_OUI_FROM_DATABASE=bitWallet, Inc. + +OUI:000ECC* + ID_OUI_FROM_DATABASE=Tableau, LLC + +OUI:000ED9* + ID_OUI_FROM_DATABASE=Aksys, Ltd. + +OUI:000ECB* + ID_OUI_FROM_DATABASE=VineSys Technology + +OUI:000ED2* + ID_OUI_FROM_DATABASE=Filtronic plc + +OUI:000EBF* + ID_OUI_FROM_DATABASE=Remsdaq Limited + +OUI:000EC6* + ID_OUI_FROM_DATABASE=ASIX ELECTRONICS CORP. + +OUI:000F23* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000F1D* + ID_OUI_FROM_DATABASE=Cosmo Techs Co., Ltd. + +OUI:000F10* + ID_OUI_FROM_DATABASE=RDM Corporation + +OUI:000F17* + ID_OUI_FROM_DATABASE=Insta Elektro GmbH + +OUI:000F1E* + ID_OUI_FROM_DATABASE=Chengdu KT Electric Co.of High & New Technology + +OUI:000F0B* + ID_OUI_FROM_DATABASE=Kentima Technologies AB + +OUI:000F04* + ID_OUI_FROM_DATABASE=cim-usa inc + +OUI:000EFE* + ID_OUI_FROM_DATABASE=EndRun Technologies LLC + +OUI:000EF8* + ID_OUI_FROM_DATABASE=SBC ASI + +OUI:000EFD* + ID_OUI_FROM_DATABASE=FUJINON CORPORATION + +OUI:000FFB* + ID_OUI_FROM_DATABASE=Nippon Denso Industry Co., Ltd. + +OUI:000FF8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000FF2* + ID_OUI_FROM_DATABASE=Loud Technologies Inc. + +OUI:000FF7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000FE5* + ID_OUI_FROM_DATABASE=MERCURY SECURITY CORPORATION + +OUI:000FE6* + ID_OUI_FROM_DATABASE=MBTech Systems, Inc. + +OUI:000FEB* + ID_OUI_FROM_DATABASE=Cylon Controls + +OUI:000FDF* + ID_OUI_FROM_DATABASE=SOLOMON Technology Corp. + +OUI:000FD8* + ID_OUI_FROM_DATABASE=Force, Inc. + +OUI:000FD3* + ID_OUI_FROM_DATABASE=Digium + +OUI:000FC6* + ID_OUI_FROM_DATABASE=Eurocom Industries A/S + +OUI:000FC5* + ID_OUI_FROM_DATABASE=KeyMed Ltd + +OUI:000FC0* + ID_OUI_FROM_DATABASE=DELCOMp + +OUI:000FB4* + ID_OUI_FROM_DATABASE=Timespace Technology + +OUI:000FB9* + ID_OUI_FROM_DATABASE=Adaptive Instruments + +OUI:000D4D* + ID_OUI_FROM_DATABASE=Ninelanes + +OUI:000D54* + ID_OUI_FROM_DATABASE=3Com Ltd + +OUI:000D45* + ID_OUI_FROM_DATABASE=Tottori SANYO Electric Co., Ltd. + +OUI:000D48* + ID_OUI_FROM_DATABASE=AEWIN Technologies Co., Ltd. + +OUI:000D40* + ID_OUI_FROM_DATABASE=Verint Loronix Video Solutions + +OUI:000D39* + ID_OUI_FROM_DATABASE=Network Electronics + +OUI:000D33* + ID_OUI_FROM_DATABASE=Prediwave Corp. + +OUI:000D34* + ID_OUI_FROM_DATABASE=Shell International Exploration and Production, Inc. + +OUI:000D2D* + ID_OUI_FROM_DATABASE=NCT Deutschland GmbH + +OUI:000D26* + ID_OUI_FROM_DATABASE=Primagraphics Limited + +OUI:000D21* + ID_OUI_FROM_DATABASE=WISCORE Inc. + +OUI:000D14* + ID_OUI_FROM_DATABASE=Vtech Innovation LP dba Advanced American Telephones + +OUI:000D13* + ID_OUI_FROM_DATABASE=Wilhelm Rutenbeck GmbH&Co.KG + +OUI:000D1A* + ID_OUI_FROM_DATABASE=Mustek System Inc. + +OUI:000D0E* + ID_OUI_FROM_DATABASE=Inqnet Systems, Inc. + +OUI:000D01* + ID_OUI_FROM_DATABASE=P&E Microcomputer Systems, Inc. + +OUI:000D02* + ID_OUI_FROM_DATABASE=NEC Platforms, Ltd. + +OUI:000D07* + ID_OUI_FROM_DATABASE=Calrec Audio Ltd + +OUI:000E8D* + ID_OUI_FROM_DATABASE=Systems in Progress Holding GmbH + +OUI:000E94* + ID_OUI_FROM_DATABASE=Maas International BV + +OUI:000E87* + ID_OUI_FROM_DATABASE=adp Gauselmann GmbH + +OUI:000E81* + ID_OUI_FROM_DATABASE=Devicescape Software, Inc. + +OUI:000E88* + ID_OUI_FROM_DATABASE=VIDEOTRON CORP. + +OUI:000E75* + ID_OUI_FROM_DATABASE=New York Air Brake Corp. + +OUI:000E7A* + ID_OUI_FROM_DATABASE=GemWon Communications Co., Ltd. + +OUI:000E66* + ID_OUI_FROM_DATABASE=Hitachi Industry & Control Solutions, Ltd. + +OUI:000DF6* + ID_OUI_FROM_DATABASE=Technology Thesaurus Corp. + +OUI:000DFD* + ID_OUI_FROM_DATABASE=Huges Hi-Tech Inc., + +OUI:000E02* + ID_OUI_FROM_DATABASE=Advantech AMT Inc. + +OUI:000DEA* + ID_OUI_FROM_DATABASE=Kingtel Telecommunication Corp. + +OUI:000DEF* + ID_OUI_FROM_DATABASE=Soc. Coop. Bilanciai + +OUI:000DDD* + ID_OUI_FROM_DATABASE=Profilo Telra Elektronik Sanayi ve Ticaret. A.Ş + +OUI:000DDE* + ID_OUI_FROM_DATABASE=Joyteck Co., Ltd. + +OUI:000DE3* + ID_OUI_FROM_DATABASE=AT Sweden AB + +OUI:000DD0* + ID_OUI_FROM_DATABASE=TetraTec Instruments GmbH + +OUI:000DD7* + ID_OUI_FROM_DATABASE=Bright + +OUI:000E61* + ID_OUI_FROM_DATABASE=MICROTROL LIMITED + +OUI:000E5A* + ID_OUI_FROM_DATABASE=TELEFIELD inc. + +OUI:000E54* + ID_OUI_FROM_DATABASE=AlphaCell Wireless Ltd. + +OUI:000E4E* + ID_OUI_FROM_DATABASE=Waveplus Technology Co., Ltd. + +OUI:000E53* + ID_OUI_FROM_DATABASE=AV TECH CORPORATION + +OUI:000E47* + ID_OUI_FROM_DATABASE=NCI System Co.,Ltd. + +OUI:000E41* + ID_OUI_FROM_DATABASE=NIHON MECHATRONICS CO.,LTD. + +OUI:000E42* + ID_OUI_FROM_DATABASE=Motic Incoporation Ltd. + +OUI:000E3C* + ID_OUI_FROM_DATABASE=Transact Technologies Inc + +OUI:000E36* + ID_OUI_FROM_DATABASE=HEINESYS, Inc. + +OUI:000DB1* + ID_OUI_FROM_DATABASE=Japan Network Service Co., Ltd. + +OUI:000DA9* + ID_OUI_FROM_DATABASE=T.E.A.M. S.L. + +OUI:000DAC* + ID_OUI_FROM_DATABASE=Japan CBM Corporation + +OUI:000DA4* + ID_OUI_FROM_DATABASE=DOSCH & AMAND SYSTEMS AG + +OUI:000D97* + ID_OUI_FROM_DATABASE=ABB Inc./Tropos + +OUI:000D98* + ID_OUI_FROM_DATABASE=S.W.A.C. Schmitt-Walter Automation Consult GmbH + +OUI:000D8A* + ID_OUI_FROM_DATABASE=Winners Electronics Co., Ltd. + +OUI:000D91* + ID_OUI_FROM_DATABASE=Eclipse (HQ Espana) S.L. + +OUI:000D7F* + ID_OUI_FROM_DATABASE=MIDAS COMMUNICATION TECHNOLOGIES PTE LTD ( Foreign Branch) + +OUI:000D79* + ID_OUI_FROM_DATABASE=Dynamic Solutions Co,.Ltd. + +OUI:000D73* + ID_OUI_FROM_DATABASE=Technical Support, Inc. + +OUI:000D7A* + ID_OUI_FROM_DATABASE=DiGATTO Asia Pacific Pte Ltd + +OUI:000D6C* + ID_OUI_FROM_DATABASE=M-Audio + +OUI:000D5A* + ID_OUI_FROM_DATABASE=Tiesse SpA + +OUI:000D60* + ID_OUI_FROM_DATABASE=IBM Corp + +OUI:000D59* + ID_OUI_FROM_DATABASE=Amity Systems, Inc. + +OUI:000DCB* + ID_OUI_FROM_DATABASE=Petcomkorea Co., Ltd. + +OUI:000DC4* + ID_OUI_FROM_DATABASE=Emcore Corporation + +OUI:000DBE* + ID_OUI_FROM_DATABASE=Bel Fuse Europe Ltd.,UK + +OUI:000DB8* + ID_OUI_FROM_DATABASE=SCHILLER AG + +OUI:000DBD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000E30* + ID_OUI_FROM_DATABASE=AERAS Networks, Inc. + +OUI:000E29* + ID_OUI_FROM_DATABASE=Shester Communications Inc + +OUI:000E23* + ID_OUI_FROM_DATABASE=Incipient, Inc. + +OUI:000E24* + ID_OUI_FROM_DATABASE=Huwell Technology Inc. + +OUI:000E16* + ID_OUI_FROM_DATABASE=SouthWing S.L. + +OUI:000E1D* + ID_OUI_FROM_DATABASE=ARION Technology Inc. + +OUI:000E09* + ID_OUI_FROM_DATABASE=Shenzhen Coship Software Co.,LTD. + +OUI:000E11* + ID_OUI_FROM_DATABASE=BDT Büro und Datentechnik GmbH & Co.KG + +OUI:000BC8* + ID_OUI_FROM_DATABASE=AirFlow Networks + +OUI:000BCF* + ID_OUI_FROM_DATABASE=AGFA NDT INC. + +OUI:000BC3* + ID_OUI_FROM_DATABASE=Multiplex, Inc. + +OUI:000BBC* + ID_OUI_FROM_DATABASE=En Garde Systems, Inc. + +OUI:000BC1* + ID_OUI_FROM_DATABASE=Bay Microsystems, Inc. + +OUI:000BB0* + ID_OUI_FROM_DATABASE=Sysnet Telematica srl + +OUI:000BB5* + ID_OUI_FROM_DATABASE=nStor Technologies, Inc. + +OUI:000BA6* + ID_OUI_FROM_DATABASE=Miyakawa Electric Works Ltd. + +OUI:000BAB* + ID_OUI_FROM_DATABASE=Advantech Technology (CHINA) Co., Ltd. + +OUI:000B99* + ID_OUI_FROM_DATABASE=SensAble Technologies, Inc. + +OUI:000B9A* + ID_OUI_FROM_DATABASE=Shanghai Ulink Telecom Equipment Co. Ltd. + +OUI:000B9F* + ID_OUI_FROM_DATABASE=Neue ELSA GmbH + +OUI:000B94* + ID_OUI_FROM_DATABASE=Digital Monitoring Products, Inc. + +OUI:000C1D* + ID_OUI_FROM_DATABASE=Mettler & Fuchs AG + +OUI:000C22* + ID_OUI_FROM_DATABASE=Double D Electronics Ltd + +OUI:000C0F* + ID_OUI_FROM_DATABASE=Techno-One Co., Ltd + +OUI:000C16* + ID_OUI_FROM_DATABASE=Concorde Microsystems Inc. + +OUI:000C0A* + ID_OUI_FROM_DATABASE=Guangdong Province Electronic Technology Research Institute + +OUI:000BFD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000BF7* + ID_OUI_FROM_DATABASE=NIDEK CO.,LTD + +OUI:000BFC* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000BFE* + ID_OUI_FROM_DATABASE=CASTEL Broadband Limited + +OUI:000C03* + ID_OUI_FROM_DATABASE=HDMI Licensing, LLC + +OUI:000CA4* + ID_OUI_FROM_DATABASE=Prompttec Product Management GmbH + +OUI:000CAB* + ID_OUI_FROM_DATABASE=COMMEND International + +OUI:000C98* + ID_OUI_FROM_DATABASE=LETEK Communications Inc. + +OUI:000C9D* + ID_OUI_FROM_DATABASE=UbeeAirWalk, Inc. + +OUI:000C9F* + ID_OUI_FROM_DATABASE=NKE Corporation + +OUI:000C8C* + ID_OUI_FROM_DATABASE=KODICOM CO.,LTD. + +OUI:000C91* + ID_OUI_FROM_DATABASE=Riverhead Networks Inc. + +OUI:000C80* + ID_OUI_FROM_DATABASE=Opelcomm Inc. + +OUI:000C85* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000CD0* + ID_OUI_FROM_DATABASE=Symetrix + +OUI:000CD5* + ID_OUI_FROM_DATABASE=Passave Inc. + +OUI:000CDC* + ID_OUI_FROM_DATABASE=BECS Technology, Inc + +OUI:000CC9* + ID_OUI_FROM_DATABASE=ILWOO DATA & TECHNOLOGY CO.,LTD + +OUI:000CB0* + ID_OUI_FROM_DATABASE=Star Semiconductor Corporation + +OUI:000CB6* + ID_OUI_FROM_DATABASE=NANJING SEU MOBILE & INTERNET TECHNOLOGY CO.,LTD + +OUI:000CBD* + ID_OUI_FROM_DATABASE=Interface Masters, Inc + +OUI:000CC2* + ID_OUI_FROM_DATABASE=ControlNet (India) Private Limited + +OUI:000CAF* + ID_OUI_FROM_DATABASE=TRI TERM CO.,LTD. + +OUI:000C71* + ID_OUI_FROM_DATABASE=Wybron, Inc + +OUI:000C78* + ID_OUI_FROM_DATABASE=In-Tech Electronics Limited + +OUI:000C7D* + ID_OUI_FROM_DATABASE=TEIKOKU ELECTRIC MFG. CO., LTD + +OUI:000C65* + ID_OUI_FROM_DATABASE=Sunin Telecom + +OUI:000C6A* + ID_OUI_FROM_DATABASE=MBARI + +OUI:000C6C* + ID_OUI_FROM_DATABASE=Elgato Systems LLC + +OUI:000B88* + ID_OUI_FROM_DATABASE=Vidisco ltd. + +OUI:000B8D* + ID_OUI_FROM_DATABASE=Avvio Networks + +OUI:000B7B* + ID_OUI_FROM_DATABASE=Test-Um Inc. + +OUI:000B7A* + ID_OUI_FROM_DATABASE=L-3 Linkabit + +OUI:000B7C* + ID_OUI_FROM_DATABASE=Telex Communications + +OUI:000B81* + ID_OUI_FROM_DATABASE=Kaparel Corporation + +OUI:000B6E* + ID_OUI_FROM_DATABASE=Neff Instrument Corp. + +OUI:000B75* + ID_OUI_FROM_DATABASE=Iosoft Ltd. + +OUI:000B69* + ID_OUI_FROM_DATABASE=Franke Finland Oy + +OUI:0091D6* + ID_OUI_FROM_DATABASE=Crystal Group, Inc. + +OUI:000B62* + ID_OUI_FROM_DATABASE=ib-mohnen KG + +OUI:000B59* + ID_OUI_FROM_DATABASE=ScriptPro, LLC + +OUI:000C52* + ID_OUI_FROM_DATABASE=Roll Systems Inc. + +OUI:000C57* + ID_OUI_FROM_DATABASE=MACKIE Engineering Services Belgium BVBA + +OUI:000C59* + ID_OUI_FROM_DATABASE=Indyme Electronics, Inc. + +OUI:000C5E* + ID_OUI_FROM_DATABASE=Calypso Medical + +OUI:000C4B* + ID_OUI_FROM_DATABASE=Cheops Elektronik + +OUI:000C46* + ID_OUI_FROM_DATABASE=Allied Telesyn Inc. + +OUI:000C3D* + ID_OUI_FROM_DATABASE=Glsystech Co., Ltd. + +OUI:000C33* + ID_OUI_FROM_DATABASE=Compucase Enterprise Co. Ltd. + +OUI:000C36* + ID_OUI_FROM_DATABASE=SHARP TAKAYA ELECTRONICS INDUSTRY CO.,LTD. + +OUI:000C2C* + ID_OUI_FROM_DATABASE=Enwiser Inc. + +OUI:000CFB* + ID_OUI_FROM_DATABASE=Korea Network Systems + +OUI:000CEF* + ID_OUI_FROM_DATABASE=Open Networks Engineering Ltd + +OUI:000CF4* + ID_OUI_FROM_DATABASE=AKATSUKI ELECTRIC MFG.CO.,LTD. + +OUI:000CE8* + ID_OUI_FROM_DATABASE=GuangZhou AnJuBao Co., Ltd + +OUI:000CE1* + ID_OUI_FROM_DATABASE=The Open Group + +OUI:000CCF* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000BEB* + ID_OUI_FROM_DATABASE=Systegra AG + +OUI:000BF0* + ID_OUI_FROM_DATABASE=MoTEX Products Co., Ltd. + +OUI:000BDD* + ID_OUI_FROM_DATABASE=TOHOKU RICOH Co., LTD. + +OUI:000BE4* + ID_OUI_FROM_DATABASE=Hosiden Corporation + +OUI:000BD8* + ID_OUI_FROM_DATABASE=Industrial Scientific Corp. + +OUI:000BD4* + ID_OUI_FROM_DATABASE=Beijing Wise Technology & Science Development Co.Ltd + +OUI:000A1D* + ID_OUI_FROM_DATABASE=Optical Communications Products Inc. + +OUI:000A1F* + ID_OUI_FROM_DATABASE=ART WARE Telecommunication Co., Ltd. + +OUI:000A24* + ID_OUI_FROM_DATABASE=Octave Communications + +OUI:000A18* + ID_OUI_FROM_DATABASE=Vichel Inc. + +OUI:000A0C* + ID_OUI_FROM_DATABASE=Scientific Research Corporation + +OUI:000A11* + ID_OUI_FROM_DATABASE=ExPet Technologies, Inc + +OUI:0009F8* + ID_OUI_FROM_DATABASE=UNIMO TECHNOLOGY CO., LTD. + +OUI:0009FB* + ID_OUI_FROM_DATABASE=Philips Patient Monitoring + +OUI:000A02* + ID_OUI_FROM_DATABASE=ANNSO CO., LTD. + +OUI:0009EB* + ID_OUI_FROM_DATABASE=HuMANDATA LTD. + +OUI:0009EC* + ID_OUI_FROM_DATABASE=Daktronics, Inc. + +OUI:0009F1* + ID_OUI_FROM_DATABASE=Yamaki Electric Corporation + +OUI:0009E5* + ID_OUI_FROM_DATABASE=Hottinger Baldwin Messtechnik GmbH + +OUI:0009D9* + ID_OUI_FROM_DATABASE=Neoscale Systems, Inc + +OUI:0009DE* + ID_OUI_FROM_DATABASE=Samjin Information & Communications Co., Ltd. + +OUI:0009CC* + ID_OUI_FROM_DATABASE=Moog GmbH + +OUI:0009C6* + ID_OUI_FROM_DATABASE=Visionics Corporation + +OUI:0009CB* + ID_OUI_FROM_DATABASE=HBrain + +OUI:0009D2* + ID_OUI_FROM_DATABASE=Mai Logic Inc. + +OUI:0009BE* + ID_OUI_FROM_DATABASE=Mamiya-OP Co.,Ltd. + +OUI:0009C2* + ID_OUI_FROM_DATABASE=Onity, Inc. + +OUI:000B51* + ID_OUI_FROM_DATABASE=Micetek International Inc. + +OUI:000B54* + ID_OUI_FROM_DATABASE=BiTMICRO Networks, Inc. + +OUI:000B45* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000B4C* + ID_OUI_FROM_DATABASE=Clarion (M) Sdn Bhd + +OUI:000B40* + ID_OUI_FROM_DATABASE=Oclaro + +OUI:000B32* + ID_OUI_FROM_DATABASE=VORMETRIC, INC. + +OUI:000B39* + ID_OUI_FROM_DATABASE=Keisoku Giken Co.,Ltd. + +OUI:000B3E* + ID_OUI_FROM_DATABASE=BittWare, Inc + +OUI:000B26* + ID_OUI_FROM_DATABASE=Wetek Corporation + +OUI:000B2B* + ID_OUI_FROM_DATABASE=HOSTNET CORPORATION + +OUI:000B2D* + ID_OUI_FROM_DATABASE=Danfoss Inc. + +OUI:000ABB* + ID_OUI_FROM_DATABASE=Taiwan Secom Co,. Ltd + +OUI:000AC7* + ID_OUI_FROM_DATABASE=Unication Group + +OUI:000AAF* + ID_OUI_FROM_DATABASE=Pipal Systems + +OUI:000AB6* + ID_OUI_FROM_DATABASE=COMPUNETIX, INC + +OUI:000AA3* + ID_OUI_FROM_DATABASE=SHIMAFUJI ELECTRIC CO.,LTD. + +OUI:000AA8* + ID_OUI_FROM_DATABASE=ePipe Pty. Ltd. + +OUI:000AAA* + ID_OUI_FROM_DATABASE=AltiGen Communications Inc. + +OUI:000A90* + ID_OUI_FROM_DATABASE=Bayside Interactive, Inc. + +OUI:000A9C* + ID_OUI_FROM_DATABASE=Server Technology, Inc. + +OUI:000A96* + ID_OUI_FROM_DATABASE=MEWTEL TECHNOLOGY INC. + +OUI:000A81* + ID_OUI_FROM_DATABASE=TEIMA Audiotex S.L. + +OUI:000A83* + ID_OUI_FROM_DATABASE=SALTO SYSTEMS S.L. + +OUI:000A88* + ID_OUI_FROM_DATABASE=InCypher S.A. + +OUI:000A7C* + ID_OUI_FROM_DATABASE=Tecton Ltd + +OUI:000A70* + ID_OUI_FROM_DATABASE=MPLS Forum + +OUI:000A75* + ID_OUI_FROM_DATABASE=Caterpillar, Inc + +OUI:000A62* + ID_OUI_FROM_DATABASE=Crinis Networks, Inc. + +OUI:000A64* + ID_OUI_FROM_DATABASE=Eracom Technologies + +OUI:000A69* + ID_OUI_FROM_DATABASE=SUNNY bell Technology Co., Ltd. + +OUI:000A5D* + ID_OUI_FROM_DATABASE=FingerTec Worldwide Sdn Bhd + +OUI:000AF4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000AE8* + ID_OUI_FROM_DATABASE=Cathay Roxus Information Technology Co. LTD + +OUI:000ADA* + ID_OUI_FROM_DATABASE=Vindicator Technologies + +OUI:000ADC* + ID_OUI_FROM_DATABASE=RuggedCom Inc. + +OUI:000AE1* + ID_OUI_FROM_DATABASE=EG Technology + +OUI:000AC9* + ID_OUI_FROM_DATABASE=Zambeel Inc + +OUI:000ACE* + ID_OUI_FROM_DATABASE=RADIANTECH, INC. + +OUI:000AD5* + ID_OUI_FROM_DATABASE=Brainchild Electronic Co., Ltd. + +OUI:000A4F* + ID_OUI_FROM_DATABASE=Brain Boxes Limited + +OUI:000A51* + ID_OUI_FROM_DATABASE=GyroSignal Technology Co., Ltd. + +OUI:000A56* + ID_OUI_FROM_DATABASE=HITACHI Maxell Ltd. + +OUI:000A4A* + ID_OUI_FROM_DATABASE=Targa Systems Ltd. + +OUI:000A37* + ID_OUI_FROM_DATABASE=Procera Networks, Inc. + +OUI:000A3E* + ID_OUI_FROM_DATABASE=EADS Telecom + +OUI:000A43* + ID_OUI_FROM_DATABASE=Chunghwa Telecom Co., Ltd. + +OUI:000A30* + ID_OUI_FROM_DATABASE=Visteon Corporation + +OUI:000A32* + ID_OUI_FROM_DATABASE=Xsido Corporation + +OUI:000A2B* + ID_OUI_FROM_DATABASE=Etherstuff + +OUI:000A29* + ID_OUI_FROM_DATABASE=Pan Dacom Networking AG + +OUI:000B1A* + ID_OUI_FROM_DATABASE=Industrial Defender, Inc. + +OUI:000B1F* + ID_OUI_FROM_DATABASE=I CON Computer Co. + +OUI:000B13* + ID_OUI_FROM_DATABASE=ZETRON INC + +OUI:000B0C* + ID_OUI_FROM_DATABASE=Agile Systems Inc. + +OUI:000B07* + ID_OUI_FROM_DATABASE=Voxpath Networks + +OUI:000AF9* + ID_OUI_FROM_DATABASE=HiConnect, Inc. + +OUI:000AFB* + ID_OUI_FROM_DATABASE=Ambri Limited + +OUI:000B00* + ID_OUI_FROM_DATABASE=FUJIAN START COMPUTER EQUIPMENT CO.,LTD + +OUI:0009B8* + ID_OUI_FROM_DATABASE=Entise Systems + +OUI:0009B7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0009B2* + ID_OUI_FROM_DATABASE=L&F Inc. + +OUI:0009A5* + ID_OUI_FROM_DATABASE=HANSUNG ELETRONIC INDUSTRIES DEVELOPMENT CO., LTD + +OUI:0009A6* + ID_OUI_FROM_DATABASE=Ignis Optics, Inc. + +OUI:0009AB* + ID_OUI_FROM_DATABASE=Netcontrol Oy + +OUI:00099F* + ID_OUI_FROM_DATABASE=VIDEX INC. + +OUI:0007B3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0007AD* + ID_OUI_FROM_DATABASE=Pentacon GmbH Foto-und Feinwerktechnik + +OUI:0007A5* + ID_OUI_FROM_DATABASE=Y.D.K Co. Ltd. + +OUI:00079F* + ID_OUI_FROM_DATABASE=Action Digital Inc. + +OUI:000792* + ID_OUI_FROM_DATABASE=Sütron Electronic GmbH + +OUI:000799* + ID_OUI_FROM_DATABASE=Tipping Point Technologies, Inc. + +OUI:00078C* + ID_OUI_FROM_DATABASE=Elektronikspecialisten i Borlange AB + +OUI:000786* + ID_OUI_FROM_DATABASE=Wireless Networks Inc. + +OUI:000775* + ID_OUI_FROM_DATABASE=Valence Semiconductor, Inc. + +OUI:00077C* + ID_OUI_FROM_DATABASE=Westermo Teleindustri AB + +OUI:000776* + ID_OUI_FROM_DATABASE=Federal APD + +OUI:00077F* + ID_OUI_FROM_DATABASE=J Communications Co., Ltd. + +OUI:000780* + ID_OUI_FROM_DATABASE=Bluegiga Technologies OY + +OUI:000881* + ID_OUI_FROM_DATABASE=DIGITAL HANDS CO.,LTD. + +OUI:02C08C* + ID_OUI_FROM_DATABASE=3COM CORPORATION + +OUI:00087B* + ID_OUI_FROM_DATABASE=RTX Telecom A/S + +OUI:000880* + ID_OUI_FROM_DATABASE=BroadTel Canada Communications inc. + +OUI:00086E* + ID_OUI_FROM_DATABASE=Hyglo AB + +OUI:000868* + ID_OUI_FROM_DATABASE=PurOptix + +OUI:000861* + ID_OUI_FROM_DATABASE=SoftEnergy Co., Ltd. + +OUI:00084F* + ID_OUI_FROM_DATABASE=Qualstar Corporation + +OUI:00085B* + ID_OUI_FROM_DATABASE=Hanbit Electronics Co., Ltd. + +OUI:000855* + ID_OUI_FROM_DATABASE=NASA-Goddard Space Flight Center + +OUI:00084E* + ID_OUI_FROM_DATABASE=DivergeNet, Inc. + +OUI:00085C* + ID_OUI_FROM_DATABASE=Shanghai Dare Technologies Co. Ltd. + +OUI:0007ED* + ID_OUI_FROM_DATABASE=Altera Corporation + +OUI:0007F4* + ID_OUI_FROM_DATABASE=Eletex Co., Ltd. + +OUI:0007E1* + ID_OUI_FROM_DATABASE=WIS Communications Co. Ltd. + +OUI:0007D4* + ID_OUI_FROM_DATABASE=Zhejiang Yutong Network Communication Co Ltd. + +OUI:0007DB* + ID_OUI_FROM_DATABASE=Kirana Networks, Inc. + +OUI:0007D5* + ID_OUI_FROM_DATABASE=3e Technologies Int;., Inc. + +OUI:0005F9* + ID_OUI_FROM_DATABASE=TOA Corporation + +OUI:0007C5* + ID_OUI_FROM_DATABASE=Gcom, Inc. + +OUI:0007CC* + ID_OUI_FROM_DATABASE=Kaba Benzing GmbH + +OUI:0007C6* + ID_OUI_FROM_DATABASE=VDS Vosskuhler GmbH + +OUI:0007B9* + ID_OUI_FROM_DATABASE=Ginganet Corporation + +OUI:0007BF* + ID_OUI_FROM_DATABASE=Armillaire Technologies, Inc. + +OUI:00047F* + ID_OUI_FROM_DATABASE=Chr. Mayr GmbH & Co. KG + +OUI:000961* + ID_OUI_FROM_DATABASE=Switchgear and Instrumentation Ltd + +OUI:00095A* + ID_OUI_FROM_DATABASE=RACEWOOD TECHNOLOGY + +OUI:000954* + ID_OUI_FROM_DATABASE=AMiT spol. s. r. o. + +OUI:00094E* + ID_OUI_FROM_DATABASE=BARTECH SYSTEMS INTERNATIONAL, INC + +OUI:000953* + ID_OUI_FROM_DATABASE=Linkage System Integration Co.Ltd. + +OUI:000942* + ID_OUI_FROM_DATABASE=Wireless Technologies, Inc + +OUI:000947* + ID_OUI_FROM_DATABASE=Aztek, Inc. + +OUI:00093B* + ID_OUI_FROM_DATABASE=HYUNDAI NETWORKS INC. + +OUI:000934* + ID_OUI_FROM_DATABASE=Dream-Multimedia-Tv GmbH + +OUI:0008BA* + ID_OUI_FROM_DATABASE=Erskine Systems Ltd + +OUI:0008B4* + ID_OUI_FROM_DATABASE=SYSPOL + +OUI:0008AE* + ID_OUI_FROM_DATABASE=PacketFront Network Products AB + +OUI:0008A7* + ID_OUI_FROM_DATABASE=iLogic Inc. + +OUI:0008A2* + ID_OUI_FROM_DATABASE=ADI Engineering, Inc. + +OUI:0008A1* + ID_OUI_FROM_DATABASE=CNet Technology Inc. + +OUI:00089B* + ID_OUI_FROM_DATABASE=ICP Electronics Inc. + +OUI:00088D* + ID_OUI_FROM_DATABASE=Sigma-Links Inc. + +OUI:000893* + ID_OUI_FROM_DATABASE=LE INFORMATION COMMUNICATION INC. + +OUI:00088E* + ID_OUI_FROM_DATABASE=Nihon Computer Co., Ltd. + +OUI:000897* + ID_OUI_FROM_DATABASE=Quake Technologies + +OUI:000887* + ID_OUI_FROM_DATABASE=Maschinenfabrik Reinhausen GmbH + +OUI:0008FD* + ID_OUI_FROM_DATABASE=BlueKorea Co., Ltd. + +OUI:0008F5* + ID_OUI_FROM_DATABASE=YESTECHNOLOGY Co.,Ltd. + +OUI:0008EF* + ID_OUI_FROM_DATABASE=DIBAL,S.A. + +OUI:0008EA* + ID_OUI_FROM_DATABASE=Motion Control Engineering, Inc + +OUI:0008DD* + ID_OUI_FROM_DATABASE=Telena Communications, Inc. + +OUI:0008DE* + ID_OUI_FROM_DATABASE=3UP Systems + +OUI:0008E3* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0008D7* + ID_OUI_FROM_DATABASE=HOW CORPORATION + +OUI:0008CB* + ID_OUI_FROM_DATABASE=Zeta Broadband Inc. + +OUI:0008D0* + ID_OUI_FROM_DATABASE=Musashi Engineering Co., LTD. + +OUI:0008C1* + ID_OUI_FROM_DATABASE=Avistar Communications Corporation + +OUI:0008C6* + ID_OUI_FROM_DATABASE=Philips Consumer Communications + +OUI:000993* + ID_OUI_FROM_DATABASE=Visteon Corporation + +OUI:000998* + ID_OUI_FROM_DATABASE=Capinfo Company Limited + +OUI:000986* + ID_OUI_FROM_DATABASE=Metalink LTD. + +OUI:000985* + ID_OUI_FROM_DATABASE=Auto Telecom Company + +OUI:00098C* + ID_OUI_FROM_DATABASE=Option Wireless Sweden + +OUI:000980* + ID_OUI_FROM_DATABASE=Power Zenith Inc. + +OUI:000973* + ID_OUI_FROM_DATABASE=Lenten Technology Co., Ltd. + +OUI:000974* + ID_OUI_FROM_DATABASE=Innopia Technologies, Inc. + +OUI:000979* + ID_OUI_FROM_DATABASE=Advanced Television Systems Committee, Inc. + +OUI:000966* + ID_OUI_FROM_DATABASE=Thales Navigation + +OUI:00096D* + ID_OUI_FROM_DATABASE=Powernet Technologies Corp. + +OUI:00081F* + ID_OUI_FROM_DATABASE=Pou Yuen Tech Corp. Ltd. + +OUI:000826* + ID_OUI_FROM_DATABASE=Colorado Med Tech + +OUI:000820* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000825* + ID_OUI_FROM_DATABASE=Acme Packet + +OUI:00082C* + ID_OUI_FROM_DATABASE=Homag AG + +OUI:000819* + ID_OUI_FROM_DATABASE=Banksys + +OUI:000810* + ID_OUI_FROM_DATABASE=Key Technology, Inc. + +OUI:000813* + ID_OUI_FROM_DATABASE=Diskbank, Inc. + +OUI:00080A* + ID_OUI_FROM_DATABASE=Espera-Werke GmbH + +OUI:000804* + ID_OUI_FROM_DATABASE=ICA Inc. + +OUI:0007FA* + ID_OUI_FROM_DATABASE=ITT Co., Ltd. + +OUI:0007E7* + ID_OUI_FROM_DATABASE=FreeWave Technologies + +OUI:0007EE* + ID_OUI_FROM_DATABASE=telco Informationssysteme GmbH + +OUI:000928* + ID_OUI_FROM_DATABASE=Telecore + +OUI:00092F* + ID_OUI_FROM_DATABASE=Akom Technology Corporation + +OUI:000922* + ID_OUI_FROM_DATABASE=TST Biometrics GmbH + +OUI:000921* + ID_OUI_FROM_DATABASE=Planmeca Oy + +OUI:00091C* + ID_OUI_FROM_DATABASE=CacheVision, Inc + +OUI:000910* + ID_OUI_FROM_DATABASE=Simple Access Inc. + +OUI:000915* + ID_OUI_FROM_DATABASE=CAS Corp. + +OUI:00090F* + ID_OUI_FROM_DATABASE=Fortinet Inc. + +OUI:000909* + ID_OUI_FROM_DATABASE=Telenor Connect A/S + +OUI:000902* + ID_OUI_FROM_DATABASE=Redline Communications Inc. + +OUI:00065E* + ID_OUI_FROM_DATABASE=Photuris, Inc. + +OUI:000645* + ID_OUI_FROM_DATABASE=Meisei Electric Co. Ltd. + +OUI:000644* + ID_OUI_FROM_DATABASE=neix,Inc + +OUI:00064B* + ID_OUI_FROM_DATABASE=Alexon Co., Ltd. + +OUI:00063B* + ID_OUI_FROM_DATABASE=Arcturus Networks Inc. + +OUI:00063A* + ID_OUI_FROM_DATABASE=Dura Micro, Inc. + +OUI:000634* + ID_OUI_FROM_DATABASE=GTE Airfone Inc. + +OUI:00062A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000627* + ID_OUI_FROM_DATABASE=Uniwide Technologies, Inc. + +OUI:00062E* + ID_OUI_FROM_DATABASE=Aristos Logic Corp. + +OUI:000617* + ID_OUI_FROM_DATABASE=Redswitch Inc. + +OUI:00061E* + ID_OUI_FROM_DATABASE=Maxan Systems + +OUI:000618* + ID_OUI_FROM_DATABASE=DigiPower Manufacturing Inc. + +OUI:000770* + ID_OUI_FROM_DATABASE=Ubiquoss Inc + +OUI:00076B* + ID_OUI_FROM_DATABASE=Stralfors AB + +OUI:00075F* + ID_OUI_FROM_DATABASE=VCS Video Communication Systems AG + +OUI:000766* + ID_OUI_FROM_DATABASE=Chou Chin Industrial Co., Ltd. + +OUI:000759* + ID_OUI_FROM_DATABASE=Boris Manufacturing Corp. + +OUI:00074C* + ID_OUI_FROM_DATABASE=Beicom Inc. + +OUI:000753* + ID_OUI_FROM_DATABASE=Beijing Qxcomm Technology Co., Ltd. + +OUI:000743* + ID_OUI_FROM_DATABASE=Chelsio Communications + +OUI:000744* + ID_OUI_FROM_DATABASE=Unico, Inc. + +OUI:000747* + ID_OUI_FROM_DATABASE=Mecalc + +OUI:000737* + ID_OUI_FROM_DATABASE=Soriya Co. Ltd. + +OUI:00073E* + ID_OUI_FROM_DATABASE=China Great-Wall Computer Shenzhen Co., Ltd. + +OUI:0006C4* + ID_OUI_FROM_DATABASE=Piolink Inc. + +OUI:0006C0* + ID_OUI_FROM_DATABASE=United Internetworks, Inc. + +OUI:0006BA* + ID_OUI_FROM_DATABASE=Westwave Communications + +OUI:0006AD* + ID_OUI_FROM_DATABASE=KB Electronics Ltd. + +OUI:0006B4* + ID_OUI_FROM_DATABASE=Vorne Industries, Inc. + +OUI:0006AE* + ID_OUI_FROM_DATABASE=Himachal Futuristic Communications Ltd + +OUI:0006B3* + ID_OUI_FROM_DATABASE=Diagraph Corporation + +OUI:0006A3* + ID_OUI_FROM_DATABASE=Bitran Corporation + +OUI:00069D* + ID_OUI_FROM_DATABASE=Petards Ltd + +OUI:0006A7* + ID_OUI_FROM_DATABASE=Primarion + +OUI:000657* + ID_OUI_FROM_DATABASE=Market Central, Inc. + +OUI:000697* + ID_OUI_FROM_DATABASE=R & D Center + +OUI:000691* + ID_OUI_FROM_DATABASE=PT Inovacao + +OUI:0005C7* + ID_OUI_FROM_DATABASE=I/F-COM A/S + +OUI:0005CE* + ID_OUI_FROM_DATABASE=Prolink Microsystems Corporation + +OUI:0005C1* + ID_OUI_FROM_DATABASE=A-Kyung Motion, Inc. + +OUI:0005BB* + ID_OUI_FROM_DATABASE=Myspace AB + +OUI:00059B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0005A7* + ID_OUI_FROM_DATABASE=Hyperchip, Inc. + +OUI:0005B5* + ID_OUI_FROM_DATABASE=Broadcom Technologies + +OUI:00059A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0005A1* + ID_OUI_FROM_DATABASE=Zenocom + +OUI:0005AB* + ID_OUI_FROM_DATABASE=Cyber Fone, Inc. + +OUI:000588* + ID_OUI_FROM_DATABASE=Sensoria Corp. + +OUI:00058E* + ID_OUI_FROM_DATABASE=Flextronics International GmbH & Co. Nfg. KG + +OUI:000612* + ID_OUI_FROM_DATABASE=Accusys, Inc. + +OUI:000609* + ID_OUI_FROM_DATABASE=Crossport Systems + +OUI:00060F* + ID_OUI_FROM_DATABASE=Narad Networks Inc + +OUI:000602* + ID_OUI_FROM_DATABASE=Cirkitech Electronics Co. + +OUI:0005ED* + ID_OUI_FROM_DATABASE=Technikum Joanneum GmbH + +OUI:000600* + ID_OUI_FROM_DATABASE=Toshiba Teli Corporation + +OUI:0005E7* + ID_OUI_FROM_DATABASE=Netrake an AudioCodes Company + +OUI:0005F3* + ID_OUI_FROM_DATABASE=Webyn + +OUI:0005FA* + ID_OUI_FROM_DATABASE=IPOptical, Inc. + +OUI:0005DE* + ID_OUI_FROM_DATABASE=Gi Fone Korea, Inc. + +OUI:0005DA* + ID_OUI_FROM_DATABASE=Apex Automationstechnik + +OUI:0005C8* + ID_OUI_FROM_DATABASE=VERYTECH + +OUI:0005D4* + ID_OUI_FROM_DATABASE=FutureSmart Networks, Inc. + +OUI:0006EC* + ID_OUI_FROM_DATABASE=Harris Corporation + +OUI:0006DF* + ID_OUI_FROM_DATABASE=AIDONIC Corporation + +OUI:0006E0* + ID_OUI_FROM_DATABASE=MAT Co., Ltd. + +OUI:0006E5* + ID_OUI_FROM_DATABASE=Fujian Newland Computer Ltd. Co. + +OUI:0006DB* + ID_OUI_FROM_DATABASE=ICHIPS Co., Ltd. + +OUI:0006D0* + ID_OUI_FROM_DATABASE=Elgar Electronics Corp. + +OUI:0006D7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0006CA* + ID_OUI_FROM_DATABASE=American Computer & Digital Components, Inc. (ACDC) + +OUI:000581* + ID_OUI_FROM_DATABASE=Snell + +OUI:00057B* + ID_OUI_FROM_DATABASE=Chung Nam Electronic Co., Ltd. + +OUI:000582* + ID_OUI_FROM_DATABASE=ClearCube Technology + +OUI:000577* + ID_OUI_FROM_DATABASE=SM Information & Communication + +OUI:000571* + ID_OUI_FROM_DATABASE=Seiwa Electronics Co. + +OUI:00056B* + ID_OUI_FROM_DATABASE=C.P. Technology Co., Ltd. + +OUI:000565* + ID_OUI_FROM_DATABASE=Tailyn Communication Company Ltd. + +OUI:00055F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00055E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000558* + ID_OUI_FROM_DATABASE=Synchronous, Inc. + +OUI:000552* + ID_OUI_FROM_DATABASE=Xycotec Computer GmbH + +OUI:000549* + ID_OUI_FROM_DATABASE=Salira Optical Network Systems + +OUI:00072B* + ID_OUI_FROM_DATABASE=Jung Myung Telecom Co., Ltd. + +OUI:000731* + ID_OUI_FROM_DATABASE=Ophir-Spiricon LLC + +OUI:00071A* + ID_OUI_FROM_DATABASE=Finedigital Inc. + +OUI:000721* + ID_OUI_FROM_DATABASE=Formac Elektronik GmbH + +OUI:00070E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000715* + ID_OUI_FROM_DATABASE=General Research of Electronics, Inc. + +OUI:000708* + ID_OUI_FROM_DATABASE=Bitrage Inc. + +OUI:0006F2* + ID_OUI_FROM_DATABASE=Platys Communications + +OUI:0006FE* + ID_OUI_FROM_DATABASE=Ambrado, Inc + +OUI:0006FC* + ID_OUI_FROM_DATABASE=Fnet Co., Ltd. + +OUI:000684* + ID_OUI_FROM_DATABASE=Biacore AB + +OUI:00068A* + ID_OUI_FROM_DATABASE=NeuronNet Co. Ltd. R&D Center + +OUI:00067E* + ID_OUI_FROM_DATABASE=WinCom Systems, Inc. + +OUI:000670* + ID_OUI_FROM_DATABASE=Upponetti Oy + +OUI:000676* + ID_OUI_FROM_DATABASE=Novra Technologies Inc. + +OUI:00067A* + ID_OUI_FROM_DATABASE=JMP Systems + +OUI:000664* + ID_OUI_FROM_DATABASE=Fostex Corporation + +OUI:00066A* + ID_OUI_FROM_DATABASE=InfiniCon Systems, Inc. + +OUI:000651* + ID_OUI_FROM_DATABASE=Aspen Networks Inc. + +OUI:00065D* + ID_OUI_FROM_DATABASE=Heidelberg Web Systems + +OUI:000415* + ID_OUI_FROM_DATABASE=Rasteme Systems Co., Ltd. + +OUI:000408* + ID_OUI_FROM_DATABASE=Sanko Electronics Co., Ltd. + +OUI:000409* + ID_OUI_FROM_DATABASE=Cratos Networks + +OUI:000402* + ID_OUI_FROM_DATABASE=Nexsan Technologies, Ltd. + +OUI:0003F8* + ID_OUI_FROM_DATABASE=SanCastle Technologies, Inc. + +OUI:0003FF* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:0003F1* + ID_OUI_FROM_DATABASE=Cicada Semiconductor, Inc. + +OUI:0003F2* + ID_OUI_FROM_DATABASE=Seneca Networks + +OUI:0003EC* + ID_OUI_FROM_DATABASE=ICG Research, Inc. + +OUI:0003E6* + ID_OUI_FROM_DATABASE=Entone, Inc. + +OUI:0003DE* + ID_OUI_FROM_DATABASE=OTC Wireless + +OUI:0003E1* + ID_OUI_FROM_DATABASE=Winmate Communication, Inc. + +OUI:0003DA* + ID_OUI_FROM_DATABASE=Takamisawa Cybernetics Co., Ltd. + +OUI:00054C* + ID_OUI_FROM_DATABASE=RF Innovations Pty Ltd + +OUI:000543* + ID_OUI_FROM_DATABASE=IQ Wireless GmbH + +OUI:00053D* + ID_OUI_FROM_DATABASE=Agere Systems + +OUI:000530* + ID_OUI_FROM_DATABASE=Andiamo Systems, Inc. + +OUI:000537* + ID_OUI_FROM_DATABASE=Nets Technology Co., Ltd. + +OUI:000536* + ID_OUI_FROM_DATABASE=Danam Communications, Inc. + +OUI:000524* + ID_OUI_FROM_DATABASE=BTL System (HK) Limited + +OUI:00052A* + ID_OUI_FROM_DATABASE=Ikegami Tsushinki Co., Ltd. + +OUI:00051D* + ID_OUI_FROM_DATABASE=Airocon, Inc. + +OUI:000517* + ID_OUI_FROM_DATABASE=Shellcomm, Inc. + +OUI:000513* + ID_OUI_FROM_DATABASE=VTLinx Multimedia Systems, Inc. + +OUI:0004D4* + ID_OUI_FROM_DATABASE=Proview Electronics Co., Ltd. + +OUI:0004CE* + ID_OUI_FROM_DATABASE=Patria Ailon + +OUI:0004CD* + ID_OUI_FROM_DATABASE=Extenway Solutions Inc + +OUI:0004C7* + ID_OUI_FROM_DATABASE=NetMount + +OUI:0004C8* + ID_OUI_FROM_DATABASE=LIBA Maschinenfabrik GmbH + +OUI:0004C1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0004BB* + ID_OUI_FROM_DATABASE=Bardac Corporation + +OUI:0004B5* + ID_OUI_FROM_DATABASE=Equitrac Corporation + +OUI:0004A7* + ID_OUI_FROM_DATABASE=FabiaTech Corporation + +OUI:0004A1* + ID_OUI_FROM_DATABASE=Pathway Connectivity + +OUI:00049A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00035B* + ID_OUI_FROM_DATABASE=BridgeWave Communications + +OUI:000356* + ID_OUI_FROM_DATABASE=Wincor Nixdorf International GmbH + +OUI:000350* + ID_OUI_FROM_DATABASE=BTICINO SPA + +OUI:000348* + ID_OUI_FROM_DATABASE=Norscan Instruments, Ltd. + +OUI:000345* + ID_OUI_FROM_DATABASE=Routrek Networks Corporation + +OUI:00033D* + ID_OUI_FROM_DATABASE=ILSHin Lab + +OUI:0001EC* + ID_OUI_FROM_DATABASE=Ericsson Group + +OUI:000331* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000338* + ID_OUI_FROM_DATABASE=Oak Technology + +OUI:000335* + ID_OUI_FROM_DATABASE=Mirae Technology + +OUI:00032C* + ID_OUI_FROM_DATABASE=ABB Switzerland Ltd + +OUI:000325* + ID_OUI_FROM_DATABASE=Arima Computer Corp. + +OUI:000453* + ID_OUI_FROM_DATABASE=YottaYotta, Inc. + +OUI:00044D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000449* + ID_OUI_FROM_DATABASE=Mapletree Networks + +OUI:000443* + ID_OUI_FROM_DATABASE=Agilent Technologies, Inc. + +OUI:00043D* + ID_OUI_FROM_DATABASE=INDEL AG + +OUI:000431* + ID_OUI_FROM_DATABASE=GlobalStreams, Inc. + +OUI:000436* + ID_OUI_FROM_DATABASE=ELANsat Technologies, Inc. + +OUI:000430* + ID_OUI_FROM_DATABASE=Netgem + +OUI:00042A* + ID_OUI_FROM_DATABASE=Wireless Networks, Inc. + +OUI:000424* + ID_OUI_FROM_DATABASE=TMC s.r.l. + +OUI:00041B* + ID_OUI_FROM_DATABASE=Bridgeworks Ltd. + +OUI:00041E* + ID_OUI_FROM_DATABASE=Shikoku Instrumentation Co., Ltd. + +OUI:0003D3* + ID_OUI_FROM_DATABASE=Internet Energy Systems, Inc. + +OUI:0003CE* + ID_OUI_FROM_DATABASE=ETEN Technologies, Inc. + +OUI:0003CB* + ID_OUI_FROM_DATABASE=Nippon Systems Development Co., Ltd. + +OUI:0003C2* + ID_OUI_FROM_DATABASE=Solphone K.K. + +OUI:0003C7* + ID_OUI_FROM_DATABASE=hopf Elektronik GmbH + +OUI:0003BB* + ID_OUI_FROM_DATABASE=Signal Communications Limited + +OUI:0003B5* + ID_OUI_FROM_DATABASE=Entra Technology Co. + +OUI:0003B0* + ID_OUI_FROM_DATABASE=Xsense Technology Corp. + +OUI:0003A4* + ID_OUI_FROM_DATABASE=Imation Corp. + +OUI:0003A9* + ID_OUI_FROM_DATABASE=AXCENT Media AG + +OUI:0003AD* + ID_OUI_FROM_DATABASE=Emerson Energy Systems AB + +OUI:000396* + ID_OUI_FROM_DATABASE=EZ Cast Co., Ltd. + +OUI:00050D* + ID_OUI_FROM_DATABASE=Midstream Technologies, Inc. + +OUI:000507* + ID_OUI_FROM_DATABASE=Fine Appliance Corp. + +OUI:0004FD* + ID_OUI_FROM_DATABASE=Japan Control Engineering Co., Ltd. + +OUI:0004F7* + ID_OUI_FROM_DATABASE=Omega Band, Inc. + +OUI:0004F1* + ID_OUI_FROM_DATABASE=WhereNet + +OUI:0004DA* + ID_OUI_FROM_DATABASE=Relax Technology, Inc. + +OUI:008087* + ID_OUI_FROM_DATABASE=OKI ELECTRIC INDUSTRY CO., LTD + +OUI:0004E0* + ID_OUI_FROM_DATABASE=Procket Networks + +OUI:000460* + ID_OUI_FROM_DATABASE=Knilink Technology, Inc. + +OUI:000494* + ID_OUI_FROM_DATABASE=Breezecom, Ltd. + +OUI:00048E* + ID_OUI_FROM_DATABASE=Ohm Tech Labs, Inc. + +OUI:000495* + ID_OUI_FROM_DATABASE=Tejas Networks India Limited + +OUI:000483* + ID_OUI_FROM_DATABASE=Deltron Technology, Inc. + +OUI:000489* + ID_OUI_FROM_DATABASE=YAFO Networks, Inc. + +OUI:000479* + ID_OUI_FROM_DATABASE=Radius Co., Ltd. + +OUI:00046D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000472* + ID_OUI_FROM_DATABASE=Telelynx, Inc. + +OUI:00046C* + ID_OUI_FROM_DATABASE=Cyber Technology Co., Ltd. + +OUI:000466* + ID_OUI_FROM_DATABASE=ARMITEL Co. + +OUI:00045A* + ID_OUI_FROM_DATABASE=The Linksys Group, Inc. + +OUI:00045F* + ID_OUI_FROM_DATABASE=Avalue Technology, Inc. + +OUI:000391* + ID_OUI_FROM_DATABASE=Advanced Digital Broadcast, Ltd. + +OUI:00038A* + ID_OUI_FROM_DATABASE=America Online, Inc. + +OUI:00038E* + ID_OUI_FROM_DATABASE=Atoga Systems, Inc. + +OUI:00037C* + ID_OUI_FROM_DATABASE=Coax Media + +OUI:000381* + ID_OUI_FROM_DATABASE=Ingenico International + +OUI:000375* + ID_OUI_FROM_DATABASE=NetMedia, Inc. + +OUI:00036E* + ID_OUI_FROM_DATABASE=Nicon Systems (Pty) Limited + +OUI:000362* + ID_OUI_FROM_DATABASE=Vodtel Communications, Inc. + +OUI:00031C* + ID_OUI_FROM_DATABASE=Svenska Hardvarufabriken AB + +OUI:000315* + ID_OUI_FROM_DATABASE=Cidco Incorporated + +OUI:000310* + ID_OUI_FROM_DATABASE=E-Globaledge Corporation + +OUI:00030D* + ID_OUI_FROM_DATABASE=Uniwill Computer Corp. + +OUI:000309* + ID_OUI_FROM_DATABASE=Texcel Technology PLC + +OUI:000304* + ID_OUI_FROM_DATABASE=Pacific Broadband Communications + +OUI:00019F* + ID_OUI_FROM_DATABASE=ReadyNet + +OUI:0002FD* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:0002F6* + ID_OUI_FROM_DATABASE=Equipe Communications + +OUI:0002F1* + ID_OUI_FROM_DATABASE=Pinetron Co., Ltd. + +OUI:0002EF* + ID_OUI_FROM_DATABASE=CCC Network Systems Group Ltd. + +OUI:0002EB* + ID_OUI_FROM_DATABASE=Pico Communications + +OUI:0002E6* + ID_OUI_FROM_DATABASE=Gould Instrument Systems, Inc. + +OUI:0002DF* + ID_OUI_FROM_DATABASE=Net Com Systems, Inc. + +OUI:0002D3* + ID_OUI_FROM_DATABASE=NetBotz, Inc. + +OUI:0002D8* + ID_OUI_FROM_DATABASE=BRECIS Communications Corporation + +OUI:0002CC* + ID_OUI_FROM_DATABASE=M.C.C.I + +OUI:0002D0* + ID_OUI_FROM_DATABASE=Comdial Corporation + +OUI:0002C5* + ID_OUI_FROM_DATABASE=Evertz Microsystems Ltd. + +OUI:0002C0* + ID_OUI_FROM_DATABASE=Bencent Tzeng Industry Co., Ltd. + +OUI:0002BD* + ID_OUI_FROM_DATABASE=Bionet Co., Ltd. + +OUI:0002B7* + ID_OUI_FROM_DATABASE=Watanabe Electric Industry Co., Ltd. + +OUI:0002B0* + ID_OUI_FROM_DATABASE=Hokubu Communication & Industrial Co., Ltd. + +OUI:0002A8* + ID_OUI_FROM_DATABASE=Air Link Technology + +OUI:0002AB* + ID_OUI_FROM_DATABASE=CTC Union Technologies Co., Ltd. + +OUI:0002A4* + ID_OUI_FROM_DATABASE=AddPac Technology Co., Ltd. + +OUI:000299* + ID_OUI_FROM_DATABASE=Apex, Inc. + +OUI:00029D* + ID_OUI_FROM_DATABASE=Merix Corp. + +OUI:000291* + ID_OUI_FROM_DATABASE=Open Network Co., Ltd. + +OUI:00028A* + ID_OUI_FROM_DATABASE=Ambit Microsystems Corporation + +OUI:000287* + ID_OUI_FROM_DATABASE=Adapcom + +OUI:00028C* + ID_OUI_FROM_DATABASE=Micrel-Synergy Semiconductor + +OUI:000282* + ID_OUI_FROM_DATABASE=ViaClix, Inc. + +OUI:00027B* + ID_OUI_FROM_DATABASE=Amplify Net, Inc. + +OUI:00024F* + ID_OUI_FROM_DATABASE=IPM Datacom S.R.L. + +OUI:000274* + ID_OUI_FROM_DATABASE=Tommy Technologies Corp. + +OUI:00026F* + ID_OUI_FROM_DATABASE=Senao International Co., Ltd. + +OUI:000264* + ID_OUI_FROM_DATABASE=AudioRamp.com + +OUI:00306C* + ID_OUI_FROM_DATABASE=Hitex Holding GmbH + +OUI:000177* + ID_OUI_FROM_DATABASE=EDSL + +OUI:000161* + ID_OUI_FROM_DATABASE=Meta Machine Technology + +OUI:000168* + ID_OUI_FROM_DATABASE=VITANA CORPORATION + +OUI:000174* + ID_OUI_FROM_DATABASE=CyberOptics Corporation + +OUI:000164* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000170* + ID_OUI_FROM_DATABASE=ESE Embedded System Engineer'g + +OUI:000152* + ID_OUI_FROM_DATABASE=CHROMATEK INC. + +OUI:000156* + ID_OUI_FROM_DATABASE=FIREWIREDIRECT.COM, INC. + +OUI:00013F* + ID_OUI_FROM_DATABASE=Neighbor World Co., Ltd. + +OUI:000146* + ID_OUI_FROM_DATABASE=Tesco Controls, Inc. + +OUI:000133* + ID_OUI_FROM_DATABASE=KYOWA Electronic Instruments C + +OUI:0001E3* + ID_OUI_FROM_DATABASE=Siemens AG + +OUI:0001EA* + ID_OUI_FROM_DATABASE=Cirilium Corp. + +OUI:0001EF* + ID_OUI_FROM_DATABASE=Camtel Technology Corp. + +OUI:0001F2* + ID_OUI_FROM_DATABASE=Mark of the Unicorn, Inc. + +OUI:0001D7* + ID_OUI_FROM_DATABASE=F5 Networks, Inc. + +OUI:0001DC* + ID_OUI_FROM_DATABASE=Activetelco + +OUI:0001DF* + ID_OUI_FROM_DATABASE=ISDN Communications, Ltd. + +OUI:0001D3* + ID_OUI_FROM_DATABASE=PAXCOMM, Inc. + +OUI:0001C5* + ID_OUI_FROM_DATABASE=Simpler Networks + +OUI:0001D0* + ID_OUI_FROM_DATABASE=VitalPoint, Inc. + +OUI:0001B2* + ID_OUI_FROM_DATABASE=Digital Processing Systems, Inc. + +OUI:0001C1* + ID_OUI_FROM_DATABASE=Vitesse Semiconductor Corporation + +OUI:0001BA* + ID_OUI_FROM_DATABASE=IC-Net, Inc. + +OUI:0001B6* + ID_OUI_FROM_DATABASE=SAEJIN T&M Co., Ltd. + +OUI:00022B* + ID_OUI_FROM_DATABASE=SAXA, Inc. + +OUI:000226* + ID_OUI_FROM_DATABASE=XESystems, Inc. + +OUI:00021E* + ID_OUI_FROM_DATABASE=SIMTEL S.R.L. + +OUI:00021A* + ID_OUI_FROM_DATABASE=Zuma Networks + +OUI:00020B* + ID_OUI_FROM_DATABASE=Native Networks, Inc. + +OUI:000212* + ID_OUI_FROM_DATABASE=SierraCom + +OUI:000217* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:000207* + ID_OUI_FROM_DATABASE=VisionGlobal Network Corp. + +OUI:000204* + ID_OUI_FROM_DATABASE=Bodmann Industries Elektronik GmbH + +OUI:0001F8* + ID_OUI_FROM_DATABASE=TEXIO TECHNOLOGY CORPORATION + +OUI:0001FF* + ID_OUI_FROM_DATABASE=Data Direct Networks, Inc. + +OUI:0001FB* + ID_OUI_FROM_DATABASE=DoTop Technology, Inc. + +OUI:000268* + ID_OUI_FROM_DATABASE=Harris Government Communications + +OUI:00025D* + ID_OUI_FROM_DATABASE=Calix Networks + +OUI:000258* + ID_OUI_FROM_DATABASE=Flying Packets Communications + +OUI:000257* + ID_OUI_FROM_DATABASE=Microcom Corp. + +OUI:000254* + ID_OUI_FROM_DATABASE=WorldGate + +OUI:000248* + ID_OUI_FROM_DATABASE=Pilz GmbH & Co. + +OUI:00022E* + ID_OUI_FROM_DATABASE=TEAC Corp. R& D + +OUI:000241* + ID_OUI_FROM_DATABASE=Amer.com + +OUI:000232* + ID_OUI_FROM_DATABASE=Avision, Inc. + +OUI:00012A* + ID_OUI_FROM_DATABASE=Telematica Sistems Inteligente + +OUI:000130* + ID_OUI_FROM_DATABASE=Extreme Networks + +OUI:000137* + ID_OUI_FROM_DATABASE=IT Farm Corporation + +OUI:000143* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00011B* + ID_OUI_FROM_DATABASE=Unizone Technologies, Inc. + +OUI:000122* + ID_OUI_FROM_DATABASE=Trend Communications, Ltd. + +OUI:00011E* + ID_OUI_FROM_DATABASE=Precidia Technologies, Inc. + +OUI:000108* + ID_OUI_FROM_DATABASE=AVLAB Technology, Inc. + +OUI:00010B* + ID_OUI_FROM_DATABASE=Space CyberLink, Inc. + +OUI:0001AE* + ID_OUI_FROM_DATABASE=Trex Enterprises + +OUI:0001AA* + ID_OUI_FROM_DATABASE=Airspan Communications, Ltd. + +OUI:000198* + ID_OUI_FROM_DATABASE=Darim Vision + +OUI:000180* + ID_OUI_FROM_DATABASE=AOpen, Inc. + +OUI:000187* + ID_OUI_FROM_DATABASE=I2SE GmbH + +OUI:00018F* + ID_OUI_FROM_DATABASE=Kenetec, Inc. + +OUI:000183* + ID_OUI_FROM_DATABASE=ANITE TELECOMS + +OUI:00019C* + ID_OUI_FROM_DATABASE=JDS Uniphase Inc. + +OUI:000190* + ID_OUI_FROM_DATABASE=SMK-M + +OUI:0030D1* + ID_OUI_FROM_DATABASE=INOVA CORPORATION + +OUI:003032* + ID_OUI_FROM_DATABASE=MagicRam, Inc. + +OUI:00305A* + ID_OUI_FROM_DATABASE=TELGEN CORPORATION + +OUI:003069* + ID_OUI_FROM_DATABASE=IMPACCT TECHNOLOGY CORP. + +OUI:0030EC* + ID_OUI_FROM_DATABASE=BORGARDT + +OUI:0030B4* + ID_OUI_FROM_DATABASE=INTERSIL CORP. + +OUI:00308E* + ID_OUI_FROM_DATABASE=CROSS MATCH TECHNOLOGIES, INC. + +OUI:0030D0* + ID_OUI_FROM_DATABASE=Tellabs + +OUI:0030A5* + ID_OUI_FROM_DATABASE=ACTIVE POWER + +OUI:003009* + ID_OUI_FROM_DATABASE=Tachion Networks, Inc. + +OUI:00302F* + ID_OUI_FROM_DATABASE=GE Aviation System + +OUI:0030A4* + ID_OUI_FROM_DATABASE=Woodwind Communications System + +OUI:0030E5* + ID_OUI_FROM_DATABASE=Amper Datos S.A. + +OUI:0030C0* + ID_OUI_FROM_DATABASE=Lara Technology, Inc. + +OUI:00300E* + ID_OUI_FROM_DATABASE=Klotz Digital AG + +OUI:003094* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00309A* + ID_OUI_FROM_DATABASE=ASTRO TERRA CORP. + +OUI:00300C* + ID_OUI_FROM_DATABASE=CONGRUENCY, LTD. + +OUI:0030FD* + ID_OUI_FROM_DATABASE=INTEGRATED SYSTEMS DESIGN + +OUI:003023* + ID_OUI_FROM_DATABASE=COGENT COMPUTER SYSTEMS, INC. + +OUI:0030DF* + ID_OUI_FROM_DATABASE=KB/TEL TELECOMUNICACIONES + +OUI:00307D* + ID_OUI_FROM_DATABASE=GRE AMERICA, INC. + +OUI:00D0E4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D08B* + ID_OUI_FROM_DATABASE=ADVA Optical Networking Ltd. + +OUI:00D098* + ID_OUI_FROM_DATABASE=Photon Dynamics Canada Inc. + +OUI:00D05E* + ID_OUI_FROM_DATABASE=STRATABEAM TECHNOLOGY, INC. + +OUI:00D0BE* + ID_OUI_FROM_DATABASE=EMUTEC INC. + +OUI:00D0F4* + ID_OUI_FROM_DATABASE=CARINTHIAN TECH INSTITUTE + +OUI:00D0AA* + ID_OUI_FROM_DATABASE=CHASE COMMUNICATIONS + +OUI:00D0FA* + ID_OUI_FROM_DATABASE=Thales e-Security Ltd. + +OUI:00D006* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D03D* + ID_OUI_FROM_DATABASE=GALILEO TECHNOLOGY, LTD. + +OUI:00D014* + ID_OUI_FROM_DATABASE=ROOT, INC. + +OUI:00D0DD* + ID_OUI_FROM_DATABASE=SUNRISE TELECOM, INC. + +OUI:00D091* + ID_OUI_FROM_DATABASE=SMARTSAN SYSTEMS, INC. + +OUI:00B0EE* + ID_OUI_FROM_DATABASE=Ajile Systems, Inc. + +OUI:00B0E7* + ID_OUI_FROM_DATABASE=British Federal Ltd. + +OUI:00B04A* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00B069* + ID_OUI_FROM_DATABASE=Honewell Oy + +OUI:00B0C2* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00B0DF* + ID_OUI_FROM_DATABASE=Starboard Storage Systems + +OUI:00B0EC* + ID_OUI_FROM_DATABASE=EACEM + +OUI:003092* + ID_OUI_FROM_DATABASE=ModuNORM GmbH + +OUI:0030EE* + ID_OUI_FROM_DATABASE=DSG Technology, Inc. + +OUI:003042* + ID_OUI_FROM_DATABASE=DeTeWe-Deutsche Telephonwerke + +OUI:003099* + ID_OUI_FROM_DATABASE=BOENIG UND KALLENBACH OHG + +OUI:003051* + ID_OUI_FROM_DATABASE=ORBIT AVIONIC & COMMUNICATION + +OUI:0030AB* + ID_OUI_FROM_DATABASE=DELTA NETWORKS, INC. + +OUI:003093* + ID_OUI_FROM_DATABASE=Sonnet Technologies, Inc + +OUI:00303C* + ID_OUI_FROM_DATABASE=ONNTO CORP. + +OUI:0030C7* + ID_OUI_FROM_DATABASE=Macromate Corp. + +OUI:003066* + ID_OUI_FROM_DATABASE=RFM + +OUI:00307F* + ID_OUI_FROM_DATABASE=IRLAN LTD. + +OUI:003016* + ID_OUI_FROM_DATABASE=ISHIDA CO., LTD. + +OUI:00302A* + ID_OUI_FROM_DATABASE=SOUTHERN INFORMATION + +OUI:0030DC* + ID_OUI_FROM_DATABASE=RIGHTECH CORPORATION + +OUI:00D0A4* + ID_OUI_FROM_DATABASE=ALANTRO COMMUNICATIONS + +OUI:00D043* + ID_OUI_FROM_DATABASE=ZONAL RETAIL DATA SYSTEMS + +OUI:00D016* + ID_OUI_FROM_DATABASE=SCM MICROSYSTEMS, INC. + +OUI:00D012* + ID_OUI_FROM_DATABASE=GATEWORKS CORP. + +OUI:00D092* + ID_OUI_FROM_DATABASE=GLENAYRE WESTERN MULTIPLEX + +OUI:00D0C5* + ID_OUI_FROM_DATABASE=COMPUTATIONAL SYSTEMS, INC. + +OUI:0001A7* + ID_OUI_FROM_DATABASE=UNEX TECHNOLOGY CORPORATION + +OUI:00D0B5* + ID_OUI_FROM_DATABASE=IPricot formerly DotCom + +OUI:0030E8* + ID_OUI_FROM_DATABASE=ENSIM CORP. + +OUI:0030ED* + ID_OUI_FROM_DATABASE=Expert Magnetics Corp. + +OUI:0030F9* + ID_OUI_FROM_DATABASE=Sollae Systems Co., Ltd. + +OUI:003098* + ID_OUI_FROM_DATABASE=Global Converging Technologies + +OUI:0030E2* + ID_OUI_FROM_DATABASE=GARNET SYSTEMS CO., LTD. + +OUI:003002* + ID_OUI_FROM_DATABASE=Expand Networks + +OUI:00300B* + ID_OUI_FROM_DATABASE=mPHASE Technologies, Inc. + +OUI:00308F* + ID_OUI_FROM_DATABASE=MICRILOR, Inc. + +OUI:0030F3* + ID_OUI_FROM_DATABASE=At Work Computers + +OUI:00D0F9* + ID_OUI_FROM_DATABASE=ACUTE COMMUNICATIONS CORP. + +OUI:00D063* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D069* + ID_OUI_FROM_DATABASE=TECHNOLOGIC SYSTEMS + +OUI:00D070* + ID_OUI_FROM_DATABASE=LONG WELL ELECTRONICS CORP. + +OUI:00D061* + ID_OUI_FROM_DATABASE=TREMON ENTERPRISES CO., LTD. + +OUI:00D0C4* + ID_OUI_FROM_DATABASE=TERATECH CORPORATION + +OUI:0030BF* + ID_OUI_FROM_DATABASE=MULTIDATA GMBH + +OUI:00D0D7* + ID_OUI_FROM_DATABASE=B2C2, INC. + +OUI:00D015* + ID_OUI_FROM_DATABASE=UNIVEX MICROTECHNOLOGY CORP. + +OUI:00D0A5* + ID_OUI_FROM_DATABASE=AMERICAN ARIUM + +OUI:00D0E5* + ID_OUI_FROM_DATABASE=SOLIDUM SYSTEMS CORP. + +OUI:00D0B3* + ID_OUI_FROM_DATABASE=DRS Technologies Canada Ltd + +OUI:00D0E9* + ID_OUI_FROM_DATABASE=Advantage Century Telecommunication Corp. + +OUI:00D094* + ID_OUI_FROM_DATABASE=Seeion Control LLC + +OUI:009045* + ID_OUI_FROM_DATABASE=Marconi Communications + +OUI:0090F6* + ID_OUI_FROM_DATABASE=ESCALATE NETWORKS, INC. + +OUI:0090EA* + ID_OUI_FROM_DATABASE=ALPHA TECHNOLOGIES, INC. + +OUI:0090FE* + ID_OUI_FROM_DATABASE=ELECOM CO., LTD. (LANEED DIV.) + +OUI:0090EB* + ID_OUI_FROM_DATABASE=SENTRY TELECOM SYSTEMS + +OUI:00908E* + ID_OUI_FROM_DATABASE=Nortel Networks Broadband Access + +OUI:0090CA* + ID_OUI_FROM_DATABASE=ACCORD VIDEO TELECOMMUNICATIONS, LTD. + +OUI:00908B* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:009099* + ID_OUI_FROM_DATABASE=ALLIED TELESIS, K.K. + +OUI:00900E* + ID_OUI_FROM_DATABASE=HANDLINK TECHNOLOGIES, INC. + +OUI:0090F7* + ID_OUI_FROM_DATABASE=NBASE COMMUNICATIONS LTD. + +OUI:009024* + ID_OUI_FROM_DATABASE=PIPELINKS, INC. + +OUI:009052* + ID_OUI_FROM_DATABASE=SELCOM ELETTRONICA S.R.L. + +OUI:0090E5* + ID_OUI_FROM_DATABASE=TEKNEMA, INC. + +OUI:009085* + ID_OUI_FROM_DATABASE=GOLDEN ENTERPRISES, INC. + +OUI:009019* + ID_OUI_FROM_DATABASE=HERMES ELECTRONICS CO., LTD. + +OUI:0090DC* + ID_OUI_FROM_DATABASE=TECO INFORMATION SYSTEMS + +OUI:00D0AE* + ID_OUI_FROM_DATABASE=ORESIS COMMUNICATIONS, INC. + +OUI:00D0D4* + ID_OUI_FROM_DATABASE=V-BITS, INC. + +OUI:00D041* + ID_OUI_FROM_DATABASE=AMIGO TECHNOLOGY CO., LTD. + +OUI:00D0D1* + ID_OUI_FROM_DATABASE=Sycamore Networks + +OUI:00D0A1* + ID_OUI_FROM_DATABASE=OSKAR VIERLING GMBH + CO. KG + +OUI:00D00B* + ID_OUI_FROM_DATABASE=RHK TECHNOLOGY, INC. + +OUI:00D02C* + ID_OUI_FROM_DATABASE=CAMPBELL SCIENTIFIC, INC. + +OUI:00D0A0* + ID_OUI_FROM_DATABASE=MIPS DENMARK + +OUI:00D04E* + ID_OUI_FROM_DATABASE=LOGIBAG + +OUI:00D0D9* + ID_OUI_FROM_DATABASE=DEDICATED MICROCOMPUTERS + +OUI:00D0CD* + ID_OUI_FROM_DATABASE=ATAN TECHNOLOGY INC. + +OUI:00D01D* + ID_OUI_FROM_DATABASE=FURUNO ELECTRIC CO., LTD. + +OUI:00D0C7* + ID_OUI_FROM_DATABASE=PATHWAY, INC. + +OUI:00D05C* + ID_OUI_FROM_DATABASE=KATHREIN TechnoTrend GmbH + +OUI:00D040* + ID_OUI_FROM_DATABASE=SYSMATE CO., LTD. + +OUI:00D08A* + ID_OUI_FROM_DATABASE=PHOTRON USA + +OUI:00D076* + ID_OUI_FROM_DATABASE=Bank of America + +OUI:00D07A* + ID_OUI_FROM_DATABASE=AMAQUEST COMPUTER CORP. + +OUI:00D0BB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:00D001* + ID_OUI_FROM_DATABASE=VST TECHNOLOGIES, INC. + +OUI:00904C* + ID_OUI_FROM_DATABASE=Epigram, Inc. + +OUI:009000* + ID_OUI_FROM_DATABASE=DIAMOND MULTIMEDIA + +OUI:009025* + ID_OUI_FROM_DATABASE=BAE Systems Australia (Electronic Systems) Pty Ltd + +OUI:0090F8* + ID_OUI_FROM_DATABASE=MEDIATRIX TELECOM + +OUI:009084* + ID_OUI_FROM_DATABASE=ATECH SYSTEM + +OUI:009054* + ID_OUI_FROM_DATABASE=INNOVATIVE SEMICONDUCTORS, INC + +OUI:009080* + ID_OUI_FROM_DATABASE=NOT LIMITED, INC. + +OUI:0090C0* + ID_OUI_FROM_DATABASE=K.J. LAW ENGINEERS, INC. + +OUI:0090BC* + ID_OUI_FROM_DATABASE=TELEMANN CO., LTD. + +OUI:00900A* + ID_OUI_FROM_DATABASE=PROTON ELECTRONIC INDUSTRIAL CO., LTD. + +OUI:00904E* + ID_OUI_FROM_DATABASE=DELEM BV + +OUI:00904A* + ID_OUI_FROM_DATABASE=CONCUR SYSTEM TECHNOLOGIES + +OUI:009029* + ID_OUI_FROM_DATABASE=CRYPTO AG + +OUI:009061* + ID_OUI_FROM_DATABASE=PACIFIC RESEARCH & ENGINEERING CORPORATION + +OUI:0090A9* + ID_OUI_FROM_DATABASE=WESTERN DIGITAL + +OUI:009072* + ID_OUI_FROM_DATABASE=SIMRAD AS + +OUI:005048* + ID_OUI_FROM_DATABASE=INFOLIBRIA + +OUI:0050EA* + ID_OUI_FROM_DATABASE=XEL COMMUNICATIONS, INC. + +OUI:0050CE* + ID_OUI_FROM_DATABASE=LG INTERNATIONAL CORP. + +OUI:005019* + ID_OUI_FROM_DATABASE=SPRING TIDE NETWORKS, INC. + +OUI:0050AC* + ID_OUI_FROM_DATABASE=MAPLE COMPUTER CORPORATION + +OUI:005044* + ID_OUI_FROM_DATABASE=ASACA CORPORATION + +OUI:0050C6* + ID_OUI_FROM_DATABASE=LOOP TELECOMMUNICATION INTERNATIONAL, INC. + +OUI:005049* + ID_OUI_FROM_DATABASE=Arbor Networks Inc + +OUI:00509F* + ID_OUI_FROM_DATABASE=HORIZON COMPUTER + +OUI:0050C8* + ID_OUI_FROM_DATABASE=Addonics Technologies, Inc. + +OUI:0050DC* + ID_OUI_FROM_DATABASE=TAS TELEFONBAU A. SCHWABE GMBH & CO. KG + +OUI:005069* + ID_OUI_FROM_DATABASE=PixStream Incorporated + +OUI:00901D* + ID_OUI_FROM_DATABASE=PEC (NZ) LTD. + +OUI:00902D* + ID_OUI_FROM_DATABASE=DATA ELECTRONICS (AUST.) PTY, LTD. + +OUI:009007* + ID_OUI_FROM_DATABASE=DOMEX TECHNOLOGY CORP. + +OUI:009048* + ID_OUI_FROM_DATABASE=ZEAL CORPORATION + +OUI:0090E6* + ID_OUI_FROM_DATABASE=ALi Corporation + +OUI:009046* + ID_OUI_FROM_DATABASE=DEXDYNE, LTD. + +OUI:00905E* + ID_OUI_FROM_DATABASE=RAULAND-BORG CORPORATION + +OUI:009067* + ID_OUI_FROM_DATABASE=WalkAbout Computers, Inc. + +OUI:0090DA* + ID_OUI_FROM_DATABASE=DYNARC, INC. + +OUI:009026* + ID_OUI_FROM_DATABASE=ADVANCED SWITCHING COMMUNICATIONS, INC. + +OUI:0090BB* + ID_OUI_FROM_DATABASE=TAINET COMMUNICATION SYSTEM Corp. + +OUI:009033* + ID_OUI_FROM_DATABASE=INNOVAPHONE AG + +OUI:009010* + ID_OUI_FROM_DATABASE=SIMULATION LABORATORIES, INC. + +OUI:00903D* + ID_OUI_FROM_DATABASE=BIOPAC SYSTEMS, INC. + +OUI:009057* + ID_OUI_FROM_DATABASE=AANetcom, Inc. + +OUI:00901C* + ID_OUI_FROM_DATABASE=mps Software Gmbh + +OUI:009056* + ID_OUI_FROM_DATABASE=TELESTREAM, INC. + +OUI:00907D* + ID_OUI_FROM_DATABASE=Lake Communications + +OUI:0090DB* + ID_OUI_FROM_DATABASE=NEXT LEVEL COMMUNICATIONS + +OUI:005042* + ID_OUI_FROM_DATABASE=SCI MANUFACTURING SINGAPORE PTE, LTD. + +OUI:0050C0* + ID_OUI_FROM_DATABASE=GATAN, INC. + +OUI:0050D3* + ID_OUI_FROM_DATABASE=DIGITAL AUDIO PROCESSING PTY. LTD. + +OUI:00509A* + ID_OUI_FROM_DATABASE=TAG ELECTRONIC SYSTEMS + +OUI:00507D* + ID_OUI_FROM_DATABASE=IFP + +OUI:0050D0* + ID_OUI_FROM_DATABASE=MINERVA SYSTEMS + +OUI:005098* + ID_OUI_FROM_DATABASE=GLOBALOOP, LTD. + +OUI:0050FA* + ID_OUI_FROM_DATABASE=OXTEL, LTD. + +OUI:005086* + ID_OUI_FROM_DATABASE=TELKOM SA, LTD. + +OUI:0050E1* + ID_OUI_FROM_DATABASE=NS TECH ELECTRONICS SDN BHD + +OUI:005013* + ID_OUI_FROM_DATABASE=Chaparral Network Storage + +OUI:005022* + ID_OUI_FROM_DATABASE=ZONET TECHNOLOGY, INC. + +OUI:005040* + ID_OUI_FROM_DATABASE=Panasonic Electric Works Co., Ltd. + +OUI:0050D6* + ID_OUI_FROM_DATABASE=ATLAS COPCO TOOLS AB + +OUI:005082* + ID_OUI_FROM_DATABASE=FORESSON CORPORATION + +OUI:0050CA* + ID_OUI_FROM_DATABASE=NET TO NET TECHNOLOGIES + +OUI:0050A6* + ID_OUI_FROM_DATABASE=OPTRONICS + +OUI:0050DB* + ID_OUI_FROM_DATABASE=CONTEMPORARY CONTROL + +OUI:00506B* + ID_OUI_FROM_DATABASE=SPX-ATEG + +OUI:005074* + ID_OUI_FROM_DATABASE=ADVANCED HI-TECH CORP. + +OUI:005047* + ID_OUI_FROM_DATABASE=Private + +OUI:005067* + ID_OUI_FROM_DATABASE=AEROCOMM, INC. + +OUI:005024* + ID_OUI_FROM_DATABASE=NAVIC SYSTEMS, INC. + +OUI:005041* + ID_OUI_FROM_DATABASE=Coretronic Corporation + +OUI:0050D2* + ID_OUI_FROM_DATABASE=CMC Electronics Inc + +OUI:0090DE* + ID_OUI_FROM_DATABASE=CARDKEY SYSTEMS, INC. + +OUI:009060* + ID_OUI_FROM_DATABASE=SYSTEM CREATE CORP. + +OUI:0090F1* + ID_OUI_FROM_DATABASE=DOT HILL SYSTEMS CORPORATION + +OUI:0090E2* + ID_OUI_FROM_DATABASE=DISTRIBUTED PROCESSING TECHNOLOGY + +OUI:00906B* + ID_OUI_FROM_DATABASE=APPLIED RESOURCES, INC. + +OUI:009020* + ID_OUI_FROM_DATABASE=PHILIPS ANALYTICAL X-RAY B.V. + +OUI:009065* + ID_OUI_FROM_DATABASE=FINISAR CORPORATION + +OUI:001053* + ID_OUI_FROM_DATABASE=COMPUTER TECHNOLOGY CORP. + +OUI:0010A3* + ID_OUI_FROM_DATABASE=OMNITRONIX, INC. + +OUI:00102B* + ID_OUI_FROM_DATABASE=UMAX DATA SYSTEMS, INC. + +OUI:001055* + ID_OUI_FROM_DATABASE=FUJITSU MICROELECTRONICS, INC. + +OUI:00103C* + ID_OUI_FROM_DATABASE=IC ENSEMBLE, INC. + +OUI:0010D9* + ID_OUI_FROM_DATABASE=IBM JAPAN, FUJISAWA MT+D + +OUI:0010A5* + ID_OUI_FROM_DATABASE=OXFORD INSTRUMENTS + +OUI:001046* + ID_OUI_FROM_DATABASE=ALCORN MCBRIDE INC. + +OUI:00E0DC* + ID_OUI_FROM_DATABASE=NEXWARE CORP. + +OUI:00E0D9* + ID_OUI_FROM_DATABASE=TAZMO CO., LTD. + +OUI:00E0C2* + ID_OUI_FROM_DATABASE=NECSY S.p.A. + +OUI:00E09B* + ID_OUI_FROM_DATABASE=ENGAGE NETWORKS, INC. + +OUI:00E045* + ID_OUI_FROM_DATABASE=TOUCHWAVE, INC. + +OUI:00E055* + ID_OUI_FROM_DATABASE=INGENIERIA ELECTRONICA COMERCIAL INELCOM S.A. + +OUI:00E037* + ID_OUI_FROM_DATABASE=CENTURY CORPORATION + +OUI:00E081* + ID_OUI_FROM_DATABASE=TYAN COMPUTER CORP. + +OUI:00E0D4* + ID_OUI_FROM_DATABASE=EXCELLENT COMPUTER + +OUI:00E01A* + ID_OUI_FROM_DATABASE=COMTEC SYSTEMS. CO., LTD. + +OUI:00E0BC* + ID_OUI_FROM_DATABASE=SYMON COMMUNICATIONS, INC. + +OUI:00E084* + ID_OUI_FROM_DATABASE=COMPULITE R&D + +OUI:00E0F6* + ID_OUI_FROM_DATABASE=DECISION EUROPE + +OUI:00E027* + ID_OUI_FROM_DATABASE=DUX, INC. + +OUI:00E07F* + ID_OUI_FROM_DATABASE=LOGISTISTEM s.r.l. + +OUI:00E043* + ID_OUI_FROM_DATABASE=VitalCom + +OUI:00E0BF* + ID_OUI_FROM_DATABASE=TORRENT NETWORKING TECHNOLOGIES CORP. + +OUI:00E09D* + ID_OUI_FROM_DATABASE=SARNOFF CORPORATION + +OUI:00E0BB* + ID_OUI_FROM_DATABASE=NBX CORPORATION + +OUI:00E08A* + ID_OUI_FROM_DATABASE=GEC AVERY, LTD. + +OUI:00E04B* + ID_OUI_FROM_DATABASE=JUMP INDUSTRIELLE COMPUTERTECHNIK GmbH + +OUI:001015* + ID_OUI_FROM_DATABASE=OOmon Inc. + +OUI:001088* + ID_OUI_FROM_DATABASE=AMERICAN NETWORKS INC. + +OUI:001008* + ID_OUI_FROM_DATABASE=VIENNA SYSTEMS CORPORATION + +OUI:0010CC* + ID_OUI_FROM_DATABASE=CLP COMPUTER LOGISTIK PLANUNG GmbH + +OUI:001094* + ID_OUI_FROM_DATABASE=Performance Analysis Broadband, Spirent plc + +OUI:0010BB* + ID_OUI_FROM_DATABASE=DATA & INFORMATION TECHNOLOGY + +OUI:001028* + ID_OUI_FROM_DATABASE=COMPUTER TECHNICA, INC. + +OUI:00108A* + ID_OUI_FROM_DATABASE=TeraLogic, Inc. + +OUI:0010C5* + ID_OUI_FROM_DATABASE=PROTOCOL TECHNOLOGIES, INC. + +OUI:00106D* + ID_OUI_FROM_DATABASE=Axxcelera Broadband Wireless + +OUI:0010FC* + ID_OUI_FROM_DATABASE=BROADBAND NETWORKS, INC. + +OUI:001078* + ID_OUI_FROM_DATABASE=NUERA COMMUNICATIONS, INC. + +OUI:001048* + ID_OUI_FROM_DATABASE=HTRC AUTOMATION, INC. + +OUI:001081* + ID_OUI_FROM_DATABASE=DPS, INC. + +OUI:00102D* + ID_OUI_FROM_DATABASE=HITACHI SOFTWARE ENGINEERING + +OUI:00109F* + ID_OUI_FROM_DATABASE=PAVO, INC. + +OUI:0010A1* + ID_OUI_FROM_DATABASE=KENDIN SEMICONDUCTOR, INC. + +OUI:001084* + ID_OUI_FROM_DATABASE=K-BOT COMMUNICATIONS + +OUI:0010AF* + ID_OUI_FROM_DATABASE=TAC SYSTEMS, INC. + +OUI:00100F* + ID_OUI_FROM_DATABASE=INDUSTRIAL CPU SYSTEMS + +OUI:0010A2* + ID_OUI_FROM_DATABASE=TNS + +OUI:001000* + ID_OUI_FROM_DATABASE=CABLE TELEVISION LABORATORIES, INC. + +OUI:00103B* + ID_OUI_FROM_DATABASE=HIPPI NETWORKING FORUM + +OUI:0060C2* + ID_OUI_FROM_DATABASE=MPL AG + +OUI:0060A2* + ID_OUI_FROM_DATABASE=NIHON UNISYS LIMITED CO. + +OUI:006046* + ID_OUI_FROM_DATABASE=VMETRO, INC. + +OUI:00609D* + ID_OUI_FROM_DATABASE=PMI FOOD EQUIPMENT GROUP + +OUI:0060BF* + ID_OUI_FROM_DATABASE=MACRAIGOR SYSTEMS, INC. + +OUI:00604A* + ID_OUI_FROM_DATABASE=SAIC IDEAS GROUP + +OUI:006081* + ID_OUI_FROM_DATABASE=TV/COM INTERNATIONAL + +OUI:0060B4* + ID_OUI_FROM_DATABASE=GLENAYRE R&D INC. + +OUI:006045* + ID_OUI_FROM_DATABASE=PATHLIGHT TECHNOLOGIES + +OUI:00A005* + ID_OUI_FROM_DATABASE=DANIEL INSTRUMENTS, LTD. + +OUI:00A053* + ID_OUI_FROM_DATABASE=COMPACT DEVICES, INC. + +OUI:00A033* + ID_OUI_FROM_DATABASE=imc MeBsysteme GmbH + +OUI:00A059* + ID_OUI_FROM_DATABASE=HAMILTON HALLMARK + +OUI:00A0AD* + ID_OUI_FROM_DATABASE=MARCONI SPA + +OUI:00A0F6* + ID_OUI_FROM_DATABASE=AutoGas Systems Inc. + +OUI:00A096* + ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO., LTD. + +OUI:00A006* + ID_OUI_FROM_DATABASE=IMAGE DATA PROCESSING SYSTEM GROUP + +OUI:0060F3* + ID_OUI_FROM_DATABASE=Performance Analysis Broadband, Spirent plc + +OUI:00600B* + ID_OUI_FROM_DATABASE=LOGWARE GmbH + +OUI:00603F* + ID_OUI_FROM_DATABASE=PATAPSCO DESIGNS + +OUI:00607C* + ID_OUI_FROM_DATABASE=WaveAccess, Ltd. + +OUI:00608D* + ID_OUI_FROM_DATABASE=UNIPULSE CORP. + +OUI:006049* + ID_OUI_FROM_DATABASE=VINA TECHNOLOGIES + +OUI:0060A1* + ID_OUI_FROM_DATABASE=VPNet, Inc. + +OUI:0060C9* + ID_OUI_FROM_DATABASE=ControlNet, Inc. + +OUI:00605F* + ID_OUI_FROM_DATABASE=NIPPON UNISOFT CORPORATION + +OUI:006021* + ID_OUI_FROM_DATABASE=DSC CORPORATION + +OUI:00601D* + ID_OUI_FROM_DATABASE=LUCENT TECHNOLOGIES + +OUI:000800* + ID_OUI_FROM_DATABASE=MULTITECH SYSTEMS, INC. + +OUI:0060C7* + ID_OUI_FROM_DATABASE=AMATI COMMUNICATIONS CORP. + +OUI:00E0CA* + ID_OUI_FROM_DATABASE=BEST DATA PRODUCTS + +OUI:00E097* + ID_OUI_FROM_DATABASE=CARRIER ACCESS CORPORATION + +OUI:00E09F* + ID_OUI_FROM_DATABASE=PIXEL VISION + +OUI:00E0F5* + ID_OUI_FROM_DATABASE=TELES AG + +OUI:00E070* + ID_OUI_FROM_DATABASE=DH TECHNOLOGY + +OUI:00E0B5* + ID_OUI_FROM_DATABASE=ARDENT COMMUNICATIONS CORP. + +OUI:00E073* + ID_OUI_FROM_DATABASE=NATIONAL AMUSEMENT NETWORK, INC. + +OUI:00E0E8* + ID_OUI_FROM_DATABASE=GRETACODER Data Systems AG + +OUI:00E016* + ID_OUI_FROM_DATABASE=RAPID CITY COMMUNICATIONS + +OUI:00E001* + ID_OUI_FROM_DATABASE=STRAND LIGHTING LIMITED + +OUI:00E082* + ID_OUI_FROM_DATABASE=ANERMA + +OUI:00E0EA* + ID_OUI_FROM_DATABASE=INNOVAT COMMUNICATIONS, INC. + +OUI:00E06A* + ID_OUI_FROM_DATABASE=KAPSCH AG + +OUI:00E023* + ID_OUI_FROM_DATABASE=TELRAD + +OUI:00E0C3* + ID_OUI_FROM_DATABASE=SAKAI SYSTEM DEVELOPMENT CORP. + +OUI:00601A* + ID_OUI_FROM_DATABASE=KEITHLEY INSTRUMENTS + +OUI:0060AF* + ID_OUI_FROM_DATABASE=PACIFIC MICRO DATA, INC. + +OUI:00601F* + ID_OUI_FROM_DATABASE=STALLION TECHNOLOGIES + +OUI:00608F* + ID_OUI_FROM_DATABASE=TEKRAM TECHNOLOGY CO., LTD. + +OUI:0060C5* + ID_OUI_FROM_DATABASE=ANCOT CORP. + +OUI:006023* + ID_OUI_FROM_DATABASE=PERICOM SEMICONDUCTOR CORP. + +OUI:006063* + ID_OUI_FROM_DATABASE=PSION DACOM PLC. + +OUI:00604F* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:0060E8* + ID_OUI_FROM_DATABASE=HITACHI COMPUTER PRODUCTS (AMERICA), INC. + +OUI:006072* + ID_OUI_FROM_DATABASE=VXL INSTRUMENTS, LIMITED + +OUI:006054* + ID_OUI_FROM_DATABASE=CONTROLWARE GMBH + +OUI:00A0DC* + ID_OUI_FROM_DATABASE=O.N. ELECTRONIC CO., LTD. + +OUI:00A013* + ID_OUI_FROM_DATABASE=TELTREND LTD. + +OUI:00A0DF* + ID_OUI_FROM_DATABASE=STS TECHNOLOGIES, INC. + +OUI:00A061* + ID_OUI_FROM_DATABASE=PURITAN BENNETT + +OUI:00A0CE* + ID_OUI_FROM_DATABASE=Ecessa + +OUI:00A02A* + ID_OUI_FROM_DATABASE=TRANCELL SYSTEMS + +OUI:00A02C* + ID_OUI_FROM_DATABASE=interWAVE Communications + +OUI:00A077* + ID_OUI_FROM_DATABASE=FUJITSU NEXION, INC. + +OUI:00A020* + ID_OUI_FROM_DATABASE=CITICORP/TTI + +OUI:00A00D* + ID_OUI_FROM_DATABASE=THE PANDA PROJECT + +OUI:00A031* + ID_OUI_FROM_DATABASE=HAZELTINE CORPORATION, MS 1-17 + +OUI:00A041* + ID_OUI_FROM_DATABASE=INFICON + +OUI:0060FA* + ID_OUI_FROM_DATABASE=EDUCATIONAL TECHNOLOGY RESOURCES, INC. + +OUI:000288* + ID_OUI_FROM_DATABASE=GLOBAL VILLAGE COMMUNICATION + +OUI:0060F9* + ID_OUI_FROM_DATABASE=DIAMOND LANE COMMUNICATIONS + +OUI:0060EA* + ID_OUI_FROM_DATABASE=StreamLogic + +OUI:0060EC* + ID_OUI_FROM_DATABASE=HERMARY OPTO ELECTRONICS INC. + +OUI:00604E* + ID_OUI_FROM_DATABASE=CYCLE COMPUTER CORPORATION, INC. + +OUI:00602C* + ID_OUI_FROM_DATABASE=LINX Data Terminals, Inc. + +OUI:006028* + ID_OUI_FROM_DATABASE=MACROVISION CORPORATION + +OUI:00606A* + ID_OUI_FROM_DATABASE=MITSUBISHI WIRELESS COMMUNICATIONS. INC. + +OUI:00E021* + ID_OUI_FROM_DATABASE=FREEGATE CORP. + +OUI:00E0AB* + ID_OUI_FROM_DATABASE=DIMAT S.A. + +OUI:00E0B6* + ID_OUI_FROM_DATABASE=Entrada Networks + +OUI:00E0EC* + ID_OUI_FROM_DATABASE=CELESTICA INC. + +OUI:00E038* + ID_OUI_FROM_DATABASE=PROXIMA CORPORATION + +OUI:00E090* + ID_OUI_FROM_DATABASE=BECKMAN LAB. AUTOMATION DIV. + +OUI:00E02E* + ID_OUI_FROM_DATABASE=SPC ELECTRONICS CORPORATION + +OUI:00E0F4* + ID_OUI_FROM_DATABASE=INSIDE Technology A/S + +OUI:00E03C* + ID_OUI_FROM_DATABASE=AdvanSys + +OUI:00E096* + ID_OUI_FROM_DATABASE=SHIMADZU CORPORATION + +OUI:00E0F1* + ID_OUI_FROM_DATABASE=THAT CORPORATION + +OUI:00A0D0* + ID_OUI_FROM_DATABASE=TEN X TECHNOLOGY, INC. + +OUI:00A0E0* + ID_OUI_FROM_DATABASE=TENNYSON TECHNOLOGIES PTY LTD + +OUI:00A099* + ID_OUI_FROM_DATABASE=K-NET LTD. + +OUI:00A03D* + ID_OUI_FROM_DATABASE=OPTO-22 + +OUI:00A08C* + ID_OUI_FROM_DATABASE=MultiMedia LANs, Inc. + +OUI:1000E8* + ID_OUI_FROM_DATABASE=NATIONAL SEMICONDUCTOR + +OUI:006076* + ID_OUI_FROM_DATABASE=SCHLUMBERGER TECHNOLOGIES RETAIL PETROLEUM SYSTEMS + +OUI:0060AE* + ID_OUI_FROM_DATABASE=TRIO INFORMATION SYSTEMS AB + +OUI:00606C* + ID_OUI_FROM_DATABASE=ARESCOM + +OUI:006032* + ID_OUI_FROM_DATABASE=I-CUBE, INC. + +OUI:006060* + ID_OUI_FROM_DATABASE=Data Innovations North America + +OUI:00A0EB* + ID_OUI_FROM_DATABASE=Encore Networks, Inc. + +OUI:00A0C1* + ID_OUI_FROM_DATABASE=ORTIVUS MEDICAL AB + +OUI:00A07D* + ID_OUI_FROM_DATABASE=SEEQ TECHNOLOGY, INC. + +OUI:00A0CF* + ID_OUI_FROM_DATABASE=SOTAS, INC. + +OUI:00A03A* + ID_OUI_FROM_DATABASE=KUBOTEK CORPORATION + +OUI:00A0D7* + ID_OUI_FROM_DATABASE=KASTEN CHASE APPLIED RESEARCH + +OUI:00A09D* + ID_OUI_FROM_DATABASE=JOHNATHON FREEMAN TECHNOLOGIES + +OUI:00A036* + ID_OUI_FROM_DATABASE=APPLIED NETWORK TECHNOLOGY + +OUI:00A0D2* + ID_OUI_FROM_DATABASE=ALLIED TELESIS INTERNATIONAL CORPORATION + +OUI:00A075* + ID_OUI_FROM_DATABASE=MICRON TECHNOLOGY, INC. + +OUI:00A009* + ID_OUI_FROM_DATABASE=WHITETREE NETWORK + +OUI:00A060* + ID_OUI_FROM_DATABASE=ACER PERIPHERALS, INC. + +OUI:00A00C* + ID_OUI_FROM_DATABASE=KINGMAX TECHNOLOGY, INC. + +OUI:0020FD* + ID_OUI_FROM_DATABASE=ITV TECHNOLOGIES, INC. + +OUI:00200D* + ID_OUI_FROM_DATABASE=CARL ZEISS + +OUI:002091* + ID_OUI_FROM_DATABASE=J125, NATIONAL SECURITY AGENCY + +OUI:002054* + ID_OUI_FROM_DATABASE=Sycamore Networks + +OUI:0020A7* + ID_OUI_FROM_DATABASE=PAIRGAIN TECHNOLOGIES, INC. + +OUI:0020DA* + ID_OUI_FROM_DATABASE=Alcatel North America ESD + +OUI:002005* + ID_OUI_FROM_DATABASE=SIMPLE TECHNOLOGY + +OUI:00202B* + ID_OUI_FROM_DATABASE=ADVANCED TELECOMMUNICATIONS MODULES, LTD. + +OUI:002086* + ID_OUI_FROM_DATABASE=MICROTECH ELECTRONICS LIMITED + +OUI:002052* + ID_OUI_FROM_DATABASE=RAGULA SYSTEMS + +OUI:002090* + ID_OUI_FROM_DATABASE=ADVANCED COMPRESSION TECHNOLOGY, INC. + +OUI:0020A3* + ID_OUI_FROM_DATABASE=Harmonic, Inc + +OUI:00206A* + ID_OUI_FROM_DATABASE=OSAKA COMPUTER CORP. + +OUI:0020DB* + ID_OUI_FROM_DATABASE=XNET TECHNOLOGY, INC. + +OUI:0020A4* + ID_OUI_FROM_DATABASE=MULTIPOINT NETWORKS + +OUI:00201C* + ID_OUI_FROM_DATABASE=EXCEL, INC. + +OUI:00209B* + ID_OUI_FROM_DATABASE=ERSAT ELECTRONIC GMBH + +OUI:0020C9* + ID_OUI_FROM_DATABASE=VICTRON BV + +OUI:0020D1* + ID_OUI_FROM_DATABASE=MICROCOMPUTER SYSTEMS (M) SDN. + +OUI:002084* + ID_OUI_FROM_DATABASE=OCE PRINTING SYSTEMS, GMBH + +OUI:0020C2* + ID_OUI_FROM_DATABASE=TEXAS MEMORY SYSTEMS, INC. + +OUI:0020C8* + ID_OUI_FROM_DATABASE=LARSCOM INCORPORATED + +OUI:0020EC* + ID_OUI_FROM_DATABASE=TECHWARE SYSTEMS CORP. + +OUI:002083* + ID_OUI_FROM_DATABASE=PRESTICOM INCORPORATED + +OUI:00206D* + ID_OUI_FROM_DATABASE=DATA RACE, INC. + +OUI:00203A* + ID_OUI_FROM_DATABASE=DIGITAL BI0METRICS INC. + +OUI:00A06C* + ID_OUI_FROM_DATABASE=SHINDENGEN ELECTRIC MFG. CO., LTD. + +OUI:00A0EE* + ID_OUI_FROM_DATABASE=NASHOBA NETWORKS + +OUI:00A0FB* + ID_OUI_FROM_DATABASE=TORAY ENGINEERING CO., LTD. + +OUI:00A0E3* + ID_OUI_FROM_DATABASE=XKL SYSTEMS CORP. + +OUI:00A01E* + ID_OUI_FROM_DATABASE=EST CORPORATION + +OUI:00A080* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:00A0C2* + ID_OUI_FROM_DATABASE=R.A. SYSTEMS CO., LTD. + +OUI:00A0CB* + ID_OUI_FROM_DATABASE=ARK TELECOMMUNICATIONS, INC. + +OUI:00A074* + ID_OUI_FROM_DATABASE=PERCEPTION TECHNOLOGY + +OUI:00A06A* + ID_OUI_FROM_DATABASE=Verilink Corporation + +OUI:00A070* + ID_OUI_FROM_DATABASE=COASTCOM + +OUI:00A079* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC (USA), INC. + +OUI:002059* + ID_OUI_FROM_DATABASE=MIRO COMPUTER PRODUCTS AG + +OUI:0020BC* + ID_OUI_FROM_DATABASE=Long Reach Networks Pty Ltd + +OUI:0020AD* + ID_OUI_FROM_DATABASE=LINQ SYSTEMS + +OUI:002046* + ID_OUI_FROM_DATABASE=CIPRICO, INC. + +OUI:002071* + ID_OUI_FROM_DATABASE=IBR GMBH + +OUI:0020A2* + ID_OUI_FROM_DATABASE=GALCOM NETWORKING LTD. + +OUI:002098* + ID_OUI_FROM_DATABASE=HECTRONIC AB + +OUI:002065* + ID_OUI_FROM_DATABASE=SUPERNET NETWORKING INC. + +OUI:002094* + ID_OUI_FROM_DATABASE=CUBIX CORPORATION + +OUI:0020C3* + ID_OUI_FROM_DATABASE=COUNTER SOLUTIONS LTD. + +OUI:0020A5* + ID_OUI_FROM_DATABASE=API ENGINEERING + +OUI:002070* + ID_OUI_FROM_DATABASE=HYNET, LTD. + +OUI:00201E* + ID_OUI_FROM_DATABASE=NETQUEST CORPORATION + +OUI:002097* + ID_OUI_FROM_DATABASE=APPLIED SIGNAL TECHNOLOGY + +OUI:0020E8* + ID_OUI_FROM_DATABASE=DATATREK CORPORATION + +OUI:00204F* + ID_OUI_FROM_DATABASE=DEUTSCHE AEROSPACE AG + +OUI:00202E* + ID_OUI_FROM_DATABASE=DAYSTAR DIGITAL + +OUI:0020B0* + ID_OUI_FROM_DATABASE=GATEWAY DEVICES, INC. + +OUI:0020A9* + ID_OUI_FROM_DATABASE=WHITE HORSE INDUSTRIAL + +OUI:002061* + ID_OUI_FROM_DATABASE=GarrettCom, Inc. + +OUI:0020C6* + ID_OUI_FROM_DATABASE=NECTEC + +OUI:0020D2* + ID_OUI_FROM_DATABASE=RAD DATA COMMUNICATIONS, LTD. + +OUI:00A0F8* + ID_OUI_FROM_DATABASE=Zebra Technologies Inc + +OUI:00A025* + ID_OUI_FROM_DATABASE=REDCOM LABS INC. + +OUI:00A0D4* + ID_OUI_FROM_DATABASE=RADIOLAN, INC. + +OUI:00A08A* + ID_OUI_FROM_DATABASE=BROOKTROUT TECHNOLOGY, INC. + +OUI:002093* + ID_OUI_FROM_DATABASE=LANDINGS TECHNOLOGY CORP. + +OUI:002056* + ID_OUI_FROM_DATABASE=NEOPRODUCTS + +OUI:0020A6* + ID_OUI_FROM_DATABASE=Proxim Wireless + +OUI:00C073* + ID_OUI_FROM_DATABASE=XEDIA CORPORATION + +OUI:00C0D4* + ID_OUI_FROM_DATABASE=AXON NETWORKS, INC. + +OUI:00C0E5* + ID_OUI_FROM_DATABASE=GESPAC, S.A. + +OUI:00A0CA* + ID_OUI_FROM_DATABASE=FUJITSU DENSO LTD. + +OUI:00A029* + ID_OUI_FROM_DATABASE=COULTER CORPORATION + +OUI:00C088* + ID_OUI_FROM_DATABASE=EKF ELEKTRONIK GMBH + +OUI:00C056* + ID_OUI_FROM_DATABASE=SOMELEC + +OUI:00C063* + ID_OUI_FROM_DATABASE=MORNING STAR TECHNOLOGIES, INC + +OUI:00C021* + ID_OUI_FROM_DATABASE=NETEXPRESS + +OUI:00C049* + ID_OUI_FROM_DATABASE=U.S. ROBOTICS, INC. + +OUI:00C032* + ID_OUI_FROM_DATABASE=I-CUBED LIMITED + +OUI:00C051* + ID_OUI_FROM_DATABASE=ADVANCED INTEGRATION RESEARCH + +OUI:00C085* + ID_OUI_FROM_DATABASE=ELECTRONICS FOR IMAGING, INC. + +OUI:00C0FE* + ID_OUI_FROM_DATABASE=APTEC COMPUTER SYSTEMS, INC. + +OUI:00C0E8* + ID_OUI_FROM_DATABASE=PLEXCOM, INC. + +OUI:00C0B2* + ID_OUI_FROM_DATABASE=NORAND CORPORATION + +OUI:00C0B1* + ID_OUI_FROM_DATABASE=GENIUS NET CO. + +OUI:00C0D9* + ID_OUI_FROM_DATABASE=QUINTE NETWORK CONFIDENTIALITY + +OUI:00C038* + ID_OUI_FROM_DATABASE=RASTER IMAGE PROCESSING SYSTEM + +OUI:00C098* + ID_OUI_FROM_DATABASE=CHUNTEX ELECTRONIC CO., LTD. + +OUI:00C0DD* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:00C08A* + ID_OUI_FROM_DATABASE=Lauterbach GmbH + +OUI:0040FF* + ID_OUI_FROM_DATABASE=TELEBIT CORPORATION + +OUI:0040D7* + ID_OUI_FROM_DATABASE=STUDIO GEN INC. + +OUI:004007* + ID_OUI_FROM_DATABASE=TELMAT INFORMATIQUE + +OUI:00408D* + ID_OUI_FROM_DATABASE=THE GOODYEAR TIRE & RUBBER CO. + +OUI:00402C* + ID_OUI_FROM_DATABASE=ISIS DISTRIBUTED SYSTEMS, INC. + +OUI:00C03D* + ID_OUI_FROM_DATABASE=WIESEMANN & THEIS GMBH + +OUI:00C026* + ID_OUI_FROM_DATABASE=LANS TECHNOLOGY CO., LTD. + +OUI:0040E2* + ID_OUI_FROM_DATABASE=MESA RIDGE TECHNOLOGIES, INC. + +OUI:004078* + ID_OUI_FROM_DATABASE=WEARNES AUTOMATION PTE LTD + +OUI:004062* + ID_OUI_FROM_DATABASE=E-SYSTEMS, INC./GARLAND DIV. + +OUI:0040D2* + ID_OUI_FROM_DATABASE=PAGINE CORPORATION + +OUI:0040D0* + ID_OUI_FROM_DATABASE=MITAC INTERNATIONAL CORP. + +OUI:0040E4* + ID_OUI_FROM_DATABASE=E-M TECHNOLOGY, INC. + +OUI:0040BF* + ID_OUI_FROM_DATABASE=CHANNEL SYSTEMS INTERN'L INC. + +OUI:004094* + ID_OUI_FROM_DATABASE=SHOGRAPHICS, INC. + +OUI:00407F* + ID_OUI_FROM_DATABASE=FLIR Systems + +OUI:0040A9* + ID_OUI_FROM_DATABASE=DATACOM INC. + +OUI:00C07D* + ID_OUI_FROM_DATABASE=RISC DEVELOPMENTS LTD. + +OUI:00C01E* + ID_OUI_FROM_DATABASE=LA FRANCAISE DES JEUX + +OUI:00C084* + ID_OUI_FROM_DATABASE=DATA LINK CORP. LTD. + +OUI:00C087* + ID_OUI_FROM_DATABASE=UUNET TECHNOLOGIES, INC. + +OUI:00C033* + ID_OUI_FROM_DATABASE=TELEBIT COMMUNICATIONS APS + +OUI:00C081* + ID_OUI_FROM_DATABASE=METRODATA LTD. + +OUI:00C006* + ID_OUI_FROM_DATABASE=NIPPON AVIONICS CO., LTD. + +OUI:00C013* + ID_OUI_FROM_DATABASE=NETRIX + +OUI:00C058* + ID_OUI_FROM_DATABASE=DATAEXPERT CORP. + +OUI:0040E8* + ID_OUI_FROM_DATABASE=CHARLES RIVER DATA SYSTEMS,INC + +OUI:004030* + ID_OUI_FROM_DATABASE=GK COMPUTER + +OUI:0080DC* + ID_OUI_FROM_DATABASE=PICKER INTERNATIONAL + +OUI:00C0A8* + ID_OUI_FROM_DATABASE=GVC CORPORATION + +OUI:00C010* + ID_OUI_FROM_DATABASE=HIRAKAWA HEWTECH CORP. + +OUI:00C020* + ID_OUI_FROM_DATABASE=ARCO ELECTRONIC, CONTROL LTD. + +OUI:0040A6* + ID_OUI_FROM_DATABASE=Cray, Inc. + +OUI:004098* + ID_OUI_FROM_DATABASE=DRESSLER GMBH & CO. + +OUI:00C0B9* + ID_OUI_FROM_DATABASE=FUNK SOFTWARE, INC. + +OUI:00C065* + ID_OUI_FROM_DATABASE=SCOPE COMMUNICATIONS, INC. + +OUI:00C018* + ID_OUI_FROM_DATABASE=LANART CORPORATION + +OUI:00C0FF* + ID_OUI_FROM_DATABASE=DOT HILL SYSTEMS CORPORATION + +OUI:00400D* + ID_OUI_FROM_DATABASE=LANNET DATA COMMUNICATIONS,LTD + +OUI:0040F5* + ID_OUI_FROM_DATABASE=OEM ENGINES + +OUI:004019* + ID_OUI_FROM_DATABASE=AEON SYSTEMS, INC. + +OUI:0040A1* + ID_OUI_FROM_DATABASE=ERGO COMPUTING + +OUI:00407E* + ID_OUI_FROM_DATABASE=EVERGREEN SYSTEMS, INC. + +OUI:0040F6* + ID_OUI_FROM_DATABASE=KATRON COMPUTERS INC. + +OUI:004076* + ID_OUI_FROM_DATABASE=Sun Conversion Technologies + +OUI:0040F4* + ID_OUI_FROM_DATABASE=CAMEO COMMUNICATIONS, INC. + +OUI:00C06D* + ID_OUI_FROM_DATABASE=BOCA RESEARCH, INC. + +OUI:00C0DB* + ID_OUI_FROM_DATABASE=IPC CORPORATION (PTE) LTD. + +OUI:00C0DA* + ID_OUI_FROM_DATABASE=NICE SYSTEMS LTD. + +OUI:00C09B* + ID_OUI_FROM_DATABASE=RELIANCE COMM/TEC, R-TEC + +OUI:00C0B8* + ID_OUI_FROM_DATABASE=FRASER'S HILL LTD. + +OUI:00C016* + ID_OUI_FROM_DATABASE=ELECTRONIC THEATRE CONTROLS + +OUI:00C096* + ID_OUI_FROM_DATABASE=TAMURA CORPORATION + +OUI:00C035* + ID_OUI_FROM_DATABASE=QUINTAR COMPANY + +OUI:00C0CC* + ID_OUI_FROM_DATABASE=TELESCIENCES CO SYSTEMS, INC. + +OUI:00C078* + ID_OUI_FROM_DATABASE=COMPUTER SYSTEMS ENGINEERING + +OUI:0040F3* + ID_OUI_FROM_DATABASE=NETCOR + +OUI:004033* + ID_OUI_FROM_DATABASE=ADDTRON TECHNOLOGY CO., LTD. + +OUI:0040A3* + ID_OUI_FROM_DATABASE=MICROUNITY SYSTEMS ENGINEERING + +OUI:0040ED* + ID_OUI_FROM_DATABASE=NETWORK CONTROLS INT'NATL INC. + +OUI:0040AD* + ID_OUI_FROM_DATABASE=SMA REGELSYSTEME GMBH + +OUI:0080D2* + ID_OUI_FROM_DATABASE=SHINNIHONDENKO CO., LTD. + +OUI:0080DF* + ID_OUI_FROM_DATABASE=ADC CODENOLL TECHNOLOGY CORP. + +OUI:008071* + ID_OUI_FROM_DATABASE=SAI TECHNOLOGY + +OUI:00803D* + ID_OUI_FROM_DATABASE=SURIGIKEN CO., LTD. + +OUI:00804B* + ID_OUI_FROM_DATABASE=EAGLE TECHNOLOGIES PTY.LTD. + +OUI:008007* + ID_OUI_FROM_DATABASE=DLOG NC-SYSTEME + +OUI:008001* + ID_OUI_FROM_DATABASE=PERIPHONICS CORPORATION + +OUI:008062* + ID_OUI_FROM_DATABASE=INTERFACE CO. + +OUI:0080F3* + ID_OUI_FROM_DATABASE=SUN ELECTRONICS CORP. + +OUI:00808D* + ID_OUI_FROM_DATABASE=WESTCOAST TECHNOLOGY B.V. + +OUI:0080B2* + ID_OUI_FROM_DATABASE=NETWORK EQUIPMENT TECHNOLOGIES + +OUI:00805B* + ID_OUI_FROM_DATABASE=CONDOR SYSTEMS, INC. + +OUI:00801C* + ID_OUI_FROM_DATABASE=NEWPORT SYSTEMS SOLUTIONS + +OUI:0080C6* + ID_OUI_FROM_DATABASE=NATIONAL DATACOMM CORPORATION + +OUI:0080FA* + ID_OUI_FROM_DATABASE=RWT GMBH + +OUI:008084* + ID_OUI_FROM_DATABASE=THE CLOUD INC. + +OUI:008046* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:0080A6* + ID_OUI_FROM_DATABASE=REPUBLIC TECHNOLOGY, INC. + +OUI:008009* + ID_OUI_FROM_DATABASE=JUPITER SYSTEMS, INC. + +OUI:0080B5* + ID_OUI_FROM_DATABASE=UNITED NETWORKS INC. + +OUI:008035* + ID_OUI_FROM_DATABASE=TECHNOLOGY WORKS, INC. + +OUI:008088* + ID_OUI_FROM_DATABASE=VICTOR COMPANY OF JAPAN, LTD. + +OUI:00809E* + ID_OUI_FROM_DATABASE=DATUS GMBH + +OUI:008055* + ID_OUI_FROM_DATABASE=FERMILAB + +OUI:00802A* + ID_OUI_FROM_DATABASE=TEST SYSTEMS & SIMULATIONS INC + +OUI:0040E3* + ID_OUI_FROM_DATABASE=QUIN SYSTEMS LTD + +OUI:004091* + ID_OUI_FROM_DATABASE=PROCOMP INDUSTRIA ELETRONICA + +OUI:004014* + ID_OUI_FROM_DATABASE=COMSOFT GMBH + +OUI:00400F* + ID_OUI_FROM_DATABASE=DATACOM TECHNOLOGIES + +OUI:004085* + ID_OUI_FROM_DATABASE=SAAB INSTRUMENTS AB + +OUI:004006* + ID_OUI_FROM_DATABASE=SAMPO TECHNOLOGY CORPORATION + +OUI:00402D* + ID_OUI_FROM_DATABASE=HARRIS ADACOM CORPORATION + +OUI:004047* + ID_OUI_FROM_DATABASE=WIND RIVER SYSTEMS + +OUI:0040FA* + ID_OUI_FROM_DATABASE=MICROBOARDS, INC. + +OUI:00002E* + ID_OUI_FROM_DATABASE=SOCIETE EVIRA + +OUI:0000ED* + ID_OUI_FROM_DATABASE=APRIL + +OUI:00003C* + ID_OUI_FROM_DATABASE=AUSPEX SYSTEMS INC. + +OUI:000051* + ID_OUI_FROM_DATABASE=HOB ELECTRONIC GMBH & CO. KG + +OUI:0000A7* + ID_OUI_FROM_DATABASE=NETWORK COMPUTING DEVICES INC. + +OUI:0000F7* + ID_OUI_FROM_DATABASE=YOUTH KEEP ENTERPRISE CO LTD + +OUI:0000FC* + ID_OUI_FROM_DATABASE=MEIKO + +OUI:0000B5* + ID_OUI_FROM_DATABASE=DATABILITY SOFTWARE SYS. INC. + +OUI:000026* + ID_OUI_FROM_DATABASE=SHA-KEN CO., LTD. + +OUI:000022* + ID_OUI_FROM_DATABASE=VISUAL TECHNOLOGY INC. + +OUI:00006D* + ID_OUI_FROM_DATABASE=CRAY COMMUNICATIONS, LTD. + +OUI:0000FA* + ID_OUI_FROM_DATABASE=MICROSAGE COMPUTER SYSTEMS INC + +OUI:00002B* + ID_OUI_FROM_DATABASE=CRISP AUTOMATION, INC + +OUI:000019* + ID_OUI_FROM_DATABASE=APPLIED DYNAMICS INTERNATIONAL + +OUI:0080D3* + ID_OUI_FROM_DATABASE=SHIVA CORP. + +OUI:0080A5* + ID_OUI_FROM_DATABASE=SPEED INTERNATIONAL + +OUI:0080A9* + ID_OUI_FROM_DATABASE=CLEARPOINT RESEARCH + +OUI:008069* + ID_OUI_FROM_DATABASE=COMPUTONE SYSTEMS + +OUI:008091* + ID_OUI_FROM_DATABASE=TOKYO ELECTRIC CO.,LTD + +OUI:0080F4* + ID_OUI_FROM_DATABASE=TELEMECANIQUE ELECTRIQUE + +OUI:00800C* + ID_OUI_FROM_DATABASE=VIDECOM LIMITED + +OUI:0080E8* + ID_OUI_FROM_DATABASE=CUMULUS CORPORATIION + +OUI:0000CD* + ID_OUI_FROM_DATABASE=Allied Telesis Labs Ltd + +OUI:0000A5* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:00801E* + ID_OUI_FROM_DATABASE=XINETRON, INC. + +OUI:00804A* + ID_OUI_FROM_DATABASE=PRO-LOG + +OUI:008059* + ID_OUI_FROM_DATABASE=STANLEY ELECTRIC CO., LTD + +OUI:00806B* + ID_OUI_FROM_DATABASE=SCHMID TELECOMMUNICATION + +OUI:00802C* + ID_OUI_FROM_DATABASE=THE SAGE GROUP PLC + +OUI:008018* + ID_OUI_FROM_DATABASE=KOBE STEEL, LTD. + +OUI:0080EE* + ID_OUI_FROM_DATABASE=THOMSON CSF + +OUI:008013* + ID_OUI_FROM_DATABASE=THOMAS-CONRAD CORPORATION + +OUI:00808E* + ID_OUI_FROM_DATABASE=RADSTONE TECHNOLOGY + +OUI:000036* + ID_OUI_FROM_DATABASE=ATARI CORPORATION + +OUI:0080BD* + ID_OUI_FROM_DATABASE=THE FURUKAWA ELECTRIC CO., LTD + +OUI:0080A8* + ID_OUI_FROM_DATABASE=VITACOM CORPORATION + +OUI:008042* + ID_OUI_FROM_DATABASE=Artesyn Embedded Technologies + +OUI:008067* + ID_OUI_FROM_DATABASE=SQUARE D COMPANY + +OUI:008045* + ID_OUI_FROM_DATABASE=MATSUSHITA ELECTRIC IND. CO + +OUI:00804C* + ID_OUI_FROM_DATABASE=CONTEC CO., LTD. + +OUI:008020* + ID_OUI_FROM_DATABASE=NETWORK PRODUCTS + +OUI:004044* + ID_OUI_FROM_DATABASE=QNIX COMPUTER CO., LTD. + +OUI:0040DD* + ID_OUI_FROM_DATABASE=HONG TECHNOLOGIES + +OUI:00403A* + ID_OUI_FROM_DATABASE=IMPACT TECHNOLOGIES + +OUI:0040C9* + ID_OUI_FROM_DATABASE=NCUBE + +OUI:004075* + ID_OUI_FROM_DATABASE=Tattile SRL + +OUI:0080F1* + ID_OUI_FROM_DATABASE=OPUS SYSTEMS + +OUI:08008F* + ID_OUI_FROM_DATABASE=CHIPCOM CORPORATION + +OUI:080081* + ID_OUI_FROM_DATABASE=ASTECH INC. + +OUI:08007A* + ID_OUI_FROM_DATABASE=INDATA + +OUI:080078* + ID_OUI_FROM_DATABASE=ACCELL CORPORATION + +OUI:08006E* + ID_OUI_FROM_DATABASE=MASSCOMP + +OUI:08006D* + ID_OUI_FROM_DATABASE=WHITECHAPEL COMPUTER WORKS + +OUI:08006C* + ID_OUI_FROM_DATABASE=SUNTEK TECHNOLOGY INT'L + +OUI:080067* + ID_OUI_FROM_DATABASE=ComDesign + +OUI:080063* + ID_OUI_FROM_DATABASE=PLESSEY + +OUI:080060* + ID_OUI_FROM_DATABASE=INDUSTRIAL NETWORKING INC. + +OUI:000081* + ID_OUI_FROM_DATABASE=Bay Networks + +OUI:0000A1* + ID_OUI_FROM_DATABASE=MARQUETTE ELECTRIC CO. + +OUI:0000F5* + ID_OUI_FROM_DATABASE=DIAMOND SALES LIMITED + +OUI:0000E5* + ID_OUI_FROM_DATABASE=SIGMEX LTD. + +OUI:0000BA* + ID_OUI_FROM_DATABASE=SIIG, INC. + +OUI:00002F* + ID_OUI_FROM_DATABASE=TIMEPLEX INC. + +OUI:0000B8* + ID_OUI_FROM_DATABASE=SEIKOSHA CO., LTD. + +OUI:00007F* + ID_OUI_FROM_DATABASE=LINOTYPE-HELL AG + +OUI:0000B7* + ID_OUI_FROM_DATABASE=DOVE COMPUTER CORPORATION + +OUI:00009A* + ID_OUI_FROM_DATABASE=RC COMPUTER A/S + +OUI:0000DE* + ID_OUI_FROM_DATABASE=CETIA + +OUI:00004B* + ID_OUI_FROM_DATABASE=ICL DATA OY + +OUI:000013* + ID_OUI_FROM_DATABASE=CAMEX + +OUI:000095* + ID_OUI_FROM_DATABASE=SONY TEKTRONIX CORP. + +OUI:080037* + ID_OUI_FROM_DATABASE=FUJI-XEROX CO. LTD. + +OUI:080031* + ID_OUI_FROM_DATABASE=LITTLE MACHINES INC. + +OUI:08002B* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:08002A* + ID_OUI_FROM_DATABASE=MOSAIC TECHNOLOGIES INC. + +OUI:080029* + ID_OUI_FROM_DATABASE=Megatek Corporation + +OUI:080026* + ID_OUI_FROM_DATABASE=NORSK DATA A.S. + +OUI:08001F* + ID_OUI_FROM_DATABASE=SHARP CORPORATION + +OUI:0000AE* + ID_OUI_FROM_DATABASE=DASSAULT ELECTRONIQUE + +OUI:0000DD* + ID_OUI_FROM_DATABASE=TCL INCORPORATED + +OUI:0000D9* + ID_OUI_FROM_DATABASE=NIPPON TELEGRAPH & TELEPHONE + +OUI:000046* + ID_OUI_FROM_DATABASE=OLIVETTI NORTH AMERICA + +OUI:000017* + ID_OUI_FROM_DATABASE=Oracle + +OUI:00009F* + ID_OUI_FROM_DATABASE=AMERISTAR TECHNOLOGIES INC. + +OUI:0000E3* + ID_OUI_FROM_DATABASE=INTEGRATED MICRO PRODUCTS LTD + +OUI:000073* + ID_OUI_FROM_DATABASE=SIECOR CORPORATION + +OUI:0000D3* + ID_OUI_FROM_DATABASE=WANG LABORATORIES INC. + +OUI:0000B3* + ID_OUI_FROM_DATABASE=CIMLINC INCORPORATED + +OUI:00009D* + ID_OUI_FROM_DATABASE=LOCUS COMPUTING CORPORATION + +OUI:000060* + ID_OUI_FROM_DATABASE=KONTRON ELEKTRONIK GMBH + +OUI:000011* + ID_OUI_FROM_DATABASE=NORMEREL SYSTEMES + +OUI:08006F* + ID_OUI_FROM_DATABASE=PHILIPS APELDOORN B.V. + +OUI:0000B0* + ID_OUI_FROM_DATABASE=RND-RAD NETWORK DEVICES + +OUI:000071* + ID_OUI_FROM_DATABASE=ADRA SYSTEMS INC. + +OUI:00006C* + ID_OUI_FROM_DATABASE=Private + +OUI:AA0000* + ID_OUI_FROM_DATABASE=DIGITAL EQUIPMENT CORPORATION + +OUI:0270B0* + ID_OUI_FROM_DATABASE=M/A-COM INC. COMPANIES + +OUI:00000B* + ID_OUI_FROM_DATABASE=MATRIX CORPORATION + +OUI:080042* + ID_OUI_FROM_DATABASE=JAPAN MACNICS CORP. + +OUI:026086* + ID_OUI_FROM_DATABASE=LOGIC REPLACEMENT TECH. LTD. + +OUI:00DD05* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:00BBF0* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:0080E9* + ID_OUI_FROM_DATABASE=Madge Ltd. + +OUI:080055* + ID_OUI_FROM_DATABASE=STANFORD TELECOMM. INC. + +OUI:080048* + ID_OUI_FROM_DATABASE=EUROTHERM GAUGING SYSTEMS + +OUI:080049* + ID_OUI_FROM_DATABASE=UNIVATION + +OUI:00DD02* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:000003* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:000008* + ID_OUI_FROM_DATABASE=XEROX CORPORATION + +OUI:080030* + ID_OUI_FROM_DATABASE=CERN + +OUI:00DD01* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:18017D* + ID_OUI_FROM_DATABASE=Harbin Arteor technology co., LTD + +OUI:001CDF* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:944452* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:08863B* + ID_OUI_FROM_DATABASE=Belkin International Inc. + +OUI:2082C0* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + +OUI:001556* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:002569* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:001BBF* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:4C17EB* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:7C034C* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:88AE1D* + ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. + +OUI:5C353B* + ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc. + +OUI:C8F230* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:1C4419* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:749DDC* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:782BCB* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:B8CA3A* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F01FAF* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:C81F66* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00183F* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:0019E4* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001AC4* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001D5A* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:34EF44* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:982CBE* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:001422* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001C23* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00219B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:000874* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:002564* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:842B2B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:E0DB55* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:A41F72* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:00C04F* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F04DA2* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:BC305B* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:001D09* + ID_OUI_FROM_DATABASE=Dell Inc. + +OUI:F8E079* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:1430C6* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:044E06* + ID_OUI_FROM_DATABASE=Ericsson AB + +OUI:000D67* + ID_OUI_FROM_DATABASE=Ericsson + +OUI:E0757D* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + +OUI:001E65* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001F3B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0016EA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00216B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0019D1* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001CC0* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:5CE0C5* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:183DA2* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:448500* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:809B20* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:100BA9* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:247703* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:C48508* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0026C6* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:74E50B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:58946B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:002710* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:64D4DA* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:DCA971* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:001CBF* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:A0A8CD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:340286* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:34DE1A* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:80000B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:B80305* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:303A64* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:ACFDCE* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E09467* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:00DBDF* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:0C8BFD* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E09D31* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:CC3D82* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:D00ED9* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + +OUI:6C2995* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:40E3D6* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:24DEC6* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:D8C7C8* + ID_OUI_FROM_DATABASE=Aruba Networks + +OUI:900BC1* + ID_OUI_FROM_DATABASE=Sprocomm Technologies CO.,Ltd + +OUI:6C71D9* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:384FF0* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:0015AF* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:485D60* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + +OUI:54E4BD* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + +OUI:98743D* + ID_OUI_FROM_DATABASE=Shenzhen Jun Kai Hengye Technology Co. Ltd + +OUI:A04FD4* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:842615* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:5CE2F4* + ID_OUI_FROM_DATABASE=AcSiP Technology Corp. + +OUI:002662* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:00193E* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:0013C8* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:DC0B1A* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:74888B* + ID_OUI_FROM_DATABASE=ADB Broadband Italia + +OUI:84D6D0* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:E0CB1D* + ID_OUI_FROM_DATABASE=Private + +OUI:ACD074* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:D05349* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:00BB3A* + ID_OUI_FROM_DATABASE=Private + +OUI:000941* + ID_OUI_FROM_DATABASE=Allied Telesis R&D Center K.K. + +OUI:00014A* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:001CA4* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:002345* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:8C6422* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:90C115* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:8400D2* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:5CB524* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:94A1A2* + ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. + +OUI:74DE2B* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:68A3C4* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:C8FF28* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:0024D2* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:DC64B8* + ID_OUI_FROM_DATABASE=Shenzhen JingHanDa Electronics Co.Ltd + +OUI:C4DA7D* + ID_OUI_FROM_DATABASE=Ivium Technologies B.V. + +OUI:9492BC* + ID_OUI_FROM_DATABASE=SYNTECH(HK) TECHNOLOGY LIMITED + +OUI:001A4F* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:00040E* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:0016E3* + ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP + +OUI:30469A* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:0026F2* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00184D* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:001E2A* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:E8FCAF* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:4C60DE* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:00300A* + ID_OUI_FROM_DATABASE=Aztech Electronics Pte Ltd + +OUI:A06391* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:9CC7A6* + ID_OUI_FROM_DATABASE=AVM GmbH + +OUI:DCEF09* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:743170* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:A8D3F7* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:7C4FB5* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:0012BF* + ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation + +OUI:200CC8* + ID_OUI_FROM_DATABASE=NETGEAR + +OUI:04FE8D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:480031* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:0019FB* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:0CF9C0* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:001BA9* + ID_OUI_FROM_DATABASE=Brother industries, LTD. + +OUI:0011B6* + ID_OUI_FROM_DATABASE=Open Systems International + +OUI:E03E44* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:D40129* + ID_OUI_FROM_DATABASE=Broadcom + +OUI:FCB698* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:00E03A* + ID_OUI_FROM_DATABASE=Cabletron Systems, Inc. + +OUI:000117* + ID_OUI_FROM_DATABASE=Canal + + +OUI:0019C7* + ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. + +OUI:006DFB* + ID_OUI_FROM_DATABASE=Vutrix Technologies Ltd + +OUI:C81073* + ID_OUI_FROM_DATABASE=CENTURY OPTICOMM CO.,LTD + +OUI:744AA4* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:9CD35B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:60AF6D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B85A73* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:103047* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:109266* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B047BF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7C0BC6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:804E81* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:244B81* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50A4C8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:8425DB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D8C4E9* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50C8E5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:446D6C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:38D40B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:647791* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:781FDB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:08FC88* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:30C7AE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:18227E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00F46F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC1485* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:9CE6E7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:380195* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5CF6DC* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:1077B1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:508569* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0090A2* + ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. + +OUI:0030DA* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:64680C* + ID_OUI_FROM_DATABASE=Comtrend Corporation + +OUI:00CF1C* + ID_OUI_FROM_DATABASE=Communication Machinery Corporation + +OUI:0090F5* + ID_OUI_FROM_DATABASE=CLEVO CO. + +OUI:0030FF* + ID_OUI_FROM_DATABASE=DataFab Systems Inc. + +OUI:E498D1* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:A8A089* + ID_OUI_FROM_DATABASE=Tactical Communications + +OUI:48365F* + ID_OUI_FROM_DATABASE=Wintecronics Ltd. + +OUI:005A39* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:5CC6D0* + ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd + +OUI:080581* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:B0A737* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:B83E59* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:DC3A5E* + ID_OUI_FROM_DATABASE=Roku, Inc. + +OUI:0014A5* + ID_OUI_FROM_DATABASE=Gemtek Technology Co., Ltd. + +OUI:001742* + ID_OUI_FROM_DATABASE=FUJITSU LIMITED + +OUI:2C10C1* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:CCFB65* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:40D28A* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:7CBB8A* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:00224C* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0023CC* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:002444* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:E0E751* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0017AB* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:001BEA* + ID_OUI_FROM_DATABASE=Nintendo Co., Ltd. + +OUI:0015DE* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001370* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00247C* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0023B4* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0021AB* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001FDF* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00194F* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00188D* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00180F* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:547975* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:2CCC15* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:00BD3A* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:0026CC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:00164E* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:0016BC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001ADC* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:002668* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001F5C* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001F00* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:001E3B* + ID_OUI_FROM_DATABASE=Nokia Danmark A/S + +OUI:A04E04* + ID_OUI_FROM_DATABASE=Nokia Corporation + +OUI:240B0A* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:C4E510* + ID_OUI_FROM_DATABASE=Mechatro, Inc. + +OUI:74C330* + ID_OUI_FROM_DATABASE=SHENZHEN FAST TECHNOLOGIES CO.,LTD + +OUI:403F8C* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:14C3C2* + ID_OUI_FROM_DATABASE=K.A. Schmersal GmbH & Co. KG + +OUI:10785B* + ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc + +OUI:20768F* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:9C5CF9* + ID_OUI_FROM_DATABASE=Sony Mobile Communications AB + +OUI:88A084* + ID_OUI_FROM_DATABASE=Formation Data Systems + +OUI:0025DC* + ID_OUI_FROM_DATABASE=Sumitomo Electric Industries,Ltd + +OUI:001CFC* + ID_OUI_FROM_DATABASE=Sumitomo Electric Industries,Ltd + +OUI:00D0EC* + ID_OUI_FROM_DATABASE=NAKAYO TELECOMMUNICATIONS,INC + +OUI:8CC661* + ID_OUI_FROM_DATABASE=Current, powered by GE + +OUI:009050* + ID_OUI_FROM_DATABASE=Teleste Corporation + +OUI:BC44B0* + ID_OUI_FROM_DATABASE=Elastifile + +OUI:7864E6* + ID_OUI_FROM_DATABASE=Green Motive Technology Limited + +OUI:743E2B* + ID_OUI_FROM_DATABASE=Ruckus Wireless + +OUI:C0CCF8* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:80ED2C* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:E8B2AC* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0080B8* + ID_OUI_FROM_DATABASE=DMG MORI B.U.G. CO., LTD. + +OUI:8489AD* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:40B688* + ID_OUI_FROM_DATABASE=LEGIC Identsystems AG + +OUI:A09D91* + ID_OUI_FROM_DATABASE=SoundBridge + +OUI:30785C* + ID_OUI_FROM_DATABASE=Partow Tamas Novin (Parman) + +OUI:441102* + ID_OUI_FROM_DATABASE=EDMI Europe Ltd + +OUI:2C21D7* + ID_OUI_FROM_DATABASE=IMAX Corporation + +OUI:0026F7* + ID_OUI_FROM_DATABASE=Nivetti Systems Pvt. Ltd. + +OUI:24C3F9* + ID_OUI_FROM_DATABASE=Securitas Direct AB + +OUI:DC4D23* + ID_OUI_FROM_DATABASE=MRV Comunications + +OUI:085BDA* + ID_OUI_FROM_DATABASE=CliniCare LTD + +OUI:0C5A9E* + ID_OUI_FROM_DATABASE=Wi-SUN Alliance + +OUI:00C164* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:C4BED4* + ID_OUI_FROM_DATABASE=Avaya Inc + +OUI:98E7F5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:24BCF8* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:10D0AB* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:202DF8* + ID_OUI_FROM_DATABASE=Digital Media Cartridge Ltd. + +OUI:042DB4* + ID_OUI_FROM_DATABASE=First Property (Beijing) Co., Ltd Modern MOMA Branch + +OUI:008A96* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:007888* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:98DED0* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:30FC68* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:5CCA1A* + ID_OUI_FROM_DATABASE=Microsoft Mobile Oy + +OUI:000594* + ID_OUI_FROM_DATABASE=HMS Industrial Networks + +OUI:000AC2* + ID_OUI_FROM_DATABASE=Wuhan FiberHome Digital Technology Co.,Ltd. + +OUI:F08CFB* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:D4F207* + ID_OUI_FROM_DATABASE=DIAODIAO(Beijing)Technology CO.,Ltd + +OUI:FCF8B7* + ID_OUI_FROM_DATABASE=TRONTEQ Electronic + +OUI:D4883F* + ID_OUI_FROM_DATABASE=HDPRO CO., LTD. + +OUI:001BF3* + ID_OUI_FROM_DATABASE=TRANSRADIO SenderSysteme Berlin AG + +OUI:E0071B* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:A86AC1* + ID_OUI_FROM_DATABASE=HanbitEDS Co., Ltd. + +OUI:40163B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:88B1E1* + ID_OUI_FROM_DATABASE=Mojo Networks, Inc. + +OUI:74DFBF* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:FC3F7C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:608334* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:84AD58* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:746FF7* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:B01BD2* + ID_OUI_FROM_DATABASE=Le Shi Zhi Xin Electronic Technology (Tianjin) Limited + +OUI:74852A* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:386077* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:60B4F7* + ID_OUI_FROM_DATABASE=Plume Design Inc + +OUI:A4D8CA* + ID_OUI_FROM_DATABASE=HONG KONG WATER WORLD TECHNOLOGY CO. LIMITED + +OUI:00109B* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:00E0D5* + ID_OUI_FROM_DATABASE=Emulex Corporation + +OUI:001035* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:ECA86B* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:4487FC* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:002197* + ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. + +OUI:649968* + ID_OUI_FROM_DATABASE=Elentec + +OUI:00208F* + ID_OUI_FROM_DATABASE=ECI Telecom Ltd. + +OUI:9CDF03* + ID_OUI_FROM_DATABASE=Harman/Becker Automotive Systems GmbH + +OUI:F0407B* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:94885E* + ID_OUI_FROM_DATABASE=Surfilter Network Technology Co., Ltd. + +OUI:002378* + ID_OUI_FROM_DATABASE=GN Netcom A/S + +OUI:002088* + ID_OUI_FROM_DATABASE=GLOBAL VILLAGE COMMUNICATION + +OUI:90C7D8* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:BC6A44* + ID_OUI_FROM_DATABASE=Commend International GmbH + +OUI:0020F2* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:00015D* + ID_OUI_FROM_DATABASE=Oracle Corporation + +OUI:943BB1* + ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD. + +OUI:146308* + ID_OUI_FROM_DATABASE=JABIL CIRCUIT (SHANGHAI) LTD. + +OUI:08000D* + ID_OUI_FROM_DATABASE=International Computers, Ltd + +OUI:00D0A2* + ID_OUI_FROM_DATABASE=INTEGRATED DEVICE + +OUI:0060B1* + ID_OUI_FROM_DATABASE=Input/Output, Inc. + +OUI:00177D* + ID_OUI_FROM_DATABASE=IDT Technology Limited + +OUI:288A1C* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:100E7E* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:84B59C* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:544B8C* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:541E56* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:64649B* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:2C6BF5* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:002283* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:EC13DB* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:AC4BC8* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:B0A86E* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:3C94D5* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:F4CC55* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:002159* + ID_OUI_FROM_DATABASE=Juniper Networks + +OUI:5C70A3* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:3497F6* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + +OUI:50680A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D89403* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:9C8D7C* + ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO.,LTD. + +OUI:E04F43* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:B0E03C* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:D09DAB* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:94D859* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:9471AC* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:70BAEF* + ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited + +OUI:009006* + ID_OUI_FROM_DATABASE=Hamamatsu Photonics K.K. + +OUI:001AF4* + ID_OUI_FROM_DATABASE=Handreamnet + +OUI:000AED* + ID_OUI_FROM_DATABASE=HARTING Electronics GmbH + +OUI:1CCB99* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:18E3BC* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:289AFA* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:44A42D* + ID_OUI_FROM_DATABASE=TCT mobile ltd + +OUI:8C8EF2* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:F40F24* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A0D385* + ID_OUI_FROM_DATABASE=AUMA Riester GmbH & Co. KG + +OUI:1414E6* + ID_OUI_FROM_DATABASE=Ningbo Sanhe Digital Co.,Ltd + +OUI:84A134* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:1C9148* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:CC167E* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:600810* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:C85B76* + ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd + +OUI:001AE8* + ID_OUI_FROM_DATABASE=Unify Software and Solutions GmbH & Co. KG + +OUI:945907* + ID_OUI_FROM_DATABASE=Shanghai HITE-BELDEN Network Technology Co., Ltd. + +OUI:48C663* + ID_OUI_FROM_DATABASE=GTO Access Systems LLC + +OUI:606453* + ID_OUI_FROM_DATABASE=AOD Co.,Ltd. + +OUI:6C98EB* + ID_OUI_FROM_DATABASE=Riverbed Technology, Inc. + +OUI:DC293A* + ID_OUI_FROM_DATABASE=Shenzhen Nuoshi Technology Co., LTD. + +OUI:40562D* + ID_OUI_FROM_DATABASE=Smartron India Pvt ltd + +OUI:70288B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00809F* + ID_OUI_FROM_DATABASE=ALE International + +OUI:B0D7CC* + ID_OUI_FROM_DATABASE=Tridonic GmbH & Co KG + +OUI:7C574E* + ID_OUI_FROM_DATABASE=COBI GmbH + +OUI:34C0F9* + ID_OUI_FROM_DATABASE=Rockwell Automation + +OUI:20C047* + ID_OUI_FROM_DATABASE=Verizon + +OUI:AC0481* + ID_OUI_FROM_DATABASE=Jiangsu Huaxing Electronics Co., Ltd. + +OUI:FC2D5E* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:E811CA* + ID_OUI_FROM_DATABASE=SHANDONG KAER ELECTRIC.CO.,LTD + +OUI:ECD68A* + ID_OUI_FROM_DATABASE=Shenzhen JMicron Intelligent Technology Developmen + +OUI:1C77F6* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + +OUI:08D0B7* + ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd. + +OUI:28285D* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:5CF4AB* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:4C9EFF* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:0023F8* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:B0B2DC* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:90EF68* + ID_OUI_FROM_DATABASE=ZyXEL Communications Corporation + +OUI:00248D* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + +OUI:54276C* + ID_OUI_FROM_DATABASE=Jiangsu Houge Technology Corp. + +OUI:00CB00* + ID_OUI_FROM_DATABASE=Private + +OUI:FCFFAA* + ID_OUI_FROM_DATABASE=IEEE Registration Authority + +OUI:40D855* + ID_OUI_FROM_DATABASE=IEEE Registration Authority + +OUI:48DF37* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:9C93E4* + ID_OUI_FROM_DATABASE=Private + +OUI:005079* + ID_OUI_FROM_DATABASE=Private + +OUI:0028F8* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:8416F9* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + +OUI:C44BD1* + ID_OUI_FROM_DATABASE=Wallys Communications Teachnologies Co.,Ltd. + +OUI:2057AF* + ID_OUI_FROM_DATABASE=Shenzhen FH-NET OPTOELECTRONICS CO.,LTD + +OUI:34EA34* + ID_OUI_FROM_DATABASE=HangZhou Gubei Electronics Technology Co.,Ltd + +OUI:F8A9D0* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:CCFA00* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:BCF5AC* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:00AA70* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:F01C13* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:CC2D8C* + ID_OUI_FROM_DATABASE=LG ELECTRONICS INC + +OUI:344DF7* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:C49A02* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:0022A9* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:0025E5* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:10F96F* + ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) + +OUI:00116E* + ID_OUI_FROM_DATABASE=Peplink International Ltd. + +OUI:A091C8* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:002597* + ID_OUI_FROM_DATABASE=Kalki Communication Technologies + +OUI:882BD7* + ID_OUI_FROM_DATABASE=ADDÉNERGIE TECHNOLOGIES + +OUI:9CA5C0* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + +OUI:B4A5EF* + ID_OUI_FROM_DATABASE=Sercomm Corporation. + +OUI:3044A1* + ID_OUI_FROM_DATABASE=Shanghai Nanchao Information Technology + +OUI:C4F1D1* + ID_OUI_FROM_DATABASE=BEIJING SOGOU TECHNOLOGY DEVELOPMENT CO., LTD. + +OUI:38A28C* + ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD. + +OUI:58528A* + ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation + +OUI:BCC00F* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + +OUI:B0C287* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:CC03FA* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:28BE9B* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:509F3B* + ID_OUI_FROM_DATABASE=OI ELECTRIC CO.,LTD + +OUI:E4029B* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:6002B4* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:98EECB* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:70E284* + ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation + +OUI:80EA23* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:D88039* + ID_OUI_FROM_DATABASE=Microchip Technology Inc. + +OUI:001D72* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:FC3D93* + ID_OUI_FROM_DATABASE=LONGCHEER TELECOMMUNICATION LIMITED + +OUI:48F7C0* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + +OUI:00409F* + ID_OUI_FROM_DATABASE=Telco Systems, Inc. + +OUI:00E09E* + ID_OUI_FROM_DATABASE=Quantum Corporation + +OUI:00148C* + ID_OUI_FROM_DATABASE=General Dynamics Mission Systems + +OUI:A47174* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D4A148* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:D065CA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:8CEBC6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:B808D7* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:FCF152* + ID_OUI_FROM_DATABASE=Sony Corporation + +OUI:784476* + ID_OUI_FROM_DATABASE=Zioncom Electronics (Shenzhen) Ltd. + +OUI:00183A* + ID_OUI_FROM_DATABASE=Westell Technologies Inc. + +OUI:E89A8F* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:001B24* + ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. + +OUI:CC52AF* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:001A6B* + ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. + +OUI:00DD0A* + ID_OUI_FROM_DATABASE=UNGERMANN-BASS INC. + +OUI:00039D* + ID_OUI_FROM_DATABASE=Qisda Corporation + +OUI:000B0E* + ID_OUI_FROM_DATABASE=Trapeze Networks + +OUI:002318* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:E89D87* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:E8E0B7* + ID_OUI_FROM_DATABASE=Toshiba + +OUI:001428* + ID_OUI_FROM_DATABASE=Vocollect Inc + +OUI:006B9E* + ID_OUI_FROM_DATABASE=Vizio, Inc + +OUI:0024FF* + ID_OUI_FROM_DATABASE=QLogic Corporation + +OUI:00A0C6* + ID_OUI_FROM_DATABASE=Qualcomm Inc. + +OUI:ECAAA0* + ID_OUI_FROM_DATABASE=PEGATRON CORPORATION + +OUI:E8886C* + ID_OUI_FROM_DATABASE=Shenzhen SC Technologies Co.,LTD + +OUI:DC35F1* + ID_OUI_FROM_DATABASE=Positivo Informática SA. + +OUI:EC6881* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:44334C* + ID_OUI_FROM_DATABASE=Shenzhen Bilian electronic CO.,LTD + +OUI:D84FB8* + ID_OUI_FROM_DATABASE=LG ELECTRONICS + +OUI:9C220E* + ID_OUI_FROM_DATABASE=TASCAN Systems GmbH + +OUI:0CA402* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:00164D* + ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD + +OUI:FCFAF7* + ID_OUI_FROM_DATABASE=Shanghai Baud Data Communication Co.,Ltd. + +OUI:C8E776* + ID_OUI_FROM_DATABASE=PTCOM Technology + +OUI:5C497D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0005CD* + ID_OUI_FROM_DATABASE=D&M Holdings Inc. + +OUI:E0286D* + ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH + +OUI:7487A9* + ID_OUI_FROM_DATABASE=OCT Technology Co., Ltd. + +OUI:34AA99* + ID_OUI_FROM_DATABASE=Nokia + +OUI:C4084A* + ID_OUI_FROM_DATABASE=Nokia + +OUI:8C90D3* + ID_OUI_FROM_DATABASE=Nokia + +OUI:0C54B9* + ID_OUI_FROM_DATABASE=Nokia + +OUI:444E1A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E8E5D6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5492BE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0021D1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:101DC0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0023B9* + ID_OUI_FROM_DATABASE=Airbus Defence and Space Deutschland GmbH + +OUI:2047ED* + ID_OUI_FROM_DATABASE=BSkyB Ltd + +OUI:C8F946* + ID_OUI_FROM_DATABASE=LOCOSYS Technology Inc. + +OUI:D41D71* + ID_OUI_FROM_DATABASE=Palo Alto Networks + +OUI:5C2443* + ID_OUI_FROM_DATABASE=O-Sung Telecom Co., Ltd. + +OUI:1861C7* + ID_OUI_FROM_DATABASE=lemonbeat GmbH + +OUI:9CDC71* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise + +OUI:C8028F* + ID_OUI_FROM_DATABASE=Nova Electronics (Shanghai) Co., Ltd. + +OUI:240D65* + ID_OUI_FROM_DATABASE=Shenzhen Vsun Communication Technology Co., Ltd. + +OUI:D8452B* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + +OUI:2CDD95* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + +OUI:5C9960* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:CC088D* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:0080FB* + ID_OUI_FROM_DATABASE=BVM LIMITED + +OUI:107223* + ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO + +OUI:AC84C9* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + +OUI:14EDBB* + ID_OUI_FROM_DATABASE=2Wire Inc + +OUI:44BA46* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:B4D135* + ID_OUI_FROM_DATABASE=Cloudistics + +OUI:A8AD3D* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:E03005* + ID_OUI_FROM_DATABASE=Alcatel-Lucent Shanghai Bell Co., Ltd + +OUI:2824FF* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + +OUI:14C1FF* + ID_OUI_FROM_DATABASE=ShenZhen QianHai Comlan communication Co.,LTD + +OUI:EC8EB5* + ID_OUI_FROM_DATABASE=Hewlett Packard + +OUI:70AF6A* + ID_OUI_FROM_DATABASE=SHENZHEN FENGLIAN TECHNOLOGY CO., LTD. + +OUI:0026F1* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:B439D6* + ID_OUI_FROM_DATABASE=ProCurve Networking by HP + +OUI:001CEF* + ID_OUI_FROM_DATABASE=Primax Electronics Ltd. + +OUI:000276* + ID_OUI_FROM_DATABASE=Primax Electronics Ltd. + +OUI:4849C7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001F9A* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0014C7* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001540* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0017D1* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:0015E8* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001660* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:001BBA* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:205EF7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00034B* + ID_OUI_FROM_DATABASE=Nortel Networks + +OUI:00001B* + ID_OUI_FROM_DATABASE=Novell, Inc. + +OUI:00E011* + ID_OUI_FROM_DATABASE=UNIDEN CORPORATION + +OUI:B03EB0* + ID_OUI_FROM_DATABASE=MICRODIA Ltd. + +OUI:00126C* + ID_OUI_FROM_DATABASE=Visonic Technologies 1993 Ltd. + +OUI:18ABF5* + ID_OUI_FROM_DATABASE=Ultra Electronics Electrics + +OUI:304487* + ID_OUI_FROM_DATABASE=Hefei Radio Communication Technology Co., Ltd + +OUI:AC6175* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:AC482D* + ID_OUI_FROM_DATABASE=Ralinwi Nanjing Electronic Technology Co., Ltd. + +OUI:A48269* + ID_OUI_FROM_DATABASE=Datrium, Inc. + +OUI:441441* + ID_OUI_FROM_DATABASE=AudioControl Inc. + +OUI:0018DA* + ID_OUI_FROM_DATABASE=AMBER wireless GmbH + +OUI:EC24B8* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:68C90B* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:F4B85E* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:5C313E* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:A0E6F8* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:20C38F* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D0FF50* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:7472B0* + ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronics Co., Ltd. + +OUI:44BFE3* + ID_OUI_FROM_DATABASE=Shenzhen Longtech Electronics Co.,Ltd + +OUI:F45214* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + +OUI:689E19* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:985945* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:1CE2CC* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:44C15C* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E9* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:0017E7* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:D00790* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:04E451* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B0D5CC* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:5CF821* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:FC0F4B* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:3C6FEA* + ID_OUI_FROM_DATABASE=Panasonic India Pvt. Ltd. + +OUI:A863F2* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:948854* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001237* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:BC6A29* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:C0E422* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:001830* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:1CBA8C* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:7CA97D* + ID_OUI_FROM_DATABASE=Objenious + +OUI:58FB84* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:E0E7BB* + ID_OUI_FROM_DATABASE=Nureva, Inc. + +OUI:BC8AA3* + ID_OUI_FROM_DATABASE=NHN Entertainment + +OUI:70A84C* + ID_OUI_FROM_DATABASE=MONAD., Inc. + +OUI:407009* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:94877C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001DD2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:9C3426* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:3C7A8A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:000FCC* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:BCCAB5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:5C8FE0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:6CCA08* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:5465DE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:F8EDA5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00A289* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + +OUI:ACEC80* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015A4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0015A3* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:7CBFB1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:8096B1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00909C* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001180* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0017EE* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00D088* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001675* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0016B5* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001784* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0017E2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:CC7D37* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001A77* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:984B4A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:80F503* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:8496D8* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:D42C0F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E0B7B1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002210* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:00211E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:E48399* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:002636* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0024A0* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:0012C9* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001CFB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001C12* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:001FC4* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:C0C522* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:5CB066* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + +OUI:486DBB* + ID_OUI_FROM_DATABASE=Vestel Elektronik San ve Tic. A.Ş. + +OUI:6C1E90* + ID_OUI_FROM_DATABASE=Hansol Technics Co., Ltd. + +OUI:E09DFA* + ID_OUI_FROM_DATABASE=Wanan Hongsheng Electronic Co.Ltd + +OUI:34E71C* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + +OUI:182861* + ID_OUI_FROM_DATABASE=AirTies Wireless Networks + +OUI:8841FC* + ID_OUI_FROM_DATABASE=AirTies Wireless Networks + +OUI:BCB1F3* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:38ECE4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:CCF9E8* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F0E77E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5CE8EB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:B8D9CE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:6CB7F4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:182666* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:C06599* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:CC07AB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E84E84* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50FC9F* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:E432CB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:889B39* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:BC72B1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:78F7BE* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:70F927* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:301966* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:28BAB5* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:103B59* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:7C11CB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:A4CAA0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:001EE1* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:F49F54* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:0018AF* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:00214C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001632* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:D0667B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:001377* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:50B7C3* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:5CA39D* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:38AA3C* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:206432* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:8018A7* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:002637* + ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. + +OUI:B88EDF* + ID_OUI_FROM_DATABASE=Zencheer Communication Technology Co., Ltd. + +OUI:707781* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:606DC7* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:681401* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:0071CC* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F866D1* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F80D43* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:785968* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:002556* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:00265C* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:90CDB6* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001E4C* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:F8DA0C* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:342387* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:9034FC* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:906EBB* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:1C666D* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:CCAF78* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:904CE5* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:002268* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:001FE1* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:689423* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:B8763F* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:1C3E84* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:C01885* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:B01041* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:D85DE2* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. + +OUI:949AA9* + ID_OUI_FROM_DATABASE=Microsoft Corporation + +OUI:F8633F* + ID_OUI_FROM_DATABASE=Intel Corporate + +OUI:088620* + ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED + +OUI:A42983* + ID_OUI_FROM_DATABASE=Boeing Defence Australia + +OUI:702E22* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:B0C128* + ID_OUI_FROM_DATABASE=Adler ELREHA GmbH + +OUI:5CA933* + ID_OUI_FROM_DATABASE=Luma Home + +OUI:60EFC6* + ID_OUI_FROM_DATABASE=Shenzhen Chima Technologies Co Limited + +OUI:502B73* + ID_OUI_FROM_DATABASE=Tenda Technology Co.,Ltd.Dongguan branch + +OUI:20DBAB* + ID_OUI_FROM_DATABASE=Samsung Electronics Co., Ltd. + +OUI:000DF0* + ID_OUI_FROM_DATABASE=QCOM TECHNOLOGY INC. + +OUI:5CF7E6* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:A0D795* + ID_OUI_FROM_DATABASE=Apple, Inc. + +OUI:002722* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:687251* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:B4FBE4* + ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. + +OUI:188B15* + ID_OUI_FROM_DATABASE=ShenZhen ZhongRuiJing Technology co.,LTD + +OUI:CCB0DA* + ID_OUI_FROM_DATABASE=Liteon Technology Corporation + +OUI:E02CF3* + ID_OUI_FROM_DATABASE=MRS Electronic GmbH + +OUI:F41F88* + ID_OUI_FROM_DATABASE=zte corporation + +OUI:D816C1* + ID_OUI_FROM_DATABASE=DEWAV (HK) ELECTRONICS LIMITED + +OUI:7CCC1F* + ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD + +OUI:C0854C* + ID_OUI_FROM_DATABASE=Ragentek Technology Group + +OUI:00FD45* + ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise diff --git a/src/grp-udev/hwdb/20-acpi-vendor.hwdb b/src/grp-udev/hwdb/20-acpi-vendor.hwdb new file mode 100644 index 0000000000..3731b33656 --- /dev/null +++ b/src/grp-udev/hwdb/20-acpi-vendor.hwdb @@ -0,0 +1,7444 @@ +# This file is part of systemd. +# +# Data imported from: +# http://www.uefi.org/uefi-pnp-export +# http://www.uefi.org/uefi-acpi-export +# +# With various additions from other sources + +acpi:3NOD*: + ID_VENDOR_FROM_DATABASE=Shenzhen three Connaught Information Technology Co., Ltd. (3nod Group) + +acpi:AAVA*: + ID_VENDOR_FROM_DATABASE=Aava Mobile Oy + +acpi:AMDI*: + ID_VENDOR_FROM_DATABASE=AMD + +acpi:APMC*: + ID_VENDOR_FROM_DATABASE=Applied Micro Circuits Corporation + +acpi:APTA*: + ID_VENDOR_FROM_DATABASE=Aptina Imaging Corporation + +acpi:ARMH*: + ID_VENDOR_FROM_DATABASE=ARM Ltd. + +acpi:ARML*: + ID_VENDOR_FROM_DATABASE=ARM Ltd. + +acpi:ASUS*: + ID_VENDOR_FROM_DATABASE=ASUS + +acpi:ATML*: + ID_VENDOR_FROM_DATABASE=Atmel + +acpi:AUTH*: + ID_VENDOR_FROM_DATABASE=AuthenTec + +acpi:BOSC*: + ID_VENDOR_FROM_DATABASE=Robert Bosch GmbH + +acpi:BRCM*: + ID_VENDOR_FROM_DATABASE=Broadcom Corporation + +acpi:CORE*: + ID_VENDOR_FROM_DATABASE=CoreOS, Inc + +acpi:CPLM*: + ID_VENDOR_FROM_DATABASE=Capella Microsystems Inc. + +acpi:DELL*: + ID_VENDOR_FROM_DATABASE=Dell, Inc. + +acpi:DLGS*: + ID_VENDOR_FROM_DATABASE=Dialog Semiconductor PLC + +acpi:DLLK*: + ID_VENDOR_FROM_DATABASE=Dell, Inc. + +acpi:DSUO*: + ID_VENDOR_FROM_DATABASE=Shenzhen DSO Microelectronics Co.,Ltd. + +acpi:ELAN*: + ID_VENDOR_FROM_DATABASE=ELAN MICROELECTRONICS CORPORATION + +acpi:ESSX*: + ID_VENDOR_FROM_DATABASE=Everest Semiconductor Co., Ltd. + +acpi:FRSC*: + ID_VENDOR_FROM_DATABASE=Freescale, Inc + +acpi:FTSC*: + ID_VENDOR_FROM_DATABASE=FocalTech Systems Co., Ltd. + +acpi:GOOG*: + ID_VENDOR_FROM_DATABASE=Google, Inc. + +acpi:HIMX*: + ID_VENDOR_FROM_DATABASE=Himax Technologies, Inc. + +acpi:HISI*: + ID_VENDOR_FROM_DATABASE=HiSilicon Technologies Co., Ltd. + +acpi:HPIC*: + ID_VENDOR_FROM_DATABASE=HP Inc. + +acpi:HPQC*: + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Company + +acpi:HTLM*: + ID_VENDOR_FROM_DATABASE=HTBLuVA Mödling + +acpi:HWPE*: + ID_VENDOR_FROM_DATABASE=Hewlett Packard Enterprise + +acpi:IBMX*: + ID_VENDOR_FROM_DATABASE=IBM + +acpi:IDEA*: + ID_VENDOR_FROM_DATABASE=Lenovo Beijing Co. Ltd. + +acpi:IMPJ*: + ID_VENDOR_FROM_DATABASE=Impinj + +acpi:INTC*: + ID_VENDOR_FROM_DATABASE=Intel Corporation + +acpi:INTL*: + ID_VENDOR_FROM_DATABASE=Intel Corporation + +acpi:INVN*: + ID_VENDOR_FROM_DATABASE=Invensense, Inc + +acpi:IP3T*: + ID_VENDOR_FROM_DATABASE=IP3 Technology Ltd. + +acpi:IPHI*: + ID_VENDOR_FROM_DATABASE=Inphi Corporation + +acpi:KIOX*: + ID_VENDOR_FROM_DATABASE=Kionix, Inc. + +acpi:LNRO*: + ID_VENDOR_FROM_DATABASE=Linaro, Ltd. + +acpi:LNUX*: + ID_VENDOR_FROM_DATABASE=The Linux Foundation + +acpi:MIPI*: + ID_VENDOR_FROM_DATABASE=MIPI Alliance + +acpi:MSAY*: + ID_VENDOR_FROM_DATABASE=Microsoft Corporation + +acpi:MSFT*: + ID_VENDOR_FROM_DATABASE=Microsoft Corporation + +acpi:MSHW*: + ID_VENDOR_FROM_DATABASE=Microsoft Corporation + +acpi:MXIM*: + ID_VENDOR_FROM_DATABASE=Maxim Integrated + +acpi:NVDA*: + ID_VENDOR_FROM_DATABASE=Nvidia + +acpi:NVTN*: + ID_VENDOR_FROM_DATABASE=Nuvoton Technology Corporation + +acpi:OBDA*: + ID_VENDOR_FROM_DATABASE=REALTEK Semiconductor Corp. + +acpi:OMPS*: + ID_VENDOR_FROM_DATABASE=OmniPreSense + +acpi:OVTI*: + ID_VENDOR_FROM_DATABASE=OmniVision Technologies, Inc. + +acpi:PEGA*: + ID_VENDOR_FROM_DATABASE=Pegatron Corporation + +acpi:QCOM*: + ID_VENDOR_FROM_DATABASE=Qualcomm Inc + +acpi:QEMU*: + ID_VENDOR_FROM_DATABASE=Red Hat, Inc. + +acpi:RAYD*: + ID_VENDOR_FROM_DATABASE=Raydium Semiconductor Corporation + +acpi:RKCP*: + ID_VENDOR_FROM_DATABASE=Fuzhou Rockchip Electronics Co., Ltd. + +acpi:RZSN*: + ID_VENDOR_FROM_DATABASE=Rozsnyó, s.r.o. + +acpi:SHRP*: + ID_VENDOR_FROM_DATABASE=Sharp Corporation + +acpi:SONY*: + ID_VENDOR_FROM_DATABASE=Sony Corporation + +acpi:ST86*: + ID_VENDOR_FROM_DATABASE=Shenzhen South-Top Computer Co., Ltd. + +acpi:SWEM*: + ID_VENDOR_FROM_DATABASE=Sierra Wireless + +acpi:SYNA*: + ID_VENDOR_FROM_DATABASE=Synaptics Inc + +acpi:TCAG*: + ID_VENDOR_FROM_DATABASE=Teracue AG + +acpi:TOSB*: + ID_VENDOR_FROM_DATABASE=Toshiba Corporation + +acpi:TXNW*: + ID_VENDOR_FROM_DATABASE=Texas Instruments + +acpi:UBLX*: + ID_VENDOR_FROM_DATABASE=u-blox AG + +acpi:VAIO*: + ID_VENDOR_FROM_DATABASE=VAIO Corporation + +acpi:VFSI*: + ID_VENDOR_FROM_DATABASE=Validity Sensors, Inc + +acpi:WCOM*: + ID_VENDOR_FROM_DATABASE=Wacom + +acpi:WSDR*: + ID_VENDOR_FROM_DATABASE=Winsider Seminars & Solutions Inc. + +acpi:XMCC*: + ID_VENDOR_FROM_DATABASE=Xiaomi Inc. + +acpi:AAA*: + ID_VENDOR_FROM_DATABASE=Avolites Ltd + +acpi:AAC*: + ID_VENDOR_FROM_DATABASE=AcerView + +acpi:AAE*: + ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc. + +acpi:AAM*: + ID_VENDOR_FROM_DATABASE=Aava Mobile Oy + +acpi:AAT*: + ID_VENDOR_FROM_DATABASE=Ann Arbor Technologies + +acpi:ABA*: + ID_VENDOR_FROM_DATABASE=ABBAHOME INC. + +acpi:ABC*: + ID_VENDOR_FROM_DATABASE=AboCom System Inc. + +acpi:ABD*: + ID_VENDOR_FROM_DATABASE=Allen Bradley Company + +acpi:ABE*: + ID_VENDOR_FROM_DATABASE=Alcatel Bell + +acpi:ABO*: + ID_VENDOR_FROM_DATABASE=D-Link Systems Inc + +acpi:ABP*: + ID_VENDOR_FROM_DATABASE=Advansys + +acpi:ABS*: + ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc. + +acpi:ABT*: + ID_VENDOR_FROM_DATABASE=Anchor Bay Technologies, Inc. + +acpi:ABV*: + ID_VENDOR_FROM_DATABASE=Advanced Research Technology + +acpi:ACA*: + ID_VENDOR_FROM_DATABASE=Ariel Corporation + +acpi:ACB*: + ID_VENDOR_FROM_DATABASE=Aculab Ltd + +acpi:ACC*: + ID_VENDOR_FROM_DATABASE=Accton Technology Corporation + +acpi:ACD*: + ID_VENDOR_FROM_DATABASE=AWETA BV + +acpi:ACE*: + ID_VENDOR_FROM_DATABASE=Actek Engineering Pty Ltd + +acpi:ACG*: + ID_VENDOR_FROM_DATABASE=A&R Cambridge Ltd. + +acpi:ACH*: + ID_VENDOR_FROM_DATABASE=Archtek Telecom Corporation + +acpi:ACI*: + ID_VENDOR_FROM_DATABASE=Ancor Communications Inc + +acpi:ACK*: + ID_VENDOR_FROM_DATABASE=Acksys + +acpi:ACL*: + ID_VENDOR_FROM_DATABASE=Apricot Computers + +acpi:ACM*: + ID_VENDOR_FROM_DATABASE=Acroloop Motion Control Systems Inc + +acpi:ACO*: + ID_VENDOR_FROM_DATABASE=Allion Computer Inc. + +acpi:ACP[0-9A-F]*: + ID_VENDOR_FROM_DATABASE=Aspen Tech Inc + +acpi:ACR*: + ID_VENDOR_FROM_DATABASE=Acer Technologies + +acpi:ACS*: + ID_VENDOR_FROM_DATABASE=Altos Computer Systems + +acpi:ACT*: + ID_VENDOR_FROM_DATABASE=Applied Creative Technology + +acpi:ACU*: + ID_VENDOR_FROM_DATABASE=Acculogic + +acpi:ACV*: + ID_VENDOR_FROM_DATABASE=ActivCard S.A + +acpi:ADA*: + ID_VENDOR_FROM_DATABASE=Addi-Data GmbH + +acpi:ADB*: + ID_VENDOR_FROM_DATABASE=Aldebbaron + +acpi:ADC*: + ID_VENDOR_FROM_DATABASE=Acnhor Datacomm + +acpi:ADD*: + ID_VENDOR_FROM_DATABASE=Advanced Peripheral Devices Inc + +acpi:ADE*: + ID_VENDOR_FROM_DATABASE=Arithmos, Inc. + +acpi:ADH*: + ID_VENDOR_FROM_DATABASE=Aerodata Holdings Ltd + +acpi:ADI*: + ID_VENDOR_FROM_DATABASE=ADI Systems Inc + +acpi:ADK*: + ID_VENDOR_FROM_DATABASE=Adtek System Science Company Ltd + +acpi:ADL*: + ID_VENDOR_FROM_DATABASE=ASTRA Security Products Ltd + +acpi:ADM*: + ID_VENDOR_FROM_DATABASE=Ad Lib MultiMedia Inc + +acpi:ADN*: + ID_VENDOR_FROM_DATABASE=Analog & Digital Devices Tel. Inc + +acpi:ADP*: + ID_VENDOR_FROM_DATABASE=Adaptec Inc + +acpi:ADR*: + ID_VENDOR_FROM_DATABASE=Nasa Ames Research Center + +acpi:ADS*: + ID_VENDOR_FROM_DATABASE=Analog Devices Inc + +acpi:ADT*: + ID_VENDOR_FROM_DATABASE=Adtek + +acpi:ADV*: + ID_VENDOR_FROM_DATABASE=Advanced Micro Devices Inc + +acpi:ADX*: + ID_VENDOR_FROM_DATABASE=Adax Inc + +acpi:ADZ*: + ID_VENDOR_FROM_DATABASE=ADDER TECHNOLOGY LTD + +acpi:AEC*: + ID_VENDOR_FROM_DATABASE=Antex Electronics Corporation + +acpi:AED*: + ID_VENDOR_FROM_DATABASE=Advanced Electronic Designs, Inc. + +acpi:AEI*: + ID_VENDOR_FROM_DATABASE=Actiontec Electric Inc + +acpi:AEJ*: + ID_VENDOR_FROM_DATABASE=Alpha Electronics Company + +acpi:AEM*: + ID_VENDOR_FROM_DATABASE=ASEM S.p.A. + +acpi:AEN*: + ID_VENDOR_FROM_DATABASE=Avencall + +acpi:AEP*: + ID_VENDOR_FROM_DATABASE=Aetas Peripheral International + +acpi:AET*: + ID_VENDOR_FROM_DATABASE=Aethra Telecomunicazioni S.r.l. + +acpi:AFA*: + ID_VENDOR_FROM_DATABASE=Alfa Inc + +acpi:AGC*: + ID_VENDOR_FROM_DATABASE=Beijing Aerospace Golden Card Electronic Engineering Co.,Ltd. + +acpi:AGI*: + ID_VENDOR_FROM_DATABASE=Artish Graphics Inc + +acpi:AGL*: + ID_VENDOR_FROM_DATABASE=Argolis + +acpi:AGM*: + ID_VENDOR_FROM_DATABASE=Advan Int'l Corporation + +acpi:AGO*: + ID_VENDOR_FROM_DATABASE=AlgolTek, Inc. + +acpi:AGT*: + ID_VENDOR_FROM_DATABASE=Agilent Technologies + +acpi:AHC*: + ID_VENDOR_FROM_DATABASE=Advantech Co., Ltd. + +acpi:AHS*: + ID_VENDOR_FROM_DATABASE=Beijing AnHeng SecoTech Information Technology Co., Ltd. + +acpi:AIC*: + ID_VENDOR_FROM_DATABASE=Arnos Insturments & Computer Systems + +acpi:AIE*: + ID_VENDOR_FROM_DATABASE=Altmann Industrieelektronik + +acpi:AII*: + ID_VENDOR_FROM_DATABASE=Amptron International Inc. + +acpi:AIK*: + ID_VENDOR_FROM_DATABASE=Dongguan Alllike Electronics Co., Ltd. + +acpi:AIL*: + ID_VENDOR_FROM_DATABASE=Altos India Ltd + +acpi:AIM*: + ID_VENDOR_FROM_DATABASE=AIMS Lab Inc + +acpi:AIR*: + ID_VENDOR_FROM_DATABASE=Advanced Integ. Research Inc + +acpi:AIS*: + ID_VENDOR_FROM_DATABASE=Alien Internet Services + +acpi:AIW*: + ID_VENDOR_FROM_DATABASE=Aiwa Company Ltd + +acpi:AIX*: + ID_VENDOR_FROM_DATABASE=ALTINEX, INC. + +acpi:AJA*: + ID_VENDOR_FROM_DATABASE=AJA Video Systems, Inc. + +acpi:AKB*: + ID_VENDOR_FROM_DATABASE=Akebia Ltd + +acpi:AKE*: + ID_VENDOR_FROM_DATABASE=AKAMI Electric Co.,Ltd + +acpi:AKI*: + ID_VENDOR_FROM_DATABASE=AKIA Corporation + +acpi:AKL*: + ID_VENDOR_FROM_DATABASE=AMiT Ltd + +acpi:AKM*: + ID_VENDOR_FROM_DATABASE=Asahi Kasei Microsystems Company Ltd + +acpi:AKP*: + ID_VENDOR_FROM_DATABASE=Atom Komplex Prylad + +acpi:AKY*: + ID_VENDOR_FROM_DATABASE=Askey Computer Corporation + +acpi:ALA*: + ID_VENDOR_FROM_DATABASE=Alacron Inc + +acpi:ALC*: + ID_VENDOR_FROM_DATABASE=Altec Corporation + +acpi:ALD*: + ID_VENDOR_FROM_DATABASE=In4S Inc + +acpi:ALE*: + ID_VENDOR_FROM_DATABASE=Alenco BV + +acpi:ALG*: + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Corp. + +acpi:ALH*: + ID_VENDOR_FROM_DATABASE=AL Systems + +acpi:ALI*: + ID_VENDOR_FROM_DATABASE=Acer Labs + +acpi:ALJ*: + ID_VENDOR_FROM_DATABASE=Altec Lansing + +acpi:ALK*: + ID_VENDOR_FROM_DATABASE=Acrolink Inc + +acpi:ALL*: + ID_VENDOR_FROM_DATABASE=Alliance Semiconductor Corporation + +acpi:ALM*: + ID_VENDOR_FROM_DATABASE=Acutec Ltd. + +acpi:ALN*: + ID_VENDOR_FROM_DATABASE=Alana Technologies + +acpi:ALO*: + ID_VENDOR_FROM_DATABASE=Algolith Inc. + +acpi:ALP*: + ID_VENDOR_FROM_DATABASE=Alps Electric Company Ltd + +acpi:ALR*: + ID_VENDOR_FROM_DATABASE=Advanced Logic + +acpi:ALS*: + ID_VENDOR_FROM_DATABASE=Avance Logic Inc + +acpi:ALT*: + ID_VENDOR_FROM_DATABASE=Altra + +acpi:ALV*: + ID_VENDOR_FROM_DATABASE=AlphaView LCD + +acpi:ALX*: + ID_VENDOR_FROM_DATABASE=ALEXON Co.,Ltd. + +acpi:AMA*: + ID_VENDOR_FROM_DATABASE=Asia Microelectronic Development Inc + +acpi:AMB*: + ID_VENDOR_FROM_DATABASE=Ambient Technologies, Inc. + +acpi:AMC*: + ID_VENDOR_FROM_DATABASE=Attachmate Corporation + +acpi:AMD*: + ID_VENDOR_FROM_DATABASE=Amdek Corporation + +acpi:AMI*: + ID_VENDOR_FROM_DATABASE=American Megatrends Inc + +acpi:AML*: + ID_VENDOR_FROM_DATABASE=Anderson Multimedia Communications (HK) Limited + +acpi:AMN*: + ID_VENDOR_FROM_DATABASE=Amimon LTD. + +acpi:AMO*: + ID_VENDOR_FROM_DATABASE=Amino Technologies PLC and Amino Communications Limited + +acpi:AMP*: + ID_VENDOR_FROM_DATABASE=AMP Inc + +acpi:AMR*: + ID_VENDOR_FROM_DATABASE=AmTRAN Technology Co., Ltd. + +acpi:AMS*: + ID_VENDOR_FROM_DATABASE=ARMSTEL, Inc. + +acpi:AMT*: + ID_VENDOR_FROM_DATABASE=AMT International Industry + +acpi:AMW*: + ID_VENDOR_FROM_DATABASE=AMW + +acpi:AMX*: + ID_VENDOR_FROM_DATABASE=AMX LLC + +acpi:ANA*: + ID_VENDOR_FROM_DATABASE=Anakron + +acpi:ANC*: + ID_VENDOR_FROM_DATABASE=Ancot + +acpi:AND*: + ID_VENDOR_FROM_DATABASE=Adtran Inc + +acpi:ANI*: + ID_VENDOR_FROM_DATABASE=Anigma Inc + +acpi:ANK*: + ID_VENDOR_FROM_DATABASE=Anko Electronic Company Ltd + +acpi:ANL*: + ID_VENDOR_FROM_DATABASE=Analogix Semiconductor, Inc + +acpi:ANO*: + ID_VENDOR_FROM_DATABASE=Anorad Corporation + +acpi:ANP*: + ID_VENDOR_FROM_DATABASE=Andrew Network Production + +acpi:ANR*: + ID_VENDOR_FROM_DATABASE=ANR Ltd + +acpi:ANS*: + ID_VENDOR_FROM_DATABASE=Ansel Communication Company + +acpi:ANT*: + ID_VENDOR_FROM_DATABASE=Ace CAD Enterprise Company Ltd + +acpi:ANV*: + ID_VENDOR_FROM_DATABASE=Beijing ANTVR Technology Co., Ltd. + +acpi:ANW*: + ID_VENDOR_FROM_DATABASE=Analog Way SAS + +acpi:ANX*: + ID_VENDOR_FROM_DATABASE=Acer Netxus Inc + +acpi:AOA*: + ID_VENDOR_FROM_DATABASE=AOpen Inc. + +acpi:AOC*: + ID_VENDOR_FROM_DATABASE=AOC + +acpi:AOE*: + ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc. + +acpi:AOL*: + ID_VENDOR_FROM_DATABASE=America OnLine + +acpi:AOT*: + ID_VENDOR_FROM_DATABASE=Alcatel + +acpi:APA*: + ID_VENDOR_FROM_DATABASE=Adaptec + +acpi:APC*: + ID_VENDOR_FROM_DATABASE=American Power Conversion + +acpi:APD*: + ID_VENDOR_FROM_DATABASE=AppliAdata + +acpi:APE*: + ID_VENDOR_FROM_DATABASE=Alpine Electronics, Inc. + +acpi:APG*: + ID_VENDOR_FROM_DATABASE=Horner Electric Inc + +acpi:API*: + ID_VENDOR_FROM_DATABASE=A Plus Info Corporation + +acpi:APL*: + ID_VENDOR_FROM_DATABASE=Aplicom Oy + +acpi:APM*: + ID_VENDOR_FROM_DATABASE=Applied Memory Tech + +acpi:APN*: + ID_VENDOR_FROM_DATABASE=Appian Tech Inc + +acpi:APP*: + ID_VENDOR_FROM_DATABASE=Apple Computer Inc + +acpi:APR*: + ID_VENDOR_FROM_DATABASE=Aprilia s.p.a. + +acpi:APS*: + ID_VENDOR_FROM_DATABASE=Autologic Inc + +acpi:APT*: + ID_VENDOR_FROM_DATABASE=Audio Processing Technology Ltd + +acpi:APV*: + ID_VENDOR_FROM_DATABASE=A+V Link + +acpi:APX*: + ID_VENDOR_FROM_DATABASE=AP Designs Ltd + +acpi:ARC*: + ID_VENDOR_FROM_DATABASE=Alta Research Corporation + +acpi:ARD*: + ID_VENDOR_FROM_DATABASE=AREC Inc. + +acpi:ARE*: + ID_VENDOR_FROM_DATABASE=ICET S.p.A. + +acpi:ARG*: + ID_VENDOR_FROM_DATABASE=Argus Electronics Co., LTD + +acpi:ARI*: + ID_VENDOR_FROM_DATABASE=Argosy Research Inc + +acpi:ARK*: + ID_VENDOR_FROM_DATABASE=Ark Logic Inc + +acpi:ARL*: + ID_VENDOR_FROM_DATABASE=Arlotto Comnet Inc + +acpi:ARM*: + ID_VENDOR_FROM_DATABASE=Arima + +acpi:ARO*: + ID_VENDOR_FROM_DATABASE=Poso International B.V. + +acpi:ARR*: + ID_VENDOR_FROM_DATABASE=ARRIS Group, Inc. + +acpi:ARS*: + ID_VENDOR_FROM_DATABASE=Arescom Inc + +acpi:ART*: + ID_VENDOR_FROM_DATABASE=Corion Industrial Corporation + +acpi:ASC*: + ID_VENDOR_FROM_DATABASE=Ascom Strategic Technology Unit + +acpi:ASD*: + ID_VENDOR_FROM_DATABASE=USC Information Sciences Institute + +acpi:ASE*: + ID_VENDOR_FROM_DATABASE=AseV Display Labs + +acpi:ASH*: + ID_VENDOR_FROM_DATABASE=Ashton Bentley Concepts + +acpi:ASI*: + ID_VENDOR_FROM_DATABASE=Ahead Systems + +acpi:ASK*: + ID_VENDOR_FROM_DATABASE=Ask A/S + +acpi:ASL*: + ID_VENDOR_FROM_DATABASE=AccuScene Corporation Ltd + +acpi:ASM*: + ID_VENDOR_FROM_DATABASE=ASEM S.p.A. + +acpi:ASN*: + ID_VENDOR_FROM_DATABASE=Asante Tech Inc + +acpi:ASP*: + ID_VENDOR_FROM_DATABASE=ASP Microelectronics Ltd + +acpi:AST*: + ID_VENDOR_FROM_DATABASE=AST Research Inc + +acpi:ASU*: + ID_VENDOR_FROM_DATABASE=Asuscom Network Inc + +acpi:ASX*: + ID_VENDOR_FROM_DATABASE=AudioScience + +acpi:ASY*: + ID_VENDOR_FROM_DATABASE=Rockwell Collins / Airshow Systems + +acpi:ATA*: + ID_VENDOR_FROM_DATABASE=Allied Telesyn International (Asia) Pte Ltd + +acpi:ATC*: + ID_VENDOR_FROM_DATABASE=Ably-Tech Corporation + +acpi:ATD*: + ID_VENDOR_FROM_DATABASE=Alpha Telecom Inc + +acpi:ATE*: + ID_VENDOR_FROM_DATABASE=Innovate Ltd + +acpi:ATH*: + ID_VENDOR_FROM_DATABASE=Athena Informatica S.R.L. + +acpi:ATI*: + ID_VENDOR_FROM_DATABASE=Allied Telesis KK + +acpi:ATJ*: + ID_VENDOR_FROM_DATABASE=ArchiTek Corporation + +acpi:ATK*: + ID_VENDOR_FROM_DATABASE=Allied Telesyn Int'l + +acpi:ATL*: + ID_VENDOR_FROM_DATABASE=Arcus Technology Ltd + +acpi:ATM*: + ID_VENDOR_FROM_DATABASE=ATM Ltd + +acpi:ATN*: + ID_VENDOR_FROM_DATABASE=Athena Smartcard Solutions Ltd. + +acpi:ATO*: + ID_VENDOR_FROM_DATABASE=ASTRO DESIGN, INC. + +acpi:ATP*: + ID_VENDOR_FROM_DATABASE=Alpha-Top Corporation + +acpi:ATT*: + ID_VENDOR_FROM_DATABASE=AT&T + +acpi:ATV*: + ID_VENDOR_FROM_DATABASE=Office Depot, Inc. + +acpi:ATX*: + ID_VENDOR_FROM_DATABASE=Athenix Corporation + +acpi:AUG*: + ID_VENDOR_FROM_DATABASE=August Home, Inc. + +acpi:AUI*: + ID_VENDOR_FROM_DATABASE=Alps Electric Inc + +acpi:AUO*: + ID_VENDOR_FROM_DATABASE=AU Optronics + +acpi:AUR*: + ID_VENDOR_FROM_DATABASE=Aureal Semiconductor + +acpi:AUS*: + ID_VENDOR_FROM_DATABASE=ASUSTek COMPUTER INC + +acpi:AUT*: + ID_VENDOR_FROM_DATABASE=Autotime Corporation + +acpi:AUV*: + ID_VENDOR_FROM_DATABASE=Auvidea GmbH + +acpi:AVA*: + ID_VENDOR_FROM_DATABASE=Avaya Communication + +acpi:AVC*: + ID_VENDOR_FROM_DATABASE=Auravision Corporation + +acpi:AVD*: + ID_VENDOR_FROM_DATABASE=Avid Electronics Corporation + +acpi:AVE*: + ID_VENDOR_FROM_DATABASE=Add Value Enterpises (Asia) Pte Ltd + +acpi:AVG*: + ID_VENDOR_FROM_DATABASE=Avegant Corporation + +acpi:AVI*: + ID_VENDOR_FROM_DATABASE=Nippon Avionics Co.,Ltd + +acpi:AVJ*: + ID_VENDOR_FROM_DATABASE=Atelier Vision Corporation + +acpi:AVL*: + ID_VENDOR_FROM_DATABASE=Avalue Technology Inc. + +acpi:AVM*: + ID_VENDOR_FROM_DATABASE=AVM GmbH + +acpi:AVN*: + ID_VENDOR_FROM_DATABASE=Advance Computer Corporation + +acpi:AVO*: + ID_VENDOR_FROM_DATABASE=Avocent Corporation + +acpi:AVR*: + ID_VENDOR_FROM_DATABASE=AVer Information Inc. + +acpi:AVT*: + ID_VENDOR_FROM_DATABASE=Avtek (Electronics) Pty Ltd + +acpi:AVV*: + ID_VENDOR_FROM_DATABASE=SBS Technologies (Canada), Inc. (was Avvida Systems, Inc.) + +acpi:AVX*: + ID_VENDOR_FROM_DATABASE=A/Vaux Electronics + +acpi:AWC*: + ID_VENDOR_FROM_DATABASE=Access Works Comm Inc + +acpi:AWL*: + ID_VENDOR_FROM_DATABASE=Aironet Wireless Communications, Inc + +acpi:AWS*: + ID_VENDOR_FROM_DATABASE=Wave Systems + +acpi:AXB*: + ID_VENDOR_FROM_DATABASE=Adrienne Electronics Corporation + +acpi:AXC*: + ID_VENDOR_FROM_DATABASE=AXIOMTEK CO., LTD. + +acpi:AXE*: + ID_VENDOR_FROM_DATABASE=D-Link Systems Inc + +acpi:AXI*: + ID_VENDOR_FROM_DATABASE=American Magnetics + +acpi:AXL*: + ID_VENDOR_FROM_DATABASE=Axel + +acpi:AXO*: + ID_VENDOR_FROM_DATABASE=Axonic Labs LLC + +acpi:AXP*: + ID_VENDOR_FROM_DATABASE=American Express + +acpi:AXT*: + ID_VENDOR_FROM_DATABASE=Axtend Technologies Inc + +acpi:AXX*: + ID_VENDOR_FROM_DATABASE=Axxon Computer Corporation + +acpi:AXY*: + ID_VENDOR_FROM_DATABASE=AXYZ Automation Services, Inc + +acpi:AYD*: + ID_VENDOR_FROM_DATABASE=Aydin Displays + +acpi:AYR*: + ID_VENDOR_FROM_DATABASE=Airlib, Inc + +acpi:AZH*: + ID_VENDOR_FROM_DATABASE=Shenzhen three Connaught Information Technology Co., Ltd. (3nod Group) + +acpi:AZM*: + ID_VENDOR_FROM_DATABASE=AZ Middelheim - Radiotherapy + +acpi:AZT*: + ID_VENDOR_FROM_DATABASE=Aztech Systems Ltd + +acpi:BAC*: + ID_VENDOR_FROM_DATABASE=Biometric Access Corporation + +acpi:BAN*: + ID_VENDOR_FROM_DATABASE=Banyan + +acpi:BBB*: + ID_VENDOR_FROM_DATABASE=an-najah university + +acpi:BBH*: + ID_VENDOR_FROM_DATABASE=B&Bh + +acpi:BBL*: + ID_VENDOR_FROM_DATABASE=Brain Boxes Limited + +acpi:BCC*: + ID_VENDOR_FROM_DATABASE=Beaver Computer Corporaton + +acpi:BCD*: + ID_VENDOR_FROM_DATABASE=Barco GmbH + +acpi:BCI*: + ID_VENDOR_FROM_DATABASE=Broadata Communications Inc. + +acpi:BCM*: + ID_VENDOR_FROM_DATABASE=Broadcom + +acpi:BCQ*: + ID_VENDOR_FROM_DATABASE=Deutsche Telekom Berkom GmbH + +acpi:BCS*: + ID_VENDOR_FROM_DATABASE=Booria CAD/CAM systems + +acpi:BDO*: + ID_VENDOR_FROM_DATABASE=Brahler ICS + +acpi:BDR*: + ID_VENDOR_FROM_DATABASE=Blonder Tongue Labs, Inc. + +acpi:BDS*: + ID_VENDOR_FROM_DATABASE=Barco Display Systems + +acpi:BEC*: + ID_VENDOR_FROM_DATABASE=Beckhoff Automation + +acpi:BEI*: + ID_VENDOR_FROM_DATABASE=Beckworth Enterprises Inc + +acpi:BEK*: + ID_VENDOR_FROM_DATABASE=Beko Elektronik A.S. + +acpi:BEL*: + ID_VENDOR_FROM_DATABASE=Beltronic Industrieelektronik GmbH + +acpi:BEO*: + ID_VENDOR_FROM_DATABASE=Baug & Olufsen + +acpi:BFE*: + ID_VENDOR_FROM_DATABASE=B.F. Engineering Corporation + +acpi:BGB*: + ID_VENDOR_FROM_DATABASE=Barco Graphics N.V + +acpi:BGT*: + ID_VENDOR_FROM_DATABASE=Budzetron Inc + +acpi:BHZ*: + ID_VENDOR_FROM_DATABASE=BitHeadz, Inc. + +acpi:BIA*: + ID_VENDOR_FROM_DATABASE=Biamp Systems Corporation + +acpi:BIC*: + ID_VENDOR_FROM_DATABASE=Big Island Communications + +acpi:BII*: + ID_VENDOR_FROM_DATABASE=Boeckeler Instruments Inc + +acpi:BIL*: + ID_VENDOR_FROM_DATABASE=Billion Electric Company Ltd + +acpi:BIO*: + ID_VENDOR_FROM_DATABASE=BioLink Technologies International, Inc. + +acpi:BIT*: + ID_VENDOR_FROM_DATABASE=Bit 3 Computer + +acpi:BLI*: + ID_VENDOR_FROM_DATABASE=Busicom + +acpi:BLN*: + ID_VENDOR_FROM_DATABASE=BioLink Technologies + +acpi:BLP*: + ID_VENDOR_FROM_DATABASE=Bloomberg L.P. + +acpi:BMD*: + ID_VENDOR_FROM_DATABASE=Blackmagic Design + +acpi:BMI*: + ID_VENDOR_FROM_DATABASE=Benson Medical Instruments Company + +acpi:BML*: + ID_VENDOR_FROM_DATABASE=BIOMED Lab + +acpi:BMM*: + ID_VENDOR_FROM_DATABASE=BMM + +acpi:BMS*: + ID_VENDOR_FROM_DATABASE=BIOMEDISYS + +acpi:BNE*: + ID_VENDOR_FROM_DATABASE=Bull AB + +acpi:BNK*: + ID_VENDOR_FROM_DATABASE=Banksia Tech Pty Ltd + +acpi:BNO*: + ID_VENDOR_FROM_DATABASE=Bang & Olufsen + +acpi:BNQ*: + ID_VENDOR_FROM_DATABASE=BenQ Corporation + +acpi:BNS*: + ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems + +acpi:BOB*: + ID_VENDOR_FROM_DATABASE=Rainy Orchard + +acpi:BOE*: + ID_VENDOR_FROM_DATABASE=BOE + +acpi:BOI*: + ID_VENDOR_FROM_DATABASE=NINGBO BOIGLE DIGITAL TECHNOLOGY CO.,LTD + +acpi:BOS*: + ID_VENDOR_FROM_DATABASE=BOS + +acpi:BPD*: + ID_VENDOR_FROM_DATABASE=Micro Solutions, Inc. + +acpi:BPS*: + ID_VENDOR_FROM_DATABASE=Barco, N.V. + +acpi:BPU*: + ID_VENDOR_FROM_DATABASE=Best Power + +acpi:BRA*: + ID_VENDOR_FROM_DATABASE=Braemac Pty Ltd + +acpi:BRC*: + ID_VENDOR_FROM_DATABASE=BARC + +acpi:BRG*: + ID_VENDOR_FROM_DATABASE=Bridge Information Co., Ltd + +acpi:BRI*: + ID_VENDOR_FROM_DATABASE=Boca Research Inc + +acpi:BRM*: + ID_VENDOR_FROM_DATABASE=Braemar Inc + +acpi:BRO*: + ID_VENDOR_FROM_DATABASE=BROTHER INDUSTRIES,LTD. + +acpi:BSE*: + ID_VENDOR_FROM_DATABASE=Bose Corporation + +acpi:BSG*: + ID_VENDOR_FROM_DATABASE=Robert Bosch GmbH + +acpi:BSL*: + ID_VENDOR_FROM_DATABASE=Biomedical Systems Laboratory + +acpi:BSN*: + ID_VENDOR_FROM_DATABASE=BRIGHTSIGN, LLC + +acpi:BST*: + ID_VENDOR_FROM_DATABASE=BodySound Technologies, Inc. + +acpi:BTC*: + ID_VENDOR_FROM_DATABASE=Bit 3 Computer + +acpi:BTE*: + ID_VENDOR_FROM_DATABASE=Brilliant Technology + +acpi:BTF*: + ID_VENDOR_FROM_DATABASE=Bitfield Oy + +acpi:BTI*: + ID_VENDOR_FROM_DATABASE=BusTech Inc + +acpi:BTO*: + ID_VENDOR_FROM_DATABASE=BioTao Ltd + +acpi:BUF*: + ID_VENDOR_FROM_DATABASE=Yasuhiko Shirai Melco Inc + +acpi:BUG*: + ID_VENDOR_FROM_DATABASE=B.U.G., Inc. + +acpi:BUJ*: + ID_VENDOR_FROM_DATABASE=ATI Tech Inc + +acpi:BUL*: + ID_VENDOR_FROM_DATABASE=Bull + +acpi:BUR*: + ID_VENDOR_FROM_DATABASE=Bernecker & Rainer Ind-Eletronik GmbH + +acpi:BUS*: + ID_VENDOR_FROM_DATABASE=BusTek + +acpi:BUT*: + ID_VENDOR_FROM_DATABASE=21ST CENTURY ENTERTAINMENT + +acpi:BWK*: + ID_VENDOR_FROM_DATABASE=Bitworks Inc. + +acpi:BXE*: + ID_VENDOR_FROM_DATABASE=Buxco Electronics + +acpi:BYD*: + ID_VENDOR_FROM_DATABASE=byd:sign corporation + +acpi:CAA*: + ID_VENDOR_FROM_DATABASE=Castles Automation Co., Ltd + +acpi:CAC*: + ID_VENDOR_FROM_DATABASE=CA & F Elettronica + +acpi:CAG*: + ID_VENDOR_FROM_DATABASE=CalComp + +acpi:CAI*: + ID_VENDOR_FROM_DATABASE=Canon Inc. + +acpi:CAL*: + ID_VENDOR_FROM_DATABASE=Acon + +acpi:CAM*: + ID_VENDOR_FROM_DATABASE=Cambridge Audio + +acpi:CAN*: + ID_VENDOR_FROM_DATABASE=Canopus Company Ltd + +acpi:CAR*: + ID_VENDOR_FROM_DATABASE=Cardinal Company Ltd + +acpi:CAS*: + ID_VENDOR_FROM_DATABASE=CASIO COMPUTER CO.,LTD + +acpi:CAT*: + ID_VENDOR_FROM_DATABASE=Consultancy in Advanced Technology + +acpi:CAV*: + ID_VENDOR_FROM_DATABASE=Cavium Networks, Inc + +acpi:CBI*: + ID_VENDOR_FROM_DATABASE=ComputerBoards Inc + +acpi:CBR*: + ID_VENDOR_FROM_DATABASE=Cebra Tech A/S + +acpi:CBT*: + ID_VENDOR_FROM_DATABASE=Cabletime Ltd + +acpi:CBX*: + ID_VENDOR_FROM_DATABASE=Cybex Computer Products Corporation + +acpi:CCC*: + ID_VENDOR_FROM_DATABASE=C-Cube Microsystems + +acpi:CCI*: + ID_VENDOR_FROM_DATABASE=Cache + +acpi:CCJ*: + ID_VENDOR_FROM_DATABASE=CONTEC CO.,LTD. + +acpi:CCL*: + ID_VENDOR_FROM_DATABASE=CCL/ITRI + +acpi:CCP*: + ID_VENDOR_FROM_DATABASE=Capetronic USA Inc + +acpi:CDC*: + ID_VENDOR_FROM_DATABASE=Core Dynamics Corporation + +acpi:CDD*: + ID_VENDOR_FROM_DATABASE=Convergent Data Devices + +acpi:CDE*: + ID_VENDOR_FROM_DATABASE=Colin.de + +acpi:CDG*: + ID_VENDOR_FROM_DATABASE=Christie Digital Systems Inc + +acpi:CDI*: + ID_VENDOR_FROM_DATABASE=Concept Development Inc + +acpi:CDK*: + ID_VENDOR_FROM_DATABASE=Cray Communications + +acpi:CDN*: + ID_VENDOR_FROM_DATABASE=Codenoll Technical Corporation + +acpi:CDP*: + ID_VENDOR_FROM_DATABASE=CalComp + +acpi:CDS*: + ID_VENDOR_FROM_DATABASE=Computer Diagnostic Systems + +acpi:CDT*: + ID_VENDOR_FROM_DATABASE=IBM Corporation + +acpi:CDV*: + ID_VENDOR_FROM_DATABASE=Convergent Design Inc. + +acpi:CEA*: + ID_VENDOR_FROM_DATABASE=Consumer Electronics Association + +acpi:CEC*: + ID_VENDOR_FROM_DATABASE=Chicony Electronics Company Ltd + +acpi:CED*: + ID_VENDOR_FROM_DATABASE=Cambridge Electronic Design Ltd + +acpi:CEF*: + ID_VENDOR_FROM_DATABASE=Cefar Digital Vision + +acpi:CEI*: + ID_VENDOR_FROM_DATABASE=Crestron Electronics, Inc. + +acpi:CEM*: + ID_VENDOR_FROM_DATABASE=MEC Electronics GmbH + +acpi:CEN*: + ID_VENDOR_FROM_DATABASE=Centurion Technologies P/L + +acpi:CEP*: + ID_VENDOR_FROM_DATABASE=C-DAC + +acpi:CER*: + ID_VENDOR_FROM_DATABASE=Ceronix + +acpi:CET*: + ID_VENDOR_FROM_DATABASE=TEC CORPORATION + +acpi:CFG*: + ID_VENDOR_FROM_DATABASE=Atlantis + +acpi:CGA*: + ID_VENDOR_FROM_DATABASE=Chunghwa Picture Tubes, LTD + +acpi:CGS*: + ID_VENDOR_FROM_DATABASE=Chyron Corp + +acpi:CGT*: + ID_VENDOR_FROM_DATABASE=congatec AG + +acpi:CHA*: + ID_VENDOR_FROM_DATABASE=Chase Research PLC + +acpi:CHC*: + ID_VENDOR_FROM_DATABASE=Chic Technology Corp. + +acpi:CHD*: + ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd + +acpi:CHE*: + ID_VENDOR_FROM_DATABASE=Acer Inc + +acpi:CHG*: + ID_VENDOR_FROM_DATABASE=Sichuan Changhong Electric CO, LTD. + +acpi:CHI*: + ID_VENDOR_FROM_DATABASE=Chrontel Inc + +acpi:CHL*: + ID_VENDOR_FROM_DATABASE=Chloride-R&D + +acpi:CHM*: + ID_VENDOR_FROM_DATABASE=CHIC TECHNOLOGY CORP. + +acpi:CHO*: + ID_VENDOR_FROM_DATABASE=Sichuang Changhong Corporation + +acpi:CHP*: + ID_VENDOR_FROM_DATABASE=CH Products + +acpi:CHS*: + ID_VENDOR_FROM_DATABASE=Agentur Chairos + +acpi:CHT*: + ID_VENDOR_FROM_DATABASE=Chunghwa Picture Tubes,LTD. + +acpi:CHY*: + ID_VENDOR_FROM_DATABASE=Cherry GmbH + +acpi:CIC*: + ID_VENDOR_FROM_DATABASE=Comm. Intelligence Corporation + +acpi:CII*: + ID_VENDOR_FROM_DATABASE=Cromack Industries Inc + +acpi:CIL*: + ID_VENDOR_FROM_DATABASE=Citicom Infotech Private Limited + +acpi:CIN*: + ID_VENDOR_FROM_DATABASE=Citron GmbH + +acpi:CIP*: + ID_VENDOR_FROM_DATABASE=Ciprico Inc + +acpi:CIR*: + ID_VENDOR_FROM_DATABASE=Cirrus Logic Inc + +acpi:CIS*: + ID_VENDOR_FROM_DATABASE=Cisco Systems Inc + +acpi:CIT*: + ID_VENDOR_FROM_DATABASE=Citifax Limited + +acpi:CKC*: + ID_VENDOR_FROM_DATABASE=The Concept Keyboard Company Ltd + +acpi:CKJ*: + ID_VENDOR_FROM_DATABASE=Carina System Co., Ltd. + +acpi:CLA*: + ID_VENDOR_FROM_DATABASE=Clarion Company Ltd + +acpi:CLD*: + ID_VENDOR_FROM_DATABASE=COMMAT L.t.d. + +acpi:CLE*: + ID_VENDOR_FROM_DATABASE=Classe Audio + +acpi:CLG*: + ID_VENDOR_FROM_DATABASE=CoreLogic + +acpi:CLI*: + ID_VENDOR_FROM_DATABASE=Cirrus Logic Inc + +acpi:CLM*: + ID_VENDOR_FROM_DATABASE=CrystaLake Multimedia + +acpi:CLO*: + ID_VENDOR_FROM_DATABASE=Clone Computers + +acpi:CLT*: + ID_VENDOR_FROM_DATABASE=automated computer control systems + +acpi:CLV*: + ID_VENDOR_FROM_DATABASE=Clevo Company + +acpi:CLX*: + ID_VENDOR_FROM_DATABASE=CardLogix + +acpi:CMC*: + ID_VENDOR_FROM_DATABASE=CMC Ltd + +acpi:CMD*: + ID_VENDOR_FROM_DATABASE=Colorado MicroDisplay, Inc. + +acpi:CMG*: + ID_VENDOR_FROM_DATABASE=Chenming Mold Ind. Corp. + +acpi:CMI*: + ID_VENDOR_FROM_DATABASE=C-Media Electronics + +acpi:CMM*: + ID_VENDOR_FROM_DATABASE=Comtime GmbH + +acpi:CMN*: + ID_VENDOR_FROM_DATABASE=Chimei Innolux Corporation + +acpi:CMO*: + ID_VENDOR_FROM_DATABASE=Chi Mei Optoelectronics corp. + +acpi:CMR*: + ID_VENDOR_FROM_DATABASE=Cambridge Research Systems Ltd + +acpi:CMS*: + ID_VENDOR_FROM_DATABASE=CompuMaster Srl + +acpi:CMX*: + ID_VENDOR_FROM_DATABASE=Comex Electronics AB + +acpi:CNB*: + ID_VENDOR_FROM_DATABASE=American Power Conversion + +acpi:CNC*: + ID_VENDOR_FROM_DATABASE=Alvedon Computers Ltd + +acpi:CNE*: + ID_VENDOR_FROM_DATABASE=Cine-tal + +acpi:CNI*: + ID_VENDOR_FROM_DATABASE=Connect Int'l A/S + +acpi:CNN*: + ID_VENDOR_FROM_DATABASE=Canon Inc + +acpi:CNT*: + ID_VENDOR_FROM_DATABASE=COINT Multimedia Systems + +acpi:COB*: + ID_VENDOR_FROM_DATABASE=COBY Electronics Co., Ltd + +acpi:COD*: + ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd. + +acpi:COG*: + ID_VENDOR_FROM_DATABASE=Cogent + +acpi:COI*: + ID_VENDOR_FROM_DATABASE=Codec Inc. + +acpi:COL*: + ID_VENDOR_FROM_DATABASE=Rockwell Collins, Inc. + +acpi:COM*: + ID_VENDOR_FROM_DATABASE=Comtrol Corporation + +acpi:CON*: + ID_VENDOR_FROM_DATABASE=Contec Company Ltd + +acpi:COO*: + ID_VENDOR_FROM_DATABASE=coolux GmbH + +acpi:COR*: + ID_VENDOR_FROM_DATABASE=Corollary Inc + +acpi:COS*: + ID_VENDOR_FROM_DATABASE=CoStar Corporation + +acpi:COT*: + ID_VENDOR_FROM_DATABASE=Core Technology Inc + +acpi:COW*: + ID_VENDOR_FROM_DATABASE=Polycow Productions + +acpi:COX*: + ID_VENDOR_FROM_DATABASE=Comrex + +acpi:CPC*: + ID_VENDOR_FROM_DATABASE=Ciprico Inc + +acpi:CPD*: + ID_VENDOR_FROM_DATABASE=CompuAdd + +acpi:CPI*: + ID_VENDOR_FROM_DATABASE=Computer Peripherals Inc + +acpi:CPL*: + ID_VENDOR_FROM_DATABASE=Compal Electronics Inc + +acpi:CPM*: + ID_VENDOR_FROM_DATABASE=Capella Microsystems Inc. + +acpi:CPP*: + ID_VENDOR_FROM_DATABASE=Compound Photonics + +acpi:CPQ*: + ID_VENDOR_FROM_DATABASE=Compaq Computer Company + +acpi:CPT*: + ID_VENDOR_FROM_DATABASE=cPATH + +acpi:CPX*: + ID_VENDOR_FROM_DATABASE=Powermatic Data Systems + +acpi:CRA*: + ID_VENDOR_FROM_DATABASE=CRALTECH ELECTRONICA, S.L. + +acpi:CRC*: + ID_VENDOR_FROM_DATABASE=CONRAC GmbH + +acpi:CRD*: + ID_VENDOR_FROM_DATABASE=Cardinal Technical Inc + +acpi:CRE*: + ID_VENDOR_FROM_DATABASE=Creative Labs Inc + +acpi:CRH*: + ID_VENDOR_FROM_DATABASE=Contemporary Research Corp. + +acpi:CRI*: + ID_VENDOR_FROM_DATABASE=Crio Inc. + +acpi:CRL*: + ID_VENDOR_FROM_DATABASE=Creative Logic + +acpi:CRN*: + ID_VENDOR_FROM_DATABASE=Cornerstone Imaging + +acpi:CRO*: + ID_VENDOR_FROM_DATABASE=Extraordinary Technologies PTY Limited + +acpi:CRQ*: + ID_VENDOR_FROM_DATABASE=Cirque Corporation + +acpi:CRS*: + ID_VENDOR_FROM_DATABASE=Crescendo Communication Inc + +acpi:CRV*: + ID_VENDOR_FROM_DATABASE=Cerevo Inc. + +acpi:CRX*: + ID_VENDOR_FROM_DATABASE=Cyrix Corporation + +acpi:CSB*: + ID_VENDOR_FROM_DATABASE=Transtex SA + +acpi:CSC*: + ID_VENDOR_FROM_DATABASE=Crystal Semiconductor + +acpi:CSD*: + ID_VENDOR_FROM_DATABASE=Cresta Systems Inc + +acpi:CSE*: + ID_VENDOR_FROM_DATABASE=Concept Solutions & Engineering + +acpi:CSI*: + ID_VENDOR_FROM_DATABASE=Cabletron System Inc + +acpi:CSL*: + ID_VENDOR_FROM_DATABASE=Cloudium Systems Ltd. + +acpi:CSM*: + ID_VENDOR_FROM_DATABASE=Cosmic Engineering Inc. + +acpi:CSO*: + ID_VENDOR_FROM_DATABASE=California Institute of Technology + +acpi:CSS*: + ID_VENDOR_FROM_DATABASE=CSS Laboratories + +acpi:CST*: + ID_VENDOR_FROM_DATABASE=CSTI Inc + +acpi:CTA*: + ID_VENDOR_FROM_DATABASE=CoSystems Inc + +acpi:CTC*: + ID_VENDOR_FROM_DATABASE=CTC Communication Development Company Ltd + +acpi:CTE*: + ID_VENDOR_FROM_DATABASE=Chunghwa Telecom Co., Ltd. + +acpi:CTL*: + ID_VENDOR_FROM_DATABASE=Creative Technology Ltd + +acpi:CTM*: + ID_VENDOR_FROM_DATABASE=Computerm Corporation + +acpi:CTN*: + ID_VENDOR_FROM_DATABASE=Computone Products + +acpi:CTP*: + ID_VENDOR_FROM_DATABASE=Computer Technology Corporation + +acpi:CTR*: + ID_VENDOR_FROM_DATABASE=Control4 Corporation + +acpi:CTS*: + ID_VENDOR_FROM_DATABASE=Comtec Systems Co., Ltd. + +acpi:CTX*: + ID_VENDOR_FROM_DATABASE=Creatix Polymedia GmbH + +acpi:CUB*: + ID_VENDOR_FROM_DATABASE=Cubix Corporation + +acpi:CUK*: + ID_VENDOR_FROM_DATABASE=Calibre UK Ltd + +acpi:CVA*: + ID_VENDOR_FROM_DATABASE=Covia Inc. + +acpi:CVI*: + ID_VENDOR_FROM_DATABASE=Colorado Video, Inc. + +acpi:CVP*: + ID_VENDOR_FROM_DATABASE=Chromatec Video Products Ltd + +acpi:CVS*: + ID_VENDOR_FROM_DATABASE=Clarity Visual Systems + +acpi:CWC*: + ID_VENDOR_FROM_DATABASE=Curtiss-Wright Controls, Inc. + +acpi:CWR*: + ID_VENDOR_FROM_DATABASE=Connectware Inc + +acpi:CXT*: + ID_VENDOR_FROM_DATABASE=Conexant Systems + +acpi:CYB*: + ID_VENDOR_FROM_DATABASE=CyberVision + +acpi:CYC*: + ID_VENDOR_FROM_DATABASE=Cylink Corporation + +acpi:CYD*: + ID_VENDOR_FROM_DATABASE=Cyclades Corporation + +acpi:CYL*: + ID_VENDOR_FROM_DATABASE=Cyberlabs + +acpi:CYP*: + ID_VENDOR_FROM_DATABASE=CYPRESS SEMICONDUCTOR CORPORATION + +acpi:CYT*: + ID_VENDOR_FROM_DATABASE=Cytechinfo Inc + +acpi:CYV*: + ID_VENDOR_FROM_DATABASE=Cyviz AS + +acpi:CYW*: + ID_VENDOR_FROM_DATABASE=Cyberware + +acpi:CYX*: + ID_VENDOR_FROM_DATABASE=Cyrix Corporation + +acpi:CZC*: + ID_VENDOR_FROM_DATABASE=Shenzhen ChuangZhiCheng Technology Co., Ltd. + +acpi:CZE*: + ID_VENDOR_FROM_DATABASE=Carl Zeiss AG + +acpi:DAC*: + ID_VENDOR_FROM_DATABASE=Digital Acoustics Corporation + +acpi:DAE*: + ID_VENDOR_FROM_DATABASE=Digatron Industrie Elektronik GmbH + +acpi:DAI*: + ID_VENDOR_FROM_DATABASE=DAIS SET Ltd. + +acpi:DAK*: + ID_VENDOR_FROM_DATABASE=Daktronics + +acpi:DAL*: + ID_VENDOR_FROM_DATABASE=Digital Audio Labs Inc + +acpi:DAN*: + ID_VENDOR_FROM_DATABASE=Danelec Marine A/S + +acpi:DAS*: + ID_VENDOR_FROM_DATABASE=DAVIS AS + +acpi:DAT*: + ID_VENDOR_FROM_DATABASE=Datel Inc + +acpi:DAU*: + ID_VENDOR_FROM_DATABASE=Daou Tech Inc + +acpi:DAV*: + ID_VENDOR_FROM_DATABASE=Davicom Semiconductor Inc + +acpi:DAW*: + ID_VENDOR_FROM_DATABASE=DA2 Technologies Inc + +acpi:DAX*: + ID_VENDOR_FROM_DATABASE=Data Apex Ltd + +acpi:DBD*: + ID_VENDOR_FROM_DATABASE=Diebold Inc. + +acpi:DBI*: + ID_VENDOR_FROM_DATABASE=DigiBoard Inc + +acpi:DBK*: + ID_VENDOR_FROM_DATABASE=Databook Inc + +acpi:DBL*: + ID_VENDOR_FROM_DATABASE=Doble Engineering Company + +acpi:DBN*: + ID_VENDOR_FROM_DATABASE=DB Networks Inc + +acpi:DCA*: + ID_VENDOR_FROM_DATABASE=Digital Communications Association + +acpi:DCC*: + ID_VENDOR_FROM_DATABASE=Dale Computer Corporation + +acpi:DCD*: + ID_VENDOR_FROM_DATABASE=Datacast LLC + +acpi:DCE*: + ID_VENDOR_FROM_DATABASE=dSPACE GmbH + +acpi:DCI*: + ID_VENDOR_FROM_DATABASE=Concepts Inc + +acpi:DCL*: + ID_VENDOR_FROM_DATABASE=Dynamic Controls Ltd + +acpi:DCM*: + ID_VENDOR_FROM_DATABASE=DCM Data Products + +acpi:DCO*: + ID_VENDOR_FROM_DATABASE=Dialogue Technology Corporation + +acpi:DCR*: + ID_VENDOR_FROM_DATABASE=Decros Ltd + +acpi:DCS*: + ID_VENDOR_FROM_DATABASE=Diamond Computer Systems Inc + +acpi:DCT*: + ID_VENDOR_FROM_DATABASE=Dancall Telecom A/S + +acpi:DCV*: + ID_VENDOR_FROM_DATABASE=Datatronics Technology Inc + +acpi:DDA*: + ID_VENDOR_FROM_DATABASE=DA2 Technologies Corporation + +acpi:DDD*: + ID_VENDOR_FROM_DATABASE=Danka Data Devices + +acpi:DDE*: + ID_VENDOR_FROM_DATABASE=Datasat Digital Entertainment + +acpi:DDI*: + ID_VENDOR_FROM_DATABASE=Data Display AG + +acpi:DDS*: + ID_VENDOR_FROM_DATABASE=Barco, N.V. + +acpi:DDT*: + ID_VENDOR_FROM_DATABASE=Datadesk Technologies Inc + +acpi:DDV*: + ID_VENDOR_FROM_DATABASE=Delta Information Systems, Inc + +acpi:DEC*: + ID_VENDOR_FROM_DATABASE=Digital Equipment Corporation + +acpi:DEI*: + ID_VENDOR_FROM_DATABASE=Deico Electronics + +acpi:DEL*: + ID_VENDOR_FROM_DATABASE=Dell Inc. + +acpi:DEN*: + ID_VENDOR_FROM_DATABASE=Densitron Computers Ltd + +acpi:DEX*: + ID_VENDOR_FROM_DATABASE=idex displays + +acpi:DFI*: + ID_VENDOR_FROM_DATABASE=DFI + +acpi:DFK*: + ID_VENDOR_FROM_DATABASE=SharkTec A/S + +acpi:DFT*: + ID_VENDOR_FROM_DATABASE=DEI Holdings dba Definitive Technology + +acpi:DGA*: + ID_VENDOR_FROM_DATABASE=Digiital Arts Inc + +acpi:DGC*: + ID_VENDOR_FROM_DATABASE=Data General Corporation + +acpi:DGI*: + ID_VENDOR_FROM_DATABASE=DIGI International + +acpi:DGK*: + ID_VENDOR_FROM_DATABASE=DugoTech Co., LTD + +acpi:DGP*: + ID_VENDOR_FROM_DATABASE=Digicorp European sales S.A. + +acpi:DGS*: + ID_VENDOR_FROM_DATABASE=Diagsoft Inc + +acpi:DGT*: + ID_VENDOR_FROM_DATABASE=Dearborn Group Technology + +acpi:DHD*: + ID_VENDOR_FROM_DATABASE=Dension Audio Systems + +acpi:DHP*: + ID_VENDOR_FROM_DATABASE=DH Print + +acpi:DHQ*: + ID_VENDOR_FROM_DATABASE=Quadram + +acpi:DHT*: + ID_VENDOR_FROM_DATABASE=Projectavision Inc + +acpi:DIA*: + ID_VENDOR_FROM_DATABASE=Diadem + +acpi:DIG*: + ID_VENDOR_FROM_DATABASE=Digicom S.p.A. + +acpi:DII*: + ID_VENDOR_FROM_DATABASE=Dataq Instruments Inc + +acpi:DIM*: + ID_VENDOR_FROM_DATABASE=dPict Imaging, Inc. + +acpi:DIN*: + ID_VENDOR_FROM_DATABASE=Daintelecom Co., Ltd + +acpi:DIS*: + ID_VENDOR_FROM_DATABASE=Diseda S.A. + +acpi:DIT*: + ID_VENDOR_FROM_DATABASE=Dragon Information Technology + +acpi:DJE*: + ID_VENDOR_FROM_DATABASE=Capstone Visual Product Development + +acpi:DJP*: + ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd + +acpi:DKY*: + ID_VENDOR_FROM_DATABASE=Datakey Inc + +acpi:DLB*: + ID_VENDOR_FROM_DATABASE=Dolby Laboratories Inc. + +acpi:DLC*: + ID_VENDOR_FROM_DATABASE=Diamond Lane Comm. Corporation + +acpi:DLG*: + ID_VENDOR_FROM_DATABASE=Digital-Logic GmbH + +acpi:DLK*: + ID_VENDOR_FROM_DATABASE=D-Link Systems Inc + +acpi:DLL*: + ID_VENDOR_FROM_DATABASE=Dell Inc + +acpi:DLT*: + ID_VENDOR_FROM_DATABASE=Digitelec Informatique Park Cadera + +acpi:DMB*: + ID_VENDOR_FROM_DATABASE=Digicom Systems Inc + +acpi:DMC*: + ID_VENDOR_FROM_DATABASE=Dune Microsystems Corporation + +acpi:DMM*: + ID_VENDOR_FROM_DATABASE=Dimond Multimedia Systems Inc + +acpi:DMO*: + ID_VENDOR_FROM_DATABASE=Data Modul AG + +acpi:DMP*: + ID_VENDOR_FROM_DATABASE=D&M Holdings Inc, Professional Business Company + +acpi:DMS*: + ID_VENDOR_FROM_DATABASE=DOME imaging systems + +acpi:DMT*: + ID_VENDOR_FROM_DATABASE=Distributed Management Task Force, Inc. (DMTF) + +acpi:DMV*: + ID_VENDOR_FROM_DATABASE=NDS Ltd + +acpi:DNA*: + ID_VENDOR_FROM_DATABASE=DNA Enterprises, Inc. + +acpi:DNG*: + ID_VENDOR_FROM_DATABASE=Apache Micro Peripherals Inc + +acpi:DNI*: + ID_VENDOR_FROM_DATABASE=Deterministic Networks Inc. + +acpi:DNT*: + ID_VENDOR_FROM_DATABASE=Dr. Neuhous Telekommunikation GmbH + +acpi:DNV*: + ID_VENDOR_FROM_DATABASE=DiCon + +acpi:DOL*: + ID_VENDOR_FROM_DATABASE=Dolman Technologies Group Inc + +acpi:DOM*: + ID_VENDOR_FROM_DATABASE=Dome Imaging Systems + +acpi:DON*: + ID_VENDOR_FROM_DATABASE=DENON, Ltd. + +acpi:DOT*: + ID_VENDOR_FROM_DATABASE=Dotronic Mikroelektronik GmbH + +acpi:DPA*: + ID_VENDOR_FROM_DATABASE=DigiTalk Pro AV + +acpi:DPC*: + ID_VENDOR_FROM_DATABASE=Delta Electronics Inc + +acpi:DPH*: + ID_VENDOR_FROM_DATABASE=Delphi Automotive LLP + +acpi:DPI*: + ID_VENDOR_FROM_DATABASE=DocuPoint + +acpi:DPL*: + ID_VENDOR_FROM_DATABASE=Digital Projection Limited + +acpi:DPM*: + ID_VENDOR_FROM_DATABASE=ADPM Synthesis sas + +acpi:DPS*: + ID_VENDOR_FROM_DATABASE=Digital Processing Systems + +acpi:DPT*: + ID_VENDOR_FROM_DATABASE=DPT + +acpi:DPX*: + ID_VENDOR_FROM_DATABASE=DpiX, Inc. + +acpi:DQB*: + ID_VENDOR_FROM_DATABASE=Datacube Inc + +acpi:DRB*: + ID_VENDOR_FROM_DATABASE=Dr. Bott KG + +acpi:DRC*: + ID_VENDOR_FROM_DATABASE=Data Ray Corp. + +acpi:DRD*: + ID_VENDOR_FROM_DATABASE=DIGITAL REFLECTION INC. + +acpi:DRI*: + ID_VENDOR_FROM_DATABASE=Data Race Inc + +acpi:DRS*: + ID_VENDOR_FROM_DATABASE=DRS Defense Solutions, LLC + +acpi:DSA*: + ID_VENDOR_FROM_DATABASE=Display Solution AG + +acpi:DSD*: + ID_VENDOR_FROM_DATABASE=DS Multimedia Pte Ltd + +acpi:DSI*: + ID_VENDOR_FROM_DATABASE=Digitan Systems Inc + +acpi:DSM*: + ID_VENDOR_FROM_DATABASE=DSM Digital Services GmbH + +acpi:DSP*: + ID_VENDOR_FROM_DATABASE=Domain Technology Inc + +acpi:DTA*: + ID_VENDOR_FROM_DATABASE=DELTATEC + +acpi:DTC*: + ID_VENDOR_FROM_DATABASE=DTC Tech Corporation + +acpi:DTE*: + ID_VENDOR_FROM_DATABASE=Dimension Technologies, Inc. + +acpi:DTI*: + ID_VENDOR_FROM_DATABASE=Diversified Technology, Inc. + +acpi:DTK*: + ID_VENDOR_FROM_DATABASE=Dynax Electronics (HK) Ltd + +acpi:DTL*: + ID_VENDOR_FROM_DATABASE=e-Net Inc + +acpi:DTN*: + ID_VENDOR_FROM_DATABASE=Datang Telephone Co + +acpi:DTO*: + ID_VENDOR_FROM_DATABASE=Deutsche Thomson OHG + +acpi:DTT*: + ID_VENDOR_FROM_DATABASE=Design & Test Technology, Inc. + +acpi:DTX*: + ID_VENDOR_FROM_DATABASE=Data Translation + +acpi:DUA*: + ID_VENDOR_FROM_DATABASE=Dosch & Amand GmbH & Company KG + +acpi:DUN*: + ID_VENDOR_FROM_DATABASE=NCR Corporation + +acpi:DVD*: + ID_VENDOR_FROM_DATABASE=Dictaphone Corporation + +acpi:DVL*: + ID_VENDOR_FROM_DATABASE=Devolo AG + +acpi:DVS*: + ID_VENDOR_FROM_DATABASE=Digital Video System + +acpi:DVT*: + ID_VENDOR_FROM_DATABASE=Data Video + +acpi:DWE*: + ID_VENDOR_FROM_DATABASE=Daewoo Electronics Company Ltd + +acpi:DXC*: + ID_VENDOR_FROM_DATABASE=Digipronix Control Systems + +acpi:DXD*: + ID_VENDOR_FROM_DATABASE=DECIMATOR DESIGN PTY LTD + +acpi:DXL*: + ID_VENDOR_FROM_DATABASE=Dextera Labs Inc + +acpi:DXP*: + ID_VENDOR_FROM_DATABASE=Data Expert Corporation + +acpi:DXS*: + ID_VENDOR_FROM_DATABASE=Signet + +acpi:DYC*: + ID_VENDOR_FROM_DATABASE=Dycam Inc + +acpi:DYM*: + ID_VENDOR_FROM_DATABASE=Dymo-CoStar Corporation + +acpi:DYN*: + ID_VENDOR_FROM_DATABASE=Askey Computer Corporation + +acpi:DYX*: + ID_VENDOR_FROM_DATABASE=Dynax Electronics (HK) Ltd + +acpi:EAG*: + ID_VENDOR_FROM_DATABASE=ELTEC Elektronik AG + +acpi:EAS*: + ID_VENDOR_FROM_DATABASE=Evans and Sutherland Computer + +acpi:EBH*: + ID_VENDOR_FROM_DATABASE=Data Price Informatica + +acpi:EBS*: + ID_VENDOR_FROM_DATABASE=EBS Euchner Büro- und Schulsysteme GmbH + +acpi:EBT*: + ID_VENDOR_FROM_DATABASE=HUALONG TECHNOLOGY CO., LTD + +acpi:ECA*: + ID_VENDOR_FROM_DATABASE=Electro Cam Corp. + +acpi:ECC*: + ID_VENDOR_FROM_DATABASE=ESSential Comm. Corporation + +acpi:ECH*: + ID_VENDOR_FROM_DATABASE=EchoStar Corporation + +acpi:ECI*: + ID_VENDOR_FROM_DATABASE=Enciris Technologies + +acpi:ECK*: + ID_VENDOR_FROM_DATABASE=Eugene Chukhlomin Sole Proprietorship, d.b.a. + +acpi:ECL*: + ID_VENDOR_FROM_DATABASE=Excel Company Ltd + +acpi:ECM*: + ID_VENDOR_FROM_DATABASE=E-Cmos Tech Corporation + +acpi:ECO*: + ID_VENDOR_FROM_DATABASE=Echo Speech Corporation + +acpi:ECP*: + ID_VENDOR_FROM_DATABASE=Elecom Company Ltd + +acpi:ECS*: + ID_VENDOR_FROM_DATABASE=Elitegroup Computer Systems Company Ltd + +acpi:ECT*: + ID_VENDOR_FROM_DATABASE=Enciris Technologies + +acpi:EDC*: + ID_VENDOR_FROM_DATABASE=e.Digital Corporation + +acpi:EDG*: + ID_VENDOR_FROM_DATABASE=Electronic-Design GmbH + +acpi:EDI*: + ID_VENDOR_FROM_DATABASE=Edimax Tech. Company Ltd + +acpi:EDM*: + ID_VENDOR_FROM_DATABASE=EDMI + +acpi:EDT*: + ID_VENDOR_FROM_DATABASE=Emerging Display Technologies Corp + +acpi:EEE*: + ID_VENDOR_FROM_DATABASE=ET&T Technology Company Ltd + +acpi:EEH*: + ID_VENDOR_FROM_DATABASE=EEH Datalink GmbH + +acpi:EEP*: + ID_VENDOR_FROM_DATABASE=E.E.P.D. GmbH + +acpi:EES*: + ID_VENDOR_FROM_DATABASE=EE Solutions, Inc. + +acpi:EGA*: + ID_VENDOR_FROM_DATABASE=Elgato Systems LLC + +acpi:EGD*: + ID_VENDOR_FROM_DATABASE=EIZO GmbH Display Technologies + +acpi:EGL*: + ID_VENDOR_FROM_DATABASE=Eagle Technology + +acpi:EGN*: + ID_VENDOR_FROM_DATABASE=Egenera, Inc. + +acpi:EGO*: + ID_VENDOR_FROM_DATABASE=Ergo Electronics + +acpi:EHJ*: + ID_VENDOR_FROM_DATABASE=Epson Research + +acpi:EHN*: + ID_VENDOR_FROM_DATABASE=Enhansoft + +acpi:EIC*: + ID_VENDOR_FROM_DATABASE=Eicon Technology Corporation + +acpi:EIZ*: + ID_VENDOR_FROM_DATABASE=Eizo + +acpi:EKA*: + ID_VENDOR_FROM_DATABASE=MagTek Inc. + +acpi:EKC*: + ID_VENDOR_FROM_DATABASE=Eastman Kodak Company + +acpi:EKS*: + ID_VENDOR_FROM_DATABASE=EKSEN YAZILIM + +acpi:ELA*: + ID_VENDOR_FROM_DATABASE=ELAD srl + +acpi:ELC*: + ID_VENDOR_FROM_DATABASE=Electro Scientific Ind + +acpi:ELE*: + ID_VENDOR_FROM_DATABASE=Elecom Company Ltd + +acpi:ELG*: + ID_VENDOR_FROM_DATABASE=Elmeg GmbH Kommunikationstechnik + +acpi:ELI*: + ID_VENDOR_FROM_DATABASE=Edsun Laboratories + +acpi:ELL*: + ID_VENDOR_FROM_DATABASE=Electrosonic Ltd + +acpi:ELM*: + ID_VENDOR_FROM_DATABASE=Elmic Systems Inc + +acpi:ELO*: + ID_VENDOR_FROM_DATABASE=Elo TouchSystems Inc + +acpi:ELS*: + ID_VENDOR_FROM_DATABASE=ELSA GmbH + +acpi:ELT*: + ID_VENDOR_FROM_DATABASE=Element Labs, Inc. + +acpi:ELU*: + ID_VENDOR_FROM_DATABASE=Express Industrial, Ltd. + +acpi:ELX*: + ID_VENDOR_FROM_DATABASE=Elonex PLC + +acpi:EMB*: + ID_VENDOR_FROM_DATABASE=Embedded computing inc ltd + +acpi:EMC*: + ID_VENDOR_FROM_DATABASE=eMicro Corporation + +acpi:EMD*: + ID_VENDOR_FROM_DATABASE=Embrionix Design Inc. + +acpi:EME*: + ID_VENDOR_FROM_DATABASE=EMiNE TECHNOLOGY COMPANY, LTD. + +acpi:EMG*: + ID_VENDOR_FROM_DATABASE=EMG Consultants Inc + +acpi:EMI*: + ID_VENDOR_FROM_DATABASE=Ex Machina Inc + +acpi:EMK*: + ID_VENDOR_FROM_DATABASE=Emcore Corporation + +acpi:EMO*: + ID_VENDOR_FROM_DATABASE=ELMO COMPANY, LIMITED + +acpi:EMU*: + ID_VENDOR_FROM_DATABASE=Emulex Corporation + +acpi:ENC*: + ID_VENDOR_FROM_DATABASE=Eizo Nanao Corporation + +acpi:END*: + ID_VENDOR_FROM_DATABASE=ENIDAN Technologies Ltd + +acpi:ENE*: + ID_VENDOR_FROM_DATABASE=ENE Technology Inc. + +acpi:ENI*: + ID_VENDOR_FROM_DATABASE=Efficient Networks + +acpi:ENS*: + ID_VENDOR_FROM_DATABASE=Ensoniq Corporation + +acpi:ENT*: + ID_VENDOR_FROM_DATABASE=Enterprise Comm. & Computing Inc + +acpi:EON*: + ID_VENDOR_FROM_DATABASE=Eon Instrumentation, Inc. + +acpi:EPC*: + ID_VENDOR_FROM_DATABASE=Empac + +acpi:EPH*: + ID_VENDOR_FROM_DATABASE=Epiphan Systems Inc. + +acpi:EPI*: + ID_VENDOR_FROM_DATABASE=Envision Peripherals, Inc + +acpi:EPN*: + ID_VENDOR_FROM_DATABASE=EPiCON Inc. + +acpi:EPS*: + ID_VENDOR_FROM_DATABASE=KEPS + +acpi:EQP*: + ID_VENDOR_FROM_DATABASE=Equipe Electronics Ltd. + +acpi:EQX*: + ID_VENDOR_FROM_DATABASE=Equinox Systems Inc + +acpi:ERG*: + ID_VENDOR_FROM_DATABASE=Ergo System + +acpi:ERI*: + ID_VENDOR_FROM_DATABASE=Ericsson Mobile Communications AB + +acpi:ERN*: + ID_VENDOR_FROM_DATABASE=Ericsson, Inc. + +acpi:ERP*: + ID_VENDOR_FROM_DATABASE=Euraplan GmbH + +acpi:ERS*: + ID_VENDOR_FROM_DATABASE=Eizo Rugged Solutions + +acpi:ERT*: + ID_VENDOR_FROM_DATABASE=Escort Insturments Corporation + +acpi:ESA*: + ID_VENDOR_FROM_DATABASE=Elbit Systems of America + +acpi:ESB*: + ID_VENDOR_FROM_DATABASE=Esterline Belgium BVBA + +acpi:ESC*: + ID_VENDOR_FROM_DATABASE=Eden Sistemas de Computacao S/A + +acpi:ESD*: + ID_VENDOR_FROM_DATABASE=Ensemble Designs, Inc + +acpi:ESG*: + ID_VENDOR_FROM_DATABASE=ELCON Systemtechnik GmbH + +acpi:ESI*: + ID_VENDOR_FROM_DATABASE=Extended Systems, Inc. + +acpi:ESK*: + ID_VENDOR_FROM_DATABASE=ES&S + +acpi:ESL*: + ID_VENDOR_FROM_DATABASE=Esterline Technologies + +acpi:ESN*: + ID_VENDOR_FROM_DATABASE=eSATURNUS + +acpi:ESS*: + ID_VENDOR_FROM_DATABASE=ESS Technology Inc + +acpi:EST*: + ID_VENDOR_FROM_DATABASE=Embedded Solution Technology + +acpi:ESY*: + ID_VENDOR_FROM_DATABASE=E-Systems Inc + +acpi:ETC*: + ID_VENDOR_FROM_DATABASE=Everton Technology Company Ltd + +acpi:ETD*: + ID_VENDOR_FROM_DATABASE=ELAN MICROELECTRONICS CORPORATION + +acpi:ETH*: + ID_VENDOR_FROM_DATABASE=Etherboot Project + +acpi:ETI*: + ID_VENDOR_FROM_DATABASE=Eclipse Tech Inc + +acpi:ETK*: + ID_VENDOR_FROM_DATABASE=eTEK Labs Inc. + +acpi:ETL*: + ID_VENDOR_FROM_DATABASE=Evertz Microsystems Ltd. + +acpi:ETS*: + ID_VENDOR_FROM_DATABASE=Electronic Trade Solutions Ltd + +acpi:ETT*: + ID_VENDOR_FROM_DATABASE=E-Tech Inc + +acpi:EUT*: + ID_VENDOR_FROM_DATABASE=Ericsson Mobile Networks B.V. + +acpi:EVE*: + ID_VENDOR_FROM_DATABASE=Advanced Micro Peripherals Ltd + +acpi:EVI*: + ID_VENDOR_FROM_DATABASE=eviateg GmbH + +acpi:EVX*: + ID_VENDOR_FROM_DATABASE=Everex + +acpi:EXA*: + ID_VENDOR_FROM_DATABASE=Exabyte + +acpi:EXC*: + ID_VENDOR_FROM_DATABASE=Excession Audio + +acpi:EXI*: + ID_VENDOR_FROM_DATABASE=Exide Electronics + +acpi:EXN*: + ID_VENDOR_FROM_DATABASE=RGB Systems, Inc. dba Extron Electronics + +acpi:EXP*: + ID_VENDOR_FROM_DATABASE=Data Export Corporation + +acpi:EXR*: + ID_VENDOR_FROM_DATABASE=Explorer Inc. + +acpi:EXT*: + ID_VENDOR_FROM_DATABASE=Exatech Computadores & Servicos Ltda + +acpi:EXX*: + ID_VENDOR_FROM_DATABASE=Exxact GmbH + +acpi:EXY*: + ID_VENDOR_FROM_DATABASE=Exterity Ltd + +acpi:EYE*: + ID_VENDOR_FROM_DATABASE=eyevis GmbH + +acpi:EYF*: + ID_VENDOR_FROM_DATABASE=eyefactive Gmbh + +acpi:EZE*: + ID_VENDOR_FROM_DATABASE=EzE Technologies + +acpi:EZP*: + ID_VENDOR_FROM_DATABASE=Storm Technology + +acpi:FAN*: + ID_VENDOR_FROM_DATABASE=Fantalooks Co., Ltd. + +acpi:FAR*: + ID_VENDOR_FROM_DATABASE=Farallon Computing + +acpi:FBI*: + ID_VENDOR_FROM_DATABASE=Interface Corporation + +acpi:FCB*: + ID_VENDOR_FROM_DATABASE=Furukawa Electric Company Ltd + +acpi:FCG*: + ID_VENDOR_FROM_DATABASE=First International Computer Ltd + +acpi:FCM*: + ID_VENDOR_FROM_DATABASE=Funai + +acpi:FCS*: + ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc. + +acpi:FDC*: + ID_VENDOR_FROM_DATABASE=Future Domain + +acpi:FDD*: + ID_VENDOR_FROM_DATABASE=Forth Dimension Displays Ltd + +acpi:FDI*: + ID_VENDOR_FROM_DATABASE=Future Designs, Inc. + +acpi:FDT*: + ID_VENDOR_FROM_DATABASE=Fujitsu Display Technologies Corp. + +acpi:FEC*: + ID_VENDOR_FROM_DATABASE=FURUNO ELECTRIC CO., LTD. + +acpi:FEL*: + ID_VENDOR_FROM_DATABASE=Fellowes & Questec + +acpi:FEN*: + ID_VENDOR_FROM_DATABASE=Fen Systems Ltd. + +acpi:FER*: + ID_VENDOR_FROM_DATABASE=Ferranti Int'L + +acpi:FFC*: + ID_VENDOR_FROM_DATABASE=FUJIFILM Corporation + +acpi:FFI*: + ID_VENDOR_FROM_DATABASE=Fairfield Industries + +acpi:FGD*: + ID_VENDOR_FROM_DATABASE=Lisa Draexlmaier GmbH + +acpi:FGL*: + ID_VENDOR_FROM_DATABASE=Fujitsu General Limited. + +acpi:FHL*: + ID_VENDOR_FROM_DATABASE=FHLP + +acpi:FIC*: + ID_VENDOR_FROM_DATABASE=Formosa Industrial Computing Inc + +acpi:FIL*: + ID_VENDOR_FROM_DATABASE=Forefront Int'l Ltd + +acpi:FIN*: + ID_VENDOR_FROM_DATABASE=Finecom Co., Ltd. + +acpi:FIR*: + ID_VENDOR_FROM_DATABASE=Chaplet Systems Inc + +acpi:FIS*: + ID_VENDOR_FROM_DATABASE=FLY-IT Simulators + +acpi:FIT*: + ID_VENDOR_FROM_DATABASE=Feature Integration Technology Inc. + +acpi:FJC*: + ID_VENDOR_FROM_DATABASE=Fujitsu Takamisawa Component Limited + +acpi:FJS*: + ID_VENDOR_FROM_DATABASE=Fujitsu Spain + +acpi:FJT*: + ID_VENDOR_FROM_DATABASE=F.J. Tieman BV + +acpi:FLE*: + ID_VENDOR_FROM_DATABASE=ADTI Media, Inc + +acpi:FLI*: + ID_VENDOR_FROM_DATABASE=Faroudja Laboratories + +acpi:FLY*: + ID_VENDOR_FROM_DATABASE=Butterfly Communications + +acpi:FMA*: + ID_VENDOR_FROM_DATABASE=Fast Multimedia AG + +acpi:FMC*: + ID_VENDOR_FROM_DATABASE=Ford Microelectronics Inc + +acpi:FMI*: + ID_VENDOR_FROM_DATABASE=Fellowes, Inc. + +acpi:FML*: + ID_VENDOR_FROM_DATABASE=Fujitsu Microelect Ltd + +acpi:FMZ*: + ID_VENDOR_FROM_DATABASE=Formoza-Altair + +acpi:FNC*: + ID_VENDOR_FROM_DATABASE=Fanuc LTD + +acpi:FNI*: + ID_VENDOR_FROM_DATABASE=Funai Electric Co., Ltd. + +acpi:FOA*: + ID_VENDOR_FROM_DATABASE=FOR-A Company Limited + +acpi:FOK*: + ID_VENDOR_FROM_DATABASE=Fokus Technologies GmbH + +acpi:FOS*: + ID_VENDOR_FROM_DATABASE=Foss Tecator + +acpi:FOV*: + ID_VENDOR_FROM_DATABASE=FOVE INC + +acpi:FOX*: + ID_VENDOR_FROM_DATABASE=HON HAI PRECISON IND.CO.,LTD. + +acpi:FPC*: + ID_VENDOR_FROM_DATABASE=Fingerprint Cards AB + +acpi:FPE*: + ID_VENDOR_FROM_DATABASE=Fujitsu Peripherals Ltd + +acpi:FPS*: + ID_VENDOR_FROM_DATABASE=Deltec Corporation + +acpi:FPX*: + ID_VENDOR_FROM_DATABASE=Cirel Systemes + +acpi:FRC*: + ID_VENDOR_FROM_DATABASE=Force Computers + +acpi:FRD*: + ID_VENDOR_FROM_DATABASE=Freedom Scientific BLV + +acpi:FRE*: + ID_VENDOR_FROM_DATABASE=Forvus Research Inc + +acpi:FRI*: + ID_VENDOR_FROM_DATABASE=Fibernet Research Inc + +acpi:FRO*: + ID_VENDOR_FROM_DATABASE=FARO Technologies + +acpi:FRS*: + ID_VENDOR_FROM_DATABASE=South Mountain Technologies, LTD + +acpi:FSC*: + ID_VENDOR_FROM_DATABASE=Future Systems Consulting KK + +acpi:FSI*: + ID_VENDOR_FROM_DATABASE=Fore Systems Inc + +acpi:FST*: + ID_VENDOR_FROM_DATABASE=Modesto PC Inc + +acpi:FTC*: + ID_VENDOR_FROM_DATABASE=Futuretouch Corporation + +acpi:FTE*: + ID_VENDOR_FROM_DATABASE=Frontline Test Equipment Inc. + +acpi:FTG*: + ID_VENDOR_FROM_DATABASE=FTG Data Systems + +acpi:FTI*: + ID_VENDOR_FROM_DATABASE=FastPoint Technologies, Inc. + +acpi:FTL*: + ID_VENDOR_FROM_DATABASE=FUJITSU TEN LIMITED + +acpi:FTN*: + ID_VENDOR_FROM_DATABASE=Fountain Technologies Inc + +acpi:FTR*: + ID_VENDOR_FROM_DATABASE=Mediasonic + +acpi:FTS*: + ID_VENDOR_FROM_DATABASE=FocalTech Systems Co., Ltd. + +acpi:FTW*: + ID_VENDOR_FROM_DATABASE=MindTribe Product Engineering, Inc. + +acpi:FUJ*: + ID_VENDOR_FROM_DATABASE=Fujitsu Ltd + +acpi:FUN*: + ID_VENDOR_FROM_DATABASE=sisel muhendislik + +acpi:FUS*: + ID_VENDOR_FROM_DATABASE=Fujitsu Siemens Computers GmbH + +acpi:FVC*: + ID_VENDOR_FROM_DATABASE=First Virtual Corporation + +acpi:FVX*: + ID_VENDOR_FROM_DATABASE=C-C-C Group Plc + +acpi:FWA*: + ID_VENDOR_FROM_DATABASE=Attero Tech, LLC + +acpi:FWR*: + ID_VENDOR_FROM_DATABASE=Flat Connections Inc + +acpi:FXX*: + ID_VENDOR_FROM_DATABASE=Fuji Xerox + +acpi:FZC*: + ID_VENDOR_FROM_DATABASE=Founder Group Shenzhen Co. + +acpi:FZI*: + ID_VENDOR_FROM_DATABASE=FZI Forschungszentrum Informatik + +acpi:GAC*: + ID_VENDOR_FROM_DATABASE=GreenArrays, Inc. + +acpi:GAG*: + ID_VENDOR_FROM_DATABASE=Gage Applied Sciences Inc + +acpi:GAL*: + ID_VENDOR_FROM_DATABASE=Galil Motion Control + +acpi:GAU*: + ID_VENDOR_FROM_DATABASE=Gaudi Co., Ltd. + +acpi:GCC*: + ID_VENDOR_FROM_DATABASE=GCC Technologies Inc + +acpi:GCI*: + ID_VENDOR_FROM_DATABASE=Gateway Comm. Inc + +acpi:GCS*: + ID_VENDOR_FROM_DATABASE=Grey Cell Systems Ltd + +acpi:GDC*: + ID_VENDOR_FROM_DATABASE=General Datacom + +acpi:GDI*: + ID_VENDOR_FROM_DATABASE=G. Diehl ISDN GmbH + +acpi:GDS*: + ID_VENDOR_FROM_DATABASE=GDS + +acpi:GDT*: + ID_VENDOR_FROM_DATABASE=Vortex Computersysteme GmbH + +acpi:GEC*: + ID_VENDOR_FROM_DATABASE=Gechic Corporation + +acpi:GED*: + ID_VENDOR_FROM_DATABASE=General Dynamics C4 Systems + +acpi:GEF*: + ID_VENDOR_FROM_DATABASE=GE Fanuc Embedded Systems + +acpi:GEH*: + ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc. + +acpi:GEM*: + ID_VENDOR_FROM_DATABASE=Gem Plus + +acpi:GEN*: + ID_VENDOR_FROM_DATABASE=Genesys ATE Inc + +acpi:GEO*: + ID_VENDOR_FROM_DATABASE=GEO Sense + +acpi:GER*: + ID_VENDOR_FROM_DATABASE=GERMANEERS GmbH + +acpi:GES*: + ID_VENDOR_FROM_DATABASE=GES Singapore Pte Ltd + +acpi:GET*: + ID_VENDOR_FROM_DATABASE=Getac Technology Corporation + +acpi:GFM*: + ID_VENDOR_FROM_DATABASE=GFMesstechnik GmbH + +acpi:GFN*: + ID_VENDOR_FROM_DATABASE=Gefen Inc. + +acpi:GGL*: + ID_VENDOR_FROM_DATABASE=Google Inc. + +acpi:GIC*: + ID_VENDOR_FROM_DATABASE=General Inst. Corporation + +acpi:GIM*: + ID_VENDOR_FROM_DATABASE=Guillemont International + +acpi:GIP*: + ID_VENDOR_FROM_DATABASE=GI Provision Ltd + +acpi:GIS*: + ID_VENDOR_FROM_DATABASE=AT&T Global Info Solutions + +acpi:GJN*: + ID_VENDOR_FROM_DATABASE=Grand Junction Networks + +acpi:GLD*: + ID_VENDOR_FROM_DATABASE=Goldmund - Digital Audio SA + +acpi:GLE*: + ID_VENDOR_FROM_DATABASE=AD electronics + +acpi:GLM*: + ID_VENDOR_FROM_DATABASE=Genesys Logic + +acpi:GLS*: + ID_VENDOR_FROM_DATABASE=Gadget Labs LLC + +acpi:GMK*: + ID_VENDOR_FROM_DATABASE=GMK Electronic Design GmbH + +acpi:GML*: + ID_VENDOR_FROM_DATABASE=General Information Systems + +acpi:GMM*: + ID_VENDOR_FROM_DATABASE=GMM Research Inc + +acpi:GMN*: + ID_VENDOR_FROM_DATABASE=GEMINI 2000 Ltd + +acpi:GMX*: + ID_VENDOR_FROM_DATABASE=GMX Inc + +acpi:GND*: + ID_VENDOR_FROM_DATABASE=Gennum Corporation + +acpi:GNN*: + ID_VENDOR_FROM_DATABASE=GN Nettest Inc + +acpi:GNZ*: + ID_VENDOR_FROM_DATABASE=Gunze Ltd + +acpi:GOE*: + ID_VENDOR_FROM_DATABASE=GOEPEL electronic GmbH + +acpi:GPR*: + ID_VENDOR_FROM_DATABASE=GoPro, Inc. + +acpi:GRA*: + ID_VENDOR_FROM_DATABASE=Graphica Computer + +acpi:GRE*: + ID_VENDOR_FROM_DATABASE=GOLD RAIN ENTERPRISES CORP. + +acpi:GRH*: + ID_VENDOR_FROM_DATABASE=Granch Ltd + +acpi:GRM*: + ID_VENDOR_FROM_DATABASE=Garmin International + +acpi:GRV*: + ID_VENDOR_FROM_DATABASE=Advanced Gravis + +acpi:GRY*: + ID_VENDOR_FROM_DATABASE=Robert Gray Company + +acpi:GSB*: + ID_VENDOR_FROM_DATABASE=NIPPONDENCHI CO,.LTD + +acpi:GSC*: + ID_VENDOR_FROM_DATABASE=General Standards Corporation + +acpi:GSM*: + ID_VENDOR_FROM_DATABASE=Goldstar Company Ltd + +acpi:GSN*: + ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc. + +acpi:GST*: + ID_VENDOR_FROM_DATABASE=Graphic SystemTechnology + +acpi:GSY*: + ID_VENDOR_FROM_DATABASE=Grossenbacher Systeme AG + +acpi:GTC*: + ID_VENDOR_FROM_DATABASE=Graphtec Corporation + +acpi:GTI*: + ID_VENDOR_FROM_DATABASE=Goldtouch + +acpi:GTK*: + ID_VENDOR_FROM_DATABASE=G-Tech Corporation + +acpi:GTM*: + ID_VENDOR_FROM_DATABASE=Garnet System Company Ltd + +acpi:GTS*: + ID_VENDOR_FROM_DATABASE=Geotest Marvin Test Systems Inc + +acpi:GTT*: + ID_VENDOR_FROM_DATABASE=General Touch Technology Co., Ltd. + +acpi:GUD*: + ID_VENDOR_FROM_DATABASE=Guntermann & Drunck GmbH + +acpi:GUZ*: + ID_VENDOR_FROM_DATABASE=Guzik Technical Enterprises + +acpi:GVC*: + ID_VENDOR_FROM_DATABASE=GVC Corporation + +acpi:GVL*: + ID_VENDOR_FROM_DATABASE=Global Village Communication + +acpi:GWI*: + ID_VENDOR_FROM_DATABASE=GW Instruments + +acpi:GWK*: + ID_VENDOR_FROM_DATABASE=Gateworks Corporation + +acpi:GWY*: + ID_VENDOR_FROM_DATABASE=Gateway 2000 + +acpi:GZE*: + ID_VENDOR_FROM_DATABASE=GUNZE Limited + +acpi:HAE*: + ID_VENDOR_FROM_DATABASE=Haider electronics + +acpi:HAI*: + ID_VENDOR_FROM_DATABASE=Haivision Systems Inc. + +acpi:HAL*: + ID_VENDOR_FROM_DATABASE=Halberthal + +acpi:HAN*: + ID_VENDOR_FROM_DATABASE=Hanchang System Corporation + +acpi:HAR*: + ID_VENDOR_FROM_DATABASE=Harris Corporation + +acpi:HAY*: + ID_VENDOR_FROM_DATABASE=Hayes Microcomputer Products Inc + +acpi:HCA*: + ID_VENDOR_FROM_DATABASE=DAT + +acpi:HCE*: + ID_VENDOR_FROM_DATABASE=Hitachi Consumer Electronics Co., Ltd + +acpi:HCL*: + ID_VENDOR_FROM_DATABASE=HCL America Inc + +acpi:HCM*: + ID_VENDOR_FROM_DATABASE=HCL Peripherals + +acpi:HCP*: + ID_VENDOR_FROM_DATABASE=Hitachi Computer Products Inc + +acpi:HCW*: + ID_VENDOR_FROM_DATABASE=Hauppauge Computer Works Inc + +acpi:HDC*: + ID_VENDOR_FROM_DATABASE=HardCom Elektronik & Datateknik + +acpi:HDI*: + ID_VENDOR_FROM_DATABASE=HD-INFO d.o.o. + +acpi:HDV*: + ID_VENDOR_FROM_DATABASE=Holografika kft. + +acpi:HEC*: + ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd. + +acpi:HEI*: + ID_VENDOR_FROM_DATABASE=Hyundai + +acpi:HEL*: + ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd + +acpi:HER*: + ID_VENDOR_FROM_DATABASE=Ascom Business Systems + +acpi:HET*: + ID_VENDOR_FROM_DATABASE=HETEC Datensysteme GmbH + +acpi:HHC*: + ID_VENDOR_FROM_DATABASE=HIRAKAWA HEWTECH CORP. + +acpi:HHI*: + ID_VENDOR_FROM_DATABASE=Fraunhofer Heinrich-Hertz-Institute + +acpi:HIB*: + ID_VENDOR_FROM_DATABASE=Hibino Corporation + +acpi:HIC*: + ID_VENDOR_FROM_DATABASE=Hitachi Information Technology Co., Ltd. + +acpi:HII*: + ID_VENDOR_FROM_DATABASE=Harman International Industries, Inc + +acpi:HIK*: + ID_VENDOR_FROM_DATABASE=Hikom Co., Ltd. + +acpi:HIL*: + ID_VENDOR_FROM_DATABASE=Hilevel Technology + +acpi:HIQ*: + ID_VENDOR_FROM_DATABASE=Kaohsiung Opto Electronics Americas, Inc. + +acpi:HIS*: + ID_VENDOR_FROM_DATABASE=Hope Industrial Systems, Inc. + +acpi:HIT*: + ID_VENDOR_FROM_DATABASE=Hitachi America Ltd + +acpi:HJI*: + ID_VENDOR_FROM_DATABASE=Harris & Jeffries Inc + +acpi:HKA*: + ID_VENDOR_FROM_DATABASE=HONKO MFG. CO., LTD. + +acpi:HKC*: + ID_VENDOR_FROM_DATABASE=HKC OVERSEAS LIMITED + +acpi:HKG*: + ID_VENDOR_FROM_DATABASE=Josef Heim KG + +acpi:HLG*: + ID_VENDOR_FROM_DATABASE=China Hualu Group Co., Ltd. + +acpi:HMC*: + ID_VENDOR_FROM_DATABASE=Hualon Microelectric Corporation + +acpi:HMK*: + ID_VENDOR_FROM_DATABASE=hmk Daten-System-Technik BmbH + +acpi:HMX*: + ID_VENDOR_FROM_DATABASE=HUMAX Co., Ltd. + +acpi:HNS*: + ID_VENDOR_FROM_DATABASE=Hughes Network Systems + +acpi:HOB*: + ID_VENDOR_FROM_DATABASE=HOB Electronic GmbH + +acpi:HOE*: + ID_VENDOR_FROM_DATABASE=Hosiden Corporation + +acpi:HOL*: + ID_VENDOR_FROM_DATABASE=Holoeye Photonics AG + +acpi:HON*: + ID_VENDOR_FROM_DATABASE=Sonitronix + +acpi:HPA*: + ID_VENDOR_FROM_DATABASE=Zytor Communications + +acpi:HPC*: + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Co. + +acpi:HPD*: + ID_VENDOR_FROM_DATABASE=Hewlett Packard + +acpi:HPE*: + ID_VENDOR_FROM_DATABASE=Hewlett Packard Enterprise + +acpi:HPI*: + ID_VENDOR_FROM_DATABASE=Headplay, Inc. + +acpi:HPK*: + ID_VENDOR_FROM_DATABASE=HAMAMATSU PHOTONICS K.K. + +acpi:HPN*: + ID_VENDOR_FROM_DATABASE=HP Inc. + +acpi:HPQ*: + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Co. + +acpi:HPR*: + ID_VENDOR_FROM_DATABASE=H.P.R. Electronics GmbH + +acpi:HRC*: + ID_VENDOR_FROM_DATABASE=Hercules + +acpi:HRE*: + ID_VENDOR_FROM_DATABASE=Qingdao Haier Electronics Co., Ltd. + +acpi:HRI*: + ID_VENDOR_FROM_DATABASE=Hall Research + +acpi:HRL*: + ID_VENDOR_FROM_DATABASE=Herolab GmbH + +acpi:HRS*: + ID_VENDOR_FROM_DATABASE=Harris Semiconductor + +acpi:HRT*: + ID_VENDOR_FROM_DATABASE=HERCULES + +acpi:HSC*: + ID_VENDOR_FROM_DATABASE=Hagiwara Sys-Com Company Ltd + +acpi:HSD*: + ID_VENDOR_FROM_DATABASE=HannStar Display Corp + +acpi:HSL*: + ID_VENDOR_FROM_DATABASE=Hansol + +acpi:HSM*: + ID_VENDOR_FROM_DATABASE=AT&T Microelectronics + +acpi:HSP*: + ID_VENDOR_FROM_DATABASE=HannStar Display Corp + +acpi:HST*: + ID_VENDOR_FROM_DATABASE=Horsent Technology Co., Ltd. + +acpi:HTC*: + ID_VENDOR_FROM_DATABASE=Hitachi Ltd + +acpi:HTI*: + ID_VENDOR_FROM_DATABASE=Hampshire Company, Inc. + +acpi:HTK*: + ID_VENDOR_FROM_DATABASE=Holtek Microelectronics Inc + +acpi:HTL*: + ID_VENDOR_FROM_DATABASE=HTBLuVA Mödling + +acpi:HTR*: + ID_VENDOR_FROM_DATABASE=Shenzhen ZhuoYi HengTong Computer Technology Limited + +acpi:HTX*: + ID_VENDOR_FROM_DATABASE=Hitex Systementwicklung GmbH + +acpi:HUB*: + ID_VENDOR_FROM_DATABASE=GAI-Tronics, A Hubbell Company + +acpi:HUK*: + ID_VENDOR_FROM_DATABASE=Hoffmann + Krippner GmbH + +acpi:HUM*: + ID_VENDOR_FROM_DATABASE=IMP Electronics Ltd. + +acpi:HVR*: + ID_VENDOR_FROM_DATABASE=HTC Corportation + +acpi:HWA*: + ID_VENDOR_FROM_DATABASE=Harris Canada Inc + +acpi:HWC*: + ID_VENDOR_FROM_DATABASE=DBA Hans Wedemeyer + +acpi:HWD*: + ID_VENDOR_FROM_DATABASE=Highwater Designs Ltd + +acpi:HWP*: + ID_VENDOR_FROM_DATABASE=Hewlett Packard + +acpi:HXM*: + ID_VENDOR_FROM_DATABASE=Hexium Ltd. + +acpi:HYC*: + ID_VENDOR_FROM_DATABASE=Hypercope Gmbh Aachen + +acpi:HYD*: + ID_VENDOR_FROM_DATABASE=Hydis Technologies.Co.,LTD + +acpi:HYO*: + ID_VENDOR_FROM_DATABASE=HYC CO., LTD. + +acpi:HYP*: + ID_VENDOR_FROM_DATABASE=Hyphen Ltd + +acpi:HYR*: + ID_VENDOR_FROM_DATABASE=Hypertec Pty Ltd + +acpi:HYT*: + ID_VENDOR_FROM_DATABASE=Heng Yu Technology (HK) Limited + +acpi:HYV*: + ID_VENDOR_FROM_DATABASE=Hynix Semiconductor + +acpi:IAD*: + ID_VENDOR_FROM_DATABASE=IAdea Corporation + +acpi:IAF*: + ID_VENDOR_FROM_DATABASE=Institut f r angewandte Funksystemtechnik GmbH + +acpi:IAI*: + ID_VENDOR_FROM_DATABASE=Integration Associates, Inc. + +acpi:IAT*: + ID_VENDOR_FROM_DATABASE=IAT Germany GmbH + +acpi:IBC*: + ID_VENDOR_FROM_DATABASE=Integrated Business Systems + +acpi:IBI*: + ID_VENDOR_FROM_DATABASE=INBINE.CO.LTD + +acpi:IBM*: + ID_VENDOR_FROM_DATABASE=IBM Brasil + +acpi:IBP*: + ID_VENDOR_FROM_DATABASE=IBP Instruments GmbH + +acpi:IBR*: + ID_VENDOR_FROM_DATABASE=IBR GmbH + +acpi:ICA*: + ID_VENDOR_FROM_DATABASE=ICA Inc + +acpi:ICC*: + ID_VENDOR_FROM_DATABASE=BICC Data Networks Ltd + +acpi:ICD*: + ID_VENDOR_FROM_DATABASE=ICD Inc + +acpi:ICE*: + ID_VENDOR_FROM_DATABASE=IC Ensemble + +acpi:ICI*: + ID_VENDOR_FROM_DATABASE=Infotek Communication Inc + +acpi:ICL*: + ID_VENDOR_FROM_DATABASE=Fujitsu ICL + +acpi:ICM*: + ID_VENDOR_FROM_DATABASE=Intracom SA + +acpi:ICN*: + ID_VENDOR_FROM_DATABASE=Sanyo Icon + +acpi:ICO*: + ID_VENDOR_FROM_DATABASE=Intel Corp + +acpi:ICP*: + ID_VENDOR_FROM_DATABASE=ICP Electronics, Inc./iEi Technology Corp. + +acpi:ICS*: + ID_VENDOR_FROM_DATABASE=Integrated Circuit Systems + +acpi:ICV*: + ID_VENDOR_FROM_DATABASE=Inside Contactless + +acpi:ICX*: + ID_VENDOR_FROM_DATABASE=ICCC A/S + +acpi:IDC*: + ID_VENDOR_FROM_DATABASE=International Datacasting Corporation + +acpi:IDE*: + ID_VENDOR_FROM_DATABASE=IDE Associates + +acpi:IDK*: + ID_VENDOR_FROM_DATABASE=IDK Corporation + +acpi:IDN*: + ID_VENDOR_FROM_DATABASE=Idneo Technologies + +acpi:IDO*: + ID_VENDOR_FROM_DATABASE=IDEO Product Development + +acpi:IDP*: + ID_VENDOR_FROM_DATABASE=Integrated Device Technology, Inc. + +acpi:IDS*: + ID_VENDOR_FROM_DATABASE=Interdigital Sistemas de Informacao + +acpi:IDT*: + ID_VENDOR_FROM_DATABASE=International Display Technology + +acpi:IDX*: + ID_VENDOR_FROM_DATABASE=IDEXX Labs + +acpi:IEC*: + ID_VENDOR_FROM_DATABASE=Interlace Engineering Corporation + +acpi:IEE*: + ID_VENDOR_FROM_DATABASE=IEE + +acpi:IEI*: + ID_VENDOR_FROM_DATABASE=Interlink Electronics + +acpi:IFS*: + ID_VENDOR_FROM_DATABASE=In Focus Systems Inc + +acpi:IFT*: + ID_VENDOR_FROM_DATABASE=Informtech + +acpi:IFX*: + ID_VENDOR_FROM_DATABASE=Infineon Technologies AG + +acpi:IFZ*: + ID_VENDOR_FROM_DATABASE=Infinite Z + +acpi:IGC*: + ID_VENDOR_FROM_DATABASE=Intergate Pty Ltd + +acpi:IGM*: + ID_VENDOR_FROM_DATABASE=IGM Communi + +acpi:IHE*: + ID_VENDOR_FROM_DATABASE=InHand Electronics + +acpi:IIC*: + ID_VENDOR_FROM_DATABASE=ISIC Innoscan Industrial Computers A/S + +acpi:III*: + ID_VENDOR_FROM_DATABASE=Intelligent Instrumentation + +acpi:IIN*: + ID_VENDOR_FROM_DATABASE=IINFRA Co., Ltd + +acpi:IIT*: + ID_VENDOR_FROM_DATABASE=Informatik Information Technologies + +acpi:IKE*: + ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd. + +acpi:IKN*: + ID_VENDOR_FROM_DATABASE=IKON + +acpi:IKS*: + ID_VENDOR_FROM_DATABASE=Ikos Systems Inc + +acpi:ILC*: + ID_VENDOR_FROM_DATABASE=Image Logic Corporation + +acpi:ILS*: + ID_VENDOR_FROM_DATABASE=Innotech Corporation + +acpi:IMA*: + ID_VENDOR_FROM_DATABASE=Imagraph + +acpi:IMB*: + ID_VENDOR_FROM_DATABASE=ART s.r.l. + +acpi:IMC*: + ID_VENDOR_FROM_DATABASE=IMC Networks + +acpi:IMD*: + ID_VENDOR_FROM_DATABASE=ImasDe Canarias S.A. + +acpi:IME*: + ID_VENDOR_FROM_DATABASE=Imagraph + +acpi:IMG*: + ID_VENDOR_FROM_DATABASE=IMAGENICS Co., Ltd. + +acpi:IMI*: + ID_VENDOR_FROM_DATABASE=International Microsystems Inc + +acpi:IMM*: + ID_VENDOR_FROM_DATABASE=Immersion Corporation + +acpi:IMN*: + ID_VENDOR_FROM_DATABASE=Impossible Production + +acpi:IMP*: + ID_VENDOR_FROM_DATABASE=Impinj + +acpi:IMT*: + ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation + +acpi:IMS*: + ID_VENDOR_FROM_DATABASE=Integrated Micro Solution Inc. + +acpi:INA*: + ID_VENDOR_FROM_DATABASE=Inventec Corporation + +acpi:INC*: + ID_VENDOR_FROM_DATABASE=Home Row Inc + +acpi:IND*: + ID_VENDOR_FROM_DATABASE=ILC + +acpi:INE*: + ID_VENDOR_FROM_DATABASE=Inventec Electronics (M) Sdn. Bhd. + +acpi:INF*: + ID_VENDOR_FROM_DATABASE=Inframetrics Inc + +acpi:ING*: + ID_VENDOR_FROM_DATABASE=Integraph Corporation + +acpi:INI*: + ID_VENDOR_FROM_DATABASE=Initio Corporation + +acpi:INK*: + ID_VENDOR_FROM_DATABASE=Indtek Co., Ltd. + +acpi:INL*: + ID_VENDOR_FROM_DATABASE=InnoLux Display Corporation + +acpi:INM*: + ID_VENDOR_FROM_DATABASE=InnoMedia Inc + +acpi:INN*: + ID_VENDOR_FROM_DATABASE=Innovent Systems, Inc. + +acpi:INO*: + ID_VENDOR_FROM_DATABASE=Innolab Pte Ltd + +acpi:INP*: + ID_VENDOR_FROM_DATABASE=Interphase Corporation + +acpi:INS*: + ID_VENDOR_FROM_DATABASE=Ines GmbH + +acpi:INT*: + ID_VENDOR_FROM_DATABASE=Interphase Corporation + +acpi:INV*: + ID_VENDOR_FROM_DATABASE=Inviso, Inc. + +acpi:INX*: + ID_VENDOR_FROM_DATABASE=Communications Supply Corporation (A division of WESCO) + +acpi:INZ*: + ID_VENDOR_FROM_DATABASE=Best Buy + +acpi:IOA*: + ID_VENDOR_FROM_DATABASE=CRE Technology Corporation + +acpi:IOD*: + ID_VENDOR_FROM_DATABASE=I-O Data Device Inc + +acpi:IOM*: + ID_VENDOR_FROM_DATABASE=Iomega + +acpi:ION*: + ID_VENDOR_FROM_DATABASE=Inside Out Networks + +acpi:IOS*: + ID_VENDOR_FROM_DATABASE=i-O Display System + +acpi:IOT*: + ID_VENDOR_FROM_DATABASE=I/OTech Inc + +acpi:IPC*: + ID_VENDOR_FROM_DATABASE=IPC Corporation + +acpi:IPD*: + ID_VENDOR_FROM_DATABASE=Industrial Products Design, Inc. + +acpi:IPI*: + ID_VENDOR_FROM_DATABASE=Intelligent Platform Management Interface (IPMI) forum (Intel, HP, NEC, Dell) + +acpi:IPM*: + ID_VENDOR_FROM_DATABASE=IPM Industria Politecnica Meridionale SpA + +acpi:IPN*: + ID_VENDOR_FROM_DATABASE=Performance Technologies + +acpi:IPP*: + ID_VENDOR_FROM_DATABASE=IP Power Technologies GmbH + +acpi:IPQ*: + ID_VENDOR_FROM_DATABASE=IP3 Technology Ltd. + +acpi:IPR*: + ID_VENDOR_FROM_DATABASE=Ithaca Peripherals + +acpi:IPS*: + ID_VENDOR_FROM_DATABASE=IPS, Inc. (Intellectual Property Solutions, Inc.) + +acpi:IPT*: + ID_VENDOR_FROM_DATABASE=International Power Technologies + +acpi:IPW*: + ID_VENDOR_FROM_DATABASE=IPWireless, Inc + +acpi:IQI*: + ID_VENDOR_FROM_DATABASE=IneoQuest Technologies, Inc + +acpi:IQT*: + ID_VENDOR_FROM_DATABASE=IMAGEQUEST Co., Ltd + +acpi:IRD*: + ID_VENDOR_FROM_DATABASE=Irdata + +acpi:ISA*: + ID_VENDOR_FROM_DATABASE=Symbol Technologies + +acpi:ISC*: + ID_VENDOR_FROM_DATABASE=Id3 Semiconductors + +acpi:ISG*: + ID_VENDOR_FROM_DATABASE=Insignia Solutions Inc + +acpi:ISI*: + ID_VENDOR_FROM_DATABASE=Interface Solutions + +acpi:ISL*: + ID_VENDOR_FROM_DATABASE=Isolation Systems + +acpi:ISM*: + ID_VENDOR_FROM_DATABASE=Image Stream Medical + +acpi:ISP*: + ID_VENDOR_FROM_DATABASE=IntreSource Systems Pte Ltd + +acpi:ISR*: + ID_VENDOR_FROM_DATABASE=INSIS Co., LTD. + +acpi:ISS*: + ID_VENDOR_FROM_DATABASE=ISS Inc + +acpi:IST*: + ID_VENDOR_FROM_DATABASE=Intersolve Technologies + +acpi:ISY*: + ID_VENDOR_FROM_DATABASE=International Integrated Systems,Inc.(IISI) + +acpi:ITA*: + ID_VENDOR_FROM_DATABASE=Itausa Export North America + +acpi:ITC*: + ID_VENDOR_FROM_DATABASE=Intercom Inc + +acpi:ITD*: + ID_VENDOR_FROM_DATABASE=Internet Technology Corporation + +acpi:ITE*: + ID_VENDOR_FROM_DATABASE=Integrated Tech Express Inc + +acpi:ITI*: + ID_VENDOR_FROM_DATABASE=VanErum Group + +acpi:ITK*: + ID_VENDOR_FROM_DATABASE=ITK Telekommunikation AG + +acpi:ITL*: + ID_VENDOR_FROM_DATABASE=Inter-Tel + +acpi:ITM*: + ID_VENDOR_FROM_DATABASE=ITM inc. + +acpi:ITN*: + ID_VENDOR_FROM_DATABASE=The NTI Group + +acpi:ITP*: + ID_VENDOR_FROM_DATABASE=IT-PRO Consulting und Systemhaus GmbH + +acpi:ITR*: + ID_VENDOR_FROM_DATABASE=Infotronic America, Inc. + +acpi:ITS*: + ID_VENDOR_FROM_DATABASE=IDTECH + +acpi:ITT*: + ID_VENDOR_FROM_DATABASE=I&T Telecom. + +acpi:ITX*: + ID_VENDOR_FROM_DATABASE=integrated Technology Express Inc + +acpi:IUC*: + ID_VENDOR_FROM_DATABASE=ICSL + +acpi:IVI*: + ID_VENDOR_FROM_DATABASE=Intervoice Inc + +acpi:IVM*: + ID_VENDOR_FROM_DATABASE=Iiyama North America + +acpi:IVS*: + ID_VENDOR_FROM_DATABASE=Intevac Photonics Inc. + +acpi:IWR*: + ID_VENDOR_FROM_DATABASE=Icuiti Corporation + +acpi:IWX*: + ID_VENDOR_FROM_DATABASE=Intelliworxx, Inc. + +acpi:IXD*: + ID_VENDOR_FROM_DATABASE=Intertex Data AB + +acpi:IXN*: + ID_VENDOR_FROM_DATABASE=Shenzhen Inet Mobile Internet Technology Co., LTD + +acpi:JAC*: + ID_VENDOR_FROM_DATABASE=Astec Inc + +acpi:JAE*: + ID_VENDOR_FROM_DATABASE=Japan Aviation Electronics Industry, Limited + +acpi:JAS*: + ID_VENDOR_FROM_DATABASE=Janz Automationssysteme AG + +acpi:JAT*: + ID_VENDOR_FROM_DATABASE=Jaton Corporation + +acpi:JAZ*: + ID_VENDOR_FROM_DATABASE=Carrera Computer Inc + +acpi:JCE*: + ID_VENDOR_FROM_DATABASE=Jace Tech Inc + +acpi:JDI*: + ID_VENDOR_FROM_DATABASE=Japan Display Inc. + +acpi:JDL*: + ID_VENDOR_FROM_DATABASE=Japan Digital Laboratory Co.,Ltd. + +acpi:JEN*: + ID_VENDOR_FROM_DATABASE=N-Vision + +acpi:JET*: + ID_VENDOR_FROM_DATABASE=JET POWER TECHNOLOGY CO., LTD. + +acpi:JFX*: + ID_VENDOR_FROM_DATABASE=Jones Futurex Inc + +acpi:JGD*: + ID_VENDOR_FROM_DATABASE=University College + +acpi:JIC*: + ID_VENDOR_FROM_DATABASE=Jaeik Information & Communication Co., Ltd. + +acpi:JKC*: + ID_VENDOR_FROM_DATABASE=JVC KENWOOD Corporation + +acpi:JMT*: + ID_VENDOR_FROM_DATABASE=Micro Technical Company Ltd + +acpi:JPC*: + ID_VENDOR_FROM_DATABASE=JPC Technology Limited + +acpi:JPW*: + ID_VENDOR_FROM_DATABASE=Wallis Hamilton Industries + +acpi:JQE*: + ID_VENDOR_FROM_DATABASE=CNet Technical Inc + +acpi:JSD*: + ID_VENDOR_FROM_DATABASE=JS DigiTech, Inc + +acpi:JSI*: + ID_VENDOR_FROM_DATABASE=Jupiter Systems, Inc. + +acpi:JSK*: + ID_VENDOR_FROM_DATABASE=SANKEN ELECTRIC CO., LTD + +acpi:JTS*: + ID_VENDOR_FROM_DATABASE=JS Motorsports + +acpi:JTY*: + ID_VENDOR_FROM_DATABASE=jetway security micro,inc + +acpi:JUK*: + ID_VENDOR_FROM_DATABASE=Janich & Klass Computertechnik GmbH + +acpi:JUP*: + ID_VENDOR_FROM_DATABASE=Jupiter Systems + +acpi:JVC*: + ID_VENDOR_FROM_DATABASE=JVC + +acpi:JWD*: + ID_VENDOR_FROM_DATABASE=Video International Inc. + +acpi:JWL*: + ID_VENDOR_FROM_DATABASE=Jewell Instruments, LLC + +acpi:JWS*: + ID_VENDOR_FROM_DATABASE=JWSpencer & Co. + +acpi:JWY*: + ID_VENDOR_FROM_DATABASE=Jetway Information Co., Ltd + +acpi:KAR*: + ID_VENDOR_FROM_DATABASE=Karna + +acpi:KBI*: + ID_VENDOR_FROM_DATABASE=Kidboard Inc + +acpi:KBL*: + ID_VENDOR_FROM_DATABASE=Kobil Systems GmbH + +acpi:KCD*: + ID_VENDOR_FROM_DATABASE=Chunichi Denshi Co.,LTD. + +acpi:KCL*: + ID_VENDOR_FROM_DATABASE=Keycorp Ltd + +acpi:KDE*: + ID_VENDOR_FROM_DATABASE=KDE + +acpi:KDK*: + ID_VENDOR_FROM_DATABASE=Kodiak Tech + +acpi:KDM*: + ID_VENDOR_FROM_DATABASE=Korea Data Systems Co., Ltd. + +acpi:KDS*: + ID_VENDOR_FROM_DATABASE=KDS USA + +acpi:KDT*: + ID_VENDOR_FROM_DATABASE=KDDI Technology Corporation + +acpi:KEC*: + ID_VENDOR_FROM_DATABASE=Kyushu Electronics Systems Inc + +acpi:KEM*: + ID_VENDOR_FROM_DATABASE=Kontron Embedded Modules GmbH + +acpi:KES*: + ID_VENDOR_FROM_DATABASE=Kesa Corporation + +acpi:KEU*: + ID_VENDOR_FROM_DATABASE=Kontron Europe GmbH + +acpi:KEY*: + ID_VENDOR_FROM_DATABASE=Key Tech Inc + +acpi:KFC*: + ID_VENDOR_FROM_DATABASE=SCD Tech + +acpi:KFE*: + ID_VENDOR_FROM_DATABASE=Komatsu Forest + +acpi:KFX*: + ID_VENDOR_FROM_DATABASE=Kofax Image Products + +acpi:KGI*: + ID_VENDOR_FROM_DATABASE=Klipsch Group, Inc + +acpi:KGL*: + ID_VENDOR_FROM_DATABASE=KEISOKU GIKEN Co.,Ltd. + +acpi:KIO*: + ID_VENDOR_FROM_DATABASE=Kionix, Inc. + +acpi:KIS*: + ID_VENDOR_FROM_DATABASE=KiSS Technology A/S + +acpi:KMC*: + ID_VENDOR_FROM_DATABASE=Mitsumi Company Ltd + +acpi:KME*: + ID_VENDOR_FROM_DATABASE=KIMIN Electronics Co., Ltd. + +acpi:KML*: + ID_VENDOR_FROM_DATABASE=Kensington Microware Ltd + +acpi:KMR*: + ID_VENDOR_FROM_DATABASE=Kramer Electronics Ltd. International + +acpi:KNC*: + ID_VENDOR_FROM_DATABASE=Konica corporation + +acpi:KNX*: + ID_VENDOR_FROM_DATABASE=Nutech Marketing PTL + +acpi:KOB*: + ID_VENDOR_FROM_DATABASE=Kobil Systems GmbH + +acpi:KOD*: + ID_VENDOR_FROM_DATABASE=Eastman Kodak Company + +acpi:KOE*: + ID_VENDOR_FROM_DATABASE=KOLTER ELECTRONIC + +acpi:KOL*: + ID_VENDOR_FROM_DATABASE=Kollmorgen Motion Technologies Group + +acpi:KOU*: + ID_VENDOR_FROM_DATABASE=KOUZIRO Co.,Ltd. + +acpi:KOW*: + ID_VENDOR_FROM_DATABASE=KOWA Company,LTD. + +acpi:KPC*: + ID_VENDOR_FROM_DATABASE=King Phoenix Company + +acpi:KRL*: + ID_VENDOR_FROM_DATABASE=Krell Industries Inc. + +acpi:KRM*: + ID_VENDOR_FROM_DATABASE=Kroma Telecom + +acpi:KRY*: + ID_VENDOR_FROM_DATABASE=Kroy LLC + +acpi:KSC*: + ID_VENDOR_FROM_DATABASE=Kinetic Systems Corporation + +acpi:KSG*: + ID_VENDOR_FROM_DATABASE=KUPA China Shenzhen Micro Technology Co., Ltd. Gold Institute + +acpi:KSL*: + ID_VENDOR_FROM_DATABASE=Karn Solutions Ltd. + +acpi:KSX*: + ID_VENDOR_FROM_DATABASE=King Tester Corporation + +acpi:KTC*: + ID_VENDOR_FROM_DATABASE=Kingston Tech Corporation + +acpi:KTD*: + ID_VENDOR_FROM_DATABASE=Takahata Electronics Co.,Ltd. + +acpi:KTE*: + ID_VENDOR_FROM_DATABASE=K-Tech + +acpi:KTG*: + ID_VENDOR_FROM_DATABASE=Kayser-Threde GmbH + +acpi:KTI*: + ID_VENDOR_FROM_DATABASE=Konica Technical Inc + +acpi:KTK*: + ID_VENDOR_FROM_DATABASE=Key Tronic Corporation + +acpi:KTN*: + ID_VENDOR_FROM_DATABASE=Katron Tech Inc + +acpi:KUR*: + ID_VENDOR_FROM_DATABASE=Kurta Corporation + +acpi:KVA*: + ID_VENDOR_FROM_DATABASE=Kvaser AB + +acpi:KVX*: + ID_VENDOR_FROM_DATABASE=KeyView + +acpi:KWD*: + ID_VENDOR_FROM_DATABASE=Kenwood Corporation + +acpi:KYC*: + ID_VENDOR_FROM_DATABASE=Kyocera Corporation + +acpi:KYE*: + ID_VENDOR_FROM_DATABASE=KYE Syst Corporation + +acpi:KYK*: + ID_VENDOR_FROM_DATABASE=Samsung Electronics America Inc + +acpi:KYN*: + ID_VENDOR_FROM_DATABASE=KEYENCE CORPORATION + +acpi:KZI*: + ID_VENDOR_FROM_DATABASE=K-Zone International co. Ltd. + +acpi:KZN*: + ID_VENDOR_FROM_DATABASE=K-Zone International + +acpi:LAB*: + ID_VENDOR_FROM_DATABASE=ACT Labs Ltd + +acpi:LAC*: + ID_VENDOR_FROM_DATABASE=LaCie + +acpi:LAF*: + ID_VENDOR_FROM_DATABASE=Microline + +acpi:LAG*: + ID_VENDOR_FROM_DATABASE=Laguna Systems + +acpi:LAN*: + ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc + +acpi:LAP*: + ID_VENDOR_FROM_DATABASE=BenQ + +acpi:LAS*: + ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S + +acpi:LAV*: + ID_VENDOR_FROM_DATABASE=Lava Computer MFG Inc + +acpi:LBO*: + ID_VENDOR_FROM_DATABASE=Lubosoft + +acpi:LCC*: + ID_VENDOR_FROM_DATABASE=LCI + +acpi:LCD*: + ID_VENDOR_FROM_DATABASE=Toshiba Matsushita Display Technology Co., Ltd + +acpi:LCE*: + ID_VENDOR_FROM_DATABASE=La Commande Electronique + +acpi:LCI*: + ID_VENDOR_FROM_DATABASE=Lite-On Communication Inc + +acpi:LCM*: + ID_VENDOR_FROM_DATABASE=Latitude Comm. + +acpi:LCN*: + ID_VENDOR_FROM_DATABASE=LEXICON + +acpi:LCS*: + ID_VENDOR_FROM_DATABASE=Longshine Electronics Company + +acpi:LCT*: + ID_VENDOR_FROM_DATABASE=Labcal Technologies + +acpi:LDN*: + ID_VENDOR_FROM_DATABASE=Laserdyne Technologies + +acpi:LDT*: + ID_VENDOR_FROM_DATABASE=LogiDataTech Electronic GmbH + +acpi:LEC*: + ID_VENDOR_FROM_DATABASE=Lectron Company Ltd + +acpi:LED*: + ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc + +acpi:LED*: + ID_VENDOR_FROM_DATABASE=LeafNet + +acpi:LEG*: + ID_VENDOR_FROM_DATABASE=Legerity, Inc + +acpi:LEN*: + ID_VENDOR_FROM_DATABASE=Lenovo Group Limited + +acpi:LEO*: + ID_VENDOR_FROM_DATABASE=First International Computer Inc + +acpi:LEX*: + ID_VENDOR_FROM_DATABASE=Lexical Ltd + +acpi:LGC*: + ID_VENDOR_FROM_DATABASE=Logic Ltd + +acpi:LGD*: + ID_VENDOR_FROM_DATABASE=LG Display + +acpi:LGI*: + ID_VENDOR_FROM_DATABASE=Logitech Inc + +acpi:LGS*: + ID_VENDOR_FROM_DATABASE=LG Semicom Company Ltd + +acpi:LGX*: + ID_VENDOR_FROM_DATABASE=Lasergraphics, Inc. + +acpi:LHA*: + ID_VENDOR_FROM_DATABASE=Lars Haagh ApS + +acpi:LHE*: + ID_VENDOR_FROM_DATABASE=Lung Hwa Electronics Company Ltd + +acpi:LHT*: + ID_VENDOR_FROM_DATABASE=Lighthouse Technologies Limited + +acpi:LIN*: + ID_VENDOR_FROM_DATABASE=Lenovo Beijing Co. Ltd. + +acpi:LIP*: + ID_VENDOR_FROM_DATABASE=Linked IP GmbH + +acpi:LIT*: + ID_VENDOR_FROM_DATABASE=Lithics Silicon Technology + +acpi:LJX*: + ID_VENDOR_FROM_DATABASE=Datalogic Corporation + +acpi:LKM*: + ID_VENDOR_FROM_DATABASE=Likom Technology Sdn. Bhd. + +acpi:LLL*: + ID_VENDOR_FROM_DATABASE=L-3 Communications + +acpi:LMG*: + ID_VENDOR_FROM_DATABASE=Lucent Technologies + +acpi:LMI*: + ID_VENDOR_FROM_DATABASE=Lexmark Int'l Inc + +acpi:LMP*: + ID_VENDOR_FROM_DATABASE=Leda Media Products + +acpi:LMT*: + ID_VENDOR_FROM_DATABASE=Laser Master + +acpi:LND*: + ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd + +acpi:LNE*: + ID_VENDOR_FROM_DATABASE=Linksys + +acpi:LNK*: + ID_VENDOR_FROM_DATABASE=Link Tech Inc + +acpi:LNR*: + ID_VENDOR_FROM_DATABASE=Linear Systems Ltd. + +acpi:LNT*: + ID_VENDOR_FROM_DATABASE=LANETCO International + +acpi:LNV*: + ID_VENDOR_FROM_DATABASE=Lenovo + +acpi:LNX*: + ID_VENDOR_FROM_DATABASE=The Linux Foundation + +acpi:LOC*: + ID_VENDOR_FROM_DATABASE=Locamation B.V. + +acpi:LOE*: + ID_VENDOR_FROM_DATABASE=Loewe Opta GmbH + +acpi:LOG*: + ID_VENDOR_FROM_DATABASE=Logicode Technology Inc + +acpi:LOL*: + ID_VENDOR_FROM_DATABASE=Litelogic Operations Ltd + +acpi:LPE*: + ID_VENDOR_FROM_DATABASE=El-PUSK Co., Ltd. + +acpi:LPI*: + ID_VENDOR_FROM_DATABASE=Design Technology + +acpi:LPL*: + ID_VENDOR_FROM_DATABASE=LG Philips + +acpi:LSC*: + ID_VENDOR_FROM_DATABASE=LifeSize Communications + +acpi:LSD*: + ID_VENDOR_FROM_DATABASE=Intersil Corporation + +acpi:LSI*: + ID_VENDOR_FROM_DATABASE=Loughborough Sound Images + +acpi:LSJ*: + ID_VENDOR_FROM_DATABASE=LSI Japan Company Ltd + +acpi:LSL*: + ID_VENDOR_FROM_DATABASE=Logical Solutions + +acpi:LSY*: + ID_VENDOR_FROM_DATABASE=LSI Systems Inc + +acpi:LTC*: + ID_VENDOR_FROM_DATABASE=Labtec Inc + +acpi:LTI*: + ID_VENDOR_FROM_DATABASE=Jongshine Tech Inc + +acpi:LTK*: + ID_VENDOR_FROM_DATABASE=Lucidity Technology Company Ltd + +acpi:LTN*: + ID_VENDOR_FROM_DATABASE=Litronic Inc + +acpi:LTS*: + ID_VENDOR_FROM_DATABASE=LTS Scale LLC + +acpi:LTV*: + ID_VENDOR_FROM_DATABASE=Leitch Technology International Inc. + +acpi:LTW*: + ID_VENDOR_FROM_DATABASE=Lightware, Inc + +acpi:LUC*: + ID_VENDOR_FROM_DATABASE=Lucent Technologies + +acpi:LUM*: + ID_VENDOR_FROM_DATABASE=Lumagen, Inc. + +acpi:LUX*: + ID_VENDOR_FROM_DATABASE=Luxxell Research Inc + +acpi:LVI*: + ID_VENDOR_FROM_DATABASE=LVI Low Vision International AB + +acpi:LWC*: + ID_VENDOR_FROM_DATABASE=Labway Corporation + +acpi:LWR*: + ID_VENDOR_FROM_DATABASE=Lightware Visual Engineering + +acpi:LWW*: + ID_VENDOR_FROM_DATABASE=Lanier Worldwide + +acpi:LXC*: + ID_VENDOR_FROM_DATABASE=LXCO Technologies AG + +acpi:LXN*: + ID_VENDOR_FROM_DATABASE=Luxeon + +acpi:LXS*: + ID_VENDOR_FROM_DATABASE=ELEA CardWare + +acpi:LZX*: + ID_VENDOR_FROM_DATABASE=Lightwell Company Ltd + +acpi:MAC*: + ID_VENDOR_FROM_DATABASE=MAC System Company Ltd + +acpi:MAD*: + ID_VENDOR_FROM_DATABASE=Xedia Corporation + +acpi:MAE*: + ID_VENDOR_FROM_DATABASE=Maestro Pty Ltd + +acpi:MAG*: + ID_VENDOR_FROM_DATABASE=MAG InnoVision + +acpi:MAI*: + ID_VENDOR_FROM_DATABASE=Mutoh America Inc + +acpi:MAL*: + ID_VENDOR_FROM_DATABASE=Meridian Audio Ltd + +acpi:MAN*: + ID_VENDOR_FROM_DATABASE=LGIC + +acpi:MAS*: + ID_VENDOR_FROM_DATABASE=Mass Inc. + +acpi:MAT*: + ID_VENDOR_FROM_DATABASE=Matsushita Electric Ind. Company Ltd + +acpi:MAX*: + ID_VENDOR_FROM_DATABASE=Rogen Tech Distribution Inc + +acpi:MAY*: + ID_VENDOR_FROM_DATABASE=Maynard Electronics + +acpi:MAZ*: + ID_VENDOR_FROM_DATABASE=MAZeT GmbH + +acpi:MBC*: + ID_VENDOR_FROM_DATABASE=MBC + +acpi:MBD*: + ID_VENDOR_FROM_DATABASE=Microbus PLC + +acpi:MBM*: + ID_VENDOR_FROM_DATABASE=Marshall Electronics + +acpi:MBV*: + ID_VENDOR_FROM_DATABASE=Moreton Bay + +acpi:MCA*: + ID_VENDOR_FROM_DATABASE=American Nuclear Systems Inc + +acpi:MCC*: + ID_VENDOR_FROM_DATABASE=Micro Industries + +acpi:MCD*: + ID_VENDOR_FROM_DATABASE=McDATA Corporation + +acpi:MCE*: + ID_VENDOR_FROM_DATABASE=Metz-Werke GmbH & Co KG + +acpi:MCG*: + ID_VENDOR_FROM_DATABASE=Motorola Computer Group + +acpi:MCI*: + ID_VENDOR_FROM_DATABASE=Micronics Computers + +acpi:MCL*: + ID_VENDOR_FROM_DATABASE=Motorola Communications Israel + +acpi:MCM*: + ID_VENDOR_FROM_DATABASE=Metricom Inc + +acpi:MCN*: + ID_VENDOR_FROM_DATABASE=Micron Electronics Inc + +acpi:MCO*: + ID_VENDOR_FROM_DATABASE=Motion Computing Inc. + +acpi:MCP*: + ID_VENDOR_FROM_DATABASE=Magni Systems Inc + +acpi:MCQ*: + ID_VENDOR_FROM_DATABASE=Mat's Computers + +acpi:MCR*: + ID_VENDOR_FROM_DATABASE=Marina Communicaitons + +acpi:MCS*: + ID_VENDOR_FROM_DATABASE=Micro Computer Systems + +acpi:MCT*: + ID_VENDOR_FROM_DATABASE=Microtec + +acpi:MCX*: + ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc. + +acpi:MCY*: + ID_VENDOR_FROM_DATABASE=Microdyne + +acpi:MDA*: + ID_VENDOR_FROM_DATABASE=Media4 Inc + +acpi:MDC*: + ID_VENDOR_FROM_DATABASE=Midori Electronics + +acpi:MDD*: + ID_VENDOR_FROM_DATABASE=MODIS + +acpi:MDF*: + ID_VENDOR_FROM_DATABASE=MILDEF AB + +acpi:MDG*: + ID_VENDOR_FROM_DATABASE=Madge Networks + +acpi:MDI*: + ID_VENDOR_FROM_DATABASE=Micro Design Inc + +acpi:MDK*: + ID_VENDOR_FROM_DATABASE=Mediatek Corporation + +acpi:MDO*: + ID_VENDOR_FROM_DATABASE=Panasonic + +acpi:MDR*: + ID_VENDOR_FROM_DATABASE=Medar Inc + +acpi:MDS*: + ID_VENDOR_FROM_DATABASE=Micro Display Systems Inc + +acpi:MDT*: + ID_VENDOR_FROM_DATABASE=Magus Data Tech + +acpi:MDV*: + ID_VENDOR_FROM_DATABASE=MET Development Inc + +acpi:MDX*: + ID_VENDOR_FROM_DATABASE=MicroDatec GmbH + +acpi:MDY*: + ID_VENDOR_FROM_DATABASE=Microdyne Inc + +acpi:MEC*: + ID_VENDOR_FROM_DATABASE=Mega System Technologies Inc + +acpi:MED*: + ID_VENDOR_FROM_DATABASE=Messeltronik Dresden GmbH + +acpi:MEE*: + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Engineering Co., Ltd. + +acpi:MEG*: + ID_VENDOR_FROM_DATABASE=Abeam Tech Ltd. + +acpi:MEI*: + ID_VENDOR_FROM_DATABASE=Panasonic Industry Company + +acpi:MEJ*: + ID_VENDOR_FROM_DATABASE=Mac-Eight Co., LTD. + +acpi:MEK*: + ID_VENDOR_FROM_DATABASE=Mediaedge Corporation + +acpi:MEL*: + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Corporation + +acpi:MEN*: + ID_VENDOR_FROM_DATABASE=MEN Mikroelectronik Nueruberg GmbH + +acpi:MEP*: + ID_VENDOR_FROM_DATABASE=Meld Technology + +acpi:MEQ*: + ID_VENDOR_FROM_DATABASE=Matelect Ltd. + +acpi:MET*: + ID_VENDOR_FROM_DATABASE=Metheus Corporation + +acpi:MEU*: + ID_VENDOR_FROM_DATABASE=MPL AG, Elektronik-Unternehmen + +acpi:MEX*: + ID_VENDOR_FROM_DATABASE=MSC Vertriebs GmbH + +acpi:MFG*: + ID_VENDOR_FROM_DATABASE=MicroField Graphics Inc + +acpi:MFI*: + ID_VENDOR_FROM_DATABASE=Micro Firmware + +acpi:MFR*: + ID_VENDOR_FROM_DATABASE=MediaFire Corp. + +acpi:MGA*: + ID_VENDOR_FROM_DATABASE=Mega System Technologies, Inc. + +acpi:MGC*: + ID_VENDOR_FROM_DATABASE=Mentor Graphics Corporation + +acpi:MGE*: + ID_VENDOR_FROM_DATABASE=Schneider Electric S.A. + +acpi:MGL*: + ID_VENDOR_FROM_DATABASE=M-G Technology Ltd + +acpi:MGT*: + ID_VENDOR_FROM_DATABASE=Megatech R & D Company + +acpi:MIC*: + ID_VENDOR_FROM_DATABASE=Micom Communications Inc + +acpi:MID*: + ID_VENDOR_FROM_DATABASE=miro Displays + +acpi:MII*: + ID_VENDOR_FROM_DATABASE=Mitec Inc + +acpi:MIL*: + ID_VENDOR_FROM_DATABASE=Marconi Instruments Ltd + +acpi:MIM*: + ID_VENDOR_FROM_DATABASE=Mimio – A Newell Rubbermaid Company + +acpi:MIN*: + ID_VENDOR_FROM_DATABASE=Minicom Digital Signage + +acpi:MIP*: + ID_VENDOR_FROM_DATABASE=micronpc.com + +acpi:MIR*: + ID_VENDOR_FROM_DATABASE=Miro Computer Prod. + +acpi:MIS*: + ID_VENDOR_FROM_DATABASE=Modular Industrial Solutions Inc + +acpi:MIT*: + ID_VENDOR_FROM_DATABASE=MCM Industrial Technology GmbH + +acpi:MIV*: + ID_VENDOR_FROM_DATABASE=MicroImage Video Systems + +acpi:MJI*: + ID_VENDOR_FROM_DATABASE=MARANTZ JAPAN, INC. + +acpi:MJS*: + ID_VENDOR_FROM_DATABASE=MJS Designs + +acpi:MKC*: + ID_VENDOR_FROM_DATABASE=Media Tek Inc. + +acpi:MKS*: + ID_VENDOR_FROM_DATABASE=MK Seiko Co., Ltd. + +acpi:MKT*: + ID_VENDOR_FROM_DATABASE=MICROTEK Inc. + +acpi:MKV*: + ID_VENDOR_FROM_DATABASE=Trtheim Technology + +acpi:MLD*: + ID_VENDOR_FROM_DATABASE=Deep Video Imaging Ltd + +acpi:MLG*: + ID_VENDOR_FROM_DATABASE=Micrologica AG + +acpi:MLI*: + ID_VENDOR_FROM_DATABASE=McIntosh Laboratory Inc. + +acpi:MLL*: + ID_VENDOR_FROM_DATABASE=Millogic Ltd. + +acpi:MLM*: + ID_VENDOR_FROM_DATABASE=Millennium Engineering Inc + +acpi:MLN*: + ID_VENDOR_FROM_DATABASE=Mark Levinson + +acpi:MLP*: + ID_VENDOR_FROM_DATABASE=Magic Leap + +acpi:MLS*: + ID_VENDOR_FROM_DATABASE=Milestone EPE + +acpi:MLT*: + ID_VENDOR_FROM_DATABASE=Wanlida Group Co., Ltd. + +acpi:MLX*: + ID_VENDOR_FROM_DATABASE=Mylex Corporation + +acpi:MMA*: + ID_VENDOR_FROM_DATABASE=Micromedia AG + +acpi:MMD*: + ID_VENDOR_FROM_DATABASE=Micromed Biotecnologia Ltd + +acpi:MMF*: + ID_VENDOR_FROM_DATABASE=Minnesota Mining and Manufacturing + +acpi:MMI*: + ID_VENDOR_FROM_DATABASE=Multimax + +acpi:MMM*: + ID_VENDOR_FROM_DATABASE=Electronic Measurements + +acpi:MMN*: + ID_VENDOR_FROM_DATABASE=MiniMan Inc + +acpi:MMS*: + ID_VENDOR_FROM_DATABASE=MMS Electronics + +acpi:MNC*: + ID_VENDOR_FROM_DATABASE=Mini Micro Methods Ltd + +acpi:MNI*: + ID_VENDOR_FROM_DATABASE=Marseille, Inc. + +acpi:MNL*: + ID_VENDOR_FROM_DATABASE=Monorail Inc + +acpi:MNP*: + ID_VENDOR_FROM_DATABASE=Microcom + +acpi:MOD*: + ID_VENDOR_FROM_DATABASE=Modular Technology + +acpi:MOM*: + ID_VENDOR_FROM_DATABASE=Momentum Data Systems + +acpi:MON*: + ID_VENDOR_FROM_DATABASE=Daewoo + +acpi:MOS*: + ID_VENDOR_FROM_DATABASE=Moses Corporation + +acpi:MOT*: + ID_VENDOR_FROM_DATABASE=Motorola UDS + +acpi:MPC*: + ID_VENDOR_FROM_DATABASE=M-Pact Inc + +acpi:MPI*: + ID_VENDOR_FROM_DATABASE=Mediatrix Peripherals Inc + +acpi:MPJ*: + ID_VENDOR_FROM_DATABASE=Microlab + +acpi:MPL*: + ID_VENDOR_FROM_DATABASE=Maple Research Inst. Company Ltd + +acpi:MPN*: + ID_VENDOR_FROM_DATABASE=Mainpine Limited + +acpi:MPS*: + ID_VENDOR_FROM_DATABASE=mps Software GmbH + +acpi:MPX*: + ID_VENDOR_FROM_DATABASE=Micropix Technologies, Ltd. + +acpi:MQP*: + ID_VENDOR_FROM_DATABASE=MultiQ Products AB + +acpi:MRA*: + ID_VENDOR_FROM_DATABASE=Miranda Technologies Inc + +acpi:MRC*: + ID_VENDOR_FROM_DATABASE=Marconi Simulation & Ty-Coch Way Training + +acpi:MRD*: + ID_VENDOR_FROM_DATABASE=MicroDisplay Corporation + +acpi:MRK*: + ID_VENDOR_FROM_DATABASE=Maruko & Company Ltd + +acpi:MRL*: + ID_VENDOR_FROM_DATABASE=Miratel + +acpi:MRO*: + ID_VENDOR_FROM_DATABASE=Medikro Oy + +acpi:MRT*: + ID_VENDOR_FROM_DATABASE=Merging Technologies + +acpi:MSA*: + ID_VENDOR_FROM_DATABASE=Micro Systemation AB + +acpi:MSC*: + ID_VENDOR_FROM_DATABASE=Mouse Systems Corporation + +acpi:MSD*: + ID_VENDOR_FROM_DATABASE=Datenerfassungs- und Informationssysteme + +acpi:MSF*: + ID_VENDOR_FROM_DATABASE=M-Systems Flash Disk Pioneers + +acpi:MSG*: + ID_VENDOR_FROM_DATABASE=MSI GmbH + +acpi:MSH*: + ID_VENDOR_FROM_DATABASE=Microsoft + +acpi:MSI*: + ID_VENDOR_FROM_DATABASE=Microstep + +acpi:MSK*: + ID_VENDOR_FROM_DATABASE=Megasoft Inc + +acpi:MSL*: + ID_VENDOR_FROM_DATABASE=MicroSlate Inc. + +acpi:MSM*: + ID_VENDOR_FROM_DATABASE=Advanced Digital Systems + +acpi:MSP*: + ID_VENDOR_FROM_DATABASE=Mistral Solutions [P] Ltd. + +acpi:MSR*: + ID_VENDOR_FROM_DATABASE=MASPRO DENKOH Corp. + +acpi:MST*: + ID_VENDOR_FROM_DATABASE=MS Telematica + +acpi:MSU*: + ID_VENDOR_FROM_DATABASE=motorola + +acpi:MSV*: + ID_VENDOR_FROM_DATABASE=Mosgi Corporation + +acpi:MSX*: + ID_VENDOR_FROM_DATABASE=Micomsoft Co., Ltd. + +acpi:MSY*: + ID_VENDOR_FROM_DATABASE=MicroTouch Systems Inc + +acpi:MTA*: + ID_VENDOR_FROM_DATABASE=Meta Watch Ltd + +acpi:MTB*: + ID_VENDOR_FROM_DATABASE=Media Technologies Ltd. + +acpi:MTC*: + ID_VENDOR_FROM_DATABASE=Mars-Tech Corporation + +acpi:MTD*: + ID_VENDOR_FROM_DATABASE=MindTech Display Co. Ltd + +acpi:MTE*: + ID_VENDOR_FROM_DATABASE=MediaTec GmbH + +acpi:MTH*: + ID_VENDOR_FROM_DATABASE=Micro-Tech Hearing Instruments + +acpi:MTI*: + ID_VENDOR_FROM_DATABASE=MaxCom Technical Inc + +acpi:MTJ*: + ID_VENDOR_FROM_DATABASE=MicroTechnica Co.,Ltd. + +acpi:MTK*: + ID_VENDOR_FROM_DATABASE=Microtek International Inc. + +acpi:MTL*: + ID_VENDOR_FROM_DATABASE=Mitel Corporation + +acpi:MTM*: + ID_VENDOR_FROM_DATABASE=Motium + +acpi:MTN*: + ID_VENDOR_FROM_DATABASE=Mtron Storage Technology Co., Ltd. + +acpi:MTR*: + ID_VENDOR_FROM_DATABASE=Mitron computer Inc + +acpi:MTS*: + ID_VENDOR_FROM_DATABASE=Multi-Tech Systems + +acpi:MTU*: + ID_VENDOR_FROM_DATABASE=Mark of the Unicorn Inc + +acpi:MTX*: + ID_VENDOR_FROM_DATABASE=Matrox + +acpi:MUD*: + ID_VENDOR_FROM_DATABASE=Multi-Dimension Institute + +acpi:MUK*: + ID_VENDOR_FROM_DATABASE=Mainpine Limited + +acpi:MVD*: + ID_VENDOR_FROM_DATABASE=Microvitec PLC + +acpi:MVI*: + ID_VENDOR_FROM_DATABASE=Media Vision Inc + +acpi:MVM*: + ID_VENDOR_FROM_DATABASE=SOBO VISION + +acpi:MVN*: + ID_VENDOR_FROM_DATABASE=Meta Company + +acpi:MVS*: + ID_VENDOR_FROM_DATABASE=Microvision + +acpi:MVX*: + ID_VENDOR_FROM_DATABASE=COM 1 + +acpi:MWI*: + ID_VENDOR_FROM_DATABASE=Multiwave Innovation Pte Ltd + +acpi:MWR*: + ID_VENDOR_FROM_DATABASE=mware + +acpi:MWY*: + ID_VENDOR_FROM_DATABASE=Microway Inc + +acpi:MXD*: + ID_VENDOR_FROM_DATABASE=MaxData Computer GmbH & Co.KG + +acpi:MXI*: + ID_VENDOR_FROM_DATABASE=Macronix Inc + +acpi:MXL*: + ID_VENDOR_FROM_DATABASE=Hitachi Maxell, Ltd. + +acpi:MXP*: + ID_VENDOR_FROM_DATABASE=Maxpeed Corporation + +acpi:MXT*: + ID_VENDOR_FROM_DATABASE=Maxtech Corporation + +acpi:MXV*: + ID_VENDOR_FROM_DATABASE=MaxVision Corporation + +acpi:MYA*: + ID_VENDOR_FROM_DATABASE=Monydata + +acpi:MYR*: + ID_VENDOR_FROM_DATABASE=Myriad Solutions Ltd + +acpi:MYX*: + ID_VENDOR_FROM_DATABASE=Micronyx Inc + +acpi:NAC*: + ID_VENDOR_FROM_DATABASE=Ncast Corporation + +acpi:NAD*: + ID_VENDOR_FROM_DATABASE=NAD Electronics + +acpi:NAK*: + ID_VENDOR_FROM_DATABASE=Nakano Engineering Co.,Ltd. + +acpi:NAL*: + ID_VENDOR_FROM_DATABASE=Network Alchemy + +acpi:NAN*: + ID_VENDOR_FROM_DATABASE=Nanao + +acpi:NAT*: + ID_VENDOR_FROM_DATABASE=NaturalPoint Inc. + +acpi:NAV*: + ID_VENDOR_FROM_DATABASE=Navigation Corporation + +acpi:NAX*: + ID_VENDOR_FROM_DATABASE=Naxos Tecnologia + +acpi:NBL*: + ID_VENDOR_FROM_DATABASE=N*Able Technologies Inc + +acpi:NBS*: + ID_VENDOR_FROM_DATABASE=National Key Lab. on ISN + +acpi:NBT*: + ID_VENDOR_FROM_DATABASE=NingBo Bestwinning Technology CO., Ltd + +acpi:NCA*: + ID_VENDOR_FROM_DATABASE=Nixdorf Company + +acpi:NCC*: + ID_VENDOR_FROM_DATABASE=NCR Corporation + +acpi:NCE*: + ID_VENDOR_FROM_DATABASE=Norcent Technology, Inc. + +acpi:NCI*: + ID_VENDOR_FROM_DATABASE=NewCom Inc + +acpi:NCL*: + ID_VENDOR_FROM_DATABASE=NetComm Ltd + +acpi:NCP*: + ID_VENDOR_FROM_DATABASE=Najing CEC Panda FPD Technology CO. ltd + +acpi:NCR*: + ID_VENDOR_FROM_DATABASE=NCR Electronics + +acpi:NCS*: + ID_VENDOR_FROM_DATABASE=Northgate Computer Systems + +acpi:NCT*: + ID_VENDOR_FROM_DATABASE=NEC CustomTechnica, Ltd. + +acpi:NDC*: + ID_VENDOR_FROM_DATABASE=National DataComm Corporaiton + +acpi:NDF*: + ID_VENDOR_FROM_DATABASE=NDF Special Light Products B.V. + +acpi:NDI*: + ID_VENDOR_FROM_DATABASE=National Display Systems + +acpi:NDK*: + ID_VENDOR_FROM_DATABASE=Naitoh Densei CO., LTD. + +acpi:NDL*: + ID_VENDOR_FROM_DATABASE=Network Designers + +acpi:NDS*: + ID_VENDOR_FROM_DATABASE=Nokia Data + +acpi:NEC*: + ID_VENDOR_FROM_DATABASE=NEC Corporation + +acpi:NEO*: + ID_VENDOR_FROM_DATABASE=NEO TELECOM CO.,LTD. + +acpi:NES*: + ID_VENDOR_FROM_DATABASE=INNES + +acpi:NET*: + ID_VENDOR_FROM_DATABASE=Mettler Toledo + +acpi:NEU*: + ID_VENDOR_FROM_DATABASE=NEUROTEC - EMPRESA DE PESQUISA E DESENVOLVIMENTO EM BIOMEDICINA + +acpi:NEX*: + ID_VENDOR_FROM_DATABASE=Nexgen Mediatech Inc., + +acpi:NFC*: + ID_VENDOR_FROM_DATABASE=BTC Korea Co., Ltd + +acpi:NFS*: + ID_VENDOR_FROM_DATABASE=Number Five Software + +acpi:NGC*: + ID_VENDOR_FROM_DATABASE=Network General + +acpi:NGS*: + ID_VENDOR_FROM_DATABASE=A D S Exports + +acpi:NHT*: + ID_VENDOR_FROM_DATABASE=Vinci Labs + +acpi:NIC*: + ID_VENDOR_FROM_DATABASE=National Instruments Corporation + +acpi:NIS*: + ID_VENDOR_FROM_DATABASE=Nissei Electric Company + +acpi:NIT*: + ID_VENDOR_FROM_DATABASE=Network Info Technology + +acpi:NIX*: + ID_VENDOR_FROM_DATABASE=Seanix Technology Inc + +acpi:NLC*: + ID_VENDOR_FROM_DATABASE=Next Level Communications + +acpi:NME*: + ID_VENDOR_FROM_DATABASE=Navico, Inc. + +acpi:NMP*: + ID_VENDOR_FROM_DATABASE=Nokia Mobile Phones + +acpi:NMS*: + ID_VENDOR_FROM_DATABASE=Natural Micro System + +acpi:NMV*: + ID_VENDOR_FROM_DATABASE=NEC-Mitsubishi Electric Visual Systems Corporation + +acpi:NMX*: + ID_VENDOR_FROM_DATABASE=Neomagic + +acpi:NNC*: + ID_VENDOR_FROM_DATABASE=NNC + +acpi:NOD*: + ID_VENDOR_FROM_DATABASE=3NOD Digital Technology Co. Ltd. + +acpi:NOE*: + ID_VENDOR_FROM_DATABASE=NordicEye AB + +acpi:NOI*: + ID_VENDOR_FROM_DATABASE=North Invent A/S + +acpi:NOK*: + ID_VENDOR_FROM_DATABASE=Nokia Display Products + +acpi:NOR*: + ID_VENDOR_FROM_DATABASE=Norand Corporation + +acpi:NOT*: + ID_VENDOR_FROM_DATABASE=Not Limited Inc + +acpi:NPA*: + ID_VENDOR_FROM_DATABASE=Arvanics + +acpi:NPI*: + ID_VENDOR_FROM_DATABASE=Network Peripherals Inc + +acpi:NRL*: + ID_VENDOR_FROM_DATABASE=U.S. Naval Research Lab + +acpi:NRT*: + ID_VENDOR_FROM_DATABASE=Beijing Northern Radiantelecom Co. + +acpi:NRV*: + ID_VENDOR_FROM_DATABASE=Taugagreining hf + +acpi:NSA*: + ID_VENDOR_FROM_DATABASE=NeuroSky, Inc. + +acpi:NSC*: + ID_VENDOR_FROM_DATABASE=National Semiconductor Corporation + +acpi:NSI*: + ID_VENDOR_FROM_DATABASE=NISSEI ELECTRIC CO.,LTD + +acpi:NSP*: + ID_VENDOR_FROM_DATABASE=Nspire System Inc. + +acpi:NSS*: + ID_VENDOR_FROM_DATABASE=Newport Systems Solutions + +acpi:NST*: + ID_VENDOR_FROM_DATABASE=Network Security Technology Co + +acpi:NTC*: + ID_VENDOR_FROM_DATABASE=NeoTech S.R.L + +acpi:NTI*: + ID_VENDOR_FROM_DATABASE=New Tech Int'l Company + +acpi:NTL*: + ID_VENDOR_FROM_DATABASE=National Transcomm. Ltd + +acpi:NTN*: + ID_VENDOR_FROM_DATABASE=Nuvoton Technology Corporation + +acpi:NTR*: + ID_VENDOR_FROM_DATABASE=N-trig Innovative Technologies, Inc. + +acpi:NTS*: + ID_VENDOR_FROM_DATABASE=Nits Technology Inc. + +acpi:NTT*: + ID_VENDOR_FROM_DATABASE=NTT Advanced Technology Corporation + +acpi:NTW*: + ID_VENDOR_FROM_DATABASE=Networth Inc + +acpi:NTX*: + ID_VENDOR_FROM_DATABASE=Netaccess Inc + +acpi:NUG*: + ID_VENDOR_FROM_DATABASE=NU Technology, Inc. + +acpi:NUI*: + ID_VENDOR_FROM_DATABASE=NU Inc. + +acpi:NVC*: + ID_VENDOR_FROM_DATABASE=NetVision Corporation + +acpi:NVD*: + ID_VENDOR_FROM_DATABASE=Nvidia + +acpi:NVI*: + ID_VENDOR_FROM_DATABASE=NuVision US, Inc. + +acpi:NVL*: + ID_VENDOR_FROM_DATABASE=Novell Inc + +acpi:NVT*: + ID_VENDOR_FROM_DATABASE=Navatek Engineering Corporation + +acpi:NWC*: + ID_VENDOR_FROM_DATABASE=NW Computer Engineering + +acpi:NWP*: + ID_VENDOR_FROM_DATABASE=NovaWeb Technologies Inc + +acpi:NWS*: + ID_VENDOR_FROM_DATABASE=Newisys, Inc. + +acpi:NXC*: + ID_VENDOR_FROM_DATABASE=NextCom K.K. + +acpi:NXG*: + ID_VENDOR_FROM_DATABASE=Nexgen + +acpi:NXP*: + ID_VENDOR_FROM_DATABASE=NXP Semiconductors bv. + +acpi:NXQ*: + ID_VENDOR_FROM_DATABASE=Nexiq Technologies, Inc. + +acpi:NXS*: + ID_VENDOR_FROM_DATABASE=Technology Nexus Secure Open Systems AB + +acpi:NYC*: + ID_VENDOR_FROM_DATABASE=Nakayo Relecommunications, Inc. + +acpi:OAK*: + ID_VENDOR_FROM_DATABASE=Oak Tech Inc + +acpi:OAS*: + ID_VENDOR_FROM_DATABASE=Oasys Technology Company + +acpi:OBS*: + ID_VENDOR_FROM_DATABASE=Optibase Technologies + +acpi:OCD*: + ID_VENDOR_FROM_DATABASE=Macraigor Systems Inc + +acpi:OCN*: + ID_VENDOR_FROM_DATABASE=Olfan + +acpi:OCS*: + ID_VENDOR_FROM_DATABASE=Open Connect Solutions + +acpi:ODM*: + ID_VENDOR_FROM_DATABASE=ODME Inc. + +acpi:ODR*: + ID_VENDOR_FROM_DATABASE=Odrac + +acpi:OEC*: + ID_VENDOR_FROM_DATABASE=ORION ELECTRIC CO.,LTD + +acpi:OEI*: + ID_VENDOR_FROM_DATABASE=Optum Engineering Inc. + +acpi:OHW*: + ID_VENDOR_FROM_DATABASE=M-Labs Limited + +acpi:OIC*: + ID_VENDOR_FROM_DATABASE=Option Industrial Computers + +acpi:OIM*: + ID_VENDOR_FROM_DATABASE=Option International + +acpi:OIN*: + ID_VENDOR_FROM_DATABASE=Option International + +acpi:OKI*: + ID_VENDOR_FROM_DATABASE=OKI Electric Industrial Company Ltd + +acpi:OLC*: + ID_VENDOR_FROM_DATABASE=Olicom A/S + +acpi:OLD*: + ID_VENDOR_FROM_DATABASE=Olidata S.p.A. + +acpi:OLI*: + ID_VENDOR_FROM_DATABASE=Olivetti + +acpi:OLT*: + ID_VENDOR_FROM_DATABASE=Olitec S.A. + +acpi:OLV*: + ID_VENDOR_FROM_DATABASE=Olitec S.A. + +acpi:OLY*: + ID_VENDOR_FROM_DATABASE=OLYMPUS CORPORATION + +acpi:OMC*: + ID_VENDOR_FROM_DATABASE=OBJIX Multimedia Corporation + +acpi:OMN*: + ID_VENDOR_FROM_DATABASE=Omnitel + +acpi:OMR*: + ID_VENDOR_FROM_DATABASE=Omron Corporation + +acpi:ONE*: + ID_VENDOR_FROM_DATABASE=Oneac Corporation + +acpi:ONK*: + ID_VENDOR_FROM_DATABASE=ONKYO Corporation + +acpi:ONL*: + ID_VENDOR_FROM_DATABASE=OnLive, Inc + +acpi:ONS*: + ID_VENDOR_FROM_DATABASE=On Systems Inc + +acpi:ONW*: + ID_VENDOR_FROM_DATABASE=OPEN Networks Ltd + +acpi:ONX*: + ID_VENDOR_FROM_DATABASE=SOMELEC Z.I. Du Vert Galanta + +acpi:OOS*: + ID_VENDOR_FROM_DATABASE=OSRAM + +acpi:OPC*: + ID_VENDOR_FROM_DATABASE=Opcode Inc + +acpi:OPI*: + ID_VENDOR_FROM_DATABASE=D.N.S. Corporation + +acpi:OPP*: + ID_VENDOR_FROM_DATABASE=OPPO Digital, Inc. + +acpi:OPT*: + ID_VENDOR_FROM_DATABASE=OPTi Inc + +acpi:OPV*: + ID_VENDOR_FROM_DATABASE=Optivision Inc + +acpi:OQI*: + ID_VENDOR_FROM_DATABASE=Oksori Company Ltd + +acpi:ORG*: + ID_VENDOR_FROM_DATABASE=ORGA Kartensysteme GmbH + +acpi:ORI*: + ID_VENDOR_FROM_DATABASE=OSR Open Systems Resources, Inc. + +acpi:ORN*: + ID_VENDOR_FROM_DATABASE=ORION ELECTRIC CO., LTD. + +acpi:OSA*: + ID_VENDOR_FROM_DATABASE=OSAKA Micro Computer, Inc. + +acpi:OSD*: + ID_VENDOR_FROM_DATABASE=Optical Systems Design Pty Ltd + +acpi:OSI*: + ID_VENDOR_FROM_DATABASE=Open Stack, Inc. + +acpi:OSP*: + ID_VENDOR_FROM_DATABASE=OPTI-UPS Corporation + +acpi:OSR*: + ID_VENDOR_FROM_DATABASE=Oksori Company Ltd + +acpi:OTB*: + ID_VENDOR_FROM_DATABASE=outsidetheboxstuff.com + +acpi:OTI*: + ID_VENDOR_FROM_DATABASE=Orchid Technology + +acpi:OTK*: + ID_VENDOR_FROM_DATABASE=OmniTek + +acpi:OTM*: + ID_VENDOR_FROM_DATABASE=Optoma Corporation + +acpi:OTT*: + ID_VENDOR_FROM_DATABASE=OPTO22, Inc. + +acpi:OUK*: + ID_VENDOR_FROM_DATABASE=OUK Company Ltd + +acpi:OVR*: + ID_VENDOR_FROM_DATABASE=Oculus VR, Inc. + +acpi:OWL*: + ID_VENDOR_FROM_DATABASE=Mediacom Technologies Pte Ltd + +acpi:OXU*: + ID_VENDOR_FROM_DATABASE=Oxus Research S.A. + +acpi:OYO*: + ID_VENDOR_FROM_DATABASE=Shadow Systems + +acpi:OZC*: + ID_VENDOR_FROM_DATABASE=OZ Corporation + +acpi:OZO*: + ID_VENDOR_FROM_DATABASE=Tribe Computer Works Inc + +acpi:PAC*: + ID_VENDOR_FROM_DATABASE=Pacific Avionics Corporation + +acpi:PAD*: + ID_VENDOR_FROM_DATABASE=Promotion and Display Technology Ltd. + +acpi:PAK*: + ID_VENDOR_FROM_DATABASE=Many CNC System Co., Ltd. + +acpi:PAM*: + ID_VENDOR_FROM_DATABASE=Peter Antesberger Messtechnik + +acpi:PAN*: + ID_VENDOR_FROM_DATABASE=The Panda Project + +acpi:PAR*: + ID_VENDOR_FROM_DATABASE=Parallan Comp Inc + +acpi:PBI*: + ID_VENDOR_FROM_DATABASE=Pitney Bowes + +acpi:PBL*: + ID_VENDOR_FROM_DATABASE=Packard Bell Electronics + +acpi:PBN*: + ID_VENDOR_FROM_DATABASE=Packard Bell NEC + +acpi:PBV*: + ID_VENDOR_FROM_DATABASE=Pitney Bowes + +acpi:PCA*: + ID_VENDOR_FROM_DATABASE=Philips BU Add On Card + +acpi:PCB*: + ID_VENDOR_FROM_DATABASE=OCTAL S.A. + +acpi:PCC*: + ID_VENDOR_FROM_DATABASE=PowerCom Technology Company Ltd + +acpi:PCG*: + ID_VENDOR_FROM_DATABASE=First Industrial Computer Inc + +acpi:PCI*: + ID_VENDOR_FROM_DATABASE=Pioneer Computer Inc + +acpi:PCK*: + ID_VENDOR_FROM_DATABASE=PCBANK21 + +acpi:PCL*: + ID_VENDOR_FROM_DATABASE=pentel.co.,ltd + +acpi:PCM*: + ID_VENDOR_FROM_DATABASE=PCM Systems Corporation + +acpi:PCO*: + ID_VENDOR_FROM_DATABASE=Performance Concepts Inc., + +acpi:PCP*: + ID_VENDOR_FROM_DATABASE=Procomp USA Inc + +acpi:PCS*: + ID_VENDOR_FROM_DATABASE=TOSHIBA PERSONAL COMPUTER SYSTEM CORPRATION + +acpi:PCT*: + ID_VENDOR_FROM_DATABASE=PC-Tel Inc + +acpi:PCW*: + ID_VENDOR_FROM_DATABASE=Pacific CommWare Inc + +acpi:PCX*: + ID_VENDOR_FROM_DATABASE=PC Xperten + +acpi:PDC*: + ID_VENDOR_FROM_DATABASE=Polaroid + +acpi:PDM*: + ID_VENDOR_FROM_DATABASE=Psion Dacom Plc. + +acpi:PDN*: + ID_VENDOR_FROM_DATABASE=AT&T Paradyne + +acpi:PDR*: + ID_VENDOR_FROM_DATABASE=Pure Data Inc + +acpi:PDS*: + ID_VENDOR_FROM_DATABASE=PD Systems International Ltd + +acpi:PDT*: + ID_VENDOR_FROM_DATABASE=PDTS - Prozessdatentechnik und Systeme + +acpi:PDV*: + ID_VENDOR_FROM_DATABASE=Prodrive B.V. + +acpi:PEC*: + ID_VENDOR_FROM_DATABASE=POTRANS Electrical Corp. + +acpi:PEG*: + ID_VENDOR_FROM_DATABASE=Pegatron Corporation + +acpi:PEI*: + ID_VENDOR_FROM_DATABASE=PEI Electronics Inc + +acpi:PEL*: + ID_VENDOR_FROM_DATABASE=Primax Electric Ltd + +acpi:PEN*: + ID_VENDOR_FROM_DATABASE=Interactive Computer Products Inc + +acpi:PEP*: + ID_VENDOR_FROM_DATABASE=Peppercon AG + +acpi:PER*: + ID_VENDOR_FROM_DATABASE=Perceptive Signal Technologies + +acpi:PET*: + ID_VENDOR_FROM_DATABASE=Practical Electronic Tools + +acpi:PFT*: + ID_VENDOR_FROM_DATABASE=Telia ProSoft AB + +acpi:PGI*: + ID_VENDOR_FROM_DATABASE=PACSGEAR, Inc. + +acpi:PGM*: + ID_VENDOR_FROM_DATABASE=Paradigm Advanced Research Centre + +acpi:PGP*: + ID_VENDOR_FROM_DATABASE=propagamma kommunikation + +acpi:PGS*: + ID_VENDOR_FROM_DATABASE=Princeton Graphic Systems + +acpi:PHC*: + ID_VENDOR_FROM_DATABASE=Pijnenburg Beheer N.V. + +acpi:PHE*: + ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH + +acpi:PHL*: + ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company + +acpi:PHO*: + ID_VENDOR_FROM_DATABASE=Photonics Systems Inc. + +acpi:PHS*: + ID_VENDOR_FROM_DATABASE=Philips Communication Systems + +acpi:PHY*: + ID_VENDOR_FROM_DATABASE=Phylon Communications + +acpi:PIC*: + ID_VENDOR_FROM_DATABASE=Picturall Ltd. + +acpi:PIE*: + ID_VENDOR_FROM_DATABASE=Pacific Image Electronics Company Ltd + +acpi:PIM*: + ID_VENDOR_FROM_DATABASE=Prism, LLC + +acpi:PIO*: + ID_VENDOR_FROM_DATABASE=Pioneer Electronic Corporation + +acpi:PIX*: + ID_VENDOR_FROM_DATABASE=Pixie Tech Inc + +acpi:PJA*: + ID_VENDOR_FROM_DATABASE=Projecta + +acpi:PJD*: + ID_VENDOR_FROM_DATABASE=Projectiondesign AS + +acpi:PJT*: + ID_VENDOR_FROM_DATABASE=Pan Jit International Inc. + +acpi:PKA*: + ID_VENDOR_FROM_DATABASE=Acco UK Ltd. + +acpi:PLC*: + ID_VENDOR_FROM_DATABASE=Pro-Log Corporation + +acpi:PLF*: + ID_VENDOR_FROM_DATABASE=Panasonic Avionics Corporation + +acpi:PLM*: + ID_VENDOR_FROM_DATABASE=PROLINK Microsystems Corp. + +acpi:PLT*: + ID_VENDOR_FROM_DATABASE=PT Hartono Istana Teknologi + +acpi:PLV*: + ID_VENDOR_FROM_DATABASE=PLUS Vision Corp. + +acpi:PLX*: + ID_VENDOR_FROM_DATABASE=Parallax Graphics + +acpi:PLY*: + ID_VENDOR_FROM_DATABASE=Polycom Inc. + +acpi:PMC*: + ID_VENDOR_FROM_DATABASE=PMC Consumer Electronics Ltd + +acpi:PMD*: + ID_VENDOR_FROM_DATABASE=TDK USA Corporation + +acpi:PMM*: + ID_VENDOR_FROM_DATABASE=Point Multimedia System + +acpi:PMT*: + ID_VENDOR_FROM_DATABASE=Promate Electronic Co., Ltd. + +acpi:PMX*: + ID_VENDOR_FROM_DATABASE=Photomatrix + +acpi:PNG*: + ID_VENDOR_FROM_DATABASE=Microsoft + +acpi:PNL*: + ID_VENDOR_FROM_DATABASE=Panelview, Inc. + +acpi:PNR*: + ID_VENDOR_FROM_DATABASE=Planar Systems, Inc. + +acpi:PNS*: + ID_VENDOR_FROM_DATABASE=PanaScope + +acpi:PNX*: + ID_VENDOR_FROM_DATABASE=Phoenix Technologies, Ltd. + +acpi:POL*: + ID_VENDOR_FROM_DATABASE=PolyComp (PTY) Ltd. + +acpi:PON*: + ID_VENDOR_FROM_DATABASE=Perpetual Technologies, LLC + +acpi:POR*: + ID_VENDOR_FROM_DATABASE=Portalis LC + +acpi:POT*: + ID_VENDOR_FROM_DATABASE=Parrot + +acpi:PPC*: + ID_VENDOR_FROM_DATABASE=Phoenixtec Power Company Ltd + +acpi:PPD*: + ID_VENDOR_FROM_DATABASE=MEPhI + +acpi:PPI*: + ID_VENDOR_FROM_DATABASE=Practical Peripherals + +acpi:PPM*: + ID_VENDOR_FROM_DATABASE=Clinton Electronics Corp. + +acpi:PPP*: + ID_VENDOR_FROM_DATABASE=Purup Prepress AS + +acpi:PPR*: + ID_VENDOR_FROM_DATABASE=PicPro + +acpi:PPX*: + ID_VENDOR_FROM_DATABASE=Perceptive Pixel Inc. + +acpi:PQI*: + ID_VENDOR_FROM_DATABASE=Pixel Qi + +acpi:PRA*: + ID_VENDOR_FROM_DATABASE=PRO/AUTOMATION + +acpi:PRC*: + ID_VENDOR_FROM_DATABASE=PerComm + +acpi:PRD*: + ID_VENDOR_FROM_DATABASE=Praim S.R.L. + +acpi:PRF*: + ID_VENDOR_FROM_DATABASE=Digital Electronics Corporation + +acpi:PRG*: + ID_VENDOR_FROM_DATABASE=The Phoenix Research Group Inc + +acpi:PRI*: + ID_VENDOR_FROM_DATABASE=Priva Hortimation BV + +acpi:PRM*: + ID_VENDOR_FROM_DATABASE=Prometheus + +acpi:PRO*: + ID_VENDOR_FROM_DATABASE=Proteon + +acpi:PRP*: + ID_VENDOR_FROM_DATABASE=UEFI Forum + +acpi:PRS*: + ID_VENDOR_FROM_DATABASE=Leutron Vision + +acpi:PRT*: + ID_VENDOR_FROM_DATABASE=Parade Technologies, Ltd. + +acpi:PRX*: + ID_VENDOR_FROM_DATABASE=Proxima Corporation + +acpi:PSA*: + ID_VENDOR_FROM_DATABASE=Advanced Signal Processing Technologies + +acpi:PSC*: + ID_VENDOR_FROM_DATABASE=Philips Semiconductors + +acpi:PSD*: + ID_VENDOR_FROM_DATABASE=Peus-Systems GmbH + +acpi:PSE*: + ID_VENDOR_FROM_DATABASE=Practical Solutions Pte., Ltd. + +acpi:PSI*: + ID_VENDOR_FROM_DATABASE=PSI-Perceptive Solutions Inc + +acpi:PSL*: + ID_VENDOR_FROM_DATABASE=Perle Systems Limited + +acpi:PSM*: + ID_VENDOR_FROM_DATABASE=Prosum + +acpi:PST*: + ID_VENDOR_FROM_DATABASE=Global Data SA + +acpi:PSY*: + ID_VENDOR_FROM_DATABASE=Prodea Systems Inc. + +acpi:PTA*: + ID_VENDOR_FROM_DATABASE=PAR Tech Inc. + +acpi:PTC*: + ID_VENDOR_FROM_DATABASE=PS Technology Corporation + +acpi:PTG*: + ID_VENDOR_FROM_DATABASE=Cipher Systems Inc + +acpi:PTH*: + ID_VENDOR_FROM_DATABASE=Pathlight Technology Inc + +acpi:PTI*: + ID_VENDOR_FROM_DATABASE=Promise Technology Inc + +acpi:PTL*: + ID_VENDOR_FROM_DATABASE=Pantel Inc + +acpi:PTS*: + ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc + +acpi:PUL*: + ID_VENDOR_FROM_DATABASE=Pulse-Eight Ltd + +acpi:PVG*: + ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd + +acpi:PVI*: + ID_VENDOR_FROM_DATABASE=Prime view international Co., Ltd + +acpi:PVM*: + ID_VENDOR_FROM_DATABASE=Penta Studiotechnik GmbH + +acpi:PVN*: + ID_VENDOR_FROM_DATABASE=Pixel Vision + +acpi:PVP*: + ID_VENDOR_FROM_DATABASE=Klos Technologies, Inc. + +acpi:PXC*: + ID_VENDOR_FROM_DATABASE=Phoenix Contact + +acpi:PXE*: + ID_VENDOR_FROM_DATABASE=PIXELA CORPORATION + +acpi:PXL*: + ID_VENDOR_FROM_DATABASE=The Moving Pixel Company + +acpi:PXM*: + ID_VENDOR_FROM_DATABASE=Proxim Inc + +acpi:QCC*: + ID_VENDOR_FROM_DATABASE=QuakeCom Company Ltd + +acpi:QCH*: + ID_VENDOR_FROM_DATABASE=Metronics Inc + +acpi:QCI*: + ID_VENDOR_FROM_DATABASE=Quanta Computer Inc + +acpi:QCK*: + ID_VENDOR_FROM_DATABASE=Quick Corporation + +acpi:QCL*: + ID_VENDOR_FROM_DATABASE=Quadrant Components Inc + +acpi:QCP*: + ID_VENDOR_FROM_DATABASE=Qualcomm Inc + +acpi:QDI*: + ID_VENDOR_FROM_DATABASE=Quantum Data Incorporated + +acpi:QDM*: + ID_VENDOR_FROM_DATABASE=Quadram + +acpi:QDS*: + ID_VENDOR_FROM_DATABASE=Quanta Display Inc. + +acpi:QFF*: + ID_VENDOR_FROM_DATABASE=Padix Co., Inc. + +acpi:QFI*: + ID_VENDOR_FROM_DATABASE=Quickflex, Inc + +acpi:QLC*: + ID_VENDOR_FROM_DATABASE=Q-Logic + +acpi:QQQ*: + ID_VENDOR_FROM_DATABASE=Chuomusen Co., Ltd. + +acpi:QSI*: + ID_VENDOR_FROM_DATABASE=Quantum Solutions, Inc. + +acpi:QTD*: + ID_VENDOR_FROM_DATABASE=Quantum 3D Inc + +acpi:QTH*: + ID_VENDOR_FROM_DATABASE=Questech Ltd + +acpi:QTI*: + ID_VENDOR_FROM_DATABASE=Quicknet Technologies Inc + +acpi:QTM*: + ID_VENDOR_FROM_DATABASE=Quantum + +acpi:QTR*: + ID_VENDOR_FROM_DATABASE=Qtronix Corporation + +acpi:QUA*: + ID_VENDOR_FROM_DATABASE=Quatographic AG + +acpi:QUE*: + ID_VENDOR_FROM_DATABASE=Questra Consulting + +acpi:QVU*: + ID_VENDOR_FROM_DATABASE=Quartics + +acpi:RAC*: + ID_VENDOR_FROM_DATABASE=Racore Computer Products Inc + +acpi:RAD*: + ID_VENDOR_FROM_DATABASE=Radisys Corporation + +acpi:RAI*: + ID_VENDOR_FROM_DATABASE=Rockwell Automation/Intecolor + +acpi:RAN*: + ID_VENDOR_FROM_DATABASE=Rancho Tech Inc + +acpi:RAR*: + ID_VENDOR_FROM_DATABASE=Raritan, Inc. + +acpi:RAS*: + ID_VENDOR_FROM_DATABASE=RAScom Inc + +acpi:RAT*: + ID_VENDOR_FROM_DATABASE=Rent-A-Tech + +acpi:RAY*: + ID_VENDOR_FROM_DATABASE=Raylar Design, Inc. + +acpi:RCE*: + ID_VENDOR_FROM_DATABASE=Parc d'Activite des Bellevues + +acpi:RCH*: + ID_VENDOR_FROM_DATABASE=Reach Technology Inc + +acpi:RCI*: + ID_VENDOR_FROM_DATABASE=RC International + +acpi:RCN*: + ID_VENDOR_FROM_DATABASE=Radio Consult SRL + +acpi:RCO*: + ID_VENDOR_FROM_DATABASE=Rockwell Collins + +acpi:RDI*: + ID_VENDOR_FROM_DATABASE=Rainbow Displays, Inc. + +acpi:RDM*: + ID_VENDOR_FROM_DATABASE=Tremon Enterprises Company Ltd + +acpi:RDN*: + ID_VENDOR_FROM_DATABASE=RADIODATA GmbH + +acpi:RDS*: + ID_VENDOR_FROM_DATABASE=Radius Inc + +acpi:REA*: + ID_VENDOR_FROM_DATABASE=Real D + +acpi:REC*: + ID_VENDOR_FROM_DATABASE=ReCom + +acpi:RED*: + ID_VENDOR_FROM_DATABASE=Research Electronics Development Inc + +acpi:REF*: + ID_VENDOR_FROM_DATABASE=Reflectivity, Inc. + +acpi:REH*: + ID_VENDOR_FROM_DATABASE=Rehan Electronics Ltd. + +acpi:REL*: + ID_VENDOR_FROM_DATABASE=Reliance Electric Ind Corporation + +acpi:REM*: + ID_VENDOR_FROM_DATABASE=SCI Systems Inc. + +acpi:REN*: + ID_VENDOR_FROM_DATABASE=Renesas Technology Corp. + +acpi:RES*: + ID_VENDOR_FROM_DATABASE=ResMed Pty Ltd + +acpi:RET*: + ID_VENDOR_FROM_DATABASE=Resonance Technology, Inc. + +acpi:REV*: + ID_VENDOR_FROM_DATABASE=Revolution Display, Inc. + +acpi:REX*: + ID_VENDOR_FROM_DATABASE=RATOC Systems, Inc. + +acpi:RFI*: + ID_VENDOR_FROM_DATABASE=RAFI GmbH & Co. KG + +acpi:RFX*: + ID_VENDOR_FROM_DATABASE=Redfox Technologies Inc. + +acpi:RGB*: + ID_VENDOR_FROM_DATABASE=RGB Spectrum + +acpi:RGL*: + ID_VENDOR_FROM_DATABASE=Robertson Geologging Ltd + +acpi:RHD*: + ID_VENDOR_FROM_DATABASE=RightHand Technologies + +acpi:RHM*: + ID_VENDOR_FROM_DATABASE=Rohm Company Ltd + +acpi:RHT*: + ID_VENDOR_FROM_DATABASE=Red Hat, Inc. + +acpi:RIC*: + ID_VENDOR_FROM_DATABASE=RICOH COMPANY, LTD. + +acpi:RII*: + ID_VENDOR_FROM_DATABASE=Racal Interlan Inc + +acpi:RIO*: + ID_VENDOR_FROM_DATABASE=Rios Systems Company Ltd + +acpi:RIT*: + ID_VENDOR_FROM_DATABASE=Ritech Inc + +acpi:RIV*: + ID_VENDOR_FROM_DATABASE=Rivulet Communications + +acpi:RJA*: + ID_VENDOR_FROM_DATABASE=Roland Corporation + +acpi:RJS*: + ID_VENDOR_FROM_DATABASE=Advanced Engineering + +acpi:RKC*: + ID_VENDOR_FROM_DATABASE=Reakin Technolohy Corporation + +acpi:RLD*: + ID_VENDOR_FROM_DATABASE=MEPCO + +acpi:RLN*: + ID_VENDOR_FROM_DATABASE=RadioLAN Inc + +acpi:RMC*: + ID_VENDOR_FROM_DATABASE=Raritan Computer, Inc + +acpi:RMP*: + ID_VENDOR_FROM_DATABASE=Research Machines + +acpi:RMS*: + ID_VENDOR_FROM_DATABASE=Shenzhen Ramos Digital Technology Co., Ltd + +acpi:RMT*: + ID_VENDOR_FROM_DATABASE=Roper Mobile + +acpi:RNB*: + ID_VENDOR_FROM_DATABASE=Rainbow Technologies + +acpi:ROB*: + ID_VENDOR_FROM_DATABASE=Robust Electronics GmbH + +acpi:ROH*: + ID_VENDOR_FROM_DATABASE=Rohm Co., Ltd. + +acpi:ROK*: + ID_VENDOR_FROM_DATABASE=Rockwell International + +acpi:ROP*: + ID_VENDOR_FROM_DATABASE=Roper International Ltd + +acpi:ROS*: + ID_VENDOR_FROM_DATABASE=Rohde & Schwarz + +acpi:RPI*: + ID_VENDOR_FROM_DATABASE=RoomPro Technologies + +acpi:RPT*: + ID_VENDOR_FROM_DATABASE=R.P.T.Intergroups + +acpi:RRI*: + ID_VENDOR_FROM_DATABASE=Radicom Research Inc + +acpi:RSC*: + ID_VENDOR_FROM_DATABASE=PhotoTelesis + +acpi:RSH*: + ID_VENDOR_FROM_DATABASE=ADC-Centre + +acpi:RSI*: + ID_VENDOR_FROM_DATABASE=Rampage Systems Inc + +acpi:RSN*: + ID_VENDOR_FROM_DATABASE=Radiospire Networks, Inc. + +acpi:RSQ*: + ID_VENDOR_FROM_DATABASE=R Squared + +acpi:RSR*: + ID_VENDOR_FROM_DATABASE=Zhong Shan City Richsound Electronic Industrial Ltd. + +acpi:RSS*: + ID_VENDOR_FROM_DATABASE=Rockwell Semiconductor Systems + +acpi:RSV*: + ID_VENDOR_FROM_DATABASE=Ross Video Ltd + +acpi:RSX*: + ID_VENDOR_FROM_DATABASE=Rapid Tech Corporation + +acpi:RTC*: + ID_VENDOR_FROM_DATABASE=Relia Technologies + +acpi:RTI*: + ID_VENDOR_FROM_DATABASE=Rancho Tech Inc + +acpi:RTL*: + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd + +acpi:RTS*: + ID_VENDOR_FROM_DATABASE=Raintree Systems + +acpi:RUN*: + ID_VENDOR_FROM_DATABASE=RUNCO International + +acpi:RUP*: + ID_VENDOR_FROM_DATABASE=Ups Manufactoring s.r.l. + +acpi:RVC*: + ID_VENDOR_FROM_DATABASE=RSI Systems Inc + +acpi:RVI*: + ID_VENDOR_FROM_DATABASE=Realvision Inc + +acpi:RVL*: + ID_VENDOR_FROM_DATABASE=Reveal Computer Prod + +acpi:RWC*: + ID_VENDOR_FROM_DATABASE=Red Wing Corporation + +acpi:RXT*: + ID_VENDOR_FROM_DATABASE=Tectona SoftSolutions (P) Ltd., + +acpi:RZS*: + ID_VENDOR_FROM_DATABASE=Rozsnyó, s.r.o. + +acpi:SAA*: + ID_VENDOR_FROM_DATABASE=Sanritz Automation Co.,Ltd. + +acpi:SAE*: + ID_VENDOR_FROM_DATABASE=Saab Aerotech + +acpi:SAG*: + ID_VENDOR_FROM_DATABASE=Sedlbauer + +acpi:SAI*: + ID_VENDOR_FROM_DATABASE=Sage Inc + +acpi:SAK*: + ID_VENDOR_FROM_DATABASE=Saitek Ltd + +acpi:SAM*: + ID_VENDOR_FROM_DATABASE=Samsung Electric Company + +acpi:SAN*: + ID_VENDOR_FROM_DATABASE=Sanyo Electric Co.,Ltd. + +acpi:SAS*: + ID_VENDOR_FROM_DATABASE=Stores Automated Systems Inc + +acpi:SAT*: + ID_VENDOR_FROM_DATABASE=Shuttle Tech + +acpi:SBC*: + ID_VENDOR_FROM_DATABASE=Shanghai Bell Telephone Equip Mfg Co + +acpi:SBD*: + ID_VENDOR_FROM_DATABASE=Softbed - Consulting & Development Ltd + +acpi:SBI*: + ID_VENDOR_FROM_DATABASE=SMART Technologies Inc. + +acpi:SBS*: + ID_VENDOR_FROM_DATABASE=SBS-or Industrial Computers GmbH + +acpi:SBT*: + ID_VENDOR_FROM_DATABASE=Senseboard Technologies AB + +acpi:SCB*: + ID_VENDOR_FROM_DATABASE=SeeCubic B.V. + +acpi:SCC*: + ID_VENDOR_FROM_DATABASE=SORD Computer Corporation + +acpi:SCD*: + ID_VENDOR_FROM_DATABASE=Sanyo Electric Company Ltd + +acpi:SCE*: + ID_VENDOR_FROM_DATABASE=Sun Corporation + +acpi:SCH*: + ID_VENDOR_FROM_DATABASE=Schlumberger Cards + +acpi:SCI*: + ID_VENDOR_FROM_DATABASE=System Craft + +acpi:SCL*: + ID_VENDOR_FROM_DATABASE=Sigmacom Co., Ltd. + +acpi:SCM*: + ID_VENDOR_FROM_DATABASE=SCM Microsystems Inc + +acpi:SCN*: + ID_VENDOR_FROM_DATABASE=Scanport, Inc. + +acpi:SCO*: + ID_VENDOR_FROM_DATABASE=SORCUS Computer GmbH + +acpi:SCP*: + ID_VENDOR_FROM_DATABASE=Scriptel Corporation + +acpi:SCR*: + ID_VENDOR_FROM_DATABASE=Systran Corporation + +acpi:SCS*: + ID_VENDOR_FROM_DATABASE=Nanomach Anstalt + +acpi:SCT*: + ID_VENDOR_FROM_DATABASE=Smart Card Technology + +acpi:SCX*: + ID_VENDOR_FROM_DATABASE=Socionext Inc. + +acpi:SDA*: + ID_VENDOR_FROM_DATABASE=SAT (Societe Anonyme) + +acpi:SDD*: + ID_VENDOR_FROM_DATABASE=Intrada-SDD Ltd + +acpi:SDE*: + ID_VENDOR_FROM_DATABASE=Sherwood Digital Electronics Corporation + +acpi:SDF*: + ID_VENDOR_FROM_DATABASE=SODIFF E&T CO., Ltd. + +acpi:SDH*: + ID_VENDOR_FROM_DATABASE=Communications Specialies, Inc. + +acpi:SDI*: + ID_VENDOR_FROM_DATABASE=Samtron Displays Inc + +acpi:SDK*: + ID_VENDOR_FROM_DATABASE=SAIT-Devlonics + +acpi:SDR*: + ID_VENDOR_FROM_DATABASE=SDR Systems + +acpi:SDS*: + ID_VENDOR_FROM_DATABASE=SunRiver Data System + +acpi:SDT*: + ID_VENDOR_FROM_DATABASE=Siemens AG + +acpi:SDX*: + ID_VENDOR_FROM_DATABASE=SDX Business Systems Ltd + +acpi:SEA*: + ID_VENDOR_FROM_DATABASE=Seanix Technology Inc. + +acpi:SEB*: + ID_VENDOR_FROM_DATABASE=system elektronik GmbH + +acpi:SEC*: + ID_VENDOR_FROM_DATABASE=Seiko Epson Corporation + +acpi:SEE*: + ID_VENDOR_FROM_DATABASE=SeeColor Corporation + +acpi:SEI*: + ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc + +acpi:SEL*: + ID_VENDOR_FROM_DATABASE=Way2Call Communications + +acpi:SEM*: + ID_VENDOR_FROM_DATABASE=Samsung Electronics Company Ltd + +acpi:SEN*: + ID_VENDOR_FROM_DATABASE=Sencore + +acpi:SEO*: + ID_VENDOR_FROM_DATABASE=SEOS Ltd + +acpi:SEP*: + ID_VENDOR_FROM_DATABASE=SEP Eletronica Ltda. + +acpi:SER*: + ID_VENDOR_FROM_DATABASE=Sony Ericsson Mobile Communications Inc. + +acpi:SES*: + ID_VENDOR_FROM_DATABASE=Session Control LLC + +acpi:SET*: + ID_VENDOR_FROM_DATABASE=SendTek Corporation + +acpi:SFM*: + ID_VENDOR_FROM_DATABASE=TORNADO Company + +acpi:SFT*: + ID_VENDOR_FROM_DATABASE=Mikroforum Ring 3 + +acpi:SGC*: + ID_VENDOR_FROM_DATABASE=Spectragraphics Corporation + +acpi:SGD*: + ID_VENDOR_FROM_DATABASE=Sigma Designs, Inc. + +acpi:SGE*: + ID_VENDOR_FROM_DATABASE=Kansai Electric Company Ltd + +acpi:SGI*: + ID_VENDOR_FROM_DATABASE=Scan Group Ltd + +acpi:SGL*: + ID_VENDOR_FROM_DATABASE=Super Gate Technology Company Ltd + +acpi:SGM*: + ID_VENDOR_FROM_DATABASE=SAGEM + +acpi:SGO*: + ID_VENDOR_FROM_DATABASE=Logos Design A/S + +acpi:SGT*: + ID_VENDOR_FROM_DATABASE=Stargate Technology + +acpi:SGW*: + ID_VENDOR_FROM_DATABASE=Shanghai Guowei Science and Technology Co., Ltd. + +acpi:SGX*: + ID_VENDOR_FROM_DATABASE=Silicon Graphics Inc + +acpi:SGZ*: + ID_VENDOR_FROM_DATABASE=Systec Computer GmbH + +acpi:SHC*: + ID_VENDOR_FROM_DATABASE=ShibaSoku Co., Ltd. + +acpi:SHG*: + ID_VENDOR_FROM_DATABASE=Soft & Hardware development Goldammer GmbH + +acpi:SHI*: + ID_VENDOR_FROM_DATABASE=Jiangsu Shinco Electronic Group Co., Ltd + +acpi:SHP*: + ID_VENDOR_FROM_DATABASE=Sharp Corporation + +acpi:SHR*: + ID_VENDOR_FROM_DATABASE=Digital Discovery + +acpi:SHT*: + ID_VENDOR_FROM_DATABASE=Shin Ho Tech + +acpi:SIA*: + ID_VENDOR_FROM_DATABASE=SIEMENS AG + +acpi:SIB*: + ID_VENDOR_FROM_DATABASE=Sanyo Electric Company Ltd + +acpi:SIC*: + ID_VENDOR_FROM_DATABASE=Sysmate Corporation + +acpi:SID*: + ID_VENDOR_FROM_DATABASE=Seiko Instruments Information Devices Inc + +acpi:SIE*: + ID_VENDOR_FROM_DATABASE=Siemens + +acpi:SIG*: + ID_VENDOR_FROM_DATABASE=Sigma Designs Inc + +acpi:SII*: + ID_VENDOR_FROM_DATABASE=Silicon Image, Inc. + +acpi:SIL*: + ID_VENDOR_FROM_DATABASE=Silicon Laboratories, Inc + +acpi:SIM*: + ID_VENDOR_FROM_DATABASE=S3 Inc + +acpi:SIN*: + ID_VENDOR_FROM_DATABASE=Singular Technology Co., Ltd. + +acpi:SIR*: + ID_VENDOR_FROM_DATABASE=Sirius Technologies Pty Ltd + +acpi:SIS*: + ID_VENDOR_FROM_DATABASE=Silicon Integrated Systems Corporation + +acpi:SIT*: + ID_VENDOR_FROM_DATABASE=Sitintel + +acpi:SIU*: + ID_VENDOR_FROM_DATABASE=Seiko Instruments USA Inc + +acpi:SIX*: + ID_VENDOR_FROM_DATABASE=Zuniq Data Corporation + +acpi:SJE*: + ID_VENDOR_FROM_DATABASE=Sejin Electron Inc + +acpi:SKD*: + ID_VENDOR_FROM_DATABASE=Schneider & Koch + +acpi:SKM*: + ID_VENDOR_FROM_DATABASE=Guangzhou Teclast Information Technology Limited + +acpi:SKT*: + ID_VENDOR_FROM_DATABASE=Samsung Electro-Mechanics Company Ltd + +acpi:SKY*: + ID_VENDOR_FROM_DATABASE=SKYDATA S.P.A. + +acpi:SLA*: + ID_VENDOR_FROM_DATABASE=Systeme Lauer GmbH&Co KG + +acpi:SLB*: + ID_VENDOR_FROM_DATABASE=Shlumberger Ltd + +acpi:SLC*: + ID_VENDOR_FROM_DATABASE=Syslogic Datentechnik AG + +acpi:SLF*: + ID_VENDOR_FROM_DATABASE=StarLeaf + +acpi:SLH*: + ID_VENDOR_FROM_DATABASE=Silicon Library Inc. + +acpi:SLI*: + ID_VENDOR_FROM_DATABASE=Symbios Logic Inc + +acpi:SLK*: + ID_VENDOR_FROM_DATABASE=Silitek Corporation + +acpi:SLM*: + ID_VENDOR_FROM_DATABASE=Solomon Technology Corporation + +acpi:SLR*: + ID_VENDOR_FROM_DATABASE=Schlumberger Technology Corporate + +acpi:SLS*: + ID_VENDOR_FROM_DATABASE=Schnick-Schnack-Systems GmbH + +acpi:SLT*: + ID_VENDOR_FROM_DATABASE=Salt Internatioinal Corp. + +acpi:SLX*: + ID_VENDOR_FROM_DATABASE=Specialix + +acpi:SMA*: + ID_VENDOR_FROM_DATABASE=SMART Modular Technologies + +acpi:SMB*: + ID_VENDOR_FROM_DATABASE=Schlumberger + +acpi:SMC*: + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corporation + +acpi:SME*: + ID_VENDOR_FROM_DATABASE=Sysmate Company + +acpi:SMI*: + ID_VENDOR_FROM_DATABASE=SpaceLabs Medical Inc + +acpi:SMK*: + ID_VENDOR_FROM_DATABASE=SMK CORPORATION + +acpi:SML*: + ID_VENDOR_FROM_DATABASE=Sumitomo Metal Industries, Ltd. + +acpi:SMM*: + ID_VENDOR_FROM_DATABASE=Shark Multimedia Inc + +acpi:SMO*: + ID_VENDOR_FROM_DATABASE=STMicroelectronics + +acpi:SMP*: + ID_VENDOR_FROM_DATABASE=Simple Computing + +acpi:SMR*: + ID_VENDOR_FROM_DATABASE=B.& V. s.r.l. + +acpi:SMS*: + ID_VENDOR_FROM_DATABASE=Silicom Multimedia Systems Inc + +acpi:SMT*: + ID_VENDOR_FROM_DATABASE=Silcom Manufacturing Tech Inc + +acpi:SNC*: + ID_VENDOR_FROM_DATABASE=Sentronic International Corp. + +acpi:SNI*: + ID_VENDOR_FROM_DATABASE=Siemens Microdesign GmbH + +acpi:SNK*: + ID_VENDOR_FROM_DATABASE=S&K Electronics + +acpi:SNN*: + ID_VENDOR_FROM_DATABASE=SUNNY ELEKTRONIK + +acpi:SNO*: + ID_VENDOR_FROM_DATABASE=SINOSUN TECHNOLOGY CO., LTD + +acpi:SNP*: + ID_VENDOR_FROM_DATABASE=Siemens Nixdorf Info Systems + +acpi:SNS*: + ID_VENDOR_FROM_DATABASE=Cirtech (UK) Ltd + +acpi:SNT*: + ID_VENDOR_FROM_DATABASE=SuperNet Inc + +acpi:SNW*: + ID_VENDOR_FROM_DATABASE=Snell & Wilcox + +acpi:SNX*: + ID_VENDOR_FROM_DATABASE=Sonix Comm. Ltd + +acpi:SNY*: + ID_VENDOR_FROM_DATABASE=Sony + +acpi:SOC*: + ID_VENDOR_FROM_DATABASE=Santec Corporation + +acpi:SOI*: + ID_VENDOR_FROM_DATABASE=Silicon Optix Corporation + +acpi:SOL*: + ID_VENDOR_FROM_DATABASE=Solitron Technologies Inc + +acpi:SON*: + ID_VENDOR_FROM_DATABASE=Sony + +acpi:SOR*: + ID_VENDOR_FROM_DATABASE=Sorcus Computer GmbH + +acpi:SOT*: + ID_VENDOR_FROM_DATABASE=Sotec Company Ltd + +acpi:SOY*: + ID_VENDOR_FROM_DATABASE=SOYO Group, Inc + +acpi:SPC*: + ID_VENDOR_FROM_DATABASE=SpinCore Technologies, Inc + +acpi:SPE*: + ID_VENDOR_FROM_DATABASE=SPEA Software AG + +acpi:SPH*: + ID_VENDOR_FROM_DATABASE=G&W Instruments GmbH + +acpi:SPI*: + ID_VENDOR_FROM_DATABASE=SPACE-I Co., Ltd. + +acpi:SPK*: + ID_VENDOR_FROM_DATABASE=SpeakerCraft + +acpi:SPL*: + ID_VENDOR_FROM_DATABASE=Smart Silicon Systems Pty Ltd + +acpi:SPN*: + ID_VENDOR_FROM_DATABASE=Sapience Corporation + +acpi:SPR*: + ID_VENDOR_FROM_DATABASE=pmns GmbH + +acpi:SPS*: + ID_VENDOR_FROM_DATABASE=Synopsys Inc + +acpi:SPT*: + ID_VENDOR_FROM_DATABASE=Sceptre Tech Inc + +acpi:SPU*: + ID_VENDOR_FROM_DATABASE=SIM2 Multimedia S.P.A. + +acpi:SPX*: + ID_VENDOR_FROM_DATABASE=Simplex Time Recorder Co. + +acpi:SQT*: + ID_VENDOR_FROM_DATABASE=Sequent Computer Systems Inc + +acpi:SRC*: + ID_VENDOR_FROM_DATABASE=Integrated Tech Express Inc + +acpi:SRD*: + ID_VENDOR_FROM_DATABASE=Setred + +acpi:SRF*: + ID_VENDOR_FROM_DATABASE=Surf Communication Solutions Ltd + +acpi:SRG*: + ID_VENDOR_FROM_DATABASE=Intuitive Surgical, Inc. + +acpi:SRS*: + ID_VENDOR_FROM_DATABASE=SR-Systems e.K. + +acpi:SRT*: + ID_VENDOR_FROM_DATABASE=SeeReal Technologies GmbH + +acpi:SSC*: + ID_VENDOR_FROM_DATABASE=Sierra Semiconductor Inc + +acpi:SSD*: + ID_VENDOR_FROM_DATABASE=FlightSafety International + +acpi:SSE*: + ID_VENDOR_FROM_DATABASE=Samsung Electronic Co. + +acpi:SSI*: + ID_VENDOR_FROM_DATABASE=S-S Technology Inc + +acpi:SSJ*: + ID_VENDOR_FROM_DATABASE=Sankyo Seiki Mfg.co., Ltd + +acpi:SSL*: + ID_VENDOR_FROM_DATABASE=Shenzhen South-Top Computer Co., Ltd. + +acpi:SSP*: + ID_VENDOR_FROM_DATABASE=Spectrum Signal Proecessing Inc + +acpi:SSS*: + ID_VENDOR_FROM_DATABASE=S3 Inc + +acpi:SST*: + ID_VENDOR_FROM_DATABASE=SystemSoft Corporation + +acpi:STA*: + ID_VENDOR_FROM_DATABASE=ST Electronics Systems Assembly Pte Ltd + +acpi:STB*: + ID_VENDOR_FROM_DATABASE=STB Systems Inc + +acpi:STC*: + ID_VENDOR_FROM_DATABASE=STAC Electronics + +acpi:STD*: + ID_VENDOR_FROM_DATABASE=STD Computer Inc + +acpi:STE*: + ID_VENDOR_FROM_DATABASE=SII Ido-Tsushin Inc + +acpi:STF*: + ID_VENDOR_FROM_DATABASE=Starflight Electronics + +acpi:STG*: + ID_VENDOR_FROM_DATABASE=StereoGraphics Corp. + +acpi:STH*: + ID_VENDOR_FROM_DATABASE=Semtech Corporation + +acpi:STI*: + ID_VENDOR_FROM_DATABASE=Smart Tech Inc + +acpi:STK*: + ID_VENDOR_FROM_DATABASE=SANTAK CORP. + +acpi:STL*: + ID_VENDOR_FROM_DATABASE=SigmaTel Inc + +acpi:STM*: + ID_VENDOR_FROM_DATABASE=SGS Thomson Microelectronics + +acpi:STN*: + ID_VENDOR_FROM_DATABASE=Samsung Electronics America + +acpi:STO*: + ID_VENDOR_FROM_DATABASE=Stollmann E+V GmbH + +acpi:STP*: + ID_VENDOR_FROM_DATABASE=StreamPlay Ltd + +acpi:STQ*: + ID_VENDOR_FROM_DATABASE=Synthetel Corporation + +acpi:STR*: + ID_VENDOR_FROM_DATABASE=Starlight Networks Inc + +acpi:STS*: + ID_VENDOR_FROM_DATABASE=SITECSYSTEM CO., LTD. + +acpi:STT*: + ID_VENDOR_FROM_DATABASE=Star Paging Telecom Tech (Shenzhen) Co. Ltd. + +acpi:STU*: + ID_VENDOR_FROM_DATABASE=Sentelic Corporation + +acpi:STW*: + ID_VENDOR_FROM_DATABASE=Starwin Inc. + +acpi:STX*: + ID_VENDOR_FROM_DATABASE=ST-Ericsson + +acpi:STY*: + ID_VENDOR_FROM_DATABASE=SDS Technologies + +acpi:SUB*: + ID_VENDOR_FROM_DATABASE=Subspace Comm. Inc + +acpi:SUM*: + ID_VENDOR_FROM_DATABASE=Summagraphics Corporation + +acpi:SUN*: + ID_VENDOR_FROM_DATABASE=Sun Electronics Corporation + +acpi:SUP*: + ID_VENDOR_FROM_DATABASE=Supra Corporation + +acpi:SUR*: + ID_VENDOR_FROM_DATABASE=Surenam Computer Corporation + +acpi:SVA*: + ID_VENDOR_FROM_DATABASE=SGEG + +acpi:SVC*: + ID_VENDOR_FROM_DATABASE=Intellix Corp. + +acpi:SVD*: + ID_VENDOR_FROM_DATABASE=SVD Computer + +acpi:SVE*: + ID_VENDOR_FROM_DATABASE=SVEC + +acpi:SVI*: + ID_VENDOR_FROM_DATABASE=Sun Microsystems + +acpi:SVR*: + ID_VENDOR_FROM_DATABASE=Sensics, Inc. + +acpi:SVS*: + ID_VENDOR_FROM_DATABASE=SVSI + +acpi:SVT*: + ID_VENDOR_FROM_DATABASE=SEVIT Co., Ltd. + +acpi:SWC*: + ID_VENDOR_FROM_DATABASE=Software Café + +acpi:SWI*: + ID_VENDOR_FROM_DATABASE=Sierra Wireless Inc. + +acpi:SWL*: + ID_VENDOR_FROM_DATABASE=Sharedware Ltd + +acpi:SWO*: + ID_VENDOR_FROM_DATABASE=Guangzhou Shirui Electronics Co., Ltd. + +acpi:SWS*: + ID_VENDOR_FROM_DATABASE=Static + +acpi:SWT*: + ID_VENDOR_FROM_DATABASE=Software Technologies Group,Inc. + +acpi:SXB*: + ID_VENDOR_FROM_DATABASE=Syntax-Brillian + +acpi:SXD*: + ID_VENDOR_FROM_DATABASE=Silex technology, Inc. + +acpi:SXG*: + ID_VENDOR_FROM_DATABASE=SELEX GALILEO + +acpi:SXL*: + ID_VENDOR_FROM_DATABASE=SolutionInside + +acpi:SXT*: + ID_VENDOR_FROM_DATABASE=SHARP TAKAYA ELECTRONIC INDUSTRY CO.,LTD. + +acpi:SYC*: + ID_VENDOR_FROM_DATABASE=Sysmic + +acpi:SYE*: + ID_VENDOR_FROM_DATABASE=SY Electronics Ltd + +acpi:SYK*: + ID_VENDOR_FROM_DATABASE=Stryker Communications + +acpi:SYL*: + ID_VENDOR_FROM_DATABASE=Sylvania Computer Products + +acpi:SYM*: + ID_VENDOR_FROM_DATABASE=Symicron Computer Communications Ltd. + +acpi:SYN*: + ID_VENDOR_FROM_DATABASE=Synaptics Inc + +acpi:SYP*: + ID_VENDOR_FROM_DATABASE=SYPRO Co Ltd + +acpi:SYS*: + ID_VENDOR_FROM_DATABASE=Sysgration Ltd + +acpi:SYT*: + ID_VENDOR_FROM_DATABASE=Seyeon Tech Company Ltd + +acpi:SYV*: + ID_VENDOR_FROM_DATABASE=SYVAX Inc + +acpi:SYX*: + ID_VENDOR_FROM_DATABASE=Prime Systems, Inc. + +acpi:SZM*: + ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd + +acpi:SZV*: + ID_VENDOR_FROM_DATABASE=OvisLink + +acpi:TAA*: + ID_VENDOR_FROM_DATABASE=Tandberg + +acpi:TAB*: + ID_VENDOR_FROM_DATABASE=Todos Data System AB + +acpi:TAG*: + ID_VENDOR_FROM_DATABASE=Teles AG + +acpi:TAI*: + ID_VENDOR_FROM_DATABASE=Toshiba America Info Systems Inc + +acpi:TAM*: + ID_VENDOR_FROM_DATABASE=Tamura Seisakusyo Ltd + +acpi:TAS*: + ID_VENDOR_FROM_DATABASE=Taskit Rechnertechnik GmbH + +acpi:TAT*: + ID_VENDOR_FROM_DATABASE=Teleliaison Inc + +acpi:TAV*: + ID_VENDOR_FROM_DATABASE=Thales Avionics + +acpi:TAX*: + ID_VENDOR_FROM_DATABASE=Taxan (Europe) Ltd + +acpi:TBB*: + ID_VENDOR_FROM_DATABASE=Triple S Engineering Inc + +acpi:TBC*: + ID_VENDOR_FROM_DATABASE=Turbo Communication, Inc + +acpi:TBS*: + ID_VENDOR_FROM_DATABASE=Turtle Beach System + +acpi:TCC*: + ID_VENDOR_FROM_DATABASE=Tandon Corporation + +acpi:TCD*: + ID_VENDOR_FROM_DATABASE=Taicom Data Systems Co., Ltd. + +acpi:TCE*: + ID_VENDOR_FROM_DATABASE=Century Corporation + +acpi:TCH*: + ID_VENDOR_FROM_DATABASE=Interaction Systems, Inc + +acpi:TCI*: + ID_VENDOR_FROM_DATABASE=Tulip Computers Int'l B.V. + +acpi:TCJ*: + ID_VENDOR_FROM_DATABASE=TEAC America Inc + +acpi:TCL*: + ID_VENDOR_FROM_DATABASE=Technical Concepts Ltd + +acpi:TCM*: + ID_VENDOR_FROM_DATABASE=3Com Corporation + +acpi:TCN*: + ID_VENDOR_FROM_DATABASE=Tecnetics (PTY) Ltd + +acpi:TCO*: + ID_VENDOR_FROM_DATABASE=Thomas-Conrad Corporation + +acpi:TCR*: + ID_VENDOR_FROM_DATABASE=Thomson Consumer Electronics + +acpi:TCS*: + ID_VENDOR_FROM_DATABASE=Tatung Company of America Inc + +acpi:TCT*: + ID_VENDOR_FROM_DATABASE=Telecom Technology Centre Co. Ltd. + +acpi:TCX*: + ID_VENDOR_FROM_DATABASE=FREEMARS Heavy Industries + +acpi:TDC*: + ID_VENDOR_FROM_DATABASE=Teradici + +acpi:TDD*: + ID_VENDOR_FROM_DATABASE=Tandberg Data Display AS + +acpi:TDK*: + ID_VENDOR_FROM_DATABASE=TDK USA Corporation + +acpi:TDM*: + ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc + +acpi:TDP*: + ID_VENDOR_FROM_DATABASE=3D Perception + +acpi:TDS*: + ID_VENDOR_FROM_DATABASE=Tri-Data Systems Inc + +acpi:TDT*: + ID_VENDOR_FROM_DATABASE=TDT + +acpi:TDV*: + ID_VENDOR_FROM_DATABASE=TDVision Systems, Inc. + +acpi:TDY*: + ID_VENDOR_FROM_DATABASE=Tandy Electronics + +acpi:TEA*: + ID_VENDOR_FROM_DATABASE=TEAC System Corporation + +acpi:TEC*: + ID_VENDOR_FROM_DATABASE=Tecmar Inc + +acpi:TEK*: + ID_VENDOR_FROM_DATABASE=Tektronix Inc + +acpi:TEL*: + ID_VENDOR_FROM_DATABASE=Promotion and Display Technology Ltd. + +acpi:TER*: + ID_VENDOR_FROM_DATABASE=TerraTec Electronic GmbH + +acpi:TET*: + ID_VENDOR_FROM_DATABASE=TETRADYNE CO., LTD. + +acpi:TEX*: + ID_VENDOR_FROM_DATABASE=Texas Instruments + +acpi:TEZ*: + ID_VENDOR_FROM_DATABASE=Tech Source Inc. + +acpi:TGC*: + ID_VENDOR_FROM_DATABASE=Toshiba Global Commerce Solutions, Inc. + +acpi:TGI*: + ID_VENDOR_FROM_DATABASE=TriGem Computer Inc + +acpi:TGM*: + ID_VENDOR_FROM_DATABASE=TriGem Computer,Inc. + +acpi:TGS*: + ID_VENDOR_FROM_DATABASE=Torus Systems Ltd + +acpi:TGV*: + ID_VENDOR_FROM_DATABASE=Grass Valley Germany GmbH + +acpi:THN*: + ID_VENDOR_FROM_DATABASE=Thundercom Holdings Sdn. Bhd. + +acpi:TIC*: + ID_VENDOR_FROM_DATABASE=Trigem KinfoComm + +acpi:TIL*: + ID_VENDOR_FROM_DATABASE=Technical Illusions Inc. + +acpi:TIP*: + ID_VENDOR_FROM_DATABASE=TIPTEL AG + +acpi:TIV*: + ID_VENDOR_FROM_DATABASE=OOO Technoinvest + +acpi:TIX*: + ID_VENDOR_FROM_DATABASE=Tixi.Com GmbH + +acpi:TKC*: + ID_VENDOR_FROM_DATABASE=Taiko Electric Works.LTD + +acpi:TKG*: + ID_VENDOR_FROM_DATABASE=Tek Gear + +acpi:TKN*: + ID_VENDOR_FROM_DATABASE=Teknor Microsystem Inc + +acpi:TKO*: + ID_VENDOR_FROM_DATABASE=TouchKo, Inc. + +acpi:TKS*: + ID_VENDOR_FROM_DATABASE=TimeKeeping Systems, Inc. + +acpi:TLA*: + ID_VENDOR_FROM_DATABASE=Ferrari Electronic GmbH + +acpi:TLD*: + ID_VENDOR_FROM_DATABASE=Telindus + +acpi:TLE*: + ID_VENDOR_FROM_DATABASE=Zhejiang Tianle Digital Electric Co., Ltd. + +acpi:TLF*: + ID_VENDOR_FROM_DATABASE=Teleforce.,co,ltd + +acpi:TLI*: + ID_VENDOR_FROM_DATABASE=TOSHIBA TELI CORPORATION + +acpi:TLK*: + ID_VENDOR_FROM_DATABASE=Telelink AG + +acpi:TLL*: + ID_VENDOR_FROM_DATABASE=Thinklogical + +acpi:TLS*: + ID_VENDOR_FROM_DATABASE=Teleste Educational OY + +acpi:TLT*: + ID_VENDOR_FROM_DATABASE=Dai Telecom S.p.A. + +acpi:TLV*: + ID_VENDOR_FROM_DATABASE=S3 Inc + +acpi:TLX*: + ID_VENDOR_FROM_DATABASE=Telxon Corporation + +acpi:TMC*: + ID_VENDOR_FROM_DATABASE=Techmedia Computer Systems Corporation + +acpi:TME*: + ID_VENDOR_FROM_DATABASE=AT&T Microelectronics + +acpi:TMI*: + ID_VENDOR_FROM_DATABASE=Texas Microsystem + +acpi:TMM*: + ID_VENDOR_FROM_DATABASE=Time Management, Inc. + +acpi:TMR*: + ID_VENDOR_FROM_DATABASE=Taicom International Inc + +acpi:TMS*: + ID_VENDOR_FROM_DATABASE=Trident Microsystems Ltd + +acpi:TMT*: + ID_VENDOR_FROM_DATABASE=T-Metrics Inc. + +acpi:TMX*: + ID_VENDOR_FROM_DATABASE=Thermotrex Corporation + +acpi:TNC*: + ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd + +acpi:TNM*: + ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA + +acpi:TNY*: + ID_VENDOR_FROM_DATABASE=Tennyson Tech Pty Ltd + +acpi:TOE*: + ID_VENDOR_FROM_DATABASE=TOEI Electronics Co., Ltd. + +acpi:TOG*: + ID_VENDOR_FROM_DATABASE=The OPEN Group + +acpi:TOL*: + ID_VENDOR_FROM_DATABASE=TCL Corporation + +acpi:TOM*: + ID_VENDOR_FROM_DATABASE=Ceton Corporation + +acpi:TON*: + ID_VENDOR_FROM_DATABASE=TONNA + +acpi:TOP*: + ID_VENDOR_FROM_DATABASE=Orion Communications Co., Ltd. + +acpi:TOS*: + ID_VENDOR_FROM_DATABASE=Toshiba Corporation + +acpi:TOU*: + ID_VENDOR_FROM_DATABASE=Touchstone Technology + +acpi:TPC*: + ID_VENDOR_FROM_DATABASE=Touch Panel Systems Corporation + +acpi:TPD*: + ID_VENDOR_FROM_DATABASE=Times (Shanghai) Computer Co., Ltd. + +acpi:TPE*: + ID_VENDOR_FROM_DATABASE=Technology Power Enterprises Inc + +acpi:TPJ*: + ID_VENDOR_FROM_DATABASE=Junnila + +acpi:TPK*: + ID_VENDOR_FROM_DATABASE=TOPRE CORPORATION + +acpi:TPR*: + ID_VENDOR_FROM_DATABASE=Topro Technology Inc + +acpi:TPS*: + ID_VENDOR_FROM_DATABASE=Teleprocessing Systeme GmbH + +acpi:TPT*: + ID_VENDOR_FROM_DATABASE=Thruput Ltd + +acpi:TPV*: + ID_VENDOR_FROM_DATABASE=Top Victory Electronics ( Fujian ) Company Ltd + +acpi:TPZ*: + ID_VENDOR_FROM_DATABASE=Ypoaz Systems Inc + +acpi:TRA*: + ID_VENDOR_FROM_DATABASE=TriTech Microelectronics International + +acpi:TRB*: + ID_VENDOR_FROM_DATABASE=Triumph Board a.s. + +acpi:TRC*: + ID_VENDOR_FROM_DATABASE=Trioc AB + +acpi:TRD*: + ID_VENDOR_FROM_DATABASE=Trident Microsystem Inc + +acpi:TRE*: + ID_VENDOR_FROM_DATABASE=Tremetrics + +acpi:TRI*: + ID_VENDOR_FROM_DATABASE=Tricord Systems + +acpi:TRL*: + ID_VENDOR_FROM_DATABASE=Royal Information + +acpi:TRM*: + ID_VENDOR_FROM_DATABASE=Tekram Technology Company Ltd + +acpi:TRN*: + ID_VENDOR_FROM_DATABASE=Datacommunicatie Tron B.V. + +acpi:TRS*: + ID_VENDOR_FROM_DATABASE=Torus Systems Ltd + +acpi:TRT*: + ID_VENDOR_FROM_DATABASE=Tritec Electronic AG + +acpi:TRU*: + ID_VENDOR_FROM_DATABASE=Aashima Technology B.V. + +acpi:TRV*: + ID_VENDOR_FROM_DATABASE=Trivisio Prototyping GmbH + +acpi:TRX*: + ID_VENDOR_FROM_DATABASE=Trex Enterprises + +acpi:TSB*: + ID_VENDOR_FROM_DATABASE=Toshiba America Info Systems Inc + +acpi:TSC*: + ID_VENDOR_FROM_DATABASE=Sanyo Electric Company Ltd + +acpi:TSD*: + ID_VENDOR_FROM_DATABASE=TechniSat Digital GmbH + +acpi:TSE*: + ID_VENDOR_FROM_DATABASE=Tottori Sanyo Electric + +acpi:TSF*: + ID_VENDOR_FROM_DATABASE=Racal-Airtech Software Forge Ltd + +acpi:TSG*: + ID_VENDOR_FROM_DATABASE=The Software Group Ltd + +acpi:TSH*: + ID_VENDOR_FROM_DATABASE=ELAN MICROELECTRONICS CORPORATION + +acpi:TSI*: + ID_VENDOR_FROM_DATABASE=TeleVideo Systems + +acpi:TSL*: + ID_VENDOR_FROM_DATABASE=Tottori SANYO Electric Co., Ltd. + +acpi:TSP*: + ID_VENDOR_FROM_DATABASE=U.S. Navy + +acpi:TST*: + ID_VENDOR_FROM_DATABASE=Transtream Inc + +acpi:TSV*: + ID_VENDOR_FROM_DATABASE=TRANSVIDEO + +acpi:TSY*: + ID_VENDOR_FROM_DATABASE=TouchSystems + +acpi:TTA*: + ID_VENDOR_FROM_DATABASE=Topson Technology Co., Ltd. + +acpi:TTB*: + ID_VENDOR_FROM_DATABASE=National Semiconductor Japan Ltd + +acpi:TTC*: + ID_VENDOR_FROM_DATABASE=Telecommunications Techniques Corporation + +acpi:TTE*: + ID_VENDOR_FROM_DATABASE=TTE, Inc. + +acpi:TTI*: + ID_VENDOR_FROM_DATABASE=Trenton Terminals Inc + +acpi:TTK*: + ID_VENDOR_FROM_DATABASE=Totoku Electric Company Ltd + +acpi:TTL*: + ID_VENDOR_FROM_DATABASE=2-Tel B.V + +acpi:TTP*: + ID_VENDOR_FROM_DATABASE=Toshiba Corporation + +acpi:TTS*: + ID_VENDOR_FROM_DATABASE=TechnoTrend Systemtechnik GmbH + +acpi:TTX*: + ID_VENDOR_FROM_DATABASE=Taitex Corporation + +acpi:TTY*: + ID_VENDOR_FROM_DATABASE=TRIDELITY Display Solutions GmbH + +acpi:TUA*: + ID_VENDOR_FROM_DATABASE=T+A elektroakustik GmbH + +acpi:TUT*: + ID_VENDOR_FROM_DATABASE=Tut Systems + +acpi:TVD*: + ID_VENDOR_FROM_DATABASE=Tecnovision + +acpi:TVI*: + ID_VENDOR_FROM_DATABASE=Truevision + +acpi:TVM*: + ID_VENDOR_FROM_DATABASE=Taiwan Video & Monitor Corporation + +acpi:TVO*: + ID_VENDOR_FROM_DATABASE=TV One Ltd + +acpi:TVR*: + ID_VENDOR_FROM_DATABASE=TV Interactive Corporation + +acpi:TVS*: + ID_VENDOR_FROM_DATABASE=TVS Electronics Limited + +acpi:TVV*: + ID_VENDOR_FROM_DATABASE=TV1 GmbH + +acpi:TWA*: + ID_VENDOR_FROM_DATABASE=Tidewater Association + +acpi:TWE*: + ID_VENDOR_FROM_DATABASE=Kontron Electronik + +acpi:TWH*: + ID_VENDOR_FROM_DATABASE=Twinhead International Corporation + +acpi:TWI*: + ID_VENDOR_FROM_DATABASE=Easytel oy + +acpi:TWK*: + ID_VENDOR_FROM_DATABASE=TOWITOKO electronics GmbH + +acpi:TWX*: + ID_VENDOR_FROM_DATABASE=TEKWorx Limited + +acpi:TXL*: + ID_VENDOR_FROM_DATABASE=Trixel Ltd + +acpi:TXN*: + ID_VENDOR_FROM_DATABASE=Texas Insturments + +acpi:TXT*: + ID_VENDOR_FROM_DATABASE=Textron Defense System + +acpi:TYN*: + ID_VENDOR_FROM_DATABASE=Tyan Computer Corporation + +acpi:UAS*: + ID_VENDOR_FROM_DATABASE=Ultima Associates Pte Ltd + +acpi:UBI*: + ID_VENDOR_FROM_DATABASE=Ungermann-Bass Inc + +acpi:UBL*: + ID_VENDOR_FROM_DATABASE=Ubinetics Ltd. + +acpi:UBU*: + ID_VENDOR_FROM_DATABASE=Canonical Ltd. + +acpi:UDN*: + ID_VENDOR_FROM_DATABASE=Uniden Corporation + +acpi:UEC*: + ID_VENDOR_FROM_DATABASE=Ultima Electronics Corporation + +acpi:UEG*: + ID_VENDOR_FROM_DATABASE=Elitegroup Computer Systems Company Ltd + +acpi:UEI*: + ID_VENDOR_FROM_DATABASE=Universal Electronics Inc + +acpi:UET*: + ID_VENDOR_FROM_DATABASE=Universal Empowering Technologies + +acpi:UFG*: + ID_VENDOR_FROM_DATABASE=UNIGRAF-USA + +acpi:UFO*: + ID_VENDOR_FROM_DATABASE=UFO Systems Inc + +acpi:UHB*: + ID_VENDOR_FROM_DATABASE=XOCECO + +acpi:UIC*: + ID_VENDOR_FROM_DATABASE=Uniform Industrial Corporation + +acpi:UJR*: + ID_VENDOR_FROM_DATABASE=Ueda Japan Radio Co., Ltd. + +acpi:ULT*: + ID_VENDOR_FROM_DATABASE=Ultra Network Tech + +acpi:UMC*: + ID_VENDOR_FROM_DATABASE=United Microelectr Corporation + +acpi:UMG*: + ID_VENDOR_FROM_DATABASE=Umezawa Giken Co.,Ltd + +acpi:UMM*: + ID_VENDOR_FROM_DATABASE=Universal Multimedia + +acpi:UNA*: + ID_VENDOR_FROM_DATABASE=Unisys DSD + +acpi:UNB*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNC*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UND* + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNE* + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNF* + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNI*: + ID_VENDOR_FROM_DATABASE=Uniform Industry Corp. + +acpi:UNM*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNO*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNP*: + ID_VENDOR_FROM_DATABASE=Unitop + +acpi:UNS*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNT*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +acpi:UNY*: + ID_VENDOR_FROM_DATABASE=Unicate + +acpi:UPP*: + ID_VENDOR_FROM_DATABASE=UPPI + +acpi:UPS*: + ID_VENDOR_FROM_DATABASE=Systems Enhancement + +acpi:URD*: + ID_VENDOR_FROM_DATABASE=Video Computer S.p.A. + +acpi:USA*: + ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG + +acpi:USC*: + ID_VENDOR_FROM_DATABASE=UltraStor + +acpi:USD*: + ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation + +acpi:USE*: + ID_VENDOR_FROM_DATABASE=U. S. Electronics Inc. + +acpi:USI*: + ID_VENDOR_FROM_DATABASE=Universal Scientific Industrial Co., Ltd. + +acpi:USR*: + ID_VENDOR_FROM_DATABASE=U.S. Robotics Inc + +acpi:UTD*: + ID_VENDOR_FROM_DATABASE=Up to Date Tech + +acpi:UWC*: + ID_VENDOR_FROM_DATABASE=Uniwill Computer Corp. + +acpi:VAD*: + ID_VENDOR_FROM_DATABASE=Vaddio, LLC + +acpi:VAI*: + ID_VENDOR_FROM_DATABASE=VAIO Corporation + +acpi:VAL*: + ID_VENDOR_FROM_DATABASE=Valence Computing Corporation + +acpi:VAR*: + ID_VENDOR_FROM_DATABASE=Varian Australia Pty Ltd + +acpi:VBR*: + ID_VENDOR_FROM_DATABASE=VBrick Systems Inc. + +acpi:VBT*: + ID_VENDOR_FROM_DATABASE=Valley Board Ltda + +acpi:VCC*: + ID_VENDOR_FROM_DATABASE=Virtual Computer Corporation + +acpi:VCI*: + ID_VENDOR_FROM_DATABASE=VistaCom Inc + +acpi:VCJ*: + ID_VENDOR_FROM_DATABASE=Victor Company of Japan, Limited + +acpi:VCM*: + ID_VENDOR_FROM_DATABASE=Vector Magnetics, LLC + +acpi:VCX*: + ID_VENDOR_FROM_DATABASE=VCONEX + +acpi:VDA*: + ID_VENDOR_FROM_DATABASE=Victor Data Systems + +acpi:VDC*: + ID_VENDOR_FROM_DATABASE=VDC Display Systems + +acpi:VDM*: + ID_VENDOR_FROM_DATABASE=Vadem + +acpi:VDO*: + ID_VENDOR_FROM_DATABASE=Video & Display Oriented Corporation + +acpi:VDS*: + ID_VENDOR_FROM_DATABASE=Vidisys GmbH & Company + +acpi:VDT*: + ID_VENDOR_FROM_DATABASE=Viditec, Inc. + +acpi:VEC*: + ID_VENDOR_FROM_DATABASE=Vector Informatik GmbH + +acpi:VEK*: + ID_VENDOR_FROM_DATABASE=Vektrex + +acpi:VES*: + ID_VENDOR_FROM_DATABASE=Vestel Elektronik Sanayi ve Ticaret A. S. + +acpi:VFI*: + ID_VENDOR_FROM_DATABASE=VeriFone Inc + +acpi:VHI*: + ID_VENDOR_FROM_DATABASE=Macrocad Development Inc. + +acpi:VIA*: + ID_VENDOR_FROM_DATABASE=VIA Tech Inc + +acpi:VIB*: + ID_VENDOR_FROM_DATABASE=Tatung UK Ltd + +acpi:VIC*: + ID_VENDOR_FROM_DATABASE=Victron B.V. + +acpi:VID*: + ID_VENDOR_FROM_DATABASE=Ingram Macrotron Germany + +acpi:VIK*: + ID_VENDOR_FROM_DATABASE=Viking Connectors + +acpi:VIM*: + ID_VENDOR_FROM_DATABASE=Via Mons Ltd. + +acpi:VIN*: + ID_VENDOR_FROM_DATABASE=Vine Micros Ltd + +acpi:VIR*: + ID_VENDOR_FROM_DATABASE=Visual Interface, Inc + +acpi:VIS*: + ID_VENDOR_FROM_DATABASE=Visioneer + +acpi:VIT*: + ID_VENDOR_FROM_DATABASE=Visitech AS + +acpi:VIZ*: + ID_VENDOR_FROM_DATABASE=VIZIO, Inc + +acpi:VLB*: + ID_VENDOR_FROM_DATABASE=ValleyBoard Ltda. + +acpi:VLC*: + ID_VENDOR_FROM_DATABASE=VersaLogic Corporation + +acpi:VLK*: + ID_VENDOR_FROM_DATABASE=Vislink International Ltd + +acpi:VLT*: + ID_VENDOR_FROM_DATABASE=VideoLan Technologies + +acpi:VLV*: + ID_VENDOR_FROM_DATABASE=Valve Corporation + +acpi:VMI*: + ID_VENDOR_FROM_DATABASE=Vermont Microsystems + +acpi:VML*: + ID_VENDOR_FROM_DATABASE=Vine Micros Limited + +acpi:VMW*: + ID_VENDOR_FROM_DATABASE=VMware Inc., + +acpi:VNC*: + ID_VENDOR_FROM_DATABASE=Vinca Corporation + +acpi:VOB*: + ID_VENDOR_FROM_DATABASE=MaxData Computer AG + +acpi:VPI*: + ID_VENDOR_FROM_DATABASE=Video Products Inc + +acpi:VPR*: + ID_VENDOR_FROM_DATABASE=Best Buy + +acpi:VPX*: + ID_VENDOR_FROM_DATABASE=VPixx Technologies Inc. + +acpi:VQ@*: + ID_VENDOR_FROM_DATABASE=Vision Quest + +acpi:VRC*: + ID_VENDOR_FROM_DATABASE=Virtual Resources Corporation + +acpi:VRM*: + ID_VENDOR_FROM_DATABASE=VRmagic Holding AG + +acpi:VSC*: + ID_VENDOR_FROM_DATABASE=ViewSonic Corporation + +acpi:VSD*: + ID_VENDOR_FROM_DATABASE=3M + +acpi:VSI*: + ID_VENDOR_FROM_DATABASE=VideoServer + +acpi:VSN*: + ID_VENDOR_FROM_DATABASE=Ingram Macrotron + +acpi:VSP*: + ID_VENDOR_FROM_DATABASE=Vision Systems GmbH + +acpi:VSR*: + ID_VENDOR_FROM_DATABASE=V-Star Electronics Inc. + +acpi:VTB*: + ID_VENDOR_FROM_DATABASE=Videotechnik Breithaupt + +acpi:VTC*: + ID_VENDOR_FROM_DATABASE=VTel Corporation + +acpi:VTG*: + ID_VENDOR_FROM_DATABASE=Voice Technologies Group Inc + +acpi:VTI*: + ID_VENDOR_FROM_DATABASE=VLSI Tech Inc + +acpi:VTK*: + ID_VENDOR_FROM_DATABASE=Viewteck Co., Ltd. + +acpi:VTL*: + ID_VENDOR_FROM_DATABASE=Vivid Technology Pte Ltd + +acpi:VTM*: + ID_VENDOR_FROM_DATABASE=Miltope Corporation + +acpi:VTN*: + ID_VENDOR_FROM_DATABASE=VIDEOTRON CORP. + +acpi:VTS*: + ID_VENDOR_FROM_DATABASE=VTech Computers Ltd + +acpi:VTV*: + ID_VENDOR_FROM_DATABASE=VATIV Technologies + +acpi:VTX*: + ID_VENDOR_FROM_DATABASE=Vestax Corporation + +acpi:VUT*: + ID_VENDOR_FROM_DATABASE=Vutrix (UK) Ltd + +acpi:VWB*: + ID_VENDOR_FROM_DATABASE=Vweb Corp. + +acpi:WAC*: + ID_VENDOR_FROM_DATABASE=Wacom Tech + +acpi:WAL*: + ID_VENDOR_FROM_DATABASE=Wave Access + +acpi:WAV*: + ID_VENDOR_FROM_DATABASE=Wavephore + +acpi:WBN*: + ID_VENDOR_FROM_DATABASE=MicroSoftWare + +acpi:WBS*: + ID_VENDOR_FROM_DATABASE=WB Systemtechnik GmbH + +acpi:WCI*: + ID_VENDOR_FROM_DATABASE=Wisecom Inc + +acpi:WCS*: + ID_VENDOR_FROM_DATABASE=Woodwind Communications Systems Inc + +acpi:WDC*: + ID_VENDOR_FROM_DATABASE=Western Digital + +acpi:WDE*: + ID_VENDOR_FROM_DATABASE=Westinghouse Digital Electronics + +acpi:WEB*: + ID_VENDOR_FROM_DATABASE=WebGear Inc + +acpi:WEC*: + ID_VENDOR_FROM_DATABASE=Winbond Electronics Corporation + +acpi:WEL*: + ID_VENDOR_FROM_DATABASE=W-DEV + +acpi:WEY*: + ID_VENDOR_FROM_DATABASE=WEY Design AG + +acpi:WHI*: + ID_VENDOR_FROM_DATABASE=Whistle Communications + +acpi:WII*: + ID_VENDOR_FROM_DATABASE=Innoware Inc + +acpi:WIL*: + ID_VENDOR_FROM_DATABASE=WIPRO Information Technology Ltd + +acpi:WIN*: + ID_VENDOR_FROM_DATABASE=Wintop Technology Inc + +acpi:WIP*: + ID_VENDOR_FROM_DATABASE=Wipro Infotech + +acpi:WKH*: + ID_VENDOR_FROM_DATABASE=Uni-Take Int'l Inc. + +acpi:WLD*: + ID_VENDOR_FROM_DATABASE=Wildfire Communications Inc + +acpi:WML*: + ID_VENDOR_FROM_DATABASE=Wolfson Microelectronics Ltd + +acpi:WMO*: + ID_VENDOR_FROM_DATABASE=Westermo Teleindustri AB + +acpi:WMT*: + ID_VENDOR_FROM_DATABASE=Winmate Communication Inc + +acpi:WNI*: + ID_VENDOR_FROM_DATABASE=WillNet Inc. + +acpi:WNV*: + ID_VENDOR_FROM_DATABASE=Winnov L.P. + +acpi:WNX*: + ID_VENDOR_FROM_DATABASE=Wincor Nixdorf International GmbH + +acpi:WPA*: + ID_VENDOR_FROM_DATABASE=Matsushita Communication Industrial Co., Ltd. + +acpi:WPI*: + ID_VENDOR_FROM_DATABASE=Wearnes Peripherals International (Pte) Ltd + +acpi:WRC*: + ID_VENDOR_FROM_DATABASE=WiNRADiO Communications + +acpi:WSC*: + ID_VENDOR_FROM_DATABASE=CIS Technology Inc + +acpi:WSP*: + ID_VENDOR_FROM_DATABASE=Wireless And Smart Products Inc. + +acpi:WST*: + ID_VENDOR_FROM_DATABASE=Wistron Corporation + +acpi:WTC*: + ID_VENDOR_FROM_DATABASE=ACC Microelectronics + +acpi:WTI*: + ID_VENDOR_FROM_DATABASE=WorkStation Tech + +acpi:WTK*: + ID_VENDOR_FROM_DATABASE=Wearnes Thakral Pte + +acpi:WTS*: + ID_VENDOR_FROM_DATABASE=Restek Electric Company Ltd + +acpi:WVM*: + ID_VENDOR_FROM_DATABASE=Wave Systems Corporation + +acpi:WVV*: + ID_VENDOR_FROM_DATABASE=WolfVision GmbH + +acpi:WWP*: + ID_VENDOR_FROM_DATABASE=Wipotec Wiege- und Positioniersysteme GmbH + +acpi:WWV*: + ID_VENDOR_FROM_DATABASE=World Wide Video, Inc. + +acpi:WXT*: + ID_VENDOR_FROM_DATABASE=Woxter Technology Co. Ltd + +acpi:WYS*: + ID_VENDOR_FROM_DATABASE=Wyse Technology + +acpi:WYT*: + ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd. + +acpi:XAC*: + ID_VENDOR_FROM_DATABASE=XAC Automation Corp + +acpi:XAD*: + ID_VENDOR_FROM_DATABASE=Alpha Data + +acpi:XDM*: + ID_VENDOR_FROM_DATABASE=XDM Ltd. + +acpi:XFG*: + ID_VENDOR_FROM_DATABASE=Jan Strapko - FOTO + +acpi:XFO*: + ID_VENDOR_FROM_DATABASE=EXFO Electro Optical Engineering + +acpi:XIN*: + ID_VENDOR_FROM_DATABASE=Xinex Networks Inc + +acpi:XIO*: + ID_VENDOR_FROM_DATABASE=Xiotech Corporation + +acpi:XIR*: + ID_VENDOR_FROM_DATABASE=Xirocm Inc + +acpi:XIT*: + ID_VENDOR_FROM_DATABASE=Xitel Pty ltd + +acpi:XLX*: + ID_VENDOR_FROM_DATABASE=Xilinx, Inc. + +acpi:XMM*: + ID_VENDOR_FROM_DATABASE=C3PO S.L. + +acpi:XNT*: + ID_VENDOR_FROM_DATABASE=XN Technologies, Inc. + +acpi:XQU*: + ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD + +acpi:XRC*: + ID_VENDOR_FROM_DATABASE=Xircom Inc + +acpi:XRO*: + ID_VENDOR_FROM_DATABASE=XORO ELECTRONICS (CHENGDU) LIMITED + +acpi:XSN*: + ID_VENDOR_FROM_DATABASE=Xscreen AS + +acpi:XST*: + ID_VENDOR_FROM_DATABASE=XS Technologies Inc + +acpi:XSY*: + ID_VENDOR_FROM_DATABASE=XSYS + +acpi:XTD*: + ID_VENDOR_FROM_DATABASE=Icuiti Corporation + +acpi:XTE*: + ID_VENDOR_FROM_DATABASE=X2E GmbH + +acpi:XTL*: + ID_VENDOR_FROM_DATABASE=Crystal Computer + +acpi:XTN*: + ID_VENDOR_FROM_DATABASE=X-10 (USA) Inc + +acpi:XYC*: + ID_VENDOR_FROM_DATABASE=Xycotec Computer GmbH + +acpi:XYE*: + ID_VENDOR_FROM_DATABASE=Shenzhen Zhuona Technology Co., Ltd. + +acpi:YED*: + ID_VENDOR_FROM_DATABASE=Y-E Data Inc + +acpi:YHQ*: + ID_VENDOR_FROM_DATABASE=Yokogawa Electric Corporation + +acpi:YHW*: + ID_VENDOR_FROM_DATABASE=Exacom SA + +acpi:YMH*: + ID_VENDOR_FROM_DATABASE=Yamaha Corporation + +acpi:YOW*: + ID_VENDOR_FROM_DATABASE=American Biometric Company + +acpi:ZAN*: + ID_VENDOR_FROM_DATABASE=Zandar Technologies plc + +acpi:ZAX*: + ID_VENDOR_FROM_DATABASE=Zefiro Acoustics + +acpi:ZAZ*: + ID_VENDOR_FROM_DATABASE=Zazzle Technologies + +acpi:ZBR*: + ID_VENDOR_FROM_DATABASE=Zebra Technologies International, LLC + +acpi:ZBX*: + ID_VENDOR_FROM_DATABASE=Zebax Technologies + +acpi:ZCM*: + ID_VENDOR_FROM_DATABASE=Zenith + +acpi:ZCT*: + ID_VENDOR_FROM_DATABASE=ZeitControl cardsystems GmbH + +acpi:ZDS*: + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +acpi:ZEN*: + ID_VENDOR_FROM_DATABASE=ZENIC Inc. + +acpi:ZGT*: + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +acpi:ZIC*: + ID_VENDOR_FROM_DATABASE=Nationz Technologies Inc. + +acpi:ZMC*: + ID_VENDOR_FROM_DATABASE=HangZhou ZMCHIVIN + +acpi:ZMT*: + ID_VENDOR_FROM_DATABASE=Zalman Tech Co., Ltd. + +acpi:ZMZ*: + ID_VENDOR_FROM_DATABASE=Z Microsystems + +acpi:ZNI*: + ID_VENDOR_FROM_DATABASE=Zetinet Inc + +acpi:ZNX*: + ID_VENDOR_FROM_DATABASE=Znyx Adv. Systems + +acpi:ZOW*: + ID_VENDOR_FROM_DATABASE=Zowie Intertainment, Inc + +acpi:ZRN*: + ID_VENDOR_FROM_DATABASE=Zoran Corporation + +acpi:ZSE*: + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +acpi:ZTC*: + ID_VENDOR_FROM_DATABASE=ZyDAS Technology Corporation + +acpi:ZTE*: + ID_VENDOR_FROM_DATABASE=ZTE Corporation + +acpi:ZTI*: + ID_VENDOR_FROM_DATABASE=Zoom Telephonics Inc + +acpi:ZTM*: + ID_VENDOR_FROM_DATABASE=ZT Group Int'l Inc. + +acpi:ZTT*: + ID_VENDOR_FROM_DATABASE=Z3 Technology + +acpi:ZWE*: + ID_VENDOR_FROM_DATABASE=Shenzhen Zowee Technology Co., LTD + +acpi:ZYD*: + ID_VENDOR_FROM_DATABASE=Zydacron Inc + +acpi:ZYP*: + ID_VENDOR_FROM_DATABASE=Zypcom Inc + +acpi:ZYT*: + ID_VENDOR_FROM_DATABASE=Zytex Computers + +acpi:ZYX*: + ID_VENDOR_FROM_DATABASE=Zyxel + +acpi:ZZZ*: + ID_VENDOR_FROM_DATABASE=Boca Research Inc + +acpi:inu*: + ID_VENDOR_FROM_DATABASE=Inovatec S.p.A. diff --git a/src/grp-udev/hwdb/20-acpi-vendor.hwdb.patch b/src/grp-udev/hwdb/20-acpi-vendor.hwdb.patch new file mode 100644 index 0000000000..734dc59422 --- /dev/null +++ b/src/grp-udev/hwdb/20-acpi-vendor.hwdb.patch @@ -0,0 +1,492 @@ +--- 20-acpi-vendor.hwdb.base 2016-06-10 12:40:38.143970821 +0200 ++++ 20-acpi-vendor.hwdb 2016-06-10 12:43:40.557054147 +0200 +@@ -3,6 +3,8 @@ + # Data imported from: + # http://www.uefi.org/uefi-pnp-export + # http://www.uefi.org/uefi-acpi-export ++# ++# With various additions from other sources + + acpi:3NOD*: + ID_VENDOR_FROM_DATABASE=Shenzhen three Connaught Information Technology Co., Ltd. (3nod Group) +@@ -10,9 +12,6 @@ + acpi:AAVA*: + ID_VENDOR_FROM_DATABASE=Aava Mobile Oy + +-acpi:ACPI*: +- ID_VENDOR_FROM_DATABASE=Intel Corporation +- + acpi:AMDI*: + ID_VENDOR_FROM_DATABASE=AMD + +@@ -217,6 +216,9 @@ + acpi:AAA*: + ID_VENDOR_FROM_DATABASE=Avolites Ltd + ++acpi:AAC*: ++ ID_VENDOR_FROM_DATABASE=AcerView ++ + acpi:AAE*: + ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc. + +@@ -241,6 +243,9 @@ + acpi:ABO*: + ID_VENDOR_FROM_DATABASE=D-Link Systems Inc + ++acpi:ABP*: ++ ID_VENDOR_FROM_DATABASE=Advansys ++ + acpi:ABS*: + ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc. + +@@ -286,7 +291,7 @@ + acpi:ACO*: + ID_VENDOR_FROM_DATABASE=Allion Computer Inc. + +-acpi:ACP*: ++acpi:ACP[0-9A-F]*: + ID_VENDOR_FROM_DATABASE=Aspen Tech Inc + + acpi:ACR*: +@@ -556,6 +561,9 @@ + acpi:AMT*: + ID_VENDOR_FROM_DATABASE=AMT International Industry + ++acpi:AMW*: ++ ID_VENDOR_FROM_DATABASE=AMW ++ + acpi:AMX*: + ID_VENDOR_FROM_DATABASE=AMX LLC + +@@ -604,6 +612,9 @@ + acpi:AOA*: + ID_VENDOR_FROM_DATABASE=AOpen Inc. + ++acpi:AOC*: ++ ID_VENDOR_FROM_DATABASE=AOC ++ + acpi:AOE*: + ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc. + +@@ -613,6 +624,9 @@ + acpi:AOT*: + ID_VENDOR_FROM_DATABASE=Alcatel + ++acpi:APA*: ++ ID_VENDOR_FROM_DATABASE=Adaptec ++ + acpi:APC*: + ID_VENDOR_FROM_DATABASE=American Power Conversion + +@@ -788,7 +802,7 @@ + ID_VENDOR_FROM_DATABASE=Alps Electric Inc + + acpi:AUO*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - AUO ++ ID_VENDOR_FROM_DATABASE=AU Optronics + + acpi:AUR*: + ID_VENDOR_FROM_DATABASE=Aureal Semiconductor +@@ -862,6 +876,9 @@ + acpi:AXC*: + ID_VENDOR_FROM_DATABASE=AXIOMTEK CO., LTD. + ++acpi:AXE*: ++ ID_VENDOR_FROM_DATABASE=D-Link Systems Inc ++ + acpi:AXI*: + ID_VENDOR_FROM_DATABASE=American Magnetics + +@@ -1003,6 +1020,9 @@ + acpi:BML*: + ID_VENDOR_FROM_DATABASE=BIOMED Lab + ++acpi:BMM*: ++ ID_VENDOR_FROM_DATABASE=BMM ++ + acpi:BMS*: + ID_VENDOR_FROM_DATABASE=BIOMEDISYS + +@@ -1015,6 +1035,9 @@ + acpi:BNO*: + ID_VENDOR_FROM_DATABASE=Bang & Olufsen + ++acpi:BNQ*: ++ ID_VENDOR_FROM_DATABASE=BenQ Corporation ++ + acpi:BNS*: + ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems + +@@ -1255,6 +1278,9 @@ + acpi:CHA*: + ID_VENDOR_FROM_DATABASE=Chase Research PLC + ++acpi:CHC*: ++ ID_VENDOR_FROM_DATABASE=Chic Technology Corp. ++ + acpi:CHD*: + ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd + +@@ -1402,6 +1428,9 @@ + acpi:COD*: + ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd. + ++acpi:COG*: ++ ID_VENDOR_FROM_DATABASE=Cogent ++ + acpi:COI*: + ID_VENDOR_FROM_DATABASE=Codec Inc. + +@@ -1805,7 +1834,7 @@ + ID_VENDOR_FROM_DATABASE=Dragon Information Technology + + acpi:DJE*: +- ID_VENDOR_FROM_DATABASE=Capstone Visua lProduct Development ++ ID_VENDOR_FROM_DATABASE=Capstone Visual Product Development + + acpi:DJP*: + ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd +@@ -2119,6 +2148,9 @@ + acpi:EIC*: + ID_VENDOR_FROM_DATABASE=Eicon Technology Corporation + ++acpi:EIZ*: ++ ID_VENDOR_FROM_DATABASE=Eizo ++ + acpi:EKA*: + ID_VENDOR_FROM_DATABASE=MagTek Inc. + +@@ -2377,6 +2409,9 @@ + acpi:FCG*: + ID_VENDOR_FROM_DATABASE=First International Computer Ltd + ++acpi:FCM*: ++ ID_VENDOR_FROM_DATABASE=Funai ++ + acpi:FCS*: + ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc. + +@@ -2839,6 +2874,9 @@ + acpi:HEC*: + ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd. + ++acpi:HEI*: ++ ID_VENDOR_FROM_DATABASE=Hyundai ++ + acpi:HEL*: + ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd + +@@ -2968,6 +3006,9 @@ + acpi:HSD*: + ID_VENDOR_FROM_DATABASE=HannStar Display Corp + ++acpi:HSL*: ++ ID_VENDOR_FROM_DATABASE=Hansol ++ + acpi:HSM*: + ID_VENDOR_FROM_DATABASE=AT&T Microelectronics + +@@ -3082,6 +3123,9 @@ + acpi:ICI*: + ID_VENDOR_FROM_DATABASE=Infotek Communication Inc + ++acpi:ICL*: ++ ID_VENDOR_FROM_DATABASE=Fujitsu ICL ++ + acpi:ICM*: + ID_VENDOR_FROM_DATABASE=Intracom SA + +@@ -3175,6 +3219,9 @@ + acpi:IKE*: + ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd. + ++acpi:IKN*: ++ ID_VENDOR_FROM_DATABASE=IKON ++ + acpi:IKS*: + ID_VENDOR_FROM_DATABASE=Ikos Systems Inc + +@@ -3217,6 +3264,9 @@ + acpi:IMT*: + ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation + ++acpi:IMS*: ++ ID_VENDOR_FROM_DATABASE=Integrated Micro Solution Inc. ++ + acpi:INA*: + ID_VENDOR_FROM_DATABASE=Inventec Corporation + +@@ -3712,6 +3762,9 @@ + acpi:LAN*: + ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc + ++acpi:LAP*: ++ ID_VENDOR_FROM_DATABASE=BenQ ++ + acpi:LAS*: + ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S + +@@ -3757,6 +3810,9 @@ + acpi:LED*: + ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc + ++acpi:LED*: ++ ID_VENDOR_FROM_DATABASE=LeafNet ++ + acpi:LEG*: + ID_VENDOR_FROM_DATABASE=Legerity, Inc + +@@ -3772,6 +3828,9 @@ + acpi:LGC*: + ID_VENDOR_FROM_DATABASE=Logic Ltd + ++acpi:LGD*: ++ ID_VENDOR_FROM_DATABASE=LG Display ++ + acpi:LGI*: + ID_VENDOR_FROM_DATABASE=Logitech Inc + +@@ -3823,6 +3882,9 @@ + acpi:LND*: + ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd + ++acpi:LNE*: ++ ID_VENDOR_FROM_DATABASE=Linksys ++ + acpi:LNK*: + ID_VENDOR_FROM_DATABASE=Link Tech Inc + +@@ -3857,7 +3919,7 @@ + ID_VENDOR_FROM_DATABASE=Design Technology + + acpi:LPL*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - LPL ++ ID_VENDOR_FROM_DATABASE=LG Philips + + acpi:LSC*: + ID_VENDOR_FROM_DATABASE=LifeSize Communications +@@ -4027,6 +4089,9 @@ + acpi:MCX*: + ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc. + ++acpi:MCY*: ++ ID_VENDOR_FROM_DATABASE=Microdyne ++ + acpi:MDA*: + ID_VENDOR_FROM_DATABASE=Media4 Inc + +@@ -4252,6 +4317,9 @@ + acpi:MOM*: + ID_VENDOR_FROM_DATABASE=Momentum Data Systems + ++acpi:MON*: ++ ID_VENDOR_FROM_DATABASE=Daewoo ++ + acpi:MOS*: + ID_VENDOR_FROM_DATABASE=Moses Corporation + +@@ -4474,6 +4542,9 @@ + acpi:NAL*: + ID_VENDOR_FROM_DATABASE=Network Alchemy + ++acpi:NAN*: ++ ID_VENDOR_FROM_DATABASE=Nanao ++ + acpi:NAT*: + ID_VENDOR_FROM_DATABASE=NaturalPoint Inc. + +@@ -4969,6 +5040,9 @@ + acpi:PCX*: + ID_VENDOR_FROM_DATABASE=PC Xperten + ++acpi:PDC*: ++ ID_VENDOR_FROM_DATABASE=Polaroid ++ + acpi:PDM*: + ID_VENDOR_FROM_DATABASE=Psion Dacom Plc. + +@@ -5032,9 +5106,6 @@ + acpi:PHE*: + ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH + +-acpi:PHI*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - PHI +- + acpi:PHL*: + ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company + +@@ -5116,9 +5187,6 @@ + acpi:PNL*: + ID_VENDOR_FROM_DATABASE=Panelview, Inc. + +-acpi:PNP*: +- ID_VENDOR_FROM_DATABASE=Microsoft +- + acpi:PNR*: + ID_VENDOR_FROM_DATABASE=Planar Systems, Inc. + +@@ -5248,15 +5316,9 @@ + acpi:PTS*: + ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc + +-acpi:PTW*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - PTW +- + acpi:PUL*: + ID_VENDOR_FROM_DATABASE=Pulse-Eight Ltd + +-acpi:PVC*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - PVC +- + acpi:PVG*: + ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd + +@@ -5560,9 +5622,6 @@ + acpi:RTI*: + ID_VENDOR_FROM_DATABASE=Rancho Tech Inc + +-acpi:RTK*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - RTK +- + acpi:RTL*: + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd + +@@ -5725,9 +5784,6 @@ + acpi:SEE*: + ID_VENDOR_FROM_DATABASE=SeeColor Corporation + +-acpi:SEG*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - SEG +- + acpi:SEI*: + ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc + +@@ -6178,6 +6234,9 @@ + acpi:SVD*: + ID_VENDOR_FROM_DATABASE=SVD Computer + ++acpi:SVE*: ++ ID_VENDOR_FROM_DATABASE=SVEC ++ + acpi:SVI*: + ID_VENDOR_FROM_DATABASE=Sun Microsystems + +@@ -6259,6 +6318,9 @@ + acpi:SZM*: + ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd + ++acpi:SZV*: ++ ID_VENDOR_FROM_DATABASE=OvisLink ++ + acpi:TAA*: + ID_VENDOR_FROM_DATABASE=Tandberg + +@@ -6343,6 +6405,9 @@ + acpi:TDD*: + ID_VENDOR_FROM_DATABASE=Tandberg Data Display AS + ++acpi:TDK*: ++ ID_VENDOR_FROM_DATABASE=TDK USA Corporation ++ + acpi:TDM*: + ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc + +@@ -6379,6 +6444,9 @@ + acpi:TET*: + ID_VENDOR_FROM_DATABASE=TETRADYNE CO., LTD. + ++acpi:TEX*: ++ ID_VENDOR_FROM_DATABASE=Texas Instruments ++ + acpi:TEZ*: + ID_VENDOR_FROM_DATABASE=Tech Source Inc. + +@@ -6490,9 +6558,6 @@ + acpi:TNC*: + ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd + +-acpi:TNJ*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - TNJ +- + acpi:TNM*: + ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA + +@@ -6787,14 +6852,14 @@ + acpi:UNC*: + ID_VENDOR_FROM_DATABASE=Unisys Corporation + +-acpi:UND*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - UND ++acpi:UND* ++ ID_VENDOR_FROM_DATABASE=Unisys Corporation + +-acpi:UNE*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - UNE ++acpi:UNE* ++ ID_VENDOR_FROM_DATABASE=Unisys Corporation + +-acpi:UNF*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - UNF ++acpi:UNF* ++ ID_VENDOR_FROM_DATABASE=Unisys Corporation + + acpi:UNI*: + ID_VENDOR_FROM_DATABASE=Uniform Industry Corp. +@@ -6829,6 +6894,9 @@ + acpi:USA*: + ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG + ++acpi:USC*: ++ ID_VENDOR_FROM_DATABASE=UltraStor ++ + acpi:USD*: + ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation + +@@ -7057,9 +7125,6 @@ + acpi:WAL*: + ID_VENDOR_FROM_DATABASE=Wave Access + +-acpi:WAN*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - WAN +- + acpi:WAV*: + ID_VENDOR_FROM_DATABASE=Wavephore + +@@ -7178,7 +7243,7 @@ + ID_VENDOR_FROM_DATABASE=Woxter Technology Co. Ltd + + acpi:WYS*: +- ID_VENDOR_FROM_DATABASE=Myse Technology ++ ID_VENDOR_FROM_DATABASE=Wyse Technology + + acpi:WYT*: + ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd. +@@ -7192,9 +7257,6 @@ + acpi:XDM*: + ID_VENDOR_FROM_DATABASE=XDM Ltd. + +-acpi:XER*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - XER +- + acpi:XFG*: + ID_VENDOR_FROM_DATABASE=Jan Strapko - FOTO + +@@ -7222,9 +7284,6 @@ + acpi:XNT*: + ID_VENDOR_FROM_DATABASE=XN Technologies, Inc. + +-acpi:XOC*: +- ID_VENDOR_FROM_DATABASE=DO NOT USE - XOC +- + acpi:XQU*: + ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD + +@@ -7291,6 +7350,9 @@ + acpi:ZBX*: + ID_VENDOR_FROM_DATABASE=Zebax Technologies + ++acpi:ZCM*: ++ ID_VENDOR_FROM_DATABASE=Zenith ++ + acpi:ZCT*: + ID_VENDOR_FROM_DATABASE=ZeitControl cardsystems GmbH diff --git a/src/grp-udev/hwdb/20-bluetooth-vendor-product.hwdb b/src/grp-udev/hwdb/20-bluetooth-vendor-product.hwdb new file mode 100644 index 0000000000..5089ab4e04 --- /dev/null +++ b/src/grp-udev/hwdb/20-bluetooth-vendor-product.hwdb @@ -0,0 +1,2830 @@ +# This file is part of systemd. +# +# Data imported from: +# http://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm + +bluetooth:v0000* + ID_VENDOR_FROM_DATABASE=Ericsson Technology Licensing + +bluetooth:v0001* + ID_VENDOR_FROM_DATABASE=Nokia Mobile Phones + +bluetooth:v0002* + ID_VENDOR_FROM_DATABASE=Intel Corp. + +bluetooth:v0003* + ID_VENDOR_FROM_DATABASE=IBM Corp. + +bluetooth:v0004* + ID_VENDOR_FROM_DATABASE=Toshiba Corp. + +bluetooth:v0005* + ID_VENDOR_FROM_DATABASE=3Com + +bluetooth:v0006* + ID_VENDOR_FROM_DATABASE=Microsoft + +bluetooth:v0007* + ID_VENDOR_FROM_DATABASE=Lucent + +bluetooth:v0008* + ID_VENDOR_FROM_DATABASE=Motorola + +bluetooth:v0009* + ID_VENDOR_FROM_DATABASE=Infineon Technologies AG + +bluetooth:v000A* + ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio + +bluetooth:v000B* + ID_VENDOR_FROM_DATABASE=Silicon Wave + +bluetooth:v000C* + ID_VENDOR_FROM_DATABASE=Digianswer A/S + +bluetooth:v000D* + ID_VENDOR_FROM_DATABASE=Texas Instruments Inc. + +bluetooth:v000E* + ID_VENDOR_FROM_DATABASE=Ceva, Inc. (formerly Parthus Technologies, Inc.) + +bluetooth:v000F* + ID_VENDOR_FROM_DATABASE=Broadcom Corporation + +bluetooth:v0010* + ID_VENDOR_FROM_DATABASE=Mitel Semiconductor + +bluetooth:v0011* + ID_VENDOR_FROM_DATABASE=Widcomm, Inc + +bluetooth:v0012* + ID_VENDOR_FROM_DATABASE=Zeevo, Inc. + +bluetooth:v0013* + ID_VENDOR_FROM_DATABASE=Atmel Corporation + +bluetooth:v0014* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Corporation + +bluetooth:v0015* + ID_VENDOR_FROM_DATABASE=RTX Telecom A/S + +bluetooth:v0016* + ID_VENDOR_FROM_DATABASE=KC Technology Inc. + +bluetooth:v0017* + ID_VENDOR_FROM_DATABASE=NewLogic + +bluetooth:v0018* + ID_VENDOR_FROM_DATABASE=Transilica, Inc. + +bluetooth:v0019* + ID_VENDOR_FROM_DATABASE=Rohde & Schwarz GmbH & Co. KG + +bluetooth:v001A* + ID_VENDOR_FROM_DATABASE=TTPCom Limited + +bluetooth:v001B* + ID_VENDOR_FROM_DATABASE=Signia Technologies, Inc. + +bluetooth:v001C* + ID_VENDOR_FROM_DATABASE=Conexant Systems Inc. + +bluetooth:v001D* + ID_VENDOR_FROM_DATABASE=Qualcomm + +bluetooth:v001E* + ID_VENDOR_FROM_DATABASE=Inventel + +bluetooth:v001F* + ID_VENDOR_FROM_DATABASE=AVM Berlin + +bluetooth:v0020* + ID_VENDOR_FROM_DATABASE=BandSpeed, Inc. + +bluetooth:v0021* + ID_VENDOR_FROM_DATABASE=Mansella Ltd + +bluetooth:v0022* + ID_VENDOR_FROM_DATABASE=NEC Corporation + +bluetooth:v0023* + ID_VENDOR_FROM_DATABASE=WavePlus Technology Co., Ltd. + +bluetooth:v0024* + ID_VENDOR_FROM_DATABASE=Alcatel + +bluetooth:v0025* + ID_VENDOR_FROM_DATABASE=NXP Semiconductors (formerly Philips Semiconductors) + +bluetooth:v0026* + ID_VENDOR_FROM_DATABASE=C Technologies + +bluetooth:v0027* + ID_VENDOR_FROM_DATABASE=Open Interface + +bluetooth:v0028* + ID_VENDOR_FROM_DATABASE=R F Micro Devices + +bluetooth:v0029* + ID_VENDOR_FROM_DATABASE=Hitachi Ltd + +bluetooth:v002A* + ID_VENDOR_FROM_DATABASE=Symbol Technologies, Inc. + +bluetooth:v002B* + ID_VENDOR_FROM_DATABASE=Tenovis + +bluetooth:v002C* + ID_VENDOR_FROM_DATABASE=Macronix International Co. Ltd. + +bluetooth:v002D* + ID_VENDOR_FROM_DATABASE=GCT Semiconductor + +bluetooth:v002E* + ID_VENDOR_FROM_DATABASE=Norwood Systems + +bluetooth:v002F* + ID_VENDOR_FROM_DATABASE=MewTel Technology Inc. + +bluetooth:v0030* + ID_VENDOR_FROM_DATABASE=ST Microelectronics + +bluetooth:v0031* + ID_VENDOR_FROM_DATABASE=Synopsys, Inc. + +bluetooth:v0032* + ID_VENDOR_FROM_DATABASE=Red-M (Communications) Ltd + +bluetooth:v0033* + ID_VENDOR_FROM_DATABASE=Commil Ltd + +bluetooth:v0034* + ID_VENDOR_FROM_DATABASE=Computer Access Technology Corporation (CATC) + +bluetooth:v0035* + ID_VENDOR_FROM_DATABASE=Eclipse (HQ Espana) S.L. + +bluetooth:v0036* + ID_VENDOR_FROM_DATABASE=Renesas Electronics Corporation + +bluetooth:v0037* + ID_VENDOR_FROM_DATABASE=Mobilian Corporation + +bluetooth:v0038* + ID_VENDOR_FROM_DATABASE=Terax + +bluetooth:v0039* + ID_VENDOR_FROM_DATABASE=Integrated System Solution Corp. + +bluetooth:v003A* + ID_VENDOR_FROM_DATABASE=Matsushita Electric Industrial Co., Ltd. + +bluetooth:v003B* + ID_VENDOR_FROM_DATABASE=Gennum Corporation + +bluetooth:v003C* + ID_VENDOR_FROM_DATABASE=BlackBerry Limited (formerly Research In Motion) + +bluetooth:v003D* + ID_VENDOR_FROM_DATABASE=IPextreme, Inc. + +bluetooth:v003E* + ID_VENDOR_FROM_DATABASE=Systems and Chips, Inc. + +bluetooth:v003F* + ID_VENDOR_FROM_DATABASE=Bluetooth SIG, Inc. + +bluetooth:v0040* + ID_VENDOR_FROM_DATABASE=Seiko Epson Corporation + +bluetooth:v0041* + ID_VENDOR_FROM_DATABASE=Integrated Silicon Solution Taiwan, Inc. + +bluetooth:v0042* + ID_VENDOR_FROM_DATABASE=CONWISE Technology Corporation Ltd + +bluetooth:v0043* + ID_VENDOR_FROM_DATABASE=PARROT SA + +bluetooth:v0044* + ID_VENDOR_FROM_DATABASE=Socket Mobile + +bluetooth:v0045* + ID_VENDOR_FROM_DATABASE=Atheros Communications, Inc. + +bluetooth:v0046* + ID_VENDOR_FROM_DATABASE=MediaTek, Inc. + +bluetooth:v0047* + ID_VENDOR_FROM_DATABASE=Bluegiga + +bluetooth:v0048* + ID_VENDOR_FROM_DATABASE=Marvell Technology Group Ltd. + +bluetooth:v0049* + ID_VENDOR_FROM_DATABASE=3DSP Corporation + +bluetooth:v004A* + ID_VENDOR_FROM_DATABASE=Accel Semiconductor Ltd. + +bluetooth:v004B* + ID_VENDOR_FROM_DATABASE=Continental Automotive Systems + +bluetooth:v004C* + ID_VENDOR_FROM_DATABASE=Apple, Inc. + +bluetooth:v004D* + ID_VENDOR_FROM_DATABASE=Staccato Communications, Inc. + +bluetooth:v004E* + ID_VENDOR_FROM_DATABASE=Avago Technologies + +bluetooth:v004F* + ID_VENDOR_FROM_DATABASE=APT Licensing Ltd. + +bluetooth:v0050* + ID_VENDOR_FROM_DATABASE=SiRF Technology + +bluetooth:v0051* + ID_VENDOR_FROM_DATABASE=Tzero Technologies, Inc. + +bluetooth:v0052* + ID_VENDOR_FROM_DATABASE=J&M Corporation + +bluetooth:v0053* + ID_VENDOR_FROM_DATABASE=Free2move AB + +bluetooth:v0054* + ID_VENDOR_FROM_DATABASE=3DiJoy Corporation + +bluetooth:v0055* + ID_VENDOR_FROM_DATABASE=Plantronics, Inc. + +bluetooth:v0056* + ID_VENDOR_FROM_DATABASE=Sony Ericsson Mobile Communications + +bluetooth:v0057* + ID_VENDOR_FROM_DATABASE=Harman International Industries, Inc. + +bluetooth:v0058* + ID_VENDOR_FROM_DATABASE=Vizio, Inc. + +bluetooth:v0059* + ID_VENDOR_FROM_DATABASE=Nordic Semiconductor ASA + +bluetooth:v005A* + ID_VENDOR_FROM_DATABASE=EM Microelectronic-Marin SA + +bluetooth:v005B* + ID_VENDOR_FROM_DATABASE=Ralink Technology Corporation + +bluetooth:v005C* + ID_VENDOR_FROM_DATABASE=Belkin International, Inc. + +bluetooth:v005D* + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Corporation + +bluetooth:v005E* + ID_VENDOR_FROM_DATABASE=Stonestreet One, LLC + +bluetooth:v005F* + ID_VENDOR_FROM_DATABASE=Wicentric, Inc. + +bluetooth:v0060* + ID_VENDOR_FROM_DATABASE=RivieraWaves S.A.S + +bluetooth:v0061* + ID_VENDOR_FROM_DATABASE=RDA Microelectronics + +bluetooth:v0062* + ID_VENDOR_FROM_DATABASE=Gibson Guitars + +bluetooth:v0063* + ID_VENDOR_FROM_DATABASE=MiCommand Inc. + +bluetooth:v0064* + ID_VENDOR_FROM_DATABASE=Band XI International, LLC + +bluetooth:v0065* + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Company + +bluetooth:v0066* + ID_VENDOR_FROM_DATABASE=9Solutions Oy + +bluetooth:v0067* + ID_VENDOR_FROM_DATABASE=GN Netcom A/S + +bluetooth:v0068* + ID_VENDOR_FROM_DATABASE=General Motors + +bluetooth:v0069* + ID_VENDOR_FROM_DATABASE=A&D Engineering, Inc. + +bluetooth:v006A* + ID_VENDOR_FROM_DATABASE=MindTree Ltd. + +bluetooth:v006B* + ID_VENDOR_FROM_DATABASE=Polar Electro OY + +bluetooth:v006C* + ID_VENDOR_FROM_DATABASE=Beautiful Enterprise Co., Ltd. + +bluetooth:v006D* + ID_VENDOR_FROM_DATABASE=BriarTek, Inc. + +bluetooth:v006E* + ID_VENDOR_FROM_DATABASE=Summit Data Communications, Inc. + +bluetooth:v006F* + ID_VENDOR_FROM_DATABASE=Sound ID + +bluetooth:v0070* + ID_VENDOR_FROM_DATABASE=Monster, LLC + +bluetooth:v0071* + ID_VENDOR_FROM_DATABASE=connectBlue AB + +bluetooth:v0072* + ID_VENDOR_FROM_DATABASE=ShangHai Super Smart Electronics Co. Ltd. + +bluetooth:v0073* + ID_VENDOR_FROM_DATABASE=Group Sense Ltd. + +bluetooth:v0074* + ID_VENDOR_FROM_DATABASE=Zomm, LLC + +bluetooth:v0075* + ID_VENDOR_FROM_DATABASE=Samsung Electronics Co. Ltd. + +bluetooth:v0076* + ID_VENDOR_FROM_DATABASE=Creative Technology Ltd. + +bluetooth:v0077* + ID_VENDOR_FROM_DATABASE=Laird Technologies + +bluetooth:v0078* + ID_VENDOR_FROM_DATABASE=Nike, Inc. + +bluetooth:v0078p0001* + ID_PRODUCT_FROM_DATABASE=Nike+ FuelBand + +bluetooth:v0079* + ID_VENDOR_FROM_DATABASE=lesswire AG + +bluetooth:v007A* + ID_VENDOR_FROM_DATABASE=MStar Semiconductor, Inc. + +bluetooth:v007B* + ID_VENDOR_FROM_DATABASE=Hanlynn Technologies + +bluetooth:v007C* + ID_VENDOR_FROM_DATABASE=A & R Cambridge + +bluetooth:v007D* + ID_VENDOR_FROM_DATABASE=Seers Technology Co. Ltd + +bluetooth:v007E* + ID_VENDOR_FROM_DATABASE=Sports Tracking Technologies Ltd. + +bluetooth:v007F* + ID_VENDOR_FROM_DATABASE=Autonet Mobile + +bluetooth:v0080* + ID_VENDOR_FROM_DATABASE=DeLorme Publishing Company, Inc. + +bluetooth:v0081* + ID_VENDOR_FROM_DATABASE=WuXi Vimicro + +bluetooth:v0082* + ID_VENDOR_FROM_DATABASE=Sennheiser Communications A/S + +bluetooth:v0083* + ID_VENDOR_FROM_DATABASE=TimeKeeping Systems, Inc. + +bluetooth:v0084* + ID_VENDOR_FROM_DATABASE=Ludus Helsinki Ltd. + +bluetooth:v0085* + ID_VENDOR_FROM_DATABASE=BlueRadios, Inc. + +bluetooth:v0086* + ID_VENDOR_FROM_DATABASE=equinox AG + +bluetooth:v0087* + ID_VENDOR_FROM_DATABASE=Garmin International, Inc. + +bluetooth:v0088* + ID_VENDOR_FROM_DATABASE=Ecotest + +bluetooth:v0089* + ID_VENDOR_FROM_DATABASE=GN ReSound A/S + +bluetooth:v008A* + ID_VENDOR_FROM_DATABASE=Jawbone + +bluetooth:v008B* + ID_VENDOR_FROM_DATABASE=Topcorn Positioning Systems, LLC + +bluetooth:v008C* + ID_VENDOR_FROM_DATABASE=Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.) + +bluetooth:v008D* + ID_VENDOR_FROM_DATABASE=Zscan Software + +bluetooth:v008E* + ID_VENDOR_FROM_DATABASE=Quintic Corp. + +bluetooth:v008F* + ID_VENDOR_FROM_DATABASE=Telit Wireless Solutions GmbH (Formerly Stollman E+V GmbH) + +bluetooth:v0090* + ID_VENDOR_FROM_DATABASE=Funai Electric Co., Ltd. + +bluetooth:v0091* + ID_VENDOR_FROM_DATABASE=Advanced PANMOBIL Systems GmbH & Co. KG + +bluetooth:v0092* + ID_VENDOR_FROM_DATABASE=ThinkOptics, Inc. + +bluetooth:v0093* + ID_VENDOR_FROM_DATABASE=Universal Electronics, Inc. + +bluetooth:v0094* + ID_VENDOR_FROM_DATABASE=Airoha Technology Corp. + +bluetooth:v0095* + ID_VENDOR_FROM_DATABASE=NEC Lighting, Ltd. + +bluetooth:v0096* + ID_VENDOR_FROM_DATABASE=ODM Technology, Inc. + +bluetooth:v0097* + ID_VENDOR_FROM_DATABASE=ConnecteDevice Ltd. + +bluetooth:v0098* + ID_VENDOR_FROM_DATABASE=zer01.tv GmbH + +bluetooth:v0099* + ID_VENDOR_FROM_DATABASE=i.Tech Dynamic Global Distribution Ltd. + +bluetooth:v009A* + ID_VENDOR_FROM_DATABASE=Alpwise + +bluetooth:v009B* + ID_VENDOR_FROM_DATABASE=Jiangsu Toppower Automotive Electronics Co., Ltd. + +bluetooth:v009C* + ID_VENDOR_FROM_DATABASE=Colorfy, Inc. + +bluetooth:v009D* + ID_VENDOR_FROM_DATABASE=Geoforce Inc. + +bluetooth:v009E* + ID_VENDOR_FROM_DATABASE=Bose Corporation + +bluetooth:v009F* + ID_VENDOR_FROM_DATABASE=Suunto Oy + +bluetooth:v00A0* + ID_VENDOR_FROM_DATABASE=Kensington Computer Products Group + +bluetooth:v00A1* + ID_VENDOR_FROM_DATABASE=SR-Medizinelektronik + +bluetooth:v00A2* + ID_VENDOR_FROM_DATABASE=Vertu Corporation Limited + +bluetooth:v00A3* + ID_VENDOR_FROM_DATABASE=Meta Watch Ltd. + +bluetooth:v00A4* + ID_VENDOR_FROM_DATABASE=LINAK A/S + +bluetooth:v00A5* + ID_VENDOR_FROM_DATABASE=OTL Dynamics LLC + +bluetooth:v00A6* + ID_VENDOR_FROM_DATABASE=Panda Ocean Inc. + +bluetooth:v00A7* + ID_VENDOR_FROM_DATABASE=Visteon Corporation + +bluetooth:v00A8* + ID_VENDOR_FROM_DATABASE=ARP Devices Limited + +bluetooth:v00A9* + ID_VENDOR_FROM_DATABASE=Magneti Marelli S.p.A + +bluetooth:v00AA* + ID_VENDOR_FROM_DATABASE=CAEN RFID srl + +bluetooth:v00AB* + ID_VENDOR_FROM_DATABASE=Ingenieur-Systemgruppe Zahn GmbH + +bluetooth:v00AC* + ID_VENDOR_FROM_DATABASE=Green Throttle Games + +bluetooth:v00AD* + ID_VENDOR_FROM_DATABASE=Peter Systemtechnik GmbH + +bluetooth:v00AE* + ID_VENDOR_FROM_DATABASE=Omegawave Oy + +bluetooth:v00AF* + ID_VENDOR_FROM_DATABASE=Cinetix + +bluetooth:v00B0* + ID_VENDOR_FROM_DATABASE=Passif Semiconductor Corp + +bluetooth:v00B1* + ID_VENDOR_FROM_DATABASE=Saris Cycling Group, Inc + +bluetooth:v00B2* + ID_VENDOR_FROM_DATABASE=Bekey A/S + +bluetooth:v00B3* + ID_VENDOR_FROM_DATABASE=Clarinox Technologies Pty. Ltd. + +bluetooth:v00B4* + ID_VENDOR_FROM_DATABASE=BDE Technology Co., Ltd. + +bluetooth:v00B5* + ID_VENDOR_FROM_DATABASE=Swirl Networks + +bluetooth:v00B6* + ID_VENDOR_FROM_DATABASE=Meso international + +bluetooth:v00B7* + ID_VENDOR_FROM_DATABASE=TreLab Ltd + +bluetooth:v00B8* + ID_VENDOR_FROM_DATABASE=Qualcomm Innovation Center, Inc. (QuIC) + +bluetooth:v00B9* + ID_VENDOR_FROM_DATABASE=Johnson Controls, Inc. + +bluetooth:v00BA* + ID_VENDOR_FROM_DATABASE=Starkey Laboratories Inc. + +bluetooth:v00BB* + ID_VENDOR_FROM_DATABASE=S-Power Electronics Limited + +bluetooth:v00BC* + ID_VENDOR_FROM_DATABASE=Ace Sensor Inc + +bluetooth:v00BD* + ID_VENDOR_FROM_DATABASE=Aplix Corporation + +bluetooth:v00BE* + ID_VENDOR_FROM_DATABASE=AAMP of America + +bluetooth:v00BF* + ID_VENDOR_FROM_DATABASE=Stalmart Technology Limited + +bluetooth:v00C0* + ID_VENDOR_FROM_DATABASE=AMICCOM Electronics Corporation + +bluetooth:v00C1* + ID_VENDOR_FROM_DATABASE=Shenzhen Excelsecu Data Technology Co.,Ltd + +bluetooth:v00C2* + ID_VENDOR_FROM_DATABASE=Geneq Inc. + +bluetooth:v00C3* + ID_VENDOR_FROM_DATABASE=adidas AG + +bluetooth:v00C4* + ID_VENDOR_FROM_DATABASE=LG Electronics + +bluetooth:v00C5* + ID_VENDOR_FROM_DATABASE=Onset Computer Corporation + +bluetooth:v00C6* + ID_VENDOR_FROM_DATABASE=Selfly BV + +bluetooth:v00C7* + ID_VENDOR_FROM_DATABASE=Quuppa Oy. + +bluetooth:v00C8* + ID_VENDOR_FROM_DATABASE=GeLo Inc + +bluetooth:v00C9* + ID_VENDOR_FROM_DATABASE=Evluma + +bluetooth:v00CA* + ID_VENDOR_FROM_DATABASE=MC10 + +bluetooth:v00CB* + ID_VENDOR_FROM_DATABASE=Binauric SE + +bluetooth:v00CC* + ID_VENDOR_FROM_DATABASE=Beats Electronics + +bluetooth:v00CD* + ID_VENDOR_FROM_DATABASE=Microchip Technology Inc. + +bluetooth:v00CE* + ID_VENDOR_FROM_DATABASE=Elgato Systems GmbH + +bluetooth:v00CF* + ID_VENDOR_FROM_DATABASE=ARCHOS SA + +bluetooth:v00D0* + ID_VENDOR_FROM_DATABASE=Dexcom, Inc. + +bluetooth:v00D1* + ID_VENDOR_FROM_DATABASE=Polar Electro Europe B.V. + +bluetooth:v00D2* + ID_VENDOR_FROM_DATABASE=Dialog Semiconductor B.V. + +bluetooth:v00D3* + ID_VENDOR_FROM_DATABASE=Taixingbang Technology (HK) Co,. LTD. + +bluetooth:v00D4* + ID_VENDOR_FROM_DATABASE=Kawantech + +bluetooth:v00D5* + ID_VENDOR_FROM_DATABASE=Austco Communication Systems + +bluetooth:v00D6* + ID_VENDOR_FROM_DATABASE=Timex Group USA, Inc. + +bluetooth:v00D7* + ID_VENDOR_FROM_DATABASE=Qualcomm Technologies, Inc. + +bluetooth:v00D8* + ID_VENDOR_FROM_DATABASE=Qualcomm Connected Experiences, Inc. + +bluetooth:v00D9* + ID_VENDOR_FROM_DATABASE=Voyetra Turtle Beach + +bluetooth:v00DA* + ID_VENDOR_FROM_DATABASE=txtr GmbH + +bluetooth:v00DB* + ID_VENDOR_FROM_DATABASE=Biosentronics + +bluetooth:v00DC* + ID_VENDOR_FROM_DATABASE=Procter & Gamble + +bluetooth:v00DD* + ID_VENDOR_FROM_DATABASE=Hosiden Corporation + +bluetooth:v00DE* + ID_VENDOR_FROM_DATABASE=Muzik LLC + +bluetooth:v00DF* + ID_VENDOR_FROM_DATABASE=Misfit Wearables Corp + +bluetooth:v00E0* + ID_VENDOR_FROM_DATABASE=Google + +bluetooth:v00E1* + ID_VENDOR_FROM_DATABASE=Danlers Ltd + +bluetooth:v00E2* + ID_VENDOR_FROM_DATABASE=Semilink Inc + +bluetooth:v00E3* + ID_VENDOR_FROM_DATABASE=inMusic Brands, Inc + +bluetooth:v00E4* + ID_VENDOR_FROM_DATABASE=L.S. Research Inc. + +bluetooth:v00E5* + ID_VENDOR_FROM_DATABASE=Eden Software Consultants Ltd. + +bluetooth:v00E6* + ID_VENDOR_FROM_DATABASE=Freshtemp + +bluetooth:v00E7* + ID_VENDOR_FROM_DATABASE=KS Technologies + +bluetooth:v00E8* + ID_VENDOR_FROM_DATABASE=ACTS Technologies + +bluetooth:v00E9* + ID_VENDOR_FROM_DATABASE=Vtrack Systems + +bluetooth:v00EA* + ID_VENDOR_FROM_DATABASE=Nielsen-Kellerman Company + +bluetooth:v00EB* + ID_VENDOR_FROM_DATABASE=Server Technology, Inc. + +bluetooth:v00EC* + ID_VENDOR_FROM_DATABASE=BioResearch Associates + +bluetooth:v00ED* + ID_VENDOR_FROM_DATABASE=Jolly Logic, LLC + +bluetooth:v00EE* + ID_VENDOR_FROM_DATABASE=Above Average Outcomes, Inc. + +bluetooth:v00EF* + ID_VENDOR_FROM_DATABASE=Bitsplitters GmbH + +bluetooth:v00F0* + ID_VENDOR_FROM_DATABASE=PayPal, Inc. + +bluetooth:v00F1* + ID_VENDOR_FROM_DATABASE=Witron Technology Limited + +bluetooth:v00F2* + ID_VENDOR_FROM_DATABASE=Aether Things Inc. (formerly Morse Project Inc.) + +bluetooth:v00F3* + ID_VENDOR_FROM_DATABASE=Kent Displays Inc. + +bluetooth:v00F4* + ID_VENDOR_FROM_DATABASE=Nautilus Inc. + +bluetooth:v00F5* + ID_VENDOR_FROM_DATABASE=Smartifier Oy + +bluetooth:v00F6* + ID_VENDOR_FROM_DATABASE=Elcometer Limited + +bluetooth:v00F7* + ID_VENDOR_FROM_DATABASE=VSN Technologies Inc. + +bluetooth:v00F8* + ID_VENDOR_FROM_DATABASE=AceUni Corp., Ltd. + +bluetooth:v00F9* + ID_VENDOR_FROM_DATABASE=StickNFind + +bluetooth:v00FA* + ID_VENDOR_FROM_DATABASE=Crystal Code AB + +bluetooth:v00FB* + ID_VENDOR_FROM_DATABASE=KOUKAAM a.s. + +bluetooth:v00FC* + ID_VENDOR_FROM_DATABASE=Delphi Corporation + +bluetooth:v00FD* + ID_VENDOR_FROM_DATABASE=ValenceTech Limited + +bluetooth:v00FE* + ID_VENDOR_FROM_DATABASE=Reserved + +bluetooth:v00FF* + ID_VENDOR_FROM_DATABASE=Typo Products, LLC + +bluetooth:v0100* + ID_VENDOR_FROM_DATABASE=TomTom International BV + +bluetooth:v0101* + ID_VENDOR_FROM_DATABASE=Fugoo, Inc + +bluetooth:v0102* + ID_VENDOR_FROM_DATABASE=Keiser Corporation + +bluetooth:v0103* + ID_VENDOR_FROM_DATABASE=Bang & Olufsen A/S + +bluetooth:v0104* + ID_VENDOR_FROM_DATABASE=PLUS Locations Systems Pty Ltd + +bluetooth:v0105* + ID_VENDOR_FROM_DATABASE=Ubiquitous Computing Technology Corporation + +bluetooth:v0106* + ID_VENDOR_FROM_DATABASE=Innovative Yachtter Solutions + +bluetooth:v0107* + ID_VENDOR_FROM_DATABASE=William Demant Holding A/S + +bluetooth:v0108* + ID_VENDOR_FROM_DATABASE=Chicony Electronics Co., Ltd. + +bluetooth:v0109* + ID_VENDOR_FROM_DATABASE=Atus BV + +bluetooth:v010A* + ID_VENDOR_FROM_DATABASE=Codegate Ltd. + +bluetooth:v010B* + ID_VENDOR_FROM_DATABASE=ERi, Inc. + +bluetooth:v010C* + ID_VENDOR_FROM_DATABASE=Transducers Direct, LLC + +bluetooth:v010D* + ID_VENDOR_FROM_DATABASE=Fujitsu Ten Limited + +bluetooth:v010E* + ID_VENDOR_FROM_DATABASE=Audi AG + +bluetooth:v010F* + ID_VENDOR_FROM_DATABASE=HiSilicon Technologies Co., Ltd. + +bluetooth:v0110* + ID_VENDOR_FROM_DATABASE=Nippon Seiki Co., Ltd. + +bluetooth:v0111* + ID_VENDOR_FROM_DATABASE=Steelseries ApS + +bluetooth:v0112* + ID_VENDOR_FROM_DATABASE=Visybl Inc. + +bluetooth:v0113* + ID_VENDOR_FROM_DATABASE=Openbrain Technologies, Co., Ltd. + +bluetooth:v0114* + ID_VENDOR_FROM_DATABASE=Xensr + +bluetooth:v0115* + ID_VENDOR_FROM_DATABASE=e.solutions + +bluetooth:v0116* + ID_VENDOR_FROM_DATABASE=1OAK Technologies + +bluetooth:v0117* + ID_VENDOR_FROM_DATABASE=Wimoto Technologies Inc + +bluetooth:v0118* + ID_VENDOR_FROM_DATABASE=Radius Networks, Inc. + +bluetooth:v0119* + ID_VENDOR_FROM_DATABASE=Wize Technology Co., Ltd. + +bluetooth:v011A* + ID_VENDOR_FROM_DATABASE=Qualcomm Labs, Inc. + +bluetooth:v011B* + ID_VENDOR_FROM_DATABASE=Aruba Networks + +bluetooth:v011C* + ID_VENDOR_FROM_DATABASE=Baidu + +bluetooth:v011D* + ID_VENDOR_FROM_DATABASE=Arendi AG + +bluetooth:v011E* + ID_VENDOR_FROM_DATABASE=Skoda Auto a.s. + +bluetooth:v011F* + ID_VENDOR_FROM_DATABASE=Volkswagen AG + +bluetooth:v0120* + ID_VENDOR_FROM_DATABASE=Porsche AG + +bluetooth:v0121* + ID_VENDOR_FROM_DATABASE=Sino Wealth Electronic Ltd. + +bluetooth:v0122* + ID_VENDOR_FROM_DATABASE=AirTurn, Inc. + +bluetooth:v0123* + ID_VENDOR_FROM_DATABASE=Kinsa, Inc. + +bluetooth:v0124* + ID_VENDOR_FROM_DATABASE=HID Global + +bluetooth:v0125* + ID_VENDOR_FROM_DATABASE=SEAT es + +bluetooth:v0126* + ID_VENDOR_FROM_DATABASE=Promethean Ltd. + +bluetooth:v0127* + ID_VENDOR_FROM_DATABASE=Salutica Allied Solutions + +bluetooth:v0128* + ID_VENDOR_FROM_DATABASE=GPSI Group Pty Ltd + +bluetooth:v0129* + ID_VENDOR_FROM_DATABASE=Nimble Devices Oy + +bluetooth:v012A* + ID_VENDOR_FROM_DATABASE=Changzhou Yongse Infotech Co., Ltd + +bluetooth:v012B* + ID_VENDOR_FROM_DATABASE=SportIQ + +bluetooth:v012C* + ID_VENDOR_FROM_DATABASE=TEMEC Instruments B.V. + +bluetooth:v012D* + ID_VENDOR_FROM_DATABASE=Sony Corporation + +bluetooth:v012E* + ID_VENDOR_FROM_DATABASE=ASSA ABLOY + +bluetooth:v012F* + ID_VENDOR_FROM_DATABASE=Clarion Co., Ltd. + +bluetooth:v0130* + ID_VENDOR_FROM_DATABASE=Warehouse Innovations + +bluetooth:v0131* + ID_VENDOR_FROM_DATABASE=Cypress Semiconductor Corporation + +bluetooth:v0132* + ID_VENDOR_FROM_DATABASE=MADS Inc + +bluetooth:v0133* + ID_VENDOR_FROM_DATABASE=Blue Maestro Limited + +bluetooth:v0134* + ID_VENDOR_FROM_DATABASE=Resolution Products, Inc. + +bluetooth:v0135* + ID_VENDOR_FROM_DATABASE=Airewear LLC + +bluetooth:v0136* + ID_VENDOR_FROM_DATABASE=Seed Labs, Inc. (formerly ETC sp. z.o.o.) + +bluetooth:v0137* + ID_VENDOR_FROM_DATABASE=Prestigio Plaza Ltd. + +bluetooth:v0138* + ID_VENDOR_FROM_DATABASE=NTEO Inc. + +bluetooth:v0139* + ID_VENDOR_FROM_DATABASE=Focus Systems Corporation + +bluetooth:v013A* + ID_VENDOR_FROM_DATABASE=Tencent Holdings Limited + +bluetooth:v013B* + ID_VENDOR_FROM_DATABASE=Allegion + +bluetooth:v013C* + ID_VENDOR_FROM_DATABASE=Murata Manufacuring Co., Ltd. + +bluetooth:v013D* + ID_VENDOR_FROM_DATABASE=WirelessWERX + +bluetooth:v013E* + ID_VENDOR_FROM_DATABASE=Nod, Inc. + +bluetooth:v013F* + ID_VENDOR_FROM_DATABASE=B&B Manufacturing Company + +bluetooth:v0140* + ID_VENDOR_FROM_DATABASE=Alpine Electronics (China) Co., Ltd + +bluetooth:v0141* + ID_VENDOR_FROM_DATABASE=FedEx Services + +bluetooth:v0142* + ID_VENDOR_FROM_DATABASE=Grape Systems Inc. + +bluetooth:v0143* + ID_VENDOR_FROM_DATABASE=Bkon Connect + +bluetooth:v0144* + ID_VENDOR_FROM_DATABASE=Lintech GmbH + +bluetooth:v0145* + ID_VENDOR_FROM_DATABASE=Novatel Wireless + +bluetooth:v0146* + ID_VENDOR_FROM_DATABASE=Ciright + +bluetooth:v0147* + ID_VENDOR_FROM_DATABASE=Mighty Cast, Inc. + +bluetooth:v0148* + ID_VENDOR_FROM_DATABASE=Ambimat Electronics + +bluetooth:v0149* + ID_VENDOR_FROM_DATABASE=Perytons Ltd. + +bluetooth:v014A* + ID_VENDOR_FROM_DATABASE=Tivoli Audio, LLC + +bluetooth:v014B* + ID_VENDOR_FROM_DATABASE=Master Lock + +bluetooth:v014C* + ID_VENDOR_FROM_DATABASE=Mesh-Net Ltd + +bluetooth:v014D* + ID_VENDOR_FROM_DATABASE=Huizhou Desay SV Automotive CO., LTD. + +bluetooth:v014E* + ID_VENDOR_FROM_DATABASE=Tangerine, Inc. + +bluetooth:v014F* + ID_VENDOR_FROM_DATABASE=B&W Group Ltd. + +bluetooth:v0150* + ID_VENDOR_FROM_DATABASE=Pioneer Corporation + +bluetooth:v0151* + ID_VENDOR_FROM_DATABASE=OnBeep + +bluetooth:v0152* + ID_VENDOR_FROM_DATABASE=Vernier Software & Technology + +bluetooth:v0153* + ID_VENDOR_FROM_DATABASE=ROL Ergo + +bluetooth:v0154* + ID_VENDOR_FROM_DATABASE=Pebble Technology + +bluetooth:v0155* + ID_VENDOR_FROM_DATABASE=NETATMO + +bluetooth:v0156* + ID_VENDOR_FROM_DATABASE=Accumulate AB + +bluetooth:v0157* + ID_VENDOR_FROM_DATABASE=Anhui Huami Information Technology Co., Ltd. + +bluetooth:v0158* + ID_VENDOR_FROM_DATABASE=Inmite s.r.o. + +bluetooth:v0159* + ID_VENDOR_FROM_DATABASE=ChefSteps, Inc. + +bluetooth:v015A* + ID_VENDOR_FROM_DATABASE=micas AG + +bluetooth:v015B* + ID_VENDOR_FROM_DATABASE=Biomedical Research Ltd. + +bluetooth:v015C* + ID_VENDOR_FROM_DATABASE=Pitius Tec S.L. + +bluetooth:v015D* + ID_VENDOR_FROM_DATABASE=Estimote, Inc. + +bluetooth:v015E* + ID_VENDOR_FROM_DATABASE=Unikey Technologies, Inc. + +bluetooth:v015F* + ID_VENDOR_FROM_DATABASE=Timer Cap Co. + +bluetooth:v0160* + ID_VENDOR_FROM_DATABASE=AwoX + +bluetooth:v0161* + ID_VENDOR_FROM_DATABASE=yikes + +bluetooth:v0162* + ID_VENDOR_FROM_DATABASE=MADSGlobal NZ Ltd. + +bluetooth:v0163* + ID_VENDOR_FROM_DATABASE=PCH International + +bluetooth:v0164* + ID_VENDOR_FROM_DATABASE=Qingdao Yeelink Information Technology Co., Ltd. + +bluetooth:v0165* + ID_VENDOR_FROM_DATABASE=Milwaukee Tool (formerly Milwaukee Electric Tools) + +bluetooth:v0166* + ID_VENDOR_FROM_DATABASE=MISHIK Pte Ltd + +bluetooth:v0167* + ID_VENDOR_FROM_DATABASE=Bayer HealthCare + +bluetooth:v0168* + ID_VENDOR_FROM_DATABASE=Spicebox LLC + +bluetooth:v0169* + ID_VENDOR_FROM_DATABASE=emberlight + +bluetooth:v016A* + ID_VENDOR_FROM_DATABASE=Cooper-Atkins Corporation + +bluetooth:v016B* + ID_VENDOR_FROM_DATABASE=Qblinks + +bluetooth:v016C* + ID_VENDOR_FROM_DATABASE=MYSPHERA + +bluetooth:v016D* + ID_VENDOR_FROM_DATABASE=LifeScan Inc + +bluetooth:v016E* + ID_VENDOR_FROM_DATABASE=Volantic AB + +bluetooth:v016F* + ID_VENDOR_FROM_DATABASE=Podo Labs, Inc + +bluetooth:v0170* + ID_VENDOR_FROM_DATABASE=F. Hoffmann-La Roche AG + +bluetooth:v0171* + ID_VENDOR_FROM_DATABASE=Amazon Fulfillment Service + +bluetooth:v0172* + ID_VENDOR_FROM_DATABASE=Connovate Technology Private Limited + +bluetooth:v0173* + ID_VENDOR_FROM_DATABASE=Kocomojo, LLC + +bluetooth:v0174* + ID_VENDOR_FROM_DATABASE=Everykey LLC + +bluetooth:v0175* + ID_VENDOR_FROM_DATABASE=Dynamic Controls + +bluetooth:v0176* + ID_VENDOR_FROM_DATABASE=SentriLock + +bluetooth:v0177* + ID_VENDOR_FROM_DATABASE=I-SYST inc. + +bluetooth:v0178* + ID_VENDOR_FROM_DATABASE=CASIO COMPUTER CO., LTD. + +bluetooth:v0179* + ID_VENDOR_FROM_DATABASE=LAPIS Semiconductor Co., Ltd. + +bluetooth:v017A* + ID_VENDOR_FROM_DATABASE=Telemonitor, Inc. + +bluetooth:v017B* + ID_VENDOR_FROM_DATABASE=taskit GmbH + +bluetooth:v017C* + ID_VENDOR_FROM_DATABASE=Daimler AG + +bluetooth:v017D* + ID_VENDOR_FROM_DATABASE=BatAndCat + +bluetooth:v017E* + ID_VENDOR_FROM_DATABASE=BluDotz Ltd + +bluetooth:v017F* + ID_VENDOR_FROM_DATABASE=XTel ApS + +bluetooth:v0180* + ID_VENDOR_FROM_DATABASE=Gigaset Communications GmbH + +bluetooth:v0181* + ID_VENDOR_FROM_DATABASE=Gecko Health Innovations, Inc. + +bluetooth:v0182* + ID_VENDOR_FROM_DATABASE=HOP Ubiquitous + +bluetooth:v0183* + ID_VENDOR_FROM_DATABASE=To Be Assigned + +bluetooth:v0184* + ID_VENDOR_FROM_DATABASE=Nectar + +bluetooth:v0185* + ID_VENDOR_FROM_DATABASE=bel'apps LLC + +bluetooth:v0186* + ID_VENDOR_FROM_DATABASE=CORE Lighting Ltd + +bluetooth:v0187* + ID_VENDOR_FROM_DATABASE=Seraphim Sense Ltd + +bluetooth:v0188* + ID_VENDOR_FROM_DATABASE=Unico RBC + +bluetooth:v0189* + ID_VENDOR_FROM_DATABASE=Physical Enterprises Inc. + +bluetooth:v018A* + ID_VENDOR_FROM_DATABASE=Able Trend Technology Limited + +bluetooth:v018B* + ID_VENDOR_FROM_DATABASE=Konica Minolta, Inc. + +bluetooth:v018C* + ID_VENDOR_FROM_DATABASE=Wilo SE + +bluetooth:v018D* + ID_VENDOR_FROM_DATABASE=Extron Design Services + +bluetooth:v018E* + ID_VENDOR_FROM_DATABASE=Fitbit, Inc. + +bluetooth:v018F* + ID_VENDOR_FROM_DATABASE=Fireflies Systems + +bluetooth:v0190* + ID_VENDOR_FROM_DATABASE=Intelletto Technologies Inc. + +bluetooth:v0191* + ID_VENDOR_FROM_DATABASE=FDK CORPORATION + +bluetooth:v0192* + ID_VENDOR_FROM_DATABASE=Cloudleaf, Inc + +bluetooth:v0193* + ID_VENDOR_FROM_DATABASE=Maveric Automation LLC + +bluetooth:v0194* + ID_VENDOR_FROM_DATABASE=Acoustic Stream Corporation + +bluetooth:v0195* + ID_VENDOR_FROM_DATABASE=Zuli + +bluetooth:v0196* + ID_VENDOR_FROM_DATABASE=Paxton Access Ltd + +bluetooth:v0197* + ID_VENDOR_FROM_DATABASE=WiSilica Inc + +bluetooth:v0198* + ID_VENDOR_FROM_DATABASE=Vengit Limited + +bluetooth:v0199* + ID_VENDOR_FROM_DATABASE=SALTO SYSTEMS S.L. + +bluetooth:v019A* + ID_VENDOR_FROM_DATABASE=TRON Forum (formerly T-Engine Forum) + +bluetooth:v019B* + ID_VENDOR_FROM_DATABASE=CUBETECH s.r.o. + +bluetooth:v019C* + ID_VENDOR_FROM_DATABASE=Cokiya Incorporated + +bluetooth:v019D* + ID_VENDOR_FROM_DATABASE=CVS Health + +bluetooth:v019E* + ID_VENDOR_FROM_DATABASE=Ceruus + +bluetooth:v019F* + ID_VENDOR_FROM_DATABASE=Strainstall Ltd + +bluetooth:v01A0* + ID_VENDOR_FROM_DATABASE=Channel Enterprises (HK) Ltd. + +bluetooth:v01A1* + ID_VENDOR_FROM_DATABASE=FIAMM + +bluetooth:v01A2* + ID_VENDOR_FROM_DATABASE=GIGALANE.CO.,LTD + +bluetooth:v01A3* + ID_VENDOR_FROM_DATABASE=EROAD + +bluetooth:v01A4* + ID_VENDOR_FROM_DATABASE=Mine Safety Appliances + +bluetooth:v01A5* + ID_VENDOR_FROM_DATABASE=Icon Health and Fitness + +bluetooth:v01A6* + ID_VENDOR_FROM_DATABASE=Asandoo GmbH + +bluetooth:v01A7* + ID_VENDOR_FROM_DATABASE=ENERGOUS CORPORATION + +bluetooth:v01A8* + ID_VENDOR_FROM_DATABASE=Taobao + +bluetooth:v01A9* + ID_VENDOR_FROM_DATABASE=Canon Inc. + +bluetooth:v01AA* + ID_VENDOR_FROM_DATABASE=Geophysical Technology Inc. + +bluetooth:v01AB* + ID_VENDOR_FROM_DATABASE=Facebook, Inc. + +bluetooth:v01AC* + ID_VENDOR_FROM_DATABASE=Nipro Diagnostics, Inc. + +bluetooth:v01AD* + ID_VENDOR_FROM_DATABASE=FlightSafety International + +bluetooth:v01AE* + ID_VENDOR_FROM_DATABASE=Earlens Corporation + +bluetooth:v01AF* + ID_VENDOR_FROM_DATABASE=Sunrise Micro Devices, Inc. + +bluetooth:v01B0* + ID_VENDOR_FROM_DATABASE=Star Micronics Co., Ltd. + +bluetooth:v01B1* + ID_VENDOR_FROM_DATABASE=Netizens Sp. z o.o. + +bluetooth:v01B2* + ID_VENDOR_FROM_DATABASE=Nymi Inc. + +bluetooth:v01B3* + ID_VENDOR_FROM_DATABASE=Nytec, Inc. + +bluetooth:v01B4* + ID_VENDOR_FROM_DATABASE=Trineo Sp. z o.o. + +bluetooth:v01B5* + ID_VENDOR_FROM_DATABASE=Nest Labs Inc. + +bluetooth:v01B6* + ID_VENDOR_FROM_DATABASE=LM Technologies Ltd + +bluetooth:v01B7* + ID_VENDOR_FROM_DATABASE=General Electric Company + +bluetooth:v01B8* + ID_VENDOR_FROM_DATABASE=i+D3 S.L. + +bluetooth:v01B9* + ID_VENDOR_FROM_DATABASE=HANA Micron + +bluetooth:v01BA* + ID_VENDOR_FROM_DATABASE=Stages Cycling LLC + +bluetooth:v01BB* + ID_VENDOR_FROM_DATABASE=Cochlear Bone Anchored Solutions AB + +bluetooth:v01BC* + ID_VENDOR_FROM_DATABASE=SenionLab AB + +bluetooth:v01BD* + ID_VENDOR_FROM_DATABASE=Syszone Co., Ltd + +bluetooth:v01BE* + ID_VENDOR_FROM_DATABASE=Pulsate Mobile Ltd. + +bluetooth:v01BF* + ID_VENDOR_FROM_DATABASE=Hong Kong HunterSun Electronic Limited + +bluetooth:v01C0* + ID_VENDOR_FROM_DATABASE=pironex GmbH + +bluetooth:v01C1* + ID_VENDOR_FROM_DATABASE=BRADATECH Corp. + +bluetooth:v01C2* + ID_VENDOR_FROM_DATABASE=Transenergooil AG + +bluetooth:v01C3* + ID_VENDOR_FROM_DATABASE=Bunch + +bluetooth:v01C4* + ID_VENDOR_FROM_DATABASE=DME Microelectronics + +bluetooth:v01C5* + ID_VENDOR_FROM_DATABASE=Bitcraze AB + +bluetooth:v01C6* + ID_VENDOR_FROM_DATABASE=HASWARE Inc. + +bluetooth:v01C7* + ID_VENDOR_FROM_DATABASE=Abiogenix Inc. + +bluetooth:v01C8* + ID_VENDOR_FROM_DATABASE=Poly-Control ApS + +bluetooth:v01C9* + ID_VENDOR_FROM_DATABASE=Avi-on + +bluetooth:v01CA* + ID_VENDOR_FROM_DATABASE=Laerdal Medical AS + +bluetooth:v01CB* + ID_VENDOR_FROM_DATABASE=Fetch My Pet + +bluetooth:v01CC* + ID_VENDOR_FROM_DATABASE=Sam Labs Ltd. + +bluetooth:v01CD* + ID_VENDOR_FROM_DATABASE=Chengdu Synwing Technology Ltd + +bluetooth:v01CE* + ID_VENDOR_FROM_DATABASE=HOUWA SYSTEM DESIGN, k.k. + +bluetooth:v01CF* + ID_VENDOR_FROM_DATABASE=BSH + +bluetooth:v01D0* + ID_VENDOR_FROM_DATABASE=Primus Inter Pares Ltd + +bluetooth:v01D1* + ID_VENDOR_FROM_DATABASE=August + +bluetooth:v01D2* + ID_VENDOR_FROM_DATABASE=Gill Electronics + +bluetooth:v01D3* + ID_VENDOR_FROM_DATABASE=Sky Wave Design + +bluetooth:v01D4* + ID_VENDOR_FROM_DATABASE=Newlab S.r.l. + +bluetooth:v01D5* + ID_VENDOR_FROM_DATABASE=ELAD srl + +bluetooth:v01D6* + ID_VENDOR_FROM_DATABASE=G-wearables inc. + +bluetooth:v01D7* + ID_VENDOR_FROM_DATABASE=Squadrone Systems Inc. + +bluetooth:v01D8* + ID_VENDOR_FROM_DATABASE=Code Corporation + +bluetooth:v01D9* + ID_VENDOR_FROM_DATABASE=Savant Systems LLC + +bluetooth:v01DA* + ID_VENDOR_FROM_DATABASE=Logitech International SA + +bluetooth:v01DB* + ID_VENDOR_FROM_DATABASE=Innblue Consulting + +bluetooth:v01DC* + ID_VENDOR_FROM_DATABASE=iParking Ltd. + +bluetooth:v01DD* + ID_VENDOR_FROM_DATABASE=Koninklijke Philips Electronics N.V. + +bluetooth:v01DE* + ID_VENDOR_FROM_DATABASE=Minelab Electronics Pty Limited + +bluetooth:v01DF* + ID_VENDOR_FROM_DATABASE=Bison Group Ltd. + +bluetooth:v01E0* + ID_VENDOR_FROM_DATABASE=Widex A/S + +bluetooth:v01E1* + ID_VENDOR_FROM_DATABASE=Jolla Ltd + +bluetooth:v01E2* + ID_VENDOR_FROM_DATABASE=Lectronix, Inc. + +bluetooth:v01E3* + ID_VENDOR_FROM_DATABASE=Caterpillar Inc + +bluetooth:v01E4* + ID_VENDOR_FROM_DATABASE=Freedom Innovations + +bluetooth:v01E5* + ID_VENDOR_FROM_DATABASE=Dynamic Devices Ltd + +bluetooth:v01E6* + ID_VENDOR_FROM_DATABASE=Technology Solutions (UK) Ltd + +bluetooth:v01E7* + ID_VENDOR_FROM_DATABASE=IPS Group Inc. + +bluetooth:v01E8* + ID_VENDOR_FROM_DATABASE=STIR + +bluetooth:v01E9* + ID_VENDOR_FROM_DATABASE=Sano, Inc + +bluetooth:v01EA* + ID_VENDOR_FROM_DATABASE=Advanced Application Design, Inc. + +bluetooth:v01EB* + ID_VENDOR_FROM_DATABASE=AutoMap LLC + +bluetooth:v01EC* + ID_VENDOR_FROM_DATABASE=Spreadtrum Communications Shanghai Ltd + +bluetooth:v01ED* + ID_VENDOR_FROM_DATABASE=CuteCircuit LTD + +bluetooth:v01EE* + ID_VENDOR_FROM_DATABASE=Valeo Service + +bluetooth:v01EF* + ID_VENDOR_FROM_DATABASE=Fullpower Technologies, Inc. + +bluetooth:v01F0* + ID_VENDOR_FROM_DATABASE=KloudNation + +bluetooth:v01F1* + ID_VENDOR_FROM_DATABASE=Zebra Technologies Corporation + +bluetooth:v01F2* + ID_VENDOR_FROM_DATABASE=Itron, Inc. + +bluetooth:v01F3* + ID_VENDOR_FROM_DATABASE=The University of Tokyo + +bluetooth:v01F4* + ID_VENDOR_FROM_DATABASE=UTC Fire and Security + +bluetooth:v01F5* + ID_VENDOR_FROM_DATABASE=Cool Webthings Limited + +bluetooth:v01F6* + ID_VENDOR_FROM_DATABASE=DJO Global + +bluetooth:v01F7* + ID_VENDOR_FROM_DATABASE=Gelliner Limited + +bluetooth:v01F8* + ID_VENDOR_FROM_DATABASE=Anyka (Guangzhou) Microelectronics Technology Co, LTD + +bluetooth:v01F9* + ID_VENDOR_FROM_DATABASE=Medtronic, Inc. + +bluetooth:v01FA* + ID_VENDOR_FROM_DATABASE=Gozio, Inc. + +bluetooth:v01FB* + ID_VENDOR_FROM_DATABASE=Form Lifting, LLC + +bluetooth:v01FC* + ID_VENDOR_FROM_DATABASE=Wahoo Fitness, LLC + +bluetooth:v01FD* + ID_VENDOR_FROM_DATABASE=Kontakt Micro-Location Sp. z o.o. + +bluetooth:v01FE* + ID_VENDOR_FROM_DATABASE=Radio System Corporation + +bluetooth:v01FF* + ID_VENDOR_FROM_DATABASE=Freescale Semiconductor, Inc. + +bluetooth:v0200* + ID_VENDOR_FROM_DATABASE=Verifone Systems PTe Ltd. Taiwan Branch + +bluetooth:v0201* + ID_VENDOR_FROM_DATABASE=AR Timing + +bluetooth:v0202* + ID_VENDOR_FROM_DATABASE=Rigado LLC + +bluetooth:v0203* + ID_VENDOR_FROM_DATABASE=Kemppi Oy + +bluetooth:v0204* + ID_VENDOR_FROM_DATABASE=Tapcentive Inc. + +bluetooth:v0205* + ID_VENDOR_FROM_DATABASE=Smartbotics Inc. + +bluetooth:v0206* + ID_VENDOR_FROM_DATABASE=Otter Products, LLC + +bluetooth:v0207* + ID_VENDOR_FROM_DATABASE=STEMP Inc. + +bluetooth:v0208* + ID_VENDOR_FROM_DATABASE=LumiGeek LLC + +bluetooth:v0209* + ID_VENDOR_FROM_DATABASE=InvisionHeart Inc. + +bluetooth:v020A* + ID_VENDOR_FROM_DATABASE=Macnica Inc. + +bluetooth:v020B* + ID_VENDOR_FROM_DATABASE=Jaguar Land Rover Limited + +bluetooth:v020C* + ID_VENDOR_FROM_DATABASE=CoroWare Technologies, Inc + +bluetooth:v020D* + ID_VENDOR_FROM_DATABASE=Simplo Technology Co., LTD + +bluetooth:v020E* + ID_VENDOR_FROM_DATABASE=Omron Healthcare Co., LTD + +bluetooth:v020F* + ID_VENDOR_FROM_DATABASE=Comodule GMBH + +bluetooth:v0210* + ID_VENDOR_FROM_DATABASE=ikeGPS + +bluetooth:v0211* + ID_VENDOR_FROM_DATABASE=Telink Semiconductor Co. Ltd + +bluetooth:v0212* + ID_VENDOR_FROM_DATABASE=Interplan Co., Ltd + +bluetooth:v0213* + ID_VENDOR_FROM_DATABASE=Wyler AG + +bluetooth:v0214* + ID_VENDOR_FROM_DATABASE=IK Multimedia Production srl + +bluetooth:v0215* + ID_VENDOR_FROM_DATABASE=Lukoton Experience Oy + +bluetooth:v0216* + ID_VENDOR_FROM_DATABASE=MTI Ltd + +bluetooth:v0217* + ID_VENDOR_FROM_DATABASE=Tech4home, Lda + +bluetooth:v0218* + ID_VENDOR_FROM_DATABASE=Hiotech AB + +bluetooth:v0219* + ID_VENDOR_FROM_DATABASE=DOTT Limited + +bluetooth:v021A* + ID_VENDOR_FROM_DATABASE=Blue Speck Labs, LLC + +bluetooth:v021B* + ID_VENDOR_FROM_DATABASE=Cisco Systems Inc + +bluetooth:v021C* + ID_VENDOR_FROM_DATABASE=Mobicomm Inc + +bluetooth:v021D* + ID_VENDOR_FROM_DATABASE=Edamic + +bluetooth:v021E* + ID_VENDOR_FROM_DATABASE=Goodnet Ltd + +bluetooth:v021F* + ID_VENDOR_FROM_DATABASE=Luster Leaf Products Inc + +bluetooth:v0220* + ID_VENDOR_FROM_DATABASE=Manus Machina BV + +bluetooth:v0221* + ID_VENDOR_FROM_DATABASE=Mobiquity Networks Inc + +bluetooth:v0222* + ID_VENDOR_FROM_DATABASE=Praxis Dynamics + +bluetooth:v0223* + ID_VENDOR_FROM_DATABASE=Philip Morris Products S.A. + +bluetooth:v0224* + ID_VENDOR_FROM_DATABASE=Comarch SA + +bluetooth:v0225* + ID_VENDOR_FROM_DATABASE=Nestlé Nespresso S.A. + +bluetooth:v0226* + ID_VENDOR_FROM_DATABASE=Merlinia A/S + +bluetooth:v0227* + ID_VENDOR_FROM_DATABASE=LifeBEAM Technologies + +bluetooth:v0228* + ID_VENDOR_FROM_DATABASE=Twocanoes Labs, LLC + +bluetooth:v0229* + ID_VENDOR_FROM_DATABASE=Muoverti Limited + +bluetooth:v022A* + ID_VENDOR_FROM_DATABASE=Stamer Musikanlagen GMBH + +bluetooth:v022B* + ID_VENDOR_FROM_DATABASE=Tesla Motors + +bluetooth:v022C* + ID_VENDOR_FROM_DATABASE=Pharynks Corporation + +bluetooth:v022D* + ID_VENDOR_FROM_DATABASE=Lupine + +bluetooth:v022E* + ID_VENDOR_FROM_DATABASE=Siemens AG + +bluetooth:v022F* + ID_VENDOR_FROM_DATABASE=Huami (Shanghai) Culture Communication CO., LTD + +bluetooth:v0230* + ID_VENDOR_FROM_DATABASE=Foster Electric Company, Ltd + +bluetooth:v0231* + ID_VENDOR_FROM_DATABASE=ETA SA + +bluetooth:v0232* + ID_VENDOR_FROM_DATABASE=x-Senso Solutions Kft + +bluetooth:v0233* + ID_VENDOR_FROM_DATABASE=Shenzhen SuLong Communication Ltd + +bluetooth:v0234* + ID_VENDOR_FROM_DATABASE=FengFan (BeiJing) Technology Co, Ltd + +bluetooth:v0235* + ID_VENDOR_FROM_DATABASE=Qrio Inc + +bluetooth:v0236* + ID_VENDOR_FROM_DATABASE=Pitpatpet Ltd + +bluetooth:v0237* + ID_VENDOR_FROM_DATABASE=MSHeli s.r.l. + +bluetooth:v0238* + ID_VENDOR_FROM_DATABASE=Trakm8 Ltd + +bluetooth:v0239* + ID_VENDOR_FROM_DATABASE=JIN CO, Ltd + +bluetooth:v023A* + ID_VENDOR_FROM_DATABASE=Alatech Technology + +bluetooth:v023B* + ID_VENDOR_FROM_DATABASE=Beijing CarePulse Electronic Technology Co, Ltd + +bluetooth:v023C* + ID_VENDOR_FROM_DATABASE=Awarepoint + +bluetooth:v023D* + ID_VENDOR_FROM_DATABASE=ViCentra B.V. + +bluetooth:v023E* + ID_VENDOR_FROM_DATABASE=Raven Industries + +bluetooth:v023F* + ID_VENDOR_FROM_DATABASE=WaveWare Technologies + +bluetooth:v0240* + ID_VENDOR_FROM_DATABASE=Argenox Technologies + +bluetooth:v0241* + ID_VENDOR_FROM_DATABASE=Bragi GmbH + +bluetooth:v0242* + ID_VENDOR_FROM_DATABASE=16Lab Inc + +bluetooth:v0243* + ID_VENDOR_FROM_DATABASE=Masimo Corp + +bluetooth:v0244* + ID_VENDOR_FROM_DATABASE=Iotera Inc. + +bluetooth:v0245* + ID_VENDOR_FROM_DATABASE=Endress+Hauser + +bluetooth:v0246* + ID_VENDOR_FROM_DATABASE=ACKme Networks, Inc. + +bluetooth:v0247* + ID_VENDOR_FROM_DATABASE=FiftyThree Inc. + +bluetooth:v0248* + ID_VENDOR_FROM_DATABASE=Parker Hannifin Corp + +bluetooth:v0249* + ID_VENDOR_FROM_DATABASE=Transcranial Ltd + +bluetooth:v024A* + ID_VENDOR_FROM_DATABASE=Uwatec AG + +bluetooth:v024B* + ID_VENDOR_FROM_DATABASE=Orlan LLC + +bluetooth:v024C* + ID_VENDOR_FROM_DATABASE=Blue Clover Devices + +bluetooth:v024D* + ID_VENDOR_FROM_DATABASE=M-Way Solutions GmbH + +bluetooth:v024E* + ID_VENDOR_FROM_DATABASE=Microtronics Engineering GmbH + +bluetooth:v024F* + ID_VENDOR_FROM_DATABASE=Schneider Schreibgeräte GmbH + +bluetooth:v0250* + ID_VENDOR_FROM_DATABASE=Sapphire Circuits LLC + +bluetooth:v0251* + ID_VENDOR_FROM_DATABASE=Lumo Bodytech Inc. + +bluetooth:v0252* + ID_VENDOR_FROM_DATABASE=UKC Technosolution + +bluetooth:v0253* + ID_VENDOR_FROM_DATABASE=Xicato Inc. + +bluetooth:v0254* + ID_VENDOR_FROM_DATABASE=Playbrush + +bluetooth:v0255* + ID_VENDOR_FROM_DATABASE=Dai Nippon Printing Co., Ltd. + +bluetooth:v0256* + ID_VENDOR_FROM_DATABASE=G24 Power Limited + +bluetooth:v0257* + ID_VENDOR_FROM_DATABASE=AdBabble Local Commerce Inc. + +bluetooth:v0258* + ID_VENDOR_FROM_DATABASE=Devialet SA + +bluetooth:v0259* + ID_VENDOR_FROM_DATABASE=ALTYOR + +bluetooth:v025A* + ID_VENDOR_FROM_DATABASE=University of Applied Sciences Valais/Haute Ecole Valaisanne + +bluetooth:v025B* + ID_VENDOR_FROM_DATABASE=Five Interactive, LLC dba Zendo + +bluetooth:v025C* + ID_VENDOR_FROM_DATABASE=NetEase (Hangzhou) Network co.Ltd. + +bluetooth:v025D* + ID_VENDOR_FROM_DATABASE=Lexmark International Inc. + +bluetooth:v025E* + ID_VENDOR_FROM_DATABASE=Fluke Corporation + +bluetooth:v025F* + ID_VENDOR_FROM_DATABASE=Yardarm Technologies + +bluetooth:v0260* + ID_VENDOR_FROM_DATABASE=SensaRx + +bluetooth:v0261* + ID_VENDOR_FROM_DATABASE=SECVRE GmbH + +bluetooth:v0262* + ID_VENDOR_FROM_DATABASE=Glacial Ridge Technologies + +bluetooth:v0263* + ID_VENDOR_FROM_DATABASE=Identiv, Inc. + +bluetooth:v0264* + ID_VENDOR_FROM_DATABASE=DDS, Inc. + +bluetooth:v0265* + ID_VENDOR_FROM_DATABASE=SMK Corporation + +bluetooth:v0266* + ID_VENDOR_FROM_DATABASE=Schawbel Technologies LLC + +bluetooth:v0267* + ID_VENDOR_FROM_DATABASE=XMI Systems SA + +bluetooth:v0268* + ID_VENDOR_FROM_DATABASE=Cerevo + +bluetooth:v0269* + ID_VENDOR_FROM_DATABASE=Torrox GmbH & Co KG + +bluetooth:v026A* + ID_VENDOR_FROM_DATABASE=Gemalto + +bluetooth:v026B* + ID_VENDOR_FROM_DATABASE=DEKA Research & Development Corp. + +bluetooth:v026C* + ID_VENDOR_FROM_DATABASE=Domster Tadeusz Szydlowski + +bluetooth:v026D* + ID_VENDOR_FROM_DATABASE=Technogym SPA + +bluetooth:v026E* + ID_VENDOR_FROM_DATABASE=FLEURBAEY BVBA + +bluetooth:v026F* + ID_VENDOR_FROM_DATABASE=Aptcode Solutions + +bluetooth:v0270* + ID_VENDOR_FROM_DATABASE=LSI ADL Technology + +bluetooth:v0271* + ID_VENDOR_FROM_DATABASE=Animas Corp + +bluetooth:v0272* + ID_VENDOR_FROM_DATABASE=Alps Electric Co., Ltd. + +bluetooth:v0273* + ID_VENDOR_FROM_DATABASE=OCEASOFT + +bluetooth:v0274* + ID_VENDOR_FROM_DATABASE=Motsai Research + +bluetooth:v0275* + ID_VENDOR_FROM_DATABASE=Geotab + +bluetooth:v0276* + ID_VENDOR_FROM_DATABASE=E.G.O. Elektro-Gerätebau GmbH + +bluetooth:v0277* + ID_VENDOR_FROM_DATABASE=bewhere inc + +bluetooth:v0278* + ID_VENDOR_FROM_DATABASE=Johnson Outdoors Inc + +bluetooth:v0279* + ID_VENDOR_FROM_DATABASE=steute Schaltgerate GmbH & Co. KG + +bluetooth:v027A* + ID_VENDOR_FROM_DATABASE=Ekomini inc. + +bluetooth:v027B* + ID_VENDOR_FROM_DATABASE=DEFA AS + +bluetooth:v027C* + ID_VENDOR_FROM_DATABASE=Aseptika Ltd + +bluetooth:v027D* + ID_VENDOR_FROM_DATABASE=HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 ) + +bluetooth:v027E* + ID_VENDOR_FROM_DATABASE=HabitAware, LLC + +bluetooth:v027F* + ID_VENDOR_FROM_DATABASE=ruwido austria gmbh + +bluetooth:v0280* + ID_VENDOR_FROM_DATABASE=ITEC corporation + +bluetooth:v0281* + ID_VENDOR_FROM_DATABASE=StoneL + +bluetooth:v0282* + ID_VENDOR_FROM_DATABASE=Sonova AG + +bluetooth:v0283* + ID_VENDOR_FROM_DATABASE=Maven Machines, Inc. + +bluetooth:v0284* + ID_VENDOR_FROM_DATABASE=Synapse Electronics + +bluetooth:v0285* + ID_VENDOR_FROM_DATABASE=Standard Innovation Inc. + +bluetooth:v0286* + ID_VENDOR_FROM_DATABASE=RF Code, Inc. + +bluetooth:v0287* + ID_VENDOR_FROM_DATABASE=Wally Ventures S.L. + +bluetooth:v0288* + ID_VENDOR_FROM_DATABASE=Willowbank Electronics Ltd + +bluetooth:v0289* + ID_VENDOR_FROM_DATABASE=SK Telecom + +bluetooth:v028A* + ID_VENDOR_FROM_DATABASE=Jetro AS + +bluetooth:v028B* + ID_VENDOR_FROM_DATABASE=Code Gears LTD + +bluetooth:v028C* + ID_VENDOR_FROM_DATABASE=NANOLINK APS + +bluetooth:v028D* + ID_VENDOR_FROM_DATABASE=IF, LLC + +bluetooth:v028E* + ID_VENDOR_FROM_DATABASE=RF Digital Corp + +bluetooth:v028F* + ID_VENDOR_FROM_DATABASE=Church & Dwight Co., Inc + +bluetooth:v0290* + ID_VENDOR_FROM_DATABASE=Multibit Oy + +bluetooth:v0291* + ID_VENDOR_FROM_DATABASE=CliniCloud Inc + +bluetooth:v0292* + ID_VENDOR_FROM_DATABASE=SwiftSensors + +bluetooth:v0293* + ID_VENDOR_FROM_DATABASE=Blue Bite + +bluetooth:v0294* + ID_VENDOR_FROM_DATABASE=ELIAS GmbH + +bluetooth:v0295* + ID_VENDOR_FROM_DATABASE=Sivantos GmbH + +bluetooth:v0296* + ID_VENDOR_FROM_DATABASE=Petzl + +bluetooth:v0297* + ID_VENDOR_FROM_DATABASE=storm power ltd + +bluetooth:v0298* + ID_VENDOR_FROM_DATABASE=EISST Ltd + +bluetooth:v0299* + ID_VENDOR_FROM_DATABASE=Inexess Technology Simma KG + +bluetooth:v029A* + ID_VENDOR_FROM_DATABASE=Currant, Inc. + +bluetooth:v029B* + ID_VENDOR_FROM_DATABASE=C2 Development, Inc. + +bluetooth:v029C* + ID_VENDOR_FROM_DATABASE=Blue Sky Scientific, LLC + +bluetooth:v029D* + ID_VENDOR_FROM_DATABASE=ALOTTAZS LABS, LLC + +bluetooth:v029E* + ID_VENDOR_FROM_DATABASE=Kupson spol. s r.o. + +bluetooth:v029F* + ID_VENDOR_FROM_DATABASE=Areus Engineering GmbH + +bluetooth:v02A0* + ID_VENDOR_FROM_DATABASE=Impossible Camera GmbH + +bluetooth:v02A1* + ID_VENDOR_FROM_DATABASE=InventureTrack Systems + +bluetooth:v02A2* + ID_VENDOR_FROM_DATABASE=LockedUp + +bluetooth:v02A3* + ID_VENDOR_FROM_DATABASE=Itude + +bluetooth:v02A4* + ID_VENDOR_FROM_DATABASE=Pacific Lock Company + +bluetooth:v02A5* + ID_VENDOR_FROM_DATABASE=Tendyron Corporation ( 天地融科技股份有限公司 ) + +bluetooth:v02A6* + ID_VENDOR_FROM_DATABASE=Robert Bosch GmbH + +bluetooth:v02A7* + ID_VENDOR_FROM_DATABASE=Illuxtron international B.V. + +bluetooth:v02A8* + ID_VENDOR_FROM_DATABASE=miSport Ltd. + +bluetooth:v02A9* + ID_VENDOR_FROM_DATABASE=Chargelib + +bluetooth:v02AA* + ID_VENDOR_FROM_DATABASE=Doppler Lab + +bluetooth:v02AB* + ID_VENDOR_FROM_DATABASE=BBPOS Limited + +bluetooth:v02AC* + ID_VENDOR_FROM_DATABASE=RTB Elektronik GmbH & Co. KG + +bluetooth:v02AD* + ID_VENDOR_FROM_DATABASE=Rx Networks, Inc. + +bluetooth:v02AE* + ID_VENDOR_FROM_DATABASE=WeatherFlow, Inc. + +bluetooth:v02AF* + ID_VENDOR_FROM_DATABASE=Technicolor USA Inc. + +bluetooth:v02B0* + ID_VENDOR_FROM_DATABASE=Bestechnic(Shanghai),Ltd + +bluetooth:v02B1* + ID_VENDOR_FROM_DATABASE=Raden Inc + +bluetooth:v02B2* + ID_VENDOR_FROM_DATABASE=JouZen Oy + +bluetooth:v02B3* + ID_VENDOR_FROM_DATABASE=CLABER S.P.A. + +bluetooth:v02B4* + ID_VENDOR_FROM_DATABASE=Hyginex, Inc. + +bluetooth:v02B5* + ID_VENDOR_FROM_DATABASE=HANSHIN ELECTRIC RAILWAY CO.,LTD. + +bluetooth:v02B6* + ID_VENDOR_FROM_DATABASE=Schneider Electric + +bluetooth:v02B7* + ID_VENDOR_FROM_DATABASE=Oort Technologies LLC + +bluetooth:v02B8* + ID_VENDOR_FROM_DATABASE=Chrono Therapeutics + +bluetooth:v02B9* + ID_VENDOR_FROM_DATABASE=Rinnai Corporation + +bluetooth:v02BA* + ID_VENDOR_FROM_DATABASE=Swissprime Technologies AG + +bluetooth:v02BB* + ID_VENDOR_FROM_DATABASE=Koha.,Co.Ltd + +bluetooth:v02BC* + ID_VENDOR_FROM_DATABASE=Genevac Ltd + +bluetooth:v02BD* + ID_VENDOR_FROM_DATABASE=Chemtronics + +bluetooth:v02BE* + ID_VENDOR_FROM_DATABASE=Seguro Technology Sp. z o.o. + +bluetooth:v02BF* + ID_VENDOR_FROM_DATABASE=Redbird Flight Simulations + +bluetooth:v02C0* + ID_VENDOR_FROM_DATABASE=Dash Robotics + +bluetooth:v02C1* + ID_VENDOR_FROM_DATABASE=LINE Corporation + +bluetooth:v02C2* + ID_VENDOR_FROM_DATABASE=Guillemot Corporation + +bluetooth:v02C3* + ID_VENDOR_FROM_DATABASE=Techtronic Power Tools Technology Limited + +bluetooth:v02C4* + ID_VENDOR_FROM_DATABASE=Wilson Sporting Goods + +bluetooth:v02C5* + ID_VENDOR_FROM_DATABASE=Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) ) + +bluetooth:v02C6* + ID_VENDOR_FROM_DATABASE=Ayatan Sensors + +bluetooth:v02C7* + ID_VENDOR_FROM_DATABASE=Electronics Tomorrow Limited + +bluetooth:v02C8* + ID_VENDOR_FROM_DATABASE=VASCO Data Security International, Inc. + +bluetooth:v02C9* + ID_VENDOR_FROM_DATABASE=PayRange Inc. + +bluetooth:v02CA* + ID_VENDOR_FROM_DATABASE=ABOV Semiconductor + +bluetooth:v02CB* + ID_VENDOR_FROM_DATABASE=AINA-Wireless Inc. + +bluetooth:v02CC* + ID_VENDOR_FROM_DATABASE=Eijkelkamp Soil & Water + +bluetooth:v02CD* + ID_VENDOR_FROM_DATABASE=BMA ergonomics b.v. + +bluetooth:v02CE* + ID_VENDOR_FROM_DATABASE=Teva Branded Pharmaceutical Products R&D, Inc. + +bluetooth:v02CF* + ID_VENDOR_FROM_DATABASE=Anima + +bluetooth:v02D0* + ID_VENDOR_FROM_DATABASE=3M + +bluetooth:v02D1* + ID_VENDOR_FROM_DATABASE=Empatica Srl + +bluetooth:v02D2* + ID_VENDOR_FROM_DATABASE=Afero, Inc. + +bluetooth:v02D3* + ID_VENDOR_FROM_DATABASE=Powercast Corporation + +bluetooth:v02D4* + ID_VENDOR_FROM_DATABASE=Secuyou ApS + +bluetooth:v02D5* + ID_VENDOR_FROM_DATABASE=OMRON Corporation + +bluetooth:v02D6* + ID_VENDOR_FROM_DATABASE=Send Solutions + +bluetooth:v02D7* + ID_VENDOR_FROM_DATABASE=NIPPON SYSTEMWARE CO.,LTD. + +bluetooth:v02D8* + ID_VENDOR_FROM_DATABASE=Neosfar + +bluetooth:v02D9* + ID_VENDOR_FROM_DATABASE=Fliegl Agrartechnik GmbH + +bluetooth:v02DA* + ID_VENDOR_FROM_DATABASE=Gilvader + +bluetooth:v02DB* + ID_VENDOR_FROM_DATABASE=Digi International Inc (R) + +bluetooth:v02DC* + ID_VENDOR_FROM_DATABASE=DeWalch Technologies, Inc. + +bluetooth:v02DD* + ID_VENDOR_FROM_DATABASE=Flint Rehabilitation Devices, LLC + +bluetooth:v02DE* + ID_VENDOR_FROM_DATABASE=Samsung SDS Co., Ltd. + +bluetooth:v02DF* + ID_VENDOR_FROM_DATABASE=Blur Product Development + +bluetooth:v02E0* + ID_VENDOR_FROM_DATABASE=University of Michigan + +bluetooth:v02E1* + ID_VENDOR_FROM_DATABASE=Victron Energy BV + +bluetooth:v02E2* + ID_VENDOR_FROM_DATABASE=NTT docomo + +bluetooth:v02E3* + ID_VENDOR_FROM_DATABASE=Carmanah Technologies Corp. + +bluetooth:v02E4* + ID_VENDOR_FROM_DATABASE=Bytestorm Ltd. + +bluetooth:v02E5* + ID_VENDOR_FROM_DATABASE=Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 ) + +bluetooth:v02E6* + ID_VENDOR_FROM_DATABASE=Unwire + +bluetooth:v02E7* + ID_VENDOR_FROM_DATABASE=Connected Yard, Inc. + +bluetooth:v02E8* + ID_VENDOR_FROM_DATABASE=American Music Environments + +bluetooth:v02E9* + ID_VENDOR_FROM_DATABASE=Sensogram Technologies, Inc. + +bluetooth:v02EA* + ID_VENDOR_FROM_DATABASE=Fujitsu Limited + +bluetooth:v02EB* + ID_VENDOR_FROM_DATABASE=Ardic Technology + +bluetooth:v02EC* + ID_VENDOR_FROM_DATABASE=Delta Systems, Inc + +bluetooth:v02ED* + ID_VENDOR_FROM_DATABASE=HTC Corporation + +bluetooth:v02EE* + ID_VENDOR_FROM_DATABASE=Citizen Holdings Co., Ltd. + +bluetooth:v02EF* + ID_VENDOR_FROM_DATABASE=SMART-INNOVATION.inc + +bluetooth:v02F0* + ID_VENDOR_FROM_DATABASE=Blackrat Software + +bluetooth:v02F1* + ID_VENDOR_FROM_DATABASE=The Idea Cave, LLC + +bluetooth:v02F2* + ID_VENDOR_FROM_DATABASE=GoPro, Inc. + +bluetooth:v02F3* + ID_VENDOR_FROM_DATABASE=AuthAir, Inc + +bluetooth:v02F4* + ID_VENDOR_FROM_DATABASE=Vensi, Inc. + +bluetooth:v02F5* + ID_VENDOR_FROM_DATABASE=Indagem Tech LLC + +bluetooth:v02F6* + ID_VENDOR_FROM_DATABASE=Intemo Technologies + +bluetooth:v02F7* + ID_VENDOR_FROM_DATABASE=DreamVisions co., Ltd. + +bluetooth:v02F8* + ID_VENDOR_FROM_DATABASE=Runteq Oy Ltd + +bluetooth:v02F9* + ID_VENDOR_FROM_DATABASE=IMAGINATION TECHNOLOGIES LTD + +bluetooth:v02FA* + ID_VENDOR_FROM_DATABASE=CoSTAR Technologies + +bluetooth:v02FB* + ID_VENDOR_FROM_DATABASE=Clarius Mobile Health Corp. + +bluetooth:v02FC* + ID_VENDOR_FROM_DATABASE=Shanghai Frequen Microelectronics Co., Ltd. + +bluetooth:v02FD* + ID_VENDOR_FROM_DATABASE=Uwanna, Inc. + +bluetooth:v02FE* + ID_VENDOR_FROM_DATABASE=Lierda Science & Technology Group Co., Ltd. + +bluetooth:v02FF* + ID_VENDOR_FROM_DATABASE=Silicon Laboratories + +bluetooth:v0300* + ID_VENDOR_FROM_DATABASE=World Moto Inc. + +bluetooth:v0301* + ID_VENDOR_FROM_DATABASE=Giatec Scientific Inc. + +bluetooth:v0302* + ID_VENDOR_FROM_DATABASE=Loop Devices, Inc + +bluetooth:v0303* + ID_VENDOR_FROM_DATABASE=IACA electronique + +bluetooth:v0304* + ID_VENDOR_FROM_DATABASE=Martians Inc + +bluetooth:v0305* + ID_VENDOR_FROM_DATABASE=Swipp ApS + +bluetooth:v0306* + ID_VENDOR_FROM_DATABASE=Life Laboratory Inc. + +bluetooth:v0307* + ID_VENDOR_FROM_DATABASE=FUJI INDUSTRIAL CO.,LTD. + +bluetooth:v0308* + ID_VENDOR_FROM_DATABASE=Surefire, LLC + +bluetooth:v0309* + ID_VENDOR_FROM_DATABASE=Dolby Labs + +bluetooth:v030A* + ID_VENDOR_FROM_DATABASE=Ellisys + +bluetooth:v030B* + ID_VENDOR_FROM_DATABASE=Magnitude Lighting Converters + +bluetooth:v030C* + ID_VENDOR_FROM_DATABASE=Hilti AG + +bluetooth:v030D* + ID_VENDOR_FROM_DATABASE=Devdata S.r.l. + +bluetooth:v030E* + ID_VENDOR_FROM_DATABASE=Deviceworx + +bluetooth:v030F* + ID_VENDOR_FROM_DATABASE=Shortcut Labs + +bluetooth:v0310* + ID_VENDOR_FROM_DATABASE=SGL Italia S.r.l. + +bluetooth:v0311* + ID_VENDOR_FROM_DATABASE=PEEQ DATA + +bluetooth:v0312* + ID_VENDOR_FROM_DATABASE=Ducere Technologies Pvt Ltd + +bluetooth:v0313* + ID_VENDOR_FROM_DATABASE=DiveNav, Inc. + +bluetooth:v0314* + ID_VENDOR_FROM_DATABASE=RIIG AI Sp. z o.o. + +bluetooth:v0315* + ID_VENDOR_FROM_DATABASE=Thermo Fisher Scientific + +bluetooth:v0316* + ID_VENDOR_FROM_DATABASE=AG Measurematics Pvt. Ltd. + +bluetooth:v0317* + ID_VENDOR_FROM_DATABASE=CHUO Electronics CO., LTD. + +bluetooth:v0318* + ID_VENDOR_FROM_DATABASE=Aspenta International + +bluetooth:v0319* + ID_VENDOR_FROM_DATABASE=Eugster Frismag AG + +bluetooth:v031A* + ID_VENDOR_FROM_DATABASE=Amber wireless GmbH + +bluetooth:v031B* + ID_VENDOR_FROM_DATABASE=HQ Inc + +bluetooth:v031C* + ID_VENDOR_FROM_DATABASE=Lab Sensor Solutions + +bluetooth:v031D* + ID_VENDOR_FROM_DATABASE=Enterlab ApS + +bluetooth:v031E* + ID_VENDOR_FROM_DATABASE=Eyefi, Inc. + +bluetooth:v031F* + ID_VENDOR_FROM_DATABASE=MetaSystem S.p.A + +bluetooth:v0320* + ID_VENDOR_FROM_DATABASE=SONO ELECTRONICS. CO., LTD + +bluetooth:v0321* + ID_VENDOR_FROM_DATABASE=Jewelbots + +bluetooth:v0322* + ID_VENDOR_FROM_DATABASE=Compumedics Limited + +bluetooth:v0323* + ID_VENDOR_FROM_DATABASE=Rotor Bike Components + +bluetooth:v0324* + ID_VENDOR_FROM_DATABASE=Astro, Inc. + +bluetooth:v0325* + ID_VENDOR_FROM_DATABASE=Amotus Solutions + +bluetooth:v0326* + ID_VENDOR_FROM_DATABASE=Healthwear Technologies (Changzhou)Ltd + +bluetooth:v0327* + ID_VENDOR_FROM_DATABASE=Essex Electronics + +bluetooth:v0328* + ID_VENDOR_FROM_DATABASE=Grundfos A/S + +bluetooth:v0329* + ID_VENDOR_FROM_DATABASE=Eargo, Inc. + +bluetooth:v032A* + ID_VENDOR_FROM_DATABASE=Electronic Design Lab + +bluetooth:v032B* + ID_VENDOR_FROM_DATABASE=ESYLUX + +bluetooth:v032C* + ID_VENDOR_FROM_DATABASE=NIPPON SMT.CO.,Ltd + +bluetooth:v032D* + ID_VENDOR_FROM_DATABASE=BM innovations GmbH + +bluetooth:v032E* + ID_VENDOR_FROM_DATABASE=indoormap + +bluetooth:v032F* + ID_VENDOR_FROM_DATABASE=OttoQ Inc + +bluetooth:v0330* + ID_VENDOR_FROM_DATABASE=North Pole Engineering + +bluetooth:v0331* + ID_VENDOR_FROM_DATABASE=3flares Technologies Inc. + +bluetooth:v0332* + ID_VENDOR_FROM_DATABASE=Electrocompaniet A.S. + +bluetooth:v0333* + ID_VENDOR_FROM_DATABASE=Mul-T-Lock + +bluetooth:v0334* + ID_VENDOR_FROM_DATABASE=Corentium AS + +bluetooth:v0335* + ID_VENDOR_FROM_DATABASE=Enlighted Inc + +bluetooth:v0336* + ID_VENDOR_FROM_DATABASE=GISTIC + +bluetooth:v0337* + ID_VENDOR_FROM_DATABASE=AJP2 Holdings, LLC + +bluetooth:v0338* + ID_VENDOR_FROM_DATABASE=COBI GmbH + +bluetooth:v0339* + ID_VENDOR_FROM_DATABASE=Blue Sky Scientific, LLC + +bluetooth:v033A* + ID_VENDOR_FROM_DATABASE=Appception, Inc. + +bluetooth:v033B* + ID_VENDOR_FROM_DATABASE=Courtney Thorne Limited + +bluetooth:v033C* + ID_VENDOR_FROM_DATABASE=Virtuosys + +bluetooth:v033D* + ID_VENDOR_FROM_DATABASE=TPV Technology Limited + +bluetooth:v033E* + ID_VENDOR_FROM_DATABASE=Monitra SA + +bluetooth:v033F* + ID_VENDOR_FROM_DATABASE=Automation Components, Inc. + +bluetooth:v0340* + ID_VENDOR_FROM_DATABASE=Letsense s.r.l. + +bluetooth:v0341* + ID_VENDOR_FROM_DATABASE=Etesian Technologies LLC + +bluetooth:v0342* + ID_VENDOR_FROM_DATABASE=GERTEC BRASIL LTDA. + +bluetooth:v0343* + ID_VENDOR_FROM_DATABASE=Drekker Development Pty. Ltd. + +bluetooth:v0344* + ID_VENDOR_FROM_DATABASE=Whirl Inc + +bluetooth:v0345* + ID_VENDOR_FROM_DATABASE=Locus Positioning + +bluetooth:v0346* + ID_VENDOR_FROM_DATABASE=Acuity Brands Lighting, Inc + +bluetooth:v0347* + ID_VENDOR_FROM_DATABASE=Prevent Biometrics + +bluetooth:v0348* + ID_VENDOR_FROM_DATABASE=Arioneo + +bluetooth:v0349* + ID_VENDOR_FROM_DATABASE=VersaMe + +bluetooth:v034A* + ID_VENDOR_FROM_DATABASE=Vaddio + +bluetooth:v034B* + ID_VENDOR_FROM_DATABASE=Libratone A/S + +bluetooth:v034C* + ID_VENDOR_FROM_DATABASE=HM Electronics, Inc. + +bluetooth:v034D* + ID_VENDOR_FROM_DATABASE=TASER International, Inc. + +bluetooth:v034E* + ID_VENDOR_FROM_DATABASE=Safe Trust Inc. + +bluetooth:v034F* + ID_VENDOR_FROM_DATABASE=Heartland Payment Systems + +bluetooth:v0350* + ID_VENDOR_FROM_DATABASE=Bitstrata Systems Inc. + +bluetooth:v0351* + ID_VENDOR_FROM_DATABASE=Pieps GmbH + +bluetooth:v0352* + ID_VENDOR_FROM_DATABASE=iRiding(Xiamen)Technology Co.,Ltd. + +bluetooth:v0353* + ID_VENDOR_FROM_DATABASE=Alpha Audiotronics, Inc. + +bluetooth:v0354* + ID_VENDOR_FROM_DATABASE=TOPPAN FORMS CO.,LTD. + +bluetooth:v0355* + ID_VENDOR_FROM_DATABASE=Sigma Designs, Inc. + +bluetooth:v0356* + ID_VENDOR_FROM_DATABASE=Spectrum Brands, Inc. + +bluetooth:v0357* + ID_VENDOR_FROM_DATABASE=Polymap Wireless + +bluetooth:v0358* + ID_VENDOR_FROM_DATABASE=MagniWare Ltd. + +bluetooth:v0359* + ID_VENDOR_FROM_DATABASE=Novotec Medical GmbH + +bluetooth:v035A* + ID_VENDOR_FROM_DATABASE=Medicom Innovation Partner a/s + +bluetooth:v035B* + ID_VENDOR_FROM_DATABASE=Matrix Inc. + +bluetooth:v035C* + ID_VENDOR_FROM_DATABASE=Eaton Corporation + +bluetooth:v035D* + ID_VENDOR_FROM_DATABASE=KYS + +bluetooth:v035E* + ID_VENDOR_FROM_DATABASE=Naya Health, Inc. + +bluetooth:v035F* + ID_VENDOR_FROM_DATABASE=Acromag + +bluetooth:v0360* + ID_VENDOR_FROM_DATABASE=Insulet Corporation + +bluetooth:v0361* + ID_VENDOR_FROM_DATABASE=Wellinks Inc. + +bluetooth:v0362* + ID_VENDOR_FROM_DATABASE=ON Semiconductor + +bluetooth:v0363* + ID_VENDOR_FROM_DATABASE=FREELAP SA + +bluetooth:v0364* + ID_VENDOR_FROM_DATABASE=Favero Electronics Srl + +bluetooth:v0365* + ID_VENDOR_FROM_DATABASE=BioMech Sensor LLC + +bluetooth:v0366* + ID_VENDOR_FROM_DATABASE=BOLTT Sports technologies Private limited + +bluetooth:v0367* + ID_VENDOR_FROM_DATABASE=Saphe International + +bluetooth:v0368* + ID_VENDOR_FROM_DATABASE=Metormote AB + +bluetooth:v0369* + ID_VENDOR_FROM_DATABASE=littleBits + +bluetooth:v036A* + ID_VENDOR_FROM_DATABASE=SetPoint Medical + +bluetooth:v036B* + ID_VENDOR_FROM_DATABASE=BRControls Products BV + +bluetooth:v036C* + ID_VENDOR_FROM_DATABASE=Zipcar + +bluetooth:v036D* + ID_VENDOR_FROM_DATABASE=AirBolt Pty Ltd + +bluetooth:v036E* + ID_VENDOR_FROM_DATABASE=KeepTruckin Inc + +bluetooth:v036F* + ID_VENDOR_FROM_DATABASE=Motiv, Inc. + +bluetooth:v0370* + ID_VENDOR_FROM_DATABASE=Wazombi Labs OÜ + +bluetooth:v0371* + ID_VENDOR_FROM_DATABASE=ORBCOMM + +bluetooth:v0372* + ID_VENDOR_FROM_DATABASE=Nixie Labs, Inc. + +bluetooth:v0373* + ID_VENDOR_FROM_DATABASE=AppNearMe Ltd + +bluetooth:v0374* + ID_VENDOR_FROM_DATABASE=Holman Industries + +bluetooth:v0375* + ID_VENDOR_FROM_DATABASE=Expain AS + +bluetooth:v0376* + ID_VENDOR_FROM_DATABASE=Electronic Temperature Instruments Ltd + +bluetooth:v0377* + ID_VENDOR_FROM_DATABASE=Plejd AB + +bluetooth:v0378* + ID_VENDOR_FROM_DATABASE=Propeller Health + +bluetooth:v0379* + ID_VENDOR_FROM_DATABASE=Shenzhen iMCO Electronic Technology Co.,Ltd + +bluetooth:v037A* + ID_VENDOR_FROM_DATABASE=Algoria + +bluetooth:v037B* + ID_VENDOR_FROM_DATABASE=Apption Labs Inc. + +bluetooth:v037C* + ID_VENDOR_FROM_DATABASE=Cronologics Corporation + +bluetooth:v037D* + ID_VENDOR_FROM_DATABASE=MICRODIA Ltd. + +bluetooth:v037E* + ID_VENDOR_FROM_DATABASE=lulabytes S.L. + +bluetooth:v037F* + ID_VENDOR_FROM_DATABASE=Nestec S.A. + +bluetooth:v0380* + ID_VENDOR_FROM_DATABASE=LLC "MEGA-F service" + +bluetooth:v0381* + ID_VENDOR_FROM_DATABASE=Sharp Corporation + +bluetooth:v0382* + ID_VENDOR_FROM_DATABASE=Precision Outcomes Ltd + +bluetooth:v0383* + ID_VENDOR_FROM_DATABASE=Kronos Incorporated + +bluetooth:v0384* + ID_VENDOR_FROM_DATABASE=OCOSMOS Co., Ltd. + +bluetooth:v0385* + ID_VENDOR_FROM_DATABASE=Embedded Electronic Solutions Ltd. dba e2Solutions + +bluetooth:v0386* + ID_VENDOR_FROM_DATABASE=Aterica Inc. + +bluetooth:v0387* + ID_VENDOR_FROM_DATABASE=BluStor PMC, Inc. + +bluetooth:v0388* + ID_VENDOR_FROM_DATABASE=Kapsch TrafficCom AB + +bluetooth:v0389* + ID_VENDOR_FROM_DATABASE=ActiveBlu Corporation + +bluetooth:v038A* + ID_VENDOR_FROM_DATABASE=Kohler Mira Limited + +bluetooth:v038B* + ID_VENDOR_FROM_DATABASE=Noke + +bluetooth:v038C* + ID_VENDOR_FROM_DATABASE=Appion Inc. + +bluetooth:v038D* + ID_VENDOR_FROM_DATABASE=Resmed Ltd + +bluetooth:v038E* + ID_VENDOR_FROM_DATABASE=Crownstone B.V. + +bluetooth:v038F* + ID_VENDOR_FROM_DATABASE=Xiaomi Inc. + +bluetooth:v0390* + ID_VENDOR_FROM_DATABASE=INFOTECH s.r.o. + +bluetooth:v0391* + ID_VENDOR_FROM_DATABASE=Thingsquare AB + +bluetooth:v0392* + ID_VENDOR_FROM_DATABASE=T&D + +bluetooth:v0393* + ID_VENDOR_FROM_DATABASE=LAVAZZA S.p.A. + +bluetooth:v0394* + ID_VENDOR_FROM_DATABASE=Netclearance Systems, Inc. + +bluetooth:v0395* + ID_VENDOR_FROM_DATABASE=SDATAWAY + +bluetooth:v0396* + ID_VENDOR_FROM_DATABASE=BLOKS GmbH + +bluetooth:v0397* + ID_VENDOR_FROM_DATABASE=LEGO System A/S + +bluetooth:v0398* + ID_VENDOR_FROM_DATABASE=Thetatronics Ltd + +bluetooth:v0399* + ID_VENDOR_FROM_DATABASE=Nikon Corporation + +bluetooth:v039A* + ID_VENDOR_FROM_DATABASE=NeST + +bluetooth:v039B* + ID_VENDOR_FROM_DATABASE=South Silicon Valley Microelectronics + +bluetooth:v039C* + ID_VENDOR_FROM_DATABASE=ALE International + +bluetooth:v039D* + ID_VENDOR_FROM_DATABASE=CareView Communications, Inc. + +bluetooth:v039E* + ID_VENDOR_FROM_DATABASE=SchoolBoard Limited + +bluetooth:v039F* + ID_VENDOR_FROM_DATABASE=Molex Corporation + +bluetooth:v03A0* + ID_VENDOR_FROM_DATABASE=IVT Wireless Limited + +bluetooth:v03A1* + ID_VENDOR_FROM_DATABASE=Alpine Labs LLC + +bluetooth:v03A2* + ID_VENDOR_FROM_DATABASE=Candura Instruments + +bluetooth:v03A3* + ID_VENDOR_FROM_DATABASE=SmartMovt Technology Co., Ltd + +bluetooth:v03A4* + ID_VENDOR_FROM_DATABASE=Token Zero Ltd + +bluetooth:v03A5* + ID_VENDOR_FROM_DATABASE=ACE CAD Enterprise Co., Ltd. (ACECAD) + +bluetooth:v03A6* + ID_VENDOR_FROM_DATABASE=Medela, Inc + +bluetooth:v03A7* + ID_VENDOR_FROM_DATABASE=AeroScout + +bluetooth:v03A8* + ID_VENDOR_FROM_DATABASE=Esrille Inc. + +bluetooth:v03A9* + ID_VENDOR_FROM_DATABASE=THINKERLY SRL + +bluetooth:v03AA* + ID_VENDOR_FROM_DATABASE=Exon Sp. z o.o. + +bluetooth:v03AB* + ID_VENDOR_FROM_DATABASE=Meizu Technology Co., Ltd. + +bluetooth:v03AC* + ID_VENDOR_FROM_DATABASE=Smablo LTD diff --git a/src/grp-udev/hwdb/20-net-ifname.hwdb b/src/grp-udev/hwdb/20-net-ifname.hwdb new file mode 100644 index 0000000000..2408dc172f --- /dev/null +++ b/src/grp-udev/hwdb/20-net-ifname.hwdb @@ -0,0 +1,5 @@ +# This file is part of systemd. + +# Dell iDRAC Virtual USB NIC +usb:v413CpA102* + ID_NET_NAME_FROM_DATABASE=idrac diff --git a/src/grp-udev/hwdb/20-pci-classes.hwdb b/src/grp-udev/hwdb/20-pci-classes.hwdb new file mode 100644 index 0000000000..3c0c465e5f --- /dev/null +++ b/src/grp-udev/hwdb/20-pci-classes.hwdb @@ -0,0 +1,564 @@ +# This file is part of systemd. +# +# Data imported from: http://pci-ids.ucw.cz/v2.2/pci.ids + +pci:v*d*sv*sd*bc00* + ID_PCI_CLASS_FROM_DATABASE=Unclassified device + +pci:v*d*sv*sd*bc00sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Non-VGA unclassified device + +pci:v*d*sv*sd*bc00sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible unclassified device + +pci:v*d*sv*sd*bc01* + ID_PCI_CLASS_FROM_DATABASE=Mass storage controller + +pci:v*d*sv*sd*bc01sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=SCSI storage controller + +pci:v*d*sv*sd*bc01sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=IDE interface + +pci:v*d*sv*sd*bc01sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Floppy disk controller + +pci:v*d*sv*sd*bc01sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=IPI bus controller + +pci:v*d*sv*sd*bc01sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=RAID bus controller + +pci:v*d*sv*sd*bc01sc05* + ID_PCI_SUBCLASS_FROM_DATABASE=ATA controller + +pci:v*d*sv*sd*bc01sc05i20* + ID_PCI_INTERFACE_FROM_DATABASE=ADMA single stepping + +pci:v*d*sv*sd*bc01sc05i30* + ID_PCI_INTERFACE_FROM_DATABASE=ADMA continuous operation + +pci:v*d*sv*sd*bc01sc06* + ID_PCI_SUBCLASS_FROM_DATABASE=SATA controller + +pci:v*d*sv*sd*bc01sc06i00* + ID_PCI_INTERFACE_FROM_DATABASE=Vendor specific + +pci:v*d*sv*sd*bc01sc06i01* + ID_PCI_INTERFACE_FROM_DATABASE=AHCI 1.0 + +pci:v*d*sv*sd*bc01sc06i02* + ID_PCI_INTERFACE_FROM_DATABASE=Serial Storage Bus + +pci:v*d*sv*sd*bc01sc07* + ID_PCI_SUBCLASS_FROM_DATABASE=Serial Attached SCSI controller + +pci:v*d*sv*sd*bc01sc07i01* + ID_PCI_INTERFACE_FROM_DATABASE=Serial Storage Bus + +pci:v*d*sv*sd*bc01sc08* + ID_PCI_SUBCLASS_FROM_DATABASE=Non-Volatile memory controller + +pci:v*d*sv*sd*bc01sc08i01* + ID_PCI_INTERFACE_FROM_DATABASE=NVMHCI + +pci:v*d*sv*sd*bc01sc08i02* + ID_PCI_INTERFACE_FROM_DATABASE=NVM Express + +pci:v*d*sv*sd*bc01sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Mass storage controller + +pci:v*d*sv*sd*bc02* + ID_PCI_CLASS_FROM_DATABASE=Network controller + +pci:v*d*sv*sd*bc02sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Ethernet controller + +pci:v*d*sv*sd*bc02sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Token ring network controller + +pci:v*d*sv*sd*bc02sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=FDDI network controller + +pci:v*d*sv*sd*bc02sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=ATM network controller + +pci:v*d*sv*sd*bc02sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=ISDN controller + +pci:v*d*sv*sd*bc02sc05* + ID_PCI_SUBCLASS_FROM_DATABASE=WorldFip controller + +pci:v*d*sv*sd*bc02sc06* + ID_PCI_SUBCLASS_FROM_DATABASE=PICMG controller + +pci:v*d*sv*sd*bc02sc07* + ID_PCI_SUBCLASS_FROM_DATABASE=Infiniband controller + +pci:v*d*sv*sd*bc02sc08* + ID_PCI_SUBCLASS_FROM_DATABASE=Fabric controller + +pci:v*d*sv*sd*bc02sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Network controller + +pci:v*d*sv*sd*bc03* + ID_PCI_CLASS_FROM_DATABASE=Display controller + +pci:v*d*sv*sd*bc03sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller + +pci:v*d*sv*sd*bc03sc00i00* + ID_PCI_INTERFACE_FROM_DATABASE=VGA controller + +pci:v*d*sv*sd*bc03sc00i01* + ID_PCI_INTERFACE_FROM_DATABASE=8514 controller + +pci:v*d*sv*sd*bc03sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=XGA compatible controller + +pci:v*d*sv*sd*bc03sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=3D controller + +pci:v*d*sv*sd*bc03sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Display controller + +pci:v*d*sv*sd*bc04* + ID_PCI_CLASS_FROM_DATABASE=Multimedia controller + +pci:v*d*sv*sd*bc04sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Multimedia video controller + +pci:v*d*sv*sd*bc04sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Multimedia audio controller + +pci:v*d*sv*sd*bc04sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Computer telephony device + +pci:v*d*sv*sd*bc04sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=Audio device + +pci:v*d*sv*sd*bc04sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Multimedia controller + +pci:v*d*sv*sd*bc05* + ID_PCI_CLASS_FROM_DATABASE=Memory controller + +pci:v*d*sv*sd*bc05sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=RAM memory + +pci:v*d*sv*sd*bc05sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=FLASH memory + +pci:v*d*sv*sd*bc05sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Memory controller + +pci:v*d*sv*sd*bc06* + ID_PCI_CLASS_FROM_DATABASE=Bridge + +pci:v*d*sv*sd*bc06sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Host bridge + +pci:v*d*sv*sd*bc06sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=ISA bridge + +pci:v*d*sv*sd*bc06sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=EISA bridge + +pci:v*d*sv*sd*bc06sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=MicroChannel bridge + +pci:v*d*sv*sd*bc06sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge + +pci:v*d*sv*sd*bc06sc04i00* + ID_PCI_INTERFACE_FROM_DATABASE=Normal decode + +pci:v*d*sv*sd*bc06sc04i01* + ID_PCI_INTERFACE_FROM_DATABASE=Subtractive decode + +pci:v*d*sv*sd*bc06sc05* + ID_PCI_SUBCLASS_FROM_DATABASE=PCMCIA bridge + +pci:v*d*sv*sd*bc06sc06* + ID_PCI_SUBCLASS_FROM_DATABASE=NuBus bridge + +pci:v*d*sv*sd*bc06sc07* + ID_PCI_SUBCLASS_FROM_DATABASE=CardBus bridge + +pci:v*d*sv*sd*bc06sc08* + ID_PCI_SUBCLASS_FROM_DATABASE=RACEway bridge + +pci:v*d*sv*sd*bc06sc08i00* + ID_PCI_INTERFACE_FROM_DATABASE=Transparent mode + +pci:v*d*sv*sd*bc06sc08i01* + ID_PCI_INTERFACE_FROM_DATABASE=Endpoint mode + +pci:v*d*sv*sd*bc06sc09* + ID_PCI_SUBCLASS_FROM_DATABASE=Semi-transparent PCI-to-PCI bridge + +pci:v*d*sv*sd*bc06sc09i40* + ID_PCI_INTERFACE_FROM_DATABASE=Primary bus towards host CPU + +pci:v*d*sv*sd*bc06sc09i80* + ID_PCI_INTERFACE_FROM_DATABASE=Secondary bus towards host CPU + +pci:v*d*sv*sd*bc06sc0A* + ID_PCI_SUBCLASS_FROM_DATABASE=InfiniBand to PCI host bridge + +pci:v*d*sv*sd*bc06sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Bridge + +pci:v*d*sv*sd*bc07* + ID_PCI_CLASS_FROM_DATABASE=Communication controller + +pci:v*d*sv*sd*bc07sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Serial controller + +pci:v*d*sv*sd*bc07sc00i00* + ID_PCI_INTERFACE_FROM_DATABASE=8250 + +pci:v*d*sv*sd*bc07sc00i01* + ID_PCI_INTERFACE_FROM_DATABASE=16450 + +pci:v*d*sv*sd*bc07sc00i02* + ID_PCI_INTERFACE_FROM_DATABASE=16550 + +pci:v*d*sv*sd*bc07sc00i03* + ID_PCI_INTERFACE_FROM_DATABASE=16650 + +pci:v*d*sv*sd*bc07sc00i04* + ID_PCI_INTERFACE_FROM_DATABASE=16750 + +pci:v*d*sv*sd*bc07sc00i05* + ID_PCI_INTERFACE_FROM_DATABASE=16850 + +pci:v*d*sv*sd*bc07sc00i06* + ID_PCI_INTERFACE_FROM_DATABASE=16950 + +pci:v*d*sv*sd*bc07sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Parallel controller + +pci:v*d*sv*sd*bc07sc01i00* + ID_PCI_INTERFACE_FROM_DATABASE=SPP + +pci:v*d*sv*sd*bc07sc01i01* + ID_PCI_INTERFACE_FROM_DATABASE=BiDir + +pci:v*d*sv*sd*bc07sc01i02* + ID_PCI_INTERFACE_FROM_DATABASE=ECP + +pci:v*d*sv*sd*bc07sc01i03* + ID_PCI_INTERFACE_FROM_DATABASE=IEEE1284 + +pci:v*d*sv*sd*bc07sc01iFE* + ID_PCI_INTERFACE_FROM_DATABASE=IEEE1284 Target + +pci:v*d*sv*sd*bc07sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Multiport serial controller + +pci:v*d*sv*sd*bc07sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=Modem + +pci:v*d*sv*sd*bc07sc03i00* + ID_PCI_INTERFACE_FROM_DATABASE=Generic + +pci:v*d*sv*sd*bc07sc03i01* + ID_PCI_INTERFACE_FROM_DATABASE=Hayes/16450 + +pci:v*d*sv*sd*bc07sc03i02* + ID_PCI_INTERFACE_FROM_DATABASE=Hayes/16550 + +pci:v*d*sv*sd*bc07sc03i03* + ID_PCI_INTERFACE_FROM_DATABASE=Hayes/16650 + +pci:v*d*sv*sd*bc07sc03i04* + ID_PCI_INTERFACE_FROM_DATABASE=Hayes/16750 + +pci:v*d*sv*sd*bc07sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=GPIB controller + +pci:v*d*sv*sd*bc07sc05* + ID_PCI_SUBCLASS_FROM_DATABASE=Smard Card controller + +pci:v*d*sv*sd*bc07sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Communication controller + +pci:v*d*sv*sd*bc08* + ID_PCI_CLASS_FROM_DATABASE=Generic system peripheral + +pci:v*d*sv*sd*bc08sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=PIC + +pci:v*d*sv*sd*bc08sc00i00* + ID_PCI_INTERFACE_FROM_DATABASE=8259 + +pci:v*d*sv*sd*bc08sc00i01* + ID_PCI_INTERFACE_FROM_DATABASE=ISA PIC + +pci:v*d*sv*sd*bc08sc00i02* + ID_PCI_INTERFACE_FROM_DATABASE=EISA PIC + +pci:v*d*sv*sd*bc08sc00i10* + ID_PCI_INTERFACE_FROM_DATABASE=IO-APIC + +pci:v*d*sv*sd*bc08sc00i20* + ID_PCI_INTERFACE_FROM_DATABASE=IO(X)-APIC + +pci:v*d*sv*sd*bc08sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=DMA controller + +pci:v*d*sv*sd*bc08sc01i00* + ID_PCI_INTERFACE_FROM_DATABASE=8237 + +pci:v*d*sv*sd*bc08sc01i01* + ID_PCI_INTERFACE_FROM_DATABASE=ISA DMA + +pci:v*d*sv*sd*bc08sc01i02* + ID_PCI_INTERFACE_FROM_DATABASE=EISA DMA + +pci:v*d*sv*sd*bc08sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Timer + +pci:v*d*sv*sd*bc08sc02i00* + ID_PCI_INTERFACE_FROM_DATABASE=8254 + +pci:v*d*sv*sd*bc08sc02i01* + ID_PCI_INTERFACE_FROM_DATABASE=ISA Timer + +pci:v*d*sv*sd*bc08sc02i02* + ID_PCI_INTERFACE_FROM_DATABASE=EISA Timers + +pci:v*d*sv*sd*bc08sc02i03* + ID_PCI_INTERFACE_FROM_DATABASE=HPET + +pci:v*d*sv*sd*bc08sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=RTC + +pci:v*d*sv*sd*bc08sc03i00* + ID_PCI_INTERFACE_FROM_DATABASE=Generic + +pci:v*d*sv*sd*bc08sc03i01* + ID_PCI_INTERFACE_FROM_DATABASE=ISA RTC + +pci:v*d*sv*sd*bc08sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=PCI Hot-plug controller + +pci:v*d*sv*sd*bc08sc05* + ID_PCI_SUBCLASS_FROM_DATABASE=SD Host controller + +pci:v*d*sv*sd*bc08sc06* + ID_PCI_SUBCLASS_FROM_DATABASE=IOMMU + +pci:v*d*sv*sd*bc08sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=System peripheral + +pci:v*d*sv*sd*bc09* + ID_PCI_CLASS_FROM_DATABASE=Input device controller + +pci:v*d*sv*sd*bc09sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Keyboard controller + +pci:v*d*sv*sd*bc09sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Digitizer Pen + +pci:v*d*sv*sd*bc09sc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Mouse controller + +pci:v*d*sv*sd*bc09sc03* + ID_PCI_SUBCLASS_FROM_DATABASE=Scanner controller + +pci:v*d*sv*sd*bc09sc04* + ID_PCI_SUBCLASS_FROM_DATABASE=Gameport controller + +pci:v*d*sv*sd*bc09sc04i00* + ID_PCI_INTERFACE_FROM_DATABASE=Generic + +pci:v*d*sv*sd*bc09sc04i10* + ID_PCI_INTERFACE_FROM_DATABASE=Extended + +pci:v*d*sv*sd*bc09sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Input device controller + +pci:v*d*sv*sd*bc0A* + ID_PCI_CLASS_FROM_DATABASE=Docking station + +pci:v*d*sv*sd*bc0Asc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Generic Docking Station + +pci:v*d*sv*sd*bc0Asc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Docking Station + +pci:v*d*sv*sd*bc0B* + ID_PCI_CLASS_FROM_DATABASE=Processor + +pci:v*d*sv*sd*bc0Bsc00* + ID_PCI_SUBCLASS_FROM_DATABASE=386 + +pci:v*d*sv*sd*bc0Bsc01* + ID_PCI_SUBCLASS_FROM_DATABASE=486 + +pci:v*d*sv*sd*bc0Bsc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Pentium + +pci:v*d*sv*sd*bc0Bsc10* + ID_PCI_SUBCLASS_FROM_DATABASE=Alpha + +pci:v*d*sv*sd*bc0Bsc20* + ID_PCI_SUBCLASS_FROM_DATABASE=Power PC + +pci:v*d*sv*sd*bc0Bsc30* + ID_PCI_SUBCLASS_FROM_DATABASE=MIPS + +pci:v*d*sv*sd*bc0Bsc40* + ID_PCI_SUBCLASS_FROM_DATABASE=Co-processor + +pci:v*d*sv*sd*bc0C* + ID_PCI_CLASS_FROM_DATABASE=Serial bus controller + +pci:v*d*sv*sd*bc0Csc00* + ID_PCI_SUBCLASS_FROM_DATABASE=FireWire (IEEE 1394) + +pci:v*d*sv*sd*bc0Csc00i00* + ID_PCI_INTERFACE_FROM_DATABASE=Generic + +pci:v*d*sv*sd*bc0Csc00i10* + ID_PCI_INTERFACE_FROM_DATABASE=OHCI + +pci:v*d*sv*sd*bc0Csc01* + ID_PCI_SUBCLASS_FROM_DATABASE=ACCESS Bus + +pci:v*d*sv*sd*bc0Csc02* + ID_PCI_SUBCLASS_FROM_DATABASE=SSA + +pci:v*d*sv*sd*bc0Csc03* + ID_PCI_SUBCLASS_FROM_DATABASE=USB controller + +pci:v*d*sv*sd*bc0Csc03i00* + ID_PCI_INTERFACE_FROM_DATABASE=UHCI + +pci:v*d*sv*sd*bc0Csc03i10* + ID_PCI_INTERFACE_FROM_DATABASE=OHCI + +pci:v*d*sv*sd*bc0Csc03i20* + ID_PCI_INTERFACE_FROM_DATABASE=EHCI + +pci:v*d*sv*sd*bc0Csc03i30* + ID_PCI_INTERFACE_FROM_DATABASE=XHCI + +pci:v*d*sv*sd*bc0Csc03i80* + ID_PCI_INTERFACE_FROM_DATABASE=Unspecified + +pci:v*d*sv*sd*bc0Csc03iFE* + ID_PCI_INTERFACE_FROM_DATABASE=USB Device + +pci:v*d*sv*sd*bc0Csc04* + ID_PCI_SUBCLASS_FROM_DATABASE=Fibre Channel + +pci:v*d*sv*sd*bc0Csc05* + ID_PCI_SUBCLASS_FROM_DATABASE=SMBus + +pci:v*d*sv*sd*bc0Csc06* + ID_PCI_SUBCLASS_FROM_DATABASE=InfiniBand + +pci:v*d*sv*sd*bc0Csc07* + ID_PCI_SUBCLASS_FROM_DATABASE=IPMI SMIC interface + +pci:v*d*sv*sd*bc0Csc08* + ID_PCI_SUBCLASS_FROM_DATABASE=SERCOS interface + +pci:v*d*sv*sd*bc0Csc09* + ID_PCI_SUBCLASS_FROM_DATABASE=CANBUS + +pci:v*d*sv*sd*bc0D* + ID_PCI_CLASS_FROM_DATABASE=Wireless controller + +pci:v*d*sv*sd*bc0Dsc00* + ID_PCI_SUBCLASS_FROM_DATABASE=IRDA controller + +pci:v*d*sv*sd*bc0Dsc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Consumer IR controller + +pci:v*d*sv*sd*bc0Dsc10* + ID_PCI_SUBCLASS_FROM_DATABASE=RF controller + +pci:v*d*sv*sd*bc0Dsc11* + ID_PCI_SUBCLASS_FROM_DATABASE=Bluetooth + +pci:v*d*sv*sd*bc0Dsc12* + ID_PCI_SUBCLASS_FROM_DATABASE=Broadband + +pci:v*d*sv*sd*bc0Dsc20* + ID_PCI_SUBCLASS_FROM_DATABASE=802.1a controller + +pci:v*d*sv*sd*bc0Dsc21* + ID_PCI_SUBCLASS_FROM_DATABASE=802.1b controller + +pci:v*d*sv*sd*bc0Dsc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Wireless controller + +pci:v*d*sv*sd*bc0E* + ID_PCI_CLASS_FROM_DATABASE=Intelligent controller + +pci:v*d*sv*sd*bc0Esc00* + ID_PCI_SUBCLASS_FROM_DATABASE=I2O + +pci:v*d*sv*sd*bc0F* + ID_PCI_CLASS_FROM_DATABASE=Satellite communications controller + +pci:v*d*sv*sd*bc0Fsc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Satellite TV controller + +pci:v*d*sv*sd*bc0Fsc02* + ID_PCI_SUBCLASS_FROM_DATABASE=Satellite audio communication controller + +pci:v*d*sv*sd*bc0Fsc03* + ID_PCI_SUBCLASS_FROM_DATABASE=Satellite voice communication controller + +pci:v*d*sv*sd*bc0Fsc04* + ID_PCI_SUBCLASS_FROM_DATABASE=Satellite data communication controller + +pci:v*d*sv*sd*bc10* + ID_PCI_CLASS_FROM_DATABASE=Encryption controller + +pci:v*d*sv*sd*bc10sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Network and computing encryption device + +pci:v*d*sv*sd*bc10sc10* + ID_PCI_SUBCLASS_FROM_DATABASE=Entertainment encryption device + +pci:v*d*sv*sd*bc10sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Encryption controller + +pci:v*d*sv*sd*bc11* + ID_PCI_CLASS_FROM_DATABASE=Signal processing controller + +pci:v*d*sv*sd*bc11sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=DPIO module + +pci:v*d*sv*sd*bc11sc01* + ID_PCI_SUBCLASS_FROM_DATABASE=Performance counters + +pci:v*d*sv*sd*bc11sc10* + ID_PCI_SUBCLASS_FROM_DATABASE=Communication synchronizer + +pci:v*d*sv*sd*bc11sc20* + ID_PCI_SUBCLASS_FROM_DATABASE=Signal processing management + +pci:v*d*sv*sd*bc11sc80* + ID_PCI_SUBCLASS_FROM_DATABASE=Signal processing controller + +pci:v*d*sv*sd*bc12* + ID_PCI_CLASS_FROM_DATABASE=Processing accelerators + +pci:v*d*sv*sd*bc12sc00* + ID_PCI_SUBCLASS_FROM_DATABASE=Processing accelerators + +pci:v*d*sv*sd*bc13* + ID_PCI_CLASS_FROM_DATABASE=Non-Essential Instrumentation + +pci:v*d*sv*sd*bc40* + ID_PCI_CLASS_FROM_DATABASE=Coprocessor + +pci:v*d*sv*sd*bcFF* + ID_PCI_CLASS_FROM_DATABASE=Unassigned class diff --git a/src/grp-udev/hwdb/20-pci-vendor-model.hwdb b/src/grp-udev/hwdb/20-pci-vendor-model.hwdb new file mode 100644 index 0000000000..0c829c8aec --- /dev/null +++ b/src/grp-udev/hwdb/20-pci-vendor-model.hwdb @@ -0,0 +1,83262 @@ +# This file is part of systemd. +# +# Data imported from: http://pci-ids.ucw.cz/v2.2/pci.ids + +pci:v00000001* + ID_VENDOR_FROM_DATABASE=SafeNet (wrong ID) + +pci:v00000010* + ID_VENDOR_FROM_DATABASE=Allied Telesis, Inc (Wrong ID) + +pci:v00000010d00008139* + ID_MODEL_FROM_DATABASE=AT-2500TX V3 Ethernet + +pci:v0000001C* + ID_VENDOR_FROM_DATABASE=PEAK-System Technik GmbH + +pci:v0000001Cd00000001* + ID_MODEL_FROM_DATABASE=PCAN-PCI CAN-Bus controller + +pci:v0000001Cd00000001sv0000001Csd00000004* + ID_MODEL_FROM_DATABASE=PCAN-PCI CAN-Bus controller (2 Channel CAN Bus SJC1000) + +pci:v0000001Cd00000001sv0000001Csd00000005* + ID_MODEL_FROM_DATABASE=PCAN-PCI CAN-Bus controller (2 Channel CAN Bus SJC1000 (Optically Isolated)) + +pci:v0000003D* + ID_VENDOR_FROM_DATABASE=Lockheed Martin-Marietta Corp + +pci:v00000059* + ID_VENDOR_FROM_DATABASE=Tiger Jet Network Inc. (Wrong ID) + +pci:v00000070* + ID_VENDOR_FROM_DATABASE=Hauppauge computer works Inc. + +pci:v00000070d00007801* + ID_MODEL_FROM_DATABASE=WinTV HVR-1800 MCE + +pci:v00000071* + ID_VENDOR_FROM_DATABASE=Nebula Electronics Ltd. + +pci:v00000095* + ID_VENDOR_FROM_DATABASE=Silicon Image, Inc. (Wrong ID) + +pci:v00000095d00000680* + ID_MODEL_FROM_DATABASE=Ultra ATA/133 IDE RAID CONTROLLER CARD + +pci:v000000A7* + ID_VENDOR_FROM_DATABASE=Teles AG (Wrong ID) + +pci:v00000100* + ID_VENDOR_FROM_DATABASE=Ncipher Corp Ltd + +pci:v00000123* + ID_VENDOR_FROM_DATABASE=General Dynamics + +pci:v0000018A* + ID_VENDOR_FROM_DATABASE=LevelOne + +pci:v0000018Ad00000106* + ID_MODEL_FROM_DATABASE=FPC-0106TX misprogrammed [RTL81xx] + +pci:v0000021B* + ID_VENDOR_FROM_DATABASE=Compaq Computer Corporation + +pci:v0000021Bd00008139* + ID_MODEL_FROM_DATABASE=HNE-300 (RealTek RTL8139c) [iPaq Networking] + +pci:v00000270* + ID_VENDOR_FROM_DATABASE=Hauppauge computer works Inc. (Wrong ID) + +pci:v000002AC* + ID_VENDOR_FROM_DATABASE=SpeedStream + +pci:v000002ACd00001012* + ID_MODEL_FROM_DATABASE=1012 PCMCIA 10/100 Ethernet Card [RTL81xx] + +pci:v00000303* + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Company (Wrong ID) + +pci:v00000308* + ID_VENDOR_FROM_DATABASE=ZyXEL Communications Corporation (Wrong ID) + +pci:v00000315* + ID_VENDOR_FROM_DATABASE=SK-Electronics Co., Ltd. + +pci:v00000357* + ID_VENDOR_FROM_DATABASE=TTTech Computertechnik AG (Wrong ID) + +pci:v00000357d0000000A* + ID_MODEL_FROM_DATABASE=TTP-Monitoring Card V2.0 + +pci:v00000432* + ID_VENDOR_FROM_DATABASE=SCM Microsystems, Inc. + +pci:v00000432d00000001* + ID_MODEL_FROM_DATABASE=Pluto2 DVB-T Receiver for PCMCIA [EasyWatch MobilSet] + +pci:v00000675* + ID_VENDOR_FROM_DATABASE=Dynalink + +pci:v00000675d00001700* + ID_MODEL_FROM_DATABASE=IS64PH ISDN Adapter + +pci:v00000675d00001702* + ID_MODEL_FROM_DATABASE=IS64PH ISDN Adapter + +pci:v00000675d00001703* + ID_MODEL_FROM_DATABASE=ISDN Adapter (PCI Bus, DV, W) + +pci:v00000675d00001704* + ID_MODEL_FROM_DATABASE=ISDN Adapter (PCI Bus, D, C) + +pci:v00000721* + ID_VENDOR_FROM_DATABASE=Sapphire, Inc. + +pci:v00000777* + ID_VENDOR_FROM_DATABASE=Ubiquiti Networks, Inc. + +pci:v00000795* + ID_VENDOR_FROM_DATABASE=Wired Inc. + +pci:v00000795d00006663* + ID_MODEL_FROM_DATABASE=Butane II (MPEG2 encoder board) + +pci:v00000795d00006666* + ID_MODEL_FROM_DATABASE=MediaPress (MPEG2 encoder board) + +pci:v000007D1* + ID_VENDOR_FROM_DATABASE=D-Link System Inc + +pci:v00000925* + ID_VENDOR_FROM_DATABASE=VIA Technologies, Inc. (Wrong ID) + +pci:v00000A89* + ID_VENDOR_FROM_DATABASE=BREA Technologies Inc + +pci:v00000B0B* + ID_VENDOR_FROM_DATABASE=Rhino Equipment Corp. + +pci:v00000B0Bd00000105* + ID_MODEL_FROM_DATABASE=Rhino R1T1 + +pci:v00000B0Bd00000205* + ID_MODEL_FROM_DATABASE=Rhino R4FXO + +pci:v00000B0Bd00000206* + ID_MODEL_FROM_DATABASE=RCB4FXO 4-channel FXO analog telphony card + +pci:v00000B0Bd00000305* + ID_MODEL_FROM_DATABASE=Rhino R4T1 + +pci:v00000B0Bd00000405* + ID_MODEL_FROM_DATABASE=Rhino R8FXX + +pci:v00000B0Bd00000406* + ID_MODEL_FROM_DATABASE=RCB8FXX 8-channel modular analog telphony card + +pci:v00000B0Bd00000505* + ID_MODEL_FROM_DATABASE=Rhino R24FXX + +pci:v00000B0Bd00000506* + ID_MODEL_FROM_DATABASE=RCB24FXS 24-Channel FXS analog telphony card + +pci:v00000B0Bd00000605* + ID_MODEL_FROM_DATABASE=Rhino R2T1 + +pci:v00000B0Bd00000705* + ID_MODEL_FROM_DATABASE=Rhino R24FXS + +pci:v00000B0Bd00000706* + ID_MODEL_FROM_DATABASE=RCB24FXO 24-Channel FXO analog telphony card + +pci:v00000B0Bd00000905* + ID_MODEL_FROM_DATABASE=R1T3 Single T3 Digital Telephony Card + +pci:v00000B0Bd00000906* + ID_MODEL_FROM_DATABASE=RCB24FXX 24-channel modular analog telphony card + +pci:v00000B0Bd00000A06* + ID_MODEL_FROM_DATABASE=RCB672FXX 672-channel modular analog telphony card + +pci:v00000E11* + ID_VENDOR_FROM_DATABASE=Compaq Computer Corporation + +pci:v00000E11d00000001* + ID_MODEL_FROM_DATABASE=PCI to EISA Bridge + +pci:v00000E11d00000002* + ID_MODEL_FROM_DATABASE=PCI to ISA Bridge + +pci:v00000E11d00000046* + ID_MODEL_FROM_DATABASE=Smart Array 64xx + +pci:v00000E11d00000046sv00000E11sd00004091* + ID_MODEL_FROM_DATABASE=Smart Array 64xx (Smart Array 6i) + +pci:v00000E11d00000046sv00000E11sd0000409A* + ID_MODEL_FROM_DATABASE=Smart Array 64xx (Smart Array 641) + +pci:v00000E11d00000046sv00000E11sd0000409B* + ID_MODEL_FROM_DATABASE=Smart Array 64xx (Smart Array 642) + +pci:v00000E11d00000046sv00000E11sd0000409C* + ID_MODEL_FROM_DATABASE=Smart Array 64xx (Smart Array 6400) + +pci:v00000E11d00000046sv00000E11sd0000409D* + ID_MODEL_FROM_DATABASE=Smart Array 64xx (Smart Array 6400 EM) + +pci:v00000E11d00000049* + ID_MODEL_FROM_DATABASE=NC7132 Gigabit Upgrade Module + +pci:v00000E11d0000004A* + ID_MODEL_FROM_DATABASE=NC6136 Gigabit Server Adapter + +pci:v00000E11d0000005A* + ID_MODEL_FROM_DATABASE=Remote Insight II board - Lights-Out + +pci:v00000E11d0000007C* + ID_MODEL_FROM_DATABASE=NC7770 1000BaseTX + +pci:v00000E11d0000007D* + ID_MODEL_FROM_DATABASE=NC6770 1000BaseTX + +pci:v00000E11d00000085* + ID_MODEL_FROM_DATABASE=NC7780 1000BaseTX + +pci:v00000E11d000000B1* + ID_MODEL_FROM_DATABASE=Remote Insight II board - PCI device + +pci:v00000E11d000000BB* + ID_MODEL_FROM_DATABASE=NC7760 + +pci:v00000E11d000000CA* + ID_MODEL_FROM_DATABASE=NC7771 + +pci:v00000E11d000000CB* + ID_MODEL_FROM_DATABASE=NC7781 + +pci:v00000E11d000000CF* + ID_MODEL_FROM_DATABASE=NC7772 + +pci:v00000E11d000000D0* + ID_MODEL_FROM_DATABASE=NC7782 + +pci:v00000E11d000000D1* + ID_MODEL_FROM_DATABASE=NC7783 + +pci:v00000E11d000000E3* + ID_MODEL_FROM_DATABASE=NC7761 + +pci:v00000E11d00000508* + ID_MODEL_FROM_DATABASE=Netelligent 4/16 Token Ring + +pci:v00000E11d00001000* + ID_MODEL_FROM_DATABASE=Triflex/Pentium Bridge, Model 1000 + +pci:v00000E11d00002000* + ID_MODEL_FROM_DATABASE=Triflex/Pentium Bridge, Model 2000 + +pci:v00000E11d00003032* + ID_MODEL_FROM_DATABASE=QVision 1280/p + +pci:v00000E11d00003033* + ID_MODEL_FROM_DATABASE=QVision 1280/p + +pci:v00000E11d00003034* + ID_MODEL_FROM_DATABASE=QVision 1280/p + +pci:v00000E11d00004000* + ID_MODEL_FROM_DATABASE=4000 [Triflex] + +pci:v00000E11d00004040* + ID_MODEL_FROM_DATABASE=Integrated Array + +pci:v00000E11d00004048* + ID_MODEL_FROM_DATABASE=Compaq Raid LC2 + +pci:v00000E11d00004050* + ID_MODEL_FROM_DATABASE=Smart Array 4200 + +pci:v00000E11d00004051* + ID_MODEL_FROM_DATABASE=Smart Array 4250ES + +pci:v00000E11d00004058* + ID_MODEL_FROM_DATABASE=Smart Array 431 + +pci:v00000E11d00004070* + ID_MODEL_FROM_DATABASE=Smart Array 5300 + +pci:v00000E11d00004080* + ID_MODEL_FROM_DATABASE=Smart Array 5i + +pci:v00000E11d00004082* + ID_MODEL_FROM_DATABASE=Smart Array 532 + +pci:v00000E11d00004083* + ID_MODEL_FROM_DATABASE=Smart Array 5312 + +pci:v00000E11d00004091* + ID_MODEL_FROM_DATABASE=Smart Array 6i + +pci:v00000E11d0000409A* + ID_MODEL_FROM_DATABASE=Smart Array 641 + +pci:v00000E11d0000409B* + ID_MODEL_FROM_DATABASE=Smart Array 642 + +pci:v00000E11d0000409C* + ID_MODEL_FROM_DATABASE=Smart Array 6400 + +pci:v00000E11d0000409D* + ID_MODEL_FROM_DATABASE=Smart Array 6400 EM + +pci:v00000E11d00006010* + ID_MODEL_FROM_DATABASE=HotPlug PCI Bridge 6010 + +pci:v00000E11d00007020* + ID_MODEL_FROM_DATABASE=USB Controller + +pci:v00000E11d0000A0EC* + ID_MODEL_FROM_DATABASE=Fibre Channel Host Controller + +pci:v00000E11d0000A0F0* + ID_MODEL_FROM_DATABASE=Advanced System Management Controller + +pci:v00000E11d0000A0F0sv00000E11sd0000B0F3* + ID_MODEL_FROM_DATABASE=Advanced System Management Controller (ProLiant DL360) + +pci:v00000E11d0000A0F3* + ID_MODEL_FROM_DATABASE=Triflex PCI to ISA Bridge + +pci:v00000E11d0000A0F7* + ID_MODEL_FROM_DATABASE=PCI Hotplug Controller + +pci:v00000E11d0000A0F7sv00008086sd0000002A* + ID_MODEL_FROM_DATABASE=PCI Hotplug Controller (A) + +pci:v00000E11d0000A0F7sv00008086sd0000002B* + ID_MODEL_FROM_DATABASE=PCI Hotplug Controller (B) + +pci:v00000E11d0000A0F8* + ID_MODEL_FROM_DATABASE=ZFMicro Chipset USB + +pci:v00000E11d0000A0FC* + ID_MODEL_FROM_DATABASE=FibreChannel HBA Tachyon + +pci:v00000E11d0000AE10* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller + +pci:v00000E11d0000AE10sv00000E11sd00004030* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller (Smart-2/P Array Controller) + +pci:v00000E11d0000AE10sv00000E11sd00004031* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller (Smart-2SL Array Controller) + +pci:v00000E11d0000AE10sv00000E11sd00004032* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller (Smart Array 3200 Controller) + +pci:v00000E11d0000AE10sv00000E11sd00004033* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller (Smart Array 3100ES Controller) + +pci:v00000E11d0000AE10sv00000E11sd00004034* + ID_MODEL_FROM_DATABASE=Smart-2/P RAID Controller (Smart Array 221 Controller) + +pci:v00000E11d0000AE29* + ID_MODEL_FROM_DATABASE=MIS-L + +pci:v00000E11d0000AE2A* + ID_MODEL_FROM_DATABASE=MPC + +pci:v00000E11d0000AE2B* + ID_MODEL_FROM_DATABASE=MIS-E + +pci:v00000E11d0000AE31* + ID_MODEL_FROM_DATABASE=System Management Controller + +pci:v00000E11d0000AE32* + ID_MODEL_FROM_DATABASE=Netelligent 10/100 TX PCI UTP + +pci:v00000E11d0000AE33* + ID_MODEL_FROM_DATABASE=Triflex Dual EIDE Controller + +pci:v00000E11d0000AE34* + ID_MODEL_FROM_DATABASE=Netelligent 10 T PCI UTP + +pci:v00000E11d0000AE35* + ID_MODEL_FROM_DATABASE=Integrated NetFlex-3/P + +pci:v00000E11d0000AE40* + ID_MODEL_FROM_DATABASE=Netelligent Dual 10/100 TX PCI UTP + +pci:v00000E11d0000AE43* + ID_MODEL_FROM_DATABASE=Netelligent Integrated 10/100 TX UTP + +pci:v00000E11d0000AE69* + ID_MODEL_FROM_DATABASE=CETUS-L + +pci:v00000E11d0000AE6C* + ID_MODEL_FROM_DATABASE=Northstar + +pci:v00000E11d0000AE6D* + ID_MODEL_FROM_DATABASE=NorthStar CPU to PCI Bridge + +pci:v00000E11d0000B011* + ID_MODEL_FROM_DATABASE=Netelligent 10/100 TX Embedded UTP + +pci:v00000E11d0000B012* + ID_MODEL_FROM_DATABASE=Netelligent 10 T/2 PCI UTP/Coax + +pci:v00000E11d0000B01E* + ID_MODEL_FROM_DATABASE=NC3120 Fast Ethernet NIC + +pci:v00000E11d0000B01F* + ID_MODEL_FROM_DATABASE=NC3122 Fast Ethernet NIC + +pci:v00000E11d0000B02F* + ID_MODEL_FROM_DATABASE=NC1120 Ethernet NIC + +pci:v00000E11d0000B030* + ID_MODEL_FROM_DATABASE=Netelligent 10/100 TX UTP + +pci:v00000E11d0000B04A* + ID_MODEL_FROM_DATABASE=10/100 TX PCI Intel WOL UTP Controller + +pci:v00000E11d0000B060* + ID_MODEL_FROM_DATABASE=Smart Array 5300 Controller + +pci:v00000E11d0000B0C6* + ID_MODEL_FROM_DATABASE=NC3161 Fast Ethernet NIC + +pci:v00000E11d0000B0C7* + ID_MODEL_FROM_DATABASE=NC3160 Fast Ethernet NIC + +pci:v00000E11d0000B0D7* + ID_MODEL_FROM_DATABASE=NC3121 Fast Ethernet NIC + +pci:v00000E11d0000B0DD* + ID_MODEL_FROM_DATABASE=NC3131 Fast Ethernet NIC + +pci:v00000E11d0000B0DE* + ID_MODEL_FROM_DATABASE=NC3132 Fast Ethernet Module + +pci:v00000E11d0000B0DF* + ID_MODEL_FROM_DATABASE=NC6132 Gigabit Module + +pci:v00000E11d0000B0E0* + ID_MODEL_FROM_DATABASE=NC6133 Gigabit Module + +pci:v00000E11d0000B0E1* + ID_MODEL_FROM_DATABASE=NC3133 Fast Ethernet Module + +pci:v00000E11d0000B123* + ID_MODEL_FROM_DATABASE=NC6134 Gigabit NIC + +pci:v00000E11d0000B134* + ID_MODEL_FROM_DATABASE=NC3163 Fast Ethernet NIC + +pci:v00000E11d0000B13C* + ID_MODEL_FROM_DATABASE=NC3162 Fast Ethernet NIC + +pci:v00000E11d0000B144* + ID_MODEL_FROM_DATABASE=NC3123 Fast Ethernet NIC + +pci:v00000E11d0000B163* + ID_MODEL_FROM_DATABASE=NC3134 Fast Ethernet NIC + +pci:v00000E11d0000B164* + ID_MODEL_FROM_DATABASE=NC3165 Fast Ethernet Upgrade Module + +pci:v00000E11d0000B178* + ID_MODEL_FROM_DATABASE=Smart Array 5i/532 + +pci:v00000E11d0000B178sv00000E11sd00004080* + ID_MODEL_FROM_DATABASE=Smart Array 5i/532 (Smart Array 5i) + +pci:v00000E11d0000B178sv00000E11sd00004082* + ID_MODEL_FROM_DATABASE=Smart Array 5i/532 (Smart Array 532) + +pci:v00000E11d0000B178sv00000E11sd00004083* + ID_MODEL_FROM_DATABASE=Smart Array 5i/532 (Smart Array 5312) + +pci:v00000E11d0000B1A4* + ID_MODEL_FROM_DATABASE=NC7131 Gigabit Server Adapter + +pci:v00000E11d0000B200* + ID_MODEL_FROM_DATABASE=Memory Hot-Plug Controller + +pci:v00000E11d0000B203* + ID_MODEL_FROM_DATABASE=Integrated Lights Out Controller + +pci:v00000E11d0000B204* + ID_MODEL_FROM_DATABASE=Integrated Lights Out Processor + +pci:v00000E11d0000C000* + ID_MODEL_FROM_DATABASE=Remote Insight Lights-Out Edition + +pci:v00000E11d0000F130* + ID_MODEL_FROM_DATABASE=NetFlex-3/P ThunderLAN 1.0 + +pci:v00000E11d0000F150* + ID_MODEL_FROM_DATABASE=NetFlex-3/P ThunderLAN 2.3 + +pci:v00000E55* + ID_VENDOR_FROM_DATABASE=HaSoTec GmbH + +pci:v00000EAC* + ID_VENDOR_FROM_DATABASE=SHF Communication Technologies AG + +pci:v00000EACd00000008* + ID_MODEL_FROM_DATABASE=Ethernet Powerlink Managing Node 01 + +pci:v00000F62* + ID_VENDOR_FROM_DATABASE=Acrox Technologies Co., Ltd. + +pci:v00001000* + ID_VENDOR_FROM_DATABASE=LSI Logic / Symbios Logic + +pci:v00001000d00000001* + ID_MODEL_FROM_DATABASE=53c810 + +pci:v00001000d00000001sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c810 (LSI53C810AE PCI to SCSI I/O Processor) + +pci:v00001000d00000002* + ID_MODEL_FROM_DATABASE=53c820 + +pci:v00001000d00000003* + ID_MODEL_FROM_DATABASE=53c825 + +pci:v00001000d00000003sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c825 (LSI53C825AE PCI to SCSI I/O Processor (Ultra Wide)) + +pci:v00001000d00000004* + ID_MODEL_FROM_DATABASE=53c815 + +pci:v00001000d00000005* + ID_MODEL_FROM_DATABASE=53c810AP + +pci:v00001000d00000006* + ID_MODEL_FROM_DATABASE=53c860 + +pci:v00001000d00000006sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c860 (LSI53C860E PCI to Ultra SCSI I/O Processor) + +pci:v00001000d0000000A* + ID_MODEL_FROM_DATABASE=53c1510 + +pci:v00001000d0000000Asv00000E11sd0000B143* + ID_MODEL_FROM_DATABASE=53c1510 (Integrated Dual Channel Wide Ultra2 SCSI Controller) + +pci:v00001000d0000000Asv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c1510 (LSI53C1510 PCI to Dual Channel Wide Ultra2 SCSI Controller (Nonintelligent mode)) + +pci:v00001000d0000000B* + ID_MODEL_FROM_DATABASE=53C896/897 + +pci:v00001000d0000000Bsv00000E11sd00006004* + ID_MODEL_FROM_DATABASE=53C896/897 (EOB003 Series SCSI host adapter) + +pci:v00001000d0000000Bsv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53C896/897 (LSI53C896/7 PCI to Dual Channel Ultra2 SCSI Multifunction Controller) + +pci:v00001000d0000000Bsv00001000sd00001010* + ID_MODEL_FROM_DATABASE=53C896/897 (LSI22910 PCI to Dual Channel Ultra2 SCSI host adapter) + +pci:v00001000d0000000Bsv00001000sd00001020* + ID_MODEL_FROM_DATABASE=53C896/897 (LSI21002 PCI to Dual Channel Ultra2 SCSI host adapter) + +pci:v00001000d0000000Bsv000013E9sd00001000* + ID_MODEL_FROM_DATABASE=53C896/897 (6221L-4U (Dual U2W SCSI, dual 10/100TX, graphics)) + +pci:v00001000d0000000C* + ID_MODEL_FROM_DATABASE=53c895 + +pci:v00001000d0000000Csv00001000sd00001010* + ID_MODEL_FROM_DATABASE=53c895 (LSI8951U PCI to Ultra2 SCSI host adapter) + +pci:v00001000d0000000Csv00001000sd00001020* + ID_MODEL_FROM_DATABASE=53c895 (LSI8952U PCI to Ultra2 SCSI host adapter) + +pci:v00001000d0000000Csv00001DE1sd00003906* + ID_MODEL_FROM_DATABASE=53c895 (DC-390U2B SCSI adapter) + +pci:v00001000d0000000Csv00001DE1sd00003907* + ID_MODEL_FROM_DATABASE=53c895 (DC-390U2W) + +pci:v00001000d0000000D* + ID_MODEL_FROM_DATABASE=53c885 + +pci:v00001000d0000000F* + ID_MODEL_FROM_DATABASE=53c875 + +pci:v00001000d0000000Fsv00000E11sd00007004* + ID_MODEL_FROM_DATABASE=53c875 (Embedded Ultra Wide SCSI Controller) + +pci:v00001000d0000000Fsv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c875 (LSI53C876/E PCI to Dual Channel SCSI Controller) + +pci:v00001000d0000000Fsv00001000sd00001010* + ID_MODEL_FROM_DATABASE=53c875 (LSI22801 PCI to Dual Channel Ultra SCSI host adapter) + +pci:v00001000d0000000Fsv00001000sd00001020* + ID_MODEL_FROM_DATABASE=53c875 (LSI22802 PCI to Dual Channel Ultra SCSI host adapter) + +pci:v00001000d0000000Fsv00001092sd00008760* + ID_MODEL_FROM_DATABASE=53c875 (FirePort 40 Dual SCSI Controller) + +pci:v00001000d0000000Fsv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=53c875 (V5D Single Board Computer Wide Ultra SCSI) + +pci:v00001000d0000000Fsv00001775sd000010D1* + ID_MODEL_FROM_DATABASE=53c875 (V5D Single Board Computer Ultra SCSI) + +pci:v00001000d0000000Fsv00001DE1sd00003904* + ID_MODEL_FROM_DATABASE=53c875 (DC390F/U Ultra Wide SCSI Adapter) + +pci:v00001000d0000000Fsv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=53c875 (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v00001000d0000000Fsv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=53c875 (CT7 mainboard) + +pci:v00001000d00000010* + ID_MODEL_FROM_DATABASE=53C1510 + +pci:v00001000d00000010sv00000E11sd00004040* + ID_MODEL_FROM_DATABASE=53C1510 (Integrated Smart Array Controller) + +pci:v00001000d00000010sv00000E11sd00004048* + ID_MODEL_FROM_DATABASE=53C1510 (RAID LC2 Controller) + +pci:v00001000d00000010sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53C1510 (PCI to Dual Channel Wide Ultra2 SCSI Controller (Intelligent mode)) + +pci:v00001000d00000012* + ID_MODEL_FROM_DATABASE=53c895a + +pci:v00001000d00000012sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c895a (LSI53C895A PCI to Ultra2 SCSI Controller) + +pci:v00001000d00000013* + ID_MODEL_FROM_DATABASE=53c875a + +pci:v00001000d00000013sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c875a (LSI53C875A PCI to Ultra SCSI Controller) + +pci:v00001000d00000020* + ID_MODEL_FROM_DATABASE=53c1010 Ultra3 SCSI Adapter + +pci:v00001000d00000020sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c1010 Ultra3 SCSI Adapter (LSI53C1010-33 PCI to Dual Channel Ultra160 SCSI Controller) + +pci:v00001000d00000020sv0000107Bsd00001040* + ID_MODEL_FROM_DATABASE=53c1010 Ultra3 SCSI Adapter (Server Onboard 53C1010-33) + +pci:v00001000d00000020sv00001DE1sd00001020* + ID_MODEL_FROM_DATABASE=53c1010 Ultra3 SCSI Adapter (DC-390U3W) + +pci:v00001000d00000021* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter + +pci:v00001000d00000021sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (LSI53C1000/1000R/1010R/1010-66 PCI to Ultra160 SCSI Controller) + +pci:v00001000d00000021sv00001000sd00001010* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (Asus TR-DLS onboard 53C1010-66) + +pci:v00001000d00000021sv0000103Csd00001300* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (Ultra160 SCSI [AB306A]) + +pci:v00001000d00000021sv0000103Csd00001310* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (Ultra160 SCSI [A9918A]) + +pci:v00001000d00000021sv0000103Csd00001330* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (Ultra160 SCSI [A7059A]) + +pci:v00001000d00000021sv0000103Csd00001340* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (Ultra160 SCSI [A7060A]) + +pci:v00001000d00000021sv0000124Bsd00001070* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (PMC-USCSI3) + +pci:v00001000d00000021sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (CT8 mainboard) + +pci:v00001000d00000021sv00004C53sd00001300* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (P017 mezzanine (32-bit PMC)) + +pci:v00001000d00000021sv00004C53sd00001310* + ID_MODEL_FROM_DATABASE=53c1010 66MHz Ultra3 SCSI Adapter (P017 mezzanine (64-bit PMC)) + +pci:v00001000d0000002F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 IOV [Thunderbolt] + +pci:v00001000d0000002Fsv00001028sd00001F39* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 IOV [Thunderbolt] (SPERC8-e) + +pci:v00001000d0000002Fsv00001028sd00001F3E* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 IOV [Thunderbolt] (SPERC 8) + +pci:v00001000d00000030* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000030sv00000E11sd000000DA* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (ProLiant ML 350) + +pci:v00001000d00000030sv00001028sd00000123* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI Logic 1020/1030) + +pci:v00001000d00000030sv00001028sd0000014A* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI Logic 1020/1030) + +pci:v00001000d00000030sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (PowerEdge 1850 MPT Fusion SCSI/RAID (Perc 4)) + +pci:v00001000d00000030sv00001028sd00000183* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI Logic 1020/1030) + +pci:v00001000d00000030sv00001028sd0000018A* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (PERC 4/IM) + +pci:v00001000d00000030sv00001028sd00001010* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI U320 SCSI Controller) + +pci:v00001000d00000030sv0000103Csd000012C5* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (Ultra320 SCSI [A7173A]) + +pci:v00001000d00000030sv0000103Csd00001323* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (Core I/O LAN/SCSI Combo [AB314A]) + +pci:v00001000d00000030sv0000103Csd00003108* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (Single Channel Ultra320 SCSI HBA G2) + +pci:v00001000d00000030sv0000103Csd0000322A* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (SC11Xe Ultra320 Single Channel PCIe x4 SCSI Host Bus Adapter (412911-B21)) + +pci:v00001000d00000030sv0000124Bsd00001170* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (PMC-USCSI320) + +pci:v00001000d00000030sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI Logic Parallel SCSI Controller) + +pci:v00001000d00000030sv00001734sd00001052* + ID_MODEL_FROM_DATABASE=53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (PRIMERGY BX/RX/TX S2 series onboard SCSI(IME)) + +pci:v00001000d00000031* + ID_MODEL_FROM_DATABASE=53c1030ZC PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000032* + ID_MODEL_FROM_DATABASE=53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000032sv00001000sd00001000* + ID_MODEL_FROM_DATABASE=53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI (LSI53C1020/1030 PCI-X to Ultra320 SCSI Controller) + +pci:v00001000d00000033* + ID_MODEL_FROM_DATABASE=1030ZC_53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000040* + ID_MODEL_FROM_DATABASE=53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000040sv00001000sd00000033* + ID_MODEL_FROM_DATABASE=53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI (MegaRAID SCSI 320-2XR) + +pci:v00001000d00000040sv00001000sd00000066* + ID_MODEL_FROM_DATABASE=53c1035 PCI-X Fusion-MPT Dual Ultra320 SCSI (MegaRAID SCSI 320-2XRWS) + +pci:v00001000d00000041* + ID_MODEL_FROM_DATABASE=53C1035ZC PCI-X Fusion-MPT Dual Ultra320 SCSI + +pci:v00001000d00000050* + ID_MODEL_FROM_DATABASE=SAS1064 PCI-X Fusion-MPT SAS + +pci:v00001000d00000050sv00001028sd00001F04* + ID_MODEL_FROM_DATABASE=SAS1064 PCI-X Fusion-MPT SAS (SAS 5/E) + +pci:v00001000d00000050sv00001028sd00001F09* + ID_MODEL_FROM_DATABASE=SAS1064 PCI-X Fusion-MPT SAS (SAS 5i/R) + +pci:v00001000d00000052* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3216/3224 [Cutlass] + +pci:v00001000d00000053* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3216/3224 [Cutlass] + +pci:v00001000d00000053sv00001000sd00009350* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3216/3224 [Cutlass] (MegaRAID SAS 9341-16i) + +pci:v00001000d00000053sv00001000sd00009351* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3216/3224 [Cutlass] (MegaRAID SAS 9341-24i) + +pci:v00001000d00000054* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS + +pci:v00001000d00000054sv00001028sd00001F04* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/E Adapter Controller) + +pci:v00001000d00000054sv00001028sd00001F05* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/i Adapter Controller) + +pci:v00001000d00000054sv00001028sd00001F06* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/i Integrated Controller) + +pci:v00001000d00000054sv00001028sd00001F07* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/iR Integrated RAID Controller) + +pci:v00001000d00000054sv00001028sd00001F08* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/iR Integrated RAID Controller) + +pci:v00001000d00000054sv00001028sd00001F09* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS 5/iR Adapter RAID Controller) + +pci:v00001000d00000054sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS Controller) + +pci:v00001000d00000055* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS + +pci:v00001000d00000055sv00001033sd00008336* + ID_MODEL_FROM_DATABASE=SAS1068 PCI-X Fusion-MPT SAS (SAS1068) + +pci:v00001000d00000056* + ID_MODEL_FROM_DATABASE=SAS1064ET PCI-Express Fusion-MPT SAS + +pci:v00001000d00000056sv00001014sd000003BB* + ID_MODEL_FROM_DATABASE=SAS1064ET PCI-Express Fusion-MPT SAS (ServeRAID BR10il SAS/SATA Controller v2) + +pci:v00001000d00000057* + ID_MODEL_FROM_DATABASE=M1064E MegaRAID SAS + +pci:v00001000d00000057sv00008086sd0000346C* + ID_MODEL_FROM_DATABASE=M1064E MegaRAID SAS (Embedded Software RAID Technology II (ESTRII)) + +pci:v00001000d00000058* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS + +pci:v00001000d00000058sv00001000sd00003140* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SAS3081E-R 8-Port SAS/SATA Host Bus Adapter) + +pci:v00001000d00000058sv00001028sd0000021D* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SAS 6/iR Integrated Workstations RAID Controller) + +pci:v00001000d00000058sv00001028sd00001F0E* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SAS 6/iR Adapter RAID Controller) + +pci:v00001000d00000058sv00001028sd00001F0F* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SAS 6/iR Integrated Blades RAID Controller) + +pci:v00001000d00000058sv00001028sd00001F10* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SAS 6/iR Integrated RAID Controller) + +pci:v00001000d00000058sv0000103Csd00003229* + ID_MODEL_FROM_DATABASE=SAS1068E PCI-Express Fusion-MPT SAS (SC44Ge Host Bus Adapter) + +pci:v00001000d00000059* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 8208ELP/8208ELP + +pci:v00001000d0000005A* + ID_MODEL_FROM_DATABASE=SAS1066E PCI-Express Fusion-MPT SAS + +pci:v00001000d0000005B* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] + +pci:v00001000d0000005Bsv00001000sd00009265* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9265-8i) + +pci:v00001000d0000005Bsv00001000sd00009266* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9266-8i) + +pci:v00001000d0000005Bsv00001000sd00009267* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9267-8i) + +pci:v00001000d0000005Bsv00001000sd00009268* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9265CV-8i / 9270CV-8i) + +pci:v00001000d0000005Bsv00001000sd00009269* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9266-4i) + +pci:v00001000d0000005Bsv00001000sd00009270* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9270-8i) + +pci:v00001000d0000005Bsv00001000sd00009271* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9271-8i) + +pci:v00001000d0000005Bsv00001000sd00009272* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9272-8i) + +pci:v00001000d0000005Bsv00001000sd00009273* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9270CV-8i) + +pci:v00001000d0000005Bsv00001000sd00009274* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9270-4i) + +pci:v00001000d0000005Bsv00001000sd00009275* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9271-8iCC) + +pci:v00001000d0000005Bsv00001000sd00009276* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9271-4i) + +pci:v00001000d0000005Bsv00001000sd00009285* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9285-8e) + +pci:v00001000d0000005Bsv00001000sd00009288* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9285CV-8e) + +pci:v00001000d0000005Bsv00001000sd00009290* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9286-8e) + +pci:v00001000d0000005Bsv00001000sd00009291* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9286CV-8e) + +pci:v00001000d0000005Bsv00001000sd00009295* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (MegaRAID SAS 9286CV-8eCC) + +pci:v00001000d0000005Bsv00001014sd0000040B* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (ServeRAID M5110 SAS/SATA Controller) + +pci:v00001000d0000005Bsv00001014sd0000040C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (ServeRAID M5120 SAS/SATA Controller) + +pci:v00001000d0000005Bsv00001014sd00000412* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (ServeRAID M5110e SAS/SATA Controller) + +pci:v00001000d0000005Bsv00001028sd00001F2D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H810 Adapter) + +pci:v00001000d0000005Bsv00001028sd00001F30* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710 Embedded) + +pci:v00001000d0000005Bsv00001028sd00001F31* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710P Adapter) + +pci:v00001000d0000005Bsv00001028sd00001F33* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710P Mini (for blades)) + +pci:v00001000d0000005Bsv00001028sd00001F34* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710P Mini (for monolithics)) + +pci:v00001000d0000005Bsv00001028sd00001F35* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710 Adapter) + +pci:v00001000d0000005Bsv00001028sd00001F37* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710 Mini (for blades)) + +pci:v00001000d0000005Bsv00001028sd00001F38* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (PERC H710 Mini (for monolithics)) + +pci:v00001000d0000005Bsv000015D9sd00000690* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (LSI MegaRAID ROMB) + +pci:v00001000d0000005Bsv00008086sd00003510* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (RMS25PB080 RAID Controller) + +pci:v00001000d0000005Bsv00008086sd00003513* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (RMS25CB080 RAID Controller) + +pci:v00001000d0000005C* + ID_MODEL_FROM_DATABASE=SAS1064A PCI-X Fusion-MPT SAS + +pci:v00001000d0000005D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] + +pci:v00001000d0000005Dsv00001000sd00009361* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (MegaRAID SAS 9361-8i) + +pci:v00001000d0000005Dsv00001028sd00001F41* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H830 Adapter) + +pci:v00001000d0000005Dsv00001028sd00001F42* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730P Adapter) + +pci:v00001000d0000005Dsv00001028sd00001F43* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730 Adapter) + +pci:v00001000d0000005Dsv00001028sd00001F47* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730P Mini) + +pci:v00001000d0000005Dsv00001028sd00001F48* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730P Mini (for blades)) + +pci:v00001000d0000005Dsv00001028sd00001F49* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730 Mini) + +pci:v00001000d0000005Dsv00001028sd00001F4A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730 Mini (for blades)) + +pci:v00001000d0000005Dsv00001028sd00001F4D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC FD33xS) + +pci:v00001000d0000005Dsv00001028sd00001F4F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC H730P Slim) + +pci:v00001000d0000005Dsv00001028sd00001F54* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (PERC FD33xD) + +pci:v00001000d0000005Dsv000017AAsd00001052* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkServer RAID 720i) + +pci:v00001000d0000005Dsv000017AAsd00001053* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkServer RAID 720ix) + +pci:v00001000d0000005E* + ID_MODEL_FROM_DATABASE=SAS1066 PCI-X Fusion-MPT SAS + +pci:v00001000d0000005F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] + +pci:v00001000d0000005Fsv00001028sd00001F44* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (PERC H330 Adapter) + +pci:v00001000d0000005Fsv00001028sd00001F4B* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (PERC H330 Mini) + +pci:v00001000d0000005Fsv00001028sd00001F4C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (PERC H330 Mini (for blades)) + +pci:v00001000d0000005Fsv00001028sd00001F4D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (PERC H330 Embedded (for monolithic)) + +pci:v00001000d0000005Fsv00001054sd0000306A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (SAS 3004 iMR ROMB) + +pci:v00001000d0000005Fsv00001D49sd000004DB* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3008 [Fury] (ServeRAID M1210 SAS/SATA Controller) + +pci:v00001000d00000060* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 + +pci:v00001000d00000060sv00001000sd00001006* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8888ELP) + +pci:v00001000d00000060sv00001000sd0000100A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8708ELP) + +pci:v00001000d00000060sv00001000sd0000100E* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8884E) + +pci:v00001000d00000060sv00001000sd0000100F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8708E) + +pci:v00001000d00000060sv00001000sd00001010* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SATA 350-8ELP) + +pci:v00001000d00000060sv00001000sd00001011* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SATA 350-4ELP) + +pci:v00001000d00000060sv00001000sd00001012* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8704ELP) + +pci:v00001000d00000060sv00001000sd00001016* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS 8880EM2) + +pci:v00001000d00000060sv00001014sd00000363* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv00001014sd00000364* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (SystemX MegaRAID SAS 8808E) + +pci:v00001000d00000060sv00001014sd00000365* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (SystemX MegaRAID SAS 8884E) + +pci:v00001000d00000060sv00001014sd00000379* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (SystemX MegaRAID SAS 8880EM2) + +pci:v00001000d00000060sv00001028sd00001F0A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (PERC 6/E Adapter RAID Controller) + +pci:v00001000d00000060sv00001028sd00001F0B* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (PERC 6/i Adapter RAID Controller) + +pci:v00001000d00000060sv00001028sd00001F0C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (PERC 6/i Integrated RAID Controller) + +pci:v00001000d00000060sv00001028sd00001F0D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (PERC 6/i Integrated RAID Controller) + +pci:v00001000d00000060sv00001028sd00001F11* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (CERC 6/i Integrated RAID Controller) + +pci:v00001000d00000060sv00001033sd0000835A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv00001043sd0000824D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv00001170sd0000002F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv00001170sd00000036* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv000015D9sd0000C080* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv000017AAsd00006B7C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv000018A1sd00000003* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (LSI MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000060sv00008086sd00001006* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (RAID Controller SRCSAS28EP) + +pci:v00001000d00000060sv00008086sd0000100A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (RAID Controller SRCSAS28EV) + +pci:v00001000d00000060sv00008086sd00001010* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (RAID Controller SRCSATA28E) + +pci:v00001000d00000060sv00008086sd000034CC* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (Integrated RAID Controller SROMBSAS28E) + +pci:v00001000d00000060sv00008086sd000034CD* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (Integrated RAID Controller SROMBSAS28E) + +pci:v00001000d00000060sv00008086sd00003505* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078 (Integrated RAID Controller SROMBSASMP2) + +pci:v00001000d00000062* + ID_MODEL_FROM_DATABASE=SAS1078 PCI-Express Fusion-MPT SAS + +pci:v00001000d00000062sv00001000sd00000062* + ID_MODEL_FROM_DATABASE=SAS1078 PCI-Express Fusion-MPT SAS + +pci:v00001000d00000064* + ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] + +pci:v00001000d00000065* + ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] + +pci:v00001000d0000006E* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000070* + ID_MODEL_FROM_DATABASE=SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire] + +pci:v00001000d00000071* + ID_MODEL_FROM_DATABASE=MR SAS HBA 2004 + +pci:v00001000d00000072* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] + +pci:v00001000d00000072sv00001028sd00001F1C* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6Gbps SAS HBA Adapter) + +pci:v00001000d00000072sv00001028sd00001F1D* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Adapter) + +pci:v00001000d00000072sv00001028sd00001F1E* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Integrated) + +pci:v00001000d00000072sv00001028sd00001F1F* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Modular) + +pci:v00001000d00000072sv00001028sd00001F20* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Embedded) + +pci:v00001000d00000072sv00001028sd00001F22* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (Internal Tape Adapter) + +pci:v00001000d00000072sv00008086sd0000350F* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (RMS2LL040 RAID Controller) + +pci:v00001000d00000073* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] + +pci:v00001000d00000073sv00001000sd00009240* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (MegaRAID SAS 9240-8i) + +pci:v00001000d00000073sv00001000sd00009241* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (MegaRAID SAS 9240-4i) + +pci:v00001000d00000073sv00001000sd000092A0* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (MegaRAID SAS 9220-8i) + +pci:v00001000d00000073sv00001014sd000003B1* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (ServeRAID M1015 SAS/SATA Controller) + +pci:v00001000d00000073sv00001028sd00001F4E* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Adapter) + +pci:v00001000d00000073sv00001028sd00001F4F* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Integrated) + +pci:v00001000d00000073sv00001028sd00001F50* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Mini Blades) + +pci:v00001000d00000073sv00001028sd00001F51* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Mini Monolithics) + +pci:v00001000d00000073sv00001028sd00001F52* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Embedded1) + +pci:v00001000d00000073sv00001028sd00001F53* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Embedded2) + +pci:v00001000d00000073sv00001028sd00001F54* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Reserved) + +pci:v00001000d00000073sv00001054sd00003035* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (LSI MegaRAID SAS 9240-8i) + +pci:v00001000d00000073sv00001137sd00000072* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (2004 iMR ROMB) + +pci:v00001000d00000073sv00001137sd00000073* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (2008 ROMB) + +pci:v00001000d00000073sv00001137sd000000B0* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (UCSC RAID SAS 2008M-8i) + +pci:v00001000d00000073sv00001137sd000000B1* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (UCSC RAID SAS 2008M-8i) + +pci:v00001000d00000073sv00001137sd000000C2* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (UCS E-Series Double Wide) + +pci:v00001000d00000073sv00001137sd000000C3* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (UCS E-Series Single Wide) + +pci:v00001000d00000073sv000015D9sd00000400* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (Supermicro SMC2008-iMR) + +pci:v00001000d00000073sv00001734sd00001177* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (RAID Ctrl SAS 6G 0/1 (D2607)) + +pci:v00001000d00000073sv000017AAsd00001051* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (ThinkServer RAID 510i) + +pci:v00001000d00000073sv00008086sd0000350D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (RMS2AF040 RAID Controller) + +pci:v00001000d00000073sv00008086sd00009240* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (RAID Controller RS2WC080) + +pci:v00001000d00000073sv00008086sd00009241* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (RAID Controller RS2WC040) + +pci:v00001000d00000074* + ID_MODEL_FROM_DATABASE=SAS2108 PCI-Express Fusion-MPT SAS-2 [Liberator] + +pci:v00001000d00000076* + ID_MODEL_FROM_DATABASE=SAS2108 PCI-Express Fusion-MPT SAS-2 [Liberator] + +pci:v00001000d00000077* + ID_MODEL_FROM_DATABASE=SAS2108 PCI-Express Fusion-MPT SAS-2 [Liberator] + +pci:v00001000d00000079* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] + +pci:v00001000d00000079sv00001000sd00009251* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260-4ix) + +pci:v00001000d00000079sv00001000sd00009256* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260-8ix) + +pci:v00001000d00000079sv00001000sd00009260* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260-4i) + +pci:v00001000d00000079sv00001000sd00009261* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260-8i) + +pci:v00001000d00000079sv00001000sd00009262* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9262-8i) + +pci:v00001000d00000079sv00001000sd00009263* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9261-8i) + +pci:v00001000d00000079sv00001000sd00009264* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9264-8i) + +pci:v00001000d00000079sv00001000sd00009267* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260CV-4i) + +pci:v00001000d00000079sv00001000sd00009268* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260CV-8i) + +pci:v00001000d00000079sv00001000sd00009275* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9280-8ex) + +pci:v00001000d00000079sv00001000sd00009276* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MR9260-16i) + +pci:v00001000d00000079sv00001000sd00009280* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9280-8e) + +pci:v00001000d00000079sv00001000sd00009281* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9281-8E) + +pci:v00001000d00000079sv00001000sd00009282* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9280-4i4e) + +pci:v00001000d00000079sv00001000sd00009290* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9280DE-24i4e) + +pci:v00001000d00000079sv00001014sd000003B2* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (ServeRAID M5015 SAS/SATA Controller) + +pci:v00001000d00000079sv00001014sd000003B3* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (ServeRAID M5025 SAS/SATA Controller) + +pci:v00001000d00000079sv00001028sd00001F15* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H800 Adapter) + +pci:v00001000d00000079sv00001028sd00001F16* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H700 Adapter) + +pci:v00001000d00000079sv00001028sd00001F17* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H700 Integrated) + +pci:v00001000d00000079sv00001028sd00001F18* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H700 Modular) + +pci:v00001000d00000079sv00001028sd00001F1A* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H800 Proto Adapter) + +pci:v00001000d00000079sv00001028sd00001F1B* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PERC H700 Integrated) + +pci:v00001000d00000079sv00001043sd00008480* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (PIKE-2108 16PD) + +pci:v00001000d00000079sv00001734sd00001176* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Ctrl SAS 6G 5/6 512MB (D2616)) + +pci:v00001000d00000079sv00001734sd00001177* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Ctrl SAS 6G 0/1 (D2607)) + +pci:v00001000d00000079sv00008086sd00009256* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (MegaRAID SAS 9260DE-8i) + +pci:v00001000d00000079sv00008086sd00009260* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Controller RS2BL040) + +pci:v00001000d00000079sv00008086sd00009261* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Controller RS2BL080) + +pci:v00001000d00000079sv00008086sd00009264* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (Warm Beach (Caster Lite)) + +pci:v00001000d00000079sv00008086sd00009267* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Controller RS2VB040) + +pci:v00001000d00000079sv00008086sd00009268* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2108 [Liberator] (RAID Controller RS2VB080) + +pci:v00001000d0000007C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078DE + +pci:v00001000d0000007Csv00001014sd00000395* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1078DE (ServeRAID-AR10is SAS/SATA Controller) + +pci:v00001000d0000007E* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD + +pci:v00001000d0000007Esv00001000sd00000504* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BLP4-800) + +pci:v00001000d0000007Esv00001000sd00000507* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BLP4-1600) + +pci:v00001000d0000007Esv00001000sd00000581* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BLP4-400) + +pci:v00001000d0000007Esv00001000sd0000100D* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BFH6-1200) + +pci:v00001000d0000007Esv00001000sd0000100E* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BFH8-1600) + +pci:v00001000d0000007Esv00001000sd0000107E* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro NWD-BFH8-3200) + +pci:v00001000d0000007Esv00001000sd00001310* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6302-8B1536) + +pci:v00001000d0000007Esv00001000sd00001311* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6302-8B2048) + +pci:v00001000d0000007Esv00001000sd00001314* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6302-8B4096) + +pci:v00001000d0000007Esv00001000sd0000150C* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6210-4A2048) + +pci:v00001000d0000007Esv00001000sd0000150F* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6210-4B2048) + +pci:v00001000d0000007Esv00001000sd0000160B* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6209-4A1024) + +pci:v00001000d0000007Esv00001000sd00001613* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro XP6209-4B2048) + +pci:v00001000d0000007Esv0000108Esd0000050A* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro ELP4x200_4d_n) + +pci:v00001000d0000007Esv0000108Esd00000581* + ID_MODEL_FROM_DATABASE=SSS6200 PCI-Express Flash SSD (Nytro ELP4x100_4d_n) + +pci:v00001000d00000080* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000081* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000082* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000083* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000084* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000085* + ID_MODEL_FROM_DATABASE=SAS2208 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000086* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000087* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00000087sv00001000sd00003020* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (9207-8i SAS2.1 HBA) + +pci:v00001000d00000087sv00001000sd00003040* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (9207-8e SAS2.1 HBA) + +pci:v00001000d00000087sv00001590sd00000044* + ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (H220i) + +pci:v00001000d0000008F* + ID_MODEL_FROM_DATABASE=53c875J + +pci:v00001000d0000008Fsv00001092sd00008000* + ID_MODEL_FROM_DATABASE=53c875J (FirePort 40 SCSI Controller) + +pci:v00001000d0000008Fsv00001092sd00008760* + ID_MODEL_FROM_DATABASE=53c875J (FirePort 40 Dual SCSI Host Adapter) + +pci:v00001000d00000090* + ID_MODEL_FROM_DATABASE=SAS3108 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000091* + ID_MODEL_FROM_DATABASE=SAS3108 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000094* + ID_MODEL_FROM_DATABASE=SAS3108 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000095* + ID_MODEL_FROM_DATABASE=SAS3108 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000096* + ID_MODEL_FROM_DATABASE=SAS3004 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000097* + ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d00000097sv00001028sd00001F45* + ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12GB/s HBA internal) + +pci:v00001000d00000097sv00001028sd00001F46* + ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12Gbps HBA) + +pci:v00001000d000000C0* + ID_MODEL_FROM_DATABASE=SAS3324 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C1* + ID_MODEL_FROM_DATABASE=SAS3324 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C2* + ID_MODEL_FROM_DATABASE=SAS3324 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C3* + ID_MODEL_FROM_DATABASE=SAS3324 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C4* + ID_MODEL_FROM_DATABASE=SAS3224 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C5* + ID_MODEL_FROM_DATABASE=SAS3316 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C6* + ID_MODEL_FROM_DATABASE=SAS3316 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C7* + ID_MODEL_FROM_DATABASE=SAS3316 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C8* + ID_MODEL_FROM_DATABASE=SAS3316 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000C9* + ID_MODEL_FROM_DATABASE=SAS3216 PCI-Express Fusion-MPT SAS-3 + +pci:v00001000d000000CE* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3316 [Intruder] + +pci:v00001000d000000CEsv00001000sd00009371* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3316 [Intruder] (MegaRAID SAS 9361-16i) + +pci:v00001000d000000CEsv00001000sd00009390* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3316 [Intruder] (MegaRAID SAS 9380-8i8e) + +pci:v00001000d000000CF* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3324 [Intruder] + +pci:v00001000d000000CFsv00001000sd00009370* + ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3324 [Intruder] (MegaRAID SAS 9361-24i) + +pci:v00001000d00000407* + ID_MODEL_FROM_DATABASE=MegaRAID + +pci:v00001000d00000407sv00001000sd00000530* + ID_MODEL_FROM_DATABASE=MegaRAID (530 SCSI 320-0X RAID Controller) + +pci:v00001000d00000407sv00001000sd00000531* + ID_MODEL_FROM_DATABASE=MegaRAID (531 SCSI 320-4X RAID Controller) + +pci:v00001000d00000407sv00001000sd00000532* + ID_MODEL_FROM_DATABASE=MegaRAID (532 SCSI 320-2X RAID Controller) + +pci:v00001000d00000407sv00001028sd00000531* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge Expandable RAID Controller 4/QC) + +pci:v00001000d00000407sv00001028sd00000533* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge Expandable RAID Controller 4/QC) + +pci:v00001000d00000407sv00008086sd00000530* + ID_MODEL_FROM_DATABASE=MegaRAID (Intel RAID Controller SRCZCRX) + +pci:v00001000d00000407sv00008086sd00000532* + ID_MODEL_FROM_DATABASE=MegaRAID (Intel RAID Controller SRCU42X) + +pci:v00001000d00000408* + ID_MODEL_FROM_DATABASE=MegaRAID + +pci:v00001000d00000408sv00001000sd00000001* + ID_MODEL_FROM_DATABASE=MegaRAID (SCSI 320-1E RAID Controller) + +pci:v00001000d00000408sv00001000sd00000002* + ID_MODEL_FROM_DATABASE=MegaRAID (SCSI 320-2E RAID Controller) + +pci:v00001000d00000408sv00001025sd0000004D* + ID_MODEL_FROM_DATABASE=MegaRAID (ACER ROMB-2E RAID Controller) + +pci:v00001000d00000408sv00001028sd00000001* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller PERC4e/SC) + +pci:v00001000d00000408sv00001028sd00000002* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller PERC4e/DC) + +pci:v00001000d00000408sv00001028sd00000012* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller RAC4) + +pci:v00001000d00000408sv00001028sd00000015* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller PERC5) + +pci:v00001000d00000408sv00001028sd00001F03* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller PERC5) + +pci:v00001000d00000408sv00001734sd00001065* + ID_MODEL_FROM_DATABASE=MegaRAID (FSC MegaRAID PCI Express ROMB) + +pci:v00001000d00000408sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=MegaRAID (Intel RAID Controller SRCU42E) + +pci:v00001000d00000408sv00008086sd00003449* + ID_MODEL_FROM_DATABASE=MegaRAID (Intel RAID Controller SROMBU) + +pci:v00001000d00000409* + ID_MODEL_FROM_DATABASE=MegaRAID + +pci:v00001000d00000409sv00001000sd00003004* + ID_MODEL_FROM_DATABASE=MegaRAID (SATA 300-4X RAID Controller) + +pci:v00001000d00000409sv00001000sd00003008* + ID_MODEL_FROM_DATABASE=MegaRAID (SATA 300-8X RAID Controller) + +pci:v00001000d00000409sv00008086sd00003008* + ID_MODEL_FROM_DATABASE=MegaRAID (RAID Controller SRCS28X) + +pci:v00001000d00000409sv00008086sd00003431* + ID_MODEL_FROM_DATABASE=MegaRAID (RAID Controller Alief SROMBU42E) + +pci:v00001000d00000409sv00008086sd00003499* + ID_MODEL_FROM_DATABASE=MegaRAID (RAID Controller Harwich SROMBU42E) + +pci:v00001000d00000411* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 + +pci:v00001000d00000411sv00001000sd00001001* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS 8408E) + +pci:v00001000d00000411sv00001000sd00001002* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS 8480E) + +pci:v00001000d00000411sv00001000sd00001003* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS 8344ELP) + +pci:v00001000d00000411sv00001000sd00001004* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS 8308ELP) + +pci:v00001000d00000411sv00001000sd00001008* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS 84016E) + +pci:v00001000d00000411sv00001000sd0000100C* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SATA 300-12E) + +pci:v00001000d00000411sv00001000sd0000100D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SATA 300-16E) + +pci:v00001000d00000411sv00001000sd00002004* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SATA 300-8ELP) + +pci:v00001000d00000411sv00001000sd00002005* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SATA 300-4ELP) + +pci:v00001000d00000411sv00001033sd00008287* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000411sv00001054sd00003016* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS RoMB Server) + +pci:v00001000d00000411sv00001734sd00001081* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000411sv00001734sd000010A3* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (MegaRAID SAS PCI Express ROMB) + +pci:v00001000d00000411sv00008086sd00001001* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (RAID Controller SRCSAS18E) + +pci:v00001000d00000411sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (RAID Controller SRCSAS144E) + +pci:v00001000d00000411sv00008086sd00003500* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (SROMBSAS18E RAID Controller) + +pci:v00001000d00000411sv00008086sd00003501* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (SROMBSAS18E RAID Controller) + +pci:v00001000d00000411sv00008086sd00003504* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 (SROMBSAS18E RAID Controller) + +pci:v00001000d00000413* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 [Verde ZCR] + +pci:v00001000d00000413sv00001000sd00001005* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 1068 [Verde ZCR] (MegaRAID SAS 8300XLP) + +pci:v00001000d00000621* + ID_MODEL_FROM_DATABASE=FC909 Fibre Channel Adapter + +pci:v00001000d00000622* + ID_MODEL_FROM_DATABASE=FC929 Fibre Channel Adapter + +pci:v00001000d00000622sv00001000sd00001020* + ID_MODEL_FROM_DATABASE=FC929 Fibre Channel Adapter (44929 O Dual Fibre Channel card) + +pci:v00001000d00000623* + ID_MODEL_FROM_DATABASE=FC929 LAN + +pci:v00001000d00000624* + ID_MODEL_FROM_DATABASE=FC919 Fibre Channel Adapter + +pci:v00001000d00000625* + ID_MODEL_FROM_DATABASE=FC919 LAN + +pci:v00001000d00000626* + ID_MODEL_FROM_DATABASE=FC929X Fibre Channel Adapter + +pci:v00001000d00000626sv00001000sd00001010* + ID_MODEL_FROM_DATABASE=FC929X Fibre Channel Adapter (7202-XP-LC Dual Fibre Channel card) + +pci:v00001000d00000627* + ID_MODEL_FROM_DATABASE=FC929X LAN + +pci:v00001000d00000628* + ID_MODEL_FROM_DATABASE=FC919X Fibre Channel Adapter + +pci:v00001000d00000629* + ID_MODEL_FROM_DATABASE=FC919X LAN + +pci:v00001000d00000640* + ID_MODEL_FROM_DATABASE=FC949X Fibre Channel Adapter + +pci:v00001000d00000642* + ID_MODEL_FROM_DATABASE=FC939X Fibre Channel Adapter + +pci:v00001000d00000646* + ID_MODEL_FROM_DATABASE=FC949ES Fibre Channel Adapter + +pci:v00001000d00000701* + ID_MODEL_FROM_DATABASE=83C885 NT50 DigitalScape Fast Ethernet + +pci:v00001000d00000702* + ID_MODEL_FROM_DATABASE=Yellowfin G-NIC gigabit ethernet + +pci:v00001000d00000702sv00001318sd00000000* + ID_MODEL_FROM_DATABASE=Yellowfin G-NIC gigabit ethernet (PEI100X) + +pci:v00001000d00000804* + ID_MODEL_FROM_DATABASE=SA2010 + +pci:v00001000d00000805* + ID_MODEL_FROM_DATABASE=SA2010ZC + +pci:v00001000d00000806* + ID_MODEL_FROM_DATABASE=SA2020 + +pci:v00001000d00000807* + ID_MODEL_FROM_DATABASE=SA2020ZC + +pci:v00001000d00000901* + ID_MODEL_FROM_DATABASE=61C102 + +pci:v00001000d00001000* + ID_MODEL_FROM_DATABASE=63C815 + +pci:v00001000d00001960* + ID_MODEL_FROM_DATABASE=MegaRAID + +pci:v00001000d00001960sv00001000sd00000518* + ID_MODEL_FROM_DATABASE=MegaRAID (518 SCSI 320-2 Controller) + +pci:v00001000d00001960sv00001000sd00000520* + ID_MODEL_FROM_DATABASE=MegaRAID (520 SCSI 320-1 Controller) + +pci:v00001000d00001960sv00001000sd00000522* + ID_MODEL_FROM_DATABASE=MegaRAID (522 i4 133 RAID Controller) + +pci:v00001000d00001960sv00001000sd00000523* + ID_MODEL_FROM_DATABASE=MegaRAID (SATA 150-6 RAID Controller) + +pci:v00001000d00001960sv00001000sd00004523* + ID_MODEL_FROM_DATABASE=MegaRAID (SATA 150-4 RAID Controller) + +pci:v00001000d00001960sv00001000sd0000A520* + ID_MODEL_FROM_DATABASE=MegaRAID (ZCR SCSI 320-0 Controller) + +pci:v00001000d00001960sv00001028sd00000518* + ID_MODEL_FROM_DATABASE=MegaRAID (518 DELL PERC 4/DC RAID Controller) + +pci:v00001000d00001960sv00001028sd00000520* + ID_MODEL_FROM_DATABASE=MegaRAID (520 DELL PERC 4/SC RAID Controller) + +pci:v00001000d00001960sv00001028sd00000531* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge Expandable RAID Controller 4/QC) + +pci:v00001000d00001960sv00001028sd00000533* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge Expandable RAID Controller 4/QC) + +pci:v00001000d00001960sv00008086sd00000520* + ID_MODEL_FROM_DATABASE=MegaRAID (RAID Controller SRCU41L) + +pci:v00001000d00001960sv00008086sd00000523* + ID_MODEL_FROM_DATABASE=MegaRAID (RAID Controller SRCS16) + +pci:v00001000d00003050* + ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 + +pci:v00001000d00006001* + ID_MODEL_FROM_DATABASE=DX1 Multiformat Broadcast HD/SD Encoder/Decoder + +pci:v00001001* + ID_VENDOR_FROM_DATABASE=Kolter Electronic + +pci:v00001001d00000010* + ID_MODEL_FROM_DATABASE=PCI 1616 Measurement card with 32 digital I/O lines + +pci:v00001001d00000011* + ID_MODEL_FROM_DATABASE=OPTO-PCI Opto-Isolated digital I/O board + +pci:v00001001d00000012* + ID_MODEL_FROM_DATABASE=PCI-AD/DA Analogue I/O board + +pci:v00001001d00000013* + ID_MODEL_FROM_DATABASE=PCI-OPTO-RELAIS Digital I/O board with relay outputs + +pci:v00001001d00000014* + ID_MODEL_FROM_DATABASE=PCI-Counter/Timer Counter Timer board + +pci:v00001001d00000015* + ID_MODEL_FROM_DATABASE=PCI-DAC416 Analogue output board + +pci:v00001001d00000016* + ID_MODEL_FROM_DATABASE=PCI-MFB Analogue I/O board + +pci:v00001001d00000017* + ID_MODEL_FROM_DATABASE=PROTO-3 PCI Prototyping board + +pci:v00001001d00009100* + ID_MODEL_FROM_DATABASE=INI-9100/9100W SCSI Host + +pci:v00001002* + ID_VENDOR_FROM_DATABASE=Advanced Micro Devices, Inc. [AMD/ATI] + +pci:v00001002d00001304* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001305* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001306* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001307* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001308* + ID_MODEL_FROM_DATABASE=Kaveri HDMI/DP Audio Controller + +pci:v00001002d00001309* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6/R7 Graphics] + +pci:v00001002d0000130A* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics] + +pci:v00001002d0000130B* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R4 Graphics] + +pci:v00001002d0000130C* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R7 Graphics] + +pci:v00001002d0000130D* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics] + +pci:v00001002d0000130E* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R5 Graphics] + +pci:v00001002d0000130F* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R7 Graphics] + +pci:v00001002d00001310* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001311* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001312* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001313* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R7 Graphics] + +pci:v00001002d00001314* + ID_MODEL_FROM_DATABASE=Wrestler HDMI Audio + +pci:v00001002d00001314sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=Wrestler HDMI Audio (PURE Fusion Mini) + +pci:v00001002d00001315* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R5 Graphics] + +pci:v00001002d00001316* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R5 Graphics] + +pci:v00001002d00001317* + ID_MODEL_FROM_DATABASE=Kaveri + +pci:v00001002d00001318* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R5 Graphics] + +pci:v00001002d0000131B* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R4 Graphics] + +pci:v00001002d0000131C* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R7 Graphics] + +pci:v00001002d0000131D* + ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics] + +pci:v00001002d00001714* + ID_MODEL_FROM_DATABASE=BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] + +pci:v00001002d00001714sv0000103Csd0000168B* + ID_MODEL_FROM_DATABASE=BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] (ProBook 4535s) + +pci:v00001002d00003150* + ID_MODEL_FROM_DATABASE=RV380/M24 [Mobility Radeon X600] + +pci:v00001002d00003150sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=RV380/M24 [Mobility Radeon X600] (nx8220) + +pci:v00001002d00003151* + ID_MODEL_FROM_DATABASE=RV380 GL [FireMV 2400] + +pci:v00001002d00003152* + ID_MODEL_FROM_DATABASE=RV370/M22 [Mobility Radeon X300] + +pci:v00001002d00003154* + ID_MODEL_FROM_DATABASE=RV380/M24 GL [Mobility FireGL V3200] + +pci:v00001002d00003155* + ID_MODEL_FROM_DATABASE=RV380 GL [FireMV 2400] + +pci:v00001002d00003171* + ID_MODEL_FROM_DATABASE=RV380 GL [FireMV 2400] (Secondary) + +pci:v00001002d00003E50* + ID_MODEL_FROM_DATABASE=RV380 [Radeon X600] + +pci:v00001002d00003E54* + ID_MODEL_FROM_DATABASE=RV380 GL [FireGL V3200] + +pci:v00001002d00003E70* + ID_MODEL_FROM_DATABASE=RV380 [Radeon X600] (Secondary) + +pci:v00001002d00004136* + ID_MODEL_FROM_DATABASE=RS100 [Mobility IGP 320M] + +pci:v00001002d00004137* + ID_MODEL_FROM_DATABASE=RS200 [Radeon IGP 340] + +pci:v00001002d00004144* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500] + +pci:v00001002d00004146* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700 PRO] + +pci:v00001002d00004147* + ID_MODEL_FROM_DATABASE=R300 GL [FireGL Z1] + +pci:v00001002d00004148* + ID_MODEL_FROM_DATABASE=R350 [Radeon 9800/9800 SE] + +pci:v00001002d00004150* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] + +pci:v00001002d00004150sv00001002sd00000002* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (R9600 Pro primary (Asus OEM for HP)) + +pci:v00001002d00004150sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (R9600 Pro secondary (Asus OEM for HP)) + +pci:v00001002d00004150sv00001002sd00004722* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (All-in-Wonder 2006 AGP Edition) + +pci:v00001002d00004150sv00001458sd00004024* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (GV-R96128D) + +pci:v00001002d00004150sv0000148Csd00002064* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (R96A-C3N) + +pci:v00001002d00004150sv0000148Csd00002066* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (R96A-C3N) + +pci:v00001002d00004150sv0000174Bsd00007C19* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Atlantis Radeon 9600 Pro) + +pci:v00001002d00004150sv0000174Bsd00007C29* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (GC-R9600PRO) + +pci:v00001002d00004150sv000017EEsd00002002* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Radeon 9600 256Mb Primary) + +pci:v00001002d00004150sv000018BCsd00000101* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (GC-R9600PRO (Primary)) + +pci:v00001002d00004151* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600 Series] + +pci:v00001002d00004151sv00001043sd0000C004* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600 Series] (A9600SE) + +pci:v00001002d00004151sv0000174Bsd00007C37* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600 Series] (Radeon 9600 SE) + +pci:v00001002d00004152* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] + +pci:v00001002d00004152sv00001002sd00000002* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (Radeon 9600XT) + +pci:v00001002d00004152sv00001002sd00004772* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (All-in-Wonder 9600 XT) + +pci:v00001002d00004152sv00001043sd0000C002* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (Radeon 9600 XT TVD) + +pci:v00001002d00004152sv00001043sd0000C01A* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (A9600XT/TD) + +pci:v00001002d00004152sv00001462sd00009510* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (RX9600XT (MS-8951)) + +pci:v00001002d00004152sv0000174Bsd00007C29* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (Radeon 9600XT) + +pci:v00001002d00004152sv00001787sd00004002* + ID_MODEL_FROM_DATABASE=RV360 [Radeon 9600/X1050 Series] (Radeon 9600 XT) + +pci:v00001002d00004153* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550] + +pci:v00001002d00004153sv00001043sd0000010C* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550] (A9550GE/TD) + +pci:v00001002d00004153sv00001462sd0000932C* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550] (RX9550SE-TD128 (MS-8932)) + +pci:v00001002d00004154* + ID_MODEL_FROM_DATABASE=RV350 GL [FireGL T2] + +pci:v00001002d00004155* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] + +pci:v00001002d00004157* + ID_MODEL_FROM_DATABASE=RV350 GL [FireGL T2] + +pci:v00001002d00004158* + ID_MODEL_FROM_DATABASE=68800AX [Graphics Ultra Pro PCI] + +pci:v00001002d00004164* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO] (Secondary) + +pci:v00001002d00004165* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700 PRO] (Secondary) + +pci:v00001002d00004166* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700 PRO] (Secondary) + +pci:v00001002d00004168* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9800 SE] (Secondary) + +pci:v00001002d00004170* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) + +pci:v00001002d00004170sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (R9600 Pro secondary (Asus OEM for HP)) + +pci:v00001002d00004170sv00001002sd00004723* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (All-in-Wonder 2006 AGP Edition (Secondary)) + +pci:v00001002d00004170sv00001458sd00004025* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (GV-R96128D (Secondary)) + +pci:v00001002d00004170sv0000148Csd00002067* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (R96A-C3N (Secondary)) + +pci:v00001002d00004170sv0000174Bsd00007C28* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (GC-R9600PRO (Secondary)) + +pci:v00001002d00004170sv000017EEsd00002003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (Radeon 9600 256Mb (Secondary)) + +pci:v00001002d00004170sv000018BCsd00000100* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] (Secondary) (GC-R9600PRO (Secondary)) + +pci:v00001002d00004171* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) + +pci:v00001002d00004171sv00001043sd0000C005* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) (A9600SE (Secondary)) + +pci:v00001002d00004171sv0000174Bsd00007C36* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) (Radeon 9600 SE (secondary)) + +pci:v00001002d00004172* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) + +pci:v00001002d00004172sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (Radeon 9600XT (Secondary)) + +pci:v00001002d00004172sv00001002sd00004773* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (All-in-Wonder 9600 XT (Secondary)) + +pci:v00001002d00004172sv00001043sd0000C003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (A9600XT (Secondary)) + +pci:v00001002d00004172sv00001043sd0000C01B* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (A9600XT/TD (Secondary)) + +pci:v00001002d00004172sv0000174Bsd00007C28* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (Radeon 9600XT (Secondary)) + +pci:v00001002d00004172sv00001787sd00004003* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600/X1050 Series] (Secondary) (Radeon 9600 XT (Secondary)) + +pci:v00001002d00004173* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550] (Secondary) + +pci:v00001002d00004173sv00001043sd0000010D* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550] (Secondary) (A9550GE/TD (Secondary)) + +pci:v00001002d00004242* + ID_MODEL_FROM_DATABASE=R200 [All-In-Wonder Radeon 8500 DV] + +pci:v00001002d00004242sv00001002sd000002AA* + ID_MODEL_FROM_DATABASE=R200 [All-In-Wonder Radeon 8500 DV] (Radeon 8500 AIW DV Edition) + +pci:v00001002d00004243* + ID_MODEL_FROM_DATABASE=R200 PCI Bridge [All-in-Wonder Radeon 8500DV] + +pci:v00001002d00004336* + ID_MODEL_FROM_DATABASE=RS100 [Radeon IGP 320M] + +pci:v00001002d00004336sv00001002sd00004336* + ID_MODEL_FROM_DATABASE=RS100 [Radeon IGP 320M] (Pavilion ze4300 ATI Radeon Mobility U1 (IGP 320 M)) + +pci:v00001002d00004336sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=RS100 [Radeon IGP 320M] (Pavilion ze4400 builtin Video) + +pci:v00001002d00004336sv0000161Fsd00002029* + ID_MODEL_FROM_DATABASE=RS100 [Radeon IGP 320M] (eMachines M5312 builtin Video) + +pci:v00001002d00004337* + ID_MODEL_FROM_DATABASE=RS200M [Radeon IGP 330M/340M/345M/350M] + +pci:v00001002d00004337sv00001014sd0000053A* + ID_MODEL_FROM_DATABASE=RS200M [Radeon IGP 330M/340M/345M/350M] (ThinkPad R40e) + +pci:v00001002d00004337sv0000103Csd00000850* + ID_MODEL_FROM_DATABASE=RS200M [Radeon IGP 330M/340M/345M/350M] (Radeon IGP 345M) + +pci:v00001002d00004341* + ID_MODEL_FROM_DATABASE=IXP150 AC'97 Audio Controller + +pci:v00001002d00004342* + ID_MODEL_FROM_DATABASE=IXP200 3COM 3C920B Ethernet Controller + +pci:v00001002d00004345* + ID_MODEL_FROM_DATABASE=EHCI USB Controller + +pci:v00001002d00004347* + ID_MODEL_FROM_DATABASE=OHCI USB Controller #1 + +pci:v00001002d00004348* + ID_MODEL_FROM_DATABASE=OHCI USB Controller #2 + +pci:v00001002d00004349* + ID_MODEL_FROM_DATABASE=Dual Channel Bus Master PCI IDE Controller + +pci:v00001002d0000434D* + ID_MODEL_FROM_DATABASE=IXP AC'97 Modem + +pci:v00001002d00004353* + ID_MODEL_FROM_DATABASE=SMBus + +pci:v00001002d00004354* + ID_MODEL_FROM_DATABASE=215CT [Mach64 CT PCI] + +pci:v00001002d00004358* + ID_MODEL_FROM_DATABASE=210888CX [Mach64 CX] + +pci:v00001002d00004361* + ID_MODEL_FROM_DATABASE=IXP SB300 AC'97 Audio Controller + +pci:v00001002d00004363* + ID_MODEL_FROM_DATABASE=SMBus + +pci:v00001002d0000436E* + ID_MODEL_FROM_DATABASE=436E Serial ATA Controller + +pci:v00001002d00004370* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller + +pci:v00001002d00004370sv00001025sd00000079* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (Aspire 5024WLMMi) + +pci:v00001002d00004370sv00001025sd00000091* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (Aspire 5032WXMi) + +pci:v00001002d00004370sv0000103Csd00002A05* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004370sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (MX6125) + +pci:v00001002d00004370sv0000105Bsd00000C81* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (Realtek ALC 653) + +pci:v00001002d00004370sv0000107Bsd00000300* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (MX6421) + +pci:v00001002d00004370sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Audio Controller (MS-1013 Notebook) + +pci:v00001002d00004371* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-PCI Bridge + +pci:v00001002d00004371sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-PCI Bridge (MX6125) + +pci:v00001002d00004371sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-PCI Bridge (Aspire L250) + +pci:v00001002d00004372* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller + +pci:v00001002d00004372sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller (Aspire 5024WLMMi) + +pci:v00001002d00004372sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004372sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller (MX6125) + +pci:v00001002d00004372sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller (MS-1013 Notebook) + +pci:v00001002d00004372sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 SMBus Controller (Aspire L250) + +pci:v00001002d00004373* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB2 Host Controller + +pci:v00001002d00004373sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB2 Host Controller (Aspire 5024WLMMi) + +pci:v00001002d00004373sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB2 Host Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004373sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB2 Host Controller (MX6125) + +pci:v00001002d00004373sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB2 Host Controller (Aspire L250) + +pci:v00001002d00004374* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller + +pci:v00001002d00004374sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004374sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (MX6125) + +pci:v00001002d00004374sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (Aspire L250) + +pci:v00001002d00004375* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller + +pci:v00001002d00004375sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (Aspire 5024WLMMi) + +pci:v00001002d00004375sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004375sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (MX6125) + +pci:v00001002d00004375sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 USB Host Controller (Aspire L250) + +pci:v00001002d00004376* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller + +pci:v00001002d00004376sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller (Aspire 5024WLMMi) + +pci:v00001002d00004376sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004376sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller (MX6125) + +pci:v00001002d00004376sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller (MS-1013 Notebook) + +pci:v00001002d00004376sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 IDE Controller (Aspire L250) + +pci:v00001002d00004377* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-ISA Bridge + +pci:v00001002d00004377sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-ISA Bridge (Aspire 5024WLMi) + +pci:v00001002d00004377sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-ISA Bridge (Pavilion t3030.de Desktop PC) + +pci:v00001002d00004377sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-ISA Bridge (MX6125) + +pci:v00001002d00004377sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=IXP SB4x0 PCI-ISA Bridge (Aspire L250) + +pci:v00001002d00004378* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Modem Controller + +pci:v00001002d00004378sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Modem Controller (Aspire 5024WLMMi) + +pci:v00001002d00004378sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Modem Controller (MX6125) + +pci:v00001002d00004378sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=IXP SB400 AC'97 Modem Controller (MS-1013 Notebook) + +pci:v00001002d00004379* + ID_MODEL_FROM_DATABASE=IXP SB4x0 Serial ATA Controller + +pci:v00001002d00004379sv00001462sd00007141* + ID_MODEL_FROM_DATABASE=IXP SB4x0 Serial ATA Controller (Aspire L250) + +pci:v00001002d0000437A* + ID_MODEL_FROM_DATABASE=IXP SB400 Serial ATA Controller + +pci:v00001002d0000437Asv00001002sd00004379* + ID_MODEL_FROM_DATABASE=IXP SB400 Serial ATA Controller (4379 Serial ATA Controller) + +pci:v00001002d0000437Asv00001002sd0000437A* + ID_MODEL_FROM_DATABASE=IXP SB400 Serial ATA Controller (437A Serial ATA Controller) + +pci:v00001002d0000437Asv00001462sd00007141* + ID_MODEL_FROM_DATABASE=IXP SB400 Serial ATA Controller (Aspire L250) + +pci:v00001002d0000437Asv000014F1sd00008800* + ID_MODEL_FROM_DATABASE=IXP SB400 Serial ATA Controller (Leadtek WinFast TV2000XP Expert) + +pci:v00001002d0000437B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 High Definition Audio Controller + +pci:v00001002d0000437Bsv00001002sd0000437B* + ID_MODEL_FROM_DATABASE=IXP SB4x0 High Definition Audio Controller + +pci:v00001002d0000437Bsv000010CFsd00001326* + ID_MODEL_FROM_DATABASE=IXP SB4x0 High Definition Audio Controller (Fujitsu Lifebook A3040) + +pci:v00001002d0000437Bsv00001734sd000010B8* + ID_MODEL_FROM_DATABASE=IXP SB4x0 High Definition Audio Controller (Realtek High Definition Audio) + +pci:v00001002d00004380* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA + +pci:v00001002d00004380sv0000103Csd00002813* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (DC5750 Microtower) + +pci:v00001002d00004380sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (Satellite P305D-S8995E) + +pci:v00001002d00004380sv00001458sd0000B003* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (GA-MA790FX-DS5 (rev. 1.0)) + +pci:v00001002d00004380sv00001458sd0000B005* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (Gigabyte GA-MA69G-S3H Motherboard) + +pci:v00001002d00004380sv00001462sd00007327* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (K9AG Neo2) + +pci:v00001002d00004380sv000017F2sd00005999* + ID_MODEL_FROM_DATABASE=SB600 Non-Raid-5 SATA (KI690-AM2 Motherboard) + +pci:v00001002d00004381* + ID_MODEL_FROM_DATABASE=SB600 SATA Controller (RAID 5 mode) + +pci:v00001002d00004382* + ID_MODEL_FROM_DATABASE=SB600 AC97 Audio + +pci:v00001002d00004383* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) + +pci:v00001002d00004383sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (A785GM-M) + +pci:v00001002d00004383sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Pavilion DM1Z-3000) + +pci:v00001002d00004383sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (DC5750 Microtower) + +pci:v00001002d00004383sv00001043sd00008230* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (M3A78-EH Motherboard) + +pci:v00001002d00004383sv00001043sd0000836C* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (M4A785TD Motherboard) + +pci:v00001002d00004383sv00001043sd00008410* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (M4A89GTD PRO/USB3 Motherboard) + +pci:v00001002d00004383sv00001043sd0000841B* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (M5A88-V EVO) + +pci:v00001002d00004383sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Satellite P305D-S8995E) + +pci:v00001002d00004383sv00001458sd0000A022* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (GA-MA770-DS3rev2.0 Motherboard) + +pci:v00001002d00004383sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (KI690-AM2 Motherboard) + +pci:v00001002d00004384* + ID_MODEL_FROM_DATABASE=SBx00 PCI to PCI Bridge + +pci:v00001002d00004385* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller + +pci:v00001002d00004385sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (A785GM-M) + +pci:v00001002d00004385sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (Pavilion DM1Z-3000) + +pci:v00001002d00004385sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (DC5750 Microtower) + +pci:v00001002d00004385sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (M3A78-EH Motherboard) + +pci:v00001002d00004385sv00001043sd00008389* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (M4A785TD Motherboard) + +pci:v00001002d00004385sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (Satellite P305D-S8995E) + +pci:v00001002d00004385sv00001458sd00004385* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (GA-MA770-DS3rev2.0 Motherboard) + +pci:v00001002d00004385sv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (K9AG Neo2) + +pci:v00001002d00004385sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (H8DGU) + +pci:v00001002d00004385sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (PURE Fusion Mini) + +pci:v00001002d00004385sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SBx00 SMBus Controller (KI690-AM2 Motherboard) + +pci:v00001002d00004386* + ID_MODEL_FROM_DATABASE=SB600 USB Controller (EHCI) + +pci:v00001002d00004386sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB Controller (EHCI) (DC5750 Microtower) + +pci:v00001002d00004386sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB Controller (EHCI) (Satellite P305D-S8995E) + +pci:v00001002d00004386sv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB Controller (EHCI) (K9AG Neo2) + +pci:v00001002d00004386sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB Controller (EHCI) (KI690-AM2 Motherboard) + +pci:v00001002d00004387* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI0) + +pci:v00001002d00004387sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI0) (DC5750 Microtower) + +pci:v00001002d00004387sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI0) (Satellite P305D-S8995E) + +pci:v00001002d00004387sv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI0) (K9AG Neo2) + +pci:v00001002d00004387sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI0) (KI690-AM2 Motherboard) + +pci:v00001002d00004388* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI1) + +pci:v00001002d00004388sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI1) (DC5750 Microtower) + +pci:v00001002d00004388sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI1) (Satellite P305D-S8995E) + +pci:v00001002d00004388sv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI1) (K9AG Neo2) + +pci:v00001002d00004388sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI1) (KI690-AM2 Motherboard) + +pci:v00001002d00004389* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI2) + +pci:v00001002d00004389sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI2) (DC5750 Microtower) + +pci:v00001002d00004389sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI2) (Satellite P305D-S8995E) + +pci:v00001002d00004389sv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI2) (K9AG Neo2) + +pci:v00001002d00004389sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI2) (KI690-AM2 Motherboard) + +pci:v00001002d0000438A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI3) + +pci:v00001002d0000438Asv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI3) (DC5750 Microtower) + +pci:v00001002d0000438Asv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI3) (Satellite P305D-S8995E) + +pci:v00001002d0000438Asv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI3) (K9AG Neo2) + +pci:v00001002d0000438Asv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI3) (KI690-AM2 Motherboard) + +pci:v00001002d0000438B* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI4) + +pci:v00001002d0000438Bsv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI4) (DC5750 Microtower) + +pci:v00001002d0000438Bsv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI4) (Satellite P305D-S8995E) + +pci:v00001002d0000438Bsv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI4) (K9AG Neo2) + +pci:v00001002d0000438Bsv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 USB (OHCI4) (KI690-AM2 Motherboard) + +pci:v00001002d0000438C* + ID_MODEL_FROM_DATABASE=SB600 IDE + +pci:v00001002d0000438Csv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 IDE (DC5750 Microtower) + +pci:v00001002d0000438Csv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 IDE (Satellite P305D-S8995E) + +pci:v00001002d0000438Csv00001458sd00005002* + ID_MODEL_FROM_DATABASE=SB600 IDE (Gigabyte GA-MA69G-S3H Motherboard) + +pci:v00001002d0000438Csv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 IDE (K9AG Neo2) + +pci:v00001002d0000438Csv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 IDE (KI690-AM2 Motherboard) + +pci:v00001002d0000438D* + ID_MODEL_FROM_DATABASE=SB600 PCI to LPC Bridge + +pci:v00001002d0000438Dsv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=SB600 PCI to LPC Bridge (DC5750 Microtower) + +pci:v00001002d0000438Dsv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=SB600 PCI to LPC Bridge (Satellite P305D-S8995E) + +pci:v00001002d0000438Dsv00001462sd00007368* + ID_MODEL_FROM_DATABASE=SB600 PCI to LPC Bridge (K9AG Neo2) + +pci:v00001002d0000438Dsv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=SB600 PCI to LPC Bridge (KI690-AM2 Motherboard) + +pci:v00001002d0000438E* + ID_MODEL_FROM_DATABASE=SB600 AC97 Modem + +pci:v00001002d00004390* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [IDE mode] + +pci:v00001002d00004390sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [IDE mode] (M3A78-EH Motherboard) + +pci:v00001002d00004390sv00001043sd00008389* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [IDE mode] (M4A785TD Motherboard) + +pci:v00001002d00004390sv00001458sd0000B002* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [IDE mode] (GA-MA770-DS3rev2.0 Motherboard) + +pci:v00001002d00004390sv00001849sd00004390* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [IDE mode] (Motherboard (one of many)) + +pci:v00001002d00004391* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] + +pci:v00001002d00004391sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (Pavilion DM1Z-3000) + +pci:v00001002d00004391sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (M3A78-EH Motherboard) + +pci:v00001002d00004391sv00001043sd00008443* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (M5A88-V EVO) + +pci:v00001002d00004391sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (PURE Fusion Mini) + +pci:v00001002d00004392* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [Non-RAID5 mode] + +pci:v00001002d00004393* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [RAID5 mode] + +pci:v00001002d00004394* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] + +pci:v00001002d00004395* + ID_MODEL_FROM_DATABASE=SB8x0/SB9x0 SATA Controller [Storage mode] + +pci:v00001002d00004396* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller + +pci:v00001002d00004396sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (A785GM-M) + +pci:v00001002d00004396sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (Pavilion DM1Z-3000) + +pci:v00001002d00004396sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (M3A78-EH Motherboard) + +pci:v00001002d00004396sv00001043sd00008443* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (M5A88-V EVO) + +pci:v00001002d00004396sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (H8DGU) + +pci:v00001002d00004396sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (PURE Fusion Mini) + +pci:v00001002d00004397* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller + +pci:v00001002d00004397sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (A785GM-M) + +pci:v00001002d00004397sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (Pavilion DM1Z-3000) + +pci:v00001002d00004397sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (M3A78-EH Motherboard) + +pci:v00001002d00004397sv00001043sd00008443* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (M5A88-V EVO) + +pci:v00001002d00004397sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (H8DGU) + +pci:v00001002d00004397sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (PURE Fusion Mini) + +pci:v00001002d00004398* + ID_MODEL_FROM_DATABASE=SB7x0 USB OHCI1 Controller + +pci:v00001002d00004398sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0 USB OHCI1 Controller (A785GM-M) + +pci:v00001002d00004398sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0 USB OHCI1 Controller (M3A78-EH Motherboard) + +pci:v00001002d00004398sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=SB7x0 USB OHCI1 Controller (H8DGU) + +pci:v00001002d00004399* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI2 Controller + +pci:v00001002d00004399sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI2 Controller (A785GM-M) + +pci:v00001002d00004399sv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI2 Controller (M3A78-EH Motherboard) + +pci:v00001002d00004399sv00001043sd00008443* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI2 Controller (M5A88-V EVO) + +pci:v00001002d00004399sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI2 Controller (PURE Fusion Mini) + +pci:v00001002d0000439C* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller + +pci:v00001002d0000439Csv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (A785GM-M) + +pci:v00001002d0000439Csv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (M3A78-EH Motherboard) + +pci:v00001002d0000439D* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller + +pci:v00001002d0000439Dsv00001019sd00002120* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (A785GM-M) + +pci:v00001002d0000439Dsv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (Pavilion DM1Z-3000) + +pci:v00001002d0000439Dsv00001043sd000082EF* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (M3A78-EH Motherboard) + +pci:v00001002d0000439Dsv00001043sd00008443* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (M5A88-V EVO) + +pci:v00001002d0000439Dsv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (PURE Fusion Mini) + +pci:v00001002d000043A0* + ID_MODEL_FROM_DATABASE=SB700/SB800/SB900 PCI to PCI bridge (PCIE port 0) + +pci:v00001002d000043A1* + ID_MODEL_FROM_DATABASE=SB700/SB800/SB900 PCI to PCI bridge (PCIE port 1) + +pci:v00001002d000043A2* + ID_MODEL_FROM_DATABASE=SB900 PCI to PCI bridge (PCIE port 2) + +pci:v00001002d000043A3* + ID_MODEL_FROM_DATABASE=SB900 PCI to PCI bridge (PCIE port 3) + +pci:v00001002d00004437* + ID_MODEL_FROM_DATABASE=RS250 [Mobility Radeon 7000 IGP] + +pci:v00001002d00004554* + ID_MODEL_FROM_DATABASE=210888ET [Mach64 ET] + +pci:v00001002d00004654* + ID_MODEL_FROM_DATABASE=Mach64 VT + +pci:v00001002d00004742* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X + +pci:v00001002d00004742sv00001002sd00000040* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001002sd00000044* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001002sd00000061* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro AIW AGP 2X) + +pci:v00001002d00004742sv00001002sd00000062* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro AIW AGP 2X) + +pci:v00001002d00004742sv00001002sd00000063* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro AIW AGP 2X) + +pci:v00001002d00004742sv00001002sd00000080* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001002sd00000084* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001002sd00004742* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001002sd00008001* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001028sd00000082* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001028sd00004082* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Optiplex GX1 Onboard Display Adapter) + +pci:v00001002d00004742sv00001028sd00008082* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00001028sd0000C082* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004742sv00008086sd00004152* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Xpert 98D AGP 2X) + +pci:v00001002d00004742sv00008086sd0000464A* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 2X (Rage Pro Turbo AGP 2X) + +pci:v00001002d00004744* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 1X + +pci:v00001002d00004744sv00001002sd00004744* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 1X (Rage Pro Turbo AGP) + +pci:v00001002d00004744sv00008086sd00004D55* + ID_MODEL_FROM_DATABASE=3D Rage PRO AGP 1X (Rage 3D Pro AGP 1X [Intel MU440EX]) + +pci:v00001002d00004749* + ID_MODEL_FROM_DATABASE=3D Rage PRO PCI + +pci:v00001002d00004749sv00001002sd00000061* + ID_MODEL_FROM_DATABASE=3D Rage PRO PCI (Rage Pro AIW) + +pci:v00001002d00004749sv00001002sd00000062* + ID_MODEL_FROM_DATABASE=3D Rage PRO PCI (Rage Pro AIW) + +pci:v00001002d0000474D* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X + +pci:v00001002d0000474Dsv00001002sd00000004* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X (Xpert 98 RXL AGP 2X) + +pci:v00001002d0000474Dsv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X (Xpert 98 RXL AGP 2X) + +pci:v00001002d0000474Dsv00001002sd00000080* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X + +pci:v00001002d0000474Dsv00001002sd00000084* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X (Xpert 98 AGP 2X) + +pci:v00001002d0000474Dsv00001002sd0000474D* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X (Rage XL AGP) + +pci:v00001002d0000474Dsv00001033sd0000806A* + ID_MODEL_FROM_DATABASE=Rage XL AGP 2X (Rage XL AGP) + +pci:v00001002d0000474E* + ID_MODEL_FROM_DATABASE=Rage XC AGP + +pci:v00001002d0000474Esv00001002sd0000474E* + ID_MODEL_FROM_DATABASE=Rage XC AGP + +pci:v00001002d0000474F* + ID_MODEL_FROM_DATABASE=Rage XL + +pci:v00001002d0000474Fsv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage XL + +pci:v00001002d0000474Fsv00001002sd0000474F* + ID_MODEL_FROM_DATABASE=Rage XL + +pci:v00001002d00004750* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI + +pci:v00001002d00004750sv00001002sd00000040* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI (Rage Pro Turbo) + +pci:v00001002d00004750sv00001002sd00000044* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI (Rage Pro Turbo) + +pci:v00001002d00004750sv00001002sd00000080* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI (Rage Pro Turbo) + +pci:v00001002d00004750sv00001002sd00000084* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI (Rage Pro Turbo) + +pci:v00001002d00004750sv00001002sd00004750* + ID_MODEL_FROM_DATABASE=3D Rage Pro PCI (Rage Pro Turbo) + +pci:v00001002d00004752* + ID_MODEL_FROM_DATABASE=Rage XL PCI + +pci:v00001002d00004752sv00000E11sd0000001E* + ID_MODEL_FROM_DATABASE=Rage XL PCI (Proliant Rage XL) + +pci:v00001002d00004752sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage XL PCI (Rage XL) + +pci:v00001002d00004752sv00001002sd00004752* + ID_MODEL_FROM_DATABASE=Rage XL PCI (Proliant Rage XL) + +pci:v00001002d00004752sv00001002sd00008008* + ID_MODEL_FROM_DATABASE=Rage XL PCI (Rage XL) + +pci:v00001002d00004752sv00001014sd00000240* + ID_MODEL_FROM_DATABASE=Rage XL PCI (eServer xSeries server mainboard) + +pci:v00001002d00004752sv00001028sd000000CE* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 1400) + +pci:v00001002d00004752sv00001028sd000000D1* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 2550) + +pci:v00001002d00004752sv00001028sd000000D9* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 2500) + +pci:v00001002d00004752sv00001028sd00000134* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 600SC) + +pci:v00001002d00004752sv00001028sd0000014A* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 1750) + +pci:v00001002d00004752sv00001028sd00000165* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PowerEdge 750) + +pci:v00001002d00004752sv0000103Csd000010E1* + ID_MODEL_FROM_DATABASE=Rage XL PCI (NetServer Rage XL) + +pci:v00001002d00004752sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=Rage XL PCI (ProLiant DL140 G2) + +pci:v00001002d00004752sv0000107Bsd00006400* + ID_MODEL_FROM_DATABASE=Rage XL PCI (6400 Server) + +pci:v00001002d00004752sv00001734sd0000007A* + ID_MODEL_FROM_DATABASE=Rage XL PCI (PRIMERGY RX/TX series onboard VGA) + +pci:v00001002d00004752sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=Rage XL PCI (Primergy Econel 200 D2020 mainboard) + +pci:v00001002d00004752sv00008086sd00003411* + ID_MODEL_FROM_DATABASE=Rage XL PCI (SDS2 Mainboard) + +pci:v00001002d00004752sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=Rage XL PCI (S875WP1-E mainboard) + +pci:v00001002d00004752sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=Rage XL PCI (S845WD1-E mainboard) + +pci:v00001002d00004753* + ID_MODEL_FROM_DATABASE=Rage XC + +pci:v00001002d00004753sv00001002sd00004753* + ID_MODEL_FROM_DATABASE=Rage XC + +pci:v00001002d00004754* + ID_MODEL_FROM_DATABASE=3D Rage II/II+ PCI [Mach64 GT] + +pci:v00001002d00004755* + ID_MODEL_FROM_DATABASE=Mach64 GTB [3D Rage II+ DVD] + +pci:v00001002d00004756* + ID_MODEL_FROM_DATABASE=3D Rage IIC PCI [Mach64 GT IIC] + +pci:v00001002d00004756sv00001002sd00004756* + ID_MODEL_FROM_DATABASE=3D Rage IIC PCI [Mach64 GT IIC] (Rage IIC) + +pci:v00001002d00004757* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP + +pci:v00001002d00004757sv00001002sd00004757* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage IIC AGP) + +pci:v00001002d00004757sv00001028sd00000089* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D IIC) + +pci:v00001002d00004757sv00001028sd0000008E* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (PowerEdge 1300 onboard video) + +pci:v00001002d00004757sv00001028sd00004082* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D IIC) + +pci:v00001002d00004757sv00001028sd00008082* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D IIC) + +pci:v00001002d00004757sv00001028sd0000C082* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D IIC) + +pci:v00001002d00004758* + ID_MODEL_FROM_DATABASE=210888GX [Mach64 GX PCI] + +pci:v00001002d00004759* + ID_MODEL_FROM_DATABASE=3D Rage IIC PCI + +pci:v00001002d0000475A* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP + +pci:v00001002d0000475Asv00001002sd00000084* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D Pro AGP 2x XPERT 98) + +pci:v00001002d0000475Asv00001002sd00000087* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage 3D IIC) + +pci:v00001002d0000475Asv00001002sd0000475A* + ID_MODEL_FROM_DATABASE=3D Rage IIC AGP (Rage IIC AGP) + +pci:v00001002d00004966* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] + +pci:v00001002d00004966sv000010F1sd00000002* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Tachyon G9000 PRO]) + +pci:v00001002d00004966sv0000148Csd00002039* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Radeon 9000 Pro "Evil Commando"]) + +pci:v00001002d00004966sv00001509sd00009A00* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Radeon 9000 "AT009"]) + +pci:v00001002d00004966sv00001681sd00000040* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [3D prophet 9000]) + +pci:v00001002d00004966sv0000174Bsd00007176* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (Radeon 9000 Pro) + +pci:v00001002d00004966sv0000174Bsd00007192* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Radeon 9000 "Atlantis"]) + +pci:v00001002d00004966sv000017AFsd00002005* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Excalibur Radeon 9000 Pro]) + +pci:v00001002d00004966sv000017AFsd00002006* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (RV250 If [Excalibur Radeon 9000]) + +pci:v00001002d0000496E* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000] (Secondary) + +pci:v00001002d00004A49* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 PRO/GTO AGP] + +pci:v00001002d00004A49sv0000174Bsd00002620* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 PRO/GTO AGP] (R420 [Radeon X800 GTO AGP]) + +pci:v00001002d00004A4A* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 GT AGP] + +pci:v00001002d00004A4B* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 AGP Series] + +pci:v00001002d00004A4D* + ID_MODEL_FROM_DATABASE=R420 GL [FireGL X3-256] + +pci:v00001002d00004A4E* + ID_MODEL_FROM_DATABASE=RV420/M18 [Mobility Radeon 9800] + +pci:v00001002d00004A4F* + ID_MODEL_FROM_DATABASE=R420 [Radeon X850 AGP] + +pci:v00001002d00004A50* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 XT Platinum Edition AGP] + +pci:v00001002d00004A54* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 VE AGP] + +pci:v00001002d00004A54sv00001002sd00004422* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 VE AGP] (All-In-Wonder X800 VE AGP) + +pci:v00001002d00004A69* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 PRO/GTO] (Secondary) + +pci:v00001002d00004A6A* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800] (Secondary) + +pci:v00001002d00004A6B* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 XT AGP] (Secondary) + +pci:v00001002d00004A70* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 XT Platinum Edition AGP] (Secondary) + +pci:v00001002d00004A74* + ID_MODEL_FROM_DATABASE=R420 [Radeon X800 VE] (Secondary) + +pci:v00001002d00004B49* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 XT AGP] + +pci:v00001002d00004B4B* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 PRO AGP] + +pci:v00001002d00004B4C* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 XT Platinum Edition AGP] + +pci:v00001002d00004B69* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 XT AGP] (Secondary) + +pci:v00001002d00004B6B* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 PRO AGP] (Secondary) + +pci:v00001002d00004B6C* + ID_MODEL_FROM_DATABASE=R481 [Radeon X850 XT Platinum Edition AGP] (Secondary) + +pci:v00001002d00004C42* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X + +pci:v00001002d00004C42sv00000E11sd0000B0E7* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage LT Pro (Compaq Presario 5240)) + +pci:v00001002d00004C42sv00000E11sd0000B0E8* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage 3D LT Pro) + +pci:v00001002d00004C42sv00000E11sd0000B10E* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (3D Rage LT Pro (Compaq Armada 1750)) + +pci:v00001002d00004C42sv00001002sd00000040* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage LT Pro AGP 2X) + +pci:v00001002d00004C42sv00001002sd00000044* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage LT Pro AGP 2X) + +pci:v00001002d00004C42sv00001002sd00004C42* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage LT Pro AGP 2X) + +pci:v00001002d00004C42sv00001002sd00008001* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage LT Pro AGP 2X) + +pci:v00001002d00004C42sv00001028sd00000085* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO AGP 2X (Rage 3D LT Pro) + +pci:v00001002d00004C46* + ID_MODEL_FROM_DATABASE=Rage Mobility 128 AGP 2X/Mobility M3 + +pci:v00001002d00004C46sv00001002sd00000155* + ID_MODEL_FROM_DATABASE=Rage Mobility 128 AGP 2X/Mobility M3 (IBM Thinkpad A22p) + +pci:v00001002d00004C46sv00001014sd00000155* + ID_MODEL_FROM_DATABASE=Rage Mobility 128 AGP 2X/Mobility M3 (IBM Thinkpad A22p) + +pci:v00001002d00004C46sv00001028sd000000B1* + ID_MODEL_FROM_DATABASE=Rage Mobility 128 AGP 2X/Mobility M3 (Latitude C600) + +pci:v00001002d00004C47* + ID_MODEL_FROM_DATABASE=3D Rage IIC PCI / Mobility Radeon 7500/7500C + +pci:v00001002d00004C49* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI + +pci:v00001002d00004C49sv00001002sd00000004* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI (Rage LT Pro) + +pci:v00001002d00004C49sv00001002sd00000040* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI (Rage LT Pro) + +pci:v00001002d00004C49sv00001002sd00000044* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI (Rage LT Pro) + +pci:v00001002d00004C49sv00001002sd00004C49* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI (Rage LT Pro) + +pci:v00001002d00004C4D* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series + +pci:v00001002d00004C4Dsv00000E11sd0000B111* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Armada M700) + +pci:v00001002d00004C4Dsv00000E11sd0000B160* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Armada E500) + +pci:v00001002d00004C4Dsv00001002sd00000084* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Xpert 98 AGP 2X (Mobility)) + +pci:v00001002d00004C4Dsv00001014sd00000154* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (ThinkPad A20m/A21m) + +pci:v00001002d00004C4Dsv00001028sd000000AA* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Latitude CPt) + +pci:v00001002d00004C4Dsv00001028sd000000BB* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Latitude CPx) + +pci:v00001002d00004C4Dsv00001179sd0000FF00* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (Satellite 1715XCDS laptop) + +pci:v00001002d00004C4Dsv000013BDsd00001019* + ID_MODEL_FROM_DATABASE=Rage Mobility AGP 2x Series (PC-AR10) + +pci:v00001002d00004C50* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI + +pci:v00001002d00004C50sv00001002sd00004C50* + ID_MODEL_FROM_DATABASE=3D Rage LT PRO PCI (Rage LT Pro) + +pci:v00001002d00004C52* + ID_MODEL_FROM_DATABASE=Rage Mobility-M1 PCI + +pci:v00001002d00004C52sv00001033sd00008112* + ID_MODEL_FROM_DATABASE=Rage Mobility-M1 PCI (Versa Note VXi) + +pci:v00001002d00004C54* + ID_MODEL_FROM_DATABASE=264LT [Mach64 LT] + +pci:v00001002d00004C57* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] + +pci:v00001002d00004C57sv00001014sd00000517* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (ThinkPad T30) + +pci:v00001002d00004C57sv00001014sd00000530* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (ThinkPad T4x Series) + +pci:v00001002d00004C57sv00001028sd000000E6* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (Radeon Mobility M7 LW (Dell Inspiron 8100)) + +pci:v00001002d00004C57sv00001028sd0000012A* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (Latitude C640) + +pci:v00001002d00004C57sv00001043sd00001622* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (Mobility Radeon M7 (L3C/S)) + +pci:v00001002d00004C57sv0000144Dsd0000C006* + ID_MODEL_FROM_DATABASE=RV200/M7 [Mobility Radeon 7500] (Radeon Mobility M7 LW in vpr Matrix 170B4) + +pci:v00001002d00004C58* + ID_MODEL_FROM_DATABASE=RV200/M7 GL [Mobility FireGL 7800] + +pci:v00001002d00004C59* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] + +pci:v00001002d00004C59sv00000E11sd0000B111* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (Evo N600c) + +pci:v00001002d00004C59sv00001014sd00000235* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (ThinkPad A30/A30p (2652/2653)) + +pci:v00001002d00004C59sv00001014sd00000239* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (ThinkPad X22/X23/X24) + +pci:v00001002d00004C59sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (XE4500 Notebook) + +pci:v00001002d00004C59sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00001002d00004C59sv0000104Dsd00008140* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (PCG-Z1SP laptop) + +pci:v00001002d00004C59sv00001509sd00001930* + ID_MODEL_FROM_DATABASE=RV100/M6 [Rage/Radeon Mobility Series] (Medion MD9703) + +pci:v00001002d00004C66* + ID_MODEL_FROM_DATABASE=RV250/M9 GL [Mobility FireGL 9000/Radeon 9000] + +pci:v00001002d00004C66sv00001014sd0000054D* + ID_MODEL_FROM_DATABASE=RV250/M9 GL [Mobility FireGL 9000/Radeon 9000] (ThinkPad T41) + +pci:v00001002d00004C6E* + ID_MODEL_FROM_DATABASE=RV250/M9 [Mobility Radeon 9000] (Secondary) + +pci:v00001002d00004D46* + ID_MODEL_FROM_DATABASE=Rage Mobility 128 AGP 4X/Mobility M4 + +pci:v00001002d00004D52* + ID_MODEL_FROM_DATABASE=Theater 550 PRO PCI [ATI TV Wonder 550] + +pci:v00001002d00004D53* + ID_MODEL_FROM_DATABASE=Theater 550 PRO PCIe + +pci:v00001002d00004E44* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700/9700 PRO] + +pci:v00001002d00004E44sv00001002sd0000515E* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700/9700 PRO] (Radeon ES1000) + +pci:v00001002d00004E44sv00001002sd00005965* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700/9700 PRO] (Radeon ES1000) + +pci:v00001002d00004E45* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO/9700] + +pci:v00001002d00004E45sv00001002sd00000002* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO/9700] (Radeon R300 NE [Radeon 9500 Pro]) + +pci:v00001002d00004E45sv00001681sd00000002* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO/9700] (Hercules 3D Prophet 9500 PRO [Radeon 9500 Pro]) + +pci:v00001002d00004E46* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9600 TX] + +pci:v00001002d00004E47* + ID_MODEL_FROM_DATABASE=R300 GL [FireGL X1] + +pci:v00001002d00004E48* + ID_MODEL_FROM_DATABASE=R350 [Radeon 9800 Series] + +pci:v00001002d00004E49* + ID_MODEL_FROM_DATABASE=R350 [Radeon 9800] + +pci:v00001002d00004E4A* + ID_MODEL_FROM_DATABASE=R360 [Radeon 9800 XXL/XT] + +pci:v00001002d00004E4Asv00001002sd00004E4A* + ID_MODEL_FROM_DATABASE=R360 [Radeon 9800 XXL/XT] (R360 [Radeon 9800 XT]) + +pci:v00001002d00004E4B* + ID_MODEL_FROM_DATABASE=R350 GL [FireGL X2 AGP Pro] + +pci:v00001002d00004E50* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] + +pci:v00001002d00004E50sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (TravelMate 290) + +pci:v00001002d00004E50sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (Extensa 3000 series laptop: ATI RV360/M11 [Mobility Radeon 9700]) + +pci:v00001002d00004E50sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (NC8000 laptop) + +pci:v00001002d00004E50sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (NC6000 laptop) + +pci:v00001002d00004E50sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (P35 notebook) + +pci:v00001002d00004E50sv00001462sd00000311* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (MSI M510A) + +pci:v00001002d00004E50sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=RV350/M10 / RV360/M11 [Mobility Radeon 9600 (PRO) / 9700] (Amilo M1420W) + +pci:v00001002d00004E51* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9550/9600/X1050 Series] + +pci:v00001002d00004E52* + ID_MODEL_FROM_DATABASE=RV350/M10 [Mobility Radeon 9500/9700 SE] + +pci:v00001002d00004E52sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=RV350/M10 [Mobility Radeon 9500/9700 SE] (P35 notebook) + +pci:v00001002d00004E54* + ID_MODEL_FROM_DATABASE=RV350/M10 GL [Mobility FireGL T2] + +pci:v00001002d00004E56* + ID_MODEL_FROM_DATABASE=RV360/M12 [Mobility Radeon 9550] + +pci:v00001002d00004E64* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9700 PRO] (Secondary) + +pci:v00001002d00004E65* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO] (Secondary) + +pci:v00001002d00004E65sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO] (Secondary) (Radeon R300 NE [Radeon 9500 Pro]) + +pci:v00001002d00004E65sv00001681sd00000003* + ID_MODEL_FROM_DATABASE=R300 [Radeon 9500 PRO] (Secondary) (Hercules 3D Prophet 9500 PRO [Radeon 9500 Pro] (Secondary)) + +pci:v00001002d00004E66* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9600] (Secondary) + +pci:v00001002d00004E67* + ID_MODEL_FROM_DATABASE=R300 GL [FireGL X1] (Secondary) + +pci:v00001002d00004E68* + ID_MODEL_FROM_DATABASE=R350 [Radeon 9800 PRO] (Secondary) + +pci:v00001002d00004E69* + ID_MODEL_FROM_DATABASE=R350 [Radeon 9800] (Secondary) + +pci:v00001002d00004E6A* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9800 XT] (Secondary) + +pci:v00001002d00004E6Asv00001002sd00004E6A* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9800 XT] (Secondary) (R360 [Radeon 9800 XT] (Secondary)) + +pci:v00001002d00004E6Asv00001002sd00004E71* + ID_MODEL_FROM_DATABASE=RV350 [Radeon 9800 XT] (Secondary) (M10 NQ [Radeon Mobility 9600]) + +pci:v00001002d00004E71* + ID_MODEL_FROM_DATABASE=RV350/M10 [Mobility Radeon 9600] (Secondary) + +pci:v00001002d00004F72* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] + +pci:v00001002d00004F73* + ID_MODEL_FROM_DATABASE=RV250 [Radeon 9000 Series] (Secondary) + +pci:v00001002d00005044* + ID_MODEL_FROM_DATABASE=All-In-Wonder 128 PCI + +pci:v00001002d00005044sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=All-In-Wonder 128 PCI (Rage 128 AIW) + +pci:v00001002d00005044sv00001002sd00000029* + ID_MODEL_FROM_DATABASE=All-In-Wonder 128 PCI (Rage 128 AIW) + +pci:v00001002d00005046* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS + +pci:v00001002d00005046sv00001002sd00000004* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury Pro) + +pci:v00001002d00005046sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury Pro/Xpert 2000 Pro) + +pci:v00001002d00005046sv00001002sd00000014* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury Pro) + +pci:v00001002d00005046sv00001002sd00000018* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury Pro/Xpert 2000 Pro) + +pci:v00001002d00005046sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage 128 Pro AIW AGP) + +pci:v00001002d00005046sv00001002sd0000002A* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage 128 Pro AIW AGP) + +pci:v00001002d00005046sv00001002sd00000048* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury Pro) + +pci:v00001002d00005046sv00001002sd00002000* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury MAXX AGP 4x (TMDS) (VGA device)) + +pci:v00001002d00005046sv00001002sd00002001* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4x TMDS (Rage Fury MAXX AGP 4x (TMDS) (Extra device?!)) + +pci:v00001002d00005050* + ID_MODEL_FROM_DATABASE=Rage128 [Xpert 128 PCI] + +pci:v00001002d00005050sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage128 [Xpert 128 PCI] (Xpert 128) + +pci:v00001002d00005052* + ID_MODEL_FROM_DATABASE=Rage 128 PRO AGP 4X TMDS + +pci:v00001002d00005144* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] + +pci:v00001002d00005144sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon VE) + +pci:v00001002d00005144sv00001002sd00000009* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd0000000A* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd0000001A* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd00000029* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon AIW) + +pci:v00001002d00005144sv00001002sd00000038* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd00000039* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd0000008A* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd000000BA* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd00000139* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd0000028A* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005144sv00001002sd000002AA* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon AIW) + +pci:v00001002d00005144sv00001002sd0000053A* + ID_MODEL_FROM_DATABASE=R100 [Radeon 7200 / All-In-Wonder Radeon] (Radeon 7000/Radeon) + +pci:v00001002d00005148* + ID_MODEL_FROM_DATABASE=R200 GL [FireGL 8800] + +pci:v00001002d00005148sv00001002sd0000010A* + ID_MODEL_FROM_DATABASE=R200 GL [FireGL 8800] (FireGL 8800 64Mb) + +pci:v00001002d00005148sv00001002sd00000152* + ID_MODEL_FROM_DATABASE=R200 GL [FireGL 8800] (FireGL 8800 128Mb) + +pci:v00001002d00005148sv00001002sd00000162* + ID_MODEL_FROM_DATABASE=R200 GL [FireGL 8800] (FireGL 8700 32Mb) + +pci:v00001002d00005148sv00001002sd00000172* + ID_MODEL_FROM_DATABASE=R200 GL [FireGL 8800] (FireGL 8700 64Mb) + +pci:v00001002d0000514C* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] + +pci:v00001002d0000514Csv00001002sd0000003A* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (Radeon R200 QL [Radeon 8500 LE]) + +pci:v00001002d0000514Csv00001002sd0000013A* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (Radeon 8500) + +pci:v00001002d0000514Csv0000148Csd00002026* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (R200 QL [Radeon 8500 Evil Master II Multi Display Edition]) + +pci:v00001002d0000514Csv00001681sd00000010* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (Radeon 8500 [3D Prophet 8500 128Mb]) + +pci:v00001002d0000514Csv0000174Bsd00007149* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (Radeon 8500 LE) + +pci:v00001002d0000514Csv00001787sd00000F08* + ID_MODEL_FROM_DATABASE=R200 [Radeon 8500/8500 LE] (Radeon R200 QL [PowerMagic Radeon 8500]) + +pci:v00001002d0000514D* + ID_MODEL_FROM_DATABASE=R200 [Radeon 9100] + +pci:v00001002d00005157* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] + +pci:v00001002d00005157sv00001002sd0000013A* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (Radeon 7500) + +pci:v00001002d00005157sv00001002sd00000F2B* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (ALL-IN-WONDER VE PCI) + +pci:v00001002d00005157sv00001002sd0000103A* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (Dell Optiplex GX260) + +pci:v00001002d00005157sv00001458sd00004000* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [RADEON 7500 PRO MAYA AR]) + +pci:v00001002d00005157sv0000148Csd00002024* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [Radeon 7500LE Dual Display]) + +pci:v00001002d00005157sv0000148Csd00002025* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [Radeon 7500 Evil Master Multi Display Edition]) + +pci:v00001002d00005157sv0000148Csd00002036* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [Radeon 7500 PCI Dual Display]) + +pci:v00001002d00005157sv0000174Bsd00007146* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [Radeon 7500 LE]) + +pci:v00001002d00005157sv0000174Bsd00007147* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (Radeon 7500 LE) + +pci:v00001002d00005157sv0000174Bsd00007161* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (Radeon RV200 QW [Radeon 7500 LE]) + +pci:v00001002d00005157sv000017AFsd00000202* + ID_MODEL_FROM_DATABASE=RV200 [Radeon 7500/7500 LE] (RV200 QW [Excalibur Radeon 7500LE]) + +pci:v00001002d00005159* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] + +pci:v00001002d00005159sv00001002sd0000000A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000/Radeon VE) + +pci:v00001002d00005159sv00001002sd0000000B* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000) + +pci:v00001002d00005159sv00001002sd00000038* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000/Radeon VE) + +pci:v00001002d00005159sv00001002sd0000003A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000/Radeon VE) + +pci:v00001002d00005159sv00001002sd000000BA* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000/Radeon VE) + +pci:v00001002d00005159sv00001002sd0000013A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000/Radeon VE) + +pci:v00001002d00005159sv00001002sd00000908* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (XVR-100 (supplied by Sun)) + +pci:v00001002d00005159sv00001014sd0000029A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Remote Supervisor Adapter II (RSA2)) + +pci:v00001002d00005159sv00001014sd000002C8* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (eServer xSeries server mainboard) + +pci:v00001002d00005159sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (PowerEdge 1850 Embedded Radeon 7000/VE) + +pci:v00001002d00005159sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (PowerEdge 2850 Embedded Radeon 7000-M) + +pci:v00001002d00005159sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (PowerEdge 6850 Embedded Radeon 7000/VE) + +pci:v00001002d00005159sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (PowerEdge SC1425) + +pci:v00001002d00005159sv0000103Csd00001292* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000) + +pci:v00001002d00005159sv00001043sd0000C00A* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (A7000/T/64M) + +pci:v00001002d00005159sv00001458sd00004002* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV100 QY [RADEON 7000 PRO MAYA AV Series]) + +pci:v00001002d00005159sv0000148Csd00002003* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV100 QY [Radeon 7000 Multi-Display Edition]) + +pci:v00001002d00005159sv0000148Csd00002023* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV100 QY [Radeon 7000 Evil Master Multi-Display]) + +pci:v00001002d00005159sv0000148Csd00002081* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV6DE) + +pci:v00001002d00005159sv0000174Bsd00000280* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon RV100 QY [Radeon 7000/VE]) + +pci:v00001002d00005159sv0000174Bsd00007112* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon VE 7000) + +pci:v00001002d00005159sv0000174Bsd00007C28* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon VE 7000 DDR) + +pci:v00001002d00005159sv00001787sd00000202* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (RV100 QY [Excalibur Radeon 7000]) + +pci:v00001002d00005159sv000017EEsd00001001* + ID_MODEL_FROM_DATABASE=RV100 [Radeon 7000 / Radeon VE] (Radeon 7000 64MB DDR + DVI) + +pci:v00001002d0000515E* + ID_MODEL_FROM_DATABASE=ES1000 + +pci:v00001002d0000515Esv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge 1955 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge SC440) + +pci:v00001002d0000515Esv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge 860) + +pci:v00001002d0000515Esv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge R900 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd00000205* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge 2970 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd0000020B* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge T605 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge R300 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd00000210* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge T300 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd00000221* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge R805 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd00000223* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge R905 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd00000225* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge T105 Embedded ATI ES1000) + +pci:v00001002d0000515Esv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=ES1000 (PowerEdge R200 Embedded ATI ES1000) + +pci:v00001002d0000515Esv0000103Csd00001304* + ID_MODEL_FROM_DATABASE=ES1000 (Integrity iLO2 Advanced KVM VGA [AD307A]) + +pci:v00001002d0000515Esv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=ES1000 (X7DVL-E-O motherboard) + +pci:v00001002d0000515Esv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=ES1000 (X7DBN Motherboard) + +pci:v00001002d0000515Esv00008086sd00003476* + ID_MODEL_FROM_DATABASE=ES1000 (S5000PSLSATA Server Board) + +pci:v00001002d00005245* + ID_MODEL_FROM_DATABASE=Rage 128 GL PCI + +pci:v00001002d00005245sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage 128 GL PCI (Xpert 128) + +pci:v00001002d00005245sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=Rage 128 GL PCI (Rage 128 AIW) + +pci:v00001002d00005245sv00001002sd00000029* + ID_MODEL_FROM_DATABASE=Rage 128 GL PCI (Rage 128 AIW) + +pci:v00001002d00005245sv00001002sd00000068* + ID_MODEL_FROM_DATABASE=Rage 128 GL PCI (Rage 128 AIW) + +pci:v00001002d00005246* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x + +pci:v00001002d00005246sv00001002sd00000004* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Magnum/Xpert 128/Xpert 99) + +pci:v00001002d00005246sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Magnum/Xpert128/X99/Xpert2000) + +pci:v00001002d00005246sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Rage 128 AIW AGP) + +pci:v00001002d00005246sv00001002sd00000044* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Rage Fury/Xpert 128/Xpert 2000) + +pci:v00001002d00005246sv00001002sd00000068* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Rage 128 AIW AGP) + +pci:v00001002d00005246sv00001002sd00000448* + ID_MODEL_FROM_DATABASE=Rage Fury/Xpert 128/Xpert 2000 AGP 2x (Rage Fury) + +pci:v00001002d0000524B* + ID_MODEL_FROM_DATABASE=Rage 128 VR PCI + +pci:v00001002d0000524C* + ID_MODEL_FROM_DATABASE=Rage 128 VR AGP + +pci:v00001002d0000524Csv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage 128 VR AGP (Xpert 99/Xpert 2000) + +pci:v00001002d0000524Csv00001002sd00000088* + ID_MODEL_FROM_DATABASE=Rage 128 VR AGP (Xpert 99) + +pci:v00001002d00005346* + ID_MODEL_FROM_DATABASE=Rage 128 SF/4x AGP 2x + +pci:v00001002d00005346sv00001002sd00000048* + ID_MODEL_FROM_DATABASE=Rage 128 SF/4x AGP 2x (RAGE 128 16MB VGA TVOUT AMC PAL) + +pci:v00001002d0000534D* + ID_MODEL_FROM_DATABASE=Rage 128 4X AGP 4x + +pci:v00001002d0000534Dsv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage 128 4X AGP 4x (Xpert 99/Xpert 2000) + +pci:v00001002d0000534Dsv00001002sd00000018* + ID_MODEL_FROM_DATABASE=Rage 128 4X AGP 4x (Xpert 2000) + +pci:v00001002d00005354* + ID_MODEL_FROM_DATABASE=Mach 64 VT + +pci:v00001002d00005354sv00001002sd00005654* + ID_MODEL_FROM_DATABASE=Mach 64 VT (Mach 64 reference) + +pci:v00001002d00005446* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x + +pci:v00001002d00005446sv00001002sd00000004* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage Fury Pro) + +pci:v00001002d00005446sv00001002sd00000008* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage Fury Pro/Xpert 2000 Pro) + +pci:v00001002d00005446sv00001002sd00000018* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage Fury Pro/Xpert 2000 Pro) + +pci:v00001002d00005446sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage 128 AIW Pro AGP) + +pci:v00001002d00005446sv00001002sd00000029* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage 128 AIW) + +pci:v00001002d00005446sv00001002sd0000002A* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage 128 AIW Pro AGP) + +pci:v00001002d00005446sv00001002sd0000002B* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Rage 128 AIW) + +pci:v00001002d00005446sv00001002sd00000048* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra AGP 4x (Xpert 2000 Pro) + +pci:v00001002d00005452* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra4XL VR-R AGP + +pci:v00001002d00005452sv00001002sd0000001C* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra4XL VR-R AGP (Rage 128 Pro 4XL) + +pci:v00001002d00005452sv0000103Csd00001279* + ID_MODEL_FROM_DATABASE=Rage 128 PRO Ultra4XL VR-R AGP (Rage 128 Pro 4XL) + +pci:v00001002d00005460* + ID_MODEL_FROM_DATABASE=RV370/M22 [Mobility Radeon X300] + +pci:v00001002d00005460sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=RV370/M22 [Mobility Radeon X300] (CR11/VR11 Single Board Computer) + +pci:v00001002d00005461* + ID_MODEL_FROM_DATABASE=RV370/M22 [Mobility Radeon X300] + +pci:v00001002d00005462* + ID_MODEL_FROM_DATABASE=RV380/M24C [Mobility Radeon X600 SE] + +pci:v00001002d00005464* + ID_MODEL_FROM_DATABASE=RV370/M22 GL [Mobility FireGL V3100] + +pci:v00001002d00005549* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 GTO] + +pci:v00001002d0000554A* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 XT Platinum Edition] + +pci:v00001002d0000554B* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 GT/SE] + +pci:v00001002d0000554Bsv00001002sd00000302* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 GT/SE] (Radeon X800 SE) + +pci:v00001002d0000554D* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800 XL] + +pci:v00001002d0000554Dsv00001002sd00000322* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800 XL] (All-In-Wonder X800 XL) + +pci:v00001002d0000554Dsv00001458sd00002124* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800 XL] (GV-R80L256V-B (AGP)) + +pci:v00001002d0000554E* + ID_MODEL_FROM_DATABASE=R430 [All-In-Wonder X800 GT] + +pci:v00001002d0000554F* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800] + +pci:v00001002d00005550* + ID_MODEL_FROM_DATABASE=R423 GL [FireGL V7100] + +pci:v00001002d00005551* + ID_MODEL_FROM_DATABASE=R423 GL [FireGL V5100] + +pci:v00001002d00005569* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 PRO] (Secondary) + +pci:v00001002d0000556B* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 GT] (Secondary) + +pci:v00001002d0000556D* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800 XL] (Secondary) + +pci:v00001002d0000556Dsv00001458sd00002125* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800 XL] (Secondary) (GV-R80L256V-B (AGP)) + +pci:v00001002d0000556F* + ID_MODEL_FROM_DATABASE=R430 [Radeon X800] (Secondary) + +pci:v00001002d00005571* + ID_MODEL_FROM_DATABASE=R423 GL [FireGL V5100] (Secondary) + +pci:v00001002d0000564B* + ID_MODEL_FROM_DATABASE=RV410/M26 GL [Mobility FireGL V5000] + +pci:v00001002d0000564F* + ID_MODEL_FROM_DATABASE=RV410/M26 [Mobility Radeon X700 XL] + +pci:v00001002d00005652* + ID_MODEL_FROM_DATABASE=RV410/M26 [Mobility Radeon X700] + +pci:v00001002d00005653* + ID_MODEL_FROM_DATABASE=RV410/M26 [Mobility Radeon X700] + +pci:v00001002d00005653sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=RV410/M26 [Mobility Radeon X700] (Aspire 5024WLMi) + +pci:v00001002d00005653sv0000103Csd00000940* + ID_MODEL_FROM_DATABASE=RV410/M26 [Mobility Radeon X700] (Compaq NW8240 Mobile Workstation) + +pci:v00001002d00005654* + ID_MODEL_FROM_DATABASE=264VT [Mach64 VT] + +pci:v00001002d00005654sv00001002sd00005654* + ID_MODEL_FROM_DATABASE=264VT [Mach64 VT] (Mach64VT Reference) + +pci:v00001002d00005655* + ID_MODEL_FROM_DATABASE=264VT3 [Mach64 VT3] + +pci:v00001002d00005656* + ID_MODEL_FROM_DATABASE=264VT4 [Mach64 VT4] + +pci:v00001002d00005657* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X550 XTX / X700] + +pci:v00001002d00005830* + ID_MODEL_FROM_DATABASE=RS300 Host Bridge + +pci:v00001002d00005831* + ID_MODEL_FROM_DATABASE=RS300 Host Bridge + +pci:v00001002d00005832* + ID_MODEL_FROM_DATABASE=RS300 Host Bridge + +pci:v00001002d00005833* + ID_MODEL_FROM_DATABASE=RS300 Host Bridge + +pci:v00001002d00005834* + ID_MODEL_FROM_DATABASE=RS300 [Radeon 9100 IGP] + +pci:v00001002d00005835* + ID_MODEL_FROM_DATABASE=RS300M [Mobility Radeon 9100 IGP] + +pci:v00001002d00005838* + ID_MODEL_FROM_DATABASE=RS300 AGP Bridge + +pci:v00001002d00005854* + ID_MODEL_FROM_DATABASE=RS480 [Radeon Xpress 200 Series] (Secondary) + +pci:v00001002d00005874* + ID_MODEL_FROM_DATABASE=RS480 [Radeon Xpress 1150] (Secondary) + +pci:v00001002d00005940* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 PRO] (Secondary) + +pci:v00001002d00005940sv000017AFsd00002021* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 PRO] (Secondary) (Excalibur Radeon 9250 (Secondary)) + +pci:v00001002d00005941* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Secondary) + +pci:v00001002d00005941sv00001458sd00004019* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Secondary) (Radeon 9200) + +pci:v00001002d00005941sv0000174Bsd00007C12* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Secondary) (Radeon 9200) + +pci:v00001002d00005941sv000017AFsd0000200D* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Secondary) (Excalibur Radeon 9200) + +pci:v00001002d00005941sv000018BCsd00000050* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Secondary) (GC-R9200-C3 (Secondary)) + +pci:v00001002d00005944* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE PCI] + +pci:v00001002d00005950* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge + +pci:v00001002d00005950sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (Aspire 5024WLMMi) + +pci:v00001002d00005950sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (DC5750 Microtower) + +pci:v00001002d00005950sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (Pavilion t3030.de Desktop PC) + +pci:v00001002d00005950sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (MX6125) + +pci:v00001002d00005950sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (MS-1013 Notebook) + +pci:v00001002d00005950sv00001462sd00007217* + ID_MODEL_FROM_DATABASE=RS480/RS482/RS485 Host Bridge (Aspire L250) + +pci:v00001002d00005951* + ID_MODEL_FROM_DATABASE=RX480/RX482 Host Bridge + +pci:v00001002d00005952* + ID_MODEL_FROM_DATABASE=RD580 Host Bridge + +pci:v00001002d00005954* + ID_MODEL_FROM_DATABASE=RS480 [Radeon Xpress 200 Series] + +pci:v00001002d00005954sv00001002sd00005954* + ID_MODEL_FROM_DATABASE=RS480 [Radeon Xpress 200 Series] (RV370 [Radeon Xpress 200G Series]) + +pci:v00001002d00005955* + ID_MODEL_FROM_DATABASE=RS480M [Mobility Radeon Xpress 200] + +pci:v00001002d00005955sv00001002sd00005955* + ID_MODEL_FROM_DATABASE=RS480M [Mobility Radeon Xpress 200] (RS480 0x5955 [Radeon XPRESS 200M 5955 (PCIE)]) + +pci:v00001002d00005955sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=RS480M [Mobility Radeon Xpress 200] (MX6125) + +pci:v00001002d00005955sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=RS480M [Mobility Radeon Xpress 200] (MS-1013 Notebook) + +pci:v00001002d00005956* + ID_MODEL_FROM_DATABASE=RD790 Host Bridge + +pci:v00001002d00005957* + ID_MODEL_FROM_DATABASE=RX780/RX790 Host Bridge + +pci:v00001002d00005957sv00001849sd00005957* + ID_MODEL_FROM_DATABASE=RX780/RX790 Host Bridge (A770CrossFire Motherboard) + +pci:v00001002d00005958* + ID_MODEL_FROM_DATABASE=RD780 Host Bridge + +pci:v00001002d00005960* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 PRO] + +pci:v00001002d00005960sv000017AFsd00002020* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 PRO] (Excalibur Radeon 9250) + +pci:v00001002d00005961* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] + +pci:v00001002d00005961sv00001002sd00002F72* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (All-in-Wonder 9200 Series) + +pci:v00001002d00005961sv00001019sd00004C30* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Radeon 9200 VIVO) + +pci:v00001002d00005961sv000012ABsd00005961* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (YUAN SMARTVGA Radeon 9200) + +pci:v00001002d00005961sv00001458sd00004018* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Radeon 9200) + +pci:v00001002d00005961sv0000174Bsd00007C13* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Radeon 9200) + +pci:v00001002d00005961sv000017AFsd0000200C* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Excalibur Radeon 9200) + +pci:v00001002d00005961sv000018BCsd00000050* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Radeon 9200 Game Buster) + +pci:v00001002d00005961sv000018BCsd00000051* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (GC-R9200-C3) + +pci:v00001002d00005961sv000018BCsd00000053* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] (Radeon 9200 Game Buster VIVO) + +pci:v00001002d00005962* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200] + +pci:v00001002d00005964* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] + +pci:v00001002d00005964sv00001002sd00005964* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE, 64-bit 128MB DDR, 200/166MHz) + +pci:v00001002d00005964sv00001043sd0000C006* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE / TD / 128M) + +pci:v00001002d00005964sv00001458sd00004018* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE) + +pci:v00001002d00005964sv00001458sd00004032* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE 128MB) + +pci:v00001002d00005964sv0000147Bsd00006191* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (R9200SE-DT) + +pci:v00001002d00005964sv0000148Csd00002073* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (CN-AG92E) + +pci:v00001002d00005964sv0000174Bsd00007C13* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE) + +pci:v00001002d00005964sv00001787sd00005964* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Excalibur 9200SE VIVO 128M) + +pci:v00001002d00005964sv000017AFsd00002012* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Radeon 9200 SE Excalibur) + +pci:v00001002d00005964sv000018BCsd00000170* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Sapphire Radeon 9200 SE 128MB Game Buster) + +pci:v00001002d00005964sv000018BCsd00000173* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (GC-R9200L(SE)-C3H [Radeon 9200 Game Buster]) + +pci:v00001002d00005965* + ID_MODEL_FROM_DATABASE=RV280 GL [FireMV 2200 PCI] + +pci:v00001002d00005974* + ID_MODEL_FROM_DATABASE=RS482/RS485 [Radeon Xpress 1100/1150] + +pci:v00001002d00005974sv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=RS482/RS485 [Radeon Xpress 1100/1150] (DC5750 Microtower) + +pci:v00001002d00005974sv00001462sd00007141* + ID_MODEL_FROM_DATABASE=RS482/RS485 [Radeon Xpress 1100/1150] (Aspire L250) + +pci:v00001002d00005975* + ID_MODEL_FROM_DATABASE=RS482M [Mobility Radeon Xpress 200] + +pci:v00001002d00005978* + ID_MODEL_FROM_DATABASE=RX780/RD790 PCI to PCI bridge (external gfx0 port A) + +pci:v00001002d00005978sv00001849sd00005957* + ID_MODEL_FROM_DATABASE=RX780/RD790 PCI to PCI bridge (external gfx0 port A) (A770CrossFire Motherboard) + +pci:v00001002d00005979* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (external gfx0 port B) + +pci:v00001002d0000597A* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port A) + +pci:v00001002d0000597B* + ID_MODEL_FROM_DATABASE=RX780/RD790 PCI to PCI bridge (PCI express gpp port B) + +pci:v00001002d0000597C* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port C) + +pci:v00001002d0000597D* + ID_MODEL_FROM_DATABASE=RX780/RD790 PCI to PCI bridge (PCI express gpp port D) + +pci:v00001002d0000597E* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port E) + +pci:v00001002d0000597Esv00001849sd00005957* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port E) (A770CrossFire Motherboard) + +pci:v00001002d0000597F* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port F) + +pci:v00001002d0000597Fsv00001849sd00005957* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (PCI express gpp port F) (A770CrossFire Motherboard) + +pci:v00001002d00005980* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (external gfx1 port A) + +pci:v00001002d00005981* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (external gfx1 port B) + +pci:v00001002d00005982* + ID_MODEL_FROM_DATABASE=RD790 PCI to PCI bridge (NB-SB link) + +pci:v00001002d00005A10* + ID_MODEL_FROM_DATABASE=RD890 Northbridge only dual slot (2x16) PCI-e GFX Hydra part + +pci:v00001002d00005A11* + ID_MODEL_FROM_DATABASE=RD890 Northbridge only single slot PCI-e GFX Hydra part + +pci:v00001002d00005A12* + ID_MODEL_FROM_DATABASE=RD890 Northbridge only dual slot (2x8) PCI-e GFX Hydra part + +pci:v00001002d00005A12sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=RD890 Northbridge only dual slot (2x8) PCI-e GFX Hydra part (H8DGU) + +pci:v00001002d00005A13* + ID_MODEL_FROM_DATABASE=RD890S/SR5650 Host Bridge + +pci:v00001002d00005A14* + ID_MODEL_FROM_DATABASE=RD9x0/RX980 Host Bridge + +pci:v00001002d00005A15* + ID_MODEL_FROM_DATABASE=RD890 PCI to PCI bridge (PCI express gpp port A) + +pci:v00001002d00005A16* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GFX port 0) + +pci:v00001002d00005A17* + ID_MODEL_FROM_DATABASE=RD890/RD9x0 PCI to PCI bridge (PCI Express GFX port 1) + +pci:v00001002d00005A18* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 0) + +pci:v00001002d00005A18sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 0) (H8DGU) + +pci:v00001002d00005A19* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 1) + +pci:v00001002d00005A1A* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 2) + +pci:v00001002d00005A1B* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 3) + +pci:v00001002d00005A1C* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 4) + +pci:v00001002d00005A1D* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP Port 5) + +pci:v00001002d00005A1E* + ID_MODEL_FROM_DATABASE=RD890/RD9x0/RX980 PCI to PCI bridge (PCI Express GPP2 Port 0) + +pci:v00001002d00005A1F* + ID_MODEL_FROM_DATABASE=RD890/RD990 PCI to PCI bridge (PCI Express GFX2 port 0) + +pci:v00001002d00005A1Fsv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=RD890/RD990 PCI to PCI bridge (PCI Express GFX2 port 0) (H8DGU) + +pci:v00001002d00005A20* + ID_MODEL_FROM_DATABASE=RD890/RD990 PCI to PCI bridge (PCI Express GFX2 port 1) + +pci:v00001002d00005A23* + ID_MODEL_FROM_DATABASE=RD890S/RD990 I/O Memory Management Unit (IOMMU) + +pci:v00001002d00005A31* + ID_MODEL_FROM_DATABASE=RC410 Host Bridge + +pci:v00001002d00005A33* + ID_MODEL_FROM_DATABASE=RS400 Host Bridge + +pci:v00001002d00005A34* + ID_MODEL_FROM_DATABASE=RS4xx PCI Express Port [ext gfx] + +pci:v00001002d00005A36* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Express Port 1 + +pci:v00001002d00005A37* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Express Port 2 + +pci:v00001002d00005A38* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Express Port 3 + +pci:v00001002d00005A39* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Express Port 4 + +pci:v00001002d00005A3F* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Bridge [int gfx] + +pci:v00001002d00005A3Fsv00001462sd00007217* + ID_MODEL_FROM_DATABASE=RC4xx/RS4xx PCI Bridge [int gfx] (Aspire L250) + +pci:v00001002d00005A41* + ID_MODEL_FROM_DATABASE=RS400 [Radeon Xpress 200] + +pci:v00001002d00005A42* + ID_MODEL_FROM_DATABASE=RS400M [Radeon Xpress 200M] + +pci:v00001002d00005A61* + ID_MODEL_FROM_DATABASE=RC410 [Radeon Xpress 200/1100] + +pci:v00001002d00005A62* + ID_MODEL_FROM_DATABASE=RC410M [Mobility Radeon Xpress 200M] + +pci:v00001002d00005B60* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] + +pci:v00001002d00005B60sv00001043sd0000002A* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (Extreme AX300SE-X) + +pci:v00001002d00005B60sv00001043sd0000032E* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (Extreme AX300/TD) + +pci:v00001002d00005B60sv00001458sd00002102* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (GV-RX30S128D (X300SE)) + +pci:v00001002d00005B60sv00001462sd00000400* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (RX300SE-TD128E (MS-8940 REV:200)) + +pci:v00001002d00005B60sv00001462sd00000402* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (RX300SE-TD128E (MS-8940)) + +pci:v00001002d00005B60sv0000174Bsd00000500* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (Radeon X300 (PCIE)) + +pci:v00001002d00005B60sv0000196Dsd00001086* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300] (X300SE HM) + +pci:v00001002d00005B62* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X600/X600 SE] + +pci:v00001002d00005B63* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300/X550/X1050 Series] + +pci:v00001002d00005B64* + ID_MODEL_FROM_DATABASE=RV370 GL [FireGL V3100] + +pci:v00001002d00005B65* + ID_MODEL_FROM_DATABASE=RV370 GL [FireMV 2200] + +pci:v00001002d00005B66* + ID_MODEL_FROM_DATABASE=RV370X + +pci:v00001002d00005B70* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300 SE] + +pci:v00001002d00005B70sv00001462sd00000403* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300 SE] (Radeon X300 SE 128MB DDR) + +pci:v00001002d00005B70sv0000174Bsd00000501* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300 SE] (Radeon X300 SE) + +pci:v00001002d00005B70sv0000196Dsd00001087* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300 SE] (Radeon X300 SE HyperMemory) + +pci:v00001002d00005B72* + ID_MODEL_FROM_DATABASE=RV380 [Radeon X300/X550/X1050 Series] (Secondary) + +pci:v00001002d00005B73* + ID_MODEL_FROM_DATABASE=RV370 [Radeon X300/X550/X1050 Series] (Secondary) + +pci:v00001002d00005B74* + ID_MODEL_FROM_DATABASE=RV370 GL [FireGL V3100] (Secondary) + +pci:v00001002d00005B75* + ID_MODEL_FROM_DATABASE=RV370 GL [FireMV 2200] (Secondary) + +pci:v00001002d00005C61* + ID_MODEL_FROM_DATABASE=RV280/M9+ [Mobility Radeon 9200 AGP] + +pci:v00001002d00005C63* + ID_MODEL_FROM_DATABASE=RV280/M9+ [Mobility Radeon 9200 AGP] + +pci:v00001002d00005C63sv00001002sd00005C63* + ID_MODEL_FROM_DATABASE=RV280/M9+ [Mobility Radeon 9200 AGP] (Apple iBook G4 2004) + +pci:v00001002d00005C63sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=RV280/M9+ [Mobility Radeon 9200 AGP] (P30 notebook) + +pci:v00001002d00005D44* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) + +pci:v00001002d00005D44sv00001458sd00004019* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Radeon 9200 SE (Secondary)) + +pci:v00001002d00005D44sv00001458sd00004032* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Radeon 9200 SE 128MB) + +pci:v00001002d00005D44sv0000147Bsd00006190* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (R9200SE-DT (Secondary)) + +pci:v00001002d00005D44sv0000174Bsd00007C12* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Radeon 9200 SE (Secondary)) + +pci:v00001002d00005D44sv00001787sd00005965* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Excalibur 9200SE VIVO 128M (Secondary)) + +pci:v00001002d00005D44sv000017AFsd00002013* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Radeon 9200 SE Excalibur (Secondary)) + +pci:v00001002d00005D44sv000018BCsd00000171* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (Radeon 9200 SE 128MB Game Buster (Secondary)) + +pci:v00001002d00005D44sv000018BCsd00000172* + ID_MODEL_FROM_DATABASE=RV280 [Radeon 9200 SE] (Secondary) (GC-R9200L(SE)-C3H [Radeon 9200 Game Buster]) + +pci:v00001002d00005D45* + ID_MODEL_FROM_DATABASE=RV280 GL [FireMV 2200 PCI] (Secondary) + +pci:v00001002d00005D48* + ID_MODEL_FROM_DATABASE=R423/M28 [Mobility Radeon X800 XT] + +pci:v00001002d00005D49* + ID_MODEL_FROM_DATABASE=R423/M28 GL [Mobility FireGL V5100] + +pci:v00001002d00005D4A* + ID_MODEL_FROM_DATABASE=R423/M28 [Mobility Radeon X800] + +pci:v00001002d00005D4D* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT Platinum Edition] + +pci:v00001002d00005D4E* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 SE] + +pci:v00001002d00005D4F* + ID_MODEL_FROM_DATABASE=R480 [Radeon X800 GTO] + +pci:v00001002d00005D50* + ID_MODEL_FROM_DATABASE=R480 GL [FireGL V7200] + +pci:v00001002d00005D52* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT] + +pci:v00001002d00005D52sv00001002sd00000B12* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT] (PowerColor X850XT PCIe (Primary)) + +pci:v00001002d00005D57* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 XT] + +pci:v00001002d00005D6D* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT Platinum Edition] (Secondary) + +pci:v00001002d00005D6F* + ID_MODEL_FROM_DATABASE=R480 [Radeon X800 GTO] (Secondary) + +pci:v00001002d00005D72* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT] (Secondary) + +pci:v00001002d00005D72sv00001002sd00000B13* + ID_MODEL_FROM_DATABASE=R480 [Radeon X850 XT] (Secondary) (PowerColor X850XT PCIe (Secondary)) + +pci:v00001002d00005D77* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 XT] (Secondary) + +pci:v00001002d00005E48* + ID_MODEL_FROM_DATABASE=RV410 GL [FireGL V5000] + +pci:v00001002d00005E49* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700 Series] + +pci:v00001002d00005E4A* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700 XT] + +pci:v00001002d00005E4B* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700 PRO] + +pci:v00001002d00005E4C* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700 SE] + +pci:v00001002d00005E4D* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] + +pci:v00001002d00005E4Dsv0000148Csd00002116* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] (Bravo X700) + +pci:v00001002d00005E4F* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] + +pci:v00001002d00005E4Fsv00001569sd00001E4F* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] (Radeon X550 XT) + +pci:v00001002d00005E6B* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700 PRO] (Secondary) + +pci:v00001002d00005E6D* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] (Secondary) + +pci:v00001002d00005E6Dsv0000148Csd00002117* + ID_MODEL_FROM_DATABASE=RV410 [Radeon X700] (Secondary) (Bravo X700 (Secondary)) + +pci:v00001002d00005F57* + ID_MODEL_FROM_DATABASE=R423 [Radeon X800 XT] + +pci:v00001002d00006600* + ID_MODEL_FROM_DATABASE=Mars [Radeon HD 8670A/8670M/8750M] + +pci:v00001002d00006600sv0000103Csd00001952* + ID_MODEL_FROM_DATABASE=Mars [Radeon HD 8670A/8670M/8750M] (ProBook 455 G1) + +pci:v00001002d00006601* + ID_MODEL_FROM_DATABASE=Mars [Radeon HD 8730M] + +pci:v00001002d00006601sv0000103Csd00002100* + ID_MODEL_FROM_DATABASE=Mars [Radeon HD 8730M] (FirePro M4100) + +pci:v00001002d00006602* + ID_MODEL_FROM_DATABASE=Mars + +pci:v00001002d00006603* + ID_MODEL_FROM_DATABASE=Mars + +pci:v00001002d00006604* + ID_MODEL_FROM_DATABASE=Opal XT [Radeon R7 M265] + +pci:v00001002d00006604sv0000103Csd00008006* + ID_MODEL_FROM_DATABASE=Opal XT [Radeon R7 M265] (FirePro M4170) + +pci:v00001002d00006604sv000017AAsd00003643* + ID_MODEL_FROM_DATABASE=Opal XT [Radeon R7 M265] (Radeon R7 A360) + +pci:v00001002d00006605* + ID_MODEL_FROM_DATABASE=Opal PRO [Radeon R7 M260] + +pci:v00001002d00006606* + ID_MODEL_FROM_DATABASE=Mars XTX [Radeon HD 8790M] + +pci:v00001002d00006606sv00001028sd00000684* + ID_MODEL_FROM_DATABASE=Mars XTX [Radeon HD 8790M] (FirePro W4170M) + +pci:v00001002d00006607* + ID_MODEL_FROM_DATABASE=Mars LE [Radeon HD 8530M / R5 M240] + +pci:v00001002d00006608* + ID_MODEL_FROM_DATABASE=Oland GL [FirePro W2100] + +pci:v00001002d00006610* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] + +pci:v00001002d00006610sv00001019sd00000030* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon HD 8670) + +pci:v00001002d00006610sv00001028sd00002120* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 250) + +pci:v00001002d00006610sv00001028sd00002322* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 250) + +pci:v00001002d00006610sv00001462sd00002910* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon HD 8670) + +pci:v00001002d00006610sv00001462sd00002911* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon HD 8670) + +pci:v00001002d00006610sv0000148Csd00007350* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 350) + +pci:v00001002d00006610sv00001642sd00003C81* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon HD 8670) + +pci:v00001002d00006610sv00001642sd00003C91* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon HD 8670) + +pci:v00001002d00006610sv00001642sd00003F09* + ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 350) + +pci:v00001002d00006611* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] + +pci:v00001002d00006611sv00001028sd0000210B* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R5 240 OEM) + +pci:v00001002d00006611sv0000174Bsd00004248* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM) + +pci:v00001002d00006611sv0000174Bsd0000A240* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM) + +pci:v00001002d00006611sv0000174Bsd0000D340* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 340 OEM) + +pci:v00001002d00006611sv00001B0Asd000090D3* + ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM) + +pci:v00001002d00006613* + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] + +pci:v00001002d00006613sv0000148Csd00007340* + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] (Radeon R7 340) + +pci:v00001002d00006613sv00001682sd00007240* + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] (R7 240 2048 MB) + +pci:v00001002d00006620* + ID_MODEL_FROM_DATABASE=Mars + +pci:v00001002d00006621* + ID_MODEL_FROM_DATABASE=Mars PRO + +pci:v00001002d00006623* + ID_MODEL_FROM_DATABASE=Mars + +pci:v00001002d00006631* + ID_MODEL_FROM_DATABASE=Oland + +pci:v00001002d00006640* + ID_MODEL_FROM_DATABASE=Saturn XT [FirePro M6100] + +pci:v00001002d00006641* + ID_MODEL_FROM_DATABASE=Saturn PRO [Radeon HD 8930M] + +pci:v00001002d00006646* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon R9 M280X] + +pci:v00001002d00006647* + ID_MODEL_FROM_DATABASE=Bonaire PRO [Radeon R9 M270X] + +pci:v00001002d00006649* + ID_MODEL_FROM_DATABASE=Bonaire [FirePro W5100] + +pci:v00001002d00006649sv00001002sd00000B0C* + ID_MODEL_FROM_DATABASE=Bonaire [FirePro W5100] (FirePro W4300) + +pci:v00001002d00006649sv0000103Csd00000B0C* + ID_MODEL_FROM_DATABASE=Bonaire [FirePro W5100] (Bonaire [FirePro W4300]) + +pci:v00001002d00006649sv0000103Csd0000230C* + ID_MODEL_FROM_DATABASE=Bonaire [FirePro W5100] (FirePro W5100) + +pci:v00001002d00006650* + ID_MODEL_FROM_DATABASE=Bonaire + +pci:v00001002d00006651* + ID_MODEL_FROM_DATABASE=Bonaire + +pci:v00001002d00006658* + ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] + +pci:v00001002d00006658sv0000148Csd00000907* + ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] (Radeon R7 360) + +pci:v00001002d00006658sv00001682sd00000907* + ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] (Radeon R7 360) + +pci:v00001002d00006658sv00001682sd00007360* + ID_MODEL_FROM_DATABASE=Bonaire XTX [Radeon R7 260X/360] (Radeon R7 360) + +pci:v00001002d0000665C* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] + +pci:v00001002d0000665Csv00001043sd00000452* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 7790 DirectCU II OC) + +pci:v00001002d0000665Csv00001462sd00002930* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 7790 OC) + +pci:v00001002d0000665Csv00001462sd00002932* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 8770) + +pci:v00001002d0000665Csv00001462sd00002934* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R9 260 OEM) + +pci:v00001002d0000665Csv00001462sd00002938* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R9 360 OEM) + +pci:v00001002d0000665Csv0000148Csd00000907* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R7 360) + +pci:v00001002d0000665Csv0000148Csd00009260* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R9 260 OEM) + +pci:v00001002d0000665Csv0000148Csd00009360* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R9 360 OEM) + +pci:v00001002d0000665Csv00001682sd00000907* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon R7 360) + +pci:v00001002d0000665Csv00001682sd00003310* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 7790 Black Edition 2 GB) + +pci:v00001002d0000665Csv0000174Bsd0000E253* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 7790 Dual-X OC) + +pci:v00001002d0000665Csv00001787sd00002329* + ID_MODEL_FROM_DATABASE=Bonaire XT [Radeon HD 7790/8770 / R7 360 / R9 260/360 OEM] (Radeon HD 7790 TurboDuo) + +pci:v00001002d0000665D* + ID_MODEL_FROM_DATABASE=Bonaire [Radeon R7 200 Series] + +pci:v00001002d0000665F* + ID_MODEL_FROM_DATABASE=Tobago PRO [Radeon R7 360 / R9 360 OEM] + +pci:v00001002d0000665Fsv00001028sd00000B04* + ID_MODEL_FROM_DATABASE=Tobago PRO [Radeon R7 360 / R9 360 OEM] (Radeon R9 360 OEM) + +pci:v00001002d0000665Fsv00001462sd00002938* + ID_MODEL_FROM_DATABASE=Tobago PRO [Radeon R7 360 / R9 360 OEM] (Radeon R9 360 OEM) + +pci:v00001002d0000665Fsv00001462sd00003271* + ID_MODEL_FROM_DATABASE=Tobago PRO [Radeon R7 360 / R9 360 OEM] (Radeon R9 360 OEM) + +pci:v00001002d0000665Fsv00001682sd00007360* + ID_MODEL_FROM_DATABASE=Tobago PRO [Radeon R7 360 / R9 360 OEM] (Radeon R7 360) + +pci:v00001002d00006660* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] + +pci:v00001002d00006660sv00001028sd000005EA* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon HD 8670M) + +pci:v00001002d00006660sv00001028sd000006BF* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M335) + +pci:v00001002d00006660sv0000103Csd00001970* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon HD 8670M) + +pci:v00001002d00006660sv0000103Csd000080BE* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M330) + +pci:v00001002d00006660sv0000103Csd00008136* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M330) + +pci:v00001002d00006660sv000017AAsd00003804* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M330) + +pci:v00001002d00006660sv000017AAsd00003809* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M330) + +pci:v00001002d00006660sv000017AAsd0000390C* + ID_MODEL_FROM_DATABASE=Sun XT [Radeon HD 8670A/8670M/8690M / R5 M330] (Radeon R5 M330) + +pci:v00001002d00006663* + ID_MODEL_FROM_DATABASE=Sun PRO [Radeon HD 8570A/8570M] + +pci:v00001002d00006663sv00001025sd00000846* + ID_MODEL_FROM_DATABASE=Sun PRO [Radeon HD 8570A/8570M] (Radeon HD 8570A) + +pci:v00001002d00006664* + ID_MODEL_FROM_DATABASE=Jet XT [Radeon R5 M240] + +pci:v00001002d00006665* + ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230] + +pci:v00001002d00006665sv000017AAsd0000368F* + ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230] (Radeon R5 A230) + +pci:v00001002d00006667* + ID_MODEL_FROM_DATABASE=Jet ULT [Radeon R5 M230] + +pci:v00001002d0000666F* + ID_MODEL_FROM_DATABASE=Sun LE [Radeon HD 8550M / R5 M230] + +pci:v00001002d00006704* + ID_MODEL_FROM_DATABASE=Cayman PRO GL [FirePro V7900] + +pci:v00001002d00006707* + ID_MODEL_FROM_DATABASE=Cayman LE GL [FirePro V5900] + +pci:v00001002d00006718* + ID_MODEL_FROM_DATABASE=Cayman XT [Radeon HD 6970] + +pci:v00001002d00006719* + ID_MODEL_FROM_DATABASE=Cayman PRO [Radeon HD 6950] + +pci:v00001002d0000671C* + ID_MODEL_FROM_DATABASE=Antilles [Radeon HD 6990] + +pci:v00001002d0000671D* + ID_MODEL_FROM_DATABASE=Antilles [Radeon HD 6990] + +pci:v00001002d0000671F* + ID_MODEL_FROM_DATABASE=Cayman CE [Radeon HD 6930] + +pci:v00001002d00006720* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] + +pci:v00001002d00006720sv00001028sd0000048F* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6990M) + +pci:v00001002d00006720sv00001028sd00000490* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Alienware M17x R3 Radeon HD 6970M) + +pci:v00001002d00006720sv00001028sd000004A4* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (FirePro M8900) + +pci:v00001002d00006720sv00001028sd000004BA* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6990M) + +pci:v00001002d00006720sv00001028sd0000053F* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (FirePro M8900) + +pci:v00001002d00006720sv0000106Bsd00000B00* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6970M) + +pci:v00001002d00006720sv00001558sd00005102* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6970M) + +pci:v00001002d00006720sv00001558sd00005104* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6990M) + +pci:v00001002d00006720sv00001558sd00007201* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6990M) + +pci:v00001002d00006720sv0000174Bsd0000E188* + ID_MODEL_FROM_DATABASE=Blackcomb [Radeon HD 6970M/6990M] (Radeon HD 6970M) + +pci:v00001002d00006738* + ID_MODEL_FROM_DATABASE=Barts XT [Radeon HD 6870] + +pci:v00001002d00006738sv00001682sd00003103* + ID_MODEL_FROM_DATABASE=Barts XT [Radeon HD 6870] (Radeon HD 8670) + +pci:v00001002d00006738sv00001787sd0000201A* + ID_MODEL_FROM_DATABASE=Barts XT [Radeon HD 6870] (Barts XT [Radeon HD 6870 X2]) + +pci:v00001002d00006738sv00001787sd0000201B* + ID_MODEL_FROM_DATABASE=Barts XT [Radeon HD 6870] (Barts XT [Radeon HD 6870 X2]) + +pci:v00001002d00006739* + ID_MODEL_FROM_DATABASE=Barts PRO [Radeon HD 6850] + +pci:v00001002d00006739sv00001043sd000003B4* + ID_MODEL_FROM_DATABASE=Barts PRO [Radeon HD 6850] (EAH6850 [Radeon HD 6850]) + +pci:v00001002d0000673E* + ID_MODEL_FROM_DATABASE=Barts LE [Radeon HD 6790] + +pci:v00001002d0000673Esv0000148Csd00007720* + ID_MODEL_FROM_DATABASE=Barts LE [Radeon HD 6790] (Radeon HD 7720 OEM) + +pci:v00001002d00006740* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] + +pci:v00001002d00006740sv00001019sd0000238C* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv00001019sd0000238E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv00001019sd00002391* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv00001019sd00002392* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Precision M4600) + +pci:v00001002d00006740sv00001028sd0000053E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (FirePro M5950) + +pci:v00001002d00006740sv0000103Csd00001630* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (FirePro M5950) + +pci:v00001002d00006740sv0000103Csd00001631* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (FirePro M5950) + +pci:v00001002d00006740sv0000103Csd0000164B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000103Csd0000164E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000103Csd00001657* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd00001658* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd0000165A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd0000165B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd00001688* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd00001689* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd0000168A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd0000185E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 7690M XT) + +pci:v00001002d00006740sv0000103Csd00003388* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd00003389* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd00003582* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6770M) + +pci:v00001002d00006740sv0000103Csd0000366C* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv00001043sd00001D02* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv00001043sd00001D12* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000104Dsd00009084* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000104Dsd00009085* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000144Dsd0000B074* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000144Dsd0000B077* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000144Dsd0000B084* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv0000144Dsd0000B088* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006740sv000017AAsd00003982* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6730M/6770M/7690M XT] (Radeon HD 6730M) + +pci:v00001002d00006741* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] + +pci:v00001002d00006741sv00001019sd0000238E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001019sd0000238F* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000379* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000037B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000037E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000382* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000384* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000385* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000386* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000387* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000388* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000442* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000451* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000489* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000048B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000048C* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000050A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000050B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000050C* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000050E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000050F* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000513* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000514* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000515* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000516* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000051E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000051F* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000520* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000521* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000052A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000555* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000556* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000055D* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000055E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000056D* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000059A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000059B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000059E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd0000059F* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000600* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000605* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000606* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001025sd00000619* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001028sd000004C1* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001028sd000004C5* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001028sd000004CD* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001028sd000004D7* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001028sd000004D9* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001028sd0000052D* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000103Csd00001617* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000103Csd00001646* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd00001647* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000103Csd0000164B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000103Csd0000164E* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000103Csd00001688* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd00001689* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd0000168A* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd00001860* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 7690M) + +pci:v00001002d00006741sv0000103Csd00003385* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000103Csd00003560* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd0000358D* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd00003590* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd00003593* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000103Csd0000366C* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001043sd00001CD2* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001043sd00002121* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001043sd00002122* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001043sd00002123* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001043sd00002125* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 7670M) + +pci:v00001002d00006741sv00001043sd00002127* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 7670M) + +pci:v00001002d00006741sv0000104Dsd0000907B* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000104Dsd00009080* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000104Dsd00009081* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000106Bsd000000E2* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (MacBookPro8,2 [Core i7, 15", Late 2011]) + +pci:v00001002d00006741sv00001179sd0000FD63* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv00001179sd0000FD65* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000144Dsd0000C093* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000144Dsd0000C0AC* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv0000144Dsd0000C0B3* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6750M) + +pci:v00001002d00006741sv0000144Dsd0000C539* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000144Dsd0000C609* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv0000152Dsd00000914* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv000017AAsd000021E1* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6630M) + +pci:v00001002d00006741sv000017AAsd00003970* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv000017AAsd00003976* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006741sv00001854sd00000907* + ID_MODEL_FROM_DATABASE=Whistler [Radeon HD 6630M/6650M/6750M/7670M/7690M] (Radeon HD 6650M) + +pci:v00001002d00006742* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] + +pci:v00001002d00006742sv00001002sd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv00001019sd00002393* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 6610M) + +pci:v00001002d00006742sv00001043sd00001D82* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (K53SK Laptop Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB22* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB23* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB27* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB2A* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB2C* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB30* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB31* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB32* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB38* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB39* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB3A* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB3B* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB40* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB41* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB47* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB48* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB49* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB51* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB52* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB53* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB56* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB81* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB82* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FB83* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FC56* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FCD4* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001179sd0000FCEE* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 7610M) + +pci:v00001002d00006742sv00001458sd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv00001462sd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv0000148Csd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv00001682sd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv0000174Bsd00005570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 5570]) + +pci:v00001002d00006742sv0000174Bsd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv0000174Bsd00007570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 7570]) + +pci:v00001002d00006742sv0000174Bsd00008510* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 8510]) + +pci:v00001002d00006742sv0000174Bsd00008570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 8570]) + +pci:v00001002d00006742sv00001787sd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv000017AFsd00006570* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Turks [Radeon HD 6570]) + +pci:v00001002d00006742sv00008086sd00002111* + ID_MODEL_FROM_DATABASE=Whistler LE [Radeon HD 6610M/7610M] (Radeon HD 6625M) + +pci:v00001002d00006743* + ID_MODEL_FROM_DATABASE=Whistler [Radeon E6760] + +pci:v00001002d00006749* + ID_MODEL_FROM_DATABASE=Turks GL [FirePro V4900] + +pci:v00001002d0000674A* + ID_MODEL_FROM_DATABASE=Turks GL [FirePro V3900] + +pci:v00001002d00006750* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] + +pci:v00001002d00006750sv00001462sd00002670* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 6670A) + +pci:v00001002d00006750sv000017AAsd00003079* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 7650A) + +pci:v00001002d00006750sv000017AAsd0000307A* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 6650A) + +pci:v00001002d00006750sv000017AAsd00003087* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 7650A) + +pci:v00001002d00006750sv000017AAsd00003618* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 6650A) + +pci:v00001002d00006750sv000017AAsd00003623* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 6650A) + +pci:v00001002d00006750sv000017AAsd00003627* + ID_MODEL_FROM_DATABASE=Onega [Radeon HD 6650A/7650A] (Radeon HD 6650A) + +pci:v00001002d00006751* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] + +pci:v00001002d00006751sv00001028sd00000548* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7650A) + +pci:v00001002d00006751sv00001462sd00002671* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7670A) + +pci:v00001002d00006751sv00001462sd00002672* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7670A) + +pci:v00001002d00006751sv00001462sd00002680* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7650A) + +pci:v00001002d00006751sv00001462sd00002681* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7650A) + +pci:v00001002d00006751sv000017AAsd00003087* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7650A/7670A] (Radeon HD 7650A) + +pci:v00001002d00006758* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] + +pci:v00001002d00006758sv00001028sd00000B0E* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 6670) + +pci:v00001002d00006758sv0000103Csd00006882* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 6670) + +pci:v00001002d00006758sv00001462sd0000250A* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 7670) + +pci:v00001002d00006758sv0000148Csd00007670* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 7670) + +pci:v00001002d00006758sv00001545sd00007670* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 7670) + +pci:v00001002d00006758sv00001682sd00003300* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 7670) + +pci:v00001002d00006758sv0000174Bsd00007670* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 7670) + +pci:v00001002d00006758sv0000174Bsd0000E181* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 6670) + +pci:v00001002d00006758sv00001787sd00002309* + ID_MODEL_FROM_DATABASE=Turks XT [Radeon HD 6670/7670] (Radeon HD 6670) + +pci:v00001002d00006759* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] + +pci:v00001002d00006759sv0000103Csd00003130* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001043sd00000403* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001462sd00002500* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001462sd00002509* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d00006759sv0000148Csd00007570* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d00006759sv00001642sd00003A67* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001682sd00003280* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d00006759sv00001682sd00003530* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 8550) + +pci:v00001002d00006759sv0000174Bsd00007570* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d00006759sv0000174Bsd0000E142* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv0000174Bsd0000E181* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd0000908F* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd00009090* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd00009091* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd00009092* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd0000909E* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 6570) + +pci:v00001002d00006759sv00001B0Asd000090B5* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d00006759sv00001B0Asd000090B6* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 6570/7570/8550] (Radeon HD 7570) + +pci:v00001002d0000675B* + ID_MODEL_FROM_DATABASE=Turks [Radeon HD 7600 Series] + +pci:v00001002d0000675D* + ID_MODEL_FROM_DATABASE=Turks PRO [Radeon HD 7570] + +pci:v00001002d0000675F* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] + +pci:v00001002d0000675Fsv0000148Csd00006510* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 6510) + +pci:v00001002d0000675Fsv0000148Csd00006530* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 6530) + +pci:v00001002d0000675Fsv0000148Csd00007510* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 7510) + +pci:v00001002d0000675Fsv00001545sd00007570* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 7570) + +pci:v00001002d0000675Fsv0000174Bsd00006510* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 6510) + +pci:v00001002d0000675Fsv0000174Bsd00007510* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 7510) + +pci:v00001002d0000675Fsv0000174Bsd00008510* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 8510) + +pci:v00001002d0000675Fsv00001787sd00002012* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 5570 2GB GDDR3) + +pci:v00001002d0000675Fsv00001787sd00002314* + ID_MODEL_FROM_DATABASE=Turks LE [Radeon HD 5570/6510/7510/8510] (Radeon HD 5570 1GB DDR2/GDDR3) + +pci:v00001002d00006760* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] + +pci:v00001002d00006760sv00001002sd00000124* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001002sd00000134* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001019sd0000238B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001019sd0000238E* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001019sd00002390* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001019sd00009985* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004C1* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004C3* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004CA* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004CB* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004CC* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Vostro 3350) + +pci:v00001002d00006760sv00001028sd000004D1* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004D3* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd000004D7* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd00000502* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd00000503* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd00000506* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd00000507* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd00000514* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001028sd0000051C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv00001028sd0000051D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv0000103Csd0000161A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000161B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000161E* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000161F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001622* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv0000103Csd00001623* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv0000103Csd0000164A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000164D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001651* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001656* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00001658* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00001659* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000165B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000165D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000165F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001661* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001663* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001665* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001667* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001669* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000166B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000166C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000166E* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001670* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001672* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000167A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000167B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000167D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000167F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000168C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000168F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001694* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001696* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00001698* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000169A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000169C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00001855* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv0000103Csd00001859* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv0000103Csd0000185C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv0000103Csd0000185D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000103Csd0000185F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000103Csd00001863* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv0000103Csd0000355C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000355F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00003563* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00003565* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00003567* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00003569* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00003581* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00003584* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd0000358C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000358F* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00003592* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd00003596* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000103Csd0000366B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000103Csd00003671* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (FirePro M3900) + +pci:v00001002d00006760sv0000103Csd00003673* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd0000100A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000100C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd0000101B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd0000101C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd0000102A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv00001043sd0000102C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd0000104B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000105D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000106B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000106D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000107D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd00001CB2* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd00001D22* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd00001D32* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd00002001* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd00002002* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd00002107* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd00002108* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd00002109* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd000084A0* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd000084E9* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001043sd00008515* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd00008517* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001043sd0000855A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000104Dsd0000907B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000104Dsd00009081* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000104Dsd00009084* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000104Dsd00009085* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv00001179sd00000003* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv00001179sd00000004* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6450M) + +pci:v00001002d00006760sv00001179sd0000FB22* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB23* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB2C* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB31* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB32* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB33* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB38* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB39* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB3A* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB40* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB41* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB42* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB47* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB48* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB51* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB52* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB53* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB81* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB82* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FB83* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FC51* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001179sd0000FC52* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FC56* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FCD3* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FCD4* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FCEE* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv00001179sd0000FDEE* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000144Dsd0000B074* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000B084* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000C095* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000C0B3* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6490M) + +pci:v00001002d00006760sv0000144Dsd0000C538* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000C581* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000C589* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000144Dsd0000C609* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000144Dsd0000C625* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv0000144Dsd0000C636* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv00001462sd000010AC* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv0000152Dsd00000916* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd000021E5* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd00003900* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv000017AAsd00003902* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv000017AAsd00003969* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd00003970* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv000017AAsd00003976* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd0000397B* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd0000397D* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv000017AAsd00005101* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7470M) + +pci:v00001002d00006760sv000017AAsd00005102* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv000017AAsd00005103* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv000017AAsd00005106* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 7450M) + +pci:v00001002d00006760sv00001854sd00000897* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001854sd00000900* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001854sd00000908* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006760sv00001854sd00002015* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M/7400M Series] (Radeon HD 6470M) + +pci:v00001002d00006761* + ID_MODEL_FROM_DATABASE=Seymour LP [Radeon HD 6430M] + +pci:v00001002d00006763* + ID_MODEL_FROM_DATABASE=Seymour [Radeon E6460] + +pci:v00001002d00006764* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M Series] + +pci:v00001002d00006765* + ID_MODEL_FROM_DATABASE=Seymour [Radeon HD 6400M Series] + +pci:v00001002d00006766* + ID_MODEL_FROM_DATABASE=Caicos + +pci:v00001002d00006767* + ID_MODEL_FROM_DATABASE=Caicos + +pci:v00001002d00006768* + ID_MODEL_FROM_DATABASE=Caicos + +pci:v00001002d00006770* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] + +pci:v00001002d00006770sv000017AAsd0000308D* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 7450A) + +pci:v00001002d00006770sv000017AAsd00003623* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 6450A) + +pci:v00001002d00006770sv000017AAsd00003627* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 6450A) + +pci:v00001002d00006770sv000017AAsd00003629* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 6450A) + +pci:v00001002d00006770sv000017AAsd0000363C* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 6450A) + +pci:v00001002d00006770sv000017AAsd00003658* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450A/7450A] (Radeon HD 7470A) + +pci:v00001002d00006771* + ID_MODEL_FROM_DATABASE=Caicos XTX [Radeon HD 8490 / R5 235X OEM] + +pci:v00001002d00006772* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 7450A] + +pci:v00001002d00006778* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] + +pci:v00001002d00006778sv00001019sd00000024* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 7470) + +pci:v00001002d00006778sv00001019sd00000027* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv00001028sd00002120* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 7470) + +pci:v00001002d00006778sv00001462sd0000B491* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv00001462sd0000B492* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv00001462sd0000B493* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470 OEM) + +pci:v00001002d00006778sv00001462sd0000B499* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon R5 235 OEM) + +pci:v00001002d00006778sv00001642sd00003C65* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv00001642sd00003C75* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv0000174Bsd00008145* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 8470) + +pci:v00001002d00006778sv0000174Bsd0000D145* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon R5 235 OEM) + +pci:v00001002d00006778sv0000174Bsd0000D335* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon R5 310 OEM) + +pci:v00001002d00006778sv0000174Bsd0000E145* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon HD 7470) + +pci:v00001002d00006778sv000017AAsd00003694* + ID_MODEL_FROM_DATABASE=Caicos XT [Radeon HD 7470/8470 / R5 235/310 OEM] (Radeon R5 A220) + +pci:v00001002d00006779* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] + +pci:v00001002d00006779sv00001019sd00000016* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001019sd00000017* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001019sd00000018* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001028sd00002120* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv0000103Csd00002128* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv0000103Csd00002AEE* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450A) + +pci:v00001002d00006779sv00001462sd00002125* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001462sd00002346* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv00001462sd00002490* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001462sd00002494* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001462sd00002496* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv0000148Csd00007450* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv0000148Csd00008450* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 8450 OEM) + +pci:v00001002d00006779sv00001545sd00007470* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7470) + +pci:v00001002d00006779sv00001642sd00003A65* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001642sd00003A66* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv00001642sd00003A75* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001642sd00003A76* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv00001682sd00003200* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv0000174Bsd00007450* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450) + +pci:v00001002d00006779sv0000174Bsd0000E127* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv0000174Bsd0000E153* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv0000174Bsd0000E164* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450 1 GB DDR3) + +pci:v00001002d00006779sv0000174Bsd0000E180* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv0000174Bsd0000E201* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv000017AFsd00008450* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 8450 OEM) + +pci:v00001002d00006779sv00001B0Asd00009096* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001B0Asd00009097* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001B0Asd000090A8* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450A) + +pci:v00001002d00006779sv00001B0Asd000090B1* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 6450) + +pci:v00001002d00006779sv00001B0Asd000090B3* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450A) + +pci:v00001002d00006779sv00001B0Asd000090BB* + ID_MODEL_FROM_DATABASE=Caicos [Radeon HD 6450/7450/8450 / R5 230 OEM] (Radeon HD 7450A) + +pci:v00001002d0000677B* + ID_MODEL_FROM_DATABASE=Caicos PRO [Radeon HD 7450] + +pci:v00001002d00006780* + ID_MODEL_FROM_DATABASE=Tahiti XT GL [FirePro W9000] + +pci:v00001002d00006784* + ID_MODEL_FROM_DATABASE=Tahiti [FirePro Series Graphics Adapter] + +pci:v00001002d00006788* + ID_MODEL_FROM_DATABASE=Tahiti [FirePro Series Graphics Adapter] + +pci:v00001002d0000678A* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] + +pci:v00001002d0000678Asv00001002sd0000030C* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro W8000) + +pci:v00001002d0000678Asv00001002sd00000310* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro S9000) + +pci:v00001002d0000678Asv00001002sd00000420* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (Radeon Sky 700) + +pci:v00001002d0000678Asv00001002sd00000422* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (Radeon Sky 900) + +pci:v00001002d0000678Asv00001002sd00000710* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro S9050) + +pci:v00001002d0000678Asv00001002sd00000B0E* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro S10000 Passive) + +pci:v00001002d0000678Asv00001002sd00000B2A* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro S10000) + +pci:v00001002d0000678Asv00001028sd0000030C* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro W8000) + +pci:v00001002d0000678Asv00001028sd00000710* + ID_MODEL_FROM_DATABASE=Tahiti PRO GL [FirePro Series] (FirePro S9000) + +pci:v00001002d00006798* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] + +pci:v00001002d00006798sv00001002sd00003000* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Radeon HD 7970 GHz Edition]) + +pci:v00001002d00006798sv00001002sd00003001* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XTL [Radeon R9 280X]) + +pci:v00001002d00006798sv00001002sd00004000* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Radeon HD 8970 OEM) + +pci:v00001002d00006798sv00001043sd0000041C* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 DirectCU II) + +pci:v00001002d00006798sv00001043sd00000420* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 DirectCU II TOP) + +pci:v00001002d00006798sv00001043sd00000444* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 DirectCU II TOP) + +pci:v00001002d00006798sv00001043sd00000448* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 DirectCU II TOP) + +pci:v00001002d00006798sv00001043sd0000044A* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Matrix HD 7970]) + +pci:v00001002d00006798sv00001043sd0000044C* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Matrix HD 7970 Platinum]) + +pci:v00001002d00006798sv00001043sd00003001* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XTL [ROG Matrix R9 280X]) + +pci:v00001002d00006798sv00001043sd00003006* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XTL [Radeon R9 280X DirectCU II TOP]) + +pci:v00001002d00006798sv00001043sd00009999* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (ARES II) + +pci:v00001002d00006798sv00001092sd00003000* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Radeon HD 7970 GHz Edition]) + +pci:v00001002d00006798sv00001458sd00002261* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Radeon HD 7970 GHz Edition OC]) + +pci:v00001002d00006798sv00001458sd00003001* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XTL [Radeon R9 280X OC]) + +pci:v00001002d00006798sv00001462sd00002774* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (MSI R7970 TF 3GD5/OC BE) + +pci:v00001002d00006798sv00001682sd00003211* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Double D HD 7970 Black Edition) + +pci:v00001002d00006798sv00001682sd00003213* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 Black Edition) + +pci:v00001002d00006798sv00001682sd00003214* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Double D HD 7970) + +pci:v00001002d00006798sv00001787sd0000201C* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (HD 7970 IceQ X²) + +pci:v00001002d00006798sv00001787sd00002317* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Radeon HD 7990) + +pci:v00001002d00006798sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Tahiti XT [Radeon HD 7970/8970 OEM / R9 280X] (Tahiti XT2 [Radeon HD 7970 GHz Edition]) + +pci:v00001002d00006799* + ID_MODEL_FROM_DATABASE=New Zealand [Radeon HD 7900 Series] + +pci:v00001002d0000679A* + ID_MODEL_FROM_DATABASE=Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280] + +pci:v00001002d0000679Asv00001002sd00000B01* + ID_MODEL_FROM_DATABASE=Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280] (Radeon HD 8950 OEM) + +pci:v00001002d0000679Asv00001002sd00003000* + ID_MODEL_FROM_DATABASE=Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280] (Tahiti PRO2 [Radeon HD 7950 Boost]) + +pci:v00001002d0000679Asv00001462sd00003000* + ID_MODEL_FROM_DATABASE=Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280] (Radeon HD 8950 OEM) + +pci:v00001002d0000679Asv0000174Bsd0000A003* + ID_MODEL_FROM_DATABASE=Tahiti PRO [Radeon HD 7950/8950 OEM / R9 280] (Radeon R9 280) + +pci:v00001002d0000679B* + ID_MODEL_FROM_DATABASE=Malta [Radeon HD 7990] + +pci:v00001002d0000679Bsv00001002sd00000B28* + ID_MODEL_FROM_DATABASE=Malta [Radeon HD 7990] (Radeon HD 8990 OEM) + +pci:v00001002d0000679Bsv00001002sd00000B2A* + ID_MODEL_FROM_DATABASE=Malta [Radeon HD 7990] (Radeon HD 7990) + +pci:v00001002d0000679Bsv00001462sd00008036* + ID_MODEL_FROM_DATABASE=Malta [Radeon HD 7990] (Radeon HD 8990 OEM) + +pci:v00001002d0000679Bsv0000148Csd00008990* + ID_MODEL_FROM_DATABASE=Malta [Radeon HD 7990] (Radeon HD 8990 OEM) + +pci:v00001002d0000679E* + ID_MODEL_FROM_DATABASE=Tahiti LE [Radeon HD 7870 XT] + +pci:v00001002d0000679Esv00001787sd00002328* + ID_MODEL_FROM_DATABASE=Tahiti LE [Radeon HD 7870 XT] (Radeon HD 7870 Black Edition 2 GB GDDR5 [2GBD5-2DHV3E]) + +pci:v00001002d0000679F* + ID_MODEL_FROM_DATABASE=Tahiti + +pci:v00001002d000067A0* + ID_MODEL_FROM_DATABASE=Hawaii XT GL [FirePro W9100] + +pci:v00001002d000067A0sv00001002sd00000335* + ID_MODEL_FROM_DATABASE=Hawaii XT GL [FirePro W9100] (FirePro S9150) + +pci:v00001002d000067A0sv00001002sd00000735* + ID_MODEL_FROM_DATABASE=Hawaii XT GL [FirePro W9100] (FirePro S9170) + +pci:v00001002d000067A0sv00001028sd0000031F* + ID_MODEL_FROM_DATABASE=Hawaii XT GL [FirePro W9100] (FirePro W9100) + +pci:v00001002d000067A0sv00001028sd00000335* + ID_MODEL_FROM_DATABASE=Hawaii XT GL [FirePro W9100] (FirePro S9150) + +pci:v00001002d000067A1* + ID_MODEL_FROM_DATABASE=Hawaii PRO GL [FirePro W8100] + +pci:v00001002d000067A1sv00001002sd00000335* + ID_MODEL_FROM_DATABASE=Hawaii PRO GL [FirePro W8100] (FirePro S9100) + +pci:v00001002d000067A1sv00001028sd00000335* + ID_MODEL_FROM_DATABASE=Hawaii PRO GL [FirePro W8100] (FirePro S9100) + +pci:v00001002d000067A2* + ID_MODEL_FROM_DATABASE=Hawaii GL + +pci:v00001002d000067A8* + ID_MODEL_FROM_DATABASE=Hawaii + +pci:v00001002d000067A9* + ID_MODEL_FROM_DATABASE=Hawaii + +pci:v00001002d000067AA* + ID_MODEL_FROM_DATABASE=Hawaii + +pci:v00001002d000067B0* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] + +pci:v00001002d000067B0sv00001028sd00000B00* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada XT [Radeon R9 390X]) + +pci:v00001002d000067B0sv0000103Csd00006566* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001043sd0000046A* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X DirectCU II) + +pci:v00001002d000067B0sv00001043sd0000046C* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X DirectCU II OC) + +pci:v00001002d000067B0sv00001043sd00000474* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Matrix R9 290X Platinum) + +pci:v00001002d000067B0sv00001043sd00000476* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (ARES III) + +pci:v00001002d000067B0sv00001043sd000004D7* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001043sd000004DB* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001043sd000004DF* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001043sd000004E9* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001458sd0000227C* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X WindForce 3X OC) + +pci:v00001002d000067B0sv00001458sd00002281* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X WindForce 3X OC) + +pci:v00001002d000067B0sv00001458sd0000228C* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X WindForce 3X) + +pci:v00001002d000067B0sv00001458sd0000228D* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X WindForce 3X OC) + +pci:v00001002d000067B0sv00001458sd00002290* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X WindForce 3X) + +pci:v00001002d000067B0sv00001458sd000022BC* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001458sd000022C1* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada PRO [Radeon R9 390]) + +pci:v00001002d000067B0sv00001462sd00002015* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv00001462sd00003070* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Lightning) + +pci:v00001002d000067B0sv00001462sd00003071* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Lightning) + +pci:v00001002d000067B0sv00001462sd00003072* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Lightning LE) + +pci:v00001002d000067B0sv00001462sd00003080* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Gaming) + +pci:v00001002d000067B0sv00001462sd00003082* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Gaming OC) + +pci:v00001002d000067B0sv0000148Csd00002347* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Devil 13 Dual Core R9 290X) + +pci:v00001002d000067B0sv0000148Csd00002357* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada XT [Radeon R9 390X]) + +pci:v00001002d000067B0sv00001682sd00009290* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Double Dissipation R9 290X) + +pci:v00001002d000067B0sv00001682sd00009395* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada XT [Radeon R9 390X]) + +pci:v00001002d000067B0sv0000174Bsd00000E34* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Radeon R9 390X) + +pci:v00001002d000067B0sv0000174Bsd0000E282* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Vapor-X R9 290X Tri-X OC) + +pci:v00001002d000067B0sv0000174Bsd0000E285* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X Tri-X OC) + +pci:v00001002d000067B0sv0000174Bsd0000E324* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada XT2 [Radeon R9 390X]) + +pci:v00001002d000067B0sv00001787sd00002020* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (R9 290X IceQ X² Turbo) + +pci:v00001002d000067B0sv00001787sd00002357* + ID_MODEL_FROM_DATABASE=Hawaii XT / Grenada XT [Radeon R9 290X/390X] (Grenada XT [Radeon R9 390X]) + +pci:v00001002d000067B1* + ID_MODEL_FROM_DATABASE=Hawaii PRO [Radeon R9 290/390] + +pci:v00001002d000067B1sv00001043sd000004DD* + ID_MODEL_FROM_DATABASE=Hawaii PRO [Radeon R9 290/390] (STRIX R9 390) + +pci:v00001002d000067B1sv0000148Csd00002358* + ID_MODEL_FROM_DATABASE=Hawaii PRO [Radeon R9 290/390] (Radeon R9 390) + +pci:v00001002d000067B1sv0000174Bsd0000E324* + ID_MODEL_FROM_DATABASE=Hawaii PRO [Radeon R9 290/390] (Sapphire Nitro R9 390) + +pci:v00001002d000067B9* + ID_MODEL_FROM_DATABASE=Vesuvius [Radeon R9 295X2] + +pci:v00001002d000067BE* + ID_MODEL_FROM_DATABASE=Hawaii LE + +pci:v00001002d000067C0* + ID_MODEL_FROM_DATABASE=Ellesmere [Polaris10] + +pci:v00001002d000067DF* + ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 480] + +pci:v00001002d000067E0* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d000067E1* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d000067E8* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d000067E9* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d000067EB* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d000067FF* + ID_MODEL_FROM_DATABASE=Baffin [Polaris11] + +pci:v00001002d00006800* + ID_MODEL_FROM_DATABASE=Wimbledon XT [Radeon HD 7970M] + +pci:v00001002d00006800sv00001002sd00000124* + ID_MODEL_FROM_DATABASE=Wimbledon XT [Radeon HD 7970M] (Radeon HD 7970M) + +pci:v00001002d00006800sv00008086sd00002110* + ID_MODEL_FROM_DATABASE=Wimbledon XT [Radeon HD 7970M] (Radeon HD 7970M) + +pci:v00001002d00006800sv00008086sd00002111* + ID_MODEL_FROM_DATABASE=Wimbledon XT [Radeon HD 7970M] (Radeon HD 7970M) + +pci:v00001002d00006801* + ID_MODEL_FROM_DATABASE=Neptune XT [Radeon HD 8970M] + +pci:v00001002d00006801sv00001002sd00000124* + ID_MODEL_FROM_DATABASE=Neptune XT [Radeon HD 8970M] (Radeon HD 8970M) + +pci:v00001002d00006801sv00001462sd00001117* + ID_MODEL_FROM_DATABASE=Neptune XT [Radeon HD 8970M] (Radeon R9 M290X) + +pci:v00001002d00006801sv00008086sd00002110* + ID_MODEL_FROM_DATABASE=Neptune XT [Radeon HD 8970M] (Radeon HD 8970M) + +pci:v00001002d00006801sv00008086sd00002111* + ID_MODEL_FROM_DATABASE=Neptune XT [Radeon HD 8970M] (Radeon HD 8970M) + +pci:v00001002d00006802* + ID_MODEL_FROM_DATABASE=Wimbledon + +pci:v00001002d00006806* + ID_MODEL_FROM_DATABASE=Neptune + +pci:v00001002d00006808* + ID_MODEL_FROM_DATABASE=Pitcairn XT GL [FirePro W7000] + +pci:v00001002d00006808sv00001002sd00000310* + ID_MODEL_FROM_DATABASE=Pitcairn XT GL [FirePro W7000] (FirePro S7000) + +pci:v00001002d00006808sv00001002sd00000420* + ID_MODEL_FROM_DATABASE=Pitcairn XT GL [FirePro W7000] (Radeon Sky 500) + +pci:v00001002d00006809* + ID_MODEL_FROM_DATABASE=Pitcairn LE GL [FirePro W5000] + +pci:v00001002d00006810* + ID_MODEL_FROM_DATABASE=Curacao XT / Trinidad XT [Radeon R7 370 / R9 270X/370X] + +pci:v00001002d00006810sv0000148Csd00000908* + ID_MODEL_FROM_DATABASE=Curacao XT / Trinidad XT [Radeon R7 370 / R9 270X/370X] (Radeon R9 370 OEM) + +pci:v00001002d00006810sv00001682sd00007370* + ID_MODEL_FROM_DATABASE=Curacao XT / Trinidad XT [Radeon R7 370 / R9 270X/370X] (Radeon R7 370) + +pci:v00001002d00006811* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] + +pci:v00001002d00006811sv00001028sd00000B00* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv00001043sd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv00001458sd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv00001462sd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv0000148Csd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv00001682sd00002015* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R7 370]) + +pci:v00001002d00006811sv0000174Bsd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006811sv00001787sd00002016* + ID_MODEL_FROM_DATABASE=Curacao PRO [Radeon R7 370 / R9 270/370 OEM] (Trinidad PRO [Radeon R9 370 OEM]) + +pci:v00001002d00006816* + ID_MODEL_FROM_DATABASE=Pitcairn + +pci:v00001002d00006817* + ID_MODEL_FROM_DATABASE=Pitcairn + +pci:v00001002d00006818* + ID_MODEL_FROM_DATABASE=Pitcairn XT [Radeon HD 7870 GHz Edition] + +pci:v00001002d00006818sv00001002sd00000B05* + ID_MODEL_FROM_DATABASE=Pitcairn XT [Radeon HD 7870 GHz Edition] (Radeon HD 8870 OEM) + +pci:v00001002d00006818sv0000174Bsd00008B04* + ID_MODEL_FROM_DATABASE=Pitcairn XT [Radeon HD 7870 GHz Edition] (Radeon HD 8860) + +pci:v00001002d00006819* + ID_MODEL_FROM_DATABASE=Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP] + +pci:v00001002d00006819sv00001682sd00007269* + ID_MODEL_FROM_DATABASE=Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP] (Radeon R9 270 1024SP) + +pci:v00001002d00006819sv00001682sd00009278* + ID_MODEL_FROM_DATABASE=Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP] (Radeon R9 270 1024SP) + +pci:v00001002d00006819sv0000174Bsd0000A008* + ID_MODEL_FROM_DATABASE=Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP] (Radeon R9 270 1024SP) + +pci:v00001002d00006819sv0000174Bsd0000E221* + ID_MODEL_FROM_DATABASE=Pitcairn PRO [Radeon HD 7850 / R7 265 / R9 270 1024SP] (Radeon HD 7850 2GB GDDR5 DVI-I/DVI-D/HDMI/DP) + +pci:v00001002d00006820* + ID_MODEL_FROM_DATABASE=Venus XTX [Radeon HD 8890M / R9 M275X/M375X] + +pci:v00001002d00006820sv0000103Csd00001851* + ID_MODEL_FROM_DATABASE=Venus XTX [Radeon HD 8890M / R9 M275X/M375X] (Radeon HD 7750M) + +pci:v00001002d00006820sv000017AAsd00003643* + ID_MODEL_FROM_DATABASE=Venus XTX [Radeon HD 8890M / R9 M275X/M375X] (Radeon R9 A375) + +pci:v00001002d00006820sv000017AAsd00003801* + ID_MODEL_FROM_DATABASE=Venus XTX [Radeon HD 8890M / R9 M275X/M375X] (Radeon R9 M275) + +pci:v00001002d00006820sv000017AAsd00003824* + ID_MODEL_FROM_DATABASE=Venus XTX [Radeon HD 8890M / R9 M275X/M375X] (Radeon R9 M375) + +pci:v00001002d00006821* + ID_MODEL_FROM_DATABASE=Venus XT [Radeon HD 8870M / R9 M270X/M370X] + +pci:v00001002d00006821sv00001002sd0000031E* + ID_MODEL_FROM_DATABASE=Venus XT [Radeon HD 8870M / R9 M270X/M370X] (FirePro SX4000) + +pci:v00001002d00006821sv00001028sd000005CC* + ID_MODEL_FROM_DATABASE=Venus XT [Radeon HD 8870M / R9 M270X/M370X] (FirePro M5100) + +pci:v00001002d00006821sv00001028sd000015CC* + ID_MODEL_FROM_DATABASE=Venus XT [Radeon HD 8870M / R9 M270X/M370X] (FirePro M5100) + +pci:v00001002d00006821sv0000106Bsd00000149* + ID_MODEL_FROM_DATABASE=Venus XT [Radeon HD 8870M / R9 M270X/M370X] (Radeon R9 M370X Mac Edition) + +pci:v00001002d00006822* + ID_MODEL_FROM_DATABASE=Venus PRO [Radeon E8860] + +pci:v00001002d00006823* + ID_MODEL_FROM_DATABASE=Venus PRO [Radeon HD 8850M / R9 M265X] + +pci:v00001002d00006825* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] + +pci:v00001002d00006825sv00001028sd0000053F* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] (FirePro M6000) + +pci:v00001002d00006825sv00001028sd000005CD* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] (FirePro M6000) + +pci:v00001002d00006825sv00001028sd000015CD* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] (FirePro M6000) + +pci:v00001002d00006825sv0000103Csd0000176C* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] (FirePro M6000) + +pci:v00001002d00006825sv00008086sd00002111* + ID_MODEL_FROM_DATABASE=Heathrow XT [Radeon HD 7870M] (Chelsea PRO) + +pci:v00001002d00006826* + ID_MODEL_FROM_DATABASE=Chelsea LP [Radeon HD 7700M Series] + +pci:v00001002d00006827* + ID_MODEL_FROM_DATABASE=Heathrow PRO [Radeon HD 7850M/8850M] + +pci:v00001002d00006828* + ID_MODEL_FROM_DATABASE=Cape Verde PRO [FirePro W600] + +pci:v00001002d00006829* + ID_MODEL_FROM_DATABASE=Cape Verde + +pci:v00001002d0000682A* + ID_MODEL_FROM_DATABASE=Venus PRO + +pci:v00001002d0000682B* + ID_MODEL_FROM_DATABASE=Venus LE [Radeon HD 8830M] + +pci:v00001002d0000682C* + ID_MODEL_FROM_DATABASE=Cape Verde GL [FirePro W4100] + +pci:v00001002d0000682D* + ID_MODEL_FROM_DATABASE=Chelsea XT GL [FirePro M4000] + +pci:v00001002d0000682F* + ID_MODEL_FROM_DATABASE=Chelsea LP [Radeon HD 7730M] + +pci:v00001002d0000682Fsv0000103Csd00001851* + ID_MODEL_FROM_DATABASE=Chelsea LP [Radeon HD 7730M] (Radeon HD 7750M) + +pci:v00001002d00006830* + ID_MODEL_FROM_DATABASE=Cape Verde [Radeon HD 7800M Series] + +pci:v00001002d00006831* + ID_MODEL_FROM_DATABASE=Cape Verde [AMD Radeon HD 7700M Series] + +pci:v00001002d00006835* + ID_MODEL_FROM_DATABASE=Cape Verde PRX [Radeon R9 255 OEM] + +pci:v00001002d00006837* + ID_MODEL_FROM_DATABASE=Cape Verde LE [Radeon HD 7730/8730] + +pci:v00001002d00006837sv00001462sd00002796* + ID_MODEL_FROM_DATABASE=Cape Verde LE [Radeon HD 7730/8730] (Radeon HD 8730) + +pci:v00001002d00006837sv00001462sd00008092* + ID_MODEL_FROM_DATABASE=Cape Verde LE [Radeon HD 7730/8730] (Radeon HD 8730) + +pci:v00001002d00006837sv0000148Csd00008730* + ID_MODEL_FROM_DATABASE=Cape Verde LE [Radeon HD 7730/8730] (Radeon HD 8730) + +pci:v00001002d00006837sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Cape Verde LE [Radeon HD 7730/8730] (Radeon HD 6570) + +pci:v00001002d0000683D* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] + +pci:v00001002d0000683Dsv00001002sd00000030* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (Radeon HD 8760 OEM) + +pci:v00001002d0000683Dsv00001019sd00000030* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (Radeon HD 8760 OEM) + +pci:v00001002d0000683Dsv0000103Csd00006890* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (Radeon HD 8760 OEM) + +pci:v00001002d0000683Dsv00001043sd00008760* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (Radeon HD 8760 OEM) + +pci:v00001002d0000683Dsv00001462sd00002710* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (R7770-PMD1GD5) + +pci:v00001002d0000683Dsv0000174Bsd00008304* + ID_MODEL_FROM_DATABASE=Cape Verde XT [Radeon HD 7770/8760 / R7 250X] (Radeon HD 8760 OEM) + +pci:v00001002d0000683F* + ID_MODEL_FROM_DATABASE=Cape Verde PRO [Radeon HD 7750/8740 / R7 250E] + +pci:v00001002d0000683Fsv00001462sd00002790* + ID_MODEL_FROM_DATABASE=Cape Verde PRO [Radeon HD 7750/8740 / R7 250E] (Radeon HD 8740) + +pci:v00001002d0000683Fsv00001462sd00002791* + ID_MODEL_FROM_DATABASE=Cape Verde PRO [Radeon HD 7750/8740 / R7 250E] (Radeon HD 8740) + +pci:v00001002d0000683Fsv00001642sd00003B97* + ID_MODEL_FROM_DATABASE=Cape Verde PRO [Radeon HD 7750/8740 / R7 250E] (Radeon HD 8740) + +pci:v00001002d00006840* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] + +pci:v00001002d00006840sv00001025sd0000050E* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd0000050F* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd00000513* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd00000514* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd0000056D* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd0000059A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd0000059B* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd0000059E* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd00000600* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd00000606* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001025sd00000696* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7650M) + +pci:v00001002d00006840sv00001025sd00000697* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7650M) + +pci:v00001002d00006840sv00001025sd00000698* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7650M) + +pci:v00001002d00006840sv00001025sd00000699* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7650M) + +pci:v00001002d00006840sv00001025sd00000757* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd0000056A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd0000056E* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd00000598* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd0000059D* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd000005A3* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd000005B9* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001028sd000005BB* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001789* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (FirePro M2000) + +pci:v00001002d00006840sv0000103Csd000017F1* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7570M) + +pci:v00001002d00006840sv0000103Csd000017F4* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7650M) + +pci:v00001002d00006840sv0000103Csd00001813* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7590M) + +pci:v00001002d00006840sv0000103Csd0000182F* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001830* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001835* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd0000183A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd0000183C* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd0000183E* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001840* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001842* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001844* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001848* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd0000184A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd0000184C* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001895* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd00001897* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd000018A5* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd000018A7* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000103Csd000018F4* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd0000100A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd0000104B* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd000010DC* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd00002121* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd00002122* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd00002123* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd00002125* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001043sd00002127* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB11* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB22* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB23* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB2C* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB31* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB32* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB38* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB39* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB3A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB40* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB41* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB47* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB48* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB51* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB52* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB53* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB81* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB82* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FB83* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FC56* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FCD4* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv00001179sd0000FCEE* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000144Dsd0000C0C5* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7690M) + +pci:v00001002d00006840sv0000144Dsd0000C0CE* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv0000144Dsd0000C0DA* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv000017AAsd00003970* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv000017AAsd0000397B* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv000017AAsd00005101* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv000017AAsd00005102* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006840sv000017AAsd00005103* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7500M/7600M Series] (Radeon HD 7670M) + +pci:v00001002d00006841* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] + +pci:v00001002d00006841sv00001028sd00000561* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001028sd0000056C* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001028sd0000057F* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7570M) + +pci:v00001002d00006841sv0000103Csd000017F1* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7570M) + +pci:v00001002d00006841sv0000103Csd000017F4* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd00001813* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7570M) + +pci:v00001002d00006841sv0000103Csd0000183A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd0000183C* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd0000183E* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd00001840* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd00001842* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv0000103Csd00001844* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001043sd0000100A* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001043sd0000104B* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001043sd000010DC* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001043sd00002134* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7650M) + +pci:v00001002d00006841sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7570M) + +pci:v00001002d00006841sv00001179sd00000002* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7570M) + +pci:v00001002d00006841sv00001179sd0000FB43* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv00001179sd0000FB91* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv00001179sd0000FB92* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv00001179sd0000FB93* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv00001179sd0000FBA2* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv00001179sd0000FBA3* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006841sv0000144Dsd0000C0C7* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7550M/7570M/7650M] (Radeon HD 7550M) + +pci:v00001002d00006842* + ID_MODEL_FROM_DATABASE=Thames LE [Radeon HD 7000M Series] + +pci:v00001002d00006843* + ID_MODEL_FROM_DATABASE=Thames [Radeon HD 7670M] + +pci:v00001002d00006888* + ID_MODEL_FROM_DATABASE=Cypress XT [FirePro V8800] + +pci:v00001002d00006889* + ID_MODEL_FROM_DATABASE=Cypress PRO [FirePro V7800] + +pci:v00001002d00006889sv00001002sd00000301* + ID_MODEL_FROM_DATABASE=Cypress PRO [FirePro V7800] (FirePro V7800P) + +pci:v00001002d0000688A* + ID_MODEL_FROM_DATABASE=Cypress XT [FirePro V9800] + +pci:v00001002d0000688Asv00001002sd0000030C* + ID_MODEL_FROM_DATABASE=Cypress XT [FirePro V9800] (FirePro V9800P) + +pci:v00001002d0000688C* + ID_MODEL_FROM_DATABASE=Cypress XT GL [FireStream 9370] + +pci:v00001002d0000688D* + ID_MODEL_FROM_DATABASE=Cypress PRO GL [FireStream 9350] + +pci:v00001002d00006898* + ID_MODEL_FROM_DATABASE=Cypress XT [Radeon HD 5870] + +pci:v00001002d00006898sv00001002sd00000B00* + ID_MODEL_FROM_DATABASE=Cypress XT [Radeon HD 5870] (Radeon HD 5870 Eyefinity⁶ Edition) + +pci:v00001002d00006898sv0000106Bsd000000D0* + ID_MODEL_FROM_DATABASE=Cypress XT [Radeon HD 5870] (Radeon HD 5870 Mac Edition) + +pci:v00001002d00006898sv00001462sd00008032* + ID_MODEL_FROM_DATABASE=Cypress XT [Radeon HD 5870] (Radeon HD 5870 1 GB GDDR5) + +pci:v00001002d00006898sv0000174Bsd00006870* + ID_MODEL_FROM_DATABASE=Cypress XT [Radeon HD 5870] (Radeon HD 6870 1600SP Edition) + +pci:v00001002d00006899* + ID_MODEL_FROM_DATABASE=Cypress PRO [Radeon HD 5850] + +pci:v00001002d00006899sv00001043sd00000330* + ID_MODEL_FROM_DATABASE=Cypress PRO [Radeon HD 5850] (Radeon HD 5850) + +pci:v00001002d00006899sv0000174Bsd0000237B* + ID_MODEL_FROM_DATABASE=Cypress PRO [Radeon HD 5850] (Radeon HD 5850 X2) + +pci:v00001002d00006899sv0000174Bsd00006850* + ID_MODEL_FROM_DATABASE=Cypress PRO [Radeon HD 5850] (Radeon HD 6850 1440SP Edition) + +pci:v00001002d0000689B* + ID_MODEL_FROM_DATABASE=Cypress PRO [Radeon HD 6800 Series] + +pci:v00001002d0000689C* + ID_MODEL_FROM_DATABASE=Hemlock [Radeon HD 5970] + +pci:v00001002d0000689Csv00001043sd00000352* + ID_MODEL_FROM_DATABASE=Hemlock [Radeon HD 5970] (ARES) + +pci:v00001002d0000689D* + ID_MODEL_FROM_DATABASE=Hemlock [Radeon HD 5970] + +pci:v00001002d0000689E* + ID_MODEL_FROM_DATABASE=Cypress LE [Radeon HD 5830] + +pci:v00001002d000068A0* + ID_MODEL_FROM_DATABASE=Broadway XT [Mobility Radeon HD 5870] + +pci:v00001002d000068A0sv00001028sd000012EF* + ID_MODEL_FROM_DATABASE=Broadway XT [Mobility Radeon HD 5870] (FirePro M7820) + +pci:v00001002d000068A0sv0000103Csd00001520* + ID_MODEL_FROM_DATABASE=Broadway XT [Mobility Radeon HD 5870] (FirePro M7820) + +pci:v00001002d000068A1* + ID_MODEL_FROM_DATABASE=Broadway PRO [Mobility Radeon HD 5850] + +pci:v00001002d000068A1sv0000106Bsd000000CC* + ID_MODEL_FROM_DATABASE=Broadway PRO [Mobility Radeon HD 5850] (iMac MC511 Mobility Radeon HD 5850 MXM Module) + +pci:v00001002d000068A8* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] + +pci:v00001002d000068A8sv00001025sd00000442* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000451* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000050A* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000050B* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000050C* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000050E* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000050F* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000513* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000514* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000515* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000516* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000525* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd00000526* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001025sd0000056D* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv00001028sd0000048F* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6870M) + +pci:v00001002d000068A8sv00001028sd00000490* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6870M) + +pci:v00001002d000068A8sv00001028sd000004B9* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6870M) + +pci:v00001002d000068A8sv00001028sd000004BA* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6870M) + +pci:v00001002d000068A8sv0000103Csd0000159B* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A8sv0000144Dsd0000C0AD* + ID_MODEL_FROM_DATABASE=Granville [Radeon HD 6850M/6870M] (Radeon HD 6850M) + +pci:v00001002d000068A9* + ID_MODEL_FROM_DATABASE=Juniper XT [FirePro V5800] + +pci:v00001002d000068B8* + ID_MODEL_FROM_DATABASE=Juniper XT [Radeon HD 5770] + +pci:v00001002d000068B8sv0000106Bsd000000CF* + ID_MODEL_FROM_DATABASE=Juniper XT [Radeon HD 5770] (MacPro5,1 [Mac Pro 2.8GHz DDR3]) + +pci:v00001002d000068B9* + ID_MODEL_FROM_DATABASE=Juniper LE [Radeon HD 5670 640SP Edition] + +pci:v00001002d000068BA* + ID_MODEL_FROM_DATABASE=Juniper XT [Radeon HD 6770] + +pci:v00001002d000068BE* + ID_MODEL_FROM_DATABASE=Juniper PRO [Radeon HD 5750] + +pci:v00001002d000068BEsv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=Juniper PRO [Radeon HD 5750] (Radeon HD 6750) + +pci:v00001002d000068BF* + ID_MODEL_FROM_DATABASE=Juniper PRO [Radeon HD 6750] + +pci:v00001002d000068BFsv0000174Bsd00006750* + ID_MODEL_FROM_DATABASE=Juniper PRO [Radeon HD 6750] (Radeon HD 6750) + +pci:v00001002d000068C0* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] + +pci:v00001002d000068C0sv00001019sd00002383* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv00001028sd000002A2* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv00001028sd000002FE* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv00001028sd00000419* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv0000103Csd0000147D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv0000103Csd00001521* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Madison XT [FirePro M5800]) + +pci:v00001002d000068C0sv0000103Csd00001593* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 6570) + +pci:v00001002d000068C0sv0000103Csd00001596* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 6570) + +pci:v00001002d000068C0sv0000103Csd00001599* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 6570) + +pci:v00001002d000068C0sv00001043sd00001C22* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv000017AAsd00003927* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv000017AAsd00003952* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Mobility Radeon HD 5730) + +pci:v00001002d000068C0sv000017AAsd00003978* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5730 / 6570M] (Radeon HD 6570M) + +pci:v00001002d000068C1* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] + +pci:v00001002d000068C1sv00001025sd00000205* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000293* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000294* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000296* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000308* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000030A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000311* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000312* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000031C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000031D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000033D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000033E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000033F* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000346* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Aspire 7740G) + +pci:v00001002d000068C1sv00001025sd00000348* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000356* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000357* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000358* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000359* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000035A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000035B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000035C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000035D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000035E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000360* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000362* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000364* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000365* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000366* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000367* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000368* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000036C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000036D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000036E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000036F* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000372* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000373* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000377* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000378* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000379* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000037A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000037B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000037E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000037F* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000382* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000383* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000384* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000385* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000386* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000387* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000388* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000038B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000038C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000039A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000411* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000412* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000418* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000419* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000420* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000421* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000425* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000042A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000042E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000042F* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000432* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000433* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000442* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000044C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd0000044E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000451* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000454* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000455* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000475* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000476* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000487* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000489* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000498* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001025sd00000517* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000051A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000051B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000051C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000051D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd00000525* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd00000526* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000052B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000052C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000053C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000053D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000053E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd0000053F* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001025sd00000607* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C1sv00001028sd0000041B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001028sd00000447* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001028sd00000448* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001028sd00000456* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001028sd00000457* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001436* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001437* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001440* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001448* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001449* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd0000144A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd0000144B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd0000147B* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd0000149C* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd0000149E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000103Csd00001521* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Madison Pro [FirePro M5800]) + +pci:v00001002d000068C1sv00001043sd00001BC2* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000104Dsd00009071* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000104Dsd00009077* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000104Dsd00009081* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD00* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD12* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD1A* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD30* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD31* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD50* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FD52* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6530M) + +pci:v00001002d000068C1sv00001179sd0000FD63* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6530M) + +pci:v00001002d000068C1sv00001179sd0000FD65* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6530M) + +pci:v00001002d000068C1sv00001179sd0000FDD0* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv00001179sd0000FDD2* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6530M) + +pci:v00001002d000068C1sv0000144Dsd0000C07E* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv0000144Dsd0000C085* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv000014C0sd00000043* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv000014C0sd0000004D* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv000017AAsd00003928* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv000017AAsd00003951* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Mobility Radeon HD 5650) + +pci:v00001002d000068C1sv000017AAsd00003977* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5650/5750 / 6530M/6550M] (Radeon HD 6550M) + +pci:v00001002d000068C7* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] + +pci:v00001002d000068C7sv00001462sd00002241* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] (Mobility Radeon HD 5570) + +pci:v00001002d000068C7sv00001462sd00002243* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] (Mobility Radeon HD 5570) + +pci:v00001002d000068C7sv00001462sd00002244* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] (Mobility Radeon HD 5570) + +pci:v00001002d000068C7sv00001462sd00002245* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] (Radeon HD 6550A) + +pci:v00001002d000068C7sv00001462sd00002246* + ID_MODEL_FROM_DATABASE=Madison [Mobility Radeon HD 5570/6550A] (Radeon HD 6550A) + +pci:v00001002d000068C8* + ID_MODEL_FROM_DATABASE=Redwood XT GL [FirePro V4800] + +pci:v00001002d000068C9* + ID_MODEL_FROM_DATABASE=Redwood PRO GL [FirePro V3800] + +pci:v00001002d000068D8* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] + +pci:v00001002d000068D8sv00001028sd000068E0* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5670) + +pci:v00001002d000068D8sv0000174Bsd00005690* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5690) + +pci:v00001002d000068D8sv0000174Bsd00005730* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5730) + +pci:v00001002d000068D8sv0000174Bsd0000E151* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5670) + +pci:v00001002d000068D8sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5730) + +pci:v00001002d000068D8sv000017AFsd00003010* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5730) + +pci:v00001002d000068D8sv000017AFsd00003011* + ID_MODEL_FROM_DATABASE=Redwood XT [Radeon HD 5670/5690/5730] (Radeon HD 5690) + +pci:v00001002d000068D9* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] + +pci:v00001002d000068D9sv0000103Csd00006870* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv0000103Csd00006872* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv00001043sd000003CE* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5550) + +pci:v00001002d000068D9sv00001462sd00002151* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv00001462sd00002240* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6510) + +pci:v00001002d000068D9sv0000148Csd00003001* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6610) + +pci:v00001002d000068D9sv00001545sd00005550* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5550) + +pci:v00001002d000068D9sv00001545sd00007570* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 7570) + +pci:v00001002d000068D9sv00001642sd00003985* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv00001642sd00003996* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6510) + +pci:v00001002d000068D9sv0000174Bsd00006510* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6510) + +pci:v00001002d000068D9sv0000174Bsd00006610* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6610) + +pci:v00001002d000068D9sv0000174Bsd0000E142* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5570) + +pci:v00001002d000068D9sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6510) + +pci:v00001002d000068D9sv000017AFsd00003000* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 6510) + +pci:v00001002d000068D9sv000017AFsd00003010* + ID_MODEL_FROM_DATABASE=Redwood PRO [Radeon HD 5550/5570/5630/6510/6610/7570] (Radeon HD 5630) + +pci:v00001002d000068DA* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] + +pci:v00001002d000068DAsv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 6390) + +pci:v00001002d000068DAsv0000148Csd00003001* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 6490) + +pci:v00001002d000068DAsv00001545sd00007570* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 7570) + +pci:v00001002d000068DAsv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 6390) + +pci:v00001002d000068DAsv0000174Bsd00005570* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 5570) + +pci:v00001002d000068DAsv0000174Bsd00005630* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 5630) + +pci:v00001002d000068DAsv0000174Bsd00006490* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 6490) + +pci:v00001002d000068DAsv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 5630) + +pci:v00001002d000068DAsv000017AFsd00003000* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 6390) + +pci:v00001002d000068DAsv000017AFsd00003010* + ID_MODEL_FROM_DATABASE=Redwood LE [Radeon HD 5550/5570/5630/6390/6490/7570] (Radeon HD 5630) + +pci:v00001002d000068DE* + ID_MODEL_FROM_DATABASE=Redwood + +pci:v00001002d000068E0* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] + +pci:v00001002d000068E0sv00001028sd00000404* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv00001028sd00000414* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv00001028sd00000434* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd00001433* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd00001434* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd00001469* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd0000146B* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd00001486* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (TouchSmart tm2-2050er discrete GPU (Mobility Radeon HD 5450)) + +pci:v00001002d000068E0sv0000103Csd00001622* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd00001623* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000103Csd0000EEEE* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv0000104Dsd00009076* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Mobility Radeon HD 5450) + +pci:v00001002d000068E0sv00001682sd0000304E* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E0sv00001682sd00006000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E0sv000017AAsd00009E52* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (FirePro M3800) + +pci:v00001002d000068E0sv000017AAsd00009E53* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430/5450/5470] (FirePro M3800) + +pci:v00001002d000068E1* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] + +pci:v00001002d000068E1sv00001043sd0000041F* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv00001043sd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv0000148Csd00003001* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6230]) + +pci:v00001002d000068E1sv0000148Csd00003002* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6250]) + +pci:v00001002d000068E1sv0000148Csd00003003* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6350]) + +pci:v00001002d000068E1sv0000148Csd00007350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv0000148Csd00008350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 8350]) + +pci:v00001002d000068E1sv00001545sd00005450* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv00001545sd00007350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv00001682sd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv00001682sd00006000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv00001682sd00007350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv0000174Bsd00005470* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5470]) + +pci:v00001002d000068E1sv0000174Bsd00006000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv0000174Bsd00006230* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6230]) + +pci:v00001002d000068E1sv0000174Bsd00006350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6350]) + +pci:v00001002d000068E1sv0000174Bsd00007350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv000017AFsd00003000* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 5450]) + +pci:v00001002d000068E1sv000017AFsd00003001* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6230]) + +pci:v00001002d000068E1sv000017AFsd00003014* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 6350]) + +pci:v00001002d000068E1sv000017AFsd00003015* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 7350]) + +pci:v00001002d000068E1sv000017AFsd00008350* + ID_MODEL_FROM_DATABASE=Park [Mobility Radeon HD 5430] (Caicos [Radeon HD 8350 OEM]) + +pci:v00001002d000068E4* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] + +pci:v00001002d000068E4sv00001019sd00002386* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6350M) + +pci:v00001002d000068E4sv00001019sd00002387* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6350M) + +pci:v00001002d000068E4sv00001019sd0000238D* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001019sd0000238E* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001025sd00000382* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001025sd00000489* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001025sd0000048A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001025sd0000048B* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001025sd0000048C* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001028sd000004C1* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001028sd000004CA* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001028sd000004CC* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001028sd000004CD* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001028sd000004D7* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001411* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001421* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001426* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001428* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000142A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000142B* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000143A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000143C* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001445* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000162C* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000162D* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000162E* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000162F* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001639* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163B* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163C* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163D* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163E* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000163F* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001641* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00001643* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00003578* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd0000357A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00003673* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000103Csd00003675* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001043sd00001C92* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001043sd000084A1* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001043sd000084AD* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000104Dsd00009081* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001545sd00007350* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Cedar [Radeon HD 7350]) + +pci:v00001002d000068E4sv00001558sd00004510* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv00001558sd00005505* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv0000174Bsd00005450* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Cedar [Radeon HD 5450]) + +pci:v00001002d000068E4sv000017AAsd000021DD* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv000017AAsd000021E9* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv000017AAsd00003971* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M) + +pci:v00001002d000068E4sv000017AAsd00003972* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 7370M) + +pci:v00001002d000068E4sv000017AAsd0000397A* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M/7370M) + +pci:v00001002d000068E4sv000017AAsd0000397B* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 6370M/7370M) + +pci:v00001002d000068E4sv000017AAsd0000397F* + ID_MODEL_FROM_DATABASE=Robson CE [Radeon HD 6370M/7370M] (Radeon HD 7370M) + +pci:v00001002d000068E5* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] + +pci:v00001002d000068E5sv00001179sd0000FD3C* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD50* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD52* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD63* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD65* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD73* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FD75* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FDD0* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FDD2* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FDEA* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv00001179sd0000FDF8* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Radeon HD 6330M) + +pci:v00001002d000068E5sv0000148Csd00005450* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Cedar [Radeon HD 5450]) + +pci:v00001002d000068E5sv0000148Csd00006350* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Cedar [Radeon HD 6350]) + +pci:v00001002d000068E5sv0000148Csd00007350* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Cedar [Radeon HD 7350]) + +pci:v00001002d000068E5sv0000148Csd00008350* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Cedar [Radeon HD 8350]) + +pci:v00001002d000068E5sv00001545sd00007350* + ID_MODEL_FROM_DATABASE=Robson LE [Radeon HD 6330M] (Cedar [Radeon HD 7350]) + +pci:v00001002d000068E8* + ID_MODEL_FROM_DATABASE=Cedar + +pci:v00001002d000068E9* + ID_MODEL_FROM_DATABASE=Cedar [ATI FirePro (FireGL) Graphics Adapter] + +pci:v00001002d000068F1* + ID_MODEL_FROM_DATABASE=Cedar GL [FirePro 2460] + +pci:v00001002d000068F2* + ID_MODEL_FROM_DATABASE=Cedar GL [FirePro 2270] + +pci:v00001002d000068F8* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7300 Series] + +pci:v00001002d000068F9* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] + +pci:v00001002d000068F9sv00001019sd00000001* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001019sd00000002* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001019sd00000019* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001025sd00000518* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001025sd00000519* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (XPS 8300) + +pci:v00001002d000068F9sv00001028sd00002126* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000103Csd00002126* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000103Csd00002AAC* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv0000103Csd00002AAE* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv0000103Csd00003580* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001043sd00000386* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001043sd000003C2* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (EAH5450 SILENT/DI/512MD2 (LP)) + +pci:v00001002d000068F9sv00001462sd00002130* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002131* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002133* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001462sd00002180* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002181* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002182* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001462sd00002183* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001462sd00002230* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002231* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001462sd00002495* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000148Csd00003001* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5530/6250) + +pci:v00001002d000068F9sv0000148Csd00003002* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6290) + +pci:v00001002d000068F9sv0000148Csd00003003* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6230) + +pci:v00001002d000068F9sv0000148Csd00003004* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000148Csd00007350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 7350) + +pci:v00001002d000068F9sv0000148Csd00008350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 8350) + +pci:v00001002d000068F9sv00001545sd00007350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 7350) + +pci:v00001002d000068F9sv00001642sd00003983* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001642sd00003984* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001642sd00003987* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv00001642sd00003997* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001642sd00003A05* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001642sd00003B31* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350A) + +pci:v00001002d000068F9sv00001682sd00003270* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 7350) + +pci:v00001002d000068F9sv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6230) + +pci:v00001002d000068F9sv0000174Bsd00003987* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000174Bsd00005470* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5470) + +pci:v00001002d000068F9sv0000174Bsd00005490* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5490) + +pci:v00001002d000068F9sv0000174Bsd00005530* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5530) + +pci:v00001002d000068F9sv0000174Bsd00006230* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6230) + +pci:v00001002d000068F9sv0000174Bsd00006250* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6250) + +pci:v00001002d000068F9sv0000174Bsd00006290* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6290) + +pci:v00001002d000068F9sv0000174Bsd00006350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068F9sv0000174Bsd00007350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 7350) + +pci:v00001002d000068F9sv0000174Bsd00008350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 8350) + +pci:v00001002d000068F9sv0000174Bsd0000E127* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv0000174Bsd0000E145* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv0000174Bsd0000E153* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5470) + +pci:v00001002d000068F9sv00001787sd00003001* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5530) + +pci:v00001002d000068F9sv00001787sd00003002* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5490) + +pci:v00001002d000068F9sv000017AAsd00003602* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv000017AAsd00003603* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv000017AAsd0000360F* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv000017AAsd00003619* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5450) + +pci:v00001002d000068F9sv000017AFsd00003000* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6250) + +pci:v00001002d000068F9sv000017AFsd00003001* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6230) + +pci:v00001002d000068F9sv000017AFsd00003002* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6290) + +pci:v00001002d000068F9sv000017AFsd00003011* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5470) + +pci:v00001002d000068F9sv000017AFsd00003012* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5490) + +pci:v00001002d000068F9sv000017AFsd00003013* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 5470) + +pci:v00001002d000068F9sv000017AFsd00003014* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 5000/6000/7350/8350 Series] (Radeon HD 6350) + +pci:v00001002d000068FA* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] + +pci:v00001002d000068FAsv00001019sd00000019* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001019sd00000021* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001019sd00000022* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001019sd00000026* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 8350) + +pci:v00001002d000068FAsv0000103Csd00002ADF* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350A) + +pci:v00001002d000068FAsv0000103Csd00002AE8* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350A) + +pci:v00001002d000068FAsv00001043sd00008350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 8350) + +pci:v00001002d000068FAsv00001462sd00002128* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001462sd00002184* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001462sd00002186* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001462sd00002495* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001462sd0000B490* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv00001642sd00003985* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv0000174Bsd00003510* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 8350) + +pci:v00001002d000068FAsv0000174Bsd00003521* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon R5 220) + +pci:v00001002d000068FAsv0000174Bsd00003522* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon R5 220) + +pci:v00001002d000068FAsv0000174Bsd00007350* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv0000174Bsd00008153* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 8350) + +pci:v00001002d000068FAsv0000174Bsd0000E127* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv0000174Bsd0000E153* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv0000174Bsd0000E180* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FAsv000017AFsd00003015* + ID_MODEL_FROM_DATABASE=Cedar [Radeon HD 7350/8350 / R5 220] (Radeon HD 7350) + +pci:v00001002d000068FE* + ID_MODEL_FROM_DATABASE=Cedar LE + +pci:v00001002d00006900* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] + +pci:v00001002d00006900sv00001025sd00001056* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360 / R8 M365DX) + +pci:v00001002d00006900sv00001028sd00000640* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260/M265) + +pci:v00001002d00006900sv00001028sd00000643* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260/M265) + +pci:v00001002d00006900sv00001028sd0000067F* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv00001028sd0000130A* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd00002263* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd00002269* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd000022C6* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd000022C8* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd0000808C* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv0000103Csd00008099* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv0000103Csd000080B5* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv0000103Csd000080B9* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv0000103Csd0000811C* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M340) + +pci:v00001002d00006900sv000010CFsd00001906* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv00001170sd00009979* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv00001179sd0000F903* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv00001179sd0000F922* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv00001179sd0000F923* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv00001179sd0000F934* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006900sv000017AAsd00003822* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv000017AAsd00003824* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M360) + +pci:v00001002d00006900sv000017AAsd00005021* + ID_MODEL_FROM_DATABASE=Topaz XT [Radeon R7 M260/M265 / M340/M360] (Radeon R7 M260) + +pci:v00001002d00006901* + ID_MODEL_FROM_DATABASE=Topaz PRO [Radeon R5 M255] + +pci:v00001002d00006901sv0000103Csd00001318* + ID_MODEL_FROM_DATABASE=Topaz PRO [Radeon R5 M255] (Radeon R6 M255DX) + +pci:v00001002d00006921* + ID_MODEL_FROM_DATABASE=Amethyst XT [Radeon R9 M295X] + +pci:v00001002d00006929* + ID_MODEL_FROM_DATABASE=Tonga XT GL [FirePro S7150] + +pci:v00001002d0000692B* + ID_MODEL_FROM_DATABASE=Tonga PRO GL [FirePro W7100] + +pci:v00001002d0000692F* + ID_MODEL_FROM_DATABASE=Tonga XTV GL [FirePro S7150V] + +pci:v00001002d00006938* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] + +pci:v00001002d00006938sv00001043sd000004F5* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006938sv00001043sd000004F7* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006938sv0000106Bsd0000013A* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 M295X Mac Edition) + +pci:v00001002d00006938sv00001458sd000022C8* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006938sv0000148Csd00002350* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006938sv00001682sd00009385* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006938sv0000174Bsd0000E308* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X Nitro 4G D5) + +pci:v00001002d00006938sv000017AFsd00002006* + ID_MODEL_FROM_DATABASE=Tonga XT / Amethyst XT [Radeon R9 380X / R9 M295X] (Radeon R9 380X) + +pci:v00001002d00006939* + ID_MODEL_FROM_DATABASE=Tonga PRO [Radeon R9 285/380] + +pci:v00001002d00006939sv0000148Csd00009380* + ID_MODEL_FROM_DATABASE=Tonga PRO [Radeon R9 285/380] (Radeon R9 380) + +pci:v00001002d00006939sv0000174Bsd0000E308* + ID_MODEL_FROM_DATABASE=Tonga PRO [Radeon R9 285/380] (Radeon R9 380 Nitro 4G D5) + +pci:v00001002d0000700F* + ID_MODEL_FROM_DATABASE=RS100 AGP Bridge + +pci:v00001002d00007010* + ID_MODEL_FROM_DATABASE=RS200/RS250 AGP Bridge + +pci:v00001002d00007100* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 XT] + +pci:v00001002d00007101* + ID_MODEL_FROM_DATABASE=R520/M58 [Mobility Radeon X1800 XT] + +pci:v00001002d00007102* + ID_MODEL_FROM_DATABASE=R520/M58 [Mobility Radeon X1800] + +pci:v00001002d00007104* + ID_MODEL_FROM_DATABASE=R520 GL [FireGL V7200] + +pci:v00001002d00007109* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 XL] + +pci:v00001002d00007109sv00001002sd00000322* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 XL] (All-in-Wonder X1800XL) + +pci:v00001002d00007109sv00001002sd00000D02* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 XL] (Radeon X1800 CrossFire Edition) + +pci:v00001002d0000710A* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 GTO] + +pci:v00001002d0000710Asv00001002sd00000B12* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 GTO] (Radeon X1800 GTO²) + +pci:v00001002d0000710B* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800 GTO] + +pci:v00001002d00007120* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800] (Secondary) + +pci:v00001002d00007124* + ID_MODEL_FROM_DATABASE=R520 GL [FireGL V7200] (Secondary) + +pci:v00001002d00007129* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800] (Secondary) + +pci:v00001002d00007129sv00001002sd00000323* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800] (Secondary) (All-In-Wonder X1800 XL (Secondary)) + +pci:v00001002d00007129sv00001002sd00000D03* + ID_MODEL_FROM_DATABASE=R520 [Radeon X1800] (Secondary) (Radeon X1800 CrossFire Edition (Secondary)) + +pci:v00001002d00007140* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550/X1600 Series] + +pci:v00001002d00007142* + ID_MODEL_FROM_DATABASE=RV515 PRO [Radeon X1300/X1550 Series] + +pci:v00001002d00007142sv00001002sd00000322* + ID_MODEL_FROM_DATABASE=RV515 PRO [Radeon X1300/X1550 Series] (All-in-Wonder 2006 PCI-E Edition) + +pci:v00001002d00007142sv00001043sd00000142* + ID_MODEL_FROM_DATABASE=RV515 PRO [Radeon X1300/X1550 Series] (EAX1300PRO/TD/256M) + +pci:v00001002d00007143* + ID_MODEL_FROM_DATABASE=RV505 [Radeon X1300/X1550 Series] + +pci:v00001002d00007145* + ID_MODEL_FROM_DATABASE=RV515/M54 [Mobility Radeon X1400] + +pci:v00001002d00007145sv000017AAsd00002006* + ID_MODEL_FROM_DATABASE=RV515/M54 [Mobility Radeon X1400] (Thinkpad T60 model 2007) + +pci:v00001002d00007146* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550] + +pci:v00001002d00007146sv00001002sd00000322* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550] (All-in-Wonder 2006 PCI-E Edition) + +pci:v00001002d00007146sv00001545sd00001996* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550] (Radeon X1300 512MB PCI-e) + +pci:v00001002d00007147* + ID_MODEL_FROM_DATABASE=RV505 [Radeon X1550 64-bit] + +pci:v00001002d00007149* + ID_MODEL_FROM_DATABASE=RV515/M52 [Mobility Radeon X1300] + +pci:v00001002d0000714A* + ID_MODEL_FROM_DATABASE=RV515/M52 [Mobility Radeon X1300] + +pci:v00001002d00007152* + ID_MODEL_FROM_DATABASE=RV515 GL [FireGL V3300] + +pci:v00001002d00007153* + ID_MODEL_FROM_DATABASE=RV515 GL [FireGL V3350] + +pci:v00001002d0000715F* + ID_MODEL_FROM_DATABASE=RV505 CE [Radeon X1550 64-bit] + +pci:v00001002d00007162* + ID_MODEL_FROM_DATABASE=RV515 PRO [Radeon X1300/X1550 Series] (Secondary) + +pci:v00001002d00007162sv00001002sd00000323* + ID_MODEL_FROM_DATABASE=RV515 PRO [Radeon X1300/X1550 Series] (Secondary) (All-in-Wonder 2006 PCI-E Edition (Secondary)) + +pci:v00001002d00007163* + ID_MODEL_FROM_DATABASE=RV505 [Radeon X1550 Series] (Secondary) + +pci:v00001002d00007166* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550 Series] (Secondary) + +pci:v00001002d00007166sv00001002sd00000323* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550 Series] (Secondary) (All-in-Wonder 2006 PCI-E Edition (Secondary)) + +pci:v00001002d00007166sv00001545sd00001997* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1300/X1550 Series] (Secondary) (Radeon X1300 512MB PCI-e (Secondary)) + +pci:v00001002d00007167* + ID_MODEL_FROM_DATABASE=RV515 [Radeon X1550 64-bit] (Secondary) + +pci:v00001002d00007172* + ID_MODEL_FROM_DATABASE=RV515 GL [FireGL V3300] (Secondary) + +pci:v00001002d00007173* + ID_MODEL_FROM_DATABASE=RV515 GL [FireGL V3350] (Secondary) + +pci:v00001002d00007181* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1600/X1650 Series] + +pci:v00001002d00007183* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1300/X1550 Series] + +pci:v00001002d00007186* + ID_MODEL_FROM_DATABASE=RV516/M64 [Mobility Radeon X1450] + +pci:v00001002d00007187* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1300/X1550 Series] + +pci:v00001002d00007188* + ID_MODEL_FROM_DATABASE=RV516/M64-S [Mobility Radeon X2300] + +pci:v00001002d00007188sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=RV516/M64-S [Mobility Radeon X2300] (6910p) + +pci:v00001002d0000718A* + ID_MODEL_FROM_DATABASE=RV516/M64 [Mobility Radeon X2300] + +pci:v00001002d0000718B* + ID_MODEL_FROM_DATABASE=RV516/M62 [Mobility Radeon X1350] + +pci:v00001002d0000718Bsv00001071sd00008209* + ID_MODEL_FROM_DATABASE=RV516/M62 [Mobility Radeon X1350] (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00001002d0000718C* + ID_MODEL_FROM_DATABASE=RV516/M62-CSP64 [Mobility Radeon X1350] + +pci:v00001002d0000718D* + ID_MODEL_FROM_DATABASE=RV516/M64-CSP128 [Mobility Radeon X1450] + +pci:v00001002d00007193* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1550 Series] + +pci:v00001002d00007196* + ID_MODEL_FROM_DATABASE=RV516/M62-S [Mobility Radeon X1350] + +pci:v00001002d0000719B* + ID_MODEL_FROM_DATABASE=RV516 GL [FireMV 2250] + +pci:v00001002d0000719F* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1550 Series] + +pci:v00001002d000071A0* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1300/X1550 Series] (Secondary) + +pci:v00001002d000071A1* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1600/X1650 Series] (Secondary) + +pci:v00001002d000071A3* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1300/X1550 Series] (Secondary) + +pci:v00001002d000071A7* + ID_MODEL_FROM_DATABASE=RV516 [Radeon X1300/X1550 Series] (Secondary) + +pci:v00001002d000071BB* + ID_MODEL_FROM_DATABASE=RV516 GL [FireMV 2250] (Secondary) + +pci:v00001002d000071C0* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600 XT/X1650 GTO] + +pci:v00001002d000071C0sv00001002sd0000E160* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600 XT/X1650 GTO] (Radeon X1650 GTO) + +pci:v00001002d000071C0sv0000174Bsd0000E160* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600 XT/X1650 GTO] (Radeon X1650 GTO) + +pci:v00001002d000071C1* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] + +pci:v00001002d000071C1sv0000174Bsd00000880* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (Radeon X1700 FSC) + +pci:v00001002d000071C2* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600 PRO] + +pci:v00001002d000071C4* + ID_MODEL_FROM_DATABASE=RV530/M56 GL [Mobility FireGL V5200] + +pci:v00001002d000071C4sv000017AAsd00002007* + ID_MODEL_FROM_DATABASE=RV530/M56 GL [Mobility FireGL V5200] (ThinkPad T60p) + +pci:v00001002d000071C5* + ID_MODEL_FROM_DATABASE=RV530/M56-P [Mobility Radeon X1600] + +pci:v00001002d000071C5sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=RV530/M56-P [Mobility Radeon X1600] (Compaq nx9420 Notebook) + +pci:v00001002d000071C5sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=RV530/M56-P [Mobility Radeon X1600] (Compaq NW8440) + +pci:v00001002d000071C5sv00001043sd000010B2* + ID_MODEL_FROM_DATABASE=RV530/M56-P [Mobility Radeon X1600] (A6J-Q008) + +pci:v00001002d000071C5sv0000106Bsd00000080* + ID_MODEL_FROM_DATABASE=RV530/M56-P [Mobility Radeon X1600] (MacBook Pro) + +pci:v00001002d000071C6* + ID_MODEL_FROM_DATABASE=RV530LE [Radeon X1600/X1650 PRO] + +pci:v00001002d000071C7* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] + +pci:v00001002d000071C7sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (PowerColor X1650 PRO AGP) + +pci:v00001002d000071CE* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1300 XT/X1600 PRO] + +pci:v00001002d000071D2* + ID_MODEL_FROM_DATABASE=RV530 GL [FireGL V3400] + +pci:v00001002d000071D4* + ID_MODEL_FROM_DATABASE=RV530/M66 GL [Mobility FireGL V5250] + +pci:v00001002d000071D5* + ID_MODEL_FROM_DATABASE=RV530/M66-P [Mobility Radeon X1700] + +pci:v00001002d000071D6* + ID_MODEL_FROM_DATABASE=RV530/M66-XT [Mobility Radeon X1700] + +pci:v00001002d000071DE* + ID_MODEL_FROM_DATABASE=RV530/M66 [Mobility Radeon X1700/X2500] + +pci:v00001002d000071E0* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600] (Secondary) + +pci:v00001002d000071E0sv0000174Bsd0000E161* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600] (Secondary) (Radeon X1600 GTO (Secondary)) + +pci:v00001002d000071E1* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (Secondary) + +pci:v00001002d000071E1sv0000174Bsd00000881* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (Secondary) (Radeon X1700 FSC (Secondary)) + +pci:v00001002d000071E2* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1600] (Secondary) + +pci:v00001002d000071E6* + ID_MODEL_FROM_DATABASE=RV530 [Radeon X1650] (Secondary) + +pci:v00001002d000071E7* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (Secondary) + +pci:v00001002d000071E7sv00001787sd00003001* + ID_MODEL_FROM_DATABASE=RV535 [Radeon X1650 PRO] (Secondary) (Radeon X1650 PRO AGP) + +pci:v00001002d000071F2* + ID_MODEL_FROM_DATABASE=RV530 GL [FireGL V3400] (Secondary) + +pci:v00001002d00007210* + ID_MODEL_FROM_DATABASE=RV550/M71 [Mobility Radeon HD 2300] + +pci:v00001002d00007211* + ID_MODEL_FROM_DATABASE=RV550/M71 [Mobility Radeon X2300 HD] + +pci:v00001002d00007240* + ID_MODEL_FROM_DATABASE=R580+ [Radeon X1950 XTX] + +pci:v00001002d00007240sv00001002sd00000D02* + ID_MODEL_FROM_DATABASE=R580+ [Radeon X1950 XTX] (Radeon X1950 CrossFire Edition) + +pci:v00001002d00007244* + ID_MODEL_FROM_DATABASE=R580+ [Radeon X1950 XT] + +pci:v00001002d00007248* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1950] + +pci:v00001002d00007249* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] + +pci:v00001002d00007249sv00001002sd00000412* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] (All-In-Wonder X1900) + +pci:v00001002d00007249sv00001002sd00000B12* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] (Radeon X1900 XT/XTX) + +pci:v00001002d00007249sv00001002sd00000D02* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] (Radeon X1900 CrossFire Edition) + +pci:v00001002d00007249sv00001043sd00000160* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] (Radeon X1900 XTX 512 MB GDDR3) + +pci:v00001002d0000724B* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 GT] + +pci:v00001002d0000724Bsv00001002sd00000B12* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 GT] (Radeon X1900 (Primary)) + +pci:v00001002d0000724Bsv00001002sd00000B13* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 GT] (Radeon X1900 (Secondary)) + +pci:v00001002d0000724E* + ID_MODEL_FROM_DATABASE=R580 GL [FireGL V7350] + +pci:v00001002d00007269* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 XT] (Secondary) + +pci:v00001002d0000726B* + ID_MODEL_FROM_DATABASE=R580 [Radeon X1900 GT] (Secondary) + +pci:v00001002d0000726E* + ID_MODEL_FROM_DATABASE=R580 [AMD Stream Processor] (Secondary) + +pci:v00001002d00007280* + ID_MODEL_FROM_DATABASE=RV570 [Radeon X1950 PRO] + +pci:v00001002d00007288* + ID_MODEL_FROM_DATABASE=RV570 [Radeon X1950 GT] + +pci:v00001002d00007291* + ID_MODEL_FROM_DATABASE=RV560 [Radeon X1650 XT] + +pci:v00001002d00007291sv00001462sd00000810* + ID_MODEL_FROM_DATABASE=RV560 [Radeon X1650 XT] (Radeon X1700 SE) + +pci:v00001002d00007293* + ID_MODEL_FROM_DATABASE=RV560 [Radeon X1650 GT] + +pci:v00001002d000072A0* + ID_MODEL_FROM_DATABASE=RV570 [Radeon X1950 PRO] (Secondary) + +pci:v00001002d000072A8* + ID_MODEL_FROM_DATABASE=RV570 [Radeon X1950 GT] (Secondary) + +pci:v00001002d000072B1* + ID_MODEL_FROM_DATABASE=RV560 [Radeon X1650 XT] (Secondary) + +pci:v00001002d000072B3* + ID_MODEL_FROM_DATABASE=RV560 [Radeon X1650 GT] (Secondary) + +pci:v00001002d00007300* + ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] + +pci:v00001002d00007300sv00001002sd00000B36* + ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] (Radeon R9 FURY X / NANO) + +pci:v00001002d00007300sv00001043sd0000049E* + ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] (Radeon R9 FURY) + +pci:v00001002d00007300sv00001043sd000004A0* + ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] (Radeon R9 FURY X) + +pci:v00001002d00007300sv0000174Bsd0000E329* + ID_MODEL_FROM_DATABASE=Fiji [Radeon R9 FURY / NANO Series] (Radeon R9 FURY) + +pci:v00001002d00007833* + ID_MODEL_FROM_DATABASE=RS350 Host Bridge + +pci:v00001002d00007834* + ID_MODEL_FROM_DATABASE=RS350 [Radeon 9100 PRO/XT IGP] + +pci:v00001002d00007835* + ID_MODEL_FROM_DATABASE=RS350M [Mobility Radeon 9000 IGP] + +pci:v00001002d00007838* + ID_MODEL_FROM_DATABASE=RS350 AGP Bridge + +pci:v00001002d00007910* + ID_MODEL_FROM_DATABASE=RS690 Host Bridge + +pci:v00001002d00007910sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=RS690 Host Bridge (Satellite P305D-S8995E) + +pci:v00001002d00007910sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=RS690 Host Bridge (KI690-AM2 Motherboard) + +pci:v00001002d00007911* + ID_MODEL_FROM_DATABASE=RS690 Host Bridge + +pci:v00001002d00007912* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (Internal gfx) + +pci:v00001002d00007913* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Graphics Port 0) + +pci:v00001002d00007915* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Port 1) + +pci:v00001002d00007916* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Port 2) + +pci:v00001002d00007917* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Port 3) + +pci:v00001002d00007917sv00001002sd00007910* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Port 3) (RS690 PCI to PCI Bridge) + +pci:v00001002d00007919* + ID_MODEL_FROM_DATABASE=RS690 HDMI Audio [Radeon Xpress 1200 Series] + +pci:v00001002d00007919sv00001179sd00007919* + ID_MODEL_FROM_DATABASE=RS690 HDMI Audio [Radeon Xpress 1200 Series] (Satellite P305D-S8995E) + +pci:v00001002d00007919sv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=RS690 HDMI Audio [Radeon Xpress 1200 Series] (KI690-AM2 Motherboard) + +pci:v00001002d0000791E* + ID_MODEL_FROM_DATABASE=RS690 [Radeon X1200] + +pci:v00001002d0000791Esv00001462sd00007327* + ID_MODEL_FROM_DATABASE=RS690 [Radeon X1200] (K9AG Neo2) + +pci:v00001002d0000791Esv000017F2sd00005000* + ID_MODEL_FROM_DATABASE=RS690 [Radeon X1200] (KI690-AM2 Motherboard) + +pci:v00001002d0000791F* + ID_MODEL_FROM_DATABASE=RS690M [Radeon Xpress 1200/1250/1270] + +pci:v00001002d0000791Fsv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=RS690M [Radeon Xpress 1200/1250/1270] (Satellite P305D-S8995E) + +pci:v00001002d00007930* + ID_MODEL_FROM_DATABASE=RS600 Host Bridge + +pci:v00001002d00007932* + ID_MODEL_FROM_DATABASE=RS600 PCI to PCI Bridge (Internal gfx) + +pci:v00001002d00007933* + ID_MODEL_FROM_DATABASE=RS600 PCI to PCI Bridge (PCI Express Graphics Port 0) + +pci:v00001002d00007935* + ID_MODEL_FROM_DATABASE=RS600 PCI to PCI Bridge (PCI Express Port 1) + +pci:v00001002d00007936* + ID_MODEL_FROM_DATABASE=RS600 PCI to PCI Bridge (PCI Express Port 2) + +pci:v00001002d00007937* + ID_MODEL_FROM_DATABASE=RS690 PCI to PCI Bridge (PCI Express Port 3) + +pci:v00001002d0000793B* + ID_MODEL_FROM_DATABASE=RS600 HDMI Audio [Radeon Xpress 1250] + +pci:v00001002d0000793F* + ID_MODEL_FROM_DATABASE=RS690M [Radeon Xpress 1200/1250/1270] (Secondary) + +pci:v00001002d00007941* + ID_MODEL_FROM_DATABASE=RS600 [Radeon Xpress 1250] + +pci:v00001002d00007942* + ID_MODEL_FROM_DATABASE=RS600M [Radeon Xpress 1250] + +pci:v00001002d0000796E* + ID_MODEL_FROM_DATABASE=RS740 [Radeon 2100] + +pci:v00001002d00009400* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 PRO/XT] + +pci:v00001002d00009400sv00001002sd00002552* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 PRO/XT] (Radeon HD 2900 XT) + +pci:v00001002d00009400sv00001002sd00003000* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 PRO/XT] (Radeon HD 2900 PRO) + +pci:v00001002d00009400sv00001002sd00003142* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 PRO/XT] (HIS Radeon HD 2900XT 512MB GDDR3 VIVO PCIe) + +pci:v00001002d00009401* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 XT] + +pci:v00001002d00009403* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 PRO] + +pci:v00001002d00009405* + ID_MODEL_FROM_DATABASE=R600 [Radeon HD 2900 GT] + +pci:v00001002d0000940A* + ID_MODEL_FROM_DATABASE=R600 GL [FireGL V8650] + +pci:v00001002d0000940B* + ID_MODEL_FROM_DATABASE=R600 GL [FireGL V8600] + +pci:v00001002d0000940F* + ID_MODEL_FROM_DATABASE=R600 GL [FireGL V7600] + +pci:v00001002d00009440* + ID_MODEL_FROM_DATABASE=RV770 [Radeon HD 4870] + +pci:v00001002d00009441* + ID_MODEL_FROM_DATABASE=R700 [Radeon HD 4870 X2] + +pci:v00001002d00009442* + ID_MODEL_FROM_DATABASE=RV770 [Radeon HD 4850] + +pci:v00001002d00009442sv00001002sd00000502* + ID_MODEL_FROM_DATABASE=RV770 [Radeon HD 4850] (MSI Radeon HD 4850 512MB GDDR3) + +pci:v00001002d00009442sv0000174Bsd0000E810* + ID_MODEL_FROM_DATABASE=RV770 [Radeon HD 4850] (Radeon HD 4850 512MB GDDR3) + +pci:v00001002d00009443* + ID_MODEL_FROM_DATABASE=R700 [Radeon HD 4850 X2] + +pci:v00001002d00009444* + ID_MODEL_FROM_DATABASE=RV770 GL [FirePro V8750] + +pci:v00001002d00009446* + ID_MODEL_FROM_DATABASE=RV770 GL [FirePro V7760] + +pci:v00001002d0000944A* + ID_MODEL_FROM_DATABASE=RV770/M98L [Mobility Radeon HD 4850] + +pci:v00001002d0000944B* + ID_MODEL_FROM_DATABASE=RV770/M98 [Mobility Radeon HD 4850 X2] + +pci:v00001002d0000944C* + ID_MODEL_FROM_DATABASE=RV770 LE [Radeon HD 4830] + +pci:v00001002d0000944E* + ID_MODEL_FROM_DATABASE=RV770 CE [Radeon HD 4710] + +pci:v00001002d0000944Esv0000174Bsd00003261* + ID_MODEL_FROM_DATABASE=RV770 CE [Radeon HD 4710] (Radeon HD 4810) + +pci:v00001002d00009450* + ID_MODEL_FROM_DATABASE=RV770 GL [FireStream 9270] + +pci:v00001002d00009452* + ID_MODEL_FROM_DATABASE=RV770 GL [FireStream 9250] + +pci:v00001002d00009456* + ID_MODEL_FROM_DATABASE=RV770 GL [FirePro V8700] + +pci:v00001002d0000945A* + ID_MODEL_FROM_DATABASE=RV770/M98-XT [Mobility Radeon HD 4870] + +pci:v00001002d00009460* + ID_MODEL_FROM_DATABASE=RV790 [Radeon HD 4890] + +pci:v00001002d00009462* + ID_MODEL_FROM_DATABASE=RV790 [Radeon HD 4860] + +pci:v00001002d0000946A* + ID_MODEL_FROM_DATABASE=RV770 GL [FirePro M7750] + +pci:v00001002d00009480* + ID_MODEL_FROM_DATABASE=RV730/M96 [Mobility Radeon HD 4650/5165] + +pci:v00001002d00009480sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=RV730/M96 [Mobility Radeon HD 4650/5165] (Mobility Radeon HD 4650 [dv6-1190en]) + +pci:v00001002d00009488* + ID_MODEL_FROM_DATABASE=RV730/M96-XT [Mobility Radeon HD 4670] + +pci:v00001002d00009489* + ID_MODEL_FROM_DATABASE=RV730/M96 GL [Mobility FireGL V5725] + +pci:v00001002d00009490* + ID_MODEL_FROM_DATABASE=RV730 XT [Radeon HD 4670] + +pci:v00001002d00009490sv0000174Bsd0000E880* + ID_MODEL_FROM_DATABASE=RV730 XT [Radeon HD 4670] (Radeon HD 4670 512MB GDDR3 Dual DVI-I/TVO) + +pci:v00001002d00009491* + ID_MODEL_FROM_DATABASE=RV730/M96-CSP [Radeon E4690] + +pci:v00001002d00009495* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] + +pci:v00001002d00009495sv00001002sd00000028* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] (Radeon HD 4650/4670 AGP) + +pci:v00001002d00009495sv00001092sd00000028* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] (Radeon HD 4670 AGP 512MB DDR2) + +pci:v00001002d00009495sv00001458sd00000028* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] (Radeon HD 4650 AGP) + +pci:v00001002d00009495sv00001682sd00000028* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] (Radeon HD 4650 AGP) + +pci:v00001002d00009495sv0000174Bsd00000028* + ID_MODEL_FROM_DATABASE=RV730 [Radeon HD 4600 AGP Series] (Radeon HD 4650 AGP DDR2) + +pci:v00001002d00009498* + ID_MODEL_FROM_DATABASE=RV730 PRO [Radeon HD 4650] + +pci:v00001002d0000949C* + ID_MODEL_FROM_DATABASE=RV730 GL [FirePro V7750] + +pci:v00001002d0000949E* + ID_MODEL_FROM_DATABASE=RV730 GL [FirePro V5700] + +pci:v00001002d0000949F* + ID_MODEL_FROM_DATABASE=RV730 GL [FirePro V3750] + +pci:v00001002d000094A0* + ID_MODEL_FROM_DATABASE=RV740/M97 [Mobility Radeon HD 4830] + +pci:v00001002d000094A1* + ID_MODEL_FROM_DATABASE=RV740/M97-XT [Mobility Radeon HD 4860] + +pci:v00001002d000094A3* + ID_MODEL_FROM_DATABASE=RV740/M97 GL [FirePro M7740] + +pci:v00001002d000094B3* + ID_MODEL_FROM_DATABASE=RV740 PRO [Radeon HD 4770] + +pci:v00001002d000094B4* + ID_MODEL_FROM_DATABASE=RV740 PRO [Radeon HD 4750] + +pci:v00001002d000094C1* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO/XT] + +pci:v00001002d000094C1sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO/XT] (Optiplex 755) + +pci:v00001002d000094C1sv00001028sd00000D02* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO/XT] (Optiplex 755) + +pci:v00001002d000094C3* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO] + +pci:v00001002d000094C3sv00001028sd00000302* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO] (Radeon HD 2400 Pro) + +pci:v00001002d000094C3sv0000174Bsd0000E400* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO] (Radeon HD 2400 PRO) + +pci:v00001002d000094C3sv000018BCsd00003550* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 PRO] (Radeon HD 2400 PRO) + +pci:v00001002d000094C4* + ID_MODEL_FROM_DATABASE=RV610 LE [Radeon HD 2400 PRO AGP] + +pci:v00001002d000094C5* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2400 LE] + +pci:v00001002d000094C7* + ID_MODEL_FROM_DATABASE=RV610 [Radeon HD 2350] + +pci:v00001002d000094C8* + ID_MODEL_FROM_DATABASE=RV610/M74 [Mobility Radeon HD 2400 XT] + +pci:v00001002d000094C9* + ID_MODEL_FROM_DATABASE=RV610/M72-S [Mobility Radeon HD 2400] + +pci:v00001002d000094C9sv00001002sd000094C9* + ID_MODEL_FROM_DATABASE=RV610/M72-S [Mobility Radeon HD 2400] (Radeon HD2400) + +pci:v00001002d000094CB* + ID_MODEL_FROM_DATABASE=RV610 [Radeon E2400] + +pci:v00001002d000094CC* + ID_MODEL_FROM_DATABASE=RV610 LE [Radeon HD 2400 PRO PCI] + +pci:v00001002d00009500* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3850 X2] + +pci:v00001002d00009501* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3870] + +pci:v00001002d00009501sv0000174Bsd0000E620* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3870] (Radeon HD 3870) + +pci:v00001002d00009504* + ID_MODEL_FROM_DATABASE=RV670/M88 [Mobility Radeon HD 3850] + +pci:v00001002d00009505* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3690/3850] + +pci:v00001002d00009505sv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3690/3850] (Radeon HD 3850) + +pci:v00001002d00009505sv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3690/3850] (Radeon HD 3690/3850) + +pci:v00001002d00009505sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3690/3850] (Radeon HD 3690) + +pci:v00001002d00009506* + ID_MODEL_FROM_DATABASE=RV670/M88 [Mobility Radeon HD 3850 X2] + +pci:v00001002d00009507* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3830] + +pci:v00001002d00009508* + ID_MODEL_FROM_DATABASE=RV670/M88-XT [Mobility Radeon HD 3870] + +pci:v00001002d00009509* + ID_MODEL_FROM_DATABASE=RV670/M88 [Mobility Radeon HD 3870 X2] + +pci:v00001002d0000950F* + ID_MODEL_FROM_DATABASE=R680 [Radeon HD 3870 X2] + +pci:v00001002d00009511* + ID_MODEL_FROM_DATABASE=RV670 GL [FireGL V7700] + +pci:v00001002d00009513* + ID_MODEL_FROM_DATABASE=RV670 [Radeon HD 3850 X2] + +pci:v00001002d00009515* + ID_MODEL_FROM_DATABASE=RV670 PRO [Radeon HD 3850 AGP] + +pci:v00001002d00009519* + ID_MODEL_FROM_DATABASE=RV670 GL [FireStream 9170] + +pci:v00001002d00009540* + ID_MODEL_FROM_DATABASE=RV710 [Radeon HD 4550] + +pci:v00001002d0000954F* + ID_MODEL_FROM_DATABASE=RV710 [Radeon HD 4350/4550] + +pci:v00001002d0000954Fsv00001462sd00001618* + ID_MODEL_FROM_DATABASE=RV710 [Radeon HD 4350/4550] (R4350 MD512H (MS-V161)) + +pci:v00001002d00009552* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] + +pci:v00001002d00009552sv00001028sd00001103* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] (M92 [Mobility Radeon HD 4330]) + +pci:v00001002d00009552sv00001458sd000021AC* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] (Radeon HD 4350) + +pci:v00001002d00009552sv00001458sd000021ED* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] (Radeon HD 4550) + +pci:v00001002d00009552sv0000148Csd00003000* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] (Radeon HD 4350 Go! Green 512MB GDDR3) + +pci:v00001002d00009552sv0000174Bsd00003000* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330/4350/4550] (Radeon HD 4350/4550 HyperMemory DDR2) + +pci:v00001002d00009553* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] + +pci:v00001002d00009553sv00001025sd0000015E* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001025sd0000017D* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001025sd00000205* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001025sd00000206* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001025sd00000237* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001028sd000002BE* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001028sd000002E8* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4530) + +pci:v00001002d00009553sv0000103Csd00003624* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4530) + +pci:v00001002d00009553sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4530) + +pci:v00001002d00009553sv0000103Csd00003636* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4530) + +pci:v00001002d00009553sv00001043sd00001B32* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001043sd00001B42* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv0000104Dsd00009056* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Mobility Radeon HD 4570) + +pci:v00001002d00009553sv00001179sd0000FF82* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4530/4570/545v] (Satellite L505-13T GPU (Mobility Radeon HD 5145)) + +pci:v00001002d00009555* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4350/4550] + +pci:v00001002d00009555sv0000103Csd00001411* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4350/4550] (ProBook 4720s GPU (Mobility Radeon HD 4350)) + +pci:v00001002d00009557* + ID_MODEL_FROM_DATABASE=RV711 GL [FirePro RG220] + +pci:v00001002d0000955F* + ID_MODEL_FROM_DATABASE=RV710/M92 [Mobility Radeon HD 4330] + +pci:v00001002d00009580* + ID_MODEL_FROM_DATABASE=RV630 [Radeon HD 2600 PRO] + +pci:v00001002d00009581* + ID_MODEL_FROM_DATABASE=RV630/M76 [Mobility Radeon HD 2600] + +pci:v00001002d00009583* + ID_MODEL_FROM_DATABASE=RV630/M76 [Mobility Radeon HD 2600 XT/2700] + +pci:v00001002d00009583sv0000106Bsd00000083* + ID_MODEL_FROM_DATABASE=RV630/M76 [Mobility Radeon HD 2600 XT/2700] (iMac 7,1) + +pci:v00001002d00009583sv00001734sd00001107* + ID_MODEL_FROM_DATABASE=RV630/M76 [Mobility Radeon HD 2600 XT/2700] (Mobility Radeon HD 2700) + +pci:v00001002d00009586* + ID_MODEL_FROM_DATABASE=RV630 XT [Radeon HD 2600 XT AGP] + +pci:v00001002d00009587* + ID_MODEL_FROM_DATABASE=RV630 PRO [Radeon HD 2600 PRO AGP] + +pci:v00001002d00009588* + ID_MODEL_FROM_DATABASE=RV630 XT [Radeon HD 2600 XT] + +pci:v00001002d00009588sv00001458sd0000216C* + ID_MODEL_FROM_DATABASE=RV630 XT [Radeon HD 2600 XT] (Radeon HD 2600 XT, 256MB GDDR3, 2x DVI, TV-out, PCIe (GV-RX26T256H)) + +pci:v00001002d00009589* + ID_MODEL_FROM_DATABASE=RV630 PRO [Radeon HD 2600 PRO] + +pci:v00001002d00009589sv00001787sd00003000* + ID_MODEL_FROM_DATABASE=RV630 PRO [Radeon HD 2600 PRO] (Radeon HD 3610) + +pci:v00001002d0000958A* + ID_MODEL_FROM_DATABASE=RV630 [Radeon HD 2600 X2] + +pci:v00001002d0000958B* + ID_MODEL_FROM_DATABASE=RV630/M76 [Mobility Radeon HD 2600 XT] + +pci:v00001002d0000958C* + ID_MODEL_FROM_DATABASE=RV630 GL [FireGL V5600] + +pci:v00001002d0000958D* + ID_MODEL_FROM_DATABASE=RV630 GL [FireGL V3600] + +pci:v00001002d00009591* + ID_MODEL_FROM_DATABASE=RV635/M86 [Mobility Radeon HD 3650] + +pci:v00001002d00009591sv00001002sd00009591* + ID_MODEL_FROM_DATABASE=RV635/M86 [Mobility Radeon HD 3650] (Mobility Radeon HD 3650) + +pci:v00001002d00009593* + ID_MODEL_FROM_DATABASE=RV635/M86 [Mobility Radeon HD 3670] + +pci:v00001002d00009595* + ID_MODEL_FROM_DATABASE=RV635/M86 GL [Mobility FireGL V5700] + +pci:v00001002d00009596* + ID_MODEL_FROM_DATABASE=RV635 PRO [Radeon HD 3650 AGP] + +pci:v00001002d00009596sv00001043sd00000028* + ID_MODEL_FROM_DATABASE=RV635 PRO [Radeon HD 3650 AGP] (EAH3650 SILENT/HTDI/512M/A) + +pci:v00001002d00009597* + ID_MODEL_FROM_DATABASE=RV635 PRO [Radeon HD 3650 AGP] + +pci:v00001002d00009598* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] + +pci:v00001002d00009598sv00001002sd00009598* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] (Mobility Radeon HD 3600) + +pci:v00001002d00009598sv00001043sd000001D6* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] (EAH3650 Silent) + +pci:v00001002d00009598sv00001043sd00003001* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] (Radeon HD 4570) + +pci:v00001002d00009598sv0000174Bsd00003001* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] (Radeon HD 3750) + +pci:v00001002d00009598sv0000174Bsd00004580* + ID_MODEL_FROM_DATABASE=RV635 [Radeon HD 3650/3750/4570/4580] (RV635 PRO [Radeon HD 4580]) + +pci:v00001002d00009599* + ID_MODEL_FROM_DATABASE=RV635 PRO [Radeon HD 3650 AGP] + +pci:v00001002d000095C0* + ID_MODEL_FROM_DATABASE=RV620 PRO [Radeon HD 3470] + +pci:v00001002d000095C0sv00001002sd000095C0* + ID_MODEL_FROM_DATABASE=RV620 PRO [Radeon HD 3470] (Mobility Radeon HD 3470) + +pci:v00001002d000095C2* + ID_MODEL_FROM_DATABASE=RV620/M82 [Mobility Radeon HD 3410/3430] + +pci:v00001002d000095C4* + ID_MODEL_FROM_DATABASE=RV620/M82 [Mobility Radeon HD 3450/3470] + +pci:v00001002d000095C4sv00001002sd000095C4* + ID_MODEL_FROM_DATABASE=RV620/M82 [Mobility Radeon HD 3450/3470] (Mobility Radeon HD 3400) + +pci:v00001002d000095C5* + ID_MODEL_FROM_DATABASE=RV620 LE [Radeon HD 3450] + +pci:v00001002d000095C5sv00001028sd00000342* + ID_MODEL_FROM_DATABASE=RV620 LE [Radeon HD 3450] (OptiPlex 980) + +pci:v00001002d000095C6* + ID_MODEL_FROM_DATABASE=RV620 LE [Radeon HD 3450 AGP] + +pci:v00001002d000095C9* + ID_MODEL_FROM_DATABASE=RV620 LE [Radeon HD 3450 PCI] + +pci:v00001002d000095CC* + ID_MODEL_FROM_DATABASE=RV620 GL [FirePro V3700] + +pci:v00001002d000095CD* + ID_MODEL_FROM_DATABASE=RV620 [FirePro 2450] + +pci:v00001002d000095CF* + ID_MODEL_FROM_DATABASE=RV620 GL [FirePro 2260] + +pci:v00001002d0000960F* + ID_MODEL_FROM_DATABASE=RS780 HDMI Audio [Radeon (HD) 3000 Series] + +pci:v00001002d00009610* + ID_MODEL_FROM_DATABASE=RS780 [Radeon HD 3200] + +pci:v00001002d00009610sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=RS780 [Radeon HD 3200] (GA-MA78GM-S2H Motherboard) + +pci:v00001002d00009611* + ID_MODEL_FROM_DATABASE=RS780C [Radeon 3100] + +pci:v00001002d00009612* + ID_MODEL_FROM_DATABASE=RS780M [Mobility Radeon HD 3200] + +pci:v00001002d00009613* + ID_MODEL_FROM_DATABASE=RS780MC [Mobility Radeon HD 3100] + +pci:v00001002d00009614* + ID_MODEL_FROM_DATABASE=RS780D [Radeon HD 3300] + +pci:v00001002d00009616* + ID_MODEL_FROM_DATABASE=RS780L [Radeon 3000] + +pci:v00001002d00009640* + ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6550D] + +pci:v00001002d00009641* + ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6620G] + +pci:v00001002d00009642* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6370D] + +pci:v00001002d00009643* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6380G] + +pci:v00001002d00009644* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6410D] + +pci:v00001002d00009645* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6410D] + +pci:v00001002d00009647* + ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6520G] + +pci:v00001002d00009648* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6480G] + +pci:v00001002d00009649* + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6480G] + +pci:v00001002d0000964A* + ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6530D] + +pci:v00001002d0000964B* + ID_MODEL_FROM_DATABASE=Sumo + +pci:v00001002d0000964C* + ID_MODEL_FROM_DATABASE=Sumo + +pci:v00001002d0000964E* + ID_MODEL_FROM_DATABASE=Sumo + +pci:v00001002d0000964F* + ID_MODEL_FROM_DATABASE=Sumo + +pci:v00001002d0000970F* + ID_MODEL_FROM_DATABASE=RS880 HDMI Audio [Radeon HD 4200 Series] + +pci:v00001002d0000970Fsv00001019sd00002120* + ID_MODEL_FROM_DATABASE=RS880 HDMI Audio [Radeon HD 4200 Series] (A785GM-M) + +pci:v00001002d0000970Fsv00001043sd000083A2* + ID_MODEL_FROM_DATABASE=RS880 HDMI Audio [Radeon HD 4200 Series] (M4A785TD Motherboard) + +pci:v00001002d0000970Fsv00001043sd0000843E* + ID_MODEL_FROM_DATABASE=RS880 HDMI Audio [Radeon HD 4200 Series] (M5A88-V EVO) + +pci:v00001002d00009710* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4200] + +pci:v00001002d00009710sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4200] (A785GM-M) + +pci:v00001002d00009710sv00001043sd000083A2* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4200] (M4A785TD Motherboard) + +pci:v00001002d00009712* + ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4225/4250] + +pci:v00001002d00009713* + ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4100] + +pci:v00001002d00009714* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4290] + +pci:v00001002d00009715* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4250] + +pci:v00001002d00009715sv00001043sd0000843E* + ID_MODEL_FROM_DATABASE=RS880 [Radeon HD 4250] (M5A88-V EVO) + +pci:v00001002d00009802* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6310] + +pci:v00001002d00009802sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6310] (PURE Fusion Mini) + +pci:v00001002d00009803* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6310] + +pci:v00001002d00009804* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6250] + +pci:v00001002d00009805* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6250] + +pci:v00001002d00009806* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6320] + +pci:v00001002d00009807* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 6290] + +pci:v00001002d00009808* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 7340] + +pci:v00001002d00009809* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 7310] + +pci:v00001002d0000980A* + ID_MODEL_FROM_DATABASE=Wrestler [Radeon HD 7290] + +pci:v00001002d00009830* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8400 / R3 Series] + +pci:v00001002d00009831* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8400E] + +pci:v00001002d00009832* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330] + +pci:v00001002d00009833* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330E] + +pci:v00001002d00009834* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8210] + +pci:v00001002d00009835* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8310E] + +pci:v00001002d00009836* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8280 / R3 Series] + +pci:v00001002d00009837* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8280E] + +pci:v00001002d00009838* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8240 / R3 Series] + +pci:v00001002d00009839* + ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8180] + +pci:v00001002d0000983D* + ID_MODEL_FROM_DATABASE=Temash [Radeon HD 8250/8280G] + +pci:v00001002d00009840* + ID_MODEL_FROM_DATABASE=Kabini HDMI/DP Audio + +pci:v00001002d00009850* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R3 Graphics] + +pci:v00001002d00009851* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R4/R5 Graphics] + +pci:v00001002d00009852* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R2 Graphics] + +pci:v00001002d00009853* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R2 Graphics] + +pci:v00001002d00009854* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R3E Graphics] + +pci:v00001002d00009855* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R6 Graphics] + +pci:v00001002d00009856* + ID_MODEL_FROM_DATABASE=Mullins [Radeon R1E/R2E Graphics] + +pci:v00001002d00009857* + ID_MODEL_FROM_DATABASE=Mullins [Radeon APU XX-2200M with R2 Graphics] + +pci:v00001002d00009858* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d00009859* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985A* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985B* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985C* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985D* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985E* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d0000985F* + ID_MODEL_FROM_DATABASE=Mullins + +pci:v00001002d00009874* + ID_MODEL_FROM_DATABASE=Carrizo + +pci:v00001002d00009900* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7660G] + +pci:v00001002d00009900sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7660G] (Pavilion 17-e163sg Notebook PC) + +pci:v00001002d00009901* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7660D] + +pci:v00001002d00009902* + ID_MODEL_FROM_DATABASE=Trinity HDMI Audio Controller + +pci:v00001002d00009902sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=Trinity HDMI Audio Controller (ProBook 455 G1 Notebook) + +pci:v00001002d00009902sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=Trinity HDMI Audio Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001002d00009903* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7640G] + +pci:v00001002d00009903sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7640G] (ProBook 455 G1 Notebook) + +pci:v00001002d00009903sv0000103Csd00001952* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7640G] (ProBook 455 G1 Notebook) + +pci:v00001002d00009904* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7560D] + +pci:v00001002d00009905* + ID_MODEL_FROM_DATABASE=Trinity [FirePro A300 Series Graphics] + +pci:v00001002d00009906* + ID_MODEL_FROM_DATABASE=Trinity [FirePro A300 Series Graphics] + +pci:v00001002d00009907* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7620G] + +pci:v00001002d00009908* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7600G] + +pci:v00001002d00009909* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7500G] + +pci:v00001002d0000990A* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7500G] + +pci:v00001002d0000990B* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8650G] + +pci:v00001002d0000990C* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8670D] + +pci:v00001002d0000990D* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8550G] + +pci:v00001002d0000990E* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8570D] + +pci:v00001002d0000990F* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8610G] + +pci:v00001002d00009910* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7660G] + +pci:v00001002d00009913* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7640G] + +pci:v00001002d00009917* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7620G] + +pci:v00001002d00009918* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7600G] + +pci:v00001002d00009919* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7500G] + +pci:v00001002d00009920* + ID_MODEL_FROM_DATABASE=Liverpool Graphics + +pci:v00001002d00009921* + ID_MODEL_FROM_DATABASE=Liverpool HDMI/DP Audio Controller + +pci:v00001002d00009990* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7520G] + +pci:v00001002d00009991* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7540D] + +pci:v00001002d00009992* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7420G] + +pci:v00001002d00009993* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7480D] + +pci:v00001002d00009994* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7400G] + +pci:v00001002d00009995* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8450G] + +pci:v00001002d00009996* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8470D] + +pci:v00001002d00009997* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8350G] + +pci:v00001002d00009998* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8370D] + +pci:v00001002d00009999* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8510G] + +pci:v00001002d0000999A* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8410G] + +pci:v00001002d0000999B* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8310G] + +pci:v00001002d0000999C* + ID_MODEL_FROM_DATABASE=Richland + +pci:v00001002d0000999D* + ID_MODEL_FROM_DATABASE=Richland [Radeon HD 8550D] + +pci:v00001002d000099A0* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7520G] + +pci:v00001002d000099A2* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7420G] + +pci:v00001002d000099A4* + ID_MODEL_FROM_DATABASE=Trinity [Radeon HD 7400G] + +pci:v00001002d0000AA00* + ID_MODEL_FROM_DATABASE=R600 HDMI Audio [Radeon HD 2900 Series] + +pci:v00001002d0000AA08* + ID_MODEL_FROM_DATABASE=RV630 HDMI Audio [Radeon HD 2600 Series] + +pci:v00001002d0000AA10* + ID_MODEL_FROM_DATABASE=RV610 HDMI Audio [Radeon HD 2350/2400 Series] + +pci:v00001002d0000AA10sv0000174Bsd0000AA10* + ID_MODEL_FROM_DATABASE=RV610 HDMI Audio [Radeon HD 2350/2400 Series] (Radeon HD 2400 PRO) + +pci:v00001002d0000AA10sv000018BCsd0000AA10* + ID_MODEL_FROM_DATABASE=RV610 HDMI Audio [Radeon HD 2350/2400 Series] (Radeon HD 2400 PRO) + +pci:v00001002d0000AA18* + ID_MODEL_FROM_DATABASE=RV670/680 HDMI Audio [Radeon HD 3690/3800 Series] + +pci:v00001002d0000AA20* + ID_MODEL_FROM_DATABASE=RV635 HDMI Audio [Radeon HD 3600 Series] + +pci:v00001002d0000AA28* + ID_MODEL_FROM_DATABASE=RV620 HDMI Audio [Radeon HD 3400 Series] + +pci:v00001002d0000AA30* + ID_MODEL_FROM_DATABASE=RV770 HDMI Audio [Radeon HD 4850/4870] + +pci:v00001002d0000AA30sv0000174Bsd0000AA30* + ID_MODEL_FROM_DATABASE=RV770 HDMI Audio [Radeon HD 4850/4870] (Radeon HD 4850 512MB GDDR3 PCI-E Dual Slot Fansink) + +pci:v00001002d0000AA38* + ID_MODEL_FROM_DATABASE=RV710/730 HDMI Audio [Radeon HD 4000 series] + +pci:v00001002d0000AA38sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=RV710/730 HDMI Audio [Radeon HD 4000 series] (dv6-1190en) + +pci:v00001002d0000AA50* + ID_MODEL_FROM_DATABASE=Cypress HDMI Audio [Radeon HD 5800 Series] + +pci:v00001002d0000AA58* + ID_MODEL_FROM_DATABASE=Juniper HDMI Audio [Radeon HD 5700 Series] + +pci:v00001002d0000AA60* + ID_MODEL_FROM_DATABASE=Redwood HDMI Audio [Radeon HD 5000 Series] + +pci:v00001002d0000AA60sv00001025sd0000033D* + ID_MODEL_FROM_DATABASE=Redwood HDMI Audio [Radeon HD 5000 Series] (Mobility Radeon HD 5650) + +pci:v00001002d0000AA60sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=Redwood HDMI Audio [Radeon HD 5000 Series] (Aspire 7740G) + +pci:v00001002d0000AA68* + ID_MODEL_FROM_DATABASE=Cedar HDMI Audio [Radeon HD 5400/6300 Series] + +pci:v00001002d0000AA68sv00001028sd0000AA68* + ID_MODEL_FROM_DATABASE=Cedar HDMI Audio [Radeon HD 5400/6300 Series] (XPS 8300) + +pci:v00001002d0000AA80* + ID_MODEL_FROM_DATABASE=Cayman/Antilles HDMI Audio [Radeon HD 6900 Series] + +pci:v00001002d0000AA88* + ID_MODEL_FROM_DATABASE=Barts HDMI Audio [Radeon HD 6800 Series] + +pci:v00001002d0000AA90* + ID_MODEL_FROM_DATABASE=Turks/Whistler HDMI Audio [Radeon HD 6000 Series] + +pci:v00001002d0000AA90sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=Turks/Whistler HDMI Audio [Radeon HD 6000 Series] (Precision M4600) + +pci:v00001002d0000AA98* + ID_MODEL_FROM_DATABASE=Caicos HDMI Audio [Radeon HD 6400 Series] + +pci:v00001002d0000AA98sv0000174Bsd0000AA98* + ID_MODEL_FROM_DATABASE=Caicos HDMI Audio [Radeon HD 6400 Series] (Radeon HD 6450 1GB DDR3) + +pci:v00001002d0000AAA0* + ID_MODEL_FROM_DATABASE=Tahiti XT HDMI Audio [Radeon HD 7970 Series] + +pci:v00001002d0000AAB0* + ID_MODEL_FROM_DATABASE=Cape Verde/Pitcairn HDMI Audio [Radeon HD 7700/7800 Series] + +pci:v00001002d0000AAC0* + ID_MODEL_FROM_DATABASE=Tobago HDMI Audio [Radeon R7 360 / R9 360 OEM] + +pci:v00001002d0000AAC8* + ID_MODEL_FROM_DATABASE=Hawaii HDMI Audio + +pci:v00001002d0000AAD8* + ID_MODEL_FROM_DATABASE=Tonga HDMI Audio [Radeon R9 285/380] + +pci:v00001002d0000AAD8sv0000174Bsd0000AAD8* + ID_MODEL_FROM_DATABASE=Tonga HDMI Audio [Radeon R9 285/380] (Radeon R9 285/380 HDMI Audio) + +pci:v00001002d0000AAE8* + ID_MODEL_FROM_DATABASE=Fiji HDMI/DP Audio Controller + +pci:v00001002d0000AC00* + ID_MODEL_FROM_DATABASE=Theater 600 Pro + +pci:v00001002d0000AC02* + ID_MODEL_FROM_DATABASE=TV Wonder HD 600 PCIe + +pci:v00001002d0000AC12* + ID_MODEL_FROM_DATABASE=Theater HD T507 (DVB-T) TV tuner/capture device + +pci:v00001002d0000CAB0* + ID_MODEL_FROM_DATABASE=RS100 Host Bridge + +pci:v00001002d0000CAB2* + ID_MODEL_FROM_DATABASE=RS200 Host Bridge + +pci:v00001002d0000CAB3* + ID_MODEL_FROM_DATABASE=RS250 Host Bridge + +pci:v00001002d0000CBB2* + ID_MODEL_FROM_DATABASE=RS200 Host Bridge + +pci:v00001003* + ID_VENDOR_FROM_DATABASE=ULSI Systems + +pci:v00001003d00000201* + ID_MODEL_FROM_DATABASE=US201 + +pci:v00001004* + ID_VENDOR_FROM_DATABASE=VLSI Technology Inc + +pci:v00001004d00000005* + ID_MODEL_FROM_DATABASE=82C592-FC1 + +pci:v00001004d00000006* + ID_MODEL_FROM_DATABASE=82C593-FC1 + +pci:v00001004d00000007* + ID_MODEL_FROM_DATABASE=82C594-AFC2 + +pci:v00001004d00000008* + ID_MODEL_FROM_DATABASE=82C596/7 [Wildcat] + +pci:v00001004d00000009* + ID_MODEL_FROM_DATABASE=82C597-AFC2 + +pci:v00001004d0000000C* + ID_MODEL_FROM_DATABASE=82C541 [Lynx] + +pci:v00001004d0000000D* + ID_MODEL_FROM_DATABASE=82C543 [Lynx] + +pci:v00001004d00000101* + ID_MODEL_FROM_DATABASE=82C532 + +pci:v00001004d00000102* + ID_MODEL_FROM_DATABASE=82C534 [Eagle] + +pci:v00001004d00000103* + ID_MODEL_FROM_DATABASE=82C538 + +pci:v00001004d00000104* + ID_MODEL_FROM_DATABASE=82C535 + +pci:v00001004d00000105* + ID_MODEL_FROM_DATABASE=82C147 + +pci:v00001004d00000200* + ID_MODEL_FROM_DATABASE=82C975 + +pci:v00001004d00000280* + ID_MODEL_FROM_DATABASE=82C925 + +pci:v00001004d00000304* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio + +pci:v00001004d00000304sv00001004sd00000304* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio + +pci:v00001004d00000304sv0000122Dsd00001206* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio (DSP368 Audio) + +pci:v00001004d00000304sv00001483sd00005020* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio (XWave Thunder 3D Audio) + +pci:v00001004d00000305* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Gameport + +pci:v00001004d00000305sv00001004sd00000305* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Gameport + +pci:v00001004d00000305sv0000122Dsd00001207* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Gameport (DSP368 Audio Gameport) + +pci:v00001004d00000305sv00001483sd00005021* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Gameport (XWave Thunder 3D Audio Gameport) + +pci:v00001004d00000306* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Support Registers + +pci:v00001004d00000306sv00001004sd00000306* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Support Registers + +pci:v00001004d00000306sv0000122Dsd00001208* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Support Registers (DSP368 Audio Support Registers) + +pci:v00001004d00000306sv00001483sd00005022* + ID_MODEL_FROM_DATABASE=QSound ThunderBird PCI Audio Support Registers (XWave Thunder 3D Audio Support Registers) + +pci:v00001004d00000307* + ID_MODEL_FROM_DATABASE=SAA7785 ThunderBird PCI Audio + +pci:v00001004d00000307sv00001004sd00000703* + ID_MODEL_FROM_DATABASE=SAA7785 ThunderBird PCI Audio (Philips Rhythmic Edge PSC703) + +pci:v00001004d00000307sv00001004sd00000705* + ID_MODEL_FROM_DATABASE=SAA7785 ThunderBird PCI Audio (Philips Seismic Edge PSC705) + +pci:v00001004d00000307sv00001004sd00000706* + ID_MODEL_FROM_DATABASE=SAA7785 ThunderBird PCI Audio (Philips Acoustic Edge PSC706) + +pci:v00001004d00000308* + ID_MODEL_FROM_DATABASE=SAA7785 ThunderBird PCI Audio Gameport + +pci:v00001004d00000702* + ID_MODEL_FROM_DATABASE=VAS96011 [Golden Gate II] + +pci:v00001004d00000703* + ID_MODEL_FROM_DATABASE=Tollgate + +pci:v00001005* + ID_VENDOR_FROM_DATABASE=Avance Logic Inc. [ALI] + +pci:v00001005d00002064* + ID_MODEL_FROM_DATABASE=ALG2032/2064 + +pci:v00001005d00002128* + ID_MODEL_FROM_DATABASE=ALG2364A + +pci:v00001005d00002301* + ID_MODEL_FROM_DATABASE=ALG2301 + +pci:v00001005d00002302* + ID_MODEL_FROM_DATABASE=ALG2302 + +pci:v00001005d00002364* + ID_MODEL_FROM_DATABASE=ALG2364 + +pci:v00001005d00002464* + ID_MODEL_FROM_DATABASE=ALG2364A + +pci:v00001005d00002501* + ID_MODEL_FROM_DATABASE=ALG2564A/25128A + +pci:v00001006* + ID_VENDOR_FROM_DATABASE=Reply Group + +pci:v00001007* + ID_VENDOR_FROM_DATABASE=NetFrame Systems Inc + +pci:v00001008* + ID_VENDOR_FROM_DATABASE=Epson + +pci:v0000100A* + ID_VENDOR_FROM_DATABASE=Phoenix Technologies + +pci:v0000100B* + ID_VENDOR_FROM_DATABASE=National Semiconductor Corporation + +pci:v0000100Bd00000001* + ID_MODEL_FROM_DATABASE=DP83810 + +pci:v0000100Bd00000002* + ID_MODEL_FROM_DATABASE=87415/87560 IDE + +pci:v0000100Bd0000000E* + ID_MODEL_FROM_DATABASE=87560 Legacy I/O + +pci:v0000100Bd0000000F* + ID_MODEL_FROM_DATABASE=FireWire Controller + +pci:v0000100Bd00000011* + ID_MODEL_FROM_DATABASE=NS87560 National PCI System I/O + +pci:v0000100Bd00000012* + ID_MODEL_FROM_DATABASE=USB Controller + +pci:v0000100Bd00000020* + ID_MODEL_FROM_DATABASE=DP83815 (MacPhyter) Ethernet Controller + +pci:v0000100Bd00000020sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=DP83815 (MacPhyter) Ethernet Controller (Pavilion ze4400 builtin Network) + +pci:v0000100Bd00000020sv000012D9sd0000000C* + ID_MODEL_FROM_DATABASE=DP83815 (MacPhyter) Ethernet Controller (Aculab E1/T1 PMXc cPCI carrier card) + +pci:v0000100Bd00000020sv00001385sd0000F311* + ID_MODEL_FROM_DATABASE=DP83815 (MacPhyter) Ethernet Controller (FA311 / FA312 (FA311 with WoL HW)) + +pci:v0000100Bd00000020sv00001385sd0000F312* + ID_MODEL_FROM_DATABASE=DP83815 (MacPhyter) Ethernet Controller (FA312 (rev. A1) Fast Ethernet PCI Adapter) + +pci:v0000100Bd00000021* + ID_MODEL_FROM_DATABASE=PC87200 PCI to ISA Bridge + +pci:v0000100Bd00000022* + ID_MODEL_FROM_DATABASE=DP83820 10/100/1000 Ethernet Controller + +pci:v0000100Bd00000022sv00001186sd00004900* + ID_MODEL_FROM_DATABASE=DP83820 10/100/1000 Ethernet Controller (DGE-500T) + +pci:v0000100Bd00000022sv00001385sd0000621A* + ID_MODEL_FROM_DATABASE=DP83820 10/100/1000 Ethernet Controller (GA621) + +pci:v0000100Bd00000022sv00001385sd0000622A* + ID_MODEL_FROM_DATABASE=DP83820 10/100/1000 Ethernet Controller (GA622T) + +pci:v0000100Bd00000028* + ID_MODEL_FROM_DATABASE=Geode GX2 Host Bridge + +pci:v0000100Bd0000002A* + ID_MODEL_FROM_DATABASE=CS5535 South Bridge + +pci:v0000100Bd0000002B* + ID_MODEL_FROM_DATABASE=CS5535 ISA bridge + +pci:v0000100Bd0000002D* + ID_MODEL_FROM_DATABASE=CS5535 IDE + +pci:v0000100Bd0000002E* + ID_MODEL_FROM_DATABASE=CS5535 Audio + +pci:v0000100Bd0000002F* + ID_MODEL_FROM_DATABASE=CS5535 USB + +pci:v0000100Bd00000030* + ID_MODEL_FROM_DATABASE=Geode GX2 Graphics Processor + +pci:v0000100Bd00000035* + ID_MODEL_FROM_DATABASE=DP83065 [Saturn] 10/100/1000 Ethernet Controller + +pci:v0000100Bd00000500* + ID_MODEL_FROM_DATABASE=SCx200 Bridge + +pci:v0000100Bd00000501* + ID_MODEL_FROM_DATABASE=SCx200 SMI + +pci:v0000100Bd00000502* + ID_MODEL_FROM_DATABASE=SCx200, SC1100 IDE controller + +pci:v0000100Bd00000502sv0000100Bsd00000502* + ID_MODEL_FROM_DATABASE=SCx200, SC1100 IDE controller (IDE Controller) + +pci:v0000100Bd00000503* + ID_MODEL_FROM_DATABASE=SCx200, SC1100 Audio Controller + +pci:v0000100Bd00000503sv0000100Bsd00000503* + ID_MODEL_FROM_DATABASE=SCx200, SC1100 Audio Controller (XpressAudio controller) + +pci:v0000100Bd00000504* + ID_MODEL_FROM_DATABASE=SCx200 Video + +pci:v0000100Bd00000505* + ID_MODEL_FROM_DATABASE=SCx200 XBus + +pci:v0000100Bd00000510* + ID_MODEL_FROM_DATABASE=SC1100 Bridge + +pci:v0000100Bd00000510sv0000100Bsd00000500* + ID_MODEL_FROM_DATABASE=SC1100 Bridge (GPIO and LPC support bridge) + +pci:v0000100Bd00000511* + ID_MODEL_FROM_DATABASE=SC1100 SMI & ACPI + +pci:v0000100Bd00000511sv0000100Bsd00000501* + ID_MODEL_FROM_DATABASE=SC1100 SMI & ACPI (bridge) + +pci:v0000100Bd00000515* + ID_MODEL_FROM_DATABASE=SC1100 XBus + +pci:v0000100Bd00000515sv0000100Bsd00000505* + ID_MODEL_FROM_DATABASE=SC1100 XBus (SC1100 PCI to XBus bridge) + +pci:v0000100Bd0000D001* + ID_MODEL_FROM_DATABASE=87410 IDE + +pci:v0000100C* + ID_VENDOR_FROM_DATABASE=Tseng Labs Inc + +pci:v0000100Cd00003202* + ID_MODEL_FROM_DATABASE=ET4000/W32p rev A + +pci:v0000100Cd00003205* + ID_MODEL_FROM_DATABASE=ET4000/W32p rev B + +pci:v0000100Cd00003206* + ID_MODEL_FROM_DATABASE=ET4000/W32p rev C + +pci:v0000100Cd00003207* + ID_MODEL_FROM_DATABASE=ET4000/W32p rev D + +pci:v0000100Cd00003208* + ID_MODEL_FROM_DATABASE=ET6000 + +pci:v0000100Cd00004702* + ID_MODEL_FROM_DATABASE=ET6300 + +pci:v0000100D* + ID_VENDOR_FROM_DATABASE=AST Research Inc + +pci:v0000100E* + ID_VENDOR_FROM_DATABASE=Weitek + +pci:v0000100Ed00009000* + ID_MODEL_FROM_DATABASE=P9000 Viper + +pci:v0000100Ed00009001* + ID_MODEL_FROM_DATABASE=P9000 Viper + +pci:v0000100Ed00009002* + ID_MODEL_FROM_DATABASE=P9000 Viper + +pci:v0000100Ed00009100* + ID_MODEL_FROM_DATABASE=P9100 Viper Pro/SE + +pci:v00001010* + ID_VENDOR_FROM_DATABASE=Video Logic, Ltd. + +pci:v00001011* + ID_VENDOR_FROM_DATABASE=Digital Equipment Corporation + +pci:v00001011d00000001* + ID_MODEL_FROM_DATABASE=DECchip 21050 + +pci:v00001011d00000002* + ID_MODEL_FROM_DATABASE=DECchip 21040 [Tulip] + +pci:v00001011d00000004* + ID_MODEL_FROM_DATABASE=DECchip 21030 [TGA] + +pci:v00001011d00000007* + ID_MODEL_FROM_DATABASE=NVRAM [Zephyr NVRAM] + +pci:v00001011d00000008* + ID_MODEL_FROM_DATABASE=KZPSA [KZPSA] + +pci:v00001011d00000009* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] + +pci:v00001011d00000009sv00001025sd00000310* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (21140 Fast Ethernet) + +pci:v00001011d00000009sv000010B8sd00002001* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (SMC9332BDT EtherPower 10/100) + +pci:v00001011d00000009sv000010B8sd00002002* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (SMC9332BVT EtherPower T4 10/100) + +pci:v00001011d00000009sv000010B8sd00002003* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (SMC9334BDT EtherPower 10/100 (1-port)) + +pci:v00001011d00000009sv00001109sd00002400* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (ANA-6944A/TX Fast Ethernet) + +pci:v00001011d00000009sv00001112sd00002300* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (RNS2300 Fast Ethernet) + +pci:v00001011d00000009sv00001112sd00002320* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (RNS2320 Fast Ethernet) + +pci:v00001011d00000009sv00001112sd00002340* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (RNS2340 Fast Ethernet) + +pci:v00001011d00000009sv00001113sd00001207* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (EN-1207-TX Fast Ethernet) + +pci:v00001011d00000009sv00001186sd00001100* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (DFE-500TX Fast Ethernet) + +pci:v00001011d00000009sv00001186sd00001112* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (DFE-570TX Fast Ethernet) + +pci:v00001011d00000009sv00001186sd00001140* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (DFE-660 Cardbus Ethernet 10/100) + +pci:v00001011d00000009sv00001186sd00001142* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (DFE-660 Cardbus Ethernet 10/100) + +pci:v00001011d00000009sv000011F6sd00000503* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (Freedomline Fast Ethernet) + +pci:v00001011d00000009sv00001282sd00009100* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (AEF-380TXD Fast Ethernet) + +pci:v00001011d00000009sv00001385sd00001100* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (FA310TX Fast Ethernet) + +pci:v00001011d00000009sv00002646sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21140 [FasterNet] (KNE100TX Fast Ethernet) + +pci:v00001011d0000000A* + ID_MODEL_FROM_DATABASE=21230 Video Codec + +pci:v00001011d0000000D* + ID_MODEL_FROM_DATABASE=PBXGB [TGA2] + +pci:v00001011d0000000F* + ID_MODEL_FROM_DATABASE=DEFPA FDDI PCI-to-PDQ Interface Chip [PFI] + +pci:v00001011d0000000Fsv00001011sd0000DEF1* + ID_MODEL_FROM_DATABASE=DEFPA FDDI PCI-to-PDQ Interface Chip [PFI] (FDDI controller (DEFPA)) + +pci:v00001011d0000000Fsv0000103Csd0000DEF1* + ID_MODEL_FROM_DATABASE=DEFPA FDDI PCI-to-PDQ Interface Chip [PFI] (FDDI controller (3X-DEFPA)) + +pci:v00001011d00000014* + ID_MODEL_FROM_DATABASE=DECchip 21041 [Tulip Pass 3] + +pci:v00001011d00000014sv00001186sd00000100* + ID_MODEL_FROM_DATABASE=DECchip 21041 [Tulip Pass 3] (DE-530+) + +pci:v00001011d00000016* + ID_MODEL_FROM_DATABASE=DGLPB [OPPO] + +pci:v00001011d00000017* + ID_MODEL_FROM_DATABASE=PV-PCI Graphics Controller (ZLXp-L) + +pci:v00001011d00000018* + ID_MODEL_FROM_DATABASE=Memory Channel interface + +pci:v00001011d00000019* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 + +pci:v00001011d00000019sv00001011sd0000500A* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DE500A Fast Ethernet) + +pci:v00001011d00000019sv00001011sd0000500B* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DE500B Fast Ethernet) + +pci:v00001011d00000019sv00001014sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (10/100 EtherJet Cardbus) + +pci:v00001011d00000019sv00001025sd00000315* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (ALN315 Fast Ethernet) + +pci:v00001011d00000019sv00001033sd0000800C* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (PC-9821-CS01 100BASE-TX Interface Card) + +pci:v00001011d00000019sv00001033sd0000800D* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (PC-9821NR-B06 100BASE-TX Interface Card) + +pci:v00001011d00000019sv0000103Csd0000125A* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (10/100Base-TX (PCI) [A5506B]) + +pci:v00001011d00000019sv0000108Dsd00000016* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Rapidfire 2327 10/100 Ethernet) + +pci:v00001011d00000019sv0000108Dsd00000017* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (GoCard 2250 Ethernet 10/100 Cardbus) + +pci:v00001011d00000019sv000010B8sd00002005* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (SMC8032DT Extreme Ethernet 10/100) + +pci:v00001011d00000019sv000010B8sd00008034* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (SMC8034 Extreme Ethernet 10/100) + +pci:v00001011d00000019sv000010EFsd00008169* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Fast Ethernet) + +pci:v00001011d00000019sv00001109sd00002A00* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (ANA-6911A/TX Fast Ethernet) + +pci:v00001011d00000019sv00001109sd00002B00* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (ANA-6911A/TXC Fast Ethernet) + +pci:v00001011d00000019sv00001109sd00003000* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (ANA-6922/TX Fast Ethernet) + +pci:v00001011d00000019sv00001113sd00001207* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cheetah Fast Ethernet) + +pci:v00001011d00000019sv00001113sd00002220* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Fast Ethernet) + +pci:v00001011d00000019sv0000115Dsd00000002* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Ethernet 10/100) + +pci:v00001011d00000019sv00001179sd00000203* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Fast Ethernet) + +pci:v00001011d00000019sv00001179sd00000204* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Fast Ethernet) + +pci:v00001011d00000019sv00001186sd00001100* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DFE-500TX Fast Ethernet) + +pci:v00001011d00000019sv00001186sd00001101* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DFE-500TX Fast Ethernet) + +pci:v00001011d00000019sv00001186sd00001102* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DFE-500TX Fast Ethernet) + +pci:v00001011d00000019sv00001186sd00001112* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (DFE-570TX Quad Fast Ethernet) + +pci:v00001011d00000019sv000011F0sd00004235* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (21143 [FASTLine-II UTP 10/100]) + +pci:v00001011d00000019sv00001259sd00002800* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (AT-2800Tx Fast Ethernet) + +pci:v00001011d00000019sv00001266sd00000004* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Eagle Fast EtherMAX) + +pci:v00001011d00000019sv000012AFsd00000019* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (NetFlyer Cardbus Fast Ethernet) + +pci:v00001011d00000019sv00001374sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Ethernet Card 10/100) + +pci:v00001011d00000019sv00001374sd00000002* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Ethernet Card 10/100) + +pci:v00001011d00000019sv00001374sd00000007* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Ethernet Card 10/100) + +pci:v00001011d00000019sv00001374sd00000008* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (Cardbus Ethernet Card 10/100) + +pci:v00001011d00000019sv00001385sd00002100* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (FA510) + +pci:v00001011d00000019sv00001395sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (10/100 Ethernet CardBus PC Card) + +pci:v00001011d00000019sv000013D1sd0000AB01* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (EtherFast 10/100 Cardbus (PCMPC200)) + +pci:v00001011d00000019sv00001498sd0000000A* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (TPMC880-10 10/100Base-T and 10Base2 PMC Ethernet Adapter) + +pci:v00001011d00000019sv00001498sd0000000B* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (TPMC880-11 Single 10/100Base-T PMC Ethernet Adapter) + +pci:v00001011d00000019sv00001498sd0000000C* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (TPMC880-12 Single 10Base2 PMC Ethernet Adapter) + +pci:v00001011d00000019sv000014CBsd00000100* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (LNDL-100N 100Base-TX Ethernet PC Card) + +pci:v00001011d00000019sv00001668sd00002000* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (FastNet Pro (PE2000)) + +pci:v00001011d00000019sv00002646sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (KNE100TX) + +pci:v00001011d00000019sv00002646sd00000002* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (KNE-CB4TX) + +pci:v00001011d00000019sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=DECchip 21142/43 (EtherExpress PRO/100 Mobile CardBus 32) + +pci:v00001011d0000001A* + ID_MODEL_FROM_DATABASE=Farallon PN9000SX Gigabit Ethernet + +pci:v00001011d00000021* + ID_MODEL_FROM_DATABASE=DECchip 21052 + +pci:v00001011d00000022* + ID_MODEL_FROM_DATABASE=DECchip 21150 + +pci:v00001011d00000023* + ID_MODEL_FROM_DATABASE=DECchip 21150 + +pci:v00001011d00000024* + ID_MODEL_FROM_DATABASE=DECchip 21152 + +pci:v00001011d00000025* + ID_MODEL_FROM_DATABASE=DECchip 21153 + +pci:v00001011d00000026* + ID_MODEL_FROM_DATABASE=DECchip 21154 + +pci:v00001011d00000034* + ID_MODEL_FROM_DATABASE=56k Modem Cardbus + +pci:v00001011d00000034sv00001374sd00000003* + ID_MODEL_FROM_DATABASE=56k Modem Cardbus + +pci:v00001011d00000045* + ID_MODEL_FROM_DATABASE=DECchip 21553 + +pci:v00001011d00000046* + ID_MODEL_FROM_DATABASE=DECchip 21554 + +pci:v00001011d00000046sv00000E11sd00004050* + ID_MODEL_FROM_DATABASE=DECchip 21554 (Smart Array 4200 Controller) + +pci:v00001011d00000046sv00000E11sd00004051* + ID_MODEL_FROM_DATABASE=DECchip 21554 (Smart Array 4250ES Controller) + +pci:v00001011d00000046sv00000E11sd00004058* + ID_MODEL_FROM_DATABASE=DECchip 21554 (Smart Array 431 Controller) + +pci:v00001011d00000046sv0000103Csd000010C2* + ID_MODEL_FROM_DATABASE=DECchip 21554 (NetRAID-4M) + +pci:v00001011d00000046sv000012D9sd0000000A* + ID_MODEL_FROM_DATABASE=DECchip 21554 (IP Telephony card) + +pci:v00001011d00000046sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=DECchip 21554 (CT7 mainboard) + +pci:v00001011d00000046sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=DECchip 21554 (CE7 mainboard) + +pci:v00001011d00000046sv00009005sd00000364* + ID_MODEL_FROM_DATABASE=DECchip 21554 (5400S (Mustang)) + +pci:v00001011d00000046sv00009005sd00000365* + ID_MODEL_FROM_DATABASE=DECchip 21554 (5400S (Mustang)) + +pci:v00001011d00000046sv00009005sd00001364* + ID_MODEL_FROM_DATABASE=DECchip 21554 (Dell PowerEdge RAID Controller 2) + +pci:v00001011d00000046sv00009005sd00001365* + ID_MODEL_FROM_DATABASE=DECchip 21554 (Dell PowerEdge RAID Controller 2) + +pci:v00001011d00000046sv0000E4BFsd00001000* + ID_MODEL_FROM_DATABASE=DECchip 21554 (CC8-1-BLUES) + +pci:v00001011d00001065* + ID_MODEL_FROM_DATABASE=StrongARM DC21285 + +pci:v00001011d00001065sv00001069sd00000020* + ID_MODEL_FROM_DATABASE=StrongARM DC21285 (DAC960P / DAC1164P) + +pci:v00001012* + ID_VENDOR_FROM_DATABASE=Micronics Computers Inc + +pci:v00001013* + ID_VENDOR_FROM_DATABASE=Cirrus Logic + +pci:v00001013d00000038* + ID_MODEL_FROM_DATABASE=GD 7548 + +pci:v00001013d00000040* + ID_MODEL_FROM_DATABASE=GD 7555 Flat Panel GUI Accelerator + +pci:v00001013d0000004C* + ID_MODEL_FROM_DATABASE=GD 7556 Video/Graphics LCD/CRT Ctrlr + +pci:v00001013d000000A0* + ID_MODEL_FROM_DATABASE=GD 5430/40 [Alpine] + +pci:v00001013d000000A2* + ID_MODEL_FROM_DATABASE=GD 5432 [Alpine] + +pci:v00001013d000000A4* + ID_MODEL_FROM_DATABASE=GD 5434-4 [Alpine] + +pci:v00001013d000000A8* + ID_MODEL_FROM_DATABASE=GD 5434-8 [Alpine] + +pci:v00001013d000000AC* + ID_MODEL_FROM_DATABASE=GD 5436 [Alpine] + +pci:v00001013d000000B0* + ID_MODEL_FROM_DATABASE=GD 5440 + +pci:v00001013d000000B8* + ID_MODEL_FROM_DATABASE=GD 5446 + +pci:v00001013d000000B8sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=GD 5446 (QEMU Virtual Machine) + +pci:v00001013d000000BC* + ID_MODEL_FROM_DATABASE=GD 5480 + +pci:v00001013d000000BCsv00001013sd000000BC* + ID_MODEL_FROM_DATABASE=GD 5480 (CL-GD5480) + +pci:v00001013d000000D0* + ID_MODEL_FROM_DATABASE=GD 5462 + +pci:v00001013d000000D2* + ID_MODEL_FROM_DATABASE=GD 5462 [Laguna I] + +pci:v00001013d000000D4* + ID_MODEL_FROM_DATABASE=GD 5464 [Laguna] + +pci:v00001013d000000D5* + ID_MODEL_FROM_DATABASE=GD 5464 BD [Laguna] + +pci:v00001013d000000D6* + ID_MODEL_FROM_DATABASE=GD 5465 [Laguna] + +pci:v00001013d000000D6sv000013CEsd00008031* + ID_MODEL_FROM_DATABASE=GD 5465 [Laguna] (Barco Metheus 2 Megapixel, Dual Head) + +pci:v00001013d000000D6sv000013CFsd00008031* + ID_MODEL_FROM_DATABASE=GD 5465 [Laguna] (Barco Metheus 2 Megapixel, Dual Head) + +pci:v00001013d000000E8* + ID_MODEL_FROM_DATABASE=GD 5436U + +pci:v00001013d00001100* + ID_MODEL_FROM_DATABASE=CL 6729 + +pci:v00001013d00001110* + ID_MODEL_FROM_DATABASE=PD 6832 PCMCIA/CardBus Ctrlr + +pci:v00001013d00001112* + ID_MODEL_FROM_DATABASE=PD 6834 PCMCIA/CardBus Ctrlr + +pci:v00001013d00001113* + ID_MODEL_FROM_DATABASE=PD 6833 PCMCIA/CardBus Ctrlr + +pci:v00001013d00001200* + ID_MODEL_FROM_DATABASE=GD 7542 [Nordic] + +pci:v00001013d00001202* + ID_MODEL_FROM_DATABASE=GD 7543 [Viking] + +pci:v00001013d00001204* + ID_MODEL_FROM_DATABASE=GD 7541 [Nordic Light] + +pci:v00001013d00004000* + ID_MODEL_FROM_DATABASE=MD 5620 [CLM Data Fax Voice] + +pci:v00001013d00004400* + ID_MODEL_FROM_DATABASE=CD 4400 + +pci:v00001013d00006001* + ID_MODEL_FROM_DATABASE=CS 4610/11 [CrystalClear SoundFusion Audio Accelerator] + +pci:v00001013d00006001sv00001014sd00001010* + ID_MODEL_FROM_DATABASE=CS 4610/11 [CrystalClear SoundFusion Audio Accelerator] (CS4610 SoundFusion Audio Accelerator) + +pci:v00001013d00006003* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] + +pci:v00001013d00006003sv00001013sd00004280* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (Crystal SoundFusion PCI Audio Accelerator) + +pci:v00001013d00006003sv00001014sd00000153* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (ThinkPad 600X/A20m) + +pci:v00001013d00006003sv0000153Bsd0000112E* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (DMX XFire 1024) + +pci:v00001013d00006003sv0000153Bsd00001136* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (SiXPack 5.1+) + +pci:v00001013d00006003sv00001681sd00000050* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (Game Theater XP) + +pci:v00001013d00006003sv00001681sd0000A010* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (Gamesurround Fortissimo II) + +pci:v00001013d00006003sv00001681sd0000A011* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (Gamesurround Fortissimo III 7.1) + +pci:v00001013d00006003sv00005053sd00003357* + ID_MODEL_FROM_DATABASE=CS 4614/22/24/30 [CrystalClear SoundFusion Audio Accelerator] (Santa Cruz) + +pci:v00001013d00006004* + ID_MODEL_FROM_DATABASE=CS 4614/22/24 [CrystalClear SoundFusion Audio Accelerator] + +pci:v00001013d00006005* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv00001013sd00004281* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010A8* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010A9* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010AA* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010AB* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010AC* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010AD* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000010CFsd000010B4* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001013d00006005sv000014C0sd0000000C* + ID_MODEL_FROM_DATABASE=Crystal CS4281 PCI Audio + +pci:v00001014* + ID_VENDOR_FROM_DATABASE=IBM + +pci:v00001014d00000002* + ID_MODEL_FROM_DATABASE=PCI to MCA Bridge + +pci:v00001014d00000005* + ID_MODEL_FROM_DATABASE=Processor to I/O Controller [Alta Lite] + +pci:v00001014d00000007* + ID_MODEL_FROM_DATABASE=Processor to I/O Controller [Alta MP] + +pci:v00001014d0000000A* + ID_MODEL_FROM_DATABASE=PCI to ISA Bridge (IBM27-82376) [Fire Coral] + +pci:v00001014d00000017* + ID_MODEL_FROM_DATABASE=CPU to PCI Bridge + +pci:v00001014d00000018* + ID_MODEL_FROM_DATABASE=TR Auto LANstreamer + +pci:v00001014d0000001B* + ID_MODEL_FROM_DATABASE=GXT-150P + +pci:v00001014d0000001C* + ID_MODEL_FROM_DATABASE=Carrera + +pci:v00001014d0000001D* + ID_MODEL_FROM_DATABASE=SCSI-2 FAST PCI Adapter (82G2675) + +pci:v00001014d00000020* + ID_MODEL_FROM_DATABASE=GXT1000 Graphics Adapter + +pci:v00001014d00000022* + ID_MODEL_FROM_DATABASE=PCI to PCI Bridge (IBM27-82351) + +pci:v00001014d0000002D* + ID_MODEL_FROM_DATABASE=Processor to I/O Controller [Python] + +pci:v00001014d0000002E* + ID_MODEL_FROM_DATABASE=SCSI RAID Adapter [ServeRAID] + +pci:v00001014d0000002Esv00001014sd0000002E* + ID_MODEL_FROM_DATABASE=SCSI RAID Adapter [ServeRAID] (ServeRAID-3x) + +pci:v00001014d0000002Esv00001014sd0000022E* + ID_MODEL_FROM_DATABASE=SCSI RAID Adapter [ServeRAID] (ServeRAID-4H) + +pci:v00001014d00000031* + ID_MODEL_FROM_DATABASE=2 Port Serial Adapter + +pci:v00001014d00000031sv00001014sd00000031* + ID_MODEL_FROM_DATABASE=2 Port Serial Adapter (2721 WAN IOA - 2 Port Sync Serial Adapter) + +pci:v00001014d00000036* + ID_MODEL_FROM_DATABASE=PCI to 32-bit LocalBus Bridge [Miami] + +pci:v00001014d00000037* + ID_MODEL_FROM_DATABASE=PowerPC to PCI Bridge (IBM27-82660) + +pci:v00001014d0000003A* + ID_MODEL_FROM_DATABASE=CPU to PCI Bridge + +pci:v00001014d0000003C* + ID_MODEL_FROM_DATABASE=GXT250P/GXT255P Graphics Adapter + +pci:v00001014d0000003E* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller + +pci:v00001014d0000003Esv00001014sd0000003E* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (Token-Ring Adapter) + +pci:v00001014d0000003Esv00001014sd000000CD* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (Token-Ring Adapter + Wake-On-LAN) + +pci:v00001014d0000003Esv00001014sd000000CE* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (16/4 Token-Ring Adapter 2) + +pci:v00001014d0000003Esv00001014sd000000CF* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (16/4 Token-Ring Adapter Special) + +pci:v00001014d0000003Esv00001014sd000000E4* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (High-Speed 100/16/4 Token-Ring Adapter) + +pci:v00001014d0000003Esv00001014sd000000E5* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (16/4 Token-Ring Adapter 2 + Wake-On-LAN) + +pci:v00001014d0000003Esv00001014sd0000016D* + ID_MODEL_FROM_DATABASE=16/4 Token ring UTP/STP controller (iSeries 2744 Card) + +pci:v00001014d00000045* + ID_MODEL_FROM_DATABASE=SSA Adapter + +pci:v00001014d00000046* + ID_MODEL_FROM_DATABASE=MPIC interrupt controller + +pci:v00001014d00000047* + ID_MODEL_FROM_DATABASE=PCI to PCI Bridge + +pci:v00001014d00000048* + ID_MODEL_FROM_DATABASE=PCI to PCI Bridge + +pci:v00001014d00000049* + ID_MODEL_FROM_DATABASE=Warhead SCSI Controller + +pci:v00001014d0000004E* + ID_MODEL_FROM_DATABASE=ATM Controller (14104e00) + +pci:v00001014d0000004F* + ID_MODEL_FROM_DATABASE=ATM Controller (14104f00) + +pci:v00001014d00000050* + ID_MODEL_FROM_DATABASE=ATM Controller (14105000) + +pci:v00001014d00000053* + ID_MODEL_FROM_DATABASE=25 MBit ATM Controller + +pci:v00001014d00000054* + ID_MODEL_FROM_DATABASE=GXT500P/GXT550P Graphics Adapter + +pci:v00001014d00000057* + ID_MODEL_FROM_DATABASE=MPEG PCI Bridge + +pci:v00001014d00000058* + ID_MODEL_FROM_DATABASE=SSA Adapter [Advanced SerialRAID/X] + +pci:v00001014d0000005E* + ID_MODEL_FROM_DATABASE=GXT800P Graphics Adapter + +pci:v00001014d0000007C* + ID_MODEL_FROM_DATABASE=ATM Controller (14107c00) + +pci:v00001014d0000007D* + ID_MODEL_FROM_DATABASE=3780IDSP [MWave] + +pci:v00001014d0000008B* + ID_MODEL_FROM_DATABASE=EADS PCI to PCI Bridge + +pci:v00001014d0000008E* + ID_MODEL_FROM_DATABASE=GXT3000P Graphics Adapter + +pci:v00001014d00000090* + ID_MODEL_FROM_DATABASE=GXT 3000P + +pci:v00001014d00000090sv00001014sd0000008E* + ID_MODEL_FROM_DATABASE=GXT 3000P (GXT-3000P) + +pci:v00001014d00000091* + ID_MODEL_FROM_DATABASE=SSA Adapter + +pci:v00001014d00000095* + ID_MODEL_FROM_DATABASE=20H2999 PCI Docking Bridge + +pci:v00001014d00000096* + ID_MODEL_FROM_DATABASE=Chukar chipset SCSI controller + +pci:v00001014d00000096sv00001014sd00000097* + ID_MODEL_FROM_DATABASE=Chukar chipset SCSI controller (iSeries 2778 DASD IOA) + +pci:v00001014d00000096sv00001014sd00000098* + ID_MODEL_FROM_DATABASE=Chukar chipset SCSI controller (iSeries 2763 DASD IOA) + +pci:v00001014d00000096sv00001014sd00000099* + ID_MODEL_FROM_DATABASE=Chukar chipset SCSI controller (iSeries 2748 DASD IOA) + +pci:v00001014d0000009F* + ID_MODEL_FROM_DATABASE=PCI 4758 Cryptographic Accelerator + +pci:v00001014d000000A5* + ID_MODEL_FROM_DATABASE=ATM Controller (1410a500) + +pci:v00001014d000000A6* + ID_MODEL_FROM_DATABASE=ATM 155MBPS MM Controller (1410a600) + +pci:v00001014d000000B7* + ID_MODEL_FROM_DATABASE=GXT2000P Graphics Adapter + +pci:v00001014d000000B7sv00001092sd000000B8* + ID_MODEL_FROM_DATABASE=GXT2000P Graphics Adapter (FireGL1 AGP 32Mb) + +pci:v00001014d000000B8* + ID_MODEL_FROM_DATABASE=GXT2000P Graphics Adapter + +pci:v00001014d000000BE* + ID_MODEL_FROM_DATABASE=ATM 622MBPS Controller (1410be00) + +pci:v00001014d000000DC* + ID_MODEL_FROM_DATABASE=Advanced Systems Management Adapter (ASMA) + +pci:v00001014d000000FC* + ID_MODEL_FROM_DATABASE=CPC710 Dual Bridge and Memory Controller (PCI-64) + +pci:v00001014d00000105* + ID_MODEL_FROM_DATABASE=CPC710 Dual Bridge and Memory Controller (PCI-32) + +pci:v00001014d0000010F* + ID_MODEL_FROM_DATABASE=Remote Supervisor Adapter (RSA) + +pci:v00001014d00000142* + ID_MODEL_FROM_DATABASE=Yotta Video Compositor Input + +pci:v00001014d00000142sv00001014sd00000143* + ID_MODEL_FROM_DATABASE=Yotta Video Compositor Input (Yotta Input Controller (ytin)) + +pci:v00001014d00000144* + ID_MODEL_FROM_DATABASE=Yotta Video Compositor Output + +pci:v00001014d00000144sv00001014sd00000145* + ID_MODEL_FROM_DATABASE=Yotta Video Compositor Output (Yotta Output Controller (ytout)) + +pci:v00001014d00000156* + ID_MODEL_FROM_DATABASE=405GP PLB to PCI Bridge + +pci:v00001014d0000015E* + ID_MODEL_FROM_DATABASE=622Mbps ATM PCI Adapter + +pci:v00001014d00000160* + ID_MODEL_FROM_DATABASE=64bit/66MHz PCI ATM 155 MMF + +pci:v00001014d0000016E* + ID_MODEL_FROM_DATABASE=GXT4000P Graphics Adapter + +pci:v00001014d00000170* + ID_MODEL_FROM_DATABASE=GXT6000P Graphics Adapter + +pci:v00001014d00000170sv00001092sd00000172* + ID_MODEL_FROM_DATABASE=GXT6000P Graphics Adapter (Fire GL2) + +pci:v00001014d00000170sv00001092sd00000173* + ID_MODEL_FROM_DATABASE=GXT6000P Graphics Adapter (Fire GL3) + +pci:v00001014d00000170sv00001092sd00000174* + ID_MODEL_FROM_DATABASE=GXT6000P Graphics Adapter (Fire GL4) + +pci:v00001014d00000170sv00001092sd00000184* + ID_MODEL_FROM_DATABASE=GXT6000P Graphics Adapter (Fire GL4s) + +pci:v00001014d0000017D* + ID_MODEL_FROM_DATABASE=GXT300P Graphics Adapter + +pci:v00001014d00000180* + ID_MODEL_FROM_DATABASE=Snipe chipset SCSI controller + +pci:v00001014d00000180sv00001014sd00000241* + ID_MODEL_FROM_DATABASE=Snipe chipset SCSI controller (iSeries 2757 DASD IOA) + +pci:v00001014d00000180sv00001014sd00000264* + ID_MODEL_FROM_DATABASE=Snipe chipset SCSI controller (Quad Channel PCI-X U320 SCSI RAID Adapter (2780)) + +pci:v00001014d00000188* + ID_MODEL_FROM_DATABASE=EADS-X PCI-X to PCI-X Bridge + +pci:v00001014d000001A7* + ID_MODEL_FROM_DATABASE=PCI-X to PCI-X Bridge + +pci:v00001014d000001BD* + ID_MODEL_FROM_DATABASE=ServeRAID Controller + +pci:v00001014d000001BDsv00001014sd000001BD* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID 4Lx) + +pci:v00001014d000001BDsv00001014sd000001BE* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-4M) + +pci:v00001014d000001BDsv00001014sd000001BF* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-4L) + +pci:v00001014d000001BDsv00001014sd00000208* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-4Mx) + +pci:v00001014d000001BDsv00001014sd0000020E* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-4Lx) + +pci:v00001014d000001BDsv00001014sd0000022E* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-4H) + +pci:v00001014d000001BDsv00001014sd00000258* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-5i) + +pci:v00001014d000001BDsv00001014sd00000259* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID-5i) + +pci:v00001014d000001C1* + ID_MODEL_FROM_DATABASE=64bit/66MHz PCI ATM 155 UTP + +pci:v00001014d000001E6* + ID_MODEL_FROM_DATABASE=Cryptographic Accelerator + +pci:v00001014d000001EF* + ID_MODEL_FROM_DATABASE=PowerPC 440GP PCI Bridge + +pci:v00001014d000001EFsv00001734sd0000102B* + ID_MODEL_FROM_DATABASE=PowerPC 440GP PCI Bridge (PCEAS PCI-X Dual Port ESCON Adapter) + +pci:v00001014d000001EFsv00001734sd000010F8* + ID_MODEL_FROM_DATABASE=PowerPC 440GP PCI Bridge (PCEAT PCI-Express Dual Port ESCON Adapter) + +pci:v00001014d000001FF* + ID_MODEL_FROM_DATABASE=10/100 Mbps Ethernet + +pci:v00001014d00000219* + ID_MODEL_FROM_DATABASE=Multiport Serial Adapter + +pci:v00001014d00000219sv00001014sd0000021A* + ID_MODEL_FROM_DATABASE=Multiport Serial Adapter (Dual RVX) + +pci:v00001014d00000219sv00001014sd00000251* + ID_MODEL_FROM_DATABASE=Multiport Serial Adapter (Internal Modem/RVX) + +pci:v00001014d00000219sv00001014sd00000252* + ID_MODEL_FROM_DATABASE=Multiport Serial Adapter (Quad Internal Modem) + +pci:v00001014d0000021B* + ID_MODEL_FROM_DATABASE=GXT6500P Graphics Adapter + +pci:v00001014d0000021C* + ID_MODEL_FROM_DATABASE=GXT4500P Graphics Adapter + +pci:v00001014d00000233* + ID_MODEL_FROM_DATABASE=GXT135P Graphics Adapter + +pci:v00001014d0000028C* + ID_MODEL_FROM_DATABASE=Citrine chipset SCSI controller + +pci:v00001014d0000028Csv00001014sd0000028D* + ID_MODEL_FROM_DATABASE=Citrine chipset SCSI controller (Dual Channel PCI-X DDR SAS RAID Adapter (572E)) + +pci:v00001014d0000028Csv00001014sd000002BE* + ID_MODEL_FROM_DATABASE=Citrine chipset SCSI controller (Dual Channel PCI-X DDR U320 SCSI RAID Adapter (571B)) + +pci:v00001014d0000028Csv00001014sd000002C0* + ID_MODEL_FROM_DATABASE=Citrine chipset SCSI controller (Dual Channel PCI-X DDR U320 SCSI Adapter (571A)) + +pci:v00001014d0000028Csv00001014sd0000030D* + ID_MODEL_FROM_DATABASE=Citrine chipset SCSI controller (PCI-X DDR Auxiliary Cache Adapter (575B)) + +pci:v00001014d000002A1* + ID_MODEL_FROM_DATABASE=Calgary PCI-X Host Bridge + +pci:v00001014d000002BD* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller + +pci:v00001014d000002BDsv00001014sd000002C1* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller (PCI-X DDR 3Gb SAS Adapter (572A/572C)) + +pci:v00001014d000002BDsv00001014sd000002C2* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller (PCI-X DDR 3Gb SAS RAID Adapter (572B/571D)) + +pci:v00001014d000002BDsv00001014sd00000338* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller (PCI-X DDR Auxiliary Cache Adapter (575C)) + +pci:v00001014d00000302* + ID_MODEL_FROM_DATABASE=Winnipeg PCI-X Host Bridge + +pci:v00001014d00000308* + ID_MODEL_FROM_DATABASE=CalIOC2 PCI-E Root Port + +pci:v00001014d00000314* + ID_MODEL_FROM_DATABASE=ZISC 036 Neural accelerator card + +pci:v00001014d0000032D* + ID_MODEL_FROM_DATABASE=Axon - Cell Companion Chip + +pci:v00001014d0000032Dsv00001014sd000003A1* + ID_MODEL_FROM_DATABASE=Axon - Cell Companion Chip (PCIe PowerXCell 8i Cell Accelerator Board) + +pci:v00001014d00000339* + ID_MODEL_FROM_DATABASE=Obsidian-E PCI-E SCSI controller + +pci:v00001014d00000339sv00001014sd0000030A* + ID_MODEL_FROM_DATABASE=Obsidian-E PCI-E SCSI controller (PCIe 3Gb SAS RAID Adapter (574E)) + +pci:v00001014d00000339sv00001014sd0000033A* + ID_MODEL_FROM_DATABASE=Obsidian-E PCI-E SCSI controller (PCIe 3Gb SAS Adapter (57B3)) + +pci:v00001014d00000339sv00001014sd0000035C* + ID_MODEL_FROM_DATABASE=Obsidian-E PCI-E SCSI controller (PCIe x8 Internal 3Gb SAS adapter (57CC)) + +pci:v00001014d00000339sv00001014sd00000360* + ID_MODEL_FROM_DATABASE=Obsidian-E PCI-E SCSI controller (PCI-E Auxiliary Cache Adapter (57B7)) + +pci:v00001014d0000033D* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) + +pci:v00001014d0000033Dsv00001014sd0000033C* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) (PCIe2 1.8GB Cache 6Gb SAS RAID Adapter Tri-port (57B5)) + +pci:v00001014d0000033Dsv00001014sd00000353* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) (PCIe2 3.1GB Cache 6Gb SAS RAID Enclosure (57C3)) + +pci:v00001014d0000033Dsv00001014sd00000354* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) (PCIe2 6Gb SAS Adapter Dual-port (57C4)) + +pci:v00001014d0000033Dsv00001014sd00000356* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) (PCIe2 1.8GB Cache 6Gb SAS RAID & SSD Adapter (574D)) + +pci:v00001014d0000033Dsv00001014sd0000035F* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (FPGA) (PCIe2 6Gb SAS Adapter Quad-port (57B2)) + +pci:v00001014d0000034A* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) + +pci:v00001014d0000034Asv00001014sd0000033B* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe2 6Gb SAS RAID Adapter Quad-port (57B4)) + +pci:v00001014d0000034Asv00001014sd00000355* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe2 3.6GB Cache 6Gb SAS RAID Adapter Quad-port (57B1)) + +pci:v00001014d0000034Asv00001014sd00000357* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe2 6Gb SAS Adapter Quad-port (57C6)) + +pci:v00001014d0000034Asv00001014sd0000035D* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 1.8GB Cache RAID SAS Adapter Quad-port 6GB (57C8)) + +pci:v00001014d0000034Asv00001014sd0000035E* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe2 3.6GB Cache 6Gb SAS RAID Adapter Quad-port (57CE)) + +pci:v00001014d0000034Asv00001014sd000003FB* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 28GB Cache RAID SAS Enclosure 6Gb x 16 (57D5)) + +pci:v00001014d0000034Asv00001014sd000003FE* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x8 Cache SAS RAID Internal Adapter 6Gb (57D8)) + +pci:v00001014d0000034Asv00001014sd000003FF* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x8 SAS RAID Internal Adapter 6Gb (57D7)) + +pci:v00001014d0000034Asv00001014sd00000474* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x16 Cache SAS RAID Internal Adapter 6Gb (57EB)) + +pci:v00001014d0000034Asv00001014sd00000475* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x16 SAS RAID Internal Adapter 6Gb (57EC)) + +pci:v00001014d0000034Asv00001014sd00000499* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x16 Cache SAS RAID Internal Adapter 6Gb (57ED)) + +pci:v00001014d0000034Asv00001014sd0000049A* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x16 SAS RAID Internal Adapter 6Gb (57EE)) + +pci:v00001014d0000034Asv00001014sd000004C7* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x 8 Cache SAS RAID Internal Adapter 6GB(2CCA)) + +pci:v00001014d0000034Asv00001014sd000004C8* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x 8 Cache SAS RAID Internal Adapter 6GB(2CD2)) + +pci:v00001014d0000034Asv00001014sd000004C9* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS Adapter (ASIC) (PCIe3 x 8 Cache SAS RAID Internal Adapter 6GB(2CCD)) + +pci:v00001014d0000044B* + ID_MODEL_FROM_DATABASE=GenWQE Accelerator Adapter + +pci:v00001014d000004AA* + ID_MODEL_FROM_DATABASE=Flash Adapter 90 (PCIe2 0.9TB) + +pci:v00001014d000004DA* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS+ Adapter (ASIC) + +pci:v00001014d000004DAsv00001014sd000004FB* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS+ Adapter (ASIC) (PCIe3 x16 20GB Cache 12Gb Quad SAS RAID+ Adapter(580B)) + +pci:v00001014d000004DAsv00001014sd000004FC* + ID_MODEL_FROM_DATABASE=PCI-E IPR SAS+ Adapter (ASIC) (PCIe3 x8 12Gb Quad SAS RAID+ Adapter(580A)) + +pci:v00001014d000004ED* + ID_MODEL_FROM_DATABASE=Internal Shared Memory (ISM) virtual PCI device + +pci:v00001014d00003022* + ID_MODEL_FROM_DATABASE=QLA3022 Network Adapter + +pci:v00001014d00004022* + ID_MODEL_FROM_DATABASE=QLA3022 Network Adapter + +pci:v00001014d0000FFFF* + ID_MODEL_FROM_DATABASE=MPIC-2 interrupt controller + +pci:v00001015* + ID_VENDOR_FROM_DATABASE=LSI Logic Corp of Canada + +pci:v00001016* + ID_VENDOR_FROM_DATABASE=ICL Personal Systems + +pci:v00001017* + ID_VENDOR_FROM_DATABASE=SPEA Software AG + +pci:v00001017d00005343* + ID_MODEL_FROM_DATABASE=SPEA 3D Accelerator + +pci:v00001018* + ID_VENDOR_FROM_DATABASE=Unisys Systems + +pci:v00001019* + ID_VENDOR_FROM_DATABASE=Elitegroup Computer Systems + +pci:v0000101A* + ID_VENDOR_FROM_DATABASE=AT&T GIS (NCR) + +pci:v0000101Ad00000005* + ID_MODEL_FROM_DATABASE=100VG ethernet + +pci:v0000101Ad00000007* + ID_MODEL_FROM_DATABASE=BYNET BIC4G/2C/2G + +pci:v0000101Ad00000007sv0000101Asd00000019* + ID_MODEL_FROM_DATABASE=BYNET BIC4G/2C/2G (BYNET BIC2C) + +pci:v0000101Ad00000007sv0000101Asd0000001C* + ID_MODEL_FROM_DATABASE=BYNET BIC4G/2C/2G (BYNET BIC2G) + +pci:v0000101Ad00000007sv0000101Asd0000001F* + ID_MODEL_FROM_DATABASE=BYNET BIC4G/2C/2G (BYNET BIC4G) + +pci:v0000101Ad00000009* + ID_MODEL_FROM_DATABASE=PQS Memory Controller + +pci:v0000101Ad0000000A* + ID_MODEL_FROM_DATABASE=BYNET BPCI Adapter + +pci:v0000101Ad0000000B* + ID_MODEL_FROM_DATABASE=BYNET 4 Port BYA Switch (BYA4P) + +pci:v0000101Ad0000000C* + ID_MODEL_FROM_DATABASE=BYNET 4 Port BYA Switch (BYA4G) + +pci:v0000101Ad00000010* + ID_MODEL_FROM_DATABASE=NCR AMC Memory Controller + +pci:v0000101Ad00001DC1* + ID_MODEL_FROM_DATABASE=BYNET BIC2M/BIC4M/BYA4M + +pci:v0000101Ad00001DC1sv0000101Asd00000019* + ID_MODEL_FROM_DATABASE=BYNET BIC2M/BIC4M/BYA4M (BIC2M) + +pci:v0000101Ad00001DC1sv0000101Asd0000001F* + ID_MODEL_FROM_DATABASE=BYNET BIC2M/BIC4M/BYA4M (BIC4M) + +pci:v0000101Ad00001DC1sv0000101Asd00000ECE* + ID_MODEL_FROM_DATABASE=BYNET BIC2M/BIC4M/BYA4M (BYA4M) + +pci:v0000101Ad00001FA8* + ID_MODEL_FROM_DATABASE=BYNET Multi-port BIC Adapter (XBIC Based) + +pci:v0000101Ad00001FA8sv0000101Asd000000C3* + ID_MODEL_FROM_DATABASE=BYNET Multi-port BIC Adapter (XBIC Based) (BYNET BIC2SE) + +pci:v0000101B* + ID_VENDOR_FROM_DATABASE=Vitesse Semiconductor + +pci:v0000101Bd00000452* + ID_MODEL_FROM_DATABASE=VSC452 [SuperBMC] + +pci:v0000101C* + ID_VENDOR_FROM_DATABASE=Western Digital + +pci:v0000101Cd00000193* + ID_MODEL_FROM_DATABASE=33C193A + +pci:v0000101Cd00000196* + ID_MODEL_FROM_DATABASE=33C196A + +pci:v0000101Cd00000197* + ID_MODEL_FROM_DATABASE=33C197A + +pci:v0000101Cd00000296* + ID_MODEL_FROM_DATABASE=33C296A + +pci:v0000101Cd00003193* + ID_MODEL_FROM_DATABASE=7193 + +pci:v0000101Cd00003197* + ID_MODEL_FROM_DATABASE=7197 + +pci:v0000101Cd00003296* + ID_MODEL_FROM_DATABASE=33C296A + +pci:v0000101Cd00004296* + ID_MODEL_FROM_DATABASE=34C296 + +pci:v0000101Cd00009710* + ID_MODEL_FROM_DATABASE=Pipeline 9710 + +pci:v0000101Cd00009712* + ID_MODEL_FROM_DATABASE=Pipeline 9712 + +pci:v0000101Cd0000C24A* + ID_MODEL_FROM_DATABASE=90C + +pci:v0000101D* + ID_VENDOR_FROM_DATABASE=Maxim Integrated Products + +pci:v0000101E* + ID_VENDOR_FROM_DATABASE=American Megatrends Inc. + +pci:v0000101Ed00000009* + ID_MODEL_FROM_DATABASE=MegaRAID 428 Ultra RAID Controller (rev 03) + +pci:v0000101Ed00001960* + ID_MODEL_FROM_DATABASE=MegaRAID + +pci:v0000101Ed00001960sv0000101Esd00000471* + ID_MODEL_FROM_DATABASE=MegaRAID (471 Enterprise 1600 RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000475* + ID_MODEL_FROM_DATABASE=MegaRAID (475 Express 500/500LC RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000477* + ID_MODEL_FROM_DATABASE=MegaRAID (477 Elite 3100 RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000493* + ID_MODEL_FROM_DATABASE=MegaRAID (493 Elite 1600 RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000494* + ID_MODEL_FROM_DATABASE=MegaRAID (494 Elite 1650 RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000503* + ID_MODEL_FROM_DATABASE=MegaRAID (503 Enterprise 1650 RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000511* + ID_MODEL_FROM_DATABASE=MegaRAID (511 i4 IDE RAID Controller) + +pci:v0000101Ed00001960sv0000101Esd00000522* + ID_MODEL_FROM_DATABASE=MegaRAID (522 i4133 RAID Controller) + +pci:v0000101Ed00001960sv00001028sd00000471* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller 3/QC) + +pci:v0000101Ed00001960sv00001028sd00000475* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller 3/SC) + +pci:v0000101Ed00001960sv00001028sd00000493* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge RAID Controller 3/DC) + +pci:v0000101Ed00001960sv00001028sd00000511* + ID_MODEL_FROM_DATABASE=MegaRAID (PowerEdge Cost Effective RAID Controller ATA100/4Ch) + +pci:v0000101Ed00001960sv0000103Csd000060E7* + ID_MODEL_FROM_DATABASE=MegaRAID (NetRAID-1M) + +pci:v0000101Ed00001960sv0000103Csd000060E8* + ID_MODEL_FROM_DATABASE=MegaRAID (NetRaid 2M [AMI MegaRaid 493]) + +pci:v0000101Ed00009010* + ID_MODEL_FROM_DATABASE=MegaRAID 428 Ultra RAID Controller + +pci:v0000101Ed00009030* + ID_MODEL_FROM_DATABASE=EIDE Controller + +pci:v0000101Ed00009031* + ID_MODEL_FROM_DATABASE=EIDE Controller + +pci:v0000101Ed00009032* + ID_MODEL_FROM_DATABASE=EIDE & SCSI Controller + +pci:v0000101Ed00009033* + ID_MODEL_FROM_DATABASE=SCSI Controller + +pci:v0000101Ed00009040* + ID_MODEL_FROM_DATABASE=Multimedia card + +pci:v0000101Ed00009060* + ID_MODEL_FROM_DATABASE=MegaRAID 434 Ultra GT RAID Controller + +pci:v0000101Ed00009063* + ID_MODEL_FROM_DATABASE=MegaRAC + +pci:v0000101Ed00009063sv0000101Esd00000767* + ID_MODEL_FROM_DATABASE=MegaRAC (Dell Remote Assistant Card 2) + +pci:v0000101F* + ID_VENDOR_FROM_DATABASE=PictureTel + +pci:v00001020* + ID_VENDOR_FROM_DATABASE=Hitachi Computer Products + +pci:v00001021* + ID_VENDOR_FROM_DATABASE=OKI Electric Industry Co. Ltd. + +pci:v00001022* + ID_VENDOR_FROM_DATABASE=Advanced Micro Devices, Inc. [AMD] + +pci:v00001022d00001100* + ID_MODEL_FROM_DATABASE=K8 [Athlon64/Opteron] HyperTransport Technology Configuration + +pci:v00001022d00001101* + ID_MODEL_FROM_DATABASE=K8 [Athlon64/Opteron] Address Map + +pci:v00001022d00001102* + ID_MODEL_FROM_DATABASE=K8 [Athlon64/Opteron] DRAM Controller + +pci:v00001022d00001103* + ID_MODEL_FROM_DATABASE=K8 [Athlon64/Opteron] Miscellaneous Control + +pci:v00001022d00001200* + ID_MODEL_FROM_DATABASE=Family 10h Processor HyperTransport Configuration + +pci:v00001022d00001201* + ID_MODEL_FROM_DATABASE=Family 10h Processor Address Map + +pci:v00001022d00001202* + ID_MODEL_FROM_DATABASE=Family 10h Processor DRAM Controller + +pci:v00001022d00001203* + ID_MODEL_FROM_DATABASE=Family 10h Processor Miscellaneous Control + +pci:v00001022d00001204* + ID_MODEL_FROM_DATABASE=Family 10h Processor Link Control + +pci:v00001022d00001300* + ID_MODEL_FROM_DATABASE=Family 11h Processor HyperTransport Configuration + +pci:v00001022d00001301* + ID_MODEL_FROM_DATABASE=Family 11h Processor Address Map + +pci:v00001022d00001302* + ID_MODEL_FROM_DATABASE=Family 11h Processor DRAM Controller + +pci:v00001022d00001303* + ID_MODEL_FROM_DATABASE=Family 11h Processor Miscellaneous Control + +pci:v00001022d00001304* + ID_MODEL_FROM_DATABASE=Family 11h Processor Link Control + +pci:v00001022d00001400* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 0 + +pci:v00001022d00001401* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 1 + +pci:v00001022d00001402* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 2 + +pci:v00001022d00001403* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 3 + +pci:v00001022d00001404* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 4 + +pci:v00001022d00001405* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Function 5 + +pci:v00001022d00001410* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Complex + +pci:v00001022d00001410sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Complex (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d00001412* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001412sv00001022sd00001234* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port (Trinity A-series APU) + +pci:v00001022d00001413* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001414* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001414sv00001022sd00001234* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port (Trinity A-series APU) + +pci:v00001022d00001415* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001416* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001417* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001418* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) Processor Root Port + +pci:v00001022d00001419* + ID_MODEL_FROM_DATABASE=Family 15h (Models 10h-1fh) I/O Memory Management Unit + +pci:v00001022d0000141A* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 0 + +pci:v00001022d0000141B* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 1 + +pci:v00001022d0000141C* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 2 + +pci:v00001022d0000141D* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 3 + +pci:v00001022d0000141E* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 4 + +pci:v00001022d0000141F* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Function 5 + +pci:v00001022d00001422* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Root Complex + +pci:v00001022d00001423* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) I/O Memory Management Unit + +pci:v00001022d00001426* + ID_MODEL_FROM_DATABASE=Family 15h (Models 30h-3fh) Processor Root Port + +pci:v00001022d00001436* + ID_MODEL_FROM_DATABASE=Liverpool Processor Root Complex + +pci:v00001022d00001437* + ID_MODEL_FROM_DATABASE=Liverpool I/O Memory Management Unit + +pci:v00001022d00001438* + ID_MODEL_FROM_DATABASE=Liverpool Processor Root Port + +pci:v00001022d00001439* + ID_MODEL_FROM_DATABASE=Family 16h Processor Functions 5:1 + +pci:v00001022d0000145B* + ID_MODEL_FROM_DATABASE=Zeppelin Non-Transparent Bridge + +pci:v00001022d00001510* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Complex + +pci:v00001022d00001510sv0000174Bsd00001001* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Complex (PURE Fusion Mini) + +pci:v00001022d00001512* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Port + +pci:v00001022d00001513* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Port + +pci:v00001022d00001514* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Port + +pci:v00001022d00001515* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Port + +pci:v00001022d00001516* + ID_MODEL_FROM_DATABASE=Family 14h Processor Root Port + +pci:v00001022d00001530* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 0 + +pci:v00001022d00001531* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 1 + +pci:v00001022d00001532* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 2 + +pci:v00001022d00001533* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 3 + +pci:v00001022d00001534* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 4 + +pci:v00001022d00001535* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 5 + +pci:v00001022d00001536* + ID_MODEL_FROM_DATABASE=Family 16h Processor Root Complex + +pci:v00001022d00001538* + ID_MODEL_FROM_DATABASE=Family 16h Processor Function 0 + +pci:v00001022d00001600* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 0 + +pci:v00001022d00001601* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 1 + +pci:v00001022d00001602* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 2 + +pci:v00001022d00001603* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 3 + +pci:v00001022d00001604* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 4 + +pci:v00001022d00001605* + ID_MODEL_FROM_DATABASE=Family 15h Processor Function 5 + +pci:v00001022d00001700* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 0 + +pci:v00001022d00001701* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 1 + +pci:v00001022d00001702* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 2 + +pci:v00001022d00001703* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 3 + +pci:v00001022d00001704* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 4 + +pci:v00001022d00001705* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Complex + +pci:v00001022d00001707* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d00001708* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d00001709* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d0000170A* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d0000170B* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d0000170C* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d0000170D* + ID_MODEL_FROM_DATABASE=Family 12h Processor Root Port + +pci:v00001022d00001716* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 5 + +pci:v00001022d00001718* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 6 + +pci:v00001022d00001719* + ID_MODEL_FROM_DATABASE=Family 12h/14h Processor Function 7 + +pci:v00001022d00002000* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] + +pci:v00001022d00002000sv00001014sd00002000* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (NetFinity 10/100 Fast Ethernet) + +pci:v00001022d00002000sv00001022sd00002000* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (PCnet - Fast 79C971) + +pci:v00001022d00002000sv0000103Csd0000104C* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv0000103Csd00001064* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv0000103Csd00001065* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv0000103Csd0000106C* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv0000103Csd0000106E* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv0000103Csd000010EA* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (Ethernet with LAN remote power Adapter) + +pci:v00001022d00002000sv00001113sd00001220* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (EN1220 10/100 Fast Ethernet) + +pci:v00001022d00002000sv00001259sd00002450* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2450 10/100 Fast Ethernet) + +pci:v00001022d00002000sv00001259sd00002454* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2450v4 10Mb Ethernet Adapter) + +pci:v00001022d00002000sv00001259sd00002700* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2700TX 10/100 Fast Ethernet) + +pci:v00001022d00002000sv00001259sd00002701* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2700FX 100Mb Ethernet) + +pci:v00001022d00002000sv00001259sd00002702* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2700FTX 10/100 Mb Fiber/Copper Fast Ethernet) + +pci:v00001022d00002000sv00001259sd00002703* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2701FX) + +pci:v00001022d00002000sv00001259sd00002704* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (AT-2701FTX 10/100 Mb Fiber/Copper Fast Ethernet) + +pci:v00001022d00002000sv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v00001022d00002000sv00004C53sd00001010* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (CP5/CR6 mainboard) + +pci:v00001022d00002000sv00004C53sd00001020* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (VR6 mainboard) + +pci:v00001022d00002000sv00004C53sd00001030* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (PC5 mainboard) + +pci:v00001022d00002000sv00004C53sd00001040* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (CL7 mainboard) + +pci:v00001022d00002000sv00004C53sd00001060* + ID_MODEL_FROM_DATABASE=79c970 [PCnet32 LANCE] (PC7 mainboard) + +pci:v00001022d00002001* + ID_MODEL_FROM_DATABASE=79c978 [HomePNA] + +pci:v00001022d00002001sv00001092sd00000A78* + ID_MODEL_FROM_DATABASE=79c978 [HomePNA] (Multimedia Home Network Adapter) + +pci:v00001022d00002001sv00001668sd00000299* + ID_MODEL_FROM_DATABASE=79c978 [HomePNA] (ActionLink Home Network Adapter) + +pci:v00001022d00002003* + ID_MODEL_FROM_DATABASE=Am 1771 MBW [Alchemy] + +pci:v00001022d00002020* + ID_MODEL_FROM_DATABASE=53c974 [PCscsi] + +pci:v00001022d00002020sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=53c974 [PCscsi] (QEMU Virtual Machine) + +pci:v00001022d00002040* + ID_MODEL_FROM_DATABASE=79c974 + +pci:v00001022d00002080* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] Host Bridge + +pci:v00001022d00002081* + ID_MODEL_FROM_DATABASE=Geode LX Video + +pci:v00001022d00002082* + ID_MODEL_FROM_DATABASE=Geode LX AES Security Block + +pci:v00001022d0000208F* + ID_MODEL_FROM_DATABASE=CS5536 GeodeLink PCI South Bridge + +pci:v00001022d00002090* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] ISA + +pci:v00001022d00002091* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] FLASH + +pci:v00001022d00002093* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] Audio + +pci:v00001022d00002094* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] OHC + +pci:v00001022d00002095* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] EHC + +pci:v00001022d00002096* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] UDC + +pci:v00001022d00002097* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] UOC + +pci:v00001022d0000209A* + ID_MODEL_FROM_DATABASE=CS5536 [Geode companion] IDE + +pci:v00001022d00003000* + ID_MODEL_FROM_DATABASE=ELanSC520 Microcontroller + +pci:v00001022d000043A0* + ID_MODEL_FROM_DATABASE=Hudson PCI to PCI bridge (PCIE port 0) + +pci:v00001022d000043A1* + ID_MODEL_FROM_DATABASE=Hudson PCI to PCI bridge (PCIE port 1) + +pci:v00001022d000043A2* + ID_MODEL_FROM_DATABASE=Hudson PCI to PCI bridge (PCIE port 2) + +pci:v00001022d000043A3* + ID_MODEL_FROM_DATABASE=Hudson PCI to PCI bridge (PCIE port 3) + +pci:v00001022d00007006* + ID_MODEL_FROM_DATABASE=AMD-751 [Irongate] System Controller + +pci:v00001022d00007007* + ID_MODEL_FROM_DATABASE=AMD-751 [Irongate] AGP Bridge + +pci:v00001022d0000700A* + ID_MODEL_FROM_DATABASE=AMD-IGR4 AGP Host to PCI Bridge + +pci:v00001022d0000700B* + ID_MODEL_FROM_DATABASE=AMD-IGR4 PCI to PCI Bridge + +pci:v00001022d0000700C* + ID_MODEL_FROM_DATABASE=AMD-760 MP [IGD4-2P] System Controller + +pci:v00001022d0000700D* + ID_MODEL_FROM_DATABASE=AMD-760 MP [IGD4-2P] AGP Bridge + +pci:v00001022d0000700E* + ID_MODEL_FROM_DATABASE=AMD-760 [IGD4-1P] System Controller + +pci:v00001022d0000700F* + ID_MODEL_FROM_DATABASE=AMD-760 [IGD4-1P] AGP Bridge + +pci:v00001022d00007400* + ID_MODEL_FROM_DATABASE=AMD-755 [Cobra] ISA + +pci:v00001022d00007401* + ID_MODEL_FROM_DATABASE=AMD-755 [Cobra] IDE + +pci:v00001022d00007403* + ID_MODEL_FROM_DATABASE=AMD-755 [Cobra] ACPI + +pci:v00001022d00007404* + ID_MODEL_FROM_DATABASE=AMD-755 [Cobra] USB + +pci:v00001022d00007408* + ID_MODEL_FROM_DATABASE=AMD-756 [Viper] ISA + +pci:v00001022d00007409* + ID_MODEL_FROM_DATABASE=AMD-756 [Viper] IDE + +pci:v00001022d0000740B* + ID_MODEL_FROM_DATABASE=AMD-756 [Viper] ACPI + +pci:v00001022d0000740C* + ID_MODEL_FROM_DATABASE=AMD-756 [Viper] USB + +pci:v00001022d00007410* + ID_MODEL_FROM_DATABASE=AMD-766 [ViperPlus] ISA + +pci:v00001022d00007411* + ID_MODEL_FROM_DATABASE=AMD-766 [ViperPlus] IDE + +pci:v00001022d00007413* + ID_MODEL_FROM_DATABASE=AMD-766 [ViperPlus] ACPI + +pci:v00001022d00007414* + ID_MODEL_FROM_DATABASE=AMD-766 [ViperPlus] USB + +pci:v00001022d00007440* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] ISA + +pci:v00001022d00007440sv00001043sd00008044* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] ISA (A7M-D Mainboard) + +pci:v00001022d00007441* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] IDE + +pci:v00001022d00007443* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] ACPI + +pci:v00001022d00007443sv00001043sd00008044* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] ACPI (A7M-D Mainboard) + +pci:v00001022d00007445* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] Audio + +pci:v00001022d00007446* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] MC97 Modem + +pci:v00001022d00007448* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] PCI + +pci:v00001022d00007449* + ID_MODEL_FROM_DATABASE=AMD-768 [Opus] USB + +pci:v00001022d00007450* + ID_MODEL_FROM_DATABASE=AMD-8131 PCI-X Bridge + +pci:v00001022d00007451* + ID_MODEL_FROM_DATABASE=AMD-8131 PCI-X IOAPIC + +pci:v00001022d00007454* + ID_MODEL_FROM_DATABASE=AMD-8151 System Controller + +pci:v00001022d00007455* + ID_MODEL_FROM_DATABASE=AMD-8151 AGP Bridge + +pci:v00001022d00007458* + ID_MODEL_FROM_DATABASE=AMD-8132 PCI-X Bridge + +pci:v00001022d00007459* + ID_MODEL_FROM_DATABASE=AMD-8132 PCI-X IOAPIC + +pci:v00001022d00007460* + ID_MODEL_FROM_DATABASE=AMD-8111 PCI + +pci:v00001022d00007460sv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 PCI (HDAMB) + +pci:v00001022d00007461* + ID_MODEL_FROM_DATABASE=AMD-8111 USB + +pci:v00001022d00007462* + ID_MODEL_FROM_DATABASE=AMD-8111 Ethernet + +pci:v00001022d00007463* + ID_MODEL_FROM_DATABASE=AMD-8111 USB EHCI + +pci:v00001022d00007464* + ID_MODEL_FROM_DATABASE=AMD-8111 USB OHCI + +pci:v00001022d00007464sv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 USB OHCI (HDAMB) + +pci:v00001022d00007468* + ID_MODEL_FROM_DATABASE=AMD-8111 LPC + +pci:v00001022d00007468sv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 LPC (HDAMB) + +pci:v00001022d00007469* + ID_MODEL_FROM_DATABASE=AMD-8111 IDE + +pci:v00001022d00007469sv00001022sd00002B80* + ID_MODEL_FROM_DATABASE=AMD-8111 IDE ([Quartet]) + +pci:v00001022d00007469sv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 IDE (HDAMB) + +pci:v00001022d0000746A* + ID_MODEL_FROM_DATABASE=AMD-8111 SMBus 2.0 + +pci:v00001022d0000746B* + ID_MODEL_FROM_DATABASE=AMD-8111 ACPI + +pci:v00001022d0000746Bsv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 ACPI (HDAMB) + +pci:v00001022d0000746D* + ID_MODEL_FROM_DATABASE=AMD-8111 AC97 Audio + +pci:v00001022d0000746Dsv0000161Fsd00003017* + ID_MODEL_FROM_DATABASE=AMD-8111 AC97 Audio (HDAMB) + +pci:v00001022d0000746E* + ID_MODEL_FROM_DATABASE=AMD-8111 MC97 Modem + +pci:v00001022d0000756B* + ID_MODEL_FROM_DATABASE=AMD-8111 ACPI + +pci:v00001022d00007800* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [IDE mode] + +pci:v00001022d00007801* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] + +pci:v00001022d00007801sv0000103Csd0000168B* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (ProBook 4535s Notebook) + +pci:v00001022d00007801sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (ProBook 455 G1 Notebook) + +pci:v00001022d00007802* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode] + +pci:v00001022d00007803* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode] + +pci:v00001022d00007804* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] + +pci:v00001022d00007804sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d00007805* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode] + +pci:v00001022d00007806* + ID_MODEL_FROM_DATABASE=FCH SD Flash Controller + +pci:v00001022d00007807* + ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller + +pci:v00001022d00007807sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (ProBook 455 G1 Notebook) + +pci:v00001022d00007807sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d00007808* + ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller + +pci:v00001022d00007808sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (ProBook 455 G1 Notebook) + +pci:v00001022d00007808sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d00007809* + ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller + +pci:v00001022d00007809sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (ProBook 455 G1 Notebook) + +pci:v00001022d0000780B* + ID_MODEL_FROM_DATABASE=FCH SMBus Controller + +pci:v00001022d0000780Bsv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH SMBus Controller (ProBook 455 G1 Notebook) + +pci:v00001022d0000780Bsv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d0000780C* + ID_MODEL_FROM_DATABASE=FCH IDE Controller + +pci:v00001022d0000780D* + ID_MODEL_FROM_DATABASE=FCH Azalia Controller + +pci:v00001022d0000780Dsv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH Azalia Controller (ProBook 455 G1 Notebook) + +pci:v00001022d0000780Dsv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH Azalia Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d0000780Dsv00001043sd00008444* + ID_MODEL_FROM_DATABASE=FCH Azalia Controller (F2A85-M Series) + +pci:v00001022d0000780E* + ID_MODEL_FROM_DATABASE=FCH LPC Bridge + +pci:v00001022d0000780Esv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH LPC Bridge (ProBook 455 G1 Notebook) + +pci:v00001022d0000780Esv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d0000780F* + ID_MODEL_FROM_DATABASE=FCH PCI Bridge + +pci:v00001022d00007812* + ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller + +pci:v00001022d00007813* + ID_MODEL_FROM_DATABASE=FCH SD Flash Controller + +pci:v00001022d00007814* + ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller + +pci:v00001022d00007814sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (ProBook 455 G1 Notebook) + +pci:v00001022d00007814sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (Pavilion 17-e163sg Notebook PC) + +pci:v00001022d00007900* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [IDE mode] + +pci:v00001022d00007901* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] + +pci:v00001022d00007902* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode] + +pci:v00001022d00007903* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode] + +pci:v00001022d00007904* + ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] + +pci:v00001022d00007906* + ID_MODEL_FROM_DATABASE=FCH SD Flash Controller + +pci:v00001022d00007908* + ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller + +pci:v00001022d0000790B* + ID_MODEL_FROM_DATABASE=FCH SMBus Controller + +pci:v00001022d0000790E* + ID_MODEL_FROM_DATABASE=FCH LPC Bridge + +pci:v00001022d0000790F* + ID_MODEL_FROM_DATABASE=FCH PCI Bridge + +pci:v00001022d00007914* + ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller + +pci:v00001022d00009600* + ID_MODEL_FROM_DATABASE=RS780 Host Bridge + +pci:v00001022d00009600sv00001043sd000082EE* + ID_MODEL_FROM_DATABASE=RS780 Host Bridge (M378A-CM Motherboard) + +pci:v00001022d00009600sv00001043sd000082F1* + ID_MODEL_FROM_DATABASE=RS780 Host Bridge (M3A78-EH Motherboard) + +pci:v00001022d00009601* + ID_MODEL_FROM_DATABASE=RS880 Host Bridge + +pci:v00001022d00009601sv00001019sd00002120* + ID_MODEL_FROM_DATABASE=RS880 Host Bridge (A785GM-M) + +pci:v00001022d00009601sv00001043sd0000843E* + ID_MODEL_FROM_DATABASE=RS880 Host Bridge (M5A88-V EVO) + +pci:v00001022d00009602* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (int gfx) + +pci:v00001022d00009603* + ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 0) + +pci:v00001022d00009604* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 0) + +pci:v00001022d00009605* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 1) + +pci:v00001022d00009606* + ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (PCIE port 2) + +pci:v00001022d00009607* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 3) + +pci:v00001022d00009608* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 4) + +pci:v00001022d00009609* + ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 5) + +pci:v00001022d0000960A* + ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (NB-SB link) + +pci:v00001022d0000960B* + ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 1) + +pci:v00001023* + ID_VENDOR_FROM_DATABASE=Trident Microsystems + +pci:v00001023d00000194* + ID_MODEL_FROM_DATABASE=82C194 + +pci:v00001023d00002000* + ID_MODEL_FROM_DATABASE=4DWave DX + +pci:v00001023d00002001* + ID_MODEL_FROM_DATABASE=4DWave NX + +pci:v00001023d00002001sv0000122Dsd00001400* + ID_MODEL_FROM_DATABASE=4DWave NX (Trident PCI288-Q3DII (NX)) + +pci:v00001023d00002100* + ID_MODEL_FROM_DATABASE=CyberBlade XP4m32 + +pci:v00001023d00002200* + ID_MODEL_FROM_DATABASE=XGI Volari XP5 + +pci:v00001023d00008400* + ID_MODEL_FROM_DATABASE=CyberBlade/i7 + +pci:v00001023d00008400sv00001023sd00008400* + ID_MODEL_FROM_DATABASE=CyberBlade/i7 (CyberBlade i7 AGP) + +pci:v00001023d00008420* + ID_MODEL_FROM_DATABASE=CyberBlade/i7d + +pci:v00001023d00008420sv00000E11sd0000B15A* + ID_MODEL_FROM_DATABASE=CyberBlade/i7d (CyberBlade i7 AGP) + +pci:v00001023d00008500* + ID_MODEL_FROM_DATABASE=CyberBlade/i1 + +pci:v00001023d00008520* + ID_MODEL_FROM_DATABASE=CyberBlade i1 + +pci:v00001023d00008520sv00000E11sd0000B16E* + ID_MODEL_FROM_DATABASE=CyberBlade i1 (AGP) + +pci:v00001023d00008520sv00001023sd00008520* + ID_MODEL_FROM_DATABASE=CyberBlade i1 (AGP) + +pci:v00001023d00008620* + ID_MODEL_FROM_DATABASE=CyberBlade/i1 + +pci:v00001023d00008620sv00001014sd00000502* + ID_MODEL_FROM_DATABASE=CyberBlade/i1 (ThinkPad R30/T30) + +pci:v00001023d00008620sv00001014sd00001025* + ID_MODEL_FROM_DATABASE=CyberBlade/i1 (Travelmate 352TE) + +pci:v00001023d00008820* + ID_MODEL_FROM_DATABASE=CyberBlade XPAi1 + +pci:v00001023d00009320* + ID_MODEL_FROM_DATABASE=TGUI 9320 + +pci:v00001023d00009350* + ID_MODEL_FROM_DATABASE=GUI Accelerator + +pci:v00001023d00009360* + ID_MODEL_FROM_DATABASE=Flat panel GUI Accelerator + +pci:v00001023d00009382* + ID_MODEL_FROM_DATABASE=Cyber 9382 [Reference design] + +pci:v00001023d00009383* + ID_MODEL_FROM_DATABASE=Cyber 9383 [Reference design] + +pci:v00001023d00009385* + ID_MODEL_FROM_DATABASE=Cyber 9385 [Reference design] + +pci:v00001023d00009386* + ID_MODEL_FROM_DATABASE=Cyber 9386 + +pci:v00001023d00009388* + ID_MODEL_FROM_DATABASE=Cyber 9388 + +pci:v00001023d00009397* + ID_MODEL_FROM_DATABASE=Cyber 9397 + +pci:v00001023d0000939A* + ID_MODEL_FROM_DATABASE=Cyber 9397DVD + +pci:v00001023d00009420* + ID_MODEL_FROM_DATABASE=TGUI 9420 + +pci:v00001023d00009430* + ID_MODEL_FROM_DATABASE=TGUI 9430 + +pci:v00001023d00009440* + ID_MODEL_FROM_DATABASE=TGUI 9440 + +pci:v00001023d00009460* + ID_MODEL_FROM_DATABASE=TGUI 9460 + +pci:v00001023d00009470* + ID_MODEL_FROM_DATABASE=TGUI 9470 + +pci:v00001023d00009520* + ID_MODEL_FROM_DATABASE=Cyber 9520 + +pci:v00001023d00009525* + ID_MODEL_FROM_DATABASE=Cyber 9525 + +pci:v00001023d00009540* + ID_MODEL_FROM_DATABASE=Cyber 9540 + +pci:v00001023d00009660* + ID_MODEL_FROM_DATABASE=TGUI 9660/938x/968x + +pci:v00001023d00009680* + ID_MODEL_FROM_DATABASE=TGUI 9680 + +pci:v00001023d00009682* + ID_MODEL_FROM_DATABASE=TGUI 9682 + +pci:v00001023d00009683* + ID_MODEL_FROM_DATABASE=TGUI 9683 + +pci:v00001023d00009685* + ID_MODEL_FROM_DATABASE=ProVIDIA 9685 + +pci:v00001023d00009750* + ID_MODEL_FROM_DATABASE=3DImage 9750 + +pci:v00001023d00009750sv00001014sd00009750* + ID_MODEL_FROM_DATABASE=3DImage 9750 + +pci:v00001023d00009750sv00001023sd00009750* + ID_MODEL_FROM_DATABASE=3DImage 9750 + +pci:v00001023d00009753* + ID_MODEL_FROM_DATABASE=TGUI 9753 + +pci:v00001023d00009754* + ID_MODEL_FROM_DATABASE=TGUI 9754 + +pci:v00001023d00009759* + ID_MODEL_FROM_DATABASE=TGUI 975 + +pci:v00001023d00009783* + ID_MODEL_FROM_DATABASE=TGUI 9783 + +pci:v00001023d00009785* + ID_MODEL_FROM_DATABASE=TGUI 9785 + +pci:v00001023d00009850* + ID_MODEL_FROM_DATABASE=3DImage 9850 + +pci:v00001023d00009880* + ID_MODEL_FROM_DATABASE=Blade 3D PCI/AGP + +pci:v00001023d00009880sv00001023sd00009880* + ID_MODEL_FROM_DATABASE=Blade 3D PCI/AGP (Blade 3D) + +pci:v00001023d00009910* + ID_MODEL_FROM_DATABASE=CyberBlade/XP + +pci:v00001023d00009930* + ID_MODEL_FROM_DATABASE=CyberBlade/XPm + +pci:v00001023d00009960* + ID_MODEL_FROM_DATABASE=CyberBlade XP2 + +pci:v00001024* + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +pci:v00001025* + ID_VENDOR_FROM_DATABASE=Acer Incorporated [ALI] + +pci:v00001025d00001435* + ID_MODEL_FROM_DATABASE=M1435 + +pci:v00001025d00001445* + ID_MODEL_FROM_DATABASE=M1445 + +pci:v00001025d00001449* + ID_MODEL_FROM_DATABASE=M1449 + +pci:v00001025d00001451* + ID_MODEL_FROM_DATABASE=M1451 + +pci:v00001025d00001461* + ID_MODEL_FROM_DATABASE=M1461 + +pci:v00001025d00001489* + ID_MODEL_FROM_DATABASE=M1489 + +pci:v00001025d00001511* + ID_MODEL_FROM_DATABASE=M1511 + +pci:v00001025d00001512* + ID_MODEL_FROM_DATABASE=ALI M1512 Aladdin + +pci:v00001025d00001513* + ID_MODEL_FROM_DATABASE=M1513 + +pci:v00001025d00001521* + ID_MODEL_FROM_DATABASE=ALI M1521 Aladdin III CPU Bridge + +pci:v00001025d00001521sv000010B9sd00001521* + ID_MODEL_FROM_DATABASE=ALI M1521 Aladdin III CPU Bridge + +pci:v00001025d00001523* + ID_MODEL_FROM_DATABASE=ALI M1523 ISA Bridge + +pci:v00001025d00001523sv000010B9sd00001523* + ID_MODEL_FROM_DATABASE=ALI M1523 ISA Bridge + +pci:v00001025d00001531* + ID_MODEL_FROM_DATABASE=M1531 Northbridge [Aladdin IV/IV+] + +pci:v00001025d00001533* + ID_MODEL_FROM_DATABASE=M1533 PCI-to-ISA Bridge + +pci:v00001025d00001533sv000010B9sd00001533* + ID_MODEL_FROM_DATABASE=M1533 PCI-to-ISA Bridge (ALI M1533 Aladdin IV/V ISA South Bridge) + +pci:v00001025d00001535* + ID_MODEL_FROM_DATABASE=M1535 PCI Bridge + Super I/O + FIR + +pci:v00001025d00001541* + ID_MODEL_FROM_DATABASE=M1541 Northbridge [Aladdin V] + +pci:v00001025d00001541sv000010B9sd00001541* + ID_MODEL_FROM_DATABASE=M1541 Northbridge [Aladdin V] (ALI M1541 Aladdin V/V+ AGP+PCI North Bridge) + +pci:v00001025d00001542* + ID_MODEL_FROM_DATABASE=M1542 Northbridge [Aladdin V] + +pci:v00001025d00001543* + ID_MODEL_FROM_DATABASE=M1543 PCI-to-ISA Bridge + Super I/O + FIR + +pci:v00001025d00001561* + ID_MODEL_FROM_DATABASE=M1561 Northbridge [Aladdin 7] + +pci:v00001025d00001621* + ID_MODEL_FROM_DATABASE=M1621 Northbridge [Aladdin-Pro II] + +pci:v00001025d00001631* + ID_MODEL_FROM_DATABASE=M1631 Northbridge+3D Graphics [Aladdin TNT2] + +pci:v00001025d00001641* + ID_MODEL_FROM_DATABASE=M1641 Northbridge [Aladdin-Pro IV] + +pci:v00001025d00001647* + ID_MODEL_FROM_DATABASE=M1647 [MaGiK1] PCI North Bridge + +pci:v00001025d00001671* + ID_MODEL_FROM_DATABASE=M1671 Northbridge [ALADDiN-P4] + +pci:v00001025d00001672* + ID_MODEL_FROM_DATABASE=Northbridge [CyberALADDiN-P4] + +pci:v00001025d00003141* + ID_MODEL_FROM_DATABASE=M3141 + +pci:v00001025d00003143* + ID_MODEL_FROM_DATABASE=M3143 + +pci:v00001025d00003145* + ID_MODEL_FROM_DATABASE=M3145 + +pci:v00001025d00003147* + ID_MODEL_FROM_DATABASE=M3147 + +pci:v00001025d00003149* + ID_MODEL_FROM_DATABASE=M3149 + +pci:v00001025d00003151* + ID_MODEL_FROM_DATABASE=M3151 + +pci:v00001025d00003307* + ID_MODEL_FROM_DATABASE=M3307 MPEG-I Video Controller + +pci:v00001025d00003309* + ID_MODEL_FROM_DATABASE=M3309 MPEG-II Video w/ Software Audio Decoder + +pci:v00001025d00003321* + ID_MODEL_FROM_DATABASE=M3321 MPEG-II Audio/Video Decoder + +pci:v00001025d00005212* + ID_MODEL_FROM_DATABASE=M4803 + +pci:v00001025d00005215* + ID_MODEL_FROM_DATABASE=ALI PCI EIDE Controller + +pci:v00001025d00005217* + ID_MODEL_FROM_DATABASE=M5217H + +pci:v00001025d00005219* + ID_MODEL_FROM_DATABASE=M5219 + +pci:v00001025d00005225* + ID_MODEL_FROM_DATABASE=M5225 + +pci:v00001025d00005229* + ID_MODEL_FROM_DATABASE=M5229 + +pci:v00001025d00005235* + ID_MODEL_FROM_DATABASE=M5235 + +pci:v00001025d00005237* + ID_MODEL_FROM_DATABASE=M5237 PCI USB Host Controller + +pci:v00001025d00005240* + ID_MODEL_FROM_DATABASE=EIDE Controller + +pci:v00001025d00005241* + ID_MODEL_FROM_DATABASE=PCMCIA Bridge + +pci:v00001025d00005242* + ID_MODEL_FROM_DATABASE=General Purpose Controller + +pci:v00001025d00005243* + ID_MODEL_FROM_DATABASE=PCI to PCI Bridge Controller + +pci:v00001025d00005244* + ID_MODEL_FROM_DATABASE=Floppy Disk Controller + +pci:v00001025d00005247* + ID_MODEL_FROM_DATABASE=M1541 PCI to PCI Bridge + +pci:v00001025d00005251* + ID_MODEL_FROM_DATABASE=M5251 P1394 Controller + +pci:v00001025d00005427* + ID_MODEL_FROM_DATABASE=PCI to AGP Bridge + +pci:v00001025d00005451* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device + +pci:v00001025d00005453* + ID_MODEL_FROM_DATABASE=M5453 PCI AC-Link Controller Modem Device + +pci:v00001025d00007101* + ID_MODEL_FROM_DATABASE=M7101 PCI PMU Power Management Controller + +pci:v00001025d00007101sv000010B9sd00007101* + ID_MODEL_FROM_DATABASE=M7101 PCI PMU Power Management Controller + +pci:v00001025d00009602* + ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx) + +pci:v00001028* + ID_VENDOR_FROM_DATABASE=Dell + +pci:v00001028d00000001* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 2/Si + +pci:v00001028d00000001sv00001028sd00000001* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 2/Si (PowerEdge 2400) + +pci:v00001028d00000002* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di + +pci:v00001028d00000002sv00001028sd00000002* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PowerEdge 4400) + +pci:v00001028d00000002sv00001028sd000000D1* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PERC 3/DiV [Viper]) + +pci:v00001028d00000002sv00001028sd000000D9* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PERC 3/DiL [Lexus]) + +pci:v00001028d00000003* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Si + +pci:v00001028d00000003sv00001028sd00000003* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Si (PowerEdge 2450) + +pci:v00001028d00000004* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di [Iguana] + +pci:v00001028d00000004sv00001028sd00000004* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di [Iguana] (PERC 3/DiF [Iguana]) + +pci:v00001028d00000006* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di + +pci:v00001028d00000007* + ID_MODEL_FROM_DATABASE=Remote Access Card III + +pci:v00001028d00000008* + ID_MODEL_FROM_DATABASE=Remote Access Card III + +pci:v00001028d00000009* + ID_MODEL_FROM_DATABASE=Remote Access Card III: BMC/SMIC device not present + +pci:v00001028d0000000A* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di + +pci:v00001028d0000000Asv00001028sd00000106* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PERC 3/DiJ [Jaguar]) + +pci:v00001028d0000000Asv00001028sd0000011B* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PERC 3/DiD [Dagger]) + +pci:v00001028d0000000Asv00001028sd00000121* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID Controller 3/Di (PERC 3/DiB [Boxster]) + +pci:v00001028d0000000C* + ID_MODEL_FROM_DATABASE=Embedded Remote Access or ERA/O + +pci:v00001028d0000000D* + ID_MODEL_FROM_DATABASE=Embedded Remote Access: BMC/SMIC device + +pci:v00001028d0000000E* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4/Di + +pci:v00001028d0000000F* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4/Di + +pci:v00001028d0000000Fsv00001028sd0000014A* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4/Di (PowerEdge 1750) + +pci:v00001028d00000010* + ID_MODEL_FROM_DATABASE=Remote Access Card 4 + +pci:v00001028d00000011* + ID_MODEL_FROM_DATABASE=Remote Access Card 4 Daughter Card + +pci:v00001028d00000012* + ID_MODEL_FROM_DATABASE=Remote Access Card 4 Daughter Card Virtual UART + +pci:v00001028d00000013* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 + +pci:v00001028d00000013sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 (PowerEdge Expandable RAID Controller 4e/Si) + +pci:v00001028d00000013sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 (PowerEdge Expandable RAID Controller 4e/Di) + +pci:v00001028d00000013sv00001028sd0000016E* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 (PowerEdge Expandable RAID Controller 4e/Di) + +pci:v00001028d00000013sv00001028sd0000016F* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 (PowerEdge Expandable RAID Controller 4e/Di) + +pci:v00001028d00000013sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 4 (PowerEdge Expandable RAID Controller 4e/Di) + +pci:v00001028d00000014* + ID_MODEL_FROM_DATABASE=Remote Access Card 4 Daughter Card SMIC interface + +pci:v00001028d00000015* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 5 + +pci:v00001028d00000015sv00001028sd00001F01* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 5 (PERC 5/E Adapter RAID Controller) + +pci:v00001028d00000015sv00001028sd00001F02* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 5 (PERC 5/i Adapter RAID Controller) + +pci:v00001028d00000015sv00001028sd00001F03* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller 5 (PERC 5/i Integrated RAID Controller) + +pci:v00001028d00000016* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller S300 + +pci:v00001028d00000016sv00001028sd00001F24* + ID_MODEL_FROM_DATABASE=PowerEdge Expandable RAID controller S300 (PERC S300 Controller) + +pci:v00001028d00000073* + ID_MODEL_FROM_DATABASE=NV-RAM Adapter + +pci:v00001029* + ID_VENDOR_FROM_DATABASE=Siemens Nixdorf IS + +pci:v0000102A* + ID_VENDOR_FROM_DATABASE=LSI Logic + +pci:v0000102Ad00000000* + ID_MODEL_FROM_DATABASE=HYDRA + +pci:v0000102Ad00000010* + ID_MODEL_FROM_DATABASE=ASPEN + +pci:v0000102Ad0000001F* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W /7890/7891 SCSI Controllers + +pci:v0000102Ad0000001Fsv00009005sd0000000F* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W /7890/7891 SCSI Controllers (2940U2W SCSI Controller) + +pci:v0000102Ad0000001Fsv00009005sd00000106* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W /7890/7891 SCSI Controllers (2940U2W SCSI Controller) + +pci:v0000102Ad0000001Fsv00009005sd0000A180* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W /7890/7891 SCSI Controllers (2940U2W SCSI Controller) + +pci:v0000102Ad000000C5* + ID_MODEL_FROM_DATABASE=AIC-7899 U160/m SCSI Controller + +pci:v0000102Ad000000C5sv00001028sd000000C5* + ID_MODEL_FROM_DATABASE=AIC-7899 U160/m SCSI Controller (PowerEdge 2550/2650/4600) + +pci:v0000102Ad000000CF* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m + +pci:v0000102Ad000000CFsv00001028sd00000106* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (PowerEdge 4600) + +pci:v0000102Ad000000CFsv00001028sd00000121* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (PowerEdge 2650) + +pci:v0000102B* + ID_VENDOR_FROM_DATABASE=Matrox Electronics Systems Ltd. + +pci:v0000102Bd00000010* + ID_MODEL_FROM_DATABASE=MGA-I [Impression?] + +pci:v0000102Bd00000100* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] + +pci:v0000102Bd00000518* + ID_MODEL_FROM_DATABASE=MGA-II [Athena] + +pci:v0000102Bd00000519* + ID_MODEL_FROM_DATABASE=MGA 2064W [Millennium] + +pci:v0000102Bd0000051A* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] + +pci:v0000102Bd0000051Asv0000102Bsd00000100* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] (MGA-1064SG Mystique) + +pci:v0000102Bd0000051Asv0000102Bsd00001100* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] (MGA-1084SG Mystique) + +pci:v0000102Bd0000051Asv0000102Bsd00001200* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] (MGA-1084SG Mystique) + +pci:v0000102Bd0000051Asv00001100sd0000102B* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] (MGA-1084SG Mystique) + +pci:v0000102Bd0000051Asv0000110Asd00000018* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] (Scenic Pro C5 (D1025)) + +pci:v0000102Bd0000051B* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] + +pci:v0000102Bd0000051Bsv0000102Bsd0000051B* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] (MGA-2164W Millennium II) + +pci:v0000102Bd0000051Bsv0000102Bsd00001100* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] (MGA-2164W Millennium II) + +pci:v0000102Bd0000051Bsv0000102Bsd00001200* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] (MGA-2164W Millennium II) + +pci:v0000102Bd0000051Bsv0000102Bsd00002100* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] (MGA-2164W Millennium II) + +pci:v0000102Bd0000051E* + ID_MODEL_FROM_DATABASE=MGA 1064SG [Mystique] AGP + +pci:v0000102Bd0000051F* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] AGP + +pci:v0000102Bd0000051Fsv0000102Bsd00002100* + ID_MODEL_FROM_DATABASE=MGA 2164W [Millennium II] AGP (MGA-2164WA [Millennium II A]) + +pci:v0000102Bd00000520* + ID_MODEL_FROM_DATABASE=MGA G200 + +pci:v0000102Bd00000520sv0000102Bsd0000DBC2* + ID_MODEL_FROM_DATABASE=MGA G200 (G200 Multi-Monitor) + +pci:v0000102Bd00000520sv0000102Bsd0000DBC8* + ID_MODEL_FROM_DATABASE=MGA G200 (G200 Multi-Monitor) + +pci:v0000102Bd00000520sv0000102Bsd0000DBE2* + ID_MODEL_FROM_DATABASE=MGA G200 (G200 Multi-Monitor) + +pci:v0000102Bd00000520sv0000102Bsd0000DBE8* + ID_MODEL_FROM_DATABASE=MGA G200 (G200 Multi-Monitor) + +pci:v0000102Bd00000520sv0000102Bsd0000FF03* + ID_MODEL_FROM_DATABASE=MGA G200 (Millennium G200 SD) + +pci:v0000102Bd00000520sv0000102Bsd0000FF04* + ID_MODEL_FROM_DATABASE=MGA G200 (Marvel G200) + +pci:v0000102Bd00000521* + ID_MODEL_FROM_DATABASE=MGA G200 AGP + +pci:v0000102Bd00000521sv00001014sd0000FF03* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd000048E9* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Mystique G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd000048F8* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 SD AGP) + +pci:v0000102Bd00000521sv0000102Bsd00004A60* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 LE AGP) + +pci:v0000102Bd00000521sv0000102Bsd00004A64* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000C93C* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000C9B0* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000C9BC* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000CA60* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G250 LE AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000CA6C* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G250 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000DBBC* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000DBC2* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 MMS (Dual G200)) + +pci:v0000102Bd00000521sv0000102Bsd0000DBC3* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBC8* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 MMS (Dual G200)) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD2* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD3* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD4* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD5* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD8* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBD9* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBE2* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 MMS (Quad G200)) + +pci:v0000102Bd00000521sv0000102Bsd0000DBE3* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBE8* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200 MMS (Quad G200)) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF2* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF3* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF4* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF5* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF8* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000DBF9* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (G200 Multi-Monitor) + +pci:v0000102Bd00000521sv0000102Bsd0000F806* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Mystique G200 Video AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000FF00* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (MGA-G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000FF02* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Mystique G200 AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000FF03* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Millennium G200A AGP) + +pci:v0000102Bd00000521sv0000102Bsd0000FF04* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (Marvel G200 AGP) + +pci:v0000102Bd00000521sv0000110Asd00000032* + ID_MODEL_FROM_DATABASE=MGA G200 AGP (MGA-G200 AGP) + +pci:v0000102Bd00000522* + ID_MODEL_FROM_DATABASE=MGA G200e [Pilot] ServerEngines (SEP1) + +pci:v0000102Bd00000522sv0000103Csd000031FA* + ID_MODEL_FROM_DATABASE=MGA G200e [Pilot] ServerEngines (SEP1) (ProLiant DL140 G3) + +pci:v0000102Bd00000525* + ID_MODEL_FROM_DATABASE=MGA G400/G450 + +pci:v0000102Bd00000525sv00000E11sd0000B16F* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (MGA-G400 AGP) + +pci:v0000102Bd00000525sv0000102Bsd00000328* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 16Mb SDRAM) + +pci:v0000102Bd00000525sv0000102Bsd00000338* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 16Mb SDRAM) + +pci:v0000102Bd00000525sv0000102Bsd00000378* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 32Mb SDRAM) + +pci:v0000102Bd00000525sv0000102Bsd00000541* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Dual Head) + +pci:v0000102Bd00000525sv0000102Bsd00000542* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Dual Head LX) + +pci:v0000102Bd00000525sv0000102Bsd00000543* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Single Head LX) + +pci:v0000102Bd00000525sv0000102Bsd00000641* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32Mb SDRAM Dual Head) + +pci:v0000102Bd00000525sv0000102Bsd00000642* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32Mb SDRAM Dual Head LX) + +pci:v0000102Bd00000525sv0000102Bsd00000643* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32Mb SDRAM Single Head LX) + +pci:v0000102Bd00000525sv0000102Bsd000007C0* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Dual Head LE) + +pci:v0000102Bd00000525sv0000102Bsd000007C1* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 SDR Dual Head LE) + +pci:v0000102Bd00000525sv0000102Bsd00000D41* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Dual Head PCI) + +pci:v0000102Bd00000525sv0000102Bsd00000D42* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Dual Head LX PCI) + +pci:v0000102Bd00000525sv0000102Bsd00000D43* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32Mb Dual Head PCI) + +pci:v0000102Bd00000525sv0000102Bsd00000E00* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Marvel G450 eTV) + +pci:v0000102Bd00000525sv0000102Bsd00000E01* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Marvel G450 eTV) + +pci:v0000102Bd00000525sv0000102Bsd00000E02* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Marvel G450 eTV) + +pci:v0000102Bd00000525sv0000102Bsd00000E03* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Marvel G450 eTV) + +pci:v0000102Bd00000525sv0000102Bsd00000F80* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Low Profile) + +pci:v0000102Bd00000525sv0000102Bsd00000F81* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Low Profile) + +pci:v0000102Bd00000525sv0000102Bsd00000F82* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Low Profile DVI) + +pci:v0000102Bd00000525sv0000102Bsd00000F83* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 Low Profile DVI) + +pci:v0000102Bd00000525sv0000102Bsd000019D8* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 16Mb SGRAM) + +pci:v0000102Bd00000525sv0000102Bsd000019F8* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 32Mb SGRAM) + +pci:v0000102Bd00000525sv0000102Bsd00002159* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 Dual Head 16Mb) + +pci:v0000102Bd00000525sv0000102Bsd00002179* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 MAX/Dual Head 32Mb) + +pci:v0000102Bd00000525sv0000102Bsd0000217D* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 Dual Head Max) + +pci:v0000102Bd00000525sv0000102Bsd000023C0* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450) + +pci:v0000102Bd00000525sv0000102Bsd000023C1* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450) + +pci:v0000102Bd00000525sv0000102Bsd000023C2* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 DVI) + +pci:v0000102Bd00000525sv0000102Bsd000023C3* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 DVI) + +pci:v0000102Bd00000525sv0000102Bsd00002F58* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400) + +pci:v0000102Bd00000525sv0000102Bsd00002F78* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400) + +pci:v0000102Bd00000525sv0000102Bsd00003693* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Marvel G400 AGP) + +pci:v0000102Bd00000525sv0000102Bsd00005DD0* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (4Sight II) + +pci:v0000102Bd00000525sv0000102Bsd00005F50* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (4Sight II) + +pci:v0000102Bd00000525sv0000102Bsd00005F51* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (4Sight II) + +pci:v0000102Bd00000525sv0000102Bsd00005F52* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (4Sight II) + +pci:v0000102Bd00000525sv0000102Bsd00009010* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G400 Dual Head) + +pci:v0000102Bd00000525sv00001458sd00000400* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (GA-G400) + +pci:v0000102Bd00000525sv00001705sd00000001* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32MB SGRAM) + +pci:v0000102Bd00000525sv00001705sd00000002* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 16MB SGRAM) + +pci:v0000102Bd00000525sv00001705sd00000003* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 32MB) + +pci:v0000102Bd00000525sv00001705sd00000004* + ID_MODEL_FROM_DATABASE=MGA G400/G450 (Millennium G450 16MB) + +pci:v0000102Bd00000527* + ID_MODEL_FROM_DATABASE=Parhelia + +pci:v0000102Bd00000527sv0000102Bsd00000840* + ID_MODEL_FROM_DATABASE=Parhelia (128Mb) + +pci:v0000102Bd00000527sv0000102Bsd00000850* + ID_MODEL_FROM_DATABASE=Parhelia (256MB) + +pci:v0000102Bd00000527sv0000102Bsd00000870* + ID_MODEL_FROM_DATABASE=Parhelia (MED2mp-DVI) + +pci:v0000102Bd00000527sv0000102Bsd00000880* + ID_MODEL_FROM_DATABASE=Parhelia (P-256 Edge Overlap Controller) + +pci:v0000102Bd00000528* + ID_MODEL_FROM_DATABASE=Parhelia + +pci:v0000102Bd00000528sv0000102Bsd00001020* + ID_MODEL_FROM_DATABASE=Parhelia (128MB) + +pci:v0000102Bd00000528sv0000102Bsd00001030* + ID_MODEL_FROM_DATABASE=Parhelia (256 MB Dual DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001040* + ID_MODEL_FROM_DATABASE=Parhelia (MED2mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001050* + ID_MODEL_FROM_DATABASE=Parhelia (Sono S20) + +pci:v0000102Bd00000528sv0000102Bsd00001060* + ID_MODEL_FROM_DATABASE=Parhelia (PJ-30L) + +pci:v0000102Bd00000528sv0000102Bsd00001070* + ID_MODEL_FROM_DATABASE=Parhelia (PJ-40L) + +pci:v0000102Bd00000528sv0000102Bsd00001421* + ID_MODEL_FROM_DATABASE=Parhelia (MED5mp) + +pci:v0000102Bd00000528sv0000102Bsd00001431* + ID_MODEL_FROM_DATABASE=Parhelia (MED3mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001451* + ID_MODEL_FROM_DATABASE=Parhelia (MED5mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001491* + ID_MODEL_FROM_DATABASE=Parhelia (MED2mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd000014B1* + ID_MODEL_FROM_DATABASE=Parhelia (MED3mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd000014C1* + ID_MODEL_FROM_DATABASE=Parhelia (MED5mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd000014E1* + ID_MODEL_FROM_DATABASE=Parhelia (PCI 256MB) + +pci:v0000102Bd00000528sv0000102Bsd000014F1* + ID_MODEL_FROM_DATABASE=Parhelia (Precision SGT) + +pci:v0000102Bd00000528sv0000102Bsd00001501* + ID_MODEL_FROM_DATABASE=Parhelia (ATC-4MP) + +pci:v0000102Bd00000528sv0000102Bsd00001511* + ID_MODEL_FROM_DATABASE=Parhelia (ATC-4MP) + +pci:v0000102Bd00000528sv0000102Bsd00001521* + ID_MODEL_FROM_DATABASE=Parhelia (TheatreVUE T30) + +pci:v0000102Bd00000528sv0000102Bsd00001531* + ID_MODEL_FROM_DATABASE=Parhelia (TheatreVUE T20) + +pci:v0000102Bd00000528sv0000102Bsd00001541* + ID_MODEL_FROM_DATABASE=Parhelia (MED2mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001551* + ID_MODEL_FROM_DATABASE=Parhelia (MED3mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001561* + ID_MODEL_FROM_DATABASE=Parhelia (MED5mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00001571* + ID_MODEL_FROM_DATABASE=Parhelia (DL256 PCI) + +pci:v0000102Bd00000528sv0000102Bsd00001591* + ID_MODEL_FROM_DATABASE=Parhelia (Precision SDT) + +pci:v0000102Bd00000528sv0000102Bsd000015A1* + ID_MODEL_FROM_DATABASE=Parhelia (MED4mp-DVI) + +pci:v0000102Bd00000528sv0000102Bsd00002011* + ID_MODEL_FROM_DATABASE=Parhelia (HR256) + +pci:v0000102Bd00000528sv0000102Bsd00002021* + ID_MODEL_FROM_DATABASE=Parhelia (QID Pro) + +pci:v0000102Bd00000528sv0000102Bsd00002061* + ID_MODEL_FROM_DATABASE=Parhelia (PJ-40LP) + +pci:v0000102Bd00000528sv0000102Bsd00002081* + ID_MODEL_FROM_DATABASE=Parhelia (EWS Quad) + +pci:v0000102Bd00000528sv0000102Bsd00002411* + ID_MODEL_FROM_DATABASE=Parhelia (PPX-OUT8) + +pci:v0000102Bd00000528sv0000102Bsd00002421* + ID_MODEL_FROM_DATABASE=Parhelia (VPX-OUT8) + +pci:v0000102Bd00000528sv0000102Bsd00002441* + ID_MODEL_FROM_DATABASE=Parhelia (PPX-OUT4) + +pci:v0000102Bd00000528sv0000102Bsd00002451* + ID_MODEL_FROM_DATABASE=Parhelia (VPX-OUT4) + +pci:v0000102Bd00000528sv0000102Bsd00002491* + ID_MODEL_FROM_DATABASE=Parhelia (LPX-OUT4) + +pci:v0000102Bd00000530* + ID_MODEL_FROM_DATABASE=MGA G200EV + +pci:v0000102Bd00000532* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 + +pci:v0000102Bd00000532sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge R710 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge R610 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge T610 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge M610 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge R410 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge T410 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge M710 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv00001028sd000002A4* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (PowerEdge T310 MGA G200eW WPCM450) + +pci:v0000102Bd00000532sv000015D9sd00000624* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (X9SCM-F Motherboard) + +pci:v0000102Bd00000532sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=MGA G200eW WPCM450 (H8DGU) + +pci:v0000102Bd00000533* + ID_MODEL_FROM_DATABASE=MGA G200EH + +pci:v0000102Bd00000533sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=MGA G200EH (iLO4) + +pci:v0000102Bd00000534* + ID_MODEL_FROM_DATABASE=G200eR2 + +pci:v0000102Bd00000540* + ID_MODEL_FROM_DATABASE=M91XX + +pci:v0000102Bd00000540sv0000102Bsd00002080* + ID_MODEL_FROM_DATABASE=M91XX (M9140 LP PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd000020C0* + ID_MODEL_FROM_DATABASE=M91XX (Xenia) + +pci:v0000102Bd00000540sv0000102Bsd000020C1* + ID_MODEL_FROM_DATABASE=M91XX (Xenia Pro) + +pci:v0000102Bd00000540sv0000102Bsd00002100* + ID_MODEL_FROM_DATABASE=M91XX (M9120 PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd00002140* + ID_MODEL_FROM_DATABASE=M91XX (M9125 PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd00002180* + ID_MODEL_FROM_DATABASE=M91XX (M9120 Plus LP PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd000021C0* + ID_MODEL_FROM_DATABASE=M91XX (M9120 Plus LP PCIe x1) + +pci:v0000102Bd00000540sv0000102Bsd00002200* + ID_MODEL_FROM_DATABASE=M91XX (VDA1164 Output Board) + +pci:v0000102Bd00000540sv0000102Bsd00002240* + ID_MODEL_FROM_DATABASE=M91XX (M9148 LP PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd00002241* + ID_MODEL_FROM_DATABASE=M91XX (M9138 LP PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd00002280* + ID_MODEL_FROM_DATABASE=M91XX (M9188 ATX PCIe x16) + +pci:v0000102Bd00000540sv0000102Bsd000022C0* + ID_MODEL_FROM_DATABASE=M91XX (M9128 LP PCIe x16) + +pci:v0000102Bd00000D10* + ID_MODEL_FROM_DATABASE=MGA Ultima/Impression + +pci:v0000102Bd00001000* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] + +pci:v0000102Bd00001000sv0000102Bsd0000FF01* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] (Productiva G100) + +pci:v0000102Bd00001000sv0000102Bsd0000FF05* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] (Productiva G100 Multi-Monitor) + +pci:v0000102Bd00001001* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP + +pci:v0000102Bd00001001sv0000102Bsd00001001* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 AGP) + +pci:v0000102Bd00001001sv0000102Bsd0000FF00* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 AGP) + +pci:v0000102Bd00001001sv0000102Bsd0000FF01* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 Productiva AGP) + +pci:v0000102Bd00001001sv0000102Bsd0000FF03* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (Millennium G100 AGP) + +pci:v0000102Bd00001001sv0000102Bsd0000FF04* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 AGP) + +pci:v0000102Bd00001001sv0000102Bsd0000FF05* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 Productiva AGP Multi-Monitor) + +pci:v0000102Bd00001001sv0000110Asd0000001E* + ID_MODEL_FROM_DATABASE=MGA G100 [Productiva] AGP (MGA-G100 AGP) + +pci:v0000102Bd00002007* + ID_MODEL_FROM_DATABASE=MGA Mistral + +pci:v0000102Bd00002527* + ID_MODEL_FROM_DATABASE=Millennium G550 + +pci:v0000102Bd00002527sv0000102Bsd00000F42* + ID_MODEL_FROM_DATABASE=Millennium G550 (Matrox G550 Low Profile PCI) + +pci:v0000102Bd00002527sv0000102Bsd00000F83* + ID_MODEL_FROM_DATABASE=Millennium G550 + +pci:v0000102Bd00002527sv0000102Bsd00000F84* + ID_MODEL_FROM_DATABASE=Millennium G550 (Dual Head DDR 32Mb) + +pci:v0000102Bd00002527sv0000102Bsd00001E41* + ID_MODEL_FROM_DATABASE=Millennium G550 + +pci:v0000102Bd00002527sv0000102Bsd00002300* + ID_MODEL_FROM_DATABASE=Millennium G550 (LP PCIE) + +pci:v0000102Bd00002537* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 + +pci:v0000102Bd00002537sv0000102Bsd00001820* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Millennium P750 64MB) + +pci:v0000102Bd00002537sv0000102Bsd00001830* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Millennium P650 64MB) + +pci:v0000102Bd00002537sv0000102Bsd00001850* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (RAD2mp) + +pci:v0000102Bd00002537sv0000102Bsd00001860* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (RAD3mp) + +pci:v0000102Bd00002537sv0000102Bsd00001880* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Sono S10) + +pci:v0000102Bd00002537sv0000102Bsd00001C10* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (QID 128MB) + +pci:v0000102Bd00002537sv0000102Bsd00002811* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Millennium P650 Low-profile PCI 64MB) + +pci:v0000102Bd00002537sv0000102Bsd00002821* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Millenium P650 Low-profile PCI) + +pci:v0000102Bd00002537sv0000102Bsd00002841* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (RAD PCI) + +pci:v0000102Bd00002537sv0000102Bsd00002851* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Spectrum PCI) + +pci:v0000102Bd00002537sv0000102Bsd00002871* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (EpicA TC2) + +pci:v0000102Bd00002537sv0000102Bsd00002C11* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (QID Low-profile PCI) + +pci:v0000102Bd00002537sv0000102Bsd00002C21* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (QID LP PCI LW) + +pci:v0000102Bd00002537sv0000102Bsd00002C31* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (QID LP PCI) + +pci:v0000102Bd00002537sv0000102Bsd00002C41* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (EpicA TC4) + +pci:v0000102Bd00002537sv0000102Bsd00003001* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Extio F1400) + +pci:v0000102Bd00002537sv0000102Bsd00003011* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Extio F1220) + +pci:v0000102Bd00002537sv0000102Bsd00003041* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (RG-200DL) + +pci:v0000102Bd00002537sv0000102Bsd00003051* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (RG-400SL) + +pci:v0000102Bd00002537sv0000102Bsd00003061* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Extio F1420) + +pci:v0000102Bd00002537sv0000102Bsd00003081* + ID_MODEL_FROM_DATABASE=Millenium P650/P750 (Extio F1240) + +pci:v0000102Bd00002538* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe + +pci:v0000102Bd00002538sv0000102Bsd00000847* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (RAD PCIe) + +pci:v0000102Bd00002538sv0000102Bsd000008C7* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Millennium P650 PCIe 128MB) + +pci:v0000102Bd00002538sv0000102Bsd00000907* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Millennium P650 PCIe 64MB) + +pci:v0000102Bd00002538sv0000102Bsd00000947* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Parhelia APVe) + +pci:v0000102Bd00002538sv0000102Bsd00000987* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (ATC PCIe 4MP) + +pci:v0000102Bd00002538sv0000102Bsd00001047* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Millennium P650 LP PCIe 128MB) + +pci:v0000102Bd00002538sv0000102Bsd00001087* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Millennium P650 LP PCIe 64MB) + +pci:v0000102Bd00002538sv0000102Bsd00001801* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (x1) + +pci:v0000102Bd00002538sv0000102Bsd00002538* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Parhelia APVe) + +pci:v0000102Bd00002538sv0000102Bsd00003007* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (QID Low-profile PCIe) + +pci:v0000102Bd00002538sv0000102Bsd00003087* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (Aurora VX3mp) + +pci:v0000102Bd00002538sv0000102Bsd000030C7* + ID_MODEL_FROM_DATABASE=Millenium P650 PCIe (QID LP PCIe) + +pci:v0000102Bd00002539* + ID_MODEL_FROM_DATABASE=Millennium P690 + +pci:v0000102Bd00002539sv0000102Bsd00000040* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 PCIe x16) + +pci:v0000102Bd00002539sv0000102Bsd00000042* + ID_MODEL_FROM_DATABASE=Millennium P690 (ONYX) + +pci:v0000102Bd00002539sv0000102Bsd00000043* + ID_MODEL_FROM_DATABASE=Millennium P690 (SPECTRA) + +pci:v0000102Bd00002539sv0000102Bsd00000080* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 Plus LP PCIe x16) + +pci:v0000102Bd00002539sv0000102Bsd00000081* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 LP PCIe x16) + +pci:v0000102Bd00002539sv0000102Bsd00000082* + ID_MODEL_FROM_DATABASE=Millennium P690 (RAD LPX PCIe x16) + +pci:v0000102Bd00002539sv0000102Bsd000000C0* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 Plus LP PCI) + +pci:v0000102Bd00002539sv0000102Bsd000000C2* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 LP PCI) + +pci:v0000102Bd00002539sv0000102Bsd000000C3* + ID_MODEL_FROM_DATABASE=Millennium P690 (RAD LPX PCI) + +pci:v0000102Bd00002539sv0000102Bsd00000101* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 PCI) + +pci:v0000102Bd00002539sv0000102Bsd00000140* + ID_MODEL_FROM_DATABASE=Millennium P690 (Millenium P690 LP PCIe x1) + +pci:v0000102Bd00002539sv0000102Bsd00000180* + ID_MODEL_FROM_DATABASE=Millennium P690 (Display Wall IP Decode 128 MB) + +pci:v0000102Bd00004164* + ID_MODEL_FROM_DATABASE=Morphis QxT frame grabber + +pci:v0000102Bd000043B4* + ID_MODEL_FROM_DATABASE=Morphis Qxt encoding engine + +pci:v0000102Bd00004510* + ID_MODEL_FROM_DATABASE=Morphis COM port + +pci:v0000102Bd00004536* + ID_MODEL_FROM_DATABASE=VIA Framegrabber + +pci:v0000102Bd00004686* + ID_MODEL_FROM_DATABASE=Concord GX (customized Intel 82541) + +pci:v0000102Bd0000475B* + ID_MODEL_FROM_DATABASE=Solios eCL/XCL-B frame grabber + +pci:v0000102Bd0000475D* + ID_MODEL_FROM_DATABASE=Vio frame grabber family + +pci:v0000102Bd0000475Dsv0000102Bsd00004B90* + ID_MODEL_FROM_DATABASE=Vio frame grabber family (Vio Duo frame grabber (single channel)) + +pci:v0000102Bd0000475Dsv0000102Bsd00004B91* + ID_MODEL_FROM_DATABASE=Vio frame grabber family (Vio Duo frame grabber) + +pci:v0000102Bd0000475Dsv0000102Bsd00004B92* + ID_MODEL_FROM_DATABASE=Vio frame grabber family (Vio Analog frame grabber) + +pci:v0000102Bd0000475Dsv0000102Bsd00004B93* + ID_MODEL_FROM_DATABASE=Vio frame grabber family (Vio SDI Frame Grabber) + +pci:v0000102Bd0000475Dsv0000102Bsd00004B94* + ID_MODEL_FROM_DATABASE=Vio frame grabber family (Vio DVI-A frame grabber) + +pci:v0000102Bd0000475F* + ID_MODEL_FROM_DATABASE=Solios (single-Full) CL frame grabber + +pci:v0000102Bd0000475Fsv0000102Bsd0000475F* + ID_MODEL_FROM_DATABASE=Solios (single-Full) CL frame grabber (Solios eCL/XCL-F frame grabber) + +pci:v0000102Bd0000475Fsv0000102Bsd00004D5F* + ID_MODEL_FROM_DATABASE=Solios (single-Full) CL frame grabber (Solios eV-CL (single-Full) frame grabber) + +pci:v0000102Bd0000475Fsv0000102Bsd00004E5F* + ID_MODEL_FROM_DATABASE=Solios (single-Full) CL frame grabber (Solios eM-CL (single-Full) frame grabber) + +pci:v0000102Bd000047A1* + ID_MODEL_FROM_DATABASE=Solios eA/XA frame grabber + +pci:v0000102Bd000047A1sv0000102Bsd00004BE0* + ID_MODEL_FROM_DATABASE=Solios eA/XA frame grabber (Solios eA/XA (single) frame grabber) + +pci:v0000102Bd000047A1sv0000102Bsd00004BE1* + ID_MODEL_FROM_DATABASE=Solios eA/XA frame grabber (Solios eA/XA (dual) frame grabber) + +pci:v0000102Bd000047A1sv0000102Bsd00004BE2* + ID_MODEL_FROM_DATABASE=Solios eA/XA frame grabber (Solios eA/XA (quad) frame grabber) + +pci:v0000102Bd000047A2* + ID_MODEL_FROM_DATABASE=Solios COM port + +pci:v0000102Bd000047C1* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber + +pci:v0000102Bd000047C1sv0000102Bsd00000000* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004B80* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eCL/XCL (single-Medium) frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004B81* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eCL/XCL (dual-Base) frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004D80* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eV-CL (single-Medium) frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004D81* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eV-CL (dual-Base) frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004E80* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eM-CL (single-Medium) frame grabber) + +pci:v0000102Bd000047C1sv0000102Bsd00004E81* + ID_MODEL_FROM_DATABASE=Solios (dual-Base/single-Medium) CL frame grabber (Solios eM-CL (dual-Base) frame grabber) + +pci:v0000102Bd000047C2* + ID_MODEL_FROM_DATABASE=Solios COM port + +pci:v0000102Bd00004949* + ID_MODEL_FROM_DATABASE=Radient frame grabber family + +pci:v0000102Bd00004949sv0000102Bsd00000010* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCL (Single-full) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00000011* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCLV (Single-full) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00000020* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCL (Dual-base) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00000030* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCL (Dual-full) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00000040* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCL (Quad-base) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00000050* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eCL (Golden) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00001010* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eV-CXP (quad CXP-6) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00001015* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eV-CXP (dual CXP-6) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00001020* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eV-CXP (quad CXP-3) frame grabber) + +pci:v0000102Bd00004949sv0000102Bsd00001050* + ID_MODEL_FROM_DATABASE=Radient frame grabber family (Radient eV-CXP (Golden) frame grabber) + +pci:v0000102Bd00004CDC* + ID_MODEL_FROM_DATABASE=Morphis JPEG2000 accelerator + +pci:v0000102Bd00004F54* + ID_MODEL_FROM_DATABASE=Morphis (e)Quad frame grabber + +pci:v0000102Bd00004FC5* + ID_MODEL_FROM_DATABASE=Morphis (e)Dual frame grabber + +pci:v0000102Bd00005E10* + ID_MODEL_FROM_DATABASE=Morphis aux I/O + +pci:v0000102Bd00006573* + ID_MODEL_FROM_DATABASE=Shark 10/100 Multiport SwitchNIC + +pci:v0000102C* + ID_VENDOR_FROM_DATABASE=Chips and Technologies + +pci:v0000102Cd000000B8* + ID_MODEL_FROM_DATABASE=F64310 + +pci:v0000102Cd000000C0* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo + +pci:v0000102Cd000000C0sv0000102Csd000000C0* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo + +pci:v0000102Cd000000C0sv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v0000102Cd000000C0sv00004C53sd00001010* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (CP5/CR6 mainboard) + +pci:v0000102Cd000000C0sv00004C53sd00001020* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (VR6 mainboard) + +pci:v0000102Cd000000C0sv00004C53sd00001030* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (PC5 mainboard) + +pci:v0000102Cd000000C0sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (CT7 mainboard) + +pci:v0000102Cd000000C0sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=F69000 HiQVideo (CE7 mainboard) + +pci:v0000102Cd000000D0* + ID_MODEL_FROM_DATABASE=F65545 + +pci:v0000102Cd000000D8* + ID_MODEL_FROM_DATABASE=F65545 + +pci:v0000102Cd000000DC* + ID_MODEL_FROM_DATABASE=F65548 + +pci:v0000102Cd000000E0* + ID_MODEL_FROM_DATABASE=F65550 + +pci:v0000102Cd000000E4* + ID_MODEL_FROM_DATABASE=F65554 + +pci:v0000102Cd000000E5* + ID_MODEL_FROM_DATABASE=F65555 HiQVPro + +pci:v0000102Cd000000E5sv00000E11sd0000B049* + ID_MODEL_FROM_DATABASE=F65555 HiQVPro (Armada 1700 Laptop Display Controller) + +pci:v0000102Cd000000E5sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=F65555 HiQVPro (Satellite Pro/Satellite) + +pci:v0000102Cd000000F0* + ID_MODEL_FROM_DATABASE=F68554 + +pci:v0000102Cd000000F4* + ID_MODEL_FROM_DATABASE=F68554 HiQVision + +pci:v0000102Cd000000F5* + ID_MODEL_FROM_DATABASE=F68555 + +pci:v0000102Cd00000C30* + ID_MODEL_FROM_DATABASE=F69030 + +pci:v0000102Cd00000C30sv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=F69030 (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v0000102Cd00000C30sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=F69030 (CT7 mainboard) + +pci:v0000102Cd00000C30sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=F69030 (CE7 mainboard) + +pci:v0000102Cd00000C30sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=F69030 (CT8 mainboard) + +pci:v0000102D* + ID_VENDOR_FROM_DATABASE=Wyse Technology Inc. + +pci:v0000102Dd000050DC* + ID_MODEL_FROM_DATABASE=3328 Audio + +pci:v0000102E* + ID_VENDOR_FROM_DATABASE=Olivetti Advanced Technology + +pci:v0000102F* + ID_VENDOR_FROM_DATABASE=Toshiba America + +pci:v0000102Fd00000009* + ID_MODEL_FROM_DATABASE=r4x00 + +pci:v0000102Fd0000000A* + ID_MODEL_FROM_DATABASE=TX3927 MIPS RISC PCI Controller + +pci:v0000102Fd00000020* + ID_MODEL_FROM_DATABASE=ATM Meteor 155 + +pci:v0000102Fd00000020sv0000102Fsd000000F8* + ID_MODEL_FROM_DATABASE=ATM Meteor 155 + +pci:v0000102Fd00000030* + ID_MODEL_FROM_DATABASE=TC35815CF PCI 10/100 Mbit Ethernet Controller + +pci:v0000102Fd00000031* + ID_MODEL_FROM_DATABASE=TC35815CF PCI 10/100 Mbit Ethernet Controller with WOL + +pci:v0000102Fd00000032* + ID_MODEL_FROM_DATABASE=TC35815CF PCI 10/100 Mbit Ethernet Controller on TX4939 + +pci:v0000102Fd00000105* + ID_MODEL_FROM_DATABASE=TC86C001 [goku-s] IDE + +pci:v0000102Fd00000106* + ID_MODEL_FROM_DATABASE=TC86C001 [goku-s] USB 1.1 Host + +pci:v0000102Fd00000107* + ID_MODEL_FROM_DATABASE=TC86C001 [goku-s] USB Device Controller + +pci:v0000102Fd00000108* + ID_MODEL_FROM_DATABASE=TC86C001 [goku-s] I2C/SIO/GPIO Controller + +pci:v0000102Fd00000180* + ID_MODEL_FROM_DATABASE=TX4927/38 MIPS RISC PCI Controller + +pci:v0000102Fd00000181* + ID_MODEL_FROM_DATABASE=TX4925 MIPS RISC PCI Controller + +pci:v0000102Fd00000182* + ID_MODEL_FROM_DATABASE=TX4937 MIPS RISC PCI Controller + +pci:v0000102Fd000001B4* + ID_MODEL_FROM_DATABASE=Celleb platform IDE interface + +pci:v0000102Fd000001B5* + ID_MODEL_FROM_DATABASE=SCC USB 2.0 EHCI controller + +pci:v0000102Fd000001B6* + ID_MODEL_FROM_DATABASE=SCC USB 1.1 OHCI controller + +pci:v00001030* + ID_VENDOR_FROM_DATABASE=TMC Research + +pci:v00001031* + ID_VENDOR_FROM_DATABASE=Miro Computer Products AG + +pci:v00001031d00005601* + ID_MODEL_FROM_DATABASE=DC20 ASIC + +pci:v00001031d00005607* + ID_MODEL_FROM_DATABASE=Video I/O & motion JPEG compressor + +pci:v00001031d00005631* + ID_MODEL_FROM_DATABASE=Media 3D + +pci:v00001031d00006057* + ID_MODEL_FROM_DATABASE=MiroVideo DC10/DC30+ + +pci:v00001032* + ID_VENDOR_FROM_DATABASE=Compaq + +pci:v00001033* + ID_VENDOR_FROM_DATABASE=NEC Corporation + +pci:v00001033d00000000* + ID_MODEL_FROM_DATABASE=Vr4181A USB Host or Function Control Unit + +pci:v00001033d00000001* + ID_MODEL_FROM_DATABASE=PCI to 486-like bus Bridge + +pci:v00001033d00000002* + ID_MODEL_FROM_DATABASE=PCI to VL98 Bridge + +pci:v00001033d00000003* + ID_MODEL_FROM_DATABASE=ATM Controller + +pci:v00001033d00000004* + ID_MODEL_FROM_DATABASE=R4000 PCI Bridge + +pci:v00001033d00000005* + ID_MODEL_FROM_DATABASE=PCI to 486-like bus Bridge + +pci:v00001033d00000006* + ID_MODEL_FROM_DATABASE=PC-9800 Graphic Accelerator + +pci:v00001033d00000007* + ID_MODEL_FROM_DATABASE=PCI to UX-Bus Bridge + +pci:v00001033d00000008* + ID_MODEL_FROM_DATABASE=PC-9800 Graphic Accelerator + +pci:v00001033d00000009* + ID_MODEL_FROM_DATABASE=PCI to PC9800 Core-Graph Bridge + +pci:v00001033d00000016* + ID_MODEL_FROM_DATABASE=PCI to VL Bridge + +pci:v00001033d0000001A* + ID_MODEL_FROM_DATABASE=[Nile II] + +pci:v00001033d00000021* + ID_MODEL_FROM_DATABASE=Vrc4373 [Nile I] + +pci:v00001033d00000029* + ID_MODEL_FROM_DATABASE=PowerVR PCX1 + +pci:v00001033d0000002A* + ID_MODEL_FROM_DATABASE=PowerVR 3D + +pci:v00001033d0000002C* + ID_MODEL_FROM_DATABASE=Star Alpha 2 + +pci:v00001033d0000002D* + ID_MODEL_FROM_DATABASE=PCI to C-bus Bridge + +pci:v00001033d00000035* + ID_MODEL_FROM_DATABASE=OHCI USB Controller + +pci:v00001033d00000035sv00001033sd00000035* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (USB Controller) + +pci:v00001033d00000035sv0000103Csd00001293* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (USB add-in card) + +pci:v00001033d00000035sv0000103Csd00001294* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (USB 2.0 add-in card) + +pci:v00001033d00000035sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (USB) + +pci:v00001033d00000035sv00001186sd00000035* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (DUB-C2 USB 2.0 2-port 32-bit cardbus controller) + +pci:v00001033d00000035sv000012EEsd00007000* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (Root Hub) + +pci:v00001033d00000035sv000014C2sd00000105* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (PTI-205N USB 2.0 Host Controller) + +pci:v00001033d00000035sv00001799sd00000001* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (Root Hub) + +pci:v00001033d00000035sv00001931sd0000000A* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (GlobeTrotter Fusion Quad Lite (PPP data)) + +pci:v00001033d00000035sv00001931sd0000000B* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (GlobeTrotter Fusion Quad Lite (GSM data)) + +pci:v00001033d00000035sv0000807Dsd00000035* + ID_MODEL_FROM_DATABASE=OHCI USB Controller (PCI-USB2 (OHCI subsystem)) + +pci:v00001033d0000003B* + ID_MODEL_FROM_DATABASE=PCI to C-bus Bridge + +pci:v00001033d0000003E* + ID_MODEL_FROM_DATABASE=NAPCCARD Cardbus Controller + +pci:v00001033d00000046* + ID_MODEL_FROM_DATABASE=PowerVR PCX2 [midas] + +pci:v00001033d0000005A* + ID_MODEL_FROM_DATABASE=Vrc5074 [Nile 4] + +pci:v00001033d00000063* + ID_MODEL_FROM_DATABASE=uPD72862 [Firewarden] IEEE1394 OHCI 1.0 Link Controller + +pci:v00001033d00000067* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset + +pci:v00001033d00000067sv00001010sd00000020* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 AGP 32Mb) + +pci:v00001033d00000067sv00001010sd00000080* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 AGP 16Mb) + +pci:v00001033d00000067sv00001010sd00000088* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 16Mb) + +pci:v00001033d00000067sv00001010sd00000090* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 AGP 16Mb) + +pci:v00001033d00000067sv00001010sd00000098* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 16Mb) + +pci:v00001033d00000067sv00001010sd000000A0* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 AGP 32Mb) + +pci:v00001033d00000067sv00001010sd000000A8* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 32Mb) + +pci:v00001033d00000067sv00001010sd00000120* + ID_MODEL_FROM_DATABASE=PowerVR Neon 250 Chipset (PowerVR Neon 250 AGP 32Mb) + +pci:v00001033d00000072* + ID_MODEL_FROM_DATABASE=uPD72874 IEEE1394 OHCI 1.1 3-port PHY-Link Ctrlr + +pci:v00001033d00000074* + ID_MODEL_FROM_DATABASE=56k Voice Modem + +pci:v00001033d00000074sv00001033sd00008014* + ID_MODEL_FROM_DATABASE=56k Voice Modem (RCV56ACF 56k Voice Modem) + +pci:v00001033d0000009B* + ID_MODEL_FROM_DATABASE=Vrc5476 + +pci:v00001033d000000A5* + ID_MODEL_FROM_DATABASE=VRC4173 + +pci:v00001033d000000A6* + ID_MODEL_FROM_DATABASE=VRC5477 AC97 + +pci:v00001033d000000CD* + ID_MODEL_FROM_DATABASE=uPD72870 [Firewarden] IEEE1394a OHCI 1.0 Link/3-port PHY Controller + +pci:v00001033d000000CDsv000012EEsd00008011* + ID_MODEL_FROM_DATABASE=uPD72870 [Firewarden] IEEE1394a OHCI 1.0 Link/3-port PHY Controller (Root hub) + +pci:v00001033d000000CE* + ID_MODEL_FROM_DATABASE=uPD72871 [Firewarden] IEEE1394a OHCI 1.0 Link/1-port PHY Controller + +pci:v00001033d000000DF* + ID_MODEL_FROM_DATABASE=Vr4131 + +pci:v00001033d000000E0* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller + +pci:v00001033d000000E0sv00001186sd0000F100* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller (DUB-C2 USB 2.0 2-port 32-bit cardbus controller) + +pci:v00001033d000000E0sv000012EEsd00007001* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller (Root hub) + +pci:v00001033d000000E0sv000014C2sd00000205* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller (PTI-205N USB 2.0 Host Controller) + +pci:v00001033d000000E0sv00001799sd00000002* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller (Root Hub) + +pci:v00001033d000000E0sv0000807Dsd00001043* + ID_MODEL_FROM_DATABASE=uPD72010x USB 2.0 Controller (PCI-USB2 (EHCI subsystem)) + +pci:v00001033d000000E7* + ID_MODEL_FROM_DATABASE=uPD72873 [Firewarden] IEEE1394a OHCI 1.1 Link/2-port PHY Controller + +pci:v00001033d000000F2* + ID_MODEL_FROM_DATABASE=uPD72874 [Firewarden] IEEE1394a OHCI 1.1 Link/3-port PHY Controller + +pci:v00001033d000000F3* + ID_MODEL_FROM_DATABASE=uPD6113x Multimedia Decoder/Processor [EMMA2] + +pci:v00001033d0000010C* + ID_MODEL_FROM_DATABASE=VR7701 + +pci:v00001033d00000125* + ID_MODEL_FROM_DATABASE=uPD720400 PCI Express - PCI/PCI-X Bridge + +pci:v00001033d0000013A* + ID_MODEL_FROM_DATABASE=Dual Tuner/MPEG Encoder + +pci:v00001033d00000194* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller + +pci:v00001033d00000194sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (Precision M4600) + +pci:v00001033d00000194sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (Vostro 3350) + +pci:v00001033d00000194sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (Vostro 3750) + +pci:v00001033d00000194sv00001043sd00008413* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (P8P67 Deluxe Motherboard) + +pci:v00001033d00000194sv0000104Dsd0000907A* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (Vaio VPCF1) + +pci:v00001033d00000194sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (QEMU Virtual Machine) + +pci:v00001033d00000194sv00001B96sd00000001* + ID_MODEL_FROM_DATABASE=uPD720200 USB 3.0 Host Controller (USB 3.0 PCIe Card) + +pci:v00001033d000001E7* + ID_MODEL_FROM_DATABASE=uPD72873 [Firewarden] IEEE1394a OHCI 1.1 Link/2-port PHY Controller + +pci:v00001033d000001F2* + ID_MODEL_FROM_DATABASE=uPD72874 [Firewarden] IEEE1394a OHCI 1.1 Link/3-port PHY Controller + +pci:v00001034* + ID_VENDOR_FROM_DATABASE=Framatome Connectors USA Inc. + +pci:v00001035* + ID_VENDOR_FROM_DATABASE=Comp. & Comm. Research Lab + +pci:v00001036* + ID_VENDOR_FROM_DATABASE=Future Domain Corp. + +pci:v00001036d00000000* + ID_MODEL_FROM_DATABASE=TMC-18C30 [36C70] + +pci:v00001037* + ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems + +pci:v00001038* + ID_VENDOR_FROM_DATABASE=AMP, Inc + +pci:v00001039* + ID_VENDOR_FROM_DATABASE=Silicon Integrated Systems [SiS] + +pci:v00001039d00000001* + ID_MODEL_FROM_DATABASE=AGP Port (virtual PCI-to-PCI bridge) + +pci:v00001039d00000002* + ID_MODEL_FROM_DATABASE=AGP Port (virtual PCI-to-PCI bridge) + +pci:v00001039d00000003* + ID_MODEL_FROM_DATABASE=AGP Port (virtual PCI-to-PCI bridge) + +pci:v00001039d00000004* + ID_MODEL_FROM_DATABASE=PCI-to-PCI bridge + +pci:v00001039d00000004sv00001039sd00000000* + ID_MODEL_FROM_DATABASE=PCI-to-PCI bridge (PCIe x16 port) + +pci:v00001039d00000006* + ID_MODEL_FROM_DATABASE=85C501/2/3 + +pci:v00001039d00000008* + ID_MODEL_FROM_DATABASE=SiS85C503/5513 (LPC Bridge) + +pci:v00001039d00000009* + ID_MODEL_FROM_DATABASE=5595 Power Management Controller + +pci:v00001039d0000000A* + ID_MODEL_FROM_DATABASE=PCI-to-PCI bridge + +pci:v00001039d0000000Asv00001039sd00000000* + ID_MODEL_FROM_DATABASE=PCI-to-PCI bridge (PCIe x1 port) + +pci:v00001039d00000016* + ID_MODEL_FROM_DATABASE=SiS961/2/3 SMBus controller + +pci:v00001039d00000018* + ID_MODEL_FROM_DATABASE=SiS85C503/5513 (LPC Bridge) + +pci:v00001039d00000163* + ID_MODEL_FROM_DATABASE=163 802.11b/g Wireless LAN Adapter + +pci:v00001039d00000180* + ID_MODEL_FROM_DATABASE=RAID bus controller 180 SATA/PATA [SiS] + +pci:v00001039d00000181* + ID_MODEL_FROM_DATABASE=SATA + +pci:v00001039d00000182* + ID_MODEL_FROM_DATABASE=182 SATA/RAID Controller + +pci:v00001039d00000182sv00001734sd00001095* + ID_MODEL_FROM_DATABASE=182 SATA/RAID Controller (D2030-A1) + +pci:v00001039d00000186* + ID_MODEL_FROM_DATABASE=AHCI Controller (0106) + +pci:v00001039d00000190* + ID_MODEL_FROM_DATABASE=190 Ethernet Adapter + +pci:v00001039d00000191* + ID_MODEL_FROM_DATABASE=191 Gigabit Ethernet Adapter + +pci:v00001039d00000191sv00001043sd00008139* + ID_MODEL_FROM_DATABASE=191 Gigabit Ethernet Adapter (P5SD2-FM/S mainboard) + +pci:v00001039d00000200* + ID_MODEL_FROM_DATABASE=5597/5598/6326 VGA + +pci:v00001039d00000200sv00001039sd00000000* + ID_MODEL_FROM_DATABASE=5597/5598/6326 VGA (SiS5597 SVGA (Shared RAM)) + +pci:v00001039d00000204* + ID_MODEL_FROM_DATABASE=82C204 + +pci:v00001039d00000205* + ID_MODEL_FROM_DATABASE=SG86C205 + +pci:v00001039d00000300* + ID_MODEL_FROM_DATABASE=300/305 PCI/AGP VGA Display Adapter + +pci:v00001039d00000300sv0000107Dsd00002720* + ID_MODEL_FROM_DATABASE=300/305 PCI/AGP VGA Display Adapter (Leadtek WinFast VR300) + +pci:v00001039d00000310* + ID_MODEL_FROM_DATABASE=315H PCI/AGP VGA Display Adapter + +pci:v00001039d00000315* + ID_MODEL_FROM_DATABASE=315 PCI/AGP VGA Display Adapter + +pci:v00001039d00000325* + ID_MODEL_FROM_DATABASE=315PRO PCI/AGP VGA Display Adapter + +pci:v00001039d00000330* + ID_MODEL_FROM_DATABASE=330 [Xabre] PCI/AGP VGA Display Adapter + +pci:v00001039d00000406* + ID_MODEL_FROM_DATABASE=85C501/2 + +pci:v00001039d00000496* + ID_MODEL_FROM_DATABASE=85C496 + +pci:v00001039d00000530* + ID_MODEL_FROM_DATABASE=530 Host + +pci:v00001039d00000540* + ID_MODEL_FROM_DATABASE=540 Host + +pci:v00001039d00000550* + ID_MODEL_FROM_DATABASE=550 Host + +pci:v00001039d00000597* + ID_MODEL_FROM_DATABASE=5513C + +pci:v00001039d00000601* + ID_MODEL_FROM_DATABASE=85C601 + +pci:v00001039d00000620* + ID_MODEL_FROM_DATABASE=620 Host + +pci:v00001039d00000630* + ID_MODEL_FROM_DATABASE=630 Host + +pci:v00001039d00000633* + ID_MODEL_FROM_DATABASE=633 Host + +pci:v00001039d00000635* + ID_MODEL_FROM_DATABASE=635 Host + +pci:v00001039d00000645* + ID_MODEL_FROM_DATABASE=SiS645 Host & Memory & AGP Controller + +pci:v00001039d00000646* + ID_MODEL_FROM_DATABASE=SiS645DX Host & Memory & AGP Controller + +pci:v00001039d00000648* + ID_MODEL_FROM_DATABASE=645xx + +pci:v00001039d00000649* + ID_MODEL_FROM_DATABASE=SiS649 Host + +pci:v00001039d00000650* + ID_MODEL_FROM_DATABASE=650/M650 Host + +pci:v00001039d00000651* + ID_MODEL_FROM_DATABASE=651 Host + +pci:v00001039d00000655* + ID_MODEL_FROM_DATABASE=655 Host + +pci:v00001039d00000660* + ID_MODEL_FROM_DATABASE=660 Host + +pci:v00001039d00000661* + ID_MODEL_FROM_DATABASE=661FX/M661FX/M661MX Host + +pci:v00001039d00000662* + ID_MODEL_FROM_DATABASE=662 Host + +pci:v00001039d00000671* + ID_MODEL_FROM_DATABASE=671MX + +pci:v00001039d00000730* + ID_MODEL_FROM_DATABASE=730 Host + +pci:v00001039d00000733* + ID_MODEL_FROM_DATABASE=733 Host + +pci:v00001039d00000735* + ID_MODEL_FROM_DATABASE=735 Host + +pci:v00001039d00000740* + ID_MODEL_FROM_DATABASE=740 Host + +pci:v00001039d00000741* + ID_MODEL_FROM_DATABASE=741/741GX/M741 Host + +pci:v00001039d00000741sv00001849sd00000741* + ID_MODEL_FROM_DATABASE=741/741GX/M741 Host (K7S41/K7S41GX motherboard) + +pci:v00001039d00000745* + ID_MODEL_FROM_DATABASE=745 Host + +pci:v00001039d00000746* + ID_MODEL_FROM_DATABASE=746 Host + +pci:v00001039d00000755* + ID_MODEL_FROM_DATABASE=755 Host + +pci:v00001039d00000760* + ID_MODEL_FROM_DATABASE=760/M760 Host + +pci:v00001039d00000761* + ID_MODEL_FROM_DATABASE=761/M761 Host + +pci:v00001039d00000761sv00001734sd00001099* + ID_MODEL_FROM_DATABASE=761/M761 Host (D2030-A1 Motherboard) + +pci:v00001039d00000900* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet + +pci:v00001039d00000900sv00001019sd00000A14* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet (K7S5A motherboard) + +pci:v00001039d00000900sv00001039sd00000900* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet (SiS900 10/100 Ethernet Adapter onboard) + +pci:v00001039d00000900sv00001043sd00008035* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet (CUSI-FX motherboard) + +pci:v00001039d00000900sv00001043sd000080A7* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet (Motherboard P4S800D-X) + +pci:v00001039d00000900sv00001462sd00000900* + ID_MODEL_FROM_DATABASE=SiS900 PCI Fast Ethernet (MS-6701 motherboard) + +pci:v00001039d00000961* + ID_MODEL_FROM_DATABASE=SiS961 [MuTIOL Media IO] + +pci:v00001039d00000962* + ID_MODEL_FROM_DATABASE=SiS962 [MuTIOL Media IO] LPC Controller + +pci:v00001039d00000963* + ID_MODEL_FROM_DATABASE=SiS963 [MuTIOL Media IO] LPC Controller + +pci:v00001039d00000964* + ID_MODEL_FROM_DATABASE=SiS964 [MuTIOL Media IO] LPC Controller + +pci:v00001039d00000965* + ID_MODEL_FROM_DATABASE=SiS965 [MuTIOL Media IO] + +pci:v00001039d00000966* + ID_MODEL_FROM_DATABASE=SiS966 [MuTIOL Media IO] + +pci:v00001039d00000968* + ID_MODEL_FROM_DATABASE=SiS968 [MuTIOL Media IO] + +pci:v00001039d00001180* + ID_MODEL_FROM_DATABASE=SATA Controller / IDE mode + +pci:v00001039d00001182* + ID_MODEL_FROM_DATABASE=SATA Controller / RAID mode + +pci:v00001039d00001182sv00001039sd00000180* + ID_MODEL_FROM_DATABASE=SATA Controller / RAID mode (SiS 966 4-port SATA controller) + +pci:v00001039d00001183* + ID_MODEL_FROM_DATABASE=SATA Controller / IDE mode + +pci:v00001039d00001183sv00001039sd00000180* + ID_MODEL_FROM_DATABASE=SATA Controller / IDE mode (SiS 966 4-port SATA controller) + +pci:v00001039d00001184* + ID_MODEL_FROM_DATABASE=AHCI Controller / RAID mode + +pci:v00001039d00001185* + ID_MODEL_FROM_DATABASE=AHCI IDE Controller (0106) + +pci:v00001039d00003602* + ID_MODEL_FROM_DATABASE=83C602 + +pci:v00001039d00005107* + ID_MODEL_FROM_DATABASE=5107 + +pci:v00001039d00005300* + ID_MODEL_FROM_DATABASE=SiS540 PCI Display Adapter + +pci:v00001039d00005315* + ID_MODEL_FROM_DATABASE=550 PCI/AGP VGA Display Adapter + +pci:v00001039d00005401* + ID_MODEL_FROM_DATABASE=486 PCI Chipset + +pci:v00001039d00005511* + ID_MODEL_FROM_DATABASE=5511/5512 + +pci:v00001039d00005513* + ID_MODEL_FROM_DATABASE=5513 IDE Controller + +pci:v00001039d00005513sv00001019sd00000970* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (P6STP-FL motherboard) + +pci:v00001039d00005513sv00001039sd00005513* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (SiS5513 EIDE Controller (A,B step)) + +pci:v00001039d00005513sv00001043sd00008035* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (CUSI-FX motherboard) + +pci:v00001039d00005513sv00001462sd00007010* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (MS-6701 motherboard) + +pci:v00001039d00005513sv00001631sd00005513* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (GA-8SIML Rev1.0 Motherboard) + +pci:v00001039d00005513sv00001734sd00001095* + ID_MODEL_FROM_DATABASE=5513 IDE Controller (D2030-A1 Motherboard) + +pci:v00001039d00005517* + ID_MODEL_FROM_DATABASE=5517 + +pci:v00001039d00005571* + ID_MODEL_FROM_DATABASE=5571 + +pci:v00001039d00005581* + ID_MODEL_FROM_DATABASE=5581 Pentium Chipset + +pci:v00001039d00005582* + ID_MODEL_FROM_DATABASE=5582 + +pci:v00001039d00005591* + ID_MODEL_FROM_DATABASE=5591/5592 Host + +pci:v00001039d00005596* + ID_MODEL_FROM_DATABASE=5596 Pentium Chipset + +pci:v00001039d00005597* + ID_MODEL_FROM_DATABASE=5597 [SiS5582] + +pci:v00001039d00005600* + ID_MODEL_FROM_DATABASE=5600 Host + +pci:v00001039d00006204* + ID_MODEL_FROM_DATABASE=Video decoder & MPEG interface + +pci:v00001039d00006205* + ID_MODEL_FROM_DATABASE=VGA Controller + +pci:v00001039d00006236* + ID_MODEL_FROM_DATABASE=6236 3D-AGP + +pci:v00001039d00006300* + ID_MODEL_FROM_DATABASE=630/730 PCI/AGP VGA Display Adapter + +pci:v00001039d00006300sv00001019sd00000970* + ID_MODEL_FROM_DATABASE=630/730 PCI/AGP VGA Display Adapter (P6STP-FL motherboard) + +pci:v00001039d00006300sv00001043sd00008035* + ID_MODEL_FROM_DATABASE=630/730 PCI/AGP VGA Display Adapter (CUSI-FX motherboard) + +pci:v00001039d00006300sv0000104Dsd000080E2* + ID_MODEL_FROM_DATABASE=630/730 PCI/AGP VGA Display Adapter (VAIO PCV-J200) + +pci:v00001039d00006306* + ID_MODEL_FROM_DATABASE=530/620 PCI/AGP VGA Display Adapter + +pci:v00001039d00006325* + ID_MODEL_FROM_DATABASE=65x/M650/740 PCI/AGP VGA Display Adapter + +pci:v00001039d00006325sv00001039sd00006325* + ID_MODEL_FROM_DATABASE=65x/M650/740 PCI/AGP VGA Display Adapter (SiS 651 onboard [Asus P4SC-EA]) + +pci:v00001039d00006325sv00001631sd00001004* + ID_MODEL_FROM_DATABASE=65x/M650/740 PCI/AGP VGA Display Adapter (SiS 651C onboard [Gigabyte GA-8SIML Rev1.0]) + +pci:v00001039d00006326* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 + +pci:v00001039d00006326sv00001039sd00006326* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SiS6326 GUI Accelerator) + +pci:v00001039d00006326sv00001092sd00000A50* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SpeedStar A50) + +pci:v00001039d00006326sv00001092sd00000A70* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SpeedStar A70) + +pci:v00001039d00006326sv00001092sd00004910* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SpeedStar A70) + +pci:v00001039d00006326sv00001092sd00004920* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SpeedStar A70) + +pci:v00001039d00006326sv000010B0sd00006326* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (S6110-B (AGP)) + +pci:v00001039d00006326sv00001569sd00006326* + ID_MODEL_FROM_DATABASE=86C326 5598/6326 (SiS6326 GUI Accelerator) + +pci:v00001039d00006330* + ID_MODEL_FROM_DATABASE=661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter + +pci:v00001039d00006330sv00001039sd00006330* + ID_MODEL_FROM_DATABASE=661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter ([M]661xX/[M]741[GX]/[M]760 PCI/AGP VGA Adapter) + +pci:v00001039d00006330sv00001043sd00008113* + ID_MODEL_FROM_DATABASE=661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter (SiS Real 256E (ASUS P5S800-VM motherboard)) + +pci:v00001039d00006330sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter (SiS661FX GUI 2D/3D Accelerator) + +pci:v00001039d00006330sv00001734sd00001099* + ID_MODEL_FROM_DATABASE=661/741/760 PCI/AGP or 662/761Gx PCIE VGA Display Adapter (D2030-A1) + +pci:v00001039d00006350* + ID_MODEL_FROM_DATABASE=770/670 PCIE VGA Display Adapter + +pci:v00001039d00006351* + ID_MODEL_FROM_DATABASE=771/671 PCIE VGA Display Adapter + +pci:v00001039d00007001* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller + +pci:v00001039d00007001sv00001019sd00000A14* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (K7S5A motherboard) + +pci:v00001039d00007001sv00001039sd00007000* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (Onboard USB Controller) + +pci:v00001039d00007001sv00001462sd00005470* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (ECS K7SOM+ motherboard) + +pci:v00001039d00007001sv00001462sd00007010* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (MS-6701 motherboard) + +pci:v00001039d00007001sv00001734sd00001095* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (D2030-A1 Motherboard) + +pci:v00001039d00007002* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller + +pci:v00001039d00007002sv00001462sd00005470* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (K7SOM+ 5.2C Motherboard) + +pci:v00001039d00007002sv00001462sd00007010* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (MS-6701 motherboard) + +pci:v00001039d00007002sv00001509sd00007002* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (Onboard USB Controller) + +pci:v00001039d00007002sv00001734sd00001095* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (D2030-A1) + +pci:v00001039d00007007* + ID_MODEL_FROM_DATABASE=FireWire Controller + +pci:v00001039d00007007sv00001462sd0000701D* + ID_MODEL_FROM_DATABASE=FireWire Controller (MS-6701) + +pci:v00001039d00007012* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller + +pci:v00001039d00007012sv00001019sd00000F05* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (A928 (i-Buddie)) + +pci:v00001039d00007012sv00001039sd00007012* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (SiS 7012 onboard [Asus P4SC-EA] AC'97 Sound Controller) + +pci:v00001039d00007012sv00001043sd0000818F* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (A8S-X Motherboard) + +pci:v00001039d00007012sv000013F6sd00000300* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (CMI9739(A) on ECS K7S series motherboard) + +pci:v00001039d00007012sv00001462sd00005850* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (MSI 648 Max (MS-6585)) + +pci:v00001039d00007012sv00001462sd00007010* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (MS-6701 motherboard) + +pci:v00001039d00007012sv000015BDsd00001001* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (DFI 661FX motherboard) + +pci:v00001039d00007012sv00001734sd0000109F* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (D2030-A1 Motherboard) + +pci:v00001039d00007012sv00001849sd00007012* + ID_MODEL_FROM_DATABASE=SiS7012 AC'97 Sound Controller (K7S41GX motherboard) + +pci:v00001039d00007013* + ID_MODEL_FROM_DATABASE=AC'97 Modem Controller + +pci:v00001039d00007016* + ID_MODEL_FROM_DATABASE=SiS7016 PCI Fast Ethernet Adapter + +pci:v00001039d00007016sv00001039sd00007016* + ID_MODEL_FROM_DATABASE=SiS7016 PCI Fast Ethernet Adapter (SiS7016 10/100 Ethernet Adapter) + +pci:v00001039d00007018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001014sd000001B6* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001014sd000001B7* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001019sd00007018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001025sd0000000E* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001025sd00000018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001039sd00007018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001043sd00001453* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001043sd0000800B* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000104Dsd000080E2* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator (VAIO PCV-J200) + +pci:v00001039d00007018sv00001054sd00007018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000107Dsd00005330* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000107Dsd00005350* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001170sd00003209* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001462sd0000400A* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv000014A4sd00002089* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv000014CDsd00002194* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv000014FFsd00001100* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000152Dsd00008808* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001558sd00001103* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001558sd00002200* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv00001563sd00007018* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv000015C5sd00000111* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000270Fsd0000A171* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007018sv0000A0A0sd00000022* + ID_MODEL_FROM_DATABASE=SiS PCI Audio Accelerator + +pci:v00001039d00007019* + ID_MODEL_FROM_DATABASE=SiS7019 Audio Accelerator + +pci:v00001039d00007502* + ID_MODEL_FROM_DATABASE=Azalia Audio Controller + +pci:v00001039d00007502sv00001043sd000081A1* + ID_MODEL_FROM_DATABASE=Azalia Audio Controller (P5SD2-FM/S mainboard) + +pci:v0000103A* + ID_VENDOR_FROM_DATABASE=Seiko Epson Corporation + +pci:v0000103B* + ID_VENDOR_FROM_DATABASE=Tatung Corp. Of America + +pci:v0000103C* + ID_VENDOR_FROM_DATABASE=Hewlett-Packard Company + +pci:v0000103Cd00001005* + ID_MODEL_FROM_DATABASE=A4977A Visualize EG + +pci:v0000103Cd00001008* + ID_MODEL_FROM_DATABASE=Visualize FX + +pci:v0000103Cd00001028* + ID_MODEL_FROM_DATABASE=Tach TL Fibre Channel Host Adapter + +pci:v0000103Cd00001029* + ID_MODEL_FROM_DATABASE=Tach XL2 Fibre Channel Host Adapter + +pci:v0000103Cd00001029sv0000107Esd0000000F* + ID_MODEL_FROM_DATABASE=Tach XL2 Fibre Channel Host Adapter (Interphase 5560 Fibre Channel Adapter) + +pci:v0000103Cd00001029sv00009004sd00009210* + ID_MODEL_FROM_DATABASE=Tach XL2 Fibre Channel Host Adapter (1Gb/2Gb Family Fibre Channel Controller) + +pci:v0000103Cd00001029sv00009004sd00009211* + ID_MODEL_FROM_DATABASE=Tach XL2 Fibre Channel Host Adapter (1Gb/2Gb Family Fibre Channel Controller) + +pci:v0000103Cd0000102A* + ID_MODEL_FROM_DATABASE=Tach TS Fibre Channel Host Adapter + +pci:v0000103Cd0000102Asv0000107Esd0000000E* + ID_MODEL_FROM_DATABASE=Tach TS Fibre Channel Host Adapter (Interphase 5540/5541 Fibre Channel Adapter) + +pci:v0000103Cd0000102Asv00009004sd00009110* + ID_MODEL_FROM_DATABASE=Tach TS Fibre Channel Host Adapter (1Gb/2Gb Family Fibre Channel Controller) + +pci:v0000103Cd0000102Asv00009004sd00009111* + ID_MODEL_FROM_DATABASE=Tach TS Fibre Channel Host Adapter (1Gb/2Gb Family Fibre Channel Controller) + +pci:v0000103Cd00001030* + ID_MODEL_FROM_DATABASE=J2585A DeskDirect 10/100VG NIC + +pci:v0000103Cd00001031* + ID_MODEL_FROM_DATABASE=J2585B HP 10/100VG PCI LAN Adapter + +pci:v0000103Cd00001031sv0000103Csd00001040* + ID_MODEL_FROM_DATABASE=J2585B HP 10/100VG PCI LAN Adapter (J2973A DeskDirect 10BaseT NIC) + +pci:v0000103Cd00001031sv0000103Csd00001041* + ID_MODEL_FROM_DATABASE=J2585B HP 10/100VG PCI LAN Adapter (J2585B DeskDirect 10/100VG NIC) + +pci:v0000103Cd00001031sv0000103Csd00001042* + ID_MODEL_FROM_DATABASE=J2585B HP 10/100VG PCI LAN Adapter (J2970A DeskDirect 10BaseT/2 NIC) + +pci:v0000103Cd00001040* + ID_MODEL_FROM_DATABASE=J2973A DeskDirect 10BaseT NIC + +pci:v0000103Cd00001041* + ID_MODEL_FROM_DATABASE=J2585B DeskDirect 10/100 NIC + +pci:v0000103Cd00001042* + ID_MODEL_FROM_DATABASE=J2970A DeskDirect 10BaseT/2 NIC + +pci:v0000103Cd00001048* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART + +pci:v0000103Cd00001048sv0000103Csd00001049* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Tosca Console) + +pci:v0000103Cd00001048sv0000103Csd0000104A* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Tosca Secondary) + +pci:v0000103Cd00001048sv0000103Csd0000104B* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Maestro SP2) + +pci:v0000103Cd00001048sv0000103Csd00001223* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Superdome Console) + +pci:v0000103Cd00001048sv0000103Csd00001226* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Keystone SP2) + +pci:v0000103Cd00001048sv0000103Csd00001227* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Powerbar SP2) + +pci:v0000103Cd00001048sv0000103Csd00001282* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Everest SP2) + +pci:v0000103Cd00001048sv0000103Csd00001301* + ID_MODEL_FROM_DATABASE=Diva Serial [GSP] Multiport UART (Diva RMP3) + +pci:v0000103Cd00001054* + ID_MODEL_FROM_DATABASE=PCI Local Bus Adapter + +pci:v0000103Cd00001064* + ID_MODEL_FROM_DATABASE=79C970 PCnet Ethernet Controller + +pci:v0000103Cd0000108B* + ID_MODEL_FROM_DATABASE=Visualize FXe + +pci:v0000103Cd000010C1* + ID_MODEL_FROM_DATABASE=NetServer Smart IRQ Router + +pci:v0000103Cd000010ED* + ID_MODEL_FROM_DATABASE=TopTools Remote Control + +pci:v0000103Cd000010F0* + ID_MODEL_FROM_DATABASE=rio System Bus Adapter + +pci:v0000103Cd000010F1* + ID_MODEL_FROM_DATABASE=rio I/O Controller + +pci:v0000103Cd00001219* + ID_MODEL_FROM_DATABASE=NetServer PCI Hot-Plug Controller + +pci:v0000103Cd0000121A* + ID_MODEL_FROM_DATABASE=NetServer SMIC Controller + +pci:v0000103Cd0000121B* + ID_MODEL_FROM_DATABASE=NetServer Legacy COM Port Decoder + +pci:v0000103Cd0000121C* + ID_MODEL_FROM_DATABASE=NetServer PCI COM Port Decoder + +pci:v0000103Cd00001229* + ID_MODEL_FROM_DATABASE=zx1 System Bus Adapter + +pci:v0000103Cd0000122A* + ID_MODEL_FROM_DATABASE=zx1 I/O Controller + +pci:v0000103Cd0000122E* + ID_MODEL_FROM_DATABASE=PCI-X Local Bus Adapter + +pci:v0000103Cd0000127B* + ID_MODEL_FROM_DATABASE=sx1000 System Bus Adapter + +pci:v0000103Cd0000127C* + ID_MODEL_FROM_DATABASE=sx1000 I/O Controller + +pci:v0000103Cd00001290* + ID_MODEL_FROM_DATABASE=Auxiliary Diva Serial Port + +pci:v0000103Cd00001290sv0000103Csd00001291* + ID_MODEL_FROM_DATABASE=Auxiliary Diva Serial Port (Diva SP2) + +pci:v0000103Cd00001291* + ID_MODEL_FROM_DATABASE=Auxiliary Diva Serial Port + +pci:v0000103Cd000012B4* + ID_MODEL_FROM_DATABASE=zx1 QuickSilver AGP8x Local Bus Adapter + +pci:v0000103Cd000012EB* + ID_MODEL_FROM_DATABASE=sx2000 System Bus Adapter + +pci:v0000103Cd000012EC* + ID_MODEL_FROM_DATABASE=sx2000 I/O Controller + +pci:v0000103Cd000012EE* + ID_MODEL_FROM_DATABASE=PCI-X 2.0 Local Bus Adapter + +pci:v0000103Cd00001302* + ID_MODEL_FROM_DATABASE=RMP-3 Shared Memory Driver + +pci:v0000103Cd00001303* + ID_MODEL_FROM_DATABASE=RMP-3 (Remote Management Processor) + +pci:v0000103Cd00002910* + ID_MODEL_FROM_DATABASE=E2910A PCIBus Exerciser + +pci:v0000103Cd00002925* + ID_MODEL_FROM_DATABASE=E2925A 32 Bit, 33 MHzPCI Exerciser & Analyzer + +pci:v0000103Cd00003206* + ID_MODEL_FROM_DATABASE=Adaptec Embedded Serial ATA HostRAID + +pci:v0000103Cd00003220* + ID_MODEL_FROM_DATABASE=Smart Array P600 + +pci:v0000103Cd00003220sv0000103Csd00003225* + ID_MODEL_FROM_DATABASE=Smart Array P600 (3 Gb/s SAS RAID) + +pci:v0000103Cd00003230* + ID_MODEL_FROM_DATABASE=Smart Array Controller + +pci:v0000103Cd00003230sv0000103Csd00003223* + ID_MODEL_FROM_DATABASE=Smart Array Controller (Smart Array P800) + +pci:v0000103Cd00003230sv0000103Csd00003234* + ID_MODEL_FROM_DATABASE=Smart Array Controller (P400 SAS Controller) + +pci:v0000103Cd00003230sv0000103Csd00003235* + ID_MODEL_FROM_DATABASE=Smart Array Controller (P400i SAS Controller) + +pci:v0000103Cd00003230sv0000103Csd00003237* + ID_MODEL_FROM_DATABASE=Smart Array Controller (E500 SAS Controller) + +pci:v0000103Cd00003230sv0000103Csd0000323D* + ID_MODEL_FROM_DATABASE=Smart Array Controller (P700m SAS Controller) + +pci:v0000103Cd00003238* + ID_MODEL_FROM_DATABASE=Smart Array E200i (SAS Controller) + +pci:v0000103Cd00003238sv0000103Csd00003211* + ID_MODEL_FROM_DATABASE=Smart Array E200i (SAS Controller) (Smart Array E200i) + +pci:v0000103Cd00003238sv0000103Csd00003212* + ID_MODEL_FROM_DATABASE=Smart Array E200i (SAS Controller) (Smart Array E200) + +pci:v0000103Cd00003239* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers + +pci:v0000103Cd00003239sv0000103Csd000021BD* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P244br) + +pci:v0000103Cd00003239sv0000103Csd000021BE* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P741m) + +pci:v0000103Cd00003239sv0000103Csd000021BF* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (H240ar) + +pci:v0000103Cd00003239sv0000103Csd000021C0* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P440ar) + +pci:v0000103Cd00003239sv0000103Csd000021C1* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P840ar) + +pci:v0000103Cd00003239sv0000103Csd000021C2* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P440) + +pci:v0000103Cd00003239sv0000103Csd000021C3* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P441) + +pci:v0000103Cd00003239sv0000103Csd000021C4* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (Smart Array) + +pci:v0000103Cd00003239sv0000103Csd000021C5* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P841) + +pci:v0000103Cd00003239sv0000103Csd000021C6* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (H244br) + +pci:v0000103Cd00003239sv0000103Csd000021C7* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (H240) + +pci:v0000103Cd00003239sv0000103Csd000021C8* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (H241) + +pci:v0000103Cd00003239sv0000103Csd000021C9* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (Smart Array) + +pci:v0000103Cd00003239sv0000103Csd000021CA* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P246br) + +pci:v0000103Cd00003239sv0000103Csd000021CB* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P840) + +pci:v0000103Cd00003239sv0000103Csd000021CC* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (Smart Array) + +pci:v0000103Cd00003239sv0000103Csd000021CD* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (P240nr) + +pci:v0000103Cd00003239sv0000103Csd000021CE* + ID_MODEL_FROM_DATABASE=Smart Array Gen9 Controllers (H240nr) + +pci:v0000103Cd0000323A* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers + +pci:v0000103Cd0000323Asv0000103Csd00003241* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P212) + +pci:v0000103Cd0000323Asv0000103Csd00003243* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P410) + +pci:v0000103Cd0000323Asv0000103Csd00003245* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P410i) + +pci:v0000103Cd0000323Asv0000103Csd00003247* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P411) + +pci:v0000103Cd0000323Asv0000103Csd00003249* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P812) + +pci:v0000103Cd0000323Asv0000103Csd0000324A* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array 712m (Mezzanine RAID controller)) + +pci:v0000103Cd0000323Asv0000103Csd0000324B* + ID_MODEL_FROM_DATABASE=Smart Array G6 controllers (Smart Array P711m (Mezzanine RAID controller)) + +pci:v0000103Cd0000323B* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers + +pci:v0000103Cd0000323Bsv0000103Csd00003350* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers (P222) + +pci:v0000103Cd0000323Bsv0000103Csd00003351* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers (P420) + +pci:v0000103Cd0000323Bsv0000103Csd00003352* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers (P421) + +pci:v0000103Cd0000323Bsv0000103Csd00003354* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers (P420i) + +pci:v0000103Cd0000323Bsv0000103Csd00003355* + ID_MODEL_FROM_DATABASE=Smart Array Gen8 Controllers (P220i) + +pci:v0000103Cd0000323C* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers + +pci:v0000103Cd0000323Csv0000103Csd00001920* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P430i) + +pci:v0000103Cd0000323Csv0000103Csd00001921* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P830i) + +pci:v0000103Cd0000323Csv0000103Csd00001922* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P430) + +pci:v0000103Cd0000323Csv0000103Csd00001923* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P431) + +pci:v0000103Cd0000323Csv0000103Csd00001924* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P830) + +pci:v0000103Cd0000323Csv0000103Csd00001925* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (Smart Array) + +pci:v0000103Cd0000323Csv0000103Csd00001926* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P731m) + +pci:v0000103Cd0000323Csv0000103Csd00001928* + ID_MODEL_FROM_DATABASE=Smart Array Gen8+ Controllers (P230i) + +pci:v0000103Cd00003300* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller + +pci:v0000103Cd00003300sv0000103Csd00003304* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller (iLO2) + +pci:v0000103Cd00003300sv0000103Csd00003305* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller (iLO2) + +pci:v0000103Cd00003300sv0000103Csd00003309* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller (iLO2 GXL/iLO3 GXE) + +pci:v0000103Cd00003300sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller (iLO3) + +pci:v0000103Cd00003300sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Virtual USB Controller (iLO4) + +pci:v0000103Cd00003301* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Serial Port + +pci:v0000103Cd00003301sv0000103Csd00003304* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Serial Port (iLO2) + +pci:v0000103Cd00003301sv0000103Csd00003305* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Serial Port (iLO2) + +pci:v0000103Cd00003301sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Serial Port (iLO3) + +pci:v0000103Cd00003301sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Serial Port (iLO4) + +pci:v0000103Cd00003302* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard KCS Interface + +pci:v0000103Cd00003302sv0000103Csd00003304* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard KCS Interface (iLO2) + +pci:v0000103Cd00003302sv0000103Csd00003305* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard KCS Interface (iLO2) + +pci:v0000103Cd00003302sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard KCS Interface (iLO3) + +pci:v0000103Cd00003302sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard KCS Interface (iLO4) + +pci:v0000103Cd00003305* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out (iLO2) Controller + +pci:v0000103Cd00003306* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Slave Instrumentation & System Support + +pci:v0000103Cd00003306sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Slave Instrumentation & System Support (iLO3) + +pci:v0000103Cd00003306sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Slave Instrumentation & System Support (iLO4) + +pci:v0000103Cd00003307* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Management Processor Support and Messaging + +pci:v0000103Cd00003307sv0000103Csd00003309* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Management Processor Support and Messaging (iLO 2) + +pci:v0000103Cd00003307sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Management Processor Support and Messaging (iLO3) + +pci:v0000103Cd00003307sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard Management Processor Support and Messaging (iLO4) + +pci:v0000103Cd00003308* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard MS Watchdog Timer + +pci:v0000103Cd00003308sv0000103Csd0000330E* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard MS Watchdog Timer (iLO3) + +pci:v0000103Cd00003308sv0000103Csd00003381* + ID_MODEL_FROM_DATABASE=Integrated Lights-Out Standard MS Watchdog Timer (iLO4) + +pci:v0000103Cd0000402F* + ID_MODEL_FROM_DATABASE=PCIe Root Port + +pci:v0000103Cd00004030* + ID_MODEL_FROM_DATABASE=zx2 System Bus Adapter + +pci:v0000103Cd00004031* + ID_MODEL_FROM_DATABASE=zx2 I/O Controller + +pci:v0000103Cd00004037* + ID_MODEL_FROM_DATABASE=PCIe Local Bus Adapter + +pci:v0000103Cd0000403B* + ID_MODEL_FROM_DATABASE=PCIe Root Port + +pci:v0000103E* + ID_VENDOR_FROM_DATABASE=Solliday Engineering + +pci:v0000103F* + ID_VENDOR_FROM_DATABASE=Synopsys/Logic Modeling Group + +pci:v00001040* + ID_VENDOR_FROM_DATABASE=Accelgraphics Inc. + +pci:v00001041* + ID_VENDOR_FROM_DATABASE=Computrend + +pci:v00001042* + ID_VENDOR_FROM_DATABASE=Micron + +pci:v00001042d00001000* + ID_MODEL_FROM_DATABASE=PC Tech RZ1000 + +pci:v00001042d00001001* + ID_MODEL_FROM_DATABASE=PC Tech RZ1001 + +pci:v00001042d00003000* + ID_MODEL_FROM_DATABASE=Samurai_0 + +pci:v00001042d00003010* + ID_MODEL_FROM_DATABASE=Samurai_1 + +pci:v00001042d00003020* + ID_MODEL_FROM_DATABASE=Samurai_IDE + +pci:v00001043* + ID_VENDOR_FROM_DATABASE=ASUSTeK Computer Inc. + +pci:v00001043d00000464* + ID_MODEL_FROM_DATABASE=Radeon R9 270x GPU + +pci:v00001043d00000675* + ID_MODEL_FROM_DATABASE=ISDNLink P-IN100-ST-D + +pci:v00001043d00000675sv00000675sd00001704* + ID_MODEL_FROM_DATABASE=ISDNLink P-IN100-ST-D (ISDN Adapter (PCI Bus, D, C)) + +pci:v00001043d00000675sv00000675sd00001707* + ID_MODEL_FROM_DATABASE=ISDNLink P-IN100-ST-D (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001043d00000675sv000010CFsd0000105E* + ID_MODEL_FROM_DATABASE=ISDNLink P-IN100-ST-D (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001043d00009602* + ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx) + +pci:v00001043d00009602sv00001043sd000083A2* + ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx) (M4A785TD Motherboard) + +pci:v00001044* + ID_VENDOR_FROM_DATABASE=Adaptec (formerly DPT) + +pci:v00001044d00001012* + ID_MODEL_FROM_DATABASE=Domino RAID Engine + +pci:v00001044d0000A400* + ID_MODEL_FROM_DATABASE=SmartCache/Raid I-IV Controller + +pci:v00001044d0000A500* + ID_MODEL_FROM_DATABASE=PCI Bridge + +pci:v00001044d0000A501* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller + +pci:v00001044d0000A501sv00001044sd0000C001* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM1554U2 Ultra2 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C002* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM1654U2 Ultra2 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C003* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM1564U3 Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C004* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM1564U3 Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C005* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM1554U2 Ultra2 Single Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C00A* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2554U2 Ultra2 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C00B* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2654U2 Ultra2 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C00C* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2664U3 Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C00D* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2664U3 Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C00E* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2554U2 Ultra2 Single Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C00F* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2654U2 Ultra2 Single Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C014* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3754U2 Ultra2 Single Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C015* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3755U2B Ultra2 Single Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C016* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3755F Fibre Channel (NON ACPI)) + +pci:v00001044d0000A501sv00001044sd0000C01E* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3757U2 Ultra2 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C01F* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3757U2 Ultra2 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C020* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3767U3 Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C021* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM3767U3 Ultra3 Quad Channel) + +pci:v00001044d0000A501sv00001044sd0000C028* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2865U3 Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C029* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2865U3 Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C02A* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (PM2865F Fibre Channel) + +pci:v00001044d0000A501sv00001044sd0000C03C* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (2000S Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C03D* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (2000S Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C03E* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (2000F Fibre Channel) + +pci:v00001044d0000A501sv00001044sd0000C046* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3000S Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C047* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3000S Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C048* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3000F Fibre Channel) + +pci:v00001044d0000A501sv00001044sd0000C050* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (5000S Ultra3 Single Channel) + +pci:v00001044d0000A501sv00001044sd0000C051* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (5000S Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C052* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (5000F Fibre Channel) + +pci:v00001044d0000A501sv00001044sd0000C05A* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (2400A UDMA Four Channel) + +pci:v00001044d0000A501sv00001044sd0000C05B* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (2400A UDMA Four Channel DAC) + +pci:v00001044d0000A501sv00001044sd0000C064* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3010S Ultra3 Dual Channel) + +pci:v00001044d0000A501sv00001044sd0000C065* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3410S Ultra160 Four Channel) + +pci:v00001044d0000A501sv00001044sd0000C066* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (3010S Fibre Channel) + +pci:v00001044d0000A511* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller + +pci:v00001044d0000A511sv00001044sd0000C032* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (ASR-2005S I2O Zero Channel) + +pci:v00001044d0000A511sv00001044sd0000C035* + ID_MODEL_FROM_DATABASE=SmartRAID V Controller (ASR-2010S I2O Zero Channel) + +pci:v00001044d0000C066* + ID_MODEL_FROM_DATABASE=3010S Ultra3 Dual Channel + +pci:v00001045* + ID_VENDOR_FROM_DATABASE=OPTi Inc. + +pci:v00001045d0000A0F8* + ID_MODEL_FROM_DATABASE=82C750 [Vendetta] USB Controller + +pci:v00001045d0000C101* + ID_MODEL_FROM_DATABASE=92C264 + +pci:v00001045d0000C178* + ID_MODEL_FROM_DATABASE=92C178 + +pci:v00001045d0000C556* + ID_MODEL_FROM_DATABASE=82X556 [Viper] + +pci:v00001045d0000C557* + ID_MODEL_FROM_DATABASE=82C557 [Viper-M] + +pci:v00001045d0000C558* + ID_MODEL_FROM_DATABASE=82C558 [Viper-M ISA+IDE] + +pci:v00001045d0000C567* + ID_MODEL_FROM_DATABASE=82C750 [Vendetta], device 0 + +pci:v00001045d0000C568* + ID_MODEL_FROM_DATABASE=82C750 [Vendetta], device 1 + +pci:v00001045d0000C569* + ID_MODEL_FROM_DATABASE=82C579 [Viper XPress+ Chipset] + +pci:v00001045d0000C621* + ID_MODEL_FROM_DATABASE=82C621 [Viper-M/N+] + +pci:v00001045d0000C700* + ID_MODEL_FROM_DATABASE=82C700 [FireStar] + +pci:v00001045d0000C701* + ID_MODEL_FROM_DATABASE=82C701 [FireStar Plus] + +pci:v00001045d0000C814* + ID_MODEL_FROM_DATABASE=82C814 [Firebridge 1] + +pci:v00001045d0000C822* + ID_MODEL_FROM_DATABASE=82C822 + +pci:v00001045d0000C824* + ID_MODEL_FROM_DATABASE=82C824 + +pci:v00001045d0000C825* + ID_MODEL_FROM_DATABASE=82C825 [Firebridge 2] + +pci:v00001045d0000C832* + ID_MODEL_FROM_DATABASE=82C832 + +pci:v00001045d0000C861* + ID_MODEL_FROM_DATABASE=82C861 + +pci:v00001045d0000C881* + ID_MODEL_FROM_DATABASE=82C881 [FireLink] 1394 OHCI Link Controller + +pci:v00001045d0000C895* + ID_MODEL_FROM_DATABASE=82C895 + +pci:v00001045d0000C935* + ID_MODEL_FROM_DATABASE=EV1935 ECTIVA MachOne PCIAudio + +pci:v00001045d0000D568* + ID_MODEL_FROM_DATABASE=82C825 [Firebridge 2] + +pci:v00001045d0000D721* + ID_MODEL_FROM_DATABASE=IDE [FireStar] + +pci:v00001046* + ID_VENDOR_FROM_DATABASE=IPC Corporation, Ltd. + +pci:v00001047* + ID_VENDOR_FROM_DATABASE=Genoa Systems Corp + +pci:v00001048* + ID_VENDOR_FROM_DATABASE=Elsa AG + +pci:v00001048d00000C60* + ID_MODEL_FROM_DATABASE=Gladiac MX + +pci:v00001048d00000D22* + ID_MODEL_FROM_DATABASE=Quadro4 900XGL [ELSA GLoria4 900XGL] + +pci:v00001048d00001000* + ID_MODEL_FROM_DATABASE=QuickStep 1000 + +pci:v00001048d00003000* + ID_MODEL_FROM_DATABASE=QuickStep 3000 + +pci:v00001048d00008901* + ID_MODEL_FROM_DATABASE=Gloria XL + +pci:v00001048d00008901sv00001048sd00000935* + ID_MODEL_FROM_DATABASE=Gloria XL (GLoria XL (Virge)) + +pci:v00001049* + ID_VENDOR_FROM_DATABASE=Fountain Technologies, Inc. + +pci:v0000104A* + ID_VENDOR_FROM_DATABASE=STMicroelectronics + +pci:v0000104Ad00000000* + ID_MODEL_FROM_DATABASE=STLS2F Host Bridge + +pci:v0000104Ad00000008* + ID_MODEL_FROM_DATABASE=STG 2000X + +pci:v0000104Ad00000009* + ID_MODEL_FROM_DATABASE=STG 1764X + +pci:v0000104Ad00000010* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] + +pci:v0000104Ad00000010sv0000104Asd00004018* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] (ST PowerVR Kyro (64MB AGP TVO)) + +pci:v0000104Ad00000010sv00001681sd00000010* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] (PowerVR Kyro II [3D Prophet 4500]) + +pci:v0000104Ad00000010sv00001681sd00000028* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] (3D Prophet 4000XT) + +pci:v0000104Ad00000010sv00001681sd0000C010* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] (3D Prophet 4500 TV-Out) + +pci:v0000104Ad00000010sv00001681sd0000C069* + ID_MODEL_FROM_DATABASE=STG4000 [3D Prophet Kyro Series] (3D Prophet 4000XT) + +pci:v0000104Ad00000201* + ID_MODEL_FROM_DATABASE=STPC Vega Northbridge + +pci:v0000104Ad00000209* + ID_MODEL_FROM_DATABASE=STPC Consumer/Industrial North- and Southbridge + +pci:v0000104Ad0000020A* + ID_MODEL_FROM_DATABASE=STPC Atlas/ConsumerS/Consumer IIA Northbridge + +pci:v0000104Ad0000020B* + ID_MODEL_FROM_DATABASE=STPC Consumer II ISA Bridge + +pci:v0000104Ad00000210* + ID_MODEL_FROM_DATABASE=STPC Atlas ISA Bridge + +pci:v0000104Ad0000021A* + ID_MODEL_FROM_DATABASE=STPC Consumer S Southbridge + +pci:v0000104Ad0000021B* + ID_MODEL_FROM_DATABASE=STPC Consumer IIA Southbridge + +pci:v0000104Ad00000220* + ID_MODEL_FROM_DATABASE=STPC Industrial PCI to PCCard bridge + +pci:v0000104Ad00000228* + ID_MODEL_FROM_DATABASE=STPC Atlas IDE + +pci:v0000104Ad00000229* + ID_MODEL_FROM_DATABASE=STPC Vega IDE + +pci:v0000104Ad00000230* + ID_MODEL_FROM_DATABASE=STPC Atlas/Vega OHCI USB Controller + +pci:v0000104Ad00000238* + ID_MODEL_FROM_DATABASE=STPC Vega LAN + +pci:v0000104Ad00000500* + ID_MODEL_FROM_DATABASE=ST70137 [Unicorn] ADSL DMT Transceiver + +pci:v0000104Ad00000500sv0000104Asd00000500* + ID_MODEL_FROM_DATABASE=ST70137 [Unicorn] ADSL DMT Transceiver (BeWAN ADSL PCI st) + +pci:v0000104Ad00000564* + ID_MODEL_FROM_DATABASE=STPC Client Northbridge + +pci:v0000104Ad00000981* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v0000104Ad00001746* + ID_MODEL_FROM_DATABASE=STG 1764X + +pci:v0000104Ad00002774* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v0000104Ad00003520* + ID_MODEL_FROM_DATABASE=MPEG-II decoder card + +pci:v0000104Ad000055CC* + ID_MODEL_FROM_DATABASE=STPC Client Southbridge + +pci:v0000104B* + ID_VENDOR_FROM_DATABASE=BusLogic + +pci:v0000104Bd00000140* + ID_MODEL_FROM_DATABASE=BT-946C (old) [multimaster 01] + +pci:v0000104Bd00001040* + ID_MODEL_FROM_DATABASE=BT-946C (BA80C30) [MultiMaster 10] + +pci:v0000104Bd00008130* + ID_MODEL_FROM_DATABASE=Flashpoint LT + +pci:v0000104C* + ID_VENDOR_FROM_DATABASE=Texas Instruments + +pci:v0000104Cd00000500* + ID_MODEL_FROM_DATABASE=100 MBit LAN Controller + +pci:v0000104Cd00000508* + ID_MODEL_FROM_DATABASE=TMS380C2X Compressor Interface + +pci:v0000104Cd00001000* + ID_MODEL_FROM_DATABASE=Eagle i/f AS + +pci:v0000104Cd0000104C* + ID_MODEL_FROM_DATABASE=PCI1510 PC card Cardbus Controller + +pci:v0000104Cd00003D04* + ID_MODEL_FROM_DATABASE=TVP4010 [Permedia] + +pci:v0000104Cd00003D07* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] + +pci:v0000104Cd00003D07sv00001011sd00004D10* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (Comet) + +pci:v0000104Cd00003D07sv00001040sd0000000F* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (AccelStar II) + +pci:v0000104Cd00003D07sv00001040sd00000011* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (AccelStar II) + +pci:v0000104Cd00003D07sv00001048sd00000A31* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (WINNER 2000) + +pci:v0000104Cd00003D07sv00001048sd00000A32* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv00001048sd00000A34* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv00001048sd00000A35* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv00001048sd00000A36* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv00001048sd00000A43* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv00001048sd00000A44* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (GLoria Synergy) + +pci:v0000104Cd00003D07sv0000107Dsd00002633* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (WinFast 3D L2300) + +pci:v0000104Cd00003D07sv00001092sd00000126* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000127* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000136* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000141* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000146* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000148* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000149* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000152* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000154* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000155* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000156* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001092sd00000157* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (FIRE GL 1000 PRO) + +pci:v0000104Cd00003D07sv00001097sd00003D01* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (Jeronimo Pro) + +pci:v0000104Cd00003D07sv00001102sd0000100F* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (Graphics Blaster Extreme) + +pci:v0000104Cd00003D07sv00003D3Dsd00000100* + ID_MODEL_FROM_DATABASE=TVP4020 [Permedia 2] (Reference Permedia 2 3D) + +pci:v0000104Cd00008000* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller + +pci:v0000104Cd00008000sv0000105Esd00008003* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (FireBoard200) + +pci:v0000104Cd00008000sv00001443sd00008003* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (FireBoard200) + +pci:v0000104Cd00008000sv00001443sd00008005* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (FireBoard400) + +pci:v0000104Cd00008000sv00001443sd00008006* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (FireBoard400) + +pci:v0000104Cd00008000sv0000E4BFsd00001010* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (CF1-1-SNARE) + +pci:v0000104Cd00008000sv0000E4BFsd00001020* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (CF1-2-SNARE) + +pci:v0000104Cd00008000sv0000E4BFsd00001040* + ID_MODEL_FROM_DATABASE=PCILynx/PCILynx2 IEEE 1394 Link Layer Controller (FireCompact400) + +pci:v0000104Cd00008009* + ID_MODEL_FROM_DATABASE=TSB12LV22 IEEE-1394 Controller + +pci:v0000104Cd00008009sv0000104Dsd00008032* + ID_MODEL_FROM_DATABASE=TSB12LV22 IEEE-1394 Controller (8032 OHCI i.LINK (IEEE 1394) Controller) + +pci:v0000104Cd00008009sv00001443sd00008010* + ID_MODEL_FROM_DATABASE=TSB12LV22 IEEE-1394 Controller (FireBoard400-OHCI) + +pci:v0000104Cd00008017* + ID_MODEL_FROM_DATABASE=PCI4410 FireWire Controller + +pci:v0000104Cd00008019* + ID_MODEL_FROM_DATABASE=TSB12LV23 IEEE-1394 Controller + +pci:v0000104Cd00008019sv000011BDsd0000000A* + ID_MODEL_FROM_DATABASE=TSB12LV23 IEEE-1394 Controller (Studio DV500-1394) + +pci:v0000104Cd00008019sv000011BDsd0000000E* + ID_MODEL_FROM_DATABASE=TSB12LV23 IEEE-1394 Controller (Studio DV) + +pci:v0000104Cd00008019sv00001443sd00008010* + ID_MODEL_FROM_DATABASE=TSB12LV23 IEEE-1394 Controller (FireBoard400-OHCI) + +pci:v0000104Cd00008019sv0000E4BFsd00001010* + ID_MODEL_FROM_DATABASE=TSB12LV23 IEEE-1394 Controller (CF2-1-CYMBAL) + +pci:v0000104Cd00008020* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) + +pci:v0000104Cd00008020sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) (Precision 530) + +pci:v0000104Cd00008020sv0000104Dsd000080E2* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) (VAIO PCV-J200) + +pci:v0000104Cd00008020sv000011BDsd0000000F* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) (Studio DV500-1394) + +pci:v0000104Cd00008020sv000011BDsd0000001C* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) (Excalibur 4.1) + +pci:v0000104Cd00008020sv00001443sd00008010* + ID_MODEL_FROM_DATABASE=TSB12LV26 IEEE-1394 Controller (Link) (FireBoard400-OHCI) + +pci:v0000104Cd00008021* + ID_MODEL_FROM_DATABASE=TSB43AA22 IEEE-1394 Controller (PHY/Link Integrated) + +pci:v0000104Cd00008021sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=TSB43AA22 IEEE-1394 Controller (PHY/Link Integrated) (Vaio PCG-FX403) + +pci:v0000104Cd00008021sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=TSB43AA22 IEEE-1394 Controller (PHY/Link Integrated) (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v0000104Cd00008022* + ID_MODEL_FROM_DATABASE=TSB43AB22 IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] + +pci:v0000104Cd00008022sv0000104Csd00008023* + ID_MODEL_FROM_DATABASE=TSB43AB22 IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (TSB43AB22/A IEEE-1394a-2000 Controller (PHY/Link)) + +pci:v0000104Cd00008023* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] + +pci:v0000104Cd00008023sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (Precision Workstation 670 Mainboard) + +pci:v0000104Cd00008023sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (NC8000 laptop) + +pci:v0000104Cd00008023sv00001043sd0000808B* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (K8N4/A8N Series Mainboard) + +pci:v0000104Cd00008023sv00001043sd0000815B* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (P5W DH Deluxe Motherboard) + +pci:v0000104Cd00008023sv00001443sd00008023* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (FireCard400) + +pci:v0000104Cd00008023sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=TSB43AB22A IEEE-1394a-2000 Controller (PHY/Link) [iOHCI-Lynx] (Desktop Board DP35DP) + +pci:v0000104Cd00008024* + ID_MODEL_FROM_DATABASE=TSB43AB23 IEEE-1394a-2000 Controller (PHY/Link) + +pci:v0000104Cd00008024sv0000107Dsd00006620* + ID_MODEL_FROM_DATABASE=TSB43AB23 IEEE-1394a-2000 Controller (PHY/Link) (Winfast DV2000 FireWire Controller) + +pci:v0000104Cd00008024sv00001443sd00008024* + ID_MODEL_FROM_DATABASE=TSB43AB23 IEEE-1394a-2000 Controller (PHY/Link) (FireBoard Blue) + +pci:v0000104Cd00008024sv00001458sd00001000* + ID_MODEL_FROM_DATABASE=TSB43AB23 IEEE-1394a-2000 Controller (PHY/Link) (Motherboard) + +pci:v0000104Cd00008025* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller + +pci:v0000104Cd00008025sv00001043sd0000813C* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller (P5P series mainboard) + +pci:v0000104Cd00008025sv00001443sd00008025* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller (FireBoard800) + +pci:v0000104Cd00008025sv00001458sd00001000* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller (GA-K8N Ultra-9 Mainboard) + +pci:v0000104Cd00008025sv00001546sd00008025* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller (FWB-PCI01) + +pci:v0000104Cd00008025sv000017FCsd00008025* + ID_MODEL_FROM_DATABASE=TSB82AA2 IEEE-1394b Link Layer Controller (GIC3800) + +pci:v0000104Cd00008026* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) + +pci:v0000104Cd00008026sv00001025sd00000035* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) (TravelMate 660) + +pci:v0000104Cd00008026sv00001025sd0000003C* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) (Aspire 2001WLCi (Compaq CL50 motherboard)) + +pci:v0000104Cd00008026sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) (XE4500 Notebook) + +pci:v0000104Cd00008026sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) (NX9500) + +pci:v0000104Cd00008026sv00001043sd0000808D* + ID_MODEL_FROM_DATABASE=TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) (A7V333 mainboard.) + +pci:v0000104Cd00008027* + ID_MODEL_FROM_DATABASE=PCI4451 IEEE-1394 Controller + +pci:v0000104Cd00008027sv00001028sd000000E5* + ID_MODEL_FROM_DATABASE=PCI4451 IEEE-1394 Controller (Latitude C810) + +pci:v0000104Cd00008027sv00001028sd000000E6* + ID_MODEL_FROM_DATABASE=PCI4451 IEEE-1394 Controller ((Dell Inspiron 8100)) + +pci:v0000104Cd00008029* + ID_MODEL_FROM_DATABASE=PCI4510 IEEE-1394 Controller + +pci:v0000104Cd00008029sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=PCI4510 IEEE-1394 Controller (Latitude D505) + +pci:v0000104Cd00008029sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=PCI4510 IEEE-1394 Controller (Inspiron 5160) + +pci:v0000104Cd00008029sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=PCI4510 IEEE-1394 Controller (MIM2900) + +pci:v0000104Cd0000802B* + ID_MODEL_FROM_DATABASE=PCI7410,7510,7610 OHCI-Lynx Controller + +pci:v0000104Cd0000802Bsv00001028sd00000139* + ID_MODEL_FROM_DATABASE=PCI7410,7510,7610 OHCI-Lynx Controller (Latitude D400) + +pci:v0000104Cd0000802Bsv00001028sd0000014E* + ID_MODEL_FROM_DATABASE=PCI7410,7510,7610 OHCI-Lynx Controller ((Latitude D800)) + +pci:v0000104Cd0000802E* + ID_MODEL_FROM_DATABASE=PCI7x20 1394a-2000 OHCI Two-Port PHY/Link-Layer Controller + +pci:v0000104Cd0000802Esv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=PCI7x20 1394a-2000 OHCI Two-Port PHY/Link-Layer Controller (Inspiron 700m/710m) + +pci:v0000104Cd00008031* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller + +pci:v0000104Cd00008031sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller (Extensa 3000 series laptop) + +pci:v0000104Cd00008031sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller (Aspire 5024WLMi) + +pci:v0000104Cd00008031sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller (Compaq nw8240/nx8220) + +pci:v0000104Cd00008031sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller (NX6110/NC6120) + +pci:v0000104Cd00008031sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=PCIxx21/x515 Cardbus Controller (MX6125) + +pci:v0000104Cd00008032* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller + +pci:v0000104Cd00008032sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller (Extensa 3000 series laptop) + +pci:v0000104Cd00008032sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller (Aspire 5024WLMi) + +pci:v0000104Cd00008032sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller (Compaq nw8240/nx8220) + +pci:v0000104Cd00008032sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller (NX6110/NC6120) + +pci:v0000104Cd00008032sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=OHCI Compliant IEEE 1394 Host Controller (MX6125) + +pci:v0000104Cd00008033* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller + +pci:v0000104Cd00008033sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller (Extensa 3000 series laptop) + +pci:v0000104Cd00008033sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller (Aspire 5024WLMi) + +pci:v0000104Cd00008033sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller (Compaq nw8240/nx8220) + +pci:v0000104Cd00008033sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller (NX6110/NC6120) + +pci:v0000104Cd00008033sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=PCIxx21 Integrated FlashMedia Controller (MX6125) + +pci:v0000104Cd00008034* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Secure Digital Controller + +pci:v0000104Cd00008034sv00001025sd00000080* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Secure Digital Controller (Aspire 5024WLMi) + +pci:v0000104Cd00008034sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Secure Digital Controller (Compaq nw8240/nx8220) + +pci:v0000104Cd00008034sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Secure Digital Controller (NX6110/NC6120) + +pci:v0000104Cd00008034sv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Secure Digital Controller (MX6125) + +pci:v0000104Cd00008035* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Smart Card Controller + +pci:v0000104Cd00008035sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Smart Card Controller (Compaq nw8240/nx8220) + +pci:v0000104Cd00008035sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=PCI6411/6421/6611/6621/7411/7421/7611/7621 Smart Card Controller (NX6110/NC6120) + +pci:v0000104Cd00008036* + ID_MODEL_FROM_DATABASE=PCI6515 Cardbus Controller + +pci:v0000104Cd00008038* + ID_MODEL_FROM_DATABASE=PCI6515 SmartCard Controller + +pci:v0000104Cd00008039* + ID_MODEL_FROM_DATABASE=PCIxx12 Cardbus Controller + +pci:v0000104Cd00008039sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=PCIxx12 Cardbus Controller (Compaq nx9420 Notebook) + +pci:v0000104Cd00008039sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=PCIxx12 Cardbus Controller (NC2400) + +pci:v0000104Cd00008039sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=PCIxx12 Cardbus Controller (Compaq nw8440) + +pci:v0000104Cd00008039sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=PCIxx12 Cardbus Controller (VAIO VGN-NR120E) + +pci:v0000104Cd0000803A* + ID_MODEL_FROM_DATABASE=PCIxx12 OHCI Compliant IEEE 1394 Host Controller + +pci:v0000104Cd0000803Asv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=PCIxx12 OHCI Compliant IEEE 1394 Host Controller (nx9420) + +pci:v0000104Cd0000803Asv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=PCIxx12 OHCI Compliant IEEE 1394 Host Controller (NC2400) + +pci:v0000104Cd0000803Asv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=PCIxx12 OHCI Compliant IEEE 1394 Host Controller (Compaq nw8440) + +pci:v0000104Cd0000803Asv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=PCIxx12 OHCI Compliant IEEE 1394 Host Controller (VAIO VGN-NR120E) + +pci:v0000104Cd0000803B* + ID_MODEL_FROM_DATABASE=5-in-1 Multimedia Card Reader (SD/MMC/MS/MS PRO/xD) + +pci:v0000104Cd0000803Bsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=5-in-1 Multimedia Card Reader (SD/MMC/MS/MS PRO/xD) (nx9420) + +pci:v0000104Cd0000803Bsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=5-in-1 Multimedia Card Reader (SD/MMC/MS/MS PRO/xD) (Compaq nw8440) + +pci:v0000104Cd0000803Bsv0000104Dsd00008212* + ID_MODEL_FROM_DATABASE=5-in-1 Multimedia Card Reader (SD/MMC/MS/MS PRO/xD) (VAIO VGN-N21E) + +pci:v0000104Cd0000803Bsv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=5-in-1 Multimedia Card Reader (SD/MMC/MS/MS PRO/xD) (VAIO VGN-NR120E) + +pci:v0000104Cd0000803C* + ID_MODEL_FROM_DATABASE=PCIxx12 SDA Standard Compliant SD Host Controller + +pci:v0000104Cd0000803Csv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=PCIxx12 SDA Standard Compliant SD Host Controller (nx9420) + +pci:v0000104Cd0000803Csv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=PCIxx12 SDA Standard Compliant SD Host Controller (Compaq nw8440) + +pci:v0000104Cd0000803D* + ID_MODEL_FROM_DATABASE=PCIxx12 GemCore based SmartCard controller + +pci:v0000104Cd0000803Dsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=PCIxx12 GemCore based SmartCard controller (Compaq nx9420 Notebook) + +pci:v0000104Cd0000803Dsv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=PCIxx12 GemCore based SmartCard controller (NC2400) + +pci:v0000104Cd0000803Dsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=PCIxx12 GemCore based SmartCard controller (nc8430) + +pci:v0000104Cd0000803Dsv0000103Csd000030AA* + ID_MODEL_FROM_DATABASE=PCIxx12 GemCore based SmartCard controller (nc6310) + +pci:v0000104Cd00008101* + ID_MODEL_FROM_DATABASE=TSB43DB42 IEEE-1394a-2000 Controller (PHY/Link) + +pci:v0000104Cd00008201* + ID_MODEL_FROM_DATABASE=PCI1620 Firmware Loading Function + +pci:v0000104Cd00008204* + ID_MODEL_FROM_DATABASE=PCI7410/7510/7610 PCI Firmware Loading Function + +pci:v0000104Cd00008204sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=PCI7410/7510/7610 PCI Firmware Loading Function (Latitude D400) + +pci:v0000104Cd00008204sv00001028sd0000014E* + ID_MODEL_FROM_DATABASE=PCI7410/7510/7610 PCI Firmware Loading Function (Latitude D800) + +pci:v0000104Cd00008231* + ID_MODEL_FROM_DATABASE=XIO2000(A)/XIO2200A PCI Express-to-PCI Bridge + +pci:v0000104Cd00008231sv00005678sd00001234* + ID_MODEL_FROM_DATABASE=XIO2000(A)/XIO2200A PCI Express-to-PCI Bridge (DC-1394 PCIe) + +pci:v0000104Cd00008232* + ID_MODEL_FROM_DATABASE=XIO3130 PCI Express Switch (Upstream) + +pci:v0000104Cd00008233* + ID_MODEL_FROM_DATABASE=XIO3130 PCI Express Switch (Downstream) + +pci:v0000104Cd00008235* + ID_MODEL_FROM_DATABASE=XIO2200A IEEE-1394a-2000 Controller (PHY/Link) + +pci:v0000104Cd00008235sv00005678sd00001234* + ID_MODEL_FROM_DATABASE=XIO2200A IEEE-1394a-2000 Controller (PHY/Link) (DC-1394 PCIe) + +pci:v0000104Cd0000823E* + ID_MODEL_FROM_DATABASE=XIO2213A/B/XIO2221 PCI Express to PCI Bridge [Cheetah Express] + +pci:v0000104Cd0000823F* + ID_MODEL_FROM_DATABASE=XIO2213A/B/XIO2221 IEEE-1394b OHCI Controller [Cheetah Express] + +pci:v0000104Cd0000823Fsv00001546sd0000803C* + ID_MODEL_FROM_DATABASE=XIO2213A/B/XIO2221 IEEE-1394b OHCI Controller [Cheetah Express] (FWB-PCIE1X11B) + +pci:v0000104Cd00008240* + ID_MODEL_FROM_DATABASE=XIO2001 PCI Express-to-PCI Bridge + +pci:v0000104Cd00008241* + ID_MODEL_FROM_DATABASE=TUSB73x0 SuperSpeed USB 3.0 xHCI Host Controller + +pci:v0000104Cd00008400* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface + +pci:v0000104Cd00008400sv00001186sd00003B00* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface (DWL-650+ PC Card cardbus 22Mbs Wireless Adapter [AirPlus]) + +pci:v0000104Cd00008400sv00001186sd00003B01* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface (DWL-520+ 22Mbps PCI Wireless Adapter) + +pci:v0000104Cd00008400sv00001395sd00002201* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface (WL22-PC) + +pci:v0000104Cd00008400sv000016ABsd00008501* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface (WL-8305 IEEE802.11b+ Wireless LAN PCI Adapter) + +pci:v0000104Cd00008401* + ID_MODEL_FROM_DATABASE=ACX 100 22Mbps Wireless Interface + +pci:v0000104Cd00008888* + ID_MODEL_FROM_DATABASE=Multicore DSP+ARM KeyStone II SOC + +pci:v0000104Cd00009000* + ID_MODEL_FROM_DATABASE=Wireless Interface (of unknown type) + +pci:v0000104Cd00009065* + ID_MODEL_FROM_DATABASE=TMS320DM642 + +pci:v0000104Cd00009066* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface + +pci:v0000104Cd00009066sv00000308sd00003404* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (G-102 v1 802.11g Wireless Cardbus Adapter) + +pci:v0000104Cd00009066sv00000308sd00003406* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (G-162 v2 802.11g Wireless Cardbus Adapter) + +pci:v0000104Cd00009066sv0000104Csd00009066* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (WL212 Sitecom Wireless Network PCI-Card 100M (Version 1)) + +pci:v0000104Cd00009066sv0000104Csd00009067* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (TNETW1130GVF) + +pci:v0000104Cd00009066sv0000104Csd00009096* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (Trendnet TEW-412PC Wireless PCI Adapter (Version A)) + +pci:v0000104Cd00009066sv00001186sd00003B04* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (DWL-G520+ Wireless PCI Adapter) + +pci:v0000104Cd00009066sv00001186sd00003B05* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (DWL-G650+ AirPlusG+ CardBus Wireless LAN) + +pci:v0000104Cd00009066sv00001186sd00003B08* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.B1)) + +pci:v0000104Cd00009066sv00001385sd00004C00* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (WG311v2 802.11g Wireless PCI Adapter) + +pci:v0000104Cd00009066sv000013D1sd0000ABA0* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (SWLMP-54108 108Mbps Wireless mini PCI card 802.11g+) + +pci:v0000104Cd00009066sv000014EAsd0000AB07* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (GW-NS54GM Wireless Cardbus Adapter) + +pci:v0000104Cd00009066sv000016ECsd0000010D* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (USR5416 802.11g Wireless Turbo PCI Adapter) + +pci:v0000104Cd00009066sv000016ECsd0000010E* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (USR5410 802.11g Wireless Cardbus Adapter) + +pci:v0000104Cd00009066sv00001737sd00000033* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (WPC54G v2 802.11g Wireless-G Notebook Adapter) + +pci:v0000104Cd00009066sv000017CFsd00000032* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (G-162 v1 802.11g Wireless Cardbus Adapter) + +pci:v0000104Cd00009066sv000017CFsd00000033* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (Z-Com XG650 Wireless miniPCI 802.11b/g) + +pci:v0000104Cd00009066sv0000187Esd0000340B* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (G-302 v2 802.11g Wireless PCI Adapter) + +pci:v0000104Cd00009066sv0000187Esd0000340C* + ID_MODEL_FROM_DATABASE=ACX 111 54Mbps Wireless Interface (G-360 v2 802.11g Wireless PCI Adapter) + +pci:v0000104Cd0000A001* + ID_MODEL_FROM_DATABASE=TDC1570 + +pci:v0000104Cd0000A100* + ID_MODEL_FROM_DATABASE=TDC1561 + +pci:v0000104Cd0000A102* + ID_MODEL_FROM_DATABASE=TNETA1575 HyperSAR Plus w/PCI Host i/f & UTOPIA i/f + +pci:v0000104Cd0000A106* + ID_MODEL_FROM_DATABASE=TMS320C6414 TMS320C6415 TMS320C6416 + +pci:v0000104Cd0000A106sv0000175Csd00005000* + ID_MODEL_FROM_DATABASE=TMS320C6414 TMS320C6415 TMS320C6416 (ASI50xx Audio Adapter) + +pci:v0000104Cd0000A106sv0000175Csd00006400* + ID_MODEL_FROM_DATABASE=TMS320C6414 TMS320C6415 TMS320C6416 (ASI6400 Cobranet series) + +pci:v0000104Cd0000A106sv0000175Csd00008700* + ID_MODEL_FROM_DATABASE=TMS320C6414 TMS320C6415 TMS320C6416 (ASI87xx Radio Tuner card) + +pci:v0000104Cd0000AC10* + ID_MODEL_FROM_DATABASE=PCI1050 + +pci:v0000104Cd0000AC11* + ID_MODEL_FROM_DATABASE=PCI1053 + +pci:v0000104Cd0000AC12* + ID_MODEL_FROM_DATABASE=PCI1130 + +pci:v0000104Cd0000AC13* + ID_MODEL_FROM_DATABASE=PCI1031 + +pci:v0000104Cd0000AC15* + ID_MODEL_FROM_DATABASE=PCI1131 + +pci:v0000104Cd0000AC16* + ID_MODEL_FROM_DATABASE=PCI1250 + +pci:v0000104Cd0000AC16sv00001014sd00000092* + ID_MODEL_FROM_DATABASE=PCI1250 (ThinkPad 600) + +pci:v0000104Cd0000AC17* + ID_MODEL_FROM_DATABASE=PCI1220 + +pci:v0000104Cd0000AC18* + ID_MODEL_FROM_DATABASE=PCI1260 + +pci:v0000104Cd0000AC19* + ID_MODEL_FROM_DATABASE=PCI1221 + +pci:v0000104Cd0000AC1A* + ID_MODEL_FROM_DATABASE=PCI1210 + +pci:v0000104Cd0000AC1B* + ID_MODEL_FROM_DATABASE=PCI1450 + +pci:v0000104Cd0000AC1Bsv00000E11sd0000B113* + ID_MODEL_FROM_DATABASE=PCI1450 (Armada M700) + +pci:v0000104Cd0000AC1Bsv00001014sd00000130* + ID_MODEL_FROM_DATABASE=PCI1450 (ThinkPad 600X/A21m/T20/T22) + +pci:v0000104Cd0000AC1C* + ID_MODEL_FROM_DATABASE=PCI1225 + +pci:v0000104Cd0000AC1Csv00000E11sd0000B121* + ID_MODEL_FROM_DATABASE=PCI1225 (Armada E500) + +pci:v0000104Cd0000AC1Csv00001028sd00000088* + ID_MODEL_FROM_DATABASE=PCI1225 (Latitude CPi A400XT) + +pci:v0000104Cd0000AC1D* + ID_MODEL_FROM_DATABASE=PCI1251A + +pci:v0000104Cd0000AC1E* + ID_MODEL_FROM_DATABASE=PCI1211 + +pci:v0000104Cd0000AC1F* + ID_MODEL_FROM_DATABASE=PCI1251B + +pci:v0000104Cd0000AC20* + ID_MODEL_FROM_DATABASE=TI 2030 + +pci:v0000104Cd0000AC21* + ID_MODEL_FROM_DATABASE=PCI2031 + +pci:v0000104Cd0000AC22* + ID_MODEL_FROM_DATABASE=PCI2032 PCI Docking Bridge + +pci:v0000104Cd0000AC23* + ID_MODEL_FROM_DATABASE=PCI2250 PCI-to-PCI Bridge + +pci:v0000104Cd0000AC28* + ID_MODEL_FROM_DATABASE=PCI2050 PCI-to-PCI Bridge + +pci:v0000104Cd0000AC2C* + ID_MODEL_FROM_DATABASE=PCI2060 PCI-to-PCI Bridge + +pci:v0000104Cd0000AC30* + ID_MODEL_FROM_DATABASE=PCI1260 PC card Cardbus Controller + +pci:v0000104Cd0000AC40* + ID_MODEL_FROM_DATABASE=PCI4450 PC card Cardbus Controller + +pci:v0000104Cd0000AC41* + ID_MODEL_FROM_DATABASE=PCI4410 PC card Cardbus Controller + +pci:v0000104Cd0000AC42* + ID_MODEL_FROM_DATABASE=PCI4451 PC card Cardbus Controller + +pci:v0000104Cd0000AC42sv00001028sd000000E6* + ID_MODEL_FROM_DATABASE=PCI4451 PC card Cardbus Controller (PCI4451 PC card CardBus Controller (Inspiron 8100)) + +pci:v0000104Cd0000AC44* + ID_MODEL_FROM_DATABASE=PCI4510 PC card Cardbus Controller + +pci:v0000104Cd0000AC44sv00001028sd00000149* + ID_MODEL_FROM_DATABASE=PCI4510 PC card Cardbus Controller (Inspiron 5100) + +pci:v0000104Cd0000AC44sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=PCI4510 PC card Cardbus Controller (Latitude D505) + +pci:v0000104Cd0000AC44sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=PCI4510 PC card Cardbus Controller (Inspiron 5160) + +pci:v0000104Cd0000AC44sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=PCI4510 PC card Cardbus Controller (MIM2000) + +pci:v0000104Cd0000AC46* + ID_MODEL_FROM_DATABASE=PCI4520 PC card Cardbus Controller + +pci:v0000104Cd0000AC46sv00001014sd00000552* + ID_MODEL_FROM_DATABASE=PCI4520 PC card Cardbus Controller (ThinkPad) + +pci:v0000104Cd0000AC47* + ID_MODEL_FROM_DATABASE=PCI7510 PC card Cardbus Controller + +pci:v0000104Cd0000AC47sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=PCI7510 PC card Cardbus Controller (Latitude D400) + +pci:v0000104Cd0000AC47sv00001028sd0000013F* + ID_MODEL_FROM_DATABASE=PCI7510 PC card Cardbus Controller (Precision M60) + +pci:v0000104Cd0000AC47sv00001028sd0000014E* + ID_MODEL_FROM_DATABASE=PCI7510 PC card Cardbus Controller (Latitude D800) + +pci:v0000104Cd0000AC48* + ID_MODEL_FROM_DATABASE=PCI7610 PC Card Cardbus Controller + +pci:v0000104Cd0000AC49* + ID_MODEL_FROM_DATABASE=PCI7410 PC Card Cardbus Controller + +pci:v0000104Cd0000AC4A* + ID_MODEL_FROM_DATABASE=PCI7510/7610 CardBus Bridge + +pci:v0000104Cd0000AC4Asv00001028sd00000139* + ID_MODEL_FROM_DATABASE=PCI7510/7610 CardBus Bridge (Latitude D400) + +pci:v0000104Cd0000AC4Asv00001028sd0000014E* + ID_MODEL_FROM_DATABASE=PCI7510/7610 CardBus Bridge (Latitude D800) + +pci:v0000104Cd0000AC4B* + ID_MODEL_FROM_DATABASE=PCI7610 SD/MMC controller + +pci:v0000104Cd0000AC4C* + ID_MODEL_FROM_DATABASE=PCI7610 Memory Stick controller + +pci:v0000104Cd0000AC50* + ID_MODEL_FROM_DATABASE=PCI1410 PC card Cardbus Controller + +pci:v0000104Cd0000AC51* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller + +pci:v0000104Cd0000AC51sv00000E11sd0000004E* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (Evo N600c) + +pci:v0000104Cd0000AC51sv00001014sd00000148* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (ThinkPad A20m) + +pci:v0000104Cd0000AC51sv00001014sd0000023B* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (ThinkPad T23) + +pci:v0000104Cd0000AC51sv00001028sd000000B1* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (Latitude C600) + +pci:v0000104Cd0000AC51sv00001028sd0000012A* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (Latitude C640) + +pci:v0000104Cd0000AC51sv00001033sd000080CD* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (Versa Note VXi) + +pci:v0000104Cd0000AC51sv000010CFsd00001095* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (Lifebook S-4510/C6155) + +pci:v0000104Cd0000AC51sv0000E4BFsd00001000* + ID_MODEL_FROM_DATABASE=PCI1420 PC card Cardbus Controller (CP2-2-HIPHOP) + +pci:v0000104Cd0000AC52* + ID_MODEL_FROM_DATABASE=PCI1451 PC card Cardbus Controller + +pci:v0000104Cd0000AC53* + ID_MODEL_FROM_DATABASE=PCI1421 PC card Cardbus Controller + +pci:v0000104Cd0000AC54* + ID_MODEL_FROM_DATABASE=PCI1620 PC Card Controller + +pci:v0000104Cd0000AC54sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=PCI1620 PC Card Controller (tc1100 tablet) + +pci:v0000104Cd0000AC55* + ID_MODEL_FROM_DATABASE=PCI1520 PC card Cardbus Controller + +pci:v0000104Cd0000AC55sv00001014sd00000512* + ID_MODEL_FROM_DATABASE=PCI1520 PC card Cardbus Controller (ThinkPad T30/T40) + +pci:v0000104Cd0000AC55sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=PCI1520 PC card Cardbus Controller (XE4500 Notebook) + +pci:v0000104Cd0000AC56* + ID_MODEL_FROM_DATABASE=PCI1510 PC card Cardbus Controller + +pci:v0000104Cd0000AC56sv00001014sd00000512* + ID_MODEL_FROM_DATABASE=PCI1510 PC card Cardbus Controller (ThinkPad R50e) + +pci:v0000104Cd0000AC56sv00001014sd00000528* + ID_MODEL_FROM_DATABASE=PCI1510 PC card Cardbus Controller (ThinkPad R40e) + +pci:v0000104Cd0000AC56sv000017AAsd00002012* + ID_MODEL_FROM_DATABASE=PCI1510 PC card Cardbus Controller (ThinkPad T60/R60 series) + +pci:v0000104Cd0000AC60* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller + +pci:v0000104Cd0000AC60sv0000175Csd00005100* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (ASI51xx Audio Adapter) + +pci:v0000104Cd0000AC60sv0000175Csd00006100* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (ASI61xx Audio Adapter) + +pci:v0000104Cd0000AC60sv0000175Csd00006200* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (ASI62xx Audio Adapter) + +pci:v0000104Cd0000AC60sv0000175Csd00008800* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (ASI88xx Audio Adapter) + +pci:v0000104Cd0000AC60sv0000186Fsd00003001* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (WR-G303 PCI radio receiver) + +pci:v0000104Cd0000AC60sv0000186Fsd00003005* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (WR-G305 PCI radio receiver) + +pci:v0000104Cd0000AC60sv0000186Fsd00003101* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (WR-G313 PCI radio receiver) + +pci:v0000104Cd0000AC60sv0000186Fsd00003105* + ID_MODEL_FROM_DATABASE=PCI2040 PCI to DSP Bridge Controller (WR-G315 PCI radio receiver) + +pci:v0000104Cd0000AC8D* + ID_MODEL_FROM_DATABASE=PCI 7620 + +pci:v0000104Cd0000AC8E* + ID_MODEL_FROM_DATABASE=PCI7420 CardBus Controller + +pci:v0000104Cd0000AC8Esv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=PCI7420 CardBus Controller (Inspiron 700m/710m) + +pci:v0000104Cd0000AC8F* + ID_MODEL_FROM_DATABASE=PCI7420/7620 SD/MS-Pro Controller + +pci:v0000104Cd0000AC8Fsv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=PCI7420/7620 SD/MS-Pro Controller (Inspiron 700m/710m) + +pci:v0000104Cd0000B001* + ID_MODEL_FROM_DATABASE=TMS320C6424 + +pci:v0000104Cd0000FE00* + ID_MODEL_FROM_DATABASE=FireWire Host Controller + +pci:v0000104Cd0000FE03* + ID_MODEL_FROM_DATABASE=12C01A FireWire Host Controller + +pci:v0000104D* + ID_VENDOR_FROM_DATABASE=Sony Corporation + +pci:v0000104Dd00008004* + ID_MODEL_FROM_DATABASE=DTL-H2500 [Playstation development board] + +pci:v0000104Dd00008009* + ID_MODEL_FROM_DATABASE=CXD1947Q i.LINK Controller + +pci:v0000104Dd00008039* + ID_MODEL_FROM_DATABASE=CXD3222 i.LINK Controller + +pci:v0000104Dd00008056* + ID_MODEL_FROM_DATABASE=Rockwell HCF 56K modem + +pci:v0000104Dd0000808A* + ID_MODEL_FROM_DATABASE=Memory Stick Controller + +pci:v0000104Dd000081CE* + ID_MODEL_FROM_DATABASE=SxS Pro memory card + +pci:v0000104Dd0000908F* + ID_MODEL_FROM_DATABASE=Aeolia ACPI + +pci:v0000104Dd0000909E* + ID_MODEL_FROM_DATABASE=Aeolia Ethernet Controller (Marvell Yukon 2 Family) + +pci:v0000104Dd0000909F* + ID_MODEL_FROM_DATABASE=Aeolia SATA AHCI Controller + +pci:v0000104Dd000090A0* + ID_MODEL_FROM_DATABASE=Aeolia SD/MMC Host Controller + +pci:v0000104Dd000090A1* + ID_MODEL_FROM_DATABASE=Aeolia PCI Express Glue and Miscellaneous Devices + +pci:v0000104Dd000090A2* + ID_MODEL_FROM_DATABASE=Aeolia DMA Controller + +pci:v0000104Dd000090A3* + ID_MODEL_FROM_DATABASE=Aeolia Memory (DDR3/SPM) + +pci:v0000104Dd000090A4* + ID_MODEL_FROM_DATABASE=Aeolia USB 3.0 xHCI Host Controller + +pci:v0000104E* + ID_VENDOR_FROM_DATABASE=Oak Technology, Inc + +pci:v0000104Ed00000017* + ID_MODEL_FROM_DATABASE=OTI-64017 + +pci:v0000104Ed00000107* + ID_MODEL_FROM_DATABASE=OTI-107 [Spitfire] + +pci:v0000104Ed00000109* + ID_MODEL_FROM_DATABASE=Video Adapter + +pci:v0000104Ed00000111* + ID_MODEL_FROM_DATABASE=OTI-64111 [Spitfire] + +pci:v0000104Ed00000217* + ID_MODEL_FROM_DATABASE=OTI-64217 + +pci:v0000104Ed00000317* + ID_MODEL_FROM_DATABASE=OTI-64317 + +pci:v0000104F* + ID_VENDOR_FROM_DATABASE=Co-time Computer Ltd + +pci:v00001050* + ID_VENDOR_FROM_DATABASE=Winbond Electronics Corp + +pci:v00001050d00000000* + ID_MODEL_FROM_DATABASE=NE2000 + +pci:v00001050d00000001* + ID_MODEL_FROM_DATABASE=W83769F + +pci:v00001050d00000033* + ID_MODEL_FROM_DATABASE=W89C33D 802.11 a/b/g BB/MAC + +pci:v00001050d00000105* + ID_MODEL_FROM_DATABASE=W82C105 + +pci:v00001050d00000840* + ID_MODEL_FROM_DATABASE=W89C840 + +pci:v00001050d00000840sv00001050sd00000001* + ID_MODEL_FROM_DATABASE=W89C840 (Ethernet Adapter) + +pci:v00001050d00000840sv00001050sd00000840* + ID_MODEL_FROM_DATABASE=W89C840 (Ethernet Adapter) + +pci:v00001050d00000940* + ID_MODEL_FROM_DATABASE=W89C940 + +pci:v00001050d00005A5A* + ID_MODEL_FROM_DATABASE=W89C940F + +pci:v00001050d00006692* + ID_MODEL_FROM_DATABASE=W6692 + +pci:v00001050d00006692sv00001043sd00001702* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, D, W)) + +pci:v00001050d00006692sv00001043sd00001703* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001050d00006692sv00001043sd00001707* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001050d00006692sv0000144Fsd00001702* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, D, W)) + +pci:v00001050d00006692sv0000144Fsd00001703* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001050d00006692sv0000144Fsd00001707* + ID_MODEL_FROM_DATABASE=W6692 (ISDN Adapter (PCI Bus, DV, W)) + +pci:v00001050d00009921* + ID_MODEL_FROM_DATABASE=W99200F MPEG-1 Video Encoder + +pci:v00001050d00009922* + ID_MODEL_FROM_DATABASE=W99200F/W9922PF MPEG-1/2 Video Encoder + +pci:v00001050d00009970* + ID_MODEL_FROM_DATABASE=W9970CF + +pci:v00001051* + ID_VENDOR_FROM_DATABASE=Anigma, Inc. + +pci:v00001052* + ID_VENDOR_FROM_DATABASE=?Young Micro Systems + +pci:v00001053* + ID_VENDOR_FROM_DATABASE=Young Micro Systems + +pci:v00001054* + ID_VENDOR_FROM_DATABASE=Hitachi, Ltd + +pci:v00001054d00003009* + ID_MODEL_FROM_DATABASE=2Gbps Fibre Channel to PCI HBA 3009 + +pci:v00001054d0000300A* + ID_MODEL_FROM_DATABASE=4Gbps Fibre Channel to PCI-X HBA 300a + +pci:v00001054d0000300B* + ID_MODEL_FROM_DATABASE=4Gbps Fibre Channel to PCI-X HBA 300b + +pci:v00001054d0000300F* + ID_MODEL_FROM_DATABASE=ColdFusion 3 Chipset Processor to I/O Controller + +pci:v00001054d00003010* + ID_MODEL_FROM_DATABASE=ColdFusion 3 Chipset Memory Controller Hub + +pci:v00001054d00003011* + ID_MODEL_FROM_DATABASE=ColdFusion 3e Chipset Processor to I/O Controller + +pci:v00001054d00003012* + ID_MODEL_FROM_DATABASE=ColdFusion 3e Chipset Memory Controller Hub + +pci:v00001054d00003017* + ID_MODEL_FROM_DATABASE=Unassigned Hitachi Shared FC Device 3017 + +pci:v00001054d0000301B* + ID_MODEL_FROM_DATABASE=Virtual VGA Device + +pci:v00001054d0000301D* + ID_MODEL_FROM_DATABASE=PCIe-to-PCIe Bridge with Virtualization IO Assist Feature + +pci:v00001054d00003020* + ID_MODEL_FROM_DATABASE=FIVE-EX based Fibre Channel to PCIe HBA + +pci:v00001054d0000302C* + ID_MODEL_FROM_DATABASE=M001 PCI Express Switch Upstream Port + +pci:v00001054d0000302D* + ID_MODEL_FROM_DATABASE=M001 PCI Express Switch Downstream Port + +pci:v00001054d00003070* + ID_MODEL_FROM_DATABASE=Hitachi FIVE-FX Fibre Channel to PCIe HBA + +pci:v00001054d00003505* + ID_MODEL_FROM_DATABASE=SH7751 PCI Controller (PCIC) + +pci:v00001054d0000350E* + ID_MODEL_FROM_DATABASE=SH7751R PCI Controller (PCIC) + +pci:v00001055* + ID_VENDOR_FROM_DATABASE=Efar Microsystems + +pci:v00001055d00009130* + ID_MODEL_FROM_DATABASE=SLC90E66 [Victory66] IDE + +pci:v00001055d00009460* + ID_MODEL_FROM_DATABASE=SLC90E66 [Victory66] ISA + +pci:v00001055d00009462* + ID_MODEL_FROM_DATABASE=SLC90E66 [Victory66] USB + +pci:v00001055d00009463* + ID_MODEL_FROM_DATABASE=SLC90E66 [Victory66] ACPI + +pci:v00001055d0000E420* + ID_MODEL_FROM_DATABASE=LAN9420/LAN9420i + +pci:v00001056* + ID_VENDOR_FROM_DATABASE=ICL + +pci:v00001057* + ID_VENDOR_FROM_DATABASE=Motorola + +pci:v00001057d00000001* + ID_MODEL_FROM_DATABASE=MPC105 [Eagle] + +pci:v00001057d00000002* + ID_MODEL_FROM_DATABASE=MPC106 [Grackle] + +pci:v00001057d00000003* + ID_MODEL_FROM_DATABASE=MPC8240 [Kahlua] + +pci:v00001057d00000004* + ID_MODEL_FROM_DATABASE=MPC107 + +pci:v00001057d00000006* + ID_MODEL_FROM_DATABASE=MPC8245 [Unity] + +pci:v00001057d00000008* + ID_MODEL_FROM_DATABASE=MPC8540 + +pci:v00001057d00000009* + ID_MODEL_FROM_DATABASE=MPC8560 + +pci:v00001057d00000012* + ID_MODEL_FROM_DATABASE=MPC8548 [PowerQUICC III] + +pci:v00001057d00000100* + ID_MODEL_FROM_DATABASE=MC145575 [HFC-PCI] + +pci:v00001057d00000431* + ID_MODEL_FROM_DATABASE=KTI829c 100VG + +pci:v00001057d00001073* + ID_MODEL_FROM_DATABASE=Nokia N770 + +pci:v00001057d00001219* + ID_MODEL_FROM_DATABASE=Nokia N800 + +pci:v00001057d00001801* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor + +pci:v00001057d00001801sv000014FBsd00000101* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Transas Radar Imitator Board [RIM]) + +pci:v00001057d00001801sv000014FBsd00000102* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Transas Radar Imitator Board [RIM-2]) + +pci:v00001057d00001801sv000014FBsd00000202* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Transas Radar Integrator Board [RIB-2]) + +pci:v00001057d00001801sv000014FBsd00000611* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (1 channel CAN bus Controller [CanPci-1]) + +pci:v00001057d00001801sv000014FBsd00000612* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (2 channels CAN bus Controller [CanPci-2]) + +pci:v00001057d00001801sv000014FBsd00000613* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (3 channels CAN bus Controller [CanPci-3]) + +pci:v00001057d00001801sv000014FBsd00000614* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (4 channels CAN bus Controller [CanPci-4]) + +pci:v00001057d00001801sv000014FBsd00000621* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (1 channel CAN bus Controller [CanPci2-1]) + +pci:v00001057d00001801sv000014FBsd00000622* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (2 channels CAN bus Controller [CanPci2-2]) + +pci:v00001057d00001801sv000014FBsd00000810* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Transas VTS Radar Integrator Board [RIB-4]) + +pci:v00001057d00001801sv0000175Csd00004200* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (ASI4215 Audio Adapter) + +pci:v00001057d00001801sv0000175Csd00004300* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (ASI43xx Audio Adapter) + +pci:v00001057d00001801sv0000175Csd00004400* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (ASI4401 Audio Adapter) + +pci:v00001057d00001801sv0000ECC0sd00000010* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Darla) + +pci:v00001057d00001801sv0000ECC0sd00000020* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Gina) + +pci:v00001057d00001801sv0000ECC0sd00000030* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Layla rev.0) + +pci:v00001057d00001801sv0000ECC0sd00000031* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Layla rev.1) + +pci:v00001057d00001801sv0000ECC0sd00000040* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Darla24 rev.0) + +pci:v00001057d00001801sv0000ECC0sd00000041* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Darla24 rev.1) + +pci:v00001057d00001801sv0000ECC0sd00000050* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Gina24 rev.0) + +pci:v00001057d00001801sv0000ECC0sd00000051* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Gina24 rev.1) + +pci:v00001057d00001801sv0000ECC0sd00000070* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Mona rev.0) + +pci:v00001057d00001801sv0000ECC0sd00000071* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Mona rev.1) + +pci:v00001057d00001801sv0000ECC0sd00000072* + ID_MODEL_FROM_DATABASE=DSP56301 Digital Signal Processor (Mona rev.2) + +pci:v00001057d000018C0* + ID_MODEL_FROM_DATABASE=MPC8265A/8266/8272 + +pci:v00001057d000018C1* + ID_MODEL_FROM_DATABASE=MPC8271/MPC8272 + +pci:v00001057d00003052* + ID_MODEL_FROM_DATABASE=SM56 Data Fax Modem + +pci:v00001057d00003410* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor + +pci:v00001057d00003410sv0000ECC0sd00000050* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Gina24 rev.0) + +pci:v00001057d00003410sv0000ECC0sd00000051* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Gina24 rev.1) + +pci:v00001057d00003410sv0000ECC0sd00000060* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Layla24) + +pci:v00001057d00003410sv0000ECC0sd00000070* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Mona rev.0) + +pci:v00001057d00003410sv0000ECC0sd00000071* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Mona rev.1) + +pci:v00001057d00003410sv0000ECC0sd00000072* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Mona rev.2) + +pci:v00001057d00003410sv0000ECC0sd00000080* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Mia rev.0) + +pci:v00001057d00003410sv0000ECC0sd00000081* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Mia rev.1) + +pci:v00001057d00003410sv0000ECC0sd00000090* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Indigo) + +pci:v00001057d00003410sv0000ECC0sd000000A0* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Indigo IO) + +pci:v00001057d00003410sv0000ECC0sd000000B0* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (Indigo DJ) + +pci:v00001057d00003410sv0000ECC0sd00000100* + ID_MODEL_FROM_DATABASE=DSP56361 Digital Signal Processor (3G) + +pci:v00001057d00004801* + ID_MODEL_FROM_DATABASE=Raven + +pci:v00001057d00004802* + ID_MODEL_FROM_DATABASE=Falcon + +pci:v00001057d00004803* + ID_MODEL_FROM_DATABASE=Hawk + +pci:v00001057d00004806* + ID_MODEL_FROM_DATABASE=CPX8216 + +pci:v00001057d00004D68* + ID_MODEL_FROM_DATABASE=20268 + +pci:v00001057d00005600* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem + +pci:v00001057d00005600sv00001057sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv00001057sd00000301* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Voice Modem) + +pci:v00001057d00005600sv00001057sd00000302* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005600sv00001057sd00005600* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Voice modem) + +pci:v00001057d00005600sv000013D2sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv000013D2sd00000301* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Voice modem) + +pci:v00001057d00005600sv000013D2sd00000302* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005600sv00001436sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv00001436sd00000301* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Voice modem) + +pci:v00001057d00005600sv00001436sd00000302* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005600sv0000144Fsd0000100C* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005600sv00001494sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv00001494sd00000301* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Voice modem) + +pci:v00001057d00005600sv000014C8sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv000014C8sd00000302* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005600sv00001668sd00000300* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Speakerphone Modem) + +pci:v00001057d00005600sv00001668sd00000302* + ID_MODEL_FROM_DATABASE=SM56 PCI Modem (SM56 PCI Fax Modem) + +pci:v00001057d00005608* + ID_MODEL_FROM_DATABASE=Wildcard X100P + +pci:v00001057d00005803* + ID_MODEL_FROM_DATABASE=MPC5200 + +pci:v00001057d00005806* + ID_MODEL_FROM_DATABASE=MCF54 Coldfire + +pci:v00001057d00005808* + ID_MODEL_FROM_DATABASE=MPC8220 + +pci:v00001057d00005809* + ID_MODEL_FROM_DATABASE=MPC5200B + +pci:v00001057d00006400* + ID_MODEL_FROM_DATABASE=MPC190 Security Processor (S1 family, encryption) + +pci:v00001057d00006405* + ID_MODEL_FROM_DATABASE=MPC184 Security Processor (S1 family) + +pci:v00001058* + ID_VENDOR_FROM_DATABASE=Electronics & Telecommunications RSH + +pci:v00001059* + ID_VENDOR_FROM_DATABASE=Kontron + +pci:v0000105A* + ID_VENDOR_FROM_DATABASE=Promise Technology, Inc. + +pci:v0000105Ad00000D30* + ID_MODEL_FROM_DATABASE=PDC20265 (FastTrak100 Lite/Ultra100) + +pci:v0000105Ad00000D30sv00001043sd00008042* + ID_MODEL_FROM_DATABASE=PDC20265 (FastTrak100 Lite/Ultra100) (AV7266-E South Bridge Promise RAID) + +pci:v0000105Ad00000D30sv0000105Asd00004D33* + ID_MODEL_FROM_DATABASE=PDC20265 (FastTrak100 Lite/Ultra100) (Ultra100) + +pci:v0000105Ad00000D38* + ID_MODEL_FROM_DATABASE=20263 + +pci:v0000105Ad00000D38sv0000105Asd00004D39* + ID_MODEL_FROM_DATABASE=20263 (Fasttrak66) + +pci:v0000105Ad00001275* + ID_MODEL_FROM_DATABASE=20275 + +pci:v0000105Ad00003318* + ID_MODEL_FROM_DATABASE=PDC20318 (SATA150 TX4) + +pci:v0000105Ad00003319* + ID_MODEL_FROM_DATABASE=PDC20319 (FastTrak S150 TX4) + +pci:v0000105Ad00003319sv0000105Asd00003319* + ID_MODEL_FROM_DATABASE=PDC20319 (FastTrak S150 TX4) (FastTrak S150 TX4 4 port SATA PCI board) + +pci:v0000105Ad00003319sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=PDC20319 (FastTrak S150 TX4) (S875WP1-E mainboard) + +pci:v0000105Ad00003371* + ID_MODEL_FROM_DATABASE=PDC20371 (FastTrak S150 TX2plus) + +pci:v0000105Ad00003373* + ID_MODEL_FROM_DATABASE=PDC20378 (FastTrak 378/SATA 378) + +pci:v0000105Ad00003373sv00001043sd000080F5* + ID_MODEL_FROM_DATABASE=PDC20378 (FastTrak 378/SATA 378) (K8V Deluxe/PC-DL Deluxe motherboard) + +pci:v0000105Ad00003373sv00001462sd0000590D* + ID_MODEL_FROM_DATABASE=PDC20378 (FastTrak 378/SATA 378) (KT6 Delta-FIS2R (MS-6590)) + +pci:v0000105Ad00003373sv00001462sd0000702E* + ID_MODEL_FROM_DATABASE=PDC20378 (FastTrak 378/SATA 378) (K8T NEO FIS2R motherboard) + +pci:v0000105Ad00003375* + ID_MODEL_FROM_DATABASE=PDC20375 (SATA150 TX2plus) + +pci:v0000105Ad00003376* + ID_MODEL_FROM_DATABASE=PDC20376 (FastTrak 376) + +pci:v0000105Ad00003376sv00001043sd0000809E* + ID_MODEL_FROM_DATABASE=PDC20376 (FastTrak 376) (A7V8X motherboard) + +pci:v0000105Ad00003515* + ID_MODEL_FROM_DATABASE=PDC40719 [FastTrak TX4300/TX4310] + +pci:v0000105Ad00003519* + ID_MODEL_FROM_DATABASE=PDC40519 (FastTrak TX4200) + +pci:v0000105Ad00003570* + ID_MODEL_FROM_DATABASE=PDC20771 [FastTrak TX2300] + +pci:v0000105Ad00003571* + ID_MODEL_FROM_DATABASE=PDC20571 (FastTrak TX2200) + +pci:v0000105Ad00003574* + ID_MODEL_FROM_DATABASE=PDC20579 SATAII 150 IDE Controller + +pci:v0000105Ad00003577* + ID_MODEL_FROM_DATABASE=PDC40779 (SATA 300 779) + +pci:v0000105Ad00003D17* + ID_MODEL_FROM_DATABASE=PDC40718 (SATA 300 TX4) + +pci:v0000105Ad00003D18* + ID_MODEL_FROM_DATABASE=PDC20518/PDC40518 (SATAII 150 TX4) + +pci:v0000105Ad00003D73* + ID_MODEL_FROM_DATABASE=PDC40775 (SATA 300 TX2plus) + +pci:v0000105Ad00003D75* + ID_MODEL_FROM_DATABASE=PDC20575 (SATAII150 TX2plus) + +pci:v0000105Ad00003F20* + ID_MODEL_FROM_DATABASE=PDC42819 [FastTrak TX2650/TX4650] + +pci:v0000105Ad00004302* + ID_MODEL_FROM_DATABASE=80333 [SuperTrak EX4350] + +pci:v0000105Ad00004D30* + ID_MODEL_FROM_DATABASE=PDC20267 (FastTrak100/Ultra100) + +pci:v0000105Ad00004D30sv0000105Asd00004D33* + ID_MODEL_FROM_DATABASE=PDC20267 (FastTrak100/Ultra100) (Ultra100) + +pci:v0000105Ad00004D30sv0000105Asd00004D39* + ID_MODEL_FROM_DATABASE=PDC20267 (FastTrak100/Ultra100) (FastTrak100) + +pci:v0000105Ad00004D30sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=PDC20267 (FastTrak100/Ultra100) (S845WD1-E mainboard) + +pci:v0000105Ad00004D33* + ID_MODEL_FROM_DATABASE=20246 + +pci:v0000105Ad00004D33sv0000105Asd00004D33* + ID_MODEL_FROM_DATABASE=20246 (IDE Controller) + +pci:v0000105Ad00004D38* + ID_MODEL_FROM_DATABASE=PDC20262 (FastTrak66/Ultra66) + +pci:v0000105Ad00004D38sv0000105Asd00004D30* + ID_MODEL_FROM_DATABASE=PDC20262 (FastTrak66/Ultra66) (Ultra Device on SuperTrak) + +pci:v0000105Ad00004D38sv0000105Asd00004D33* + ID_MODEL_FROM_DATABASE=PDC20262 (FastTrak66/Ultra66) (Ultra66) + +pci:v0000105Ad00004D38sv0000105Asd00004D39* + ID_MODEL_FROM_DATABASE=PDC20262 (FastTrak66/Ultra66) (FastTrak66) + +pci:v0000105Ad00004D68* + ID_MODEL_FROM_DATABASE=PDC20268 [Ultra100 TX2] + +pci:v0000105Ad00004D68sv0000105Asd00004D68* + ID_MODEL_FROM_DATABASE=PDC20268 [Ultra100 TX2] (Ultra100 TX2) + +pci:v0000105Ad00004D69* + ID_MODEL_FROM_DATABASE=20269 + +pci:v0000105Ad00004D69sv0000105Asd00004D68* + ID_MODEL_FROM_DATABASE=20269 (Ultra133TX2) + +pci:v0000105Ad00005275* + ID_MODEL_FROM_DATABASE=PDC20276 (MBFastTrak133 Lite) + +pci:v0000105Ad00005275sv00001043sd0000807E* + ID_MODEL_FROM_DATABASE=PDC20276 (MBFastTrak133 Lite) (A7V333 motherboard.) + +pci:v0000105Ad00005275sv0000105Asd00000275* + ID_MODEL_FROM_DATABASE=PDC20276 (MBFastTrak133 Lite) (SuperTrak SX6000 IDE) + +pci:v0000105Ad00005275sv0000105Asd00001275* + ID_MODEL_FROM_DATABASE=PDC20276 (MBFastTrak133 Lite) (MBFastTrak133 Lite (tm) Controller (RAID mode)) + +pci:v0000105Ad00005275sv00001458sd0000B001* + ID_MODEL_FROM_DATABASE=PDC20276 (MBFastTrak133 Lite) (MBUltra 133) + +pci:v0000105Ad00005300* + ID_MODEL_FROM_DATABASE=DC5300 + +pci:v0000105Ad00006268* + ID_MODEL_FROM_DATABASE=PDC20270 (FastTrak100 LP/TX2/TX4) + +pci:v0000105Ad00006268sv0000105Asd00004D68* + ID_MODEL_FROM_DATABASE=PDC20270 (FastTrak100 LP/TX2/TX4) (FastTrak100 TX2) + +pci:v0000105Ad00006269* + ID_MODEL_FROM_DATABASE=PDC20271 (FastTrak TX2000) + +pci:v0000105Ad00006269sv0000105Asd00006269* + ID_MODEL_FROM_DATABASE=PDC20271 (FastTrak TX2000) (FastTrak TX2/TX2000) + +pci:v0000105Ad00006300* + ID_MODEL_FROM_DATABASE=PDC81731 [FastTrak SX8300] + +pci:v0000105Ad00006621* + ID_MODEL_FROM_DATABASE=PDC20621 (FastTrak S150 SX4/FastTrak SX4000 lite) + +pci:v0000105Ad00006622* + ID_MODEL_FROM_DATABASE=PDC20621 [SATA150 SX4] 4 Channel IDE RAID Controller + +pci:v0000105Ad00006624* + ID_MODEL_FROM_DATABASE=PDC20621 [FastTrak SX4100] + +pci:v0000105Ad00006626* + ID_MODEL_FROM_DATABASE=PDC20618 (Ultra 618) + +pci:v0000105Ad00006629* + ID_MODEL_FROM_DATABASE=PDC20619 (FastTrak TX4000) + +pci:v0000105Ad00007275* + ID_MODEL_FROM_DATABASE=PDC20277 (SBFastTrak133 Lite) + +pci:v0000105Ad00008002* + ID_MODEL_FROM_DATABASE=SATAII150 SX8 + +pci:v0000105Ad00008350* + ID_MODEL_FROM_DATABASE=80333 [SuperTrak EX8350/EX16350], 80331 [SuperTrak EX8300/EX16300] + +pci:v0000105Ad00008650* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] + +pci:v0000105Ad00008650sv0000105Asd00004600* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX4650A) + +pci:v0000105Ad00008650sv0000105Asd00004601* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX4650) + +pci:v0000105Ad00008650sv0000105Asd00004610* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX4650EL) + +pci:v0000105Ad00008650sv0000105Asd00008600* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8650EL) + +pci:v0000105Ad00008650sv0000105Asd00008601* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8650A) + +pci:v0000105Ad00008650sv0000105Asd00008602* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8654) + +pci:v0000105Ad00008650sv0000105Asd00008603* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8658) + +pci:v0000105Ad00008650sv0000105Asd00008604* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8650) + +pci:v0000105Ad00008650sv0000105Asd00008610* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX8650M) + +pci:v0000105Ad00008650sv0000105Asd0000A600* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX12650) + +pci:v0000105Ad00008650sv0000105Asd0000B600* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX16650) + +pci:v0000105Ad00008650sv0000105Asd0000B601* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX16654) + +pci:v0000105Ad00008650sv0000105Asd0000B602* + ID_MODEL_FROM_DATABASE=81384 [SuperTrak EX SAS and SATA RAID Controller] (SuperTrak EX16658) + +pci:v0000105Ad00008760* + ID_MODEL_FROM_DATABASE=PM8010 [SuperTrak EX SAS and SATA 6G RAID Controller] + +pci:v0000105Ad0000C350* + ID_MODEL_FROM_DATABASE=80333 [SuperTrak EX12350] + +pci:v0000105Ad0000E350* + ID_MODEL_FROM_DATABASE=80333 [SuperTrak EX24350] + +pci:v0000105B* + ID_VENDOR_FROM_DATABASE=Foxconn International, Inc. + +pci:v0000105C* + ID_VENDOR_FROM_DATABASE=Wipro Infotech Limited + +pci:v0000105D* + ID_VENDOR_FROM_DATABASE=Number 9 Computer Company + +pci:v0000105Dd00002309* + ID_MODEL_FROM_DATABASE=Imagine 128 + +pci:v0000105Dd00002339* + ID_MODEL_FROM_DATABASE=Imagine 128-II + +pci:v0000105Dd00002339sv0000105Dsd00000000* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000001* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000002* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000003* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000004* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000005* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000006* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000007* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 4Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000008* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2e 4Mb DRAM) + +pci:v0000105Dd00002339sv0000105Dsd00000009* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2e 4Mb DRAM) + +pci:v0000105Dd00002339sv0000105Dsd0000000A* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 8Mb VRAM) + +pci:v0000105Dd00002339sv0000105Dsd0000000B* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Imagine 128 series 2 8Mb H-VRAM) + +pci:v0000105Dd00002339sv000011A4sd0000000A* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000000* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000004* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000005* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000006* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000008* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd00000009* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd0000000A* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd00002339sv000013CCsd0000000C* + ID_MODEL_FROM_DATABASE=Imagine 128-II (Barco Metheus 5 Megapixel) + +pci:v0000105Dd0000493D* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] + +pci:v0000105Dd0000493Dsv000011A4sd0000000A* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000011A4sd0000000B* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd00000002* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 4 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd00000003* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd00000007* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd00000008* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd00000009* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd0000493Dsv000013CCsd0000000A* + ID_MODEL_FROM_DATABASE=Imagine 128 T2R [Ticket to Ride] (Barco Metheus 5 Megapixel, Dual Head) + +pci:v0000105Dd00005348* + ID_MODEL_FROM_DATABASE=Revolution 4 + +pci:v0000105Dd00005348sv0000105Dsd00000037* + ID_MODEL_FROM_DATABASE=Revolution 4 (Revolution IV-FP AGP (For SGI 1600SW)) + +pci:v0000105Dd00005348sv000011A4sd00000028* + ID_MODEL_FROM_DATABASE=Revolution 4 (PVS5600M) + +pci:v0000105Dd00005348sv000011A4sd00000038* + ID_MODEL_FROM_DATABASE=Revolution 4 (PVS5600D) + +pci:v0000105E* + ID_VENDOR_FROM_DATABASE=Vtech Computers Ltd + +pci:v0000105F* + ID_VENDOR_FROM_DATABASE=Infotronic America Inc + +pci:v00001060* + ID_VENDOR_FROM_DATABASE=United Microelectronics [UMC] + +pci:v00001060d00000001* + ID_MODEL_FROM_DATABASE=UM82C881 + +pci:v00001060d00000002* + ID_MODEL_FROM_DATABASE=UM82C886 + +pci:v00001060d00000101* + ID_MODEL_FROM_DATABASE=UM8673F + +pci:v00001060d00000881* + ID_MODEL_FROM_DATABASE=UM8881 + +pci:v00001060d00000886* + ID_MODEL_FROM_DATABASE=UM8886F + +pci:v00001060d00000891* + ID_MODEL_FROM_DATABASE=UM8891A + +pci:v00001060d00001001* + ID_MODEL_FROM_DATABASE=UM886A + +pci:v00001060d0000673A* + ID_MODEL_FROM_DATABASE=UM8886BF + +pci:v00001060d0000673B* + ID_MODEL_FROM_DATABASE=EIDE Master/DMA + +pci:v00001060d00008710* + ID_MODEL_FROM_DATABASE=UM8710 + +pci:v00001060d0000886A* + ID_MODEL_FROM_DATABASE=UM8886A + +pci:v00001060d00008881* + ID_MODEL_FROM_DATABASE=UM8881F + +pci:v00001060d00008886* + ID_MODEL_FROM_DATABASE=UM8886F + +pci:v00001060d0000888A* + ID_MODEL_FROM_DATABASE=UM8886A + +pci:v00001060d00008891* + ID_MODEL_FROM_DATABASE=UM8891A + +pci:v00001060d00009017* + ID_MODEL_FROM_DATABASE=UM9017F + +pci:v00001060d00009018* + ID_MODEL_FROM_DATABASE=UM9018 + +pci:v00001060d00009026* + ID_MODEL_FROM_DATABASE=UM9026 + +pci:v00001060d0000E881* + ID_MODEL_FROM_DATABASE=UM8881N + +pci:v00001060d0000E886* + ID_MODEL_FROM_DATABASE=UM8886N + +pci:v00001060d0000E88A* + ID_MODEL_FROM_DATABASE=UM8886N + +pci:v00001060d0000E891* + ID_MODEL_FROM_DATABASE=UM8891N + +pci:v00001061* + ID_VENDOR_FROM_DATABASE=I.I.T. + +pci:v00001061d00000001* + ID_MODEL_FROM_DATABASE=AGX016 + +pci:v00001061d00000002* + ID_MODEL_FROM_DATABASE=IIT3204/3501 + +pci:v00001062* + ID_VENDOR_FROM_DATABASE=Maspar Computer Corp + +pci:v00001063* + ID_VENDOR_FROM_DATABASE=Ocean Office Automation + +pci:v00001064* + ID_VENDOR_FROM_DATABASE=Alcatel + +pci:v00001064d00001102* + ID_MODEL_FROM_DATABASE=Dynamite 2840 (ADSL PCI modem) + +pci:v00001065* + ID_VENDOR_FROM_DATABASE=Texas Microsystems + +pci:v00001066* + ID_VENDOR_FROM_DATABASE=PicoPower Technology + +pci:v00001066d00000000* + ID_MODEL_FROM_DATABASE=PT80C826 + +pci:v00001066d00000001* + ID_MODEL_FROM_DATABASE=PT86C521 [Vesuvius v1] Host Bridge + +pci:v00001066d00000002* + ID_MODEL_FROM_DATABASE=PT86C523 [Vesuvius v3] PCI-ISA Bridge Master + +pci:v00001066d00000003* + ID_MODEL_FROM_DATABASE=PT86C524 [Nile] PCI-to-PCI Bridge + +pci:v00001066d00000004* + ID_MODEL_FROM_DATABASE=PT86C525 [Nile-II] PCI-to-PCI Bridge + +pci:v00001066d00000005* + ID_MODEL_FROM_DATABASE=National PC87550 System Controller + +pci:v00001066d00008002* + ID_MODEL_FROM_DATABASE=PT86C523 [Vesuvius v3] PCI-ISA Bridge Slave + +pci:v00001067* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric + +pci:v00001067d00000301* + ID_MODEL_FROM_DATABASE=AccelGraphics AccelECLIPSE + +pci:v00001067d00000304* + ID_MODEL_FROM_DATABASE=AccelGALAXY A2100 [OEM Evans & Sutherland] + +pci:v00001067d00000308* + ID_MODEL_FROM_DATABASE=Tornado 3000 [OEM Evans & Sutherland] + +pci:v00001067d00001002* + ID_MODEL_FROM_DATABASE=VG500 [VolumePro Volume Rendering Accelerator] + +pci:v00001068* + ID_VENDOR_FROM_DATABASE=Diversified Technology + +pci:v00001069* + ID_VENDOR_FROM_DATABASE=Mylex Corporation + +pci:v00001069d00000001* + ID_MODEL_FROM_DATABASE=DAC960P + +pci:v00001069d00000002* + ID_MODEL_FROM_DATABASE=DAC960PD + +pci:v00001069d00000010* + ID_MODEL_FROM_DATABASE=DAC960PG + +pci:v00001069d00000020* + ID_MODEL_FROM_DATABASE=DAC960LA + +pci:v00001069d00000050* + ID_MODEL_FROM_DATABASE=AcceleRAID 352/170/160 support Device + +pci:v00001069d00000050sv00001069sd00000050* + ID_MODEL_FROM_DATABASE=AcceleRAID 352/170/160 support Device (AcceleRAID 352 support Device) + +pci:v00001069d00000050sv00001069sd00000052* + ID_MODEL_FROM_DATABASE=AcceleRAID 352/170/160 support Device (AcceleRAID 170 support Device) + +pci:v00001069d00000050sv00001069sd00000054* + ID_MODEL_FROM_DATABASE=AcceleRAID 352/170/160 support Device (AcceleRAID 160 support Device) + +pci:v00001069d0000B166* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device + +pci:v00001069d0000B166sv00001014sd00000242* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (iSeries 2872 DASD IOA) + +pci:v00001069d0000B166sv00001014sd00000266* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (Dual Channel PCI-X U320 SCSI Adapter) + +pci:v00001069d0000B166sv00001014sd00000278* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (Dual Channel PCI-X U320 SCSI RAID Adapter) + +pci:v00001069d0000B166sv00001014sd000002D3* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (Dual Channel PCI-X U320 SCSI Adapter) + +pci:v00001069d0000B166sv00001014sd000002D4* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (Dual Channel PCI-X U320 SCSI RAID Adapter) + +pci:v00001069d0000B166sv00001069sd00000200* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (AcceleRAID 400, Single Channel, PCI-X, U320, SCSI RAID) + +pci:v00001069d0000B166sv00001069sd00000202* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (AcceleRAID Sapphire, Dual Channel, PCI-X, U320, SCSI RAID) + +pci:v00001069d0000B166sv00001069sd00000204* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (AcceleRAID 500, Dual Channel, Low-Profile, PCI-X, U320, SCSI RAID) + +pci:v00001069d0000B166sv00001069sd00000206* + ID_MODEL_FROM_DATABASE=AcceleRAID 600/500/400/Sapphire support Device (AcceleRAID 600, Dual Channel, PCI-X, U320, SCSI RAID) + +pci:v00001069d0000BA55* + ID_MODEL_FROM_DATABASE=eXtremeRAID 1100 support Device + +pci:v00001069d0000BA56* + ID_MODEL_FROM_DATABASE=eXtremeRAID 2000/3000 support Device + +pci:v00001069d0000BA56sv00001069sd00000030* + ID_MODEL_FROM_DATABASE=eXtremeRAID 2000/3000 support Device (eXtremeRAID 3000 support Device) + +pci:v00001069d0000BA56sv00001069sd00000040* + ID_MODEL_FROM_DATABASE=eXtremeRAID 2000/3000 support Device (eXtremeRAID 2000 support Device) + +pci:v00001069d0000BA57* + ID_MODEL_FROM_DATABASE=eXtremeRAID 4000/5000 support Device + +pci:v00001069d0000BA57sv00001069sd00000072* + ID_MODEL_FROM_DATABASE=eXtremeRAID 4000/5000 support Device (eXtremeRAID 5000 support Device) + +pci:v0000106A* + ID_VENDOR_FROM_DATABASE=Aten Research Inc + +pci:v0000106B* + ID_VENDOR_FROM_DATABASE=Apple Inc. + +pci:v0000106Bd00000001* + ID_MODEL_FROM_DATABASE=Bandit PowerPC host bridge + +pci:v0000106Bd00000002* + ID_MODEL_FROM_DATABASE=Grand Central I/O + +pci:v0000106Bd00000003* + ID_MODEL_FROM_DATABASE=Control Video + +pci:v0000106Bd00000004* + ID_MODEL_FROM_DATABASE=PlanB Video-In + +pci:v0000106Bd00000007* + ID_MODEL_FROM_DATABASE=O'Hare I/O + +pci:v0000106Bd0000000C* + ID_MODEL_FROM_DATABASE=DOS on Mac + +pci:v0000106Bd0000000E* + ID_MODEL_FROM_DATABASE=Hydra Mac I/O + +pci:v0000106Bd00000010* + ID_MODEL_FROM_DATABASE=Heathrow Mac I/O + +pci:v0000106Bd00000017* + ID_MODEL_FROM_DATABASE=Paddington Mac I/O + +pci:v0000106Bd00000018* + ID_MODEL_FROM_DATABASE=UniNorth FireWire + +pci:v0000106Bd00000019* + ID_MODEL_FROM_DATABASE=KeyLargo USB + +pci:v0000106Bd0000001E* + ID_MODEL_FROM_DATABASE=UniNorth Internal PCI + +pci:v0000106Bd0000001F* + ID_MODEL_FROM_DATABASE=UniNorth PCI + +pci:v0000106Bd00000020* + ID_MODEL_FROM_DATABASE=UniNorth AGP + +pci:v0000106Bd00000021* + ID_MODEL_FROM_DATABASE=UniNorth GMAC (Sun GEM) + +pci:v0000106Bd00000022* + ID_MODEL_FROM_DATABASE=KeyLargo Mac I/O + +pci:v0000106Bd00000024* + ID_MODEL_FROM_DATABASE=UniNorth/Pangea GMAC (Sun GEM) + +pci:v0000106Bd00000025* + ID_MODEL_FROM_DATABASE=KeyLargo/Pangea Mac I/O + +pci:v0000106Bd00000026* + ID_MODEL_FROM_DATABASE=KeyLargo/Pangea USB + +pci:v0000106Bd00000027* + ID_MODEL_FROM_DATABASE=UniNorth/Pangea AGP + +pci:v0000106Bd00000028* + ID_MODEL_FROM_DATABASE=UniNorth/Pangea PCI + +pci:v0000106Bd00000029* + ID_MODEL_FROM_DATABASE=UniNorth/Pangea Internal PCI + +pci:v0000106Bd0000002D* + ID_MODEL_FROM_DATABASE=UniNorth 1.5 AGP + +pci:v0000106Bd0000002E* + ID_MODEL_FROM_DATABASE=UniNorth 1.5 PCI + +pci:v0000106Bd0000002F* + ID_MODEL_FROM_DATABASE=UniNorth 1.5 Internal PCI + +pci:v0000106Bd00000030* + ID_MODEL_FROM_DATABASE=UniNorth/Pangea FireWire + +pci:v0000106Bd00000031* + ID_MODEL_FROM_DATABASE=UniNorth 2 FireWire + +pci:v0000106Bd00000031sv0000106Bsd00005811* + ID_MODEL_FROM_DATABASE=UniNorth 2 FireWire (iBook G4 2004) + +pci:v0000106Bd00000032* + ID_MODEL_FROM_DATABASE=UniNorth 2 GMAC (Sun GEM) + +pci:v0000106Bd00000033* + ID_MODEL_FROM_DATABASE=UniNorth 2 ATA/100 + +pci:v0000106Bd00000034* + ID_MODEL_FROM_DATABASE=UniNorth 2 AGP + +pci:v0000106Bd00000035* + ID_MODEL_FROM_DATABASE=UniNorth 2 PCI + +pci:v0000106Bd00000036* + ID_MODEL_FROM_DATABASE=UniNorth 2 Internal PCI + +pci:v0000106Bd0000003B* + ID_MODEL_FROM_DATABASE=UniNorth/Intrepid ATA/100 + +pci:v0000106Bd0000003E* + ID_MODEL_FROM_DATABASE=KeyLargo/Intrepid Mac I/O + +pci:v0000106Bd0000003F* + ID_MODEL_FROM_DATABASE=KeyLargo/Intrepid USB + +pci:v0000106Bd0000003Fsv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=KeyLargo/Intrepid USB (QEMU Virtual Machine) + +pci:v0000106Bd00000040* + ID_MODEL_FROM_DATABASE=K2 KeyLargo USB + +pci:v0000106Bd00000041* + ID_MODEL_FROM_DATABASE=K2 KeyLargo Mac/IO + +pci:v0000106Bd00000042* + ID_MODEL_FROM_DATABASE=K2 FireWire + +pci:v0000106Bd00000043* + ID_MODEL_FROM_DATABASE=K2 ATA/100 + +pci:v0000106Bd00000045* + ID_MODEL_FROM_DATABASE=K2 HT-PCI Bridge + +pci:v0000106Bd00000046* + ID_MODEL_FROM_DATABASE=K2 HT-PCI Bridge + +pci:v0000106Bd00000047* + ID_MODEL_FROM_DATABASE=K2 HT-PCI Bridge + +pci:v0000106Bd00000048* + ID_MODEL_FROM_DATABASE=K2 HT-PCI Bridge + +pci:v0000106Bd00000049* + ID_MODEL_FROM_DATABASE=K2 HT-PCI Bridge + +pci:v0000106Bd0000004A* + ID_MODEL_FROM_DATABASE=CPC945 HT Bridge + +pci:v0000106Bd0000004B* + ID_MODEL_FROM_DATABASE=U3 AGP + +pci:v0000106Bd0000004C* + ID_MODEL_FROM_DATABASE=K2 GMAC (Sun GEM) + +pci:v0000106Bd0000004F* + ID_MODEL_FROM_DATABASE=Shasta Mac I/O + +pci:v0000106Bd00000050* + ID_MODEL_FROM_DATABASE=Shasta IDE + +pci:v0000106Bd00000051* + ID_MODEL_FROM_DATABASE=Shasta (Sun GEM) + +pci:v0000106Bd00000052* + ID_MODEL_FROM_DATABASE=Shasta Firewire + +pci:v0000106Bd00000053* + ID_MODEL_FROM_DATABASE=Shasta PCI Bridge + +pci:v0000106Bd00000054* + ID_MODEL_FROM_DATABASE=Shasta PCI Bridge + +pci:v0000106Bd00000055* + ID_MODEL_FROM_DATABASE=Shasta PCI Bridge + +pci:v0000106Bd00000056* + ID_MODEL_FROM_DATABASE=U4 PCIe + +pci:v0000106Bd00000057* + ID_MODEL_FROM_DATABASE=U3 HT Bridge + +pci:v0000106Bd00000058* + ID_MODEL_FROM_DATABASE=U3L AGP Bridge + +pci:v0000106Bd00000059* + ID_MODEL_FROM_DATABASE=U3H AGP Bridge + +pci:v0000106Bd0000005B* + ID_MODEL_FROM_DATABASE=CPC945 PCIe Bridge + +pci:v0000106Bd00000066* + ID_MODEL_FROM_DATABASE=Intrepid2 AGP Bridge + +pci:v0000106Bd00000067* + ID_MODEL_FROM_DATABASE=Intrepid2 PCI Bridge + +pci:v0000106Bd00000068* + ID_MODEL_FROM_DATABASE=Intrepid2 PCI Bridge + +pci:v0000106Bd00000069* + ID_MODEL_FROM_DATABASE=Intrepid2 ATA/100 + +pci:v0000106Bd0000006A* + ID_MODEL_FROM_DATABASE=Intrepid2 Firewire + +pci:v0000106Bd0000006B* + ID_MODEL_FROM_DATABASE=Intrepid2 GMAC (Sun GEM) + +pci:v0000106Bd00000074* + ID_MODEL_FROM_DATABASE=U4 HT Bridge + +pci:v0000106Bd00001645* + ID_MODEL_FROM_DATABASE=Broadcom NetXtreme BCM5701 Gigabit Ethernet + +pci:v0000106Bd00002001* + ID_MODEL_FROM_DATABASE=PCI Express SSD + +pci:v0000106C* + ID_VENDOR_FROM_DATABASE=Hynix Semiconductor + +pci:v0000106Cd00008139* + ID_MODEL_FROM_DATABASE=8139c 100BaseTX Ethernet Controller + +pci:v0000106Cd00008801* + ID_MODEL_FROM_DATABASE=Dual Pentium ISA/PCI Motherboard + +pci:v0000106Cd00008802* + ID_MODEL_FROM_DATABASE=PowerPC ISA/PCI Motherboard + +pci:v0000106Cd00008803* + ID_MODEL_FROM_DATABASE=Dual Window Graphics Accelerator + +pci:v0000106Cd00008804* + ID_MODEL_FROM_DATABASE=LAN Controller + +pci:v0000106Cd00008805* + ID_MODEL_FROM_DATABASE=100-BaseT LAN + +pci:v0000106D* + ID_VENDOR_FROM_DATABASE=Sequent Computer Systems + +pci:v0000106E* + ID_VENDOR_FROM_DATABASE=DFI, Inc + +pci:v0000106F* + ID_VENDOR_FROM_DATABASE=City Gate Development Ltd + +pci:v00001070* + ID_VENDOR_FROM_DATABASE=Daewoo Telecom Ltd + +pci:v00001071* + ID_VENDOR_FROM_DATABASE=Mitac + +pci:v00001071d00008160* + ID_MODEL_FROM_DATABASE=Mitac 8060B Mobile Platform + +pci:v00001072* + ID_VENDOR_FROM_DATABASE=GIT Co Ltd + +pci:v00001073* + ID_VENDOR_FROM_DATABASE=Yamaha Corporation + +pci:v00001073d00000001* + ID_MODEL_FROM_DATABASE=3D GUI Accelerator + +pci:v00001073d00000002* + ID_MODEL_FROM_DATABASE=YGV615 [RPA3 3D-Graphics Controller] + +pci:v00001073d00000003* + ID_MODEL_FROM_DATABASE=YMF-740 + +pci:v00001073d00000004* + ID_MODEL_FROM_DATABASE=YMF-724 + +pci:v00001073d00000004sv00001073sd00000004* + ID_MODEL_FROM_DATABASE=YMF-724 (YMF724-Based PCI Audio Adapter) + +pci:v00001073d00000005* + ID_MODEL_FROM_DATABASE=DS1 Audio + +pci:v00001073d00000005sv00001073sd00000005* + ID_MODEL_FROM_DATABASE=DS1 Audio (DS-XG PCI Audio CODEC) + +pci:v00001073d00000006* + ID_MODEL_FROM_DATABASE=DS1 Audio + +pci:v00001073d00000008* + ID_MODEL_FROM_DATABASE=DS1 Audio + +pci:v00001073d00000008sv00001073sd00000008* + ID_MODEL_FROM_DATABASE=DS1 Audio (DS-XG PCI Audio CODEC) + +pci:v00001073d0000000A* + ID_MODEL_FROM_DATABASE=DS1L Audio + +pci:v00001073d0000000Asv00001073sd00000004* + ID_MODEL_FROM_DATABASE=DS1L Audio (DS-XG PCI Audio CODEC) + +pci:v00001073d0000000Asv00001073sd0000000A* + ID_MODEL_FROM_DATABASE=DS1L Audio (DS-XG PCI Audio CODEC) + +pci:v00001073d0000000Asv00008086sd00004D55* + ID_MODEL_FROM_DATABASE=DS1L Audio (DS-XG PCI Audio CODEC [Intel MU440EX]) + +pci:v00001073d0000000C* + ID_MODEL_FROM_DATABASE=YMF-740C [DS-1L Audio Controller] + +pci:v00001073d0000000Csv0000107Asd0000000C* + ID_MODEL_FROM_DATABASE=YMF-740C [DS-1L Audio Controller] (DS-XG PCI Audio CODEC) + +pci:v00001073d0000000D* + ID_MODEL_FROM_DATABASE=YMF-724F [DS-1 Audio Controller] + +pci:v00001073d0000000Dsv00001073sd0000000D* + ID_MODEL_FROM_DATABASE=YMF-724F [DS-1 Audio Controller] (DS-XG PCI Audio CODEC) + +pci:v00001073d00000010* + ID_MODEL_FROM_DATABASE=YMF-744B [DS-1S Audio Controller] + +pci:v00001073d00000010sv00001073sd00000006* + ID_MODEL_FROM_DATABASE=YMF-744B [DS-1S Audio Controller] (DS-XG PCI Audio CODEC) + +pci:v00001073d00000010sv00001073sd00000010* + ID_MODEL_FROM_DATABASE=YMF-744B [DS-1S Audio Controller] (DS-XG PCI Audio CODEC) + +pci:v00001073d00000012* + ID_MODEL_FROM_DATABASE=YMF-754 [DS-1E Audio Controller] + +pci:v00001073d00000012sv00001073sd00000012* + ID_MODEL_FROM_DATABASE=YMF-754 [DS-1E Audio Controller] (DS-XG PCI Audio Codec) + +pci:v00001073d00000020* + ID_MODEL_FROM_DATABASE=DS-1 Audio + +pci:v00001073d00001000* + ID_MODEL_FROM_DATABASE=SW1000XG [XG Factory] + +pci:v00001073d00002000* + ID_MODEL_FROM_DATABASE=DS2416 Digital Mixing Card + +pci:v00001073d00002000sv00001073sd00002000* + ID_MODEL_FROM_DATABASE=DS2416 Digital Mixing Card + +pci:v00001074* + ID_VENDOR_FROM_DATABASE=NexGen Microsystems + +pci:v00001074d00004E78* + ID_MODEL_FROM_DATABASE=82c500/1 + +pci:v00001075* + ID_VENDOR_FROM_DATABASE=Advanced Integrations Research + +pci:v00001076* + ID_VENDOR_FROM_DATABASE=Chaintech Computer Co. Ltd + +pci:v00001077* + ID_VENDOR_FROM_DATABASE=QLogic Corp. + +pci:v00001077d00001016* + ID_MODEL_FROM_DATABASE=ISP10160 Single Channel Ultra3 SCSI Processor + +pci:v00001077d00001020* + ID_MODEL_FROM_DATABASE=ISP1020 Fast-wide SCSI + +pci:v00001077d00001022* + ID_MODEL_FROM_DATABASE=ISP1022 Fast-wide SCSI + +pci:v00001077d00001080* + ID_MODEL_FROM_DATABASE=ISP1080 SCSI Host Adapter + +pci:v00001077d00001216* + ID_MODEL_FROM_DATABASE=ISP12160 Dual Channel Ultra3 SCSI Processor + +pci:v00001077d00001216sv0000101Esd00008471* + ID_MODEL_FROM_DATABASE=ISP12160 Dual Channel Ultra3 SCSI Processor (QLA12160 on AMI MegaRAID) + +pci:v00001077d00001216sv0000101Esd00008493* + ID_MODEL_FROM_DATABASE=ISP12160 Dual Channel Ultra3 SCSI Processor (QLA12160 on AMI MegaRAID) + +pci:v00001077d00001240* + ID_MODEL_FROM_DATABASE=ISP1240 SCSI Host Adapter + +pci:v00001077d00001280* + ID_MODEL_FROM_DATABASE=ISP1280 SCSI Host Adapter + +pci:v00001077d00001634* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller + +pci:v00001077d00001634sv00001077sd0000E4F1* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FastLinQ QL45212H 40GbE Adapter) + +pci:v00001077d00001634sv00001077sd0000E4F2* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FastLinQ QL45211H 40GbE Adapter) + +pci:v00001077d00001634sv00001077sd0000E4F3* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FastLinQ QL45412H 40GbE Adapter) + +pci:v00001077d00001634sv00001077sd0000E4F4* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FastLinQ QL45411H 40GbE Adapter) + +pci:v00001077d00001644* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 100GbE Controller + +pci:v00001077d00001644sv00001077sd0000E4F8* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 100GbE Controller (FastLinQ QL45611H 100GbE Adapter) + +pci:v00001077d00001656* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller + +pci:v00001077d00001656sv00001077sd000002A7* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller (QL45212-DE 25GbE Adapter) + +pci:v00001077d00001656sv00001077sd0000E4F6* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller (FastLinQ QL45211H 25GbE Adapter) + +pci:v00001077d00001656sv00001077sd0000E4F7* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller (FastLinQ QL45212H 25GbE Adapter) + +pci:v00001077d0000165C* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FCoE) + +pci:v00001077d0000165Csv00001077sd0000E4F1* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FCoE) (FastLinQ QL45462H 40GbE FCoE Adapter) + +pci:v00001077d0000165Csv00001077sd0000E4F2* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (FCoE) (FastLinQ QL45461H 40GbE FCoE Adapter) + +pci:v00001077d0000165E* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (iSCSI) + +pci:v00001077d0000165Esv00001077sd0000E4F1* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (iSCSI) (FastLinQ QL45462H 40GbE iSCSI Adapter) + +pci:v00001077d0000165Esv00001077sd0000E4F2* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 40GbE Controller (iSCSI) (FastLinQ QL45461H 40GbE iSCSI Adapter) + +pci:v00001077d00001664* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) + +pci:v00001077d00001664sv00001077sd0000E4F1* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45462H 40GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F2* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45461H 40GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F3* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45412H 40GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F4* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45411H 40GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F6* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45211H 25GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F7* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45212H 25GbE Adapter (SR-IOV VF)) + +pci:v00001077d00001664sv00001077sd0000E4F8* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45611H 100GbE Adapter (SR-IOV VF)) + +pci:v00001077d00002020* + ID_MODEL_FROM_DATABASE=ISP2020A Fast!SCSI Basic Adapter + +pci:v00001077d00002031* + ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter + +pci:v00001077d00002031sv0000103Csd000017E7* + ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter (HP SN1000Q 16Gb Single Port Fibre Channel Adapter) + +pci:v00001077d00002031sv0000103Csd000017E8* + ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter (HP SN1000Q 16Gb Dual Port Fibre Channel Adapter) + +pci:v00001077d00002031sv0000103Csd00001939* + ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter (HP QMH2672 16Gb Dual Port Fibre Channel Adapter) + +pci:v00001077d00002031sv0000103Csd00008002* + ID_MODEL_FROM_DATABASE=ISP8324-based 16Gb Fibre Channel to PCI Express Adapter (3830C 16G Fibre Channel Host Bus Adapter) + +pci:v00001077d00002071* + ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter + +pci:v00001077d00002071sv00001077sd00000283* + ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2764 Quad Port 32Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002071sv00001077sd0000029E* + ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2694 Quad Port 16Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002071sv00001077sd000002A2* + ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2694L Quad Port 16Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002071sv00001077sd000002AD* + ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2694U Quad Port 16/32Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002100* + ID_MODEL_FROM_DATABASE=QLA2100 64-bit Fibre Channel Adapter + +pci:v00001077d00002100sv00001077sd00000001* + ID_MODEL_FROM_DATABASE=QLA2100 64-bit Fibre Channel Adapter + +pci:v00001077d00002200* + ID_MODEL_FROM_DATABASE=QLA2200 64-bit Fibre Channel Adapter + +pci:v00001077d00002200sv00001077sd00000002* + ID_MODEL_FROM_DATABASE=QLA2200 64-bit Fibre Channel Adapter (QLA2200) + +pci:v00001077d00002261* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter + +pci:v00001077d00002261sv00001077sd00000299* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2740 Single Port 32Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002261sv00001077sd0000029A* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2742 Dual Port 32Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002261sv00001077sd0000029B* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2690 Single Port 16Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002261sv00001077sd0000029C* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2692 Dual Port 16Gb Fibre Channel to PCIe Adapter) + +pci:v00001077d00002261sv00001077sd000002A7* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2690 Single Port 16Gb FC to PCIe Gen3 x8 Adapter) + +pci:v00001077d00002261sv00001077sd000002A8* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2692 Dual Port 16Gb FC to PCIe Gen3 x8 Adapter) + +pci:v00001077d00002261sv00001077sd000002AB* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2740 Single Port 32Gb FC to PCIe Gen3 x8 Adapter) + +pci:v00001077d00002261sv00001077sd000002AC* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2742 Dual Port 32Gb FC to PCIe Gen3 x8 Adapter) + +pci:v00001077d00002261sv00001590sd000000F9* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (HPE StoreFabric SN1100Q 16Gb Single Port Fibre Channel Host Bus Adapter) + +pci:v00001077d00002261sv00001590sd000000FA* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (HPE StoreFabric SN1100Q 16Gb Dual Port Fibre Channel Host Bus Adapter) + +pci:v00001077d00002261sv00001590sd00000203* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (HPE StoreFabric SN1600Q 32Gb Single Port Fibre Channel Host Bus Adapter) + +pci:v00001077d00002261sv00001590sd00000204* + ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (HPE StoreFabric SN1600Q 32Gb Dual Port Fibre Channel Host Bus Adapter) + +pci:v00001077d00002300* + ID_MODEL_FROM_DATABASE=QLA2300 64-bit Fibre Channel Adapter + +pci:v00001077d00002312* + ID_MODEL_FROM_DATABASE=ISP2312-based 2Gb Fibre Channel to PCI-X HBA + +pci:v00001077d00002312sv0000103Csd00000131* + ID_MODEL_FROM_DATABASE=ISP2312-based 2Gb Fibre Channel to PCI-X HBA (2Gb Fibre Channel - Single port [A7538A]) + +pci:v00001077d00002312sv0000103Csd000012BA* + ID_MODEL_FROM_DATABASE=ISP2312-based 2Gb Fibre Channel to PCI-X HBA (2Gb Fibre Channel - Dual port [A6826A]) + +pci:v00001077d00002322* + ID_MODEL_FROM_DATABASE=ISP2322-based 2Gb Fibre Channel to PCI-X HBA + +pci:v00001077d00002422* + ID_MODEL_FROM_DATABASE=ISP2422-based 4Gb Fibre Channel to PCI-X HBA + +pci:v00001077d00002422sv0000103Csd000012D7* + ID_MODEL_FROM_DATABASE=ISP2422-based 4Gb Fibre Channel to PCI-X HBA (4Gb Fibre Channel [AB379A]) + +pci:v00001077d00002422sv0000103Csd000012DD* + ID_MODEL_FROM_DATABASE=ISP2422-based 4Gb Fibre Channel to PCI-X HBA (4Gb Fibre Channel [AB429A]) + +pci:v00001077d00002432* + ID_MODEL_FROM_DATABASE=ISP2432-based 4Gb Fibre Channel to PCI Express HBA + +pci:v00001077d00002432sv0000103Csd00007040* + ID_MODEL_FROM_DATABASE=ISP2432-based 4Gb Fibre Channel to PCI Express HBA (FC1142SR 4Gb 1-port PCIe Fibre Channel Host Bus Adapter [HPAE311A]) + +pci:v00001077d00002532* + ID_MODEL_FROM_DATABASE=ISP2532-based 8Gb Fibre Channel to PCI Express HBA + +pci:v00001077d00002532sv0000103Csd00003262* + ID_MODEL_FROM_DATABASE=ISP2532-based 8Gb Fibre Channel to PCI Express HBA (StorageWorks 81Q) + +pci:v00001077d00002532sv0000103Csd00003263* + ID_MODEL_FROM_DATABASE=ISP2532-based 8Gb Fibre Channel to PCI Express HBA (StorageWorks 82Q) + +pci:v00001077d00002532sv00001077sd00000167* + ID_MODEL_FROM_DATABASE=ISP2532-based 8Gb Fibre Channel to PCI Express HBA (QME2572 Dual Port FC8 HBA Mezzanine) + +pci:v00001077d00002532sv00001590sd000000FC* + ID_MODEL_FROM_DATABASE=ISP2532-based 8Gb Fibre Channel to PCI Express HBA (HPE StoreFabric 84Q 8Gb Quad Port Fibre Channel Host Bus Adapter) + +pci:v00001077d00003022* + ID_MODEL_FROM_DATABASE=ISP4022-based Ethernet NIC + +pci:v00001077d00003032* + ID_MODEL_FROM_DATABASE=ISP4032-based Ethernet IPv6 NIC + +pci:v00001077d00004010* + ID_MODEL_FROM_DATABASE=ISP4010-based iSCSI TOE HBA + +pci:v00001077d00004022* + ID_MODEL_FROM_DATABASE=ISP4022-based iSCSI TOE HBA + +pci:v00001077d00004032* + ID_MODEL_FROM_DATABASE=ISP4032-based iSCSI TOE IPv6 HBA + +pci:v00001077d00005432* + ID_MODEL_FROM_DATABASE=SP232-based 4Gb Fibre Channel to PCI Express HBA + +pci:v00001077d00006312* + ID_MODEL_FROM_DATABASE=SP202-based 2Gb Fibre Channel to PCI-X HBA + +pci:v00001077d00006322* + ID_MODEL_FROM_DATABASE=SP212-based 2Gb Fibre Channel to PCI-X HBA + +pci:v00001077d00007220* + ID_MODEL_FROM_DATABASE=IBA7220 InfiniBand HCA + +pci:v00001077d00007322* + ID_MODEL_FROM_DATABASE=IBA7322 QDR InfiniBand HCA + +pci:v00001077d00008000* + ID_MODEL_FROM_DATABASE=10GbE Converged Network Adapter (TCP/IP Networking) + +pci:v00001077d00008001* + ID_MODEL_FROM_DATABASE=10GbE Converged Network Adapter (FCoE) + +pci:v00001077d00008020* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller + +pci:v00001077d00008020sv00001028sd00001F64* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (QMD8262-k 10G DP bNDC KR) + +pci:v00001077d00008020sv0000103Csd00003346* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (CN1000Q Dual Port Converged Network Adapter) + +pci:v00001077d00008020sv0000103Csd00003733* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (NC523SFP 10Gb 2-port Server Adapter) + +pci:v00001077d00008020sv00001077sd00000203* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (8200 Series Single Port 10GbE Converged Network Adapter (TCP/IP Networking)) + +pci:v00001077d00008020sv00001077sd00000207* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (8200 Series Dual Port 10GbE Converged Network Adapter (TCP/IP Networking)) + +pci:v00001077d00008020sv00001077sd0000020B* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (3200 Series Dual Port 10Gb Intelligent Ethernet Adapter) + +pci:v00001077d00008020sv00001077sd0000020C* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (3200 Series Quad Port 1Gb Intelligent Ethernet Adapter) + +pci:v00001077d00008020sv00001077sd0000020F* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (3200 Series Single Port 10Gb Intelligent Ethernet Adapter) + +pci:v00001077d00008020sv00001077sd00000210* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (QME8242-k 10GbE Dual Port Mezzanine Card) + +pci:v00001077d00008020sv00001077sd00000233* + ID_MODEL_FROM_DATABASE=cLOM8214 1/10GbE Controller (QME8262-k 10GbE Dual Port Mezzanine Card) + +pci:v00001077d00008021* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (FCoE) + +pci:v00001077d00008021sv0000103Csd00003348* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (FCoE) (CN1000Q Dual Port Converged Network Adapter) + +pci:v00001077d00008021sv00001077sd00000211* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (FCoE) (QME8242-k 10GbE Dual Port Mezzanine Card, FCoE) + +pci:v00001077d00008022* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (iSCSI) + +pci:v00001077d00008022sv0000103Csd00003347* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (iSCSI) (CN1000Q Dual Port Converged Network Adapter) + +pci:v00001077d00008022sv00001077sd00000212* + ID_MODEL_FROM_DATABASE=8200 Series 10GbE Converged Network Adapter (iSCSI) (QME8242-k 10GbE Dual Port Mezzanine Card, iSCSI) + +pci:v00001077d00008030* + ID_MODEL_FROM_DATABASE=ISP8324 1/10GbE Converged Network Controller + +pci:v00001077d00008030sv00001077sd00000243* + ID_MODEL_FROM_DATABASE=ISP8324 1/10GbE Converged Network Controller (8300 Series Single Port 10GbE Converged Network Adapter (TCP/IP Networking)) + +pci:v00001077d00008030sv00001077sd00000246* + ID_MODEL_FROM_DATABASE=ISP8324 1/10GbE Converged Network Controller (8300 Series Dual Port 10GbE Converged Network Adapter (TCP/IP Networking)) + +pci:v00001077d00008031* + ID_MODEL_FROM_DATABASE=8300 Series 10GbE Converged Network Adapter (FCoE) + +pci:v00001077d00008032* + ID_MODEL_FROM_DATABASE=8300 Series 10GbE Converged Network Adapter (iSCSI) + +pci:v00001077d00008430* + ID_MODEL_FROM_DATABASE=ISP8324 1/10GbE Converged Network Controller (NIC VF) + +pci:v00001077d00008431* + ID_MODEL_FROM_DATABASE=8300 Series 10GbE Converged Network Adapter (FCoE VF) + +pci:v00001077d00008432* + ID_MODEL_FROM_DATABASE=ISP2432M-based 10GbE Converged Network Adapter (CNA) + +pci:v00001078* + ID_VENDOR_FROM_DATABASE=Cyrix Corporation + +pci:v00001078d00000000* + ID_MODEL_FROM_DATABASE=5510 [Grappa] + +pci:v00001078d00000001* + ID_MODEL_FROM_DATABASE=PCI Master + +pci:v00001078d00000002* + ID_MODEL_FROM_DATABASE=5520 [Cognac] + +pci:v00001078d00000100* + ID_MODEL_FROM_DATABASE=5530 Legacy [Kahlua] + +pci:v00001078d00000101* + ID_MODEL_FROM_DATABASE=5530 SMI [Kahlua] + +pci:v00001078d00000102* + ID_MODEL_FROM_DATABASE=5530 IDE [Kahlua] + +pci:v00001078d00000103* + ID_MODEL_FROM_DATABASE=5530 Audio [Kahlua] + +pci:v00001078d00000104* + ID_MODEL_FROM_DATABASE=5530 Video [Kahlua] + +pci:v00001078d00000400* + ID_MODEL_FROM_DATABASE=ZFMicro PCI Bridge + +pci:v00001078d00000401* + ID_MODEL_FROM_DATABASE=ZFMicro Chipset SMI + +pci:v00001078d00000402* + ID_MODEL_FROM_DATABASE=ZFMicro Chipset IDE + +pci:v00001078d00000403* + ID_MODEL_FROM_DATABASE=ZFMicro Expansion Bus + +pci:v00001079* + ID_VENDOR_FROM_DATABASE=I-Bus + +pci:v0000107A* + ID_VENDOR_FROM_DATABASE=NetWorth + +pci:v0000107B* + ID_VENDOR_FROM_DATABASE=Gateway, Inc. + +pci:v0000107C* + ID_VENDOR_FROM_DATABASE=LG Electronics [Lucky Goldstar Co. Ltd] + +pci:v0000107D* + ID_VENDOR_FROM_DATABASE=LeadTek Research Inc. + +pci:v0000107Dd00000000* + ID_MODEL_FROM_DATABASE=P86C850 + +pci:v0000107E* + ID_VENDOR_FROM_DATABASE=Interphase Corporation + +pci:v0000107Ed00000001* + ID_MODEL_FROM_DATABASE=5515 ATM Adapter [Flipper] + +pci:v0000107Ed00000002* + ID_MODEL_FROM_DATABASE=100 VG AnyLan Controller + +pci:v0000107Ed00000004* + ID_MODEL_FROM_DATABASE=5526 Fibre Channel Host Adapter + +pci:v0000107Ed00000005* + ID_MODEL_FROM_DATABASE=x526 Fibre Channel Host Adapter + +pci:v0000107Ed00000008* + ID_MODEL_FROM_DATABASE=5525/5575 ATM Adapter (155 Mbit) [Atlantic] + +pci:v0000107Ed00009003* + ID_MODEL_FROM_DATABASE=5535-4P-BRI-ST + +pci:v0000107Ed00009007* + ID_MODEL_FROM_DATABASE=5535-4P-BRI-U + +pci:v0000107Ed00009008* + ID_MODEL_FROM_DATABASE=5535-1P-SR + +pci:v0000107Ed0000900C* + ID_MODEL_FROM_DATABASE=5535-1P-SR-ST + +pci:v0000107Ed0000900E* + ID_MODEL_FROM_DATABASE=5535-1P-SR-U + +pci:v0000107Ed00009011* + ID_MODEL_FROM_DATABASE=5535-1P-PRI + +pci:v0000107Ed00009013* + ID_MODEL_FROM_DATABASE=5535-2P-PRI + +pci:v0000107Ed00009023* + ID_MODEL_FROM_DATABASE=5536-4P-BRI-ST + +pci:v0000107Ed00009027* + ID_MODEL_FROM_DATABASE=5536-4P-BRI-U + +pci:v0000107Ed00009031* + ID_MODEL_FROM_DATABASE=5536-1P-PRI + +pci:v0000107Ed00009033* + ID_MODEL_FROM_DATABASE=5536-2P-PRI + +pci:v0000107F* + ID_VENDOR_FROM_DATABASE=Data Technology Corporation + +pci:v0000107Fd00000802* + ID_MODEL_FROM_DATABASE=SL82C105 + +pci:v00001080* + ID_VENDOR_FROM_DATABASE=Contaq Microsystems + +pci:v00001080d00000600* + ID_MODEL_FROM_DATABASE=82C599 + +pci:v00001080d0000C691* + ID_MODEL_FROM_DATABASE=Cypress CY82C691 + +pci:v00001080d0000C693* + ID_MODEL_FROM_DATABASE=82c693 + +pci:v00001081* + ID_VENDOR_FROM_DATABASE=Supermac Technology + +pci:v00001081d00000D47* + ID_MODEL_FROM_DATABASE=Radius PCI to NuBUS Bridge + +pci:v00001082* + ID_VENDOR_FROM_DATABASE=EFA Corporation of America + +pci:v00001083* + ID_VENDOR_FROM_DATABASE=Forex Computer Corporation + +pci:v00001083d00000001* + ID_MODEL_FROM_DATABASE=FR710 + +pci:v00001084* + ID_VENDOR_FROM_DATABASE=Parador + +pci:v00001086* + ID_VENDOR_FROM_DATABASE=J. Bond Computer Systems + +pci:v00001087* + ID_VENDOR_FROM_DATABASE=Cache Computer + +pci:v00001088* + ID_VENDOR_FROM_DATABASE=Microcomputer Systems (M) Son + +pci:v00001089* + ID_VENDOR_FROM_DATABASE=Data General Corporation + +pci:v0000108A* + ID_VENDOR_FROM_DATABASE=SBS Technologies + +pci:v0000108Ad00000001* + ID_MODEL_FROM_DATABASE=VME Bridge Model 617 + +pci:v0000108Ad00000010* + ID_MODEL_FROM_DATABASE=VME Bridge Model 618 + +pci:v0000108Ad00000040* + ID_MODEL_FROM_DATABASE=dataBLIZZARD + +pci:v0000108Ad00003000* + ID_MODEL_FROM_DATABASE=VME Bridge Model 2706 + +pci:v0000108C* + ID_VENDOR_FROM_DATABASE=Oakleigh Systems Inc. + +pci:v0000108D* + ID_VENDOR_FROM_DATABASE=Olicom + +pci:v0000108Dd00000001* + ID_MODEL_FROM_DATABASE=Token-Ring 16/4 PCI Adapter (3136/3137) + +pci:v0000108Dd00000002* + ID_MODEL_FROM_DATABASE=16/4 Token Ring + +pci:v0000108Dd00000004* + ID_MODEL_FROM_DATABASE=RapidFire OC-3139/3140 Token-Ring 16/4 PCI Adapter + +pci:v0000108Dd00000004sv0000108Dsd00000004* + ID_MODEL_FROM_DATABASE=RapidFire OC-3139/3140 Token-Ring 16/4 PCI Adapter (OC-3139/3140 RapidFire Token-Ring 16/4 Adapter) + +pci:v0000108Dd00000005* + ID_MODEL_FROM_DATABASE=GoCard 3250 Token-Ring 16/4 CardBus PC Card + +pci:v0000108Dd00000006* + ID_MODEL_FROM_DATABASE=OC-3530 RapidFire Token-Ring 100 + +pci:v0000108Dd00000007* + ID_MODEL_FROM_DATABASE=RapidFire 3141 Token-Ring 16/4 PCI Fiber Adapter + +pci:v0000108Dd00000007sv0000108Dsd00000007* + ID_MODEL_FROM_DATABASE=RapidFire 3141 Token-Ring 16/4 PCI Fiber Adapter (OC-3141 RapidFire Token-Ring 16/4 Adapter) + +pci:v0000108Dd00000008* + ID_MODEL_FROM_DATABASE=RapidFire 3540 HSTR 100/16/4 PCI Adapter + +pci:v0000108Dd00000008sv0000108Dsd00000008* + ID_MODEL_FROM_DATABASE=RapidFire 3540 HSTR 100/16/4 PCI Adapter (OC-3540 RapidFire HSTR 100/16/4 Adapter) + +pci:v0000108Dd00000011* + ID_MODEL_FROM_DATABASE=OC-2315 + +pci:v0000108Dd00000012* + ID_MODEL_FROM_DATABASE=OC-2325 + +pci:v0000108Dd00000013* + ID_MODEL_FROM_DATABASE=OC-2183/2185 + +pci:v0000108Dd00000014* + ID_MODEL_FROM_DATABASE=OC-2326 + +pci:v0000108Dd00000019* + ID_MODEL_FROM_DATABASE=OC-2327/2250 10/100 Ethernet Adapter + +pci:v0000108Dd00000019sv0000108Dsd00000016* + ID_MODEL_FROM_DATABASE=OC-2327/2250 10/100 Ethernet Adapter (OC-2327 Rapidfire 10/100 Ethernet Adapter) + +pci:v0000108Dd00000019sv0000108Dsd00000017* + ID_MODEL_FROM_DATABASE=OC-2327/2250 10/100 Ethernet Adapter (OC-2250 GoCard 10/100 Ethernet Adapter) + +pci:v0000108Dd00000021* + ID_MODEL_FROM_DATABASE=OC-6151/6152 [RapidFire ATM 155] + +pci:v0000108Dd00000022* + ID_MODEL_FROM_DATABASE=ATM Adapter + +pci:v0000108E* + ID_VENDOR_FROM_DATABASE=Oracle/SUN + +pci:v0000108Ed00000001* + ID_MODEL_FROM_DATABASE=EBUS + +pci:v0000108Ed00001000* + ID_MODEL_FROM_DATABASE=EBUS + +pci:v0000108Ed00001001* + ID_MODEL_FROM_DATABASE=Happy Meal 10/100 Ethernet [hme] + +pci:v0000108Ed00001100* + ID_MODEL_FROM_DATABASE=RIO EBUS + +pci:v0000108Ed00001100sv0000108Esd00001100* + ID_MODEL_FROM_DATABASE=RIO EBUS (on Blade 100 motherboard) + +pci:v0000108Ed00001101* + ID_MODEL_FROM_DATABASE=RIO 10/100 Ethernet [eri] + +pci:v0000108Ed00001101sv0000108Esd00001101* + ID_MODEL_FROM_DATABASE=RIO 10/100 Ethernet [eri] (RIO GEM on Blade 100 motherboard) + +pci:v0000108Ed00001102* + ID_MODEL_FROM_DATABASE=RIO 1394 + +pci:v0000108Ed00001102sv0000108Esd00001102* + ID_MODEL_FROM_DATABASE=RIO 1394 (on Blade 100 motherboard) + +pci:v0000108Ed00001103* + ID_MODEL_FROM_DATABASE=RIO USB + +pci:v0000108Ed00001103sv0000108Esd00001103* + ID_MODEL_FROM_DATABASE=RIO USB (on Blade 100 motherboard) + +pci:v0000108Ed00001647* + ID_MODEL_FROM_DATABASE=Broadcom 570x 10/100/1000 Ethernet [bge] + +pci:v0000108Ed00001648* + ID_MODEL_FROM_DATABASE=Broadcom 570x 10/100/1000 Ethernet [bge] + +pci:v0000108Ed000016A7* + ID_MODEL_FROM_DATABASE=Broadcom 570x 10/100/1000 Ethernet [bge] + +pci:v0000108Ed000016A8* + ID_MODEL_FROM_DATABASE=Broadcom 570x 10/100/1000 Ethernet [bge] + +pci:v0000108Ed00002BAD* + ID_MODEL_FROM_DATABASE=GEM 10/100/1000 Ethernet [ge] + +pci:v0000108Ed00005000* + ID_MODEL_FROM_DATABASE=Simba Advanced PCI Bridge + +pci:v0000108Ed00005000sv0000108Esd00005000* + ID_MODEL_FROM_DATABASE=Simba Advanced PCI Bridge (Netra AX1105-500) + +pci:v0000108Ed00005043* + ID_MODEL_FROM_DATABASE=SunPCI Co-processor + +pci:v0000108Ed00005CA0* + ID_MODEL_FROM_DATABASE=Crypto Accelerator 6000 [mca] + +pci:v0000108Ed00006300* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006301* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006302* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006303* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006310* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006311* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006312* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006313* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006320* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006323* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006330* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006331* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006332* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006333* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006340* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006343* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006350* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006353* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed00006722* + ID_MODEL_FROM_DATABASE=Intel 21554 PCI-PCI bus bridge [db21554] + +pci:v0000108Ed0000676E* + ID_MODEL_FROM_DATABASE=SunPCiIII + +pci:v0000108Ed00007063* + ID_MODEL_FROM_DATABASE=SunPCiII / SunPCiIIpro + +pci:v0000108Ed00008000* + ID_MODEL_FROM_DATABASE=Psycho PCI Bus Module + +pci:v0000108Ed00008001* + ID_MODEL_FROM_DATABASE=Schizo PCI Bus Module + +pci:v0000108Ed00008002* + ID_MODEL_FROM_DATABASE=Schizo+ PCI Bus Module + +pci:v0000108Ed000080F0* + ID_MODEL_FROM_DATABASE=PCIe switch [px] + +pci:v0000108Ed000080F8* + ID_MODEL_FROM_DATABASE=PCIe switch [px] + +pci:v0000108Ed00009010* + ID_MODEL_FROM_DATABASE=PCIe/PCI bridge switch [pxb_plx] + +pci:v0000108Ed00009020* + ID_MODEL_FROM_DATABASE=PCIe/PCI bridge switch [pxb_plx] + +pci:v0000108Ed00009102* + ID_MODEL_FROM_DATABASE=Davicom Fast Ethernet driver for Davicom DM9102A [dmfe] + +pci:v0000108Ed0000A000* + ID_MODEL_FROM_DATABASE=Psycho UPA-PCI Bus Module [pcipsy] + +pci:v0000108Ed0000A001* + ID_MODEL_FROM_DATABASE=Psycho UPA-PCI Bus Module [pcipsy] + +pci:v0000108Ed0000A001sv0000108Esd0000A001* + ID_MODEL_FROM_DATABASE=Psycho UPA-PCI Bus Module [pcipsy] (Ultra IIe on Blade 100 motherboard) + +pci:v0000108Ed0000A801* + ID_MODEL_FROM_DATABASE=Schizo Fireplane-PCI bus bridge module [pcisch] + +pci:v0000108Ed0000AAAA* + ID_MODEL_FROM_DATABASE=Multithreaded Shared 10GbE Ethernet Network Controller + +pci:v0000108Ed0000ABBA* + ID_MODEL_FROM_DATABASE=Cassini 10/100/1000 + +pci:v0000108Ed0000ABCD* + ID_MODEL_FROM_DATABASE=Multithreaded 10-Gigabit Ethernet Network Controller + +pci:v0000108Ed0000C416* + ID_MODEL_FROM_DATABASE=Sun Fire System/System Controller Interface chip [sbbc] + +pci:v0000108F* + ID_VENDOR_FROM_DATABASE=Systemsoft + +pci:v00001090* + ID_VENDOR_FROM_DATABASE=Compro Computer Services, Inc. + +pci:v00001090d00004610* + ID_MODEL_FROM_DATABASE=PCI RTOM + +pci:v00001090d00004620* + ID_MODEL_FROM_DATABASE=GPIO HSD + +pci:v00001091* + ID_VENDOR_FROM_DATABASE=Intergraph Corporation + +pci:v00001091d00000020* + ID_MODEL_FROM_DATABASE=3D graphics processor + +pci:v00001091d00000021* + ID_MODEL_FROM_DATABASE=3D graphics processor w/Texturing + +pci:v00001091d00000040* + ID_MODEL_FROM_DATABASE=3D graphics frame buffer + +pci:v00001091d00000041* + ID_MODEL_FROM_DATABASE=3D graphics frame buffer + +pci:v00001091d00000060* + ID_MODEL_FROM_DATABASE=Proprietary bus bridge + +pci:v00001091d000000E4* + ID_MODEL_FROM_DATABASE=Powerstorm 4D50T + +pci:v00001091d00000720* + ID_MODEL_FROM_DATABASE=Motion JPEG codec + +pci:v00001091d00000780* + ID_MODEL_FROM_DATABASE=Intense3D Wildcat 3410 (MSMT496) + +pci:v00001091d000007A0* + ID_MODEL_FROM_DATABASE=Sun Expert3D-Lite Graphics Accelerator + +pci:v00001091d00001091* + ID_MODEL_FROM_DATABASE=Sun Expert3D Graphics Accelerator + +pci:v00001092* + ID_VENDOR_FROM_DATABASE=Diamond Multimedia Systems + +pci:v00001092d00000028* + ID_MODEL_FROM_DATABASE=Viper V770 + +pci:v00001092d00000028sv00001092sd00004A00* + ID_MODEL_FROM_DATABASE=Viper V770 (32MB) + +pci:v00001092d000000A0* + ID_MODEL_FROM_DATABASE=Speedstar Pro SE + +pci:v00001092d000000A8* + ID_MODEL_FROM_DATABASE=Speedstar 64 + +pci:v00001092d00000550* + ID_MODEL_FROM_DATABASE=Viper V550 + +pci:v00001092d000008D4* + ID_MODEL_FROM_DATABASE=Supra 2260 Modem + +pci:v00001092d0000094C* + ID_MODEL_FROM_DATABASE=SupraExpress 56i Pro + +pci:v00001092d00001001* + ID_MODEL_FROM_DATABASE=Video Crunch It 1001 capture card + +pci:v00001092d00001092* + ID_MODEL_FROM_DATABASE=Viper V330 + +pci:v00001092d00006120* + ID_MODEL_FROM_DATABASE=Maximum DVD + +pci:v00001092d00008810* + ID_MODEL_FROM_DATABASE=Stealth SE + +pci:v00001092d00008811* + ID_MODEL_FROM_DATABASE=Stealth 64/SE + +pci:v00001092d00008880* + ID_MODEL_FROM_DATABASE=Stealth + +pci:v00001092d00008881* + ID_MODEL_FROM_DATABASE=Stealth + +pci:v00001092d000088B0* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088B1* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088C0* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088C1* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088D0* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088D1* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088F0* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d000088F1* + ID_MODEL_FROM_DATABASE=Stealth 64 + +pci:v00001092d00009999* + ID_MODEL_FROM_DATABASE=DMD-I0928-1 "Monster sound" sound chip + +pci:v00001093* + ID_VENDOR_FROM_DATABASE=National Instruments + +pci:v00001093d00000160* + ID_MODEL_FROM_DATABASE=PCI-DIO-96 + +pci:v00001093d00000162* + ID_MODEL_FROM_DATABASE=PCI-MIO-16XE-50 + +pci:v00001093d00000FE1* + ID_MODEL_FROM_DATABASE=PXI-8320 + +pci:v00001093d00001150* + ID_MODEL_FROM_DATABASE=PCI-6533 (PCI-DIO-32HS) + +pci:v00001093d00001170* + ID_MODEL_FROM_DATABASE=PCI-MIO-16XE-10 + +pci:v00001093d00001180* + ID_MODEL_FROM_DATABASE=PCI-MIO-16E-1 + +pci:v00001093d00001190* + ID_MODEL_FROM_DATABASE=PCI-MIO-16E-4 + +pci:v00001093d000011B0* + ID_MODEL_FROM_DATABASE=PXI-6070E + +pci:v00001093d000011C0* + ID_MODEL_FROM_DATABASE=PXI-6040E + +pci:v00001093d000011D0* + ID_MODEL_FROM_DATABASE=PXI-6030E + +pci:v00001093d00001270* + ID_MODEL_FROM_DATABASE=PCI-6032E + +pci:v00001093d00001290* + ID_MODEL_FROM_DATABASE=PCI-6704 + +pci:v00001093d000012B0* + ID_MODEL_FROM_DATABASE=PCI-6534 + +pci:v00001093d00001310* + ID_MODEL_FROM_DATABASE=PCI-6602 + +pci:v00001093d00001320* + ID_MODEL_FROM_DATABASE=PXI-6533 + +pci:v00001093d00001330* + ID_MODEL_FROM_DATABASE=PCI-6031E + +pci:v00001093d00001340* + ID_MODEL_FROM_DATABASE=PCI-6033E + +pci:v00001093d00001350* + ID_MODEL_FROM_DATABASE=PCI-6071E + +pci:v00001093d00001360* + ID_MODEL_FROM_DATABASE=PXI-6602 + +pci:v00001093d000013C0* + ID_MODEL_FROM_DATABASE=PXI-6508 + +pci:v00001093d00001490* + ID_MODEL_FROM_DATABASE=PXI-6534 + +pci:v00001093d000014E0* + ID_MODEL_FROM_DATABASE=PCI-6110 + +pci:v00001093d000014F0* + ID_MODEL_FROM_DATABASE=PCI-6111 + +pci:v00001093d00001580* + ID_MODEL_FROM_DATABASE=PXI-6031E + +pci:v00001093d000015B0* + ID_MODEL_FROM_DATABASE=PXI-6071E + +pci:v00001093d00001710* + ID_MODEL_FROM_DATABASE=PXI-6509 + +pci:v00001093d000017C0* + ID_MODEL_FROM_DATABASE=PXI-5690 + +pci:v00001093d000017D0* + ID_MODEL_FROM_DATABASE=PCI-6503 + +pci:v00001093d00001870* + ID_MODEL_FROM_DATABASE=PCI-6713 + +pci:v00001093d00001880* + ID_MODEL_FROM_DATABASE=PCI-6711 + +pci:v00001093d000018B0* + ID_MODEL_FROM_DATABASE=PCI-6052E + +pci:v00001093d000018C0* + ID_MODEL_FROM_DATABASE=PXI-6052E + +pci:v00001093d00001920* + ID_MODEL_FROM_DATABASE=PXI-6704 + +pci:v00001093d00001930* + ID_MODEL_FROM_DATABASE=PCI-6040E + +pci:v00001093d000019C0* + ID_MODEL_FROM_DATABASE=PCI-4472 + +pci:v00001093d00001AA0* + ID_MODEL_FROM_DATABASE=PXI-4110 + +pci:v00001093d00001AD0* + ID_MODEL_FROM_DATABASE=PCI-6133 + +pci:v00001093d00001AE0* + ID_MODEL_FROM_DATABASE=PXI-6133 + +pci:v00001093d00001E30* + ID_MODEL_FROM_DATABASE=PCI-6624 + +pci:v00001093d00001E40* + ID_MODEL_FROM_DATABASE=PXI-6624 + +pci:v00001093d00001E50* + ID_MODEL_FROM_DATABASE=PXI-5404 + +pci:v00001093d00002410* + ID_MODEL_FROM_DATABASE=PCI-6733 + +pci:v00001093d00002420* + ID_MODEL_FROM_DATABASE=PXI-6733 + +pci:v00001093d00002430* + ID_MODEL_FROM_DATABASE=PCI-6731 + +pci:v00001093d00002470* + ID_MODEL_FROM_DATABASE=PCI-4474 + +pci:v00001093d000024A0* + ID_MODEL_FROM_DATABASE=PCI-4065 + +pci:v00001093d000024B0* + ID_MODEL_FROM_DATABASE=PXI-4200 + +pci:v00001093d000024F0* + ID_MODEL_FROM_DATABASE=PXI-4472 + +pci:v00001093d00002510* + ID_MODEL_FROM_DATABASE=PCI-4472 + +pci:v00001093d00002520* + ID_MODEL_FROM_DATABASE=PCI-4474 + +pci:v00001093d000027A0* + ID_MODEL_FROM_DATABASE=PCI-6123 + +pci:v00001093d000027B0* + ID_MODEL_FROM_DATABASE=PXI-6123 + +pci:v00001093d00002880* + ID_MODEL_FROM_DATABASE=DAQCard-6601 + +pci:v00001093d00002890* + ID_MODEL_FROM_DATABASE=PCI-6036E + +pci:v00001093d000028A0* + ID_MODEL_FROM_DATABASE=PXI-4461 + +pci:v00001093d000028B0* + ID_MODEL_FROM_DATABASE=PCI-6013 + +pci:v00001093d000028C0* + ID_MODEL_FROM_DATABASE=PCI-6014 + +pci:v00001093d000028D0* + ID_MODEL_FROM_DATABASE=PCI-5122 + +pci:v00001093d000028E0* + ID_MODEL_FROM_DATABASE=PXI-5122 + +pci:v00001093d000029F0* + ID_MODEL_FROM_DATABASE=PXI-7334 + +pci:v00001093d00002A00* + ID_MODEL_FROM_DATABASE=PXI-7344 + +pci:v00001093d00002A60* + ID_MODEL_FROM_DATABASE=PCI-6023E + +pci:v00001093d00002A70* + ID_MODEL_FROM_DATABASE=PCI-6024E + +pci:v00001093d00002A80* + ID_MODEL_FROM_DATABASE=PCI-6025E + +pci:v00001093d00002AB0* + ID_MODEL_FROM_DATABASE=PXI-6025E + +pci:v00001093d00002B10* + ID_MODEL_FROM_DATABASE=PXI-6527 + +pci:v00001093d00002B20* + ID_MODEL_FROM_DATABASE=PCI-6527 + +pci:v00001093d00002B80* + ID_MODEL_FROM_DATABASE=PXI-6713 + +pci:v00001093d00002B90* + ID_MODEL_FROM_DATABASE=PXI-6711 + +pci:v00001093d00002C60* + ID_MODEL_FROM_DATABASE=PCI-6601 + +pci:v00001093d00002C70* + ID_MODEL_FROM_DATABASE=PXI-6601 + +pci:v00001093d00002C80* + ID_MODEL_FROM_DATABASE=PCI-6035E + +pci:v00001093d00002C90* + ID_MODEL_FROM_DATABASE=PCI-6703 + +pci:v00001093d00002CA0* + ID_MODEL_FROM_DATABASE=PCI-6034E + +pci:v00001093d00002CB0* + ID_MODEL_FROM_DATABASE=PCI-7344 + +pci:v00001093d00002CC0* + ID_MODEL_FROM_DATABASE=PXI-6608 + +pci:v00001093d00002D20* + ID_MODEL_FROM_DATABASE=PXI-5600 + +pci:v00001093d00002DB0* + ID_MODEL_FROM_DATABASE=PCI-6608 + +pci:v00001093d00002DC0* + ID_MODEL_FROM_DATABASE=PCI-4070 + +pci:v00001093d00002DD0* + ID_MODEL_FROM_DATABASE=PXI-4070 + +pci:v00001093d00002EB0* + ID_MODEL_FROM_DATABASE=PXI-4472 + +pci:v00001093d00002EC0* + ID_MODEL_FROM_DATABASE=PXI-6115 + +pci:v00001093d00002ED0* + ID_MODEL_FROM_DATABASE=PCI-6115 + +pci:v00001093d00002EE0* + ID_MODEL_FROM_DATABASE=PXI-6120 + +pci:v00001093d00002EF0* + ID_MODEL_FROM_DATABASE=PCI-6120 + +pci:v00001093d00002FD1* + ID_MODEL_FROM_DATABASE=PCI-7334 + +pci:v00001093d00002FD2* + ID_MODEL_FROM_DATABASE=PCI-7350 + +pci:v00001093d00002FD3* + ID_MODEL_FROM_DATABASE=PCI-7342 + +pci:v00001093d00002FD5* + ID_MODEL_FROM_DATABASE=PXI-7350 + +pci:v00001093d00002FD6* + ID_MODEL_FROM_DATABASE=PXI-7342 + +pci:v00001093d00007003* + ID_MODEL_FROM_DATABASE=PCI-6551 + +pci:v00001093d00007004* + ID_MODEL_FROM_DATABASE=PXI-6551 + +pci:v00001093d0000700B* + ID_MODEL_FROM_DATABASE=PXI-5421 + +pci:v00001093d0000700C* + ID_MODEL_FROM_DATABASE=PCI-5421 + +pci:v00001093d0000701A* + ID_MODEL_FROM_DATABASE=VXIpc-87xB + +pci:v00001093d0000701B* + ID_MODEL_FROM_DATABASE=VXIpc-770 + +pci:v00001093d00007023* + ID_MODEL_FROM_DATABASE=PXI-2593 + +pci:v00001093d00007027* + ID_MODEL_FROM_DATABASE=PCI-MXI-2 Universal + +pci:v00001093d0000702C* + ID_MODEL_FROM_DATABASE=PXI-7831R + +pci:v00001093d0000702D* + ID_MODEL_FROM_DATABASE=PCI-7831R + +pci:v00001093d0000702E* + ID_MODEL_FROM_DATABASE=PXI-7811R + +pci:v00001093d0000702F* + ID_MODEL_FROM_DATABASE=PCI-7811R + +pci:v00001093d00007030* + ID_MODEL_FROM_DATABASE=PCI-CAN (Series 2) + +pci:v00001093d00007031* + ID_MODEL_FROM_DATABASE=PCI-CAN/2 (Series 2) + +pci:v00001093d00007032* + ID_MODEL_FROM_DATABASE=PCI-CAN/LS (Series 2) + +pci:v00001093d00007033* + ID_MODEL_FROM_DATABASE=PCI-CAN/LS2 (Series 2) + +pci:v00001093d00007034* + ID_MODEL_FROM_DATABASE=PCI-CAN/DS (Series 2) + +pci:v00001093d00007035* + ID_MODEL_FROM_DATABASE=PXI-8460 (Series 2, 1 port) + +pci:v00001093d00007036* + ID_MODEL_FROM_DATABASE=PXI-8460 (Series 2, 2 ports) + +pci:v00001093d00007037* + ID_MODEL_FROM_DATABASE=PXI-8461 (Series 2, 1 port) + +pci:v00001093d00007038* + ID_MODEL_FROM_DATABASE=PXI-8461 (Series 2, 2 ports) + +pci:v00001093d00007039* + ID_MODEL_FROM_DATABASE=PXI-8462 (Series 2) + +pci:v00001093d0000703F* + ID_MODEL_FROM_DATABASE=PXI-2566 + +pci:v00001093d00007040* + ID_MODEL_FROM_DATABASE=PXI-2567 + +pci:v00001093d00007044* + ID_MODEL_FROM_DATABASE=MXI-4 Connection Monitor + +pci:v00001093d00007047* + ID_MODEL_FROM_DATABASE=PXI-6653 + +pci:v00001093d0000704C* + ID_MODEL_FROM_DATABASE=PXI-2530 + +pci:v00001093d0000704F* + ID_MODEL_FROM_DATABASE=PXI-4220 + +pci:v00001093d00007050* + ID_MODEL_FROM_DATABASE=PXI-4204 + +pci:v00001093d00007055* + ID_MODEL_FROM_DATABASE=PXI-7830R + +pci:v00001093d00007056* + ID_MODEL_FROM_DATABASE=PCI-7830R + +pci:v00001093d0000705A* + ID_MODEL_FROM_DATABASE=PCI-CAN/XS (Series 2) + +pci:v00001093d0000705B* + ID_MODEL_FROM_DATABASE=PCI-CAN/XS2 (Series 2) + +pci:v00001093d0000705C* + ID_MODEL_FROM_DATABASE=PXI-8464 (Series 2, 1 port) + +pci:v00001093d0000705D* + ID_MODEL_FROM_DATABASE=PXI-8464 (Series 2, 2 ports) + +pci:v00001093d0000705E* + ID_MODEL_FROM_DATABASE=cRIO-9102 + +pci:v00001093d00007060* + ID_MODEL_FROM_DATABASE=PXI-5610 + +pci:v00001093d00007064* + ID_MODEL_FROM_DATABASE=PXI-1045 Trigger Routing Module + +pci:v00001093d00007065* + ID_MODEL_FROM_DATABASE=PXI-6652 + +pci:v00001093d00007066* + ID_MODEL_FROM_DATABASE=PXI-6651 + +pci:v00001093d00007067* + ID_MODEL_FROM_DATABASE=PXI-2529 + +pci:v00001093d00007068* + ID_MODEL_FROM_DATABASE=PCI-CAN/SW (Series 2) + +pci:v00001093d00007069* + ID_MODEL_FROM_DATABASE=PCI-CAN/SW2 (Series 2) + +pci:v00001093d0000706A* + ID_MODEL_FROM_DATABASE=PXI-8463 (Series 2, 1 port) + +pci:v00001093d0000706B* + ID_MODEL_FROM_DATABASE=PXI-8463 (Series 2, 2 ports) + +pci:v00001093d00007073* + ID_MODEL_FROM_DATABASE=PCI-6723 + +pci:v00001093d00007074* + ID_MODEL_FROM_DATABASE=PXI-7833R + +pci:v00001093d00007075* + ID_MODEL_FROM_DATABASE=PXI-6552 + +pci:v00001093d00007076* + ID_MODEL_FROM_DATABASE=PCI-6552 + +pci:v00001093d0000707C* + ID_MODEL_FROM_DATABASE=PXI-1428 + +pci:v00001093d0000707E* + ID_MODEL_FROM_DATABASE=PXI-4462 + +pci:v00001093d00007080* + ID_MODEL_FROM_DATABASE=PXI-8430/2 (RS-232) Interface + +pci:v00001093d00007081* + ID_MODEL_FROM_DATABASE=PXI-8431/2 (RS-485) Interface + +pci:v00001093d00007083* + ID_MODEL_FROM_DATABASE=PCI-7833R + +pci:v00001093d00007085* + ID_MODEL_FROM_DATABASE=PCI-6509 + +pci:v00001093d00007086* + ID_MODEL_FROM_DATABASE=PXI-6528 + +pci:v00001093d00007087* + ID_MODEL_FROM_DATABASE=PCI-6515 + +pci:v00001093d00007088* + ID_MODEL_FROM_DATABASE=PCI-6514 + +pci:v00001093d0000708C* + ID_MODEL_FROM_DATABASE=PXI-2568 + +pci:v00001093d0000708D* + ID_MODEL_FROM_DATABASE=PXI-2569 + +pci:v00001093d000070A9* + ID_MODEL_FROM_DATABASE=PCI-6528 + +pci:v00001093d000070AA* + ID_MODEL_FROM_DATABASE=PCI-6229 + +pci:v00001093d000070AB* + ID_MODEL_FROM_DATABASE=PCI-6259 + +pci:v00001093d000070AC* + ID_MODEL_FROM_DATABASE=PCI-6289 + +pci:v00001093d000070AD* + ID_MODEL_FROM_DATABASE=PXI-6251 + +pci:v00001093d000070AE* + ID_MODEL_FROM_DATABASE=PXI-6220 + +pci:v00001093d000070AF* + ID_MODEL_FROM_DATABASE=PCI-6221 + +pci:v00001093d000070B0* + ID_MODEL_FROM_DATABASE=PCI-6220 + +pci:v00001093d000070B1* + ID_MODEL_FROM_DATABASE=PXI-6229 + +pci:v00001093d000070B2* + ID_MODEL_FROM_DATABASE=PXI-6259 + +pci:v00001093d000070B3* + ID_MODEL_FROM_DATABASE=PXI-6289 + +pci:v00001093d000070B4* + ID_MODEL_FROM_DATABASE=PCI-6250 + +pci:v00001093d000070B5* + ID_MODEL_FROM_DATABASE=PXI-6221 + +pci:v00001093d000070B6* + ID_MODEL_FROM_DATABASE=PCI-6280 + +pci:v00001093d000070B7* + ID_MODEL_FROM_DATABASE=PCI-6254 + +pci:v00001093d000070B8* + ID_MODEL_FROM_DATABASE=PCI-6251 + +pci:v00001093d000070B9* + ID_MODEL_FROM_DATABASE=PXI-6250 + +pci:v00001093d000070BA* + ID_MODEL_FROM_DATABASE=PXI-6254 + +pci:v00001093d000070BB* + ID_MODEL_FROM_DATABASE=PXI-6280 + +pci:v00001093d000070BC* + ID_MODEL_FROM_DATABASE=PCI-6284 + +pci:v00001093d000070BD* + ID_MODEL_FROM_DATABASE=PCI-6281 + +pci:v00001093d000070BE* + ID_MODEL_FROM_DATABASE=PXI-6284 + +pci:v00001093d000070BF* + ID_MODEL_FROM_DATABASE=PXI-6281 + +pci:v00001093d000070C0* + ID_MODEL_FROM_DATABASE=PCI-6143 + +pci:v00001093d000070C3* + ID_MODEL_FROM_DATABASE=PCI-6511 + +pci:v00001093d000070C4* + ID_MODEL_FROM_DATABASE=PXI-7330 + +pci:v00001093d000070C5* + ID_MODEL_FROM_DATABASE=PXI-7340 + +pci:v00001093d000070C6* + ID_MODEL_FROM_DATABASE=PCI-7330 + +pci:v00001093d000070C7* + ID_MODEL_FROM_DATABASE=PCI-7340 + +pci:v00001093d000070C8* + ID_MODEL_FROM_DATABASE=PCI-6513 + +pci:v00001093d000070C9* + ID_MODEL_FROM_DATABASE=PXI-6515 + +pci:v00001093d000070CA* + ID_MODEL_FROM_DATABASE=PCI-1405 + +pci:v00001093d000070CC* + ID_MODEL_FROM_DATABASE=PCI-6512 + +pci:v00001093d000070CD* + ID_MODEL_FROM_DATABASE=PXI-6514 + +pci:v00001093d000070CE* + ID_MODEL_FROM_DATABASE=PXI-1405 + +pci:v00001093d000070CF* + ID_MODEL_FROM_DATABASE=PCIe-GPIB + +pci:v00001093d000070D0* + ID_MODEL_FROM_DATABASE=PXI-2570 + +pci:v00001093d000070D1* + ID_MODEL_FROM_DATABASE=PXI-6513 + +pci:v00001093d000070D2* + ID_MODEL_FROM_DATABASE=PXI-6512 + +pci:v00001093d000070D3* + ID_MODEL_FROM_DATABASE=PXI-6511 + +pci:v00001093d000070D4* + ID_MODEL_FROM_DATABASE=PCI-6722 + +pci:v00001093d000070D6* + ID_MODEL_FROM_DATABASE=PXI-4072 + +pci:v00001093d000070D7* + ID_MODEL_FROM_DATABASE=PXI-6541 + +pci:v00001093d000070D8* + ID_MODEL_FROM_DATABASE=PXI-6542 + +pci:v00001093d000070D9* + ID_MODEL_FROM_DATABASE=PCI-6541 + +pci:v00001093d000070DA* + ID_MODEL_FROM_DATABASE=PCI-6542 + +pci:v00001093d000070DB* + ID_MODEL_FROM_DATABASE=PCI-8430/2 (RS-232) Interface + +pci:v00001093d000070DC* + ID_MODEL_FROM_DATABASE=PCI-8431/2 (RS-485) Interface + +pci:v00001093d000070DD* + ID_MODEL_FROM_DATABASE=PXI-8430/4 (RS-232) Interface + +pci:v00001093d000070DE* + ID_MODEL_FROM_DATABASE=PXI-8431/4 (RS-485) Interface + +pci:v00001093d000070DF* + ID_MODEL_FROM_DATABASE=PCI-8430/4 (RS-232) Interface + +pci:v00001093d000070E0* + ID_MODEL_FROM_DATABASE=PCI-8431/4 (RS-485) Interface + +pci:v00001093d000070E1* + ID_MODEL_FROM_DATABASE=PXI-2532 + +pci:v00001093d000070E2* + ID_MODEL_FROM_DATABASE=PXI-8430/8 (RS-232) Interface + +pci:v00001093d000070E3* + ID_MODEL_FROM_DATABASE=PXI-8431/8 (RS-485) Interface + +pci:v00001093d000070E4* + ID_MODEL_FROM_DATABASE=PCI-8430/8 (RS-232) Interface + +pci:v00001093d000070E5* + ID_MODEL_FROM_DATABASE=PCI-8431/8 (RS-485) Interface + +pci:v00001093d000070E6* + ID_MODEL_FROM_DATABASE=PXI-8430/16 (RS-232) Interface + +pci:v00001093d000070E7* + ID_MODEL_FROM_DATABASE=PCI-8430/16 (RS-232) Interface + +pci:v00001093d000070E8* + ID_MODEL_FROM_DATABASE=PXI-8432/2 (Isolated RS-232) Interface + +pci:v00001093d000070E9* + ID_MODEL_FROM_DATABASE=PXI-8433/2 (Isolated RS-485) Interface + +pci:v00001093d000070EA* + ID_MODEL_FROM_DATABASE=PCI-8432/2 (Isolated RS-232) Interface + +pci:v00001093d000070EB* + ID_MODEL_FROM_DATABASE=PCI-8433/2 (Isolated RS-485) Interface + +pci:v00001093d000070EC* + ID_MODEL_FROM_DATABASE=PXI-8432/4 (Isolated RS-232) Interface + +pci:v00001093d000070ED* + ID_MODEL_FROM_DATABASE=PXI-8433/4 (Isolated RS-485) Interface + +pci:v00001093d000070EE* + ID_MODEL_FROM_DATABASE=PCI-8432/4 (Isolated RS-232) Interface + +pci:v00001093d000070EF* + ID_MODEL_FROM_DATABASE=PCI-8433/4 (Isolated RS-485) Interface + +pci:v00001093d000070F0* + ID_MODEL_FROM_DATABASE=PXI-5922 + +pci:v00001093d000070F1* + ID_MODEL_FROM_DATABASE=PCI-5922 + +pci:v00001093d000070F2* + ID_MODEL_FROM_DATABASE=PCI-6224 + +pci:v00001093d000070F3* + ID_MODEL_FROM_DATABASE=PXI-6224 + +pci:v00001093d000070F6* + ID_MODEL_FROM_DATABASE=cRIO-9101 + +pci:v00001093d000070F7* + ID_MODEL_FROM_DATABASE=cRIO-9103 + +pci:v00001093d000070F8* + ID_MODEL_FROM_DATABASE=cRIO-9104 + +pci:v00001093d000070FF* + ID_MODEL_FROM_DATABASE=PXI-6723 + +pci:v00001093d00007100* + ID_MODEL_FROM_DATABASE=PXI-6722 + +pci:v00001093d00007104* + ID_MODEL_FROM_DATABASE=PCIx-1429 + +pci:v00001093d00007105* + ID_MODEL_FROM_DATABASE=PCIe-1429 + +pci:v00001093d0000710A* + ID_MODEL_FROM_DATABASE=PXI-4071 + +pci:v00001093d0000710D* + ID_MODEL_FROM_DATABASE=PXI-6143 + +pci:v00001093d0000710E* + ID_MODEL_FROM_DATABASE=PCIe-GPIB + +pci:v00001093d0000710F* + ID_MODEL_FROM_DATABASE=PXI-5422 + +pci:v00001093d00007110* + ID_MODEL_FROM_DATABASE=PCI-5422 + +pci:v00001093d00007111* + ID_MODEL_FROM_DATABASE=PXI-5441 + +pci:v00001093d00007119* + ID_MODEL_FROM_DATABASE=PXI-6561 + +pci:v00001093d0000711A* + ID_MODEL_FROM_DATABASE=PXI-6562 + +pci:v00001093d0000711B* + ID_MODEL_FROM_DATABASE=PCI-6561 + +pci:v00001093d0000711C* + ID_MODEL_FROM_DATABASE=PCI-6562 + +pci:v00001093d00007120* + ID_MODEL_FROM_DATABASE=PCI-7390 + +pci:v00001093d00007121* + ID_MODEL_FROM_DATABASE=PXI-5122EX + +pci:v00001093d00007122* + ID_MODEL_FROM_DATABASE=PCI-5122EX + +pci:v00001093d00007123* + ID_MODEL_FROM_DATABASE=PXIe-5653 + +pci:v00001093d00007124* + ID_MODEL_FROM_DATABASE=PCI-6510 + +pci:v00001093d00007125* + ID_MODEL_FROM_DATABASE=PCI-6516 + +pci:v00001093d00007126* + ID_MODEL_FROM_DATABASE=PCI-6517 + +pci:v00001093d00007127* + ID_MODEL_FROM_DATABASE=PCI-6518 + +pci:v00001093d00007128* + ID_MODEL_FROM_DATABASE=PCI-6519 + +pci:v00001093d00007137* + ID_MODEL_FROM_DATABASE=PXI-2575 + +pci:v00001093d0000713C* + ID_MODEL_FROM_DATABASE=PXI-2585 + +pci:v00001093d0000713D* + ID_MODEL_FROM_DATABASE=PXI-2586 + +pci:v00001093d00007142* + ID_MODEL_FROM_DATABASE=PXI-4224 + +pci:v00001093d00007144* + ID_MODEL_FROM_DATABASE=PXI-5124 + +pci:v00001093d00007145* + ID_MODEL_FROM_DATABASE=PCI-5124 + +pci:v00001093d00007146* + ID_MODEL_FROM_DATABASE=PCI-6132 + +pci:v00001093d00007147* + ID_MODEL_FROM_DATABASE=PXI-6132 + +pci:v00001093d00007148* + ID_MODEL_FROM_DATABASE=PCI-6122 + +pci:v00001093d00007149* + ID_MODEL_FROM_DATABASE=PXI-6122 + +pci:v00001093d0000714C* + ID_MODEL_FROM_DATABASE=PXI-5114 + +pci:v00001093d0000714D* + ID_MODEL_FROM_DATABASE=PCI-5114 + +pci:v00001093d00007150* + ID_MODEL_FROM_DATABASE=PXI-2564 + +pci:v00001093d00007152* + ID_MODEL_FROM_DATABASE=PCI-5640R + +pci:v00001093d00007156* + ID_MODEL_FROM_DATABASE=PXI-1044 Trigger Routing Module + +pci:v00001093d0000715D* + ID_MODEL_FROM_DATABASE=PCI-1426 + +pci:v00001093d00007167* + ID_MODEL_FROM_DATABASE=PXI-5412 + +pci:v00001093d00007168* + ID_MODEL_FROM_DATABASE=PCI-5412 + +pci:v00001093d0000716B* + ID_MODEL_FROM_DATABASE=PCI-6230 + +pci:v00001093d0000716C* + ID_MODEL_FROM_DATABASE=PCI-6225 + +pci:v00001093d0000716D* + ID_MODEL_FROM_DATABASE=PXI-6225 + +pci:v00001093d0000716F* + ID_MODEL_FROM_DATABASE=PCI-4461 + +pci:v00001093d00007170* + ID_MODEL_FROM_DATABASE=PCI-4462 + +pci:v00001093d00007171* + ID_MODEL_FROM_DATABASE=PCI-6010 + +pci:v00001093d00007174* + ID_MODEL_FROM_DATABASE=PXI-8360 + +pci:v00001093d00007177* + ID_MODEL_FROM_DATABASE=PXI-6230 + +pci:v00001093d0000717D* + ID_MODEL_FROM_DATABASE=PCIe-6251 + +pci:v00001093d0000717F* + ID_MODEL_FROM_DATABASE=PCIe-6259 + +pci:v00001093d00007187* + ID_MODEL_FROM_DATABASE=PCI-1410 + +pci:v00001093d0000718B* + ID_MODEL_FROM_DATABASE=PCI-6521 + +pci:v00001093d0000718C* + ID_MODEL_FROM_DATABASE=PXI-6521 + +pci:v00001093d00007191* + ID_MODEL_FROM_DATABASE=PCI-6154 + +pci:v00001093d00007193* + ID_MODEL_FROM_DATABASE=PXI-7813R + +pci:v00001093d00007194* + ID_MODEL_FROM_DATABASE=PCI-7813R + +pci:v00001093d00007195* + ID_MODEL_FROM_DATABASE=PCI-8254R + +pci:v00001093d00007197* + ID_MODEL_FROM_DATABASE=PXI-5402 + +pci:v00001093d00007198* + ID_MODEL_FROM_DATABASE=PCI-5402 + +pci:v00001093d0000719F* + ID_MODEL_FROM_DATABASE=PCIe-6535 + +pci:v00001093d000071A0* + ID_MODEL_FROM_DATABASE=PCIe-6536 + +pci:v00001093d000071A3* + ID_MODEL_FROM_DATABASE=PXI-5650 + +pci:v00001093d000071A4* + ID_MODEL_FROM_DATABASE=PXI-5652 + +pci:v00001093d000071A5* + ID_MODEL_FROM_DATABASE=PXI-2594 + +pci:v00001093d000071A7* + ID_MODEL_FROM_DATABASE=PXI-2595 + +pci:v00001093d000071A9* + ID_MODEL_FROM_DATABASE=PXI-2596 + +pci:v00001093d000071AA* + ID_MODEL_FROM_DATABASE=PXI-2597 + +pci:v00001093d000071AB* + ID_MODEL_FROM_DATABASE=PXI-2598 + +pci:v00001093d000071AC* + ID_MODEL_FROM_DATABASE=PXI-2599 + +pci:v00001093d000071AD* + ID_MODEL_FROM_DATABASE=PCI-GPIB+ + +pci:v00001093d000071AE* + ID_MODEL_FROM_DATABASE=PCIe-1430 + +pci:v00001093d000071B7* + ID_MODEL_FROM_DATABASE=PXI-1056 Trigger Routing Module + +pci:v00001093d000071B8* + ID_MODEL_FROM_DATABASE=PXI-1045 Trigger Routing Module + +pci:v00001093d000071B9* + ID_MODEL_FROM_DATABASE=PXI-1044 Trigger Routing Module + +pci:v00001093d000071BB* + ID_MODEL_FROM_DATABASE=PXI-2584 + +pci:v00001093d000071BC* + ID_MODEL_FROM_DATABASE=PCI-6221 (37-pin) + +pci:v00001093d000071BF* + ID_MODEL_FROM_DATABASE=PCIe-1427 + +pci:v00001093d000071C5* + ID_MODEL_FROM_DATABASE=PCI-6520 + +pci:v00001093d000071C6* + ID_MODEL_FROM_DATABASE=PXI-2576 + +pci:v00001093d000071C7* + ID_MODEL_FROM_DATABASE=cRIO-9072 + +pci:v00001093d000071DC* + ID_MODEL_FROM_DATABASE=PCI-1588 + +pci:v00001093d000071E0* + ID_MODEL_FROM_DATABASE=PCI-6255 + +pci:v00001093d000071E1* + ID_MODEL_FROM_DATABASE=PXI-6255 + +pci:v00001093d000071E2* + ID_MODEL_FROM_DATABASE=PXI-5406 + +pci:v00001093d000071E3* + ID_MODEL_FROM_DATABASE=PCI-5406 + +pci:v00001093d000071FC* + ID_MODEL_FROM_DATABASE=PXI-4022 + +pci:v00001093d00007209* + ID_MODEL_FROM_DATABASE=PCI-6233 + +pci:v00001093d0000720A* + ID_MODEL_FROM_DATABASE=PXI-6233 + +pci:v00001093d0000720B* + ID_MODEL_FROM_DATABASE=PCI-6238 + +pci:v00001093d0000720C* + ID_MODEL_FROM_DATABASE=PXI-6238 + +pci:v00001093d00007260* + ID_MODEL_FROM_DATABASE=PXI-5142 + +pci:v00001093d00007261* + ID_MODEL_FROM_DATABASE=PCI-5142 + +pci:v00001093d0000726D* + ID_MODEL_FROM_DATABASE=PXI-5651 + +pci:v00001093d00007273* + ID_MODEL_FROM_DATABASE=PXI-4461 + +pci:v00001093d00007274* + ID_MODEL_FROM_DATABASE=PXI-4462 + +pci:v00001093d00007279* + ID_MODEL_FROM_DATABASE=PCI-6232 + +pci:v00001093d0000727A* + ID_MODEL_FROM_DATABASE=PXI-6232 + +pci:v00001093d0000727B* + ID_MODEL_FROM_DATABASE=PCI-6239 + +pci:v00001093d0000727C* + ID_MODEL_FROM_DATABASE=PXI-6239 + +pci:v00001093d0000727E* + ID_MODEL_FROM_DATABASE=SMBus Controller + +pci:v00001093d0000727Esv00001093sd000075AC* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8388) + +pci:v00001093d0000727Esv00001093sd000075AD* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8389) + +pci:v00001093d0000727Esv00001093sd00007650* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8381) + +pci:v00001093d0000727Esv00001093sd00008360* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8360) + +pci:v00001093d0000727Esv00001093sd00008370* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8370) + +pci:v00001093d0000727Esv00001093sd00008375* + ID_MODEL_FROM_DATABASE=SMBus Controller (PXIe-8375) + +pci:v00001093d00007281* + ID_MODEL_FROM_DATABASE=PCI-6236 + +pci:v00001093d00007282* + ID_MODEL_FROM_DATABASE=PXI-6236 + +pci:v00001093d00007283* + ID_MODEL_FROM_DATABASE=PXI-2554 + +pci:v00001093d00007288* + ID_MODEL_FROM_DATABASE=PXIe-5611 + +pci:v00001093d00007293* + ID_MODEL_FROM_DATABASE=PCIe-8255R + +pci:v00001093d0000729D* + ID_MODEL_FROM_DATABASE=cRIO-9074 + +pci:v00001093d000072A4* + ID_MODEL_FROM_DATABASE=PCIe-4065 + +pci:v00001093d000072A7* + ID_MODEL_FROM_DATABASE=PCIe-6537 + +pci:v00001093d000072A8* + ID_MODEL_FROM_DATABASE=PXI-5152 + +pci:v00001093d000072A9* + ID_MODEL_FROM_DATABASE=PCI-5152 + +pci:v00001093d000072AA* + ID_MODEL_FROM_DATABASE=PXI-5105 + +pci:v00001093d000072AB* + ID_MODEL_FROM_DATABASE=PCI-5105 + +pci:v00001093d000072B8* + ID_MODEL_FROM_DATABASE=PXI-6682 + +pci:v00001093d000072D0* + ID_MODEL_FROM_DATABASE=PXI-2545 + +pci:v00001093d000072D1* + ID_MODEL_FROM_DATABASE=PXI-2546 + +pci:v00001093d000072D2* + ID_MODEL_FROM_DATABASE=PXI-2547 + +pci:v00001093d000072D3* + ID_MODEL_FROM_DATABASE=PXI-2548 + +pci:v00001093d000072D4* + ID_MODEL_FROM_DATABASE=PXI-2549 + +pci:v00001093d000072D5* + ID_MODEL_FROM_DATABASE=PXI-2555 + +pci:v00001093d000072D6* + ID_MODEL_FROM_DATABASE=PXI-2556 + +pci:v00001093d000072D7* + ID_MODEL_FROM_DATABASE=PXI-2557 + +pci:v00001093d000072D8* + ID_MODEL_FROM_DATABASE=PXI-2558 + +pci:v00001093d000072D9* + ID_MODEL_FROM_DATABASE=PXI-2559 + +pci:v00001093d000072E8* + ID_MODEL_FROM_DATABASE=PXIe-6251 + +pci:v00001093d000072E9* + ID_MODEL_FROM_DATABASE=PXIe-6259 + +pci:v00001093d000072EF* + ID_MODEL_FROM_DATABASE=PXI-4498 + +pci:v00001093d000072F0* + ID_MODEL_FROM_DATABASE=PXI-4496 + +pci:v00001093d000072FB* + ID_MODEL_FROM_DATABASE=PXIe-6672 + +pci:v00001093d0000730E* + ID_MODEL_FROM_DATABASE=PXI-4130 + +pci:v00001093d0000730F* + ID_MODEL_FROM_DATABASE=PXI-5922EX + +pci:v00001093d00007310* + ID_MODEL_FROM_DATABASE=PCI-5922EX + +pci:v00001093d0000731C* + ID_MODEL_FROM_DATABASE=PXI-2535 + +pci:v00001093d0000731D* + ID_MODEL_FROM_DATABASE=PXI-2536 + +pci:v00001093d00007322* + ID_MODEL_FROM_DATABASE=PXIe-6124 + +pci:v00001093d00007327* + ID_MODEL_FROM_DATABASE=PXI-6529 + +pci:v00001093d0000732C* + ID_MODEL_FROM_DATABASE=VXI-8360T + +pci:v00001093d00007331* + ID_MODEL_FROM_DATABASE=PXIe-5602 + +pci:v00001093d00007332* + ID_MODEL_FROM_DATABASE=PXIe-5601 + +pci:v00001093d00007333* + ID_MODEL_FROM_DATABASE=PXI-5900 + +pci:v00001093d00007335* + ID_MODEL_FROM_DATABASE=PXI-2533 + +pci:v00001093d00007336* + ID_MODEL_FROM_DATABASE=PXI-2534 + +pci:v00001093d00007342* + ID_MODEL_FROM_DATABASE=PXI-4461 + +pci:v00001093d00007349* + ID_MODEL_FROM_DATABASE=PXI-5154 + +pci:v00001093d0000734A* + ID_MODEL_FROM_DATABASE=PCI-5154 + +pci:v00001093d00007357* + ID_MODEL_FROM_DATABASE=PXI-4065 + +pci:v00001093d00007359* + ID_MODEL_FROM_DATABASE=PXI-4495 + +pci:v00001093d00007370* + ID_MODEL_FROM_DATABASE=PXI-4461 + +pci:v00001093d00007373* + ID_MODEL_FROM_DATABASE=sbRIO-9601 + +pci:v00001093d00007374* + ID_MODEL_FROM_DATABASE=IOtech-9601 + +pci:v00001093d00007375* + ID_MODEL_FROM_DATABASE=sbRIO-9602 + +pci:v00001093d00007378* + ID_MODEL_FROM_DATABASE=sbRIO-9641 + +pci:v00001093d0000737D* + ID_MODEL_FROM_DATABASE=PXI-5124EX + +pci:v00001093d00007384* + ID_MODEL_FROM_DATABASE=PXI-7851R + +pci:v00001093d00007385* + ID_MODEL_FROM_DATABASE=PXI-7852R + +pci:v00001093d00007386* + ID_MODEL_FROM_DATABASE=PCIe-7851R + +pci:v00001093d00007387* + ID_MODEL_FROM_DATABASE=PCIe-7852R + +pci:v00001093d00007390* + ID_MODEL_FROM_DATABASE=PXI-7841R + +pci:v00001093d00007391* + ID_MODEL_FROM_DATABASE=PXI-7842R + +pci:v00001093d00007392* + ID_MODEL_FROM_DATABASE=PXI-7853R + +pci:v00001093d00007393* + ID_MODEL_FROM_DATABASE=PCIe-7841R + +pci:v00001093d00007394* + ID_MODEL_FROM_DATABASE=PCIe-7842R + +pci:v00001093d00007397* + ID_MODEL_FROM_DATABASE=sbRIO-9611 + +pci:v00001093d00007398* + ID_MODEL_FROM_DATABASE=sbRIO-9612 + +pci:v00001093d00007399* + ID_MODEL_FROM_DATABASE=sbRIO-9631 + +pci:v00001093d0000739A* + ID_MODEL_FROM_DATABASE=sbRIO-9632 + +pci:v00001093d0000739B* + ID_MODEL_FROM_DATABASE=sbRIO-9642 + +pci:v00001093d000073A1* + ID_MODEL_FROM_DATABASE=PXIe-4498 + +pci:v00001093d000073A2* + ID_MODEL_FROM_DATABASE=PXIe-4496 + +pci:v00001093d000073A5* + ID_MODEL_FROM_DATABASE=PXIe-5641R + +pci:v00001093d000073A7* + ID_MODEL_FROM_DATABASE=PXI-8250 Chassis Monitor Module + +pci:v00001093d000073A8* + ID_MODEL_FROM_DATABASE=PXI-8511 CAN/LS + +pci:v00001093d000073A9* + ID_MODEL_FROM_DATABASE=PXI-8511 CAN/LS + +pci:v00001093d000073AA* + ID_MODEL_FROM_DATABASE=PXI-8512 CAN/HS + +pci:v00001093d000073AB* + ID_MODEL_FROM_DATABASE=PXI-8512 CAN/HS + +pci:v00001093d000073AC* + ID_MODEL_FROM_DATABASE=PXI-8513 CAN/XS + +pci:v00001093d000073AD* + ID_MODEL_FROM_DATABASE=PXI-8513 CAN/XS + +pci:v00001093d000073AF* + ID_MODEL_FROM_DATABASE=PXI-8516 LIN + +pci:v00001093d000073B1* + ID_MODEL_FROM_DATABASE=PXI-8517 FlexRay + +pci:v00001093d000073B2* + ID_MODEL_FROM_DATABASE=PXI-8531 CANopen + +pci:v00001093d000073B3* + ID_MODEL_FROM_DATABASE=PXI-8531 CANopen + +pci:v00001093d000073B4* + ID_MODEL_FROM_DATABASE=PXI-8532 DeviceNet + +pci:v00001093d000073B5* + ID_MODEL_FROM_DATABASE=PXI-8532 DeviceNet + +pci:v00001093d000073B6* + ID_MODEL_FROM_DATABASE=PCI-8511 CAN/LS + +pci:v00001093d000073B7* + ID_MODEL_FROM_DATABASE=PCI-8511 CAN/LS + +pci:v00001093d000073B8* + ID_MODEL_FROM_DATABASE=PCI-8512 CAN/HS + +pci:v00001093d000073B9* + ID_MODEL_FROM_DATABASE=PCI-8512 CAN/HS + +pci:v00001093d000073BA* + ID_MODEL_FROM_DATABASE=PCI-8513 CAN/XS + +pci:v00001093d000073BB* + ID_MODEL_FROM_DATABASE=PCI-8513 CAN/XS + +pci:v00001093d000073BD* + ID_MODEL_FROM_DATABASE=PCI-8516 LIN + +pci:v00001093d000073BF* + ID_MODEL_FROM_DATABASE=PCI-8517 FlexRay + +pci:v00001093d000073C0* + ID_MODEL_FROM_DATABASE=PCI-8531 CANopen + +pci:v00001093d000073C1* + ID_MODEL_FROM_DATABASE=PCI-8531 CANopen + +pci:v00001093d000073C2* + ID_MODEL_FROM_DATABASE=PCI-8532 DeviceNet + +pci:v00001093d000073C3* + ID_MODEL_FROM_DATABASE=PCI-8532 DeviceNet + +pci:v00001093d000073C5* + ID_MODEL_FROM_DATABASE=PXIe-2527 + +pci:v00001093d000073C6* + ID_MODEL_FROM_DATABASE=PXIe-2529 + +pci:v00001093d000073C8* + ID_MODEL_FROM_DATABASE=PXIe-2530 + +pci:v00001093d000073C9* + ID_MODEL_FROM_DATABASE=PXIe-2532 + +pci:v00001093d000073CA* + ID_MODEL_FROM_DATABASE=PXIe-2569 + +pci:v00001093d000073CB* + ID_MODEL_FROM_DATABASE=PXIe-2575 + +pci:v00001093d000073CC* + ID_MODEL_FROM_DATABASE=PXIe-2593 + +pci:v00001093d000073D5* + ID_MODEL_FROM_DATABASE=PXI-7951R + +pci:v00001093d000073D6* + ID_MODEL_FROM_DATABASE=PXI-7952R + +pci:v00001093d000073D7* + ID_MODEL_FROM_DATABASE=PXI-7953R + +pci:v00001093d000073E1* + ID_MODEL_FROM_DATABASE=PXI-7854R + +pci:v00001093d000073EC* + ID_MODEL_FROM_DATABASE=PXI-7954R + +pci:v00001093d000073ED* + ID_MODEL_FROM_DATABASE=cRIO-9073 + +pci:v00001093d000073F0* + ID_MODEL_FROM_DATABASE=PXI-5153 + +pci:v00001093d000073F1* + ID_MODEL_FROM_DATABASE=PCI-5153 + +pci:v00001093d000073F4* + ID_MODEL_FROM_DATABASE=PXI-2515 + +pci:v00001093d000073F6* + ID_MODEL_FROM_DATABASE=cRIO-9111 + +pci:v00001093d000073F7* + ID_MODEL_FROM_DATABASE=cRIO-9112 + +pci:v00001093d000073F8* + ID_MODEL_FROM_DATABASE=cRIO-9113 + +pci:v00001093d000073F9* + ID_MODEL_FROM_DATABASE=cRIO-9114 + +pci:v00001093d000073FA* + ID_MODEL_FROM_DATABASE=cRIO-9116 + +pci:v00001093d000073FB* + ID_MODEL_FROM_DATABASE=cRIO-9118 + +pci:v00001093d00007404* + ID_MODEL_FROM_DATABASE=PXI-4132 + +pci:v00001093d00007405* + ID_MODEL_FROM_DATABASE=PXIe-6674T + +pci:v00001093d00007406* + ID_MODEL_FROM_DATABASE=PXIe-6674 + +pci:v00001093d0000740E* + ID_MODEL_FROM_DATABASE=PCIe-8430/16 (RS-232) Interface + +pci:v00001093d0000740F* + ID_MODEL_FROM_DATABASE=PCIe-8430/8 (RS-232) Interface + +pci:v00001093d00007410* + ID_MODEL_FROM_DATABASE=PCIe-8431/16 (RS-485) Interface + +pci:v00001093d00007411* + ID_MODEL_FROM_DATABASE=PCIe-8431/8 (RS-485) Interface + +pci:v00001093d00007414* + ID_MODEL_FROM_DATABASE=PCIe-GPIB+ + +pci:v00001093d0000741C* + ID_MODEL_FROM_DATABASE=PXI-5691 + +pci:v00001093d0000741D* + ID_MODEL_FROM_DATABASE=PXI-5695 + +pci:v00001093d0000743C* + ID_MODEL_FROM_DATABASE=CSC-3059 + +pci:v00001093d00007448* + ID_MODEL_FROM_DATABASE=PXI-2510 + +pci:v00001093d00007454* + ID_MODEL_FROM_DATABASE=PXI-2512 + +pci:v00001093d00007455* + ID_MODEL_FROM_DATABASE=PXI-2514 + +pci:v00001093d00007456* + ID_MODEL_FROM_DATABASE=PXIe-2512 + +pci:v00001093d00007457* + ID_MODEL_FROM_DATABASE=PXIe-2514 + +pci:v00001093d0000745A* + ID_MODEL_FROM_DATABASE=PXI-6682H + +pci:v00001093d0000745E* + ID_MODEL_FROM_DATABASE=PXI-5153EX + +pci:v00001093d0000745F* + ID_MODEL_FROM_DATABASE=PCI-5153EX + +pci:v00001093d00007460* + ID_MODEL_FROM_DATABASE=PXI-5154EX + +pci:v00001093d00007461* + ID_MODEL_FROM_DATABASE=PCI-5154EX + +pci:v00001093d0000746D* + ID_MODEL_FROM_DATABASE=PXIe-5650 + +pci:v00001093d0000746E* + ID_MODEL_FROM_DATABASE=PXIe-5651 + +pci:v00001093d0000746F* + ID_MODEL_FROM_DATABASE=PXIe-5652 + +pci:v00001093d00007472* + ID_MODEL_FROM_DATABASE=PXI-2800 + +pci:v00001093d00007495* + ID_MODEL_FROM_DATABASE=PXIe-5603 + +pci:v00001093d00007497* + ID_MODEL_FROM_DATABASE=PXIe-5605 + +pci:v00001093d000074AE* + ID_MODEL_FROM_DATABASE=PXIe-2515 + +pci:v00001093d000074B4* + ID_MODEL_FROM_DATABASE=PXI-2531 + +pci:v00001093d000074B5* + ID_MODEL_FROM_DATABASE=PXIe-2531 + +pci:v00001093d000074C1* + ID_MODEL_FROM_DATABASE=PXIe-8430/16 (RS-232) Interface + +pci:v00001093d000074C2* + ID_MODEL_FROM_DATABASE=PXIe-8430/8 (RS-232) Interface + +pci:v00001093d000074C3* + ID_MODEL_FROM_DATABASE=PXIe-8431/16 (RS-485) Interface + +pci:v00001093d000074C4* + ID_MODEL_FROM_DATABASE=PXIe-8431/8 (RS-485) Interface + +pci:v00001093d000074D5* + ID_MODEL_FROM_DATABASE=PXIe-5630 + +pci:v00001093d000074D9* + ID_MODEL_FROM_DATABASE=PCIe-8432/2 (Isolated RS-232) Interface + +pci:v00001093d000074DA* + ID_MODEL_FROM_DATABASE=PCIe-8433/2 (Isolated RS-485) Interface + +pci:v00001093d000074DB* + ID_MODEL_FROM_DATABASE=PCIe-8432/4 (Isolated RS-232) Interface + +pci:v00001093d000074DC* + ID_MODEL_FROM_DATABASE=PCIe-8433/4 (Isolated RS-485) Interface + +pci:v00001093d000074E8* + ID_MODEL_FROM_DATABASE=NI 9148 + +pci:v00001093d00007515* + ID_MODEL_FROM_DATABASE=PCIe-8430/2 (RS-232) Interface + +pci:v00001093d00007516* + ID_MODEL_FROM_DATABASE=PCIe-8430/4 (RS-232) Interface + +pci:v00001093d00007517* + ID_MODEL_FROM_DATABASE=PCIe-8431/2 (RS-485) Interface + +pci:v00001093d00007518* + ID_MODEL_FROM_DATABASE=PCIe-8431/4 (RS-485) Interface + +pci:v00001093d0000751B* + ID_MODEL_FROM_DATABASE=cRIO-9081 + +pci:v00001093d0000751C* + ID_MODEL_FROM_DATABASE=cRIO-9082 + +pci:v00001093d00007528* + ID_MODEL_FROM_DATABASE=PXIe-4497 + +pci:v00001093d00007529* + ID_MODEL_FROM_DATABASE=PXIe-4499 + +pci:v00001093d0000752A* + ID_MODEL_FROM_DATABASE=PXIe-4492 + +pci:v00001093d00007539* + ID_MODEL_FROM_DATABASE=NI 9157 + +pci:v00001093d0000753A* + ID_MODEL_FROM_DATABASE=NI 9159 + +pci:v00001093d00007598* + ID_MODEL_FROM_DATABASE=PXI-2571 + +pci:v00001093d000075A4* + ID_MODEL_FROM_DATABASE=PXI-4131A + +pci:v00001093d000075B1* + ID_MODEL_FROM_DATABASE=PCIe-7854R + +pci:v00001093d000075BA* + ID_MODEL_FROM_DATABASE=PXI-2543 + +pci:v00001093d000075BB* + ID_MODEL_FROM_DATABASE=PXIe-2543 + +pci:v00001093d000075E5* + ID_MODEL_FROM_DATABASE=PXI-6683 + +pci:v00001093d000075E6* + ID_MODEL_FROM_DATABASE=PXI-6683H + +pci:v00001093d000075EF* + ID_MODEL_FROM_DATABASE=PXIe-5632 + +pci:v00001093d0000761C* + ID_MODEL_FROM_DATABASE=VXI-8360LT + +pci:v00001093d0000761F* + ID_MODEL_FROM_DATABASE=PXI-2540 + +pci:v00001093d00007620* + ID_MODEL_FROM_DATABASE=PXIe-2540 + +pci:v00001093d00007621* + ID_MODEL_FROM_DATABASE=PXI-2541 + +pci:v00001093d00007622* + ID_MODEL_FROM_DATABASE=PXIe-2541 + +pci:v00001093d00007626* + ID_MODEL_FROM_DATABASE=NI 9154 + +pci:v00001093d00007627* + ID_MODEL_FROM_DATABASE=NI 9155 + +pci:v00001093d00007638* + ID_MODEL_FROM_DATABASE=PXI-2720 + +pci:v00001093d00007639* + ID_MODEL_FROM_DATABASE=PXI-2722 + +pci:v00001093d0000763A* + ID_MODEL_FROM_DATABASE=PXIe-2725 + +pci:v00001093d0000763B* + ID_MODEL_FROM_DATABASE=PXIe-2727 + +pci:v00001093d0000763C* + ID_MODEL_FROM_DATABASE=PXI-4465 + +pci:v00001093d0000764B* + ID_MODEL_FROM_DATABASE=PXIe-2790 + +pci:v00001093d0000764C* + ID_MODEL_FROM_DATABASE=PXI-2520 + +pci:v00001093d0000764D* + ID_MODEL_FROM_DATABASE=PXI-2521 + +pci:v00001093d0000764E* + ID_MODEL_FROM_DATABASE=PXI-2522 + +pci:v00001093d0000764F* + ID_MODEL_FROM_DATABASE=PXI-2523 + +pci:v00001093d00007654* + ID_MODEL_FROM_DATABASE=PXI-2796 + +pci:v00001093d00007655* + ID_MODEL_FROM_DATABASE=PXI-2797 + +pci:v00001093d00007656* + ID_MODEL_FROM_DATABASE=PXI-2798 + +pci:v00001093d00007657* + ID_MODEL_FROM_DATABASE=PXI-2799 + +pci:v00001093d0000765D* + ID_MODEL_FROM_DATABASE=PXI-2542 + +pci:v00001093d0000765E* + ID_MODEL_FROM_DATABASE=PXIe-2542 + +pci:v00001093d0000765F* + ID_MODEL_FROM_DATABASE=PXI-2544 + +pci:v00001093d00007660* + ID_MODEL_FROM_DATABASE=PXIe-2544 + +pci:v00001093d0000766D* + ID_MODEL_FROM_DATABASE=PCIe-6535B + +pci:v00001093d0000766E* + ID_MODEL_FROM_DATABASE=PCIe-6536B + +pci:v00001093d0000766F* + ID_MODEL_FROM_DATABASE=PCIe-6537B + +pci:v00001093d000076A3* + ID_MODEL_FROM_DATABASE=PXIe-6535B + +pci:v00001093d000076A4* + ID_MODEL_FROM_DATABASE=PXIe-6536B + +pci:v00001093d000076A5* + ID_MODEL_FROM_DATABASE=PXIe-6537B + +pci:v00001093d0000783E* + ID_MODEL_FROM_DATABASE=PXI-8368 + +pci:v00001093d00009020* + ID_MODEL_FROM_DATABASE=PXI-2501 + +pci:v00001093d00009030* + ID_MODEL_FROM_DATABASE=PXI-2503 + +pci:v00001093d00009040* + ID_MODEL_FROM_DATABASE=PXI-2527 + +pci:v00001093d00009050* + ID_MODEL_FROM_DATABASE=PXI-2565 + +pci:v00001093d00009060* + ID_MODEL_FROM_DATABASE=PXI-2590 + +pci:v00001093d00009070* + ID_MODEL_FROM_DATABASE=PXI-2591 + +pci:v00001093d00009080* + ID_MODEL_FROM_DATABASE=PXI-2580 + +pci:v00001093d00009090* + ID_MODEL_FROM_DATABASE=PCI-4021 + +pci:v00001093d000090A0* + ID_MODEL_FROM_DATABASE=PXI-4021 + +pci:v00001093d0000A001* + ID_MODEL_FROM_DATABASE=PCI-MXI-2 + +pci:v00001093d0000B001* + ID_MODEL_FROM_DATABASE=PCI-1408 + +pci:v00001093d0000B011* + ID_MODEL_FROM_DATABASE=PXI-1408 + +pci:v00001093d0000B021* + ID_MODEL_FROM_DATABASE=PCI-1424 + +pci:v00001093d0000B022* + ID_MODEL_FROM_DATABASE=PXI-1424 + +pci:v00001093d0000B031* + ID_MODEL_FROM_DATABASE=PCI-1413 + +pci:v00001093d0000B041* + ID_MODEL_FROM_DATABASE=PCI-1407 + +pci:v00001093d0000B051* + ID_MODEL_FROM_DATABASE=PXI-1407 + +pci:v00001093d0000B061* + ID_MODEL_FROM_DATABASE=PCI-1411 + +pci:v00001093d0000B071* + ID_MODEL_FROM_DATABASE=PCI-1422 + +pci:v00001093d0000B081* + ID_MODEL_FROM_DATABASE=PXI-1422 + +pci:v00001093d0000B091* + ID_MODEL_FROM_DATABASE=PXI-1411 + +pci:v00001093d0000B0B1* + ID_MODEL_FROM_DATABASE=PCI-1409 + +pci:v00001093d0000B0C1* + ID_MODEL_FROM_DATABASE=PXI-1409 + +pci:v00001093d0000B0E1* + ID_MODEL_FROM_DATABASE=PCI-1428 + +pci:v00001093d0000C4C4* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device + +pci:v00001093d0000C4C4sv00001093sd0000728A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5421) + +pci:v00001093d0000C4C4sv00001093sd0000728B* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5442) + +pci:v00001093d0000C4C4sv00001093sd0000728D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5451) + +pci:v00001093d0000C4C4sv00001093sd000072A2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5122) + +pci:v00001093d0000C4C4sv00001093sd000072DA* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5422) + +pci:v00001093d0000C4C4sv00001093sd000072F7* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6535) + +pci:v00001093d0000C4C4sv00001093sd000072F8* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6536) + +pci:v00001093d0000C4C4sv00001093sd000072F9* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6537) + +pci:v00001093d0000C4C4sv00001093sd00007326* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6509) + +pci:v00001093d0000C4C4sv00001093sd0000736C* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4140) + +pci:v00001093d0000C4C4sv00001093sd0000738B* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5622) + +pci:v00001093d0000C4C4sv00001093sd000073C4* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5450) + +pci:v00001093d0000C4C4sv00001093sd000073C7* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6545) + +pci:v00001093d0000C4C4sv00001093sd000073D4* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6544) + +pci:v00001093d0000C4C4sv00001093sd00007425* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6320) + +pci:v00001093d0000C4C4sv00001093sd00007427* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6321) + +pci:v00001093d0000C4C4sv00001093sd00007428* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6323) + +pci:v00001093d0000C4C4sv00001093sd00007429* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6323) + +pci:v00001093d0000C4C4sv00001093sd0000742A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6341) + +pci:v00001093d0000C4C4sv00001093sd0000742B* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6341) + +pci:v00001093d0000C4C4sv00001093sd0000742C* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6343) + +pci:v00001093d0000C4C4sv00001093sd0000742D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6343) + +pci:v00001093d0000C4C4sv00001093sd0000742F* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6351) + +pci:v00001093d0000C4C4sv00001093sd00007431* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6353) + +pci:v00001093d0000C4C4sv00001093sd00007432* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6361) + +pci:v00001093d0000C4C4sv00001093sd00007433* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6361) + +pci:v00001093d0000C4C4sv00001093sd00007434* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6363) + +pci:v00001093d0000C4C4sv00001093sd00007435* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6363) + +pci:v00001093d0000C4C4sv00001093sd00007436* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6356) + +pci:v00001093d0000C4C4sv00001093sd00007437* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6358) + +pci:v00001093d0000C4C4sv00001093sd00007438* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6366) + +pci:v00001093d0000C4C4sv00001093sd00007439* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6368) + +pci:v00001093d0000C4C4sv00001093sd00007468* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5185) + +pci:v00001093d0000C4C4sv00001093sd00007469* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5186) + +pci:v00001093d0000C4C4sv00001093sd00007492* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4300) + +pci:v00001093d0000C4C4sv00001093sd00007498* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6548) + +pci:v00001093d0000C4C4sv00001093sd00007499* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6547) + +pci:v00001093d0000C4C4sv00001093sd000074A8* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4330) + +pci:v00001093d0000C4C4sv00001093sd000074A9* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4331) + +pci:v00001093d0000C4C4sv00001093sd000074B1* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4154) + +pci:v00001093d0000C4C4sv00001093sd000074B2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4353) + +pci:v00001093d0000C4C4sv00001093sd000074B6* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-1433) + +pci:v00001093d0000C4C4sv00001093sd000074CD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5643R) + +pci:v00001093d0000C4C4sv00001093sd000074D0* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7961R) + +pci:v00001093d0000C4C4sv00001093sd000074DD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6376) + +pci:v00001093d0000C4C4sv00001093sd000074DE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6378) + +pci:v00001093d0000C4C4sv00001093sd000074E2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7962R) + +pci:v00001093d0000C4C4sv00001093sd000074E3* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7965R) + +pci:v00001093d0000C4C4sv00001093sd000074E5* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4844) + +pci:v00001093d0000C4C4sv00001093sd000074F3* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-5140) + +pci:v00001093d0000C4C4sv00001093sd0000753C* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-1435) + +pci:v00001093d0000C4C4sv00001093sd00007548* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5622 (25MHz DDC)) + +pci:v00001093d0000C4C4sv00001093sd0000754D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-5155) + +pci:v00001093d0000C4C4sv00001093sd00007551* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6556) + +pci:v00001093d0000C4C4sv00001093sd00007553* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-1473R) + +pci:v00001093d0000C4C4sv00001093sd00007570* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-1474R) + +pci:v00001093d0000C4C4sv00001093sd00007571* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-1475R) + +pci:v00001093d0000C4C4sv00001093sd00007572* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-1476R) + +pci:v00001093d0000C4C4sv00001093sd000075A2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5693) + +pci:v00001093d0000C4C4sv00001093sd000075A3* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5694) + +pci:v00001093d0000C4C4sv00001093sd000075A5* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4141) + +pci:v00001093d0000C4C4sv00001093sd000075CE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7966R) + +pci:v00001093d0000C4C4sv00001093sd000075CF* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4357) + +pci:v00001093d0000C4C4sv00001093sd000075D2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-RevB-5643R) + +pci:v00001093d0000C4C4sv00001093sd000075D3* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5644R) + +pci:v00001093d0000C4C4sv00001093sd000075EE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5645R) + +pci:v00001093d0000C4C4sv00001093sd00007613* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6555) + +pci:v00001093d0000C4C4sv00001093sd00007619* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5185) + +pci:v00001093d0000C4C4sv00001093sd0000761A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5186) + +pci:v00001093d0000C4C4sv00001093sd00007629* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4142) + +pci:v00001093d0000C4C4sv00001093sd0000762A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4143) + +pci:v00001093d0000C4C4sv00001093sd0000762B* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4138) + +pci:v00001093d0000C4C4sv00001093sd0000762C* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4144) + +pci:v00001093d0000C4C4sv00001093sd0000762D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4145) + +pci:v00001093d0000C4C4sv00001093sd0000762E* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5606) + +pci:v00001093d0000C4C4sv00001093sd00007644* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4841) + +pci:v00001093d0000C4C4sv00001093sd0000764A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-8237R-S) + +pci:v00001093d0000C4C4sv00001093sd00007658* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5162 (4CH)) + +pci:v00001093d0000C4C4sv00001093sd000076AB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4322) + +pci:v00001093d0000C4C4sv00001093sd000076AD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4112) + +pci:v00001093d0000C4C4sv00001093sd000076AE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4113) + +pci:v00001093d0000C4C4sv00001093sd000076B5* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7971R) + +pci:v00001093d0000C4C4sv00001093sd000076B6* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7972R) + +pci:v00001093d0000C4C4sv00001093sd000076B7* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7975R) + +pci:v00001093d0000C4C4sv00001093sd000076B8* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5696) + +pci:v00001093d0000C4C4sv00001093sd000076B9* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5654) + +pci:v00001093d0000C4C4sv00001093sd000076C8* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6614) + +pci:v00001093d0000C4C4sv00001093sd000076C9* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6612) + +pci:v00001093d0000C4C4sv00001093sd000076CB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5646R) + +pci:v00001093d0000C4C4sv00001093sd000076CC* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5162 (2CH)) + +pci:v00001093d0000C4C4sv00001093sd000076CE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (CVS-1459) + +pci:v00001093d0000C4C4sv00001093sd000076D0* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5160 (2CH)) + +pci:v00001093d0000C4C4sv00001093sd000076D1* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5160 (4CH)) + +pci:v00001093d0000C4C4sv00001093sd000076DC* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4610) + +pci:v00001093d0000C4C4sv00001093sd000076EC* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2524) + +pci:v00001093d0000C4C4sv00001093sd000076ED* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2525) + +pci:v00001093d0000C4C4sv00001093sd000076EE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2526) + +pci:v00001093d0000C4C4sv00001093sd000076EF* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2737) + +pci:v00001093d0000C4C4sv00001093sd000076F0* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2738) + +pci:v00001093d0000C4C4sv00001093sd000076F1* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2739) + +pci:v00001093d0000C4C4sv00001093sd000076FB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-1473R-LX110) + +pci:v00001093d0000C4C4sv00001093sd000076FC* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5105) + +pci:v00001093d0000C4C4sv00001093sd000076FD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5114) + +pci:v00001093d0000C4C4sv00001093sd000076FE* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5644R) + +pci:v00001093d0000C4C4sv00001093sd000076FF* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5644R) + +pci:v00001093d0000C4C4sv00001093sd00007700* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5644R) + +pci:v00001093d0000C4C4sv00001093sd00007701* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5645R) + +pci:v00001093d0000C4C4sv00001093sd00007702* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5645R) + +pci:v00001093d0000C4C4sv00001093sd00007703* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5645R) + +pci:v00001093d0000C4C4sv00001093sd0000770C* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4139) + +pci:v00001093d0000C4C4sv00001093sd00007711* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4464) + +pci:v00001093d0000C4C4sv00001093sd00007712* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4463) + +pci:v00001093d0000C4C4sv00001093sd00007716* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PCIe-6612) + +pci:v00001093d0000C4C4sv00001093sd0000771D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (Unconfigured CA4 Switch) + +pci:v00001093d0000C4C4sv00001093sd0000771E* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4339) + +pci:v00001093d0000C4C4sv00001093sd00007735* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9033) + +pci:v00001093d0000C4C4sv00001093sd0000773E* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5624R) + +pci:v00001093d0000C4C4sv00001093sd0000774B* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9031) + +pci:v00001093d0000C4C4sv00001093sd0000774D* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9034) + +pci:v00001093d0000C4C4sv00001093sd00007755* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9030) + +pci:v00001093d0000C4C4sv00001093sd00007768* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2747) + +pci:v00001093d0000C4C4sv00001093sd00007769* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2748) + +pci:v00001093d0000C4C4sv00001093sd0000776A* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-2746) + +pci:v00001093d0000C4C4sv00001093sd00007777* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7976R) + +pci:v00001093d0000C4C4sv00001093sd00007782* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5646R) + +pci:v00001093d0000C4C4sv00001093sd00007783* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5646R) + +pci:v00001093d0000C4C4sv00001093sd00007784* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5646R) + +pci:v00001093d0000C4C4sv00001093sd00007790* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5170R (4CH)) + +pci:v00001093d0000C4C4sv00001093sd00007791* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5170R (8CH)) + +pci:v00001093d0000C4C4sv00001093sd00007793* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5171R (8CH)) + +pci:v00001093d0000C4C4sv00001093sd000077A5* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6345) + +pci:v00001093d0000C4C4sv00001093sd000077A6* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6355) + +pci:v00001093d0000C4C4sv00001093sd000077A7* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6365) + +pci:v00001093d0000C4C4sv00001093sd000077A8* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6375) + +pci:v00001093d0000C4C4sv00001093sd000077AA* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (CVS-1458) + +pci:v00001093d0000C4C4sv00001093sd000077AD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (IC-3173) + +pci:v00001093d0000C4C4sv00001093sd000077B4* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7820R) + +pci:v00001093d0000C4C4sv00001093sd000077B5* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7821R) + +pci:v00001093d0000C4C4sv00001093sd000077B6* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-7822R) + +pci:v00001093d0000C4C4sv00001093sd000077B9* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9038) + +pci:v00001093d0000C4C4sv00001093sd000077BA* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4136) + +pci:v00001093d0000C4C4sv00001093sd000077BB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4137) + +pci:v00001093d0000C4C4sv00001093sd000077C0* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5624R) + +pci:v00001093d0000C4C4sv00001093sd000077C1* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5624R) + +pci:v00001093d0000C4C4sv00001093sd000077C2* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-5624R) + +pci:v00001093d0000C4C4sv00001093sd000077CA* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6738) + +pci:v00001093d0000C4C4sv00001093sd000077CB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-6739) + +pci:v00001093d0000C4C4sv00001093sd000077DB* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9035) + +pci:v00001093d0000C4C4sv00001093sd000077DC* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9036) + +pci:v00001093d0000C4C4sv00001093sd000077DD* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (cRIO-9039) + +pci:v00001093d0000C4C4sv00001093sd00007802* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4302) + +pci:v00001093d0000C4C4sv00001093sd00007803* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4303) + +pci:v00001093d0000C4C4sv00001093sd00007805* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4305) + +pci:v00001093d0000C4C4sv00001093sd0000788E* + ID_MODEL_FROM_DATABASE=PXIe/PCIe Device (PXIe-4304) + +pci:v00001093d0000C801* + ID_MODEL_FROM_DATABASE=PCI-GPIB + +pci:v00001093d0000C811* + ID_MODEL_FROM_DATABASE=PCI-GPIB+ + +pci:v00001093d0000C821* + ID_MODEL_FROM_DATABASE=PXI-GPIB + +pci:v00001093d0000C831* + ID_MODEL_FROM_DATABASE=PMC-GPIB + +pci:v00001093d0000C840* + ID_MODEL_FROM_DATABASE=PCI-GPIB + +pci:v00001093d0000D130* + ID_MODEL_FROM_DATABASE=PCI-232/2 Interface + +pci:v00001093d0000D140* + ID_MODEL_FROM_DATABASE=PCI-232/4 Interface + +pci:v00001093d0000D150* + ID_MODEL_FROM_DATABASE=PCI-232/8 Interface + +pci:v00001093d0000D160* + ID_MODEL_FROM_DATABASE=PCI-485/2 Interface + +pci:v00001093d0000D170* + ID_MODEL_FROM_DATABASE=PCI-485/4 Interface + +pci:v00001093d0000D190* + ID_MODEL_FROM_DATABASE=PXI-8422/2 (Isolated RS-232) Interface + +pci:v00001093d0000D1A0* + ID_MODEL_FROM_DATABASE=PXI-8422/4 (Isolated RS-232) Interface + +pci:v00001093d0000D1B0* + ID_MODEL_FROM_DATABASE=PXI-8423/2 (Isolated RS-485) Interface + +pci:v00001093d0000D1C0* + ID_MODEL_FROM_DATABASE=PXI-8423/4 (Isolated RS-485) Interface + +pci:v00001093d0000D1D0* + ID_MODEL_FROM_DATABASE=PXI-8420/2 (RS-232) Interface + +pci:v00001093d0000D1E0* + ID_MODEL_FROM_DATABASE=PXI-8420/4 (RS-232) Interface + +pci:v00001093d0000D1F0* + ID_MODEL_FROM_DATABASE=PXI-8420/8 (RS-232) Interface + +pci:v00001093d0000D1F1* + ID_MODEL_FROM_DATABASE=PXI-8420/16 (RS-232) Interface + +pci:v00001093d0000D230* + ID_MODEL_FROM_DATABASE=PXI-8421/2 (RS-485) Interface + +pci:v00001093d0000D240* + ID_MODEL_FROM_DATABASE=PXI-8421/4 (RS-485) Interface + +pci:v00001093d0000D250* + ID_MODEL_FROM_DATABASE=PCI-232/2 (Isolated) Interface + +pci:v00001093d0000D260* + ID_MODEL_FROM_DATABASE=PCI-485/2 (Isolated) Interface + +pci:v00001093d0000D270* + ID_MODEL_FROM_DATABASE=PCI-232/4 (Isolated) Interface + +pci:v00001093d0000D280* + ID_MODEL_FROM_DATABASE=PCI-485/4 (Isolated) Interface + +pci:v00001093d0000D290* + ID_MODEL_FROM_DATABASE=PCI-485/8 Interface + +pci:v00001093d0000D2A0* + ID_MODEL_FROM_DATABASE=PXI-8421/8 (RS-485) Interface + +pci:v00001093d0000D2B0* + ID_MODEL_FROM_DATABASE=PCI-232/16 Interface + +pci:v00001093d0000E111* + ID_MODEL_FROM_DATABASE=PCI-CAN + +pci:v00001093d0000E131* + ID_MODEL_FROM_DATABASE=PXI-8461 (1 port) + +pci:v00001093d0000E141* + ID_MODEL_FROM_DATABASE=PCI-CAN/LS + +pci:v00001093d0000E151* + ID_MODEL_FROM_DATABASE=PXI-8460 (1 port) + +pci:v00001093d0000E211* + ID_MODEL_FROM_DATABASE=PCI-CAN/2 + +pci:v00001093d0000E231* + ID_MODEL_FROM_DATABASE=PXI-8461 (2 ports) + +pci:v00001093d0000E241* + ID_MODEL_FROM_DATABASE=PCI-CAN/LS2 + +pci:v00001093d0000E251* + ID_MODEL_FROM_DATABASE=PXI-8460 (2 ports) + +pci:v00001093d0000E261* + ID_MODEL_FROM_DATABASE=PCI-CAN/DS + +pci:v00001093d0000E271* + ID_MODEL_FROM_DATABASE=PXI-8462 + +pci:v00001093d0000F110* + ID_MODEL_FROM_DATABASE=VMEpc-650 + +pci:v00001093d0000F120* + ID_MODEL_FROM_DATABASE=VXIpc-650 + +pci:v00001093d0000FE00* + ID_MODEL_FROM_DATABASE=VXIpc-87x + +pci:v00001093d0000FE41* + ID_MODEL_FROM_DATABASE=VXIpc-860 + +pci:v00001093d0000FE51* + ID_MODEL_FROM_DATABASE=VXIpc-74x + +pci:v00001093d0000FE61* + ID_MODEL_FROM_DATABASE=VXIpc-850 + +pci:v00001093d0000FE70* + ID_MODEL_FROM_DATABASE=VXIpc-880 + +pci:v00001094* + ID_VENDOR_FROM_DATABASE=First International Computers [FIC] + +pci:v00001095* + ID_VENDOR_FROM_DATABASE=Silicon Image, Inc. + +pci:v00001095d00000240* + ID_MODEL_FROM_DATABASE=Adaptec AAR-1210SA SATA HostRAID Controller + +pci:v00001095d00000640* + ID_MODEL_FROM_DATABASE=PCI0640 + +pci:v00001095d00000643* + ID_MODEL_FROM_DATABASE=PCI0643 + +pci:v00001095d00000646* + ID_MODEL_FROM_DATABASE=PCI0646 + +pci:v00001095d00000647* + ID_MODEL_FROM_DATABASE=PCI0647 + +pci:v00001095d00000648* + ID_MODEL_FROM_DATABASE=PCI0648 + +pci:v00001095d00000648sv00001043sd00008025* + ID_MODEL_FROM_DATABASE=PCI0648 (CUBX motherboard) + +pci:v00001095d00000649* + ID_MODEL_FROM_DATABASE=SiI 0649 Ultra ATA/100 PCI to ATA Host Controller + +pci:v00001095d00000649sv00000E11sd0000005D* + ID_MODEL_FROM_DATABASE=SiI 0649 Ultra ATA/100 PCI to ATA Host Controller (Integrated Ultra ATA-100 Dual Channel Controller) + +pci:v00001095d00000649sv00000E11sd0000007E* + ID_MODEL_FROM_DATABASE=SiI 0649 Ultra ATA/100 PCI to ATA Host Controller (Integrated Ultra ATA-100 IDE RAID Controller) + +pci:v00001095d00000649sv0000101Esd00000649* + ID_MODEL_FROM_DATABASE=SiI 0649 Ultra ATA/100 PCI to ATA Host Controller (AMI MegaRAID IDE 100 Controller) + +pci:v00001095d00000650* + ID_MODEL_FROM_DATABASE=PBC0650A + +pci:v00001095d00000670* + ID_MODEL_FROM_DATABASE=USB0670 + +pci:v00001095d00000670sv00001095sd00000670* + ID_MODEL_FROM_DATABASE=USB0670 + +pci:v00001095d00000673* + ID_MODEL_FROM_DATABASE=USB0673 + +pci:v00001095d00000680* + ID_MODEL_FROM_DATABASE=PCI0680 Ultra ATA-133 Host Controller + +pci:v00001095d00000680sv00001095sd00000680* + ID_MODEL_FROM_DATABASE=PCI0680 Ultra ATA-133 Host Controller (SiI 0680 ATA/133 Controller) + +pci:v00001095d00000680sv00001095sd00003680* + ID_MODEL_FROM_DATABASE=PCI0680 Ultra ATA-133 Host Controller (Winic W-680 (Silicon Image 680 based)) + +pci:v00001095d00003112* + ID_MODEL_FROM_DATABASE=SiI 3112 [SATALink/SATARaid] Serial ATA Controller + +pci:v00001095d00003112sv00001095sd00003112* + ID_MODEL_FROM_DATABASE=SiI 3112 [SATALink/SATARaid] Serial ATA Controller (SiI 3112 SATALink Controller) + +pci:v00001095d00003112sv00001095sd00006112* + ID_MODEL_FROM_DATABASE=SiI 3112 [SATALink/SATARaid] Serial ATA Controller (SiI 3112 SATARaid Controller) + +pci:v00001095d00003112sv00009005sd00000250* + ID_MODEL_FROM_DATABASE=SiI 3112 [SATALink/SATARaid] Serial ATA Controller (SATAConnect 1205SA Host Controller) + +pci:v00001095d00003114* + ID_MODEL_FROM_DATABASE=SiI 3114 [SATALink/SATARaid] Serial ATA Controller + +pci:v00001095d00003114sv00001043sd00008167* + ID_MODEL_FROM_DATABASE=SiI 3114 [SATALink/SATARaid] Serial ATA Controller (A8N-SLI Deluxe/Premium Mainboard) + +pci:v00001095d00003114sv00001095sd00003114* + ID_MODEL_FROM_DATABASE=SiI 3114 [SATALink/SATARaid] Serial ATA Controller (SiI 3114 SATALink Controller) + +pci:v00001095d00003114sv00001095sd00006114* + ID_MODEL_FROM_DATABASE=SiI 3114 [SATALink/SATARaid] Serial ATA Controller (SiI 3114 SATARaid Controller) + +pci:v00001095d00003124* + ID_MODEL_FROM_DATABASE=SiI 3124 PCI-X Serial ATA Controller + +pci:v00001095d00003124sv00001095sd00003124* + ID_MODEL_FROM_DATABASE=SiI 3124 PCI-X Serial ATA Controller + +pci:v00001095d00003132* + ID_MODEL_FROM_DATABASE=SiI 3132 Serial ATA Raid II Controller + +pci:v00001095d00003512* + ID_MODEL_FROM_DATABASE=SiI 3512 [SATALink/SATARaid] Serial ATA Controller + +pci:v00001095d00003512sv00001095sd00003512* + ID_MODEL_FROM_DATABASE=SiI 3512 [SATALink/SATARaid] Serial ATA Controller (SiI 3512 SATALink Controller) + +pci:v00001095d00003512sv00001095sd00006512* + ID_MODEL_FROM_DATABASE=SiI 3512 [SATALink/SATARaid] Serial ATA Controller (SiI 3512 SATARaid Controller) + +pci:v00001095d00003531* + ID_MODEL_FROM_DATABASE=SiI 3531 [SATALink/SATARaid] Serial ATA Controller + +pci:v00001095d00003531sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=SiI 3531 [SATALink/SATARaid] Serial ATA Controller (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00001096* + ID_VENDOR_FROM_DATABASE=Alacron + +pci:v00001097* + ID_VENDOR_FROM_DATABASE=Appian Technology + +pci:v00001098* + ID_VENDOR_FROM_DATABASE=Quantum Designs (H.K.) Ltd + +pci:v00001098d00000001* + ID_MODEL_FROM_DATABASE=QD-8500 + +pci:v00001098d00000002* + ID_MODEL_FROM_DATABASE=QD-8580 + +pci:v00001099* + ID_VENDOR_FROM_DATABASE=Samsung Electronics Co., Ltd + +pci:v0000109A* + ID_VENDOR_FROM_DATABASE=Packard Bell + +pci:v0000109B* + ID_VENDOR_FROM_DATABASE=Gemlight Computer Ltd. + +pci:v0000109C* + ID_VENDOR_FROM_DATABASE=Megachips Corporation + +pci:v0000109D* + ID_VENDOR_FROM_DATABASE=Zida Technologies Ltd. + +pci:v0000109E* + ID_VENDOR_FROM_DATABASE=Brooktree Corporation + +pci:v0000109Ed00000310* + ID_MODEL_FROM_DATABASE=Bt848 Video Capture + +pci:v0000109Ed0000032E* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture + +pci:v0000109Ed00000350* + ID_MODEL_FROM_DATABASE=Bt848 Video Capture + +pci:v0000109Ed00000351* + ID_MODEL_FROM_DATABASE=Bt849A Video capture + +pci:v0000109Ed00000369* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture + +pci:v0000109Ed00000369sv00001002sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (TV-Wonder) + +pci:v0000109Ed00000369sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (TV-Wonder/VE) + +pci:v0000109Ed0000036C* + ID_MODEL_FROM_DATABASE=Bt879(??) Video Capture + +pci:v0000109Ed0000036Csv000013E9sd00000070* + ID_MODEL_FROM_DATABASE=Bt879(??) Video Capture (Win/TV (Video Section)) + +pci:v0000109Ed0000036E* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture + +pci:v0000109Ed0000036Esv00000070sd000013EB* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (WinTV Series) + +pci:v0000109Ed0000036Esv00000070sd0000FF01* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Viewcast Osprey 200) + +pci:v0000109Ed0000036Esv00000071sd00000101* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (DigiTV PCI) + +pci:v0000109Ed0000036Esv0000107Dsd00006606* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (WinFast TV 2000) + +pci:v0000109Ed0000036Esv000011BDsd00000012* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (PCTV pro (TV + FM stereo receiver)) + +pci:v0000109Ed0000036Esv000011BDsd0000001C* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (PCTV Sat (DBC receiver)) + +pci:v0000109Ed0000036Esv0000127Asd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878 Mediastream Controller NTSC) + +pci:v0000109Ed0000036Esv0000127Asd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878 Mediastream Controller PAL BG) + +pci:v0000109Ed0000036Esv0000127Asd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878a Mediastream Controller PAL BG) + +pci:v0000109Ed0000036Esv0000127Asd00000048* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878/832 Mediastream Controller) + +pci:v0000109Ed0000036Esv0000144Fsd00003000* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (MagicTView CPH060 - Video) + +pci:v0000109Ed0000036Esv00001461sd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (TV98 Series (TV/No FM/Remote)) + +pci:v0000109Ed0000036Esv00001461sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (AverMedia UltraTV PCI 350) + +pci:v0000109Ed0000036Esv00001461sd00000004* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (AVerTV WDM Video Capture) + +pci:v0000109Ed0000036Esv00001461sd00000761* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (AverTV DVB-T) + +pci:v0000109Ed0000036Esv00001461sd00000771* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (AverMedia AVerTV DVB-T 771) + +pci:v0000109Ed0000036Esv000014F1sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878 Mediastream Controller NTSC) + +pci:v0000109Ed0000036Esv000014F1sd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878 Mediastream Controller PAL BG) + +pci:v0000109Ed0000036Esv000014F1sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878a Mediastream Controller PAL BG) + +pci:v0000109Ed0000036Esv000014F1sd00000048* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Bt878/832 Mediastream Controller) + +pci:v0000109Ed0000036Esv00001822sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (VisionPlus DVB card) + +pci:v0000109Ed0000036Esv00001851sd00001850* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (FlyVideo'98 - Video) + +pci:v0000109Ed0000036Esv00001851sd00001851* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (FlyVideo II) + +pci:v0000109Ed0000036Esv00001852sd00001852* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (FlyVideo'98 - Video (with FM Tuner)) + +pci:v0000109Ed0000036Esv000018ACsd0000D500* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (DViCO FusionHDTV5 Lite) + +pci:v0000109Ed0000036Esv0000270Fsd0000FC00* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Digitop DTT-1000) + +pci:v0000109Ed0000036Esv0000AA00sd00001460* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardA Input0) + +pci:v0000109Ed0000036Esv0000AA01sd00001461* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardA Input1) + +pci:v0000109Ed0000036Esv0000AA02sd00001462* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardA Input2) + +pci:v0000109Ed0000036Esv0000AA03sd00001463* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardA Input3) + +pci:v0000109Ed0000036Esv0000AA04sd00001464* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardB Input0) + +pci:v0000109Ed0000036Esv0000AA05sd00001465* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardB Input1) + +pci:v0000109Ed0000036Esv0000AA06sd00001466* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardB Input2) + +pci:v0000109Ed0000036Esv0000AA07sd00001467* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardB Input3) + +pci:v0000109Ed0000036Esv0000AA08sd00001468* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardC Input0) + +pci:v0000109Ed0000036Esv0000AA09sd00001469* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardC Input1) + +pci:v0000109Ed0000036Esv0000AA0Asd0000146A* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardC Input2) + +pci:v0000109Ed0000036Esv0000AA0Bsd0000146B* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardC Input3) + +pci:v0000109Ed0000036Esv0000AA0Csd0000146C* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardD Input0) + +pci:v0000109Ed0000036Esv0000AA0Dsd0000146D* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardD Input1) + +pci:v0000109Ed0000036Esv0000AA0Esd0000146E* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardD Input2) + +pci:v0000109Ed0000036Esv0000AA0Fsd0000146F* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (Spectra8 CardD Input3) + +pci:v0000109Ed0000036Esv0000BD11sd00001200* + ID_MODEL_FROM_DATABASE=Bt878 Video Capture (PCTV pro (TV + FM stereo receiver)) + +pci:v0000109Ed0000036F* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture + +pci:v0000109Ed0000036Fsv0000127Asd00000044* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00000122* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL I) + +pci:v0000109Ed0000036Fsv0000127Asd00000144* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00000222* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL BG) + +pci:v0000109Ed0000036Fsv0000127Asd00000244* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00000322* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00000422* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00001122* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL I) + +pci:v0000109Ed0000036Fsv0000127Asd00001222* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL BG) + +pci:v0000109Ed0000036Fsv0000127Asd00001322* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv0000127Asd00001522* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture PAL I) + +pci:v0000109Ed0000036Fsv0000127Asd00001622* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture PAL BG) + +pci:v0000109Ed0000036Fsv0000127Asd00001722* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00000044* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00000122* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL I) + +pci:v0000109Ed0000036Fsv000014F1sd00000144* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00000222* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL BG) + +pci:v0000109Ed0000036Fsv000014F1sd00000244* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00000322* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00000422* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00001122* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL I) + +pci:v0000109Ed0000036Fsv000014F1sd00001222* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (PAL BG) + +pci:v0000109Ed0000036Fsv000014F1sd00001322* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (NTSC) + +pci:v0000109Ed0000036Fsv000014F1sd00001522* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture PAL I) + +pci:v0000109Ed0000036Fsv000014F1sd00001622* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture PAL BG) + +pci:v0000109Ed0000036Fsv000014F1sd00001722* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (Bt879a Video Capture NTSC) + +pci:v0000109Ed0000036Fsv00001851sd00001850* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (FlyVideo'98 - Video) + +pci:v0000109Ed0000036Fsv00001851sd00001851* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (FlyVideo II) + +pci:v0000109Ed0000036Fsv00001852sd00001852* + ID_MODEL_FROM_DATABASE=Bt879 Video Capture (FlyVideo'98 - Video (with FM Tuner)) + +pci:v0000109Ed00000370* + ID_MODEL_FROM_DATABASE=Bt880 Video Capture + +pci:v0000109Ed00000370sv00001851sd00001850* + ID_MODEL_FROM_DATABASE=Bt880 Video Capture (FlyVideo'98) + +pci:v0000109Ed00000370sv00001851sd00001851* + ID_MODEL_FROM_DATABASE=Bt880 Video Capture (FlyVideo'98 EZ - video) + +pci:v0000109Ed00000370sv00001852sd00001852* + ID_MODEL_FROM_DATABASE=Bt880 Video Capture (FlyVideo'98 (with FM Tuner)) + +pci:v0000109Ed00000878* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture + +pci:v0000109Ed00000878sv00000070sd000013EB* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (WinTV Series) + +pci:v0000109Ed00000878sv00000070sd0000FF01* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Viewcast Osprey 200) + +pci:v0000109Ed00000878sv00000071sd00000101* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (DigiTV PCI) + +pci:v0000109Ed00000878sv00001002sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (TV-Wonder) + +pci:v0000109Ed00000878sv00001002sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (TV-Wonder/VE) + +pci:v0000109Ed00000878sv000011BDsd00000012* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (PCTV pro (TV + FM stereo receiver, audio section)) + +pci:v0000109Ed00000878sv000011BDsd0000001C* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (PCTV Sat (DBC receiver)) + +pci:v0000109Ed00000878sv0000127Asd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv0000127Asd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv0000127Asd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv0000127Asd00000048* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv000013E9sd00000070* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Win/TV (Audio Section)) + +pci:v0000109Ed00000878sv0000144Fsd00003000* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (MagicTView CPH060 - Audio) + +pci:v0000109Ed00000878sv00001461sd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Avermedia PCTV98 Audio Capture) + +pci:v0000109Ed00000878sv00001461sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (UltraTV PCI 350) + +pci:v0000109Ed00000878sv00001461sd00000004* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (AVerTV WDM Audio Capture) + +pci:v0000109Ed00000878sv00001461sd00000761* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (AVerTV DVB-T) + +pci:v0000109Ed00000878sv00001461sd00000771* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (AverMedia AVerTV DVB-T 771) + +pci:v0000109Ed00000878sv000014F1sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv000014F1sd00000002* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv000014F1sd00000003* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv000014F1sd00000048* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Bt878 Video Capture (Audio Section)) + +pci:v0000109Ed00000878sv00001822sd00000001* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (VisionPlus DVB Card) + +pci:v0000109Ed00000878sv000018ACsd0000D500* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (DViCO FusionHDTV5 Lite) + +pci:v0000109Ed00000878sv0000270Fsd0000FC00* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (Digitop DTT-1000) + +pci:v0000109Ed00000878sv0000BD11sd00001200* + ID_MODEL_FROM_DATABASE=Bt878 Audio Capture (PCTV pro (TV + FM stereo receiver, audio section)) + +pci:v0000109Ed00000879* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture + +pci:v0000109Ed00000879sv0000127Asd00000044* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000122* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000144* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000222* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000244* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000322* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00000422* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001122* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001222* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001322* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001522* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001622* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv0000127Asd00001722* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000044* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000122* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000144* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000222* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000244* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000322* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00000422* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001122* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001222* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001322* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001522* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001622* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000879sv000014F1sd00001722* + ID_MODEL_FROM_DATABASE=Bt879 Audio Capture (Bt879 Video Capture (Audio Section)) + +pci:v0000109Ed00000880* + ID_MODEL_FROM_DATABASE=Bt880 Audio Capture + +pci:v0000109Ed00002115* + ID_MODEL_FROM_DATABASE=BtV 2115 Mediastream controller + +pci:v0000109Ed00002125* + ID_MODEL_FROM_DATABASE=BtV 2125 Mediastream controller + +pci:v0000109Ed00002164* + ID_MODEL_FROM_DATABASE=BtV 2164 + +pci:v0000109Ed00002165* + ID_MODEL_FROM_DATABASE=BtV 2165 + +pci:v0000109Ed00008230* + ID_MODEL_FROM_DATABASE=Bt8230 ATM Segment/Reassembly Ctrlr (SRC) + +pci:v0000109Ed00008472* + ID_MODEL_FROM_DATABASE=Bt8472 + +pci:v0000109Ed00008474* + ID_MODEL_FROM_DATABASE=Bt8474 + +pci:v0000109F* + ID_VENDOR_FROM_DATABASE=Trigem Computer Inc. + +pci:v000010A0* + ID_VENDOR_FROM_DATABASE=Meidensha Corporation + +pci:v000010A1* + ID_VENDOR_FROM_DATABASE=Juko Electronics Ind. Co. Ltd + +pci:v000010A2* + ID_VENDOR_FROM_DATABASE=Quantum Corporation + +pci:v000010A3* + ID_VENDOR_FROM_DATABASE=Everex Systems Inc + +pci:v000010A4* + ID_VENDOR_FROM_DATABASE=Globe Manufacturing Sales + +pci:v000010A5* + ID_VENDOR_FROM_DATABASE=Smart Link Ltd. + +pci:v000010A5d00003052* + ID_MODEL_FROM_DATABASE=SmartPCI562 56K Modem + +pci:v000010A5d00005449* + ID_MODEL_FROM_DATABASE=SmartPCI561 modem + +pci:v000010A6* + ID_VENDOR_FROM_DATABASE=Informtech Industrial Ltd. + +pci:v000010A7* + ID_VENDOR_FROM_DATABASE=Benchmarq Microelectronics + +pci:v000010A8* + ID_VENDOR_FROM_DATABASE=Sierra Semiconductor + +pci:v000010A8d00000000* + ID_MODEL_FROM_DATABASE=STB Horizon 64 + +pci:v000010A9* + ID_VENDOR_FROM_DATABASE=Silicon Graphics Intl. Corp. + +pci:v000010A9d00000001* + ID_MODEL_FROM_DATABASE=Crosstalk to PCI Bridge + +pci:v000010A9d00000002* + ID_MODEL_FROM_DATABASE=Linc I/O controller + +pci:v000010A9d00000003* + ID_MODEL_FROM_DATABASE=IOC3 I/O controller + +pci:v000010A9d00000004* + ID_MODEL_FROM_DATABASE=O2 MACE + +pci:v000010A9d00000005* + ID_MODEL_FROM_DATABASE=RAD Audio + +pci:v000010A9d00000006* + ID_MODEL_FROM_DATABASE=HPCEX + +pci:v000010A9d00000007* + ID_MODEL_FROM_DATABASE=RPCEX + +pci:v000010A9d00000008* + ID_MODEL_FROM_DATABASE=DiVO VIP + +pci:v000010A9d00000009* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet + +pci:v000010A9d00000009sv000010A9sd00008002* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet + +pci:v000010A9d00000010* + ID_MODEL_FROM_DATABASE=AMP Video I/O + +pci:v000010A9d00000011* + ID_MODEL_FROM_DATABASE=GRIP + +pci:v000010A9d00000012* + ID_MODEL_FROM_DATABASE=SGH PSHAC GSN + +pci:v000010A9d00000208* + ID_MODEL_FROM_DATABASE=SSIM1 SAS Adapter + +pci:v000010A9d00001001* + ID_MODEL_FROM_DATABASE=Magic Carpet + +pci:v000010A9d00001002* + ID_MODEL_FROM_DATABASE=Lithium + +pci:v000010A9d00001003* + ID_MODEL_FROM_DATABASE=Dual JPEG 1 + +pci:v000010A9d00001004* + ID_MODEL_FROM_DATABASE=Dual JPEG 2 + +pci:v000010A9d00001005* + ID_MODEL_FROM_DATABASE=Dual JPEG 3 + +pci:v000010A9d00001006* + ID_MODEL_FROM_DATABASE=Dual JPEG 4 + +pci:v000010A9d00001007* + ID_MODEL_FROM_DATABASE=Dual JPEG 5 + +pci:v000010A9d00001008* + ID_MODEL_FROM_DATABASE=Cesium + +pci:v000010A9d0000100A* + ID_MODEL_FROM_DATABASE=IOC4 I/O controller + +pci:v000010A9d00001504* + ID_MODEL_FROM_DATABASE=SSIM1 Fibre Channel Adapter + +pci:v000010A9d00002001* + ID_MODEL_FROM_DATABASE=Fibre Channel + +pci:v000010A9d00002002* + ID_MODEL_FROM_DATABASE=ASDE + +pci:v000010A9d00004001* + ID_MODEL_FROM_DATABASE=TIO-CE PCI Express Bridge + +pci:v000010A9d00004002* + ID_MODEL_FROM_DATABASE=TIO-CE PCI Express Port + +pci:v000010A9d00008001* + ID_MODEL_FROM_DATABASE=O2 1394 + +pci:v000010A9d00008002* + ID_MODEL_FROM_DATABASE=G-net NT + +pci:v000010A9d0000802B* + ID_MODEL_FROM_DATABASE=REACT external interrupt controller + +pci:v000010AA* + ID_VENDOR_FROM_DATABASE=ACC Microelectronics + +pci:v000010AAd00000000* + ID_MODEL_FROM_DATABASE=ACCM 2188 + +pci:v000010AAd00002051* + ID_MODEL_FROM_DATABASE=2051 CPU bridge + +pci:v000010AAd00005842* + ID_MODEL_FROM_DATABASE=2051 ISA bridge + +pci:v000010AB* + ID_VENDOR_FROM_DATABASE=Digicom + +pci:v000010AC* + ID_VENDOR_FROM_DATABASE=Honeywell IAC + +pci:v000010AD* + ID_VENDOR_FROM_DATABASE=Symphony Labs + +pci:v000010ADd00000001* + ID_MODEL_FROM_DATABASE=W83769F + +pci:v000010ADd00000003* + ID_MODEL_FROM_DATABASE=SL82C103 + +pci:v000010ADd00000005* + ID_MODEL_FROM_DATABASE=SL82C105 + +pci:v000010ADd00000103* + ID_MODEL_FROM_DATABASE=SL82c103 + +pci:v000010ADd00000105* + ID_MODEL_FROM_DATABASE=SL82c105 + +pci:v000010ADd00000565* + ID_MODEL_FROM_DATABASE=W83C553F/W83C554F + +pci:v000010AE* + ID_VENDOR_FROM_DATABASE=Cornerstone Technology + +pci:v000010AF* + ID_VENDOR_FROM_DATABASE=Micro Computer Systems Inc + +pci:v000010B0* + ID_VENDOR_FROM_DATABASE=CardExpert Technology + +pci:v000010B1* + ID_VENDOR_FROM_DATABASE=Cabletron Systems Inc + +pci:v000010B2* + ID_VENDOR_FROM_DATABASE=Raytheon Company + +pci:v000010B3* + ID_VENDOR_FROM_DATABASE=Databook Inc + +pci:v000010B3d00003106* + ID_MODEL_FROM_DATABASE=DB87144 + +pci:v000010B3d0000B106* + ID_MODEL_FROM_DATABASE=DB87144 + +pci:v000010B4* + ID_VENDOR_FROM_DATABASE=STB Systems Inc + +pci:v000010B4d00001B1D* + ID_MODEL_FROM_DATABASE=Velocity 128 3D + +pci:v000010B4d00001B1Dsv000010B4sd0000237E* + ID_MODEL_FROM_DATABASE=Velocity 128 3D (Velocity 4400) + +pci:v000010B5* + ID_VENDOR_FROM_DATABASE=PLX Technology, Inc. + +pci:v000010B5d00000001* + ID_MODEL_FROM_DATABASE=i960 PCI bus interface + +pci:v000010B5d00000557* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00000557sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Digium Tormenta 2 T400P-SS7 or E400P-SS7 Quad T1 or E1 PCI card) + +pci:v000010B5d00001000* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00001000sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (ATCOM AT400P Quad T1 PCI card) + +pci:v000010B5d00001024* + ID_MODEL_FROM_DATABASE=Acromag, Inc. IndustryPack Carrier Card + +pci:v000010B5d00001042* + ID_MODEL_FROM_DATABASE=Brandywine / jxi2, Inc. - PMC-SyncClock32, IRIG A & B, Nasa 36 + +pci:v000010B5d0000106A* + ID_MODEL_FROM_DATABASE=Dual OX16C952 4 port serial adapter [Megawolf Romulus/4] + +pci:v000010B5d00001076* + ID_MODEL_FROM_DATABASE=VScom 800 8 port serial adaptor + +pci:v000010B5d00001077* + ID_MODEL_FROM_DATABASE=VScom 400 4 port serial adaptor + +pci:v000010B5d00001078* + ID_MODEL_FROM_DATABASE=VScom 210 2 port serial and 1 port parallel adaptor + +pci:v000010B5d00001103* + ID_MODEL_FROM_DATABASE=VScom 200 2 port serial adaptor + +pci:v000010B5d00001146* + ID_MODEL_FROM_DATABASE=VScom 010 1 port parallel adaptor + +pci:v000010B5d00001147* + ID_MODEL_FROM_DATABASE=VScom 020 2 port parallel adaptor + +pci:v000010B5d00002000* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00002000sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (ATCOM AE400P Quad E1 PCI card) + +pci:v000010B5d00002540* + ID_MODEL_FROM_DATABASE=IXXAT CAN-Interface PC-I 04/PCI + +pci:v000010B5d00002724* + ID_MODEL_FROM_DATABASE=Thales PCSM Security Card + +pci:v000010B5d00003376* + ID_MODEL_FROM_DATABASE=Cosateq 4 Port CAN Card + +pci:v000010B5d00004000* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00004000sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Tormenta 3 Varion V400P/ATCOM TE400P Quad E1/T1/J1 PCI card) + +pci:v000010B5d00004001* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00004001sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (ATCOM A400PE Quad E1 PCI card) + +pci:v000010B5d00004002* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00004002sv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (ATCOM A400PT Quad T1 PCI card) + +pci:v000010B5d00006140* + ID_MODEL_FROM_DATABASE=PCI6140 32-bit 33MHz PCI-to-PCI Bridge + +pci:v000010B5d00006150* + ID_MODEL_FROM_DATABASE=PCI6150 32-bit 33MHz PCI-to-PCI Bridge + +pci:v000010B5d00006152* + ID_MODEL_FROM_DATABASE=PCI6152 32-bit 66MHz PCI-to-PCI Bridge + +pci:v000010B5d00006154* + ID_MODEL_FROM_DATABASE=PCI6154 64-bit 66MHz PCI-to-PCI Bridge + +pci:v000010B5d00006254* + ID_MODEL_FROM_DATABASE=PCI6254 64-bit 66MHz PCI-to-PCI Bridge + +pci:v000010B5d00006466* + ID_MODEL_FROM_DATABASE=PCI6466 64-bit 66MHz PCI-to-PCI Bridge + +pci:v000010B5d00006520* + ID_MODEL_FROM_DATABASE=PCI6520 64-bit 133MHz PCI-X-to-PCI-X Bridge + +pci:v000010B5d00006540* + ID_MODEL_FROM_DATABASE=PCI6540 64-bit 133MHz PCI-X-to-PCI-X Bridge + +pci:v000010B5d00006540sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=PCI6540 64-bit 133MHz PCI-X-to-PCI-X Bridge (CR11 Single Board Computer) + +pci:v000010B5d00006540sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=PCI6540 64-bit 133MHz PCI-X-to-PCI-X Bridge (PSL09 PrPMC) + +pci:v000010B5d00006541* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, primary side) + +pci:v000010B5d00006541sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, primary side) (CR11 Single Board Computer) + +pci:v000010B5d00006541sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, primary side) (PSL09 PrPMC) + +pci:v000010B5d00006542* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, secondary side) + +pci:v000010B5d00006542sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, secondary side) (CR11 Single Board Computer) + +pci:v000010B5d00006542sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=PCI6540/6466 PCI-PCI bridge (non-transparent mode, secondary side) (PSL09 PrPMC) + +pci:v000010B5d00008111* + ID_MODEL_FROM_DATABASE=PEX 8111 PCI Express-to-PCI Bridge + +pci:v000010B5d00008112* + ID_MODEL_FROM_DATABASE=PEX8112 x1 Lane PCI Express-to-PCI Bridge + +pci:v000010B5d00008114* + ID_MODEL_FROM_DATABASE=PEX 8114 PCI Express-to-PCI/PCI-X Bridge + +pci:v000010B5d00008311* + ID_MODEL_FROM_DATABASE=PEX8311 x1 Lane PCI Express-to-Generic Local Bus Bridge + +pci:v000010B5d00008505* + ID_MODEL_FROM_DATABASE=PEX 8505 5-lane, 5-port PCI Express Switch + +pci:v000010B5d00008508* + ID_MODEL_FROM_DATABASE=PEX 8508 8-lane, 5-port PCI Express Switch + +pci:v000010B5d00008509* + ID_MODEL_FROM_DATABASE=PEX 8509 8-lane, 8-port PCI Express Switch + +pci:v000010B5d00008512* + ID_MODEL_FROM_DATABASE=PEX 8512 12-lane, 5-port PCI Express Switch + +pci:v000010B5d00008516* + ID_MODEL_FROM_DATABASE=PEX 8516 Versatile PCI Express Switch + +pci:v000010B5d00008517* + ID_MODEL_FROM_DATABASE=PEX 8517 16-lane, 5-port PCI Express Switch + +pci:v000010B5d00008518* + ID_MODEL_FROM_DATABASE=PEX 8518 16-lane, 5-port PCI Express Switch + +pci:v000010B5d00008524* + ID_MODEL_FROM_DATABASE=PEX 8524 24-lane, 6-port PCI Express Switch + +pci:v000010B5d00008525* + ID_MODEL_FROM_DATABASE=PEX 8525 24-lane, 5-port PCI Express Switch + +pci:v000010B5d00008532* + ID_MODEL_FROM_DATABASE=PEX 8532 Versatile PCI Express Switch + +pci:v000010B5d00008533* + ID_MODEL_FROM_DATABASE=PEX 8533 32-lane, 6-port PCI Express Switch + +pci:v000010B5d00008547* + ID_MODEL_FROM_DATABASE=PEX 8547 48-lane, 3-port PCI Express Switch + +pci:v000010B5d00008548* + ID_MODEL_FROM_DATABASE=PEX 8548 48-lane, 9-port PCI Express Switch + +pci:v000010B5d00008604* + ID_MODEL_FROM_DATABASE=PEX 8604 4-lane, 4-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008605* + ID_MODEL_FROM_DATABASE=PEX 8605 PCI Express 4-port Gen2 Switch + +pci:v000010B5d00008606* + ID_MODEL_FROM_DATABASE=PEX 8606 6 Lane, 6 Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008608* + ID_MODEL_FROM_DATABASE=PEX 8608 8-lane, 8-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008609* + ID_MODEL_FROM_DATABASE=PEX 8609 8-lane, 8-Port PCI Express Gen 2 (5.0 GT/s) Switch with DMA + +pci:v000010B5d00008612* + ID_MODEL_FROM_DATABASE=PEX 8612 12-lane, 4-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008613* + ID_MODEL_FROM_DATABASE=PEX 8613 12-lane, 3-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008614* + ID_MODEL_FROM_DATABASE=PEX 8614 12-lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008615* + ID_MODEL_FROM_DATABASE=PEX 8615 12-lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch with DMA + +pci:v000010B5d00008616* + ID_MODEL_FROM_DATABASE=PEX 8616 16-lane, 4-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008617* + ID_MODEL_FROM_DATABASE=PEX 8617 16-lane, 4-Port PCI Express Gen 2 (5.0 GT/s) Switch with P2P + +pci:v000010B5d00008618* + ID_MODEL_FROM_DATABASE=PEX 8618 16-lane, 16-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008619* + ID_MODEL_FROM_DATABASE=PEX 8619 16-lane, 16-Port PCI Express Gen 2 (5.0 GT/s) Switch with DMA + +pci:v000010B5d00008624* + ID_MODEL_FROM_DATABASE=PEX 8624 24-lane, 6-Port PCI Express Gen 2 (5.0 GT/s) Switch [ExpressLane] + +pci:v000010B5d00008624sv000013A3sd00001845* + ID_MODEL_FROM_DATABASE=PEX 8624 24-lane, 6-Port PCI Express Gen 2 (5.0 GT/s) Switch [ExpressLane] (DX1845 Acceleration Card) + +pci:v000010B5d00008625* + ID_MODEL_FROM_DATABASE=PEX 8625 24-lane, 24-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008632* + ID_MODEL_FROM_DATABASE=PEX 8632 32-lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008636* + ID_MODEL_FROM_DATABASE=PEX 8636 36-lane, 24-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008647* + ID_MODEL_FROM_DATABASE=PEX 8647 48-Lane, 3-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008648* + ID_MODEL_FROM_DATABASE=PEX 8648 48-lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008649* + ID_MODEL_FROM_DATABASE=PEX 8649 48-lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008664* + ID_MODEL_FROM_DATABASE=PEX 8664 64-lane, 16-Port PCI Express Gen 2 (5.0 GT/s) Switch + +pci:v000010B5d00008680* + ID_MODEL_FROM_DATABASE=PEX 8680 80-lane, 20-Port PCI Express Gen 2 (5.0 GT/s) Multi-Root Switch + +pci:v000010B5d00008696* + ID_MODEL_FROM_DATABASE=PEX 8696 96-lane, 24-Port PCI Express Gen 2 (5.0 GT/s) Multi-Root Switch + +pci:v000010B5d00008717* + ID_MODEL_FROM_DATABASE=PEX 8717 16-lane, 8-Port PCI Express Gen 3 (8.0 GT/s) Switch with DMA + +pci:v000010B5d00008718* + ID_MODEL_FROM_DATABASE=PEX 8718 16-Lane, 5-Port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d00008732* + ID_MODEL_FROM_DATABASE=PEX 8732 32-lane, 8-Port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d00008734* + ID_MODEL_FROM_DATABASE=PEX 8734 32-lane, 8-Port PCI Express Gen 3 (8.0GT/s) Switch + +pci:v000010B5d00008747* + ID_MODEL_FROM_DATABASE=PEX 8747 48-Lane, 5-Port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d000087B0* + ID_MODEL_FROM_DATABASE=PEX 8732 32-lane, 8-Port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d000087B0sv00001093sd00007761* + ID_MODEL_FROM_DATABASE=PEX 8732 32-lane, 8-Port PCI Express Gen 3 (8.0 GT/s) Switch (PXIe-8830mc) + +pci:v000010B5d00009016* + ID_MODEL_FROM_DATABASE=PLX 9016 8-port serial controller + +pci:v000010B5d00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00009030sv000010B5sd00002695* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Hilscher CIF50-PB/DPS Profibus) + +pci:v000010B5d00009030sv000010B5sd00002862* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI LV (3V/5V): Timecode Reader Board) + +pci:v000010B5d00009030sv000010B5sd00002906* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCI TS (3V/5V): Time Synchronisation Board) + +pci:v000010B5d00009030sv000010B5sd00002940* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI D (3V/5V): Timecode Reader Board) + +pci:v000010B5d00009030sv000010B5sd00002977* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (IXXAT iPC-I XC16/PCI CAN Board) + +pci:v000010B5d00009030sv000010B5sd00002978* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (SH ARC-PCIu/SH ARC-PCI104/SH ARC-PCIe SOHARD ARCNET card) + +pci:v000010B5d00009030sv000010B5sd00003025* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI L (3V/5V): Timecode Reader Board) + +pci:v000010B5d00009030sv000010B5sd00003068* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI HD (3V/5V): Timecode Reader Board) + +pci:v000010B5d00009030sv000010B5sd00003463* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI D (v2) (3V/5V): Timecode Reader Board) + +pci:v000010B5d00009030sv000010B5sd00003591* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (PLURA PCL PCI L (v2) (3.3V/5V): Time Code Reader Board) + +pci:v000010B5d00009030sv000012FEsd00000111* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (CPCI-ASIO4 (ESD 4-port Serial Interface Board)) + +pci:v000010B5d00009030sv00001369sd00009C01* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (VX222v2) + +pci:v000010B5d00009030sv00001369sd00009D01* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (VX222-Mic) + +pci:v000010B5d00009030sv00001369sd00009D02* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (VX222-Mic) + +pci:v000010B5d00009030sv00001369sd00009E01* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (PCX924v2) + +pci:v000010B5d00009030sv00001369sd00009F01* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (PCX924-Mic) + +pci:v000010B5d00009030sv00001369sd00009F02* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (PCX924-Mic) + +pci:v000010B5d00009030sv00001369sd0000A001* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (PCX22v2) + +pci:v000010B5d00009030sv00001369sd0000A701* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (LCM220v2) + +pci:v000010B5d00009030sv00001369sd0000A801* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (LCM200) + +pci:v000010B5d00009030sv00001397sd00003136* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (4xS0-ISDN PCI Adapter) + +pci:v000010B5d00009030sv00001397sd00003137* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (S2M-E1-ISDN PCI Adapter) + +pci:v000010B5d00009030sv00001518sd00000200* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (ThinkIO-C) + +pci:v000010B5d00009030sv000015EDsd00001002* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (MCCS 8-port Serial Hot Swap) + +pci:v000010B5d00009030sv000015EDsd00001003* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (MCCS 16-port Serial Hot Swap) + +pci:v000010B5d00009030sv0000E1C5sd00000001* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (TE1-PCI) + +pci:v000010B5d00009030sv0000E1C5sd00000005* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (TA1-PCI) + +pci:v000010B5d00009030sv0000E1C5sd00000006* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (TA1-PCI4) + +pci:v000010B5d00009036* + ID_MODEL_FROM_DATABASE=9036 + +pci:v000010B5d00009050* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge + +pci:v000010B5d00009050sv000010B5sd00001067* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IXXAT CAN i165) + +pci:v000010B5d00009050sv000010B5sd0000114E* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Wasco WITIO PCI168extended) + +pci:v000010B5d00009050sv000010B5sd00001169* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Wasco OPTOIO32standard 32 digital in, 32 digital out) + +pci:v000010B5d00009050sv000010B5sd00001172* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IK220 (Heidenhain)) + +pci:v000010B5d00009050sv000010B5sd00002036* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (SatPak GPS) + +pci:v000010B5d00009050sv000010B5sd00002221* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI LV: Timecode Reader Board) + +pci:v000010B5d00009050sv000010B5sd00002273* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (SH ARC-PCI SOHARD ARCNET card) + +pci:v000010B5d00009050sv000010B5sd00002431* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Alpermann+Velte PCL PCI D: Timecode Reader Board) + +pci:v000010B5d00009050sv000010B5sd00002905* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Alpermann+Velte PCI TS: Time Synchronisation Board) + +pci:v000010B5d00009050sv000010B5sd00003196* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Goramo PLX200SYN sync serial card) + +pci:v000010B5d00009050sv000010B5sd00009050* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (PCI-I04 PCI Passive PC/CAN Interface) + +pci:v000010B5d00009050sv00001369sd00008901* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (PCX11+ PCI) + +pci:v000010B5d00009050sv00001369sd00008F01* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (VX222) + +pci:v000010B5d00009050sv00001369sd00009401* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (PCX924) + +pci:v000010B5d00009050sv00001369sd00009501* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (PCX22) + +pci:v000010B5d00009050sv00001498sd00000362* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (TPMC866 8 Channel Serial Card) + +pci:v000010B5d00009050sv00001522sd00000001* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce 4 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv00001522sd00000002* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce 2 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv00001522sd00000003* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce 6 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv00001522sd00000004* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce 8 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv00001522sd00000010* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce2000 4 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv00001522sd00000020* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce2000 2 Port V.90 Data/Fax/Voice Modem) + +pci:v000010B5d00009050sv000015EDsd00001000* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Macrolink MCCS 8-port Serial) + +pci:v000010B5d00009050sv000015EDsd00001001* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Macrolink MCCS 16-port Serial) + +pci:v000010B5d00009050sv000015EDsd00001002* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Macrolink MCCS 8-port Serial Hot Swap) + +pci:v000010B5d00009050sv000015EDsd00001003* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (Macrolink MCCS 16-port Serial Hot Swap) + +pci:v000010B5d00009050sv00005654sd00002036* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (OpenSwitch 6 Telephony card) + +pci:v000010B5d00009050sv00005654sd00003132* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (OpenSwitch 12 Telephony card) + +pci:v000010B5d00009050sv00005654sd00005634* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (OpenLine4 Telephony Card) + +pci:v000010B5d00009050sv0000D531sd0000C002* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (PCIntelliCAN 2xSJA1000 CAN bus) + +pci:v000010B5d00009050sv0000D84Dsd00004006* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4006 1P) + +pci:v000010B5d00009050sv0000D84Dsd00004008* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4008 1P EPP/ECP) + +pci:v000010B5d00009050sv0000D84Dsd00004014* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4014 2P) + +pci:v000010B5d00009050sv0000D84Dsd00004018* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4018 3P EPP/ECP) + +pci:v000010B5d00009050sv0000D84Dsd00004025* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4025 1S(16C550) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004027* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4027 1S(16C650) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004028* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4028 1S(16C850) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004036* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4036 2S(16C650) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004037* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4037 2S(16C650) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004038* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4038 2S(16C850) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004052* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4052 1S(16C550) RS-422/485) + +pci:v000010B5d00009050sv0000D84Dsd00004053* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4053 2S(16C550) RS-422/485) + +pci:v000010B5d00009050sv0000D84Dsd00004055* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4055 4S(16C550) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004058* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4055 4S(16C650) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004065* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4065 8S(16C550) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004068* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4068 8S(16C650) RS-232) + +pci:v000010B5d00009050sv0000D84Dsd00004078* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (EX-4078 2S(16C552) RS-232+1P) + +pci:v000010B5d00009052* + ID_MODEL_FROM_DATABASE=PCI9052 PCI <-> IOBus Bridge + +pci:v000010B5d00009054* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00009054sv000010B5sd00002455* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Wessex Techology PHIL-PCI) + +pci:v000010B5d00009054sv000010B5sd00002696* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Innes Corp AM Radcap card) + +pci:v000010B5d00009054sv000010B5sd00002717* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Innes Corp Auricon card) + +pci:v000010B5d00009054sv000010B5sd00002844* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Innes Corp TVS Encoder card) + +pci:v000010B5d00009054sv000012C7sd00004001* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Intel Dialogic DM/V960-4T1 PCI) + +pci:v000010B5d00009054sv000012D9sd00000002* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PCI Prosody Card rev 1.5) + +pci:v000010B5d00009054sv000014B4sd0000D100* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Dektec DTA-100) + +pci:v000010B5d00009054sv000014B4sd0000D114* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (Dektec DTA-120) + +pci:v000010B5d00009054sv000016DFsd00000011* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA PrimeNet MM PCI) + +pci:v000010B5d00009054sv000016DFsd00000012* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA PrimeNet MM cPCI 8) + +pci:v000010B5d00009054sv000016DFsd00000013* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA PrimeNet MM cPCI 8 (without CAS Signaling)) + +pci:v000010B5d00009054sv000016DFsd00000014* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA PrimeNet MM cPCI 4) + +pci:v000010B5d00009054sv000016DFsd00000015* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA Daytona MM) + +pci:v000010B5d00009054sv000016DFsd00000016* + ID_MODEL_FROM_DATABASE=PCI9054 32-bit 33MHz PCI <-> IOBus Bridge (PIKA InLine MM) + +pci:v000010B5d00009056* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge + +pci:v000010B5d00009056sv000010B5sd00002979* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (CellinkBlade 11 - CPCI board VoATM AAL1) + +pci:v000010B5d00009056sv000010B5sd00003268* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (IXXAT iPC-I XC16/PCIe CAN Board) + +pci:v000010B5d00009056sv000010B5sd00003352* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCIe HD: Timecode Reader Board) + +pci:v000010B5d00009056sv000010B5sd00003353* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCIe D: Timecode Reader Board) + +pci:v000010B5d00009056sv000010B5sd00003354* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCIe LV: Timecode Reader Board) + +pci:v000010B5d00009056sv000010B5sd00003355* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCIe L: Timecode Reader Board) + +pci:v000010B5d00009056sv000010B5sd00003415* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCIe TS: Time Synchronisation Board) + +pci:v000010B5d00009056sv000010B5sd00003493* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Alpermann+Velte PCL PCIe 3G: Timecode Reader Board) + +pci:v000010B5d00009056sv00001369sd0000C001* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (LX6464ES) + +pci:v000010B5d00009056sv00001369sd0000C201* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (LX1616ES) + +pci:v000010B5d00009056sv000014B4sd0000D10A* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (DekTec DTA-110T) + +pci:v000010B5d00009056sv000014B4sd0000D128* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Dektec DTA-140) + +pci:v000010B5d00009056sv000014B4sd0000D140* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Dektec DTA-140) + +pci:v000010B5d00009056sv00001A0Esd0000006F* + ID_MODEL_FROM_DATABASE=PCI9056 32-bit 66MHz PCI <-> IOBus Bridge (Dektec DTA-111) + +pci:v000010B5d00009060* + ID_MODEL_FROM_DATABASE=PCI9060 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d0000906D* + ID_MODEL_FROM_DATABASE=9060SD + +pci:v000010B5d0000906Dsv0000125Csd00000640* + ID_MODEL_FROM_DATABASE=9060SD (Aries 16000P) + +pci:v000010B5d0000906E* + ID_MODEL_FROM_DATABASE=9060ES + +pci:v000010B5d00009080* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d00009080sv0000103Csd000010EB* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge ((Agilent) E2777B 83K Series Optical Communication Interface) + +pci:v000010B5d00009080sv0000103Csd000010EC* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge ((Agilent) E6978-66442 PCI CIC) + +pci:v000010B5d00009080sv000010B5sd00001123* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (Sectra KK631 encryption board) + +pci:v000010B5d00009080sv000010B5sd00009080* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (9080 [real subsystem ID not set]) + +pci:v000010B5d00009080sv000012D9sd00000002* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (PCI Prosody Card) + +pci:v000010B5d00009080sv000012DFsd00004422* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (4422PCI ["Do-All" Telemetry Data Acquisition System]) + +pci:v000010B5d00009080sv00001369sd00009601* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (PCX822np) + +pci:v000010B5d00009080sv00001369sd0000A102* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (PCX822v2) + +pci:v000010B5d00009080sv00001369sd0000A201* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (PCX442) + +pci:v000010B5d00009080sv00001369sd0000A301* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (LCM440v2) + +pci:v000010B5d00009080sv00001369sd0000A401* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (VX822) + +pci:v000010B5d00009080sv00001369sd0000A402* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (VX822v2) + +pci:v000010B5d00009080sv00001369sd0000A901* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (LCM420) + +pci:v000010B5d00009080sv00001369sd0000AA01* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (VX820v2) + +pci:v000010B5d00009080sv00001517sd0000000B* + ID_MODEL_FROM_DATABASE=PCI9080 32-bit; 33MHz PCI <-> IOBus Bridge (ECSG-1R3ADC-PMC Clock synthesizer) + +pci:v000010B5d00009656* + ID_MODEL_FROM_DATABASE=PCI9656 PCI <-> IOBus Bridge + +pci:v000010B5d00009656sv00001517sd0000000F* + ID_MODEL_FROM_DATABASE=PCI9656 PCI <-> IOBus Bridge (ECDR-GC314-PMC Receiver) + +pci:v000010B5d00009656sv00001885sd00000700* + ID_MODEL_FROM_DATABASE=PCI9656 PCI <-> IOBus Bridge (Tsunami FPGA PMC with Altera Stratix S40) + +pci:v000010B5d00009656sv00001885sd00000701* + ID_MODEL_FROM_DATABASE=PCI9656 PCI <-> IOBus Bridge (Tsunami FPGA PMC with Altera Stratix S30) + +pci:v000010B5d00009733* + ID_MODEL_FROM_DATABASE=PEX 9733 33-lane, 9-port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d00009749* + ID_MODEL_FROM_DATABASE=PEX 9749 49-lane, 13-port PCI Express Gen 3 (8.0 GT/s) Switch + +pci:v000010B5d0000A100* + ID_MODEL_FROM_DATABASE=Blackmagic Design DeckLink + +pci:v000010B5d0000BB04* + ID_MODEL_FROM_DATABASE=B&B 3PCIOSD1A Isolated PCI Serial + +pci:v000010B5d0000C001* + ID_MODEL_FROM_DATABASE=CronyxOmega-PCI (8-port RS232) + +pci:v000010B5d0000D00D* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d0000D00Dsv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Digium Tormenta 2 T400P or E400P Quad T1 or E1 PCI card) + +pci:v000010B5d0000D33D* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d0000D33Dsv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Tormenta 3 Varion V401PT Quad T1/J1 PCI card) + +pci:v000010B5d0000D44D* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge + +pci:v000010B5d0000D44Dsv000010B5sd000017F6* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Allo CP100P/E 1-port E1/T1/J1 PCI/PCIe card) + +pci:v000010B5d0000D44Dsv000010B5sd000017F7* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Allo CP400P/E 4-port E1/T1/J1 PCI/PCIe card) + +pci:v000010B5d0000D44Dsv000010B5sd000017F8* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Allo CP200P/E 2-port E1/T1/J1 PCI/PCIe card) + +pci:v000010B5d0000D44Dsv000010B5sd00009030* + ID_MODEL_FROM_DATABASE=PCI9030 32-bit 33MHz PCI <-> IOBus Bridge (Tormenta 3 Varion V401PE Quad E1 PCI card) + +pci:v000010B6* + ID_VENDOR_FROM_DATABASE=Madge Networks + +pci:v000010B6d00000001* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode + +pci:v000010B6d00000002* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk2 + +pci:v000010B6d00000002sv000010B6sd00000002* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk2 + +pci:v000010B6d00000002sv000010B6sd00000006* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk2 (16/4 CardBus Adapter) + +pci:v000010B6d00000003* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk3 + +pci:v000010B6d00000003sv00000E11sd0000B0FD* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk3 (Compaq NC4621 PCI, 4/16, WOL) + +pci:v000010B6d00000003sv000010B6sd00000003* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk3 + +pci:v000010B6d00000003sv000010B6sd00000007* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk3 (Presto PCI Plus Adapter) + +pci:v000010B6d00000004* + ID_MODEL_FROM_DATABASE=Smart 16/4 PCI Ringnode Mk1 + +pci:v000010B6d00000006* + ID_MODEL_FROM_DATABASE=16/4 Cardbus Adapter + +pci:v000010B6d00000006sv000010B6sd00000006* + ID_MODEL_FROM_DATABASE=16/4 Cardbus Adapter (16/4 CardBus Adapter) + +pci:v000010B6d00000007* + ID_MODEL_FROM_DATABASE=Presto PCI Adapter + +pci:v000010B6d00000007sv000010B6sd00000007* + ID_MODEL_FROM_DATABASE=Presto PCI Adapter (Presto PCI) + +pci:v000010B6d00000009* + ID_MODEL_FROM_DATABASE=Smart 100/16/4 PCI-HS Ringnode + +pci:v000010B6d00000009sv000010B6sd00000009* + ID_MODEL_FROM_DATABASE=Smart 100/16/4 PCI-HS Ringnode + +pci:v000010B6d0000000A* + ID_MODEL_FROM_DATABASE=Token Ring 100/16/4 Ringnode/Ringrunner + +pci:v000010B6d0000000Asv000010B6sd0000000A* + ID_MODEL_FROM_DATABASE=Token Ring 100/16/4 Ringnode/Ringrunner + +pci:v000010B6d0000000B* + ID_MODEL_FROM_DATABASE=16/4 CardBus Adapter Mk2 + +pci:v000010B6d0000000Bsv000010B6sd00000008* + ID_MODEL_FROM_DATABASE=16/4 CardBus Adapter Mk2 + +pci:v000010B6d0000000Bsv000010B6sd0000000B* + ID_MODEL_FROM_DATABASE=16/4 CardBus Adapter Mk2 (16/4 Cardbus Adapter Mk2) + +pci:v000010B6d0000000C* + ID_MODEL_FROM_DATABASE=RapidFire 3140V2 16/4 TR Adapter + +pci:v000010B6d0000000Csv000010B6sd0000000C* + ID_MODEL_FROM_DATABASE=RapidFire 3140V2 16/4 TR Adapter + +pci:v000010B6d00001000* + ID_MODEL_FROM_DATABASE=Collage 25/155 ATM Client Adapter + +pci:v000010B6d00001001* + ID_MODEL_FROM_DATABASE=Collage 155 ATM Server Adapter + +pci:v000010B7* + ID_VENDOR_FROM_DATABASE=3Com Corporation + +pci:v000010B7d00000001* + ID_MODEL_FROM_DATABASE=3c985 1000BaseSX (SX/TX) + +pci:v000010B7d00000001sv00009850sd00000001* + ID_MODEL_FROM_DATABASE=3c985 1000BaseSX (SX/TX) (3c985B-SX) + +pci:v000010B7d00000013* + ID_MODEL_FROM_DATABASE=AR5212 802.11abg NIC (3CRDAG675) + +pci:v000010B7d00000013sv000010B7sd00002031* + ID_MODEL_FROM_DATABASE=AR5212 802.11abg NIC (3CRDAG675) (3CRDAG675 11a/b/g Wireless PCI Adapter) + +pci:v000010B7d00000910* + ID_MODEL_FROM_DATABASE=3C910-A01 + +pci:v000010B7d00001006* + ID_MODEL_FROM_DATABASE=MINI PCI type 3B Data Fax Modem + +pci:v000010B7d00001007* + ID_MODEL_FROM_DATABASE=Mini PCI 56k Winmodem + +pci:v000010B7d00001007sv000010B7sd0000615B* + ID_MODEL_FROM_DATABASE=Mini PCI 56k Winmodem (Mini PCI 56K Modem) + +pci:v000010B7d00001007sv000010B7sd0000615C* + ID_MODEL_FROM_DATABASE=Mini PCI 56k Winmodem (Mini PCI 56K Modem) + +pci:v000010B7d00001201* + ID_MODEL_FROM_DATABASE=3c982-TXM 10/100baseTX Dual Port A [Hydra] + +pci:v000010B7d00001202* + ID_MODEL_FROM_DATABASE=3c982-TXM 10/100baseTX Dual Port B [Hydra] + +pci:v000010B7d00001700* + ID_MODEL_FROM_DATABASE=3c940 10/100/1000Base-T [Marvell] + +pci:v000010B7d00001700sv00001043sd000080EB* + ID_MODEL_FROM_DATABASE=3c940 10/100/1000Base-T [Marvell] (A7V600/P4P800/K8V motherboard) + +pci:v000010B7d00001700sv000010B7sd00000010* + ID_MODEL_FROM_DATABASE=3c940 10/100/1000Base-T [Marvell] (3C940 Gigabit LOM Ethernet Adapter) + +pci:v000010B7d00001700sv000010B7sd00000020* + ID_MODEL_FROM_DATABASE=3c940 10/100/1000Base-T [Marvell] (3C941 Gigabit LOM Ethernet Adapter) + +pci:v000010B7d00001700sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=3c940 10/100/1000Base-T [Marvell] (KV8-MAX3 motherboard) + +pci:v000010B7d00003390* + ID_MODEL_FROM_DATABASE=3c339 TokenLink Velocity + +pci:v000010B7d00003590* + ID_MODEL_FROM_DATABASE=3c359 TokenLink Velocity XL + +pci:v000010B7d00003590sv000010B7sd00003590* + ID_MODEL_FROM_DATABASE=3c359 TokenLink Velocity XL (TokenLink Velocity XL Adapter (3C359/359B)) + +pci:v000010B7d00004500* + ID_MODEL_FROM_DATABASE=3c450 HomePNA [Tornado] + +pci:v000010B7d00005055* + ID_MODEL_FROM_DATABASE=3c555 Laptop Hurricane + +pci:v000010B7d00005057* + ID_MODEL_FROM_DATABASE=3c575 Megahertz 10/100 LAN CardBus [Boomerang] + +pci:v000010B7d00005057sv000010B7sd00005A57* + ID_MODEL_FROM_DATABASE=3c575 Megahertz 10/100 LAN CardBus [Boomerang] (3C575 Megahertz 10/100 LAN Cardbus PC Card) + +pci:v000010B7d00005157* + ID_MODEL_FROM_DATABASE=3cCFE575BT Megahertz 10/100 LAN CardBus [Cyclone] + +pci:v000010B7d00005157sv000010B7sd00005B57* + ID_MODEL_FROM_DATABASE=3cCFE575BT Megahertz 10/100 LAN CardBus [Cyclone] (3C575 Megahertz 10/100 LAN Cardbus PC Card) + +pci:v000010B7d00005257* + ID_MODEL_FROM_DATABASE=3cCFE575CT CardBus [Cyclone] + +pci:v000010B7d00005257sv000010B7sd00005C57* + ID_MODEL_FROM_DATABASE=3cCFE575CT CardBus [Cyclone] (FE575C-3Com 10/100 LAN CardBus-Fast Ethernet) + +pci:v000010B7d00005900* + ID_MODEL_FROM_DATABASE=3c590 10BaseT [Vortex] + +pci:v000010B7d00005920* + ID_MODEL_FROM_DATABASE=3c592 EISA 10mbps Demon/Vortex + +pci:v000010B7d00005950* + ID_MODEL_FROM_DATABASE=3c595 100BaseTX [Vortex] + +pci:v000010B7d00005951* + ID_MODEL_FROM_DATABASE=3c595 100BaseT4 [Vortex] + +pci:v000010B7d00005952* + ID_MODEL_FROM_DATABASE=3c595 100Base-MII [Vortex] + +pci:v000010B7d00005970* + ID_MODEL_FROM_DATABASE=3c597 EISA Fast Demon/Vortex + +pci:v000010B7d00005B57* + ID_MODEL_FROM_DATABASE=3c595 Megahertz 10/100 LAN CardBus [Boomerang] + +pci:v000010B7d00005B57sv000010B7sd00005B57* + ID_MODEL_FROM_DATABASE=3c595 Megahertz 10/100 LAN CardBus [Boomerang] (3C575 Megahertz 10/100 LAN Cardbus PC Card) + +pci:v000010B7d00006000* + ID_MODEL_FROM_DATABASE=3CRSHPW796 [OfficeConnect Wireless CardBus] + +pci:v000010B7d00006001* + ID_MODEL_FROM_DATABASE=3com 3CRWE154G72 [Office Connect Wireless LAN Adapter] + +pci:v000010B7d00006055* + ID_MODEL_FROM_DATABASE=3c556 Hurricane CardBus [Cyclone] + +pci:v000010B7d00006056* + ID_MODEL_FROM_DATABASE=3c556B CardBus [Tornado] + +pci:v000010B7d00006056sv000010B7sd00006556* + ID_MODEL_FROM_DATABASE=3c556B CardBus [Tornado] (10/100 Mini PCI Ethernet Adapter) + +pci:v000010B7d00006560* + ID_MODEL_FROM_DATABASE=3cCFE656 CardBus [Cyclone] + +pci:v000010B7d00006560sv000010B7sd0000656A* + ID_MODEL_FROM_DATABASE=3cCFE656 CardBus [Cyclone] (3CCFEM656 10/100 LAN+56K Modem CardBus) + +pci:v000010B7d00006561* + ID_MODEL_FROM_DATABASE=3cCFEM656 10/100 LAN+56K Modem CardBus + +pci:v000010B7d00006561sv000010B7sd0000656B* + ID_MODEL_FROM_DATABASE=3cCFEM656 10/100 LAN+56K Modem CardBus (3CCFEM656 10/100 LAN+56K Modem CardBus) + +pci:v000010B7d00006562* + ID_MODEL_FROM_DATABASE=3cCFEM656B 10/100 LAN+Winmodem CardBus [Cyclone] + +pci:v000010B7d00006562sv000010B7sd0000656B* + ID_MODEL_FROM_DATABASE=3cCFEM656B 10/100 LAN+Winmodem CardBus [Cyclone] (3CCFEM656B 10/100 LAN+56K Modem CardBus) + +pci:v000010B7d00006563* + ID_MODEL_FROM_DATABASE=3cCFEM656B 10/100 LAN+56K Modem CardBus + +pci:v000010B7d00006563sv000010B7sd0000656B* + ID_MODEL_FROM_DATABASE=3cCFEM656B 10/100 LAN+56K Modem CardBus (3CCFEM656 10/100 LAN+56K Modem CardBus) + +pci:v000010B7d00006564* + ID_MODEL_FROM_DATABASE=3cXFEM656C 10/100 LAN+Winmodem CardBus [Tornado] + +pci:v000010B7d00007646* + ID_MODEL_FROM_DATABASE=3cSOHO100-TX Hurricane + +pci:v000010B7d00007770* + ID_MODEL_FROM_DATABASE=3CRWE777 PCI Wireless Adapter [Airconnect] + +pci:v000010B7d00007940* + ID_MODEL_FROM_DATABASE=3c803 FDDILink UTP Controller + +pci:v000010B7d00007980* + ID_MODEL_FROM_DATABASE=3c804 FDDILink SAS Controller + +pci:v000010B7d00007990* + ID_MODEL_FROM_DATABASE=3c805 FDDILink DAS Controller + +pci:v000010B7d000080EB* + ID_MODEL_FROM_DATABASE=3c940B 10/100/1000Base-T + +pci:v000010B7d00008811* + ID_MODEL_FROM_DATABASE=Token ring + +pci:v000010B7d00009000* + ID_MODEL_FROM_DATABASE=3c900 10BaseT [Boomerang] + +pci:v000010B7d00009001* + ID_MODEL_FROM_DATABASE=3c900 10Mbps Combo [Boomerang] + +pci:v000010B7d00009004* + ID_MODEL_FROM_DATABASE=3c900B-TPO Etherlink XL [Cyclone] + +pci:v000010B7d00009004sv000010B7sd00009004* + ID_MODEL_FROM_DATABASE=3c900B-TPO Etherlink XL [Cyclone] (3C900B-TPO Etherlink XL TPO 10Mb) + +pci:v000010B7d00009005* + ID_MODEL_FROM_DATABASE=3c900B-Combo Etherlink XL [Cyclone] + +pci:v000010B7d00009005sv000010B7sd00009005* + ID_MODEL_FROM_DATABASE=3c900B-Combo Etherlink XL [Cyclone] (3C900B-Combo Etherlink XL Combo) + +pci:v000010B7d00009006* + ID_MODEL_FROM_DATABASE=3c900B-TPC Etherlink XL [Cyclone] + +pci:v000010B7d0000900A* + ID_MODEL_FROM_DATABASE=3c900B-FL 10base-FL [Cyclone] + +pci:v000010B7d00009050* + ID_MODEL_FROM_DATABASE=3c905 100BaseTX [Boomerang] + +pci:v000010B7d00009051* + ID_MODEL_FROM_DATABASE=3c905 100BaseT4 [Boomerang] + +pci:v000010B7d00009054* + ID_MODEL_FROM_DATABASE=3C905B-TX Fast Etherlink XL PCI + +pci:v000010B7d00009054sv000010B7sd00009054* + ID_MODEL_FROM_DATABASE=3C905B-TX Fast Etherlink XL PCI + +pci:v000010B7d00009055* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] + +pci:v000010B7d00009055sv00001028sd00000080* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000081* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000082* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000083* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000084* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000085* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000086* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000087* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000088* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000089* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000090* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000091* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000092* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000093* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000094* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000095* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000096* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000097* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000098* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv00001028sd00000099* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009055sv000010B7sd00009055* + ID_MODEL_FROM_DATABASE=3c905B 100BaseTX [Cyclone] (3C905B Fast Etherlink XL 10/100) + +pci:v000010B7d00009056* + ID_MODEL_FROM_DATABASE=3c905B-T4 Fast EtherLink XL [Cyclone] + +pci:v000010B7d00009058* + ID_MODEL_FROM_DATABASE=3c905B Deluxe Etherlink 10/100/BNC [Cyclone] + +pci:v000010B7d0000905A* + ID_MODEL_FROM_DATABASE=3c905B-FX Fast Etherlink XL FX 100baseFx [Cyclone] + +pci:v000010B7d00009200* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] + +pci:v000010B7d00009200sv00001028sd00000095* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (3C920 Integrated Fast Ethernet Controller) + +pci:v000010B7d00009200sv00001028sd00000097* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (3C920 Integrated Fast Ethernet Controller) + +pci:v000010B7d00009200sv00001028sd000000B4* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (OptiPlex GX110) + +pci:v000010B7d00009200sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (Precision 530) + +pci:v000010B7d00009200sv00001028sd000000FE* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (Optiplex GX240) + +pci:v000010B7d00009200sv00001028sd0000012A* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (3C920 Integrated Fast Ethernet Controller [Latitude C640]) + +pci:v000010B7d00009200sv000010B7sd00001000* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (3C905CX-TX/TX-M Fast Etherlink for PC Management NIC) + +pci:v000010B7d00009200sv000010B7sd00007000* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (10/100 Mini PCI Ethernet Adapter) + +pci:v000010B7d00009200sv000010F1sd00002466* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (Tiger MPX S2466 (3C920 Integrated Fast Ethernet Controller)) + +pci:v000010B7d00009200sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=3c905C-TX/TX-M [Tornado] (X10 Laptop) + +pci:v000010B7d00009201* + ID_MODEL_FROM_DATABASE=3C920B-EMB Integrated Fast Ethernet Controller [Tornado] + +pci:v000010B7d00009201sv00001043sd000080AB* + ID_MODEL_FROM_DATABASE=3C920B-EMB Integrated Fast Ethernet Controller [Tornado] (A7N8X Deluxe onboard 3C920B-EMB Integrated Fast Ethernet Controller) + +pci:v000010B7d00009202* + ID_MODEL_FROM_DATABASE=3Com 3C920B-EMB-WNM Integrated Fast Ethernet Controller + +pci:v000010B7d00009210* + ID_MODEL_FROM_DATABASE=3C920B-EMB-WNM Integrated Fast Ethernet Controller + +pci:v000010B7d00009300* + ID_MODEL_FROM_DATABASE=3CSOHO100B-TX 910-A01 [tulip] + +pci:v000010B7d00009800* + ID_MODEL_FROM_DATABASE=3c980-TX Fast Etherlink XL Server Adapter [Cyclone] + +pci:v000010B7d00009800sv000010B7sd00009800* + ID_MODEL_FROM_DATABASE=3c980-TX Fast Etherlink XL Server Adapter [Cyclone] (3c980-TX Fast Etherlink XL Server Adapter) + +pci:v000010B7d00009805* + ID_MODEL_FROM_DATABASE=3c980-C 10/100baseTX NIC [Python-T] + +pci:v000010B7d00009805sv000010B7sd00001201* + ID_MODEL_FROM_DATABASE=3c980-C 10/100baseTX NIC [Python-T] (EtherLink Server 10/100 Dual Port A) + +pci:v000010B7d00009805sv000010B7sd00001202* + ID_MODEL_FROM_DATABASE=3c980-C 10/100baseTX NIC [Python-T] (EtherLink Server 10/100 Dual Port B) + +pci:v000010B7d00009805sv000010B7sd00009805* + ID_MODEL_FROM_DATABASE=3c980-C 10/100baseTX NIC [Python-T] (3c980 10/100baseTX NIC [Python-T]) + +pci:v000010B7d00009805sv000010F1sd00002462* + ID_MODEL_FROM_DATABASE=3c980-C 10/100baseTX NIC [Python-T] (Thunder K7 S2462) + +pci:v000010B7d00009900* + ID_MODEL_FROM_DATABASE=3C990-TX [Typhoon] + +pci:v000010B7d00009902* + ID_MODEL_FROM_DATABASE=3CR990-TX-95 [Typhoon 56-bit] + +pci:v000010B7d00009903* + ID_MODEL_FROM_DATABASE=3CR990-TX-97 [Typhoon 168-bit] + +pci:v000010B7d00009904* + ID_MODEL_FROM_DATABASE=3C990B-TX-M/3C990BSVR [Typhoon2] + +pci:v000010B7d00009904sv000010B7sd00001000* + ID_MODEL_FROM_DATABASE=3C990B-TX-M/3C990BSVR [Typhoon2] (3CR990B-TX-M [Typhoon2]) + +pci:v000010B7d00009904sv000010B7sd00002000* + ID_MODEL_FROM_DATABASE=3C990B-TX-M/3C990BSVR [Typhoon2] (3CR990BSVR [Typhoon2 Server]) + +pci:v000010B7d00009905* + ID_MODEL_FROM_DATABASE=3CR990-FX-95/97/95 [Typhon Fiber] + +pci:v000010B7d00009905sv000010B7sd00001101* + ID_MODEL_FROM_DATABASE=3CR990-FX-95/97/95 [Typhon Fiber] (3CR990-FX-95 [Typhoon Fiber 56-bit]) + +pci:v000010B7d00009905sv000010B7sd00001102* + ID_MODEL_FROM_DATABASE=3CR990-FX-95/97/95 [Typhon Fiber] (3CR990-FX-97 [Typhoon Fiber 168-bit]) + +pci:v000010B7d00009905sv000010B7sd00002101* + ID_MODEL_FROM_DATABASE=3CR990-FX-95/97/95 [Typhon Fiber] (3CR990-FX-95 Server [Typhoon Fiber 56-bit]) + +pci:v000010B7d00009905sv000010B7sd00002102* + ID_MODEL_FROM_DATABASE=3CR990-FX-95/97/95 [Typhon Fiber] (3CR990-FX-97 Server [Typhoon Fiber 168-bit]) + +pci:v000010B7d00009908* + ID_MODEL_FROM_DATABASE=3CR990SVR95 [Typhoon Server 56-bit] + +pci:v000010B7d00009909* + ID_MODEL_FROM_DATABASE=3CR990SVR97 [Typhoon Server 168-bit] + +pci:v000010B7d0000990A* + ID_MODEL_FROM_DATABASE=3C990SVR [Typhoon Server] + +pci:v000010B7d0000990B* + ID_MODEL_FROM_DATABASE=3C990SVR [Typhoon Server] + +pci:v000010B8* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp [SMC] + +pci:v000010B8d00000005* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter + +pci:v000010B8d00000005sv00001055sd0000E000* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (LANEPIC 10/100 [EVB171Q-PCI]) + +pci:v000010B8d00000005sv00001055sd0000E002* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (LANEPIC 10/100 [EVB171G-PCI]) + +pci:v000010B8d00000005sv000010B8sd0000A011* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (EtherPower II 10/100) + +pci:v000010B8d00000005sv000010B8sd0000A014* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (EtherPower II 10/100) + +pci:v000010B8d00000005sv000010B8sd0000A015* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (EtherPower II 10/100) + +pci:v000010B8d00000005sv000010B8sd0000A016* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (EtherPower II 10/100) + +pci:v000010B8d00000005sv000010B8sd0000A017* + ID_MODEL_FROM_DATABASE=83c170 EPIC/100 Fast Ethernet Adapter (EtherPower II 10/100) + +pci:v000010B8d00000006* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter + +pci:v000010B8d00000006sv00001055sd0000E100* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv00001055sd0000E102* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv00001055sd0000E300* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv00001055sd0000E302* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv000010B8sd0000A012* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv000013A2sd00008002* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00000006sv000013A2sd00008006* + ID_MODEL_FROM_DATABASE=83c175 EPIC/100 Fast Ethernet Adapter (LANEPIC Cardbus Fast Ethernet Adapter) + +pci:v000010B8d00001000* + ID_MODEL_FROM_DATABASE=FDC 37c665 + +pci:v000010B8d00001001* + ID_MODEL_FROM_DATABASE=FDC 37C922 + +pci:v000010B8d0000A011* + ID_MODEL_FROM_DATABASE=83C170QF + +pci:v000010B8d0000B106* + ID_MODEL_FROM_DATABASE=SMC34C90 + +pci:v000010B9* + ID_VENDOR_FROM_DATABASE=ULi Electronics Inc. + +pci:v000010B9d00000101* + ID_MODEL_FROM_DATABASE=CMI8338/C3DX PCI Audio Device + +pci:v000010B9d00000111* + ID_MODEL_FROM_DATABASE=C-Media CMI8738/C3DX Audio Device (OEM) + +pci:v000010B9d00000111sv000010B9sd00000111* + ID_MODEL_FROM_DATABASE=C-Media CMI8738/C3DX Audio Device (OEM) + +pci:v000010B9d00000780* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v000010B9d00000782* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v000010B9d00001435* + ID_MODEL_FROM_DATABASE=M1435 + +pci:v000010B9d00001445* + ID_MODEL_FROM_DATABASE=M1445 + +pci:v000010B9d00001449* + ID_MODEL_FROM_DATABASE=M1449 + +pci:v000010B9d00001451* + ID_MODEL_FROM_DATABASE=M1451 + +pci:v000010B9d00001461* + ID_MODEL_FROM_DATABASE=M1461 + +pci:v000010B9d00001489* + ID_MODEL_FROM_DATABASE=M1489 + +pci:v000010B9d00001511* + ID_MODEL_FROM_DATABASE=M1511 [Aladdin] + +pci:v000010B9d00001512* + ID_MODEL_FROM_DATABASE=M1512 [Aladdin] + +pci:v000010B9d00001513* + ID_MODEL_FROM_DATABASE=M1513 [Aladdin] + +pci:v000010B9d00001521* + ID_MODEL_FROM_DATABASE=M1521 [Aladdin III] + +pci:v000010B9d00001521sv000010B9sd00001521* + ID_MODEL_FROM_DATABASE=M1521 [Aladdin III] (ALI M1521 Aladdin III CPU Bridge) + +pci:v000010B9d00001523* + ID_MODEL_FROM_DATABASE=M1523 + +pci:v000010B9d00001523sv000010B9sd00001523* + ID_MODEL_FROM_DATABASE=M1523 (ALI M1523 ISA Bridge) + +pci:v000010B9d00001531* + ID_MODEL_FROM_DATABASE=M1531 [Aladdin IV] + +pci:v000010B9d00001533* + ID_MODEL_FROM_DATABASE=M1533/M1535/M1543 PCI to ISA Bridge [Aladdin IV/V/V+] + +pci:v000010B9d00001533sv00001014sd0000053B* + ID_MODEL_FROM_DATABASE=M1533/M1535/M1543 PCI to ISA Bridge [Aladdin IV/V/V+] (ThinkPad R40e) + +pci:v000010B9d00001533sv000010B9sd00001533* + ID_MODEL_FROM_DATABASE=M1533/M1535/M1543 PCI to ISA Bridge [Aladdin IV/V/V+] (ALi M1533 Aladdin IV/V ISA Bridge) + +pci:v000010B9d00001541* + ID_MODEL_FROM_DATABASE=M1541 + +pci:v000010B9d00001541sv000010B9sd00001541* + ID_MODEL_FROM_DATABASE=M1541 (ALI M1541 Aladdin V/V+ AGP System Controller) + +pci:v000010B9d00001543* + ID_MODEL_FROM_DATABASE=M1543 + +pci:v000010B9d00001563* + ID_MODEL_FROM_DATABASE=M1563 HyperTransport South Bridge + +pci:v000010B9d00001563sv000010B9sd00001563* + ID_MODEL_FROM_DATABASE=M1563 HyperTransport South Bridge (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00001563sv00001849sd00001563* + ID_MODEL_FROM_DATABASE=M1563 HyperTransport South Bridge (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00001573* + ID_MODEL_FROM_DATABASE=PCI to LPC Controller + +pci:v000010B9d00001575* + ID_MODEL_FROM_DATABASE=M1575 South Bridge + +pci:v000010B9d00001621* + ID_MODEL_FROM_DATABASE=M1621 + +pci:v000010B9d00001631* + ID_MODEL_FROM_DATABASE=ALI M1631 PCI North Bridge Aladdin Pro III + +pci:v000010B9d00001632* + ID_MODEL_FROM_DATABASE=M1632M Northbridge+Trident + +pci:v000010B9d00001641* + ID_MODEL_FROM_DATABASE=ALI M1641 PCI North Bridge Aladdin Pro IV + +pci:v000010B9d00001644* + ID_MODEL_FROM_DATABASE=M1644/M1644T Northbridge+Trident + +pci:v000010B9d00001646* + ID_MODEL_FROM_DATABASE=M1646 Northbridge+Trident + +pci:v000010B9d00001647* + ID_MODEL_FROM_DATABASE=M1647 Northbridge [MAGiK 1 / MobileMAGiK 1] + +pci:v000010B9d00001651* + ID_MODEL_FROM_DATABASE=M1651/M1651T Northbridge [Aladdin-Pro 5/5M,Aladdin-Pro 5T/5TM] + +pci:v000010B9d00001671* + ID_MODEL_FROM_DATABASE=M1671 Super P4 Northbridge [AGP4X,PCI and SDR/DDR] + +pci:v000010B9d00001672* + ID_MODEL_FROM_DATABASE=M1672 Northbridge [CyberALADDiN-P4] + +pci:v000010B9d00001681* + ID_MODEL_FROM_DATABASE=M1681 P4 Northbridge [AGP8X,HyperTransport and SDR/DDR] + +pci:v000010B9d00001687* + ID_MODEL_FROM_DATABASE=M1687 K8 Northbridge [AGP8X and HyperTransport] + +pci:v000010B9d00001689* + ID_MODEL_FROM_DATABASE=M1689 K8 Northbridge [Super K8 Single Chip] + +pci:v000010B9d00001695* + ID_MODEL_FROM_DATABASE=M1695 Host Bridge + +pci:v000010B9d00001697* + ID_MODEL_FROM_DATABASE=M1697 HTT Host Bridge + +pci:v000010B9d00003141* + ID_MODEL_FROM_DATABASE=M3141 + +pci:v000010B9d00003143* + ID_MODEL_FROM_DATABASE=M3143 + +pci:v000010B9d00003145* + ID_MODEL_FROM_DATABASE=M3145 + +pci:v000010B9d00003147* + ID_MODEL_FROM_DATABASE=M3147 + +pci:v000010B9d00003149* + ID_MODEL_FROM_DATABASE=M3149 + +pci:v000010B9d00003151* + ID_MODEL_FROM_DATABASE=M3151 + +pci:v000010B9d00003307* + ID_MODEL_FROM_DATABASE=M3307 + +pci:v000010B9d00003309* + ID_MODEL_FROM_DATABASE=M3309 + +pci:v000010B9d00003323* + ID_MODEL_FROM_DATABASE=M3325 Video/Audio Decoder + +pci:v000010B9d00005212* + ID_MODEL_FROM_DATABASE=M4803 + +pci:v000010B9d00005215* + ID_MODEL_FROM_DATABASE=MS4803 + +pci:v000010B9d00005217* + ID_MODEL_FROM_DATABASE=M5217H + +pci:v000010B9d00005219* + ID_MODEL_FROM_DATABASE=M5219 + +pci:v000010B9d00005225* + ID_MODEL_FROM_DATABASE=M5225 + +pci:v000010B9d00005228* + ID_MODEL_FROM_DATABASE=M5228 ALi ATA/RAID Controller + +pci:v000010B9d00005229* + ID_MODEL_FROM_DATABASE=M5229 IDE + +pci:v000010B9d00005229sv00001014sd0000050F* + ID_MODEL_FROM_DATABASE=M5229 IDE (ThinkPad R30) + +pci:v000010B9d00005229sv00001014sd0000053D* + ID_MODEL_FROM_DATABASE=M5229 IDE (ThinkPad R40e) + +pci:v000010B9d00005229sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=M5229 IDE (Pavilion ze4400 builtin IDE) + +pci:v000010B9d00005229sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=M5229 IDE (XE4500 Notebook) + +pci:v000010B9d00005229sv00001043sd00008053* + ID_MODEL_FROM_DATABASE=M5229 IDE (A7A266 Motherboard IDE) + +pci:v000010B9d00005229sv00001849sd00005229* + ID_MODEL_FROM_DATABASE=M5229 IDE (ASRock 939Dual-SATA2 Motherboard IDE (PATA)) + +pci:v000010B9d00005235* + ID_MODEL_FROM_DATABASE=M5225 + +pci:v000010B9d00005237* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller + +pci:v000010B9d00005237sv00001014sd00000540* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (ThinkPad R40e) + +pci:v000010B9d00005237sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (Pavilion ze4400 builtin USB) + +pci:v000010B9d00005237sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (XE4500 Notebook) + +pci:v000010B9d00005237sv0000104Dsd0000810F* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (VAIO PCG-U1 USB/OHCI Revision 1.0) + +pci:v000010B9d00005237sv000010B9sd00005237* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005237sv00001849sd00005237* + ID_MODEL_FROM_DATABASE=USB 1.1 Controller (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005239* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller + +pci:v000010B9d00005239sv000010B9sd00005239* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005239sv00001849sd00005239* + ID_MODEL_FROM_DATABASE=USB 2.0 Controller (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005243* + ID_MODEL_FROM_DATABASE=M1541 PCI to AGP Controller + +pci:v000010B9d00005246* + ID_MODEL_FROM_DATABASE=AGP8X Controller + +pci:v000010B9d00005247* + ID_MODEL_FROM_DATABASE=PCI to AGP Controller + +pci:v000010B9d00005249* + ID_MODEL_FROM_DATABASE=M5249 HTT to PCI Bridge + +pci:v000010B9d0000524B* + ID_MODEL_FROM_DATABASE=PCI Express Root Port + +pci:v000010B9d0000524C* + ID_MODEL_FROM_DATABASE=PCI Express Root Port + +pci:v000010B9d0000524D* + ID_MODEL_FROM_DATABASE=PCI Express Root Port + +pci:v000010B9d0000524E* + ID_MODEL_FROM_DATABASE=PCI Express Root Port + +pci:v000010B9d00005251* + ID_MODEL_FROM_DATABASE=M5251 P1394 OHCI 1.0 Controller + +pci:v000010B9d00005253* + ID_MODEL_FROM_DATABASE=M5253 P1394 OHCI 1.1 Controller + +pci:v000010B9d00005261* + ID_MODEL_FROM_DATABASE=M5261 Ethernet Controller + +pci:v000010B9d00005263* + ID_MODEL_FROM_DATABASE=ULi 1689,1573 integrated ethernet. + +pci:v000010B9d00005281* + ID_MODEL_FROM_DATABASE=ALi M5281 Serial ATA / RAID Host Controller + +pci:v000010B9d00005287* + ID_MODEL_FROM_DATABASE=ULi 5287 SATA + +pci:v000010B9d00005288* + ID_MODEL_FROM_DATABASE=ULi M5288 SATA + +pci:v000010B9d00005288sv00001043sd00008056* + ID_MODEL_FROM_DATABASE=ULi M5288 SATA (A8R-MVP Mainboard) + +pci:v000010B9d00005289* + ID_MODEL_FROM_DATABASE=ULi 5289 SATA + +pci:v000010B9d00005450* + ID_MODEL_FROM_DATABASE=Lucent Technologies Soft Modem AMR + +pci:v000010B9d00005451* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device + +pci:v000010B9d00005451sv00001014sd00000506* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device (ThinkPad R30) + +pci:v000010B9d00005451sv00001014sd0000053E* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device (ThinkPad R40e) + +pci:v000010B9d00005451sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device (Pavilion ze4400 builtin Audio) + +pci:v000010B9d00005451sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=M5451 PCI AC-Link Controller Audio Device (XE4500 Notebook) + +pci:v000010B9d00005453* + ID_MODEL_FROM_DATABASE=M5453 PCI AC-Link Controller Modem Device + +pci:v000010B9d00005455* + ID_MODEL_FROM_DATABASE=M5455 PCI AC-Link Controller Audio Device + +pci:v000010B9d00005455sv000010B9sd00005455* + ID_MODEL_FROM_DATABASE=M5455 PCI AC-Link Controller Audio Device (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005455sv00001849sd00000850* + ID_MODEL_FROM_DATABASE=M5455 PCI AC-Link Controller Audio Device (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010B9d00005457* + ID_MODEL_FROM_DATABASE=M5457 AC'97 Modem Controller + +pci:v000010B9d00005457sv00001014sd00000535* + ID_MODEL_FROM_DATABASE=M5457 AC'97 Modem Controller (ThinkPad R40e) + +pci:v000010B9d00005457sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=M5457 AC'97 Modem Controller (Pavilion ze4400 builtin Modem Device) + +pci:v000010B9d00005457sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=M5457 AC'97 Modem Controller (XE4500 Notebook) + +pci:v000010B9d00005459* + ID_MODEL_FROM_DATABASE=SmartLink SmartPCI561 56K Modem + +pci:v000010B9d0000545A* + ID_MODEL_FROM_DATABASE=SmartLink SmartPCI563 56K Modem + +pci:v000010B9d00005461* + ID_MODEL_FROM_DATABASE=HD Audio Controller + +pci:v000010B9d00005471* + ID_MODEL_FROM_DATABASE=M5471 Memory Stick Controller + +pci:v000010B9d00005473* + ID_MODEL_FROM_DATABASE=M5473 SD-MMC Controller + +pci:v000010B9d00007101* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] + +pci:v000010B9d00007101sv00001014sd00000510* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] (ThinkPad R30) + +pci:v000010B9d00007101sv00001014sd0000053C* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] (ThinkPad R40e) + +pci:v000010B9d00007101sv0000103Csd00000024* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] (Pavilion ze4400) + +pci:v000010B9d00007101sv0000103Csd00000025* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] (XE4500 Notebook) + +pci:v000010B9d00007101sv00001849sd00007101* + ID_MODEL_FROM_DATABASE=M7101 Power Management Controller [PMU] (ASRock 939Dual-SATA2 Motherboard) + +pci:v000010BA* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Corp. + +pci:v000010BAd00000301* + ID_MODEL_FROM_DATABASE=AccelGraphics AccelECLIPSE + +pci:v000010BAd00000304* + ID_MODEL_FROM_DATABASE=AccelGALAXY A2100 [OEM Evans & Sutherland] + +pci:v000010BAd00000308* + ID_MODEL_FROM_DATABASE=Tornado 3000 [OEM Evans & Sutherland] + +pci:v000010BAd00000308sv000010DDsd00000024* + ID_MODEL_FROM_DATABASE=Tornado 3000 [OEM Evans & Sutherland] (Tornado 3000) + +pci:v000010BAd00001002* + ID_MODEL_FROM_DATABASE=VG500 [VolumePro Volume Rendering Accelerator] + +pci:v000010BB* + ID_VENDOR_FROM_DATABASE=Dapha Electronics Corporation + +pci:v000010BC* + ID_VENDOR_FROM_DATABASE=Advanced Logic Research + +pci:v000010BD* + ID_VENDOR_FROM_DATABASE=Surecom Technology + +pci:v000010BDd00000E34* + ID_MODEL_FROM_DATABASE=NE-34 + +pci:v000010BE* + ID_VENDOR_FROM_DATABASE=Tseng Labs International Co. + +pci:v000010BF* + ID_VENDOR_FROM_DATABASE=Most Inc + +pci:v000010C0* + ID_VENDOR_FROM_DATABASE=Boca Research Inc. + +pci:v000010C1* + ID_VENDOR_FROM_DATABASE=ICM Co., Ltd. + +pci:v000010C2* + ID_VENDOR_FROM_DATABASE=Auspex Systems Inc. + +pci:v000010C3* + ID_VENDOR_FROM_DATABASE=Samsung Semiconductors, Inc. + +pci:v000010C4* + ID_VENDOR_FROM_DATABASE=Award Software International Inc. + +pci:v000010C5* + ID_VENDOR_FROM_DATABASE=Xerox Corporation + +pci:v000010C6* + ID_VENDOR_FROM_DATABASE=Rambus Inc. + +pci:v000010C7* + ID_VENDOR_FROM_DATABASE=Media Vision + +pci:v000010C8* + ID_VENDOR_FROM_DATABASE=Neomagic Corporation + +pci:v000010C8d00000001* + ID_MODEL_FROM_DATABASE=NM2070 [MagicGraph 128] + +pci:v000010C8d00000002* + ID_MODEL_FROM_DATABASE=NM2090 [MagicGraph 128V] + +pci:v000010C8d00000003* + ID_MODEL_FROM_DATABASE=NM2093 [MagicGraph 128ZV] + +pci:v000010C8d00000004* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] + +pci:v000010C8d00000004sv00001014sd000000BA* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001025sd00001007* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001028sd00000074* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001028sd00000075* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001028sd0000007D* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001028sd0000007E* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv00001033sd0000802F* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv0000104Dsd0000801B* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv0000104Dsd0000802F* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv0000104Dsd0000830B* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010BAsd00000E00* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010C8sd00000004* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010CFsd00001029* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010F7sd00008308* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010F7sd00008309* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010F7sd0000830B* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010F7sd0000830D* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000004sv000010F7sd00008312* + ID_MODEL_FROM_DATABASE=NM2160 [MagicGraph 128XD] (MagicGraph 128XD) + +pci:v000010C8d00000005* + ID_MODEL_FROM_DATABASE=NM2200 [MagicGraph 256AV] + +pci:v000010C8d00000005sv00001014sd000000DD* + ID_MODEL_FROM_DATABASE=NM2200 [MagicGraph 256AV] (ThinkPad 570) + +pci:v000010C8d00000005sv00001028sd00000088* + ID_MODEL_FROM_DATABASE=NM2200 [MagicGraph 256AV] (Latitude CPi A) + +pci:v000010C8d00000006* + ID_MODEL_FROM_DATABASE=NM2360 [MagicMedia 256ZX] + +pci:v000010C8d00000006sv00001014sd00000152* + ID_MODEL_FROM_DATABASE=NM2360 [MagicMedia 256ZX] (ThinkPad 600X) + +pci:v000010C8d00000016* + ID_MODEL_FROM_DATABASE=NM2380 [MagicMedia 256XL+] + +pci:v000010C8d00000016sv000010C8sd00000016* + ID_MODEL_FROM_DATABASE=NM2380 [MagicMedia 256XL+] (MagicMedia 256XL+) + +pci:v000010C8d00000025* + ID_MODEL_FROM_DATABASE=NM2230 [MagicGraph 256AV+] + +pci:v000010C8d00000083* + ID_MODEL_FROM_DATABASE=NM2093 [MagicGraph 128ZV+] + +pci:v000010C8d00008005* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] + +pci:v000010C8d00008005sv00000E11sd0000B0D1* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Discovery) + +pci:v000010C8d00008005sv00000E11sd0000B126* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Durango) + +pci:v000010C8d00008005sv00001014sd000000DD* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (ThinkPad 390/i1720/i1721) + +pci:v000010C8d00008005sv00001025sd00001003* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on TravelMate 720) + +pci:v000010C8d00008005sv00001028sd00000088* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (Latitude CPi A) + +pci:v000010C8d00008005sv00001028sd0000008F* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Colorado Inspiron) + +pci:v000010C8d00008005sv0000103Csd00000007* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Voyager II) + +pci:v000010C8d00008005sv0000103Csd00000008* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Voyager III) + +pci:v000010C8d00008005sv0000103Csd0000000D* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on Omnibook 900) + +pci:v000010C8d00008005sv000010C8sd00008005* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device on FireAnt) + +pci:v000010C8d00008005sv0000110Asd00008005* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device) + +pci:v000010C8d00008005sv000014C0sd00000004* + ID_MODEL_FROM_DATABASE=NM2200 [MagicMedia 256AV Audio] (MagicMedia 256AV Audio Device) + +pci:v000010C8d00008006* + ID_MODEL_FROM_DATABASE=NM2360 [MagicMedia 256ZX Audio] + +pci:v000010C8d00008016* + ID_MODEL_FROM_DATABASE=NM2380 [MagicMedia 256XL+ Audio] + +pci:v000010C9* + ID_VENDOR_FROM_DATABASE=Dataexpert Corporation + +pci:v000010CA* + ID_VENDOR_FROM_DATABASE=Fujitsu Microelectr., Inc. + +pci:v000010CB* + ID_VENDOR_FROM_DATABASE=Omron Corporation + +pci:v000010CC* + ID_VENDOR_FROM_DATABASE=Mai Logic Incorporated + +pci:v000010CCd00000660* + ID_MODEL_FROM_DATABASE=Articia S Host Bridge + +pci:v000010CCd00000661* + ID_MODEL_FROM_DATABASE=Articia S PCI Bridge + +pci:v000010CD* + ID_VENDOR_FROM_DATABASE=Advanced System Products, Inc + +pci:v000010CDd00001100* + ID_MODEL_FROM_DATABASE=ASC1100 + +pci:v000010CDd00001200* + ID_MODEL_FROM_DATABASE=ASC1200 [(abp940) Fast SCSI-II] + +pci:v000010CDd00001300* + ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925] + +pci:v000010CDd00001300sv000010CDsd00001310* + ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925] (ASC1300/3030 SCSI adapter) + +pci:v000010CDd00001300sv00001195sd00001320* + ID_MODEL_FROM_DATABASE=ASC1300 / ASC3030 [ABP940-U / ABP960-U / ABP3925] (Ultra-SCSI CardBus PC Card REX CB31) + +pci:v000010CDd00002300* + ID_MODEL_FROM_DATABASE=ABP940-UW + +pci:v000010CDd00002500* + ID_MODEL_FROM_DATABASE=ABP940-U2W + +pci:v000010CDd00002700* + ID_MODEL_FROM_DATABASE=ABP3950-U3W + +pci:v000010CE* + ID_VENDOR_FROM_DATABASE=Radius + +pci:v000010CF* + ID_VENDOR_FROM_DATABASE=Fujitsu Limited. + +pci:v000010CFd000001EF* + ID_MODEL_FROM_DATABASE=PCEA4 PCI-Express Dual Port ESCON Adapter + +pci:v000010CFd00001414* + ID_MODEL_FROM_DATABASE=On-board USB 1.1 companion controller + +pci:v000010CFd00001415* + ID_MODEL_FROM_DATABASE=On-board USB 2.0 EHCI controller + +pci:v000010CFd00001422* + ID_MODEL_FROM_DATABASE=E8410 nVidia graphics adapter + +pci:v000010CFd0000142D* + ID_MODEL_FROM_DATABASE=HD audio (Realtek ALC262) + +pci:v000010CFd00001430* + ID_MODEL_FROM_DATABASE=82566MM Intel 1Gb copper LAN interface + +pci:v000010CFd00001623* + ID_MODEL_FROM_DATABASE=PCEA4 PCI-Express Dual Port ESCON Adapter + +pci:v000010CFd00002001* + ID_MODEL_FROM_DATABASE=mb86605 + +pci:v000010CFd0000200C* + ID_MODEL_FROM_DATABASE=MB86613L IEEE1394 OHCI 1.0 Controller + +pci:v000010CFd00002010* + ID_MODEL_FROM_DATABASE=MB86613S IEEE1394 OHCI 1.1 Controller + +pci:v000010CFd00002019* + ID_MODEL_FROM_DATABASE=MB86295S [CORAL P] + +pci:v000010CFd0000201E* + ID_MODEL_FROM_DATABASE=MB86296S [CORAL PA] + +pci:v000010CFd0000202B* + ID_MODEL_FROM_DATABASE=MB86297A [Carmine Graphics Controller] + +pci:v000010D1* + ID_VENDOR_FROM_DATABASE=FuturePlus Systems Corp. + +pci:v000010D2* + ID_VENDOR_FROM_DATABASE=Molex Incorporated + +pci:v000010D3* + ID_VENDOR_FROM_DATABASE=Jabil Circuit Inc + +pci:v000010D4* + ID_VENDOR_FROM_DATABASE=Hualon Microelectronics + +pci:v000010D5* + ID_VENDOR_FROM_DATABASE=Autologic Inc. + +pci:v000010D6* + ID_VENDOR_FROM_DATABASE=Cetia + +pci:v000010D7* + ID_VENDOR_FROM_DATABASE=BCM Advanced Research + +pci:v000010D8* + ID_VENDOR_FROM_DATABASE=Advanced Peripherals Labs + +pci:v000010D9* + ID_VENDOR_FROM_DATABASE=Macronix, Inc. [MXIC] + +pci:v000010D9d00000431* + ID_MODEL_FROM_DATABASE=MX98715 + +pci:v000010D9d00000512* + ID_MODEL_FROM_DATABASE=MX98713 + +pci:v000010D9d00000531* + ID_MODEL_FROM_DATABASE=MX987x5 + +pci:v000010D9d00000531sv00001186sd00001200* + ID_MODEL_FROM_DATABASE=MX987x5 (DFE-540TX ProFAST 10/100 Adapter) + +pci:v000010D9d00008625* + ID_MODEL_FROM_DATABASE=MX86250 + +pci:v000010D9d00008626* + ID_MODEL_FROM_DATABASE=Macronix MX86251 + 3Dfx Voodoo Rush + +pci:v000010D9d00008888* + ID_MODEL_FROM_DATABASE=MX86200 + +pci:v000010DA* + ID_VENDOR_FROM_DATABASE=Compaq IPG-Austin + +pci:v000010DAd00000508* + ID_MODEL_FROM_DATABASE=TC4048 Token Ring 4/16 + +pci:v000010DAd00003390* + ID_MODEL_FROM_DATABASE=Tl3c3x9 + +pci:v000010DB* + ID_VENDOR_FROM_DATABASE=Rohm LSI Systems, Inc. + +pci:v000010DC* + ID_VENDOR_FROM_DATABASE=CERN/ECP/EDU + +pci:v000010DCd00000001* + ID_MODEL_FROM_DATABASE=STAR/RD24 SCI-PCI (PMC) + +pci:v000010DCd00000002* + ID_MODEL_FROM_DATABASE=TAR/RD24 SCI-PCI (PMC) + +pci:v000010DCd00000021* + ID_MODEL_FROM_DATABASE=HIPPI destination + +pci:v000010DCd00000022* + ID_MODEL_FROM_DATABASE=HIPPI source + +pci:v000010DCd000010DC* + ID_MODEL_FROM_DATABASE=ATT2C15-3 FPGA + +pci:v000010DD* + ID_VENDOR_FROM_DATABASE=Evans & Sutherland + +pci:v000010DDd00000100* + ID_MODEL_FROM_DATABASE=Lightning 1200 + +pci:v000010DDd00000100sv000010DDsd00000023* + ID_MODEL_FROM_DATABASE=Lightning 1200 (15+16M) + +pci:v000010DE* + ID_VENDOR_FROM_DATABASE=NVIDIA Corporation + +pci:v000010DEd00000008* + ID_MODEL_FROM_DATABASE=NV1 [EDGE 3D] + +pci:v000010DEd00000009* + ID_MODEL_FROM_DATABASE=NV1 [EDGE 3D] + +pci:v000010DEd00000020* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] + +pci:v000010DEd00000020sv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (V3400 TNT) + +pci:v000010DEd00000020sv00001048sd00000C18* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Erazor II SGRAM) + +pci:v000010DEd00000020sv00001048sd00000C19* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Erazor II) + +pci:v000010DEd00000020sv00001048sd00000C1B* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Erazor II) + +pci:v000010DEd00000020sv00001048sd00000C1C* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Erazor II) + +pci:v000010DEd00000020sv00001092sd00000550* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00000552* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004804* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004808* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004810* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004812* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004815* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004820* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550 with TV out) + +pci:v000010DEd00000020sv00001092sd00004822* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004904* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00004914* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv00001092sd00008225* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Viper V550) + +pci:v000010DEd00000020sv000010B4sd0000273D* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Velocity 4400) + +pci:v000010DEd00000020sv000010B4sd0000273E* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Velocity 4400) + +pci:v000010DEd00000020sv000010B4sd00002740* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Velocity 4400) + +pci:v000010DEd00000020sv000010DEsd00000020* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Riva TNT) + +pci:v000010DEd00000020sv00001102sd00001015* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Graphics Blaster CT6710) + +pci:v000010DEd00000020sv00001102sd00001016* + ID_MODEL_FROM_DATABASE=NV4 [Riva TNT] (Graphics Blaster RIVA TNT) + +pci:v000010DEd00000028* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] + +pci:v000010DEd00000028sv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (AGP-V3800 SGRAM) + +pci:v000010DEd00000028sv00001043sd00000201* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (AGP-V3800 SDRAM) + +pci:v000010DEd00000028sv00001043sd00000205* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (PCI-V3800) + +pci:v000010DEd00000028sv00001043sd00004000* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (AGP-V3800PRO) + +pci:v000010DEd00000028sv00001048sd00000C21* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Synergy II) + +pci:v000010DEd00000028sv00001048sd00000C28* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III) + +pci:v000010DEd00000028sv00001048sd00000C29* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III) + +pci:v000010DEd00000028sv00001048sd00000C2A* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III) + +pci:v000010DEd00000028sv00001048sd00000C2B* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III) + +pci:v000010DEd00000028sv00001048sd00000C31* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III Pro) + +pci:v000010DEd00000028sv00001048sd00000C32* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III Pro) + +pci:v000010DEd00000028sv00001048sd00000C33* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III Pro) + +pci:v000010DEd00000028sv00001048sd00000C34* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Erazor III Pro) + +pci:v000010DEd00000028sv0000107Dsd00002134* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (WinFast 3D S320 II + TV-Out) + +pci:v000010DEd00000028sv00001092sd00004804* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770) + +pci:v000010DEd00000028sv00001092sd00004A00* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770) + +pci:v000010DEd00000028sv00001092sd00004A02* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770 Ultra) + +pci:v000010DEd00000028sv00001092sd00005A00* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (RIVA TNT2/TNT2 Pro) + +pci:v000010DEd00000028sv00001092sd00005A40* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770D AGP) + +pci:v000010DEd00000028sv00001092sd00006A02* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770 Ultra) + +pci:v000010DEd00000028sv00001092sd00007A02* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Viper V770 Ultra) + +pci:v000010DEd00000028sv000010DEsd00000005* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (RIVA TNT2 Pro) + +pci:v000010DEd00000028sv000010DEsd0000000F* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Compaq NVIDIA TNT2 Pro) + +pci:v000010DEd00000028sv00001102sd00001020* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (3D Blaster RIVA TNT2) + +pci:v000010DEd00000028sv00001102sd00001026* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (3D Blaster RIVA TNT2 Digital) + +pci:v000010DEd00000028sv00001462sd00008806* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (MS-8806 AGPhantom Graphics Card) + +pci:v000010DEd00000028sv000014AFsd00005810* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 / TNT2 Pro] (Maxi Gamer Xentor) + +pci:v000010DEd00000029* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] + +pci:v000010DEd00000029sv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (AGP-V3800 Deluxe) + +pci:v000010DEd00000029sv00001043sd00000201* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (AGP-V3800 Ultra SDRAM) + +pci:v000010DEd00000029sv00001043sd00000205* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (PCI-V3800 Ultra) + +pci:v000010DEd00000029sv00001048sd00000C2E* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (Erazor III Ultra) + +pci:v000010DEd00000029sv00001048sd00000C2F* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (Erazor III Ultra) + +pci:v000010DEd00000029sv00001048sd00000C30* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (Erazor III Ultra) + +pci:v000010DEd00000029sv00001102sd00001021* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (3D Blaster RIVA TNT2 Ultra) + +pci:v000010DEd00000029sv00001102sd00001029* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (3D Blaster RIVA TNT2 Ultra) + +pci:v000010DEd00000029sv00001102sd0000102F* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (3D Blaster RIVA TNT2 Ultra) + +pci:v000010DEd00000029sv000014AFsd00005820* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (Maxi Gamer Xentor 32) + +pci:v000010DEd00000029sv00004843sd00004F34* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Ultra] (Dynamite) + +pci:v000010DEd0000002A* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2] + +pci:v000010DEd0000002B* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2] + +pci:v000010DEd0000002C* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] + +pci:v000010DEd0000002Csv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (AGP-V3800 Combat SDRAM) + +pci:v000010DEd0000002Csv00001043sd00000201* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (AGP-V3800 Combat) + +pci:v000010DEd0000002Csv00001048sd00000C20* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (TNT2 Vanta) + +pci:v000010DEd0000002Csv00001048sd00000C21* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (TNT2 Vanta) + +pci:v000010DEd0000002Csv00001048sd00000C25* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (TNT2 Vanta 16MB) + +pci:v000010DEd0000002Csv00001092sd00006820* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (Viper V730) + +pci:v000010DEd0000002Csv00001102sd00001031* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (CT6938 VANTA 8MB) + +pci:v000010DEd0000002Csv00001102sd00001034* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (CT6894 VANTA 16MB) + +pci:v000010DEd0000002Csv000014AFsd00005008* + ID_MODEL_FROM_DATABASE=NV5 [Vanta / Vanta LT] (Maxi Gamer Phoenix 2) + +pci:v000010DEd0000002D* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] + +pci:v000010DEd0000002Dsv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (AGP-V3800M) + +pci:v000010DEd0000002Dsv00001043sd00000201* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (AGP-V3800M) + +pci:v000010DEd0000002Dsv00001048sd00000C3A* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (Erazor III LT) + +pci:v000010DEd0000002Dsv00001048sd00000C3B* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (Erazor III LT) + +pci:v000010DEd0000002Dsv0000107Dsd00002137* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (WinFast 3D S325) + +pci:v000010DEd0000002Dsv000010DEsd00000006* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (RIVA TNT2 Model 64/Model 64 Pro) + +pci:v000010DEd0000002Dsv000010DEsd0000001E* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (M64 AGP4x) + +pci:v000010DEd0000002Dsv00001102sd00001023* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (CT6892 RIVA TNT2 Value) + +pci:v000010DEd0000002Dsv00001102sd00001024* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (CT6932 RIVA TNT2 Value 32Mb) + +pci:v000010DEd0000002Dsv00001102sd0000102C* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (CT6931 RIVA TNT2 Value [Jumper]) + +pci:v000010DEd0000002Dsv00001102sd00001030* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (CT6931 RIVA TNT2 Value) + +pci:v000010DEd0000002Dsv0000110Asd0000006F* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (GM1000-16) + +pci:v000010DEd0000002Dsv0000110Asd00000081* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (GM1000-16) + +pci:v000010DEd0000002Dsv00001462sd00008808* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (MSI-8808) + +pci:v000010DEd0000002Dsv000014AFsd00005620* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (Gamer Cougar Video Edition) + +pci:v000010DEd0000002Dsv00001554sd00001041* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (Pixelview RIVA TNT2 M64) + +pci:v000010DEd0000002Dsv00001569sd0000002D* + ID_MODEL_FROM_DATABASE=NV5 [Riva TNT2 Model 64 / Model 64 Pro] (Palit Microsystems Daytona TNT2 M64) + +pci:v000010DEd00000034* + ID_MODEL_FROM_DATABASE=MCP04 SMBus + +pci:v000010DEd00000035* + ID_MODEL_FROM_DATABASE=MCP04 IDE + +pci:v000010DEd00000036* + ID_MODEL_FROM_DATABASE=MCP04 Serial ATA Controller + +pci:v000010DEd00000037* + ID_MODEL_FROM_DATABASE=MCP04 Ethernet Controller + +pci:v000010DEd00000038* + ID_MODEL_FROM_DATABASE=MCP04 Ethernet Controller + +pci:v000010DEd0000003A* + ID_MODEL_FROM_DATABASE=MCP04 AC'97 Audio Controller + +pci:v000010DEd0000003B* + ID_MODEL_FROM_DATABASE=MCP04 USB Controller + +pci:v000010DEd0000003C* + ID_MODEL_FROM_DATABASE=MCP04 USB Controller + +pci:v000010DEd0000003D* + ID_MODEL_FROM_DATABASE=MCP04 PCI Bridge + +pci:v000010DEd0000003E* + ID_MODEL_FROM_DATABASE=MCP04 Serial ATA Controller + +pci:v000010DEd00000040* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 Ultra] + +pci:v000010DEd00000041* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800] + +pci:v000010DEd00000041sv00001043sd0000817B* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800] (V9999 Gamer Edition) + +pci:v000010DEd00000041sv0000107Dsd00002992* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800] (WinFast A400) + +pci:v000010DEd00000041sv00001458sd0000310F* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800] (Geforce 6800 GV-N6812) + +pci:v000010DEd00000042* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 LE] + +pci:v000010DEd00000042sv0000107Dsd0000299B* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 LE] (WinFast A400 LE) + +pci:v000010DEd00000043* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 XE] + +pci:v000010DEd00000044* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 XT] + +pci:v000010DEd00000045* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT] + +pci:v000010DEd00000045sv00001043sd0000817D* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT] (V9999GT) + +pci:v000010DEd00000045sv00001458sd00003140* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT] (GV-N68T256D) + +pci:v000010DEd00000047* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GS] + +pci:v000010DEd00000047sv00001682sd00002109* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GS] (GeForce 6800 GS) + +pci:v000010DEd00000048* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 XT] + +pci:v000010DEd0000004E* + ID_MODEL_FROM_DATABASE=NV40GL [Quadro FX 4000] + +pci:v000010DEd00000050* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge + +pci:v000010DEd00000050sv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000050sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (Tomcat K8E (S2865)) + +pci:v000010DEd00000050sv00001458sd00000C11* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000050sv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (MSI K8N Diamond) + +pci:v000010DEd00000050sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (K8N Neo4-F mainboard) + +pci:v000010DEd00000050sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (KN8-Ultra Mainboard) + +pci:v000010DEd00000050sv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (NF4 AM2L Mainboard) + +pci:v000010DEd00000051* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge + +pci:v000010DEd00000051sv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 ISA Bridge (PowerEdge T105 ISA Bridge) + +pci:v000010DEd00000052* + ID_MODEL_FROM_DATABASE=CK804 SMBus + +pci:v000010DEd00000052sv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 SMBus (PowerEdge T105 SMBus) + +pci:v000010DEd00000052sv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 SMBus (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000052sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 SMBus (Tomcat K8E (S2865)) + +pci:v000010DEd00000052sv00001458sd00000C11* + ID_MODEL_FROM_DATABASE=CK804 SMBus (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000052sv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 SMBus (MSI K8N Diamond) + +pci:v000010DEd00000052sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 SMBus (K8N Neo4-F mainboard) + +pci:v000010DEd00000052sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 SMBus (KN8-Ultra Mainboard) + +pci:v000010DEd00000052sv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 SMBus (NF4 AM2L Mainboard) + +pci:v000010DEd00000053* + ID_MODEL_FROM_DATABASE=CK804 IDE + +pci:v000010DEd00000053sv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 IDE (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000053sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 IDE (Tomcat K8E (S2865)) + +pci:v000010DEd00000053sv00001458sd00005002* + ID_MODEL_FROM_DATABASE=CK804 IDE (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000053sv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 IDE (MSI K8N Diamond) + +pci:v000010DEd00000053sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 IDE (K8N Neo4-F mainboard) + +pci:v000010DEd00000053sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 IDE (KN8-Ultra Mainboard) + +pci:v000010DEd00000053sv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 IDE (NF4 AM2L Mainboard) + +pci:v000010DEd00000054* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller + +pci:v000010DEd00000054sv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (PowerEdge T105 Serial ATA) + +pci:v000010DEd00000054sv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (A8N Series Mainboard) + +pci:v000010DEd00000054sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (Tomcat K8E (S2865)) + +pci:v000010DEd00000054sv00001458sd0000B003* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000054sv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (MSI K8N Diamond) + +pci:v000010DEd00000054sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (K8N Neo4-F mainboard) + +pci:v000010DEd00000054sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (KN8-Ultra Mainboard) + +pci:v000010DEd00000054sv00001565sd00005401* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (NF4 AM2L Mainboard) + +pci:v000010DEd00000055* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller + +pci:v000010DEd00000055sv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (PowerEdge T105 Serial ATA) + +pci:v000010DEd00000055sv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000055sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (Tomcat K8E (S2865)) + +pci:v000010DEd00000055sv00001458sd0000B003* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000055sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (K8N Neo4-F mainboard) + +pci:v000010DEd00000055sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (KN8-Ultra Mainboard) + +pci:v000010DEd00000055sv00001565sd00005401* + ID_MODEL_FROM_DATABASE=CK804 Serial ATA Controller (NF4 AM2L Mainboard) + +pci:v000010DEd00000056* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller + +pci:v000010DEd00000057* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller + +pci:v000010DEd00000057sv00001043sd00008141* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000057sv000010DEsd0000CB84* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (NF4 Lanparty) + +pci:v000010DEd00000057sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (Tomcat K8E (S2865)) + +pci:v000010DEd00000057sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd00000057sv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (MSI K8N Diamond) + +pci:v000010DEd00000057sv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (K8N Neo4-F mainboard) + +pci:v000010DEd00000057sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (KN8-Ultra Mainboard) + +pci:v000010DEd00000057sv00001565sd00002501* + ID_MODEL_FROM_DATABASE=CK804 Ethernet Controller (NF4 AM2L Mainboard) + +pci:v000010DEd00000058* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Modem + +pci:v000010DEd00000059* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller + +pci:v000010DEd00000059sv00001043sd0000812A* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller (K8N4/A8N Series Mainboard) + +pci:v000010DEd00000059sv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller (Tomcat K8E (S2865)) + +pci:v000010DEd00000059sv00001462sd00007585* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller (K8N Neo4-F mainboard) + +pci:v000010DEd00000059sv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller (KN8-Ultra Mainboard) + +pci:v000010DEd00000059sv00001565sd00008211* + ID_MODEL_FROM_DATABASE=CK804 AC'97 Audio Controller (NF4 AM2L Mainboard) + +pci:v000010DEd0000005A* + ID_MODEL_FROM_DATABASE=CK804 USB Controller + +pci:v000010DEd0000005Asv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (PowerEdge T105 onboard USB) + +pci:v000010DEd0000005Asv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (K8N4/A8N Series Mainboard) + +pci:v000010DEd0000005Asv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (Tomcat K8E (S2865)) + +pci:v000010DEd0000005Asv00001458sd00005004* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd0000005Asv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (MSI K8N Diamond) + +pci:v000010DEd0000005Asv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (K8N Neo4-F mainboard) + +pci:v000010DEd0000005Asv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (KN8-Ultra Mainboard) + +pci:v000010DEd0000005Asv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (NF4 AM2L Mainboard) + +pci:v000010DEd0000005B* + ID_MODEL_FROM_DATABASE=CK804 USB Controller + +pci:v000010DEd0000005Bsv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (PowerEdge T105 onboard USB) + +pci:v000010DEd0000005Bsv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (K8N4/A8N Series Mainboard) + +pci:v000010DEd0000005Bsv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (Tomcat K8E (S2865)) + +pci:v000010DEd0000005Bsv00001458sd00005004* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd0000005Bsv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (MSI K8N Diamond) + +pci:v000010DEd0000005Bsv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (K8N Neo4-F mainboard) + +pci:v000010DEd0000005Bsv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (KN8-Ultra Mainboard) + +pci:v000010DEd0000005Bsv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 USB Controller (NF4 AM2L Mainboard) + +pci:v000010DEd0000005C* + ID_MODEL_FROM_DATABASE=CK804 PCI Bridge + +pci:v000010DEd0000005D* + ID_MODEL_FROM_DATABASE=CK804 PCIE Bridge + +pci:v000010DEd0000005E* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller + +pci:v000010DEd0000005Esv00001028sd00000225* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (PowerEdge T105 Memory Controller) + +pci:v000010DEd0000005Esv00001043sd0000815A* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (A8N Series Mainboard) + +pci:v000010DEd0000005Esv000010DEsd0000005E* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (ECS Elitegroup NFORCE3-A939 motherboard.) + +pci:v000010DEd0000005Esv000010F1sd00002865* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (Tomcat K8E (S2865)) + +pci:v000010DEd0000005Esv000010F1sd00002891* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (Thunder K8SRE Mainboard) + +pci:v000010DEd0000005Esv00001458sd00005000* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (GA-K8N Ultra-9 Mainboard) + +pci:v000010DEd0000005Esv00001462sd00007100* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (K8N Diamond Mainboard) + +pci:v000010DEd0000005Esv00001462sd00007125* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (K8N Neo4-F Mainboard) + +pci:v000010DEd0000005Esv0000147Bsd00001C1A* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (KN8-Ultra Mainboard) + +pci:v000010DEd0000005Esv00001565sd00003402* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller (NF4 AM2L Mainboard) + +pci:v000010DEd0000005F* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller + +pci:v000010DEd00000060* + ID_MODEL_FROM_DATABASE=nForce2 ISA Bridge + +pci:v000010DEd00000060sv00001043sd000080AD* + ID_MODEL_FROM_DATABASE=nForce2 ISA Bridge (A7N8X Mainboard) + +pci:v000010DEd00000060sv0000147Bsd00001C02* + ID_MODEL_FROM_DATABASE=nForce2 ISA Bridge (NF7-S/NF7 (nVidia-nForce2) 2.X) + +pci:v000010DEd00000060sv0000A0A0sd000003BA* + ID_MODEL_FROM_DATABASE=nForce2 ISA Bridge (UK79G-1394 motherboard) + +pci:v000010DEd00000064* + ID_MODEL_FROM_DATABASE=nForce2 SMBus (MCP) + +pci:v000010DEd00000064sv0000147Bsd00001C02* + ID_MODEL_FROM_DATABASE=nForce2 SMBus (MCP) (NF7-S/NF7 (nVidia-nForce2) 2.X) + +pci:v000010DEd00000064sv0000A0A0sd000003BB* + ID_MODEL_FROM_DATABASE=nForce2 SMBus (MCP) (UK79G-1394 motherboard) + +pci:v000010DEd00000065* + ID_MODEL_FROM_DATABASE=nForce2 IDE + +pci:v000010DEd00000065sv000010DEsd00000C11* + ID_MODEL_FROM_DATABASE=nForce2 IDE (nForce 2 EIDE Controller) + +pci:v000010DEd00000065sv0000A0A0sd000003B2* + ID_MODEL_FROM_DATABASE=nForce2 IDE (UK79G-1394 motherboard) + +pci:v000010DEd00000066* + ID_MODEL_FROM_DATABASE=nForce2 Ethernet Controller + +pci:v000010DEd00000066sv00001043sd000080A7* + ID_MODEL_FROM_DATABASE=nForce2 Ethernet Controller (A7N8X Mainboard onboard nForce2 Ethernet) + +pci:v000010DEd00000066sv000010DEsd00000C11* + ID_MODEL_FROM_DATABASE=nForce2 Ethernet Controller (nForce MCP-T Networking Adapter) + +pci:v000010DEd00000066sv0000A0A0sd000003B3* + ID_MODEL_FROM_DATABASE=nForce2 Ethernet Controller (UK79G-1394 motherboard) + +pci:v000010DEd00000067* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller + +pci:v000010DEd00000067sv00001043sd00000C11* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller (A7N8X Mainboard) + +pci:v000010DEd00000067sv0000A0A0sd000003B4* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller (UK79G-1394 motherboard) + +pci:v000010DEd00000068* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller + +pci:v000010DEd00000068sv00001043sd00000C11* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller (A7N8X Mainboard) + +pci:v000010DEd00000068sv0000A0A0sd000003B4* + ID_MODEL_FROM_DATABASE=nForce2 USB Controller (UK79G-1394 motherboard) + +pci:v000010DEd0000006A* + ID_MODEL_FROM_DATABASE=nForce2 AC97 Audio Controler (MCP) + +pci:v000010DEd0000006Asv00001043sd00008095* + ID_MODEL_FROM_DATABASE=nForce2 AC97 Audio Controler (MCP) (nForce2 AC97 Audio Controller (MCP)) + +pci:v000010DEd0000006Asv0000A0A0sd00000304* + ID_MODEL_FROM_DATABASE=nForce2 AC97 Audio Controler (MCP) (UK79G-1394 motherboard) + +pci:v000010DEd0000006B* + ID_MODEL_FROM_DATABASE=nForce Audio Processing Unit + +pci:v000010DEd0000006Bsv000010DEsd0000006B* + ID_MODEL_FROM_DATABASE=nForce Audio Processing Unit (nForce2 MCP Audio Processing Unit) + +pci:v000010DEd0000006Bsv0000A0A0sd00000304* + ID_MODEL_FROM_DATABASE=nForce Audio Processing Unit (UK79G-1394 motherboard) + +pci:v000010DEd0000006C* + ID_MODEL_FROM_DATABASE=nForce2 External PCI Bridge + +pci:v000010DEd0000006D* + ID_MODEL_FROM_DATABASE=nForce2 PCI Bridge + +pci:v000010DEd0000006E* + ID_MODEL_FROM_DATABASE=nForce2 FireWire (IEEE 1394) Controller + +pci:v000010DEd0000006Esv0000A0A0sd00000306* + ID_MODEL_FROM_DATABASE=nForce2 FireWire (IEEE 1394) Controller (UK79G-1394 motherboard) + +pci:v000010DEd00000080* + ID_MODEL_FROM_DATABASE=MCP2A ISA bridge + +pci:v000010DEd00000080sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2A ISA bridge (NV7 Motherboard) + +pci:v000010DEd00000084* + ID_MODEL_FROM_DATABASE=MCP2A SMBus + +pci:v000010DEd00000084sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2A SMBus (NV7 Motherboard) + +pci:v000010DEd00000085* + ID_MODEL_FROM_DATABASE=MCP2A IDE + +pci:v000010DEd00000085sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2A IDE (NV7 Motherboard) + +pci:v000010DEd00000086* + ID_MODEL_FROM_DATABASE=MCP2A Ethernet Controller + +pci:v000010DEd00000087* + ID_MODEL_FROM_DATABASE=MCP2A USB Controller + +pci:v000010DEd00000087sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2A USB Controller (NV7 Motherboard) + +pci:v000010DEd00000088* + ID_MODEL_FROM_DATABASE=MCP2A USB Controller + +pci:v000010DEd00000088sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2A USB Controller (NV7 Motherboard) + +pci:v000010DEd0000008A* + ID_MODEL_FROM_DATABASE=MCP2S AC'97 Audio Controller + +pci:v000010DEd0000008Asv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=MCP2S AC'97 Audio Controller (NV7 Motherboard) + +pci:v000010DEd0000008B* + ID_MODEL_FROM_DATABASE=MCP2A PCI Bridge + +pci:v000010DEd0000008C* + ID_MODEL_FROM_DATABASE=MCP2A Ethernet Controller + +pci:v000010DEd0000008E* + ID_MODEL_FROM_DATABASE=nForce2 Serial ATA Controller + +pci:v000010DEd00000090* + ID_MODEL_FROM_DATABASE=G70 [GeForce 7800 GTX] + +pci:v000010DEd00000091* + ID_MODEL_FROM_DATABASE=G70 [GeForce 7800 GTX] + +pci:v000010DEd00000092* + ID_MODEL_FROM_DATABASE=G70 [GeForce 7800 GT] + +pci:v000010DEd00000093* + ID_MODEL_FROM_DATABASE=G70 [GeForce 7800 GS] + +pci:v000010DEd00000095* + ID_MODEL_FROM_DATABASE=G70 [GeForce 7800 SLI] + +pci:v000010DEd00000097* + ID_MODEL_FROM_DATABASE=G70 [GeForce GTS 250] + +pci:v000010DEd00000098* + ID_MODEL_FROM_DATABASE=G70M [GeForce Go 7800] + +pci:v000010DEd00000099* + ID_MODEL_FROM_DATABASE=G70M [GeForce Go 7800 GTX] + +pci:v000010DEd0000009D* + ID_MODEL_FROM_DATABASE=G70GL [Quadro FX 4500] + +pci:v000010DEd000000A0* + ID_MODEL_FROM_DATABASE=NV5 [Aladdin TNT2] + +pci:v000010DEd000000A0sv000014AFsd00005810* + ID_MODEL_FROM_DATABASE=NV5 [Aladdin TNT2] (Maxi Gamer Xentor) + +pci:v000010DEd000000C0* + ID_MODEL_FROM_DATABASE=NV41 [GeForce 6800 GS] + +pci:v000010DEd000000C1* + ID_MODEL_FROM_DATABASE=NV41 [GeForce 6800] + +pci:v000010DEd000000C2* + ID_MODEL_FROM_DATABASE=NV41 [GeForce 6800 LE] + +pci:v000010DEd000000C3* + ID_MODEL_FROM_DATABASE=NV41 [GeForce 6800 XT] + +pci:v000010DEd000000C5* + ID_MODEL_FROM_DATABASE=NV41 + +pci:v000010DEd000000C6* + ID_MODEL_FROM_DATABASE=NV41 + +pci:v000010DEd000000C7* + ID_MODEL_FROM_DATABASE=NV41 + +pci:v000010DEd000000C8* + ID_MODEL_FROM_DATABASE=NV41M [GeForce Go 6800] + +pci:v000010DEd000000C9* + ID_MODEL_FROM_DATABASE=NV41M [GeForce Go 6800 Ultra] + +pci:v000010DEd000000CC* + ID_MODEL_FROM_DATABASE=NV41GLM [Quadro FX Go1400] + +pci:v000010DEd000000CD* + ID_MODEL_FROM_DATABASE=NV42GL [Quadro FX 3450/4000 SDI] + +pci:v000010DEd000000CDsv000010DEsd0000029B* + ID_MODEL_FROM_DATABASE=NV42GL [Quadro FX 3450/4000 SDI] (Quadro FX 3450) + +pci:v000010DEd000000CE* + ID_MODEL_FROM_DATABASE=NV41GL [Quadro FX 1400] + +pci:v000010DEd000000CF* + ID_MODEL_FROM_DATABASE=NV41 + +pci:v000010DEd000000D0* + ID_MODEL_FROM_DATABASE=nForce3 LPC Bridge + +pci:v000010DEd000000D1* + ID_MODEL_FROM_DATABASE=nForce3 Host Bridge + +pci:v000010DEd000000D2* + ID_MODEL_FROM_DATABASE=nForce3 AGP Bridge + +pci:v000010DEd000000D3* + ID_MODEL_FROM_DATABASE=CK804 Memory Controller + +pci:v000010DEd000000D4* + ID_MODEL_FROM_DATABASE=nForce3 SMBus + +pci:v000010DEd000000D5* + ID_MODEL_FROM_DATABASE=nForce3 IDE + +pci:v000010DEd000000D6* + ID_MODEL_FROM_DATABASE=nForce3 Ethernet + +pci:v000010DEd000000D7* + ID_MODEL_FROM_DATABASE=nForce3 USB 1.1 + +pci:v000010DEd000000D8* + ID_MODEL_FROM_DATABASE=nForce3 USB 2.0 + +pci:v000010DEd000000D9* + ID_MODEL_FROM_DATABASE=nForce3 Audio + +pci:v000010DEd000000DA* + ID_MODEL_FROM_DATABASE=nForce3 Audio + +pci:v000010DEd000000DD* + ID_MODEL_FROM_DATABASE=nForce3 PCI Bridge + +pci:v000010DEd000000DF* + ID_MODEL_FROM_DATABASE=CK8S Ethernet Controller + +pci:v000010DEd000000DFsv00001043sd000080A7* + ID_MODEL_FROM_DATABASE=CK8S Ethernet Controller (K8N-E) + +pci:v000010DEd000000DFsv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=CK8S Ethernet Controller (Winfast NF3250K8AA) + +pci:v000010DEd000000DFsv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=CK8S Ethernet Controller (NF8 Mainboard) + +pci:v000010DEd000000E0* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge + +pci:v000010DEd000000E0sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge (K8N-E) + +pci:v000010DEd000000E0sv000010DEsd00000C11* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge (Winfast NF3250K8AA) + +pci:v000010DEd000000E0sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E0sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge (NF8 Mainboard) + +pci:v000010DEd000000E0sv00001849sd000000E0* + ID_MODEL_FROM_DATABASE=nForce3 250Gb LPC Bridge (Motherboard (one of many)) + +pci:v000010DEd000000E1* + ID_MODEL_FROM_DATABASE=nForce3 250Gb Host Bridge + +pci:v000010DEd000000E1sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=nForce3 250Gb Host Bridge (K8N-E) + +pci:v000010DEd000000E1sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=nForce3 250Gb Host Bridge (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E1sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce3 250Gb Host Bridge (NF8 Mainboard) + +pci:v000010DEd000000E1sv00001849sd000000E1* + ID_MODEL_FROM_DATABASE=nForce3 250Gb Host Bridge (Motherboard (one of many)) + +pci:v000010DEd000000E2* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AGP Host to PCI Bridge + +pci:v000010DEd000000E3* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller + +pci:v000010DEd000000E3sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller (K8N-E) + +pci:v000010DEd000000E3sv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller (Winfast NF3250K8AA) + +pci:v000010DEd000000E3sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller (NF8 Mainboard) + +pci:v000010DEd000000E3sv00001849sd000000E3* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller (Motherboard (one of many)) + +pci:v000010DEd000000E4* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management + +pci:v000010DEd000000E4sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management (K8N-E) + +pci:v000010DEd000000E4sv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management (Winfast NF3250K8AA) + +pci:v000010DEd000000E4sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E4sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management (NF8 Mainboard) + +pci:v000010DEd000000E4sv00001849sd000000E4* + ID_MODEL_FROM_DATABASE=nForce 250Gb PCI System Management (Motherboard (one of many)) + +pci:v000010DEd000000E5* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) + +pci:v000010DEd000000E5sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (K8N-E) + +pci:v000010DEd000000E5sv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (Winfast NF3250K8AA) + +pci:v000010DEd000000E5sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E5sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (NF8 Mainboard) + +pci:v000010DEd000000E5sv00001849sd000000E5* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (Motherboard (one of many)) + +pci:v000010DEd000000E5sv0000F849sd000000E5* + ID_MODEL_FROM_DATABASE=CK8S Parallel ATA Controller (v2.5) (Motherboard (one of many)) + +pci:v000010DEd000000E6* + ID_MODEL_FROM_DATABASE=CK8S Ethernet Controller + +pci:v000010DEd000000E7* + ID_MODEL_FROM_DATABASE=CK8S USB Controller + +pci:v000010DEd000000E7sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=CK8S USB Controller (K8N-E) + +pci:v000010DEd000000E7sv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=CK8S USB Controller (Winfast NF3250K8AA) + +pci:v000010DEd000000E7sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=CK8S USB Controller (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E7sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=CK8S USB Controller (NF8 Mainboard) + +pci:v000010DEd000000E7sv00001849sd000000E7* + ID_MODEL_FROM_DATABASE=CK8S USB Controller (Motherboard (one of many)) + +pci:v000010DEd000000E8* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller + +pci:v000010DEd000000E8sv00001043sd0000813F* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller (K8N-E) + +pci:v000010DEd000000E8sv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller (Winfast NF3250K8AA) + +pci:v000010DEd000000E8sv00001462sd00007030* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller (K8N Neo-FSR v2.0) + +pci:v000010DEd000000E8sv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller (NF8 Mainboard) + +pci:v000010DEd000000E8sv00001849sd000000E8* + ID_MODEL_FROM_DATABASE=nForce3 EHCI USB 2.0 Controller (Motherboard (one of many)) + +pci:v000010DEd000000EA* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AC'97 Audio Controller + +pci:v000010DEd000000EAsv00001043sd0000819D* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AC'97 Audio Controller (K8N-E) + +pci:v000010DEd000000EAsv0000105Bsd00000C43* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AC'97 Audio Controller (Winfast NF3250K8AA) + +pci:v000010DEd000000EAsv00001462sd0000B010* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AC'97 Audio Controller (K8N Neo-FSR v2.0) + +pci:v000010DEd000000EAsv0000147Bsd00001C0B* + ID_MODEL_FROM_DATABASE=nForce3 250Gb AC'97 Audio Controller (NF8 Mainboard) + +pci:v000010DEd000000ED* + ID_MODEL_FROM_DATABASE=nForce3 250Gb PCI-to-PCI Bridge + +pci:v000010DEd000000EE* + ID_MODEL_FROM_DATABASE=nForce3 Serial ATA Controller 2 + +pci:v000010DEd000000F1* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] + +pci:v000010DEd000000F1sv00001043sd000081A6* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (N6600GT TD 128M AGP) + +pci:v000010DEd000000F1sv00001043sd000081C6* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (N6600GT TD 128M AGP) + +pci:v000010DEd000000F1sv00001458sd00003150* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (GV-N66T128VP) + +pci:v000010DEd000000F1sv00001554sd00001191* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (PixelView PV-N43UA (128KD)) + +pci:v000010DEd000000F1sv00001682sd00002119* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (GeForce 6600 GT AGP) + +pci:v000010DEd000000F2* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] + +pci:v000010DEd000000F2sv00001554sd00001194* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (PixelView PV-N43AT (256KD)) + +pci:v000010DEd000000F2sv00001682sd0000211C* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (GeForce 6600 256MB DDR DUAL DVI TV) + +pci:v000010DEd000000F3* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6200] + +pci:v000010DEd000000F4* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 LE] + +pci:v000010DEd000000F5* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7800 GS] + +pci:v000010DEd000000F6* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6800 GS/XT] + +pci:v000010DEd000000F6sv00001682sd0000217E* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6800 GS/XT] (XFX GeForce 6800 XTreme 256MB DDR3 AGP) + +pci:v000010DEd000000F8* + ID_MODEL_FROM_DATABASE=NV40GL [Quadro FX 3400/4400] + +pci:v000010DEd000000F9* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT/GTO/Ultra] + +pci:v000010DEd000000F9sv000010DEsd000000F9* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT/GTO/Ultra] (NV40 [GeForce 6800 GT]) + +pci:v000010DEd000000F9sv00001682sd00002120* + ID_MODEL_FROM_DATABASE=NV40 [GeForce 6800 GT/GTO/Ultra] (GEFORCE 6800 GT PCI-E) + +pci:v000010DEd000000FA* + ID_MODEL_FROM_DATABASE=NV36 [GeForce PCX 5750] + +pci:v000010DEd000000FB* + ID_MODEL_FROM_DATABASE=NV38 [GeForce PCX 5900] + +pci:v000010DEd000000FC* + ID_MODEL_FROM_DATABASE=NV37GL [Quadro FX 330/GeForce PCX 5300] + +pci:v000010DEd000000FD* + ID_MODEL_FROM_DATABASE=NV37GL [Quadro PCI-E Series] + +pci:v000010DEd000000FE* + ID_MODEL_FROM_DATABASE=NV38GL [Quadro FX 1300] + +pci:v000010DEd000000FF* + ID_MODEL_FROM_DATABASE=NV18 [GeForce PCX 4300] + +pci:v000010DEd00000100* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] + +pci:v000010DEd00000100sv00001043sd00000200* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (AGP-V6600 SGRAM) + +pci:v000010DEd00000100sv00001043sd00000201* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (AGP-V6600 SDRAM) + +pci:v000010DEd00000100sv00001043sd00004008* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (AGP-V6600 SGRAM) + +pci:v000010DEd00000100sv00001043sd00004009* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (AGP-V6600 SDRAM) + +pci:v000010DEd00000100sv00001048sd00000C41* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (Erazor X) + +pci:v000010DEd00000100sv00001048sd00000C43* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (ERAZOR X PCI) + +pci:v000010DEd00000100sv00001048sd00000C48* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (Synergy Force) + +pci:v000010DEd00000100sv00001102sd0000102D* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (CT6941 GeForce 256) + +pci:v000010DEd00000100sv000014AFsd00005022* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 SDR] (3D Prophet SE) + +pci:v000010DEd00000101* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] + +pci:v000010DEd00000101sv00001043sd00000202* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (AGP-V6800 DDR) + +pci:v000010DEd00000101sv00001043sd0000400A* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (AGP-V6800 DDR SGRAM) + +pci:v000010DEd00000101sv00001043sd0000400B* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (AGP-V6800 DDR SDRAM) + +pci:v000010DEd00000101sv00001048sd00000C42* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (Erazor X) + +pci:v000010DEd00000101sv0000107Dsd00002822* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (WinFast GeForce 256) + +pci:v000010DEd00000101sv00001102sd0000102E* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (CT6970/CT6971) + +pci:v000010DEd00000101sv000014AFsd00005021* + ID_MODEL_FROM_DATABASE=NV10 [GeForce 256 DDR] (3D Prophet DDR-DVI) + +pci:v000010DEd00000103* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] + +pci:v000010DEd00000103sv00001048sd00000C40* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] (GLoria II-64) + +pci:v000010DEd00000103sv00001048sd00000C44* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] (GLoria II) + +pci:v000010DEd00000103sv00001048sd00000C45* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] (GLoria II) + +pci:v000010DEd00000103sv00001048sd00000C4A* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] (GLoria II-64 Pro) + +pci:v000010DEd00000103sv00001048sd00000C4B* + ID_MODEL_FROM_DATABASE=NV10GL [Quadro] (GLoria II-64 Pro DVII) + +pci:v000010DEd00000110* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] + +pci:v000010DEd00000110sv00001043sd00004015* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (AGP-V7100 Pro) + +pci:v000010DEd00000110sv00001043sd00004021* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (V7100 Deluxe Combo) + +pci:v000010DEd00000110sv00001043sd00004031* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (V7100 Pro with TV output) + +pci:v000010DEd00000110sv00001048sd00000C60* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac MX) + +pci:v000010DEd00000110sv00001048sd00000C61* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac 511PCI) + +pci:v000010DEd00000110sv00001048sd00000C63* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac 511TV-OUT 32MB) + +pci:v000010DEd00000110sv00001048sd00000C64* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac 511TV-OUT 64MB) + +pci:v000010DEd00000110sv00001048sd00000C65* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac 511TWIN) + +pci:v000010DEd00000110sv00001048sd00000C66* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Gladiac 311) + +pci:v000010DEd00000110sv000010B0sd00000001* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (GeForce2 MX Jumbo TV) + +pci:v000010DEd00000110sv000010DEsd00000091* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Dell OEM GeForce 2 MX 400) + +pci:v000010DEd00000110sv000010DEsd000000A1* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Apple OEM GeForce2 MX) + +pci:v000010DEd00000110sv00001462sd00008523* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (MS-8852) + +pci:v000010DEd00000110sv00001462sd00008817* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (MSI GeForce2 MX400 Pro32S [MS-8817]) + +pci:v000010DEd00000110sv000014AFsd00007102* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (3D Prophet II MX) + +pci:v000010DEd00000110sv000014AFsd00007103* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (3D Prophet II MX Dual-Display) + +pci:v000010DEd00000110sv00001545sd00000023* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (Xtasy Rev. B2) + +pci:v000010DEd00000110sv00001554sd00001081* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX/MX 400] (MVGA-NVG11AM(400)) + +pci:v000010DEd00000111* + ID_MODEL_FROM_DATABASE=NV11 [GeForce2 MX200] + +pci:v000010DEd00000112* + ID_MODEL_FROM_DATABASE=NV11M [GeForce2 Go] + +pci:v000010DEd00000113* + ID_MODEL_FROM_DATABASE=NV11GL [Quadro2 MXR/EX/Go] + +pci:v000010DEd00000140* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] + +pci:v000010DEd00000140sv00001458sd00003125* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (GV-NX66T128D) + +pci:v000010DEd00000140sv00001458sd00003126* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (GV-NX66T256DE) + +pci:v000010DEd00000140sv00001462sd00008939* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] (MS-8983) + +pci:v000010DEd00000141* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] + +pci:v000010DEd00000141sv00001043sd000081B0* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (EN6600 Silencer) + +pci:v000010DEd00000141sv0000107Dsd0000593A* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (LR2A22 128MB TV OUT) + +pci:v000010DEd00000141sv0000107Dsd0000597B* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (WINFAST PX6600) + +pci:v000010DEd00000141sv00001458sd00003124* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600] (GV-NX66128DP Turbo Force Edition) + +pci:v000010DEd00000142* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 LE] + +pci:v000010DEd00000143* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 VE] + +pci:v000010DEd00000144* + ID_MODEL_FROM_DATABASE=NV43M [GeForce Go 6600] + +pci:v000010DEd00000145* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6610 XL] + +pci:v000010DEd00000146* + ID_MODEL_FROM_DATABASE=NV43M [GeForce Go6200 TE / 6600 TE] + +pci:v000010DEd00000147* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6700 XL] + +pci:v000010DEd00000148* + ID_MODEL_FROM_DATABASE=NV43M [GeForce Go 6600] + +pci:v000010DEd00000149* + ID_MODEL_FROM_DATABASE=NV43M [GeForce Go 6600 GT] + +pci:v000010DEd0000014A* + ID_MODEL_FROM_DATABASE=NV43 [Quadro NVS 440] + +pci:v000010DEd0000014B* + ID_MODEL_FROM_DATABASE=NV43 + +pci:v000010DEd0000014D* + ID_MODEL_FROM_DATABASE=NV43GL [Quadro FX 550] + +pci:v000010DEd0000014E* + ID_MODEL_FROM_DATABASE=NV43GL [Quadro FX 540] + +pci:v000010DEd0000014F* + ID_MODEL_FROM_DATABASE=NV43 [GeForce 6200] + +pci:v000010DEd00000150* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] + +pci:v000010DEd00000150sv00001043sd00004016* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (V7700 AGP Video Card) + +pci:v000010DEd00000150sv00001043sd0000402A* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (AGP-V7700) + +pci:v000010DEd00000150sv00001048sd00000C50* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (Gladiac) + +pci:v000010DEd00000150sv00001048sd00000C52* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (Gladiac-64) + +pci:v000010DEd00000150sv0000107Dsd00002840* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (WinFast GeForce2 GTS with TV output) + +pci:v000010DEd00000150sv0000107Dsd00002842* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (WinFast GeForce 2 Pro) + +pci:v000010DEd00000150sv000010DEsd0000002E* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (GeForce2 GTS) + +pci:v000010DEd00000150sv00001462sd0000815A* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (MS-8815) + +pci:v000010DEd00000150sv00001462sd00008831* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 GTS/Pro] (Creative GeForce2 Pro) + +pci:v000010DEd00000151* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ti] + +pci:v000010DEd00000151sv00001043sd0000405F* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ti] (V7700Ti) + +pci:v000010DEd00000151sv00001462sd00005506* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ti] (Creative 3D Blaster GeForce2 Titanium) + +pci:v000010DEd00000151sv00001462sd00008364* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ti] (MS-8836) + +pci:v000010DEd00000152* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ultra] + +pci:v000010DEd00000152sv00001048sd00000C56* + ID_MODEL_FROM_DATABASE=NV15 [GeForce2 Ultra] (GLADIAC Ultra) + +pci:v000010DEd00000153* + ID_MODEL_FROM_DATABASE=NV15GL [Quadro2 Pro] + +pci:v000010DEd00000160* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6500] + +pci:v000010DEd00000161* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6200 TurboCache] + +pci:v000010DEd00000162* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6200 SE TurboCache] + +pci:v000010DEd00000163* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6200 LE] + +pci:v000010DEd00000164* + ID_MODEL_FROM_DATABASE=NV44M [GeForce Go 6200] + +pci:v000010DEd00000165* + ID_MODEL_FROM_DATABASE=NV44 [Quadro NVS 285] + +pci:v000010DEd00000166* + ID_MODEL_FROM_DATABASE=NV44M [GeForce Go 6400] + +pci:v000010DEd00000167* + ID_MODEL_FROM_DATABASE=NV44M [GeForce Go 6200] + +pci:v000010DEd00000168* + ID_MODEL_FROM_DATABASE=NV44M [GeForce Go 6400] + +pci:v000010DEd00000169* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6250] + +pci:v000010DEd0000016A* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 7100 GS] + +pci:v000010DEd0000016D* + ID_MODEL_FROM_DATABASE=NV44 + +pci:v000010DEd0000016E* + ID_MODEL_FROM_DATABASE=NV44 + +pci:v000010DEd0000016F* + ID_MODEL_FROM_DATABASE=NV44 + +pci:v000010DEd00000170* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 460] + +pci:v000010DEd00000170sv00001462sd00008630* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 460] (MS-8863) + +pci:v000010DEd00000171* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] + +pci:v000010DEd00000171sv000010B0sd00000002* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (Gainward Pro/600 TV) + +pci:v000010DEd00000171sv000010DEsd00000008* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (Apple OEM GeForce4 MX 440) + +pci:v000010DEd00000171sv00001462sd00008661* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (G4MX440-VTP) + +pci:v000010DEd00000171sv00001462sd00008730* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (MX440SES-T (MS-8873)) + +pci:v000010DEd00000171sv00001462sd00008743* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (MS-8874) + +pci:v000010DEd00000171sv00001462sd00008852* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (GeForce4 MX440 PCI) + +pci:v000010DEd00000171sv0000147Bsd00008F00* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440] (Abit Siluro GeForce4MX440) + +pci:v000010DEd00000172* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 420] + +pci:v000010DEd00000172sv00001462sd00008730* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 420] (MS-8873) + +pci:v000010DEd00000172sv00001462sd00008784* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 420] (MS-8878) + +pci:v000010DEd00000173* + ID_MODEL_FROM_DATABASE=NV17 [GeForce4 MX 440-SE] + +pci:v000010DEd00000174* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 440 Go] + +pci:v000010DEd00000175* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 420 Go] + +pci:v000010DEd00000176* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 420 Go 32M] + +pci:v000010DEd00000176sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 420 Go 32M] (tc1100 tablet) + +pci:v000010DEd00000176sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 420 Go 32M] (X10 Laptop) + +pci:v000010DEd00000176sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 420 Go 32M] (Cx9 / Vx9 mainboard) + +pci:v000010DEd00000177* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 460 Go] + +pci:v000010DEd00000178* + ID_MODEL_FROM_DATABASE=NV17GL [Quadro4 550 XGL] + +pci:v000010DEd00000179* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 440 Go 64M] + +pci:v000010DEd00000179sv000010DEsd00000179* + ID_MODEL_FROM_DATABASE=NV17M [GeForce4 440 Go 64M] (GeForce4 MX (Mac)) + +pci:v000010DEd0000017A* + ID_MODEL_FROM_DATABASE=NV17GL [Quadro NVS] + +pci:v000010DEd0000017B* + ID_MODEL_FROM_DATABASE=NV17GL [Quadro4 550 XGL] + +pci:v000010DEd0000017C* + ID_MODEL_FROM_DATABASE=NV17GL [Quadro4 500 GoGL] + +pci:v000010DEd0000017F* + ID_MODEL_FROM_DATABASE=NV17 + +pci:v000010DEd00000181* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] + +pci:v000010DEd00000181sv00001043sd00008063* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (GeForce4 MX 440 AGP 8X) + +pci:v000010DEd00000181sv00001043sd0000806F* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (V9180 Magic) + +pci:v000010DEd00000181sv00001462sd00008880* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (MS-StarForce GeForce4 MX 440 with AGP8X) + +pci:v000010DEd00000181sv00001462sd00008900* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (MS-8890 GeForce 4 MX440 AGP8X) + +pci:v000010DEd00000181sv00001462sd00009350* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (MSI GeForce4 MX T8X with AGP8X) + +pci:v000010DEd00000181sv0000147Bsd00008F0D* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (Siluro GF4 MX-8X) + +pci:v000010DEd00000181sv00001554sd00001111* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440 AGP 8x] (PixelView MVGA-NVG18A) + +pci:v000010DEd00000182* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 440SE AGP 8x] + +pci:v000010DEd00000183* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 420 AGP 8x] + +pci:v000010DEd00000184* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX] + +pci:v000010DEd00000185* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX 4000] + +pci:v000010DEd00000186* + ID_MODEL_FROM_DATABASE=NV18M [GeForce4 448 Go] + +pci:v000010DEd00000187* + ID_MODEL_FROM_DATABASE=NV18M [GeForce4 488 Go] + +pci:v000010DEd00000188* + ID_MODEL_FROM_DATABASE=NV18GL [Quadro4 580 XGL] + +pci:v000010DEd00000189* + ID_MODEL_FROM_DATABASE=NV18 [GeForce4 MX with AGP8X (Mac)] + +pci:v000010DEd0000018A* + ID_MODEL_FROM_DATABASE=NV18GL [Quadro NVS 280 SD] + +pci:v000010DEd0000018B* + ID_MODEL_FROM_DATABASE=NV18GL [Quadro4 380 XGL] + +pci:v000010DEd0000018C* + ID_MODEL_FROM_DATABASE=NV18GL [Quadro NVS 50 PCI] + +pci:v000010DEd0000018D* + ID_MODEL_FROM_DATABASE=NV18M [GeForce4 448 Go] + +pci:v000010DEd0000018F* + ID_MODEL_FROM_DATABASE=NV18 + +pci:v000010DEd00000190* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 GTS / 8800 GTX] + +pci:v000010DEd00000191* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 GTX] + +pci:v000010DEd00000192* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 GTS] + +pci:v000010DEd00000193* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 GTS] + +pci:v000010DEd00000193sv0000107Dsd000020BD* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 GTS] (WinFast PX 8800 GTS TDH) + +pci:v000010DEd00000194* + ID_MODEL_FROM_DATABASE=G80 [GeForce 8800 Ultra] + +pci:v000010DEd00000197* + ID_MODEL_FROM_DATABASE=G80GL [Tesla C870] + +pci:v000010DEd0000019D* + ID_MODEL_FROM_DATABASE=G80GL [Quadro FX 5600] + +pci:v000010DEd0000019E* + ID_MODEL_FROM_DATABASE=G80GL [Quadro FX 4600] + +pci:v000010DEd000001A0* + ID_MODEL_FROM_DATABASE=nForce 220/420 NV11 [GeForce2 MX] + +pci:v000010DEd000001A4* + ID_MODEL_FROM_DATABASE=nForce CPU bridge + +pci:v000010DEd000001AB* + ID_MODEL_FROM_DATABASE=nForce 420 Memory Controller (DDR) + +pci:v000010DEd000001AC* + ID_MODEL_FROM_DATABASE=nForce 220/420 Memory Controller + +pci:v000010DEd000001AD* + ID_MODEL_FROM_DATABASE=nForce 220/420 Memory Controller + +pci:v000010DEd000001B0* + ID_MODEL_FROM_DATABASE=nForce Audio Processing Unit + +pci:v000010DEd000001B1* + ID_MODEL_FROM_DATABASE=nForce AC'97 Audio Controller + +pci:v000010DEd000001B2* + ID_MODEL_FROM_DATABASE=nForce ISA Bridge + +pci:v000010DEd000001B4* + ID_MODEL_FROM_DATABASE=nForce PCI System Management + +pci:v000010DEd000001B7* + ID_MODEL_FROM_DATABASE=nForce AGP to PCI Bridge + +pci:v000010DEd000001B8* + ID_MODEL_FROM_DATABASE=nForce PCI-to-PCI bridge + +pci:v000010DEd000001BC* + ID_MODEL_FROM_DATABASE=nForce IDE + +pci:v000010DEd000001C1* + ID_MODEL_FROM_DATABASE=nForce AC'97 Modem Controller + +pci:v000010DEd000001C2* + ID_MODEL_FROM_DATABASE=nForce USB Controller + +pci:v000010DEd000001C3* + ID_MODEL_FROM_DATABASE=nForce Ethernet Controller + +pci:v000010DEd000001D0* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7350 LE] + +pci:v000010DEd000001D1* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7300 LE] + +pci:v000010DEd000001D1sv0000107Dsd00005EFA* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7300 LE] (WinFast PX7300LE-TD128) + +pci:v000010DEd000001D1sv0000107Dsd00005EFB* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7300 LE] (WinFast PX7300LE-TD256) + +pci:v000010DEd000001D1sv00001462sd00000345* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7300 LE] (7300LE PCI Express Graphics Adapter) + +pci:v000010DEd000001D2* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7550 LE] + +pci:v000010DEd000001D3* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7200 GS / 7300 SE] + +pci:v000010DEd000001D3sv00001043sd00008203* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7200 GS / 7300 SE] (EN7300SE) + +pci:v000010DEd000001D3sv00001043sd00008250* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7200 GS / 7300 SE] (EN7200GS) + +pci:v000010DEd000001D5* + ID_MODEL_FROM_DATABASE=G72 + +pci:v000010DEd000001D6* + ID_MODEL_FROM_DATABASE=G72M [GeForce Go 7200] + +pci:v000010DEd000001D7* + ID_MODEL_FROM_DATABASE=G72M [Quadro NVS 110M/GeForce Go 7300] + +pci:v000010DEd000001D8* + ID_MODEL_FROM_DATABASE=G72M [GeForce Go 7400] + +pci:v000010DEd000001D8sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=G72M [GeForce Go 7400] (XPS M1210) + +pci:v000010DEd000001D9* + ID_MODEL_FROM_DATABASE=G72M [GeForce Go 7450] + +pci:v000010DEd000001DA* + ID_MODEL_FROM_DATABASE=G72M [Quadro NVS 110M] + +pci:v000010DEd000001DB* + ID_MODEL_FROM_DATABASE=G72M [Quadro NVS 120M] + +pci:v000010DEd000001DC* + ID_MODEL_FROM_DATABASE=G72GLM [Quadro FX 350M] + +pci:v000010DEd000001DD* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7500 LE] + +pci:v000010DEd000001DE* + ID_MODEL_FROM_DATABASE=G72GL [Quadro FX 350] + +pci:v000010DEd000001DEsv000010DEsd000001DC* + ID_MODEL_FROM_DATABASE=G72GL [Quadro FX 350] (Quadro FX Go350M) + +pci:v000010DEd000001DF* + ID_MODEL_FROM_DATABASE=G72 [GeForce 7300 GS] + +pci:v000010DEd000001E0* + ID_MODEL_FROM_DATABASE=nForce2 IGP2 + +pci:v000010DEd000001E0sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=nForce2 IGP2 (NV7 Motherboard) + +pci:v000010DEd000001E8* + ID_MODEL_FROM_DATABASE=nForce2 AGP + +pci:v000010DEd000001EA* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 0 + +pci:v000010DEd000001EAsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 0 (UK79G-1394 motherboard) + +pci:v000010DEd000001EB* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 1 + +pci:v000010DEd000001EBsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 1 (UK79G-1394 motherboard) + +pci:v000010DEd000001EC* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 2 + +pci:v000010DEd000001ECsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 2 (UK79G-1394 motherboard) + +pci:v000010DEd000001ED* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 3 + +pci:v000010DEd000001EDsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 3 (UK79G-1394 motherboard) + +pci:v000010DEd000001EE* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 4 + +pci:v000010DEd000001EEsv000010DEsd000001EE* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 4 (MSI Delta-L nForce2 memory controller) + +pci:v000010DEd000001EEsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 4 (UK79G-1394 motherboard) + +pci:v000010DEd000001EF* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 5 + +pci:v000010DEd000001EFsv0000A0A0sd000003B9* + ID_MODEL_FROM_DATABASE=nForce2 Memory Controller 5 (UK79G-1394 motherboard) + +pci:v000010DEd000001F0* + ID_MODEL_FROM_DATABASE=C17 [GeForce4 MX IGP] + +pci:v000010DEd000001F0sv0000A0A0sd000003B5* + ID_MODEL_FROM_DATABASE=C17 [GeForce4 MX IGP] (UK79G-1394 motherboard) + +pci:v000010DEd00000200* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3] + +pci:v000010DEd00000200sv00001043sd0000402F* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3] (AGP-V8200 DDR) + +pci:v000010DEd00000200sv00001048sd00000C70* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3] (GLADIAC 920) + +pci:v000010DEd00000201* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3 Ti 200] + +pci:v000010DEd00000202* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3 Ti 500] + +pci:v000010DEd00000202sv00001043sd0000405B* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3 Ti 500] (V8200 T5) + +pci:v000010DEd00000202sv00001545sd0000002F* + ID_MODEL_FROM_DATABASE=NV20 [GeForce3 Ti 500] (Xtasy 6964) + +pci:v000010DEd00000203* + ID_MODEL_FROM_DATABASE=NV20GL [Quadro DCC] + +pci:v000010DEd00000211* + ID_MODEL_FROM_DATABASE=NV48 [GeForce 6800] + +pci:v000010DEd00000212* + ID_MODEL_FROM_DATABASE=NV48 [GeForce 6800 LE] + +pci:v000010DEd00000215* + ID_MODEL_FROM_DATABASE=NV48 [GeForce 6800 GT] + +pci:v000010DEd00000218* + ID_MODEL_FROM_DATABASE=NV48 [GeForce 6800 XT] + +pci:v000010DEd00000221* + ID_MODEL_FROM_DATABASE=NV44A [GeForce 6200] + +pci:v000010DEd00000221sv00001043sd000081E1* + ID_MODEL_FROM_DATABASE=NV44A [GeForce 6200] (N6200/TD/256M/A) + +pci:v000010DEd00000221sv00003842sd0000A341* + ID_MODEL_FROM_DATABASE=NV44A [GeForce 6200] (256A8N341DX) + +pci:v000010DEd00000222* + ID_MODEL_FROM_DATABASE=NV44 [GeForce 6200 A-LE] + +pci:v000010DEd00000224* + ID_MODEL_FROM_DATABASE=NV44 + +pci:v000010DEd00000240* + ID_MODEL_FROM_DATABASE=C51PV [GeForce 6150] + +pci:v000010DEd00000240sv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51PV [GeForce 6150] (A8N-VM CSM) + +pci:v000010DEd00000240sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51PV [GeForce 6150] (K8NGM2 series) + +pci:v000010DEd00000241* + ID_MODEL_FROM_DATABASE=C51 [GeForce 6150 LE] + +pci:v000010DEd00000242* + ID_MODEL_FROM_DATABASE=C51G [GeForce 6100] + +pci:v000010DEd00000242sv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=C51G [GeForce 6100] (Winfast 6100K8MB) + +pci:v000010DEd00000243* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd00000244* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6150] + +pci:v000010DEd00000244sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6150] (Presario V3242AU) + +pci:v000010DEd00000244sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6150] (Presario V6133CL) + +pci:v000010DEd00000244sv000010DEsd00000244* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6150] (GeForce Go 6150) + +pci:v000010DEd00000245* + ID_MODEL_FROM_DATABASE=C51 [Quadro NVS 210S/GeForce 6150LE] + +pci:v000010DEd00000246* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd00000247* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6100] + +pci:v000010DEd00000247sv00001043sd00001382* + ID_MODEL_FROM_DATABASE=C51 [GeForce Go 6100] (MCP51 PCI-X GeForce Go 6100) + +pci:v000010DEd00000248* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd00000249* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024A* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024B* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024C* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024D* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024E* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd0000024F* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd00000250* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4600] + +pci:v000010DEd00000251* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4400] + +pci:v000010DEd00000251sv00001043sd00008023* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4400] (v8440 GeForce 4 Ti4400) + +pci:v000010DEd00000251sv000010DEsd00000251* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4400] (PNY GeForce4 Ti 4400) + +pci:v000010DEd00000251sv00001462sd00008710* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4400] (PNY GeForce4 Ti 4400) + +pci:v000010DEd00000252* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti] + +pci:v000010DEd00000253* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4200] + +pci:v000010DEd00000253sv0000107Dsd00002896* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4200] (WinFast A250 LE TD (Dual VGA/TV-out/DVI)) + +pci:v000010DEd00000253sv0000147Bsd00008F09* + ID_MODEL_FROM_DATABASE=NV25 [GeForce4 Ti 4200] (Siluro (Dual VGA/TV-out/DVI)) + +pci:v000010DEd00000258* + ID_MODEL_FROM_DATABASE=NV25GL [Quadro4 900 XGL] + +pci:v000010DEd00000259* + ID_MODEL_FROM_DATABASE=NV25GL [Quadro4 750 XGL] + +pci:v000010DEd0000025B* + ID_MODEL_FROM_DATABASE=NV25GL [Quadro4 700 XGL] + +pci:v000010DEd00000260* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge + +pci:v000010DEd00000260sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (Pavilion a1677c) + +pci:v000010DEd00000260sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (Presario V6133CL) + +pci:v000010DEd00000260sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (A8N-VM CSM Mainboard) + +pci:v000010DEd00000260sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (GA-M55plus-S3G) + +pci:v000010DEd00000260sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (K8NGM2 series) + +pci:v000010DEd00000261* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge + +pci:v000010DEd00000261sv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge (Winfast 6100K8MB) + +pci:v000010DEd00000262* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge + +pci:v000010DEd00000263* + ID_MODEL_FROM_DATABASE=MCP51 LPC Bridge + +pci:v000010DEd00000264* + ID_MODEL_FROM_DATABASE=MCP51 SMBus + +pci:v000010DEd00000264sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 SMBus (Pavilion a1677c) + +pci:v000010DEd00000264sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 SMBus (Presario V6133CL) + +pci:v000010DEd00000264sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 SMBus (A8N-VM CSM Mainboard) + +pci:v000010DEd00000264sv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 SMBus (Winfast 6100K8MB) + +pci:v000010DEd00000264sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 SMBus (K8NGM2 series) + +pci:v000010DEd00000265* + ID_MODEL_FROM_DATABASE=MCP51 IDE + +pci:v000010DEd00000265sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 IDE (Pavilion a1677c) + +pci:v000010DEd00000265sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 IDE (Presario V6133CL) + +pci:v000010DEd00000265sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 IDE (A8N-VM CSM Mainboard) + +pci:v000010DEd00000265sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 IDE (K8NGM2 series) + +pci:v000010DEd00000265sv0000F05Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 IDE (Winfast 6100K8MB) + +pci:v000010DEd00000266* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller + +pci:v000010DEd00000266sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (Pavilion a1677c) + +pci:v000010DEd00000266sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (Presario V6133CL) + +pci:v000010DEd00000266sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (A8N-VM CSM Mainboard) + +pci:v000010DEd00000266sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (K8NGM2 series) + +pci:v000010DEd00000267* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller + +pci:v000010DEd00000267sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (Pavilion a1677c) + +pci:v000010DEd00000267sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (A8N-VM CSM Mainboard) + +pci:v000010DEd00000267sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 Serial ATA Controller (K8NGM2 series) + +pci:v000010DEd00000268* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller + +pci:v000010DEd00000269* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller + +pci:v000010DEd00000269sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller (Pavilion a1677c) + +pci:v000010DEd00000269sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller (Presario V6133CL) + +pci:v000010DEd00000269sv00001043sd00008141* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller (A8N-VM CSM Mainboard) + +pci:v000010DEd00000269sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 Ethernet Controller (K8NGM2 series) + +pci:v000010DEd0000026A* + ID_MODEL_FROM_DATABASE=MCP51 MCI + +pci:v000010DEd0000026B* + ID_MODEL_FROM_DATABASE=MCP51 AC97 Audio Controller + +pci:v000010DEd0000026Bsv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 AC97 Audio Controller (Winfast 6100K8MB) + +pci:v000010DEd0000026C* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio + +pci:v000010DEd0000026Csv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio (Pavilion a1677c) + +pci:v000010DEd0000026Csv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio (Presario V3242AU) + +pci:v000010DEd0000026Csv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio (Presario V6133CL) + +pci:v000010DEd0000026Csv000010DEsd0000CB84* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio (ASUSTeK Computer Inc. A8N-VM CSM Mainboard) + +pci:v000010DEd0000026Csv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 High Definition Audio (K8NGM2 series) + +pci:v000010DEd0000026D* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller + +pci:v000010DEd0000026Dsv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Pavilion a1677c) + +pci:v000010DEd0000026Dsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Presario V6133CL) + +pci:v000010DEd0000026Dsv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (A8N-VM CSM Mainboard) + +pci:v000010DEd0000026Dsv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Winfast 6100K8MB) + +pci:v000010DEd0000026Dsv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (K8NGM2 series) + +pci:v000010DEd0000026E* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller + +pci:v000010DEd0000026Esv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Pavilion a1677c) + +pci:v000010DEd0000026Esv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Presario V6133CL) + +pci:v000010DEd0000026Esv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (A8N-VM CSM Mainboard) + +pci:v000010DEd0000026Esv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (Winfast 6100K8MB) + +pci:v000010DEd0000026Esv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 USB Controller (K8NGM2 series) + +pci:v000010DEd0000026F* + ID_MODEL_FROM_DATABASE=MCP51 PCI Bridge + +pci:v000010DEd0000026Fsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 PCI Bridge (Presario V6133CL) + +pci:v000010DEd00000270* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge + +pci:v000010DEd00000270sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (Pavilion a1677c) + +pci:v000010DEd00000270sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (Presario V6133CL) + +pci:v000010DEd00000270sv00001043sd000081BC* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (A8N-VM CSM Mainboard) + +pci:v000010DEd00000270sv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (Winfast 6100K8MB) + +pci:v000010DEd00000270sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (GA-M55plus-S3G) + +pci:v000010DEd00000270sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=MCP51 Host Bridge (K8NGM2 series) + +pci:v000010DEd00000271* + ID_MODEL_FROM_DATABASE=MCP51 PMU + +pci:v000010DEd00000271sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=MCP51 PMU (Presario V3242AU) + +pci:v000010DEd00000271sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=MCP51 PMU (Presario V6133CL) + +pci:v000010DEd00000272* + ID_MODEL_FROM_DATABASE=MCP51 Memory Controller 0 + +pci:v000010DEd00000272sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=MCP51 Memory Controller 0 (Pavilion a1677c) + +pci:v000010DEd00000272sv0000105Bsd00000CAD* + ID_MODEL_FROM_DATABASE=MCP51 Memory Controller 0 (Winfast 6100K8MB) + +pci:v000010DEd0000027E* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 + +pci:v000010DEd0000027Esv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 (Pavilion a1677c) + +pci:v000010DEd0000027Esv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 (Presario V6133CL) + +pci:v000010DEd0000027Esv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 (A8N-VM CSM Mainboard) + +pci:v000010DEd0000027Esv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 (GA-M55plus-S3G) + +pci:v000010DEd0000027Esv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 2 (K8NGM2 series) + +pci:v000010DEd0000027F* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 + +pci:v000010DEd0000027Fsv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 (Pavilion a1677c) + +pci:v000010DEd0000027Fsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 (Presario V6133CL) + +pci:v000010DEd0000027Fsv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 (A8N-VM CSM Mainboard) + +pci:v000010DEd0000027Fsv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 (GA-M55plus-S3G) + +pci:v000010DEd0000027Fsv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 3 (K8NGM2 series) + +pci:v000010DEd00000280* + ID_MODEL_FROM_DATABASE=NV28 [GeForce4 Ti 4800] + +pci:v000010DEd00000281* + ID_MODEL_FROM_DATABASE=NV28 [GeForce4 Ti 4200 AGP 8x] + +pci:v000010DEd00000282* + ID_MODEL_FROM_DATABASE=NV28 [GeForce4 Ti 4800 SE] + +pci:v000010DEd00000286* + ID_MODEL_FROM_DATABASE=NV28M [GeForce4 Ti 4200 Go AGP 8x] + +pci:v000010DEd00000288* + ID_MODEL_FROM_DATABASE=NV28GL [Quadro4 980 XGL] + +pci:v000010DEd00000289* + ID_MODEL_FROM_DATABASE=NV28GL [Quadro4 780 XGL] + +pci:v000010DEd0000028C* + ID_MODEL_FROM_DATABASE=NV28GLM [Quadro4 Go700] + +pci:v000010DEd00000290* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GTX] + +pci:v000010DEd00000291* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GT/GTO] + +pci:v000010DEd00000291sv000010DEsd0000042B* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GT/GTO] (NX7900GTO-T2D512E [7900 GTO]) + +pci:v000010DEd00000292* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GS] + +pci:v000010DEd00000293* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GX2] + +pci:v000010DEd00000294* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GX2] + +pci:v000010DEd00000295* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] + +pci:v000010DEd00000295sv00001043sd00008225* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] (GeForce 7950 GT) + +pci:v000010DEd00000295sv0000107Dsd00002A68* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] (WinFast PX7950GT TDH) + +pci:v000010DEd00000295sv00001462sd00000663* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] (NX7950GT-VT2D512EZ-HD) + +pci:v000010DEd00000297* + ID_MODEL_FROM_DATABASE=G71M [GeForce Go 7950 GTX] + +pci:v000010DEd00000298* + ID_MODEL_FROM_DATABASE=G71M [GeForce Go 7900 GS] + +pci:v000010DEd00000299* + ID_MODEL_FROM_DATABASE=G71M [GeForce Go 7900 GTX] + +pci:v000010DEd0000029A* + ID_MODEL_FROM_DATABASE=G71GLM [Quadro FX 2500M] + +pci:v000010DEd0000029B* + ID_MODEL_FROM_DATABASE=G71GLM [Quadro FX 1500M] + +pci:v000010DEd0000029C* + ID_MODEL_FROM_DATABASE=G71GL [Quadro FX 5500] + +pci:v000010DEd0000029D* + ID_MODEL_FROM_DATABASE=G71GL [Quadro FX 3500] + +pci:v000010DEd0000029Dsv00001028sd0000019B* + ID_MODEL_FROM_DATABASE=G71GL [Quadro FX 3500] (G71GLM [Quadro FX 3500M]) + +pci:v000010DEd0000029E* + ID_MODEL_FROM_DATABASE=G71GL [Quadro FX 1500] + +pci:v000010DEd0000029F* + ID_MODEL_FROM_DATABASE=G71GL [Quadro FX 4500 X2] + +pci:v000010DEd000002A0* + ID_MODEL_FROM_DATABASE=NV2A [XGPU] + +pci:v000010DEd000002A5* + ID_MODEL_FROM_DATABASE=MCPX CPU Bridge + +pci:v000010DEd000002A6* + ID_MODEL_FROM_DATABASE=MCPX Memory Controller + +pci:v000010DEd000002E0* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GT] + +pci:v000010DEd000002E0sv000002E0sd00002249* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GT] (GF 7600GT 560M 256MB DDR3 DUAL DVI TV) + +pci:v000010DEd000002E1* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GS] + +pci:v000010DEd000002E1sv00001682sd0000222B* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GS] (PV-T73K-UAL3 (256MB)) + +pci:v000010DEd000002E1sv00001682sd00002247* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GS] (GF 7600GS 512MB DDR2) + +pci:v000010DEd000002E2* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7300 GT] + +pci:v000010DEd000002E3* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7900 GS] + +pci:v000010DEd000002E4* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] + +pci:v000010DEd000002E4sv00001682sd00002271* + ID_MODEL_FROM_DATABASE=G71 [GeForce 7950 GT] (PV-T71A-YDF7 (512MB)) + +pci:v000010DEd000002F0* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F0sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (Pavilion a1677c) + +pci:v000010DEd000002F0sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (Presario V6133CL) + +pci:v000010DEd000002F0sv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (A8N-VM CSM Mainboard) + +pci:v000010DEd000002F0sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (K8NGM2 series) + +pci:v000010DEd000002F1* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F1sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (GA-M55plus-S3G) + +pci:v000010DEd000002F2* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F3* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F4* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F5* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F6* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F7* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002F8* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 + +pci:v000010DEd000002F8sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 (Pavilion a1677c) + +pci:v000010DEd000002F8sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 (Presario V6133CL) + +pci:v000010DEd000002F8sv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 (A8N-VM CSM Mainboard) + +pci:v000010DEd000002F8sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 (GA-M55plus-S3G) + +pci:v000010DEd000002F8sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 5 (K8NGM2 series) + +pci:v000010DEd000002F9* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 + +pci:v000010DEd000002F9sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 (Pavilion a1677c) + +pci:v000010DEd000002F9sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 (Presario V6133CL) + +pci:v000010DEd000002F9sv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 (A8N-VM CSM Mainboard) + +pci:v000010DEd000002F9sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 (GA-M55plus-S3G) + +pci:v000010DEd000002F9sv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 4 (K8NGM2 series) + +pci:v000010DEd000002FA* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 + +pci:v000010DEd000002FAsv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 (Pavilion a1677c) + +pci:v000010DEd000002FAsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 (Presario V6133CL) + +pci:v000010DEd000002FAsv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 (A8N-VM CSM Mainboard) + +pci:v000010DEd000002FAsv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 (GA-M55plus-S3G) + +pci:v000010DEd000002FAsv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 0 (K8NGM2 series) + +pci:v000010DEd000002FB* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd000002FC* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd000002FCsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge (Presario V6133CL) + +pci:v000010DEd000002FD* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge + +pci:v000010DEd000002FDsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 PCI Express Bridge (Presario V6133CL) + +pci:v000010DEd000002FE* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 + +pci:v000010DEd000002FEsv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 (Pavilion a1677c) + +pci:v000010DEd000002FEsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 (Presario V6133CL) + +pci:v000010DEd000002FEsv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 (A8N-VM CSM Mainboard) + +pci:v000010DEd000002FEsv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 (GA-M55plus-S3G) + +pci:v000010DEd000002FEsv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Memory Controller 1 (K8NGM2 series) + +pci:v000010DEd000002FF* + ID_MODEL_FROM_DATABASE=C51 Host Bridge + +pci:v000010DEd000002FFsv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (Pavilion a1677c) + +pci:v000010DEd000002FFsv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (Presario V6133CL) + +pci:v000010DEd000002FFsv00001043sd000081CD* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (A8N-VM CSM Mainboard) + +pci:v000010DEd000002FFsv00001458sd00005000* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (GA-M55plus-S3G) + +pci:v000010DEd000002FFsv00001462sd00007207* + ID_MODEL_FROM_DATABASE=C51 Host Bridge (K8NGM2 series) + +pci:v000010DEd00000300* + ID_MODEL_FROM_DATABASE=NV30 [GeForce FX] + +pci:v000010DEd00000301* + ID_MODEL_FROM_DATABASE=NV30 [GeForce FX 5800 Ultra] + +pci:v000010DEd00000302* + ID_MODEL_FROM_DATABASE=NV30 [GeForce FX 5800] + +pci:v000010DEd00000308* + ID_MODEL_FROM_DATABASE=NV30GL [Quadro FX 2000] + +pci:v000010DEd00000309* + ID_MODEL_FROM_DATABASE=NV30GL [Quadro FX 1000] + +pci:v000010DEd00000311* + ID_MODEL_FROM_DATABASE=NV31 [GeForce FX 5600 Ultra] + +pci:v000010DEd00000312* + ID_MODEL_FROM_DATABASE=NV31 [GeForce FX 5600] + +pci:v000010DEd00000314* + ID_MODEL_FROM_DATABASE=NV31 [GeForce FX 5600XT] + +pci:v000010DEd00000314sv00001043sd0000814A* + ID_MODEL_FROM_DATABASE=NV31 [GeForce FX 5600XT] (V9560XT/TD) + +pci:v000010DEd00000316* + ID_MODEL_FROM_DATABASE=NV31M + +pci:v000010DEd00000318* + ID_MODEL_FROM_DATABASE=NV31GL + +pci:v000010DEd0000031A* + ID_MODEL_FROM_DATABASE=NV31M [GeForce FX Go5600] + +pci:v000010DEd0000031B* + ID_MODEL_FROM_DATABASE=NV31M [GeForce FX Go5650] + +pci:v000010DEd0000031C* + ID_MODEL_FROM_DATABASE=NV31GLM [Quadro FX Go700] + +pci:v000010DEd00000320* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] + +pci:v000010DEd00000321* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200 Ultra] + +pci:v000010DEd00000322* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] + +pci:v000010DEd00000322sv00001043sd000002FB* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (V9250 Magic) + +pci:v000010DEd00000322sv00001043sd00008180* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (V9520-X/TD/128M) + +pci:v000010DEd00000322sv0000107Dsd00002967* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (WinFast A340T 128MB) + +pci:v000010DEd00000322sv00001462sd00009110* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (MS-8911 (FX5200-TD128)) + +pci:v000010DEd00000322sv00001462sd00009171* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (MS-8917 (FX5200-T128)) + +pci:v000010DEd00000322sv00001462sd00009360* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (MS-8936 (FX5200-T128)) + +pci:v000010DEd00000322sv00001682sd00001351* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] (GeForce FX 5200) + +pci:v000010DEd00000323* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200LE] + +pci:v000010DEd00000324* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200 64M] + +pci:v000010DEd00000324sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200 64M] (Inspiron 5160) + +pci:v000010DEd00000324sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200 64M] (Pavilion ZD7000 laptop) + +pci:v000010DEd00000324sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200 64M] (MIM2000) + +pci:v000010DEd00000325* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5250] + +pci:v000010DEd00000326* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5500] + +pci:v000010DEd00000326sv00001458sd0000310D* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5500] (GeForce FX 5500 128 MB) + +pci:v000010DEd00000326sv00001682sd00002034* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5500] (GeForce 5500 256 MB) + +pci:v000010DEd00000327* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5100] + +pci:v000010DEd00000328* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200 32M/64M] + +pci:v000010DEd00000329* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200] + +pci:v000010DEd00000329sv000010DEsd00000010* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5200] (Powerbook G4) + +pci:v000010DEd0000032A* + ID_MODEL_FROM_DATABASE=NV34GL [Quadro NVS 280 PCI] + +pci:v000010DEd0000032B* + ID_MODEL_FROM_DATABASE=NV34GL [Quadro FX 500/600 PCI] + +pci:v000010DEd0000032C* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5300 / Go5350] + +pci:v000010DEd0000032D* + ID_MODEL_FROM_DATABASE=NV34M [GeForce FX Go5100] + +pci:v000010DEd0000032E* + ID_MODEL_FROM_DATABASE=NV34 + +pci:v000010DEd0000032F* + ID_MODEL_FROM_DATABASE=NV34 [GeForce FX 5200] + +pci:v000010DEd00000330* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900 Ultra] + +pci:v000010DEd00000330sv00001043sd00008137* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900 Ultra] (V9950 Ultra / 256 MB) + +pci:v000010DEd00000331* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900] + +pci:v000010DEd00000331sv00001043sd00008145* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900] (V9950GE) + +pci:v000010DEd00000332* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900XT] + +pci:v000010DEd00000333* + ID_MODEL_FROM_DATABASE=NV38 [GeForce FX 5950 Ultra] + +pci:v000010DEd00000334* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900ZT] + +pci:v000010DEd00000334sv00001462sd00009373* + ID_MODEL_FROM_DATABASE=NV35 [GeForce FX 5900ZT] (FX5900ZT-VTD128 (MS-8937)) + +pci:v000010DEd00000338* + ID_MODEL_FROM_DATABASE=NV35GL [Quadro FX 3000] + +pci:v000010DEd0000033F* + ID_MODEL_FROM_DATABASE=NV35GL [Quadro FX 700] + +pci:v000010DEd00000341* + ID_MODEL_FROM_DATABASE=NV36 [GeForce FX 5700 Ultra] + +pci:v000010DEd00000341sv00001462sd00009380* + ID_MODEL_FROM_DATABASE=NV36 [GeForce FX 5700 Ultra] (MS-8938 (FX5700U-TD128)) + +pci:v000010DEd00000342* + ID_MODEL_FROM_DATABASE=NV36 [GeForce FX 5700] + +pci:v000010DEd00000343* + ID_MODEL_FROM_DATABASE=NV36 [GeForce FX 5700LE] + +pci:v000010DEd00000344* + ID_MODEL_FROM_DATABASE=NV36 [GeForce FX 5700VE] + +pci:v000010DEd00000347* + ID_MODEL_FROM_DATABASE=NV36M [GeForce FX Go5700] + +pci:v000010DEd00000347sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=NV36M [GeForce FX Go5700] (NX9500) + +pci:v000010DEd00000348* + ID_MODEL_FROM_DATABASE=NV36M [GeForce FX Go5700] + +pci:v000010DEd0000034C* + ID_MODEL_FROM_DATABASE=NV36 [Quadro FX Go1000] + +pci:v000010DEd0000034D* + ID_MODEL_FROM_DATABASE=NV36 + +pci:v000010DEd0000034E* + ID_MODEL_FROM_DATABASE=NV36GL [Quadro FX 1100] + +pci:v000010DEd00000360* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000361* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000361sv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge (PowerEdge R805 MCP55 LPC Bridge) + +pci:v000010DEd00000362* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000362sv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge (KN9 series mainboard) + +pci:v000010DEd00000363* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000364* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000364sv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge (PowerEdge R805 MCP55 LPC Bridge) + +pci:v000010DEd00000365* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000366* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000367* + ID_MODEL_FROM_DATABASE=MCP55 LPC Bridge + +pci:v000010DEd00000368* + ID_MODEL_FROM_DATABASE=MCP55 SMBus Controller + +pci:v000010DEd00000368sv00001028sd0000020C* + ID_MODEL_FROM_DATABASE=MCP55 SMBus Controller (PowerEdge M605 MCP55 SMBus) + +pci:v000010DEd00000368sv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 SMBus Controller (PowerEdge R805 MCP55 SMBus) + +pci:v000010DEd00000368sv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 SMBus Controller (KN9 series mainboard) + +pci:v000010DEd00000369* + ID_MODEL_FROM_DATABASE=MCP55 Memory Controller + +pci:v000010DEd00000369sv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 Memory Controller (KN9 series mainboard) + +pci:v000010DEd0000036A* + ID_MODEL_FROM_DATABASE=MCP55 Memory Controller + +pci:v000010DEd0000036B* + ID_MODEL_FROM_DATABASE=MCP55 SMU + +pci:v000010DEd0000036C* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller + +pci:v000010DEd0000036Csv00001028sd0000020C* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (PowerEdge M605 MCP55 USB Controller) + +pci:v000010DEd0000036Csv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (PowerEdge R805 MCP55 USB Controller) + +pci:v000010DEd0000036Csv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (KN9 series mainboard) + +pci:v000010DEd0000036D* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller + +pci:v000010DEd0000036Dsv00001028sd0000020C* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (PowerEdge M605 MCP55 USB Controller) + +pci:v000010DEd0000036Dsv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (PowerEdge R805 MCP55 USB Controller) + +pci:v000010DEd0000036Dsv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 USB Controller (KN9 series mainboard) + +pci:v000010DEd0000036E* + ID_MODEL_FROM_DATABASE=MCP55 IDE + +pci:v000010DEd0000036Esv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 IDE (KN9 series mainboard) + +pci:v000010DEd00000370* + ID_MODEL_FROM_DATABASE=MCP55 PCI bridge + +pci:v000010DEd00000371* + ID_MODEL_FROM_DATABASE=MCP55 High Definition Audio + +pci:v000010DEd00000371sv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 High Definition Audio (KN9 series mainboard) + +pci:v000010DEd00000372* + ID_MODEL_FROM_DATABASE=MCP55 Ethernet + +pci:v000010DEd00000373* + ID_MODEL_FROM_DATABASE=MCP55 Ethernet + +pci:v000010DEd00000373sv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 Ethernet (KN9 series mainboard) + +pci:v000010DEd00000374* + ID_MODEL_FROM_DATABASE=MCP55 PCI Express bridge + +pci:v000010DEd00000375* + ID_MODEL_FROM_DATABASE=MCP55 PCI Express bridge + +pci:v000010DEd00000376* + ID_MODEL_FROM_DATABASE=MCP55 PCI Express bridge + +pci:v000010DEd00000377* + ID_MODEL_FROM_DATABASE=MCP55 PCI Express bridge + +pci:v000010DEd00000378* + ID_MODEL_FROM_DATABASE=MCP55 PCI Express bridge + +pci:v000010DEd0000037A* + ID_MODEL_FROM_DATABASE=MCP55 Memory Controller + +pci:v000010DEd0000037E* + ID_MODEL_FROM_DATABASE=MCP55 SATA Controller + +pci:v000010DEd0000037F* + ID_MODEL_FROM_DATABASE=MCP55 SATA Controller + +pci:v000010DEd0000037Fsv00001028sd00000221* + ID_MODEL_FROM_DATABASE=MCP55 SATA Controller (PowerEdge R805 MCP55 SATA Controller) + +pci:v000010DEd0000037Fsv0000147Bsd00001C24* + ID_MODEL_FROM_DATABASE=MCP55 SATA Controller (KN9 series mainboard) + +pci:v000010DEd0000038B* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7650 GS] + +pci:v000010DEd00000390* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7650 GS] + +pci:v000010DEd00000391* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GT] + +pci:v000010DEd00000391sv00001458sd00003427* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GT] (GV-NX76T128D-RH) + +pci:v000010DEd00000391sv00001462sd00000452* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GT] (NX7600GT-VT2D256E) + +pci:v000010DEd00000392* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GS] + +pci:v000010DEd00000392sv00001462sd00000622* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 GS] (NX7600GS-T2D256EH) + +pci:v000010DEd00000393* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7300 GT] + +pci:v000010DEd00000393sv000010DEsd00000412* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7300 GT] (NX7300GT-TD256EH) + +pci:v000010DEd00000393sv00001462sd00000412* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7300 GT] (NX7300GT-TD256EH) + +pci:v000010DEd00000394* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7600 LE] + +pci:v000010DEd00000395* + ID_MODEL_FROM_DATABASE=G73 [GeForce 7300 GT] + +pci:v000010DEd00000396* + ID_MODEL_FROM_DATABASE=G73 + +pci:v000010DEd00000397* + ID_MODEL_FROM_DATABASE=G73M [GeForce Go 7700] + +pci:v000010DEd00000398* + ID_MODEL_FROM_DATABASE=G73M [GeForce Go 7600] + +pci:v000010DEd00000398sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=G73M [GeForce Go 7600] (Acer 9814 WKMI) + +pci:v000010DEd00000399* + ID_MODEL_FROM_DATABASE=G73M [GeForce Go 7600 GT] + +pci:v000010DEd0000039A* + ID_MODEL_FROM_DATABASE=G73M [Quadro NVS 300M] + +pci:v000010DEd0000039B* + ID_MODEL_FROM_DATABASE=G73M [GeForce Go 7900 SE] + +pci:v000010DEd0000039C* + ID_MODEL_FROM_DATABASE=G73GLM [Quadro FX 550M] + +pci:v000010DEd0000039Csv000010DEsd0000039C* + ID_MODEL_FROM_DATABASE=G73GLM [Quadro FX 550M] (Quadro FX 560M) + +pci:v000010DEd0000039D* + ID_MODEL_FROM_DATABASE=G73 + +pci:v000010DEd0000039E* + ID_MODEL_FROM_DATABASE=G73GL [Quadro FX 560] + +pci:v000010DEd0000039F* + ID_MODEL_FROM_DATABASE=G73 + +pci:v000010DEd000003A0* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A1* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A2* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A3* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A4* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A5* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A6* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A7* + ID_MODEL_FROM_DATABASE=C55 Host Bridge + +pci:v000010DEd000003A8* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003A9* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AA* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AB* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AC* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AD* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AE* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003AF* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B0* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B1* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B2* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B3* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B4* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B5* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B6* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003B7* + ID_MODEL_FROM_DATABASE=C55 PCI Express bridge + +pci:v000010DEd000003B8* + ID_MODEL_FROM_DATABASE=C55 PCI Express bridge + +pci:v000010DEd000003B9* + ID_MODEL_FROM_DATABASE=C55 PCI Express bridge + +pci:v000010DEd000003BA* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003BB* + ID_MODEL_FROM_DATABASE=C55 PCI Express bridge + +pci:v000010DEd000003BC* + ID_MODEL_FROM_DATABASE=C55 Memory Controller + +pci:v000010DEd000003D0* + ID_MODEL_FROM_DATABASE=C61 [GeForce 6150SE nForce 430] + +pci:v000010DEd000003D0sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=C61 [GeForce 6150SE nForce 430] (Inspiron 531) + +pci:v000010DEd000003D1* + ID_MODEL_FROM_DATABASE=C61 [GeForce 6100 nForce 405] + +pci:v000010DEd000003D2* + ID_MODEL_FROM_DATABASE=C61 [GeForce 6100 nForce 400] + +pci:v000010DEd000003D5* + ID_MODEL_FROM_DATABASE=C61 [GeForce 6100 nForce 420] + +pci:v000010DEd000003D6* + ID_MODEL_FROM_DATABASE=C61 [GeForce 7025 / nForce 630a] + +pci:v000010DEd000003E0* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge + +pci:v000010DEd000003E0sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge (Inspiron 531) + +pci:v000010DEd000003E0sv00001849sd000003E0* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge (939NF6G-VSTA Board) + +pci:v000010DEd000003E1* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge + +pci:v000010DEd000003E1sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge (M4N68T series motherboard) + +pci:v000010DEd000003E2* + ID_MODEL_FROM_DATABASE=MCP61 Host Bridge + +pci:v000010DEd000003E2sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 Host Bridge (M4N68T series motherboard) + +pci:v000010DEd000003E3* + ID_MODEL_FROM_DATABASE=MCP61 LPC Bridge + +pci:v000010DEd000003E4* + ID_MODEL_FROM_DATABASE=MCP61 High Definition Audio + +pci:v000010DEd000003E5* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet + +pci:v000010DEd000003E6* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet + +pci:v000010DEd000003E7* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller + +pci:v000010DEd000003E8* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge + +pci:v000010DEd000003E8sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge (Inspiron 531) + +pci:v000010DEd000003E8sv00001849sd000003E8* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge (939NF6G-VSTA Board) + +pci:v000010DEd000003E9* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge + +pci:v000010DEd000003E9sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge (Inspiron 531) + +pci:v000010DEd000003E9sv00001849sd000003E9* + ID_MODEL_FROM_DATABASE=MCP61 PCI Express bridge (939NF6G-VSTA Board) + +pci:v000010DEd000003EA* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller + +pci:v000010DEd000003EAsv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller (Inspiron 531) + +pci:v000010DEd000003EAsv00001849sd000003EA* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller (939NF6G-VSTA Board) + +pci:v000010DEd000003EB* + ID_MODEL_FROM_DATABASE=MCP61 SMBus + +pci:v000010DEd000003EBsv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 SMBus (Inspiron 531) + +pci:v000010DEd000003EBsv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 SMBus (M4N68T series motherboard) + +pci:v000010DEd000003EBsv00001849sd000003EB* + ID_MODEL_FROM_DATABASE=MCP61 SMBus (939NF6G-VSTA Board) + +pci:v000010DEd000003EC* + ID_MODEL_FROM_DATABASE=MCP61 IDE + +pci:v000010DEd000003ECsv00001025sd00000392* + ID_MODEL_FROM_DATABASE=MCP61 IDE (ET1350) + +pci:v000010DEd000003ECsv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 IDE (Inspiron 531) + +pci:v000010DEd000003ECsv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 IDE (M4N68T series motherboard) + +pci:v000010DEd000003ECsv00001849sd000003EC* + ID_MODEL_FROM_DATABASE=MCP61 IDE (939NF6G-VSTA Board) + +pci:v000010DEd000003EE* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet + +pci:v000010DEd000003EF* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet + +pci:v000010DEd000003EFsv00001025sd00008000* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet (ET1350) + +pci:v000010DEd000003EFsv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet (Inspiron 531) + +pci:v000010DEd000003EFsv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet (M4N68T series motherboard) + +pci:v000010DEd000003EFsv00001849sd000003EF* + ID_MODEL_FROM_DATABASE=MCP61 Ethernet (939NF6G-VSTA Board) + +pci:v000010DEd000003F0* + ID_MODEL_FROM_DATABASE=MCP61 High Definition Audio + +pci:v000010DEd000003F0sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 High Definition Audio (Inspiron 531) + +pci:v000010DEd000003F0sv00001043sd00008415* + ID_MODEL_FROM_DATABASE=MCP61 High Definition Audio (M4N68T series motherboard) + +pci:v000010DEd000003F0sv00001849sd00000888* + ID_MODEL_FROM_DATABASE=MCP61 High Definition Audio (939NF6G-VSTA Board) + +pci:v000010DEd000003F1* + ID_MODEL_FROM_DATABASE=MCP61 USB 1.1 Controller + +pci:v000010DEd000003F1sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 USB 1.1 Controller (Inspiron 531) + +pci:v000010DEd000003F1sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 USB 1.1 Controller (M4N68T series motherboard) + +pci:v000010DEd000003F1sv00001849sd000003F1* + ID_MODEL_FROM_DATABASE=MCP61 USB 1.1 Controller (939NF6G-VSTA Board) + +pci:v000010DEd000003F2* + ID_MODEL_FROM_DATABASE=MCP61 USB 2.0 Controller + +pci:v000010DEd000003F2sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 USB 2.0 Controller (Inspiron 531) + +pci:v000010DEd000003F2sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 USB 2.0 Controller (M4N68T series motherboard) + +pci:v000010DEd000003F2sv00001849sd000003F2* + ID_MODEL_FROM_DATABASE=MCP61 USB 2.0 Controller (939NF6G-VSTA Board) + +pci:v000010DEd000003F3* + ID_MODEL_FROM_DATABASE=MCP61 PCI bridge + +pci:v000010DEd000003F3sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 PCI bridge (Inspiron 531) + +pci:v000010DEd000003F3sv00001849sd000003F3* + ID_MODEL_FROM_DATABASE=MCP61 PCI bridge (939NF6G-VSTA Board) + +pci:v000010DEd000003F4* + ID_MODEL_FROM_DATABASE=MCP61 SMU + +pci:v000010DEd000003F5* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller + +pci:v000010DEd000003F5sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller (Inspiron 531) + +pci:v000010DEd000003F5sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller (M4N68T series motherboard) + +pci:v000010DEd000003F5sv00001849sd000003EB* + ID_MODEL_FROM_DATABASE=MCP61 Memory Controller (939NF6G-VSTA Board) + +pci:v000010DEd000003F6* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller + +pci:v000010DEd000003F6sv00001028sd0000020E* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller (Inspiron 531) + +pci:v000010DEd000003F6sv00001043sd000083A4* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller (M4N68T series motherboard) + +pci:v000010DEd000003F6sv00001849sd000003F6* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller (939NF6G-VSTA Board) + +pci:v000010DEd000003F7* + ID_MODEL_FROM_DATABASE=MCP61 SATA Controller + +pci:v000010DEd00000400* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GTS] + +pci:v000010DEd00000400sv00001043sd00008241* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GTS] (EN8600GTS) + +pci:v000010DEd00000401* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GT] + +pci:v000010DEd00000402* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GT] + +pci:v000010DEd00000402sv00001458sd00003455* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GT] (GV-NX86T512H) + +pci:v000010DEd00000402sv00001462sd00000910* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GT] (NX8600GT-T2D256EZ) + +pci:v000010DEd00000403* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8600 GS] + +pci:v000010DEd00000404* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8400 GS] + +pci:v000010DEd00000404sv00001462sd00001230* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8400 GS] (NX8400GS-TD256E) + +pci:v000010DEd00000405* + ID_MODEL_FROM_DATABASE=G84M [GeForce 9500M GS] + +pci:v000010DEd00000406* + ID_MODEL_FROM_DATABASE=G84 [GeForce 8300 GS] + +pci:v000010DEd00000407* + ID_MODEL_FROM_DATABASE=G84M [GeForce 8600M GT] + +pci:v000010DEd00000408* + ID_MODEL_FROM_DATABASE=G84M [GeForce 9650M GS] + +pci:v000010DEd00000409* + ID_MODEL_FROM_DATABASE=G84M [GeForce 8700M GT] + +pci:v000010DEd0000040A* + ID_MODEL_FROM_DATABASE=G84GL [Quadro FX 370] + +pci:v000010DEd0000040B* + ID_MODEL_FROM_DATABASE=G84GLM [Quadro NVS 320M] + +pci:v000010DEd0000040C* + ID_MODEL_FROM_DATABASE=G84GLM [Quadro FX 570M] + +pci:v000010DEd0000040Csv000017AAsd000020D9* + ID_MODEL_FROM_DATABASE=G84GLM [Quadro FX 570M] (ThinkPad T61p) + +pci:v000010DEd0000040D* + ID_MODEL_FROM_DATABASE=G84GLM [Quadro FX 1600M] + +pci:v000010DEd0000040E* + ID_MODEL_FROM_DATABASE=G84GL [Quadro FX 570] + +pci:v000010DEd0000040F* + ID_MODEL_FROM_DATABASE=G84GL [Quadro FX 1700] + +pci:v000010DEd00000410* + ID_MODEL_FROM_DATABASE=G92 [GeForce GT 330] + +pci:v000010DEd00000414* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT] + +pci:v000010DEd00000420* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8400 SE] + +pci:v000010DEd00000421* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8500 GT] + +pci:v000010DEd00000421sv00001462sd00000960* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8500 GT] (NX8500GT-TD512EH/M2) + +pci:v000010DEd00000422* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8400 GS] + +pci:v000010DEd00000423* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8300 GS] + +pci:v000010DEd00000424* + ID_MODEL_FROM_DATABASE=G86 [GeForce 8400 GS] + +pci:v000010DEd00000425* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8600M GS] + +pci:v000010DEd00000425sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8600M GS] (Aspire 5920G) + +pci:v000010DEd00000426* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8400M GT] + +pci:v000010DEd00000427* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8400M GS] + +pci:v000010DEd00000427sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8400M GS] (Pavilion dv6700) + +pci:v000010DEd00000427sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8400M GS] (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000428* + ID_MODEL_FROM_DATABASE=G86M [GeForce 8400M G] + +pci:v000010DEd00000429* + ID_MODEL_FROM_DATABASE=G86M [Quadro NVS 140M] + +pci:v000010DEd00000429sv000017AAsd000020D8* + ID_MODEL_FROM_DATABASE=G86M [Quadro NVS 140M] (ThinkPad T61) + +pci:v000010DEd0000042A* + ID_MODEL_FROM_DATABASE=G86M [Quadro NVS 130M] + +pci:v000010DEd0000042B* + ID_MODEL_FROM_DATABASE=G86M [Quadro NVS 135M] + +pci:v000010DEd0000042C* + ID_MODEL_FROM_DATABASE=G86 [GeForce 9400 GT] + +pci:v000010DEd0000042D* + ID_MODEL_FROM_DATABASE=G86GLM [Quadro FX 360M] + +pci:v000010DEd0000042E* + ID_MODEL_FROM_DATABASE=G86M [GeForce 9300M G] + +pci:v000010DEd0000042F* + ID_MODEL_FROM_DATABASE=G86 [Quadro NVS 290] + +pci:v000010DEd00000440* + ID_MODEL_FROM_DATABASE=MCP65 LPC Bridge + +pci:v000010DEd00000441* + ID_MODEL_FROM_DATABASE=MCP65 LPC Bridge + +pci:v000010DEd00000442* + ID_MODEL_FROM_DATABASE=MCP65 LPC Bridge + +pci:v000010DEd00000442sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 LPC Bridge (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000443* + ID_MODEL_FROM_DATABASE=MCP65 LPC Bridge + +pci:v000010DEd00000444* + ID_MODEL_FROM_DATABASE=MCP65 Memory Controller + +pci:v000010DEd00000444sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 Memory Controller (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000445* + ID_MODEL_FROM_DATABASE=MCP65 Memory Controller + +pci:v000010DEd00000446* + ID_MODEL_FROM_DATABASE=MCP65 SMBus + +pci:v000010DEd00000446sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 SMBus (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000447* + ID_MODEL_FROM_DATABASE=MCP65 SMU + +pci:v000010DEd00000447sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 SMU (Pavilion dv9500/9600/9700 series) + +pci:v000010DEd00000448* + ID_MODEL_FROM_DATABASE=MCP65 IDE + +pci:v000010DEd00000448sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 IDE (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000449* + ID_MODEL_FROM_DATABASE=MCP65 PCI bridge + +pci:v000010DEd00000449sv000010DEsd0000CB84* + ID_MODEL_FROM_DATABASE=MCP65 PCI bridge (HP Pavilion dv9668eg Laptop) + +pci:v000010DEd0000044A* + ID_MODEL_FROM_DATABASE=MCP65 High Definition Audio + +pci:v000010DEd0000044Asv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 High Definition Audio (Pavilion dv9668eg Laptop) + +pci:v000010DEd0000044B* + ID_MODEL_FROM_DATABASE=MCP65 High Definition Audio + +pci:v000010DEd0000044C* + ID_MODEL_FROM_DATABASE=MCP65 AHCI Controller + +pci:v000010DEd0000044D* + ID_MODEL_FROM_DATABASE=MCP65 AHCI Controller + +pci:v000010DEd0000044E* + ID_MODEL_FROM_DATABASE=MCP65 AHCI Controller + +pci:v000010DEd0000044F* + ID_MODEL_FROM_DATABASE=MCP65 AHCI Controller + +pci:v000010DEd00000450* + ID_MODEL_FROM_DATABASE=MCP65 Ethernet + +pci:v000010DEd00000450sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 Ethernet (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000451* + ID_MODEL_FROM_DATABASE=MCP65 Ethernet + +pci:v000010DEd00000452* + ID_MODEL_FROM_DATABASE=MCP65 Ethernet + +pci:v000010DEd00000453* + ID_MODEL_FROM_DATABASE=MCP65 Ethernet + +pci:v000010DEd00000454* + ID_MODEL_FROM_DATABASE=MCP65 USB 1.1 OHCI Controller + +pci:v000010DEd00000454sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 USB 1.1 OHCI Controller (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000455* + ID_MODEL_FROM_DATABASE=MCP65 USB 2.0 EHCI Controller + +pci:v000010DEd00000455sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 USB 2.0 EHCI Controller (Pavilion dv9668eg Laptop) + +pci:v000010DEd00000456* + ID_MODEL_FROM_DATABASE=MCP65 USB Controller + +pci:v000010DEd00000457* + ID_MODEL_FROM_DATABASE=MCP65 USB Controller + +pci:v000010DEd00000458* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd00000458sv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd00000459* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd00000459sv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd0000045A* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd0000045Asv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd0000045B* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd0000045Bsv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP65 PCI Express bridge + +pci:v000010DEd0000045C* + ID_MODEL_FROM_DATABASE=MCP65 SATA Controller + +pci:v000010DEd0000045D* + ID_MODEL_FROM_DATABASE=MCP65 SATA Controller + +pci:v000010DEd0000045Dsv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=MCP65 SATA Controller (Pavilion dv9668eg Laptop) + +pci:v000010DEd0000045E* + ID_MODEL_FROM_DATABASE=MCP65 SATA Controller + +pci:v000010DEd0000045F* + ID_MODEL_FROM_DATABASE=MCP65 SATA Controller + +pci:v000010DEd00000531* + ID_MODEL_FROM_DATABASE=C67 [GeForce 7150M / nForce 630M] + +pci:v000010DEd00000533* + ID_MODEL_FROM_DATABASE=C67 [GeForce 7000M / nForce 610M] + +pci:v000010DEd0000053A* + ID_MODEL_FROM_DATABASE=C68 [GeForce 7050 PV / nForce 630a] + +pci:v000010DEd0000053B* + ID_MODEL_FROM_DATABASE=C68 [GeForce 7050 PV / nForce 630a] + +pci:v000010DEd0000053Bsv00001043sd00008308* + ID_MODEL_FROM_DATABASE=C68 [GeForce 7050 PV / nForce 630a] (M2N68-AM Motherbord) + +pci:v000010DEd0000053E* + ID_MODEL_FROM_DATABASE=C68 [GeForce 7025 / nForce 630a] + +pci:v000010DEd00000541* + ID_MODEL_FROM_DATABASE=MCP67 Memory Controller + +pci:v000010DEd00000542* + ID_MODEL_FROM_DATABASE=MCP67 SMBus + +pci:v000010DEd00000542sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 SMBus (M2N68-AM Motherbord) + +pci:v000010DEd00000543* + ID_MODEL_FROM_DATABASE=MCP67 Co-processor + +pci:v000010DEd00000547* + ID_MODEL_FROM_DATABASE=MCP67 Memory Controller + +pci:v000010DEd00000547sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 Memory Controller (M2N68-AM Motherbord) + +pci:v000010DEd00000547sv00001849sd00000547* + ID_MODEL_FROM_DATABASE=MCP67 Memory Controller (ALiveNF7G-HDready) + +pci:v000010DEd00000548* + ID_MODEL_FROM_DATABASE=MCP67 ISA Bridge + +pci:v000010DEd00000548sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 ISA Bridge (M2N68-AM Motherboard) + +pci:v000010DEd0000054C* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet + +pci:v000010DEd0000054Csv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet (M2N68-AM Motherbord) + +pci:v000010DEd0000054Csv00001849sd0000054C* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet (ALiveNF7G-HDready, MCP67 Gigabit Ethernet) + +pci:v000010DEd0000054D* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet + +pci:v000010DEd0000054E* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet + +pci:v000010DEd0000054F* + ID_MODEL_FROM_DATABASE=MCP67 Ethernet + +pci:v000010DEd00000550* + ID_MODEL_FROM_DATABASE=MCP67 AHCI Controller + +pci:v000010DEd00000550sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 AHCI Controller (M2N68-AM Motherboard) + +pci:v000010DEd00000554* + ID_MODEL_FROM_DATABASE=MCP67 AHCI Controller + +pci:v000010DEd00000554sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 AHCI Controller (M2N68-AM Motherboard) + +pci:v000010DEd00000555* + ID_MODEL_FROM_DATABASE=MCP67 SATA Controller + +pci:v000010DEd00000555sv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 SATA Controller (M2N68-AM Motherboard) + +pci:v000010DEd0000055C* + ID_MODEL_FROM_DATABASE=MCP67 High Definition Audio + +pci:v000010DEd0000055Csv00001043sd00008290* + ID_MODEL_FROM_DATABASE=MCP67 High Definition Audio (M2N68-AM Motherboard) + +pci:v000010DEd0000055D* + ID_MODEL_FROM_DATABASE=MCP67 High Definition Audio + +pci:v000010DEd0000055E* + ID_MODEL_FROM_DATABASE=MCP67 OHCI USB 1.1 Controller + +pci:v000010DEd0000055Esv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 OHCI USB 1.1 Controller (M2N68-AM Motherboard) + +pci:v000010DEd0000055F* + ID_MODEL_FROM_DATABASE=MCP67 EHCI USB 2.0 Controller + +pci:v000010DEd0000055Fsv00001043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 EHCI USB 2.0 Controller (M2N68-AM Motherboard) + +pci:v000010DEd00000560* + ID_MODEL_FROM_DATABASE=MCP67 IDE Controller + +pci:v000010DEd00000560sv0000F043sd00008308* + ID_MODEL_FROM_DATABASE=MCP67 IDE Controller (M2N68-AM Motherboard) + +pci:v000010DEd00000561* + ID_MODEL_FROM_DATABASE=MCP67 PCI Bridge + +pci:v000010DEd00000562* + ID_MODEL_FROM_DATABASE=MCP67 PCI Express Bridge + +pci:v000010DEd00000562sv00001849sd00000562* + ID_MODEL_FROM_DATABASE=MCP67 PCI Express Bridge (ALiveNF7G-HDready) + +pci:v000010DEd00000563* + ID_MODEL_FROM_DATABASE=MCP67 PCI Express Bridge + +pci:v000010DEd00000568* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller + +pci:v000010DEd00000568sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (Pavilion p6310f) + +pci:v000010DEd00000568sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (M3N72-D) + +pci:v000010DEd00000568sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K9N2GM-FIH) + +pci:v000010DEd00000568sv00001849sd00000568* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K10N78FullHD-hSLI R3.0 Memory Controller) + +pci:v000010DEd00000569* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge + +pci:v000010DEd00000569sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (Pavilion p6310f) + +pci:v000010DEd00000569sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (M3N72-D) + +pci:v000010DEd00000569sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K9N2GM-FIH) + +pci:v000010DEd00000569sv00001849sd00000569* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K10N78FullHD-hSLI R3.0 PCI Express Bridge) + +pci:v000010DEd0000056A* + ID_MODEL_FROM_DATABASE=MCP73 [nForce 630i] USB 2.0 Controller (EHCI) + +pci:v000010DEd0000056Asv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 [nForce 630i] USB 2.0 Controller (EHCI) (MCP73PVT-SM) + +pci:v000010DEd0000056Asv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 [nForce 630i] USB 2.0 Controller (EHCI) (I-N73V motherboard) + +pci:v000010DEd0000056C* + ID_MODEL_FROM_DATABASE=MCP73 IDE Controller + +pci:v000010DEd0000056Csv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 IDE Controller (MCP73PVT-SM) + +pci:v000010DEd0000056Csv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 IDE Controller (I-N73V motherboard) + +pci:v000010DEd0000056Csv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 IDE Controller (JW-IN7150-HD) + +pci:v000010DEd0000056D* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge + +pci:v000010DEd0000056Dsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73PVT-SM) + +pci:v000010DEd0000056Dsv000010DEsd0000CB73* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCI Bridge) + +pci:v000010DEd0000056E* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge + +pci:v000010DEd0000056Esv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73PVT-SM) + +pci:v000010DEd0000056Esv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCIe x16 port) + +pci:v000010DEd0000056F* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge + +pci:v000010DEd0000056Fsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73PVT-SM) + +pci:v000010DEd0000056Fsv000010DEsd00000000* + ID_MODEL_FROM_DATABASE=MCP73 PCI Express bridge (MCP73 PCIe x1 port) + +pci:v000010DEd000005B1* + ID_MODEL_FROM_DATABASE=NF200 PCIe 2.0 switch + +pci:v000010DEd000005B8* + ID_MODEL_FROM_DATABASE=NF200 PCIe 2.0 switch for GTX 295 + +pci:v000010DEd000005BE* + ID_MODEL_FROM_DATABASE=NF200 PCIe 2.0 switch for Quadro Plex S4 / Tesla S870 / Tesla S1070 / Tesla S2050 + +pci:v000010DEd000005E0* + ID_MODEL_FROM_DATABASE=GT200b [GeForce GTX 295] + +pci:v000010DEd000005E1* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 280] + +pci:v000010DEd000005E2* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 260] + +pci:v000010DEd000005E3* + ID_MODEL_FROM_DATABASE=GT200b [GeForce GTX 285] + +pci:v000010DEd000005E3sv00001682sd00002490* + ID_MODEL_FROM_DATABASE=GT200b [GeForce GTX 285] (GX-285N-ZDF) + +pci:v000010DEd000005E6* + ID_MODEL_FROM_DATABASE=GT200b [GeForce GTX 275] + +pci:v000010DEd000005E7* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] + +pci:v000010DEd000005E7sv000010DEsd00000595* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] (Tesla T10 Processor) + +pci:v000010DEd000005E7sv000010DEsd0000068F* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] (Tesla T10 Processor) + +pci:v000010DEd000005E7sv000010DEsd00000697* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] (Tesla M1060) + +pci:v000010DEd000005E7sv000010DEsd00000714* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] (Tesla M1060) + +pci:v000010DEd000005E7sv000010DEsd00000743* + ID_MODEL_FROM_DATABASE=GT200GL [Tesla C1060 / M1060] (Tesla M1060) + +pci:v000010DEd000005EA* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 260] + +pci:v000010DEd000005EB* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 295] + +pci:v000010DEd000005ED* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro Plex 2200 D2] + +pci:v000010DEd000005F1* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 280] + +pci:v000010DEd000005F2* + ID_MODEL_FROM_DATABASE=GT200 [GeForce GTX 260] + +pci:v000010DEd000005F8* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro Plex 2200 S4] + +pci:v000010DEd000005F9* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro CX] + +pci:v000010DEd000005FD* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro FX 5800] + +pci:v000010DEd000005FE* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro FX 4800] + +pci:v000010DEd000005FF* + ID_MODEL_FROM_DATABASE=GT200GL [Quadro FX 3800] + +pci:v000010DEd00000600* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GTS 512] + +pci:v000010DEd00000601* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT] + +pci:v000010DEd00000602* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GT] + +pci:v000010DEd00000603* + ID_MODEL_FROM_DATABASE=G92 [GeForce GT 230 OEM] + +pci:v000010DEd00000604* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GX2] + +pci:v000010DEd00000605* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT] + +pci:v000010DEd00000606* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GS] + +pci:v000010DEd00000607* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 240] + +pci:v000010DEd00000608* + ID_MODEL_FROM_DATABASE=G92M [GeForce 9800M GTX] + +pci:v000010DEd00000609* + ID_MODEL_FROM_DATABASE=G92M [GeForce 8800M GTS] + +pci:v000010DEd00000609sv0000106Bsd000000A7* + ID_MODEL_FROM_DATABASE=G92M [GeForce 8800M GTS] (GeForce 8800 GS) + +pci:v000010DEd0000060A* + ID_MODEL_FROM_DATABASE=G92M [GeForce GTX 280M] + +pci:v000010DEd0000060B* + ID_MODEL_FROM_DATABASE=G92M [GeForce 9800M GT] + +pci:v000010DEd0000060C* + ID_MODEL_FROM_DATABASE=G92M [GeForce 8800M GTX] + +pci:v000010DEd0000060D* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GS] + +pci:v000010DEd0000060F* + ID_MODEL_FROM_DATABASE=G92M [GeForce GTX 285M] + +pci:v000010DEd00000610* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9600 GSO] + +pci:v000010DEd00000610sv00001682sd00002385* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9600 GSO] (GeForce 9600 GSO 768mb) + +pci:v000010DEd00000611* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GT] + +pci:v000010DEd00000611sv0000107Dsd00002AB0* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GT] (Winfast PX8800 GT PCI-E) + +pci:v000010DEd00000611sv000019DAsd00001040* + ID_MODEL_FROM_DATABASE=G92 [GeForce 8800 GT] (ZT-88TES2P-FSP) + +pci:v000010DEd00000612* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GTX / 9800 GTX+] + +pci:v000010DEd00000613* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GTX+] + +pci:v000010DEd00000614* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT] + +pci:v000010DEd00000614sv0000107Dsd00002AB3* + ID_MODEL_FROM_DATABASE=G92 [GeForce 9800 GT] (WinFast PX9800 GT (S-Fanpipe)) + +pci:v000010DEd00000615* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 250] + +pci:v000010DEd00000615sv00003842sd00001150* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 250] (GeForce GTS 250 P/N 512-P3-1150-TR) + +pci:v000010DEd00000615sv00003842sd00001151* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 250] (GeForce GTS 250 P/N 512-P3-1151-TR) + +pci:v000010DEd00000615sv00003842sd00001155* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 250] (GeForce GTS 250 P/N 01G-P3-1155-TR) + +pci:v000010DEd00000615sv00003842sd00001156* + ID_MODEL_FROM_DATABASE=G92 [GeForce GTS 250] (GeForce GTS 250 P/N 01G-P3-1156-TR) + +pci:v000010DEd00000617* + ID_MODEL_FROM_DATABASE=G92M [GeForce 9800M GTX] + +pci:v000010DEd00000618* + ID_MODEL_FROM_DATABASE=G92M [GeForce GTX 260M] + +pci:v000010DEd00000619* + ID_MODEL_FROM_DATABASE=G92GL [Quadro FX 4700 X2] + +pci:v000010DEd0000061A* + ID_MODEL_FROM_DATABASE=G92GL [Quadro FX 3700] + +pci:v000010DEd0000061B* + ID_MODEL_FROM_DATABASE=G92GL [Quadro VX 200] + +pci:v000010DEd0000061C* + ID_MODEL_FROM_DATABASE=G92GLM [Quadro FX 3600M] + +pci:v000010DEd0000061D* + ID_MODEL_FROM_DATABASE=G92GLM [Quadro FX 2800M] + +pci:v000010DEd0000061E* + ID_MODEL_FROM_DATABASE=G92GLM [Quadro FX 3700M] + +pci:v000010DEd0000061F* + ID_MODEL_FROM_DATABASE=G92GLM [Quadro FX 3800M] + +pci:v000010DEd00000620* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9800 GT] + +pci:v000010DEd00000621* + ID_MODEL_FROM_DATABASE=G94 [GeForce GT 230] + +pci:v000010DEd00000622* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] + +pci:v000010DEd00000622sv0000107Dsd00002AC1* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] (WinFast PX9600GT 1024MB) + +pci:v000010DEd00000622sv00001458sd00003481* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] (GV-NX96T512HP) + +pci:v000010DEd00000623* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GS] + +pci:v000010DEd00000624* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT Green Edition] + +pci:v000010DEd00000625* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GSO 512] + +pci:v000010DEd00000626* + ID_MODEL_FROM_DATABASE=G94 [GeForce GT 130] + +pci:v000010DEd00000627* + ID_MODEL_FROM_DATABASE=G94 [GeForce GT 140] + +pci:v000010DEd00000628* + ID_MODEL_FROM_DATABASE=G94M [GeForce 9800M GTS] + +pci:v000010DEd0000062A* + ID_MODEL_FROM_DATABASE=G94M [GeForce 9700M GTS] + +pci:v000010DEd0000062B* + ID_MODEL_FROM_DATABASE=G94M [GeForce 9800M GS] + +pci:v000010DEd0000062C* + ID_MODEL_FROM_DATABASE=G94M [GeForce 9800M GTS] + +pci:v000010DEd0000062D* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] + +pci:v000010DEd0000062E* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] + +pci:v000010DEd0000062Esv0000106Bsd00000605* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] (GeForce GT 130) + +pci:v000010DEd0000062F* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9800 S] + +pci:v000010DEd00000630* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] + +pci:v000010DEd00000631* + ID_MODEL_FROM_DATABASE=G94M [GeForce GTS 160M] + +pci:v000010DEd00000632* + ID_MODEL_FROM_DATABASE=G94M [GeForce GTS 150M] + +pci:v000010DEd00000633* + ID_MODEL_FROM_DATABASE=G94 [GeForce GT 220] + +pci:v000010DEd00000635* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GSO] + +pci:v000010DEd00000637* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GT] + +pci:v000010DEd00000638* + ID_MODEL_FROM_DATABASE=G94GL [Quadro FX 1800] + +pci:v000010DEd0000063A* + ID_MODEL_FROM_DATABASE=G94GLM [Quadro FX 2700M] + +pci:v000010DEd0000063F* + ID_MODEL_FROM_DATABASE=G94 [GeForce 9600 GE] + +pci:v000010DEd00000640* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GT] + +pci:v000010DEd00000641* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9400 GT] + +pci:v000010DEd00000641sv00001682sd00004009* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9400 GT] (PV-T94G-ZAFG) + +pci:v000010DEd00000642* + ID_MODEL_FROM_DATABASE=G96 [D9M-10] + +pci:v000010DEd00000643* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GT] + +pci:v000010DEd00000644* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS] + +pci:v000010DEd00000644sv0000174Bsd00009600* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS] (Geforce 9500GS 512M DDR2 V/D/HDMI) + +pci:v000010DEd00000645* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GS] + +pci:v000010DEd00000646* + ID_MODEL_FROM_DATABASE=G96 [GeForce GT 120] + +pci:v000010DEd00000647* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9600M GT] + +pci:v000010DEd00000648* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9600M GS] + +pci:v000010DEd00000649* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9600M GT] + +pci:v000010DEd00000649sv00001043sd0000202D* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9600M GT] (GeForce GT 220M) + +pci:v000010DEd0000064A* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9700M GT] + +pci:v000010DEd0000064B* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9500M G] + +pci:v000010DEd0000064C* + ID_MODEL_FROM_DATABASE=G96M [GeForce 9650M GT] + +pci:v000010DEd0000064D* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9600 GT] + +pci:v000010DEd0000064E* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9600 GT / 9800 GT] + +pci:v000010DEd00000651* + ID_MODEL_FROM_DATABASE=G96M [GeForce G 110M] + +pci:v000010DEd00000652* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 130M] + +pci:v000010DEd00000652sv0000152Dsd00000850* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 130M] (GeForce GT 240M LE) + +pci:v000010DEd00000653* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 120M] + +pci:v000010DEd00000654* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 220M] + +pci:v000010DEd00000654sv00001043sd000014A2* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 220M] (GeForce GT 320M) + +pci:v000010DEd00000654sv00001043sd000014D2* + ID_MODEL_FROM_DATABASE=G96M [GeForce GT 220M] (GeForce GT 320M) + +pci:v000010DEd00000655* + ID_MODEL_FROM_DATABASE=G96 [GeForce GT 120] + +pci:v000010DEd00000656* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9650 S] + +pci:v000010DEd00000658* + ID_MODEL_FROM_DATABASE=G96GL [Quadro FX 380] + +pci:v000010DEd00000659* + ID_MODEL_FROM_DATABASE=G96GL [Quadro FX 580] + +pci:v000010DEd0000065A* + ID_MODEL_FROM_DATABASE=G96GLM [Quadro FX 1700M] + +pci:v000010DEd0000065B* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9400 GT] + +pci:v000010DEd0000065C* + ID_MODEL_FROM_DATABASE=G96GLM [Quadro FX 770M] + +pci:v000010DEd0000065D* + ID_MODEL_FROM_DATABASE=G96 [GeForce 9500 GA / 9600 GT / GTS 250] + +pci:v000010DEd0000065F* + ID_MODEL_FROM_DATABASE=G96 [GeForce G210] + +pci:v000010DEd000006C0* + ID_MODEL_FROM_DATABASE=GF100 [GeForce GTX 480] + +pci:v000010DEd000006C4* + ID_MODEL_FROM_DATABASE=GF100 [GeForce GTX 465] + +pci:v000010DEd000006CA* + ID_MODEL_FROM_DATABASE=GF100M [GeForce GTX 480M] + +pci:v000010DEd000006CB* + ID_MODEL_FROM_DATABASE=GF100 [GeForce GTX 480] + +pci:v000010DEd000006CD* + ID_MODEL_FROM_DATABASE=GF100 [GeForce GTX 470] + +pci:v000010DEd000006D1* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla C2050 / C2070] + +pci:v000010DEd000006D1sv000010DEsd00000771* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla C2050 / C2070] (Tesla C2050) + +pci:v000010DEd000006D1sv000010DEsd00000772* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla C2050 / C2070] (Tesla C2070) + +pci:v000010DEd000006D2* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] + +pci:v000010DEd000006D2sv000010DEsd00000774* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] (Tesla M2070) + +pci:v000010DEd000006D2sv000010DEsd00000830* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] (Tesla M2070) + +pci:v000010DEd000006D2sv000010DEsd00000842* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] (Tesla M2070) + +pci:v000010DEd000006D2sv000010DEsd0000088F* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] (Tesla X2070) + +pci:v000010DEd000006D2sv000010DEsd00000908* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070] (Tesla M2070) + +pci:v000010DEd000006D8* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 6000] + +pci:v000010DEd000006D9* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 5000] + +pci:v000010DEd000006DA* + ID_MODEL_FROM_DATABASE=GF100GLM [Quadro 5000M] + +pci:v000010DEd000006DC* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 6000] + +pci:v000010DEd000006DD* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 4000] + +pci:v000010DEd000006DE* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] + +pci:v000010DEd000006DEsv000010DEsd00000773* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla S2050) + +pci:v000010DEd000006DEsv000010DEsd0000082F* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DEsv000010DEsd00000840* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla X2070) + +pci:v000010DEd000006DEsv000010DEsd00000842* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DEsv000010DEsd00000846* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DEsv000010DEsd00000866* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DEsv000010DEsd00000907* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DEsv000010DEsd0000091E* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla T20 Processor] (Tesla M2050) + +pci:v000010DEd000006DF* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070-Q] + +pci:v000010DEd000006DFsv000010DEsd0000084D* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070-Q] (Tesla M2070-Q) + +pci:v000010DEd000006DFsv000010DEsd0000087F* + ID_MODEL_FROM_DATABASE=GF100GL [Tesla M2070-Q] (Tesla M2070-Q) + +pci:v000010DEd000006E0* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GE] + +pci:v000010DEd000006E0sv0000107Dsd00005A96* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GE] (Geforce 9300GE) + +pci:v000010DEd000006E1* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 GS] + +pci:v000010DEd000006E2* + ID_MODEL_FROM_DATABASE=G98 [GeForce 8400] + +pci:v000010DEd000006E3* + ID_MODEL_FROM_DATABASE=G98 [GeForce 8300 GS] + +pci:v000010DEd000006E4* + ID_MODEL_FROM_DATABASE=G98 [GeForce 8400 GS Rev. 2] + +pci:v000010DEd000006E4sv00001458sd00003475* + ID_MODEL_FROM_DATABASE=G98 [GeForce 8400 GS Rev. 2] (GV-NX84S256HE [GeForce 8400 GS]) + +pci:v000010DEd000006E5* + ID_MODEL_FROM_DATABASE=G98M [GeForce 9300M GS] + +pci:v000010DEd000006E6* + ID_MODEL_FROM_DATABASE=G98 [GeForce G 100] + +pci:v000010DEd000006E7* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9300 SE] + +pci:v000010DEd000006E8* + ID_MODEL_FROM_DATABASE=G98M [GeForce 9200M GS] + +pci:v000010DEd000006E8sv0000103Csd0000360B* + ID_MODEL_FROM_DATABASE=G98M [GeForce 9200M GS] (GeForce 9200M GE) + +pci:v000010DEd000006E9* + ID_MODEL_FROM_DATABASE=G98M [GeForce 9300M GS] + +pci:v000010DEd000006E9sv00001043sd000019B2* + ID_MODEL_FROM_DATABASE=G98M [GeForce 9300M GS] (U6V laptop) + +pci:v000010DEd000006EA* + ID_MODEL_FROM_DATABASE=G98M [Quadro NVS 150M] + +pci:v000010DEd000006EB* + ID_MODEL_FROM_DATABASE=G98M [Quadro NVS 160M] + +pci:v000010DEd000006EC* + ID_MODEL_FROM_DATABASE=G98M [GeForce G 105M] + +pci:v000010DEd000006ED* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9600 GT / 9800 GT] + +pci:v000010DEd000006EE* + ID_MODEL_FROM_DATABASE=G98 [GeForce 9600 GT / 9800 GT] + +pci:v000010DEd000006EF* + ID_MODEL_FROM_DATABASE=G98M [GeForce G 103M] + +pci:v000010DEd000006F1* + ID_MODEL_FROM_DATABASE=G98M [GeForce G 105M] + +pci:v000010DEd000006F8* + ID_MODEL_FROM_DATABASE=G98 [Quadro NVS 420] + +pci:v000010DEd000006F9* + ID_MODEL_FROM_DATABASE=G98GL [Quadro FX 370 LP] + +pci:v000010DEd000006FA* + ID_MODEL_FROM_DATABASE=G98 [Quadro NVS 450] + +pci:v000010DEd000006FB* + ID_MODEL_FROM_DATABASE=G98GLM [Quadro FX 370M] + +pci:v000010DEd000006FD* + ID_MODEL_FROM_DATABASE=G98 [Quadro NVS 295] + +pci:v000010DEd000006FF* + ID_MODEL_FROM_DATABASE=G98 [HICx16 + Graphics] + +pci:v000010DEd000006FFsv000010DEsd00000711* + ID_MODEL_FROM_DATABASE=G98 [HICx16 + Graphics] (HICx8 + Graphics) + +pci:v000010DEd00000751* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller + +pci:v000010DEd00000751sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (Pavilion p6310f) + +pci:v000010DEd00000751sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (M3N72-D) + +pci:v000010DEd00000751sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K9N2GM-FIH) + +pci:v000010DEd00000751sv00001849sd00000751* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K10N78FullHD-hSLI R3.0 Memory Controller) + +pci:v000010DEd00000752* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SMBus + +pci:v000010DEd00000752sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SMBus (Pavilion p6310f) + +pci:v000010DEd00000752sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SMBus (M3N72-D) + +pci:v000010DEd00000752sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SMBus (K9N2GM-FIH) + +pci:v000010DEd00000752sv00001849sd00000752* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SMBus (K10N78FullHD-hSLI R3.0 SMBus) + +pci:v000010DEd00000753* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Co-Processor + +pci:v000010DEd00000753sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Co-Processor (Pavilion p6310f) + +pci:v000010DEd00000753sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Co-Processor (M3N72-D) + +pci:v000010DEd00000753sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Co-Processor (K9N2GM-FIH) + +pci:v000010DEd00000753sv00001849sd00000753* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Co-Processor (K10N78FullHD-hSLI R3.0 Co-Processor) + +pci:v000010DEd00000754* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller + +pci:v000010DEd00000754sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (Pavilion p6310f) + +pci:v000010DEd00000754sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (M3N72-D) + +pci:v000010DEd00000754sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K9N2GM-FIH) + +pci:v000010DEd00000754sv00001849sd00000754* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] Memory Controller (K10N78FullHD-hSLI R3.0 Memory Controller) + +pci:v000010DEd00000759* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] IDE + +pci:v000010DEd00000759sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] IDE (M3N72-D) + +pci:v000010DEd00000759sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] IDE (K9N2GM-FIH) + +pci:v000010DEd00000759sv00001849sd00000759* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] IDE (K10N78FullHD-hSLI R3.0 IDE) + +pci:v000010DEd0000075A* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge + +pci:v000010DEd0000075Asv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (Pavilion p6310f) + +pci:v000010DEd0000075Asv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (M3N72-D) + +pci:v000010DEd0000075Asv00001849sd0000075A* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (K10N78FullHD-hSLI R3.0 PCI Bridge) + +pci:v000010DEd0000075B* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge + +pci:v000010DEd0000075Bsv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (Pavilion p6310f) + +pci:v000010DEd0000075Bsv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (M3N72-D) + +pci:v000010DEd0000075Bsv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K9N2GM-FIH) + +pci:v000010DEd0000075Bsv00001849sd0000075B* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K10N78FullHD-hSLI R3.0 PCI Express Bridge) + +pci:v000010DEd0000075C* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge + +pci:v000010DEd0000075Csv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge (Pavilion p6310f) + +pci:v000010DEd0000075Csv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge (K9N2GM-FIH) + +pci:v000010DEd0000075Csv00001849sd0000075C* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge (K10N78FullHD-hSLI R3.0 LPC Bridge) + +pci:v000010DEd0000075D* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge + +pci:v000010DEd0000075Dsv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] LPC Bridge (M3N72-D) + +pci:v000010DEd00000760* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet + +pci:v000010DEd00000760sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet (Pavilion p6310f) + +pci:v000010DEd00000760sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet (M3N72-D) + +pci:v000010DEd00000760sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet (K9N2GM-FIH) + +pci:v000010DEd00000760sv00001849sd00000760* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet (K10N78FullHD-hSLI R3.0 Ethernet) + +pci:v000010DEd00000761* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet + +pci:v000010DEd00000762* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet + +pci:v000010DEd00000763* + ID_MODEL_FROM_DATABASE=MCP77 Ethernet + +pci:v000010DEd00000774* + ID_MODEL_FROM_DATABASE=MCP72XE/MCP72P/MCP78U/MCP78S High Definition Audio + +pci:v000010DEd00000774sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP72XE/MCP72P/MCP78U/MCP78S High Definition Audio (Pavilion p6310f) + +pci:v000010DEd00000774sv00001043sd000082FE* + ID_MODEL_FROM_DATABASE=MCP72XE/MCP72P/MCP78U/MCP78S High Definition Audio (M3N72-D) + +pci:v000010DEd00000774sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP72XE/MCP72P/MCP78U/MCP78S High Definition Audio (K9N2GM-FIH) + +pci:v000010DEd00000774sv00001849sd00003662* + ID_MODEL_FROM_DATABASE=MCP72XE/MCP72P/MCP78U/MCP78S High Definition Audio (K10N78FullHD-hSLI R3.0 High Definition Audio) + +pci:v000010DEd00000778* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge + +pci:v000010DEd00000778sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (Pavilion p6310f) + +pci:v000010DEd00000778sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (M3N72-D) + +pci:v000010DEd00000778sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K9N2GM-FIH) + +pci:v000010DEd00000778sv00001849sd00000778* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Express Bridge (K10N78FullHD-hSLI R3.0 PCI Express Bridge) + +pci:v000010DEd0000077A* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge + +pci:v000010DEd0000077Asv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (Pavilion p6310f) + +pci:v000010DEd0000077Asv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (M3N72-D) + +pci:v000010DEd0000077Asv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (K9N2GM-FIH) + +pci:v000010DEd0000077Asv00001849sd0000077A* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] PCI Bridge (K10N78FullHD-hSLI R3.0 PCI Bridge) + +pci:v000010DEd0000077B* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller + +pci:v000010DEd0000077Bsv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (Pavilion p6310f) + +pci:v000010DEd0000077Bsv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (M3N72-D) + +pci:v000010DEd0000077Bsv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (K9N2GM-FIH) + +pci:v000010DEd0000077Bsv00001849sd0000077B* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (K10N78FullHD-hSLI R3.0 OHCI USB 1.1 Controller) + +pci:v000010DEd0000077C* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller + +pci:v000010DEd0000077Csv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (Pavilion p6310f) + +pci:v000010DEd0000077Csv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (M3N72-D) + +pci:v000010DEd0000077Csv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (K9N2GM-FIH) + +pci:v000010DEd0000077Csv00001849sd0000077C* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (K10N78FullHD-hSLI R3.0 EHCI USB 2.0 Controller) + +pci:v000010DEd0000077D* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller + +pci:v000010DEd0000077Dsv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (Pavilion p6310f) + +pci:v000010DEd0000077Dsv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (M3N72-D) + +pci:v000010DEd0000077Dsv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (K9N2GM-FIH) + +pci:v000010DEd0000077Dsv00001849sd0000077D* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] OHCI USB 1.1 Controller (K10N78FullHD-hSLI R3.0 OHCI USB 1.1 Controller) + +pci:v000010DEd0000077E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller + +pci:v000010DEd0000077Esv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (Pavilion p6310f) + +pci:v000010DEd0000077Esv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (M3N72-D) + +pci:v000010DEd0000077Esv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (K9N2GM-FIH) + +pci:v000010DEd0000077Esv00001849sd0000077E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] EHCI USB 2.0 Controller (K10N78FullHD-hSLI R3.0 EHCI USB 2.0 Controller) + +pci:v000010DEd000007C0* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge + +pci:v000010DEd000007C0sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge (JW-IN7150-HD) + +pci:v000010DEd000007C1* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge + +pci:v000010DEd000007C1sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge (MCP73PVT-SM) + +pci:v000010DEd000007C2* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge + +pci:v000010DEd000007C3* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge + +pci:v000010DEd000007C3sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge (I-N73V motherboard) + +pci:v000010DEd000007C5* + ID_MODEL_FROM_DATABASE=MCP73 Host Bridge + +pci:v000010DEd000007C8* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller + +pci:v000010DEd000007C8sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (MCP73PVT-SM) + +pci:v000010DEd000007C8sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (I-N73V motherboard) + +pci:v000010DEd000007C8sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (JW-IN7150-HD) + +pci:v000010DEd000007CB* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007CBsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007CBsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007CBsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007CD* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007CDsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007CDsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007CDsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007CE* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007CEsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007CEsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007CEsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007CF* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007CFsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007CFsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007CFsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D0* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007D0sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007D0sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007D0sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D1* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007D1sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007D1sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007D1sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D2* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007D2sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007D2sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007D2sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D3* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007D3sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007D3sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007D3sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D6* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller + +pci:v000010DEd000007D6sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (MCP73PVT-SM) + +pci:v000010DEd000007D6sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (I-N73V motherboard) + +pci:v000010DEd000007D6sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=nForce 610i/630i memory controller (JW-IN7150-HD) + +pci:v000010DEd000007D7* + ID_MODEL_FROM_DATABASE=MCP73 LPC Bridge + +pci:v000010DEd000007D7sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 LPC Bridge (MCP73PVT-SM) + +pci:v000010DEd000007D7sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 LPC Bridge (I-N73V motherboard) + +pci:v000010DEd000007D7sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 LPC Bridge (JW-IN7150-HD) + +pci:v000010DEd000007D8* + ID_MODEL_FROM_DATABASE=MCP73 SMBus + +pci:v000010DEd000007D8sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 SMBus (MCP73PVT-SM) + +pci:v000010DEd000007D8sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 SMBus (I-N73V motherboard) + +pci:v000010DEd000007D8sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 SMBus (JW-IN7150-HD) + +pci:v000010DEd000007D9* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller + +pci:v000010DEd000007D9sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (MCP73PVT-SM) + +pci:v000010DEd000007D9sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (I-N73V motherboard) + +pci:v000010DEd000007D9sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 Memory Controller (JW-IN7150-HD) + +pci:v000010DEd000007DA* + ID_MODEL_FROM_DATABASE=MCP73 Co-processor + +pci:v000010DEd000007DAsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 Co-processor (JW-IN7150-HD) + +pci:v000010DEd000007DC* + ID_MODEL_FROM_DATABASE=MCP73 Ethernet + +pci:v000010DEd000007DCsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 Ethernet (I-N73V motherboard) + +pci:v000010DEd000007DD* + ID_MODEL_FROM_DATABASE=MCP73 Ethernet + +pci:v000010DEd000007DE* + ID_MODEL_FROM_DATABASE=MCP73 Ethernet + +pci:v000010DEd000007DF* + ID_MODEL_FROM_DATABASE=MCP73 Ethernet + +pci:v000010DEd000007E0* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7150 / nForce 630i] + +pci:v000010DEd000007E0sv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7150 / nForce 630i] (JW-IN7150-HD) + +pci:v000010DEd000007E1* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7100 / nForce 630i] + +pci:v000010DEd000007E1sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7100 / nForce 630i] (MCP73PVT-SM) + +pci:v000010DEd000007E2* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7050 / nForce 630i] + +pci:v000010DEd000007E3* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7050 / nForce 610i] + +pci:v000010DEd000007E3sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7050 / nForce 610i] (I-N73V motherboard) + +pci:v000010DEd000007E5* + ID_MODEL_FROM_DATABASE=C73 [GeForce 7100 / nForce 620i] + +pci:v000010DEd000007F0* + ID_MODEL_FROM_DATABASE=MCP73 SATA Controller (IDE mode) + +pci:v000010DEd000007F0sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 SATA Controller (IDE mode) (I-N73V motherboard) + +pci:v000010DEd000007F4* + ID_MODEL_FROM_DATABASE=GeForce 7100/nForce 630i SATA + +pci:v000010DEd000007F4sv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=GeForce 7100/nForce 630i SATA (MCP73PVT-SM) + +pci:v000010DEd000007F4sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=GeForce 7100/nForce 630i SATA (I-N73V motherboard) + +pci:v000010DEd000007F8* + ID_MODEL_FROM_DATABASE=MCP73 SATA RAID Controller + +pci:v000010DEd000007F8sv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 SATA RAID Controller (I-N73V motherboard) + +pci:v000010DEd000007FC* + ID_MODEL_FROM_DATABASE=MCP73 High Definition Audio + +pci:v000010DEd000007FCsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 High Definition Audio (MCP73PVT-SM) + +pci:v000010DEd000007FCsv000010DEsd000007FC* + ID_MODEL_FROM_DATABASE=MCP73 High Definition Audio + +pci:v000010DEd000007FCsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 High Definition Audio (I-N73V motherboard) + +pci:v000010DEd000007FE* + ID_MODEL_FROM_DATABASE=MCP73 OHCI USB 1.1 Controller + +pci:v000010DEd000007FEsv00001019sd0000297A* + ID_MODEL_FROM_DATABASE=MCP73 OHCI USB 1.1 Controller (MCP73PVT-SM) + +pci:v000010DEd000007FEsv0000147Bsd00001C3E* + ID_MODEL_FROM_DATABASE=MCP73 OHCI USB 1.1 Controller (I-N73V motherboard) + +pci:v000010DEd000007FEsv00001AFAsd00007150* + ID_MODEL_FROM_DATABASE=MCP73 OHCI USB 1.1 Controller (JW-IN7150-HD) + +pci:v000010DEd00000840* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200M] + +pci:v000010DEd00000844* + ID_MODEL_FROM_DATABASE=C77 [GeForce 9100M G] + +pci:v000010DEd00000845* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200M G] + +pci:v000010DEd00000846* + ID_MODEL_FROM_DATABASE=C77 [GeForce 9200] + +pci:v000010DEd00000847* + ID_MODEL_FROM_DATABASE=C78 [GeForce 9100] + +pci:v000010DEd00000847sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=C78 [GeForce 9100] (Pavilion p6310f) + +pci:v000010DEd00000848* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8300] + +pci:v000010DEd00000849* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200] + +pci:v000010DEd00000849sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200] (K9N2GM-FIH) + +pci:v000010DEd00000849sv00001849sd00000849* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200] (K10N78FullHD-hSLI R3.0 GeForce 8200) + +pci:v000010DEd0000084A* + ID_MODEL_FROM_DATABASE=C77 [nForce 730a] + +pci:v000010DEd0000084B* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8200] + +pci:v000010DEd0000084C* + ID_MODEL_FROM_DATABASE=C77 [nForce 780a/980a SLI] + +pci:v000010DEd0000084D* + ID_MODEL_FROM_DATABASE=C77 [nForce 750a SLI] + +pci:v000010DEd0000084Dsv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=C77 [nForce 750a SLI] (M3N72-D mGPU) + +pci:v000010DEd0000084F* + ID_MODEL_FROM_DATABASE=C77 [GeForce 8100 / nForce 720a] + +pci:v000010DEd00000860* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9300] + +pci:v000010DEd00000861* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400] + +pci:v000010DEd00000862* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M G] + +pci:v000010DEd00000863* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M] + +pci:v000010DEd00000863sv0000106Bsd000000AA* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M] (MacBook5,1) + +pci:v000010DEd00000864* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9300] + +pci:v000010DEd00000865* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9300/ION] + +pci:v000010DEd00000866* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M G] + +pci:v000010DEd00000866sv0000106Bsd000000B1* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M G] (GeForce 9400M) + +pci:v000010DEd00000867* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400] + +pci:v000010DEd00000867sv0000106Bsd000000AD* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400] (iMac 9,1) + +pci:v000010DEd00000868* + ID_MODEL_FROM_DATABASE=C79 [nForce 760i SLI] + +pci:v000010DEd00000869* + ID_MODEL_FROM_DATABASE=MCP7A [GeForce 9400] + +pci:v000010DEd0000086A* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400] + +pci:v000010DEd0000086C* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9300 / nForce 730i] + +pci:v000010DEd0000086D* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9200] + +pci:v000010DEd0000086E* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9100M G] + +pci:v000010DEd0000086F* + ID_MODEL_FROM_DATABASE=MCP79 [GeForce 8200M G] + +pci:v000010DEd00000870* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400M] + +pci:v000010DEd00000871* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9200] + +pci:v000010DEd00000872* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] + +pci:v000010DEd00000872sv00001043sd000019B4* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G102M) + +pci:v000010DEd00000872sv00001043sd00001AA2* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G102M) + +pci:v000010DEd00000872sv00001043sd00001C02* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G102M) + +pci:v000010DEd00000872sv00001043sd00001C42* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G205M) + +pci:v000010DEd00000873* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] + +pci:v000010DEd00000873sv00001043sd000019B4* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G102M) + +pci:v000010DEd00000873sv00001043sd00001C12* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G102M) + +pci:v000010DEd00000873sv00001043sd00001C52* + ID_MODEL_FROM_DATABASE=C79 [GeForce G102M] (GeForce G205M) + +pci:v000010DEd00000874* + ID_MODEL_FROM_DATABASE=C79 [ION] + +pci:v000010DEd00000876* + ID_MODEL_FROM_DATABASE=ION VGA [GeForce 9400M] + +pci:v000010DEd0000087A* + ID_MODEL_FROM_DATABASE=C79 [GeForce 9400] + +pci:v000010DEd0000087D* + ID_MODEL_FROM_DATABASE=ION VGA + +pci:v000010DEd0000087Dsv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=ION VGA (IONITX-F-E) + +pci:v000010DEd0000087E* + ID_MODEL_FROM_DATABASE=ION LE VGA + +pci:v000010DEd0000087F* + ID_MODEL_FROM_DATABASE=ION LE VGA + +pci:v000010DEd000008A0* + ID_MODEL_FROM_DATABASE=MCP89 [GeForce 320M] + +pci:v000010DEd000008A2* + ID_MODEL_FROM_DATABASE=MCP89 [GeForce 320M] + +pci:v000010DEd000008A3* + ID_MODEL_FROM_DATABASE=MCP89 [GeForce 320M] + +pci:v000010DEd000008A4* + ID_MODEL_FROM_DATABASE=MCP89 [GeForce 320M] + +pci:v000010DEd000008A5* + ID_MODEL_FROM_DATABASE=MCP89 [GeForce 320M] + +pci:v000010DEd00000A20* + ID_MODEL_FROM_DATABASE=GT216 [GeForce GT 220] + +pci:v000010DEd00000A20sv00001043sd00008311* + ID_MODEL_FROM_DATABASE=GT216 [GeForce GT 220] (ENGT220/DI/1GD3(LP)/V2) + +pci:v000010DEd00000A21* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 330M] + +pci:v000010DEd00000A22* + ID_MODEL_FROM_DATABASE=GT216 [GeForce 315] + +pci:v000010DEd00000A23* + ID_MODEL_FROM_DATABASE=GT216 [GeForce 210] + +pci:v000010DEd00000A26* + ID_MODEL_FROM_DATABASE=GT216 [GeForce 405] + +pci:v000010DEd00000A27* + ID_MODEL_FROM_DATABASE=GT216 [GeForce 405] + +pci:v000010DEd00000A28* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 230M] + +pci:v000010DEd00000A29* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 330M] + +pci:v000010DEd00000A2A* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 230M] + +pci:v000010DEd00000A2B* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 330M] + +pci:v000010DEd00000A2C* + ID_MODEL_FROM_DATABASE=GT216M [NVS 5100M] + +pci:v000010DEd00000A2D* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 320M] + +pci:v000010DEd00000A30* + ID_MODEL_FROM_DATABASE=GT216 [GeForce 505] + +pci:v000010DEd00000A32* + ID_MODEL_FROM_DATABASE=GT216 [GeForce GT 415] + +pci:v000010DEd00000A34* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 240M] + +pci:v000010DEd00000A35* + ID_MODEL_FROM_DATABASE=GT216M [GeForce GT 325M] + +pci:v000010DEd00000A38* + ID_MODEL_FROM_DATABASE=GT216GL [Quadro 400] + +pci:v000010DEd00000A3C* + ID_MODEL_FROM_DATABASE=GT216GLM [Quadro FX 880M] + +pci:v000010DEd00000A60* + ID_MODEL_FROM_DATABASE=GT218 [GeForce G210] + +pci:v000010DEd00000A62* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 205] + +pci:v000010DEd00000A63* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 310] + +pci:v000010DEd00000A64* + ID_MODEL_FROM_DATABASE=GT218 [ION] + +pci:v000010DEd00000A65* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 210] + +pci:v000010DEd00000A65sv00001043sd00008334* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 210] (EN210 SILENT) + +pci:v000010DEd00000A65sv00001462sd00008094* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 210] (N210 [Geforce 210] PCIe graphics adapter) + +pci:v000010DEd00000A66* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 310] + +pci:v000010DEd00000A67* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 315] + +pci:v000010DEd00000A68* + ID_MODEL_FROM_DATABASE=GT218M [GeForce G 105M] + +pci:v000010DEd00000A69* + ID_MODEL_FROM_DATABASE=GT218M [GeForce G 105M] + +pci:v000010DEd00000A6A* + ID_MODEL_FROM_DATABASE=GT218M [NVS 2100M] + +pci:v000010DEd00000A6C* + ID_MODEL_FROM_DATABASE=GT218M [NVS 3100M] + +pci:v000010DEd00000A6Csv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=GT218M [NVS 3100M] (Latitude E6510) + +pci:v000010DEd00000A6Csv000017AAsd00002142* + ID_MODEL_FROM_DATABASE=GT218M [NVS 3100M] (ThinkPad T410) + +pci:v000010DEd00000A6E* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 305M] + +pci:v000010DEd00000A6F* + ID_MODEL_FROM_DATABASE=GT218 [ION] + +pci:v000010DEd00000A70* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 310M] + +pci:v000010DEd00000A71* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 305M] + +pci:v000010DEd00000A72* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 310M] + +pci:v000010DEd00000A73* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 305M] + +pci:v000010DEd00000A74* + ID_MODEL_FROM_DATABASE=GT218M [GeForce G210M] + +pci:v000010DEd00000A74sv00001B0Asd0000903A* + ID_MODEL_FROM_DATABASE=GT218M [GeForce G210M] (GeForce G210) + +pci:v000010DEd00000A75* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 310M] + +pci:v000010DEd00000A76* + ID_MODEL_FROM_DATABASE=GT218 [ION 2] + +pci:v000010DEd00000A78* + ID_MODEL_FROM_DATABASE=GT218GL [Quadro FX 380 LP] + +pci:v000010DEd00000A7A* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] + +pci:v000010DEd00000A7Asv0000104Dsd0000907E* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FC50* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FC61* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FC71* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FC90* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FCC0* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FCD0* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FCE2* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FCF2* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD16* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD40* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD50* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD52* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD61* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD71* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD92* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FD96* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FDD0* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FDD2* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001179sd0000FDFE* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C0A2* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C0B2* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C581* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C587* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C588* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C597* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv0000144Dsd0000C606* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001462sd0000AA51* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001462sd0000AA58* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001462sd0000AC71* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001462sd0000AC81* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001462sd0000AC82* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001462sd0000AE33* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001642sd00003980* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv000017AAsd00003950* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405M) + +pci:v000010DEd00000A7Asv000017AAsd0000397D* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405M) + +pci:v000010DEd00000A7Asv00001B0Asd00002091* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7Asv00001B0Asd000090B4* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001BFDsd00000003* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001BFDsd00008006* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 405) + +pci:v000010DEd00000A7Asv00001BFDsd00008007* + ID_MODEL_FROM_DATABASE=GT218M [GeForce 315M] (GeForce 315M) + +pci:v000010DEd00000A7B* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 505] + +pci:v000010DEd00000A7C* + ID_MODEL_FROM_DATABASE=GT218GLM [Quadro FX 380M] + +pci:v000010DEd00000A80* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A81* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A82* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A83* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A84* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A85* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A86* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A87* + ID_MODEL_FROM_DATABASE=MCP79 Host Bridge + +pci:v000010DEd00000A88* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller + +pci:v000010DEd00000A89* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller + +pci:v000010DEd00000A98* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller + +pci:v000010DEd00000A98sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller (iMac 9,1) + +pci:v000010DEd00000AA0* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AA0sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AA2* + ID_MODEL_FROM_DATABASE=MCP79 SMBus + +pci:v000010DEd00000AA2sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 SMBus (Apple iMac 9,1) + +pci:v000010DEd00000AA2sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 SMBus (IONITX-F-E) + +pci:v000010DEd00000AA3* + ID_MODEL_FROM_DATABASE=MCP79 Co-processor + +pci:v000010DEd00000AA3sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 Co-processor (Apple iMac 9,1) + +pci:v000010DEd00000AA3sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 Co-processor (IONITX-F-E) + +pci:v000010DEd00000AA4* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller + +pci:v000010DEd00000AA4sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 Memory Controller (IONITX-F-E) + +pci:v000010DEd00000AA5* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller + +pci:v000010DEd00000AA5sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller (Apple iMac 9,1) + +pci:v000010DEd00000AA5sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller (IONITX-F-E) + +pci:v000010DEd00000AA6* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller + +pci:v000010DEd00000AA6sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller (Apple iMac 9,1) + +pci:v000010DEd00000AA6sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller (IONITX-F-E) + +pci:v000010DEd00000AA7* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller + +pci:v000010DEd00000AA7sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller (Apple iMac 9,1) + +pci:v000010DEd00000AA7sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller (IONITX-F-E) + +pci:v000010DEd00000AA8* + ID_MODEL_FROM_DATABASE=MCP79 OHCI USB 1.1 Controller + +pci:v000010DEd00000AA9* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller + +pci:v000010DEd00000AA9sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller (Apple iMac 9,1) + +pci:v000010DEd00000AA9sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller (IONITX-F-E) + +pci:v000010DEd00000AAA* + ID_MODEL_FROM_DATABASE=MCP79 EHCI USB 2.0 Controller + +pci:v000010DEd00000AAB* + ID_MODEL_FROM_DATABASE=MCP79 PCI Bridge + +pci:v000010DEd00000AABsv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 PCI Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AAC* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge + +pci:v000010DEd00000AAD* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge + +pci:v000010DEd00000AADsv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge (IONITX-F-E) + +pci:v000010DEd00000AAE* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge + +pci:v000010DEd00000AAEsv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AAF* + ID_MODEL_FROM_DATABASE=MCP79 LPC Bridge + +pci:v000010DEd00000AB0* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet + +pci:v000010DEd00000AB0sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet (Apple iMac 9,1) + +pci:v000010DEd00000AB0sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet (IONITX-F-E) + +pci:v000010DEd00000AB1* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet + +pci:v000010DEd00000AB2* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet + +pci:v000010DEd00000AB3* + ID_MODEL_FROM_DATABASE=MCP79 Ethernet + +pci:v000010DEd00000AB4* + ID_MODEL_FROM_DATABASE=MCP79 SATA Controller + +pci:v000010DEd00000AB4sv000019DAsd0000A123* + ID_MODEL_FROM_DATABASE=MCP79 SATA Controller (IONITX-F-E) + +pci:v000010DEd00000AB5* + ID_MODEL_FROM_DATABASE=MCP79 SATA Controller + +pci:v000010DEd00000AB6* + ID_MODEL_FROM_DATABASE=MCP79 SATA Controller + +pci:v000010DEd00000AB7* + ID_MODEL_FROM_DATABASE=MCP79 SATA Controller + +pci:v000010DEd00000AB8* + ID_MODEL_FROM_DATABASE=MCP79 AHCI Controller + +pci:v000010DEd00000AB9* + ID_MODEL_FROM_DATABASE=MCP79 AHCI Controller + +pci:v000010DEd00000AB9sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 AHCI Controller (Apple iMac 9,1) + +pci:v000010DEd00000ABA* + ID_MODEL_FROM_DATABASE=MCP79 AHCI Controller + +pci:v000010DEd00000ABB* + ID_MODEL_FROM_DATABASE=MCP79 AHCI Controller + +pci:v000010DEd00000ABC* + ID_MODEL_FROM_DATABASE=MCP79 RAID Controller + +pci:v000010DEd00000ABD* + ID_MODEL_FROM_DATABASE=MCP79 RAID Controller + +pci:v000010DEd00000ABE* + ID_MODEL_FROM_DATABASE=MCP79 RAID Controller + +pci:v000010DEd00000ABF* + ID_MODEL_FROM_DATABASE=MCP79 RAID Controller + +pci:v000010DEd00000AC0* + ID_MODEL_FROM_DATABASE=MCP79 High Definition Audio + +pci:v000010DEd00000AC0sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 High Definition Audio (Apple iMac 9,1) + +pci:v000010DEd00000AC1* + ID_MODEL_FROM_DATABASE=MCP79 High Definition Audio + +pci:v000010DEd00000AC2* + ID_MODEL_FROM_DATABASE=MCP79 High Definition Audio + +pci:v000010DEd00000AC3* + ID_MODEL_FROM_DATABASE=MCP79 High Definition Audio + +pci:v000010DEd00000AC4* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AC4sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AC5* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AC6* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AC6sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AC7* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AC7sv000010DEsd0000CB79* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge (Apple iMac 9,1) + +pci:v000010DEd00000AC8* + ID_MODEL_FROM_DATABASE=MCP79 PCI Express Bridge + +pci:v000010DEd00000AD0* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SATA Controller (non-AHCI mode) + +pci:v000010DEd00000AD0sv00001462sd00007508* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SATA Controller (non-AHCI mode) (K9N2GM-FIH) + +pci:v000010DEd00000AD0sv00001849sd00000AD0* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SATA Controller (non-AHCI mode) (K10N78FullHD-hSLI R3.0 IDE) + +pci:v000010DEd00000AD4* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] AHCI Controller + +pci:v000010DEd00000AD4sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] AHCI Controller (Pavilion p6310f) + +pci:v000010DEd00000AD4sv00001043sd000082E8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] AHCI Controller (M3N72-D) + +pci:v000010DEd00000AD4sv00001849sd00000AD4* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] AHCI Controller (K10N78FullHD-hSLI R3.0 AHCI Controller) + +pci:v000010DEd00000AD8* + ID_MODEL_FROM_DATABASE=MCP78S [GeForce 8200] SATA Controller (RAID mode) + +pci:v000010DEd00000BE2* + ID_MODEL_FROM_DATABASE=GT216 HDMI Audio Controller + +pci:v000010DEd00000BE2sv00001043sd00008311* + ID_MODEL_FROM_DATABASE=GT216 HDMI Audio Controller (ENGT220/DI/1GD3(LP)/V2) + +pci:v000010DEd00000BE3* + ID_MODEL_FROM_DATABASE=High Definition Audio Controller + +pci:v000010DEd00000BE3sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=High Definition Audio Controller (Latitude E6510) + +pci:v000010DEd00000BE3sv000010DEsd0000066D* + ID_MODEL_FROM_DATABASE=High Definition Audio Controller (G98 [GeForce 8400GS]) + +pci:v000010DEd00000BE3sv00001462sd00008094* + ID_MODEL_FROM_DATABASE=High Definition Audio Controller (N210 [Geforce 210] PCIe graphics adapter) + +pci:v000010DEd00000BE4* + ID_MODEL_FROM_DATABASE=High Definition Audio Controller + +pci:v000010DEd00000BE5* + ID_MODEL_FROM_DATABASE=GF100 High Definition Audio Controller + +pci:v000010DEd00000BE9* + ID_MODEL_FROM_DATABASE=GF106 High Definition Audio Controller + +pci:v000010DEd00000BE9sv00001558sd00008687* + ID_MODEL_FROM_DATABASE=GF106 High Definition Audio Controller (CLEVO/KAPOK W860CU) + +pci:v000010DEd00000BE9sv00003842sd00001452* + ID_MODEL_FROM_DATABASE=GF106 High Definition Audio Controller (GeForce GTS 450) + +pci:v000010DEd00000BEA* + ID_MODEL_FROM_DATABASE=GF108 High Definition Audio Controller + +pci:v000010DEd00000BEAsv00003842sd00001430* + ID_MODEL_FROM_DATABASE=GF108 High Definition Audio Controller (GeForce GT 430) + +pci:v000010DEd00000BEB* + ID_MODEL_FROM_DATABASE=GF104 High Definition Audio Controller + +pci:v000010DEd00000BEBsv00001462sd00002322* + ID_MODEL_FROM_DATABASE=GF104 High Definition Audio Controller (N460GTX Cyclone 1GD5/OC) + +pci:v000010DEd00000BEE* + ID_MODEL_FROM_DATABASE=GF116 High Definition Audio Controller + +pci:v000010DEd00000BF0* + ID_MODEL_FROM_DATABASE=Tegra2 PCIe x4 Bridge + +pci:v000010DEd00000BF1* + ID_MODEL_FROM_DATABASE=Tegra2 PCIe x2 Bridge + +pci:v000010DEd00000CA0* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 330] + +pci:v000010DEd00000CA2* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 320] + +pci:v000010DEd00000CA3* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 240] + +pci:v000010DEd00000CA4* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 340] + +pci:v000010DEd00000CA5* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 220] + +pci:v000010DEd00000CA7* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 330] + +pci:v000010DEd00000CA8* + ID_MODEL_FROM_DATABASE=GT215M [GeForce GTS 260M] + +pci:v000010DEd00000CA9* + ID_MODEL_FROM_DATABASE=GT215M [GeForce GTS 250M] + +pci:v000010DEd00000CAC* + ID_MODEL_FROM_DATABASE=GT215 [GeForce GT 220/315] + +pci:v000010DEd00000CAF* + ID_MODEL_FROM_DATABASE=GT215M [GeForce GT 335M] + +pci:v000010DEd00000CB0* + ID_MODEL_FROM_DATABASE=GT215M [GeForce GTS 350M] + +pci:v000010DEd00000CB1* + ID_MODEL_FROM_DATABASE=GT215M [GeForce GTS 360M] + +pci:v000010DEd00000CBC* + ID_MODEL_FROM_DATABASE=GT215GLM [Quadro FX 1800M] + +pci:v000010DEd00000D60* + ID_MODEL_FROM_DATABASE=MCP89 HOST Bridge + +pci:v000010DEd00000D68* + ID_MODEL_FROM_DATABASE=MCP89 Memory Controller + +pci:v000010DEd00000D69* + ID_MODEL_FROM_DATABASE=MCP89 Memory Controller + +pci:v000010DEd00000D76* + ID_MODEL_FROM_DATABASE=MCP89 PCI Express Bridge + +pci:v000010DEd00000D79* + ID_MODEL_FROM_DATABASE=MCP89 SMBus + +pci:v000010DEd00000D7A* + ID_MODEL_FROM_DATABASE=MCP89 Co-Processor + +pci:v000010DEd00000D7B* + ID_MODEL_FROM_DATABASE=MCP89 Memory Controller + +pci:v000010DEd00000D7D* + ID_MODEL_FROM_DATABASE=MCP89 Ethernet + +pci:v000010DEd00000D80* + ID_MODEL_FROM_DATABASE=MCP89 LPC Bridge + +pci:v000010DEd00000D85* + ID_MODEL_FROM_DATABASE=MCP89 SATA Controller + +pci:v000010DEd00000D88* + ID_MODEL_FROM_DATABASE=MCP89 SATA Controller (AHCI mode) + +pci:v000010DEd00000D89* + ID_MODEL_FROM_DATABASE=MCP89 SATA Controller (AHCI mode) + +pci:v000010DEd00000D8D* + ID_MODEL_FROM_DATABASE=MCP89 SATA Controller (RAID mode) + +pci:v000010DEd00000D94* + ID_MODEL_FROM_DATABASE=MCP89 High Definition Audio + +pci:v000010DEd00000D9C* + ID_MODEL_FROM_DATABASE=MCP89 OHCI USB 1.1 Controller + +pci:v000010DEd00000D9D* + ID_MODEL_FROM_DATABASE=MCP89 EHCI USB 2.0 Controller + +pci:v000010DEd00000DC0* + ID_MODEL_FROM_DATABASE=GF106 [GeForce GT 440] + +pci:v000010DEd00000DC4* + ID_MODEL_FROM_DATABASE=GF106 [GeForce GTS 450] + +pci:v000010DEd00000DC5* + ID_MODEL_FROM_DATABASE=GF106 [GeForce GTS 450 OEM] + +pci:v000010DEd00000DC6* + ID_MODEL_FROM_DATABASE=GF106 [GeForce GTS 450] + +pci:v000010DEd00000DCD* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GT 555M] + +pci:v000010DEd00000DCE* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GT 555M] + +pci:v000010DEd00000DD1* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GTX 460M] + +pci:v000010DEd00000DD1sv00001558sd00008687* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GTX 460M] (CLEVO/KAPOK W860CU) + +pci:v000010DEd00000DD2* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GT 445M] + +pci:v000010DEd00000DD3* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GT 435M] + +pci:v000010DEd00000DD6* + ID_MODEL_FROM_DATABASE=GF106M [GeForce GT 550M] + +pci:v000010DEd00000DD8* + ID_MODEL_FROM_DATABASE=GF106GL [Quadro 2000] + +pci:v000010DEd00000DD8sv000010DEsd00000914* + ID_MODEL_FROM_DATABASE=GF106GL [Quadro 2000] (Quadro 2000D) + +pci:v000010DEd00000DDA* + ID_MODEL_FROM_DATABASE=GF106GLM [Quadro 2000M] + +pci:v000010DEd00000DE0* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 440] + +pci:v000010DEd00000DE1* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 430] + +pci:v000010DEd00000DE1sv00003842sd00001430* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 430] (GeForce GT 430) + +pci:v000010DEd00000DE2* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 420] + +pci:v000010DEd00000DE3* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 635M] + +pci:v000010DEd00000DE4* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 520] + +pci:v000010DEd00000DE5* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 530] + +pci:v000010DEd00000DE7* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 610] + +pci:v000010DEd00000DE8* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 620M] + +pci:v000010DEd00000DE9* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] + +pci:v000010DEd00000DE9sv00001025sd00000692* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd00000725* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd00000728* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd0000072B* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd0000072E* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd00000753* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv00001025sd00000754* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 620M) + +pci:v000010DEd00000DE9sv000017AAsd00003977* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 640M LE) + +pci:v000010DEd00000DE9sv00001B0Asd00002210* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 630M] (GeForce GT 635M) + +pci:v000010DEd00000DEA* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] + +pci:v000010DEd00000DEAsv000017AAsd0000365A* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] (GeForce 615) + +pci:v000010DEd00000DEAsv000017AAsd0000365B* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] (GeForce 615) + +pci:v000010DEd00000DEAsv000017AAsd0000365E* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] (GeForce 615) + +pci:v000010DEd00000DEAsv000017AAsd00003660* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] (GeForce 615) + +pci:v000010DEd00000DEAsv000017AAsd0000366C* + ID_MODEL_FROM_DATABASE=GF108M [GeForce 610M] (GeForce 615) + +pci:v000010DEd00000DEB* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 555M] + +pci:v000010DEd00000DEC* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 525M] + +pci:v000010DEd00000DED* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 520M] + +pci:v000010DEd00000DEE* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 415M] + +pci:v000010DEd00000DEF* + ID_MODEL_FROM_DATABASE=GF108M [NVS 5400M] + +pci:v000010DEd00000DF0* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 425M] + +pci:v000010DEd00000DF1* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 420M] + +pci:v000010DEd00000DF2* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 435M] + +pci:v000010DEd00000DF3* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 420M] + +pci:v000010DEd00000DF4* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 540M] + +pci:v000010DEd00000DF4sv0000152Dsd00000952* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 540M] (GeForce GT 630M) + +pci:v000010DEd00000DF4sv0000152Dsd00000953* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 540M] (GeForce GT 630M) + +pci:v000010DEd00000DF5* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 525M] + +pci:v000010DEd00000DF6* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 550M] + +pci:v000010DEd00000DF7* + ID_MODEL_FROM_DATABASE=GF108M [GeForce GT 520M] + +pci:v000010DEd00000DF8* + ID_MODEL_FROM_DATABASE=GF108GL [Quadro 600] + +pci:v000010DEd00000DF9* + ID_MODEL_FROM_DATABASE=GF108GLM [Quadro 500M] + +pci:v000010DEd00000DFA* + ID_MODEL_FROM_DATABASE=GF108GLM [Quadro 1000M] + +pci:v000010DEd00000DFC* + ID_MODEL_FROM_DATABASE=GF108GLM [NVS 5200M] + +pci:v000010DEd00000E08* + ID_MODEL_FROM_DATABASE=GF119 HDMI Audio Controller + +pci:v000010DEd00000E08sv000010B0sd0000104A* + ID_MODEL_FROM_DATABASE=GF119 HDMI Audio Controller (Gainward GeForce GT 610) + +pci:v000010DEd00000E09* + ID_MODEL_FROM_DATABASE=GF110 High Definition Audio Controller + +pci:v000010DEd00000E0A* + ID_MODEL_FROM_DATABASE=GK104 HDMI Audio Controller + +pci:v000010DEd00000E0B* + ID_MODEL_FROM_DATABASE=GK106 HDMI Audio Controller + +pci:v000010DEd00000E0C* + ID_MODEL_FROM_DATABASE=GF114 HDMI Audio Controller + +pci:v000010DEd00000E0F* + ID_MODEL_FROM_DATABASE=GK208 HDMI/DP Audio Controller + +pci:v000010DEd00000E12* + ID_MODEL_FROM_DATABASE=TegraK1 PCIe x4 Bridge + +pci:v000010DEd00000E13* + ID_MODEL_FROM_DATABASE=TegraK1 PCIe x1 Bridge + +pci:v000010DEd00000E1A* + ID_MODEL_FROM_DATABASE=GK110 HDMI Audio + +pci:v000010DEd00000E1B* + ID_MODEL_FROM_DATABASE=GK107 HDMI Audio Controller + +pci:v000010DEd00000E1Bsv0000103Csd0000197B* + ID_MODEL_FROM_DATABASE=GK107 HDMI Audio Controller (ZBook 15) + +pci:v000010DEd00000E1Bsv00001043sd00008428* + ID_MODEL_FROM_DATABASE=GK107 HDMI Audio Controller (GTX650-DC-1GD5) + +pci:v000010DEd00000E1C* + ID_MODEL_FROM_DATABASE=Tegra3+ PCIe x4 Bridge + +pci:v000010DEd00000E1D* + ID_MODEL_FROM_DATABASE=Tegra3+ PCIe x2 Bridge + +pci:v000010DEd00000E22* + ID_MODEL_FROM_DATABASE=GF104 [GeForce GTX 460] + +pci:v000010DEd00000E22sv00001462sd00002322* + ID_MODEL_FROM_DATABASE=GF104 [GeForce GTX 460] (N460GTX Cyclone 1GD5/OC) + +pci:v000010DEd00000E23* + ID_MODEL_FROM_DATABASE=GF104 [GeForce GTX 460 SE] + +pci:v000010DEd00000E24* + ID_MODEL_FROM_DATABASE=GF104 [GeForce GTX 460 OEM] + +pci:v000010DEd00000E30* + ID_MODEL_FROM_DATABASE=GF104M [GeForce GTX 470M] + +pci:v000010DEd00000E31* + ID_MODEL_FROM_DATABASE=GF104M [GeForce GTX 485M] + +pci:v000010DEd00000E3A* + ID_MODEL_FROM_DATABASE=GF104GLM [Quadro 3000M] + +pci:v000010DEd00000E3B* + ID_MODEL_FROM_DATABASE=GF104GLM [Quadro 4000M] + +pci:v000010DEd00000F00* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 630] + +pci:v000010DEd00000F01* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 620] + +pci:v000010DEd00000F02* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 730] + +pci:v000010DEd00000F06* + ID_MODEL_FROM_DATABASE=GF108 [GeForce GT 730] + +pci:v000010DEd00000FB0* + ID_MODEL_FROM_DATABASE=GM200 High Definition Audio + +pci:v000010DEd00000FBB* + ID_MODEL_FROM_DATABASE=GM204 High Definition Audio Controller + +pci:v000010DEd00000FC0* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 640 OEM] + +pci:v000010DEd00000FC1* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 640] + +pci:v000010DEd00000FC2* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 630 OEM] + +pci:v000010DEd00000FC6* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GTX 650] + +pci:v000010DEd00000FC6sv00001043sd00008428* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GTX 650] (GTX650-DC-1GD5) + +pci:v000010DEd00000FC8* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 740] + +pci:v000010DEd00000FC9* + ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 730] + +pci:v000010DEd00000FCD* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 755M] + +pci:v000010DEd00000FCE* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M LE] + +pci:v000010DEd00000FD1* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] + +pci:v000010DEd00000FD1sv00001043sd00001597* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] (GeForce GT 650M) + +pci:v000010DEd00000FD1sv00001043sd000015A7* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] (GeForce GT 650M) + +pci:v000010DEd00000FD1sv00001043sd00002103* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] (N56VZ) + +pci:v000010DEd00000FD1sv00001043sd00002105* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] (GeForce GT 650M) + +pci:v000010DEd00000FD1sv00001043sd00002141* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M] (GeForce GT 650M) + +pci:v000010DEd00000FD2* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M] + +pci:v000010DEd00000FD2sv00001028sd0000054F* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M] (GeForce GT 640M) + +pci:v000010DEd00000FD2sv00001028sd0000055F* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M] (GeForce GT 640M) + +pci:v000010DEd00000FD2sv00001028sd00000595* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M] (GeForce GT 640M LE) + +pci:v000010DEd00000FD2sv00001028sd000005B2* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M] (GeForce GT 640M LE) + +pci:v000010DEd00000FD3* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M LE] + +pci:v000010DEd00000FD4* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GTX 660M] + +pci:v000010DEd00000FD5* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 650M Mac Edition] + +pci:v000010DEd00000FD8* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 640M Mac Edition] + +pci:v000010DEd00000FD9* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 645M] + +pci:v000010DEd00000FDB* + ID_MODEL_FROM_DATABASE=GK107M + +pci:v000010DEd00000FDF* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 740M] + +pci:v000010DEd00000FE0* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GTX 660M Mac Edition] + +pci:v000010DEd00000FE1* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 730M] + +pci:v000010DEd00000FE2* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 745M] + +pci:v000010DEd00000FE3* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 745M] + +pci:v000010DEd00000FE3sv0000103Csd00002B16* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 745M] (GeForce GT 745A) + +pci:v000010DEd00000FE3sv000017AAsd00003675* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 745M] (GeForce GT 745A) + +pci:v000010DEd00000FE4* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 750M] + +pci:v000010DEd00000FE5* + ID_MODEL_FROM_DATABASE=GK107 [GeForce K340 USM] + +pci:v000010DEd00000FE6* + ID_MODEL_FROM_DATABASE=GK107 [GRID K1 NVS USM] + +pci:v000010DEd00000FE7* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K100 vGPU] + +pci:v000010DEd00000FE7sv000010DEsd0000101E* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K100 vGPU] (GRID K100) + +pci:v000010DEd00000FE9* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 750M Mac Edition] + +pci:v000010DEd00000FEA* + ID_MODEL_FROM_DATABASE=GK107M [GeForce GT 755M Mac Edition] + +pci:v000010DEd00000FEC* + ID_MODEL_FROM_DATABASE=GK107M [GeForce 710A] + +pci:v000010DEd00000FED* + ID_MODEL_FROM_DATABASE=GK107M [GeForce 820M] + +pci:v000010DEd00000FEE* + ID_MODEL_FROM_DATABASE=GK107M [GeForce 810M] + +pci:v000010DEd00000FEF* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K340] + +pci:v000010DEd00000FF1* + ID_MODEL_FROM_DATABASE=GK107 [NVS 1000] + +pci:v000010DEd00000FF2* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K1] + +pci:v000010DEd00000FF3* + ID_MODEL_FROM_DATABASE=GK107GL [Quadro K420] + +pci:v000010DEd00000FF5* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K1 Tesla USM] + +pci:v000010DEd00000FF6* + ID_MODEL_FROM_DATABASE=GK107GLM [Quadro K1100M] + +pci:v000010DEd00000FF6sv0000103Csd0000197B* + ID_MODEL_FROM_DATABASE=GK107GLM [Quadro K1100M] (ZBook 15) + +pci:v000010DEd00000FF7* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K140Q vGPU] + +pci:v000010DEd00000FF7sv000010DEsd00001037* + ID_MODEL_FROM_DATABASE=GK107GL [GRID K140Q vGPU] (GRID K140Q) + +pci:v000010DEd00000FF8* + ID_MODEL_FROM_DATABASE=GK107GLM [Quadro K500M] + +pci:v000010DEd00000FF9* + ID_MODEL_FROM_DATABASE=GK107GL [Quadro K2000D] + +pci:v000010DEd00000FFA* + ID_MODEL_FROM_DATABASE=GK107GL [Quadro K600] + +pci:v000010DEd00000FFB* + ID_MODEL_FROM_DATABASE=GK107GLM [Quadro K2000M] + +pci:v000010DEd00000FFC* + ID_MODEL_FROM_DATABASE=GK107GLM [Quadro K1000M] + +pci:v000010DEd00000FFD* + ID_MODEL_FROM_DATABASE=GK107 [NVS 510] + +pci:v000010DEd00000FFE* + ID_MODEL_FROM_DATABASE=GK107GL [Quadro K2000] + +pci:v000010DEd00000FFF* + ID_MODEL_FROM_DATABASE=GK107GL [Quadro 410] + +pci:v000010DEd00001001* + ID_MODEL_FROM_DATABASE=GK110B [GeForce GTX TITAN Z] + +pci:v000010DEd00001003* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX Titan LE] + +pci:v000010DEd00001004* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780] + +pci:v000010DEd00001004sv00003842sd00000784* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780] (GK110B [GeForce GTX 780 SC w/ ACX Cooler]) + +pci:v000010DEd00001004sv00003842sd00001784* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780] (GK110B [GeForce GTX 780 Dual FTW w/ ACX Cooler]) + +pci:v000010DEd00001004sv00003842sd00001788* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780] (GK110B [GeForce GTX 780 Dual Classified w/ ACX Cooler]) + +pci:v000010DEd00001005* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] + +pci:v000010DEd00001005sv00001043sd00008451* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GTXTITAN-6GD5) + +pci:v000010DEd00001005sv000010DEsd00001035* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan) + +pci:v000010DEd00001005sv00003842sd00002790* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan) + +pci:v000010DEd00001005sv00003842sd00002791* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan SC) + +pci:v000010DEd00001005sv00003842sd00002793* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan SC Signature) + +pci:v000010DEd00001005sv00003842sd00002794* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan SC Hydro Copper) + +pci:v000010DEd00001005sv00003842sd00002795* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX TITAN] (GeForce GTX Titan SC Hydro Copper Signature) + +pci:v000010DEd00001007* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780 Rev. 2] + +pci:v000010DEd00001008* + ID_MODEL_FROM_DATABASE=GK110 [GeForce GTX 780 Ti Rev. 2] + +pci:v000010DEd0000100A* + ID_MODEL_FROM_DATABASE=GK110B [GeForce GTX 780 Ti] + +pci:v000010DEd0000100C* + ID_MODEL_FROM_DATABASE=GK110B [GeForce GTX TITAN Black] + +pci:v000010DEd0000101E* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20X] + +pci:v000010DEd0000101F* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20] + +pci:v000010DEd00001020* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20X] + +pci:v000010DEd00001021* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20Xm] + +pci:v000010DEd00001022* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20c] + +pci:v000010DEd00001023* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40m] + +pci:v000010DEd00001023sv000010DEsd0000097E* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40m] (12GB Computational Accelerator) + +pci:v000010DEd00001024* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40c] + +pci:v000010DEd00001026* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20s] + +pci:v000010DEd00001027* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40st] + +pci:v000010DEd00001028* + ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20m] + +pci:v000010DEd00001029* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40s] + +pci:v000010DEd0000102A* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40t] + +pci:v000010DEd0000102D* + ID_MODEL_FROM_DATABASE=GK210GL [Tesla K80] + +pci:v000010DEd0000102E* + ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40d] + +pci:v000010DEd0000103A* + ID_MODEL_FROM_DATABASE=GK110GL [Quadro K6000] + +pci:v000010DEd0000103C* + ID_MODEL_FROM_DATABASE=GK110GL [Quadro K5200] + +pci:v000010DEd00001040* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 520] + +pci:v000010DEd00001042* + ID_MODEL_FROM_DATABASE=GF119 [GeForce 510] + +pci:v000010DEd00001048* + ID_MODEL_FROM_DATABASE=GF119 [GeForce 605] + +pci:v000010DEd00001049* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 620 OEM] + +pci:v000010DEd0000104A* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 610] + +pci:v000010DEd0000104Asv000010B0sd0000104A* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 610] (Gainward GeForce GT 610) + +pci:v000010DEd0000104B* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 625 OEM] + +pci:v000010DEd0000104C* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 705] + +pci:v000010DEd0000104D* + ID_MODEL_FROM_DATABASE=GF119 [GeForce GT 710] + +pci:v000010DEd00001050* + ID_MODEL_FROM_DATABASE=GF119M [GeForce GT 520M] + +pci:v000010DEd00001051* + ID_MODEL_FROM_DATABASE=GF119M [GeForce GT 520MX] + +pci:v000010DEd00001052* + ID_MODEL_FROM_DATABASE=GF119M [GeForce GT 520M] + +pci:v000010DEd00001054* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 410M] + +pci:v000010DEd00001055* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 410M] + +pci:v000010DEd00001056* + ID_MODEL_FROM_DATABASE=GF119M [NVS 4200M] + +pci:v000010DEd00001057* + ID_MODEL_FROM_DATABASE=GF119M [Quadro NVS 4200M] + +pci:v000010DEd00001058* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] + +pci:v000010DEd00001058sv0000103Csd00002AED* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 610) + +pci:v000010DEd00001058sv0000103Csd00002AF1* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 610) + +pci:v000010DEd00001058sv00001043sd000010AC* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce GT 610M) + +pci:v000010DEd00001058sv00001043sd000010BC* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce GT 610M) + +pci:v000010DEd00001058sv00001043sd00001652* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce GT 610M) + +pci:v000010DEd00001058sv000017AAsd0000367A* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 610M) + +pci:v000010DEd00001058sv000017AAsd00003682* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 800A) + +pci:v000010DEd00001058sv000017AAsd00003687* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 800A) + +pci:v000010DEd00001058sv000017AAsd00003692* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 705A) + +pci:v000010DEd00001058sv000017AAsd00003695* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 800A) + +pci:v000010DEd00001058sv000017AAsd0000A117* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce 610M) + +pci:v000010DEd00001059* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] + +pci:v000010DEd0000105A* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] + +pci:v000010DEd0000105Asv00001043sd00002111* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce GT 610M) + +pci:v000010DEd0000105Asv00001043sd00002112* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 610M] (GeForce GT 610M) + +pci:v000010DEd0000105B* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] + +pci:v000010DEd0000105Bsv0000103Csd00002AFB* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] (GeForce 705A) + +pci:v000010DEd0000105Bsv000017AAsd0000309D* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] (GeForce 705A) + +pci:v000010DEd0000105Bsv000017AAsd000030B1* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] (GeForce 800A) + +pci:v000010DEd0000105Bsv000017AAsd000030F3* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] (GeForce 705A) + +pci:v000010DEd0000105Bsv000017AAsd000036A1* + ID_MODEL_FROM_DATABASE=GF119M [GeForce 705M] (GeForce 800A) + +pci:v000010DEd0000107C* + ID_MODEL_FROM_DATABASE=GF119 [NVS 315] + +pci:v000010DEd0000107D* + ID_MODEL_FROM_DATABASE=GF119 [NVS 310] + +pci:v000010DEd00001080* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 580] + +pci:v000010DEd00001081* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 570] + +pci:v000010DEd00001081sv000010DEsd0000087E* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 570] (Leadtek WinFast GTX 570) + +pci:v000010DEd00001082* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 560 Ti OEM] + +pci:v000010DEd00001084* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 560 OEM] + +pci:v000010DEd00001086* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 570 Rev. 2] + +pci:v000010DEd00001087* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 560 Ti 448 Cores] + +pci:v000010DEd00001088* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 590] + +pci:v000010DEd00001089* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 580] + +pci:v000010DEd0000108B* + ID_MODEL_FROM_DATABASE=GF110 [GeForce GTX 580] + +pci:v000010DEd0000108E* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla C2090] + +pci:v000010DEd00001091* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2090] + +pci:v000010DEd00001091sv000010DEsd0000088E* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2090] (Tesla X2090) + +pci:v000010DEd00001091sv000010DEsd00000891* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2090] (Tesla X2090) + +pci:v000010DEd00001091sv000010DEsd00000974* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2090] (Tesla X2090) + +pci:v000010DEd00001091sv000010DEsd0000098D* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2090] (Tesla X2090) + +pci:v000010DEd00001094* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2075] + +pci:v000010DEd00001094sv000010DEsd00000888* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla M2075] (Tesla M2075) + +pci:v000010DEd00001096* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla C2050 / C2075] + +pci:v000010DEd00001096sv000010DEsd00000910* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla C2050 / C2075] (Tesla C2075) + +pci:v000010DEd00001096sv000010DEsd00000911* + ID_MODEL_FROM_DATABASE=GF110GL [Tesla C2050 / C2075] (Tesla C2050) + +pci:v000010DEd0000109A* + ID_MODEL_FROM_DATABASE=GF100GLM [Quadro 5010M] + +pci:v000010DEd0000109B* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 7000] + +pci:v000010DEd0000109Bsv000010DEsd00000918* + ID_MODEL_FROM_DATABASE=GF100GL [Quadro 7000] (Quadro 7000) + +pci:v000010DEd000010C0* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 9300 GS Rev. 2] + +pci:v000010DEd000010C3* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 8400 GS Rev. 3] + +pci:v000010DEd000010C5* + ID_MODEL_FROM_DATABASE=GT218 [GeForce 405] + +pci:v000010DEd000010D8* + ID_MODEL_FROM_DATABASE=GT218 [NVS 300] + +pci:v000010DEd000010F0* + ID_MODEL_FROM_DATABASE=GP104 High Definition Audio Controller + +pci:v000010DEd00001140* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] + +pci:v000010DEd00001140sv00001019sd00000799* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001019sd0000999F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000600* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000606* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd0000064A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd0000064C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd0000067A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000680* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000686* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000689* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000068B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000068D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000068E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000691* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000692* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000694* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000702* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000719* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000725* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000728* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd0000072B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd0000072E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000732* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001025sd00000763* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000773* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000774* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000776* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd0000077A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000077B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000077C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000077D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000077E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd0000077F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000781* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000798* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000799* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd0000079B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd0000079C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000807* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000821* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000823* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000830* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000833* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd00000837* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001025sd0000083E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000841* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001025sd00000854* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000855* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000856* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000857* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000858* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000863* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000868* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000869* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 810M) + +pci:v000010DEd00001140sv00001025sd00000873* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000878* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000087B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000087C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 810M) + +pci:v000010DEd00001140sv00001025sd00000881* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000088A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000089B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000090F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000921* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000092E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 810M) + +pci:v000010DEd00001140sv00001025sd0000092F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000932* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000093A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000093C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd0000093F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000941* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000945* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000954* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001025sd00000965* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd0000054D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd0000054E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd00000554* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001028sd00000557* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001028sd00000562* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd00000565* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd00000568* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd00000590* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd00000592* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd00000594* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd00000595* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd000005A2* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd000005B1* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd000005B3* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 625M) + +pci:v000010DEd00001140sv00001028sd000005DA* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd000005DE* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001028sd000005E0* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001028sd000005E8* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv00001028sd000005F4* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001028sd0000060F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001028sd0000064E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd00000652* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd00000653* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd00000655* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd0000065E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd00000662* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd0000068D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001028sd000006C1* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000103Csd000018EF* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000103Csd000018F9* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000103Csd000018FB* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000103Csd000018FD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000103Csd000018FF* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000103Csd00002335* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000103Csd00002337* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000103Csd00002AEF* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720A) + +pci:v000010DEd00001140sv0000103Csd00002AF9* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710A) + +pci:v000010DEd00001140sv00001043sd000010DD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv00001043sd000010ED* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv00001043sd000011FD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000124D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000126D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000131D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000013FD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000014C7* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd00001507* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001043sd000015AD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000015ED* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000160D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000163D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000166D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000016CD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000016DD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000170D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000176D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000178D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000179D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000017DD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd00002132* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001043sd00002136* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv00001043sd000021BA* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000021FA* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000220A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000221A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd0000223A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 710M) + +pci:v000010DEd00001140sv00001043sd0000224A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 710M) + +pci:v000010DEd00001140sv00001043sd0000227A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000228A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000232A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000233A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000236A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000238A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd00008595* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000085EA* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000085EB* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000085EC* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd000085EE* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001043sd000085F3* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000860E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000861A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000861B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd00008628* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd00008643* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd0000864C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001043sd00008652* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000105Bsd00000DAC* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv0000105Bsd00000DAD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv0000105Bsd00000EF3* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001072sd0000152D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000010CFsd000017F5* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001179sd0000FA01* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA02* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA03* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA05* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA11* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA13* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA18* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA19* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA21* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA23* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA2A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA32* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA33* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA36* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA38* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA42* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA43* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA45* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA47* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA49* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA58* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA59* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA88* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001179sd0000FA89* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000144Dsd0000B092* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv0000144Dsd0000C0D5* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000144Dsd0000C0D7* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv0000144Dsd0000C0E2* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv0000144Dsd0000C0E3* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv0000144Dsd0000C0E4* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv0000144Dsd0000C10D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000144Dsd0000C652* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv0000144Dsd0000C709* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000144Dsd0000C711* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000144Dsd0000C736* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000144Dsd0000C737* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000144Dsd0000C745* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000144Dsd0000C750* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001462sd000010B8* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 710M) + +pci:v000010DEd00001140sv00001462sd000010E9* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001462sd00001116* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001462sd0000AA33* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 720M) + +pci:v000010DEd00001140sv00001462sd0000AAA2* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001462sd0000AAA3* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001462sd0000ACB2* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001462sd0000ACC1* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001462sd0000AE61* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 720M) + +pci:v000010DEd00001140sv00001462sd0000AE65* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001462sd0000AE6A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001462sd0000AE71* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000014C0sd00000083* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000152Dsd00000926* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 620M) + +pci:v000010DEd00001140sv0000152Dsd00000982* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000152Dsd00000983* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000152Dsd00001005* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 820M) + +pci:v000010DEd00001140sv0000152Dsd00001012* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000152Dsd00001019* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000152Dsd00001030* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 630M) + +pci:v000010DEd00001140sv0000152Dsd00001055* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv0000152Dsd00001067* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv0000152Dsd00001072* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv0000152Dsd00001086* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv0000152Dsd00001092* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00002200* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (NVS 5200M) + +pci:v000010DEd00001140sv000017AAsd00002213* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00002220* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd0000309C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720A) + +pci:v000010DEd00001140sv000017AAsd000030B4* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd000030B7* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 720A) + +pci:v000010DEd00001140sv000017AAsd0000361B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000361C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd00003656* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv000017AAsd0000365A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 705M) + +pci:v000010DEd00001140sv000017AAsd0000365E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 800M) + +pci:v000010DEd00001140sv000017AAsd00003661* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000366C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 800M) + +pci:v000010DEd00001140sv000017AAsd00003685* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 800M) + +pci:v000010DEd00001140sv000017AAsd00003686* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 800M) + +pci:v000010DEd00001140sv000017AAsd00003687* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 705A) + +pci:v000010DEd00001140sv000017AAsd00003696* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000369B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000369C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000369D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd0000369E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd000036A9* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd000036AF* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd000036B0* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd000036B6* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820A) + +pci:v000010DEd00001140sv000017AAsd00003800* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003801* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003802* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003803* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003804* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003806* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003808* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd0000380D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd0000380E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd0000380F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003811* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003812* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003813* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003816* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003818* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd0000381A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd0000381C* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003901* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 610M / GT 620M) + +pci:v000010DEd00001140sv000017AAsd00003902* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd00003903* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 610M/710M) + +pci:v000010DEd00001140sv000017AAsd00003904* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M/625M) + +pci:v000010DEd00001140sv000017AAsd00003905* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003907* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003910* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 720M) + +pci:v000010DEd00001140sv000017AAsd00003912* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 720M) + +pci:v000010DEd00001140sv000017AAsd00003913* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003915* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00003977* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00003983* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 610M) + +pci:v000010DEd00001140sv000017AAsd00005001* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 610M) + +pci:v000010DEd00001140sv000017AAsd00005003* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00005005* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 705M) + +pci:v000010DEd00001140sv000017AAsd0000500D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv000017AAsd00005014* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd00005017* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd00005019* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000501A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000501F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00005025* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd00005027* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000502A* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000502B* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd0000502D* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000502E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd0000502F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv000017AAsd00005030* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 705M) + +pci:v000010DEd00001140sv000017AAsd00005031* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 705M) + +pci:v000010DEd00001140sv000017AAsd00005032* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00005033* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd0000503E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv000017AAsd0000503F* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv000017AAsd00005040* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001854sd00000177* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001854sd00000180* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 710M) + +pci:v000010DEd00001140sv00001854sd00000190* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001854sd00000192* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001854sd00000224* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B0Asd000001C0* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B0Asd000020DD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001B0Asd000020DF* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 620M) + +pci:v000010DEd00001140sv00001B0Asd0000210E* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B0Asd00002202* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001B0Asd000090D7* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B0Asd000090DD* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B50sd00005530* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001B6Csd00005531* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce GT 720M) + +pci:v000010DEd00001140sv00001BABsd00000106* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 820M) + +pci:v000010DEd00001140sv00001D05sd00001013* + ID_MODEL_FROM_DATABASE=GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] (GeForce 810M) + +pci:v000010DEd00001180* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 680] + +pci:v000010DEd00001180sv00001043sd000083F1* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 680] (GTX680-DC2-2GD5) + +pci:v000010DEd00001180sv00003842sd00003682* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 680] (GeForce GTX 680 Mac Edition) + +pci:v000010DEd00001182* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 760 Ti] + +pci:v000010DEd00001183* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 660 Ti] + +pci:v000010DEd00001184* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 770] + +pci:v000010DEd00001185* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 660 OEM] + +pci:v000010DEd00001185sv000010DEsd0000106F* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 660 OEM] (GK104 [GeForce GTX 760 OEM]) + +pci:v000010DEd00001187* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 760] + +pci:v000010DEd00001188* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 690] + +pci:v000010DEd00001189* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 670] + +pci:v000010DEd00001189sv000010DEsd00001074* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 670] (GK104 [GeForce GTX 760 Ti OEM]) + +pci:v000010DEd0000118A* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K520] + +pci:v000010DEd0000118B* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K2 GeForce USM] + +pci:v000010DEd0000118C* + ID_MODEL_FROM_DATABASE=GK104 [GRID K2 NVS USM] + +pci:v000010DEd0000118D* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K200 vGPU] + +pci:v000010DEd0000118Dsv000010DEsd0000101D* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K200 vGPU] (GRID K200) + +pci:v000010DEd0000118E* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 760 OEM] + +pci:v000010DEd0000118F* + ID_MODEL_FROM_DATABASE=GK104GL [Tesla K10] + +pci:v000010DEd00001191* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 760 Rev. 2] + +pci:v000010DEd00001193* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 760 Ti OEM] + +pci:v000010DEd00001194* + ID_MODEL_FROM_DATABASE=GK104GL [Tesla K8] + +pci:v000010DEd00001195* + ID_MODEL_FROM_DATABASE=GK104 [GeForce GTX 660 Rev. 2] + +pci:v000010DEd00001198* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 880M] + +pci:v000010DEd00001199* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 870M] + +pci:v000010DEd0000119A* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 860M] + +pci:v000010DEd0000119D* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 775M Mac Edition] + +pci:v000010DEd0000119E* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 780M Mac Edition] + +pci:v000010DEd0000119F* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 780M] + +pci:v000010DEd000011A0* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 680M] + +pci:v000010DEd000011A1* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 670MX] + +pci:v000010DEd000011A2* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 675MX Mac Edition] + +pci:v000010DEd000011A3* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 680MX] + +pci:v000010DEd000011A3sv0000106Bsd0000010D* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 680MX] (iMac 13,2) + +pci:v000010DEd000011A7* + ID_MODEL_FROM_DATABASE=GK104M [GeForce GTX 675MX] + +pci:v000010DEd000011B0* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K240Q\K260Q vGPU] + +pci:v000010DEd000011B0sv000010DEsd0000101A* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K240Q\K260Q vGPU] (GRID K240Q) + +pci:v000010DEd000011B0sv000010DEsd0000101B* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K240Q\K260Q vGPU] (GRID K260Q) + +pci:v000010DEd000011B1* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K2 Tesla USM] + +pci:v000010DEd000011B4* + ID_MODEL_FROM_DATABASE=GK104GL [Quadro K4200] + +pci:v000010DEd000011B6* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K3100M] + +pci:v000010DEd000011B7* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K4100M] + +pci:v000010DEd000011B8* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K5100M] + +pci:v000010DEd000011BA* + ID_MODEL_FROM_DATABASE=GK104GL [Quadro K5000] + +pci:v000010DEd000011BB* + ID_MODEL_FROM_DATABASE=GK104GL [Quadro 4100] + +pci:v000010DEd000011BC* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K5000M] + +pci:v000010DEd000011BD* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K4000M] + +pci:v000010DEd000011BE* + ID_MODEL_FROM_DATABASE=GK104GLM [Quadro K3000M] + +pci:v000010DEd000011BF* + ID_MODEL_FROM_DATABASE=GK104GL [GRID K2] + +pci:v000010DEd000011C0* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 660] + +pci:v000010DEd000011C2* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] + +pci:v000010DEd000011C2sv00001043sd0000845B* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost DirectCU II OC) + +pci:v000010DEd000011C2sv00001462sd00002874* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost TwinFrozr II OC) + +pci:v000010DEd000011C2sv00001569sd000011C2* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost OC) + +pci:v000010DEd000011C2sv000019DAsd00001281* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost OC) + +pci:v000010DEd000011C2sv00003842sd00003657* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost) + +pci:v000010DEd000011C2sv00003842sd00003658* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti Boost] (GeForce GTX 650 Ti Boost Superclocked) + +pci:v000010DEd000011C3* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti OEM] + +pci:v000010DEd000011C3sv000010DEsd00001030* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti OEM] (GeForce GTX 650 Ti OEM) + +pci:v000010DEd000011C4* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 645 OEM] + +pci:v000010DEd000011C5* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GT 740] + +pci:v000010DEd000011C6* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 Ti] + +pci:v000010DEd000011C7* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 750 Ti] + +pci:v000010DEd000011C8* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GTX 650 OEM] + +pci:v000010DEd000011CB* + ID_MODEL_FROM_DATABASE=GK106 [GeForce GT 740] + +pci:v000010DEd000011E0* + ID_MODEL_FROM_DATABASE=GK106M [GeForce GTX 770M] + +pci:v000010DEd000011E1* + ID_MODEL_FROM_DATABASE=GK106M [GeForce GTX 765M] + +pci:v000010DEd000011E2* + ID_MODEL_FROM_DATABASE=GK106M [GeForce GTX 765M] + +pci:v000010DEd000011E3* + ID_MODEL_FROM_DATABASE=GK106M [GeForce GTX 760M] + +pci:v000010DEd000011E3sv000017AAsd00003683* + ID_MODEL_FROM_DATABASE=GK106M [GeForce GTX 760M] (GeForce GTX 760A) + +pci:v000010DEd000011E7* + ID_MODEL_FROM_DATABASE=GK106M + +pci:v000010DEd000011FA* + ID_MODEL_FROM_DATABASE=GK106GL [Quadro K4000] + +pci:v000010DEd000011FC* + ID_MODEL_FROM_DATABASE=GK106GLM [Quadro K2100M] + +pci:v000010DEd00001200* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 560 Ti] + +pci:v000010DEd00001201* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 560] + +pci:v000010DEd00001202* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 560 Ti OEM] + +pci:v000010DEd00001203* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 460 SE v2] + +pci:v000010DEd00001205* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 460 v2] + +pci:v000010DEd00001206* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 555] + +pci:v000010DEd00001207* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GT 645 OEM] + +pci:v000010DEd00001208* + ID_MODEL_FROM_DATABASE=GF114 [GeForce GTX 560 SE] + +pci:v000010DEd00001210* + ID_MODEL_FROM_DATABASE=GF114M [GeForce GTX 570M] + +pci:v000010DEd00001211* + ID_MODEL_FROM_DATABASE=GF114M [GeForce GTX 580M] + +pci:v000010DEd00001212* + ID_MODEL_FROM_DATABASE=GF114M [GeForce GTX 675M] + +pci:v000010DEd00001213* + ID_MODEL_FROM_DATABASE=GF114M [GeForce GTX 670M] + +pci:v000010DEd00001241* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GT 545 OEM] + +pci:v000010DEd00001243* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GT 545] + +pci:v000010DEd00001244* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GTX 550 Ti] + +pci:v000010DEd00001245* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GTS 450 Rev. 2] + +pci:v000010DEd00001246* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 550M] + +pci:v000010DEd00001247* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] + +pci:v000010DEd00001247sv00001043sd00001752* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001247sv00001043sd00002050* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001247sv00001043sd00002051* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001247sv00001043sd0000212A* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001247sv00001043sd0000212B* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001247sv00001043sd0000212C* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001247sv0000152Dsd00000930* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001248* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] + +pci:v000010DEd00001248sv0000152Dsd00000930* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001248sv000017C0sd000010E7* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv000017C0sd000010E8* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv000017C0sd000010EA* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv00001854sd00000890* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv00001854sd00000891* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv00001854sd00001795* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv00001854sd00001796* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001248sv00001854sd00003005* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd00001249* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GTS 450 Rev. 3] + +pci:v000010DEd0000124B* + ID_MODEL_FROM_DATABASE=GF116 [GeForce GT 640 OEM] + +pci:v000010DEd0000124D* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] + +pci:v000010DEd0000124Dsv00001028sd00000491* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd0000124Dsv00001028sd00000570* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd0000124Dsv00001028sd00000571* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd0000124Dsv00001462sd0000108D* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 555M) + +pci:v000010DEd0000124Dsv00001462sd000010CC* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 555M/635M] (GeForce GT 635M) + +pci:v000010DEd00001251* + ID_MODEL_FROM_DATABASE=GF116M [GeForce GT 560M] + +pci:v000010DEd00001280* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 635] + +pci:v000010DEd00001281* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 710] + +pci:v000010DEd00001282* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 640 Rev. 2] + +pci:v000010DEd00001284* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 630 Rev. 2] + +pci:v000010DEd00001286* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 720] + +pci:v000010DEd00001287* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 730] + +pci:v000010DEd00001288* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 720] + +pci:v000010DEd00001289* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 710] + +pci:v000010DEd0000128B* + ID_MODEL_FROM_DATABASE=GK208 [GeForce GT 710B] + +pci:v000010DEd00001290* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] + +pci:v000010DEd00001290sv0000103Csd00002AFA* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] (GeForce GT 730A) + +pci:v000010DEd00001290sv0000103Csd00002B04* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] (GeForce GT 730A) + +pci:v000010DEd00001290sv00001043sd000013AD* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] (GeForce GT 730M) + +pci:v000010DEd00001290sv00001043sd000013CD* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] (GeForce GT 730M) + +pci:v000010DEd00001291* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 735M] + +pci:v000010DEd00001292* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 740M] + +pci:v000010DEd00001292sv000017AAsd00003675* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 740M] (GeForce GT 740A) + +pci:v000010DEd00001292sv000017AAsd0000367C* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 740M] (GeForce GT 740A) + +pci:v000010DEd00001292sv000017AAsd00003684* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 740M] (GeForce GT 740A) + +pci:v000010DEd00001293* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 730M] + +pci:v000010DEd00001294* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 740M] + +pci:v000010DEd00001295* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] + +pci:v000010DEd00001295sv0000103Csd00002B0D* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 710A) + +pci:v000010DEd00001295sv0000103Csd00002B0F* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 710A) + +pci:v000010DEd00001295sv0000103Csd00002B11* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 710A) + +pci:v000010DEd00001295sv0000103Csd00002B20* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 810A) + +pci:v000010DEd00001295sv0000103Csd00002B21* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 810A) + +pci:v000010DEd00001295sv0000103Csd00002B22* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 810A) + +pci:v000010DEd00001295sv000017AAsd0000367A* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 805A) + +pci:v000010DEd00001295sv000017AAsd0000367C* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 710M] (GeForce 710A) + +pci:v000010DEd00001296* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 825M] + +pci:v000010DEd00001298* + ID_MODEL_FROM_DATABASE=GK208M [GeForce GT 720M] + +pci:v000010DEd00001299* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 920M] + +pci:v000010DEd00001299sv000017AAsd000030BB* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 920M] (GeForce 920A) + +pci:v000010DEd00001299sv000017AAsd000036A7* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 920M] (GeForce 920A) + +pci:v000010DEd00001299sv000017AAsd000036AF* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 920M] (GeForce 920M) + +pci:v000010DEd0000129A* + ID_MODEL_FROM_DATABASE=GK208M [GeForce 910M] + +pci:v000010DEd000012A0* + ID_MODEL_FROM_DATABASE=GK208 + +pci:v000010DEd000012B9* + ID_MODEL_FROM_DATABASE=GK208GLM [Quadro K610M] + +pci:v000010DEd000012BA* + ID_MODEL_FROM_DATABASE=GK208GLM [Quadro K510M] + +pci:v000010DEd00001340* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 830M] + +pci:v000010DEd00001340sv0000103Csd00002B2B* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 830M] (GeForce 830A) + +pci:v000010DEd00001341* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 840M] + +pci:v000010DEd00001341sv000017AAsd00003697* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 840M] (GeForce 840A) + +pci:v000010DEd00001341sv000017AAsd00003699* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 840M] (GeForce 840A) + +pci:v000010DEd00001341sv000017AAsd0000369C* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 840M] (GeForce 840A) + +pci:v000010DEd00001344* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 845M] + +pci:v000010DEd00001346* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 930M] + +pci:v000010DEd00001347* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 940M] + +pci:v000010DEd00001348* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 945M / 945A] + +pci:v000010DEd00001349* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 930M] + +pci:v000010DEd0000134D* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 940MX] + +pci:v000010DEd0000134E* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 930MX] + +pci:v000010DEd0000134F* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 920MX] + +pci:v000010DEd0000137A* + ID_MODEL_FROM_DATABASE=GM108GLM [Quadro K620M / Quadro M500M] + +pci:v000010DEd0000137Asv000017AAsd0000505A* + ID_MODEL_FROM_DATABASE=GM108GLM [Quadro K620M / Quadro M500M] (Quadro M500M) + +pci:v000010DEd0000137D* + ID_MODEL_FROM_DATABASE=GM108M [GeForce 940A] + +pci:v000010DEd00001380* + ID_MODEL_FROM_DATABASE=GM107 [GeForce GTX 750 Ti] + +pci:v000010DEd00001381* + ID_MODEL_FROM_DATABASE=GM107 [GeForce GTX 750] + +pci:v000010DEd00001382* + ID_MODEL_FROM_DATABASE=GM107 [GeForce GTX 745] + +pci:v000010DEd00001389* + ID_MODEL_FROM_DATABASE=GM107GL [GRID M30] + +pci:v000010DEd00001390* + ID_MODEL_FROM_DATABASE=GM107M [GeForce 845M] + +pci:v000010DEd00001391* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 850M] + +pci:v000010DEd00001391sv000017AAsd00003697* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 850M] (GeForce GTX 850A) + +pci:v000010DEd00001391sv000017AAsd0000A125* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 850M] (GeForce GTX 850A) + +pci:v000010DEd00001392* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 860M] + +pci:v000010DEd00001393* + ID_MODEL_FROM_DATABASE=GM107M [GeForce 840M] + +pci:v000010DEd00001398* + ID_MODEL_FROM_DATABASE=GM107M [GeForce 845M] + +pci:v000010DEd0000139A* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] + +pci:v000010DEd0000139Asv000017AAsd0000362C* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139Asv000017AAsd0000362F* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139Asv000017AAsd0000363F* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139Asv000017AAsd00003640* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139Asv000017AAsd00003647* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139Asv000017AAsd000036B9* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 950M] (GeForce GTX 950A) + +pci:v000010DEd0000139B* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 960M] + +pci:v000010DEd0000139Bsv0000103Csd00002B4C* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 960M] (GeForce GTX 960A) + +pci:v000010DEd0000139C* + ID_MODEL_FROM_DATABASE=GM107M [GeForce 940M] + +pci:v000010DEd0000139D* + ID_MODEL_FROM_DATABASE=GM107M [GeForce GTX 750 Ti] + +pci:v000010DEd000013B0* + ID_MODEL_FROM_DATABASE=GM107GLM [Quadro M2000M] + +pci:v000010DEd000013B1* + ID_MODEL_FROM_DATABASE=GM107GLM [Quadro M1000M] + +pci:v000010DEd000013B2* + ID_MODEL_FROM_DATABASE=GM107GLM [Quadro M600M] + +pci:v000010DEd000013B3* + ID_MODEL_FROM_DATABASE=GM107GLM [Quadro K2200M] + +pci:v000010DEd000013B9* + ID_MODEL_FROM_DATABASE=GM107GL [NVS 810] + +pci:v000010DEd000013BA* + ID_MODEL_FROM_DATABASE=GM107GL [Quadro K2200] + +pci:v000010DEd000013BB* + ID_MODEL_FROM_DATABASE=GM107GL [Quadro K620] + +pci:v000010DEd000013BC* + ID_MODEL_FROM_DATABASE=GM107GL [Quadro K1200] + +pci:v000010DEd000013BD* + ID_MODEL_FROM_DATABASE=GM107GL [Tesla M10] + +pci:v000010DEd000013BDsv000010DEsd0000110A* + ID_MODEL_FROM_DATABASE=GM107GL [Tesla M10] (GRID M40) + +pci:v000010DEd000013BDsv000010DEsd00001160* + ID_MODEL_FROM_DATABASE=GM107GL [Tesla M10] (Tesla M10) + +pci:v000010DEd000013C0* + ID_MODEL_FROM_DATABASE=GM204 [GeForce GTX 980] + +pci:v000010DEd000013C0sv00001043sd00008504* + ID_MODEL_FROM_DATABASE=GM204 [GeForce GTX 980] (GTX980-4GD5) + +pci:v000010DEd000013C1* + ID_MODEL_FROM_DATABASE=GM204 + +pci:v000010DEd000013C2* + ID_MODEL_FROM_DATABASE=GM204 [GeForce GTX 970] + +pci:v000010DEd000013C3* + ID_MODEL_FROM_DATABASE=GM204 + +pci:v000010DEd000013D7* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 980M] + +pci:v000010DEd000013D8* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 970M] + +pci:v000010DEd000013D9* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 965M] + +pci:v000010DEd000013DA* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 980] + +pci:v000010DEd000013F0* + ID_MODEL_FROM_DATABASE=GM204GL [Quadro M5000] + +pci:v000010DEd000013F1* + ID_MODEL_FROM_DATABASE=GM204GL [Quadro M4000] + +pci:v000010DEd000013F2* + ID_MODEL_FROM_DATABASE=GM204GL [Tesla M60] + +pci:v000010DEd000013F3* + ID_MODEL_FROM_DATABASE=GM204GL [Tesla M6] + +pci:v000010DEd000013F8* + ID_MODEL_FROM_DATABASE=GM204GLM [Quadro M5000M] + +pci:v000010DEd000013F9* + ID_MODEL_FROM_DATABASE=GM204GLM [Quadro M4000M] + +pci:v000010DEd000013FA* + ID_MODEL_FROM_DATABASE=GM204GLM [Quadro M3000M] + +pci:v000010DEd000013FB* + ID_MODEL_FROM_DATABASE=GM204GLM [Quadro M5500] + +pci:v000010DEd00001401* + ID_MODEL_FROM_DATABASE=GM206 [GeForce GTX 960] + +pci:v000010DEd00001402* + ID_MODEL_FROM_DATABASE=GM206 [GeForce GTX 950] + +pci:v000010DEd00001406* + ID_MODEL_FROM_DATABASE=GM206 [GeForce GTX 960] + +pci:v000010DEd00001407* + ID_MODEL_FROM_DATABASE=GM206 [GeForce GTX 750 v2] + +pci:v000010DEd00001427* + ID_MODEL_FROM_DATABASE=GM206M [GeForce GTX 965M] + +pci:v000010DEd00001430* + ID_MODEL_FROM_DATABASE=GM206GL [Quadro M2000] + +pci:v000010DEd00001431* + ID_MODEL_FROM_DATABASE=GM206GL [Tesla M4] + +pci:v000010DEd000015F0* + ID_MODEL_FROM_DATABASE=GP100GL + +pci:v000010DEd000015F1* + ID_MODEL_FROM_DATABASE=GP100GL + +pci:v000010DEd000015F8* + ID_MODEL_FROM_DATABASE=GP100GL + +pci:v000010DEd000015F9* + ID_MODEL_FROM_DATABASE=GP100GL + +pci:v000010DEd00001617* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 980M] + +pci:v000010DEd00001618* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 970M] + +pci:v000010DEd00001619* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 965M] + +pci:v000010DEd0000161A* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 980] + +pci:v000010DEd00001667* + ID_MODEL_FROM_DATABASE=GM204M [GeForce GTX 965M] + +pci:v000010DEd00001725* + ID_MODEL_FROM_DATABASE=GP100 + +pci:v000010DEd0000172E* + ID_MODEL_FROM_DATABASE=GP100 + +pci:v000010DEd0000172F* + ID_MODEL_FROM_DATABASE=GP100 + +pci:v000010DEd000017C2* + ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX TITAN X] + +pci:v000010DEd000017C8* + ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX 980 Ti] + +pci:v000010DEd000017F0* + ID_MODEL_FROM_DATABASE=GM200GL [Quadro M6000] + +pci:v000010DEd000017F1* + ID_MODEL_FROM_DATABASE=GM200GL [Quadro M6000 24GB] + +pci:v000010DEd000017FD* + ID_MODEL_FROM_DATABASE=GM200GL [Tesla M40] + +pci:v000010DEd00001B00* + ID_MODEL_FROM_DATABASE=GP102 + +pci:v000010DEd00001B01* + ID_MODEL_FROM_DATABASE=GP102 + +pci:v000010DEd00001B70* + ID_MODEL_FROM_DATABASE=GP102GL + +pci:v000010DEd00001B78* + ID_MODEL_FROM_DATABASE=GP102GL + +pci:v000010DEd00001B80* + ID_MODEL_FROM_DATABASE=GP104 [GeForce GTX 1080] + +pci:v000010DEd00001B81* + ID_MODEL_FROM_DATABASE=GP104 [GeForce GTX 1070] + +pci:v000010DEd00001B82* + ID_MODEL_FROM_DATABASE=GP104 + +pci:v000010DEd00001B83* + ID_MODEL_FROM_DATABASE=GP104 + +pci:v000010DEd00001BA1* + ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1070] + +pci:v000010DEd00001BB0* + ID_MODEL_FROM_DATABASE=GP104GL + +pci:v000010DEd00001BB1* + ID_MODEL_FROM_DATABASE=GP104GL + +pci:v000010DEd00001BB4* + ID_MODEL_FROM_DATABASE=GP104GL + +pci:v000010DEd00001BE0* + ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1080] + +pci:v000010DEd00001BE1* + ID_MODEL_FROM_DATABASE=GP104M [GeForce GTX 1070] + +pci:v000010DEd00001C00* + ID_MODEL_FROM_DATABASE=GP106 + +pci:v000010DEd00001C01* + ID_MODEL_FROM_DATABASE=GP106 + +pci:v000010DEd00001C02* + ID_MODEL_FROM_DATABASE=GP106 + +pci:v000010DEd00001C03* + ID_MODEL_FROM_DATABASE=GP106 [GeForce GTX 1060] + +pci:v000010DEd00001C30* + ID_MODEL_FROM_DATABASE=GP106GL + +pci:v000010DEd00001C70* + ID_MODEL_FROM_DATABASE=GP106GL + +pci:v000010DEd00001C80* + ID_MODEL_FROM_DATABASE=GP107 + +pci:v000010DEd00001C81* + ID_MODEL_FROM_DATABASE=GP107 + +pci:v000010DEd00001C82* + ID_MODEL_FROM_DATABASE=GP107 + +pci:v000010DEd00001CA7* + ID_MODEL_FROM_DATABASE=GP107GL + +pci:v000010DEd00001CA8* + ID_MODEL_FROM_DATABASE=GP107GL + +pci:v000010DEd00001CAA* + ID_MODEL_FROM_DATABASE=GP107GL + +pci:v000010DEd00001D01* + ID_MODEL_FROM_DATABASE=GP108 + +pci:v000010DF* + ID_VENDOR_FROM_DATABASE=Emulex Corporation + +pci:v000010DFd00000720* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) + +pci:v000010DFd00000720sv0000103Csd00001934* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (FlexFabric 20Gb 2-port 650M Adapter) + +pci:v000010DFd00000720sv0000103Csd00001935* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (FlexFabric 20Gb 2-port 650FLB Adapter) + +pci:v000010DFd00000720sv0000103Csd000021D4* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (StoreFabric CN1200E 10Gb Converged Network Adapter) + +pci:v000010DFd00000720sv0000103Csd0000220A* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (FlexFabric 10Gb 2-port 556FLR-SFP+ Adapter) + +pci:v000010DFd00000720sv0000103Csd0000803F* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (Ethernet 10Gb 2-port 557SFP+ Adapter) + +pci:v000010DFd00000720sv000017AAsd00001056* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (ThinkServer OCm14102-UX-L AnyFabric) + +pci:v000010DFd00000720sv000017AAsd00001057* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (ThinkServer OCm14104-UX-L AnyFabric) + +pci:v000010DFd00000720sv000017AAsd00001059* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (ThinkServer OCm14104-UT-L AnyFabric) + +pci:v000010DFd00000720sv000017AAsd00004014* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk) (ThinkServer OCm14102-NX-L AnyFabric) + +pci:v000010DFd00000722* + ID_MODEL_FROM_DATABASE=OneConnect iSCSI Initiator (Skyhawk) + +pci:v000010DFd00000723* + ID_MODEL_FROM_DATABASE=OneConnect iSCSI Initiator + Target (Skyhawk) + +pci:v000010DFd00000724* + ID_MODEL_FROM_DATABASE=OneConnect FCoE Initiator (Skyhawk) + +pci:v000010DFd00000728* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Skyhawk-VF) + +pci:v000010DFd0000072A* + ID_MODEL_FROM_DATABASE=OneConnect iSCSI Initiator (Skyhawk-VF) + +pci:v000010DFd0000072B* + ID_MODEL_FROM_DATABASE=OneConnect iSCSI Initiator + Target (Skyhawk-VF) + +pci:v000010DFd0000072C* + ID_MODEL_FROM_DATABASE=OneConnect FCoE Initiator (Skyhawk-VF) + +pci:v000010DFd00001AE5* + ID_MODEL_FROM_DATABASE=LP6000 Fibre Channel Host Adapter + +pci:v000010DFd0000E100* + ID_MODEL_FROM_DATABASE=Proteus-X: LightPulse IOV Fibre Channel Host Adapter + +pci:v000010DFd0000E131* + ID_MODEL_FROM_DATABASE=LightPulse 8Gb/s PCIe Shared I/O Fibre Channel Adapter + +pci:v000010DFd0000E180* + ID_MODEL_FROM_DATABASE=Proteus-X: LightPulse IOV Fibre Channel Host Adapter + +pci:v000010DFd0000E200* + ID_MODEL_FROM_DATABASE=Lancer-X: LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000E208* + ID_MODEL_FROM_DATABASE=LightPulse 16Gb Fibre Channel Host Adapter (Lancer-VF) + +pci:v000010DFd0000E220* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Lancer) + +pci:v000010DFd0000E220sv000017AAsd00001054* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Lancer) (ThinkServer LPm16002B-M6-L AnyFabric) + +pci:v000010DFd0000E220sv000017AAsd00001055* + ID_MODEL_FROM_DATABASE=OneConnect NIC (Lancer) (ThinkServer LPm16004B-M8-L AnyFabric) + +pci:v000010DFd0000E240* + ID_MODEL_FROM_DATABASE=OneConnect iSCSI Initiator (Lancer) + +pci:v000010DFd0000E260* + ID_MODEL_FROM_DATABASE=OneConnect FCoE Initiator (Lancer) + +pci:v000010DFd0000E268* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb FCoE Converged Network Adapter (Lancer-VF) + +pci:v000010DFd0000E300* + ID_MODEL_FROM_DATABASE=Lancer Gen6: LPe32000 Fibre Channel Host Adapter + +pci:v000010DFd0000F011* + ID_MODEL_FROM_DATABASE=Saturn: LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F015* + ID_MODEL_FROM_DATABASE=Saturn: LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F085* + ID_MODEL_FROM_DATABASE=LP850 Fibre Channel Host Adapter + +pci:v000010DFd0000F095* + ID_MODEL_FROM_DATABASE=LP952 Fibre Channel Host Adapter + +pci:v000010DFd0000F098* + ID_MODEL_FROM_DATABASE=LP982 Fibre Channel Host Adapter + +pci:v000010DFd0000F0A1* + ID_MODEL_FROM_DATABASE=Thor LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0A5* + ID_MODEL_FROM_DATABASE=Thor LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0B5* + ID_MODEL_FROM_DATABASE=Viper LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0D1* + ID_MODEL_FROM_DATABASE=Helios LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0D5* + ID_MODEL_FROM_DATABASE=Helios LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0E1* + ID_MODEL_FROM_DATABASE=Zephyr LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0E5* + ID_MODEL_FROM_DATABASE=Zephyr LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F0F5* + ID_MODEL_FROM_DATABASE=Neptune LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F100* + ID_MODEL_FROM_DATABASE=Saturn-X: LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F100sv0000103Csd00003282* + ID_MODEL_FROM_DATABASE=Saturn-X: LightPulse Fibre Channel Host Adapter (8Gb Dual-port PCI-e FC HBA) + +pci:v000010DFd0000F111* + ID_MODEL_FROM_DATABASE=Saturn-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F112* + ID_MODEL_FROM_DATABASE=Saturn-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000F180* + ID_MODEL_FROM_DATABASE=LPSe12002 EmulexSecure Fibre Channel Adapter + +pci:v000010DFd0000F700* + ID_MODEL_FROM_DATABASE=LP7000 Fibre Channel Host Adapter + +pci:v000010DFd0000F701* + ID_MODEL_FROM_DATABASE=LP7000 Fibre Channel Host Adapter Alternate ID (JX1:2-3, JX2:1-2) + +pci:v000010DFd0000F800* + ID_MODEL_FROM_DATABASE=LP8000 Fibre Channel Host Adapter + +pci:v000010DFd0000F801* + ID_MODEL_FROM_DATABASE=LP8000 Fibre Channel Host Adapter Alternate ID (JX1:2-3, JX2:1-2) + +pci:v000010DFd0000F900* + ID_MODEL_FROM_DATABASE=LP9000 Fibre Channel Host Adapter + +pci:v000010DFd0000F901* + ID_MODEL_FROM_DATABASE=LP9000 Fibre Channel Host Adapter Alternate ID (JX1:2-3, JX2:1-2) + +pci:v000010DFd0000F980* + ID_MODEL_FROM_DATABASE=LP9802 Fibre Channel Host Adapter + +pci:v000010DFd0000F981* + ID_MODEL_FROM_DATABASE=LP9802 Fibre Channel Host Adapter Alternate ID + +pci:v000010DFd0000F982* + ID_MODEL_FROM_DATABASE=LP9802 Fibre Channel Host Adapter Alternate ID + +pci:v000010DFd0000FA00* + ID_MODEL_FROM_DATABASE=Thor-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FB00* + ID_MODEL_FROM_DATABASE=Viper LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FC00* + ID_MODEL_FROM_DATABASE=Thor-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FC00sv000010DFsd0000FC00* + ID_MODEL_FROM_DATABASE=Thor-X LightPulse Fibre Channel Host Adapter (LP10000 LightPulse Fibre Channel Host Adapter) + +pci:v000010DFd0000FC10* + ID_MODEL_FROM_DATABASE=Helios-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FC20* + ID_MODEL_FROM_DATABASE=Zephyr-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FC40* + ID_MODEL_FROM_DATABASE=Saturn-X: LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FC50* + ID_MODEL_FROM_DATABASE=Proteus-X: LightPulse IOV Fibre Channel Host Adapter + +pci:v000010DFd0000FD00* + ID_MODEL_FROM_DATABASE=Helios-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FD11* + ID_MODEL_FROM_DATABASE=Helios-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FD12* + ID_MODEL_FROM_DATABASE=Helios-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FE00* + ID_MODEL_FROM_DATABASE=Zephyr-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FE05* + ID_MODEL_FROM_DATABASE=Zephyr-X: LightPulse FCoE Adapter + +pci:v000010DFd0000FE11* + ID_MODEL_FROM_DATABASE=Zephyr-X LightPulse Fibre Channel Host Adapter + +pci:v000010DFd0000FE12* + ID_MODEL_FROM_DATABASE=Zephyr-X LightPulse FCoE Adapter + +pci:v000010DFd0000FF00* + ID_MODEL_FROM_DATABASE=Neptune LightPulse Fibre Channel Host Adapter + +pci:v000010E0* + ID_VENDOR_FROM_DATABASE=Integrated Micro Solutions Inc. + +pci:v000010E0d00005026* + ID_MODEL_FROM_DATABASE=IMS5026/27/28 + +pci:v000010E0d00005027* + ID_MODEL_FROM_DATABASE=IMS5027 + +pci:v000010E0d00005028* + ID_MODEL_FROM_DATABASE=IMS5028 + +pci:v000010E0d00008849* + ID_MODEL_FROM_DATABASE=IMS8849 + +pci:v000010E0d00008853* + ID_MODEL_FROM_DATABASE=IMS8853 + +pci:v000010E0d00009128* + ID_MODEL_FROM_DATABASE=IMS9128 [Twin turbo 128] + +pci:v000010E1* + ID_VENDOR_FROM_DATABASE=Tekram Technology Co.,Ltd. + +pci:v000010E1d00000391* + ID_MODEL_FROM_DATABASE=TRM-S1040 + +pci:v000010E1d00000391sv000010E1sd00000391* + ID_MODEL_FROM_DATABASE=TRM-S1040 (DC-315U SCSI-3 Host Adapter) + +pci:v000010E1d0000690C* + ID_MODEL_FROM_DATABASE=DC-690c + +pci:v000010E1d0000DC29* + ID_MODEL_FROM_DATABASE=DC-290 + +pci:v000010E2* + ID_VENDOR_FROM_DATABASE=Aptix Corporation + +pci:v000010E3* + ID_VENDOR_FROM_DATABASE=Tundra Semiconductor Corp. + +pci:v000010E3d00000000* + ID_MODEL_FROM_DATABASE=CA91C042 [Universe] + +pci:v000010E3d00000108* + ID_MODEL_FROM_DATABASE=Tsi108 Host Bridge for Single PowerPC + +pci:v000010E3d00000148* + ID_MODEL_FROM_DATABASE=Tsi148 [Tempe] + +pci:v000010E3d00000148sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=Tsi148 [Tempe] (VR11 Single Board Computer) + +pci:v000010E3d00000860* + ID_MODEL_FROM_DATABASE=CA91C860 [QSpan] + +pci:v000010E3d00000862* + ID_MODEL_FROM_DATABASE=CA91C862A [QSpan-II] + +pci:v000010E3d00008260* + ID_MODEL_FROM_DATABASE=CA91L8200B [Dual PCI PowerSpan II] + +pci:v000010E3d00008261* + ID_MODEL_FROM_DATABASE=CA91L8260B [Single PCI PowerSpan II] + +pci:v000010E3d0000A108* + ID_MODEL_FROM_DATABASE=Tsi109 Host Bridge for Dual PowerPC + +pci:v000010E4* + ID_VENDOR_FROM_DATABASE=Tandem Computers + +pci:v000010E4d00008029* + ID_MODEL_FROM_DATABASE=Realtek 8029 Network Card + +pci:v000010E5* + ID_VENDOR_FROM_DATABASE=Micro Industries Corporation + +pci:v000010E6* + ID_VENDOR_FROM_DATABASE=Gainbery Computer Products Inc. + +pci:v000010E7* + ID_VENDOR_FROM_DATABASE=Vadem + +pci:v000010E8* + ID_VENDOR_FROM_DATABASE=Applied Micro Circuits Corp. + +pci:v000010E8d00001072* + ID_MODEL_FROM_DATABASE=INES GPIB-PCI (AMCC5920 based) + +pci:v000010E8d00002011* + ID_MODEL_FROM_DATABASE=Q-Motion Video Capture/Edit board + +pci:v000010E8d00004750* + ID_MODEL_FROM_DATABASE=S5930 [Matchmaker] + +pci:v000010E8d00005920* + ID_MODEL_FROM_DATABASE=S5920 + +pci:v000010E8d00008043* + ID_MODEL_FROM_DATABASE=LANai4.x [Myrinet LANai interface chip] + +pci:v000010E8d00008062* + ID_MODEL_FROM_DATABASE=S5933_PARASTATION + +pci:v000010E8d0000807D* + ID_MODEL_FROM_DATABASE=S5933 [Matchmaker] + +pci:v000010E8d00008088* + ID_MODEL_FROM_DATABASE=Kongsberg Spacetec Format Synchronizer + +pci:v000010E8d00008089* + ID_MODEL_FROM_DATABASE=Kongsberg Spacetec Serial Output Board + +pci:v000010E8d0000809C* + ID_MODEL_FROM_DATABASE=S5933_HEPC3 + +pci:v000010E8d000080B9* + ID_MODEL_FROM_DATABASE=Harmonix Hi-Card P8 (4x active ISDN BRI) + +pci:v000010E8d000080D7* + ID_MODEL_FROM_DATABASE=PCI-9112 + +pci:v000010E8d000080D8* + ID_MODEL_FROM_DATABASE=PCI-7200 + +pci:v000010E8d000080D9* + ID_MODEL_FROM_DATABASE=PCI-9118 + +pci:v000010E8d000080DA* + ID_MODEL_FROM_DATABASE=PCI-9812 + +pci:v000010E8d000080FC* + ID_MODEL_FROM_DATABASE=APCI1500 Signal processing controller (16 dig. inputs + 16 dig. outputs) + +pci:v000010E8d0000811A* + ID_MODEL_FROM_DATABASE=PCI-IEEE1355-DS-DE Interface + +pci:v000010E8d0000814C* + ID_MODEL_FROM_DATABASE=Fastcom ESCC-PCI (Commtech, Inc.) + +pci:v000010E8d00008170* + ID_MODEL_FROM_DATABASE=S5933 [Matchmaker] (Chipset Development Tool) + +pci:v000010E8d000081E6* + ID_MODEL_FROM_DATABASE=Multimedia video controller + +pci:v000010E8d0000828D* + ID_MODEL_FROM_DATABASE=APCI3001 Signal processing controller (up to 16 analog inputs) + +pci:v000010E8d00008291* + ID_MODEL_FROM_DATABASE=Fastcom 232/8-PCI (Commtech, Inc.) + +pci:v000010E8d000082C4* + ID_MODEL_FROM_DATABASE=Fastcom 422/4-PCI (Commtech, Inc.) + +pci:v000010E8d000082C5* + ID_MODEL_FROM_DATABASE=Fastcom 422/2-PCI (Commtech, Inc.) + +pci:v000010E8d000082C6* + ID_MODEL_FROM_DATABASE=Fastcom IG422/1-PCI (Commtech, Inc.) + +pci:v000010E8d000082C7* + ID_MODEL_FROM_DATABASE=Fastcom IG232/2-PCI (Commtech, Inc.) + +pci:v000010E8d000082CA* + ID_MODEL_FROM_DATABASE=Fastcom 232/4-PCI (Commtech, Inc.) + +pci:v000010E8d000082DB* + ID_MODEL_FROM_DATABASE=AJA HDNTV HD SDI Framestore + +pci:v000010E8d000082E2* + ID_MODEL_FROM_DATABASE=Fastcom DIO24H-PCI (Commtech, Inc.) + +pci:v000010E8d00008406* + ID_MODEL_FROM_DATABASE=PCIcanx/PCIcan CAN interface [Kvaser AB] + +pci:v000010E8d00008407* + ID_MODEL_FROM_DATABASE=PCIcan II CAN interface (A1021, PCB-07, PCB-08) [Kvaser AB] + +pci:v000010E8d00008851* + ID_MODEL_FROM_DATABASE=S5933 on Innes Corp FM Radio Capture card + +pci:v000010E8d0000E004* + ID_MODEL_FROM_DATABASE=X-Gene PCIe bridge + +pci:v000010E9* + ID_VENDOR_FROM_DATABASE=Alps Electric Co., Ltd. + +pci:v000010EA* + ID_VENDOR_FROM_DATABASE=Integraphics + +pci:v000010EAd00001680* + ID_MODEL_FROM_DATABASE=IGA-1680 + +pci:v000010EAd00001682* + ID_MODEL_FROM_DATABASE=IGA-1682 + +pci:v000010EAd00001683* + ID_MODEL_FROM_DATABASE=IGA-1683 + +pci:v000010EAd00002000* + ID_MODEL_FROM_DATABASE=CyberPro 2000 + +pci:v000010EAd00002010* + ID_MODEL_FROM_DATABASE=CyberPro 2000A + +pci:v000010EAd00005000* + ID_MODEL_FROM_DATABASE=CyberPro 5000 + +pci:v000010EAd00005050* + ID_MODEL_FROM_DATABASE=CyberPro 5050 + +pci:v000010EAd00005202* + ID_MODEL_FROM_DATABASE=CyberPro 5202 + +pci:v000010EAd00005252* + ID_MODEL_FROM_DATABASE=CyberPro5252 + +pci:v000010EB* + ID_VENDOR_FROM_DATABASE=Artists Graphics + +pci:v000010EBd00000101* + ID_MODEL_FROM_DATABASE=3GA + +pci:v000010EBd00008111* + ID_MODEL_FROM_DATABASE=Twist3 Frame Grabber + +pci:v000010EC* + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Co., Ltd. + +pci:v000010ECd00000139* + ID_MODEL_FROM_DATABASE=RTL-8139/8139C/8139C+ Ethernet Controller + +pci:v000010ECd00005208* + ID_MODEL_FROM_DATABASE=RTS5208 PCI Express Card Reader + +pci:v000010ECd00005209* + ID_MODEL_FROM_DATABASE=RTS5209 PCI Express Card Reader + +pci:v000010ECd00005227* + ID_MODEL_FROM_DATABASE=RTS5227 PCI Express Card Reader + +pci:v000010ECd00005227sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=RTS5227 PCI Express Card Reader (ThinkPad T440p) + +pci:v000010ECd00005227sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=RTS5227 PCI Express Card Reader (ThinkPad X240) + +pci:v000010ECd00005229* + ID_MODEL_FROM_DATABASE=RTS5229 PCI Express Card Reader + +pci:v000010ECd00005229sv00001025sd00000813* + ID_MODEL_FROM_DATABASE=RTS5229 PCI Express Card Reader (Aspire R7-571) + +pci:v000010ECd00005229sv0000103Csd0000194E* + ID_MODEL_FROM_DATABASE=RTS5229 PCI Express Card Reader (ProBook 455 G1 Notebook) + +pci:v000010ECd00005229sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=RTS5229 PCI Express Card Reader (Pavilion 17-e163sg Notebook PC) + +pci:v000010ECd0000522A* + ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader + +pci:v000010ECd00005249* + ID_MODEL_FROM_DATABASE=RTS5249 PCI Express Card Reader + +pci:v000010ECd00005249sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=RTS5249 PCI Express Card Reader (ZBook 15) + +pci:v000010ECd0000524A* + ID_MODEL_FROM_DATABASE=RTS524A PCI Express Card Reader + +pci:v000010ECd00005250* + ID_MODEL_FROM_DATABASE=RTS5250 PCI Express Card Reader + +pci:v000010ECd0000525A* + ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader + +pci:v000010ECd00005286* + ID_MODEL_FROM_DATABASE=RTS5286 PCI Express Card Reader + +pci:v000010ECd00005288* + ID_MODEL_FROM_DATABASE=RTS5288 PCI Express Card Reader + +pci:v000010ECd00005289* + ID_MODEL_FROM_DATABASE=RTL8411 PCI Express Card Reader + +pci:v000010ECd00005289sv00001043sd00001457* + ID_MODEL_FROM_DATABASE=RTL8411 PCI Express Card Reader (K55A Laptop) + +pci:v000010ECd00008029* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) + +pci:v000010ECd00008029sv000010B8sd00002011* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) (EZ-Card (SMC1208)) + +pci:v000010ECd00008029sv000010ECsd00008029* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) + +pci:v000010ECd00008029sv00001113sd00001208* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) (EN1208) + +pci:v000010ECd00008029sv00001186sd00000300* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) (DE-528) + +pci:v000010ECd00008029sv00001259sd00002400* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) (AT-2400) + +pci:v000010ECd00008029sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=RTL-8029(AS) (QEMU Virtual Machine) + +pci:v000010ECd00008129* + ID_MODEL_FROM_DATABASE=RTL-8129 + +pci:v000010ECd00008129sv000010ECsd00008129* + ID_MODEL_FROM_DATABASE=RTL-8129 (RT8129 Fast Ethernet Adapter) + +pci:v000010ECd00008129sv000011ECsd00008129* + ID_MODEL_FROM_DATABASE=RTL-8129 (RTL8111/8168 PCIe Gigabit Ethernet (misconfigured)) + +pci:v000010ECd00008136* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller + +pci:v000010ECd00008136sv0000103Csd00001985* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (Pavilion 17-e163sg Notebook PC) + +pci:v000010ECd00008136sv0000103Csd00002AB1* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (Pavilion p6774) + +pci:v000010ECd00008136sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (Pavilion dv6700) + +pci:v000010ECd00008136sv00001179sd0000FF64* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (RTL8102E PCI-E Fast Ethernet NIC) + +pci:v000010ECd00008136sv000017C0sd00001053* + ID_MODEL_FROM_DATABASE=RTL8101/2/6E PCI Express Fast/Gigabit Ethernet controller (RTL8101e Medion WIM 2210 Notebook PC [MD96850]) + +pci:v000010ECd00008138* + ID_MODEL_FROM_DATABASE=RT8139 (B/C) Cardbus Fast Ethernet Adapter + +pci:v000010ECd00008138sv000010ECsd00008138* + ID_MODEL_FROM_DATABASE=RT8139 (B/C) Cardbus Fast Ethernet Adapter (RT8139 (B/C) Fast Ethernet Adapter) + +pci:v000010ECd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter + +pci:v000010ECd00008139sv00000357sd0000000A* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (TTP-Monitoring Card V2.0) + +pci:v000010ECd00008139sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (TravelMate 290) + +pci:v000010ECd00008139sv00001025sd00008920* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (ALN-325) + +pci:v000010ECd00008139sv00001025sd00008921* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (ALN-325) + +pci:v000010ECd00008139sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (NX9500) + +pci:v000010ECd00008139sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Pavilion t3030.de Desktop PC) + +pci:v000010ECd00008139sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Presario C700) + +pci:v000010ECd00008139sv00001043sd00001045* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (L8400B or L3C/S notebook) + +pci:v000010ECd00008139sv00001043sd00008109* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (P5P800-MX Mainboard) + +pci:v000010ECd00008139sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (MIM2000) + +pci:v000010ECd00008139sv000010BDsd00000320* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (EP-320X-R) + +pci:v000010ECd00008139sv000010ECsd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter + +pci:v000010ECd00008139sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Panasonic CF-Y5 laptop) + +pci:v000010ECd00008139sv00001113sd0000EC01* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (LevelOne FNC-0107TX/FNC-0109TX) + +pci:v000010ECd00008139sv00001186sd00001104* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (DFE-520TX Fast Ethernet PCI Adapter (rev. D1)) + +pci:v000010ECd00008139sv00001186sd00001300* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (DFE-538TX) + +pci:v000010ECd00008139sv00001186sd00001320* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (SN5200) + +pci:v000010ECd00008139sv00001186sd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (DRN-32TX) + +pci:v000010ECd00008139sv000011F6sd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (FN22-3(A) LinxPRO Ethernet Adapter) + +pci:v000010ECd00008139sv00001259sd00002500* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (AT-2500TX) + +pci:v000010ECd00008139sv00001259sd00002503* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (AT-2500TX/ACPI) + +pci:v000010ECd00008139sv00001385sd0000F31D* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (FA311 v2) + +pci:v000010ECd00008139sv00001395sd00002100* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (AMB2100) + +pci:v000010ECd00008139sv00001429sd0000D010* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (ND010/ND012) + +pci:v000010ECd00008139sv00001432sd00009130* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (EN-9130TX) + +pci:v000010ECd00008139sv00001436sd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (RT8139) + +pci:v000010ECd00008139sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (P30/P35 notebook) + +pci:v000010ECd00008139sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (GA-7VM400M/7VT600 Motherboard) + +pci:v000010ECd00008139sv00001462sd00000131* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (MS-1013 Notebook) + +pci:v000010ECd00008139sv00001462sd0000217C* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Aspire L250) + +pci:v000010ECd00008139sv00001462sd0000788C* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (865PE Neo2-V Mainboard) + +pci:v000010ECd00008139sv0000146Csd00001439* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (FE-1439TX) + +pci:v000010ECd00008139sv00001489sd00006001* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (GF100TXRII) + +pci:v000010ECd00008139sv00001489sd00006002* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (GF100TXRA) + +pci:v000010ECd00008139sv0000149Csd0000139A* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (LFE-8139ATX) + +pci:v000010ECd00008139sv0000149Csd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (LFE-8139TX) + +pci:v000010ECd00008139sv000014CBsd00000200* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (LNR-100 Family 10/100 Base-TX Ethernet) + +pci:v000010ECd00008139sv00001565sd00002300* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (P4TSV Onboard LAN (RTL8100B)) + +pci:v000010ECd00008139sv00001631sd00007003* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Onboard RTL8111 on GA-8SIML Rev1.0 Mainboard) + +pci:v000010ECd00008139sv00001695sd00009001* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Onboard RTL8101L 10/100 MBit) + +pci:v000010ECd00008139sv000016ECsd000000FF* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (USR997900A) + +pci:v000010ECd00008139sv00001799sd00005000* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (F5D5000 PCI Card/Desktop Network PCI Card) + +pci:v000010ECd00008139sv00001799sd00005010* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (F5D5010 CardBus Notebook Network Card) + +pci:v000010ECd00008139sv0000187Esd00003303* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (FN312) + +pci:v000010ECd00008139sv00001904sd00008139* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (RTL8139D Fast Ethernet Adapter) + +pci:v000010ECd00008139sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (QEMU Virtual Machine) + +pci:v000010ECd00008139sv00002646sd00000001* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (KNE120TX) + +pci:v000010ECd00008139sv00008E2Esd00007000* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (KF-230TX) + +pci:v000010ECd00008139sv00008E2Esd00007100* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (KF-230TX/2) + +pci:v000010ECd00008139sv0000A0A0sd00000007* + ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (ALN-325C) + +pci:v000010ECd00008167* + ID_MODEL_FROM_DATABASE=RTL-8110SC/8169SC Gigabit Ethernet + +pci:v000010ECd00008167sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=RTL-8110SC/8169SC Gigabit Ethernet (GA-MA69G-S3H Motherboard) + +pci:v000010ECd00008167sv00001462sd0000235C* + ID_MODEL_FROM_DATABASE=RTL-8110SC/8169SC Gigabit Ethernet (P965 Neo MS-7235 mainboard) + +pci:v000010ECd00008167sv00001462sd0000236C* + ID_MODEL_FROM_DATABASE=RTL-8110SC/8169SC Gigabit Ethernet (945P Neo3-F motherboard) + +pci:v000010ECd00008168* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller + +pci:v000010ECd00008168sv00001019sd00008168* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (RTL8111/8168 PCI Express Gigabit Ethernet controller) + +pci:v000010ECd00008168sv00001028sd00000283* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Vostro 220) + +pci:v000010ECd00008168sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Vostro 3350) + +pci:v000010ECd00008168sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Vostro 3750) + +pci:v000010ECd00008168sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Latitude 3570) + +pci:v000010ECd00008168sv0000103Csd00001611* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Pavilion DM1Z-3000) + +pci:v000010ECd00008168sv0000103Csd00001950* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (ProBook 450/455) + +pci:v000010ECd00008168sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Asus IPIBL-LB Motherboard) + +pci:v000010ECd00008168sv00001043sd000016D5* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (U6V/U31J laptop) + +pci:v000010ECd00008168sv00001043sd000081AA* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (P5B) + +pci:v000010ECd00008168sv00001043sd000082C6* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (M3A78 Series Motherboard) + +pci:v000010ECd00008168sv00001043sd000083A3* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (M4A785TD Motherboard) + +pci:v000010ECd00008168sv00001043sd00008432* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (P8P67 and other motherboards) + +pci:v000010ECd00008168sv00001043sd00008505* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (P8 series motherboard) + +pci:v000010ECd00008168sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (D270S/D250S Motherboard) + +pci:v000010ECd00008168sv000010ECsd00008168* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (RTL8111/8168 PCI Express Gigabit Ethernet controller) + +pci:v000010ECd00008168sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Onboard Ethernet) + +pci:v000010ECd00008168sv00001462sd0000238C* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Onboard RTL8111b on MSI P965 Platinum Mainboard) + +pci:v000010ECd00008168sv00001462sd0000368C* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (K9AG Neo2) + +pci:v000010ECd00008168sv00001462sd00004180* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Wind PC MS-7418) + +pci:v000010ECd00008168sv00001462sd00007522* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (X58 Pro-E) + +pci:v000010ECd00008168sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (CC11/CL11) + +pci:v000010ECd00008168sv00001849sd00008168* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard (one of many)) + +pci:v000010ECd00008168sv00007470sd00003468* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (TG-3468 Gigabit PCI Express Network Adapter) + +pci:v000010ECd00008168sv00008086sd0000D615* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Desktop Board D510MO/D525MW) + +pci:v000010ECd00008169* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller + +pci:v000010ECd00008169sv00001025sd00000079* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (Aspire 5024WLMi) + +pci:v000010ECd00008169sv000010BDsd00003202* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (EP-320G-TX1 32-bit PCI Gigabit Ethernet Adapter) + +pci:v000010ECd00008169sv000010ECsd00008169* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (RTL8169/8110 Family PCI Gigabit Ethernet NIC) + +pci:v000010ECd00008169sv00001259sd0000C107* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (CG-LAPCIGT) + +pci:v000010ECd00008169sv00001371sd0000434E* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (ProG-2000L) + +pci:v000010ECd00008169sv00001385sd0000311A* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (GA311) + +pci:v000010ECd00008169sv00001385sd00005200* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (GA511 Gigabit PC Card) + +pci:v000010ECd00008169sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (GA-8I915ME-G Mainboard) + +pci:v000010ECd00008169sv00001462sd0000030C* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (K8N Neo-FSR v2.0 mainboard) + +pci:v000010ECd00008169sv00001462sd0000065C* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (Hetis 865GV-E (MS-7065)) + +pci:v000010ECd00008169sv00001462sd0000702C* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (K8T NEO 2 motherboard) + +pci:v000010ECd00008169sv00001462sd00007094* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (K8T Neo2-F V2.0) + +pci:v000010ECd00008169sv000016ECsd0000011F* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (USR997903) + +pci:v000010ECd00008169sv00001734sd00001091* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (D2030-A1) + +pci:v000010ECd00008169sv0000A0A0sd00000449* + ID_MODEL_FROM_DATABASE=RTL8169 PCI Gigabit Ethernet Controller (AK86-L motherboard) + +pci:v000010ECd00008171* + ID_MODEL_FROM_DATABASE=RTL8191SEvA Wireless LAN Controller + +pci:v000010ECd00008172* + ID_MODEL_FROM_DATABASE=RTL8191SEvB Wireless LAN Controller + +pci:v000010ECd00008173* + ID_MODEL_FROM_DATABASE=RTL8192SE Wireless LAN Controller + +pci:v000010ECd00008174* + ID_MODEL_FROM_DATABASE=RTL8192SE Wireless LAN Controller + +pci:v000010ECd00008176* + ID_MODEL_FROM_DATABASE=RTL8188CE 802.11b/g/n WiFi Adapter + +pci:v000010ECd00008176sv00001A3Bsd00001139* + ID_MODEL_FROM_DATABASE=RTL8188CE 802.11b/g/n WiFi Adapter (AW-NE139H Half-size Mini PCIe Card) + +pci:v000010ECd00008177* + ID_MODEL_FROM_DATABASE=RTL8191CE PCIe Wireless Network Adapter + +pci:v000010ECd00008178* + ID_MODEL_FROM_DATABASE=RTL8192CE PCIe Wireless Network Adapter + +pci:v000010ECd00008179* + ID_MODEL_FROM_DATABASE=RTL8188EE Wireless Network Adapter + +pci:v000010ECd00008179sv0000103Csd0000197D* + ID_MODEL_FROM_DATABASE=RTL8188EE Wireless Network Adapter (RTL8188EE mini-PCIe card) + +pci:v000010ECd00008180* + ID_MODEL_FROM_DATABASE=RTL8180L 802.11b MAC + +pci:v000010ECd00008180sv00001385sd00004700* + ID_MODEL_FROM_DATABASE=RTL8180L 802.11b MAC (MA521 802.11b Wireless PC Card) + +pci:v000010ECd00008180sv00001737sd00000019* + ID_MODEL_FROM_DATABASE=RTL8180L 802.11b MAC (WPC11v4 802.11b Wireless-B Notebook Adapter) + +pci:v000010ECd00008185* + ID_MODEL_FROM_DATABASE=RTL-8185 IEEE 802.11a/b/g Wireless LAN Controller + +pci:v000010ECd0000818B* + ID_MODEL_FROM_DATABASE=RTL8192EE PCIe Wireless Network Adapter + +pci:v000010ECd00008190* + ID_MODEL_FROM_DATABASE=RTL8190 802.11n PCI Wireless Network Adapter + +pci:v000010ECd00008191* + ID_MODEL_FROM_DATABASE=RTL8192CE PCIe Wireless Network Adapter + +pci:v000010ECd00008192* + ID_MODEL_FROM_DATABASE=RTL8192E/RTL8192SE Wireless LAN Controller + +pci:v000010ECd00008193* + ID_MODEL_FROM_DATABASE=RTL8192DE Wireless LAN Controller + +pci:v000010ECd00008197* + ID_MODEL_FROM_DATABASE=SmartLAN56 56K Modem + +pci:v000010ECd00008199* + ID_MODEL_FROM_DATABASE=RTL8187SE Wireless LAN Controller + +pci:v000010ECd00008199sv00001462sd00006894* + ID_MODEL_FROM_DATABASE=RTL8187SE Wireless LAN Controller (MN54G2 / MS-6894 Wireless Mini PCIe Card) + +pci:v000010ECd00008723* + ID_MODEL_FROM_DATABASE=RTL8723AE PCIe Wireless Network Adapter + +pci:v000010ECd00008812* + ID_MODEL_FROM_DATABASE=RTL8812AE 802.11ac PCIe Wireless Network Adapter + +pci:v000010ECd00008813* + ID_MODEL_FROM_DATABASE=RTL8813AE 802.11ac PCIe Wireless Network Adapter + +pci:v000010ECd00008821* + ID_MODEL_FROM_DATABASE=RTL8821AE 802.11ac PCIe Wireless Network Adapter + +pci:v000010ECd0000B723* + ID_MODEL_FROM_DATABASE=RTL8723BE PCIe Wireless Network Adapter + +pci:v000010ED* + ID_VENDOR_FROM_DATABASE=Ascii Corporation + +pci:v000010EDd00007310* + ID_MODEL_FROM_DATABASE=V7310 + +pci:v000010EE* + ID_VENDOR_FROM_DATABASE=Xilinx Corporation + +pci:v000010EEd00000001* + ID_MODEL_FROM_DATABASE=EUROCOM for PCI (ECOMP) + +pci:v000010EEd00000002* + ID_MODEL_FROM_DATABASE=Octal E1/T1 for PCI ETP Card + +pci:v000010EEd00000007* + ID_MODEL_FROM_DATABASE=Default PCIe endpoint ID + +pci:v000010EEd00000205* + ID_MODEL_FROM_DATABASE=Wildcard TE205P + +pci:v000010EEd00000210* + ID_MODEL_FROM_DATABASE=Wildcard TE210P + +pci:v000010EEd00000300* + ID_MODEL_FROM_DATABASE=Spartan 3 Designs (Xilinx IP) + +pci:v000010EEd00000314* + ID_MODEL_FROM_DATABASE=Wildcard TE405P/TE410P (1st Gen) + +pci:v000010EEd00000405* + ID_MODEL_FROM_DATABASE=Wildcard TE405P (2nd Gen) + +pci:v000010EEd00000410* + ID_MODEL_FROM_DATABASE=Wildcard TE410P (2nd Gen) + +pci:v000010EEd00000600* + ID_MODEL_FROM_DATABASE=Xilinx 6 Designs (Xilinx IP) + +pci:v000010EEd00003FC0* + ID_MODEL_FROM_DATABASE=RME Digi96 + +pci:v000010EEd00003FC1* + ID_MODEL_FROM_DATABASE=RME Digi96/8 + +pci:v000010EEd00003FC2* + ID_MODEL_FROM_DATABASE=RME Digi96/8 Pro + +pci:v000010EEd00003FC3* + ID_MODEL_FROM_DATABASE=RME Digi96/8 Pad + +pci:v000010EEd00003FC4* + ID_MODEL_FROM_DATABASE=RME Digi9652 (Hammerfall) + +pci:v000010EEd00003FC5* + ID_MODEL_FROM_DATABASE=RME Hammerfall DSP + +pci:v000010EEd00003FC6* + ID_MODEL_FROM_DATABASE=RME Hammerfall DSP MADI + +pci:v000010EEd00008380* + ID_MODEL_FROM_DATABASE=Ellips ProfiXpress Profibus Master + +pci:v000010EEd00008381* + ID_MODEL_FROM_DATABASE=Ellips Santos Frame Grabber + +pci:v000010EEd0000D154* + ID_MODEL_FROM_DATABASE=Copley Controls CAN card (PCI-CAN-02) + +pci:v000010EEd0000EBF0* + ID_MODEL_FROM_DATABASE=SED Systems Modulator/Demodulator + +pci:v000010EEd0000EBF1* + ID_MODEL_FROM_DATABASE=SED Systems Audio Interface Card + +pci:v000010EEd0000EBF2* + ID_MODEL_FROM_DATABASE=SED Systems Common PCI Interface + +pci:v000010EF* + ID_VENDOR_FROM_DATABASE=Racore Computer Products, Inc. + +pci:v000010EFd00008154* + ID_MODEL_FROM_DATABASE=M815x Token Ring Adapter + +pci:v000010F0* + ID_VENDOR_FROM_DATABASE=Peritek Corporation + +pci:v000010F1* + ID_VENDOR_FROM_DATABASE=Tyan Computer + +pci:v000010F1d00002865* + ID_MODEL_FROM_DATABASE=Tyan Thunder K8E S2865 + +pci:v000010F1d00005300* + ID_MODEL_FROM_DATABASE=Tyan S5380 Mainboard + +pci:v000010F2* + ID_VENDOR_FROM_DATABASE=Achme Computer, Inc. + +pci:v000010F3* + ID_VENDOR_FROM_DATABASE=Alaris, Inc. + +pci:v000010F4* + ID_VENDOR_FROM_DATABASE=S-MOS Systems, Inc. + +pci:v000010F5* + ID_VENDOR_FROM_DATABASE=NKK Corporation + +pci:v000010F5d0000A001* + ID_MODEL_FROM_DATABASE=NDR4000 [NR4600 Bridge] + +pci:v000010F6* + ID_VENDOR_FROM_DATABASE=Creative Electronic Systems SA + +pci:v000010F7* + ID_VENDOR_FROM_DATABASE=Matsushita Electric Industrial Co., Ltd. + +pci:v000010F8* + ID_VENDOR_FROM_DATABASE=Altos India Ltd + +pci:v000010F9* + ID_VENDOR_FROM_DATABASE=PC Direct + +pci:v000010FA* + ID_VENDOR_FROM_DATABASE=Truevision + +pci:v000010FAd0000000C* + ID_MODEL_FROM_DATABASE=TARGA 1000 + +pci:v000010FB* + ID_VENDOR_FROM_DATABASE=Thesys Gesellschaft fuer Mikroelektronik mbH + +pci:v000010FBd0000186F* + ID_MODEL_FROM_DATABASE=TH 6255 + +pci:v000010FC* + ID_VENDOR_FROM_DATABASE=I-O Data Device, Inc. + +pci:v000010FCd00000003* + ID_MODEL_FROM_DATABASE=Cardbus IDE Controller + +pci:v000010FCd00000005* + ID_MODEL_FROM_DATABASE=Cardbus SCSI CBSC II + +pci:v000010FD* + ID_VENDOR_FROM_DATABASE=Soyo Computer, Inc + +pci:v000010FE* + ID_VENDOR_FROM_DATABASE=Fast Multimedia AG + +pci:v000010FF* + ID_VENDOR_FROM_DATABASE=NCube + +pci:v00001100* + ID_VENDOR_FROM_DATABASE=Jazz Multimedia + +pci:v00001101* + ID_VENDOR_FROM_DATABASE=Initio Corporation + +pci:v00001101d00000002* + ID_MODEL_FROM_DATABASE=INI-920 Ultra SCSI Adapter + +pci:v00001101d00001060* + ID_MODEL_FROM_DATABASE=INI-A100U2W + +pci:v00001101d00001622* + ID_MODEL_FROM_DATABASE=INI-1623 PCI SATA-II Controller + +pci:v00001101d00009100* + ID_MODEL_FROM_DATABASE=INI-9100/9100W + +pci:v00001101d00009400* + ID_MODEL_FROM_DATABASE=INI-940 Fast Wide SCSI Adapter + +pci:v00001101d00009401* + ID_MODEL_FROM_DATABASE=INI-935 Fast Wide SCSI Adapter + +pci:v00001101d00009500* + ID_MODEL_FROM_DATABASE=INI-950 SCSI Adapter + +pci:v00001101d00009502* + ID_MODEL_FROM_DATABASE=INI-950P Ultra Wide SCSI Adapter + +pci:v00001102* + ID_VENDOR_FROM_DATABASE=Creative Labs + +pci:v00001102d00000002* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] + +pci:v00001102d00000002sv0000100Asd00001102* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB Live! 5.1 Digital OEM SB0220 EMU10K1-JFF) + +pci:v00001102d00000002sv00001102sd00000020* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4670/4850 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00000021* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4620 SBLive!) + +pci:v00001102d00000002sv00001102sd0000002F* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (M002/M003 Integrated SBLive!) + +pci:v00001102d00000002sv00001102sd0000100A* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0220/0229 SBLive! 5.1 Digital OEM) + +pci:v00001102d00000002sv00001102sd00004001* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (E-mu APS) + +pci:v00001102d00000002sv00001102sd00008022* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4780 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008023* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4790 SoundBlaster PCI512) + +pci:v00001102d00000002sv00001102sd00008024* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4760 SBLive!) + +pci:v00001102d00000002sv00001102sd00008025* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT1140/SB0040 Integrated SBLive!) + +pci:v00001102d00000002sv00001102sd00008026* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4830 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008027* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4832 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008028* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4870 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008029* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4872 SBLive! Value) + +pci:v00001102d00000002sv00001102sd0000802A* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4890 SoundBlaster PCI256) + +pci:v00001102d00000002sv00001102sd0000802B* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4891 SoundBlaster PCI256) + +pci:v00001102d00000002sv00001102sd00008031* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4831 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008032* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4871 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008033* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4893 SoundBlaster PCI256) + +pci:v00001102d00000002sv00001102sd00008035* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT0060 SBLive!) + +pci:v00001102d00000002sv00001102sd00008040* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4760 SBLive!) + +pci:v00001102d00000002sv00001102sd00008050* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4750 SoundBlaster PCI512) + +pci:v00001102d00000002sv00001102sd00008051* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT4850 SBLive! Value) + +pci:v00001102d00000002sv00001102sd00008061* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB060 SBLive! Player 5.1) + +pci:v00001102d00000002sv00001102sd00008062* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0100 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd00008063* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (DXW Integrated SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd00008064* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0100/SB0102 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd00008065* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0220/0222 SBLive! 5.1 Digital) + +pci:v00001102d00000002sv00001102sd00008066* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0228 SBLive! 5.1 Digital) + +pci:v00001102d00000002sv00001102sd00008067* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0220 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd00008068* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (CT0061 SBLive!) + +pci:v00001102d00000002sv00001102sd00008069* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0101 SBLive! 5.1 Value) + +pci:v00001102d00000002sv00001102sd0000806A* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0103 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd0000806B* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0105 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd0000806C* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0221 SBLive! 5.1) + +pci:v00001102d00000002sv00001102sd00008071* + ID_MODEL_FROM_DATABASE=EMU10k1 [Sound Blaster Live! Series] (SB0150 SoundBlaster PCI512) + +pci:v00001102d00000003* + ID_MODEL_FROM_DATABASE=SB AWE64(D) + +pci:v00001102d00000004* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] + +pci:v00001102d00000004sv00001102sd00000040* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0090 Audigy Player) + +pci:v00001102d00000004sv00001102sd00000041* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (CT4820 SBLive!2) + +pci:v00001102d00000004sv00001102sd00000042* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (CT0070 Audigy) + +pci:v00001102d00000004sv00001102sd00000043* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (CT0072 Audigy) + +pci:v00001102d00000004sv00001102sd00000051* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0090 Audigy Player/Platinum (EX)) + +pci:v00001102d00000004sv00001102sd00000052* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0162 Audigy ES) + +pci:v00001102d00000004sv00001102sd00000053* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (CT0090/SB0092 Audigy Player/OEM) + +pci:v00001102d00000004sv00001102sd00000054* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0161 Audigy ES) + +pci:v00001102d00000004sv00001102sd00000055* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0192 Audigy) + +pci:v00001102d00000004sv00001102sd00000056* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0191 Audigy) + +pci:v00001102d00000004sv00001102sd00000057* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0091 Audigy) + +pci:v00001102d00000004sv00001102sd00000058* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0095 Audigy Player/OEM) + +pci:v00001102d00000004sv00001102sd00000059* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0230 Audigy) + +pci:v00001102d00000004sv00001102sd0000005A* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0231 Audigy) + +pci:v00001102d00000004sv00001102sd0000005B* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0232 Audigy) + +pci:v00001102d00000004sv00001102sd0000005C* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0238 Audigy) + +pci:v00001102d00000004sv00001102sd00001002* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0240 Audigy 2 Platinum 6.1) + +pci:v00001102d00000004sv00001102sd00001003* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0350 Audigy 2 / SB0243 Audigy 2 OEM) + +pci:v00001102d00000004sv00001102sd00001004* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0242 Audigy 2) + +pci:v00001102d00000004sv00001102sd00001005* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0280 Audigy 2 Platinum Ex) + +pci:v00001102d00000004sv00001102sd00001006* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0245 Audigy 2 OEM) + +pci:v00001102d00000004sv00001102sd00001007* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0240/SB0244 Audigy 2 Platinum) + +pci:v00001102d00000004sv00001102sd00001008* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0320 Audigy 2) + +pci:v00001102d00000004sv00001102sd00001009* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0249 Audigy 2 OEM) + +pci:v00001102d00000004sv00001102sd0000100A* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0246 Audigy 2) + +pci:v00001102d00000004sv00001102sd00002001* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0360 Audigy 2 ZS Platinum Pro) + +pci:v00001102d00000004sv00001102sd00002002* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0350 Audigy 2 ZS) + +pci:v00001102d00000004sv00001102sd00002003* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0352 Audigy 2 ZS) + +pci:v00001102d00000004sv00001102sd00002004* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0355 Audigy 2 ZS) + +pci:v00001102d00000004sv00001102sd00002005* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0359 Audigy 2 ZS) + +pci:v00001102d00000004sv00001102sd00002006* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB035x Audigy 2 OEM) + +pci:v00001102d00000004sv00001102sd00002007* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (SB0380 Audigy 4 Pro) + +pci:v00001102d00000004sv00001102sd00004001* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (E-MU 1010 [MAEM8810]) + +pci:v00001102d00000004sv00001102sd00004002* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (E-MU 0404) + +pci:v00001102d00000004sv00001102sd00004003* + ID_MODEL_FROM_DATABASE=EMU10k2/CA0100/CA0102/CA10200 [Sound Blaster Audigy Series] (E-MU 1010) + +pci:v00001102d00000005* + ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] + +pci:v00001102d00000005sv00001102sd00000021* + ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] (X-Fi Platinum) + +pci:v00001102d00000005sv00001102sd0000002C* + ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] (X-Fi XtremeGamer FATAL1TY PRO) + +pci:v00001102d00000005sv00001102sd00001003* + ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] (X-Fi XtremeMusic) + +pci:v00001102d00000006* + ID_MODEL_FROM_DATABASE=EMU10k1X [SB Live! Value/OEM Series] + +pci:v00001102d00000007* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] + +pci:v00001102d00000007sv00001102sd00000007* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SBLive! 24bit) + +pci:v00001102d00000007sv00001102sd00001001* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SB0310 Audigy LS) + +pci:v00001102d00000007sv00001102sd00001002* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SB0312 Audigy LS) + +pci:v00001102d00000007sv00001102sd00001006* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SB0410 SBLive! 24-bit) + +pci:v00001102d00000007sv00001102sd0000100A* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SB0570 [SB Audigy SE]) + +pci:v00001102d00000007sv00001102sd00001012* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (SB0790 X-Fi XA) + +pci:v00001102d00000007sv00001102sd00001013* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (Soundblaster X-Fi Xtreme Audio) + +pci:v00001102d00000007sv00001462sd00001009* + ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series] (K8N Diamond) + +pci:v00001102d00000008* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] + +pci:v00001102d00000008sv00001102sd00000008* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (EMU0404 Digital Audio System) + +pci:v00001102d00000008sv00001102sd00001001* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SB0400 Audigy 2 Value) + +pci:v00001102d00000008sv00001102sd00001021* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SB0610 Audigy 4 Value) + +pci:v00001102d00000008sv00001102sd00001022* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SBxxx Audigy 2/4 Value) + +pci:v00001102d00000008sv00001102sd00001023* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SB0612 Audigy 2 LS) + +pci:v00001102d00000008sv00001102sd00001024* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SB1550 Audigy 5/Rx) + +pci:v00001102d00000008sv00001102sd00001101* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SBxxxx Audigy 2 SA) + +pci:v00001102d00000008sv00001102sd00002001* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SB0530 Audigy 2 ZS Notebook) + +pci:v00001102d00000008sv00001102sd00002021* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (SBxxxx Audigy 4 Notebook) + +pci:v00001102d00000008sv00001102sd00004002* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (E-MU 0404) + +pci:v00001102d00000008sv00001102sd00004003* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (E-MU 1010) + +pci:v00001102d00000008sv00001102sd00004004* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (EMU1010 Digital Audio System [MAEM8960]) + +pci:v00001102d00000008sv00001102sd00004005* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (E-MU 0404 [MAEM8984]) + +pci:v00001102d00000008sv00001102sd00004007* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (E-MU 1010 [MAEM8982]) + +pci:v00001102d00000008sv00001102sd00004201* + ID_MODEL_FROM_DATABASE=CA0108/CA10300 [Sound Blaster Audigy Series] (E-MU 0202 [MAEM8950]) + +pci:v00001102d00000009* + ID_MODEL_FROM_DATABASE=CA0110 [Sound Blaster X-Fi Xtreme Audio] + +pci:v00001102d00000009sv00001102sd00000010* + ID_MODEL_FROM_DATABASE=CA0110 [Sound Blaster X-Fi Xtreme Audio] (MB0820 Integrated) + +pci:v00001102d00000009sv00001102sd00000018* + ID_MODEL_FROM_DATABASE=CA0110 [Sound Blaster X-Fi Xtreme Audio] (SB1040 PCI Express) + +pci:v00001102d0000000B* + ID_MODEL_FROM_DATABASE=EMU20k2 [Sound Blaster X-Fi Titanium Series] + +pci:v00001102d0000000Bsv00001102sd00000041* + ID_MODEL_FROM_DATABASE=EMU20k2 [Sound Blaster X-Fi Titanium Series] (SB0880 [SoundBlaster X-Fi Titanium PCI-e]) + +pci:v00001102d0000000Bsv00001102sd00000062* + ID_MODEL_FROM_DATABASE=EMU20k2 [Sound Blaster X-Fi Titanium Series] (SB1270 [SoundBlaster X-Fi Titanium HD]) + +pci:v00001102d00000012* + ID_MODEL_FROM_DATABASE=Sound Core3D [Sound Blaster Recon3D / Z-Series] + +pci:v00001102d00000012sv00001102sd00000010* + ID_MODEL_FROM_DATABASE=Sound Core3D [Sound Blaster Recon3D / Z-Series] (SB1570 SB Audigy Fx) + +pci:v00001102d00004001* + ID_MODEL_FROM_DATABASE=SB Audigy FireWire Port + +pci:v00001102d00004001sv00001102sd00000010* + ID_MODEL_FROM_DATABASE=SB Audigy FireWire Port + +pci:v00001102d00007002* + ID_MODEL_FROM_DATABASE=SB Live! Game Port + +pci:v00001102d00007002sv00001102sd00000020* + ID_MODEL_FROM_DATABASE=SB Live! Game Port (Gameport Joystick) + +pci:v00001102d00007003* + ID_MODEL_FROM_DATABASE=SB Audigy Game Port + +pci:v00001102d00007003sv00001102sd00000040* + ID_MODEL_FROM_DATABASE=SB Audigy Game Port + +pci:v00001102d00007003sv00001102sd00000060* + ID_MODEL_FROM_DATABASE=SB Audigy Game Port (SB Audigy2 MIDI/Game Port) + +pci:v00001102d00007004* + ID_MODEL_FROM_DATABASE=[SB Live! Value] Input device controller + +pci:v00001102d00007005* + ID_MODEL_FROM_DATABASE=SB Audigy LS Game Port + +pci:v00001102d00007005sv00001102sd00001001* + ID_MODEL_FROM_DATABASE=SB Audigy LS Game Port (SB0310 Audigy LS MIDI/Game port) + +pci:v00001102d00007005sv00001102sd00001002* + ID_MODEL_FROM_DATABASE=SB Audigy LS Game Port (SB0312 Audigy LS MIDI/Game port) + +pci:v00001102d00007006* + ID_MODEL_FROM_DATABASE=[SB X-Fi Xtreme Audio] CA0110-IBG PCIe to PCI Bridge + +pci:v00001102d00008938* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 + +pci:v00001102d00008938sv00001033sd000080E5* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (SlimTower-Jim (NEC)) + +pci:v00001102d00008938sv00001071sd00007150* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Mitac 7150) + +pci:v00001102d00008938sv0000110Asd00005938* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Siemens Scenic Mobile 510PIII) + +pci:v00001102d00008938sv000013BDsd0000100C* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Ceres-C (Sharp, Intel BX)) + +pci:v00001102d00008938sv000013BDsd0000100D* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Sharp, Intel Banister) + +pci:v00001102d00008938sv000013BDsd0000100E* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (TwinHead P09S/P09S3 (Sharp)) + +pci:v00001102d00008938sv000013BDsd0000F6F1* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Marlin (Sharp)) + +pci:v00001102d00008938sv000014FFsd00000E70* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (P88TE (TWINHEAD INTERNATIONAL Corp)) + +pci:v00001102d00008938sv000014FFsd0000C401* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (Notebook 9100/9200/2000 (TWINHEAD INTERNATIONAL Corp)) + +pci:v00001102d00008938sv0000156Dsd0000B400* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G400 - Geo (AlphaTop (Taiwan))) + +pci:v00001102d00008938sv0000156Dsd0000B550* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G560 (AlphaTop (Taiwan))) + +pci:v00001102d00008938sv0000156Dsd0000B560* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G560 (AlphaTop (Taiwan))) + +pci:v00001102d00008938sv0000156Dsd0000B700* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G700/U700 (AlphaTop (Taiwan))) + +pci:v00001102d00008938sv0000156Dsd0000B795* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G795 (AlphaTop (Taiwan))) + +pci:v00001102d00008938sv0000156Dsd0000B797* + ID_MODEL_FROM_DATABASE=Ectiva EV1938 (G797 (AlphaTop (Taiwan))) + +pci:v00001103* + ID_VENDOR_FROM_DATABASE=HighPoint Technologies, Inc. + +pci:v00001103d00000003* + ID_MODEL_FROM_DATABASE=HPT343/345/346/363 + +pci:v00001103d00000004* + ID_MODEL_FROM_DATABASE=HPT366/368/370/370A/372/372N + +pci:v00001103d00000004sv00001103sd00000001* + ID_MODEL_FROM_DATABASE=HPT366/368/370/370A/372/372N (HPT370A) + +pci:v00001103d00000004sv00001103sd00000004* + ID_MODEL_FROM_DATABASE=HPT366/368/370/370A/372/372N (HPT366 UDMA66 (r1) / HPT368 UDMA66 (r2) / HPT370 UDMA100 (r3) / HPT370 UDMA100 RAID (r4)) + +pci:v00001103d00000004sv00001103sd00000005* + ID_MODEL_FROM_DATABASE=HPT366/368/370/370A/372/372N (HPT370 UDMA100) + +pci:v00001103d00000004sv00001103sd00000006* + ID_MODEL_FROM_DATABASE=HPT366/368/370/370A/372/372N (HPT302/302N) + +pci:v00001103d00000005* + ID_MODEL_FROM_DATABASE=HPT372A/372N + +pci:v00001103d00000006* + ID_MODEL_FROM_DATABASE=HPT302/302N + +pci:v00001103d00000007* + ID_MODEL_FROM_DATABASE=HPT371/371N + +pci:v00001103d00000008* + ID_MODEL_FROM_DATABASE=HPT374 + +pci:v00001103d00000009* + ID_MODEL_FROM_DATABASE=HPT372N + +pci:v00001103d00000620* + ID_MODEL_FROM_DATABASE=RocketRAID 620 2 Port SATA-III Controller + +pci:v00001103d00000622* + ID_MODEL_FROM_DATABASE=RocketRAID 622 2 Port SATA-III Controller + +pci:v00001103d00000640* + ID_MODEL_FROM_DATABASE=RocketRAID 640 4 Port SATA-III Controller + +pci:v00001103d00000641* + ID_MODEL_FROM_DATABASE=RocketRAID 640L 4 Port SATA-III Controller + +pci:v00001103d00000642* + ID_MODEL_FROM_DATABASE=RocketRAID 642L SATA-III Controller (2 eSATA ports + 2 internal SATA ports) + +pci:v00001103d00000644* + ID_MODEL_FROM_DATABASE=RocketRAID 644 4 Port SATA-III Controller (eSATA) + +pci:v00001103d00000645* + ID_MODEL_FROM_DATABASE=RocketRAID 644L 4 Port SATA-III Controller (eSATA) + +pci:v00001103d00000646* + ID_MODEL_FROM_DATABASE=RocketRAID 644LS SATA-III Controller (4 eSATA devices connected by 1 SAS cable) + +pci:v00001103d00001720* + ID_MODEL_FROM_DATABASE=RocketRAID 1720 (2x SATA II RAID Controller) + +pci:v00001103d00001740* + ID_MODEL_FROM_DATABASE=RocketRAID 1740 + +pci:v00001103d00001742* + ID_MODEL_FROM_DATABASE=RocketRAID 1742 + +pci:v00001103d00002210* + ID_MODEL_FROM_DATABASE=RocketRAID 2210 SATA-II Controller + +pci:v00001103d00002210sv000011ABsd000011AB* + ID_MODEL_FROM_DATABASE=RocketRAID 2210 SATA-II Controller (88SX6042) + +pci:v00001103d00002300* + ID_MODEL_FROM_DATABASE=RocketRAID 230x 4 Port SATA-II Controller + +pci:v00001103d00002310* + ID_MODEL_FROM_DATABASE=RocketRAID 2310 4 Port SATA-II Controller + +pci:v00001103d00002320* + ID_MODEL_FROM_DATABASE=RocketRAID 2320 SATA-II Controller + +pci:v00001103d00002322* + ID_MODEL_FROM_DATABASE=RocketRAID 2322 SATA-II Controller + +pci:v00001103d00002340* + ID_MODEL_FROM_DATABASE=RocketRAID 2340 16 Port SATA-II Controller + +pci:v00001103d00002640* + ID_MODEL_FROM_DATABASE=RocketRAID 2640 SAS/SATA Controller + +pci:v00001103d00002722* + ID_MODEL_FROM_DATABASE=RocketRAID 2722 + +pci:v00001103d00002740* + ID_MODEL_FROM_DATABASE=RocketRAID 2740 + +pci:v00001103d00002744* + ID_MODEL_FROM_DATABASE=RocketRaid 2744 + +pci:v00001103d00002782* + ID_MODEL_FROM_DATABASE=RocketRAID 2782 + +pci:v00001103d00003120* + ID_MODEL_FROM_DATABASE=RocketRAID 3120 + +pci:v00001103d00003220* + ID_MODEL_FROM_DATABASE=RocketRAID 3220 + +pci:v00001103d00003320* + ID_MODEL_FROM_DATABASE=RocketRAID 3320 + +pci:v00001103d00004310* + ID_MODEL_FROM_DATABASE=RocketRaid 4310 + +pci:v00001104* + ID_VENDOR_FROM_DATABASE=RasterOps Corp. + +pci:v00001105* + ID_VENDOR_FROM_DATABASE=Sigma Designs, Inc. + +pci:v00001105d00001105* + ID_MODEL_FROM_DATABASE=REALmagic Xcard MPEG 1/2/3/4 DVD Decoder + +pci:v00001105d00008300* + ID_MODEL_FROM_DATABASE=REALmagic Hollywood Plus DVD Decoder + +pci:v00001105d00008400* + ID_MODEL_FROM_DATABASE=EM840x REALmagic DVD/MPEG-2 Audio/Video Decoder + +pci:v00001105d00008401* + ID_MODEL_FROM_DATABASE=EM8401 REALmagic DVD/MPEG-2 A/V Decoder + +pci:v00001105d00008470* + ID_MODEL_FROM_DATABASE=EM8470 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d00008471* + ID_MODEL_FROM_DATABASE=EM8471 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d00008475* + ID_MODEL_FROM_DATABASE=EM8475 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d00008475sv00001105sd00000001* + ID_MODEL_FROM_DATABASE=EM8475 REALmagic DVD/MPEG-4 A/V Decoder (REALmagic X-Card) + +pci:v00001105d00008476* + ID_MODEL_FROM_DATABASE=EM8476 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d00008476sv0000127Dsd00000000* + ID_MODEL_FROM_DATABASE=EM8476 REALmagic DVD/MPEG-4 A/V Decoder (CineView II) + +pci:v00001105d00008485* + ID_MODEL_FROM_DATABASE=EM8485 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d00008486* + ID_MODEL_FROM_DATABASE=EM8486 REALmagic DVD/MPEG-4 A/V Decoder + +pci:v00001105d0000C621* + ID_MODEL_FROM_DATABASE=EM8621L Digital Media Processor + +pci:v00001105d0000C622* + ID_MODEL_FROM_DATABASE=EM8622L MPEG-4.10 (H.264) and SMPTE 421M (VC-1) A/V Decoder + +pci:v00001106* + ID_VENDOR_FROM_DATABASE=VIA Technologies, Inc. + +pci:v00001106d00000102* + ID_MODEL_FROM_DATABASE=Embedded VIA Ethernet Controller + +pci:v00001106d00000130* + ID_MODEL_FROM_DATABASE=VT6305 1394.A Controller + +pci:v00001106d00000198* + ID_MODEL_FROM_DATABASE=P4X600 Host Bridge + +pci:v00001106d00000204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00000208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00000238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00000258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00000259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 Host Bridge + +pci:v00001106d00000269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00000282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00000282sv00001043sd000080A3* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge (A8V Deluxe) + +pci:v00001106d00000290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00000293* + ID_MODEL_FROM_DATABASE=PM896 Host Bridge + +pci:v00001106d00000296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00000305* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] + +pci:v00001106d00000305sv00001019sd00000987* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] (K7VZA Mainboard) + +pci:v00001106d00000305sv00001043sd00008033* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] (A7V Mainboard) + +pci:v00001106d00000305sv00001043sd0000803E* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] (A7V-E Mainboard) + +pci:v00001106d00000305sv00001043sd00008042* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] (A7V133/A7V133-C Mainboard) + +pci:v00001106d00000305sv0000147Bsd0000A401* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] (KT7/KT7-RAID/KT7A/KT7A-RAID Mainboard) + +pci:v00001106d00000308* + ID_MODEL_FROM_DATABASE=PT880 Ultra/PT894 Host Bridge + +pci:v00001106d00000308sv00001043sd00008199* + ID_MODEL_FROM_DATABASE=PT880 Ultra/PT894 Host Bridge (P4V800D-X Mainboard) + +pci:v00001106d00000308sv00001849sd00000308* + ID_MODEL_FROM_DATABASE=PT880 Ultra/PT894 Host Bridge (Motherboard) + +pci:v00001106d00000314* + ID_MODEL_FROM_DATABASE=CN700/VN800/P4M800CE/Pro Host Bridge + +pci:v00001106d00000324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00000327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00000336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00000340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00000351* + ID_MODEL_FROM_DATABASE=K8T890CF Host Bridge + +pci:v00001106d00000353* + ID_MODEL_FROM_DATABASE=VX800 Host Bridge + +pci:v00001106d00000364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00000364sv00001043sd000081CE* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge (P5VD2-VM mothervoard) + +pci:v00001106d00000391* + ID_MODEL_FROM_DATABASE=VT8371 [KX133] + +pci:v00001106d00000409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Host Bridge: Host Control + +pci:v00001106d00000410* + ID_MODEL_FROM_DATABASE=VX900 Host Bridge: Host Control + +pci:v00001106d00000415* + ID_MODEL_FROM_DATABASE=VT6415 PATA IDE Host Controller + +pci:v00001106d00000415sv00001043sd0000838F* + ID_MODEL_FROM_DATABASE=VT6415 PATA IDE Host Controller (Motherboard) + +pci:v00001106d00000501* + ID_MODEL_FROM_DATABASE=VT8501 [Apollo MVP4] + +pci:v00001106d00000505* + ID_MODEL_FROM_DATABASE=VT82C505 + +pci:v00001106d00000561* + ID_MODEL_FROM_DATABASE=VT82C576MV + +pci:v00001106d00000571* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE + +pci:v00001106d00000571sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (P6VXA Motherboard) + +pci:v00001106d00000571sv00001019sd00000A81* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (L7VTA v1.0 Motherboard (KT400-8235)) + +pci:v00001106d00000571sv00001043sd00008052* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (VT8233A Bus Master ATA100/66/33 IDE) + +pci:v00001106d00000571sv00001043sd0000808C* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (A7V8X / A7V333 motherboard) + +pci:v00001106d00000571sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (A7V8X-X motherboard rev. 1.01) + +pci:v00001106d00000571sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (A7V600/K8V-X/A8V Deluxe motherboard) + +pci:v00001106d00000571sv00001106sd00000571* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (VT82C586/B/VT82C686/A/B/VT8233/A/C/VT8235 PIPC Bus Master IDE) + +pci:v00001106d00000571sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (Magnia Z310) + +pci:v00001106d00000571sv00001297sd0000F641* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (FX41 motherboard) + +pci:v00001106d00000571sv00001458sd00005002* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (GA-7VAX Mainboard) + +pci:v00001106d00000571sv00001462sd00005901* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00000571sv00001462sd00007020* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (K8T NEO 2 motherboard) + +pci:v00001106d00000571sv00001462sd00007094* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (K8T Neo2-F V2.0) + +pci:v00001106d00000571sv00001462sd00007120* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (KT4AV motherboard) + +pci:v00001106d00000571sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (K8MM3-V mainboard) + +pci:v00001106d00000571sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (KV8-MAX3 motherboard) + +pci:v00001106d00000571sv00001849sd00000571* + ID_MODEL_FROM_DATABASE=VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (K7VT series Motherboards) + +pci:v00001106d00000576* + ID_MODEL_FROM_DATABASE=VT82C576 3V [Apollo Master] + +pci:v00001106d00000581* + ID_MODEL_FROM_DATABASE=CX700/VX700 RAID Controller + +pci:v00001106d00000581sv00001106sd00000581* + ID_MODEL_FROM_DATABASE=CX700/VX700 RAID Controller (Wrong IDE ID) + +pci:v00001106d00000585* + ID_MODEL_FROM_DATABASE=VT82C585VP [Apollo VP1/VPX] + +pci:v00001106d00000586* + ID_MODEL_FROM_DATABASE=VT82C586/A/B PCI-to-ISA [Apollo VP] + +pci:v00001106d00000586sv00001106sd00000000* + ID_MODEL_FROM_DATABASE=VT82C586/A/B PCI-to-ISA [Apollo VP] (MVP3 ISA Bridge) + +pci:v00001106d00000591* + ID_MODEL_FROM_DATABASE=VT8237A SATA 2-Port Controller + +pci:v00001106d00000595* + ID_MODEL_FROM_DATABASE=VT82C595 [Apollo VP2] + +pci:v00001106d00000596* + ID_MODEL_FROM_DATABASE=VT82C596 ISA [Mobile South] + +pci:v00001106d00000596sv00001106sd00000000* + ID_MODEL_FROM_DATABASE=VT82C596 ISA [Mobile South] (VT82C596/A/B PCI to ISA Bridge) + +pci:v00001106d00000596sv00001458sd00000596* + ID_MODEL_FROM_DATABASE=VT82C596 ISA [Mobile South] (VT82C596/A/B PCI to ISA Bridge) + +pci:v00001106d00000597* + ID_MODEL_FROM_DATABASE=VT82C597 [Apollo VP3] + +pci:v00001106d00000598* + ID_MODEL_FROM_DATABASE=VT82C598 [Apollo MVP3] + +pci:v00001106d00000601* + ID_MODEL_FROM_DATABASE=VT8601 [Apollo ProMedia] + +pci:v00001106d00000605* + ID_MODEL_FROM_DATABASE=VT8605 [ProSavage PM133] + +pci:v00001106d00000605sv0000103Csd00001254* + ID_MODEL_FROM_DATABASE=VT8605 [ProSavage PM133] (D9840-60001 [Brio BA410 Motherboard]) + +pci:v00001106d00000605sv00001043sd0000802C* + ID_MODEL_FROM_DATABASE=VT8605 [ProSavage PM133] (CUV4X mainboard) + +pci:v00001106d00000680* + ID_MODEL_FROM_DATABASE=VT82C680 [Apollo P6] + +pci:v00001106d00000686* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] + +pci:v00001106d00000686sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (P6VXA Motherboard) + +pci:v00001106d00000686sv0000103Csd00001256* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (D9840-60001 [Brio BA410 Motherboard]) + +pci:v00001106d00000686sv00001043sd0000802C* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (CUV4X mainboard) + +pci:v00001106d00000686sv00001043sd00008033* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (A7V Mainboard) + +pci:v00001106d00000686sv00001043sd0000803E* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (A7V-E Mainboard) + +pci:v00001106d00000686sv00001043sd00008040* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (A7M266 Mainboard) + +pci:v00001106d00000686sv00001043sd00008042* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (A7V133/A7V133-C Mainboard) + +pci:v00001106d00000686sv00001106sd00000000* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (VT82C686/A PCI to ISA Bridge) + +pci:v00001106d00000686sv00001106sd00000686* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (VT82C686/A PCI to ISA Bridge) + +pci:v00001106d00000686sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (Magnia Z310) + +pci:v00001106d00000686sv0000147Bsd0000A702* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super South] (KG7-Lite Mainboard) + +pci:v00001106d00000691* + ID_MODEL_FROM_DATABASE=VT82C693A/694x [Apollo PRO133x] + +pci:v00001106d00000691sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C693A/694x [Apollo PRO133x] (P6VXA Motherboard) + +pci:v00001106d00000691sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=VT82C693A/694x [Apollo PRO133x] (Magnia Z310) + +pci:v00001106d00000691sv00001458sd00000691* + ID_MODEL_FROM_DATABASE=VT82C693A/694x [Apollo PRO133x] (VT82C691 Apollo Pro System Controller) + +pci:v00001106d00000693* + ID_MODEL_FROM_DATABASE=VT82C693 [Apollo Pro Plus] + +pci:v00001106d00000698* + ID_MODEL_FROM_DATABASE=VT82C693A [Apollo Pro133 AGP] + +pci:v00001106d00000709* + ID_MODEL_FROM_DATABASE=VX11 Standard Host Bridge + +pci:v00001106d0000070A* + ID_MODEL_FROM_DATABASE=VX11 PCI Express Root Port + +pci:v00001106d0000070B* + ID_MODEL_FROM_DATABASE=VX11 PCI Express Root Port + +pci:v00001106d0000070C* + ID_MODEL_FROM_DATABASE=VX11 PCI Express Root Port + +pci:v00001106d0000070D* + ID_MODEL_FROM_DATABASE=VX11 PCI Express Root Port + +pci:v00001106d0000070E* + ID_MODEL_FROM_DATABASE=VX11 PCI Express Root Port + +pci:v00001106d00000926* + ID_MODEL_FROM_DATABASE=VT82C926 [Amazon] + +pci:v00001106d00001000* + ID_MODEL_FROM_DATABASE=VT82C570MV + +pci:v00001106d00001106* + ID_MODEL_FROM_DATABASE=VT82C570MV + +pci:v00001106d00001122* + ID_MODEL_FROM_DATABASE=VX800/VX820 Chrome 9 HC3 Integrated Graphics + +pci:v00001106d00001204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00001208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00001238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00001258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00001259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 Host Bridge + +pci:v00001106d00001269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00001282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00001290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00001293* + ID_MODEL_FROM_DATABASE=PM896 Host Bridge + +pci:v00001106d00001296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00001308* + ID_MODEL_FROM_DATABASE=PT894 Host Bridge + +pci:v00001106d00001314* + ID_MODEL_FROM_DATABASE=CN700/VN800/P4M800CE/Pro Host Bridge + +pci:v00001106d00001324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00001327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00001336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00001340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00001351* + ID_MODEL_FROM_DATABASE=VT3351 Host Bridge + +pci:v00001106d00001353* + ID_MODEL_FROM_DATABASE=VX800/VX820 Error Reporting + +pci:v00001106d00001364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00001409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Error Reporting + +pci:v00001106d00001410* + ID_MODEL_FROM_DATABASE=VX900 Error Reporting + +pci:v00001106d00001571* + ID_MODEL_FROM_DATABASE=VT82C576M/VT82C586 + +pci:v00001106d00001595* + ID_MODEL_FROM_DATABASE=VT82C595/97 [Apollo VP2/97] + +pci:v00001106d00001732* + ID_MODEL_FROM_DATABASE=VT1732 [Envy24 II] PCI Multi-Channel Audio Controller + +pci:v00001106d00002106* + ID_MODEL_FROM_DATABASE=VIA Rhine Family Fast Ethernet Adapter (VT6105) + +pci:v00001106d00002204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00002208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00002238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00002258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00002259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 CPU Host Bridge + +pci:v00001106d00002269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00002282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00002290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00002293* + ID_MODEL_FROM_DATABASE=PM896 Host Bridge + +pci:v00001106d00002296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00002308* + ID_MODEL_FROM_DATABASE=PT894 Host Bridge + +pci:v00001106d00002314* + ID_MODEL_FROM_DATABASE=CN700/VN800/P4M800CE/Pro Host Bridge + +pci:v00001106d00002324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00002327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00002336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00002340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00002351* + ID_MODEL_FROM_DATABASE=VT3351 Host Bridge + +pci:v00001106d00002353* + ID_MODEL_FROM_DATABASE=VX800/VX820 Host Bus Control + +pci:v00001106d00002364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00002409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Host Bus Control + +pci:v00001106d00002410* + ID_MODEL_FROM_DATABASE=VX900 CPU Bus Controller + +pci:v00001106d0000287A* + ID_MODEL_FROM_DATABASE=VT8251 PCI to PCI Bridge + +pci:v00001106d0000287B* + ID_MODEL_FROM_DATABASE=VT8251 Host Bridge + +pci:v00001106d0000287C* + ID_MODEL_FROM_DATABASE=VT8251 PCIE Root Port + +pci:v00001106d0000287D* + ID_MODEL_FROM_DATABASE=VT8251 PCIE Root Port + +pci:v00001106d0000287E* + ID_MODEL_FROM_DATABASE=VT8237/8251 Ultra VLINK Controller + +pci:v00001106d00003022* + ID_MODEL_FROM_DATABASE=CLE266 + +pci:v00001106d00003038* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller + +pci:v00001106d00003038sv00000925sd00001234* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (onboard UHCI USB 1.1 Controller) + +pci:v00001106d00003038sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (P6VXA Motherboard) + +pci:v00001106d00003038sv00001019sd00000A81* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (L7VTA v1.0 Motherboard (KT400-8235)) + +pci:v00001106d00003038sv00001043sd00008080* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (A7V333 motherboard) + +pci:v00001106d00003038sv00001043sd0000808C* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (VT6202 USB2.0 4 port controller) + +pci:v00001106d00003038sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (A7V8X-X motherboard) + +pci:v00001106d00003038sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (A7V600/K8V-X/A8V Deluxe motherboard) + +pci:v00001106d00003038sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (Magnia Z310) + +pci:v00001106d00003038sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (GA-7VAX Mainboard) + +pci:v00001106d00003038sv00001462sd00005901* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00003038sv00001462sd00007020* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (K8T NEO 2 motherboard) + +pci:v00001106d00003038sv00001462sd00007094* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (K8T Neo2-F V2.0) + +pci:v00001106d00003038sv00001462sd00007120* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (KT4AV motherboard) + +pci:v00001106d00003038sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (K8MM3-V mainboard) + +pci:v00001106d00003038sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (KV8-MAX3 motherboard) + +pci:v00001106d00003038sv0000182Dsd0000201D* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (CN-029 USB2.0 4 port PCI Card) + +pci:v00001106d00003038sv00001849sd00003038* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (K7VT series Motherboards) + +pci:v00001106d00003038sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (ZBOX nano VD01) + +pci:v00001106d00003038sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=VT82xx/62xx UHCI USB 1.1 Controller (QEMU Virtual Machine) + +pci:v00001106d00003040* + ID_MODEL_FROM_DATABASE=VT82C586B ACPI + +pci:v00001106d00003043* + ID_MODEL_FROM_DATABASE=VT86C100A [Rhine] + +pci:v00001106d00003043sv000010BDsd00000000* + ID_MODEL_FROM_DATABASE=VT86C100A [Rhine] (VT86C100A Fast Ethernet Adapter) + +pci:v00001106d00003043sv00001106sd00000100* + ID_MODEL_FROM_DATABASE=VT86C100A [Rhine] (VT86C100A Fast Ethernet Adapter) + +pci:v00001106d00003043sv00001186sd00001400* + ID_MODEL_FROM_DATABASE=VT86C100A [Rhine] (DFE-530TX PCI Fast Ethernet Adapter (rev. A)) + +pci:v00001106d00003044* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller + +pci:v00001106d00003044sv00000010sd00000001* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (IEEE 1394 4port DCST 1394-3+1B) + +pci:v00001106d00003044sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (TravelMate 290) + +pci:v00001106d00003044sv0000103Csd00002A20* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (Pavilion t3030.de Desktop PC) + +pci:v00001106d00003044sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (Media Center PC m7590n) + +pci:v00001106d00003044sv00001043sd0000808A* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (A8V/A8N/P4P800/P5SD2 series motherboard) + +pci:v00001106d00003044sv00001043sd000081FE* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (Motherboard) + +pci:v00001106d00003044sv00001458sd00001000* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (GA-7VT600-1394 Motherboard) + +pci:v00001106d00003044sv00001462sd0000207D* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (K8NGM2 series motherboard) + +pci:v00001106d00003044sv00001462sd0000217D* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (Aspire L250) + +pci:v00001106d00003044sv00001462sd0000590D* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00003044sv00001462sd0000702D* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (K8T NEO 2 motherboard) + +pci:v00001106d00003044sv00001462sd0000971D* + ID_MODEL_FROM_DATABASE=VT6306/7/8 [Fire II(M)] IEEE 1394 OHCI Controller (MS-6917) + +pci:v00001106d00003050* + ID_MODEL_FROM_DATABASE=VT82C596 Power Management + +pci:v00001106d00003051* + ID_MODEL_FROM_DATABASE=VT82C596 Power Management + +pci:v00001106d00003053* + ID_MODEL_FROM_DATABASE=VT6105M [Rhine-III] + +pci:v00001106d00003053sv00001186sd00001404* + ID_MODEL_FROM_DATABASE=VT6105M [Rhine-III] (DFE-530TX PCI Fast Ethernet Adapter (rev. D)) + +pci:v00001106d00003057* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] + +pci:v00001106d00003057sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (P6VXA Motherboard) + +pci:v00001106d00003057sv00001019sd00000987* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (K7VZA Motherboard) + +pci:v00001106d00003057sv00001043sd00008033* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (A7V Mainboard) + +pci:v00001106d00003057sv00001043sd0000803E* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (A7V-E Mainboard) + +pci:v00001106d00003057sv00001043sd00008040* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (A7M266 Mainboard) + +pci:v00001106d00003057sv00001043sd00008042* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (A7V133/A7V133-C Mainboard) + +pci:v00001106d00003057sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=VT82C686 [Apollo Super ACPI] (Magnia Z310) + +pci:v00001106d00003058* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller + +pci:v00001106d00003058sv00000E11sd00000097* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (SoundMax Digital Integrated Audio) + +pci:v00001106d00003058sv00000E11sd0000B194* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (Soundmax integrated digital audio) + +pci:v00001106d00003058sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (P6VXA Motherboard) + +pci:v00001106d00003058sv00001019sd00000987* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (K7VZA Motherboard) + +pci:v00001106d00003058sv0000103Csd00001251* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (D9840-60001 [Brio BA410 Motherboard]) + +pci:v00001106d00003058sv00001043sd00001106* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (A7V133/A7V133-C Mainboard) + +pci:v00001106d00003058sv00001106sd00004511* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (Onboard Audio on EP7KXA) + +pci:v00001106d00003058sv00001106sd0000AA03* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (VT1612A AC'97 Audio Controller) + +pci:v00001106d00003058sv000011D4sd00005348* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (AD1881A audio) + +pci:v00001106d00003058sv00001458sd00007600* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (Onboard Audio) + +pci:v00001106d00003058sv00001462sd00003091* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (MS-6309 Onboard Audio) + +pci:v00001106d00003058sv00001462sd00003092* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (MS-6309 v2.x Mainboard (VIA VT1611A codec)) + +pci:v00001106d00003058sv00001462sd00003300* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (MS-6330 Onboard Audio) + +pci:v00001106d00003058sv00001462sd00003400* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (MS-6340 (VT8363) motherboard) + +pci:v00001106d00003058sv000015DDsd00007609* + ID_MODEL_FROM_DATABASE=VT82C686 AC97 Audio Controller (Onboard Audio) + +pci:v00001106d00003059* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller + +pci:v00001106d00003059sv00001019sd00000A81* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (L7VTA v1.0 Motherboard (KT400-8235)) + +pci:v00001106d00003059sv00001019sd00001841* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (M811 (VT8367/VT8235/VT6103) [KT333] motherboard) + +pci:v00001106d00003059sv00001019sd00001877* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (K8M800-M2 (V2.0) onboard audio) + +pci:v00001106d00003059sv00001043sd00008095* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (A7V8X Motherboard (Realtek ALC650 codec)) + +pci:v00001106d00003059sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (A7V8X-X Motherboard) + +pci:v00001106d00003059sv00001043sd000080B0* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (A7V600/K8V-X/K8V Deluxe motherboard (ADI AD1980 codec [SoundMAX])) + +pci:v00001106d00003059sv00001043sd000080F3* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (ASUSTek SK8V motherboard) + +pci:v00001106d00003059sv00001043sd0000810D* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (Asus P5VD1-X (AD1888 codec [SoundMax])) + +pci:v00001106d00003059sv00001043sd0000812A* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (A8V Deluxe motherboard (Realtek ALC850 codec)) + +pci:v00001106d00003059sv000010ECsd00008168* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (High Definition Audio) + +pci:v00001106d00003059sv00001106sd00003059* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (L7VMM2 Motherboard) + +pci:v00001106d00003059sv00001106sd00004161* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (K7VT2 motherboard) + +pci:v00001106d00003059sv00001106sd00004170* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (PCPartner P4M800-8237R Motherboard) + +pci:v00001106d00003059sv00001106sd00004552* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (Soyo KT-600 Dragon Plus (Realtek ALC 650)) + +pci:v00001106d00003059sv00001297sd0000C160* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (FX41 motherboard (Realtek ALC650 codec)) + +pci:v00001106d00003059sv00001413sd0000147B* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (KV8 Pro motherboard onboard audio) + +pci:v00001106d00003059sv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (GA-7VAX Onboard Audio (Realtek ALC650)) + +pci:v00001106d00003059sv00001462sd00000080* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (K8T NEO 2 motherboard) + +pci:v00001106d00003059sv00001462sd00003800* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (KT266 onboard audio) + +pci:v00001106d00003059sv00001462sd00005901* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00003059sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (K8MM3-V mainboard) + +pci:v00001106d00003059sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (KV8-MAX3 motherboard) + +pci:v00001106d00003059sv00001695sd0000300C* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (Realtek ALC655 audio on EP-8KRA series mainboard) + +pci:v00001106d00003059sv00001849sd00000850* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (ASRock 775Dual-880 Pro onboard audio (Realtek ALC850)) + +pci:v00001106d00003059sv00001849sd00009739* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (P4VT8 Mainboard (C-Media CMI9739A codec)) + +pci:v00001106d00003059sv00001849sd00009761* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (K7VT series Motherboards) + +pci:v00001106d00003059sv00004005sd00004710* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (MSI K7T266 Pro2-RU (MSI-6380 v2) onboard audio (Realtek/ALC 200/200P)) + +pci:v00001106d00003059sv0000A0A0sd000001B6* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (AK77-8XN onboard audio) + +pci:v00001106d00003059sv0000A0A0sd00000342* + ID_MODEL_FROM_DATABASE=VT8233/A/8235/8237 AC97 Audio Controller (AK86-L motherboard) + +pci:v00001106d00003065* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] + +pci:v00001106d00003065sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (A7V8X-X Motherboard) + +pci:v00001106d00003065sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (A7V600-X Motherboard) + +pci:v00001106d00003065sv00001106sd00000102* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (VT6102/6103 [Rhine II] Ethernet Controller) + +pci:v00001106d00003065sv00001186sd00001400* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (DFE-530TX PCI Fast Ethernet Adapter (rev. A)) + +pci:v00001106d00003065sv00001186sd00001401* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (DFE-530TX PCI Fast Ethernet Adapter (rev. B)) + +pci:v00001106d00003065sv00001186sd00001402* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (DFE-530TX PCI Fast Ethernet Adapter (rev. B)) + +pci:v00001106d00003065sv000013B9sd00001421* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (LD-10/100AL PCI Fast Ethernet Adapter (rev.B)) + +pci:v00001106d00003065sv00001462sd00007061* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (MS-7061) + +pci:v00001106d00003065sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (K8MM3-V mainboard) + +pci:v00001106d00003065sv0000147Bsd00001C09* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (NV7 Motherboard) + +pci:v00001106d00003065sv00001695sd00003005* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (VT6103) + +pci:v00001106d00003065sv00001849sd00003065* + ID_MODEL_FROM_DATABASE=VT6102/VT6103 [Rhine-II] (K7VT series Motherboards) + +pci:v00001106d00003068* + ID_MODEL_FROM_DATABASE=AC'97 Modem Controller + +pci:v00001106d00003068sv00001462sd0000309E* + ID_MODEL_FROM_DATABASE=AC'97 Modem Controller (MS-6309 Saturn Motherboard) + +pci:v00001106d00003074* + ID_MODEL_FROM_DATABASE=VT8233 PCI to ISA Bridge + +pci:v00001106d00003074sv00001043sd00008052* + ID_MODEL_FROM_DATABASE=VT8233 PCI to ISA Bridge (VT8233A) + +pci:v00001106d00003091* + ID_MODEL_FROM_DATABASE=VT8633 [Apollo Pro266] + +pci:v00001106d00003099* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333] + +pci:v00001106d00003099sv00001019sd00001841* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333] (M811 (VT8367/VT8235/VT6103) [KT333] motherboard) + +pci:v00001106d00003099sv00001043sd00008064* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333] (A7V266-E Mainboard) + +pci:v00001106d00003099sv00001043sd0000807F* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333] (A7V333 Mainboard) + +pci:v00001106d00003099sv00001849sd00003099* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333] (K7VT2 motherboard) + +pci:v00001106d00003101* + ID_MODEL_FROM_DATABASE=VT8653 Host Bridge + +pci:v00001106d00003102* + ID_MODEL_FROM_DATABASE=VT8662 Host Bridge + +pci:v00001106d00003103* + ID_MODEL_FROM_DATABASE=VT8615 Host Bridge + +pci:v00001106d00003104* + ID_MODEL_FROM_DATABASE=USB 2.0 + +pci:v00001106d00003104sv00000925sd00001234* + ID_MODEL_FROM_DATABASE=USB 2.0 (onboard EHCI USB 2.0 Controller) + +pci:v00001106d00003104sv00001019sd00000A81* + ID_MODEL_FROM_DATABASE=USB 2.0 (L7VTA v1.0 Motherboard (KT400-8235)) + +pci:v00001106d00003104sv00001043sd0000808C* + ID_MODEL_FROM_DATABASE=USB 2.0 (A7V8X motherboard) + +pci:v00001106d00003104sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=USB 2.0 (A7V8X-X motherboard rev 1.01) + +pci:v00001106d00003104sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=USB 2.0 (A7V600/K8V-X/A8V Deluxe motherboard) + +pci:v00001106d00003104sv00001106sd00003104* + ID_MODEL_FROM_DATABASE=USB 2.0 (Controller) + +pci:v00001106d00003104sv00001297sd0000F641* + ID_MODEL_FROM_DATABASE=USB 2.0 (FX41 motherboard) + +pci:v00001106d00003104sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=USB 2.0 (GA-7VAX Mainboard) + +pci:v00001106d00003104sv00001462sd00005901* + ID_MODEL_FROM_DATABASE=USB 2.0 (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00003104sv00001462sd00007020* + ID_MODEL_FROM_DATABASE=USB 2.0 (K8T NEO 2 motherboard) + +pci:v00001106d00003104sv00001462sd00007094* + ID_MODEL_FROM_DATABASE=USB 2.0 (K8T Neo2-F V2.0) + +pci:v00001106d00003104sv00001462sd00007120* + ID_MODEL_FROM_DATABASE=USB 2.0 (KT4AV motherboard) + +pci:v00001106d00003104sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=USB 2.0 (K8MM3-V mainboard) + +pci:v00001106d00003104sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=USB 2.0 (KV8-MAX3 motherboard) + +pci:v00001106d00003104sv0000182Dsd0000201D* + ID_MODEL_FROM_DATABASE=USB 2.0 (CN-029 USB 2.0 4 port PCI Card) + +pci:v00001106d00003104sv00001849sd00003104* + ID_MODEL_FROM_DATABASE=USB 2.0 (K7VT series Motherboards) + +pci:v00001106d00003104sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=USB 2.0 (ZBOX nano VD01) + +pci:v00001106d00003106* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] + +pci:v00001106d00003106sv00001106sd00000105* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] (VT6106S [Rhine-III]) + +pci:v00001106d00003106sv00001186sd00001403* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] (DFE-530TX PCI Fast Ethernet Adapter (rev. C)) + +pci:v00001106d00003106sv00001186sd00001405* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] (DFE-520TX Fast Ethernet PCI Adapter) + +pci:v00001106d00003106sv00001186sd00001406* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] (DFE-530TX+ rev F2) + +pci:v00001106d00003106sv00001186sd00001407* + ID_MODEL_FROM_DATABASE=VT6105/VT6106S [Rhine-III] (DFE-538TX) + +pci:v00001106d00003108* + ID_MODEL_FROM_DATABASE=K8M800/K8N800/K8N800A [S3 UniChrome Pro] + +pci:v00001106d00003109* + ID_MODEL_FROM_DATABASE=VT8233C PCI to ISA Bridge + +pci:v00001106d00003112* + ID_MODEL_FROM_DATABASE=VT8361 [KLE133] Host Bridge + +pci:v00001106d00003113* + ID_MODEL_FROM_DATABASE=VPX/VPX2 PCI to PCI Bridge Controller + +pci:v00001106d00003116* + ID_MODEL_FROM_DATABASE=VT8375 [KM266/KL266] Host Bridge + +pci:v00001106d00003116sv00001297sd0000F641* + ID_MODEL_FROM_DATABASE=VT8375 [KM266/KL266] Host Bridge (FX41 motherboard) + +pci:v00001106d00003118* + ID_MODEL_FROM_DATABASE=CN400/PM800/PM880/PN800/PN880 [S3 UniChrome Pro] + +pci:v00001106d00003119* + ID_MODEL_FROM_DATABASE=VT6120/VT6121/VT6122 Gigabit Ethernet Adapter + +pci:v00001106d00003122* + ID_MODEL_FROM_DATABASE=VT8623 [Apollo CLE266] integrated CastleRock graphics + +pci:v00001106d00003123* + ID_MODEL_FROM_DATABASE=VT8623 [Apollo CLE266] + +pci:v00001106d00003128* + ID_MODEL_FROM_DATABASE=VT8753 [P4X266 AGP] + +pci:v00001106d00003133* + ID_MODEL_FROM_DATABASE=VT3133 Host Bridge + +pci:v00001106d00003142* + ID_MODEL_FROM_DATABASE=VT6651 WiFi Adapter, 802.11b + +pci:v00001106d00003147* + ID_MODEL_FROM_DATABASE=VT8233A ISA Bridge + +pci:v00001106d00003147sv00001043sd0000808C* + ID_MODEL_FROM_DATABASE=VT8233A ISA Bridge (A7V333 motherboard) + +pci:v00001106d00003148* + ID_MODEL_FROM_DATABASE=P4M266 Host Bridge + +pci:v00001106d00003149* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller + +pci:v00001106d00003149sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (A7V600/K8V Deluxe/K8V-X/A8V Deluxe motherboard) + +pci:v00001106d00003149sv00001458sd0000B003* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (GA-7VM400AM(F) Motherboard) + +pci:v00001106d00003149sv00001462sd00005901* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (KT6 Delta-FIS2R (MS-6590)) + +pci:v00001106d00003149sv00001462sd00007020* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (K8T Neo 2 Motherboard) + +pci:v00001106d00003149sv00001462sd00007094* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (K8T Neo2-F V2.0) + +pci:v00001106d00003149sv00001462sd00007181* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (K8MM3-V mainboard) + +pci:v00001106d00003149sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (KV8-MAX3 motherboard) + +pci:v00001106d00003149sv0000147Bsd00001408* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (KV7) + +pci:v00001106d00003149sv00001849sd00003149* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (K7VT6 motherboard) + +pci:v00001106d00003149sv0000A0A0sd000004AD* + ID_MODEL_FROM_DATABASE=VIA VT6420 SATA RAID Controller (AK86-L motherboard) + +pci:v00001106d00003156* + ID_MODEL_FROM_DATABASE=P/KN266 Host Bridge + +pci:v00001106d00003157* + ID_MODEL_FROM_DATABASE=CX700/VX700 [S3 UniChrome Pro] + +pci:v00001106d00003164* + ID_MODEL_FROM_DATABASE=VT6410 ATA133 RAID controller + +pci:v00001106d00003164sv00001043sd000080F4* + ID_MODEL_FROM_DATABASE=VT6410 ATA133 RAID controller (P4P800 Mainboard Deluxe ATX) + +pci:v00001106d00003164sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=VT6410 ATA133 RAID controller (915P/G Neo2) + +pci:v00001106d00003168* + ID_MODEL_FROM_DATABASE=P4X333/P4X400/PT800 AGP Bridge + +pci:v00001106d00003168sv00001849sd00003168* + ID_MODEL_FROM_DATABASE=P4X333/P4X400/PT800 AGP Bridge (P4VT8 Mainboard) + +pci:v00001106d00003177* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge + +pci:v00001106d00003177sv00001019sd00000A81* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (L7VTA v1.0 Motherboard (KT400-8235)) + +pci:v00001106d00003177sv00001019sd00001841* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (M811 (VT8367/VT8235/VT6103) [KT333] motherboard) + +pci:v00001106d00003177sv00001043sd0000808C* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (A7V8X motherboard) + +pci:v00001106d00003177sv00001043sd000080A1* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (A7V8X-X motherboard) + +pci:v00001106d00003177sv00001106sd00000000* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (KT4AV motherboard) + +pci:v00001106d00003177sv00001297sd0000F641* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (FX41 motherboard) + +pci:v00001106d00003177sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (GA-7VAX Mainboard) + +pci:v00001106d00003177sv00001849sd00003177* + ID_MODEL_FROM_DATABASE=VT8235 ISA Bridge (K7VT series Motherboards) + +pci:v00001106d00003178* + ID_MODEL_FROM_DATABASE=ProSavageDDR P4N333 Host Bridge + +pci:v00001106d00003188* + ID_MODEL_FROM_DATABASE=VT8385 [K8T800 AGP] Host Bridge + +pci:v00001106d00003188sv00001043sd000080A3* + ID_MODEL_FROM_DATABASE=VT8385 [K8T800 AGP] Host Bridge (K8V Deluxe/K8V-X motherboard) + +pci:v00001106d00003188sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT8385 [K8T800 AGP] Host Bridge (KV8-MAX3 motherboard) + +pci:v00001106d00003189* + ID_MODEL_FROM_DATABASE=VT8377 [KT400/KT600 AGP] Host Bridge + +pci:v00001106d00003189sv00001043sd0000807F* + ID_MODEL_FROM_DATABASE=VT8377 [KT400/KT600 AGP] Host Bridge (A7V8X motherboard) + +pci:v00001106d00003189sv00001106sd00000000* + ID_MODEL_FROM_DATABASE=VT8377 [KT400/KT600 AGP] Host Bridge (KT4AV motherboard (KT400A)) + +pci:v00001106d00003189sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=VT8377 [KT400/KT600 AGP] Host Bridge (GA-7VAX Mainboard) + +pci:v00001106d00003189sv00001849sd00003189* + ID_MODEL_FROM_DATABASE=VT8377 [KT400/KT600 AGP] Host Bridge (K7VT series Motherboards) + +pci:v00001106d000031B0* + ID_MODEL_FROM_DATABASE=VX11 Standard Host Bridge + +pci:v00001106d000031B1* + ID_MODEL_FROM_DATABASE=VX11 Standard Host Bridge + +pci:v00001106d000031B2* + ID_MODEL_FROM_DATABASE=VX11 DRAM Controller + +pci:v00001106d000031B3* + ID_MODEL_FROM_DATABASE=VX11 Power Management Controller + +pci:v00001106d000031B4* + ID_MODEL_FROM_DATABASE=VX11 I/O APIC + +pci:v00001106d000031B5* + ID_MODEL_FROM_DATABASE=VX11 Scratch Device + +pci:v00001106d000031B7* + ID_MODEL_FROM_DATABASE=VX11 Standard Host Bridge + +pci:v00001106d000031B8* + ID_MODEL_FROM_DATABASE=VX11 PCI to PCI Bridge + +pci:v00001106d00003204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00003205* + ID_MODEL_FROM_DATABASE=VT8378 [KM400/A] Chipset Host Bridge + +pci:v00001106d00003205sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=VT8378 [KM400/A] Chipset Host Bridge (GA-7VM400M Motherboard) + +pci:v00001106d00003208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00003213* + ID_MODEL_FROM_DATABASE=VPX/VPX2 PCI to PCI Bridge Controller + +pci:v00001106d00003218* + ID_MODEL_FROM_DATABASE=K8T800M Host Bridge + +pci:v00001106d00003227* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] + +pci:v00001106d00003227sv00001043sd000080ED* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] (A7V600/K8V-X/A8V Deluxe motherboard) + +pci:v00001106d00003227sv00001106sd00003227* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] (DFI KT600-AL / Soltek SL-B9D-FGR Motherboard) + +pci:v00001106d00003227sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] (GA-7VT600 Motherboard) + +pci:v00001106d00003227sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] (KV8-MAX3 motherboard) + +pci:v00001106d00003227sv00001849sd00003227* + ID_MODEL_FROM_DATABASE=VT8237 ISA bridge [KT600/K8T800/K8T890 South] (K7VT4 motherboard) + +pci:v00001106d00003230* + ID_MODEL_FROM_DATABASE=K8M890CE/K8N890CE [Chrome 9] + +pci:v00001106d00003238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00003249* + ID_MODEL_FROM_DATABASE=VT6421 IDE/SATA Controller + +pci:v00001106d00003249sv00001106sd00003249* + ID_MODEL_FROM_DATABASE=VT6421 IDE/SATA Controller + +pci:v00001106d0000324A* + ID_MODEL_FROM_DATABASE=CX700/VX700 PCI to PCI Bridge + +pci:v00001106d0000324B* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d0000324E* + ID_MODEL_FROM_DATABASE=CX700/VX700 Internal Module Bus + +pci:v00001106d00003253* + ID_MODEL_FROM_DATABASE=VT6655 WiFi Adapter, 802.11a/b/g + +pci:v00001106d00003258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00003259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 Host Bridge + +pci:v00001106d00003260* + ID_MODEL_FROM_DATABASE=VIA Chrome9 HC IGP + +pci:v00001106d00003269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00003282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00003287* + ID_MODEL_FROM_DATABASE=VT8251 PCI to ISA Bridge + +pci:v00001106d00003288* + ID_MODEL_FROM_DATABASE=VT8237A/VT8251 HDA Controller + +pci:v00001106d00003288sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VT8237A/VT8251 HDA Controller (ZBOX VD01) + +pci:v00001106d00003290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00003296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00003324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00003327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00003336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00003337* + ID_MODEL_FROM_DATABASE=VT8237A PCI to ISA Bridge + +pci:v00001106d00003340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00003343* + ID_MODEL_FROM_DATABASE=P4M890 [S3 UniChrome Pro] + +pci:v00001106d00003344* + ID_MODEL_FROM_DATABASE=CN700/P4M800 Pro/P4M800 CE/VN800 Graphics [S3 UniChrome Pro] + +pci:v00001106d00003349* + ID_MODEL_FROM_DATABASE=VT8251 AHCI/SATA 4-Port Controller + +pci:v00001106d00003351* + ID_MODEL_FROM_DATABASE=VT3351 Host Bridge + +pci:v00001106d00003353* + ID_MODEL_FROM_DATABASE=VX800 PCI to PCI Bridge + +pci:v00001106d00003364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00003371* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 [Chrome 9 HC] + +pci:v00001106d00003372* + ID_MODEL_FROM_DATABASE=VT8237S PCI to ISA Bridge + +pci:v00001106d0000337A* + ID_MODEL_FROM_DATABASE=VT8237A PCI to PCI Bridge + +pci:v00001106d0000337B* + ID_MODEL_FROM_DATABASE=VT8237A Host Bridge + +pci:v00001106d00003403* + ID_MODEL_FROM_DATABASE=VT6315 Series Firewire Controller + +pci:v00001106d00003403sv00001043sd00008374* + ID_MODEL_FROM_DATABASE=VT6315 Series Firewire Controller (M5A88-V EVO) + +pci:v00001106d00003403sv00001043sd00008384* + ID_MODEL_FROM_DATABASE=VT6315 Series Firewire Controller (P8P67 Deluxe Motherboard) + +pci:v00001106d00003409* + ID_MODEL_FROM_DATABASE=VX855/VX875 DRAM Bus Control + +pci:v00001106d00003410* + ID_MODEL_FROM_DATABASE=VX900 DRAM Bus Control + +pci:v00001106d00003410sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VX900 DRAM Bus Control (ZBOX nano VD01) + +pci:v00001106d00003432* + ID_MODEL_FROM_DATABASE=VL80x xHCI USB 3.0 Controller + +pci:v00001106d00003456* + ID_MODEL_FROM_DATABASE=VX11 Standard Host Bridge + +pci:v00001106d0000345B* + ID_MODEL_FROM_DATABASE=VX11 Miscellaneous Bus + +pci:v00001106d00003483* + ID_MODEL_FROM_DATABASE=VL805 USB 3.0 Host Controller + +pci:v00001106d00003A01* + ID_MODEL_FROM_DATABASE=VX11 Graphics [Chrome 645/640] + +pci:v00001106d00004149* + ID_MODEL_FROM_DATABASE=VIA VT6420 (ATA133) Controller + +pci:v00001106d00004204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00004208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00004238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00004258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00004259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 Host Bridge + +pci:v00001106d00004269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00004282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00004290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00004293* + ID_MODEL_FROM_DATABASE=PM896 Host Bridge + +pci:v00001106d00004296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00004308* + ID_MODEL_FROM_DATABASE=PT894 Host Bridge + +pci:v00001106d00004314* + ID_MODEL_FROM_DATABASE=CN700/VN800/P4M800CE/Pro Host Bridge + +pci:v00001106d00004324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00004327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00004336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00004340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00004351* + ID_MODEL_FROM_DATABASE=VT3351 Host Bridge + +pci:v00001106d00004353* + ID_MODEL_FROM_DATABASE=VX800/VX820 Power Management Control + +pci:v00001106d00004364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00004409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Power Management Control + +pci:v00001106d00004410* + ID_MODEL_FROM_DATABASE=VX900 Power Management and Chip Testing Control + +pci:v00001106d00004410sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VX900 Power Management and Chip Testing Control (ZBOX nano VD01) + +pci:v00001106d00005030* + ID_MODEL_FROM_DATABASE=VT82C596 ACPI [Apollo PRO] + +pci:v00001106d00005122* + ID_MODEL_FROM_DATABASE=VX855/VX875 Chrome 9 HCM Integrated Graphics + +pci:v00001106d00005208* + ID_MODEL_FROM_DATABASE=PT890 I/O APIC Interrupt Controller + +pci:v00001106d00005238* + ID_MODEL_FROM_DATABASE=K8T890 I/O APIC Interrupt Controller + +pci:v00001106d00005287* + ID_MODEL_FROM_DATABASE=VT8251 Serial ATA Controller + +pci:v00001106d00005290* + ID_MODEL_FROM_DATABASE=K8M890 I/O APIC Interrupt Controller + +pci:v00001106d00005308* + ID_MODEL_FROM_DATABASE=PT894 I/O APIC Interrupt Controller + +pci:v00001106d00005324* + ID_MODEL_FROM_DATABASE=VX800 Serial ATA and EIDE Controller + +pci:v00001106d00005327* + ID_MODEL_FROM_DATABASE=P4M890 I/O APIC Interrupt Controller + +pci:v00001106d00005336* + ID_MODEL_FROM_DATABASE=K8M890CE I/O APIC Interrupt Controller + +pci:v00001106d00005340* + ID_MODEL_FROM_DATABASE=PT900 I/O APIC Interrupt Controller + +pci:v00001106d00005351* + ID_MODEL_FROM_DATABASE=VT3351 I/O APIC Interrupt Controller + +pci:v00001106d00005353* + ID_MODEL_FROM_DATABASE=VX800/VX820 APIC and Central Traffic Control + +pci:v00001106d00005364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 I/O APIC Interrupt Controller + +pci:v00001106d00005372* + ID_MODEL_FROM_DATABASE=VT8237/8251 Serial ATA Controller + +pci:v00001106d00005409* + ID_MODEL_FROM_DATABASE=VX855/VX875 APIC and Central Traffic Control + +pci:v00001106d00005410* + ID_MODEL_FROM_DATABASE=VX900 APIC and Central Traffic Control + +pci:v00001106d00006100* + ID_MODEL_FROM_DATABASE=VT85C100A [Rhine II] + +pci:v00001106d00006287* + ID_MODEL_FROM_DATABASE=SATA RAID Controller + +pci:v00001106d00006290* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00006327* + ID_MODEL_FROM_DATABASE=P4M890 Security Device + +pci:v00001106d00006353* + ID_MODEL_FROM_DATABASE=VX800/VX820 Scratch Registers + +pci:v00001106d00006364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Security Device + +pci:v00001106d00006409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Scratch Registers + +pci:v00001106d00006410* + ID_MODEL_FROM_DATABASE=VX900 Scratch Registers + +pci:v00001106d00006410sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VX900 Scratch Registers (ZBOX nano VD01) + +pci:v00001106d00007122* + ID_MODEL_FROM_DATABASE=VX900 Graphics [Chrome9 HD] + +pci:v00001106d00007204* + ID_MODEL_FROM_DATABASE=K8M800 Host Bridge + +pci:v00001106d00007205* + ID_MODEL_FROM_DATABASE=KM400/KN400/P4M800 [S3 UniChrome] + +pci:v00001106d00007205sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=KM400/KN400/P4M800 [S3 UniChrome] (Gigabyte GA-7VM400(A)M(F) Motherboard) + +pci:v00001106d00007205sv00001462sd00007061* + ID_MODEL_FROM_DATABASE=KM400/KN400/P4M800 [S3 UniChrome] (MS-7061) + +pci:v00001106d00007208* + ID_MODEL_FROM_DATABASE=PT890 Host Bridge + +pci:v00001106d00007238* + ID_MODEL_FROM_DATABASE=K8T890 Host Bridge + +pci:v00001106d00007258* + ID_MODEL_FROM_DATABASE=PT880 Host Bridge + +pci:v00001106d00007259* + ID_MODEL_FROM_DATABASE=CN333/CN400/PM880 Host Bridge + +pci:v00001106d00007269* + ID_MODEL_FROM_DATABASE=KT880 Host Bridge + +pci:v00001106d00007282* + ID_MODEL_FROM_DATABASE=K8T800Pro Host Bridge + +pci:v00001106d00007290* + ID_MODEL_FROM_DATABASE=K8M890 Host Bridge + +pci:v00001106d00007293* + ID_MODEL_FROM_DATABASE=PM896 Host Bridge + +pci:v00001106d00007296* + ID_MODEL_FROM_DATABASE=P4M800 Host Bridge + +pci:v00001106d00007308* + ID_MODEL_FROM_DATABASE=PT894 Host Bridge + +pci:v00001106d00007314* + ID_MODEL_FROM_DATABASE=CN700/VN800/P4M800CE/Pro Host Bridge + +pci:v00001106d00007324* + ID_MODEL_FROM_DATABASE=CX700/VX700 Host Bridge + +pci:v00001106d00007327* + ID_MODEL_FROM_DATABASE=P4M890 Host Bridge + +pci:v00001106d00007336* + ID_MODEL_FROM_DATABASE=K8M890CE Host Bridge + +pci:v00001106d00007340* + ID_MODEL_FROM_DATABASE=PT900 Host Bridge + +pci:v00001106d00007351* + ID_MODEL_FROM_DATABASE=VT3351 Host Bridge + +pci:v00001106d00007353* + ID_MODEL_FROM_DATABASE=VX800/VX820 North-South Module Interface Control + +pci:v00001106d00007364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 Host Bridge + +pci:v00001106d00007409* + ID_MODEL_FROM_DATABASE=VX855/VX875 North-South Module Interface Control + +pci:v00001106d00007410* + ID_MODEL_FROM_DATABASE=VX900 North-South Module Interface Control + +pci:v00001106d00007410sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VX900 North-South Module Interface Control (ZBOX nano VD01) + +pci:v00001106d00008231* + ID_MODEL_FROM_DATABASE=VT8231 [PCI-to-ISA Bridge] + +pci:v00001106d00008235* + ID_MODEL_FROM_DATABASE=VT8235 ACPI + +pci:v00001106d00008305* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133 AGP] + +pci:v00001106d00008324* + ID_MODEL_FROM_DATABASE=CX700/VX700 PCI to ISA Bridge + +pci:v00001106d00008353* + ID_MODEL_FROM_DATABASE=VX800/VX820 Bus Control and Power Management + +pci:v00001106d00008391* + ID_MODEL_FROM_DATABASE=VT8371 [KX133 AGP] + +pci:v00001106d00008400* + ID_MODEL_FROM_DATABASE=MVP4 + +pci:v00001106d00008409* + ID_MODEL_FROM_DATABASE=VX855/VX875 Bus Control and Power Management + +pci:v00001106d00008410* + ID_MODEL_FROM_DATABASE=VX900 Bus Control and Power Management + +pci:v00001106d00008410sv000019DAsd0000A179* + ID_MODEL_FROM_DATABASE=VX900 Bus Control and Power Management (ZBOX VD01) + +pci:v00001106d00008500* + ID_MODEL_FROM_DATABASE=KLE133/PLE133/PLE133T + +pci:v00001106d00008501* + ID_MODEL_FROM_DATABASE=VT8501 [Apollo MVP4 AGP] + +pci:v00001106d00008596* + ID_MODEL_FROM_DATABASE=VT82C596 [Apollo PRO AGP] + +pci:v00001106d00008597* + ID_MODEL_FROM_DATABASE=VT82C597 [Apollo VP3 AGP] + +pci:v00001106d00008598* + ID_MODEL_FROM_DATABASE=VT82C598/694x [Apollo MVP3/Pro133x AGP] + +pci:v00001106d00008598sv00001019sd00000985* + ID_MODEL_FROM_DATABASE=VT82C598/694x [Apollo MVP3/Pro133x AGP] (P6VXA Motherboard) + +pci:v00001106d00008601* + ID_MODEL_FROM_DATABASE=VT8601 [Apollo ProMedia AGP] + +pci:v00001106d00008605* + ID_MODEL_FROM_DATABASE=VT8605 [PM133 AGP] + +pci:v00001106d00008691* + ID_MODEL_FROM_DATABASE=VT82C691 [Apollo Pro] + +pci:v00001106d00008693* + ID_MODEL_FROM_DATABASE=VT82C693 [Apollo Pro Plus] PCI Bridge + +pci:v00001106d00008A25* + ID_MODEL_FROM_DATABASE=PL133/PL133T [S3 ProSavage] + +pci:v00001106d00008A26* + ID_MODEL_FROM_DATABASE=KL133/KL133A/KM133/KM133A [S3 ProSavage] + +pci:v00001106d00008D01* + ID_MODEL_FROM_DATABASE=PN133/PN133T [S3 Twister] + +pci:v00001106d00008D04* + ID_MODEL_FROM_DATABASE=KM266/P4M266/P4M266A/P4N266 [S3 ProSavageDDR] + +pci:v00001106d00009001* + ID_MODEL_FROM_DATABASE=VX900 Serial ATA Controller + +pci:v00001106d00009082* + ID_MODEL_FROM_DATABASE=Standard AHCI 1.0 SATA Controller + +pci:v00001106d00009140* + ID_MODEL_FROM_DATABASE=HDMI Audio Device + +pci:v00001106d00009201* + ID_MODEL_FROM_DATABASE=USB3.0 Controller + +pci:v00001106d00009530* + ID_MODEL_FROM_DATABASE=Secure Digital Memory Card Controller + +pci:v00001106d000095D0* + ID_MODEL_FROM_DATABASE=SDIO Host Controller + +pci:v00001106d0000A208* + ID_MODEL_FROM_DATABASE=PT890 PCI to PCI Bridge Controller + +pci:v00001106d0000A238* + ID_MODEL_FROM_DATABASE=K8T890 PCI to PCI Bridge Controller + +pci:v00001106d0000A327* + ID_MODEL_FROM_DATABASE=P4M890 PCI to PCI Bridge Controller + +pci:v00001106d0000A353* + ID_MODEL_FROM_DATABASE=VX8xx South-North Module Interface Control + +pci:v00001106d0000A364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 PCI to PCI Bridge Controller + +pci:v00001106d0000A409* + ID_MODEL_FROM_DATABASE=VX855/VX875 USB Device Controller + +pci:v00001106d0000A410* + ID_MODEL_FROM_DATABASE=VX900 PCI Express Root Port 0 + +pci:v00001106d0000B091* + ID_MODEL_FROM_DATABASE=VT8633 [Apollo Pro266 AGP] + +pci:v00001106d0000B099* + ID_MODEL_FROM_DATABASE=VT8366/A/7 [Apollo KT266/A/333 AGP] + +pci:v00001106d0000B101* + ID_MODEL_FROM_DATABASE=VT8653 AGP Bridge + +pci:v00001106d0000B102* + ID_MODEL_FROM_DATABASE=VT8362 AGP Bridge + +pci:v00001106d0000B103* + ID_MODEL_FROM_DATABASE=VT8615 AGP Bridge + +pci:v00001106d0000B112* + ID_MODEL_FROM_DATABASE=VT8361 [KLE133] AGP Bridge + +pci:v00001106d0000B113* + ID_MODEL_FROM_DATABASE=VPX/VPX2 I/O APIC Interrupt Controller + +pci:v00001106d0000B115* + ID_MODEL_FROM_DATABASE=VT8363/8365 [KT133/KM133] PCI Bridge + +pci:v00001106d0000B168* + ID_MODEL_FROM_DATABASE=VT8235 PCI Bridge + +pci:v00001106d0000B188* + ID_MODEL_FROM_DATABASE=VT8237/8251 PCI bridge [K8M890/K8T800/K8T890 South] + +pci:v00001106d0000B188sv0000147Bsd00001407* + ID_MODEL_FROM_DATABASE=VT8237/8251 PCI bridge [K8M890/K8T800/K8T890 South] (KV8-MAX3 motherboard) + +pci:v00001106d0000B198* + ID_MODEL_FROM_DATABASE=VT8237/VX700 PCI Bridge + +pci:v00001106d0000B213* + ID_MODEL_FROM_DATABASE=VPX/VPX2 I/O APIC Interrupt Controller + +pci:v00001106d0000B353* + ID_MODEL_FROM_DATABASE=VX855/VX875/VX900 PCI to PCI Bridge + +pci:v00001106d0000B410* + ID_MODEL_FROM_DATABASE=VX900 PCI Express Root Port 1 + +pci:v00001106d0000B999* + ID_MODEL_FROM_DATABASE=[K8T890 North / VT8237 South] PCI Bridge + +pci:v00001106d0000C208* + ID_MODEL_FROM_DATABASE=PT890 PCI to PCI Bridge Controller + +pci:v00001106d0000C238* + ID_MODEL_FROM_DATABASE=K8T890 PCI to PCI Bridge Controller + +pci:v00001106d0000C327* + ID_MODEL_FROM_DATABASE=P4M890 PCI to PCI Bridge Controller + +pci:v00001106d0000C340* + ID_MODEL_FROM_DATABASE=PT900 PCI to PCI Bridge Controller + +pci:v00001106d0000C353* + ID_MODEL_FROM_DATABASE=VX800/VX820 PCI Express Root Port + +pci:v00001106d0000C364* + ID_MODEL_FROM_DATABASE=CN896/VN896/P4M900 PCI to PCI Bridge Controller + +pci:v00001106d0000C409* + ID_MODEL_FROM_DATABASE=VX855/VX875 EIDE Controller + +pci:v00001106d0000C410* + ID_MODEL_FROM_DATABASE=VX900 PCI Express Root Port 2 + +pci:v00001106d0000D104* + ID_MODEL_FROM_DATABASE=VT8237R USB UDCI Controller + +pci:v00001106d0000D208* + ID_MODEL_FROM_DATABASE=PT890 PCI to PCI Bridge Controller + +pci:v00001106d0000D213* + ID_MODEL_FROM_DATABASE=VPX/VPX2 PCI to PCI Bridge Controller + +pci:v00001106d0000D238* + ID_MODEL_FROM_DATABASE=K8T890 PCI to PCI Bridge Controller + +pci:v00001106d0000D340* + ID_MODEL_FROM_DATABASE=PT900 PCI to PCI Bridge Controller + +pci:v00001106d0000D410* + ID_MODEL_FROM_DATABASE=VX900 PCI Express Root Port 3 + +pci:v00001106d0000E208* + ID_MODEL_FROM_DATABASE=PT890 PCI to PCI Bridge Controller + +pci:v00001106d0000E238* + ID_MODEL_FROM_DATABASE=K8T890 PCI to PCI Bridge Controller + +pci:v00001106d0000E340* + ID_MODEL_FROM_DATABASE=PT900 PCI to PCI Bridge Controller + +pci:v00001106d0000E353* + ID_MODEL_FROM_DATABASE=VX800/VX820 PCI Express Root Port + +pci:v00001106d0000E410* + ID_MODEL_FROM_DATABASE=VX900 PCI Express Physical Layer Electrical Sub-block + +pci:v00001106d0000F208* + ID_MODEL_FROM_DATABASE=PT890 PCI to PCI Bridge Controller + +pci:v00001106d0000F238* + ID_MODEL_FROM_DATABASE=K8T890 PCI to PCI Bridge Controller + +pci:v00001106d0000F340* + ID_MODEL_FROM_DATABASE=PT900 PCI to PCI Bridge Controller + +pci:v00001106d0000F353* + ID_MODEL_FROM_DATABASE=VX800/VX820 PCI Express Root Port + +pci:v00001107* + ID_VENDOR_FROM_DATABASE=Stratus Computers + +pci:v00001107d00000576* + ID_MODEL_FROM_DATABASE=VIA VT82C570MV [Apollo] (Wrong vendor ID!) + +pci:v00001108* + ID_VENDOR_FROM_DATABASE=Proteon, Inc. + +pci:v00001108d00000100* + ID_MODEL_FROM_DATABASE=p1690plus_AA + +pci:v00001108d00000101* + ID_MODEL_FROM_DATABASE=p1690plus_AB + +pci:v00001108d00000105* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001108d00000108* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001108d00000138* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001108d00000139* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001108d0000013C* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001108d0000013D* + ID_MODEL_FROM_DATABASE=P1690Plus + +pci:v00001109* + ID_VENDOR_FROM_DATABASE=Cogent Data Technologies, Inc. + +pci:v00001109d00001400* + ID_MODEL_FROM_DATABASE=EM110TX [EX110TX] + +pci:v0000110A* + ID_VENDOR_FROM_DATABASE=Siemens AG + +pci:v0000110Ad00000002* + ID_MODEL_FROM_DATABASE=Pirahna 2-port + +pci:v0000110Ad00000005* + ID_MODEL_FROM_DATABASE=Tulip controller, power management, switch extender + +pci:v0000110Ad00000006* + ID_MODEL_FROM_DATABASE=FSC PINC (I/O-APIC) + +pci:v0000110Ad00000015* + ID_MODEL_FROM_DATABASE=FSC Multiprocessor Interrupt Controller + +pci:v0000110Ad0000001D* + ID_MODEL_FROM_DATABASE=FSC Copernicus Management Controller + +pci:v0000110Ad0000007B* + ID_MODEL_FROM_DATABASE=FSC Remote Service Controller, mailbox device + +pci:v0000110Ad0000007C* + ID_MODEL_FROM_DATABASE=FSC Remote Service Controller, shared memory device + +pci:v0000110Ad0000007D* + ID_MODEL_FROM_DATABASE=FSC Remote Service Controller, SMIC device + +pci:v0000110Ad00002101* + ID_MODEL_FROM_DATABASE=HST SAPHIR V Primary PCI (ISDN/PMx) + +pci:v0000110Ad00002102* + ID_MODEL_FROM_DATABASE=DSCC4 PEB/PEF 20534 DMA Supported Serial Communication Controller with 4 Channels + +pci:v0000110Ad00002104* + ID_MODEL_FROM_DATABASE=Eicon Diva 2.02 compatible passive ISDN card + +pci:v0000110Ad00003141* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5611 / 5621 + +pci:v0000110Ad00003142* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5613 / 5614 + +pci:v0000110Ad00003143* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 1613 + +pci:v0000110Ad00004021* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5512 (Profibus and MPI Cardbus Adapter) + +pci:v0000110Ad00004029* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5613 A2 + +pci:v0000110Ad00004029sv0000110Asd00004029* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5613 A2 + +pci:v0000110Ad00004029sv0000110Asd0000C029* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5613 A2 (SIMATIC NET CP 5614 A2) + +pci:v0000110Ad00004035* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 1613 A2 + +pci:v0000110Ad00004036* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 1616 + +pci:v0000110Ad00004038* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 1604 + +pci:v0000110Ad00004069* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5623 + +pci:v0000110Ad00004069sv0000110Asd00004069* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5623 + +pci:v0000110Ad00004069sv0000110Asd0000C069* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5623 (SIMATIC NET CP 5624) + +pci:v0000110Ad0000407C* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5612 + +pci:v0000110Ad0000407D* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5613 A3 + +pci:v0000110Ad0000407E* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5622 + +pci:v0000110Ad00004083* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5614 A3 + +pci:v0000110Ad00004084* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 1626 + +pci:v0000110Ad00004942* + ID_MODEL_FROM_DATABASE=FPGA I-Bus Tracer for MBD + +pci:v0000110Ad00006120* + ID_MODEL_FROM_DATABASE=SZB6120 + +pci:v0000110B* + ID_VENDOR_FROM_DATABASE=Chromatic Research Inc. + +pci:v0000110Bd00000001* + ID_MODEL_FROM_DATABASE=Mpact Media Processor + +pci:v0000110Bd00000004* + ID_MODEL_FROM_DATABASE=Mpact 2 + +pci:v0000110C* + ID_VENDOR_FROM_DATABASE=Mini-Max Technology, Inc. + +pci:v0000110D* + ID_VENDOR_FROM_DATABASE=Znyx Advanced Systems + +pci:v0000110E* + ID_VENDOR_FROM_DATABASE=CPU Technology + +pci:v0000110F* + ID_VENDOR_FROM_DATABASE=Ross Technology + +pci:v00001110* + ID_VENDOR_FROM_DATABASE=Powerhouse Systems + +pci:v00001110d00006037* + ID_MODEL_FROM_DATABASE=Firepower Powerized SMP I/O ASIC + +pci:v00001110d00006073* + ID_MODEL_FROM_DATABASE=Firepower Powerized SMP I/O ASIC + +pci:v00001111* + ID_VENDOR_FROM_DATABASE=Santa Cruz Operation + +pci:v00001112* + ID_VENDOR_FROM_DATABASE=Osicom Technologies Inc + +pci:v00001112d00002200* + ID_MODEL_FROM_DATABASE=FDDI Adapter + +pci:v00001112d00002300* + ID_MODEL_FROM_DATABASE=Fast Ethernet Adapter + +pci:v00001112d00002340* + ID_MODEL_FROM_DATABASE=4 Port Fast Ethernet Adapter + +pci:v00001112d00002400* + ID_MODEL_FROM_DATABASE=ATM Adapter + +pci:v00001113* + ID_VENDOR_FROM_DATABASE=Accton Technology Corporation + +pci:v00001113d00001211* + ID_MODEL_FROM_DATABASE=SMC2-1211TX + +pci:v00001113d00001211sv0000103Csd00001207* + ID_MODEL_FROM_DATABASE=SMC2-1211TX (EN-1207D Fast Ethernet Adapter) + +pci:v00001113d00001211sv00001113sd00001211* + ID_MODEL_FROM_DATABASE=SMC2-1211TX (EN-1207D Fast Ethernet Adapter) + +pci:v00001113d00001216* + ID_MODEL_FROM_DATABASE=EN-1216 Ethernet Adapter + +pci:v00001113d00001216sv00001113sd00001216* + ID_MODEL_FROM_DATABASE=EN-1216 Ethernet Adapter (EN1207F series PCI Fast Ethernet Adapter) + +pci:v00001113d00001216sv00001113sd00002220* + ID_MODEL_FROM_DATABASE=EN-1216 Ethernet Adapter (EN2220A Cardbus Fast Ethernet Adapter) + +pci:v00001113d00001216sv00001113sd00002242* + ID_MODEL_FROM_DATABASE=EN-1216 Ethernet Adapter (EN2242 10/100 Ethernet Mini-PCI Card) + +pci:v00001113d00001216sv0000111Asd00001020* + ID_MODEL_FROM_DATABASE=EN-1216 Ethernet Adapter (SpeedStream 1020 PCI 10/100 Ethernet Adaptor [EN-1207F-TX ?]) + +pci:v00001113d00001217* + ID_MODEL_FROM_DATABASE=EN-1217 Ethernet Adapter + +pci:v00001113d00005105* + ID_MODEL_FROM_DATABASE=10Mbps Network card + +pci:v00001113d00009211* + ID_MODEL_FROM_DATABASE=EN-1207D Fast Ethernet Adapter + +pci:v00001113d00009211sv00001113sd00009211* + ID_MODEL_FROM_DATABASE=EN-1207D Fast Ethernet Adapter + +pci:v00001113d00009511* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible Fast Ethernet + +pci:v00001113d0000D301* + ID_MODEL_FROM_DATABASE=CPWNA100 (Philips wireless PCMCIA) + +pci:v00001113d0000EC02* + ID_MODEL_FROM_DATABASE=SMC 1244TX v3 + +pci:v00001113d0000EE23* + ID_MODEL_FROM_DATABASE=SMCWPCIT-G 108Mbps Wireless PCI adapter + +pci:v00001114* + ID_VENDOR_FROM_DATABASE=Atmel Corporation + +pci:v00001114d00000506* + ID_MODEL_FROM_DATABASE=at76c506 802.11b Wireless Network Adaptor + +pci:v00001115* + ID_VENDOR_FROM_DATABASE=3D Labs + +pci:v00001116* + ID_VENDOR_FROM_DATABASE=Data Translation + +pci:v00001116d00000022* + ID_MODEL_FROM_DATABASE=DT3001 + +pci:v00001116d00000023* + ID_MODEL_FROM_DATABASE=DT3002 + +pci:v00001116d00000024* + ID_MODEL_FROM_DATABASE=DT3003 + +pci:v00001116d00000025* + ID_MODEL_FROM_DATABASE=DT3004 + +pci:v00001116d00000026* + ID_MODEL_FROM_DATABASE=DT3005 + +pci:v00001116d00000027* + ID_MODEL_FROM_DATABASE=DT3001-PGL + +pci:v00001116d00000028* + ID_MODEL_FROM_DATABASE=DT3003-PGL + +pci:v00001116d00000051* + ID_MODEL_FROM_DATABASE=DT322 + +pci:v00001116d00000060* + ID_MODEL_FROM_DATABASE=DT340 + +pci:v00001116d00000069* + ID_MODEL_FROM_DATABASE=DT332 + +pci:v00001116d000080C2* + ID_MODEL_FROM_DATABASE=DT3162 + +pci:v00001117* + ID_VENDOR_FROM_DATABASE=Datacube, Inc + +pci:v00001117d00009500* + ID_MODEL_FROM_DATABASE=Max-1C SVGA card + +pci:v00001117d00009501* + ID_MODEL_FROM_DATABASE=Max-1C image processing + +pci:v00001118* + ID_VENDOR_FROM_DATABASE=Berg Electronics + +pci:v00001119* + ID_VENDOR_FROM_DATABASE=ICP Vortex Computersysteme GmbH + +pci:v00001119d00000000* + ID_MODEL_FROM_DATABASE=GDT 6000/6020/6050 + +pci:v00001119d00000001* + ID_MODEL_FROM_DATABASE=GDT 6000B/6010 + +pci:v00001119d00000002* + ID_MODEL_FROM_DATABASE=GDT 6110/6510 + +pci:v00001119d00000003* + ID_MODEL_FROM_DATABASE=GDT 6120/6520 + +pci:v00001119d00000004* + ID_MODEL_FROM_DATABASE=GDT 6530 + +pci:v00001119d00000005* + ID_MODEL_FROM_DATABASE=GDT 6550 + +pci:v00001119d00000006* + ID_MODEL_FROM_DATABASE=GDT 6117/6517 + +pci:v00001119d00000007* + ID_MODEL_FROM_DATABASE=GDT 6127/6527 + +pci:v00001119d00000008* + ID_MODEL_FROM_DATABASE=GDT 6537 + +pci:v00001119d00000009* + ID_MODEL_FROM_DATABASE=GDT 6557/6557-ECC + +pci:v00001119d0000000A* + ID_MODEL_FROM_DATABASE=GDT 6115/6515 + +pci:v00001119d0000000B* + ID_MODEL_FROM_DATABASE=GDT 6125/6525 + +pci:v00001119d0000000C* + ID_MODEL_FROM_DATABASE=GDT 6535 + +pci:v00001119d0000000D* + ID_MODEL_FROM_DATABASE=GDT 6555/6555-ECC + +pci:v00001119d00000100* + ID_MODEL_FROM_DATABASE=GDT 6117RP/6517RP + +pci:v00001119d00000101* + ID_MODEL_FROM_DATABASE=GDT 6127RP/6527RP + +pci:v00001119d00000102* + ID_MODEL_FROM_DATABASE=GDT 6537RP + +pci:v00001119d00000103* + ID_MODEL_FROM_DATABASE=GDT 6557RP + +pci:v00001119d00000104* + ID_MODEL_FROM_DATABASE=GDT 6111RP/6511RP + +pci:v00001119d00000105* + ID_MODEL_FROM_DATABASE=GDT 6121RP/6521RP + +pci:v00001119d00000110* + ID_MODEL_FROM_DATABASE=GDT 6117RD/6517RD + +pci:v00001119d00000111* + ID_MODEL_FROM_DATABASE=GDT 6127RD/6527RD + +pci:v00001119d00000112* + ID_MODEL_FROM_DATABASE=GDT 6537RD + +pci:v00001119d00000113* + ID_MODEL_FROM_DATABASE=GDT 6557RD + +pci:v00001119d00000114* + ID_MODEL_FROM_DATABASE=GDT 6111RD/6511RD + +pci:v00001119d00000115* + ID_MODEL_FROM_DATABASE=GDT 6121RD/6521RD + +pci:v00001119d00000118* + ID_MODEL_FROM_DATABASE=GDT 6118RD/6518RD/6618RD + +pci:v00001119d00000119* + ID_MODEL_FROM_DATABASE=GDT 6128RD/6528RD/6628RD + +pci:v00001119d0000011A* + ID_MODEL_FROM_DATABASE=GDT 6538RD/6638RD + +pci:v00001119d0000011B* + ID_MODEL_FROM_DATABASE=GDT 6558RD/6658RD + +pci:v00001119d00000120* + ID_MODEL_FROM_DATABASE=GDT 6117RP2/6517RP2 + +pci:v00001119d00000121* + ID_MODEL_FROM_DATABASE=GDT 6127RP2/6527RP2 + +pci:v00001119d00000122* + ID_MODEL_FROM_DATABASE=GDT 6537RP2 + +pci:v00001119d00000123* + ID_MODEL_FROM_DATABASE=GDT 6557RP2 + +pci:v00001119d00000124* + ID_MODEL_FROM_DATABASE=GDT 6111RP2/6511RP2 + +pci:v00001119d00000125* + ID_MODEL_FROM_DATABASE=GDT 6121RP2/6521RP2 + +pci:v00001119d00000136* + ID_MODEL_FROM_DATABASE=GDT 6113RS/6513RS + +pci:v00001119d00000137* + ID_MODEL_FROM_DATABASE=GDT 6123RS/6523RS + +pci:v00001119d00000138* + ID_MODEL_FROM_DATABASE=GDT 6118RS/6518RS/6618RS + +pci:v00001119d00000139* + ID_MODEL_FROM_DATABASE=GDT 6128RS/6528RS/6628RS + +pci:v00001119d0000013A* + ID_MODEL_FROM_DATABASE=GDT 6538RS/6638RS + +pci:v00001119d0000013B* + ID_MODEL_FROM_DATABASE=GDT 6558RS/6658RS + +pci:v00001119d0000013C* + ID_MODEL_FROM_DATABASE=GDT 6533RS/6633RS + +pci:v00001119d0000013D* + ID_MODEL_FROM_DATABASE=GDT 6543RS/6643RS + +pci:v00001119d0000013E* + ID_MODEL_FROM_DATABASE=GDT 6553RS/6653RS + +pci:v00001119d0000013F* + ID_MODEL_FROM_DATABASE=GDT 6563RS/6663RS + +pci:v00001119d00000166* + ID_MODEL_FROM_DATABASE=GDT 7113RN/7513RN/7613RN + +pci:v00001119d00000167* + ID_MODEL_FROM_DATABASE=GDT 7123RN/7523RN/7623RN + +pci:v00001119d00000168* + ID_MODEL_FROM_DATABASE=GDT 7118RN/7518RN/7518RN + +pci:v00001119d00000169* + ID_MODEL_FROM_DATABASE=GDT 7128RN/7528RN/7628RN + +pci:v00001119d0000016A* + ID_MODEL_FROM_DATABASE=GDT 7538RN/7638RN + +pci:v00001119d0000016B* + ID_MODEL_FROM_DATABASE=GDT 7558RN/7658RN + +pci:v00001119d0000016C* + ID_MODEL_FROM_DATABASE=GDT 7533RN/7633RN + +pci:v00001119d0000016D* + ID_MODEL_FROM_DATABASE=GDT 7543RN/7643RN + +pci:v00001119d0000016E* + ID_MODEL_FROM_DATABASE=GDT 7553RN/7653RN + +pci:v00001119d0000016F* + ID_MODEL_FROM_DATABASE=GDT 7563RN/7663RN + +pci:v00001119d000001D6* + ID_MODEL_FROM_DATABASE=GDT 4x13RZ + +pci:v00001119d000001D7* + ID_MODEL_FROM_DATABASE=GDT 4x23RZ + +pci:v00001119d000001F6* + ID_MODEL_FROM_DATABASE=GDT 8x13RZ + +pci:v00001119d000001F7* + ID_MODEL_FROM_DATABASE=GDT 8x23RZ + +pci:v00001119d000001FC* + ID_MODEL_FROM_DATABASE=GDT 8x33RZ + +pci:v00001119d000001FD* + ID_MODEL_FROM_DATABASE=GDT 8x43RZ + +pci:v00001119d000001FE* + ID_MODEL_FROM_DATABASE=GDT 8x53RZ + +pci:v00001119d000001FF* + ID_MODEL_FROM_DATABASE=GDT 8x63RZ + +pci:v00001119d00000210* + ID_MODEL_FROM_DATABASE=GDT 6519RD/6619RD + +pci:v00001119d00000211* + ID_MODEL_FROM_DATABASE=GDT 6529RD/6629RD + +pci:v00001119d00000260* + ID_MODEL_FROM_DATABASE=GDT 7519RN/7619RN + +pci:v00001119d00000261* + ID_MODEL_FROM_DATABASE=GDT 7529RN/7629RN + +pci:v00001119d000002FF* + ID_MODEL_FROM_DATABASE=GDT MAXRP + +pci:v00001119d00000300* + ID_MODEL_FROM_DATABASE=GDT NEWRX + +pci:v00001119d00000301* + ID_MODEL_FROM_DATABASE=GDT NEWRX2 + +pci:v0000111A* + ID_VENDOR_FROM_DATABASE=Efficient Networks, Inc + +pci:v0000111Ad00000000* + ID_MODEL_FROM_DATABASE=155P-MF1 (FPGA) + +pci:v0000111Ad00000002* + ID_MODEL_FROM_DATABASE=155P-MF1 (ASIC) + +pci:v0000111Ad00000003* + ID_MODEL_FROM_DATABASE=ENI-25P ATM + +pci:v0000111Ad00000003sv0000111Asd00000000* + ID_MODEL_FROM_DATABASE=ENI-25P ATM (ENI-25p Miniport ATM Adapter) + +pci:v0000111Ad00000005* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) + +pci:v0000111Ad00000005sv0000111Asd00000001* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3010 ATM) + +pci:v0000111Ad00000005sv0000111Asd00000009* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3060 ADSL (VPI=0)) + +pci:v0000111Ad00000005sv0000111Asd00000101* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3010 ATM) + +pci:v0000111Ad00000005sv0000111Asd00000109* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3060CO ADSL (VPI=0)) + +pci:v0000111Ad00000005sv0000111Asd00000809* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3060 ADSL (VPI=0 or 8)) + +pci:v0000111Ad00000005sv0000111Asd00000909* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3060CO ADSL (VPI=0 or 8)) + +pci:v0000111Ad00000005sv0000111Asd00000A09* + ID_MODEL_FROM_DATABASE=SpeedStream (LANAI) (ENI-3060 ADSL (VPI=<0..15>)) + +pci:v0000111Ad00000007* + ID_MODEL_FROM_DATABASE=SpeedStream ADSL + +pci:v0000111Ad00000007sv0000111Asd00001001* + ID_MODEL_FROM_DATABASE=SpeedStream ADSL (ENI-3061 ADSL [ASIC]) + +pci:v0000111Ad00001020* + ID_MODEL_FROM_DATABASE=SpeedStream PCI 10/100 Network Card + +pci:v0000111Ad00001203* + ID_MODEL_FROM_DATABASE=SpeedStream 1023 Wireless PCI Adapter + +pci:v0000111B* + ID_VENDOR_FROM_DATABASE=Teledyne Electronic Systems + +pci:v0000111C* + ID_VENDOR_FROM_DATABASE=Tricord Systems Inc. + +pci:v0000111Cd00000001* + ID_MODEL_FROM_DATABASE=Powerbis Bridge + +pci:v0000111D* + ID_VENDOR_FROM_DATABASE=Integrated Device Technology, Inc. [IDT] + +pci:v0000111Dd00000001* + ID_MODEL_FROM_DATABASE=IDT77201/77211 155Mbps ATM SAR Controller [NICStAR] + +pci:v0000111Dd00000003* + ID_MODEL_FROM_DATABASE=IDT77222/77252 155Mbps ATM MICRO ABR SAR Controller + +pci:v0000111Dd00000004* + ID_MODEL_FROM_DATABASE=IDT77V252 155Mbps ATM MICRO ABR SAR Controller + +pci:v0000111Dd00000005* + ID_MODEL_FROM_DATABASE=IDT77V222 155Mbps ATM MICRO ABR SAR Controller + +pci:v0000111Dd00008018* + ID_MODEL_FROM_DATABASE=PES12N3A PCI Express Switch + +pci:v0000111Dd0000801C* + ID_MODEL_FROM_DATABASE=PES24N3A PCI Express Switch + +pci:v0000111Dd00008028* + ID_MODEL_FROM_DATABASE=PES4T4 PCI Express Switch + +pci:v0000111Dd0000802B* + ID_MODEL_FROM_DATABASE=PES8T5A PCI Express Switch + +pci:v0000111Dd0000802C* + ID_MODEL_FROM_DATABASE=PES16T4 PCI Express Switch + +pci:v0000111Dd0000802D* + ID_MODEL_FROM_DATABASE=PES16T7 PCI Express Switch + +pci:v0000111Dd0000802E* + ID_MODEL_FROM_DATABASE=PES24T6 PCI Express Switch + +pci:v0000111Dd0000802F* + ID_MODEL_FROM_DATABASE=PES32T8 PCI Express Switch + +pci:v0000111Dd00008032* + ID_MODEL_FROM_DATABASE=PES48T12 PCI Express Switch + +pci:v0000111Dd00008034* + ID_MODEL_FROM_DATABASE=PES16/22/34H16 PCI Express Switch + +pci:v0000111Dd00008035* + ID_MODEL_FROM_DATABASE=PES32H8 PCI Express Switch + +pci:v0000111Dd00008036* + ID_MODEL_FROM_DATABASE=PES48H12 PCI Express Switch + +pci:v0000111Dd00008037* + ID_MODEL_FROM_DATABASE=PES64H16 PCI Express Switch + +pci:v0000111Dd00008039* + ID_MODEL_FROM_DATABASE=PES3T3 PCI Express Switch + +pci:v0000111Dd0000803A* + ID_MODEL_FROM_DATABASE=PES4T4 PCI Express Switch + +pci:v0000111Dd0000803C* + ID_MODEL_FROM_DATABASE=PES5T5 PCI Express Switch + +pci:v0000111Dd0000803D* + ID_MODEL_FROM_DATABASE=PES6T5 PCI Express Switch + +pci:v0000111Dd00008048* + ID_MODEL_FROM_DATABASE=PES8NT2 PCI Express Switch + +pci:v0000111Dd00008049* + ID_MODEL_FROM_DATABASE=PES8NT2 PCI Express Switch + +pci:v0000111Dd0000804A* + ID_MODEL_FROM_DATABASE=PES8NT2 PCI Express Internal NTB + +pci:v0000111Dd0000804B* + ID_MODEL_FROM_DATABASE=PES8NT2 PCI Express External NTB + +pci:v0000111Dd0000804C* + ID_MODEL_FROM_DATABASE=PES16NT2 PCI Express Switch + +pci:v0000111Dd0000804D* + ID_MODEL_FROM_DATABASE=PES16NT2 PCI Express Switch + +pci:v0000111Dd0000804E* + ID_MODEL_FROM_DATABASE=PES16NT2 PCI Express Internal NTB + +pci:v0000111Dd0000804F* + ID_MODEL_FROM_DATABASE=PES16NT2 PCI Express External NTB + +pci:v0000111Dd00008058* + ID_MODEL_FROM_DATABASE=PES12NT3 PCI Express Switch + +pci:v0000111Dd00008059* + ID_MODEL_FROM_DATABASE=PES12NT3 PCI Express Switch + +pci:v0000111Dd0000805A* + ID_MODEL_FROM_DATABASE=PES12NT3 PCI Express Internal NTB + +pci:v0000111Dd0000805B* + ID_MODEL_FROM_DATABASE=PES12NT3 PCI Express External NTB + +pci:v0000111Dd0000805C* + ID_MODEL_FROM_DATABASE=PES24NT3 PCI Express Switch + +pci:v0000111Dd0000805D* + ID_MODEL_FROM_DATABASE=PES24NT3 PCI Express Switch + +pci:v0000111Dd0000805E* + ID_MODEL_FROM_DATABASE=PES24NT3 PCI Express Internal NTB + +pci:v0000111Dd0000805F* + ID_MODEL_FROM_DATABASE=PES24NT3 PCI Express External NTB + +pci:v0000111Dd00008060* + ID_MODEL_FROM_DATABASE=PES16T4G2 PCI Express Gen2 Switch + +pci:v0000111Dd00008061* + ID_MODEL_FROM_DATABASE=PES12T3G2 PCI Express Gen2 Switch + +pci:v0000111Dd00008068* + ID_MODEL_FROM_DATABASE=PES6T6G2 PCI Express Gen2 Switch + +pci:v0000111Dd0000806A* + ID_MODEL_FROM_DATABASE=PES24T3G2 PCI Express Gen2 Switch + +pci:v0000111Dd0000806Asv000014C1sd0000000C* + ID_MODEL_FROM_DATABASE=PES24T3G2 PCI Express Gen2 Switch (10G-PCIE2-8B2) + +pci:v0000111Dd0000806C* + ID_MODEL_FROM_DATABASE=PES16T4A/4T4G2 PCI Express Gen2 Switch + +pci:v0000111Dd0000806E* + ID_MODEL_FROM_DATABASE=PES24T6G2 PCI Express Gen2 Switch + +pci:v0000111Dd0000806F* + ID_MODEL_FROM_DATABASE=HIO524G2 PCI Express Gen2 Switch + +pci:v0000111Dd00008088* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch + +pci:v0000111Dd00008088sv00001093sd0000752F* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8383mc Device) + +pci:v0000111Dd00008088sv00001093sd00007543* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8383mc System Host) + +pci:v0000111Dd00008088sv00001093sd0000755C* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8364) + +pci:v0000111Dd00008088sv00001093sd0000755D* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8374) + +pci:v0000111Dd00008088sv00001093sd000075FF* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8383mc DMA) + +pci:v0000111Dd00008088sv00001093sd00007600* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8383mc DMA) + +pci:v0000111Dd00008088sv00001093sd00007602* + ID_MODEL_FROM_DATABASE=PES32NT8BG2 PCI Express Switch (PXIe-8384) + +pci:v0000111Dd0000808F* + ID_MODEL_FROM_DATABASE=PES32NT8AG2 + +pci:v0000111Dd000080CF* + ID_MODEL_FROM_DATABASE=F32P08xG3 [PCIe boot mode] + +pci:v0000111Dd000080D2* + ID_MODEL_FROM_DATABASE=F32P08xG3 NVMe controller + +pci:v0000111E* + ID_VENDOR_FROM_DATABASE=Eldec + +pci:v0000111F* + ID_VENDOR_FROM_DATABASE=Precision Digital Images + +pci:v0000111Fd00004A47* + ID_MODEL_FROM_DATABASE=Precision MX Video engine interface + +pci:v0000111Fd00005243* + ID_MODEL_FROM_DATABASE=Frame capture bus interface + +pci:v00001120* + ID_VENDOR_FROM_DATABASE=EMC Corporation + +pci:v00001121* + ID_VENDOR_FROM_DATABASE=Zilog + +pci:v00001122* + ID_VENDOR_FROM_DATABASE=Multi-tech Systems, Inc. + +pci:v00001123* + ID_VENDOR_FROM_DATABASE=Excellent Design, Inc. + +pci:v00001124* + ID_VENDOR_FROM_DATABASE=Leutron Vision AG + +pci:v00001124d00002581* + ID_MODEL_FROM_DATABASE=Picport Monochrome + +pci:v00001125* + ID_VENDOR_FROM_DATABASE=Eurocore + +pci:v00001126* + ID_VENDOR_FROM_DATABASE=Vigra + +pci:v00001127* + ID_VENDOR_FROM_DATABASE=FORE Systems Inc + +pci:v00001127d00000200* + ID_MODEL_FROM_DATABASE=ForeRunner PCA-200 ATM + +pci:v00001127d00000210* + ID_MODEL_FROM_DATABASE=PCA-200PC + +pci:v00001127d00000250* + ID_MODEL_FROM_DATABASE=ATM + +pci:v00001127d00000300* + ID_MODEL_FROM_DATABASE=ForeRunner PCA-200EPC ATM + +pci:v00001127d00000310* + ID_MODEL_FROM_DATABASE=ATM + +pci:v00001127d00000400* + ID_MODEL_FROM_DATABASE=ForeRunnerHE ATM Adapter + +pci:v00001127d00000400sv00001127sd00000400* + ID_MODEL_FROM_DATABASE=ForeRunnerHE ATM Adapter (ForeRunnerHE ATM) + +pci:v00001129* + ID_VENDOR_FROM_DATABASE=Firmworks + +pci:v0000112A* + ID_VENDOR_FROM_DATABASE=Hermes Electronics Company, Ltd. + +pci:v0000112B* + ID_VENDOR_FROM_DATABASE=Linotype - Hell AG + +pci:v0000112C* + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +pci:v0000112D* + ID_VENDOR_FROM_DATABASE=Ravicad + +pci:v0000112E* + ID_VENDOR_FROM_DATABASE=Infomedia Microelectronics Inc. + +pci:v0000112F* + ID_VENDOR_FROM_DATABASE=Dalsa Inc. + +pci:v0000112Fd00000000* + ID_MODEL_FROM_DATABASE=MVC IC-PCI + +pci:v0000112Fd00000001* + ID_MODEL_FROM_DATABASE=MVC IM-PCI Video frame grabber/processor + +pci:v0000112Fd00000008* + ID_MODEL_FROM_DATABASE=PC-CamLink PCI framegrabber + +pci:v00001130* + ID_VENDOR_FROM_DATABASE=Computervision + +pci:v00001131* + ID_VENDOR_FROM_DATABASE=Philips Semiconductors + +pci:v00001131d00001561* + ID_MODEL_FROM_DATABASE=USB 1.1 Host Controller + +pci:v00001131d00001561sv00001775sd0000C200* + ID_MODEL_FROM_DATABASE=USB 1.1 Host Controller (C2K onboard USB 1.1 host controller) + +pci:v00001131d00001562* + ID_MODEL_FROM_DATABASE=USB 2.0 Host Controller + +pci:v00001131d00001562sv00001775sd0000C200* + ID_MODEL_FROM_DATABASE=USB 2.0 Host Controller (C2K onboard USB 2.0 host controller) + +pci:v00001131d00003400* + ID_MODEL_FROM_DATABASE=SmartPCI56(UCB1500) 56K Modem + +pci:v00001131d00005400* + ID_MODEL_FROM_DATABASE=TriMedia TM1000/1100 + +pci:v00001131d00005400sv000012CAsd00000000* + ID_MODEL_FROM_DATABASE=TriMedia TM1000/1100 (BlueICE) + +pci:v00001131d00005402* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 + +pci:v00001131d00005402sv00001244sd00000F00* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (Fritz!Card DSL) + +pci:v00001131d00005402sv000015EBsd00001300* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1300) + +pci:v00001131d00005402sv000015EBsd00001302* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1302) + +pci:v00001131d00005402sv000015EBsd00001304* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1304) + +pci:v00001131d00005402sv000015EBsd00001305* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1305) + +pci:v00001131d00005402sv000015EBsd00001306* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (PMCDT1306) + +pci:v00001131d00005402sv000015EBsd00001308* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1308) + +pci:v00001131d00005402sv000015EBsd00001331* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1301 with SAA7121) + +pci:v00001131d00005402sv000015EBsd00001337* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (DT1301 with SAA7127) + +pci:v00001131d00005402sv000015EBsd00002D3D* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (X3D) + +pci:v00001131d00005402sv000015EBsd00007022* + ID_MODEL_FROM_DATABASE=TriMedia TM1300 (PTM1300) + +pci:v00001131d00005405* + ID_MODEL_FROM_DATABASE=TriMedia TM1500 + +pci:v00001131d00005405sv00001136sd00000005* + ID_MODEL_FROM_DATABASE=TriMedia TM1500 (LCP-1500) + +pci:v00001131d00005406* + ID_MODEL_FROM_DATABASE=TriMedia TM1700 + +pci:v00001131d0000540B* + ID_MODEL_FROM_DATABASE=PNX1005 Media Processor + +pci:v00001131d0000540Bsv00001131sd00000020* + ID_MODEL_FROM_DATABASE=PNX1005 Media Processor (PNXLite PCI Demo Board) + +pci:v00001131d00007130* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder + +pci:v00001131d00007130sv00000000sd00004016* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 401) + +pci:v00001131d00007130sv00000000sd00004051* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 405 FM) + +pci:v00001131d00007130sv00000000sd00005051* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 505 RDS) + +pci:v00001131d00007130sv00000000sd0000505B* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 505 RDS) + +pci:v00001131d00007130sv0000102Bsd000048D0* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Matrox CronosPlus) + +pci:v00001131d00007130sv00001048sd0000226B* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (ELSA EX-VISION 300TV) + +pci:v00001131d00007130sv0000107Dsd00006655* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (WinFast DTV1000S) + +pci:v00001131d00007130sv00001131sd00000000* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (SAA7130-based TV tuner card) + +pci:v00001131d00007130sv00001131sd00002001* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (10MOONS PCI TV CAPTURE CARD) + +pci:v00001131d00007130sv00001131sd00002005* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Techcom (India) TV Tuner Card (SSD-TV-670)) + +pci:v00001131d00007130sv00001458sd00009006* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (GT-PS700 DVB-S tuner) + +pci:v00001131d00007130sv00001461sd0000050C* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Nagase Sangyo TransGear 3000TV) + +pci:v00001131d00007130sv00001461sd000010FF* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (AVerMedia DVD EZMaker) + +pci:v00001131d00007130sv00001461sd00002108* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (AverMedia AverTV/305) + +pci:v00001131d00007130sv00001461sd00002115* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (AverMedia AverTV Studio 305) + +pci:v00001131d00007130sv0000153Bsd00001152* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Terratec Cinergy 200 TV) + +pci:v00001131d00007130sv0000185Bsd0000C100* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Compro VideoMate TV PVR/FM) + +pci:v00001131d00007130sv0000185Bsd0000C901* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Videomate DVB-T200) + +pci:v00001131d00007130sv00005168sd00000138* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (LifeView FlyVIDEO2000) + +pci:v00001131d00007130sv00005ACEsd00005010* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 501) + +pci:v00001131d00007130sv00005ACEsd00005050* + ID_MODEL_FROM_DATABASE=SAA7130 Video Broadcast Decoder (Behold TV 505 FM) + +pci:v00001131d00007133* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder + +pci:v00001131d00007133sv00000000sd00004091* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Beholder BeholdTV 409 FM) + +pci:v00001131d00007133sv00000000sd00005071* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 507 RDS) + +pci:v00001131d00007133sv00000000sd0000507B* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 507 RDS) + +pci:v00001131d00007133sv00000000sd00005201* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV Columbus) + +pci:v00001131d00007133sv00000070sd00006701* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (WinTV HVR-1110) + +pci:v00001131d00007133sv00001019sd00004CB5* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)) + +pci:v00001131d00007133sv00001043sd00000210* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (FlyTV mini Asus Digimatrix) + +pci:v00001131d00007133sv00001043sd00004843* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (ASUS TV-FM 7133) + +pci:v00001131d00007133sv00001043sd00004845* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (TV-FM 7135) + +pci:v00001131d00007133sv00001043sd00004862* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (P7131 Dual) + +pci:v00001131d00007133sv00001043sd00004876* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (My Cinema-P7131 Hybrid) + +pci:v00001131d00007133sv00001131sd00000000* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (SAA713x-based TV tuner card) + +pci:v00001131d00007133sv00001131sd00002001* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Proteus Pro [philips reference design]) + +pci:v00001131d00007133sv00001131sd00002018* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Tiger reference design) + +pci:v00001131d00007133sv00001131sd00004EE9* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (MonsterTV Mobile) + +pci:v00001131d00007133sv00001131sd00007133* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Pinnacle PCTV 301i) + +pci:v00001131d00007133sv000011BDsd0000002B* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (PCTV Stereo) + +pci:v00001131d00007133sv000011BDsd0000002E* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (PCTV 110i (saa7133)) + +pci:v00001131d00007133sv000012ABsd00000800* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (PURPLE TV) + +pci:v00001131d00007133sv000013C2sd00002804* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Technotrend Budget T-3000 Hybrid) + +pci:v00001131d00007133sv00001421sd00000335* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Instant TV DVB-T Cardbus) + +pci:v00001131d00007133sv00001421sd00001370* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Instant TV (saa7135)) + +pci:v00001131d00007133sv00001435sd00007330* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (VFG7330) + +pci:v00001131d00007133sv00001435sd00007350* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (VFG7350) + +pci:v00001131d00007133sv00001458sd00009001* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GC-PTV-TAF Hybrid TV card) + +pci:v00001131d00007133sv00001458sd00009002* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GT-PTV-TAF-RH DVB-T/Analog TV/FM tuner) + +pci:v00001131d00007133sv00001458sd00009003* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GT-PTV-AF-RH Analog TV/FM tuner) + +pci:v00001131d00007133sv00001458sd00009004* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GT-P8000 DVB-T/Analog TV/FM tuner) + +pci:v00001131d00007133sv00001458sd00009005* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GT-P6000 Analog TV/FM tuner) + +pci:v00001131d00007133sv00001458sd00009008* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (GT-P5100 Analog TV tuner) + +pci:v00001131d00007133sv00001461sd00001044* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (AVerTVHD MCE A180) + +pci:v00001131d00007133sv00001461sd00004836* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (M10D Hybrid DVBT) + +pci:v00001131d00007133sv00001461sd0000861E* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (M105 PAL/SECAM/NTSC/FM Tuner) + +pci:v00001131d00007133sv00001461sd0000A14B* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (AVerTV Studio 509) + +pci:v00001131d00007133sv00001461sd0000A836* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (M115 DVB-T, PAL/SECAM/NTSC Tuner) + +pci:v00001131d00007133sv00001461sd0000F01D* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (DVB-T Super 007) + +pci:v00001131d00007133sv00001461sd0000F31F* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Avermedia AVerTV GO 007 FM) + +pci:v00001131d00007133sv00001461sd0000F936* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Hybrid+FM PCI (rev A16D)) + +pci:v00001131d00007133sv00001462sd00006231* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (TV@nywhere Plus) + +pci:v00001131d00007133sv00001489sd00000214* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyTV Platinum FM) + +pci:v00001131d00007133sv000014C0sd00001212* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyTV Platinum Mini2) + +pci:v00001131d00007133sv0000153Bsd00001160* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Cinergy 250 PCI TV) + +pci:v00001131d00007133sv0000153Bsd00001162* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Terratec Cinergy 400 mobile) + +pci:v00001131d00007133sv000017DEsd00007256* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (PlusTV All In One PI610 card) + +pci:v00001131d00007133sv000017DEsd00007350* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (ATSC 110 Digital / Analog HDTV Tuner) + +pci:v00001131d00007133sv000017DEsd00007352* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (ATSC 115 Digital / Analog HDTV Tuner) + +pci:v00001131d00007133sv0000185Bsd0000C100* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (VideoMate TV) + +pci:v00001131d00007133sv0000185Bsd0000C900* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (VideoMate T750) + +pci:v00001131d00007133sv00005168sd00000306* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB-T DUO) + +pci:v00001131d00007133sv00005168sd00000319* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB Trio) + +pci:v00001131d00007133sv00005168sd00000502* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB-T Duo CardBus) + +pci:v00001131d00007133sv00005168sd00000520* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB Trio CardBus) + +pci:v00001131d00007133sv00005168sd00001502* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyTV CardBus) + +pci:v00001131d00007133sv00005168sd00002502* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB-T CardBus) + +pci:v00001131d00007133sv00005168sd00002520* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB-S Duo CardBus) + +pci:v00001131d00007133sv00005168sd00003502* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB-T Hybrid CardBus) + +pci:v00001131d00007133sv00005168sd00003520* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (LifeView FlyDVB Trio N CardBus) + +pci:v00001131d00007133sv00005ACEsd00005030* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 503 FM) + +pci:v00001131d00007133sv00005ACEsd00005090* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 509 FM) + +pci:v00001131d00007133sv00005ACEsd00006090* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 609 FM) + +pci:v00001131d00007133sv00005ACEsd00006091* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 609 FM) + +pci:v00001131d00007133sv00005ACEsd00006092* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 609 RDS) + +pci:v00001131d00007133sv00005ACEsd00006093* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV 609 RDS) + +pci:v00001131d00007133sv00005ACEsd00006190* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV M6) + +pci:v00001131d00007133sv00005ACEsd00006191* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV M63) + +pci:v00001131d00007133sv00005ACEsd00006193* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV M6 Extra) + +pci:v00001131d00007133sv00005ACEsd00006290* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV H6) + +pci:v00001131d00007133sv00005ACEsd00007090* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV A7) + +pci:v00001131d00007133sv00005ACEsd00007150* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV H75) + +pci:v00001131d00007133sv00005ACEsd00007151* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV H75) + +pci:v00001131d00007133sv00005ACEsd00007190* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV H7) + +pci:v00001131d00007133sv00005ACEsd00007191* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV H7) + +pci:v00001131d00007133sv00005ACEsd00007290* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV T7) + +pci:v00001131d00007133sv00005ACEsd00007591* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV X7) + +pci:v00001131d00007133sv00005ACEsd00007595* + ID_MODEL_FROM_DATABASE=SAA7131/SAA7133/SAA7135 Video Broadcast Decoder (Behold TV X7) + +pci:v00001131d00007134* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder + +pci:v00001131d00007134sv00000000sd00004036* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 403) + +pci:v00001131d00007134sv00000000sd00004037* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 403 FM) + +pci:v00001131d00007134sv00000000sd00004071* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 407 FM) + +pci:v00001131d00007134sv00001019sd00004CB4* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM)) + +pci:v00001131d00007134sv00001043sd00000210* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Digimatrix TV) + +pci:v00001131d00007134sv00001043sd00004840* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (ASUS TV-FM 7134) + +pci:v00001131d00007134sv00001043sd00004842* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (TV-FM 7134) + +pci:v00001131d00007134sv00001131sd00000000* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (SAA713x-based TV tuner card) + +pci:v00001131d00007134sv00001131sd00002004* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (EUROPA V3 reference design) + +pci:v00001131d00007134sv00001131sd00004E85* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (SKNet Monster TV) + +pci:v00001131d00007134sv00001131sd00006752* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (EMPRESS) + +pci:v00001131d00007134sv000011BDsd0000002B* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (PCTV Stereo) + +pci:v00001131d00007134sv000011BDsd0000002D* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (PCTV 300i DVB-T + PAL) + +pci:v00001131d00007134sv00001461sd00002C00* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (AverTV Hybrid+FM PCI) + +pci:v00001131d00007134sv00001461sd00009715* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (AVerTV Studio 307) + +pci:v00001131d00007134sv00001461sd0000A70A* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Avermedia AVerTV 307) + +pci:v00001131d00007134sv00001461sd0000A70B* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (AverMedia M156 / Medion 2819) + +pci:v00001131d00007134sv00001461sd0000D6EE* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Cardbus TV/Radio (E500)) + +pci:v00001131d00007134sv00001471sd0000B7E9* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (AVerTV Cardbus plus) + +pci:v00001131d00007134sv0000153Bsd00001142* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Terratec Cinergy 400 TV) + +pci:v00001131d00007134sv0000153Bsd00001143* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Terratec Cinergy 600 TV) + +pci:v00001131d00007134sv0000153Bsd00001158* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Terratec Cinergy 600 TV MK3) + +pci:v00001131d00007134sv00001540sd00009524* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (ProVideo PV952) + +pci:v00001131d00007134sv000016BEsd00000003* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Medion 7134) + +pci:v00001131d00007134sv0000185Bsd0000C200* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Compro VideoMate Gold+ Pal) + +pci:v00001131d00007134sv0000185Bsd0000C900* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Videomate DVB-T300) + +pci:v00001131d00007134sv00001894sd0000A006* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (KNC One TV-Station DVR) + +pci:v00001131d00007134sv00001894sd0000FE01* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (KNC One TV-Station RDS / Typhoon TV Tuner RDS) + +pci:v00001131d00007134sv00005168sd00000138* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (FLY TV PRIME 34FM) + +pci:v00001131d00007134sv00005168sd00000300* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (FlyDVB-S) + +pci:v00001131d00007134sv00005ACEsd00005070* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 507 FM) + +pci:v00001131d00007134sv00005ACEsd00006070* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 607 FM) + +pci:v00001131d00007134sv00005ACEsd00006071* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 607 FM) + +pci:v00001131d00007134sv00005ACEsd00006072* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 607 RDS) + +pci:v00001131d00007134sv00005ACEsd00006073* + ID_MODEL_FROM_DATABASE=SAA7134/SAA7135HL Video Broadcast Decoder (Behold TV 607 RDS) + +pci:v00001131d00007145* + ID_MODEL_FROM_DATABASE=SAA7145 + +pci:v00001131d00007146* + ID_MODEL_FROM_DATABASE=SAA7146 + +pci:v00001131d00007146sv0000110Asd00000000* + ID_MODEL_FROM_DATABASE=SAA7146 (Fujitsu/Siemens DVB-C card rev1.5) + +pci:v00001131d00007146sv0000110Asd0000FFFF* + ID_MODEL_FROM_DATABASE=SAA7146 (Fujitsu/Siemens DVB-C card rev1.5) + +pci:v00001131d00007146sv00001124sd00002581* + ID_MODEL_FROM_DATABASE=SAA7146 (Leutron Vision PicPort) + +pci:v00001131d00007146sv00001131sd00004F56* + ID_MODEL_FROM_DATABASE=SAA7146 (KNC1 DVB-S Budget) + +pci:v00001131d00007146sv00001131sd00004F60* + ID_MODEL_FROM_DATABASE=SAA7146 (Fujitsu-Siemens Activy DVB-S Budget Rev AL) + +pci:v00001131d00007146sv00001131sd00004F61* + ID_MODEL_FROM_DATABASE=SAA7146 (Activy DVB-S Budget Rev GR) + +pci:v00001131d00007146sv00001131sd00005F61* + ID_MODEL_FROM_DATABASE=SAA7146 (Activy DVB-T Budget) + +pci:v00001131d00007146sv0000114Bsd00002003* + ID_MODEL_FROM_DATABASE=SAA7146 (DVRaptor Video Edit/Capture Card) + +pci:v00001131d00007146sv000011BDsd00000006* + ID_MODEL_FROM_DATABASE=SAA7146 (DV500 Overlay) + +pci:v00001131d00007146sv000011BDsd0000000A* + ID_MODEL_FROM_DATABASE=SAA7146 (DV500 Overlay) + +pci:v00001131d00007146sv000011BDsd0000000F* + ID_MODEL_FROM_DATABASE=SAA7146 (DV500 Overlay) + +pci:v00001131d00007146sv000013C2sd00000000* + ID_MODEL_FROM_DATABASE=SAA7146 (Siemens/Technotrend/Hauppauge DVB card rev1.3 or rev1.5) + +pci:v00001131d00007146sv000013C2sd00000001* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev1.3 or rev1.6) + +pci:v00001131d00007146sv000013C2sd00000002* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev2.1) + +pci:v00001131d00007146sv000013C2sd00000003* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev2.1) + +pci:v00001131d00007146sv000013C2sd00000004* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev2.1) + +pci:v00001131d00007146sv000013C2sd00000006* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev1.3 or rev1.6) + +pci:v00001131d00007146sv000013C2sd00000008* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB-T) + +pci:v00001131d00007146sv000013C2sd0000000A* + ID_MODEL_FROM_DATABASE=SAA7146 (Octal/Technotrend DVB-C for iTV) + +pci:v00001131d00007146sv000013C2sd0000000E* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev2.3) + +pci:v00001131d00007146sv000013C2sd00001003* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-S DVB card) + +pci:v00001131d00007146sv000013C2sd00001004* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-C DVB card) + +pci:v00001131d00007146sv000013C2sd00001005* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-T DVB card) + +pci:v00001131d00007146sv000013C2sd0000100C* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-CI DVB card) + +pci:v00001131d00007146sv000013C2sd0000100F* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-CI DVB card) + +pci:v00001131d00007146sv000013C2sd00001010* + ID_MODEL_FROM_DATABASE=SAA7146 (DVB C-1500) + +pci:v00001131d00007146sv000013C2sd00001011* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend-Budget/Hauppauge WinTV-NOVA-T DVB card) + +pci:v00001131d00007146sv000013C2sd00001012* + ID_MODEL_FROM_DATABASE=SAA7146 (DVB T-1500) + +pci:v00001131d00007146sv000013C2sd00001013* + ID_MODEL_FROM_DATABASE=SAA7146 (SATELCO Multimedia DVB) + +pci:v00001131d00007146sv000013C2sd00001016* + ID_MODEL_FROM_DATABASE=SAA7146 (WinTV-NOVA-SE DVB card) + +pci:v00001131d00007146sv000013C2sd00001018* + ID_MODEL_FROM_DATABASE=SAA7146 (DVB S-1401) + +pci:v00001131d00007146sv000013C2sd00001019* + ID_MODEL_FROM_DATABASE=SAA7146 (S2-3200) + +pci:v00001131d00007146sv000013C2sd00001102* + ID_MODEL_FROM_DATABASE=SAA7146 (Technotrend/Hauppauge DVB card rev2.1) + +pci:v00001131d00007146sv0000153Bsd00001155* + ID_MODEL_FROM_DATABASE=SAA7146 (Cinergy 1200 DVB-S) + +pci:v00001131d00007146sv0000153Bsd00001156* + ID_MODEL_FROM_DATABASE=SAA7146 (Cinergy 1200 DVB-C) + +pci:v00001131d00007146sv0000153Bsd00001157* + ID_MODEL_FROM_DATABASE=SAA7146 (Cinergy 1200 DVB-T) + +pci:v00001131d00007146sv0000153Bsd00001176* + ID_MODEL_FROM_DATABASE=SAA7146 (Cinergy 1200 DVB-C (MK3)) + +pci:v00001131d00007146sv00001894sd00000020* + ID_MODEL_FROM_DATABASE=SAA7146 (KNC One DVB-C V1.0) + +pci:v00001131d00007146sv00001894sd00000023* + ID_MODEL_FROM_DATABASE=SAA7146 (TVStation DVB-C plus) + +pci:v00001131d00007146sv00001894sd00000054* + ID_MODEL_FROM_DATABASE=SAA7146 (TV-Station DVB-S) + +pci:v00001131d00007160* + ID_MODEL_FROM_DATABASE=SAA7160 + +pci:v00001131d00007160sv00001458sd00009009* + ID_MODEL_FROM_DATABASE=SAA7160 (E8000 DVB-T/Analog TV/FM tuner) + +pci:v00001131d00007160sv00001461sd00001455* + ID_MODEL_FROM_DATABASE=SAA7160 (AVerTV Hybrid Speedy PCI-E (H788)) + +pci:v00001131d00007162* + ID_MODEL_FROM_DATABASE=SAA7162 + +pci:v00001131d00007162sv000011BDsd00000101* + ID_MODEL_FROM_DATABASE=SAA7162 (Pinnacle PCTV 7010iX TV Card) + +pci:v00001131d00007164* + ID_MODEL_FROM_DATABASE=SAA7164 + +pci:v00001131d00007164sv00000070sd00008800* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008810* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008851* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008853* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008880* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008891* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd000088A0* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd000088A1* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2250) + +pci:v00001131d00007164sv00000070sd00008900* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008901* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008940* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200 (submodel 89619)) + +pci:v00001131d00007164sv00000070sd00008951* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008953* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008980* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008991* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd00008993* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd000089A0* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd000089A1* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2200) + +pci:v00001131d00007164sv00000070sd0000F120* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2205) + +pci:v00001131d00007164sv00000070sd0000F123* + ID_MODEL_FROM_DATABASE=SAA7164 (WinTV HVR-2215) + +pci:v00001131d00007231* + ID_MODEL_FROM_DATABASE=SAA7231 + +pci:v00001131d00007231sv00005ACEsd00008000* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV H8) + +pci:v00001131d00007231sv00005ACEsd00008001* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV H8) + +pci:v00001131d00007231sv00005ACEsd00008050* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV H85) + +pci:v00001131d00007231sv00005ACEsd00008051* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV H85) + +pci:v00001131d00007231sv00005ACEsd00008100* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV A8) + +pci:v00001131d00007231sv00005ACEsd00008101* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV A8) + +pci:v00001131d00007231sv00005ACEsd00008150* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV A85) + +pci:v00001131d00007231sv00005ACEsd00008151* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV A85) + +pci:v00001131d00007231sv00005ACEsd00008201* + ID_MODEL_FROM_DATABASE=SAA7231 (Behold TV T8) + +pci:v00001131d00009730* + ID_MODEL_FROM_DATABASE=SAA9730 Integrated Multimedia and Peripheral Controller + +pci:v00001131d00009730sv00001131sd00000000* + ID_MODEL_FROM_DATABASE=SAA9730 Integrated Multimedia and Peripheral Controller (Integrated Multimedia and Peripheral Controller) + +pci:v00001132* + ID_VENDOR_FROM_DATABASE=Mitel Corp. + +pci:v00001133* + ID_VENDOR_FROM_DATABASE=Dialogic Corporation + +pci:v00001133d00007701* + ID_MODEL_FROM_DATABASE=Eiconcard C90 + +pci:v00001133d00007711* + ID_MODEL_FROM_DATABASE=Eiconcard C91 + +pci:v00001133d00007901* + ID_MODEL_FROM_DATABASE=EiconCard S90 + +pci:v00001133d00007902* + ID_MODEL_FROM_DATABASE=EiconCard S90 + +pci:v00001133d00007911* + ID_MODEL_FROM_DATABASE=EiconCard S91 + +pci:v00001133d00007912* + ID_MODEL_FROM_DATABASE=EiconCard S91 + +pci:v00001133d00007921* + ID_MODEL_FROM_DATABASE=Eiconcard S92 + +pci:v00001133d00007941* + ID_MODEL_FROM_DATABASE=EiconCard S94 + +pci:v00001133d00007942* + ID_MODEL_FROM_DATABASE=EiconCard S94 + +pci:v00001133d00007943* + ID_MODEL_FROM_DATABASE=EiconCard S94 + +pci:v00001133d00007944* + ID_MODEL_FROM_DATABASE=EiconCard S94 + +pci:v00001133d00007945* + ID_MODEL_FROM_DATABASE=Eiconcard S94 + +pci:v00001133d00007948* + ID_MODEL_FROM_DATABASE=Eiconcard S94 64bit/66MHz + +pci:v00001133d00009711* + ID_MODEL_FROM_DATABASE=Eiconcard S91 V2 + +pci:v00001133d00009911* + ID_MODEL_FROM_DATABASE=Eiconcard S91 V2 + +pci:v00001133d00009941* + ID_MODEL_FROM_DATABASE=Eiconcard S94 V2 + +pci:v00001133d00009A41* + ID_MODEL_FROM_DATABASE=Eiconcard S94 PCIe + +pci:v00001133d0000B921* + ID_MODEL_FROM_DATABASE=EiconCard P92 + +pci:v00001133d0000B922* + ID_MODEL_FROM_DATABASE=EiconCard P92 + +pci:v00001133d0000B923* + ID_MODEL_FROM_DATABASE=EiconCard P92 + +pci:v00001133d0000E001* + ID_MODEL_FROM_DATABASE=Diva Pro 2.0 S/T + +pci:v00001133d0000E002* + ID_MODEL_FROM_DATABASE=Diva 2.0 S/T PCI + +pci:v00001133d0000E003* + ID_MODEL_FROM_DATABASE=Diva Pro 2.0 U + +pci:v00001133d0000E004* + ID_MODEL_FROM_DATABASE=Diva 2.0 U PCI + +pci:v00001133d0000E005* + ID_MODEL_FROM_DATABASE=Diva 2.01 S/T PCI + +pci:v00001133d0000E006* + ID_MODEL_FROM_DATABASE=Diva CT S/T PCI + +pci:v00001133d0000E007* + ID_MODEL_FROM_DATABASE=Diva CT U PCI + +pci:v00001133d0000E008* + ID_MODEL_FROM_DATABASE=Diva CT Lite S/T PCI + +pci:v00001133d0000E009* + ID_MODEL_FROM_DATABASE=Diva CT Lite U PCI + +pci:v00001133d0000E00A* + ID_MODEL_FROM_DATABASE=Diva ISDN+V.90 PCI + +pci:v00001133d0000E00B* + ID_MODEL_FROM_DATABASE=Diva ISDN PCI 2.02 + +pci:v00001133d0000E00C* + ID_MODEL_FROM_DATABASE=Diva 2.02 PCI U + +pci:v00001133d0000E00D* + ID_MODEL_FROM_DATABASE=Diva Pro 3.0 PCI + +pci:v00001133d0000E00E* + ID_MODEL_FROM_DATABASE=Diva ISDN+CT S/T PCI Rev 2 + +pci:v00001133d0000E010* + ID_MODEL_FROM_DATABASE=Diva Server BRI-2M PCI + +pci:v00001133d0000E010sv0000110Asd00000021* + ID_MODEL_FROM_DATABASE=Diva Server BRI-2M PCI (Fujitsu Siemens ISDN S0) + +pci:v00001133d0000E011* + ID_MODEL_FROM_DATABASE=Diva Server BRI S/T Rev 2 + +pci:v00001133d0000E012* + ID_MODEL_FROM_DATABASE=Diva Server 4BRI-8M PCI + +pci:v00001133d0000E013* + ID_MODEL_FROM_DATABASE=4BRI + +pci:v00001133d0000E013sv00001133sd00001300* + ID_MODEL_FROM_DATABASE=4BRI (Diva V-4BRI-8 PCI v2) + +pci:v00001133d0000E013sv00001133sd0000E013* + ID_MODEL_FROM_DATABASE=4BRI (Diva 4BRI-8 PCI v2) + +pci:v00001133d0000E014* + ID_MODEL_FROM_DATABASE=Diva Server PRI-30M PCI + +pci:v00001133d0000E015* + ID_MODEL_FROM_DATABASE=Diva PRI PCI v2 + +pci:v00001133d0000E016* + ID_MODEL_FROM_DATABASE=Diva Server Voice 4BRI PCI + +pci:v00001133d0000E017* + ID_MODEL_FROM_DATABASE=Diva Server Voice 4BRI Rev 2 + +pci:v00001133d0000E017sv00001133sd0000E017* + ID_MODEL_FROM_DATABASE=Diva Server Voice 4BRI Rev 2 (Diva Server Voice 4BRI-8M 2.0 PCI) + +pci:v00001133d0000E018* + ID_MODEL_FROM_DATABASE=BRI + +pci:v00001133d0000E018sv00001133sd00001800* + ID_MODEL_FROM_DATABASE=BRI (Diva V-BRI-2 PCI v2) + +pci:v00001133d0000E018sv00001133sd0000E018* + ID_MODEL_FROM_DATABASE=BRI (Diva BRI-2 PCI v2) + +pci:v00001133d0000E019* + ID_MODEL_FROM_DATABASE=Diva Server Voice PRI Rev 2 + +pci:v00001133d0000E019sv00001133sd0000E019* + ID_MODEL_FROM_DATABASE=Diva Server Voice PRI Rev 2 (Diva Server Voice PRI 2.0 PCI) + +pci:v00001133d0000E01A* + ID_MODEL_FROM_DATABASE=Diva BRI-2FX PCI v2 + +pci:v00001133d0000E01B* + ID_MODEL_FROM_DATABASE=Diva Server Voice BRI-2M 2.0 PCI + +pci:v00001133d0000E01Bsv00001133sd0000E01B* + ID_MODEL_FROM_DATABASE=Diva Server Voice BRI-2M 2.0 PCI + +pci:v00001133d0000E01C* + ID_MODEL_FROM_DATABASE=PRI + +pci:v00001133d0000E01Csv00001133sd00001C01* + ID_MODEL_FROM_DATABASE=PRI (Diva PRI/E1/T1-8 PCI v3) + +pci:v00001133d0000E01Csv00001133sd00001C02* + ID_MODEL_FROM_DATABASE=PRI (Diva PRI/T1-24 PCI(e) v3) + +pci:v00001133d0000E01Csv00001133sd00001C03* + ID_MODEL_FROM_DATABASE=PRI (Diva PRI/E1-30 PCI(e) v3) + +pci:v00001133d0000E01Csv00001133sd00001C04* + ID_MODEL_FROM_DATABASE=PRI (Diva PRI/E1/T1-CTI PCI(e) v3) + +pci:v00001133d0000E01Csv00001133sd00001C05* + ID_MODEL_FROM_DATABASE=PRI (Diva V-PRI/T1-24 PCI(e) v3) + +pci:v00001133d0000E01Csv00001133sd00001C06* + ID_MODEL_FROM_DATABASE=PRI (Diva V-PRI/E1-30 PCI(e) v3) + +pci:v00001133d0000E01Csv00001133sd00001C07* + ID_MODEL_FROM_DATABASE=PRI (Diva Server PRI/E1/T1-8 Cornet NQ) + +pci:v00001133d0000E01Csv00001133sd00001C08* + ID_MODEL_FROM_DATABASE=PRI (Diva Server PRI/T1-24 Cornet NQ) + +pci:v00001133d0000E01Csv00001133sd00001C09* + ID_MODEL_FROM_DATABASE=PRI (Diva Server PRI/E1-30 Cornet NQ) + +pci:v00001133d0000E01Csv00001133sd00001C0A* + ID_MODEL_FROM_DATABASE=PRI (Diva Server PRI/E1/T1 Cornet NQ) + +pci:v00001133d0000E01Csv00001133sd00001C0B* + ID_MODEL_FROM_DATABASE=PRI (Diva Server V-PRI/T1-24 Cornet NQ) + +pci:v00001133d0000E01Csv00001133sd00001C0C* + ID_MODEL_FROM_DATABASE=PRI (Diva Server V-PRI/E1-30 Cornet NQ) + +pci:v00001133d0000E01E* + ID_MODEL_FROM_DATABASE=2PRI + +pci:v00001133d0000E01Esv00001133sd00001E01* + ID_MODEL_FROM_DATABASE=2PRI (Diva 2PRI/E1/T1-60 PCI v1) + +pci:v00001133d0000E01Esv00001133sd0000E01E* + ID_MODEL_FROM_DATABASE=2PRI (Diva V-2PRI/E1/T1-60 PCI v1) + +pci:v00001133d0000E020* + ID_MODEL_FROM_DATABASE=4PRI + +pci:v00001133d0000E020sv00001133sd00002001* + ID_MODEL_FROM_DATABASE=4PRI (Diva 4PRI/E1/T1-120 PCI v1) + +pci:v00001133d0000E020sv00001133sd0000E020* + ID_MODEL_FROM_DATABASE=4PRI (Diva V-4PRI/E1/T1-120 PCI v1) + +pci:v00001133d0000E022* + ID_MODEL_FROM_DATABASE=Analog-2 + +pci:v00001133d0000E022sv00001133sd00002200* + ID_MODEL_FROM_DATABASE=Analog-2 (Diva V-Analog-2 PCI v1) + +pci:v00001133d0000E022sv00001133sd0000E022* + ID_MODEL_FROM_DATABASE=Analog-2 (Diva Analog-2 PCI v1) + +pci:v00001133d0000E024* + ID_MODEL_FROM_DATABASE=Analog-4 + +pci:v00001133d0000E024sv00001133sd00002400* + ID_MODEL_FROM_DATABASE=Analog-4 (Diva V-Analog-4 PCI v1) + +pci:v00001133d0000E024sv00001133sd0000E024* + ID_MODEL_FROM_DATABASE=Analog-4 (Diva Analog-4 PCI v1) + +pci:v00001133d0000E028* + ID_MODEL_FROM_DATABASE=Analog-8 + +pci:v00001133d0000E028sv00001133sd00002800* + ID_MODEL_FROM_DATABASE=Analog-8 (Diva V-Analog-8 PCI v1) + +pci:v00001133d0000E028sv00001133sd0000E028* + ID_MODEL_FROM_DATABASE=Analog-8 (Diva Analog-8 PCI v1) + +pci:v00001133d0000E02A* + ID_MODEL_FROM_DATABASE=Diva IPM-300 PCI v1 + +pci:v00001133d0000E02C* + ID_MODEL_FROM_DATABASE=Diva IPM-600 PCI v1 + +pci:v00001133d0000E02E* + ID_MODEL_FROM_DATABASE=4BRI + +pci:v00001133d0000E02Esv00001133sd00002E01* + ID_MODEL_FROM_DATABASE=4BRI (Diva V-4BRI-8 PCIe v2) + +pci:v00001133d0000E02Esv00001133sd0000E02E* + ID_MODEL_FROM_DATABASE=4BRI (Diva 4BRI-8 PCIe v2) + +pci:v00001133d0000E032* + ID_MODEL_FROM_DATABASE=BRI + +pci:v00001133d0000E032sv00001133sd00003201* + ID_MODEL_FROM_DATABASE=BRI (Diva V-BRI-2 PCIe v2) + +pci:v00001133d0000E032sv00001133sd0000E032* + ID_MODEL_FROM_DATABASE=BRI (Diva BRI-2 PCIe v2) + +pci:v00001133d0000E034* + ID_MODEL_FROM_DATABASE=Diva BRI-CTI PCI v2 + +pci:v00001134* + ID_VENDOR_FROM_DATABASE=Mercury Computer Systems + +pci:v00001134d00000001* + ID_MODEL_FROM_DATABASE=Raceway Bridge + +pci:v00001134d00000002* + ID_MODEL_FROM_DATABASE=Dual PCI to RapidIO Bridge + +pci:v00001134d0000000B* + ID_MODEL_FROM_DATABASE=POET Serial RapidIO Bridge + +pci:v00001134d0000000D* + ID_MODEL_FROM_DATABASE=POET PSDMS Device + +pci:v00001135* + ID_VENDOR_FROM_DATABASE=Fuji Xerox Co Ltd + +pci:v00001135d00000001* + ID_MODEL_FROM_DATABASE=Printer controller + +pci:v00001136* + ID_VENDOR_FROM_DATABASE=Momentum Data Systems + +pci:v00001136d00000002* + ID_MODEL_FROM_DATABASE=PCI-JTAG + +pci:v00001137* + ID_VENDOR_FROM_DATABASE=Cisco Systems Inc + +pci:v00001137d00000023* + ID_MODEL_FROM_DATABASE=VIC 81 PCIe Upstream Port + +pci:v00001137d00000040* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port + +pci:v00001137d00000040sv00001137sd0000004F* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1280 Dual 40Gb Mezzanine) + +pci:v00001137d00000040sv00001137sd00000084* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1240 Dual 40Gb MLOM) + +pci:v00001137d00000040sv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1225 Dual 10Gb SFP+ PCIe) + +pci:v00001137d00000040sv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1285 Dual 40Gb QSFP+ PCIe) + +pci:v00001137d00000040sv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1225T Dual 10GBaseT PCIe) + +pci:v00001137d00000040sv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC M4308 Dual 40Gb) + +pci:v00001137d00000040sv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1340 Dual 40Gb MLOM) + +pci:v00001137d00000040sv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1227 Dual 10Gb SFP+ PCIe) + +pci:v00001137d00000040sv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1380 Dual 40Gb Mezzanine) + +pci:v00001137d00000040sv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC PCIe Upstream Port (VIC 1385 Dual 40Gb PCIe) + +pci:v00001137d00000041* + ID_MODEL_FROM_DATABASE=VIC PCIe Downstream Port + +pci:v00001137d00000042* + ID_MODEL_FROM_DATABASE=VIC Management Controller + +pci:v00001137d00000042sv00001137sd00000047* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC P81E PCIe Management Controller) + +pci:v00001137d00000042sv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC 1225 PCIe Management Controller) + +pci:v00001137d00000042sv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC 1285 PCIe Management Controller) + +pci:v00001137d00000042sv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC 1225T PCIe Management Controller) + +pci:v00001137d00000042sv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC 1227 PCIe Management Controller) + +pci:v00001137d00000042sv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC Management Controller (VIC 1385 PCIe Management Controller) + +pci:v00001137d00000043* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC + +pci:v00001137d00000043sv00001137sd00000047* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC P81E PCIe Ethernet NIC) + +pci:v00001137d00000043sv00001137sd00000048* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC M81KR Mezzanine Ethernet NIC) + +pci:v00001137d00000043sv00001137sd0000004F* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1280 Mezzanine Ethernet NIC) + +pci:v00001137d00000043sv00001137sd00000084* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1240 MLOM Ethernet NIC) + +pci:v00001137d00000043sv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1225 PCIe Ethernet NIC) + +pci:v00001137d00000043sv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1285 PCIe Ethernet NIC) + +pci:v00001137d00000043sv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1225T PCIe Ethernet NIC) + +pci:v00001137d00000043sv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC M4308 Ethernet NIC) + +pci:v00001137d00000043sv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1340 MLOM Ethernet NIC) + +pci:v00001137d00000043sv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1227 PCIe Ethernet NIC) + +pci:v00001137d00000043sv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1380 Mezzanine Ethernet NIC) + +pci:v00001137d00000043sv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC (VIC 1385 PCIe Ethernet NIC) + +pci:v00001137d00000044* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic + +pci:v00001137d00000044sv00001137sd00000047* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC P81E PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd00000048* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC M81KR Mezzanine Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd0000004F* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1280 Mezzanine Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd00000084* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1240 MLOM Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1225 PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1285 PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1225T PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC M4308 Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1340 MLOM Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1227 PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1380 Mezzanine Ethernet NIC Dynamic) + +pci:v00001137d00000044sv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC Ethernet NIC Dynamic (VIC 1385 PCIe Ethernet NIC Dynamic) + +pci:v00001137d00000045* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA + +pci:v00001137d00000045sv00001137sd00000047* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC P81E PCIe FCoE HBA) + +pci:v00001137d00000045sv00001137sd00000048* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC M81KR Mezzanine FCoE HBA) + +pci:v00001137d00000045sv00001137sd0000004F* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1280 Mezzanine FCoE HBA) + +pci:v00001137d00000045sv00001137sd00000084* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1240 MLOM FCoE HBA) + +pci:v00001137d00000045sv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1225 PCIe FCoE HBA) + +pci:v00001137d00000045sv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1285 PCIe FCoE HBA) + +pci:v00001137d00000045sv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1225T PCIe FCoE HBA) + +pci:v00001137d00000045sv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC M4308 FCoE HBA) + +pci:v00001137d00000045sv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1340 MLOM FCoE HBA) + +pci:v00001137d00000045sv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1227 PCIe FCoE HBA) + +pci:v00001137d00000045sv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1380 Mezzanine FCoE HBA) + +pci:v00001137d00000045sv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC FCoE HBA (VIC 1385 PCIe FCoE HBA) + +pci:v00001137d00000046* + ID_MODEL_FROM_DATABASE=VIC SCSI Controller + +pci:v00001137d00000046sv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC SCSI Controller (VIC M4308 SCSI Controller) + +pci:v00001137d0000004E* + ID_MODEL_FROM_DATABASE=VIC 82 PCIe Upstream Port + +pci:v00001137d00000071* + ID_MODEL_FROM_DATABASE=VIC SR-IOV VF + +pci:v00001137d0000007A* + ID_MODEL_FROM_DATABASE=VIC 1300 PCIe Upstream Port + +pci:v00001137d0000007Asv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC 1300 PCIe Upstream Port (VIC M4308 Dual 40Gb) + +pci:v00001137d0000007Asv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC 1300 PCIe Upstream Port (VIC 1340 Dual 40Gb MLOM) + +pci:v00001137d0000007Asv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC 1300 PCIe Upstream Port (VIC 1380 Dual 40Gb Mezzanine) + +pci:v00001137d0000007Asv00001137sd0000014D* + ID_MODEL_FROM_DATABASE=VIC 1300 PCIe Upstream Port (VIC 1385 Dual 40Gb PCIe) + +pci:v00001137d000000CF* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC + +pci:v00001137d000000CFsv00001137sd0000004F* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1280 Mezzanine Userspace NIC) + +pci:v00001137d000000CFsv00001137sd00000084* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1240 MLOM Userspace NIC) + +pci:v00001137d000000CFsv00001137sd00000085* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1225 PCIe Userspace NIC) + +pci:v00001137d000000CFsv00001137sd000000CD* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1285 PCIe Userspace NIC) + +pci:v00001137d000000CFsv00001137sd000000CE* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1225T PCIe Userspace NIC) + +pci:v00001137d000000CFsv00001137sd0000012A* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC M4308 Userspace NIC) + +pci:v00001137d000000CFsv00001137sd0000012C* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1340 MLOM Userspace NIC) + +pci:v00001137d000000CFsv00001137sd0000012E* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1227 PCIe Userspace NIC) + +pci:v00001137d000000CFsv00001137sd00000137* + ID_MODEL_FROM_DATABASE=VIC Userspace NIC (VIC 1380 Mezzanine Userspace NIC) + +pci:v00001138* + ID_VENDOR_FROM_DATABASE=Ziatech Corporation + +pci:v00001138d00008905* + ID_MODEL_FROM_DATABASE=8905 [STD 32 Bridge] + +pci:v00001139* + ID_VENDOR_FROM_DATABASE=Dynamic Pictures, Inc + +pci:v00001139d00000001* + ID_MODEL_FROM_DATABASE=VGA Compatible 3D Graphics + +pci:v0000113A* + ID_VENDOR_FROM_DATABASE=FWB Inc + +pci:v0000113B* + ID_VENDOR_FROM_DATABASE=Network Computing Devices + +pci:v0000113C* + ID_VENDOR_FROM_DATABASE=Cyclone Microsystems, Inc. + +pci:v0000113Cd00000000* + ID_MODEL_FROM_DATABASE=PCI-9060 i960 Bridge + +pci:v0000113Cd00000001* + ID_MODEL_FROM_DATABASE=PCI-SDK [PCI i960 Evaluation Platform] + +pci:v0000113Cd00000911* + ID_MODEL_FROM_DATABASE=PCI-911 [i960Jx-based Intelligent I/O Controller] + +pci:v0000113Cd00000912* + ID_MODEL_FROM_DATABASE=PCI-912 [i960CF-based Intelligent I/O Controller] + +pci:v0000113Cd00000913* + ID_MODEL_FROM_DATABASE=PCI-913 + +pci:v0000113Cd00000914* + ID_MODEL_FROM_DATABASE=PCI-914 [I/O Controller w/ secondary PCI bus] + +pci:v0000113D* + ID_VENDOR_FROM_DATABASE=Leading Edge Products Inc + +pci:v0000113E* + ID_VENDOR_FROM_DATABASE=Sanyo Electric Co - Computer Engineering Dept + +pci:v0000113F* + ID_VENDOR_FROM_DATABASE=Equinox Systems, Inc. + +pci:v0000113Fd00000808* + ID_MODEL_FROM_DATABASE=SST-64P Adapter + +pci:v0000113Fd00001010* + ID_MODEL_FROM_DATABASE=SST-128P Adapter + +pci:v0000113Fd000080C0* + ID_MODEL_FROM_DATABASE=SST-16P DB Adapter + +pci:v0000113Fd000080C4* + ID_MODEL_FROM_DATABASE=SST-16P RJ Adapter + +pci:v0000113Fd000080C8* + ID_MODEL_FROM_DATABASE=SST-16P Adapter + +pci:v0000113Fd00008888* + ID_MODEL_FROM_DATABASE=SST-4P Adapter + +pci:v0000113Fd00009090* + ID_MODEL_FROM_DATABASE=SST-8P Adapter + +pci:v00001140* + ID_VENDOR_FROM_DATABASE=Intervoice Inc + +pci:v00001141* + ID_VENDOR_FROM_DATABASE=Crest Microsystem Inc + +pci:v00001142* + ID_VENDOR_FROM_DATABASE=Alliance Semiconductor Corporation + +pci:v00001142d00003210* + ID_MODEL_FROM_DATABASE=AP6410 + +pci:v00001142d00006422* + ID_MODEL_FROM_DATABASE=ProVideo 6422 + +pci:v00001142d00006424* + ID_MODEL_FROM_DATABASE=ProVideo 6424 + +pci:v00001142d00006425* + ID_MODEL_FROM_DATABASE=ProMotion AT25 + +pci:v00001142d0000643D* + ID_MODEL_FROM_DATABASE=ProMotion AT3D + +pci:v00001143* + ID_VENDOR_FROM_DATABASE=NetPower, Inc + +pci:v00001144* + ID_VENDOR_FROM_DATABASE=Cincinnati Milacron + +pci:v00001144d00000001* + ID_MODEL_FROM_DATABASE=Noservo controller + +pci:v00001145* + ID_VENDOR_FROM_DATABASE=Workbit Corporation + +pci:v00001145d00008007* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Workbit + +pci:v00001145d0000F007* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 KME + +pci:v00001145d0000F010* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Workbit + +pci:v00001145d0000F012* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Logitec + +pci:v00001145d0000F013* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Logitec + +pci:v00001145d0000F015* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Melco + +pci:v00001145d0000F020* + ID_MODEL_FROM_DATABASE=NinjaSCSI-32 Sony PCGA-DVD51 + +pci:v00001145d0000F021* + ID_MODEL_FROM_DATABASE=NinjaPATA-32 Delkin Cardbus UDMA + +pci:v00001145d0000F024* + ID_MODEL_FROM_DATABASE=NinjaPATA-32 Delkin Cardbus UDMA + +pci:v00001145d0000F103* + ID_MODEL_FROM_DATABASE=NinjaPATA-32 Delkin Cardbus UDMA + +pci:v00001146* + ID_VENDOR_FROM_DATABASE=Force Computers + +pci:v00001147* + ID_VENDOR_FROM_DATABASE=Interface Corp + +pci:v00001148* + ID_VENDOR_FROM_DATABASE=SysKonnect + +pci:v00001148d00004000* + ID_MODEL_FROM_DATABASE=FDDI Adapter + +pci:v00001148d00004000sv00000E11sd0000B03B* + ID_MODEL_FROM_DATABASE=FDDI Adapter (Netelligent 100 FDDI DAS Fibre SC) + +pci:v00001148d00004000sv00000E11sd0000B03C* + ID_MODEL_FROM_DATABASE=FDDI Adapter (Netelligent 100 FDDI SAS Fibre SC) + +pci:v00001148d00004000sv00000E11sd0000B03D* + ID_MODEL_FROM_DATABASE=FDDI Adapter (Netelligent 100 FDDI DAS UTP) + +pci:v00001148d00004000sv00000E11sd0000B03E* + ID_MODEL_FROM_DATABASE=FDDI Adapter (Netelligent 100 FDDI SAS UTP) + +pci:v00001148d00004000sv00000E11sd0000B03F* + ID_MODEL_FROM_DATABASE=FDDI Adapter (Netelligent 100 FDDI SAS Fibre MIC) + +pci:v00001148d00004000sv00001148sd00005521* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5521 (SK-NET FDDI-UP)) + +pci:v00001148d00004000sv00001148sd00005522* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5522 (SK-NET FDDI-UP DAS)) + +pci:v00001148d00004000sv00001148sd00005541* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5541 (SK-NET FDDI-FP)) + +pci:v00001148d00004000sv00001148sd00005543* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5543 (SK-NET FDDI-LP)) + +pci:v00001148d00004000sv00001148sd00005544* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5544 (SK-NET FDDI-LP DAS)) + +pci:v00001148d00004000sv00001148sd00005821* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5821 (SK-NET FDDI-UP64)) + +pci:v00001148d00004000sv00001148sd00005822* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5822 (SK-NET FDDI-UP64 DAS)) + +pci:v00001148d00004000sv00001148sd00005841* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5841 (SK-NET FDDI-FP64)) + +pci:v00001148d00004000sv00001148sd00005843* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5843 (SK-NET FDDI-LP64)) + +pci:v00001148d00004000sv00001148sd00005844* + ID_MODEL_FROM_DATABASE=FDDI Adapter (FDDI SK-5844 (SK-NET FDDI-LP64 DAS)) + +pci:v00001148d00004200* + ID_MODEL_FROM_DATABASE=Token Ring adapter + +pci:v00001148d00004300* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) + +pci:v00001148d00004300sv00001148sd00009821* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9821 Gigabit Ethernet Server Adapter (SK-NET GE-T)) + +pci:v00001148d00004300sv00001148sd00009822* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9822 Gigabit Ethernet Server Adapter (SK-NET GE-T dual link)) + +pci:v00001148d00004300sv00001148sd00009841* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9841 Gigabit Ethernet Server Adapter (SK-NET GE-LX)) + +pci:v00001148d00004300sv00001148sd00009842* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9842 Gigabit Ethernet Server Adapter (SK-NET GE-LX dual link)) + +pci:v00001148d00004300sv00001148sd00009843* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9843 Gigabit Ethernet Server Adapter (SK-NET GE-SX)) + +pci:v00001148d00004300sv00001148sd00009844* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9844 Gigabit Ethernet Server Adapter (SK-NET GE-SX dual link)) + +pci:v00001148d00004300sv00001148sd00009861* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9861 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition)) + +pci:v00001148d00004300sv00001148sd00009862* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9862 Gigabit Ethernet Server Adapter (SK-NET GE-SX Volition dual link)) + +pci:v00001148d00004300sv00001148sd00009871* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (SK-9871 Gigabit Ethernet Server Adapter (SK-NET GE-ZX)) + +pci:v00001148d00004300sv00001148sd00009872* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) + +pci:v00001148d00004300sv00001259sd00002970* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970SX Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002971* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970LX Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002972* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970TX Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002973* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2971SX Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002974* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2971T Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002975* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970SX/2SC Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002976* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970LX/2SC Gigabit Ethernet Adapter) + +pci:v00001148d00004300sv00001259sd00002977* + ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970TX/2TX Gigabit Ethernet Adapter) + +pci:v00001148d00004320* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC + +pci:v00001148d00004320sv00001148sd00000121* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8001 Adapter) + +pci:v00001148d00004320sv00001148sd00000221* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8002 Adapter) + +pci:v00001148d00004320sv00001148sd00000321* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8003 Adapter) + +pci:v00001148d00004320sv00001148sd00000421* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8004 Adapter) + +pci:v00001148d00004320sv00001148sd00000621* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8006 Adapter) + +pci:v00001148d00004320sv00001148sd00000721* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8007 Adapter) + +pci:v00001148d00004320sv00001148sd00000821* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8008 Adapter) + +pci:v00001148d00004320sv00001148sd00000921* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8009 Adapter) + +pci:v00001148d00004320sv00001148sd00001121* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8011 Adapter) + +pci:v00001148d00004320sv00001148sd00001221* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8012 Adapter) + +pci:v00001148d00004320sv00001148sd00003221* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 V2.0 10/100/1000Base-T Adapter) + +pci:v00001148d00004320sv00001148sd00005021* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter) + +pci:v00001148d00004320sv00001148sd00005041* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter) + +pci:v00001148d00004320sv00001148sd00005043* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter) + +pci:v00001148d00004320sv00001148sd00005051* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter) + +pci:v00001148d00004320sv00001148sd00005061* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter) + +pci:v00001148d00004320sv00001148sd00005071* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter) + +pci:v00001148d00004320sv00001148sd00009521* + ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 10/100/1000Base-T Adapter) + +pci:v00001148d00004400* + ID_MODEL_FROM_DATABASE=SK-9Dxx Gigabit Ethernet Adapter + +pci:v00001148d00004500* + ID_MODEL_FROM_DATABASE=SK-9Mxx Gigabit Ethernet Adapter + +pci:v00001148d00009000* + ID_MODEL_FROM_DATABASE=SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45 + +pci:v00001148d00009843* + ID_MODEL_FROM_DATABASE=[Fujitsu] Gigabit Ethernet + +pci:v00001148d00009E00* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 + +pci:v00001148d00009E00sv00001148sd00002100* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E21 Server Adapter) + +pci:v00001148d00009E00sv00001148sd000021D0* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E21D 10/100/1000Base-T Adapter) + +pci:v00001148d00009E00sv00001148sd00002200* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E22 Server Adapter) + +pci:v00001148d00009E00sv00001148sd00008100* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E81 Server Adapter) + +pci:v00001148d00009E00sv00001148sd00008200* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E82 Server Adapter) + +pci:v00001148d00009E00sv00001148sd00009100* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E91 Server Adapter) + +pci:v00001148d00009E00sv00001148sd00009200* + ID_MODEL_FROM_DATABASE=SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45 (SK-9E92 Server Adapter) + +pci:v00001148d00009E01* + ID_MODEL_FROM_DATABASE=SK-9E21M 10/100/1000Base-T Adapter + +pci:v00001149* + ID_VENDOR_FROM_DATABASE=Win System Corporation + +pci:v0000114A* + ID_VENDOR_FROM_DATABASE=VMIC + +pci:v0000114Ad00005565* + ID_MODEL_FROM_DATABASE=GE-IP PCI5565,PMC5565 Reflective Memory Node + +pci:v0000114Ad00005579* + ID_MODEL_FROM_DATABASE=VMIPCI-5579 (Reflective Memory Card) + +pci:v0000114Ad00005587* + ID_MODEL_FROM_DATABASE=VMIPCI-5587 (Reflective Memory Card) + +pci:v0000114Ad00006504* + ID_MODEL_FROM_DATABASE=VMIC PCI 7755 FPGA + +pci:v0000114Ad00007587* + ID_MODEL_FROM_DATABASE=VMIVME-7587 + +pci:v0000114B* + ID_VENDOR_FROM_DATABASE=Canopus Co., Ltd + +pci:v0000114C* + ID_VENDOR_FROM_DATABASE=Annabooks + +pci:v0000114D* + ID_VENDOR_FROM_DATABASE=IC Corporation + +pci:v0000114E* + ID_VENDOR_FROM_DATABASE=Nikon Systems Inc + +pci:v0000114F* + ID_VENDOR_FROM_DATABASE=Digi International + +pci:v0000114Fd00000002* + ID_MODEL_FROM_DATABASE=AccelePort EPC + +pci:v0000114Fd00000003* + ID_MODEL_FROM_DATABASE=RightSwitch SE-6 + +pci:v0000114Fd00000004* + ID_MODEL_FROM_DATABASE=AccelePort Xem + +pci:v0000114Fd00000005* + ID_MODEL_FROM_DATABASE=AccelePort Xr + +pci:v0000114Fd00000006* + ID_MODEL_FROM_DATABASE=AccelePort Xr,C/X + +pci:v0000114Fd00000009* + ID_MODEL_FROM_DATABASE=AccelePort Xr/J + +pci:v0000114Fd0000000A* + ID_MODEL_FROM_DATABASE=AccelePort EPC/J + +pci:v0000114Fd0000000C* + ID_MODEL_FROM_DATABASE=DataFirePRIme T1 (1-port) + +pci:v0000114Fd0000000D* + ID_MODEL_FROM_DATABASE=SyncPort 2-Port (x.25/FR) + +pci:v0000114Fd00000011* + ID_MODEL_FROM_DATABASE=AccelePort 8r EIA-232 (IBM) + +pci:v0000114Fd00000012* + ID_MODEL_FROM_DATABASE=AccelePort 8r EIA-422 + +pci:v0000114Fd00000013* + ID_MODEL_FROM_DATABASE=AccelePort Xr + +pci:v0000114Fd00000014* + ID_MODEL_FROM_DATABASE=AccelePort 8r EIA-422 + +pci:v0000114Fd00000015* + ID_MODEL_FROM_DATABASE=AccelePort Xem + +pci:v0000114Fd00000016* + ID_MODEL_FROM_DATABASE=AccelePort EPC/X + +pci:v0000114Fd00000017* + ID_MODEL_FROM_DATABASE=AccelePort C/X + +pci:v0000114Fd0000001A* + ID_MODEL_FROM_DATABASE=DataFirePRIme E1 (1-port) + +pci:v0000114Fd0000001B* + ID_MODEL_FROM_DATABASE=AccelePort C/X (IBM) + +pci:v0000114Fd0000001C* + ID_MODEL_FROM_DATABASE=AccelePort Xr (SAIP) + +pci:v0000114Fd0000001D* + ID_MODEL_FROM_DATABASE=DataFire RAS T1/E1/PRI + +pci:v0000114Fd0000001Dsv0000114Fsd00000050* + ID_MODEL_FROM_DATABASE=DataFire RAS T1/E1/PRI (DataFire RAS E1 Adapter) + +pci:v0000114Fd0000001Dsv0000114Fsd00000051* + ID_MODEL_FROM_DATABASE=DataFire RAS T1/E1/PRI (DataFire RAS Dual E1 Adapter) + +pci:v0000114Fd0000001Dsv0000114Fsd00000052* + ID_MODEL_FROM_DATABASE=DataFire RAS T1/E1/PRI (DataFire RAS T1 Adapter) + +pci:v0000114Fd0000001Dsv0000114Fsd00000053* + ID_MODEL_FROM_DATABASE=DataFire RAS T1/E1/PRI (DataFire RAS Dual T1 Adapter) + +pci:v0000114Fd00000023* + ID_MODEL_FROM_DATABASE=AccelePort RAS + +pci:v0000114Fd00000024* + ID_MODEL_FROM_DATABASE=DataFire RAS B4 ST/U + +pci:v0000114Fd00000024sv0000114Fsd00000030* + ID_MODEL_FROM_DATABASE=DataFire RAS B4 ST/U (DataFire RAS BRI U Adapter) + +pci:v0000114Fd00000024sv0000114Fsd00000031* + ID_MODEL_FROM_DATABASE=DataFire RAS B4 ST/U (DataFire RAS BRI S/T Adapter) + +pci:v0000114Fd00000026* + ID_MODEL_FROM_DATABASE=AccelePort 4r 920 + +pci:v0000114Fd00000027* + ID_MODEL_FROM_DATABASE=AccelePort Xr 920 + +pci:v0000114Fd00000028* + ID_MODEL_FROM_DATABASE=ClassicBoard 4 + +pci:v0000114Fd00000029* + ID_MODEL_FROM_DATABASE=ClassicBoard 8 + +pci:v0000114Fd00000034* + ID_MODEL_FROM_DATABASE=AccelePort 2r 920 + +pci:v0000114Fd00000035* + ID_MODEL_FROM_DATABASE=DataFire DSP T1/E1/PRI cPCI + +pci:v0000114Fd00000040* + ID_MODEL_FROM_DATABASE=AccelePort Xp + +pci:v0000114Fd00000040sv0000114Fsd00000042* + ID_MODEL_FROM_DATABASE=AccelePort Xp (AccelePort 2p PCI) + +pci:v0000114Fd00000040sv0000114Fsd00000043* + ID_MODEL_FROM_DATABASE=AccelePort Xp (AccelePort 4p PCI) + +pci:v0000114Fd00000040sv0000114Fsd00000044* + ID_MODEL_FROM_DATABASE=AccelePort Xp (AccelePort 8p PCI) + +pci:v0000114Fd00000040sv0000114Fsd00000045* + ID_MODEL_FROM_DATABASE=AccelePort Xp (AccelePort 16p PCI) + +pci:v0000114Fd00000040sv0000114Fsd0000004E* + ID_MODEL_FROM_DATABASE=AccelePort Xp (AccelePort 32p PCI) + +pci:v0000114Fd00000042* + ID_MODEL_FROM_DATABASE=AccelePort 2p + +pci:v0000114Fd00000043* + ID_MODEL_FROM_DATABASE=AccelePort 4p + +pci:v0000114Fd00000044* + ID_MODEL_FROM_DATABASE=AccelePort 8p + +pci:v0000114Fd00000045* + ID_MODEL_FROM_DATABASE=AccelePort 16p + +pci:v0000114Fd0000004E* + ID_MODEL_FROM_DATABASE=AccelePort 32p + +pci:v0000114Fd00000070* + ID_MODEL_FROM_DATABASE=Datafire Micro V IOM2 (Europe) + +pci:v0000114Fd00000071* + ID_MODEL_FROM_DATABASE=Datafire Micro V (Europe) + +pci:v0000114Fd00000072* + ID_MODEL_FROM_DATABASE=Datafire Micro V IOM2 (North America) + +pci:v0000114Fd00000073* + ID_MODEL_FROM_DATABASE=Datafire Micro V (North America) + +pci:v0000114Fd000000B0* + ID_MODEL_FROM_DATABASE=Digi Neo 4 + +pci:v0000114Fd000000B1* + ID_MODEL_FROM_DATABASE=Digi Neo 8 + +pci:v0000114Fd000000C8* + ID_MODEL_FROM_DATABASE=Digi Neo 2 DB9 + +pci:v0000114Fd000000C9* + ID_MODEL_FROM_DATABASE=Digi Neo 2 DB9 PRI + +pci:v0000114Fd000000CA* + ID_MODEL_FROM_DATABASE=Digi Neo 2 RJ45 + +pci:v0000114Fd000000CB* + ID_MODEL_FROM_DATABASE=Digi Neo 2 RJ45 PRI + +pci:v0000114Fd000000CC* + ID_MODEL_FROM_DATABASE=Digi Neo 1 422 + +pci:v0000114Fd000000CD* + ID_MODEL_FROM_DATABASE=Digi Neo 1 422 485 + +pci:v0000114Fd000000CE* + ID_MODEL_FROM_DATABASE=Digi Neo 2 422 485 + +pci:v0000114Fd000000D0* + ID_MODEL_FROM_DATABASE=ClassicBoard 4 422 + +pci:v0000114Fd000000D1* + ID_MODEL_FROM_DATABASE=ClassicBoard 8 422 + +pci:v0000114Fd000000F1* + ID_MODEL_FROM_DATABASE=Digi Neo PCI-E 4 port + +pci:v0000114Fd000000F4* + ID_MODEL_FROM_DATABASE=Digi Neo 4 (IBM version) + +pci:v0000114Fd00006001* + ID_MODEL_FROM_DATABASE=Avanstar + +pci:v00001150* + ID_VENDOR_FROM_DATABASE=Thinking Machines Corp + +pci:v00001151* + ID_VENDOR_FROM_DATABASE=JAE Electronics Inc. + +pci:v00001152* + ID_VENDOR_FROM_DATABASE=Megatek + +pci:v00001153* + ID_VENDOR_FROM_DATABASE=Land Win Electronic Corp + +pci:v00001154* + ID_VENDOR_FROM_DATABASE=Melco Inc + +pci:v00001155* + ID_VENDOR_FROM_DATABASE=Pine Technology Ltd + +pci:v00001156* + ID_VENDOR_FROM_DATABASE=Periscope Engineering + +pci:v00001157* + ID_VENDOR_FROM_DATABASE=Avsys Corporation + +pci:v00001158* + ID_VENDOR_FROM_DATABASE=Voarx R & D Inc + +pci:v00001158d00003011* + ID_MODEL_FROM_DATABASE=Tokenet/vg 1001/10m anylan + +pci:v00001158d00009050* + ID_MODEL_FROM_DATABASE=Lanfleet/Truevalue + +pci:v00001158d00009051* + ID_MODEL_FROM_DATABASE=Lanfleet/Truevalue + +pci:v00001159* + ID_VENDOR_FROM_DATABASE=Mutech Corp + +pci:v00001159d00000001* + ID_MODEL_FROM_DATABASE=MV-1000 + +pci:v00001159d00000002* + ID_MODEL_FROM_DATABASE=MV-1500 + +pci:v0000115A* + ID_VENDOR_FROM_DATABASE=Harlequin Ltd + +pci:v0000115B* + ID_VENDOR_FROM_DATABASE=Parallax Graphics + +pci:v0000115C* + ID_VENDOR_FROM_DATABASE=Photron Ltd. + +pci:v0000115D* + ID_VENDOR_FROM_DATABASE=Xircom + +pci:v0000115Dd00000003* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000003sv00001014sd00000181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000003sv00001014sd00001181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000003sv00001014sd00008181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000003sv00001014sd00009181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000003sv0000115Dsd00000181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000003sv0000115Dsd00000182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (RealPort2 CardBus Ethernet 10/100 (R2BE-100)) + +pci:v0000115Dd00000003sv0000115Dsd00001181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000003sv00001179sd00000181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000003sv00008086sd00008181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (EtherExpress PRO/100 Mobile CardBus 32 Adapter) + +pci:v0000115Dd00000003sv00008086sd00009181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (EtherExpress PRO/100 Mobile CardBus 32 Adapter) + +pci:v0000115Dd00000005* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000005sv00001014sd00000182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000005sv00001014sd00001182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000005sv0000115Dsd00000182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000005sv0000115Dsd00001182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000007* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000007sv00001014sd00000182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000007sv00001014sd00001182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd00000007sv0000115Dsd00000182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd00000007sv0000115Dsd00001182* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd0000000B* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd0000000Bsv00001014sd00000183* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd0000000Bsv0000115Dsd00000183* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd0000000C* + ID_MODEL_FROM_DATABASE=Mini-PCI V.90 56k Modem + +pci:v0000115Dd0000000F* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd0000000Fsv00001014sd00000183* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 (10/100 EtherJet Cardbus Adapter) + +pci:v0000115Dd0000000Fsv0000115Dsd00000183* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet 10/100 + +pci:v0000115Dd000000D4* + ID_MODEL_FROM_DATABASE=Mini-PCI K56Flex Modem + +pci:v0000115Dd00000101* + ID_MODEL_FROM_DATABASE=Cardbus 56k modem + +pci:v0000115Dd00000101sv0000115Dsd00001081* + ID_MODEL_FROM_DATABASE=Cardbus 56k modem (Cardbus 56k Modem) + +pci:v0000115Dd00000103* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet + 56k Modem + +pci:v0000115Dd00000103sv00001014sd00009181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet + 56k Modem (Cardbus 56k Modem) + +pci:v0000115Dd00000103sv00001115sd00001181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet + 56k Modem (Cardbus Ethernet 100 + 56k Modem) + +pci:v0000115Dd00000103sv0000115Dsd00001181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet + 56k Modem (CBEM56G-100 Ethernet + 56k Modem) + +pci:v0000115Dd00000103sv00008086sd00009181* + ID_MODEL_FROM_DATABASE=Cardbus Ethernet + 56k Modem (PRO/100 LAN + Modem56 CardBus) + +pci:v0000115E* + ID_VENDOR_FROM_DATABASE=Peer Protocols Inc + +pci:v0000115F* + ID_VENDOR_FROM_DATABASE=Maxtor Corporation + +pci:v00001160* + ID_VENDOR_FROM_DATABASE=Megasoft Inc + +pci:v00001161* + ID_VENDOR_FROM_DATABASE=PFU Limited + +pci:v00001162* + ID_VENDOR_FROM_DATABASE=OA Laboratory Co Ltd + +pci:v00001163* + ID_VENDOR_FROM_DATABASE=Rendition + +pci:v00001163d00000001* + ID_MODEL_FROM_DATABASE=Verite 1000 + +pci:v00001163d00002000* + ID_MODEL_FROM_DATABASE=Verite V2000/V2100/V2200 + +pci:v00001163d00002000sv00001092sd00002000* + ID_MODEL_FROM_DATABASE=Verite V2000/V2100/V2200 (Stealth II S220) + +pci:v00001164* + ID_VENDOR_FROM_DATABASE=Advanced Peripherals Technologies + +pci:v00001165* + ID_VENDOR_FROM_DATABASE=Imagraph Corporation + +pci:v00001165d00000001* + ID_MODEL_FROM_DATABASE=Motion TPEG Recorder/Player with audio + +pci:v00001166* + ID_VENDOR_FROM_DATABASE=Broadcom + +pci:v00001166d00000000* + ID_MODEL_FROM_DATABASE=CMIC-LE + +pci:v00001166d00000005* + ID_MODEL_FROM_DATABASE=CNB20-LE Host Bridge + +pci:v00001166d00000006* + ID_MODEL_FROM_DATABASE=CNB20HE Host Bridge + +pci:v00001166d00000007* + ID_MODEL_FROM_DATABASE=CNB20-LE Host Bridge + +pci:v00001166d00000008* + ID_MODEL_FROM_DATABASE=CNB20HE Host Bridge + +pci:v00001166d00000009* + ID_MODEL_FROM_DATABASE=CNB20LE Host Bridge + +pci:v00001166d00000010* + ID_MODEL_FROM_DATABASE=CIOB30 + +pci:v00001166d00000011* + ID_MODEL_FROM_DATABASE=CMIC-HE + +pci:v00001166d00000012* + ID_MODEL_FROM_DATABASE=CMIC-WS Host Bridge (GC-LE chipset) + +pci:v00001166d00000013* + ID_MODEL_FROM_DATABASE=CNB20-HE Host Bridge + +pci:v00001166d00000014* + ID_MODEL_FROM_DATABASE=CMIC-LE Host Bridge (GC-LE chipset) + +pci:v00001166d00000015* + ID_MODEL_FROM_DATABASE=CMIC-GC Host Bridge + +pci:v00001166d00000016* + ID_MODEL_FROM_DATABASE=CMIC-GC Host Bridge + +pci:v00001166d00000017* + ID_MODEL_FROM_DATABASE=GCNB-LE Host Bridge + +pci:v00001166d00000031* + ID_MODEL_FROM_DATABASE=HT1100 HPX0 HT Host Bridge + +pci:v00001166d00000036* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] PCI/PCI-X Bridge + +pci:v00001166d00000101* + ID_MODEL_FROM_DATABASE=CIOB-X2 PCI-X I/O Bridge + +pci:v00001166d00000103* + ID_MODEL_FROM_DATABASE=EPB PCI-Express to PCI-X Bridge + +pci:v00001166d00000104* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] PCI/PCI-X Bridge + +pci:v00001166d00000110* + ID_MODEL_FROM_DATABASE=CIOB-E I/O Bridge with Gigabit Ethernet + +pci:v00001166d00000130* + ID_MODEL_FROM_DATABASE=BCM5780 [HT2000] PCI-X bridge + +pci:v00001166d00000132* + ID_MODEL_FROM_DATABASE=BCM5780 [HT2000] PCI-Express Bridge + +pci:v00001166d00000132sv00001166sd00000132* + ID_MODEL_FROM_DATABASE=BCM5780 [HT2000] PCI-Express Bridge (HT2000 PCI-Express bridge) + +pci:v00001166d00000140* + ID_MODEL_FROM_DATABASE=HT2100 PCI-Express Bridge + +pci:v00001166d00000141* + ID_MODEL_FROM_DATABASE=HT2100 PCI-Express Bridge + +pci:v00001166d00000142* + ID_MODEL_FROM_DATABASE=HT2100 PCI-Express Bridge + +pci:v00001166d00000144* + ID_MODEL_FROM_DATABASE=HT2100 PCI-Express Bridge + +pci:v00001166d00000200* + ID_MODEL_FROM_DATABASE=OSB4 South Bridge + +pci:v00001166d00000201* + ID_MODEL_FROM_DATABASE=CSB5 South Bridge + +pci:v00001166d00000201sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=CSB5 South Bridge (CT8 mainboard) + +pci:v00001166d00000203* + ID_MODEL_FROM_DATABASE=CSB6 South Bridge + +pci:v00001166d00000203sv00001734sd00001012* + ID_MODEL_FROM_DATABASE=CSB6 South Bridge (PRIMERGY RX/TX series) + +pci:v00001166d00000205* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] Legacy South Bridge + +pci:v00001166d00000211* + ID_MODEL_FROM_DATABASE=OSB4 IDE Controller + +pci:v00001166d00000212* + ID_MODEL_FROM_DATABASE=CSB5 IDE Controller + +pci:v00001166d00000212sv00001028sd0000014A* + ID_MODEL_FROM_DATABASE=CSB5 IDE Controller (PowerEdge 1750) + +pci:v00001166d00000212sv00001028sd0000810B* + ID_MODEL_FROM_DATABASE=CSB5 IDE Controller (PowerEdge 1650/2550) + +pci:v00001166d00000212sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=CSB5 IDE Controller (CT8 mainboard) + +pci:v00001166d00000213* + ID_MODEL_FROM_DATABASE=CSB6 RAID/IDE Controller + +pci:v00001166d00000213sv00001028sd00004134* + ID_MODEL_FROM_DATABASE=CSB6 RAID/IDE Controller (PowerEdge 600SC) + +pci:v00001166d00000213sv00001028sd0000C134* + ID_MODEL_FROM_DATABASE=CSB6 RAID/IDE Controller (Poweredge SC600) + +pci:v00001166d00000213sv00001734sd00001012* + ID_MODEL_FROM_DATABASE=CSB6 RAID/IDE Controller (PRIMERGY RX/TX series onboard IDE) + +pci:v00001166d00000214* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] IDE + +pci:v00001166d00000214sv00001028sd00000205* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] IDE (PowerEdge 2970 HT1000 IDE) + +pci:v00001166d00000217* + ID_MODEL_FROM_DATABASE=CSB6 IDE Controller + +pci:v00001166d00000217sv00001028sd00004134* + ID_MODEL_FROM_DATABASE=CSB6 IDE Controller (Poweredge SC600) + +pci:v00001166d0000021B* + ID_MODEL_FROM_DATABASE=HT1100 HD Audio + +pci:v00001166d00000220* + ID_MODEL_FROM_DATABASE=OSB4/CSB5 OHCI USB Controller + +pci:v00001166d00000220sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=OSB4/CSB5 OHCI USB Controller (CT8 mainboard) + +pci:v00001166d00000221* + ID_MODEL_FROM_DATABASE=CSB6 OHCI USB Controller + +pci:v00001166d00000221sv00001734sd00001012* + ID_MODEL_FROM_DATABASE=CSB6 OHCI USB Controller (PRIMERGY RX/TX series onboard OHCI) + +pci:v00001166d00000223* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] USB + +pci:v00001166d00000223sv00001028sd00000205* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] USB (PowerEdge 2970 HT1000 USB Controller) + +pci:v00001166d00000223sv00001028sd0000020B* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] USB (PowerEdge T605 HT1000 USB Controller) + +pci:v00001166d00000225* + ID_MODEL_FROM_DATABASE=CSB5 LPC bridge + +pci:v00001166d00000227* + ID_MODEL_FROM_DATABASE=GCLE-2 Host Bridge + +pci:v00001166d00000227sv00001734sd00001012* + ID_MODEL_FROM_DATABASE=GCLE-2 Host Bridge (PRIMERGY RX/TX series) + +pci:v00001166d00000230* + ID_MODEL_FROM_DATABASE=CSB5 LPC bridge + +pci:v00001166d00000230sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=CSB5 LPC bridge (CT8 mainboard) + +pci:v00001166d00000234* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] LPC + +pci:v00001166d00000234sv00001028sd00000205* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] LPC (PowerEdge 2970 HT1000 LPC) + +pci:v00001166d00000234sv00001028sd0000020B* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] LPC (PowerEdge T605 HT1000 LPC) + +pci:v00001166d00000235* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] XIOAPIC0-2 + +pci:v00001166d00000238* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] WDTimer + +pci:v00001166d00000240* + ID_MODEL_FROM_DATABASE=K2 SATA + +pci:v00001166d00000241* + ID_MODEL_FROM_DATABASE=RAIDCore RC4000 + +pci:v00001166d00000242* + ID_MODEL_FROM_DATABASE=RAIDCore BC4000 + +pci:v00001166d0000024A* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] SATA (Native SATA Mode) + +pci:v00001166d0000024Asv00001028sd0000020B* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] SATA (Native SATA Mode) (PowerEdge T605 onboard SATA Controller) + +pci:v00001166d0000024B* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] SATA (PATA/IDE Mode) + +pci:v00001166d0000024Bsv00001028sd00000205* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] SATA (PATA/IDE Mode) (PowerEdge 2970 HT1000 SATA controller) + +pci:v00001166d00000406* + ID_MODEL_FROM_DATABASE=HT1100 PCI-X Bridge + +pci:v00001166d00000408* + ID_MODEL_FROM_DATABASE=HT1100 Legacy Device + +pci:v00001166d0000040A* + ID_MODEL_FROM_DATABASE=HT1100 ISA-LPC Bridge + +pci:v00001166d0000040Asv00001028sd00000223* + ID_MODEL_FROM_DATABASE=HT1100 ISA-LPC Bridge (PowerEdge R905 HT1100 ISA-LPC Bridge) + +pci:v00001166d00000410* + ID_MODEL_FROM_DATABASE=HT1100 SATA Controller (Native SATA Mode) + +pci:v00001166d00000411* + ID_MODEL_FROM_DATABASE=HT1100 SATA Controller (PATA / IDE Mode) + +pci:v00001166d00000412* + ID_MODEL_FROM_DATABASE=HT1100 USB OHCI Controller + +pci:v00001166d00000414* + ID_MODEL_FROM_DATABASE=HT1100 USB EHCI Controller + +pci:v00001166d00000416* + ID_MODEL_FROM_DATABASE=HT1100 USB EHCI Controller (with Debug Port) + +pci:v00001166d00000420* + ID_MODEL_FROM_DATABASE=HT1100 PCI-Express Bridge + +pci:v00001166d00000421* + ID_MODEL_FROM_DATABASE=HT1100 SAS/SATA Controller + +pci:v00001166d00000422* + ID_MODEL_FROM_DATABASE=HT1100 PCI-Express Bridge + +pci:v00001167* + ID_VENDOR_FROM_DATABASE=Mutoh Industries Inc + +pci:v00001168* + ID_VENDOR_FROM_DATABASE=Thine Electronics Inc + +pci:v00001169* + ID_VENDOR_FROM_DATABASE=Centre for Development of Advanced Computing + +pci:v0000116A* + ID_VENDOR_FROM_DATABASE=Luminex Software, Inc. + +pci:v0000116Ad00006100* + ID_MODEL_FROM_DATABASE=Bus/Tag Channel + +pci:v0000116Ad00006800* + ID_MODEL_FROM_DATABASE=Escon Channel + +pci:v0000116Ad00007100* + ID_MODEL_FROM_DATABASE=Bus/Tag Channel + +pci:v0000116Ad00007800* + ID_MODEL_FROM_DATABASE=Escon Channel + +pci:v0000116B* + ID_VENDOR_FROM_DATABASE=Connectware Inc + +pci:v0000116C* + ID_VENDOR_FROM_DATABASE=Intelligent Resources Integrated Systems + +pci:v0000116D* + ID_VENDOR_FROM_DATABASE=Martin-Marietta + +pci:v0000116E* + ID_VENDOR_FROM_DATABASE=Electronics for Imaging + +pci:v0000116F* + ID_VENDOR_FROM_DATABASE=Workstation Technology + +pci:v00001170* + ID_VENDOR_FROM_DATABASE=Inventec Corporation + +pci:v00001171* + ID_VENDOR_FROM_DATABASE=Loughborough Sound Images Plc + +pci:v00001172* + ID_VENDOR_FROM_DATABASE=Altera Corporation + +pci:v00001173* + ID_VENDOR_FROM_DATABASE=Adobe Systems, Inc + +pci:v00001174* + ID_VENDOR_FROM_DATABASE=Bridgeport Machines + +pci:v00001175* + ID_VENDOR_FROM_DATABASE=Mitron Computer Inc. + +pci:v00001176* + ID_VENDOR_FROM_DATABASE=SBE Incorporated + +pci:v00001177* + ID_VENDOR_FROM_DATABASE=Silicon Engineering + +pci:v00001178* + ID_VENDOR_FROM_DATABASE=Alfa, Inc. + +pci:v00001178d0000AFA1* + ID_MODEL_FROM_DATABASE=Fast Ethernet Adapter + +pci:v00001179* + ID_VENDOR_FROM_DATABASE=Toshiba America Info Systems + +pci:v00001179d00000102* + ID_MODEL_FROM_DATABASE=Extended IDE Controller + +pci:v00001179d00000103* + ID_MODEL_FROM_DATABASE=EX-IDE Type-B + +pci:v00001179d00000404* + ID_MODEL_FROM_DATABASE=DVD Decoder card + +pci:v00001179d00000406* + ID_MODEL_FROM_DATABASE=Tecra Video Capture device + +pci:v00001179d00000407* + ID_MODEL_FROM_DATABASE=DVD Decoder card (Version 2) + +pci:v00001179d00000601* + ID_MODEL_FROM_DATABASE=CPU to PCI bridge + +pci:v00001179d00000601sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=CPU to PCI bridge (Satellite Pro) + +pci:v00001179d00000602* + ID_MODEL_FROM_DATABASE=PCI to ISA bridge + +pci:v00001179d00000603* + ID_MODEL_FROM_DATABASE=ToPIC95 PCI to CardBus Bridge for Notebooks + +pci:v00001179d00000604* + ID_MODEL_FROM_DATABASE=PCI-Docking Host bridge + +pci:v00001179d0000060A* + ID_MODEL_FROM_DATABASE=ToPIC95 + +pci:v00001179d0000060Asv00001179sd00000001* + ID_MODEL_FROM_DATABASE=ToPIC95 (Satellite Pro) + +pci:v00001179d0000060F* + ID_MODEL_FROM_DATABASE=ToPIC97 + +pci:v00001179d0000060Fsv00001179sd00000001* + ID_MODEL_FROM_DATABASE=ToPIC97 (Satellite 4010) + +pci:v00001179d00000617* + ID_MODEL_FROM_DATABASE=ToPIC100 PCI to Cardbus Bridge with ZV Support + +pci:v00001179d00000618* + ID_MODEL_FROM_DATABASE=CPU to PCI and PCI to ISA bridge + +pci:v00001179d00000701* + ID_MODEL_FROM_DATABASE=FIR Port Type-O + +pci:v00001179d00000803* + ID_MODEL_FROM_DATABASE=TC6371AF SD Host Controller + +pci:v00001179d00000804* + ID_MODEL_FROM_DATABASE=TC6371AF SmartMedia Controller + +pci:v00001179d00000805* + ID_MODEL_FROM_DATABASE=SD TypA Controller + +pci:v00001179d00000D01* + ID_MODEL_FROM_DATABASE=FIR Port Type-DO + +pci:v00001179d00000D01sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=FIR Port Type-DO + +pci:v0000117A* + ID_VENDOR_FROM_DATABASE=A-Trend Technology + +pci:v0000117B* + ID_VENDOR_FROM_DATABASE=L G Electronics, Inc. + +pci:v0000117C* + ID_VENDOR_FROM_DATABASE=ATTO Technology, Inc. + +pci:v0000117Cd0000002C* + ID_MODEL_FROM_DATABASE=ExpressSAS R380 + +pci:v0000117Cd0000002D* + ID_MODEL_FROM_DATABASE=ExpressSAS R348 + +pci:v0000117Cd00000030* + ID_MODEL_FROM_DATABASE=Ultra320 SCSI Host Adapter + +pci:v0000117Cd00000030sv0000117Csd00008013* + ID_MODEL_FROM_DATABASE=Ultra320 SCSI Host Adapter (ExpressPCI UL4D) + +pci:v0000117Cd00000030sv0000117Csd00008014* + ID_MODEL_FROM_DATABASE=Ultra320 SCSI Host Adapter (ExpressPCI UL4S) + +pci:v0000117Cd00000030sv0000117Csd00008027* + ID_MODEL_FROM_DATABASE=Ultra320 SCSI Host Adapter (ExpressPCI UL5D) + +pci:v0000117Cd00000030sv0000117Csd0000802F* + ID_MODEL_FROM_DATABASE=Ultra320 SCSI Host Adapter (ExpressPCI UL5D Low Profile) + +pci:v0000117Cd00000033* + ID_MODEL_FROM_DATABASE=SAS Adapter + +pci:v0000117Cd00000041* + ID_MODEL_FROM_DATABASE=ExpressSAS R30F + +pci:v0000117Cd00008013* + ID_MODEL_FROM_DATABASE=ExpressPCI UL4D + +pci:v0000117Cd00008014* + ID_MODEL_FROM_DATABASE=ExpressPCI UL4S + +pci:v0000117Cd00008027* + ID_MODEL_FROM_DATABASE=ExpressPCI UL5D + +pci:v0000117D* + ID_VENDOR_FROM_DATABASE=Becton & Dickinson + +pci:v0000117E* + ID_VENDOR_FROM_DATABASE=T/R Systems + +pci:v0000117F* + ID_VENDOR_FROM_DATABASE=Integrated Circuit Systems + +pci:v00001180* + ID_VENDOR_FROM_DATABASE=Ricoh Co Ltd + +pci:v00001180d00000465* + ID_MODEL_FROM_DATABASE=RL5c465 + +pci:v00001180d00000466* + ID_MODEL_FROM_DATABASE=RL5c466 + +pci:v00001180d00000475* + ID_MODEL_FROM_DATABASE=RL5c475 + +pci:v00001180d00000475sv0000144Dsd0000C006* + ID_MODEL_FROM_DATABASE=RL5c475 (vpr Matrix 170B4 CardBus bridge) + +pci:v00001180d00000476* + ID_MODEL_FROM_DATABASE=RL5c476 II + +pci:v00001180d00000476sv00001014sd00000185* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad A/T/X Series) + +pci:v00001180d00000476sv00001014sd00000555* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad X41) + +pci:v00001180d00000476sv00001014sd0000056C* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad Z60t) + +pci:v00001180d00000476sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=RL5c476 II (Latitude X300 laptop) + +pci:v00001180d00000476sv00001028sd00000188* + ID_MODEL_FROM_DATABASE=RL5c476 II (Inspiron 6000 laptop) + +pci:v00001180d00000476sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=RL5c476 II (Compaq 6710b) + +pci:v00001180d00000476sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=RL5c476 II (Compaq 6910p) + +pci:v00001180d00000476sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=RL5c476 II (A6J-Q008) + +pci:v00001180d00000476sv00001043sd00001967* + ID_MODEL_FROM_DATABASE=RL5c476 II (V6800V) + +pci:v00001180d00000476sv00001043sd00001987* + ID_MODEL_FROM_DATABASE=RL5c476 II (Asus A4K and Z81K notebooks, possibly others ( mid-2005 machines )) + +pci:v00001180d00000476sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=RL5c476 II (Vaio PCG-FX403) + +pci:v00001180d00000476sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=RL5c476 II (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00001180d00000476sv0000104Dsd0000814E* + ID_MODEL_FROM_DATABASE=RL5c476 II (VAIO GRZ390Z) + +pci:v00001180d00000476sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=RL5c476 II (Panasonic CF-Y5 laptop) + +pci:v00001180d00000476sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=RL5c476 II (X10 Laptop) + +pci:v00001180d00000476sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=RL5c476 II (P30/P35 notebook) + +pci:v00001180d00000476sv000014EFsd00000220* + ID_MODEL_FROM_DATABASE=RL5c476 II (PCD-RP-220S) + +pci:v00001180d00000476sv000017AAsd0000201C* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad X60/X60s) + +pci:v00001180d00000476sv000017AAsd000020C4* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad T61) + +pci:v00001180d00000476sv000017AAsd000020C6* + ID_MODEL_FROM_DATABASE=RL5c476 II (ThinkPad R61) + +pci:v00001180d00000477* + ID_MODEL_FROM_DATABASE=RL5c477 + +pci:v00001180d00000478* + ID_MODEL_FROM_DATABASE=RL5c478 + +pci:v00001180d00000478sv00001014sd00000184* + ID_MODEL_FROM_DATABASE=RL5c478 (ThinkPad A30p) + +pci:v00001180d00000511* + ID_MODEL_FROM_DATABASE=R5C511 + +pci:v00001180d00000522* + ID_MODEL_FROM_DATABASE=R5C522 IEEE 1394 Controller + +pci:v00001180d00000522sv00001014sd000001CF* + ID_MODEL_FROM_DATABASE=R5C522 IEEE 1394 Controller (ThinkPad A30p) + +pci:v00001180d00000522sv00001043sd00001967* + ID_MODEL_FROM_DATABASE=R5C522 IEEE 1394 Controller (V6800V) + +pci:v00001180d00000551* + ID_MODEL_FROM_DATABASE=R5C551 IEEE 1394 Controller + +pci:v00001180d00000551sv0000144Dsd0000C006* + ID_MODEL_FROM_DATABASE=R5C551 IEEE 1394 Controller (vpr Matrix 170B4) + +pci:v00001180d00000552* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller + +pci:v00001180d00000552sv00001014sd00000511* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (ThinkPad A/T/X Series) + +pci:v00001180d00000552sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (Latitude X300 laptop) + +pci:v00001180d00000552sv00001028sd00000188* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (Inspiron 6000 laptop) + +pci:v00001180d00000552sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (A6J-Q008) + +pci:v00001180d00000552sv00001043sd00001757* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (M2400N laptop) + +pci:v00001180d00000552sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (X10 Laptop) + +pci:v00001180d00000552sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (P30/P35 notebook) + +pci:v00001180d00000552sv000017AAsd0000201E* + ID_MODEL_FROM_DATABASE=R5C552 IEEE 1394 Controller (ThinkPad X60/X60s) + +pci:v00001180d00000554* + ID_MODEL_FROM_DATABASE=R5C554 + +pci:v00001180d00000575* + ID_MODEL_FROM_DATABASE=R5C575 SD Bus Host Adapter + +pci:v00001180d00000576* + ID_MODEL_FROM_DATABASE=R5C576 SD Bus Host Adapter + +pci:v00001180d00000592* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter + +pci:v00001180d00000592sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Aspire 5920G) + +pci:v00001180d00000592sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (XPS M1210) + +pci:v00001180d00000592sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Inspiron 1420) + +pci:v00001180d00000592sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Presario V3242AU) + +pci:v00001180d00000592sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Presario V6133CL) + +pci:v00001180d00000592sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Pavilion dv6700) + +pci:v00001180d00000592sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Pavilion dv95xx/96xx/97xx/98xx series) + +pci:v00001180d00000592sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (A6J-Q008) + +pci:v00001180d00000592sv00001043sd00001967* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (V6800V) + +pci:v00001180d00000592sv0000144Dsd0000C018* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (X20 IV) + +pci:v00001180d00000592sv000017AAsd000020CA* + ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (ThinkPad T61) + +pci:v00001180d00000811* + ID_MODEL_FROM_DATABASE=R5C811 + +pci:v00001180d00000822* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter + +pci:v00001180d00000822sv00001014sd00000556* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad X40 / X41 / X60s / Z60t) + +pci:v00001180d00000822sv00001014sd00000598* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad Z60m) + +pci:v00001180d00000822sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Aspire 5920G) + +pci:v00001180d00000822sv00001028sd00000188* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Inspiron 6000 laptop) + +pci:v00001180d00000822sv00001028sd000001A2* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Inspiron 9200) + +pci:v00001180d00000822sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (XPS M1210) + +pci:v00001180d00000822sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Inspiron 1420) + +pci:v00001180d00000822sv0000103Csd000003B5* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Presario V3242AU) + +pci:v00001180d00000822sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Presario V6133CL) + +pci:v00001180d00000822sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Compaq 6910p) + +pci:v00001180d00000822sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Pavilion dv6700) + +pci:v00001180d00000822sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Pavilion dv9668eg Laptop) + +pci:v00001180d00000822sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (A6J-Q008) + +pci:v00001180d00000822sv00001043sd00001967* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ASUS V6800V) + +pci:v00001180d00000822sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Panasonic CF-Y5 laptop) + +pci:v00001180d00000822sv0000144Dsd0000C018* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (X20 IV) + +pci:v00001180d00000822sv000017AAsd0000201D* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad X60/X60s) + +pci:v00001180d00000822sv000017AAsd000020C7* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad T61) + +pci:v00001180d00000822sv000017AAsd000020C8* + ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (ThinkPad W500) + +pci:v00001180d00000832* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller + +pci:v00001180d00000832sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Aspire 5920G) + +pci:v00001180d00000832sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (XPS M1210) + +pci:v00001180d00000832sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Inspiron 1420) + +pci:v00001180d00000832sv00001028sd0000024D* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Latitude E4300) + +pci:v00001180d00000832sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Presario V3242AU) + +pci:v00001180d00000832sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Presario V6133CL) + +pci:v00001180d00000832sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Compaq 6910p) + +pci:v00001180d00000832sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Pavilion dv6700) + +pci:v00001180d00000832sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (Pavilion dv9668eg Laptop) + +pci:v00001180d00000832sv000017AAsd000020C7* + ID_MODEL_FROM_DATABASE=R5C832 IEEE 1394 Controller (ThinkPad R61) + +pci:v00001180d00000841* + ID_MODEL_FROM_DATABASE=R5C841 CardBus/SD/SDIO/MMC/MS/MSPro/xD/IEEE1394 + +pci:v00001180d00000843* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller + +pci:v00001180d00000843sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Aspire 5920G) + +pci:v00001180d00000843sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (XPS M1210) + +pci:v00001180d00000843sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Inspiron 1420) + +pci:v00001180d00000843sv00001028sd000001F5* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Dell Inspiron 1501) + +pci:v00001180d00000843sv00001028sd0000024F* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Dell Latitude e6500) + +pci:v00001180d00000843sv0000103Csd000003B5* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Presario V3242AU) + +pci:v00001180d00000843sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Presario V6133CL) + +pci:v00001180d00000843sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Pavilion dv9500/9600/9700 series) + +pci:v00001180d00000843sv00001183sd00000843* + ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Alienware Aurora m9700) + +pci:v00001180d00000852* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller + +pci:v00001180d00000852sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Aspire 5920G) + +pci:v00001180d00000852sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Inspiron 1420) + +pci:v00001180d00000852sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Presario V3242AU) + +pci:v00001180d00000852sv0000103Csd000030B7* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Presario V6133CL) + +pci:v00001180d00000852sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Pavilion dv6700) + +pci:v00001180d00000852sv0000103Csd000030CF* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Pavilion dv9668eg Laptop) + +pci:v00001180d00000852sv00001043sd00001967* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (V6800V) + +pci:v00001180d00000852sv00001180sd00000852* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (Pavilion 2410us) + +pci:v00001180d00000852sv00001324sd000010CF* + ID_MODEL_FROM_DATABASE=xD-Picture Card Controller (P7120) + +pci:v00001180d0000E230* + ID_MODEL_FROM_DATABASE=R5U2xx (R5U230 / R5U231 / R5U241) [Memory Stick Host Controller] + +pci:v00001180d0000E476* + ID_MODEL_FROM_DATABASE=CardBus bridge + +pci:v00001180d0000E476sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=CardBus bridge (Latitude E6410) + +pci:v00001180d0000E476sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=CardBus bridge (Latitude E6510) + +pci:v00001180d0000E822* + ID_MODEL_FROM_DATABASE=MMC/SD Host Controller + +pci:v00001180d0000E822sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=MMC/SD Host Controller (Latitude E6410) + +pci:v00001180d0000E822sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=MMC/SD Host Controller (Latitude E6510) + +pci:v00001180d0000E823* + ID_MODEL_FROM_DATABASE=PCIe SDXC/MMC Host Controller + +pci:v00001180d0000E832* + ID_MODEL_FROM_DATABASE=R5C832 PCIe IEEE 1394 Controller + +pci:v00001180d0000E832sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=R5C832 PCIe IEEE 1394 Controller (Latitude E6410) + +pci:v00001180d0000E832sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=R5C832 PCIe IEEE 1394 Controller (Latitude E6510) + +pci:v00001180d0000E852* + ID_MODEL_FROM_DATABASE=PCIe xD-Picture Card Controller + +pci:v00001181* + ID_VENDOR_FROM_DATABASE=Telmatics International + +pci:v00001183* + ID_VENDOR_FROM_DATABASE=Fujikura Ltd + +pci:v00001184* + ID_VENDOR_FROM_DATABASE=Forks Inc + +pci:v00001185* + ID_VENDOR_FROM_DATABASE=Dataworld International Ltd + +pci:v00001186* + ID_VENDOR_FROM_DATABASE=D-Link System Inc + +pci:v00001186d00001002* + ID_MODEL_FROM_DATABASE=DL10050 Sundance Ethernet + +pci:v00001186d00001002sv00001186sd00001002* + ID_MODEL_FROM_DATABASE=DL10050 Sundance Ethernet (DFE-550TX/FX) + +pci:v00001186d00001002sv00001186sd00001012* + ID_MODEL_FROM_DATABASE=DL10050 Sundance Ethernet (DFE-580TX) + +pci:v00001186d00001025* + ID_MODEL_FROM_DATABASE=AirPlus Xtreme G DWL-G650 Adapter + +pci:v00001186d00001026* + ID_MODEL_FROM_DATABASE=AirXpert DWL-AG650 Wireless Cardbus Adapter + +pci:v00001186d00001043* + ID_MODEL_FROM_DATABASE=AirXpert DWL-AG650 Wireless Cardbus Adapter + +pci:v00001186d00001300* + ID_MODEL_FROM_DATABASE=RTL8139 Ethernet + +pci:v00001186d00001300sv00001186sd00001300* + ID_MODEL_FROM_DATABASE=RTL8139 Ethernet (DFE-538TX 10/100 Ethernet Adapter) + +pci:v00001186d00001300sv00001186sd00001301* + ID_MODEL_FROM_DATABASE=RTL8139 Ethernet (DFE-530TX+ 10/100 Ethernet Adapter) + +pci:v00001186d00001300sv00001186sd00001303* + ID_MODEL_FROM_DATABASE=RTL8139 Ethernet (DFE-528TX 10/100 Fast Ethernet PCI Adapter) + +pci:v00001186d00001340* + ID_MODEL_FROM_DATABASE=DFE-690TXD CardBus PC Card + +pci:v00001186d00001540* + ID_MODEL_FROM_DATABASE=DFE-680TX + +pci:v00001186d00001541* + ID_MODEL_FROM_DATABASE=DFE-680TXD CardBus PC Card + +pci:v00001186d00001561* + ID_MODEL_FROM_DATABASE=DRP-32TXD Cardbus PC Card + +pci:v00001186d00003300* + ID_MODEL_FROM_DATABASE=DWL-510 / DWL-610 802.11b [Realtek RTL8180L] + +pci:v00001186d00003300sv00001186sd00003300* + ID_MODEL_FROM_DATABASE=DWL-510 / DWL-610 802.11b [Realtek RTL8180L] (DWL-610 Wireless Cardbus Adapter) + +pci:v00001186d00003300sv00001186sd00003301* + ID_MODEL_FROM_DATABASE=DWL-510 / DWL-610 802.11b [Realtek RTL8180L] (DWL-510 Wireless PCI Adapter) + +pci:v00001186d00003A10* + ID_MODEL_FROM_DATABASE=AirXpert DWL-AG650 Wireless Cardbus Adapter(rev.B) + +pci:v00001186d00003A11* + ID_MODEL_FROM_DATABASE=AirXpert DWL-AG520 Wireless PCI Adapter(rev.B) + +pci:v00001186d00004000* + ID_MODEL_FROM_DATABASE=DL2000-based Gigabit Ethernet + +pci:v00001186d00004001* + ID_MODEL_FROM_DATABASE=DGE-550SX PCI-X Gigabit Ethernet Adapter + +pci:v00001186d00004200* + ID_MODEL_FROM_DATABASE=DFE-520TX Fast Ethernet PCI Adapter + +pci:v00001186d00004200sv00001186sd00001103* + ID_MODEL_FROM_DATABASE=DFE-520TX Fast Ethernet PCI Adapter ((rev. C1)) + +pci:v00001186d00004300* + ID_MODEL_FROM_DATABASE=DGE-528T Gigabit Ethernet Adapter + +pci:v00001186d00004300sv00001186sd00004300* + ID_MODEL_FROM_DATABASE=DGE-528T Gigabit Ethernet Adapter (DGE-528T PCI Gigabit Ethernet Adapter) + +pci:v00001186d00004300sv00001186sd00004B10* + ID_MODEL_FROM_DATABASE=DGE-528T Gigabit Ethernet Adapter (DGE-560T PCI Express (x1) Gigabit Ethernet Adapter) + +pci:v00001186d00004302* + ID_MODEL_FROM_DATABASE=DGE-530T Gigabit Ethernet Adapter (rev.C1) [Realtek RTL8169] + +pci:v00001186d00004B00* + ID_MODEL_FROM_DATABASE=DGE-560T PCI Express Gigabit Ethernet Adapter + +pci:v00001186d00004B01* + ID_MODEL_FROM_DATABASE=DGE-530T Gigabit Ethernet Adapter (rev 11) + +pci:v00001186d00004B02* + ID_MODEL_FROM_DATABASE=DGE-560SX PCI Express Gigabit Ethernet Adapter + +pci:v00001186d00004B03* + ID_MODEL_FROM_DATABASE=DGE-550T Gigabit Ethernet Adapter V.B1 + +pci:v00001186d00004C00* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter + +pci:v00001186d00004C00sv00001186sd00004C00* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter (DGE-530T Gigabit Ethernet Adapter) + +pci:v00001186d00008400* + ID_MODEL_FROM_DATABASE=D-Link DWL-650+ CardBus PC Card + +pci:v00001187* + ID_VENDOR_FROM_DATABASE=Advanced Technology Laboratories, Inc. + +pci:v00001188* + ID_VENDOR_FROM_DATABASE=Shima Seiki Manufacturing Ltd. + +pci:v00001189* + ID_VENDOR_FROM_DATABASE=Matsushita Electronics Co Ltd + +pci:v0000118A* + ID_VENDOR_FROM_DATABASE=Hilevel Technology + +pci:v0000118B* + ID_VENDOR_FROM_DATABASE=Hypertec Pty Limited + +pci:v0000118C* + ID_VENDOR_FROM_DATABASE=Corollary, Inc + +pci:v0000118Cd00000014* + ID_MODEL_FROM_DATABASE=PCIB [C-bus II to PCI bus host bridge chip] + +pci:v0000118Cd00001117* + ID_MODEL_FROM_DATABASE=Intel 8-way XEON Profusion Chipset [Cache Coherency Filter] + +pci:v0000118D* + ID_VENDOR_FROM_DATABASE=BitFlow Inc + +pci:v0000118Dd00000001* + ID_MODEL_FROM_DATABASE=Raptor-PCI framegrabber + +pci:v0000118Dd00000012* + ID_MODEL_FROM_DATABASE=Model 12 Road Runner Frame Grabber + +pci:v0000118Dd00000014* + ID_MODEL_FROM_DATABASE=Model 14 Road Runner Frame Grabber + +pci:v0000118Dd00000024* + ID_MODEL_FROM_DATABASE=Model 24 Road Runner Frame Grabber + +pci:v0000118Dd00000044* + ID_MODEL_FROM_DATABASE=Model 44 Road Runner Frame Grabber + +pci:v0000118Dd00000112* + ID_MODEL_FROM_DATABASE=Model 12 Road Runner Frame Grabber + +pci:v0000118Dd00000114* + ID_MODEL_FROM_DATABASE=Model 14 Road Runner Frame Grabber + +pci:v0000118Dd00000124* + ID_MODEL_FROM_DATABASE=Model 24 Road Runner Frame Grabber + +pci:v0000118Dd00000144* + ID_MODEL_FROM_DATABASE=Model 44 Road Runner Frame Grabber + +pci:v0000118Dd00000212* + ID_MODEL_FROM_DATABASE=Model 12 Road Runner Frame Grabber + +pci:v0000118Dd00000214* + ID_MODEL_FROM_DATABASE=Model 14 Road Runner Frame Grabber + +pci:v0000118Dd00000224* + ID_MODEL_FROM_DATABASE=Model 24 Road Runner Frame Grabber + +pci:v0000118Dd00000244* + ID_MODEL_FROM_DATABASE=Model 44 Road Runner Frame Grabber + +pci:v0000118Dd00000312* + ID_MODEL_FROM_DATABASE=Model 12 Road Runner Frame Grabber + +pci:v0000118Dd00000314* + ID_MODEL_FROM_DATABASE=Model 14 Road Runner Frame Grabber + +pci:v0000118Dd00000324* + ID_MODEL_FROM_DATABASE=Model 24 Road Runner Frame Grabber + +pci:v0000118Dd00000344* + ID_MODEL_FROM_DATABASE=Model 44 Road Runner Frame Grabber + +pci:v0000118E* + ID_VENDOR_FROM_DATABASE=Hermstedt GmbH + +pci:v0000118F* + ID_VENDOR_FROM_DATABASE=Green Logic + +pci:v00001190* + ID_VENDOR_FROM_DATABASE=Tripace + +pci:v00001190d0000C731* + ID_MODEL_FROM_DATABASE=TP-910/920/940 PCI Ultra(Wide) SCSI Adapter + +pci:v00001191* + ID_VENDOR_FROM_DATABASE=Artop Electronic Corp + +pci:v00001191d00000003* + ID_MODEL_FROM_DATABASE=SCSI Cache Host Adapter + +pci:v00001191d00000004* + ID_MODEL_FROM_DATABASE=ATP8400 + +pci:v00001191d00000005* + ID_MODEL_FROM_DATABASE=ATP850UF + +pci:v00001191d00000006* + ID_MODEL_FROM_DATABASE=ATP860 NO-BIOS + +pci:v00001191d00000007* + ID_MODEL_FROM_DATABASE=ATP860 + +pci:v00001191d00000008* + ID_MODEL_FROM_DATABASE=ATP865 NO-ROM + +pci:v00001191d00000009* + ID_MODEL_FROM_DATABASE=ATP865 + +pci:v00001191d0000000A* + ID_MODEL_FROM_DATABASE=ATP867-A + +pci:v00001191d0000000B* + ID_MODEL_FROM_DATABASE=ATP867-B + +pci:v00001191d0000000D* + ID_MODEL_FROM_DATABASE=ATP8620 + +pci:v00001191d0000000E* + ID_MODEL_FROM_DATABASE=ATP8620 + +pci:v00001191d00008002* + ID_MODEL_FROM_DATABASE=AEC6710 SCSI-2 Host Adapter + +pci:v00001191d00008010* + ID_MODEL_FROM_DATABASE=AEC6712UW SCSI + +pci:v00001191d00008020* + ID_MODEL_FROM_DATABASE=AEC6712U SCSI + +pci:v00001191d00008030* + ID_MODEL_FROM_DATABASE=AEC6712S SCSI + +pci:v00001191d00008040* + ID_MODEL_FROM_DATABASE=AEC6712D SCSI + +pci:v00001191d00008050* + ID_MODEL_FROM_DATABASE=AEC6712SUW SCSI + +pci:v00001191d00008060* + ID_MODEL_FROM_DATABASE=AEC6712 SCSI + +pci:v00001191d00008080* + ID_MODEL_FROM_DATABASE=AEC67160 SCSI + +pci:v00001191d00008081* + ID_MODEL_FROM_DATABASE=AEC67160S SCSI + +pci:v00001191d0000808A* + ID_MODEL_FROM_DATABASE=AEC67162 2-ch. LVD SCSI + +pci:v00001192* + ID_VENDOR_FROM_DATABASE=Densan Company Ltd + +pci:v00001193* + ID_VENDOR_FROM_DATABASE=Zeitnet Inc. + +pci:v00001193d00000001* + ID_MODEL_FROM_DATABASE=1221 + +pci:v00001193d00000002* + ID_MODEL_FROM_DATABASE=1225 + +pci:v00001194* + ID_VENDOR_FROM_DATABASE=Toucan Technology + +pci:v00001195* + ID_VENDOR_FROM_DATABASE=Ratoc System Inc + +pci:v00001196* + ID_VENDOR_FROM_DATABASE=Hytec Electronics Ltd + +pci:v00001197* + ID_VENDOR_FROM_DATABASE=Gage Applied Sciences, Inc. + +pci:v00001197d0000010C* + ID_MODEL_FROM_DATABASE=CompuScope 82G 8bit 2GS/s Analog Input Card + +pci:v00001198* + ID_VENDOR_FROM_DATABASE=Lambda Systems Inc + +pci:v00001199* + ID_VENDOR_FROM_DATABASE=Attachmate Corporation + +pci:v00001199d00000101* + ID_MODEL_FROM_DATABASE=Advanced ISCA/PCI Adapter + +pci:v0000119A* + ID_VENDOR_FROM_DATABASE=Mind Share, Inc. + +pci:v0000119B* + ID_VENDOR_FROM_DATABASE=Omega Micro Inc. + +pci:v0000119Bd00001221* + ID_MODEL_FROM_DATABASE=82C092G + +pci:v0000119C* + ID_VENDOR_FROM_DATABASE=Information Technology Inst. + +pci:v0000119D* + ID_VENDOR_FROM_DATABASE=Bug, Inc. Sapporo Japan + +pci:v0000119E* + ID_VENDOR_FROM_DATABASE=Fujitsu Microelectronics Ltd. + +pci:v0000119Ed00000001* + ID_MODEL_FROM_DATABASE=FireStream 155 + +pci:v0000119Ed00000003* + ID_MODEL_FROM_DATABASE=FireStream 50 + +pci:v0000119F* + ID_VENDOR_FROM_DATABASE=Bull HN Information Systems + +pci:v000011A0* + ID_VENDOR_FROM_DATABASE=Convex Computer Corporation + +pci:v000011A1* + ID_VENDOR_FROM_DATABASE=Hamamatsu Photonics K.K. + +pci:v000011A2* + ID_VENDOR_FROM_DATABASE=Sierra Research and Technology + +pci:v000011A3* + ID_VENDOR_FROM_DATABASE=Deuretzbacher GmbH & Co. Eng. KG + +pci:v000011A4* + ID_VENDOR_FROM_DATABASE=Barco Graphics NV + +pci:v000011A5* + ID_VENDOR_FROM_DATABASE=Microunity Systems Eng. Inc + +pci:v000011A6* + ID_VENDOR_FROM_DATABASE=Pure Data Ltd. + +pci:v000011A7* + ID_VENDOR_FROM_DATABASE=Power Computing Corp. + +pci:v000011A8* + ID_VENDOR_FROM_DATABASE=Systech Corp. + +pci:v000011A9* + ID_VENDOR_FROM_DATABASE=InnoSys Inc. + +pci:v000011A9d00004240* + ID_MODEL_FROM_DATABASE=AMCC S933Q Intelligent Serial Card + +pci:v000011AA* + ID_VENDOR_FROM_DATABASE=Actel + +pci:v000011AB* + ID_VENDOR_FROM_DATABASE=Marvell Technology Group Ltd. + +pci:v000011ABd00000146* + ID_MODEL_FROM_DATABASE=GT-64010/64010A System Controller + +pci:v000011ABd00000F53* + ID_MODEL_FROM_DATABASE=88E6318 Link Street network controller + +pci:v000011ABd000011AB* + ID_MODEL_FROM_DATABASE=MV88SE614x SATA II PCI-E controller + +pci:v000011ABd0000138F* + ID_MODEL_FROM_DATABASE=W8300 802.11 Adapter (rev 07) + +pci:v000011ABd00001FA6* + ID_MODEL_FROM_DATABASE=Marvell W8300 802.11 Adapter + +pci:v000011ABd00001FA6sv00001186sd00003B08* + ID_MODEL_FROM_DATABASE=Marvell W8300 802.11 Adapter (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.A1)) + +pci:v000011ABd00001FA7* + ID_MODEL_FROM_DATABASE=88W8310 and 88W8000G [Libertas] 802.11g client chipset + +pci:v000011ABd00001FAA* + ID_MODEL_FROM_DATABASE=88w8335 [Libertas] 802.11b/g Wireless + +pci:v000011ABd00001FAAsv00001385sd00004E00* + ID_MODEL_FROM_DATABASE=88w8335 [Libertas] 802.11b/g Wireless (WG511v2 54 Mbps Wireless PC Card) + +pci:v000011ABd00001FAAsv00001385sd00006B00* + ID_MODEL_FROM_DATABASE=88w8335 [Libertas] 802.11b/g Wireless (WG311v3 802.11g Wireless PCI Adapter) + +pci:v000011ABd00001FAAsv00001737sd00000040* + ID_MODEL_FROM_DATABASE=88w8335 [Libertas] 802.11b/g Wireless (WPC54G v5 802.11g Wireless-G Notebook Adapter) + +pci:v000011ABd00002211* + ID_MODEL_FROM_DATABASE=88SB2211 PCI Express to PCI Bridge + +pci:v000011ABd00002A01* + ID_MODEL_FROM_DATABASE=88W8335 [Libertas] 802.11b/g Wireless + +pci:v000011ABd00002A02* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A02sv000007D1sd00003B02* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless (DIR-615 rev. A1 Mini PCI Wireless Module) + +pci:v000011ABd00002A02sv00001385sd00007C00* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless (WN511T RangeMax Next 300 Mbps Wireless PC Card) + +pci:v000011ABd00002A02sv00001385sd00007C01* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless (WN511T RangeMax Next 300 Mbps Wireless Notebook Adapter) + +pci:v000011ABd00002A02sv00001385sd00007E00* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless (WN311T RangeMax Next 300 Mbps Wireless PCI Adapter) + +pci:v000011ABd00002A02sv00001799sd0000801B* + ID_MODEL_FROM_DATABASE=88W8361 [TopDog] 802.11n Wireless (F5D8011 v2 802.11n N1 Wireless Notebook Card) + +pci:v000011ABd00002A08* + ID_MODEL_FROM_DATABASE=88W8362e [TopDog] 802.11a/b/g/n Wireless + +pci:v000011ABd00002A0A* + ID_MODEL_FROM_DATABASE=88W8363 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A0C* + ID_MODEL_FROM_DATABASE=88W8363 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A24* + ID_MODEL_FROM_DATABASE=88W8363 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A2B* + ID_MODEL_FROM_DATABASE=88W8687 [TopDog] 802.11b/g Wireless + +pci:v000011ABd00002A30* + ID_MODEL_FROM_DATABASE=88W8687 [TopDog] 802.11b/g Wireless + +pci:v000011ABd00002A40* + ID_MODEL_FROM_DATABASE=88W8366 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A41* + ID_MODEL_FROM_DATABASE=88W8366 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A42* + ID_MODEL_FROM_DATABASE=88W8366 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A43* + ID_MODEL_FROM_DATABASE=88W8366 [TopDog] 802.11n Wireless + +pci:v000011ABd00002A55* + ID_MODEL_FROM_DATABASE=88W8864 [Avastar] 802.11ac Wireless + +pci:v000011ABd00002B36* + ID_MODEL_FROM_DATABASE=88W8764 [Avastar] 802.11n Wireless + +pci:v000011ABd00002B38* + ID_MODEL_FROM_DATABASE=88W8897 [AVASTAR] 802.11ac Wireless + +pci:v000011ABd00002B40* + ID_MODEL_FROM_DATABASE=88W8964 [Avastar] 802.11ac Wireless + +pci:v000011ABd00004101* + ID_MODEL_FROM_DATABASE=OLPC Cafe Controller Secure Digital Controller + +pci:v000011ABd00004320* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller + +pci:v000011ABd00004320sv00001019sd00000F38* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (ECS)) + +pci:v000011ABd00004320sv00001019sd00008001* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (ECS)) + +pci:v000011ABd00004320sv00001043sd0000173C* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Asus)) + +pci:v000011ABd00004320sv00001043sd0000811A* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Asus)) + +pci:v000011ABd00004320sv0000105Bsd00000C19* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Foxconn)) + +pci:v000011ABd00004320sv000010B8sd0000B452* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (EZ Card 1000 (SMC9452TXV.2)) + +pci:v000011ABd00004320sv000011ABsd00000121* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell RDK-8001) + +pci:v000011ABd00004320sv000011ABsd00000321* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell RDK-8003) + +pci:v000011ABd00004320sv000011ABsd00001021* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell RDK-8010) + +pci:v000011ABd00004320sv000011ABsd00004320* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell Yukon Gigabit Ethernet 10/100/1000Baset-T Constroller (Asus)) + +pci:v000011ABd00004320sv000011ABsd00005021* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Controller (64 bit)) + +pci:v000011ABd00004320sv000011ABsd00009521* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell Yukon Gigabit Ethernet 10/100/1000Base-T Controller (32 bit)) + +pci:v000011ABd00004320sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Gigabyte)) + +pci:v000011ABd00004320sv0000147Bsd00001406* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Abit)) + +pci:v000011ABd00004320sv000015D4sd00000047* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Iwill)) + +pci:v000011ABd00004320sv00001695sd00009025* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Epox)) + +pci:v000011ABd00004320sv000017F2sd00001C03* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Albatron)) + +pci:v000011ABd00004320sv0000270Fsd00002803* + ID_MODEL_FROM_DATABASE=88E8001 Gigabit Ethernet Controller (Marvell 88E8001 Gigabit Ethernet Controller (Chaintech)) + +pci:v000011ABd00004340* + ID_MODEL_FROM_DATABASE=88E8021 PCI-X IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004341* + ID_MODEL_FROM_DATABASE=88E8022 PCI-X IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004342* + ID_MODEL_FROM_DATABASE=88E8061 PCI-E IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004343* + ID_MODEL_FROM_DATABASE=88E8062 PCI-E IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004344* + ID_MODEL_FROM_DATABASE=88E8021 PCI-X IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004345* + ID_MODEL_FROM_DATABASE=88E8022 PCI-X IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004346* + ID_MODEL_FROM_DATABASE=88E8061 PCI-E IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004347* + ID_MODEL_FROM_DATABASE=88E8062 PCI-E IPMI Gigabit Ethernet Controller + +pci:v000011ABd00004347sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=88E8062 PCI-E IPMI Gigabit Ethernet Controller (Telum ASLP10 PrAMC Gigabit Ethernet) + +pci:v000011ABd00004350* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004350sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (Toshiba)) + +pci:v000011ABd00004350sv000011ABsd00003521* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell RDK-8035) + +pci:v000011ABd00004350sv00001854sd0000000D* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd0000000E* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd0000000F* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000011* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000012* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000016* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000017* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000018* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000019* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd0000001C* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd0000001E* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004350sv00001854sd00000020* + ID_MODEL_FROM_DATABASE=88E8035 PCI-E Fast Ethernet Controller (Marvell 88E8035 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004351sv0000107Bsd00004009* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Wistron)) + +pci:v000011ABd00004351sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Panasonic)) + +pci:v000011ABd00004351sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Toshiba)) + +pci:v000011ABd00004351sv00001179sd0000FF00* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Compal)) + +pci:v000011ABd00004351sv00001179sd0000FF10* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Inventec)) + +pci:v000011ABd00004351sv000011ABsd00003621* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell RDK-8036) + +pci:v000011ABd00004351sv000013D1sd0000AC12* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Abocom EFE3K - 10/100 Ethernet Expresscard) + +pci:v000011ABd00004351sv0000161Fsd0000203D* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (Arima)) + +pci:v000011ABd00004351sv00001854sd0000000D* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd0000000E* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd0000000F* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000011* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000012* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000016* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000017* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000018* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000019* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd0000001C* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd0000001E* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004351sv00001854sd00000020* + ID_MODEL_FROM_DATABASE=88E8036 PCI-E Fast Ethernet Controller (Marvell 88E8036 Fast Ethernet Controller (LGE)) + +pci:v000011ABd00004352* + ID_MODEL_FROM_DATABASE=88E8038 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004353* + ID_MODEL_FROM_DATABASE=88E8039 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004353sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=88E8039 PCI-E Fast Ethernet Controller (VAIO VGN-NR120E) + +pci:v000011ABd00004354* + ID_MODEL_FROM_DATABASE=88E8040 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004354sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=88E8040 PCI-E Fast Ethernet Controller (R730 Laptop) + +pci:v000011ABd00004354sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=88E8040 PCI-E Fast Ethernet Controller (Notebook N150P) + +pci:v000011ABd00004355* + ID_MODEL_FROM_DATABASE=88E8040T PCI-E Fast Ethernet Controller + +pci:v000011ABd00004355sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=88E8040T PCI-E Fast Ethernet Controller (Satellite P305D-S8995E) + +pci:v000011ABd00004356* + ID_MODEL_FROM_DATABASE=88EC033 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004357* + ID_MODEL_FROM_DATABASE=88E8042 PCI-E Fast Ethernet Controller + +pci:v000011ABd0000435A* + ID_MODEL_FROM_DATABASE=88E8048 PCI-E Fast Ethernet Controller + +pci:v000011ABd00004360* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller + +pci:v000011ABd00004360sv00001043sd00008134* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (Asus)) + +pci:v000011ABd00004360sv0000107Bsd00004009* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (Wistron)) + +pci:v000011ABd00004360sv000011ABsd00005221* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell RDK-8052) + +pci:v000011ABd00004360sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (Gigabyte)) + +pci:v000011ABd00004360sv00001462sd0000052C* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (MSI)) + +pci:v000011ABd00004360sv00001849sd00008052* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (ASRock)) + +pci:v000011ABd00004360sv0000A0A0sd00000509* + ID_MODEL_FROM_DATABASE=88E8052 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8052 Gigabit Ethernet Controller (Aopen)) + +pci:v000011ABd00004361* + ID_MODEL_FROM_DATABASE=88E8050 PCI-E ASF Gigabit Ethernet Controller + +pci:v000011ABd00004361sv0000107Bsd00003015* + ID_MODEL_FROM_DATABASE=88E8050 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8050 Gigabit Ethernet Controller (Gateway)) + +pci:v000011ABd00004361sv000011ABsd00005021* + ID_MODEL_FROM_DATABASE=88E8050 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8050 Gigabit Ethernet Controller (Intel)) + +pci:v000011ABd00004361sv00008086sd00003063* + ID_MODEL_FROM_DATABASE=88E8050 PCI-E ASF Gigabit Ethernet Controller (D925XCVLK mainboard) + +pci:v000011ABd00004361sv00008086sd00003439* + ID_MODEL_FROM_DATABASE=88E8050 PCI-E ASF Gigabit Ethernet Controller (Marvell 88E8050 Gigabit Ethernet Controller (Intel)) + +pci:v000011ABd00004362* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004362sv0000103Csd00002A0D* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Asus)) + +pci:v000011ABd00004362sv00001043sd00008142* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet controller PCIe (Asus)) + +pci:v000011ABd00004362sv0000109Fsd00003197* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Trigem)) + +pci:v000011ABd00004362sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Panasonic)) + +pci:v000011ABd00004362sv000010FDsd0000A430* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (SOYO)) + +pci:v000011ABd00004362sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Toshiba)) + +pci:v000011ABd00004362sv00001179sd0000FF00* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Compal)) + +pci:v000011ABd00004362sv00001179sd0000FF10* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Inventec)) + +pci:v000011ABd00004362sv000011ABsd00005321* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell RDK-8053) + +pci:v000011ABd00004362sv00001297sd0000C240* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Shuttle)) + +pci:v000011ABd00004362sv00001297sd0000C241* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Shuttle)) + +pci:v000011ABd00004362sv00001297sd0000C242* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Shuttle)) + +pci:v000011ABd00004362sv00001297sd0000C243* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Shuttle)) + +pci:v000011ABd00004362sv00001297sd0000C244* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Shuttle)) + +pci:v000011ABd00004362sv000013D1sd0000AC11* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (EGE5K - Giga Ethernet Expresscard) + +pci:v000011ABd00004362sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Gigabyte)) + +pci:v000011ABd00004362sv00001462sd0000058C* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (MSI)) + +pci:v000011ABd00004362sv000014C0sd00000012* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Compal)) + +pci:v000011ABd00004362sv00001558sd000004A0* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Clevo)) + +pci:v000011ABd00004362sv000015BDsd00001003* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (DFI)) + +pci:v000011ABd00004362sv0000161Fsd0000203C* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Arima)) + +pci:v000011ABd00004362sv0000161Fsd0000203D* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Arima)) + +pci:v000011ABd00004362sv00001695sd00009029* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Epox)) + +pci:v000011ABd00004362sv000017F2sd00002C08* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Albatron)) + +pci:v000011ABd00004362sv000017FFsd00000585* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Quanta)) + +pci:v000011ABd00004362sv00001849sd00008053* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (ASRock)) + +pci:v000011ABd00004362sv00001854sd0000000B* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd0000000C* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000010* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000013* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000014* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000015* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd0000001A* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd0000001B* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd0000001D* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd0000001F* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000021* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv00001854sd00000022* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (LGE)) + +pci:v000011ABd00004362sv0000270Fsd00002801* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Chaintech)) + +pci:v000011ABd00004362sv0000A0A0sd00000506* + ID_MODEL_FROM_DATABASE=88E8053 PCI-E Gigabit Ethernet Controller (Marvell 88E8053 Gigabit Ethernet Controller (Aopen)) + +pci:v000011ABd00004363* + ID_MODEL_FROM_DATABASE=88E8055 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004364* + ID_MODEL_FROM_DATABASE=88E8056 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004364sv00001043sd000081F8* + ID_MODEL_FROM_DATABASE=88E8056 PCI-E Gigabit Ethernet Controller (Motherboard) + +pci:v000011ABd00004364sv000011BAsd000000BA* + ID_MODEL_FROM_DATABASE=88E8056 PCI-E Gigabit Ethernet Controller (8056 Gigabit Ethernet Controller) + +pci:v000011ABd00004365* + ID_MODEL_FROM_DATABASE=88E8070 based Ethernet Controller + +pci:v000011ABd00004366* + ID_MODEL_FROM_DATABASE=88EC036 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004367* + ID_MODEL_FROM_DATABASE=88EC032 Ethernet Controller + +pci:v000011ABd00004368* + ID_MODEL_FROM_DATABASE=88EC034 Ethernet Controller + +pci:v000011ABd00004369* + ID_MODEL_FROM_DATABASE=88EC042 Ethernet Controller + +pci:v000011ABd0000436A* + ID_MODEL_FROM_DATABASE=88E8058 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd0000436Asv000011ABsd000000BA* + ID_MODEL_FROM_DATABASE=88E8058 PCI-E Gigabit Ethernet Controller (Imac 8,1 Wired Ethernet Adapter) + +pci:v000011ABd0000436B* + ID_MODEL_FROM_DATABASE=88E8071 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd0000436C* + ID_MODEL_FROM_DATABASE=88E8072 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd0000436D* + ID_MODEL_FROM_DATABASE=88E8055 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004370* + ID_MODEL_FROM_DATABASE=88E8075 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004380* + ID_MODEL_FROM_DATABASE=88E8057 PCI-E Gigabit Ethernet Controller + +pci:v000011ABd00004381* + ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB] + +pci:v000011ABd00004611* + ID_MODEL_FROM_DATABASE=GT-64115 System Controller + +pci:v000011ABd00004620* + ID_MODEL_FROM_DATABASE=GT-64120/64120A/64121A System Controller + +pci:v000011ABd00004801* + ID_MODEL_FROM_DATABASE=GT-48001 + +pci:v000011ABd00005005* + ID_MODEL_FROM_DATABASE=Belkin F5D5005 Gigabit Desktop Network PCI Card + +pci:v000011ABd00005040* + ID_MODEL_FROM_DATABASE=MV88SX5040 4-port SATA I PCI-X Controller + +pci:v000011ABd00005041* + ID_MODEL_FROM_DATABASE=MV88SX5041 4-port SATA I PCI-X Controller + +pci:v000011ABd00005080* + ID_MODEL_FROM_DATABASE=MV88SX5080 8-port SATA I PCI-X Controller + +pci:v000011ABd00005081* + ID_MODEL_FROM_DATABASE=MV88SX5081 8-port SATA I PCI-X Controller + +pci:v000011ABd00005181* + ID_MODEL_FROM_DATABASE=88f5181 [Orion-1] ARM SoC + +pci:v000011ABd00005182* + ID_MODEL_FROM_DATABASE=88f5182 [Orion-NAS] ARM SoC + +pci:v000011ABd00005281* + ID_MODEL_FROM_DATABASE=88f5281 [Orion-2] ARM SoC + +pci:v000011ABd00006041* + ID_MODEL_FROM_DATABASE=MV88SX6041 4-port SATA II PCI-X Controller + +pci:v000011ABd00006042* + ID_MODEL_FROM_DATABASE=88SX6042 PCI-X 4-Port SATA-II + +pci:v000011ABd00006081* + ID_MODEL_FROM_DATABASE=MV88SX6081 8-port SATA II PCI-X Controller + +pci:v000011ABd00006101* + ID_MODEL_FROM_DATABASE=88SE6101/6102 single-port PATA133 interface + +pci:v000011ABd00006101sv00001043sd000082E0* + ID_MODEL_FROM_DATABASE=88SE6101/6102 single-port PATA133 interface (P5K PRO Motherboard) + +pci:v000011ABd00006111* + ID_MODEL_FROM_DATABASE=88SE6111 1-port PATA133(IDE) and 1-port SATA II Controllers + +pci:v000011ABd00006121* + ID_MODEL_FROM_DATABASE=88SE6121 SATA II / PATA Controller + +pci:v000011ABd00006141* + ID_MODEL_FROM_DATABASE=88SE614x SATA II PCI-E controller + +pci:v000011ABd00006145* + ID_MODEL_FROM_DATABASE=88SE6145 SATA II PCI-E controller + +pci:v000011ABd00006180* + ID_MODEL_FROM_DATABASE=88F6180 [Kirkwood] ARM SoC + +pci:v000011ABd00006192* + ID_MODEL_FROM_DATABASE=88F6190/6192 [Kirkwood] ARM SoC + +pci:v000011ABd00006281* + ID_MODEL_FROM_DATABASE=88F6281 [Kirkwood] ARM SoC + +pci:v000011ABd00006381* + ID_MODEL_FROM_DATABASE=MV78xx0 [Discovery Innovation] ARM SoC + +pci:v000011ABd00006440* + ID_MODEL_FROM_DATABASE=88SE6440 SAS/SATA PCIe controller + +pci:v000011ABd00006450* + ID_MODEL_FROM_DATABASE=64560 System Controller + +pci:v000011ABd00006460* + ID_MODEL_FROM_DATABASE=MV64360/64361/64362 System Controller + +pci:v000011ABd00006480* + ID_MODEL_FROM_DATABASE=MV64460/64461/64462 System Controller + +pci:v000011ABd00006480sv00001775sd0000C200* + ID_MODEL_FROM_DATABASE=MV64460/64461/64462 System Controller (C2K CompactPCI single board computer) + +pci:v000011ABd00006485* + ID_MODEL_FROM_DATABASE=MV64460/64461/64462 System Controller, Revision B + +pci:v000011ABd00007042* + ID_MODEL_FROM_DATABASE=88SX7042 PCI-e 4-port SATA-II + +pci:v000011ABd00007042sv000016B8sd0000434B* + ID_MODEL_FROM_DATABASE=88SX7042 PCI-e 4-port SATA-II (Tempo SATA E4P) + +pci:v000011ABd00007810* + ID_MODEL_FROM_DATABASE=MV78100 [Discovery Innovation] ARM SoC + +pci:v000011ABd00007820* + ID_MODEL_FROM_DATABASE=MV78200 [Discovery Innovation] ARM SoC + +pci:v000011ABd00007823* + ID_MODEL_FROM_DATABASE=MV78230 [Armada XP] ARM SoC + +pci:v000011ABd00007846* + ID_MODEL_FROM_DATABASE=88F6820 [Armada 385] ARM SoC + +pci:v000011ABd0000F003* + ID_MODEL_FROM_DATABASE=GT-64010 Primary Image Piranha Image Generator + +pci:v000011AC* + ID_VENDOR_FROM_DATABASE=Canon Information Systems Research Aust. + +pci:v000011AD* + ID_VENDOR_FROM_DATABASE=Lite-On Communications Inc + +pci:v000011ADd00000002* + ID_MODEL_FROM_DATABASE=LNE100TX + +pci:v000011ADd00000002sv000011ADsd00000002* + ID_MODEL_FROM_DATABASE=LNE100TX + +pci:v000011ADd00000002sv000011ADsd00000003* + ID_MODEL_FROM_DATABASE=LNE100TX + +pci:v000011ADd00000002sv000011ADsd0000F003* + ID_MODEL_FROM_DATABASE=LNE100TX + +pci:v000011ADd00000002sv000011ADsd0000FFFF* + ID_MODEL_FROM_DATABASE=LNE100TX + +pci:v000011ADd00000002sv00001385sd0000F004* + ID_MODEL_FROM_DATABASE=LNE100TX (FA310TX) + +pci:v000011ADd00000002sv00002646sd0000F002* + ID_MODEL_FROM_DATABASE=LNE100TX (KNE110TX EtheRx Fast Ethernet) + +pci:v000011ADd0000C115* + ID_MODEL_FROM_DATABASE=LNE100TX [Linksys EtherFast 10/100] + +pci:v000011ADd0000C115sv000011ADsd0000C001* + ID_MODEL_FROM_DATABASE=LNE100TX [Linksys EtherFast 10/100] (LNE100TX [ver 2.0]) + +pci:v000011ADd0000C115sv00002646sd0000000B* + ID_MODEL_FROM_DATABASE=LNE100TX [Linksys EtherFast 10/100] (KNE111TX) + +pci:v000011AE* + ID_VENDOR_FROM_DATABASE=Aztech System Ltd + +pci:v000011AF* + ID_VENDOR_FROM_DATABASE=Avid Technology Inc. + +pci:v000011AFd00000001* + ID_MODEL_FROM_DATABASE=Cinema + +pci:v000011AFd0000EE40* + ID_MODEL_FROM_DATABASE=Digidesign Audiomedia III + +pci:v000011B0* + ID_VENDOR_FROM_DATABASE=V3 Semiconductor Inc. + +pci:v000011B0d00000002* + ID_MODEL_FROM_DATABASE=V300PSC + +pci:v000011B0d00000292* + ID_MODEL_FROM_DATABASE=V292PBC [Am29030/40 Bridge] + +pci:v000011B0d00000960* + ID_MODEL_FROM_DATABASE=V96xPBC + +pci:v000011B0d0000880A* + ID_MODEL_FROM_DATABASE=Deltacast Delta-HD-22 + +pci:v000011B0d0000C960* + ID_MODEL_FROM_DATABASE=V96DPC + +pci:v000011B1* + ID_VENDOR_FROM_DATABASE=Apricot Computers + +pci:v000011B2* + ID_VENDOR_FROM_DATABASE=Eastman Kodak + +pci:v000011B3* + ID_VENDOR_FROM_DATABASE=Barr Systems Inc. + +pci:v000011B4* + ID_VENDOR_FROM_DATABASE=Leitch Technology International + +pci:v000011B5* + ID_VENDOR_FROM_DATABASE=Radstone Technology Plc + +pci:v000011B6* + ID_VENDOR_FROM_DATABASE=United Video Corp + +pci:v000011B7* + ID_VENDOR_FROM_DATABASE=Motorola + +pci:v000011B8* + ID_VENDOR_FROM_DATABASE=XPoint Technologies, Inc + +pci:v000011B8d00000001* + ID_MODEL_FROM_DATABASE=Quad PeerMaster + +pci:v000011B9* + ID_VENDOR_FROM_DATABASE=Pathlight Technology Inc. + +pci:v000011B9d0000C0ED* + ID_MODEL_FROM_DATABASE=SSA Controller + +pci:v000011BA* + ID_VENDOR_FROM_DATABASE=Videotron Corp + +pci:v000011BB* + ID_VENDOR_FROM_DATABASE=Pyramid Technology + +pci:v000011BC* + ID_VENDOR_FROM_DATABASE=Network Peripherals Inc + +pci:v000011BCd00000001* + ID_MODEL_FROM_DATABASE=NP-PCI + +pci:v000011BD* + ID_VENDOR_FROM_DATABASE=Pinnacle Systems Inc. + +pci:v000011BDd0000002E* + ID_MODEL_FROM_DATABASE=PCTV 40i + +pci:v000011BDd00000040* + ID_MODEL_FROM_DATABASE=Royal TS Function 1 + +pci:v000011BDd00000040sv000011BDsd00000044* + ID_MODEL_FROM_DATABASE=Royal TS Function 1 (PCTV 2000i Dual DVB-T Pro PCI Tuner 1) + +pci:v000011BDd00000040sv000011BDsd00000045* + ID_MODEL_FROM_DATABASE=Royal TS Function 1 (PCTV Dual Sat Pro PCI 4000i Tuner 1) + +pci:v000011BDd00000041* + ID_MODEL_FROM_DATABASE=RoyalTS Function 2 + +pci:v000011BDd00000041sv000011BDsd00000044* + ID_MODEL_FROM_DATABASE=RoyalTS Function 2 (PCTV 2000i Dual DVB-T Pro PCI Tuner 2) + +pci:v000011BDd00000041sv000011BDsd00000045* + ID_MODEL_FROM_DATABASE=RoyalTS Function 2 (PCTV Dual Sat Pro PCI 4000i Tuner 2) + +pci:v000011BDd00000042* + ID_MODEL_FROM_DATABASE=Royal TS Function 3 + +pci:v000011BDd00000042sv000011BDsd00000044* + ID_MODEL_FROM_DATABASE=Royal TS Function 3 (PCTV 2000i Dual DVB-T Pro PCI Common) + +pci:v000011BDd00000042sv000011BDsd00000045* + ID_MODEL_FROM_DATABASE=Royal TS Function 3 (PCTV Dual Sat Pro PCI 4000i Common) + +pci:v000011BDd00000051* + ID_MODEL_FROM_DATABASE=PCTV HD 800i + +pci:v000011BDd0000BEDE* + ID_MODEL_FROM_DATABASE=AV/DV Studio Capture Card + +pci:v000011BE* + ID_VENDOR_FROM_DATABASE=International Microcircuits Inc + +pci:v000011BF* + ID_VENDOR_FROM_DATABASE=Astrodesign, Inc. + +pci:v000011C0* + ID_VENDOR_FROM_DATABASE=Hewlett Packard + +pci:v000011C1* + ID_VENDOR_FROM_DATABASE=LSI Corporation + +pci:v000011C1d00000440* + ID_MODEL_FROM_DATABASE=56k WinModem + +pci:v000011C1d00000440sv00001033sd00008015* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv00001033sd00008047* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv00001033sd0000804F* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000010CFsd0000102C* + ID_MODEL_FROM_DATABASE=56k WinModem (LB LT Modem V.90 56k) + +pci:v000011C1d00000440sv000010CFsd0000104A* + ID_MODEL_FROM_DATABASE=56k WinModem (BIBLO LT Modem 56k) + +pci:v000011C1d00000440sv000010CFsd0000105F* + ID_MODEL_FROM_DATABASE=56k WinModem (LB2 LT Modem V.90 56k) + +pci:v000011C1d00000440sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=56k WinModem (Internal V.90 Modem) + +pci:v000011C1d00000440sv000011C1sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv0000122Dsd00004101* + ID_MODEL_FROM_DATABASE=56k WinModem (MDP7800-U Modem) + +pci:v000011C1d00000440sv0000122Dsd00004102* + ID_MODEL_FROM_DATABASE=56k WinModem (MDP7800SP-U Modem) + +pci:v000011C1d00000440sv000013E0sd00000040* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000013E0sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000013E0sd00000441* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000013E0sd00000450* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000013E0sd0000F100* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv000013E0sd0000F101* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000440sv0000144Dsd00002101* + ID_MODEL_FROM_DATABASE=56k WinModem (LT56PV Modem) + +pci:v000011C1d00000440sv0000149Fsd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000441* + ID_MODEL_FROM_DATABASE=56k WinModem + +pci:v000011C1d00000441sv00001033sd0000804D* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv00001033sd00008065* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv00001092sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (Supra 56i) + +pci:v000011C1d00000441sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=56k WinModem (Internal V.90 Modem) + +pci:v000011C1d00000441sv000011C1sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000011C1sd00000441* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv0000122Dsd00004100* + ID_MODEL_FROM_DATABASE=56k WinModem (MDP7800-U Modem) + +pci:v000011C1d00000441sv000013E0sd00000040* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000013E0sd00000100* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000013E0sd00000410* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000013E0sd00000420* + ID_MODEL_FROM_DATABASE=56k WinModem (TelePath Internet 56k WinModem) + +pci:v000011C1d00000441sv000013E0sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000013E0sd00000443* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv000013E0sd0000F102* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv00001416sd00009804* + ID_MODEL_FROM_DATABASE=56k WinModem (CommWave 56k Modem) + +pci:v000011C1d00000441sv0000141Dsd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000441sv0000144Fsd00000441* + ID_MODEL_FROM_DATABASE=56k WinModem (Lucent 56k V.90 DF Modem) + +pci:v000011C1d00000441sv0000144Fsd00000449* + ID_MODEL_FROM_DATABASE=56k WinModem (Lucent 56k V.90 DF Modem) + +pci:v000011C1d00000441sv0000144Fsd0000110D* + ID_MODEL_FROM_DATABASE=56k WinModem (Lucent Win Modem) + +pci:v000011C1d00000441sv00001468sd00000441* + ID_MODEL_FROM_DATABASE=56k WinModem (Presario 56k V.90 DF Modem) + +pci:v000011C1d00000441sv00001668sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (Lucent Win Modem) + +pci:v000011C1d00000442* + ID_MODEL_FROM_DATABASE=56k WinModem + +pci:v000011C1d00000442sv000011C1sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv000011C1sd00000442* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv000013E0sd00000412* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv000013E0sd00000442* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv000013FCsd00002471* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv0000144Dsd00002104* + ID_MODEL_FROM_DATABASE=56k WinModem (LT56PT Modem) + +pci:v000011C1d00000442sv0000144Fsd00001104* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv0000149Fsd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000442sv00001668sd00000440* + ID_MODEL_FROM_DATABASE=56k WinModem (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d00000443* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000444* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000445* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000445sv00008086sd00002203* + ID_MODEL_FROM_DATABASE=LT WinModem (PRO/100+ MiniPCI (probably an Ambit U98.003.C.00 combo card)) + +pci:v000011C1d00000445sv00008086sd00002204* + ID_MODEL_FROM_DATABASE=LT WinModem (PRO/100+ MiniPCI on Armada E500) + +pci:v000011C1d00000446* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000447* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000448* + ID_MODEL_FROM_DATABASE=WinModem 56k + +pci:v000011C1d00000448sv00001014sd00000131* + ID_MODEL_FROM_DATABASE=WinModem 56k (Lucent Win Modem) + +pci:v000011C1d00000448sv00001033sd00008066* + ID_MODEL_FROM_DATABASE=WinModem 56k (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000448sv000013E0sd00000030* + ID_MODEL_FROM_DATABASE=WinModem 56k (56k Voice Modem) + +pci:v000011C1d00000448sv000013E0sd00000040* + ID_MODEL_FROM_DATABASE=WinModem 56k (LT WinModem 56k Data+Fax+Voice+Dsvd) + +pci:v000011C1d00000448sv00001668sd00002400* + ID_MODEL_FROM_DATABASE=WinModem 56k (LT WinModem 56k (MiniPCI Ethernet+Modem)) + +pci:v000011C1d00000449* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k + +pci:v000011C1d00000449sv00000E11sd0000B14D* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (56k V.90 Modem) + +pci:v000011C1d00000449sv00001014sd0000018C* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (ThinkPad 600X) + +pci:v000011C1d00000449sv000013E0sd00000020* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (LT WinModem 56k Data+Fax) + +pci:v000011C1d00000449sv000013E0sd00000041* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (TelePath Internet 56k WinModem) + +pci:v000011C1d00000449sv00001436sd00000440* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (Lucent Win Modem) + +pci:v000011C1d00000449sv0000144Fsd00000449* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (Lucent 56k V.90 DFi Modem) + +pci:v000011C1d00000449sv00001468sd00000410* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (IBM ThinkPad T23) + +pci:v000011C1d00000449sv00001468sd00000440* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (Lucent Win Modem) + +pci:v000011C1d00000449sv00001468sd00000449* + ID_MODEL_FROM_DATABASE=L56xM+S [Mars-2] WinModem 56k (Presario 56k V.90 DFi Modem) + +pci:v000011C1d0000044A* + ID_MODEL_FROM_DATABASE=F-1156IV WinModem (V90, 56KFlex) + +pci:v000011C1d0000044Asv000010CFsd00001072* + ID_MODEL_FROM_DATABASE=F-1156IV WinModem (V90, 56KFlex) (LB Global LT Modem) + +pci:v000011C1d0000044Asv000013E0sd00000012* + ID_MODEL_FROM_DATABASE=F-1156IV WinModem (V90, 56KFlex) (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d0000044Asv000013E0sd00000042* + ID_MODEL_FROM_DATABASE=F-1156IV WinModem (V90, 56KFlex) (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d0000044Asv0000144Fsd00001005* + ID_MODEL_FROM_DATABASE=F-1156IV WinModem (V90, 56KFlex) (LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd) + +pci:v000011C1d0000044B* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000044C* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000044D* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000044E* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000044F* + ID_MODEL_FROM_DATABASE=V90 WildWire Modem + +pci:v000011C1d00000450* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000450sv00001033sd000080A8* + ID_MODEL_FROM_DATABASE=LT WinModem (Versa Note Vxi) + +pci:v000011C1d00000450sv0000144Fsd00004005* + ID_MODEL_FROM_DATABASE=LT WinModem (Magnia SG20) + +pci:v000011C1d00000450sv00001468sd00000450* + ID_MODEL_FROM_DATABASE=LT WinModem (Evo N600c) + +pci:v000011C1d00000451* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000452* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000453* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000454* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000455* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000456* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000457* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000458* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000459* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000045A* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d0000045C* + ID_MODEL_FROM_DATABASE=LT WinModem + +pci:v000011C1d00000461* + ID_MODEL_FROM_DATABASE=V90 WildWire Modem + +pci:v000011C1d00000462* + ID_MODEL_FROM_DATABASE=V90 WildWire Modem + +pci:v000011C1d00000480* + ID_MODEL_FROM_DATABASE=Venus Modem (V90, 56KFlex) + +pci:v000011C1d0000048C* + ID_MODEL_FROM_DATABASE=V.92 56K WinModem + +pci:v000011C1d0000048F* + ID_MODEL_FROM_DATABASE=V.92 56k WinModem + +pci:v000011C1d00000620* + ID_MODEL_FROM_DATABASE=Lucent V.92 Data/Fax Modem + +pci:v000011C1d00002600* + ID_MODEL_FROM_DATABASE=StarPro26XX family (SP2601, SP2603, SP2612) DSP + +pci:v000011C1d00005400* + ID_MODEL_FROM_DATABASE=OR3TP12 FPSC + +pci:v000011C1d00005656* + ID_MODEL_FROM_DATABASE=Venus Modem + +pci:v000011C1d00005801* + ID_MODEL_FROM_DATABASE=USB + +pci:v000011C1d00005802* + ID_MODEL_FROM_DATABASE=USS-312 USB Controller + +pci:v000011C1d00005803* + ID_MODEL_FROM_DATABASE=USS-344S USB Controller + +pci:v000011C1d00005811* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller + +pci:v000011C1d00005811sv0000103Csd00002A34* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (Pavilion a1677c) + +pci:v000011C1d00005811sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (Asus IPIBL-LB Motherboard) + +pci:v000011C1d00005811sv0000103Csd00002A9E* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (Pavilion p6310f) + +pci:v000011C1d00005811sv00001043sd00008294* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (LSI FW322/323 IEEE 1394a FireWire Controller) + +pci:v000011C1d00005811sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (D865PERL mainboard) + +pci:v000011C1d00005811sv0000DEADsd00000800* + ID_MODEL_FROM_DATABASE=FW322/323 [TrueFire] 1394a Controller (FireWire Host Bus Adapter) + +pci:v000011C1d00005901* + ID_MODEL_FROM_DATABASE=FW643 [TrueFire] PCIe 1394b Controller + +pci:v000011C1d00005901sv000011C1sd00005900* + ID_MODEL_FROM_DATABASE=FW643 [TrueFire] PCIe 1394b Controller + +pci:v000011C1d00005901sv00001443sd00000643* + ID_MODEL_FROM_DATABASE=FW643 [TrueFire] PCIe 1394b Controller (FireBoard800-e V.2) + +pci:v000011C1d00005901sv00001546sd00000643* + ID_MODEL_FROM_DATABASE=FW643 [TrueFire] PCIe 1394b Controller (FWB-PCIE1X2x) + +pci:v000011C1d00005903* + ID_MODEL_FROM_DATABASE=FW533 [TrueFire] PCIe 1394a Controller + +pci:v000011C1d00008110* + ID_MODEL_FROM_DATABASE=T8110 H.100/H.110 TDM switch + +pci:v000011C1d00008110sv000012D9sd0000000C* + ID_MODEL_FROM_DATABASE=T8110 H.100/H.110 TDM switch (E1/T1 PMXc cPCI carrier card) + +pci:v000011C1d0000AB10* + ID_MODEL_FROM_DATABASE=WL60010 Wireless LAN MAC + +pci:v000011C1d0000AB11* + ID_MODEL_FROM_DATABASE=WL60040 Multimode Wireles LAN MAC + +pci:v000011C1d0000AB11sv000011C1sd0000AB12* + ID_MODEL_FROM_DATABASE=WL60040 Multimode Wireles LAN MAC (WaveLAN 11abg Cardbus card (Model 1102)) + +pci:v000011C1d0000AB11sv000011C1sd0000AB13* + ID_MODEL_FROM_DATABASE=WL60040 Multimode Wireles LAN MAC (WaveLAN 11abg MiniPCI card (Model 0512)) + +pci:v000011C1d0000AB11sv000011C1sd0000AB15* + ID_MODEL_FROM_DATABASE=WL60040 Multimode Wireles LAN MAC (WaveLAN 11abg Cardbus card (Model 1106)) + +pci:v000011C1d0000AB11sv000011C1sd0000AB16* + ID_MODEL_FROM_DATABASE=WL60040 Multimode Wireles LAN MAC (WaveLAN 11abg MiniPCI card (Model 0516)) + +pci:v000011C1d0000AB20* + ID_MODEL_FROM_DATABASE=ORiNOCO PCI Adapter + +pci:v000011C1d0000AB21* + ID_MODEL_FROM_DATABASE=Agere Wireless PCI Adapter + +pci:v000011C1d0000AB30* + ID_MODEL_FROM_DATABASE=Hermes2 Mini-PCI WaveLAN a/b/g + +pci:v000011C1d0000AB30sv000014CDsd00002012* + ID_MODEL_FROM_DATABASE=Hermes2 Mini-PCI WaveLAN a/b/g + +pci:v000011C1d0000ED00* + ID_MODEL_FROM_DATABASE=ET-131x PCI-E Ethernet Controller + +pci:v000011C1d0000ED01* + ID_MODEL_FROM_DATABASE=ET-131x PCI-E Ethernet Controller + +pci:v000011C2* + ID_VENDOR_FROM_DATABASE=Sand Microelectronics + +pci:v000011C3* + ID_VENDOR_FROM_DATABASE=NEC Corporation + +pci:v000011C4* + ID_VENDOR_FROM_DATABASE=Document Technologies, Inc + +pci:v000011C5* + ID_VENDOR_FROM_DATABASE=Shiva Corporation + +pci:v000011C6* + ID_VENDOR_FROM_DATABASE=Dainippon Screen Mfg. Co. Ltd + +pci:v000011C7* + ID_VENDOR_FROM_DATABASE=D.C.M. Data Systems + +pci:v000011C8* + ID_VENDOR_FROM_DATABASE=Dolphin Interconnect Solutions AS + +pci:v000011C8d00000658* + ID_MODEL_FROM_DATABASE=PSB32 SCI-Adapter D31x + +pci:v000011C8d0000D665* + ID_MODEL_FROM_DATABASE=PSB64 SCI-Adapter D32x + +pci:v000011C8d0000D667* + ID_MODEL_FROM_DATABASE=PSB66 SCI-Adapter D33x + +pci:v000011C9* + ID_VENDOR_FROM_DATABASE=Magma + +pci:v000011C9d00000010* + ID_MODEL_FROM_DATABASE=16-line serial port w/- DMA + +pci:v000011C9d00000011* + ID_MODEL_FROM_DATABASE=4-line serial port w/- DMA + +pci:v000011CA* + ID_VENDOR_FROM_DATABASE=LSI Systems, Inc + +pci:v000011CB* + ID_VENDOR_FROM_DATABASE=Specialix Research Ltd. + +pci:v000011CBd00002000* + ID_MODEL_FROM_DATABASE=PCI_9050 + +pci:v000011CBd00002000sv000011CBsd00000200* + ID_MODEL_FROM_DATABASE=PCI_9050 (SX) + +pci:v000011CBd00002000sv000011CBsd0000B008* + ID_MODEL_FROM_DATABASE=PCI_9050 (I/O8+) + +pci:v000011CBd00004000* + ID_MODEL_FROM_DATABASE=SUPI_1 + +pci:v000011CBd00008000* + ID_MODEL_FROM_DATABASE=T225 + +pci:v000011CC* + ID_VENDOR_FROM_DATABASE=Michels & Kleberhoff Computer GmbH + +pci:v000011CD* + ID_VENDOR_FROM_DATABASE=HAL Computer Systems, Inc. + +pci:v000011CE* + ID_VENDOR_FROM_DATABASE=Netaccess + +pci:v000011CF* + ID_VENDOR_FROM_DATABASE=Pioneer Electronic Corporation + +pci:v000011D0* + ID_VENDOR_FROM_DATABASE=Lockheed Martin Federal Systems-Manassas + +pci:v000011D1* + ID_VENDOR_FROM_DATABASE=Auravision + +pci:v000011D1d000001F7* + ID_MODEL_FROM_DATABASE=VxP524 + +pci:v000011D1d000001F9* + ID_MODEL_FROM_DATABASE=VxP951 + +pci:v000011D2* + ID_VENDOR_FROM_DATABASE=Intercom Inc. + +pci:v000011D3* + ID_VENDOR_FROM_DATABASE=Trancell Systems Inc + +pci:v000011D4* + ID_VENDOR_FROM_DATABASE=Analog Devices + +pci:v000011D4d00001535* + ID_MODEL_FROM_DATABASE=Blackfin BF535 processor + +pci:v000011D4d00001805* + ID_MODEL_FROM_DATABASE=SM56 PCI modem + +pci:v000011D5* + ID_VENDOR_FROM_DATABASE=Ikon Corporation + +pci:v000011D5d00000115* + ID_MODEL_FROM_DATABASE=10115 + +pci:v000011D5d00000117* + ID_MODEL_FROM_DATABASE=10117 + +pci:v000011D6* + ID_VENDOR_FROM_DATABASE=Tekelec Telecom + +pci:v000011D7* + ID_VENDOR_FROM_DATABASE=Trenton Technology, Inc. + +pci:v000011D8* + ID_VENDOR_FROM_DATABASE=Image Technologies Development + +pci:v000011D9* + ID_VENDOR_FROM_DATABASE=TEC Corporation + +pci:v000011DA* + ID_VENDOR_FROM_DATABASE=Novell + +pci:v000011DB* + ID_VENDOR_FROM_DATABASE=Sega Enterprises Ltd + +pci:v000011DC* + ID_VENDOR_FROM_DATABASE=Questra Corporation + +pci:v000011DD* + ID_VENDOR_FROM_DATABASE=Crosfield Electronics Limited + +pci:v000011DE* + ID_VENDOR_FROM_DATABASE=Zoran Corporation + +pci:v000011DEd00006017* + ID_MODEL_FROM_DATABASE=miroVIDEO DC30 + +pci:v000011DEd00006057* + ID_MODEL_FROM_DATABASE=ZR36057PQC Video cutting chipset + +pci:v000011DEd00006057sv00001031sd00007EFE* + ID_MODEL_FROM_DATABASE=ZR36057PQC Video cutting chipset (DC10 Plus) + +pci:v000011DEd00006057sv00001031sd0000FC00* + ID_MODEL_FROM_DATABASE=ZR36057PQC Video cutting chipset (MiroVIDEO DC50, Motion JPEG Capture/CODEC Board) + +pci:v000011DEd00006057sv000012F8sd00008A02* + ID_MODEL_FROM_DATABASE=ZR36057PQC Video cutting chipset (Tekram Video Kit) + +pci:v000011DEd00006057sv000013CAsd00004231* + ID_MODEL_FROM_DATABASE=ZR36057PQC Video cutting chipset (JPEG/TV Card) + +pci:v000011DEd00006120* + ID_MODEL_FROM_DATABASE=ZR36120 + +pci:v000011DEd00006120sv00001328sd0000F001* + ID_MODEL_FROM_DATABASE=ZR36120 (Cinemaster C DVD Decoder) + +pci:v000011DEd00006120sv000013C2sd00000000* + ID_MODEL_FROM_DATABASE=ZR36120 (MediaFocus Satellite TV Card) + +pci:v000011DEd00006120sv00001DE1sd00009FFF* + ID_MODEL_FROM_DATABASE=ZR36120 (Video Kit C210) + +pci:v000011DF* + ID_VENDOR_FROM_DATABASE=New Wave PDG + +pci:v000011E0* + ID_VENDOR_FROM_DATABASE=Cray Communications A/S + +pci:v000011E1* + ID_VENDOR_FROM_DATABASE=GEC Plessey Semi Inc. + +pci:v000011E2* + ID_VENDOR_FROM_DATABASE=Samsung Information Systems America + +pci:v000011E3* + ID_VENDOR_FROM_DATABASE=Quicklogic Corporation + +pci:v000011E3d00000001* + ID_MODEL_FROM_DATABASE=COM-ON-AIR Dosch&Amand DECT + +pci:v000011E3d00000560* + ID_MODEL_FROM_DATABASE=QL5064 Companion Design Demo Board + +pci:v000011E3d00005030* + ID_MODEL_FROM_DATABASE=PC Watchdog + +pci:v000011E3d00008417* + ID_MODEL_FROM_DATABASE=QL5064 [QuickPCI] PCI v2.2 bridge for SMT417 Dual TMS320C6416T PMC Module + +pci:v000011E4* + ID_VENDOR_FROM_DATABASE=Second Wave Inc + +pci:v000011E5* + ID_VENDOR_FROM_DATABASE=IIX Consulting + +pci:v000011E6* + ID_VENDOR_FROM_DATABASE=Mitsui-Zosen System Research + +pci:v000011E7* + ID_VENDOR_FROM_DATABASE=Toshiba America, Elec. Company + +pci:v000011E8* + ID_VENDOR_FROM_DATABASE=Digital Processing Systems Inc. + +pci:v000011E9* + ID_VENDOR_FROM_DATABASE=Highwater Designs Ltd. + +pci:v000011EA* + ID_VENDOR_FROM_DATABASE=Elsag Bailey + +pci:v000011EB* + ID_VENDOR_FROM_DATABASE=Formation Inc. + +pci:v000011EC* + ID_VENDOR_FROM_DATABASE=Coreco Inc + +pci:v000011ECd0000000D* + ID_MODEL_FROM_DATABASE=Oculus-F/64P + +pci:v000011ECd00001800* + ID_MODEL_FROM_DATABASE=Cobra/C6 + +pci:v000011ED* + ID_VENDOR_FROM_DATABASE=Mediamatics + +pci:v000011EE* + ID_VENDOR_FROM_DATABASE=Dome Imaging Systems Inc + +pci:v000011EF* + ID_VENDOR_FROM_DATABASE=Nicolet Technologies B.V. + +pci:v000011F0* + ID_VENDOR_FROM_DATABASE=Compu-Shack + +pci:v000011F0d00004231* + ID_MODEL_FROM_DATABASE=FDDI + +pci:v000011F0d00004232* + ID_MODEL_FROM_DATABASE=FASTline UTP Quattro + +pci:v000011F0d00004233* + ID_MODEL_FROM_DATABASE=FASTline FO + +pci:v000011F0d00004234* + ID_MODEL_FROM_DATABASE=FASTline UTP + +pci:v000011F0d00004235* + ID_MODEL_FROM_DATABASE=FASTline-II UTP + +pci:v000011F0d00004236* + ID_MODEL_FROM_DATABASE=FASTline-II FO + +pci:v000011F0d00004731* + ID_MODEL_FROM_DATABASE=GIGAline + +pci:v000011F1* + ID_VENDOR_FROM_DATABASE=Symbios Logic Inc + +pci:v000011F2* + ID_VENDOR_FROM_DATABASE=Picture Tel Japan K.K. + +pci:v000011F3* + ID_VENDOR_FROM_DATABASE=Keithley Metrabyte + +pci:v000011F3d00000011* + ID_MODEL_FROM_DATABASE=KPCI-PIO24 + +pci:v000011F4* + ID_VENDOR_FROM_DATABASE=Kinetic Systems Corporation + +pci:v000011F4d00002915* + ID_MODEL_FROM_DATABASE=CAMAC controller + +pci:v000011F5* + ID_VENDOR_FROM_DATABASE=Computing Devices International + +pci:v000011F6* + ID_VENDOR_FROM_DATABASE=Compex + +pci:v000011F6d00000112* + ID_MODEL_FROM_DATABASE=ENet100VG4 + +pci:v000011F6d00000113* + ID_MODEL_FROM_DATABASE=FreedomLine 100 + +pci:v000011F6d00001401* + ID_MODEL_FROM_DATABASE=ReadyLink 2000 + +pci:v000011F6d00002011* + ID_MODEL_FROM_DATABASE=RL100-ATX 10/100 + +pci:v000011F6d00002011sv000011F6sd00002011* + ID_MODEL_FROM_DATABASE=RL100-ATX 10/100 (RL100-ATX) + +pci:v000011F6d00002201* + ID_MODEL_FROM_DATABASE=ReadyLink 100TX (Winbond W89C840) + +pci:v000011F6d00002201sv000011F6sd00002011* + ID_MODEL_FROM_DATABASE=ReadyLink 100TX (Winbond W89C840) (ReadyLink 100TX) + +pci:v000011F6d00009881* + ID_MODEL_FROM_DATABASE=RL100TX Fast Ethernet + +pci:v000011F7* + ID_VENDOR_FROM_DATABASE=Scientific Atlanta + +pci:v000011F8* + ID_VENDOR_FROM_DATABASE=PMC-Sierra Inc. + +pci:v000011F8d00005220* + ID_MODEL_FROM_DATABASE=BR522x [PMC-Sierra maxRAID SAS Controller] + +pci:v000011F8d00007364* + ID_MODEL_FROM_DATABASE=PM7364 [FREEDM - 32 Frame Engine & Datalink Mgr] + +pci:v000011F8d00007375* + ID_MODEL_FROM_DATABASE=PM7375 [LASAR-155 ATM SAR] + +pci:v000011F8d00007384* + ID_MODEL_FROM_DATABASE=PM7384 [FREEDM - 84P672 Frm Engine & Datalink Mgr] + +pci:v000011F8d00008000* + ID_MODEL_FROM_DATABASE=PM8000 [SPC - SAS Protocol Controller] + +pci:v000011F8d00008032* + ID_MODEL_FROM_DATABASE=ATTO Celerity FC8xEN + +pci:v000011F8d00008032sv0000117Csd0000003B* + ID_MODEL_FROM_DATABASE=ATTO Celerity FC8xEN (Celerity FC-82EN Fibre Channel Adapter) + +pci:v000011F8d00008032sv0000117Csd0000003C* + ID_MODEL_FROM_DATABASE=ATTO Celerity FC8xEN (Celerity FC-84EN Fibre Channel Adapter) + +pci:v000011F9* + ID_VENDOR_FROM_DATABASE=I-Cube Inc + +pci:v000011FA* + ID_VENDOR_FROM_DATABASE=Kasan Electronics Company, Ltd. + +pci:v000011FB* + ID_VENDOR_FROM_DATABASE=Datel Inc + +pci:v000011FC* + ID_VENDOR_FROM_DATABASE=Silicon Magic + +pci:v000011FD* + ID_VENDOR_FROM_DATABASE=High Street Consultants + +pci:v000011FE* + ID_VENDOR_FROM_DATABASE=Comtrol Corporation + +pci:v000011FEd00000001* + ID_MODEL_FROM_DATABASE=RocketPort 32 port w/external I/F + +pci:v000011FEd00000002* + ID_MODEL_FROM_DATABASE=RocketPort 8 port w/external I/F + +pci:v000011FEd00000003* + ID_MODEL_FROM_DATABASE=RocketPort 16 port w/external I/F + +pci:v000011FEd00000004* + ID_MODEL_FROM_DATABASE=RocketPort 4 port w/quad cable + +pci:v000011FEd00000005* + ID_MODEL_FROM_DATABASE=RocketPort 8 port w/octa cable + +pci:v000011FEd00000006* + ID_MODEL_FROM_DATABASE=RocketPort 8 port w/RJ11 connectors + +pci:v000011FEd00000007* + ID_MODEL_FROM_DATABASE=RocketPort 4 port w/RJ11 connectors + +pci:v000011FEd00000008* + ID_MODEL_FROM_DATABASE=RocketPort 8 port w/ DB78 SNI (Siemens) connector + +pci:v000011FEd00000009* + ID_MODEL_FROM_DATABASE=RocketPort 16 port w/ DB78 SNI (Siemens) connector + +pci:v000011FEd0000000A* + ID_MODEL_FROM_DATABASE=RocketPort Plus 4 port + +pci:v000011FEd0000000B* + ID_MODEL_FROM_DATABASE=RocketPort Plus 8 port + +pci:v000011FEd0000000C* + ID_MODEL_FROM_DATABASE=RocketModem 6 port + +pci:v000011FEd0000000D* + ID_MODEL_FROM_DATABASE=RocketModem 4-port + +pci:v000011FEd0000000E* + ID_MODEL_FROM_DATABASE=RocketPort Plus 2 port RS232 + +pci:v000011FEd0000000F* + ID_MODEL_FROM_DATABASE=RocketPort Plus 2 port RS422 + +pci:v000011FEd00000040* + ID_MODEL_FROM_DATABASE=RocketPort Infinity Octa, 8port, RJ45 + +pci:v000011FEd00000041* + ID_MODEL_FROM_DATABASE=RocketPort Infinity 32port, External Interface + +pci:v000011FEd00000042* + ID_MODEL_FROM_DATABASE=RocketPort Infinity 8port, External Interface + +pci:v000011FEd00000043* + ID_MODEL_FROM_DATABASE=RocketPort Infinity 16port, External Interface + +pci:v000011FEd00000044* + ID_MODEL_FROM_DATABASE=RocketPort Infinity Quad, 4port, DB + +pci:v000011FEd00000045* + ID_MODEL_FROM_DATABASE=RocketPort Infinity Octa, 8port, DB + +pci:v000011FEd00000047* + ID_MODEL_FROM_DATABASE=RocketPort Infinity 4port, RJ45 + +pci:v000011FEd0000004F* + ID_MODEL_FROM_DATABASE=RocketPort Infinity 2port, SMPTE + +pci:v000011FEd00000052* + ID_MODEL_FROM_DATABASE=RocketPort Infinity Octa, 8port, SMPTE + +pci:v000011FEd00000801* + ID_MODEL_FROM_DATABASE=RocketPort UPCI 32 port w/external I/F + +pci:v000011FEd00000802* + ID_MODEL_FROM_DATABASE=RocketPort UPCI 8 port w/external I/F + +pci:v000011FEd00000803* + ID_MODEL_FROM_DATABASE=RocketPort UPCI 16 port w/external I/F + +pci:v000011FEd00000805* + ID_MODEL_FROM_DATABASE=RocketPort UPCI 8 port w/octa cable + +pci:v000011FEd0000080C* + ID_MODEL_FROM_DATABASE=RocketModem III 8 port + +pci:v000011FEd0000080D* + ID_MODEL_FROM_DATABASE=RocketModem III 4 port + +pci:v000011FEd00000810* + ID_MODEL_FROM_DATABASE=RocketPort UPCI Plus 4 port RS232 + +pci:v000011FEd00000811* + ID_MODEL_FROM_DATABASE=RocketPort UPCI Plus 8 port RS232 + +pci:v000011FEd00000812* + ID_MODEL_FROM_DATABASE=RocketPort UPCI Plus 8 port RS422 + +pci:v000011FEd00000903* + ID_MODEL_FROM_DATABASE=RocketPort Compact PCI 16 port w/external I/F + +pci:v000011FEd00008015* + ID_MODEL_FROM_DATABASE=RocketPort 4-port UART 16954 + +pci:v000011FF* + ID_VENDOR_FROM_DATABASE=Scion Corporation + +pci:v000011FFd00000003* + ID_MODEL_FROM_DATABASE=AG-5 + +pci:v00001200* + ID_VENDOR_FROM_DATABASE=CSS Corporation + +pci:v00001201* + ID_VENDOR_FROM_DATABASE=Vista Controls Corp + +pci:v00001202* + ID_VENDOR_FROM_DATABASE=Network General Corp. + +pci:v00001202d00004300* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter + +pci:v00001202d00004300sv00001202sd00009841* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter (SK-9841 LX) + +pci:v00001202d00004300sv00001202sd00009842* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter (SK-9841 LX dual link) + +pci:v00001202d00004300sv00001202sd00009843* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter (SK-9843 SX) + +pci:v00001202d00004300sv00001202sd00009844* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Adapter (SK-9843 SX dual link) + +pci:v00001203* + ID_VENDOR_FROM_DATABASE=Bayer Corporation, Agfa Division + +pci:v00001204* + ID_VENDOR_FROM_DATABASE=Lattice Semiconductor Corporation + +pci:v00001204d00001965* + ID_MODEL_FROM_DATABASE=SB6501 802.11ad Wireless Network Adapter + +pci:v00001205* + ID_VENDOR_FROM_DATABASE=Array Corporation + +pci:v00001206* + ID_VENDOR_FROM_DATABASE=Amdahl Corporation + +pci:v00001208* + ID_VENDOR_FROM_DATABASE=Parsytec GmbH + +pci:v00001208d00004853* + ID_MODEL_FROM_DATABASE=HS-Link Device + +pci:v00001209* + ID_VENDOR_FROM_DATABASE=SCI Systems Inc + +pci:v0000120A* + ID_VENDOR_FROM_DATABASE=Synaptel + +pci:v0000120B* + ID_VENDOR_FROM_DATABASE=Adaptive Solutions + +pci:v0000120C* + ID_VENDOR_FROM_DATABASE=Technical Corp. + +pci:v0000120D* + ID_VENDOR_FROM_DATABASE=Compression Labs, Inc. + +pci:v0000120E* + ID_VENDOR_FROM_DATABASE=Cyclades Corporation + +pci:v0000120Ed00000100* + ID_MODEL_FROM_DATABASE=Cyclom-Y below first megabyte + +pci:v0000120Ed00000101* + ID_MODEL_FROM_DATABASE=Cyclom-Y above first megabyte + +pci:v0000120Ed00000102* + ID_MODEL_FROM_DATABASE=Cyclom-4Y below first megabyte + +pci:v0000120Ed00000103* + ID_MODEL_FROM_DATABASE=Cyclom-4Y above first megabyte + +pci:v0000120Ed00000104* + ID_MODEL_FROM_DATABASE=Cyclom-8Y below first megabyte + +pci:v0000120Ed00000105* + ID_MODEL_FROM_DATABASE=Cyclom-8Y above first megabyte + +pci:v0000120Ed00000200* + ID_MODEL_FROM_DATABASE=Cyclades-Z below first megabyte + +pci:v0000120Ed00000201* + ID_MODEL_FROM_DATABASE=Cyclades-Z above first megabyte + +pci:v0000120Ed00000300* + ID_MODEL_FROM_DATABASE=PC300/RSV or /X21 (2 ports) + +pci:v0000120Ed00000301* + ID_MODEL_FROM_DATABASE=PC300/RSV or /X21 (1 port) + +pci:v0000120Ed00000310* + ID_MODEL_FROM_DATABASE=PC300/TE (2 ports) + +pci:v0000120Ed00000311* + ID_MODEL_FROM_DATABASE=PC300/TE (1 port) + +pci:v0000120Ed00000320* + ID_MODEL_FROM_DATABASE=PC300/TE-M (2 ports) + +pci:v0000120Ed00000321* + ID_MODEL_FROM_DATABASE=PC300/TE-M (1 port) + +pci:v0000120Ed00000400* + ID_MODEL_FROM_DATABASE=PC400 + +pci:v0000120F* + ID_VENDOR_FROM_DATABASE=Essential Communications + +pci:v0000120Fd00000001* + ID_MODEL_FROM_DATABASE=Roadrunner serial HIPPI + +pci:v00001210* + ID_VENDOR_FROM_DATABASE=Hyperparallel Technologies + +pci:v00001211* + ID_VENDOR_FROM_DATABASE=Braintech Inc + +pci:v00001212* + ID_VENDOR_FROM_DATABASE=Kingston Technology Corp. + +pci:v00001213* + ID_VENDOR_FROM_DATABASE=Applied Intelligent Systems, Inc. + +pci:v00001214* + ID_VENDOR_FROM_DATABASE=Performance Technologies, Inc. + +pci:v00001215* + ID_VENDOR_FROM_DATABASE=Interware Co., Ltd + +pci:v00001216* + ID_VENDOR_FROM_DATABASE=Purup Prepress A/S + +pci:v00001217* + ID_VENDOR_FROM_DATABASE=O2 Micro, Inc. + +pci:v00001217d000000F7* + ID_MODEL_FROM_DATABASE=Firewire (IEEE 1394) + +pci:v00001217d000000F7sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=Firewire (IEEE 1394) (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00001217d000000F7sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=Firewire (IEEE 1394) (Satellite P305D-S8995E) + +pci:v00001217d000010F7* + ID_MODEL_FROM_DATABASE=1394 OHCI Compliant Host Controller + +pci:v00001217d000011F7* + ID_MODEL_FROM_DATABASE=OZ600 1394a-2000 Controller + +pci:v00001217d000011F7sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=OZ600 1394a-2000 Controller (Precision M4600) + +pci:v00001217d000013F7* + ID_MODEL_FROM_DATABASE=1394 OHCI Compliant Host Controller + +pci:v00001217d00006729* + ID_MODEL_FROM_DATABASE=OZ6729 + +pci:v00001217d0000673A* + ID_MODEL_FROM_DATABASE=OZ6730 + +pci:v00001217d00006832* + ID_MODEL_FROM_DATABASE=OZ6832/6833 CardBus Controller + +pci:v00001217d00006836* + ID_MODEL_FROM_DATABASE=OZ6836/6860 CardBus Controller + +pci:v00001217d00006872* + ID_MODEL_FROM_DATABASE=OZ6812 CardBus Controller + +pci:v00001217d00006925* + ID_MODEL_FROM_DATABASE=OZ6922 CardBus Controller + +pci:v00001217d00006933* + ID_MODEL_FROM_DATABASE=OZ6933/711E1 CardBus/SmartCardBus Controller + +pci:v00001217d00006933sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=OZ6933/711E1 CardBus/SmartCardBus Controller (Travelmate 612 TX) + +pci:v00001217d00006972* + ID_MODEL_FROM_DATABASE=OZ601/6912/711E0 CardBus/SmartCardBus Controller + +pci:v00001217d00006972sv00001014sd0000020C* + ID_MODEL_FROM_DATABASE=OZ601/6912/711E0 CardBus/SmartCardBus Controller (ThinkPad R30) + +pci:v00001217d00006972sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=OZ601/6912/711E0 CardBus/SmartCardBus Controller (Latitude D500) + +pci:v00001217d00006972sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=OZ601/6912/711E0 CardBus/SmartCardBus Controller (Magnia Z310) + +pci:v00001217d00007110* + ID_MODEL_FROM_DATABASE=OZ711Mx 4-in-1 MemoryCardBus Accelerator + +pci:v00001217d00007110sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=OZ711Mx 4-in-1 MemoryCardBus Accelerator (NC8000 laptop) + +pci:v00001217d00007110sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=OZ711Mx 4-in-1 MemoryCardBus Accelerator (NC6000 laptop) + +pci:v00001217d00007110sv00001734sd0000106C* + ID_MODEL_FROM_DATABASE=OZ711Mx 4-in-1 MemoryCardBus Accelerator (Amilo A1645) + +pci:v00001217d00007112* + ID_MODEL_FROM_DATABASE=OZ711EC1/M1 SmartCardBus/MemoryCardBus Controller + +pci:v00001217d00007113* + ID_MODEL_FROM_DATABASE=OZ711EC1 SmartCardBus Controller + +pci:v00001217d00007113sv00001025sd00000035* + ID_MODEL_FROM_DATABASE=OZ711EC1 SmartCardBus Controller (TravelMate 660) + +pci:v00001217d00007114* + ID_MODEL_FROM_DATABASE=OZ711M1/MC1 4-in-1 MemoryCardBus Controller + +pci:v00001217d00007120* + ID_MODEL_FROM_DATABASE=Integrated MMC/SD Controller + +pci:v00001217d00007120sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=Integrated MMC/SD Controller (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00001217d00007120sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=Integrated MMC/SD Controller (Satellite P305D-S8995E) + +pci:v00001217d00007130* + ID_MODEL_FROM_DATABASE=Integrated MS/xD Controller + +pci:v00001217d00007130sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=Integrated MS/xD Controller (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00001217d00007130sv00001179sd0000FF50* + ID_MODEL_FROM_DATABASE=Integrated MS/xD Controller (Satellite P305D-S8995E) + +pci:v00001217d00007134* + ID_MODEL_FROM_DATABASE=OZ711MP1/MS1 MemoryCardBus Controller + +pci:v00001217d00007135* + ID_MODEL_FROM_DATABASE=Cardbus bridge + +pci:v00001217d00007136* + ID_MODEL_FROM_DATABASE=OZ711SP1 Memory CardBus Controller + +pci:v00001217d000071E2* + ID_MODEL_FROM_DATABASE=OZ711E2 SmartCardBus Controller + +pci:v00001217d00007212* + ID_MODEL_FROM_DATABASE=OZ711M2 4-in-1 MemoryCardBus Controller + +pci:v00001217d00007213* + ID_MODEL_FROM_DATABASE=OZ6933E CardBus Controller + +pci:v00001217d00007223* + ID_MODEL_FROM_DATABASE=OZ711M3/MC3 4-in-1 MemoryCardBus Controller + +pci:v00001217d00007223sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=OZ711M3/MC3 4-in-1 MemoryCardBus Controller (NC8000 laptop) + +pci:v00001217d00007223sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=OZ711M3/MC3 4-in-1 MemoryCardBus Controller (NC6000 laptop) + +pci:v00001217d00007223sv000010CFsd000011C4* + ID_MODEL_FROM_DATABASE=OZ711M3/MC3 4-in-1 MemoryCardBus Controller (Lifebook P5020D Laptop) + +pci:v00001217d00007233* + ID_MODEL_FROM_DATABASE=OZ711MP3/MS3 4-in-1 MemoryCardBus Controller + +pci:v00001217d00008120* + ID_MODEL_FROM_DATABASE=Integrated MMC/SD Controller + +pci:v00001217d00008130* + ID_MODEL_FROM_DATABASE=Integrated MS/MSPRO/xD Controller + +pci:v00001217d00008220* + ID_MODEL_FROM_DATABASE=OZ600FJ1/OZ900FJ1 SD/MMC Card Reader Controller + +pci:v00001217d00008221* + ID_MODEL_FROM_DATABASE=OZ600FJ0/OZ900FJ0/OZ600FJS SD/MMC Card Reader Controller + +pci:v00001217d00008320* + ID_MODEL_FROM_DATABASE=OZ600RJ1/OZ900RJ1 SD/MMC Card Reader Controller + +pci:v00001217d00008320sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=OZ600RJ1/OZ900RJ1 SD/MMC Card Reader Controller (Precision M4600) + +pci:v00001217d00008321* + ID_MODEL_FROM_DATABASE=OZ600RJ0/OZ900RJ0/OZ600RJS SD/MMC Card Reader Controller + +pci:v00001217d00008330* + ID_MODEL_FROM_DATABASE=OZ600 MS/xD Controller + +pci:v00001217d00008330sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=OZ600 MS/xD Controller (Precision M4600) + +pci:v00001217d00008331* + ID_MODEL_FROM_DATABASE=O2 Flash Memory Card + +pci:v00001217d00008520* + ID_MODEL_FROM_DATABASE=SD/MMC Card Reader Controller + +pci:v00001217d00008621* + ID_MODEL_FROM_DATABASE=SD/MMC Card Reader Controller + +pci:v00001218* + ID_VENDOR_FROM_DATABASE=Hybricon Corp. + +pci:v00001219* + ID_VENDOR_FROM_DATABASE=First Virtual Corporation + +pci:v0000121A* + ID_VENDOR_FROM_DATABASE=3Dfx Interactive, Inc. + +pci:v0000121Ad00000001* + ID_MODEL_FROM_DATABASE=Voodoo + +pci:v0000121Ad00000002* + ID_MODEL_FROM_DATABASE=Voodoo 2 + +pci:v0000121Ad00000003* + ID_MODEL_FROM_DATABASE=Voodoo Banshee + +pci:v0000121Ad00000003sv00001092sd00000003* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion) + +pci:v0000121Ad00000003sv00001092sd00004000* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion) + +pci:v0000121Ad00000003sv00001092sd00004002* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion) + +pci:v0000121Ad00000003sv00001092sd00004801* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion AGP) + +pci:v0000121Ad00000003sv00001092sd00004803* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion AGP) + +pci:v0000121Ad00000003sv00001092sd00008030* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion) + +pci:v0000121Ad00000003sv00001092sd00008035* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Monster Fusion AGP) + +pci:v0000121Ad00000003sv000010B0sd00000001* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Dragon 4000) + +pci:v0000121Ad00000003sv00001102sd00001017* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (3D Blaster Banshee PCI (CT6760)) + +pci:v0000121Ad00000003sv00001102sd00001018* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (3D Blaster Banshee VE) + +pci:v0000121Ad00000003sv0000121Asd00000001* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (AGP) + +pci:v0000121Ad00000003sv0000121Asd00000003* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (AGP SGRAM) + +pci:v0000121Ad00000003sv0000121Asd00000004* + ID_MODEL_FROM_DATABASE=Voodoo Banshee + +pci:v0000121Ad00000003sv0000139Csd00000016* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Raven) + +pci:v0000121Ad00000003sv0000139Csd00000017* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Raven) + +pci:v0000121Ad00000003sv000014AFsd00000002* + ID_MODEL_FROM_DATABASE=Voodoo Banshee (Maxi Gamer Phoenix) + +pci:v0000121Ad00000004* + ID_MODEL_FROM_DATABASE=Voodoo Banshee [Velocity 100] + +pci:v0000121Ad00000005* + ID_MODEL_FROM_DATABASE=Voodoo 3 + +pci:v0000121Ad00000005sv0000121Asd00000004* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000030* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000031* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000034* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000036* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 2000 PCI) + +pci:v0000121Ad00000005sv0000121Asd00000037* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000038* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd0000003A* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000044* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3) + +pci:v0000121Ad00000005sv0000121Asd0000004B* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Velocity 100) + +pci:v0000121Ad00000005sv0000121Asd0000004C* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Velocity 200) + +pci:v0000121Ad00000005sv0000121Asd0000004D* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd0000004E* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000051* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000052* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 AGP) + +pci:v0000121Ad00000005sv0000121Asd00000057* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 3000 PCI) + +pci:v0000121Ad00000005sv0000121Asd00000060* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 3500 TV (NTSC)) + +pci:v0000121Ad00000005sv0000121Asd00000061* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 3500 TV (PAL)) + +pci:v0000121Ad00000005sv0000121Asd00000062* + ID_MODEL_FROM_DATABASE=Voodoo 3 (Voodoo3 3500 TV (SECAM)) + +pci:v0000121Ad00000009* + ID_MODEL_FROM_DATABASE=Voodoo 4 / Voodoo 5 + +pci:v0000121Ad00000009sv0000121Asd00000003* + ID_MODEL_FROM_DATABASE=Voodoo 4 / Voodoo 5 (Voodoo5 PCI 5500) + +pci:v0000121Ad00000009sv0000121Asd00000009* + ID_MODEL_FROM_DATABASE=Voodoo 4 / Voodoo 5 (Voodoo5 AGP 5500/6000) + +pci:v0000121Ad00000057* + ID_MODEL_FROM_DATABASE=Voodoo 3/3000 [Avenger] + +pci:v0000121B* + ID_VENDOR_FROM_DATABASE=Advanced Telecommunications Modules + +pci:v0000121C* + ID_VENDOR_FROM_DATABASE=Nippon Texaco., Ltd + +pci:v0000121D* + ID_VENDOR_FROM_DATABASE=LiPPERT ADLINK Technology GmbH + +pci:v0000121E* + ID_VENDOR_FROM_DATABASE=CSPI + +pci:v0000121Ed00000201* + ID_MODEL_FROM_DATABASE=Myrinet 2000 Scalable Cluster Interconnect + +pci:v0000121F* + ID_VENDOR_FROM_DATABASE=Arcus Technology, Inc. + +pci:v00001220* + ID_VENDOR_FROM_DATABASE=Ariel Corporation + +pci:v00001220d00001220* + ID_MODEL_FROM_DATABASE=AMCC 5933 TMS320C80 DSP/Imaging board + +pci:v00001221* + ID_VENDOR_FROM_DATABASE=Contec Co., Ltd + +pci:v00001221d00009172* + ID_MODEL_FROM_DATABASE=PO-64L(PCI)H [Isolated Digital Output Board for PCI] + +pci:v00001221d000091A2* + ID_MODEL_FROM_DATABASE=PO-32L(PCI)H [Isolated Digital Output Board for PCI] + +pci:v00001221d000091C3* + ID_MODEL_FROM_DATABASE=DA16-16(LPCI)L [Un-insulated highly precise analog output board for Low Profile PCI] + +pci:v00001221d0000B152* + ID_MODEL_FROM_DATABASE=DIO-96D2-LPCI + +pci:v00001221d0000C103* + ID_MODEL_FROM_DATABASE=ADA16-32/2(PCI)F [High-Speed Analog I/O Board for PCI] + +pci:v00001222* + ID_VENDOR_FROM_DATABASE=Ancor Communications, Inc. + +pci:v00001223* + ID_VENDOR_FROM_DATABASE=Artesyn Communication Products + +pci:v00001223d00000003* + ID_MODEL_FROM_DATABASE=PM/Link + +pci:v00001223d00000004* + ID_MODEL_FROM_DATABASE=PM/T1 + +pci:v00001223d00000005* + ID_MODEL_FROM_DATABASE=PM/E1 + +pci:v00001223d00000008* + ID_MODEL_FROM_DATABASE=PM/SLS + +pci:v00001223d00000009* + ID_MODEL_FROM_DATABASE=BajaSpan Resource Target + +pci:v00001223d0000000A* + ID_MODEL_FROM_DATABASE=BajaSpan Section 0 + +pci:v00001223d0000000B* + ID_MODEL_FROM_DATABASE=BajaSpan Section 1 + +pci:v00001223d0000000C* + ID_MODEL_FROM_DATABASE=BajaSpan Section 2 + +pci:v00001223d0000000D* + ID_MODEL_FROM_DATABASE=BajaSpan Section 3 + +pci:v00001223d0000000E* + ID_MODEL_FROM_DATABASE=PM/PPC + +pci:v00001224* + ID_VENDOR_FROM_DATABASE=Interactive Images + +pci:v00001225* + ID_VENDOR_FROM_DATABASE=Power I/O, Inc. + +pci:v00001227* + ID_VENDOR_FROM_DATABASE=Tech-Source + +pci:v00001227d00000006* + ID_MODEL_FROM_DATABASE=Raptor GFX 8P + +pci:v00001227d00000023* + ID_MODEL_FROM_DATABASE=Raptor GFX [1100T] + +pci:v00001227d00000045* + ID_MODEL_FROM_DATABASE=Raptor 4000-L [Linux version] + +pci:v00001227d0000004A* + ID_MODEL_FROM_DATABASE=Raptor 4000-LR-L [Linux version] + +pci:v00001228* + ID_VENDOR_FROM_DATABASE=Norsk Elektro Optikk A/S + +pci:v00001229* + ID_VENDOR_FROM_DATABASE=Data Kinesis Inc. + +pci:v0000122A* + ID_VENDOR_FROM_DATABASE=Integrated Telecom + +pci:v0000122B* + ID_VENDOR_FROM_DATABASE=LG Industrial Systems Co., Ltd + +pci:v0000122C* + ID_VENDOR_FROM_DATABASE=Sican GmbH + +pci:v0000122D* + ID_VENDOR_FROM_DATABASE=Aztech System Ltd + +pci:v0000122Dd00001206* + ID_MODEL_FROM_DATABASE=368DSP + +pci:v0000122Dd00001400* + ID_MODEL_FROM_DATABASE=Trident PCI288-Q3DII (NX) + +pci:v0000122Dd000050DC* + ID_MODEL_FROM_DATABASE=3328 Audio + +pci:v0000122Dd000050DCsv0000122Dsd00000001* + ID_MODEL_FROM_DATABASE=3328 Audio + +pci:v0000122Dd000080DA* + ID_MODEL_FROM_DATABASE=3328 Audio + +pci:v0000122Dd000080DAsv0000122Dsd00000001* + ID_MODEL_FROM_DATABASE=3328 Audio + +pci:v0000122E* + ID_VENDOR_FROM_DATABASE=Xyratex + +pci:v0000122Ed00007722* + ID_MODEL_FROM_DATABASE=Napatech XL1 + +pci:v0000122Ed00007724* + ID_MODEL_FROM_DATABASE=Napatech XL2/XA + +pci:v0000122Ed00007729* + ID_MODEL_FROM_DATABASE=Napatech XD + +pci:v0000122F* + ID_VENDOR_FROM_DATABASE=Andrew Corporation + +pci:v00001230* + ID_VENDOR_FROM_DATABASE=Fishcamp Engineering + +pci:v00001231* + ID_VENDOR_FROM_DATABASE=Woodward McCoach, Inc. + +pci:v00001231d000004E1* + ID_MODEL_FROM_DATABASE=Desktop PCI Telephony 4 + +pci:v00001231d000005E1* + ID_MODEL_FROM_DATABASE=Desktop PCI Telephony 5/6 + +pci:v00001231d00000D00* + ID_MODEL_FROM_DATABASE=LightParser + +pci:v00001231d00000D02* + ID_MODEL_FROM_DATABASE=LightParser 2 + +pci:v00001231d00000D13* + ID_MODEL_FROM_DATABASE=Desktop PCI L1/L3 Telephony + +pci:v00001232* + ID_VENDOR_FROM_DATABASE=GPT Limited + +pci:v00001233* + ID_VENDOR_FROM_DATABASE=Bus-Tech, Inc. + +pci:v00001235* + ID_VENDOR_FROM_DATABASE=Risq Modular Systems, Inc. + +pci:v00001236* + ID_VENDOR_FROM_DATABASE=Sigma Designs Corporation + +pci:v00001236d00000000* + ID_MODEL_FROM_DATABASE=RealMagic64/GX + +pci:v00001236d00006401* + ID_MODEL_FROM_DATABASE=REALmagic 64/GX (SD 6425) + +pci:v00001237* + ID_VENDOR_FROM_DATABASE=Alta Technology Corporation + +pci:v00001238* + ID_VENDOR_FROM_DATABASE=Adtran + +pci:v00001239* + ID_VENDOR_FROM_DATABASE=3DO Company + +pci:v0000123A* + ID_VENDOR_FROM_DATABASE=Visicom Laboratories, Inc. + +pci:v0000123B* + ID_VENDOR_FROM_DATABASE=Seeq Technology, Inc. + +pci:v0000123C* + ID_VENDOR_FROM_DATABASE=Century Systems, Inc. + +pci:v0000123D* + ID_VENDOR_FROM_DATABASE=Engineering Design Team, Inc. + +pci:v0000123Dd00000000* + ID_MODEL_FROM_DATABASE=EasyConnect 8/32 + +pci:v0000123Dd00000002* + ID_MODEL_FROM_DATABASE=EasyConnect 8/64 + +pci:v0000123Dd00000003* + ID_MODEL_FROM_DATABASE=EasyIO + +pci:v0000123E* + ID_VENDOR_FROM_DATABASE=Simutech, Inc. + +pci:v0000123F* + ID_VENDOR_FROM_DATABASE=LSI Logic + +pci:v0000123Fd000000E4* + ID_MODEL_FROM_DATABASE=MPEG + +pci:v0000123Fd00008120* + ID_MODEL_FROM_DATABASE=DVxplore Codec + +pci:v0000123Fd00008120sv000010DEsd000001E1* + ID_MODEL_FROM_DATABASE=DVxplore Codec (NVTV PAL) + +pci:v0000123Fd00008120sv000010DEsd000001E2* + ID_MODEL_FROM_DATABASE=DVxplore Codec (NVTV NTSC) + +pci:v0000123Fd00008120sv000010DEsd000001E3* + ID_MODEL_FROM_DATABASE=DVxplore Codec (NVTV PAL) + +pci:v0000123Fd00008120sv000010DEsd00000248* + ID_MODEL_FROM_DATABASE=DVxplore Codec (NVTV NTSC) + +pci:v0000123Fd00008120sv000010DEsd00000249* + ID_MODEL_FROM_DATABASE=DVxplore Codec (NVTV PAL) + +pci:v0000123Fd00008120sv000011BDsd00000006* + ID_MODEL_FROM_DATABASE=DVxplore Codec (DV500 E4) + +pci:v0000123Fd00008120sv000011BDsd0000000A* + ID_MODEL_FROM_DATABASE=DVxplore Codec (DV500 E4) + +pci:v0000123Fd00008120sv000011BDsd0000000F* + ID_MODEL_FROM_DATABASE=DVxplore Codec (DV500 E4) + +pci:v0000123Fd00008120sv00001809sd00000016* + ID_MODEL_FROM_DATABASE=DVxplore Codec (Emuzed MAUI-III PCI PVR FM TV) + +pci:v0000123Fd00008888* + ID_MODEL_FROM_DATABASE=Cinemaster C 3.0 DVD Decoder + +pci:v0000123Fd00008888sv00001002sd00000001* + ID_MODEL_FROM_DATABASE=Cinemaster C 3.0 DVD Decoder + +pci:v0000123Fd00008888sv00001002sd00000002* + ID_MODEL_FROM_DATABASE=Cinemaster C 3.0 DVD Decoder + +pci:v0000123Fd00008888sv00001328sd00000001* + ID_MODEL_FROM_DATABASE=Cinemaster C 3.0 DVD Decoder + +pci:v00001240* + ID_VENDOR_FROM_DATABASE=Marathon Technologies Corp. + +pci:v00001241* + ID_VENDOR_FROM_DATABASE=DSC Communications + +pci:v00001242* + ID_VENDOR_FROM_DATABASE=JNI Corporation + +pci:v00001242d00001560* + ID_MODEL_FROM_DATABASE=JNIC-1560 PCI-X Fibre Channel Controller + +pci:v00001242d00001560sv00001242sd00006562* + ID_MODEL_FROM_DATABASE=JNIC-1560 PCI-X Fibre Channel Controller (FCX2-6562 Dual Channel PCI-X Fibre Channel Adapter) + +pci:v00001242d00001560sv00001242sd0000656A* + ID_MODEL_FROM_DATABASE=JNIC-1560 PCI-X Fibre Channel Controller (FCX-6562 PCI-X Fibre Channel Adapter) + +pci:v00001242d00004643* + ID_MODEL_FROM_DATABASE=FCI-1063 Fibre Channel Adapter + +pci:v00001242d00006562* + ID_MODEL_FROM_DATABASE=FCX2-6562 Dual Channel PCI-X Fibre Channel Adapter + +pci:v00001242d0000656A* + ID_MODEL_FROM_DATABASE=FCX-6562 PCI-X Fibre Channel Adapter + +pci:v00001243* + ID_VENDOR_FROM_DATABASE=Delphax + +pci:v00001244* + ID_VENDOR_FROM_DATABASE=AVM GmbH + +pci:v00001244d00000700* + ID_MODEL_FROM_DATABASE=B1 ISDN + +pci:v00001244d00000800* + ID_MODEL_FROM_DATABASE=C4 ISDN + +pci:v00001244d00000A00* + ID_MODEL_FROM_DATABASE=A1 ISDN [Fritz] + +pci:v00001244d00000A00sv00001244sd00000A00* + ID_MODEL_FROM_DATABASE=A1 ISDN [Fritz] (FRITZ!Card ISDN Controller) + +pci:v00001244d00000E00* + ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.0 ISDN + +pci:v00001244d00000E80* + ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.1 ISDN + +pci:v00001244d00000E80sv00001244sd00000E00* + ID_MODEL_FROM_DATABASE=Fritz!Card PCI v2.1 ISDN (PSB 3100F (AVM KAFKA) [Fritz!Card PCI v2.1]) + +pci:v00001244d00001100* + ID_MODEL_FROM_DATABASE=C2 ISDN + +pci:v00001244d00001200* + ID_MODEL_FROM_DATABASE=T1 ISDN + +pci:v00001244d00002700* + ID_MODEL_FROM_DATABASE=Fritz!Card DSL SL + +pci:v00001244d00002900* + ID_MODEL_FROM_DATABASE=Fritz!Card DSL v2.0 + +pci:v00001245* + ID_VENDOR_FROM_DATABASE=A.P.D., S.A. + +pci:v00001246* + ID_VENDOR_FROM_DATABASE=Dipix Technologies, Inc. + +pci:v00001247* + ID_VENDOR_FROM_DATABASE=Xylon Research, Inc. + +pci:v00001248* + ID_VENDOR_FROM_DATABASE=Central Data Corporation + +pci:v00001249* + ID_VENDOR_FROM_DATABASE=Samsung Electronics Co., Ltd. + +pci:v0000124A* + ID_VENDOR_FROM_DATABASE=AEG Electrocom GmbH + +pci:v0000124B* + ID_VENDOR_FROM_DATABASE=SBS/Greenspring Modular I/O + +pci:v0000124Bd00000040* + ID_MODEL_FROM_DATABASE=PCI-40A or cPCI-200 Quad IndustryPack carrier + +pci:v0000124Bd00000040sv0000124Bsd00009080* + ID_MODEL_FROM_DATABASE=PCI-40A or cPCI-200 Quad IndustryPack carrier (PCI9080 Bridge) + +pci:v0000124C* + ID_VENDOR_FROM_DATABASE=Solitron Technologies, Inc. + +pci:v0000124D* + ID_VENDOR_FROM_DATABASE=Stallion Technologies, Inc. + +pci:v0000124Dd00000000* + ID_MODEL_FROM_DATABASE=EasyConnection 8/32 + +pci:v0000124Dd00000002* + ID_MODEL_FROM_DATABASE=EasyConnection 8/64 + +pci:v0000124Dd00000003* + ID_MODEL_FROM_DATABASE=EasyIO + +pci:v0000124Dd00000004* + ID_MODEL_FROM_DATABASE=EasyConnection/RA + +pci:v0000124E* + ID_VENDOR_FROM_DATABASE=Cylink + +pci:v0000124F* + ID_VENDOR_FROM_DATABASE=Infortrend Technology, Inc. + +pci:v0000124Fd00000041* + ID_MODEL_FROM_DATABASE=IFT-2000 Series RAID Controller + +pci:v00001250* + ID_VENDOR_FROM_DATABASE=Hitachi Microcomputer System Ltd + +pci:v00001251* + ID_VENDOR_FROM_DATABASE=VLSI Solutions Oy + +pci:v00001253* + ID_VENDOR_FROM_DATABASE=Guzik Technical Enterprises + +pci:v00001254* + ID_VENDOR_FROM_DATABASE=Linear Systems Ltd. + +pci:v00001254d00000065* + ID_MODEL_FROM_DATABASE=DVB Master FD + +pci:v00001254d0000007C* + ID_MODEL_FROM_DATABASE=DVB Master Quad/o + +pci:v00001255* + ID_VENDOR_FROM_DATABASE=Optibase Ltd + +pci:v00001255d00001110* + ID_MODEL_FROM_DATABASE=MPEG Forge + +pci:v00001255d00001210* + ID_MODEL_FROM_DATABASE=MPEG Fusion + +pci:v00001255d00002110* + ID_MODEL_FROM_DATABASE=VideoPlex + +pci:v00001255d00002120* + ID_MODEL_FROM_DATABASE=VideoPlex CC + +pci:v00001255d00002130* + ID_MODEL_FROM_DATABASE=VideoQuest + +pci:v00001256* + ID_VENDOR_FROM_DATABASE=Perceptive Solutions, Inc. + +pci:v00001256d00004201* + ID_MODEL_FROM_DATABASE=PCI-2220I + +pci:v00001256d00004401* + ID_MODEL_FROM_DATABASE=PCI-2240I + +pci:v00001256d00005201* + ID_MODEL_FROM_DATABASE=PCI-2000 + +pci:v00001257* + ID_VENDOR_FROM_DATABASE=Vertex Networks, Inc. + +pci:v00001258* + ID_VENDOR_FROM_DATABASE=Gilbarco, Inc. + +pci:v00001259* + ID_VENDOR_FROM_DATABASE=Allied Telesis + +pci:v00001259d00002560* + ID_MODEL_FROM_DATABASE=AT-2560 Fast Ethernet Adapter (i82557B) + +pci:v00001259d00002801* + ID_MODEL_FROM_DATABASE=AT-2801FX (RTL-8139) + +pci:v00001259d0000A117* + ID_MODEL_FROM_DATABASE=RTL81xx Fast Ethernet + +pci:v00001259d0000A11E* + ID_MODEL_FROM_DATABASE=RTL81xx Fast Ethernet + +pci:v00001259d0000A120* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v0000125A* + ID_VENDOR_FROM_DATABASE=ABB Power Systems + +pci:v0000125B* + ID_VENDOR_FROM_DATABASE=Asix Electronics Corporation + +pci:v0000125Bd00001400* + ID_MODEL_FROM_DATABASE=AX88141 Fast Ethernet Controller + +pci:v0000125Bd00001400sv00001186sd00001100* + ID_MODEL_FROM_DATABASE=AX88141 Fast Ethernet Controller (AX8814X Based PCI Fast Ethernet Adapter) + +pci:v0000125C* + ID_VENDOR_FROM_DATABASE=Aurora Technologies, Inc. + +pci:v0000125Cd00000101* + ID_MODEL_FROM_DATABASE=Saturn 4520P + +pci:v0000125Cd00000640* + ID_MODEL_FROM_DATABASE=Aries 16000P + +pci:v0000125D* + ID_VENDOR_FROM_DATABASE=ESS Technology + +pci:v0000125Dd00000000* + ID_MODEL_FROM_DATABASE=ES336H Fax Modem (Early Model) + +pci:v0000125Dd00001948* + ID_MODEL_FROM_DATABASE=ES1948 Maestro-1 + +pci:v0000125Dd00001968* + ID_MODEL_FROM_DATABASE=ES1968 Maestro 2 + +pci:v0000125Dd00001968sv00001028sd00000085* + ID_MODEL_FROM_DATABASE=ES1968 Maestro 2 (ES1968 Maestro-2 PCI) + +pci:v0000125Dd00001968sv00001033sd00008051* + ID_MODEL_FROM_DATABASE=ES1968 Maestro 2 (ES1968 Maestro-2 Audiodrive) + +pci:v0000125Dd00001969* + ID_MODEL_FROM_DATABASE=ES1938/ES1946/ES1969 Solo-1 Audiodrive + +pci:v0000125Dd00001969sv00001014sd00000166* + ID_MODEL_FROM_DATABASE=ES1938/ES1946/ES1969 Solo-1 Audiodrive (ES1969 SOLO-1 AudioDrive on IBM Aptiva Mainboard) + +pci:v0000125Dd00001969sv0000125Dsd00008888* + ID_MODEL_FROM_DATABASE=ES1938/ES1946/ES1969 Solo-1 Audiodrive (Solo-1 Audio Adapter) + +pci:v0000125Dd00001969sv0000125Dsd00008898* + ID_MODEL_FROM_DATABASE=ES1938/ES1946/ES1969 Solo-1 Audiodrive (ES1938S TTSOLO1-SL [TerraTec 128i PCI]) + +pci:v0000125Dd00001969sv0000153Bsd0000111B* + ID_MODEL_FROM_DATABASE=ES1938/ES1946/ES1969 Solo-1 Audiodrive (Terratec 128i PCI) + +pci:v0000125Dd00001978* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E + +pci:v0000125Dd00001978sv00000E11sd0000B112* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E (Armada M700/E500) + +pci:v0000125Dd00001978sv00001033sd0000803C* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E (ES1978 Maestro-2E Audiodrive) + +pci:v0000125Dd00001978sv00001033sd00008058* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E (ES1978 Maestro-2E Audiodrive) + +pci:v0000125Dd00001978sv00001092sd00004000* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E (Monster Sound MX400) + +pci:v0000125Dd00001978sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=ES1978 Maestro 2E (ES1978 Maestro-2E Audiodrive) + +pci:v0000125Dd00001988* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 + +pci:v0000125Dd00001988sv00000E11sd00000098* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (Evo N600c) + +pci:v0000125Dd00001988sv00001092sd00004100* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (Sonic Impact S100) + +pci:v0000125Dd00001988sv0000125Dsd00000431* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (Allegro AudioDrive) + +pci:v0000125Dd00001988sv0000125Dsd00001988* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (ESS Allegro-1 Audiodrive) + +pci:v0000125Dd00001988sv0000125Dsd00001998* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (Allegro AudioDrive) + +pci:v0000125Dd00001988sv0000125Dsd00001999* + ID_MODEL_FROM_DATABASE=ES1988 Allegro-1 (Allegro-1 AudioDrive) + +pci:v0000125Dd00001989* + ID_MODEL_FROM_DATABASE=ESS Modem + +pci:v0000125Dd00001989sv0000125Dsd00001989* + ID_MODEL_FROM_DATABASE=ESS Modem + +pci:v0000125Dd00001998* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Audio Accelerator + +pci:v0000125Dd00001998sv00001028sd000000B1* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Audio Accelerator (Latitude C600) + +pci:v0000125Dd00001998sv00001028sd000000E5* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Audio Accelerator (Latitude C810) + +pci:v0000125Dd00001998sv00001028sd000000E6* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Audio Accelerator (ES1983S Maestro-3i (Dell Inspiron 8100)) + +pci:v0000125Dd00001999* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Modem Accelerator + +pci:v0000125Dd0000199A* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Audio Accelerator + +pci:v0000125Dd0000199B* + ID_MODEL_FROM_DATABASE=ES1983S Maestro-3i PCI Modem Accelerator + +pci:v0000125Dd00002808* + ID_MODEL_FROM_DATABASE=ES336H Fax Modem (Later Model) + +pci:v0000125Dd00002838* + ID_MODEL_FROM_DATABASE=ES2838/2839 SuperLink Modem + +pci:v0000125Dd00002898* + ID_MODEL_FROM_DATABASE=ES2898 Modem + +pci:v0000125Dd00002898sv0000125Dsd00000424* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000125Dsd00000425* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56T-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000125Dsd00000426* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56V-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000125Dsd00000427* + ID_MODEL_FROM_DATABASE=ES2898 Modem (VW-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000125Dsd00000428* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56ST-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000125Dsd00000429* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56SV-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000147Asd0000C001* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56-PI Data Fax Modem) + +pci:v0000125Dd00002898sv0000148Dsd00001030* + ID_MODEL_FROM_DATABASE=ES2898 Modem (HCF WV-PI56 [ESS ES56-PI Data Fax Modem]) + +pci:v0000125Dd00002898sv000014FEsd00000428* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56-PI Data Fax Modem) + +pci:v0000125Dd00002898sv000014FEsd00000429* + ID_MODEL_FROM_DATABASE=ES2898 Modem (ES56-PI Data Fax Modem) + +pci:v0000125E* + ID_VENDOR_FROM_DATABASE=Specialvideo Engineering SRL + +pci:v0000125F* + ID_VENDOR_FROM_DATABASE=Concurrent Technologies, Inc. + +pci:v0000125Fd00002071* + ID_MODEL_FROM_DATABASE=CC PMC/232 + +pci:v0000125Fd00002084* + ID_MODEL_FROM_DATABASE=CC PMC/23P + +pci:v0000125Fd00002091* + ID_MODEL_FROM_DATABASE=CC PMC/422 + +pci:v00001260* + ID_VENDOR_FROM_DATABASE=Intersil Corporation + +pci:v00001260d00003872* + ID_MODEL_FROM_DATABASE=ISL3872 [Prism 3] + +pci:v00001260d00003872sv00001468sd00000202* + ID_MODEL_FROM_DATABASE=ISL3872 [Prism 3] (LAN-Express IEEE 802.11b Wireless LAN) + +pci:v00001260d00003873* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] + +pci:v00001260d00003873sv000010CFsd00001169* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (MBH7WM01-8734 802.11b Wireless Mini PCI Card [ISL3874]) + +pci:v00001260d00003873sv00001186sd00003501* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (DWL-520 Wireless PCI Adapter (rev A or B) [ISL3874]) + +pci:v00001260d00003873sv00001186sd00003700* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (DWL-520 Wireless PCI Adapter (rev E1) [ISL3872]) + +pci:v00001260d00003873sv00001385sd00004105* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (MA311 802.11b wireless adapter [ISL3874]) + +pci:v00001260d00003873sv00001668sd00000414* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (HWP01170-01 802.11b PCI Wireless Adapter) + +pci:v00001260d00003873sv000016A5sd00001601* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (AIR.mate PC-400 PCI Wireless LAN Adapter) + +pci:v00001260d00003873sv00001737sd00003874* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (WMP11 v1 802.11b Wireless-B PCI Adapter [ISL3874]) + +pci:v00001260d00003873sv00004033sd00007033* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (PCW200 802.11b Wireless PCI Adapter [ISL3874]) + +pci:v00001260d00003873sv00008086sd00002510* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (M3AWEB Wireless 802.11b MiniPCI Adapter) + +pci:v00001260d00003873sv00008086sd00002513* + ID_MODEL_FROM_DATABASE=ISL3874 [Prism 2.5]/ISL3872 [Prism 3] (Wireless 802.11b MiniPCI Adapter) + +pci:v00001260d00003877* + ID_MODEL_FROM_DATABASE=ISL3877 [Prism Indigo] + +pci:v00001260d00003886* + ID_MODEL_FROM_DATABASE=ISL3886 [Prism Javelin/Prism Xbow] + +pci:v00001260d00003886sv000017CFsd00000037* + ID_MODEL_FROM_DATABASE=ISL3886 [Prism Javelin/Prism Xbow] (XG-901 and clones Wireless Adapter) + +pci:v00001260d00003890* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] + +pci:v00001260d00003890sv000010B8sd00002802* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (SMC2802W V1 Wireless PCI Adapter [ISL3890]) + +pci:v00001260d00003890sv000010B8sd00002835* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (SMC2835W Wireless Cardbus Adapter) + +pci:v00001260d00003890sv000010B8sd0000A835* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (SMC2835W V2 Wireless Cardbus Adapter) + +pci:v00001260d00003890sv00001113sd00004203* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (WN4201B) + +pci:v00001260d00003890sv00001113sd00008201* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (T-Com T-Sinus 154pcicard Wireless PCI Adapter) + +pci:v00001260d00003890sv00001113sd0000B301* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (T-Sinus 154card Cardbus) + +pci:v00001260d00003890sv00001113sd0000EE03* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (SMC2802W V2 Wireless PCI Adapter [ISL3886]) + +pci:v00001260d00003890sv00001113sd0000EE08* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (SMC2835W V3 EU Wireless Cardbus Adapter) + +pci:v00001260d00003890sv00001186sd00003202* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (DWL-G650 A1 Wireless Adapter) + +pci:v00001260d00003890sv00001259sd0000C104* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (CG-WLCB54GT Wireless Adapter) + +pci:v00001260d00003890sv00001260sd00000000* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (WG511 v1 54 Mbps Wireless PC Card) + +pci:v00001260d00003890sv00001385sd00004800* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (WG511 v2/v3 54 Mbps Wireless PC Card) + +pci:v00001260d00003890sv000016A5sd00001605* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (ALLNET ALL0271 Wireless PCI Adapter) + +pci:v00001260d00003890sv000017CFsd00000014* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (XG-600 and clones Wireless Adapter) + +pci:v00001260d00003890sv000017CFsd00000020* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (XG-900 and clones Wireless Adapter) + +pci:v00001260d00003890sv0000187Esd00003403* + ID_MODEL_FROM_DATABASE=ISL3890 [Prism GT/Prism Duette]/ISL3886 [Prism Javelin/Prism Xbow] (G-110 802.11g Wireless Cardbus Adapter) + +pci:v00001260d00008130* + ID_MODEL_FROM_DATABASE=HMP8130 NTSC/PAL Video Decoder + +pci:v00001260d00008131* + ID_MODEL_FROM_DATABASE=HMP8131 NTSC/PAL Video Decoder + +pci:v00001260d0000FFFF* + ID_MODEL_FROM_DATABASE=ISL3886IK + +pci:v00001260d0000FFFFsv00001260sd00000000* + ID_MODEL_FROM_DATABASE=ISL3886IK (Senao 3054MP+ (J) mini-PCI WLAN 802.11g adapter) + +pci:v00001261* + ID_VENDOR_FROM_DATABASE=Matsushita-Kotobuki Electronics Industries, Ltd. + +pci:v00001262* + ID_VENDOR_FROM_DATABASE=ES Computer Company, Ltd. + +pci:v00001263* + ID_VENDOR_FROM_DATABASE=Sonic Solutions + +pci:v00001264* + ID_VENDOR_FROM_DATABASE=Aval Nagasaki Corporation + +pci:v00001265* + ID_VENDOR_FROM_DATABASE=Casio Computer Co., Ltd. + +pci:v00001266* + ID_VENDOR_FROM_DATABASE=Microdyne Corporation + +pci:v00001266d00000001* + ID_MODEL_FROM_DATABASE=NE10/100 Adapter (i82557B) + +pci:v00001266d00001910* + ID_MODEL_FROM_DATABASE=NE2000Plus (RT8029) Ethernet Adapter + +pci:v00001266d00001910sv00001266sd00001910* + ID_MODEL_FROM_DATABASE=NE2000Plus (RT8029) Ethernet Adapter (NE2000Plus Ethernet Adapter) + +pci:v00001267* + ID_VENDOR_FROM_DATABASE=S. A. Telecommunications + +pci:v00001267d00005352* + ID_MODEL_FROM_DATABASE=PCR2101 + +pci:v00001267d00005A4B* + ID_MODEL_FROM_DATABASE=Telsat Turbo + +pci:v00001268* + ID_VENDOR_FROM_DATABASE=Tektronix + +pci:v00001269* + ID_VENDOR_FROM_DATABASE=Thomson-CSF/TTM + +pci:v0000126A* + ID_VENDOR_FROM_DATABASE=Lexmark International, Inc. + +pci:v0000126B* + ID_VENDOR_FROM_DATABASE=Adax, Inc. + +pci:v0000126C* + ID_VENDOR_FROM_DATABASE=Northern Telecom + +pci:v0000126Cd00001211* + ID_MODEL_FROM_DATABASE=10/100BaseTX [RTL81xx] + +pci:v0000126Cd0000126C* + ID_MODEL_FROM_DATABASE=802.11b Wireless Ethernet Adapter + +pci:v0000126D* + ID_VENDOR_FROM_DATABASE=Splash Technology, Inc. + +pci:v0000126E* + ID_VENDOR_FROM_DATABASE=Sumitomo Metal Industries, Ltd. + +pci:v0000126F* + ID_VENDOR_FROM_DATABASE=Silicon Motion, Inc. + +pci:v0000126Fd00000501* + ID_MODEL_FROM_DATABASE=SM501 VoyagerGX Rev. AA + +pci:v0000126Fd00000510* + ID_MODEL_FROM_DATABASE=SM501 VoyagerGX Rev. B + +pci:v0000126Fd00000710* + ID_MODEL_FROM_DATABASE=SM710 LynxEM + +pci:v0000126Fd00000712* + ID_MODEL_FROM_DATABASE=SM712 LynxEM+ + +pci:v0000126Fd00000718* + ID_MODEL_FROM_DATABASE=SM718 LynxSE+ + +pci:v0000126Fd00000720* + ID_MODEL_FROM_DATABASE=SM720 Lynx3DM + +pci:v0000126Fd00000730* + ID_MODEL_FROM_DATABASE=SM731 Cougar3DR + +pci:v0000126Fd00000750* + ID_MODEL_FROM_DATABASE=SM750 + +pci:v0000126Fd00000810* + ID_MODEL_FROM_DATABASE=SM810 LynxE + +pci:v0000126Fd00000811* + ID_MODEL_FROM_DATABASE=SM811 LynxE + +pci:v0000126Fd00000820* + ID_MODEL_FROM_DATABASE=SM820 Lynx3D + +pci:v0000126Fd00000910* + ID_MODEL_FROM_DATABASE=SM910 + +pci:v00001270* + ID_VENDOR_FROM_DATABASE=Olympus Optical Co., Ltd. + +pci:v00001271* + ID_VENDOR_FROM_DATABASE=GW Instruments + +pci:v00001272* + ID_VENDOR_FROM_DATABASE=Telematics International + +pci:v00001273* + ID_VENDOR_FROM_DATABASE=Hughes Network Systems + +pci:v00001273d00000002* + ID_MODEL_FROM_DATABASE=DirecPC + +pci:v00001274* + ID_VENDOR_FROM_DATABASE=Ensoniq + +pci:v00001274d00001171* + ID_MODEL_FROM_DATABASE=ES1373 / Creative Labs CT5803 [AudioPCI] + +pci:v00001274d00001371* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 + +pci:v00001274d00001371sv00000E11sd00000024* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (AudioPCI on Motherboard Compaq Deskpro) + +pci:v00001274d00001371sv00000E11sd0000B1A7* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI) + +pci:v00001274d00001371sv00001033sd000080AC* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI) + +pci:v00001274d00001371sv00001042sd00001854* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (Tazer) + +pci:v00001274d00001371sv0000107Bsd00008054* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (Tabor2) + +pci:v00001274d00001371sv00001274sd00001371* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (Audio PCI 64V/128/5200 / Creative CT4810/CT5803/CT5806 [Sound Blaster PCI]) + +pci:v00001274d00001371sv00001274sd00008001* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (CT4751 board) + +pci:v00001274d00001371sv00001462sd00006470* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6147 1.1A) + +pci:v00001274d00001371sv00001462sd00006560* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6156 1.10) + +pci:v00001274d00001371sv00001462sd00006630* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6163BX 1.0A) + +pci:v00001274d00001371sv00001462sd00006631* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6163VIA 1.0A) + +pci:v00001274d00001371sv00001462sd00006632* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6163BX 2.0A) + +pci:v00001274d00001371sv00001462sd00006633* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6163VIA 2.0A) + +pci:v00001274d00001371sv00001462sd00006820* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6182 1.00) + +pci:v00001274d00001371sv00001462sd00006822* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6182 1.00A) + +pci:v00001274d00001371sv00001462sd00006830* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6183 1.00) + +pci:v00001274d00001371sv00001462sd00006880* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6188 1.00) + +pci:v00001274d00001371sv00001462sd00006900* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6190 1.00) + +pci:v00001274d00001371sv00001462sd00006910* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6191) + +pci:v00001274d00001371sv00001462sd00006930* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6193) + +pci:v00001274d00001371sv00001462sd00006990* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6199BX 2.0A) + +pci:v00001274d00001371sv00001462sd00006991* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MS-6199VIA 2.0A) + +pci:v00001274d00001371sv000014A4sd00002077* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard KR639) + +pci:v00001274d00001371sv000014A4sd00002105* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MR800) + +pci:v00001274d00001371sv000014A4sd00002107* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard MR801) + +pci:v00001274d00001371sv000014A4sd00002172* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard DR739) + +pci:v00001274d00001371sv00001509sd00009902* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard KW11) + +pci:v00001274d00001371sv00001509sd00009903* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard KW31) + +pci:v00001274d00001371sv00001509sd00009904* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard KA11) + +pci:v00001274d00001371sv00001509sd00009905* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard KC13) + +pci:v00001274d00001371sv0000152Dsd00008801* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard CP810E) + +pci:v00001274d00001371sv0000152Dsd00008802* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard CP810) + +pci:v00001274d00001371sv0000152Dsd00008803* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard P3810E) + +pci:v00001274d00001371sv0000152Dsd00008804* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard P3810-S) + +pci:v00001274d00001371sv0000152Dsd00008805* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard P3820-S) + +pci:v00001274d00001371sv0000270Fsd00002001* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6CTR) + +pci:v00001274d00001371sv0000270Fsd00002200* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6WTX) + +pci:v00001274d00001371sv0000270Fsd00003000* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6WSV) + +pci:v00001274d00001371sv0000270Fsd00003100* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6WIV2) + +pci:v00001274d00001371sv0000270Fsd00003102* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6WIV) + +pci:v00001274d00001371sv0000270Fsd00007060* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard 6ASA2) + +pci:v00001274d00001371sv00008086sd00004249* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard BI440ZX) + +pci:v00001274d00001371sv00008086sd0000424C* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard BL440ZX) + +pci:v00001274d00001371sv00008086sd0000425A* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard BZ440ZX) + +pci:v00001274d00001371sv00008086sd00004341* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard Cayman) + +pci:v00001274d00001371sv00008086sd00004343* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard Cape Cod) + +pci:v00001274d00001371sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (D815EEA Motherboard) + +pci:v00001274d00001371sv00008086sd00004649* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard Fire Island) + +pci:v00001274d00001371sv00008086sd0000464A* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard FJ440ZX) + +pci:v00001274d00001371sv00008086sd00004D4F* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard Montreal) + +pci:v00001274d00001371sv00008086sd00004F43* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard OC440LX) + +pci:v00001274d00001371sv00008086sd00005243* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard RC440BX) + +pci:v00001274d00001371sv00008086sd00005352* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard SunRiver) + +pci:v00001274d00001371sv00008086sd00005643* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard Vancouver) + +pci:v00001274d00001371sv00008086sd00005753* + ID_MODEL_FROM_DATABASE=ES1371/ES1373 / Creative Labs CT2518 (ES1371, ES1373 AudioPCI On Motherboard WS440BX) + +pci:v00001274d00005000* + ID_MODEL_FROM_DATABASE=ES1370 [AudioPCI] + +pci:v00001274d00005880* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 + +pci:v00001274d00005880sv00001274sd00002000* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (Creative CT4810 [Sound Blaster AudioPCI 128]) + +pci:v00001274d00005880sv00001274sd00002003* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (Creative SoundBlaster AudioPCI 128) + +pci:v00001274d00005880sv00001274sd00005880* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (Creative CT4750 [Sound Blaster PCI 128]) + +pci:v00001274d00005880sv00001274sd00008001* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (Sound Blaster 16PCI 4.1ch) + +pci:v00001274d00005880sv00001458sd0000A000* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (5880 AudioPCI On Motherboard 6OXET) + +pci:v00001274d00005880sv00001462sd00006880* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (5880 AudioPCI On Motherboard MS-6188 1.00) + +pci:v00001274d00005880sv0000270Fsd00002001* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (5880 AudioPCI On Motherboard 6CTR) + +pci:v00001274d00005880sv0000270Fsd00002200* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (5880 AudioPCI On Motherboard 6WTX) + +pci:v00001274d00005880sv0000270Fsd00007040* + ID_MODEL_FROM_DATABASE=5880B / Creative Labs CT5880 (5880 AudioPCI On Motherboard 6ATA4) + +pci:v00001274d00008001* + ID_MODEL_FROM_DATABASE=CT5880 [AudioPCI] + +pci:v00001274d00008002* + ID_MODEL_FROM_DATABASE=5880A [AudioPCI] + +pci:v00001275* + ID_VENDOR_FROM_DATABASE=Network Appliance Corporation + +pci:v00001276* + ID_VENDOR_FROM_DATABASE=Switched Network Technologies, Inc. + +pci:v00001277* + ID_VENDOR_FROM_DATABASE=Comstream + +pci:v00001278* + ID_VENDOR_FROM_DATABASE=Transtech Parallel Systems Ltd. + +pci:v00001278d00000701* + ID_MODEL_FROM_DATABASE=TPE3/TM3 PowerPC Node + +pci:v00001278d00000710* + ID_MODEL_FROM_DATABASE=TPE5 PowerPC PCI board + +pci:v00001278d00001100* + ID_MODEL_FROM_DATABASE=PMC-FPGA02 + +pci:v00001278d00001101* + ID_MODEL_FROM_DATABASE=TS-C43 card with 4 ADSP-TS101 processors + +pci:v00001279* + ID_VENDOR_FROM_DATABASE=Transmeta Corporation + +pci:v00001279d00000060* + ID_MODEL_FROM_DATABASE=TM8000 Northbridge + +pci:v00001279d00000061* + ID_MODEL_FROM_DATABASE=TM8000 AGP bridge + +pci:v00001279d00000295* + ID_MODEL_FROM_DATABASE=Northbridge + +pci:v00001279d00000395* + ID_MODEL_FROM_DATABASE=LongRun Northbridge + +pci:v00001279d00000396* + ID_MODEL_FROM_DATABASE=SDRAM controller + +pci:v00001279d00000397* + ID_MODEL_FROM_DATABASE=BIOS scratchpad + +pci:v0000127A* + ID_VENDOR_FROM_DATABASE=Rockwell International + +pci:v0000127Ad00001002* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v0000127Ad00001002sv00001092sd0000094C* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (SupraExpress 56i PRO [Diamond SUP2380]) + +pci:v0000127Ad00001002sv0000122Dsd00004002* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (HPG / MDP3858-U) + +pci:v0000127Ad00001002sv0000122Dsd00004005* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-E) + +pci:v0000127Ad00001002sv0000122Dsd00004007* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-A/-NZ) + +pci:v0000127Ad00001002sv0000122Dsd00004012* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-SA) + +pci:v0000127Ad00001002sv0000122Dsd00004017* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-W) + +pci:v0000127Ad00001002sv0000122Dsd00004018* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-W) + +pci:v0000127Ad00001002sv0000127Asd00001002* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Rockwell 56K D/F HCF Modem) + +pci:v0000127Ad00001003* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v0000127Ad00001003sv00000E11sd0000B0BC* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (229-DF Zephyr) + +pci:v0000127Ad00001003sv00000E11sd0000B114* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (229-DF Cheetah) + +pci:v0000127Ad00001003sv00001033sd0000802B* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (229-DF) + +pci:v0000127Ad00001003sv000013DFsd00001003* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (PCI56RX Modem) + +pci:v0000127Ad00001003sv000013E0sd00000117* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001003sv000013E0sd00000147* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM F-1156IV+/R3 Spain V.90 Modem) + +pci:v0000127Ad00001003sv000013E0sd00000197* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001003sv000013E0sd000001C7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM F-1156IV+/R3 WW V.90 Modem) + +pci:v0000127Ad00001003sv000013E0sd000001F7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001003sv00001436sd00001003* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001003sv00001436sd00001103* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM 5614PM3G V.90 Modem) + +pci:v0000127Ad00001003sv00001436sd00001602* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Compaq 229-DF Ducati) + +pci:v0000127Ad00001004* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v0000127Ad00001004sv00001048sd00001500* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem (MicroLink 56k Modem) + +pci:v0000127Ad00001004sv000010CFsd00001059* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem (Fujitsu 229-DFRT) + +pci:v0000127Ad00001005* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v0000127Ad00001005sv00001005sd0000127A* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (AOpen FM56-P) + +pci:v0000127Ad00001005sv00001033sd00008029* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (229-DFSV) + +pci:v0000127Ad00001005sv00001033sd00008054* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Modem) + +pci:v0000127Ad00001005sv000010CFsd0000103C* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu) + +pci:v0000127Ad00001005sv000010CFsd00001055* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu 229-DFSV) + +pci:v0000127Ad00001005sv000010CFsd00001056* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu 229-DFSV) + +pci:v0000127Ad00001005sv0000122Dsd00004003* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-U) + +pci:v0000127Ad00001005sv0000122Dsd00004006* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Packard Bell MDP3858V-E) + +pci:v0000127Ad00001005sv0000122Dsd00004008* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-A/SP-NZ) + +pci:v0000127Ad00001005sv0000122Dsd00004009* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-E) + +pci:v0000127Ad00001005sv0000122Dsd00004010* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858V-U) + +pci:v0000127Ad00001005sv0000122Dsd00004011* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-SA) + +pci:v0000127Ad00001005sv0000122Dsd00004013* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858V-A/V-NZ) + +pci:v0000127Ad00001005sv0000122Dsd00004015* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-W) + +pci:v0000127Ad00001005sv0000122Dsd00004016* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858V-W) + +pci:v0000127Ad00001005sv0000122Dsd00004019* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858V-SA) + +pci:v0000127Ad00001005sv000013DFsd00001005* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (PCI56RVP Modem) + +pci:v0000127Ad00001005sv000013E0sd00000187* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM) + +pci:v0000127Ad00001005sv000013E0sd000001A7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM) + +pci:v0000127Ad00001005sv000013E0sd000001B7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM DF-1156IV+/R3 Spain V.90 Modem) + +pci:v0000127Ad00001005sv000013E0sd000001D7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM DF-1156IV+/R3 WW V.90 Modem) + +pci:v0000127Ad00001005sv00001436sd00001005* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM) + +pci:v0000127Ad00001005sv00001436sd00001105* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM) + +pci:v0000127Ad00001005sv00001437sd00001105* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (IBM 5614PS3G V.90 Modem) + +pci:v0000127Ad00001022* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v0000127Ad00001022sv00001436sd00001303* + ID_MODEL_FROM_DATABASE=HCF 56k Modem (M3-5614PM3G V.90 Modem) + +pci:v0000127Ad00001023* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v0000127Ad00001023sv0000122Dsd00004020* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Packard Bell MDP3858-WE) + +pci:v0000127Ad00001023sv0000122Dsd00004023* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (MDP3858-UE) + +pci:v0000127Ad00001023sv000013E0sd00000247* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM F-1156IV+/R6 Spain V.90 Modem) + +pci:v0000127Ad00001023sv000013E0sd00000297* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001023sv000013E0sd000002C7* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM F-1156IV+/R6 WW V.90 Modem) + +pci:v0000127Ad00001023sv00001436sd00001203* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001023sv00001436sd00001303* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v0000127Ad00001024* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v0000127Ad00001025* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v0000127Ad00001025sv000010CFsd0000106A* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu 235-DFSV) + +pci:v0000127Ad00001025sv0000122Dsd00004021* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Packard Bell MDP3858V-WE) + +pci:v0000127Ad00001025sv0000122Dsd00004022* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-WE) + +pci:v0000127Ad00001025sv0000122Dsd00004024* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858V-UE) + +pci:v0000127Ad00001025sv0000122Dsd00004025* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (MDP3858SP-UE) + +pci:v0000127Ad00001026* + ID_MODEL_FROM_DATABASE=HCF 56k PCI Speakerphone Modem + +pci:v0000127Ad00001032* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v0000127Ad00001033* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v0000127Ad00001034* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v0000127Ad00001035* + ID_MODEL_FROM_DATABASE=HCF 56k PCI Speakerphone Modem + +pci:v0000127Ad00001036* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v0000127Ad00001085* + ID_MODEL_FROM_DATABASE=HCF 56k Volcano PCI Modem + +pci:v0000127Ad00002004* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v0000127Ad00002005* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v0000127Ad00002005sv0000104Dsd00008044* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (229-DFSV) + +pci:v0000127Ad00002005sv0000104Dsd00008045* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (229-DFSV) + +pci:v0000127Ad00002005sv0000104Dsd00008055* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (PBE/Aztech 235W-DFSV) + +pci:v0000127Ad00002005sv0000104Dsd00008056* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (235-DFSV) + +pci:v0000127Ad00002005sv0000104Dsd0000805A* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Modem) + +pci:v0000127Ad00002005sv0000104Dsd0000805F* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Modem) + +pci:v0000127Ad00002005sv0000104Dsd00008074* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Modem) + +pci:v0000127Ad00002013* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem + +pci:v0000127Ad00002013sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Modem) + +pci:v0000127Ad00002013sv00001179sd0000FF00* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Modem) + +pci:v0000127Ad00002014* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem + +pci:v0000127Ad00002014sv000010CFsd00001057* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (Fujitsu Citicorp III) + +pci:v0000127Ad00002014sv0000122Dsd00004050* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (MSP3880-U) + +pci:v0000127Ad00002014sv0000122Dsd00004055* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (MSP3880-W) + +pci:v0000127Ad00002015* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v0000127Ad00002015sv000010CFsd00001063* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu) + +pci:v0000127Ad00002015sv000010CFsd00001064* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu) + +pci:v0000127Ad00002015sv00001468sd00002015* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu) + +pci:v0000127Ad00002016* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem + +pci:v0000127Ad00002016sv0000122Dsd00004051* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (MSP3880V-W) + +pci:v0000127Ad00002016sv0000122Dsd00004052* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (MSP3880SP-W) + +pci:v0000127Ad00002016sv0000122Dsd00004054* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (MSP3880V-U) + +pci:v0000127Ad00002016sv0000122Dsd00004056* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (MSP3880SP-U) + +pci:v0000127Ad00002016sv0000122Dsd00004057* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (MSP3880SP-A) + +pci:v0000127Ad00004311* + ID_MODEL_FROM_DATABASE=Riptide HSF 56k PCI Modem + +pci:v0000127Ad00004311sv0000127Asd00004311* + ID_MODEL_FROM_DATABASE=Riptide HSF 56k PCI Modem (Ring Modular? Riptide HSF RT HP Dom) + +pci:v0000127Ad00004311sv000013E0sd00000210* + ID_MODEL_FROM_DATABASE=Riptide HSF 56k PCI Modem (HP-GVC) + +pci:v0000127Ad00004320* + ID_MODEL_FROM_DATABASE=Riptide PCI Audio Controller + +pci:v0000127Ad00004320sv00001235sd00004320* + ID_MODEL_FROM_DATABASE=Riptide PCI Audio Controller + +pci:v0000127Ad00004321* + ID_MODEL_FROM_DATABASE=Riptide HCF 56k PCI Modem + +pci:v0000127Ad00004321sv00001235sd00004321* + ID_MODEL_FROM_DATABASE=Riptide HCF 56k PCI Modem (Hewlett Packard DF) + +pci:v0000127Ad00004321sv00001235sd00004324* + ID_MODEL_FROM_DATABASE=Riptide HCF 56k PCI Modem (Hewlett Packard DF) + +pci:v0000127Ad00004321sv000013E0sd00000210* + ID_MODEL_FROM_DATABASE=Riptide HCF 56k PCI Modem (Hewlett Packard DF) + +pci:v0000127Ad00004321sv0000144Dsd00002321* + ID_MODEL_FROM_DATABASE=Riptide HCF 56k PCI Modem (Riptide) + +pci:v0000127Ad00004322* + ID_MODEL_FROM_DATABASE=Riptide PCI Game Controller + +pci:v0000127Ad00004322sv00001235sd00004322* + ID_MODEL_FROM_DATABASE=Riptide PCI Game Controller + +pci:v0000127Ad00008234* + ID_MODEL_FROM_DATABASE=RapidFire 616X ATM155 Adapter + +pci:v0000127Ad00008234sv0000108Dsd00000022* + ID_MODEL_FROM_DATABASE=RapidFire 616X ATM155 Adapter + +pci:v0000127Ad00008234sv0000108Dsd00000027* + ID_MODEL_FROM_DATABASE=RapidFire 616X ATM155 Adapter + +pci:v0000127B* + ID_VENDOR_FROM_DATABASE=Pixera Corporation + +pci:v0000127C* + ID_VENDOR_FROM_DATABASE=Crosspoint Solutions, Inc. + +pci:v0000127D* + ID_VENDOR_FROM_DATABASE=Vela Research + +pci:v0000127E* + ID_VENDOR_FROM_DATABASE=Winnov, L.P. + +pci:v0000127Ed00000010* + ID_MODEL_FROM_DATABASE=Videum 1000 Plus + +pci:v0000127F* + ID_VENDOR_FROM_DATABASE=Fujifilm + +pci:v00001280* + ID_VENDOR_FROM_DATABASE=Photoscript Group Ltd. + +pci:v00001281* + ID_VENDOR_FROM_DATABASE=Yokogawa Electric Corporation + +pci:v00001282* + ID_VENDOR_FROM_DATABASE=Davicom Semiconductor, Inc. + +pci:v00001282d00006585* + ID_MODEL_FROM_DATABASE=DM562P V90 Modem + +pci:v00001282d00009009* + ID_MODEL_FROM_DATABASE=Ethernet 100/10 MBit + +pci:v00001282d00009100* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001282d00009102* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001282d00009102sv00000291sd00008212* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet (DM9102A (DM9102AE, SM9102AF) Ethernet 100/10 MBit) + +pci:v00001282d00009132* + ID_MODEL_FROM_DATABASE=Ethernet 100/10 MBit + +pci:v00001283* + ID_VENDOR_FROM_DATABASE=Integrated Technology Express, Inc. + +pci:v00001283d0000673A* + ID_MODEL_FROM_DATABASE=IT8330G + +pci:v00001283d00008152* + ID_MODEL_FROM_DATABASE=IT8152F/G Advanced RISC-to-PCI Companion Chip + +pci:v00001283d00008211* + ID_MODEL_FROM_DATABASE=ITE 8211F Single Channel UDMA 133 + +pci:v00001283d00008211sv00001043sd00008138* + ID_MODEL_FROM_DATABASE=ITE 8211F Single Channel UDMA 133 (P5GD1-VW Mainboard) + +pci:v00001283d00008212* + ID_MODEL_FROM_DATABASE=IT8212 Dual channel ATA RAID controller + +pci:v00001283d00008212sv00001283sd00000001* + ID_MODEL_FROM_DATABASE=IT8212 Dual channel ATA RAID controller (IT/ITE8212 Dual channel ATA RAID controller) + +pci:v00001283d00008213* + ID_MODEL_FROM_DATABASE=IT8213 IDE Controller + +pci:v00001283d00008213sv00001458sd0000B000* + ID_MODEL_FROM_DATABASE=IT8213 IDE Controller (GA-EG45M-DS2H Mainboard) + +pci:v00001283d00008330* + ID_MODEL_FROM_DATABASE=IT8330G + +pci:v00001283d00008872* + ID_MODEL_FROM_DATABASE=IT887xF PCI to ISA I/O chip with SMB, GPIO, Serial or Parallel Port + +pci:v00001283d00008888* + ID_MODEL_FROM_DATABASE=IT8888F/G PCI to ISA Bridge with SMB [Golden Gate] + +pci:v00001283d00008889* + ID_MODEL_FROM_DATABASE=IT8889F PCI to ISA Bridge + +pci:v00001283d00008893* + ID_MODEL_FROM_DATABASE=IT8893E PCIe to PCI Bridge + +pci:v00001283d0000E886* + ID_MODEL_FROM_DATABASE=IT8330G + +pci:v00001284* + ID_VENDOR_FROM_DATABASE=Sahara Networks, Inc. + +pci:v00001285* + ID_VENDOR_FROM_DATABASE=Platform Technologies, Inc. + +pci:v00001285d00000100* + ID_MODEL_FROM_DATABASE=AGOGO sound chip (aka ESS Maestro 1) + +pci:v00001286* + ID_VENDOR_FROM_DATABASE=Mazet GmbH + +pci:v00001287* + ID_VENDOR_FROM_DATABASE=M-Pact, Inc. + +pci:v00001287d0000001E* + ID_MODEL_FROM_DATABASE=LS220D DVD Decoder + +pci:v00001287d0000001F* + ID_MODEL_FROM_DATABASE=LS220C DVD Decoder + +pci:v00001288* + ID_VENDOR_FROM_DATABASE=Timestep Corporation + +pci:v00001289* + ID_VENDOR_FROM_DATABASE=AVC Technology, Inc. + +pci:v0000128A* + ID_VENDOR_FROM_DATABASE=Asante Technologies, Inc. + +pci:v0000128B* + ID_VENDOR_FROM_DATABASE=Transwitch Corporation + +pci:v0000128C* + ID_VENDOR_FROM_DATABASE=Retix Corporation + +pci:v0000128D* + ID_VENDOR_FROM_DATABASE=G2 Networks, Inc. + +pci:v0000128Dd00000021* + ID_MODEL_FROM_DATABASE=ATM155 Adapter + +pci:v0000128E* + ID_VENDOR_FROM_DATABASE=Hoontech Corporation/Samho Multi Tech Ltd. + +pci:v0000128Ed00000008* + ID_MODEL_FROM_DATABASE=ST128 WSS/SB + +pci:v0000128Ed00000009* + ID_MODEL_FROM_DATABASE=ST128 SAM9407 + +pci:v0000128Ed0000000A* + ID_MODEL_FROM_DATABASE=ST128 Game Port + +pci:v0000128Ed0000000B* + ID_MODEL_FROM_DATABASE=ST128 MPU Port + +pci:v0000128Ed0000000C* + ID_MODEL_FROM_DATABASE=ST128 Ctrl Port + +pci:v0000128F* + ID_VENDOR_FROM_DATABASE=Tateno Dennou, Inc. + +pci:v00001290* + ID_VENDOR_FROM_DATABASE=Sord Computer Corporation + +pci:v00001291* + ID_VENDOR_FROM_DATABASE=NCS Computer Italia + +pci:v00001292* + ID_VENDOR_FROM_DATABASE=Tritech Microelectronics Inc + +pci:v00001292d0000FC02* + ID_MODEL_FROM_DATABASE=Pyramid3D TR25202 + +pci:v00001293* + ID_VENDOR_FROM_DATABASE=Media Reality Technology + +pci:v00001294* + ID_VENDOR_FROM_DATABASE=Rhetorex, Inc. + +pci:v00001295* + ID_VENDOR_FROM_DATABASE=Imagenation Corporation + +pci:v00001295d00000800* + ID_MODEL_FROM_DATABASE=PXR800 + +pci:v00001295d00001000* + ID_MODEL_FROM_DATABASE=PXD1000 + +pci:v00001296* + ID_VENDOR_FROM_DATABASE=Kofax Image Products + +pci:v00001297* + ID_VENDOR_FROM_DATABASE=Holco Enterprise Co, Ltd/Shuttle Computer + +pci:v00001298* + ID_VENDOR_FROM_DATABASE=Spellcaster Telecommunications Inc. + +pci:v00001299* + ID_VENDOR_FROM_DATABASE=Knowledge Technology Lab. + +pci:v0000129A* + ID_VENDOR_FROM_DATABASE=VMetro, inc. + +pci:v0000129Ad00000615* + ID_MODEL_FROM_DATABASE=PBT-615 PCI-X Bus Analyzer + +pci:v0000129Ad00001100* + ID_MODEL_FROM_DATABASE=PMC-FPGA05 + +pci:v0000129Ad00001106* + ID_MODEL_FROM_DATABASE=XMC-FPGA05F, PCI interface + +pci:v0000129Ad00001107* + ID_MODEL_FROM_DATABASE=XMC-FPGA05F, PCIe interface + +pci:v0000129Ad00001108* + ID_MODEL_FROM_DATABASE=XMC-FPGA05D, PCI interface + +pci:v0000129Ad00001109* + ID_MODEL_FROM_DATABASE=XMC-FPGA05D, PCIe interface + +pci:v0000129B* + ID_VENDOR_FROM_DATABASE=Image Access + +pci:v0000129C* + ID_VENDOR_FROM_DATABASE=Jaycor + +pci:v0000129D* + ID_VENDOR_FROM_DATABASE=Compcore Multimedia, Inc. + +pci:v0000129E* + ID_VENDOR_FROM_DATABASE=Victor Company of Japan, Ltd. + +pci:v0000129F* + ID_VENDOR_FROM_DATABASE=OEC Medical Systems, Inc. + +pci:v000012A0* + ID_VENDOR_FROM_DATABASE=Allen-Bradley Company + +pci:v000012A1* + ID_VENDOR_FROM_DATABASE=Simpact Associates, Inc. + +pci:v000012A2* + ID_VENDOR_FROM_DATABASE=Newgen Systems Corporation + +pci:v000012A3* + ID_VENDOR_FROM_DATABASE=Lucent Technologies + +pci:v000012A3d00008105* + ID_MODEL_FROM_DATABASE=T8105 H100 Digital Switch + +pci:v000012A4* + ID_VENDOR_FROM_DATABASE=NTT Electronics Technology Company + +pci:v000012A5* + ID_VENDOR_FROM_DATABASE=Vision Dynamics Ltd. + +pci:v000012A6* + ID_VENDOR_FROM_DATABASE=Scalable Networks, Inc. + +pci:v000012A7* + ID_VENDOR_FROM_DATABASE=AMO GmbH + +pci:v000012A8* + ID_VENDOR_FROM_DATABASE=News Datacom + +pci:v000012A9* + ID_VENDOR_FROM_DATABASE=Xiotech Corporation + +pci:v000012AA* + ID_VENDOR_FROM_DATABASE=SDL Communications, Inc. + +pci:v000012AB* + ID_VENDOR_FROM_DATABASE=YUAN High-Tech Development Co., Ltd. + +pci:v000012ABd00000000* + ID_MODEL_FROM_DATABASE=MPG160/Kuroutoshikou ITVC15-STVLP + +pci:v000012ABd00000002* + ID_MODEL_FROM_DATABASE=AU8830 [Vortex2] Based Sound Card With A3D Support + +pci:v000012ABd00000003* + ID_MODEL_FROM_DATABASE=T507 (DVB-T) TV tuner/capture device + +pci:v000012ABd00002300* + ID_MODEL_FROM_DATABASE=Club-3D Zap TV2100 + +pci:v000012ABd00003000* + ID_MODEL_FROM_DATABASE=MPG-200C PCI DVD Decoder Card + +pci:v000012ABd00004789* + ID_MODEL_FROM_DATABASE=MPC788 MiniPCI Hybrid TV Tuner + +pci:v000012ABd0000FFF3* + ID_MODEL_FROM_DATABASE=MPG600/Kuroutoshikou ITVC16-STVLP + +pci:v000012ABd0000FFFF* + ID_MODEL_FROM_DATABASE=MPG600/Kuroutoshikou ITVC16-STVLP + +pci:v000012AC* + ID_VENDOR_FROM_DATABASE=Measurex Corporation + +pci:v000012AD* + ID_VENDOR_FROM_DATABASE=Multidata GmbH + +pci:v000012AE* + ID_VENDOR_FROM_DATABASE=Alteon Networks Inc. + +pci:v000012AEd00000001* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet + +pci:v000012AEd00000001sv00001014sd00000104* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet (Gigabit Ethernet-SX PCI Adapter) + +pci:v000012AEd00000001sv000012AEsd00000001* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet (Gigabit Ethernet-SX (Universal)) + +pci:v000012AEd00000002* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet (Copper) + +pci:v000012AEd00000002sv000010A9sd00008002* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet (Copper) (Acenic Gigabit Ethernet) + +pci:v000012AEd00000002sv000012AEsd00000002* + ID_MODEL_FROM_DATABASE=AceNIC Gigabit Ethernet (Copper) (Gigabit Ethernet-T (3C986-T)) + +pci:v000012AEd000000FA* + ID_MODEL_FROM_DATABASE=Farallon PN9100-T Gigabit Ethernet + +pci:v000012AF* + ID_VENDOR_FROM_DATABASE=TDK USA Corp + +pci:v000012B0* + ID_VENDOR_FROM_DATABASE=Jorge Scientific Corp + +pci:v000012B1* + ID_VENDOR_FROM_DATABASE=GammaLink + +pci:v000012B2* + ID_VENDOR_FROM_DATABASE=General Signal Networks + +pci:v000012B3* + ID_VENDOR_FROM_DATABASE=Inter-Face Co Ltd + +pci:v000012B4* + ID_VENDOR_FROM_DATABASE=FutureTel Inc + +pci:v000012B5* + ID_VENDOR_FROM_DATABASE=Granite Systems Inc. + +pci:v000012B6* + ID_VENDOR_FROM_DATABASE=Natural Microsystems + +pci:v000012B7* + ID_VENDOR_FROM_DATABASE=Cognex Modular Vision Systems Div. - Acumen Inc. + +pci:v000012B8* + ID_VENDOR_FROM_DATABASE=Korg + +pci:v000012B9* + ID_VENDOR_FROM_DATABASE=3Com Corp, Modem Division + +pci:v000012B9d00001006* + ID_MODEL_FROM_DATABASE=WinModem + +pci:v000012B9d00001006sv000012B9sd0000005C* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Model 3472)) + +pci:v000012B9d00001006sv000012B9sd0000005E* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal WinModem (Models 662975)) + +pci:v000012B9d00001006sv000012B9sd00000062* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Model 662978)) + +pci:v000012B9d00001006sv000012B9sd00000068* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Model 5690)) + +pci:v000012B9d00001006sv000012B9sd0000007A* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Model 662974)) + +pci:v000012B9d00001006sv000012B9sd0000007F* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal WinModem (Models 5698, 5699)) + +pci:v000012B9d00001006sv000012B9sd00000080* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal WinModem (Models 2975, 3528)) + +pci:v000012B9d00001006sv000012B9sd00000081* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Models 2974, 3529)) + +pci:v000012B9d00001006sv000012B9sd00000091* + ID_MODEL_FROM_DATABASE=WinModem (USR 56k Internal Voice WinModem (Model 2978)) + +pci:v000012B9d00001007* + ID_MODEL_FROM_DATABASE=USR 56k Internal WinModem + +pci:v000012B9d00001007sv000012B9sd000000A3* + ID_MODEL_FROM_DATABASE=USR 56k Internal WinModem ((Model 3595)) + +pci:v000012B9d00001007sv000012B9sd000000C4* + ID_MODEL_FROM_DATABASE=USR 56k Internal WinModem (U.S. Robotics V.92 Voice Faxmodem (2884A/B/C)) + +pci:v000012B9d00001008* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 + +pci:v000012B9d00001008sv000012B9sd000000A2* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56k Internal FAX Modem (Model 2977)) + +pci:v000012B9d00001008sv000012B9sd000000AA* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56k Internal Voice Modem (Model 2976)) + +pci:v000012B9d00001008sv000012B9sd000000AB* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56k Internal Voice Modem (Model 5609)) + +pci:v000012B9d00001008sv000012B9sd000000AC* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56k Internal Voice Modem (Model 3298)) + +pci:v000012B9d00001008sv000012B9sd000000AD* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56k Internal FAX Modem (Model 5610)) + +pci:v000012B9d00001008sv000012B9sd000000D3* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56K Internal V92 FAX Modem (Model 5610)) + +pci:v000012B9d00001008sv000012B9sd0000BABA* + ID_MODEL_FROM_DATABASE=56K FaxModem Model 5610 (USR 56K Internal Voice Modem 3CP3298-DEL (Model 5601) [Hawk]) + +pci:v000012BA* + ID_VENDOR_FROM_DATABASE=BittWare, Inc. + +pci:v000012BB* + ID_VENDOR_FROM_DATABASE=Nippon Unisoft Corporation + +pci:v000012BC* + ID_VENDOR_FROM_DATABASE=Array Microsystems + +pci:v000012BD* + ID_VENDOR_FROM_DATABASE=Computerm Corp. + +pci:v000012BE* + ID_VENDOR_FROM_DATABASE=Anchor Chips Inc. + +pci:v000012BEd00003041* + ID_MODEL_FROM_DATABASE=AN3041Q CO-MEM + +pci:v000012BEd00003042* + ID_MODEL_FROM_DATABASE=AN3042Q CO-MEM Lite + +pci:v000012BEd00003042sv000012BEsd00003042* + ID_MODEL_FROM_DATABASE=AN3042Q CO-MEM Lite (Anchor Chips Lite Evaluation Board) + +pci:v000012BF* + ID_VENDOR_FROM_DATABASE=Fujifilm Microdevices + +pci:v000012C0* + ID_VENDOR_FROM_DATABASE=Infimed + +pci:v000012C1* + ID_VENDOR_FROM_DATABASE=GMM Research Corp + +pci:v000012C2* + ID_VENDOR_FROM_DATABASE=Mentec Limited + +pci:v000012C3* + ID_VENDOR_FROM_DATABASE=Holtek Microelectronics Inc + +pci:v000012C3d00000058* + ID_MODEL_FROM_DATABASE=PCI NE2K Ethernet + +pci:v000012C3d00005598* + ID_MODEL_FROM_DATABASE=PCI NE2K Ethernet + +pci:v000012C4* + ID_VENDOR_FROM_DATABASE=Connect Tech Inc + +pci:v000012C4d00000001* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 8 (RS232/CL/RJ11) + +pci:v000012C4d00000002* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 4 (RS232) + +pci:v000012C4d00000003* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2 (RS232) + +pci:v000012C4d00000004* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 8 (UNIV, RS485) + +pci:v000012C4d00000005* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 4+4/6+2 (UNIV, RS232/485) + +pci:v000012C4d00000006* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 4 (OPTO, RS485) + +pci:v000012C4d00000007* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2+2 (RS232/485) + +pci:v000012C4d00000008* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2 (OPTO, Tx, RS485) + +pci:v000012C4d00000009* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2+6 (RS232/485) + +pci:v000012C4d0000000A* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 8 (Tx, RS485) + +pci:v000012C4d0000000B* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 4 (Tx, RS485) + +pci:v000012C4d0000000C* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2 (20 MHz, RS485) + +pci:v000012C4d0000000D* + ID_MODEL_FROM_DATABASE=Blue HEAT/PCI 2 PTM + +pci:v000012C4d00000100* + ID_MODEL_FROM_DATABASE=NT960/PCI + +pci:v000012C4d00000201* + ID_MODEL_FROM_DATABASE=cPCI Titan - 2 Port + +pci:v000012C4d00000202* + ID_MODEL_FROM_DATABASE=cPCI Titan - 4 Port + +pci:v000012C4d00000300* + ID_MODEL_FROM_DATABASE=CTI PCI UART 2 (RS232) + +pci:v000012C4d00000301* + ID_MODEL_FROM_DATABASE=CTI PCI UART 4 (RS232) + +pci:v000012C4d00000302* + ID_MODEL_FROM_DATABASE=CTI PCI UART 8 (RS232) + +pci:v000012C4d00000310* + ID_MODEL_FROM_DATABASE=CTI PCI UART 1+1 (RS232/485) + +pci:v000012C4d00000311* + ID_MODEL_FROM_DATABASE=CTI PCI UART 2+2 (RS232/485) + +pci:v000012C4d00000312* + ID_MODEL_FROM_DATABASE=CTI PCI UART 4+4 (RS232/485) + +pci:v000012C4d00000320* + ID_MODEL_FROM_DATABASE=CTI PCI UART 2 + +pci:v000012C4d00000321* + ID_MODEL_FROM_DATABASE=CTI PCI UART 4 + +pci:v000012C4d00000322* + ID_MODEL_FROM_DATABASE=CTI PCI UART 8 + +pci:v000012C4d00000330* + ID_MODEL_FROM_DATABASE=CTI PCI UART 2 (RS485) + +pci:v000012C4d00000331* + ID_MODEL_FROM_DATABASE=CTI PCI UART 4 (RS485) + +pci:v000012C4d00000332* + ID_MODEL_FROM_DATABASE=CTI PCI UART 8 (RS485) + +pci:v000012C5* + ID_VENDOR_FROM_DATABASE=Picture Elements Incorporated + +pci:v000012C5d0000007E* + ID_MODEL_FROM_DATABASE=Imaging/Scanning Subsystem Engine + +pci:v000012C5d0000007F* + ID_MODEL_FROM_DATABASE=Imaging/Scanning Subsystem Engine + +pci:v000012C5d00000081* + ID_MODEL_FROM_DATABASE=PCIVST [Grayscale Thresholding Engine] + +pci:v000012C5d00000085* + ID_MODEL_FROM_DATABASE=Video Simulator/Sender + +pci:v000012C5d00000086* + ID_MODEL_FROM_DATABASE=THR2 Multi-scale Thresholder + +pci:v000012C6* + ID_VENDOR_FROM_DATABASE=Mitani Corporation + +pci:v000012C7* + ID_VENDOR_FROM_DATABASE=Dialogic Corp + +pci:v000012C7d00000546* + ID_MODEL_FROM_DATABASE=Springware D/120JCT-LS + +pci:v000012C7d00000647* + ID_MODEL_FROM_DATABASE=Springware D/240JCT-T1 + +pci:v000012C7d00000676* + ID_MODEL_FROM_DATABASE=Springware D/41JCT-LS + +pci:v000012C7d00000685* + ID_MODEL_FROM_DATABASE=Springware D/480JCT-2T1 + +pci:v000012C8* + ID_VENDOR_FROM_DATABASE=G Force Co, Ltd + +pci:v000012C9* + ID_VENDOR_FROM_DATABASE=Gigi Operations + +pci:v000012CA* + ID_VENDOR_FROM_DATABASE=Integrated Computing Engines + +pci:v000012CB* + ID_VENDOR_FROM_DATABASE=Antex Electronics Corporation + +pci:v000012CBd00000027* + ID_MODEL_FROM_DATABASE=SC4 (StudioCard) + +pci:v000012CBd0000002E* + ID_MODEL_FROM_DATABASE=StudioCard 2000 + +pci:v000012CC* + ID_VENDOR_FROM_DATABASE=Pluto Technologies International + +pci:v000012CD* + ID_VENDOR_FROM_DATABASE=Aims Lab + +pci:v000012CE* + ID_VENDOR_FROM_DATABASE=Netspeed Inc. + +pci:v000012CF* + ID_VENDOR_FROM_DATABASE=Prophet Systems, Inc. + +pci:v000012D0* + ID_VENDOR_FROM_DATABASE=GDE Systems, Inc. + +pci:v000012D1* + ID_VENDOR_FROM_DATABASE=PSITech + +pci:v000012D2* + ID_VENDOR_FROM_DATABASE=NVidia / SGS Thomson (Joint Venture) + +pci:v000012D2d00000008* + ID_MODEL_FROM_DATABASE=NV1 + +pci:v000012D2d00000009* + ID_MODEL_FROM_DATABASE=DAC64 + +pci:v000012D2d00000018* + ID_MODEL_FROM_DATABASE=Riva128 + +pci:v000012D2d00000018sv00001048sd00000C10* + ID_MODEL_FROM_DATABASE=Riva128 (VICTORY Erazor) + +pci:v000012D2d00000018sv0000107Bsd00008030* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv00001092sd00000350* + ID_MODEL_FROM_DATABASE=Riva128 (Viper V330) + +pci:v000012D2d00000018sv00001092sd00001092* + ID_MODEL_FROM_DATABASE=Riva128 (Viper V330) + +pci:v000012D2d00000018sv000010B4sd00001B1B* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv000010B4sd00001B1D* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv000010B4sd00001B1E* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128, PAL TV-Out) + +pci:v000012D2d00000018sv000010B4sd00001B20* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 Sapphire) + +pci:v000012D2d00000018sv000010B4sd00001B21* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv000010B4sd00001B22* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 AGP, NTSC TV-Out) + +pci:v000012D2d00000018sv000010B4sd00001B23* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 AGP, PAL TV-Out) + +pci:v000012D2d00000018sv000010B4sd00001B27* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 DVD) + +pci:v000012D2d00000018sv000010B4sd00001B88* + ID_MODEL_FROM_DATABASE=Riva128 (MVP Pro 128) + +pci:v000012D2d00000018sv000010B4sd0000222A* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 AGP) + +pci:v000012D2d00000018sv000010B4sd00002230* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv000010B4sd00002232* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128) + +pci:v000012D2d00000018sv000010B4sd00002235* + ID_MODEL_FROM_DATABASE=Riva128 (STB Velocity 128 AGP) + +pci:v000012D2d00000018sv00002A15sd000054A3* + ID_MODEL_FROM_DATABASE=Riva128 (3DVision-SAGP / 3DexPlorer 3000) + +pci:v000012D2d00000019* + ID_MODEL_FROM_DATABASE=Riva128ZX + +pci:v000012D2d00000020* + ID_MODEL_FROM_DATABASE=TNT + +pci:v000012D2d00000028* + ID_MODEL_FROM_DATABASE=TNT2 + +pci:v000012D2d00000029* + ID_MODEL_FROM_DATABASE=UTNT2 + +pci:v000012D2d0000002C* + ID_MODEL_FROM_DATABASE=VTNT2 + +pci:v000012D2d000000A0* + ID_MODEL_FROM_DATABASE=ITNT2 + +pci:v000012D3* + ID_VENDOR_FROM_DATABASE=Vingmed Sound A/S + +pci:v000012D4* + ID_VENDOR_FROM_DATABASE=Ulticom (Formerly DGM&S) + +pci:v000012D4d00000200* + ID_MODEL_FROM_DATABASE=T1 Card + +pci:v000012D5* + ID_VENDOR_FROM_DATABASE=Equator Technologies Inc + +pci:v000012D5d00000003* + ID_MODEL_FROM_DATABASE=BSP16 + +pci:v000012D5d00001000* + ID_MODEL_FROM_DATABASE=BSP15 + +pci:v000012D6* + ID_VENDOR_FROM_DATABASE=Analogic Corp + +pci:v000012D7* + ID_VENDOR_FROM_DATABASE=Biotronic SRL + +pci:v000012D8* + ID_VENDOR_FROM_DATABASE=Pericom Semiconductor + +pci:v000012D8d000001A7* + ID_MODEL_FROM_DATABASE=PI7C21P100 PCI to PCI Bridge + +pci:v000012D8d0000400A* + ID_MODEL_FROM_DATABASE=PI7C9X442SL PCI Express Bridge Port + +pci:v000012D8d0000400E* + ID_MODEL_FROM_DATABASE=PI7C9X442SL USB OHCI Controller + +pci:v000012D8d0000400F* + ID_MODEL_FROM_DATABASE=PI7C9X442SL USB EHCI Controller + +pci:v000012D8d000071E2* + ID_MODEL_FROM_DATABASE=PI7C7300A/PI7C7300D PCI-to-PCI Bridge + +pci:v000012D8d000071E3* + ID_MODEL_FROM_DATABASE=PI7C7300A/PI7C7300D PCI-to-PCI Bridge (Secondary Bus 2) + +pci:v000012D8d00008140* + ID_MODEL_FROM_DATABASE=PI7C8140A PCI-to-PCI Bridge + +pci:v000012D8d00008148* + ID_MODEL_FROM_DATABASE=PI7C8148A/PI7C8148B PCI-to-PCI Bridge + +pci:v000012D8d00008150* + ID_MODEL_FROM_DATABASE=PCI to PCI Bridge + +pci:v000012D8d00008152* + ID_MODEL_FROM_DATABASE=PI7C8152A/PI7C8152B/PI7C8152BI PCI-to-PCI Bridge + +pci:v000012D8d00008154* + ID_MODEL_FROM_DATABASE=PI7C8154A/PI7C8154B/PI7C8154BI PCI-to-PCI Bridge + +pci:v000012D8d0000E110* + ID_MODEL_FROM_DATABASE=PI7C9X110 PCI Express to PCI bridge + +pci:v000012D8d0000E110sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=PI7C9X110 PCI Express to PCI bridge (CC11/CL11 CompactPCI Bridge) + +pci:v000012D8d0000E111* + ID_MODEL_FROM_DATABASE=PI7C9X111SL PCIe-to-PCI Reversible Bridge + +pci:v000012D8d0000E130* + ID_MODEL_FROM_DATABASE=PCI Express to PCI-XPI7C9X130 PCI-X Bridge + +pci:v000012D9* + ID_VENDOR_FROM_DATABASE=Aculab PLC + +pci:v000012D9d00000002* + ID_MODEL_FROM_DATABASE=PCI Prosody + +pci:v000012D9d00000004* + ID_MODEL_FROM_DATABASE=cPCI Prosody + +pci:v000012D9d00000005* + ID_MODEL_FROM_DATABASE=Aculab E1/T1 PCI card + +pci:v000012D9d00001078* + ID_MODEL_FROM_DATABASE=Prosody X class e1000 device + +pci:v000012D9d00001078sv000012D9sd0000000D* + ID_MODEL_FROM_DATABASE=Prosody X class e1000 device (Prosody X PCI) + +pci:v000012D9d00001078sv000012D9sd0000000E* + ID_MODEL_FROM_DATABASE=Prosody X class e1000 device (Prosody X cPCI) + +pci:v000012DA* + ID_VENDOR_FROM_DATABASE=True Time Inc. + +pci:v000012DB* + ID_VENDOR_FROM_DATABASE=Annapolis Micro Systems, Inc + +pci:v000012DC* + ID_VENDOR_FROM_DATABASE=Symicron Computer Communication Ltd. + +pci:v000012DD* + ID_VENDOR_FROM_DATABASE=Management Graphics + +pci:v000012DE* + ID_VENDOR_FROM_DATABASE=Rainbow Technologies + +pci:v000012DEd00000200* + ID_MODEL_FROM_DATABASE=CryptoSwift CS200 + +pci:v000012DF* + ID_VENDOR_FROM_DATABASE=SBS Technologies Inc + +pci:v000012E0* + ID_VENDOR_FROM_DATABASE=Chase Research + +pci:v000012E0d00000010* + ID_MODEL_FROM_DATABASE=ST16C654 Quad UART + +pci:v000012E0d00000020* + ID_MODEL_FROM_DATABASE=ST16C654 Quad UART + +pci:v000012E0d00000030* + ID_MODEL_FROM_DATABASE=ST16C654 Quad UART + +pci:v000012E1* + ID_VENDOR_FROM_DATABASE=Nintendo Co, Ltd + +pci:v000012E2* + ID_VENDOR_FROM_DATABASE=Datum Inc. Bancomm-Timing Division + +pci:v000012E3* + ID_VENDOR_FROM_DATABASE=Imation Corp - Medical Imaging Systems + +pci:v000012E4* + ID_VENDOR_FROM_DATABASE=Brooktrout Technology Inc + +pci:v000012E5* + ID_VENDOR_FROM_DATABASE=Apex Semiconductor Inc + +pci:v000012E6* + ID_VENDOR_FROM_DATABASE=Cirel Systems + +pci:v000012E7* + ID_VENDOR_FROM_DATABASE=Sunsgroup Corporation + +pci:v000012E8* + ID_VENDOR_FROM_DATABASE=Crisc Corp + +pci:v000012E9* + ID_VENDOR_FROM_DATABASE=GE Spacenet + +pci:v000012EA* + ID_VENDOR_FROM_DATABASE=Zuken + +pci:v000012EB* + ID_VENDOR_FROM_DATABASE=Aureal Semiconductor + +pci:v000012EBd00000001* + ID_MODEL_FROM_DATABASE=Vortex 1 + +pci:v000012EBd00000001sv0000104Dsd00008036* + ID_MODEL_FROM_DATABASE=Vortex 1 (AU8820 Vortex Digital Audio Processor) + +pci:v000012EBd00000001sv00001092sd00002000* + ID_MODEL_FROM_DATABASE=Vortex 1 (Sonic Impact A3D) + +pci:v000012EBd00000001sv00001092sd00002100* + ID_MODEL_FROM_DATABASE=Vortex 1 (Sonic Impact A3D) + +pci:v000012EBd00000001sv00001092sd00002110* + ID_MODEL_FROM_DATABASE=Vortex 1 (Sonic Impact A3D) + +pci:v000012EBd00000001sv00001092sd00002200* + ID_MODEL_FROM_DATABASE=Vortex 1 (Sonic Impact A3D) + +pci:v000012EBd00000001sv0000122Dsd00001002* + ID_MODEL_FROM_DATABASE=Vortex 1 (AU8820 Vortex Digital Audio Processor) + +pci:v000012EBd00000001sv000012EBsd00000001* + ID_MODEL_FROM_DATABASE=Vortex 1 (AU8820 Vortex Digital Audio Processor) + +pci:v000012EBd00000001sv00005053sd00003355* + ID_MODEL_FROM_DATABASE=Vortex 1 (Montego) + +pci:v000012EBd00000001sv000050B2sd00001111* + ID_MODEL_FROM_DATABASE=Vortex 1 (XLerate) + +pci:v000012EBd00000002* + ID_MODEL_FROM_DATABASE=Vortex 2 + +pci:v000012EBd00000002sv0000104Dsd00008049* + ID_MODEL_FROM_DATABASE=Vortex 2 (AU8830 Vortex 3D Digital Audio Processor) + +pci:v000012EBd00000002sv0000104Dsd0000807B* + ID_MODEL_FROM_DATABASE=Vortex 2 (AU8830 Vortex 3D Digital Audio Processor) + +pci:v000012EBd00000002sv00001092sd00003000* + ID_MODEL_FROM_DATABASE=Vortex 2 (Monster Sound II) + +pci:v000012EBd00000002sv00001092sd00003001* + ID_MODEL_FROM_DATABASE=Vortex 2 (Monster Sound II) + +pci:v000012EBd00000002sv00001092sd00003002* + ID_MODEL_FROM_DATABASE=Vortex 2 (Monster Sound II) + +pci:v000012EBd00000002sv00001092sd00003003* + ID_MODEL_FROM_DATABASE=Vortex 2 (Monster Sound II) + +pci:v000012EBd00000002sv00001092sd00003004* + ID_MODEL_FROM_DATABASE=Vortex 2 (Monster Sound II) + +pci:v000012EBd00000002sv000012EBsd00000002* + ID_MODEL_FROM_DATABASE=Vortex 2 (AU8830 Vortex 3D Digital Audio Processor) + +pci:v000012EBd00000002sv000012EBsd00000088* + ID_MODEL_FROM_DATABASE=Vortex 2 (AU8830 Vortex 3D Digital Audio Processor) + +pci:v000012EBd00000002sv0000144Dsd00003510* + ID_MODEL_FROM_DATABASE=Vortex 2 (AU8830 Vortex 3D Digital Audio Processor) + +pci:v000012EBd00000002sv00005053sd00003356* + ID_MODEL_FROM_DATABASE=Vortex 2 (Montego II) + +pci:v000012EBd00000003* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv0000104Dsd00008049* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv0000104Dsd00008077* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv0000109Fsd00001000* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv000012EBsd00000003* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv00001462sd00006780* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv000014A4sd00002073* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv000014A4sd00002091* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv000014A4sd00002104* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00000003sv000014A4sd00002106* + ID_MODEL_FROM_DATABASE=AU8810 Vortex Digital Audio Processor + +pci:v000012EBd00008803* + ID_MODEL_FROM_DATABASE=Vortex 56k Software Modem + +pci:v000012EBd00008803sv000012EBsd00008803* + ID_MODEL_FROM_DATABASE=Vortex 56k Software Modem + +pci:v000012EC* + ID_VENDOR_FROM_DATABASE=3A International, Inc. + +pci:v000012ED* + ID_VENDOR_FROM_DATABASE=Optivision Inc. + +pci:v000012EE* + ID_VENDOR_FROM_DATABASE=Orange Micro + +pci:v000012EF* + ID_VENDOR_FROM_DATABASE=Vienna Systems + +pci:v000012F0* + ID_VENDOR_FROM_DATABASE=Pentek + +pci:v000012F1* + ID_VENDOR_FROM_DATABASE=Sorenson Vision Inc + +pci:v000012F2* + ID_VENDOR_FROM_DATABASE=Gammagraphx, Inc. + +pci:v000012F3* + ID_VENDOR_FROM_DATABASE=Radstone Technology + +pci:v000012F4* + ID_VENDOR_FROM_DATABASE=Megatel + +pci:v000012F5* + ID_VENDOR_FROM_DATABASE=Forks + +pci:v000012F6* + ID_VENDOR_FROM_DATABASE=Dawson France + +pci:v000012F7* + ID_VENDOR_FROM_DATABASE=Cognex + +pci:v000012F8* + ID_VENDOR_FROM_DATABASE=Electronic Design GmbH + +pci:v000012F8d00000002* + ID_MODEL_FROM_DATABASE=VideoMaker + +pci:v000012F9* + ID_VENDOR_FROM_DATABASE=Four Fold Ltd + +pci:v000012FB* + ID_VENDOR_FROM_DATABASE=Spectrum Signal Processing + +pci:v000012FBd00000001* + ID_MODEL_FROM_DATABASE=PMC-MAI + +pci:v000012FBd000000F5* + ID_MODEL_FROM_DATABASE=F5 Dakar + +pci:v000012FBd000002AD* + ID_MODEL_FROM_DATABASE=PMC-2MAI + +pci:v000012FBd00002ADC* + ID_MODEL_FROM_DATABASE=ePMC-2ADC + +pci:v000012FBd00003100* + ID_MODEL_FROM_DATABASE=PRO-3100 + +pci:v000012FBd00003500* + ID_MODEL_FROM_DATABASE=PRO-3500 + +pci:v000012FBd00004D4F* + ID_MODEL_FROM_DATABASE=Modena + +pci:v000012FBd00008120* + ID_MODEL_FROM_DATABASE=ePMC-8120 + +pci:v000012FBd0000DA62* + ID_MODEL_FROM_DATABASE=Daytona C6201 PCI (Hurricane) + +pci:v000012FBd0000DB62* + ID_MODEL_FROM_DATABASE=Ingliston XBIF + +pci:v000012FBd0000DC62* + ID_MODEL_FROM_DATABASE=Ingliston PLX9054 + +pci:v000012FBd0000DD62* + ID_MODEL_FROM_DATABASE=Ingliston JTAG/ISP + +pci:v000012FBd0000EDDC* + ID_MODEL_FROM_DATABASE=ePMC-MSDDC + +pci:v000012FBd0000FA01* + ID_MODEL_FROM_DATABASE=ePMC-FPGA + +pci:v000012FC* + ID_VENDOR_FROM_DATABASE=Capital Equipment Corp + +pci:v000012FD* + ID_VENDOR_FROM_DATABASE=I2S + +pci:v000012FE* + ID_VENDOR_FROM_DATABASE=ESD Electronic System Design GmbH + +pci:v000012FF* + ID_VENDOR_FROM_DATABASE=Lexicon + +pci:v00001300* + ID_VENDOR_FROM_DATABASE=Harman International Industries Inc + +pci:v00001302* + ID_VENDOR_FROM_DATABASE=Computer Sciences Corp + +pci:v00001303* + ID_VENDOR_FROM_DATABASE=Innovative Integration + +pci:v00001303d00000030* + ID_MODEL_FROM_DATABASE=X3-SDF 4-channel XMC acquisition board + +pci:v00001304* + ID_VENDOR_FROM_DATABASE=Juniper Networks + +pci:v00001305* + ID_VENDOR_FROM_DATABASE=Netphone, Inc + +pci:v00001306* + ID_VENDOR_FROM_DATABASE=Duet Technologies + +pci:v00001307* + ID_VENDOR_FROM_DATABASE=Measurement Computing + +pci:v00001307d00000001* + ID_MODEL_FROM_DATABASE=PCI-DAS1602/16 + +pci:v00001307d0000000B* + ID_MODEL_FROM_DATABASE=PCI-DIO48H + +pci:v00001307d0000000C* + ID_MODEL_FROM_DATABASE=PCI-PDISO8 + +pci:v00001307d0000000D* + ID_MODEL_FROM_DATABASE=PCI-PDISO16 + +pci:v00001307d0000000F* + ID_MODEL_FROM_DATABASE=PCI-DAS1200 + +pci:v00001307d00000010* + ID_MODEL_FROM_DATABASE=PCI-DAS1602/12 + +pci:v00001307d00000014* + ID_MODEL_FROM_DATABASE=PCI-DIO24H + +pci:v00001307d00000015* + ID_MODEL_FROM_DATABASE=PCI-DIO24H/CTR3 + +pci:v00001307d00000016* + ID_MODEL_FROM_DATABASE=PCI-DIO48H/CTR15 + +pci:v00001307d00000017* + ID_MODEL_FROM_DATABASE=PCI-DIO96H + +pci:v00001307d00000018* + ID_MODEL_FROM_DATABASE=PCI-CTR05 + +pci:v00001307d00000019* + ID_MODEL_FROM_DATABASE=PCI-DAS1200/JR + +pci:v00001307d0000001A* + ID_MODEL_FROM_DATABASE=PCI-DAS1001 + +pci:v00001307d0000001B* + ID_MODEL_FROM_DATABASE=PCI-DAS1002 + +pci:v00001307d0000001C* + ID_MODEL_FROM_DATABASE=PCI-DAS1602JR/16 + +pci:v00001307d0000001D* + ID_MODEL_FROM_DATABASE=PCI-DAS6402/16 + +pci:v00001307d0000001E* + ID_MODEL_FROM_DATABASE=PCI-DAS6402/12 + +pci:v00001307d0000001F* + ID_MODEL_FROM_DATABASE=PCI-DAS16/M1 + +pci:v00001307d00000020* + ID_MODEL_FROM_DATABASE=PCI-DDA02/12 + +pci:v00001307d00000021* + ID_MODEL_FROM_DATABASE=PCI-DDA04/12 + +pci:v00001307d00000022* + ID_MODEL_FROM_DATABASE=PCI-DDA08/12 + +pci:v00001307d00000023* + ID_MODEL_FROM_DATABASE=PCI-DDA02/16 + +pci:v00001307d00000024* + ID_MODEL_FROM_DATABASE=PCI-DDA04/16 + +pci:v00001307d00000025* + ID_MODEL_FROM_DATABASE=PCI-DDA08/16 + +pci:v00001307d00000026* + ID_MODEL_FROM_DATABASE=PCI-DAC04/12-HS + +pci:v00001307d00000027* + ID_MODEL_FROM_DATABASE=PCI-DAC04/16-HS + +pci:v00001307d00000028* + ID_MODEL_FROM_DATABASE=PCI-DIO24 + +pci:v00001307d00000029* + ID_MODEL_FROM_DATABASE=PCI-DAS08 + +pci:v00001307d0000002C* + ID_MODEL_FROM_DATABASE=PCI-INT32 + +pci:v00001307d00000033* + ID_MODEL_FROM_DATABASE=PCI-DUAL-AC5 + +pci:v00001307d00000034* + ID_MODEL_FROM_DATABASE=PCI-DAS-TC + +pci:v00001307d00000035* + ID_MODEL_FROM_DATABASE=PCI-DAS64/M1/16 + +pci:v00001307d00000036* + ID_MODEL_FROM_DATABASE=PCI-DAS64/M2/16 + +pci:v00001307d00000037* + ID_MODEL_FROM_DATABASE=PCI-DAS64/M3/16 + +pci:v00001307d0000004C* + ID_MODEL_FROM_DATABASE=PCI-DAS1000 + +pci:v00001307d0000004D* + ID_MODEL_FROM_DATABASE=PCI-QUAD04 + +pci:v00001307d00000052* + ID_MODEL_FROM_DATABASE=PCI-DAS4020/12 + +pci:v00001307d00000053* + ID_MODEL_FROM_DATABASE=PCIM-DDA06/16 + +pci:v00001307d00000054* + ID_MODEL_FROM_DATABASE=PCI-DIO96 + +pci:v00001307d0000005D* + ID_MODEL_FROM_DATABASE=PCI-DAS6023 + +pci:v00001307d0000005E* + ID_MODEL_FROM_DATABASE=PCI-DAS6025 + +pci:v00001307d0000005F* + ID_MODEL_FROM_DATABASE=PCI-DAS6030 + +pci:v00001307d00000060* + ID_MODEL_FROM_DATABASE=PCI-DAS6031 + +pci:v00001307d00000061* + ID_MODEL_FROM_DATABASE=PCI-DAS6032 + +pci:v00001307d00000062* + ID_MODEL_FROM_DATABASE=PCI-DAS6033 + +pci:v00001307d00000063* + ID_MODEL_FROM_DATABASE=PCI-DAS6034 + +pci:v00001307d00000064* + ID_MODEL_FROM_DATABASE=PCI-DAS6035 + +pci:v00001307d00000065* + ID_MODEL_FROM_DATABASE=PCI-DAS6040 + +pci:v00001307d00000066* + ID_MODEL_FROM_DATABASE=PCI-DAS6052 + +pci:v00001307d00000067* + ID_MODEL_FROM_DATABASE=PCI-DAS6070 + +pci:v00001307d00000068* + ID_MODEL_FROM_DATABASE=PCI-DAS6071 + +pci:v00001307d0000006F* + ID_MODEL_FROM_DATABASE=PCI-DAS6036 + +pci:v00001307d00000070* + ID_MODEL_FROM_DATABASE=PCI-DAC6702 + +pci:v00001307d00000078* + ID_MODEL_FROM_DATABASE=PCI-DAS6013 + +pci:v00001307d00000079* + ID_MODEL_FROM_DATABASE=PCI-DAS6014 + +pci:v00001307d00000115* + ID_MODEL_FROM_DATABASE=PCIe-DAS1602/16 + +pci:v00001308* + ID_VENDOR_FROM_DATABASE=Jato Technologies Inc. + +pci:v00001308d00000001* + ID_MODEL_FROM_DATABASE=NetCelerator Adapter + +pci:v00001308d00000001sv00001308sd00000001* + ID_MODEL_FROM_DATABASE=NetCelerator Adapter + +pci:v00001309* + ID_VENDOR_FROM_DATABASE=AB Semiconductor Ltd + +pci:v0000130A* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Microcomputer + +pci:v0000130B* + ID_VENDOR_FROM_DATABASE=Colorgraphic Communications Corp + +pci:v0000130C* + ID_VENDOR_FROM_DATABASE=Ambex Technologies, Inc + +pci:v0000130D* + ID_VENDOR_FROM_DATABASE=Accelerix Inc + +pci:v0000130E* + ID_VENDOR_FROM_DATABASE=Yamatake-Honeywell Co. Ltd + +pci:v0000130F* + ID_VENDOR_FROM_DATABASE=Advanet Inc + +pci:v00001310* + ID_VENDOR_FROM_DATABASE=Gespac + +pci:v00001311* + ID_VENDOR_FROM_DATABASE=Videoserver, Inc + +pci:v00001312* + ID_VENDOR_FROM_DATABASE=Acuity Imaging, Inc + +pci:v00001313* + ID_VENDOR_FROM_DATABASE=Yaskawa Electric Co. + +pci:v00001315* + ID_VENDOR_FROM_DATABASE=Wavesat + +pci:v00001316* + ID_VENDOR_FROM_DATABASE=Teradyne Inc + +pci:v00001317* + ID_VENDOR_FROM_DATABASE=ADMtek + +pci:v00001317d00000981* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001317d00000985* + ID_MODEL_FROM_DATABASE=NC100 Network Everywhere Fast Ethernet 10/100 + +pci:v00001317d00000985sv00001734sd0000100C* + ID_MODEL_FROM_DATABASE=NC100 Network Everywhere Fast Ethernet 10/100 (Scenic N300 ADMtek AN983 10/100 Mbps PCI Adapter) + +pci:v00001317d00001985* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001317d00001985sv00001385sd0000511A* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet (FA511) + +pci:v00001317d00001985sv00001395sd00002103* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet (CB100-EZ (4-LED version)) + +pci:v00001317d00002850* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v00001317d00005120* + ID_MODEL_FROM_DATABASE=ADM5120 OpenGate System-on-Chip + +pci:v00001317d00008201* + ID_MODEL_FROM_DATABASE=ADM8211 802.11b Wireless Interface + +pci:v00001317d00008201sv000010B8sd00002635* + ID_MODEL_FROM_DATABASE=ADM8211 802.11b Wireless Interface (SMC2635W v1 802.11b Wireless Cardbus Adapter) + +pci:v00001317d00008201sv00001317sd00008201* + ID_MODEL_FROM_DATABASE=ADM8211 802.11b Wireless Interface (SMC2635W v2 802.11b Wireless Cardbus Adapter) + +pci:v00001317d00008211* + ID_MODEL_FROM_DATABASE=ADM8211 802.11b Wireless Interface + +pci:v00001317d00009511* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001318* + ID_VENDOR_FROM_DATABASE=Packet Engines Inc. + +pci:v00001318d00000911* + ID_MODEL_FROM_DATABASE=GNIC-II PCI Gigabit Ethernet [Hamachi] + +pci:v00001319* + ID_VENDOR_FROM_DATABASE=Fortemedia, Inc + +pci:v00001319d00000801* + ID_MODEL_FROM_DATABASE=Xwave QS3000A [FM801] + +pci:v00001319d00000801sv00001319sd00001319* + ID_MODEL_FROM_DATABASE=Xwave QS3000A [FM801] (FM801 PCI Audio) + +pci:v00001319d00000802* + ID_MODEL_FROM_DATABASE=Xwave QS3000A [FM801 game port] + +pci:v00001319d00000802sv00001319sd00001319* + ID_MODEL_FROM_DATABASE=Xwave QS3000A [FM801 game port] (FM801 PCI Joystick) + +pci:v00001319d00001000* + ID_MODEL_FROM_DATABASE=FM801 PCI Audio + +pci:v00001319d00001001* + ID_MODEL_FROM_DATABASE=FM801 PCI Joystick + +pci:v0000131A* + ID_VENDOR_FROM_DATABASE=Finisar Corp. + +pci:v0000131C* + ID_VENDOR_FROM_DATABASE=Nippon Electro-Sensory Devices Corp + +pci:v0000131D* + ID_VENDOR_FROM_DATABASE=Sysmic, Inc. + +pci:v0000131E* + ID_VENDOR_FROM_DATABASE=Xinex Networks Inc + +pci:v0000131F* + ID_VENDOR_FROM_DATABASE=Siig Inc + +pci:v0000131Fd00001000* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16550 + +pci:v0000131Fd00001001* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16650 + +pci:v0000131Fd00001002* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16850 + +pci:v0000131Fd00001010* + ID_MODEL_FROM_DATABASE=Duet 1S(16550)+1P + +pci:v0000131Fd00001011* + ID_MODEL_FROM_DATABASE=Duet 1S(16650)+1P + +pci:v0000131Fd00001012* + ID_MODEL_FROM_DATABASE=Duet 1S(16850)+1P + +pci:v0000131Fd00001020* + ID_MODEL_FROM_DATABASE=CyberParallel (1-port) + +pci:v0000131Fd00001021* + ID_MODEL_FROM_DATABASE=CyberParallel (2-port) + +pci:v0000131Fd00001030* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16550 + +pci:v0000131Fd00001031* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16650 + +pci:v0000131Fd00001032* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16850 + +pci:v0000131Fd00001034* + ID_MODEL_FROM_DATABASE=Trio 2S(16550)+1P + +pci:v0000131Fd00001035* + ID_MODEL_FROM_DATABASE=Trio 2S(16650)+1P + +pci:v0000131Fd00001036* + ID_MODEL_FROM_DATABASE=Trio 2S(16850)+1P + +pci:v0000131Fd00001050* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16550 + +pci:v0000131Fd00001051* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16650 + +pci:v0000131Fd00001052* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16850 + +pci:v0000131Fd00002000* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16550 + +pci:v0000131Fd00002001* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16650 + +pci:v0000131Fd00002002* + ID_MODEL_FROM_DATABASE=CyberSerial (1-port) 16850 + +pci:v0000131Fd00002010* + ID_MODEL_FROM_DATABASE=Duet 1S(16550)+1P + +pci:v0000131Fd00002011* + ID_MODEL_FROM_DATABASE=Duet 1S(16650)+1P + +pci:v0000131Fd00002012* + ID_MODEL_FROM_DATABASE=Duet 1S(16850)+1P + +pci:v0000131Fd00002020* + ID_MODEL_FROM_DATABASE=CyberParallel (1-port) + +pci:v0000131Fd00002021* + ID_MODEL_FROM_DATABASE=CyberParallel (2-port) + +pci:v0000131Fd00002030* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16550 + +pci:v0000131Fd00002030sv0000131Fsd00002030* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16550 (PCI Serial Card) + +pci:v0000131Fd00002031* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16650 + +pci:v0000131Fd00002032* + ID_MODEL_FROM_DATABASE=CyberSerial (2-port) 16850 + +pci:v0000131Fd00002040* + ID_MODEL_FROM_DATABASE=Trio 1S(16550)+2P + +pci:v0000131Fd00002041* + ID_MODEL_FROM_DATABASE=Trio 1S(16650)+2P + +pci:v0000131Fd00002042* + ID_MODEL_FROM_DATABASE=Trio 1S(16850)+2P + +pci:v0000131Fd00002050* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16550 + +pci:v0000131Fd00002051* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16650 + +pci:v0000131Fd00002052* + ID_MODEL_FROM_DATABASE=CyberSerial (4-port) 16850 + +pci:v0000131Fd00002060* + ID_MODEL_FROM_DATABASE=Trio 2S(16550)+1P + +pci:v0000131Fd00002061* + ID_MODEL_FROM_DATABASE=Trio 2S(16650)+1P + +pci:v0000131Fd00002062* + ID_MODEL_FROM_DATABASE=Trio 2S(16850)+1P + +pci:v0000131Fd00002081* + ID_MODEL_FROM_DATABASE=CyberSerial (8-port) ST16654 + +pci:v00001320* + ID_VENDOR_FROM_DATABASE=Crypto AG + +pci:v00001321* + ID_VENDOR_FROM_DATABASE=Arcobel Graphics BV + +pci:v00001322* + ID_VENDOR_FROM_DATABASE=MTT Co., Ltd + +pci:v00001323* + ID_VENDOR_FROM_DATABASE=Dome Inc + +pci:v00001324* + ID_VENDOR_FROM_DATABASE=Sphere Communications + +pci:v00001325* + ID_VENDOR_FROM_DATABASE=Salix Technologies, Inc + +pci:v00001326* + ID_VENDOR_FROM_DATABASE=Seachange international + +pci:v00001327* + ID_VENDOR_FROM_DATABASE=Voss scientific + +pci:v00001328* + ID_VENDOR_FROM_DATABASE=quadrant international + +pci:v00001329* + ID_VENDOR_FROM_DATABASE=Productivity Enhancement + +pci:v0000132A* + ID_VENDOR_FROM_DATABASE=Microcom Inc. + +pci:v0000132B* + ID_VENDOR_FROM_DATABASE=Broadband Technologies + +pci:v0000132C* + ID_VENDOR_FROM_DATABASE=Micrel Inc + +pci:v0000132D* + ID_VENDOR_FROM_DATABASE=Integrated Silicon Solution, Inc. + +pci:v00001330* + ID_VENDOR_FROM_DATABASE=MMC Networks + +pci:v00001331* + ID_VENDOR_FROM_DATABASE=RadiSys Corporation + +pci:v00001331d00000030* + ID_MODEL_FROM_DATABASE=ENP-2611 + +pci:v00001331d00008200* + ID_MODEL_FROM_DATABASE=82600 Host Bridge + +pci:v00001331d00008201* + ID_MODEL_FROM_DATABASE=82600 IDE + +pci:v00001331d00008202* + ID_MODEL_FROM_DATABASE=82600 USB + +pci:v00001331d00008210* + ID_MODEL_FROM_DATABASE=82600 PCI Bridge + +pci:v00001332* + ID_VENDOR_FROM_DATABASE=Micro Memory + +pci:v00001332d00005415* + ID_MODEL_FROM_DATABASE=MM-5415CN PCI Memory Module with Battery Backup + +pci:v00001332d00005425* + ID_MODEL_FROM_DATABASE=MM-5425CN PCI 64/66 Memory Module with Battery Backup + +pci:v00001332d00006140* + ID_MODEL_FROM_DATABASE=MM-6140D + +pci:v00001334* + ID_VENDOR_FROM_DATABASE=Redcreek Communications, Inc + +pci:v00001335* + ID_VENDOR_FROM_DATABASE=Videomail, Inc + +pci:v00001337* + ID_VENDOR_FROM_DATABASE=Third Planet Publishing + +pci:v00001338* + ID_VENDOR_FROM_DATABASE=BT Electronics + +pci:v0000133A* + ID_VENDOR_FROM_DATABASE=Vtel Corp + +pci:v0000133B* + ID_VENDOR_FROM_DATABASE=Softcom Microsystems + +pci:v0000133C* + ID_VENDOR_FROM_DATABASE=Holontech Corp + +pci:v0000133D* + ID_VENDOR_FROM_DATABASE=SS Technologies + +pci:v0000133E* + ID_VENDOR_FROM_DATABASE=Virtual Computer Corp + +pci:v0000133F* + ID_VENDOR_FROM_DATABASE=SCM Microsystems + +pci:v00001340* + ID_VENDOR_FROM_DATABASE=Atalla Corp + +pci:v00001341* + ID_VENDOR_FROM_DATABASE=Kyoto Microcomputer Co + +pci:v00001342* + ID_VENDOR_FROM_DATABASE=Promax Systems Inc + +pci:v00001343* + ID_VENDOR_FROM_DATABASE=Phylon Communications Inc + +pci:v00001344* + ID_VENDOR_FROM_DATABASE=Micron Technology Inc + +pci:v00001344d00005150* + ID_MODEL_FROM_DATABASE=RealSSD P320h + +pci:v00001344d00005151* + ID_MODEL_FROM_DATABASE=RealSSD P320m + +pci:v00001344d00005152* + ID_MODEL_FROM_DATABASE=RealSSD P320s + +pci:v00001344d00005153* + ID_MODEL_FROM_DATABASE=RealSSD P325m + +pci:v00001344d00005160* + ID_MODEL_FROM_DATABASE=RealSSD P420h + +pci:v00001344d00005161* + ID_MODEL_FROM_DATABASE=RealSSD P420m + +pci:v00001344d00005163* + ID_MODEL_FROM_DATABASE=RealSSD P425m + +pci:v00001345* + ID_VENDOR_FROM_DATABASE=Arescom Inc + +pci:v00001347* + ID_VENDOR_FROM_DATABASE=Odetics + +pci:v00001349* + ID_VENDOR_FROM_DATABASE=Sumitomo Electric Industries, Ltd. + +pci:v0000134A* + ID_VENDOR_FROM_DATABASE=DTC Technology Corp. + +pci:v0000134Ad00000001* + ID_MODEL_FROM_DATABASE=Domex 536 + +pci:v0000134Ad00000002* + ID_MODEL_FROM_DATABASE=Domex DMX3194UP SCSI Adapter + +pci:v0000134B* + ID_VENDOR_FROM_DATABASE=ARK Research Corp. + +pci:v0000134C* + ID_VENDOR_FROM_DATABASE=Chori Joho System Co. Ltd + +pci:v0000134D* + ID_VENDOR_FROM_DATABASE=PCTel Inc + +pci:v0000134Dd00002189* + ID_MODEL_FROM_DATABASE=HSP56 MicroModem + +pci:v0000134Dd00002486* + ID_MODEL_FROM_DATABASE=2304WT V.92 MDC Modem + +pci:v0000134Dd00007890* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007890sv0000134Dsd00000001* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 (PCT789 adapter) + +pci:v0000134Dd00007891* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007891sv0000134Dsd00000001* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007892* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007893* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007894* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007895* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007896* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134Dd00007897* + ID_MODEL_FROM_DATABASE=HSP MicroModem 56 + +pci:v0000134E* + ID_VENDOR_FROM_DATABASE=CSTI + +pci:v0000134F* + ID_VENDOR_FROM_DATABASE=Algo System Co Ltd + +pci:v00001350* + ID_VENDOR_FROM_DATABASE=Systec Co. Ltd + +pci:v00001351* + ID_VENDOR_FROM_DATABASE=Sonix Inc + +pci:v00001353* + ID_VENDOR_FROM_DATABASE=Vierling Communication SAS + +pci:v00001353d00000002* + ID_MODEL_FROM_DATABASE=Proserver + +pci:v00001353d00000003* + ID_MODEL_FROM_DATABASE=PCI-FUT + +pci:v00001353d00000004* + ID_MODEL_FROM_DATABASE=PCI-S0 + +pci:v00001353d00000005* + ID_MODEL_FROM_DATABASE=PCI-FUT-S0 + +pci:v00001354* + ID_VENDOR_FROM_DATABASE=Dwave System Inc + +pci:v00001355* + ID_VENDOR_FROM_DATABASE=Kratos Analytical Ltd + +pci:v00001356* + ID_VENDOR_FROM_DATABASE=The Logical Co + +pci:v00001359* + ID_VENDOR_FROM_DATABASE=Prisa Networks + +pci:v0000135A* + ID_VENDOR_FROM_DATABASE=Brain Boxes + +pci:v0000135Ad00000A61* + ID_MODEL_FROM_DATABASE=UC-324 [VELOCITY RS422/485] + +pci:v0000135B* + ID_VENDOR_FROM_DATABASE=Giganet Inc + +pci:v0000135C* + ID_VENDOR_FROM_DATABASE=Quatech Inc + +pci:v0000135Cd00000010* + ID_MODEL_FROM_DATABASE=QSC-100 + +pci:v0000135Cd00000020* + ID_MODEL_FROM_DATABASE=DSC-100 + +pci:v0000135Cd00000030* + ID_MODEL_FROM_DATABASE=DSC-200/300 + +pci:v0000135Cd00000040* + ID_MODEL_FROM_DATABASE=QSC-200/300 + +pci:v0000135Cd00000050* + ID_MODEL_FROM_DATABASE=ESC-100D + +pci:v0000135Cd00000060* + ID_MODEL_FROM_DATABASE=ESC-100M + +pci:v0000135Cd000000F0* + ID_MODEL_FROM_DATABASE=MPAC-100 Syncronous Serial Card (Zilog 85230) + +pci:v0000135Cd00000170* + ID_MODEL_FROM_DATABASE=QSCLP-100 + +pci:v0000135Cd00000180* + ID_MODEL_FROM_DATABASE=DSCLP-100 + +pci:v0000135Cd00000190* + ID_MODEL_FROM_DATABASE=SSCLP-100 + +pci:v0000135Cd000001A0* + ID_MODEL_FROM_DATABASE=QSCLP-200/300 + +pci:v0000135Cd000001B0* + ID_MODEL_FROM_DATABASE=DSCLP-200/300 + +pci:v0000135Cd000001C0* + ID_MODEL_FROM_DATABASE=SSCLP-200/300 + +pci:v0000135Cd00000258* + ID_MODEL_FROM_DATABASE=DSPSX-200/300 + +pci:v0000135D* + ID_VENDOR_FROM_DATABASE=ABB Network Partner AB + +pci:v0000135E* + ID_VENDOR_FROM_DATABASE=Sealevel Systems Inc + +pci:v0000135Ed00005101* + ID_MODEL_FROM_DATABASE=Route 56.PCI - Multi-Protocol Serial Interface (Zilog Z16C32) + +pci:v0000135Ed00007101* + ID_MODEL_FROM_DATABASE=Single Port RS-232/422/485/530 + +pci:v0000135Ed00007201* + ID_MODEL_FROM_DATABASE=Dual Port RS-232/422/485 Interface + +pci:v0000135Ed00007202* + ID_MODEL_FROM_DATABASE=Dual Port RS-232 Interface + +pci:v0000135Ed00007401* + ID_MODEL_FROM_DATABASE=Four Port RS-232 Interface + +pci:v0000135Ed00007402* + ID_MODEL_FROM_DATABASE=Four Port RS-422/485 Interface + +pci:v0000135Ed00007801* + ID_MODEL_FROM_DATABASE=Eight Port RS-232 Interface + +pci:v0000135Ed00007804* + ID_MODEL_FROM_DATABASE=Eight Port RS-232/422/485 Interface + +pci:v0000135Ed00008001* + ID_MODEL_FROM_DATABASE=8001 Digital I/O Adapter + +pci:v0000135F* + ID_VENDOR_FROM_DATABASE=I-Data International A-S + +pci:v00001360* + ID_VENDOR_FROM_DATABASE=Meinberg Funkuhren + +pci:v00001360d00000101* + ID_MODEL_FROM_DATABASE=PCI32 DCF77 Radio Clock + +pci:v00001360d00000102* + ID_MODEL_FROM_DATABASE=PCI509 DCF77 Radio Clock + +pci:v00001360d00000103* + ID_MODEL_FROM_DATABASE=PCI510 DCF77 Radio Clock + +pci:v00001360d00000104* + ID_MODEL_FROM_DATABASE=PCI511 DCF77 Radio Clock + +pci:v00001360d00000105* + ID_MODEL_FROM_DATABASE=PEX511 DCF77 Radio Clock (PCI Express) + +pci:v00001360d00000106* + ID_MODEL_FROM_DATABASE=PZF180PEX High Precision DCF77 Radio Clock (PCI Express) + +pci:v00001360d00000201* + ID_MODEL_FROM_DATABASE=GPS167PCI GPS Receiver + +pci:v00001360d00000202* + ID_MODEL_FROM_DATABASE=GPS168PCI GPS Receiver + +pci:v00001360d00000203* + ID_MODEL_FROM_DATABASE=GPS169PCI GPS Receiver + +pci:v00001360d00000204* + ID_MODEL_FROM_DATABASE=GPS170PCI GPS Receiver + +pci:v00001360d00000205* + ID_MODEL_FROM_DATABASE=GPS170PEX GPS Receiver (PCI Express) + +pci:v00001360d00000206* + ID_MODEL_FROM_DATABASE=GPS180PEX GPS Receiver (PCI Express) + +pci:v00001360d00000207* + ID_MODEL_FROM_DATABASE=GLN180PEX GPS/GLONASS receiver (PCI Express) + +pci:v00001360d00000208* + ID_MODEL_FROM_DATABASE=GPS180AMC GPS Receiver (PCI Express / MicroTCA / AdvancedMC) + +pci:v00001360d00000301* + ID_MODEL_FROM_DATABASE=TCR510PCI IRIG Timecode Reader + +pci:v00001360d00000302* + ID_MODEL_FROM_DATABASE=TCR167PCI IRIG Timecode Reader + +pci:v00001360d00000303* + ID_MODEL_FROM_DATABASE=TCR511PCI IRIG Timecode Reader + +pci:v00001360d00000304* + ID_MODEL_FROM_DATABASE=TCR511PEX IRIG Timecode Reader (PCI Express) + +pci:v00001360d00000305* + ID_MODEL_FROM_DATABASE=TCR170PEX IRIG Timecode Reader (PCI Express) + +pci:v00001360d00000306* + ID_MODEL_FROM_DATABASE=TCR180PEX IRIG Timecode Reader (PCI Express) + +pci:v00001360d00000501* + ID_MODEL_FROM_DATABASE=PTP270PEX PTP/IEEE1588 slave card (PCI Express) + +pci:v00001360d00000601* + ID_MODEL_FROM_DATABASE=FRC511PEX Free Running Clock (PCI Express) + +pci:v00001361* + ID_VENDOR_FROM_DATABASE=Soliton Systems K.K. + +pci:v00001362* + ID_VENDOR_FROM_DATABASE=Fujifacom Corporation + +pci:v00001363* + ID_VENDOR_FROM_DATABASE=Phoenix Technology Ltd + +pci:v00001364* + ID_VENDOR_FROM_DATABASE=ATM Communications Inc + +pci:v00001365* + ID_VENDOR_FROM_DATABASE=Hypercope GmbH + +pci:v00001366* + ID_VENDOR_FROM_DATABASE=Teijin Seiki Co. Ltd + +pci:v00001367* + ID_VENDOR_FROM_DATABASE=Hitachi Zosen Corporation + +pci:v00001368* + ID_VENDOR_FROM_DATABASE=Skyware Corporation + +pci:v00001369* + ID_VENDOR_FROM_DATABASE=Digigram + +pci:v0000136A* + ID_VENDOR_FROM_DATABASE=High Soft Tech + +pci:v0000136Ad00000004* + ID_MODEL_FROM_DATABASE=HST Saphir VII mini PCI + +pci:v0000136Ad00000007* + ID_MODEL_FROM_DATABASE=HST Saphir III E MultiLink 4 + +pci:v0000136Ad00000008* + ID_MODEL_FROM_DATABASE=HST Saphir III E MultiLink 8 + +pci:v0000136Ad0000000A* + ID_MODEL_FROM_DATABASE=HST Saphir III E MultiLink 2 + +pci:v0000136B* + ID_VENDOR_FROM_DATABASE=Kawasaki Steel Corporation + +pci:v0000136Bd0000FF01* + ID_MODEL_FROM_DATABASE=KL5A72002 Motion JPEG + +pci:v0000136C* + ID_VENDOR_FROM_DATABASE=Adtek System Science Co Ltd + +pci:v0000136D* + ID_VENDOR_FROM_DATABASE=Gigalabs Inc + +pci:v0000136F* + ID_VENDOR_FROM_DATABASE=Applied Magic Inc + +pci:v00001370* + ID_VENDOR_FROM_DATABASE=ATL Products + +pci:v00001371* + ID_VENDOR_FROM_DATABASE=CNet Technology Inc + +pci:v00001371d0000434E* + ID_MODEL_FROM_DATABASE=GigaCard Network Adapter + +pci:v00001371d0000434Esv00001371sd0000434E* + ID_MODEL_FROM_DATABASE=GigaCard Network Adapter (N-Way PCI-Bus Giga-Card 1000/100/10Mbps(L)) + +pci:v00001373* + ID_VENDOR_FROM_DATABASE=Silicon Vision Inc + +pci:v00001374* + ID_VENDOR_FROM_DATABASE=Silicom Ltd. + +pci:v00001374d00000024* + ID_MODEL_FROM_DATABASE=Silicom Dual port Giga Ethernet BGE Bypass Server Adapter + +pci:v00001374d00000025* + ID_MODEL_FROM_DATABASE=Silicom Quad port Giga Ethernet BGE Bypass Server Adapter + +pci:v00001374d00000026* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber Giga Ethernet 546 Bypass Server Adapter + +pci:v00001374d00000027* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber LX Giga Ethernet 546 Bypass Server Adapter + +pci:v00001374d00000029* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Giga Ethernet 546GB Bypass Server Adapter + +pci:v00001374d0000002A* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber Giga Ethernet 546 TAP/Bypass Server Adapter + +pci:v00001374d0000002B* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Fast Ethernet 546 TAP/Bypass Server Adapter (PXE2TBI) + +pci:v00001374d0000002C* + ID_MODEL_FROM_DATABASE=Silicom Quad port Copper Giga Ethernet 546GB Bypass Server Adapter (PXG4BPI) + +pci:v00001374d0000002D* + ID_MODEL_FROM_DATABASE=Silicom Quad port Fiber-SX Giga Ethernet 546GB Bypass Server Adapter (PXG4BPFI) + +pci:v00001374d0000002E* + ID_MODEL_FROM_DATABASE=Silicom Quad port Fiber-LX Giga Ethernet 546GB Bypass Server Adapter (PXG4BPFI-LX) + +pci:v00001374d0000002F* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber-SX Giga Ethernet 546GB Low profile Bypass Server Adapter (PXG2BPFIL) + +pci:v00001374d00000030* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber-LX Giga Ethernet 546GB Low profile Bypass Server Adapter + +pci:v00001374d00000031* + ID_MODEL_FROM_DATABASE=Silicom Quad port Copper Giga Ethernet PCI-E Bypass Server Adapter + +pci:v00001374d00000032* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Fast Ethernet 546 TAP/Bypass Server Adapter + +pci:v00001374d00000034* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Giga Ethernet PCI-E BGE Bypass Server Adapter + +pci:v00001374d00000035* + ID_MODEL_FROM_DATABASE=Silicom Quad port Copper Giga Ethernet PCI-E BGE Bypass Server Adapter + +pci:v00001374d00000036* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber Giga Ethernet PCI-E BGE Bypass Server Adapter + +pci:v00001374d00000037* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Ethernet PCI-E Intel based Bypass Server Adapter + +pci:v00001374d00000038* + ID_MODEL_FROM_DATABASE=Silicom Quad port Copper Ethernet PCI-E Intel based Bypass Server Adapter + +pci:v00001374d00000039* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber-SX Ethernet PCI-E Intel based Bypass Server Adapter + +pci:v00001374d0000003A* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber-LX Ethernet PCI-E Intel based Bypass Server Adapter + +pci:v00001374d0000003B* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber Ethernet PMC Intel based Bypass Server Adapter (PMCX2BPFI) + +pci:v00001374d0000003C* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Ethernet PCI-X BGE based Bypass Server Adapter (PXG2BPRB) + +pci:v00001374d0000003D* + ID_MODEL_FROM_DATABASE=2-port Copper GBE Bypass with Caviume 1010 PCI-X + +pci:v00001374d0000003E* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber Giga Ethernet PCI-E 571 TAP/Bypass Server Adapter (PEG2TBFI) + +pci:v00001374d0000003F* + ID_MODEL_FROM_DATABASE=Silicom Dual port Copper Giga Ethernet PCI-X 546 TAP/Bypass Server Adapter (PXG2TBI) + +pci:v00001374d00000040* + ID_MODEL_FROM_DATABASE=Silicom Quad port Fiber-SX Giga Ethernet 571 Bypass Server Adapter (PEG4BPFI) + +pci:v00001374d00000042* + ID_MODEL_FROM_DATABASE=4-port Copper GBE PMC-X Bypass + +pci:v00001374d00000043* + ID_MODEL_FROM_DATABASE=Silicom Quad port Fiber-SX Giga Ethernet 546 Bypass Server Adapter (PXG4BPFID) + +pci:v00001374d00000045* + ID_MODEL_FROM_DATABASE=Silicom 6 port Copper Giga Ethernet 546 Bypass Server Adapter (PXG6BPI) + +pci:v00001374d00000046* + ID_MODEL_FROM_DATABASE=4-port bypass PCI-E w disconnect low profile + +pci:v00001374d00000047* + ID_MODEL_FROM_DATABASE=Silicom Dual port Fiber-SX Giga Ethernet 571 Bypass Disconnect Server Adapter (PEG2BPFID) + +pci:v00001374d0000004A* + ID_MODEL_FROM_DATABASE=Silicom Quad port Fiber-LX Giga Ethernet 571 Bypass Server Adapter (PEG4BPFI-LX) + +pci:v00001374d0000004D* + ID_MODEL_FROM_DATABASE=Dual port Copper Giga Ethernet PCI-E Bypass Server Adapter + +pci:v00001374d00000401* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001374d00000420* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001374d00000460* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet Express Module Bypass Server Adapter + +pci:v00001374d00000461* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001374d00000462* + ID_MODEL_FROM_DATABASE=Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001374d00000470* + ID_MODEL_FROM_DATABASE=Octal-port Copper Gigabit Ethernet Express Module Bypass Server Adapter + +pci:v00001374d00000482* + ID_MODEL_FROM_DATABASE=Dual-port Fiber (SR) 10 Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001374d00000483* + ID_MODEL_FROM_DATABASE=Dual-port Fiber (LR) 10 Gigabit Ethernet ExpressModule Bypass Server Adapter + +pci:v00001375* + ID_VENDOR_FROM_DATABASE=Argosystems Inc + +pci:v00001376* + ID_VENDOR_FROM_DATABASE=LMC + +pci:v00001377* + ID_VENDOR_FROM_DATABASE=Electronic Equipment Production & Distribution GmbH + +pci:v00001378* + ID_VENDOR_FROM_DATABASE=Telemann Co. Ltd + +pci:v00001379* + ID_VENDOR_FROM_DATABASE=Asahi Kasei Microsystems Co Ltd + +pci:v0000137A* + ID_VENDOR_FROM_DATABASE=Mark of the Unicorn Inc + +pci:v0000137Ad00000001* + ID_MODEL_FROM_DATABASE=PCI-324 Audiowire Interface + +pci:v0000137B* + ID_VENDOR_FROM_DATABASE=PPT Vision + +pci:v0000137C* + ID_VENDOR_FROM_DATABASE=Iwatsu Electric Co Ltd + +pci:v0000137D* + ID_VENDOR_FROM_DATABASE=Dynachip Corporation + +pci:v0000137E* + ID_VENDOR_FROM_DATABASE=Patriot Scientific Corporation + +pci:v0000137F* + ID_VENDOR_FROM_DATABASE=Japan Satellite Systems Inc + +pci:v00001380* + ID_VENDOR_FROM_DATABASE=Sanritz Automation Co Ltd + +pci:v00001381* + ID_VENDOR_FROM_DATABASE=Brains Co. Ltd + +pci:v00001382* + ID_VENDOR_FROM_DATABASE=Marian - Electronic & Software + +pci:v00001382d00000001* + ID_MODEL_FROM_DATABASE=ARC88 audio recording card + +pci:v00001382d00002008* + ID_MODEL_FROM_DATABASE=Prodif 96 Pro sound system + +pci:v00001382d00002048* + ID_MODEL_FROM_DATABASE=Prodif Plus sound system + +pci:v00001382d00002088* + ID_MODEL_FROM_DATABASE=Marc 8 Midi sound system + +pci:v00001382d000020C8* + ID_MODEL_FROM_DATABASE=Marc A sound system + +pci:v00001382d00004008* + ID_MODEL_FROM_DATABASE=Marc 2 sound system + +pci:v00001382d00004010* + ID_MODEL_FROM_DATABASE=Marc 2 Pro sound system + +pci:v00001382d00004048* + ID_MODEL_FROM_DATABASE=Marc 4 MIDI sound system + +pci:v00001382d00004088* + ID_MODEL_FROM_DATABASE=Marc 4 Digi sound system + +pci:v00001382d00004248* + ID_MODEL_FROM_DATABASE=Marc X sound system + +pci:v00001382d00004424* + ID_MODEL_FROM_DATABASE=TRACE D4 Sound System + +pci:v00001383* + ID_VENDOR_FROM_DATABASE=Controlnet Inc + +pci:v00001384* + ID_VENDOR_FROM_DATABASE=Reality Simulation Systems Inc + +pci:v00001385* + ID_VENDOR_FROM_DATABASE=Netgear + +pci:v00001385d0000006B* + ID_MODEL_FROM_DATABASE=WA301 802.11b Wireless PCI Adapter + +pci:v00001385d00004100* + ID_MODEL_FROM_DATABASE=MA301 802.11b Wireless PCI Adapter + +pci:v00001385d00004601* + ID_MODEL_FROM_DATABASE=WAG511 802.11a/b/g Dual Band Wireless PC Card + +pci:v00001385d0000620A* + ID_MODEL_FROM_DATABASE=GA620 Gigabit Ethernet + +pci:v00001385d0000630A* + ID_MODEL_FROM_DATABASE=GA630 Gigabit Ethernet + +pci:v00001386* + ID_VENDOR_FROM_DATABASE=Video Domain Technologies + +pci:v00001387* + ID_VENDOR_FROM_DATABASE=Systran Corp + +pci:v00001388* + ID_VENDOR_FROM_DATABASE=Hitachi Information Technology Co Ltd + +pci:v00001389* + ID_VENDOR_FROM_DATABASE=Applicom International + +pci:v00001389d00000001* + ID_MODEL_FROM_DATABASE=PCI1500PFB [Intelligent fieldbus adaptor] + +pci:v0000138A* + ID_VENDOR_FROM_DATABASE=Fusion Micromedia Corp + +pci:v0000138Ad0000003D* + ID_MODEL_FROM_DATABASE=VFS491 Validity Sensor + +pci:v0000138B* + ID_VENDOR_FROM_DATABASE=Tokimec Inc + +pci:v0000138C* + ID_VENDOR_FROM_DATABASE=Silicon Reality + +pci:v0000138D* + ID_VENDOR_FROM_DATABASE=Future Techno Designs pte Ltd + +pci:v0000138E* + ID_VENDOR_FROM_DATABASE=Basler GmbH + +pci:v0000138F* + ID_VENDOR_FROM_DATABASE=Patapsco Designs Inc + +pci:v00001390* + ID_VENDOR_FROM_DATABASE=Concept Development Inc + +pci:v00001391* + ID_VENDOR_FROM_DATABASE=Development Concepts Inc + +pci:v00001392* + ID_VENDOR_FROM_DATABASE=Medialight Inc + +pci:v00001393* + ID_VENDOR_FROM_DATABASE=Moxa Technologies Co Ltd + +pci:v00001393d00000001* + ID_MODEL_FROM_DATABASE=UC7000 Serial + +pci:v00001393d00001020* + ID_MODEL_FROM_DATABASE=CP102 (2-port RS-232 PCI) + +pci:v00001393d00001021* + ID_MODEL_FROM_DATABASE=CP102UL (2-port RS-232 Universal PCI) + +pci:v00001393d00001022* + ID_MODEL_FROM_DATABASE=CP102U (2-port RS-232 Universal PCI) + +pci:v00001393d00001023* + ID_MODEL_FROM_DATABASE=CP-102UF + +pci:v00001393d00001024* + ID_MODEL_FROM_DATABASE=CP-102E (2-port RS-232 Smart PCI Express Serial Board) + +pci:v00001393d00001025* + ID_MODEL_FROM_DATABASE=CP-102EL (2-port RS-232 Smart PCI Express Serial Board) + +pci:v00001393d00001040* + ID_MODEL_FROM_DATABASE=Smartio C104H/PCI + +pci:v00001393d00001041* + ID_MODEL_FROM_DATABASE=CP104U (4-port RS-232 Universal PCI) + +pci:v00001393d00001042* + ID_MODEL_FROM_DATABASE=CP104JU (4-port RS-232 Universal PCI) + +pci:v00001393d00001043* + ID_MODEL_FROM_DATABASE=CP104EL (4-port RS-232 Smart PCI Express) + +pci:v00001393d00001044* + ID_MODEL_FROM_DATABASE=POS104UL (4-port RS-232 Universal PCI) + +pci:v00001393d00001045* + ID_MODEL_FROM_DATABASE=CP-104EL-A (4-port RS-232 PCI Express Serial Board) + +pci:v00001393d00001080* + ID_MODEL_FROM_DATABASE=CB108 (8-port RS-232 PC/104-plus Module) + +pci:v00001393d00001140* + ID_MODEL_FROM_DATABASE=CT-114 series + +pci:v00001393d00001141* + ID_MODEL_FROM_DATABASE=Industrio CP-114 + +pci:v00001393d00001142* + ID_MODEL_FROM_DATABASE=CB114 (4-port RS-232/422/485 PC/104-plus Module) + +pci:v00001393d00001143* + ID_MODEL_FROM_DATABASE=CP-114UL (4-port RS-232/422/485 Smart Universal PCI Serial Board) + +pci:v00001393d00001144* + ID_MODEL_FROM_DATABASE=CP-114EL (4-port RS-232/422/485 Smart PCI Express Serial Board) + +pci:v00001393d00001180* + ID_MODEL_FROM_DATABASE=CP118U (8-port RS-232/422/485 Smart Universal PCI) + +pci:v00001393d00001181* + ID_MODEL_FROM_DATABASE=CP118EL (8-port RS-232/422/485 Smart PCI Express) + +pci:v00001393d00001182* + ID_MODEL_FROM_DATABASE=CP-118EL-A (8-port RS-232/422/485 PCI Express Serial Board) + +pci:v00001393d00001320* + ID_MODEL_FROM_DATABASE=CP132 (2-port RS-422/485 PCI) + +pci:v00001393d00001321* + ID_MODEL_FROM_DATABASE=CP132U (2-Port RS-422/485 Universal PCI) + +pci:v00001393d00001322* + ID_MODEL_FROM_DATABASE=CP-132EL (2-port RS-422/485 Smart PCI Express Serial Board) + +pci:v00001393d00001340* + ID_MODEL_FROM_DATABASE=CP134U (4-Port RS-422/485 Universal PCI) + +pci:v00001393d00001341* + ID_MODEL_FROM_DATABASE=CB134I (4-port RS-422/485 PC/104-plus Module) + +pci:v00001393d00001380* + ID_MODEL_FROM_DATABASE=CP138U (8-port RS-232/422/485 Smart Universal PCI) + +pci:v00001393d00001680* + ID_MODEL_FROM_DATABASE=Smartio C168H/PCI + +pci:v00001393d00001681* + ID_MODEL_FROM_DATABASE=CP-168U V2 Smart Serial Board (8-port RS-232) + +pci:v00001393d00001682* + ID_MODEL_FROM_DATABASE=CP168EL (8-port RS-232 Smart PCI Express) + +pci:v00001393d00001683* + ID_MODEL_FROM_DATABASE=CP-168EL-A (8-port RS-232 PCI Express Serial Board) + +pci:v00001393d00002040* + ID_MODEL_FROM_DATABASE=Intellio CP-204J + +pci:v00001393d00002180* + ID_MODEL_FROM_DATABASE=Intellio C218 Turbo PCI + +pci:v00001393d00003200* + ID_MODEL_FROM_DATABASE=Intellio C320 Turbo PCI + +pci:v00001394* + ID_VENDOR_FROM_DATABASE=Level One Communications + +pci:v00001394d00000001* + ID_MODEL_FROM_DATABASE=LXT1001 Gigabit Ethernet + +pci:v00001394d00000001sv00001186sd00004800* + ID_MODEL_FROM_DATABASE=LXT1001 Gigabit Ethernet (DGE-500SX) + +pci:v00001394d00000001sv00001394sd00000001* + ID_MODEL_FROM_DATABASE=LXT1001 Gigabit Ethernet (NetCelerator Adapter) + +pci:v00001395* + ID_VENDOR_FROM_DATABASE=Ambicom Inc + +pci:v00001396* + ID_VENDOR_FROM_DATABASE=Cipher Systems Inc + +pci:v00001397* + ID_VENDOR_FROM_DATABASE=Cologne Chip Designs GmbH + +pci:v00001397d000008B4* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] + +pci:v00001397d000008B4sv00001397sd000008B4* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Cologne Chip HFC-4S Eval. Board]) + +pci:v00001397d000008B4sv00001397sd0000B51A* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Allo.com BRI card]) + +pci:v00001397d000008B4sv00001397sd0000B520* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [IOB4ST]) + +pci:v00001397d000008B4sv00001397sd0000B540* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Swyx SX2 QuadBri]) + +pci:v00001397d000008B4sv00001397sd0000B550* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Junghanns.NET quadBRI]) + +pci:v00001397d000008B4sv00001397sd0000B556* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Junghanns.NET duoBRI]) + +pci:v00001397d000008B4sv00001397sd0000B559* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Junghanns.NET duoBRI miniPCI]) + +pci:v00001397d000008B4sv00001397sd0000B560* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN4S0]) + +pci:v00001397d000008B4sv00001397sd0000B566* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN2S0]) + +pci:v00001397d000008B4sv00001397sd0000B567* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN1S0 miniPCI]) + +pci:v00001397d000008B4sv00001397sd0000B568* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN4S0 miniPCI]) + +pci:v00001397d000008B4sv00001397sd0000B569* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN2S0 miniPCI]) + +pci:v00001397d000008B4sv00001397sd0000B620* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S) + +pci:v00001397d000008B4sv00001397sd0000B752* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [Junghanns.NET quadBRI PCIe]) + +pci:v00001397d000008B4sv00001397sd0000B761* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN2S0 PCIe]) + +pci:v00001397d000008B4sv00001397sd0000B762* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [BeroNet BN4S0 PCIe]) + +pci:v00001397d000008B4sv00001397sd0000E884* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [OpenVox B200P]) + +pci:v00001397d000008B4sv00001397sd0000E888* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-4S] (HFC-4S [OpenVox B200P / B400P]) + +pci:v00001397d000016B8* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] + +pci:v00001397d000016B8sv00001397sd000016B8* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [Cologne Chip HFC-8S Eval. Board]) + +pci:v00001397d000016B8sv00001397sd0000B521* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [IOB4ST Recording]) + +pci:v00001397d000016B8sv00001397sd0000B522* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [IOB8ST]) + +pci:v00001397d000016B8sv00001397sd0000B552* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [Junghanns.NET octoBRI]) + +pci:v00001397d000016B8sv00001397sd0000B55B* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [Junghanns.NET octoBRI]) + +pci:v00001397d000016B8sv00001397sd0000B562* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [BeroNet BN8S0]) + +pci:v00001397d000016B8sv00001397sd0000B56B* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [BeroNet BN8S0+]) + +pci:v00001397d000016B8sv00001397sd0000B622* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S) + +pci:v00001397d000016B8sv00001397sd0000E998* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-8S] (HFC-8S [OpenVox B800P]) + +pci:v00001397d00002BD0* + ID_MODEL_FROM_DATABASE=ISDN network controller [HFC-PCI] + +pci:v00001397d00002BD0sv00000675sd00001704* + ID_MODEL_FROM_DATABASE=ISDN network controller [HFC-PCI] (ISDN Adapter (PCI Bus, D, C)) + +pci:v00001397d00002BD0sv00000675sd00001708* + ID_MODEL_FROM_DATABASE=ISDN network controller [HFC-PCI] (ISDN Adapter (PCI Bus, D, C, ACPI)) + +pci:v00001397d00002BD0sv00001397sd00002BD0* + ID_MODEL_FROM_DATABASE=ISDN network controller [HFC-PCI] (ISDN Board) + +pci:v00001397d00002BD0sv0000E4BFsd00001000* + ID_MODEL_FROM_DATABASE=ISDN network controller [HFC-PCI] (CI1-1-Harp) + +pci:v00001397d000030B1* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] + +pci:v00001397d000030B1sv00001397sd000030B1* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Cologne Chip HFC-E1 Eval. Board]) + +pci:v00001397d000030B1sv00001397sd0000B523* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [IOB1E1]) + +pci:v00001397d000030B1sv00001397sd0000B543* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Swyx SX2 SinglePRI V2]) + +pci:v00001397d000030B1sv00001397sd0000B544* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Swyx SX2 DualPRI V2]) + +pci:v00001397d000030B1sv00001397sd0000B553* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Junghanns.NET singleE1]) + +pci:v00001397d000030B1sv00001397sd0000B554* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Junghanns.NET doubleE1]) + +pci:v00001397d000030B1sv00001397sd0000B555* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Junghanns.NET doubleE1 2.0]) + +pci:v00001397d000030B1sv00001397sd0000B55A* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [Junghanns.NET singleE1 miniPCI]) + +pci:v00001397d000030B1sv00001397sd0000B563* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [beroNet BN1E1]) + +pci:v00001397d000030B1sv00001397sd0000B564* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [beroNet BN2E1]) + +pci:v00001397d000030B1sv00001397sd0000B565* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [beroNet BN2E1+]) + +pci:v00001397d000030B1sv00001397sd0000B56A* + ID_MODEL_FROM_DATABASE=ISDN network Controller [HFC-E1] (HFC-E1 [beroNet BN1E1 miniPCI]) + +pci:v00001397d0000B700* + ID_MODEL_FROM_DATABASE=ISDN network controller PrimuX S0 [HFC-PCI] + +pci:v00001397d0000F001* + ID_MODEL_FROM_DATABASE=GSM Network Controller [HFC-4GSM] + +pci:v00001398* + ID_VENDOR_FROM_DATABASE=Clarion co. Ltd + +pci:v00001399* + ID_VENDOR_FROM_DATABASE=Rios systems Co Ltd + +pci:v0000139A* + ID_VENDOR_FROM_DATABASE=Alacritech Inc + +pci:v0000139Ad00000001* + ID_MODEL_FROM_DATABASE=Quad Port 10/100 Server Accelerator + +pci:v0000139Ad00000003* + ID_MODEL_FROM_DATABASE=Single Port 10/100 Server Accelerator + +pci:v0000139Ad00000005* + ID_MODEL_FROM_DATABASE=Single Port Gigabit Server Accelerator + +pci:v0000139B* + ID_VENDOR_FROM_DATABASE=Mediasonic Multimedia Systems Ltd + +pci:v0000139C* + ID_VENDOR_FROM_DATABASE=Quantum 3d Inc + +pci:v0000139D* + ID_VENDOR_FROM_DATABASE=EPL limited + +pci:v0000139E* + ID_VENDOR_FROM_DATABASE=Media4 + +pci:v0000139F* + ID_VENDOR_FROM_DATABASE=Aethra s.r.l. + +pci:v000013A0* + ID_VENDOR_FROM_DATABASE=Crystal Group Inc + +pci:v000013A1* + ID_VENDOR_FROM_DATABASE=Kawasaki Heavy Industries Ltd + +pci:v000013A2* + ID_VENDOR_FROM_DATABASE=Ositech Communications Inc + +pci:v000013A3* + ID_VENDOR_FROM_DATABASE=Hifn Inc. + +pci:v000013A3d00000005* + ID_MODEL_FROM_DATABASE=7751 Security Processor + +pci:v000013A3d00000006* + ID_MODEL_FROM_DATABASE=6500 Public Key Processor + +pci:v000013A3d00000007* + ID_MODEL_FROM_DATABASE=7811 Security Processor + +pci:v000013A3d00000012* + ID_MODEL_FROM_DATABASE=7951 Security Processor + +pci:v000013A3d00000014* + ID_MODEL_FROM_DATABASE=78XX Security Processor + +pci:v000013A3d00000016* + ID_MODEL_FROM_DATABASE=8065 Security Processor + +pci:v000013A3d00000017* + ID_MODEL_FROM_DATABASE=8165 Security Processor + +pci:v000013A3d00000018* + ID_MODEL_FROM_DATABASE=8154 Security Processor + +pci:v000013A3d0000001D* + ID_MODEL_FROM_DATABASE=7956 Security Processor + +pci:v000013A3d0000001F* + ID_MODEL_FROM_DATABASE=7855 Security Processor + +pci:v000013A3d00000020* + ID_MODEL_FROM_DATABASE=7955 Security Processor + +pci:v000013A3d00000026* + ID_MODEL_FROM_DATABASE=8155 Security Processor + +pci:v000013A3d0000002E* + ID_MODEL_FROM_DATABASE=9630 Compression Processor + +pci:v000013A3d0000002F* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor + +pci:v000013A3d0000002Fsv000013A3sd00001600* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1600 Acceleration Card) + +pci:v000013A3d0000002Fsv000013A3sd00001605* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1605 Acceleration Card) + +pci:v000013A3d0000002Fsv000013A3sd00001610* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1610 Acceleration Card) + +pci:v000013A3d0000002Fsv000013A3sd00001615* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1615 Acceleration Card) + +pci:v000013A3d0000002Fsv000013A3sd00001620* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1620 Acceleration Card) + +pci:v000013A3d0000002Fsv000013A3sd00001625* + ID_MODEL_FROM_DATABASE=9725 Compression and Security Processor (DR1625 Acceleration Card) + +pci:v000013A3d00000033* + ID_MODEL_FROM_DATABASE=8201 Acceleration Processor + +pci:v000013A3d00000033sv000013A3sd00000036* + ID_MODEL_FROM_DATABASE=8201 Acceleration Processor (DX1710 Acceleration Card) + +pci:v000013A3d00000034* + ID_MODEL_FROM_DATABASE=8202 Acceleration Processor + +pci:v000013A3d00000034sv000013A3sd00000036* + ID_MODEL_FROM_DATABASE=8202 Acceleration Processor (DX1720 Acceleration Card) + +pci:v000013A3d00000035* + ID_MODEL_FROM_DATABASE=8203 Acceleration Processor + +pci:v000013A3d00000035sv000013A3sd00000036* + ID_MODEL_FROM_DATABASE=8203 Acceleration Processor (DX1730 Acceleration Card) + +pci:v000013A3d00000037* + ID_MODEL_FROM_DATABASE=8204 Acceleration Processor + +pci:v000013A3d00000037sv000013A3sd00000036* + ID_MODEL_FROM_DATABASE=8204 Acceleration Processor (DX1740 Acceleration Card) + +pci:v000013A4* + ID_VENDOR_FROM_DATABASE=Rascom Inc + +pci:v000013A5* + ID_VENDOR_FROM_DATABASE=Audio Digital Imaging Inc + +pci:v000013A6* + ID_VENDOR_FROM_DATABASE=Videonics Inc + +pci:v000013A7* + ID_VENDOR_FROM_DATABASE=Teles AG + +pci:v000013A8* + ID_VENDOR_FROM_DATABASE=Exar Corp. + +pci:v000013A8d00000152* + ID_MODEL_FROM_DATABASE=XR17C/D152 Dual PCI UART + +pci:v000013A8d00000154* + ID_MODEL_FROM_DATABASE=XR17C154 Quad UART + +pci:v000013A8d00000158* + ID_MODEL_FROM_DATABASE=XR17C158 Octal UART + +pci:v000013A8d00000252* + ID_MODEL_FROM_DATABASE=XR17V252 Dual UART PCI controller + +pci:v000013A8d00000254* + ID_MODEL_FROM_DATABASE=XR17V254 Quad UART PCI controller + +pci:v000013A8d00000258* + ID_MODEL_FROM_DATABASE=XR17V258 Octal UART PCI controller + +pci:v000013A9* + ID_VENDOR_FROM_DATABASE=Siemens Medical Systems, Ultrasound Group + +pci:v000013AA* + ID_VENDOR_FROM_DATABASE=Broadband Networks Inc + +pci:v000013AB* + ID_VENDOR_FROM_DATABASE=Arcom Control Systems Ltd + +pci:v000013AC* + ID_VENDOR_FROM_DATABASE=Motion Media Technology Ltd + +pci:v000013AD* + ID_VENDOR_FROM_DATABASE=Nexus Inc + +pci:v000013AE* + ID_VENDOR_FROM_DATABASE=ALD Technology Ltd + +pci:v000013AF* + ID_VENDOR_FROM_DATABASE=T.Sqware + +pci:v000013B0* + ID_VENDOR_FROM_DATABASE=Maxspeed Corp + +pci:v000013B1* + ID_VENDOR_FROM_DATABASE=Tamura corporation + +pci:v000013B2* + ID_VENDOR_FROM_DATABASE=Techno Chips Co. Ltd + +pci:v000013B3* + ID_VENDOR_FROM_DATABASE=Lanart Corporation + +pci:v000013B4* + ID_VENDOR_FROM_DATABASE=Wellbean Co Inc + +pci:v000013B5* + ID_VENDOR_FROM_DATABASE=ARM + +pci:v000013B6* + ID_VENDOR_FROM_DATABASE=Dlog GmbH + +pci:v000013B7* + ID_VENDOR_FROM_DATABASE=Logic Devices Inc + +pci:v000013B8* + ID_VENDOR_FROM_DATABASE=Nokia Telecommunications oy + +pci:v000013B9* + ID_VENDOR_FROM_DATABASE=Elecom Co Ltd + +pci:v000013BA* + ID_VENDOR_FROM_DATABASE=Oxford Instruments + +pci:v000013BB* + ID_VENDOR_FROM_DATABASE=Sanyo Technosound Co Ltd + +pci:v000013BC* + ID_VENDOR_FROM_DATABASE=Bitran Corporation + +pci:v000013BD* + ID_VENDOR_FROM_DATABASE=Sharp corporation + +pci:v000013BE* + ID_VENDOR_FROM_DATABASE=Miroku Jyoho Service Co. Ltd + +pci:v000013BF* + ID_VENDOR_FROM_DATABASE=Sharewave Inc + +pci:v000013C0* + ID_VENDOR_FROM_DATABASE=Microgate Corporation + +pci:v000013C0d00000010* + ID_MODEL_FROM_DATABASE=SyncLink Adapter v1 + +pci:v000013C0d00000020* + ID_MODEL_FROM_DATABASE=SyncLink SCC Adapter + +pci:v000013C0d00000030* + ID_MODEL_FROM_DATABASE=SyncLink Multiport Adapter + +pci:v000013C0d00000070* + ID_MODEL_FROM_DATABASE=SyncLink GT Adapter + +pci:v000013C0d00000080* + ID_MODEL_FROM_DATABASE=SyncLink GT4 Adapter + +pci:v000013C0d000000A0* + ID_MODEL_FROM_DATABASE=SyncLink GT2 Adapter + +pci:v000013C0d00000210* + ID_MODEL_FROM_DATABASE=SyncLink Adapter v2 + +pci:v000013C1* + ID_VENDOR_FROM_DATABASE=3ware Inc + +pci:v000013C1d00001000* + ID_MODEL_FROM_DATABASE=5xxx/6xxx-series PATA-RAID + +pci:v000013C1d00001001* + ID_MODEL_FROM_DATABASE=7xxx/8xxx-series PATA/SATA-RAID + +pci:v000013C1d00001001sv000013C1sd00001001* + ID_MODEL_FROM_DATABASE=7xxx/8xxx-series PATA/SATA-RAID + +pci:v000013C1d00001002* + ID_MODEL_FROM_DATABASE=9xxx-series SATA-RAID + +pci:v000013C1d00001003* + ID_MODEL_FROM_DATABASE=9550SX SATA-II RAID PCI-X + +pci:v000013C1d00001004* + ID_MODEL_FROM_DATABASE=9650SE SATA-II RAID PCIe + +pci:v000013C1d00001005* + ID_MODEL_FROM_DATABASE=9690SA SAS/SATA-II RAID PCIe + +pci:v000013C1d00001010* + ID_MODEL_FROM_DATABASE=9750 SAS2/SATA-II RAID PCIe + +pci:v000013C2* + ID_VENDOR_FROM_DATABASE=Technotrend Systemtechnik GmbH + +pci:v000013C2d0000000E* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge DVB card rev2.3 + +pci:v000013C2d00001019* + ID_MODEL_FROM_DATABASE=TTechnoTrend-budget DVB S2-3200 + +pci:v000013C3* + ID_VENDOR_FROM_DATABASE=Janz Computer AG + +pci:v000013C4* + ID_VENDOR_FROM_DATABASE=Phase Metrics + +pci:v000013C5* + ID_VENDOR_FROM_DATABASE=Alphi Technology Corp + +pci:v000013C6* + ID_VENDOR_FROM_DATABASE=Condor Engineering Inc + +pci:v000013C6d00000520* + ID_MODEL_FROM_DATABASE=CEI-520 A429 Card + +pci:v000013C6d00000620* + ID_MODEL_FROM_DATABASE=CEI-620 A429 Card + +pci:v000013C6d00000820* + ID_MODEL_FROM_DATABASE=CEI-820 A429 Card + +pci:v000013C6d00000830* + ID_MODEL_FROM_DATABASE=CEI-830 A429 Card + +pci:v000013C6d00001004* + ID_MODEL_FROM_DATABASE=P-SER Multi-channel PMC to RS-485/422/232 adapter + +pci:v000013C7* + ID_VENDOR_FROM_DATABASE=Blue Chip Technology Ltd + +pci:v000013C7d00000ADC* + ID_MODEL_FROM_DATABASE=PCI-ADC + +pci:v000013C7d00000B10* + ID_MODEL_FROM_DATABASE=PCI-PIO + +pci:v000013C7d00000D10* + ID_MODEL_FROM_DATABASE=PCI-DIO + +pci:v000013C7d0000524C* + ID_MODEL_FROM_DATABASE=PCI-RLY + +pci:v000013C7d00005744* + ID_MODEL_FROM_DATABASE=PCI-WDT + +pci:v000013C8* + ID_VENDOR_FROM_DATABASE=Apptech Inc + +pci:v000013C9* + ID_VENDOR_FROM_DATABASE=Eaton Corporation + +pci:v000013CA* + ID_VENDOR_FROM_DATABASE=Iomega Corporation + +pci:v000013CB* + ID_VENDOR_FROM_DATABASE=Yano Electric Co Ltd + +pci:v000013CC* + ID_VENDOR_FROM_DATABASE=Metheus Corporation + +pci:v000013CD* + ID_VENDOR_FROM_DATABASE=Compatible Systems Corporation + +pci:v000013CE* + ID_VENDOR_FROM_DATABASE=Cocom A/S + +pci:v000013CF* + ID_VENDOR_FROM_DATABASE=Studio Audio & Video Ltd + +pci:v000013D0* + ID_VENDOR_FROM_DATABASE=Techsan Electronics Co Ltd + +pci:v000013D0d00002103* + ID_MODEL_FROM_DATABASE=B2C2 FlexCopII DVB chip / Technisat SkyStar2 DVB card + +pci:v000013D0d00002104* + ID_MODEL_FROM_DATABASE=B2C2 FlexCopIII DVB chip / Technisat SkyStar2 DVB card (rev 01) + +pci:v000013D0d00002200* + ID_MODEL_FROM_DATABASE=B2C2 FlexCopIII DVB chip / Technisat SkyStar2 DVB card + +pci:v000013D1* + ID_VENDOR_FROM_DATABASE=Abocom Systems Inc + +pci:v000013D1d0000AB02* + ID_MODEL_FROM_DATABASE=ADMtek Centaur-C rev 17 [D-Link DFE-680TX] CardBus Fast Ethernet Adapter + +pci:v000013D1d0000AB03* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v000013D1d0000AB06* + ID_MODEL_FROM_DATABASE=RTL8139 [FE2000VX] CardBus Fast Ethernet Attached Port Adapter + +pci:v000013D1d0000AB08* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v000013D2* + ID_VENDOR_FROM_DATABASE=Shark Multimedia Inc + +pci:v000013D4* + ID_VENDOR_FROM_DATABASE=Graphics Microsystems Inc + +pci:v000013D5* + ID_VENDOR_FROM_DATABASE=Media 100 Inc + +pci:v000013D6* + ID_VENDOR_FROM_DATABASE=K.I. Technology Co Ltd + +pci:v000013D7* + ID_VENDOR_FROM_DATABASE=Toshiba Engineering Corporation + +pci:v000013D8* + ID_VENDOR_FROM_DATABASE=Phobos corporation + +pci:v000013D9* + ID_VENDOR_FROM_DATABASE=Apex PC Solutions Inc + +pci:v000013DA* + ID_VENDOR_FROM_DATABASE=Intresource Systems pte Ltd + +pci:v000013DB* + ID_VENDOR_FROM_DATABASE=Janich & Klass Computertechnik GmbH + +pci:v000013DC* + ID_VENDOR_FROM_DATABASE=Netboost Corporation + +pci:v000013DD* + ID_VENDOR_FROM_DATABASE=Multimedia Bundle Inc + +pci:v000013DE* + ID_VENDOR_FROM_DATABASE=ABB Robotics Products AB + +pci:v000013DF* + ID_VENDOR_FROM_DATABASE=E-Tech Inc + +pci:v000013DFd00000001* + ID_MODEL_FROM_DATABASE=PCI56RVP Modem + +pci:v000013DFd00000001sv000013DFsd00000001* + ID_MODEL_FROM_DATABASE=PCI56RVP Modem + +pci:v000013E0* + ID_VENDOR_FROM_DATABASE=GVC Corporation + +pci:v000013E1* + ID_VENDOR_FROM_DATABASE=Silicom Multimedia Systems Inc + +pci:v000013E2* + ID_VENDOR_FROM_DATABASE=Dynamics Research Corporation + +pci:v000013E3* + ID_VENDOR_FROM_DATABASE=Nest Inc + +pci:v000013E4* + ID_VENDOR_FROM_DATABASE=Calculex Inc + +pci:v000013E5* + ID_VENDOR_FROM_DATABASE=Telesoft Design Ltd + +pci:v000013E6* + ID_VENDOR_FROM_DATABASE=Argosy research Inc + +pci:v000013E7* + ID_VENDOR_FROM_DATABASE=NAC Incorporated + +pci:v000013E8* + ID_VENDOR_FROM_DATABASE=Chip Express Corporation + +pci:v000013E9* + ID_VENDOR_FROM_DATABASE=Intraserver Technology Inc + +pci:v000013EA* + ID_VENDOR_FROM_DATABASE=Dallas Semiconductor + +pci:v000013EB* + ID_VENDOR_FROM_DATABASE=Hauppauge Computer Works Inc + +pci:v000013EC* + ID_VENDOR_FROM_DATABASE=Zydacron Inc + +pci:v000013ECd0000000A* + ID_MODEL_FROM_DATABASE=NPC-RC01 Remote control receiver + +pci:v000013ED* + ID_VENDOR_FROM_DATABASE=Raytheion E-Systems + +pci:v000013EE* + ID_VENDOR_FROM_DATABASE=Hayes Microcomputer Products Inc + +pci:v000013EF* + ID_VENDOR_FROM_DATABASE=Coppercom Inc + +pci:v000013F0* + ID_VENDOR_FROM_DATABASE=Sundance Technology Inc / IC Plus Corp + +pci:v000013F0d00000200* + ID_MODEL_FROM_DATABASE=IC Plus IP100A Integrated 10/100 Ethernet MAC + PHY + +pci:v000013F0d00000200sv00001043sd00008213* + ID_MODEL_FROM_DATABASE=IC Plus IP100A Integrated 10/100 Ethernet MAC + PHY (NX1001) + +pci:v000013F0d00000201* + ID_MODEL_FROM_DATABASE=ST201 Sundance Ethernet + +pci:v000013F0d00001021* + ID_MODEL_FROM_DATABASE=TC902x Gigabit Ethernet + +pci:v000013F0d00001023* + ID_MODEL_FROM_DATABASE=IP1000 Family Gigabit Ethernet + +pci:v000013F0d00001023sv00001043sd00008180* + ID_MODEL_FROM_DATABASE=IP1000 Family Gigabit Ethernet (NX1101) + +pci:v000013F1* + ID_VENDOR_FROM_DATABASE=Oce' - Technologies B.V. + +pci:v000013F2* + ID_VENDOR_FROM_DATABASE=Ford Microelectronics Inc + +pci:v000013F3* + ID_VENDOR_FROM_DATABASE=Mcdata Corporation + +pci:v000013F4* + ID_VENDOR_FROM_DATABASE=Troika Networks, Inc. + +pci:v000013F4d00001401* + ID_MODEL_FROM_DATABASE=Zentai Fibre Channel Adapter + +pci:v000013F5* + ID_VENDOR_FROM_DATABASE=Kansai Electric Co. Ltd + +pci:v000013F6* + ID_VENDOR_FROM_DATABASE=C-Media Electronics Inc + +pci:v000013F6d00000011* + ID_MODEL_FROM_DATABASE=CMI8738 + +pci:v000013F6d00000100* + ID_MODEL_FROM_DATABASE=CM8338A + +pci:v000013F6d00000100sv000013F6sd0000FFFF* + ID_MODEL_FROM_DATABASE=CM8338A (CMI8338/C3DX PCI Audio Device) + +pci:v000013F6d00000101* + ID_MODEL_FROM_DATABASE=CM8338B + +pci:v000013F6d00000101sv000013F6sd00000101* + ID_MODEL_FROM_DATABASE=CM8338B (CMI8338-031 PCI Audio Device) + +pci:v000013F6d00000111* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio + +pci:v000013F6d00000111sv00001019sd00000970* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (P6STP-FL motherboard) + +pci:v000013F6d00000111sv00001043sd00008035* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (CUSI-FX motherboard) + +pci:v000013F6d00000111sv00001043sd00008077* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (CMI8738 6-channel audio controller) + +pci:v000013F6d00000111sv00001043sd000080E2* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (CMI8738 6ch-MX) + +pci:v000013F6d00000111sv000013F6sd00000111* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (CMI8738/C3DX PCI Audio Device) + +pci:v000013F6d00000111sv000013F6sd00009761* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Theatron Agrippa) + +pci:v000013F6d00000111sv0000153Bsd00001144* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Aureon 5.1) + +pci:v000013F6d00000111sv0000153Bsd00001170* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Aureon 7.1) + +pci:v000013F6d00000111sv00001681sd0000A000* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Gamesurround MUSE XL) + +pci:v000013F6d00000111sv000017ABsd00000604* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (PSC604 Dynamic Edge) + +pci:v000013F6d00000111sv000017ABsd00000605* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (PSC605 Sonic Edge) + +pci:v000013F6d00000111sv000017ABsd00007777* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (PSC605 Sonic Edge) + +pci:v000013F6d00000111sv0000270Fsd00001103* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (CT-7NJS Ultra motherboard) + +pci:v000013F6d00000111sv0000270Fsd0000F462* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (7NJL1 motherboard) + +pci:v000013F6d00000111sv0000584Dsd00003731* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Digital X-Mystique) + +pci:v000013F6d00000111sv0000584Dsd00003741* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (X-Plosion 7.1) + +pci:v000013F6d00000111sv0000584Dsd00003751* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (X-Raider 7.1) + +pci:v000013F6d00000111sv0000584Dsd00003761* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (X-Mystique 7.1 LP) + +pci:v000013F6d00000111sv0000584Dsd00003771* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (X-Mystique 7.1 LP Value) + +pci:v000013F6d00000111sv00007284sd00008384* + ID_MODEL_FROM_DATABASE=CMI8738/CMI8768 PCI Audio (Striker 7.1) + +pci:v000013F6d00000211* + ID_MODEL_FROM_DATABASE=CM8738 + +pci:v000013F6d00005011* + ID_MODEL_FROM_DATABASE=CM8888 [Oxygen Express] + +pci:v000013F6d00005011sv000013F6sd00005011* + ID_MODEL_FROM_DATABASE=CM8888 [Oxygen Express] (HDA Controller) + +pci:v000013F6d00008788* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] + +pci:v000013F6d00008788sv00001043sd00008269* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 200 (Xonar D2)) + +pci:v000013F6d00008788sv00001043sd00008275* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar DX)) + +pci:v000013F6d00008788sv00001043sd000082B7* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 200 (Xonar D2X)) + +pci:v000013F6d00008788sv00001043sd00008314* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 200 (Xonar HDAV1.3)) + +pci:v000013F6d00008788sv00001043sd00008327* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar DX)) + +pci:v000013F6d00008788sv00001043sd0000834F* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar D1)) + +pci:v000013F6d00008788sv00001043sd0000835C* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar Essence STX)) + +pci:v000013F6d00008788sv00001043sd0000835D* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar ST)) + +pci:v000013F6d00008788sv00001043sd0000835E* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 200 (Xonar HDAV1.3 Slim)) + +pci:v000013F6d00008788sv00001043sd0000838E* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 66 (Xonar DS)) + +pci:v000013F6d00008788sv00001043sd00008428* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar Xense)) + +pci:v000013F6d00008788sv00001043sd00008467* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (CMI8786 (Xonar DG)) + +pci:v000013F6d00008788sv00001043sd000085F4* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Virtuoso 100 (Xonar Essence STX II)) + +pci:v000013F6d00008788sv000013F6sd00008782* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (PCI 2.0 HD Audio) + +pci:v000013F6d00008788sv000013F6sd0000FFFF* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (CMI8787-HG2PCI) + +pci:v000013F6d00008788sv000014C3sd00001710* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (HiFier Fantasia) + +pci:v000013F6d00008788sv000014C3sd00001711* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (HiFier Serenade) + +pci:v000013F6d00008788sv000014C3sd00001713* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (HiFier Serenade III) + +pci:v000013F6d00008788sv00001A58sd00000910* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (Barracuda AC-1) + +pci:v000013F6d00008788sv0000415Asd00005431* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (X-Meridian 7.1) + +pci:v000013F6d00008788sv00005431sd0000017A* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (X-Meridian 7.1 2G) + +pci:v000013F6d00008788sv0000584Dsd00003781* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (HDA X-Purity 7.1 Platinum) + +pci:v000013F6d00008788sv00007284sd00009761* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (CLARO) + +pci:v000013F6d00008788sv00007284sd00009781* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (CLARO halo) + +pci:v000013F6d00008788sv00007284sd00009783* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (eCLARO) + +pci:v000013F6d00008788sv00007284sd00009787* + ID_MODEL_FROM_DATABASE=CMI8788 [Oxygen HD Audio] (CLARO II) + +pci:v000013F7* + ID_VENDOR_FROM_DATABASE=Wildfire Communications + +pci:v000013F8* + ID_VENDOR_FROM_DATABASE=Ad Lib Multimedia Inc + +pci:v000013F9* + ID_VENDOR_FROM_DATABASE=NTT Advanced Technology Corp. + +pci:v000013FA* + ID_VENDOR_FROM_DATABASE=Pentland Systems Ltd + +pci:v000013FB* + ID_VENDOR_FROM_DATABASE=Aydin Corp + +pci:v000013FC* + ID_VENDOR_FROM_DATABASE=Computer Peripherals International + +pci:v000013FD* + ID_VENDOR_FROM_DATABASE=Micro Science Inc + +pci:v000013FE* + ID_VENDOR_FROM_DATABASE=Advantech Co. Ltd + +pci:v000013FEd00001240* + ID_MODEL_FROM_DATABASE=PCI-1240 4-channel stepper motor controller card + +pci:v000013FEd00001600* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 0) + +pci:v000013FEd00001600sv00001601sd00000002* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 0) (PCI-1601 2-port unisolated RS-422/485) + +pci:v000013FEd00001600sv00001602sd00000002* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 0) (PCI-1602 2-port isolated RS-422/485) + +pci:v000013FEd00001600sv00001612sd00000004* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 0) (PCI-1612 4-port RS-232/422/485) + +pci:v000013FEd00001603* + ID_MODEL_FROM_DATABASE=PCI-1603 2-port isolated RS-232/current loop + +pci:v000013FEd00001604* + ID_MODEL_FROM_DATABASE=PCI-1604 2-port RS-232 + +pci:v000013FEd000016FF* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 1: RX/TX steering CPLD) + +pci:v000013FEd000016FFsv00001601sd00000000* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 1: RX/TX steering CPLD) (PCI-1601 2-port unisolated RS-422/485 PCI communications card) + +pci:v000013FEd000016FFsv00001602sd00000000* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 1: RX/TX steering CPLD) (PCI-1602 2-port isolated RS-422/485) + +pci:v000013FEd000016FFsv00001612sd00000000* + ID_MODEL_FROM_DATABASE=PCI-16xx series PCI multiport serial board (function 1: RX/TX steering CPLD) (PCI-1612 4-port RS-232/422/485) + +pci:v000013FEd00001711* + ID_MODEL_FROM_DATABASE=PCI-1711 16-channel data acquisition card 12-bit, 100kS/s + +pci:v000013FEd00001733* + ID_MODEL_FROM_DATABASE=PCI-1733 32-channel isolated digital input card + +pci:v000013FEd00001752* + ID_MODEL_FROM_DATABASE=PCI-1752 + +pci:v000013FEd00001754* + ID_MODEL_FROM_DATABASE=PCI-1754 + +pci:v000013FEd00001756* + ID_MODEL_FROM_DATABASE=PCI-1756 + +pci:v000013FEd0000C302* + ID_MODEL_FROM_DATABASE=MIOe-3680 2-Port CAN-Bus MIOe Module with Isolation Protection + +pci:v000013FF* + ID_VENDOR_FROM_DATABASE=Silicon Spice Inc + +pci:v00001400* + ID_VENDOR_FROM_DATABASE=Artx Inc + +pci:v00001400d00001401* + ID_MODEL_FROM_DATABASE=9432 TX + +pci:v00001401* + ID_VENDOR_FROM_DATABASE=CR-Systems A/S + +pci:v00001402* + ID_VENDOR_FROM_DATABASE=Meilhaus Electronic GmbH + +pci:v00001402d00000630* + ID_MODEL_FROM_DATABASE=ME-630 + +pci:v00001402d00000940* + ID_MODEL_FROM_DATABASE=ME-94 + +pci:v00001402d00000950* + ID_MODEL_FROM_DATABASE=ME-95 + +pci:v00001402d00000960* + ID_MODEL_FROM_DATABASE=ME-96 + +pci:v00001402d00001000* + ID_MODEL_FROM_DATABASE=ME-1000 + +pci:v00001402d0000100A* + ID_MODEL_FROM_DATABASE=ME-1000 + +pci:v00001402d0000100B* + ID_MODEL_FROM_DATABASE=ME-1000 + +pci:v00001402d00001400* + ID_MODEL_FROM_DATABASE=ME-1400 + +pci:v00001402d0000140A* + ID_MODEL_FROM_DATABASE=ME-1400A + +pci:v00001402d0000140B* + ID_MODEL_FROM_DATABASE=ME-1400B + +pci:v00001402d0000140C* + ID_MODEL_FROM_DATABASE=ME-1400C + +pci:v00001402d0000140D* + ID_MODEL_FROM_DATABASE=ME-1400D + +pci:v00001402d0000140E* + ID_MODEL_FROM_DATABASE=ME-1400E + +pci:v00001402d000014EA* + ID_MODEL_FROM_DATABASE=ME-1400EA + +pci:v00001402d000014EB* + ID_MODEL_FROM_DATABASE=ME-1400EB + +pci:v00001402d00001604* + ID_MODEL_FROM_DATABASE=ME-1600/4U + +pci:v00001402d00001608* + ID_MODEL_FROM_DATABASE=ME-1600/8U + +pci:v00001402d0000160C* + ID_MODEL_FROM_DATABASE=ME-1600/12U + +pci:v00001402d0000160F* + ID_MODEL_FROM_DATABASE=ME-1600/16U + +pci:v00001402d0000168F* + ID_MODEL_FROM_DATABASE=ME-1600/16U8I + +pci:v00001402d00004610* + ID_MODEL_FROM_DATABASE=ME-4610 + +pci:v00001402d00004650* + ID_MODEL_FROM_DATABASE=ME-4650 + +pci:v00001402d00004660* + ID_MODEL_FROM_DATABASE=ME-4660 + +pci:v00001402d00004661* + ID_MODEL_FROM_DATABASE=ME-4660I + +pci:v00001402d00004662* + ID_MODEL_FROM_DATABASE=ME-4660 + +pci:v00001402d00004663* + ID_MODEL_FROM_DATABASE=ME-4660I + +pci:v00001402d00004670* + ID_MODEL_FROM_DATABASE=ME-4670 + +pci:v00001402d00004671* + ID_MODEL_FROM_DATABASE=ME-4670I + +pci:v00001402d00004672* + ID_MODEL_FROM_DATABASE=ME-4670S + +pci:v00001402d00004673* + ID_MODEL_FROM_DATABASE=ME-4670IS + +pci:v00001402d00004680* + ID_MODEL_FROM_DATABASE=ME-4680 + +pci:v00001402d00004681* + ID_MODEL_FROM_DATABASE=ME-4680I + +pci:v00001402d00004682* + ID_MODEL_FROM_DATABASE=ME-4680S + +pci:v00001402d00004683* + ID_MODEL_FROM_DATABASE=ME-4680IS + +pci:v00001402d00006004* + ID_MODEL_FROM_DATABASE=ME-6000/4 + +pci:v00001402d00006008* + ID_MODEL_FROM_DATABASE=ME-6000/8 + +pci:v00001402d0000600F* + ID_MODEL_FROM_DATABASE=ME-6000/16 + +pci:v00001402d00006014* + ID_MODEL_FROM_DATABASE=ME-6000I/4 + +pci:v00001402d00006018* + ID_MODEL_FROM_DATABASE=ME-6000I/8 + +pci:v00001402d0000601F* + ID_MODEL_FROM_DATABASE=ME-6000I/16 + +pci:v00001402d00006034* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/4 + +pci:v00001402d00006038* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/8 + +pci:v00001402d0000603F* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/16 + +pci:v00001402d00006044* + ID_MODEL_FROM_DATABASE=ME-6000/4/DIO + +pci:v00001402d00006048* + ID_MODEL_FROM_DATABASE=ME-6000/8/DIO + +pci:v00001402d0000604F* + ID_MODEL_FROM_DATABASE=ME-6000/16/DIO + +pci:v00001402d00006054* + ID_MODEL_FROM_DATABASE=ME-6000I/4/DIO + +pci:v00001402d00006058* + ID_MODEL_FROM_DATABASE=ME-6000I/8/DIO + +pci:v00001402d0000605F* + ID_MODEL_FROM_DATABASE=ME-6000I/16/DIO + +pci:v00001402d00006074* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/4/DIO + +pci:v00001402d00006078* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/8/DIO + +pci:v00001402d0000607F* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/16/DIO + +pci:v00001402d00006104* + ID_MODEL_FROM_DATABASE=ME-6100/4 + +pci:v00001402d00006108* + ID_MODEL_FROM_DATABASE=ME-6100/8 + +pci:v00001402d0000610F* + ID_MODEL_FROM_DATABASE=ME-6100/16 + +pci:v00001402d00006114* + ID_MODEL_FROM_DATABASE=ME-6100I/4 + +pci:v00001402d00006118* + ID_MODEL_FROM_DATABASE=ME-6100I/8 + +pci:v00001402d0000611F* + ID_MODEL_FROM_DATABASE=ME-6100I/16 + +pci:v00001402d00006134* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/4 + +pci:v00001402d00006138* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/8 + +pci:v00001402d0000613F* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/16 + +pci:v00001402d00006144* + ID_MODEL_FROM_DATABASE=ME-6100/4/DIO + +pci:v00001402d00006148* + ID_MODEL_FROM_DATABASE=ME-6100/8/DIO + +pci:v00001402d0000614F* + ID_MODEL_FROM_DATABASE=ME-6100/16/DIO + +pci:v00001402d00006154* + ID_MODEL_FROM_DATABASE=ME-6100I/4/DIO + +pci:v00001402d00006158* + ID_MODEL_FROM_DATABASE=ME-6100I/8/DIO + +pci:v00001402d0000615F* + ID_MODEL_FROM_DATABASE=ME-6100I/16/DIO + +pci:v00001402d00006174* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/4/DIO + +pci:v00001402d00006178* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/8/DIO + +pci:v00001402d0000617F* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/16/DIO + +pci:v00001402d00006259* + ID_MODEL_FROM_DATABASE=ME-6200I/9/DIO + +pci:v00001402d00006359* + ID_MODEL_FROM_DATABASE=ME-6300I/9/DIO + +pci:v00001402d0000810A* + ID_MODEL_FROM_DATABASE=ME-8100A + +pci:v00001402d0000810B* + ID_MODEL_FROM_DATABASE=ME-8100B + +pci:v00001402d0000820A* + ID_MODEL_FROM_DATABASE=ME-8200A + +pci:v00001402d0000820B* + ID_MODEL_FROM_DATABASE=ME-8200B + +pci:v00001403* + ID_VENDOR_FROM_DATABASE=Ascor Inc + +pci:v00001404* + ID_VENDOR_FROM_DATABASE=Fundamental Software Inc + +pci:v00001405* + ID_VENDOR_FROM_DATABASE=Excalibur Systems Inc + +pci:v00001406* + ID_VENDOR_FROM_DATABASE=Oce' Printing Systems GmbH + +pci:v00001407* + ID_VENDOR_FROM_DATABASE=Lava Computer mfg Inc + +pci:v00001407d00000100* + ID_MODEL_FROM_DATABASE=Lava Dual Serial + +pci:v00001407d00000101* + ID_MODEL_FROM_DATABASE=Lava Quatro A + +pci:v00001407d00000102* + ID_MODEL_FROM_DATABASE=Lava Quatro B + +pci:v00001407d00000110* + ID_MODEL_FROM_DATABASE=Lava DSerial-PCI Port A + +pci:v00001407d00000111* + ID_MODEL_FROM_DATABASE=Lava DSerial-PCI Port B + +pci:v00001407d00000120* + ID_MODEL_FROM_DATABASE=Quattro-PCI A + +pci:v00001407d00000121* + ID_MODEL_FROM_DATABASE=Quattro-PCI B + +pci:v00001407d00000180* + ID_MODEL_FROM_DATABASE=Lava Octo A + +pci:v00001407d00000181* + ID_MODEL_FROM_DATABASE=Lava Octo B + +pci:v00001407d00000200* + ID_MODEL_FROM_DATABASE=Lava Port Plus + +pci:v00001407d00000201* + ID_MODEL_FROM_DATABASE=Lava Quad A + +pci:v00001407d00000202* + ID_MODEL_FROM_DATABASE=Lava Quad B + +pci:v00001407d00000220* + ID_MODEL_FROM_DATABASE=Lava Quattro PCI Ports A/B + +pci:v00001407d00000221* + ID_MODEL_FROM_DATABASE=Lava Quattro PCI Ports C/D + +pci:v00001407d00000400* + ID_MODEL_FROM_DATABASE=Lava 8255-PIO-PCI + +pci:v00001407d00000500* + ID_MODEL_FROM_DATABASE=Lava Single Serial + +pci:v00001407d00000520* + ID_MODEL_FROM_DATABASE=Lava RS422-SS-PCI + +pci:v00001407d00000600* + ID_MODEL_FROM_DATABASE=Lava Port 650 + +pci:v00001407d00008000* + ID_MODEL_FROM_DATABASE=Lava Parallel + +pci:v00001407d00008001* + ID_MODEL_FROM_DATABASE=Dual parallel port controller A + +pci:v00001407d00008002* + ID_MODEL_FROM_DATABASE=Lava Dual Parallel port A + +pci:v00001407d00008003* + ID_MODEL_FROM_DATABASE=Lava Dual Parallel port B + +pci:v00001407d00008800* + ID_MODEL_FROM_DATABASE=BOCA Research IOPPAR + +pci:v00001408* + ID_VENDOR_FROM_DATABASE=Aloka Co. Ltd + +pci:v00001409* + ID_VENDOR_FROM_DATABASE=Timedia Technology Co Ltd + +pci:v00001409d00007168* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) + +pci:v00001409d00007168sv00001409sd00000002* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4036A3V (2x RS232 port)) + +pci:v00001409d00007168sv00001409sd00004027* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4027A (1x RS232 port)) + +pci:v00001409d00007168sv00001409sd00004037* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4037A(L) [SUNIX SUN1889] (2x RS232 port)) + +pci:v00001409d00007168sv00001409sd00004056* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4056A (4x RS232)) + +pci:v00001409d00007168sv00001409sd00005027* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4027D) + +pci:v00001409d00007168sv00001409sd00005037* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4037D (2x RS232 port)) + +pci:v00001409d00007168sv00001409sd00005066* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4066R (8x RS232)) + +pci:v00001409d00007168sv00001409sd00006056* + ID_MODEL_FROM_DATABASE=PCI2S550 (Dual 16550 UART) (SER4056D (4x RS232 port)) + +pci:v00001409d00007268* + ID_MODEL_FROM_DATABASE=SUN1888 (Dual IEEE1284 parallel port) + +pci:v00001409d00007268sv00001409sd00000103* + ID_MODEL_FROM_DATABASE=SUN1888 (Dual IEEE1284 parallel port) (PAR4008A) + +pci:v00001409d00007268sv00001409sd00000104* + ID_MODEL_FROM_DATABASE=SUN1888 (Dual IEEE1284 parallel port) (PAR4018A) + +pci:v0000140A* + ID_VENDOR_FROM_DATABASE=DSP Research Inc + +pci:v0000140B* + ID_VENDOR_FROM_DATABASE=GE Intelligent Platforms + +pci:v0000140C* + ID_VENDOR_FROM_DATABASE=Elmic Systems Inc + +pci:v0000140D* + ID_VENDOR_FROM_DATABASE=Matsushita Electric Works Ltd + +pci:v0000140E* + ID_VENDOR_FROM_DATABASE=Goepel Electronic GmbH + +pci:v0000140F* + ID_VENDOR_FROM_DATABASE=Salient Systems Corp + +pci:v00001410* + ID_VENDOR_FROM_DATABASE=Midas lab Inc + +pci:v00001411* + ID_VENDOR_FROM_DATABASE=Ikos Systems Inc + +pci:v00001412* + ID_VENDOR_FROM_DATABASE=VIA Technologies Inc. + +pci:v00001412d00001712* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller + +pci:v00001412d00001712sv00001412sd00001712* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (Hoontech ST Audio DSP 24) + +pci:v00001412d00001712sv00001412sd0000D630* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta 1010) + +pci:v00001412d00001712sv00001412sd0000D631* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta DiO) + +pci:v00001412d00001712sv00001412sd0000D632* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta 66) + +pci:v00001412d00001712sv00001412sd0000D633* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta 44) + +pci:v00001412d00001712sv00001412sd0000D634* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta Audiophile 2496) + +pci:v00001412d00001712sv00001412sd0000D635* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta TDIF) + +pci:v00001412d00001712sv00001412sd0000D637* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta RBUS) + +pci:v00001412d00001712sv00001412sd0000D638* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta 410) + +pci:v00001412d00001712sv00001412sd0000D63B* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (M-Audio Delta 1010LT) + +pci:v00001412d00001712sv00001412sd0000D63C* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (Digigram VX442) + +pci:v00001412d00001712sv00001416sd00001712* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (Hoontech ST Audio DSP 24 Media 7.1) + +pci:v00001412d00001712sv0000153Bsd00001115* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (EWS88 MT) + +pci:v00001412d00001712sv0000153Bsd00001125* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (EWS88 MT (Master)) + +pci:v00001412d00001712sv0000153Bsd0000112B* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (EWS88 D) + +pci:v00001412d00001712sv0000153Bsd0000112C* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (EWS88 D (Master)) + +pci:v00001412d00001712sv0000153Bsd00001130* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (EWX 24/96) + +pci:v00001412d00001712sv0000153Bsd00001138* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (DMX 6fire 24/96) + +pci:v00001412d00001712sv0000153Bsd00001151* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (PHASE88) + +pci:v00001412d00001712sv000016CEsd00001040* + ID_MODEL_FROM_DATABASE=ICE1712 [Envy24] PCI Multi-Channel I/O Controller (Edirol DA-2496) + +pci:v00001412d00001724* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller + +pci:v00001412d00001724sv000010B0sd00000200* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (Hollywood@Home 7.1) + +pci:v00001412d00001724sv00001412sd00001724* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (Albatron PX865PE 7.1) + +pci:v00001412d00001724sv00001412sd00003630* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (M-Audio Revolution 7.1) + +pci:v00001412d00001724sv00001412sd00003631* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (M-Audio Revolution 5.1) + +pci:v00001412d00001724sv00001412sd00003632* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (M-Audio Audiophile 192) + +pci:v00001412d00001724sv0000153Bsd00001145* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (Aureon 7.1 Space) + +pci:v00001412d00001724sv0000153Bsd00001147* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (Aureon 5.1 Sky) + +pci:v00001412d00001724sv0000153Bsd00001150* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (PHASE 22) + +pci:v00001412d00001724sv0000153Bsd00001153* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (Aureon 7.1 Universe) + +pci:v00001412d00001724sv000017ABsd00001906* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (PSC 724 [Ultimate Edge]) + +pci:v00001412d00001724sv0000270Fsd0000F641* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (ZNF3-150) + +pci:v00001412d00001724sv0000270Fsd0000F645* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (ZNF3-250) + +pci:v00001412d00001724sv00003130sd00004154* + ID_MODEL_FROM_DATABASE=VT1720/24 [Envy24PT/HT] PCI Multi-Channel Audio Controller (MAYA 44 MKII) + +pci:v00001413* + ID_VENDOR_FROM_DATABASE=Addonics + +pci:v00001414* + ID_VENDOR_FROM_DATABASE=Microsoft Corporation + +pci:v00001414d00000001* + ID_MODEL_FROM_DATABASE=MN-120 (ADMtek Centaur-C based) + +pci:v00001414d00000002* + ID_MODEL_FROM_DATABASE=MN-130 (ADMtek Centaur-P based) + +pci:v00001414d00005353* + ID_MODEL_FROM_DATABASE=Hyper-V virtual VGA + +pci:v00001414d00005801* + ID_MODEL_FROM_DATABASE=XMA Decoder (Xenon) + +pci:v00001414d00005802* + ID_MODEL_FROM_DATABASE=SATA Controller - CdRom (Xenon) + +pci:v00001414d00005803* + ID_MODEL_FROM_DATABASE=SATA Controller - Disk (Xenon) + +pci:v00001414d00005804* + ID_MODEL_FROM_DATABASE=OHCI Controller 0 (Xenon) + +pci:v00001414d00005805* + ID_MODEL_FROM_DATABASE=EHCI Controller 0 (Xenon) + +pci:v00001414d00005806* + ID_MODEL_FROM_DATABASE=OHCI Controller 1 (Xenon) + +pci:v00001414d00005807* + ID_MODEL_FROM_DATABASE=EHCI Controller 1 (Xenon) + +pci:v00001414d0000580A* + ID_MODEL_FROM_DATABASE=Fast Ethernet Adapter (Xenon) + +pci:v00001414d0000580B* + ID_MODEL_FROM_DATABASE=Secure Flash Controller (Xenon) + +pci:v00001414d0000580D* + ID_MODEL_FROM_DATABASE=System Management Controller (Xenon) + +pci:v00001414d00005811* + ID_MODEL_FROM_DATABASE=Xenos GPU (Xenon) + +pci:v00001415* + ID_VENDOR_FROM_DATABASE=Oxford Semiconductor Ltd + +pci:v00001415d00008401* + ID_MODEL_FROM_DATABASE=OX9162 Mode 1 (8-bit bus) + +pci:v00001415d00008403* + ID_MODEL_FROM_DATABASE=OX9162 Mode 0 (parallel port) + +pci:v00001415d00009500* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Disabled) + +pci:v00001415d00009501* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) + +pci:v00001415d00009501sv000012C4sd00000201* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (Titan/cPCI (2 port)) + +pci:v00001415d00009501sv000012C4sd00000202* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (Titan/cPCI (4 port)) + +pci:v00001415d00009501sv000012C4sd00000203* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (Titan/cPCI (8 port)) + +pci:v00001415d00009501sv000012C4sd00000210* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (Titan/104-Plus (8 port, p1-4)) + +pci:v00001415d00009501sv0000131Fsd00002050* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (CyberPro (4-port)) + +pci:v00001415d00009501sv0000131Fsd00002051* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (CyberSerial 4S Plus) + +pci:v00001415d00009501sv000015EDsd00002000* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (MCCR Serial p0-3 of 8) + +pci:v00001415d00009501sv000015EDsd00002001* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 0 (Uart) (MCCR Serial p0-3 of 16) + +pci:v00001415d00009505* + ID_MODEL_FROM_DATABASE=OXuPCI952 (Dual 16C950 UART) + +pci:v00001415d0000950A* + ID_MODEL_FROM_DATABASE=EXSYS EX-41092 Dual 16950 Serial adapter + +pci:v00001415d0000950B* + ID_MODEL_FROM_DATABASE=OXCB950 Cardbus 16950 UART + +pci:v00001415d00009510* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (Disabled) + +pci:v00001415d00009510sv000012C4sd00000200* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (Disabled) (Titan/cPCI (Unused)) + +pci:v00001415d00009511* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (8bit bus) + +pci:v00001415d00009511sv000012C4sd00000211* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (8bit bus) (Titan/104-Plus (8 port, p5-8)) + +pci:v00001415d00009511sv000015EDsd00002000* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (8bit bus) (MCCR Serial p4-7 of 8) + +pci:v00001415d00009511sv000015EDsd00002001* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (8bit bus) (MCCR Serial p4-15 of 16) + +pci:v00001415d00009512* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (32bit bus) + +pci:v00001415d00009513* + ID_MODEL_FROM_DATABASE=OX16PCI954 (Quad 16950 UART) function 1 (parallel port) + +pci:v00001415d00009521* + ID_MODEL_FROM_DATABASE=OX16PCI952 (Dual 16950 UART) + +pci:v00001415d00009523* + ID_MODEL_FROM_DATABASE=OX16PCI952 Integrated Parallel Port + +pci:v00001415d0000C158* + ID_MODEL_FROM_DATABASE=OXPCIe952 Dual 16C950 UART + +pci:v00001415d0000C158sv0000E4BFsd0000C504* + ID_MODEL_FROM_DATABASE=OXPCIe952 Dual 16C950 UART (CP4-SCAT Wireless Technologies Carrier Board) + +pci:v00001415d0000C158sv0000E4BFsd0000D551* + ID_MODEL_FROM_DATABASE=OXPCIe952 Dual 16C950 UART (DU1-MUSTANG Dual-Port RS-485 Interface) + +pci:v00001415d0000C308* + ID_MODEL_FROM_DATABASE=EX-44016 16-port serial + +pci:v00001416* + ID_VENDOR_FROM_DATABASE=Multiwave Innovation pte Ltd + +pci:v00001417* + ID_VENDOR_FROM_DATABASE=Convergenet Technologies Inc + +pci:v00001418* + ID_VENDOR_FROM_DATABASE=Kyushu electronics systems Inc + +pci:v00001419* + ID_VENDOR_FROM_DATABASE=Excel Switching Corp + +pci:v0000141A* + ID_VENDOR_FROM_DATABASE=Apache Micro Peripherals Inc + +pci:v0000141B* + ID_VENDOR_FROM_DATABASE=Zoom Telephonics Inc + +pci:v0000141D* + ID_VENDOR_FROM_DATABASE=Digitan Systems Inc + +pci:v0000141E* + ID_VENDOR_FROM_DATABASE=Fanuc Ltd + +pci:v0000141F* + ID_VENDOR_FROM_DATABASE=Visiontech Ltd + +pci:v00001420* + ID_VENDOR_FROM_DATABASE=Psion Dacom plc + +pci:v00001420d00008002* + ID_MODEL_FROM_DATABASE=Gold Card NetGlobal 56k+10/100Mb CardBus (Ethernet part) + +pci:v00001420d00008003* + ID_MODEL_FROM_DATABASE=Gold Card NetGlobal 56k+10/100Mb CardBus (Modem part) + +pci:v00001421* + ID_VENDOR_FROM_DATABASE=Ads Technologies Inc + +pci:v00001422* + ID_VENDOR_FROM_DATABASE=Ygrec Systems Co Ltd + +pci:v00001423* + ID_VENDOR_FROM_DATABASE=Custom Technology Corp. + +pci:v00001424* + ID_VENDOR_FROM_DATABASE=Videoserver Connections + +pci:v00001425* + ID_VENDOR_FROM_DATABASE=Chelsio Communications Inc + +pci:v00001425d0000000B* + ID_MODEL_FROM_DATABASE=T210 Protocol Engine + +pci:v00001425d0000000C* + ID_MODEL_FROM_DATABASE=T204 Protocol Engine + +pci:v00001425d00000022* + ID_MODEL_FROM_DATABASE=10GbE Ethernet Adapter + +pci:v00001425d00000030* + ID_MODEL_FROM_DATABASE=T310 10GbE Single Port Adapter + +pci:v00001425d00000030sv0000103Csd0000705E* + ID_MODEL_FROM_DATABASE=T310 10GbE Single Port Adapter (PCIe 10GBase-SR [AD386A]) + +pci:v00001425d00000031* + ID_MODEL_FROM_DATABASE=T320 10GbE Dual Port Adapter + +pci:v00001425d00000032* + ID_MODEL_FROM_DATABASE=T302 1GbE Dual Port Adapter + +pci:v00001425d00000033* + ID_MODEL_FROM_DATABASE=T304 1GbE Quad Port Adapter + +pci:v00001425d00000034* + ID_MODEL_FROM_DATABASE=B320 10GbE Dual Port Adapter + +pci:v00001425d00000035* + ID_MODEL_FROM_DATABASE=S310-CR 10GbE Single Port Adapter + +pci:v00001425d00000036* + ID_MODEL_FROM_DATABASE=S320-LP-CR 10GbE Dual Port Adapter + +pci:v00001425d00000037* + ID_MODEL_FROM_DATABASE=N320-G2-CR 10GbE Dual Port Adapter + +pci:v00001425d00004001* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Ethernet Controller + +pci:v00001425d00004002* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Ethernet Controller + +pci:v00001425d00004003* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Ethernet Controller + +pci:v00001425d00004004* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004005* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004006* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Ethernet Controller + +pci:v00001425d00004007* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Ethernet Controller + +pci:v00001425d00004008* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Ethernet Controller + +pci:v00001425d00004009* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Ethernet Controller + +pci:v00001425d0000400A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000400B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Ethernet Controller + +pci:v00001425d0000400C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000400D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Ethernet Controller + +pci:v00001425d0000400E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000400F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00004080* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Ethernet Controller + +pci:v00001425d00004081* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Ethernet Controller + +pci:v00001425d00004082* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Ethernet Controller + +pci:v00001425d00004083* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Ethernet Controller + +pci:v00001425d00004084* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Ethernet Controller + +pci:v00001425d00004085* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Ethernet Controller + +pci:v00001425d00004086* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Ethernet Controller + +pci:v00001425d00004087* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Ethernet Controller + +pci:v00001425d00004088* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Ethernet Controller + +pci:v00001425d00004401* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Ethernet Controller + +pci:v00001425d00004402* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Ethernet Controller + +pci:v00001425d00004403* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Ethernet Controller + +pci:v00001425d00004404* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004405* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004406* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Ethernet Controller + +pci:v00001425d00004407* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Ethernet Controller + +pci:v00001425d00004408* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Ethernet Controller + +pci:v00001425d00004409* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Ethernet Controller + +pci:v00001425d0000440A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000440B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Ethernet Controller + +pci:v00001425d0000440C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000440D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Ethernet Controller + +pci:v00001425d0000440E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000440F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00004480* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Ethernet Controller + +pci:v00001425d00004481* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Ethernet Controller + +pci:v00001425d00004482* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Ethernet Controller + +pci:v00001425d00004483* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Ethernet Controller + +pci:v00001425d00004484* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Ethernet Controller + +pci:v00001425d00004485* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Ethernet Controller + +pci:v00001425d00004486* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Ethernet Controller + +pci:v00001425d00004487* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Ethernet Controller + +pci:v00001425d00004488* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Ethernet Controller + +pci:v00001425d00004501* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Storage Controller + +pci:v00001425d00004502* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Storage Controller + +pci:v00001425d00004503* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Storage Controller + +pci:v00001425d00004504* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Storage Controller + +pci:v00001425d00004505* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Storage Controller + +pci:v00001425d00004506* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Storage Controller + +pci:v00001425d00004507* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Storage Controller + +pci:v00001425d00004508* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Storage Controller + +pci:v00001425d00004509* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Storage Controller + +pci:v00001425d0000450A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Storage Controller + +pci:v00001425d0000450B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Storage Controller + +pci:v00001425d0000450C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Storage Controller + +pci:v00001425d0000450D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Storage Controller + +pci:v00001425d0000450E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Storage Controller + +pci:v00001425d0000450F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Storage Controller + +pci:v00001425d00004580* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Storage Controller + +pci:v00001425d00004581* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Storage Controller + +pci:v00001425d00004582* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Storage Controller + +pci:v00001425d00004583* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Storage Controller + +pci:v00001425d00004584* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Storage Controller + +pci:v00001425d00004585* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Storage Controller + +pci:v00001425d00004586* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Storage Controller + +pci:v00001425d00004587* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Storage Controller + +pci:v00001425d00004588* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Storage Controller + +pci:v00001425d00004601* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Storage Controller + +pci:v00001425d00004602* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Storage Controller + +pci:v00001425d00004603* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Storage Controller + +pci:v00001425d00004604* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Storage Controller + +pci:v00001425d00004605* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Storage Controller + +pci:v00001425d00004606* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Storage Controller + +pci:v00001425d00004607* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Storage Controller + +pci:v00001425d00004608* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Storage Controller + +pci:v00001425d00004609* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Storage Controller + +pci:v00001425d0000460A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Storage Controller + +pci:v00001425d0000460B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Storage Controller + +pci:v00001425d0000460C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Storage Controller + +pci:v00001425d0000460D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Storage Controller + +pci:v00001425d0000460E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Storage Controller + +pci:v00001425d0000460F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Storage Controller + +pci:v00001425d00004680* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Storage Controller + +pci:v00001425d00004681* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Storage Controller + +pci:v00001425d00004682* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Storage Controller + +pci:v00001425d00004683* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Storage Controller + +pci:v00001425d00004684* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Storage Controller + +pci:v00001425d00004685* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Storage Controller + +pci:v00001425d00004686* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Storage Controller + +pci:v00001425d00004687* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Storage Controller + +pci:v00001425d00004688* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Storage Controller + +pci:v00001425d00004701* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Ethernet Controller + +pci:v00001425d00004702* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Ethernet Controller + +pci:v00001425d00004703* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Ethernet Controller + +pci:v00001425d00004704* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004705* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Ethernet Controller + +pci:v00001425d00004706* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Ethernet Controller + +pci:v00001425d00004707* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Ethernet Controller + +pci:v00001425d00004708* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Ethernet Controller + +pci:v00001425d00004709* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Ethernet Controller + +pci:v00001425d0000470A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000470B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Ethernet Controller + +pci:v00001425d0000470C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Ethernet Controller + +pci:v00001425d0000470D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Ethernet Controller + +pci:v00001425d0000470E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000470F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00004780* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Ethernet Controller + +pci:v00001425d00004781* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Ethernet Controller + +pci:v00001425d00004782* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Ethernet Controller + +pci:v00001425d00004783* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Ethernet Controller + +pci:v00001425d00004784* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Ethernet Controller + +pci:v00001425d00004785* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Ethernet Controller + +pci:v00001425d00004786* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Ethernet Controller + +pci:v00001425d00004787* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Ethernet Controller + +pci:v00001425d00004788* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Ethernet Controller + +pci:v00001425d00004801* + ID_MODEL_FROM_DATABASE=T420-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004802* + ID_MODEL_FROM_DATABASE=T422-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004803* + ID_MODEL_FROM_DATABASE=T440-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004804* + ID_MODEL_FROM_DATABASE=T420-BCH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004805* + ID_MODEL_FROM_DATABASE=T440-BCH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004806* + ID_MODEL_FROM_DATABASE=T440-CH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004807* + ID_MODEL_FROM_DATABASE=T420-SO Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004808* + ID_MODEL_FROM_DATABASE=T420-CX Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004809* + ID_MODEL_FROM_DATABASE=T420-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480A* + ID_MODEL_FROM_DATABASE=T404-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480B* + ID_MODEL_FROM_DATABASE=B420-SR Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480C* + ID_MODEL_FROM_DATABASE=B404-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480D* + ID_MODEL_FROM_DATABASE=T480 Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480E* + ID_MODEL_FROM_DATABASE=T440-LP-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000480F* + ID_MODEL_FROM_DATABASE=T440 [Amsterdam] Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004880* + ID_MODEL_FROM_DATABASE=T480-4080 T480 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004881* + ID_MODEL_FROM_DATABASE=T440F-4081 T440-FCoE Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004882* + ID_MODEL_FROM_DATABASE=T420-4082 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004883* + ID_MODEL_FROM_DATABASE=T420X-4083 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004884* + ID_MODEL_FROM_DATABASE=T440-4084 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004885* + ID_MODEL_FROM_DATABASE=T420-4085 SFP+ Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004886* + ID_MODEL_FROM_DATABASE=T440-4086 10Gbase-T Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004887* + ID_MODEL_FROM_DATABASE=T440T-4087 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00004888* + ID_MODEL_FROM_DATABASE=T440-4088 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005001* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller + +pci:v00001425d00005002* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Ethernet Controller + +pci:v00001425d00005003* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Ethernet Controller + +pci:v00001425d00005004* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005005* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005006* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Ethernet Controller + +pci:v00001425d00005007* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Ethernet Controller + +pci:v00001425d00005008* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Ethernet Controller + +pci:v00001425d00005009* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Ethernet Controller + +pci:v00001425d0000500A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000500B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Ethernet Controller + +pci:v00001425d0000500C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000500D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Ethernet Controller + +pci:v00001425d0000500E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000500F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00005010* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d00005011* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Ethernet Controller + +pci:v00001425d00005012* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Ethernet Controller + +pci:v00001425d00005013* + ID_MODEL_FROM_DATABASE=T580-CHR Unified Wire Ethernet Controller + +pci:v00001425d00005014* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Ethernet Controller + +pci:v00001425d00005015* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Ethernet Controller + +pci:v00001425d00005016* + ID_MODEL_FROM_DATABASE=T580-OCP-SO Unified Wire Ethernet Controller + +pci:v00001425d00005017* + ID_MODEL_FROM_DATABASE=T520-OCP-SO Unified Wire Ethernet Controller + +pci:v00001425d00005018* + ID_MODEL_FROM_DATABASE=T540-BT Unified Wire Ethernet Controller + +pci:v00001425d00005080* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Ethernet Controller + +pci:v00001425d00005081* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Ethernet Controller + +pci:v00001425d00005082* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Ethernet Controller + +pci:v00001425d00005083* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Ethernet Controller + +pci:v00001425d00005084* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Ethernet Controller + +pci:v00001425d00005085* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Ethernet Controller + +pci:v00001425d00005086* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Ethernet Controller + +pci:v00001425d00005087* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Ethernet Controller + +pci:v00001425d00005088* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Ethernet Controller + +pci:v00001425d00005089* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Ethernet Controller + +pci:v00001425d00005090* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Ethernet Controller + +pci:v00001425d00005091* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Ethernet Controller + +pci:v00001425d00005092* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Ethernet Controller + +pci:v00001425d00005093* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Ethernet Controller + +pci:v00001425d00005094* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Ethernet Controller + +pci:v00001425d00005095* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Ethernet Controller + +pci:v00001425d00005096* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Ethernet Controller + +pci:v00001425d00005097* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Ethernet Controller + +pci:v00001425d00005098* + ID_MODEL_FROM_DATABASE=T580-5098 Unified Wire Ethernet Controller + +pci:v00001425d00005099* + ID_MODEL_FROM_DATABASE=T580-5099 Unified Wire Ethernet Controller + +pci:v00001425d0000509A* + ID_MODEL_FROM_DATABASE=T520-509A Unified Wire Ethernet Controller + +pci:v00001425d0000509B* + ID_MODEL_FROM_DATABASE=T540-509B Unified Wire Ethernet Controller + +pci:v00001425d0000509C* + ID_MODEL_FROM_DATABASE=T520-509C Unified Wire Ethernet Controller + +pci:v00001425d00005401* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller + +pci:v00001425d00005402* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Ethernet Controller + +pci:v00001425d00005403* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Ethernet Controller + +pci:v00001425d00005404* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005405* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005406* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Ethernet Controller + +pci:v00001425d00005407* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Ethernet Controller + +pci:v00001425d00005408* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Ethernet Controller + +pci:v00001425d00005409* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Ethernet Controller + +pci:v00001425d0000540A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000540B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Ethernet Controller + +pci:v00001425d0000540C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000540D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Ethernet Controller + +pci:v00001425d0000540E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000540F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00005410* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d00005411* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Ethernet Controller + +pci:v00001425d00005412* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Ethernet Controller + +pci:v00001425d00005413* + ID_MODEL_FROM_DATABASE=T580-CHR Unified Wire Ethernet Controller + +pci:v00001425d00005414* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Ethernet Controller + +pci:v00001425d00005415* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Ethernet Controller + +pci:v00001425d00005416* + ID_MODEL_FROM_DATABASE=T580-OCP-SO Unified Wire Ethernet Controller + +pci:v00001425d00005417* + ID_MODEL_FROM_DATABASE=T520-OCP-SO Unified Wire Ethernet Controller + +pci:v00001425d00005418* + ID_MODEL_FROM_DATABASE=T540-BT Unified Wire Ethernet Controller + +pci:v00001425d00005480* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Ethernet Controller + +pci:v00001425d00005481* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Ethernet Controller + +pci:v00001425d00005482* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Ethernet Controller + +pci:v00001425d00005483* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Ethernet Controller + +pci:v00001425d00005484* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Ethernet Controller + +pci:v00001425d00005485* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Ethernet Controller + +pci:v00001425d00005486* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Ethernet Controller + +pci:v00001425d00005487* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Ethernet Controller + +pci:v00001425d00005488* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Ethernet Controller + +pci:v00001425d00005489* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Ethernet Controller + +pci:v00001425d00005490* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Ethernet Controller + +pci:v00001425d00005491* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Ethernet Controller + +pci:v00001425d00005492* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Ethernet Controller + +pci:v00001425d00005493* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Ethernet Controller + +pci:v00001425d00005494* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Ethernet Controller + +pci:v00001425d00005495* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Ethernet Controller + +pci:v00001425d00005496* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Ethernet Controller + +pci:v00001425d00005497* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Ethernet Controller + +pci:v00001425d00005498* + ID_MODEL_FROM_DATABASE=T580-5098 Unified Wire Ethernet Controller + +pci:v00001425d00005499* + ID_MODEL_FROM_DATABASE=T580-5099 Unified Wire Ethernet Controller + +pci:v00001425d0000549A* + ID_MODEL_FROM_DATABASE=T520-509A Unified Wire Ethernet Controller + +pci:v00001425d0000549B* + ID_MODEL_FROM_DATABASE=T540-509B Unified Wire Ethernet Controller + +pci:v00001425d0000549C* + ID_MODEL_FROM_DATABASE=T520-509C Unified Wire Ethernet Controller + +pci:v00001425d00005501* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Storage Controller + +pci:v00001425d00005502* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Storage Controller + +pci:v00001425d00005503* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Storage Controller + +pci:v00001425d00005504* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Storage Controller + +pci:v00001425d00005505* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Storage Controller + +pci:v00001425d00005506* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Storage Controller + +pci:v00001425d00005507* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Storage Controller + +pci:v00001425d00005508* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Storage Controller + +pci:v00001425d00005509* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Storage Controller + +pci:v00001425d0000550A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Storage Controller + +pci:v00001425d0000550B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Storage Controller + +pci:v00001425d0000550C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Storage Controller + +pci:v00001425d0000550D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Storage Controller + +pci:v00001425d0000550E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Storage Controller + +pci:v00001425d0000550F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Storage Controller + +pci:v00001425d00005510* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Storage Controller + +pci:v00001425d00005511* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Storage Controller + +pci:v00001425d00005512* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Storage Controller + +pci:v00001425d00005513* + ID_MODEL_FROM_DATABASE=T580-CHR Unified Wire Storage Controller + +pci:v00001425d00005514* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Storage Controller + +pci:v00001425d00005515* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Storage Controller + +pci:v00001425d00005516* + ID_MODEL_FROM_DATABASE=T580-OCP-SO Unified Wire Storage Controller + +pci:v00001425d00005517* + ID_MODEL_FROM_DATABASE=T520-OCP-SO Unified Wire Storage Controller + +pci:v00001425d00005518* + ID_MODEL_FROM_DATABASE=T540-BT Unified Wire Storage Controller + +pci:v00001425d00005580* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Storage Controller + +pci:v00001425d00005581* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Storage Controller + +pci:v00001425d00005582* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Storage Controller + +pci:v00001425d00005583* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Storage Controller + +pci:v00001425d00005584* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Storage Controller + +pci:v00001425d00005585* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Storage Controller + +pci:v00001425d00005586* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Storage Controller + +pci:v00001425d00005587* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Storage Controller + +pci:v00001425d00005588* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Storage Controller + +pci:v00001425d00005589* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Storage Controller + +pci:v00001425d00005590* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Storage Controller + +pci:v00001425d00005591* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Storage Controller + +pci:v00001425d00005592* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Storage Controller + +pci:v00001425d00005593* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Storage Controller + +pci:v00001425d00005594* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Storage Controller + +pci:v00001425d00005595* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Storage Controller + +pci:v00001425d00005596* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Storage Controller + +pci:v00001425d00005597* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Storage Controller + +pci:v00001425d00005598* + ID_MODEL_FROM_DATABASE=T580-5098 Unified Wire Storage Controller + +pci:v00001425d00005599* + ID_MODEL_FROM_DATABASE=T580-5099 Unified Wire Storage Controller + +pci:v00001425d0000559A* + ID_MODEL_FROM_DATABASE=T520-509A Unified Wire Storage Controller + +pci:v00001425d0000559B* + ID_MODEL_FROM_DATABASE=T540-509B Unified Wire Storage Controller + +pci:v00001425d0000559C* + ID_MODEL_FROM_DATABASE=T520-509C Unified Wire Storage Controller + +pci:v00001425d00005601* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Storage Controller + +pci:v00001425d00005602* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Storage Controller + +pci:v00001425d00005603* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Storage Controller + +pci:v00001425d00005604* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Storage Controller + +pci:v00001425d00005605* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Storage Controller + +pci:v00001425d00005606* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Storage Controller + +pci:v00001425d00005607* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Storage Controller + +pci:v00001425d00005608* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Storage Controller + +pci:v00001425d00005609* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Storage Controller + +pci:v00001425d0000560A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Storage Controller + +pci:v00001425d0000560B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Storage Controller + +pci:v00001425d0000560C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Storage Controller + +pci:v00001425d0000560D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Storage Controller + +pci:v00001425d0000560E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Storage Controller + +pci:v00001425d0000560F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Storage Controller + +pci:v00001425d00005610* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Storage Controller + +pci:v00001425d00005611* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Storage Controller + +pci:v00001425d00005612* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Storage Controller + +pci:v00001425d00005613* + ID_MODEL_FROM_DATABASE=T580-CHR Unified Wire Storage Controller + +pci:v00001425d00005614* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Storage Controller + +pci:v00001425d00005615* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Storage Controller + +pci:v00001425d00005616* + ID_MODEL_FROM_DATABASE=T580-OCP-SO Unified Wire Storage Controller + +pci:v00001425d00005617* + ID_MODEL_FROM_DATABASE=T520-OCP-SO Unified Wire Storage Controller + +pci:v00001425d00005618* + ID_MODEL_FROM_DATABASE=T540-BT Unified Wire Storage Controller + +pci:v00001425d00005680* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Storage Controller + +pci:v00001425d00005681* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Storage Controller + +pci:v00001425d00005682* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Storage Controller + +pci:v00001425d00005683* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Storage Controller + +pci:v00001425d00005684* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Storage Controller + +pci:v00001425d00005685* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Storage Controller + +pci:v00001425d00005686* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Storage Controller + +pci:v00001425d00005687* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Storage Controller + +pci:v00001425d00005688* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Storage Controller + +pci:v00001425d00005689* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Storage Controller + +pci:v00001425d00005690* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Storage Controller + +pci:v00001425d00005691* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Storage Controller + +pci:v00001425d00005692* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Storage Controller + +pci:v00001425d00005693* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Storage Controller + +pci:v00001425d00005694* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Storage Controller + +pci:v00001425d00005695* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Storage Controller + +pci:v00001425d00005696* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Storage Controller + +pci:v00001425d00005697* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Storage Controller + +pci:v00001425d00005698* + ID_MODEL_FROM_DATABASE=T580-5098 Unified Wire Storage Controller + +pci:v00001425d00005699* + ID_MODEL_FROM_DATABASE=T580-5099 Unified Wire Storage Controller + +pci:v00001425d0000569A* + ID_MODEL_FROM_DATABASE=T520-509A Unified Wire Storage Controller + +pci:v00001425d0000569B* + ID_MODEL_FROM_DATABASE=T540-509B Unified Wire Storage Controller + +pci:v00001425d0000569C* + ID_MODEL_FROM_DATABASE=T520-509C Unified Wire Storage Controller + +pci:v00001425d00005701* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller + +pci:v00001425d00005702* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Ethernet Controller + +pci:v00001425d00005703* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Ethernet Controller + +pci:v00001425d00005704* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005705* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Ethernet Controller + +pci:v00001425d00005706* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Ethernet Controller + +pci:v00001425d00005707* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Ethernet Controller + +pci:v00001425d00005708* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Ethernet Controller + +pci:v00001425d00005709* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Ethernet Controller + +pci:v00001425d0000570A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000570B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Ethernet Controller + +pci:v00001425d0000570C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Ethernet Controller + +pci:v00001425d0000570D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Ethernet Controller + +pci:v00001425d0000570E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d0000570F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Ethernet Controller + +pci:v00001425d00005710* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Ethernet Controller + +pci:v00001425d00005711* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Ethernet Controller + +pci:v00001425d00005712* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Ethernet Controller + +pci:v00001425d00005713* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Ethernet Controller + +pci:v00001425d00005714* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Ethernet Controller + +pci:v00001425d00005715* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Ethernet Controller + +pci:v00001425d00005780* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Ethernet Controller + +pci:v00001425d00005781* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Ethernet Controller + +pci:v00001425d00005782* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Ethernet Controller + +pci:v00001425d00005783* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Ethernet Controller + +pci:v00001425d00005784* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Ethernet Controller + +pci:v00001425d00005785* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Ethernet Controller + +pci:v00001425d00005786* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Ethernet Controller + +pci:v00001425d00005787* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Ethernet Controller + +pci:v00001425d00005788* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Ethernet Controller + +pci:v00001425d00005789* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Ethernet Controller + +pci:v00001425d00005790* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Ethernet Controller + +pci:v00001425d00005791* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Ethernet Controller + +pci:v00001425d00005792* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Ethernet Controller + +pci:v00001425d00005793* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Ethernet Controller + +pci:v00001425d00005794* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Ethernet Controller + +pci:v00001425d00005795* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Ethernet Controller + +pci:v00001425d00005796* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Ethernet Controller + +pci:v00001425d00005797* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Ethernet Controller + +pci:v00001425d00005801* + ID_MODEL_FROM_DATABASE=T520-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005802* + ID_MODEL_FROM_DATABASE=T522-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005803* + ID_MODEL_FROM_DATABASE=T540-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005804* + ID_MODEL_FROM_DATABASE=T520-BCH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005805* + ID_MODEL_FROM_DATABASE=T540-BCH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005806* + ID_MODEL_FROM_DATABASE=T540-CH Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005807* + ID_MODEL_FROM_DATABASE=T520-SO Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005808* + ID_MODEL_FROM_DATABASE=T520-CX Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005809* + ID_MODEL_FROM_DATABASE=T520-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580A* + ID_MODEL_FROM_DATABASE=T504-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580B* + ID_MODEL_FROM_DATABASE=B520-SR Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580C* + ID_MODEL_FROM_DATABASE=B504-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580D* + ID_MODEL_FROM_DATABASE=T580-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580E* + ID_MODEL_FROM_DATABASE=T540-LP-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000580F* + ID_MODEL_FROM_DATABASE=T540 [Amsterdam] Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005810* + ID_MODEL_FROM_DATABASE=T580-LP-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005811* + ID_MODEL_FROM_DATABASE=T520-LL-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005812* + ID_MODEL_FROM_DATABASE=T560-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005813* + ID_MODEL_FROM_DATABASE=T580-CHR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005814* + ID_MODEL_FROM_DATABASE=T580-SO-CR Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005815* + ID_MODEL_FROM_DATABASE=T502-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005816* + ID_MODEL_FROM_DATABASE=T580-OCP-SO Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005817* + ID_MODEL_FROM_DATABASE=T520-OCP-SO Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005818* + ID_MODEL_FROM_DATABASE=T540-BT Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005880* + ID_MODEL_FROM_DATABASE=T540-5080 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005881* + ID_MODEL_FROM_DATABASE=T540-5081 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005882* + ID_MODEL_FROM_DATABASE=T504-5082 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005883* + ID_MODEL_FROM_DATABASE=T540-5083 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005884* + ID_MODEL_FROM_DATABASE=T580-5084 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005885* + ID_MODEL_FROM_DATABASE=T580-5085 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005886* + ID_MODEL_FROM_DATABASE=T580-5086 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005887* + ID_MODEL_FROM_DATABASE=T580-5087 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005888* + ID_MODEL_FROM_DATABASE=T570-5088 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005889* + ID_MODEL_FROM_DATABASE=T520-5089 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005890* + ID_MODEL_FROM_DATABASE=T540-5090 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005891* + ID_MODEL_FROM_DATABASE=T522-5091 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005892* + ID_MODEL_FROM_DATABASE=T520-5092 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005893* + ID_MODEL_FROM_DATABASE=T580-5093 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005894* + ID_MODEL_FROM_DATABASE=T540-5094 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005895* + ID_MODEL_FROM_DATABASE=T540-5095 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005896* + ID_MODEL_FROM_DATABASE=T580-5096 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005897* + ID_MODEL_FROM_DATABASE=T520-5097 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005898* + ID_MODEL_FROM_DATABASE=T580-5098 Unified Wire Ethernet Controller [VF] + +pci:v00001425d00005899* + ID_MODEL_FROM_DATABASE=T580-5099 Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000589A* + ID_MODEL_FROM_DATABASE=T520-509A Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000589B* + ID_MODEL_FROM_DATABASE=T540-509B Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000589C* + ID_MODEL_FROM_DATABASE=T520-509C Unified Wire Ethernet Controller [VF] + +pci:v00001425d0000A000* + ID_MODEL_FROM_DATABASE=PE10K Unified Wire Ethernet Controller + +pci:v00001426* + ID_VENDOR_FROM_DATABASE=Storage Technology Corp. + +pci:v00001427* + ID_VENDOR_FROM_DATABASE=Better On-Line Solutions + +pci:v00001428* + ID_VENDOR_FROM_DATABASE=Edec Co Ltd + +pci:v00001429* + ID_VENDOR_FROM_DATABASE=Unex Technology Corp. + +pci:v0000142A* + ID_VENDOR_FROM_DATABASE=Kingmax Technology Inc + +pci:v0000142B* + ID_VENDOR_FROM_DATABASE=Radiolan + +pci:v0000142C* + ID_VENDOR_FROM_DATABASE=Minton Optic Industry Co Ltd + +pci:v0000142D* + ID_VENDOR_FROM_DATABASE=Pix stream Inc + +pci:v0000142E* + ID_VENDOR_FROM_DATABASE=Vitec Multimedia + +pci:v0000142Ed00004020* + ID_MODEL_FROM_DATABASE=VM2-2 [Video Maker 2] MPEG1/2 Encoder + +pci:v0000142Ed00004337* + ID_MODEL_FROM_DATABASE=VM2-2-C7 [Video Maker 2 rev. C7] MPEG1/2 Encoder + +pci:v0000142F* + ID_VENDOR_FROM_DATABASE=Radicom Research Inc + +pci:v00001430* + ID_VENDOR_FROM_DATABASE=ITT Aerospace/Communications Division + +pci:v00001431* + ID_VENDOR_FROM_DATABASE=Gilat Satellite Networks + +pci:v00001432* + ID_VENDOR_FROM_DATABASE=Edimax Computer Co. + +pci:v00001432d00009130* + ID_MODEL_FROM_DATABASE=RTL81xx Fast Ethernet + +pci:v00001433* + ID_VENDOR_FROM_DATABASE=Eltec Elektronik GmbH + +pci:v00001435* + ID_VENDOR_FROM_DATABASE=RTD Embedded Technologies, Inc. + +pci:v00001435d00004520* + ID_MODEL_FROM_DATABASE=PCI4520 + +pci:v00001435d00006020* + ID_MODEL_FROM_DATABASE=SPM6020 + +pci:v00001435d00006030* + ID_MODEL_FROM_DATABASE=SPM6030 + +pci:v00001435d00006420* + ID_MODEL_FROM_DATABASE=SPM186420 + +pci:v00001435d00006430* + ID_MODEL_FROM_DATABASE=SPM176430 + +pci:v00001435d00006431* + ID_MODEL_FROM_DATABASE=SPM176431 + +pci:v00001435d00007520* + ID_MODEL_FROM_DATABASE=DM7520 + +pci:v00001435d00007540* + ID_MODEL_FROM_DATABASE=SDM7540 + +pci:v00001435d00007820* + ID_MODEL_FROM_DATABASE=DM7820 + +pci:v00001436* + ID_VENDOR_FROM_DATABASE=CIS Technology Inc + +pci:v00001437* + ID_VENDOR_FROM_DATABASE=Nissin Inc Co + +pci:v00001438* + ID_VENDOR_FROM_DATABASE=Atmel-dream + +pci:v00001439* + ID_VENDOR_FROM_DATABASE=Outsource Engineering & Mfg. Inc + +pci:v0000143A* + ID_VENDOR_FROM_DATABASE=Stargate Solutions Inc + +pci:v0000143B* + ID_VENDOR_FROM_DATABASE=Canon Research Center, America + +pci:v0000143C* + ID_VENDOR_FROM_DATABASE=Amlogic Inc + +pci:v0000143D* + ID_VENDOR_FROM_DATABASE=Tamarack Microelectronics Inc + +pci:v0000143E* + ID_VENDOR_FROM_DATABASE=Jones Futurex Inc + +pci:v0000143F* + ID_VENDOR_FROM_DATABASE=Lightwell Co Ltd - Zax Division + +pci:v00001440* + ID_VENDOR_FROM_DATABASE=ALGOL Corp. + +pci:v00001441* + ID_VENDOR_FROM_DATABASE=AGIE Ltd + +pci:v00001442* + ID_VENDOR_FROM_DATABASE=Phoenix Contact GmbH & Co. + +pci:v00001443* + ID_VENDOR_FROM_DATABASE=Unibrain S.A. + +pci:v00001444* + ID_VENDOR_FROM_DATABASE=TRW + +pci:v00001445* + ID_VENDOR_FROM_DATABASE=Logical DO Ltd + +pci:v00001446* + ID_VENDOR_FROM_DATABASE=Graphin Co Ltd + +pci:v00001447* + ID_VENDOR_FROM_DATABASE=AIM GmBH + +pci:v00001448* + ID_VENDOR_FROM_DATABASE=Alesis Studio Electronics + +pci:v00001449* + ID_VENDOR_FROM_DATABASE=TUT Systems Inc + +pci:v0000144A* + ID_VENDOR_FROM_DATABASE=Adlink Technology + +pci:v0000144Ad00006208* + ID_MODEL_FROM_DATABASE=PCI-6208V + +pci:v0000144Ad00007250* + ID_MODEL_FROM_DATABASE=PCI-7250 + +pci:v0000144Ad00007296* + ID_MODEL_FROM_DATABASE=PCI-7296 + +pci:v0000144Ad00007432* + ID_MODEL_FROM_DATABASE=PCI-7432 + +pci:v0000144Ad00007433* + ID_MODEL_FROM_DATABASE=PCI-7433 + +pci:v0000144Ad00007434* + ID_MODEL_FROM_DATABASE=PCI-7434 + +pci:v0000144Ad00007841* + ID_MODEL_FROM_DATABASE=PCI-7841 + +pci:v0000144Ad00008133* + ID_MODEL_FROM_DATABASE=PCI-8133 + +pci:v0000144Ad00008164* + ID_MODEL_FROM_DATABASE=PCI-8164 + +pci:v0000144Ad00008554* + ID_MODEL_FROM_DATABASE=PCI-8554 + +pci:v0000144Ad00009111* + ID_MODEL_FROM_DATABASE=PCI-9111 + +pci:v0000144Ad00009113* + ID_MODEL_FROM_DATABASE=PCI-9113 + +pci:v0000144Ad00009114* + ID_MODEL_FROM_DATABASE=PCI-9114 + +pci:v0000144Ad0000A001* + ID_MODEL_FROM_DATABASE=ADi-BSEC + +pci:v0000144B* + ID_VENDOR_FROM_DATABASE=Verint Systems Inc. + +pci:v0000144C* + ID_VENDOR_FROM_DATABASE=Catalina Research Inc + +pci:v0000144D* + ID_VENDOR_FROM_DATABASE=Samsung Electronics Co Ltd + +pci:v0000144Dd00001600* + ID_MODEL_FROM_DATABASE=Apple PCIe SSD + +pci:v0000144Dd0000A800* + ID_MODEL_FROM_DATABASE=XP941 PCIe SSD + +pci:v0000144Dd0000A802* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller + +pci:v0000144Dd0000A820* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X + +pci:v0000144Dd0000A820sv00001028sd00001F95* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe XS1715 SSD 400GB) + +pci:v0000144Dd0000A820sv00001028sd00001F96* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe XS1715 SSD 800GB) + +pci:v0000144Dd0000A820sv00001028sd00001F97* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe XS1715 SSD 1600GB) + +pci:v0000144Dd0000A820sv00001028sd00001FA4* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe SM1715 3.2TB SFF) + +pci:v0000144Dd0000A820sv00001028sd00001FA6* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe SM1715 3.2TB AIC) + +pci:v0000144Dd0000A820sv00001028sd00001FBA* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe SM1715 800GB SFF) + +pci:v0000144Dd0000A820sv00001028sd00001FBB* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe SM1715 1.6TB SFF) + +pci:v0000144Dd0000A820sv00001028sd00001FBC* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 171X (Express Flash NVMe SM1715 1.6TB AIC) + +pci:v0000144Dd0000A821* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X + +pci:v0000144Dd0000A821sv00001028sd00001FB7* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 3.2TB SFF) + +pci:v0000144Dd0000A821sv00001028sd00001FB8* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 3.2TB AIC) + +pci:v0000144Dd0000A821sv00001028sd00001FB9* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 6.4TB AIC) + +pci:v0000144Dd0000A821sv00001028sd00001FC1* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 800GB SFF) + +pci:v0000144Dd0000A821sv00001028sd00001FC2* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 1.6TB SFF) + +pci:v0000144Dd0000A821sv00001028sd00001FC4* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172X (Express Flash NVMe PM1725 1.6TB AIC) + +pci:v0000144E* + ID_VENDOR_FROM_DATABASE=OLITEC + +pci:v0000144F* + ID_VENDOR_FROM_DATABASE=Askey Computer Corp. + +pci:v00001450* + ID_VENDOR_FROM_DATABASE=Octave Communications Ind. + +pci:v00001451* + ID_VENDOR_FROM_DATABASE=SP3D Chip Design GmBH + +pci:v00001453* + ID_VENDOR_FROM_DATABASE=MYCOM Inc + +pci:v00001454* + ID_VENDOR_FROM_DATABASE=Altiga Networks + +pci:v00001455* + ID_VENDOR_FROM_DATABASE=Logic Plus Plus Inc + +pci:v00001456* + ID_VENDOR_FROM_DATABASE=Advanced Hardware Architectures + +pci:v00001457* + ID_VENDOR_FROM_DATABASE=Nuera Communications Inc + +pci:v00001458* + ID_VENDOR_FROM_DATABASE=Gigabyte Technology Co., Ltd + +pci:v00001459* + ID_VENDOR_FROM_DATABASE=DOOIN Electronics + +pci:v0000145A* + ID_VENDOR_FROM_DATABASE=Escalate Networks Inc + +pci:v0000145B* + ID_VENDOR_FROM_DATABASE=PRAIM SRL + +pci:v0000145C* + ID_VENDOR_FROM_DATABASE=Cryptek + +pci:v0000145D* + ID_VENDOR_FROM_DATABASE=Gallant Computer Inc + +pci:v0000145E* + ID_VENDOR_FROM_DATABASE=Aashima Technology B.V. + +pci:v0000145F* + ID_VENDOR_FROM_DATABASE=Baldor Electric Company + +pci:v0000145Fd00000001* + ID_MODEL_FROM_DATABASE=NextMove PCI + +pci:v00001460* + ID_VENDOR_FROM_DATABASE=DYNARC INC + +pci:v00001461* + ID_VENDOR_FROM_DATABASE=Avermedia Technologies Inc + +pci:v00001461d0000A3CE* + ID_MODEL_FROM_DATABASE=M179 + +pci:v00001461d0000A3CF* + ID_MODEL_FROM_DATABASE=M179 + +pci:v00001461d0000A836* + ID_MODEL_FROM_DATABASE=M115 DVB-T, PAL/SECAM/NTSC Tuner + +pci:v00001461d0000E836* + ID_MODEL_FROM_DATABASE=M115S Hybrid Analog/DVB PAL/SECAM/NTSC Tuner + +pci:v00001461d0000F436* + ID_MODEL_FROM_DATABASE=AVerTV Hybrid+FM + +pci:v00001462* + ID_VENDOR_FROM_DATABASE=Micro-Star International Co., Ltd. [MSI] + +pci:v00001463* + ID_VENDOR_FROM_DATABASE=Fast Corporation + +pci:v00001464* + ID_VENDOR_FROM_DATABASE=Interactive Circuits & Systems Ltd + +pci:v00001465* + ID_VENDOR_FROM_DATABASE=GN NETTEST Telecom DIV. + +pci:v00001466* + ID_VENDOR_FROM_DATABASE=Designpro Inc. + +pci:v00001467* + ID_VENDOR_FROM_DATABASE=DIGICOM SPA + +pci:v00001468* + ID_VENDOR_FROM_DATABASE=AMBIT Microsystem Corp. + +pci:v00001469* + ID_VENDOR_FROM_DATABASE=Cleveland Motion Controls + +pci:v0000146A* + ID_VENDOR_FROM_DATABASE=Aeroflex + +pci:v0000146Ad00003010* + ID_MODEL_FROM_DATABASE=3010 RF Synthesizer + +pci:v0000146Ad00003A11* + ID_MODEL_FROM_DATABASE=3011A PXI RF Synthesizer + +pci:v0000146B* + ID_VENDOR_FROM_DATABASE=Parascan Technologies Ltd + +pci:v0000146C* + ID_VENDOR_FROM_DATABASE=Ruby Tech Corp. + +pci:v0000146Cd00001430* + ID_MODEL_FROM_DATABASE=FE-1430TX Fast Ethernet PCI Adapter + +pci:v0000146D* + ID_VENDOR_FROM_DATABASE=Tachyon, INC. + +pci:v0000146E* + ID_VENDOR_FROM_DATABASE=Williams Electronics Games, Inc. + +pci:v0000146F* + ID_VENDOR_FROM_DATABASE=Multi Dimensional Consulting Inc + +pci:v00001470* + ID_VENDOR_FROM_DATABASE=Bay Networks + +pci:v00001471* + ID_VENDOR_FROM_DATABASE=Integrated Telecom Express Inc + +pci:v00001472* + ID_VENDOR_FROM_DATABASE=DAIKIN Industries, Ltd + +pci:v00001473* + ID_VENDOR_FROM_DATABASE=ZAPEX Technologies Inc + +pci:v00001474* + ID_VENDOR_FROM_DATABASE=Doug Carson & Associates + +pci:v00001475* + ID_VENDOR_FROM_DATABASE=PICAZO Communications + +pci:v00001476* + ID_VENDOR_FROM_DATABASE=MORTARA Instrument Inc + +pci:v00001477* + ID_VENDOR_FROM_DATABASE=Net Insight + +pci:v00001478* + ID_VENDOR_FROM_DATABASE=DIATREND Corporation + +pci:v00001479* + ID_VENDOR_FROM_DATABASE=TORAY Industries Inc + +pci:v0000147A* + ID_VENDOR_FROM_DATABASE=FORMOSA Industrial Computing + +pci:v0000147B* + ID_VENDOR_FROM_DATABASE=ABIT Computer Corp. + +pci:v0000147Bd00001084* + ID_MODEL_FROM_DATABASE=IP35 [Dark Raider] + +pci:v0000147C* + ID_VENDOR_FROM_DATABASE=AWARE, Inc. + +pci:v0000147D* + ID_VENDOR_FROM_DATABASE=Interworks Computer Products + +pci:v0000147E* + ID_VENDOR_FROM_DATABASE=Matsushita Graphic Communication Systems, Inc. + +pci:v0000147F* + ID_VENDOR_FROM_DATABASE=NIHON UNISYS, Ltd. + +pci:v00001480* + ID_VENDOR_FROM_DATABASE=SCII Telecom + +pci:v00001481* + ID_VENDOR_FROM_DATABASE=BIOPAC Systems Inc + +pci:v00001482* + ID_VENDOR_FROM_DATABASE=ISYTEC - Integrierte Systemtechnik GmBH + +pci:v00001482d00000001* + ID_MODEL_FROM_DATABASE=PCI-16 Host Interface for ITC-16 + +pci:v00001483* + ID_VENDOR_FROM_DATABASE=LABWAY Corporation + +pci:v00001484* + ID_VENDOR_FROM_DATABASE=Logic Corporation + +pci:v00001485* + ID_VENDOR_FROM_DATABASE=ERMA - Electronic GmBH + +pci:v00001486* + ID_VENDOR_FROM_DATABASE=L3 Communications Telemetry & Instrumentation + +pci:v00001487* + ID_VENDOR_FROM_DATABASE=MARQUETTE Medical Systems + +pci:v00001489* + ID_VENDOR_FROM_DATABASE=KYE Systems Corporation + +pci:v0000148A* + ID_VENDOR_FROM_DATABASE=OPTO + +pci:v0000148B* + ID_VENDOR_FROM_DATABASE=INNOMEDIALOGIC Inc. + +pci:v0000148C* + ID_VENDOR_FROM_DATABASE=Tul Corporation / PowerColor + +pci:v0000148D* + ID_VENDOR_FROM_DATABASE=DIGICOM Systems, Inc. + +pci:v0000148Dd00001003* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v0000148E* + ID_VENDOR_FROM_DATABASE=OSI Plus Corporation + +pci:v0000148F* + ID_VENDOR_FROM_DATABASE=Plant Equipment, Inc. + +pci:v00001490* + ID_VENDOR_FROM_DATABASE=Stone Microsystems PTY Ltd. + +pci:v00001491* + ID_VENDOR_FROM_DATABASE=ZEAL Corporation + +pci:v00001492* + ID_VENDOR_FROM_DATABASE=Time Logic Corporation + +pci:v00001493* + ID_VENDOR_FROM_DATABASE=MAKER Communications + +pci:v00001494* + ID_VENDOR_FROM_DATABASE=WINTOP Technology, Inc. + +pci:v00001495* + ID_VENDOR_FROM_DATABASE=TOKAI Communications Industry Co. Ltd + +pci:v00001496* + ID_VENDOR_FROM_DATABASE=JOYTECH Computer Co., Ltd. + +pci:v00001497* + ID_VENDOR_FROM_DATABASE=SMA Regelsysteme GmBH + +pci:v00001497d00001497* + ID_MODEL_FROM_DATABASE=SMA Technologie AG + +pci:v00001498* + ID_VENDOR_FROM_DATABASE=TEWS Technologies GmbH + +pci:v00001498d00000330* + ID_MODEL_FROM_DATABASE=TPMC816 2 Channel CAN bus controller. + +pci:v00001498d0000035D* + ID_MODEL_FROM_DATABASE=TPMC861 4-Channel Isolated Serial Interface RS422/RS485 + +pci:v00001498d00000385* + ID_MODEL_FROM_DATABASE=TPMC901 Extended CAN bus with 2/4/6 CAN controller + +pci:v00001498d000021CC* + ID_MODEL_FROM_DATABASE=TCP460 CompactPCI 16 Channel Serial Interface RS232/RS422 + +pci:v00001498d000021CD* + ID_MODEL_FROM_DATABASE=TCP461 CompactPCI 8 Channel Serial Interface RS232/RS422 + +pci:v00001498d00003064* + ID_MODEL_FROM_DATABASE=TPCI100 (2 Slot IndustryPack PCI Carrier) + +pci:v00001498d000030C8* + ID_MODEL_FROM_DATABASE=TPCI200 4 Slot IndustryPack PCI Carrier + +pci:v00001498d000070C8* + ID_MODEL_FROM_DATABASE=TPCE200 4 Slot IndustryPack PCIe Carrier + +pci:v00001498d00009177* + ID_MODEL_FROM_DATABASE=TXMC375 8 channel RS232/RS422/RS485 programmable serial interface + +pci:v00001499* + ID_VENDOR_FROM_DATABASE=EMTEC CO., Ltd + +pci:v0000149A* + ID_VENDOR_FROM_DATABASE=ANDOR Technology Ltd + +pci:v0000149B* + ID_VENDOR_FROM_DATABASE=SEIKO Instruments Inc + +pci:v0000149C* + ID_VENDOR_FROM_DATABASE=OVISLINK Corp. + +pci:v0000149D* + ID_VENDOR_FROM_DATABASE=NEWTEK Inc + +pci:v0000149Dd00000001* + ID_MODEL_FROM_DATABASE=Video Toaster for PC + +pci:v0000149E* + ID_VENDOR_FROM_DATABASE=Mapletree Networks Inc. + +pci:v0000149F* + ID_VENDOR_FROM_DATABASE=LECTRON Co Ltd + +pci:v000014A0* + ID_VENDOR_FROM_DATABASE=SOFTING GmBH + +pci:v000014A1* + ID_VENDOR_FROM_DATABASE=Systembase Co Ltd + +pci:v000014A2* + ID_VENDOR_FROM_DATABASE=Millennium Engineering Inc + +pci:v000014A3* + ID_VENDOR_FROM_DATABASE=Maverick Networks + +pci:v000014A4* + ID_VENDOR_FROM_DATABASE=Lite-On Technology Corporation + +pci:v000014A4d00004318* + ID_MODEL_FROM_DATABASE=Broadcom BCM4318 [AirForce One 54g] 802.11g WLAN Controller + +pci:v000014A5* + ID_VENDOR_FROM_DATABASE=XIONICS Document Technologies Inc + +pci:v000014A6* + ID_VENDOR_FROM_DATABASE=INOVA Computers GmBH & Co KG + +pci:v000014A7* + ID_VENDOR_FROM_DATABASE=MYTHOS Systems Inc + +pci:v000014A8* + ID_VENDOR_FROM_DATABASE=FEATRON Technologies Corporation + +pci:v000014A9* + ID_VENDOR_FROM_DATABASE=HIVERTEC Inc + +pci:v000014AA* + ID_VENDOR_FROM_DATABASE=Advanced MOS Technology Inc + +pci:v000014AB* + ID_VENDOR_FROM_DATABASE=Mentor Graphics Corp. + +pci:v000014AC* + ID_VENDOR_FROM_DATABASE=Novaweb Technologies Inc + +pci:v000014AD* + ID_VENDOR_FROM_DATABASE=Time Space Radio AB + +pci:v000014AE* + ID_VENDOR_FROM_DATABASE=CTI, Inc + +pci:v000014AF* + ID_VENDOR_FROM_DATABASE=Guillemot Corporation + +pci:v000014AFd00007102* + ID_MODEL_FROM_DATABASE=3D Prophet II MX + +pci:v000014B0* + ID_VENDOR_FROM_DATABASE=BST Communication Technology Ltd + +pci:v000014B1* + ID_VENDOR_FROM_DATABASE=Nextcom K.K. + +pci:v000014B2* + ID_VENDOR_FROM_DATABASE=ENNOVATE Networks Inc + +pci:v000014B3* + ID_VENDOR_FROM_DATABASE=XPEED Inc + +pci:v000014B3d00000000* + ID_MODEL_FROM_DATABASE=DSL NIC + +pci:v000014B4* + ID_VENDOR_FROM_DATABASE=PHILIPS Business Electronics B.V. + +pci:v000014B5* + ID_VENDOR_FROM_DATABASE=Creamware GmBH + +pci:v000014B5d00000200* + ID_MODEL_FROM_DATABASE=Scope + +pci:v000014B5d00000300* + ID_MODEL_FROM_DATABASE=Pulsar + +pci:v000014B5d00000400* + ID_MODEL_FROM_DATABASE=PulsarSRB + +pci:v000014B5d00000600* + ID_MODEL_FROM_DATABASE=Pulsar2 + +pci:v000014B5d00000800* + ID_MODEL_FROM_DATABASE=DSP-Board + +pci:v000014B5d00000900* + ID_MODEL_FROM_DATABASE=DSP-Board + +pci:v000014B5d00000A00* + ID_MODEL_FROM_DATABASE=DSP-Board + +pci:v000014B5d00000B00* + ID_MODEL_FROM_DATABASE=DSP-Board + +pci:v000014B6* + ID_VENDOR_FROM_DATABASE=Quantum Data Corp. + +pci:v000014B7* + ID_VENDOR_FROM_DATABASE=PROXIM Inc + +pci:v000014B7d00000001* + ID_MODEL_FROM_DATABASE=Symphony 4110 + +pci:v000014B8* + ID_VENDOR_FROM_DATABASE=Techsoft Technology Co Ltd + +pci:v000014B9* + ID_VENDOR_FROM_DATABASE=Cisco Aironet Wireless Communications + +pci:v000014B9d00000001* + ID_MODEL_FROM_DATABASE=PC4800 + +pci:v000014B9d00000340* + ID_MODEL_FROM_DATABASE=PC4800 + +pci:v000014B9d00000350* + ID_MODEL_FROM_DATABASE=350 series 802.11b Wireless LAN Adapter + +pci:v000014B9d00004500* + ID_MODEL_FROM_DATABASE=PC4500 + +pci:v000014B9d00004800* + ID_MODEL_FROM_DATABASE=Cisco Aironet 340 802.11b Wireless LAN Adapter/Aironet PC4800 + +pci:v000014B9d0000A504* + ID_MODEL_FROM_DATABASE=Cisco Aironet Wireless 802.11b + +pci:v000014B9d0000A505* + ID_MODEL_FROM_DATABASE=Cisco Aironet CB20a 802.11a Wireless LAN Adapter + +pci:v000014B9d0000A506* + ID_MODEL_FROM_DATABASE=Cisco Aironet Mini PCI b/g + +pci:v000014BA* + ID_VENDOR_FROM_DATABASE=INTERNIX Inc. + +pci:v000014BAd00000600* + ID_MODEL_FROM_DATABASE=ARC-PCI/22 + +pci:v000014BB* + ID_VENDOR_FROM_DATABASE=SEMTECH Corporation + +pci:v000014BC* + ID_VENDOR_FROM_DATABASE=Globespan Semiconductor Inc. + +pci:v000014BCd0000D002* + ID_MODEL_FROM_DATABASE=Pulsar [PCI ADSL Card] + +pci:v000014BCd0000D00F* + ID_MODEL_FROM_DATABASE=Pulsar [PCI ADSL Card] + +pci:v000014BD* + ID_VENDOR_FROM_DATABASE=CARDIO Control N.V. + +pci:v000014BE* + ID_VENDOR_FROM_DATABASE=L3 Communications + +pci:v000014BF* + ID_VENDOR_FROM_DATABASE=SPIDER Communications Inc. + +pci:v000014C0* + ID_VENDOR_FROM_DATABASE=COMPAL Electronics Inc + +pci:v000014C1* + ID_VENDOR_FROM_DATABASE=MYRICOM Inc. + +pci:v000014C1d00000008* + ID_MODEL_FROM_DATABASE=Myri-10G Dual-Protocol NIC + +pci:v000014C1d00000008sv000014C1sd00000008* + ID_MODEL_FROM_DATABASE=Myri-10G Dual-Protocol NIC (10G-PCIE-8A) + +pci:v000014C1d00000008sv000014C1sd00000009* + ID_MODEL_FROM_DATABASE=Myri-10G Dual-Protocol NIC (10G-PCIE-8A (MSI-X firmware)) + +pci:v000014C1d00000008sv000014C1sd0000000A* + ID_MODEL_FROM_DATABASE=Myri-10G Dual-Protocol NIC (10G-PCIE-8B) + +pci:v000014C1d00008043* + ID_MODEL_FROM_DATABASE=Myrinet 2000 Scalable Cluster Interconnect + +pci:v000014C1d00008043sv0000103Csd00001240* + ID_MODEL_FROM_DATABASE=Myrinet 2000 Scalable Cluster Interconnect (Myrinet M2L-PCI64/2-3.0 LANai 7.4 (HP OEM)) + +pci:v000014C2* + ID_VENDOR_FROM_DATABASE=DTK Computer + +pci:v000014C3* + ID_VENDOR_FROM_DATABASE=MEDIATEK Corp. + +pci:v000014C3d00007630* + ID_MODEL_FROM_DATABASE=MT7630e 802.11bgn Wireless Network Adapter + +pci:v000014C3d00007662* + ID_MODEL_FROM_DATABASE=MT7662E 802.11ac PCI Express Wireless Network Adapter + +pci:v000014C4* + ID_VENDOR_FROM_DATABASE=IWASAKI Information Systems Co Ltd + +pci:v000014C5* + ID_VENDOR_FROM_DATABASE=Automation Products AB + +pci:v000014C6* + ID_VENDOR_FROM_DATABASE=Data Race Inc + +pci:v000014C7* + ID_VENDOR_FROM_DATABASE=Modular Technology Holdings Ltd + +pci:v000014C8* + ID_VENDOR_FROM_DATABASE=Turbocomm Tech. Inc. + +pci:v000014C9* + ID_VENDOR_FROM_DATABASE=ODIN Telesystems Inc + +pci:v000014CA* + ID_VENDOR_FROM_DATABASE=PE Logic Corp. + +pci:v000014CB* + ID_VENDOR_FROM_DATABASE=Billionton Systems Inc + +pci:v000014CC* + ID_VENDOR_FROM_DATABASE=NAKAYO Telecommunications Inc + +pci:v000014CD* + ID_VENDOR_FROM_DATABASE=Universal Scientific Ind. + +pci:v000014CE* + ID_VENDOR_FROM_DATABASE=Whistle Communications + +pci:v000014CF* + ID_VENDOR_FROM_DATABASE=TEK Microsystems Inc. + +pci:v000014D0* + ID_VENDOR_FROM_DATABASE=Ericsson Axe R & D + +pci:v000014D1* + ID_VENDOR_FROM_DATABASE=Computer Hi-Tech Co Ltd + +pci:v000014D2* + ID_VENDOR_FROM_DATABASE=Titan Electronics Inc + +pci:v000014D2d00008001* + ID_MODEL_FROM_DATABASE=VScom 010L 1 port parallel adaptor + +pci:v000014D2d00008002* + ID_MODEL_FROM_DATABASE=VScom 020L 2 port parallel adaptor + +pci:v000014D2d00008010* + ID_MODEL_FROM_DATABASE=VScom 100L 1 port serial adaptor + +pci:v000014D2d00008011* + ID_MODEL_FROM_DATABASE=VScom 110L 1 port serial and 1 port parallel adaptor + +pci:v000014D2d00008020* + ID_MODEL_FROM_DATABASE=VScom 200L 1 or 2 port serial adaptor + +pci:v000014D2d00008021* + ID_MODEL_FROM_DATABASE=VScom 210L 2 port serial and 1 port parallel adaptor + +pci:v000014D2d00008028* + ID_MODEL_FROM_DATABASE=VScom 200I/200I-SI 2-port serial adapter + +pci:v000014D2d00008040* + ID_MODEL_FROM_DATABASE=VScom 400L 4 port serial adaptor + +pci:v000014D2d00008043* + ID_MODEL_FROM_DATABASE=VScom 430L 4-port serial and 3-port parallel adapter + +pci:v000014D2d00008048* + ID_MODEL_FROM_DATABASE=VScom 400I 4-port serial adapter + +pci:v000014D2d00008080* + ID_MODEL_FROM_DATABASE=VScom 800L 8 port serial adaptor + +pci:v000014D2d00008088* + ID_MODEL_FROM_DATABASE=VScom 800I 8-port serial adapter + +pci:v000014D2d0000A000* + ID_MODEL_FROM_DATABASE=VScom 010H 1 port parallel adaptor + +pci:v000014D2d0000A001* + ID_MODEL_FROM_DATABASE=VScom 100H 1 port serial adaptor + +pci:v000014D2d0000A003* + ID_MODEL_FROM_DATABASE=VScom 400H 4 port serial adaptor + +pci:v000014D2d0000A004* + ID_MODEL_FROM_DATABASE=VScom 400HF1 4 port serial adaptor + +pci:v000014D2d0000A005* + ID_MODEL_FROM_DATABASE=VScom 200H 2 port serial adaptor + +pci:v000014D2d0000A007* + ID_MODEL_FROM_DATABASE=VScom PCI800EH (PCIe) 8-port serial adapter Port 1-4 + +pci:v000014D2d0000A008* + ID_MODEL_FROM_DATABASE=VScom PCI800EH (PCIe) 8-port serial adapter Port 5-8 + +pci:v000014D2d0000A009* + ID_MODEL_FROM_DATABASE=VScom PCI400EH (PCIe) 4-port serial adapter + +pci:v000014D2d0000E001* + ID_MODEL_FROM_DATABASE=VScom 010HV2 1 port parallel adaptor + +pci:v000014D2d0000E010* + ID_MODEL_FROM_DATABASE=VScom 100HV2 1 port serial adaptor + +pci:v000014D2d0000E020* + ID_MODEL_FROM_DATABASE=VScom 200HV2 2 port serial adaptor + +pci:v000014D3* + ID_VENDOR_FROM_DATABASE=CIRTECH (UK) Ltd + +pci:v000014D4* + ID_VENDOR_FROM_DATABASE=Panacom Technology Corp + +pci:v000014D5* + ID_VENDOR_FROM_DATABASE=Nitsuko Corporation + +pci:v000014D6* + ID_VENDOR_FROM_DATABASE=Accusys Inc + +pci:v000014D6d00006101* + ID_MODEL_FROM_DATABASE=ACS-61xxx, PCIe to SAS/SATA RAID HBA + +pci:v000014D6d00006201* + ID_MODEL_FROM_DATABASE=ACS-62xxx, External PCIe to SAS/SATA RAID controller + +pci:v000014D7* + ID_VENDOR_FROM_DATABASE=Hirakawa Hewtech Corp + +pci:v000014D8* + ID_VENDOR_FROM_DATABASE=HOPF Elektronik GmBH + +pci:v000014D9* + ID_VENDOR_FROM_DATABASE=Alliance Semiconductor Corporation + +pci:v000014D9d00000010* + ID_MODEL_FROM_DATABASE=AP1011/SP1011 HyperTransport-PCI Bridge [Sturgeon] + +pci:v000014D9d00009000* + ID_MODEL_FROM_DATABASE=AS90L10204/10208 HyperTransport to PCI-X Bridge + +pci:v000014DA* + ID_VENDOR_FROM_DATABASE=National Aerospace Laboratories + +pci:v000014DB* + ID_VENDOR_FROM_DATABASE=AFAVLAB Technology Inc + +pci:v000014DBd00002120* + ID_MODEL_FROM_DATABASE=TK9902 + +pci:v000014DBd00002182* + ID_MODEL_FROM_DATABASE=AFAVLAB Technology Inc. 8-port serial card + +pci:v000014DC* + ID_VENDOR_FROM_DATABASE=Amplicon Liveline Ltd + +pci:v000014DCd00000000* + ID_MODEL_FROM_DATABASE=PCI230 + +pci:v000014DCd00000001* + ID_MODEL_FROM_DATABASE=PCI242 + +pci:v000014DCd00000002* + ID_MODEL_FROM_DATABASE=PCI244 + +pci:v000014DCd00000003* + ID_MODEL_FROM_DATABASE=PCI247 + +pci:v000014DCd00000004* + ID_MODEL_FROM_DATABASE=PCI248 + +pci:v000014DCd00000005* + ID_MODEL_FROM_DATABASE=PCI249 + +pci:v000014DCd00000006* + ID_MODEL_FROM_DATABASE=PCI260 + +pci:v000014DCd00000007* + ID_MODEL_FROM_DATABASE=PCI224 + +pci:v000014DCd00000008* + ID_MODEL_FROM_DATABASE=PCI234 + +pci:v000014DCd00000009* + ID_MODEL_FROM_DATABASE=PCI236 + +pci:v000014DCd0000000A* + ID_MODEL_FROM_DATABASE=PCI272 + +pci:v000014DCd0000000B* + ID_MODEL_FROM_DATABASE=PCI215 + +pci:v000014DD* + ID_VENDOR_FROM_DATABASE=Boulder Design Labs Inc + +pci:v000014DE* + ID_VENDOR_FROM_DATABASE=Applied Integration Corporation + +pci:v000014DF* + ID_VENDOR_FROM_DATABASE=ASIC Communications Corp + +pci:v000014E1* + ID_VENDOR_FROM_DATABASE=INVERTEX + +pci:v000014E2* + ID_VENDOR_FROM_DATABASE=INFOLIBRIA + +pci:v000014E3* + ID_VENDOR_FROM_DATABASE=AMTELCO + +pci:v000014E4* + ID_VENDOR_FROM_DATABASE=Broadcom Corporation + +pci:v000014E4d00000576* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n + +pci:v000014E4d00000800* + ID_MODEL_FROM_DATABASE=Sentry5 Chipcommon I/O Controller + +pci:v000014E4d00000804* + ID_MODEL_FROM_DATABASE=Sentry5 PCI Bridge + +pci:v000014E4d00000805* + ID_MODEL_FROM_DATABASE=Sentry5 MIPS32 CPU + +pci:v000014E4d00000806* + ID_MODEL_FROM_DATABASE=Sentry5 Ethernet Controller + +pci:v000014E4d0000080B* + ID_MODEL_FROM_DATABASE=Sentry5 Crypto Accelerator + +pci:v000014E4d0000080F* + ID_MODEL_FROM_DATABASE=Sentry5 DDR/SDR RAM Controller + +pci:v000014E4d00000811* + ID_MODEL_FROM_DATABASE=Sentry5 External Interface Core + +pci:v000014E4d00000816* + ID_MODEL_FROM_DATABASE=BCM3302 Sentry5 MIPS32 CPU + +pci:v000014E4d00001570* + ID_MODEL_FROM_DATABASE=720p FaceTime HD Camera + +pci:v000014E4d00001600* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express + +pci:v000014E4d00001600sv00001028sd000001C1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express (Precision 490) + +pci:v000014E4d00001600sv00001028sd000001C2* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express (Latitude D620) + +pci:v000014E4d00001600sv0000103Csd00003015* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express (PCIe LAN on Motherboard) + +pci:v000014E4d00001600sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express (E4500 Onboard) + +pci:v000014E4d00001600sv00001259sd00002705* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752 Gigabit Ethernet PCI Express (AT-2711FX) + +pci:v000014E4d00001601* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5752M Gigabit Ethernet PCI Express + +pci:v000014E4d00001612* + ID_MODEL_FROM_DATABASE=BCM70012 Video Decoder [Crystal HD] + +pci:v000014E4d00001615* + ID_MODEL_FROM_DATABASE=BCM70015 Video Decoder [Crystal HD] + +pci:v000014E4d00001639* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet + +pci:v000014E4d00001639sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (PowerEdge R710 BCM5709 Gigabit Ethernet) + +pci:v000014E4d00001639sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (PowerEdge R610 BCM5709 Gigabit Ethernet) + +pci:v000014E4d00001639sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (PowerEdge T610 BCM5709 Gigabit Ethernet) + +pci:v000014E4d00001639sv0000103Csd00007055* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (NC382i Integrated Multi-port PCI Express Gigabit Server Adapter) + +pci:v000014E4d00001639sv0000103Csd00007059* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (NC382T PCIe Dual Port Multifunction Gigabit Server Adapter) + +pci:v000014E4d00001639sv000010A9sd00008027* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709 Gigabit Ethernet (Quad port Gigabit Ethernet Controller) + +pci:v000014E4d0000163A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet + +pci:v000014E4d0000163Asv00001028sd0000027B* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet (PowerEdge M805 Broadcom NetXtreme II BCM5709S) + +pci:v000014E4d0000163Asv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet (PowerEdge M710 BCM5709S Gigabit Ethernet) + +pci:v000014E4d0000163Asv0000103Csd0000171D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet (NC382m Dual Port 1GbE Multifunction BL-c Adapter) + +pci:v000014E4d0000163Asv0000103Csd00007056* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet (NC382i Integrated Quad Port PCI Express Gigabit Server Adapter) + +pci:v000014E4d0000163Asv00001259sd00002984* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5709S Gigabit Ethernet (AT-2973SX) + +pci:v000014E4d0000163B* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5716 Gigabit Ethernet + +pci:v000014E4d0000163Bsv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5716 Gigabit Ethernet (PowerEdge R410 BCM5716 Gigabit Ethernet) + +pci:v000014E4d0000163Bsv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5716 Gigabit Ethernet (PowerEdge T410 BCM5716 Gigabit Ethernet) + +pci:v000014E4d0000163Bsv00001028sd000002F1* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5716 Gigabit Ethernet (PowerEdge R510 BCM5716 Gigabit Ethernet) + +pci:v000014E4d0000163C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5716S Gigabit Ethernet + +pci:v000014E4d0000163D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57811 10-Gigabit Ethernet + +pci:v000014E4d0000163E* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57811 10 Gigabit Ethernet Multi Function + +pci:v000014E4d0000163F* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57811 10-Gigabit Ethernet Virtual Function + +pci:v000014E4d00001641* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57787 Gigabit Ethernet PCIe + +pci:v000014E4d00001642* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57764 Gigabit Ethernet PCIe + +pci:v000014E4d00001643* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5725 Gigabit Ethernet PCIe + +pci:v000014E4d00001644* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet + +pci:v000014E4d00001644sv00001014sd00000277* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (Broadcom Vigil B5700 1000Base-T) + +pci:v000014E4d00001644sv00001028sd000000D1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (Broadcom BCM5700) + +pci:v000014E4d00001644sv00001028sd00000106* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (Broadcom BCM5700) + +pci:v000014E4d00001644sv00001028sd00000109* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (Broadcom BCM5700 1000Base-T) + +pci:v000014E4d00001644sv00001028sd0000010A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (Broadcom BCM5700 1000BaseTX) + +pci:v000014E4d00001644sv000010B7sd00001000* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C996-T 1000Base-T) + +pci:v000014E4d00001644sv000010B7sd00001001* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C996B-T 1000Base-T) + +pci:v000014E4d00001644sv000010B7sd00001002* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C996C-T 1000Base-T) + +pci:v000014E4d00001644sv000010B7sd00001003* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C997-T 1000Base-T Dual Port) + +pci:v000014E4d00001644sv000010B7sd00001004* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C996-SX 1000Base-SX) + +pci:v000014E4d00001644sv000010B7sd00001005* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C997-SX 1000Base-SX Dual Port) + +pci:v000014E4d00001644sv000010B7sd00001008* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (3C942 Gigabit LOM (31X31)) + +pci:v000014E4d00001644sv000014E4sd00000002* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (NetXtreme 1000Base-SX) + +pci:v000014E4d00001644sv000014E4sd00000003* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (NetXtreme 1000Base-SX) + +pci:v000014E4d00001644sv000014E4sd00000004* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (NetXtreme 1000Base-T) + +pci:v000014E4d00001644sv000014E4sd00001028* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (NetXtreme 1000BaseTX) + +pci:v000014E4d00001644sv000014E4sd00001644* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5700 Gigabit Ethernet (BCM5700 1000Base-T) + +pci:v000014E4d00001645* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet + +pci:v000014E4d00001645sv00000E11sd0000007C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC7770 Gigabit Server Adapter (PCI-X, 10/100/1000-T)) + +pci:v000014E4d00001645sv00000E11sd0000007D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC6770 Gigabit Server Adapter (PCI-X, 1000-SX)) + +pci:v000014E4d00001645sv00000E11sd00000085* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC7780 Gigabit Server Adapter (embedded, WOL)) + +pci:v000014E4d00001645sv00000E11sd00000099* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC7780 Gigabit Server Adapter (embedded, WOL)) + +pci:v000014E4d00001645sv00000E11sd0000009A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC7770 Gigabit Server Adapter (PCI-X, 10/100/1000-T)) + +pci:v000014E4d00001645sv00000E11sd000000C1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (NC6770 Gigabit Server Adapter (PCI-X, 1000-SX)) + +pci:v000014E4d00001645sv00001028sd00000121* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (Broadcom BCM5701 1000Base-T) + +pci:v000014E4d00001645sv0000103Csd0000128A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T (HP, OEM 3COM)) + +pci:v000014E4d00001645sv0000103Csd0000128B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (1000Base-SX (PCI) [A7073A]) + +pci:v000014E4d00001645sv0000103Csd000012A4* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (Core Lan 1000Base-T) + +pci:v000014E4d00001645sv0000103Csd000012C1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (IOX Core Lan 1000Base-T [A7109AX]) + +pci:v000014E4d00001645sv0000103Csd00001300* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (Core LAN/SCSI Combo [A6794A]) + +pci:v000014E4d00001645sv000010A9sd00008010* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (IO9/IO10 Gigabit Ethernet (Copper)) + +pci:v000014E4d00001645sv000010A9sd00008011* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (Gigabit Ethernet (Copper)) + +pci:v000014E4d00001645sv000010A9sd00008012* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (Gigabit Ethernet (Fiber)) + +pci:v000014E4d00001645sv000010B7sd00001004* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (3C996-SX 1000Base-SX) + +pci:v000014E4d00001645sv000010B7sd00001006* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (3C996B-T 1000Base-T) + +pci:v000014E4d00001645sv000010B7sd00001007* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (3C1000-T 1000Base-T) + +pci:v000014E4d00001645sv000010B7sd00001008* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (3C940-BR01 1000Base-T) + +pci:v000014E4d00001645sv000014E4sd00000001* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T) + +pci:v000014E4d00001645sv000014E4sd00000005* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T) + +pci:v000014E4d00001645sv000014E4sd00000006* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T) + +pci:v000014E4d00001645sv000014E4sd00000007* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-SX) + +pci:v000014E4d00001645sv000014E4sd00000008* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T) + +pci:v000014E4d00001645sv000014E4sd00001645* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet + +pci:v000014E4d00001645sv000014E4sd00008008* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5701 Gigabit Ethernet (BCM5701 1000Base-T) + +pci:v000014E4d00001646* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702 Gigabit Ethernet + +pci:v000014E4d00001646sv00000E11sd000000BB* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702 Gigabit Ethernet (NC7760 1000BaseTX) + +pci:v000014E4d00001646sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702 Gigabit Ethernet (Broadcom BCM5702 1000BaseTX) + +pci:v000014E4d00001646sv000014E4sd00008009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702 Gigabit Ethernet (BCM5702 1000BaseTX) + +pci:v000014E4d00001647* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet + +pci:v000014E4d00001647sv00000E11sd00000099* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NC7780 1000BaseTX) + +pci:v000014E4d00001647sv00000E11sd0000009A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NC7770 1000BaseTX) + +pci:v000014E4d00001647sv000010A9sd00008010* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (IO9 Gigabit Ethernet (Copper)) + +pci:v000014E4d00001647sv000014E4sd00000009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (BCM5703 1000BaseTX) + +pci:v000014E4d00001647sv000014E4sd0000000A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (BCM5703 1000BaseSX) + +pci:v000014E4d00001647sv000014E4sd0000000B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (BCM5703 1000BaseTX) + +pci:v000014E4d00001647sv000014E4sd00008009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (BCM5703 1000BaseTX) + +pci:v000014E4d00001647sv000014E4sd0000800A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (BCM5703 1000BaseTX) + +pci:v000014E4d00001648* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet + +pci:v000014E4d00001648sv00000E11sd000000CF* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (NC7772 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d00001648sv00000E11sd000000D0* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (NC7782 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d00001648sv00000E11sd000000D1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (NC7783 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d00001648sv00001028sd0000014A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (PowerEdge 1750) + +pci:v000014E4d00001648sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (PowerEdge 6850 Broadcom NetXtreme BCM5704) + +pci:v000014E4d00001648sv0000103Csd0000310F* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (NC7782 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d00001648sv000010A9sd00008013* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (Dual Port Gigabit Ethernet (PCI-X,Copper)) + +pci:v000014E4d00001648sv000010A9sd00008018* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (Dual Port Gigabit Ethernet (A330)) + +pci:v000014E4d00001648sv000010A9sd0000801A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (Dual Port Gigabit Ethernet (IA-blade)) + +pci:v000014E4d00001648sv000010A9sd0000801B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (Quad Port Gigabit Ethernet (PCI-E,Copper)) + +pci:v000014E4d00001648sv000010B7sd00002000* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (3C998-T Dual Port 10/100/1000 PCI-X) + +pci:v000014E4d00001648sv000010B7sd00003000* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (3C999-T Quad Port 10/100/1000 PCI-X) + +pci:v000014E4d00001648sv00001166sd00001648* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (NetXtreme CIOB-E 1000Base-T) + +pci:v000014E4d00001648sv00001734sd0000100B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704 Gigabit Ethernet (PRIMERGY RX/TX series onboard LAN) + +pci:v000014E4d00001649* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S_2 Gigabit Ethernet + +pci:v000014E4d0000164A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706 Gigabit Ethernet + +pci:v000014E4d0000164Asv0000103Csd00001709* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706 Gigabit Ethernet (NC371i Integrated PCI-X Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164Asv0000103Csd00003070* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706 Gigabit Ethernet (NC380T PCI Express Dual Port Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164Asv0000103Csd00003101* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706 Gigabit Ethernet (NC370T MultifuNCtion Gigabit Server Adapter) + +pci:v000014E4d0000164Asv0000103Csd00003106* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706 Gigabit Ethernet (NC370i Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet + +pci:v000014E4d0000164Csv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge R900 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv00001028sd00000205* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge 2970 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv00001028sd0000020B* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge T605 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv00001028sd00000221* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge R805 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv00001028sd00000223* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge R905 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv00001028sd00001F12* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (PowerEdge R805/R905 Broadcom NetXtreme II BCM5708) + +pci:v000014E4d0000164Csv0000103Csd00007037* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (NC373T PCI Express Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164Csv0000103Csd00007038* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (NC373i Integrated Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164Csv0000103Csd00007045* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708 Gigabit Ethernet (NC374m PCI Express Dual Port Multifunction Gigabit Server Adapter) + +pci:v000014E4d0000164D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702FE Gigabit Ethernet + +pci:v000014E4d0000164E* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57710 10-Gigabit PCIe [Everest] + +pci:v000014E4d0000164Esv0000103Csd0000171C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57710 10-Gigabit PCIe [Everest] (NC532m Dual Port 10GbE Multifunction BL-C Adapter) + +pci:v000014E4d0000164Esv0000103Csd00007058* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57710 10-Gigabit PCIe [Everest] (NC532i Dual Port 10GbE Multifunction BL-C Adapter) + +pci:v000014E4d0000164F* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57711 10-Gigabit PCIe + +pci:v000014E4d00001650* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57711E 10-Gigabit PCIe + +pci:v000014E4d00001650sv0000103Csd0000171C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57711E 10-Gigabit PCIe (NC532m Dual Port 10GbE Multifunction BL-C Adapter) + +pci:v000014E4d00001650sv0000103Csd00007058* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57711E 10-Gigabit PCIe (NC532i Dual Port 10GbE Multifunction BL-C Adapter) + +pci:v000014E4d00001653* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705 Gigabit Ethernet + +pci:v000014E4d00001653sv00000E11sd000000E3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705 Gigabit Ethernet (NC7761 Gigabit Server Adapter) + +pci:v000014E4d00001653sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705 Gigabit Ethernet (Primergy Econel 200 D2020 mainboard) + +pci:v000014E4d00001654* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705_2 Gigabit Ethernet + +pci:v000014E4d00001654sv00000E11sd000000E3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705_2 Gigabit Ethernet (NC7761 Gigabit Server Adapter) + +pci:v000014E4d00001654sv0000103Csd00003100* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705_2 Gigabit Ethernet (NC1020 ProLiant Gigabit Server Adapter 32 PCI) + +pci:v000014E4d00001654sv0000103Csd00003226* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705_2 Gigabit Ethernet (NC150T 4-port Gigabit Combo Switch & Adapter) + +pci:v000014E4d00001655* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5717 Gigabit Ethernet PCIe + +pci:v000014E4d00001656* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5718 Gigabit Ethernet PCIe + +pci:v000014E4d00001657* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5719 Gigabit Ethernet PCIe + +pci:v000014E4d00001657sv0000103Csd0000169D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5719 Gigabit Ethernet PCIe (Ethernet 1Gb 4-port 331FLR Adapter) + +pci:v000014E4d00001657sv0000103Csd000022BE* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5719 Gigabit Ethernet PCIe (Ethernet 1Gb 4-port 331i Adapter) + +pci:v000014E4d00001657sv0000103Csd00003383* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5719 Gigabit Ethernet PCIe (Ethernet 1Gb 4-port 331T Adapter) + +pci:v000014E4d00001659* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express + +pci:v000014E4d00001659sv00001014sd000002C6* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (eServer xSeries server mainboard) + +pci:v000014E4d00001659sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (PowerEdge 860) + +pci:v000014E4d00001659sv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (PowerEdge R200 Broadcom NetXtreme BCM5721) + +pci:v000014E4d00001659sv0000103Csd0000170B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (NC320m PCI Express Dual Port Gigabit Server Adapter) + +pci:v000014E4d00001659sv0000103Csd00007031* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (NC320T PCIe Gigabit Server Adapter) + +pci:v000014E4d00001659sv0000103Csd00007032* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (NC320i PCIe Gigabit Server Adapter) + +pci:v000014E4d00001659sv00001734sd00001061* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5721 Gigabit Ethernet PCI Express (PRIMERGY RX/TX S2 series onboard LAN) + +pci:v000014E4d0000165A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express + +pci:v000014E4d0000165Asv00001014sd00000378* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (IBM System x3350 (Machine type 4192)) + +pci:v000014E4d0000165Asv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (PowerEdge R300 Broadcom NetXtreme 5722) + +pci:v000014E4d0000165Asv00001028sd00000210* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (PowerEdge T300 Broadcom NetXtreme 5722) + +pci:v000014E4d0000165Asv00001028sd00000225* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (PowerEdge T105 Broadcom NetXtreme 5722) + +pci:v000014E4d0000165Asv0000103Csd00007051* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (NC105i PCIe Gigabit Server Adapter) + +pci:v000014E4d0000165Asv0000103Csd00007052* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5722 Gigabit Ethernet PCI Express (NC105T PCIe Gigabit Server Adapter) + +pci:v000014E4d0000165B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5723 Gigabit Ethernet PCIe + +pci:v000014E4d0000165Bsv0000103Csd0000705D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5723 Gigabit Ethernet PCIe (NC107i Integrated PCI Express Gigabit Server Adapter) + +pci:v000014E4d0000165C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5724 Gigabit Ethernet PCIe + +pci:v000014E4d0000165D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M Gigabit Ethernet + +pci:v000014E4d0000165Dsv00001028sd0000865D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M Gigabit Ethernet (Latitude D400) + +pci:v000014E4d0000165Dsv000014E4sd0000165D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M Gigabit Ethernet (Dell Latitude D600) + +pci:v000014E4d0000165E* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet + +pci:v000014E4d0000165Esv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet (NC8000 laptop) + +pci:v000014E4d0000165Esv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet (NC6000 laptop) + +pci:v000014E4d0000165Esv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet (NX6110/NC6120) + +pci:v000014E4d0000165Esv000010CFsd00001279* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5705M_2 Gigabit Ethernet (LifeBook E8010D) + +pci:v000014E4d0000165F* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 Gigabit Ethernet PCIe + +pci:v000014E4d00001662* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57712 10 Gigabit Ethernet + +pci:v000014E4d00001663* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function + +pci:v000014E4d00001665* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5717 Gigabit Ethernet PCIe + +pci:v000014E4d00001668* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5714 Gigabit Ethernet + +pci:v000014E4d00001668sv0000103Csd00007039* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5714 Gigabit Ethernet (NC324i PCIe Dual Port Gigabit Server Adapter) + +pci:v000014E4d00001669* + ID_MODEL_FROM_DATABASE=NetXtreme 5714S Gigabit Ethernet + +pci:v000014E4d0000166A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5780 Gigabit Ethernet + +pci:v000014E4d0000166Asv0000103Csd00007035* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5780 Gigabit Ethernet (NC325i Integrated Dual port PCIe Express Gigabit Server Adapter) + +pci:v000014E4d0000166B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5780S Gigabit Ethernet + +pci:v000014E4d0000166E* + ID_MODEL_FROM_DATABASE=570x 10/100 Integrated Controller + +pci:v000014E4d0000166F* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57712 10 Gigabit Ethernet Virtual Function + +pci:v000014E4d00001672* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754M Gigabit Ethernet PCI Express + +pci:v000014E4d00001673* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5755M Gigabit Ethernet PCI Express + +pci:v000014E4d00001674* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5756ME Gigabit Ethernet PCI Express + +pci:v000014E4d00001677* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express + +pci:v000014E4d00001677sv00001028sd00000176* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Dimension XPS Gen 4) + +pci:v000014E4d00001677sv00001028sd00000177* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Dimension 8400) + +pci:v000014E4d00001677sv00001028sd00000179* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Optiplex GX280) + +pci:v000014E4d00001677sv00001028sd00000182* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Latitude D610) + +pci:v000014E4d00001677sv00001028sd00000187* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Precision M70) + +pci:v000014E4d00001677sv00001028sd000001A8* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Precision 380) + +pci:v000014E4d00001677sv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (OptiPlex GX620) + +pci:v000014E4d00001677sv0000103Csd00003006* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (DC7100 SFF(DX878AV)) + +pci:v000014E4d00001677sv00001462sd0000028C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (915P/G Neo2) + +pci:v000014E4d00001677sv00001734sd0000105D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751 Gigabit Ethernet PCI Express (Scenic W620) + +pci:v000014E4d00001678* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715 Gigabit Ethernet + +pci:v000014E4d00001678sv0000103Csd0000703E* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715 Gigabit Ethernet (NC326i PCIe Dual Port Gigabit Server Adapter) + +pci:v000014E4d00001679* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715S Gigabit Ethernet + +pci:v000014E4d00001679sv0000103Csd00001707* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715S Gigabit Ethernet (NC326m PCIe Dual Port Adapter) + +pci:v000014E4d00001679sv0000103Csd0000170C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715S Gigabit Ethernet (NC325m PCIe Quad Port Adapter) + +pci:v000014E4d00001679sv0000103Csd0000703C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5715S Gigabit Ethernet (NC326i PCIe Dual Port Gigabit Server Adapter) + +pci:v000014E4d0000167A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express + +pci:v000014E4d0000167Asv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express (OptiPlex 745) + +pci:v000014E4d0000167Asv00001028sd000001DE* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express (Precision 390) + +pci:v000014E4d0000167Asv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express (PowerEdge SC440) + +pci:v000014E4d0000167Asv00001028sd00000214* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express (Precision T3400) + +pci:v000014E4d0000167Asv00001028sd0000021E* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5754 Gigabit Ethernet PCI Express (Precision T5400) + +pci:v000014E4d0000167B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5755 Gigabit Ethernet PCI Express + +pci:v000014E4d0000167Bsv0000103Csd0000280A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5755 Gigabit Ethernet PCI Express (DC5750 Microtower) + +pci:v000014E4d0000167D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751M Gigabit Ethernet PCI Express + +pci:v000014E4d0000167Dsv00001014sd00000577* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751M Gigabit Ethernet PCI Express (ThinkPad X41 / Z60t) + +pci:v000014E4d0000167Dsv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751M Gigabit Ethernet PCI Express (nx8220) + +pci:v000014E4d0000167Dsv0000103Csd00000940* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751M Gigabit Ethernet PCI Express (Compaq nw8240 Mobile Workstation) + +pci:v000014E4d0000167Dsv000017AAsd00002081* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751M Gigabit Ethernet PCI Express (ThinkPad R60e) + +pci:v000014E4d0000167E* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5751F Fast Ethernet PCI Express + +pci:v000014E4d0000167F* + ID_MODEL_FROM_DATABASE=NetLink BCM5787F Fast Ethernet PCI Express + +pci:v000014E4d00001680* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5761e Gigabit Ethernet PCIe + +pci:v000014E4d00001681* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5761 Gigabit Ethernet PCIe + +pci:v000014E4d00001682* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57762 Gigabit Ethernet PCIe + +pci:v000014E4d00001683* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57767 Gigabit Ethernet PCIe + +pci:v000014E4d00001684* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5764M Gigabit Ethernet PCIe + +pci:v000014E4d00001685* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57500S Gigabit Ethernet + +pci:v000014E4d00001686* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57766 Gigabit Ethernet PCIe + +pci:v000014E4d00001687* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5762 Gigabit Ethernet PCIe + +pci:v000014E4d00001688* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5761 10/100/1000BASE-T Ethernet + +pci:v000014E4d00001688sv00001259sd00002708* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5761 10/100/1000BASE-T Ethernet (AT-2712 FX) + +pci:v000014E4d0000168A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet + +pci:v000014E4d0000168Asv00001028sd00001F5C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (BCM57800 10-Gigabit Ethernet) + +pci:v000014E4d0000168Asv00001028sd00001F5D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (BCM57800 10-Gigabit Ethernet) + +pci:v000014E4d0000168Asv00001028sd00001F67* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (BCM57800 1-Gigabit Ethernet) + +pci:v000014E4d0000168Asv00001028sd00001F68* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (BCM57800 1-Gigabit Ethernet) + +pci:v000014E4d0000168D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet + +pci:v000014E4d0000168E* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet + +pci:v000014E4d0000168Esv0000103Csd00001798* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (Flex-10 10Gb 2-port 530FLB Adapter [Meru]) + +pci:v000014E4d0000168Esv0000103Csd000017A5* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (Flex-10 10Gb 2-port 530M Adapter) + +pci:v000014E4d0000168Esv0000103Csd000018D3* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (Ethernet 10Gb 2-port 530T Adapter) + +pci:v000014E4d0000168Esv0000103Csd00001930* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (FlexFabric 10Gb 2-port 534FLR-SFP+ Adapter) + +pci:v000014E4d0000168Esv0000103Csd00001931* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (StoreFabric CN1100R Dual Port Converged Network Adapter) + +pci:v000014E4d0000168Esv0000103Csd00001932* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (FlexFabric 10Gb 2-port 534FLB Adapter) + +pci:v000014E4d0000168Esv0000103Csd00001933* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (FlexFabric 10Gb 2-port 534M Adapter) + +pci:v000014E4d0000168Esv0000103Csd0000193A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (FlexFabric 10Gb 2-port 533FLR-T Adapter) + +pci:v000014E4d0000168Esv0000103Csd00003382* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (Ethernet 10Gb 2-port 530FLR-SFP+ Adapter) + +pci:v000014E4d0000168Esv0000103Csd0000339D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet (Ethernet 10Gb 2-port 530SFP+ Adapter) + +pci:v000014E4d00001690* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57760 Gigabit Ethernet PCIe + +pci:v000014E4d00001691* + ID_MODEL_FROM_DATABASE=NetLink BCM57788 Gigabit Ethernet PCIe + +pci:v000014E4d00001691sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=NetLink BCM57788 Gigabit Ethernet PCIe (XPS 8300) + +pci:v000014E4d00001692* + ID_MODEL_FROM_DATABASE=NetLink BCM57780 Gigabit Ethernet PCIe + +pci:v000014E4d00001692sv00001025sd0000033D* + ID_MODEL_FROM_DATABASE=NetLink BCM57780 Gigabit Ethernet PCIe (Aspire 7740G) + +pci:v000014E4d00001693* + ID_MODEL_FROM_DATABASE=NetLink BCM5787M Gigabit Ethernet PCI Express + +pci:v000014E4d00001693sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=NetLink BCM5787M Gigabit Ethernet PCI Express (Aspire 5920G) + +pci:v000014E4d00001693sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=NetLink BCM5787M Gigabit Ethernet PCI Express (6710b) + +pci:v000014E4d00001694* + ID_MODEL_FROM_DATABASE=NetLink BCM57790 Gigabit Ethernet PCIe + +pci:v000014E4d00001696* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5782 Gigabit Ethernet + +pci:v000014E4d00001696sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5782 Gigabit Ethernet (d530 CMT (DG746A)) + +pci:v000014E4d00001696sv000014E4sd0000000D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5782 Gigabit Ethernet (NetXtreme BCM5782 1000Base-T) + +pci:v000014E4d00001698* + ID_MODEL_FROM_DATABASE=NetLink BCM5784M Gigabit Ethernet PCIe + +pci:v000014E4d00001699* + ID_MODEL_FROM_DATABASE=NetLink BCM5785 Gigabit Ethernet + +pci:v000014E4d0000169A* + ID_MODEL_FROM_DATABASE=NetLink BCM5786 Gigabit Ethernet PCI Express + +pci:v000014E4d0000169B* + ID_MODEL_FROM_DATABASE=NetLink BCM5787 Gigabit Ethernet PCI Express + +pci:v000014E4d0000169C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5788 Gigabit Ethernet + +pci:v000014E4d0000169Csv0000103Csd0000308B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5788 Gigabit Ethernet (MX6125) + +pci:v000014E4d0000169Csv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5788 Gigabit Ethernet (NC2400) + +pci:v000014E4d0000169Csv0000144Dsd0000C018* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5788 Gigabit Ethernet (X20) + +pci:v000014E4d0000169Csv00001462sd0000590C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5788 Gigabit Ethernet (KT6 Delta-FIS2R (MS-6590)) + +pci:v000014E4d0000169D* + ID_MODEL_FROM_DATABASE=NetLink BCM5789 Gigabit Ethernet PCI Express + +pci:v000014E4d000016A0* + ID_MODEL_FROM_DATABASE=NetLink BCM5785 Fast Ethernet + +pci:v000014E4d000016A1* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10 Gigabit Ethernet + +pci:v000014E4d000016A2* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet + +pci:v000014E4d000016A2sv0000103Csd00001916* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (FlexFabric 20Gb 2-port 630FLB Adapter) + +pci:v000014E4d000016A2sv0000103Csd00001917* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (FlexFabric 20Gb 2-port 630M Adapter) + +pci:v000014E4d000016A2sv0000103Csd00002231* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (3820C 10/20Gb Converged Network Adapter) + +pci:v000014E4d000016A2sv0000103Csd000022FA* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II 10/20-Gigabit Ethernet (FlexFabric 10Gb 2-port 536FLB Adapter) + +pci:v000014E4d000016A3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57786 Gigabit Ethernet PCIe + +pci:v000014E4d000016A4* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function + +pci:v000014E4d000016A4sv0000103Csd00001916* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (NPAR 20Gb 2-port 630FLB Adapter) + +pci:v000014E4d000016A4sv0000103Csd00001917* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (NPAR 20Gb 2-port 630M Adapter) + +pci:v000014E4d000016A4sv0000103Csd00002231* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (3820C 10/20Gb Converged Network Adapter (NPAR 1.5)) + +pci:v000014E4d000016A4sv0000103Csd000022FA* + ID_MODEL_FROM_DATABASE=BCM57840 NetXtreme II Ethernet Multi Function (FlexFabric 10Gb 2-port 536FLB Adapter (NPAR 1.5)) + +pci:v000014E4d000016A5* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function + +pci:v000014E4d000016A5sv00001028sd00001F5C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function (NetXtreme II BCM57800 10-Gigabit Ethernet Multi Function) + +pci:v000014E4d000016A5sv00001028sd00001F5D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function (NetXtreme II BCM57800 10-Gigabit Ethernet Multi Function) + +pci:v000014E4d000016A5sv00001028sd00001F67* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function (NetXtreme II BCM57800 1-Gigabit Ethernet Multi Function) + +pci:v000014E4d000016A5sv00001028sd00001F68* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Multi Function (NetXtreme II BCM57800 1-Gigabit Ethernet Multi Function) + +pci:v000014E4d000016A6* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702X Gigabit Ethernet + +pci:v000014E4d000016A6sv00000E11sd000000BB* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702X Gigabit Ethernet (NC7760 Gigabit Server Adapter (PCI-X, 10/100/1000-T)) + +pci:v000014E4d000016A6sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702X Gigabit Ethernet (BCM5702 1000Base-T) + +pci:v000014E4d000016A6sv000014E4sd0000000C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702X Gigabit Ethernet (BCM5702 1000Base-T) + +pci:v000014E4d000016A6sv000014E4sd00008009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702X Gigabit Ethernet (BCM5702 1000Base-T) + +pci:v000014E4d000016A7* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet + +pci:v000014E4d000016A7sv00000E11sd000000CA* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NC7771 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d000016A7sv00000E11sd000000CB* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NC7781 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d000016A7sv00001014sd0000026F* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (eServer xSeries server mainboard) + +pci:v000014E4d000016A7sv000014E4sd00000009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NetXtreme BCM5703 1000Base-T) + +pci:v000014E4d000016A7sv000014E4sd0000000A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NetXtreme BCM5703 1000Base-SX) + +pci:v000014E4d000016A7sv000014E4sd0000000B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NetXtreme BCM5703 1000Base-T) + +pci:v000014E4d000016A7sv000014E4sd0000800A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703X Gigabit Ethernet (NetXtreme BCM5703 1000Base-T) + +pci:v000014E4d000016A8* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S Gigabit Ethernet + +pci:v000014E4d000016A8sv0000103Csd0000132B* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S Gigabit Ethernet (PCI-X 1000Mbps Dual-port Built-in) + +pci:v000014E4d000016A8sv000010A9sd00008014* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S Gigabit Ethernet (Dual Port Gigabit Ethernet (PCI-X,Fiber)) + +pci:v000014E4d000016A8sv000010A9sd0000801C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S Gigabit Ethernet (Quad Port Gigabit Ethernet (PCI-E,Fiber)) + +pci:v000014E4d000016A8sv000010B7sd00002001* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5704S Gigabit Ethernet (3C998-SX Dual Port 1000-SX PCI-X) + +pci:v000014E4d000016A9* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function + +pci:v000014E4d000016A9sv00001028sd00001F5C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function (NetXtreme II BCM57800 10-Gigabit Ethernet Virtual Function) + +pci:v000014E4d000016A9sv00001028sd00001F5D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function (NetXtreme II BCM57800 10-Gigabit Ethernet Virtual Function) + +pci:v000014E4d000016A9sv00001028sd00001F67* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function (NetXtreme II BCM57800 1-Gigabit Ethernet Virtual Function) + +pci:v000014E4d000016A9sv00001028sd00001F68* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet Virtual Function (NetXtreme II BCM57800 1-Gigabit Ethernet Virtual Function) + +pci:v000014E4d000016AA* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706S Gigabit Ethernet + +pci:v000014E4d000016AAsv0000103Csd00003102* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706S Gigabit Ethernet (NC370F MultifuNCtion Gigabit Server Adapter) + +pci:v000014E4d000016AAsv0000103Csd0000310C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5706S Gigabit Ethernet (NC370i Multifunction Gigabit Server Adapter) + +pci:v000014E4d000016AB* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Multi Function + +pci:v000014E4d000016AC* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet + +pci:v000014E4d000016ACsv00001014sd00000304* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet + +pci:v000014E4d000016ACsv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (PowerEdge 1955 Broadcom NetXtreme II BCM5708S) + +pci:v000014E4d000016ACsv00001028sd0000020C* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (PowerEdge M605 Broadcom NetXtreme II BCM5708S) + +pci:v000014E4d000016ACsv0000103Csd00001706* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (NC373m Multifunction Gigabit Server Adapter) + +pci:v000014E4d000016ACsv0000103Csd00007038* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (NC373i PCI Express Multifunction Gigabit Server Adapter) + +pci:v000014E4d000016ACsv0000103Csd0000703B* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (NC373i Integrated Multifunction Gigabit Server Adapter) + +pci:v000014E4d000016ACsv0000103Csd0000703D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM5708S Gigabit Ethernet (NC373F PCI Express Multifunction Gigabit Server Adapter) + +pci:v000014E4d000016AD* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function + +pci:v000014E4d000016ADsv0000103Csd00001916* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (FlexFabric 20Gb 2-port 630FLB Adapter) + +pci:v000014E4d000016ADsv0000103Csd00001917* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (FlexFabric 20Gb 2-port 630M Adapter) + +pci:v000014E4d000016ADsv0000103Csd00002231* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (3820C 10/20Gb Converged Network Adapter (SR-IOV VF)) + +pci:v000014E4d000016ADsv0000103Csd000022FA* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 536FLB Adapter (SR-IOV VF)) + +pci:v000014E4d000016AE* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function + +pci:v000014E4d000016AEsv0000103Csd00001798* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 530FLB Adapter) + +pci:v000014E4d000016AEsv0000103Csd000017A5* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 530M Adapter) + +pci:v000014E4d000016AEsv0000103Csd000018D3* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 530T Adapter) + +pci:v000014E4d000016AEsv0000103Csd00001930* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 534FLR-SFP+ Adapter) + +pci:v000014E4d000016AEsv0000103Csd00001931* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR CN1100R Dual Port Converged Network Adapter) + +pci:v000014E4d000016AEsv0000103Csd00001932* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 534FLB Adapter) + +pci:v000014E4d000016AEsv0000103Csd00001933* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 534M Adapter) + +pci:v000014E4d000016AEsv0000103Csd0000193A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 533FLR-T Adapter) + +pci:v000014E4d000016AEsv0000103Csd00003382* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 530FLR-SFP+ Adapter) + +pci:v000014E4d000016AEsv0000103Csd0000339D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function (NPAR 10Gb 2-port 530SFP+ Adapter) + +pci:v000014E4d000016AF* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function + +pci:v000014E4d000016AFsv0000103Csd00001798* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (Flex-10 10Gb 2-port 530FLB Adapter) + +pci:v000014E4d000016AFsv0000103Csd000017A5* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (Flex-10 10Gb 2-port 530M Adapter) + +pci:v000014E4d000016AFsv0000103Csd000018D3* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (Ethernet 10Gb 2-port 530T Adapter) + +pci:v000014E4d000016AFsv0000103Csd00001930* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 534FLR-SFP+ Adapter) + +pci:v000014E4d000016AFsv0000103Csd00001931* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (StoreFabric CN1100R Dual Port Converged Network Adapter) + +pci:v000014E4d000016AFsv0000103Csd00001932* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 534FLB Adapter) + +pci:v000014E4d000016AFsv0000103Csd00001933* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 534M Adapter) + +pci:v000014E4d000016AFsv0000103Csd0000193A* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (FlexFabric 10Gb 2-port 533FLR-T Adapter) + +pci:v000014E4d000016AFsv0000103Csd00003382* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (Ethernet 10Gb 2-port 530FLR-SFP+ Adapter) + +pci:v000014E4d000016AFsv0000103Csd0000339D* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function (Ethernet 10Gb 2-port 530SFP+ Adapter) + +pci:v000014E4d000016B0* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57761 Gigabit Ethernet PCIe + +pci:v000014E4d000016B1* + ID_MODEL_FROM_DATABASE=NetLink BCM57781 Gigabit Ethernet PCIe + +pci:v000014E4d000016B1sv00001849sd000096B1* + ID_MODEL_FROM_DATABASE=NetLink BCM57781 Gigabit Ethernet PCIe (Z77 Extreme4 motherboard) + +pci:v000014E4d000016B2* + ID_MODEL_FROM_DATABASE=NetLink BCM57791 Gigabit Ethernet PCIe + +pci:v000014E4d000016B3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57786 Gigabit Ethernet PCIe + +pci:v000014E4d000016B4* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57765 Gigabit Ethernet PCIe + +pci:v000014E4d000016B5* + ID_MODEL_FROM_DATABASE=NetLink BCM57785 Gigabit Ethernet PCIe + +pci:v000014E4d000016B6* + ID_MODEL_FROM_DATABASE=NetLink BCM57795 Gigabit Ethernet PCIe + +pci:v000014E4d000016B7* + ID_MODEL_FROM_DATABASE=NetXtreme BCM57782 Gigabit Ethernet PCIe + +pci:v000014E4d000016BC* + ID_MODEL_FROM_DATABASE=BCM57765/57785 SDXC/MMC Card Reader + +pci:v000014E4d000016BE* + ID_MODEL_FROM_DATABASE=BCM57765/57785 MS Card Reader + +pci:v000014E4d000016BF* + ID_MODEL_FROM_DATABASE=BCM57765/57785 xD-Picture Card Reader + +pci:v000014E4d000016C6* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702A3 Gigabit Ethernet + +pci:v000014E4d000016C6sv000010B7sd00001100* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702A3 Gigabit Ethernet (3C1000B-T 10/100/1000 PCI) + +pci:v000014E4d000016C6sv000014E4sd0000000C* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702A3 Gigabit Ethernet (BCM5702 1000Base-T) + +pci:v000014E4d000016C6sv000014E4sd00008009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5702A3 Gigabit Ethernet (BCM5702 1000Base-T) + +pci:v000014E4d000016C7* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet + +pci:v000014E4d000016C7sv00000E11sd000000CA* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NC7771 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d000016C7sv00000E11sd000000CB* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NC7781 Gigabit Server Adapter (PCI-X, 10,100,1000-T)) + +pci:v000014E4d000016C7sv0000103Csd000012C3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (Combo FC/GigE-SX [A9782A]) + +pci:v000014E4d000016C7sv0000103Csd000012CA* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (Combo FC/GigE-T [A9784A]) + +pci:v000014E4d000016C7sv0000103Csd00001321* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (Core I/O LAN/SCSI Combo [AB314A]) + +pci:v000014E4d000016C7sv000014E4sd00000009* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NetXtreme BCM5703 1000Base-T) + +pci:v000014E4d000016C7sv000014E4sd0000000A* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5703 Gigabit Ethernet (NetXtreme BCM5703 1000Base-SX) + +pci:v000014E4d000016C8* + ID_MODEL_FROM_DATABASE=BCM57301 NetXtreme-C Single-port 10Gb Ethernet + +pci:v000014E4d000016C9* + ID_MODEL_FROM_DATABASE=BCM57302 NetXtreme-C Dual-port 10Gb/25Gb Ethernet + +pci:v000014E4d000016CA* + ID_MODEL_FROM_DATABASE=BCM57304 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet + +pci:v000014E4d000016CB* + ID_MODEL_FROM_DATABASE=BCM57304 NetXtreme-C Ethernet Virtual Function + +pci:v000014E4d000016CE* + ID_MODEL_FROM_DATABASE=BCM57311 NetXtreme-C Single-port 10Gb RDMA Ethernet + +pci:v000014E4d000016CF* + ID_MODEL_FROM_DATABASE=BCM57312 NetXtreme-C Dual-port 10Gb/25Gb RDMA Ethernet + +pci:v000014E4d000016D0* + ID_MODEL_FROM_DATABASE=BCM57402 NetXtreme-E Dual-port 10Gb Ethernet + +pci:v000014E4d000016D1* + ID_MODEL_FROM_DATABASE=BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet + +pci:v000014E4d000016D2* + ID_MODEL_FROM_DATABASE=BCM57406 NetXtreme-E Dual-port 10GBase-T Ethernet + +pci:v000014E4d000016D3* + ID_MODEL_FROM_DATABASE=BCM57404 NetXtreme-E Ethernet Virtual Function + +pci:v000014E4d000016D4* + ID_MODEL_FROM_DATABASE=BCM57404 NetXtreme-E Ethernet Partition + +pci:v000014E4d000016D6* + ID_MODEL_FROM_DATABASE=BCM57412 NetXtreme-E Dual-port 10Gb RDMA Ethernet + +pci:v000014E4d000016D7* + ID_MODEL_FROM_DATABASE=BCM57414 NetXtreme-E Dual-port 10Gb/25Gb RDMA Ethernet + +pci:v000014E4d000016D8* + ID_MODEL_FROM_DATABASE=BCM57416 NetXtreme-E Dual-port 10GBase-T RDMA Ethernet + +pci:v000014E4d000016D9* + ID_MODEL_FROM_DATABASE=BCM57417 NetXtreme-E Dual-port 10GBase-T RDMA Ethernet + +pci:v000014E4d000016DC* + ID_MODEL_FROM_DATABASE=BCM57414 NetXtreme-E Ethernet Virtual Function + +pci:v000014E4d000016DD* + ID_MODEL_FROM_DATABASE=NetLink BCM5781 Gigabit Ethernet PCI Express + +pci:v000014E4d000016DE* + ID_MODEL_FROM_DATABASE=BCM57414 NetXtreme-E Ethernet Partition + +pci:v000014E4d000016DF* + ID_MODEL_FROM_DATABASE=BCM57314 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb RDMA Ethernet + +pci:v000014E4d000016E1* + ID_MODEL_FROM_DATABASE=BCM57314 NetXtreme-C Ethernet Virtual Function + +pci:v000014E4d000016E2* + ID_MODEL_FROM_DATABASE=BCM57417 NetXtreme-E Dual-port 10Gb/25Gb RDMA Ethernet + +pci:v000014E4d000016F3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5727 Gigabit Ethernet PCIe + +pci:v000014E4d000016F7* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753 Gigabit Ethernet PCI Express + +pci:v000014E4d000016FD* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753M Gigabit Ethernet PCI Express + +pci:v000014E4d000016FDsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753M Gigabit Ethernet PCI Express (Compaq nx9420 Notebook) + +pci:v000014E4d000016FDsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753M Gigabit Ethernet PCI Express (Compaq nw8440) + +pci:v000014E4d000016FE* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753F Fast Ethernet PCI Express + +pci:v000014E4d0000170C* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX + +pci:v000014E4d0000170Csv00001028sd00000188* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron 6000 laptop) + +pci:v000014E4d0000170Csv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron 700m/710m) + +pci:v000014E4d0000170Csv00001028sd00000196* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron 5160) + +pci:v000014E4d0000170Csv00001028sd000001AF* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron 6400) + +pci:v000014E4d0000170Csv00001028sd000001CD* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron 9400 Laptop) + +pci:v000014E4d0000170Csv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (XPS M1210) + +pci:v000014E4d0000170Csv00001028sd000001D8* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (Inspiron E1405) + +pci:v000014E4d0000170Csv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (NX6110/NC6120) + +pci:v000014E4d0000170Csv0000103Csd000030A2* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (NX7300 laptop) + +pci:v000014E4d0000170Csv000014E4sd0000170C* + ID_MODEL_FROM_DATABASE=BCM4401-B0 100Base-TX (HP Compaq 6720t Mobile Thin Client) + +pci:v000014E4d0000170D* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5901 100Base-TX + +pci:v000014E4d0000170Dsv00001014sd00000545* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5901 100Base-TX (ThinkPad R40e) + +pci:v000014E4d0000170E* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5901 100Base-TX + +pci:v000014E4d00001712* + ID_MODEL_FROM_DATABASE=NetLink BCM5906 Fast Ethernet PCI Express + +pci:v000014E4d00001713* + ID_MODEL_FROM_DATABASE=NetLink BCM5906M Fast Ethernet PCI Express + +pci:v000014E4d00001713sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=NetLink BCM5906M Fast Ethernet PCI Express (Inspiron 1420) + +pci:v000014E4d00001713sv00001028sd00000209* + ID_MODEL_FROM_DATABASE=NetLink BCM5906M Fast Ethernet PCI Express (XPS M1330) + +pci:v000014E4d00001713sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=NetLink BCM5906M Fast Ethernet PCI Express (Compaq 6710b) + +pci:v000014E4d00001713sv000017AAsd00003A23* + ID_MODEL_FROM_DATABASE=NetLink BCM5906M Fast Ethernet PCI Express (IdeaPad S10e) + +pci:v000014E4d00003352* + ID_MODEL_FROM_DATABASE=BCM3352 + +pci:v000014E4d00003360* + ID_MODEL_FROM_DATABASE=BCM3360 + +pci:v000014E4d00004210* + ID_MODEL_FROM_DATABASE=BCM4210 iLine10 HomePNA 2.0 + +pci:v000014E4d00004211* + ID_MODEL_FROM_DATABASE=BCM4211 iLine10 HomePNA 2.0 + V.90 56k modem + +pci:v000014E4d00004212* + ID_MODEL_FROM_DATABASE=BCM4212 v.90 56k modem + +pci:v000014E4d00004220* + ID_MODEL_FROM_DATABASE=802-11b/g Wireless PCI controller, packaged as a Linksys WPC54G ver 1.2 PCMCIA card + +pci:v000014E4d00004222* + ID_MODEL_FROM_DATABASE=NetXtreme BCM5753M Gigabit Ethernet PCI Express + +pci:v000014E4d00004301* + ID_MODEL_FROM_DATABASE=BCM4301 802.11b Wireless LAN Controller + +pci:v000014E4d00004301sv00001028sd00000407* + ID_MODEL_FROM_DATABASE=BCM4301 802.11b Wireless LAN Controller (TrueMobile 1180 Onboard WLAN) + +pci:v000014E4d00004301sv00001043sd00000120* + ID_MODEL_FROM_DATABASE=BCM4301 802.11b Wireless LAN Controller (WL-103b Wireless LAN PC Card) + +pci:v000014E4d00004301sv000016A5sd00001602* + ID_MODEL_FROM_DATABASE=BCM4301 802.11b Wireless LAN Controller (B-300 802.11b Wireless CardBus Adapter) + +pci:v000014E4d00004301sv00001737sd00004301* + ID_MODEL_FROM_DATABASE=BCM4301 802.11b Wireless LAN Controller (WMP11 v2.7 802.11b Wireless-B PCI Adapter) + +pci:v000014E4d00004305* + ID_MODEL_FROM_DATABASE=BCM4307 V.90 56k Modem + +pci:v000014E4d00004306* + ID_MODEL_FROM_DATABASE=BCM4306 802.11bg Wireless LAN controller + +pci:v000014E4d00004307* + ID_MODEL_FROM_DATABASE=BCM4306 802.11bg Wireless LAN Controller + +pci:v000014E4d00004310* + ID_MODEL_FROM_DATABASE=BCM4310 Chipcommon I/OController + +pci:v000014E4d00004311* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN + +pci:v000014E4d00004311sv00001028sd00000007* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (Wireless 1390 WLAN Mini-Card) + +pci:v000014E4d00004311sv00001028sd00000008* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (Wireless 1390 WLAN ExpressCard) + +pci:v000014E4d00004311sv0000103Csd00001363* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001364* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001365* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001374* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001375* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001376* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001377* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4311 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd0000137F* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4322 802.11a/b/g/n Wireless LAN Controller) + +pci:v000014E4d00004311sv0000103Csd00001380* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM4322 802.11a/b/g/n Wireless LAN Controller) + +pci:v000014E4d00004311sv000014E4sd00004311* + ID_MODEL_FROM_DATABASE=BCM4311 802.11b/g WLAN (BCM94311MCG) + +pci:v000014E4d00004312* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g + +pci:v000014E4d00004312sv00001028sd00000007* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Wireless 1490 Dual Band WLAN Mini-Card) + +pci:v000014E4d00004312sv00001028sd00000008* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Wireless 1490 Dual Band WLAN ExpressCard) + +pci:v000014E4d00004312sv0000103Csd0000135A* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd0000135F* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001360* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001361* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001362* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001370* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001371* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001372* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd00001373* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004312sv0000103Csd000030B5* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Presario V3242AU) + +pci:v000014E4d00004312sv0000106Bsd00000089* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (AirPort Extreme) + +pci:v000014E4d00004312sv00001371sd0000103C* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a/b/g (Broadcom 802.11 Multiband-netwerkadapter(6715s)) + +pci:v000014E4d00004313* + ID_MODEL_FROM_DATABASE=BCM4311 802.11a + +pci:v000014E4d00004315* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY + +pci:v000014E4d00004315sv00001028sd0000000B* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (Wireless 1395 WLAN Mini-Card) + +pci:v000014E4d00004315sv00001028sd0000000C* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (Wireless 1397 WLAN Mini-Card) + +pci:v000014E4d00004315sv0000103Csd0000137C* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (BCM4312 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004315sv0000103Csd0000137D* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (BCM4312 802.11b/g Wireless LAN Controller) + +pci:v000014E4d00004315sv0000103Csd00001507* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (U98Z049.00 Wireless Mini PCIe Card) + +pci:v000014E4d00004315sv0000105Bsd0000E003* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (T77H030.00 Wireless Mini PCIe Card) + +pci:v000014E4d00004315sv0000105Bsd0000E01B* + ID_MODEL_FROM_DATABASE=BCM4312 802.11b/g LP-PHY (T77H106.00 Wireless Half-size Mini PCIe Card) + +pci:v000014E4d00004318* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller + +pci:v000014E4d00004318sv00001028sd00000005* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Wireless 1370 WLAN Mini-PCI Card) + +pci:v000014E4d00004318sv00001028sd00000006* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Wireless 1370 WLAN PC Card) + +pci:v000014E4d00004318sv0000103Csd00001355* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004318sv0000103Csd00001356* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004318sv0000103Csd00001357* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004318sv00001043sd0000100F* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (WL-138G v2 / WL-138gE / WL-100gE) + +pci:v000014E4d00004318sv00001043sd0000120F* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (A6U notebook embedded card) + +pci:v000014E4d00004318sv00001154sd00000355* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Buffalo WLI2-PCI-G54S High Speed Mode Wireless Adapter) + +pci:v000014E4d00004318sv00001468sd00000311* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Aspire 3022WLMi, 5024WLMi, 5020) + +pci:v000014E4d00004318sv00001468sd00000312* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (TravelMate 2410) + +pci:v000014E4d00004318sv000014E4sd00000449* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (Gateway 7510GX) + +pci:v000014E4d00004318sv000016ECsd00000119* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (U.S.Robotics Wireless MAXg PC Card) + +pci:v000014E4d00004318sv00001737sd00000042* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (WMP54GS v1.1 802.11g Wireless-G PCI Adapter with SpeedBooster) + +pci:v000014E4d00004318sv00001737sd00000048* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (WPC54G v3 802.11g Wireless-G Notebook Adapter) + +pci:v000014E4d00004318sv00001737sd00000049* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (WPC54GS v2 802.11g Wireless-G Notebook Adapter with SpeedBooster) + +pci:v000014E4d00004318sv00001799sd00007000* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (F5D7000 v4000 Wireless G Desktop Card) + +pci:v000014E4d00004318sv00001799sd00007001* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (F5D7001 v2000 Wireless G Plus Desktop Card) + +pci:v000014E4d00004318sv00001799sd00007010* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (F5D7010 v4000 Wireless G Notebook Card) + +pci:v000014E4d00004318sv00001799sd00007011* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce One 54g] 802.11g Wireless LAN Controller (F5D7011 v2000 High-Speed Mode Wireless G Notebook Card) + +pci:v000014E4d00004319* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver + +pci:v000014E4d00004319sv00001028sd00000005* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver (Wireless 1470 Dual Band WLAN Mini-PCI Card) + +pci:v000014E4d00004319sv00001028sd00000006* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver (Wireless 1470 Dual Band WLAN PC Card) + +pci:v000014E4d00004319sv0000103Csd00001358* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004319sv0000103Csd00001359* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004319sv0000103Csd0000135A* + ID_MODEL_FROM_DATABASE=BCM4318 [AirForce 54g] 802.11a/b/g PCI Express Transceiver (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004320* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller + +pci:v000014E4d00004320sv00001028sd00000001* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (TrueMobile 1300 WLAN Mini-PCI Card) + +pci:v000014E4d00004320sv00001028sd00000002* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (TrueMobile 1300 WLAN PC Card) + +pci:v000014E4d00004320sv00001028sd00000003* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Wireless 1350 WLAN Mini-PCI Card) + +pci:v000014E4d00004320sv00001028sd00000004* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Wireless 1350 WLAN PC Card) + +pci:v000014E4d00004320sv0000103Csd000012F4* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004320sv0000103Csd000012F8* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004320sv0000103Csd000012FA* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004320sv0000103Csd000012FB* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Broadcom 802.11b/g WLAN) + +pci:v000014E4d00004320sv00001043sd0000100F* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WL-100G) + +pci:v000014E4d00004320sv00001057sd00007025* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WN825G) + +pci:v000014E4d00004320sv0000106Bsd0000004E* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (AirPort Extreme) + +pci:v000014E4d00004320sv00001154sd00000330* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Buffalo WLI2-PCI-G54S High Speed Mode Wireless Desktop Adapter) + +pci:v000014E4d00004320sv0000144Fsd00007050* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (eMachines M6805 802.11g Built-in Wireless) + +pci:v000014E4d00004320sv0000144Fsd00007051* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (Sonnet Aria Extreme PCI) + +pci:v000014E4d00004320sv00001737sd00000013* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WMP54G v1 802.11g PCI Adapter) + +pci:v000014E4d00004320sv00001737sd00000014* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WMP54G v2 802.11g PCI Adapter) + +pci:v000014E4d00004320sv00001737sd00000015* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WMP54GS v1.0 802.11g Wireless-G PCI Adapter with SpeedBooster) + +pci:v000014E4d00004320sv00001737sd00004320* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (WPC54G v1 / WPC54GS v1 802.11g Wireless-G Notebook Adapter) + +pci:v000014E4d00004320sv00001799sd00007000* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (F5D7000 v1000 Wireless G Desktop Card) + +pci:v000014E4d00004320sv00001799sd00007001* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (F5D7001 v1000 Wireless G Plus Desktop Card) + +pci:v000014E4d00004320sv00001799sd00007010* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (F5D7010 v1000 Wireless G Notebook Card) + +pci:v000014E4d00004320sv00001799sd00007011* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (F5D7011 v1000 High-Speed Mode Wireless G Notebook Card) + +pci:v000014E4d00004320sv0000185Fsd00001220* + ID_MODEL_FROM_DATABASE=BCM4306 802.11b/g Wireless LAN Controller (TravelMate 290E WLAN Mini-PCI Card) + +pci:v000014E4d00004321* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a Wireless Network Controller + +pci:v000014E4d00004322* + ID_MODEL_FROM_DATABASE=BCM4322 802.11bgn Wireless Network Controller + +pci:v000014E4d00004324* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller + +pci:v000014E4d00004324sv00001028sd00000001* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (Truemobile 1400) + +pci:v000014E4d00004324sv00001028sd00000002* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (TrueMobile 1400 Dual Band WLAN PC Card) + +pci:v000014E4d00004324sv00001028sd00000003* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (Truemobile 1450 MiniPCI) + +pci:v000014E4d00004324sv00001028sd00000004* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (Wireless 1450 Dual Band WLAN PC Card) + +pci:v000014E4d00004324sv0000103Csd000012F9* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004324sv0000103Csd000012FC* + ID_MODEL_FROM_DATABASE=BCM4309 802.11abg Wireless Network Controller (Broadcom 802.11a/b/g WLAN) + +pci:v000014E4d00004325* + ID_MODEL_FROM_DATABASE=BCM4306 802.11bg Wireless Network Controller + +pci:v000014E4d00004325sv00001414sd00000003* + ID_MODEL_FROM_DATABASE=BCM4306 802.11bg Wireless Network Controller (Wireless Notebook Adapter MN-720) + +pci:v000014E4d00004325sv00001414sd00000004* + ID_MODEL_FROM_DATABASE=BCM4306 802.11bg Wireless Network Controller (Wireless PCI Adapter MN-730) + +pci:v000014E4d00004326* + ID_MODEL_FROM_DATABASE=BCM4307 Chipcommon I/O Controller? + +pci:v000014E4d00004328* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n + +pci:v000014E4d00004328sv00001028sd00000009* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless 1500 Draft 802.11n WLAN Mini-Card) + +pci:v000014E4d00004328sv00001028sd0000000A* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless 1500 Draft 802.11n WLAN Mini-card) + +pci:v000014E4d00004328sv0000103Csd00001366* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless LAN Controller) + +pci:v000014E4d00004328sv0000103Csd00001367* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless LAN Controller) + +pci:v000014E4d00004328sv0000103Csd00001368* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless LAN Controller) + +pci:v000014E4d00004328sv0000103Csd00001369* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (Wireless LAN Controller) + +pci:v000014E4d00004328sv0000106Bsd00000087* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004328sv0000106Bsd00000088* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004328sv0000106Bsd0000008B* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004328sv0000106Bsd0000008C* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004328sv0000106Bsd00000090* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004328sv000014E4sd00004328* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (BCM4328 802.11a/b/g/n) + +pci:v000014E4d00004328sv00001737sd00000066* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (WPC600N v1 802.11a/b/g/n Wireless-N CardBus Adapter) + +pci:v000014E4d00004328sv00001737sd00000068* + ID_MODEL_FROM_DATABASE=BCM4321 802.11a/b/g/n (WEC600N v1 802.11a/b/g/n Wireless-N ExpressCard) + +pci:v000014E4d00004329* + ID_MODEL_FROM_DATABASE=BCM4321 802.11b/g/n + +pci:v000014E4d00004329sv00001385sd00007B00* + ID_MODEL_FROM_DATABASE=BCM4321 802.11b/g/n (WN511B RangeMax NEXT Wireless Notebook Adapter) + +pci:v000014E4d00004329sv00001385sd00007D00* + ID_MODEL_FROM_DATABASE=BCM4321 802.11b/g/n (WN311B RangeMax Next 270 Mbps Wireless PCI Adapter) + +pci:v000014E4d00004329sv00001737sd00000058* + ID_MODEL_FROM_DATABASE=BCM4321 802.11b/g/n (WPC300N v1 Wireless-N Notebook Adapter) + +pci:v000014E4d0000432A* + ID_MODEL_FROM_DATABASE=BCM4321 802.11an Wireless Network Controller + +pci:v000014E4d0000432B* + ID_MODEL_FROM_DATABASE=BCM4322 802.11a/b/g/n Wireless LAN Controller + +pci:v000014E4d0000432Bsv00001028sd0000000D* + ID_MODEL_FROM_DATABASE=BCM4322 802.11a/b/g/n Wireless LAN Controller (Wireless 1510 Wireless-N WLAN Mini-Card) + +pci:v000014E4d0000432Bsv0000106Bsd0000008D* + ID_MODEL_FROM_DATABASE=BCM4322 802.11a/b/g/n Wireless LAN Controller (AirPort Extreme) + +pci:v000014E4d0000432Bsv0000106Bsd0000008E* + ID_MODEL_FROM_DATABASE=BCM4322 802.11a/b/g/n Wireless LAN Controller (AirPort Extreme) + +pci:v000014E4d0000432C* + ID_MODEL_FROM_DATABASE=BCM4322 802.11b/g/n + +pci:v000014E4d0000432Csv00001799sd0000D311* + ID_MODEL_FROM_DATABASE=BCM4322 802.11b/g/n (Dynex DX-NNBX 802.11n WLAN Cardbus Card) + +pci:v000014E4d0000432D* + ID_MODEL_FROM_DATABASE=BCM4322 802.11an Wireless Network Controller + +pci:v000014E4d00004331* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n + +pci:v000014E4d00004331sv0000106Bsd000000D6* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd000000E4* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd000000EF* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd000000F4* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd000000F5* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd0000010E* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004331sv0000106Bsd0000010F* + ID_MODEL_FROM_DATABASE=BCM4331 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004333* + ID_MODEL_FROM_DATABASE=Serial (EDGE/GPRS modem part of Option GT Combo Edge) + +pci:v000014E4d00004344* + ID_MODEL_FROM_DATABASE=EDGE/GPRS data and 802.11b/g combo cardbus [GC89] + +pci:v000014E4d00004350* + ID_MODEL_FROM_DATABASE=BCM43222 Wireless Network Adapter + +pci:v000014E4d00004351* + ID_MODEL_FROM_DATABASE=BCM43222 802.11abgn Wireless Network Adapter + +pci:v000014E4d00004353* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n + +pci:v000014E4d00004353sv00001028sd0000000E* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n (Wireless 1520 Half-size Mini PCIe Card) + +pci:v000014E4d00004353sv0000103Csd00001509* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n (WMIB-275N Half-size Mini PCIe Card) + +pci:v000014E4d00004353sv0000106Bsd00000093* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004353sv0000106Bsd000000D1* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004353sv0000106Bsd000000E9* + ID_MODEL_FROM_DATABASE=BCM43224 802.11a/b/g/n (AirPort Extreme) + +pci:v000014E4d00004357* + ID_MODEL_FROM_DATABASE=BCM43225 802.11b/g/n + +pci:v000014E4d00004357sv0000105Bsd0000E021* + ID_MODEL_FROM_DATABASE=BCM43225 802.11b/g/n (T77H103.00 Wireless Half-size Mini PCIe Card) + +pci:v000014E4d00004358* + ID_MODEL_FROM_DATABASE=BCM43227 802.11b/g/n + +pci:v000014E4d00004359* + ID_MODEL_FROM_DATABASE=BCM43228 802.11a/b/g/n + +pci:v000014E4d00004359sv00001028sd00000011* + ID_MODEL_FROM_DATABASE=BCM43228 802.11a/b/g/n (Wireless 1530 Half-size Mini PCIe Card) + +pci:v000014E4d00004359sv0000103Csd0000182C* + ID_MODEL_FROM_DATABASE=BCM43228 802.11a/b/g/n (BCM943228HM4L 802.11a/b/g/n 2x2 Wi-Fi Adapter) + +pci:v000014E4d00004360* + ID_MODEL_FROM_DATABASE=BCM4360 802.11ac Wireless Network Adapter + +pci:v000014E4d00004365* + ID_MODEL_FROM_DATABASE=BCM43142 802.11b/g/n + +pci:v000014E4d00004365sv00001028sd00000016* + ID_MODEL_FROM_DATABASE=BCM43142 802.11b/g/n (Wireless 1704 802.11n + BT 4.0) + +pci:v000014E4d000043A0* + ID_MODEL_FROM_DATABASE=BCM4360 802.11ac Wireless Network Adapter + +pci:v000014E4d000043A1* + ID_MODEL_FROM_DATABASE=BCM4360 802.11ac Wireless Network Adapter + +pci:v000014E4d000043A2* + ID_MODEL_FROM_DATABASE=BCM4360 802.11ac Wireless Network Adapter + +pci:v000014E4d000043A3* + ID_MODEL_FROM_DATABASE=BCM4350 802.11ac Wireless Network Adapter + +pci:v000014E4d000043A9* + ID_MODEL_FROM_DATABASE=BCM43217 802.11b/g/n + +pci:v000014E4d000043AA* + ID_MODEL_FROM_DATABASE=BCM43131 802.11b/g/n + +pci:v000014E4d000043AE* + ID_MODEL_FROM_DATABASE=BCM43162 802.11ac Wireless Network Adapter + +pci:v000014E4d000043B1* + ID_MODEL_FROM_DATABASE=BCM4352 802.11ac Wireless Network Adapter + +pci:v000014E4d000043BA* + ID_MODEL_FROM_DATABASE=BCM43602 802.11ac Wireless LAN SoC + +pci:v000014E4d000043BB* + ID_MODEL_FROM_DATABASE=BCM43602 802.11ac Wireless LAN SoC + +pci:v000014E4d000043BC* + ID_MODEL_FROM_DATABASE=BCM43602 802.11ac Wireless LAN SoC + +pci:v000014E4d000043D3* + ID_MODEL_FROM_DATABASE=BCM43567 802.11ac Wireless Network Adapter + +pci:v000014E4d000043D9* + ID_MODEL_FROM_DATABASE=BCM43570 802.11ac Wireless Network Adapter + +pci:v000014E4d000043DF* + ID_MODEL_FROM_DATABASE=BCM4354 802.11ac Wireless LAN SoC + +pci:v000014E4d000043E9* + ID_MODEL_FROM_DATABASE=BCM4358 802.11ac Wireless LAN SoC + +pci:v000014E4d000043EC* + ID_MODEL_FROM_DATABASE=BCM4356 802.11ac Wireless Network Adapter + +pci:v000014E4d00004401* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T + +pci:v000014E4d00004401sv00001025sd00000035* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T (TravelMate 660) + +pci:v000014E4d00004401sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T (Extensa 3000 series laptop) + +pci:v000014E4d00004401sv00001028sd00008127* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T (Dimension 2400) + +pci:v000014E4d00004401sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T (tc1100 tablet) + +pci:v000014E4d00004401sv00001043sd000080A8* + ID_MODEL_FROM_DATABASE=BCM4401 100Base-T (A7V8X motherboard) + +pci:v000014E4d00004402* + ID_MODEL_FROM_DATABASE=BCM4402 Integrated 10/100BaseT + +pci:v000014E4d00004403* + ID_MODEL_FROM_DATABASE=BCM4402 V.90 56k Modem + +pci:v000014E4d00004410* + ID_MODEL_FROM_DATABASE=BCM4413 iLine32 HomePNA 2.0 + +pci:v000014E4d00004411* + ID_MODEL_FROM_DATABASE=BCM4413 V.90 56k modem + +pci:v000014E4d00004412* + ID_MODEL_FROM_DATABASE=BCM4412 10/100BaseT + +pci:v000014E4d00004430* + ID_MODEL_FROM_DATABASE=BCM44xx CardBus iLine32 HomePNA 2.0 + +pci:v000014E4d00004432* + ID_MODEL_FROM_DATABASE=BCM4432 CardBus 10/100BaseT + +pci:v000014E4d00004610* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 PCI to SB Bridge + +pci:v000014E4d00004611* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 iLine32 HomePNA 1.0 + +pci:v000014E4d00004612* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 V.90 56k Modem + +pci:v000014E4d00004613* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 Ethernet Controller + +pci:v000014E4d00004614* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 External Interface + +pci:v000014E4d00004615* + ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 USB Controller + +pci:v000014E4d00004704* + ID_MODEL_FROM_DATABASE=BCM4704 PCI to SB Bridge + +pci:v000014E4d00004705* + ID_MODEL_FROM_DATABASE=BCM4704 Sentry5 802.11b Wireless LAN Controller + +pci:v000014E4d00004706* + ID_MODEL_FROM_DATABASE=BCM4704 Sentry5 Ethernet Controller + +pci:v000014E4d00004707* + ID_MODEL_FROM_DATABASE=BCM4704 Sentry5 USB Controller + +pci:v000014E4d00004708* + ID_MODEL_FROM_DATABASE=BCM4704 Crypto Accelerator + +pci:v000014E4d00004710* + ID_MODEL_FROM_DATABASE=BCM4710 Sentry5 PCI to SB Bridge + +pci:v000014E4d00004711* + ID_MODEL_FROM_DATABASE=BCM47xx Sentry5 iLine32 HomePNA 2.0 + +pci:v000014E4d00004712* + ID_MODEL_FROM_DATABASE=BCM47xx V.92 56k modem + +pci:v000014E4d00004713* + ID_MODEL_FROM_DATABASE=Sentry5 Ethernet Controller + +pci:v000014E4d00004714* + ID_MODEL_FROM_DATABASE=BCM47xx Sentry5 External Interface + +pci:v000014E4d00004715* + ID_MODEL_FROM_DATABASE=BCM47xx Sentry5 USB / Ethernet Controller + +pci:v000014E4d00004716* + ID_MODEL_FROM_DATABASE=BCM47xx Sentry5 USB Host Controller + +pci:v000014E4d00004717* + ID_MODEL_FROM_DATABASE=BCM47xx Sentry5 USB Device Controller + +pci:v000014E4d00004718* + ID_MODEL_FROM_DATABASE=Sentry5 Crypto Accelerator + +pci:v000014E4d00004719* + ID_MODEL_FROM_DATABASE=BCM47xx/53xx RoboSwitch Core + +pci:v000014E4d00004720* + ID_MODEL_FROM_DATABASE=BCM4712 MIPS CPU + +pci:v000014E4d00004727* + ID_MODEL_FROM_DATABASE=BCM4313 802.11bgn Wireless Network Adapter + +pci:v000014E4d00004727sv00001028sd00000010* + ID_MODEL_FROM_DATABASE=BCM4313 802.11bgn Wireless Network Adapter (Inspiron M5010 / XPS 8300) + +pci:v000014E4d00005365* + ID_MODEL_FROM_DATABASE=BCM5365P Sentry5 Host Bridge + +pci:v000014E4d00005600* + ID_MODEL_FROM_DATABASE=BCM5600 StrataSwitch 24+2 Ethernet Switch Controller + +pci:v000014E4d00005605* + ID_MODEL_FROM_DATABASE=BCM5605 StrataSwitch 24+2 Ethernet Switch Controller + +pci:v000014E4d00005615* + ID_MODEL_FROM_DATABASE=BCM5615 StrataSwitch 24+2 Ethernet Switch Controller + +pci:v000014E4d00005625* + ID_MODEL_FROM_DATABASE=BCM5625 StrataSwitch 24+2 Ethernet Switch Controller + +pci:v000014E4d00005645* + ID_MODEL_FROM_DATABASE=BCM5645 StrataSwitch 24+2 Ethernet Switch Controller + +pci:v000014E4d00005670* + ID_MODEL_FROM_DATABASE=BCM5670 8-Port 10GE Ethernet Switch Fabric + +pci:v000014E4d00005680* + ID_MODEL_FROM_DATABASE=BCM5680 G-Switch 8 Port Gigabit Ethernet Switch Controller + +pci:v000014E4d00005690* + ID_MODEL_FROM_DATABASE=BCM5690 12-port Multi-Layer Gigabit Ethernet Switch + +pci:v000014E4d00005691* + ID_MODEL_FROM_DATABASE=BCM5691 GE/10GE 8+2 Gigabit Ethernet Switch Controller + +pci:v000014E4d00005692* + ID_MODEL_FROM_DATABASE=BCM5692 12-port Multi-Layer Gigabit Ethernet Switch + +pci:v000014E4d00005695* + ID_MODEL_FROM_DATABASE=BCM5695 12-port + HiGig Multi-Layer Gigabit Ethernet Switch + +pci:v000014E4d00005698* + ID_MODEL_FROM_DATABASE=BCM5698 12-port Multi-Layer Gigabit Ethernet Switch + +pci:v000014E4d00005820* + ID_MODEL_FROM_DATABASE=BCM5820 Crypto Accelerator + +pci:v000014E4d00005821* + ID_MODEL_FROM_DATABASE=BCM5821 Crypto Accelerator + +pci:v000014E4d00005822* + ID_MODEL_FROM_DATABASE=BCM5822 Crypto Accelerator + +pci:v000014E4d00005823* + ID_MODEL_FROM_DATABASE=BCM5823 Crypto Accelerator + +pci:v000014E4d00005824* + ID_MODEL_FROM_DATABASE=BCM5824 Crypto Accelerator + +pci:v000014E4d00005840* + ID_MODEL_FROM_DATABASE=BCM5840 Crypto Accelerator + +pci:v000014E4d00005841* + ID_MODEL_FROM_DATABASE=BCM5841 Crypto Accelerator + +pci:v000014E4d00005850* + ID_MODEL_FROM_DATABASE=BCM5850 Crypto Accelerator + +pci:v000014E4d00008602* + ID_MODEL_FROM_DATABASE=BCM7400/BCM7405 Serial ATA Controller + +pci:v000014E4d0000A8D8* + ID_MODEL_FROM_DATABASE=BCM43224/5 Wireless Network Adapter + +pci:v000014E4d0000AA52* + ID_MODEL_FROM_DATABASE=BCM43602 802.11ac Wireless LAN SoC + +pci:v000014E4d0000B302* + ID_MODEL_FROM_DATABASE=BCM56302 StrataXGS 24x1GE 2x10GE Switch Controller + +pci:v000014E4d0000B334* + ID_MODEL_FROM_DATABASE=BCM56334 StrataXGS 24x1GE 4x10GE Switch Controller + +pci:v000014E4d0000B800* + ID_MODEL_FROM_DATABASE=BCM56800 StrataXGS 10GE Switch Controller + +pci:v000014E4d0000B842* + ID_MODEL_FROM_DATABASE=BCM56842 Trident 10GE Switch Controller + +pci:v000014E5* + ID_VENDOR_FROM_DATABASE=Pixelfusion Ltd + +pci:v000014E6* + ID_VENDOR_FROM_DATABASE=SHINING Technology Inc + +pci:v000014E7* + ID_VENDOR_FROM_DATABASE=3CX + +pci:v000014E8* + ID_VENDOR_FROM_DATABASE=RAYCER Inc + +pci:v000014E9* + ID_VENDOR_FROM_DATABASE=GARNETS System CO Ltd + +pci:v000014EA* + ID_VENDOR_FROM_DATABASE=Planex Communications, Inc + +pci:v000014EAd0000AB06* + ID_MODEL_FROM_DATABASE=FNW-3603-TX CardBus Fast Ethernet + +pci:v000014EAd0000AB07* + ID_MODEL_FROM_DATABASE=RTL81xx RealTek Ethernet + +pci:v000014EAd0000AB08* + ID_MODEL_FROM_DATABASE=FNW-3602-TX CardBus Fast Ethernet + +pci:v000014EB* + ID_VENDOR_FROM_DATABASE=SEIKO EPSON Corp + +pci:v000014EC* + ID_VENDOR_FROM_DATABASE=Agilent Technologies + +pci:v000014ECd00000000* + ID_MODEL_FROM_DATABASE=Aciris Digitizer (malformed ID) + +pci:v000014ED* + ID_VENDOR_FROM_DATABASE=DATAKINETICS Ltd + +pci:v000014EE* + ID_VENDOR_FROM_DATABASE=MASPRO KENKOH Corp + +pci:v000014EF* + ID_VENDOR_FROM_DATABASE=CARRY Computer ENG. CO Ltd + +pci:v000014F0* + ID_VENDOR_FROM_DATABASE=CANON RESEACH CENTRE FRANCE + +pci:v000014F1* + ID_VENDOR_FROM_DATABASE=Conexant Systems, Inc. + +pci:v000014F1d00001002* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001003* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001004* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001005* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001006* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001022* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001023* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001024* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001025* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001026* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001032* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001033* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v000014F1d00001033sv00001033sd00008077* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (NEC) + +pci:v000014F1d00001033sv0000122Dsd00004027* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Dell Zeus - MDP3880-W(B) Data Fax Modem) + +pci:v000014F1d00001033sv0000122Dsd00004030* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Dell Mercury - MDP3880-U(B) Data Fax Modem) + +pci:v000014F1d00001033sv0000122Dsd00004034* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Dell Thor - MDP3880-W(U) Data Fax Modem) + +pci:v000014F1d00001033sv000013E0sd0000020D* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Dell Copper) + +pci:v000014F1d00001033sv000013E0sd0000020E* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Dell Silver) + +pci:v000014F1d00001033sv000013E0sd00000261* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001033sv000013E0sd00000290* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Compaq Goldwing) + +pci:v000014F1d00001033sv000013E0sd000002A0* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001033sv000013E0sd000002B0* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001033sv000013E0sd000002C0* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Compaq Scooter) + +pci:v000014F1d00001033sv000013E0sd000002D0* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001033sv0000144Fsd00001500* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P85-DF (1)) + +pci:v000014F1d00001033sv0000144Fsd00001501* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P85-DF (2)) + +pci:v000014F1d00001033sv0000144Fsd0000150A* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P85-DF (3)) + +pci:v000014F1d00001033sv0000144Fsd0000150B* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P85-DF Low Profile (1)) + +pci:v000014F1d00001033sv0000144Fsd00001510* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P85-DF Low Profile (2)) + +pci:v000014F1d00001034* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v000014F1d00001035* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00001035sv000010CFsd00001098* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Fujitsu P85-DFSV) + +pci:v000014F1d00001036* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00001036sv0000104Dsd00008067* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (HCF 56k Modem) + +pci:v000014F1d00001036sv0000122Dsd00004029* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (MDP3880SP-W) + +pci:v000014F1d00001036sv0000122Dsd00004031* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (MDP3880SP-U) + +pci:v000014F1d00001036sv000013E0sd00000209* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Dell Titanium) + +pci:v000014F1d00001036sv000013E0sd0000020A* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Dell Graphite) + +pci:v000014F1d00001036sv000013E0sd00000260* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Gateway Red Owl) + +pci:v000014F1d00001036sv000013E0sd00000270* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Gateway White Horse) + +pci:v000014F1d00001052* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Worldwide) + +pci:v000014F1d00001053* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (Worldwide) + +pci:v000014F1d00001054* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem (Worldwide) + +pci:v000014F1d00001055* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (Worldwide) + +pci:v000014F1d00001056* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Worldwide) + +pci:v000014F1d00001056sv0000122Dsd00004035* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Worldwide) (MDP3900V-W) + +pci:v000014F1d00001057* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Worldwide) + +pci:v000014F1d00001059* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem (Worldwide) + +pci:v000014F1d00001063* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v000014F1d00001064* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v000014F1d00001065* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00001066* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00001066sv0000122Dsd00004033* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Dell Athena - MDP3900V-U) + +pci:v000014F1d00001085* + ID_MODEL_FROM_DATABASE=HCF V90 56k Data/Fax/Voice/Spkp PCI Modem + +pci:v000014F1d000010B6* + ID_MODEL_FROM_DATABASE=CX06834-11 HCF V.92 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00001433* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v000014F1d00001434* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v000014F1d00001435* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00001436* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v000014F1d00001453* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem + +pci:v000014F1d00001453sv000013E0sd00000240* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001453sv000013E0sd00000250* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM) + +pci:v000014F1d00001453sv0000144Fsd00001502* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P95-DF (1)) + +pci:v000014F1d00001453sv0000144Fsd00001503* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax Modem (IBM P95-DF (2)) + +pci:v000014F1d00001454* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice Modem + +pci:v000014F1d00001455* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00001456* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00001456sv0000122Dsd00004035* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Dell Europa - MDP3900V-W) + +pci:v000014F1d00001456sv0000122Dsd00004302* + ID_MODEL_FROM_DATABASE=HCF 56k Data/Fax/Voice/Spkp Modem (Dell MP3930V-W(C) MiniPCI) + +pci:v000014F1d00001610* + ID_MODEL_FROM_DATABASE=ADSL AccessRunner PCI Arbitration Device + +pci:v000014F1d00001611* + ID_MODEL_FROM_DATABASE=AccessRunner PCI ADSL Interface Device + +pci:v000014F1d00001620* + ID_MODEL_FROM_DATABASE=AccessRunner V2 PCI ADSL Arbitration Device + +pci:v000014F1d00001621* + ID_MODEL_FROM_DATABASE=AccessRunner V2 PCI ADSL Interface Device + +pci:v000014F1d00001622* + ID_MODEL_FROM_DATABASE=AccessRunner V2 PCI ADSL Yukon WAN Adapter + +pci:v000014F1d00001803* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001803sv00000E11sd00000023* + ID_MODEL_FROM_DATABASE=HCF 56k Modem (623-LAN Grizzly) + +pci:v000014F1d00001803sv00000E11sd00000043* + ID_MODEL_FROM_DATABASE=HCF 56k Modem (623-LAN Yogi) + +pci:v000014F1d00001811* + ID_MODEL_FROM_DATABASE=MiniPCI Network Adapter + +pci:v000014F1d00001815* + ID_MODEL_FROM_DATABASE=HCF 56k Modem + +pci:v000014F1d00001815sv00000E11sd00000022* + ID_MODEL_FROM_DATABASE=HCF 56k Modem (Grizzly) + +pci:v000014F1d00001815sv00000E11sd00000042* + ID_MODEL_FROM_DATABASE=HCF 56k Modem (Yogi) + +pci:v000014F1d00001830* + ID_MODEL_FROM_DATABASE=CX861xx Integrated Host Bridge + +pci:v000014F1d00002003* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem + +pci:v000014F1d00002004* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem + +pci:v000014F1d00002005* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00002006* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00002013* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem + +pci:v000014F1d00002013sv00000E11sd0000B195* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Bear) + +pci:v000014F1d00002013sv00000E11sd0000B196* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Seminole 1) + +pci:v000014F1d00002013sv00000E11sd0000B1BE* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Seminole 2) + +pci:v000014F1d00002013sv00001025sd00008013* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Acer) + +pci:v000014F1d00002013sv00001033sd0000809D* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (NEC) + +pci:v000014F1d00002013sv00001033sd000080BC* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (NEC) + +pci:v000014F1d00002013sv0000155Dsd00006793* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (HP) + +pci:v000014F1d00002013sv0000155Dsd00008850* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (E Machines) + +pci:v000014F1d00002014* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem + +pci:v000014F1d00002015* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem + +pci:v000014F1d00002016* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem + +pci:v000014F1d00002043* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (WorldW SmartDAA) + +pci:v000014F1d00002044* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (WorldW SmartDAA) + +pci:v000014F1d00002045* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (WorldW SmartDAA) + +pci:v000014F1d00002045sv000014F1sd00002045* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (WorldW SmartDAA) (Generic SoftK56) + +pci:v000014F1d00002046* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (WorldW SmartDAA) + +pci:v000014F1d00002063* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (SmartDAA) + +pci:v000014F1d00002064* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (SmartDAA) + +pci:v000014F1d00002065* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem (SmartDAA) + +pci:v000014F1d00002066* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (SmartDAA) + +pci:v000014F1d00002093* + ID_MODEL_FROM_DATABASE=HSF 56k Modem + +pci:v000014F1d00002093sv0000155Dsd00002F07* + ID_MODEL_FROM_DATABASE=HSF 56k Modem (Legend) + +pci:v000014F1d00002143* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Cell Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002144* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Cell Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002145* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS)/Cell Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002146* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp/Cell Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002163* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Cell Modem (Mob SmartDAA) + +pci:v000014F1d00002164* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Cell Modem (Mob SmartDAA) + +pci:v000014F1d00002165* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS)/Cell Modem (Mob SmartDAA) + +pci:v000014F1d00002166* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp/Cell Modem (Mob SmartDAA) + +pci:v000014F1d00002343* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax CardBus Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002344* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice CardBus Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002345* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS) CardBus Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002346* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp CardBus Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002363* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax CardBus Modem (Mob SmartDAA) + +pci:v000014F1d00002364* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice CardBus Modem (Mob SmartDAA) + +pci:v000014F1d00002365* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS) CardBus Modem (Mob SmartDAA) + +pci:v000014F1d00002366* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp CardBus Modem (Mob SmartDAA) + +pci:v000014F1d00002443* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002443sv0000104Dsd00008075* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Mob WorldW SmartDAA) (Modem) + +pci:v000014F1d00002443sv0000104Dsd00008083* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Mob WorldW SmartDAA) (Modem) + +pci:v000014F1d00002443sv0000104Dsd00008097* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Mob WorldW SmartDAA) (Modem) + +pci:v000014F1d00002444* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002445* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS) Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002446* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (Mob WorldW SmartDAA) + +pci:v000014F1d00002463* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Mob SmartDAA) + +pci:v000014F1d00002464* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice Modem (Mob SmartDAA) + +pci:v000014F1d00002465* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp (w/HS) Modem (Mob SmartDAA) + +pci:v000014F1d00002466* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax/Voice/Spkp Modem (Mob SmartDAA) + +pci:v000014F1d00002702* + ID_MODEL_FROM_DATABASE=HSFi modem RD01-D270 + +pci:v000014F1d00002702sv00001028sd00008D88* + ID_MODEL_FROM_DATABASE=HSFi modem RD01-D270 (SmartHSFi V92 56K PCI Modem) + +pci:v000014F1d00002F00* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Modem + +pci:v000014F1d00002F00sv000013E0sd00008D84* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Modem (IBM HSFi V.90) + +pci:v000014F1d00002F00sv000013E0sd00008D85* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Modem (Compaq Stinger) + +pci:v000014F1d00002F00sv000014F1sd00002004* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Modem (Dynalink 56PMi) + +pci:v000014F1d00002F02* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Data/Fax + +pci:v000014F1d00002F11* + ID_MODEL_FROM_DATABASE=HSF 56k HSFi Modem + +pci:v000014F1d00002F20* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem + +pci:v000014F1d00002F20sv000014F1sd0000200C* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Soft Data Fax Modem with SmartCP) + +pci:v000014F1d00002F20sv000014F1sd0000200F* + ID_MODEL_FROM_DATABASE=HSF 56k Data/Fax Modem (Dimension 3000) + +pci:v000014F1d00002F30* + ID_MODEL_FROM_DATABASE=SoftV92 SpeakerPhone SoftRing Modem with SmartSP + +pci:v000014F1d00002F30sv000014F1sd00002014* + ID_MODEL_FROM_DATABASE=SoftV92 SpeakerPhone SoftRing Modem with SmartSP (Devolo MikroLink 56K Modem PCI) + +pci:v000014F1d00002F50* + ID_MODEL_FROM_DATABASE=Conexant SoftK56 Data/Fax Modem + +pci:v000014F1d00005B7A* + ID_MODEL_FROM_DATABASE=CX23418 Single-Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder + +pci:v000014F1d00005B7Asv00000070sd00007444* + ID_MODEL_FROM_DATABASE=CX23418 Single-Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder (WinTV HVR-1600) + +pci:v000014F1d00005B7Asv0000107Dsd00006F34* + ID_MODEL_FROM_DATABASE=CX23418 Single-Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder (WinFast DVR3100 H) + +pci:v000014F1d00005B7Asv00005854sd00003343* + ID_MODEL_FROM_DATABASE=CX23418 Single-Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder (GoTView PCI DVD3 Hybrid) + +pci:v000014F1d00008200* + ID_MODEL_FROM_DATABASE=CX25850 + +pci:v000014F1d00008234* + ID_MODEL_FROM_DATABASE=RS8234 ATM SAR Controller [ServiceSAR Plus] + +pci:v000014F1d00008800* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder + +pci:v000014F1d00008800sv00000070sd00002801* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Hauppauge WinTV 28xxx (Roslyn) models) + +pci:v000014F1d00008800sv00000070sd00003400* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV 34604) + +pci:v000014F1d00008800sv00000070sd00003401* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Hauppauge WinTV 34xxx models) + +pci:v000014F1d00008800sv00000070sd00006902* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV HVR-4000-HD) + +pci:v000014F1d00008800sv00000070sd00007801* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV HVR-1800 MCE) + +pci:v000014F1d00008800sv00000070sd00009001* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Nova-T DVB-T) + +pci:v000014F1d00008800sv00000070sd00009200* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Nova-SE2 DVB-S) + +pci:v000014F1d00008800sv00000070sd00009202* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Nova-S-Plus DVB-S) + +pci:v000014F1d00008800sv00000070sd00009402* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV-HVR1100 DVB-T/Hybrid) + +pci:v000014F1d00008800sv00000070sd00009600* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV 88x Video) + +pci:v000014F1d00008800sv00000070sd00009802* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinTV-HVR1100 DVB-T/Hybrid (Low Profile)) + +pci:v000014F1d00008800sv00001002sd000000F8* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (ATI TV Wonder Pro) + +pci:v000014F1d00008800sv00001002sd000000F9* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (ATI TV Wonder) + +pci:v000014F1d00008800sv00001002sd0000A101* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (HDTV Wonder) + +pci:v000014F1d00008800sv00001043sd00004823* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (ASUS PVR-416) + +pci:v000014F1d00008800sv0000107Dsd00006611* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Winfast TV 2000XP Expert) + +pci:v000014F1d00008800sv0000107Dsd00006613* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Leadtek Winfast 2000XP Expert) + +pci:v000014F1d00008800sv0000107Dsd00006620* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Leadtek Winfast DV2000) + +pci:v000014F1d00008800sv0000107Dsd0000663C* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Leadtek PVR 2000) + +pci:v000014F1d00008800sv0000107Dsd0000665F* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (WinFast DTV1000-T) + +pci:v000014F1d00008800sv000010FCsd0000D003* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (IODATA GV-VCP3/PCI) + +pci:v000014F1d00008800sv000010FCsd0000D035* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (IODATA GV/BCTV7E) + +pci:v000014F1d00008800sv00001421sd00000334* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Instant TV DVB-T PCI) + +pci:v000014F1d00008800sv00001461sd0000000A* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (AVerTV 303 (M126)) + +pci:v000014F1d00008800sv00001461sd0000000B* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (AverTV Studio 303 (M126)) + +pci:v000014F1d00008800sv00001461sd00008011* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (UltraTV Media Center PCI 550) + +pci:v000014F1d00008800sv00001462sd00008606* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (MSI TV-@nywhere Master) + +pci:v000014F1d00008800sv000014C7sd00000107* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (GDI Black Gold) + +pci:v000014F1d00008800sv000014F1sd00000187* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Conexant DVB-T reference design) + +pci:v000014F1d00008800sv000014F1sd00000342* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Digital-Logic MICROSPACE Entertainment Center (MEC)) + +pci:v000014F1d00008800sv0000153Bsd00001166* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Cinergy 1400 DVB-T) + +pci:v000014F1d00008800sv00001540sd00002580* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Provideo PV259) + +pci:v000014F1d00008800sv00001554sd00004811* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (PixelView) + +pci:v000014F1d00008800sv00001554sd00004813* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (Club 3D ZAP1000 MCE Edition) + +pci:v000014F1d00008800sv000017DEsd000008A1* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (KWorld/VStream XPert DVB-T with cx22702) + +pci:v000014F1d00008800sv000017DEsd000008A6* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (KWorld/VStream XPert DVB-T) + +pci:v000014F1d00008800sv000017DEsd000008B2* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (KWorld DVB-S 100) + +pci:v000014F1d00008800sv000017DEsd0000A8A6* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (digitalnow DNTV Live! DVB-T) + +pci:v000014F1d00008800sv00001822sd00000025* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (digitalnow DNTV Live! DVB-T Pro) + +pci:v000014F1d00008800sv0000185Bsd0000E000* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (VideoMate X500) + +pci:v000014F1d00008800sv000018ACsd0000D500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV 5 Gold) + +pci:v000014F1d00008800sv000018ACsd0000D810* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV 3 Gold-Q) + +pci:v000014F1d00008800sv000018ACsd0000D820* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV 3 Gold-T) + +pci:v000014F1d00008800sv000018ACsd0000DB00* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV DVB-T1) + +pci:v000014F1d00008800sv000018ACsd0000DB11* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV DVB-T Plus) + +pci:v000014F1d00008800sv000018ACsd0000DB50* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (FusionHDTV DVB-T Dual Digital) + +pci:v000014F1d00008800sv00005654sd00002388* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (GoTView PCI Hybrid TV Tuner Card) + +pci:v000014F1d00008800sv00007063sd00003000* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (pcHDTV HD3000 HDTV) + +pci:v000014F1d00008800sv00007063sd00005500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder (pcHDTV HD-5500) + +pci:v000014F1d00008801* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] + +pci:v000014F1d00008801sv00000070sd00002801* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (Hauppauge WinTV 28xxx (Roslyn) models) + +pci:v000014F1d00008801sv0000185Bsd0000E000* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (VideoMate X500) + +pci:v000014F1d00008801sv00005654sd00002388* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (GoTView PCI Hybrid Audio AVStream Device) + +pci:v000014F1d00008801sv00007063sd00005500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (pcHDTV HD-5500) + +pci:v000014F1d00008802* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] + +pci:v000014F1d00008802sv00000070sd00002801* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (Hauppauge WinTV 28xxx (Roslyn) models) + +pci:v000014F1d00008802sv00000070sd00006902* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (WinTV HVR-4000-HD) + +pci:v000014F1d00008802sv00000070sd00009002* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (Nova-T DVB-T Model 909) + +pci:v000014F1d00008802sv00000070sd00009402* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (WinTV-HVR1100 DVB-T/Hybrid) + +pci:v000014F1d00008802sv00000070sd00009600* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (WinTV 88x MPEG Encoder) + +pci:v000014F1d00008802sv00001043sd00004823* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (ASUS PVR-416) + +pci:v000014F1d00008802sv0000107Dsd0000663C* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (Leadtek PVR 2000) + +pci:v000014F1d00008802sv0000107Dsd0000665F* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (WinFast DTV1000-T) + +pci:v000014F1d00008802sv000014F1sd00000187* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (Conexant DVB-T reference design) + +pci:v000014F1d00008802sv000017DEsd000008A1* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (XPert DVB-T PCI BDA DVBT 23880 Transport Stream Capture) + +pci:v000014F1d00008802sv000017DEsd000008A6* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (KWorld/VStream XPert DVB-T) + +pci:v000014F1d00008802sv000018ACsd0000D500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (DViCO FusionHDTV5 Gold) + +pci:v000014F1d00008802sv000018ACsd0000D810* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (DViCO FusionHDTV3 Gold-Q) + +pci:v000014F1d00008802sv000018ACsd0000D820* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (DViCO FusionHDTV3 Gold-T) + +pci:v000014F1d00008802sv000018ACsd0000DB00* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (DVICO FusionHDTV DVB-T1) + +pci:v000014F1d00008802sv000018ACsd0000DB10* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (DVICO FusionHDTV DVB-T Plus) + +pci:v000014F1d00008802sv00005654sd00002388* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (GoTView PCI Hybrid TS Capture Device) + +pci:v000014F1d00008802sv00007063sd00003000* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (pcHDTV HD3000 HDTV) + +pci:v000014F1d00008802sv00007063sd00005500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [MPEG Port] (pcHDTV HD-5500) + +pci:v000014F1d00008804* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [IR Port] + +pci:v000014F1d00008804sv00000070sd00006902* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [IR Port] (WinTV HVR-4000-HD) + +pci:v000014F1d00008804sv00000070sd00009002* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [IR Port] (Nova-T DVB-T Model 909) + +pci:v000014F1d00008804sv00000070sd00009402* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [IR Port] (WinTV-HVR1100 DVB-T/Hybrid) + +pci:v000014F1d00008804sv00007063sd00005500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [IR Port] (pcHDTV HD-5500) + +pci:v000014F1d00008811* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] + +pci:v000014F1d00008811sv00000070sd00003400* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (WinTV 34604) + +pci:v000014F1d00008811sv00000070sd00003401* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (Hauppauge WinTV 34xxx models) + +pci:v000014F1d00008811sv00000070sd00006902* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (WinTV HVR-4000-HD) + +pci:v000014F1d00008811sv00000070sd00009402* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (WinTV-HVR1100 DVB-T/Hybrid) + +pci:v000014F1d00008811sv00000070sd00009600* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (WinTV 88x Audio) + +pci:v000014F1d00008811sv00001462sd00008606* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (MSI TV-@nywhere Master) + +pci:v000014F1d00008811sv000018ACsd0000D500* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (DViCO FusionHDTV5 Gold) + +pci:v000014F1d00008811sv000018ACsd0000D810* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (DViCO FusionHDTV3 Gold-Q) + +pci:v000014F1d00008811sv000018ACsd0000D820* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (DViCO FusionHDTV3 Gold-T) + +pci:v000014F1d00008811sv000018ACsd0000DB00* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (DVICO FusionHDTV DVB-T1) + +pci:v000014F1d00008811sv00005654sd00002388* + ID_MODEL_FROM_DATABASE=CX23880/1/2/3 PCI Video and Audio Decoder [Audio Port] (GoTView PCI Hybrid Audio Capture Device) + +pci:v000014F1d00008852* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder + +pci:v000014F1d00008852sv00000070sd00008010* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (WinTV HVR-1400 ExpressCard) + +pci:v000014F1d00008852sv00000070sd0000F038* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (WinTV HVR-5525) + +pci:v000014F1d00008852sv0000107Dsd00006F22* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (WinFast PxTV1200) + +pci:v000014F1d00008852sv000012ABsd0000D585* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (PE988J Hybrid ATSC/QAM PCI-E AVS Video Capture (SoftEncoder)) + +pci:v000014F1d00008852sv000013C2sd00003013* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (TT-budget CT2-4500 CI) + +pci:v000014F1d00008852sv00001461sd0000C039* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (AVerTV Hybrid Express (A577)) + +pci:v000014F1d00008852sv0000153Bsd0000117E* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (Cinergy T PCIe Dual) + +pci:v000014F1d00008852sv000018ACsd0000DB78* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (FusionHDTV DVB-T Dual Express) + +pci:v000014F1d00008852sv00004254sd00000950* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (S950) + +pci:v000014F1d00008852sv00004254sd00000952* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (S952) + +pci:v000014F1d00008852sv00004254sd00000982* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (T982) + +pci:v000014F1d00008852sv00004254sd00009580* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (T9580) + +pci:v000014F1d00008852sv00004254sd0000980C* + ID_MODEL_FROM_DATABASE=CX23885 PCI Video and Audio Decoder (T980C) + +pci:v000014F1d00008880* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb + +pci:v000014F1d00008880sv00000070sd00002259* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb (WinTV HVR-1250) + +pci:v000014F1d00008880sv00000070sd00006A18* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb (WinTV-quadHD) + +pci:v000014F1d00008880sv00000070sd0000C108* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb (WinTV-HVR-4400-HD model 1278) + +pci:v000014F1d00008880sv00005654sd00002389* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb (GoTView X5 DVD Hybrid PCI-E) + +pci:v000014F1d00008880sv00005654sd00002390* + ID_MODEL_FROM_DATABASE=CX23887/8 PCIe Broadcast Audio and Video Decoder with 3D Comb (GoTView X5 3D HYBRID PCI-E) + +pci:v000014F2* + ID_VENDOR_FROM_DATABASE=MOBILITY Electronics + +pci:v000014F2d00000120* + ID_MODEL_FROM_DATABASE=EV1000 bridge + +pci:v000014F2d00000121* + ID_MODEL_FROM_DATABASE=EV1000 Parallel port + +pci:v000014F2d00000122* + ID_MODEL_FROM_DATABASE=EV1000 Serial port + +pci:v000014F2d00000123* + ID_MODEL_FROM_DATABASE=EV1000 Keyboard controller + +pci:v000014F2d00000124* + ID_MODEL_FROM_DATABASE=EV1000 Mouse controller + +pci:v000014F3* + ID_VENDOR_FROM_DATABASE=BroadLogic + +pci:v000014F3d00002030* + ID_MODEL_FROM_DATABASE=2030 DVB-S Satellite Receiver + +pci:v000014F3d00002035* + ID_MODEL_FROM_DATABASE=2035 DVB-S Satellite Receiver + +pci:v000014F3d00002050* + ID_MODEL_FROM_DATABASE=2050 DVB-T Terrestrial (Cable) Receiver + +pci:v000014F3d00002060* + ID_MODEL_FROM_DATABASE=2060 ATSC Terrestrial (Cable) Receiver + +pci:v000014F4* + ID_VENDOR_FROM_DATABASE=TOKYO Electronic Industry CO Ltd + +pci:v000014F5* + ID_VENDOR_FROM_DATABASE=SOPAC Ltd + +pci:v000014F6* + ID_VENDOR_FROM_DATABASE=COYOTE Technologies LLC + +pci:v000014F7* + ID_VENDOR_FROM_DATABASE=WOLF Technology Inc + +pci:v000014F8* + ID_VENDOR_FROM_DATABASE=AUDIOCODES Inc + +pci:v000014F8d00002077* + ID_MODEL_FROM_DATABASE=TP-240 dual span E1 VoIP PCI card + +pci:v000014F9* + ID_VENDOR_FROM_DATABASE=AG COMMUNICATIONS + +pci:v000014FA* + ID_VENDOR_FROM_DATABASE=WANDEL & GOLTERMANN + +pci:v000014FB* + ID_VENDOR_FROM_DATABASE=TRANSAS MARINE (UK) Ltd + +pci:v000014FC* + ID_VENDOR_FROM_DATABASE=Quadrics Ltd + +pci:v000014FCd00000000* + ID_MODEL_FROM_DATABASE=QsNet Elan3 Network Adapter + +pci:v000014FCd00000001* + ID_MODEL_FROM_DATABASE=QsNetII Elan4 Network Adapter + +pci:v000014FCd00000002* + ID_MODEL_FROM_DATABASE=QsNetIII Elan5 Network Adapter + +pci:v000014FD* + ID_VENDOR_FROM_DATABASE=JAPAN Computer Industry Inc + +pci:v000014FE* + ID_VENDOR_FROM_DATABASE=ARCHTEK TELECOM Corp + +pci:v000014FF* + ID_VENDOR_FROM_DATABASE=TWINHEAD INTERNATIONAL Corp + +pci:v00001500* + ID_VENDOR_FROM_DATABASE=DELTA Electronics, Inc + +pci:v00001500d00001360* + ID_MODEL_FROM_DATABASE=RTL81xx RealTek Ethernet + +pci:v00001501* + ID_VENDOR_FROM_DATABASE=BANKSOFT CANADA Ltd + +pci:v00001502* + ID_VENDOR_FROM_DATABASE=MITSUBISHI ELECTRIC LOGISTICS SUPPORT Co Ltd + +pci:v00001503* + ID_VENDOR_FROM_DATABASE=KAWASAKI LSI USA Inc + +pci:v00001504* + ID_VENDOR_FROM_DATABASE=KAISER Electronics + +pci:v00001505* + ID_VENDOR_FROM_DATABASE=ITA INGENIEURBURO FUR TESTAUFGABEN GmbH + +pci:v00001506* + ID_VENDOR_FROM_DATABASE=CHAMELEON Systems Inc + +pci:v00001507* + ID_VENDOR_FROM_DATABASE=Motorola ?? / HTEC + +pci:v00001507d00000001* + ID_MODEL_FROM_DATABASE=MPC105 [Eagle] + +pci:v00001507d00000002* + ID_MODEL_FROM_DATABASE=MPC106 [Grackle] + +pci:v00001507d00000003* + ID_MODEL_FROM_DATABASE=MPC8240 [Kahlua] + +pci:v00001507d00000100* + ID_MODEL_FROM_DATABASE=MC145575 [HFC-PCI] + +pci:v00001507d00000431* + ID_MODEL_FROM_DATABASE=KTI829c 100VG + +pci:v00001507d00004801* + ID_MODEL_FROM_DATABASE=Raven + +pci:v00001507d00004802* + ID_MODEL_FROM_DATABASE=Falcon + +pci:v00001507d00004803* + ID_MODEL_FROM_DATABASE=Hawk + +pci:v00001507d00004806* + ID_MODEL_FROM_DATABASE=CPX8216 + +pci:v00001508* + ID_VENDOR_FROM_DATABASE=HONDA CONNECTORS/MHOTRONICS Inc + +pci:v00001509* + ID_VENDOR_FROM_DATABASE=FIRST INTERNATIONAL Computer Inc + +pci:v0000150A* + ID_VENDOR_FROM_DATABASE=FORVUS RESEARCH Inc + +pci:v0000150B* + ID_VENDOR_FROM_DATABASE=YAMASHITA Systems Corp + +pci:v0000150C* + ID_VENDOR_FROM_DATABASE=KYOPAL CO Ltd + +pci:v0000150D* + ID_VENDOR_FROM_DATABASE=WARPSPPED Inc + +pci:v0000150E* + ID_VENDOR_FROM_DATABASE=C-PORT Corp + +pci:v0000150F* + ID_VENDOR_FROM_DATABASE=INTEC GmbH + +pci:v00001510* + ID_VENDOR_FROM_DATABASE=BEHAVIOR TECH Computer Corp + +pci:v00001511* + ID_VENDOR_FROM_DATABASE=CENTILLIUM Technology Corp + +pci:v00001512* + ID_VENDOR_FROM_DATABASE=ROSUN Technologies Inc + +pci:v00001513* + ID_VENDOR_FROM_DATABASE=Raychem + +pci:v00001514* + ID_VENDOR_FROM_DATABASE=TFL LAN Inc + +pci:v00001515* + ID_VENDOR_FROM_DATABASE=Advent design + +pci:v00001516* + ID_VENDOR_FROM_DATABASE=MYSON Technology Inc + +pci:v00001516d00000800* + ID_MODEL_FROM_DATABASE=MTD-8xx 100/10M Ethernet PCI Adapter + +pci:v00001516d00000803* + ID_MODEL_FROM_DATABASE=SURECOM EP-320X-S 100/10M Ethernet PCI Adapter + +pci:v00001516d00000803sv00001320sd000010BD* + ID_MODEL_FROM_DATABASE=SURECOM EP-320X-S 100/10M Ethernet PCI Adapter + +pci:v00001516d00000891* + ID_MODEL_FROM_DATABASE=MTD-8xx 100/10M Ethernet PCI Adapter + +pci:v00001517* + ID_VENDOR_FROM_DATABASE=ECHOTEK Corp + +pci:v00001518* + ID_VENDOR_FROM_DATABASE=Kontron + +pci:v00001519* + ID_VENDOR_FROM_DATABASE=TELEFON AKTIEBOLAGET LM Ericsson + +pci:v0000151A* + ID_VENDOR_FROM_DATABASE=Globetek + +pci:v0000151Ad00001002* + ID_MODEL_FROM_DATABASE=PCI-1002 + +pci:v0000151Ad00001004* + ID_MODEL_FROM_DATABASE=PCI-1004 + +pci:v0000151Ad00001008* + ID_MODEL_FROM_DATABASE=PCI-1008 + +pci:v0000151B* + ID_VENDOR_FROM_DATABASE=COMBOX Ltd + +pci:v0000151C* + ID_VENDOR_FROM_DATABASE=DIGITAL AUDIO LABS Inc + +pci:v0000151Cd00000003* + ID_MODEL_FROM_DATABASE=Prodif T 2496 + +pci:v0000151Cd00004000* + ID_MODEL_FROM_DATABASE=Prodif 88 + +pci:v0000151D* + ID_VENDOR_FROM_DATABASE=Fujitsu Computer Products Of America + +pci:v0000151E* + ID_VENDOR_FROM_DATABASE=MATRIX Corp + +pci:v0000151F* + ID_VENDOR_FROM_DATABASE=TOPIC SEMICONDUCTOR Corp + +pci:v0000151Fd00000000* + ID_MODEL_FROM_DATABASE=TP560 Data/Fax/Voice 56k modem + +pci:v00001520* + ID_VENDOR_FROM_DATABASE=CHAPLET System Inc + +pci:v00001521* + ID_VENDOR_FROM_DATABASE=BELL Corp + +pci:v00001522* + ID_VENDOR_FROM_DATABASE=MainPine Ltd + +pci:v00001522d00000100* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge + +pci:v00001522d00000100sv00001522sd00000200* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceDUO 2 Port V.92/V.44 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000300* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceQUATRO 4 Port V.92/V.44 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000400* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceDUO+ 2 Port V.92/V.44 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000500* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceQUATRO+ 4 Port V.92/V.44 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000600* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce+ 2 Port V.90 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000700* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForce+ 4 Port V.90 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000800* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceOCTO+ 8 Port V.92/V.44 Data/Fax/Voice Modem) + +pci:v00001522d00000100sv00001522sd00000C00* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceDUO+ 2 Port V.92/V.44 Data, V.34 Super-G3 Fax, Voice Modem) + +pci:v00001522d00000100sv00001522sd00000D00* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceQUATRO+ 4 Port V.92/V.44 Data, V.34 Super-G3 Fax, Voice Modem) + +pci:v00001522d00000100sv00001522sd00001D00* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceOCTO+ 8 Port V.92/V.44 Data, V.34 Super-G3 Fax, Voice Modem) + +pci:v00001522d00000100sv00001522sd00002000* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceD1 1 Port V.90 Data Modem) + +pci:v00001522d00000100sv00001522sd00002100* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceF1 1 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00002200* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceD2 2 Port V.90 Data Modem) + +pci:v00001522d00000100sv00001522sd00002300* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceF2 2 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00002400* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceD4 4 Port V.90 Data Modem) + +pci:v00001522d00000100sv00001522sd00002500* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceF4 4 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00002600* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceD8 8 Port V.90 Data Modem) + +pci:v00001522d00000100sv00001522sd00002700* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (RockForceF8 8 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00003000* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express D1 - 1 Port V.92 Data Modem) + +pci:v00001522d00000100sv00001522sd00003100* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express F1 - 1 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00003200* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express D2 - 2 Port V.92 Data Modem) + +pci:v00001522d00000100sv00001522sd00003300* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express F2 - 2 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00003400* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express D4 - 4 Port V.92 Data Modem) + +pci:v00001522d00000100sv00001522sd00003500* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express F4 - 4 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00000100sv00001522sd00003C00* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express D8 - 8 Port V.92 Data Modem) + +pci:v00001522d00000100sv00001522sd00003D00* + ID_MODEL_FROM_DATABASE=PCI <-> IOBus Bridge (IQ Express F8 - 8 Port V.34 Super-G3 Fax Modem) + +pci:v00001522d00004000* + ID_MODEL_FROM_DATABASE=PCI Express UART + +pci:v00001522d00004000sv00001522sd00004001* + ID_MODEL_FROM_DATABASE=PCI Express UART (IQ Express 1-port V.34 Super-G3 Fax) + +pci:v00001522d00004000sv00001522sd00004002* + ID_MODEL_FROM_DATABASE=PCI Express UART (IQ Express 2-port V.34 Super-G3 Fax) + +pci:v00001522d00004000sv00001522sd00004004* + ID_MODEL_FROM_DATABASE=PCI Express UART (IQ Express 4-port V.34 Super-G3 Fax) + +pci:v00001522d00004000sv00001522sd00004008* + ID_MODEL_FROM_DATABASE=PCI Express UART (IQ Express 8-port V.34 Super-G3 Fax) + +pci:v00001522d00004000sv00001522sd00004100* + ID_MODEL_FROM_DATABASE=PCI Express UART (IQ Express SideBand) + +pci:v00001523* + ID_VENDOR_FROM_DATABASE=MUSIC Semiconductors + +pci:v00001524* + ID_VENDOR_FROM_DATABASE=ENE Technology Inc + +pci:v00001524d00000510* + ID_MODEL_FROM_DATABASE=CB710 Memory Card Reader Controller + +pci:v00001524d00000510sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=CB710 Memory Card Reader Controller (NX9500) + +pci:v00001524d00000520* + ID_MODEL_FROM_DATABASE=FLASH memory: ENE Technology Inc: + +pci:v00001524d00000530* + ID_MODEL_FROM_DATABASE=ENE PCI Memory Stick Card Reader Controller + +pci:v00001524d00000550* + ID_MODEL_FROM_DATABASE=ENE PCI Secure Digital Card Reader Controller + +pci:v00001524d00000551* + ID_MODEL_FROM_DATABASE=SD/MMC Card Reader Controller + +pci:v00001524d00000610* + ID_MODEL_FROM_DATABASE=PCI Smart Card Reader Controller + +pci:v00001524d00000720* + ID_MODEL_FROM_DATABASE=Memory Stick Card Reader Controller + +pci:v00001524d00000730* + ID_MODEL_FROM_DATABASE=ENE PCI Memory Stick Card Reader Controller + +pci:v00001524d00000750* + ID_MODEL_FROM_DATABASE=ENE PCI SmartMedia / xD Card Reader Controller + +pci:v00001524d00000751* + ID_MODEL_FROM_DATABASE=ENE PCI Secure Digital / MMC Card Reader Controller + +pci:v00001524d00001211* + ID_MODEL_FROM_DATABASE=CB1211 Cardbus Controller + +pci:v00001524d00001225* + ID_MODEL_FROM_DATABASE=CB1225 Cardbus Controller + +pci:v00001524d00001410* + ID_MODEL_FROM_DATABASE=CB1410 Cardbus Controller + +pci:v00001524d00001410sv00001025sd0000003C* + ID_MODEL_FROM_DATABASE=CB1410 Cardbus Controller (CL50 motherboard) + +pci:v00001524d00001410sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=CB1410 Cardbus Controller (TravelMate 290) + +pci:v00001524d00001411* + ID_MODEL_FROM_DATABASE=CB-710/2/4 Cardbus Controller + +pci:v00001524d00001411sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=CB-710/2/4 Cardbus Controller (NX9500) + +pci:v00001524d00001412* + ID_MODEL_FROM_DATABASE=CB-712/4 Cardbus Controller + +pci:v00001524d00001420* + ID_MODEL_FROM_DATABASE=CB1420 Cardbus Controller + +pci:v00001524d00001421* + ID_MODEL_FROM_DATABASE=CB-720/2/4 Cardbus Controller + +pci:v00001524d00001422* + ID_MODEL_FROM_DATABASE=CB-722/4 Cardbus Controller + +pci:v00001525* + ID_VENDOR_FROM_DATABASE=IMPACT Technologies + +pci:v00001526* + ID_VENDOR_FROM_DATABASE=ISS, Inc + +pci:v00001527* + ID_VENDOR_FROM_DATABASE=SOLECTRON + +pci:v00001528* + ID_VENDOR_FROM_DATABASE=ACKSYS + +pci:v00001529* + ID_VENDOR_FROM_DATABASE=AMERICAN MICROSystems Inc + +pci:v0000152A* + ID_VENDOR_FROM_DATABASE=QUICKTURN DESIGN Systems + +pci:v0000152B* + ID_VENDOR_FROM_DATABASE=FLYTECH Technology CO Ltd + +pci:v0000152C* + ID_VENDOR_FROM_DATABASE=MACRAIGOR Systems LLC + +pci:v0000152D* + ID_VENDOR_FROM_DATABASE=QUANTA Computer Inc + +pci:v0000152E* + ID_VENDOR_FROM_DATABASE=MELEC Inc + +pci:v0000152F* + ID_VENDOR_FROM_DATABASE=PHILIPS - CRYPTO + +pci:v00001530* + ID_VENDOR_FROM_DATABASE=ACQIS Technology Inc + +pci:v00001531* + ID_VENDOR_FROM_DATABASE=CHRYON Corp + +pci:v00001532* + ID_VENDOR_FROM_DATABASE=ECHELON Corp + +pci:v00001532d00000020* + ID_MODEL_FROM_DATABASE=LonWorks PCLTA-20 PCI LonTalk Adapter + +pci:v00001533* + ID_VENDOR_FROM_DATABASE=BALTIMORE + +pci:v00001534* + ID_VENDOR_FROM_DATABASE=ROAD Corp + +pci:v00001535* + ID_VENDOR_FROM_DATABASE=EVERGREEN Technologies Inc + +pci:v00001536* + ID_VENDOR_FROM_DATABASE=ACTIS Computer + +pci:v00001537* + ID_VENDOR_FROM_DATABASE=DATALEX COMMUNCATIONS + +pci:v00001538* + ID_VENDOR_FROM_DATABASE=ARALION Inc + +pci:v00001538d00000303* + ID_MODEL_FROM_DATABASE=ARS106S Ultra ATA 133/100/66 Host Controller + +pci:v00001539* + ID_VENDOR_FROM_DATABASE=ATELIER INFORMATIQUES et ELECTRONIQUE ETUDES S.A. + +pci:v0000153A* + ID_VENDOR_FROM_DATABASE=ONO SOKKI + +pci:v0000153B* + ID_VENDOR_FROM_DATABASE=TERRATEC Electronic GmbH + +pci:v0000153Bd00001144* + ID_MODEL_FROM_DATABASE=Aureon 5.1 + +pci:v0000153Bd00001147* + ID_MODEL_FROM_DATABASE=Aureon 5.1 Sky + +pci:v0000153Bd00001158* + ID_MODEL_FROM_DATABASE=Philips Semiconductors SAA7134 (rev 01) [Terratec Cinergy 600 TV] + +pci:v0000153C* + ID_VENDOR_FROM_DATABASE=ANTAL Electronic + +pci:v0000153D* + ID_VENDOR_FROM_DATABASE=FILANET Corp + +pci:v0000153E* + ID_VENDOR_FROM_DATABASE=TECHWELL Inc + +pci:v0000153F* + ID_VENDOR_FROM_DATABASE=MIPS Technologies, Inc. + +pci:v0000153Fd00000001* + ID_MODEL_FROM_DATABASE=SOC-it 101 System Controller + +pci:v00001540* + ID_VENDOR_FROM_DATABASE=PROVIDEO MULTIMEDIA Co Ltd + +pci:v00001541* + ID_VENDOR_FROM_DATABASE=MACHONE Communications + +pci:v00001542* + ID_VENDOR_FROM_DATABASE=Concurrent Computer Corporation + +pci:v00001542d00009260* + ID_MODEL_FROM_DATABASE=RCIM-II Real-Time Clock & Interrupt Module + +pci:v00001542d00009271* + ID_MODEL_FROM_DATABASE=RCIM-III Real-Time Clock & Interrupt Module (PCIe) + +pci:v00001542d00009272* + ID_MODEL_FROM_DATABASE=Pulse Width Modulator Card + +pci:v00001542d00009277* + ID_MODEL_FROM_DATABASE=5 Volt Delta Sigma Converter Card + +pci:v00001542d00009278* + ID_MODEL_FROM_DATABASE=10 Volt Delta Sigma Converter Card + +pci:v00001542d00009287* + ID_MODEL_FROM_DATABASE=Analog Output Card + +pci:v00001543* + ID_VENDOR_FROM_DATABASE=SILICON Laboratories + +pci:v00001543d00003052* + ID_MODEL_FROM_DATABASE=Intel 537 [Winmodem] + +pci:v00001543d00004C22* + ID_MODEL_FROM_DATABASE=Si3036 MC'97 DAA + +pci:v00001544* + ID_VENDOR_FROM_DATABASE=DCM DATA Systems + +pci:v00001545* + ID_VENDOR_FROM_DATABASE=VISIONTEK + +pci:v00001546* + ID_VENDOR_FROM_DATABASE=IOI Technology Corp + +pci:v00001547* + ID_VENDOR_FROM_DATABASE=MITUTOYO Corp + +pci:v00001548* + ID_VENDOR_FROM_DATABASE=JET PROPULSION Laboratory + +pci:v00001549* + ID_VENDOR_FROM_DATABASE=INTERCONNECT Systems Solutions + +pci:v0000154A* + ID_VENDOR_FROM_DATABASE=MAX Technologies Inc + +pci:v0000154B* + ID_VENDOR_FROM_DATABASE=COMPUTEX Co Ltd + +pci:v0000154C* + ID_VENDOR_FROM_DATABASE=VISUAL Technology Inc + +pci:v0000154D* + ID_VENDOR_FROM_DATABASE=PAN INTERNATIONAL Industrial Corp + +pci:v0000154E* + ID_VENDOR_FROM_DATABASE=SERVOTEST Ltd + +pci:v0000154F* + ID_VENDOR_FROM_DATABASE=STRATABEAM Technology + +pci:v00001550* + ID_VENDOR_FROM_DATABASE=OPEN NETWORK Co Ltd + +pci:v00001551* + ID_VENDOR_FROM_DATABASE=SMART Electronic DEVELOPMENT GmBH + +pci:v00001552* + ID_VENDOR_FROM_DATABASE=RACAL AIRTECH Ltd + +pci:v00001553* + ID_VENDOR_FROM_DATABASE=CHICONY Electronics Co Ltd + +pci:v00001554* + ID_VENDOR_FROM_DATABASE=PROLINK Microsystems Corp + +pci:v00001555* + ID_VENDOR_FROM_DATABASE=GESYTEC GmBH + +pci:v00001556* + ID_VENDOR_FROM_DATABASE=PLDA + +pci:v00001556d00001100* + ID_MODEL_FROM_DATABASE=PCI Express Core Reference Design + +pci:v00001556d0000110F* + ID_MODEL_FROM_DATABASE=PCI Express Core Reference Design Virtual Function + +pci:v00001557* + ID_VENDOR_FROM_DATABASE=MEDIASTAR Co Ltd + +pci:v00001558* + ID_VENDOR_FROM_DATABASE=CLEVO/KAPOK Computer + +pci:v00001559* + ID_VENDOR_FROM_DATABASE=SI LOGIC Ltd + +pci:v0000155A* + ID_VENDOR_FROM_DATABASE=INNOMEDIA Inc + +pci:v0000155B* + ID_VENDOR_FROM_DATABASE=PROTAC INTERNATIONAL Corp + +pci:v0000155C* + ID_VENDOR_FROM_DATABASE=Cemax-Icon Inc + +pci:v0000155D* + ID_VENDOR_FROM_DATABASE=Mac System Co Ltd + +pci:v0000155E* + ID_VENDOR_FROM_DATABASE=LP Elektronik GmbH + +pci:v0000155F* + ID_VENDOR_FROM_DATABASE=Perle Systems Ltd + +pci:v00001560* + ID_VENDOR_FROM_DATABASE=Terayon Communications Systems + +pci:v00001561* + ID_VENDOR_FROM_DATABASE=Viewgraphics Inc + +pci:v00001562* + ID_VENDOR_FROM_DATABASE=Symbol Technologies + +pci:v00001563* + ID_VENDOR_FROM_DATABASE=A-Trend Technology Co Ltd + +pci:v00001564* + ID_VENDOR_FROM_DATABASE=Yamakatsu Electronics Industry Co Ltd + +pci:v00001565* + ID_VENDOR_FROM_DATABASE=Biostar Microtech Int'l Corp + +pci:v00001566* + ID_VENDOR_FROM_DATABASE=Ardent Technologies Inc + +pci:v00001567* + ID_VENDOR_FROM_DATABASE=Jungsoft + +pci:v00001568* + ID_VENDOR_FROM_DATABASE=DDK Electronics Inc + +pci:v00001569* + ID_VENDOR_FROM_DATABASE=Palit Microsystems Inc. + +pci:v0000156A* + ID_VENDOR_FROM_DATABASE=Avtec Systems + +pci:v0000156B* + ID_VENDOR_FROM_DATABASE=2wire Inc + +pci:v0000156C* + ID_VENDOR_FROM_DATABASE=Vidac Electronics GmbH + +pci:v0000156D* + ID_VENDOR_FROM_DATABASE=Alpha-Top Corp + +pci:v0000156E* + ID_VENDOR_FROM_DATABASE=Alfa Inc + +pci:v0000156F* + ID_VENDOR_FROM_DATABASE=M-Systems Flash Disk Pioneers Ltd + +pci:v00001570* + ID_VENDOR_FROM_DATABASE=Lecroy Corp + +pci:v00001571* + ID_VENDOR_FROM_DATABASE=Contemporary Controls + +pci:v00001571d0000A001* + ID_MODEL_FROM_DATABASE=CCSI PCI20-485 ARCnet + +pci:v00001571d0000A002* + ID_MODEL_FROM_DATABASE=CCSI PCI20-485D ARCnet + +pci:v00001571d0000A003* + ID_MODEL_FROM_DATABASE=CCSI PCI20-485X ARCnet + +pci:v00001571d0000A004* + ID_MODEL_FROM_DATABASE=CCSI PCI20-CXB ARCnet + +pci:v00001571d0000A005* + ID_MODEL_FROM_DATABASE=CCSI PCI20-CXS ARCnet + +pci:v00001571d0000A006* + ID_MODEL_FROM_DATABASE=CCSI PCI20-FOG-SMA ARCnet + +pci:v00001571d0000A007* + ID_MODEL_FROM_DATABASE=CCSI PCI20-FOG-ST ARCnet + +pci:v00001571d0000A008* + ID_MODEL_FROM_DATABASE=CCSI PCI20-TB5 ARCnet + +pci:v00001571d0000A009* + ID_MODEL_FROM_DATABASE=CCSI PCI20-5-485 5Mbit ARCnet + +pci:v00001571d0000A00A* + ID_MODEL_FROM_DATABASE=CCSI PCI20-5-485D 5Mbit ARCnet + +pci:v00001571d0000A00B* + ID_MODEL_FROM_DATABASE=CCSI PCI20-5-485X 5Mbit ARCnet + +pci:v00001571d0000A00C* + ID_MODEL_FROM_DATABASE=CCSI PCI20-5-FOG-ST 5Mbit ARCnet + +pci:v00001571d0000A00D* + ID_MODEL_FROM_DATABASE=CCSI PCI20-5-FOG-SMA 5Mbit ARCnet + +pci:v00001571d0000A201* + ID_MODEL_FROM_DATABASE=CCSI PCI22-485 10Mbit ARCnet + +pci:v00001571d0000A202* + ID_MODEL_FROM_DATABASE=CCSI PCI22-485D 10Mbit ARCnet + +pci:v00001571d0000A203* + ID_MODEL_FROM_DATABASE=CCSI PCI22-485X 10Mbit ARCnet + +pci:v00001571d0000A204* + ID_MODEL_FROM_DATABASE=CCSI PCI22-CHB 10Mbit ARCnet + +pci:v00001571d0000A205* + ID_MODEL_FROM_DATABASE=CCSI PCI22-FOG_ST 10Mbit ARCnet + +pci:v00001571d0000A206* + ID_MODEL_FROM_DATABASE=CCSI PCI22-THB 10Mbit ARCnet + +pci:v00001572* + ID_VENDOR_FROM_DATABASE=Otis Elevator Company + +pci:v00001573* + ID_VENDOR_FROM_DATABASE=Lattice - Vantis + +pci:v00001574* + ID_VENDOR_FROM_DATABASE=Fairchild Semiconductor + +pci:v00001575* + ID_VENDOR_FROM_DATABASE=Voltaire Advanced Data Security Ltd + +pci:v00001576* + ID_VENDOR_FROM_DATABASE=Viewcast COM + +pci:v00001578* + ID_VENDOR_FROM_DATABASE=HITT + +pci:v00001578d00004D34* + ID_MODEL_FROM_DATABASE=VPMK4 [Video Processor Mk IV] + +pci:v00001578d00005615* + ID_MODEL_FROM_DATABASE=VPMK3 [Video Processor Mk III] + +pci:v00001579* + ID_VENDOR_FROM_DATABASE=Dual Technology Corp + +pci:v0000157A* + ID_VENDOR_FROM_DATABASE=Japan Elecronics Ind Inc + +pci:v0000157B* + ID_VENDOR_FROM_DATABASE=Star Multimedia Corp + +pci:v0000157C* + ID_VENDOR_FROM_DATABASE=Eurosoft (UK) + +pci:v0000157Cd00008001* + ID_MODEL_FROM_DATABASE=Fix2000 PCI Y2K Compliance Card + +pci:v0000157D* + ID_VENDOR_FROM_DATABASE=Gemflex Networks + +pci:v0000157E* + ID_VENDOR_FROM_DATABASE=Transition Networks + +pci:v0000157F* + ID_VENDOR_FROM_DATABASE=PX Instruments Technology Ltd + +pci:v00001580* + ID_VENDOR_FROM_DATABASE=Primex Aerospace Co + +pci:v00001581* + ID_VENDOR_FROM_DATABASE=SEH Computertechnik GmbH + +pci:v00001582* + ID_VENDOR_FROM_DATABASE=Cytec Corp + +pci:v00001583* + ID_VENDOR_FROM_DATABASE=Inet Technologies Inc + +pci:v00001584* + ID_VENDOR_FROM_DATABASE=Uniwill Computer Corp + +pci:v00001585* + ID_VENDOR_FROM_DATABASE=Logitron + +pci:v00001586* + ID_VENDOR_FROM_DATABASE=Lancast Inc + +pci:v00001587* + ID_VENDOR_FROM_DATABASE=Konica Corp + +pci:v00001588* + ID_VENDOR_FROM_DATABASE=Solidum Systems Corp + +pci:v00001589* + ID_VENDOR_FROM_DATABASE=Atlantek Microsystems Pty Ltd + +pci:v00001589d00000008* + ID_MODEL_FROM_DATABASE=Leutron Vision PicPortExpress CL + +pci:v00001589d00000009* + ID_MODEL_FROM_DATABASE=Leutron Vision PicPortExpress CL Stereo + +pci:v0000158A* + ID_VENDOR_FROM_DATABASE=Digalog Systems Inc + +pci:v0000158B* + ID_VENDOR_FROM_DATABASE=Allied Data Technologies + +pci:v0000158C* + ID_VENDOR_FROM_DATABASE=Hitachi Semiconductor & Devices Sales Co Ltd + +pci:v0000158D* + ID_VENDOR_FROM_DATABASE=Point Multimedia Systems + +pci:v0000158E* + ID_VENDOR_FROM_DATABASE=Lara Technology Inc + +pci:v0000158F* + ID_VENDOR_FROM_DATABASE=Ditect Coop + +pci:v00001590* + ID_VENDOR_FROM_DATABASE=Hewlett Packard Enterprise + +pci:v00001590d00000001* + ID_MODEL_FROM_DATABASE=Eagle Cluster Manager + +pci:v00001590d00000002* + ID_MODEL_FROM_DATABASE=Osprey Cluster Manager + +pci:v00001590d00000003* + ID_MODEL_FROM_DATABASE=Harrier Cluster Manager + +pci:v00001590d0000A01D* + ID_MODEL_FROM_DATABASE=FC044X Fibre Channel HBA + +pci:v00001591* + ID_VENDOR_FROM_DATABASE=ARN + +pci:v00001592* + ID_VENDOR_FROM_DATABASE=Syba Tech Ltd + +pci:v00001592d00000781* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d00000782* + ID_MODEL_FROM_DATABASE=Parallel Port Card 2xEPP + +pci:v00001592d00000783* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d00000785* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d00000786* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d00000787* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d00000788* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001592d0000078A* + ID_MODEL_FROM_DATABASE=Multi-IO Card + +pci:v00001593* + ID_VENDOR_FROM_DATABASE=Bops Inc + +pci:v00001594* + ID_VENDOR_FROM_DATABASE=Netgame Ltd + +pci:v00001595* + ID_VENDOR_FROM_DATABASE=Diva Systems Corp + +pci:v00001596* + ID_VENDOR_FROM_DATABASE=Folsom Research Inc + +pci:v00001597* + ID_VENDOR_FROM_DATABASE=Memec Design Services + +pci:v00001598* + ID_VENDOR_FROM_DATABASE=Granite Microsystems + +pci:v00001599* + ID_VENDOR_FROM_DATABASE=Delta Electronics Inc + +pci:v0000159A* + ID_VENDOR_FROM_DATABASE=General Instrument + +pci:v0000159B* + ID_VENDOR_FROM_DATABASE=Faraday Technology Corp + +pci:v0000159C* + ID_VENDOR_FROM_DATABASE=Stratus Computer Systems + +pci:v0000159D* + ID_VENDOR_FROM_DATABASE=Ningbo Harrison Electronics Co Ltd + +pci:v0000159E* + ID_VENDOR_FROM_DATABASE=A-Max Technology Co Ltd + +pci:v0000159F* + ID_VENDOR_FROM_DATABASE=Galea Network Security + +pci:v000015A0* + ID_VENDOR_FROM_DATABASE=Compumaster SRL + +pci:v000015A1* + ID_VENDOR_FROM_DATABASE=Geocast Network Systems + +pci:v000015A2* + ID_VENDOR_FROM_DATABASE=Catalyst Enterprises Inc + +pci:v000015A2d00000001* + ID_MODEL_FROM_DATABASE=TA700 PCI Bus Analyzer/Exerciser + +pci:v000015A3* + ID_VENDOR_FROM_DATABASE=Italtel + +pci:v000015A4* + ID_VENDOR_FROM_DATABASE=X-Net OY + +pci:v000015A5* + ID_VENDOR_FROM_DATABASE=Toyota Macs Inc + +pci:v000015A6* + ID_VENDOR_FROM_DATABASE=Sunlight Ultrasound Technologies Ltd + +pci:v000015A7* + ID_VENDOR_FROM_DATABASE=SSE Telecom Inc + +pci:v000015A8* + ID_VENDOR_FROM_DATABASE=Shanghai Communications Technologies Center + +pci:v000015AA* + ID_VENDOR_FROM_DATABASE=Moreton Bay + +pci:v000015AB* + ID_VENDOR_FROM_DATABASE=Bluesteel Networks Inc + +pci:v000015AC* + ID_VENDOR_FROM_DATABASE=North Atlantic Instruments + +pci:v000015ACd00006893* + ID_MODEL_FROM_DATABASE=3U OpenVPX Multi-function I/O Board [Model 68C3] + +pci:v000015AD* + ID_VENDOR_FROM_DATABASE=VMware + +pci:v000015ADd00000405* + ID_MODEL_FROM_DATABASE=SVGA II Adapter + +pci:v000015ADd00000710* + ID_MODEL_FROM_DATABASE=SVGA Adapter + +pci:v000015ADd00000720* + ID_MODEL_FROM_DATABASE=VMXNET Ethernet Controller + +pci:v000015ADd00000740* + ID_MODEL_FROM_DATABASE=Virtual Machine Communication Interface + +pci:v000015ADd00000770* + ID_MODEL_FROM_DATABASE=USB2 EHCI Controller + +pci:v000015ADd00000774* + ID_MODEL_FROM_DATABASE=USB1.1 UHCI Controller + +pci:v000015ADd00000778* + ID_MODEL_FROM_DATABASE=USB3 xHCI 0.96 Controller + +pci:v000015ADd00000779* + ID_MODEL_FROM_DATABASE=USB3 xHCI 1.0 Controller + +pci:v000015ADd00000790* + ID_MODEL_FROM_DATABASE=PCI bridge + +pci:v000015ADd000007A0* + ID_MODEL_FROM_DATABASE=PCI Express Root Port + +pci:v000015ADd000007B0* + ID_MODEL_FROM_DATABASE=VMXNET3 Ethernet Controller + +pci:v000015ADd000007C0* + ID_MODEL_FROM_DATABASE=PVSCSI SCSI Controller + +pci:v000015ADd000007E0* + ID_MODEL_FROM_DATABASE=SATA AHCI controller + +pci:v000015ADd00000801* + ID_MODEL_FROM_DATABASE=Virtual Machine Interface + +pci:v000015ADd00000801sv000015ADsd00000800* + ID_MODEL_FROM_DATABASE=Virtual Machine Interface (Hypervisor ROM Interface) + +pci:v000015ADd00000820* + ID_MODEL_FROM_DATABASE=Paravirtual RDMA controller + +pci:v000015ADd00001977* + ID_MODEL_FROM_DATABASE=HD Audio Controller + +pci:v000015AE* + ID_VENDOR_FROM_DATABASE=Amersham Pharmacia Biotech + +pci:v000015B0* + ID_VENDOR_FROM_DATABASE=Zoltrix International Ltd + +pci:v000015B1* + ID_VENDOR_FROM_DATABASE=Source Technology Inc + +pci:v000015B2* + ID_VENDOR_FROM_DATABASE=Mosaid Technologies Inc + +pci:v000015B3* + ID_VENDOR_FROM_DATABASE=Mellanox Technologies + +pci:v000015B3d00000191* + ID_MODEL_FROM_DATABASE=MT25408 [ConnectX IB Flash Recovery] + +pci:v000015B3d000001F6* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3 Flash Recovery] + +pci:v000015B3d000001FF* + ID_MODEL_FROM_DATABASE=MT27600 Family [Connect-IB Flash Recovery] + +pci:v000015B3d00000209* + ID_MODEL_FROM_DATABASE=MT27700 Family [ConnectX-4 Flash Recovery] + +pci:v000015B3d0000020B* + ID_MODEL_FROM_DATABASE=MT27710 Family [ConnectX-4 Lx Flash Recovery] + +pci:v000015B3d0000020D* + ID_MODEL_FROM_DATABASE=MT28800 Family [ConnectX-5 Flash Recovery] + +pci:v000015B3d00000262* + ID_MODEL_FROM_DATABASE=MT27710 [ConnectX-4 Lx Programmable] EN + +pci:v000015B3d00000263* + ID_MODEL_FROM_DATABASE=MT27710 [ConnectX-4 Lx Programmable Virtual Function] EN + +pci:v000015B3d00001002* + ID_MODEL_FROM_DATABASE=MT25400 Family [ConnectX-2 Virtual Function] + +pci:v000015B3d00001003* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] + +pci:v000015B3d00001003sv0000103Csd00001777* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] (InfiniBand FDR/EN 10/40Gb Dual Port 544FLR-QSFP Adapter (Rev Cx)) + +pci:v000015B3d00001003sv0000103Csd000017C9* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] (Infiniband QDR/Ethernet 10Gb 2-port 544i Adapter) + +pci:v000015B3d00001003sv0000103Csd000018CE* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] (InfiniBand QDR/EN 10Gb Dual Port 544M Adapter) + +pci:v000015B3d00001003sv0000103Csd000018CF* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] (InfiniBand FDR/EN 10/40Gb Dual Port 544M Adapter) + +pci:v000015B3d00001003sv0000103Csd000018D6* + ID_MODEL_FROM_DATABASE=MT27500 Family [ConnectX-3] (InfiniBand FDR/EN 10/40Gb Dual Port 544QSFP Adapter) + +pci:v000015B3d00001004* + ID_MODEL_FROM_DATABASE=MT27500/MT27520 Family [ConnectX-3/ConnectX-3 Pro Virtual Function] + +pci:v000015B3d00001005* + ID_MODEL_FROM_DATABASE=MT27510 Family + +pci:v000015B3d00001006* + ID_MODEL_FROM_DATABASE=MT27511 Family + +pci:v000015B3d00001007* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] + +pci:v000015B3d00001007sv0000103Csd000022F3* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (InfiniBand FDR/Ethernet 10Gb/40Gb 2-port 544+QSFP Adapter) + +pci:v000015B3d00001007sv0000103Csd000022F4* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (InfiniBand FDR/Ethernet 10Gb/40Gb 2-port 544+FLR-QSFP Adapter) + +pci:v000015B3d00001007sv0000103Csd0000801F* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (Ethernet 10G 2-port 546SFP+ Adapter) + +pci:v000015B3d00001007sv0000117Csd00000090* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (FastFrame NQ41) + +pci:v000015B3d00001007sv0000117Csd00000091* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (FastFrame NQ42) + +pci:v000015B3d00001007sv0000117Csd00000092* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (FastFrame NQ11) + +pci:v000015B3d00001007sv0000117Csd00000093* + ID_MODEL_FROM_DATABASE=MT27520 Family [ConnectX-3 Pro] (FastFrame NQ12) + +pci:v000015B3d00001009* + ID_MODEL_FROM_DATABASE=MT27530 Family + +pci:v000015B3d0000100A* + ID_MODEL_FROM_DATABASE=MT27531 Family + +pci:v000015B3d0000100B* + ID_MODEL_FROM_DATABASE=MT27540 Family + +pci:v000015B3d0000100C* + ID_MODEL_FROM_DATABASE=MT27541 Family + +pci:v000015B3d0000100D* + ID_MODEL_FROM_DATABASE=MT27550 Family + +pci:v000015B3d0000100E* + ID_MODEL_FROM_DATABASE=MT27551 Family + +pci:v000015B3d0000100F* + ID_MODEL_FROM_DATABASE=MT27560 Family + +pci:v000015B3d00001010* + ID_MODEL_FROM_DATABASE=MT27561 Family + +pci:v000015B3d00001011* + ID_MODEL_FROM_DATABASE=MT27600 [Connect-IB] + +pci:v000015B3d00001012* + ID_MODEL_FROM_DATABASE=MT27600 Family [Connect-IB Virtual Function] + +pci:v000015B3d00001013* + ID_MODEL_FROM_DATABASE=MT27700 Family [ConnectX-4] + +pci:v000015B3d00001014* + ID_MODEL_FROM_DATABASE=MT27700 Family [ConnectX-4 Virtual Function] + +pci:v000015B3d00001015* + ID_MODEL_FROM_DATABASE=MT27710 Family [ConnectX-4 Lx] + +pci:v000015B3d00001016* + ID_MODEL_FROM_DATABASE=MT27710 Family [ConnectX-4 Lx Virtual Function] + +pci:v000015B3d00001017* + ID_MODEL_FROM_DATABASE=MT27800 Family [ConnectX-5, PCIe 3.0] + +pci:v000015B3d00001018* + ID_MODEL_FROM_DATABASE=MT28800 Family [ConnectX-5 Virtual Function] + +pci:v000015B3d00001019* + ID_MODEL_FROM_DATABASE=MT28800 Family [ConnectX-5, PCIe 4.0] + +pci:v000015B3d0000101A* + ID_MODEL_FROM_DATABASE=MT28830 + +pci:v000015B3d0000101B* + ID_MODEL_FROM_DATABASE=MT28831 + +pci:v000015B3d0000101C* + ID_MODEL_FROM_DATABASE=MT28840 + +pci:v000015B3d0000101D* + ID_MODEL_FROM_DATABASE=MT28841 + +pci:v000015B3d0000101E* + ID_MODEL_FROM_DATABASE=MT28850 + +pci:v000015B3d0000101F* + ID_MODEL_FROM_DATABASE=MT28851 + +pci:v000015B3d00001020* + ID_MODEL_FROM_DATABASE=MT28860 + +pci:v000015B3d00001021* + ID_MODEL_FROM_DATABASE=MT28861 + +pci:v000015B3d00005274* + ID_MODEL_FROM_DATABASE=MT21108 InfiniBridge + +pci:v000015B3d00005A44* + ID_MODEL_FROM_DATABASE=MT23108 InfiniHost + +pci:v000015B3d00005A45* + ID_MODEL_FROM_DATABASE=MT23108 [Infinihost HCA Flash Recovery] + +pci:v000015B3d00005A46* + ID_MODEL_FROM_DATABASE=MT23108 PCI Bridge + +pci:v000015B3d00005E8C* + ID_MODEL_FROM_DATABASE=MT24204 [InfiniHost III Lx HCA] + +pci:v000015B3d00005E8D* + ID_MODEL_FROM_DATABASE=MT25204 [InfiniHost III Lx HCA Flash Recovery] + +pci:v000015B3d00006274* + ID_MODEL_FROM_DATABASE=MT25204 [InfiniHost III Lx HCA] + +pci:v000015B3d00006278* + ID_MODEL_FROM_DATABASE=MT25208 InfiniHost III Ex (Tavor compatibility mode) + +pci:v000015B3d00006279* + ID_MODEL_FROM_DATABASE=MT25208 [InfiniHost III Ex HCA Flash Recovery] + +pci:v000015B3d00006282* + ID_MODEL_FROM_DATABASE=MT25208 [InfiniHost III Ex] + +pci:v000015B3d00006340* + ID_MODEL_FROM_DATABASE=MT25408 [ConnectX VPI - IB SDR / 10GigE] + +pci:v000015B3d0000634A* + ID_MODEL_FROM_DATABASE=MT25418 [ConnectX VPI PCIe 2.0 2.5GT/s - IB DDR / 10GigE] + +pci:v000015B3d00006368* + ID_MODEL_FROM_DATABASE=MT25448 [ConnectX EN 10GigE, PCIe 2.0 2.5GT/s] + +pci:v000015B3d00006372* + ID_MODEL_FROM_DATABASE=MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe 2.0 2.5GT/s] + +pci:v000015B3d00006732* + ID_MODEL_FROM_DATABASE=MT26418 [ConnectX VPI PCIe 2.0 5GT/s - IB DDR / 10GigE] + +pci:v000015B3d0000673C* + ID_MODEL_FROM_DATABASE=MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE] + +pci:v000015B3d0000673Csv0000103Csd00001782* + ID_MODEL_FROM_DATABASE=MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE] (4X QDR InfiniBand Mezzanine HCA for c-Class BladeSystem) + +pci:v000015B3d0000673Csv000015B3sd00000021* + ID_MODEL_FROM_DATABASE=MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE] (HP InfiniBand 4X QDR CX-2 PCI-e G2 Dual Port HCA) + +pci:v000015B3d00006746* + ID_MODEL_FROM_DATABASE=MT26438 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE Virtualization+] + +pci:v000015B3d00006746sv0000103Csd00001781* + ID_MODEL_FROM_DATABASE=MT26438 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE Virtualization+] (NC543i 1-port 4x QDR IB/Flex-10 10Gb Adapter) + +pci:v000015B3d00006746sv0000103Csd00003349* + ID_MODEL_FROM_DATABASE=MT26438 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE Virtualization+] (NC543i 2-port 4xQDR IB/10Gb Adapter) + +pci:v000015B3d00006750* + ID_MODEL_FROM_DATABASE=MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s] + +pci:v000015B3d00006750sv000015B3sd00000018* + ID_MODEL_FROM_DATABASE=MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s] (HP 10 GbE PCI-e G2 Dual-Port NIC (rev C1)) + +pci:v000015B3d0000675A* + ID_MODEL_FROM_DATABASE=MT25408 [ConnectX EN 10GigE 10GBaseT, PCIe Gen2 5GT/s] + +pci:v000015B3d00006764* + ID_MODEL_FROM_DATABASE=MT26468 [ConnectX EN 10GigE, PCIe 2.0 5GT/s Virtualization+] + +pci:v000015B3d00006764sv0000103Csd00003313* + ID_MODEL_FROM_DATABASE=MT26468 [ConnectX EN 10GigE, PCIe 2.0 5GT/s Virtualization+] (NC542m Dual Port Flex-10 10GbE BLc Adapter) + +pci:v000015B3d0000676E* + ID_MODEL_FROM_DATABASE=MT26478 [ConnectX EN 40GigE, PCIe 2.0 5GT/s] + +pci:v000015B3d00006778* + ID_MODEL_FROM_DATABASE=MT26488 [ConnectX VPI PCIe 2.0 5GT/s - IB DDR / 10GigE Virtualization+] + +pci:v000015B3d00007101* + ID_MODEL_FROM_DATABASE=NPS-400 configuration and management interface + +pci:v000015B3d00007102* + ID_MODEL_FROM_DATABASE=NPS-400 network interface PF + +pci:v000015B3d00007103* + ID_MODEL_FROM_DATABASE=NPS-400 network interface VF + +pci:v000015B3d00007121* + ID_MODEL_FROM_DATABASE=NPS-600 configuration and management interface + +pci:v000015B3d00007122* + ID_MODEL_FROM_DATABASE=NPS-600 network interface PF + +pci:v000015B3d00007123* + ID_MODEL_FROM_DATABASE=NPS-600 network interface VF + +pci:v000015B3d0000C738* + ID_MODEL_FROM_DATABASE=MT51136 + +pci:v000015B3d0000C739* + ID_MODEL_FROM_DATABASE=MT51136 GW + +pci:v000015B3d0000C838* + ID_MODEL_FROM_DATABASE=MT52236 + +pci:v000015B3d0000C839* + ID_MODEL_FROM_DATABASE=MT52236 router + +pci:v000015B3d0000CAF1* + ID_MODEL_FROM_DATABASE=ConnectX-4 CAPI Function + +pci:v000015B3d0000CB84* + ID_MODEL_FROM_DATABASE=MT52100 + +pci:v000015B3d0000CF08* + ID_MODEL_FROM_DATABASE=MT53236 + +pci:v000015B3d0000D2F0* + ID_MODEL_FROM_DATABASE=Switch-IB 3 HDR (200Gbps) switch + +pci:v000015B4* + ID_VENDOR_FROM_DATABASE=CCI/TRIAD + +pci:v000015B5* + ID_VENDOR_FROM_DATABASE=Cimetrics Inc + +pci:v000015B6* + ID_VENDOR_FROM_DATABASE=Texas Memory Systems Inc + +pci:v000015B6d00000001* + ID_MODEL_FROM_DATABASE=XP15 DSP Accelerator + +pci:v000015B6d00000002* + ID_MODEL_FROM_DATABASE=XP30 DSP Accelerator + +pci:v000015B6d00000003* + ID_MODEL_FROM_DATABASE=XP00 Data Acquisition Device + +pci:v000015B6d00000004* + ID_MODEL_FROM_DATABASE=XP35 DSP Accelerator + +pci:v000015B6d00000007* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-T0] + +pci:v000015B6d00000008* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-T1] + +pci:v000015B6d00000009* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-E0] + +pci:v000015B6d0000000A* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-E1] + +pci:v000015B6d0000000E* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-0] + +pci:v000015B6d0000000F* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-1] + +pci:v000015B6d00000010* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-P0] + +pci:v000015B6d00000011* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-P1] + +pci:v000015B6d00000012* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-P2] + +pci:v000015B6d00000013* + ID_MODEL_FROM_DATABASE=XP100 DSP Accelerator [XP100-P3] + +pci:v000015B6d00000014* + ID_MODEL_FROM_DATABASE=RamSan Flash SSD + +pci:v000015B6d00000015* + ID_MODEL_FROM_DATABASE=ZBox + +pci:v000015B7* + ID_VENDOR_FROM_DATABASE=Sandisk Corp + +pci:v000015B8* + ID_VENDOR_FROM_DATABASE=ADDI-DATA GmbH + +pci:v000015B8d00001001* + ID_MODEL_FROM_DATABASE=APCI1516 SP controller (16 digi outputs) + +pci:v000015B8d00001003* + ID_MODEL_FROM_DATABASE=APCI1032 SP controller (32 digi inputs w/ opto coupler) + +pci:v000015B8d00001004* + ID_MODEL_FROM_DATABASE=APCI2032 SP controller (32 digi outputs) + +pci:v000015B8d00001005* + ID_MODEL_FROM_DATABASE=APCI2200 SP controller (8/16 digi outputs (relay)) + +pci:v000015B8d00001006* + ID_MODEL_FROM_DATABASE=APCI1564 SP controller (32 digi ins, 32 digi outs) + +pci:v000015B8d0000100A* + ID_MODEL_FROM_DATABASE=APCI1696 SP controller (96 TTL I/Os) + +pci:v000015B8d00003001* + ID_MODEL_FROM_DATABASE=APCI3501 SP controller (analog output board) + +pci:v000015B8d0000300F* + ID_MODEL_FROM_DATABASE=APCI3600 Noise and vibration measurement board + +pci:v000015B8d00007001* + ID_MODEL_FROM_DATABASE=APCI7420 2-port Serial Controller + +pci:v000015B8d00007002* + ID_MODEL_FROM_DATABASE=APCI7300 Serial Controller + +pci:v000015B9* + ID_VENDOR_FROM_DATABASE=Maestro Digital Communications + +pci:v000015BA* + ID_VENDOR_FROM_DATABASE=Impacct Technology Corp + +pci:v000015BB* + ID_VENDOR_FROM_DATABASE=Portwell Inc + +pci:v000015BC* + ID_VENDOR_FROM_DATABASE=Agilent Technologies + +pci:v000015BCd00000100* + ID_MODEL_FROM_DATABASE=HPFC-5600 Tachyon DX2+ FC + +pci:v000015BCd00000103* + ID_MODEL_FROM_DATABASE=QX4 PCI Express quad 4-gigabit Fibre Channel controller + +pci:v000015BCd00000105* + ID_MODEL_FROM_DATABASE=Celerity FC-44XS/FC-42XS/FC-41XS/FC-44ES/FC-42ES/FC-41ES + +pci:v000015BCd00000105sv0000117Csd00000022* + ID_MODEL_FROM_DATABASE=Celerity FC-44XS/FC-42XS/FC-41XS/FC-44ES/FC-42ES/FC-41ES (Celerity FC-42XS Fibre Channel Adapter) + +pci:v000015BCd00000105sv0000117Csd00000025* + ID_MODEL_FROM_DATABASE=Celerity FC-44XS/FC-42XS/FC-41XS/FC-44ES/FC-42ES/FC-41ES (Celerity FC-44ES Fibre Channel Adapter) + +pci:v000015BCd00000105sv0000117Csd00000026* + ID_MODEL_FROM_DATABASE=Celerity FC-44XS/FC-42XS/FC-41XS/FC-44ES/FC-42ES/FC-41ES (Celerity FC-42ES Fibre Channel Adapter) + +pci:v000015BCd00001100* + ID_MODEL_FROM_DATABASE=E8001-66442 PCI Express CIC + +pci:v000015BCd00002922* + ID_MODEL_FROM_DATABASE=64 Bit, 133MHz PCI-X Exerciser & Protocol Checker + +pci:v000015BCd00002928* + ID_MODEL_FROM_DATABASE=64 Bit, 66MHz PCI Exerciser & Analyzer + +pci:v000015BCd00002929* + ID_MODEL_FROM_DATABASE=64 Bit, 133MHz PCI-X Analyzer & Exerciser + +pci:v000015BD* + ID_VENDOR_FROM_DATABASE=DFI Inc + +pci:v000015BE* + ID_VENDOR_FROM_DATABASE=Sola Electronics + +pci:v000015BF* + ID_VENDOR_FROM_DATABASE=High Tech Computer Corp (HTC) + +pci:v000015C0* + ID_VENDOR_FROM_DATABASE=BVM Ltd + +pci:v000015C1* + ID_VENDOR_FROM_DATABASE=Quantel + +pci:v000015C2* + ID_VENDOR_FROM_DATABASE=Newer Technology Inc + +pci:v000015C3* + ID_VENDOR_FROM_DATABASE=Taiwan Mycomp Co Ltd + +pci:v000015C4* + ID_VENDOR_FROM_DATABASE=EVSX Inc + +pci:v000015C5* + ID_VENDOR_FROM_DATABASE=Procomp Informatics Ltd + +pci:v000015C5d00008010* + ID_MODEL_FROM_DATABASE=1394b - 1394 Firewire 3-Port Host Adapter Card + +pci:v000015C6* + ID_VENDOR_FROM_DATABASE=Technical University of Budapest + +pci:v000015C7* + ID_VENDOR_FROM_DATABASE=Tateyama System Laboratory Co Ltd + +pci:v000015C7d00000349* + ID_MODEL_FROM_DATABASE=Tateyama C-PCI PLC/NC card Rev.01A + +pci:v000015C8* + ID_VENDOR_FROM_DATABASE=Penta Media Co Ltd + +pci:v000015C9* + ID_VENDOR_FROM_DATABASE=Serome Technology Inc + +pci:v000015CA* + ID_VENDOR_FROM_DATABASE=Bitboys OY + +pci:v000015CB* + ID_VENDOR_FROM_DATABASE=AG Electronics Ltd + +pci:v000015CC* + ID_VENDOR_FROM_DATABASE=Hotrail Inc + +pci:v000015CD* + ID_VENDOR_FROM_DATABASE=Dreamtech Co Ltd + +pci:v000015CE* + ID_VENDOR_FROM_DATABASE=Genrad Inc + +pci:v000015CF* + ID_VENDOR_FROM_DATABASE=Hilscher GmbH + +pci:v000015CFd00000000* + ID_MODEL_FROM_DATABASE=CIFX 50E-DP(M/S) + +pci:v000015D1* + ID_VENDOR_FROM_DATABASE=Infineon Technologies AG + +pci:v000015D2* + ID_VENDOR_FROM_DATABASE=FIC (First International Computer Inc) + +pci:v000015D3* + ID_VENDOR_FROM_DATABASE=NDS Technologies Israel Ltd + +pci:v000015D4* + ID_VENDOR_FROM_DATABASE=Iwill Corp + +pci:v000015D5* + ID_VENDOR_FROM_DATABASE=Tatung Co + +pci:v000015D6* + ID_VENDOR_FROM_DATABASE=Entridia Corp + +pci:v000015D7* + ID_VENDOR_FROM_DATABASE=Rockwell-Collins Inc + +pci:v000015D8* + ID_VENDOR_FROM_DATABASE=Cybernetics Technology Co Ltd + +pci:v000015D9* + ID_VENDOR_FROM_DATABASE=Super Micro Computer Inc + +pci:v000015DA* + ID_VENDOR_FROM_DATABASE=Cyberfirm Inc + +pci:v000015DB* + ID_VENDOR_FROM_DATABASE=Applied Computing Systems Inc + +pci:v000015DC* + ID_VENDOR_FROM_DATABASE=Litronic Inc + +pci:v000015DCd00000001* + ID_MODEL_FROM_DATABASE=Argus 300 PCI Cryptography Module + +pci:v000015DD* + ID_VENDOR_FROM_DATABASE=Sigmatel Inc + +pci:v000015DE* + ID_VENDOR_FROM_DATABASE=Malleable Technologies Inc + +pci:v000015DF* + ID_VENDOR_FROM_DATABASE=Infinilink Corp + +pci:v000015E0* + ID_VENDOR_FROM_DATABASE=Cacheflow Inc + +pci:v000015E1* + ID_VENDOR_FROM_DATABASE=Voice Technologies Group Inc + +pci:v000015E2* + ID_VENDOR_FROM_DATABASE=Quicknet Technologies Inc + +pci:v000015E2d00000500* + ID_MODEL_FROM_DATABASE=PhoneJack-PCI + +pci:v000015E3* + ID_VENDOR_FROM_DATABASE=Networth Technologies Inc + +pci:v000015E4* + ID_VENDOR_FROM_DATABASE=VSN Systemen BV + +pci:v000015E5* + ID_VENDOR_FROM_DATABASE=Valley technologies Inc + +pci:v000015E6* + ID_VENDOR_FROM_DATABASE=Agere Inc + +pci:v000015E7* + ID_VENDOR_FROM_DATABASE=Get Engineering Corp + +pci:v000015E8* + ID_VENDOR_FROM_DATABASE=National Datacomm Corp + +pci:v000015E8d00000130* + ID_MODEL_FROM_DATABASE=Wireless PCI Card + +pci:v000015E8d00000131* + ID_MODEL_FROM_DATABASE=NCP130A2 Wireless NIC + +pci:v000015E9* + ID_VENDOR_FROM_DATABASE=Pacific Digital Corp + +pci:v000015E9d00001841* + ID_MODEL_FROM_DATABASE=ADMA-100 DiscStaQ ATA Controller + +pci:v000015EA* + ID_VENDOR_FROM_DATABASE=Tokyo Denshi Sekei K.K. + +pci:v000015EB* + ID_VENDOR_FROM_DATABASE=DResearch Digital Media Systems GmbH + +pci:v000015EC* + ID_VENDOR_FROM_DATABASE=Beckhoff GmbH + +pci:v000015ECd00003101* + ID_MODEL_FROM_DATABASE=FC3101 Profibus DP 1 Channel PCI + +pci:v000015ECd00005102* + ID_MODEL_FROM_DATABASE=FC5102 + +pci:v000015ED* + ID_VENDOR_FROM_DATABASE=Macrolink Inc + +pci:v000015EE* + ID_VENDOR_FROM_DATABASE=In Win Development Inc + +pci:v000015EF* + ID_VENDOR_FROM_DATABASE=Intelligent Paradigm Inc + +pci:v000015F0* + ID_VENDOR_FROM_DATABASE=B-Tree Systems Inc + +pci:v000015F1* + ID_VENDOR_FROM_DATABASE=Times N Systems Inc + +pci:v000015F2* + ID_VENDOR_FROM_DATABASE=Diagnostic Instruments Inc + +pci:v000015F3* + ID_VENDOR_FROM_DATABASE=Digitmedia Corp + +pci:v000015F4* + ID_VENDOR_FROM_DATABASE=Valuesoft + +pci:v000015F5* + ID_VENDOR_FROM_DATABASE=Power Micro Research + +pci:v000015F6* + ID_VENDOR_FROM_DATABASE=Extreme Packet Device Inc + +pci:v000015F7* + ID_VENDOR_FROM_DATABASE=Banctec + +pci:v000015F8* + ID_VENDOR_FROM_DATABASE=Koga Electronics Co + +pci:v000015F9* + ID_VENDOR_FROM_DATABASE=Zenith Electronics Corp + +pci:v000015FA* + ID_VENDOR_FROM_DATABASE=J.P. Axzam Corp + +pci:v000015FB* + ID_VENDOR_FROM_DATABASE=Zilog Inc + +pci:v000015FC* + ID_VENDOR_FROM_DATABASE=Techsan Electronics Co Ltd + +pci:v000015FD* + ID_VENDOR_FROM_DATABASE=N-CUBED.NET + +pci:v000015FE* + ID_VENDOR_FROM_DATABASE=Kinpo Electronics Inc + +pci:v000015FF* + ID_VENDOR_FROM_DATABASE=Fastpoint Technologies Inc + +pci:v00001600* + ID_VENDOR_FROM_DATABASE=Northrop Grumman - Canada Ltd + +pci:v00001601* + ID_VENDOR_FROM_DATABASE=Tenta Technology + +pci:v00001602* + ID_VENDOR_FROM_DATABASE=Prosys-tec Inc + +pci:v00001603* + ID_VENDOR_FROM_DATABASE=Nokia Wireless Communications + +pci:v00001604* + ID_VENDOR_FROM_DATABASE=Central System Research Co Ltd + +pci:v00001605* + ID_VENDOR_FROM_DATABASE=Pairgain Technologies + +pci:v00001606* + ID_VENDOR_FROM_DATABASE=Europop AG + +pci:v00001607* + ID_VENDOR_FROM_DATABASE=Lava Semiconductor Manufacturing Inc + +pci:v00001608* + ID_VENDOR_FROM_DATABASE=Automated Wagering International + +pci:v00001609* + ID_VENDOR_FROM_DATABASE=Scimetric Instruments Inc + +pci:v00001612* + ID_VENDOR_FROM_DATABASE=Telesynergy Research Inc. + +pci:v00001618* + ID_VENDOR_FROM_DATABASE=Stone Ridge Technology + +pci:v00001618d00000001* + ID_MODEL_FROM_DATABASE=RDX 11 + +pci:v00001618d00000002* + ID_MODEL_FROM_DATABASE=HFT-01 + +pci:v00001618d00000400* + ID_MODEL_FROM_DATABASE=FarSync T2P (2 port X.21/V.35/V.24) + +pci:v00001618d00000440* + ID_MODEL_FROM_DATABASE=FarSync T4P (4 port X.21/V.35/V.24) + +pci:v00001618d00000610* + ID_MODEL_FROM_DATABASE=FarSync T1U (1 port X.21/V.35/V.24) + +pci:v00001618d00000620* + ID_MODEL_FROM_DATABASE=FarSync T2U (2 port X.21/V.35/V.24) + +pci:v00001618d00000640* + ID_MODEL_FROM_DATABASE=FarSync T4U (4 port X.21/V.35/V.24) + +pci:v00001618d00001610* + ID_MODEL_FROM_DATABASE=FarSync TE1 (T1,E1) + +pci:v00001618d00002610* + ID_MODEL_FROM_DATABASE=FarSync DSL-S1 (SHDSL) + +pci:v00001618d00003640* + ID_MODEL_FROM_DATABASE=FarSync T4E (4-port X.21/V.35/V.24) + +pci:v00001618d00004620* + ID_MODEL_FROM_DATABASE=FarSync T2Ue PCI Express (2-port X.21/V.35/V.24) + +pci:v00001618d00004640* + ID_MODEL_FROM_DATABASE=FarSync T4Ue PCI Express (4-port X.21/V.35/V.24) + +pci:v00001619* + ID_VENDOR_FROM_DATABASE=FarSite Communications Ltd + +pci:v00001619d00000400* + ID_MODEL_FROM_DATABASE=FarSync T2P (2 port X.21/V.35/V.24) + +pci:v00001619d00000440* + ID_MODEL_FROM_DATABASE=FarSync T4P (4 port X.21/V.35/V.24) + +pci:v00001619d00000610* + ID_MODEL_FROM_DATABASE=FarSync T1U (1 port X.21/V.35/V.24) + +pci:v00001619d00000620* + ID_MODEL_FROM_DATABASE=FarSync T2U (2 port X.21/V.35/V.24) + +pci:v00001619d00000640* + ID_MODEL_FROM_DATABASE=FarSync T4U (4 port X.21/V.35/V.24) + +pci:v00001619d00001610* + ID_MODEL_FROM_DATABASE=FarSync TE1 (T1,E1) + +pci:v00001619d00001612* + ID_MODEL_FROM_DATABASE=FarSync TE1 PCI Express (T1,E1) + +pci:v00001619d00002610* + ID_MODEL_FROM_DATABASE=FarSync DSL-S1 (SHDSL) + +pci:v00001619d00003640* + ID_MODEL_FROM_DATABASE=FarSync T4E (4-port X.21/V.35/V.24) + +pci:v00001619d00004620* + ID_MODEL_FROM_DATABASE=FarSync T2Ue PCI Express (2-port X.21/V.35/V.24) + +pci:v00001619d00004640* + ID_MODEL_FROM_DATABASE=FarSync T4Ue PCI Express (4-port X.21/V.35/V.24) + +pci:v00001619d00005621* + ID_MODEL_FROM_DATABASE=FarSync T2Ee PCI Express (2 port X.21/V.35/V.24) + +pci:v00001619d00005641* + ID_MODEL_FROM_DATABASE=FarSync T4Ee PCI Express (4 port X.21/V.35/V.24) + +pci:v00001619d00006620* + ID_MODEL_FROM_DATABASE=FarSync T2U-PMC PCI Express (2 port X.21/V.35/V.24) + +pci:v0000161F* + ID_VENDOR_FROM_DATABASE=Rioworks + +pci:v00001626* + ID_VENDOR_FROM_DATABASE=TDK Semiconductor Corp. + +pci:v00001626d00008410* + ID_MODEL_FROM_DATABASE=RTL81xx Fast Ethernet + +pci:v00001629* + ID_VENDOR_FROM_DATABASE=Kongsberg Spacetec AS + +pci:v00001629d00001003* + ID_MODEL_FROM_DATABASE=Format synchronizer v3.0 + +pci:v00001629d00001006* + ID_MODEL_FROM_DATABASE=Format synchronizer, model 10500 + +pci:v00001629d00001007* + ID_MODEL_FROM_DATABASE=Format synchronizer, model 21000 + +pci:v00001629d00002002* + ID_MODEL_FROM_DATABASE=Fast Universal Data Output + +pci:v00001631* + ID_VENDOR_FROM_DATABASE=Packard Bell B.V. + +pci:v00001638* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp [SMC] + +pci:v00001638d00001100* + ID_MODEL_FROM_DATABASE=SMC2602W EZConnect / Addtron AWA-100 / Eumitcom PCI WL11000 + +pci:v0000163C* + ID_VENDOR_FROM_DATABASE=Smart Link Ltd. + +pci:v0000163Cd00003052* + ID_MODEL_FROM_DATABASE=SmartLink SmartPCI562 56K Modem + +pci:v0000163Cd00005449* + ID_MODEL_FROM_DATABASE=SmartPCI561 Modem + +pci:v00001641* + ID_VENDOR_FROM_DATABASE=MKNet Corp. + +pci:v00001642* + ID_VENDOR_FROM_DATABASE=Bitland(ShenZhen) Information Technology Co., Ltd. + +pci:v00001657* + ID_VENDOR_FROM_DATABASE=Brocade Communications Systems, Inc. + +pci:v00001657d00000013* + ID_MODEL_FROM_DATABASE=425/825/42B/82B 4Gbps/8Gbps PCIe dual port FC HBA + +pci:v00001657d00000013sv0000103Csd00001742* + ID_MODEL_FROM_DATABASE=425/825/42B/82B 4Gbps/8Gbps PCIe dual port FC HBA (82B 8Gbps dual port FC HBA) + +pci:v00001657d00000013sv0000103Csd00001744* + ID_MODEL_FROM_DATABASE=425/825/42B/82B 4Gbps/8Gbps PCIe dual port FC HBA (42B 4Gbps dual port FC HBA) + +pci:v00001657d00000013sv00001657sd00000014* + ID_MODEL_FROM_DATABASE=425/825/42B/82B 4Gbps/8Gbps PCIe dual port FC HBA (425/825 4Gbps/8Gbps PCIe dual port FC HBA) + +pci:v00001657d00000014* + ID_MODEL_FROM_DATABASE=1010/1020/1007/1741 10Gbps CNA + +pci:v00001657d00000014sv00001657sd00000014* + ID_MODEL_FROM_DATABASE=1010/1020/1007/1741 10Gbps CNA (- FCOE) + +pci:v00001657d00000014sv00001657sd00000015* + ID_MODEL_FROM_DATABASE=1010/1020/1007/1741 10Gbps CNA (- LL) + +pci:v00001657d00000017* + ID_MODEL_FROM_DATABASE=415/815/41B/81B 4Gbps/8Gbps PCIe single port FC HBA + +pci:v00001657d00000017sv0000103Csd00001741* + ID_MODEL_FROM_DATABASE=415/815/41B/81B 4Gbps/8Gbps PCIe single port FC HBA (41B 4Gbps single port FC HBA) + +pci:v00001657d00000017sv0000103Csd00001743* + ID_MODEL_FROM_DATABASE=415/815/41B/81B 4Gbps/8Gbps PCIe single port FC HBA (81B 8Gbps single port FC HBA) + +pci:v00001657d00000017sv00001657sd00000014* + ID_MODEL_FROM_DATABASE=415/815/41B/81B 4Gbps/8Gbps PCIe single port FC HBA (415/815 4Gbps/8Gbps single port PCIe FC HBA) + +pci:v00001657d00000021* + ID_MODEL_FROM_DATABASE=804 8Gbps FC HBA for HP Bladesystem c-class + +pci:v00001657d00000022* + ID_MODEL_FROM_DATABASE=1860 16Gbps/10Gbps Fabric Adapter + +pci:v00001657d00000022sv00001657sd00000022* + ID_MODEL_FROM_DATABASE=1860 16Gbps/10Gbps Fabric Adapter (10Gbps CNA - FCOE) + +pci:v00001657d00000022sv00001657sd00000023* + ID_MODEL_FROM_DATABASE=1860 16Gbps/10Gbps Fabric Adapter (10Gbps CNA - LL) + +pci:v00001657d00000022sv00001657sd00000024* + ID_MODEL_FROM_DATABASE=1860 16Gbps/10Gbps Fabric Adapter (16Gbps FC HBA) + +pci:v00001657d00000023* + ID_MODEL_FROM_DATABASE=1867/1869 16Gbps FC HBA + +pci:v00001657d00000646* + ID_MODEL_FROM_DATABASE=400 4Gbps PCIe FC HBA + +pci:v0000165A* + ID_VENDOR_FROM_DATABASE=Epix Inc + +pci:v0000165Ad0000C100* + ID_MODEL_FROM_DATABASE=PIXCI(R) CL1 Camera Link Video Capture Board [custom QL5232] + +pci:v0000165Ad0000D200* + ID_MODEL_FROM_DATABASE=PIXCI(R) D2X Digital Video Capture Board [custom QL5232] + +pci:v0000165Ad0000D300* + ID_MODEL_FROM_DATABASE=PIXCI(R) D3X Digital Video Capture Board [custom QL5232] + +pci:v0000165Ad0000EB01* + ID_MODEL_FROM_DATABASE=PIXCI(R) EB1 PCI Camera Link Video Capture Board + +pci:v0000165C* + ID_VENDOR_FROM_DATABASE=Gidel Ltd. + +pci:v0000165Cd00005361* + ID_MODEL_FROM_DATABASE=PROCStarII60-1 + +pci:v0000165Cd00005362* + ID_MODEL_FROM_DATABASE=PROCStarII60-2 + +pci:v0000165Cd00005364* + ID_MODEL_FROM_DATABASE=PROCStarII60-4 + +pci:v0000165Cd00005435* + ID_MODEL_FROM_DATABASE=ProcSparkII + +pci:v0000165Cd00005661* + ID_MODEL_FROM_DATABASE=ProcE60 + +pci:v0000165Cd000056E1* + ID_MODEL_FROM_DATABASE=ProcE180 + +pci:v0000165Cd00005911* + ID_MODEL_FROM_DATABASE=ProcStarIII110-1 + +pci:v0000165Cd00005912* + ID_MODEL_FROM_DATABASE=ProcStarIII110-2 + +pci:v0000165Cd00005913* + ID_MODEL_FROM_DATABASE=ProcStarIII110-3 + +pci:v0000165Cd00005914* + ID_MODEL_FROM_DATABASE=ProcStarIII110-4 + +pci:v0000165Cd00005921* + ID_MODEL_FROM_DATABASE=ProcStarIII150-1 + +pci:v0000165Cd00005922* + ID_MODEL_FROM_DATABASE=ProcStarIII150-2 + +pci:v0000165Cd00005923* + ID_MODEL_FROM_DATABASE=ProcStarIII150-3 + +pci:v0000165Cd00005924* + ID_MODEL_FROM_DATABASE=ProcStarIII150-4 + +pci:v0000165Cd00005931* + ID_MODEL_FROM_DATABASE=ProcStarIII260-1 + +pci:v0000165Cd00005932* + ID_MODEL_FROM_DATABASE=ProcStarIII260-2 + +pci:v0000165Cd00005933* + ID_MODEL_FROM_DATABASE=ProcStarIII260-3 + +pci:v0000165Cd00005934* + ID_MODEL_FROM_DATABASE=ProcStarIII260-4 + +pci:v0000165Cd00005941* + ID_MODEL_FROM_DATABASE=ProcStarIII340-1 + +pci:v0000165Cd00005942* + ID_MODEL_FROM_DATABASE=ProcStarIII340-2 + +pci:v0000165Cd00005943* + ID_MODEL_FROM_DATABASE=ProcStarIII340-3 + +pci:v0000165Cd00005944* + ID_MODEL_FROM_DATABASE=ProcStarIII340-4 + +pci:v0000165Cd00005A01* + ID_MODEL_FROM_DATABASE=ProceIII80 + +pci:v0000165Cd00005A11* + ID_MODEL_FROM_DATABASE=ProceIII110 + +pci:v0000165Cd00005A21* + ID_MODEL_FROM_DATABASE=ProceIII150 + +pci:v0000165Cd00005A31* + ID_MODEL_FROM_DATABASE=ProceIII260 + +pci:v0000165Cd00005A41* + ID_MODEL_FROM_DATABASE=ProceIII340 + +pci:v0000165Cd00005B51* + ID_MODEL_FROM_DATABASE=ProceIV360 + +pci:v0000165Cd00005B61* + ID_MODEL_FROM_DATABASE=ProceIV530 + +pci:v0000165Cd00005B71* + ID_MODEL_FROM_DATABASE=ProceIV820 + +pci:v0000165Cd00005C01* + ID_MODEL_FROM_DATABASE=ProcStarIV80-1 + +pci:v0000165Cd00005C02* + ID_MODEL_FROM_DATABASE=ProcStarIV80-2 + +pci:v0000165Cd00005C03* + ID_MODEL_FROM_DATABASE=ProcStarIV80-3 + +pci:v0000165Cd00005C04* + ID_MODEL_FROM_DATABASE=ProcStarIV80-4 + +pci:v0000165Cd00005C11* + ID_MODEL_FROM_DATABASE=ProcStarIV110-1 + +pci:v0000165Cd00005C12* + ID_MODEL_FROM_DATABASE=ProcStarIV110-2 + +pci:v0000165Cd00005C13* + ID_MODEL_FROM_DATABASE=ProcStarIV110-3 + +pci:v0000165Cd00005C14* + ID_MODEL_FROM_DATABASE=ProcStarIV110-4 + +pci:v0000165Cd00005C51* + ID_MODEL_FROM_DATABASE=ProcStarIV360-1 + +pci:v0000165Cd00005C52* + ID_MODEL_FROM_DATABASE=ProcStarIV360-2 + +pci:v0000165Cd00005C53* + ID_MODEL_FROM_DATABASE=ProcStarIV360-3 + +pci:v0000165Cd00005C54* + ID_MODEL_FROM_DATABASE=ProcStarIV360-4 + +pci:v0000165Cd00005C61* + ID_MODEL_FROM_DATABASE=ProcStarIV530-1 + +pci:v0000165Cd00005C62* + ID_MODEL_FROM_DATABASE=ProcStarIV530-2 + +pci:v0000165Cd00005C63* + ID_MODEL_FROM_DATABASE=ProcStarIV530-3 + +pci:v0000165Cd00005C64* + ID_MODEL_FROM_DATABASE=ProcStarIV530-4 + +pci:v0000165Cd00005C71* + ID_MODEL_FROM_DATABASE=ProcStarIV820-1 + +pci:v0000165Cd00005C72* + ID_MODEL_FROM_DATABASE=ProcStarIV820-2 + +pci:v0000165Cd00005C73* + ID_MODEL_FROM_DATABASE=ProcStarIV820-3 + +pci:v0000165Cd00005C74* + ID_MODEL_FROM_DATABASE=ProcStarIV820-4 + +pci:v0000165Cd00005D01* + ID_MODEL_FROM_DATABASE=Proc10480 + +pci:v0000165Cd00005D11* + ID_MODEL_FROM_DATABASE=Proc104110 + +pci:v0000165Cd00005F01* + ID_MODEL_FROM_DATABASE=ProceV_A3 + +pci:v0000165Cd00005F11* + ID_MODEL_FROM_DATABASE=ProceV_A7 + +pci:v0000165Cd00005F21* + ID_MODEL_FROM_DATABASE=ProceV_AB + +pci:v0000165Cd00005F31* + ID_MODEL_FROM_DATABASE=ProceV_D5 + +pci:v0000165Cd00005F41* + ID_MODEL_FROM_DATABASE=ProceV_D8 + +pci:v0000165Cd00006732* + ID_MODEL_FROM_DATABASE=Proc6M + +pci:v0000165Cd00006832* + ID_MODEL_FROM_DATABASE=Proc12M + +pci:v0000165Cd00007101* + ID_MODEL_FROM_DATABASE=Proc10a_27 + +pci:v0000165Cd00007111* + ID_MODEL_FROM_DATABASE=Proc10a_48 + +pci:v0000165Cd00007121* + ID_MODEL_FROM_DATABASE=Proc10a_66 + +pci:v0000165Cd00007141* + ID_MODEL_FROM_DATABASE=Proc10a_115 + +pci:v0000165Cd00007181* + ID_MODEL_FROM_DATABASE=Proc10a_27S + +pci:v0000165Cd00007191* + ID_MODEL_FROM_DATABASE=Proc10a_48S + +pci:v0000165Cd000071A1* + ID_MODEL_FROM_DATABASE=Proc10a_66S + +pci:v0000165D* + ID_VENDOR_FROM_DATABASE=Hsing Tech. Enterprise Co., Ltd. + +pci:v0000165F* + ID_VENDOR_FROM_DATABASE=Linux Media Labs, LLC + +pci:v0000165Fd00001020* + ID_MODEL_FROM_DATABASE=LMLM4 MPEG-4 encoder + +pci:v00001661* + ID_VENDOR_FROM_DATABASE=Worldspace Corp. + +pci:v00001668* + ID_VENDOR_FROM_DATABASE=Actiontec Electronics Inc + +pci:v00001668d00000100* + ID_MODEL_FROM_DATABASE=Mini-PCI bridge + +pci:v0000166D* + ID_VENDOR_FROM_DATABASE=Broadcom Corporation + +pci:v0000166Dd00000001* + ID_MODEL_FROM_DATABASE=SiByte BCM1125/1125H/1250 System-on-a-Chip PCI + +pci:v0000166Dd00000002* + ID_MODEL_FROM_DATABASE=SiByte BCM1125H/1250 System-on-a-Chip HyperTransport + +pci:v0000166Dd00000012* + ID_MODEL_FROM_DATABASE=SiByte BCM1280/BCM1480 System-on-a-Chip PCI-X + +pci:v0000166Dd00000014* + ID_MODEL_FROM_DATABASE=Sibyte BCM1280/BCM1480 System-on-a-Chip HyperTransport + +pci:v00001677* + ID_VENDOR_FROM_DATABASE=Bernecker + Rainer + +pci:v00001677d0000104E* + ID_MODEL_FROM_DATABASE=5LS172.6 B&R Dual CAN Interface Card + +pci:v00001677d000012D7* + ID_MODEL_FROM_DATABASE=5LS172.61 B&R Dual CAN Interface Card + +pci:v00001677d000020AD* + ID_MODEL_FROM_DATABASE=5ACPCI.MFIO-K01 Profibus DP / K-Feldbus / COM + +pci:v00001678* + ID_VENDOR_FROM_DATABASE=NetEffect + +pci:v00001678d00000100* + ID_MODEL_FROM_DATABASE=NE020 10Gb Accelerated Ethernet Adapter (iWARP RNIC) + +pci:v00001679* + ID_VENDOR_FROM_DATABASE=Tokyo Electron Device Ltd. + +pci:v00001679d00003000* + ID_MODEL_FROM_DATABASE=SD Standard host controller [Ellen] + +pci:v0000167B* + ID_VENDOR_FROM_DATABASE=ZyDAS Technology Corp. + +pci:v0000167Bd00002102* + ID_MODEL_FROM_DATABASE=ZyDAS ZD1202 + +pci:v0000167Bd00002102sv0000187Esd00003406* + ID_MODEL_FROM_DATABASE=ZyDAS ZD1202 (ZyAIR B-122 CardBus 11Mbs Wireless LAN Card) + +pci:v0000167Bd00002102sv0000187Esd00003407* + ID_MODEL_FROM_DATABASE=ZyDAS ZD1202 (ZyAIR B-320 802.11b Wireless PCI Adapter) + +pci:v0000167Bd00002116* + ID_MODEL_FROM_DATABASE=ZD1212B Wireless Adapter + +pci:v0000167D* + ID_VENDOR_FROM_DATABASE=Samsung Electro-Mechanics Co., Ltd. + +pci:v0000167Dd0000A000* + ID_MODEL_FROM_DATABASE=MagicLAN SWL-2210P 802.11b [Intersil ISL3874] + +pci:v0000167E* + ID_VENDOR_FROM_DATABASE=ONNTO Corp. + +pci:v00001681* + ID_VENDOR_FROM_DATABASE=Hercules + +pci:v00001682* + ID_VENDOR_FROM_DATABASE=XFX Pine Group Inc. + +pci:v00001688* + ID_VENDOR_FROM_DATABASE=CastleNet Technology Inc. + +pci:v00001688d00001170* + ID_MODEL_FROM_DATABASE=WLAN 802.11b card + +pci:v0000168C* + ID_VENDOR_FROM_DATABASE=Qualcomm Atheros + +pci:v0000168Cd00000007* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] + +pci:v0000168Cd00000007sv00001737sd00000007* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] (WPC54A Wireless PC Card) + +pci:v0000168Cd00000007sv00001B47sd00000100* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] (Harmony 8450CN Wireless CardBus Module) + +pci:v0000168Cd00000007sv00001B47sd00000110* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] (Skyline 4030 / Harmony 8450 802.11a Wireless CardBus Adapter) + +pci:v0000168Cd00000007sv00008086sd00002501* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] (PRO/Wireless 5000 LAN PCI Adapter Module) + +pci:v0000168Cd00000011* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001A 802.11a] + +pci:v0000168Cd00000012* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] + +pci:v0000168Cd00000012sv00001186sd00003A03* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (AirPro DWL-A650 Wireless Cardbus Adapter (rev.B)) + +pci:v0000168Cd00000012sv00001186sd00003A04* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (AirPro DWL-AB650 Multimode Wireless Cardbus Adapter) + +pci:v0000168Cd00000012sv00001186sd00003A05* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (AirPro DWL-AB520 Multimode Wireless PCI Adapter) + +pci:v0000168Cd00000012sv0000126Csd00008031* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (2201 Mobile Adapter) + +pci:v0000168Cd00000012sv00001385sd00004400* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (WAB501 802.11ab Wireless CardBus Card) + +pci:v0000168Cd00000012sv00001B47sd0000AA00* + ID_MODEL_FROM_DATABASE=AR5211 Wireless Network Adapter [AR5001X 802.11ab] (8460 802.11ab Wireless CardBus Adapter) + +pci:v0000168Cd00000013* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter + +pci:v0000168Cd00000013sv00000308sd00003402* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AG-100 802.11ag Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00000308sd00003405* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (G-102 v2 802.11g Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00000308sd00003408* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (G-170S 802.11g Wireless CardBus Adapter) + +pci:v0000168Cd00000013sv00000E11sd000000E5* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (NC6000/NC8000 laptop) + +pci:v0000168Cd00000013sv000010B7sd00006002* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (3CRWE154A72 802.11abg Cardbus Adapter) + +pci:v0000168Cd00000013sv00001113sd0000D301* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Philips CPWNA100 Wireless CardBus adapter) + +pci:v0000168Cd00000013sv00001113sd0000EE23* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (SMCWPCIT-G 108Mbps Wireless PCI adapter) + +pci:v0000168Cd00000013sv00001154sd0000033B* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Buffalo WLI-CB-AMG54) + +pci:v0000168Cd00000013sv00001154sd0000034E* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Buffalo WLI-CB-AG108HP 802.11abg Cardbus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003202* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (DWL-G650 (Rev B3,B5) Wireless cardbus adapter) + +pci:v0000168Cd00000013sv00001186sd00003203* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. A)) + +pci:v0000168Cd00000013sv00001186sd00003A07* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirXpert DWL-AG650 Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A08* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirXpert DWL-AG520 Wireless PCI Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A12* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPlus DWL-G650 Wireless Cardbus Adapter(rev.C)) + +pci:v0000168Cd00000013sv00001186sd00003A13* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus DWL-G520 Wireless PCI Adapter (rev. B)) + +pci:v0000168Cd00000013sv00001186sd00003A14* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPremier AG DWL-AG530 Wireless PCI Adapter (rev.A)) + +pci:v0000168Cd00000013sv00001186sd00003A17* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-G680 Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A18* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-G550 Wireless PCI Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A1A* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WNA-2330 802.11bg Wireless CardBus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A63* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPremier DWL-AG660 Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003A93* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Conceptronic C54I Wireless 801.11g PCI card) + +pci:v0000168Cd00000013sv00001186sd00003A94* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Conceptronic C54C 802.11g Wireless Cardbus Adapter) + +pci:v0000168Cd00000013sv00001186sd00003AB0* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Allnet ALL0281 Wireless PCI Card) + +pci:v0000168Cd00000013sv00001385sd00004600* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card) + +pci:v0000168Cd00000013sv00001385sd00004610* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG511 802.11a/b/g Dual Band Wireless PC Card) + +pci:v0000168Cd00000013sv00001385sd00004900* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311v1 802.11g Wireless PCI Adapter) + +pci:v0000168Cd00000013sv00001385sd00004A00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WAG311 802.11a/g Wireless PCI Adapter) + +pci:v0000168Cd00000013sv00001385sd00004B00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.A/B)) + +pci:v0000168Cd00000013sv00001385sd00004D00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311T 108 Mbps Wireless PCI Adapter (rev.A2)) + +pci:v0000168Cd00000013sv00001385sd00004F00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511U Double 108 Mbps Wireless PC Card) + +pci:v0000168Cd00000013sv00001385sd00005A00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG311T (rev.A3 v1h3/v1h4) 108 Mbps Wireless PCI Adapter [AR2412]) + +pci:v0000168Cd00000013sv00001385sd00005B00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WG511T 108 Mbps Wireless PC Card (rev.C)) + +pci:v0000168Cd00000013sv00001385sd00005D00* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPN511 RangeMax Wireless PC Card) + +pci:v0000168Cd00000013sv00001458sd0000E911* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (GN-WIAG02) + +pci:v0000168Cd00000013sv00001468sd00000403* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (U10H014 802.11g Cardbus Adapter) + +pci:v0000168Cd00000013sv00001468sd00000408* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (ThinkPad 11b/g Wireless LAN Mini PCI Adapter) + +pci:v0000168Cd00000013sv000014B7sd00000A10* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8480-WD 802.11abg Cardbus Adapter) + +pci:v0000168Cd00000013sv000014B7sd00000A60* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8482-WD ORiNOCO 11a/b/g Wireless PCI Adapter) + +pci:v0000168Cd00000013sv000014B7sd0000AA30* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8800-FC 802.11bg Cardbus Adapter) + +pci:v0000168Cd00000013sv000014B7sd0000AA40* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (8470-WD 802.11bg Cardbus Adapter) + +pci:v0000168Cd00000013sv000014B9sd0000CB21* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (CB21 802.11a/b/g Cardbus Adapter) + +pci:v0000168Cd00000013sv00001668sd00001026* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (IBM HighRate 11 a/b/g Wireless CardBus Adapter) + +pci:v0000168Cd00000013sv0000168Csd00000013* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (AirPlus XtremeG DWL-G650 Wireless PCMCIA Adapter) + +pci:v0000168Cd00000013sv0000168Csd00001025* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (DWL-G650B2 Wireless CardBus Adapter) + +pci:v0000168Cd00000013sv0000168Csd00001027* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius NL-3054CB ARIES b/g CardBus Adapter) + +pci:v0000168Cd00000013sv0000168Csd00001042* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Ubiquiti Networks SuperRange a/b/g Cardbus Adapter) + +pci:v0000168Cd00000013sv0000168Csd00001051* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (EZ Connect g 802.11g 108Mbps Wireless PCI Adapter) + +pci:v0000168Cd00000013sv0000168Csd00002026* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Netgate 5354MP ARIES a(108Mb turbo)/b/g MiniPCI Adapter) + +pci:v0000168Cd00000013sv0000168Csd00002027* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (D-Link AirPlus DWL-G520 Wireless PCI Adapter (rev. A)) + +pci:v0000168Cd00000013sv0000168Csd00002041* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 b/g MiniPCI Adapter) + +pci:v0000168Cd00000013sv0000168Csd00002042* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Engenius 5354MP Plus ARIES2 a/b/g MiniPCI Adapter) + +pci:v0000168Cd00000013sv0000168Csd00002051* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (TRENDnet TEW-443PI Wireless PCI Adapter) + +pci:v0000168Cd00000013sv000016A5sd0000160A* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (BWP712 802.11bg Wireless CardBus Adapter) + +pci:v0000168Cd00000013sv000016ABsd00007302* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Trust Speedshare Turbo Pro Wireless PCI Adapter) + +pci:v0000168Cd00000013sv00001737sd00000017* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPC55AG) + +pci:v0000168Cd00000013sv00001737sd00000026* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WMP55AG v1.1) + +pci:v0000168Cd00000013sv00001737sd00000035* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WPC55AG v1.2 802.11abg Cardbus Adapter) + +pci:v0000168Cd00000013sv00001737sd00000036* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (WMP55AG v1.2 802.11abg PCI Adapter) + +pci:v0000168Cd00000013sv00001799sd00003000* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (F6D3000 Dual-Band Wireless A+G Desktop Card) + +pci:v0000168Cd00000013sv00001799sd00003010* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (F6D3010 Dual-Band Wireless A+G Notebook Card) + +pci:v0000168Cd00000013sv000017CFsd00000042* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Z-COMAX Highpower XG-622H (400mw) 802.11b/g mini-PCI Adapter) + +pci:v0000168Cd00000013sv0000185Fsd00001012* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (CM9 Wireless a/b/g MiniPCI Adapter) + +pci:v0000168Cd00000013sv0000185Fsd00002012* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (Wistron NeWeb WLAN a+b+g model CB9) + +pci:v0000168Cd00000013sv0000A727sd00006801* + ID_MODEL_FROM_DATABASE=AR5212/5213/2414 Wireless Network Adapter (3CRXJK10075 OfficeConnect Wireless 108Mbps 11g XJACK PC Card) + +pci:v0000168Cd0000001A* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] + +pci:v0000168Cd0000001Asv00001052sd0000168C* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (Sweex Wireless Lan PC Card 54Mbps) + +pci:v0000168Cd0000001Asv00001113sd0000EE20* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (SMC Wireless CardBus Adapter 802.11g (SMCWCB-G EU)) + +pci:v0000168Cd0000001Asv00001113sd0000EE24* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (SMC Wireless PCI Card WPCI-G) + +pci:v0000168Cd0000001Asv00001186sd00003A15* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.D1)) + +pci:v0000168Cd0000001Asv00001186sd00003A16* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (AirPlus G DWL-G510 Wireless PCI Adapter(rev.B)) + +pci:v0000168Cd0000001Asv00001186sd00003A1C* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (WNA-1330 Notebook Adapter) + +pci:v0000168Cd0000001Asv00001186sd00003A1D* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (WDA-1320 Desktop Adapter) + +pci:v0000168Cd0000001Asv00001186sd00003A23* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (AirPlus G DWL-G520+A Wireless PCI Adapter) + +pci:v0000168Cd0000001Asv00001186sd00003A24* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (AirPlus G DWL-G650+A Wireless Cardbus Adapter) + +pci:v0000168Cd0000001Asv00001186sd00003B08* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.C1)) + +pci:v0000168Cd0000001Asv0000168Csd0000001A* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (Belkin FD7000) + +pci:v0000168Cd0000001Asv0000168Csd00001052* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (TP-Link TL-WN510G Wireless CardBus Adapter) + +pci:v0000168Cd0000001Asv0000168Csd00002052* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (Compex Wireless 802.11 b/g MiniPCI Adapter, Rev A1 [WLM54G]) + +pci:v0000168Cd0000001Asv000016ECsd00000122* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (Wireless PCI Adapter Model 5418) + +pci:v0000168Cd0000001Asv00001737sd00000053* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (WPC54G v7 802.11g Wireless-G Notebook Adapter) + +pci:v0000168Cd0000001Asv00001799sd0000700C* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (F5D7000 v5000 Wireless G Desktop Card) + +pci:v0000168Cd0000001Asv00001799sd0000701D* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (F5D7010 v5000 Wireless G Notebook Card) + +pci:v0000168Cd0000001Asv000017F9sd00000008* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (DX-WGNBC 802.11bg Wireless CardBus Adapter) + +pci:v0000168Cd0000001Asv000017F9sd00000018* + ID_MODEL_FROM_DATABASE=AR2413/AR2414 Wireless Network Adapter [AR5005G(S) 802.11bg] (DX-WGDTC 802.11bg Wireless PCI Adapter) + +pci:v0000168Cd0000001B* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] + +pci:v0000168Cd0000001Bsv00000777sd00001107* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (UB5 802.11a Wireless Mini PCI Adapter) + +pci:v0000168Cd0000001Bsv00000777sd00003002* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (XR2 802.11g Wireless Mini PCI Adapter) + +pci:v0000168Cd0000001Bsv00000777sd00003005* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (XR5 802.11a Wireless Mini PCI Adapter) + +pci:v0000168Cd0000001Bsv00000777sd00003009* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (XR9 900MHz Wireless Mini PCI Adapter) + +pci:v0000168Cd0000001Bsv00001154sd0000034E* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (WLI-CB-AG108HP 802.11abg Wireless CardBus Adapter) + +pci:v0000168Cd0000001Bsv00001186sd00003A19* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (D-Link AirPremier AG DWL-AG660 Wireless Cardbus Adapter) + +pci:v0000168Cd0000001Bsv00001186sd00003A22* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (AirPremier AG DWL-AG530 Wireless PCI Adapter (rev.B)) + +pci:v0000168Cd0000001Bsv000011ADsd00005001* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (WN5301A 802.11bg Wireless PCI Adapter) + +pci:v0000168Cd0000001Bsv00001458sd0000E901* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (GN-WI01HT Wireless a/b/g MiniPCI Adapter) + +pci:v0000168Cd0000001Bsv0000168Csd0000001B* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (Wireless LAN PCI LiteOn) + +pci:v0000168Cd0000001Bsv0000168Csd00001062* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (IPN-W100CB 802.11abg Wireless CardBus Adapter) + +pci:v0000168Cd0000001Bsv0000168Csd00002062* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (EnGenius EMP-8602 (400mw) or Compex WLM54AG (SuperAG)) + +pci:v0000168Cd0000001Bsv0000168Csd00002063* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (EnGenius EMP-8602 (400mw) or Compex WLM54AG) + +pci:v0000168Cd0000001Bsv000017F9sd0000000B* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (WL-711A 802.11abg Wireless CardBus Adapter) + +pci:v0000168Cd0000001Bsv000017F9sd0000000C* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (WPIA-112AG 802.11abg Wireless PCI Adapter) + +pci:v0000168Cd0000001Bsv000017F9sd0000000D* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (PC-686X 802.11abg Wireless Mini PCI Adapter) + +pci:v0000168Cd0000001Bsv0000185Fsd00001600* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (DCMA-82 High Power WLAN 802.11a/b/g mini-PCI Module (Super A/G, eXtended Range, 400mW)) + +pci:v0000168Cd0000001Bsv00001948sd00003ABA* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (RBTBJ-AW 802.11abg Wireless Cardbus Adapter) + +pci:v0000168Cd0000001Bsv0000A727sd00006804* + ID_MODEL_FROM_DATABASE=AR5413/AR5414 Wireless Network Adapter [AR5006X(S) 802.11abg] (Wireless 11a/b/g PC Card with XJACK(r) Antenna) + +pci:v0000168Cd0000001C* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) + +pci:v0000168Cd0000001Csv00000777sd00003006* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (SRX 802.11abg Wireless ExpressCard Adapter) + +pci:v0000168Cd0000001Csv0000103Csd0000137A* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5BXB63 (Foxconn) 802.11bg Mini PCIe NIC) + +pci:v0000168Cd0000001Csv0000106Bsd00000086* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AirPort Extreme) + +pci:v0000168Cd0000001Csv0000144Fsd00007106* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (WLL3140 (Toshiba PA3501U-1MPC) 802.11bg Wireless Mini PCIe Card) + +pci:v0000168Cd0000001Csv0000144Fsd00007128* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (WLL3141 (Toshiba PA3613U-1MPC) 802.11bg Wireless Mini PCIe Card) + +pci:v0000168Cd0000001Csv00001468sd00000428* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5BXB63 802.11bg NIC) + +pci:v0000168Cd0000001Csv00001468sd0000042A* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5007EG 802.11bg NIC) + +pci:v0000168Cd0000001Csv0000147Bsd00001033* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AirPace Wi-Fi) + +pci:v0000168Cd0000001Csv0000168Csd0000001C* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR242x 802.11abg NIC (PCI Express)) + +pci:v0000168Cd0000001Csv0000168Csd00003061* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5006EGS 802.11bg NIC (2.4GHz, PCI Express)) + +pci:v0000168Cd0000001Csv0000168Csd00003062* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5006EXS 802.11abg NIC (2.4/5.0GHz, PCI Express)) + +pci:v0000168Cd0000001Csv0000168Csd00003063* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5006EX 802.11abg NIC (2.4/5.0GHz, PCI Express)) + +pci:v0000168Cd0000001Csv0000168Csd00003065* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR5006EG 802.11bg NIC (2.4GHz, PCI Express)) + +pci:v0000168Cd0000001Csv0000168Csd00003067* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AR242x 802.11abg Wireless PCI Express Adapter (rev 01)) + +pci:v0000168Cd0000001Csv00001A3Bsd00001026* + ID_MODEL_FROM_DATABASE=AR242x / AR542x Wireless Network Adapter (PCI-Express) (AW-GE780 802.11bg Wireless Mini PCIe Card) + +pci:v0000168Cd0000001D* + ID_MODEL_FROM_DATABASE=AR2417 Wireless Network Adapter [AR5007G 802.11bg] + +pci:v0000168Cd0000001Dsv00001799sd0000720B* + ID_MODEL_FROM_DATABASE=AR2417 Wireless Network Adapter [AR5007G 802.11bg] (F5D7000 v8000 Wireless G Desktop Card) + +pci:v0000168Cd0000001Dsv00001799sd0000721B* + ID_MODEL_FROM_DATABASE=AR2417 Wireless Network Adapter [AR5007G 802.11bg] (F5D7010 v8000 Wireless G Notebook Card) + +pci:v0000168Cd00000020* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC + +pci:v0000168Cd00000020sv00000308sd00003407* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC (M-102 802.11g Wireless Cardbus Adapter) + +pci:v0000168Cd00000020sv00001186sd00003A67* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC (DWL-G650M Super G MIMO Wireless Notebook Adapter) + +pci:v0000168Cd00000020sv00001186sd00003A68* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC (DWL-G520M Wireless 108G MIMO Desktop Adapter) + +pci:v0000168Cd00000020sv0000187Esd0000340E* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC (M-302 802.11g Wireless PCI Adapter) + +pci:v0000168Cd00000020sv00001976sd00002003* + ID_MODEL_FROM_DATABASE=AR5513 802.11abg Wireless NIC (TEW-601PC 802.11g Wireless CardBus Adapter) + +pci:v0000168Cd00000023* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] + +pci:v0000168Cd00000023sv00000308sd0000340B* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (NWD-170N 802.11bgn Wireless CardBus Adapter) + +pci:v0000168Cd00000023sv00001154sd00000365* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (Buffalo WLP-CB-AG300 802.11abgn Cardbus Adapter) + +pci:v0000168Cd00000023sv00001154sd00000367* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (WLI-CB-AG301N 802.11abgn Wireless CardBus Adapter) + +pci:v0000168Cd00000023sv00001186sd00003A6A* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (DWA-642 802.11n RangeBooster N CardBus Adapter) + +pci:v0000168Cd00000023sv00001186sd00003A6B* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (DWA-547 802.11n RangeBooster N 650 DeskTop Adapter) + +pci:v0000168Cd00000023sv00001186sd00003A6D* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (DWA-552 802.11n Xtreme N Desktop Adapter (rev A1)) + +pci:v0000168Cd00000023sv00001186sd00003A76* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (DWA-645 802.11n RangeBooster N 650 Notebook Adapter (rev A1)) + +pci:v0000168Cd00000023sv00001737sd00000059* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (WPC300N v2 Wireless-N Notebook Adapter) + +pci:v0000168Cd00000023sv00001737sd00000069* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (WPC100 v1 802.11n RangePlus Wireless Notebook Adapter) + +pci:v0000168Cd00000023sv00001737sd00000072* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (WMP110 v1 802.11n RangePlus Wireless PCI Adapter) + +pci:v0000168Cd00000023sv00001799sd00008011* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (F5D8011 v1 802.11n N1 Wireless Notebook Card) + +pci:v0000168Cd00000023sv0000187Esd00003411* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (NWD-370N 802.11n Wireless PCI Adapter) + +pci:v0000168Cd00000023sv00001976sd00002008* + ID_MODEL_FROM_DATABASE=AR5416 Wireless Network Adapter [AR5008 802.11(a)bgn] (TEW-621PC 802.11bgn Wireless CardBus Adapter) + +pci:v0000168Cd00000024* + ID_MODEL_FROM_DATABASE=AR5418 Wireless Network Adapter [AR5008E 802.11(a)bgn] (PCI-Express) + +pci:v0000168Cd00000024sv0000106Bsd00000087* + ID_MODEL_FROM_DATABASE=AR5418 Wireless Network Adapter [AR5008E 802.11(a)bgn] (PCI-Express) (AirPort Extreme) + +pci:v0000168Cd00000024sv00001186sd00003A70* + ID_MODEL_FROM_DATABASE=AR5418 Wireless Network Adapter [AR5008E 802.11(a)bgn] (PCI-Express) (DWA-556 Xtreme N PCI Express Desktop Adapter) + +pci:v0000168Cd00000027* + ID_MODEL_FROM_DATABASE=AR9160 Wireless Network Adapter [AR9001 802.11(a)bgn] + +pci:v0000168Cd00000027sv00000777sd00004082* + ID_MODEL_FROM_DATABASE=AR9160 Wireless Network Adapter [AR9001 802.11(a)bgn] (SR71-A 802.11abgn Wireless Mini PCI Adapter) + +pci:v0000168Cd00000029* + ID_MODEL_FROM_DATABASE=AR922X Wireless Network Adapter + +pci:v0000168Cd00000029sv00000777sd00004005* + ID_MODEL_FROM_DATABASE=AR922X Wireless Network Adapter (SR71-15 802.11an Mini PCI Adapter) + +pci:v0000168Cd00000029sv00001186sd00003A7A* + ID_MODEL_FROM_DATABASE=AR922X Wireless Network Adapter (DWA-552 802.11n Xtreme N Desktop Adapter (rev A2)) + +pci:v0000168Cd00000029sv00001186sd00003A7D* + ID_MODEL_FROM_DATABASE=AR922X Wireless Network Adapter (DWA-552 802.11n Xtreme N Desktop Adapter (rev A3)) + +pci:v0000168Cd00000029sv0000168Csd00002096* + ID_MODEL_FROM_DATABASE=AR922X Wireless Network Adapter (Compex WLM200NX / Wistron DNMA-92) + +pci:v0000168Cd0000002A* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) + +pci:v0000168Cd0000002Asv00000777sd00004F05* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (SR71-X 802.11abgn Wireless ExpressCard Adapter [AR9280]) + +pci:v0000168Cd0000002Asv0000103Csd00003041* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AR5BHB92-H 802.11abgn Wireless Half-size Mini PCIe Card [AR9280]) + +pci:v0000168Cd0000002Asv0000103Csd00003042* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AzureWave AW-NE773 802.11abgn Wireless Half-size Mini PCIe Card [AR9280]) + +pci:v0000168Cd0000002Asv0000105Bsd0000E006* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (T77H053.00 802.11bgn Wireless Mini PCIe Card [AR9281]) + +pci:v0000168Cd0000002Asv0000105Bsd0000E01F* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (T77H047.31 802.11bgn Wireless Half-size Mini PCIe Card [AR9283]) + +pci:v0000168Cd0000002Asv0000106Bsd0000008F* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AirPort Extreme) + +pci:v0000168Cd0000002Asv000011ADsd00006600* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (WN6600A 802.11bgn Wireless Mini PCIe Card [AR9281]) + +pci:v0000168Cd0000002Asv0000144Fsd00007141* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (WLL6080 802.11bgn Wireless Mini PCIe Card [AR9281]) + +pci:v0000168Cd0000002Asv0000168Csd00000203* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (DW1525 802.11abgn WLAN PCIe Card [AR9280]) + +pci:v0000168Cd0000002Asv00001A32sd00000303* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (EM303 802.11bgn Wireless Mini PCIe Card [AR9281]) + +pci:v0000168Cd0000002Asv00001A32sd00000306* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (EM306 802.11bgn Wireless Half-size Mini PCIe Card [AR9283]) + +pci:v0000168Cd0000002Asv00001A3Bsd00001067* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AW-NE771 802.11bgn Wireless Mini PCIe Card [AR9281]) + +pci:v0000168Cd0000002Asv00001A3Bsd00001081* + ID_MODEL_FROM_DATABASE=AR928X Wireless Network Adapter (PCI-Express) (AW-NE773 802.11abgn Wireless Half-size Mini PCIe Card [AR9280]) + +pci:v0000168Cd0000002B* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) + +pci:v0000168Cd0000002Bsv00001028sd00000204* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (Wireless 1502 802.11bgn Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv00001028sd00000205* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (Wireless 1702 802.11bgn Half-size Mini PCIe Card [AR9002WB-1NGCD]) + +pci:v0000168Cd0000002Bsv0000103Csd0000303F* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (U98Z062.10 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv0000103Csd00003040* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (U98Z062.12 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv0000105Bsd0000E017* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (T77H126.00 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv0000105Bsd0000E023* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (T77H121.04 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv0000105Bsd0000E025* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (T77H121.05 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv00001113sd0000E811* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (WN7811A (Toshiba PA3722U-1MPC) 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv0000185Fsd000030AF* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (DNXA-95 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv00001931sd00000023* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (Option GTM67x PCIe WiFi Adapter) + +pci:v0000168Cd0000002Bsv00001A3Bsd00001089* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (AW-NE785 / AW-NE785H 802.11bgn Wireless Full or Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv00001A3Bsd00002C37* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (AW-NB037H 802.11bgn Wireless Half-size Mini PCIe Card [AR9002WB-1NGCD]) + +pci:v0000168Cd0000002Bsv00001B9Asd00000401* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (XW204E 802.11bgn Wireless Half-size Mini PCIe Card) + +pci:v0000168Cd0000002Bsv00001B9Asd00000C03* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) (WB214E 802.11bgn Wireless Half-size Mini PCIe Card [AR9002WB-1NGCD]) + +pci:v0000168Cd0000002C* + ID_MODEL_FROM_DATABASE=AR2427 802.11bg Wireless Network Adapter (PCI-Express) + +pci:v0000168Cd0000002D* + ID_MODEL_FROM_DATABASE=AR9227 Wireless Network Adapter + +pci:v0000168Cd0000002E* + ID_MODEL_FROM_DATABASE=AR9287 Wireless Network Adapter (PCI-Express) + +pci:v0000168Cd0000002Esv0000105Bsd0000E034* + ID_MODEL_FROM_DATABASE=AR9287 Wireless Network Adapter (PCI-Express) (T77H167.00) + +pci:v0000168Cd00000030* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter + +pci:v0000168Cd00000030sv0000103Csd00001627* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter (AR9380/HB112 802.11abgn 3×3 Wi-Fi Adapter) + +pci:v0000168Cd00000030sv0000106Bsd0000009A* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter (AirPort Extreme) + +pci:v0000168Cd00000030sv00001186sd00003A7E* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter (DWA-566 Wireless N 300 Dual Band PCIe Desktop Adapter) + +pci:v0000168Cd00000030sv00001A56sd00002000* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter (Killer Wireless-N 1102 Half-size Mini PCIe Card [AR9382]) + +pci:v0000168Cd00000030sv00001A56sd00002001* + ID_MODEL_FROM_DATABASE=AR93xx Wireless Network Adapter (Killer Wireless-N 1103 Half-size Mini PCIe Card [AR9380]) + +pci:v0000168Cd00000032* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter + +pci:v0000168Cd00000032sv00001028sd00000208* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter (Wireless 1506 WLAN Half Mini-Card) + +pci:v0000168Cd00000032sv0000103Csd00001838* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter (AR9485/HB125 802.11bgn 1×1 Wi-Fi Adapter) + +pci:v0000168Cd00000032sv0000105Bsd0000E044* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter (Unex DHXA-225) + +pci:v0000168Cd00000032sv00001A3Bsd00001186* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter (AW-NE186H) + +pci:v0000168Cd00000033* + ID_MODEL_FROM_DATABASE=AR958x 802.11abgn Wireless Network Adapter + +pci:v0000168Cd00000033sv0000168Csd0000A120* + ID_MODEL_FROM_DATABASE=AR958x 802.11abgn Wireless Network Adapter (AR9582 802.11a/n WLAN Mini-PCIe Adapter) + +pci:v0000168Cd00000034* + ID_MODEL_FROM_DATABASE=AR9462 Wireless Network Adapter + +pci:v0000168Cd00000034sv00001028sd00000300* + ID_MODEL_FROM_DATABASE=AR9462 Wireless Network Adapter (Wireless 1802 802.11abgn Adapter) + +pci:v0000168Cd00000034sv00001A56sd00002003* + ID_MODEL_FROM_DATABASE=AR9462 Wireless Network Adapter (Killer Wireless-N 1202 Half-size Mini PCIe Card) + +pci:v0000168Cd00000036* + ID_MODEL_FROM_DATABASE=QCA9565 / AR9565 Wireless Network Adapter + +pci:v0000168Cd00000037* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter + +pci:v0000168Cd00000037sv00001A3Bsd00002100* + ID_MODEL_FROM_DATABASE=AR9485 Wireless Network Adapter (AW-NB100H 802.11n Wireless Mini PCIe Card) + +pci:v0000168Cd0000003C* + ID_MODEL_FROM_DATABASE=QCA986x/988x 802.11ac Wireless Network Adapter + +pci:v0000168Cd0000003E* + ID_MODEL_FROM_DATABASE=QCA6174 802.11ac Wireless Network Adapter + +pci:v0000168Cd0000003Esv00001A56sd00001525* + ID_MODEL_FROM_DATABASE=QCA6174 802.11ac Wireless Network Adapter (Killer N1525 Wireless-AC) + +pci:v0000168Cd00000040* + ID_MODEL_FROM_DATABASE=QCA9980/9990 802.11ac Wireless Network Adapter + +pci:v0000168Cd00000041* + ID_MODEL_FROM_DATABASE=QCA6164 802.11ac Wireless Network Adapter + +pci:v0000168Cd00000042* + ID_MODEL_FROM_DATABASE=QCA9377 802.11ac Wireless Network Adapter + +pci:v0000168Cd00000050* + ID_MODEL_FROM_DATABASE=QCA9887 802.11ac Wireless Network Adapter + +pci:v0000168Cd00000207* + ID_MODEL_FROM_DATABASE=AR5210 Wireless Network Adapter [AR5000 802.11a] + +pci:v0000168Cd00001014* + ID_MODEL_FROM_DATABASE=AR5212 802.11abg NIC + +pci:v0000168Cd00001014sv00001014sd0000058A* + ID_MODEL_FROM_DATABASE=AR5212 802.11abg NIC (ThinkPad 11a/b/g Wireless LAN Mini Express Adapter (AR5BXB6)) + +pci:v0000168Cd00009013* + ID_MODEL_FROM_DATABASE=AR5002X Wireless Network Adapter + +pci:v0000168Cd0000FF19* + ID_MODEL_FROM_DATABASE=AR5006X Wireless Network Adapter + +pci:v0000168Cd0000FF1B* + ID_MODEL_FROM_DATABASE=AR2425 Wireless Network Adapter [AR5007EG 802.11bg] + +pci:v0000168Cd0000FF1C* + ID_MODEL_FROM_DATABASE=AR5008 Wireless Network Adapter + +pci:v0000168Cd0000FF1D* + ID_MODEL_FROM_DATABASE=AR922x Wireless Network Adapter + +pci:v0000168Cd0000FF1Dsv0000168Csd0000EE1C* + ID_MODEL_FROM_DATABASE=AR922x Wireless Network Adapter (AR9220-AC1A [AVM Fritz!Box FON WLAN 7270 v3]) + +pci:v00001695* + ID_VENDOR_FROM_DATABASE=EPoX Computer Co., Ltd. + +pci:v0000169C* + ID_VENDOR_FROM_DATABASE=Netcell Corporation + +pci:v0000169Cd00000044* + ID_MODEL_FROM_DATABASE=Revolution Storage Processing Card + +pci:v0000169D* + ID_VENDOR_FROM_DATABASE=Club-3D VB (Wrong ID) + +pci:v000016A5* + ID_VENDOR_FROM_DATABASE=Tekram Technology Co.,Ltd. + +pci:v000016AB* + ID_VENDOR_FROM_DATABASE=Global Sun Technology Inc + +pci:v000016ABd00001100* + ID_MODEL_FROM_DATABASE=GL24110P + +pci:v000016ABd00001101* + ID_MODEL_FROM_DATABASE=PLX9052 PCMCIA-to-PCI Wireless LAN + +pci:v000016ABd00001102* + ID_MODEL_FROM_DATABASE=PCMCIA-to-PCI Wireless Network Bridge + +pci:v000016ABd00008501* + ID_MODEL_FROM_DATABASE=WL-8305 Wireless LAN PCI Adapter + +pci:v000016AE* + ID_VENDOR_FROM_DATABASE=SafeNet Inc + +pci:v000016AEd00000001* + ID_MODEL_FROM_DATABASE=SafeXcel 1140 + +pci:v000016AEd0000000A* + ID_MODEL_FROM_DATABASE=SafeXcel 1841 + +pci:v000016AEd00001141* + ID_MODEL_FROM_DATABASE=SafeXcel 1141 + +pci:v000016AEd00001141sv00000001sd00000001* + ID_MODEL_FROM_DATABASE=SafeXcel 1141 (v. 1.1) + +pci:v000016AEd00001841* + ID_MODEL_FROM_DATABASE=SafeXcel 1842 + +pci:v000016AF* + ID_VENDOR_FROM_DATABASE=SparkLAN Communications, Inc. + +pci:v000016B4* + ID_VENDOR_FROM_DATABASE=Aspex Semiconductor Ltd + +pci:v000016B8* + ID_VENDOR_FROM_DATABASE=Sonnet Technologies, Inc. + +pci:v000016BE* + ID_VENDOR_FROM_DATABASE=Creatix Polymedia GmbH + +pci:v000016C3* + ID_VENDOR_FROM_DATABASE=Synopsys, Inc. + +pci:v000016C6* + ID_VENDOR_FROM_DATABASE=Micrel-Kendin + +pci:v000016C6d00008695* + ID_MODEL_FROM_DATABASE=Centaur KS8695 ARM processor + +pci:v000016C6d00008842* + ID_MODEL_FROM_DATABASE=KSZ8842-PMQL 2-Port Ethernet Switch + +pci:v000016C8* + ID_VENDOR_FROM_DATABASE=Octasic Inc. + +pci:v000016C9* + ID_VENDOR_FROM_DATABASE=EONIC B.V. The Netherlands + +pci:v000016CA* + ID_VENDOR_FROM_DATABASE=CENATEK Inc + +pci:v000016CAd00000001* + ID_MODEL_FROM_DATABASE=Rocket Drive DL + +pci:v000016CD* + ID_VENDOR_FROM_DATABASE=Advantech Co. Ltd + +pci:v000016CDd00000101* + ID_MODEL_FROM_DATABASE=DirectPCI SRAM for DPX-11x series + +pci:v000016CDd00000102* + ID_MODEL_FROM_DATABASE=DirectPCI SRAM for DPX-S/C/E-series + +pci:v000016CDd00000103* + ID_MODEL_FROM_DATABASE=DirectPCI ROM for DPX-11x series + +pci:v000016CDd00000104* + ID_MODEL_FROM_DATABASE=DirectPCI ROM for DPX-S/C/E-series + +pci:v000016CDd00000105* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-114/DPX-115 + +pci:v000016CDd00000106* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-116 + +pci:v000016CDd00000107* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-116U + +pci:v000016CDd00000108* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-117 + +pci:v000016CDd00000109* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-112 + +pci:v000016CDd0000010A* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-C/E-series + +pci:v000016CDd0000010B* + ID_MODEL_FROM_DATABASE=DirectPCI I/O for DPX-S series + +pci:v000016CE* + ID_VENDOR_FROM_DATABASE=Roland Corp. + +pci:v000016D5* + ID_VENDOR_FROM_DATABASE=Acromag, Inc. + +pci:v000016D5d00000504* + ID_MODEL_FROM_DATABASE=PMC-DX504 Reconfigurable FPGA with LVDS I/O + +pci:v000016D5d00000520* + ID_MODEL_FROM_DATABASE=PMC520 Serial Communication, 232 Octal + +pci:v000016D5d00000521* + ID_MODEL_FROM_DATABASE=PMC521 Serial Communication, 422/485 Octal + +pci:v000016D5d00001020* + ID_MODEL_FROM_DATABASE=PMC-AX1020 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00001065* + ID_MODEL_FROM_DATABASE=PMC-AX1065 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00002004* + ID_MODEL_FROM_DATABASE=PMC-DX2004 Reconfigurable FPGA with LVDS I/O + +pci:v000016D5d00002020* + ID_MODEL_FROM_DATABASE=PMC-AX2020 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00002065* + ID_MODEL_FROM_DATABASE=PMC-AX2065 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00003020* + ID_MODEL_FROM_DATABASE=PMC-AX3020 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00003065* + ID_MODEL_FROM_DATABASE=PMC-AX3065 Reconfigurable FPGA with A/D & D/A + +pci:v000016D5d00004243* + ID_MODEL_FROM_DATABASE=PMC424, APC424, AcPC424 Digital I/O and Counter Timer Module + +pci:v000016D5d00004248* + ID_MODEL_FROM_DATABASE=PMC464, APC464, AcPC464 Digital I/O and Counter Timer Module + +pci:v000016D5d0000424B* + ID_MODEL_FROM_DATABASE=PMC-DX2002 Reconfigurable FPGA with Differential I/O + +pci:v000016D5d00004253* + ID_MODEL_FROM_DATABASE=PMC-DX503 Reconfigurable FPGA with TTL and Differential I/O + +pci:v000016D5d00004312* + ID_MODEL_FROM_DATABASE=PMC-CX1002 Reconfigurable Conduction-Cooled FPGA Virtex-II with Differential I/O + +pci:v000016D5d00004313* + ID_MODEL_FROM_DATABASE=PMC-CX1003 Reconfigurable Conduction-Cooled FPGA Virtex-II with CMOS and Differential I/O + +pci:v000016D5d00004322* + ID_MODEL_FROM_DATABASE=PMC-CX2002 Reconfigurable Conduction-Cooled FPGA Virtex-II with Differential I/O + +pci:v000016D5d00004323* + ID_MODEL_FROM_DATABASE=PMC-CX2003 Reconfigurable Conduction-Cooled FPGA Virtex-II with CMOS and Differential I/O + +pci:v000016D5d00004350* + ID_MODEL_FROM_DATABASE=PMC-DX501 Reconfigurable Digital I/O Module + +pci:v000016D5d00004353* + ID_MODEL_FROM_DATABASE=PMC-DX2003 Reconfigurable FPGA with TTL and Differential I/O + +pci:v000016D5d00004357* + ID_MODEL_FROM_DATABASE=PMC-DX502 Reconfigurable Differential I/O Module + +pci:v000016D5d00004457* + ID_MODEL_FROM_DATABASE=PMC730, APC730, AcPC730 Multifunction Module + +pci:v000016D5d0000464D* + ID_MODEL_FROM_DATABASE=PMC408 32-Channel Digital Input/Output Module + +pci:v000016D5d00004850* + ID_MODEL_FROM_DATABASE=PMC220-16 12-Bit Analog Output Module + +pci:v000016D5d00004A42* + ID_MODEL_FROM_DATABASE=PMC483, APC483, AcPC483 Counter Timer Module + +pci:v000016D5d00004A50* + ID_MODEL_FROM_DATABASE=PMC484, APC484, AcPC484 Counter Timer Module + +pci:v000016D5d00004A56* + ID_MODEL_FROM_DATABASE=PMC230 16-Bit Analog Output Module + +pci:v000016D5d00004B47* + ID_MODEL_FROM_DATABASE=PMC330, APC330, AcPC330 Analog Input Module, 16-bit A/D + +pci:v000016D5d00004C40* + ID_MODEL_FROM_DATABASE=PMC-LX40 Reconfigurable Virtex-4 FPGA with plug-in I/O + +pci:v000016D5d00004C60* + ID_MODEL_FROM_DATABASE=PMC-LX60 Reconfigurable Virtex-4 FPGA with plug-in I/O + +pci:v000016D5d00004D4D* + ID_MODEL_FROM_DATABASE=PMC341, APC341, AcPC341 Analog Input Module, Simultaneous Sample & Hold + +pci:v000016D5d00004D4E* + ID_MODEL_FROM_DATABASE=PMC482, APC482, AcPC482 Counter Timer Board + +pci:v000016D5d0000524D* + ID_MODEL_FROM_DATABASE=PMC-DX2001 Reconfigurable FPGA with TTL I/O + +pci:v000016D5d00005335* + ID_MODEL_FROM_DATABASE=PMC-SX35 Reconfigurable Virtex-4 FPGA with plug-in I/O + +pci:v000016D5d00005456* + ID_MODEL_FROM_DATABASE=PMC470 48-Channel Digital Input/Output Module + +pci:v000016D5d00005601* + ID_MODEL_FROM_DATABASE=PMC-VLX85 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005602* + ID_MODEL_FROM_DATABASE=PMC-VLX110 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005603* + ID_MODEL_FROM_DATABASE=PMC-VSX95 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005604* + ID_MODEL_FROM_DATABASE=PMC-VLX155 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005605* + ID_MODEL_FROM_DATABASE=PMC-VFX70 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005606* + ID_MODEL_FROM_DATABASE=PMC-VLX155-1M Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005701* + ID_MODEL_FROM_DATABASE=PMC-SLX150: Reconfigurable Spartan-6 FPGA with plug-in I/O + +pci:v000016D5d00005702* + ID_MODEL_FROM_DATABASE=PMC-SLX150-1M: Reconfigurable Spartan-6 FPGA with plug-in I/O + +pci:v000016D5d00005801* + ID_MODEL_FROM_DATABASE=XMC-VLX85 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005802* + ID_MODEL_FROM_DATABASE=XMC-VLX110 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005803* + ID_MODEL_FROM_DATABASE=XMC-VSX95 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005804* + ID_MODEL_FROM_DATABASE=XMC-VLX155 Reconfigurable Virtex-5 FPGA with plug-in I/O + +pci:v000016D5d00005807* + ID_MODEL_FROM_DATABASE=XMC-SLX150: Reconfigurable Spartan-6 FPGA with plug-in I/O + +pci:v000016D5d00005808* + ID_MODEL_FROM_DATABASE=XMC-SLX150-1M: Reconfigurable Spartan-6 FPGA with plug-in I/O + +pci:v000016D5d00005901* + ID_MODEL_FROM_DATABASE=APCe8650 PCI Express IndustryPack Carrier Card + +pci:v000016D5d00006301* + ID_MODEL_FROM_DATABASE=XMC Module with user-configurable Virtex-6 FPGA, 240k logic cells, SFP front I/O + +pci:v000016D5d00006302* + ID_MODEL_FROM_DATABASE=XMC Module with user-configurable Virtex-6 FPGA, 365k logic cells, SFP front I/O + +pci:v000016D5d00006303* + ID_MODEL_FROM_DATABASE=XMC Module with user-configurable Virtex-6 FPGA, 240k logic cells, no front I/O + +pci:v000016D5d00006304* + ID_MODEL_FROM_DATABASE=XMC Module with user-configurable Virtex-6 FPGA, 365k logic cells, no front I/O + +pci:v000016D5d00007000* + ID_MODEL_FROM_DATABASE=XMC-7K325F: User-configurable Kintex-7 FPGA, 325k logic cells plus SFP front I/O + +pci:v000016D5d00007001* + ID_MODEL_FROM_DATABASE=XMC-7K410F: User-configurable Kintex-7 FPGA, 410k logic cells plus SFP front I/O + +pci:v000016D5d00007002* + ID_MODEL_FROM_DATABASE=XMC-7K325AX: User-Configurable Kintex-7 FPGA, 325k logic cells with AXM Plug-In I/O + +pci:v000016D5d00007003* + ID_MODEL_FROM_DATABASE=XMC-7K410AX: User-Configurable Kintex-7 FPGA, 410k logic cells with AXM Plug-In I/O + +pci:v000016D5d00007004* + ID_MODEL_FROM_DATABASE=XMC-7K325CC: User-Configurable Kintex-7 FPGA, 325k logic cells, conduction-cooled + +pci:v000016D5d00007005* + ID_MODEL_FROM_DATABASE=XMC-7K410CC: User-Configurable Kintex-7 FPGA, 410k logic cells, conduction-cooled + +pci:v000016D5d00007006* + ID_MODEL_FROM_DATABASE=XMC-7A200: User-Configurable Artix-7 FPGA, 200k logic cells with Plug-In I/O + +pci:v000016D5d00007007* + ID_MODEL_FROM_DATABASE=XMC-7A200CC: User-Configurable Conduction-Cooled Artix-7 FPGA, with 200k logic cells + +pci:v000016D5d00007011* + ID_MODEL_FROM_DATABASE=AP440-1: 32-Channel Isolated Digital Input Module + +pci:v000016D5d00007012* + ID_MODEL_FROM_DATABASE=AP440-2: 32-Channel Isolated Digital Input Module + +pci:v000016D5d00007013* + ID_MODEL_FROM_DATABASE=AP440-3: 32-Channel Isolated Digital Input Module + +pci:v000016D5d00007014* + ID_MODEL_FROM_DATABASE=AP445: 32-Channel Isolated Digital Output Module + +pci:v000016D5d00007018* + ID_MODEL_FROM_DATABASE=AP408: 32-Channel Digital I/O Module + +pci:v000016DA* + ID_VENDOR_FROM_DATABASE=Advantech Co., Ltd. + +pci:v000016DAd00000011* + ID_MODEL_FROM_DATABASE=INES GPIB-PCI + +pci:v000016DF* + ID_VENDOR_FROM_DATABASE=PIKA Technologies Inc. + +pci:v000016E2* + ID_VENDOR_FROM_DATABASE=Geotest-MTS + +pci:v000016E3* + ID_VENDOR_FROM_DATABASE=European Space Agency + +pci:v000016E3d00001E0F* + ID_MODEL_FROM_DATABASE=LEON2FT Processor + +pci:v000016E5* + ID_VENDOR_FROM_DATABASE=Intellon Corp. + +pci:v000016E5d00006000* + ID_MODEL_FROM_DATABASE=INT6000 Ethernet-to-Powerline Bridge [HomePlug AV] + +pci:v000016E5d00006300* + ID_MODEL_FROM_DATABASE=INT6300 Ethernet-to-Powerline Bridge [HomePlug AV] + +pci:v000016EC* + ID_VENDOR_FROM_DATABASE=U.S. Robotics + +pci:v000016ECd000000ED* + ID_MODEL_FROM_DATABASE=USR997900 + +pci:v000016ECd00000116* + ID_MODEL_FROM_DATABASE=USR997902 10/100/1000 Mbps PCI Network Card + +pci:v000016ECd00002F00* + ID_MODEL_FROM_DATABASE=USR5660A (USR265660A, USR5660A-BP) 56K PCI Faxmodem + +pci:v000016ECd00003685* + ID_MODEL_FROM_DATABASE=Wireless Access PCI Adapter Model 022415 + +pci:v000016ECd00004320* + ID_MODEL_FROM_DATABASE=USR997904 10/100/1000 64-bit NIC (Marvell Yukon) + +pci:v000016ECd0000AB06* + ID_MODEL_FROM_DATABASE=USR997901A 10/100 Cardbus NIC + +pci:v000016ED* + ID_VENDOR_FROM_DATABASE=Sycron N. V. + +pci:v000016EDd00001001* + ID_MODEL_FROM_DATABASE=UMIO communication card + +pci:v000016F2* + ID_VENDOR_FROM_DATABASE=ETAS GmbH + +pci:v000016F2d00000200* + ID_MODEL_FROM_DATABASE=I/O board + +pci:v000016F2d00000200sv000016F2sd00000010* + ID_MODEL_FROM_DATABASE=I/O board (ES53xx I/O board) + +pci:v000016F3* + ID_VENDOR_FROM_DATABASE=Jetway Information Co., Ltd. + +pci:v000016F4* + ID_VENDOR_FROM_DATABASE=Vweb Corp + +pci:v000016F4d00008000* + ID_MODEL_FROM_DATABASE=VW2010 + +pci:v000016F6* + ID_VENDOR_FROM_DATABASE=VideoTele.com, Inc. + +pci:v00001702* + ID_VENDOR_FROM_DATABASE=Internet Machines Corporation (IMC) + +pci:v00001705* + ID_VENDOR_FROM_DATABASE=Digital First, Inc. + +pci:v0000170B* + ID_VENDOR_FROM_DATABASE=NetOctave + +pci:v0000170Bd00000100* + ID_MODEL_FROM_DATABASE=NSP2000-SSL crypto accelerator + +pci:v0000170C* + ID_VENDOR_FROM_DATABASE=YottaYotta Inc. + +pci:v00001719* + ID_VENDOR_FROM_DATABASE=EZChip Technologies + +pci:v00001719d00001000* + ID_MODEL_FROM_DATABASE=NPA Access Network Processor Family + +pci:v00001725* + ID_VENDOR_FROM_DATABASE=Vitesse Semiconductor + +pci:v00001725d00007174* + ID_MODEL_FROM_DATABASE=VSC7174 PCI/PCI-X Serial ATA Host Bus Controller + +pci:v0000172A* + ID_VENDOR_FROM_DATABASE=Accelerated Encryption + +pci:v0000172Ad000013C8* + ID_MODEL_FROM_DATABASE=AEP SureWare Runner 1000V3 + +pci:v00001734* + ID_VENDOR_FROM_DATABASE=Fujitsu Technology Solutions + +pci:v00001735* + ID_VENDOR_FROM_DATABASE=Aten International Co. Ltd. + +pci:v00001737* + ID_VENDOR_FROM_DATABASE=Linksys + +pci:v00001737d00000029* + ID_MODEL_FROM_DATABASE=WPG54G ver. 4 PCI Card + +pci:v00001737d00001032* + ID_MODEL_FROM_DATABASE=Gigabit Network Adapter + +pci:v00001737d00001032sv00001737sd00000015* + ID_MODEL_FROM_DATABASE=Gigabit Network Adapter (EG1032 v2 Instant Gigabit Network Adapter) + +pci:v00001737d00001032sv00001737sd00000024* + ID_MODEL_FROM_DATABASE=Gigabit Network Adapter (EG1032 v3 Instant Gigabit Network Adapter) + +pci:v00001737d00001064* + ID_MODEL_FROM_DATABASE=Gigabit Network Adapter + +pci:v00001737d00001064sv00001737sd00000016* + ID_MODEL_FROM_DATABASE=Gigabit Network Adapter (EG1064 v2 Instant Gigabit Network Adapter) + +pci:v00001737d0000AB08* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v00001737d0000AB09* + ID_MODEL_FROM_DATABASE=21x4x DEC-Tulip compatible 10/100 Ethernet + +pci:v0000173B* + ID_VENDOR_FROM_DATABASE=Altima (nee Broadcom) + +pci:v0000173Bd000003E8* + ID_MODEL_FROM_DATABASE=AC1000 Gigabit Ethernet + +pci:v0000173Bd000003E9* + ID_MODEL_FROM_DATABASE=AC1001 Gigabit Ethernet + +pci:v0000173Bd000003EA* + ID_MODEL_FROM_DATABASE=AC9100 Gigabit Ethernet + +pci:v0000173Bd000003EAsv0000173Bsd00000001* + ID_MODEL_FROM_DATABASE=AC9100 Gigabit Ethernet (AC1002) + +pci:v0000173Bd000003EB* + ID_MODEL_FROM_DATABASE=AC1003 Gigabit Ethernet + +pci:v00001743* + ID_VENDOR_FROM_DATABASE=Peppercon AG + +pci:v00001743d00008139* + ID_MODEL_FROM_DATABASE=ROL/F-100 Fast Ethernet Adapter with ROL + +pci:v00001745* + ID_VENDOR_FROM_DATABASE=ViXS Systems, Inc. + +pci:v00001745d00002020* + ID_MODEL_FROM_DATABASE=XCode II Series + +pci:v00001745d00002100* + ID_MODEL_FROM_DATABASE=XCode 2100 Series + +pci:v00001749* + ID_VENDOR_FROM_DATABASE=RLX Technologies + +pci:v0000174B* + ID_VENDOR_FROM_DATABASE=PC Partner Limited / Sapphire Technology + +pci:v0000174D* + ID_VENDOR_FROM_DATABASE=WellX Telecom SA + +pci:v0000175C* + ID_VENDOR_FROM_DATABASE=AudioScience Inc + +pci:v0000175E* + ID_VENDOR_FROM_DATABASE=Sanera Systems, Inc. + +pci:v00001760* + ID_VENDOR_FROM_DATABASE=TEDIA spol. s r. o. + +pci:v00001760d00000101* + ID_MODEL_FROM_DATABASE=PCD-7004 Digital Bi-Directional Ports PCI Card + +pci:v00001760d00000102* + ID_MODEL_FROM_DATABASE=PCD-7104 Digital Input & Output PCI Card + +pci:v00001760d00000303* + ID_MODEL_FROM_DATABASE=PCD-7006C Digital Input & Output PCI Card + +pci:v00001771* + ID_VENDOR_FROM_DATABASE=InnoVISION Multimedia Ltd. + +pci:v00001775* + ID_VENDOR_FROM_DATABASE=GE Intelligent Platforms + +pci:v0000177D* + ID_VENDOR_FROM_DATABASE=Cavium, Inc. + +pci:v0000177Dd00000001* + ID_MODEL_FROM_DATABASE=Nitrox XL N1 + +pci:v0000177Dd00000003* + ID_MODEL_FROM_DATABASE=Nitrox XL N1 Lite + +pci:v0000177Dd00000004* + ID_MODEL_FROM_DATABASE=Octeon (and older) FIPS + +pci:v0000177Dd00000005* + ID_MODEL_FROM_DATABASE=Octeon CN38XX Network Processor Pass 3.x + +pci:v0000177Dd00000006* + ID_MODEL_FROM_DATABASE=RoHS + +pci:v0000177Dd00000010* + ID_MODEL_FROM_DATABASE=Nitrox XL NPX + +pci:v0000177Dd00000020* + ID_MODEL_FROM_DATABASE=Octeon CN31XX Network Processor + +pci:v0000177Dd00000030* + ID_MODEL_FROM_DATABASE=Octeon CN30XX Network Processor + +pci:v0000177Dd00000040* + ID_MODEL_FROM_DATABASE=Octeon CN58XX Network Processor + +pci:v0000177Dd00000050* + ID_MODEL_FROM_DATABASE=Octeon CN57XX Network Processor (CN54XX/CN55XX/CN56XX) + +pci:v0000177Dd00000070* + ID_MODEL_FROM_DATABASE=Octeon CN50XX Network Processor + +pci:v0000177Dd00000080* + ID_MODEL_FROM_DATABASE=Octeon CN52XX Network Processor + +pci:v0000177Dd00000090* + ID_MODEL_FROM_DATABASE=Octeon II CN63XX Network Processor + +pci:v0000177Dd00000091* + ID_MODEL_FROM_DATABASE=Octeon II CN68XX Network Processor + +pci:v0000177Dd00000092* + ID_MODEL_FROM_DATABASE=Octeon II CN65XX Network Processor + +pci:v0000177Dd00000093* + ID_MODEL_FROM_DATABASE=Octeon II CN61XX Network Processor + +pci:v0000177Dd00000094* + ID_MODEL_FROM_DATABASE=Octeon Fusion CNF71XX Cell processor + +pci:v0000177Dd00000095* + ID_MODEL_FROM_DATABASE=Octeon III CN78XX Network Processor + +pci:v0000177Dd00000096* + ID_MODEL_FROM_DATABASE=Octeon III CN70XX Network Processor + +pci:v0000177Dd00009700* + ID_MODEL_FROM_DATABASE=Octeon III CN73XX Network Processor + +pci:v0000177Dd00009702* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] Intelligent Adapter + +pci:v0000177Dd00009702sv0000177Dsd00000003* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] Intelligent Adapter (CN2350 [LiquidIO II] 2-port 10GbE Intelligent adapter) + +pci:v0000177Dd00009702sv0000177Dsd00000004* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] Intelligent Adapter (CN2350 [LiquidIO II] 2-port 25GbE Intelligent adapter) + +pci:v0000177Dd00009703* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] NVMe Controller + +pci:v0000177Dd00009712* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] SRIOV Virtual Function + +pci:v0000177Dd00009712sv0000177Dsd00000003* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] SRIOV Virtual Function (CN2350 [LiquidIO II] 2-port 10GbE SRIOV Virtual Function) + +pci:v0000177Dd00009713* + ID_MODEL_FROM_DATABASE=CN23XX [LiquidIO II] NVMe SRIOV Virtual Function + +pci:v0000177Dd00009800* + ID_MODEL_FROM_DATABASE=Octeon Fusion CNF75XX Processor + +pci:v0000177Dd0000A001* + ID_MODEL_FROM_DATABASE=ThunderX MRML(Master RML Bridge to RSL devices) + +pci:v0000177Dd0000A002* + ID_MODEL_FROM_DATABASE=THUNDERX PCC Bridge + +pci:v0000177Dd0000A002sv0000177Dsd0000A102* + ID_MODEL_FROM_DATABASE=THUNDERX PCC Bridge (CN88XX PCC Bridge) + +pci:v0000177Dd0000A008* + ID_MODEL_FROM_DATABASE=THUNDERX SMMU + +pci:v0000177Dd0000A008sv0000177Dsd0000A108* + ID_MODEL_FROM_DATABASE=THUNDERX SMMU (CN88XX SMMU) + +pci:v0000177Dd0000A009* + ID_MODEL_FROM_DATABASE=THUNDERX Generic Interrupt Controller + +pci:v0000177Dd0000A00A* + ID_MODEL_FROM_DATABASE=THUNDERX GPIO Controller + +pci:v0000177Dd0000A00B* + ID_MODEL_FROM_DATABASE=THUNDERX MPI / SPI Controller + +pci:v0000177Dd0000A00C* + ID_MODEL_FROM_DATABASE=THUNDERX MIO-PTP Controller + +pci:v0000177Dd0000A00D* + ID_MODEL_FROM_DATABASE=THUNDERX MIX Network Controller + +pci:v0000177Dd0000A00E* + ID_MODEL_FROM_DATABASE=THUNDERX Reset Controller + +pci:v0000177Dd0000A00F* + ID_MODEL_FROM_DATABASE=THUNDERX UART Controller + +pci:v0000177Dd0000A010* + ID_MODEL_FROM_DATABASE=THUNDERX eMMC/SD Controller + +pci:v0000177Dd0000A011* + ID_MODEL_FROM_DATABASE=THUNDERX MIO-BOOT Controller + +pci:v0000177Dd0000A012* + ID_MODEL_FROM_DATABASE=THUNDERX TWSI / I2C Controller + +pci:v0000177Dd0000A013* + ID_MODEL_FROM_DATABASE=THUNDERX CCPI (Multi-node connect) + +pci:v0000177Dd0000A014* + ID_MODEL_FROM_DATABASE=THUNDERX Voltage Regulator Module + +pci:v0000177Dd0000A015* + ID_MODEL_FROM_DATABASE=THUNDERX PCIe Switch Logic Interface + +pci:v0000177Dd0000A016* + ID_MODEL_FROM_DATABASE=THUNDERX Key Memory + +pci:v0000177Dd0000A017* + ID_MODEL_FROM_DATABASE=THUNDERX GTI (Global System Timers) + +pci:v0000177Dd0000A018* + ID_MODEL_FROM_DATABASE=THUNDERX Random Number Generator + +pci:v0000177Dd0000A019* + ID_MODEL_FROM_DATABASE=THUNDERX DFA + +pci:v0000177Dd0000A01A* + ID_MODEL_FROM_DATABASE=THUNDERX Zip Coprocessor + +pci:v0000177Dd0000A01B* + ID_MODEL_FROM_DATABASE=THUNDERX xHCI USB Controller + +pci:v0000177Dd0000A01C* + ID_MODEL_FROM_DATABASE=THUNDERX AHCI SATA Controller + +pci:v0000177Dd0000A01Csv0000177Dsd0000A11C* + ID_MODEL_FROM_DATABASE=THUNDERX AHCI SATA Controller (CN88XX AHCI SATA Controller) + +pci:v0000177Dd0000A01D* + ID_MODEL_FROM_DATABASE=THUNDERX RAID Coprocessor + +pci:v0000177Dd0000A01E* + ID_MODEL_FROM_DATABASE=THUNDERX Network Interface Controller + +pci:v0000177Dd0000A01F* + ID_MODEL_FROM_DATABASE=THUNDERX Traffic Network Switch + +pci:v0000177Dd0000A020* + ID_MODEL_FROM_DATABASE=THUNDERX PEM (PCI Express Interface) + +pci:v0000177Dd0000A021* + ID_MODEL_FROM_DATABASE=THUNDERX L2C (Level-2 Cache Controller) + +pci:v0000177Dd0000A022* + ID_MODEL_FROM_DATABASE=THUNDERX LMC (DRAM Controller) + +pci:v0000177Dd0000A023* + ID_MODEL_FROM_DATABASE=THUNDERX OCLA (On-Chip Logic Analyzer) + +pci:v0000177Dd0000A024* + ID_MODEL_FROM_DATABASE=THUNDERX OSM + +pci:v0000177Dd0000A025* + ID_MODEL_FROM_DATABASE=THUNDERX GSER (General Serializer/Deserializer) + +pci:v0000177Dd0000A026* + ID_MODEL_FROM_DATABASE=THUNDERX BGX (Common Ethernet Interface) + +pci:v0000177Dd0000A027* + ID_MODEL_FROM_DATABASE=THUNDERX IOBN + +pci:v0000177Dd0000A029* + ID_MODEL_FROM_DATABASE=THUNDERX NCSI (Network Controller Sideband Interface) + +pci:v0000177Dd0000A02A* + ID_MODEL_FROM_DATABASE=ThunderX SGPIO (Serial GPIO controller for SATA disk lights) + +pci:v0000177Dd0000A02B* + ID_MODEL_FROM_DATABASE=THUNDERX SMI / MDIO Controller + +pci:v0000177Dd0000A02C* + ID_MODEL_FROM_DATABASE=THUNDERX DAP (Debug Access Port) + +pci:v0000177Dd0000A02D* + ID_MODEL_FROM_DATABASE=THUNDERX PCIERC (PCIe Root Complex) + +pci:v0000177Dd0000A02E* + ID_MODEL_FROM_DATABASE=ThunderX L2C-TAD (Level 2 cache tag and data) + +pci:v0000177Dd0000A02F* + ID_MODEL_FROM_DATABASE=THUNDERX L2C-CBC + +pci:v0000177Dd0000A030* + ID_MODEL_FROM_DATABASE=THUNDERX L2C-MCI + +pci:v0000177Dd0000A031* + ID_MODEL_FROM_DATABASE=THUNDERX MIO-FUS (Fuse Access Controller) + +pci:v0000177Dd0000A032* + ID_MODEL_FROM_DATABASE=THUNDERX FUSF (Fuse Controller) + +pci:v0000177Dd0000A033* + ID_MODEL_FROM_DATABASE=THUNDERX Random Number Generator virtual function + +pci:v0000177Dd0000A034* + ID_MODEL_FROM_DATABASE=THUNDERX Network Interface Controller virtual function + +pci:v0000177Dd0000A035* + ID_MODEL_FROM_DATABASE=THUNDERX Parallel Bus + +pci:v0000177Dd0000A036* + ID_MODEL_FROM_DATABASE=ThunderX RAD (RAID acceleration engine) virtual function + +pci:v0000177Dd0000A037* + ID_MODEL_FROM_DATABASE=THUNDERX ZIP virtual function + +pci:v0000177Dd0000A040* + ID_MODEL_FROM_DATABASE=THUNDERX CPT Cryptographic Accelerator + +pci:v0000177Dd0000A100* + ID_MODEL_FROM_DATABASE=THUNDERX CN88XX 48 core SoC + +pci:v0000177Dd0000A200* + ID_MODEL_FROM_DATABASE=OCTEON TX CN81XX/CN80XX + +pci:v0000177Dd0000A300* + ID_MODEL_FROM_DATABASE=OCTEON TX CN83XX + +pci:v00001787* + ID_VENDOR_FROM_DATABASE=Hightech Information System Ltd. + +pci:v00001789* + ID_VENDOR_FROM_DATABASE=Ennyah Technologies Corp. + +pci:v00001796* + ID_VENDOR_FROM_DATABASE=Research Centre Juelich + +pci:v00001796d00000001* + ID_MODEL_FROM_DATABASE=SIS1100 [Gigabit link] + +pci:v00001796d00000002* + ID_MODEL_FROM_DATABASE=HOTlink + +pci:v00001796d00000003* + ID_MODEL_FROM_DATABASE=Counter Timer + +pci:v00001796d00000004* + ID_MODEL_FROM_DATABASE=CAMAC Controller + +pci:v00001796d00000005* + ID_MODEL_FROM_DATABASE=PROFIBUS + +pci:v00001796d00000006* + ID_MODEL_FROM_DATABASE=AMCC HOTlink + +pci:v00001796d0000000D* + ID_MODEL_FROM_DATABASE=Synchronisation Slave + +pci:v00001796d0000000E* + ID_MODEL_FROM_DATABASE=SIS1100-eCMC + +pci:v00001796d0000000F* + ID_MODEL_FROM_DATABASE=TDC (GPX) + +pci:v00001796d00000010* + ID_MODEL_FROM_DATABASE=PCIe Counter Timer + +pci:v00001796d00000011* + ID_MODEL_FROM_DATABASE=SIS1100-e single link + +pci:v00001796d00000012* + ID_MODEL_FROM_DATABASE=SIS1100-e quad link + +pci:v00001796d00000015* + ID_MODEL_FROM_DATABASE=SIS8100 [Gigabit link, MicroTCA] + +pci:v00001797* + ID_VENDOR_FROM_DATABASE=Intersil Techwell + +pci:v00001797d00005864* + ID_MODEL_FROM_DATABASE=TW5864 multimedia video controller + +pci:v00001797d00006801* + ID_MODEL_FROM_DATABASE=TW6802 multimedia video card + +pci:v00001797d00006802* + ID_MODEL_FROM_DATABASE=TW6802 multimedia other device + +pci:v00001797d00006810* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006811* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006812* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006813* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006814* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006815* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006816* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006817* + ID_MODEL_FROM_DATABASE=TW6816 multimedia video controller + +pci:v00001797d00006864* + ID_MODEL_FROM_DATABASE=TW6864 multimedia video controller + +pci:v00001799* + ID_VENDOR_FROM_DATABASE=Belkin + +pci:v00001799d00006001* + ID_MODEL_FROM_DATABASE=F5D6001 Wireless PCI Card [Realtek RTL8180] + +pci:v00001799d00006020* + ID_MODEL_FROM_DATABASE=F5D6020 v3000 Wireless PCMCIA Card [Realtek RTL8180] + +pci:v00001799d00006060* + ID_MODEL_FROM_DATABASE=F5D6060 Wireless PDA Card + +pci:v00001799d0000700F* + ID_MODEL_FROM_DATABASE=F5D7000 v7000 Wireless G Desktop Card [Realtek RTL8185] + +pci:v00001799d0000701F* + ID_MODEL_FROM_DATABASE=F5D7010 v7000 Wireless G Notebook Card [Realtek RTL8185] + +pci:v0000179A* + ID_VENDOR_FROM_DATABASE=id Quantique + +pci:v0000179Ad00000001* + ID_MODEL_FROM_DATABASE=Quantis PCI 16Mbps + +pci:v0000179C* + ID_VENDOR_FROM_DATABASE=Data Patterns + +pci:v0000179Cd00000557* + ID_MODEL_FROM_DATABASE=DP-PCI-557 [PCI 1553B] + +pci:v0000179Cd00000566* + ID_MODEL_FROM_DATABASE=DP-PCI-566 [Intelligent PCI 1553B] + +pci:v0000179Cd00001152* + ID_MODEL_FROM_DATABASE=DP-cPCI-1152 (8-channel Isolated ADC Module) + +pci:v0000179Cd00005031* + ID_MODEL_FROM_DATABASE=DP-CPCI-5031-Synchro Module + +pci:v0000179Cd00005112* + ID_MODEL_FROM_DATABASE=DP-cPCI-5112 [MM-Carrier] + +pci:v0000179Cd00005121* + ID_MODEL_FROM_DATABASE=DP-CPCI-5121-IP Carrier + +pci:v0000179Cd00005211* + ID_MODEL_FROM_DATABASE=DP-CPCI-5211-IP Carrier + +pci:v0000179Cd00005679* + ID_MODEL_FROM_DATABASE=AGE Display Module + +pci:v000017A0* + ID_VENDOR_FROM_DATABASE=Genesys Logic, Inc + +pci:v000017A0d00007163* + ID_MODEL_FROM_DATABASE=GL9701 PCIe to PCI Bridge + +pci:v000017A0d00008083* + ID_MODEL_FROM_DATABASE=GL880 USB 1.1 UHCI controller + +pci:v000017A0d00008084* + ID_MODEL_FROM_DATABASE=GL880 USB 2.0 EHCI controller + +pci:v000017AA* + ID_VENDOR_FROM_DATABASE=Lenovo + +pci:v000017AAd0000402B* + ID_MODEL_FROM_DATABASE=Intel 82599ES 10Gb 2-port Server Adapter X520-2 + +pci:v000017AB* + ID_VENDOR_FROM_DATABASE=Phillips Components + +pci:v000017AF* + ID_VENDOR_FROM_DATABASE=Hightech Information System Ltd. + +pci:v000017B3* + ID_VENDOR_FROM_DATABASE=Hawking Technologies + +pci:v000017B3d0000AB08* + ID_MODEL_FROM_DATABASE=PN672TX 10/100 Ethernet + +pci:v000017B4* + ID_VENDOR_FROM_DATABASE=Indra Networks, Inc. + +pci:v000017B4d00000011* + ID_MODEL_FROM_DATABASE=WebEnhance 100 GZIP Compression Card + +pci:v000017B4d00000012* + ID_MODEL_FROM_DATABASE=WebEnhance 200 GZIP Compression Card + +pci:v000017B4d00000015* + ID_MODEL_FROM_DATABASE=WebEnhance 300 GZIP Compression Card + +pci:v000017B4d00000016* + ID_MODEL_FROM_DATABASE=StorCompress 300 GZIP Compression Card + +pci:v000017B4d00000017* + ID_MODEL_FROM_DATABASE=StorSecure 300 GZIP Compression and AES Encryption Card + +pci:v000017C0* + ID_VENDOR_FROM_DATABASE=Wistron Corp. + +pci:v000017C2* + ID_VENDOR_FROM_DATABASE=Newisys, Inc. + +pci:v000017CB* + ID_VENDOR_FROM_DATABASE=Airgo Networks, Inc. + +pci:v000017CBd00000001* + ID_MODEL_FROM_DATABASE=AGN100 802.11 a/b/g True MIMO Wireless Card + +pci:v000017CBd00000001sv00001385sd00005C00* + ID_MODEL_FROM_DATABASE=AGN100 802.11 a/b/g True MIMO Wireless Card (WGM511 Pre-N 802.11g Wireless CardBus Adapter) + +pci:v000017CBd00000001sv00001737sd00000045* + ID_MODEL_FROM_DATABASE=AGN100 802.11 a/b/g True MIMO Wireless Card (WMP54GX v1 802.11g Wireless-G PCI Adapter with SRX) + +pci:v000017CBd00000002* + ID_MODEL_FROM_DATABASE=AGN300 802.11 a/b/g True MIMO Wireless Card + +pci:v000017CBd00000002sv00001385sd00006D00* + ID_MODEL_FROM_DATABASE=AGN300 802.11 a/b/g True MIMO Wireless Card (WPNT511 RangeMax 240 Mbps Wireless CardBus Adapter) + +pci:v000017CBd00000002sv00001737sd00000054* + ID_MODEL_FROM_DATABASE=AGN300 802.11 a/b/g True MIMO Wireless Card (WPC54GX4 v1 802.11g Wireless-G Notebook Adapter with SRX400) + +pci:v000017CC* + ID_VENDOR_FROM_DATABASE=NetChip Technology, Inc + +pci:v000017CCd00002280* + ID_MODEL_FROM_DATABASE=USB 2.0 + +pci:v000017CF* + ID_VENDOR_FROM_DATABASE=Z-Com, Inc. + +pci:v000017D3* + ID_VENDOR_FROM_DATABASE=Areca Technology Corp. + +pci:v000017D3d00001110* + ID_MODEL_FROM_DATABASE=ARC-1110 4-Port PCI-X to SATA RAID Controller + +pci:v000017D3d00001120* + ID_MODEL_FROM_DATABASE=ARC-1120 8-Port PCI-X to SATA RAID Controller + +pci:v000017D3d00001130* + ID_MODEL_FROM_DATABASE=ARC-1130 12-Port PCI-X to SATA RAID Controller + +pci:v000017D3d00001160* + ID_MODEL_FROM_DATABASE=ARC-1160 16-Port PCI-X to SATA RAID Controller + +pci:v000017D3d00001170* + ID_MODEL_FROM_DATABASE=ARC-1170 24-Port PCI-X to SATA RAID Controller + +pci:v000017D3d00001201* + ID_MODEL_FROM_DATABASE=ARC-1200 2-Port PCI-Express to SATA II RAID Controller + +pci:v000017D3d00001210* + ID_MODEL_FROM_DATABASE=ARC-1210 4-Port PCI-Express to SATA RAID Controller + +pci:v000017D3d00001214* + ID_MODEL_FROM_DATABASE=ARC-12x4 PCIe 2.0 to SAS/SATA 6Gb RAID Controller + +pci:v000017D3d00001214sv000017D3sd00001214* + ID_MODEL_FROM_DATABASE=ARC-12x4 PCIe 2.0 to SAS/SATA 6Gb RAID Controller (ARC-1214 4-Port PCIe 2.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001214sv000017D3sd00001224* + ID_MODEL_FROM_DATABASE=ARC-12x4 PCIe 2.0 to SAS/SATA 6Gb RAID Controller (ARC-1224 8-Port PCIe 2.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001214sv000017D3sd00001264* + ID_MODEL_FROM_DATABASE=ARC-12x4 PCIe 2.0 to SAS/SATA 6Gb RAID Controller (ARC-1264 12/16 Port PCIe 2.0 to SATA 6Gb RAID Controller) + +pci:v000017D3d00001214sv000017D3sd00001284* + ID_MODEL_FROM_DATABASE=ARC-12x4 PCIe 2.0 to SAS/SATA 6Gb RAID Controller (ARC-1284 24 Port PCIe 2.0 to SATA 6Gb RAID Controller) + +pci:v000017D3d00001220* + ID_MODEL_FROM_DATABASE=ARC-1220 8-Port PCI-Express to SATA RAID Controller + +pci:v000017D3d00001222* + ID_MODEL_FROM_DATABASE=ARC-1222 8-Port PCI-Express to SAS/SATA II RAID Controller + +pci:v000017D3d00001230* + ID_MODEL_FROM_DATABASE=ARC-1230 12-Port PCI-Express to SATA RAID Controller + +pci:v000017D3d00001260* + ID_MODEL_FROM_DATABASE=ARC-1260 16-Port PCI-Express to SATA RAID Controller + +pci:v000017D3d00001280* + ID_MODEL_FROM_DATABASE=ARC-1280/1280ML 24-Port PCI-Express to SATA II RAID Controller + +pci:v000017D3d00001280sv000017D3sd00001221* + ID_MODEL_FROM_DATABASE=ARC-1280/1280ML 24-Port PCI-Express to SATA II RAID Controller (ARC-1221 8-Port PCI-Express to SATA RAID Controller) + +pci:v000017D3d00001300* + ID_MODEL_FROM_DATABASE=ARC-1300ix-16 16-Port PCI-Express to SAS Non-RAID Host Adapter + +pci:v000017D3d00001320* + ID_MODEL_FROM_DATABASE=ARC-1320 8/16 Port PCIe 2.0 to SAS/SATA 6Gb Non-RAID Host Adapter + +pci:v000017D3d00001330* + ID_MODEL_FROM_DATABASE=ARC-1330 16 Port PCIe 3.0 to SAS/SATA 12Gb Non-RAID Host Adapter + +pci:v000017D3d00001680* + ID_MODEL_FROM_DATABASE=ARC-1680 series PCIe to SAS/SATA 3Gb RAID Controller + +pci:v000017D3d00001680sv000017D3sd00001212* + ID_MODEL_FROM_DATABASE=ARC-1680 series PCIe to SAS/SATA 3Gb RAID Controller (ARC-1212 4-Port PCIe to SAS/SATA II RAID Controller) + +pci:v000017D3d00001680sv000017D3sd00001222* + ID_MODEL_FROM_DATABASE=ARC-1680 series PCIe to SAS/SATA 3Gb RAID Controller (ARC-1222 8-Port PCIe to SAS/SATA 3Gb RAID Controller) + +pci:v000017D3d00001680sv000017D3sd00001680* + ID_MODEL_FROM_DATABASE=ARC-1680 series PCIe to SAS/SATA 3Gb RAID Controller (ARC-1680 8/12/16/24 Port PCIe to SAS/SATA 3Gb RAID Controller) + +pci:v000017D3d00001880* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller + +pci:v000017D3d00001880sv000017D3sd00001213* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1213 4-Port PCIe 2.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001215* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1215 4-Port PCIe 3.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001216* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1216 4-Port PCIe 3.0 to SAS/SATA 12Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001223* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1223 8-Port PCIe 2.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001225* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1225 8-Port PCIe 3.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001226* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1226 8-Port PCIe 3.0 to SAS/SATA 12Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001880* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1880 8/12/16/24 Port PCIe 2.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001882* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1882 8/12/16/24 Port PCIe 3.0 to SAS/SATA 6Gb RAID Controller) + +pci:v000017D3d00001880sv000017D3sd00001883* + ID_MODEL_FROM_DATABASE=ARC-188x series PCIe 2.0/3.0 to SAS/SATA 6/12Gb RAID Controller (ARC-1883 8/12/16/24 Port PCIe 3.0 to SAS/SATA 12Gb RAID Controller) + +pci:v000017D5* + ID_VENDOR_FROM_DATABASE=Exar Corp. + +pci:v000017D5d00005731* + ID_MODEL_FROM_DATABASE=Xframe 10-Gigabit Ethernet PCI-X + +pci:v000017D5d00005732* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 + +pci:v000017D5d00005831* + ID_MODEL_FROM_DATABASE=Xframe 10-Gigabit Ethernet PCI-X + +pci:v000017D5d00005831sv0000103Csd000012D5* + ID_MODEL_FROM_DATABASE=Xframe 10-Gigabit Ethernet PCI-X (PCI-X 133MHz 10GbE SR Fiber) + +pci:v000017D5d00005831sv000010A9sd00008020* + ID_MODEL_FROM_DATABASE=Xframe 10-Gigabit Ethernet PCI-X (Single Port 10-Gigabit Ethernet (PCI-X, Fiber)) + +pci:v000017D5d00005831sv000010A9sd00008024* + ID_MODEL_FROM_DATABASE=Xframe 10-Gigabit Ethernet PCI-X (Single Port 10-Gigabit Ethernet (PCI-X, Fiber)) + +pci:v000017D5d00005832* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 + +pci:v000017D5d00005832sv0000103Csd00001337* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (PCI-X 266MHz 10GigE SR [AD385A]) + +pci:v000017D5d00005832sv000010A9sd00008021* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Single Port 10-Gigabit Ethernet II (PCI-X, Fiber)) + +pci:v000017D5d00005832sv000017D5sd00006020* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II SR) + +pci:v000017D5d00005832sv000017D5sd00006021* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II SR, Low Profile) + +pci:v000017D5d00005832sv000017D5sd00006022* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe E SR) + +pci:v000017D5d00005832sv000017D5sd00006420* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II LR) + +pci:v000017D5d00005832sv000017D5sd00006421* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II LR, Low Profile) + +pci:v000017D5d00005832sv000017D5sd00006422* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe E LR) + +pci:v000017D5d00005832sv000017D5sd00006C20* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II CX4) + +pci:v000017D5d00005832sv000017D5sd00006C21* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe II CX4, Low Profile) + +pci:v000017D5d00005832sv000017D5sd00006C22* + ID_MODEL_FROM_DATABASE=Xframe II 10-Gigabit Ethernet PCI-X 2.0 (Xframe E CX4) + +pci:v000017D5d00005833* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe + +pci:v000017D5d00005833sv000017D5sd00006030* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3110 Single Port SR) + +pci:v000017D5d00005833sv000017D5sd00006031* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3120 Dual Port SR) + +pci:v000017D5d00005833sv000017D5sd00006430* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3110 Single Port LR) + +pci:v000017D5d00005833sv000017D5sd00006431* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3120 Dual Port LR) + +pci:v000017D5d00005833sv000017D5sd00007030* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3110 Single Port LRM) + +pci:v000017D5d00005833sv000017D5sd00007031* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3120 Dual Port LRM) + +pci:v000017D5d00005833sv000017D5sd00007430* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3110 Single Port 10GBase-T) + +pci:v000017D5d00005833sv000017D5sd00007431* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3120 Dual Port 10GBase-T) + +pci:v000017D5d00005833sv000017D5sd00007830* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3110 Single Port 10GBase-CR) + +pci:v000017D5d00005833sv000017D5sd00007831* + ID_MODEL_FROM_DATABASE=X3100 Series 10 Gigabit Ethernet PCIe (X3120 Dual Port 10GBase-CR) + +pci:v000017DB* + ID_VENDOR_FROM_DATABASE=Cray Inc + +pci:v000017DBd00000101* + ID_MODEL_FROM_DATABASE=XT Series [Seastar] 3D Toroidal Router + +pci:v000017DE* + ID_VENDOR_FROM_DATABASE=KWorld Computer Co. Ltd. + +pci:v000017DF* + ID_VENDOR_FROM_DATABASE=Dini Group + +pci:v000017DFd00001864* + ID_MODEL_FROM_DATABASE=Virtex4 PCI Board w/ QL5064 Bridge [DN7000K10PCI/DN8000K10PCI/DN8000K10PSX/NOTUS] + +pci:v000017DFd00001865* + ID_MODEL_FROM_DATABASE=Virtex4 ASIC Emulator [DN8000K10PCIe] + +pci:v000017DFd00001866* + ID_MODEL_FROM_DATABASE=Virtex4 ASIC Emulator Cable Connection [DN8000K10PCI] + +pci:v000017DFd00001867* + ID_MODEL_FROM_DATABASE=Virtex4 ASIC Emulator Cable Connection [DN8000K10PCIe] + +pci:v000017DFd00001868* + ID_MODEL_FROM_DATABASE=Virtex4 ASIC Emulator [DN8000K10PCIe-8] + +pci:v000017DFd00001900* + ID_MODEL_FROM_DATABASE=Virtex5 PCIe ASIC Emulator [DN9000K10PCIe8T/DN9002K10PCIe8T/DN9200K10PCIe8T/DN7006K10PCIe8T/DN7406K10PCIe8T] + +pci:v000017DFd00001901* + ID_MODEL_FROM_DATABASE=Virtex5 PCIe ASIC Emulator Large BARs [DN9000K10PCIe8T/DN9002K10PCIe8T/DN9200K10PCIe8T/DN7006K10PCIe8T/DN7406K10PCIe8T] + +pci:v000017DFd00001902* + ID_MODEL_FROM_DATABASE=Virtex5 PCIe ASIC Emulator Low Power [Interceptor] + +pci:v000017DFd00001903* + ID_MODEL_FROM_DATABASE=Spartan6 PCIe FPGA Accelerator Board [DNBFCS12PCIe] + +pci:v000017DFd00001904* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe ASIC Emulation Board [DNDUALV6_PCIe4] + +pci:v000017DFd00001905* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe ASIC Emulation Board [DNV6F6PCIe] + +pci:v000017DFd00001906* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe ASIC Emulation Board [DN2076K10] + +pci:v000017DFd00001907* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe ASIC Emulation Board [DNV6F2PCIe] + +pci:v000017DFd00001908* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe ASIC Emulation Board Large BARs[DNV6F2PCIe] + +pci:v000017DFd00001909* + ID_MODEL_FROM_DATABASE=Kintex7 PCIe FPGA Accelerator Board [DNK7F5PCIe] + +pci:v000017DFd0000190A* + ID_MODEL_FROM_DATABASE=Virtex7 PCIe ASIC Emulation Board [DNV7F1A] + +pci:v000017DFd0000190B* + ID_MODEL_FROM_DATABASE=Stratix5 PCIe ASIC Emulation Board [DNS5GXF2] + +pci:v000017DFd0000190C* + ID_MODEL_FROM_DATABASE=Virtex7 PCIe ASIC Emulation Board [DNV7F2A] + +pci:v000017DFd0000190D* + ID_MODEL_FROM_DATABASE=Virtex7 PCIe ASIC Emulation Board [DNV7F4A] + +pci:v000017DFd0000190E* + ID_MODEL_FROM_DATABASE=Virtex7 PCIe ASIC Emulation Board [DNV7F2B] + +pci:v000017DFd0000190F* + ID_MODEL_FROM_DATABASE=KintexUS PCIe MainRef Design [DNPCIE_40G_KU_LL] + +pci:v000017DFd00001910* + ID_MODEL_FROM_DATABASE=VirtexUS ASIC Emulation Board [DNVUF4A] + +pci:v000017DFd00001911* + ID_MODEL_FROM_DATABASE=VirtexUS PCIe ASIC Emulation Board [DNVU_F2PCIe] + +pci:v000017DFd00001912* + ID_MODEL_FROM_DATABASE=KintexUS PCIe MainRef Design [DNPCIe_40G_KU_LL_QSFP] + +pci:v000017DFd00001913* + ID_MODEL_FROM_DATABASE=VirtexUS ASIC Emulation Board [DNVUF1A] + +pci:v000017DFd00001914* + ID_MODEL_FROM_DATABASE=VirtexUS ASIC Emulation Board [DNVUF2A] + +pci:v000017DFd00001915* + ID_MODEL_FROM_DATABASE=Arria10 PCIe MainRef Design [DNPCIe_80G_A10_LL] + +pci:v000017DFd00001916* + ID_MODEL_FROM_DATABASE=VirtexUS PCIe Accelerator Board [DNVUF2_HPC_PCIe] + +pci:v000017DFd00001A00* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe DMA Netlist Design + +pci:v000017DFd00001A01* + ID_MODEL_FROM_DATABASE=Virtex6 PCIe Darklite Design [DNPCIe_HXT_10G_LL] + +pci:v000017DFd00001A02* + ID_MODEL_FROM_DATABASE=Virtex7 PCIe DMA Netlist Design + +pci:v000017DFd00001A03* + ID_MODEL_FROM_DATABASE=Kintex7 PCIe Darklite Design [DNPCIe_K7_10G_LL] + +pci:v000017DFd00001A05* + ID_MODEL_FROM_DATABASE=Stratix5 PCIe Darklite Design [DNS5GX_F2] + +pci:v000017DFd00001A06* + ID_MODEL_FROM_DATABASE=VirtexUS PCIe DMA Netlist Design + +pci:v000017DFd00001A07* + ID_MODEL_FROM_DATABASE=KintexUS PCIe Darklite Design [DNPCIe_40G_KU_LL] + +pci:v000017DFd00001A08* + ID_MODEL_FROM_DATABASE=KintexUS PCIe Darklite Design [DNPCIe_40G_KU_LL_QSFP] + +pci:v000017DFd00001A09* + ID_MODEL_FROM_DATABASE=Arria10 PCIe Darklite Design [DNPCIe_80G_A10_LL] + +pci:v000017E4* + ID_VENDOR_FROM_DATABASE=Sectra AB + +pci:v000017E4d00000001* + ID_MODEL_FROM_DATABASE=KK671 Cardbus encryption board + +pci:v000017E4d00000002* + ID_MODEL_FROM_DATABASE=KK672 Cardbus encryption board + +pci:v000017E6* + ID_VENDOR_FROM_DATABASE=Entropic Communications Inc. + +pci:v000017E6d00000010* + ID_MODEL_FROM_DATABASE=EN2010 [c.Link] MoCA Network Controller (Coax, PCI interface) + +pci:v000017E6d00000011* + ID_MODEL_FROM_DATABASE=EN2010 [c.Link] MoCA Network Controller (Coax, MPEG interface) + +pci:v000017E6d00000021* + ID_MODEL_FROM_DATABASE=EN2210 [c.Link] MoCA Network Controller (Coax) + +pci:v000017E6d00000025* + ID_MODEL_FROM_DATABASE=EN2510 [c.Link] MoCA Network Controller (Coax, PCIe interface) + +pci:v000017E6d00000027* + ID_MODEL_FROM_DATABASE=EN2710 [c.Link] MoCA 2.0 Network Controller (Coax, PCIe interface) + +pci:v000017EE* + ID_VENDOR_FROM_DATABASE=Connect Components Ltd + +pci:v000017F2* + ID_VENDOR_FROM_DATABASE=Albatron Corp. + +pci:v000017F3* + ID_VENDOR_FROM_DATABASE=RDC Semiconductor, Inc. + +pci:v000017F3d00001010* + ID_MODEL_FROM_DATABASE=R1010 IDE Controller + +pci:v000017F3d00006020* + ID_MODEL_FROM_DATABASE=R6020 North Bridge + +pci:v000017F3d00006021* + ID_MODEL_FROM_DATABASE=R6021 Host Bridge + +pci:v000017F3d00006030* + ID_MODEL_FROM_DATABASE=R6030 ISA Bridge + +pci:v000017F3d00006031* + ID_MODEL_FROM_DATABASE=R6031 ISA Bridge + +pci:v000017F3d00006040* + ID_MODEL_FROM_DATABASE=R6040 MAC Controller + +pci:v000017F3d00006060* + ID_MODEL_FROM_DATABASE=R6060 USB 1.1 Controller + +pci:v000017F3d00006061* + ID_MODEL_FROM_DATABASE=R6061 USB 2.0 Controller + +pci:v000017F7* + ID_VENDOR_FROM_DATABASE=Topdek Semiconductor Inc. + +pci:v000017F9* + ID_VENDOR_FROM_DATABASE=Gemtek Technology Co., Ltd + +pci:v000017FC* + ID_VENDOR_FROM_DATABASE=IOGEAR, Inc. + +pci:v000017FE* + ID_VENDOR_FROM_DATABASE=InProComm Inc. + +pci:v000017FEd00002120* + ID_MODEL_FROM_DATABASE=IPN 2120 802.11b + +pci:v000017FEd00002120sv00001737sd00000020* + ID_MODEL_FROM_DATABASE=IPN 2120 802.11b (WMP11 v4 802.11b Wireless-B PCI Adapter) + +pci:v000017FEd00002220* + ID_MODEL_FROM_DATABASE=IPN 2220 802.11g + +pci:v000017FEd00002220sv00001468sd00000305* + ID_MODEL_FROM_DATABASE=IPN 2220 802.11g (T60N871 802.11g Mini PCI Wireless Adapter) + +pci:v000017FEd00002220sv00001737sd00000029* + ID_MODEL_FROM_DATABASE=IPN 2220 802.11g (WPC54G v4 802.11g Wireless-G Notebook Adapter) + +pci:v000017FF* + ID_VENDOR_FROM_DATABASE=Benq Corporation + +pci:v00001800* + ID_VENDOR_FROM_DATABASE=Qualcore Logic Inc. + +pci:v00001800d00001100* + ID_MODEL_FROM_DATABASE=Nanospeed Trading Gateway + +pci:v00001803* + ID_VENDOR_FROM_DATABASE=ProdaSafe GmbH + +pci:v00001805* + ID_VENDOR_FROM_DATABASE=Euresys S.A. + +pci:v00001809* + ID_VENDOR_FROM_DATABASE=Lumanate, Inc. + +pci:v00001813* + ID_VENDOR_FROM_DATABASE=Ambient Technologies Inc + +pci:v00001813d00004000* + ID_MODEL_FROM_DATABASE=HaM controllerless modem + +pci:v00001813d00004000sv000016BEsd00000001* + ID_MODEL_FROM_DATABASE=HaM controllerless modem (V9x HAM Data Fax Modem) + +pci:v00001813d00004100* + ID_MODEL_FROM_DATABASE=HaM plus Data Fax Modem + +pci:v00001813d00004100sv000016BEsd00000002* + ID_MODEL_FROM_DATABASE=HaM plus Data Fax Modem (V9x HAM 1394) + +pci:v00001814* + ID_VENDOR_FROM_DATABASE=Ralink corp. + +pci:v00001814d00000101* + ID_MODEL_FROM_DATABASE=Wireless PCI Adapter RT2400 / RT2460 + +pci:v00001814d00000101sv00001043sd00000127* + ID_MODEL_FROM_DATABASE=Wireless PCI Adapter RT2400 / RT2460 (WiFi-b add-on Card) + +pci:v00001814d00000101sv00001371sd00000010* + ID_MODEL_FROM_DATABASE=Wireless PCI Adapter RT2400 / RT2460 (Minitar MNW2BPCI Wireless PCI Card) + +pci:v00001814d00000101sv00001462sd00006828* + ID_MODEL_FROM_DATABASE=Wireless PCI Adapter RT2400 / RT2460 (PC11B2 (MS-6828) Wireless 11b PCI Card) + +pci:v00001814d00000200* + ID_MODEL_FROM_DATABASE=RT2500 802.11g PCI [PC54G2] + +pci:v00001814d00000201* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg + +pci:v00001814d00000201sv00001043sd0000130F* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (WL-130g) + +pci:v00001814d00000201sv00001186sd00003C00* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (DWL-G650X Wireless 11g CardBus Adapter) + +pci:v00001814d00000201sv00001371sd0000001E* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (CWC-854 Wireless-G CardBus Adapter) + +pci:v00001814d00000201sv00001371sd0000001F* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (CWM-854 Wireless-G Mini PCI Adapter) + +pci:v00001814d00000201sv00001371sd00000020* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (CWP-854 Wireless-G PCI Adapter) + +pci:v00001814d00000201sv00001458sd0000E381* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (GN-WMKG 802.11b/g Wireless CardBus Adapter) + +pci:v00001814d00000201sv00001458sd0000E931* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (GN-WIKG 802.11b/g mini-PCI Adapter) + +pci:v00001814d00000201sv00001462sd00006833* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (Unknown 802.11g mini-PCI Adapter) + +pci:v00001814d00000201sv00001462sd00006835* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (Wireless 11G CardBus CB54G2) + +pci:v00001814d00000201sv00001737sd00000032* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (WMP54G v4.0 PCI Adapter) + +pci:v00001814d00000201sv00001799sd0000700A* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (F5D7000 v2000/v3000 Wireless G Desktop Card) + +pci:v00001814d00000201sv00001799sd0000701A* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (F5D7010 v2000/v3000 Wireless G Notebook Card) + +pci:v00001814d00000201sv00001814sd00002560* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg + +pci:v00001814d00000201sv0000182Dsd00009073* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (WL-115 Wireless Network PCI Adapter) + +pci:v00001814d00000201sv0000185Fsd000022A0* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (CN-WF513 Wireless Cardbus Adapter) + +pci:v00001814d00000201sv000018EBsd00005312* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (WL531P IEEE 802.11g PCI Card-EU) + +pci:v00001814d00000201sv00001948sd00003C00* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (C54RC v1 Wireless 11g CardBus Adapter) + +pci:v00001814d00000201sv00001948sd00003C01* + ID_MODEL_FROM_DATABASE=RT2500 Wireless 802.11bg (C54Ri v1 Wireless 11g PCI Adapter) + +pci:v00001814d00000300* + ID_MODEL_FROM_DATABASE=Wireless Adapter Canyon CN-WF511 + +pci:v00001814d00000301* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI + +pci:v00001814d00000301sv00001186sd00003C08* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.E1)) + +pci:v00001814d00000301sv00001186sd00003C09* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (DWL-G510 Rev C) + +pci:v00001814d00000301sv000013D1sd0000ABE3* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (miniPCI Pluscom 802.11 a/b/g) + +pci:v00001814d00000301sv00001458sd0000E933* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (GN-WI01GS) + +pci:v00001814d00000301sv00001458sd0000E934* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (GN-WP01GS) + +pci:v00001814d00000301sv00001462sd0000B833* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (MP54G5 (MS-6833B)) + +pci:v00001814d00000301sv00001737sd00000055* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (WMP54G v4.1) + +pci:v00001814d00000301sv00001799sd0000700E* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (F5D7000 v6000 Wireless G Desktop Card) + +pci:v00001814d00000301sv00001799sd0000701E* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (F5D7010 v6000 Wireless G Notebook Card) + +pci:v00001814d00000301sv000017F9sd00000012* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (AWLC3026T 802.11g Wireless CardBus Adapter) + +pci:v00001814d00000301sv00001814sd00002561* + ID_MODEL_FROM_DATABASE=RT2561/RT61 802.11g PCI (EW-7108PCg/EW-7128g) + +pci:v00001814d00000302* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g + +pci:v00001814d00000302sv00001186sd00003A71* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (DWA-510 Wireless G Desktop Adapter) + +pci:v00001814d00000302sv00001186sd00003C08* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (AirPlus G DWL-G630 Wireless Cardbus Adapter (rev.E2)) + +pci:v00001814d00000302sv00001186sd00003C09* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (AirPlus G DWL-G510 Wireless Network Adapter (Rev.C)) + +pci:v00001814d00000302sv00001462sd0000B834* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (PC54G3 Wireless 11g PCI Card) + +pci:v00001814d00000302sv00001948sd00003C23* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (C54RC v2 Wireless 11g CardBus Adapter) + +pci:v00001814d00000302sv00001948sd00003C24* + ID_MODEL_FROM_DATABASE=RT2561/RT61 rev B 802.11g (C54Ri v2 Wireless 11g PCI Adapter) + +pci:v00001814d00000401* + ID_MODEL_FROM_DATABASE=RT2600 802.11 MIMO + +pci:v00001814d00000401sv00001737sd00000052* + ID_MODEL_FROM_DATABASE=RT2600 802.11 MIMO (WPC54GR v1 802.11g Wireless-G Notebook Adapter with RangeBooster) + +pci:v00001814d00000401sv000017F9sd00000011* + ID_MODEL_FROM_DATABASE=RT2600 802.11 MIMO (WPCR-137G 802.11bg Wireless CardBus Adapter) + +pci:v00001814d00000401sv000017F9sd00000016* + ID_MODEL_FROM_DATABASE=RT2600 802.11 MIMO (WPIR-119GH 802.11bg Wireless Desktop Adapter) + +pci:v00001814d00000601* + ID_MODEL_FROM_DATABASE=RT2800 802.11n PCI + +pci:v00001814d00000601sv00001799sd0000801C* + ID_MODEL_FROM_DATABASE=RT2800 802.11n PCI (F5D8011 v3 802.11n N1 Wireless Notebook Card) + +pci:v00001814d00000601sv0000187Esd00003412* + ID_MODEL_FROM_DATABASE=RT2800 802.11n PCI (NWD-310N 802.11n Wireless PCI Adapter) + +pci:v00001814d00000681* + ID_MODEL_FROM_DATABASE=RT2890 Wireless 802.11n PCIe + +pci:v00001814d00000681sv00001458sd0000E939* + ID_MODEL_FROM_DATABASE=RT2890 Wireless 802.11n PCIe (GN-WS30N-RH 802.11bgn Mini PCIe Card) + +pci:v00001814d00000701* + ID_MODEL_FROM_DATABASE=RT2760 Wireless 802.11n 1T/2R + +pci:v00001814d00000701sv00001737sd00000074* + ID_MODEL_FROM_DATABASE=RT2760 Wireless 802.11n 1T/2R (WMP110 v2 802.11n RangePlus Wireless PCI Adapter) + +pci:v00001814d00000781* + ID_MODEL_FROM_DATABASE=RT2790 Wireless 802.11n 1T/2R PCIe + +pci:v00001814d00000781sv00001814sd00002790* + ID_MODEL_FROM_DATABASE=RT2790 Wireless 802.11n 1T/2R PCIe + +pci:v00001814d00003060* + ID_MODEL_FROM_DATABASE=RT3060 Wireless 802.11n 1T/1R + +pci:v00001814d00003060sv00001186sd00003C04* + ID_MODEL_FROM_DATABASE=RT3060 Wireless 802.11n 1T/1R (DWA-525 Wireless N 150 Desktop Adapter (rev.A1)) + +pci:v00001814d00003062* + ID_MODEL_FROM_DATABASE=RT3062 Wireless 802.11n 2T/2R + +pci:v00001814d00003090* + ID_MODEL_FROM_DATABASE=RT3090 Wireless 802.11n 1T/1R PCIe + +pci:v00001814d00003090sv000013BDsd00001057* + ID_MODEL_FROM_DATABASE=RT3090 Wireless 802.11n 1T/1R PCIe (GN-WS32L-RH Half-size Mini PCIe Card) + +pci:v00001814d00003091* + ID_MODEL_FROM_DATABASE=RT3091 Wireless 802.11n 1T/2R PCIe + +pci:v00001814d00003092* + ID_MODEL_FROM_DATABASE=RT3092 Wireless 802.11n 2T/2R PCIe + +pci:v00001814d00003290* + ID_MODEL_FROM_DATABASE=RT3290 Wireless 802.11n 1T/1R PCIe + +pci:v00001814d00003290sv0000103Csd000018EC* + ID_MODEL_FROM_DATABASE=RT3290 Wireless 802.11n 1T/1R PCIe (Ralink RT3290LE 802.11bgn 1x1 Wi-Fi and Bluetooth 4.0 Combo Adapter) + +pci:v00001814d00003298* + ID_MODEL_FROM_DATABASE=RT3290 Bluetooth + +pci:v00001814d00003298sv0000103Csd000018EC* + ID_MODEL_FROM_DATABASE=RT3290 Bluetooth (Ralink RT3290LE 802.11bgn 1x1 Wi-Fi and Bluetooth 4.0 Combo Adapter) + +pci:v00001814d00003592* + ID_MODEL_FROM_DATABASE=RT3592 Wireless 802.11abgn 2T/2R PCIe + +pci:v00001814d0000359F* + ID_MODEL_FROM_DATABASE=RT3592 PCIe Wireless Network Adapter + +pci:v00001814d00005360* + ID_MODEL_FROM_DATABASE=RT5360 Wireless 802.11n 1T/1R + +pci:v00001814d00005360sv00001186sd00003C05* + ID_MODEL_FROM_DATABASE=RT5360 Wireless 802.11n 1T/1R (DWA-525 Wireless N 150 Desktop Adapter (rev.A2)) + +pci:v00001814d00005360sv000020F4sd0000703A* + ID_MODEL_FROM_DATABASE=RT5360 Wireless 802.11n 1T/1R (TEW-703PI N150 Wireless PCI Adapter) + +pci:v00001814d00005362* + ID_MODEL_FROM_DATABASE=RT5362 PCI 802.11n Wireless Network Adapter + +pci:v00001814d00005390* + ID_MODEL_FROM_DATABASE=RT5390 Wireless 802.11n 1T/1R PCIe + +pci:v00001814d00005390sv0000103Csd00001636* + ID_MODEL_FROM_DATABASE=RT5390 Wireless 802.11n 1T/1R PCIe (U98Z077.00 Half-size Mini PCIe Card) + +pci:v00001814d00005392* + ID_MODEL_FROM_DATABASE=RT5392 PCIe Wireless Network Adapter + +pci:v00001814d0000539B* + ID_MODEL_FROM_DATABASE=RT5390R 802.11bgn PCIe Wireless Network Adapter + +pci:v00001814d0000539F* + ID_MODEL_FROM_DATABASE=RT5390 [802.11 b/g/n 1T1R G-band PCI Express Single Chip] + +pci:v00001814d0000539Fsv0000103Csd00001637* + ID_MODEL_FROM_DATABASE=RT5390 [802.11 b/g/n 1T1R G-band PCI Express Single Chip] (Pavilion DM1Z-3000 PCIe wireless card) + +pci:v00001814d00005592* + ID_MODEL_FROM_DATABASE=RT5592 PCIe Wireless Network Adapter + +pci:v00001814d0000E932* + ID_MODEL_FROM_DATABASE=RT2560F 802.11 b/g PCI + +pci:v00001815* + ID_VENDOR_FROM_DATABASE=Devolo AG + +pci:v00001820* + ID_VENDOR_FROM_DATABASE=InfiniCon Systems Inc. + +pci:v00001822* + ID_VENDOR_FROM_DATABASE=Twinhan Technology Co. Ltd + +pci:v00001822d00004E35* + ID_MODEL_FROM_DATABASE=Mantis DTV PCI Bridge Controller [Ver 1.0] + +pci:v0000182D* + ID_VENDOR_FROM_DATABASE=SiteCom Europe BV + +pci:v0000182Dd00003069* + ID_MODEL_FROM_DATABASE=ISDN PCI DC-105V2 + +pci:v0000182Dd00009790* + ID_MODEL_FROM_DATABASE=WL-121 Wireless Network Adapter 100g+ [Ver.3] + +pci:v0000182E* + ID_VENDOR_FROM_DATABASE=Raza Microelectronics, Inc. + +pci:v0000182Ed00000008* + ID_MODEL_FROM_DATABASE=XLR516 Processor + +pci:v0000182F* + ID_VENDOR_FROM_DATABASE=Broadcom + +pci:v0000182Fd0000000B* + ID_MODEL_FROM_DATABASE=BCM5785 [HT1000] SATA (RAID Mode) + +pci:v00001830* + ID_VENDOR_FROM_DATABASE=Credence Systems Corporation + +pci:v0000183B* + ID_VENDOR_FROM_DATABASE=MikroM GmbH + +pci:v0000183Bd000008A7* + ID_MODEL_FROM_DATABASE=MVC100 DVI + +pci:v0000183Bd000008A8* + ID_MODEL_FROM_DATABASE=MVC101 SDI + +pci:v0000183Bd000008A9* + ID_MODEL_FROM_DATABASE=MVC102 DVI+Audio + +pci:v0000183Bd000008B0* + ID_MODEL_FROM_DATABASE=MVC200-DC + +pci:v00001846* + ID_VENDOR_FROM_DATABASE=Alcatel-Lucent + +pci:v00001849* + ID_VENDOR_FROM_DATABASE=ASRock Incorporation + +pci:v0000184A* + ID_VENDOR_FROM_DATABASE=Thales Computers + +pci:v0000184Ad00001100* + ID_MODEL_FROM_DATABASE=MAX II cPLD + +pci:v00001850* + ID_VENDOR_FROM_DATABASE=Advantest Corporation + +pci:v00001850d00000048* + ID_MODEL_FROM_DATABASE=EK220-66401 Computer Interface Card + +pci:v00001851* + ID_VENDOR_FROM_DATABASE=Microtune, Inc. + +pci:v00001852* + ID_VENDOR_FROM_DATABASE=Anritsu Corp. + +pci:v00001853* + ID_VENDOR_FROM_DATABASE=SMSC Automotive Infotainment System Group + +pci:v00001854* + ID_VENDOR_FROM_DATABASE=LG Electronics, Inc. + +pci:v0000185B* + ID_VENDOR_FROM_DATABASE=Compro Technology, Inc. + +pci:v0000185Bd00001489* + ID_MODEL_FROM_DATABASE=VideoMate Vista T100 + +pci:v0000185F* + ID_VENDOR_FROM_DATABASE=Wistron NeWeb Corp. + +pci:v00001864* + ID_VENDOR_FROM_DATABASE=SilverBack + +pci:v00001864d00002110* + ID_MODEL_FROM_DATABASE=ISNAP 2110 + +pci:v00001867* + ID_VENDOR_FROM_DATABASE=Topspin Communications + +pci:v00001867d00005A44* + ID_MODEL_FROM_DATABASE=MT23108 InfiniHost HCA + +pci:v00001867d00005A45* + ID_MODEL_FROM_DATABASE=MT23108 InfiniHost HCA flash recovery + +pci:v00001867d00005A46* + ID_MODEL_FROM_DATABASE=MT23108 InfiniHost HCA bridge + +pci:v00001867d00006278* + ID_MODEL_FROM_DATABASE=MT25208 InfiniHost III Ex (Tavor compatibility mode) + +pci:v00001867d00006282* + ID_MODEL_FROM_DATABASE=MT25208 InfiniHost III Ex + +pci:v0000186C* + ID_VENDOR_FROM_DATABASE=Humusoft, s.r.o. + +pci:v0000186Cd00000612* + ID_MODEL_FROM_DATABASE=AD612 Data Acquisition Device + +pci:v0000186Cd00000614* + ID_MODEL_FROM_DATABASE=MF614 Multifunction I/O Card + +pci:v0000186Cd00000622* + ID_MODEL_FROM_DATABASE=AD622 Data Acquisition Device + +pci:v0000186Cd00000624* + ID_MODEL_FROM_DATABASE=MF624 Multifunction I/O PCI Card + +pci:v0000186Cd00000625* + ID_MODEL_FROM_DATABASE=MF625 3-phase Motor Driver + +pci:v0000186Cd00000634* + ID_MODEL_FROM_DATABASE=MF634 Multifunction I/O PCIe Card + +pci:v0000186F* + ID_VENDOR_FROM_DATABASE=WiNRADiO Communications + +pci:v00001876* + ID_VENDOR_FROM_DATABASE=L-3 Communications + +pci:v00001876d0000A101* + ID_MODEL_FROM_DATABASE=VigraWATCH PCI + +pci:v00001876d0000A102* + ID_MODEL_FROM_DATABASE=VigraWATCH PMC + +pci:v00001876d0000A103* + ID_MODEL_FROM_DATABASE=Vigra I/O + +pci:v0000187E* + ID_VENDOR_FROM_DATABASE=ZyXEL Communications Corporation + +pci:v0000187Ed00003403* + ID_MODEL_FROM_DATABASE=ZyAir G-110 802.11g + +pci:v0000187Ed0000340E* + ID_MODEL_FROM_DATABASE=M-302 802.11g XtremeMIMO + +pci:v00001885* + ID_VENDOR_FROM_DATABASE=Avvida Systems Inc. + +pci:v00001888* + ID_VENDOR_FROM_DATABASE=Varisys Ltd + +pci:v00001888d00000301* + ID_MODEL_FROM_DATABASE=VMFX1 FPGA PMC module + +pci:v00001888d00000601* + ID_MODEL_FROM_DATABASE=VSM2 dual PMC carrier + +pci:v00001888d00000710* + ID_MODEL_FROM_DATABASE=VS14x series PowerPC PCI board + +pci:v00001888d00000720* + ID_MODEL_FROM_DATABASE=VS24x series PowerPC PCI board + +pci:v0000188A* + ID_VENDOR_FROM_DATABASE=Ample Communications, Inc + +pci:v00001890* + ID_VENDOR_FROM_DATABASE=Egenera, Inc. + +pci:v00001894* + ID_VENDOR_FROM_DATABASE=KNC One + +pci:v00001896* + ID_VENDOR_FROM_DATABASE=B&B Electronics Manufacturing Company, Inc. + +pci:v00001896d00004202* + ID_MODEL_FROM_DATABASE=MIport 3PCIU2 2-port Serial + +pci:v00001896d00004204* + ID_MODEL_FROM_DATABASE=MIport 3PCIU4 4-port Serial + +pci:v00001896d00004208* + ID_MODEL_FROM_DATABASE=MIport 3PCIU8 8-port Serial + +pci:v00001896d00004211* + ID_MODEL_FROM_DATABASE=MIport 3PCIOU1 1-port Isolated Serial + +pci:v00001896d00004212* + ID_MODEL_FROM_DATABASE=MIport 3PCIOU2 2-port Isolated Serial + +pci:v00001896d00004214* + ID_MODEL_FROM_DATABASE=MIport 3PCIOU4 4-port Isolated Serial + +pci:v00001896d0000BB10* + ID_MODEL_FROM_DATABASE=3PCI2 2-Port Serial + +pci:v00001896d0000BB11* + ID_MODEL_FROM_DATABASE=3PCIO1 1-Port Isolated Serial + +pci:v00001897* + ID_VENDOR_FROM_DATABASE=AMtek + +pci:v000018A1* + ID_VENDOR_FROM_DATABASE=Astute Networks Inc. + +pci:v000018A2* + ID_VENDOR_FROM_DATABASE=Stretch Inc. + +pci:v000018A2d00000002* + ID_MODEL_FROM_DATABASE=VRC6016 16-Channel PCIe DVR Card + +pci:v000018A3* + ID_VENDOR_FROM_DATABASE=AT&T + +pci:v000018AC* + ID_VENDOR_FROM_DATABASE=DViCO Corporation + +pci:v000018ACd0000D500* + ID_MODEL_FROM_DATABASE=FusionHDTV 5 + +pci:v000018ACd0000D800* + ID_MODEL_FROM_DATABASE=FusionHDTV 3 Gold + +pci:v000018ACd0000D810* + ID_MODEL_FROM_DATABASE=FusionHDTV 3 Gold-Q + +pci:v000018ACd0000D820* + ID_MODEL_FROM_DATABASE=FusionHDTV 3 Gold-T + +pci:v000018ACd0000DB30* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T Pro + +pci:v000018ACd0000DB40* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T Hybrid + +pci:v000018ACd0000DB78* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T Dual Express + +pci:v000018B8* + ID_VENDOR_FROM_DATABASE=Ammasso + +pci:v000018B8d0000B001* + ID_MODEL_FROM_DATABASE=AMSO 1100 iWARP/RDMA Gigabit Ethernet Coprocessor + +pci:v000018BC* + ID_VENDOR_FROM_DATABASE=GeCube Technologies, Inc. + +pci:v000018C3* + ID_VENDOR_FROM_DATABASE=Micronas Semiconductor Holding AG + +pci:v000018C3d00000720* + ID_MODEL_FROM_DATABASE=nGene PCI-Express Multimedia Controller + +pci:v000018C3d00000720sv000007CAsd0000032E* + ID_MODEL_FROM_DATABASE=nGene PCI-Express Multimedia Controller (Hybrid M779 PCI-E) + +pci:v000018C8* + ID_VENDOR_FROM_DATABASE=Cray Inc + +pci:v000018C9* + ID_VENDOR_FROM_DATABASE=ARVOO Engineering BV + +pci:v000018CA* + ID_VENDOR_FROM_DATABASE=XGI Technology Inc. (eXtreme Graphics Innovation) + +pci:v000018CAd00000020* + ID_MODEL_FROM_DATABASE=Z7/Z9 (XG20 core) + +pci:v000018CAd00000021* + ID_MODEL_FROM_DATABASE=Z9s/Z9m (XG21 core) + +pci:v000018CAd00000027* + ID_MODEL_FROM_DATABASE=Z11/Z11M + +pci:v000018CAd00000040* + ID_MODEL_FROM_DATABASE=Volari V3XT/V5/V8 + +pci:v000018CAd00000047* + ID_MODEL_FROM_DATABASE=Volari 8300 (chip: XP10, codename: XG47) + +pci:v000018D2* + ID_VENDOR_FROM_DATABASE=Sitecom Europe BV (Wrong ID) + +pci:v000018D2d00003069* + ID_MODEL_FROM_DATABASE=DC-105v2 ISDN controller + +pci:v000018D8* + ID_VENDOR_FROM_DATABASE=Dialogue Technology Corp. + +pci:v000018DD* + ID_VENDOR_FROM_DATABASE=Artimi Inc + +pci:v000018DDd00004C6F* + ID_MODEL_FROM_DATABASE=Artimi RTMI-100 UWB adapter + +pci:v000018DF* + ID_VENDOR_FROM_DATABASE=LeWiz Communications + +pci:v000018E6* + ID_VENDOR_FROM_DATABASE=MPL AG + +pci:v000018E6d00000001* + ID_MODEL_FROM_DATABASE=OSCI [Octal Serial Communication Interface] + +pci:v000018EB* + ID_VENDOR_FROM_DATABASE=Advance Multimedia Internet Technology, Inc. + +pci:v000018EC* + ID_VENDOR_FROM_DATABASE=Cesnet, z.s.p.o. + +pci:v000018ECd00006D05* + ID_MODEL_FROM_DATABASE=ML555 + +pci:v000018ECd00006D05sv000018ECsd00000100* + ID_MODEL_FROM_DATABASE=ML555 (NIC (ethernet interfaces)) + +pci:v000018ECd00006D05sv000018ECsd00000200* + ID_MODEL_FROM_DATABASE=ML555 (NIC (szedata2) 4x1G) + +pci:v000018ECd00006D05sv000018ECsd00000201* + ID_MODEL_FROM_DATABASE=ML555 (NIC (szedata2) 2x10G) + +pci:v000018ECd00006D05sv000018ECsd00000300* + ID_MODEL_FROM_DATABASE=ML555 (NIFIC (szedata2) 4x1G) + +pci:v000018ECd00006D05sv000018ECsd00000302* + ID_MODEL_FROM_DATABASE=ML555 (NIFIC (szedata2) 2x10G) + +pci:v000018ECd00006D05sv000018ECsd00004200* + ID_MODEL_FROM_DATABASE=ML555 (Flexible FlowMon (szedata2) 1x10G) + +pci:v000018ECd00006D05sv000018ECsd0000FF00* + ID_MODEL_FROM_DATABASE=ML555 (Testing design) + +pci:v000018ECd00006D05sv000018ECsd0000FF01* + ID_MODEL_FROM_DATABASE=ML555 (Boot design) + +pci:v000018ECd0000C006* + ID_MODEL_FROM_DATABASE=COMBO6 + +pci:v000018ECd0000C006sv000018ECsd0000D001* + ID_MODEL_FROM_DATABASE=COMBO6 (COMBO-4MTX) + +pci:v000018ECd0000C006sv000018ECsd0000D002* + ID_MODEL_FROM_DATABASE=COMBO6 (COMBO-4SFP) + +pci:v000018ECd0000C006sv000018ECsd0000D003* + ID_MODEL_FROM_DATABASE=COMBO6 (COMBO-4SFPRO) + +pci:v000018ECd0000C006sv000018ECsd0000D004* + ID_MODEL_FROM_DATABASE=COMBO6 (COMBO-2XFP) + +pci:v000018ECd0000C032* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 + +pci:v000018ECd0000C032sv000018ECsd00000100* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (NIC (ethernet interfaces)) + +pci:v000018ECd0000C032sv000018ECsd00000200* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (NIC (szedata2) 4x1G) + +pci:v000018ECd0000C032sv000018ECsd00000201* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (NIC (szedata2) 2x10G) + +pci:v000018ECd0000C032sv000018ECsd00000300* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (NIFIC (szedata2) 4x1G) + +pci:v000018ECd0000C032sv000018ECsd00000302* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (NIFIC (szedata2) 2x10G) + +pci:v000018ECd0000C032sv000018ECsd00004200* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (Flexible FlowMon (szedata2) 1x10G) + +pci:v000018ECd0000C032sv000018ECsd0000FF00* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (Testing design) + +pci:v000018ECd0000C032sv000018ECsd0000FF01* + ID_MODEL_FROM_DATABASE=COMBO-LXT110 (Boot design) + +pci:v000018ECd0000C045* + ID_MODEL_FROM_DATABASE=COMBO6E + +pci:v000018ECd0000C050* + ID_MODEL_FROM_DATABASE=COMBO-PTM + +pci:v000018ECd0000C058* + ID_MODEL_FROM_DATABASE=COMBO6X + +pci:v000018ECd0000C058sv000018ECsd0000D001* + ID_MODEL_FROM_DATABASE=COMBO6X (COMBO-4MTX) + +pci:v000018ECd0000C058sv000018ECsd0000D002* + ID_MODEL_FROM_DATABASE=COMBO6X (COMBO-4SFP) + +pci:v000018ECd0000C058sv000018ECsd0000D003* + ID_MODEL_FROM_DATABASE=COMBO6X (COMBO-4SFPRO) + +pci:v000018ECd0000C058sv000018ECsd0000D004* + ID_MODEL_FROM_DATABASE=COMBO6X (COMBO-2XFP) + +pci:v000018ECd0000C132* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 + +pci:v000018ECd0000C132sv000018ECsd00000100* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (NIC (ethernet interfaces)) + +pci:v000018ECd0000C132sv000018ECsd00000200* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (NIC (szedata2) 4x1G) + +pci:v000018ECd0000C132sv000018ECsd00000201* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (NIC (szedata2) 2x10G) + +pci:v000018ECd0000C132sv000018ECsd00000300* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (NIFIC (szedata2) 4x1G) + +pci:v000018ECd0000C132sv000018ECsd00000302* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (NIFIC (szedata2) 2x10G) + +pci:v000018ECd0000C132sv000018ECsd00004200* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (Flexible FlowMon (szedata2) 1x10G) + +pci:v000018ECd0000C132sv000018ECsd0000FF00* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (Testing design) + +pci:v000018ECd0000C132sv000018ECsd0000FF01* + ID_MODEL_FROM_DATABASE=COMBO-LXT155 (Boot design) + +pci:v000018ECd0000C232* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 + +pci:v000018ECd0000C232sv000018ECsd00000100* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (NIC (ethernet interfaces)) + +pci:v000018ECd0000C232sv000018ECsd00000200* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (NIC (szedata2) 4x1G) + +pci:v000018ECd0000C232sv000018ECsd00000201* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (NIC (szedata2) 2x10G) + +pci:v000018ECd0000C232sv000018ECsd00000300* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (NIFIC (szedata2) 4x1G) + +pci:v000018ECd0000C232sv000018ECsd00000302* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (NIFIC (szedata2) 2x10G) + +pci:v000018ECd0000C232sv000018ECsd00004200* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (Flexible FlowMon (szedata2) 1x10G) + +pci:v000018ECd0000C232sv000018ECsd0000FF00* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (Testing design) + +pci:v000018ECd0000C232sv000018ECsd0000FF01* + ID_MODEL_FROM_DATABASE=COMBO-FXT100 (Boot design) + +pci:v000018EE* + ID_VENDOR_FROM_DATABASE=Chenming Mold Ind. Corp. + +pci:v000018F1* + ID_VENDOR_FROM_DATABASE=Spectrum GmbH + +pci:v000018F4* + ID_VENDOR_FROM_DATABASE=Napatech A/S + +pci:v000018F4d00000031* + ID_MODEL_FROM_DATABASE=NT20X Network Adapter + +pci:v000018F4d00000051* + ID_MODEL_FROM_DATABASE=NT20X Capture Card + +pci:v000018F4d00000061* + ID_MODEL_FROM_DATABASE=NT20E Capture Card + +pci:v000018F4d00000064* + ID_MODEL_FROM_DATABASE=NT20E Inline Card + +pci:v000018F4d00000071* + ID_MODEL_FROM_DATABASE=NT4E Capture Card + +pci:v000018F4d00000074* + ID_MODEL_FROM_DATABASE=NT4E Inline Card + +pci:v000018F4d00000081* + ID_MODEL_FROM_DATABASE=NT4E 4-port Expansion Card + +pci:v000018F4d00000091* + ID_MODEL_FROM_DATABASE=NT20X Capture Card [New Rev] + +pci:v000018F4d000000A1* + ID_MODEL_FROM_DATABASE=NT4E-STD Capture Card + +pci:v000018F4d000000A4* + ID_MODEL_FROM_DATABASE=NT4E-STD Inline Card + +pci:v000018F4d000000B1* + ID_MODEL_FROM_DATABASE=NTBPE Optical Bypass Adapter + +pci:v000018F4d000000C5* + ID_MODEL_FROM_DATABASE=NT20E2 Network Adapter 2x10Gb + +pci:v000018F4d000000D5* + ID_MODEL_FROM_DATABASE=NT40E2-4 Network Adapter 4x10Gb + +pci:v000018F4d000000E5* + ID_MODEL_FROM_DATABASE=NT40E2-1 Network Adapter 1x40Gb + +pci:v000018F4d000000F5* + ID_MODEL_FROM_DATABASE=NT4E2-4T-BP Network Adapter 4x1Gb with Electrical Bypass + +pci:v000018F4d00000105* + ID_MODEL_FROM_DATABASE=NT4E2-4-PTP Network Adapter 4x1Gb + +pci:v000018F4d00000115* + ID_MODEL_FROM_DATABASE=NT20E2-PTP Network Adapter 2x10Gb + +pci:v000018F4d00000125* + ID_MODEL_FROM_DATABASE=NT4E2-4-PTP Network Adapter 4x1Gb + +pci:v000018F4d00000135* + ID_MODEL_FROM_DATABASE=NT20E2-PTP Network Adapter 2x10Gb + +pci:v000018F4d00000145* + ID_MODEL_FROM_DATABASE=NT40E3-4-PTP Network Adapter 4x10Gb + +pci:v000018F4d00000155* + ID_MODEL_FROM_DATABASE=NT100E3-1-PTP Network Adapter 1x100Gb + +pci:v000018F4d00000165* + ID_MODEL_FROM_DATABASE=NT80E3-2-PTP Network Adapter 2x40Gb + +pci:v000018F4d00000175* + ID_MODEL_FROM_DATABASE=NT20E3-2-PTP Network Adapter 2x10Gb + +pci:v000018F6* + ID_VENDOR_FROM_DATABASE=NextIO + +pci:v000018F6d00001000* + ID_MODEL_FROM_DATABASE=[Nexsis] Switch Virtual P2P PCIe Bridge + +pci:v000018F6d00001001* + ID_MODEL_FROM_DATABASE=[Texsis] Switch Virtual P2P PCIe Bridge + +pci:v000018F6d00001050* + ID_MODEL_FROM_DATABASE=[Nexsis] Switch Virtual P2P PCI Bridge + +pci:v000018F6d00001051* + ID_MODEL_FROM_DATABASE=[Texsis] Switch Virtual P2P PCI Bridge + +pci:v000018F6d00002000* + ID_MODEL_FROM_DATABASE=[Nexsis] Switch Integrated Mgmt. Endpoint + +pci:v000018F6d00002001* + ID_MODEL_FROM_DATABASE=[Texsis] Switch Integrated Mgmt. Endpoint + +pci:v000018F7* + ID_VENDOR_FROM_DATABASE=Commtech, Inc. + +pci:v000018F7d00000001* + ID_MODEL_FROM_DATABASE=ESCC-PCI-335 Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000002* + ID_MODEL_FROM_DATABASE=422/4-PCI-335 Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000003* + ID_MODEL_FROM_DATABASE=232/4-1M-PCI Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000004* + ID_MODEL_FROM_DATABASE=422/2-PCI-335 Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000005* + ID_MODEL_FROM_DATABASE=IGESCC-PCI-ISO/1 Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000000A* + ID_MODEL_FROM_DATABASE=232/4-PCI-335 Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000000B* + ID_MODEL_FROM_DATABASE=232/8-PCI-335 Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000000F* + ID_MODEL_FROM_DATABASE=FSCC Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000010* + ID_MODEL_FROM_DATABASE=GSCC Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000011* + ID_MODEL_FROM_DATABASE=QSSB Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000014* + ID_MODEL_FROM_DATABASE=SuperFSCC Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000015* + ID_MODEL_FROM_DATABASE=SuperFSCC-104-LVDS Serial PC/104+ Adapter [Fastcom] + +pci:v000018F7d00000016* + ID_MODEL_FROM_DATABASE=FSCC-232 RS-232 Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000017* + ID_MODEL_FROM_DATABASE=SuperFSCC-104 Serial PC/104+ Adapter [Fastcom] + +pci:v000018F7d00000018* + ID_MODEL_FROM_DATABASE=SuperFSCC/4 Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000019* + ID_MODEL_FROM_DATABASE=SuperFSCC Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000001A* + ID_MODEL_FROM_DATABASE=SuperFSCC-LVDS Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000001B* + ID_MODEL_FROM_DATABASE=FSCC/4 Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000001C* + ID_MODEL_FROM_DATABASE=SuperFSCC/4-LVDS Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000001D* + ID_MODEL_FROM_DATABASE=FSCC Serial PCI Adapter [Fastcom] + +pci:v000018F7d0000001E* + ID_MODEL_FROM_DATABASE=SuperFSCC/4 Serial PCIe Adapter [Fastcom] + +pci:v000018F7d0000001F* + ID_MODEL_FROM_DATABASE=SuperFSCC/4 Serial cPCI Adapter [Fastcom] + +pci:v000018F7d00000020* + ID_MODEL_FROM_DATABASE=422/4-PCIe Serial PCIe Adapter [Fastcom] + +pci:v000018F7d00000021* + ID_MODEL_FROM_DATABASE=422/8-PCIe Serial PCIe Adapter [Fastcom] + +pci:v000018F7d00000022* + ID_MODEL_FROM_DATABASE=SuperFSCC/4-LVDS Serial PCIe Adapter [Fastcom] + +pci:v000018F7d00000023* + ID_MODEL_FROM_DATABASE=SuperFSCC/4 Serial cPCI Adapter [Fastcom] + +pci:v000018F7d00000025* + ID_MODEL_FROM_DATABASE=SuperFSCC/4-LVDS Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000026* + ID_MODEL_FROM_DATABASE=SuperFSCC-LVDS Serial PCI Adapter [Fastcom] + +pci:v000018F7d00000027* + ID_MODEL_FROM_DATABASE=FSCC/4 Serial PCIe Adapter [Fastcom] + +pci:v000018FB* + ID_VENDOR_FROM_DATABASE=Resilience Corporation + +pci:v00001904* + ID_VENDOR_FROM_DATABASE=Hangzhou Silan Microelectronics Co., Ltd. + +pci:v00001904d00002031* + ID_MODEL_FROM_DATABASE=SC92031 PCI Fast Ethernet Adapter + +pci:v00001904d00008139* + ID_MODEL_FROM_DATABASE=RTL8139D [Realtek] PCI 10/100BaseTX ethernet adaptor + +pci:v00001905* + ID_VENDOR_FROM_DATABASE=Micronas USA, Inc. + +pci:v00001912* + ID_VENDOR_FROM_DATABASE=Renesas Technology Corp. + +pci:v00001912d00000002* + ID_MODEL_FROM_DATABASE=SH7780 PCI Controller (PCIC) + +pci:v00001912d00000011* + ID_MODEL_FROM_DATABASE=SH7757 PCIe End-Point [PBI] + +pci:v00001912d00000012* + ID_MODEL_FROM_DATABASE=SH7757 PCIe-PCI Bridge [PPB] + +pci:v00001912d00000013* + ID_MODEL_FROM_DATABASE=SH7757 PCIe Switch [PS] + +pci:v00001912d00000014* + ID_MODEL_FROM_DATABASE=uPD720201 USB 3.0 Host Controller + +pci:v00001912d00000015* + ID_MODEL_FROM_DATABASE=uPD720202 USB 3.0 Host Controller + +pci:v00001912d0000001A* + ID_MODEL_FROM_DATABASE=SH7758 PCIe-PCI Bridge [PPB] + +pci:v00001912d0000001B* + ID_MODEL_FROM_DATABASE=SH7758 PCIe End-Point [PBI] + +pci:v00001912d0000001D* + ID_MODEL_FROM_DATABASE=SH7758 PCIe Switch [PS] + +pci:v00001919* + ID_VENDOR_FROM_DATABASE=Soltek Computer Inc. + +pci:v00001923* + ID_VENDOR_FROM_DATABASE=Sangoma Technologies Corp. + +pci:v00001923d00000040* + ID_MODEL_FROM_DATABASE=A200/Remora FXO/FXS Analog AFT card + +pci:v00001923d00000100* + ID_MODEL_FROM_DATABASE=A104d QUAD T1/E1 AFT card + +pci:v00001923d00000300* + ID_MODEL_FROM_DATABASE=A101 single-port T1/E1 + +pci:v00001923d00000400* + ID_MODEL_FROM_DATABASE=A104u Quad T1/E1 AFT + +pci:v00001924* + ID_VENDOR_FROM_DATABASE=Solarflare Communications + +pci:v00001924d00000703* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] + +pci:v00001924d00000703sv000010B8sd00000102* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SMC10GPCIe-10BT (A2) [TigerCard]) + +pci:v00001924d00000703sv000010B8sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SMC10GPCIe-10BT (A3) [TigerCard]) + +pci:v00001924d00000703sv000010B8sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SMC10GPCIe-XFP (A1) [TigerCard]) + +pci:v00001924d00000703sv00001924sd00000101* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4001-A1) + +pci:v00001924d00000703sv00001924sd00000102* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4001-A2) + +pci:v00001924d00000703sv00001924sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4001-A3) + +pci:v00001924d00000703sv00001924sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4002-A1) + +pci:v00001924d00000703sv00001924sd00000301* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4003-A1) + +pci:v00001924d00000703sv00001924sd00000302* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4003-A2) + +pci:v00001924d00000703sv00001924sd00000303* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4003-A3) + +pci:v00001924d00000703sv00001924sd00000304* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4003-A4) + +pci:v00001924d00000703sv00001924sd00000500* + ID_MODEL_FROM_DATABASE=SFC4000 rev A net [Solarstorm] (SFE4005-A0) + +pci:v00001924d00000710* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] + +pci:v00001924d00000710sv000010B8sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SMC10GPCIe-10BT (A3) [TigerCard]) + +pci:v00001924d00000710sv000010B8sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SMC10GPCIe-XFP (A1) [TigerCard]) + +pci:v00001924d00000710sv00001924sd00000102* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4001-A2) + +pci:v00001924d00000710sv00001924sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4001-A3) + +pci:v00001924d00000710sv00001924sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4002-A1) + +pci:v00001924d00000710sv00001924sd00000302* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4003-A2) + +pci:v00001924d00000710sv00001924sd00000303* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4003-A3) + +pci:v00001924d00000710sv00001924sd00000304* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4003-A4) + +pci:v00001924d00000710sv00001924sd00000500* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFE4005-A0) + +pci:v00001924d00000710sv00001924sd00005102* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4111T-A2) + +pci:v00001924d00000710sv00001924sd00005103* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4111T-R3) + +pci:v00001924d00000710sv00001924sd00005104* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4111T-R4) + +pci:v00001924d00000710sv00001924sd00005105* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4111T-R5) + +pci:v00001924d00000710sv00001924sd00005201* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4112F-R1) + +pci:v00001924d00000710sv00001924sd00005202* + ID_MODEL_FROM_DATABASE=SFC4000 rev B [Solarstorm] (SFN4112F-R2) + +pci:v00001924d00000803* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] + +pci:v00001924d00000803sv00001014sd00000478* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (2-port 10GbE Low-Latency (R7)) + +pci:v00001924d00000803sv00001014sd00000479* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (2-port 10GbE OpenOnload (R7)) + +pci:v00001924d00000803sv00001014sd000004A7* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (Solarflare 10Gb Low-latency Dual-port HBA (R7)) + +pci:v00001924d00000803sv00001014sd000004A8* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (Solarflare 10Gb Dual-port HBA (R7)) + +pci:v00001924d00000803sv0000103Csd00002132* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (Ethernet 10Gb 2-port 570FLR-SFP+ Adapter (R1)) + +pci:v00001924d00000803sv0000103Csd00002136* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (Ethernet 10Gb 2-port 570SFP+ Adapter (R7)) + +pci:v00001924d00000803sv00001924sd00001201* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFA6902F-R1 SFP+ AOE Adapter) + +pci:v00001924d00000803sv00001924sd00006200* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R0 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006201* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R1 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006202* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R2 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006204* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R4 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006205* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R5 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006206* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R6 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006207* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5122F-R7 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006210* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5322F-R0 SFP+ Precision Time Synchronization Server Adapter) + +pci:v00001924d00000803sv00001924sd00006211* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5322F-R1 SFP+ Precision Time Synchronization Server Adapter) + +pci:v00001924d00000803sv00001924sd00006217* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5322F-R7 SFP+ Precision Time Synchronization Server Adapter) + +pci:v00001924d00000803sv00001924sd00006227* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN6122F-R7 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006237* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN6322F-R7 SFP+ Precision Time Synchronization Server Adapter) + +pci:v00001924d00000803sv00001924sd00006501* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5802K-R1 Mezzanine Adapter) + +pci:v00001924d00000803sv00001924sd00006511* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5814H-R1 Mezzanine Adapter) + +pci:v00001924d00000803sv00001924sd00006521* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5812H-R1 Mezzanine Adapter) + +pci:v00001924d00000803sv00001924sd00006562* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN6832F-R2 SFP+ Mezzanine Adapter) + +pci:v00001924d00000803sv00001924sd00006A05* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5112F-R5 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00006A06* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5112F-R6 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00007206* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5162F-R6 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00007207* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5162F-R7 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00007A06* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5152F-R6 SFP+ Server Adapter) + +pci:v00001924d00000803sv00001924sd00007A07* + ID_MODEL_FROM_DATABASE=SFC9020 [Solarstorm] (SFN5152F-R7 SFP+ Server Adapter) + +pci:v00001924d00000813* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] + +pci:v00001924d00000813sv00001924sd00006100* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5121T-R0 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00006102* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5121T-R2 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00006103* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5121T-R3 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00006104* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5121T-R4 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00006902* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5111T-R2 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00006904* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5111T-R4 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00007104* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5161T-R4 10GBASE-T Server Adapter) + +pci:v00001924d00000813sv00001924sd00007904* + ID_MODEL_FROM_DATABASE=SFL9021 [Solarstorm] (SFN5151T-R4 10GBASE-T Server Adapter) + +pci:v00001924d00000903* + ID_MODEL_FROM_DATABASE=SFC9120 + +pci:v00001924d00000903sv00001014sd000004CC* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7122F-R2 2x10GbE SFP+ Flareon Ultra) + +pci:v00001924d00000903sv00001924sd00008002* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7122F-R1 SFP+ Server Adapter) + +pci:v00001924d00000903sv00001924sd00008003* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7x41Q-R1 Flareon Ultra 7000 Series 10/40G Adapter) + +pci:v00001924d00000903sv00001924sd00008006* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7022F-R1 SFP+ Server Adapter) + +pci:v00001924d00000903sv00001924sd00008007* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7322F-R2 Precision Time SFP+ Server Adapter) + +pci:v00001924d00000903sv00001924sd00008009* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7x22F-R2 Flareon Ultra 7000 Series 10G Adapter) + +pci:v00001924d00000903sv00001924sd0000800A* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7x02F-R2 Flareon 7000 Series 10G Adapter) + +pci:v00001924d00000903sv00001924sd0000800C* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7x22F-R3 Flareon Ultra 7000 Series 10G Adapter) + +pci:v00001924d00000903sv00001924sd0000800D* + ID_MODEL_FROM_DATABASE=SFC9120 (SFN7x02F-R3 Flareon 7000 Series 10G Adapter) + +pci:v00001924d00000903sv00001924sd00008010* + ID_MODEL_FROM_DATABASE=SFC9120 (SFA7942Q-R1 QSFP+ AOE Adapter) + +pci:v00001924d00000903sv00001924sd00008015* + ID_MODEL_FROM_DATABASE=SFC9120 (SFA7942Q-A5-0-R1 QSFP+ AOE Adapter) + +pci:v00001924d00000923* + ID_MODEL_FROM_DATABASE=SFC9140 + +pci:v00001924d00000923sv00001924sd0000800B* + ID_MODEL_FROM_DATABASE=SFC9140 (SFN7x42Q-R1 Flareon Ultra 7000 Series 10/40G Adapter) + +pci:v00001924d00000923sv00001924sd0000800E* + ID_MODEL_FROM_DATABASE=SFC9140 (SFN7x42Q-R2 Flareon Ultra 7000 Series 10/40G Adapter) + +pci:v00001924d00000923sv00001924sd0000800F* + ID_MODEL_FROM_DATABASE=SFC9140 (SFN7xx4F-R1 Flareon Ultra 7000 Series 10G Adapter) + +pci:v00001924d00000A03* + ID_MODEL_FROM_DATABASE=SFC9220 + +pci:v00001924d00000A03sv00001924sd00008011* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN 8022-R1 Solarflare Flareon 8000 Series 10G Adapter) + +pci:v00001924d00000A03sv00001924sd00008012* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8522-R1 Flareon Ultra 8000 Series 10G Adapter) + +pci:v00001924d00000A03sv00001924sd00008013* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8042-R1 Solarflare Flareon 8000 Series 10/40G Adapter) + +pci:v00001924d00000A03sv00001924sd00008014* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8542-R1 Flareon Ultra 8000 Series 10/40G Adapter) + +pci:v00001924d00000A03sv00001924sd00008016* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8022-R2 Flareon 8000 Series 10G Adapter) + +pci:v00001924d00000A03sv00001924sd00008017* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8522-R2 Flareon Ultra 8000 Series 10G Adapter) + +pci:v00001924d00000A03sv00001924sd00008018* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8042-R2 Flareon 8000 Series 10/40G Adapter) + +pci:v00001924d00000A03sv00001924sd00008019* + ID_MODEL_FROM_DATABASE=SFC9220 (SFN8542-R2 Flareon Ultra 8000 Series 10/40G Adapter) + +pci:v00001924d00001803* + ID_MODEL_FROM_DATABASE=SFC9020 Virtual Function [Solarstorm] + +pci:v00001924d00001813* + ID_MODEL_FROM_DATABASE=SFL9021 Virtual Function [Solarstorm] + +pci:v00001924d00001903* + ID_MODEL_FROM_DATABASE=SFC9120 Virtual Function + +pci:v00001924d00001923* + ID_MODEL_FROM_DATABASE=SFC9140 Virtual Function + +pci:v00001924d00006703* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] + +pci:v00001924d00006703sv000010B8sd00000102* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SMC10GPCIe-10BT (A2) [TigerCard]) + +pci:v00001924d00006703sv000010B8sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SMC10GPCIe-10BT (A3) [TigerCard]) + +pci:v00001924d00006703sv000010B8sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SMC10GPCIe-XFP (A1) [TigerCard]) + +pci:v00001924d00006703sv00001924sd00000101* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4001-A1) + +pci:v00001924d00006703sv00001924sd00000102* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4001-A2) + +pci:v00001924d00006703sv00001924sd00000103* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4001-A3) + +pci:v00001924d00006703sv00001924sd00000201* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4002-A1) + +pci:v00001924d00006703sv00001924sd00000301* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4003-A1) + +pci:v00001924d00006703sv00001924sd00000302* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4003-A2) + +pci:v00001924d00006703sv00001924sd00000303* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4003-A3) + +pci:v00001924d00006703sv00001924sd00000304* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4003-A4) + +pci:v00001924d00006703sv00001924sd00000500* + ID_MODEL_FROM_DATABASE=SFC4000 rev A iSCSI/Onload [Solarstorm] (SFE4005-A0) + +pci:v00001924d0000C101* + ID_MODEL_FROM_DATABASE=EF1-21022T [EtherFabric] + +pci:v0000192A* + ID_VENDOR_FROM_DATABASE=BiTMICRO Networks Inc. + +pci:v0000192E* + ID_VENDOR_FROM_DATABASE=TransDimension + +pci:v00001931* + ID_VENDOR_FROM_DATABASE=Option N.V. + +pci:v00001931d0000000C* + ID_MODEL_FROM_DATABASE=Qualcomm MSM6275 UMTS chip + +pci:v00001932* + ID_VENDOR_FROM_DATABASE=DiBcom + +pci:v0000193C* + ID_VENDOR_FROM_DATABASE=MAXIM Integrated Products + +pci:v0000193F* + ID_VENDOR_FROM_DATABASE=AHA Products Group + +pci:v0000193Fd00000001* + ID_MODEL_FROM_DATABASE=AHA36x-PCIX + +pci:v0000193Fd00000360* + ID_MODEL_FROM_DATABASE=AHA360-PCIe + +pci:v0000193Fd00000363* + ID_MODEL_FROM_DATABASE=AHA363-PCIe + +pci:v0000193Fd00000364* + ID_MODEL_FROM_DATABASE=AHA364-PCIe + +pci:v0000193Fd00000367* + ID_MODEL_FROM_DATABASE=AHA367-PCIe + +pci:v0000193Fd00000370* + ID_MODEL_FROM_DATABASE=AHA370-PCIe + +pci:v0000193Fd00000604* + ID_MODEL_FROM_DATABASE=AHA604 + +pci:v0000193Fd00000605* + ID_MODEL_FROM_DATABASE=AHA605 + +pci:v0000193Fd00003641* + ID_MODEL_FROM_DATABASE=AHA3641 + +pci:v0000193Fd00003642* + ID_MODEL_FROM_DATABASE=AHA3642 + +pci:v0000193Fd00006101* + ID_MODEL_FROM_DATABASE=AHA6101 + +pci:v0000193Fd00006102* + ID_MODEL_FROM_DATABASE=AHA6102 + +pci:v00001942* + ID_VENDOR_FROM_DATABASE=ClearSpeed Technology plc + +pci:v00001942d0000E511* + ID_MODEL_FROM_DATABASE=Advance X620 accelerator card + +pci:v00001942d0000E521* + ID_MODEL_FROM_DATABASE=Advance e620 accelerator card + +pci:v00001947* + ID_VENDOR_FROM_DATABASE=C-guys, Inc. + +pci:v00001947d00004743* + ID_MODEL_FROM_DATABASE=CG200 Dual SD/SDIO Host controller device + +pci:v00001948* + ID_VENDOR_FROM_DATABASE=Alpha Networks Inc. + +pci:v0000194A* + ID_VENDOR_FROM_DATABASE=DapTechnology B.V. + +pci:v0000194Ad00001111* + ID_MODEL_FROM_DATABASE=FireSpy3850 + +pci:v0000194Ad00001112* + ID_MODEL_FROM_DATABASE=FireSpy450b + +pci:v0000194Ad00001113* + ID_MODEL_FROM_DATABASE=FireSpy450bT + +pci:v0000194Ad00001114* + ID_MODEL_FROM_DATABASE=FireSpy850 + +pci:v0000194Ad00001115* + ID_MODEL_FROM_DATABASE=FireSpy850bT + +pci:v0000194Ad00001200* + ID_MODEL_FROM_DATABASE=FireTrac 3460bT + +pci:v0000194Ad00001201* + ID_MODEL_FROM_DATABASE=FireTrac 3460bT (fallback firmware) + +pci:v0000194Ad00001202* + ID_MODEL_FROM_DATABASE=FireTrac 3460bT + +pci:v0000194Ad00001203* + ID_MODEL_FROM_DATABASE=FireTrac 3460bT (fallback firmware) + +pci:v00001954* + ID_VENDOR_FROM_DATABASE=One Stop Systems, Inc. + +pci:v00001957* + ID_VENDOR_FROM_DATABASE=Freescale Semiconductor Inc + +pci:v00001957d00000012* + ID_MODEL_FROM_DATABASE=MPC8548E + +pci:v00001957d00000013* + ID_MODEL_FROM_DATABASE=MPC8548 + +pci:v00001957d00000014* + ID_MODEL_FROM_DATABASE=MPC8543E + +pci:v00001957d00000015* + ID_MODEL_FROM_DATABASE=MPC8543 + +pci:v00001957d00000018* + ID_MODEL_FROM_DATABASE=MPC8547E + +pci:v00001957d00000019* + ID_MODEL_FROM_DATABASE=MPC8545E + +pci:v00001957d0000001A* + ID_MODEL_FROM_DATABASE=MPC8545 + +pci:v00001957d00000020* + ID_MODEL_FROM_DATABASE=MPC8568E + +pci:v00001957d00000021* + ID_MODEL_FROM_DATABASE=MPC8568 + +pci:v00001957d00000022* + ID_MODEL_FROM_DATABASE=MPC8567E + +pci:v00001957d00000023* + ID_MODEL_FROM_DATABASE=MPC8567 + +pci:v00001957d00000030* + ID_MODEL_FROM_DATABASE=MPC8533E + +pci:v00001957d00000031* + ID_MODEL_FROM_DATABASE=MPC8533 + +pci:v00001957d00000032* + ID_MODEL_FROM_DATABASE=MPC8544E + +pci:v00001957d00000033* + ID_MODEL_FROM_DATABASE=MPC8544 + +pci:v00001957d00000040* + ID_MODEL_FROM_DATABASE=MPC8572E + +pci:v00001957d00000041* + ID_MODEL_FROM_DATABASE=MPC8572 + +pci:v00001957d00000050* + ID_MODEL_FROM_DATABASE=MPC8536E + +pci:v00001957d00000051* + ID_MODEL_FROM_DATABASE=MPC8536 + +pci:v00001957d00000052* + ID_MODEL_FROM_DATABASE=MPC8535E + +pci:v00001957d00000053* + ID_MODEL_FROM_DATABASE=MPC8535 + +pci:v00001957d00000060* + ID_MODEL_FROM_DATABASE=MPC8569 + +pci:v00001957d00000061* + ID_MODEL_FROM_DATABASE=MPC8569E + +pci:v00001957d00000070* + ID_MODEL_FROM_DATABASE=P2020E + +pci:v00001957d00000071* + ID_MODEL_FROM_DATABASE=P2020 + +pci:v00001957d00000078* + ID_MODEL_FROM_DATABASE=P2010E + +pci:v00001957d00000079* + ID_MODEL_FROM_DATABASE=P2010 + +pci:v00001957d00000080* + ID_MODEL_FROM_DATABASE=MPC8349E + +pci:v00001957d00000081* + ID_MODEL_FROM_DATABASE=MPC8349 + +pci:v00001957d00000082* + ID_MODEL_FROM_DATABASE=MPC8347E TBGA + +pci:v00001957d00000083* + ID_MODEL_FROM_DATABASE=MPC8347 TBGA + +pci:v00001957d00000084* + ID_MODEL_FROM_DATABASE=MPC8347E PBGA + +pci:v00001957d00000084sv0000110Asd00004074* + ID_MODEL_FROM_DATABASE=MPC8347E PBGA (SIMATIC NET CP 1628) + +pci:v00001957d00000085* + ID_MODEL_FROM_DATABASE=MPC8347 PBGA + +pci:v00001957d00000085sv0000110Asd00004046* + ID_MODEL_FROM_DATABASE=MPC8347 PBGA (SIMATIC NET CP 1623) + +pci:v00001957d00000086* + ID_MODEL_FROM_DATABASE=MPC8343E + +pci:v00001957d00000087* + ID_MODEL_FROM_DATABASE=MPC8343 + +pci:v00001957d000000B4* + ID_MODEL_FROM_DATABASE=MPC8315E + +pci:v00001957d000000B6* + ID_MODEL_FROM_DATABASE=MPC8314E + +pci:v00001957d000000B6sv00001A56sd00001101* + ID_MODEL_FROM_DATABASE=MPC8314E (Killer Xeno Pro Gigabit Ethernet Controller) + +pci:v00001957d000000C2* + ID_MODEL_FROM_DATABASE=MPC8379E + +pci:v00001957d000000C3* + ID_MODEL_FROM_DATABASE=MPC8379 + +pci:v00001957d000000C4* + ID_MODEL_FROM_DATABASE=MPC8378E + +pci:v00001957d000000C5* + ID_MODEL_FROM_DATABASE=MPC8378 + +pci:v00001957d000000C6* + ID_MODEL_FROM_DATABASE=MPC8377E + +pci:v00001957d000000C7* + ID_MODEL_FROM_DATABASE=MPC8377 + +pci:v00001957d00000100* + ID_MODEL_FROM_DATABASE=P1020E + +pci:v00001957d00000101* + ID_MODEL_FROM_DATABASE=P1020 + +pci:v00001957d00000102* + ID_MODEL_FROM_DATABASE=P1021E + +pci:v00001957d00000103* + ID_MODEL_FROM_DATABASE=P1021 + +pci:v00001957d00000108* + ID_MODEL_FROM_DATABASE=P1011E + +pci:v00001957d00000109* + ID_MODEL_FROM_DATABASE=P1011 + +pci:v00001957d0000010A* + ID_MODEL_FROM_DATABASE=P1012E + +pci:v00001957d0000010B* + ID_MODEL_FROM_DATABASE=P1012 + +pci:v00001957d00000110* + ID_MODEL_FROM_DATABASE=P1022E + +pci:v00001957d00000111* + ID_MODEL_FROM_DATABASE=P1022 + +pci:v00001957d00000111sv00001C7Fsd00005200* + ID_MODEL_FROM_DATABASE=P1022 (EB5200) + +pci:v00001957d00000118* + ID_MODEL_FROM_DATABASE=P1013E + +pci:v00001957d00000119* + ID_MODEL_FROM_DATABASE=P1013 + +pci:v00001957d00000128* + ID_MODEL_FROM_DATABASE=P1010 + +pci:v00001957d00000400* + ID_MODEL_FROM_DATABASE=P4080E + +pci:v00001957d00000401* + ID_MODEL_FROM_DATABASE=P4080 + +pci:v00001957d00000408* + ID_MODEL_FROM_DATABASE=P4040E + +pci:v00001957d00000409* + ID_MODEL_FROM_DATABASE=P4040 + +pci:v00001957d0000041F* + ID_MODEL_FROM_DATABASE=P3041 + +pci:v00001957d00000440* + ID_MODEL_FROM_DATABASE=T4240 with security + +pci:v00001957d00000441* + ID_MODEL_FROM_DATABASE=T4240 without security + +pci:v00001957d00000446* + ID_MODEL_FROM_DATABASE=T4160 with security + +pci:v00001957d00000447* + ID_MODEL_FROM_DATABASE=T4160 without security + +pci:v00001957d00000830* + ID_MODEL_FROM_DATABASE=T2080 with security + +pci:v00001957d00000831* + ID_MODEL_FROM_DATABASE=T2080 without security + +pci:v00001957d00000838* + ID_MODEL_FROM_DATABASE=T2081 with security + +pci:v00001957d00000839* + ID_MODEL_FROM_DATABASE=T2081 without security + +pci:v00001957d0000580C* + ID_MODEL_FROM_DATABASE=MPC5121e + +pci:v00001957d00007010* + ID_MODEL_FROM_DATABASE=MPC8641 PCI Host Bridge + +pci:v00001957d00007011* + ID_MODEL_FROM_DATABASE=MPC8641D PCI Host Bridge + +pci:v00001957d00007018* + ID_MODEL_FROM_DATABASE=MPC8610 + +pci:v00001957d0000C006* + ID_MODEL_FROM_DATABASE=MPC8308 + +pci:v00001957d0000C006sv00001A56sd00001201* + ID_MODEL_FROM_DATABASE=MPC8308 (Killer E2100 Gigabit Ethernet Controller) + +pci:v00001957d0000FC02* + ID_MODEL_FROM_DATABASE=RedStone + +pci:v00001957d0000FC03* + ID_MODEL_FROM_DATABASE=CFI + +pci:v00001958* + ID_VENDOR_FROM_DATABASE=Faster Technology, LLC. + +pci:v00001959* + ID_VENDOR_FROM_DATABASE=PA Semi, Inc + +pci:v00001959d0000A000* + ID_MODEL_FROM_DATABASE=PA6T Core + +pci:v00001959d0000A001* + ID_MODEL_FROM_DATABASE=PWRficient Host Bridge + +pci:v00001959d0000A002* + ID_MODEL_FROM_DATABASE=PWRficient PCI-Express Port + +pci:v00001959d0000A003* + ID_MODEL_FROM_DATABASE=PWRficient SMBus Controller + +pci:v00001959d0000A004* + ID_MODEL_FROM_DATABASE=PWRficient 16550 UART + +pci:v00001959d0000A005* + ID_MODEL_FROM_DATABASE=PWRficient Gigabit Ethernet + +pci:v00001959d0000A006* + ID_MODEL_FROM_DATABASE=PWRficient 10-Gigabit Ethernet + +pci:v00001959d0000A007* + ID_MODEL_FROM_DATABASE=PWRficient DMA Controller + +pci:v00001959d0000A008* + ID_MODEL_FROM_DATABASE=PWRficient LPC/Localbus Interface + +pci:v00001959d0000A009* + ID_MODEL_FROM_DATABASE=PWRficient L2 Cache + +pci:v00001959d0000A00A* + ID_MODEL_FROM_DATABASE=PWRficient DDR2 Memory Controller + +pci:v00001959d0000A00B* + ID_MODEL_FROM_DATABASE=PWRficient SERDES + +pci:v00001959d0000A00C* + ID_MODEL_FROM_DATABASE=PWRficient System/Debug Controller + +pci:v00001959d0000A00D* + ID_MODEL_FROM_DATABASE=PWRficient PCI-Express Internal Endpoint + +pci:v00001966* + ID_VENDOR_FROM_DATABASE=Orad Hi-Tec Systems + +pci:v00001966d00001975* + ID_MODEL_FROM_DATABASE=DVG64 family + +pci:v00001966d00001977* + ID_MODEL_FROM_DATABASE=DVG128 family + +pci:v00001969* + ID_VENDOR_FROM_DATABASE=Qualcomm Atheros + +pci:v00001969d00001026* + ID_MODEL_FROM_DATABASE=AR8121/AR8113/AR8114 Gigabit or Fast Ethernet + +pci:v00001969d00001026sv00001043sd00008304* + ID_MODEL_FROM_DATABASE=AR8121/AR8113/AR8114 Gigabit or Fast Ethernet (P5KPL-CM Motherboard) + +pci:v00001969d00001048* + ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet + +pci:v00001969d00001048sv00001043sd00008226* + ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet (P5KPL-VM Motherboard) + +pci:v00001969d00001062* + ID_MODEL_FROM_DATABASE=AR8132 Fast Ethernet + +pci:v00001969d00001063* + ID_MODEL_FROM_DATABASE=AR8131 Gigabit Ethernet + +pci:v00001969d00001063sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=AR8131 Gigabit Ethernet (GA-G31M-ES2L Motherboard) + +pci:v00001969d00001063sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=AR8131 Gigabit Ethernet (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00001969d00001066* + ID_MODEL_FROM_DATABASE=Attansic L2c Gigabit Ethernet + +pci:v00001969d00001067* + ID_MODEL_FROM_DATABASE=Attansic L1c Gigabit Ethernet + +pci:v00001969d00001073* + ID_MODEL_FROM_DATABASE=AR8151 v1.0 Gigabit Ethernet + +pci:v00001969d00001083* + ID_MODEL_FROM_DATABASE=AR8151 v2.0 Gigabit Ethernet + +pci:v00001969d00001090* + ID_MODEL_FROM_DATABASE=AR8162 Fast Ethernet + +pci:v00001969d00001090sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=AR8162 Fast Ethernet (VivoBook X202E, X202EV) + +pci:v00001969d00001091* + ID_MODEL_FROM_DATABASE=AR8161 Gigabit Ethernet + +pci:v00001969d00001091sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=AR8161 Gigabit Ethernet (N56VZ) + +pci:v00001969d000010A0* + ID_MODEL_FROM_DATABASE=QCA8172 Fast Ethernet + +pci:v00001969d000010A1* + ID_MODEL_FROM_DATABASE=QCA8171 Gigabit Ethernet + +pci:v00001969d00002048* + ID_MODEL_FROM_DATABASE=Attansic L2 Fast Ethernet + +pci:v00001969d00002060* + ID_MODEL_FROM_DATABASE=AR8152 v1.1 Fast Ethernet + +pci:v00001969d00002062* + ID_MODEL_FROM_DATABASE=AR8152 v2.0 Fast Ethernet + +pci:v00001969d0000E091* + ID_MODEL_FROM_DATABASE=Killer E220x Gigabit Ethernet Controller + +pci:v00001969d0000E0A1* + ID_MODEL_FROM_DATABASE=Killer E2400 Gigabit Ethernet Controller + +pci:v0000196A* + ID_VENDOR_FROM_DATABASE=Sensory Networks Inc. + +pci:v0000196Ad00000101* + ID_MODEL_FROM_DATABASE=NodalCore C-1000 Content Classification Accelerator + +pci:v0000196Ad00000102* + ID_MODEL_FROM_DATABASE=NodalCore C-2000 Content Classification Accelerator + +pci:v0000196Ad00000105* + ID_MODEL_FROM_DATABASE=NodalCore C-3000 Content Classification Accelerator + +pci:v0000196D* + ID_VENDOR_FROM_DATABASE=Club-3D BV + +pci:v00001971* + ID_VENDOR_FROM_DATABASE=AGEIA Technologies, Inc. + +pci:v00001971d00001011* + ID_MODEL_FROM_DATABASE=Physics Processing Unit [PhysX] + +pci:v00001971d00001011sv00001043sd00000001* + ID_MODEL_FROM_DATABASE=Physics Processing Unit [PhysX] (PhysX P1) + +pci:v00001974* + ID_VENDOR_FROM_DATABASE=Eberspaecher Electronics + +pci:v00001976* + ID_VENDOR_FROM_DATABASE=TRENDnet + +pci:v00001977* + ID_VENDOR_FROM_DATABASE=Parsec + +pci:v0000197B* + ID_VENDOR_FROM_DATABASE=JMicron Technology Corp. + +pci:v0000197Bd00000250* + ID_MODEL_FROM_DATABASE=JMC250 PCI Express Gigabit Ethernet Controller + +pci:v0000197Bd00000260* + ID_MODEL_FROM_DATABASE=JMC260 PCI Express Fast Ethernet Controller + +pci:v0000197Bd00000368* + ID_MODEL_FROM_DATABASE=JMB368 IDE controller + +pci:v0000197Bd00002360* + ID_MODEL_FROM_DATABASE=JMB360 AHCI Controller + +pci:v0000197Bd00002361* + ID_MODEL_FROM_DATABASE=JMB361 AHCI/IDE + +pci:v0000197Bd00002361sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=JMB361 AHCI/IDE (P965 Neo MS-7235 mainboard) + +pci:v0000197Bd00002362* + ID_MODEL_FROM_DATABASE=JMB362 SATA Controller + +pci:v0000197Bd00002362sv00001043sd00008460* + ID_MODEL_FROM_DATABASE=JMB362 SATA Controller (P8P67 Deluxe Motherboard) + +pci:v0000197Bd00002363* + ID_MODEL_FROM_DATABASE=JMB363 SATA/IDE Controller + +pci:v0000197Bd00002363sv00001043sd000081E4* + ID_MODEL_FROM_DATABASE=JMB363 SATA/IDE Controller (P5B [JMB363]) + +pci:v0000197Bd00002363sv00001458sd0000B000* + ID_MODEL_FROM_DATABASE=JMB363 SATA/IDE Controller (Motherboard) + +pci:v0000197Bd00002363sv00001849sd00002363* + ID_MODEL_FROM_DATABASE=JMB363 SATA/IDE Controller (Motherboard (one of many)) + +pci:v0000197Bd00002364* + ID_MODEL_FROM_DATABASE=JMB364 AHCI Controller + +pci:v0000197Bd00002365* + ID_MODEL_FROM_DATABASE=JMB365 AHCI/IDE + +pci:v0000197Bd00002366* + ID_MODEL_FROM_DATABASE=JMB366 AHCI/IDE + +pci:v0000197Bd00002368* + ID_MODEL_FROM_DATABASE=JMB368 IDE controller + +pci:v0000197Bd00002369* + ID_MODEL_FROM_DATABASE=JMB369 Serial ATA Controller + +pci:v0000197Bd00002380* + ID_MODEL_FROM_DATABASE=IEEE 1394 Host Controller + +pci:v0000197Bd00002381* + ID_MODEL_FROM_DATABASE=Standard SD Host Controller + +pci:v0000197Bd00002382* + ID_MODEL_FROM_DATABASE=SD/MMC Host Controller + +pci:v0000197Bd00002383* + ID_MODEL_FROM_DATABASE=MS Host Controller + +pci:v0000197Bd00002384* + ID_MODEL_FROM_DATABASE=xD Host Controller + +pci:v0000197Bd00002386* + ID_MODEL_FROM_DATABASE=Standard SD Host Controller + +pci:v0000197Bd00002387* + ID_MODEL_FROM_DATABASE=SD/MMC Host Controller + +pci:v0000197Bd00002388* + ID_MODEL_FROM_DATABASE=MS Host Controller + +pci:v0000197Bd00002389* + ID_MODEL_FROM_DATABASE=xD Host Controller + +pci:v0000197Bd00002391* + ID_MODEL_FROM_DATABASE=Standard SD Host Controller + +pci:v0000197Bd00002392* + ID_MODEL_FROM_DATABASE=SD/MMC Host Controller + +pci:v0000197Bd00002393* + ID_MODEL_FROM_DATABASE=MS Host Controller + +pci:v0000197Bd00002394* + ID_MODEL_FROM_DATABASE=xD Host Controller + +pci:v00001982* + ID_VENDOR_FROM_DATABASE=Distant Early Warning Communications Inc + +pci:v00001982d00001600* + ID_MODEL_FROM_DATABASE=OX16C954 HOST-A + +pci:v00001982d000016FF* + ID_MODEL_FROM_DATABASE=OX16C954 HOST-B + +pci:v00001989* + ID_VENDOR_FROM_DATABASE=Montilio Inc. + +pci:v00001989d00000001* + ID_MODEL_FROM_DATABASE=RapidFile Bridge + +pci:v00001989d00008001* + ID_MODEL_FROM_DATABASE=RapidFile + +pci:v0000198A* + ID_VENDOR_FROM_DATABASE=Nallatech Ltd. + +pci:v00001993* + ID_VENDOR_FROM_DATABASE=Innominate Security Technologies AG + +pci:v00001999* + ID_VENDOR_FROM_DATABASE=A-Logics + +pci:v00001999d0000A900* + ID_MODEL_FROM_DATABASE=AM-7209 Video Processor + +pci:v0000199A* + ID_VENDOR_FROM_DATABASE=Pulse-LINK, Inc. + +pci:v0000199D* + ID_VENDOR_FROM_DATABASE=Xsigo Systems + +pci:v0000199Dd00008209* + ID_MODEL_FROM_DATABASE=Virtual NIC Device + +pci:v0000199Dd0000890A* + ID_MODEL_FROM_DATABASE=Virtual HBA Device + +pci:v0000199F* + ID_VENDOR_FROM_DATABASE=Auvitek + +pci:v0000199Fd00008501* + ID_MODEL_FROM_DATABASE=AU85X1 PCI REV1.1 + +pci:v0000199Fd00008521* + ID_MODEL_FROM_DATABASE=AU8521 TV card + +pci:v000019A2* + ID_VENDOR_FROM_DATABASE=Emulex Corporation + +pci:v000019A2d00000120* + ID_MODEL_FROM_DATABASE=x1 PCIe Gen2 Bridge[Pilot4] + +pci:v000019A2d00000200* + ID_MODEL_FROM_DATABASE=BladeEngine 10Gb PCI-E iSCSI adapter + +pci:v000019A2d00000201* + ID_MODEL_FROM_DATABASE=BladeEngine 10Gb PCIe Network Adapter + +pci:v000019A2d00000211* + ID_MODEL_FROM_DATABASE=BladeEngine2 10Gb Gen2 PCIe Network Adapter + +pci:v000019A2d00000212* + ID_MODEL_FROM_DATABASE=BladeEngine2 10Gb Gen2 PCIe iSCSI Adapter + +pci:v000019A2d00000221* + ID_MODEL_FROM_DATABASE=BladeEngine3 10Gb Gen2 PCIe Network Adapter + +pci:v000019A2d00000222* + ID_MODEL_FROM_DATABASE=BladeEngine3 10Gb Gen2 PCIe iSCSI Adapter + +pci:v000019A2d00000700* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC + +pci:v000019A2d00000700sv0000103Csd00001747* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (NC550SFP DualPort 10GbE Server Adapter) + +pci:v000019A2d00000700sv0000103Csd00001749* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (NC550SFP Dual Port Server Adapter) + +pci:v000019A2d00000700sv0000103Csd0000174A* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (NC551m Dual Port FlexFabric 10Gb Adapter) + +pci:v000019A2d00000700sv0000103Csd0000174B* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (StorageWorks NC550 DualPort Converged Network Adapter) + +pci:v000019A2d00000700sv0000103Csd00003314* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (NC551i Dual Port FlexFabric 10Gb Adapter) + +pci:v000019A2d00000702* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb iSCSI Initiator + +pci:v000019A2d00000704* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb FCoE Initiator + +pci:v000019A2d00000710* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) + +pci:v000019A2d00000710sv0000103Csd00003315* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) (NC553i 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A2d00000710sv0000103Csd00003340* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) (NC552SFP 2-port 10Gb Server Adapter) + +pci:v000019A2d00000710sv0000103Csd00003341* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) (NC552m 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A2d00000710sv0000103Csd00003345* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) (NC553m 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A2d00000710sv0000103Csd0000337B* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb NIC (be3) (NC554FLB 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A2d00000712* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb iSCSI Initiator (be3) + +pci:v000019A2d00000714* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb FCoE Initiator (be3) + +pci:v000019A2d00000714sv0000103Csd00003315* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb FCoE Initiator (be3) (NC553i 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A2d00000714sv0000103Csd0000337B* + ID_MODEL_FROM_DATABASE=OneConnect 10Gb FCoE Initiator (be3) (NC554FLB 10Gb 2-port FlexFabric Converged Network Adapter) + +pci:v000019A8* + ID_VENDOR_FROM_DATABASE=DAQDATA GmbH + +pci:v000019AC* + ID_VENDOR_FROM_DATABASE=Kasten Chase Applied Research + +pci:v000019ACd00000001* + ID_MODEL_FROM_DATABASE=ACA2400 Crypto Accelerator + +pci:v000019AE* + ID_VENDOR_FROM_DATABASE=Progeny Systems Corporation + +pci:v000019AEd00000520* + ID_MODEL_FROM_DATABASE=4135 HFT Interface Controller + +pci:v000019AEd00000521* + ID_MODEL_FROM_DATABASE=Decimator + +pci:v000019BA* + ID_VENDOR_FROM_DATABASE=ZyXEL Communications Corp. + +pci:v000019BAd00002330* + ID_MODEL_FROM_DATABASE=ZyWALL Turbo Card + +pci:v000019C1* + ID_VENDOR_FROM_DATABASE=Exegy Inc. + +pci:v000019D1* + ID_VENDOR_FROM_DATABASE=Motorola Expedience + +pci:v000019D4* + ID_VENDOR_FROM_DATABASE=Quixant Limited + +pci:v000019DA* + ID_VENDOR_FROM_DATABASE=ZOTAC International (MCO) Ltd. + +pci:v000019DE* + ID_VENDOR_FROM_DATABASE=Pico Computing + +pci:v000019E2* + ID_VENDOR_FROM_DATABASE=Vector Informatik GmbH + +pci:v000019E3* + ID_VENDOR_FROM_DATABASE=DDRdrive LLC + +pci:v000019E3d00005801* + ID_MODEL_FROM_DATABASE=DDRdrive X1 + +pci:v000019E3d00005808* + ID_MODEL_FROM_DATABASE=DDRdrive X8 + +pci:v000019E3d0000DD52* + ID_MODEL_FROM_DATABASE=DDRdrive X1-30 + +pci:v000019E7* + ID_VENDOR_FROM_DATABASE=NET (Network Equipment Technologies) + +pci:v000019E7d00001001* + ID_MODEL_FROM_DATABASE=STIX DSP Card + +pci:v000019E7d00001002* + ID_MODEL_FROM_DATABASE=STIX - 1 Port T1/E1 Card + +pci:v000019E7d00001003* + ID_MODEL_FROM_DATABASE=STIX - 2 Port T1/E1 Card + +pci:v000019E7d00001004* + ID_MODEL_FROM_DATABASE=STIX - 4 Port T1/E1 Card + +pci:v000019E7d00001005* + ID_MODEL_FROM_DATABASE=STIX - 4 Port FXS Card + +pci:v000019EE* + ID_VENDOR_FROM_DATABASE=Netronome Systems, Inc. + +pci:v000019F1* + ID_VENDOR_FROM_DATABASE=BFG Tech + +pci:v000019FF* + ID_VENDOR_FROM_DATABASE=Eclipse Electronic Systems, Inc. + +pci:v00001A03* + ID_VENDOR_FROM_DATABASE=ASPEED Technology, Inc. + +pci:v00001A03d00001150* + ID_MODEL_FROM_DATABASE=AST1150 PCI-to-PCI Bridge + +pci:v00001A03d00002000* + ID_MODEL_FROM_DATABASE=ASPEED Graphics Family + +pci:v00001A07* + ID_VENDOR_FROM_DATABASE=Kvaser AB + +pci:v00001A07d00000006* + ID_MODEL_FROM_DATABASE=CAN interface PC104+ HS/HS + +pci:v00001A07d00000007* + ID_MODEL_FROM_DATABASE=CAN interface PCIcanx II HS or HS/HS + +pci:v00001A07d00000008* + ID_MODEL_FROM_DATABASE=CAN interface PCIEcan HS or HS/HS + +pci:v00001A07d00000009* + ID_MODEL_FROM_DATABASE=CAN interface PCI104 HS/HS + +pci:v00001A08* + ID_VENDOR_FROM_DATABASE=Sierra semiconductor + +pci:v00001A08d00000000* + ID_MODEL_FROM_DATABASE=SC15064 + +pci:v00001A0E* + ID_VENDOR_FROM_DATABASE=DekTec Digital Video B.V. + +pci:v00001A0Ed0000083F* + ID_MODEL_FROM_DATABASE=DTA-2111 VHF/UHF Modulator + +pci:v00001A17* + ID_VENDOR_FROM_DATABASE=Force10 Networks, Inc. + +pci:v00001A17d00008002* + ID_MODEL_FROM_DATABASE=PB-10GE-2P 10GbE Security Card + +pci:v00001A1D* + ID_VENDOR_FROM_DATABASE=GFaI e.V. + +pci:v00001A1Dd00001A17* + ID_MODEL_FROM_DATABASE=Meta Networks MTP-1G IDPS NIC + +pci:v00001A1E* + ID_VENDOR_FROM_DATABASE=3Leaf Systems, Inc. + +pci:v00001A22* + ID_VENDOR_FROM_DATABASE=Ambric Inc. + +pci:v00001A29* + ID_VENDOR_FROM_DATABASE=Fortinet, Inc. + +pci:v00001A29d00004338* + ID_MODEL_FROM_DATABASE=CP8 Content Processor ASIC + +pci:v00001A29d00004E36* + ID_MODEL_FROM_DATABASE=NP6 Network Processor + +pci:v00001A2B* + ID_VENDOR_FROM_DATABASE=Ascom AG + +pci:v00001A2Bd00000000* + ID_MODEL_FROM_DATABASE=GESP v1.2 + +pci:v00001A2Bd00000001* + ID_MODEL_FROM_DATABASE=GESP v1.3 + +pci:v00001A2Bd00000002* + ID_MODEL_FROM_DATABASE=ECOMP v1.3 + +pci:v00001A2Bd00000005* + ID_MODEL_FROM_DATABASE=ETP v1.4 + +pci:v00001A2Bd0000000A* + ID_MODEL_FROM_DATABASE=ETP-104 v1.1 + +pci:v00001A2Bd0000000E* + ID_MODEL_FROM_DATABASE=DSLP-104 v1.1 + +pci:v00001A30* + ID_VENDOR_FROM_DATABASE=Lantiq + +pci:v00001A30d00000680* + ID_MODEL_FROM_DATABASE=MtW8171 [Hyperion II] + +pci:v00001A30d00000700* + ID_MODEL_FROM_DATABASE=Wave300 PSB8224 [Hyperion III] + +pci:v00001A30d00000710* + ID_MODEL_FROM_DATABASE=Wave300 PSB8231 [Hyperion III] + +pci:v00001A32* + ID_VENDOR_FROM_DATABASE=Quanta Microsystems, Inc + +pci:v00001A3B* + ID_VENDOR_FROM_DATABASE=AzureWave + +pci:v00001A3Bd00001112* + ID_MODEL_FROM_DATABASE=AR9285 Wireless Network Adapter (PCI-Express) + +pci:v00001A41* + ID_VENDOR_FROM_DATABASE=Tilera Corp. + +pci:v00001A41d00000001* + ID_MODEL_FROM_DATABASE=TILE64 processor + +pci:v00001A41d00000002* + ID_MODEL_FROM_DATABASE=TILEPro processor + +pci:v00001A41d00000200* + ID_MODEL_FROM_DATABASE=TILE-Gx processor + +pci:v00001A41d00000201* + ID_MODEL_FROM_DATABASE=TILE-Gx Processor Virtual Function + +pci:v00001A41d00002000* + ID_MODEL_FROM_DATABASE=TILE-Gx PCI Express Root Port + +pci:v00001A4A* + ID_VENDOR_FROM_DATABASE=SLAC National Accelerator Lab PPA-REG + +pci:v00001A4Ad00001000* + ID_MODEL_FROM_DATABASE=MCOR Power Supply Controller + +pci:v00001A4Ad00001010* + ID_MODEL_FROM_DATABASE=AMC EVR - Stockholm Timing Board + +pci:v00001A4Ad00002000* + ID_MODEL_FROM_DATABASE=PGPCard - 4 Lane + +pci:v00001A4Ad00002001* + ID_MODEL_FROM_DATABASE=PGPCard - 8 Lane Plus EVR + +pci:v00001A4Ad00002010* + ID_MODEL_FROM_DATABASE=PCI-Express EVR + +pci:v00001A51* + ID_VENDOR_FROM_DATABASE=Hectronic AB + +pci:v00001A55* + ID_VENDOR_FROM_DATABASE=Rohde & Schwarz DVS GmbH + +pci:v00001A55d00000010* + ID_MODEL_FROM_DATABASE=SDStationOEM + +pci:v00001A55d00000011* + ID_MODEL_FROM_DATABASE=SDStationOEM II + +pci:v00001A55d00000020* + ID_MODEL_FROM_DATABASE=Centaurus + +pci:v00001A55d00000021* + ID_MODEL_FROM_DATABASE=Centaurus II + +pci:v00001A55d00000022* + ID_MODEL_FROM_DATABASE=Centaurus II LT + +pci:v00001A55d00000030* + ID_MODEL_FROM_DATABASE=CLIPSTER-VPU 1.x (Hugo) + +pci:v00001A55d00000040* + ID_MODEL_FROM_DATABASE=Hydra Cinema (JPEG) + +pci:v00001A55d00000050* + ID_MODEL_FROM_DATABASE=CLIPSTER-VPU 2.x (DigiLab) + +pci:v00001A55d00000060* + ID_MODEL_FROM_DATABASE=CLIPSTER-DCI 2.x (HydraX) + +pci:v00001A55d00000061* + ID_MODEL_FROM_DATABASE=Atomix + +pci:v00001A55d00000062* + ID_MODEL_FROM_DATABASE=Atomix LT + +pci:v00001A55d00000063* + ID_MODEL_FROM_DATABASE=Atomix HDMI + +pci:v00001A55d00000064* + ID_MODEL_FROM_DATABASE=Atomix STAN + +pci:v00001A55d00000065* + ID_MODEL_FROM_DATABASE=Atomix HDMI STAN + +pci:v00001A55d00000070* + ID_MODEL_FROM_DATABASE=RED Rocket + +pci:v00001A55d00000090* + ID_MODEL_FROM_DATABASE=CinePlay + +pci:v00001A56* + ID_VENDOR_FROM_DATABASE=Bigfoot Networks, Inc. + +pci:v00001A57* + ID_VENDOR_FROM_DATABASE=Highly Reliable Systems + +pci:v00001A58* + ID_VENDOR_FROM_DATABASE=Razer USA Ltd. + +pci:v00001A5D* + ID_VENDOR_FROM_DATABASE=Celoxica + +pci:v00001A5E* + ID_VENDOR_FROM_DATABASE=Aprius Inc. + +pci:v00001A5F* + ID_VENDOR_FROM_DATABASE=System TALKS Inc. + +pci:v00001A68* + ID_VENDOR_FROM_DATABASE=VirtenSys Limited + +pci:v00001A71* + ID_VENDOR_FROM_DATABASE=XenSource, Inc. + +pci:v00001A73* + ID_VENDOR_FROM_DATABASE=Violin Memory, Inc + +pci:v00001A73d00000001* + ID_MODEL_FROM_DATABASE=Mozart [Memory Appliance 1010] + +pci:v00001A76* + ID_VENDOR_FROM_DATABASE=Wavesat + +pci:v00001A77* + ID_VENDOR_FROM_DATABASE=Lightfleet Corporation + +pci:v00001A78* + ID_VENDOR_FROM_DATABASE=Virident Systems Inc. + +pci:v00001A78d00000031* + ID_MODEL_FROM_DATABASE=FlashMAX Drive + +pci:v00001A78d00000031sv00001A78sd00000034* + ID_MODEL_FROM_DATABASE=FlashMAX Drive (FlashMAX PCIe SSD [rev 3]) + +pci:v00001A78d00000031sv00001A78sd00000037* + ID_MODEL_FROM_DATABASE=FlashMAX Drive (FlashMAX PCIe SSD [rev 3D]) + +pci:v00001A78d00000031sv00001A78sd00000038* + ID_MODEL_FROM_DATABASE=FlashMAX Drive (FlashMAX PCIe SSD [rev 4]) + +pci:v00001A78d00000031sv00001A78sd00000039* + ID_MODEL_FROM_DATABASE=FlashMAX Drive (FlashMAX PCIe SSD [rev 4D]) + +pci:v00001A78d00000040* + ID_MODEL_FROM_DATABASE=FlashMAX II + +pci:v00001A78d00000041* + ID_MODEL_FROM_DATABASE=FlashMAX II + +pci:v00001A78d00000042* + ID_MODEL_FROM_DATABASE=FlashMAX II + +pci:v00001A78d00000050* + ID_MODEL_FROM_DATABASE=FlashMAX III + +pci:v00001A84* + ID_VENDOR_FROM_DATABASE=Commex Technologies + +pci:v00001A84d00000001* + ID_MODEL_FROM_DATABASE=Vulcan SP HT6210 10-Gigabit Ethernet (rev 02) + +pci:v00001A88* + ID_VENDOR_FROM_DATABASE=MEN Mikro Elektronik + +pci:v00001A88d00004D45* + ID_MODEL_FROM_DATABASE=Multifunction IP core + +pci:v00001A8A* + ID_VENDOR_FROM_DATABASE=StarBridge, Inc. + +pci:v00001A8C* + ID_VENDOR_FROM_DATABASE=Verigy Pte. Ltd. + +pci:v00001A8Cd00001100* + ID_MODEL_FROM_DATABASE=E8001-66443 PCI Express CIC + +pci:v00001A8E* + ID_VENDOR_FROM_DATABASE=DRS Technologies + +pci:v00001A8Ed00002090* + ID_MODEL_FROM_DATABASE=Model 2090 PCI Express + +pci:v00001AA8* + ID_VENDOR_FROM_DATABASE=Ciprico, Inc. + +pci:v00001AA8d00000009* + ID_MODEL_FROM_DATABASE=RAIDCore Controller + +pci:v00001AA8d0000000A* + ID_MODEL_FROM_DATABASE=RAIDCore Controller + +pci:v00001AAE* + ID_VENDOR_FROM_DATABASE=Global Velocity, Inc. + +pci:v00001AB6* + ID_VENDOR_FROM_DATABASE=CalDigit, Inc. + +pci:v00001AB6d00006201* + ID_MODEL_FROM_DATABASE=RAID Card + +pci:v00001AB8* + ID_VENDOR_FROM_DATABASE=Parallels, Inc. + +pci:v00001AB8d00004000* + ID_MODEL_FROM_DATABASE=Virtual Machine Communication Interface + +pci:v00001AB8d00004005* + ID_MODEL_FROM_DATABASE=Accelerated Virtual Video Adapter + +pci:v00001AB8d00004006* + ID_MODEL_FROM_DATABASE=Memory Ballooning Controller + +pci:v00001AB9* + ID_VENDOR_FROM_DATABASE=Espia Srl + +pci:v00001AC8* + ID_VENDOR_FROM_DATABASE=Aeroflex Gaisler + +pci:v00001ACC* + ID_VENDOR_FROM_DATABASE=Point of View BV + +pci:v00001AD7* + ID_VENDOR_FROM_DATABASE=Spectracom Corporation + +pci:v00001AD7d00008000* + ID_MODEL_FROM_DATABASE=TSync-PCIe Time Code Processor + +pci:v00001AD7d00009100* + ID_MODEL_FROM_DATABASE=TPRO-PCI-66U Timecode Reader/Generator + +pci:v00001ADE* + ID_VENDOR_FROM_DATABASE=Spin Master Ltd. + +pci:v00001ADEd00001501* + ID_MODEL_FROM_DATABASE=Swipetech barcode scanner + +pci:v00001ADEd00003038* + ID_MODEL_FROM_DATABASE=PCIe Video Bridge + +pci:v00001ADEd00003038sv000013C2sd00003016* + ID_MODEL_FROM_DATABASE=PCIe Video Bridge (TT-budget S2-4200 Twin) + +pci:v00001ADEd00003038sv00004254sd00000552* + ID_MODEL_FROM_DATABASE=PCIe Video Bridge (S952 v3) + +pci:v00001AE0* + ID_VENDOR_FROM_DATABASE=Google, Inc. + +pci:v00001AE7* + ID_VENDOR_FROM_DATABASE=First Wise Media GmbH + +pci:v00001AE7d00000520* + ID_MODEL_FROM_DATABASE=HFC-S PCI A [X-TENSIONS XC-520] + +pci:v00001AE8* + ID_VENDOR_FROM_DATABASE=Silicon Software GmbH + +pci:v00001AE8d00000A40* + ID_MODEL_FROM_DATABASE=microEnable IV-BASE x1 + +pci:v00001AE8d00000A41* + ID_MODEL_FROM_DATABASE=microEnable IV-FULL x1 + +pci:v00001AE8d00000A44* + ID_MODEL_FROM_DATABASE=microEnable IV-FULL x4 + +pci:v00001AE8d00000E44* + ID_MODEL_FROM_DATABASE=microEnable IV-GigE x4 + +pci:v00001AE9* + ID_VENDOR_FROM_DATABASE=Wilocity Ltd. + +pci:v00001AE9d00000101* + ID_MODEL_FROM_DATABASE=Wil6200 PCI Express Root Port + +pci:v00001AE9d00000200* + ID_MODEL_FROM_DATABASE=Wil6200 PCI Express Port + +pci:v00001AE9d00000201* + ID_MODEL_FROM_DATABASE=Wil6200 Wireless PCI Express Port + +pci:v00001AE9d00000301* + ID_MODEL_FROM_DATABASE=Wil6200 802.11ad Wireless Network Adapter + +pci:v00001AE9d00000302* + ID_MODEL_FROM_DATABASE=Wil6200 802.11ad Wireless Network Adapter + +pci:v00001AE9d00000310* + ID_MODEL_FROM_DATABASE=Wil6200 802.11ad Wireless Network Adapter + +pci:v00001AEA* + ID_VENDOR_FROM_DATABASE=Alcor Micro + +pci:v00001AEAd00006601* + ID_MODEL_FROM_DATABASE=AU6601 PCI-E Flash card reader controller + +pci:v00001AEC* + ID_VENDOR_FROM_DATABASE=Wolfson Microelectronics + +pci:v00001AED* + ID_VENDOR_FROM_DATABASE=SanDisk + +pci:v00001AEDd00001003* + ID_MODEL_FROM_DATABASE=ioDimm3 (v1.2) + +pci:v00001AEDd00001005* + ID_MODEL_FROM_DATABASE=ioDimm3 + +pci:v00001AEDd00001005sv00001014sd000003C3* + ID_MODEL_FROM_DATABASE=ioDimm3 (High IOPS SSD PCIe Adapter) + +pci:v00001AEDd00001005sv0000103Csd0000176F* + ID_MODEL_FROM_DATABASE=ioDimm3 (1.28TB MLC PCIe ioDrive Duo) + +pci:v00001AEDd00001005sv0000103Csd00001770* + ID_MODEL_FROM_DATABASE=ioDimm3 (5.2TB MLC PCIe ioDrive Octal) + +pci:v00001AEDd00001005sv0000103Csd0000178B* + ID_MODEL_FROM_DATABASE=ioDimm3 (160GB SLC PCIe ioDrive) + +pci:v00001AEDd00001005sv0000103Csd0000178C* + ID_MODEL_FROM_DATABASE=ioDimm3 (320GB MLC PCIe ioDrive) + +pci:v00001AEDd00001005sv0000103Csd0000178D* + ID_MODEL_FROM_DATABASE=ioDimm3 (320GB SLC PCIe ioDrive Duo) + +pci:v00001AEDd00001005sv0000103Csd0000178E* + ID_MODEL_FROM_DATABASE=ioDimm3 (640GB MLC PCIe ioDrive Duo) + +pci:v00001AEDd00001006* + ID_MODEL_FROM_DATABASE=ioXtreme + +pci:v00001AEDd00001007* + ID_MODEL_FROM_DATABASE=ioXtreme Pro + +pci:v00001AEDd00001008* + ID_MODEL_FROM_DATABASE=ioXtreme-2 + +pci:v00001AEDd00002001* + ID_MODEL_FROM_DATABASE=ioDrive2 + +pci:v00001AEDd00003001* + ID_MODEL_FROM_DATABASE=ioMemory FHHL + +pci:v00001AEDd00003002* + ID_MODEL_FROM_DATABASE=ioMemory HHHL + +pci:v00001AEDd00003003* + ID_MODEL_FROM_DATABASE=ioMemory Mezzanine + +pci:v00001AEE* + ID_VENDOR_FROM_DATABASE=Caustic Graphics Inc. + +pci:v00001AF4* + ID_VENDOR_FROM_DATABASE=Red Hat, Inc + +pci:v00001AF4d00001000* + ID_MODEL_FROM_DATABASE=Virtio network device + +pci:v00001AF4d00001001* + ID_MODEL_FROM_DATABASE=Virtio block device + +pci:v00001AF4d00001002* + ID_MODEL_FROM_DATABASE=Virtio memory balloon + +pci:v00001AF4d00001003* + ID_MODEL_FROM_DATABASE=Virtio console + +pci:v00001AF4d00001004* + ID_MODEL_FROM_DATABASE=Virtio SCSI + +pci:v00001AF4d00001005* + ID_MODEL_FROM_DATABASE=Virtio RNG + +pci:v00001AF4d00001009* + ID_MODEL_FROM_DATABASE=Virtio filesystem + +pci:v00001AF4d00001041* + ID_MODEL_FROM_DATABASE=Virtio network device + +pci:v00001AF4d00001042* + ID_MODEL_FROM_DATABASE=Virtio block device + +pci:v00001AF4d00001043* + ID_MODEL_FROM_DATABASE=Virtio console + +pci:v00001AF4d00001044* + ID_MODEL_FROM_DATABASE=Virtio RNG + +pci:v00001AF4d00001045* + ID_MODEL_FROM_DATABASE=Virtio memory balloon + +pci:v00001AF4d00001048* + ID_MODEL_FROM_DATABASE=Virtio SCSI + +pci:v00001AF4d00001049* + ID_MODEL_FROM_DATABASE=Virtio filesystem + +pci:v00001AF4d00001050* + ID_MODEL_FROM_DATABASE=Virtio GPU + +pci:v00001AF4d00001052* + ID_MODEL_FROM_DATABASE=Virtio input + +pci:v00001AF4d00001110* + ID_MODEL_FROM_DATABASE=Inter-VM shared memory + +pci:v00001AF4d00001110sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=Inter-VM shared memory (QEMU Virtual Machine) + +pci:v00001AF5* + ID_VENDOR_FROM_DATABASE=Netezza Corp. + +pci:v00001AFA* + ID_VENDOR_FROM_DATABASE=J & W Electronics Co., Ltd. + +pci:v00001B03* + ID_VENDOR_FROM_DATABASE=Magnum Semiconductor, Inc, + +pci:v00001B03d00006100* + ID_MODEL_FROM_DATABASE=DXT/DXTPro Multiformat Broadcast HD/SD Encoder/Decoder/Transcoder + +pci:v00001B03d00007000* + ID_MODEL_FROM_DATABASE=D7 Multiformat Broadcast HD/SD Encoder/Decoder/Transcoder + +pci:v00001B08* + ID_VENDOR_FROM_DATABASE=MSC Vertriebs GmbH + +pci:v00001B0A* + ID_VENDOR_FROM_DATABASE=Pegatron + +pci:v00001B13* + ID_VENDOR_FROM_DATABASE=Jaton Corp + +pci:v00001B1A* + ID_VENDOR_FROM_DATABASE=K&F Computing Research Co. + +pci:v00001B1Ad00000E70* + ID_MODEL_FROM_DATABASE=GRAPE + +pci:v00001B21* + ID_VENDOR_FROM_DATABASE=ASMedia Technology Inc. + +pci:v00001B21d00000611* + ID_MODEL_FROM_DATABASE=ASM1061 SATA IDE Controller + +pci:v00001B21d00000612* + ID_MODEL_FROM_DATABASE=ASM1062 Serial ATA Controller + +pci:v00001B21d00000612sv00001849sd00000612* + ID_MODEL_FROM_DATABASE=ASM1062 Serial ATA Controller (Motherboard) + +pci:v00001B21d00001042* + ID_MODEL_FROM_DATABASE=ASM1042 SuperSpeed USB Host Controller + +pci:v00001B21d00001042sv00001043sd00008488* + ID_MODEL_FROM_DATABASE=ASM1042 SuperSpeed USB Host Controller (P8B WS Motherboard) + +pci:v00001B21d00001042sv00001849sd00001042* + ID_MODEL_FROM_DATABASE=ASM1042 SuperSpeed USB Host Controller (Motherboard) + +pci:v00001B21d00001080* + ID_MODEL_FROM_DATABASE=ASM1083/1085 PCIe to PCI Bridge + +pci:v00001B21d00001080sv00001849sd00001080* + ID_MODEL_FROM_DATABASE=ASM1083/1085 PCIe to PCI Bridge (Motherboard) + +pci:v00001B21d00001142* + ID_MODEL_FROM_DATABASE=ASM1042A USB 3.0 Host Controller + +pci:v00001B21d00001242* + ID_MODEL_FROM_DATABASE=ASM1142 USB 3.1 Host Controller + +pci:v00001B2C* + ID_VENDOR_FROM_DATABASE=Opal-RT Technologies Inc. + +pci:v00001B36* + ID_VENDOR_FROM_DATABASE=Red Hat, Inc. + +pci:v00001B36d00000001* + ID_MODEL_FROM_DATABASE=QEMU PCI-PCI bridge + +pci:v00001B36d00000002* + ID_MODEL_FROM_DATABASE=QEMU PCI 16550A Adapter + +pci:v00001B36d00000002sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QEMU PCI 16550A Adapter (QEMU Virtual Machine) + +pci:v00001B36d00000003* + ID_MODEL_FROM_DATABASE=QEMU PCI Dual-port 16550A Adapter + +pci:v00001B36d00000003sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QEMU PCI Dual-port 16550A Adapter (QEMU Virtual Machine) + +pci:v00001B36d00000004* + ID_MODEL_FROM_DATABASE=QEMU PCI Quad-port 16550A Adapter + +pci:v00001B36d00000004sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QEMU PCI Quad-port 16550A Adapter (QEMU Virtual Machine) + +pci:v00001B36d00000005* + ID_MODEL_FROM_DATABASE=QEMU PCI Test Device + +pci:v00001B36d00000005sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QEMU PCI Test Device (QEMU Virtual Machine) + +pci:v00001B36d00000006* + ID_MODEL_FROM_DATABASE=PCI Rocker Ethernet switch device + +pci:v00001B36d00000007* + ID_MODEL_FROM_DATABASE=PCI SD Card Host Controller Interface + +pci:v00001B36d0000000A* + ID_MODEL_FROM_DATABASE=PCI-PCI bridge (multiseat) + +pci:v00001B36d00000100* + ID_MODEL_FROM_DATABASE=QXL paravirtual graphic card + +pci:v00001B36d00000100sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QXL paravirtual graphic card (QEMU Virtual Machine) + +pci:v00001B37* + ID_VENDOR_FROM_DATABASE=Signal Processing Devices Sweden AB + +pci:v00001B37d00000001* + ID_MODEL_FROM_DATABASE=ADQ214 + +pci:v00001B37d00000003* + ID_MODEL_FROM_DATABASE=ADQ114 + +pci:v00001B37d00000005* + ID_MODEL_FROM_DATABASE=ADQ112 + +pci:v00001B37d0000000E* + ID_MODEL_FROM_DATABASE=ADQ108 + +pci:v00001B37d0000000F* + ID_MODEL_FROM_DATABASE=ADQDSP + +pci:v00001B37d00000014* + ID_MODEL_FROM_DATABASE=ADQ412 + +pci:v00001B37d00000015* + ID_MODEL_FROM_DATABASE=ADQ212 + +pci:v00001B37d0000001B* + ID_MODEL_FROM_DATABASE=SDR14 + +pci:v00001B37d0000001C* + ID_MODEL_FROM_DATABASE=ADQ1600 + +pci:v00001B37d0000001E* + ID_MODEL_FROM_DATABASE=ADQ208 + +pci:v00001B37d0000001F* + ID_MODEL_FROM_DATABASE=DSU + +pci:v00001B37d00000020* + ID_MODEL_FROM_DATABASE=ADQ14 + +pci:v00001B37d00002014* + ID_MODEL_FROM_DATABASE=TX320 + +pci:v00001B37d00002019* + ID_MODEL_FROM_DATABASE=S6000 + +pci:v00001B39* + ID_VENDOR_FROM_DATABASE=sTec, Inc. + +pci:v00001B39d00000001* + ID_MODEL_FROM_DATABASE=S1120 PCIe Accelerator SSD + +pci:v00001B3A* + ID_VENDOR_FROM_DATABASE=Westar Display Technologies + +pci:v00001B3Ad00007589* + ID_MODEL_FROM_DATABASE=HRED J2000 - JPEG 2000 Video Codec Device + +pci:v00001B3E* + ID_VENDOR_FROM_DATABASE=Teradata Corp. + +pci:v00001B3Ed00001FA8* + ID_MODEL_FROM_DATABASE=BYNET BIC2SE/X + +pci:v00001B3Ed00001FA8sv00001B3Esd000000A3* + ID_MODEL_FROM_DATABASE=BYNET BIC2SE/X (BYNET BIC2SX) + +pci:v00001B3Ed00001FA8sv00001B3Esd000000C3* + ID_MODEL_FROM_DATABASE=BYNET BIC2SE/X (BYNET BIC2SE) + +pci:v00001B40* + ID_VENDOR_FROM_DATABASE=Schooner Information Technology, Inc. + +pci:v00001B47* + ID_VENDOR_FROM_DATABASE=Numascale AS + +pci:v00001B47d00000601* + ID_MODEL_FROM_DATABASE=NumaChip N601 + +pci:v00001B47d00000602* + ID_MODEL_FROM_DATABASE=NumaChip N602 + +pci:v00001B4B* + ID_VENDOR_FROM_DATABASE=Marvell Technology Group Ltd. + +pci:v00001B4Bd00000640* + ID_MODEL_FROM_DATABASE=88SE9128 SATA III 6Gb/s RAID Controller + +pci:v00001B4Bd00009120* + ID_MODEL_FROM_DATABASE=88SE9120 SATA 6Gb/s Controller + +pci:v00001B4Bd00009123* + ID_MODEL_FROM_DATABASE=88SE9123 PCIe SATA 6.0 Gb/s controller + +pci:v00001B4Bd00009123sv0000DC93sd0000600E* + ID_MODEL_FROM_DATABASE=88SE9123 PCIe SATA 6.0 Gb/s controller (DC-6xxe series SATA 6G controller) + +pci:v00001B4Bd00009125* + ID_MODEL_FROM_DATABASE=88SE9125 PCIe SATA 6.0 Gb/s controller + +pci:v00001B4Bd00009128* + ID_MODEL_FROM_DATABASE=88SE9128 PCIe SATA 6 Gb/s RAID controller + +pci:v00001B4Bd00009130* + ID_MODEL_FROM_DATABASE=88SE9128 PCIe SATA 6 Gb/s RAID controller with HyperDuo + +pci:v00001B4Bd00009130sv00001043sd00008438* + ID_MODEL_FROM_DATABASE=88SE9128 PCIe SATA 6 Gb/s RAID controller with HyperDuo (P8P67 Deluxe Motherboard) + +pci:v00001B4Bd00009172* + ID_MODEL_FROM_DATABASE=88SE9172 SATA 6Gb/s Controller + +pci:v00001B4Bd00009178* + ID_MODEL_FROM_DATABASE=88SE9170 PCIe SATA 6Gb/s Controller + +pci:v00001B4Bd0000917A* + ID_MODEL_FROM_DATABASE=88SE9172 SATA III 6Gb/s RAID Controller + +pci:v00001B4Bd00009183* + ID_MODEL_FROM_DATABASE=88SS9183 PCIe SSD Controller + +pci:v00001B4Bd00009192* + ID_MODEL_FROM_DATABASE=88SE9172 SATA III 6Gb/s RAID Controller + +pci:v00001B4Bd000091A0* + ID_MODEL_FROM_DATABASE=88SE912x SATA 6Gb/s Controller [IDE mode] + +pci:v00001B4Bd000091A4* + ID_MODEL_FROM_DATABASE=88SE912x IDE Controller + +pci:v00001B4Bd00009220* + ID_MODEL_FROM_DATABASE=88SE9220 PCIe 2.0 x2 2-port SATA 6 Gb/s RAID Controller + +pci:v00001B4Bd00009230* + ID_MODEL_FROM_DATABASE=88SE9230 PCIe SATA 6Gb/s Controller + +pci:v00001B4Bd00009230sv00001D49sd00000300* + ID_MODEL_FROM_DATABASE=88SE9230 PCIe SATA 6Gb/s Controller (ThinkSystem M.2 with Mirroring Enablement Kit) + +pci:v00001B4Bd00009235* + ID_MODEL_FROM_DATABASE=88SE9235 PCIe 2.0 x2 4-port SATA 6 Gb/s Controller + +pci:v00001B4Bd00009445* + ID_MODEL_FROM_DATABASE=88SE9445 PCIe 2.0 x4 4-Port SAS/SATA 6 Gbps RAID Controller + +pci:v00001B4Bd00009480* + ID_MODEL_FROM_DATABASE=88SE9480 SAS/SATA 6Gb/s RAID controller + +pci:v00001B4Bd00009485* + ID_MODEL_FROM_DATABASE=88SE9485 SAS/SATA 6Gb/s controller + +pci:v00001B55* + ID_VENDOR_FROM_DATABASE=NetUP Inc. + +pci:v00001B55d000018F6* + ID_MODEL_FROM_DATABASE=Dual DVB Universal CI card + +pci:v00001B55d000018F7* + ID_MODEL_FROM_DATABASE=Dual DVB Universal CI card rev 1.4 + +pci:v00001B55d00002A2C* + ID_MODEL_FROM_DATABASE=Dual DVB-S2-CI card + +pci:v00001B55d0000E2E4* + ID_MODEL_FROM_DATABASE=Dual DVB-T/C-CI RF card + +pci:v00001B55d0000E5F4* + ID_MODEL_FROM_DATABASE=MPEG2 and H264 Encoder-Transcoder + +pci:v00001B55d0000F1C4* + ID_MODEL_FROM_DATABASE=Dual ASI-RX/TX-CI card + +pci:v00001B66* + ID_VENDOR_FROM_DATABASE=Deltacast + +pci:v00001B66d00000007* + ID_MODEL_FROM_DATABASE=Delta-3G-elp-11 SDI I/O Board + +pci:v00001B6F* + ID_VENDOR_FROM_DATABASE=Etron Technology, Inc. + +pci:v00001B6Fd00007023* + ID_MODEL_FROM_DATABASE=EJ168 USB 3.0 Host Controller + +pci:v00001B6Fd00007052* + ID_MODEL_FROM_DATABASE=EJ188/EJ198 USB 3.0 Host Controller + +pci:v00001B73* + ID_VENDOR_FROM_DATABASE=Fresco Logic + +pci:v00001B73d00001000* + ID_MODEL_FROM_DATABASE=FL1000G USB 3.0 Host Controller + +pci:v00001B73d00001000sv00001D5Csd00001000* + ID_MODEL_FROM_DATABASE=FL1000G USB 3.0 Host Controller (Anker USB 3.0 Express Card) + +pci:v00001B73d00001009* + ID_MODEL_FROM_DATABASE=FL1009 USB 3.0 Host Controller + +pci:v00001B73d00001100* + ID_MODEL_FROM_DATABASE=FL1100 USB 3.0 Host Controller + +pci:v00001B74* + ID_VENDOR_FROM_DATABASE=OpenVox Communication Co. Ltd. + +pci:v00001B74d00000115* + ID_MODEL_FROM_DATABASE=D115P/D115E Single-port E1/T1 card + +pci:v00001B74d0000D130* + ID_MODEL_FROM_DATABASE=D130P/D130E Single-port E1/T1 card (3rd GEN) + +pci:v00001B74d0000D210* + ID_MODEL_FROM_DATABASE=D210P/D210E Dual-port E1/T1 card(2nd generation) + +pci:v00001B74d0000D230* + ID_MODEL_FROM_DATABASE=D230 Dual-port E1/T1 card (2nd generation) + +pci:v00001B74d0000D410* + ID_MODEL_FROM_DATABASE=D410/430 Quad-port E1/T1 card + +pci:v00001B74d0000D430* + ID_MODEL_FROM_DATABASE=D410/430 Quad-port E1/T1 card + +pci:v00001B79* + ID_VENDOR_FROM_DATABASE=Absolute Analysis + +pci:v00001B85* + ID_VENDOR_FROM_DATABASE=OCZ Technology Group, Inc. + +pci:v00001B85d00001041* + ID_MODEL_FROM_DATABASE=RevoDrive 3 X2 PCI-Express SSD 240 GB (Marvell Controller) + +pci:v00001B85d00008788* + ID_MODEL_FROM_DATABASE=RevoDrive Hybrid + +pci:v00001B94* + ID_VENDOR_FROM_DATABASE=Signatec / Dynamic Signals Corp + +pci:v00001B94d0000E400* + ID_MODEL_FROM_DATABASE=PX14400 Dual Xilinx Virtex5 based Digitizer + +pci:v00001B96* + ID_VENDOR_FROM_DATABASE=Western Digital + +pci:v00001B9A* + ID_VENDOR_FROM_DATABASE=XAVi Technologies Corp. + +pci:v00001BAD* + ID_VENDOR_FROM_DATABASE=ReFLEX CES + +pci:v00001BB0* + ID_VENDOR_FROM_DATABASE=SimpliVity Corporation + +pci:v00001BB0d00000002* + ID_MODEL_FROM_DATABASE=OmniCube Accelerator OA-3000 + +pci:v00001BB0d00000010* + ID_MODEL_FROM_DATABASE=OmniCube Accelerator OA-3000-2 + +pci:v00001BB1* + ID_VENDOR_FROM_DATABASE=Seagate Technology PLC + +pci:v00001BB1d0000005D* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage + +pci:v00001BB1d0000005Dsv00001BB1sd00006501* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A1536 1.5TB) + +pci:v00001BB1d0000005Dsv00001BB1sd00006502* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A2048) + +pci:v00001BB1d0000005Dsv00001BB1sd00006503* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A4096) + +pci:v00001BB1d0000005Dsv00001BB1sd00006511* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XH6550-2GB DRAM) + +pci:v00001BB1d0000005Dsv00001BB1sd00006512* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XH6550-8GB DRAM) + +pci:v00001BB1d0000005Dsv00001BB1sd00006521* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A1536 1.5TB) + +pci:v00001BB1d0000005Dsv00001BB1sd00006522* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A2048) + +pci:v00001BB1d0000005Dsv00001BB1sd00006523* + ID_MODEL_FROM_DATABASE=Nytro PCIe Flash Storage (Nytro XP6500-8A4096) + +pci:v00001BB1d00000100* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage + +pci:v00001BB1d00000100sv00001BB1sd00000101* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XF1440) + +pci:v00001BB1d00000100sv00001BB1sd00000121* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XM1440) + +pci:v00001BB1d00000100sv00001BB1sd000001A1* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XP7102) + +pci:v00001BB3* + ID_VENDOR_FROM_DATABASE=Bluecherry + +pci:v00001BB3d00004304* + ID_MODEL_FROM_DATABASE=BC-04120A MPEG4 4 port video encoder / decoder + +pci:v00001BB3d00004309* + ID_MODEL_FROM_DATABASE=BC-08240A MPEG4 4 port video encoder / decoder + +pci:v00001BB3d00004310* + ID_MODEL_FROM_DATABASE=BC-16480A MPEG4 16 port video encoder / decoder + +pci:v00001BB3d00004E04* + ID_MODEL_FROM_DATABASE=BC-04120A 4 port MPEG4 video encoder / decoder + +pci:v00001BB3d00004E09* + ID_MODEL_FROM_DATABASE=BC-08240A 8 port MPEG4 video encoder / decoder + +pci:v00001BB3d00004E10* + ID_MODEL_FROM_DATABASE=BC-16480A 16 port MPEG4 video encoder / decoder + +pci:v00001BB3d00005304* + ID_MODEL_FROM_DATABASE=BC-H04120A 4 port H.264 video and audio encoder / decoder + +pci:v00001BB3d00005308* + ID_MODEL_FROM_DATABASE=BC-H08240A 8 port H.264 video and audio encoder / decoder + +pci:v00001BB3d00005310* + ID_MODEL_FROM_DATABASE=BC-H16480A 16 port H.264 video and audio encoder / decoder + +pci:v00001BB5* + ID_VENDOR_FROM_DATABASE=Quantenna Communications, Inc. + +pci:v00001BBF* + ID_VENDOR_FROM_DATABASE=Maxeler Technologies Ltd. + +pci:v00001BBFd00000003* + ID_MODEL_FROM_DATABASE=MAX3 + +pci:v00001BBFd00000004* + ID_MODEL_FROM_DATABASE=MAX4 + +pci:v00001BD0* + ID_VENDOR_FROM_DATABASE=Astronics Corporation + +pci:v00001BD0d00001001* + ID_MODEL_FROM_DATABASE=Mx5 PMC/XMC Databus Interface Card + +pci:v00001BD0d00001002* + ID_MODEL_FROM_DATABASE=PM1553-5 (PC/104+ MIL-STD-1553 Interface Card) + +pci:v00001BD0d00001004* + ID_MODEL_FROM_DATABASE=AB3000 Series Rugged Computer + +pci:v00001BD0d00001005* + ID_MODEL_FROM_DATABASE=PE1000 (Multi-Protocol PCIe/104 Interface Card) + +pci:v00001BD0d00001101* + ID_MODEL_FROM_DATABASE=OmniBus II PCIe Multi-Protocol Interface Card + +pci:v00001BD0d00001102* + ID_MODEL_FROM_DATABASE=OmniBusBox II Multi-Protocol Interface Core + +pci:v00001BD0d00001103* + ID_MODEL_FROM_DATABASE=OmniBus II cPCIe/PXIe Multi-Protocol Interface Card + +pci:v00001BD4* + ID_VENDOR_FROM_DATABASE=Inspur Electronic Information Industry Co., Ltd. + +pci:v00001BEE* + ID_VENDOR_FROM_DATABASE=IXXAT Automation GmbH + +pci:v00001BEEd00000003* + ID_MODEL_FROM_DATABASE=CAN-IB200/PCIe + +pci:v00001BF4* + ID_VENDOR_FROM_DATABASE=VTI Instruments Corporation + +pci:v00001BF4d00000001* + ID_MODEL_FROM_DATABASE=SentinelEX + +pci:v00001BFD* + ID_VENDOR_FROM_DATABASE=EeeTOP + +pci:v00001C09* + ID_VENDOR_FROM_DATABASE=CSP, Inc. + +pci:v00001C09d00004254* + ID_MODEL_FROM_DATABASE=10G-PCIE3-8D-2S + +pci:v00001C09d00004255* + ID_MODEL_FROM_DATABASE=10G-PCIE3-8D-Q + +pci:v00001C09d00004256* + ID_MODEL_FROM_DATABASE=10G-PCIE3-8D-2S + +pci:v00001C09d00004258* + ID_MODEL_FROM_DATABASE=10G-PCIE3-8E-2S + +pci:v00001C1C* + ID_VENDOR_FROM_DATABASE=Symphony + +pci:v00001C1Cd00000001* + ID_MODEL_FROM_DATABASE=82C101 + +pci:v00001C28* + ID_VENDOR_FROM_DATABASE=Lite-On IT Corp. / Plextor + +pci:v00001C28d00000122* + ID_MODEL_FROM_DATABASE=M6e PCI Express SSD [Marvell 88SS9183] + +pci:v00001C2C* + ID_VENDOR_FROM_DATABASE=Fiberblaze + +pci:v00001C2Cd0000000A* + ID_MODEL_FROM_DATABASE=Capture + +pci:v00001C2Cd0000000F* + ID_MODEL_FROM_DATABASE=SmartNIC + +pci:v00001C2Cd000000A0* + ID_MODEL_FROM_DATABASE=FBC4G Capture 4x1Gb + +pci:v00001C2Cd000000A1* + ID_MODEL_FROM_DATABASE=FBC4XG Capture 4x10Gb + +pci:v00001C2Cd000000A2* + ID_MODEL_FROM_DATABASE=FBC8XG Capture 8x10Gb + +pci:v00001C2Cd000000A3* + ID_MODEL_FROM_DATABASE=FBC2XG Capture 2x10Gb + +pci:v00001C2Cd000000A4* + ID_MODEL_FROM_DATABASE=FBC4XGG3 Capture 4x10Gb + +pci:v00001C2Cd000000A5* + ID_MODEL_FROM_DATABASE=FBC2XLG Capture 2x40Gb + +pci:v00001C2Cd000000A6* + ID_MODEL_FROM_DATABASE=FBC1CG Capture 1x100Gb + +pci:v00001C2Cd000000A9* + ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb + +pci:v00001C2Cd000000AF* + ID_MODEL_FROM_DATABASE=Capture slave device + +pci:v00001C32* + ID_VENDOR_FROM_DATABASE=Highland Technology, Inc. + +pci:v00001C33* + ID_VENDOR_FROM_DATABASE=Daktronics, Inc + +pci:v00001C3B* + ID_VENDOR_FROM_DATABASE=Accensus, LLC + +pci:v00001C3Bd00000200* + ID_MODEL_FROM_DATABASE=Telas2 + +pci:v00001C3Bd00000300* + ID_MODEL_FROM_DATABASE=Telas 2.V + +pci:v00001C44* + ID_VENDOR_FROM_DATABASE=Enmotus Inc + +pci:v00001C44d00008000* + ID_MODEL_FROM_DATABASE=8000 Storage IO Controller + +pci:v00001C58* + ID_VENDOR_FROM_DATABASE=HGST, Inc. + +pci:v00001C58d00000003* + ID_MODEL_FROM_DATABASE=Ultrastar SN100 Series NVMe SSD + +pci:v00001C58d00000003sv00001014sd000004F5* + ID_MODEL_FROM_DATABASE=Ultrastar SN100 Series NVMe SSD (PCIe3 1.6TB NVMe Flash Adapter) + +pci:v00001C58d00000003sv00001014sd000004F6* + ID_MODEL_FROM_DATABASE=Ultrastar SN100 Series NVMe SSD (PCIe3 3.2TB NVMe Flash Adapter) + +pci:v00001C63* + ID_VENDOR_FROM_DATABASE=Science and Research Centre of Computer Technology (JSC "NICEVT") + +pci:v00001C63d00000008* + ID_MODEL_FROM_DATABASE=K1927BB1Ya [EC8430] Angara Interconnection Network Adapter + +pci:v00001C7E* + ID_VENDOR_FROM_DATABASE=TTTech Computertechnik AG + +pci:v00001C7Ed00000200* + ID_MODEL_FROM_DATABASE=zFAS Debug Port + +pci:v00001C7F* + ID_VENDOR_FROM_DATABASE=Elektrobit Austria GmbH + +pci:v00001C7Fd00005100* + ID_MODEL_FROM_DATABASE=EB5100 + +pci:v00001C8A* + ID_VENDOR_FROM_DATABASE=TSF5 Corporation + +pci:v00001C8Ad00000001* + ID_MODEL_FROM_DATABASE=Hunter PCI Express + +pci:v00001CB1* + ID_VENDOR_FROM_DATABASE=Collion UG & Co.KG + +pci:v00001CB8* + ID_VENDOR_FROM_DATABASE=Dawning Information Industry Co., Ltd. + +pci:v00001CC5* + ID_VENDOR_FROM_DATABASE=Embedded Intelligence, Inc. + +pci:v00001CC5d00000100* + ID_MODEL_FROM_DATABASE=CAN-PCIe-02 + +pci:v00001CC7* + ID_VENDOR_FROM_DATABASE=Radian Memory Systems Inc. + +pci:v00001CC7d00000200* + ID_MODEL_FROM_DATABASE=RMS-200 + +pci:v00001CC7d00000250* + ID_MODEL_FROM_DATABASE=RMS-250 + +pci:v00001CD2* + ID_VENDOR_FROM_DATABASE=SesKion GmbH + +pci:v00001CD2d00000301* + ID_MODEL_FROM_DATABASE=Simulyzer-RT CompactPCI Serial DIO-1 card + +pci:v00001CD2d00000302* + ID_MODEL_FROM_DATABASE=Simulyzer-RT CompactPCI Serial PSI5-ECU-1 card + +pci:v00001CD2d00000303* + ID_MODEL_FROM_DATABASE=Simulyzer-RT CompactPCI Serial PSI5-SIM-1 card + +pci:v00001CD2d00000304* + ID_MODEL_FROM_DATABASE=Simulyzer-RT CompactPCI Serial PWR-ANA-1 card + +pci:v00001CD2d00000305* + ID_MODEL_FROM_DATABASE=Simulyzer-RT CompactPCI Serial CAN-1 card + +pci:v00001CDD* + ID_VENDOR_FROM_DATABASE=secunet Security Networks AG + +pci:v00001CE4* + ID_VENDOR_FROM_DATABASE=Exablaze + +pci:v00001CE4d00000001* + ID_MODEL_FROM_DATABASE=ExaNIC X4 + +pci:v00001CE4d00000002* + ID_MODEL_FROM_DATABASE=ExaNIC X2 + +pci:v00001CE4d00000003* + ID_MODEL_FROM_DATABASE=ExaNIC X10 + +pci:v00001CE4d00000004* + ID_MODEL_FROM_DATABASE=ExaNIC X10-GM + +pci:v00001CE4d00000005* + ID_MODEL_FROM_DATABASE=ExaNIC X40 + +pci:v00001CF7* + ID_VENDOR_FROM_DATABASE=Subspace Dynamics + +pci:v00001D00* + ID_VENDOR_FROM_DATABASE=Pure Storage + +pci:v00001D1D* + ID_VENDOR_FROM_DATABASE=CNEX Labs + +pci:v00001D1Dd00001F1F* + ID_MODEL_FROM_DATABASE=QEMU NVM Express LightNVM Controller + +pci:v00001D1Dd00002807* + ID_MODEL_FROM_DATABASE=8800 series NVMe SSD + +pci:v00001D21* + ID_VENDOR_FROM_DATABASE=Allo + +pci:v00001D26* + ID_VENDOR_FROM_DATABASE=Kalray Inc. + +pci:v00001D26d00000040* + ID_MODEL_FROM_DATABASE=Turbocard2 Accelerator + +pci:v00001D26d00000080* + ID_MODEL_FROM_DATABASE=Open Network Interface Card 80G + +pci:v00001D26d000000C0* + ID_MODEL_FROM_DATABASE=Turbocard3 Accelerator + +pci:v00001D26d0000E004* + ID_MODEL_FROM_DATABASE=AB01/EMB01 Development Board + +pci:v00001D40* + ID_VENDOR_FROM_DATABASE=Techman Electronics (Changshu) Co., Ltd. + +pci:v00001D44* + ID_VENDOR_FROM_DATABASE=DPT + +pci:v00001D44d0000A400* + ID_MODEL_FROM_DATABASE=PM2x24/PM3224 + +pci:v00001D49* + ID_VENDOR_FROM_DATABASE=Lenovo + +pci:v00001D5C* + ID_VENDOR_FROM_DATABASE=Fantasia Trading LLC + +pci:v00001D61* + ID_VENDOR_FROM_DATABASE=Technobox, Inc. + +pci:v00001D62* + ID_VENDOR_FROM_DATABASE=Nebbiolo Technologies + +pci:v00001D65* + ID_VENDOR_FROM_DATABASE=Imagine Communications Corp. + +pci:v00001D65d000004DE* + ID_MODEL_FROM_DATABASE=Taurus/McKinley + +pci:v00001D6C* + ID_VENDOR_FROM_DATABASE=Atomic Rules LLC + +pci:v00001D6Cd00001001* + ID_MODEL_FROM_DATABASE=A5PL-E1 + +pci:v00001D6Cd00001002* + ID_MODEL_FROM_DATABASE=A5PL-E7 + +pci:v00001D6Cd00001003* + ID_MODEL_FROM_DATABASE=S5PEDS-AB + +pci:v00001D6Cd00001004* + ID_MODEL_FROM_DATABASE=KC705-K325 + +pci:v00001D6Cd00001005* + ID_MODEL_FROM_DATABASE=ZC706-Z045 + +pci:v00001D6Cd00001006* + ID_MODEL_FROM_DATABASE=KCU105-KU040 + +pci:v00001D6Cd00001007* + ID_MODEL_FROM_DATABASE=XUSP3S-VU095 [Jasper] + +pci:v00001D6Cd00001008* + ID_MODEL_FROM_DATABASE=XUSPL4-VU065 [Mustang UltraScale] + +pci:v00001D6Cd00001009* + ID_MODEL_FROM_DATABASE=XUSPL4-VU3P [Mustang UltraScale+] + +pci:v00001D6Cd0000100A* + ID_MODEL_FROM_DATABASE=A10PL4-A10GX115 + +pci:v00001D6Cd0000100B* + ID_MODEL_FROM_DATABASE=K35-2SFP + +pci:v00001D6Cd0000100C* + ID_MODEL_FROM_DATABASE=K35-4SFP + +pci:v00001D6Cd0000100D* + ID_MODEL_FROM_DATABASE=AR-ARKA-FX0 [Arkville 32B DPDK Data Mover] + +pci:v00001D6Cd0000100E* + ID_MODEL_FROM_DATABASE=AR-ARKA-FX1 [Arkville 64B DPDK Data Mover] + +pci:v00001D6Cd00004200* + ID_MODEL_FROM_DATABASE=A5PL-E1-10GETI [10 GbE Ethernet Traffic Instrument] + +pci:v00001DE1* + ID_VENDOR_FROM_DATABASE=Tekram Technology Co.,Ltd. + +pci:v00001DE1d00000391* + ID_MODEL_FROM_DATABASE=TRM-S1040 [DC-315 / DC-395 series] + +pci:v00001DE1d00002020* + ID_MODEL_FROM_DATABASE=DC-390 + +pci:v00001DE1d0000690C* + ID_MODEL_FROM_DATABASE=690c + +pci:v00001DE1d0000DC29* + ID_MODEL_FROM_DATABASE=DC290 + +pci:v00001FC0* + ID_VENDOR_FROM_DATABASE=Ascom (Finland) Oy + +pci:v00001FC0d00000300* + ID_MODEL_FROM_DATABASE=E2200 Dual E1/Rawpipe Card + +pci:v00001FC0d00000301* + ID_MODEL_FROM_DATABASE=C5400 SHDSL/E1 Card + +pci:v00001FC1* + ID_VENDOR_FROM_DATABASE=QLogic, Corp. + +pci:v00001FC1d0000000D* + ID_MODEL_FROM_DATABASE=IBA6110 InfiniBand HCA + +pci:v00001FC1d00000010* + ID_MODEL_FROM_DATABASE=IBA6120 InfiniBand HCA + +pci:v00001FC9* + ID_VENDOR_FROM_DATABASE=Tehuti Networks Ltd. + +pci:v00001FC9d00003009* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC + +pci:v00001FC9d00003010* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC + +pci:v00001FC9d00003010sv00000000sd00003002* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC (10-Giga TOE Single Port XFP SmartNIC) + +pci:v00001FC9d00003010sv00000000sd00003004* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC (10-Giga TOE Single Port SFP+ SmartNIC) + +pci:v00001FC9d00003010sv00000000sd00003008* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC (10-Giga TOE Single Port CX4 SmartNIC) + +pci:v00001FC9d00003014* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC 2-Port + +pci:v00001FC9d00003014sv00000000sd00003003* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC 2-Port (10-Giga TOE Dual Port XFP Low Profile SmartNIC) + +pci:v00001FC9d00003014sv00000000sd00003005* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC 2-Port (10-Giga TOE Dual Port SFP+ Low Profile SmartNIC) + +pci:v00001FC9d00003014sv00000000sd00003014* + ID_MODEL_FROM_DATABASE=10-Giga TOE SmartNIC 2-Port (10-Giga TOE Dual Port CX4 Low Profile SmartNIC) + +pci:v00001FC9d00003110* + ID_MODEL_FROM_DATABASE=10-Giga TOE Single Port SmartNIC + +pci:v00001FC9d00003110sv00000000sd00003004* + ID_MODEL_FROM_DATABASE=10-Giga TOE Single Port SmartNIC (10-Giga TOE Single Port SFP+ SmartNIC) + +pci:v00001FC9d00003114* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC + +pci:v00001FC9d00003114sv00000000sd00003005* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port SFP+ Low Profile SmartNIC) + +pci:v00001FC9d00003114sv00000000sd00003011* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port SFP+/CX4 Low Profile SmartNIC) + +pci:v00001FC9d00003114sv00000000sd00003012* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port CX4/SFP+ Low Profile SmartNIC) + +pci:v00001FC9d00003114sv00000000sd00003014* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port CX4 Low Profile SmartNIC) + +pci:v00001FC9d00003310* + ID_MODEL_FROM_DATABASE=10-Giga TOE SFP+ Single Port SmartNIC + +pci:v00001FC9d00003310sv00000000sd00003004* + ID_MODEL_FROM_DATABASE=10-Giga TOE SFP+ Single Port SmartNIC (10-Giga TOE Single Port SFP+ SmartNIC) + +pci:v00001FC9d00003314* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC + +pci:v00001FC9d00003314sv00000000sd00003005* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port SFP+ Low Profile SmartNIC) + +pci:v00001FC9d00003314sv00000000sd00003011* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port SFP+/CX4 Low Profile SmartNIC) + +pci:v00001FC9d00003314sv00000000sd00003012* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port CX4/SFP+ Low Profile SmartNIC) + +pci:v00001FC9d00003314sv00000000sd00003014* + ID_MODEL_FROM_DATABASE=10-Giga TOE Dual Port Low Profile SmartNIC (10-Giga TOE Dual Port CX4 Low Profile SmartNIC) + +pci:v00001FC9d00004010* + ID_MODEL_FROM_DATABASE=TN4010 Clean SROM + +pci:v00001FC9d00004020* + ID_MODEL_FROM_DATABASE=TN9030 10GbE CX4 Ethernet Adapter + +pci:v00001FC9d00004022* + ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter + +pci:v00001FC9d00004022sv00001043sd00008709* + ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (XG-C100F 10GbE SFP+ Ethernet Adapter) + +pci:v00001FC9d00004022sv00001186sd00004D00* + ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (DXE-810S 10GbE SFP+ Ethernet Adapter) + +pci:v00001FC9d00004022sv00001432sd00008103* + ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (EN-8102PF 10GbE SPF+ Ethernet Adapter) + +pci:v00001FC9d00004022sv00001FC9sd00003015* + ID_MODEL_FROM_DATABASE=TN9310 10GbE SFP+ Ethernet Adapter (Ethernet Adapter) + +pci:v00001FC9d00004024* + ID_MODEL_FROM_DATABASE=TN9210 10GBase-T Ethernet Adapter + +pci:v00001FC9d00004025* + ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter + +pci:v00001FC9d00004025sv0000105Asd00007203* + ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (SANLink3 NBase-T1) + +pci:v00001FC9d00004025sv00001186sd00002900* + ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (DXE-810T 10GBase-T Ethernet Adapter) + +pci:v00001FC9d00004025sv00001432sd00008102* + ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (EN-8102P 10GbE Ethernet Adapter) + +pci:v00001FC9d00004025sv00001FC9sd00003015* + ID_MODEL_FROM_DATABASE=TN9510 10GBase-T/NBASE-T Ethernet Adapter (Ethernet Adapter) + +pci:v00001FC9d00004026* + ID_MODEL_FROM_DATABASE=TN9610 10GbE SFP+ Ethernet Adapter + +pci:v00001FC9d00004027* + ID_MODEL_FROM_DATABASE=TN9710 10GBase-T/NBASE-T Ethernet Adapter + +pci:v00001FCC* + ID_VENDOR_FROM_DATABASE=StreamLabs + +pci:v00001FCCd0000F416* + ID_MODEL_FROM_DATABASE=MS416 + +pci:v00001FCCd0000FB01* + ID_MODEL_FROM_DATABASE=MH4LM + +pci:v00001FCE* + ID_VENDOR_FROM_DATABASE=Cognio Inc. + +pci:v00001FCEd00000001* + ID_MODEL_FROM_DATABASE=Spectrum Analyzer PC Card (SAgE) + +pci:v00001FD4* + ID_VENDOR_FROM_DATABASE=SUNIX Co., Ltd. + +pci:v00001FD4d00000001* + ID_MODEL_FROM_DATABASE=Matrix multiport serial adapter + +pci:v00001FD4d00001999* + ID_MODEL_FROM_DATABASE=Multiport serial controller + +pci:v00002000* + ID_VENDOR_FROM_DATABASE=Smart Link Ltd. + +pci:v00002000d00002800* + ID_MODEL_FROM_DATABASE=SmartPCI2800 V.92 PCI Soft DFT + +pci:v00002001* + ID_VENDOR_FROM_DATABASE=Temporal Research Ltd + +pci:v00002003* + ID_VENDOR_FROM_DATABASE=Smart Link Ltd. + +pci:v00002003d00008800* + ID_MODEL_FROM_DATABASE=LM-I56N + +pci:v00002004* + ID_VENDOR_FROM_DATABASE=Smart Link Ltd. + +pci:v000020F4* + ID_VENDOR_FROM_DATABASE=TRENDnet + +pci:v00002116* + ID_VENDOR_FROM_DATABASE=ZyDAS Technology Corp. + +pci:v000021C3* + ID_VENDOR_FROM_DATABASE=21st Century Computer Corp. + +pci:v00002304* + ID_VENDOR_FROM_DATABASE=Colorgraphic Communications Corp. + +pci:v00002348* + ID_VENDOR_FROM_DATABASE=Racore + +pci:v00002348d00002010* + ID_MODEL_FROM_DATABASE=8142 100VG/AnyLAN + +pci:v00002646* + ID_VENDOR_FROM_DATABASE=Kingston Technologies + +pci:v0000270B* + ID_VENDOR_FROM_DATABASE=Xantel Corporation + +pci:v0000270F* + ID_VENDOR_FROM_DATABASE=Chaintech Computer Co. Ltd + +pci:v00002711* + ID_VENDOR_FROM_DATABASE=AVID Technology Inc. + +pci:v00002955* + ID_VENDOR_FROM_DATABASE=Connectix Virtual PC + +pci:v00002955d00006E61* + ID_MODEL_FROM_DATABASE=OHCI USB 1.1 controller + +pci:v00002A15* + ID_VENDOR_FROM_DATABASE=3D Vision(???) + +pci:v00002BD8* + ID_VENDOR_FROM_DATABASE=ROPEX Industrie-Elektronik GmbH + +pci:v00003000* + ID_VENDOR_FROM_DATABASE=Hansol Electronics Inc. + +pci:v00003112* + ID_VENDOR_FROM_DATABASE=Satelco Ingenieria S.A. + +pci:v00003142* + ID_VENDOR_FROM_DATABASE=Post Impression Systems. + +pci:v000031AB* + ID_VENDOR_FROM_DATABASE=Zonet + +pci:v000031ABd00001FAA* + ID_MODEL_FROM_DATABASE=ZEW1602 802.11b/g Wireless Adapter + +pci:v00003388* + ID_VENDOR_FROM_DATABASE=Hint Corp + +pci:v00003388d00000013* + ID_MODEL_FROM_DATABASE=HiNT HC4 PCI to ISDN bridge, Multimedia audio controller + +pci:v00003388d00000014* + ID_MODEL_FROM_DATABASE=HiNT HC4 PCI to ISDN bridge, Network controller + +pci:v00003388d00000020* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (transparent mode) + +pci:v00003388d00000021* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) + +pci:v00003388d00000021sv00001775sd0000C200* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (C2K CompactPCI interface bridge) + +pci:v00003388d00000021sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (CE9) + +pci:v00003388d00000021sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (CT7 mainboard) + +pci:v00003388d00000021sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (CT8 mainboard) + +pci:v00003388d00000021sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (Cx9 mainboard) + +pci:v00003388d00000021sv00004C53sd000010A0* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (CA3/CR3 mainboard) + +pci:v00003388d00000021sv00004C53sd00003010* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (PPCI mezzanine (32-bit PMC)) + +pci:v00003388d00000021sv00004C53sd00003011* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (PPCI mezzanine (64-bit PMC)) + +pci:v00003388d00000021sv00004C53sd00004000* + ID_MODEL_FROM_DATABASE=HB6 Universal PCI-PCI bridge (non-transparent mode) (PMCCARR1 carrier board) + +pci:v00003388d00000022* + ID_MODEL_FROM_DATABASE=HiNT HB4 PCI-PCI Bridge (PCI6150) + +pci:v00003388d00000026* + ID_MODEL_FROM_DATABASE=HB2 PCI-PCI Bridge + +pci:v00003388d00001014* + ID_MODEL_FROM_DATABASE=AudioTrak Maya + +pci:v00003388d00001018* + ID_MODEL_FROM_DATABASE=Audiotrak INCA88 + +pci:v00003388d00001019* + ID_MODEL_FROM_DATABASE=Miditrak 2120 + +pci:v00003388d0000101A* + ID_MODEL_FROM_DATABASE=E.Band [AudioTrak Inca88] + +pci:v00003388d0000101B* + ID_MODEL_FROM_DATABASE=E.Band [AudioTrak Inca88] + +pci:v00003388d00008011* + ID_MODEL_FROM_DATABASE=VXPro II Chipset + +pci:v00003388d00008011sv00003388sd00008011* + ID_MODEL_FROM_DATABASE=VXPro II Chipset (CPU to PCI Bridge) + +pci:v00003388d00008012* + ID_MODEL_FROM_DATABASE=VXPro II Chipset + +pci:v00003388d00008012sv00003388sd00008012* + ID_MODEL_FROM_DATABASE=VXPro II Chipset (PCI to ISA Bridge) + +pci:v00003388d00008013* + ID_MODEL_FROM_DATABASE=VXPro II IDE + +pci:v00003388d00008013sv00003388sd00008013* + ID_MODEL_FROM_DATABASE=VXPro II IDE (VXPro II Chipset EIDE Controller) + +pci:v00003388d0000A103* + ID_MODEL_FROM_DATABASE=Blackmagic Design DeckLink HD Pro + +pci:v00003411* + ID_VENDOR_FROM_DATABASE=Quantum Designs (H.K.) Inc + +pci:v00003442* + ID_VENDOR_FROM_DATABASE=Bihl+Wiedemann GmbH + +pci:v00003442d00001783* + ID_MODEL_FROM_DATABASE=AS-i 3.0 cPCI Master + +pci:v00003442d00001922* + ID_MODEL_FROM_DATABASE=AS-i 3.0 PCI Master + +pci:v00003475* + ID_VENDOR_FROM_DATABASE=Arastra Inc. + +pci:v00003513* + ID_VENDOR_FROM_DATABASE=ARCOM Control Systems Ltd + +pci:v000037D9* + ID_VENDOR_FROM_DATABASE=ITD Firm ltd. + +pci:v000037D9d00001138* + ID_MODEL_FROM_DATABASE=SCHD-PH-8 Phase detector + +pci:v000037D9d00001140* + ID_MODEL_FROM_DATABASE=VR-12-PCI + +pci:v000037D9d00001141* + ID_MODEL_FROM_DATABASE=PCI-485(422) + +pci:v000037D9d00001142* + ID_MODEL_FROM_DATABASE=PCI-CAN2 + +pci:v00003842* + ID_VENDOR_FROM_DATABASE=eVga.com. Corp. + +pci:v000038EF* + ID_VENDOR_FROM_DATABASE=4Links + +pci:v00003D3D* + ID_VENDOR_FROM_DATABASE=3DLabs + +pci:v00003D3Dd00000001* + ID_MODEL_FROM_DATABASE=GLINT 300SX + +pci:v00003D3Dd00000002* + ID_MODEL_FROM_DATABASE=GLINT 500TX + +pci:v00003D3Dd00000002sv00000000sd00000000* + ID_MODEL_FROM_DATABASE=GLINT 500TX (GLoria L) + +pci:v00003D3Dd00000003* + ID_MODEL_FROM_DATABASE=GLINT Delta + +pci:v00003D3Dd00000003sv00000000sd00000000* + ID_MODEL_FROM_DATABASE=GLINT Delta (GLoria XL) + +pci:v00003D3Dd00000004* + ID_MODEL_FROM_DATABASE=Permedia + +pci:v00003D3Dd00000005* + ID_MODEL_FROM_DATABASE=Permedia + +pci:v00003D3Dd00000006* + ID_MODEL_FROM_DATABASE=GLINT MX + +pci:v00003D3Dd00000006sv00000000sd00000000* + ID_MODEL_FROM_DATABASE=GLINT MX (GLoria XL) + +pci:v00003D3Dd00000006sv00001048sd00000A42* + ID_MODEL_FROM_DATABASE=GLINT MX (GLoria XXL) + +pci:v00003D3Dd00000007* + ID_MODEL_FROM_DATABASE=3D Extreme + +pci:v00003D3Dd00000008* + ID_MODEL_FROM_DATABASE=GLINT Gamma G1 + +pci:v00003D3Dd00000008sv00001048sd00000A42* + ID_MODEL_FROM_DATABASE=GLINT Gamma G1 (GLoria XXL) + +pci:v00003D3Dd00000009* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D + +pci:v00003D3Dd00000009sv00001040sd00000011* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (AccelStar II) + +pci:v00003D3Dd00000009sv00001048sd00000A42* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (GLoria XXL) + +pci:v00003D3Dd00000009sv000013E9sd00001000* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (6221L-4U) + +pci:v00003D3Dd00000009sv00003D3Dsd00000100* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (AccelStar II 3D Accelerator) + +pci:v00003D3Dd00000009sv00003D3Dsd00000111* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Permedia 3:16) + +pci:v00003D3Dd00000009sv00003D3Dsd00000114* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Santa Ana) + +pci:v00003D3Dd00000009sv00003D3Dsd00000116* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Oxygen GVX1) + +pci:v00003D3Dd00000009sv00003D3Dsd00000119* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Scirocco) + +pci:v00003D3Dd00000009sv00003D3Dsd00000120* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Santa Ana PCL) + +pci:v00003D3Dd00000009sv00003D3Dsd00000125* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Oxygen VX1) + +pci:v00003D3Dd00000009sv00003D3Dsd00000127* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D (Permedia3 Create!) + +pci:v00003D3Dd0000000A* + ID_MODEL_FROM_DATABASE=GLINT R3 + +pci:v00003D3Dd0000000Asv00003D3Dsd00000121* + ID_MODEL_FROM_DATABASE=GLINT R3 (Oxygen VX1) + +pci:v00003D3Dd0000000C* + ID_MODEL_FROM_DATABASE=GLINT R3 [Oxygen VX1] + +pci:v00003D3Dd0000000Csv00003D3Dsd00000144* + ID_MODEL_FROM_DATABASE=GLINT R3 [Oxygen VX1] (Oxygen VX1-4X AGP [Permedia 4]) + +pci:v00003D3Dd0000000D* + ID_MODEL_FROM_DATABASE=GLint R4 rev A + +pci:v00003D3Dd0000000E* + ID_MODEL_FROM_DATABASE=GLINT Gamma G2 + +pci:v00003D3Dd00000011* + ID_MODEL_FROM_DATABASE=GLint R4 rev B + +pci:v00003D3Dd00000012* + ID_MODEL_FROM_DATABASE=GLint R5 rev A + +pci:v00003D3Dd00000013* + ID_MODEL_FROM_DATABASE=GLint R5 rev B + +pci:v00003D3Dd00000020* + ID_MODEL_FROM_DATABASE=VP10 visual processor + +pci:v00003D3Dd00000022* + ID_MODEL_FROM_DATABASE=VP10 visual processor + +pci:v00003D3Dd00000024* + ID_MODEL_FROM_DATABASE=VP9 visual processor + +pci:v00003D3Dd0000002C* + ID_MODEL_FROM_DATABASE=Wildcat Realizm 100/200 + +pci:v00003D3Dd00000030* + ID_MODEL_FROM_DATABASE=Wildcat Realizm 800 + +pci:v00003D3Dd00000032* + ID_MODEL_FROM_DATABASE=Wildcat Realizm 500 + +pci:v00003D3Dd00000100* + ID_MODEL_FROM_DATABASE=Permedia II 2D+3D + +pci:v00003D3Dd000007A1* + ID_MODEL_FROM_DATABASE=Wildcat III 6210 + +pci:v00003D3Dd000007A2* + ID_MODEL_FROM_DATABASE=Sun XVR-500 Graphics Accelerator + +pci:v00003D3Dd000007A3* + ID_MODEL_FROM_DATABASE=Wildcat IV 7210 + +pci:v00003D3Dd00001004* + ID_MODEL_FROM_DATABASE=Permedia + +pci:v00003D3Dd00003D04* + ID_MODEL_FROM_DATABASE=Permedia + +pci:v00003D3Dd0000FFFF* + ID_MODEL_FROM_DATABASE=Glint VGA + +pci:v00004005* + ID_VENDOR_FROM_DATABASE=Avance Logic Inc. + +pci:v00004005d00000300* + ID_MODEL_FROM_DATABASE=ALS300 PCI Audio Device + +pci:v00004005d00000308* + ID_MODEL_FROM_DATABASE=ALS300+ PCI Audio Device + +pci:v00004005d00000309* + ID_MODEL_FROM_DATABASE=PCI Input Controller + +pci:v00004005d00001064* + ID_MODEL_FROM_DATABASE=ALG-2064 + +pci:v00004005d00002064* + ID_MODEL_FROM_DATABASE=ALG-2064i + +pci:v00004005d00002128* + ID_MODEL_FROM_DATABASE=ALG-2364A GUI Accelerator + +pci:v00004005d00002301* + ID_MODEL_FROM_DATABASE=ALG-2301 + +pci:v00004005d00002302* + ID_MODEL_FROM_DATABASE=ALG-2302 + +pci:v00004005d00002303* + ID_MODEL_FROM_DATABASE=AVG-2302 GUI Accelerator + +pci:v00004005d00002364* + ID_MODEL_FROM_DATABASE=ALG-2364A + +pci:v00004005d00002464* + ID_MODEL_FROM_DATABASE=ALG-2464 + +pci:v00004005d00002501* + ID_MODEL_FROM_DATABASE=ALG-2564A/25128A + +pci:v00004005d00004000* + ID_MODEL_FROM_DATABASE=ALS4000 Audio Chipset + +pci:v00004005d00004000sv00004005sd00004000* + ID_MODEL_FROM_DATABASE=ALS4000 Audio Chipset + +pci:v00004005d00004710* + ID_MODEL_FROM_DATABASE=ALC200/200P + +pci:v00004033* + ID_VENDOR_FROM_DATABASE=Addtron Technology Co, Inc. + +pci:v00004033d00001360* + ID_MODEL_FROM_DATABASE=RTL8139 Ethernet + +pci:v00004040* + ID_VENDOR_FROM_DATABASE=NetXen Incorporated + +pci:v00004040d00000001* + ID_MODEL_FROM_DATABASE=NXB-10GXSR 10-Gigabit Ethernet PCIe Adapter with SR-XFP optical interface + +pci:v00004040d00000001sv0000103Csd00007047* + ID_MODEL_FROM_DATABASE=NXB-10GXSR 10-Gigabit Ethernet PCIe Adapter with SR-XFP optical interface (NC510F PCIe 10-Gigabit Server Adapter) + +pci:v00004040d00000002* + ID_MODEL_FROM_DATABASE=NXB-10GCX4 10-Gigabit Ethernet PCIe Adapter with CX4 copper interface + +pci:v00004040d00000002sv0000103Csd00007048* + ID_MODEL_FROM_DATABASE=NXB-10GCX4 10-Gigabit Ethernet PCIe Adapter with CX4 copper interface (NC510c PCIe 10-Gigabit Server Adapter) + +pci:v00004040d00000003* + ID_MODEL_FROM_DATABASE=NXB-4GCU Quad Gigabit Ethernet PCIe Adapter with 1000-BASE-T interface + +pci:v00004040d00000004* + ID_MODEL_FROM_DATABASE=BladeCenter-H 10-Gigabit Ethernet High Speed Daughter Card + +pci:v00004040d00000005* + ID_MODEL_FROM_DATABASE=NetXen Dual Port 10GbE Multifunction Adapter for c-Class + +pci:v00004040d00000005sv0000103Csd0000170E* + ID_MODEL_FROM_DATABASE=NetXen Dual Port 10GbE Multifunction Adapter for c-Class (NC512m Dual Port 10GbE Multifunction BL-C Adapter) + +pci:v00004040d00000024* + ID_MODEL_FROM_DATABASE=XG Mgmt + +pci:v00004040d00000025* + ID_MODEL_FROM_DATABASE=XG Mgmt + +pci:v00004040d00000100* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter + +pci:v00004040d00000100sv0000103Csd0000171B* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NC522m Dual Port 10GbE Multifunction BL-c Adapter) + +pci:v00004040d00000100sv0000103Csd00001740* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NC375T PCI Express Quad Port Gigabit Server Adapter) + +pci:v00004040d00000100sv0000103Csd00003251* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NC375i 1G w/NC524SFP 10G Module) + +pci:v00004040d00000100sv0000103Csd0000705A* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NC375i Integrated Quad Port Multifunction Gigabit Server Adapter) + +pci:v00004040d00000100sv0000103Csd0000705B* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NC522SFP Dual Port 10GbE Server Adapter) + +pci:v00004040d00000100sv0000152Dsd0000896B* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (TG20 Dual Port 10GbE Server/Storage Adapter) + +pci:v00004040d00000100sv00004040sd00000124* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (NX3031 Quad Port Gigabit Server Adapter) + +pci:v00004040d00000100sv00004040sd00000126* + ID_MODEL_FROM_DATABASE=NX3031 Multifunction 1/10-Gigabit Server Adapter (Dual Port SFP+ 10GbE Server Adapter) + +pci:v00004143* + ID_VENDOR_FROM_DATABASE=Digital Equipment Corp + +pci:v00004144* + ID_VENDOR_FROM_DATABASE=Alpha Data + +pci:v00004144d00000044* + ID_MODEL_FROM_DATABASE=ADM-XRCIIPro + +pci:v00004150* + ID_VENDOR_FROM_DATABASE=ONA Electroerosion + +pci:v00004150d00000001* + ID_MODEL_FROM_DATABASE=PCI32TLITE FILSTRUP1 PCI to VME Bridge Controller + +pci:v00004150d00000006* + ID_MODEL_FROM_DATABASE=PCI32TLITE UART 16550 Opencores + +pci:v00004150d00000007* + ID_MODEL_FROM_DATABASE=PCI32TLITE CAN Controller Opencores + +pci:v0000415A* + ID_VENDOR_FROM_DATABASE=Auzentech, Inc. + +pci:v0000416C* + ID_VENDOR_FROM_DATABASE=Aladdin Knowledge Systems + +pci:v0000416Cd00000100* + ID_MODEL_FROM_DATABASE=AladdinCARD + +pci:v0000416Cd00000200* + ID_MODEL_FROM_DATABASE=CPC + +pci:v00004254* + ID_VENDOR_FROM_DATABASE=DVBSky + +pci:v00004321* + ID_VENDOR_FROM_DATABASE=Tata Power Strategic Electronics Division + +pci:v00004348* + ID_VENDOR_FROM_DATABASE=WCH.CN + +pci:v00004348d00002273* + ID_MODEL_FROM_DATABASE=CH351 PCI Dual Serial Port Controller + +pci:v00004348d00003253* + ID_MODEL_FROM_DATABASE=CH352 PCI Dual Serial Port Controller + +pci:v00004348d00003453* + ID_MODEL_FROM_DATABASE=CH353 PCI Quad Serial Port Controller + +pci:v00004348d00005053* + ID_MODEL_FROM_DATABASE=CH352 PCI Serial and Parallel Port Controller + +pci:v00004348d00007053* + ID_MODEL_FROM_DATABASE=CH353 PCI Dual Serial and Parallel Ports Controller + +pci:v00004348d00007073* + ID_MODEL_FROM_DATABASE=CH356 PCI Quad Serial and Parallel Ports Controller + +pci:v00004348d00007173* + ID_MODEL_FROM_DATABASE=CH355 PCI Quad Serial Port Controller + +pci:v0000434E* + ID_VENDOR_FROM_DATABASE=CAST Navigation LLC + +pci:v00004444* + ID_VENDOR_FROM_DATABASE=Internext Compression Inc + +pci:v00004444d00000016* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder + +pci:v00004444d00000016sv00000070sd00000003* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 250) + +pci:v00004444d00000016sv00000070sd00000009* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd00000801* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd00000807* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd00004001* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 250) + +pci:v00004444d00000016sv00000070sd00004009* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 250) + +pci:v00004444d00000016sv00000070sd00004801* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 250) + +pci:v00004444d00000016sv00000070sd00004803* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 250) + +pci:v00004444d00000016sv00000070sd00008003* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd00008801* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd0000C801* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv00000070sd0000E807* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 500 (1st unit)) + +pci:v00004444d00000016sv00000070sd0000E817* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 500 (2nd unit)) + +pci:v00004444d00000016sv00000070sd0000FF92* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WiNTV PVR-550) + +pci:v00004444d00000016sv00000270sd00000801* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (WinTV PVR 150) + +pci:v00004444d00000016sv0000104Dsd0000013D* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (ENX-26 TV Encoder) + +pci:v00004444d00000016sv000010FCsd0000D038* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (GV-MVP/RX2W (1st unit)) + +pci:v00004444d00000016sv000010FCsd0000D039* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (GV-MVP/RX2W (2nd unit)) + +pci:v00004444d00000016sv000012ABsd0000FFF3* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (MPG600) + +pci:v00004444d00000016sv000012ABsd0000FFFF* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (MPG600) + +pci:v00004444d00000016sv00001461sd0000C00A* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM, Philips FQ1216MK3 tuner)) + +pci:v00004444d00000016sv00001461sd0000C00B* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM+FM, Philips FM1216MK3 tuner)) + +pci:v00004444d00000016sv00001461sd0000C00C* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC, JAPAN version, Philips FI1286MK2 tuner)) + +pci:v00004444d00000016sv00001461sd0000C010* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC, Philips FI1236MK3 tuner)) + +pci:v00004444d00000016sv00001461sd0000C011* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC+FM, Philips FM1236MK3 tuner)) + +pci:v00004444d00000016sv00001461sd0000C018* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC, Philips FQ1236MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C019* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (UltraTV 1500 MCE, a.k.a. M113 PCI Analog TV (NTSC+FM, Philips FQ1236MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C01A* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM, Philips FQ1216MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C01B* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM+FM, Philips FM1216MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C030* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC-J, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C031* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC-J+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C032* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C033* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (PAL/SECAM+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C034* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C035* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M113 PCI Analog TV (NTSC+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C03F* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (C115 PCI video capture card (no tuner)) + +pci:v00004444d00000016sv00001461sd0000C136* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M104 mini-PCI Analog TV) + +pci:v00004444d00000016sv00001461sd0000C20A* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (PAL/SECAM, Philips FQ1216MK3 tuner)) + +pci:v00004444d00000016sv00001461sd0000C218* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC, Philips FQ1236MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C219* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC+FM, Philips FQ1236MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C21A* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (PAL/SECAM, Philips FQ1216MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C21B* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (PAL/SECAM+FM, Philips FM1216MK5 tuner)) + +pci:v00004444d00000016sv00001461sd0000C230* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC-J, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C231* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC-J+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C232* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (PAL/SECAM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C233* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (PAL/SECAM+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C234* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C235* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M755 AVerTV Video Capture (NTSC+FM, Partsnic tuner)) + +pci:v00004444d00000016sv00001461sd0000C337* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (E106 AVerMedia AVerTV Video Capture) + +pci:v00004444d00000016sv00001461sd0000C439* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M116 AVerMedia AVerTV MCE 116 Plus (NTSC/PAL/SECAM+FM+REMOTE, Xceive 2028 tuner)) + +pci:v00004444d00000016sv00001461sd0000C5FF* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (C755 AVerTV Video Capture card (no tuner)) + +pci:v00004444d00000016sv00001461sd0000C6FF* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (C115 PCI video capture card (no tuner)) + +pci:v00004444d00000016sv00001461sd0000C739* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (M785 AVerMedia PCI Analog TV (NTSC/PAL/SECAM+FM, Xceive 2028 tuner)) + +pci:v00004444d00000016sv00009005sd00000092* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (VideOh! AVC-2010) + +pci:v00004444d00000016sv00009005sd00000093* + ID_MODEL_FROM_DATABASE=iTVC16 (CX23416) Video Decoder (VideOh! AVC-2410) + +pci:v00004444d00000803* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder + +pci:v00004444d00000803sv00000070sd00004000* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (WinTV PVR-350) + +pci:v00004444d00000803sv00000070sd00004001* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (WinTV PVR-250) + +pci:v00004444d00000803sv00000070sd00004800* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (WinTV PVR-350 (V1)) + +pci:v00004444d00000803sv000012ABsd00000000* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (MPG160) + +pci:v00004444d00000803sv00001461sd0000A3CE* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (M179) + +pci:v00004444d00000803sv00001461sd0000A3CF* + ID_MODEL_FROM_DATABASE=iTVC15 (CX23415) Video Decoder (M179) + +pci:v00004468* + ID_VENDOR_FROM_DATABASE=Bridgeport machines + +pci:v00004594* + ID_VENDOR_FROM_DATABASE=Cogetec Informatique Inc + +pci:v000045FB* + ID_VENDOR_FROM_DATABASE=Baldor Electric Company + +pci:v00004624* + ID_VENDOR_FROM_DATABASE=Budker Institute of Nuclear Physics + +pci:v00004624d0000ADC1* + ID_MODEL_FROM_DATABASE=ADC200ME High speed ADC + +pci:v00004624d0000DE01* + ID_MODEL_FROM_DATABASE=DL200ME High resolution delay line PCI based card + +pci:v00004624d0000DE02* + ID_MODEL_FROM_DATABASE=DL200ME Middle resolution delay line PCI based card + +pci:v00004651* + ID_VENDOR_FROM_DATABASE=TXIC + +pci:v00004680* + ID_VENDOR_FROM_DATABASE=Umax Computer Corp + +pci:v00004843* + ID_VENDOR_FROM_DATABASE=Hercules Computer Technology Inc + +pci:v00004916* + ID_VENDOR_FROM_DATABASE=RedCreek Communications Inc + +pci:v00004916d00001960* + ID_MODEL_FROM_DATABASE=RedCreek PCI adapter + +pci:v00004943* + ID_VENDOR_FROM_DATABASE=Growth Networks + +pci:v0000494F* + ID_VENDOR_FROM_DATABASE=ACCES I/O Products, Inc. + +pci:v0000494Fd00000508* + ID_MODEL_FROM_DATABASE=PCI-IDO-16A FET Output Card + +pci:v0000494Fd00000518* + ID_MODEL_FROM_DATABASE=PCI-IDO-32A FET Output Card + +pci:v0000494Fd00000520* + ID_MODEL_FROM_DATABASE=PCI-IDO-48 FET Output Card + +pci:v0000494Fd00000521* + ID_MODEL_FROM_DATABASE=PCI-IDO-48A FET Output Card + +pci:v0000494Fd00000703* + ID_MODEL_FROM_DATABASE=PCIe-RO-4 Electromechanical Relay Output Card + +pci:v0000494Fd000007D0* + ID_MODEL_FROM_DATABASE=PCIe-IDO-24 FET Output Card + +pci:v0000494Fd00000920* + ID_MODEL_FROM_DATABASE=PCI-IDI-48 Isolated Digital Input Card + +pci:v0000494Fd00000BD0* + ID_MODEL_FROM_DATABASE=PCIe-IDI-24 Isolated Digital Input Card + +pci:v0000494Fd00000C50* + ID_MODEL_FROM_DATABASE=PCI-DIO-24H 1x 8255 Digital Input / Output Card + +pci:v0000494Fd00000C51* + ID_MODEL_FROM_DATABASE=PCI-DIO-24D 1x 8255 Digital Input / Output Card + +pci:v0000494Fd00000C52* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24 1x 8255 Digital Input / Output Card + +pci:v0000494Fd00000C53* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24H 8255 Digital Input / Output Card + +pci:v0000494Fd00000C57* + ID_MODEL_FROM_DATABASE=mPCIe-DIO-24 8255 Digital Input / Output Card + +pci:v0000494Fd00000C60* + ID_MODEL_FROM_DATABASE=PCI-DIO-48H 8255 Digital Input / Output Card + +pci:v0000494Fd00000C61* + ID_MODEL_FROM_DATABASE=PCIe-DIO-48 8255 Digital Input / Output Card + +pci:v0000494Fd00000C62* + ID_MODEL_FROM_DATABASE=P104-DIO-48 8255 Digital Input / Output Card + +pci:v0000494Fd00000C68* + ID_MODEL_FROM_DATABASE=PCI-DIO-72 8255 Digital Input / Output Card + +pci:v0000494Fd00000C69* + ID_MODEL_FROM_DATABASE=P104-DIO-96 8255 Digital Input / Output Card + +pci:v0000494Fd00000C70* + ID_MODEL_FROM_DATABASE=PCI-DIO-96 8255 Digital Input / Output Card + +pci:v0000494Fd00000C78* + ID_MODEL_FROM_DATABASE=PCI-DIO-120 8255 Digital Input / Output Card + +pci:v0000494Fd00000DC8* + ID_MODEL_FROM_DATABASE=PCI-IDIO-16 Isolated Digital Input / FET Output Card + +pci:v0000494Fd00000E50* + ID_MODEL_FROM_DATABASE=PCI-DIO-24S 8255 Digital Input / Output Card + +pci:v0000494Fd00000E51* + ID_MODEL_FROM_DATABASE=PCI-DIO-24H(C) 8255 Digital Input / Output Card + +pci:v0000494Fd00000E52* + ID_MODEL_FROM_DATABASE=PCI-DIO-24D(C) 8255 Digital Input / Output Card + +pci:v0000494Fd00000E53* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24S 8255 Digital Input / Output Card + +pci:v0000494Fd00000E54* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24HS 8255 Digital Input / Output Card + +pci:v0000494Fd00000E55* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24DC 8255 Digital Input / Output Card + +pci:v0000494Fd00000E56* + ID_MODEL_FROM_DATABASE=PCIe-DIO-24DCS 8255 Digital Input / Output Card + +pci:v0000494Fd00000E57* + ID_MODEL_FROM_DATABASE=mPCIe-DIO-24S 8255 Digital Input / Output Card + +pci:v0000494Fd00000E60* + ID_MODEL_FROM_DATABASE=PCI-DIO-48S 2x 8255 Digital Input / Output Card + +pci:v0000494Fd00000E61* + ID_MODEL_FROM_DATABASE=PCIe-DIO-48S 2x 8255 Digital Input / Output Card + +pci:v0000494Fd00000E62* + ID_MODEL_FROM_DATABASE=P104-DIO-48S 2x 8255 Digital Input / Output Card + +pci:v0000494Fd00000F00* + ID_MODEL_FROM_DATABASE=PCI-IIRO-8 Isolated Digital / Relay Output Card + +pci:v0000494Fd00000F01* + ID_MODEL_FROM_DATABASE=LPCI-IIRO-8 Isolated Digital / Relay Output Card + +pci:v0000494Fd00000F02* + ID_MODEL_FROM_DATABASE=PCIe-IIRO-8 Isolated Digital / Relay Output Card + +pci:v0000494Fd00000F08* + ID_MODEL_FROM_DATABASE=PCI-IIRO-16 Isolated Digital / Relay Output Card + +pci:v0000494Fd00000F09* + ID_MODEL_FROM_DATABASE=PCIe-IIRO-16 Isolated Digital / Relay Output Card + +pci:v0000494Fd00000FC0* + ID_MODEL_FROM_DATABASE=PCIe-IDIO-12 Isolated Digital Input / FET Output Card + +pci:v0000494Fd00000FC1* + ID_MODEL_FROM_DATABASE=PCIe-IDI-12 Isolated Digital Input Card + +pci:v0000494Fd00000FC2* + ID_MODEL_FROM_DATABASE=PCIe-IDO-12 FET Output Card + +pci:v0000494Fd00000FD0* + ID_MODEL_FROM_DATABASE=PCIe-IDIO-24 Isolated Digital Input / FET Output Card + +pci:v0000494Fd00001050* + ID_MODEL_FROM_DATABASE=PCI-422/485-2 2x RS422/RS484 Card + +pci:v0000494Fd00001051* + ID_MODEL_FROM_DATABASE=PCIe-COM-2SRJ 2x RS422/RS484 Card w/RJ45 Connectors + +pci:v0000494Fd00001052* + ID_MODEL_FROM_DATABASE=104I-COM-2S 2x RS422/RS484 PCI/104 Board + +pci:v0000494Fd00001053* + ID_MODEL_FROM_DATABASE=mPCIe-COM-2S 2x RS422/RS484 PCI Express Mini Card + +pci:v0000494Fd00001058* + ID_MODEL_FROM_DATABASE=PCI-COM422/4 4x RS422 Card + +pci:v0000494Fd00001059* + ID_MODEL_FROM_DATABASE=PCI-COM485/4 4x RS485 Card + +pci:v0000494Fd0000105A* + ID_MODEL_FROM_DATABASE=PCIe-COM422-4 4x RS422 Card + +pci:v0000494Fd0000105B* + ID_MODEL_FROM_DATABASE=PCIe-COM485-4 4x RS485 Card + +pci:v0000494Fd0000105C* + ID_MODEL_FROM_DATABASE=PCIe-COM-4SRJ 4x RS422/RS485 Card w/RJ45 Connectors + +pci:v0000494Fd0000105D* + ID_MODEL_FROM_DATABASE=104I-COM-4S 4x RS422/RS484 PCI/104 Board + +pci:v0000494Fd0000105E* + ID_MODEL_FROM_DATABASE=mPCIe-COM-4S 4x RS422/RS484 PCI Express Mini Card + +pci:v0000494Fd00001068* + ID_MODEL_FROM_DATABASE=PCI-COM422/8 8x RS422 Card + +pci:v0000494Fd00001069* + ID_MODEL_FROM_DATABASE=PCI-COM485/8 8x RS485 Card + +pci:v0000494Fd0000106A* + ID_MODEL_FROM_DATABASE=PCIe-COM422-8 8x RS422 Card + +pci:v0000494Fd0000106B* + ID_MODEL_FROM_DATABASE=PCIe-COM485-8 8x RS485 Card + +pci:v0000494Fd0000106C* + ID_MODEL_FROM_DATABASE=104I-COM-8S 8x RS422/RS485 PCI/104 Board + +pci:v0000494Fd00001088* + ID_MODEL_FROM_DATABASE=PCI-COM232/1 1x RS232 Card + +pci:v0000494Fd00001090* + ID_MODEL_FROM_DATABASE=PCI-COM232/2 2x RS232 Card + +pci:v0000494Fd00001091* + ID_MODEL_FROM_DATABASE=PCIe-COM232-2RJ 2x RS232 Card w/RJ45 Connectors + +pci:v0000494Fd00001093* + ID_MODEL_FROM_DATABASE=mPCIe-COM232-2 2x RS232 PCI Express Mini Card + +pci:v0000494Fd00001098* + ID_MODEL_FROM_DATABASE=PCIe-COM232-4 4x RS232 Card + +pci:v0000494Fd00001099* + ID_MODEL_FROM_DATABASE=PCIe-COM232-4RJ 4x RS232 Card w/RJ45 Connectors + +pci:v0000494Fd0000109B* + ID_MODEL_FROM_DATABASE=mPCIe-COM232-4 4x RS232 PCI Express Mini Card + +pci:v0000494Fd000010A8* + ID_MODEL_FROM_DATABASE=P104-COM232-8 8x RS232 PC-104+ Board + +pci:v0000494Fd000010A9* + ID_MODEL_FROM_DATABASE=PCIe-COM232-8 8x RS232 Card + +pci:v0000494Fd000010C9* + ID_MODEL_FROM_DATABASE=PCI-COM-1S 1x RS422/RS485 Card + +pci:v0000494Fd000010D0* + ID_MODEL_FROM_DATABASE=PCI-COM2S 2x RS422/RS485 Card + +pci:v0000494Fd000010D1* + ID_MODEL_FROM_DATABASE=PCIe-COM-2SMRJ 2x RS232/RS422/RS485 Card w/RJ45 Connectors + +pci:v0000494Fd000010D2* + ID_MODEL_FROM_DATABASE=104I-COM-2SM 2x RS232/RS422/RS485 PCI/104 Board + +pci:v0000494Fd000010D3* + ID_MODEL_FROM_DATABASE=mPCIe-COM-2SM 2x RS232/RS422/RS485 PCI Express Mini Card + +pci:v0000494Fd000010D8* + ID_MODEL_FROM_DATABASE=PCI-COM-4SM 4x RS232/RS422/RS485 Card + +pci:v0000494Fd000010D9* + ID_MODEL_FROM_DATABASE=PCIe-COM-4SM 4x RS232/RS422/RS485 Card + +pci:v0000494Fd000010DA* + ID_MODEL_FROM_DATABASE=PCIe-COM-4SMRJ 4x RS232/RS422/RS485 Card w/RJ45 Connectors + +pci:v0000494Fd000010DB* + ID_MODEL_FROM_DATABASE=104I-COM-4SM 4x RS232/RS422/RS485 PCI/104 Board + +pci:v0000494Fd000010DC* + ID_MODEL_FROM_DATABASE=mPCIe-COM-4SM 4x RS232/RS422/RS485 PCI Express Mini Card + +pci:v0000494Fd000010E8* + ID_MODEL_FROM_DATABASE=PCI-COM-8SM 8x RS232/RS422/RS485 Card + +pci:v0000494Fd000010E9* + ID_MODEL_FROM_DATABASE=PCIe-COM-8SM 8x RS232/RS422/RS485 Card + +pci:v0000494Fd000010EA* + ID_MODEL_FROM_DATABASE=104I-COM-8SM 8x RS232/RS422/RS485 PCI-104 Board + +pci:v0000494Fd00001108* + ID_MODEL_FROM_DATABASE=mPCIe-ICM485-1 1x Isolated RS485 PCI Express Mini Card + +pci:v0000494Fd00001110* + ID_MODEL_FROM_DATABASE=mPCIe-ICM422-2 2x Isolated RS422 PCI Express Mini Card + +pci:v0000494Fd00001111* + ID_MODEL_FROM_DATABASE=mPCIe-ICM485-2 2x Isolated RS485 PCI Express Mini Card + +pci:v0000494Fd00001118* + ID_MODEL_FROM_DATABASE=mPCIe-ICM422-4 4x Isolated RS422 PCI Express Mini Card + +pci:v0000494Fd00001119* + ID_MODEL_FROM_DATABASE=mPCIe-ICM485-4 4x Isolated RS485 PCI Express Mini Card + +pci:v0000494Fd00001148* + ID_MODEL_FROM_DATABASE=PCI-ICM-1S 1x Isolated RS422/RS485 Card + +pci:v0000494Fd00001150* + ID_MODEL_FROM_DATABASE=PCI-ICM-2S 2x Isolated RS422/RS485 Card + +pci:v0000494Fd00001152* + ID_MODEL_FROM_DATABASE=PCIe-ICM-2S 2x Isolated RS422/RS485 Card + +pci:v0000494Fd00001158* + ID_MODEL_FROM_DATABASE=PCI-ICM422/4 4x Isolated RS422 Card + +pci:v0000494Fd00001159* + ID_MODEL_FROM_DATABASE=PCI-ICM485/4 4x Isolated RS485 Card + +pci:v0000494Fd0000115A* + ID_MODEL_FROM_DATABASE=PCIe-ICM-4S 4x Isolated RS422/RS485 Card + +pci:v0000494Fd00001190* + ID_MODEL_FROM_DATABASE=PCIe-ICM232-2 2x Isolated RS232 Card + +pci:v0000494Fd00001191* + ID_MODEL_FROM_DATABASE=mPCIe-ICM232-2 2x Isolated RS232 PCI Express Mini Card + +pci:v0000494Fd00001198* + ID_MODEL_FROM_DATABASE=PCIe-ICM232-4 4x Isolated RS232 Card + +pci:v0000494Fd00001199* + ID_MODEL_FROM_DATABASE=mPCIe-ICM232-4 4x Isolated RS422 PCI Express Mini Card + +pci:v0000494Fd000011D0* + ID_MODEL_FROM_DATABASE=PCIe-ICM-2SM 2x Isolated RS232/RS422/RS485 Card + +pci:v0000494Fd000011D8* + ID_MODEL_FROM_DATABASE=PCIe-ICM-4SM 4x Isolated RS232/RS422/RS485 Card + +pci:v0000494Fd00001250* + ID_MODEL_FROM_DATABASE=PCI-WDG-2S Watchdog and 2x Serial Card + +pci:v0000494Fd000012D0* + ID_MODEL_FROM_DATABASE=PCI-WDG-IMPAC + +pci:v0000494Fd00002230* + ID_MODEL_FROM_DATABASE=PCI-QUAD-8 8x Quadrature Input Card + +pci:v0000494Fd00002231* + ID_MODEL_FROM_DATABASE=PCI-QUAD-4 4x Quadrature Input Card + +pci:v0000494Fd000022C0* + ID_MODEL_FROM_DATABASE=PCI-WDG-CSM Watchdog Card + +pci:v0000494Fd000025C0* + ID_MODEL_FROM_DATABASE=P104-WDG-E Watchdog PC/104+ Board + +pci:v0000494Fd00002C50* + ID_MODEL_FROM_DATABASE=PCI-DIO-96CT 96x Digital Input / Output Card + +pci:v0000494Fd00002C58* + ID_MODEL_FROM_DATABASE=PCI-DIO-96C3 96x Digital Input / Output Card w/3x 8254 Counter Card + +pci:v0000494Fd00002EE0* + ID_MODEL_FROM_DATABASE=PCIe-DIO24S-CTR12 24x Digital Input / Output Card w/4x 8254 Counter Card + +pci:v0000494Fd00002FC0* + ID_MODEL_FROM_DATABASE=P104-WDG-CSM Watchdog PC/104+ Board + +pci:v0000494Fd00002FC1* + ID_MODEL_FROM_DATABASE=P104-WDG-CSMA Advanced Watchdog PC/104+ Board + +pci:v0000494Fd00005ED0* + ID_MODEL_FROM_DATABASE=PCI-DAC + +pci:v0000494Fd00006C90* + ID_MODEL_FROM_DATABASE=PCI-DA12-2 2x 12-bit Analog Output Card + +pci:v0000494Fd00006C98* + ID_MODEL_FROM_DATABASE=PCI-DA12-4 4x 12-bit Analog Output Card + +pci:v0000494Fd00006CA0* + ID_MODEL_FROM_DATABASE=PCI-DA12-6 6x 12-bit Analog Output Card + +pci:v0000494Fd00006CA8* + ID_MODEL_FROM_DATABASE=PCI-DA12-8 8x 12-bit Analog Output Card + +pci:v0000494Fd00006CA9* + ID_MODEL_FROM_DATABASE=PCI-DA12-8V + +pci:v0000494Fd00006CB0* + ID_MODEL_FROM_DATABASE=PCI-DA12-16 16x 12-bit Analog Output Card + +pci:v0000494Fd00006CB1* + ID_MODEL_FROM_DATABASE=PCI-DA12-16V + +pci:v0000494Fd00008EF0* + ID_MODEL_FROM_DATABASE=P104-FAS16-16 + +pci:v0000494Fd0000ACA8* + ID_MODEL_FROM_DATABASE=PCI-AI12-16 12-bit 100kHz Analog Input Card + +pci:v0000494Fd0000ACA9* + ID_MODEL_FROM_DATABASE=PCI-AI12-16A 12-bit 100kHz Analog Input w/FIFO Card + +pci:v0000494Fd0000ECA8* + ID_MODEL_FROM_DATABASE=PCI-AIO12-16 12-bit 100kHz Analog Input w/2x Analog Output and FIFO Card + +pci:v0000494Fd0000ECAA* + ID_MODEL_FROM_DATABASE=PCI-A12-16A 12-bit 100kHz Analog Input w/2x Analog Output and FIFO Card + +pci:v0000494Fd0000ECE8* + ID_MODEL_FROM_DATABASE=LPCI-A16-16A 16-bit 500kHz Analog Input low-profile Card + +pci:v0000494Fd0000ECE9* + ID_MODEL_FROM_DATABASE=LPCI-AIO16A 16-bit 500kHz Analog Input low-profile Card + +pci:v00004978* + ID_VENDOR_FROM_DATABASE=Axil Computer Inc + +pci:v00004A14* + ID_VENDOR_FROM_DATABASE=NetVin + +pci:v00004A14d00005000* + ID_MODEL_FROM_DATABASE=NV5000SC + +pci:v00004A14d00005000sv00004A14sd00005000* + ID_MODEL_FROM_DATABASE=NV5000SC (RT8029-Based Ethernet Adapter) + +pci:v00004B10* + ID_VENDOR_FROM_DATABASE=Buslogic Inc. + +pci:v00004C48* + ID_VENDOR_FROM_DATABASE=LUNG HWA Electronics + +pci:v00004C53* + ID_VENDOR_FROM_DATABASE=SBS Technologies + +pci:v00004C53d00000000* + ID_MODEL_FROM_DATABASE=PLUSTEST device + +pci:v00004C53d00000000sv00004C53sd00003000* + ID_MODEL_FROM_DATABASE=PLUSTEST device (PLUSTEST card (PC104+)) + +pci:v00004C53d00000000sv00004C53sd00003001* + ID_MODEL_FROM_DATABASE=PLUSTEST device (PLUSTEST card (PMC)) + +pci:v00004C53d00000001* + ID_MODEL_FROM_DATABASE=PLUSTEST-MM device + +pci:v00004C53d00000001sv00004C53sd00003002* + ID_MODEL_FROM_DATABASE=PLUSTEST-MM device (PLUSTEST-MM card (PMC)) + +pci:v00004CA1* + ID_VENDOR_FROM_DATABASE=Seanix Technology Inc + +pci:v00004D51* + ID_VENDOR_FROM_DATABASE=MediaQ Inc. + +pci:v00004D51d00000200* + ID_MODEL_FROM_DATABASE=MQ-200 + +pci:v00004D54* + ID_VENDOR_FROM_DATABASE=Microtechnica Co Ltd + +pci:v00004D56* + ID_VENDOR_FROM_DATABASE=MATRIX VISION GmbH + +pci:v00004D56d00000000* + ID_MODEL_FROM_DATABASE=[mvHYPERION-CLe/CLb] CameraLink PCI Express x1 Frame Grabber + +pci:v00004D56d00000001* + ID_MODEL_FROM_DATABASE=[mvHYPERION-CLf/CLm] CameraLink PCI Express x4 Frame Grabber + +pci:v00004D56d00000010* + ID_MODEL_FROM_DATABASE=[mvHYPERION-16R16/-32R16] 16 Video Channel PCI Express x4 Frame Grabber + +pci:v00004D56d00000020* + ID_MODEL_FROM_DATABASE=[mvHYPERION-HD-SDI] HD-SDI PCI Express x4 Frame Grabber + +pci:v00004D56d00000030* + ID_MODEL_FROM_DATABASE=[mvHYPERION-HD-SDI-Merger] HD-SDI PCI Express x4 Frame Grabber + +pci:v00004DDC* + ID_VENDOR_FROM_DATABASE=ILC Data Device Corp + +pci:v00004DDCd00000100* + ID_MODEL_FROM_DATABASE=DD-42924I5-300 (ARINC 429 Data Bus) + +pci:v00004DDCd00000801* + ID_MODEL_FROM_DATABASE=BU-65570I1 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000802* + ID_MODEL_FROM_DATABASE=BU-65570I2 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000811* + ID_MODEL_FROM_DATABASE=BU-65572I1 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000812* + ID_MODEL_FROM_DATABASE=BU-65572I2 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000881* + ID_MODEL_FROM_DATABASE=BU-65570T1 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000882* + ID_MODEL_FROM_DATABASE=BU-65570T2 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000891* + ID_MODEL_FROM_DATABASE=BU-65572T1 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000892* + ID_MODEL_FROM_DATABASE=BU-65572T2 MIL-STD-1553 Test and Simulation + +pci:v00004DDCd00000901* + ID_MODEL_FROM_DATABASE=BU-65565C1 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000902* + ID_MODEL_FROM_DATABASE=BU-65565C2 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000903* + ID_MODEL_FROM_DATABASE=BU-65565C3 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000904* + ID_MODEL_FROM_DATABASE=BU-65565C4 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000B01* + ID_MODEL_FROM_DATABASE=BU-65569I1 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000B02* + ID_MODEL_FROM_DATABASE=BU-65569I2 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000B03* + ID_MODEL_FROM_DATABASE=BU-65569I3 MIL-STD-1553 Data Bus + +pci:v00004DDCd00000B04* + ID_MODEL_FROM_DATABASE=BU-65569I4 MIL-STD-1553 Data Bus + +pci:v00005045* + ID_VENDOR_FROM_DATABASE=University of Toronto + +pci:v00005045d00004243* + ID_MODEL_FROM_DATABASE=BLASTbus PCI Interface Card v1 + +pci:v00005046* + ID_VENDOR_FROM_DATABASE=GemTek Technology Corporation + +pci:v00005046d00001001* + ID_MODEL_FROM_DATABASE=PCI Radio + +pci:v00005053* + ID_VENDOR_FROM_DATABASE=Voyetra Technologies + +pci:v00005053d00002010* + ID_MODEL_FROM_DATABASE=Daytona Audio Adapter + +pci:v000050B2* + ID_VENDOR_FROM_DATABASE=TerraTec Electronic GmbH + +pci:v00005136* + ID_VENDOR_FROM_DATABASE=S S Technologies + +pci:v00005143* + ID_VENDOR_FROM_DATABASE=Qualcomm Inc + +pci:v00005145* + ID_VENDOR_FROM_DATABASE=Ensoniq (Old) + +pci:v00005145d00003031* + ID_MODEL_FROM_DATABASE=Concert AudioPCI + +pci:v00005168* + ID_VENDOR_FROM_DATABASE=Animation Technologies Inc. + +pci:v00005168d00000300* + ID_MODEL_FROM_DATABASE=FlyDVB-S + +pci:v00005168d00000301* + ID_MODEL_FROM_DATABASE=FlyDVB-T + +pci:v00005301* + ID_VENDOR_FROM_DATABASE=Alliance Semiconductor Corp. + +pci:v00005301d00000001* + ID_MODEL_FROM_DATABASE=ProMotion aT3D + +pci:v00005333* + ID_VENDOR_FROM_DATABASE=S3 Graphics Ltd. + +pci:v00005333d00000551* + ID_MODEL_FROM_DATABASE=Plato/PX (system) + +pci:v00005333d00005631* + ID_MODEL_FROM_DATABASE=86c325 [ViRGE] + +pci:v00005333d00008800* + ID_MODEL_FROM_DATABASE=86c866 [Vision 866] + +pci:v00005333d00008801* + ID_MODEL_FROM_DATABASE=86c964 [Vision 964] + +pci:v00005333d00008810* + ID_MODEL_FROM_DATABASE=86c764_0 [Trio 32 vers 0] + +pci:v00005333d00008811* + ID_MODEL_FROM_DATABASE=86c764/765 [Trio32/64/64V+] + +pci:v00005333d00008812* + ID_MODEL_FROM_DATABASE=86cM65 [Aurora64V+] + +pci:v00005333d00008813* + ID_MODEL_FROM_DATABASE=86c764_3 [Trio 32/64 vers 3] + +pci:v00005333d00008814* + ID_MODEL_FROM_DATABASE=86c767 [Trio 64UV+] + +pci:v00005333d00008815* + ID_MODEL_FROM_DATABASE=86cM65 [Aurora 128] + +pci:v00005333d0000883D* + ID_MODEL_FROM_DATABASE=86c988 [ViRGE/VX] + +pci:v00005333d00008870* + ID_MODEL_FROM_DATABASE=FireGL + +pci:v00005333d00008880* + ID_MODEL_FROM_DATABASE=86c868 [Vision 868 VRAM] vers 0 + +pci:v00005333d00008881* + ID_MODEL_FROM_DATABASE=86c868 [Vision 868 VRAM] vers 1 + +pci:v00005333d00008882* + ID_MODEL_FROM_DATABASE=86c868 [Vision 868 VRAM] vers 2 + +pci:v00005333d00008883* + ID_MODEL_FROM_DATABASE=86c868 [Vision 868 VRAM] vers 3 + +pci:v00005333d000088B0* + ID_MODEL_FROM_DATABASE=86c928 [Vision 928 VRAM] vers 0 + +pci:v00005333d000088B1* + ID_MODEL_FROM_DATABASE=86c928 [Vision 928 VRAM] vers 1 + +pci:v00005333d000088B2* + ID_MODEL_FROM_DATABASE=86c928 [Vision 928 VRAM] vers 2 + +pci:v00005333d000088B3* + ID_MODEL_FROM_DATABASE=86c928 [Vision 928 VRAM] vers 3 + +pci:v00005333d000088C0* + ID_MODEL_FROM_DATABASE=86c864 [Vision 864 DRAM] vers 0 + +pci:v00005333d000088C1* + ID_MODEL_FROM_DATABASE=86c864 [Vision 864 DRAM] vers 1 + +pci:v00005333d000088C2* + ID_MODEL_FROM_DATABASE=86c864 [Vision 864-P DRAM] vers 2 + +pci:v00005333d000088C3* + ID_MODEL_FROM_DATABASE=86c864 [Vision 864-P DRAM] vers 3 + +pci:v00005333d000088D0* + ID_MODEL_FROM_DATABASE=86c964 [Vision 964 VRAM] vers 0 + +pci:v00005333d000088D1* + ID_MODEL_FROM_DATABASE=86c964 [Vision 964 VRAM] vers 1 + +pci:v00005333d000088D2* + ID_MODEL_FROM_DATABASE=86c964 [Vision 964-P VRAM] vers 2 + +pci:v00005333d000088D3* + ID_MODEL_FROM_DATABASE=86c964 [Vision 964-P VRAM] vers 3 + +pci:v00005333d000088F0* + ID_MODEL_FROM_DATABASE=86c968 [Vision 968 VRAM] rev 0 + +pci:v00005333d000088F1* + ID_MODEL_FROM_DATABASE=86c968 [Vision 968 VRAM] rev 1 + +pci:v00005333d000088F2* + ID_MODEL_FROM_DATABASE=86c968 [Vision 968 VRAM] rev 2 + +pci:v00005333d000088F3* + ID_MODEL_FROM_DATABASE=86c968 [Vision 968 VRAM] rev 3 + +pci:v00005333d00008900* + ID_MODEL_FROM_DATABASE=86c755 [Trio 64V2/DX] + +pci:v00005333d00008900sv00005333sd00008900* + ID_MODEL_FROM_DATABASE=86c755 [Trio 64V2/DX] (86C775 Trio64V2/DX) + +pci:v00005333d00008901* + ID_MODEL_FROM_DATABASE=86c775/86c785 [Trio 64V2/DX or /GX] + +pci:v00005333d00008901sv00005333sd00008901* + ID_MODEL_FROM_DATABASE=86c775/86c785 [Trio 64V2/DX or /GX] (86C775 Trio64V2/DX, 86C785 Trio64V2/GX) + +pci:v00005333d00008902* + ID_MODEL_FROM_DATABASE=Plato/PX + +pci:v00005333d00008903* + ID_MODEL_FROM_DATABASE=Trio 3D business multimedia + +pci:v00005333d00008904* + ID_MODEL_FROM_DATABASE=86c365, 86c366 [Trio 3D] + +pci:v00005333d00008904sv00001014sd000000DB* + ID_MODEL_FROM_DATABASE=86c365, 86c366 [Trio 3D] (Integrated Trio3D) + +pci:v00005333d00008904sv00004843sd0000314A* + ID_MODEL_FROM_DATABASE=86c365, 86c366 [Trio 3D] (Terminator 128/3D GLH) + +pci:v00005333d00008904sv00005333sd00008904* + ID_MODEL_FROM_DATABASE=86c365, 86c366 [Trio 3D] (86C365 Trio3D AGP) + +pci:v00005333d00008905* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d00008906* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d00008907* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d00008908* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d00008909* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890A* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890B* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890C* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890D* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890E* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d0000890F* + ID_MODEL_FROM_DATABASE=Trio 64V+ family + +pci:v00005333d00008A01* + ID_MODEL_FROM_DATABASE=86c375 [ViRGE/DX] or 86c385 [ViRGE/GX] + +pci:v00005333d00008A01sv00000E11sd0000B032* + ID_MODEL_FROM_DATABASE=86c375 [ViRGE/DX] or 86c385 [ViRGE/GX] (ViRGE/GX) + +pci:v00005333d00008A01sv000010B4sd00001617* + ID_MODEL_FROM_DATABASE=86c375 [ViRGE/DX] or 86c385 [ViRGE/GX] (Nitro 3D) + +pci:v00005333d00008A01sv000010B4sd00001717* + ID_MODEL_FROM_DATABASE=86c375 [ViRGE/DX] or 86c385 [ViRGE/GX] (Nitro 3D) + +pci:v00005333d00008A01sv00005333sd00008A01* + ID_MODEL_FROM_DATABASE=86c375 [ViRGE/DX] or 86c385 [ViRGE/GX] (ViRGE/DX) + +pci:v00005333d00008A10* + ID_MODEL_FROM_DATABASE=ViRGE/GX2 + +pci:v00005333d00008A10sv00001092sd00008A10* + ID_MODEL_FROM_DATABASE=ViRGE/GX2 (Stealth 3D 4000) + +pci:v00005333d00008A13* + ID_MODEL_FROM_DATABASE=86c360 [Trio 3D/1X], 86c362, 86c368 [Trio 3D/2X] + +pci:v00005333d00008A13sv00005333sd00008A13* + ID_MODEL_FROM_DATABASE=86c360 [Trio 3D/1X], 86c362, 86c368 [Trio 3D/2X] (Trio3D/2X) + +pci:v00005333d00008A20* + ID_MODEL_FROM_DATABASE=86c794 [Savage 3D] + +pci:v00005333d00008A20sv00005333sd00008A20* + ID_MODEL_FROM_DATABASE=86c794 [Savage 3D] (86C391 Savage3D) + +pci:v00005333d00008A21* + ID_MODEL_FROM_DATABASE=86c390 [Savage 3D/MV] + +pci:v00005333d00008A21sv00005333sd00008A21* + ID_MODEL_FROM_DATABASE=86c390 [Savage 3D/MV] (86C390 Savage3D/MV) + +pci:v00005333d00008A22* + ID_MODEL_FROM_DATABASE=Savage 4 + +pci:v00005333d00008A22sv00001033sd00008068* + ID_MODEL_FROM_DATABASE=Savage 4 + +pci:v00005333d00008A22sv00001033sd00008069* + ID_MODEL_FROM_DATABASE=Savage 4 + +pci:v00005333d00008A22sv00001033sd00008110* + ID_MODEL_FROM_DATABASE=Savage 4 (LT) + +pci:v00005333d00008A22sv0000105Dsd00000018* + ID_MODEL_FROM_DATABASE=Savage 4 (SR9 8Mb SDRAM) + +pci:v00005333d00008A22sv0000105Dsd0000002A* + ID_MODEL_FROM_DATABASE=Savage 4 (SR9 Pro 16Mb SDRAM) + +pci:v00005333d00008A22sv0000105Dsd0000003A* + ID_MODEL_FROM_DATABASE=Savage 4 (SR9 Pro 32Mb SDRAM) + +pci:v00005333d00008A22sv0000105Dsd0000092F* + ID_MODEL_FROM_DATABASE=Savage 4 (SR9 Pro+ 16Mb SGRAM) + +pci:v00005333d00008A22sv00001092sd00004207* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004800* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004807* + ID_MODEL_FROM_DATABASE=Savage 4 (SpeedStar A90) + +pci:v00005333d00008A22sv00001092sd00004808* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004809* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd0000480E* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004904* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S520) + +pci:v00005333d00008A22sv00001092sd00004905* + ID_MODEL_FROM_DATABASE=Savage 4 (SpeedStar A200) + +pci:v00005333d00008A22sv00001092sd00004A09* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004A0B* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540 Xtreme) + +pci:v00005333d00008A22sv00001092sd00004A0F* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001092sd00004E01* + ID_MODEL_FROM_DATABASE=Savage 4 (Stealth III S540) + +pci:v00005333d00008A22sv00001102sd0000101D* + ID_MODEL_FROM_DATABASE=Savage 4 (3d Blaster Savage 4) + +pci:v00005333d00008A22sv00001102sd0000101E* + ID_MODEL_FROM_DATABASE=Savage 4 (3d Blaster Savage 4) + +pci:v00005333d00008A22sv00005333sd00008100* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SDRAM 100) + +pci:v00005333d00008A22sv00005333sd00008110* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SDRAM 110) + +pci:v00005333d00008A22sv00005333sd00008125* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SDRAM 125) + +pci:v00005333d00008A22sv00005333sd00008143* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SDRAM 143) + +pci:v00005333d00008A22sv00005333sd00008A22* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4) + +pci:v00005333d00008A22sv00005333sd00008A2E* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 32bit) + +pci:v00005333d00008A22sv00005333sd00009125* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SGRAM 125) + +pci:v00005333d00008A22sv00005333sd00009143* + ID_MODEL_FROM_DATABASE=Savage 4 (86C394-397 Savage4 SGRAM 143) + +pci:v00005333d00008A23* + ID_MODEL_FROM_DATABASE=Savage 4 + +pci:v00005333d00008A25* + ID_MODEL_FROM_DATABASE=ProSavage PM133 + +pci:v00005333d00008A25sv00000303sd00000303* + ID_MODEL_FROM_DATABASE=ProSavage PM133 (D9840-60001 [Brio BA410 Motherboard]) + +pci:v00005333d00008A26* + ID_MODEL_FROM_DATABASE=ProSavage KM133 + +pci:v00005333d00008C00* + ID_MODEL_FROM_DATABASE=ViRGE/M3 + +pci:v00005333d00008C01* + ID_MODEL_FROM_DATABASE=ViRGE/MX + +pci:v00005333d00008C01sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=ViRGE/MX + +pci:v00005333d00008C02* + ID_MODEL_FROM_DATABASE=ViRGE/MX+ + +pci:v00005333d00008C03* + ID_MODEL_FROM_DATABASE=ViRGE/MX+MV + +pci:v00005333d00008C10* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageMX-MV] + +pci:v00005333d00008C11* + ID_MODEL_FROM_DATABASE=82C270-294 [SavageMX] + +pci:v00005333d00008C12* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageIX-MV] + +pci:v00005333d00008C12sv00001014sd0000017F* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageIX-MV] (ThinkPad T20/T22) + +pci:v00005333d00008C12sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageIX-MV] (86C584 SuperSavage/IXC Toshiba) + +pci:v00005333d00008C13* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageIX] + +pci:v00005333d00008C13sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=86C270-294 [SavageIX] (Magnia Z310) + +pci:v00005333d00008C22* + ID_MODEL_FROM_DATABASE=SuperSavage MX/128 + +pci:v00005333d00008C24* + ID_MODEL_FROM_DATABASE=SuperSavage MX/64 + +pci:v00005333d00008C26* + ID_MODEL_FROM_DATABASE=SuperSavage MX/64C + +pci:v00005333d00008C2A* + ID_MODEL_FROM_DATABASE=SuperSavage IX/128 SDR + +pci:v00005333d00008C2B* + ID_MODEL_FROM_DATABASE=SuperSavage IX/128 DDR + +pci:v00005333d00008C2C* + ID_MODEL_FROM_DATABASE=SuperSavage IX/64 SDR + +pci:v00005333d00008C2D* + ID_MODEL_FROM_DATABASE=SuperSavage IX/64 DDR + +pci:v00005333d00008C2E* + ID_MODEL_FROM_DATABASE=SuperSavage IX/C SDR + +pci:v00005333d00008C2Esv00001014sd000001FC* + ID_MODEL_FROM_DATABASE=SuperSavage IX/C SDR (ThinkPad T23) + +pci:v00005333d00008C2F* + ID_MODEL_FROM_DATABASE=SuperSavage IX/C DDR + +pci:v00005333d00008D01* + ID_MODEL_FROM_DATABASE=86C380 [ProSavageDDR K4M266] + +pci:v00005333d00008D02* + ID_MODEL_FROM_DATABASE=VT8636A [ProSavage KN133] AGP4X VGA Controller (TwisterK) + +pci:v00005333d00008D03* + ID_MODEL_FROM_DATABASE=VT8751 [ProSavageDDR P4M266] + +pci:v00005333d00008D04* + ID_MODEL_FROM_DATABASE=VT8375 [ProSavage8 KM266/KL266] + +pci:v00005333d00008E00* + ID_MODEL_FROM_DATABASE=DeltaChrome + +pci:v00005333d00008E26* + ID_MODEL_FROM_DATABASE=ProSavage + +pci:v00005333d00008E40* + ID_MODEL_FROM_DATABASE=2300E Graphics Processor + +pci:v00005333d00008E48* + ID_MODEL_FROM_DATABASE=Matrix [Chrome S25 / S27] + +pci:v00005333d00008E48sv00005333sd00000130* + ID_MODEL_FROM_DATABASE=Matrix [Chrome S25 / S27] (Chrome S27 256M DDR2) + +pci:v00005333d00009043* + ID_MODEL_FROM_DATABASE=Chrome 430 GT + +pci:v00005333d00009045* + ID_MODEL_FROM_DATABASE=Chrome 430 ULP / 435 ULP / 440 GTX + +pci:v00005333d00009060* + ID_MODEL_FROM_DATABASE=Chrome 530 GT + +pci:v00005333d00009102* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] + +pci:v00005333d00009102sv00001092sd00005932* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005934* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005952* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005954* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005A35* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005A37* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005A55* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d00009102sv00001092sd00005A57* + ID_MODEL_FROM_DATABASE=86C410 [Savage 2000] (Viper II Z200) + +pci:v00005333d0000CA00* + ID_MODEL_FROM_DATABASE=SonicVibes + +pci:v00005431* + ID_VENDOR_FROM_DATABASE=AuzenTech, Inc. + +pci:v0000544C* + ID_VENDOR_FROM_DATABASE=Teralogic Inc + +pci:v0000544Cd00000350* + ID_MODEL_FROM_DATABASE=TL880-based HDTV/ATSC tuner + +pci:v0000544D* + ID_VENDOR_FROM_DATABASE=TBS Technologies + +pci:v0000544Dd00006178* + ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card + +pci:v0000544Dd00006178sv0000544Dsd00006904* + ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6904 DVB-S2 Quad Tuner PCIe Card) + +pci:v0000544Dd00006178sv0000544Dsd00006905* + ID_MODEL_FROM_DATABASE=DVB-S2 4 Tuner PCIe Card (TBS6905 DVB-S2 Quad Tuner PCIe Card) + +pci:v00005452* + ID_VENDOR_FROM_DATABASE=SCANLAB AG + +pci:v00005452d00003443* + ID_MODEL_FROM_DATABASE=RTC4 + +pci:v00005455* + ID_VENDOR_FROM_DATABASE=Technische University Berlin + +pci:v00005455d00004458* + ID_MODEL_FROM_DATABASE=S5933 + +pci:v00005456* + ID_VENDOR_FROM_DATABASE=GoTView + +pci:v00005519* + ID_VENDOR_FROM_DATABASE=Cnet Technologies, Inc. + +pci:v00005544* + ID_VENDOR_FROM_DATABASE=Dunord Technologies + +pci:v00005544d00000001* + ID_MODEL_FROM_DATABASE=I-30xx Scanner Interface + +pci:v00005555* + ID_VENDOR_FROM_DATABASE=Genroco, Inc + +pci:v00005555d00000003* + ID_MODEL_FROM_DATABASE=TURBOstor HFP-832 [HiPPI NIC] + +pci:v00005646* + ID_VENDOR_FROM_DATABASE=Vector Fabrics BV + +pci:v00005654* + ID_VENDOR_FROM_DATABASE=VoiceTronix Pty Ltd + +pci:v00005678* + ID_VENDOR_FROM_DATABASE=Dawicontrol Computersysteme GmbH + +pci:v00005700* + ID_VENDOR_FROM_DATABASE=Netpower + +pci:v0000584D* + ID_VENDOR_FROM_DATABASE=AuzenTech Co., Ltd. + +pci:v00005851* + ID_VENDOR_FROM_DATABASE=Exacq Technologies + +pci:v00005851d00008008* + ID_MODEL_FROM_DATABASE=tDVR8008 8-port video capture card + +pci:v00005851d00008016* + ID_MODEL_FROM_DATABASE=tDVR8016 16-chan video capture card + +pci:v00005851d00008032* + ID_MODEL_FROM_DATABASE=tDVR8032 32-chan video capture card + +pci:v00005853* + ID_VENDOR_FROM_DATABASE=XenSource, Inc. + +pci:v00005853d00000001* + ID_MODEL_FROM_DATABASE=Xen Platform Device + +pci:v00005853d0000C000* + ID_MODEL_FROM_DATABASE=Citrix XenServer PCI Device for Windows Update + +pci:v00005853d0000C110* + ID_MODEL_FROM_DATABASE=Virtualized HID + +pci:v00005853d0000C147* + ID_MODEL_FROM_DATABASE=Virtualized Graphics Device + +pci:v00005854* + ID_VENDOR_FROM_DATABASE=GoTView + +pci:v00005ACE* + ID_VENDOR_FROM_DATABASE=Beholder International Ltd. + +pci:v0000631C* + ID_VENDOR_FROM_DATABASE=SmartInfra Ltd + +pci:v0000631Cd00001652* + ID_MODEL_FROM_DATABASE=PXI-1652 Signal Generator + +pci:v0000631Cd00002504* + ID_MODEL_FROM_DATABASE=PXI-2504 Signal Interrogator + +pci:v00006356* + ID_VENDOR_FROM_DATABASE=UltraStor + +pci:v00006374* + ID_VENDOR_FROM_DATABASE=c't Magazin fuer Computertechnik + +pci:v00006374d00006773* + ID_MODEL_FROM_DATABASE=GPPCI + +pci:v00006409* + ID_VENDOR_FROM_DATABASE=Logitec Corp. + +pci:v00006549* + ID_VENDOR_FROM_DATABASE=Teradici Corp. + +pci:v00006549d00001200* + ID_MODEL_FROM_DATABASE=TERA1200 PC-over-IP Host + +pci:v00006666* + ID_VENDOR_FROM_DATABASE=Decision Computer International Co. + +pci:v00006666d00000001* + ID_MODEL_FROM_DATABASE=PCCOM4 + +pci:v00006666d00000002* + ID_MODEL_FROM_DATABASE=PCCOM8 + +pci:v00006666d00000004* + ID_MODEL_FROM_DATABASE=PCCOM2 + +pci:v00006666d00000101* + ID_MODEL_FROM_DATABASE=PCI 8255/8254 I/O Card + +pci:v00006666d00000200* + ID_MODEL_FROM_DATABASE=12-bit AD/DA Card + +pci:v00006666d00000201* + ID_MODEL_FROM_DATABASE=14-bit AD/DA Card + +pci:v00006666d00001011* + ID_MODEL_FROM_DATABASE=Industrial Card + +pci:v00006666d00001021* + ID_MODEL_FROM_DATABASE=8 photo couple 8 relay Card + +pci:v00006666d00001022* + ID_MODEL_FROM_DATABASE=4 photo couple 4 relay Card + +pci:v00006666d00001025* + ID_MODEL_FROM_DATABASE=16 photo couple 16 relay Card + +pci:v00006666d00004000* + ID_MODEL_FROM_DATABASE=WatchDog Card + +pci:v00006688* + ID_VENDOR_FROM_DATABASE=Zycoo Co., Ltd + +pci:v00006688d00001200* + ID_MODEL_FROM_DATABASE=CooVox TDM Analog Module + +pci:v00006688d00001400* + ID_MODEL_FROM_DATABASE=CooVOX TDM GSM Module + +pci:v00006688d00001600* + ID_MODEL_FROM_DATABASE=CooVOX TDM E1/T1 Module + +pci:v00006688d00001800* + ID_MODEL_FROM_DATABASE=CooVOX TDM BRI Module + +pci:v00006900* + ID_VENDOR_FROM_DATABASE=Red Hat, Inc. + +pci:v00007063* + ID_VENDOR_FROM_DATABASE=pcHDTV + +pci:v00007063d00002000* + ID_MODEL_FROM_DATABASE=HD-2000 + +pci:v00007063d00003000* + ID_MODEL_FROM_DATABASE=HD-3000 + +pci:v00007063d00005500* + ID_MODEL_FROM_DATABASE=HD5500 HDTV + +pci:v00007284* + ID_VENDOR_FROM_DATABASE=HT OMEGA Inc. + +pci:v00007401* + ID_VENDOR_FROM_DATABASE=EndRun Technologies + +pci:v00007401d0000E100* + ID_MODEL_FROM_DATABASE=PTP3100 PCIe PTP Slave Clock + +pci:v00007470* + ID_VENDOR_FROM_DATABASE=TP-LINK Technologies Co., Ltd. + +pci:v00007604* + ID_VENDOR_FROM_DATABASE=O.N. Electronic Co Ltd. + +pci:v00007BDE* + ID_VENDOR_FROM_DATABASE=MIDAC Corporation + +pci:v00007FED* + ID_VENDOR_FROM_DATABASE=PowerTV + +pci:v00008008* + ID_VENDOR_FROM_DATABASE=Quancom Electronic GmbH + +pci:v00008008d00000010* + ID_MODEL_FROM_DATABASE=WDOG1 [PCI-Watchdog 1] + +pci:v00008008d00000011* + ID_MODEL_FROM_DATABASE=PWDOG2 [PCI-Watchdog 2] + +pci:v00008008d00000015* + ID_MODEL_FROM_DATABASE=Clock77/PCI & Clock77/PCIe (DCF-77 receiver) + +pci:v0000807D* + ID_VENDOR_FROM_DATABASE=Asustek Computer, Inc. + +pci:v00008086* + ID_VENDOR_FROM_DATABASE=Intel Corporation + +pci:v00008086d00000007* + ID_MODEL_FROM_DATABASE=82379AB + +pci:v00008086d00000008* + ID_MODEL_FROM_DATABASE=Extended Express System Support Controller + +pci:v00008086d00000039* + ID_MODEL_FROM_DATABASE=21145 Fast Ethernet + +pci:v00008086d00000040* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller + +pci:v00008086d00000041* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express x16 Root Port + +pci:v00008086d00000042* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller + +pci:v00008086d00000043* + ID_MODEL_FROM_DATABASE=Core Processor Secondary PCI Express Root Port + +pci:v00008086d00000044* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller + +pci:v00008086d00000044sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (Aspire 7740G) + +pci:v00008086d00000044sv00001025sd00000487* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (TravelMate 5742) + +pci:v00008086d00000044sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (Latitude E6410) + +pci:v00008086d00000044sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (R730 Laptop) + +pci:v00008086d00000044sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00000044sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller (PC1-GROOVE) + +pci:v00008086d00000045* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express x16 Root Port + +pci:v00008086d00000045sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express x16 Root Port (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00000046* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller + +pci:v00008086d00000046sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller (Latitude E6410) + +pci:v00008086d00000046sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller (R730 Laptop) + +pci:v00008086d00000046sv000017C0sd000010D9* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00000046sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller (PC1-GROOVE) + +pci:v00008086d00000047* + ID_MODEL_FROM_DATABASE=Core Processor Secondary PCI Express Root Port + +pci:v00008086d00000048* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller + +pci:v00008086d00000049* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express x16 Root Port + +pci:v00008086d0000004A* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Graphics Controller + +pci:v00008086d0000004B* + ID_MODEL_FROM_DATABASE=Core Processor Secondary PCI Express Root Port + +pci:v00008086d00000050* + ID_MODEL_FROM_DATABASE=Core Processor Thermal Management Controller + +pci:v00008086d00000069* + ID_MODEL_FROM_DATABASE=Core Processor DRAM Controller + +pci:v00008086d00000082* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] + +pci:v00008086d00000082sv00008086sd00001301* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 AGN) + +pci:v00008086d00000082sv00008086sd00001306* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 ABG) + +pci:v00008086d00000082sv00008086sd00001307* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 BG) + +pci:v00008086d00000082sv00008086sd00001321* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 AGN) + +pci:v00008086d00000082sv00008086sd00001326* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 ABG) + +pci:v00008086d00000083* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] + +pci:v00008086d00000083sv00008086sd00001205* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000083sv00008086sd00001206* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000083sv00008086sd00001225* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000083sv00008086sd00001226* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000083sv00008086sd00001305* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000083sv00008086sd00001306* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000083sv00008086sd00001325* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000083sv00008086sd00001326* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000084* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] + +pci:v00008086d00000084sv00008086sd00001215* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000084sv00008086sd00001216* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000084sv00008086sd00001315* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BGN) + +pci:v00008086d00000084sv00008086sd00001316* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1000 [Condor Peak] (Centrino Wireless-N 1000 BG) + +pci:v00008086d00000085* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] + +pci:v00008086d00000085sv00008086sd00001311* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 AGN) + +pci:v00008086d00000085sv00008086sd00001316* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6205 [Taylor Peak] (Centrino Advanced-N 6205 ABG) + +pci:v00008086d00000087* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] + +pci:v00008086d00000087sv00008086sd00001301* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 AGN) + +pci:v00008086d00000087sv00008086sd00001306* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 ABG) + +pci:v00008086d00000087sv00008086sd00001321* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 AGN) + +pci:v00008086d00000087sv00008086sd00001326* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 ABG) + +pci:v00008086d00000089* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] + +pci:v00008086d00000089sv00008086sd00001311* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 AGN) + +pci:v00008086d00000089sv00008086sd00001316* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N + WiMAX 6250 [Kilmer Peak] (Centrino Advanced-N + WiMAX 6250 2x2 ABG) + +pci:v00008086d0000008A* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] + +pci:v00008086d0000008Asv00008086sd00005305* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BGN) + +pci:v00008086d0000008Asv00008086sd00005307* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BG) + +pci:v00008086d0000008Asv00008086sd00005325* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BGN) + +pci:v00008086d0000008Asv00008086sd00005327* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BG) + +pci:v00008086d0000008B* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] + +pci:v00008086d0000008Bsv00008086sd00005315* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BGN) + +pci:v00008086d0000008Bsv00008086sd00005317* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 1030 [Rainbow Peak] (Centrino Wireless-N 1030 BG) + +pci:v00008086d00000090* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] + +pci:v00008086d00000090sv00008086sd00005211* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 AGN) + +pci:v00008086d00000090sv00008086sd00005215* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 BGN) + +pci:v00008086d00000090sv00008086sd00005216* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 ABG) + +pci:v00008086d00000091* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] + +pci:v00008086d00000091sv00008086sd00005201* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 AGN) + +pci:v00008086d00000091sv00008086sd00005205* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 BGN) + +pci:v00008086d00000091sv00008086sd00005206* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 ABG) + +pci:v00008086d00000091sv00008086sd00005207* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 BG) + +pci:v00008086d00000091sv00008086sd00005221* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 AGN) + +pci:v00008086d00000091sv00008086sd00005225* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 BGN) + +pci:v00008086d00000091sv00008086sd00005226* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6230 [Rainbow Peak] (Centrino Advanced-N 6230 ABG) + +pci:v00008086d00000100* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller + +pci:v00008086d00000100sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (XPS 8300) + +pci:v00008086d00000100sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (P8P67/P8H67 Series Motherboard) + +pci:v00008086d00000101* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port + +pci:v00008086d00000101sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port (Vostro 3350) + +pci:v00008086d00000101sv0000106Bsd000000DC* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port (MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00000102* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000102sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller (XPS 8300) + +pci:v00008086d00000102sv00001043sd00000102* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller (P8H67 Series Motherboard) + +pci:v00008086d00000104* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller + +pci:v00008086d00000104sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (Precision M4600) + +pci:v00008086d00000104sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (Vostro 3350) + +pci:v00008086d00000104sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (Vostro 3750) + +pci:v00008086d00000104sv0000106Bsd000000DC* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family DRAM Controller (MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00000105* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port + +pci:v00008086d00000105sv0000106Bsd000000DC* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port (MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00000106* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000108* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 Processor Family DRAM Controller + +pci:v00008086d00000109* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port + +pci:v00008086d0000010A* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 Processor Family Integrated Graphics Controller + +pci:v00008086d0000010B* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d0000010C* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family DRAM Controller + +pci:v00008086d0000010D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family PCI Express Root Port + +pci:v00008086d0000010E* + ID_MODEL_FROM_DATABASE=Xeon E3-1200/2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000112* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000116* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000116sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller (Vostro 3750) + +pci:v00008086d00000122* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000126* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000126sv00001028sd000004CC* + ID_MODEL_FROM_DATABASE=2nd Generation Core Processor Family Integrated Graphics Controller (Vostro 3350) + +pci:v00008086d00000150* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller + +pci:v00008086d00000150sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller (P8 series motherboard) + +pci:v00008086d00000150sv000015D9sd00000624* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller (X9SCM-F Motherboard) + +pci:v00008086d00000150sv00001849sd00000150* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller (Motherboard) + +pci:v00008086d00000151* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port + +pci:v00008086d00000151sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (N56VZ) + +pci:v00008086d00000151sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (P8 series motherboard) + +pci:v00008086d00000151sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (P8H77-I Motherboard) + +pci:v00008086d00000151sv00008086sd00002010* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (Server Board S1200BTS) + +pci:v00008086d00000152* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller + +pci:v00008086d00000152sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller (P8H77-I Motherboard) + +pci:v00008086d00000153* + ID_MODEL_FROM_DATABASE=3rd Gen Core Processor Thermal Subsystem + +pci:v00008086d00000153sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=3rd Gen Core Processor Thermal Subsystem (Zenbook Prime UX31A) + +pci:v00008086d00000154* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller + +pci:v00008086d00000154sv00001025sd00000806* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (Aspire E1-470G) + +pci:v00008086d00000154sv00001025sd00000813* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (Aspire R7-571) + +pci:v00008086d00000154sv0000103Csd000017F6* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (ProBook 4540s) + +pci:v00008086d00000154sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (VivoBook X202EV) + +pci:v00008086d00000154sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (N56VZ) + +pci:v00008086d00000154sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (Zenbook Prime UX31A) + +pci:v00008086d00000155* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port + +pci:v00008086d00000155sv00008086sd00002010* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (Server Board S1200BTS) + +pci:v00008086d00000156* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller + +pci:v00008086d00000156sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (VivoBook X202EV) + +pci:v00008086d00000158* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller + +pci:v00008086d00000158sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller (P8 series motherboard) + +pci:v00008086d00000158sv00008086sd00002010* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge DRAM Controller (Server Board S1200BTS) + +pci:v00008086d00000159* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port + +pci:v00008086d0000015A* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/Ivy Bridge Graphics Controller + +pci:v00008086d0000015C* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor DRAM Controller + +pci:v00008086d0000015D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port + +pci:v00008086d0000015Dsv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port (P8 series motherboard) + +pci:v00008086d0000015E* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller + +pci:v00008086d00000162* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller + +pci:v00008086d00000162sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller (P8 series motherboard) + +pci:v00008086d00000162sv00001849sd00000162* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller (Motherboard) + +pci:v00008086d00000166* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller + +pci:v00008086d00000166sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (Zenbook Prime UX31A) + +pci:v00008086d00000166sv00001043sd00002103* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (N56VZ) + +pci:v00008086d0000016A* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller + +pci:v00008086d0000016Asv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller (P8B WS Motherboard) + +pci:v00008086d00000172* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller + +pci:v00008086d00000176* + ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller + +pci:v00008086d00000309* + ID_MODEL_FROM_DATABASE=80303 I/O Processor PCI-to-PCI Bridge + +pci:v00008086d0000030D* + ID_MODEL_FROM_DATABASE=80312 I/O Companion Chip PCI-to-PCI Bridge + +pci:v00008086d00000326* + ID_MODEL_FROM_DATABASE=6700/6702PXH I/OxAPIC Interrupt Controller A + +pci:v00008086d00000326sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=6700/6702PXH I/OxAPIC Interrupt Controller A (ProLiant DL140 G2) + +pci:v00008086d00000326sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6700/6702PXH I/OxAPIC Interrupt Controller A (CR11/VR11 Single Board Computer) + +pci:v00008086d00000327* + ID_MODEL_FROM_DATABASE=6700PXH I/OxAPIC Interrupt Controller B + +pci:v00008086d00000327sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=6700PXH I/OxAPIC Interrupt Controller B (ProLiant DL140 G2) + +pci:v00008086d00000327sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6700PXH I/OxAPIC Interrupt Controller B (CR11/VR11 Single Board Computer) + +pci:v00008086d00000329* + ID_MODEL_FROM_DATABASE=6700PXH PCI Express-to-PCI Bridge A + +pci:v00008086d0000032A* + ID_MODEL_FROM_DATABASE=6700PXH PCI Express-to-PCI Bridge B + +pci:v00008086d0000032C* + ID_MODEL_FROM_DATABASE=6702PXH PCI Express-to-PCI Bridge A + +pci:v00008086d00000330* + ID_MODEL_FROM_DATABASE=80332 [Dobson] I/O processor (A-Segment Bridge) + +pci:v00008086d00000331* + ID_MODEL_FROM_DATABASE=80332 [Dobson] I/O processor (A-Segment IOAPIC) + +pci:v00008086d00000332* + ID_MODEL_FROM_DATABASE=80332 [Dobson] I/O processor (B-Segment Bridge) + +pci:v00008086d00000333* + ID_MODEL_FROM_DATABASE=80332 [Dobson] I/O processor (B-Segment IOAPIC) + +pci:v00008086d00000334* + ID_MODEL_FROM_DATABASE=80332 [Dobson] I/O processor (ATU) + +pci:v00008086d00000335* + ID_MODEL_FROM_DATABASE=80331 [Lindsay] I/O processor (PCI-X Bridge) + +pci:v00008086d00000336* + ID_MODEL_FROM_DATABASE=80331 [Lindsay] I/O processor (ATU) + +pci:v00008086d00000340* + ID_MODEL_FROM_DATABASE=41210 [Lanai] Serial to Parallel PCI Bridge (A-Segment Bridge) + +pci:v00008086d00000341* + ID_MODEL_FROM_DATABASE=41210 [Lanai] Serial to Parallel PCI Bridge (B-Segment Bridge) + +pci:v00008086d00000370* + ID_MODEL_FROM_DATABASE=80333 Segment-A PCIe Express to PCI-X bridge + +pci:v00008086d00000371* + ID_MODEL_FROM_DATABASE=80333 A-Bus IOAPIC + +pci:v00008086d00000372* + ID_MODEL_FROM_DATABASE=80333 Segment-B PCIe Express to PCI-X bridge + +pci:v00008086d00000373* + ID_MODEL_FROM_DATABASE=80333 B-Bus IOAPIC + +pci:v00008086d00000374* + ID_MODEL_FROM_DATABASE=80333 Address Translation Unit + +pci:v00008086d00000402* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller + +pci:v00008086d00000406* + ID_MODEL_FROM_DATABASE=4th Gen Core Processor Integrated Graphics Controller + +pci:v00008086d0000040A* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3 Processor Integrated Graphics Controller + +pci:v00008086d00000412* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller + +pci:v00008086d00000416* + ID_MODEL_FROM_DATABASE=4th Gen Core Processor Integrated Graphics Controller + +pci:v00008086d00000416sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=4th Gen Core Processor Integrated Graphics Controller (ThinkPad T440p) + +pci:v00008086d0000041A* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3 Processor Integrated Graphics Controller + +pci:v00008086d0000041E* + ID_MODEL_FROM_DATABASE=4th Generation Core Processor Family Integrated Graphics Controller + +pci:v00008086d00000434* + ID_MODEL_FROM_DATABASE=DH89XXCC Series QAT + +pci:v00008086d00000435* + ID_MODEL_FROM_DATABASE=DH895XCC Series QAT + +pci:v00008086d00000436* + ID_MODEL_FROM_DATABASE=DH8900CC Null Device + +pci:v00008086d00000438* + ID_MODEL_FROM_DATABASE=DH8900CC Series Gigabit Network Connection + +pci:v00008086d0000043A* + ID_MODEL_FROM_DATABASE=DH8900CC Series Gigabit Fiber Network Connection + +pci:v00008086d0000043C* + ID_MODEL_FROM_DATABASE=DH8900CC Series Gigabit Backplane Network Connection + +pci:v00008086d00000440* + ID_MODEL_FROM_DATABASE=DH8900CC Series Gigabit SFP Network Connection + +pci:v00008086d00000442* + ID_MODEL_FROM_DATABASE=DH89XXCC Series QAT Virtual Function + +pci:v00008086d00000443* + ID_MODEL_FROM_DATABASE=DH895XCC Series QAT Virtual Function + +pci:v00008086d00000482* + ID_MODEL_FROM_DATABASE=82375EB/SB PCI to EISA Bridge + +pci:v00008086d00000483* + ID_MODEL_FROM_DATABASE=82424TX/ZX [Saturn] CPU to PCI bridge + +pci:v00008086d00000484* + ID_MODEL_FROM_DATABASE=82378ZB/IB, 82379AB (SIO, SIO.A) PCI to ISA Bridge + +pci:v00008086d00000486* + ID_MODEL_FROM_DATABASE=82425EX/ZX [Aries] PCIset with ISA bridge + +pci:v00008086d000004A3* + ID_MODEL_FROM_DATABASE=82434LX/NX [Mercury/Neptune] Processor to PCI bridge + +pci:v00008086d000004D0* + ID_MODEL_FROM_DATABASE=82437FX [Triton FX] + +pci:v00008086d00000500* + ID_MODEL_FROM_DATABASE=E8870 Processor bus control + +pci:v00008086d00000501* + ID_MODEL_FROM_DATABASE=E8870 Memory controller + +pci:v00008086d00000502* + ID_MODEL_FROM_DATABASE=E8870 Scalability Port 0 + +pci:v00008086d00000503* + ID_MODEL_FROM_DATABASE=E8870 Scalability Port 1 + +pci:v00008086d00000510* + ID_MODEL_FROM_DATABASE=E8870IO Hub Interface Port 0 registers (8-bit compatibility port) + +pci:v00008086d00000511* + ID_MODEL_FROM_DATABASE=E8870IO Hub Interface Port 1 registers + +pci:v00008086d00000512* + ID_MODEL_FROM_DATABASE=E8870IO Hub Interface Port 2 registers + +pci:v00008086d00000513* + ID_MODEL_FROM_DATABASE=E8870IO Hub Interface Port 3 registers + +pci:v00008086d00000514* + ID_MODEL_FROM_DATABASE=E8870IO Hub Interface Port 4 registers + +pci:v00008086d00000515* + ID_MODEL_FROM_DATABASE=E8870IO General SIOH registers + +pci:v00008086d00000516* + ID_MODEL_FROM_DATABASE=E8870IO RAS registers + +pci:v00008086d00000530* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 0 registers + +pci:v00008086d00000531* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 1 registers + +pci:v00008086d00000532* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 2 registers + +pci:v00008086d00000533* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 3 registers + +pci:v00008086d00000534* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 4 registers + +pci:v00008086d00000535* + ID_MODEL_FROM_DATABASE=E8870SP Scalability Port 5 registers + +pci:v00008086d00000536* + ID_MODEL_FROM_DATABASE=E8870SP Interleave registers 0 and 1 + +pci:v00008086d00000537* + ID_MODEL_FROM_DATABASE=E8870SP Interleave registers 2 and 3 + +pci:v00008086d00000600* + ID_MODEL_FROM_DATABASE=RAID Controller + +pci:v00008086d00000600sv00008086sd00000136* + ID_MODEL_FROM_DATABASE=RAID Controller (SRCU31L) + +pci:v00008086d00000600sv00008086sd000001AF* + ID_MODEL_FROM_DATABASE=RAID Controller (SRCZCR) + +pci:v00008086d00000600sv00008086sd000001C1* + ID_MODEL_FROM_DATABASE=RAID Controller (ICP Vortex GDT8546RZ) + +pci:v00008086d00000600sv00008086sd000001F7* + ID_MODEL_FROM_DATABASE=RAID Controller (SCRU32) + +pci:v00008086d0000061F* + ID_MODEL_FROM_DATABASE=80303 I/O Processor + +pci:v00008086d00000700* + ID_MODEL_FROM_DATABASE=CE Media Processor A/V Bridge + +pci:v00008086d00000701* + ID_MODEL_FROM_DATABASE=CE Media Processor NAND Flash Controller + +pci:v00008086d00000703* + ID_MODEL_FROM_DATABASE=CE Media Processor Media Control Unit 1 + +pci:v00008086d00000704* + ID_MODEL_FROM_DATABASE=CE Media Processor Video Capture Interface + +pci:v00008086d00000707* + ID_MODEL_FROM_DATABASE=CE Media Processor SPI Slave + +pci:v00008086d00000708* + ID_MODEL_FROM_DATABASE=CE Media Processor 4100 + +pci:v00008086d00000800* + ID_MODEL_FROM_DATABASE=Moorestown SPI Ctrl 0 + +pci:v00008086d00000801* + ID_MODEL_FROM_DATABASE=Moorestown SPI Ctrl 1 + +pci:v00008086d00000802* + ID_MODEL_FROM_DATABASE=Moorestown I2C 0 + +pci:v00008086d00000803* + ID_MODEL_FROM_DATABASE=Moorestown I2C 1 + +pci:v00008086d00000804* + ID_MODEL_FROM_DATABASE=Moorestown I2C 2 + +pci:v00008086d00000805* + ID_MODEL_FROM_DATABASE=Moorestown Keyboard Ctrl + +pci:v00008086d00000806* + ID_MODEL_FROM_DATABASE=Moorestown USB Ctrl + +pci:v00008086d00000807* + ID_MODEL_FROM_DATABASE=Moorestown SD Host Ctrl 0 + +pci:v00008086d00000808* + ID_MODEL_FROM_DATABASE=Moorestown SD Host Ctrl 1 + +pci:v00008086d00000809* + ID_MODEL_FROM_DATABASE=Moorestown NAND Ctrl + +pci:v00008086d0000080A* + ID_MODEL_FROM_DATABASE=Moorestown Audio Ctrl + +pci:v00008086d0000080B* + ID_MODEL_FROM_DATABASE=Moorestown ISP + +pci:v00008086d0000080C* + ID_MODEL_FROM_DATABASE=Moorestown Security Controller + +pci:v00008086d0000080D* + ID_MODEL_FROM_DATABASE=Moorestown External Displays + +pci:v00008086d0000080E* + ID_MODEL_FROM_DATABASE=Moorestown SCU IPC + +pci:v00008086d0000080F* + ID_MODEL_FROM_DATABASE=Moorestown GPIO Controller + +pci:v00008086d00000810* + ID_MODEL_FROM_DATABASE=Moorestown Power Management Unit + +pci:v00008086d00000811* + ID_MODEL_FROM_DATABASE=Moorestown OTG Ctrl + +pci:v00008086d00000812* + ID_MODEL_FROM_DATABASE=Moorestown SPI Ctrl 2 + +pci:v00008086d00000813* + ID_MODEL_FROM_DATABASE=Moorestown SC DMA + +pci:v00008086d00000814* + ID_MODEL_FROM_DATABASE=Moorestown LPE DMA + +pci:v00008086d00000815* + ID_MODEL_FROM_DATABASE=Moorestown SSP0 + +pci:v00008086d00000885* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 + +pci:v00008086d00000885sv00008086sd00001305* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BGN) + +pci:v00008086d00000885sv00008086sd00001307* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BG) + +pci:v00008086d00000885sv00008086sd00001325* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BGN) + +pci:v00008086d00000885sv00008086sd00001327* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BG) + +pci:v00008086d00000886* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 + +pci:v00008086d00000886sv00008086sd00001315* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BGN) + +pci:v00008086d00000886sv00008086sd00001317* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N + WiMAX 6150 (BG) + +pci:v00008086d00000887* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2230 + +pci:v00008086d00000887sv00008086sd00004062* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2230 (BGN) + +pci:v00008086d00000887sv00008086sd00004462* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2230 (BGN) + +pci:v00008086d00000888* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2230 + +pci:v00008086d00000888sv00008086sd00004262* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2230 (BGN) + +pci:v00008086d0000088E* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6235 + +pci:v00008086d0000088Esv00008086sd00004060* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6235 (AGN) + +pci:v00008086d0000088Esv00008086sd00004460* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6235 (AGN) + +pci:v00008086d0000088F* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6235 + +pci:v00008086d0000088Fsv00008086sd00004260* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6235 (AGN) + +pci:v00008086d00000890* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 + +pci:v00008086d00000890sv00008086sd00004022* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 (BGN) + +pci:v00008086d00000890sv00008086sd00004422* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 (BGN) + +pci:v00008086d00000890sv00008086sd00004822* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 (BGN) + +pci:v00008086d00000891* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 + +pci:v00008086d00000891sv00008086sd00004222* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 2200 (BGN) + +pci:v00008086d00000892* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 135 + +pci:v00008086d00000892sv00008086sd00000062* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 135 (BGN) + +pci:v00008086d00000892sv00008086sd00000462* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 135 (BGN) + +pci:v00008086d00000893* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 135 + +pci:v00008086d00000893sv00008086sd00000262* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 135 (BGN) + +pci:v00008086d00000894* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 + +pci:v00008086d00000894sv00008086sd00000022* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 (BGN) + +pci:v00008086d00000894sv00008086sd00000422* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 (BGN) + +pci:v00008086d00000894sv00008086sd00000822* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 (BGN) + +pci:v00008086d00000895* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 + +pci:v00008086d00000895sv00008086sd00000222* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 105 (BGN) + +pci:v00008086d00000896* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 + +pci:v00008086d00000896sv00008086sd00005005* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BGN) + +pci:v00008086d00000896sv00008086sd00005007* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BG) + +pci:v00008086d00000896sv00008086sd00005025* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BGN) + +pci:v00008086d00000896sv00008086sd00005027* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BG) + +pci:v00008086d00000897* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 + +pci:v00008086d00000897sv00008086sd00005015* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BGN) + +pci:v00008086d00000897sv00008086sd00005017* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 130 (BG) + +pci:v00008086d000008AE* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 + +pci:v00008086d000008AEsv00008086sd00001005* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BGN) + +pci:v00008086d000008AEsv00008086sd00001007* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BG) + +pci:v00008086d000008AEsv00008086sd00001025* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BGN) + +pci:v00008086d000008AEsv00008086sd00001027* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BG) + +pci:v00008086d000008AF* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 + +pci:v00008086d000008AFsv00008086sd00001015* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BGN) + +pci:v00008086d000008AFsv00008086sd00001017* + ID_MODEL_FROM_DATABASE=Centrino Wireless-N 100 (BG) + +pci:v00008086d000008B1* + ID_MODEL_FROM_DATABASE=Wireless 7260 + +pci:v00008086d000008B1sv00008086sd00004020* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000402A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004060* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004062* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000406A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004070* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004072* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004160* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004162* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004170* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004420* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004460* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004462* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000446A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004470* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004472* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004560* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd00004570* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000486E* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004870* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004A6C* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004A6E* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004A70* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004C60* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00004C70* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00005070* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00005072* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00005170* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd00005770* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C020* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C02A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C060* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C062* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C06A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C070* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C072* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C160* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C162* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C170* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C360* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C420* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C460* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C462* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C470* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C472* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C560* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C570* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000C760* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B1sv00008086sd0000C770* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000CC60* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B1sv00008086sd0000CC70* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B2* + ID_MODEL_FROM_DATABASE=Wireless 7260 + +pci:v00008086d000008B2sv00008086sd00004220* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd00004260* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd00004262* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd0000426A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd00004270* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd00004272* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B2sv00008086sd00004360* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd00004370* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B2sv00008086sd0000C220* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd0000C260* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd0000C262* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd0000C26A* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) + +pci:v00008086d000008B2sv00008086sd0000C270* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B2sv00008086sd0000C272* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B2sv00008086sd0000C370* + ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-AC 7260) + +pci:v00008086d000008B3* + ID_MODEL_FROM_DATABASE=Wireless 3160 + +pci:v00008086d000008B3sv00008086sd00000060* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-N 3160) + +pci:v00008086d000008B3sv00008086sd00000062* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Wireless-N 3160) + +pci:v00008086d000008B3sv00008086sd00000070* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00000072* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00000170* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00000172* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00000260* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-N 3160) + +pci:v00008086d000008B3sv00008086sd00000470* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00000472* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00001070* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00001170* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B3sv00008086sd00008060* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless N-3160) + +pci:v00008086d000008B3sv00008086sd00008062* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Wireless N-3160) + +pci:v00008086d000008B3sv00008086sd00008070* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B3sv00008086sd00008072* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B3sv00008086sd00008170* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B3sv00008086sd00008172* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B3sv00008086sd00008470* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B3sv00008086sd00008570* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B4* + ID_MODEL_FROM_DATABASE=Wireless 3160 + +pci:v00008086d000008B4sv00008086sd00000270* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B4sv00008086sd00000272* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B4sv00008086sd00000370* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless-AC 3160) + +pci:v00008086d000008B4sv00008086sd00008260* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B4sv00008086sd00008270* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B4sv00008086sd00008272* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008B4sv00008086sd00008370* + ID_MODEL_FROM_DATABASE=Wireless 3160 (Dual Band Wireless AC 3160) + +pci:v00008086d000008CF* + ID_MODEL_FROM_DATABASE=Atom Processor Z2760 Integrated Graphics Controller + +pci:v00008086d00000953* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD + +pci:v00008086d00000953sv00008086sd00003702* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3700 SSD) + +pci:v00008086d00000953sv00008086sd00003703* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3700 SSD [2.5" SFF]) + +pci:v00008086d00000953sv00008086sd00003704* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3500 SSD [Add-in Card]) + +pci:v00008086d00000953sv00008086sd00003705* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3500 SSD [2.5" SFF]) + +pci:v00008086d00000953sv00008086sd00003709* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3600 SSD [Add-in Card]) + +pci:v00008086d00000953sv00008086sd0000370A* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (DC P3600 SSD [2.5" SFF]) + +pci:v00008086d00000953sv00008086sd0000370D* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (SSD 750 Series [Add-in Card]) + +pci:v00008086d00000953sv00008086sd0000370E* + ID_MODEL_FROM_DATABASE=PCIe Data Center SSD (SSD 750 Series [2.5" SFF]) + +pci:v00008086d0000095A* + ID_MODEL_FROM_DATABASE=Wireless 7265 + +pci:v00008086d0000095Asv00008086sd00001010* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005000* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd00005002* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd0000500A* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd00005010* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005012* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005020* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd0000502A* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd00005090* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005100* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005102* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd0000510A* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005110* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005112* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005190* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005400* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005410* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005412* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005420* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Asv00008086sd00005490* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005510* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00005590* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009000* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd0000900A* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009010* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009012* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009110* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009112* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009210* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009310* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009400* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009410* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Asv00008086sd00009510* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095B* + ID_MODEL_FROM_DATABASE=Wireless 7265 + +pci:v00008086d0000095Bsv00008086sd00005200* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Bsv00008086sd00005202* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Wireless-N 7265) + +pci:v00008086d0000095Bsv00008086sd0000520A* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-N 7265) + +pci:v00008086d0000095Bsv00008086sd00005210* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Bsv00008086sd00005212* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Bsv00008086sd00005290* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Bsv00008086sd00005302* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Wireless-N 7265) + +pci:v00008086d0000095Bsv00008086sd00005310* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d0000095Bsv00008086sd00009200* + ID_MODEL_FROM_DATABASE=Wireless 7265 (Dual Band Wireless-AC 7265) + +pci:v00008086d00000960* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor/Bridge + +pci:v00008086d00000962* + ID_MODEL_FROM_DATABASE=80960RM (i960RM) Bridge + +pci:v00008086d00000964* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor/Bridge + +pci:v00008086d00000A04* + ID_MODEL_FROM_DATABASE=Haswell-ULT DRAM Controller + +pci:v00008086d00000A04sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=Haswell-ULT DRAM Controller (ThinkPad X240) + +pci:v00008086d00000A06* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000A0C* + ID_MODEL_FROM_DATABASE=Haswell-ULT HD Audio Controller + +pci:v00008086d00000A0Csv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=Haswell-ULT HD Audio Controller (ThinkPad X240) + +pci:v00008086d00000A16* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000A16sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller (ThinkPad X240) + +pci:v00008086d00000A22* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000A26* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000A2A* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000A2E* + ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller + +pci:v00008086d00000BE0* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE1* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE1sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller (D270S/D250S Motherboard) + +pci:v00008086d00000BE2* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE3* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE4* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE5* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE6* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE7* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE8* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BE9* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BEA* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BEB* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BEC* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BED* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BEE* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BEF* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx Integrated Graphics Controller + +pci:v00008086d00000BF0* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF1* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF2* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF3* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF4* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF5* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF5sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller (D270S/D250S Motherboard) + +pci:v00008086d00000BF6* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000BF7* + ID_MODEL_FROM_DATABASE=Atom Processor D2xxx/N2xxx DRAM Controller + +pci:v00008086d00000C00* + ID_MODEL_FROM_DATABASE=4th Gen Core Processor DRAM Controller + +pci:v00008086d00000C01* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor PCI Express x16 Controller + +pci:v00008086d00000C04* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller + +pci:v00008086d00000C04sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller (ZBook 15) + +pci:v00008086d00000C04sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor DRAM Controller (ThinkPad T440p) + +pci:v00008086d00000C05* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor PCI Express x8 Controller + +pci:v00008086d00000C08* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3 Processor DRAM Controller + +pci:v00008086d00000C09* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor PCI Express x4 Controller + +pci:v00008086d00000C0C* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller + +pci:v00008086d00000C0Csv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller (ThinkPad T440p) + +pci:v00008086d00000C46* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 PCI Express Root Port 1 + +pci:v00008086d00000C47* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 PCI Express Root Port 2 + +pci:v00008086d00000C48* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 PCI Express Root Port 3 + +pci:v00008086d00000C49* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 PCI Express Root Port 4 + +pci:v00008086d00000C4E* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 NTB Primary + +pci:v00008086d00000C50* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QuickData Technology Device + +pci:v00008086d00000C51* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QuickData Technology Device + +pci:v00008086d00000C52* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QuickData Technology Device + +pci:v00008086d00000C53* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QuickData Technology Device + +pci:v00008086d00000C54* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C55* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 DFX 1 + +pci:v00008086d00000C56* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 DFX 2 + +pci:v00008086d00000C59* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus 2.0 Controller 0 + +pci:v00008086d00000C5A* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus 2.0 Controller 1 + +pci:v00008086d00000C5B* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus Controller 2 + +pci:v00008086d00000C5C* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus Controller 3 + +pci:v00008086d00000C5D* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus Controller 4 + +pci:v00008086d00000C5E* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 SMBus Controller 5 + +pci:v00008086d00000C5F* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 UART + +pci:v00008086d00000C60* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Integrated Legacy Bus + +pci:v00008086d00000C70* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C71* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C72* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C73* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C74* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C75* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C76* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C77* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C78* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C79* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7A* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7B* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7C* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7D* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7E* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000C7F* + ID_MODEL_FROM_DATABASE=Atom Processor S1200 Internal + +pci:v00008086d00000D00* + ID_MODEL_FROM_DATABASE=Crystal Well DRAM Controller + +pci:v00008086d00000D01* + ID_MODEL_FROM_DATABASE=Crystal Well PCI Express x16 Controller + +pci:v00008086d00000D04* + ID_MODEL_FROM_DATABASE=Crystal Well DRAM Controller + +pci:v00008086d00000D05* + ID_MODEL_FROM_DATABASE=Crystal Well PCI Express x8 Controller + +pci:v00008086d00000D09* + ID_MODEL_FROM_DATABASE=Crystal Well PCI Express x4 Controller + +pci:v00008086d00000D0C* + ID_MODEL_FROM_DATABASE=Crystal Well HD Audio Controller + +pci:v00008086d00000D16* + ID_MODEL_FROM_DATABASE=Crystal Well Integrated Graphics Controller + +pci:v00008086d00000D26* + ID_MODEL_FROM_DATABASE=Crystal Well Integrated Graphics Controller + +pci:v00008086d00000D36* + ID_MODEL_FROM_DATABASE=Crystal Well Integrated Graphics Controller + +pci:v00008086d00000E00* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DMI2 + +pci:v00008086d00000E01* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port in DMI2 Mode + +pci:v00008086d00000E02* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1a + +pci:v00008086d00000E03* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 1b + +pci:v00008086d00000E04* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2a + +pci:v00008086d00000E05* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2b + +pci:v00008086d00000E06* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2c + +pci:v00008086d00000E07* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 2d + +pci:v00008086d00000E08* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3a + +pci:v00008086d00000E09* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3b + +pci:v00008086d00000E0A* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3c + +pci:v00008086d00000E0B* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 PCI Express Root Port 3d + +pci:v00008086d00000E10* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO Configuration Registers + +pci:v00008086d00000E13* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO Configuration Registers + +pci:v00008086d00000E17* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO Configuration Registers + +pci:v00008086d00000E18* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO Configuration Registers + +pci:v00008086d00000E1C* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO Configuration Registers + +pci:v00008086d00000E1D* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe + +pci:v00008086d00000E1E* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 UBOX Registers + +pci:v00008086d00000E1F* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 UBOX Registers + +pci:v00008086d00000E20* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 0 + +pci:v00008086d00000E21* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 1 + +pci:v00008086d00000E22* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 2 + +pci:v00008086d00000E23* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 3 + +pci:v00008086d00000E24* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 4 + +pci:v00008086d00000E25* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 5 + +pci:v00008086d00000E26* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 6 + +pci:v00008086d00000E27* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Crystal Beach DMA Channel 7 + +pci:v00008086d00000E28* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 VTd/Memory Map/Misc + +pci:v00008086d00000E29* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Memory Hotplug + +pci:v00008086d00000E2A* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IIO RAS + +pci:v00008086d00000E2C* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 IOAPIC + +pci:v00008086d00000E2E* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 CBDMA + +pci:v00008086d00000E2F* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 CBDMA + +pci:v00008086d00000E30* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0 + +pci:v00008086d00000E32* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 0 + +pci:v00008086d00000E33* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 1 + +pci:v00008086d00000E34* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe + +pci:v00008086d00000E36* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring + +pci:v00008086d00000E37* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring + +pci:v00008086d00000E38* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 1 + +pci:v00008086d00000E3A* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 2 + +pci:v00008086d00000E3E* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring + +pci:v00008086d00000E3F* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Performance Ring Monitoring + +pci:v00008086d00000E40* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 2 + +pci:v00008086d00000E41* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers + +pci:v00008086d00000E43* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 2 + +pci:v00008086d00000E44* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 2 + +pci:v00008086d00000E45* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register + +pci:v00008086d00000E47* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register + +pci:v00008086d00000E60* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 1 + +pci:v00008086d00000E68* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Target Address/Thermal Registers + +pci:v00008086d00000E6A* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder Registers + +pci:v00008086d00000E6B* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder Registers + +pci:v00008086d00000E6C* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder Registers + +pci:v00008086d00000E6D* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder Registers + +pci:v00008086d00000E71* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 RAS Registers + +pci:v00008086d00000E74* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe + +pci:v00008086d00000E75* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 R2PCIe + +pci:v00008086d00000E77* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers + +pci:v00008086d00000E79* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 RAS Registers + +pci:v00008086d00000E7D* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 UBOX Registers + +pci:v00008086d00000E7F* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers + +pci:v00008086d00000E80* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 0 + +pci:v00008086d00000E81* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Ring Registers + +pci:v00008086d00000E83* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 0 + +pci:v00008086d00000E84* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 0 + +pci:v00008086d00000E85* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register + +pci:v00008086d00000E87* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Registers + +pci:v00008086d00000E90* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 1 + +pci:v00008086d00000E93* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link 1 + +pci:v00008086d00000E94* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Reut 1 + +pci:v00008086d00000E95* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 QPI Link Agent Register + +pci:v00008086d00000EA0* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Home Agent 0 + +pci:v00008086d00000EA8* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Target Address/Thermal Registers + +pci:v00008086d00000EAA* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers + +pci:v00008086d00000EAB* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers + +pci:v00008086d00000EAC* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers + +pci:v00008086d00000EAD* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder Registers + +pci:v00008086d00000EAE* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO Registers + +pci:v00008086d00000EAF* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO Registers + +pci:v00008086d00000EB0* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 Thermal Control 0 + +pci:v00008086d00000EB1* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 Thermal Control 1 + +pci:v00008086d00000EB2* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 ERROR Registers 0 + +pci:v00008086d00000EB3* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 ERROR Registers 1 + +pci:v00008086d00000EB4* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 Thermal Control 2 + +pci:v00008086d00000EB5* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 Thermal Control 3 + +pci:v00008086d00000EB6* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 ERROR Registers 2 + +pci:v00008086d00000EB7* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 1 Channel 0-3 ERROR Registers 3 + +pci:v00008086d00000EBC* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO Registers + +pci:v00008086d00000EBE* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO Registers + +pci:v00008086d00000EBF* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO Registers + +pci:v00008086d00000EC0* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Power Control Unit 0 + +pci:v00008086d00000EC1* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Power Control Unit 1 + +pci:v00008086d00000EC2* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Power Control Unit 2 + +pci:v00008086d00000EC3* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Power Control Unit 3 + +pci:v00008086d00000EC4* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Power Control Unit 4 + +pci:v00008086d00000EC8* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 System Address Decoder + +pci:v00008086d00000EC9* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Broadcast Registers + +pci:v00008086d00000ECA* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Broadcast Registers + +pci:v00008086d00000ED8* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000ED9* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EDC* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EDD* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EDE* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EDF* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EE0* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE1* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE2* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE3* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE4* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE5* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE6* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE7* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE8* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EE9* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EEA* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EEB* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EEC* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EED* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EEE* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Unicast Registers + +pci:v00008086d00000EF0* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 Thermal Control 0 + +pci:v00008086d00000EF1* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 Thermal Control 1 + +pci:v00008086d00000EF2* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 ERROR Registers 0 + +pci:v00008086d00000EF3* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 ERROR Registers 1 + +pci:v00008086d00000EF4* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 Thermal Control 2 + +pci:v00008086d00000EF5* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 Thermal Control 3 + +pci:v00008086d00000EF6* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 ERROR Registers 2 + +pci:v00008086d00000EF7* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 Integrated Memory Controller 0 Channel 0-3 ERROR Registers 3 + +pci:v00008086d00000EF8* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EF9* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EFA* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EFB* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EFC* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000EFD* + ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DDRIO + +pci:v00008086d00000F00* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series SoC Transaction Register + +pci:v00008086d00000F04* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series High Definition Audio Controller + +pci:v00008086d00000F06* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 DMA Controller + +pci:v00008086d00000F08* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 PWM Controller + +pci:v00008086d00000F09* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 PWM Controller + +pci:v00008086d00000F0A* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 HSUART Controller #1 + +pci:v00008086d00000F0C* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 HSUART Controller #2 + +pci:v00008086d00000F0E* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO1 SPI Controller + +pci:v00008086d00000F12* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series SMBus Controller + +pci:v00008086d00000F14* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series SDIO Controller + +pci:v00008086d00000F15* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series SDIO Controller + +pci:v00008086d00000F16* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series SDIO Controller + +pci:v00008086d00000F18* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series Trusted Execution Engine + +pci:v00008086d00000F1C* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series Power Control Unit + +pci:v00008086d00000F20* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series SATA IDE Controller + +pci:v00008086d00000F21* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series SATA IDE Controller + +pci:v00008086d00000F22* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series SATA AHCI Controller + +pci:v00008086d00000F23* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series SATA AHCI Controller + +pci:v00008086d00000F28* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPE Audio Controller + +pci:v00008086d00000F31* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series Graphics & Display + +pci:v00008086d00000F34* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series USB EHCI + +pci:v00008086d00000F35* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx, Celeron N2000 Series USB xHCI + +pci:v00008086d00000F35sv00001025sd00000936* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx, Celeron N2000 Series USB xHCI (Aspire ES1) + +pci:v00008086d00000F37* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series OTG USB Device + +pci:v00008086d00000F38* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series Camera ISP + +pci:v00008086d00000F40* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 DMA Controller + +pci:v00008086d00000F41* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #1 + +pci:v00008086d00000F42* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #2 + +pci:v00008086d00000F43* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #3 + +pci:v00008086d00000F44* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #4 + +pci:v00008086d00000F45* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #5 + +pci:v00008086d00000F46* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #6 + +pci:v00008086d00000F47* + ID_MODEL_FROM_DATABASE=Atom Processor Z36xxx/Z37xxx Series LPIO2 I2C Controller #7 + +pci:v00008086d00000F48* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series PCI Express Root Port 1 + +pci:v00008086d00000F4A* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series PCI Express Root Port 2 + +pci:v00008086d00000F4C* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series PCI Express Root Port 3 + +pci:v00008086d00000F4E* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series PCI Express Root Port 4 + +pci:v00008086d00000F50* + ID_MODEL_FROM_DATABASE=Atom Processor E3800 Series eMMC 4.5 Controller + +pci:v00008086d00001000* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) + +pci:v00008086d00001000sv00000E11sd0000B0DF* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) (NC6132 Gigabit Ethernet Adapter (1000-SX)) + +pci:v00008086d00001000sv00000E11sd0000B0E0* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) (NC6133 Gigabit Ethernet Adapter (1000-LX)) + +pci:v00008086d00001000sv00000E11sd0000B123* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) (NC6134 Gigabit Ethernet Adapter (1000-LX)) + +pci:v00008086d00001000sv00001014sd00000119* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) (Netfinity Gigabit Ethernet SX Adapter) + +pci:v00008086d00001000sv00008086sd00001000* + ID_MODEL_FROM_DATABASE=82542 Gigabit Ethernet Controller (Fiber) (PRO/1000 Gigabit Server Adapter) + +pci:v00008086d00001001* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Fiber) + +pci:v00008086d00001001sv00000E11sd0000004A* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Fiber) (NC6136 Gigabit Server Adapter) + +pci:v00008086d00001001sv00001014sd000001EA* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Fiber) (Netfinity Gigabit Ethernet SX Adapter) + +pci:v00008086d00001001sv00008086sd00001002* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Fiber) (PRO/1000 F Server Adapter) + +pci:v00008086d00001001sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Fiber) (PRO/1000 F Server Adapter) + +pci:v00008086d00001002* + ID_MODEL_FROM_DATABASE=Pro 100 LAN+Modem 56 Cardbus II + +pci:v00008086d00001002sv00008086sd0000200E* + ID_MODEL_FROM_DATABASE=Pro 100 LAN+Modem 56 Cardbus II + +pci:v00008086d00001002sv00008086sd00002013* + ID_MODEL_FROM_DATABASE=Pro 100 LAN+Modem 56 Cardbus II (Pro 100 SR Mobile Combo Adapter) + +pci:v00008086d00001002sv00008086sd00002017* + ID_MODEL_FROM_DATABASE=Pro 100 LAN+Modem 56 Cardbus II (Pro 100 S Combo Mobile Adapter) + +pci:v00008086d00001004* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) + +pci:v00008086d00001004sv00000E11sd00000049* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) (NC7132 Gigabit Upgrade Module) + +pci:v00008086d00001004sv00000E11sd0000B1A4* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) (NC7131 Gigabit Server Adapter) + +pci:v00008086d00001004sv00001014sd000010F2* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) (Gigabit Ethernet Server Adapter) + +pci:v00008086d00001004sv00008086sd00001004* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) (PRO/1000 T Server Adapter) + +pci:v00008086d00001004sv00008086sd00002004* + ID_MODEL_FROM_DATABASE=82543GC Gigabit Ethernet Controller (Copper) (PRO/1000 T Server Adapter) + +pci:v00008086d00001008* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) + +pci:v00008086d00001008sv00001014sd00000269* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (iSeries 1000/100/10 Ethernet Adapter) + +pci:v00008086d00001008sv00001028sd0000011B* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PowerEdge 1650/2550) + +pci:v00008086d00001008sv00001028sd0000011C* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PRO/1000 XT Network Connection) + +pci:v00008086d00001008sv00008086sd00001107* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PRO/1000 XT Server Adapter) + +pci:v00008086d00001008sv00008086sd00002107* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PRO/1000 XT Server Adapter) + +pci:v00008086d00001008sv00008086sd00002110* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PRO/1000 XT Desktop Adapter) + +pci:v00008086d00001008sv00008086sd00003108* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Copper) (PRO/1000 XT Network Connection) + +pci:v00008086d00001009* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Fiber) + +pci:v00008086d00001009sv00001014sd00000268* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Fiber) (iSeries Gigabit Ethernet Adapter) + +pci:v00008086d00001009sv00008086sd00001109* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Fiber) (PRO/1000 XF Server Adapter) + +pci:v00008086d00001009sv00008086sd00002109* + ID_MODEL_FROM_DATABASE=82544EI Gigabit Ethernet Controller (Fiber) (PRO/1000 XF Server Adapter) + +pci:v00008086d0000100A* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller + +pci:v00008086d0000100C* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000100Csv00008086sd00001112* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (Copper) (PRO/1000 T Desktop Adapter) + +pci:v00008086d0000100Csv00008086sd00002112* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (Copper) (PRO/1000 T Desktop Adapter) + +pci:v00008086d0000100D* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (LOM) + +pci:v00008086d0000100Dsv00001028sd00000123* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (LOM) (PRO/1000 XT Network Connection) + +pci:v00008086d0000100Dsv00001079sd0000891F* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (LOM) (82544GC Based Network Connection) + +pci:v00008086d0000100Dsv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (LOM) (CT8 mainboard) + +pci:v00008086d0000100Dsv00008086sd0000110D* + ID_MODEL_FROM_DATABASE=82544GC Gigabit Ethernet Controller (LOM) (82544GC Based Network Connection) + +pci:v00008086d0000100E* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller + +pci:v00008086d0000100Esv00001014sd00000265* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d0000100Esv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d0000100Esv00001014sd0000026A* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d0000100Esv00001028sd0000002E* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (Optiplex GX260) + +pci:v00008086d0000100Esv00001028sd00000134* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PowerEdge 600SC) + +pci:v00008086d0000100Esv00001028sd00000151* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (Optiplex GX270) + +pci:v00008086d0000100Esv0000107Bsd00008920* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Desktop Adapter) + +pci:v00008086d0000100Esv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (QEMU Virtual Machine) + +pci:v00008086d0000100Esv00008086sd0000001E* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Desktop Adapter) + +pci:v00008086d0000100Esv00008086sd0000002E* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 MT Desktop Adapter) + +pci:v00008086d0000100Esv00008086sd00001376* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 GT Desktop Adapter) + +pci:v00008086d0000100Esv00008086sd00001476* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (PRO/1000 GT Desktop Adapter) + +pci:v00008086d0000100F* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000100Fsv00001014sd00000269* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) (iSeries 1000/100/10 Ethernet Adapter) + +pci:v00008086d0000100Fsv00001014sd0000028E* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) (PRO/1000 MT Network Connection) + +pci:v00008086d0000100Fsv000015ADsd00000750* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) (PRO/1000 MT Single Port Adapter) + +pci:v00008086d0000100Fsv00008086sd00001000* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) (PRO/1000 MT Network Connection) + +pci:v00008086d0000100Fsv00008086sd00001001* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Copper) (PRO/1000 MT Server Adapter) + +pci:v00008086d00001010* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) + +pci:v00008086d00001010sv00000E11sd000000DB* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (NC7170 Gigabit Server Adapter) + +pci:v00008086d00001010sv00001014sd0000027C* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (PRO/1000 MT Dual Port Network Adapter) + +pci:v00008086d00001010sv000015ADsd00000760* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (PRO/1000 MT Dual Port Adapter) + +pci:v00008086d00001010sv000018FBsd00007872* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (RESlink-X) + +pci:v00008086d00001010sv00001FC1sd00000026* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (Niagara 2260 Bypass Card) + +pci:v00008086d00001010sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (CT8 mainboard) + +pci:v00008086d00001010sv00004C53sd000010A0* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (CA3/CR3 mainboard) + +pci:v00008086d00001010sv00008086sd00001011* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (PRO/1000 MT Dual Port Server Adapter) + +pci:v00008086d00001010sv00008086sd00001012* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (PRO/1000 MT Dual Port Server Adapter) + +pci:v00008086d00001010sv00008086sd0000101A* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (PRO/1000 MT Dual Port Network Connection) + +pci:v00008086d00001010sv00008086sd00003424* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Copper) (SE7501HG2 Mainboard) + +pci:v00008086d00001011* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Fiber) + +pci:v00008086d00001011sv00001014sd00000268* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Fiber) (iSeries Gigabit Ethernet Adapter) + +pci:v00008086d00001011sv00008086sd00001002* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Fiber) (PRO/1000 MF Server Adapter) + +pci:v00008086d00001011sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=82545EM Gigabit Ethernet Controller (Fiber) (PRO/1000 MF Server Adapter (LX)) + +pci:v00008086d00001012* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Fiber) + +pci:v00008086d00001012sv00000E11sd000000DC* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Fiber) (NC6170 Gigabit Server Adapter) + +pci:v00008086d00001012sv00008086sd00001012* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (Fiber) (PRO/1000 MF Dual Port Server Adapter) + +pci:v00008086d00001013* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller + +pci:v00008086d00001013sv00008086sd00000013* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001013sv00008086sd00001013* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001013sv00008086sd00001113* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller (PRO/1000 MT Desktop Adapter) + +pci:v00008086d00001014* + ID_MODEL_FROM_DATABASE=82541ER Gigabit Ethernet Controller + +pci:v00008086d00001014sv00008086sd00000014* + ID_MODEL_FROM_DATABASE=82541ER Gigabit Ethernet Controller (PRO/1000 MT Desktop Connection) + +pci:v00008086d00001014sv00008086sd00001014* + ID_MODEL_FROM_DATABASE=82541ER Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001015* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (LOM) + +pci:v00008086d00001015sv00008086sd00001015* + ID_MODEL_FROM_DATABASE=82540EM Gigabit Ethernet Controller (LOM) (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001016* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) + +pci:v00008086d00001016sv00001014sd0000052C* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001016sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001016sv00008086sd00001016* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001017* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller + +pci:v00008086d00001017sv00008086sd00001017* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (PR0/1000 MT Desktop Connection) + +pci:v00008086d00001018* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller + +pci:v00008086d00001018sv00008086sd00001018* + ID_MODEL_FROM_DATABASE=82541EI Gigabit Ethernet Controller (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001019* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller + +pci:v00008086d00001019sv00001458sd00001019* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d00001019sv00001458sd0000E000* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (Intel Gigabit Ethernet (Kenai II)) + +pci:v00008086d00001019sv00008086sd00001019* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (PRO/1000 CT Desktop Connection) + +pci:v00008086d00001019sv00008086sd0000301F* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (D865PERL mainboard) + +pci:v00008086d00001019sv00008086sd00003025* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (D875PBZ motherboard) + +pci:v00008086d00001019sv00008086sd0000302C* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (Intel 82865G Mainboard (D865GBF)) + +pci:v00008086d00001019sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (S875WP1-E mainboard) + +pci:v00008086d0000101A* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (Mobile) + +pci:v00008086d0000101Asv00008086sd0000101A* + ID_MODEL_FROM_DATABASE=82547EI Gigabit Ethernet Controller (Mobile) (PRO/1000 CT Mobile Connection) + +pci:v00008086d0000101D* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller + +pci:v00008086d0000101Dsv00008086sd00001000* + ID_MODEL_FROM_DATABASE=82546EB Gigabit Ethernet Controller (PRO/1000 MT Quad Port Server Adapter) + +pci:v00008086d0000101E* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) + +pci:v00008086d0000101Esv00001014sd00000549* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (Thinkpad) + +pci:v00008086d0000101Esv00001179sd00000001* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection) + +pci:v00008086d0000101Esv00008086sd0000101E* + ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001026* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller + +pci:v00008086d00001026sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d00001026sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (Precision 470) + +pci:v00008086d00001026sv00008086sd00001000* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MT Server Connection) + +pci:v00008086d00001026sv00008086sd00001001* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MT Server Adapter) + +pci:v00008086d00001026sv00008086sd00001002* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MT Server Adapter) + +pci:v00008086d00001026sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 GT Server Adapter) + +pci:v00008086d00001026sv00008086sd00001026* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MT Server Connection) + +pci:v00008086d00001027* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller + +pci:v00008086d00001027sv0000103Csd00003103* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (NC310F PCI-X Gigabit Server Adapter) + +pci:v00008086d00001027sv00008086sd00001001* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MF Server Adapter(LX)) + +pci:v00008086d00001027sv00008086sd00001002* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MF Server Adapter(LX)) + +pci:v00008086d00001027sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MF Server Adapter(LX)) + +pci:v00008086d00001027sv00008086sd00001027* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MF Server Adapter) + +pci:v00008086d00001028* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller + +pci:v00008086d00001028sv00008086sd00001028* + ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller (PRO/1000 MB Server Connection) + +pci:v00008086d00001029* + ID_MODEL_FROM_DATABASE=82559 Ethernet Controller + +pci:v00008086d00001030* + ID_MODEL_FROM_DATABASE=82559 InBusiness 10/100 + +pci:v00008086d00001031* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller + +pci:v00008086d00001031sv00001014sd00000209* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (ThinkPad A/T/X Series) + +pci:v00008086d00001031sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (Vaio PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00001031sv0000104Dsd0000813C* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (Vaio PCG-GRV616G) + +pci:v00008086d00001031sv0000107Bsd00005350* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00001031sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00001031sv0000144Dsd0000C000* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00001031sv0000144Dsd0000C001* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00001031sv0000144Dsd0000C003* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00001031sv0000144Dsd0000C006* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller (vpr Matrix 170B4) + +pci:v00008086d00001032* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VE Ethernet Controller + +pci:v00008086d00001033* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller + +pci:v00008086d00001034* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VM Ethernet Controller + +pci:v00008086d00001035* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3)/82562EH (LOM) Ethernet Controller + +pci:v00008086d00001036* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) 82562EH Ethernet Controller + +pci:v00008086d00001037* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) Chipset Ethernet Controller + +pci:v00008086d00001038* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller + +pci:v00008086d00001038sv00000E11sd00000098* + ID_MODEL_FROM_DATABASE=82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller (Evo N600c) + +pci:v00008086d00001039* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (LOM) Ethernet Controller + +pci:v00008086d00001039sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (LOM) Ethernet Controller (NetVista A30p) + +pci:v00008086d00001039sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (LOM) Ethernet Controller (PC8 onboard ethernet ETH1) + +pci:v00008086d0000103A* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (CNR) Ethernet Controller + +pci:v00008086d0000103B* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VM (LOM) Ethernet Controller + +pci:v00008086d0000103C* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VM (CNR) Ethernet Controller + +pci:v00008086d0000103D* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (MOB) Ethernet Controller + +pci:v00008086d0000103Dsv00001014sd00000522* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (MOB) Ethernet Controller (ThinkPad R40) + +pci:v00008086d0000103Dsv00001028sd00002002* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (MOB) Ethernet Controller (Latitude D500) + +pci:v00008086d0000103Dsv00008086sd0000103D* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VE (MOB) Ethernet Controller (82562EZ 10/100 Ethernet Controller) + +pci:v00008086d0000103E* + ID_MODEL_FROM_DATABASE=82801DB PRO/100 VM (MOB) Ethernet Controller + +pci:v00008086d00001040* + ID_MODEL_FROM_DATABASE=536EP Data Fax Modem + +pci:v00008086d00001040sv000016BEsd00001040* + ID_MODEL_FROM_DATABASE=536EP Data Fax Modem (V.9X DSP Data Fax Modem) + +pci:v00008086d00001043* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter + +pci:v00008086d00001043sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter (tc1100 tablet) + +pci:v00008086d00001043sv00008086sd00002522* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter (Samsung X10/P30 integrated WLAN) + +pci:v00008086d00001043sv00008086sd00002527* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter (MIM2000/Centrino) + +pci:v00008086d00001043sv00008086sd00002561* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter (Dell Latitude D800) + +pci:v00008086d00001043sv00008086sd00002581* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN 2100 3B Mini PCI Adapter (Toshiba Satellite M10) + +pci:v00008086d00001048* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller + +pci:v00008086d00001048sv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE LR Server Adapter) + +pci:v00008086d00001048sv00008086sd0000A11F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE LR Server Adapter) + +pci:v00008086d00001049* + ID_MODEL_FROM_DATABASE=82566MM Gigabit Network Connection + +pci:v00008086d00001049sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82566MM Gigabit Network Connection (Compaq 6910p) + +pci:v00008086d00001049sv000017AAsd000020B9* + ID_MODEL_FROM_DATABASE=82566MM Gigabit Network Connection (ThinkPad T61/R61) + +pci:v00008086d0000104A* + ID_MODEL_FROM_DATABASE=82566DM Gigabit Network Connection + +pci:v00008086d0000104B* + ID_MODEL_FROM_DATABASE=82566DC Gigabit Network Connection + +pci:v00008086d0000104C* + ID_MODEL_FROM_DATABASE=82562V 10/100 Network Connection + +pci:v00008086d0000104D* + ID_MODEL_FROM_DATABASE=82566MC Gigabit Network Connection + +pci:v00008086d00001050* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller + +pci:v00008086d00001050sv00001028sd0000019D* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (Dimension 3000) + +pci:v00008086d00001050sv00001462sd0000728C* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d00001050sv00001462sd0000758C* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (MS-6758 (875P Neo)) + +pci:v00008086d00001050sv00008086sd00003020* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (D865PERL mainboard) + +pci:v00008086d00001050sv00008086sd0000302F* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (Desktop Board D865GBF) + +pci:v00008086d00001050sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (S875WP1-E mainboard) + +pci:v00008086d00001051* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) integrated LAN Controller + +pci:v00008086d00001052* + ID_MODEL_FROM_DATABASE=PRO/100 VM Network Connection + +pci:v00008086d00001053* + ID_MODEL_FROM_DATABASE=PRO/100 VM Network Connection + +pci:v00008086d00001054* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001055* + ID_MODEL_FROM_DATABASE=PRO/100 VM Network Connection + +pci:v00008086d00001056* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001057* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001059* + ID_MODEL_FROM_DATABASE=82551QM Ethernet Controller + +pci:v00008086d0000105B* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000105E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller + +pci:v00008086d0000105Esv0000103Csd00007044* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (NC360T PCI Express Dual Port Gigabit Server Adapter) + +pci:v00008086d0000105Esv0000103Csd0000704E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Dual Port 1000Base-T (PCIe) [AD337A]) + +pci:v00008086d0000105Esv00001775sd00001100* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d0000105Esv00001775sd00006003* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Telum GE-QT) + +pci:v00008086d0000105Esv000018DFsd00001214* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (2x 1GbE, PCIe x1, dual Intel 82571EB chips) + +pci:v00008086d0000105Esv00008086sd0000005E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Dual Port Server Connection) + +pci:v00008086d0000105Esv00008086sd0000105E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Dual Port Network Connection) + +pci:v00008086d0000105Esv00008086sd000010D5* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (82571PT Gigabit PT Quad Port Server ExpressModule) + +pci:v00008086d0000105Esv00008086sd0000115E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Dual Port Server Adapter) + +pci:v00008086d0000105Esv00008086sd0000125E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Dual Port Server Adapter) + +pci:v00008086d0000105Esv00008086sd0000135E* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Dual Port Server Adapter) + +pci:v00008086d0000105F* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller + +pci:v00008086d0000105Fsv0000103Csd0000704F* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Dual Port 1000Base-SX (PCIe) [AD338A]) + +pci:v00008086d0000105Fsv00008086sd0000005A* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PF Dual Port Server Adapter) + +pci:v00008086d0000105Fsv00008086sd0000115F* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PF Dual Port Server Adapter) + +pci:v00008086d0000105Fsv00008086sd0000125F* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PF Dual Port Server Adapter) + +pci:v00008086d0000105Fsv00008086sd0000135F* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PF Dual Port Server Adapter) + +pci:v00008086d00001060* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller + +pci:v00008086d00001060sv00008086sd00000060* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PB Dual Port Server Connection) + +pci:v00008086d00001060sv00008086sd00001060* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PB Dual Port Server Connection) + +pci:v00008086d00001064* + ID_MODEL_FROM_DATABASE=82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller + +pci:v00008086d00001064sv00001043sd000080F8* + ID_MODEL_FROM_DATABASE=82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller (P5GD1-VW Mainboard) + +pci:v00008086d00001065* + ID_MODEL_FROM_DATABASE=82562ET/EZ/GT/GZ - PRO/100 VE Ethernet Controller + +pci:v00008086d00001066* + ID_MODEL_FROM_DATABASE=82562 EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller + +pci:v00008086d00001067* + ID_MODEL_FROM_DATABASE=82562 EM/EX/GX - PRO/100 VM Ethernet Controller + +pci:v00008086d00001068* + ID_MODEL_FROM_DATABASE=82562ET/EZ/GT/GZ - PRO/100 VE (LOM) Ethernet Controller Mobile + +pci:v00008086d00001069* + ID_MODEL_FROM_DATABASE=82562EM/EX/GX - PRO/100 VM (LOM) Ethernet Controller Mobile + +pci:v00008086d0000106A* + ID_MODEL_FROM_DATABASE=82562G - PRO/100 VE (LOM) Ethernet Controller + +pci:v00008086d0000106B* + ID_MODEL_FROM_DATABASE=82562G - PRO/100 VE Ethernet Controller Mobile + +pci:v00008086d00001075* + ID_MODEL_FROM_DATABASE=82547GI Gigabit Ethernet Controller + +pci:v00008086d00001075sv00001028sd00000165* + ID_MODEL_FROM_DATABASE=82547GI Gigabit Ethernet Controller (PowerEdge 750) + +pci:v00008086d00001075sv00008086sd00000075* + ID_MODEL_FROM_DATABASE=82547GI Gigabit Ethernet Controller (PRO/1000 CT Network Connection) + +pci:v00008086d00001075sv00008086sd00001075* + ID_MODEL_FROM_DATABASE=82547GI Gigabit Ethernet Controller (PRO/1000 CT Network Connection) + +pci:v00008086d00001076* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller + +pci:v00008086d00001076sv00001028sd00000165* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00001028sd0000106D* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00008086sd00000076* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00008086sd00001076* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Connection) + +pci:v00008086d00001076sv00008086sd00001176* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Desktop Adapter) + +pci:v00008086d00001076sv00008086sd00001276* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Network Adapter) + +pci:v00008086d00001077* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller + +pci:v00008086d00001077sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001077sv00008086sd00000077* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001077sv00008086sd00001077* + ID_MODEL_FROM_DATABASE=82541GI Gigabit Ethernet Controller (PRO/1000 MT Mobile Connection) + +pci:v00008086d00001078* + ID_MODEL_FROM_DATABASE=82541ER Gigabit Ethernet Controller + +pci:v00008086d00001078sv00008086sd00001078* + ID_MODEL_FROM_DATABASE=82541ER Gigabit Ethernet Controller (82541ER-based Network Connection) + +pci:v00008086d00001079* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller + +pci:v00008086d00001079sv0000103Csd000012A6* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Dual Port 1000Base-T [A9900A]) + +pci:v00008086d00001079sv0000103Csd000012CF* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Core Dual Port 1000Base-T [AB352A]) + +pci:v00008086d00001079sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (V5D Single Board Computer Gigabit Ethernet) + +pci:v00008086d00001079sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (CE9) + +pci:v00008086d00001079sv00001FC1sd00000027* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Niagara 2261 Failover NIC) + +pci:v00008086d00001079sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d00001079sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (CL9 mainboard) + +pci:v00008086d00001079sv00008086sd00000079* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MT Dual Port Network Connection) + +pci:v00008086d00001079sv00008086sd00001079* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MT Dual Port Network Connection) + +pci:v00008086d00001079sv00008086sd00001179* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MT Dual Port Server Adapter) + +pci:v00008086d00001079sv00008086sd0000117A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MT Dual Port Server Adapter) + +pci:v00008086d0000107A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller + +pci:v00008086d0000107Asv0000103Csd000012A8* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Dual Port 1000base-SX [A9899A]) + +pci:v00008086d0000107Asv00008086sd0000107A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MF Dual Port Server Adapter) + +pci:v00008086d0000107Asv00008086sd0000127A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MF Dual Port Server Adapter) + +pci:v00008086d0000107B* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller + +pci:v00008086d0000107Bsv00008086sd0000007B* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MB Dual Port Server Connection) + +pci:v00008086d0000107Bsv00008086sd0000107B* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 MB Dual Port Server Connection) + +pci:v00008086d0000107C* + ID_MODEL_FROM_DATABASE=82541PI Gigabit Ethernet Controller + +pci:v00008086d0000107Csv00008086sd00001376* + ID_MODEL_FROM_DATABASE=82541PI Gigabit Ethernet Controller (PRO/1000 GT Desktop Adapter) + +pci:v00008086d0000107Csv00008086sd00001476* + ID_MODEL_FROM_DATABASE=82541PI Gigabit Ethernet Controller (PRO/1000 GT Desktop Adapter) + +pci:v00008086d0000107D* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000107Dsv00008086sd00001082* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (PRO/1000 PT Server Adapter) + +pci:v00008086d0000107Dsv00008086sd00001084* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (PRO/1000 PT Server Adapter) + +pci:v00008086d0000107Dsv00008086sd00001092* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (PRO/1000 PT Server Adapter) + +pci:v00008086d0000107E* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Fiber) + +pci:v00008086d0000107Esv00008086sd00001084* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Fiber) (PRO/1000 PF Server Adapter) + +pci:v00008086d0000107Esv00008086sd00001085* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Fiber) (PRO/1000 PF Server Adapter) + +pci:v00008086d0000107Esv00008086sd00001094* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Fiber) (PRO/1000 PF Server Adapter) + +pci:v00008086d0000107F* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller + +pci:v00008086d00001080* + ID_MODEL_FROM_DATABASE=FA82537EP 56K V.92 Data/Fax Modem PCI + +pci:v00008086d00001081* + ID_MODEL_FROM_DATABASE=631xESB/632xESB LAN Controller Copper + +pci:v00008086d00001082* + ID_MODEL_FROM_DATABASE=631xESB/632xESB LAN Controller fiber + +pci:v00008086d00001083* + ID_MODEL_FROM_DATABASE=631xESB/632xESB LAN Controller SERDES + +pci:v00008086d00001084* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IDE Redirection + +pci:v00008086d00001085* + ID_MODEL_FROM_DATABASE=631xESB/632xESB Serial Port Redirection + +pci:v00008086d00001086* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IPMI/KCS0 + +pci:v00008086d00001087* + ID_MODEL_FROM_DATABASE=631xESB/632xESB UHCI Redirection + +pci:v00008086d00001089* + ID_MODEL_FROM_DATABASE=631xESB/632xESB BT + +pci:v00008086d0000108A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller + +pci:v00008086d0000108Asv00008086sd0000108A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 P Dual Port Server Adapter) + +pci:v00008086d0000108Asv00008086sd0000118A* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (PRO/1000 P Dual Port Server Adapter) + +pci:v00008086d0000108B* + ID_MODEL_FROM_DATABASE=82573V Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000108Bsv00001462sd0000176C* + ID_MODEL_FROM_DATABASE=82573V Gigabit Ethernet Controller (Copper) (on board on MSI 945P - NEO (MS-7176)) + +pci:v00008086d0000108C* + ID_MODEL_FROM_DATABASE=82573E Gigabit Ethernet Controller (Copper) + +pci:v00008086d0000108E* + ID_MODEL_FROM_DATABASE=82573E KCS (Active Management) + +pci:v00008086d0000108F* + ID_MODEL_FROM_DATABASE=Active Management Technology - SOL + +pci:v00008086d00001091* + ID_MODEL_FROM_DATABASE=PRO/100 VM Network Connection + +pci:v00008086d00001092* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001092sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d00001093* + ID_MODEL_FROM_DATABASE=PRO/100 VM Network Connection + +pci:v00008086d00001094* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001095* + ID_MODEL_FROM_DATABASE=PRO/100 VE Network Connection + +pci:v00008086d00001096* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Copper) + +pci:v00008086d00001096sv000015D9sd00001096* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Copper) (Motherboard) + +pci:v00008086d00001096sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Copper) (X7DVL-E-O motherboard) + +pci:v00008086d00001096sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Copper) (Intel S5000PSLSATA Server Board) + +pci:v00008086d00001097* + ID_MODEL_FROM_DATABASE=631xESB/632xESB DPT LAN Controller (Fiber) + +pci:v00008086d00001098* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Serdes) + +pci:v00008086d00001099* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) + +pci:v00008086d00001099sv00008086sd00001099* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) (PRO/1000 GT Quad Port Server Adapter) + +pci:v00008086d0000109A* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller + +pci:v00008086d0000109Asv00001179sd0000FF10* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (PRO/1000 PL) + +pci:v00008086d0000109Asv000017AAsd00002001* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (ThinkPad T60) + +pci:v00008086d0000109Asv000017AAsd0000207E* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (ThinkPad X60/X60s) + +pci:v00008086d0000109Asv00008086sd0000109A* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (PRO/1000 PL Network Connection) + +pci:v00008086d0000109Asv00008086sd0000309C* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (Desktop Board D945GTP) + +pci:v00008086d0000109Asv00008086sd000030A5* + ID_MODEL_FROM_DATABASE=82573L Gigabit Ethernet Controller (Desktop Board D975XBX) + +pci:v00008086d0000109B* + ID_MODEL_FROM_DATABASE=82546GB PRO/1000 GF Quad Port Server Adapter + +pci:v00008086d0000109E* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller + +pci:v00008086d0000109Esv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE CX4 Server Adapter) + +pci:v00008086d0000109Esv00008086sd0000A11F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE CX4 Server Adapter) + +pci:v00008086d000010A0* + ID_MODEL_FROM_DATABASE=82571EB PRO/1000 AT Quad Port Bypass Adapter + +pci:v00008086d000010A1* + ID_MODEL_FROM_DATABASE=82571EB PRO/1000 AF Quad Port Bypass Adapter + +pci:v00008086d000010A4* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller + +pci:v00008086d000010A4sv00008086sd000010A4* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Quad Port Server Adapter) + +pci:v00008086d000010A4sv00008086sd000011A4* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (PRO/1000 PT Quad Port Server Adapter) + +pci:v00008086d000010A5* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Fiber) + +pci:v00008086d000010A5sv00008086sd000010A5* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Fiber) (PRO/1000 PF Quad Port Server Adapter) + +pci:v00008086d000010A5sv00008086sd000010A6* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Fiber) (PRO/1000 PF Quad Port Server Adapter) + +pci:v00008086d000010A6* + ID_MODEL_FROM_DATABASE=82599EB 10-Gigabit Dummy Function + +pci:v00008086d000010A7* + ID_MODEL_FROM_DATABASE=82575EB Gigabit Network Connection + +pci:v00008086d000010A7sv00008086sd000010A8* + ID_MODEL_FROM_DATABASE=82575EB Gigabit Network Connection (82575EB Gigabit Riser Card) + +pci:v00008086d000010A9* + ID_MODEL_FROM_DATABASE=82575EB Gigabit Backplane Connection + +pci:v00008086d000010B0* + ID_MODEL_FROM_DATABASE=82573L PRO/1000 PL Network Connection + +pci:v00008086d000010B2* + ID_MODEL_FROM_DATABASE=82573V PRO/1000 PM Network Connection + +pci:v00008086d000010B3* + ID_MODEL_FROM_DATABASE=82573E PRO/1000 PM Network Connection + +pci:v00008086d000010B4* + ID_MODEL_FROM_DATABASE=82573L PRO/1000 PL Network Connection + +pci:v00008086d000010B5* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) + +pci:v00008086d000010B5sv0000103Csd00003109* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) (NC340T PCI-X Quad-port Gigabit Server Adapter) + +pci:v00008086d000010B5sv00008086sd00001099* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) (PRO/1000 GT Quad Port Server Adapter) + +pci:v00008086d000010B5sv00008086sd00001199* + ID_MODEL_FROM_DATABASE=82546GB Gigabit Ethernet Controller (Copper) (PRO/1000 GT Quad Port Server Adapter) + +pci:v00008086d000010B6* + ID_MODEL_FROM_DATABASE=82598 10GbE PCI-Express Ethernet Controller + +pci:v00008086d000010B9* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) + +pci:v00008086d000010B9sv0000103Csd0000704A* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (110T PCIe Gigabit Server Adapter) + +pci:v00008086d000010B9sv00008086sd00001083* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (PRO/1000 PT Desktop Adapter) + +pci:v00008086d000010B9sv00008086sd00001093* + ID_MODEL_FROM_DATABASE=82572EI Gigabit Ethernet Controller (Copper) (PRO/1000 PT Desktop Adapter) + +pci:v00008086d000010BA* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Copper) + +pci:v00008086d000010BB* + ID_MODEL_FROM_DATABASE=80003ES2LAN Gigabit Ethernet Controller (Serdes) + +pci:v00008086d000010BC* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Copper) + +pci:v00008086d000010BCsv0000103Csd0000704B* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Copper) (NC364T PCI Express Quad Port Gigabit Server Adapter) + +pci:v00008086d000010BCsv0000108Esd000011BC* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Copper) (x4 PCI-Express Quad Gigabit Ethernet UTP Low Profile Adapter) + +pci:v00008086d000010BCsv00008086sd000010BC* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Copper) (PRO/1000 PT Quad Port LP Server Adapter) + +pci:v00008086d000010BCsv00008086sd000011BC* + ID_MODEL_FROM_DATABASE=82571EB Gigabit Ethernet Controller (Copper) (PRO/1000 PT Quad Port LP Server Adapter) + +pci:v00008086d000010BD* + ID_MODEL_FROM_DATABASE=82566DM-2 Gigabit Network Connection + +pci:v00008086d000010BDsv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82566DM-2 Gigabit Network Connection (OptiPlex 755) + +pci:v00008086d000010BF* + ID_MODEL_FROM_DATABASE=82567LF Gigabit Network Connection + +pci:v00008086d000010C0* + ID_MODEL_FROM_DATABASE=82562V-2 10/100 Network Connection + +pci:v00008086d000010C0sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82562V-2 10/100 Network Connection (Inspiron 530) + +pci:v00008086d000010C2* + ID_MODEL_FROM_DATABASE=82562G-2 10/100 Network Connection + +pci:v00008086d000010C3* + ID_MODEL_FROM_DATABASE=82562GT-2 10/100 Network Connection + +pci:v00008086d000010C4* + ID_MODEL_FROM_DATABASE=82562GT 10/100 Network Connection + +pci:v00008086d000010C5* + ID_MODEL_FROM_DATABASE=82562G 10/100 Network Connection + +pci:v00008086d000010C6* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection + +pci:v00008086d000010C6sv00008086sd0000A05F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection (10-Gigabit XF SR Dual Port Server Adapter) + +pci:v00008086d000010C6sv00008086sd0000A15F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection (10-Gigabit XF SR Dual Port Server Adapter) + +pci:v00008086d000010C7* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection + +pci:v00008086d000010C7sv00001014sd0000037F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF SR Server Adapter) + +pci:v00008086d000010C7sv00001014sd00000380* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF LR Server Adapter) + +pci:v00008086d000010C7sv00008086sd0000A05F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF SR Server Adapter) + +pci:v00008086d000010C7sv00008086sd0000A15F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF SR Server Adapter) + +pci:v00008086d000010C7sv00008086sd0000A16F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF SR Server Adapter) + +pci:v00008086d000010C8* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT Network Connection + +pci:v00008086d000010C8sv00008086sd0000A10C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT Network Connection (10-Gigabit AT Server Adapter) + +pci:v00008086d000010C8sv00008086sd0000A11C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT Network Connection (10-Gigabit AT Server Adapter) + +pci:v00008086d000010C8sv00008086sd0000A12C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT Network Connection (10-Gigabit AT Server Adapter) + +pci:v00008086d000010C9* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection + +pci:v00008086d000010C9sv0000103Csd000031EF* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (NC362i Integrated Dual port Gigabit Server Adapter) + +pci:v00008086d000010C9sv0000103Csd0000323F* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (NC362i Integrated Dual port Gigabit Server Adapter) + +pci:v00008086d000010C9sv000010A9sd00008028* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (UV-BaseIO dual-port GbE) + +pci:v00008086d000010C9sv000013A3sd00000037* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (DS4100 Secure Multi-Gigabit Server Adapter with Compression) + +pci:v00008086d000010C9sv000015D9sd0000A811* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (H8DGU) + +pci:v00008086d000010C9sv00008086sd0000A01C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET Dual Port Server Adapter) + +pci:v00008086d000010C9sv00008086sd0000A03C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET Dual Port Server Adapter) + +pci:v00008086d000010C9sv00008086sd0000A04C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET Dual Port Server Adapter) + +pci:v00008086d000010CA* + ID_MODEL_FROM_DATABASE=82576 Virtual Function + +pci:v00008086d000010CB* + ID_MODEL_FROM_DATABASE=82567V Gigabit Network Connection + +pci:v00008086d000010CC* + ID_MODEL_FROM_DATABASE=82567LM-2 Gigabit Network Connection + +pci:v00008086d000010CD* + ID_MODEL_FROM_DATABASE=82567LF-2 Gigabit Network Connection + +pci:v00008086d000010CE* + ID_MODEL_FROM_DATABASE=82567V-2 Gigabit Network Connection + +pci:v00008086d000010D3* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection + +pci:v00008086d000010D3sv0000103Csd00001785* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (NC112i 1-port Ethernet Server Adapter) + +pci:v00008086d000010D3sv0000103Csd00003250* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (NC112T PCI Express single Port Gigabit Server Adapter) + +pci:v00008086d000010D3sv00001043sd00008369* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Motherboard) + +pci:v00008086d000010D3sv00001093sd000076E9* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (PCIe-8233 Ethernet Adapter) + +pci:v00008086d000010D3sv000010A9sd00008029* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Prism XL Single Port Gigabit Ethernet) + +pci:v00008086d000010D3sv000015D9sd0000060A* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (X7SPA-H/X7SPA-HF Motherboard) + +pci:v00008086d000010D3sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (C7SIM-Q Motherboard) + +pci:v00008086d000010D3sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Gigabit CT2 Desktop Adapter) + +pci:v00008086d000010D3sv00008086sd0000357A* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Server Board S1200BTS) + +pci:v00008086d000010D3sv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (Gigabit CT Desktop Adapter) + +pci:v00008086d000010D3sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (PC1-GROOVE) + +pci:v00008086d000010D3sv0000E4BFsd000050C2* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection (PC2-LIMBO) + +pci:v00008086d000010D4* + ID_MODEL_FROM_DATABASE=Matrox Concord GE (customized Intel 82574) + +pci:v00008086d000010D5* + ID_MODEL_FROM_DATABASE=82571PT Gigabit PT Quad Port Server ExpressModule + +pci:v00008086d000010D6* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection + +pci:v00008086d000010D6sv00008086sd000010D6* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection (Gigabit VT Quad Port Server Adapter) + +pci:v00008086d000010D6sv00008086sd0000145A* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection (Gigabit VT Quad Port Server Adapter) + +pci:v00008086d000010D6sv00008086sd0000147A* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection (Gigabit VT Quad Port Server Adapter) + +pci:v00008086d000010D8* + ID_MODEL_FROM_DATABASE=82599EB 10 Gigabit Unprogrammed + +pci:v00008086d000010D9* + ID_MODEL_FROM_DATABASE=82571EB Dual Port Gigabit Mezzanine Adapter + +pci:v00008086d000010D9sv0000103Csd00001716* + ID_MODEL_FROM_DATABASE=82571EB Dual Port Gigabit Mezzanine Adapter (NC360m Dual Port 1GbE BL-c Adapter) + +pci:v00008086d000010DA* + ID_MODEL_FROM_DATABASE=82571EB Quad Port Gigabit Mezzanine Adapter + +pci:v00008086d000010DAsv0000103Csd00001717* + ID_MODEL_FROM_DATABASE=82571EB Quad Port Gigabit Mezzanine Adapter (NC364m Quad Port 1GbE BL-c Adapter) + +pci:v00008086d000010DB* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit Dual Port Network Connection + +pci:v00008086d000010DD* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT CX4 Network Connection + +pci:v00008086d000010DE* + ID_MODEL_FROM_DATABASE=82567LM-3 Gigabit Network Connection + +pci:v00008086d000010DF* + ID_MODEL_FROM_DATABASE=82567LF-3 Gigabit Network Connection + +pci:v00008086d000010E1* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection + +pci:v00008086d000010E1sv00008086sd0000A15F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection (10-Gigabit SR Dual Port Express Module) + +pci:v00008086d000010E2* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection + +pci:v00008086d000010E2sv00008086sd000010E2* + ID_MODEL_FROM_DATABASE=82575GB Gigabit Network Connection (Gigabit VT Quad Port Server Adapter) + +pci:v00008086d000010E5* + ID_MODEL_FROM_DATABASE=82567LM-4 Gigabit Network Connection + +pci:v00008086d000010E6* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection + +pci:v00008086d000010E6sv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit EF Dual Port Server Adapter) + +pci:v00008086d000010E6sv00008086sd0000A02F* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit EF Dual Port Server Adapter) + +pci:v00008086d000010E7* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection + +pci:v00008086d000010E7sv0000103Csd000031FF* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (NC362i Integrated Dual Port BL-c Gigabit Server Adapter) + +pci:v00008086d000010E8* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection + +pci:v00008086d000010E8sv00008086sd0000A02B* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET Quad Port Server Adapter) + +pci:v00008086d000010E8sv00008086sd0000A02C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET Quad Port Server Adapter) + +pci:v00008086d000010EA* + ID_MODEL_FROM_DATABASE=82577LM Gigabit Network Connection + +pci:v00008086d000010EAsv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=82577LM Gigabit Network Connection (Latitude E6410) + +pci:v00008086d000010EAsv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=82577LM Gigabit Network Connection (Latitude E6510) + +pci:v00008086d000010EAsv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=82577LM Gigabit Network Connection (PC1-GROOVE) + +pci:v00008086d000010EB* + ID_MODEL_FROM_DATABASE=82577LC Gigabit Network Connection + +pci:v00008086d000010EC* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT CX4 Network Connection + +pci:v00008086d000010ECsv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT CX4 Network Connection (10-Gigabit CX4 Dual Port Server Adapter) + +pci:v00008086d000010ECsv00008086sd0000A11F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT CX4 Network Connection (10-Gigabit CX4 Dual Port Server Adapter) + +pci:v00008086d000010ED* + ID_MODEL_FROM_DATABASE=82599 Ethernet Controller Virtual Function + +pci:v00008086d000010EF* + ID_MODEL_FROM_DATABASE=82578DM Gigabit Network Connection + +pci:v00008086d000010EFsv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=82578DM Gigabit Network Connection (OptiPlex 980) + +pci:v00008086d000010EFsv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=82578DM Gigabit Network Connection (C7SIM-Q Motherboard) + +pci:v00008086d000010F0* + ID_MODEL_FROM_DATABASE=82578DC Gigabit Network Connection + +pci:v00008086d000010F1* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection + +pci:v00008086d000010F1sv00008086sd0000A20F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection (10-Gigabit AF DA Dual Port Server Adapter) + +pci:v00008086d000010F1sv00008086sd0000A21F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Dual Port Network Connection (10-Gigabit AF DA Dual Port Server Adapter) + +pci:v00008086d000010F4* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection + +pci:v00008086d000010F4sv00008086sd0000106F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF LR Server Adapter) + +pci:v00008086d000010F4sv00008086sd0000A06F* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AF Network Connection (10-Gigabit XF LR Server Adapter) + +pci:v00008086d000010F5* + ID_MODEL_FROM_DATABASE=82567LM Gigabit Network Connection + +pci:v00008086d000010F6* + ID_MODEL_FROM_DATABASE=82574L Gigabit Network Connection + +pci:v00008086d000010F7* + ID_MODEL_FROM_DATABASE=10 Gigabit BR KX4 Dual Port Network Connection + +pci:v00008086d000010F7sv0000108Esd00007B12* + ID_MODEL_FROM_DATABASE=10 Gigabit BR KX4 Dual Port Network Connection (Sun Dual 10GbE PCIe 2.0 FEM) + +pci:v00008086d000010F7sv00008086sd0000000D* + ID_MODEL_FROM_DATABASE=10 Gigabit BR KX4 Dual Port Network Connection (Ethernet Mezzanine Adapter X520-KX4-2) + +pci:v00008086d000010F8* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection + +pci:v00008086d000010F8sv00001028sd00001F63* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection (10GbE 2P X520k bNDC) + +pci:v00008086d000010F8sv0000103Csd000017D2* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection (Ethernet 10Gb 2-port 560M Adapter) + +pci:v00008086d000010F8sv0000103Csd000018D0* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection (Ethernet 10Gb 2-port 560FLB Adapter) + +pci:v00008086d000010F8sv00001059sd00000111* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection (T4007 10GbE interface) + +pci:v00008086d000010F8sv00008086sd0000000C* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection (Ethernet X520 10GbE Dual Port KX4-KR Mezz) + +pci:v00008086d000010F9* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Network Connection + +pci:v00008086d000010FB* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection + +pci:v00008086d000010FBsv00001028sd00001F72* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10G 4P X520/I350 rNDC) + +pci:v00008086d000010FBsv0000103Csd000017D0* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 2-port 560FLR-SFP+ Adapter) + +pci:v00008086d000010FBsv0000103Csd000017D2* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 2-port 560M Adapter) + +pci:v00008086d000010FBsv0000103Csd000017D3* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 2-port 560SFP+ Adapter) + +pci:v00008086d000010FBsv0000103Csd0000211B* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 1-port P560FLR-SFP+ Adapter) + +pci:v00008086d000010FBsv0000103Csd00002147* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 1-port 561i Adapter) + +pci:v00008086d000010FBsv0000103Csd00002159* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet 10Gb 2-port 562i Adapter) + +pci:v00008086d000010FBsv0000108Esd00007B11* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-2) + +pci:v00008086d000010FBsv00001734sd000011A9* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (10 Gigabit Dual Port Network Connection) + +pci:v00008086d000010FBsv000017AAsd00001071* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (ThinkServer X520-2 AnyFabric) + +pci:v00008086d000010FBsv000017AAsd00004007* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection + +pci:v00008086d000010FBsv000017AAsd0000402B* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (82599ES 10Gb 2-port Server Adapter X520-DA2) + +pci:v00008086d000010FBsv00008086sd00000002* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-DA2) + +pci:v00008086d000010FBsv00008086sd00000003* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-2) + +pci:v00008086d000010FBsv00008086sd00000006* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-1) + +pci:v00008086d000010FBsv00008086sd00000008* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet OCP Server Adapter X520-2) + +pci:v00008086d000010FBsv00008086sd0000000A* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-1) + +pci:v00008086d000010FBsv00008086sd0000000C* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-2) + +pci:v00008086d000010FBsv00008086sd00007A11* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-2) + +pci:v00008086d000010FBsv00008086sd00007A12* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (Ethernet Server Adapter X520-2) + +pci:v00008086d000010FC* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Network Connection + +pci:v00008086d000010FE* + ID_MODEL_FROM_DATABASE=82552 10/100 Network Connection + +pci:v00008086d00001107* + ID_MODEL_FROM_DATABASE=PRO/1000 MF Server Adapter (LX) + +pci:v00008086d00001130* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub + +pci:v00008086d00001130sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub (Travelmate 612 TX) + +pci:v00008086d00001130sv00001043sd00008027* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub (TUSL2-C Mainboard) + +pci:v00008086d00001130sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub (Vaio PCG-FX403) + +pci:v00008086d00001130sv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d00001130sv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82815 815 Chipset Host Bridge and Memory Controller Hub (D815EGEW Mainboard) + +pci:v00008086d00001131* + ID_MODEL_FROM_DATABASE=82815 815 Chipset AGP Bridge + +pci:v00008086d00001132* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) + +pci:v00008086d00001132sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (Travelmate 612 TX) + +pci:v00008086d00001132sv0000103Csd00002001* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (e-pc 40) + +pci:v00008086d00001132sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (Vaio PCG-FX403) + +pci:v00008086d00001132sv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d00001132sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (D815EEA Motherboard) + +pci:v00008086d00001132sv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82815 Chipset Graphics Controller (CGC) (D815EGEW Mainboard) + +pci:v00008086d00001161* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub Advanced Programmable Interrupt Controller + +pci:v00008086d00001161sv00008086sd00001161* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub Advanced Programmable Interrupt Controller (82806AA PCI64 Hub APIC) + +pci:v00008086d00001162* + ID_MODEL_FROM_DATABASE=Xscale 80200 Big Endian Companion Chip + +pci:v00008086d00001200* + ID_MODEL_FROM_DATABASE=IXP1200 Network Processor + +pci:v00008086d00001200sv0000172Asd00000000* + ID_MODEL_FROM_DATABASE=IXP1200 Network Processor (AEP SSL Accelerator) + +pci:v00008086d00001209* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller + +pci:v00008086d00001209sv0000140Bsd00000610* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller (PMC610 quad Ethernet board) + +pci:v00008086d00001209sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller (QEMU Virtual Machine) + +pci:v00008086d00001209sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller (CT7 mainboard) + +pci:v00008086d00001209sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller (CE7 mainboard) + +pci:v00008086d00001209sv00004C53sd00001070* + ID_MODEL_FROM_DATABASE=8255xER/82551IT Fast Ethernet Controller (PC6 mainboard) + +pci:v00008086d00001221* + ID_MODEL_FROM_DATABASE=82092AA PCI to PCMCIA Bridge + +pci:v00008086d00001222* + ID_MODEL_FROM_DATABASE=82092AA IDE Controller + +pci:v00008086d00001223* + ID_MODEL_FROM_DATABASE=SAA7116 + +pci:v00008086d00001225* + ID_MODEL_FROM_DATABASE=82452KX/GX [Orion] + +pci:v00008086d00001226* + ID_MODEL_FROM_DATABASE=82596 PRO/10 PCI + +pci:v00008086d00001227* + ID_MODEL_FROM_DATABASE=82865 EtherExpress PRO/100A + +pci:v00008086d00001228* + ID_MODEL_FROM_DATABASE=82556 EtherExpress PRO/100 Smart + +pci:v00008086d00001229* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 + +pci:v00008086d00001229sv00000E11sd00003001* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003002* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003003* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003004* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003005* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003006* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd00003007* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN*) + +pci:v00008086d00001229sv00000E11sd0000B01E* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3120 Fast Ethernet NIC) + +pci:v00008086d00001229sv00000E11sd0000B01F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3122 Fast Ethernet NIC (dual port)) + +pci:v00008086d00001229sv00000E11sd0000B02F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC1120 Ethernet NIC) + +pci:v00008086d00001229sv00000E11sd0000B04A* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (Netelligent 10/100TX NIC with Wake on LAN) + +pci:v00008086d00001229sv00000E11sd0000B0C6* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3161 Fast Ethernet NIC (embedded, WOL)) + +pci:v00008086d00001229sv00000E11sd0000B0C7* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3160 Fast Ethernet NIC (embedded)) + +pci:v00008086d00001229sv00000E11sd0000B0D7* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3121 Fast Ethernet NIC (WOL)) + +pci:v00008086d00001229sv00000E11sd0000B0DD* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3131 Fast Ethernet NIC (dual port)) + +pci:v00008086d00001229sv00000E11sd0000B0DE* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3132 Fast Ethernet Module (dual port)) + +pci:v00008086d00001229sv00000E11sd0000B0E1* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3133 Fast Ethernet Module (100-FX)) + +pci:v00008086d00001229sv00000E11sd0000B134* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3163 Fast Ethernet NIC (embedded, WOL)) + +pci:v00008086d00001229sv00000E11sd0000B13C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3162 Fast Ethernet NIC (embedded)) + +pci:v00008086d00001229sv00000E11sd0000B144* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3123 Fast Ethernet NIC (WOL)) + +pci:v00008086d00001229sv00000E11sd0000B163* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3134 Fast Ethernet NIC (dual port)) + +pci:v00008086d00001229sv00000E11sd0000B164* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC3135 Fast Ethernet Upgrade Module (dual port)) + +pci:v00008086d00001229sv00000E11sd0000B1A4* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NC7131 Gigabit Server Adapter) + +pci:v00008086d00001229sv00001014sd0000005C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82558B Ethernet Pro 10/100) + +pci:v00008086d00001229sv00001014sd000001BC* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LAN On Motherboard) + +pci:v00008086d00001229sv00001014sd000001F1* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Ethernet Server Adapter) + +pci:v00008086d00001229sv00001014sd000001F2* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Ethernet Server Adapter) + +pci:v00008086d00001229sv00001014sd00000207* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (Ethernet Pro/100 S) + +pci:v00008086d00001229sv00001014sd00000232* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Dual Port Server Adapter) + +pci:v00008086d00001229sv00001014sd0000023A* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (ThinkPad R30) + +pci:v00008086d00001229sv00001014sd0000105C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (Netfinity 10/100) + +pci:v00008086d00001229sv00001014sd00002205* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (ThinkPad A22p) + +pci:v00008086d00001229sv00001014sd0000305C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 EtherJet Management Adapter) + +pci:v00008086d00001229sv00001014sd0000405C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 EtherJet Adapter with Alert on LAN) + +pci:v00008086d00001229sv00001014sd0000505C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 EtherJet Secure Management Adapter) + +pci:v00008086d00001229sv00001014sd0000605C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 EtherJet Secure Management Adapter) + +pci:v00008086d00001229sv00001014sd0000705C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Netfinity 10/100 Ethernet Security Adapter) + +pci:v00008086d00001229sv00001014sd0000805C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Netfinity 10/100 Ethernet Security Adapter) + +pci:v00008086d00001229sv00001028sd0000009B* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Ethernet Server Adapter) + +pci:v00008086d00001229sv00001028sd000000CE* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (10/100 Ethernet Server Adapter) + +pci:v00008086d00001229sv00001033sd00008000* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PC-9821X-B06) + +pci:v00008086d00001229sv00001033sd00008016* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PK-UG-X006) + +pci:v00008086d00001229sv00001033sd0000801F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PK-UG-X006) + +pci:v00008086d00001229sv00001033sd00008026* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PK-UG-X006) + +pci:v00008086d00001229sv00001033sd00008063* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559-based Fast Ethernet Adapter) + +pci:v00008086d00001229sv00001033sd00008064* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559-based Fast Ethernet Adapter) + +pci:v00008086d00001229sv0000103Csd000010C0* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd000010C3* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd000010CA* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd000010CB* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd000010E3* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd000010E4* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000103Csd00001200* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NetServer 10/100TX) + +pci:v00008086d00001229sv0000108Esd000010CF* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100(B)) + +pci:v00008086d00001229sv000010C3sd00001100* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (SmartEther100 SC1100) + +pci:v00008086d00001229sv000010CFsd00001115* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (8255x-based Ethernet Adapter (10/100)) + +pci:v00008086d00001229sv000010CFsd00001143* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (8255x-based Ethernet Adapter (10/100)) + +pci:v00008086d00001229sv0000110Asd0000008B* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82551QM Fast Ethernet Multifuction PCI/CardBus Controller) + +pci:v00008086d00001229sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PC8 onboard ethernet ETH2) + +pci:v00008086d00001229sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (8255x-based Ethernet Adapter (10/100)) + +pci:v00008086d00001229sv00001179sd00000002* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PCI FastEther LAN on Docker) + +pci:v00008086d00001229sv00001179sd00000003* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (8255x-based Fast Ethernet) + +pci:v00008086d00001229sv00001259sd00002560* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (AT-2560 100) + +pci:v00008086d00001229sv00001259sd00002561* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (AT-2560 100 FX Ethernet Adapter) + +pci:v00008086d00001229sv00001266sd00000001* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (NE10/100 Adapter) + +pci:v00008086d00001229sv000013E9sd00001000* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (6221L-4U) + +pci:v00008086d00001229sv0000144Dsd00002501* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (SEM-2000 MiniPCI LAN Adapter) + +pci:v00008086d00001229sv0000144Dsd00002502* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (SEM-2100IL MiniPCI LAN Adapter) + +pci:v00008086d00001229sv00001668sd00001100* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100B (TX) (MiniPCI Ethernet+Modem)) + +pci:v00008086d00001229sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (CR11/VR11 Single Board Computer) + +pci:v00008086d00001229sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (CE9) + +pci:v00008086d00001229sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (QEMU Virtual Machine) + +pci:v00008086d00001229sv00004C53sd00001080* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (CT8 mainboard) + +pci:v00008086d00001229sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (PSL09 PrPMC) + +pci:v00008086d00001229sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100B (TX)) + +pci:v00008086d00001229sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100B (T4)) + +pci:v00008086d00001229sv00008086sd00000003* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/10+) + +pci:v00008086d00001229sv00008086sd00000004* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 WfM) + +pci:v00008086d00001229sv00008086sd00000005* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82557 10/100) + +pci:v00008086d00001229sv00008086sd00000006* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82557 10/100 with Wake on LAN) + +pci:v00008086d00001229sv00008086sd00000007* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82558 10/100 Adapter) + +pci:v00008086d00001229sv00008086sd00000008* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82558 10/100 with Wake on LAN) + +pci:v00008086d00001229sv00008086sd00000009* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82558B PRO/100+ PCI (TP)) + +pci:v00008086d00001229sv00008086sd0000000A* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Management Adapter) + +pci:v00008086d00001229sv00008086sd0000000B* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+) + +pci:v00008086d00001229sv00008086sd0000000C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Management Adapter) + +pci:v00008086d00001229sv00008086sd0000000D* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Alert On LAN II* Adapter) + +pci:v00008086d00001229sv00008086sd0000000E* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Management Adapter with Alert On LAN*) + +pci:v00008086d00001229sv00008086sd0000000F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00000010* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Management Adapter) + +pci:v00008086d00001229sv00008086sd00000011* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Management Adapter) + +pci:v00008086d00001229sv00008086sd00000012* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Advanced Management Adapter (D)) + +pci:v00008086d00001229sv00008086sd00000013* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Advanced Management Adapter (E)) + +pci:v00008086d00001229sv00008086sd00000030* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Management Adapter with Alert On LAN* GC) + +pci:v00008086d00001229sv00008086sd00000031* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00000040* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00000041* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00000042* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00000050* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Desktop Adapter) + +pci:v00008086d00001229sv00008086sd00001009* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Server Adapter) + +pci:v00008086d00001229sv00008086sd0000100C* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Server Adapter (PILA8470B)) + +pci:v00008086d00001229sv00008086sd00001012* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Server Adapter (D)) + +pci:v00008086d00001229sv00008086sd00001013* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Server Adapter (E)) + +pci:v00008086d00001229sv00008086sd00001015* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Dual Port Server Adapter) + +pci:v00008086d00001229sv00008086sd00001017* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Dual Port Server Adapter) + +pci:v00008086d00001229sv00008086sd00001030* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Management Adapter with Alert On LAN* G Server) + +pci:v00008086d00001229sv00008086sd00001040* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Server Adapter) + +pci:v00008086d00001229sv00008086sd00001041* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Server Adapter) + +pci:v00008086d00001229sv00008086sd00001042* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Server Adapter) + +pci:v00008086d00001229sv00008086sd00001050* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Server Adapter) + +pci:v00008086d00001229sv00008086sd00001051* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Server Adapter) + +pci:v00008086d00001229sv00008086sd00001052* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Server Adapter) + +pci:v00008086d00001229sv00008086sd000010F0* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ Dual Port Adapter) + +pci:v00008086d00001229sv00008086sd00001229* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82557/8/9 [Ethernet Pro 100]) + +pci:v00008086d00001229sv00008086sd00002009* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Mobile Adapter) + +pci:v00008086d00001229sv00008086sd0000200D* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Cardbus) + +pci:v00008086d00001229sv00008086sd0000200E* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 LAN+V90 Cardbus Modem) + +pci:v00008086d00001229sv00008086sd0000200F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SR Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002010* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002013* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SR Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002016* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002017* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Combo Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002018* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SR Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002019* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SR Combo Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002101* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 P Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002102* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002103* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002104* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002105* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002106* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 P Mobile Adapter) + +pci:v00008086d00001229sv00008086sd00002107* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Network Connection) + +pci:v00008086d00001229sv00008086sd00002108* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Network Connection) + +pci:v00008086d00001229sv00008086sd00002200* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 P Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002201* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 P Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002202* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002203* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002204* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002205* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002206* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002207* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 SP Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002208* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 P Mobile Combo Adapter) + +pci:v00008086d00001229sv00008086sd00002402* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002407* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002408* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002409* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd0000240F* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002410* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002411* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002412* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00002413* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100+ MiniPCI) + +pci:v00008086d00001229sv00008086sd00003000* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LAN on Motherboard) + +pci:v00008086d00001229sv00008086sd00003001* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Basic Alert on LAN*) + +pci:v00008086d00001229sv00008086sd00003002* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (82559 Fast Ethernet LOM with Alert on LAN II*) + +pci:v00008086d00001229sv00008086sd00003006* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Network Connection) + +pci:v00008086d00001229sv00008086sd00003007* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Network Connection) + +pci:v00008086d00001229sv00008086sd00003008* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Network Connection) + +pci:v00008086d00001229sv00008086sd00003010* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Network Connection) + +pci:v00008086d00001229sv00008086sd00003011* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 S Network Connection) + +pci:v00008086d00001229sv00008086sd00003012* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (EtherExpress PRO/100 Network Connection) + +pci:v00008086d00001229sv00008086sd0000301A* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (S845WD1-E mainboard) + +pci:v00008086d00001229sv00008086sd00003411* + ID_MODEL_FROM_DATABASE=82557/8/9/0/1 Ethernet Pro 100 (SDS2 Mainboard) + +pci:v00008086d0000122D* + ID_MODEL_FROM_DATABASE=430FX - 82437FX TSC [Triton I] + +pci:v00008086d0000122E* + ID_MODEL_FROM_DATABASE=82371FB PIIX ISA [Triton I] + +pci:v00008086d00001230* + ID_MODEL_FROM_DATABASE=82371FB PIIX IDE [Triton I] + +pci:v00008086d00001231* + ID_MODEL_FROM_DATABASE=DSVD Modem + +pci:v00008086d00001234* + ID_MODEL_FROM_DATABASE=430MX - 82371MX Mobile PCI I/O IDE Xcelerator (MPIIX) + +pci:v00008086d00001235* + ID_MODEL_FROM_DATABASE=430MX - 82437MX Mob. System Ctrlr (MTSC) & 82438MX Data Path (MTDP) + +pci:v00008086d00001237* + ID_MODEL_FROM_DATABASE=440FX - 82441FX PMC [Natoma] + +pci:v00008086d00001237sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=440FX - 82441FX PMC [Natoma] (Qemu virtual machine) + +pci:v00008086d00001239* + ID_MODEL_FROM_DATABASE=82371FB PIIX IDE Interface + +pci:v00008086d0000123B* + ID_MODEL_FROM_DATABASE=82380PB PCI to PCI Docking Bridge + +pci:v00008086d0000123C* + ID_MODEL_FROM_DATABASE=82380AB (MISA) Mobile PCI-to-ISA Bridge + +pci:v00008086d0000123D* + ID_MODEL_FROM_DATABASE=683053 Programmable Interrupt Device + +pci:v00008086d0000123E* + ID_MODEL_FROM_DATABASE=82466GX (IHPC) Integrated Hot-Plug Controller (hidden mode) + +pci:v00008086d0000123F* + ID_MODEL_FROM_DATABASE=82466GX Integrated Hot-Plug Controller (IHPC) + +pci:v00008086d00001240* + ID_MODEL_FROM_DATABASE=82752 (752) AGP Graphics Accelerator + +pci:v00008086d0000124B* + ID_MODEL_FROM_DATABASE=82380FB (MPCI2) Mobile Docking Controller + +pci:v00008086d00001250* + ID_MODEL_FROM_DATABASE=430HX - 82439HX TXC [Triton II] + +pci:v00008086d00001360* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub PCI Bridge + +pci:v00008086d00001361* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub Controller (HRes) + +pci:v00008086d00001361sv00008086sd00001361* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub Controller (HRes) + +pci:v00008086d00001361sv00008086sd00008000* + ID_MODEL_FROM_DATABASE=82806AA PCI64 Hub Controller (HRes) + +pci:v00008086d00001460* + ID_MODEL_FROM_DATABASE=82870P2 P64H2 Hub PCI Bridge + +pci:v00008086d00001461* + ID_MODEL_FROM_DATABASE=82870P2 P64H2 I/OxAPIC + +pci:v00008086d00001461sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82870P2 P64H2 I/OxAPIC (P4DP6) + +pci:v00008086d00001461sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82870P2 P64H2 I/OxAPIC (Cx9/Vx9 mainboard) + +pci:v00008086d00001462* + ID_MODEL_FROM_DATABASE=82870P2 P64H2 Hot Plug Controller + +pci:v00008086d00001501* + ID_MODEL_FROM_DATABASE=82567V-3 Gigabit Network Connection + +pci:v00008086d00001502* + ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection + +pci:v00008086d00001502sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection (Precision M4600) + +pci:v00008086d00001502sv00008086sd0000357A* + ID_MODEL_FROM_DATABASE=82579LM Gigabit Network Connection (Server Board S1200BTS) + +pci:v00008086d00001503* + ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection + +pci:v00008086d00001503sv00001043sd0000849C* + ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection (P8P67 Deluxe Motherboard) + +pci:v00008086d00001507* + ID_MODEL_FROM_DATABASE=Ethernet Express Module X520-P2 + +pci:v00008086d00001508* + ID_MODEL_FROM_DATABASE=82598EB Gigabit BX Network Connection + +pci:v00008086d0000150A* + ID_MODEL_FROM_DATABASE=82576NS Gigabit Network Connection + +pci:v00008086d0000150B* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT2 Server Adapter + +pci:v00008086d0000150Bsv00008086sd0000A10C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT2 Server Adapter + +pci:v00008086d0000150Bsv00008086sd0000A11C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT2 Server Adapter + +pci:v00008086d0000150Bsv00008086sd0000A12C* + ID_MODEL_FROM_DATABASE=82598EB 10-Gigabit AT2 Server Adapter + +pci:v00008086d0000150C* + ID_MODEL_FROM_DATABASE=82583V Gigabit Network Connection + +pci:v00008086d0000150D* + ID_MODEL_FROM_DATABASE=82576 Gigabit Backplane Connection + +pci:v00008086d0000150Dsv00008086sd0000A10C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Backplane Connection (Gigabit ET Quad Port Mezzanine Card) + +pci:v00008086d0000150E* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection + +pci:v00008086d0000150Esv0000103Csd00001780* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection (NC365T 4-port Ethernet Server Adapter) + +pci:v00008086d0000150Esv00008086sd000012A1* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection (Ethernet Server Adapter I340-T4) + +pci:v00008086d0000150Esv00008086sd000012A2* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection (Ethernet Server Adapter I340-T4) + +pci:v00008086d0000150F* + ID_MODEL_FROM_DATABASE=82580 Gigabit Fiber Network Connection + +pci:v00008086d00001510* + ID_MODEL_FROM_DATABASE=82580 Gigabit Backplane Connection + +pci:v00008086d00001511* + ID_MODEL_FROM_DATABASE=82580 Gigabit SFP Connection + +pci:v00008086d00001513* + ID_MODEL_FROM_DATABASE=CV82524 Thunderbolt Controller [Light Ridge 4C 2010] + +pci:v00008086d00001514* + ID_MODEL_FROM_DATABASE=Ethernet X520 10GbE Dual Port KX4 Mezz + +pci:v00008086d00001514sv00008086sd0000000B* + ID_MODEL_FROM_DATABASE=Ethernet X520 10GbE Dual Port KX4 Mezz + +pci:v00008086d00001515* + ID_MODEL_FROM_DATABASE=X540 Ethernet Controller Virtual Function + +pci:v00008086d00001516* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection + +pci:v00008086d00001516sv00008086sd000012B1* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection (Ethernet Server Adapter I340-T2) + +pci:v00008086d00001516sv00008086sd000012B2* + ID_MODEL_FROM_DATABASE=82580 Gigabit Network Connection (Ethernet Server Adapter I340-T2) + +pci:v00008086d00001517* + ID_MODEL_FROM_DATABASE=82599ES 10 Gigabit Network Connection + +pci:v00008086d00001517sv00001137sd0000006A* + ID_MODEL_FROM_DATABASE=82599ES 10 Gigabit Network Connection (UCS CNA M61KR-I Intel Converged Network Adapter) + +pci:v00008086d00001518* + ID_MODEL_FROM_DATABASE=82576NS SerDes Gigabit Network Connection + +pci:v00008086d0000151A* + ID_MODEL_FROM_DATABASE=DSL2310 Thunderbolt Controller [Eagle Ridge 2C 2011] + +pci:v00008086d0000151B* + ID_MODEL_FROM_DATABASE=CVL2510 Thunderbolt Controller [Light Peak 2C 2010] + +pci:v00008086d0000151C* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit TN Network Connection + +pci:v00008086d0000151Csv0000108Esd00007B13* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit TN Network Connection (Dual 10GBASE-T LP) + +pci:v00008086d00001520* + ID_MODEL_FROM_DATABASE=I350 Ethernet Controller Virtual Function + +pci:v00008086d00001521* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection + +pci:v00008086d00001521sv00001028sd00000602* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Gigabit 2P I350-t LOM) + +pci:v00008086d00001521sv00001028sd00001F60* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Gigabit 4P I350-t rNDC) + +pci:v00008086d00001521sv00001028sd00001F62* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Gigabit 4P X540/I350 rNDC) + +pci:v00008086d00001521sv00001028sd0000FF9A* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Gigabit 4P X710/I350 rNDC) + +pci:v00008086d00001521sv0000103Csd000017D1* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 4-port 366FLR Adapter) + +pci:v00008086d00001521sv0000103Csd00002003* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 2-port 367i Adapter) + +pci:v00008086d00001521sv0000103Csd00002226* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 1-port 364i Adapter) + +pci:v00008086d00001521sv0000103Csd0000337F* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 2-port 361i Adapter) + +pci:v00008086d00001521sv0000103Csd00003380* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 4-port 366i Adapter) + +pci:v00008086d00001521sv0000103Csd0000339E* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 2-port 361T Adapter) + +pci:v00008086d00001521sv0000103Csd00008157* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet 1Gb 4-port 366T Adapter) + +pci:v00008086d00001521sv0000108Esd00007B16* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Quad Port GbE PCIe 2.0 ExpressModule, UTP) + +pci:v00008086d00001521sv0000108Esd00007B18* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Quad Port GbE PCIe 2.0 Low Profile Adapter, UTP) + +pci:v00008086d00001521sv00001093sd00007648* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (PCIe-8237R Ethernet Adapter) + +pci:v00008086d00001521sv00001093sd00007649* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (PCIe-8236 Ethernet Adapter) + +pci:v00008086d00001521sv00001093sd000076B1* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (PCIe-8237R-S Ethernet Adapter) + +pci:v00008086d00001521sv00001093sd0000775B* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (PCIe-8237 Ethernet Adapter) + +pci:v00008086d00001521sv000010A9sd0000802A* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (UV2-BaseIO dual-port GbE) + +pci:v00008086d00001521sv000015D9sd00000652* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Dual Port i350 GbE MicroLP [AOC-CGP-i2]) + +pci:v00008086d00001521sv000017AAsd00001074* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (ThinkServer I350-T4 AnyFabric) + +pci:v00008086d00001521sv000017AAsd00004005* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection + +pci:v00008086d00001521sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T4) + +pci:v00008086d00001521sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T2) + +pci:v00008086d00001521sv00008086sd000000A1* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T4) + +pci:v00008086d00001521sv00008086sd000000A2* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T2) + +pci:v00008086d00001521sv00008086sd00005001* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T4) + +pci:v00008086d00001521sv00008086sd00005002* + ID_MODEL_FROM_DATABASE=I350 Gigabit Network Connection (Ethernet Server Adapter I350-T2) + +pci:v00008086d00001522* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection + +pci:v00008086d00001522sv0000108Esd00007B17* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Quad Port GbE PCIe 2.0 ExpressModule, MMF) + +pci:v00008086d00001522sv0000108Esd00007B19* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Dual Port GbE PCIe 2.0 Low Profile Adapter, MMF) + +pci:v00008086d00001522sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-T2) + +pci:v00008086d00001522sv00008086sd00000003* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-F4) + +pci:v00008086d00001522sv00008086sd00000004* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-F2) + +pci:v00008086d00001522sv00008086sd00000005* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-F1) + +pci:v00008086d00001522sv00008086sd000000A2* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-T2) + +pci:v00008086d00001522sv00008086sd000000A3* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-F4) + +pci:v00008086d00001522sv00008086sd000000A4* + ID_MODEL_FROM_DATABASE=I350 Gigabit Fiber Network Connection (Ethernet Server Adapter I350-F2) + +pci:v00008086d00001523* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection + +pci:v00008086d00001523sv00001028sd00000060* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Gigabit 2P I350 LOM) + +pci:v00008086d00001523sv00001028sd00001F9B* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Gigabit 4P I350-t bNDC) + +pci:v00008086d00001523sv0000103Csd00001784* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Ethernet 1Gb 2-port 361FLB Adapter) + +pci:v00008086d00001523sv0000103Csd000018D1* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Ethernet 1Gb 2-port 361FLB Adapter) + +pci:v00008086d00001523sv0000103Csd00001989* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Ethernet 1Gb 2-port 363i Adapter) + +pci:v00008086d00001523sv0000103Csd0000339F* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (Ethernet 1Gb 4-port 366M Adapter) + +pci:v00008086d00001523sv00008086sd00001F52* + ID_MODEL_FROM_DATABASE=I350 Gigabit Backplane Connection (1GbE 4P I350 Mezz) + +pci:v00008086d00001524* + ID_MODEL_FROM_DATABASE=I350 Gigabit Connection + +pci:v00008086d00001525* + ID_MODEL_FROM_DATABASE=82567V-4 Gigabit Network Connection + +pci:v00008086d00001526* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection + +pci:v00008086d00001526sv00008086sd0000A05C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET2 Quad Port Server Adapter) + +pci:v00008086d00001526sv00008086sd0000A06C* + ID_MODEL_FROM_DATABASE=82576 Gigabit Network Connection (Gigabit ET2 Quad Port Server Adapter) + +pci:v00008086d00001527* + ID_MODEL_FROM_DATABASE=82580 Gigabit Fiber Network Connection + +pci:v00008086d00001527sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=82580 Gigabit Fiber Network Connection (Ethernet Server Adapter I340-F4) + +pci:v00008086d00001527sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=82580 Gigabit Fiber Network Connection (Ethernet Server Adapter I340-F4) + +pci:v00008086d00001528* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 + +pci:v00008086d00001528sv00001028sd00001F61* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10G 4P X540/I350 rNDC) + +pci:v00008086d00001528sv0000103Csd0000192D* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (561FLR-T 2-port 10Gb Ethernet Adapter) + +pci:v00008086d00001528sv0000103Csd00002004* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10Gb 2-port 561i Adapter) + +pci:v00008086d00001528sv0000103Csd0000211A* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10Gb 2-port 561T Adapter) + +pci:v00008086d00001528sv0000108Esd00004853* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 + +pci:v00008086d00001528sv0000108Esd00007B14* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Sun Dual Port 10 GbE PCIe 2.0 ExpressModule, Base-T) + +pci:v00008086d00001528sv0000108Esd00007B15* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Sun Dual Port 10 GbE PCIe 2.0 Low Profile Adapter, Base-T) + +pci:v00008086d00001528sv00001137sd000000BF* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet Converged Network Adapter X540-T2) + +pci:v00008086d00001528sv000017AAsd00001073* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (ThinkServer X540-T2 AnyFabric) + +pci:v00008086d00001528sv000017AAsd00004006* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 + +pci:v00008086d00001528sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet Converged Network Adapter X540-T2) + +pci:v00008086d00001528sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet Converged Network Adapter X540-T1) + +pci:v00008086d00001528sv00008086sd0000001A* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet Converged Network Adapter X540-T2) + +pci:v00008086d00001528sv00008086sd000000A2* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet Converged Network Adapter X540-T1) + +pci:v00008086d00001528sv00008086sd00001F61* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10G 4P X540/I350 rNDC) + +pci:v00008086d00001528sv00008086sd00005003* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10G 2P X540-t Adapter) + +pci:v00008086d00001528sv00008086sd00005004* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (Ethernet 10G 2P X540-t Adapter) + +pci:v00008086d00001529* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Network Connection with FCoE + +pci:v00008086d0000152A* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Dual Port Backplane Connection with FCoE + +pci:v00008086d00001533* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection + +pci:v00008086d00001533sv0000103Csd00000003* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (Ethernet I210-T1 GbE NIC) + +pci:v00008086d00001533sv00001093sd00007706* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (Compact Vision System Ethernet Adapter) + +pci:v00008086d00001533sv000010A9sd0000802C* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (UV300 BaseIO single-port GbE) + +pci:v00008086d00001533sv000010A9sd0000802D* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (UV3000 BaseIO GbE Network) + +pci:v00008086d00001533sv000017AAsd00001100* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (ThinkServer Ethernet Server Adapter) + +pci:v00008086d00001533sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (Ethernet Server Adapter I210-T1) + +pci:v00008086d00001533sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection (Ethernet Server Adapter I210-T1) + +pci:v00008086d00001536* + ID_MODEL_FROM_DATABASE=I210 Gigabit Fiber Network Connection + +pci:v00008086d00001537* + ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection + +pci:v00008086d00001537sv00001059sd00000110* + ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection (T4005 1GbE interface) + +pci:v00008086d00001537sv00001059sd00000111* + ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection (T4007 1GbE interface) + +pci:v00008086d00001537sv00001059sd00000120* + ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection (T4008 1GbE interface) + +pci:v00008086d00001538* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection + +pci:v00008086d00001539* + ID_MODEL_FROM_DATABASE=I211 Gigabit Network Connection + +pci:v00008086d0000153A* + ID_MODEL_FROM_DATABASE=Ethernet Connection I217-LM + +pci:v00008086d0000153Asv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=Ethernet Connection I217-LM (ZBook 15) + +pci:v00008086d0000153Asv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=Ethernet Connection I217-LM (ThinkPad T440p) + +pci:v00008086d0000153B* + ID_MODEL_FROM_DATABASE=Ethernet Connection I217-V + +pci:v00008086d00001547* + ID_MODEL_FROM_DATABASE=DSL3510 Thunderbolt Controller [Cactus Ridge 4C 2012] + +pci:v00008086d00001548* + ID_MODEL_FROM_DATABASE=DSL3310 Thunderbolt Controller [Cactus Ridge 2C 2012] + +pci:v00008086d00001549* + ID_MODEL_FROM_DATABASE=DSL2210 Thunderbolt Controller [Port Ridge 1C 2011] + +pci:v00008086d0000154A* + ID_MODEL_FROM_DATABASE=Ethernet Server Adapter X520-4 + +pci:v00008086d0000154Asv00008086sd0000011A* + ID_MODEL_FROM_DATABASE=Ethernet Server Adapter X520-4 (Ethernet Converged Network Adapter X520-4) + +pci:v00008086d0000154Asv00008086sd0000011B* + ID_MODEL_FROM_DATABASE=Ethernet Server Adapter X520-4 (Ethernet Converged Network Adapter X520-4) + +pci:v00008086d0000154Asv00008086sd0000011C* + ID_MODEL_FROM_DATABASE=Ethernet Server Adapter X520-4 (Ethernet Converged Network Adapter X520-4) + +pci:v00008086d0000154C* + ID_MODEL_FROM_DATABASE=XL710/X710 Virtual Function + +pci:v00008086d0000154D* + ID_MODEL_FROM_DATABASE=Ethernet 10G 2P X520 Adapter + +pci:v00008086d0000154Dsv00008086sd00007B11* + ID_MODEL_FROM_DATABASE=Ethernet 10G 2P X520 Adapter (10GbE 2P X520 Adapter) + +pci:v00008086d00001557* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Network Connection + +pci:v00008086d00001557sv000017AAsd00004008* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Network Connection (82599EN 10 Gigabit Network Connection) + +pci:v00008086d00001557sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=82599 10 Gigabit Network Connection (Ethernet OCP Server Adapter X520-1) + +pci:v00008086d00001558* + ID_MODEL_FROM_DATABASE=Ethernet Converged Network Adapter X520-Q1 + +pci:v00008086d00001558sv00008086sd0000011A* + ID_MODEL_FROM_DATABASE=Ethernet Converged Network Adapter X520-Q1 + +pci:v00008086d00001558sv00008086sd0000011B* + ID_MODEL_FROM_DATABASE=Ethernet Converged Network Adapter X520-Q1 + +pci:v00008086d00001559* + ID_MODEL_FROM_DATABASE=Ethernet Connection I218-V + +pci:v00008086d0000155A* + ID_MODEL_FROM_DATABASE=Ethernet Connection I218-LM + +pci:v00008086d0000155Asv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=Ethernet Connection I218-LM (ThinkPad X240) + +pci:v00008086d0000155C* + ID_MODEL_FROM_DATABASE=Ethernet Server Bypass Adapter + +pci:v00008086d0000155Csv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Server Bypass Adapter (X540-T2) + +pci:v00008086d0000155D* + ID_MODEL_FROM_DATABASE=Ethernet Server Bypass Adapter + +pci:v00008086d0000155Dsv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Server Bypass Adapter (X520-SR2) + +pci:v00008086d0000155Dsv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Server Bypass Adapter (X520-LR2) + +pci:v00008086d00001560* + ID_MODEL_FROM_DATABASE=Ethernet Controller X540 + +pci:v00008086d00001563* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T + +pci:v00008086d00001563sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T2) + +pci:v00008086d00001563sv00008086sd0000001A* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T2) + +pci:v00008086d00001563sv00008086sd00000022* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T2) + +pci:v00008086d00001565* + ID_MODEL_FROM_DATABASE=X550 Virtual Function + +pci:v00008086d00001566* + ID_MODEL_FROM_DATABASE=DSL4410 Thunderbolt NHI [Redwood Ridge 2C 2013] + +pci:v00008086d00001567* + ID_MODEL_FROM_DATABASE=DSL4410 Thunderbolt Bridge [Redwood Ridge 2C 2013] + +pci:v00008086d00001568* + ID_MODEL_FROM_DATABASE=DSL4510 Thunderbolt NHI [Redwood Ridge 4C 2013] + +pci:v00008086d00001569* + ID_MODEL_FROM_DATABASE=DSL4510 Thunderbolt Bridge [Redwood Ridge 4C 2013] + +pci:v00008086d0000156A* + ID_MODEL_FROM_DATABASE=DSL5320 Thunderbolt 2 NHI [Falcon Ridge 2C 2013] + +pci:v00008086d0000156B* + ID_MODEL_FROM_DATABASE=DSL5320 Thunderbolt 2 Bridge [Falcon Ridge 2C 2013] + +pci:v00008086d0000156C* + ID_MODEL_FROM_DATABASE=DSL5520 Thunderbolt 2 NHI [Falcon Ridge 4C 2013] + +pci:v00008086d0000156D* + ID_MODEL_FROM_DATABASE=DSL5520 Thunderbolt 2 Bridge [Falcon Ridge 4C 2013] + +pci:v00008086d0000156F* + ID_MODEL_FROM_DATABASE=Ethernet Connection I219-LM + +pci:v00008086d00001570* + ID_MODEL_FROM_DATABASE=Ethernet Connection I219-V + +pci:v00008086d00001571* + ID_MODEL_FROM_DATABASE=XL710/X710 Virtual Function + +pci:v00008086d00001572* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ + +pci:v00008086d00001572sv00001028sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10G X710 rNDC) + +pci:v00008086d00001572sv00001028sd00001F99* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10G 4P X710/I350 rNDC) + +pci:v00008086d00001572sv00001028sd00001F9C* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10G 4P X710 SFP+ rNDC) + +pci:v00008086d00001572sv0000103Csd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10Gb 562SFP+ Adapter) + +pci:v00008086d00001572sv0000103Csd000022FC* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (HP Ethernet 10Gb 2-port 562FLR-SFP+ Adapter) + +pci:v00008086d00001572sv0000103Csd000022FD* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (HP Ethernet 10Gb 2-port 562SFP+ Adapter) + +pci:v00008086d00001572sv00001137sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged NIC X710-4) + +pci:v00008086d00001572sv00001137sd0000013B* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged NIC X710-4) + +pci:v00008086d00001572sv000017AAsd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (ThinkServer X710 AnyFabric for 10GbE SFP+) + +pci:v00008086d00001572sv000017AAsd00004001* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (ThinkServer X710-4 AnyFabric for 10GbE SFP+) + +pci:v00008086d00001572sv000017AAsd00004002* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (ThinkServer X710-2 AnyFabric for 10GbE SFP+) + +pci:v00008086d00001572sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710) + +pci:v00008086d00001572sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710-4) + +pci:v00008086d00001572sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710-4) + +pci:v00008086d00001572sv00008086sd00000004* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710-4) + +pci:v00008086d00001572sv00008086sd00000005* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10G 4P X710 Adapter) + +pci:v00008086d00001572sv00008086sd00000006* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet 10G 2P X710 Adapter) + +pci:v00008086d00001572sv00008086sd00000007* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710-2) + +pci:v00008086d00001572sv00008086sd00000008* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710-2) + +pci:v00008086d00001572sv00008086sd00000009* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ + +pci:v00008086d00001572sv00008086sd0000000A* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ + +pci:v00008086d00001572sv00008086sd0000000B* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Server Adapter X710-DA2 for OCP) + +pci:v00008086d00001572sv00008086sd0000000D* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ + +pci:v00008086d00001572sv00008086sd00000010* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Converged Network Adapter X710) + +pci:v00008086d00001572sv00008086sd00004005* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ (Ethernet Controller XL710 for 10 Gigabit SFP+) + +pci:v00008086d00001572sv00008086sd00004006* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE SFP+ + +pci:v00008086d00001575* + ID_MODEL_FROM_DATABASE=DSL6340 Thunderbolt 3 NHI [Alpine Ridge 2C 2015] + +pci:v00008086d00001576* + ID_MODEL_FROM_DATABASE=DSL6340 Thunderbolt 3 Bridge [Alpine Ridge 2C 2015] + +pci:v00008086d00001577* + ID_MODEL_FROM_DATABASE=DSL6540 Thunderbolt 3 NHI [Alpine Ridge 4C 2015] + +pci:v00008086d00001578* + ID_MODEL_FROM_DATABASE=DSL6540 Thunderbolt 3 Bridge [Alpine Ridge 4C 2015] + +pci:v00008086d0000157B* + ID_MODEL_FROM_DATABASE=I210 Gigabit Network Connection + +pci:v00008086d0000157C* + ID_MODEL_FROM_DATABASE=I210 Gigabit Backplane Connection + +pci:v00008086d0000157D* + ID_MODEL_FROM_DATABASE=DSL5110 Thunderbolt 2 NHI (Low Power) [Win Ridge 2C 2014] + +pci:v00008086d0000157E* + ID_MODEL_FROM_DATABASE=DSL5110 Thunderbolt 2 Bridge (Low Power) [Win Ridge 2C 2014] + +pci:v00008086d00001580* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE backplane + +pci:v00008086d00001581* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane + +pci:v00008086d00001581sv00001028sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 10G X710-k bNDC) + +pci:v00008086d00001581sv00001028sd00001F98* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 10G 4P X710-k bNDC) + +pci:v00008086d00001581sv00001028sd00001F9E* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 10G 2P X710-k bNDC) + +pci:v00008086d00001581sv00001590sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 2-port 563i Adapter) + +pci:v00008086d00001581sv00001590sd000000F8* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet 2-port 563i Adapter) + +pci:v00008086d00001581sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE backplane (Ethernet Converged Network Adapter XL710-Q2) + +pci:v00008086d00001583* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ + +pci:v00008086d00001583sv00001028sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet 40G 2P XL710 QSFP+ rNDC) + +pci:v00008086d00001583sv00001028sd00001F9F* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet 40G 2P XL710 QSFP+ rNDC) + +pci:v00008086d00001583sv0000108Esd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (10 Gb/40 Gb Ethernet Adapter) + +pci:v00008086d00001583sv0000108Esd00007B1B* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (10 Gb/40 Gb Ethernet Adapter) + +pci:v00008086d00001583sv00001137sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged NIC XL710-QDA2) + +pci:v00008086d00001583sv00001137sd0000013C* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged NIC XL710-QDA2) + +pci:v00008086d00001583sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q2) + +pci:v00008086d00001583sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q2) + +pci:v00008086d00001583sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q2) + +pci:v00008086d00001583sv00008086sd00000003* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet I/O Module XL710-Q2) + +pci:v00008086d00001583sv00008086sd00000004* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Server Adapter XL710-Q2OCP) + +pci:v00008086d00001583sv00008086sd00000006* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q2) + +pci:v00008086d00001584* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ + +pci:v00008086d00001584sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q1) + +pci:v00008086d00001584sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q1) + +pci:v00008086d00001584sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Converged Network Adapter XL710-Q1) + +pci:v00008086d00001584sv00008086sd00000003* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet I/O Module XL710-Q1) + +pci:v00008086d00001584sv00008086sd00000004* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 40GbE QSFP+ (Ethernet Server Adapter XL710-Q1OCP) + +pci:v00008086d00001585* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GbE QSFP+ + +pci:v00008086d00001586* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T + +pci:v00008086d00001586sv0000108Esd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T + +pci:v00008086d00001586sv0000108Esd00004857* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T + +pci:v00008086d00001587* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane + +pci:v00008086d00001587sv0000103Csd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane (HP Flex-20 20Gb 2-port 660FLB Adapter) + +pci:v00008086d00001587sv0000103Csd000022FE* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane (HP Flex-20 20Gb 2-port 660FLB Adapter) + +pci:v00008086d00001588* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane + +pci:v00008086d00001588sv0000103Csd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane (HP Flex-20 20Gb 2-port 660M Adapter) + +pci:v00008086d00001588sv0000103Csd000022FF* + ID_MODEL_FROM_DATABASE=Ethernet Controller XL710 for 20GbE backplane (HP Flex-20 20Gb 2-port 660M Adapter) + +pci:v00008086d00001589* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T + +pci:v00008086d00001589sv0000108Esd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Quad Port 10GBase-T Adapter) + +pci:v00008086d00001589sv0000108Esd00007B1C* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Quad Port 10GBase-T Adapter) + +pci:v00008086d00001589sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Ethernet Converged Network Adapter X710-T) + +pci:v00008086d00001589sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Ethernet Converged Network Adapter X710-T4) + +pci:v00008086d00001589sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Ethernet Converged Network Adapter X710-T4) + +pci:v00008086d00001589sv00008086sd00001003* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710/X557-AT 10GBASE-T (Ethernet Converged Network Adapter X710-T) + +pci:v00008086d000015A0* + ID_MODEL_FROM_DATABASE=Ethernet Connection (2) I218-LM + +pci:v00008086d000015A1* + ID_MODEL_FROM_DATABASE=Ethernet Connection (2) I218-V + +pci:v00008086d000015A2* + ID_MODEL_FROM_DATABASE=Ethernet Connection (3) I218-LM + +pci:v00008086d000015A3* + ID_MODEL_FROM_DATABASE=Ethernet Connection (3) I218-V + +pci:v00008086d000015A4* + ID_MODEL_FROM_DATABASE=Ethernet Switch FM10000 Host Interface + +pci:v00008086d000015A5* + ID_MODEL_FROM_DATABASE=Ethernet Switch FM10000 Host Virtual Interface + +pci:v00008086d000015A8* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 Virtual Function + +pci:v00008086d000015AA* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane + +pci:v00008086d000015AAsv00001059sd00000120* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane (T4008 10GbE interface) + +pci:v00008086d000015AB* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE Backplane + +pci:v00008086d000015AC* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 10 GbE SFP+ + +pci:v00008086d000015AD* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552/X557-AT 10GBASE-T + +pci:v00008086d000015AE* + ID_MODEL_FROM_DATABASE=Ethernet Connection X552 1000BASE-T + +pci:v00008086d000015B5* + ID_MODEL_FROM_DATABASE=DSL6340 USB 3.1 Controller [Alpine Ridge] + +pci:v00008086d000015B6* + ID_MODEL_FROM_DATABASE=DSL6540 USB 3.1 Controller [Alpine Ridge] + +pci:v00008086d000015B7* + ID_MODEL_FROM_DATABASE=Ethernet Connection (2) I219-LM + +pci:v00008086d000015B8* + ID_MODEL_FROM_DATABASE=Ethernet Connection (2) I219-V + +pci:v00008086d000015B9* + ID_MODEL_FROM_DATABASE=Ethernet Connection (3) I219-LM + +pci:v00008086d000015BF* + ID_MODEL_FROM_DATABASE=JHL6240 Thunderbolt 3 NHI (Low Power) [Alpine Ridge LP 2016] + +pci:v00008086d000015C0* + ID_MODEL_FROM_DATABASE=JHL6240 Thunderbolt 3 Bridge (Low Power) [Alpine Ridge LP 2016] + +pci:v00008086d000015D0* + ID_MODEL_FROM_DATABASE=Ethernet SDI Adapter FM10420-100GbE-QDA2 + +pci:v00008086d000015D1* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T + +pci:v00008086d000015D1sv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T1) + +pci:v00008086d000015D1sv00008086sd00000021* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T1) + +pci:v00008086d000015D1sv00008086sd000000A2* + ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T1) + +pci:v00008086d000015D2* + ID_MODEL_FROM_DATABASE=JHL6540 Thunderbolt 3 NHI (C step) [Alpine Ridge 4C 2016] + +pci:v00008086d000015D3* + ID_MODEL_FROM_DATABASE=JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] + +pci:v00008086d000015D5* + ID_MODEL_FROM_DATABASE=Ethernet SDI Adapter FM10420-25GbE-DA2 + +pci:v00008086d000015D5sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet SDI Adapter FM10420-25GbE-DA2 (Intel(R) Ethernet SDI Adapter FM10420-25GbE-DA2) + +pci:v00008086d000015D6* + ID_MODEL_FROM_DATABASE=Ethernet Connection (5) I219-V + +pci:v00008086d000015D7* + ID_MODEL_FROM_DATABASE=Ethernet Connection (4) I219-LM + +pci:v00008086d000015D8* + ID_MODEL_FROM_DATABASE=Ethernet Connection (4) I219-V + +pci:v00008086d000015D9* + ID_MODEL_FROM_DATABASE=JHL6340 Thunderbolt 3 NHI (C step) [Alpine Ridge 2C 2016] + +pci:v00008086d000015DA* + ID_MODEL_FROM_DATABASE=JHL6340 Thunderbolt 3 Bridge (C step) [Alpine Ridge 2C 2016] + +pci:v00008086d000015E3* + ID_MODEL_FROM_DATABASE=Ethernet Connection (5) I219-LM + +pci:v00008086d00001600* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge -OPI + +pci:v00008086d00001601* + ID_MODEL_FROM_DATABASE=Broadwell-U PCI Express x16 Controller + +pci:v00008086d00001602* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d00001603* + ID_MODEL_FROM_DATABASE=Broadwell-U Processor Thermal Subsystem + +pci:v00008086d00001604* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge -OPI + +pci:v00008086d00001605* + ID_MODEL_FROM_DATABASE=Broadwell-U PCI Express x8 Controller + +pci:v00008086d00001606* + ID_MODEL_FROM_DATABASE=HD Graphics + +pci:v00008086d00001607* + ID_MODEL_FROM_DATABASE=Broadwell-U CHAPS Device + +pci:v00008086d00001608* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge -OPI + +pci:v00008086d00001609* + ID_MODEL_FROM_DATABASE=Broadwell-U x4 PCIe + +pci:v00008086d0000160A* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000160B* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000160C* + ID_MODEL_FROM_DATABASE=Broadwell-U Audio Controller + +pci:v00008086d0000160D* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000160E* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000160F* + ID_MODEL_FROM_DATABASE=Broadwell-U SoftSKU + +pci:v00008086d00001610* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge - DMI + +pci:v00008086d00001612* + ID_MODEL_FROM_DATABASE=HD Graphics 5600 + +pci:v00008086d00001614* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge - DMI + +pci:v00008086d00001616* + ID_MODEL_FROM_DATABASE=HD Graphics 5500 + +pci:v00008086d00001616sv0000103Csd00002216* + ID_MODEL_FROM_DATABASE=HD Graphics 5500 (ZBook 15u G2 Mobile Workstation) + +pci:v00008086d00001618* + ID_MODEL_FROM_DATABASE=Broadwell-U Host Bridge - DMI + +pci:v00008086d0000161A* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000161B* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000161D* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000161E* + ID_MODEL_FROM_DATABASE=HD Graphics 5300 + +pci:v00008086d00001622* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics 6200 + +pci:v00008086d00001626* + ID_MODEL_FROM_DATABASE=HD Graphics 6000 + +pci:v00008086d0000162A* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics P6300 + +pci:v00008086d0000162B* + ID_MODEL_FROM_DATABASE=Iris Graphics 6100 + +pci:v00008086d0000162D* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000162E* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d00001632* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d00001636* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000163A* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000163B* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000163D* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d0000163E* + ID_MODEL_FROM_DATABASE=Broadwell-U Integrated Graphics + +pci:v00008086d00001900* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001901* + ID_MODEL_FROM_DATABASE=Skylake PCIe Controller (x16) + +pci:v00008086d00001902* + ID_MODEL_FROM_DATABASE=HD Graphics 510 + +pci:v00008086d00001903* + ID_MODEL_FROM_DATABASE=Skylake Processor Thermal Subsystem + +pci:v00008086d00001904* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001904sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers (Latitude 3570) + +pci:v00008086d00001905* + ID_MODEL_FROM_DATABASE=Skylake PCIe Controller (x8) + +pci:v00008086d00001906* + ID_MODEL_FROM_DATABASE=HD Graphics 510 + +pci:v00008086d00001908* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001909* + ID_MODEL_FROM_DATABASE=Skylake PCIe Controller (x4) + +pci:v00008086d0000190C* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d0000190F* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001910* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001911* + ID_MODEL_FROM_DATABASE=Skylake Gaussian Mixture Model + +pci:v00008086d00001912* + ID_MODEL_FROM_DATABASE=HD Graphics 530 + +pci:v00008086d00001916* + ID_MODEL_FROM_DATABASE=HD Graphics 520 + +pci:v00008086d00001916sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=HD Graphics 520 (Latitude 3570) + +pci:v00008086d00001918* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001919* + ID_MODEL_FROM_DATABASE=Skylake Imaging Unit + +pci:v00008086d0000191B* + ID_MODEL_FROM_DATABASE=HD Graphics 530 + +pci:v00008086d0000191D* + ID_MODEL_FROM_DATABASE=HD Graphics P530 + +pci:v00008086d0000191E* + ID_MODEL_FROM_DATABASE=HD Graphics 515 + +pci:v00008086d0000191F* + ID_MODEL_FROM_DATABASE=Skylake Host Bridge/DRAM Registers + +pci:v00008086d00001921* + ID_MODEL_FROM_DATABASE=HD Graphics 520 + +pci:v00008086d00001926* + ID_MODEL_FROM_DATABASE=Iris Graphics 540 + +pci:v00008086d00001927* + ID_MODEL_FROM_DATABASE=Iris Graphics 550 + +pci:v00008086d0000192B* + ID_MODEL_FROM_DATABASE=Iris Graphics 555 + +pci:v00008086d0000192D* + ID_MODEL_FROM_DATABASE=Iris Graphics P555 + +pci:v00008086d00001932* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics 580 + +pci:v00008086d0000193A* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics P580 + +pci:v00008086d0000193B* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics 580 + +pci:v00008086d0000193D* + ID_MODEL_FROM_DATABASE=Iris Pro Graphics P580 + +pci:v00008086d00001960* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor + +pci:v00008086d00001960sv0000101Esd00000431* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 431 RAID Controller) + +pci:v00008086d00001960sv0000101Esd00000438* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 438 Ultra2 LVD RAID Controller) + +pci:v00008086d00001960sv0000101Esd00000466* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 466 Express Plus RAID Controller) + +pci:v00008086d00001960sv0000101Esd00000467* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 467 Enterprise 1500 RAID Controller) + +pci:v00008086d00001960sv0000101Esd00000490* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 490 Express 300 RAID Controller) + +pci:v00008086d00001960sv0000101Esd00000762* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 762 Express RAID Controller) + +pci:v00008086d00001960sv0000101Esd000009A0* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (PowerEdge Expandable RAID Controller 2/SC) + +pci:v00008086d00001960sv00001028sd00000467* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (PowerEdge Expandable RAID Controller 2/DC) + +pci:v00008086d00001960sv00001028sd00001111* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (PowerEdge Expandable RAID Controller 2/SC) + +pci:v00008086d00001960sv0000103Csd000003A2* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID) + +pci:v00008086d00001960sv0000103Csd000010C6* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 438, NetRAID-3Si) + +pci:v00008086d00001960sv0000103Csd000010C7* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID T5, Integrated NetRAID) + +pci:v00008086d00001960sv0000103Csd000010CC* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID, Integrated NetRAID) + +pci:v00008086d00001960sv0000103Csd000010CD* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (NetRAID-1Si) + +pci:v00008086d00001960sv0000105Asd00000000* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (SuperTrak) + +pci:v00008086d00001960sv0000105Asd00002168* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (SuperTrak Pro) + +pci:v00008086d00001960sv0000105Asd00005168* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (SuperTrak66/100) + +pci:v00008086d00001960sv00001111sd00001111* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID 466, PowerEdge Expandable RAID Controller 2/SC) + +pci:v00008086d00001960sv00001111sd00001112* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (PowerEdge Expandable RAID Controller 2/SC) + +pci:v00008086d00001960sv0000113Csd000003A2* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (MegaRAID) + +pci:v00008086d00001960sv0000E4BFsd00001010* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (CG1-RADIO) + +pci:v00008086d00001960sv0000E4BFsd00001020* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (CU2-QUARTET) + +pci:v00008086d00001960sv0000E4BFsd00001040* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (CU1-CHORUS) + +pci:v00008086d00001960sv0000E4BFsd00003100* + ID_MODEL_FROM_DATABASE=80960RP (i960RP) Microprocessor (CX1-BAND) + +pci:v00008086d00001962* + ID_MODEL_FROM_DATABASE=80960RM (i960RM) Microprocessor + +pci:v00008086d00001962sv0000105Asd00000000* + ID_MODEL_FROM_DATABASE=80960RM (i960RM) Microprocessor (SuperTrak SX6000 I2O CPU) + +pci:v00008086d000019DF* + ID_MODEL_FROM_DATABASE=DNV SMBus controller + +pci:v00008086d00001A21* + ID_MODEL_FROM_DATABASE=82840 840 [Carmel] Chipset Host Bridge (Hub A) + +pci:v00008086d00001A23* + ID_MODEL_FROM_DATABASE=82840 840 [Carmel] Chipset AGP Bridge + +pci:v00008086d00001A24* + ID_MODEL_FROM_DATABASE=82840 840 [Carmel] Chipset PCI Bridge (Hub B) + +pci:v00008086d00001A30* + ID_MODEL_FROM_DATABASE=82845 845 [Brookdale] Chipset Host Bridge + +pci:v00008086d00001A30sv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=82845 845 [Brookdale] Chipset Host Bridge (Optiplex GX240) + +pci:v00008086d00001A30sv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82845 845 [Brookdale] Chipset Host Bridge (BL7 motherboard) + +pci:v00008086d00001A30sv000015D9sd00003280* + ID_MODEL_FROM_DATABASE=82845 845 [Brookdale] Chipset Host Bridge (Supermicro P4SBE Mainboard) + +pci:v00008086d00001A31* + ID_MODEL_FROM_DATABASE=82845 845 [Brookdale] Chipset AGP Bridge + +pci:v00008086d00001A38* + ID_MODEL_FROM_DATABASE=5000 Series Chipset DMA Engine + +pci:v00008086d00001A38sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset DMA Engine (X7DVL-E-O motherboard) + +pci:v00008086d00001A38sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset DMA Engine (Intel S5000PSLSATA Server Board) + +pci:v00008086d00001A48* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller + +pci:v00008086d00001A48sv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE SR Server Adapter) + +pci:v00008086d00001A48sv00008086sd0000A11F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE SR Server Adapter) + +pci:v00008086d00001B48* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller + +pci:v00008086d00001B48sv00008086sd0000A01F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE LR Server Adapter) + +pci:v00008086d00001B48sv00008086sd0000A11F* + ID_MODEL_FROM_DATABASE=82597EX 10GbE Ethernet Controller (PRO/10GbE LR Server Adapter) + +pci:v00008086d00001C00* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 4 port SATA IDE Controller + +pci:v00008086d00001C01* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 4 port SATA IDE Controller + +pci:v00008086d00001C02* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA AHCI Controller + +pci:v00008086d00001C02sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA AHCI Controller (XPS 8300) + +pci:v00008086d00001C02sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA AHCI Controller (P8 series motherboard) + +pci:v00008086d00001C02sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA AHCI Controller (Server Board S1200BTS) + +pci:v00008086d00001C03* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller + +pci:v00008086d00001C03sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (Precision M4600) + +pci:v00008086d00001C03sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (Vostro 3350) + +pci:v00008086d00001C03sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (Vostro 3750) + +pci:v00008086d00001C03sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 6 port SATA AHCI Controller (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C04* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA RAID Controller + +pci:v00008086d00001C04sv0000103Csd00003118* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA RAID Controller (Smart Array B110i SATA RAID Controller) + +pci:v00008086d00001C05* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SATA RAID Controller + +pci:v00008086d00001C08* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 2 port SATA IDE Controller + +pci:v00008086d00001C09* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family 2 port SATA IDE Controller + +pci:v00008086d00001C10* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 1 + +pci:v00008086d00001C10sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 1 (XPS 8300) + +pci:v00008086d00001C10sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 1 (Vostro 3750) + +pci:v00008086d00001C10sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 1 (P8 series motherboard) + +pci:v00008086d00001C10sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 1 (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2) + +pci:v00008086d00001C12* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 2 + +pci:v00008086d00001C12sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 2 (XPS 8300) + +pci:v00008086d00001C12sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 2 (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C14* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 3 + +pci:v00008086d00001C14sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 3 (Vostro 3750) + +pci:v00008086d00001C14sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 3 (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C16* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 4 + +pci:v00008086d00001C16sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 4 (XPS 8300) + +pci:v00008086d00001C18* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 5 + +pci:v00008086d00001C18sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 5 (Vostro 3750) + +pci:v00008086d00001C18sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 5 (Server Board S1200BTS) + +pci:v00008086d00001C1A* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 6 + +pci:v00008086d00001C1Asv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 6 (Vostro 3750) + +pci:v00008086d00001C1Asv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 6 (P8 series motherboard) + +pci:v00008086d00001C1C* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 7 + +pci:v00008086d00001C1E* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 8 + +pci:v00008086d00001C1Esv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 8 (P8 series motherboard) + +pci:v00008086d00001C20* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller + +pci:v00008086d00001C20sv00001028sd00000490* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (Alienware M17x R3) + +pci:v00008086d00001C20sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (Precision M4600) + +pci:v00008086d00001C20sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (XPS 8300) + +pci:v00008086d00001C20sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (Vostro 3350) + +pci:v00008086d00001C20sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (Vostro 3750) + +pci:v00008086d00001C20sv00001043sd00008418* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (P8P67 Deluxe Motherboard) + +pci:v00008086d00001C20sv00001043sd0000841B* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (P8H67 Series Motherboard) + +pci:v00008086d00001C20sv00008086sd00002008* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (DQ67SW board) + +pci:v00008086d00001C20sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family High Definition Audio Controller (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C22* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller + +pci:v00008086d00001C22sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Precision M4600) + +pci:v00008086d00001C22sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (XPS 8300) + +pci:v00008086d00001C22sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Vostro 3350) + +pci:v00008086d00001C22sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Vostro 3750) + +pci:v00008086d00001C22sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (P8 series motherboard) + +pci:v00008086d00001C22sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family SMBus Controller (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2) + +pci:v00008086d00001C24* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family Thermal Management Controller + +pci:v00008086d00001C25* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family DMI to PCI Bridge + +pci:v00008086d00001C26* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 + +pci:v00008086d00001C26sv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Precision M4600) + +pci:v00008086d00001C26sv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (XPS 8300) + +pci:v00008086d00001C26sv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Vostro 3350) + +pci:v00008086d00001C26sv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Vostro 3750) + +pci:v00008086d00001C26sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (P8 series motherboard) + +pci:v00008086d00001C26sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #1 (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2) + +pci:v00008086d00001C27* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Universal Host Controller #1 + +pci:v00008086d00001C27sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Universal Host Controller #1 (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C2C* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Universal Host Controller #5 + +pci:v00008086d00001C2Csv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Universal Host Controller #5 (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C2D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 + +pci:v00008086d00001C2Dsv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Precision M4600) + +pci:v00008086d00001C2Dsv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (XPS 8300) + +pci:v00008086d00001C2Dsv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Vostro 3350) + +pci:v00008086d00001C2Dsv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Vostro 3750) + +pci:v00008086d00001C2Dsv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (P8 series motherboard) + +pci:v00008086d00001C2Dsv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family USB Enhanced Host Controller #2 (Server Board S1200BTS / Apple MacBook Pro 8,1/8,2) + +pci:v00008086d00001C33* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LAN Controller + +pci:v00008086d00001C35* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family VECI Controller + +pci:v00008086d00001C3A* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 + +pci:v00008086d00001C3Asv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (Precision M4600) + +pci:v00008086d00001C3Asv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (XPS 8300) + +pci:v00008086d00001C3Asv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (Vostro 3350) + +pci:v00008086d00001C3Asv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (Vostro 3750) + +pci:v00008086d00001C3Asv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (P8 series motherboard) + +pci:v00008086d00001C3Asv00008086sd00007270* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #1 (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C3B* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family MEI Controller #2 + +pci:v00008086d00001C3C* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family IDE-r Controller + +pci:v00008086d00001C3D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family KT Controller + +pci:v00008086d00001C40* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C41* + ID_MODEL_FROM_DATABASE=Mobile SFF 6 Series Chipset Family LPC Controller + +pci:v00008086d00001C42* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C43* + ID_MODEL_FROM_DATABASE=Mobile 6 Series Chipset Family LPC Controller + +pci:v00008086d00001C44* + ID_MODEL_FROM_DATABASE=Z68 Express Chipset Family LPC Controller + +pci:v00008086d00001C45* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C46* + ID_MODEL_FROM_DATABASE=P67 Express Chipset Family LPC Controller + +pci:v00008086d00001C46sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=P67 Express Chipset Family LPC Controller (P8P67 Deluxe Motherboard) + +pci:v00008086d00001C47* + ID_MODEL_FROM_DATABASE=UM67 Express Chipset Family LPC Controller + +pci:v00008086d00001C48* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C49* + ID_MODEL_FROM_DATABASE=HM65 Express Chipset Family LPC Controller + +pci:v00008086d00001C49sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=HM65 Express Chipset Family LPC Controller (Apple MacBookPro8,2 [Core i7, 15", 2011]) + +pci:v00008086d00001C4A* + ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller + +pci:v00008086d00001C4Asv00001028sd000004AA* + ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller (XPS 8300) + +pci:v00008086d00001C4Asv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=H67 Express Chipset Family LPC Controller (P8H67 Series Motherboard) + +pci:v00008086d00001C4B* + ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller + +pci:v00008086d00001C4Bsv00001028sd000004B2* + ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller (Vostro 3350) + +pci:v00008086d00001C4Bsv00001028sd000004DA* + ID_MODEL_FROM_DATABASE=HM67 Express Chipset Family LPC Controller (Vostro 3750) + +pci:v00008086d00001C4C* + ID_MODEL_FROM_DATABASE=Q65 Express Chipset Family LPC Controller + +pci:v00008086d00001C4D* + ID_MODEL_FROM_DATABASE=QS67 Express Chipset Family LPC Controller + +pci:v00008086d00001C4E* + ID_MODEL_FROM_DATABASE=Q67 Express Chipset Family LPC Controller + +pci:v00008086d00001C4F* + ID_MODEL_FROM_DATABASE=QM67 Express Chipset Family LPC Controller + +pci:v00008086d00001C4Fsv00001028sd000004A3* + ID_MODEL_FROM_DATABASE=QM67 Express Chipset Family LPC Controller (Precision M4600) + +pci:v00008086d00001C50* + ID_MODEL_FROM_DATABASE=B65 Express Chipset Family LPC Controller + +pci:v00008086d00001C51* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C52* + ID_MODEL_FROM_DATABASE=C202 Chipset Family LPC Controller + +pci:v00008086d00001C52sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=C202 Chipset Family LPC Controller (Server Board S1200BTS) + +pci:v00008086d00001C53* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C54* + ID_MODEL_FROM_DATABASE=C204 Chipset Family LPC Controller + +pci:v00008086d00001C55* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C56* + ID_MODEL_FROM_DATABASE=C206 Chipset Family LPC Controller + +pci:v00008086d00001C56sv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=C206 Chipset Family LPC Controller (P8B WS Motherboard) + +pci:v00008086d00001C57* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C58* + ID_MODEL_FROM_DATABASE=Upgraded B65 Express Chipset Family LPC Controller + +pci:v00008086d00001C59* + ID_MODEL_FROM_DATABASE=Upgraded HM67 Express Chipset Family LPC Controller + +pci:v00008086d00001C5A* + ID_MODEL_FROM_DATABASE=Upgraded Q67 Express Chipset Family LPC Controller + +pci:v00008086d00001C5B* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C5C* + ID_MODEL_FROM_DATABASE=H61 Express Chipset Family LPC Controller + +pci:v00008086d00001C5D* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C5E* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001C5F* + ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family LPC Controller + +pci:v00008086d00001D00* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA IDE Controller + +pci:v00008086d00001D02* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 6-Port SATA AHCI Controller + +pci:v00008086d00001D04* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset SATA RAID Controller + +pci:v00008086d00001D06* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset SATA Premium RAID Controller + +pci:v00008086d00001D08* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 2-Port SATA IDE Controller + +pci:v00008086d00001D10* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 1 + +pci:v00008086d00001D11* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 1 + +pci:v00008086d00001D12* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 2 + +pci:v00008086d00001D13* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 2 + +pci:v00008086d00001D14* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 3 + +pci:v00008086d00001D15* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 3 + +pci:v00008086d00001D16* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 4 + +pci:v00008086d00001D17* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 4 + +pci:v00008086d00001D18* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 5 + +pci:v00008086d00001D19* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 5 + +pci:v00008086d00001D1A* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 6 + +pci:v00008086d00001D1B* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 6 + +pci:v00008086d00001D1C* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 7 + +pci:v00008086d00001D1D* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 7 + +pci:v00008086d00001D1E* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 8 + +pci:v00008086d00001D1F* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Root Port 8 + +pci:v00008086d00001D20* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset High Definition Audio Controller + +pci:v00008086d00001D22* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset SMBus Host Controller + +pci:v00008086d00001D24* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Thermal Management Controller + +pci:v00008086d00001D25* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset DMI to PCI Bridge + +pci:v00008086d00001D26* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #1 + +pci:v00008086d00001D2D* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset USB2 Enhanced Host Controller #2 + +pci:v00008086d00001D33* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset LAN Controller + +pci:v00008086d00001D35* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset VECI Controller + +pci:v00008086d00001D3A* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #1 + +pci:v00008086d00001D3B* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset MEI Controller #2 + +pci:v00008086d00001D3C* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset IDE-r Controller + +pci:v00008086d00001D3D* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset KT Controller + +pci:v00008086d00001D3E* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset PCI Express Virtual Root Port + +pci:v00008086d00001D3F* + ID_MODEL_FROM_DATABASE=C608/C606/X79 series chipset PCI Express Virtual Switch Port + +pci:v00008086d00001D40* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset LPC Controller + +pci:v00008086d00001D41* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset LPC Controller + +pci:v00008086d00001D50* + ID_MODEL_FROM_DATABASE=C608 chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D54* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D55* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D58* + ID_MODEL_FROM_DATABASE=C606 chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D59* + ID_MODEL_FROM_DATABASE=C604/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D5A* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA Storage Control Unit + +pci:v00008086d00001D5B* + ID_MODEL_FROM_DATABASE=C602 chipset 4-Port SATA Storage Control Unit + +pci:v00008086d00001D5C* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D5D* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D5E* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA Storage Control Unit + +pci:v00008086d00001D5F* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA Storage Control Unit + +pci:v00008086d00001D60* + ID_MODEL_FROM_DATABASE=C608 chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D64* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D65* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D68* + ID_MODEL_FROM_DATABASE=C606 chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D69* + ID_MODEL_FROM_DATABASE=C604/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D6A* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA Storage Control Unit + +pci:v00008086d00001D6B* + ID_MODEL_FROM_DATABASE=C602 chipset 4-Port SATA Storage Control Unit + +pci:v00008086d00001D6C* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D6D* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA/SAS Storage Control Unit + +pci:v00008086d00001D6E* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Dual 4-Port SATA Storage Control Unit + +pci:v00008086d00001D6F* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset 4-Port SATA Storage Control Unit + +pci:v00008086d00001D70* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset SMBus Controller 0 + +pci:v00008086d00001D71* + ID_MODEL_FROM_DATABASE=C608/C606/X79 series chipset SMBus Controller 1 + +pci:v00008086d00001D72* + ID_MODEL_FROM_DATABASE=C608 chipset SMBus Controller 2 + +pci:v00008086d00001D74* + ID_MODEL_FROM_DATABASE=C608/C606/X79 series chipset PCI Express Upstream Port + +pci:v00008086d00001D76* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset Multi-Function Glue + +pci:v00008086d00001E00* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family 4-port SATA Controller [IDE mode] + +pci:v00008086d00001E01* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 4-port SATA Controller [IDE mode] + +pci:v00008086d00001E02* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family 6-port SATA Controller [AHCI mode] + +pci:v00008086d00001E02sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family 6-port SATA Controller [AHCI mode] (P8 series motherboard) + +pci:v00008086d00001E02sv00001849sd00001E02* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family 6-port SATA Controller [AHCI mode] (Motherboard) + +pci:v00008086d00001E03* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] + +pci:v00008086d00001E03sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (VivoBook X202EV) + +pci:v00008086d00001E03sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (N56VZ) + +pci:v00008086d00001E03sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (Zenbook Prime UX31A) + +pci:v00008086d00001E04* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SATA Controller [RAID mode] + +pci:v00008086d00001E05* + ID_MODEL_FROM_DATABASE=7 Series Chipset SATA Controller [RAID mode] + +pci:v00008086d00001E06* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SATA Controller [RAID mode] + +pci:v00008086d00001E07* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family SATA Controller [RAID mode] + +pci:v00008086d00001E08* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family 2-port SATA Controller [IDE mode] + +pci:v00008086d00001E09* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family 2-port SATA Controller [IDE mode] + +pci:v00008086d00001E0E* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SATA Controller [RAID mode] + +pci:v00008086d00001E10* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 + +pci:v00008086d00001E10sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 (VivoBook X202EV) + +pci:v00008086d00001E10sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 (N56VZ) + +pci:v00008086d00001E10sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 (Zenbook Prime UX31A) + +pci:v00008086d00001E10sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 (P8H77-I Motherboard) + +pci:v00008086d00001E10sv00001849sd00001E10* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 1 (Motherboard) + +pci:v00008086d00001E12* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 2 + +pci:v00008086d00001E12sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 2 (VivoBook X202EV) + +pci:v00008086d00001E12sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 2 (N56VZ) + +pci:v00008086d00001E12sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 2 (Zenbook Prime UX31A) + +pci:v00008086d00001E14* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 3 + +pci:v00008086d00001E16* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 4 + +pci:v00008086d00001E16sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 4 (VivoBook X202EV) + +pci:v00008086d00001E16sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 4 (N56VZ) + +pci:v00008086d00001E16sv00001849sd00001618* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 4 (Z77 Extreme4 motherboard) + +pci:v00008086d00001E18* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 5 + +pci:v00008086d00001E18sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 5 (P8H77-I Motherboard) + +pci:v00008086d00001E18sv00001849sd00001E18* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 5 (Motherboard) + +pci:v00008086d00001E1A* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 6 + +pci:v00008086d00001E1Asv00001849sd00001E1A* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 6 (Motherboard) + +pci:v00008086d00001E1C* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 7 + +pci:v00008086d00001E1E* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 + +pci:v00008086d00001E1Esv00001849sd00001E1E* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 (Motherboard) + +pci:v00008086d00001E20* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller + +pci:v00008086d00001E20sv00001028sd0000054B* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (Dell XPS One 2710) + +pci:v00008086d00001E20sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (VivoBook X202EV) + +pci:v00008086d00001E20sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (N56VZ) + +pci:v00008086d00001E20sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (Zenbook Prime UX31A) + +pci:v00008086d00001E20sv00001043sd00008415* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (P8H77-I Motherboard) + +pci:v00008086d00001E20sv00001043sd00008445* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (ASUS P8Z77-V LX Motherboard) + +pci:v00008086d00001E20sv00001849sd00001898* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family High Definition Audio Controller (Z77 Extreme4 motherboard) + +pci:v00008086d00001E22* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller + +pci:v00008086d00001E22sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller (VivoBook X202EV) + +pci:v00008086d00001E22sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller (N56VZ) + +pci:v00008086d00001E22sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller (Zenbook Prime UX31A) + +pci:v00008086d00001E22sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller (P8 series motherboard) + +pci:v00008086d00001E22sv00001849sd00001E22* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family SMBus Controller (Motherboard) + +pci:v00008086d00001E24* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family Thermal Management Controller + +pci:v00008086d00001E24sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family Thermal Management Controller (Zenbook Prime UX31A) + +pci:v00008086d00001E25* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family DMI to PCI Bridge + +pci:v00008086d00001E26* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 + +pci:v00008086d00001E26sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 (VivoBook X202EV) + +pci:v00008086d00001E26sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 (N56VZ) + +pci:v00008086d00001E26sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 (Zenbook Prime UX31A) + +pci:v00008086d00001E26sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 (P8 series motherboard) + +pci:v00008086d00001E26sv00001849sd00001E26* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #1 (Motherboard) + +pci:v00008086d00001E2D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 + +pci:v00008086d00001E2Dsv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 (VivoBook X202EV) + +pci:v00008086d00001E2Dsv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 (N56VZ) + +pci:v00008086d00001E2Dsv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 (Zenbook Prime UX31A) + +pci:v00008086d00001E2Dsv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 (P8 series motherboard) + +pci:v00008086d00001E2Dsv00001849sd00001E2D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB Enhanced Host Controller #2 (Motherboard) + +pci:v00008086d00001E31* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller + +pci:v00008086d00001E31sv0000103Csd000017AB* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (ProBook 6570b) + +pci:v00008086d00001E31sv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (VivoBook X202EV) + +pci:v00008086d00001E31sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (N56VZ) + +pci:v00008086d00001E31sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (Zenbook Prime UX31A) + +pci:v00008086d00001E31sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (P8 series motherboard) + +pci:v00008086d00001E31sv00001849sd00001E31* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (Motherboard) + +pci:v00008086d00001E33* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family LAN Controller + +pci:v00008086d00001E3A* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 + +pci:v00008086d00001E3Asv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 (VivoBook X202EV) + +pci:v00008086d00001E3Asv00001043sd00001477* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 (N56VZ) + +pci:v00008086d00001E3Asv00001043sd00001517* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 (Zenbook Prime UX31A) + +pci:v00008086d00001E3Asv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 (P8 series motherboard) + +pci:v00008086d00001E3Asv00001849sd00001E3A* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #1 (Motherboard) + +pci:v00008086d00001E3B* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family MEI Controller #2 + +pci:v00008086d00001E3C* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family IDE-r Controller + +pci:v00008086d00001E3D* + ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family KT Controller + +pci:v00008086d00001E41* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E42* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E43* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E44* + ID_MODEL_FROM_DATABASE=Z77 Express Chipset LPC Controller + +pci:v00008086d00001E44sv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=Z77 Express Chipset LPC Controller (P8 series motherboard) + +pci:v00008086d00001E44sv00001849sd00001E44* + ID_MODEL_FROM_DATABASE=Z77 Express Chipset LPC Controller (Motherboard) + +pci:v00008086d00001E45* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E46* + ID_MODEL_FROM_DATABASE=Z75 Express Chipset LPC Controller + +pci:v00008086d00001E47* + ID_MODEL_FROM_DATABASE=Q77 Express Chipset LPC Controller + +pci:v00008086d00001E48* + ID_MODEL_FROM_DATABASE=Q75 Express Chipset LPC Controller + +pci:v00008086d00001E49* + ID_MODEL_FROM_DATABASE=B75 Express Chipset LPC Controller + +pci:v00008086d00001E4A* + ID_MODEL_FROM_DATABASE=H77 Express Chipset LPC Controller + +pci:v00008086d00001E4Asv00001043sd000084CA* + ID_MODEL_FROM_DATABASE=H77 Express Chipset LPC Controller (P8H77-I Motherboard) + +pci:v00008086d00001E4B* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E4C* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E4D* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E4E* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E4F* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E50* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E51* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E52* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E53* + ID_MODEL_FROM_DATABASE=C216 Series Chipset LPC Controller + +pci:v00008086d00001E54* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E55* + ID_MODEL_FROM_DATABASE=QM77 Express Chipset LPC Controller + +pci:v00008086d00001E56* + ID_MODEL_FROM_DATABASE=QS77 Express Chipset LPC Controller + +pci:v00008086d00001E57* + ID_MODEL_FROM_DATABASE=HM77 Express Chipset LPC Controller + +pci:v00008086d00001E58* + ID_MODEL_FROM_DATABASE=UM77 Express Chipset LPC Controller + +pci:v00008086d00001E59* + ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller + +pci:v00008086d00001E59sv00001043sd00001477* + ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (N56VZ) + +pci:v00008086d00001E59sv00001043sd00001517* + ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (Zenbook Prime UX31A) + +pci:v00008086d00001E5A* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E5B* + ID_MODEL_FROM_DATABASE=UM77 Express Chipset LPC Controller + +pci:v00008086d00001E5C* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E5D* + ID_MODEL_FROM_DATABASE=HM75 Express Chipset LPC Controller + +pci:v00008086d00001E5E* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001E5Esv00001043sd0000108D* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller (VivoBook X202EV) + +pci:v00008086d00001E5F* + ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller + +pci:v00008086d00001F00* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F01* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F02* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F03* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F04* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F05* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F06* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F07* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F08* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F09* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0A* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0B* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0C* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0D* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0E* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F0F* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SoC Transaction Router + +pci:v00008086d00001F10* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCIe Root Port 1 + +pci:v00008086d00001F11* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCIe Root Port 2 + +pci:v00008086d00001F12* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCIe Root Port 3 + +pci:v00008086d00001F13* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCIe Root Port 4 + +pci:v00008086d00001F14* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAS + +pci:v00008086d00001F15* + ID_MODEL_FROM_DATABASE=Atom processor C2000 SMBus 2.0 + +pci:v00008086d00001F16* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RCEC + +pci:v00008086d00001F18* + ID_MODEL_FROM_DATABASE=Atom processor C2000 QAT + +pci:v00008086d00001F19* + ID_MODEL_FROM_DATABASE=Atom processor C2000 QAT + +pci:v00008086d00001F20* + ID_MODEL_FROM_DATABASE=Atom processor C2000 4-Port IDE SATA2 Controller + +pci:v00008086d00001F21* + ID_MODEL_FROM_DATABASE=Atom processor C2000 4-Port IDE SATA2 Controller + +pci:v00008086d00001F22* + ID_MODEL_FROM_DATABASE=Atom processor C2000 AHCI SATA2 Controller + +pci:v00008086d00001F23* + ID_MODEL_FROM_DATABASE=Atom processor C2000 AHCI SATA2 Controller + +pci:v00008086d00001F24* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F25* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F26* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F27* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F2C* + ID_MODEL_FROM_DATABASE=Atom processor C2000 USB Enhanced Host Controller + +pci:v00008086d00001F2E* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F2F* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA2 Controller + +pci:v00008086d00001F30* + ID_MODEL_FROM_DATABASE=Atom processor C2000 2-Port IDE SATA3 Controller + +pci:v00008086d00001F31* + ID_MODEL_FROM_DATABASE=Atom processor C2000 2-Port IDE SATA3 Controller + +pci:v00008086d00001F32* + ID_MODEL_FROM_DATABASE=Atom processor C2000 AHCI SATA3 Controller + +pci:v00008086d00001F33* + ID_MODEL_FROM_DATABASE=Atom processor C2000 AHCI SATA3 Controller + +pci:v00008086d00001F34* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F35* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F36* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F37* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F38* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU + +pci:v00008086d00001F39* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU + +pci:v00008086d00001F3A* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU + +pci:v00008086d00001F3B* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU + +pci:v00008086d00001F3C* + ID_MODEL_FROM_DATABASE=Atom processor C2000 PCU SMBus + +pci:v00008086d00001F3E* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F3F* + ID_MODEL_FROM_DATABASE=Atom processor C2000 RAID SATA3 Controller + +pci:v00008086d00001F40* + ID_MODEL_FROM_DATABASE=Ethernet Connection I354 1.0 GbE Backplane + +pci:v00008086d00001F40sv00001028sd000005F1* + ID_MODEL_FROM_DATABASE=Ethernet Connection I354 1.0 GbE Backplane + +pci:v00008086d00001F41* + ID_MODEL_FROM_DATABASE=Ethernet Connection I354 + +pci:v00008086d00001F42* + ID_MODEL_FROM_DATABASE=Atom processor C2000 GbE + +pci:v00008086d00001F44* + ID_MODEL_FROM_DATABASE=Atom processor C2000 GbE Virtual Function + +pci:v00008086d00001F45* + ID_MODEL_FROM_DATABASE=Ethernet Connection I354 2.5 GbE Backplane + +pci:v00008086d00002014* + ID_MODEL_FROM_DATABASE=Sky Lake-E Ubox Registers + +pci:v00008086d00002015* + ID_MODEL_FROM_DATABASE=Sky Lake-E Ubox Registers + +pci:v00008086d00002016* + ID_MODEL_FROM_DATABASE=Sky Lake-E Ubox Registers + +pci:v00008086d00002018* + ID_MODEL_FROM_DATABASE=Sky Lake-E M2PCI Registers + +pci:v00008086d0000201A* + ID_MODEL_FROM_DATABASE=Sky Lake-E Non-Transparent Bridge Registers + +pci:v00008086d0000201C* + ID_MODEL_FROM_DATABASE=Sky Lake-E Non-Transparent Bridge Registers + +pci:v00008086d00002021* + ID_MODEL_FROM_DATABASE=Sky Lake-E CBDMA Registers + +pci:v00008086d00002024* + ID_MODEL_FROM_DATABASE=Sky Lake-E MM/Vt-d Configuration Registers + +pci:v00008086d00002030* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCI Express Root Port 1A + +pci:v00008086d00002031* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCI Express Root Port 1B + +pci:v00008086d00002032* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCI Express Root Port 1C + +pci:v00008086d00002033* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCI Express Root Port 1D + +pci:v00008086d00002035* + ID_MODEL_FROM_DATABASE=Sky Lake-E RAS Configuration Registers + +pci:v00008086d0000204C* + ID_MODEL_FROM_DATABASE=Sky Lake-E M3KTI Registers + +pci:v00008086d0000204D* + ID_MODEL_FROM_DATABASE=Sky Lake-E M3KTI Registers + +pci:v00008086d0000204E* + ID_MODEL_FROM_DATABASE=Sky Lake-E M3KTI Registers + +pci:v00008086d00002054* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d00002055* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d00002056* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d00002057* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d00002068* + ID_MODEL_FROM_DATABASE=Sky Lake-E DDRIO Registers + +pci:v00008086d00002069* + ID_MODEL_FROM_DATABASE=Sky Lake-E DDRIO Registers + +pci:v00008086d0000206A* + ID_MODEL_FROM_DATABASE=Sky Lake-E IOxAPIC Configuration Registers + +pci:v00008086d0000206E* + ID_MODEL_FROM_DATABASE=Sky Lake-E DDRIO Registers + +pci:v00008086d0000206F* + ID_MODEL_FROM_DATABASE=Sky Lake-E DDRIO Registers + +pci:v00008086d00002078* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d0000207A* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002080* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002081* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002082* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002083* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002084* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002085* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d00002086* + ID_MODEL_FROM_DATABASE=Sky Lake-E PCU Registers + +pci:v00008086d0000208D* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d0000208E* + ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers + +pci:v00008086d00002250* + ID_MODEL_FROM_DATABASE=Xeon Phi coprocessor 5100 series + +pci:v00008086d0000225C* + ID_MODEL_FROM_DATABASE=Xeon Phi coprocessor SE10/7120 series + +pci:v00008086d0000225D* + ID_MODEL_FROM_DATABASE=Xeon Phi coprocessor 3120 series + +pci:v00008086d0000225E* + ID_MODEL_FROM_DATABASE=Xeon Phi coprocessor 31S1 + +pci:v00008086d00002280* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series SoC Transaction Register + +pci:v00008086d00002284* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series High Definition Audio Controller + +pci:v00008086d00002286* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO1 DMA Controller + +pci:v00008086d0000228A* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO1 HSUART Controller #1 + +pci:v00008086d0000228C* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO1 HSUART Controller #2 + +pci:v00008086d00002292* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx SMBus Controller + +pci:v00008086d00002294* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series MMC Controller + +pci:v00008086d00002295* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series SDIO Controller + +pci:v00008086d00002296* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series SD Controller + +pci:v00008086d00002298* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series Trusted Execution Engine + +pci:v00008086d0000229C* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCU + +pci:v00008086d000022A3* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series SATA Controller + +pci:v00008086d000022A4* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series SATA AHCI Controller + +pci:v00008086d000022A8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series Low Power Engine Audio + +pci:v00008086d000022B0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCI Configuration Registers + +pci:v00008086d000022B1* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Integrated Graphics Controller + +pci:v00008086d000022B5* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series USB xHCI Controller + +pci:v00008086d000022B8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series Imaging Unit + +pci:v00008086d000022C0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 DMA Controller + +pci:v00008086d000022C1* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #1 + +pci:v00008086d000022C2* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #2 + +pci:v00008086d000022C3* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #3 + +pci:v00008086d000022C4* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #4 + +pci:v00008086d000022C5* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #5 + +pci:v00008086d000022C6* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #6 + +pci:v00008086d000022C7* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series LPIO2 I2C Controller #7 + +pci:v00008086d000022C8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCI Express Port #1 + +pci:v00008086d000022CA* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCI Express Port #2 + +pci:v00008086d000022CC* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCI Express Port #3 + +pci:v00008086d000022CE* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series PCI Express Port #4 + +pci:v00008086d000022DC* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor x5-E8000/J3xxx/N3xxx Series Power Management Controller + +pci:v00008086d00002310* + ID_MODEL_FROM_DATABASE=DH89xxCC LPC Controller + +pci:v00008086d00002323* + ID_MODEL_FROM_DATABASE=DH89xxCC 4 Port SATA AHCI Controller + +pci:v00008086d00002330* + ID_MODEL_FROM_DATABASE=DH89xxCC SMBus Controller + +pci:v00008086d00002331* + ID_MODEL_FROM_DATABASE=DH89xxCC Chap Counter + +pci:v00008086d00002332* + ID_MODEL_FROM_DATABASE=DH89xxCC Thermal Subsystem + +pci:v00008086d00002334* + ID_MODEL_FROM_DATABASE=DH89xxCC USB2 Enhanced Host Controller #1 + +pci:v00008086d00002335* + ID_MODEL_FROM_DATABASE=DH89xxCC USB2 Enhanced Host Controller #1 + +pci:v00008086d00002342* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #1 + +pci:v00008086d00002343* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #1 + +pci:v00008086d00002344* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #2 + +pci:v00008086d00002345* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #2 + +pci:v00008086d00002346* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #3 + +pci:v00008086d00002347* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #3 + +pci:v00008086d00002348* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #4 + +pci:v00008086d00002349* + ID_MODEL_FROM_DATABASE=DH89xxCC PCI Express Root Port #4 + +pci:v00008086d00002360* + ID_MODEL_FROM_DATABASE=DH89xxCC Watchdog Timer + +pci:v00008086d00002364* + ID_MODEL_FROM_DATABASE=DH89xxCC MEI 0 + +pci:v00008086d00002365* + ID_MODEL_FROM_DATABASE=DH89xxCC MEI 1 + +pci:v00008086d00002390* + ID_MODEL_FROM_DATABASE=DH895XCC Series LPC Controller + +pci:v00008086d000023A1* + ID_MODEL_FROM_DATABASE=DH895XCC Series 2-Port SATA Controller [IDE Mode] + +pci:v00008086d000023A3* + ID_MODEL_FROM_DATABASE=DH895XCC Series 4-Port SATA Controller [AHCI Mode] + +pci:v00008086d000023A6* + ID_MODEL_FROM_DATABASE=DH895XCC Series 2-Port SATA Controller [IDE Mode] + +pci:v00008086d000023B0* + ID_MODEL_FROM_DATABASE=DH895XCC Series SMBus Controller + +pci:v00008086d000023B1* + ID_MODEL_FROM_DATABASE=DH895XCC Series CHAP Counter + +pci:v00008086d000023B2* + ID_MODEL_FROM_DATABASE=DH895XCC Series Thermal Management Controller + +pci:v00008086d000023B4* + ID_MODEL_FROM_DATABASE=DH895XCC Series USB2 Enhanced Host Controller #1 + +pci:v00008086d000023B5* + ID_MODEL_FROM_DATABASE=DH895XCC Series USB2 Enhanced Host Controller #1 + +pci:v00008086d000023C2* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #1 + +pci:v00008086d000023C3* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #1 + +pci:v00008086d000023C4* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #2 + +pci:v00008086d000023C5* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #2 + +pci:v00008086d000023C6* + ID_MODEL_FROM_DATABASE=CDH895XCC Series PCI Express Root Port #3 + +pci:v00008086d000023C7* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #3 + +pci:v00008086d000023C8* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #4 + +pci:v00008086d000023C9* + ID_MODEL_FROM_DATABASE=DH895XCC Series PCI Express Root Port #4 + +pci:v00008086d000023E0* + ID_MODEL_FROM_DATABASE=DH895XCC Series Watchdog Timer + +pci:v00008086d000023E4* + ID_MODEL_FROM_DATABASE=DH895XCC Series MEI Controller #1 + +pci:v00008086d000023E5* + ID_MODEL_FROM_DATABASE=DH895XCC Series MEI Controller #2 + +pci:v00008086d00002410* + ID_MODEL_FROM_DATABASE=82801AA ISA Bridge (LPC) + +pci:v00008086d00002411* + ID_MODEL_FROM_DATABASE=82801AA IDE Controller + +pci:v00008086d00002412* + ID_MODEL_FROM_DATABASE=82801AA USB Controller + +pci:v00008086d00002413* + ID_MODEL_FROM_DATABASE=82801AA SMBus Controller + +pci:v00008086d00002415* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller + +pci:v00008086d00002415sv00001028sd00000095* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (Precision Workstation 220 Integrated Digital Audio) + +pci:v00008086d00002415sv00001028sd000000B4* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (OptiPlex GX110) + +pci:v00008086d00002415sv0000110Asd00000051* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (Activy 2xx) + +pci:v00008086d00002415sv000011D4sd00000040* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00002415sv000011D4sd00000048* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00002415sv000011D4sd00005340* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00002415sv00001734sd00001025* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (Activy 3xx) + +pci:v00008086d00002415sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Audio Controller (QEMU Virtual Machine) + +pci:v00008086d00002416* + ID_MODEL_FROM_DATABASE=82801AA AC'97 Modem Controller + +pci:v00008086d00002418* + ID_MODEL_FROM_DATABASE=82801AA PCI Bridge + +pci:v00008086d00002420* + ID_MODEL_FROM_DATABASE=82801AB ISA Bridge (LPC) + +pci:v00008086d00002421* + ID_MODEL_FROM_DATABASE=82801AB IDE Controller + +pci:v00008086d00002422* + ID_MODEL_FROM_DATABASE=82801AB USB Controller + +pci:v00008086d00002423* + ID_MODEL_FROM_DATABASE=82801AB SMBus Controller + +pci:v00008086d00002425* + ID_MODEL_FROM_DATABASE=82801AB AC'97 Audio Controller + +pci:v00008086d00002425sv000011D4sd00000040* + ID_MODEL_FROM_DATABASE=82801AB AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00002425sv000011D4sd00000048* + ID_MODEL_FROM_DATABASE=82801AB AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00002426* + ID_MODEL_FROM_DATABASE=82801AB AC'97 Modem Controller + +pci:v00008086d00002428* + ID_MODEL_FROM_DATABASE=82801AB PCI Bridge + +pci:v00008086d00002440* + ID_MODEL_FROM_DATABASE=82801BA ISA Bridge (LPC) + +pci:v00008086d00002440sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=82801BA ISA Bridge (LPC) (S845WD1-E) + +pci:v00008086d00002442* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 + +pci:v00008086d00002442sv00001014sd000001C6* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Netvista A40/A40p) + +pci:v00008086d00002442sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Travelmate 612 TX) + +pci:v00008086d00002442sv00001028sd000000C7* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Dimension 8100) + +pci:v00008086d00002442sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Precision 530) + +pci:v00008086d00002442sv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Optiplex GX240) + +pci:v00008086d00002442sv0000103Csd0000126F* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (e-pc 40) + +pci:v00008086d00002442sv00001043sd00008027* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (TUSL2-C Mainboard) + +pci:v00008086d00002442sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Vaio PCG-FX403) + +pci:v00008086d00002442sv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (BL7 motherboard) + +pci:v00008086d00002442sv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (TH7II-RAID) + +pci:v00008086d00002442sv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d00002442sv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (D815EGEW Mainboard) + +pci:v00008086d00002442sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #1 (S845WD1-E mainboard) + +pci:v00008086d00002443* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller + +pci:v00008086d00002443sv00001014sd000001C6* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Netvista A40/A40p) + +pci:v00008086d00002443sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Travelmate 612 TX) + +pci:v00008086d00002443sv00001028sd000000C7* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Dimension 8100) + +pci:v00008086d00002443sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Precision 530) + +pci:v00008086d00002443sv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Optiplex GX240) + +pci:v00008086d00002443sv0000103Csd0000126F* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (e-pc 40) + +pci:v00008086d00002443sv00001043sd00008027* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (TUSL2-C Mainboard) + +pci:v00008086d00002443sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Vaio PCG-FX403) + +pci:v00008086d00002443sv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (BL7 motherboard) + +pci:v00008086d00002443sv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (TH7II-RAID) + +pci:v00008086d00002443sv000015D9sd00003280* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Supermicro P4SBE Mainboard) + +pci:v00008086d00002443sv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d00002443sv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (D815EGEW Mainboard) + +pci:v00008086d00002443sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=82801BA/BAM SMBus Controller (S845WD1-E mainboard) + +pci:v00008086d00002444* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 + +pci:v00008086d00002444sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Travelmate 612 TX) + +pci:v00008086d00002444sv00001028sd000000C7* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Dimension 8100) + +pci:v00008086d00002444sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Precision 530) + +pci:v00008086d00002444sv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Optiplex GX240) + +pci:v00008086d00002444sv0000103Csd0000126F* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (e-pc 40) + +pci:v00008086d00002444sv00001043sd00008027* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (TUSL2-C Mainboard) + +pci:v00008086d00002444sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Vaio PCG-FX403) + +pci:v00008086d00002444sv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (BL7 motherboard) + +pci:v00008086d00002444sv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (TH7II-RAID) + +pci:v00008086d00002444sv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d00002444sv00008086sd00005744* + ID_MODEL_FROM_DATABASE=82801BA/BAM UHCI USB 1.1 Controller #2 (S845WD1-E mainboard) + +pci:v00008086d00002445* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller + +pci:v00008086d00002445sv00000E11sd0000000B* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Compaq Deskpro EN Audio) + +pci:v00008086d00002445sv00000E11sd00000088* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Evo D500) + +pci:v00008086d00002445sv00001014sd000001C6* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Netvista A40/A40p) + +pci:v00008086d00002445sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Travelmate 612 TX) + +pci:v00008086d00002445sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Precision 530) + +pci:v00008086d00002445sv0000103Csd0000126F* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (e-pc 40) + +pci:v00008086d00002445sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Vaio PCG-FX403) + +pci:v00008086d00002445sv00001462sd00003370* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (STAC9721 AC) + +pci:v00008086d00002445sv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (BL7 motherboard) + +pci:v00008086d00002445sv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (TH7II-RAID) + +pci:v00008086d00002445sv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (D815EGEW Mainboard) + +pci:v00008086d00002445sv00008086sd00004656* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Audio Controller (Desktop Board D815EFV) + +pci:v00008086d00002446* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Modem Controller + +pci:v00008086d00002446sv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Modem Controller (Travelmate 612 TX) + +pci:v00008086d00002446sv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BA/BAM AC'97 Modem Controller (Vaio PCG-FX403) + +pci:v00008086d00002448* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge + +pci:v00008086d00002448sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Latitude E6410) + +pci:v00008086d00002448sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Latitude E6510) + +pci:v00008086d00002448sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq nw8240 Mobile Workstation) + +pci:v00008086d00002448sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (NX6110/NC6120) + +pci:v00008086d00002448sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq nx9420 Notebook) + +pci:v00008086d00002448sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq nw8440) + +pci:v00008086d00002448sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq 6910p) + +pci:v00008086d00002448sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (VAIO VGN-NR120E) + +pci:v00008086d00002448sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (D270S/D250S Motherboard) + +pci:v00008086d00002448sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d00002448sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (P30 notebook) + +pci:v00008086d00002448sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (R730 Laptop) + +pci:v00008086d00002448sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Notebook N150P) + +pci:v00008086d00002448sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (GA-D525TUD) + +pci:v00008086d00002448sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Amilo M1420) + +pci:v00008086d00002448sv000017AAsd000020AE* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (ThinkPad T61/R61) + +pci:v00008086d00002448sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00002448sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002448sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Desktop Board D425KT) + +pci:v00008086d00002448sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (CCG-RUMBA) + +pci:v00008086d00002449* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller + +pci:v00008086d00002449sv00000E11sd00000012* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VM) + +pci:v00008086d00002449sv00000E11sd00000091* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd000001CE* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd000001DC* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd000001EB* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd000001EC* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000202* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000205* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000217* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000234* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd0000023D* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000244* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000245* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001014sd00000265* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (PRO/100 VE Desktop Connection) + +pci:v00008086d00002449sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (PRO/100 VE Desktop Connection) + +pci:v00008086d00002449sv00001014sd0000026A* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (PRO/100 VE Desktop Connection) + +pci:v00008086d00002449sv0000109Fsd0000315D* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv0000109Fsd00003181* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00001179sd0000FF01* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (PRO/100 VE Network Connection) + +pci:v00008086d00002449sv00001186sd00007801* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv0000144Dsd00002602* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (HomePNA 1M CNR) + +pci:v00008086d00002449sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (QEMU Virtual Machine) + +pci:v00008086d00002449sv00008086sd00003010* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00008086sd00003011* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VM) + +pci:v00008086d00002449sv00008086sd00003012* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (82562EH based Phoneline) + +pci:v00008086d00002449sv00008086sd00003013* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VE) + +pci:v00008086d00002449sv00008086sd00003014* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 VM) + +pci:v00008086d00002449sv00008086sd00003015* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (82562EH based Phoneline) + +pci:v00008086d00002449sv00008086sd00003016* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 P Mobile Combo) + +pci:v00008086d00002449sv00008086sd00003017* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100 P Mobile) + +pci:v00008086d00002449sv00008086sd00003018* + ID_MODEL_FROM_DATABASE=82801BA/BAM/CA/CAM Ethernet Controller (EtherExpress PRO/100) + +pci:v00008086d0000244A* + ID_MODEL_FROM_DATABASE=82801BAM IDE U100 Controller + +pci:v00008086d0000244Asv00001025sd00001016* + ID_MODEL_FROM_DATABASE=82801BAM IDE U100 Controller (Travelmate 612TX) + +pci:v00008086d0000244Asv0000104Dsd000080DF* + ID_MODEL_FROM_DATABASE=82801BAM IDE U100 Controller (Vaio PCG-FX403) + +pci:v00008086d0000244B* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller + +pci:v00008086d0000244Bsv00001014sd000001C6* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Netvista A40/A40p) + +pci:v00008086d0000244Bsv00001028sd000000C7* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Dimension 8100) + +pci:v00008086d0000244Bsv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Precision 530) + +pci:v00008086d0000244Bsv00001028sd0000010E* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Optiplex GX240) + +pci:v00008086d0000244Bsv0000103Csd0000126F* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (e-pc 40) + +pci:v00008086d0000244Bsv00001043sd00008027* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (TUSL2-C Mainboard) + +pci:v00008086d0000244Bsv0000147Bsd00000505* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (BL7 motherboard) + +pci:v00008086d0000244Bsv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (TH7II-RAID) + +pci:v00008086d0000244Bsv000015D9sd00003280* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Supermicro P4SBE Mainboard) + +pci:v00008086d0000244Bsv00008086sd00004532* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (Desktop Board D815EEA2/D815EFV) + +pci:v00008086d0000244Bsv00008086sd00004557* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (D815EGEW Mainboard) + +pci:v00008086d0000244Bsv00008086sd00005744* + ID_MODEL_FROM_DATABASE=82801BA IDE U100 Controller (S845WD1-E mainboard) + +pci:v00008086d0000244C* + ID_MODEL_FROM_DATABASE=82801BAM ISA Bridge (LPC) + +pci:v00008086d0000244E* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge + +pci:v00008086d0000244Esv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (NetVista A30p) + +pci:v00008086d0000244Esv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Inspiron 530) + +pci:v00008086d0000244Esv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Optiplex 755) + +pci:v00008086d0000244Esv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (OptiPlex 980) + +pci:v00008086d0000244Esv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Pavilion A1512X) + +pci:v00008086d0000244Esv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Asus IPIBL-LB Motherboard) + +pci:v00008086d0000244Esv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (ProLiant DL140 G3) + +pci:v00008086d0000244Esv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (ProLiant ML150 G6 Server) + +pci:v00008086d0000244Esv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (P5K PRO Motherboard) + +pci:v00008086d0000244Esv00001043sd0000844D* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (P8 series motherboard) + +pci:v00008086d0000244Esv00001458sd00005000* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Motherboard) + +pci:v00008086d0000244Esv00001462sd00007418* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Wind PC MS-7418) + +pci:v00008086d0000244Esv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (C7SIM-Q Motherboard) + +pci:v00008086d0000244Esv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (X7DBN Motherboard) + +pci:v00008086d0000244Esv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (CC11/CL11) + +pci:v00008086d0000244Esv00008086sd00007270* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge (Server Board S1200BTS) + +pci:v00008086d00002450* + ID_MODEL_FROM_DATABASE=82801E ISA Bridge (LPC) + +pci:v00008086d00002452* + ID_MODEL_FROM_DATABASE=82801E USB Controller + +pci:v00008086d00002453* + ID_MODEL_FROM_DATABASE=82801E SMBus Controller + +pci:v00008086d00002459* + ID_MODEL_FROM_DATABASE=82801E Ethernet Controller 0 + +pci:v00008086d0000245B* + ID_MODEL_FROM_DATABASE=82801E IDE U100 Controller + +pci:v00008086d0000245D* + ID_MODEL_FROM_DATABASE=82801E Ethernet Controller 1 + +pci:v00008086d0000245E* + ID_MODEL_FROM_DATABASE=82801E PCI Bridge + +pci:v00008086d00002480* + ID_MODEL_FROM_DATABASE=82801CA LPC Interface Controller + +pci:v00008086d00002482* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 + +pci:v00008086d00002482sv00000E11sd00000030* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (Evo N600c) + +pci:v00008086d00002482sv00001014sd00000220* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (ThinkPad A/T/X Series) + +pci:v00008086d00002482sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002482sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (P4DP6) + +pci:v00008086d00002482sv00008086sd00001958* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (vpr Matrix 170B4) + +pci:v00008086d00002482sv00008086sd00003424* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (SE7501HG2 Mainboard) + +pci:v00008086d00002482sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #1 (Latitude C640) + +pci:v00008086d00002483* + ID_MODEL_FROM_DATABASE=82801CA/CAM SMBus Controller + +pci:v00008086d00002483sv00001014sd00000220* + ID_MODEL_FROM_DATABASE=82801CA/CAM SMBus Controller (ThinkPad A/T/X Series) + +pci:v00008086d00002483sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM SMBus Controller (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002483sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82801CA/CAM SMBus Controller (P4DP6) + +pci:v00008086d00002483sv00008086sd00001958* + ID_MODEL_FROM_DATABASE=82801CA/CAM SMBus Controller (vpr Matrix 170B4) + +pci:v00008086d00002484* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 + +pci:v00008086d00002484sv00000E11sd00000030* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 (Evo N600c) + +pci:v00008086d00002484sv00001014sd00000220* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 (ThinkPad A/T/X Series) + +pci:v00008086d00002484sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002484sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 (P4DP6) + +pci:v00008086d00002484sv00008086sd00001958* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #2 (vpr Matrix 170B4) + +pci:v00008086d00002485* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller + +pci:v00008086d00002485sv00001013sd00005959* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (Crystal WMD Audio Codec) + +pci:v00008086d00002485sv00001014sd00000222* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (ThinkPad A30/A30p/T23) + +pci:v00008086d00002485sv00001014sd00000508* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (ThinkPad T30) + +pci:v00008086d00002485sv00001014sd0000051C* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (ThinkPad A/T/X Series) + +pci:v00008086d00002485sv00001043sd00001583* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (L3C (SPDIF)) + +pci:v00008086d00002485sv00001043sd00001623* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (L2B (no SPDIF)) + +pci:v00008086d00002485sv00001043sd00001643* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (L3F) + +pci:v00008086d00002485sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002485sv0000144Dsd0000C006* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Audio Controller (vpr Matrix 170B4) + +pci:v00008086d00002486* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller + +pci:v00008086d00002486sv00001014sd00000223* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (ThinkPad A/T/X Series) + +pci:v00008086d00002486sv00001014sd00000503* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (ThinkPad R31) + +pci:v00008086d00002486sv00001014sd0000051A* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (ThinkPad A/T/X Series) + +pci:v00008086d00002486sv0000101Fsd00001025* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (620 Series) + +pci:v00008086d00002486sv00001043sd00001496* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (PCtel HSP56 MR) + +pci:v00008086d00002486sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002486sv0000134Dsd00004C21* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (Dell Inspiron 2100 internal modem) + +pci:v00008086d00002486sv0000144Dsd00002115* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (vpr Matrix 170B4 internal modem) + +pci:v00008086d00002486sv000014F1sd00005421* + ID_MODEL_FROM_DATABASE=82801CA/CAM AC'97 Modem Controller (MD56ORD V.92 MDC Modem) + +pci:v00008086d00002487* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 + +pci:v00008086d00002487sv00000E11sd00000030* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 (Evo N600c) + +pci:v00008086d00002487sv00001014sd00000220* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 (ThinkPad A/T/X Series) + +pci:v00008086d00002487sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00002487sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 (P4DP6) + +pci:v00008086d00002487sv00008086sd00001958* + ID_MODEL_FROM_DATABASE=82801CA/CAM USB Controller #3 (vpr Matrix 170B4) + +pci:v00008086d0000248A* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller + +pci:v00008086d0000248Asv00000E11sd00000030* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller (Evo N600c) + +pci:v00008086d0000248Asv00001014sd00000220* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller (ThinkPad A/T/X Series) + +pci:v00008086d0000248Asv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d0000248Asv00008086sd00001958* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller (vpr Matrix 170B4) + +pci:v00008086d0000248Asv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801CAM IDE U100 Controller (Latitude C640) + +pci:v00008086d0000248B* + ID_MODEL_FROM_DATABASE=82801CA Ultra ATA Storage Controller + +pci:v00008086d0000248Bsv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=82801CA Ultra ATA Storage Controller (P4DP6) + +pci:v00008086d0000248C* + ID_MODEL_FROM_DATABASE=82801CAM ISA Bridge (LPC) + +pci:v00008086d000024C0* + ID_MODEL_FROM_DATABASE=82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge + +pci:v00008086d000024C0sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge (NetVista A30p) + +pci:v00008086d000024C0sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge (845PE Max (MS-6580)) + +pci:v00008086d000024C1* + ID_MODEL_FROM_DATABASE=82801DBL (ICH4-L) IDE Controller + +pci:v00008086d000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 + +pci:v00008086d000024C2sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (NetVista A30p) + +pci:v00008086d000024C2sv00001014sd0000052D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (ThinkPad) + +pci:v00008086d000024C2sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (TravelMate 290) + +pci:v00008086d000024C2sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C2sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Optiplex GX260) + +pci:v00008086d000024C2sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Dimension 2400) + +pci:v00008086d000024C2sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Latitude D505) + +pci:v00008086d000024C2sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Inspiron 700m/710m) + +pci:v00008086d000024C2sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Inspiron 5160) + +pci:v00008086d000024C2sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (NC8000 laptop) + +pci:v00008086d000024C2sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (NC6000 laptop) + +pci:v00008086d000024C2sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (tc1100 tablet) + +pci:v00008086d000024C2sv00001043sd00008089* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (P4B533) + +pci:v00008086d000024C2sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (MIM2000) + +pci:v00008086d000024C2sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (PC8 onboard USB 1.x) + +pci:v00008086d000024C2sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (X10 Laptop) + +pci:v00008086d000024C2sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (P30/P35 notebook) + +pci:v00008086d000024C2sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (845PE Max (MS-6580)) + +pci:v00008086d000024C2sv00001509sd00002990* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Averatec 5110H laptop) + +pci:v00008086d000024C2sv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024C2sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Amilo M1420) + +pci:v00008086d000024C2sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Cx9 / Vx9 mainboard) + +pci:v00008086d000024C2sv00008086sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Latitude X300) + +pci:v00008086d000024C2sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (Latitude D400/D500) + +pci:v00008086d000024C2sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (CC9-SAMBA) + +pci:v00008086d000024C2sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #1 (CD2-BEBOP) + +pci:v00008086d000024C3* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller + +pci:v00008086d000024C3sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (NetVista A30p) + +pci:v00008086d000024C3sv00001014sd0000052D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (ThinkPad) + +pci:v00008086d000024C3sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (TravelMate 290) + +pci:v00008086d000024C3sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C3sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Optiplex GX260) + +pci:v00008086d000024C3sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Latitude X300) + +pci:v00008086d000024C3sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Dimension 2400) + +pci:v00008086d000024C3sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Inspiron 700m/710m) + +pci:v00008086d000024C3sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (NC8000 laptop) + +pci:v00008086d000024C3sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (NC6000 laptop) + +pci:v00008086d000024C3sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (tc1100 tablet) + +pci:v00008086d000024C3sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (MIM2000) + +pci:v00008086d000024C3sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (PC8 onboard SMbus) + +pci:v00008086d000024C3sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (X10 Laptop) + +pci:v00008086d000024C3sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (P30/P35 notebook) + +pci:v00008086d000024C3sv00001458sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (GA-8PE667 Ultra) + +pci:v00008086d000024C3sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (845PE Max (MS-6580)) + +pci:v00008086d000024C3sv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024C3sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Amilo M1420) + +pci:v00008086d000024C3sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d000024C3sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (CC9-SAMBA) + +pci:v00008086d000024C3sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) SMBus Controller (CD2-BEBOP) + +pci:v00008086d000024C4* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 + +pci:v00008086d000024C4sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (NetVista A30p) + +pci:v00008086d000024C4sv00001014sd0000052D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (ThinkPad) + +pci:v00008086d000024C4sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (TravelMate 290) + +pci:v00008086d000024C4sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C4sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Optiplex GX260) + +pci:v00008086d000024C4sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Dimension 2400) + +pci:v00008086d000024C4sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Latitude D505) + +pci:v00008086d000024C4sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Inspiron 700m/710m) + +pci:v00008086d000024C4sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Inspiron 5160) + +pci:v00008086d000024C4sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (NC8000 laptop) + +pci:v00008086d000024C4sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (NC6000 laptop) + +pci:v00008086d000024C4sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (tc1100 tablet) + +pci:v00008086d000024C4sv00001043sd00008089* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (P4B533) + +pci:v00008086d000024C4sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (MIM2000) + +pci:v00008086d000024C4sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (P30/P35 notebook) + +pci:v00008086d000024C4sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (845PE Max (MS-6580)) + +pci:v00008086d000024C4sv00001509sd00002990* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Averatec 5110H) + +pci:v00008086d000024C4sv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024C4sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Cx9 / Vx9 mainboard) + +pci:v00008086d000024C4sv00008086sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Latitude X300) + +pci:v00008086d000024C4sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (Latitude D400/D500) + +pci:v00008086d000024C4sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (CC9-SAMBA) + +pci:v00008086d000024C4sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #2 (CD2-BEBOP) + +pci:v00008086d000024C5* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller + +pci:v00008086d000024C5sv00000E11sd000000B8* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Analog Devices Inc. codec [SoundMAX]) + +pci:v00008086d000024C5sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (NetVista A30p) + +pci:v00008086d000024C5sv00001014sd00000537* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (ThinkPad T4x Series) + +pci:v00008086d000024C5sv00001014sd0000055F* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Thinkpad R50e model 1634) + +pci:v00008086d000024C5sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (TravelMate 290) + +pci:v00008086d000024C5sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C5sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Latitude D400) + +pci:v00008086d000024C5sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Latitude X300) + +pci:v00008086d000024C5sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Latitude D500) + +pci:v00008086d000024C5sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Dimension 2400) + +pci:v00008086d000024C5sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Latitude D505) + +pci:v00008086d000024C5sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Inspiron 700m/710m [SigmaTel STAC9750,51]) + +pci:v00008086d000024C5sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Inspiron 5160) + +pci:v00008086d000024C5sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (NC8000 laptop) + +pci:v00008086d000024C5sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (NC6000 laptop) + +pci:v00008086d000024C5sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (tc1100 tablet) + +pci:v00008086d000024C5sv00001043sd00001713* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (M2400N/M6800N laptop) + +pci:v00008086d000024C5sv00001043sd000080B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (P4B533) + +pci:v00008086d000024C5sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (MIM2000) + +pci:v00008086d000024C5sv00001179sd00000201* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Toshiba Tecra M1) + +pci:v00008086d000024C5sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (X10 Laptop) + +pci:v00008086d000024C5sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (P30/P35 notebook) + +pci:v00008086d000024C5sv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (GA-8PE667 Ultra) + +pci:v00008086d000024C5sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (845PE Max (MS-6580)) + +pci:v00008086d000024C5sv00001734sd00001005* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (D1451 (SCENIC N300, i845GV) Sigmatel STAC9750T) + +pci:v00008086d000024C5sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Audio Controller (Amilo M1420) + +pci:v00008086d000024C6* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller + +pci:v00008086d000024C6sv00001014sd00000524* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (ThinkPad T4x Series) + +pci:v00008086d000024C6sv00001014sd00000525* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (ThinkPad) + +pci:v00008086d000024C6sv00001014sd00000559* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (ThinkPad R50e) + +pci:v00008086d000024C6sv00001025sd0000003C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (Aspire 2001WLCi (Compal CL50 motherboard) implementation) + +pci:v00008086d000024C6sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (TravelMate 290) + +pci:v00008086d000024C6sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C6sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (Inspiron 5160) + +pci:v00008086d000024C6sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (NC8000 laptop) + +pci:v00008086d000024C6sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (NC6000 laptop) + +pci:v00008086d000024C6sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (tc1100 tablet) + +pci:v00008086d000024C6sv00001043sd00001716* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (M2400N laptop) + +pci:v00008086d000024C6sv00001043sd00001826* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (M6800N) + +pci:v00008086d000024C6sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (MIM2000) + +pci:v00008086d000024C6sv0000134Dsd00004C21* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (Latitude D500) + +pci:v00008086d000024C6sv0000144Dsd00002115* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (X10 Laptop) + +pci:v00008086d000024C6sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (P30/P35 notebook) + +pci:v00008086d000024C6sv000014F1sd00005422* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) AC'97 Modem Controller (D480 MDC V.9x Modem) + +pci:v00008086d000024C7* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 + +pci:v00008086d000024C7sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (NetVista A30p) + +pci:v00008086d000024C7sv00001014sd0000052D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (ThinkPad) + +pci:v00008086d000024C7sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (TravelMate 290) + +pci:v00008086d000024C7sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024C7sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Optiplex GX260) + +pci:v00008086d000024C7sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Dimension 2400) + +pci:v00008086d000024C7sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Latitude D505) + +pci:v00008086d000024C7sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Inspiron 700m/710m) + +pci:v00008086d000024C7sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Inspiron 5160) + +pci:v00008086d000024C7sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (NC8000 laptop) + +pci:v00008086d000024C7sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (NC6000 laptop) + +pci:v00008086d000024C7sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (tc1100 tablet) + +pci:v00008086d000024C7sv00001043sd00008089* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (P4B533) + +pci:v00008086d000024C7sv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (MIM2000) + +pci:v00008086d000024C7sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (P30/P35 notebook) + +pci:v00008086d000024C7sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (845PE Max (MS-6580)) + +pci:v00008086d000024C7sv00001509sd00002990* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Averatec 5110H) + +pci:v00008086d000024C7sv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024C7sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Cx9 / Vx9 mainboard) + +pci:v00008086d000024C7sv00008086sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Latitude X300) + +pci:v00008086d000024C7sv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (Latitude D400/D500) + +pci:v00008086d000024C7sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (CC9-SAMBA) + +pci:v00008086d000024C7sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) USB UHCI Controller #3 (CD2-BEBOP) + +pci:v00008086d000024CA* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller + +pci:v00008086d000024CAsv00001014sd0000052D* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (ThinkPad) + +pci:v00008086d000024CAsv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (TravelMate 290) + +pci:v00008086d000024CAsv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024CAsv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Latitude X300) + +pci:v00008086d000024CAsv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Latitude D505) + +pci:v00008086d000024CAsv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Inspiron 700m/710m) + +pci:v00008086d000024CAsv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Inspiron 5160) + +pci:v00008086d000024CAsv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (NC8000 laptop) + +pci:v00008086d000024CAsv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (NC6000 laptop) + +pci:v00008086d000024CAsv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (tc1100 tablet) + +pci:v00008086d000024CAsv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (MIM2000) + +pci:v00008086d000024CAsv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (X10 Laptop) + +pci:v00008086d000024CAsv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (P30/P35 notebook) + +pci:v00008086d000024CAsv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Amilo M1420) + +pci:v00008086d000024CAsv00008086sd00004541* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) IDE Controller (Latitude D400/D500) + +pci:v00008086d000024CB* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller + +pci:v00008086d000024CBsv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (NetVista A30p) + +pci:v00008086d000024CBsv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (Optiplex GX260) + +pci:v00008086d000024CBsv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (Dimension 2400) + +pci:v00008086d000024CBsv00001043sd00008089* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (P4B533) + +pci:v00008086d000024CBsv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (PC8 onboard IDE) + +pci:v00008086d000024CBsv00001458sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (GA-8PE667 Ultra) + +pci:v00008086d000024CBsv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (845PE Max (MS-6580)) + +pci:v00008086d000024CBsv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024CBsv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d000024CBsv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (CC9-SAMBA) + +pci:v00008086d000024CBsv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB (ICH4) IDE Controller (CD2-BEBOP) + +pci:v00008086d000024CC* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) LPC Interface Bridge + +pci:v00008086d000024CCsv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) LPC Interface Bridge (P30 notebook) + +pci:v00008086d000024CCsv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DBM (ICH4-M) LPC Interface Bridge (Amilo M1420) + +pci:v00008086d000024CD* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller + +pci:v00008086d000024CDsv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (NetVista A30p) + +pci:v00008086d000024CDsv00001014sd0000052E* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (ThinkPad) + +pci:v00008086d000024CDsv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (TravelMate 290) + +pci:v00008086d000024CDsv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Extensa 3000 series laptop: Intel 82801DBM (ICH4-M)) + +pci:v00008086d000024CDsv00001028sd0000011D* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Latitude D600) + +pci:v00008086d000024CDsv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Optiplex GX260) + +pci:v00008086d000024CDsv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Latitude D400) + +pci:v00008086d000024CDsv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Latitude D500) + +pci:v00008086d000024CDsv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Dimension 2400) + +pci:v00008086d000024CDsv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Latitude D505) + +pci:v00008086d000024CDsv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Inspiron 700m/710m) + +pci:v00008086d000024CDsv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Inspiron 5160) + +pci:v00008086d000024CDsv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (NC8000 laptop) + +pci:v00008086d000024CDsv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (NC6000 laptop) + +pci:v00008086d000024CDsv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (tc1100 tablet) + +pci:v00008086d000024CDsv00001043sd00008089* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (P4B533) + +pci:v00008086d000024CDsv00001071sd00008160* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (MIM2000) + +pci:v00008086d000024CDsv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (PC8 onboard USB 2.0) + +pci:v00008086d000024CDsv00001179sd0000FF00* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Satellite 2430) + +pci:v00008086d000024CDsv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (X10 Laptop) + +pci:v00008086d000024CDsv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (P30/P35 notebook) + +pci:v00008086d000024CDsv00001462sd00003981* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (845PE Max (MS-6580)) + +pci:v00008086d000024CDsv00001509sd00001968* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Averatec 5110H) + +pci:v00008086d000024CDsv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d000024CDsv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Amilo M1420) + +pci:v00008086d000024CDsv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (QEMU Virtual Machine) + +pci:v00008086d000024CDsv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d000024CDsv00008086sd000024C2* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (Latitude X300) + +pci:v00008086d000024CDsv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (CC9-SAMBA) + +pci:v00008086d000024CDsv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82801DB/DBM (ICH4/ICH4-M) USB2 EHCI Controller (CD2-BEBOP) + +pci:v00008086d000024D0* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) LPC Interface Bridge + +pci:v00008086d000024D1* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller + +pci:v00008086d000024D1sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D1sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (Precision 470) + +pci:v00008086d000024D1sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (PowerEdge SC1425) + +pci:v00008086d000024D1sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (d530 CMT (DG746A)) + +pci:v00008086d000024D1sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (ProLiant DL140 G2) + +pci:v00008086d000024D1sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (P4P800 series motherboard) + +pci:v00008086d000024D1sv00001458sd000024D1* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024D1sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D1sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024D1sv00001565sd00005200* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (P4TSV Motherboard (865G)) + +pci:v00008086d000024D1sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (P4SCE Mainboard) + +pci:v00008086d000024D1sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (S875WP1-E mainboard) + +pci:v00008086d000024D1sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (Desktop Board D865GBF) + +pci:v00008086d000024D1sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (Desktop Board D865GLC) + +pci:v00008086d000024D1sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) SATA Controller (D865PERL mainboard) + +pci:v00008086d000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 + +pci:v00008086d000024D2sv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (eServer xSeries server mainboard) + +pci:v00008086d000024D2sv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (eServer xSeries server mainboard) + +pci:v00008086d000024D2sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D2sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (Precision 470) + +pci:v00008086d000024D2sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PowerEdge 1850 onboard UHCI) + +pci:v00008086d000024D2sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PowerEdge 2850 onboard UHCI) + +pci:v00008086d000024D2sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PowerEdge 6850 onboard UHCI) + +pci:v00008086d000024D2sv00001028sd00000183* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PowerEdge 1800) + +pci:v00008086d000024D2sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PowerEdge SC1425) + +pci:v00008086d000024D2sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (NX9500) + +pci:v00008086d000024D2sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (d530 CMT (DG746A)) + +pci:v00008086d000024D2sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (ProLiant DL140 G2) + +pci:v00008086d000024D2sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (P4P800/P5P800 series motherboard) + +pci:v00008086d000024D2sv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (GA-8IPE1000/8KNXP motherboard) + +pci:v00008086d000024D2sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D2sv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (P4TSV Motherboard (865G)) + +pci:v00008086d000024D2sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (P4SCE Mainboard) + +pci:v00008086d000024D2sv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (PRIMERGY RX/TX series onboard UHCI) + +pci:v00008086d000024D2sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (S875WP1-E mainboard) + +pci:v00008086d000024D2sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (Desktop Board D865GBF) + +pci:v00008086d000024D2sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (Desktop Board D865GLC) + +pci:v00008086d000024D2sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (D865PERL mainboard) + +pci:v00008086d000024D3* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller + +pci:v00008086d000024D3sv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (eServer xSeries server mainboard) + +pci:v00008086d000024D3sv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (eServer xSeries server mainboard) + +pci:v00008086d000024D3sv00001028sd00000156* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Precision 360) + +pci:v00008086d000024D3sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D3sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Precision 470) + +pci:v00008086d000024D3sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (d330 uT) + +pci:v00008086d000024D3sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (ProLiant DL140 G2) + +pci:v00008086d000024D3sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (P4P800/P5P800 series motherboard) + +pci:v00008086d000024D3sv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024D3sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D3sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024D3sv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (P4TSV Motherboard (865G)) + +pci:v00008086d000024D3sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (P4SCE Mainboard) + +pci:v00008086d000024D3sv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (PRIMERGY RX/TX S2 series SMBus) + +pci:v00008086d000024D3sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (S875WP1-E mainboard) + +pci:v00008086d000024D3sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Desktop Board D865GBF) + +pci:v00008086d000024D3sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (Desktop Board D865GLC) + +pci:v00008086d000024D3sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (D865PERL mainboard) + +pci:v00008086d000024D4* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 + +pci:v00008086d000024D4sv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (eServer xSeries server mainboard) + +pci:v00008086d000024D4sv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (eServer xSeries server mainboard) + +pci:v00008086d000024D4sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D4sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (Precision 470) + +pci:v00008086d000024D4sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PowerEdge 1850 onboard UHCI) + +pci:v00008086d000024D4sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PowerEdge 2850 onboard UHCI) + +pci:v00008086d000024D4sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PowerEdge 6850 onboard UHCI) + +pci:v00008086d000024D4sv00001028sd00000183* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PowerEdge 1800) + +pci:v00008086d000024D4sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PowerEdge SC1425) + +pci:v00008086d000024D4sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (NX9500) + +pci:v00008086d000024D4sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (d530 CMT (DG746A)) + +pci:v00008086d000024D4sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (ProLiant DL140 G2) + +pci:v00008086d000024D4sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (P4P800/P5P800 series motherboard) + +pci:v00008086d000024D4sv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024D4sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D4sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024D4sv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (P4TSV Motherboard (865G)) + +pci:v00008086d000024D4sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (P4SCE Mainboard) + +pci:v00008086d000024D4sv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (PRIMERGY RX/TX S2 series onboard UHCI) + +pci:v00008086d000024D4sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (S875WP1-E mainboard) + +pci:v00008086d000024D4sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (Desktop Board D865GBF) + +pci:v00008086d000024D4sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (Desktop Board D865GLC) + +pci:v00008086d000024D4sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (D865PERL mainboard) + +pci:v00008086d000024D5* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller + +pci:v00008086d000024D5sv0000100Asd0000147B* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Abit IS7-E motherboard) + +pci:v00008086d000024D5sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D5sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Precision 470) + +pci:v00008086d000024D5sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (NX9500) + +pci:v00008086d000024D5sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (d330 uT) + +pci:v00008086d000024D5sv00001043sd000080F3* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (P4P800 series motherboard) + +pci:v00008086d000024D5sv00001043sd0000810F* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (P5P800-MX Mainboard) + +pci:v00008086d000024D5sv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (GA-8IPE1000/8KNXP motherboard) + +pci:v00008086d000024D5sv00001462sd00000080* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (865PE Neo2-V (MS-6788) Mainboard) + +pci:v00008086d000024D5sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D5sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024D5sv00008086sd0000A000* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (D865PERL mainboard) + +pci:v00008086d000024D5sv00008086sd0000E000* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (D865PERL mainboard) + +pci:v00008086d000024D5sv00008086sd0000E001* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Desktop Board D865GBF) + +pci:v00008086d000024D5sv00008086sd0000E002* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (SoundMax Intergrated Digital Audio) + +pci:v00008086d000024D6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Modem Controller + +pci:v00008086d000024D6sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Modem Controller (NX9500) + +pci:v00008086d000024D7* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 + +pci:v00008086d000024D7sv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (xSeries server mainboard) + +pci:v00008086d000024D7sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (Precision Workstation 670 Mainboard) + +pci:v00008086d000024D7sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (Precision 470) + +pci:v00008086d000024D7sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (PowerEdge 1850 onboard UHCI) + +pci:v00008086d000024D7sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (PowerEdge 2850 onboard UHCI) + +pci:v00008086d000024D7sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (PowerEdge 6850 onboard UHCI) + +pci:v00008086d000024D7sv00001028sd00000183* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (PowerEdge 1800) + +pci:v00008086d000024D7sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (NX9500) + +pci:v00008086d000024D7sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (d530 CMT (DG746A)) + +pci:v00008086d000024D7sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (P4P800/P5P800 series motherboard) + +pci:v00008086d000024D7sv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024D7sv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (865PE Neo2 (MS-6728)) + +pci:v00008086d000024D7sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024D7sv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (P4TSV Motherboard (865G)) + +pci:v00008086d000024D7sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (P4SCE Mainboard) + +pci:v00008086d000024D7sv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (PRIMERGY RX/TX S2 series onboard UHCI) + +pci:v00008086d000024D7sv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (S875WP1-E mainboard) + +pci:v00008086d000024D7sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (Desktop Board D865GBF) + +pci:v00008086d000024D7sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (Desktop Board D865GLC) + +pci:v00008086d000024D7sv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (D865PERL mainboard) + +pci:v00008086d000024DB* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller + +pci:v00008086d000024DBsv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (eServer xSeries server mainboard) + +pci:v00008086d000024DBsv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (eServer xSeries server mainboard) + +pci:v00008086d000024DBsv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024DBsv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (Precision 470) + +pci:v00008086d000024DBsv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (PowerEdge 1850 IDE Controller) + +pci:v00008086d000024DBsv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (PowerEdge 2850 IDE Controller) + +pci:v00008086d000024DBsv00001028sd00000170* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (PowerEdge 6850 IDE Controller) + +pci:v00008086d000024DBsv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (PowerEdge SC1425) + +pci:v00008086d000024DBsv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (NX9500) + +pci:v00008086d000024DBsv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (d530 CMT (DG746A)) + +pci:v00008086d000024DBsv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (P4P800/P5P800 series motherboard) + +pci:v00008086d000024DBsv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024DBsv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d000024DBsv00001462sd00007580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (MSI 875P) + +pci:v00008086d000024DBsv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024DBsv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (P4TSV Motherboard (865G)) + +pci:v00008086d000024DBsv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (P4SCE Mainboard) + +pci:v00008086d000024DBsv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (PRIMERGY RX/TX S2 series onboard IDE) + +pci:v00008086d000024DBsv00008086sd000024DB* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (P4C800 Mainboard) + +pci:v00008086d000024DBsv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (S875WP1-E mainboard) + +pci:v00008086d000024DBsv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (Desktop Board D865GBF) + +pci:v00008086d000024DBsv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (Desktop Board D865GLC) + +pci:v00008086d000024DBsv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (D865PERL mainboard) + +pci:v00008086d000024DC* + ID_MODEL_FROM_DATABASE=82801EB (ICH5) LPC Interface Bridge + +pci:v00008086d000024DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller + +pci:v00008086d000024DDsv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (eServer xSeries server mainboard) + +pci:v00008086d000024DDsv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (eServer xSeries server mainboard) + +pci:v00008086d000024DDsv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024DDsv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (Precision 470) + +pci:v00008086d000024DDsv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (PowerEdge 1850 onboard EHCI) + +pci:v00008086d000024DDsv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (PowerEdge 2850 onboard EHCI) + +pci:v00008086d000024DDsv00001028sd00000170* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (PowerEdge 6850 onboard EHCI) + +pci:v00008086d000024DDsv00001028sd00000183* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (PowerEdge 1800) + +pci:v00008086d000024DDsv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (PowerEdge SC1425) + +pci:v00008086d000024DDsv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (NX9500) + +pci:v00008086d000024DDsv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (d530 CMT (DG746A)) + +pci:v00008086d000024DDsv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (ProLiant DL140 G2) + +pci:v00008086d000024DDsv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (P4P800/P5P800 series motherboard) + +pci:v00008086d000024DDsv00001458sd00005006* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024DDsv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (865PE Neo2 (MS-6728)) + +pci:v00008086d000024DDsv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024DDsv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (S875WP1-E mainboard) + +pci:v00008086d000024DDsv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (Desktop Board D865GBF) + +pci:v00008086d000024DDsv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (Desktop Board D865GLC) + +pci:v00008086d000024DDsv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (D865PERL mainboard) + +pci:v00008086d000024DE* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 + +pci:v00008086d000024DEsv00001014sd000002ED* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (xSeries server mainboard) + +pci:v00008086d000024DEsv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (Precision Workstation 670 Mainboard) + +pci:v00008086d000024DEsv00001028sd00000169* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (Precision 470) + +pci:v00008086d000024DEsv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (P4P800/P5P800 series motherboard) + +pci:v00008086d000024DEsv00001458sd000024D2* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d000024DEsv00001462sd00007280* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (865PE Neo2 (MS-6728)) + +pci:v00008086d000024DEsv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (Hetis 865GV-E (MS-7065)) + +pci:v00008086d000024DEsv00001565sd00003101* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (P4TSV Motherboard (865G)) + +pci:v00008086d000024DEsv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (P4SCE Mainboard) + +pci:v00008086d000024DEsv00001734sd0000101C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (PRIMERGY RX/TX S2 series onboard UHCI) + +pci:v00008086d000024DEsv00008086sd00003427* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (S875WP1-E mainboard) + +pci:v00008086d000024DEsv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (Desktop Board D865GBF) + +pci:v00008086d000024DEsv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (Desktop Board D865GLC) + +pci:v00008086d000024DEsv00008086sd0000524C* + ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (D865PERL mainboard) + +pci:v00008086d000024DF* + ID_MODEL_FROM_DATABASE=82801ER (ICH5R) SATA Controller + +pci:v00008086d000024DFsv00001028sd00000168* + ID_MODEL_FROM_DATABASE=82801ER (ICH5R) SATA Controller (Precision Workstation 670 Mainboard) + +pci:v00008086d000024F0* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] + +pci:v00008086d000024F0sv000010A9sd0000802E* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-path HFI 100 Series, 1-port A-board) + +pci:v00008086d000024F0sv000010A9sd0000802F* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-path HFI 100 Series, 2-port A-board) + +pci:v00008086d000024F0sv000010A9sd00008030* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-path HFI 100 Series, 1-port B-board) + +pci:v00008086d000024F0sv000010A9sd00008031* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-path HFI 100 Series, 2-port B-board) + +pci:v00008086d000024F0sv000015D9sd00000934* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x16, SIOM Module) + +pci:v00008086d000024F0sv00001CB8sd00000001* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x16, TC4600 QSFP28) + +pci:v00008086d000024F0sv00001CB8sd00000002* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x16, TC6600 Fixed Port) + +pci:v00008086d000024F0sv00008086sd00002628* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x16) + +pci:v00008086d000024F0sv00008086sd00002629* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x8) + +pci:v00008086d000024F0sv00008086sd0000262A* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 2 Ports, Split PCIe x16) + +pci:v00008086d000024F0sv00008086sd0000262D* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [discrete] (Omni-Path HFI Adapter 100 Series, 1 Port, PCIe x16, IO Module AHWKPTP100HF) + +pci:v00008086d000024F1* + ID_MODEL_FROM_DATABASE=Omni-Path HFI Silicon 100 Series [integrated] + +pci:v00008086d000024F3* + ID_MODEL_FROM_DATABASE=Wireless 8260 + +pci:v00008086d000024F3sv00008086sd00000010* + ID_MODEL_FROM_DATABASE=Wireless 8260 (Dual Band Wireless-AC 8260) + +pci:v00008086d000024F4* + ID_MODEL_FROM_DATABASE=Wireless 8260 + +pci:v00008086d000024F4sv00008086sd00000030* + ID_MODEL_FROM_DATABASE=Wireless 8260 (Dual Band Wireless-AC 8260) + +pci:v00008086d00002500* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge (MCH) + +pci:v00008086d00002500sv00001028sd00000095* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge (MCH) (Precision Workstation 220 Chipset) + +pci:v00008086d00002500sv00001043sd0000801C* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge (MCH) (P3C-2000 system chipset) + +pci:v00008086d00002501* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge (MCH) + +pci:v00008086d00002501sv00001043sd0000801C* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge (MCH) (P3C-2000 system chipset) + +pci:v00008086d0000250B* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset Host Bridge + +pci:v00008086d0000250F* + ID_MODEL_FROM_DATABASE=82820 820 (Camino) Chipset AGP Bridge + +pci:v00008086d00002520* + ID_MODEL_FROM_DATABASE=82805AA MTH Memory Translator Hub + +pci:v00008086d00002521* + ID_MODEL_FROM_DATABASE=82804AA MRH-S Memory Repeater Hub for SDRAM + +pci:v00008086d00002530* + ID_MODEL_FROM_DATABASE=82850 850 (Tehama) Chipset Host Bridge (MCH) + +pci:v00008086d00002530sv00001028sd000000C7* + ID_MODEL_FROM_DATABASE=82850 850 (Tehama) Chipset Host Bridge (MCH) (Dimension 8100) + +pci:v00008086d00002530sv0000147Bsd00000507* + ID_MODEL_FROM_DATABASE=82850 850 (Tehama) Chipset Host Bridge (MCH) (TH7II-RAID) + +pci:v00008086d00002531* + ID_MODEL_FROM_DATABASE=82860 860 (Wombat) Chipset Host Bridge (MCH) + +pci:v00008086d00002531sv00001028sd000000D8* + ID_MODEL_FROM_DATABASE=82860 860 (Wombat) Chipset Host Bridge (MCH) (Precision 530) + +pci:v00008086d00002532* + ID_MODEL_FROM_DATABASE=82850 850 (Tehama) Chipset AGP Bridge + +pci:v00008086d00002533* + ID_MODEL_FROM_DATABASE=82860 860 (Wombat) Chipset AGP Bridge + +pci:v00008086d00002534* + ID_MODEL_FROM_DATABASE=82860 860 (Wombat) Chipset PCI Bridge + +pci:v00008086d00002540* + ID_MODEL_FROM_DATABASE=E7500 Memory Controller Hub + +pci:v00008086d00002540sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=E7500 Memory Controller Hub (P4DP6) + +pci:v00008086d00002541* + ID_MODEL_FROM_DATABASE=E7500/E7501 Host RASUM Controller + +pci:v00008086d00002541sv000015D9sd00003480* + ID_MODEL_FROM_DATABASE=E7500/E7501 Host RASUM Controller (P4DP6) + +pci:v00008086d00002541sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=E7500/E7501 Host RASUM Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d00002541sv00008086sd00003424* + ID_MODEL_FROM_DATABASE=E7500/E7501 Host RASUM Controller (SE7501HG2 Mainboard) + +pci:v00008086d00002543* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface B PCI-to-PCI Bridge + +pci:v00008086d00002544* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface B RASUM Controller + +pci:v00008086d00002544sv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface B RASUM Controller (Cx9 / Vx9 mainboard) + +pci:v00008086d00002545* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface C PCI-to-PCI Bridge + +pci:v00008086d00002546* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface C RASUM Controller + +pci:v00008086d00002547* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface D PCI-to-PCI Bridge + +pci:v00008086d00002548* + ID_MODEL_FROM_DATABASE=E7500/E7501 Hub Interface D RASUM Controller + +pci:v00008086d0000254C* + ID_MODEL_FROM_DATABASE=E7501 Memory Controller Hub + +pci:v00008086d0000254Csv00004C53sd00001090* + ID_MODEL_FROM_DATABASE=E7501 Memory Controller Hub (Cx9 / Vx9 mainboard) + +pci:v00008086d0000254Csv00008086sd00003424* + ID_MODEL_FROM_DATABASE=E7501 Memory Controller Hub (SE7501HG2 Mainboard) + +pci:v00008086d00002550* + ID_MODEL_FROM_DATABASE=E7505 Memory Controller Hub + +pci:v00008086d00002551* + ID_MODEL_FROM_DATABASE=E7505/E7205 Series RAS Controller + +pci:v00008086d00002552* + ID_MODEL_FROM_DATABASE=E7505/E7205 PCI-to-AGP Bridge + +pci:v00008086d00002553* + ID_MODEL_FROM_DATABASE=E7505 Hub Interface B PCI-to-PCI Bridge + +pci:v00008086d00002554* + ID_MODEL_FROM_DATABASE=E7505 Hub Interface B PCI-to-PCI Bridge RAS Controller + +pci:v00008086d0000255D* + ID_MODEL_FROM_DATABASE=E7205 Memory Controller Hub + +pci:v00008086d00002560* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface + +pci:v00008086d00002560sv00001028sd00000126* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface (Optiplex GX260) + +pci:v00008086d00002560sv00001458sd00002560* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface (GA-8PE667 Ultra) + +pci:v00008086d00002560sv00001462sd00005800* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE/PE DRAM Controller/Host-Hub Interface (845PE Max (MS-6580)) + +pci:v00008086d00002561* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE/PE Host-to-AGP Bridge + +pci:v00008086d00002562* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device + +pci:v00008086d00002562sv00000E11sd000000B9* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (Evo D510 SFF) + +pci:v00008086d00002562sv00001014sd00000267* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (NetVista A30p) + +pci:v00008086d00002562sv00001028sd00000160* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (Dimension 2400) + +pci:v00008086d00002562sv00001734sd00001003* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (D1521 Mainboard (Fujitsu-Siemens)) + +pci:v00008086d00002562sv00001734sd00001004* + ID_MODEL_FROM_DATABASE=82845G/GL[Brookdale-G]/GE Chipset Integrated Graphics Device (D1451 Mainboard (SCENIC N300, i845GV)) + +pci:v00008086d00002570* + ID_MODEL_FROM_DATABASE=82865G/PE/P DRAM Controller/Host-Hub Interface + +pci:v00008086d00002570sv0000103Csd0000006A* + ID_MODEL_FROM_DATABASE=82865G/PE/P DRAM Controller/Host-Hub Interface (NX9500) + +pci:v00008086d00002570sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82865G/PE/P DRAM Controller/Host-Hub Interface (d330 uT) + +pci:v00008086d00002570sv00001043sd000080F2* + ID_MODEL_FROM_DATABASE=82865G/PE/P DRAM Controller/Host-Hub Interface (P4P800/P5P800 series motherboard) + +pci:v00008086d00002570sv00001458sd00002570* + ID_MODEL_FROM_DATABASE=82865G/PE/P DRAM Controller/Host-Hub Interface (GA-8IPE1000 Pro2 motherboard (865PE)) + +pci:v00008086d00002571* + ID_MODEL_FROM_DATABASE=82865G/PE/P AGP Bridge + +pci:v00008086d00002572* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller + +pci:v00008086d00002572sv00001028sd0000019D* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Dimension 3000) + +pci:v00008086d00002572sv0000103Csd000012BC* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (D530 sff(dc578av)) + +pci:v00008086d00002572sv00001043sd000080A5* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (P5P800-MX Mainboard) + +pci:v00008086d00002572sv00001462sd00007650* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Hetis 865GV-E (MS-7065)) + +pci:v00008086d00002572sv00001734sd0000101B* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Fujitsu-Siemens Scenic E300 i865GV) + +pci:v00008086d00002572sv00008086sd00004246* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Desktop Board D865GBF) + +pci:v00008086d00002572sv00008086sd00004C43* + ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Desktop Board D865GLC) + +pci:v00008086d00002573* + ID_MODEL_FROM_DATABASE=82865G/PE/P PCI to CSA Bridge + +pci:v00008086d00002576* + ID_MODEL_FROM_DATABASE=82865G/PE/P Processor to I/O Memory Interface + +pci:v00008086d00002578* + ID_MODEL_FROM_DATABASE=82875P/E7210 Memory Controller Hub + +pci:v00008086d00002578sv00001458sd00002578* + ID_MODEL_FROM_DATABASE=82875P/E7210 Memory Controller Hub (GA-8KNXP motherboard (875P)) + +pci:v00008086d00002578sv00001462sd00007580* + ID_MODEL_FROM_DATABASE=82875P/E7210 Memory Controller Hub (MS-6758 (875P Neo)) + +pci:v00008086d00002578sv000015D9sd00004580* + ID_MODEL_FROM_DATABASE=82875P/E7210 Memory Controller Hub (P4SCE Motherboard) + +pci:v00008086d00002579* + ID_MODEL_FROM_DATABASE=82875P Processor to AGP Controller + +pci:v00008086d0000257B* + ID_MODEL_FROM_DATABASE=82875P/E7210 Processor to PCI to CSA Bridge + +pci:v00008086d0000257E* + ID_MODEL_FROM_DATABASE=82875P/E7210 Processor to I/O Memory Interface + +pci:v00008086d00002580* + ID_MODEL_FROM_DATABASE=82915G/P/GV/GL/PL/910GL Memory Controller Hub + +pci:v00008086d00002580sv00001458sd00002580* + ID_MODEL_FROM_DATABASE=82915G/P/GV/GL/PL/910GL Memory Controller Hub (GA-8I915ME-G Mainboard) + +pci:v00008086d00002580sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82915G/P/GV/GL/PL/910GL Memory Controller Hub (915P/G Neo2) + +pci:v00008086d00002580sv00001734sd0000105B* + ID_MODEL_FROM_DATABASE=82915G/P/GV/GL/PL/910GL Memory Controller Hub (Scenic W620) + +pci:v00008086d00002581* + ID_MODEL_FROM_DATABASE=82915G/P/GV/GL/PL/910GL PCI Express Root Port + +pci:v00008086d00002582* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller + +pci:v00008086d00002582sv00001028sd00001079* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (Optiplex GX280) + +pci:v00008086d00002582sv0000103Csd00003006* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (DC7100 SFF(DX878AV)) + +pci:v00008086d00002582sv00001043sd00002582* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (P5GD1-VW Mainboard) + +pci:v00008086d00002582sv00001458sd00002582* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (GA-8I915ME-G Mainboard) + +pci:v00008086d00002582sv00001734sd0000105B* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (Scenic W620) + +pci:v00008086d00002582sv00001849sd00002582* + ID_MODEL_FROM_DATABASE=82915G/GV/910GL Integrated Graphics Controller (ASRock P4Dual-915GL) + +pci:v00008086d00002584* + ID_MODEL_FROM_DATABASE=82925X/XE Memory Controller Hub + +pci:v00008086d00002584sv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82925X/XE Memory Controller Hub (Dimension 8400) + +pci:v00008086d00002585* + ID_MODEL_FROM_DATABASE=82925X/XE PCI Express Root Port + +pci:v00008086d00002588* + ID_MODEL_FROM_DATABASE=E7220/E7221 Memory Controller Hub + +pci:v00008086d00002589* + ID_MODEL_FROM_DATABASE=E7220/E7221 PCI Express Root Port + +pci:v00008086d0000258A* + ID_MODEL_FROM_DATABASE=E7221 Integrated Graphics Controller + +pci:v00008086d00002590* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller + +pci:v00008086d00002590sv00001014sd00000575* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (ThinkPad X41 / Z60t) + +pci:v00008086d00002590sv00001028sd00000182* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (Dell Latitude C610) + +pci:v00008086d00002590sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (Compaq nw8240/nx8220) + +pci:v00008086d00002590sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (NX6110/NC6120) + +pci:v00008086d00002590sv0000104Dsd000081B7* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (Vaio VGN-S3XP) + +pci:v00008086d00002590sv0000A304sd000081B7* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (Vaio VGN-S3XP) + +pci:v00008086d00002590sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (CCD-CALYPSO) + +pci:v00008086d00002590sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (CD3-JIVE) + +pci:v00008086d00002590sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (XB1) + +pci:v00008086d00002591* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM Express PCI Express Root Port + +pci:v00008086d00002591sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=Mobile 915GM/PM Express PCI Express Root Port (Compaq nw8240 Mobile Workstation) + +pci:v00008086d00002592* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller + +pci:v00008086d00002592sv00001014sd00000582* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (ThinkPad X41) + +pci:v00008086d00002592sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (NX6110/NC6120) + +pci:v00008086d00002592sv0000103Csd0000308A* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (NC6220) + +pci:v00008086d00002592sv00001043sd00001881* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (GMA 900 915GM Integrated Graphics) + +pci:v00008086d00002592sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (CCD-CALYPSO) + +pci:v00008086d00002592sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (CD3-JIVE) + +pci:v00008086d00002592sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (XB1) + +pci:v00008086d000025A1* + ID_MODEL_FROM_DATABASE=6300ESB LPC Interface Controller + +pci:v00008086d000025A2* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller + +pci:v00008086d000025A2sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025A2sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (V5D Single Board Computer IDE) + +pci:v00008086d000025A2sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025A2sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (CE9) + +pci:v00008086d000025A2sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (CL9 mainboard) + +pci:v00008086d000025A2sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB PATA Storage Controller (PSL09 PrPMC) + +pci:v00008086d000025A3* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller + +pci:v00008086d000025A3sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025A3sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025A3sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (CE9) + +pci:v00008086d000025A3sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (CL9 mainboard) + +pci:v00008086d000025A3sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025A3sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB SATA Storage Controller (PSL09 PrPMC) + +pci:v00008086d000025A4* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller + +pci:v00008086d000025A4sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025A4sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (V5D Single Board Computer) + +pci:v00008086d000025A4sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025A4sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (CE9) + +pci:v00008086d000025A4sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (CL9 mainboard) + +pci:v00008086d000025A4sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025A4sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB SMBus Controller (PSL09 PrPMC) + +pci:v00008086d000025A6* + ID_MODEL_FROM_DATABASE=6300ESB AC'97 Audio Controller + +pci:v00008086d000025A6sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB AC'97 Audio Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025A6sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB AC'97 Audio Controller (CE9) + +pci:v00008086d000025A6sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB AC'97 Audio Controller (CL9 mainboard) + +pci:v00008086d000025A7* + ID_MODEL_FROM_DATABASE=6300ESB AC'97 Modem Controller + +pci:v00008086d000025A9* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller + +pci:v00008086d000025A9sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025A9sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (V5D Single Board Computer USB) + +pci:v00008086d000025A9sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025A9sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CE9) + +pci:v00008086d000025A9sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CL9 mainboard) + +pci:v00008086d000025A9sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025A9sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (PSL09 PrPMC) + +pci:v00008086d000025AA* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller + +pci:v00008086d000025AAsv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025AAsv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025AAsv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CE9) + +pci:v00008086d000025AAsv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (CL9 mainboard) + +pci:v00008086d000025AAsv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025AAsv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB USB Universal Host Controller (PSL09 PrPMC) + +pci:v00008086d000025AB* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer + +pci:v00008086d000025ABsv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025ABsv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (V5D Single Board Computer) + +pci:v00008086d000025ABsv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (CR11/VR11 Single Board Computer) + +pci:v00008086d000025ABsv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (CE9) + +pci:v00008086d000025ABsv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (QEMU Virtual Machine) + +pci:v00008086d000025ABsv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (CL9 mainboard) + +pci:v00008086d000025ABsv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (Telum ASLP10 Processor AMC) + +pci:v00008086d000025ABsv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB Watchdog Timer (PSL09 PrPMC) + +pci:v00008086d000025AC* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller + +pci:v00008086d000025ACsv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025ACsv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (V5D Single Board Computer) + +pci:v00008086d000025ACsv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025ACsv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (CE9) + +pci:v00008086d000025ACsv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (CL9 mainboard) + +pci:v00008086d000025ACsv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025ACsv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB I/O Advanced Programmable Interrupt Controller (PSL09 PrPMC) + +pci:v00008086d000025AD* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller + +pci:v00008086d000025ADsv00001734sd00001073* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d000025ADsv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (V5D Single Board Computer USB 2.0) + +pci:v00008086d000025ADsv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025ADsv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (CE9) + +pci:v00008086d000025ADsv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (CL9 mainboard) + +pci:v00008086d000025ADsv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025ADsv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB USB2 Enhanced Host Controller (PSL09 PrPMC) + +pci:v00008086d000025AE* + ID_MODEL_FROM_DATABASE=6300ESB 64-bit PCI-X Bridge + +pci:v00008086d000025B0* + ID_MODEL_FROM_DATABASE=6300ESB SATA RAID Controller + +pci:v00008086d000025B0sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=6300ESB SATA RAID Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d000025B0sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=6300ESB SATA RAID Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d000025B0sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=6300ESB SATA RAID Controller (PSL09 PrPMC) + +pci:v00008086d000025C0* + ID_MODEL_FROM_DATABASE=5000X Chipset Memory Controller Hub + +pci:v00008086d000025D0* + ID_MODEL_FROM_DATABASE=5000Z Chipset Memory Controller Hub + +pci:v00008086d000025D4* + ID_MODEL_FROM_DATABASE=5000V Chipset Memory Controller Hub + +pci:v00008086d000025D4sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000V Chipset Memory Controller Hub (X7DVL-E-O motherboard) + +pci:v00008086d000025D8* + ID_MODEL_FROM_DATABASE=5000P Chipset Memory Controller Hub + +pci:v00008086d000025D8sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000P Chipset Memory Controller Hub (X7DBN Motherboard) + +pci:v00008086d000025D8sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000P Chipset Memory Controller Hub (S5000PSLSATA Server Board) + +pci:v00008086d000025E2* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 2 + +pci:v00008086d000025E3* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 3 + +pci:v00008086d000025E4* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 4 + +pci:v00008086d000025E5* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 5 + +pci:v00008086d000025E6* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 6 + +pci:v00008086d000025E7* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x4 Port 7 + +pci:v00008086d000025F0* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers + +pci:v00008086d000025F0sv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers (PowerEdge 1955 FSB Registers) + +pci:v00008086d000025F0sv0000103Csd000031FD* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers (ProLiant DL140 G3) + +pci:v00008086d000025F0sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers (X7DVL-E-O motherboard) + +pci:v00008086d000025F0sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers (X7DBN Motherboard) + +pci:v00008086d000025F0sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FSB Registers (S5000PSLSATA Server Board) + +pci:v00008086d000025F1* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers + +pci:v00008086d000025F1sv0000103Csd000031FD* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (ProLiant DL140 G3) + +pci:v00008086d000025F1sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (X7DVL-E-O motherboard) + +pci:v00008086d000025F1sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (X7DBN Motherboard) + +pci:v00008086d000025F1sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (S5000PSLSATA Server Board) + +pci:v00008086d000025F3* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers + +pci:v00008086d000025F3sv0000103Csd000031FD* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (ProLiant DL140 G3) + +pci:v00008086d000025F3sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (X7DVL-E-O motherboard) + +pci:v00008086d000025F3sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (X7DBN Motherboard) + +pci:v00008086d000025F3sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset Reserved Registers (S5000PSLSATA Server Board) + +pci:v00008086d000025F5* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers + +pci:v00008086d000025F5sv0000103Csd000031FD* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (ProLiant DL140 G3) + +pci:v00008086d000025F5sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (X7DVL-E-O motherboard) + +pci:v00008086d000025F5sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (X7DBN Motherboard) + +pci:v00008086d000025F5sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (S5000PSLSATA Server Board) + +pci:v00008086d000025F6* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers + +pci:v00008086d000025F6sv0000103Csd000031FD* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (ProLiant DL140 G3) + +pci:v00008086d000025F6sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (X7DVL-E-O motherboard) + +pci:v00008086d000025F6sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (X7DBN Motherboard) + +pci:v00008086d000025F6sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=5000 Series Chipset FBD Registers (S5000PSLSATA Server Board) + +pci:v00008086d000025F7* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x8 Port 2-3 + +pci:v00008086d000025F8* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x8 Port 4-5 + +pci:v00008086d000025F9* + ID_MODEL_FROM_DATABASE=5000 Series Chipset PCI Express x8 Port 6-7 + +pci:v00008086d000025FA* + ID_MODEL_FROM_DATABASE=5000X Chipset PCI Express x16 Port 4-7 + +pci:v00008086d00002600* + ID_MODEL_FROM_DATABASE=E8500/E8501 Hub Interface 1.5 + +pci:v00008086d00002600sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 Hub Interface 1.5 (PowerEdge 6850 Hub Interface) + +pci:v00008086d00002601* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port D + +pci:v00008086d00002602* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port C0 + +pci:v00008086d00002603* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port C1 + +pci:v00008086d00002604* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port B0 + +pci:v00008086d00002605* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port B1 + +pci:v00008086d00002606* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port A0 + +pci:v00008086d00002607* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x4 Port A1 + +pci:v00008086d00002608* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x8 Port C + +pci:v00008086d00002609* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x8 Port B + +pci:v00008086d0000260A* + ID_MODEL_FROM_DATABASE=E8500/E8501 PCI Express x8 Port A + +pci:v00008086d0000260C* + ID_MODEL_FROM_DATABASE=E8500/E8501 IMI Registers + +pci:v00008086d00002610* + ID_MODEL_FROM_DATABASE=E8500/E8501 FSB Registers + +pci:v00008086d00002611* + ID_MODEL_FROM_DATABASE=E8500/E8501 Address Mapping Registers + +pci:v00008086d00002612* + ID_MODEL_FROM_DATABASE=E8500/E8501 RAS Registers + +pci:v00008086d00002613* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d00002614* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d00002615* + ID_MODEL_FROM_DATABASE=E8500/E8501 Miscellaneous Registers + +pci:v00008086d00002617* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d00002618* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d00002619* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d0000261A* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d0000261B* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d0000261C* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d0000261D* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d0000261E* + ID_MODEL_FROM_DATABASE=E8500/E8501 Reserved Registers + +pci:v00008086d00002620* + ID_MODEL_FROM_DATABASE=E8500/E8501 eXternal Memory Bridge + +pci:v00008086d00002620sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 eXternal Memory Bridge (PowerEdge 6850 Memory Bridge) + +pci:v00008086d00002621* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Miscellaneous Registers + +pci:v00008086d00002621sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Miscellaneous Registers (PowerEdge 6850 XMB Registers) + +pci:v00008086d00002622* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Memory Interleaving Registers + +pci:v00008086d00002622sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Memory Interleaving Registers (PowerEdge 6850 Memory Interleaving Registers) + +pci:v00008086d00002623* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB DDR Initialization and Calibration + +pci:v00008086d00002623sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB DDR Initialization and Calibration (PowerEdge 6850 DDR Initialization and Calibration) + +pci:v00008086d00002624* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers + +pci:v00008086d00002624sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers (PowerEdge 6850 Reserved Registers) + +pci:v00008086d00002625* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers + +pci:v00008086d00002625sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers (PowerEdge 6850 Reserved Registers) + +pci:v00008086d00002626* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers + +pci:v00008086d00002626sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers (PowerEdge 6850 Reserved Registers) + +pci:v00008086d00002627* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers + +pci:v00008086d00002627sv00001028sd00000170* + ID_MODEL_FROM_DATABASE=E8500/E8501 XMB Reserved Registers (PowerEdge 6850 Reserved Registers) + +pci:v00008086d00002640* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge + +pci:v00008086d00002640sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge (915P/G Neo2) + +pci:v00008086d00002640sv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge (Scenic W620) + +pci:v00008086d00002640sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge (CCD-CALYPSO) + +pci:v00008086d00002640sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge (CD3-JIVE) + +pci:v00008086d00002640sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FR (ICH6/ICH6R) LPC Interface Bridge (XB1) + +pci:v00008086d00002641* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) LPC Interface Bridge + +pci:v00008086d00002641sv00001014sd00000568* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) LPC Interface Bridge (ThinkPad X41) + +pci:v00008086d00002641sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) LPC Interface Bridge (Compaq nw8240/nx8220) + +pci:v00008086d00002641sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) LPC Interface Bridge (NX6110/NC6120) + +pci:v00008086d00002642* + ID_MODEL_FROM_DATABASE=82801FW/FRW (ICH6W/ICH6RW) LPC Interface Bridge + +pci:v00008086d00002651* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller + +pci:v00008086d00002651sv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (Optiplex GX280) + +pci:v00008086d00002651sv00001043sd00002601* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (P5GD1-VW Mainboard) + +pci:v00008086d00002651sv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (Scenic W620) + +pci:v00008086d00002651sv00008086sd00004147* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (D915GAG Motherboard) + +pci:v00008086d00002651sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (CCD-CALYPSO) + +pci:v00008086d00002651sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (CD3-JIVE) + +pci:v00008086d00002651sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FW (ICH6/ICH6W) SATA Controller (XB1) + +pci:v00008086d00002652* + ID_MODEL_FROM_DATABASE=82801FR/FRW (ICH6R/ICH6RW) SATA Controller + +pci:v00008086d00002652sv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FR/FRW (ICH6R/ICH6RW) SATA Controller (Dimension 8400) + +pci:v00008086d00002652sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FR/FRW (ICH6R/ICH6RW) SATA Controller (915P/G Neo2) + +pci:v00008086d00002653* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) SATA Controller + +pci:v00008086d00002653sv00001014sd0000056A* + ID_MODEL_FROM_DATABASE=82801FBM (ICH6M) SATA Controller (ThinkPad X41) + +pci:v00008086d00002658* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 + +pci:v00008086d00002658sv00001014sd00000565* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (ThinkPad X41) + +pci:v00008086d00002658sv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (Dimension 8400) + +pci:v00008086d00002658sv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (Optiplex GX280) + +pci:v00008086d00002658sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (Compaq nw8240/nx8220) + +pci:v00008086d00002658sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (NX6110/NC6120) + +pci:v00008086d00002658sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (P5GD1-VW Mainboard) + +pci:v00008086d00002658sv00001458sd00002558* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (GA-8I915ME-G Mainboard) + +pci:v00008086d00002658sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (915P/G Neo2) + +pci:v00008086d00002658sv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (Scenic W620) + +pci:v00008086d00002658sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (CCD-CALYPSO) + +pci:v00008086d00002658sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (CD3-JIVE) + +pci:v00008086d00002658sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (XB1) + +pci:v00008086d00002659* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 + +pci:v00008086d00002659sv00001014sd00000565* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (ThinkPad X41) + +pci:v00008086d00002659sv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (Dimension 8400) + +pci:v00008086d00002659sv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (Optiplex GX280) + +pci:v00008086d00002659sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (Compaq nw8240/nx8220) + +pci:v00008086d00002659sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (NX6110/NC6120) + +pci:v00008086d00002659sv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (P5GD1-VW Mainboard) + +pci:v00008086d00002659sv00001458sd00002659* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (GA-8I915ME-G Mainboard) + +pci:v00008086d00002659sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (915P/G Neo2) + +pci:v00008086d00002659sv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (Scenic W620) + +pci:v00008086d00002659sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (CCD-CALYPSO) + +pci:v00008086d00002659sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (CD3-JIVE) + +pci:v00008086d00002659sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (XB1) + +pci:v00008086d0000265A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 + +pci:v00008086d0000265Asv00001014sd00000565* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (ThinkPad X41) + +pci:v00008086d0000265Asv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (Dimension 8400) + +pci:v00008086d0000265Asv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (Optiplex GX280) + +pci:v00008086d0000265Asv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (Compaq nw8240/nx8220) + +pci:v00008086d0000265Asv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (NX6110/NC6120) + +pci:v00008086d0000265Asv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (P5GD1-VW Mainboard) + +pci:v00008086d0000265Asv00001458sd0000265A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (GA-8I915ME-G Mainboard) + +pci:v00008086d0000265Asv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (915P/G Neo2) + +pci:v00008086d0000265Asv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (Scenic W620) + +pci:v00008086d0000265Asv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (CCD-CALYPSO) + +pci:v00008086d0000265Asv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (CD3-JIVE) + +pci:v00008086d0000265Asv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (XB1) + +pci:v00008086d0000265B* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 + +pci:v00008086d0000265Bsv00001014sd00000565* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (ThinkPad X41) + +pci:v00008086d0000265Bsv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (Dimension 8400) + +pci:v00008086d0000265Bsv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (Optiplex GX280) + +pci:v00008086d0000265Bsv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (NX6110/NC6120) + +pci:v00008086d0000265Bsv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (P5GD1-VW Mainboard) + +pci:v00008086d0000265Bsv00001458sd0000265A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (GA-8I915ME-G Mainboard) + +pci:v00008086d0000265Bsv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (915P/G Neo2) + +pci:v00008086d0000265Bsv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (Scenic W620) + +pci:v00008086d0000265Bsv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (CCD-CALYPSO) + +pci:v00008086d0000265Bsv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (CD3-JIVE) + +pci:v00008086d0000265Bsv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (XB1) + +pci:v00008086d0000265C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller + +pci:v00008086d0000265Csv00001014sd00000566* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (ThinkPad X41) + +pci:v00008086d0000265Csv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (Dimension 8400) + +pci:v00008086d0000265Csv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (Optiplex GX280) + +pci:v00008086d0000265Csv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (Compaq nw8240/nx8220) + +pci:v00008086d0000265Csv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (NX6110/NC6120) + +pci:v00008086d0000265Csv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (P5GD1-VW Mainboard) + +pci:v00008086d0000265Csv00001458sd00005006* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (GA-8I915ME-G Mainboard) + +pci:v00008086d0000265Csv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (915P/G Neo2) + +pci:v00008086d0000265Csv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (Scenic W620) + +pci:v00008086d0000265Csv00008086sd0000265C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (Dimension 3100) + +pci:v00008086d0000265Csv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (CCD-CALYPSO) + +pci:v00008086d0000265Csv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (CD3-JIVE) + +pci:v00008086d0000265Csv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (XB1) + +pci:v00008086d00002660* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 + +pci:v00008086d00002660sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (Compaq nw8240 Mobile Workstation) + +pci:v00008086d00002660sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (NX6110/NC6120) + +pci:v00008086d00002660sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (CCD-CALYPSO) + +pci:v00008086d00002660sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (CD3-JIVE) + +pci:v00008086d00002660sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (XB1) + +pci:v00008086d00002662* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 2 + +pci:v00008086d00002662sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 2 (Compaq nw8240 Mobile Workstation) + +pci:v00008086d00002662sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 2 (CCD-CALYPSO) + +pci:v00008086d00002662sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 2 (CD3-JIVE) + +pci:v00008086d00002662sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 2 (XB1) + +pci:v00008086d00002664* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 3 + +pci:v00008086d00002664sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 3 (CCD-CALYPSO) + +pci:v00008086d00002664sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 3 (CD3-JIVE) + +pci:v00008086d00002664sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 3 (XB1) + +pci:v00008086d00002666* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 4 + +pci:v00008086d00002666sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 4 (CCD-CALYPSO) + +pci:v00008086d00002666sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 4 (CD3-JIVE) + +pci:v00008086d00002666sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 4 (XB1) + +pci:v00008086d00002668* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller + +pci:v00008086d00002668sv00001014sd000005B7* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (ThinkPad Z60t) + +pci:v00008086d00002668sv0000103Csd00002A09* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (PufferM-UL8E) + +pci:v00008086d00002668sv00001043sd00001173* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (Asus A6VC) + +pci:v00008086d00002668sv00001043sd0000814E* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (P5GD1-VW Mainboard) + +pci:v00008086d00002668sv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (915P/G Neo2) + +pci:v00008086d00002668sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (QEMU Virtual Machine) + +pci:v00008086d0000266A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller + +pci:v00008086d0000266Asv00001014sd0000056B* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (ThinkPad X41) + +pci:v00008086d0000266Asv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (Dimension 8400) + +pci:v00008086d0000266Asv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (Optiplex GX280) + +pci:v00008086d0000266Asv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (P5GD1-VW Mainboard) + +pci:v00008086d0000266Asv00001458sd0000266A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (GA-8I915ME-G Mainboard) + +pci:v00008086d0000266Asv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (915P/G Neo2) + +pci:v00008086d0000266Asv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (Scenic W620) + +pci:v00008086d0000266Asv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (CCD-CALYPSO) + +pci:v00008086d0000266Asv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (CD3-JIVE) + +pci:v00008086d0000266Asv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (XB1) + +pci:v00008086d0000266C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) LAN Controller + +pci:v00008086d0000266D* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Modem Controller + +pci:v00008086d0000266Dsv00001025sd0000006A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Modem Controller (Conexant AC'97 CoDec (in Acer TravelMate 2410 serie laptop)) + +pci:v00008086d0000266Dsv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Modem Controller (Compaq nw8240/nx8220) + +pci:v00008086d0000266Dsv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Modem Controller (NX6110/NC6120) + +pci:v00008086d0000266E* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller + +pci:v00008086d0000266Esv00001014sd00000581* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (ThinkPad X41 (Analog Devices AD1981B codec)) + +pci:v00008086d0000266Esv00001025sd0000006A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Realtek ALC 655 codec (in Acer TravelMate 2410 serie laptop)) + +pci:v00008086d0000266Esv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Dimension 8400) + +pci:v00008086d0000266Esv00001028sd00000179* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Optiplex GX280) + +pci:v00008086d0000266Esv00001028sd00000182* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Latitude D610 Laptop) + +pci:v00008086d0000266Esv00001028sd00000187* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Dell Precision M70 Laptop) + +pci:v00008086d0000266Esv00001028sd00000188* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Inspiron 6000 laptop) + +pci:v00008086d0000266Esv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Compaq nw8240/nx8220) + +pci:v00008086d0000266Esv0000103Csd00000944* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Compaq NC6220) + +pci:v00008086d0000266Esv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (NX6110/NC6120) + +pci:v00008086d0000266Esv0000103Csd00003006* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (DC7100 SFF(DX878AV)) + +pci:v00008086d0000266Esv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (GA-8I915ME-G Mainboard) + +pci:v00008086d0000266Esv0000152Dsd00000745* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Packard Bell A8550 Laptop) + +pci:v00008086d0000266Esv00001734sd0000105A* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) AC'97 Audio Controller (Scenic W620) + +pci:v00008086d0000266F* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller + +pci:v00008086d0000266Fsv00001028sd00000177* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (Dimension 8400) + +pci:v00008086d0000266Fsv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (Compaq nw8240/nx8220) + +pci:v00008086d0000266Fsv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (NX6110/NC6120) + +pci:v00008086d0000266Fsv00001043sd000080A6* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (P5GD1-VW Mainboard) + +pci:v00008086d0000266Fsv00001458sd0000266F* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (GA-8I915ME-G Mainboard) + +pci:v00008086d0000266Fsv00001462sd00007028* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (915P/G Neo2) + +pci:v00008086d0000266Fsv00001734sd0000105C* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (Scenic W620) + +pci:v00008086d0000266Fsv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (CCD-CALYPSO) + +pci:v00008086d0000266Fsv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (CD3-JIVE) + +pci:v00008086d0000266Fsv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=82801FB/FBM/FR/FW/FRW (ICH6 Family) IDE Controller (XB1) + +pci:v00008086d00002670* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset LPC Interface Controller + +pci:v00008086d00002670sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset LPC Interface Controller (ProLiant DL140 G3) + +pci:v00008086d00002670sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset LPC Interface Controller (X7DVL-E-O motherboard) + +pci:v00008086d00002670sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset LPC Interface Controller (X7DBN Motherboard) + +pci:v00008086d00002670sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset LPC Interface Controller (Intel S5000PSLSATA Server Board) + +pci:v00008086d00002680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SATA IDE Controller + +pci:v00008086d00002681* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA AHCI Controller + +pci:v00008086d00002681sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA AHCI Controller (ProLiant DL140 G3) + +pci:v00008086d00002681sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA AHCI Controller (X7DVL-E-O motherboard) + +pci:v00008086d00002681sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA AHCI Controller (X7DBN Motherboard) + +pci:v00008086d00002681sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA AHCI Controller (Intel S5000PSLSATA Server Board) + +pci:v00008086d00002682* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA RAID Controller + +pci:v00008086d00002682sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA RAID Controller (Adaptec Serial ATA HostRAID) + +pci:v00008086d00002683* + ID_MODEL_FROM_DATABASE=631xESB/632xESB SATA RAID Controller + +pci:v00008086d00002688* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 + +pci:v00008086d00002688sv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (PowerEdge 1955 onboard USB) + +pci:v00008086d00002688sv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (PowerEdge R900 onboard USB) + +pci:v00008086d00002688sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (ProLiant DL140 G3) + +pci:v00008086d00002688sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (X7DVL-E-O motherboard) + +pci:v00008086d00002688sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (X7DBN Motherboard) + +pci:v00008086d00002688sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #1 (Intel S5000PSLSATA Server Board) + +pci:v00008086d00002689* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 + +pci:v00008086d00002689sv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (PowerEdge 1955 onboard USB) + +pci:v00008086d00002689sv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (PowerEdge R900 onboard USB) + +pci:v00008086d00002689sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (ProLiant DL140 G3) + +pci:v00008086d00002689sv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (X7DVL-E-O motherboard) + +pci:v00008086d00002689sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (X7DBN Motherboard) + +pci:v00008086d00002689sv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #2 (Intel S5000PSLSATA Server Board) + +pci:v00008086d0000268A* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 + +pci:v00008086d0000268Asv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 (PowerEdge R900 onboard USB) + +pci:v00008086d0000268Asv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 (ProLiant DL140 G3) + +pci:v00008086d0000268Asv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 (X7DVL-E-O motherboard) + +pci:v00008086d0000268Asv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 (X7DBN Motherboard) + +pci:v00008086d0000268Asv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #3 (Intel S5000PSLSATA Server Board) + +pci:v00008086d0000268B* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #4 + +pci:v00008086d0000268Bsv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #4 (PowerEdge R900 onboard USB) + +pci:v00008086d0000268Bsv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #4 (X7DVL-E-O motherboard) + +pci:v00008086d0000268Bsv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset UHCI USB Controller #4 (Intel S5000PSLSATA Server Board) + +pci:v00008086d0000268C* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller + +pci:v00008086d0000268Csv00001028sd000001BB* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (PowerEdge 1955 onboard USB) + +pci:v00008086d0000268Csv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (PowerEdge R900 onboard USB) + +pci:v00008086d0000268Csv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (ProLiant DL140 G3) + +pci:v00008086d0000268Csv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (X7DVL-E-O motherboard) + +pci:v00008086d0000268Csv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (X7DBN Motherboard) + +pci:v00008086d0000268Csv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset EHCI USB2 Controller (Intel S5000PSLSATA Server Board) + +pci:v00008086d00002690* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 1 + +pci:v00008086d00002690sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 1 (ProLiant DL140 G3) + +pci:v00008086d00002690sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 1 (X7DBN Motherboard) + +pci:v00008086d00002692* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 2 + +pci:v00008086d00002692sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 2 (ProLiant DL140 G3) + +pci:v00008086d00002694* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 3 + +pci:v00008086d00002696* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset PCI Express Root Port 4 + +pci:v00008086d00002698* + ID_MODEL_FROM_DATABASE=631xESB/632xESB AC '97 Audio Controller + +pci:v00008086d00002699* + ID_MODEL_FROM_DATABASE=631xESB/632xESB AC '97 Modem Controller + +pci:v00008086d0000269A* + ID_MODEL_FROM_DATABASE=631xESB/632xESB High Definition Audio Controller + +pci:v00008086d0000269B* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SMBus Controller + +pci:v00008086d0000269Bsv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SMBus Controller (ProLiant DL140 G3) + +pci:v00008086d0000269Bsv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SMBus Controller (X7DVL-E-O motherboard) + +pci:v00008086d0000269Bsv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SMBus Controller (X7DBN Motherboard) + +pci:v00008086d0000269Bsv00008086sd00003476* + ID_MODEL_FROM_DATABASE=631xESB/632xESB/3100 Chipset SMBus Controller (Intel S5000PSLSATA Server Board) + +pci:v00008086d0000269E* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IDE Controller + +pci:v00008086d0000269Esv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IDE Controller (ProLiant DL140 G3) + +pci:v00008086d0000269Esv000015D9sd00008680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IDE Controller (X7DVL-E-O motherboard) + +pci:v00008086d0000269Esv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=631xESB/632xESB IDE Controller (X7DBN Motherboard) + +pci:v00008086d00002770* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub + +pci:v00008086d00002770sv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (OptiPlex GX620) + +pci:v00008086d00002770sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (Pavilion A1512X) + +pci:v00008086d00002770sv00001043sd0000817A* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (P5LD2-VM Mainboard) + +pci:v00008086d00002770sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (E4500) + +pci:v00008086d00002770sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (Wind PC MS-7418) + +pci:v00008086d00002770sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL Memory Controller Hub (DeskTop Board D945GTP) + +pci:v00008086d00002771* + ID_MODEL_FROM_DATABASE=82945G/GZ/P/PL PCI Express Root Port + +pci:v00008086d00002772* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller + +pci:v00008086d00002772sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (Pavilion A1512X) + +pci:v00008086d00002772sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (Wind PC MS-7418) + +pci:v00008086d00002772sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (DeskTop Board D945GTP) + +pci:v00008086d00002772sv00008086sd0000D605* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller (Intel Desktop Board D945GCCR) + +pci:v00008086d00002774* + ID_MODEL_FROM_DATABASE=82955X Memory Controller Hub + +pci:v00008086d00002775* + ID_MODEL_FROM_DATABASE=82955X PCI Express Root Port + +pci:v00008086d00002776* + ID_MODEL_FROM_DATABASE=82945G/GZ Integrated Graphics Controller + +pci:v00008086d00002778* + ID_MODEL_FROM_DATABASE=E7230/3000/3010 Memory Controller Hub + +pci:v00008086d00002778sv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=E7230/3000/3010 Memory Controller Hub (PowerEdge SC440) + +pci:v00008086d00002778sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=E7230/3000/3010 Memory Controller Hub (PowerEdge 860) + +pci:v00008086d00002779* + ID_MODEL_FROM_DATABASE=E7230/3000/3010 PCI Express Root Port + +pci:v00008086d0000277A* + ID_MODEL_FROM_DATABASE=82975X/3010 PCI Express Root Port + +pci:v00008086d0000277C* + ID_MODEL_FROM_DATABASE=82975X Memory Controller Hub + +pci:v00008086d0000277Csv00001043sd00008178* + ID_MODEL_FROM_DATABASE=82975X Memory Controller Hub (P5WDG2 WS Professional motherboard) + +pci:v00008086d0000277D* + ID_MODEL_FROM_DATABASE=82975X PCI Express Root Port + +pci:v00008086d00002782* + ID_MODEL_FROM_DATABASE=82915G Integrated Graphics Controller + +pci:v00008086d00002782sv00001043sd00002582* + ID_MODEL_FROM_DATABASE=82915G Integrated Graphics Controller (P5GD1-VW Mainboard) + +pci:v00008086d00002782sv00001734sd0000105B* + ID_MODEL_FROM_DATABASE=82915G Integrated Graphics Controller (Scenic W620) + +pci:v00008086d00002792* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller + +pci:v00008086d00002792sv00001014sd00000582* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (ThinkPad X41) + +pci:v00008086d00002792sv0000103Csd0000099C* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (NX6110/NC6120) + +pci:v00008086d00002792sv00001043sd00001881* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (GMA 900 915GM Integrated Graphics) + +pci:v00008086d00002792sv0000E4BFsd00000CCD* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (CCD-CALYPSO) + +pci:v00008086d00002792sv0000E4BFsd00000CD3* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (CD3-JIVE) + +pci:v00008086d00002792sv0000E4BFsd000058B1* + ID_MODEL_FROM_DATABASE=Mobile 915GM/GMS/910GML Express Graphics Controller (XB1) + +pci:v00008086d000027A0* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub + +pci:v00008086d000027A0sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (9814 WKMI) + +pci:v00008086d000027A0sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (XPS M1210) + +pci:v00008086d000027A0sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (Compaq nx9420 Notebook) + +pci:v00008086d000027A0sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (NC2400) + +pci:v00008086d000027A0sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (Compaq nw8440) + +pci:v00008086d000027A0sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (A6J-Q008) + +pci:v00008086d000027A0sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027A0sv000017AAsd00002015* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (ThinkPad T60) + +pci:v00008086d000027A0sv000017AAsd00002017* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express Memory Controller Hub (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027A1* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express PCI Express Root Port + +pci:v00008086d000027A1sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express PCI Express Root Port (Compaq nx9420 Notebook) + +pci:v00008086d000027A1sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express PCI Express Root Port (Compaq nw8440) + +pci:v00008086d000027A1sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=Mobile 945GM/PM/GMS, 943/940GML and 945GT Express PCI Express Root Port (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027A2* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller + +pci:v00008086d000027A2sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller (NC2400) + +pci:v00008086d000027A2sv000017AAsd0000201A* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027A2sv00009902sd00001584* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller (CCE MPL-D10H120F) + +pci:v00008086d000027A6* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS/GME, 943/940GML Express Integrated Graphics Controller + +pci:v00008086d000027A6sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS/GME, 943/940GML Express Integrated Graphics Controller (NC2400) + +pci:v00008086d000027A6sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS/GME, 943/940GML Express Integrated Graphics Controller (CC11/CL11 integrated graphics (secondary)) + +pci:v00008086d000027A6sv000017AAsd0000201A* + ID_MODEL_FROM_DATABASE=Mobile 945GM/GMS/GME, 943/940GML Express Integrated Graphics Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027AC* + ID_MODEL_FROM_DATABASE=Mobile 945GSE Express Memory Controller Hub + +pci:v00008086d000027ACsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=Mobile 945GSE Express Memory Controller Hub (CC11/CL11) + +pci:v00008086d000027AD* + ID_MODEL_FROM_DATABASE=Mobile 945GSE Express PCI Express Root Port + +pci:v00008086d000027AE* + ID_MODEL_FROM_DATABASE=Mobile 945GSE Express Integrated Graphics Controller + +pci:v00008086d000027AEsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=Mobile 945GSE Express Integrated Graphics Controller (CC11/CL11 integrated graphics (primary)) + +pci:v00008086d000027B0* + ID_MODEL_FROM_DATABASE=82801GH (ICH7DH) LPC Interface Bridge + +pci:v00008086d000027B0sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=82801GH (ICH7DH) LPC Interface Bridge (Pavilion A1512X) + +pci:v00008086d000027B0sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82801GH (ICH7DH) LPC Interface Bridge (DeskTop Board D945GTP) + +pci:v00008086d000027B8* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge + +pci:v00008086d000027B8sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (PowerEdge 860) + +pci:v00008086d000027B8sv00001043sd00008179* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (P5KPL-VM Motherboard) + +pci:v00008086d000027B8sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (E4500) + +pci:v00008086d000027B8sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (Wind PC MS-7418) + +pci:v00008086d000027B8sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (CC11/CL11) + +pci:v00008086d000027B8sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (DeskTop Board D945GTP) + +pci:v00008086d000027B9* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge + +pci:v00008086d000027B9sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (XPS M1210) + +pci:v00008086d000027B9sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (Compaq nx9420 Notebook) + +pci:v00008086d000027B9sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (NC2400) + +pci:v00008086d000027B9sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (Compaq nw8440) + +pci:v00008086d000027B9sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027B9sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (Panasonic CF-Y5 laptop) + +pci:v00008086d000027B9sv000017AAsd00002009* + ID_MODEL_FROM_DATABASE=82801GBM (ICH7-M) LPC Interface Bridge (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027BC* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller + +pci:v00008086d000027BCsv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (D270S/D250S Motherboard) + +pci:v00008086d000027BCsv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (Notebook N150P) + +pci:v00008086d000027BCsv00001458sd00005001* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (GA-D525TUD) + +pci:v00008086d000027BCsv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (DeskTop Board D510MO) + +pci:v00008086d000027BCsv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10 Family LPC Controller (Desktop Board D425KT) + +pci:v00008086d000027BD* + ID_MODEL_FROM_DATABASE=82801GHM (ICH7-M DH) LPC Interface Bridge + +pci:v00008086d000027BDsv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=82801GHM (ICH7-M DH) LPC Interface Bridge (9814 WKMI) + +pci:v00008086d000027C0* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] + +pci:v00008086d000027C0sv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (OptiPlex GX620) + +pci:v00008086d000027C0sv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (PowerEdge SC440) + +pci:v00008086d000027C0sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (PowerEdge 860) + +pci:v00008086d000027C0sv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (P5KPL-VM Motherboard) + +pci:v00008086d000027C0sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (E4500) + +pci:v00008086d000027C0sv00001462sd00002310* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (MSI Hetis 945) + +pci:v00008086d000027C0sv00001462sd00007236* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (945P Neo3-F Rev. 2.2 motherboard) + +pci:v00008086d000027C0sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (Wind PC MS-7418) + +pci:v00008086d000027C0sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (CC11/CL11) + +pci:v00008086d000027C0sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (Desktop Board D425KT) + +pci:v00008086d000027C0sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (DeskTop Board D945GTP) + +pci:v00008086d000027C1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] + +pci:v00008086d000027C1sv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (PowerEdge SC440) + +pci:v00008086d000027C1sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (Pavilion A1512X) + +pci:v00008086d000027C1sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (D270S/D250S Motherboard) + +pci:v00008086d000027C1sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (Notebook N150P) + +pci:v00008086d000027C1sv00001458sd0000B005* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (GA-D525TUD) + +pci:v00008086d000027C1sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (CC11/CL11) + +pci:v00008086d000027C1sv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (DeskTop Board D510MO) + +pci:v00008086d000027C1sv00008086sd00005842* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [AHCI mode] (DeskTop Board D975XBX) + +pci:v00008086d000027C3* + ID_MODEL_FROM_DATABASE=82801GR/GDH (ICH7R/ICH7DH) SATA Controller [RAID mode] + +pci:v00008086d000027C3sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801GR/GDH (ICH7R/ICH7DH) SATA Controller [RAID mode] (CC11/CL11) + +pci:v00008086d000027C3sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82801GR/GDH (ICH7R/ICH7DH) SATA Controller [RAID mode] (DeskTop Board D945GTP) + +pci:v00008086d000027C4* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [IDE mode] + +pci:v00008086d000027C4sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [IDE mode] (9814 WKMI) + +pci:v00008086d000027C4sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [IDE mode] (XPS M1210) + +pci:v00008086d000027C4sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [IDE mode] (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027C4sv000017AAsd0000200E* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [IDE mode] (ThinkPad T60) + +pci:v00008086d000027C5* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [AHCI mode] + +pci:v00008086d000027C5sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [AHCI mode] (Compaq nx9420 Notebook) + +pci:v00008086d000027C5sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [AHCI mode] (Compaq nw8440) + +pci:v00008086d000027C5sv000017AAsd0000200D* + ID_MODEL_FROM_DATABASE=82801GBM/GHM (ICH7-M Family) SATA Controller [AHCI mode] (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027C6* + ID_MODEL_FROM_DATABASE=82801GHM (ICH7-M DH) SATA Controller [RAID mode] + +pci:v00008086d000027C8* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 + +pci:v00008086d000027C8sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (9814 WKMI) + +pci:v00008086d000027C8sv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (OptiPlex GX620) + +pci:v00008086d000027C8sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (XPS M1210) + +pci:v00008086d000027C8sv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (PowerEdge SC440) + +pci:v00008086d000027C8sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (PowerEdge 860) + +pci:v00008086d000027C8sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Pavilion A1512X) + +pci:v00008086d000027C8sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Compaq nx9420 Notebook) + +pci:v00008086d000027C8sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (NC2400) + +pci:v00008086d000027C8sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Compaq nw8440) + +pci:v00008086d000027C8sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (A6J-Q008) + +pci:v00008086d000027C8sv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5KPL-VM,P5LD2-VM Mainboard) + +pci:v00008086d000027C8sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (D270S/D250S Motherboard) + +pci:v00008086d000027C8sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027C8sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (E4500) + +pci:v00008086d000027C8sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Notebook N150P) + +pci:v00008086d000027C8sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (GA-D525TUD) + +pci:v00008086d000027C8sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Wind PC MS-7418) + +pci:v00008086d000027C8sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (CC11/CL11) + +pci:v00008086d000027C8sv000017AAsd0000200A* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027C8sv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (DeskTop Board D510MO) + +pci:v00008086d000027C8sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Desktop Board D425KT) + +pci:v00008086d000027C8sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (DeskTop Board D945GTP) + +pci:v00008086d000027C9* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 + +pci:v00008086d000027C9sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (9814 WKMI) + +pci:v00008086d000027C9sv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (OptiPlex GX620) + +pci:v00008086d000027C9sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (XPS M1210) + +pci:v00008086d000027C9sv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (PowerEdge SC440) + +pci:v00008086d000027C9sv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (PowerEdge 860) + +pci:v00008086d000027C9sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Pavilion A1512X) + +pci:v00008086d000027C9sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Compaq nx9420 Notebook) + +pci:v00008086d000027C9sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (NC2400) + +pci:v00008086d000027C9sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Compaq nw8440) + +pci:v00008086d000027C9sv00001043sd00001237* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (A6J-Q008) + +pci:v00008086d000027C9sv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5KPL-VM,P5LD2-VM Mainboard) + +pci:v00008086d000027C9sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (D270S/D250S Motherboard) + +pci:v00008086d000027C9sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027C9sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (E4500) + +pci:v00008086d000027C9sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Notebook N150P) + +pci:v00008086d000027C9sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (GA-D525TUD) + +pci:v00008086d000027C9sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Wind PC MS-7418) + +pci:v00008086d000027C9sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (CC11/CL11) + +pci:v00008086d000027C9sv000017AAsd0000200A* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027C9sv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (DeskTop Board D510MO) + +pci:v00008086d000027C9sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Desktop Board D425KT) + +pci:v00008086d000027C9sv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (DeskTop Board D945GTP) + +pci:v00008086d000027CA* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 + +pci:v00008086d000027CAsv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (9814 WKMI) + +pci:v00008086d000027CAsv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (OptiPlex GX620) + +pci:v00008086d000027CAsv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (XPS M1210) + +pci:v00008086d000027CAsv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (PowerEdge SC440) + +pci:v00008086d000027CAsv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (PowerEdge 860) + +pci:v00008086d000027CAsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Pavilion A1512X) + +pci:v00008086d000027CAsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Compaq nx9420 Notebook) + +pci:v00008086d000027CAsv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (NC2400) + +pci:v00008086d000027CAsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Compaq nw8440) + +pci:v00008086d000027CAsv00001043sd00001237* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (A6J-Q008) + +pci:v00008086d000027CAsv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5KPL-VM,P5LD2-VM Mainboard) + +pci:v00008086d000027CAsv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (D270S/D250S Motherboard) + +pci:v00008086d000027CAsv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027CAsv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (E4500) + +pci:v00008086d000027CAsv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Notebook N150P) + +pci:v00008086d000027CAsv00001458sd00005004* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (GA-D525TUD) + +pci:v00008086d000027CAsv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Wind PC MS-7418) + +pci:v00008086d000027CAsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (CC11/CL11) + +pci:v00008086d000027CAsv000017AAsd0000200A* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027CAsv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (DeskTop Board D510MO) + +pci:v00008086d000027CAsv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (DeskTop Board D945GTP) + +pci:v00008086d000027CB* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 + +pci:v00008086d000027CBsv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (9814 WKMI) + +pci:v00008086d000027CBsv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (OptiPlex GX620) + +pci:v00008086d000027CBsv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (XPS M1210) + +pci:v00008086d000027CBsv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (PowerEdge SC440) + +pci:v00008086d000027CBsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Pavilion A1512X) + +pci:v00008086d000027CBsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Compaq nx9420 Notebook) + +pci:v00008086d000027CBsv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (NC2400) + +pci:v00008086d000027CBsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Compaq nw8440) + +pci:v00008086d000027CBsv00001043sd00001237* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (A6J-Q008) + +pci:v00008086d000027CBsv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5KPL-VM,P5LD2-VM Mainboard) + +pci:v00008086d000027CBsv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (D270S/D250S Motherboard) + +pci:v00008086d000027CBsv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027CBsv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (E4500) + +pci:v00008086d000027CBsv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Notebook N150P) + +pci:v00008086d000027CBsv00001458sd00005004* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (GA-D525TUD) + +pci:v00008086d000027CBsv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Wind PC MS-7418) + +pci:v00008086d000027CBsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (CC11/CL11) + +pci:v00008086d000027CBsv000017AAsd0000200A* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027CBsv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (DeskTop Board D510MO) + +pci:v00008086d000027CBsv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (DeskTop Board D945GTP) + +pci:v00008086d000027CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller + +pci:v00008086d000027CCsv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (9814 WKMI) + +pci:v00008086d000027CCsv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (OptiPlex GX620) + +pci:v00008086d000027CCsv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (XPS M1210) + +pci:v00008086d000027CCsv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (PowerEdge SC440) + +pci:v00008086d000027CCsv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (PowerEdge 860) + +pci:v00008086d000027CCsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Pavilion A1512X) + +pci:v00008086d000027CCsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Compaq nx9420 Notebook) + +pci:v00008086d000027CCsv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (NC2400) + +pci:v00008086d000027CCsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Compaq nw8440) + +pci:v00008086d000027CCsv00001043sd00001237* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (A6J-Q008) + +pci:v00008086d000027CCsv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5KPL-VM,P5LD2-VM Mainboard) + +pci:v00008086d000027CCsv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (D270S/D250S Motherboard) + +pci:v00008086d000027CCsv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027CCsv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Notebook N150P) + +pci:v00008086d000027CCsv00001458sd00005006* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (GA-D525TUD) + +pci:v00008086d000027CCsv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Wind PC MS-7418) + +pci:v00008086d000027CCsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (CC11/CL11) + +pci:v00008086d000027CCsv000017AAsd0000200B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027CCsv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (DeskTop Board D510MO) + +pci:v00008086d000027CCsv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Desktop Board D425KT) + +pci:v00008086d000027CCsv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (DeskTop Board D945GTP) + +pci:v00008086d000027D0* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 + +pci:v00008086d000027D0sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Compaq nx9420 Notebook) + +pci:v00008086d000027D0sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Compaq nw8440) + +pci:v00008086d000027D0sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027D0sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Notebook N150P) + +pci:v00008086d000027D0sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (GA-D525TUD) + +pci:v00008086d000027D0sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Wind PC MS-7418) + +pci:v00008086d000027D0sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (CC11/CL11) + +pci:v00008086d000027D0sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 1 (Desktop Board D425KT) + +pci:v00008086d000027D2* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 + +pci:v00008086d000027D2sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Compaq nx9420 Notebook) + +pci:v00008086d000027D2sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Compaq nw8440) + +pci:v00008086d000027D2sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027D2sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Notebook N150P) + +pci:v00008086d000027D2sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Wind PC MS-7418) + +pci:v00008086d000027D2sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (CC11/CL11) + +pci:v00008086d000027D2sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 2 (Desktop Board D425KT) + +pci:v00008086d000027D4* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 + +pci:v00008086d000027D4sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027D4sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 (Notebook N150P) + +pci:v00008086d000027D4sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 (Wind PC MS-7418) + +pci:v00008086d000027D4sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 (CC11/CL11) + +pci:v00008086d000027D4sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 3 (Desktop Board D425KT) + +pci:v00008086d000027D6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 + +pci:v00008086d000027D6sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (Compaq nw8440) + +pci:v00008086d000027D6sv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027D6sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (Notebook N150P) + +pci:v00008086d000027D6sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (Wind PC MS-7418) + +pci:v00008086d000027D6sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (CC11/CL11) + +pci:v00008086d000027D6sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family PCI Express Port 4 (Desktop Board D425KT) + +pci:v00008086d000027D8* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller + +pci:v00008086d000027D8sv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (9814 WKMI) + +pci:v00008086d000027D8sv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (XPS M1210) + +pci:v00008086d000027D8sv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Pavilion A1512X) + +pci:v00008086d000027D8sv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Compaq nx9420 Notebook) + +pci:v00008086d000027D8sv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (NC2400) + +pci:v00008086d000027D8sv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Compaq nw8440) + +pci:v00008086d000027D8sv00001043sd00001123* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (A6J-Q008) + +pci:v00008086d000027D8sv00001043sd000013C4* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Asus G2P) + +pci:v00008086d000027D8sv00001043sd0000817F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5LD2-VM Mainboard (Realtek ALC 882 codec)) + +pci:v00008086d000027D8sv00001043sd00008290* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5KPL-VM Motherboard) + +pci:v00008086d000027D8sv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5KPL-CM Motherboard) + +pci:v00008086d000027D8sv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (D270S/D250S Motherboard) + +pci:v00008086d000027D8sv00001071sd00008207* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027D8sv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (E4500) + +pci:v00008086d000027D8sv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Panasonic CF-Y5 laptop) + +pci:v00008086d000027D8sv00001179sd0000FF10* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Toshiba Satellite A100-796 audio (Realtek ALC861)) + +pci:v00008086d000027D8sv00001179sd0000FF31* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (AC97 Data Fax SoftModem with SmartCP) + +pci:v00008086d000027D8sv00001447sd00001043* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Asus A8JP (Analog Devices AD1986A)) + +pci:v00008086d000027D8sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Notebook N150P) + +pci:v00008086d000027D8sv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (GA-D525TUD (Realtek ALC887)) + +pci:v00008086d000027D8sv00001458sd0000A102* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (GA-8I945PG-RH Mainboard) + +pci:v00008086d000027D8sv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Wind PC MS-7418) + +pci:v00008086d000027D8sv0000152Dsd00000753* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Softmodem) + +pci:v00008086d000027D8sv00001734sd000010AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Conexant softmodem SmartCP) + +pci:v00008086d000027D8sv000017AAsd00002010* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027D8sv000017AAsd00003802* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (Lenovo 3000 C200 audio [Realtek ALC861VD]) + +pci:v00008086d000027D8sv00008086sd00001112* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (DeskTop Board D945GTP) + +pci:v00008086d000027D8sv00008086sd000027D8* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (DeskTop Board D945GTP) + +pci:v00008086d000027D8sv00008086sd0000D618* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (DeskTop Board D510MO) + +pci:v00008086d000027D8sv00008384sd00007680* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (STAC9221 HD Audio Codec) + +pci:v00008086d000027DA* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller + +pci:v00008086d000027DAsv00001025sd0000006C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (9814 WKMI) + +pci:v00008086d000027DAsv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (OptiPlex GX620) + +pci:v00008086d000027DAsv00001028sd000001D7* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (XPS M1210) + +pci:v00008086d000027DAsv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (PowerEdge SC440) + +pci:v00008086d000027DAsv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (PowerEdge 860) + +pci:v00008086d000027DAsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Pavilion A1512X) + +pci:v00008086d000027DAsv00001043sd00008179* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (P5KPL-VM Motherboard) + +pci:v00008086d000027DAsv0000105Bsd00000D7C* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (D270S/D250S Motherboard) + +pci:v00008086d000027DAsv00001071sd00008209* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Medion MIM 2240 Notebook PC [MD98100]) + +pci:v00008086d000027DAsv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Panasonic CF-Y5 laptop) + +pci:v00008086d000027DAsv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Notebook N150P) + +pci:v00008086d000027DAsv00001458sd00005001* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (GA-8I945PG-RH/GA-D525TUD Mainboard) + +pci:v00008086d000027DAsv00001462sd00007418* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Wind PC MS-7418) + +pci:v00008086d000027DAsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (CC11/CL11) + +pci:v00008086d000027DAsv000017AAsd0000200F* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027DAsv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (DeskTop Board D510MO) + +pci:v00008086d000027DAsv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Desktop Board D425KT) + +pci:v00008086d000027DAsv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (DeskTop Board D945GTP) + +pci:v00008086d000027DAsv00008086sd00005842* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (DeskTop Board D975XBX) + +pci:v00008086d000027DC* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family LAN Controller + +pci:v00008086d000027DCsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family LAN Controller (Pavilion A1512X) + +pci:v00008086d000027DCsv00008086sd0000308D* + ID_MODEL_FROM_DATABASE=NM10/ICH7 Family LAN Controller (DeskTop Board D945GTP) + +pci:v00008086d000027DD* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) AC'97 Modem Controller + +pci:v00008086d000027DE* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) AC'97 Audio Controller + +pci:v00008086d000027DEsv00001028sd000001AD* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) AC'97 Audio Controller (OptiPlex GX620) + +pci:v00008086d000027DEsv00001462sd00007267* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) AC'97 Audio Controller (Realtek ALC883 Audio Controller) + +pci:v00008086d000027DEsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) AC'97 Audio Controller (CC11 integrated audio (AD1981BL codec)) + +pci:v00008086d000027DF* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller + +pci:v00008086d000027DFsv00001028sd000001DF* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (PowerEdge SC440) + +pci:v00008086d000027DFsv00001028sd000001E6* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (PowerEdge 860) + +pci:v00008086d000027DFsv0000103Csd00002A3B* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (Pavilion A1512X) + +pci:v00008086d000027DFsv0000103Csd0000309F* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (Compaq nx9420 Notebook) + +pci:v00008086d000027DFsv0000103Csd000030A1* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (NC2400) + +pci:v00008086d000027DFsv0000103Csd000030A3* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (Compaq nw8440) + +pci:v00008086d000027DFsv00001043sd00001237* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (A6J-Q008) + +pci:v00008086d000027DFsv00001043sd00008179* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (P5KPL-VM Motherboard) + +pci:v00008086d000027DFsv0000107Bsd00005048* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (E4500) + +pci:v00008086d000027DFsv000010F7sd00008338* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (Panasonic CF-Y5 laptop) + +pci:v00008086d000027DFsv00001462sd00007418* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (Wind PC MS-7418) + +pci:v00008086d000027DFsv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (CC11/CL11) + +pci:v00008086d000027DFsv000017AAsd0000200C* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (ThinkPad R60/T60/X60 series) + +pci:v00008086d000027DFsv00008086sd0000544E* + ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (DeskTop Board D945GTP) + +pci:v00008086d000027E0* + ID_MODEL_FROM_DATABASE=82801GR/GH/GHM (ICH7 Family) PCI Express Port 5 + +pci:v00008086d000027E0sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801GR/GH/GHM (ICH7 Family) PCI Express Port 5 (CC11/CL11) + +pci:v00008086d000027E2* + ID_MODEL_FROM_DATABASE=82801GR/GH/GHM (ICH7 Family) PCI Express Port 6 + +pci:v00008086d000027E2sv00001775sd000011CC* + ID_MODEL_FROM_DATABASE=82801GR/GH/GHM (ICH7 Family) PCI Express Port 6 (CC11/CL11) + +pci:v00008086d00002810* + ID_MODEL_FROM_DATABASE=82801HB/HR (ICH8/R) LPC Interface Controller + +pci:v00008086d00002810sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801HB/HR (ICH8/R) LPC Interface Controller (P5B) + +pci:v00008086d00002811* + ID_MODEL_FROM_DATABASE=82801HEM (ICH8M-E) LPC Interface Controller + +pci:v00008086d00002811sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801HEM (ICH8M-E) LPC Interface Controller (Compaq 6910p) + +pci:v00008086d00002811sv000017AAsd000020B6* + ID_MODEL_FROM_DATABASE=82801HEM (ICH8M-E) LPC Interface Controller (ThinkPad T61/R61) + +pci:v00008086d00002811sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801HEM (ICH8M-E) LPC Interface Controller (CCG-RUMBA) + +pci:v00008086d00002812* + ID_MODEL_FROM_DATABASE=82801HH (ICH8DH) LPC Interface Controller + +pci:v00008086d00002814* + ID_MODEL_FROM_DATABASE=82801HO (ICH8DO) LPC Interface Controller + +pci:v00008086d00002815* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller + +pci:v00008086d00002815sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Aspire 5920G) + +pci:v00008086d00002815sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Inspiron 1420) + +pci:v00008086d00002815sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Compaq 6710b) + +pci:v00008086d00002815sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Pavilion dv6700) + +pci:v00008086d00002815sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Presario C700) + +pci:v00008086d00002815sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Vaio VGN-FZ260E) + +pci:v00008086d00002815sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (VAIO VGN-NR120E) + +pci:v00008086d00002815sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002820* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) 4 port SATA Controller [IDE mode] + +pci:v00008086d00002820sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) 4 port SATA Controller [IDE mode] (OptiPlex 745) + +pci:v00008086d00002820sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) 4 port SATA Controller [IDE mode] (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002821* + ID_MODEL_FROM_DATABASE=82801HR/HO/HH (ICH8R/DO/DH) 6 port SATA Controller [AHCI mode] + +pci:v00008086d00002822* + ID_MODEL_FROM_DATABASE=SATA Controller [RAID mode] + +pci:v00008086d00002822sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=SATA Controller [RAID mode] (Inspiron 530) + +pci:v00008086d00002822sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=SATA Controller [RAID mode] (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002822sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=SATA Controller [RAID mode] (P5K PRO Motherboard) + +pci:v00008086d00002823* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [RAID mode] + +pci:v00008086d00002824* + ID_MODEL_FROM_DATABASE=82801HB (ICH8) 4 port SATA Controller [AHCI mode] + +pci:v00008086d00002824sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801HB (ICH8) 4 port SATA Controller [AHCI mode] (P5B) + +pci:v00008086d00002825* + ID_MODEL_FROM_DATABASE=82801HR/HO/HH (ICH8R/DO/DH) 2 port SATA Controller [IDE mode] + +pci:v00008086d00002825sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801HR/HO/HH (ICH8R/DO/DH) 2 port SATA Controller [IDE mode] (OptiPlex 745) + +pci:v00008086d00002825sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801HR/HO/HH (ICH8R/DO/DH) 2 port SATA Controller [IDE mode] (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002826* + ID_MODEL_FROM_DATABASE=C600/X79 series chipset SATA RAID Controller + +pci:v00008086d00002827* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [RAID mode] + +pci:v00008086d00002828* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [IDE mode] + +pci:v00008086d00002828sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [IDE mode] (Inspiron 1420) + +pci:v00008086d00002828sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [IDE mode] (Compaq 6710b) + +pci:v00008086d00002828sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [IDE mode] (CCG-RUMBA) + +pci:v00008086d00002829* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] + +pci:v00008086d00002829sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Aspire 5920G) + +pci:v00008086d00002829sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Compaq 6710b) + +pci:v00008086d00002829sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Compaq 6910p) + +pci:v00008086d00002829sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Pavilion dv6700) + +pci:v00008086d00002829sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Presario C700) + +pci:v00008086d00002829sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Vaio VGN-FZ260E) + +pci:v00008086d00002829sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (VAIO VGN-NR120E) + +pci:v00008086d00002829sv000017AAsd000020A7* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (ThinkPad T61/R61) + +pci:v00008086d00002829sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002829sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (CCG-RUMBA) + +pci:v00008086d0000282A* + ID_MODEL_FROM_DATABASE=82801 Mobile SATA Controller [RAID mode] + +pci:v00008086d0000282Asv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=82801 Mobile SATA Controller [RAID mode] (Latitude E6510) + +pci:v00008086d0000282Asv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=82801 Mobile SATA Controller [RAID mode] (PC1-GROOVE) + +pci:v00008086d00002830* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 + +pci:v00008086d00002830sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Acer Aspire 5920G) + +pci:v00008086d00002830sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (OptiPlex 745) + +pci:v00008086d00002830sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Inspiron 1420) + +pci:v00008086d00002830sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Compaq 6710b) + +pci:v00008086d00002830sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Compaq 6910p) + +pci:v00008086d00002830sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Pavilion dv6700) + +pci:v00008086d00002830sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Presario C700) + +pci:v00008086d00002830sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (P5B) + +pci:v00008086d00002830sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Vaio VGN-FZ260E) + +pci:v00008086d00002830sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (VAIO VGN-NR120E) + +pci:v00008086d00002830sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002830sv000017AAsd000020AA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (ThinkPad T61/R61) + +pci:v00008086d00002830sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002830sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (CCG-RUMBA) + +pci:v00008086d00002831* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 + +pci:v00008086d00002831sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Aspire 5920G) + +pci:v00008086d00002831sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (OptiPlex 745) + +pci:v00008086d00002831sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Inspiron 1420) + +pci:v00008086d00002831sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Compaq 6710b) + +pci:v00008086d00002831sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Compaq 6910p) + +pci:v00008086d00002831sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Pavilion dv6700) + +pci:v00008086d00002831sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Presario C700) + +pci:v00008086d00002831sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (P5B) + +pci:v00008086d00002831sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Vaio VGN-FZ260E) + +pci:v00008086d00002831sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (VAIO VGN-NR120E) + +pci:v00008086d00002831sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002831sv000017AAsd000020AA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (ThinkPad T61/R61) + +pci:v00008086d00002831sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002831sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (CCG-RUMBA) + +pci:v00008086d00002832* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 + +pci:v00008086d00002832sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Aspire 5920G) + +pci:v00008086d00002832sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (OptiPlex 745) + +pci:v00008086d00002832sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Inspiron 1420) + +pci:v00008086d00002832sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Compaq 6710b) + +pci:v00008086d00002832sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Compaq 6910p) + +pci:v00008086d00002832sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Pavilion dv6700) + +pci:v00008086d00002832sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Presario C700) + +pci:v00008086d00002832sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (P5B) + +pci:v00008086d00002832sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Vaio VGN-FZ260E) + +pci:v00008086d00002832sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (VAIO VGN-NR120E) + +pci:v00008086d00002832sv000017AAsd000020AA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (ThinkPad T61/R61) + +pci:v00008086d00002832sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002832sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (CCG-RUMBA) + +pci:v00008086d00002833* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 + +pci:v00008086d00002833sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (P5B) + +pci:v00008086d00002834* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 + +pci:v00008086d00002834sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Aspire 5920G) + +pci:v00008086d00002834sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (OptiPlex 745) + +pci:v00008086d00002834sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Inspiron 1420) + +pci:v00008086d00002834sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Compaq 6710b) + +pci:v00008086d00002834sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Compaq 6910p) + +pci:v00008086d00002834sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Pavilion dv6700) + +pci:v00008086d00002834sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (P5B) + +pci:v00008086d00002834sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Vaio VGN-FZ260E) + +pci:v00008086d00002834sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (VAIO VGN-NR120E) + +pci:v00008086d00002834sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002834sv000017AAsd000020AA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (ThinkPad T61/R61) + +pci:v00008086d00002834sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002834sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (CCG-RUMBA) + +pci:v00008086d00002835* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 + +pci:v00008086d00002835sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Acer Aspire 5920G) + +pci:v00008086d00002835sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (OptiPlex 745) + +pci:v00008086d00002835sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Inspiron 1420) + +pci:v00008086d00002835sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Compaq 6710b) + +pci:v00008086d00002835sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Compaq 6910p) + +pci:v00008086d00002835sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Pavilion dv6700) + +pci:v00008086d00002835sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (P5B) + +pci:v00008086d00002835sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Vaio VGN-FZ260E) + +pci:v00008086d00002835sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (VAIO VGN-NR120E) + +pci:v00008086d00002835sv000017AAsd000020AA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Thinkpad T61/R61) + +pci:v00008086d00002835sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002835sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (CCG-RUMBA) + +pci:v00008086d00002836* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 + +pci:v00008086d00002836sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Aspire 5920G) + +pci:v00008086d00002836sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (OptiPlex 745) + +pci:v00008086d00002836sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Inspiron 1420) + +pci:v00008086d00002836sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Compaq 6710b) + +pci:v00008086d00002836sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Compaq 6910p) + +pci:v00008086d00002836sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Pavilion dv6700) + +pci:v00008086d00002836sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Presario C700) + +pci:v00008086d00002836sv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (P5B) + +pci:v00008086d00002836sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Vaio VGN-FZ260E) + +pci:v00008086d00002836sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (VAIO VGN-NR120E) + +pci:v00008086d00002836sv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (P965 Neo MS-7235 mainboard) + +pci:v00008086d00002836sv000017AAsd000020AB* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (ThinkPad T61/R61) + +pci:v00008086d00002836sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002836sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (CCG-RUMBA) + +pci:v00008086d0000283A* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 + +pci:v00008086d0000283Asv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Acer Aspire 5920G) + +pci:v00008086d0000283Asv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (OptiPlex 745) + +pci:v00008086d0000283Asv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Inspiron 1420) + +pci:v00008086d0000283Asv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Compaq 6710b) + +pci:v00008086d0000283Asv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Compaq 6910p) + +pci:v00008086d0000283Asv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Pavilion dv6700) + +pci:v00008086d0000283Asv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (P5B) + +pci:v00008086d0000283Asv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Vaio VGN-FZ260E) + +pci:v00008086d0000283Asv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (VAIO VGN-NR120E) + +pci:v00008086d0000283Asv000017AAsd000020AB* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (ThinkPad T61/R61) + +pci:v00008086d0000283Asv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d0000283Asv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (CCG-RUMBA) + +pci:v00008086d0000283E* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller + +pci:v00008086d0000283Esv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Aspire 5920G) + +pci:v00008086d0000283Esv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (OptiPlex 745) + +pci:v00008086d0000283Esv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Inspiron 1420) + +pci:v00008086d0000283Esv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Presario C700) + +pci:v00008086d0000283Esv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (P5B) + +pci:v00008086d0000283Esv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Vaio VGN-FZ260E) + +pci:v00008086d0000283Esv0000104Dsd00009008* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Vaio VGN-SZ79SN_C) + +pci:v00008086d0000283Esv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (VAIO VGN-NR120E) + +pci:v00008086d0000283Esv00001462sd00007235* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (P965 Neo MS-7235 mainboard) + +pci:v00008086d0000283Esv000017AAsd000020A9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (ThinkPad T61/R61) + +pci:v00008086d0000283Esv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d0000283Esv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (CCG-RUMBA) + +pci:v00008086d0000283F* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 + +pci:v00008086d0000283Fsv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (OptiPlex 745) + +pci:v00008086d0000283Fsv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (Compaq 6910p) + +pci:v00008086d0000283Fsv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (VAIO VGN-NR120E) + +pci:v00008086d0000283Fsv000017AAsd000020AD* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (ThinkPad T61/R61) + +pci:v00008086d0000283Fsv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002841* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 + +pci:v00008086d00002841sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (Compaq 6910p) + +pci:v00008086d00002841sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (VAIO VGN-NR120E) + +pci:v00008086d00002841sv000017AAsd000020AD* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (ThinkPad T61/R61) + +pci:v00008086d00002841sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002843* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 + +pci:v00008086d00002843sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (VAIO VGN-NR120E) + +pci:v00008086d00002843sv000017AAsd000020AD* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (ThinkPad T61/R61) + +pci:v00008086d00002843sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002845* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 + +pci:v00008086d00002845sv000017AAsd000020AD* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (ThinkPad T61/R61) + +pci:v00008086d00002845sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002847* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 5 + +pci:v00008086d00002847sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 5 (OptiPlex 745) + +pci:v00008086d00002847sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 5 (Compaq 6910p) + +pci:v00008086d00002847sv000017AAsd000020AD* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 5 (ThinkPad T61/R61) + +pci:v00008086d00002847sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 5 (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002849* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 6 + +pci:v00008086d0000284B* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller + +pci:v00008086d0000284Bsv00001025sd0000011F* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Realtek ALC268 audio codec) + +pci:v00008086d0000284Bsv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Aspire 5920G) + +pci:v00008086d0000284Bsv00001025sd00000145* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Realtek ALC889 (Aspire 8920G w. Dolby Theather)) + +pci:v00008086d0000284Bsv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (OptiPlex 745) + +pci:v00008086d0000284Bsv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Inspiron 1420) + +pci:v00008086d0000284Bsv00001028sd000001F9* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Dell Latitude D630) + +pci:v00008086d0000284Bsv00001028sd000001FF* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Dell Precision M4300) + +pci:v00008086d0000284Bsv00001028sd00000256* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Studio 1735) + +pci:v00008086d0000284Bsv0000103Csd00002802* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Compaq dc7700p) + +pci:v00008086d0000284Bsv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Compaq 6710b) + +pci:v00008086d0000284Bsv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Compaq 6910p) + +pci:v00008086d0000284Bsv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Pavilion dv6700) + +pci:v00008086d0000284Bsv00001043sd00001339* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Asus M51S series) + +pci:v00008086d0000284Bsv00001043sd000081EC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (P5B) + +pci:v00008086d0000284Bsv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Vaio VGN-FZ260E) + +pci:v00008086d0000284Bsv0000104Dsd00009008* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Vaio VGN-SZ79SN_C) + +pci:v00008086d0000284Bsv0000104Dsd00009016* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Sony VAIO VGN-AR51M) + +pci:v00008086d0000284Bsv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (VAIO VGN-NR120E) + +pci:v00008086d0000284Bsv000014F1sd00005051* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Presario C700) + +pci:v00008086d0000284Bsv000017AAsd000020AC* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (ThinkPad T61/R61) + +pci:v00008086d0000284Bsv000017C0sd00004088* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d0000284Bsv00008384sd00007616* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (Dell Vostro 1400) + +pci:v00008086d0000284Bsv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (CCG-RUMBA) + +pci:v00008086d0000284F* + ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) Thermal Reporting Device + +pci:v00008086d00002850* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller + +pci:v00008086d00002850sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Aspire 5920G) + +pci:v00008086d00002850sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Inspiron 1420) + +pci:v00008086d00002850sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Compaq 6710b) + +pci:v00008086d00002850sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Compaq 6910p) + +pci:v00008086d00002850sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Pavilion dv6700) + +pci:v00008086d00002850sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Presario C700) + +pci:v00008086d00002850sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Vaio VGN-FZ260E) + +pci:v00008086d00002850sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (VAIO VGN-NR120E) + +pci:v00008086d00002850sv000017AAsd000020A6* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (ThinkPad T61/R61) + +pci:v00008086d00002850sv000017C0sd00004083* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002850sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (CCG-RUMBA) + +pci:v00008086d00002912* + ID_MODEL_FROM_DATABASE=82801IH (ICH9DH) LPC Interface Controller + +pci:v00008086d00002914* + ID_MODEL_FROM_DATABASE=82801IO (ICH9DO) LPC Interface Controller + +pci:v00008086d00002914sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801IO (ICH9DO) LPC Interface Controller (Optiplex 755) + +pci:v00008086d00002916* + ID_MODEL_FROM_DATABASE=82801IR (ICH9R) LPC Interface Controller + +pci:v00008086d00002916sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801IR (ICH9R) LPC Interface Controller (Inspiron 530) + +pci:v00008086d00002916sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801IR (ICH9R) LPC Interface Controller (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002916sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801IR (ICH9R) LPC Interface Controller (P5K PRO Motherboard) + +pci:v00008086d00002916sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801IR (ICH9R) LPC Interface Controller (Desktop Board DP35DP) + +pci:v00008086d00002917* + ID_MODEL_FROM_DATABASE=ICH9M-E LPC Interface Controller + +pci:v00008086d00002917sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=ICH9M-E LPC Interface Controller (CCM-BOOGIE) + +pci:v00008086d00002918* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) LPC Interface Controller + +pci:v00008086d00002918sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) LPC Interface Controller (PowerEdge R610 82801IB (ICH9) LPC Interface Controller) + +pci:v00008086d00002918sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) LPC Interface Controller (G33/P35 Neo) + +pci:v00008086d00002918sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) LPC Interface Controller (QEMU Virtual Machine) + +pci:v00008086d00002919* + ID_MODEL_FROM_DATABASE=ICH9M LPC Interface Controller + +pci:v00008086d00002920* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] + +pci:v00008086d00002920sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (Inspiron 530) + +pci:v00008086d00002920sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (PowerEdge R300 onboard SATA Controller) + +pci:v00008086d00002920sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (PowerEdge T300 onboard SATA Controller) + +pci:v00008086d00002920sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (Optiplex 755) + +pci:v00008086d00002920sv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (PowerEdge R200 onboard SATA Controller) + +pci:v00008086d00002920sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 4 port SATA Controller [IDE mode] (P5K PRO Motherboard) + +pci:v00008086d00002921* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 2 port SATA Controller [IDE mode] + +pci:v00008086d00002921sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 2 port SATA Controller [IDE mode] (PowerEdge R710 SATA IDE Controller) + +pci:v00008086d00002921sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 2 port SATA Controller [IDE mode] (PowerEdge R610 SATA IDE Controller) + +pci:v00008086d00002921sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 2 port SATA Controller [IDE mode] (PowerEdge T610 SATA IDE Controller) + +pci:v00008086d00002921sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 2 port SATA Controller [IDE mode] (G33/P35 Neo) + +pci:v00008086d00002922* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] + +pci:v00008086d00002922sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] (P5K PRO Motherboard) + +pci:v00008086d00002922sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] (QEMU Virtual Machine) + +pci:v00008086d00002922sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode] (Desktop Board DP35DP) + +pci:v00008086d00002923* + ID_MODEL_FROM_DATABASE=82801IB (ICH9) 4 port SATA Controller [AHCI mode] + +pci:v00008086d00002925* + ID_MODEL_FROM_DATABASE=82801IR/IO (ICH9R/DO) SATA Controller [RAID mode] + +pci:v00008086d00002925sv00001734sd000010E0* + ID_MODEL_FROM_DATABASE=82801IR/IO (ICH9R/DO) SATA Controller [RAID mode] (System Board D2542) + +pci:v00008086d00002925sv00008086sd00002925* + ID_MODEL_FROM_DATABASE=82801IR/IO (ICH9R/DO) SATA Controller [RAID mode] (System Board D2542) + +pci:v00008086d00002926* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] + +pci:v00008086d00002926sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (Inspiron 530) + +pci:v00008086d00002926sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (PowerEdge R300 onboard SATA Controller) + +pci:v00008086d00002926sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (PowerEdge T300 onboard SATA Controller) + +pci:v00008086d00002926sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (Optiplex 755) + +pci:v00008086d00002926sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (P5K PRO Motherboard) + +pci:v00008086d00002926sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) 2 port SATA Controller [IDE mode] (G33/P35 Neo) + +pci:v00008086d00002928* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 2 port SATA Controller [IDE mode] + +pci:v00008086d00002929* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] + +pci:v00008086d00002929sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] (dv6-1190en) + +pci:v00008086d00002929sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 4 port SATA Controller [AHCI mode] (CCM-BOOGIE) + +pci:v00008086d0000292C* + ID_MODEL_FROM_DATABASE=82801IEM (ICH9M-E) SATA Controller [RAID mode] + +pci:v00008086d0000292D* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 2 port SATA Controller [IDE mode] + +pci:v00008086d0000292Dsv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801IBM/IEM (ICH9M/ICH9M-E) 2 port SATA Controller [IDE mode] (CCM-BOOGIE) + +pci:v00008086d00002930* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller + +pci:v00008086d00002930sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (Inspiron 530) + +pci:v00008086d00002930sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (Optiplex 755) + +pci:v00008086d00002930sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002930sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (dv6-1190en) + +pci:v00008086d00002930sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (P5K PRO Motherboard) + +pci:v00008086d00002930sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (G33/P35 Neo) + +pci:v00008086d00002930sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (QEMU Virtual Machine) + +pci:v00008086d00002930sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (Desktop Board DP35DP) + +pci:v00008086d00002930sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) SMBus Controller (CCM-BOOGIE) + +pci:v00008086d00002932* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) Thermal Subsystem + +pci:v00008086d00002932sv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) Thermal Subsystem (dv6-1190en) + +pci:v00008086d00002934* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 + +pci:v00008086d00002934sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (Inspiron 530) + +pci:v00008086d00002934sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge R300 onboard UHCI) + +pci:v00008086d00002934sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge T300 onboard UHCI) + +pci:v00008086d00002934sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (Optiplex 755) + +pci:v00008086d00002934sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge R710 USB UHCI Controller) + +pci:v00008086d00002934sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge R610 USB UHCI Controller) + +pci:v00008086d00002934sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002934sv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge R200 onboard UHCI) + +pci:v00008086d00002934sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge M610 onboard UHCI) + +pci:v00008086d00002934sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (PowerEdge M710 USB UHCI Controller) + +pci:v00008086d00002934sv00001028sd00002011* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (Optiplex 755) + +pci:v00008086d00002934sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002934sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (P5K PRO Motherboard) + +pci:v00008086d00002934sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (G33/P35 Neo) + +pci:v00008086d00002934sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (QEMU Virtual Machine) + +pci:v00008086d00002934sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (Desktop Board DP35DP) + +pci:v00008086d00002934sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #1 (CCM-BOOGIE) + +pci:v00008086d00002935* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 + +pci:v00008086d00002935sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (Inspiron 530) + +pci:v00008086d00002935sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge R300 onboard UHCI) + +pci:v00008086d00002935sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge T300 onboard UHCI) + +pci:v00008086d00002935sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (Optiplex 755) + +pci:v00008086d00002935sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge R710 USB UHCI Controller) + +pci:v00008086d00002935sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge R610 USB UHCI Controller) + +pci:v00008086d00002935sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002935sv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge R200 onboard UHCI) + +pci:v00008086d00002935sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge M610 onboard UHCI) + +pci:v00008086d00002935sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (PowerEdge M710 USB UHCI Controller) + +pci:v00008086d00002935sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002935sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (P5K PRO Motherboard) + +pci:v00008086d00002935sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (G33/P35 Neo) + +pci:v00008086d00002935sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (QEMU Virtual Machine) + +pci:v00008086d00002935sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (Desktop Board DP35DP) + +pci:v00008086d00002935sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #2 (CCM-BOOGIE) + +pci:v00008086d00002936* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 + +pci:v00008086d00002936sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (Inspiron 530) + +pci:v00008086d00002936sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge R300 onboard UHCI) + +pci:v00008086d00002936sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge T300 onboard UHCI) + +pci:v00008086d00002936sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (Optiplex 755) + +pci:v00008086d00002936sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002936sv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge R200 onboard UHCI) + +pci:v00008086d00002936sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge M610 onboard UHCI) + +pci:v00008086d00002936sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (PowerEdge M710 USB UHCI Controller) + +pci:v00008086d00002936sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002936sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (P5K PRO Motherboard) + +pci:v00008086d00002936sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (G33/P35 Neo) + +pci:v00008086d00002936sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (QEMU Virtual Machine) + +pci:v00008086d00002936sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (Desktop Board DP35DP) + +pci:v00008086d00002936sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #3 (CCM-BOOGIE) + +pci:v00008086d00002937* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 + +pci:v00008086d00002937sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Inspiron 530) + +pci:v00008086d00002937sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Optiplex 755) + +pci:v00008086d00002937sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (PowerEdge R710 USB UHCI Controller) + +pci:v00008086d00002937sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (PowerEdge R610 USB UHCI Controller) + +pci:v00008086d00002937sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002937sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (PowerEdge M610 onboard UHCI) + +pci:v00008086d00002937sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (PowerEdge M710 USB UHCI Controller) + +pci:v00008086d00002937sv00001028sd00002011* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Optiplex 755) + +pci:v00008086d00002937sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002937sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (P5K PRO Motherboard) + +pci:v00008086d00002937sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (G33/P35 Neo) + +pci:v00008086d00002937sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (QEMU Virtual Machine) + +pci:v00008086d00002937sv00008086sd00002937* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Optiplex 755) + +pci:v00008086d00002937sv00008086sd00002942* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (828011 (ICH9 Family ) USB UHCI Controller) + +pci:v00008086d00002937sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (Desktop Board DP35DP) + +pci:v00008086d00002937sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #4 (CCM-BOOGIE) + +pci:v00008086d00002938* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 + +pci:v00008086d00002938sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (Inspiron 530) + +pci:v00008086d00002938sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (Optiplex 755) + +pci:v00008086d00002938sv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (PowerEdge R710 USB UHCI Controller) + +pci:v00008086d00002938sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (PowerEdge R610 USB UHCI Controller) + +pci:v00008086d00002938sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002938sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (PowerEdge M610 onboard UHCI) + +pci:v00008086d00002938sv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (PowerEdge M710 USB UHCI Controller) + +pci:v00008086d00002938sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002938sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (P5K PRO Motherboard) + +pci:v00008086d00002938sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (G33/P35 Neo) + +pci:v00008086d00002938sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (QEMU Virtual Machine) + +pci:v00008086d00002938sv00008086sd00002938* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (Optiplex 755) + +pci:v00008086d00002938sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (Desktop Board DP35DP) + +pci:v00008086d00002938sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #5 (CCM-BOOGIE) + +pci:v00008086d00002939* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 + +pci:v00008086d00002939sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (Inspiron 530) + +pci:v00008086d00002939sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (PowerEdge T300 onboard UHCI) + +pci:v00008086d00002939sv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (PowerEdge T610 USB UHCI Controller) + +pci:v00008086d00002939sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002939sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (P5K PRO Motherboard) + +pci:v00008086d00002939sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (G33/P35 Neo) + +pci:v00008086d00002939sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (QEMU Virtual Machine) + +pci:v00008086d00002939sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (Desktop Board DP35DP) + +pci:v00008086d00002939sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB UHCI Controller #6 (CCM-BOOGIE) + +pci:v00008086d0000293A* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 + +pci:v00008086d0000293Asv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (Inspiron 530) + +pci:v00008086d0000293Asv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge R300 onboard EHCI) + +pci:v00008086d0000293Asv00001028sd00000210* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge T300 onboard EHCI) + +pci:v00008086d0000293Asv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (Optiplex 755) + +pci:v00008086d0000293Asv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge R710 USB EHCI Controller) + +pci:v00008086d0000293Asv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge R610 USB EHCI Controller) + +pci:v00008086d0000293Asv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge T610 USB EHCI Controller) + +pci:v00008086d0000293Asv00001028sd0000023C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge R200 onboard EHCI) + +pci:v00008086d0000293Asv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge M610 onboard EHCI) + +pci:v00008086d0000293Asv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (PowerEdge M710 USB EHCI Controller) + +pci:v00008086d0000293Asv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (Asus IPIBL-LB Motherboard) + +pci:v00008086d0000293Asv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (P5K PRO Motherboard) + +pci:v00008086d0000293Asv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (G33/P35 Neo) + +pci:v00008086d0000293Asv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (QEMU Virtual Machine) + +pci:v00008086d0000293Asv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (Desktop Board DP35DP) + +pci:v00008086d0000293Asv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #1 (CCM-BOOGIE) + +pci:v00008086d0000293C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 + +pci:v00008086d0000293Csv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (Inspiron 530) + +pci:v00008086d0000293Csv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (Optiplex 755) + +pci:v00008086d0000293Csv00001028sd00000235* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (PowerEdge R710 USB EHCI Controller) + +pci:v00008086d0000293Csv00001028sd00000236* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (PowerEdge R610 USB EHCI Controller) + +pci:v00008086d0000293Csv00001028sd00000237* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (PowerEdge T610 USB EHCI Controller) + +pci:v00008086d0000293Csv00001028sd00000287* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (PowerEdge M610 onboard EHCI) + +pci:v00008086d0000293Csv00001028sd0000029C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (PowerEdge M710 USB EHCI Controller) + +pci:v00008086d0000293Csv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (Asus IPIBL-LB Motherboard) + +pci:v00008086d0000293Csv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (P5K PRO Motherboard) + +pci:v00008086d0000293Csv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (G33/P35 Neo) + +pci:v00008086d0000293Csv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (QEMU Virtual Machine) + +pci:v00008086d0000293Csv00008086sd0000293C* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (Optiplex 755) + +pci:v00008086d0000293Csv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (Desktop Board DP35DP) + +pci:v00008086d0000293Csv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) USB2 EHCI Controller #2 (CCM-BOOGIE) + +pci:v00008086d0000293E* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller + +pci:v00008086d0000293Esv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (Inspiron 530) + +pci:v00008086d0000293Esv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (Optiplex 755) + +pci:v00008086d0000293Esv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (Asus IPIBL-LB Motherboard) + +pci:v00008086d0000293Esv0000103Csd00003628* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (dv6-1190en) + +pci:v00008086d0000293Esv00001043sd0000829F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (P5K PRO Motherboard) + +pci:v00008086d0000293Esv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (G33/P35 Neo) + +pci:v00008086d0000293Esv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (QEMU Virtual Machine) + +pci:v00008086d0000293Esv00008086sd0000293E* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (Optiplex 755) + +pci:v00008086d0000293Esv00008086sd00002940* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (Optiplex 755) + +pci:v00008086d0000293Esv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) HD Audio Controller (CCM-BOOGIE) + +pci:v00008086d00002940* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 + +pci:v00008086d00002940sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 (Inspiron 530) + +pci:v00008086d00002940sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 (Optiplex 755) + +pci:v00008086d00002940sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002940sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 (P5K PRO Motherboard) + +pci:v00008086d00002940sv00008086sd00002940* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 1 (Optiplex 755) + +pci:v00008086d00002942* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 2 + +pci:v00008086d00002942sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 2 (Inspiron 530) + +pci:v00008086d00002944* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 3 + +pci:v00008086d00002944sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 3 (Inspiron 530) + +pci:v00008086d00002944sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 3 (Asus IPIBL-LB Motherboard) + +pci:v00008086d00002946* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 4 + +pci:v00008086d00002946sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 4 (Inspiron 530) + +pci:v00008086d00002948* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 5 + +pci:v00008086d00002948sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 5 (Inspiron 530) + +pci:v00008086d00002948sv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 5 (P5K PRO Motherboard) + +pci:v00008086d0000294A* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 6 + +pci:v00008086d0000294Asv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 6 (Inspiron 530) + +pci:v00008086d0000294Asv00001043sd00008277* + ID_MODEL_FROM_DATABASE=82801I (ICH9 Family) PCI Express Port 6 (P5K PRO Motherboard) + +pci:v00008086d0000294C* + ID_MODEL_FROM_DATABASE=82566DC-2 Gigabit Network Connection + +pci:v00008086d0000294Csv000017AAsd0000302E* + ID_MODEL_FROM_DATABASE=82566DC-2 Gigabit Network Connection (82566DM-2 Gigabit Network Connection) + +pci:v00008086d00002970* + ID_MODEL_FROM_DATABASE=82946GZ/PL/GL Memory Controller Hub + +pci:v00008086d00002971* + ID_MODEL_FROM_DATABASE=82946GZ/PL/GL PCI Express Root Port + +pci:v00008086d00002972* + ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller + +pci:v00008086d00002973* + ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller + +pci:v00008086d00002974* + ID_MODEL_FROM_DATABASE=82946GZ/GL HECI Controller + +pci:v00008086d00002975* + ID_MODEL_FROM_DATABASE=82946GZ/GL HECI Controller + +pci:v00008086d00002976* + ID_MODEL_FROM_DATABASE=82946GZ/GL PT IDER Controller + +pci:v00008086d00002977* + ID_MODEL_FROM_DATABASE=82946GZ/GL KT Controller + +pci:v00008086d00002980* + ID_MODEL_FROM_DATABASE=82G35 Express DRAM Controller + +pci:v00008086d00002981* + ID_MODEL_FROM_DATABASE=82G35 Express PCI Express Root Port + +pci:v00008086d00002982* + ID_MODEL_FROM_DATABASE=82G35 Express Integrated Graphics Controller + +pci:v00008086d00002983* + ID_MODEL_FROM_DATABASE=82G35 Express Integrated Graphics Controller + +pci:v00008086d00002984* + ID_MODEL_FROM_DATABASE=82G35 Express HECI Controller + +pci:v00008086d00002990* + ID_MODEL_FROM_DATABASE=82Q963/Q965 Memory Controller Hub + +pci:v00008086d00002990sv00001028sd000001DA* + ID_MODEL_FROM_DATABASE=82Q963/Q965 Memory Controller Hub (OptiPlex 745) + +pci:v00008086d00002991* + ID_MODEL_FROM_DATABASE=82Q963/Q965 PCI Express Root Port + +pci:v00008086d00002992* + ID_MODEL_FROM_DATABASE=82Q963/Q965 Integrated Graphics Controller + +pci:v00008086d00002993* + ID_MODEL_FROM_DATABASE=82Q963/Q965 Integrated Graphics Controller + +pci:v00008086d00002994* + ID_MODEL_FROM_DATABASE=82Q963/Q965 HECI Controller + +pci:v00008086d00002995* + ID_MODEL_FROM_DATABASE=82Q963/Q965 HECI Controller + +pci:v00008086d00002996* + ID_MODEL_FROM_DATABASE=82Q963/Q965 PT IDER Controller + +pci:v00008086d00002997* + ID_MODEL_FROM_DATABASE=82Q963/Q965 KT Controller + +pci:v00008086d000029A0* + ID_MODEL_FROM_DATABASE=82P965/G965 Memory Controller Hub + +pci:v00008086d000029A0sv00001043sd000081EA* + ID_MODEL_FROM_DATABASE=82P965/G965 Memory Controller Hub (P5B) + +pci:v00008086d000029A0sv00001462sd00007276* + ID_MODEL_FROM_DATABASE=82P965/G965 Memory Controller Hub (MS-7276 [G965MDH]) + +pci:v00008086d000029A1* + ID_MODEL_FROM_DATABASE=82P965/G965 PCI Express Root Port + +pci:v00008086d000029A2* + ID_MODEL_FROM_DATABASE=82G965 Integrated Graphics Controller + +pci:v00008086d000029A2sv00001462sd00007276* + ID_MODEL_FROM_DATABASE=82G965 Integrated Graphics Controller (MS-7276 [G965MDH]) + +pci:v00008086d000029A3* + ID_MODEL_FROM_DATABASE=82G965 Integrated Graphics Controller + +pci:v00008086d000029A4* + ID_MODEL_FROM_DATABASE=82P965/G965 HECI Controller + +pci:v00008086d000029A5* + ID_MODEL_FROM_DATABASE=82P965/G965 HECI Controller + +pci:v00008086d000029A6* + ID_MODEL_FROM_DATABASE=82P965/G965 PT IDER Controller + +pci:v00008086d000029A7* + ID_MODEL_FROM_DATABASE=82P965/G965 KT Controller + +pci:v00008086d000029B0* + ID_MODEL_FROM_DATABASE=82Q35 Express DRAM Controller + +pci:v00008086d000029B0sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express DRAM Controller (OptiPlex 755) + +pci:v00008086d000029B1* + ID_MODEL_FROM_DATABASE=82Q35 Express PCI Express Root Port + +pci:v00008086d000029B1sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express PCI Express Root Port (OptiPlex 755) + +pci:v00008086d000029B2* + ID_MODEL_FROM_DATABASE=82Q35 Express Integrated Graphics Controller + +pci:v00008086d000029B2sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express Integrated Graphics Controller (OptiPlex 755) + +pci:v00008086d000029B3* + ID_MODEL_FROM_DATABASE=82Q35 Express Integrated Graphics Controller + +pci:v00008086d000029B3sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express Integrated Graphics Controller (OptiPlex 755) + +pci:v00008086d000029B4* + ID_MODEL_FROM_DATABASE=82Q35 Express MEI Controller + +pci:v00008086d000029B4sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express MEI Controller (OptiPlex 755) + +pci:v00008086d000029B5* + ID_MODEL_FROM_DATABASE=82Q35 Express MEI Controller + +pci:v00008086d000029B6* + ID_MODEL_FROM_DATABASE=82Q35 Express PT IDER Controller + +pci:v00008086d000029B6sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express PT IDER Controller (OptiPlex 755) + +pci:v00008086d000029B7* + ID_MODEL_FROM_DATABASE=82Q35 Express Serial KT Controller + +pci:v00008086d000029B7sv00001028sd00000211* + ID_MODEL_FROM_DATABASE=82Q35 Express Serial KT Controller (OptiPlex 755) + +pci:v00008086d000029C0* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller + +pci:v00008086d000029C0sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (Inspiron 530) + +pci:v00008086d000029C0sv0000103Csd00002A6F* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (Asus IPIBL-LB Motherboard) + +pci:v00008086d000029C0sv00001043sd00008276* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (P5K PRO Motherboard) + +pci:v00008086d000029C0sv00001043sd000082B0* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (P5KPL-VM Motherboard) + +pci:v00008086d000029C0sv00001462sd00007360* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (G33/P35 Neo) + +pci:v00008086d000029C0sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (QEMU Virtual Machine) + +pci:v00008086d000029C0sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express DRAM Controller (Desktop Board DP35DP) + +pci:v00008086d000029C1* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express PCI Express Root Port + +pci:v00008086d000029C1sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express PCI Express Root Port (Inspiron 530) + +pci:v00008086d000029C1sv00001043sd00008276* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express PCI Express Root Port (P5K PRO Motherboard) + +pci:v00008086d000029C2* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller + +pci:v00008086d000029C2sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller (Inspiron 530) + +pci:v00008086d000029C2sv00001043sd000082B0* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller (P5KPL-VM Motherboard) + +pci:v00008086d000029C3* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller + +pci:v00008086d000029C3sv00001028sd0000020D* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller (Inspiron 530) + +pci:v00008086d000029C3sv00001043sd000082B0* + ID_MODEL_FROM_DATABASE=82G33/G31 Express Integrated Graphics Controller (P5KPL-VM Motherboard) + +pci:v00008086d000029C4* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express MEI Controller + +pci:v00008086d000029C4sv00008086sd00005044* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express MEI Controller (Desktop Board DP35DP) + +pci:v00008086d000029C5* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express MEI Controller + +pci:v00008086d000029C6* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express PT IDER Controller + +pci:v00008086d000029C7* + ID_MODEL_FROM_DATABASE=82G33/G31/P35/P31 Express Serial KT Controller + +pci:v00008086d000029CF* + ID_MODEL_FROM_DATABASE=Virtual HECI Controller + +pci:v00008086d000029D0* + ID_MODEL_FROM_DATABASE=82Q33 Express DRAM Controller + +pci:v00008086d000029D1* + ID_MODEL_FROM_DATABASE=82Q33 Express PCI Express Root Port + +pci:v00008086d000029D2* + ID_MODEL_FROM_DATABASE=82Q33 Express Integrated Graphics Controller + +pci:v00008086d000029D3* + ID_MODEL_FROM_DATABASE=82Q33 Express Integrated Graphics Controller + +pci:v00008086d000029D4* + ID_MODEL_FROM_DATABASE=82Q33 Express MEI Controller + +pci:v00008086d000029D5* + ID_MODEL_FROM_DATABASE=82Q33 Express MEI Controller + +pci:v00008086d000029D6* + ID_MODEL_FROM_DATABASE=82Q33 Express PT IDER Controller + +pci:v00008086d000029D7* + ID_MODEL_FROM_DATABASE=82Q33 Express Serial KT Controller + +pci:v00008086d000029E0* + ID_MODEL_FROM_DATABASE=82X38/X48 Express DRAM Controller + +pci:v00008086d000029E1* + ID_MODEL_FROM_DATABASE=82X38/X48 Express Host-Primary PCI Express Bridge + +pci:v00008086d000029E4* + ID_MODEL_FROM_DATABASE=82X38/X48 Express MEI Controller + +pci:v00008086d000029E5* + ID_MODEL_FROM_DATABASE=82X38/X48 Express MEI Controller + +pci:v00008086d000029E6* + ID_MODEL_FROM_DATABASE=82X38/X48 Express PT IDER Controller + +pci:v00008086d000029E7* + ID_MODEL_FROM_DATABASE=82X38/X48 Express Serial KT Controller + +pci:v00008086d000029E9* + ID_MODEL_FROM_DATABASE=82X38/X48 Express Host-Secondary PCI Express Bridge + +pci:v00008086d000029F0* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset DRAM Controller + +pci:v00008086d000029F1* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset Host-Primary PCI Express Bridge + +pci:v00008086d000029F4* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset MEI Controller + +pci:v00008086d000029F5* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset MEI Controller + +pci:v00008086d000029F6* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset PT IDER Controller + +pci:v00008086d000029F7* + ID_MODEL_FROM_DATABASE=3200/3210 Chipset Serial KT Controller + +pci:v00008086d000029F9* + ID_MODEL_FROM_DATABASE=3210 Chipset Host-Secondary PCI Express Bridge + +pci:v00008086d00002A00* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub + +pci:v00008086d00002A00sv00001025sd00000121* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Acer Aspire 5920G) + +pci:v00008086d00002A00sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Inspiron 1420) + +pci:v00008086d00002A00sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Compaq 6710b) + +pci:v00008086d00002A00sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Compaq 6910p) + +pci:v00008086d00002A00sv0000103Csd000030CC* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Pavilion dv6700) + +pci:v00008086d00002A00sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Presario C700) + +pci:v00008086d00002A00sv0000104Dsd00009005* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Vaio VGN-FZ260E) + +pci:v00008086d00002A00sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (VAIO VGN-NR120E) + +pci:v00008086d00002A00sv000017AAsd000020B1* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (ThinkPad T61) + +pci:v00008086d00002A00sv000017AAsd000020B3* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (ThinkPad T61/R61) + +pci:v00008086d00002A00sv000017C0sd00004082* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002A00sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (CCG-RUMBA) + +pci:v00008086d00002A01* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 PCI Express Root Port + +pci:v00008086d00002A02* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) + +pci:v00008086d00002A02sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Inspiron 1420) + +pci:v00008086d00002A02sv00001028sd000001F9* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Latitude D630) + +pci:v00008086d00002A02sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Compaq 6710b) + +pci:v00008086d00002A02sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Presario C700) + +pci:v00008086d00002A02sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (VAIO VGN-NR120E) + +pci:v00008086d00002A02sv000017AAsd000020B5* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (ThinkPad T61/R61) + +pci:v00008086d00002A02sv000017C0sd00004082* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002A02sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (CCG-RUMBA) + +pci:v00008086d00002A03* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) + +pci:v00008086d00002A03sv00001028sd000001F3* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Inspiron 1420) + +pci:v00008086d00002A03sv0000103Csd000030C0* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Compaq 6710b) + +pci:v00008086d00002A03sv0000103Csd000030D9* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Presario C700) + +pci:v00008086d00002A03sv0000104Dsd0000902D* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (VAIO VGN-NR120E) + +pci:v00008086d00002A03sv000017AAsd000020B5* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (ThinkPad T61/R61) + +pci:v00008086d00002A03sv000017C0sd00004082* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Medion WIM 2210 Notebook PC [MD96850]) + +pci:v00008086d00002A03sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (CCG-RUMBA) + +pci:v00008086d00002A04* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 MEI Controller + +pci:v00008086d00002A04sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 MEI Controller (Compaq 6910p) + +pci:v00008086d00002A05* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 MEI Controller + +pci:v00008086d00002A06* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 PT IDER Controller + +pci:v00008086d00002A06sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 PT IDER Controller (Compaq 6910p) + +pci:v00008086d00002A07* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 KT Controller + +pci:v00008086d00002A07sv0000103Csd000030C1* + ID_MODEL_FROM_DATABASE=Mobile PM965/GM965 KT Controller (Compaq 6910p) + +pci:v00008086d00002A10* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Memory Controller Hub + +pci:v00008086d00002A10sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Memory Controller Hub (CCG-RUMBA) + +pci:v00008086d00002A11* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 PCI Express Root Port + +pci:v00008086d00002A12* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Integrated Graphics Controller + +pci:v00008086d00002A12sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Integrated Graphics Controller (CCG-RUMBA) + +pci:v00008086d00002A13* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Integrated Graphics Controller + +pci:v00008086d00002A13sv0000E4BFsd0000CC47* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 Integrated Graphics Controller (CCG-RUMBA) + +pci:v00008086d00002A14* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 MEI Controller + +pci:v00008086d00002A15* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 MEI Controller + +pci:v00008086d00002A16* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 PT IDER Controller + +pci:v00008086d00002A17* + ID_MODEL_FROM_DATABASE=Mobile GME965/GLE960 KT Controller + +pci:v00008086d00002A40* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Memory Controller Hub + +pci:v00008086d00002A40sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Memory Controller Hub (CCM-BOOGIE) + +pci:v00008086d00002A41* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset PCI Express Graphics Port + +pci:v00008086d00002A41sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset PCI Express Graphics Port (CCM-BOOGIE) + +pci:v00008086d00002A42* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002A42sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (CCM-BOOGIE) + +pci:v00008086d00002A43* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002A43sv0000E4BFsd0000CC4D* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset Integrated Graphics Controller (CCM-BOOGIE) + +pci:v00008086d00002A44* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset MEI Controller + +pci:v00008086d00002A45* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset MEI Controller + +pci:v00008086d00002A46* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset PT IDER Controller + +pci:v00008086d00002A47* + ID_MODEL_FROM_DATABASE=Mobile 4 Series Chipset AMT SOL Redirection + +pci:v00008086d00002A50* + ID_MODEL_FROM_DATABASE=Cantiga MEI Controller + +pci:v00008086d00002A51* + ID_MODEL_FROM_DATABASE=Cantiga MEI Controller + +pci:v00008086d00002A52* + ID_MODEL_FROM_DATABASE=Cantiga PT IDER Controller + +pci:v00008086d00002A53* + ID_MODEL_FROM_DATABASE=Cantiga AMT SOL Redirection + +pci:v00008086d00002B00* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family System Configuration Controller 1 + +pci:v00008086d00002B02* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family System Configuration Controller 2 + +pci:v00008086d00002B04* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Power Controller + +pci:v00008086d00002B08* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Caching Agent 0 + +pci:v00008086d00002B0C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Caching Agent 1 + +pci:v00008086d00002B10* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Home Agent 0 + +pci:v00008086d00002B13* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 0c + +pci:v00008086d00002B14* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 0a + +pci:v00008086d00002B16* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 0b + +pci:v00008086d00002B18* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Home Agent 1 + +pci:v00008086d00002B1B* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 1c + +pci:v00008086d00002B1C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 1a + +pci:v00008086d00002B1E* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Memory Controller 1b + +pci:v00008086d00002B20* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 0 + +pci:v00008086d00002B22* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family System Configuration Controller 3 + +pci:v00008086d00002B24* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 1 + +pci:v00008086d00002B28* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 2 + +pci:v00008086d00002B2A* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family System Configuration Controller 4 + +pci:v00008086d00002B2C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 3 + +pci:v00008086d00002B30* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 4 + +pci:v00008086d00002B34* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 5 + +pci:v00008086d00002B38* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 6 + +pci:v00008086d00002B3C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 7 + +pci:v00008086d00002B40* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Router Port 0-1 + +pci:v00008086d00002B42* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Router Port 2-3 + +pci:v00008086d00002B44* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Router Port 4-5 + +pci:v00008086d00002B46* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Router Port 6-7 + +pci:v00008086d00002B48* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Test and Debug 0 + +pci:v00008086d00002B4C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Test and Debug 1 + +pci:v00008086d00002B50* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 0: REUT control/status + +pci:v00008086d00002B52* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 0: Misc. control/status + +pci:v00008086d00002B54* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 1: REUT control/status + +pci:v00008086d00002B56* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 1: Misc. control/status + +pci:v00008086d00002B58* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 2: REUT control/status + +pci:v00008086d00002B5A* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 2: Misc. control/status + +pci:v00008086d00002B5C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 3: REUT control/status + +pci:v00008086d00002B5E* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family QPI Physical Port 3: Misc. control/status + +pci:v00008086d00002B60* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family SMI Physical Port 0: REUT control/status + +pci:v00008086d00002B62* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family SMI Physical Port 0: Misc control/status + +pci:v00008086d00002B64* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family SMI Physical Port 1: REUT control/status + +pci:v00008086d00002B66* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family SMI Physical Port 1: Misc control/status + +pci:v00008086d00002B68* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 8 + +pci:v00008086d00002B6C* + ID_MODEL_FROM_DATABASE=Xeon Processor E7 Product Family Last Level Cache Coherence Engine 9 + +pci:v00008086d00002C01* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QuickPath Architecture System Address Decoder + +pci:v00008086d00002C10* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QPI Link 0 + +pci:v00008086d00002C11* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QPI Physical 0 + +pci:v00008086d00002C14* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QPI Link 1 + +pci:v00008086d00002C15* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QPI Physical 1 + +pci:v00008086d00002C18* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller + +pci:v00008086d00002C19* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Target Address Decoder + +pci:v00008086d00002C1A* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller RAS Registers + +pci:v00008086d00002C1C* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Test Registers + +pci:v00008086d00002C20* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 0 Control Registers + +pci:v00008086d00002C21* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 0 Address Registers + +pci:v00008086d00002C22* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 0 Rank Registers + +pci:v00008086d00002C23* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 0 Thermal Control Registers + +pci:v00008086d00002C28* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 1 Control Registers + +pci:v00008086d00002C29* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 1 Address Registers + +pci:v00008086d00002C2A* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 1 Rank Registers + +pci:v00008086d00002C2B* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 1 Thermal Control Registers + +pci:v00008086d00002C30* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 2 Control Registers + +pci:v00008086d00002C31* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 2 Address Registers + +pci:v00008086d00002C32* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 2 Rank Registers + +pci:v00008086d00002C33* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 Integrated Memory Controller Channel 2 Thermal Control Registers + +pci:v00008086d00002C40* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C41* + ID_MODEL_FROM_DATABASE=Xeon 5500/Core i7 QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C50* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C51* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C52* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C53* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C54* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C55* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C56* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C57* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-Core Registers + +pci:v00008086d00002C58* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C59* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5A* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5B* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5C* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5D* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5E* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C5F* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Generic Non-core Registers + +pci:v00008086d00002C61* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-core Registers + +pci:v00008086d00002C62* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture Generic Non-core Registers + +pci:v00008086d00002C70* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QuickPath Architecture Generic Non-core Registers + +pci:v00008086d00002C81* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture System Address Decoder + +pci:v00008086d00002C90* + ID_MODEL_FROM_DATABASE=Core Processor QPI Link 0 + +pci:v00008086d00002C91* + ID_MODEL_FROM_DATABASE=Core Processor QPI Physical 0 + +pci:v00008086d00002C98* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller + +pci:v00008086d00002C99* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Target Address Decoder + +pci:v00008086d00002C9A* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Test Registers + +pci:v00008086d00002C9C* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Test Registers + +pci:v00008086d00002CA0* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 0 Control Registers + +pci:v00008086d00002CA1* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 0 Address Registers + +pci:v00008086d00002CA2* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 0 Rank Registers + +pci:v00008086d00002CA3* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 0 Thermal Control Registers + +pci:v00008086d00002CA8* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 1 Control Registers + +pci:v00008086d00002CA9* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 1 Address Registers + +pci:v00008086d00002CAA* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 1 Rank Registers + +pci:v00008086d00002CAB* + ID_MODEL_FROM_DATABASE=Core Processor Integrated Memory Controller Channel 1 Thermal Control Registers + +pci:v00008086d00002CC1* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI System Address Decoder + +pci:v00008086d00002CD0* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Link 0 + +pci:v00008086d00002CD1* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Physical 0 + +pci:v00008086d00002CD4* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Link 1 + +pci:v00008086d00002CD5* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Physical 1 + +pci:v00008086d00002CD8* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Registers + +pci:v00008086d00002CD9* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Target Address Decoder + +pci:v00008086d00002CDA* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller RAS Registers + +pci:v00008086d00002CDC* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Test Registers + +pci:v00008086d00002CE0* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 0 Control + +pci:v00008086d00002CE1* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 0 Address + +pci:v00008086d00002CE2* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 0 Rank + +pci:v00008086d00002CE3* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 0 Thermal Control + +pci:v00008086d00002CE8* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 1 Control + +pci:v00008086d00002CE9* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 1 Address + +pci:v00008086d00002CEA* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 1 Rank + +pci:v00008086d00002CEB* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 1 Thermal Control + +pci:v00008086d00002CF0* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 2 Control + +pci:v00008086d00002CF1* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 2 Address + +pci:v00008086d00002CF2* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 2 Rank + +pci:v00008086d00002CF3* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Integrated Memory Controller Channel 2 Thermal Control + +pci:v00008086d00002D01* + ID_MODEL_FROM_DATABASE=Core Processor QuickPath Architecture System Address Decoder + +pci:v00008086d00002D10* + ID_MODEL_FROM_DATABASE=Core Processor QPI Link 0 + +pci:v00008086d00002D11* + ID_MODEL_FROM_DATABASE=1st Generation Core i3/5/7 Processor QPI Physical 0 + +pci:v00008086d00002D12* + ID_MODEL_FROM_DATABASE=1st Generation Core i3/5/7 Processor Reserved + +pci:v00008086d00002D13* + ID_MODEL_FROM_DATABASE=1st Generation Core i3/5/7 Processor Reserved + +pci:v00008086d00002D81* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QuickPath Architecture System Address Decoder + +pci:v00008086d00002D90* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QPI Link 0 + +pci:v00008086d00002D91* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QPI Physical 0 + +pci:v00008086d00002D92* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Mirror Port Link 0 + +pci:v00008086d00002D93* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Mirror Port Link 1 + +pci:v00008086d00002D94* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QPI Link 1 + +pci:v00008086d00002D95* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series QPI Physical 1 + +pci:v00008086d00002D98* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Registers + +pci:v00008086d00002D99* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Target Address Decoder + +pci:v00008086d00002D9A* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller RAS Registers + +pci:v00008086d00002D9C* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Test Registers + +pci:v00008086d00002DA0* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 0 Control + +pci:v00008086d00002DA1* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 0 Address + +pci:v00008086d00002DA2* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 0 Rank + +pci:v00008086d00002DA3* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 0 Thermal Control + +pci:v00008086d00002DA8* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 1 Control + +pci:v00008086d00002DA9* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 1 Address + +pci:v00008086d00002DAA* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 1 Rank + +pci:v00008086d00002DAB* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 1 Thermal Control + +pci:v00008086d00002DB0* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 2 Control + +pci:v00008086d00002DB1* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 2 Address + +pci:v00008086d00002DB2* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 2 Rank + +pci:v00008086d00002DB3* + ID_MODEL_FROM_DATABASE=Xeon 5600 Series Integrated Memory Controller Channel 2 Thermal Control + +pci:v00008086d00002E00* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E01* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E02* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E03* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E04* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E05* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E06* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002E07* + ID_MODEL_FROM_DATABASE=4 Series Chipset Serial KT Controller + +pci:v00008086d00002E10* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E11* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E12* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E12sv000017AAsd00003048* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller (ThinkCentre M6258) + +pci:v00008086d00002E13* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E14* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E15* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E16* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002E17* + ID_MODEL_FROM_DATABASE=4 Series Chipset Serial KT Controller + +pci:v00008086d00002E20* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E20sv00001028sd00000283* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller (Dell Vostro 220) + +pci:v00008086d00002E20sv00001043sd000082D3* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller (P5Q Deluxe Motherboard) + +pci:v00008086d00002E20sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00002E21* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E21sv00001043sd000082D3* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port (P5Q Deluxe Motherboard) + +pci:v00008086d00002E21sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port (GA-EP45-DS5 Motherboard) + +pci:v00008086d00002E22* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E22sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller (GA-EG45M-DS2H Mainboard) + +pci:v00008086d00002E23* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E23sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller (GA-EG45M-DS2H Mainboard) + +pci:v00008086d00002E24* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E25* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E26* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002E27* + ID_MODEL_FROM_DATABASE=4 Series Chipset Serial KT Controller + +pci:v00008086d00002E29* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E30* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E31* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E32* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E33* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E34* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E35* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E36* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002E37* + ID_MODEL_FROM_DATABASE=4 Series Chipset Serial KT Controller + +pci:v00008086d00002E40* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E41* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E42* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E43* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E44* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E45* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E46* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002E47* + ID_MODEL_FROM_DATABASE=4 Series Chipset Serial KT Controller + +pci:v00008086d00002E50* + ID_MODEL_FROM_DATABASE=CE Media Processor CE3100 + +pci:v00008086d00002E52* + ID_MODEL_FROM_DATABASE=CE Media Processor Clock and Reset Controller + +pci:v00008086d00002E58* + ID_MODEL_FROM_DATABASE=CE Media Processor Interrupt Controller + +pci:v00008086d00002E5A* + ID_MODEL_FROM_DATABASE=CE Media Processor CE3100 A/V Bridge + +pci:v00008086d00002E5B* + ID_MODEL_FROM_DATABASE=Graphics Media Accelerator 500 Graphics + +pci:v00008086d00002E5C* + ID_MODEL_FROM_DATABASE=CE Media Processor Video Decoder + +pci:v00008086d00002E5D* + ID_MODEL_FROM_DATABASE=CE Media Processor Transport Stream Interface + +pci:v00008086d00002E5E* + ID_MODEL_FROM_DATABASE=CE Media Processor Transport Stream Processor 0 + +pci:v00008086d00002E5F* + ID_MODEL_FROM_DATABASE=CE Media Processor Audio DSP + +pci:v00008086d00002E60* + ID_MODEL_FROM_DATABASE=CE Media Processor Audio Interfaces + +pci:v00008086d00002E61* + ID_MODEL_FROM_DATABASE=CE Media Processor Video Display Controller + +pci:v00008086d00002E62* + ID_MODEL_FROM_DATABASE=CE Media Processor Video Processing Unit + +pci:v00008086d00002E63* + ID_MODEL_FROM_DATABASE=CE Media Processor HDMI Tx Interface + +pci:v00008086d00002E65* + ID_MODEL_FROM_DATABASE=CE Media Processor Expansion Bus Interface + +pci:v00008086d00002E66* + ID_MODEL_FROM_DATABASE=CE Media Processor UART + +pci:v00008086d00002E67* + ID_MODEL_FROM_DATABASE=CE Media Processor General Purpose I/Os + +pci:v00008086d00002E68* + ID_MODEL_FROM_DATABASE=CE Media Processor I2C Interface + +pci:v00008086d00002E69* + ID_MODEL_FROM_DATABASE=CE Media Processor Smart Card Interface + +pci:v00008086d00002E6A* + ID_MODEL_FROM_DATABASE=CE Media Processor SPI Master Interface + +pci:v00008086d00002E6E* + ID_MODEL_FROM_DATABASE=CE Media Processor Gigabit Ethernet Controller + +pci:v00008086d00002E6F* + ID_MODEL_FROM_DATABASE=CE Media Processor Media Timing Unit + +pci:v00008086d00002E70* + ID_MODEL_FROM_DATABASE=CE Media Processor USB + +pci:v00008086d00002E71* + ID_MODEL_FROM_DATABASE=CE Media Processor SATA + +pci:v00008086d00002E73* + ID_MODEL_FROM_DATABASE=CE Media Processor CE3100 PCI Express + +pci:v00008086d00002E90* + ID_MODEL_FROM_DATABASE=4 Series Chipset DRAM Controller + +pci:v00008086d00002E91* + ID_MODEL_FROM_DATABASE=4 Series Chipset PCI Express Root Port + +pci:v00008086d00002E92* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E93* + ID_MODEL_FROM_DATABASE=4 Series Chipset Integrated Graphics Controller + +pci:v00008086d00002E94* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E95* + ID_MODEL_FROM_DATABASE=4 Series Chipset HECI Controller + +pci:v00008086d00002E96* + ID_MODEL_FROM_DATABASE=4 Series Chipset PT IDER Controller + +pci:v00008086d00002F00* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMI2 + +pci:v00008086d00002F01* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 0 + +pci:v00008086d00002F02* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 1 + +pci:v00008086d00002F03* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 1 + +pci:v00008086d00002F04* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 2 + +pci:v00008086d00002F05* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 2 + +pci:v00008086d00002F06* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 2 + +pci:v00008086d00002F07* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 2 + +pci:v00008086d00002F08* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 3 + +pci:v00008086d00002F09* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 3 + +pci:v00008086d00002F0A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 3 + +pci:v00008086d00002F0B* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCI Express Root Port 3 + +pci:v00008086d00002F10* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F11* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F12* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F13* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F14* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F15* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F16* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F17* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F18* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F19* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F1A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F1B* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F1C* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 IIO Debug + +pci:v00008086d00002F1D* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCIe Ring Interface + +pci:v00008086d00002F1E* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Scratchpad & Semaphore Registers + +pci:v00008086d00002F1F* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Scratchpad & Semaphore Registers + +pci:v00008086d00002F20* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 0 + +pci:v00008086d00002F21* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 1 + +pci:v00008086d00002F22* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 2 + +pci:v00008086d00002F23* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 3 + +pci:v00008086d00002F24* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 4 + +pci:v00008086d00002F25* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 5 + +pci:v00008086d00002F26* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 6 + +pci:v00008086d00002F27* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DMA Channel 7 + +pci:v00008086d00002F28* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Address Map, VTd_Misc, System Management + +pci:v00008086d00002F29* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Hot Plug + +pci:v00008086d00002F2A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 RAS, Control Status and Global Errors + +pci:v00008086d00002F2C* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 I/O APIC + +pci:v00008086d00002F2E* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 RAID 5/6 + +pci:v00008086d00002F2F* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 RAID 5/6 + +pci:v00008086d00002F30* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 0 + +pci:v00008086d00002F32* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 + +pci:v00008086d00002F33* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 1 + +pci:v00008086d00002F34* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 PCIe Ring Interface + +pci:v00008086d00002F36* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 0 & 1 Monitoring + +pci:v00008086d00002F37* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 0 & 1 Monitoring + +pci:v00008086d00002F38* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 1 + +pci:v00008086d00002F39* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 I/O Performance Monitoring + +pci:v00008086d00002F3A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 + +pci:v00008086d00002F3E* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 2 Monitoring + +pci:v00008086d00002F3F* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 2 Monitoring + +pci:v00008086d00002F40* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 + +pci:v00008086d00002F41* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 2 Monitoring + +pci:v00008086d00002F43* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 + +pci:v00008086d00002F45* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 Debug + +pci:v00008086d00002F46* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 Debug + +pci:v00008086d00002F47* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 2 Debug + +pci:v00008086d00002F60* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 1 + +pci:v00008086d00002F68* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Target Address, Thermal & RAS Registers + +pci:v00008086d00002F6A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder + +pci:v00008086d00002F6B* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder + +pci:v00008086d00002F6C* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder + +pci:v00008086d00002F6D* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel Target Address Decoder + +pci:v00008086d00002F6E* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO Channel 2/3 Broadcast + +pci:v00008086d00002F6F* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO Global Broadcast + +pci:v00008086d00002F70* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 0 Debug + +pci:v00008086d00002F71* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Target Address, Thermal & RAS Registers + +pci:v00008086d00002F76* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 E3 QPI Link Debug + +pci:v00008086d00002F78* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 1 Debug + +pci:v00008086d00002F79* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Target Address, Thermal & RAS Registers + +pci:v00008086d00002F7D* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Scratchpad & Semaphore Registers + +pci:v00008086d00002F7E* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 E3 QPI Link Debug + +pci:v00008086d00002F80* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 + +pci:v00008086d00002F81* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 R3 QPI Link 0 & 1 Monitoring + +pci:v00008086d00002F83* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 + +pci:v00008086d00002F85* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 Debug + +pci:v00008086d00002F86* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 Debug + +pci:v00008086d00002F87* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 0 Debug + +pci:v00008086d00002F88* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 VCU + +pci:v00008086d00002F8A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 VCU + +pci:v00008086d00002F90* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 1 + +pci:v00008086d00002F93* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 1 + +pci:v00008086d00002F95* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 1 Debug + +pci:v00008086d00002F96* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 QPI Link 1 Debug + +pci:v00008086d00002F98* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002F99* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002F9A* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002F9C* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FA0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Home Agent 0 + +pci:v00008086d00002FA8* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Target Address, Thermal & RAS Registers + +pci:v00008086d00002FAA* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder + +pci:v00008086d00002FAB* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder + +pci:v00008086d00002FAC* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder + +pci:v00008086d00002FAD* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel Target Address Decoder + +pci:v00008086d00002FAE* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO Channel 0/1 Broadcast + +pci:v00008086d00002FAF* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO Global Broadcast + +pci:v00008086d00002FB0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 0 Thermal Control + +pci:v00008086d00002FB1* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 1 Thermal Control + +pci:v00008086d00002FB2* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 0 ERROR Registers + +pci:v00008086d00002FB3* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 1 ERROR Registers + +pci:v00008086d00002FB4* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 2 Thermal Control + +pci:v00008086d00002FB5* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 3 Thermal Control + +pci:v00008086d00002FB6* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 2 ERROR Registers + +pci:v00008086d00002FB7* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 0 Channel 3 ERROR Registers + +pci:v00008086d00002FB8* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 2 & 3 + +pci:v00008086d00002FB9* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 2 & 3 + +pci:v00008086d00002FBA* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 2 & 3 + +pci:v00008086d00002FBB* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 2 & 3 + +pci:v00008086d00002FBC* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 0 & 1 + +pci:v00008086d00002FBD* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 0 & 1 + +pci:v00008086d00002FBE* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 0 & 1 + +pci:v00008086d00002FBF* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 DDRIO (VMSE) 0 & 1 + +pci:v00008086d00002FC0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FC1* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FC2* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FC3* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FC4* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FC5* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Power Control Unit + +pci:v00008086d00002FD0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 0 Thermal Control + +pci:v00008086d00002FD1* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 1 Thermal Control + +pci:v00008086d00002FD2* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 0 ERROR Registers + +pci:v00008086d00002FD3* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 1 ERROR Registers + +pci:v00008086d00002FD4* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 2 Thermal Control + +pci:v00008086d00002FD5* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 3 Thermal Control + +pci:v00008086d00002FD6* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 2 ERROR Registers + +pci:v00008086d00002FD7* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Integrated Memory Controller 1 Channel 3 ERROR Registers + +pci:v00008086d00002FE0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE1* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE2* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE3* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE4* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE5* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE6* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE7* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE8* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FE9* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FEA* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FEB* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FEC* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FED* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FEE* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FEF* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF0* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF1* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF2* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF3* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF4* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF5* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF6* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF7* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Unicast Registers + +pci:v00008086d00002FF8* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Buffered Ring Agent + +pci:v00008086d00002FF9* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Buffered Ring Agent + +pci:v00008086d00002FFA* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Buffered Ring Agent + +pci:v00008086d00002FFB* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 Buffered Ring Agent + +pci:v00008086d00002FFC* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 System Address Decoder & Broadcast Registers + +pci:v00008086d00002FFD* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 System Address Decoder & Broadcast Registers + +pci:v00008086d00002FFE* + ID_MODEL_FROM_DATABASE=Xeon E7 v3/Xeon E5 v3/Core i7 System Address Decoder & Broadcast Registers + +pci:v00008086d00003165* + ID_MODEL_FROM_DATABASE=Wireless 3165 + +pci:v00008086d00003165sv00008086sd00004010* + ID_MODEL_FROM_DATABASE=Wireless 3165 (Dual Band Wireless AC 3165) + +pci:v00008086d00003165sv00008086sd00004210* + ID_MODEL_FROM_DATABASE=Wireless 3165 (Dual Band Wireless AC 3165) + +pci:v00008086d00003166* + ID_MODEL_FROM_DATABASE=Intel Dual Band Wireless-AC 3165 Plus Bluetooth + +pci:v00008086d00003200* + ID_MODEL_FROM_DATABASE=GD31244 PCI-X SATA HBA + +pci:v00008086d00003200sv00001775sd0000C200* + ID_MODEL_FROM_DATABASE=GD31244 PCI-X SATA HBA (C2K onboard SATA host bus adapter) + +pci:v00008086d00003310* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor + +pci:v00008086d00003310sv00001054sd00003030* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (HRA380 Hitachi RAID Adapter to PCIe) + +pci:v00008086d00003310sv00001054sd00003034* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (HRA381 Hitachi RAID Adapter to PCIe) + +pci:v00008086d00003313* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (SL8e) in IOC Mode SAS/SATA + +pci:v00008086d0000331B* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (SL8x) in IOC Mode SAS/SATA + +pci:v00008086d00003331* + ID_MODEL_FROM_DATABASE=IOC340 I/O Controller (VV8e) SAS/SATA + +pci:v00008086d00003339* + ID_MODEL_FROM_DATABASE=IOC340 I/O Controller (VV8x) SAS/SATA + +pci:v00008086d00003340* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller + +pci:v00008086d00003340sv00001014sd00000529* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (Thinkpad T40 series) + +pci:v00008086d00003340sv00001025sd0000005A* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (TravelMate 290) + +pci:v00008086d00003340sv0000103Csd0000088C* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (NC8000 laptop) + +pci:v00008086d00003340sv0000103Csd00000890* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (NC6000 laptop) + +pci:v00008086d00003340sv0000103Csd000008B0* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (tc1100 tablet) + +pci:v00008086d00003340sv0000144Dsd0000C005* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (X10 Laptop) + +pci:v00008086d00003340sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82855PM Processor to I/O Controller (P30/P35 notebook) + +pci:v00008086d00003341* + ID_MODEL_FROM_DATABASE=82855PM Processor to AGP Controller + +pci:v00008086d00003341sv0000144Dsd0000C00C* + ID_MODEL_FROM_DATABASE=82855PM Processor to AGP Controller (P30 notebook) + +pci:v00008086d00003363* + ID_MODEL_FROM_DATABASE=IOC340 I/O Controller in IOC Mode SAS/SATA + +pci:v00008086d00003382* + ID_MODEL_FROM_DATABASE=81342 [Chevelon] I/O Processor (ATUe) + +pci:v00008086d000033C3* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (SL8De) in IOC Mode SAS/SATA + +pci:v00008086d000033CB* + ID_MODEL_FROM_DATABASE=IOP348 I/O Processor (SL8Dx) in IOC Mode SAS/SATA + +pci:v00008086d00003400* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003401* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003402* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003403* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port + +pci:v00008086d00003403sv00001028sd00000236* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port (PowerEdge R610 I/O Hub to ESI Port) + +pci:v00008086d00003403sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port (PowerEdge M610 I/O Hub to ESI Port) + +pci:v00008086d00003403sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port (PowerEdge R410 I/O Hub to ESI Port) + +pci:v00008086d00003403sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port (PowerEdge T410 I/O Hub to ESI Port) + +pci:v00008086d00003403sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=5500 I/O Hub to ESI Port (ProLiant ML150 G6 Server) + +pci:v00008086d00003404* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003405* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003406* + ID_MODEL_FROM_DATABASE=5520 I/O Hub to ESI Port + +pci:v00008086d00003406sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=5520 I/O Hub to ESI Port (ProLiant G6 series) + +pci:v00008086d00003407* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub to ESI Port + +pci:v00008086d00003408* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 1 + +pci:v00008086d00003408sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 1 (ProLiant G6 series) + +pci:v00008086d00003409* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 2 + +pci:v00008086d0000340A* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 3 + +pci:v00008086d0000340Asv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 3 (ProLiant ML150 G6 Server) + +pci:v00008086d0000340B* + ID_MODEL_FROM_DATABASE=5520/X58 I/O Hub PCI Express Root Port 4 + +pci:v00008086d0000340C* + ID_MODEL_FROM_DATABASE=5520/X58 I/O Hub PCI Express Root Port 5 + +pci:v00008086d0000340D* + ID_MODEL_FROM_DATABASE=5520/X58 I/O Hub PCI Express Root Port 6 + +pci:v00008086d0000340E* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 7 + +pci:v00008086d0000340Esv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 7 (ProLiant ML150 G6 Server) + +pci:v00008086d0000340F* + ID_MODEL_FROM_DATABASE=5520/5500/X58 I/O Hub PCI Express Root Port 8 + +pci:v00008086d00003410* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub PCI Express Root Port 9 + +pci:v00008086d00003411* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub PCI Express Root Port 10 + +pci:v00008086d00003418* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 Physical Layer Port 0 + +pci:v00008086d00003419* + ID_MODEL_FROM_DATABASE=7500/5520/5500 Physical Layer Port 1 + +pci:v00008086d00003420* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 + +pci:v00008086d00003421* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub PCI Express Root Port 0 + +pci:v00008086d00003422* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub GPIO and Scratch Pad Registers + +pci:v00008086d00003422sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub GPIO and Scratch Pad Registers (ProLiant G6 series) + +pci:v00008086d00003423* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub Control Status and RAS Registers + +pci:v00008086d00003423sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub Control Status and RAS Registers (ProLiant G6 series) + +pci:v00008086d00003425* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 Physical and Link Layer Registers Port 0 + +pci:v00008086d00003426* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 Routing and Protocol Layer Registers Port 0 + +pci:v00008086d00003427* + ID_MODEL_FROM_DATABASE=7500/5520/5500 Physical and Link Layer Registers Port 1 + +pci:v00008086d00003428* + ID_MODEL_FROM_DATABASE=7500/5520/5500 Routing & Protocol Layer Register Port 1 + +pci:v00008086d00003429* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d0000342A* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d0000342B* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d0000342C* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d0000342D* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub I/OxAPIC Interrupt Controller + +pci:v00008086d0000342E* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub System Management Registers + +pci:v00008086d0000342Esv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub System Management Registers (ProLiant G6 series) + +pci:v00008086d0000342F* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 Trusted Execution Technology Registers + +pci:v00008086d00003430* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d00003431* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d00003432* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d00003433* + ID_MODEL_FROM_DATABASE=5520/5500/X58 Chipset QuickData Technology Device + +pci:v00008086d00003438* + ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub Throttle Registers + +pci:v00008086d00003500* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Upstream Port + +pci:v00008086d00003500sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Upstream Port (ProLiant DL140 G3) + +pci:v00008086d00003500sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Upstream Port (X7DBN Motherboard) + +pci:v00008086d00003501* + ID_MODEL_FROM_DATABASE=6310ESB PCI Express Upstream Port + +pci:v00008086d00003504* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB I/OxAPIC Interrupt Controller + +pci:v00008086d00003505* + ID_MODEL_FROM_DATABASE=6310ESB I/OxAPIC Interrupt Controller + +pci:v00008086d0000350C* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express to PCI-X Bridge + +pci:v00008086d0000350Csv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express to PCI-X Bridge (ProLiant DL140 G3) + +pci:v00008086d0000350Csv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express to PCI-X Bridge (X7DBN Motherboard) + +pci:v00008086d0000350D* + ID_MODEL_FROM_DATABASE=6310ESB PCI Express to PCI-X Bridge + +pci:v00008086d00003510* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E1 + +pci:v00008086d00003510sv0000103Csd000031FE* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E1 (ProLiant DL140 G3) + +pci:v00008086d00003510sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E1 (X7DBN Motherboard) + +pci:v00008086d00003511* + ID_MODEL_FROM_DATABASE=6310ESB PCI Express Downstream Port E1 + +pci:v00008086d00003514* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E2 + +pci:v00008086d00003515* + ID_MODEL_FROM_DATABASE=6310ESB PCI Express Downstream Port E2 + +pci:v00008086d00003518* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E3 + +pci:v00008086d00003518sv000015D9sd00009680* + ID_MODEL_FROM_DATABASE=6311ESB/6321ESB PCI Express Downstream Port E3 (X7DBN Motherboard) + +pci:v00008086d00003519* + ID_MODEL_FROM_DATABASE=6310ESB PCI Express Downstream Port E3 + +pci:v00008086d00003575* + ID_MODEL_FROM_DATABASE=82830M/MG/MP Host Bridge + +pci:v00008086d00003575sv00000E11sd00000030* + ID_MODEL_FROM_DATABASE=82830M/MG/MP Host Bridge (Evo N600c) + +pci:v00008086d00003575sv00001014sd0000021D* + ID_MODEL_FROM_DATABASE=82830M/MG/MP Host Bridge (ThinkPad A/T/X Series) + +pci:v00008086d00003575sv0000104Dsd000080E7* + ID_MODEL_FROM_DATABASE=82830M/MG/MP Host Bridge (VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP) + +pci:v00008086d00003576* + ID_MODEL_FROM_DATABASE=82830M/MP AGP Bridge + +pci:v00008086d00003577* + ID_MODEL_FROM_DATABASE=82830M/MG Integrated Graphics Controller + +pci:v00008086d00003577sv00001014sd00000513* + ID_MODEL_FROM_DATABASE=82830M/MG Integrated Graphics Controller (ThinkPad A/T/X Series) + +pci:v00008086d00003578* + ID_MODEL_FROM_DATABASE=82830M/MG/MP Host Bridge + +pci:v00008086d00003580* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller + +pci:v00008086d00003580sv00001014sd0000055C* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (ThinkPad R50e) + +pci:v00008086d00003580sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Extensa 3000 series laptop) + +pci:v00008086d00003580sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D400) + +pci:v00008086d00003580sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude X300) + +pci:v00008086d00003580sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D500) + +pci:v00008086d00003580sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D505) + +pci:v00008086d00003580sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 700m/710m) + +pci:v00008086d00003580sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 5160) + +pci:v00008086d00003580sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PC8) + +pci:v00008086d00003580sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Amilo M1420) + +pci:v00008086d00003580sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (V5D Single Board Computer) + +pci:v00008086d00003580sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CE9) + +pci:v00008086d00003580sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CL9 mainboard) + +pci:v00008086d00003580sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PSL09 PrPMC) + +pci:v00008086d00003580sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CC9-SAMBA) + +pci:v00008086d00003580sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CD2-BEBOP) + +pci:v00008086d00003581* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to AGP Controller + +pci:v00008086d00003581sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to AGP Controller (Amilo M1420) + +pci:v00008086d00003582* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device + +pci:v00008086d00003582sv00001014sd00000562* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (ThinkPad R50e) + +pci:v00008086d00003582sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (Latitude D400) + +pci:v00008086d00003582sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (Latitude X300) + +pci:v00008086d00003582sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (Latitude D500) + +pci:v00008086d00003582sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (Latitude D505) + +pci:v00008086d00003582sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (Inspiron 700m/710m) + +pci:v00008086d00003582sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (PC8 integrated graphics) + +pci:v00008086d00003582sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (V5D Single Board Computer VGA) + +pci:v00008086d00003582sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (CE9) + +pci:v00008086d00003582sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (CL9 mainboard) + +pci:v00008086d00003582sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (PSL09 PrPMC) + +pci:v00008086d00003582sv0000E4BFsd00000CC9* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (CC9-SAMBA) + +pci:v00008086d00003582sv0000E4BFsd00000CD2* + ID_MODEL_FROM_DATABASE=82852/855GM Integrated Graphics Device (CD2-BEBOP) + +pci:v00008086d00003584* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller + +pci:v00008086d00003584sv00001014sd0000055D* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (ThinkPad R50e) + +pci:v00008086d00003584sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Extensa 3000 series laptop) + +pci:v00008086d00003584sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D400) + +pci:v00008086d00003584sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude X300) + +pci:v00008086d00003584sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D500) + +pci:v00008086d00003584sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D505) + +pci:v00008086d00003584sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 700m/710m) + +pci:v00008086d00003584sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 5160) + +pci:v00008086d00003584sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PC8) + +pci:v00008086d00003584sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Amilo M1420) + +pci:v00008086d00003584sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (V5D Single Board Computer) + +pci:v00008086d00003584sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CE9) + +pci:v00008086d00003584sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CL9 mainboard) + +pci:v00008086d00003584sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PSL09 PrPMC) + +pci:v00008086d00003585* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller + +pci:v00008086d00003585sv00001014sd0000055E* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (ThinkPad R50e) + +pci:v00008086d00003585sv00001025sd00000064* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Extensa 3000 series laptop) + +pci:v00008086d00003585sv00001028sd00000139* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D400) + +pci:v00008086d00003585sv00001028sd0000014F* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude X300) + +pci:v00008086d00003585sv00001028sd00000152* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D500) + +pci:v00008086d00003585sv00001028sd00000163* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Latitude D505) + +pci:v00008086d00003585sv00001028sd0000018D* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 700m/710m) + +pci:v00008086d00003585sv00001028sd00000196* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Inspiron 5160) + +pci:v00008086d00003585sv0000114Asd00000582* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PC8) + +pci:v00008086d00003585sv00001734sd00001055* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (Amilo M1420) + +pci:v00008086d00003585sv00001775sd000010D0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (V5D Single Board Computer) + +pci:v00008086d00003585sv00001775sd0000CE90* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CE9) + +pci:v00008086d00003585sv00004C53sd000010B0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (CL9 mainboard) + +pci:v00008086d00003585sv00004C53sd000010E0* + ID_MODEL_FROM_DATABASE=82852/82855 GM/GME/PM/GMV Processor to I/O Controller (PSL09 PrPMC) + +pci:v00008086d0000358C* + ID_MODEL_FROM_DATABASE=82854 GMCH + +pci:v00008086d0000358E* + ID_MODEL_FROM_DATABASE=82854 GMCH Integrated Graphics Device + +pci:v00008086d00003590* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub + +pci:v00008086d00003590sv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (eServer xSeries server mainboard) + +pci:v00008086d00003590sv00001028sd0000016C* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (PowerEdge 1850 Memory Controller Hub) + +pci:v00008086d00003590sv00001028sd0000016D* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (PowerEdge 2850 Memory Controller Hub) + +pci:v00008086d00003590sv00001028sd0000019A* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (PowerEdge SC1425) + +pci:v00008086d00003590sv00001734sd0000103E* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (PRIMERGY RX/TX S2 series) + +pci:v00008086d00003590sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (CR11/VR11 Single Board Computer) + +pci:v00008086d00003590sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=E7520 Memory Controller Hub (Telum ASLP10 Processor AMC) + +pci:v00008086d00003591* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers + +pci:v00008086d00003591sv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers (eServer xSeries server mainboard) + +pci:v00008086d00003591sv00001028sd00000168* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers (Precision Workstation 670 Mainboard) + +pci:v00008086d00003591sv00001028sd00000169* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers (Precision 470) + +pci:v00008086d00003591sv0000103Csd00003208* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers (ProLiant DL140 G2) + +pci:v00008086d00003591sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=E7525/E7520 Error Reporting Registers (Telum ASLP10 Processor AMC) + +pci:v00008086d00003592* + ID_MODEL_FROM_DATABASE=E7320 Memory Controller Hub + +pci:v00008086d00003592sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=E7320 Memory Controller Hub (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d00003593* + ID_MODEL_FROM_DATABASE=E7320 Error Reporting Registers + +pci:v00008086d00003593sv00001734sd00001073* + ID_MODEL_FROM_DATABASE=E7320 Error Reporting Registers (Primergy Econel 200 D2020 mainboard) + +pci:v00008086d00003594* + ID_MODEL_FROM_DATABASE=E7520 DMA Controller + +pci:v00008086d00003594sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7520 DMA Controller (CR11/VR11 Single Board Computer) + +pci:v00008086d00003594sv00004C53sd000010D0* + ID_MODEL_FROM_DATABASE=E7520 DMA Controller (Telum ASLP10 Processor AMC) + +pci:v00008086d00003595* + ID_MODEL_FROM_DATABASE=E7525/E7520/E7320 PCI Express Port A + +pci:v00008086d00003595sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7525/E7520/E7320 PCI Express Port A (CR11/VR11 Single Board Computer) + +pci:v00008086d00003596* + ID_MODEL_FROM_DATABASE=E7525/E7520/E7320 PCI Express Port A1 + +pci:v00008086d00003597* + ID_MODEL_FROM_DATABASE=E7525/E7520 PCI Express Port B + +pci:v00008086d00003597sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7525/E7520 PCI Express Port B (CR11/VR11 Single Board Computer) + +pci:v00008086d00003598* + ID_MODEL_FROM_DATABASE=E7520 PCI Express Port B1 + +pci:v00008086d00003598sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7520 PCI Express Port B1 (CR11/VR11 Single Board Computer) + +pci:v00008086d00003599* + ID_MODEL_FROM_DATABASE=E7520 PCI Express Port C + +pci:v00008086d00003599sv00001775sd00001100* + ID_MODEL_FROM_DATABASE=E7520 PCI Express Port C (CR11/VR11 Single Board Computer) + +pci:v00008086d0000359A* + ID_MODEL_FROM_DATABASE=E7520 PCI Express Port C1 + +pci:v00008086d0000359B* + ID_MODEL_FROM_DATABASE=E7525/E7520/E7320 Extended Configuration Registers + +pci:v00008086d0000359Bsv00001014sd000002DD* + ID_MODEL_FROM_DATABASE=E7525/E7520/E7320 Extended Configuration Registers (eServer xSeries server mainboard) + +pci:v00008086d0000359E* + ID_MODEL_FROM_DATABASE=E7525 Memory Controller Hub + +pci:v00008086d0000359Esv00001028sd00000168* + ID_MODEL_FROM_DATABASE=E7525 Memory Controller Hub (Precision Workstation 670 Mainboard) + +pci:v00008086d0000359Esv00001028sd00000169* + ID_MODEL_FROM_DATABASE=E7525 Memory Controller Hub (Precision 470) + +pci:v00008086d000035B0* + ID_MODEL_FROM_DATABASE=3100 Chipset Memory I/O Controller Hub + +pci:v00008086d000035B1* + ID_MODEL_FROM_DATABASE=3100 DRAM Controller Error Reporting Registers + +pci:v00008086d000035B5* + ID_MODEL_FROM_DATABASE=3100 Chipset Enhanced DMA Controller + +pci:v00008086d000035B6* + ID_MODEL_FROM_DATABASE=3100 Chipset PCI Express Port A + +pci:v00008086d000035B7* + ID_MODEL_FROM_DATABASE=3100 Chipset PCI Express Port A1 + +pci:v00008086d000035C8* + ID_MODEL_FROM_DATABASE=3100 Extended Configuration Test Overflow Registers + +pci:v00008086d00003600* + ID_MODEL_FROM_DATABASE=7300 Chipset Memory Controller Hub + +pci:v00008086d00003604* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 1 + +pci:v00008086d00003605* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 2 + +pci:v00008086d00003606* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 3 + +pci:v00008086d00003607* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 4 + +pci:v00008086d00003608* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 5 + +pci:v00008086d00003609* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 6 + +pci:v00008086d0000360A* + ID_MODEL_FROM_DATABASE=7300 Chipset PCI Express Port 7 + +pci:v00008086d0000360B* + ID_MODEL_FROM_DATABASE=7300 Chipset QuickData Technology Device + +pci:v00008086d0000360C* + ID_MODEL_FROM_DATABASE=7300 Chipset FSB Registers + +pci:v00008086d0000360Csv00001028sd000001F0* + ID_MODEL_FROM_DATABASE=7300 Chipset FSB Registers (PowerEdge R900 7300 Chipset FSB Registers) + +pci:v00008086d0000360D* + ID_MODEL_FROM_DATABASE=7300 Chipset Snoop Filter Registers + +pci:v00008086d0000360E* + ID_MODEL_FROM_DATABASE=7300 Chipset Debug and Miscellaneous Registers + +pci:v00008086d0000360F* + ID_MODEL_FROM_DATABASE=7300 Chipset FBD Branch 0 Registers + +pci:v00008086d00003610* + ID_MODEL_FROM_DATABASE=7300 Chipset FBD Branch 1 Registers + +pci:v00008086d00003700* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003701* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003702* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003703* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003704* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003705* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003706* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003707* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003708* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003709* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370A* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370B* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370C* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370D* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370E* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d0000370F* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 DMI + +pci:v00008086d00003710* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003711* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003712* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003713* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003714* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003715* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003716* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003717* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003718* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d00003719* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 CB3 DMA + +pci:v00008086d0000371A* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Link + +pci:v00008086d0000371B* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Routing and Protocol + +pci:v00008086d0000371D* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 QPI Routing and Protocol + +pci:v00008086d00003720* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 PCI Express Root Port 0 + +pci:v00008086d00003721* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 PCI Express Root Port 1 + +pci:v00008086d00003722* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 PCI Express Root Port 2 + +pci:v00008086d00003723* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 PCI Express Root Port 3 + +pci:v00008086d00003724* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 PCI Express Root Port 4 + +pci:v00008086d00003725* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 NTB Primary + +pci:v00008086d00003726* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 NTB Primary + +pci:v00008086d00003727* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 NTB Secondary + +pci:v00008086d00003728* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Core + +pci:v00008086d00003729* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Core + +pci:v00008086d0000372A* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Core + +pci:v00008086d0000372B* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Core + +pci:v00008086d0000372C* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 Reserved + +pci:v00008086d0000373F* + ID_MODEL_FROM_DATABASE=Xeon C5500/C3500 IOxAPIC + +pci:v00008086d000037CD* + ID_MODEL_FROM_DATABASE=X722 Virtual Function + +pci:v00008086d000037CE* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE backplane + +pci:v00008086d000037CEsv00001590sd00000200* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE backplane (Ethernet 10Gb 2-port 568i Adapter) + +pci:v00008086d000037CEsv00008086sd00000215* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE backplane (Ethernet 10Gb 2-port 568i Adapter) + +pci:v00008086d000037CF* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE QSFP+ + +pci:v00008086d000037D0* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE SFP+ + +pci:v00008086d000037D1* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 1GbE + +pci:v00008086d000037D2* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GBASE-T + +pci:v00008086d000037D3* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE SFP+ + +pci:v00008086d000037D4* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE QSFP+ + +pci:v00008086d000037D9* + ID_MODEL_FROM_DATABASE=X722 Hyper-V Virtual Function + +pci:v00008086d00003A00* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) 4-port SATA IDE Controller + +pci:v00008086d00003A02* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) SATA AHCI Controller + +pci:v00008086d00003A05* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) SATA RAID Controller + +pci:v00008086d00003A06* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) 2-port SATA IDE Controller + +pci:v00008086d00003A14* + ID_MODEL_FROM_DATABASE=82801JDO (ICH10DO) LPC Interface Controller + +pci:v00008086d00003A16* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller + +pci:v00008086d00003A16sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller (PowerEdge R410 LPC Interface Controller) + +pci:v00008086d00003A16sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller (PowerEdge T410 LPC Interface Controller) + +pci:v00008086d00003A16sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller (ProLiant G6 series) + +pci:v00008086d00003A16sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller (P5Q Deluxe Motherboard) + +pci:v00008086d00003A16sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) LPC Interface Controller (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A18* + ID_MODEL_FROM_DATABASE=82801JIB (ICH10) LPC Interface Controller + +pci:v00008086d00003A1A* + ID_MODEL_FROM_DATABASE=82801JD (ICH10D) LPC Interface Controller + +pci:v00008086d00003A20* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 4 port SATA IDE Controller #1 + +pci:v00008086d00003A20sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 4 port SATA IDE Controller #1 (PowerEdge R410 SATA IDE Controller) + +pci:v00008086d00003A20sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 4 port SATA IDE Controller #1 (PowerEdge T410 SATA IDE Controller) + +pci:v00008086d00003A22* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SATA AHCI Controller + +pci:v00008086d00003A22sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SATA AHCI Controller (ProLiant G6 series) + +pci:v00008086d00003A22sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SATA AHCI Controller (P5Q Deluxe Motherboard) + +pci:v00008086d00003A22sv00001458sd0000B005* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SATA AHCI Controller (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00003A25* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) SATA RAID Controller + +pci:v00008086d00003A25sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) SATA RAID Controller (PERC S100 Controller (PE R410)) + +pci:v00008086d00003A25sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) SATA RAID Controller (PERC S100 Controller (PE T410)) + +pci:v00008086d00003A25sv00001028sd000002F1* + ID_MODEL_FROM_DATABASE=82801JIR (ICH10R) SATA RAID Controller (PERC S100 Controller (PE R510)) + +pci:v00008086d00003A26* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 2 port SATA IDE Controller #2 + +pci:v00008086d00003A26sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 2 port SATA IDE Controller #2 (PowerEdge R410 SATA IDE Controller) + +pci:v00008086d00003A26sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) 2 port SATA IDE Controller #2 (PowerEdge T410 SATA IDE Controller) + +pci:v00008086d00003A30* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SMBus Controller + +pci:v00008086d00003A30sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SMBus Controller (P5Q Deluxe Motherboard) + +pci:v00008086d00003A30sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) SMBus Controller (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00003A32* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) Thermal Subsystem + +pci:v00008086d00003A34* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 + +pci:v00008086d00003A34sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A34sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A34sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 (ProLiant G6 series) + +pci:v00008086d00003A34sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A34sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #1 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A35* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 + +pci:v00008086d00003A35sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A35sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A35sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 (ProLiant G6 series) + +pci:v00008086d00003A35sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A35sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #2 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A36* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 + +pci:v00008086d00003A36sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A36sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A36sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 (ProLiant G6 series) + +pci:v00008086d00003A36sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A36sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #3 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A37* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 + +pci:v00008086d00003A37sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A37sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A37sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 (ProLiant G6 series) + +pci:v00008086d00003A37sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A37sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #4 (Motherboard) + +pci:v00008086d00003A38* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 + +pci:v00008086d00003A38sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A38sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A38sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 (ProLiant ML150 G6 Server) + +pci:v00008086d00003A38sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A38sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #5 (Motherboard) + +pci:v00008086d00003A39* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 + +pci:v00008086d00003A39sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 (PowerEdge R410 USB UHCI Controller) + +pci:v00008086d00003A39sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 (PowerEdge T410 USB UHCI Controller) + +pci:v00008086d00003A39sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 (ProLiant ML150 G6 Server) + +pci:v00008086d00003A39sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A39sv00001458sd00005004* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB UHCI Controller #6 (Motherboard) + +pci:v00008086d00003A3A* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 + +pci:v00008086d00003A3Asv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 (PowerEdge R410 USB EHCI Controller) + +pci:v00008086d00003A3Asv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 (PowerEdge T410 USB EHCI Controller) + +pci:v00008086d00003A3Asv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 (ProLiant G6 series) + +pci:v00008086d00003A3Asv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A3Asv00001458sd00005006* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #1 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A3C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 + +pci:v00008086d00003A3Csv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 (PowerEdge R410 USB EHCI Controller) + +pci:v00008086d00003A3Csv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 (PowerEdge T410 USB EHCI Controller) + +pci:v00008086d00003A3Csv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 (ProLiant G6 series) + +pci:v00008086d00003A3Csv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A3Csv00001458sd00005006* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) USB2 EHCI Controller #2 (Motherboard) + +pci:v00008086d00003A3E* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) HD Audio Controller + +pci:v00008086d00003A3Esv00001043sd00008311* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) HD Audio Controller (P5Q Deluxe Motherboard) + +pci:v00008086d00003A3Esv00001458sd0000A002* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) HD Audio Controller (GA-EP45-UD3R Motherboard) + +pci:v00008086d00003A3Esv00001458sd0000A102* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) HD Audio Controller (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00003A40* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 + +pci:v00008086d00003A40sv00001028sd0000028C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (PowerEdge R410 PCI Express Port 1) + +pci:v00008086d00003A40sv00001028sd0000028D* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (PowerEdge T410 PCI Express Port 1) + +pci:v00008086d00003A40sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (ProLiant ML150 G6 Server) + +pci:v00008086d00003A40sv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A40sv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (P6T DeLuxe Motherboard) + +pci:v00008086d00003A40sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 1 (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00003A42* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Port 2 + +pci:v00008086d00003A44* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 3 + +pci:v00008086d00003A44sv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 3 (P6T DeLuxe Motherboard) + +pci:v00008086d00003A46* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 4 + +pci:v00008086d00003A46sv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 4 (P6T DeLuxe Motherboard) + +pci:v00008086d00003A46sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 4 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A48* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 5 + +pci:v00008086d00003A48sv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 5 (ProLiant ML150 G6 Server) + +pci:v00008086d00003A48sv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 5 (P6T Deluxe Motherboard) + +pci:v00008086d00003A48sv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 5 (GA-EP45-DS5 Motherboard) + +pci:v00008086d00003A4A* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 6 + +pci:v00008086d00003A4Asv0000103Csd0000330B* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 6 (ProLiant ML150 G6 Server) + +pci:v00008086d00003A4Asv00001043sd000082D4* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 6 (P5Q Deluxe Motherboard) + +pci:v00008086d00003A4Asv00001043sd000082EA* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 6 (P6T DeLuxe Motherboard) + +pci:v00008086d00003A4Asv00001458sd00005001* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) PCI Express Root Port 6 (GA-EP45-DS5/GA-EG45M-DS2H Motherboard) + +pci:v00008086d00003A4C* + ID_MODEL_FROM_DATABASE=82801JI (ICH10 Family) Gigabit Ethernet Controller + +pci:v00008086d00003A51* + ID_MODEL_FROM_DATABASE=82801JDO (ICH10DO) VECI Controller + +pci:v00008086d00003A55* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) Virtual SATA Controller + +pci:v00008086d00003A60* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) SMBus Controller + +pci:v00008086d00003A62* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) Thermal Subsystem + +pci:v00008086d00003A64* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #1 + +pci:v00008086d00003A65* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #2 + +pci:v00008086d00003A66* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #3 + +pci:v00008086d00003A67* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #4 + +pci:v00008086d00003A68* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #5 + +pci:v00008086d00003A69* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB UHCI Controller #6 + +pci:v00008086d00003A6A* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB2 EHCI Controller #1 + +pci:v00008086d00003A6C* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) USB2 EHCI Controller #2 + +pci:v00008086d00003A6E* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) HD Audio Controller + +pci:v00008086d00003A70* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 1 + +pci:v00008086d00003A72* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 2 + +pci:v00008086d00003A74* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 3 + +pci:v00008086d00003A76* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 4 + +pci:v00008086d00003A78* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 5 + +pci:v00008086d00003A7A* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) PCI Express Port 6 + +pci:v00008086d00003A7C* + ID_MODEL_FROM_DATABASE=82801JD/DO (ICH10 Family) Gigabit Ethernet Controller + +pci:v00008086d00003B00* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B01* + ID_MODEL_FROM_DATABASE=Mobile 5 Series Chipset LPC Interface Controller + +pci:v00008086d00003B02* + ID_MODEL_FROM_DATABASE=P55 Chipset LPC Interface Controller + +pci:v00008086d00003B03* + ID_MODEL_FROM_DATABASE=PM55 Chipset LPC Interface Controller + +pci:v00008086d00003B04* + ID_MODEL_FROM_DATABASE=5 Series Chipset LPC Interface Controller + +pci:v00008086d00003B05* + ID_MODEL_FROM_DATABASE=Mobile 5 Series Chipset LPC Interface Controller + +pci:v00008086d00003B06* + ID_MODEL_FROM_DATABASE=H55 Chipset LPC Interface Controller + +pci:v00008086d00003B07* + ID_MODEL_FROM_DATABASE=QM57 Chipset LPC Interface Controller + +pci:v00008086d00003B07sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=QM57 Chipset LPC Interface Controller (Latitude E6410) + +pci:v00008086d00003B07sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=QM57 Chipset LPC Interface Controller (Latitude E6510) + +pci:v00008086d00003B07sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=QM57 Chipset LPC Interface Controller (PC1-GROOVE) + +pci:v00008086d00003B08* + ID_MODEL_FROM_DATABASE=H57 Chipset LPC Interface Controller + +pci:v00008086d00003B09* + ID_MODEL_FROM_DATABASE=HM55 Chipset LPC Interface Controller + +pci:v00008086d00003B09sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=HM55 Chipset LPC Interface Controller (Aspire 7740G) + +pci:v00008086d00003B09sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=HM55 Chipset LPC Interface Controller (R730 Laptop) + +pci:v00008086d00003B09sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=HM55 Chipset LPC Interface Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B0A* + ID_MODEL_FROM_DATABASE=Q57 Chipset LPC Interface Controller + +pci:v00008086d00003B0Asv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=Q57 Chipset LPC Interface Controller (OptiPlex 980) + +pci:v00008086d00003B0Asv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=Q57 Chipset LPC Interface Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B0B* + ID_MODEL_FROM_DATABASE=HM57 Chipset LPC Interface Controller + +pci:v00008086d00003B0C* + ID_MODEL_FROM_DATABASE=5 Series Chipset LPC Interface Controller + +pci:v00008086d00003B0D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B0E* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B0F* + ID_MODEL_FROM_DATABASE=QS57 Chipset LPC Interface Controller + +pci:v00008086d00003B10* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B11* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B12* + ID_MODEL_FROM_DATABASE=3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B13* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B14* + ID_MODEL_FROM_DATABASE=3420 Chipset LPC Interface Controller + +pci:v00008086d00003B15* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B16* + ID_MODEL_FROM_DATABASE=3450 Chipset LPC Interface Controller + +pci:v00008086d00003B17* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B18* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B19* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1C* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1E* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B1F* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LPC Interface Controller + +pci:v00008086d00003B20* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller + +pci:v00008086d00003B21* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller + +pci:v00008086d00003B22* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller + +pci:v00008086d00003B22sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (OptiPlex 980) + +pci:v00008086d00003B22sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B23* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA AHCI Controller + +pci:v00008086d00003B25* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SATA RAID Controller + +pci:v00008086d00003B25sv0000103Csd00003118* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SATA RAID Controller (Smart Array B110i SATA RAID Controller) + +pci:v00008086d00003B26* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller + +pci:v00008086d00003B28* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller + +pci:v00008086d00003B28sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller (R730 Laptop) + +pci:v00008086d00003B28sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B29* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA AHCI Controller + +pci:v00008086d00003B29sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA AHCI Controller (Aspire 7740G) + +pci:v00008086d00003B29sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA AHCI Controller (R730 Laptop) + +pci:v00008086d00003B29sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA AHCI Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B2C* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SATA RAID Controller + +pci:v00008086d00003B2D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller + +pci:v00008086d00003B2Dsv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller (R730 Laptop) + +pci:v00008086d00003B2Dsv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B2Dsv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 2 port SATA IDE Controller (PC1-GROOVE) + +pci:v00008086d00003B2E* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller + +pci:v00008086d00003B2Esv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 4 port SATA IDE Controller (PC1-GROOVE) + +pci:v00008086d00003B2F* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller + +pci:v00008086d00003B2Fsv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (Latitude E6410) + +pci:v00008086d00003B2Fsv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (Latitude E6510) + +pci:v00008086d00003B2Fsv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset 6 port SATA AHCI Controller (PC1-GROOVE) + +pci:v00008086d00003B30* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller + +pci:v00008086d00003B30sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (Aspire 7740G) + +pci:v00008086d00003B30sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (OptiPlex 980) + +pci:v00008086d00003B30sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (Latitude E6410) + +pci:v00008086d00003B30sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (Latitude E6510) + +pci:v00008086d00003B30sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (R730 Laptop) + +pci:v00008086d00003B30sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B30sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B30sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset SMBus Controller (PC1-GROOVE) + +pci:v00008086d00003B32* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset Thermal Subsystem + +pci:v00008086d00003B32sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset Thermal Subsystem (Aspire 7740G) + +pci:v00008086d00003B32sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset Thermal Subsystem (Latitude E6410) + +pci:v00008086d00003B32sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset Thermal Subsystem (R730 Laptop) + +pci:v00008086d00003B32sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset Thermal Subsystem (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B34* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller + +pci:v00008086d00003B34sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Aspire 7740G) + +pci:v00008086d00003B34sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (OptiPlex 980) + +pci:v00008086d00003B34sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Latitude E6410) + +pci:v00008086d00003B34sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Latitude E6510) + +pci:v00008086d00003B34sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (R730 Laptop) + +pci:v00008086d00003B34sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B34sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B34sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (PC1-GROOVE) + +pci:v00008086d00003B36* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B37* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B38* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B39* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B3A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B3B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B3C* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller + +pci:v00008086d00003B3Csv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Aspire 7740G) + +pci:v00008086d00003B3Csv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (OptiPlex 980) + +pci:v00008086d00003B3Csv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Latitude E6410) + +pci:v00008086d00003B3Csv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Latitude E6510) + +pci:v00008086d00003B3Csv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (R730 Laptop) + +pci:v00008086d00003B3Csv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B3Csv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B3Csv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB2 Enhanced Host Controller (PC1-GROOVE) + +pci:v00008086d00003B3E* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B3F* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B40* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset USB Universal Host Controller + +pci:v00008086d00003B41* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset LAN Controller + +pci:v00008086d00003B42* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 + +pci:v00008086d00003B42sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (OptiPlex 980) + +pci:v00008086d00003B42sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (Latitude E6410) + +pci:v00008086d00003B42sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (Latitude E6510) + +pci:v00008086d00003B42sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (R730 Laptop) + +pci:v00008086d00003B42sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (C7SIM-Q Motherboard) + +pci:v00008086d00003B42sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B44* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 2 + +pci:v00008086d00003B44sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 2 (Latitude E6410) + +pci:v00008086d00003B44sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 2 (Latitude E6510) + +pci:v00008086d00003B44sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 2 (C7SIM-Q Motherboard) + +pci:v00008086d00003B44sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 2 (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B46* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 3 + +pci:v00008086d00003B46sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 3 (Latitude E6410) + +pci:v00008086d00003B46sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 3 (Latitude E6510) + +pci:v00008086d00003B46sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 3 (R730 Laptop) + +pci:v00008086d00003B46sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 3 (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B48* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 4 + +pci:v00008086d00003B48sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 4 (Latitude E6410) + +pci:v00008086d00003B48sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 4 (Latitude E6510) + +pci:v00008086d00003B48sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 4 (R730 Laptop) + +pci:v00008086d00003B4A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 5 + +pci:v00008086d00003B4Asv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 5 (OptiPlex 980) + +pci:v00008086d00003B4Asv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 5 (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B4C* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 6 + +pci:v00008086d00003B4E* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 7 + +pci:v00008086d00003B50* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 8 + +pci:v00008086d00003B53* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset VECI Controller + +pci:v00008086d00003B56* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio + +pci:v00008086d00003B56sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (Aspire 7740G) + +pci:v00008086d00003B56sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (OptiPlex 980) + +pci:v00008086d00003B56sv00001028sd0000040A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (Latitude E6410) + +pci:v00008086d00003B56sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (Latitude E6510) + +pci:v00008086d00003B56sv00001043sd00001373* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (ASUSTek G73-series gaming laptop) + +pci:v00008086d00003B56sv0000144Dsd0000C06A* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (R730 Laptop) + +pci:v00008086d00003B56sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (C7SIM-Q Motherboard) + +pci:v00008086d00003B56sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B56sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio (PC1-GROOVE) + +pci:v00008086d00003B57* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset High Definition Audio + +pci:v00008086d00003B64* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller + +pci:v00008086d00003B64sv00001025sd00000347* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller (Aspire 7740G) + +pci:v00008086d00003B64sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller (C7SIM-Q Motherboard) + +pci:v00008086d00003B64sv000017C0sd000010D2* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller (Medion Akoya E7214 Notebook PC [MD98410]) + +pci:v00008086d00003B64sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller (PC1-GROOVE) + +pci:v00008086d00003B65* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset HECI Controller + +pci:v00008086d00003B66* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PT IDER Controller + +pci:v00008086d00003B67* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset KT Controller + +pci:v00008086d00003B67sv0000E4BFsd000050C1* + ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset KT Controller (PC1-GROOVE) + +pci:v00008086d00003C00* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMI2 + +pci:v00008086d00003C01* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMI2 in PCI Express Mode + +pci:v00008086d00003C02* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 1a + +pci:v00008086d00003C03* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 1b + +pci:v00008086d00003C04* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 2a + +pci:v00008086d00003C05* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 2b + +pci:v00008086d00003C06* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 2c + +pci:v00008086d00003C07* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 2d + +pci:v00008086d00003C08* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 3a in PCI Express Mode + +pci:v00008086d00003C09* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 3b + +pci:v00008086d00003C0A* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 3c + +pci:v00008086d00003C0B* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO PCI Express Root Port 3d + +pci:v00008086d00003C0D* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Non-Transparent Bridge + +pci:v00008086d00003C0E* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Non-Transparent Bridge + +pci:v00008086d00003C0F* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Non-Transparent Bridge + +pci:v00008086d00003C20* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 0 + +pci:v00008086d00003C21* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 1 + +pci:v00008086d00003C22* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 2 + +pci:v00008086d00003C23* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 3 + +pci:v00008086d00003C24* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 4 + +pci:v00008086d00003C25* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 5 + +pci:v00008086d00003C26* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 6 + +pci:v00008086d00003C27* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA Channel 7 + +pci:v00008086d00003C28* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Address Map, VTd_Misc, System Management + +pci:v00008086d00003C2A* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Control Status and Global Errors + +pci:v00008086d00003C2C* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 I/O APIC + +pci:v00008086d00003C2E* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA + +pci:v00008086d00003C2F* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DMA + +pci:v00008086d00003C40* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 IIO Switch and IRP Performance Monitor + +pci:v00008086d00003C43* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Ring to PCI Express Performance Monitor + +pci:v00008086d00003C44* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Ring to QuickPath Interconnect Link 0 Performance Monitor + +pci:v00008086d00003C45* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Ring to QuickPath Interconnect Link 1 Performance Monitor + +pci:v00008086d00003C46* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Processor Home Agent Performance Monitoring + +pci:v00008086d00003C71* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller RAS Registers + +pci:v00008086d00003C80* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link 0 + +pci:v00008086d00003C83* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link Reut 0 + +pci:v00008086d00003C84* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link Reut 0 + +pci:v00008086d00003C90* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link 1 + +pci:v00008086d00003C93* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link Reut 1 + +pci:v00008086d00003C94* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QPI Link Reut 1 + +pci:v00008086d00003CA0* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Processor Home Agent + +pci:v00008086d00003CA8* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Registers + +pci:v00008086d00003CAA* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Target Address Decoder 0 + +pci:v00008086d00003CAB* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Target Address Decoder 1 + +pci:v00008086d00003CAC* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Target Address Decoder 2 + +pci:v00008086d00003CAD* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Target Address Decoder 3 + +pci:v00008086d00003CAE* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Target Address Decoder 4 + +pci:v00008086d00003CB0* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Channel 0-3 Thermal Control 0 + +pci:v00008086d00003CB1* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Channel 0-3 Thermal Control 1 + +pci:v00008086d00003CB2* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller ERROR Registers 0 + +pci:v00008086d00003CB3* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller ERROR Registers 1 + +pci:v00008086d00003CB4* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Channel 0-3 Thermal Control 2 + +pci:v00008086d00003CB5* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller Channel 0-3 Thermal Control 3 + +pci:v00008086d00003CB6* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller ERROR Registers 2 + +pci:v00008086d00003CB7* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller ERROR Registers 3 + +pci:v00008086d00003CB8* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 DDRIO + +pci:v00008086d00003CC0* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Power Control Unit 0 + +pci:v00008086d00003CC1* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Power Control Unit 1 + +pci:v00008086d00003CC2* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Power Control Unit 2 + +pci:v00008086d00003CD0* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Power Control Unit 3 + +pci:v00008086d00003CE0* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Interrupt Control Registers + +pci:v00008086d00003CE3* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Semaphore and Scratchpad Configuration Registers + +pci:v00008086d00003CE4* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 R2PCIe + +pci:v00008086d00003CE6* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 QuickPath Interconnect Agent Ring Registers + +pci:v00008086d00003CE8* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 0 + +pci:v00008086d00003CE9* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 5 + +pci:v00008086d00003CEA* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 1 + +pci:v00008086d00003CEB* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 6 + +pci:v00008086d00003CEC* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 3 + +pci:v00008086d00003CED* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 7 + +pci:v00008086d00003CEE* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 4 + +pci:v00008086d00003CEF* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Unicast Register 8 + +pci:v00008086d00003CF4* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller System Address Decoder 0 + +pci:v00008086d00003CF5* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 Integrated Memory Controller System Address Decoder 1 + +pci:v00008086d00003CF6* + ID_MODEL_FROM_DATABASE=Xeon E5/Core i7 System Address Decoder + +pci:v00008086d00004000* + ID_MODEL_FROM_DATABASE=5400 Chipset Memory Controller Hub + +pci:v00008086d00004001* + ID_MODEL_FROM_DATABASE=5400 Chipset Memory Controller Hub + +pci:v00008086d00004003* + ID_MODEL_FROM_DATABASE=5400 Chipset Memory Controller Hub + +pci:v00008086d00004021* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 1 + +pci:v00008086d00004022* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 2 + +pci:v00008086d00004023* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 3 + +pci:v00008086d00004024* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 4 + +pci:v00008086d00004025* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 5 + +pci:v00008086d00004026* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 6 + +pci:v00008086d00004027* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 7 + +pci:v00008086d00004028* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 8 + +pci:v00008086d00004029* + ID_MODEL_FROM_DATABASE=5400 Chipset PCI Express Port 9 + +pci:v00008086d0000402D* + ID_MODEL_FROM_DATABASE=5400 Chipset IBIST Registers + +pci:v00008086d0000402E* + ID_MODEL_FROM_DATABASE=5400 Chipset IBIST Registers + +pci:v00008086d0000402F* + ID_MODEL_FROM_DATABASE=5400 Chipset QuickData Technology Device + +pci:v00008086d00004030* + ID_MODEL_FROM_DATABASE=5400 Chipset FSB Registers + +pci:v00008086d00004031* + ID_MODEL_FROM_DATABASE=5400 Chipset CE/SF Registers + +pci:v00008086d00004032* + ID_MODEL_FROM_DATABASE=5400 Chipset IOxAPIC + +pci:v00008086d00004035* + ID_MODEL_FROM_DATABASE=5400 Chipset FBD Registers + +pci:v00008086d00004036* + ID_MODEL_FROM_DATABASE=5400 Chipset FBD Registers + +pci:v00008086d00004100* + ID_MODEL_FROM_DATABASE=Moorestown Graphics and Video + +pci:v00008086d00004108* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d00004109* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410A* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410B* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410C* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410D* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410E* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d0000410F* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d00004114* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Host Bridge #1 + +pci:v00008086d00004115* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Host Bridge #2 + +pci:v00008086d00004116* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Host Bridge #3 + +pci:v00008086d00004117* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Host Bridge #4 + +pci:v00008086d00004220* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection + +pci:v00008086d00004220sv0000103Csd00000934* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (Compaq nw8240/nx8220) + +pci:v00008086d00004220sv0000103Csd000012F6* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (nc6120/nx8220/nw8240) + +pci:v00008086d00004220sv00008086sd00002701* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (WM3B2200BG Mini-PCI Card) + +pci:v00008086d00004220sv00008086sd00002712* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (IBM ThinkPad R50e) + +pci:v00008086d00004220sv00008086sd00002721* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (Dell B130 laptop integrated WLAN) + +pci:v00008086d00004220sv00008086sd00002722* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (Dell Latitude D600) + +pci:v00008086d00004220sv00008086sd00002731* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2200BG [Calexico2] Network Connection (Samsung P35 integrated WLAN) + +pci:v00008086d00004222* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection + +pci:v00008086d00004222sv0000103Csd0000135C* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection + +pci:v00008086d00004222sv00008086sd00001000* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945ABG Network Connection) + +pci:v00008086d00004222sv00008086sd00001001* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (WM3945ABG MOW2) + +pci:v00008086d00004222sv00008086sd00001005* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945BG Network Connection) + +pci:v00008086d00004222sv00008086sd00001034* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945BG Network Connection) + +pci:v00008086d00004222sv00008086sd00001044* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945BG Network Connection) + +pci:v00008086d00004222sv00008086sd00001C00* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945ABG Network Connection) + +pci:v00008086d00004223* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection + +pci:v00008086d00004223sv00001000sd00008086* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection (mPCI 3B Americas/Europe ZZA) + +pci:v00008086d00004223sv00001001sd00008086* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection (mPCI 3B Europe ZZE) + +pci:v00008086d00004223sv00001002sd00008086* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection (mPCI 3B Japan ZZJ) + +pci:v00008086d00004223sv00001003sd00008086* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection (mPCI 3B High-Band ZZH) + +pci:v00008086d00004223sv00001351sd0000103C* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection (Compaq NC6220) + +pci:v00008086d00004224* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2915ABG [Calexico2] Network Connection + +pci:v00008086d00004227* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection + +pci:v00008086d00004227sv00008086sd00001011* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (ThinkPad T60/R60e/X60s) + +pci:v00008086d00004227sv00008086sd00001014* + ID_MODEL_FROM_DATABASE=PRO/Wireless 3945ABG [Golan] Network Connection (PRO/Wireless 3945BG Network Connection) + +pci:v00008086d00004229* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection + +pci:v00008086d00004229sv00008086sd00001100* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (Vaio VGN-SZ79SN_C) + +pci:v00008086d00004229sv00008086sd00001101* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (PRO/Wireless 4965 AG or AGN) + +pci:v00008086d0000422B* + ID_MODEL_FROM_DATABASE=Centrino Ultimate-N 6300 + +pci:v00008086d0000422Bsv00008086sd00001101* + ID_MODEL_FROM_DATABASE=Centrino Ultimate-N 6300 (3x3 AGN) + +pci:v00008086d0000422Bsv00008086sd00001121* + ID_MODEL_FROM_DATABASE=Centrino Ultimate-N 6300 (3x3 AGN) + +pci:v00008086d0000422C* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 + +pci:v00008086d0000422Csv00008086sd00001301* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 AGN) + +pci:v00008086d0000422Csv00008086sd00001306* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 ABG) + +pci:v00008086d0000422Csv00008086sd00001307* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 BG) + +pci:v00008086d0000422Csv00008086sd00001321* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 AGN) + +pci:v00008086d0000422Csv00008086sd00001326* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 ABG) + +pci:v00008086d00004230* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection + +pci:v00008086d00004230sv00008086sd00001110* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (Lenovo ThinkPad T51) + +pci:v00008086d00004230sv00008086sd00001111* + ID_MODEL_FROM_DATABASE=PRO/Wireless 4965 AG or AGN [Kedron] Network Connection (Lenovo ThinkPad T61) + +pci:v00008086d00004232* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 + +pci:v00008086d00004232sv00008086sd00001201* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001204* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001205* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (BGN) + +pci:v00008086d00004232sv00008086sd00001206* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (ABG) + +pci:v00008086d00004232sv00008086sd00001221* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001224* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001225* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (BGN) + +pci:v00008086d00004232sv00008086sd00001226* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (ABG) + +pci:v00008086d00004232sv00008086sd00001301* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001304* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001305* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (BGN) + +pci:v00008086d00004232sv00008086sd00001306* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (ABG) + +pci:v00008086d00004232sv00008086sd00001321* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001324* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (AGN) + +pci:v00008086d00004232sv00008086sd00001325* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (BGN) + +pci:v00008086d00004232sv00008086sd00001326* + ID_MODEL_FROM_DATABASE=WiFi Link 5100 (ABG) + +pci:v00008086d00004235* + ID_MODEL_FROM_DATABASE=Ultimate N WiFi Link 5300 + +pci:v00008086d00004236* + ID_MODEL_FROM_DATABASE=Ultimate N WiFi Link 5300 + +pci:v00008086d00004237* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection + +pci:v00008086d00004237sv00008086sd00001211* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 AGN) + +pci:v00008086d00004237sv00008086sd00001214* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 AGN) + +pci:v00008086d00004237sv00008086sd00001215* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 BGN) + +pci:v00008086d00004237sv00008086sd00001216* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 ABG) + +pci:v00008086d00004237sv00008086sd00001311* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 AGN) + +pci:v00008086d00004237sv00008086sd00001314* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 AGN) + +pci:v00008086d00004237sv00008086sd00001315* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 BGN) + +pci:v00008086d00004237sv00008086sd00001316* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5100 AGN [Shiloh] Network Connection (WiFi Link 5100 ABG) + +pci:v00008086d00004238* + ID_MODEL_FROM_DATABASE=Centrino Ultimate-N 6300 + +pci:v00008086d00004238sv00008086sd00001111* + ID_MODEL_FROM_DATABASE=Centrino Ultimate-N 6300 (3x3 AGN) + +pci:v00008086d00004239* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 + +pci:v00008086d00004239sv00008086sd00001311* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 AGN) + +pci:v00008086d00004239sv00008086sd00001316* + ID_MODEL_FROM_DATABASE=Centrino Advanced-N 6200 (2x2 ABG) + +pci:v00008086d0000423A* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5350 AGN [Echo Peak] Network Connection + +pci:v00008086d0000423B* + ID_MODEL_FROM_DATABASE=PRO/Wireless 5350 AGN [Echo Peak] Network Connection + +pci:v00008086d0000423C* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 + +pci:v00008086d0000423Csv00008086sd00001201* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423Csv00008086sd00001206* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (ABG) + +pci:v00008086d0000423Csv00008086sd00001221* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423Csv00008086sd00001301* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423Csv00008086sd00001306* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (ABG) + +pci:v00008086d0000423Csv00008086sd00001321* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423D* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 + +pci:v00008086d0000423Dsv00008086sd00001211* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423Dsv00008086sd00001216* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (ABG) + +pci:v00008086d0000423Dsv00008086sd00001311* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (AGN) + +pci:v00008086d0000423Dsv00008086sd00001316* + ID_MODEL_FROM_DATABASE=WiMAX/WiFi Link 5150 (ABG) + +pci:v00008086d0000444E* + ID_MODEL_FROM_DATABASE=Turbo Memory Controller + +pci:v00008086d00005001* + ID_MODEL_FROM_DATABASE=LE80578 + +pci:v00008086d00005002* + ID_MODEL_FROM_DATABASE=LE80578 Graphics Processor Unit + +pci:v00008086d00005009* + ID_MODEL_FROM_DATABASE=LE80578 Video Display Controller + +pci:v00008086d0000500D* + ID_MODEL_FROM_DATABASE=LE80578 Expansion Bus + +pci:v00008086d0000500E* + ID_MODEL_FROM_DATABASE=LE80578 UART Controller + +pci:v00008086d0000500F* + ID_MODEL_FROM_DATABASE=LE80578 General Purpose IO + +pci:v00008086d00005010* + ID_MODEL_FROM_DATABASE=LE80578 I2C Controller + +pci:v00008086d00005012* + ID_MODEL_FROM_DATABASE=LE80578 Serial Peripheral Interface Bus + +pci:v00008086d00005020* + ID_MODEL_FROM_DATABASE=EP80579 Memory Controller Hub + +pci:v00008086d00005021* + ID_MODEL_FROM_DATABASE=EP80579 DRAM Error Reporting Registers + +pci:v00008086d00005023* + ID_MODEL_FROM_DATABASE=EP80579 EDMA Controller + +pci:v00008086d00005024* + ID_MODEL_FROM_DATABASE=EP80579 PCI Express Port PEA0 + +pci:v00008086d00005025* + ID_MODEL_FROM_DATABASE=EP80579 PCI Express Port PEA1 + +pci:v00008086d00005028* + ID_MODEL_FROM_DATABASE=EP80579 S-ATA IDE + +pci:v00008086d00005029* + ID_MODEL_FROM_DATABASE=EP80579 S-ATA AHCI + +pci:v00008086d0000502A* + ID_MODEL_FROM_DATABASE=EP80579 S-ATA Reserved + +pci:v00008086d0000502B* + ID_MODEL_FROM_DATABASE=EP80579 S-ATA Reserved + +pci:v00008086d0000502C* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor ASU + +pci:v00008086d0000502D* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor with QuickAssist ASU + +pci:v00008086d0000502E* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d0000502F* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005030* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005031* + ID_MODEL_FROM_DATABASE=EP80579 LPC Bus + +pci:v00008086d00005032* + ID_MODEL_FROM_DATABASE=EP80579 SMBus Controller + +pci:v00008086d00005033* + ID_MODEL_FROM_DATABASE=EP80579 USB 1.1 Controller + +pci:v00008086d00005035* + ID_MODEL_FROM_DATABASE=EP80579 USB 2.0 Controller + +pci:v00008086d00005037* + ID_MODEL_FROM_DATABASE=EP80579 PCI-PCI Bridge (transparent mode) + +pci:v00008086d00005039* + ID_MODEL_FROM_DATABASE=EP80579 Controller Area Network (CAN) interface #1 + +pci:v00008086d0000503A* + ID_MODEL_FROM_DATABASE=EP80579 Controller Area Network (CAN) interface #2 + +pci:v00008086d0000503B* + ID_MODEL_FROM_DATABASE=EP80579 Synchronous Serial Port (SPP) + +pci:v00008086d0000503C* + ID_MODEL_FROM_DATABASE=EP80579 IEEE 1588 Hardware Assist + +pci:v00008086d0000503D* + ID_MODEL_FROM_DATABASE=EP80579 Local Expansion Bus + +pci:v00008086d0000503E* + ID_MODEL_FROM_DATABASE=EP80579 Global Control Unit (GCU) + +pci:v00008086d0000503F* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005040* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor Gigabit Ethernet MAC + +pci:v00008086d00005041* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor with QuickAssist Gigabit Ethernet MAC + +pci:v00008086d00005042* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005043* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005044* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor Gigabit Ethernet MAC + +pci:v00008086d00005045* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor with QuickAssist Gigabit Ethernet MAC + +pci:v00008086d00005046* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005047* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d00005048* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor Gigabit Ethernet MAC + +pci:v00008086d00005049* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor with QuickAssist Gigabit Ethernet MAC + +pci:v00008086d0000504A* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d0000504B* + ID_MODEL_FROM_DATABASE=EP80579 Reserved + +pci:v00008086d0000504C* + ID_MODEL_FROM_DATABASE=EP80579 Integrated Processor with QuickAssist TDM + +pci:v00008086d00005200* + ID_MODEL_FROM_DATABASE=EtherExpress PRO/100 Intelligent Server PCI Bridge + +pci:v00008086d00005201* + ID_MODEL_FROM_DATABASE=EtherExpress PRO/100 Intelligent Server Fast Ethernet Controller + +pci:v00008086d00005201sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=EtherExpress PRO/100 Intelligent Server Fast Ethernet Controller (EtherExpress PRO/100 Server Ethernet Adapter) + +pci:v00008086d0000530D* + ID_MODEL_FROM_DATABASE=80310 (IOP) IO Processor + +pci:v00008086d00005845* + ID_MODEL_FROM_DATABASE=QEMU NVM Express Controller + +pci:v00008086d00005845sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=QEMU NVM Express Controller (QEMU Virtual Machine) + +pci:v00008086d00005A84* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Integrated Graphics Controller + +pci:v00008086d00005A88* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Imaging Unit + +pci:v00008086d00005A98* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Audio Cluster + +pci:v00008086d00005A9A* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Trusted Execution Engine + +pci:v00008086d00005AA2* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Integrated Sensor Hub + +pci:v00008086d00005AA8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series USB xHCI + +pci:v00008086d00005AAC* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #1 + +pci:v00008086d00005AAE* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #2 + +pci:v00008086d00005AB0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #3 + +pci:v00008086d00005AB2* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #4 + +pci:v00008086d00005AB4* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #5 + +pci:v00008086d00005AB6* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #6 + +pci:v00008086d00005AB8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #7 + +pci:v00008086d00005ABA* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series I2C Controller #8 + +pci:v00008086d00005ABC* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series HSUART Controller #1 + +pci:v00008086d00005ABE* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series HSUART Controller #2 + +pci:v00008086d00005AC0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series HSUART Controller #3 + +pci:v00008086d00005AC2* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SPI Controller #1 + +pci:v00008086d00005AC4* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SPI Controller #2 + +pci:v00008086d00005AC6* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SPI Controller #3 + +pci:v00008086d00005AC8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PWM Pin Controller + +pci:v00008086d00005ACA* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SDXC/MMC Host Controller + +pci:v00008086d00005ACC* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series eMMC Controller + +pci:v00008086d00005AD0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SDIO Controller + +pci:v00008086d00005AD4* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SMBus Controller + +pci:v00008086d00005AD6* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port B #1 + +pci:v00008086d00005AD7* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port B #2 + +pci:v00008086d00005AD8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port A #1 + +pci:v00008086d00005AD9* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port A #2 + +pci:v00008086d00005ADA* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port A #3 + +pci:v00008086d00005ADB* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series PCI Express Port A #4 + +pci:v00008086d00005AE3* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series SATA AHCI Controller + +pci:v00008086d00005AE8* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Low Pin Count Interface + +pci:v00008086d00005AEE* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series HSUART Controller #4 + +pci:v00008086d00005AF0* + ID_MODEL_FROM_DATABASE=Atom/Celeron/Pentium Processor N4200/N3350/E3900 Series Host Bridge + +pci:v00008086d000065C0* + ID_MODEL_FROM_DATABASE=5100 Chipset Memory Controller Hub + +pci:v00008086d000065E2* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 2 + +pci:v00008086d000065E3* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 3 + +pci:v00008086d000065E4* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 4 + +pci:v00008086d000065E5* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 5 + +pci:v00008086d000065E6* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 6 + +pci:v00008086d000065E7* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x4 Port 7 + +pci:v00008086d000065F0* + ID_MODEL_FROM_DATABASE=5100 Chipset FSB Registers + +pci:v00008086d000065F0sv00001028sd0000020F* + ID_MODEL_FROM_DATABASE=5100 Chipset FSB Registers (PowerEdge R300) + +pci:v00008086d000065F0sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=5100 Chipset FSB Registers (PowerEdge T300) + +pci:v00008086d000065F1* + ID_MODEL_FROM_DATABASE=5100 Chipset Reserved Registers + +pci:v00008086d000065F1sv00001028sd00000210* + ID_MODEL_FROM_DATABASE=5100 Chipset Reserved Registers (PowerEdge T300) + +pci:v00008086d000065F3* + ID_MODEL_FROM_DATABASE=5100 Chipset Reserved Registers + +pci:v00008086d000065F5* + ID_MODEL_FROM_DATABASE=5100 Chipset DDR Channel 0 Registers + +pci:v00008086d000065F6* + ID_MODEL_FROM_DATABASE=5100 Chipset DDR Channel 1 Registers + +pci:v00008086d000065F7* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x8 Port 2-3 + +pci:v00008086d000065F8* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x8 Port 4-5 + +pci:v00008086d000065F9* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x8 Port 6-7 + +pci:v00008086d000065FA* + ID_MODEL_FROM_DATABASE=5100 Chipset PCI Express x16 Port 4-7 + +pci:v00008086d000065FF* + ID_MODEL_FROM_DATABASE=5100 Chipset DMA Engine + +pci:v00008086d00006F00* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2 + +pci:v00008086d00006F01* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 0 + +pci:v00008086d00006F02* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1 + +pci:v00008086d00006F03* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 1 + +pci:v00008086d00006F04* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 2 + +pci:v00008086d00006F05* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 2 + +pci:v00008086d00006F06* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 2 + +pci:v00008086d00006F07* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 2 + +pci:v00008086d00006F08* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 3 + +pci:v00008086d00006F09* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 3 + +pci:v00008086d00006F0A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 3 + +pci:v00008086d00006F0B* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D PCI Express Root Port 3 + +pci:v00008086d00006F10* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F11* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F12* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F13* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F14* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F15* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F16* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F17* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F18* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F19* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F1A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F1B* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F1C* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Debug + +pci:v00008086d00006F1D* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R2PCIe Agent + +pci:v00008086d00006F1E* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Ubox + +pci:v00008086d00006F1F* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Ubox + +pci:v00008086d00006F20* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 0 + +pci:v00008086d00006F21* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 1 + +pci:v00008086d00006F22* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 2 + +pci:v00008086d00006F23* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 3 + +pci:v00008086d00006F24* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 4 + +pci:v00008086d00006F25* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 5 + +pci:v00008086d00006F26* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 6 + +pci:v00008086d00006F27* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Crystal Beach DMA Channel 7 + +pci:v00008086d00006F28* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management + +pci:v00008086d00006F29* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug + +pci:v00008086d00006F2A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors + +pci:v00008086d00006F2C* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC + +pci:v00008086d00006F30* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0 + +pci:v00008086d00006F32* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 + +pci:v00008086d00006F33* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1 + +pci:v00008086d00006F34* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R2PCIe Agent + +pci:v00008086d00006F36* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 0/1 + +pci:v00008086d00006F37* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 0/1 + +pci:v00008086d00006F38* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 1 + +pci:v00008086d00006F39* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IO Performance Monitoring + +pci:v00008086d00006F3A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 + +pci:v00008086d00006F3E* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 2 + +pci:v00008086d00006F3F* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 2 + +pci:v00008086d00006F40* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 + +pci:v00008086d00006F41* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 2 + +pci:v00008086d00006F43* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 + +pci:v00008086d00006F45* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 Debug + +pci:v00008086d00006F46* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 Debug + +pci:v00008086d00006F47* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 2 Debug + +pci:v00008086d00006F50* + ID_MODEL_FROM_DATABASE=Xeon Processor D Family QuickData Technology Register DMA Channel 0 + +pci:v00008086d00006F51* + ID_MODEL_FROM_DATABASE=Xeon Processor D Family QuickData Technology Register DMA Channel 1 + +pci:v00008086d00006F52* + ID_MODEL_FROM_DATABASE=Xeon Processor D Family QuickData Technology Register DMA Channel 2 + +pci:v00008086d00006F53* + ID_MODEL_FROM_DATABASE=Xeon Processor D Family QuickData Technology Register DMA Channel 3 + +pci:v00008086d00006F60* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 1 + +pci:v00008086d00006F68* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Target Address/Thermal/RAS + +pci:v00008086d00006F6A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Channel Target Address Decoder + +pci:v00008086d00006F6B* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Channel Target Address Decoder + +pci:v00008086d00006F6C* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Channel Target Address Decoder + +pci:v00008086d00006F6D* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Channel Target Address Decoder + +pci:v00008086d00006F6E* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 2/3 Broadcast + +pci:v00008086d00006F6F* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Global Broadcast + +pci:v00008086d00006F70* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0 Debug + +pci:v00008086d00006F71* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Target Address/Thermal/RAS + +pci:v00008086d00006F76* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link Debug + +pci:v00008086d00006F78* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 1 Debug + +pci:v00008086d00006F79* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Target Address/Thermal/RAS + +pci:v00008086d00006F7D* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Ubox + +pci:v00008086d00006F7E* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link Debug + +pci:v00008086d00006F80* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 + +pci:v00008086d00006F81* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D R3 QPI Link 0/1 + +pci:v00008086d00006F83* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 + +pci:v00008086d00006F85* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 Debug + +pci:v00008086d00006F86* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 Debug + +pci:v00008086d00006F87* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 0 Debug + +pci:v00008086d00006F88* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006F8A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006F90* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1 + +pci:v00008086d00006F93* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1 + +pci:v00008086d00006F95* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1 Debug + +pci:v00008086d00006F96* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D QPI Link 1 Debug + +pci:v00008086d00006F98* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006F99* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006F9A* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006F9C* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FA0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0 + +pci:v00008086d00006FA8* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Target Address/Thermal/RAS + +pci:v00008086d00006FAA* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder + +pci:v00008086d00006FAB* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder + +pci:v00008086d00006FAC* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder + +pci:v00008086d00006FAD* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder + +pci:v00008086d00006FAE* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 0/1 Broadcast + +pci:v00008086d00006FAF* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Global Broadcast + +pci:v00008086d00006FB0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 0 Thermal Control + +pci:v00008086d00006FB1* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 1 Thermal Control + +pci:v00008086d00006FB2* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 0 Error + +pci:v00008086d00006FB3* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 1 Error + +pci:v00008086d00006FB4* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 2 Thermal Control + +pci:v00008086d00006FB5* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 3 Thermal Control + +pci:v00008086d00006FB6* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 2 Error + +pci:v00008086d00006FB7* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel 3 Error + +pci:v00008086d00006FB8* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 2/3 Interface + +pci:v00008086d00006FB9* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 2/3 Interface + +pci:v00008086d00006FBA* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 2/3 Interface + +pci:v00008086d00006FBB* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 2/3 Interface + +pci:v00008086d00006FBC* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 0/1 Interface + +pci:v00008086d00006FBD* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 0/1 Interface + +pci:v00008086d00006FBE* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 0/1 Interface + +pci:v00008086d00006FBF* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DDRIO Channel 0/1 Interface + +pci:v00008086d00006FC0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC1* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC2* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC3* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC4* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC5* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC6* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC7* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC8* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FC9* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCA* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCB* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCC* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCD* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCE* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FCF* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Power Control Unit + +pci:v00008086d00006FD0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 0 Thermal Control + +pci:v00008086d00006FD1* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 1 Thermal Control + +pci:v00008086d00006FD2* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 0 Error + +pci:v00008086d00006FD3* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 1 Error + +pci:v00008086d00006FD4* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 2 Thermal Control + +pci:v00008086d00006FD5* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 3 Thermal Control + +pci:v00008086d00006FD6* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 2 Error + +pci:v00008086d00006FD7* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 1 - Channel 3 Error + +pci:v00008086d00006FE0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE1* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE2* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE3* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE4* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE5* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE6* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE7* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE8* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FE9* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FEA* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FEB* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FEC* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FED* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FEE* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FEF* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FF0* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FF1* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FF8* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FF9* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FFA* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FFB* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FFC* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FFD* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00006FFE* + ID_MODEL_FROM_DATABASE=Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Caching Agent + +pci:v00008086d00007000* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 ISA [Natoma/Triton II] + +pci:v00008086d00007000sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 ISA [Natoma/Triton II] (Qemu virtual machine) + +pci:v00008086d00007010* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 IDE [Natoma/Triton II] + +pci:v00008086d00007010sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 IDE [Natoma/Triton II] (Qemu virtual machine) + +pci:v00008086d00007020* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 USB [Natoma/Triton II] + +pci:v00008086d00007020sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82371SB PIIX3 USB [Natoma/Triton II] (QEMU Virtual Machine) + +pci:v00008086d00007030* + ID_MODEL_FROM_DATABASE=430VX - 82437VX TVX [Triton VX] + +pci:v00008086d00007050* + ID_MODEL_FROM_DATABASE=Intercast Video Capture Card + +pci:v00008086d00007051* + ID_MODEL_FROM_DATABASE=PB 642365-003 (Business Video Conferencing Card) + +pci:v00008086d00007100* + ID_MODEL_FROM_DATABASE=430TX - 82439TX MTXC + +pci:v00008086d00007110* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 ISA + +pci:v00008086d00007110sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 ISA (Virtual Machine Chipset) + +pci:v00008086d00007111* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 IDE + +pci:v00008086d00007111sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 IDE (Virtual Machine Chipset) + +pci:v00008086d00007112* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 USB + +pci:v00008086d00007112sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 USB (Virtual Machine Chipset) + +pci:v00008086d00007112sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 USB (QEMU Virtual Machine) + +pci:v00008086d00007113* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 ACPI + +pci:v00008086d00007113sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 ACPI (Virtual Machine Chipset) + +pci:v00008086d00007113sv00001AF4sd00001100* + ID_MODEL_FROM_DATABASE=82371AB/EB/MB PIIX4 ACPI (Qemu virtual machine) + +pci:v00008086d00007120* + ID_MODEL_FROM_DATABASE=82810 GMCH (Graphics Memory Controller Hub) + +pci:v00008086d00007120sv00004C53sd00001040* + ID_MODEL_FROM_DATABASE=82810 GMCH (Graphics Memory Controller Hub) (CL7 mainboard) + +pci:v00008086d00007120sv00004C53sd00001060* + ID_MODEL_FROM_DATABASE=82810 GMCH (Graphics Memory Controller Hub) (PC7 mainboard) + +pci:v00008086d00007121* + ID_MODEL_FROM_DATABASE=82810 (CGC) Chipset Graphics Controller + +pci:v00008086d00007121sv00004C53sd00001040* + ID_MODEL_FROM_DATABASE=82810 (CGC) Chipset Graphics Controller (CL7 mainboard) + +pci:v00008086d00007121sv00004C53sd00001060* + ID_MODEL_FROM_DATABASE=82810 (CGC) Chipset Graphics Controller (PC7 mainboard) + +pci:v00008086d00007121sv00008086sd00004341* + ID_MODEL_FROM_DATABASE=82810 (CGC) Chipset Graphics Controller (Cayman (CA810) Mainboard) + +pci:v00008086d00007122* + ID_MODEL_FROM_DATABASE=82810 DC-100 (GMCH) Graphics Memory Controller Hub + +pci:v00008086d00007123* + ID_MODEL_FROM_DATABASE=82810 DC-100 (CGC) Chipset Graphics Controller + +pci:v00008086d00007124* + ID_MODEL_FROM_DATABASE=82810E DC-133 (GMCH) Graphics Memory Controller Hub + +pci:v00008086d00007124sv00001028sd000000B4* + ID_MODEL_FROM_DATABASE=82810E DC-133 (GMCH) Graphics Memory Controller Hub (OptiPlex GX110) + +pci:v00008086d00007125* + ID_MODEL_FROM_DATABASE=82810E DC-133 (CGC) Chipset Graphics Controller + +pci:v00008086d00007125sv00001028sd000000B4* + ID_MODEL_FROM_DATABASE=82810E DC-133 (CGC) Chipset Graphics Controller (OptiPlex GX110) + +pci:v00008086d00007126* + ID_MODEL_FROM_DATABASE=82810 DC-133 System and Graphics Controller + +pci:v00008086d00007128* + ID_MODEL_FROM_DATABASE=82810-M DC-100 System and Graphics Controller + +pci:v00008086d0000712A* + ID_MODEL_FROM_DATABASE=82810-M DC-133 System and Graphics Controller + +pci:v00008086d00007180* + ID_MODEL_FROM_DATABASE=440LX/EX - 82443LX/EX Host bridge + +pci:v00008086d00007181* + ID_MODEL_FROM_DATABASE=440LX/EX - 82443LX/EX AGP bridge + +pci:v00008086d00007190* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge + +pci:v00008086d00007190sv00000E11sd00000500* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (Armada 1750 Laptop System Chipset) + +pci:v00008086d00007190sv00000E11sd0000B110* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (Armada M700/E500) + +pci:v00008086d00007190sv00001028sd0000008E* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (PowerEdge 1300 mainboard) + +pci:v00008086d00007190sv00001043sd0000803B* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (CUBX-L/E Mainboard) + +pci:v00008086d00007190sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (Toshiba Tecra 8100 Laptop System Chipset) + +pci:v00008086d00007190sv000015ADsd00001976* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (Virtual Machine Chipset) + +pci:v00008086d00007190sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (CT7 mainboard) + +pci:v00008086d00007190sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (CE7 mainboard) + +pci:v00008086d00007191* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX AGP bridge + +pci:v00008086d00007191sv00001028sd0000008E* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (PowerEdge 1300 mainboard) + +pci:v00008086d00007192* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) + +pci:v00008086d00007192sv00000E11sd00000460* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (Armada 1700 Laptop System Chipset) + +pci:v00008086d00007192sv00001179sd00000001* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (Satellite 4010) + +pci:v00008086d00007192sv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v00008086d00007192sv00008086sd00007190* + ID_MODEL_FROM_DATABASE=440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) (Dell PowerEdge 350) + +pci:v00008086d00007194* + ID_MODEL_FROM_DATABASE=82440MX Host Bridge + +pci:v00008086d00007194sv00001033sd00000000* + ID_MODEL_FROM_DATABASE=82440MX Host Bridge (Versa Note Vxi) + +pci:v00008086d00007194sv00004C53sd000010A0* + ID_MODEL_FROM_DATABASE=82440MX Host Bridge (CA3/CR3 mainboard) + +pci:v00008086d00007195* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Audio Controller + +pci:v00008086d00007195sv00001033sd000080CC* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Audio Controller (Versa Note VXi) + +pci:v00008086d00007195sv000010CFsd00001099* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Audio Controller (QSound_SigmaTel Stac97 PCI Audio) + +pci:v00008086d00007195sv000011D4sd00000040* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00007195sv000011D4sd00000048* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Audio Controller (SoundMAX Integrated Digital Audio) + +pci:v00008086d00007196* + ID_MODEL_FROM_DATABASE=82440MX AC'97 Modem Controller + +pci:v00008086d00007198* + ID_MODEL_FROM_DATABASE=82440MX ISA Bridge + +pci:v00008086d00007199* + ID_MODEL_FROM_DATABASE=82440MX EIDE Controller + +pci:v00008086d0000719A* + ID_MODEL_FROM_DATABASE=82440MX USB Universal Host Controller + +pci:v00008086d0000719B* + ID_MODEL_FROM_DATABASE=82440MX Power Management Controller + +pci:v00008086d000071A0* + ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge + +pci:v00008086d000071A0sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge (CT7 mainboard) + +pci:v00008086d000071A0sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge (CE7 mainboard) + +pci:v00008086d000071A1* + ID_MODEL_FROM_DATABASE=440GX - 82443GX AGP bridge + +pci:v00008086d000071A2* + ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge (AGP disabled) + +pci:v00008086d000071A2sv00004C53sd00001000* + ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge (AGP disabled) (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) + +pci:v00008086d00007600* + ID_MODEL_FROM_DATABASE=82372FB PIIX5 ISA + +pci:v00008086d00007601* + ID_MODEL_FROM_DATABASE=82372FB PIIX5 IDE + +pci:v00008086d00007602* + ID_MODEL_FROM_DATABASE=82372FB PIIX5 USB + +pci:v00008086d00007603* + ID_MODEL_FROM_DATABASE=82372FB PIIX5 SMBus + +pci:v00008086d00007800* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator + +pci:v00008086d00007800sv0000003Dsd00000008* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Starfighter AGP) + +pci:v00008086d00007800sv0000003Dsd0000000B* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Starfighter AGP) + +pci:v00008086d00007800sv00001092sd00000100* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Stealth II G460) + +pci:v00008086d00007800sv000010B4sd0000201A* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Lightspeed 740) + +pci:v00008086d00007800sv000010B4sd0000202F* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Lightspeed 740) + +pci:v00008086d00007800sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Terminator 2x/i) + +pci:v00008086d00007800sv00008086sd00000100* + ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Intel740 Graphics Accelerator) + +pci:v00008086d00008002* + ID_MODEL_FROM_DATABASE=Trusted Execution Technology Registers + +pci:v00008086d00008003* + ID_MODEL_FROM_DATABASE=Trusted Execution Technology Registers + +pci:v00008086d00008100* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) + +pci:v00008086d00008108* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) Graphics Controller + +pci:v00008086d00008110* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) PCI Express Port 1 + +pci:v00008086d00008112* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) PCI Express Port 2 + +pci:v00008086d00008114* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #1 + +pci:v00008086d00008115* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #2 + +pci:v00008086d00008116* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB UHCI #3 + +pci:v00008086d00008117* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB EHCI #1 + +pci:v00008086d00008118* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) USB Client Controller + +pci:v00008086d00008119* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) LPC Bridge + +pci:v00008086d0000811A* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) IDE Controller + +pci:v00008086d0000811B* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) HD Audio Controller + +pci:v00008086d0000811C* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #1 + +pci:v00008086d0000811D* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #2 + +pci:v00008086d0000811E* + ID_MODEL_FROM_DATABASE=System Controller Hub (SCH Poulsbo) SDIO Controller #3 + +pci:v00008086d00008180* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Express Port 3 + +pci:v00008086d00008181* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Express Port 4 + +pci:v00008086d00008182* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Integrated Graphics Controller + +pci:v00008086d00008183* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx Configuration Unit + +pci:v00008086d00008184* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Express Port 1 + +pci:v00008086d00008185* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx PCI Express Port 2 + +pci:v00008086d00008186* + ID_MODEL_FROM_DATABASE=Atom Processor E6xx LPC Bridge + +pci:v00008086d000084C4* + ID_MODEL_FROM_DATABASE=450KX/GX [Orion] - 82454KX/GX PCI bridge + +pci:v00008086d000084C5* + ID_MODEL_FROM_DATABASE=450KX/GX [Orion] - 82453KX/GX Memory controller + +pci:v00008086d000084CA* + ID_MODEL_FROM_DATABASE=450NX - 82451NX Memory & I/O Controller + +pci:v00008086d000084CB* + ID_MODEL_FROM_DATABASE=450NX - 82454NX/84460GX PCI Expander Bridge + +pci:v00008086d000084E0* + ID_MODEL_FROM_DATABASE=460GX - 84460GX System Address Controller (SAC) + +pci:v00008086d000084E1* + ID_MODEL_FROM_DATABASE=460GX - 84460GX System Data Controller (SDC) + +pci:v00008086d000084E2* + ID_MODEL_FROM_DATABASE=460GX - 84460GX AGP Bridge (GXB function 2) + +pci:v00008086d000084E3* + ID_MODEL_FROM_DATABASE=460GX - 84460GX Memory Address Controller (MAC) + +pci:v00008086d000084E4* + ID_MODEL_FROM_DATABASE=460GX - 84460GX Memory Data Controller (MDC) + +pci:v00008086d000084E6* + ID_MODEL_FROM_DATABASE=460GX - 82466GX Wide and fast PCI eXpander Bridge (WXB) + +pci:v00008086d000084EA* + ID_MODEL_FROM_DATABASE=460GX - 84460GX AGP Bridge (GXB function 1) + +pci:v00008086d00008500* + ID_MODEL_FROM_DATABASE=IXP4XX Network Processor (IXP420/421/422/425/IXC1100) + +pci:v00008086d00008500sv00001993sd00000DED* + ID_MODEL_FROM_DATABASE=IXP4XX Network Processor (IXP420/421/422/425/IXC1100) (mGuard-PCI AV#2) + +pci:v00008086d00008500sv00001993sd00000DEE* + ID_MODEL_FROM_DATABASE=IXP4XX Network Processor (IXP420/421/422/425/IXC1100) (mGuard-PCI AV#1) + +pci:v00008086d00008500sv00001993sd00000DEF* + ID_MODEL_FROM_DATABASE=IXP4XX Network Processor (IXP420/421/422/425/IXC1100) (mGuard-PCI AV#0) + +pci:v00008086d00008800* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T PCI Express Port + +pci:v00008086d00008801* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T Packet Hub + +pci:v00008086d00008802* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T Gigabit Ethernet Controller + +pci:v00008086d00008803* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T General Purpose IO Controller + +pci:v00008086d00008804* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #4 + +pci:v00008086d00008805* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #5 + +pci:v00008086d00008806* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #6 + +pci:v00008086d00008807* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB2 EHCI Controller #2 + +pci:v00008086d00008808* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB Client Controller + +pci:v00008086d00008809* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T SDIO Controller #1 + +pci:v00008086d0000880A* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T SDIO Controller #2 + +pci:v00008086d0000880B* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T SATA AHCI Controller + +pci:v00008086d0000880C* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #1 + +pci:v00008086d0000880D* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #2 + +pci:v00008086d0000880E* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB OHCI Controller #3 + +pci:v00008086d0000880F* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T USB2 EHCI Controller #1 + +pci:v00008086d00008810* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T DMA Controller #1 + +pci:v00008086d00008811* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T UART Controller 0 + +pci:v00008086d00008812* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T UART Controller 1 + +pci:v00008086d00008813* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T UART Controller 2 + +pci:v00008086d00008814* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T UART Controller 3 + +pci:v00008086d00008815* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T DMA Controller #2 + +pci:v00008086d00008816* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T Serial Peripheral Interface Bus + +pci:v00008086d00008817* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T I2C Controller + +pci:v00008086d00008818* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T Controller Area Network (CAN) Controller + +pci:v00008086d00008819* + ID_MODEL_FROM_DATABASE=Platform Controller Hub EG20T IEEE 1588 Hardware Assist + +pci:v00008086d00008C00* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 4-port SATA Controller 1 [IDE mode] + +pci:v00008086d00008C01* + ID_MODEL_FROM_DATABASE=8 Series Chipset Family 4-port SATA Controller 1 [IDE mode] - Mobile + +pci:v00008086d00008C02* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] + +pci:v00008086d00008C03* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] + +pci:v00008086d00008C03sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] (ZBook 15) + +pci:v00008086d00008C03sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 6-port SATA Controller 1 [AHCI mode] (ThinkPad T440p) + +pci:v00008086d00008C04* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C05* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C06* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C07* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C08* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 2-port SATA Controller 2 [IDE mode] + +pci:v00008086d00008C09* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family 2-port SATA Controller 2 [IDE mode] + +pci:v00008086d00008C0E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C0F* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SATA Controller 1 [RAID mode] + +pci:v00008086d00008C10* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #1 + +pci:v00008086d00008C10sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #1 (ThinkPad T440p) + +pci:v00008086d00008C11* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #1 + +pci:v00008086d00008C12* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #2 + +pci:v00008086d00008C12sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #2 (ThinkPad T440p) + +pci:v00008086d00008C13* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #2 + +pci:v00008086d00008C14* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #3 + +pci:v00008086d00008C15* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #3 + +pci:v00008086d00008C16* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #4 + +pci:v00008086d00008C17* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #4 + +pci:v00008086d00008C18* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #5 + +pci:v00008086d00008C19* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #5 + +pci:v00008086d00008C1A* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #6 + +pci:v00008086d00008C1B* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #6 + +pci:v00008086d00008C1C* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #7 + +pci:v00008086d00008C1D* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #7 + +pci:v00008086d00008C1E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #8 + +pci:v00008086d00008C1F* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family PCI Express Root Port #8 + +pci:v00008086d00008C20* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller + +pci:v00008086d00008C20sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller (ZBook 15) + +pci:v00008086d00008C20sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller (ThinkPad T440p) + +pci:v00008086d00008C21* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset High Definition Audio Controller + +pci:v00008086d00008C22* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SMBus Controller + +pci:v00008086d00008C22sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SMBus Controller (ZBook 15) + +pci:v00008086d00008C22sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family SMBus Controller (ThinkPad T440p) + +pci:v00008086d00008C23* + ID_MODEL_FROM_DATABASE=8 Series Chipset Family CHAP Counters + +pci:v00008086d00008C24* + ID_MODEL_FROM_DATABASE=8 Series Chipset Family Thermal Management Controller + +pci:v00008086d00008C26* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 + +pci:v00008086d00008C26sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 (ZBook 15) + +pci:v00008086d00008C26sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #1 (ThinkPad T440p) + +pci:v00008086d00008C2D* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #2 + +pci:v00008086d00008C2Dsv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #2 (ZBook 15) + +pci:v00008086d00008C2Dsv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB EHCI #2 (ThinkPad T440p) + +pci:v00008086d00008C31* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI + +pci:v00008086d00008C31sv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI (ZBook 15) + +pci:v00008086d00008C31sv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family USB xHCI (ThinkPad T440p) + +pci:v00008086d00008C33* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LAN Controller + +pci:v00008086d00008C34* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family NAND Controller + +pci:v00008086d00008C3A* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #1 + +pci:v00008086d00008C3Asv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #1 (ZBook 15) + +pci:v00008086d00008C3Asv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #1 (ThinkPad T440p) + +pci:v00008086d00008C3B* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family MEI Controller #2 + +pci:v00008086d00008C3C* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family IDE-r Controller + +pci:v00008086d00008C3D* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family KT Controller + +pci:v00008086d00008C40* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C41* + ID_MODEL_FROM_DATABASE=8 Series Chipset Family Mobile Super SKU LPC Controller + +pci:v00008086d00008C42* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family Desktop Super SKU LPC Controller + +pci:v00008086d00008C43* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C44* + ID_MODEL_FROM_DATABASE=Z87 Express LPC Controller + +pci:v00008086d00008C45* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C46* + ID_MODEL_FROM_DATABASE=Z85 Express LPC Controller + +pci:v00008086d00008C47* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C48* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C49* + ID_MODEL_FROM_DATABASE=HM86 Express LPC Controller + +pci:v00008086d00008C4A* + ID_MODEL_FROM_DATABASE=H87 Express LPC Controller + +pci:v00008086d00008C4B* + ID_MODEL_FROM_DATABASE=HM87 Express LPC Controller + +pci:v00008086d00008C4C* + ID_MODEL_FROM_DATABASE=Q85 Express LPC Controller + +pci:v00008086d00008C4D* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C4E* + ID_MODEL_FROM_DATABASE=Q87 Express LPC Controller + +pci:v00008086d00008C4F* + ID_MODEL_FROM_DATABASE=QM87 Express LPC Controller + +pci:v00008086d00008C4Fsv0000103Csd00001909* + ID_MODEL_FROM_DATABASE=QM87 Express LPC Controller (ZBook 15) + +pci:v00008086d00008C4Fsv000017AAsd0000220E* + ID_MODEL_FROM_DATABASE=QM87 Express LPC Controller (ThinkPad T440p) + +pci:v00008086d00008C50* + ID_MODEL_FROM_DATABASE=B85 Express LPC Controller + +pci:v00008086d00008C51* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C52* + ID_MODEL_FROM_DATABASE=C222 Series Chipset Family Server Essential SKU LPC Controller + +pci:v00008086d00008C53* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C54* + ID_MODEL_FROM_DATABASE=C224 Series Chipset Family Server Standard SKU LPC Controller + +pci:v00008086d00008C55* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C56* + ID_MODEL_FROM_DATABASE=C226 Series Chipset Family Server Advanced SKU LPC Controller + +pci:v00008086d00008C57* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C58* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family WS SKU LPC Controller + +pci:v00008086d00008C59* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C5A* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C5B* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C5C* + ID_MODEL_FROM_DATABASE=C220 Series Chipset Family H81 Express LPC Controller + +pci:v00008086d00008C5D* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C5E* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C5F* + ID_MODEL_FROM_DATABASE=8 Series/C220 Series Chipset Family LPC Controller + +pci:v00008086d00008C80* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [IDE Mode] + +pci:v00008086d00008C81* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [IDE Mode] + +pci:v00008086d00008C82* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [AHCI Mode] + +pci:v00008086d00008C83* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [AHCI Mode] + +pci:v00008086d00008C84* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C85* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C86* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C87* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C88* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [IDE Mode] + +pci:v00008086d00008C89* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [IDE Mode] + +pci:v00008086d00008C8E* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C8F* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SATA Controller [RAID Mode] + +pci:v00008086d00008C90* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 1 + +pci:v00008086d00008C92* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 2 + +pci:v00008086d00008C94* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 3 + +pci:v00008086d00008C96* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 4 + +pci:v00008086d00008C98* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 5 + +pci:v00008086d00008C9A* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 6 + +pci:v00008086d00008C9C* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 7 + +pci:v00008086d00008C9E* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family PCI Express Root Port 8 + +pci:v00008086d00008CA0* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family HD Audio Controller + +pci:v00008086d00008CA2* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family SMBus Controller + +pci:v00008086d00008CA4* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family Thermal Controller + +pci:v00008086d00008CA6* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family USB EHCI Controller #1 + +pci:v00008086d00008CAD* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family USB EHCI Controller #2 + +pci:v00008086d00008CB1* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family USB xHCI Controller + +pci:v00008086d00008CB3* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family LAN Controller + +pci:v00008086d00008CBA* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family ME Interface #1 + +pci:v00008086d00008CBB* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family ME Interface #2 + +pci:v00008086d00008CBC* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family IDE-R Controller + +pci:v00008086d00008CBD* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family KT Controller + +pci:v00008086d00008CC1* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family LPC Controller + +pci:v00008086d00008CC2* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family LPC Controller + +pci:v00008086d00008CC3* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family HM97 LPC Controller + +pci:v00008086d00008CC4* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family Z97 LPC Controller + +pci:v00008086d00008CC6* + ID_MODEL_FROM_DATABASE=9 Series Chipset Family H97 Controller + +pci:v00008086d00008D00* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset 4-port SATA Controller [IDE mode] + +pci:v00008086d00008D02* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset 6-Port SATA Controller [AHCI mode] + +pci:v00008086d00008D04* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SATA Controller [RAID mode] + +pci:v00008086d00008D06* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SATA Controller [RAID mode] + +pci:v00008086d00008D06sv000017AAsd00001031* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SATA Controller [RAID mode] (ThinkServer RAID 110i) + +pci:v00008086d00008D08* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset 2-port SATA Controller [IDE mode] + +pci:v00008086d00008D0E* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SATA Controller [RAID mode] + +pci:v00008086d00008D10* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #1 + +pci:v00008086d00008D11* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #1 + +pci:v00008086d00008D12* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #2 + +pci:v00008086d00008D13* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #2 + +pci:v00008086d00008D14* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #3 + +pci:v00008086d00008D15* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #3 + +pci:v00008086d00008D16* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #4 + +pci:v00008086d00008D17* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #4 + +pci:v00008086d00008D18* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #5 + +pci:v00008086d00008D19* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #5 + +pci:v00008086d00008D1A* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #6 + +pci:v00008086d00008D1B* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #6 + +pci:v00008086d00008D1C* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #7 + +pci:v00008086d00008D1D* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #7 + +pci:v00008086d00008D1E* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #8 + +pci:v00008086d00008D1F* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset PCI Express Root Port #8 + +pci:v00008086d00008D20* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset HD Audio Controller + +pci:v00008086d00008D21* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset HD Audio Controller + +pci:v00008086d00008D22* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SMBus Controller + +pci:v00008086d00008D24* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset Thermal Subsystem + +pci:v00008086d00008D26* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #1 + +pci:v00008086d00008D2D* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB Enhanced Host Controller #2 + +pci:v00008086d00008D31* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset USB xHCI Host Controller + +pci:v00008086d00008D33* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LAN Controller + +pci:v00008086d00008D34* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset NAND Controller + +pci:v00008086d00008D3A* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #1 + +pci:v00008086d00008D3B* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset MEI Controller #2 + +pci:v00008086d00008D3C* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset IDE-r Controller + +pci:v00008086d00008D3D* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset KT Controller + +pci:v00008086d00008D40* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D41* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D42* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D43* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D44* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D45* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D46* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D47* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D48* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D49* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4A* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4B* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4C* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4D* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4E* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D4F* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset LPC Controller + +pci:v00008086d00008D60* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [IDE mode] + +pci:v00008086d00008D62* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [AHCI mode] + +pci:v00008086d00008D64* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [RAID mode] + +pci:v00008086d00008D66* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [RAID mode] + +pci:v00008086d00008D68* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [IDE mode] + +pci:v00008086d00008D6E* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset sSATA Controller [RAID mode] + +pci:v00008086d00008D7C* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset SPSR + +pci:v00008086d00008D7D* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset MS SMBus 0 + +pci:v00008086d00008D7E* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset MS SMBus 1 + +pci:v00008086d00008D7F* + ID_MODEL_FROM_DATABASE=C610/X99 series chipset MS SMBus 2 + +pci:v00008086d00009000* + ID_MODEL_FROM_DATABASE=IXP2000 Family Network Processor + +pci:v00008086d00009001* + ID_MODEL_FROM_DATABASE=IXP2400 Network Processor + +pci:v00008086d00009002* + ID_MODEL_FROM_DATABASE=IXP2300 Network Processor + +pci:v00008086d00009004* + ID_MODEL_FROM_DATABASE=IXP2800 Network Processor + +pci:v00008086d00009621* + ID_MODEL_FROM_DATABASE=Integrated RAID + +pci:v00008086d00009622* + ID_MODEL_FROM_DATABASE=Integrated RAID + +pci:v00008086d00009641* + ID_MODEL_FROM_DATABASE=Integrated RAID + +pci:v00008086d000096A1* + ID_MODEL_FROM_DATABASE=Integrated RAID + +pci:v00008086d00009C00* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [IDE mode] + +pci:v00008086d00009C01* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [IDE mode] + +pci:v00008086d00009C02* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [AHCI mode] + +pci:v00008086d00009C03* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [AHCI mode] + +pci:v00008086d00009C03sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [AHCI mode] (ThinkPad X240) + +pci:v00008086d00009C04* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C05* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C06* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C07* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C08* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 2 [IDE mode] + +pci:v00008086d00009C09* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 2 [IDE mode] + +pci:v00008086d00009C0A* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller [Reserved] + +pci:v00008086d00009C0B* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller [Reserved] + +pci:v00008086d00009C0C* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller [Reserved] + +pci:v00008086d00009C0D* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller [Reserved] + +pci:v00008086d00009C0E* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C0F* + ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [RAID mode] + +pci:v00008086d00009C10* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 1 + +pci:v00008086d00009C11* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 1 + +pci:v00008086d00009C12* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 2 + +pci:v00008086d00009C13* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 2 + +pci:v00008086d00009C14* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 3 + +pci:v00008086d00009C15* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 3 + +pci:v00008086d00009C16* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 4 + +pci:v00008086d00009C17* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 4 + +pci:v00008086d00009C18* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 5 + +pci:v00008086d00009C19* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 5 + +pci:v00008086d00009C1A* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 6 + +pci:v00008086d00009C1B* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 6 + +pci:v00008086d00009C1C* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 7 + +pci:v00008086d00009C1D* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 7 + +pci:v00008086d00009C1E* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 8 + +pci:v00008086d00009C1F* + ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 8 + +pci:v00008086d00009C20* + ID_MODEL_FROM_DATABASE=8 Series HD Audio Controller + +pci:v00008086d00009C20sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series HD Audio Controller (ThinkPad X240) + +pci:v00008086d00009C21* + ID_MODEL_FROM_DATABASE=8 Series HD Audio Controller + +pci:v00008086d00009C22* + ID_MODEL_FROM_DATABASE=8 Series SMBus Controller + +pci:v00008086d00009C22sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series SMBus Controller (ThinkPad X240) + +pci:v00008086d00009C23* + ID_MODEL_FROM_DATABASE=8 Series CHAP Counters + +pci:v00008086d00009C24* + ID_MODEL_FROM_DATABASE=8 Series Thermal + +pci:v00008086d00009C26* + ID_MODEL_FROM_DATABASE=8 Series USB EHCI #1 + +pci:v00008086d00009C26sv000017AAsd0000220C* + ID_MODEL_FROM_DATABASE=8 Series USB EHCI #1 (T440s) + +pci:v00008086d00009C26sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series USB EHCI #1 (ThinkPad X240) + +pci:v00008086d00009C2D* + ID_MODEL_FROM_DATABASE=8 Series USB EHCI #2 + +pci:v00008086d00009C31* + ID_MODEL_FROM_DATABASE=8 Series USB xHCI HC + +pci:v00008086d00009C31sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series USB xHCI HC (ThinkPad X240) + +pci:v00008086d00009C31sv00008086sd00007270* + ID_MODEL_FROM_DATABASE=8 Series USB xHCI HC (Apple MacBookAir6,2 / MacBookPro11,1) + +pci:v00008086d00009C35* + ID_MODEL_FROM_DATABASE=8 Series SDIO Controller + +pci:v00008086d00009C36* + ID_MODEL_FROM_DATABASE=8 Series Audio DSP Controller + +pci:v00008086d00009C3A* + ID_MODEL_FROM_DATABASE=8 Series HECI #0 + +pci:v00008086d00009C3Asv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series HECI #0 (ThinkPad X240) + +pci:v00008086d00009C3B* + ID_MODEL_FROM_DATABASE=8 Series HECI #1 + +pci:v00008086d00009C3C* + ID_MODEL_FROM_DATABASE=8 Series HECI IDER + +pci:v00008086d00009C3D* + ID_MODEL_FROM_DATABASE=8 Series HECI KT + +pci:v00008086d00009C40* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C41* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C42* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C43* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C43sv000017AAsd00002214* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller (ThinkPad X240) + +pci:v00008086d00009C44* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C45* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C46* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C47* + ID_MODEL_FROM_DATABASE=8 Series LPC Controller + +pci:v00008086d00009C60* + ID_MODEL_FROM_DATABASE=8 Series Low Power Sub-System DMA + +pci:v00008086d00009C61* + ID_MODEL_FROM_DATABASE=8 Series I2C Controller #0 + +pci:v00008086d00009C62* + ID_MODEL_FROM_DATABASE=8 Series I2C Controller #1 + +pci:v00008086d00009C63* + ID_MODEL_FROM_DATABASE=8 Series UART Controller #0 + +pci:v00008086d00009C64* + ID_MODEL_FROM_DATABASE=8 Series UART Controller #1 + +pci:v00008086d00009C65* + ID_MODEL_FROM_DATABASE=8 Series SPI Controller #0 + +pci:v00008086d00009C66* + ID_MODEL_FROM_DATABASE=8 Series SPI Controller #1 + +pci:v00008086d00009C83* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP SATA Controller [AHCI Mode] + +pci:v00008086d00009C85* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP SATA Controller [RAID Mode] + +pci:v00008086d00009C87* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP SATA Controller [RAID Mode] + +pci:v00008086d00009C8F* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP SATA Controller [RAID Mode] + +pci:v00008086d00009C90* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #1 + +pci:v00008086d00009C92* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #2 + +pci:v00008086d00009C94* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #3 + +pci:v00008086d00009C96* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #4 + +pci:v00008086d00009C98* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #5 + +pci:v00008086d00009C9A* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP PCI Express Root Port #6 + +pci:v00008086d00009CA0* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP High Definition Audio Controller + +pci:v00008086d00009CA2* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP SMBus Controller + +pci:v00008086d00009CA4* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Thermal Management Controller + +pci:v00008086d00009CA6* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP USB EHCI Controller + +pci:v00008086d00009CB1* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP USB xHCI Controller + +pci:v00008086d00009CB5* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Secure Digital IO Controller + +pci:v00008086d00009CB6* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Smart Sound Technology Controller + +pci:v00008086d00009CBA* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP MEI Controller #1 + +pci:v00008086d00009CBB* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP MEI Controller #2 + +pci:v00008086d00009CBC* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP IDE-r Controller + +pci:v00008086d00009CBD* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP KT Controller + +pci:v00008086d00009CC1* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC2* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC3* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC5* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC6* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC7* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CC9* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP LPC Controller + +pci:v00008086d00009CE0* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO DMA Controller + +pci:v00008086d00009CE1* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO I2C Controller #0 + +pci:v00008086d00009CE2* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO I2C Controller #1 + +pci:v00008086d00009CE3* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO UART Controller #0 + +pci:v00008086d00009CE4* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO UART Controller #1 + +pci:v00008086d00009CE5* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO GSPI Controller #0 + +pci:v00008086d00009CE6* + ID_MODEL_FROM_DATABASE=Wildcat Point-LP Serial IO GSPI Controller #1 + +pci:v00008086d00009D03* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] + +pci:v00008086d00009D03sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude 3570) + +pci:v00008086d00009D14* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #5 + +pci:v00008086d00009D15* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #6 + +pci:v00008086d00009D21* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC + +pci:v00008086d00009D21sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude 3570) + +pci:v00008086d00009D23* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus + +pci:v00008086d00009D23sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude 3570) + +pci:v00008086d00009D27* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO UART Controller #0 + +pci:v00008086d00009D28* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO UART Controller #1 + +pci:v00008086d00009D29* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO SPI Controller #0 + +pci:v00008086d00009D2A* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO SPI Controller #1 + +pci:v00008086d00009D2D* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Secure Digital IO Controller + +pci:v00008086d00009D2F* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller + +pci:v00008086d00009D2Fsv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude 3570) + +pci:v00008086d00009D31* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem + +pci:v00008086d00009D31sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude 3570) + +pci:v00008086d00009D3A* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 + +pci:v00008086d00009D3Asv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude 3570) + +pci:v00008086d00009D48* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller + +pci:v00008086d00009D48sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Latitude 3570) + +pci:v00008086d00009D60* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 + +pci:v00008086d00009D60sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Latitude 3570) + +pci:v00008086d00009D60sv00008086sd00009D60* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (100 Series PCH/Sunrise Point PCH I2C0 [Skylake/Kaby Lake LPSS I2C]) + +pci:v00008086d00009D61* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #1 + +pci:v00008086d00009D62* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #2 + +pci:v00008086d00009D63* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #3 + +pci:v00008086d00009D64* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #4 + +pci:v00008086d00009D65* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #5 + +pci:v00008086d00009D66* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO UART Controller #2 + +pci:v00008086d00009D70* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio + +pci:v00008086d00009D70sv00001028sd000006F3* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Latitude 3570) + +pci:v00008086d0000A000* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge + +pci:v00008086d0000A000sv00001458sd00005000* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (GA-D525TUD) + +pci:v00008086d0000A000sv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (DeskTop Board D510MO) + +pci:v00008086d0000A000sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (Desktop Board D425KT) + +pci:v00008086d0000A001* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + +pci:v00008086d0000A001sv00001458sd0000D000* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (GA-D525TUD) + +pci:v00008086d0000A001sv00008086sd00004F4D* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (DeskTop Board D510MO) + +pci:v00008086d0000A001sv00008086sd0000544B* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Desktop Board D425KT) + +pci:v00008086d0000A002* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + +pci:v00008086d0000A003* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx CHAPS counter + +pci:v00008086d0000A010* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge + +pci:v00008086d0000A010sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge (Notebook N150P) + +pci:v00008086d0000A011* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + +pci:v00008086d0000A011sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Notebook N150P) + +pci:v00008086d0000A012* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller + +pci:v00008086d0000A012sv0000144Dsd0000C072* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller (Notebook N150P) + +pci:v00008086d0000A013* + ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx CHAPS counter + +pci:v00008086d0000A102* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA controller [AHCI mode] + +pci:v00008086d0000A103* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [AHCI mode] + +pci:v00008086d0000A105* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode] + +pci:v00008086d0000A107* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode] + +pci:v00008086d0000A10F* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode] + +pci:v00008086d0000A110* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #1 + +pci:v00008086d0000A111* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #2 + +pci:v00008086d0000A112* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #3 + +pci:v00008086d0000A113* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #4 + +pci:v00008086d0000A114* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #5 + +pci:v00008086d0000A115* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #6 + +pci:v00008086d0000A116* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #7 + +pci:v00008086d0000A117* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #8 + +pci:v00008086d0000A118* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #9 + +pci:v00008086d0000A119* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #10 + +pci:v00008086d0000A11A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #11 + +pci:v00008086d0000A11B* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #12 + +pci:v00008086d0000A11C* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #13 + +pci:v00008086d0000A11D* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #14 + +pci:v00008086d0000A11E* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #15 + +pci:v00008086d0000A11F* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Express Root Port #16 + +pci:v00008086d0000A120* + ID_MODEL_FROM_DATABASE=Sunrise Point-H P2SB + +pci:v00008086d0000A121* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PMC + +pci:v00008086d0000A122* + ID_MODEL_FROM_DATABASE=Sunrise Point-H cAVS + +pci:v00008086d0000A123* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SMBus + +pci:v00008086d0000A124* + ID_MODEL_FROM_DATABASE=Sunrise Point-H SPI Controller + +pci:v00008086d0000A125* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Gigabit Ethernet Controller + +pci:v00008086d0000A126* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak + +pci:v00008086d0000A127* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART #0 + +pci:v00008086d0000A128* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART #1 + +pci:v00008086d0000A129* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO SPI #0 + +pci:v00008086d0000A12A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO SPI #1 + +pci:v00008086d0000A12F* + ID_MODEL_FROM_DATABASE=Sunrise Point-H USB 3.0 xHCI Controller + +pci:v00008086d0000A130* + ID_MODEL_FROM_DATABASE=Sunrise Point-H USB Device Controller (OTG) + +pci:v00008086d0000A131* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Thermal subsystem + +pci:v00008086d0000A133* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak ACPI Function + +pci:v00008086d0000A135* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Integrated Sensor Hub + +pci:v00008086d0000A13A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #1 + +pci:v00008086d0000A13B* + ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #2 + +pci:v00008086d0000A13C* + ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME IDE Redirection + +pci:v00008086d0000A13D* + ID_MODEL_FROM_DATABASE=Sunrise Point-H KT Redirection + +pci:v00008086d0000A13E* + ID_MODEL_FROM_DATABASE=Sunrise Point-H CSME HECI #3 + +pci:v00008086d0000A140* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A141* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A142* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A143* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A144* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A145* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A146* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A147* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A148* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A149* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14B* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14C* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14D* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14E* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A14F* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A150* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A151* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A152* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A153* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A154* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A155* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A156* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A157* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A158* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A159* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15B* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15C* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15D* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15E* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A15F* + ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller + +pci:v00008086d0000A160* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO I2C Controller #0 + +pci:v00008086d0000A161* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO I2C Controller #1 + +pci:v00008086d0000A166* + ID_MODEL_FROM_DATABASE=Sunrise Point-H Serial IO UART Controller #2 + +pci:v00008086d0000A167* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #17 + +pci:v00008086d0000A168* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #18 + +pci:v00008086d0000A169* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #19 + +pci:v00008086d0000A16A* + ID_MODEL_FROM_DATABASE=Sunrise Point-H PCI Root Port #20 + +pci:v00008086d0000A170* + ID_MODEL_FROM_DATABASE=Sunrise Point-H HD Audio + +pci:v00008086d0000A182* + ID_MODEL_FROM_DATABASE=Lewisburg SATA Controller [AHCI mode] + +pci:v00008086d0000A190* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #1 + +pci:v00008086d0000A191* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #2 + +pci:v00008086d0000A192* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #3 + +pci:v00008086d0000A193* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #4 + +pci:v00008086d0000A194* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #5 + +pci:v00008086d0000A195* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #6 + +pci:v00008086d0000A196* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #7 + +pci:v00008086d0000A197* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #8 + +pci:v00008086d0000A198* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #9 + +pci:v00008086d0000A199* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #10 + +pci:v00008086d0000A19A* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #11 + +pci:v00008086d0000A19B* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #12 + +pci:v00008086d0000A19C* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #13 + +pci:v00008086d0000A19D* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #14 + +pci:v00008086d0000A19E* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #15 + +pci:v00008086d0000A19F* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #16 + +pci:v00008086d0000A1A0* + ID_MODEL_FROM_DATABASE=Lewisburg P2SB + +pci:v00008086d0000A1A1* + ID_MODEL_FROM_DATABASE=Lewisburg PMC + +pci:v00008086d0000A1A2* + ID_MODEL_FROM_DATABASE=Lewisburg cAVS + +pci:v00008086d0000A1A3* + ID_MODEL_FROM_DATABASE=Lewisburg SMBus + +pci:v00008086d0000A1A4* + ID_MODEL_FROM_DATABASE=Lewisburg SPI Controller + +pci:v00008086d0000A1AF* + ID_MODEL_FROM_DATABASE=Lewisburg USB 3.0 xHCI Controller + +pci:v00008086d0000A1BA* + ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #1 + +pci:v00008086d0000A1BB* + ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #2 + +pci:v00008086d0000A1BC* + ID_MODEL_FROM_DATABASE=Lewisburg CSME: IDE-r + +pci:v00008086d0000A1BD* + ID_MODEL_FROM_DATABASE=Lewisburg CSME: KT Controller + +pci:v00008086d0000A1BE* + ID_MODEL_FROM_DATABASE=Lewisburg CSME: HECI #3 + +pci:v00008086d0000A1C1* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C2* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C3* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C4* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C5* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C6* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1C7* + ID_MODEL_FROM_DATABASE=Lewisburg LPC Controller + +pci:v00008086d0000A1D2* + ID_MODEL_FROM_DATABASE=Lewisburg SSATA Controller [AHCI mode] + +pci:v00008086d0000A1E7* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #17 + +pci:v00008086d0000A1E8* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #18 + +pci:v00008086d0000A1E9* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #19 + +pci:v00008086d0000A1EA* + ID_MODEL_FROM_DATABASE=Lewisburg PCI Express Root Port #20 + +pci:v00008086d0000A1F0* + ID_MODEL_FROM_DATABASE=Lewisburg MROM 0 + +pci:v00008086d0000A1F1* + ID_MODEL_FROM_DATABASE=Lewisburg MROM 1 + +pci:v00008086d0000A1F8* + ID_MODEL_FROM_DATABASE=Lewisburg IE: HECI #1 + +pci:v00008086d0000A1F9* + ID_MODEL_FROM_DATABASE=Lewisburg IE: HECI #2 + +pci:v00008086d0000A1FA* + ID_MODEL_FROM_DATABASE=Lewisburg IE: IDE-r + +pci:v00008086d0000A1FB* + ID_MODEL_FROM_DATABASE=Lewisburg IE: KT Controller + +pci:v00008086d0000A1FC* + ID_MODEL_FROM_DATABASE=Lewisburg IE: HECI #3 + +pci:v00008086d0000A620* + ID_MODEL_FROM_DATABASE=6400/6402 Advanced Memory Buffer (AMB) + +pci:v00008086d0000ABC0* + ID_MODEL_FROM_DATABASE=Omni-Path Fabric Switch Silicon 100 Series + +pci:v00008086d0000B152* + ID_MODEL_FROM_DATABASE=21152 PCI-to-PCI Bridge + +pci:v00008086d0000B152sv00008086sd0000B152* + ID_MODEL_FROM_DATABASE=21152 PCI-to-PCI Bridge + +pci:v00008086d0000B154* + ID_MODEL_FROM_DATABASE=21154 PCI-to-PCI Bridge + +pci:v00008086d0000B555* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge + +pci:v00008086d0000B555sv000012C7sd00005005* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (SS7HD PCI Adaptor Card) + +pci:v00008086d0000B555sv000012C7sd00005006* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (SS7HDC cPCI Adaptor Card) + +pci:v00008086d0000B555sv000012D9sd0000000A* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (PCI VoIP Gateway) + +pci:v00008086d0000B555sv00004C53sd00001050* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (CT7 mainboard) + +pci:v00008086d0000B555sv00004C53sd00001051* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (CE7 mainboard) + +pci:v00008086d0000B555sv0000E4BFsd00001000* + ID_MODEL_FROM_DATABASE=21555 Non transparent PCI-to-PCI Bridge (CC8-1-BLUES) + +pci:v00008086d0000D130* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D131* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D131sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=Core Processor DMI (OptiPlex 980) + +pci:v00008086d0000D131sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=Core Processor DMI (C7SIM-Q Motherboard) + +pci:v00008086d0000D132* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D132sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=Core Processor DMI (Latitude E6510) + +pci:v00008086d0000D133* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D134* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D135* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D136* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D137* + ID_MODEL_FROM_DATABASE=Core Processor DMI + +pci:v00008086d0000D138* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 1 + +pci:v00008086d0000D138sv00001028sd000002DA* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 1 (OptiPlex 980) + +pci:v00008086d0000D138sv00001028sd0000040B* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 1 (Latitude E6510) + +pci:v00008086d0000D138sv000015D9sd0000060D* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 1 (C7SIM-Q Motherboard) + +pci:v00008086d0000D139* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 2 + +pci:v00008086d0000D13A* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 3 + +pci:v00008086d0000D13B* + ID_MODEL_FROM_DATABASE=Core Processor PCI Express Root Port 4 + +pci:v00008086d0000D150* + ID_MODEL_FROM_DATABASE=Core Processor QPI Link + +pci:v00008086d0000D151* + ID_MODEL_FROM_DATABASE=Core Processor QPI Routing and Protocol Registers + +pci:v00008086d0000D155* + ID_MODEL_FROM_DATABASE=Core Processor System Management Registers + +pci:v00008086d0000D156* + ID_MODEL_FROM_DATABASE=Core Processor Semaphore and Scratchpad Registers + +pci:v00008086d0000D157* + ID_MODEL_FROM_DATABASE=Core Processor System Control and Status Registers + +pci:v00008086d0000D158* + ID_MODEL_FROM_DATABASE=Core Processor Miscellaneous Registers + +pci:v000080EE* + ID_VENDOR_FROM_DATABASE=InnoTek Systemberatung GmbH + +pci:v000080EEd0000BEEF* + ID_MODEL_FROM_DATABASE=VirtualBox Graphics Adapter + +pci:v000080EEd0000CAFE* + ID_MODEL_FROM_DATABASE=VirtualBox Guest Service + +pci:v00008322* + ID_VENDOR_FROM_DATABASE=Sodick America Corp. + +pci:v00008384* + ID_VENDOR_FROM_DATABASE=SigmaTel + +pci:v00008401* + ID_VENDOR_FROM_DATABASE=TRENDware International Inc. + +pci:v00008686* + ID_VENDOR_FROM_DATABASE=ScaleMP + +pci:v00008686d00001010* + ID_MODEL_FROM_DATABASE=vSMP Foundation controller [vSMP CTL] + +pci:v00008686d00001011* + ID_MODEL_FROM_DATABASE=vSMP Foundation MEX/FLX controller [vSMP CTL] + +pci:v00008800* + ID_VENDOR_FROM_DATABASE=Trigem Computer Inc. + +pci:v00008800d00002008* + ID_MODEL_FROM_DATABASE=Video assistant component + +pci:v00008866* + ID_VENDOR_FROM_DATABASE=T-Square Design Inc. + +pci:v00008888* + ID_VENDOR_FROM_DATABASE=Silicon Magic + +pci:v00008912* + ID_VENDOR_FROM_DATABASE=TRX + +pci:v00008C4A* + ID_VENDOR_FROM_DATABASE=Winbond + +pci:v00008C4Ad00001980* + ID_MODEL_FROM_DATABASE=W89C940 misprogrammed [ne2k] + +pci:v00008E0E* + ID_VENDOR_FROM_DATABASE=Computone Corporation + +pci:v00008E2E* + ID_VENDOR_FROM_DATABASE=KTI + +pci:v00008E2Ed00003000* + ID_MODEL_FROM_DATABASE=ET32P2 + +pci:v00009004* + ID_VENDOR_FROM_DATABASE=Adaptec + +pci:v00009004d00000078* + ID_MODEL_FROM_DATABASE=AHA-2940U_CN + +pci:v00009004d00001078* + ID_MODEL_FROM_DATABASE=AIC-7810 + +pci:v00009004d00001160* + ID_MODEL_FROM_DATABASE=AIC-1160 [Family Fibre Channel Adapter] + +pci:v00009004d00002178* + ID_MODEL_FROM_DATABASE=AIC-7821 + +pci:v00009004d00003860* + ID_MODEL_FROM_DATABASE=AHA-2930CU + +pci:v00009004d00003B78* + ID_MODEL_FROM_DATABASE=AHA-4844W/4844UW + +pci:v00009004d00005075* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005078* + ID_MODEL_FROM_DATABASE=AIC-7850T/7856T [AVA-2902/4/6 / AHA-2910] + +pci:v00009004d00005078sv00009004sd00007850* + ID_MODEL_FROM_DATABASE=AIC-7850T/7856T [AVA-2902/4/6 / AHA-2910] (AIC-7850T/7856T [AVA-290x / AHA-2910]) + +pci:v00009004d00005175* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005178* + ID_MODEL_FROM_DATABASE=AIC-7851 + +pci:v00009004d00005275* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005278* + ID_MODEL_FROM_DATABASE=AIC-7852 + +pci:v00009004d00005375* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005378* + ID_MODEL_FROM_DATABASE=AIC-7850 + +pci:v00009004d00005475* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005478* + ID_MODEL_FROM_DATABASE=AIC-7850 + +pci:v00009004d00005575* + ID_MODEL_FROM_DATABASE=AVA-2930 + +pci:v00009004d00005578* + ID_MODEL_FROM_DATABASE=AIC-7855 + +pci:v00009004d00005647* + ID_MODEL_FROM_DATABASE=ANA-7711 TCP Offload Engine + +pci:v00009004d00005647sv00009004sd00007710* + ID_MODEL_FROM_DATABASE=ANA-7711 TCP Offload Engine (ANA-7711F TCP Offload Engine - Optical) + +pci:v00009004d00005647sv00009004sd00007711* + ID_MODEL_FROM_DATABASE=ANA-7711 TCP Offload Engine (ANA-7711LP TCP Offload Engine - Copper) + +pci:v00009004d00005675* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005678* + ID_MODEL_FROM_DATABASE=AIC-7856 + +pci:v00009004d00005775* + ID_MODEL_FROM_DATABASE=AIC-755x + +pci:v00009004d00005778* + ID_MODEL_FROM_DATABASE=AIC-7850 + +pci:v00009004d00005800* + ID_MODEL_FROM_DATABASE=AIC-5800 + +pci:v00009004d00005900* + ID_MODEL_FROM_DATABASE=ANA-5910/5930/5940 ATM155 & 25 LAN Adapter + +pci:v00009004d00005905* + ID_MODEL_FROM_DATABASE=ANA-5910A/5930A/5940A ATM Adapter + +pci:v00009004d00006038* + ID_MODEL_FROM_DATABASE=AIC-3860 + +pci:v00009004d00006075* + ID_MODEL_FROM_DATABASE=AIC-1480 / APA-1480 + +pci:v00009004d00006075sv00009004sd00007560* + ID_MODEL_FROM_DATABASE=AIC-1480 / APA-1480 (Cardbus) + +pci:v00009004d00006078* + ID_MODEL_FROM_DATABASE=AIC-7860 + +pci:v00009004d00006178* + ID_MODEL_FROM_DATABASE=AIC-7861 + +pci:v00009004d00006178sv00009004sd00007861* + ID_MODEL_FROM_DATABASE=AIC-7861 (AHA-2940AU Single) + +pci:v00009004d00006278* + ID_MODEL_FROM_DATABASE=AIC-7860 + +pci:v00009004d00006378* + ID_MODEL_FROM_DATABASE=AIC-7860 + +pci:v00009004d00006478* + ID_MODEL_FROM_DATABASE=AIC-786x + +pci:v00009004d00006578* + ID_MODEL_FROM_DATABASE=AIC-786x + +pci:v00009004d00006678* + ID_MODEL_FROM_DATABASE=AIC-786x + +pci:v00009004d00006778* + ID_MODEL_FROM_DATABASE=AIC-786x + +pci:v00009004d00006915* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A + +pci:v00009004d00006915sv00009004sd00000008* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 10/100) + +pci:v00009004d00006915sv00009004sd00000009* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 10/100) + +pci:v00009004d00006915sv00009004sd00000010* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62022 2-port 10/100) + +pci:v00009004d00006915sv00009004sd00000018* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62044 4-port 10/100) + +pci:v00009004d00006915sv00009004sd00000019* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62044 4-port 10/100) + +pci:v00009004d00006915sv00009004sd00000020* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62022 2-port 10/100) + +pci:v00009004d00006915sv00009004sd00000028* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 10/100) + +pci:v00009004d00006915sv00009004sd00008008* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008009* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008010* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62022 2-port 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008018* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62044 4-port 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008019* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62044 4-port 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008020* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA62022 2-port 64 bit 10/100) + +pci:v00009004d00006915sv00009004sd00008028* + ID_MODEL_FROM_DATABASE=ANA620xx/ANA69011A (ANA69011A/TX 64 bit 10/100) + +pci:v00009004d00007078* + ID_MODEL_FROM_DATABASE=AHA-294x / AIC-7870 + +pci:v00009004d00007178* + ID_MODEL_FROM_DATABASE=AIC-7870P/7871 [AHA-2940/W/S76] + +pci:v00009004d00007278* + ID_MODEL_FROM_DATABASE=AHA-3940/3940W / AIC-7872 + +pci:v00009004d00007378* + ID_MODEL_FROM_DATABASE=AHA-3985 / AIC-7873 + +pci:v00009004d00007478* + ID_MODEL_FROM_DATABASE=AHA-2944/2944W / AIC-7874 + +pci:v00009004d00007578* + ID_MODEL_FROM_DATABASE=AHA-3944/3944W / AIC-7875 + +pci:v00009004d00007678* + ID_MODEL_FROM_DATABASE=AHA-4944W/UW / AIC-7876 + +pci:v00009004d00007710* + ID_MODEL_FROM_DATABASE=ANA-7711F Network Accelerator Card (NAC) - Optical + +pci:v00009004d00007711* + ID_MODEL_FROM_DATABASE=ANA-7711C Network Accelerator Card (NAC) - Copper + +pci:v00009004d00007778* + ID_MODEL_FROM_DATABASE=AIC-787x + +pci:v00009004d00007810* + ID_MODEL_FROM_DATABASE=AIC-7810 + +pci:v00009004d00007815* + ID_MODEL_FROM_DATABASE=AIC-7815 RAID+Memory Controller IC + +pci:v00009004d00007815sv00009004sd00007815* + ID_MODEL_FROM_DATABASE=AIC-7815 RAID+Memory Controller IC (ARO-1130U2 RAID Controller) + +pci:v00009004d00007815sv00009004sd00007840* + ID_MODEL_FROM_DATABASE=AIC-7815 RAID+Memory Controller IC + +pci:v00009004d00007850* + ID_MODEL_FROM_DATABASE=AIC-7850 + +pci:v00009004d00007855* + ID_MODEL_FROM_DATABASE=AHA-2930 + +pci:v00009004d00007860* + ID_MODEL_FROM_DATABASE=AIC-7860 + +pci:v00009004d00007870* + ID_MODEL_FROM_DATABASE=AIC-7870 + +pci:v00009004d00007871* + ID_MODEL_FROM_DATABASE=AHA-2940 + +pci:v00009004d00007872* + ID_MODEL_FROM_DATABASE=AHA-3940 + +pci:v00009004d00007873* + ID_MODEL_FROM_DATABASE=AHA-3980 + +pci:v00009004d00007874* + ID_MODEL_FROM_DATABASE=AHA-2944 + +pci:v00009004d00007880* + ID_MODEL_FROM_DATABASE=AIC-7880P + +pci:v00009004d00007890* + ID_MODEL_FROM_DATABASE=AIC-7890 + +pci:v00009004d00007891* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00007892* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00007893* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00007894* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00007895* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 + +pci:v00009004d00007895sv00009004sd00007890* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B) + +pci:v00009004d00007895sv00009004sd00007891* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-2940U/2940UW Dual) + +pci:v00009004d00007895sv00009004sd00007892* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-3940AU/AUW/AUWD/UWD) + +pci:v00009004d00007895sv00009004sd00007894* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-3944AUWD) + +pci:v00009004d00007895sv00009004sd00007895* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B) + +pci:v00009004d00007895sv00009004sd00007896* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B) + +pci:v00009004d00007895sv00009004sd00007897* + ID_MODEL_FROM_DATABASE=AHA-2940U/UW / AHA-39xx / AIC-7895 (AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B) + +pci:v00009004d00007896* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00007897* + ID_MODEL_FROM_DATABASE=AIC-789x + +pci:v00009004d00008078* + ID_MODEL_FROM_DATABASE=AIC-7880U + +pci:v00009004d00008078sv00009004sd00007880* + ID_MODEL_FROM_DATABASE=AIC-7880U (AIC-7880P Ultra/Ultra Wide SCSI Chipset) + +pci:v00009004d00008178* + ID_MODEL_FROM_DATABASE=AIC-7870P/7881U [AHA-2940U/UW/D/S76] + +pci:v00009004d00008178sv00009004sd00007881* + ID_MODEL_FROM_DATABASE=AIC-7870P/7881U [AHA-2940U/UW/D/S76] (AHA-2940UW SCSI Host Adapter) + +pci:v00009004d00008278* + ID_MODEL_FROM_DATABASE=AHA-3940U/UW/UWD / AIC-7882U + +pci:v00009004d00008378* + ID_MODEL_FROM_DATABASE=AHA-3940U/UW / AIC-7883U + +pci:v00009004d00008478* + ID_MODEL_FROM_DATABASE=AHA-2944UW / AIC-7884U + +pci:v00009004d00008578* + ID_MODEL_FROM_DATABASE=AHA-3944U/UWD / AIC-7885 + +pci:v00009004d00008678* + ID_MODEL_FROM_DATABASE=AHA-4944UW / AIC-7886 + +pci:v00009004d00008778* + ID_MODEL_FROM_DATABASE=AHA-2940UW Pro / AIC-788x + +pci:v00009004d00008778sv00009004sd00007887* + ID_MODEL_FROM_DATABASE=AHA-2940UW Pro / AIC-788x (2940UW Pro Ultra-Wide SCSI Controller) + +pci:v00009004d00008878* + ID_MODEL_FROM_DATABASE=AHA-2930UW / AIC-7888 + +pci:v00009004d00008878sv00009004sd00007888* + ID_MODEL_FROM_DATABASE=AHA-2930UW / AIC-7888 (AHA-2930UW SCSI Controller) + +pci:v00009004d00008B78* + ID_MODEL_FROM_DATABASE=ABA-1030 + +pci:v00009004d0000EC78* + ID_MODEL_FROM_DATABASE=AHA-4944W/UW + +pci:v00009005* + ID_VENDOR_FROM_DATABASE=Adaptec + +pci:v00009005d00000010* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W + +pci:v00009005d00000010sv00009005sd00002180* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W (AHA-2940U2 SCSI Controller) + +pci:v00009005d00000010sv00009005sd00008100* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W (AHA-2940U2B SCSI Controller) + +pci:v00009005d00000010sv00009005sd0000A100* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W (AHA-2940U2B SCSI Controller) + +pci:v00009005d00000010sv00009005sd0000A180* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W (AIC-3860Q [AHA-2940U2W/GE] SCSI Controller) + +pci:v00009005d00000010sv00009005sd0000E100* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W (AHA-2950U2B SCSI Controller) + +pci:v00009005d00000011* + ID_MODEL_FROM_DATABASE=AHA-2930U2 + +pci:v00009005d00000013* + ID_MODEL_FROM_DATABASE=78902 + +pci:v00009005d00000013sv00009005sd00000003* + ID_MODEL_FROM_DATABASE=78902 (AAA-131U2 Array1000 1 Channel RAID Controller) + +pci:v00009005d00000013sv00009005sd0000000F* + ID_MODEL_FROM_DATABASE=78902 (AIC7890_ARO) + +pci:v00009005d0000001F* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W / 7890/7891 + +pci:v00009005d0000001Fsv00009005sd0000000F* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W / 7890/7891 (2940U2W SCSI Controller) + +pci:v00009005d0000001Fsv00009005sd0000A180* + ID_MODEL_FROM_DATABASE=AHA-2940U2/U2W / 7890/7891 (2940U2W SCSI Controller) + +pci:v00009005d00000020* + ID_MODEL_FROM_DATABASE=AIC-7890 + +pci:v00009005d0000002F* + ID_MODEL_FROM_DATABASE=AIC-7890 + +pci:v00009005d00000030* + ID_MODEL_FROM_DATABASE=AIC-7890 + +pci:v00009005d0000003F* + ID_MODEL_FROM_DATABASE=AIC-7890 + +pci:v00009005d00000050* + ID_MODEL_FROM_DATABASE=AHA-3940U2x/395U2x + +pci:v00009005d00000050sv00009005sd0000F500* + ID_MODEL_FROM_DATABASE=AHA-3940U2x/395U2x (AHA-3950U2B) + +pci:v00009005d00000050sv00009005sd0000FFFF* + ID_MODEL_FROM_DATABASE=AHA-3940U2x/395U2x (AHA-3950U2B) + +pci:v00009005d00000051* + ID_MODEL_FROM_DATABASE=AHA-3950U2D + +pci:v00009005d00000051sv00009005sd0000B500* + ID_MODEL_FROM_DATABASE=AHA-3950U2D + +pci:v00009005d00000053* + ID_MODEL_FROM_DATABASE=AIC-7896 SCSI Controller + +pci:v00009005d00000053sv00009005sd0000FFFF* + ID_MODEL_FROM_DATABASE=AIC-7896 SCSI Controller (mainboard implementation) + +pci:v00009005d0000005F* + ID_MODEL_FROM_DATABASE=AIC-7896U2/7897U2 + +pci:v00009005d00000080* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m + +pci:v00009005d00000080sv00000E11sd0000E2A0* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m (Compaq 64-Bit/66MHz Wide Ultra3 SCSI Adapter) + +pci:v00009005d00000080sv00009005sd00006220* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m (AHA-29160C) + +pci:v00009005d00000080sv00009005sd000062A0* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m (29160N Ultra160 SCSI Controller) + +pci:v00009005d00000080sv00009005sd0000E220* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m (29160LP Low Profile Ultra160 SCSI Controller) + +pci:v00009005d00000080sv00009005sd0000E2A0* + ID_MODEL_FROM_DATABASE=AIC-7892A U160/m (29160 Ultra160 SCSI Controller) + +pci:v00009005d00000081* + ID_MODEL_FROM_DATABASE=AIC-7892B U160/m + +pci:v00009005d00000081sv00009005sd000062A1* + ID_MODEL_FROM_DATABASE=AIC-7892B U160/m (19160 Ultra160 SCSI Controller) + +pci:v00009005d00000083* + ID_MODEL_FROM_DATABASE=AIC-7892D U160/m + +pci:v00009005d0000008F* + ID_MODEL_FROM_DATABASE=AIC-7892P U160/m + +pci:v00009005d0000008Fsv00001179sd00000001* + ID_MODEL_FROM_DATABASE=AIC-7892P U160/m (Magnia Z310) + +pci:v00009005d0000008Fsv000015D9sd00009005* + ID_MODEL_FROM_DATABASE=AIC-7892P U160/m (Onboard SCSI Host Adapter) + +pci:v00009005d00000092* + ID_MODEL_FROM_DATABASE=AVC-2010 [VideoH!] + +pci:v00009005d00000093* + ID_MODEL_FROM_DATABASE=AVC-2410 [VideoH!] + +pci:v00009005d000000C0* + ID_MODEL_FROM_DATABASE=AHA-3960D / AIC-7899A U160/m + +pci:v00009005d000000C0sv00000E11sd0000F620* + ID_MODEL_FROM_DATABASE=AHA-3960D / AIC-7899A U160/m (Compaq 64-Bit/66MHz Dual Channel Wide Ultra3 SCSI Adapter) + +pci:v00009005d000000C0sv00009005sd0000F620* + ID_MODEL_FROM_DATABASE=AHA-3960D / AIC-7899A U160/m (AHA-3960D U160/m) + +pci:v00009005d000000C1* + ID_MODEL_FROM_DATABASE=AIC-7899B U160/m + +pci:v00009005d000000C3* + ID_MODEL_FROM_DATABASE=AIC-7899D U160/m + +pci:v00009005d000000C5* + ID_MODEL_FROM_DATABASE=RAID subsystem HBA + +pci:v00009005d000000C5sv00001028sd000000C5* + ID_MODEL_FROM_DATABASE=RAID subsystem HBA (PowerEdge 2400,2500,2550,4400) + +pci:v00009005d000000CF* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m + +pci:v00009005d000000CFsv00001028sd000000CE* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (PowerEdge 1400) + +pci:v00009005d000000CFsv00001028sd000000D1* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (PowerEdge 2550) + +pci:v00009005d000000CFsv00001028sd000000D9* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (PowerEdge 2500) + +pci:v00009005d000000CFsv000010F1sd00002462* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (Thunder K7 S2462) + +pci:v00009005d000000CFsv000015D9sd00009005* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (Onboard SCSI Host Adapter) + +pci:v00009005d000000CFsv00008086sd00003411* + ID_MODEL_FROM_DATABASE=AIC-7899P U160/m (SDS2 Mainboard) + +pci:v00009005d00000241* + ID_MODEL_FROM_DATABASE=Serial ATA II RAID 1420SA + +pci:v00009005d00000242* + ID_MODEL_FROM_DATABASE=Serial ATA II RAID 1220SA + +pci:v00009005d00000243* + ID_MODEL_FROM_DATABASE=Serial ATA II RAID 1430SA + +pci:v00009005d00000244* + ID_MODEL_FROM_DATABASE=eSATA II RAID 1225SA + +pci:v00009005d00000250* + ID_MODEL_FROM_DATABASE=ServeRAID Controller + +pci:v00009005d00000250sv00001014sd00000279* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID 6M) + +pci:v00009005d00000250sv00001014sd0000028C* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID 6i/6i+) + +pci:v00009005d00000250sv00001014sd0000028E* + ID_MODEL_FROM_DATABASE=ServeRAID Controller (ServeRAID 7k) + +pci:v00009005d00000279* + ID_MODEL_FROM_DATABASE=ServeRAID 6M + +pci:v00009005d00000283* + ID_MODEL_FROM_DATABASE=AAC-RAID + +pci:v00009005d00000283sv00009005sd00000283* + ID_MODEL_FROM_DATABASE=AAC-RAID (Catapult) + +pci:v00009005d00000284* + ID_MODEL_FROM_DATABASE=AAC-RAID + +pci:v00009005d00000284sv00009005sd00000284* + ID_MODEL_FROM_DATABASE=AAC-RAID (Tomcat) + +pci:v00009005d00000285* + ID_MODEL_FROM_DATABASE=AAC-RAID + +pci:v00009005d00000285sv00000E11sd00000295* + ID_MODEL_FROM_DATABASE=AAC-RAID (SATA 6Ch (Bearcat)) + +pci:v00009005d00000285sv00001014sd000002F2* + ID_MODEL_FROM_DATABASE=AAC-RAID (ServeRAID 8i) + +pci:v00009005d00000285sv00001028sd00000287* + ID_MODEL_FROM_DATABASE=AAC-RAID (PowerEdge Expandable RAID Controller 320/DC) + +pci:v00009005d00000285sv00001028sd00000291* + ID_MODEL_FROM_DATABASE=AAC-RAID (CERC SATA RAID 2 PCI SATA 6ch (DellCorsair)) + +pci:v00009005d00000285sv0000103Csd00003227* + ID_MODEL_FROM_DATABASE=AAC-RAID (AAR-2610SA) + +pci:v00009005d00000285sv0000108Esd00000286* + ID_MODEL_FROM_DATABASE=AAC-RAID (Sun StorageTek SAS RAID HBA, Internal) + +pci:v00009005d00000285sv0000108Esd00000287* + ID_MODEL_FROM_DATABASE=AAC-RAID (STK RAID EXT) + +pci:v00009005d00000285sv0000108Esd00007AAC* + ID_MODEL_FROM_DATABASE=AAC-RAID (STK RAID REM) + +pci:v00009005d00000285sv0000108Esd00007AAE* + ID_MODEL_FROM_DATABASE=AAC-RAID (STK RAID EX) + +pci:v00009005d00000285sv000015D9sd000002B5* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S4i) + +pci:v00009005d00000285sv000015D9sd000002B6* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S8i) + +pci:v00009005d00000285sv000015D9sd000002C9* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S4iR) + +pci:v00009005d00000285sv000015D9sd000002CA* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S8iR) + +pci:v00009005d00000285sv000015D9sd000002D2* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S8i-LP) + +pci:v00009005d00000285sv000015D9sd000002D3* + ID_MODEL_FROM_DATABASE=AAC-RAID (SMC AOC-USAS-S8iR-LP) + +pci:v00009005d00000285sv000017AAsd00000286* + ID_MODEL_FROM_DATABASE=AAC-RAID (Legend S220 (Legend Crusader)) + +pci:v00009005d00000285sv000017AAsd00000287* + ID_MODEL_FROM_DATABASE=AAC-RAID (Legend S230 (Legend Vulcan)) + +pci:v00009005d00000285sv00009005sd00000285* + ID_MODEL_FROM_DATABASE=AAC-RAID (2200S (Vulcan)) + +pci:v00009005d00000285sv00009005sd00000286* + ID_MODEL_FROM_DATABASE=AAC-RAID (2120S (Crusader)) + +pci:v00009005d00000285sv00009005sd00000287* + ID_MODEL_FROM_DATABASE=AAC-RAID (2200S (Vulcan-2m)) + +pci:v00009005d00000285sv00009005sd00000288* + ID_MODEL_FROM_DATABASE=AAC-RAID (3230S (Harrier)) + +pci:v00009005d00000285sv00009005sd00000289* + ID_MODEL_FROM_DATABASE=AAC-RAID (3240S (Tornado)) + +pci:v00009005d00000285sv00009005sd0000028A* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2020ZCR) + +pci:v00009005d00000285sv00009005sd0000028B* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2025ZCR (Terminator)) + +pci:v00009005d00000285sv00009005sd0000028E* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2020SA (Skyhawk)) + +pci:v00009005d00000285sv00009005sd0000028F* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2025SA) + +pci:v00009005d00000285sv00009005sd00000290* + ID_MODEL_FROM_DATABASE=AAC-RAID (AAR-2410SA PCI SATA 4ch (Jaguar II)) + +pci:v00009005d00000285sv00009005sd00000292* + ID_MODEL_FROM_DATABASE=AAC-RAID (AAR-2810SA PCI SATA 8ch (Corsair-8)) + +pci:v00009005d00000285sv00009005sd00000293* + ID_MODEL_FROM_DATABASE=AAC-RAID (AAR-21610SA PCI SATA 16ch (Corsair-16)) + +pci:v00009005d00000285sv00009005sd00000294* + ID_MODEL_FROM_DATABASE=AAC-RAID (ESD SO-DIMM PCI-X SATA ZCR (Prowler)) + +pci:v00009005d00000285sv00009005sd00000296* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2240S) + +pci:v00009005d00000285sv00009005sd00000297* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-4005SAS) + +pci:v00009005d00000285sv00009005sd00000298* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-4000) + +pci:v00009005d00000285sv00009005sd00000299* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-4800SAS) + +pci:v00009005d00000285sv00009005sd0000029A* + ID_MODEL_FROM_DATABASE=AAC-RAID (4805SAS) + +pci:v00009005d00000285sv00009005sd000002A4* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP9085LI) + +pci:v00009005d00000285sv00009005sd000002A5* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5085BR) + +pci:v00009005d00000285sv00009005sd000002B5* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR5800) + +pci:v00009005d00000285sv00009005sd000002B6* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR5805) + +pci:v00009005d00000285sv00009005sd000002B7* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR5808) + +pci:v00009005d00000285sv00009005sd000002B8* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP5445SL) + +pci:v00009005d00000285sv00009005sd000002B9* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP5085SL) + +pci:v00009005d00000285sv00009005sd000002BA* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP5805SL) + +pci:v00009005d00000285sv00009005sd000002BB* + ID_MODEL_FROM_DATABASE=AAC-RAID (3405) + +pci:v00009005d00000285sv00009005sd000002BC* + ID_MODEL_FROM_DATABASE=AAC-RAID (3805) + +pci:v00009005d00000285sv00009005sd000002BD* + ID_MODEL_FROM_DATABASE=AAC-RAID (31205) + +pci:v00009005d00000285sv00009005sd000002BE* + ID_MODEL_FROM_DATABASE=AAC-RAID (31605) + +pci:v00009005d00000285sv00009005sd000002BF* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5045BL) + +pci:v00009005d00000285sv00009005sd000002C0* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5085BL) + +pci:v00009005d00000285sv00009005sd000002C1* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5125BR) + +pci:v00009005d00000285sv00009005sd000002C2* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5165BR) + +pci:v00009005d00000285sv00009005sd000002C3* + ID_MODEL_FROM_DATABASE=AAC-RAID (51205) + +pci:v00009005d00000285sv00009005sd000002C4* + ID_MODEL_FROM_DATABASE=AAC-RAID (51605) + +pci:v00009005d00000285sv00009005sd000002C5* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5125SL) + +pci:v00009005d00000285sv00009005sd000002C6* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP ICP5165SL) + +pci:v00009005d00000285sv00009005sd000002C7* + ID_MODEL_FROM_DATABASE=AAC-RAID (3085) + +pci:v00009005d00000285sv00009005sd000002C8* + ID_MODEL_FROM_DATABASE=AAC-RAID (ICP5805BL) + +pci:v00009005d00000285sv00009005sd000002CE* + ID_MODEL_FROM_DATABASE=AAC-RAID (51245) + +pci:v00009005d00000285sv00009005sd000002CF* + ID_MODEL_FROM_DATABASE=AAC-RAID (51645) + +pci:v00009005d00000285sv00009005sd000002D0* + ID_MODEL_FROM_DATABASE=AAC-RAID (52445) + +pci:v00009005d00000285sv00009005sd000002D1* + ID_MODEL_FROM_DATABASE=AAC-RAID (5405) + +pci:v00009005d00000285sv00009005sd000002D4* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2045) + +pci:v00009005d00000285sv00009005sd000002D5* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2405) + +pci:v00009005d00000285sv00009005sd000002D6* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2445) + +pci:v00009005d00000285sv00009005sd000002D7* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2805) + +pci:v00009005d00000285sv00009005sd000002D8* + ID_MODEL_FROM_DATABASE=AAC-RAID (5405G) + +pci:v00009005d00000285sv00009005sd000002D9* + ID_MODEL_FROM_DATABASE=AAC-RAID (5445G) + +pci:v00009005d00000285sv00009005sd000002DA* + ID_MODEL_FROM_DATABASE=AAC-RAID (5805G) + +pci:v00009005d00000285sv00009005sd000002DB* + ID_MODEL_FROM_DATABASE=AAC-RAID (5085G) + +pci:v00009005d00000285sv00009005sd000002DC* + ID_MODEL_FROM_DATABASE=AAC-RAID (51245G) + +pci:v00009005d00000285sv00009005sd000002DD* + ID_MODEL_FROM_DATABASE=AAC-RAID (51645G) + +pci:v00009005d00000285sv00009005sd000002DE* + ID_MODEL_FROM_DATABASE=AAC-RAID (52445G) + +pci:v00009005d00000285sv00009005sd000002DF* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2045G) + +pci:v00009005d00000285sv00009005sd000002E0* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2405G) + +pci:v00009005d00000285sv00009005sd000002E1* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2445G) + +pci:v00009005d00000285sv00009005sd000002E2* + ID_MODEL_FROM_DATABASE=AAC-RAID (ASR-2805G) + +pci:v00009005d00000286* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) + +pci:v00009005d00000286sv00001014sd0000034D* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (8s) + +pci:v00009005d00000286sv00001014sd00009540* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ServeRAID 8k/8k-l4) + +pci:v00009005d00000286sv00001014sd00009580* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ServeRAID 8k/8k-l8) + +pci:v00009005d00000286sv00009005sd0000028C* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ASR-2230S + ASR-2230SLP PCI-X (Lancer)) + +pci:v00009005d00000286sv00009005sd0000028D* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ASR-2130S) + +pci:v00009005d00000286sv00009005sd0000029B* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ASR-2820SA) + +pci:v00009005d00000286sv00009005sd0000029C* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ASR-2620SA) + +pci:v00009005d00000286sv00009005sd0000029D* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ASR-2420SA) + +pci:v00009005d00000286sv00009005sd0000029E* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP9024R0) + +pci:v00009005d00000286sv00009005sd0000029F* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP9014R0) + +pci:v00009005d00000286sv00009005sd000002A0* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP9047MA) + +pci:v00009005d00000286sv00009005sd000002A1* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP9087MA) + +pci:v00009005d00000286sv00009005sd000002A2* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (3800) + +pci:v00009005d00000286sv00009005sd000002A3* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP5445AU) + +pci:v00009005d00000286sv00009005sd000002A4* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP9085LI) + +pci:v00009005d00000286sv00009005sd000002A5* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP5085BR) + +pci:v00009005d00000286sv00009005sd000002A6* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP9067MA) + +pci:v00009005d00000286sv00009005sd000002A7* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (3805) + +pci:v00009005d00000286sv00009005sd000002A8* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (3400) + +pci:v00009005d00000286sv00009005sd000002A9* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP5085AU) + +pci:v00009005d00000286sv00009005sd000002AA* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP5045AU) + +pci:v00009005d00000286sv00009005sd000002AC* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (1800) + +pci:v00009005d00000286sv00009005sd000002B3* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (2400) + +pci:v00009005d00000286sv00009005sd000002B4* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (ICP ICP5045AL) + +pci:v00009005d00000286sv00009005sd00000800* + ID_MODEL_FROM_DATABASE=AAC-RAID (Rocket) (Callisto) + +pci:v00009005d0000028B* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 + +pci:v00009005d0000028Bsv00009005sd00000200* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 Entry Level - ASR-6405E - 4 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000201* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 Entry Level - ASR-6805E - 8 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000300* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-6405 - 4 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000301* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-6805 - 8 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000302* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-6445 - 4 internal and 4 external 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000310* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 Connectors on Top - ASR-6405T - 4 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000311* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 Connectors on Top - ASR-6805T - 8 internal 6G SAS) + +pci:v00009005d0000028Bsv00009005sd00000400* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-61205 - 12 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000401* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-61605 - 16 internal 6G SAS ports) + +pci:v00009005d0000028Bsv00009005sd00000403* + ID_MODEL_FROM_DATABASE=Series 6 - 6G SAS/PCIe 2 (Series 6 - ASR-62405 - 24 internal 6G SAS ports) + +pci:v00009005d0000028C* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 + +pci:v00009005d0000028Csv00009005sd00000500* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 - ASR-7805 - 8 internal 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000501* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 - ASR-71605 - 16 internal 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000502* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 - ASR-71685 - 16 internal 8 external 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000503* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 - ASR-72405 - 24 internal 0 external 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000504* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 - ASR-7885 - 8 internal 8 external 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000505* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 Entry Level - ASR-71685E - 16 internal 8 external 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Csv00009005sd00000506* + ID_MODEL_FROM_DATABASE=Series 7 6G SAS/PCIe 3 (Series 7 Entry Level - ASR-72405E - 24 internal 0 external 6G SAS Port/PCIe 3.0) + +pci:v00009005d0000028D* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 + +pci:v00009005d0000028Dsv00009005sd00000550* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 (Series 8 - ASR-82405 - 24 internal 0 external 12G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Dsv00009005sd00000551* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 (Series 8 - ASR-81605 - 16 internal 0 external 12G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Dsv00009005sd00000552* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 (Series 8 - ASR-8805 - 8 internal 0 external 12G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Dsv00009005sd00000553* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 (Series 8 - ASR-8085 - 0 internal 8 external 12G SAS Port/PCIe 3.0) + +pci:v00009005d0000028Dsv00009005sd00000554* + ID_MODEL_FROM_DATABASE=Series 8 12G SAS/PCIe 3 (Series 8 - ASR-8885 - 8 internal 8 external 12G SAS Port/PCIe 3.0) + +pci:v00009005d00000410* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA RAID) + +pci:v00009005d00000410sv00009005sd00000410* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA RAID) (ASC-48300(Spirit RAID)) + +pci:v00009005d00000410sv00009005sd00000411* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA RAID) (ASC-58300 (Oakmont RAID)) + +pci:v00009005d00000412* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA non-RAID) + +pci:v00009005d00000412sv00009005sd00000412* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA non-RAID) (ASC-48300 (Spirit non-RAID)) + +pci:v00009005d00000412sv00009005sd00000413* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor HBA non-RAID) (ASC-58300 (Oakmont non-RAID)) + +pci:v00009005d00000415* + ID_MODEL_FROM_DATABASE=ASC-58300 SAS (Razor-External HBA RAID) + +pci:v00009005d00000416* + ID_MODEL_FROM_DATABASE=ASC-58300 SAS (Razor-External HBA non-RAID) + +pci:v00009005d0000041E* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor ASIC non-RAID) + +pci:v00009005d0000041F* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor ASIC RAID) + +pci:v00009005d0000041Fsv00009005sd0000041F* + ID_MODEL_FROM_DATABASE=AIC-9410W SAS (Razor ASIC RAID) + +pci:v00009005d0000042F* + ID_MODEL_FROM_DATABASE=VSC7250/7251 SAS (Aurora ASIC non-RAID) + +pci:v00009005d00000430* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite HBA RAID) + +pci:v00009005d00000430sv00009005sd00000430* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite HBA RAID) (ASC-44300 (Spirit-Lite RAID)) + +pci:v00009005d00000432* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite HBA non-RAID) + +pci:v00009005d00000432sv00009005sd00000432* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite HBA non-RAID) (ASC-44300 (Spirit-Lite non-RAID)) + +pci:v00009005d0000043E* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite ASIC non-RAID) + +pci:v00009005d0000043F* + ID_MODEL_FROM_DATABASE=AIC-9405W SAS (Razor-Lite ASIC RAID) + +pci:v00009005d00000450* + ID_MODEL_FROM_DATABASE=ASC-1405 Unified Serial HBA + +pci:v00009005d00000500* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller + +pci:v00009005d00000500sv00001014sd000002C1* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller (PCI-X DDR 3Gb SAS Adapter (572A/572C)) + +pci:v00009005d00000500sv00001014sd000002C2* + ID_MODEL_FROM_DATABASE=Obsidian chipset SCSI controller (PCI-X DDR 3Gb SAS RAID Adapter (572B/572D)) + +pci:v00009005d00000503* + ID_MODEL_FROM_DATABASE=Scamp chipset SCSI controller + +pci:v00009005d00000503sv00001014sd000002BF* + ID_MODEL_FROM_DATABASE=Scamp chipset SCSI controller (Quad Channel PCI-X DDR U320 SCSI RAID Adapter (571E)) + +pci:v00009005d00000503sv00001014sd000002C3* + ID_MODEL_FROM_DATABASE=Scamp chipset SCSI controller (PCI-X DDR 3Gb SAS RAID Adapter (572F)) + +pci:v00009005d00000503sv00001014sd000002D5* + ID_MODEL_FROM_DATABASE=Scamp chipset SCSI controller (Quad Channel PCI-X DDR U320 SCSI RAID Adapter (571F)) + +pci:v00009005d00000910* + ID_MODEL_FROM_DATABASE=AUA-3100B + +pci:v00009005d0000091E* + ID_MODEL_FROM_DATABASE=AUA-3100B + +pci:v00009005d00008000* + ID_MODEL_FROM_DATABASE=ASC-29320A U320 + +pci:v00009005d0000800F* + ID_MODEL_FROM_DATABASE=AIC-7901 U320 + +pci:v00009005d00008010* + ID_MODEL_FROM_DATABASE=ASC-39320 U320 + +pci:v00009005d00008011* + ID_MODEL_FROM_DATABASE=ASC-39320D + +pci:v00009005d00008011sv00000E11sd000000AC* + ID_MODEL_FROM_DATABASE=ASC-39320D (U320) + +pci:v00009005d00008011sv00009005sd00000041* + ID_MODEL_FROM_DATABASE=ASC-39320D (U320) + +pci:v00009005d00008012* + ID_MODEL_FROM_DATABASE=ASC-29320 U320 + +pci:v00009005d00008013* + ID_MODEL_FROM_DATABASE=ASC-29320B U320 + +pci:v00009005d00008014* + ID_MODEL_FROM_DATABASE=ASC-29320LP U320 + +pci:v00009005d00008015* + ID_MODEL_FROM_DATABASE=ASC-39320B U320 + +pci:v00009005d00008016* + ID_MODEL_FROM_DATABASE=ASC-39320A U320 + +pci:v00009005d00008017* + ID_MODEL_FROM_DATABASE=ASC-29320ALP U320 + +pci:v00009005d00008017sv00009005sd00000044* + ID_MODEL_FROM_DATABASE=ASC-29320ALP U320 (ASC-29320ALP PCIx U320) + +pci:v00009005d00008017sv00009005sd00000045* + ID_MODEL_FROM_DATABASE=ASC-29320ALP U320 (ASC-29320LPE PCIe U320) + +pci:v00009005d0000801C* + ID_MODEL_FROM_DATABASE=ASC-39320D U320 + +pci:v00009005d0000801D* + ID_MODEL_FROM_DATABASE=AIC-7902B U320 + +pci:v00009005d0000801Dsv00001014sd000002CC* + ID_MODEL_FROM_DATABASE=AIC-7902B U320 (ServeRAID 7e) + +pci:v00009005d0000801E* + ID_MODEL_FROM_DATABASE=AIC-7901A U320 + +pci:v00009005d0000801F* + ID_MODEL_FROM_DATABASE=AIC-7902 U320 + +pci:v00009005d0000801Fsv00001734sd00001011* + ID_MODEL_FROM_DATABASE=AIC-7902 U320 (PRIMERGY RX300 onboard SCSI) + +pci:v00009005d00008080* + ID_MODEL_FROM_DATABASE=ASC-29320A U320 w/HostRAID + +pci:v00009005d00008081* + ID_MODEL_FROM_DATABASE=PMC-Sierra PM8001 SAS HBA [Series 6H] + +pci:v00009005d00008088* + ID_MODEL_FROM_DATABASE=PMC-Sierra PM8018 SAS HBA [Series 7H] + +pci:v00009005d00008089* + ID_MODEL_FROM_DATABASE=PMC-Sierra PM8019 SAS encryption HBA [Series 7He] + +pci:v00009005d0000808F* + ID_MODEL_FROM_DATABASE=AIC-7901 U320 w/HostRAID + +pci:v00009005d0000808Fsv00001028sd00000168* + ID_MODEL_FROM_DATABASE=AIC-7901 U320 w/HostRAID (Precision Workstation 670 Mainboard) + +pci:v00009005d00008090* + ID_MODEL_FROM_DATABASE=ASC-39320 U320 w/HostRAID + +pci:v00009005d00008091* + ID_MODEL_FROM_DATABASE=ASC-39320D U320 w/HostRAID + +pci:v00009005d00008092* + ID_MODEL_FROM_DATABASE=ASC-29320 U320 w/HostRAID + +pci:v00009005d00008093* + ID_MODEL_FROM_DATABASE=ASC-29320B U320 w/HostRAID + +pci:v00009005d00008094* + ID_MODEL_FROM_DATABASE=ASC-29320LP U320 w/HostRAID + +pci:v00009005d00008095* + ID_MODEL_FROM_DATABASE=ASC-39320(B) U320 w/HostRAID + +pci:v00009005d00008096* + ID_MODEL_FROM_DATABASE=ASC-39320A U320 w/HostRAID + +pci:v00009005d00008097* + ID_MODEL_FROM_DATABASE=ASC-29320ALP U320 w/HostRAID + +pci:v00009005d0000809C* + ID_MODEL_FROM_DATABASE=ASC-39320D(B) U320 w/HostRAID + +pci:v00009005d0000809D* + ID_MODEL_FROM_DATABASE=AIC-7902(B) U320 w/HostRAID + +pci:v00009005d0000809Dsv00001014sd000002CC* + ID_MODEL_FROM_DATABASE=AIC-7902(B) U320 w/HostRAID (ServeRAID 7e) + +pci:v00009005d0000809E* + ID_MODEL_FROM_DATABASE=AIC-7901A U320 w/HostRAID + +pci:v00009005d0000809F* + ID_MODEL_FROM_DATABASE=AIC-7902 U320 w/HostRAID + +pci:v0000907F* + ID_VENDOR_FROM_DATABASE=Atronics + +pci:v0000907Fd00002015* + ID_MODEL_FROM_DATABASE=IDE-2015PL + +pci:v0000919A* + ID_VENDOR_FROM_DATABASE=Gigapixel Corp + +pci:v00009412* + ID_VENDOR_FROM_DATABASE=Holtek + +pci:v00009412d00006565* + ID_MODEL_FROM_DATABASE=6565 + +pci:v00009413* + ID_VENDOR_FROM_DATABASE=Softlogic Co., Ltd. + +pci:v00009413d00006010* + ID_MODEL_FROM_DATABASE=SOLO6010 MPEG-4 Video encoder/decoder + +pci:v00009413d00006110* + ID_MODEL_FROM_DATABASE=SOLO6110 H.264 Video encoder/decoder + +pci:v00009618* + ID_VENDOR_FROM_DATABASE=JusonTech Corporation + +pci:v00009618d00000001* + ID_MODEL_FROM_DATABASE=JusonTech Gigabit Ethernet Controller + +pci:v00009699* + ID_VENDOR_FROM_DATABASE=Omni Media Technology Inc + +pci:v00009699d00006565* + ID_MODEL_FROM_DATABASE=6565 + +pci:v00009710* + ID_VENDOR_FROM_DATABASE=MosChip Semiconductor Technology Ltd. + +pci:v00009710d00009250* + ID_MODEL_FROM_DATABASE=PCI-to-PCI bridge [MCS9250] + +pci:v00009710d00009805* + ID_MODEL_FROM_DATABASE=PCI 1 port parallel adapter + +pci:v00009710d00009815* + ID_MODEL_FROM_DATABASE=PCI 9815 Multi-I/O Controller + +pci:v00009710d00009815sv00001000sd00000020* + ID_MODEL_FROM_DATABASE=PCI 9815 Multi-I/O Controller (2P0S (2 port parallel adaptor)) + +pci:v00009710d00009820* + ID_MODEL_FROM_DATABASE=PCI 9820 Multi-I/O Controller + +pci:v00009710d00009835* + ID_MODEL_FROM_DATABASE=PCI 9835 Multi-I/O Controller + +pci:v00009710d00009835sv00001000sd00000002* + ID_MODEL_FROM_DATABASE=PCI 9835 Multi-I/O Controller (2S (16C550 UART)) + +pci:v00009710d00009835sv00001000sd00000012* + ID_MODEL_FROM_DATABASE=PCI 9835 Multi-I/O Controller (1P2S) + +pci:v00009710d00009845* + ID_MODEL_FROM_DATABASE=PCI 9845 Multi-I/O Controller + +pci:v00009710d00009845sv00001000sd00000004* + ID_MODEL_FROM_DATABASE=PCI 9845 Multi-I/O Controller (0P4S (4 port 16550A serial card)) + +pci:v00009710d00009845sv00001000sd00000006* + ID_MODEL_FROM_DATABASE=PCI 9845 Multi-I/O Controller (0P6S (6 port 16550a serial card)) + +pci:v00009710d00009845sv00001000sd00000014* + ID_MODEL_FROM_DATABASE=PCI 9845 Multi-I/O Controller (1P4S (1 Parallel / 4 16550A Serial Port Adapter)) + +pci:v00009710d00009855* + ID_MODEL_FROM_DATABASE=PCI 9855 Multi-I/O Controller + +pci:v00009710d00009855sv00001000sd00000014* + ID_MODEL_FROM_DATABASE=PCI 9855 Multi-I/O Controller (1P4S) + +pci:v00009710d00009855sv00001000sd00000022* + ID_MODEL_FROM_DATABASE=PCI 9855 Multi-I/O Controller (2P2S (2 Parallel / 2 16550A Serial Port Adapter)) + +pci:v00009710d00009865* + ID_MODEL_FROM_DATABASE=PCI 9865 Multi-I/O Controller + +pci:v00009710d00009901* + ID_MODEL_FROM_DATABASE=PCIe 9901 Multi-I/O Controller + +pci:v00009710d00009904* + ID_MODEL_FROM_DATABASE=4-Port PCIe Serial Adapter + +pci:v00009710d00009912* + ID_MODEL_FROM_DATABASE=PCIe 9912 Multi-I/O Controller + +pci:v00009710d00009922* + ID_MODEL_FROM_DATABASE=MCS9922 PCIe Multi-I/O Controller + +pci:v00009710d00009990* + ID_MODEL_FROM_DATABASE=MCS9990 PCIe to 4‐Port USB 2.0 Host Controller + +pci:v00009850* + ID_VENDOR_FROM_DATABASE=3Com (wrong ID) + +pci:v00009902* + ID_VENDOR_FROM_DATABASE=Stargen Inc. + +pci:v00009902d00000001* + ID_MODEL_FROM_DATABASE=SG2010 PCI over Starfabric Bridge + +pci:v00009902d00000002* + ID_MODEL_FROM_DATABASE=SG2010 PCI to Starfabric Gateway + +pci:v00009902d00000003* + ID_MODEL_FROM_DATABASE=SG1010 Starfabric Switch and PCI Bridge + +pci:v0000A0A0* + ID_VENDOR_FROM_DATABASE=AOPEN Inc. + +pci:v0000A0F1* + ID_VENDOR_FROM_DATABASE=UNISYS Corporation + +pci:v0000A200* + ID_VENDOR_FROM_DATABASE=NEC Corporation + +pci:v0000A259* + ID_VENDOR_FROM_DATABASE=Hewlett Packard + +pci:v0000A25B* + ID_VENDOR_FROM_DATABASE=Hewlett Packard GmbH PL24-MKT + +pci:v0000A304* + ID_VENDOR_FROM_DATABASE=Sony + +pci:v0000A727* + ID_VENDOR_FROM_DATABASE=3Com Corporation + +pci:v0000A727d00000013* + ID_MODEL_FROM_DATABASE=3CRPAG175 Wireless PC Card + +pci:v0000A727d00006803* + ID_MODEL_FROM_DATABASE=3CRDAG675B Wireless 11a/b/g Adapter + +pci:v0000AA00* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA01* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA02* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA03* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA04* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA05* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA06* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA07* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA08* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA09* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0A* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0B* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0C* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0D* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0E* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA0F* + ID_VENDOR_FROM_DATABASE=iTuner + +pci:v0000AA42* + ID_VENDOR_FROM_DATABASE=Scitex Digital Video + +pci:v0000AA55* + ID_VENDOR_FROM_DATABASE=Ncomputing X300 PCI-Engine + +pci:v0000AAAA* + ID_VENDOR_FROM_DATABASE=Adnaco Technology Inc. + +pci:v0000AAAAd00000001* + ID_MODEL_FROM_DATABASE=H1 PCIe over fiber optic host controller + +pci:v0000AAAAd00000002* + ID_MODEL_FROM_DATABASE=R1BP1 PCIe over fiber optic expansion chassis + +pci:v0000ABCD* + ID_VENDOR_FROM_DATABASE=Vadatech Inc. + +pci:v0000AC1E* + ID_VENDOR_FROM_DATABASE=Digital Receiver Technology Inc + +pci:v0000AC3D* + ID_VENDOR_FROM_DATABASE=Actuality Systems + +pci:v0000AD00* + ID_VENDOR_FROM_DATABASE=Alta Data Technologies LLC + +pci:v0000AECB* + ID_VENDOR_FROM_DATABASE=Adrienne Electronics Corporation + +pci:v0000AECBd00006250* + ID_MODEL_FROM_DATABASE=VITC/LTC Timecode Reader card [PCI-VLTC/RDR] + +pci:v0000AFFE* + ID_VENDOR_FROM_DATABASE=Sirrix AG security technologies + +pci:v0000AFFEd000001E1* + ID_MODEL_FROM_DATABASE=PCI1E1 1-port ISDN E1 interface + +pci:v0000AFFEd000002E1* + ID_MODEL_FROM_DATABASE=PCI2E1 2-port ISDN E1 interface + +pci:v0000AFFEd0000450E* + ID_MODEL_FROM_DATABASE=PCI4S0EC 4-port ISDN S0 interface + +pci:v0000AFFEd0000DEAD* + ID_MODEL_FROM_DATABASE=Sirrix.PCI4S0 4-port ISDN S0 interface + +pci:v0000B100* + ID_VENDOR_FROM_DATABASE=OpenVox Communication Co. Ltd. + +pci:v0000B10B* + ID_VENDOR_FROM_DATABASE=Uakron PCI Project + +pci:v0000B1B3* + ID_VENDOR_FROM_DATABASE=Shiva Europe Limited + +pci:v0000B1D9* + ID_VENDOR_FROM_DATABASE=ATCOM Technology co., LTD. + +pci:v0000BD11* + ID_VENDOR_FROM_DATABASE=Pinnacle Systems, Inc. (Wrong ID) + +pci:v0000BDBD* + ID_VENDOR_FROM_DATABASE=Blackmagic Design + +pci:v0000BDBDd0000A106* + ID_MODEL_FROM_DATABASE=Multibridge Extreme + +pci:v0000BDBDd0000A117* + ID_MODEL_FROM_DATABASE=Intensity Pro + +pci:v0000BDBDd0000A11A* + ID_MODEL_FROM_DATABASE=DeckLink HD Extreme 2 + +pci:v0000BDBDd0000A11B* + ID_MODEL_FROM_DATABASE=DeckLink SDI/Duo/Quad + +pci:v0000BDBDd0000A11C* + ID_MODEL_FROM_DATABASE=DeckLink HD Extreme 3 + +pci:v0000BDBDd0000A11D* + ID_MODEL_FROM_DATABASE=DeckLink Studio + +pci:v0000BDBDd0000A11E* + ID_MODEL_FROM_DATABASE=DeckLink Optical Fibre + +pci:v0000BDBDd0000A120* + ID_MODEL_FROM_DATABASE=Decklink Studio 2 + +pci:v0000BDBDd0000A121* + ID_MODEL_FROM_DATABASE=DeckLink HD Extreme 3D/3D+ + +pci:v0000BDBDd0000A124* + ID_MODEL_FROM_DATABASE=Intensity Extreme + +pci:v0000BDBDd0000A126* + ID_MODEL_FROM_DATABASE=Intensity Shuttle + +pci:v0000BDBDd0000A127* + ID_MODEL_FROM_DATABASE=UltraStudio Express + +pci:v0000BDBDd0000A129* + ID_MODEL_FROM_DATABASE=UltraStudio Mini Monitor + +pci:v0000BDBDd0000A12A* + ID_MODEL_FROM_DATABASE=UltraStudio Mini Recorder + +pci:v0000BDBDd0000A12D* + ID_MODEL_FROM_DATABASE=UltraStudio 4K + +pci:v0000BDBDd0000A12E* + ID_MODEL_FROM_DATABASE=DeckLink 4K Extreme + +pci:v0000BDBDd0000A12F* + ID_MODEL_FROM_DATABASE=DeckLink Mini Monitor + +pci:v0000BDBDd0000A130* + ID_MODEL_FROM_DATABASE=DeckLink Mini Recorder + +pci:v0000BDBDd0000A132* + ID_MODEL_FROM_DATABASE=UltraStudio 4K + +pci:v0000BDBDd0000A136* + ID_MODEL_FROM_DATABASE=DeckLink 4K Extreme 12G + +pci:v0000BDBDd0000A137* + ID_MODEL_FROM_DATABASE=DeckLink Studio 4K + +pci:v0000BDBDd0000A138* + ID_MODEL_FROM_DATABASE=Decklink SDI 4K + +pci:v0000BDBDd0000A139* + ID_MODEL_FROM_DATABASE=Intensity Pro 4K + +pci:v0000BDBDd0000A13B* + ID_MODEL_FROM_DATABASE=DeckLink Micro Recorder + +pci:v0000BDBDd0000A13D* + ID_MODEL_FROM_DATABASE=DeckLink 4K Pro + +pci:v0000BDBDd0000A13E* + ID_MODEL_FROM_DATABASE=UltraStudio 4K Extreme + +pci:v0000BDBDd0000A13F* + ID_MODEL_FROM_DATABASE=DeckLink Quad 2 + +pci:v0000BDBDd0000A140* + ID_MODEL_FROM_DATABASE=DeckLink Duo 2 + +pci:v0000C001* + ID_VENDOR_FROM_DATABASE=TSI Telsys + +pci:v0000C0A9* + ID_VENDOR_FROM_DATABASE=Micron/Crucial Technology + +pci:v0000C0DE* + ID_VENDOR_FROM_DATABASE=Motorola + +pci:v0000C0FE* + ID_VENDOR_FROM_DATABASE=Motion Engineering, Inc. + +pci:v0000CA50* + ID_VENDOR_FROM_DATABASE=Varian Australia Pty Ltd + +pci:v0000CACE* + ID_VENDOR_FROM_DATABASE=CACE Technologies, Inc. + +pci:v0000CACEd00000001* + ID_MODEL_FROM_DATABASE=TurboCap Port A + +pci:v0000CACEd00000002* + ID_MODEL_FROM_DATABASE=TurboCap Port B + +pci:v0000CACEd00000023* + ID_MODEL_FROM_DATABASE=AirPcap N + +pci:v0000CAED* + ID_VENDOR_FROM_DATABASE=Canny Edge + +pci:v0000CAFE* + ID_VENDOR_FROM_DATABASE=Chrysalis-ITS + +pci:v0000CAFEd00000003* + ID_MODEL_FROM_DATABASE=Luna K3 Hardware Security Module + +pci:v0000CAFEd00000006* + ID_MODEL_FROM_DATABASE=Luna PCI-e 3000 Hardware Security Module + +pci:v0000CCCC* + ID_VENDOR_FROM_DATABASE=Catapult Communications + +pci:v0000CCEC* + ID_VENDOR_FROM_DATABASE=Curtiss-Wright Controls Embedded Computing + +pci:v0000CDDD* + ID_VENDOR_FROM_DATABASE=Tyzx, Inc. + +pci:v0000CDDDd00000101* + ID_MODEL_FROM_DATABASE=DeepSea 1 High Speed Stereo Vision Frame Grabber + +pci:v0000CDDDd00000200* + ID_MODEL_FROM_DATABASE=DeepSea 2 High Speed Stereo Vision Frame Grabber + +pci:v0000CEBA* + ID_VENDOR_FROM_DATABASE=KEBA AG + +pci:v0000D161* + ID_VENDOR_FROM_DATABASE=Digium, Inc. + +pci:v0000D161d00000120* + ID_MODEL_FROM_DATABASE=Wildcard TE120P single-span T1/E1/J1 card + +pci:v0000D161d00000205* + ID_MODEL_FROM_DATABASE=Wildcard TE205P/TE207P dual-span T1/E1/J1 card 5.0V + +pci:v0000D161d00000210* + ID_MODEL_FROM_DATABASE=Wildcard TE210P/TE212P dual-span T1/E1/J1 card 3.3V + +pci:v0000D161d00000220* + ID_MODEL_FROM_DATABASE=Wildcard TE220 dual-span T1/E1/J1 card 3.3V (PCI-Express) + +pci:v0000D161d00000405* + ID_MODEL_FROM_DATABASE=Wildcard TE405P/TE407P quad-span T1/E1/J1 card 5.0V + +pci:v0000D161d00000410* + ID_MODEL_FROM_DATABASE=Wildcard TE410P/TE412P quad-span T1/E1/J1 card 3.3V + +pci:v0000D161d00000420* + ID_MODEL_FROM_DATABASE=Wildcard TE420P quad-span T1/E1/J1 card 3.3V (PCI-Express) + +pci:v0000D161d00000800* + ID_MODEL_FROM_DATABASE=Wildcard TDM800P 8-port analog card + +pci:v0000D161d00001205* + ID_MODEL_FROM_DATABASE=Wildcard TE205P/TE207P dual-span T1/E1/J1 card 5.0V (u1) + +pci:v0000D161d00001220* + ID_MODEL_FROM_DATABASE=Wildcard TE220 dual-span T1/E1/J1 card 3.3V (PCI-Express) (5th gen) + +pci:v0000D161d00001405* + ID_MODEL_FROM_DATABASE=Wildcard TE405P/TE407P quad-span T1/E1/J1 card 5.0V (u1) + +pci:v0000D161d00001410* + ID_MODEL_FROM_DATABASE=Wildcard TE410P quad-span T1/E1/J1 card 3.3V (5th Gen) + +pci:v0000D161d00001420* + ID_MODEL_FROM_DATABASE=Wildcard TE420 quad-span T1/E1/J1 card 3.3V (PCI-Express) (5th gen) + +pci:v0000D161d00001820* + ID_MODEL_FROM_DATABASE=Wildcard TE820 octal-span T1/E1/J1 card 3.3V (PCI-Express) + +pci:v0000D161d00002400* + ID_MODEL_FROM_DATABASE=Wildcard TDM2400P 24-port analog card + +pci:v0000D161d00003400* + ID_MODEL_FROM_DATABASE=Wildcard TC400P transcoder base card + +pci:v0000D161d00008000* + ID_MODEL_FROM_DATABASE=Wildcard TE121 single-span T1/E1/J1 card (PCI-Express) + +pci:v0000D161d00008001* + ID_MODEL_FROM_DATABASE=Wildcard TE122 single-span T1/E1/J1 card + +pci:v0000D161d00008002* + ID_MODEL_FROM_DATABASE=Wildcard AEX800 8-port analog card (PCI-Express) + +pci:v0000D161d00008003* + ID_MODEL_FROM_DATABASE=Wildcard AEX2400 24-port analog card (PCI-Express) + +pci:v0000D161d00008004* + ID_MODEL_FROM_DATABASE=Wildcard TCE400P transcoder base card + +pci:v0000D161d00008005* + ID_MODEL_FROM_DATABASE=Wildcard TDM410 4-port analog card + +pci:v0000D161d00008006* + ID_MODEL_FROM_DATABASE=Wildcard AEX410 4-port analog card (PCI-Express) + +pci:v0000D161d00008007* + ID_MODEL_FROM_DATABASE=Hx8 Series 8-port Base Card + +pci:v0000D161d00008008* + ID_MODEL_FROM_DATABASE=Hx8 Series 8-port Base Card (PCI-Express) + +pci:v0000D161d0000800A* + ID_MODEL_FROM_DATABASE=Wildcard TE133 single-span T1/E1/J1 card (PCI Express) + +pci:v0000D161d0000800B* + ID_MODEL_FROM_DATABASE=Wildcard TE134 single-span T1/E1/J1 card + +pci:v0000D161d0000800C* + ID_MODEL_FROM_DATABASE=Wildcard A8A 8-port analog card + +pci:v0000D161d0000800D* + ID_MODEL_FROM_DATABASE=Wildcard A8B 8-port analog card (PCI-Express) + +pci:v0000D161d0000800E* + ID_MODEL_FROM_DATABASE=Wildcard TE235/TE435 quad-span T1/E1/J1 card (PCI-Express) + +pci:v0000D161d0000800F* + ID_MODEL_FROM_DATABASE=Wildcard A4A 4-port analog card + +pci:v0000D161d00008010* + ID_MODEL_FROM_DATABASE=Wildcard A4B 4-port analog card (PCI-Express) + +pci:v0000D161d00008013* + ID_MODEL_FROM_DATABASE=Wildcard TE236/TE436 quad-span T1/E1/J1 card + +pci:v0000D161d0000B410* + ID_MODEL_FROM_DATABASE=Wildcard B410 quad-BRI card + +pci:v0000D4D4* + ID_VENDOR_FROM_DATABASE=Dy4 Systems Inc + +pci:v0000D4D4d00000601* + ID_MODEL_FROM_DATABASE=PCI Mezzanine Card + +pci:v0000D531* + ID_VENDOR_FROM_DATABASE=I+ME ACTIA GmbH + +pci:v0000D84D* + ID_VENDOR_FROM_DATABASE=Exsys + +pci:v0000DADA* + ID_VENDOR_FROM_DATABASE=Datapath Limited + +pci:v0000DADAd00000133* + ID_MODEL_FROM_DATABASE=VisionRGB-X2 + +pci:v0000DADAd00000139* + ID_MODEL_FROM_DATABASE=VisionRGB-E1 + +pci:v0000DADAd00000144* + ID_MODEL_FROM_DATABASE=VisionSD8 + +pci:v0000DADAd00000150* + ID_MODEL_FROM_DATABASE=VisionRGB-E2 + +pci:v0000DADAd00000151* + ID_MODEL_FROM_DATABASE=VisionSD4+1 + +pci:v0000DADAd00000159* + ID_MODEL_FROM_DATABASE=VisionAV + +pci:v0000DADAd00000161* + ID_MODEL_FROM_DATABASE=DGC161 + +pci:v0000DADAd00000165* + ID_MODEL_FROM_DATABASE=DGC165 + +pci:v0000DADAd00000167* + ID_MODEL_FROM_DATABASE=DGC167 + +pci:v0000DADAd00000168* + ID_MODEL_FROM_DATABASE=DGC168 + +pci:v0000DADAd00001139* + ID_MODEL_FROM_DATABASE=VisionRGB-E1S + +pci:v0000DADAd00001150* + ID_MODEL_FROM_DATABASE=VisionRGB-E2S + +pci:v0000DADAd00001151* + ID_MODEL_FROM_DATABASE=VisionSD4+1S + +pci:v0000DADAd00001153* + ID_MODEL_FROM_DATABASE=VisionDVI-DL + +pci:v0000DADAd00001154* + ID_MODEL_FROM_DATABASE=VisionSDI2 + +pci:v0000DB10* + ID_VENDOR_FROM_DATABASE=Diablo Technologies + +pci:v0000DC93* + ID_VENDOR_FROM_DATABASE=Dawicontrol GmbH + +pci:v0000DCBA* + ID_VENDOR_FROM_DATABASE=Dynamic Engineering + +pci:v0000DCBAd00000046* + ID_MODEL_FROM_DATABASE=PCIe Altera Cyclone IV + +pci:v0000DCBAd00000047* + ID_MODEL_FROM_DATABASE=VPX-RCB + +pci:v0000DCBAd00000048* + ID_MODEL_FROM_DATABASE=PMC-Biserial-III-BAE9 + +pci:v0000DCBAd0000004E* + ID_MODEL_FROM_DATABASE=PC104p-Biserial-III-NVY5 + +pci:v0000DCBAd0000004F* + ID_MODEL_FROM_DATABASE=PC104p-Biserial-III-NVY6 + +pci:v0000DCBAd00000052* + ID_MODEL_FROM_DATABASE=PCIeBiSerialDb37 BA22 LVDS IO + +pci:v0000DD01* + ID_VENDOR_FROM_DATABASE=Digital Devices GmbH + +pci:v0000DD01d00000003* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter + +pci:v0000DD01d00000003sv0000DD01sd00000001* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Octopus DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd00000002* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Octopus LE DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd00000003* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Octopus OEM) + +pci:v0000DD01d00000003sv0000DD01sd00000004* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Octopus V3 DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd00000010* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Octopus Mini) + +pci:v0000DD01d00000003sv0000DD01sd00000020* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Cine S2 V6 DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd00000021* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Cine S2 V6.5 DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd00000030* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Cine CT V6.1 DVB adapter) + +pci:v0000DD01d00000003sv0000DD01sd0000DB03* + ID_MODEL_FROM_DATABASE=Octopus DVB Adapter (Mystique SaTiX-S2 V3 DVB adapter) + +pci:v0000DD01d00000006* + ID_MODEL_FROM_DATABASE=Cine V7 + +pci:v0000DD01d00000007* + ID_MODEL_FROM_DATABASE=Max + +pci:v0000DD01d00000007sv0000DD01sd00000023* + ID_MODEL_FROM_DATABASE=Max (S8 4/8) + +pci:v0000DD01d00000011* + ID_MODEL_FROM_DATABASE=Octopus CI DVB Adapter + +pci:v0000DD01d00000011sv0000DD01sd00000040* + ID_MODEL_FROM_DATABASE=Octopus CI DVB Adapter (Octopus CI) + +pci:v0000DD01d00000011sv0000DD01sd00000041* + ID_MODEL_FROM_DATABASE=Octopus CI DVB Adapter (Octopus CI Single) + +pci:v0000DD01d00000201* + ID_MODEL_FROM_DATABASE=Resi DVB-C Modulator + +pci:v0000DD01d00000201sv0000DD01sd00000001* + ID_MODEL_FROM_DATABASE=Resi DVB-C Modulator + +pci:v0000DEAD* + ID_VENDOR_FROM_DATABASE=Indigita Corporation + +pci:v0000DEAF* + ID_VENDOR_FROM_DATABASE=Middle Digital Inc. + +pci:v0000DEAFd00009050* + ID_MODEL_FROM_DATABASE=PC Weasel Virtual VGA + +pci:v0000DEAFd00009051* + ID_MODEL_FROM_DATABASE=PC Weasel Serial Port + +pci:v0000DEAFd00009052* + ID_MODEL_FROM_DATABASE=PC Weasel Watchdog Timer + +pci:v0000DEDA* + ID_VENDOR_FROM_DATABASE=XIMEA + +pci:v0000DEDAd00004001* + ID_MODEL_FROM_DATABASE=Camera CB + +pci:v0000DEDAd00004021* + ID_MODEL_FROM_DATABASE=Camera MT + +pci:v0000E000* + ID_VENDOR_FROM_DATABASE=Winbond + +pci:v0000E000d0000E000* + ID_MODEL_FROM_DATABASE=W89C940 + +pci:v0000E159* + ID_VENDOR_FROM_DATABASE=Tiger Jet Network Inc. + +pci:v0000E159d00000001* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface + +pci:v0000E159d00000001sv00000059sd00000001* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (128k ISDN-S/T Adapter) + +pci:v0000E159d00000001sv00000059sd00000003* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (128k ISDN-U Adapter) + +pci:v0000E159d00000001sv000000A7sd00000001* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (TELES.S0/PCI 2.x ISDN Adapter) + +pci:v0000E159d00000001sv00008086sd00000003* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (Digium X100P/X101P analogue PSTN FXO interface) + +pci:v0000E159d00000001sv0000B100sd00000003* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (OpenVox A400P 4-port analog card) + +pci:v0000E159d00000001sv0000B1D9sd00000003* + ID_MODEL_FROM_DATABASE=Tiger3XX Modem/ISDN interface (AX400P 4-port analog card) + +pci:v0000E159d00000002* + ID_MODEL_FROM_DATABASE=Tiger100APC ISDN chipset + +pci:v0000E1C5* + ID_VENDOR_FROM_DATABASE=Elcus + +pci:v0000E4BF* + ID_VENDOR_FROM_DATABASE=EKF Elektronik GmbH + +pci:v0000E4BFd00000CCD* + ID_MODEL_FROM_DATABASE=CCD-CALYPSO + +pci:v0000E4BFd00000CD1* + ID_MODEL_FROM_DATABASE=CD1-OPERA + +pci:v0000E4BFd00000CD2* + ID_MODEL_FROM_DATABASE=CD2-BEBOP + +pci:v0000E4BFd00000CD3* + ID_MODEL_FROM_DATABASE=CD3-JIVE + +pci:v0000E4BFd000050C1* + ID_MODEL_FROM_DATABASE=PC1-GROOVE + +pci:v0000E4BFd000050C2* + ID_MODEL_FROM_DATABASE=PC2-LIMBO + +pci:v0000E4BFd000053C1* + ID_MODEL_FROM_DATABASE=SC1-ALLEGRO + +pci:v0000E4BFd0000CC47* + ID_MODEL_FROM_DATABASE=CCG-RUMBA + +pci:v0000E4BFd0000CC4D* + ID_MODEL_FROM_DATABASE=CCM-BOOGIE + +pci:v0000E4E4* + ID_VENDOR_FROM_DATABASE=Xorcom + +pci:v0000E55E* + ID_VENDOR_FROM_DATABASE=Essence Technology, Inc. + +pci:v0000EA01* + ID_VENDOR_FROM_DATABASE=Eagle Technology + +pci:v0000EA01d0000000A* + ID_MODEL_FROM_DATABASE=PCI-773 Temperature Card + +pci:v0000EA01d00000032* + ID_MODEL_FROM_DATABASE=PCI-730 & PC104P-30 Card + +pci:v0000EA01d0000003E* + ID_MODEL_FROM_DATABASE=PCI-762 Opto-Isolator Card + +pci:v0000EA01d00000041* + ID_MODEL_FROM_DATABASE=PCI-763 Reed Relay Card + +pci:v0000EA01d00000043* + ID_MODEL_FROM_DATABASE=PCI-769 Opto-Isolator Reed Relay Combo Card + +pci:v0000EA01d00000046* + ID_MODEL_FROM_DATABASE=PCI-766 Analog Output Card + +pci:v0000EA01d00000052* + ID_MODEL_FROM_DATABASE=PCI-703 Analog I/O Card + +pci:v0000EA01d00000800* + ID_MODEL_FROM_DATABASE=PCI-800 Digital I/O Card + +pci:v0000EA60* + ID_VENDOR_FROM_DATABASE=RME + +pci:v0000EA60d00009896* + ID_MODEL_FROM_DATABASE=Digi32 + +pci:v0000EA60d00009897* + ID_MODEL_FROM_DATABASE=Digi32 Pro + +pci:v0000EA60d00009898* + ID_MODEL_FROM_DATABASE=Digi32/8 + +pci:v0000EABB* + ID_VENDOR_FROM_DATABASE=Aashima Technology B.V. + +pci:v0000EACE* + ID_VENDOR_FROM_DATABASE=Endace Measurement Systems, Ltd + +pci:v0000EACEd00003100* + ID_MODEL_FROM_DATABASE=DAG 3.10 OC-3/OC-12 + +pci:v0000EACEd00003200* + ID_MODEL_FROM_DATABASE=DAG 3.2x OC-3/OC-12 + +pci:v0000EACEd0000320E* + ID_MODEL_FROM_DATABASE=DAG 3.2E Fast Ethernet + +pci:v0000EACEd0000340E* + ID_MODEL_FROM_DATABASE=DAG 3.4E Fast Ethernet + +pci:v0000EACEd0000341E* + ID_MODEL_FROM_DATABASE=DAG 3.41E Fast Ethernet + +pci:v0000EACEd00003500* + ID_MODEL_FROM_DATABASE=DAG 3.5 OC-3/OC-12 + +pci:v0000EACEd0000351C* + ID_MODEL_FROM_DATABASE=DAG 3.5ECM Fast Ethernet + +pci:v0000EACEd0000360D* + ID_MODEL_FROM_DATABASE=DAG 3.6D DS3 + +pci:v0000EACEd0000360E* + ID_MODEL_FROM_DATABASE=DAG 3.6E Fast Ethernet + +pci:v0000EACEd0000368E* + ID_MODEL_FROM_DATABASE=DAG 3.6E Gig Ethernet + +pci:v0000EACEd00003707* + ID_MODEL_FROM_DATABASE=DAG 3.7T T1/E1/J1 + +pci:v0000EACEd0000370D* + ID_MODEL_FROM_DATABASE=DAG 3.7D DS3/E3 + +pci:v0000EACEd0000378E* + ID_MODEL_FROM_DATABASE=DAG 3.7G Gig Ethernet + +pci:v0000EACEd00003800* + ID_MODEL_FROM_DATABASE=DAG 3.8S OC-3/OC-12 + +pci:v0000EACEd00004100* + ID_MODEL_FROM_DATABASE=DAG 4.10 OC-48 + +pci:v0000EACEd00004110* + ID_MODEL_FROM_DATABASE=DAG 4.11 OC-48 + +pci:v0000EACEd00004220* + ID_MODEL_FROM_DATABASE=DAG 4.2 OC-48 + +pci:v0000EACEd0000422E* + ID_MODEL_FROM_DATABASE=DAG 4.2GE Gig Ethernet + +pci:v0000EACEd00004230* + ID_MODEL_FROM_DATABASE=DAG 4.2S OC-48 + +pci:v0000EACEd0000423E* + ID_MODEL_FROM_DATABASE=DAG 4.2GE Gig Ethernet + +pci:v0000EACEd00004300* + ID_MODEL_FROM_DATABASE=DAG 4.3S OC-48 + +pci:v0000EACEd0000430E* + ID_MODEL_FROM_DATABASE=DAG 4.3GE Gig Ethernet + +pci:v0000EACEd0000452E* + ID_MODEL_FROM_DATABASE=DAG 4.5G2 Gig Ethernet + +pci:v0000EACEd0000454E* + ID_MODEL_FROM_DATABASE=DAG 4.5G4 Gig Ethernet + +pci:v0000EACEd000045B8* + ID_MODEL_FROM_DATABASE=DAG 4.5Z8 Gig Ethernet + +pci:v0000EACEd000045BE* + ID_MODEL_FROM_DATABASE=DAG 4.5Z2 Gig Ethernet + +pci:v0000EACEd0000520E* + ID_MODEL_FROM_DATABASE=DAG 5.2X 10G Ethernet + +pci:v0000EACEd0000521A* + ID_MODEL_FROM_DATABASE=DAG 5.2SXA 10G Ethernet/OC-192 + +pci:v0000EACEd00005400* + ID_MODEL_FROM_DATABASE=DAG 5.4S-12 OC-3/OC-12 + +pci:v0000EACEd00005401* + ID_MODEL_FROM_DATABASE=DAG 5.4SG-48 Gig Ethernet/OC-3/OC-12/OC-48 + +pci:v0000EACEd0000540A* + ID_MODEL_FROM_DATABASE=DAG 5.4GA Gig Ethernet + +pci:v0000EACEd0000541A* + ID_MODEL_FROM_DATABASE=DAG 5.4SA-12 OC-3/OC-12 + +pci:v0000EACEd0000542A* + ID_MODEL_FROM_DATABASE=DAG 5.4SGA-48 Gig Ethernet/OC-3/OC-12/OC-48 + +pci:v0000EACEd00006000* + ID_MODEL_FROM_DATABASE=DAG 6.0SE 10G Ethernet/OC-192 + +pci:v0000EACEd00006100* + ID_MODEL_FROM_DATABASE=DAG 6.1SE 10G Ethernet/OC-192 + +pci:v0000EACEd00006200* + ID_MODEL_FROM_DATABASE=DAG 6.2SE 10G Ethernet/OC-192 + +pci:v0000EACEd00007100* + ID_MODEL_FROM_DATABASE=DAG 7.1S OC-3/OC-12 + +pci:v0000EACEd00007400* + ID_MODEL_FROM_DATABASE=DAG 7.4S OC-3/OC-12 + +pci:v0000EACEd00007401* + ID_MODEL_FROM_DATABASE=DAG 7.4S48 OC-48 + +pci:v0000EACEd0000752E* + ID_MODEL_FROM_DATABASE=DAG 7.5G2 Gig Ethernet + +pci:v0000EACEd0000754E* + ID_MODEL_FROM_DATABASE=DAG 7.5G4 Gig Ethernet + +pci:v0000EACEd00008100* + ID_MODEL_FROM_DATABASE=DAG 8.1X 10G Ethernet + +pci:v0000EACEd00008101* + ID_MODEL_FROM_DATABASE=DAG 8.1SX 10G Ethernet/OC-192 + +pci:v0000EACEd00008102* + ID_MODEL_FROM_DATABASE=DAG 8.1X 10G Ethernet + +pci:v0000EACEd0000820E* + ID_MODEL_FROM_DATABASE=DAG 8.2X 10G Ethernet + +pci:v0000EACEd0000820F* + ID_MODEL_FROM_DATABASE=DAG 8.2X 10G Ethernet (2nd bus) + +pci:v0000EACEd00008400* + ID_MODEL_FROM_DATABASE=DAG 8.4I Infiniband x4 SDR + +pci:v0000EACEd00008500* + ID_MODEL_FROM_DATABASE=DAG 8.5I Infiniband x4 DDR + +pci:v0000EACEd00009200* + ID_MODEL_FROM_DATABASE=DAG 9.2SX2 10G Ethernet + +pci:v0000EACEd0000920E* + ID_MODEL_FROM_DATABASE=DAG 9.2X2 10G Ethernet + +pci:v0000EACEd0000A120* + ID_MODEL_FROM_DATABASE=DAG 10X2-P 10G Ethernet + +pci:v0000EACEd0000A12E* + ID_MODEL_FROM_DATABASE=DAG 10X2-S 10G Ethernet + +pci:v0000EACEd0000A140* + ID_MODEL_FROM_DATABASE=DAG 10X4-P 10G Ethernet + +pci:v0000EC80* + ID_VENDOR_FROM_DATABASE=Belkin Corporation + +pci:v0000EC80d0000EC00* + ID_MODEL_FROM_DATABASE=F5D6000 + +pci:v0000ECC0* + ID_VENDOR_FROM_DATABASE=Echo Digital Audio Corporation + +pci:v0000EDD8* + ID_VENDOR_FROM_DATABASE=ARK Logic Inc + +pci:v0000EDD8d0000A091* + ID_MODEL_FROM_DATABASE=1000PV [Stingray] + +pci:v0000EDD8d0000A099* + ID_MODEL_FROM_DATABASE=2000PV [Stingray] + +pci:v0000EDD8d0000A0A1* + ID_MODEL_FROM_DATABASE=2000MT + +pci:v0000EDD8d0000A0A9* + ID_MODEL_FROM_DATABASE=2000MI + +pci:v0000F043* + ID_VENDOR_FROM_DATABASE=ASUSTeK Computer Inc. (Wrong ID) + +pci:v0000F05B* + ID_VENDOR_FROM_DATABASE=Foxconn International, Inc. (Wrong ID) + +pci:v0000F1D0* + ID_VENDOR_FROM_DATABASE=AJA Video + +pci:v0000F1D0d0000C0FE* + ID_MODEL_FROM_DATABASE=Xena HS/HD-R + +pci:v0000F1D0d0000C0FF* + ID_MODEL_FROM_DATABASE=Kona/Xena 2 + +pci:v0000F1D0d0000CAFE* + ID_MODEL_FROM_DATABASE=Kona SD + +pci:v0000F1D0d0000CFEE* + ID_MODEL_FROM_DATABASE=Xena LS/SD-22-DA/SD-DA + +pci:v0000F1D0d0000DAFF* + ID_MODEL_FROM_DATABASE=KONA LHi + +pci:v0000F1D0d0000DCAF* + ID_MODEL_FROM_DATABASE=Kona HD + +pci:v0000F1D0d0000DFEE* + ID_MODEL_FROM_DATABASE=Xena HD-DA + +pci:v0000F1D0d0000EFAC* + ID_MODEL_FROM_DATABASE=Xena SD-MM/SD-22-MM + +pci:v0000F1D0d0000FACD* + ID_MODEL_FROM_DATABASE=Xena HD-MM + +pci:v0000F5F5* + ID_VENDOR_FROM_DATABASE=F5 Networks, Inc. + +pci:v0000F849* + ID_VENDOR_FROM_DATABASE=ASRock Incorporation (Wrong ID) + +pci:v0000FA57* + ID_VENDOR_FROM_DATABASE=Interagon AS + +pci:v0000FA57d00000001* + ID_MODEL_FROM_DATABASE=PMC [Pattern Matching Chip] + +pci:v0000FAB7* + ID_VENDOR_FROM_DATABASE=Fabric7 Systems, Inc. + +pci:v0000FEBD* + ID_VENDOR_FROM_DATABASE=Ultraview Corp. + +pci:v0000FEDA* + ID_VENDOR_FROM_DATABASE=Broadcom Inc + +pci:v0000FEDAd0000A0FA* + ID_MODEL_FROM_DATABASE=BCM4210 iLine10 HomePNA 2.0 + +pci:v0000FEDAd0000A10E* + ID_MODEL_FROM_DATABASE=BCM4230 iLine10 HomePNA 2.0 + +pci:v0000FEDE* + ID_VENDOR_FROM_DATABASE=Fedetec Inc. + +pci:v0000FEDEd00000003* + ID_MODEL_FROM_DATABASE=TABIC PCI v3 + +pci:v0000FFFD* + ID_VENDOR_FROM_DATABASE=XenSource, Inc. + +pci:v0000FFFDd00000101* + ID_MODEL_FROM_DATABASE=PCI Event Channel Controller + +pci:v0000FFFE* + ID_VENDOR_FROM_DATABASE=VMWare Inc (temporary ID) + +pci:v0000FFFEd00000710* + ID_MODEL_FROM_DATABASE=Virtual SVGA + +pci:v0000FFFF* + ID_VENDOR_FROM_DATABASE=Illegal Vendor ID diff --git a/src/grp-udev/hwdb/20-sdio-classes.hwdb b/src/grp-udev/hwdb/20-sdio-classes.hwdb new file mode 100644 index 0000000000..72cce9d898 --- /dev/null +++ b/src/grp-udev/hwdb/20-sdio-classes.hwdb @@ -0,0 +1,33 @@ +# This file is part of systemd. +# +# Data imported from: hwdb/sdio.ids + +sdio:c00v*d* + ID_SDIO_CLASS_FROM_DATABASE=Not a SDIO standard interface + +sdio:c01v*d* + ID_SDIO_CLASS_FROM_DATABASE=UART standard interface + +sdio:c02v*d* + ID_SDIO_CLASS_FROM_DATABASE=Bluetooth Type-A standard interface + +sdio:c03v*d* + ID_SDIO_CLASS_FROM_DATABASE=Bluetooth Type-B standard interface + +sdio:c04v*d* + ID_SDIO_CLASS_FROM_DATABASE=GPS standard interface + +sdio:c05v*d* + ID_SDIO_CLASS_FROM_DATABASE=Camera standard interface + +sdio:c06v*d* + ID_SDIO_CLASS_FROM_DATABASE=PHS standard interface + +sdio:c07v*d* + ID_SDIO_CLASS_FROM_DATABASE=WLAN interface + +sdio:c08v*d* + ID_SDIO_CLASS_FROM_DATABASE=Embedded SDIO-ATA standard interface + +sdio:c09v*d* + ID_SDIO_CLASS_FROM_DATABASE=Bluetooth AMP standard interface diff --git a/src/grp-udev/hwdb/20-sdio-vendor-model.hwdb b/src/grp-udev/hwdb/20-sdio-vendor-model.hwdb new file mode 100644 index 0000000000..9cf34b2a39 --- /dev/null +++ b/src/grp-udev/hwdb/20-sdio-vendor-model.hwdb @@ -0,0 +1,207 @@ +# This file is part of systemd. +# +# Data imported from: hwdb/sdio.ids + +sdio:c*v0020* + ID_VENDOR_FROM_DATABASE=ST-Ericsson + +sdio:c*v0020d2280* + ID_MODEL_FROM_DATABASE=CW1200 + +sdio:c*v0089* + ID_VENDOR_FROM_DATABASE=Intel Corp. + +sdio:c*v0092* + ID_VENDOR_FROM_DATABASE=C-guys, Inc. + +sdio:c*v0092d0001* + ID_MODEL_FROM_DATABASE=SD-Link11b WiFi Card (TI ACX100) + +sdio:c*v0092d0004* + ID_MODEL_FROM_DATABASE=EW-CG1102GC + +sdio:c*v0092d0005* + ID_MODEL_FROM_DATABASE=SD FM Radio 2 + +sdio:c*v0092d5544* + ID_MODEL_FROM_DATABASE=SD FM Radio + +sdio:c*v0097* + ID_VENDOR_FROM_DATABASE=Texas Instruments, Inc. + +sdio:c*v0097d4076* + ID_MODEL_FROM_DATABASE=WL1271 + +sdio:c*v0098* + ID_VENDOR_FROM_DATABASE=Toshiba Corp. + +sdio:c*v0098d0001* + ID_MODEL_FROM_DATABASE=SD BT Card 1 + +sdio:c*v0098d0002* + ID_MODEL_FROM_DATABASE=SD BT Card 2 + +sdio:c*v0098d0003* + ID_MODEL_FROM_DATABASE=SD BT Card 3 + +sdio:c*v0104* + ID_VENDOR_FROM_DATABASE=Socket Communications, Inc. + +sdio:c*v0104d005E* + ID_MODEL_FROM_DATABASE=SD Scanner + +sdio:c*v0104d00C5* + ID_MODEL_FROM_DATABASE=Bluetooth SDIO Card + +sdio:c*v0271* + ID_VENDOR_FROM_DATABASE=Atheros Communications, Inc. + +sdio:c*v0271d0108* + ID_MODEL_FROM_DATABASE=AR6001 + +sdio:c*v0271d0109* + ID_MODEL_FROM_DATABASE=AR6001 + +sdio:c*v0271d010A* + ID_MODEL_FROM_DATABASE=AR6001 + +sdio:c*v0271d010B* + ID_MODEL_FROM_DATABASE=AR6001 + +sdio:c*v0296* + ID_VENDOR_FROM_DATABASE=GCT Semiconductor, Inc. + +sdio:c*v0296d5347* + ID_MODEL_FROM_DATABASE=GDM72xx WiMAX + +sdio:c*v02D0* + ID_VENDOR_FROM_DATABASE=Broadcom Corp. + +sdio:c*v02D0d044B* + ID_MODEL_FROM_DATABASE=Nintendo Wii WLAN daughter card + +sdio:c*v02D0dA887* + ID_MODEL_FROM_DATABASE=BCM43143 WLAN card + +sdio:c*v02D0d4324* + ID_MODEL_FROM_DATABASE=BCM43241 WLAN card + +sdio:c*v02D0d4329* + ID_MODEL_FROM_DATABASE=BCM4329 WLAN card + +sdio:c*v02D0d4330* + ID_MODEL_FROM_DATABASE=BCM4330 WLAN card + +sdio:c*v02D0d4334* + ID_MODEL_FROM_DATABASE=BCM4334 WLAN card + +sdio:c*v02D0dA94C* + ID_MODEL_FROM_DATABASE=BCM43340 WLAN card + +sdio:c*v02D0dA94D* + ID_MODEL_FROM_DATABASE=BCM43341 WLAN card + +sdio:c*v02D0d4335* + ID_MODEL_FROM_DATABASE=BCM4335/BCM4339 WLAN card + +sdio:c*v02D0dA962* + ID_MODEL_FROM_DATABASE=BCM43362 WLAN card + +sdio:c*v02D0d4354* + ID_MODEL_FROM_DATABASE=BCM4354 WLAN card + +sdio:c*v02DB* + ID_VENDOR_FROM_DATABASE=SyChip Inc. + +sdio:c*v02DBd0002* + ID_MODEL_FROM_DATABASE=Pegasus WLAN SDIO Card (6060SD) + +sdio:c*v02DF* + ID_VENDOR_FROM_DATABASE=Marvell Technology Group Ltd. + +sdio:c*v02DFd9103* + ID_MODEL_FROM_DATABASE=Libertas + +sdio:c*v02DFd9104* + ID_MODEL_FROM_DATABASE=SD8688 WLAN + +sdio:c*v02DFd9105* + ID_MODEL_FROM_DATABASE=SD8688 BT + +sdio:c*v02DFd9116* + ID_MODEL_FROM_DATABASE=SD8786 WLAN + +sdio:c*v02DFd9119* + ID_MODEL_FROM_DATABASE=SD8787 WLAN + +sdio:c*v02DFd911A* + ID_MODEL_FROM_DATABASE=SD8787 BT + +sdio:c*v02DFd911B* + ID_MODEL_FROM_DATABASE=SD8787 BT AMP + +sdio:c*v02DFd9129* + ID_MODEL_FROM_DATABASE=SD8797 WLAN + +sdio:c*v02DFd912A* + ID_MODEL_FROM_DATABASE=SD8797 BT + +sdio:c*v02DFd912E* + ID_MODEL_FROM_DATABASE=SD8897 BT + +sdio:c*v02DFd912D* + ID_MODEL_FROM_DATABASE=SD8897 WLAN + +sdio:c*v02FE* + ID_VENDOR_FROM_DATABASE=Spectec Computer Co., Ltd + +sdio:c*v02FEd2128* + ID_MODEL_FROM_DATABASE=SDIO WLAN Card (SDW820) + +sdio:c*v032A* + ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio + +sdio:c*v032Ad0001* + ID_MODEL_FROM_DATABASE=UniFi 1 + +sdio:c*v032Ad0002* + ID_MODEL_FROM_DATABASE=UniFi 2 + +sdio:c*v032Ad0007* + ID_MODEL_FROM_DATABASE=UniFi 3 + +sdio:c*v032Ad0008* + ID_MODEL_FROM_DATABASE=UniFi 4 + +sdio:c*v037A* + ID_VENDOR_FROM_DATABASE=MediaTek Inc. + +sdio:c*v037Ad5911* + ID_MODEL_FROM_DATABASE=Spectec WLAN-11b/g + +sdio:c*v039A* + ID_VENDOR_FROM_DATABASE=Siano Mobile Silicon + +sdio:c*v0501* + ID_VENDOR_FROM_DATABASE=Globalsat Technology Co. + +sdio:c*v0501dF501* + ID_MODEL_FROM_DATABASE=SD-501 GPS Card + +sdio:c*v104C* + ID_VENDOR_FROM_DATABASE=Texas Instruments, Inc. + +sdio:c*v104Cd9066* + ID_MODEL_FROM_DATABASE=WL1251 + +sdio:c*v1180* + ID_VENDOR_FROM_DATABASE=Ricoh Co., Ltd + +sdio:c*v1180dE823* + ID_MODEL_FROM_DATABASE=MMC card reader + +sdio:c*v13D1* + ID_VENDOR_FROM_DATABASE=AboCom Systems, Inc. + +sdio:c*v13D1dAC02* + ID_MODEL_FROM_DATABASE=SDW11G diff --git a/src/grp-udev/hwdb/20-usb-classes.hwdb b/src/grp-udev/hwdb/20-usb-classes.hwdb new file mode 100644 index 0000000000..418d39bb84 --- /dev/null +++ b/src/grp-udev/hwdb/20-usb-classes.hwdb @@ -0,0 +1,342 @@ +# This file is part of systemd. +# +# Data imported from: http://www.linux-usb.org/usb.ids + +usb:v*p*d*dc01* + ID_USB_CLASS_FROM_DATABASE=Audio + +usb:v*p*d*dc01dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Control Device + +usb:v*p*d*dc01dsc02* + ID_USB_SUBCLASS_FROM_DATABASE=Streaming + +usb:v*p*d*dc01dsc03* + ID_USB_SUBCLASS_FROM_DATABASE=MIDI Streaming + +usb:v*p*d*dc02* + ID_USB_CLASS_FROM_DATABASE=Communications + +usb:v*p*d*dc02dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Direct Line + +usb:v*p*d*dc02dsc02* + ID_USB_SUBCLASS_FROM_DATABASE=Abstract (modem) + +usb:v*p*d*dc02dsc02dp01* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (v.25ter) + +usb:v*p*d*dc02dsc02dp02* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (PCCA101) + +usb:v*p*d*dc02dsc02dp03* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (PCCA101 + wakeup) + +usb:v*p*d*dc02dsc02dp04* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (GSM) + +usb:v*p*d*dc02dsc02dp05* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (3G) + +usb:v*p*d*dc02dsc02dp06* + ID_USB_PROTOCOL_FROM_DATABASE=AT-commands (CDMA) + +usb:v*p*d*dc02dsc02dpFE* + ID_USB_PROTOCOL_FROM_DATABASE=Defined by command set descriptor + +usb:v*p*d*dc02dsc02dpFF* + ID_USB_PROTOCOL_FROM_DATABASE=Vendor Specific (MSFT RNDIS?) + +usb:v*p*d*dc02dsc03* + ID_USB_SUBCLASS_FROM_DATABASE=Telephone + +usb:v*p*d*dc02dsc04* + ID_USB_SUBCLASS_FROM_DATABASE=Multi-Channel + +usb:v*p*d*dc02dsc05* + ID_USB_SUBCLASS_FROM_DATABASE=CAPI Control + +usb:v*p*d*dc02dsc06* + ID_USB_SUBCLASS_FROM_DATABASE=Ethernet Networking + +usb:v*p*d*dc02dsc07* + ID_USB_SUBCLASS_FROM_DATABASE=ATM Networking + +usb:v*p*d*dc02dsc08* + ID_USB_SUBCLASS_FROM_DATABASE=Wireless Handset Control + +usb:v*p*d*dc02dsc09* + ID_USB_SUBCLASS_FROM_DATABASE=Device Management + +usb:v*p*d*dc02dsc0A* + ID_USB_SUBCLASS_FROM_DATABASE=Mobile Direct Line + +usb:v*p*d*dc02dsc0B* + ID_USB_SUBCLASS_FROM_DATABASE=OBEX + +usb:v*p*d*dc02dsc0C* + ID_USB_SUBCLASS_FROM_DATABASE=Ethernet Emulation + +usb:v*p*d*dc02dsc0Cdp07* + ID_USB_PROTOCOL_FROM_DATABASE=Ethernet Emulation (EEM) + +usb:v*p*d*dc03* + ID_USB_CLASS_FROM_DATABASE=Human Interface Device + +usb:v*p*d*dc03dsc00dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Keyboard + +usb:v*p*d*dc03dsc00dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Mouse + +usb:v*p*d*dc03dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Boot Interface Subclass + +usb:v*p*d*dc03dsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Keyboard + +usb:v*p*d*dc03dsc01dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Mouse + +usb:v*p*d*dc05* + ID_USB_CLASS_FROM_DATABASE=Physical Interface Device + +usb:v*p*d*dc06* + ID_USB_CLASS_FROM_DATABASE=Imaging + +usb:v*p*d*dc06dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Still Image Capture + +usb:v*p*d*dc06dsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Picture Transfer Protocol (PIMA 15470) + +usb:v*p*d*dc07* + ID_USB_CLASS_FROM_DATABASE=Printer + +usb:v*p*d*dc07dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Printer + +usb:v*p*d*dc07dsc01dp00* + ID_USB_PROTOCOL_FROM_DATABASE=Reserved/Undefined + +usb:v*p*d*dc07dsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Unidirectional + +usb:v*p*d*dc07dsc01dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Bidirectional + +usb:v*p*d*dc07dsc01dp03* + ID_USB_PROTOCOL_FROM_DATABASE=IEEE 1284.4 compatible bidirectional + +usb:v*p*d*dc07dsc01dpFF* + ID_USB_PROTOCOL_FROM_DATABASE=Vendor Specific + +usb:v*p*d*dc08* + ID_USB_CLASS_FROM_DATABASE=Mass Storage + +usb:v*p*d*dc08dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=RBC (typically Flash) + +usb:v*p*d*dc08dsc01dp00* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk/Interrupt + +usb:v*p*d*dc08dsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk + +usb:v*p*d*dc08dsc01dp50* + ID_USB_PROTOCOL_FROM_DATABASE=Bulk-Only + +usb:v*p*d*dc08dsc02* + ID_USB_SUBCLASS_FROM_DATABASE=SFF-8020i, MMC-2 (ATAPI) + +usb:v*p*d*dc08dsc03* + ID_USB_SUBCLASS_FROM_DATABASE=QIC-157 + +usb:v*p*d*dc08dsc04* + ID_USB_SUBCLASS_FROM_DATABASE=Floppy (UFI) + +usb:v*p*d*dc08dsc04dp00* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk/Interrupt + +usb:v*p*d*dc08dsc04dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk + +usb:v*p*d*dc08dsc04dp50* + ID_USB_PROTOCOL_FROM_DATABASE=Bulk-Only + +usb:v*p*d*dc08dsc05* + ID_USB_SUBCLASS_FROM_DATABASE=SFF-8070i + +usb:v*p*d*dc08dsc06* + ID_USB_SUBCLASS_FROM_DATABASE=SCSI + +usb:v*p*d*dc08dsc06dp00* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk/Interrupt + +usb:v*p*d*dc08dsc06dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Control/Bulk + +usb:v*p*d*dc08dsc06dp50* + ID_USB_PROTOCOL_FROM_DATABASE=Bulk-Only + +usb:v*p*d*dc09* + ID_USB_CLASS_FROM_DATABASE=Hub + +usb:v*p*d*dc09dsc00dp00* + ID_USB_PROTOCOL_FROM_DATABASE=Full speed (or root) hub + +usb:v*p*d*dc09dsc00dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Single TT + +usb:v*p*d*dc09dsc00dp02* + ID_USB_PROTOCOL_FROM_DATABASE=TT per port + +usb:v*p*d*dc0A* + ID_USB_CLASS_FROM_DATABASE=CDC Data + +usb:v*p*d*dc0Adsc00dp30* + ID_USB_PROTOCOL_FROM_DATABASE=I.430 ISDN BRI + +usb:v*p*d*dc0Adsc00dp31* + ID_USB_PROTOCOL_FROM_DATABASE=HDLC + +usb:v*p*d*dc0Adsc00dp32* + ID_USB_PROTOCOL_FROM_DATABASE=Transparent + +usb:v*p*d*dc0Adsc00dp50* + ID_USB_PROTOCOL_FROM_DATABASE=Q.921M + +usb:v*p*d*dc0Adsc00dp51* + ID_USB_PROTOCOL_FROM_DATABASE=Q.921 + +usb:v*p*d*dc0Adsc00dp52* + ID_USB_PROTOCOL_FROM_DATABASE=Q.921TM + +usb:v*p*d*dc0Adsc00dp90* + ID_USB_PROTOCOL_FROM_DATABASE=V.42bis + +usb:v*p*d*dc0Adsc00dp91* + ID_USB_PROTOCOL_FROM_DATABASE=Q.932 EuroISDN + +usb:v*p*d*dc0Adsc00dp92* + ID_USB_PROTOCOL_FROM_DATABASE=V.120 V.24 rate ISDN + +usb:v*p*d*dc0Adsc00dp93* + ID_USB_PROTOCOL_FROM_DATABASE=CAPI 2.0 + +usb:v*p*d*dc0Adsc00dpFD* + ID_USB_PROTOCOL_FROM_DATABASE=Host Based Driver + +usb:v*p*d*dc0Adsc00dpFE* + ID_USB_PROTOCOL_FROM_DATABASE=CDC PUF + +usb:v*p*d*dc0Adsc00dpFF* + ID_USB_PROTOCOL_FROM_DATABASE=Vendor specific + +usb:v*p*d*dc0B* + ID_USB_CLASS_FROM_DATABASE=Chip/SmartCard + +usb:v*p*d*dc0D* + ID_USB_CLASS_FROM_DATABASE=Content Security + +usb:v*p*d*dc0E* + ID_USB_CLASS_FROM_DATABASE=Video + +usb:v*p*d*dc0Edsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Video Control + +usb:v*p*d*dc0Edsc02* + ID_USB_SUBCLASS_FROM_DATABASE=Video Streaming + +usb:v*p*d*dc0Edsc03* + ID_USB_SUBCLASS_FROM_DATABASE=Video Interface Collection + +usb:v*p*d*dc58* + ID_USB_CLASS_FROM_DATABASE=Xbox + +usb:v*p*d*dc58dsc42* + ID_USB_SUBCLASS_FROM_DATABASE=Controller + +usb:v*p*d*dcDC* + ID_USB_CLASS_FROM_DATABASE=Diagnostic + +usb:v*p*d*dcDCdsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Reprogrammable Diagnostics + +usb:v*p*d*dcDCdsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=USB2 Compliance + +usb:v*p*d*dcE0* + ID_USB_CLASS_FROM_DATABASE=Wireless + +usb:v*p*d*dcE0dsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency + +usb:v*p*d*dcE0dsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth + +usb:v*p*d*dcE0dsc01dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Ultra WideBand Radio Control + +usb:v*p*d*dcE0dsc01dp03* + ID_USB_PROTOCOL_FROM_DATABASE=RNDIS + +usb:v*p*d*dcE0dsc02* + ID_USB_SUBCLASS_FROM_DATABASE=Wireless USB Wire Adapter + +usb:v*p*d*dcE0dsc02dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Host Wire Adapter Control/Data Streaming + +usb:v*p*d*dcE0dsc02dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Device Wire Adapter Control/Data Streaming + +usb:v*p*d*dcE0dsc02dp03* + ID_USB_PROTOCOL_FROM_DATABASE=Device Wire Adapter Isochronous Streaming + +usb:v*p*d*dcEF* + ID_USB_CLASS_FROM_DATABASE=Miscellaneous Device + +usb:v*p*d*dcEFdsc01dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Microsoft ActiveSync + +usb:v*p*d*dcEFdsc01dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Palm Sync + +usb:v*p*d*dcEFdsc02dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Interface Association + +usb:v*p*d*dcEFdsc02dp02* + ID_USB_PROTOCOL_FROM_DATABASE=Wire Adapter Multifunction Peripheral + +usb:v*p*d*dcEFdsc03dp01* + ID_USB_PROTOCOL_FROM_DATABASE=Cable Based Association + +usb:v*p*d*dcEFdsc05* + ID_USB_SUBCLASS_FROM_DATABASE=USB3 Vision + +usb:v*p*d*dcFE* + ID_USB_CLASS_FROM_DATABASE=Application Specific Interface + +usb:v*p*d*dcFEdsc01* + ID_USB_SUBCLASS_FROM_DATABASE=Device Firmware Update + +usb:v*p*d*dcFEdsc02* + ID_USB_SUBCLASS_FROM_DATABASE=IRDA Bridge + +usb:v*p*d*dcFEdsc03* + ID_USB_SUBCLASS_FROM_DATABASE=Test and Measurement + +usb:v*p*d*dcFEdsc03dp01* + ID_USB_PROTOCOL_FROM_DATABASE=TMC + +usb:v*p*d*dcFEdsc03dp02* + ID_USB_PROTOCOL_FROM_DATABASE=USB488 + +usb:v*p*d*dcFF* + ID_USB_CLASS_FROM_DATABASE=Vendor Specific Class + +usb:v*p*d*dcFFdscFF* + ID_USB_SUBCLASS_FROM_DATABASE=Vendor Specific Subclass + +usb:v*p*d*dcFFdscFFdpFF* + ID_USB_PROTOCOL_FROM_DATABASE=Vendor Specific Protocol diff --git a/src/grp-udev/hwdb/20-usb-vendor-model.hwdb b/src/grp-udev/hwdb/20-usb-vendor-model.hwdb new file mode 100644 index 0000000000..fec0fb4daa --- /dev/null +++ b/src/grp-udev/hwdb/20-usb-vendor-model.hwdb @@ -0,0 +1,55296 @@ +# This file is part of systemd. +# +# Data imported from: http://www.linux-usb.org/usb.ids + +usb:v0001* + ID_VENDOR_FROM_DATABASE=Fry's Electronics + +usb:v0001p7778* + ID_MODEL_FROM_DATABASE=Counterfeit flash drive [Kingston] + +usb:v0002* + ID_VENDOR_FROM_DATABASE=Ingram + +usb:v0003* + ID_VENDOR_FROM_DATABASE=Club Mac + +usb:v0004* + ID_VENDOR_FROM_DATABASE=Nebraska Furniture Mart + +usb:v0011* + ID_VENDOR_FROM_DATABASE=Unknown + +usb:v0011p7788* + ID_MODEL_FROM_DATABASE=counterfeit flash drive + +usb:v0053* + ID_VENDOR_FROM_DATABASE=Planex + +usb:v0053p5301* + ID_MODEL_FROM_DATABASE=GW-US54ZGL 802.11bg + +usb:v0079* + ID_VENDOR_FROM_DATABASE=DragonRise Inc. + +usb:v0079p0006* + ID_MODEL_FROM_DATABASE=PC TWIN SHOCK Gamepad + +usb:v0079p0011* + ID_MODEL_FROM_DATABASE=Gamepad + +usb:v0105* + ID_VENDOR_FROM_DATABASE=Trust International B.V. + +usb:v0105p145F* + ID_MODEL_FROM_DATABASE=NW-3100 802.11b/g 54Mbps Wireless Network Adapter [zd1211] + +usb:v0127* + ID_VENDOR_FROM_DATABASE=IBP + +usb:v0127p0002* + ID_MODEL_FROM_DATABASE=HDM Interface + +usb:v0145* + ID_VENDOR_FROM_DATABASE=Unknown + +usb:v0145p0112* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v017C* + ID_VENDOR_FROM_DATABASE=MLK + +usb:v017Cp145F* + ID_MODEL_FROM_DATABASE=Trust Deskset + +usb:v0200* + ID_VENDOR_FROM_DATABASE=TP-Link + +usb:v0200p0201* + ID_MODEL_FROM_DATABASE=MA180 UMTS Modem + +usb:v0204* + ID_VENDOR_FROM_DATABASE=Chipsbank Microelectronics Co., Ltd + +usb:v0204p6025* + ID_MODEL_FROM_DATABASE=CBM2080 / CBM2090 Flash drive controller + +usb:v0204p6026* + ID_MODEL_FROM_DATABASE=CBM1180 Flash drive controller + +usb:v0218* + ID_VENDOR_FROM_DATABASE=Hangzhou Worlde + +usb:v0218p0301* + ID_MODEL_FROM_DATABASE=MIDI Port + +usb:v02AD* + ID_VENDOR_FROM_DATABASE=HUMAX Co., Ltd. + +usb:v02ADp138C* + ID_MODEL_FROM_DATABASE=PVR Mass Storage + +usb:v0300* + ID_VENDOR_FROM_DATABASE=MM300 eBook Reader + +usb:v0324* + ID_VENDOR_FROM_DATABASE=OCZ Technology Inc + +usb:v0324pBC06* + ID_MODEL_FROM_DATABASE=OCZ ATV USB 2.0 Flash Drive + +usb:v0324pBC08* + ID_MODEL_FROM_DATABASE=OCZ Rally2/ATV USB 2.0 Flash Drive + +usb:v0325* + ID_VENDOR_FROM_DATABASE=OCZ Technology Inc + +usb:v0325pAC02* + ID_MODEL_FROM_DATABASE=ATV Turbo / Rally2 Dual Channel USB 2.0 Flash Drive + +usb:v0386* + ID_VENDOR_FROM_DATABASE=LTS + +usb:v0386p0001* + ID_MODEL_FROM_DATABASE=PSX for USB Converter + +usb:v03D9* + ID_VENDOR_FROM_DATABASE=Shenzhen Sinote Tech-Electron Co., Ltd + +usb:v03D9p0499* + ID_MODEL_FROM_DATABASE=SE340D PC Remote Control + +usb:v03DA* + ID_VENDOR_FROM_DATABASE=Bernd Walter Computer Technology + +usb:v03DAp0002* + ID_MODEL_FROM_DATABASE=HD44780 LCD interface + +usb:v03E8* + ID_VENDOR_FROM_DATABASE=EndPoints, Inc. + +usb:v03E8p0004* + ID_MODEL_FROM_DATABASE=SE401 Webcam + +usb:v03E8p0008* + ID_MODEL_FROM_DATABASE=101 Ethernet [klsi] + +usb:v03E8p0015* + ID_MODEL_FROM_DATABASE=ATAPI Enclosure + +usb:v03E8p2123* + ID_MODEL_FROM_DATABASE=SiPix StyleCam Deluxe + +usb:v03E8p8004* + ID_MODEL_FROM_DATABASE=Aox 99001 + +usb:v03E9* + ID_VENDOR_FROM_DATABASE=Thesys Microelectronics + +usb:v03EA* + ID_VENDOR_FROM_DATABASE=Data Broadcasting Corp. + +usb:v03EB* + ID_VENDOR_FROM_DATABASE=Atmel Corp. + +usb:v03EBp0902* + ID_MODEL_FROM_DATABASE=4-Port Hub + +usb:v03EBp2002* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v03EBp2015* + ID_MODEL_FROM_DATABASE=at90usbkey sample firmware (HID keyboard) + +usb:v03EBp2018* + ID_MODEL_FROM_DATABASE=at90usbkey sample firmware (CDC ACM) + +usb:v03EBp2019* + ID_MODEL_FROM_DATABASE=stk525 sample firmware (microphone) + +usb:v03EBp201C* + ID_MODEL_FROM_DATABASE=at90usbkey sample firmware (HID mouse) + +usb:v03EBp201D* + ID_MODEL_FROM_DATABASE=at90usbkey sample firmware (HID generic) + +usb:v03EBp2022* + ID_MODEL_FROM_DATABASE=at90usbkey sample firmware (composite device) + +usb:v03EBp2040* + ID_MODEL_FROM_DATABASE=LUFA Test PID + +usb:v03EBp2041* + ID_MODEL_FROM_DATABASE=LUFA Mouse Demo Application + +usb:v03EBp2042* + ID_MODEL_FROM_DATABASE=LUFA Keyboard Demo Application + +usb:v03EBp2043* + ID_MODEL_FROM_DATABASE=LUFA Joystick Demo Application + +usb:v03EBp2044* + ID_MODEL_FROM_DATABASE=LUFA CDC Demo Application + +usb:v03EBp2045* + ID_MODEL_FROM_DATABASE=LUFA Mass Storage Demo Application + +usb:v03EBp2046* + ID_MODEL_FROM_DATABASE=LUFA Audio Output Demo Application + +usb:v03EBp2047* + ID_MODEL_FROM_DATABASE=LUFA Audio Input Demo Application + +usb:v03EBp2048* + ID_MODEL_FROM_DATABASE=LUFA MIDI Demo Application + +usb:v03EBp2049* + ID_MODEL_FROM_DATABASE=Stripe Snoop Magnetic Stripe Reader + +usb:v03EBp204A* + ID_MODEL_FROM_DATABASE=LUFA CDC Class Bootloader + +usb:v03EBp204B* + ID_MODEL_FROM_DATABASE=LUFA USB to Serial Adapter Project + +usb:v03EBp204C* + ID_MODEL_FROM_DATABASE=LUFA RNDIS Demo Application + +usb:v03EBp204D* + ID_MODEL_FROM_DATABASE=LUFA Combined Mouse and Keyboard Demo Application + +usb:v03EBp204E* + ID_MODEL_FROM_DATABASE=LUFA Dual CDC Demo Application + +usb:v03EBp204F* + ID_MODEL_FROM_DATABASE=LUFA Generic HID Demo Application + +usb:v03EBp2060* + ID_MODEL_FROM_DATABASE=Benito Programmer Project + +usb:v03EBp2061* + ID_MODEL_FROM_DATABASE=LUFA Combined Mass Storage and Keyboard Demo Application + +usb:v03EBp2062* + ID_MODEL_FROM_DATABASE=LUFA Combined CDC and Mouse Demo Application + +usb:v03EBp2063* + ID_MODEL_FROM_DATABASE=LUFA Datalogger Device + +usb:v03EBp2064* + ID_MODEL_FROM_DATABASE=Interfaceless Control-Only LUFA Devices + +usb:v03EBp2065* + ID_MODEL_FROM_DATABASE=LUFA Test and Measurement Demo Application + +usb:v03EBp2066* + ID_MODEL_FROM_DATABASE=LUFA Multiple Report HID Demo + +usb:v03EBp2068* + ID_MODEL_FROM_DATABASE=LUFA Virtual Serial/Mass Storage Demo + +usb:v03EBp2069* + ID_MODEL_FROM_DATABASE=LUFA Webserver Project + +usb:v03EBp2103* + ID_MODEL_FROM_DATABASE=JTAG ICE mkII + +usb:v03EBp2104* + ID_MODEL_FROM_DATABASE=AVR ISP mkII + +usb:v03EBp2105* + ID_MODEL_FROM_DATABASE=AVRONE! + +usb:v03EBp2106* + ID_MODEL_FROM_DATABASE=STK600 development board + +usb:v03EBp2107* + ID_MODEL_FROM_DATABASE=AVR Dragon + +usb:v03EBp2109* + ID_MODEL_FROM_DATABASE=STK541 ZigBee Development Board + +usb:v03EBp210D* + ID_MODEL_FROM_DATABASE=XPLAIN evaluation kit (CDC ACM) + +usb:v03EBp2110* + ID_MODEL_FROM_DATABASE=AVR JTAGICE3 Debugger and Programmer + +usb:v03EBp2111* + ID_MODEL_FROM_DATABASE=Xplained Pro board debugger and programmer + +usb:v03EBp2122* + ID_MODEL_FROM_DATABASE=XMEGA-A1 Explained evaluation kit + +usb:v03EBp2140* + ID_MODEL_FROM_DATABASE=AVR JTAGICE3 (v3.x) Debugger and Programmer + +usb:v03EBp2141* + ID_MODEL_FROM_DATABASE=ICE debugger + +usb:v03EBp2310* + ID_MODEL_FROM_DATABASE=EVK11xx evaluation board + +usb:v03EBp2FE4* + ID_MODEL_FROM_DATABASE=ATxmega32A4U DFU bootloader + +usb:v03EBp2FE6* + ID_MODEL_FROM_DATABASE=Cactus V6 (DFU) + +usb:v03EBp2FEA* + ID_MODEL_FROM_DATABASE=Cactus RF60 (DFU) + +usb:v03EBp2FEE* + ID_MODEL_FROM_DATABASE=atmega8u2 DFU bootloader + +usb:v03EBp2FEF* + ID_MODEL_FROM_DATABASE=atmega16u2 DFU bootloader + +usb:v03EBp2FF0* + ID_MODEL_FROM_DATABASE=atmega32u2 DFU bootloader + +usb:v03EBp2FF1* + ID_MODEL_FROM_DATABASE=at32uc3a3 DFU bootloader + +usb:v03EBp2FF3* + ID_MODEL_FROM_DATABASE=atmega16u4 DFU bootloader + +usb:v03EBp2FF4* + ID_MODEL_FROM_DATABASE=atmega32u4 DFU bootloader + +usb:v03EBp2FF6* + ID_MODEL_FROM_DATABASE=at32uc3b0/1 DFU bootloader + +usb:v03EBp2FF7* + ID_MODEL_FROM_DATABASE=at90usb82 DFU bootloader + +usb:v03EBp2FF8* + ID_MODEL_FROM_DATABASE=at32uc3a0/1 DFU bootloader + +usb:v03EBp2FF9* + ID_MODEL_FROM_DATABASE=at90usb646/647 DFU bootloader + +usb:v03EBp2FFA* + ID_MODEL_FROM_DATABASE=at90usb162 DFU bootloader + +usb:v03EBp2FFB* + ID_MODEL_FROM_DATABASE=at90usb AVR DFU bootloader + +usb:v03EBp2FFD* + ID_MODEL_FROM_DATABASE=at89c5130/c5131 DFU bootloader + +usb:v03EBp2FFF* + ID_MODEL_FROM_DATABASE=at89c5132/c51snd1c DFU bootloader + +usb:v03EBp3301* + ID_MODEL_FROM_DATABASE=at43301 4-Port Hub + +usb:v03EBp3312* + ID_MODEL_FROM_DATABASE=4-Port Hub + +usb:v03EBp4102* + ID_MODEL_FROM_DATABASE=AirVast W-Buddie WN210 + +usb:v03EBp5601* + ID_MODEL_FROM_DATABASE=at76c510 Prism-II 802.11b Access Point + +usb:v03EBp5603* + ID_MODEL_FROM_DATABASE=Cisco 7920 WiFi IP Phone + +usb:v03EBp6119* + ID_MODEL_FROM_DATABASE=AT91SAM CDC Demo Application + +usb:v03EBp6124* + ID_MODEL_FROM_DATABASE=at91sam SAMBA bootloader + +usb:v03EBp6127* + ID_MODEL_FROM_DATABASE=AT91SAM HID Keyboard Demo Application + +usb:v03EBp6129* + ID_MODEL_FROM_DATABASE=AT91SAM Mass Storage Demo Application + +usb:v03EBp6200* + ID_MODEL_FROM_DATABASE=AT91SAM HID Mouse Demo Application + +usb:v03EBp7603* + ID_MODEL_FROM_DATABASE=D-Link DWL-120 802.11b Wireless Adapter [Atmel at76c503a] + +usb:v03EBp7604* + ID_MODEL_FROM_DATABASE=at76c503a 802.11b Adapter + +usb:v03EBp7605* + ID_MODEL_FROM_DATABASE=at76c503a 802.11b Adapter + +usb:v03EBp7606* + ID_MODEL_FROM_DATABASE=at76c505 802.11b Adapter + +usb:v03EBp7611* + ID_MODEL_FROM_DATABASE=at76c510 rfmd2948 802.11b Access Point + +usb:v03EBp7613* + ID_MODEL_FROM_DATABASE=WL-1130 USB + +usb:v03EBp7614* + ID_MODEL_FROM_DATABASE=AT76c505a Wireless Adapter + +usb:v03EBp7615* + ID_MODEL_FROM_DATABASE=AT76C505AMX Wireless Adapter + +usb:v03EBp7617* + ID_MODEL_FROM_DATABASE=AT76C505AS Wireless Adapter + +usb:v03EBp7800* + ID_MODEL_FROM_DATABASE=Mini Album + +usb:v03EBpFF07* + ID_MODEL_FROM_DATABASE=Tux Droid fish dongle + +usb:v03EC* + ID_VENDOR_FROM_DATABASE=Iwatsu America, Inc. + +usb:v03ED* + ID_VENDOR_FROM_DATABASE=Mitel Corp. + +usb:v03EE* + ID_VENDOR_FROM_DATABASE=Mitsumi + +usb:v03EEp0000* + ID_MODEL_FROM_DATABASE=CD-R/RW Drive + +usb:v03EEp2501* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v03EEp2502* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v03EEp5609* + ID_MODEL_FROM_DATABASE=Japanese Keyboard + +usb:v03EEp641F* + ID_MODEL_FROM_DATABASE=WIF-0402C Bluetooth Adapter + +usb:v03EEp6438* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v03EEp6440* + ID_MODEL_FROM_DATABASE=WML-C52APR Bluetooth Adapter + +usb:v03EEp6901* + ID_MODEL_FROM_DATABASE=SmartDisk FDD + +usb:v03EEp6902* + ID_MODEL_FROM_DATABASE=Floppy Disk Drive + +usb:v03EEp7500* + ID_MODEL_FROM_DATABASE=CD-R/RW + +usb:v03EEpFFFF* + ID_MODEL_FROM_DATABASE=Dongle with BlueCore in DFU mode + +usb:v03F0* + ID_VENDOR_FROM_DATABASE=Hewlett-Packard + +usb:v03F0p0004* + ID_MODEL_FROM_DATABASE=DeskJet 895c + +usb:v03F0p0011* + ID_MODEL_FROM_DATABASE=OfficeJet G55 + +usb:v03F0p0012* + ID_MODEL_FROM_DATABASE=DeskJet 1125C Printer Port + +usb:v03F0p0024* + ID_MODEL_FROM_DATABASE=KU-0316 Keyboard + +usb:v03F0p002A* + ID_MODEL_FROM_DATABASE=LaserJet P1102 + +usb:v03F0p0101* + ID_MODEL_FROM_DATABASE=ScanJet 4100c + +usb:v03F0p0102* + ID_MODEL_FROM_DATABASE=PhotoSmart S20 + +usb:v03F0p0104* + ID_MODEL_FROM_DATABASE=DeskJet 880c/970c + +usb:v03F0p0105* + ID_MODEL_FROM_DATABASE=ScanJet 4200c + +usb:v03F0p0107* + ID_MODEL_FROM_DATABASE=CD-Writer Plus + +usb:v03F0p010C* + ID_MODEL_FROM_DATABASE=Multimedia Keyboard Hub + +usb:v03F0p0111* + ID_MODEL_FROM_DATABASE=G55xi Printer/Scanner/Copier + +usb:v03F0p0117* + ID_MODEL_FROM_DATABASE=LaserJet 3200 + +usb:v03F0p011C* + ID_MODEL_FROM_DATABASE=hn210w 802.11b Adapter + +usb:v03F0p011D* + ID_MODEL_FROM_DATABASE=Bluetooth 1.2 Interface [Broadcom BCM2035] + +usb:v03F0p0121* + ID_MODEL_FROM_DATABASE=HP 39g+ [F2224A], 39gs [F2223A], 40gs [F2225A], 48gII [F2226A], 49g+ [F2228A], 50g [F2229A, NW240AA] + +usb:v03F0p0122* + ID_MODEL_FROM_DATABASE=HID Internet Keyboard + +usb:v03F0p0125* + ID_MODEL_FROM_DATABASE=DAT72 Tape + +usb:v03F0p0139* + ID_MODEL_FROM_DATABASE=Barcode Scanner 4430 + +usb:v03F0p0201* + ID_MODEL_FROM_DATABASE=ScanJet 6200c + +usb:v03F0p0202* + ID_MODEL_FROM_DATABASE=PhotoSmart S20 + +usb:v03F0p0204* + ID_MODEL_FROM_DATABASE=DeskJet 815c + +usb:v03F0p0205* + ID_MODEL_FROM_DATABASE=ScanJet 3300c + +usb:v03F0p0207* + ID_MODEL_FROM_DATABASE=CD-Writer Plus 8200e + +usb:v03F0p020C* + ID_MODEL_FROM_DATABASE=Multimedia Keyboard + +usb:v03F0p0211* + ID_MODEL_FROM_DATABASE=OfficeJet G85 + +usb:v03F0p0212* + ID_MODEL_FROM_DATABASE=DeskJet 1220C + +usb:v03F0p0217* + ID_MODEL_FROM_DATABASE=LaserJet 2200 + +usb:v03F0p0218* + ID_MODEL_FROM_DATABASE=APOLLO P2500/2600 + +usb:v03F0p0221* + ID_MODEL_FROM_DATABASE=StreamSmart 400 [F2235AA] + +usb:v03F0p022A* + ID_MODEL_FROM_DATABASE=Laserjet CP1525nw + +usb:v03F0p0241* + ID_MODEL_FROM_DATABASE=Link-5 micro dongle + +usb:v03F0p0304* + ID_MODEL_FROM_DATABASE=DeskJet 810c/812c + +usb:v03F0p0305* + ID_MODEL_FROM_DATABASE=ScanJet 4300c + +usb:v03F0p0307* + ID_MODEL_FROM_DATABASE=CD-Writer+ CD-4e + +usb:v03F0p0311* + ID_MODEL_FROM_DATABASE=OfficeJet G85xi + +usb:v03F0p0312* + ID_MODEL_FROM_DATABASE=Color Inkjet CP1700 + +usb:v03F0p0314* + ID_MODEL_FROM_DATABASE=designjet 30/130 series + +usb:v03F0p0317* + ID_MODEL_FROM_DATABASE=LaserJet 1200 + +usb:v03F0p0324* + ID_MODEL_FROM_DATABASE=SK-2885 keyboard + +usb:v03F0p034A* + ID_MODEL_FROM_DATABASE=Elite Keyboard + +usb:v03F0p0401* + ID_MODEL_FROM_DATABASE=ScanJet 5200c + +usb:v03F0p0404* + ID_MODEL_FROM_DATABASE=DeskJet 830c/832c + +usb:v03F0p0405* + ID_MODEL_FROM_DATABASE=ScanJet 3400cse + +usb:v03F0p0411* + ID_MODEL_FROM_DATABASE=OfficeJet G95 + +usb:v03F0p0412* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p0417* + ID_MODEL_FROM_DATABASE=LaserJet 1200 series + +usb:v03F0p0423* + ID_MODEL_FROM_DATABASE=HS-COMBO Cardreader + +usb:v03F0p042A* + ID_MODEL_FROM_DATABASE=LaserJet M1132 MFP + +usb:v03F0p0441* + ID_MODEL_FROM_DATABASE=Prime [NW280AA, G8X92AA] + +usb:v03F0p0504* + ID_MODEL_FROM_DATABASE=DeskJet 885c + +usb:v03F0p0505* + ID_MODEL_FROM_DATABASE=ScanJet 2100c + +usb:v03F0p0507* + ID_MODEL_FROM_DATABASE=DVD+RW + +usb:v03F0p050C* + ID_MODEL_FROM_DATABASE=5219 Wireless Keyboard + +usb:v03F0p0511* + ID_MODEL_FROM_DATABASE=OfficeJet K60 + +usb:v03F0p0512* + ID_MODEL_FROM_DATABASE=DeckJet 450 + +usb:v03F0p0517* + ID_MODEL_FROM_DATABASE=LaserJet 1000 + +usb:v03F0p051D* + ID_MODEL_FROM_DATABASE=Bluetooth Interface + +usb:v03F0p0601* + ID_MODEL_FROM_DATABASE=ScanJet 6300c + +usb:v03F0p0604* + ID_MODEL_FROM_DATABASE=DeskJet 840c + +usb:v03F0p0605* + ID_MODEL_FROM_DATABASE=ScanJet 2200c + +usb:v03F0p0611* + ID_MODEL_FROM_DATABASE=OfficeJet K60xi + +usb:v03F0p0612* + ID_MODEL_FROM_DATABASE=business inkjet 3000 + +usb:v03F0p0624* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v03F0p0701* + ID_MODEL_FROM_DATABASE=ScanJet 5300c/5370c + +usb:v03F0p0704* + ID_MODEL_FROM_DATABASE=DeskJet 825c + +usb:v03F0p0705* + ID_MODEL_FROM_DATABASE=ScanJet 4400c + +usb:v03F0p070C* + ID_MODEL_FROM_DATABASE=Personal Media Drive + +usb:v03F0p0711* + ID_MODEL_FROM_DATABASE=OfficeJet K80 + +usb:v03F0p0712* + ID_MODEL_FROM_DATABASE=DeskJet 1180c + +usb:v03F0p0714* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p0741* + ID_MODEL_FROM_DATABASE=Prime Wireless Kit [FOK65AA] + +usb:v03F0p0801* + ID_MODEL_FROM_DATABASE=ScanJet 7400c + +usb:v03F0p0804* + ID_MODEL_FROM_DATABASE=DeskJet 816c + +usb:v03F0p0805* + ID_MODEL_FROM_DATABASE=HP4470C + +usb:v03F0p0811* + ID_MODEL_FROM_DATABASE=OfficeJet K80xi + +usb:v03F0p0817* + ID_MODEL_FROM_DATABASE=LaserJet 3300 + +usb:v03F0p0901* + ID_MODEL_FROM_DATABASE=ScanJet 2300c + +usb:v03F0p0904* + ID_MODEL_FROM_DATABASE=DeskJet 845c + +usb:v03F0p0912* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p0917* + ID_MODEL_FROM_DATABASE=LaserJet 3330 + +usb:v03F0p0924* + ID_MODEL_FROM_DATABASE=Modular Smartcard Keyboard + +usb:v03F0p094A* + ID_MODEL_FROM_DATABASE=Optical Mouse [672662-001] + +usb:v03F0p0A01* + ID_MODEL_FROM_DATABASE=ScanJet 2400c + +usb:v03F0p0A17* + ID_MODEL_FROM_DATABASE=color LaserJet 3700 + +usb:v03F0p0B01* + ID_MODEL_FROM_DATABASE=ScanJet 82x0C + +usb:v03F0p0B0C* + ID_MODEL_FROM_DATABASE=Wireless Keyboard and Optical Mouse receiver + +usb:v03F0p0B17* + ID_MODEL_FROM_DATABASE=LaserJet 2300d + +usb:v03F0p0C17* + ID_MODEL_FROM_DATABASE=LaserJet 1010 + +usb:v03F0p0C24* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v03F0p0D12* + ID_MODEL_FROM_DATABASE=OfficeJet 9100 series + +usb:v03F0p0D17* + ID_MODEL_FROM_DATABASE=LaserJet 1012 + +usb:v03F0p0D4A* + ID_MODEL_FROM_DATABASE=SK-2025 Keyboard + +usb:v03F0p0E17* + ID_MODEL_FROM_DATABASE=LaserJet 1015 + +usb:v03F0p0F0C* + ID_MODEL_FROM_DATABASE=Wireless Keyboard and Optical Mouse receiver + +usb:v03F0p0F11* + ID_MODEL_FROM_DATABASE=OfficeJet V40 + +usb:v03F0p0F12* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p0F17* + ID_MODEL_FROM_DATABASE=LaserJet 1150 + +usb:v03F0p0F2A* + ID_MODEL_FROM_DATABASE=LaserJet 400 color M451dn + +usb:v03F0p1001* + ID_MODEL_FROM_DATABASE=Photo Scanner 1000 + +usb:v03F0p1002* + ID_MODEL_FROM_DATABASE=PhotoSmart 140 series + +usb:v03F0p1004* + ID_MODEL_FROM_DATABASE=DeskJet 970c/970cse + +usb:v03F0p1005* + ID_MODEL_FROM_DATABASE=ScanJet 5400c + +usb:v03F0p1011* + ID_MODEL_FROM_DATABASE=OfficeJet V40xi + +usb:v03F0p1016* + ID_MODEL_FROM_DATABASE=Jornada 548 / iPAQ HW6515 Pocket PC + +usb:v03F0p1017* + ID_MODEL_FROM_DATABASE=LaserJet 1300 + +usb:v03F0p1024* + ID_MODEL_FROM_DATABASE=Smart Card Keyboard + +usb:v03F0p1027* + ID_MODEL_FROM_DATABASE=Virtual keyboard and mouse + +usb:v03F0p102A* + ID_MODEL_FROM_DATABASE=LaserJet Professional P 1102w + +usb:v03F0p1102* + ID_MODEL_FROM_DATABASE=PhotoSmart 240 series + +usb:v03F0p1104* + ID_MODEL_FROM_DATABASE=DeskJet 959c + +usb:v03F0p1105* + ID_MODEL_FROM_DATABASE=ScanJet 5470c/5490c + +usb:v03F0p1111* + ID_MODEL_FROM_DATABASE=OfficeJet v60 + +usb:v03F0p1116* + ID_MODEL_FROM_DATABASE=Jornada 568 Pocket PC + +usb:v03F0p1117* + ID_MODEL_FROM_DATABASE=LaserJet 1300n + +usb:v03F0p1151* + ID_MODEL_FROM_DATABASE=PSC-750xi Printer/Scanner/Copier + +usb:v03F0p1198* + ID_MODEL_FROM_DATABASE=HID-compliant mouse + +usb:v03F0p1202* + ID_MODEL_FROM_DATABASE=PhotoSmart 320 series + +usb:v03F0p1204* + ID_MODEL_FROM_DATABASE=DeskJet 930c + +usb:v03F0p1205* + ID_MODEL_FROM_DATABASE=ScanJet 4500C/5550C + +usb:v03F0p1211* + ID_MODEL_FROM_DATABASE=OfficeJet v60xi + +usb:v03F0p1217* + ID_MODEL_FROM_DATABASE=LaserJet 2300L + +usb:v03F0p1227* + ID_MODEL_FROM_DATABASE=Virtual CD-ROM + +usb:v03F0p1302* + ID_MODEL_FROM_DATABASE=PhotoSmart 370 series + +usb:v03F0p1305* + ID_MODEL_FROM_DATABASE=ScanJet 4570c + +usb:v03F0p1311* + ID_MODEL_FROM_DATABASE=OfficeJet V30 + +usb:v03F0p1312* + ID_MODEL_FROM_DATABASE=DeskJet 460 + +usb:v03F0p1317* + ID_MODEL_FROM_DATABASE=LaserJet 1005 + +usb:v03F0p1327* + ID_MODEL_FROM_DATABASE=iLO Virtual Hub + +usb:v03F0p134A* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v03F0p1405* + ID_MODEL_FROM_DATABASE=ScanJet 3670 + +usb:v03F0p1411* + ID_MODEL_FROM_DATABASE=PSC 750 + +usb:v03F0p1424* + ID_MODEL_FROM_DATABASE=f2105 Monitor Hub + +usb:v03F0p1502* + ID_MODEL_FROM_DATABASE=PhotoSmart 420 series + +usb:v03F0p1504* + ID_MODEL_FROM_DATABASE=DeskJet 920c + +usb:v03F0p150C* + ID_MODEL_FROM_DATABASE=Mood Lighting (Microchip Technology Inc.) + +usb:v03F0p1511* + ID_MODEL_FROM_DATABASE=PSC 750xi + +usb:v03F0p1512* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p1517* + ID_MODEL_FROM_DATABASE=color LaserJet 3500 + +usb:v03F0p1524* + ID_MODEL_FROM_DATABASE=Smart Card Keyboard - KR + +usb:v03F0p1539* + ID_MODEL_FROM_DATABASE=Mini Magnetic Stripe Reader + +usb:v03F0p1541* + ID_MODEL_FROM_DATABASE=Prime [G8X92AA] + +usb:v03F0p1602* + ID_MODEL_FROM_DATABASE=PhotoSmart 330 series + +usb:v03F0p1604* + ID_MODEL_FROM_DATABASE=DeskJet 940c + +usb:v03F0p1605* + ID_MODEL_FROM_DATABASE=ScanJet 5530C PhotoSmart + +usb:v03F0p1611* + ID_MODEL_FROM_DATABASE=psc 780 + +usb:v03F0p1617* + ID_MODEL_FROM_DATABASE=LaserJet 3015 + +usb:v03F0p161D* + ID_MODEL_FROM_DATABASE=Wireless Rechargeable Optical Mouse (HID) + +usb:v03F0p1624* + ID_MODEL_FROM_DATABASE=Smart Card Keyboard - JP + +usb:v03F0p1702* + ID_MODEL_FROM_DATABASE=PhotoSmart 380 series + +usb:v03F0p1704* + ID_MODEL_FROM_DATABASE=DeskJet 948C + +usb:v03F0p1705* + ID_MODEL_FROM_DATABASE=ScanJet 5590 + +usb:v03F0p1711* + ID_MODEL_FROM_DATABASE=psc 780xi + +usb:v03F0p1712* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p1717* + ID_MODEL_FROM_DATABASE=LaserJet 3020 + +usb:v03F0p171D* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 Interface [Broadcom BCM2045] + +usb:v03F0p1801* + ID_MODEL_FROM_DATABASE=Inkjet P-2000U + +usb:v03F0p1802* + ID_MODEL_FROM_DATABASE=PhotoSmart 470 series + +usb:v03F0p1804* + ID_MODEL_FROM_DATABASE=DeskJet 916C + +usb:v03F0p1805* + ID_MODEL_FROM_DATABASE=ScanJet 7650 + +usb:v03F0p1811* + ID_MODEL_FROM_DATABASE=PSC 720 + +usb:v03F0p1812* + ID_MODEL_FROM_DATABASE=OfficeJet Pro K550 + +usb:v03F0p1817* + ID_MODEL_FROM_DATABASE=LaserJet 3030 + +usb:v03F0p181D* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 Interface + +usb:v03F0p1902* + ID_MODEL_FROM_DATABASE=PhotoSmart A430 series + +usb:v03F0p1904* + ID_MODEL_FROM_DATABASE=DeskJet 3820 + +usb:v03F0p1911* + ID_MODEL_FROM_DATABASE=OfficeJet V45 + +usb:v03F0p1917* + ID_MODEL_FROM_DATABASE=LaserJet 3380 + +usb:v03F0p1A02* + ID_MODEL_FROM_DATABASE=PhotoSmart A510 series + +usb:v03F0p1A11* + ID_MODEL_FROM_DATABASE=OfficeJet 5100 series + +usb:v03F0p1A17* + ID_MODEL_FROM_DATABASE=color LaserJet 4650 + +usb:v03F0p1B02* + ID_MODEL_FROM_DATABASE=PhotoSmart A610 series + +usb:v03F0p1B04* + ID_MODEL_FROM_DATABASE=DeskJet 3810 + +usb:v03F0p1B05* + ID_MODEL_FROM_DATABASE=ScanJet 4850C/4890C + +usb:v03F0p1B07* + ID_MODEL_FROM_DATABASE=Premium Starter Webcam + +usb:v03F0p1C02* + ID_MODEL_FROM_DATABASE=PhotoSmart A710 series + +usb:v03F0p1C17* + ID_MODEL_FROM_DATABASE=Color LaserJet 2550l + +usb:v03F0p1D02* + ID_MODEL_FROM_DATABASE=PhotoSmart A310 series + +usb:v03F0p1D17* + ID_MODEL_FROM_DATABASE=LaserJet 1320 + +usb:v03F0p1D24* + ID_MODEL_FROM_DATABASE=Barcode scanner + +usb:v03F0p1E02* + ID_MODEL_FROM_DATABASE=PhotoSmart A320 Printer series + +usb:v03F0p1E11* + ID_MODEL_FROM_DATABASE=PSC-950 + +usb:v03F0p1E17* + ID_MODEL_FROM_DATABASE=LaserJet 1160 series + +usb:v03F0p1F02* + ID_MODEL_FROM_DATABASE=PhotoSmart A440 Printer series + +usb:v03F0p1F11* + ID_MODEL_FROM_DATABASE=PSC 920 + +usb:v03F0p1F12* + ID_MODEL_FROM_DATABASE=OfficeJet Pro K5300 + +usb:v03F0p1F17* + ID_MODEL_FROM_DATABASE=color LaserJet 5550 + +usb:v03F0p1F1D* + ID_MODEL_FROM_DATABASE=un2400 Gobi Wireless Modem + +usb:v03F0p2001* + ID_MODEL_FROM_DATABASE=Floppy + +usb:v03F0p2002* + ID_MODEL_FROM_DATABASE=Hub + +usb:v03F0p2004* + ID_MODEL_FROM_DATABASE=DeskJet 640c + +usb:v03F0p2005* + ID_MODEL_FROM_DATABASE=ScanJet 3570c + +usb:v03F0p2012* + ID_MODEL_FROM_DATABASE=OfficeJet Pro K5400 + +usb:v03F0p201D* + ID_MODEL_FROM_DATABASE=un2400 Gobi Wireless Modem (QDL mode) + +usb:v03F0p2039* + ID_MODEL_FROM_DATABASE=Cashdrawer + +usb:v03F0p2102* + ID_MODEL_FROM_DATABASE=PhotoSmart 7345 + +usb:v03F0p2104* + ID_MODEL_FROM_DATABASE=DeskJet 630c + +usb:v03F0p2112* + ID_MODEL_FROM_DATABASE=OfficeJet Pro L7500 + +usb:v03F0p211D* + ID_MODEL_FROM_DATABASE=Sierra MC5725 [ev2210] + +usb:v03F0p2202* + ID_MODEL_FROM_DATABASE=PhotoSmart 7600 series + +usb:v03F0p2205* + ID_MODEL_FROM_DATABASE=ScanJet 3500c + +usb:v03F0p2212* + ID_MODEL_FROM_DATABASE=OfficeJet Pro L7600 + +usb:v03F0p2217* + ID_MODEL_FROM_DATABASE=color LaserJet 9500 MFP + +usb:v03F0p2302* + ID_MODEL_FROM_DATABASE=PhotoSmart 7600 series + +usb:v03F0p2304* + ID_MODEL_FROM_DATABASE=DeskJet 656c + +usb:v03F0p2305* + ID_MODEL_FROM_DATABASE=ScanJet 3970c + +usb:v03F0p2311* + ID_MODEL_FROM_DATABASE=OfficeJet d series + +usb:v03F0p2312* + ID_MODEL_FROM_DATABASE=OfficeJet Pro L7700 + +usb:v03F0p2317* + ID_MODEL_FROM_DATABASE=LaserJet 4350 + +usb:v03F0p231D* + ID_MODEL_FROM_DATABASE=Broadcom 2070 Bluetooth Combo + +usb:v03F0p2402* + ID_MODEL_FROM_DATABASE=PhotoSmart 7700 series + +usb:v03F0p2404* + ID_MODEL_FROM_DATABASE=Deskjet F2280 series + +usb:v03F0p2405* + ID_MODEL_FROM_DATABASE=ScanJet 4070 PhotoSmart + +usb:v03F0p2417* + ID_MODEL_FROM_DATABASE=LaserJet 4250 + +usb:v03F0p241D* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem (QDL mode) + +usb:v03F0p2424* + ID_MODEL_FROM_DATABASE=LP1965 19" Monitor Hub + +usb:v03F0p2502* + ID_MODEL_FROM_DATABASE=PhotoSmart 7700 series + +usb:v03F0p2504* + ID_MODEL_FROM_DATABASE=DeskJet F4200 series + +usb:v03F0p2505* + ID_MODEL_FROM_DATABASE=ScanJet 3770 + +usb:v03F0p2512* + ID_MODEL_FROM_DATABASE=OfficeJet Pro L7300 / Compaq LA2405 series monitor + +usb:v03F0p2514* + ID_MODEL_FROM_DATABASE=4-port hub + +usb:v03F0p2517* + ID_MODEL_FROM_DATABASE=LaserJet 2410 + +usb:v03F0p251D* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v03F0p2524* + ID_MODEL_FROM_DATABASE=LP3065 30" Monitor Hub + +usb:v03F0p2602* + ID_MODEL_FROM_DATABASE=PhotoSmart A520 series + +usb:v03F0p2605* + ID_MODEL_FROM_DATABASE=ScanJet 3800c + +usb:v03F0p2611* + ID_MODEL_FROM_DATABASE=OfficeJet 7100 series + +usb:v03F0p2617* + ID_MODEL_FROM_DATABASE=Color LaserJet 2820 series + +usb:v03F0p2624* + ID_MODEL_FROM_DATABASE=Pole Display (HP522 2 x 20 Line Display) + +usb:v03F0p2702* + ID_MODEL_FROM_DATABASE=PhotoSmart A620 series + +usb:v03F0p2704* + ID_MODEL_FROM_DATABASE=DeskJet 915 + +usb:v03F0p2717* + ID_MODEL_FROM_DATABASE=Color LaserJet 2830 + +usb:v03F0p2724* + ID_MODEL_FROM_DATABASE=Magnetic Stripe Reader IDRA-334133-HP + +usb:v03F0p2805* + ID_MODEL_FROM_DATABASE=Scanjet G2710 + +usb:v03F0p2811* + ID_MODEL_FROM_DATABASE=PSC-2100 + +usb:v03F0p2817* + ID_MODEL_FROM_DATABASE=Color LaserJet 2840 + +usb:v03F0p2902* + ID_MODEL_FROM_DATABASE=PhotoSmart A820 series + +usb:v03F0p2911* + ID_MODEL_FROM_DATABASE=PSC 2200 + +usb:v03F0p2917* + ID_MODEL_FROM_DATABASE=LaserJet 2420 + +usb:v03F0p2A11* + ID_MODEL_FROM_DATABASE=PSC 2150 series + +usb:v03F0p2A17* + ID_MODEL_FROM_DATABASE=LaserJet 2430 + +usb:v03F0p2A1D* + ID_MODEL_FROM_DATABASE=Integrated Module with Bluetooth 2.1 Wireless technology + +usb:v03F0p2B11* + ID_MODEL_FROM_DATABASE=PSC 2170 series + +usb:v03F0p2B17* + ID_MODEL_FROM_DATABASE=LaserJet 1020 + +usb:v03F0p2C12* + ID_MODEL_FROM_DATABASE=Officejet J4680 + +usb:v03F0p2C17* + ID_MODEL_FROM_DATABASE=LaserJet 1022 + +usb:v03F0p2C24* + ID_MODEL_FROM_DATABASE=Logitech M-UAL-96 Mouse + +usb:v03F0p2D05* + ID_MODEL_FROM_DATABASE=Scanjet 7000 + +usb:v03F0p2D11* + ID_MODEL_FROM_DATABASE=OfficeJet 6110 + +usb:v03F0p2D17* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p2E11* + ID_MODEL_FROM_DATABASE=PSC 1000 + +usb:v03F0p2E17* + ID_MODEL_FROM_DATABASE=LaserJet 2600n + +usb:v03F0p2E24* + ID_MODEL_FROM_DATABASE=LP2275w Monitor Hub + +usb:v03F0p2F11* + ID_MODEL_FROM_DATABASE=PSC 1200 + +usb:v03F0p2F17* + ID_MODEL_FROM_DATABASE=Color LaserJet 2605dn + +usb:v03F0p2F24* + ID_MODEL_FROM_DATABASE=LP2475w Monitor Hub + +usb:v03F0p3002* + ID_MODEL_FROM_DATABASE=PhotoSmart P1000 + +usb:v03F0p3004* + ID_MODEL_FROM_DATABASE=DeskJet 980c + +usb:v03F0p3005* + ID_MODEL_FROM_DATABASE=ScanJet 4670v + +usb:v03F0p3011* + ID_MODEL_FROM_DATABASE=PSC 1100 series + +usb:v03F0p3017* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p3102* + ID_MODEL_FROM_DATABASE=PhotoSmart P1100 Printer w/ Card Reader + +usb:v03F0p3104* + ID_MODEL_FROM_DATABASE=DeskJet 960c + +usb:v03F0p3111* + ID_MODEL_FROM_DATABASE=OfficeJet 4100 series + +usb:v03F0p3117* + ID_MODEL_FROM_DATABASE=EWS 2605dtn + +usb:v03F0p311D* + ID_MODEL_FROM_DATABASE=Atheros AR9285 Malbec Bluetooth Adapter + +usb:v03F0p3202* + ID_MODEL_FROM_DATABASE=PhotoSmart 1215 + +usb:v03F0p3207* + ID_MODEL_FROM_DATABASE=4 GB flash drive + +usb:v03F0p3211* + ID_MODEL_FROM_DATABASE=OfficeJet 4105 series + +usb:v03F0p3217* + ID_MODEL_FROM_DATABASE=LaserJet 3050 + +usb:v03F0p3302* + ID_MODEL_FROM_DATABASE=PhotoSmart 1218 + +usb:v03F0p3304* + ID_MODEL_FROM_DATABASE=DeskJet 990c + +usb:v03F0p3307* + ID_MODEL_FROM_DATABASE=v125w Stick + +usb:v03F0p3312* + ID_MODEL_FROM_DATABASE=OfficeJet J6410 + +usb:v03F0p3317* + ID_MODEL_FROM_DATABASE=LaserJet 3052 + +usb:v03F0p3402* + ID_MODEL_FROM_DATABASE=PhotoSmart 1115 + +usb:v03F0p3404* + ID_MODEL_FROM_DATABASE=DeskJet 6122 + +usb:v03F0p3417* + ID_MODEL_FROM_DATABASE=LaserJet 3055 + +usb:v03F0p3502* + ID_MODEL_FROM_DATABASE=PhotoSmart 230 + +usb:v03F0p3504* + ID_MODEL_FROM_DATABASE=DeskJet 6127c + +usb:v03F0p3511* + ID_MODEL_FROM_DATABASE=PSC 2300 + +usb:v03F0p3517* + ID_MODEL_FROM_DATABASE=LaserJet 3390 + +usb:v03F0p3602* + ID_MODEL_FROM_DATABASE=PhotoSmart 1315 + +usb:v03F0p3611* + ID_MODEL_FROM_DATABASE=PSC 2410 PhotoSmart + +usb:v03F0p3617* + ID_MODEL_FROM_DATABASE=Color LaserJet 2605 + +usb:v03F0p3711* + ID_MODEL_FROM_DATABASE=PSC 2500 + +usb:v03F0p3717* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p3724* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v03F0p3802* + ID_MODEL_FROM_DATABASE=PhotoSmart 100 + +usb:v03F0p3807* + ID_MODEL_FROM_DATABASE=c485w Flash Drive + +usb:v03F0p3817* + ID_MODEL_FROM_DATABASE=LaserJet P2015 series + +usb:v03F0p3902* + ID_MODEL_FROM_DATABASE=PhotoSmart 130 + +usb:v03F0p3912* + ID_MODEL_FROM_DATABASE=Officejet Pro 8500 + +usb:v03F0p3A02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7150 + +usb:v03F0p3A11* + ID_MODEL_FROM_DATABASE=OfficeJet 5500 series + +usb:v03F0p3A17* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p3B02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7150~ + +usb:v03F0p3B05* + ID_MODEL_FROM_DATABASE=Scanjet N8460 + +usb:v03F0p3B11* + ID_MODEL_FROM_DATABASE=PSC 1300 series + +usb:v03F0p3B17* + ID_MODEL_FROM_DATABASE=LaserJet M1005 MFP + +usb:v03F0p3C02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7350 + +usb:v03F0p3C05* + ID_MODEL_FROM_DATABASE=Scanjet Professional 1000 Mobile Scanner + +usb:v03F0p3C11* + ID_MODEL_FROM_DATABASE=PSC 1358 + +usb:v03F0p3C17* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p3D02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7350~ + +usb:v03F0p3D11* + ID_MODEL_FROM_DATABASE=OfficeJet 4215 + +usb:v03F0p3D17* + ID_MODEL_FROM_DATABASE=LaserJet P1005 + +usb:v03F0p3E02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7550 + +usb:v03F0p3E17* + ID_MODEL_FROM_DATABASE=LaserJet P1006 + +usb:v03F0p3F02* + ID_MODEL_FROM_DATABASE=PhotoSmart 7550~ + +usb:v03F0p3F11* + ID_MODEL_FROM_DATABASE=PSC-1315/PSC-1317 + +usb:v03F0p4002* + ID_MODEL_FROM_DATABASE=PhotoSmart 635/715/720/735/935/E337 (storage) + +usb:v03F0p4004* + ID_MODEL_FROM_DATABASE=CP1160 + +usb:v03F0p4102* + ID_MODEL_FROM_DATABASE=PhotoSmart 618 + +usb:v03F0p4105* + ID_MODEL_FROM_DATABASE=ScanJet 4370 + +usb:v03F0p4111* + ID_MODEL_FROM_DATABASE=OfficeJet 7200 series + +usb:v03F0p4117* + ID_MODEL_FROM_DATABASE=LaserJet 1018 + +usb:v03F0p4202* + ID_MODEL_FROM_DATABASE=PhotoSmart 812 + +usb:v03F0p4205* + ID_MODEL_FROM_DATABASE=ScanJet G3010 + +usb:v03F0p4211* + ID_MODEL_FROM_DATABASE=OfficeJet 7300 series + +usb:v03F0p4217* + ID_MODEL_FROM_DATABASE=EWS CM1015 + +usb:v03F0p4302* + ID_MODEL_FROM_DATABASE=PhotoSmart 850 (ptp) + +usb:v03F0p4305* + ID_MODEL_FROM_DATABASE=ScanJet G3110 + +usb:v03F0p4311* + ID_MODEL_FROM_DATABASE=OfficeJet 7400 series + +usb:v03F0p4317* + ID_MODEL_FROM_DATABASE=Color LaserJet CM1017 + +usb:v03F0p4402* + ID_MODEL_FROM_DATABASE=PhotoSmart 935 (ptp) + +usb:v03F0p4417* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p4502* + ID_MODEL_FROM_DATABASE=PhotoSmart 945 (PTP mode) + +usb:v03F0p4505* + ID_MODEL_FROM_DATABASE=ScanJet G4010 + +usb:v03F0p4507* + ID_MODEL_FROM_DATABASE=External HDD + +usb:v03F0p4511* + ID_MODEL_FROM_DATABASE=PhotoSmart 2600 + +usb:v03F0p4512* + ID_MODEL_FROM_DATABASE=E709n [Officejet 6500 Wireless] + +usb:v03F0p4517* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p4605* + ID_MODEL_FROM_DATABASE=ScanJet G4050 + +usb:v03F0p4611* + ID_MODEL_FROM_DATABASE=PhotoSmart 2700 + +usb:v03F0p4717* + ID_MODEL_FROM_DATABASE=Color LaserJet CP1215 + +usb:v03F0p4811* + ID_MODEL_FROM_DATABASE=PSC 1600 + +usb:v03F0p4911* + ID_MODEL_FROM_DATABASE=PSC 2350 + +usb:v03F0p4B11* + ID_MODEL_FROM_DATABASE=OfficeJet 6200 + +usb:v03F0p4C11* + ID_MODEL_FROM_DATABASE=PSC 1500 series + +usb:v03F0p4C17* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p4D11* + ID_MODEL_FROM_DATABASE=PSC 1400 + +usb:v03F0p4D17* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p4E11* + ID_MODEL_FROM_DATABASE=PhotoSmart 2570 series + +usb:v03F0p4F11* + ID_MODEL_FROM_DATABASE=OfficeJet 5600 (USBHUB) + +usb:v03F0p4F17* + ID_MODEL_FROM_DATABASE=Color LaserJet CM1312 MFP + +usb:v03F0p5004* + ID_MODEL_FROM_DATABASE=DeskJet 995c + +usb:v03F0p5011* + ID_MODEL_FROM_DATABASE=PhotoSmart 3100 series + +usb:v03F0p5017* + ID_MODEL_FROM_DATABASE=EWS UPD + +usb:v03F0p5111* + ID_MODEL_FROM_DATABASE=PhotoSmart 3200 series + +usb:v03F0p5211* + ID_MODEL_FROM_DATABASE=PhotoSmart 3300 series + +usb:v03F0p5307* + ID_MODEL_FROM_DATABASE=v165w Stick + +usb:v03F0p5311* + ID_MODEL_FROM_DATABASE=OfficeJet 6300 + +usb:v03F0p5312* + ID_MODEL_FROM_DATABASE=Officejet Pro 8500A + +usb:v03F0p5411* + ID_MODEL_FROM_DATABASE=OfficeJet 4300 + +usb:v03F0p5511* + ID_MODEL_FROM_DATABASE=DeskJet F300 series + +usb:v03F0p5611* + ID_MODEL_FROM_DATABASE=PhotoSmart C3180 + +usb:v03F0p5617* + ID_MODEL_FROM_DATABASE=LaserJet M1120 MFP + +usb:v03F0p5711* + ID_MODEL_FROM_DATABASE=PhotoSmart C4100 series + +usb:v03F0p5717* + ID_MODEL_FROM_DATABASE=LaserJet M1120n MFP + +usb:v03F0p5811* + ID_MODEL_FROM_DATABASE=PhotoSmart C5100 series + +usb:v03F0p5817* + ID_MODEL_FROM_DATABASE=LaserJet M1319f MFP + +usb:v03F0p581D* + ID_MODEL_FROM_DATABASE=lt4112 Gobi 4G Module Network Device + +usb:v03F0p5911* + ID_MODEL_FROM_DATABASE=PhotoSmart C6180 + +usb:v03F0p5912* + ID_MODEL_FROM_DATABASE=Officejet Pro 8600 + +usb:v03F0p5A11* + ID_MODEL_FROM_DATABASE=PhotoSmart C7100 series + +usb:v03F0p5B11* + ID_MODEL_FROM_DATABASE=OfficeJet J2100 series + +usb:v03F0p5B12* + ID_MODEL_FROM_DATABASE=Officejet Pro 8100 + +usb:v03F0p5C11* + ID_MODEL_FROM_DATABASE=PhotoSmart C4200 Printer series + +usb:v03F0p5C12* + ID_MODEL_FROM_DATABASE=OfficeJet 6700 + +usb:v03F0p5C17* + ID_MODEL_FROM_DATABASE=LaserJet P2055 series + +usb:v03F0p5D11* + ID_MODEL_FROM_DATABASE=PhotoSmart C5200 series + +usb:v03F0p5E11* + ID_MODEL_FROM_DATABASE=PhotoSmart D7400 series + +usb:v03F0p6004* + ID_MODEL_FROM_DATABASE=DeskJet 5550 + +usb:v03F0p6102* + ID_MODEL_FROM_DATABASE=Hewlett Packard Digital Camera + +usb:v03F0p6104* + ID_MODEL_FROM_DATABASE=DeskJet 5650c + +usb:v03F0p6117* + ID_MODEL_FROM_DATABASE=color LaserJet 3550 + +usb:v03F0p6202* + ID_MODEL_FROM_DATABASE=PhotoSmart 215 + +usb:v03F0p6204* + ID_MODEL_FROM_DATABASE=DeskJet 5150c + +usb:v03F0p6217* + ID_MODEL_FROM_DATABASE=Color LaserJet 4700 + +usb:v03F0p6302* + ID_MODEL_FROM_DATABASE=PhotoSmart 318/612 + +usb:v03F0p6317* + ID_MODEL_FROM_DATABASE=Color LaserJet 4730mfp + +usb:v03F0p6402* + ID_MODEL_FROM_DATABASE=PhotoSmart 715 (ptp) + +usb:v03F0p6411* + ID_MODEL_FROM_DATABASE=PhotoSmart C8100 series + +usb:v03F0p6417* + ID_MODEL_FROM_DATABASE=LaserJet 5200 + +usb:v03F0p6502* + ID_MODEL_FROM_DATABASE=PhotoSmart 120 (ptp) + +usb:v03F0p6511* + ID_MODEL_FROM_DATABASE=PhotoSmart C7200 series + +usb:v03F0p6602* + ID_MODEL_FROM_DATABASE=PhotoSmart 320 + +usb:v03F0p6611* + ID_MODEL_FROM_DATABASE=PhotoSmart C4380 series + +usb:v03F0p6617* + ID_MODEL_FROM_DATABASE=LaserJet 5200L + +usb:v03F0p6702* + ID_MODEL_FROM_DATABASE=PhotoSmart 720 (ptp) + +usb:v03F0p6717* + ID_MODEL_FROM_DATABASE=Color LaserJet 3000 + +usb:v03F0p6802* + ID_MODEL_FROM_DATABASE=PhotoSmart 620 (ptp) + +usb:v03F0p6811* + ID_MODEL_FROM_DATABASE=PhotoSmart D5300 series + +usb:v03F0p6817* + ID_MODEL_FROM_DATABASE=Color LaserJet 3800 + +usb:v03F0p6911* + ID_MODEL_FROM_DATABASE=PhotoSmart D7200 series + +usb:v03F0p6917* + ID_MODEL_FROM_DATABASE=Color LaserJet 3600 + +usb:v03F0p6A02* + ID_MODEL_FROM_DATABASE=PhotoSmart 735 (ptp) + +usb:v03F0p6A11* + ID_MODEL_FROM_DATABASE=PhotoSmart C6200 series + +usb:v03F0p6A17* + ID_MODEL_FROM_DATABASE=LaserJet 4240 + +usb:v03F0p6B02* + ID_MODEL_FROM_DATABASE=PhotoSmart R707 (PTP mode) + +usb:v03F0p6B11* + ID_MODEL_FROM_DATABASE=Photosmart C4500 series + +usb:v03F0p6C11* + ID_MODEL_FROM_DATABASE=Photosmart C4480 + +usb:v03F0p6C17* + ID_MODEL_FROM_DATABASE=Color LaserJet 4610 + +usb:v03F0p6F17* + ID_MODEL_FROM_DATABASE=Color LaserJet CP6015 series + +usb:v03F0p7004* + ID_MODEL_FROM_DATABASE=DeskJet 3320c + +usb:v03F0p7102* + ID_MODEL_FROM_DATABASE=PhotoSmart 635 (PTP mode) + +usb:v03F0p7104* + ID_MODEL_FROM_DATABASE=DeskJet 3420c + +usb:v03F0p7117* + ID_MODEL_FROM_DATABASE=CM8060 Color MFP with Edgeline Technology + +usb:v03F0p7202* + ID_MODEL_FROM_DATABASE=PhotoSmart 43x (ptp) + +usb:v03F0p7204* + ID_MODEL_FROM_DATABASE=DeskJet 36xx + +usb:v03F0p7217* + ID_MODEL_FROM_DATABASE=LaserJet M5035 MFP + +usb:v03F0p7302* + ID_MODEL_FROM_DATABASE=PhotoSmart M307 (PTP mode) + +usb:v03F0p7304* + ID_MODEL_FROM_DATABASE=DeskJet 35xx + +usb:v03F0p7311* + ID_MODEL_FROM_DATABASE=Photosmart Premium C309 + +usb:v03F0p7317* + ID_MODEL_FROM_DATABASE=LaserJet P3005 + +usb:v03F0p7404* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p7417* + ID_MODEL_FROM_DATABASE=LaserJet M4345 MFP + +usb:v03F0p7504* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p7517* + ID_MODEL_FROM_DATABASE=LaserJet M3035 MFP + +usb:v03F0p7604* + ID_MODEL_FROM_DATABASE=DeskJet 3940 + +usb:v03F0p7611* + ID_MODEL_FROM_DATABASE=DeskJet F2492 All-in-One + +usb:v03F0p7617* + ID_MODEL_FROM_DATABASE=LaserJet P3004 + +usb:v03F0p7702* + ID_MODEL_FROM_DATABASE=PhotoSmart R817 (PTP mode) + +usb:v03F0p7704* + ID_MODEL_FROM_DATABASE=DeskJet D4100 + +usb:v03F0p7717* + ID_MODEL_FROM_DATABASE=CM8050 Color MFP with Edgeline Technology + +usb:v03F0p7804* + ID_MODEL_FROM_DATABASE=DeskJet D1360 + +usb:v03F0p7817* + ID_MODEL_FROM_DATABASE=Color LaserJet CP3505 + +usb:v03F0p7917* + ID_MODEL_FROM_DATABASE=LaserJet M5025 MFP + +usb:v03F0p7A02* + ID_MODEL_FROM_DATABASE=PhotoSmart M415 (PTP mode) + +usb:v03F0p7A04* + ID_MODEL_FROM_DATABASE=DeskJet D2460 + +usb:v03F0p7A17* + ID_MODEL_FROM_DATABASE=LaserJet M3027 MFP + +usb:v03F0p7B02* + ID_MODEL_FROM_DATABASE=PhotoSmart M23 (PTP mode) + +usb:v03F0p7B17* + ID_MODEL_FROM_DATABASE=Color LaserJet CP4005 + +usb:v03F0p7C17* + ID_MODEL_FROM_DATABASE=Color LaserJet CM6040 series + +usb:v03F0p7D04* + ID_MODEL_FROM_DATABASE=DeskJet F2100 Printer series + +usb:v03F0p7D17* + ID_MODEL_FROM_DATABASE=Color LaserJet CM4730 MFP + +usb:v03F0p7E04* + ID_MODEL_FROM_DATABASE=DeskJet F4100 Printer series + +usb:v03F0p8017* + ID_MODEL_FROM_DATABASE=LaserJet P4515 + +usb:v03F0p8104* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p8117* + ID_MODEL_FROM_DATABASE=LaserJet P4015 + +usb:v03F0p811C* + ID_MODEL_FROM_DATABASE=Ethernet HN210E + +usb:v03F0p8204* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v03F0p8207* + ID_MODEL_FROM_DATABASE=FHA-3510 2.4GHz Wireless Optical Mobile Mouse + +usb:v03F0p8217* + ID_MODEL_FROM_DATABASE=LaserJet P4014 + +usb:v03F0p8317* + ID_MODEL_FROM_DATABASE=LaserJet M9050 MFP + +usb:v03F0p8404* + ID_MODEL_FROM_DATABASE=DeskJet 6800 series + +usb:v03F0p8417* + ID_MODEL_FROM_DATABASE=LaserJet M9040 MFP + +usb:v03F0p8504* + ID_MODEL_FROM_DATABASE=DeskJet 6600 series + +usb:v03F0p8604* + ID_MODEL_FROM_DATABASE=DeskJet 5440 + +usb:v03F0p8607* + ID_MODEL_FROM_DATABASE=Optical Mobile Mouse + +usb:v03F0p8704* + ID_MODEL_FROM_DATABASE=DeskJet 5940 + +usb:v03F0p8711* + ID_MODEL_FROM_DATABASE=Deskjet 2050 J510 + +usb:v03F0p8804* + ID_MODEL_FROM_DATABASE=DeskJet 6980 series + +usb:v03F0p8904* + ID_MODEL_FROM_DATABASE=DeskJet 6940 series + +usb:v03F0p8C07* + ID_MODEL_FROM_DATABASE=Digital Stereo Headset + +usb:v03F0p8C11* + ID_MODEL_FROM_DATABASE=Deskjet F4500 series + +usb:v03F0p9002* + ID_MODEL_FROM_DATABASE=PhotoSmart M437 + +usb:v03F0p9102* + ID_MODEL_FROM_DATABASE=PhotoSmart M537 + +usb:v03F0p9302* + ID_MODEL_FROM_DATABASE=PhotoSmart R930 series + +usb:v03F0p9402* + ID_MODEL_FROM_DATABASE=PhotoSmart R837 + +usb:v03F0p9502* + ID_MODEL_FROM_DATABASE=PhotoSmart R840 series + +usb:v03F0p9602* + ID_MODEL_FROM_DATABASE=PhotoSmart M730 series + +usb:v03F0p9702* + ID_MODEL_FROM_DATABASE=PhotoSmart R740 series + +usb:v03F0p9802* + ID_MODEL_FROM_DATABASE=PhotoSmart Mz60 series + +usb:v03F0p9902* + ID_MODEL_FROM_DATABASE=PhotoSmart M630 series + +usb:v03F0p9A02* + ID_MODEL_FROM_DATABASE=PhotoSmart E330 series + +usb:v03F0p9B02* + ID_MODEL_FROM_DATABASE=PhotoSmart M540 series + +usb:v03F0p9B07* + ID_MODEL_FROM_DATABASE=Portable Drive + +usb:v03F0p9C02* + ID_MODEL_FROM_DATABASE=PhotoSmart M440 series + +usb:v03F0pA004* + ID_MODEL_FROM_DATABASE=DeskJet 5850c + +usb:v03F0pA011* + ID_MODEL_FROM_DATABASE=Deskjet 3050A + +usb:v03F0pB002* + ID_MODEL_FROM_DATABASE=PhotoSmart 7200 series + +usb:v03F0pB102* + ID_MODEL_FROM_DATABASE=PhotoSmart 7200 series + +usb:v03F0pB107* + ID_MODEL_FROM_DATABASE=v255w/c310w Flash Drive + +usb:v03F0pB116* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v03F0pB202* + ID_MODEL_FROM_DATABASE=PhotoSmart 7600 series + +usb:v03F0pB302* + ID_MODEL_FROM_DATABASE=PhotoSmart 7600 series + +usb:v03F0pB402* + ID_MODEL_FROM_DATABASE=PhotoSmart 7700 series + +usb:v03F0pB502* + ID_MODEL_FROM_DATABASE=PhotoSmart 7700 series + +usb:v03F0pB602* + ID_MODEL_FROM_DATABASE=PhotoSmart 7900 series + +usb:v03F0pB702* + ID_MODEL_FROM_DATABASE=PhotoSmart 7900 series + +usb:v03F0pB802* + ID_MODEL_FROM_DATABASE=PhotoSmart 7400 series + +usb:v03F0pB902* + ID_MODEL_FROM_DATABASE=PhotoSmart 7800 series + +usb:v03F0pBA02* + ID_MODEL_FROM_DATABASE=PhotoSmart 8100 series + +usb:v03F0pBB02* + ID_MODEL_FROM_DATABASE=PhotoSmart 8400 series + +usb:v03F0pBC02* + ID_MODEL_FROM_DATABASE=PhotoSmart 8700 series + +usb:v03F0pBD02* + ID_MODEL_FROM_DATABASE=PhotoSmart Pro B9100 series + +usb:v03F0pBEF4* + ID_MODEL_FROM_DATABASE=NEC Picty760 + +usb:v03F0pC002* + ID_MODEL_FROM_DATABASE=PhotoSmart 7800 series + +usb:v03F0pC102* + ID_MODEL_FROM_DATABASE=PhotoSmart 8000 series + +usb:v03F0pC111* + ID_MODEL_FROM_DATABASE=Deskjet 1510 + +usb:v03F0pC202* + ID_MODEL_FROM_DATABASE=PhotoSmart 8200 series + +usb:v03F0pC302* + ID_MODEL_FROM_DATABASE=DeskJet D2300 + +usb:v03F0pC402* + ID_MODEL_FROM_DATABASE=PhotoSmart D5100 series + +usb:v03F0pC502* + ID_MODEL_FROM_DATABASE=PhotoSmart D6100 series + +usb:v03F0pC602* + ID_MODEL_FROM_DATABASE=PhotoSmart D7100 series + +usb:v03F0pC702* + ID_MODEL_FROM_DATABASE=PhotoSmart D7300 series + +usb:v03F0pC802* + ID_MODEL_FROM_DATABASE=PhotoSmart D5060 Printer + +usb:v03F0pD104* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v03F0pD507* + ID_MODEL_FROM_DATABASE=39gII [NW249AA] + +usb:v03F0pEFBE* + ID_MODEL_FROM_DATABASE=NEC Picty900 + +usb:v03F0pF0BE* + ID_MODEL_FROM_DATABASE=NEC Picty920 + +usb:v03F0pF1BE* + ID_MODEL_FROM_DATABASE=NEC Picty800 + +usb:v03F1* + ID_VENDOR_FROM_DATABASE=Genoa Technology + +usb:v03F2* + ID_VENDOR_FROM_DATABASE=Oak Technology, Inc. + +usb:v03F3* + ID_VENDOR_FROM_DATABASE=Adaptec, Inc. + +usb:v03F3p0020* + ID_MODEL_FROM_DATABASE=AWN-8020 WLAN [Intersil PRISM 2.5] + +usb:v03F3p0080* + ID_MODEL_FROM_DATABASE=AVC-1100 Audio Capture + +usb:v03F3p0083* + ID_MODEL_FROM_DATABASE=AVC-2200 Device + +usb:v03F3p0087* + ID_MODEL_FROM_DATABASE=AVC-2210 Loader + +usb:v03F3p0088* + ID_MODEL_FROM_DATABASE=AVC-2210 Device + +usb:v03F3p008B* + ID_MODEL_FROM_DATABASE=AVC-2310 Loader + +usb:v03F3p008C* + ID_MODEL_FROM_DATABASE=AVC-2310 Device + +usb:v03F3p0094* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v03F3p009B* + ID_MODEL_FROM_DATABASE=AVC-1410 GameBridge TV NTSC + +usb:v03F3p2000* + ID_MODEL_FROM_DATABASE=USBXchange + +usb:v03F3p2001* + ID_MODEL_FROM_DATABASE=USBXchange Adapter + +usb:v03F3p2002* + ID_MODEL_FROM_DATABASE=USB2-Xchange + +usb:v03F3p2003* + ID_MODEL_FROM_DATABASE=USB2-Xchange Adapter + +usb:v03F3p4000* + ID_MODEL_FROM_DATABASE=4-port hub + +usb:v03F3pADCC* + ID_MODEL_FROM_DATABASE=Composite Device Support + +usb:v03F4* + ID_VENDOR_FROM_DATABASE=Diebold, Inc. + +usb:v03F5* + ID_VENDOR_FROM_DATABASE=Siemens Electromechanical + +usb:v03F8* + ID_VENDOR_FROM_DATABASE=Epson Imaging Technology Center + +usb:v03F9* + ID_VENDOR_FROM_DATABASE=KeyTronic Corp. + +usb:v03F9p0100* + ID_MODEL_FROM_DATABASE=KT-2001 Keyboard + +usb:v03F9p0101* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v03F9p0102* + ID_MODEL_FROM_DATABASE=Keyboard Mouse + +usb:v03FB* + ID_VENDOR_FROM_DATABASE=OPTi, Inc. + +usb:v03FC* + ID_VENDOR_FROM_DATABASE=Elitegroup Computer Systems + +usb:v03FD* + ID_VENDOR_FROM_DATABASE=Xilinx, Inc. + +usb:v03FDp0008* + ID_MODEL_FROM_DATABASE=Platform Cable USB II + +usb:v03FE* + ID_VENDOR_FROM_DATABASE=Farallon Comunications + +usb:v0400* + ID_VENDOR_FROM_DATABASE=National Semiconductor Corp. + +usb:v0400p05DC* + ID_MODEL_FROM_DATABASE=Rigol Technologies DS1000USB Oscilloscope + +usb:v0400p0807* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0400p080A* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0400p09C4* + ID_MODEL_FROM_DATABASE=Rigol Technologies DG1022 Arbitrary Waveform Generator + +usb:v0400p1000* + ID_MODEL_FROM_DATABASE=Mustek BearPaw 1200 Scanner + +usb:v0400p1001* + ID_MODEL_FROM_DATABASE=Mustek BearPaw 2400 Scanner + +usb:v0400p1237* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0400pA000* + ID_MODEL_FROM_DATABASE=Smart Display Reference Device + +usb:v0400pC359* + ID_MODEL_FROM_DATABASE=Logitech Harmony + +usb:v0400pC35B* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v0400pC55D* + ID_MODEL_FROM_DATABASE=Rigol Technologies DS5000USB Oscilloscope + +usb:v0401* + ID_VENDOR_FROM_DATABASE=National Registry, Inc. + +usb:v0402* + ID_VENDOR_FROM_DATABASE=ALi Corp. + +usb:v0402p5462* + ID_MODEL_FROM_DATABASE=M5462 IDE Controller + +usb:v0402p5602* + ID_MODEL_FROM_DATABASE=M5602 Video Camera Controller + +usb:v0402p5603* + ID_MODEL_FROM_DATABASE=M5603 Video Camera Controller + +usb:v0402p5606* + ID_MODEL_FROM_DATABASE=M5606 Video Camera Controller [UVC] + +usb:v0402p5621* + ID_MODEL_FROM_DATABASE=M5621 High-Speed IDE Controller + +usb:v0402p5623* + ID_MODEL_FROM_DATABASE=M5623 Scanner Controller + +usb:v0402p5627* + ID_MODEL_FROM_DATABASE=Welland ME-740PS USB2 3.5" Power Saving Enclosure + +usb:v0402p5632* + ID_MODEL_FROM_DATABASE=M5632 Host-to-Host Link + +usb:v0402p5635* + ID_MODEL_FROM_DATABASE=M5635 Flash Card Reader + +usb:v0402p5636* + ID_MODEL_FROM_DATABASE=USB 2.0 Storage Device + +usb:v0402p5637* + ID_MODEL_FROM_DATABASE=M5637 IDE Controller + +usb:v0402p5642* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v0402p5661* + ID_MODEL_FROM_DATABASE=M5661 MP3 player + +usb:v0402p5667* + ID_MODEL_FROM_DATABASE=M5667 MP3 player + +usb:v0402p9665* + ID_MODEL_FROM_DATABASE=Gateway Webcam + +usb:v0403* + ID_VENDOR_FROM_DATABASE=Future Technology Devices International, Ltd + +usb:v0403p0000* + ID_MODEL_FROM_DATABASE=H4SMK 7 Port Hub / Bricked Counterfeit FT232 Serial (UART) IC + +usb:v0403p0232* + ID_MODEL_FROM_DATABASE=Serial Converter + +usb:v0403p1060* + ID_MODEL_FROM_DATABASE=JTAG adapter + +usb:v0403p1234* + ID_MODEL_FROM_DATABASE=IronLogic RFID Adapter [Z-2 USB] + +usb:v0403p1235* + ID_MODEL_FROM_DATABASE=Iron Logic Z-397 RS-485/422 converter + +usb:v0403p6001* + ID_MODEL_FROM_DATABASE=FT232 Serial (UART) IC + +usb:v0403p6002* + ID_MODEL_FROM_DATABASE=Lumel PD12 + +usb:v0403p6007* + ID_MODEL_FROM_DATABASE=Serial Converter + +usb:v0403p6008* + ID_MODEL_FROM_DATABASE=Serial Converter + +usb:v0403p6009* + ID_MODEL_FROM_DATABASE=Serial Converter + +usb:v0403p6010* + ID_MODEL_FROM_DATABASE=FT2232C Dual USB-UART/FIFO IC + +usb:v0403p6011* + ID_MODEL_FROM_DATABASE=FT4232H Quad HS USB-UART/FIFO IC + +usb:v0403p6014* + ID_MODEL_FROM_DATABASE=FT232H Single HS USB-UART/FIFO IC + +usb:v0403p6015* + ID_MODEL_FROM_DATABASE=Bridge(I2C/SPI/UART/FIFO) + +usb:v0403p8028* + ID_MODEL_FROM_DATABASE=Dev board JTAG (FT232H based) + +usb:v0403p8040* + ID_MODEL_FROM_DATABASE=4 Port Hub + +usb:v0403p8070* + ID_MODEL_FROM_DATABASE=7 Port Hub + +usb:v0403p8140* + ID_MODEL_FROM_DATABASE=Vehicle Explorer Interface + +usb:v0403p8210* + ID_MODEL_FROM_DATABASE=MGTimer - MGCC (Vic) Timing System + +usb:v0403p8370* + ID_MODEL_FROM_DATABASE=7 Port Hub + +usb:v0403p8371* + ID_MODEL_FROM_DATABASE=PS/2 Keyboard And Mouse + +usb:v0403p8372* + ID_MODEL_FROM_DATABASE=FT8U100AX Serial Port + +usb:v0403p8A28* + ID_MODEL_FROM_DATABASE=Rainforest Automation ZigBee Controller + +usb:v0403p8A98* + ID_MODEL_FROM_DATABASE=TIAO Multi-Protocol Adapter + +usb:v0403p8B28* + ID_MODEL_FROM_DATABASE=Alpermann+Velte TCI70 + +usb:v0403p8B29* + ID_MODEL_FROM_DATABASE=Alpermann+Velte TC60 CLS + +usb:v0403p8B2A* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium Q1 + +usb:v0403p8B2B* + ID_MODEL_FROM_DATABASE=Alpermann+Velte TCD + +usb:v0403p8B2C* + ID_MODEL_FROM_DATABASE=Alpermann+Velte TCC70 + +usb:v0403p9090* + ID_MODEL_FROM_DATABASE=SNAP Stick 200 + +usb:v0403p9132* + ID_MODEL_FROM_DATABASE=LCD and Temperature Interface + +usb:v0403p9133* + ID_MODEL_FROM_DATABASE=CallerID + +usb:v0403p9135* + ID_MODEL_FROM_DATABASE=Rotary Pub alarm + +usb:v0403p9136* + ID_MODEL_FROM_DATABASE=Pulsecounter + +usb:v0403p9E90* + ID_MODEL_FROM_DATABASE=Marvell OpenRD Base/Client + +usb:v0403p9F80* + ID_MODEL_FROM_DATABASE=Ewert Energy Systems CANdapter + +usb:v0403pA6D0* + ID_MODEL_FROM_DATABASE=Texas Instruments XDS100v2 JTAG / BeagleBone A3 + +usb:v0403pA951* + ID_MODEL_FROM_DATABASE=HCP HIT GSM/GPRS modem [Cinterion MC55i] + +usb:v0403pA9A0* + ID_MODEL_FROM_DATABASE=FT2232D - Dual UART/FIFO IC - FTDI + +usb:v0403pABB8* + ID_MODEL_FROM_DATABASE=Lego Mindstorms NXTCam + +usb:v0403pB810* + ID_MODEL_FROM_DATABASE=US Interface Navigator (CAT and 2nd PTT lines) + +usb:v0403pB811* + ID_MODEL_FROM_DATABASE=US Interface Navigator (WKEY and FSK lines) + +usb:v0403pB812* + ID_MODEL_FROM_DATABASE=US Interface Navigator (RS232 and CONFIG lines) + +usb:v0403pB9B0* + ID_MODEL_FROM_DATABASE=Fujitsu SK-16FX-100PMC V1.1 + +usb:v0403pBAF8* + ID_MODEL_FROM_DATABASE=Amontec JTAGkey + +usb:v0403pBCD8* + ID_MODEL_FROM_DATABASE=Stellaris Development Board + +usb:v0403pBCD9* + ID_MODEL_FROM_DATABASE=Stellaris Evaluation Board + +usb:v0403pBCDA* + ID_MODEL_FROM_DATABASE=Stellaris ICDI Board + +usb:v0403pBDC8* + ID_MODEL_FROM_DATABASE=Egnite GmbH - JTAG/RS-232 adapter + +usb:v0403pBFD8* + ID_MODEL_FROM_DATABASE=OpenDCC + +usb:v0403pBFD9* + ID_MODEL_FROM_DATABASE=OpenDCC (Sniffer) + +usb:v0403pBFDA* + ID_MODEL_FROM_DATABASE=OpenDCC (Throttle) + +usb:v0403pBFDB* + ID_MODEL_FROM_DATABASE=OpenDCC (Gateway) + +usb:v0403pBFDC* + ID_MODEL_FROM_DATABASE=OpenDCC (GBM) + +usb:v0403pC630* + ID_MODEL_FROM_DATABASE=lcd2usb interface + +usb:v0403pC631* + ID_MODEL_FROM_DATABASE=i2c-tiny-usb interface + +usb:v0403pC632* + ID_MODEL_FROM_DATABASE=xu1541 c64 floppy drive interface + +usb:v0403pC633* + ID_MODEL_FROM_DATABASE=TinyCrypt dongle + +usb:v0403pC634* + ID_MODEL_FROM_DATABASE=glcd2usb interface + +usb:v0403pC7D0* + ID_MODEL_FROM_DATABASE=RR-CirKits LocoBuffer-USB + +usb:v0403pC8B8* + ID_MODEL_FROM_DATABASE=Alpermann+Velte MTD TCU + +usb:v0403pC8B9* + ID_MODEL_FROM_DATABASE=Alpermann+Velte MTD TCU 1HE + +usb:v0403pC8BA* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium H1 + +usb:v0403pC8BB* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium H3 + +usb:v0403pC8BC* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium S1 + +usb:v0403pC8BD* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium T1 + +usb:v0403pC8BE* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Rubidium D1 + +usb:v0403pC8BF* + ID_MODEL_FROM_DATABASE=Alpermann+Velte TC60 RLV + +usb:v0403pCC48* + ID_MODEL_FROM_DATABASE=Tactrix OpenPort 1.3 Mitsubishi + +usb:v0403pCC49* + ID_MODEL_FROM_DATABASE=Tactrix OpenPort 1.3 Subaru + +usb:v0403pCC4A* + ID_MODEL_FROM_DATABASE=Tactrix OpenPort 1.3 Universal + +usb:v0403pCFF8* + ID_MODEL_FROM_DATABASE=Amontec JTAGkey + +usb:v0403pD010* + ID_MODEL_FROM_DATABASE=SCS PTC-IIusb + +usb:v0403pD011* + ID_MODEL_FROM_DATABASE=SCS Position-Tracker/TNC + +usb:v0403pD012* + ID_MODEL_FROM_DATABASE=SCS DRAGON 1 + +usb:v0403pD013* + ID_MODEL_FROM_DATABASE=SCS DRAGON 1 + +usb:v0403pD388* + ID_MODEL_FROM_DATABASE=Xsens converter + +usb:v0403pD389* + ID_MODEL_FROM_DATABASE=Xsens Wireless Receiver + +usb:v0403pD38A* + ID_MODEL_FROM_DATABASE=Xsens serial converter + +usb:v0403pD38B* + ID_MODEL_FROM_DATABASE=Xsens serial converter + +usb:v0403pD38C* + ID_MODEL_FROM_DATABASE=Xsens Wireless Receiver + +usb:v0403pD38D* + ID_MODEL_FROM_DATABASE=Xsens Awinda Station + +usb:v0403pD38E* + ID_MODEL_FROM_DATABASE=Xsens serial converter + +usb:v0403pD38F* + ID_MODEL_FROM_DATABASE=Xsens serial converter + +usb:v0403pD491* + ID_MODEL_FROM_DATABASE=Zolix Omni 1509 monochromator + +usb:v0403pD578* + ID_MODEL_FROM_DATABASE=Accesio USB-COM-4SM + +usb:v0403pD6F8* + ID_MODEL_FROM_DATABASE=UNI Black BOX + +usb:v0403pD738* + ID_MODEL_FROM_DATABASE=Propox JTAGcable II + +usb:v0403pD739* + ID_MODEL_FROM_DATABASE=Propox ISPcable III + +usb:v0403pD9A9* + ID_MODEL_FROM_DATABASE=Actisense USG-1 NMEA Serial Gateway + +usb:v0403pD9AA* + ID_MODEL_FROM_DATABASE=Actisense NGT-1 NMEA2000 PC Interface + +usb:v0403pD9AB* + ID_MODEL_FROM_DATABASE=Actisense NGT-1 NMEA2000 Gateway + +usb:v0403pDAF4* + ID_MODEL_FROM_DATABASE=Qundis Serial Infrared Head + +usb:v0403pE0D0* + ID_MODEL_FROM_DATABASE=Total Phase Aardvark I2C/SPI Host Adapter + +usb:v0403pE521* + ID_MODEL_FROM_DATABASE=EVER Sinline XL Series UPS + +usb:v0403pE6C8* + ID_MODEL_FROM_DATABASE=PYRAMID Computer GmbH LCD + +usb:v0403pE700* + ID_MODEL_FROM_DATABASE=Elster Unicom III Optical Probe + +usb:v0403pE729* + ID_MODEL_FROM_DATABASE=Segway Robotic Mobility Platforms 200 + +usb:v0403pE888* + ID_MODEL_FROM_DATABASE=Expert ISDN Control USB + +usb:v0403pE889* + ID_MODEL_FROM_DATABASE=USB-RS232 OptoBridge + +usb:v0403pE88A* + ID_MODEL_FROM_DATABASE=Expert mouseCLOCK USB II + +usb:v0403pE88B* + ID_MODEL_FROM_DATABASE=Precision Clock MSF USB + +usb:v0403pE88C* + ID_MODEL_FROM_DATABASE=Expert mouseCLOCK USB II HBG + +usb:v0403pE8D8* + ID_MODEL_FROM_DATABASE=Aaronia AG Spectran Spectrum Analyzer + +usb:v0403pE8DC* + ID_MODEL_FROM_DATABASE=Aaronia AG UBBV Preamplifier + +usb:v0403pEA90* + ID_MODEL_FROM_DATABASE=Eclo 1-Wire Adapter + +usb:v0403pECD9* + ID_MODEL_FROM_DATABASE=miControl miCan-Stick + +usb:v0403pED71* + ID_MODEL_FROM_DATABASE=HAMEG HO870 Serial Port + +usb:v0403pED72* + ID_MODEL_FROM_DATABASE=HAMEG HO720 Serial Port + +usb:v0403pED73* + ID_MODEL_FROM_DATABASE=HAMEG HO730 Serial Port + +usb:v0403pED74* + ID_MODEL_FROM_DATABASE=HAMEG HO820 Serial Port + +usb:v0403pEF10* + ID_MODEL_FROM_DATABASE=FT1245BL + +usb:v0403pF070* + ID_MODEL_FROM_DATABASE=Serial Converter 422/485 [Vardaan VEUSB422R3] + +usb:v0403pF0C8* + ID_MODEL_FROM_DATABASE=SPROG Decoder Programmer + +usb:v0403pF0C9* + ID_MODEL_FROM_DATABASE=SPROG-DCC CAN-USB + +usb:v0403pF0E9* + ID_MODEL_FROM_DATABASE=Tagsys L-P101 + +usb:v0403pF1A0* + ID_MODEL_FROM_DATABASE=Asix PRESTO Programmer + +usb:v0403pF208* + ID_MODEL_FROM_DATABASE=Papenmeier Braille-Display + +usb:v0403pF3C0* + ID_MODEL_FROM_DATABASE=4N-GALAXY Serial Converter + +usb:v0403pF608* + ID_MODEL_FROM_DATABASE=CTI USB-485-Mini + +usb:v0403pF60B* + ID_MODEL_FROM_DATABASE=CTI USB-Nano-485 + +usb:v0403pF680* + ID_MODEL_FROM_DATABASE=Suunto Sports Instrument + +usb:v0403pF758* + ID_MODEL_FROM_DATABASE=GW Instek GDS-8x0 Oscilloscope + +usb:v0403pF7C0* + ID_MODEL_FROM_DATABASE=ZeitControl Cardsystems TagTracer MIFARE + +usb:v0403pF850* + ID_MODEL_FROM_DATABASE=USB-UIRT (Universal Infrared Receiver+Transmitter) + +usb:v0403pF918* + ID_MODEL_FROM_DATABASE=Ant8 Logic Probe + +usb:v0403pFA00* + ID_MODEL_FROM_DATABASE=Matrix Orbital USB Serial + +usb:v0403pFA01* + ID_MODEL_FROM_DATABASE=Matrix Orbital MX2 or MX3 + +usb:v0403pFA02* + ID_MODEL_FROM_DATABASE=Matrix Orbital MX4 or MX5 + +usb:v0403pFA03* + ID_MODEL_FROM_DATABASE=Matrix Orbital VK/LK202 Family + +usb:v0403pFA04* + ID_MODEL_FROM_DATABASE=Matrix Orbital VK/LK204 Family + +usb:v0403pFA20* + ID_MODEL_FROM_DATABASE=Ross-Tech HEX-USB + +usb:v0403pFC08* + ID_MODEL_FROM_DATABASE=Crystalfontz CFA-632 USB LCD + +usb:v0403pFC09* + ID_MODEL_FROM_DATABASE=Crystalfontz CFA-634 USB LCD + +usb:v0403pFC0B* + ID_MODEL_FROM_DATABASE=Crystalfontz CFA-633 USB LCD + +usb:v0403pFC0C* + ID_MODEL_FROM_DATABASE=Crystalfontz CFA-631 USB LCD + +usb:v0403pFC0D* + ID_MODEL_FROM_DATABASE=Crystalfontz CFA-635 USB LCD + +usb:v0403pFC82* + ID_MODEL_FROM_DATABASE=SEMC DSS-20/DSS-25 SyncStation + +usb:v0403pFD48* + ID_MODEL_FROM_DATABASE=ShipModul MiniPlex-4xUSB NMEA Multiplexer + +usb:v0403pFD49* + ID_MODEL_FROM_DATABASE=ShipModul MiniPlex-4xUSB-AIS NMEA Multiplexer + +usb:v0403pFD4B* + ID_MODEL_FROM_DATABASE=ShipModul MiniPlex NMEA Multiplexer + +usb:v0403pFF08* + ID_MODEL_FROM_DATABASE=ToolHouse LoopBack Adapter + +usb:v0403pFF18* + ID_MODEL_FROM_DATABASE=ScienceScope Logbook ML + +usb:v0403pFF19* + ID_MODEL_FROM_DATABASE=Logbook Bus + +usb:v0403pFF1A* + ID_MODEL_FROM_DATABASE=Logbook Bus + +usb:v0403pFF1B* + ID_MODEL_FROM_DATABASE=Logbook Bus + +usb:v0403pFF1C* + ID_MODEL_FROM_DATABASE=ScienceScope Logbook LS + +usb:v0403pFF1D* + ID_MODEL_FROM_DATABASE=ScienceScope Logbook HS + +usb:v0403pFF1E* + ID_MODEL_FROM_DATABASE=Logbook Bus + +usb:v0403pFF1F* + ID_MODEL_FROM_DATABASE=Logbook Bus + +usb:v0404* + ID_VENDOR_FROM_DATABASE=NCR Corp. + +usb:v0404p0202* + ID_MODEL_FROM_DATABASE=78XX Scanner + +usb:v0404p0203* + ID_MODEL_FROM_DATABASE=78XX Scanner - Embedded System + +usb:v0404p0310* + ID_MODEL_FROM_DATABASE=K590 Printer, Self-Service + +usb:v0404p0311* + ID_MODEL_FROM_DATABASE=7167 Printer, Receipt/Slip + +usb:v0404p0312* + ID_MODEL_FROM_DATABASE=7197 Printer Receipt + +usb:v0404p0320* + ID_MODEL_FROM_DATABASE=5932-USB Keyboard + +usb:v0404p0321* + ID_MODEL_FROM_DATABASE=5953-USB Dynakey + +usb:v0404p0322* + ID_MODEL_FROM_DATABASE=5932-USB Enhanced Keyboard + +usb:v0404p0323* + ID_MODEL_FROM_DATABASE=5932-USB Enhanced Keyboard, Flash-Recovery/Download + +usb:v0404p0324* + ID_MODEL_FROM_DATABASE=5953-USB Enhanced Dynakey + +usb:v0404p0325* + ID_MODEL_FROM_DATABASE=5953-USB Enhanced Dynakey Flash-Recovery/Download + +usb:v0404p0328* + ID_MODEL_FROM_DATABASE=K016: USB-MSR ISO 3-track MSR: POS Standard (See HID pages) + +usb:v0404p0329* + ID_MODEL_FROM_DATABASE=K018: USB-MSR JIS 2-Track MSR: POS Standard + +usb:v0404p032A* + ID_MODEL_FROM_DATABASE=K016: USB-MSR ISO 3-Track MSR: HID Keyboard Mode + +usb:v0404p032B* + ID_MODEL_FROM_DATABASE=K016/K018: USB-MSR Flash-Recovery/Download + +usb:v0405* + ID_VENDOR_FROM_DATABASE=Synopsys, Inc. + +usb:v0406* + ID_VENDOR_FROM_DATABASE=Fujitsu-ICL Computers + +usb:v0407* + ID_VENDOR_FROM_DATABASE=Fujitsu Personal Systems, Inc. + +usb:v0408* + ID_VENDOR_FROM_DATABASE=Quanta Computer, Inc. + +usb:v0408p0103* + ID_MODEL_FROM_DATABASE=FV TouchCam N1 (Audio) + +usb:v0408p030C* + ID_MODEL_FROM_DATABASE=HP Webcam + +usb:v0408p03B2* + ID_MODEL_FROM_DATABASE=HP Webcam + +usb:v0408p1030* + ID_MODEL_FROM_DATABASE=FV TouchCam N1 (Video) + +usb:v0408p3000* + ID_MODEL_FROM_DATABASE=Optical dual-touch panel + +usb:v0408p3001* + ID_MODEL_FROM_DATABASE=Optical Touch Screen + +usb:v0409* + ID_VENDOR_FROM_DATABASE=NEC Corp. + +usb:v0409p0011* + ID_MODEL_FROM_DATABASE=PC98 Series Layout Keyboard Mouse + +usb:v0409p0012* + ID_MODEL_FROM_DATABASE=ATerm IT75DSU ISDN TA + +usb:v0409p0014* + ID_MODEL_FROM_DATABASE=Japanese Keyboard + +usb:v0409p0019* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard with Bus-Powered Hub + +usb:v0409p001A* + ID_MODEL_FROM_DATABASE=PC98 Series Layout Keyboard with Bus-Powered Hub + +usb:v0409p0025* + ID_MODEL_FROM_DATABASE=Mini Keyboard with Bus-Powered Hub + +usb:v0409p0027* + ID_MODEL_FROM_DATABASE=MultiSync Monitor + +usb:v0409p002C* + ID_MODEL_FROM_DATABASE=Clik!-USB Drive + +usb:v0409p0034* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard with One-touch start buttons + +usb:v0409p003F* + ID_MODEL_FROM_DATABASE=Wireless Keyboard with One-touch start buttons + +usb:v0409p0040* + ID_MODEL_FROM_DATABASE=Floppy + +usb:v0409p004E* + ID_MODEL_FROM_DATABASE=SuperScript 1400 Series + +usb:v0409p004F* + ID_MODEL_FROM_DATABASE=Wireless Keyboard with One-touch start buttons + +usb:v0409p0050* + ID_MODEL_FROM_DATABASE=7-port hub + +usb:v0409p0058* + ID_MODEL_FROM_DATABASE=HighSpeed Hub + +usb:v0409p0059* + ID_MODEL_FROM_DATABASE=HighSpeed Hub + +usb:v0409p005A* + ID_MODEL_FROM_DATABASE=HighSpeed Hub + +usb:v0409p006A* + ID_MODEL_FROM_DATABASE=Conceptronic USB Harddisk Box + +usb:v0409p007D* + ID_MODEL_FROM_DATABASE=MINICUBE2 + +usb:v0409p007E* + ID_MODEL_FROM_DATABASE=PG-FP5 Flash Memory Programmer + +usb:v0409p0081* + ID_MODEL_FROM_DATABASE=SuperScript 1400 Series + +usb:v0409p0082* + ID_MODEL_FROM_DATABASE=SuperScript 1400 Series + +usb:v0409p0094* + ID_MODEL_FROM_DATABASE=Japanese Keyboard with One-touch start buttons + +usb:v0409p0095* + ID_MODEL_FROM_DATABASE=Japanese Keyboard + +usb:v0409p00A9* + ID_MODEL_FROM_DATABASE=AtermIT21L 128K Support Standard + +usb:v0409p00AA* + ID_MODEL_FROM_DATABASE=AtermITX72 128K Support Standard + +usb:v0409p00AB* + ID_MODEL_FROM_DATABASE=AtermITX62 128K Support Standard + +usb:v0409p00AC* + ID_MODEL_FROM_DATABASE=AtermIT42 128K Support Standard + +usb:v0409p00AE* + ID_MODEL_FROM_DATABASE=INSMATEV70G-MAX Standard + +usb:v0409p00AF* + ID_MODEL_FROM_DATABASE=AtermITX70 128K Support Standard + +usb:v0409p00B0* + ID_MODEL_FROM_DATABASE=AtermITX80 128K Support Standard + +usb:v0409p00B2* + ID_MODEL_FROM_DATABASE=AtermITX80D 128K Support Standard + +usb:v0409p00C0* + ID_MODEL_FROM_DATABASE=Wireless Remocon + +usb:v0409p00F7* + ID_MODEL_FROM_DATABASE=Smart Display PK-SD10 + +usb:v0409p011D* + ID_MODEL_FROM_DATABASE=e228 Mobile Phone + +usb:v0409p0203* + ID_MODEL_FROM_DATABASE=HID Audio Controls + +usb:v0409p021D* + ID_MODEL_FROM_DATABASE=Aterm WL54SU2 802.11g Wireless Adapter [Atheros AR5523] + +usb:v0409p0248* + ID_MODEL_FROM_DATABASE=Aterm PA-WL54GU + +usb:v0409p0249* + ID_MODEL_FROM_DATABASE=Aterm WL300NU-G + +usb:v0409p02B4* + ID_MODEL_FROM_DATABASE=Aterm WL300NU-AG + +usb:v0409p02B6* + ID_MODEL_FROM_DATABASE=Aterm WL300NU-GS 802.11n Wireless Adapter + +usb:v0409p02BC* + ID_MODEL_FROM_DATABASE=Computer Monitor + +usb:v0409p0300* + ID_MODEL_FROM_DATABASE=LifeTouch Note + +usb:v0409p0301* + ID_MODEL_FROM_DATABASE=LifeTouch Note (debug mode) + +usb:v0409p55AA* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0409p55AB* + ID_MODEL_FROM_DATABASE=Hub [iMac/iTouch kbd] + +usb:v0409p8010* + ID_MODEL_FROM_DATABASE=Intellibase Hub + +usb:v0409p8011* + ID_MODEL_FROM_DATABASE=Intellibase Hub + +usb:v0409pEFBE* + ID_MODEL_FROM_DATABASE=P!cty 900 [HP DJ] + +usb:v0409pF0BE* + ID_MODEL_FROM_DATABASE=P!cty 920 [HP DJ 812c] + +usb:v040A* + ID_VENDOR_FROM_DATABASE=Kodak Co. + +usb:v040Ap0001* + ID_MODEL_FROM_DATABASE=DVC-323 + +usb:v040Ap0002* + ID_MODEL_FROM_DATABASE=DVC-325 + +usb:v040Ap0100* + ID_MODEL_FROM_DATABASE=DC-220 + +usb:v040Ap0110* + ID_MODEL_FROM_DATABASE=DC-260 + +usb:v040Ap0111* + ID_MODEL_FROM_DATABASE=DC-265 + +usb:v040Ap0112* + ID_MODEL_FROM_DATABASE=DC-290 + +usb:v040Ap0120* + ID_MODEL_FROM_DATABASE=DC-240 + +usb:v040Ap0121* + ID_MODEL_FROM_DATABASE=DC-240 (PTP firmware) + +usb:v040Ap0130* + ID_MODEL_FROM_DATABASE=DC-280 + +usb:v040Ap0131* + ID_MODEL_FROM_DATABASE=DC-5000 + +usb:v040Ap0132* + ID_MODEL_FROM_DATABASE=DC-3400 + +usb:v040Ap0140* + ID_MODEL_FROM_DATABASE=DC-4800 + +usb:v040Ap0160* + ID_MODEL_FROM_DATABASE=DC4800 + +usb:v040Ap0170* + ID_MODEL_FROM_DATABASE=DX3900 + +usb:v040Ap0200* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0300* + ID_MODEL_FROM_DATABASE=EZ-200 + +usb:v040Ap0400* + ID_MODEL_FROM_DATABASE=MC3 + +usb:v040Ap0402* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0403* + ID_MODEL_FROM_DATABASE=Z7590 + +usb:v040Ap0500* + ID_MODEL_FROM_DATABASE=DX3500 + +usb:v040Ap0510* + ID_MODEL_FROM_DATABASE=DX3600 + +usb:v040Ap0525* + ID_MODEL_FROM_DATABASE=DX3215 + +usb:v040Ap0530* + ID_MODEL_FROM_DATABASE=DX3700 + +usb:v040Ap0535* + ID_MODEL_FROM_DATABASE=EasyShare CX4230 Camera + +usb:v040Ap0540* + ID_MODEL_FROM_DATABASE=LS420 + +usb:v040Ap0550* + ID_MODEL_FROM_DATABASE=DX4900 + +usb:v040Ap0555* + ID_MODEL_FROM_DATABASE=DX4330 + +usb:v040Ap0560* + ID_MODEL_FROM_DATABASE=CX4200 + +usb:v040Ap0565* + ID_MODEL_FROM_DATABASE=CX4210 + +usb:v040Ap0566* + ID_MODEL_FROM_DATABASE=CX4300 + +usb:v040Ap0567* + ID_MODEL_FROM_DATABASE=LS753 + +usb:v040Ap0568* + ID_MODEL_FROM_DATABASE=LS443 + +usb:v040Ap0569* + ID_MODEL_FROM_DATABASE=LS663 + +usb:v040Ap0570* + ID_MODEL_FROM_DATABASE=DX6340 + +usb:v040Ap0571* + ID_MODEL_FROM_DATABASE=CX6330 + +usb:v040Ap0572* + ID_MODEL_FROM_DATABASE=DX6440 + +usb:v040Ap0573* + ID_MODEL_FROM_DATABASE=CX6230 + +usb:v040Ap0574* + ID_MODEL_FROM_DATABASE=CX6200 + +usb:v040Ap0575* + ID_MODEL_FROM_DATABASE=DX6490 + +usb:v040Ap0576* + ID_MODEL_FROM_DATABASE=DX4530 + +usb:v040Ap0577* + ID_MODEL_FROM_DATABASE=DX7630 + +usb:v040Ap0578* + ID_MODEL_FROM_DATABASE=CX7300/CX7310 + +usb:v040Ap0579* + ID_MODEL_FROM_DATABASE=CX7220 + +usb:v040Ap057A* + ID_MODEL_FROM_DATABASE=CX7330 + +usb:v040Ap057B* + ID_MODEL_FROM_DATABASE=CX7430 + +usb:v040Ap057C* + ID_MODEL_FROM_DATABASE=CX7530 + +usb:v040Ap057D* + ID_MODEL_FROM_DATABASE=DX7440 + +usb:v040Ap057E* + ID_MODEL_FROM_DATABASE=C300 + +usb:v040Ap057F* + ID_MODEL_FROM_DATABASE=DX7590 + +usb:v040Ap0580* + ID_MODEL_FROM_DATABASE=Z730 + +usb:v040Ap0581* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0582* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0583* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0584* + ID_MODEL_FROM_DATABASE=CX6445 + +usb:v040Ap0585* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0586* + ID_MODEL_FROM_DATABASE=CX7525 + +usb:v040Ap0587* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0588* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0589* + ID_MODEL_FROM_DATABASE=EasyShare C360 + +usb:v040Ap058A* + ID_MODEL_FROM_DATABASE=C310 + +usb:v040Ap058B* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap058C* + ID_MODEL_FROM_DATABASE=C330 + +usb:v040Ap058D* + ID_MODEL_FROM_DATABASE=C340 + +usb:v040Ap058E* + ID_MODEL_FROM_DATABASE=V530 + +usb:v040Ap058F* + ID_MODEL_FROM_DATABASE=V550 + +usb:v040Ap0590* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0591* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0592* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0593* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0594* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0595* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0596* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0597* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap0598* + ID_MODEL_FROM_DATABASE=EASYSHARE M1033 digital camera + +usb:v040Ap0599* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059A* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059B* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059C* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059D* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059E* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap059F* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A0* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A1* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A2* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A3* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A4* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A5* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A6* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A7* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A8* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05A9* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AA* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AB* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AC* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AD* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AE* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05AF* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B0* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B1* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B2* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B3* + ID_MODEL_FROM_DATABASE=EasyShare Z710 Camera + +usb:v040Ap05B4* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B5* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B6* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B7* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B8* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05B9* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BA* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BB* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BC* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BD* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BE* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05BF* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C0* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C1* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C2* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C3* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C4* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C5* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v040Ap05C8* + ID_MODEL_FROM_DATABASE=EASYSHARE Z1485 IS Digital Camera + +usb:v040Ap05D3* + ID_MODEL_FROM_DATABASE=EasyShare M320 Camera + +usb:v040Ap05D4* + ID_MODEL_FROM_DATABASE=EasyShare C180 Digital Camera + +usb:v040Ap1001* + ID_MODEL_FROM_DATABASE=EasyShare SV811 Digital Picture Frame + +usb:v040Ap4000* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v040Ap4021* + ID_MODEL_FROM_DATABASE=Photo Printer 6800 + +usb:v040Ap4022* + ID_MODEL_FROM_DATABASE=1400 Digital Photo Printer + +usb:v040Ap402B* + ID_MODEL_FROM_DATABASE=Photo Printer 6850 + +usb:v040Ap402E* + ID_MODEL_FROM_DATABASE=605 Photo Printer + +usb:v040Ap4034* + ID_MODEL_FROM_DATABASE=805 Photo Printer + +usb:v040Ap404F* + ID_MODEL_FROM_DATABASE=305 Photo Printer + +usb:v040Ap4056* + ID_MODEL_FROM_DATABASE=ESP 7200 Series AiO + +usb:v040Ap4109* + ID_MODEL_FROM_DATABASE=EasyShare Printer Dock Series 3 + +usb:v040Ap410D* + ID_MODEL_FROM_DATABASE=EasyShare G600 Printer Dock + +usb:v040Ap5010* + ID_MODEL_FROM_DATABASE=Wireless Adapter + +usb:v040Ap5012* + ID_MODEL_FROM_DATABASE=DBT-220 Bluetooth Adapter + +usb:v040Ap6001* + ID_MODEL_FROM_DATABASE=i30 + +usb:v040Ap6002* + ID_MODEL_FROM_DATABASE=i40 + +usb:v040Ap6003* + ID_MODEL_FROM_DATABASE=i50 + +usb:v040Ap6004* + ID_MODEL_FROM_DATABASE=i60 + +usb:v040Ap6005* + ID_MODEL_FROM_DATABASE=i80 + +usb:v040Ap6029* + ID_MODEL_FROM_DATABASE=i900 + +usb:v040Ap602A* + ID_MODEL_FROM_DATABASE=i900 + +usb:v040B* + ID_VENDOR_FROM_DATABASE=Weltrend Semiconductor + +usb:v040Bp0A68* + ID_MODEL_FROM_DATABASE=Func MS-3 gaming mouse [WT6573F MCU] + +usb:v040Bp6510* + ID_MODEL_FROM_DATABASE=Weltrend Bar Code Reader + +usb:v040Bp6520* + ID_MODEL_FROM_DATABASE=XBOX Xploder + +usb:v040Bp6533* + ID_MODEL_FROM_DATABASE=Speed-Link Competition Pro + +usb:v040Bp6543* + ID_MODEL_FROM_DATABASE=Manhattan Magnetic Card Strip Reader + +usb:v040C* + ID_VENDOR_FROM_DATABASE=VTech Computers, Ltd + +usb:v040D* + ID_VENDOR_FROM_DATABASE=VIA Technologies, Inc. + +usb:v040Dp3184* + ID_MODEL_FROM_DATABASE=VNT VT6656 USB-802.11 Wireless LAN Adapter + +usb:v040Dp6205* + ID_MODEL_FROM_DATABASE=USB 2.0 Card Reader + +usb:v040E* + ID_VENDOR_FROM_DATABASE=MCCI + +usb:v040F* + ID_VENDOR_FROM_DATABASE=Echo Speech Corp. + +usb:v0411* + ID_VENDOR_FROM_DATABASE=BUFFALO INC. (formerly MelCo., Inc.) + +usb:v0411p0001* + ID_MODEL_FROM_DATABASE=LUA-TX Ethernet [pegasus] + +usb:v0411p0005* + ID_MODEL_FROM_DATABASE=LUA-TX Ethernet + +usb:v0411p0006* + ID_MODEL_FROM_DATABASE=WLI-USB-L11 Wireless LAN Adapter + +usb:v0411p0009* + ID_MODEL_FROM_DATABASE=LUA2-TX Ethernet + +usb:v0411p000B* + ID_MODEL_FROM_DATABASE=WLI-USB-L11G-WR Wireless LAN Adapter + +usb:v0411p000D* + ID_MODEL_FROM_DATABASE=WLI-USB-L11G Wireless LAN Adapter + +usb:v0411p0012* + ID_MODEL_FROM_DATABASE=LUA-KTX Ethernet + +usb:v0411p0013* + ID_MODEL_FROM_DATABASE=USB2-IDE Adapter + +usb:v0411p0016* + ID_MODEL_FROM_DATABASE=WLI-USB-S11 802.11b Adapter + +usb:v0411p0018* + ID_MODEL_FROM_DATABASE=USB2-IDE Adapter + +usb:v0411p001C* + ID_MODEL_FROM_DATABASE=USB-IDE Bridge: DUB-PxxG + +usb:v0411p0027* + ID_MODEL_FROM_DATABASE=WLI-USB-KS11G 802.11b Adapter + +usb:v0411p002A* + ID_MODEL_FROM_DATABASE=SMSC USB97C202 "HD-HB300V2-EU" + +usb:v0411p003D* + ID_MODEL_FROM_DATABASE=LUA-U2-KTX Ethernet + +usb:v0411p0044* + ID_MODEL_FROM_DATABASE=WLI-USB-KB11 Wireless LAN Adapter + +usb:v0411p004B* + ID_MODEL_FROM_DATABASE=WLI-USB-G54 802.11g Adapter [Broadcom 4320 USB] + +usb:v0411p004D* + ID_MODEL_FROM_DATABASE=WLI-USB-B11 Wireless LAN Adapter + +usb:v0411p0050* + ID_MODEL_FROM_DATABASE=WLI2-USB2-G54 Wireless LAN Adapter + +usb:v0411p005E* + ID_MODEL_FROM_DATABASE=WLI-U2-KG54-YB WLAN + +usb:v0411p0065* + ID_MODEL_FROM_DATABASE=Python2 WDM Encoder + +usb:v0411p0066* + ID_MODEL_FROM_DATABASE=WLI-U2-KG54 WLAN + +usb:v0411p0067* + ID_MODEL_FROM_DATABASE=WLI-U2-KG54-AI WLAN + +usb:v0411p006E* + ID_MODEL_FROM_DATABASE=LUA-U2-GT 10/100/1000 Ethernet Adapter + +usb:v0411p0089* + ID_MODEL_FROM_DATABASE=RUF-C/U2 Flash Drive + +usb:v0411p008B* + ID_MODEL_FROM_DATABASE=Nintendo Wi-Fi + +usb:v0411p0091* + ID_MODEL_FROM_DATABASE=WLI-U2-KAMG54 Wireless LAN Adapter + +usb:v0411p0092* + ID_MODEL_FROM_DATABASE=WLI-U2-KAMG54 Bootloader + +usb:v0411p0097* + ID_MODEL_FROM_DATABASE=WLI-U2-KG54-BB + +usb:v0411p00A9* + ID_MODEL_FROM_DATABASE=WLI-U2-AMG54HP Wireless LAN Adapter + +usb:v0411p00AA* + ID_MODEL_FROM_DATABASE=WLI-U2-AMG54HP Bootloader + +usb:v0411p00B3* + ID_MODEL_FROM_DATABASE=PC-OP-RS1 RemoteStation + +usb:v0411p00BC* + ID_MODEL_FROM_DATABASE=WLI-U2-KG125S 802.11g Adapter [Broadcom 4320 USB] + +usb:v0411p00CA* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0411p00CB* + ID_MODEL_FROM_DATABASE=WLI-U2-G300N 802.11n Adapter + +usb:v0411p00D8* + ID_MODEL_FROM_DATABASE=WLI-U2-SG54HP + +usb:v0411p00D9* + ID_MODEL_FROM_DATABASE=WLI-U2-G54HP + +usb:v0411p00DA* + ID_MODEL_FROM_DATABASE=WLI-U2-KG54L 802.11bg [ZyDAS ZD1211B] + +usb:v0411p00DB* + ID_MODEL_FROM_DATABASE=External Hard Drive HD-PF32OU2 [Buffalo Ministation] + +usb:v0411p00E8* + ID_MODEL_FROM_DATABASE=WLI-UC-G300N Wireless LAN Adapter [Ralink RT2870] + +usb:v0411p0105* + ID_MODEL_FROM_DATABASE=External Hard Drive HD-CEU2 [Drive Station] + +usb:v0411p012C* + ID_MODEL_FROM_DATABASE=SATA Bridge + +usb:v0411p012E* + ID_MODEL_FROM_DATABASE=WLI-UC-AG300N Wireless LAN Adapter + +usb:v0411p0148* + ID_MODEL_FROM_DATABASE=WLI-UC-G300HP Wireless LAN Adapter + +usb:v0411p0150* + ID_MODEL_FROM_DATABASE=WLP-UC-AG300 Wireless LAN Adapter + +usb:v0411p0157* + ID_MODEL_FROM_DATABASE=External Hard Drive HD-PEU2 + +usb:v0411p0158* + ID_MODEL_FROM_DATABASE=WLI-UC-GNHP Wireless LAN Adapter + +usb:v0411p015D* + ID_MODEL_FROM_DATABASE=WLI-UC-GN Wireless LAN Adapter [Ralink RT3070] + +usb:v0411p016F* + ID_MODEL_FROM_DATABASE=WLI-UC-G301N Wireless LAN Adapter [Ralink RT3072] + +usb:v0411p017F* + ID_MODEL_FROM_DATABASE=Sony UWA-BR100 802.11abgn Wireless Adapter [Atheros AR7010+AR9280] + +usb:v0411p019E* + ID_MODEL_FROM_DATABASE=WLI-UC-GNP Wireless LAN Adapter + +usb:v0411p01A1* + ID_MODEL_FROM_DATABASE=MiniStation Metro + +usb:v0411p01A2* + ID_MODEL_FROM_DATABASE=WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070] + +usb:v0411p01DC* + ID_MODEL_FROM_DATABASE=Ultra-Slim Portable DVD Writer (DVSM-PC58U2V) + +usb:v0411p01DE* + ID_MODEL_FROM_DATABASE=External Hard Drive HD-PCTU3 [Buffalo MiniStation] + +usb:v0411p01EE* + ID_MODEL_FROM_DATABASE=WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070] + +usb:v0411p01F1* + ID_MODEL_FROM_DATABASE=SATA Adapter [HD-LBU3] + +usb:v0411p01FD* + ID_MODEL_FROM_DATABASE=WLI-UC-G450 Wireless LAN Adapter + +usb:v0412* + ID_VENDOR_FROM_DATABASE=Award Software International + +usb:v0413* + ID_VENDOR_FROM_DATABASE=Leadtek Research, Inc. + +usb:v0413p1310* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC + FM + +usb:v0413p1311* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC + MTS + FM + +usb:v0413p1312* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL BG + FM + +usb:v0413p1313* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL BG+TXT + FM + +usb:v0413p1314* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL I + +usb:v0413p1315* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL I+TXT + +usb:v0413p1316* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL DK + +usb:v0413p1317* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL DK+TXT + +usb:v0413p1318* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL I/DK + FM + +usb:v0413p1319* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL N + FM + +usb:v0413p131A* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM LL + +usb:v0413p131B* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM LL+TXT + +usb:v0413p131C* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM DK + +usb:v0413p131D* + ID_MODEL_FROM_DATABASE=WinFast TV - SECAM DK + TXT + FM + +usb:v0413p131E* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC Japan + FM + +usb:v0413p1320* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC + +usb:v0413p1321* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC + MTS + +usb:v0413p1322* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL BG + +usb:v0413p1323* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL BG+TXT + +usb:v0413p1324* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL I + +usb:v0413p1325* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL I+TXT + +usb:v0413p1326* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL DK + +usb:v0413p1327* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP PAL DK+TXT + +usb:v0413p1328* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL I/DK + +usb:v0413p1329* + ID_MODEL_FROM_DATABASE=WinFast TV - PAL N + +usb:v0413p132A* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM LL + +usb:v0413p132B* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM LL+TXT + +usb:v0413p132C* + ID_MODEL_FROM_DATABASE=WinFast TV Audio - PHP SECAM DK + +usb:v0413p132D* + ID_MODEL_FROM_DATABASE=WinFast TV - SECAM DK + TXT + +usb:v0413p132E* + ID_MODEL_FROM_DATABASE=WinFast TV - NTSC Japan + +usb:v0413p6023* + ID_MODEL_FROM_DATABASE=EMP Audio Device + +usb:v0413p6024* + ID_MODEL_FROM_DATABASE=WinFast PalmTop/Novo TV Video + +usb:v0413p6025* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle (cold state) + +usb:v0413p6026* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle (warm state) + +usb:v0413p6029* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle Gold + +usb:v0413p6125* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle + +usb:v0413p6126* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle BDA Driver + +usb:v0413p6A03* + ID_MODEL_FROM_DATABASE=RTL2832 [WinFast DTV Dongle Mini] + +usb:v0413p6F00* + ID_MODEL_FROM_DATABASE=WinFast DTV Dongle (STK7700P based) + +usb:v0414* + ID_VENDOR_FROM_DATABASE=Giga-Byte Technology Co., Ltd + +usb:v0416* + ID_VENDOR_FROM_DATABASE=Winbond Electronics Corp. + +usb:v0416p0035* + ID_MODEL_FROM_DATABASE=W89C35 802.11bg WLAN Adapter + +usb:v0416p0101* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0416p0961* + ID_MODEL_FROM_DATABASE=AVL Flash Card Reader + +usb:v0416p3810* + ID_MODEL_FROM_DATABASE=Smart Card Controller + +usb:v0416p3811* + ID_MODEL_FROM_DATABASE=Generic Controller - Single interface + +usb:v0416p3812* + ID_MODEL_FROM_DATABASE=Smart Card Controller_2Interface + +usb:v0416p3813* + ID_MODEL_FROM_DATABASE=Panel Display + +usb:v0416p5011* + ID_MODEL_FROM_DATABASE=Virtual Com Port + +usb:v0416p5518* + ID_MODEL_FROM_DATABASE=4-Port Hub + +usb:v0416p551A* + ID_MODEL_FROM_DATABASE=PC Sync Keypad + +usb:v0416p551B* + ID_MODEL_FROM_DATABASE=PC Async Keypad + +usb:v0416p551C* + ID_MODEL_FROM_DATABASE=Sync Tenkey + +usb:v0416p551D* + ID_MODEL_FROM_DATABASE=Async Tenkey + +usb:v0416p551E* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0416p551F* + ID_MODEL_FROM_DATABASE=Keyboard w/ Sys and Media + +usb:v0416p5521* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0416p6481* + ID_MODEL_FROM_DATABASE=16-bit Scanner + +usb:v0416p7721* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v0416p7722* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v0416p7723* + ID_MODEL_FROM_DATABASE=SD Card Reader + +usb:v0417* + ID_VENDOR_FROM_DATABASE=Symbios Logic + +usb:v0418* + ID_VENDOR_FROM_DATABASE=AST Research + +usb:v0419* + ID_VENDOR_FROM_DATABASE=Samsung Info. Systems America, Inc. + +usb:v0419p0001* + ID_MODEL_FROM_DATABASE=IrDA Remote Controller / Creative Cordless Mouse + +usb:v0419p0600* + ID_MODEL_FROM_DATABASE=Desktop Wireless 6000 + +usb:v0419p3001* + ID_MODEL_FROM_DATABASE=Xerox P1202 Laser Printer + +usb:v0419p3003* + ID_MODEL_FROM_DATABASE=Olivetti PG L12L + +usb:v0419p3201* + ID_MODEL_FROM_DATABASE=Docuprint P8ex + +usb:v0419p3404* + ID_MODEL_FROM_DATABASE=SCX-5x12 series + +usb:v0419p3406* + ID_MODEL_FROM_DATABASE=MFP 830 series + +usb:v0419p3407* + ID_MODEL_FROM_DATABASE=ML-912 + +usb:v0419p3601* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0419p3602* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0419p4602* + ID_MODEL_FROM_DATABASE=Remote NDIS Network Device + +usb:v0419p8001* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0419p8002* + ID_MODEL_FROM_DATABASE=SyncMaster HID Monitor Control + +usb:v0419pAA03* + ID_MODEL_FROM_DATABASE=SDAS-3 MP3 Player + +usb:v041A* + ID_VENDOR_FROM_DATABASE=Phoenix Technologies, Ltd + +usb:v041B* + ID_VENDOR_FROM_DATABASE=d'TV + +usb:v041D* + ID_VENDOR_FROM_DATABASE=S3, Inc. + +usb:v041E* + ID_VENDOR_FROM_DATABASE=Creative Technology, Ltd + +usb:v041Ep1002* + ID_MODEL_FROM_DATABASE=Nomad II + +usb:v041Ep1003* + ID_MODEL_FROM_DATABASE=Blaster GamePad Cobra + +usb:v041Ep1050* + ID_MODEL_FROM_DATABASE=GamePad Cobra + +usb:v041Ep1053* + ID_MODEL_FROM_DATABASE=Mouse Gamer HD7600L + +usb:v041Ep200C* + ID_MODEL_FROM_DATABASE=MuVo V100 + +usb:v041Ep2020* + ID_MODEL_FROM_DATABASE=Zen X-Fi 2 + +usb:v041Ep2029* + ID_MODEL_FROM_DATABASE=ZiiO + +usb:v041Ep2801* + ID_MODEL_FROM_DATABASE=Prodikeys PC-MIDI multifunction keyboard + +usb:v041Ep3000* + ID_MODEL_FROM_DATABASE=SoundBlaster Extigy + +usb:v041Ep3002* + ID_MODEL_FROM_DATABASE=SB External Composite Device + +usb:v041Ep3010* + ID_MODEL_FROM_DATABASE=SoundBlaster MP3+ + +usb:v041Ep3014* + ID_MODEL_FROM_DATABASE=SB External Composite Device + +usb:v041Ep3015* + ID_MODEL_FROM_DATABASE=Sound Blaster Digital Music LX + +usb:v041Ep3020* + ID_MODEL_FROM_DATABASE=SoundBlaster Audigy 2 NX + +usb:v041Ep3030* + ID_MODEL_FROM_DATABASE=SB External Composite Device + +usb:v041Ep3040* + ID_MODEL_FROM_DATABASE=SoundBlaster Live! 24-bit External SB0490 + +usb:v041Ep3060* + ID_MODEL_FROM_DATABASE=Sound Blaster Audigy 2 ZS External + +usb:v041Ep3061* + ID_MODEL_FROM_DATABASE=SoundBlaster Audigy 2 ZS Video Editor + +usb:v041Ep3090* + ID_MODEL_FROM_DATABASE=Sound Blaster Digital Music SX + +usb:v041Ep30D0* + ID_MODEL_FROM_DATABASE=Xmod + +usb:v041Ep30D3* + ID_MODEL_FROM_DATABASE=Sound Blaster Play! + +usb:v041Ep3100* + ID_MODEL_FROM_DATABASE=IR Receiver (SB0540) + +usb:v041Ep3121* + ID_MODEL_FROM_DATABASE=WoW tap chat + +usb:v041Ep3220* + ID_MODEL_FROM_DATABASE=Sound Blaster Tactic(3D) Sigma sound card + +usb:v041Ep3F00* + ID_MODEL_FROM_DATABASE=E-Mu Xboard 25 MIDI Controller + +usb:v041Ep3F02* + ID_MODEL_FROM_DATABASE=E-Mu 0202 + +usb:v041Ep3F04* + ID_MODEL_FROM_DATABASE=E-Mu 0404 + +usb:v041Ep3F07* + ID_MODEL_FROM_DATABASE=E-Mu Xmidi 1x1 + +usb:v041Ep3F0E* + ID_MODEL_FROM_DATABASE=Xmidi 1x1 Tab + +usb:v041Ep4003* + ID_MODEL_FROM_DATABASE=VideoBlaster Webcam Go Plus [W9967CF] + +usb:v041Ep4004* + ID_MODEL_FROM_DATABASE=Nomad II MG + +usb:v041Ep4005* + ID_MODEL_FROM_DATABASE=Webcam Blaster Go ES + +usb:v041Ep4007* + ID_MODEL_FROM_DATABASE=Go Mini + +usb:v041Ep400A* + ID_MODEL_FROM_DATABASE=PC-Cam 300 + +usb:v041Ep400B* + ID_MODEL_FROM_DATABASE=PC-Cam 600 + +usb:v041Ep400C* + ID_MODEL_FROM_DATABASE=Webcam 5 [pwc] + +usb:v041Ep400D* + ID_MODEL_FROM_DATABASE=Webcam PD1001 + +usb:v041Ep400F* + ID_MODEL_FROM_DATABASE=PC-CAM 550 (Composite) + +usb:v041Ep4011* + ID_MODEL_FROM_DATABASE=Webcam PRO eX + +usb:v041Ep4012* + ID_MODEL_FROM_DATABASE=PC-CAM350 + +usb:v041Ep4013* + ID_MODEL_FROM_DATABASE=PC-Cam 750 + +usb:v041Ep4015* + ID_MODEL_FROM_DATABASE=CardCam Value + +usb:v041Ep4016* + ID_MODEL_FROM_DATABASE=CardCam + +usb:v041Ep4017* + ID_MODEL_FROM_DATABASE=Webcam Mobile [PD1090] + +usb:v041Ep4018* + ID_MODEL_FROM_DATABASE=Webcam Vista [PD1100] + +usb:v041Ep4019* + ID_MODEL_FROM_DATABASE=Audio Device + +usb:v041Ep401A* + ID_MODEL_FROM_DATABASE=Webcam Vista [PD1100] + +usb:v041Ep401C* + ID_MODEL_FROM_DATABASE=Webcam NX [PD1110] + +usb:v041Ep401D* + ID_MODEL_FROM_DATABASE=Webcam NX Ultra + +usb:v041Ep401E* + ID_MODEL_FROM_DATABASE=Webcam NX Pro + +usb:v041Ep401F* + ID_MODEL_FROM_DATABASE=Webcam Notebook [PD1171] + +usb:v041Ep4020* + ID_MODEL_FROM_DATABASE=Webcam NX + +usb:v041Ep4021* + ID_MODEL_FROM_DATABASE=Webcam NX Ultra + +usb:v041Ep4022* + ID_MODEL_FROM_DATABASE=Webcam NX Pro + +usb:v041Ep4028* + ID_MODEL_FROM_DATABASE=Vista Plus cam [VF0090] + +usb:v041Ep4029* + ID_MODEL_FROM_DATABASE=Webcam Live! + +usb:v041Ep402F* + ID_MODEL_FROM_DATABASE=DC-CAM 3000Z + +usb:v041Ep4034* + ID_MODEL_FROM_DATABASE=Webcam Instant + +usb:v041Ep4035* + ID_MODEL_FROM_DATABASE=Webcam Instant + +usb:v041Ep4036* + ID_MODEL_FROM_DATABASE=Webcam Live!/Live! Pro + +usb:v041Ep4037* + ID_MODEL_FROM_DATABASE=Webcam Live! + +usb:v041Ep4038* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam [PC370R] + +usb:v041Ep4039* + ID_MODEL_FROM_DATABASE=Webcam Live! Effects + +usb:v041Ep403A* + ID_MODEL_FROM_DATABASE=Webcam NX Pro 2 + +usb:v041Ep403B* + ID_MODEL_FROM_DATABASE=Creative Webcam Vista [VF0010] + +usb:v041Ep403C* + ID_MODEL_FROM_DATABASE=Webcam Live! Ultra + +usb:v041Ep403D* + ID_MODEL_FROM_DATABASE=Webcam Notebook Ultra + +usb:v041Ep403E* + ID_MODEL_FROM_DATABASE=Webcam Vista Plus + +usb:v041Ep4041* + ID_MODEL_FROM_DATABASE=Webcam Live! Motion + +usb:v041Ep4043* + ID_MODEL_FROM_DATABASE=Vibra Plus Webcam + +usb:v041Ep4045* + ID_MODEL_FROM_DATABASE=Live! Cam Voice + +usb:v041Ep4049* + ID_MODEL_FROM_DATABASE=Live! Cam Voice + +usb:v041Ep4051* + ID_MODEL_FROM_DATABASE=Live! Cam Notebook Pro [VF0250] + +usb:v041Ep4052* + ID_MODEL_FROM_DATABASE=Live! Cam Vista IM + +usb:v041Ep4053* + ID_MODEL_FROM_DATABASE=Live! Cam Video IM + +usb:v041Ep4054* + ID_MODEL_FROM_DATABASE=Live! Cam Video IM + +usb:v041Ep4055* + ID_MODEL_FROM_DATABASE=Live! Cam Video IM Pro + +usb:v041Ep4056* + ID_MODEL_FROM_DATABASE=Live! Cam Video IM Pro + +usb:v041Ep4057* + ID_MODEL_FROM_DATABASE=Live! Cam Optia + +usb:v041Ep4058* + ID_MODEL_FROM_DATABASE=Live! Cam Optia AF + +usb:v041Ep405F* + ID_MODEL_FROM_DATABASE=WebCam Vista (VF0330) + +usb:v041Ep4061* + ID_MODEL_FROM_DATABASE=Live! Cam Notebook Pro [VF0400] + +usb:v041Ep4063* + ID_MODEL_FROM_DATABASE=Live! Cam Video IM Pro + +usb:v041Ep4068* + ID_MODEL_FROM_DATABASE=Live! Cam Notebook [VF0470] + +usb:v041Ep406C* + ID_MODEL_FROM_DATABASE=Live! Cam Sync [VF0520] + +usb:v041Ep4083* + ID_MODEL_FROM_DATABASE=Live! Cam Socialize [VF0640] + +usb:v041Ep4087* + ID_MODEL_FROM_DATABASE=Live! Cam Socialize HD 1080 [VF0680] + +usb:v041Ep4088* + ID_MODEL_FROM_DATABASE=Live! Cam Chat HD [VF0700] + +usb:v041Ep4095* + ID_MODEL_FROM_DATABASE=Live! Cam Sync HD [VF0770] + +usb:v041Ep4097* + ID_MODEL_FROM_DATABASE=Live! Cam Chat HD [VF0700] + +usb:v041Ep4100* + ID_MODEL_FROM_DATABASE=Nomad Jukebox 2 + +usb:v041Ep4101* + ID_MODEL_FROM_DATABASE=Nomad Jukebox 3 + +usb:v041Ep4102* + ID_MODEL_FROM_DATABASE=NOMAD MuVo^2 + +usb:v041Ep4106* + ID_MODEL_FROM_DATABASE=Nomad MuVo + +usb:v041Ep4107* + ID_MODEL_FROM_DATABASE=NOMAD MuVo + +usb:v041Ep4108* + ID_MODEL_FROM_DATABASE=Nomad Jukebox Zen + +usb:v041Ep4109* + ID_MODEL_FROM_DATABASE=Nomad Jukebox Zen NX + +usb:v041Ep410B* + ID_MODEL_FROM_DATABASE=Nomad Jukebox Zen USB 2.0 + +usb:v041Ep410C* + ID_MODEL_FROM_DATABASE=Nomad MuVo NX + +usb:v041Ep410F* + ID_MODEL_FROM_DATABASE=NOMAD MuVo^2 (Flash) + +usb:v041Ep4110* + ID_MODEL_FROM_DATABASE=Nomad Jukebox Zen Xtra + +usb:v041Ep4111* + ID_MODEL_FROM_DATABASE=Dell Digital Jukebox + +usb:v041Ep4116* + ID_MODEL_FROM_DATABASE=MuVo^2 + +usb:v041Ep4117* + ID_MODEL_FROM_DATABASE=Nomad MuVo TX + +usb:v041Ep411B* + ID_MODEL_FROM_DATABASE=Zen Touch + +usb:v041Ep411C* + ID_MODEL_FROM_DATABASE=Nomad MuVo USB 2.0 + +usb:v041Ep411D* + ID_MODEL_FROM_DATABASE=Zen + +usb:v041Ep411E* + ID_MODEL_FROM_DATABASE=Zen Micro + +usb:v041Ep4120* + ID_MODEL_FROM_DATABASE=Nomad MuVo TX FM + +usb:v041Ep4123* + ID_MODEL_FROM_DATABASE=Zen Portable Media Center + +usb:v041Ep4124* + ID_MODEL_FROM_DATABASE=MuVo^2 FM (uHDD) + +usb:v041Ep4126* + ID_MODEL_FROM_DATABASE=Dell DJ (2nd gen) + +usb:v041Ep4127* + ID_MODEL_FROM_DATABASE=Dell DJ + +usb:v041Ep4128* + ID_MODEL_FROM_DATABASE=NOMAD Jukebox Zen Xtra (mtp) + +usb:v041Ep412B* + ID_MODEL_FROM_DATABASE=MuVo N200 with FM radio + +usb:v041Ep412F* + ID_MODEL_FROM_DATABASE=Dell Digital Jukebox 2.Gen + +usb:v041Ep4130* + ID_MODEL_FROM_DATABASE=Zen Micro (mtp) + +usb:v041Ep4131* + ID_MODEL_FROM_DATABASE=DAP-HD0014 [Zen Touch] (MTP) + +usb:v041Ep4133* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v041Ep4134* + ID_MODEL_FROM_DATABASE=Zen Neeon + +usb:v041Ep4136* + ID_MODEL_FROM_DATABASE=Zen Sleek + +usb:v041Ep4137* + ID_MODEL_FROM_DATABASE=Zen Sleek (mtp) + +usb:v041Ep4139* + ID_MODEL_FROM_DATABASE=Zen Nano Plus + +usb:v041Ep413C* + ID_MODEL_FROM_DATABASE=Zen MicroPhoto + +usb:v041Ep4150* + ID_MODEL_FROM_DATABASE=Zen V (MTP) + +usb:v041Ep4151* + ID_MODEL_FROM_DATABASE=Zen Vision:M (mtp) + +usb:v041Ep4152* + ID_MODEL_FROM_DATABASE=Zen V Plus + +usb:v041Ep4153* + ID_MODEL_FROM_DATABASE=Zen Vision W + +usb:v041Ep4154* + ID_MODEL_FROM_DATABASE=Zen Stone + +usb:v041Ep4155* + ID_MODEL_FROM_DATABASE=Zen Stone plus + +usb:v041Ep4157* + ID_MODEL_FROM_DATABASE=Zen (MTP) + +usb:v041Ep500F* + ID_MODEL_FROM_DATABASE=Broadband Blaster 8012U-V + +usb:v041Ep5015* + ID_MODEL_FROM_DATABASE=TECOM Bluetooth Device + +usb:v041EpFFFF* + ID_MODEL_FROM_DATABASE=Webcam Live! Ultra + +usb:v041F* + ID_VENDOR_FROM_DATABASE=LCS Telegraphics + +usb:v0420* + ID_VENDOR_FROM_DATABASE=Chips and Technologies + +usb:v0420p1307* + ID_MODEL_FROM_DATABASE=Celly SIM Card Reader + +usb:v0421* + ID_VENDOR_FROM_DATABASE=Nokia Mobile Phones + +usb:v0421p0001* + ID_MODEL_FROM_DATABASE=E61i (PC Suite mode) + +usb:v0421p0018* + ID_MODEL_FROM_DATABASE=6288 GSM Smartphone + +usb:v0421p0019* + ID_MODEL_FROM_DATABASE=6288 GSM Smartphone (imaging mode) + +usb:v0421p001A* + ID_MODEL_FROM_DATABASE=6288 GSM Smartphone (file transfer mode) + +usb:v0421p0024* + ID_MODEL_FROM_DATABASE=5610 XpressMusic (Storage mode) + +usb:v0421p0025* + ID_MODEL_FROM_DATABASE=5610 XpressMusic (PC Suite mode) + +usb:v0421p0028* + ID_MODEL_FROM_DATABASE=5610 XpressMusic (Imaging mode) + +usb:v0421p002D* + ID_MODEL_FROM_DATABASE=6120 Phone (Mass storage mode) + +usb:v0421p002E* + ID_MODEL_FROM_DATABASE=6120 Phone (Media-Player mode) + +usb:v0421p002F* + ID_MODEL_FROM_DATABASE=6120 Phone (PC-Suite mode) + +usb:v0421p0042* + ID_MODEL_FROM_DATABASE=E51 (PC Suite mode) + +usb:v0421p0064* + ID_MODEL_FROM_DATABASE=3109c GSM Phone + +usb:v0421p006B* + ID_MODEL_FROM_DATABASE=5310 Xpress Music (PC Suite mode) + +usb:v0421p006C* + ID_MODEL_FROM_DATABASE=5310 Xpress music (Storage mode) + +usb:v0421p006D* + ID_MODEL_FROM_DATABASE=N95 (Storage mode) + +usb:v0421p006E* + ID_MODEL_FROM_DATABASE=N95 (Multimedia mode) + +usb:v0421p006F* + ID_MODEL_FROM_DATABASE=N95 (Printing mode) + +usb:v0421p0070* + ID_MODEL_FROM_DATABASE=N95 (PC Suite mode) + +usb:v0421p0096* + ID_MODEL_FROM_DATABASE=N810 Internet Tablet + +usb:v0421p00AA* + ID_MODEL_FROM_DATABASE=E71 (Mass storage mode) + +usb:v0421p00AB* + ID_MODEL_FROM_DATABASE=E71 (PC Suite mode) + +usb:v0421p00E4* + ID_MODEL_FROM_DATABASE=E71 (Media transfer mode) + +usb:v0421p0103* + ID_MODEL_FROM_DATABASE=ADL Flashing Engine AVALON Parent + +usb:v0421p0104* + ID_MODEL_FROM_DATABASE=ADL Re-Flashing Engine Parent + +usb:v0421p0105* + ID_MODEL_FROM_DATABASE=Nokia Firmware Upgrade Mode + +usb:v0421p0106* + ID_MODEL_FROM_DATABASE=ROM Parent + +usb:v0421p0154* + ID_MODEL_FROM_DATABASE=5800 XpressMusic (PC Suite mode) + +usb:v0421p0155* + ID_MODEL_FROM_DATABASE=5800 XpressMusic (Multimedia mode) + +usb:v0421p0156* + ID_MODEL_FROM_DATABASE=5800 XpressMusic (Storage mode) + +usb:v0421p0157* + ID_MODEL_FROM_DATABASE=5800 XpressMusic (Imaging mode) + +usb:v0421p0199* + ID_MODEL_FROM_DATABASE=6700 Classic (msc) + +usb:v0421p019A* + ID_MODEL_FROM_DATABASE=6700 Classic (PC Suite) + +usb:v0421p019B* + ID_MODEL_FROM_DATABASE=6700 Classic (mtp) + +usb:v0421p01B0* + ID_MODEL_FROM_DATABASE=6303 classic Phone (PC Suite mode) + +usb:v0421p01B1* + ID_MODEL_FROM_DATABASE=6303 classic Phone (Mass storage mode) + +usb:v0421p01B2* + ID_MODEL_FROM_DATABASE=6303 classic Phone (Printing and media mode) + +usb:v0421p01C7* + ID_MODEL_FROM_DATABASE=N900 (Storage Mode) + +usb:v0421p01C8* + ID_MODEL_FROM_DATABASE=N900 (PC-Suite Mode) + +usb:v0421p0228* + ID_MODEL_FROM_DATABASE=5530 XpressMusic + +usb:v0421p023A* + ID_MODEL_FROM_DATABASE=6730 Classic + +usb:v0421p026A* + ID_MODEL_FROM_DATABASE=N97 (mass storage) + +usb:v0421p026B* + ID_MODEL_FROM_DATABASE=N97 (Multimedia) + +usb:v0421p026C* + ID_MODEL_FROM_DATABASE=N97 (PC Suite) + +usb:v0421p026D* + ID_MODEL_FROM_DATABASE=N97 (Pictures) + +usb:v0421p0295* + ID_MODEL_FROM_DATABASE=660i/6600i Slide Phone (Mass Storage) + +usb:v0421p0297* + ID_MODEL_FROM_DATABASE=660i/6600i Slide Phone (Still Image) + +usb:v0421p02E1* + ID_MODEL_FROM_DATABASE=5230 (Storage mode) + +usb:v0421p02E2* + ID_MODEL_FROM_DATABASE=5230 (Multimedia mode) + +usb:v0421p02E3* + ID_MODEL_FROM_DATABASE=5230 (PC-Suite mode) + +usb:v0421p02E4* + ID_MODEL_FROM_DATABASE=5230 (Imaging mode) + +usb:v0421p0360* + ID_MODEL_FROM_DATABASE=C1-01 Ovi Suite Mode + +usb:v0421p0396* + ID_MODEL_FROM_DATABASE=C7-00 (Modem mode) + +usb:v0421p03A4* + ID_MODEL_FROM_DATABASE=C5 (Storage mode) + +usb:v0421p03C0* + ID_MODEL_FROM_DATABASE=C7-00 (Mass storage mode) + +usb:v0421p03C1* + ID_MODEL_FROM_DATABASE=C7-00 (Media transfer mode) + +usb:v0421p03CD* + ID_MODEL_FROM_DATABASE=C7-00 (Nokia Suite mode) + +usb:v0421p03D1* + ID_MODEL_FROM_DATABASE=N950 + +usb:v0421p0400* + ID_MODEL_FROM_DATABASE=7600 Phone Parent + +usb:v0421p0401* + ID_MODEL_FROM_DATABASE=6650 GSM Phone + +usb:v0421p0402* + ID_MODEL_FROM_DATABASE=6255 Phone Parent + +usb:v0421p0404* + ID_MODEL_FROM_DATABASE=5510 + +usb:v0421p0405* + ID_MODEL_FROM_DATABASE=9500 GSM Communicator + +usb:v0421p0407* + ID_MODEL_FROM_DATABASE=Music Player HDR-1(tm) + +usb:v0421p040B* + ID_MODEL_FROM_DATABASE=N-Gage GSM Phone + +usb:v0421p040D* + ID_MODEL_FROM_DATABASE=6620 Phone Parent + +usb:v0421p040E* + ID_MODEL_FROM_DATABASE=6651 Phone Parent + +usb:v0421p040F* + ID_MODEL_FROM_DATABASE=6230 GSM Phone + +usb:v0421p0410* + ID_MODEL_FROM_DATABASE=6630 Imaging Smartphone + +usb:v0421p0411* + ID_MODEL_FROM_DATABASE=7610 Phone Parent + +usb:v0421p0413* + ID_MODEL_FROM_DATABASE=6260 Phone Parent + +usb:v0421p0414* + ID_MODEL_FROM_DATABASE=7370 + +usb:v0421p0415* + ID_MODEL_FROM_DATABASE=9300 GSM Smartphone + +usb:v0421p0416* + ID_MODEL_FROM_DATABASE=6170 Phone Parent + +usb:v0421p0417* + ID_MODEL_FROM_DATABASE=7270 Phone Parent + +usb:v0421p0418* + ID_MODEL_FROM_DATABASE=E70 (PC Suite mode) + +usb:v0421p0419* + ID_MODEL_FROM_DATABASE=E60 (PC Suite mode) + +usb:v0421p041A* + ID_MODEL_FROM_DATABASE=9500 GSM Communicator (RNDIS) + +usb:v0421p041B* + ID_MODEL_FROM_DATABASE=9300 GSM Smartphone (RNDIS) + +usb:v0421p041C* + ID_MODEL_FROM_DATABASE=7710 Phone Parent + +usb:v0421p041D* + ID_MODEL_FROM_DATABASE=6670 Phone Parent + +usb:v0421p041E* + ID_MODEL_FROM_DATABASE=6680 + +usb:v0421p041F* + ID_MODEL_FROM_DATABASE=6235 Phone Parent + +usb:v0421p0421* + ID_MODEL_FROM_DATABASE=3230 Phone Parent + +usb:v0421p0422* + ID_MODEL_FROM_DATABASE=6681 Phone Parent + +usb:v0421p0423* + ID_MODEL_FROM_DATABASE=6682 Phone Parent + +usb:v0421p0428* + ID_MODEL_FROM_DATABASE=6230i Modem + +usb:v0421p0429* + ID_MODEL_FROM_DATABASE=6230i MultiMedia Card + +usb:v0421p0431* + ID_MODEL_FROM_DATABASE=770 Internet Tablet + +usb:v0421p0432* + ID_MODEL_FROM_DATABASE=N90 Phone Parent + +usb:v0421p0435* + ID_MODEL_FROM_DATABASE=E70 (IP Passthrough/RNDIS mode) + +usb:v0421p0436* + ID_MODEL_FROM_DATABASE=E60 (IP Passthrough/RNDIS mode) + +usb:v0421p0437* + ID_MODEL_FROM_DATABASE=6265 Phone Parent + +usb:v0421p043A* + ID_MODEL_FROM_DATABASE=N70 USB Phone Parent + +usb:v0421p043B* + ID_MODEL_FROM_DATABASE=3155 Phone Parent + +usb:v0421p043C* + ID_MODEL_FROM_DATABASE=6155 Phone Parent + +usb:v0421p043D* + ID_MODEL_FROM_DATABASE=6270 Phone Parent + +usb:v0421p0443* + ID_MODEL_FROM_DATABASE=N70 Phone Parent + +usb:v0421p0444* + ID_MODEL_FROM_DATABASE=N91 + +usb:v0421p044C* + ID_MODEL_FROM_DATABASE=NM850iG Phone Parent + +usb:v0421p044D* + ID_MODEL_FROM_DATABASE=E61 (PC Suite mode) + +usb:v0421p044E* + ID_MODEL_FROM_DATABASE=E61 (Data Exchange mode) + +usb:v0421p044F* + ID_MODEL_FROM_DATABASE=E61 (IP Passthrough/RNDIS mode) + +usb:v0421p0453* + ID_MODEL_FROM_DATABASE=9300 Phone Parent + +usb:v0421p0456* + ID_MODEL_FROM_DATABASE=6111 Phone Parent + +usb:v0421p0457* + ID_MODEL_FROM_DATABASE=6111 Phone (Printing mode) + +usb:v0421p045A* + ID_MODEL_FROM_DATABASE=6280 Phone Parent + +usb:v0421p045D* + ID_MODEL_FROM_DATABASE=6282 Phone Parent + +usb:v0421p046E* + ID_MODEL_FROM_DATABASE=6110 Navigator + +usb:v0421p0471* + ID_MODEL_FROM_DATABASE=6110 Navigator + +usb:v0421p0485* + ID_MODEL_FROM_DATABASE=MTP Device + +usb:v0421p04B9* + ID_MODEL_FROM_DATABASE=5300 + +usb:v0421p04BC* + ID_MODEL_FROM_DATABASE=5200 (Nokia mode) + +usb:v0421p04BD* + ID_MODEL_FROM_DATABASE=5200 (Storage mode) + +usb:v0421p04BE* + ID_MODEL_FROM_DATABASE=5200 (MTP mode) + +usb:v0421p04C3* + ID_MODEL_FROM_DATABASE=N800 Internet Tablet + +usb:v0421p04CE* + ID_MODEL_FROM_DATABASE=E90 Communicator (PC Suite mode) + +usb:v0421p04CF* + ID_MODEL_FROM_DATABASE=E90 Communicator (Storage mode) + +usb:v0421p04F0* + ID_MODEL_FROM_DATABASE=Nokia N95 (PC Suite mode) + +usb:v0421p04F9* + ID_MODEL_FROM_DATABASE=6300 (PC Suite mode) + +usb:v0421p0508* + ID_MODEL_FROM_DATABASE=E65 (PC Suite mode) + +usb:v0421p0509* + ID_MODEL_FROM_DATABASE=E65 (Storage mode) + +usb:v0421p0518* + ID_MODEL_FROM_DATABASE=N9 Phone + +usb:v0421p054D* + ID_MODEL_FROM_DATABASE=C2-01 + +usb:v0421p0600* + ID_MODEL_FROM_DATABASE=Digital Pen SU-1B + +usb:v0421p0610* + ID_MODEL_FROM_DATABASE=CS-15 (Internet Stick 3G modem) + +usb:v0421p0661* + ID_MODEL_FROM_DATABASE=Lumia 620/920 + +usb:v0421p0662* + ID_MODEL_FROM_DATABASE=301 Dual SIM (Mass Storage) + +usb:v0421p0663* + ID_MODEL_FROM_DATABASE=301 Dual SIM + +usb:v0421p069A* + ID_MODEL_FROM_DATABASE=130 [RM-1035] (Charging only) + +usb:v0421p06FC* + ID_MODEL_FROM_DATABASE=Lumia 640 Phone + +usb:v0421p0720* + ID_MODEL_FROM_DATABASE=X (RM-980) + +usb:v0421p0800* + ID_MODEL_FROM_DATABASE=Connectivity Cable DKU-5 + +usb:v0421p0801* + ID_MODEL_FROM_DATABASE=Data Cable DKU-6 + +usb:v0421p0802* + ID_MODEL_FROM_DATABASE=CA-42 Phone Parent + +usb:v0422* + ID_VENDOR_FROM_DATABASE=ADI Systems, Inc. + +usb:v0423* + ID_VENDOR_FROM_DATABASE=Computer Access Technology Corp. + +usb:v0423p000A* + ID_MODEL_FROM_DATABASE=NetMate Ethernet + +usb:v0423p000C* + ID_MODEL_FROM_DATABASE=NetMate2 Ethernet + +usb:v0423p000D* + ID_MODEL_FROM_DATABASE=USB Chief Analyzer + +usb:v0423p0100* + ID_MODEL_FROM_DATABASE=Generic Universal Protocol Analyzer + +usb:v0423p0101* + ID_MODEL_FROM_DATABASE=UPA USBTracer + +usb:v0423p0200* + ID_MODEL_FROM_DATABASE=Generic 10K Universal Protocol Analyzer + +usb:v0423p020A* + ID_MODEL_FROM_DATABASE=PETracer ML + +usb:v0423p0300* + ID_MODEL_FROM_DATABASE=Generic Universal Protocol Analyzer + +usb:v0423p0301* + ID_MODEL_FROM_DATABASE=2500H Tracer Trainer + +usb:v0423p030A* + ID_MODEL_FROM_DATABASE=PETracer x1 + +usb:v0423p1237* + ID_MODEL_FROM_DATABASE=Andromeda Hub + +usb:v0424* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp. + +usb:v0424p0001* + ID_MODEL_FROM_DATABASE=Integrated Hub + +usb:v0424p0140* + ID_MODEL_FROM_DATABASE=LPC47M14x hub + +usb:v0424p0ACD* + ID_MODEL_FROM_DATABASE=Sitecom Internal Multi Memory reader/writer MD-005 + +usb:v0424p0FDC* + ID_MODEL_FROM_DATABASE=Floppy + +usb:v0424p10CD* + ID_MODEL_FROM_DATABASE=Sitecom Internal Multi Memory reader/writer MD-005 + +usb:v0424p2020* + ID_MODEL_FROM_DATABASE=USB Hub + +usb:v0424p20CD* + ID_MODEL_FROM_DATABASE=Sitecom Internal Multi Memory reader/writer MD-005 + +usb:v0424p20FC* + ID_MODEL_FROM_DATABASE=6-in-1 Card Reader + +usb:v0424p2134* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0424p2228* + ID_MODEL_FROM_DATABASE=9-in-2 Card Reader + +usb:v0424p223A* + ID_MODEL_FROM_DATABASE=8-in-1 Card Reader + +usb:v0424p2503* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2504* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2507* + ID_MODEL_FROM_DATABASE=hub + +usb:v0424p2512* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2513* + ID_MODEL_FROM_DATABASE=2.0 Hub + +usb:v0424p2514* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2517* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0424p2524* + ID_MODEL_FROM_DATABASE=USB MultiSwitch Hub + +usb:v0424p2602* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2640* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v0424p2660* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0424p4060* + ID_MODEL_FROM_DATABASE=Ultra Fast Media Reader + +usb:v0424p4064* + ID_MODEL_FROM_DATABASE=Ultra Fast Media Reader + +usb:v0424p5434* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0424p5534* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0424p7500* + ID_MODEL_FROM_DATABASE=LAN7500 Ethernet 10/100/1000 Adapter + +usb:v0424p9512* + ID_MODEL_FROM_DATABASE=SMC9512/9514 USB Hub + +usb:v0424p9514* + ID_MODEL_FROM_DATABASE=SMC9514 Hub + +usb:v0424p9904* + ID_MODEL_FROM_DATABASE=LAN9512/LAN9514 Ethernet 10/100 Adapter (SAL10) + +usb:v0424pA700* + ID_MODEL_FROM_DATABASE=2 Port Hub + +usb:v0424pEC00* + ID_MODEL_FROM_DATABASE=SMSC9512/9514 Fast Ethernet Adapter + +usb:v0425* + ID_VENDOR_FROM_DATABASE=Motorola Semiconductors HK, Ltd + +usb:v0425p0101* + ID_MODEL_FROM_DATABASE=G-Tech Wireless Mouse & Keyboard + +usb:v0425pF102* + ID_MODEL_FROM_DATABASE=G-Tech U+P Wireless Mouse + +usb:v0426* + ID_VENDOR_FROM_DATABASE=Integrated Device Technology, Inc. + +usb:v0426p0426* + ID_MODEL_FROM_DATABASE=WDM Driver + +usb:v0427* + ID_VENDOR_FROM_DATABASE=Motorola Electronics Taiwan, Ltd + +usb:v0428* + ID_VENDOR_FROM_DATABASE=Advanced Gravis Computer Tech, Ltd + +usb:v0428p4001* + ID_MODEL_FROM_DATABASE=GamePad Pro + +usb:v0429* + ID_VENDOR_FROM_DATABASE=Cirrus Logic + +usb:v042A* + ID_VENDOR_FROM_DATABASE=Ericsson Austrian, AG + +usb:v042B* + ID_VENDOR_FROM_DATABASE=Intel Corp. + +usb:v042Bp9316* + ID_MODEL_FROM_DATABASE=8x931Hx Customer Hub + +usb:v042C* + ID_VENDOR_FROM_DATABASE=Innovative Semiconductors, Inc. + +usb:v042D* + ID_VENDOR_FROM_DATABASE=Micronics + +usb:v042E* + ID_VENDOR_FROM_DATABASE=Acer, Inc. + +usb:v042Ep0380* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v042F* + ID_VENDOR_FROM_DATABASE=Molex, Inc. + +usb:v0430* + ID_VENDOR_FROM_DATABASE=Sun Microsystems, Inc. + +usb:v0430p0002* + ID_MODEL_FROM_DATABASE=109 Keyboard + +usb:v0430p0005* + ID_MODEL_FROM_DATABASE=Type 6 Keyboard + +usb:v0430p000A* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard + +usb:v0430p000B* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard + +usb:v0430p0082* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard + +usb:v0430p0083* + ID_MODEL_FROM_DATABASE=109 Japanese Keyboard + +usb:v0430p00A2* + ID_MODEL_FROM_DATABASE=Type 7 Keyboard + +usb:v0430p0100* + ID_MODEL_FROM_DATABASE=3-button Mouse + +usb:v0430p100E* + ID_MODEL_FROM_DATABASE=24.1" LCD Monitor v4 / FID-638 Mouse + +usb:v0430p36BA* + ID_MODEL_FROM_DATABASE=Bus Powered Hub + +usb:v0430pA101* + ID_MODEL_FROM_DATABASE=remote key/mouse for P3 chip + +usb:v0430pA102* + ID_MODEL_FROM_DATABASE=remote key/mouse/storage for P3 chip + +usb:v0430pA103* + ID_MODEL_FROM_DATABASE=remote storage for P3 chip + +usb:v0430pA4A2* + ID_MODEL_FROM_DATABASE=Ethernet (RNDIS and CDC ethernet) + +usb:v0430pCDAB* + ID_MODEL_FROM_DATABASE=Raritan KVM dongle + +usb:v0431* + ID_VENDOR_FROM_DATABASE=Itac Systems, Inc. + +usb:v0431p0100* + ID_MODEL_FROM_DATABASE=Mouse-Trak 3-button Track Ball + +usb:v0432* + ID_VENDOR_FROM_DATABASE=Unisys Corp. + +usb:v0432p0031* + ID_MODEL_FROM_DATABASE=Document Processor + +usb:v0433* + ID_VENDOR_FROM_DATABASE=Alps Electric, Inc. + +usb:v0433p1101* + ID_MODEL_FROM_DATABASE=IBM Game Controller + +usb:v0433pABAB* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0434* + ID_VENDOR_FROM_DATABASE=Samsung Info. Systems America, Inc. + +usb:v0435* + ID_VENDOR_FROM_DATABASE=Hyundai Electronics America + +usb:v0436* + ID_VENDOR_FROM_DATABASE=Taugagreining HF + +usb:v0436p0005* + ID_MODEL_FROM_DATABASE=CameraMate (DPCM_USB) + +usb:v0437* + ID_VENDOR_FROM_DATABASE=Framatome Connectors USA + +usb:v0438* + ID_VENDOR_FROM_DATABASE=Advanced Micro Devices, Inc. + +usb:v0439* + ID_VENDOR_FROM_DATABASE=Voice Technologies Group + +usb:v043D* + ID_VENDOR_FROM_DATABASE=Lexmark International, Inc. + +usb:v043Dp0001* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0002* + ID_MODEL_FROM_DATABASE=Optra E310 Printer + +usb:v043Dp0003* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0004* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0005* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0006* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0007* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0008* + ID_MODEL_FROM_DATABASE=Inkjet Color Printer + +usb:v043Dp0009* + ID_MODEL_FROM_DATABASE=Optra S2450 Printer + +usb:v043Dp000A* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp000B* + ID_MODEL_FROM_DATABASE=Inkjet Color Printer + +usb:v043Dp000C* + ID_MODEL_FROM_DATABASE=Optra E312 Printer + +usb:v043Dp000D* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp000E* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp000F* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0010* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0011* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0012* + ID_MODEL_FROM_DATABASE=Inkjet Color Printer + +usb:v043Dp0013* + ID_MODEL_FROM_DATABASE=Inkjet Color Printer + +usb:v043Dp0014* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0015* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0016* + ID_MODEL_FROM_DATABASE=Z12 Color Jetprinter + +usb:v043Dp0017* + ID_MODEL_FROM_DATABASE=Z32 printer + +usb:v043Dp0018* + ID_MODEL_FROM_DATABASE=Z52 Printer + +usb:v043Dp0019* + ID_MODEL_FROM_DATABASE=Forms Printer + +usb:v043Dp001A* + ID_MODEL_FROM_DATABASE=Z65 Printer + +usb:v043Dp001B* + ID_MODEL_FROM_DATABASE=InkJet Photo Printer + +usb:v043Dp001C* + ID_MODEL_FROM_DATABASE=Kodak Personal Picture Maker 200 Printer + +usb:v043Dp001D* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp001E* + ID_MODEL_FROM_DATABASE=InkJet Photo Printer + +usb:v043Dp001F* + ID_MODEL_FROM_DATABASE=Kodak Personal Picture Maker 200 Card Reader + +usb:v043Dp0020* + ID_MODEL_FROM_DATABASE=Z51 Printer + +usb:v043Dp0021* + ID_MODEL_FROM_DATABASE=Z33 Printer + +usb:v043Dp0022* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0023* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0024* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0025* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0026* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0027* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0028* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0029* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp002A* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp002B* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp002C* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp002D* + ID_MODEL_FROM_DATABASE=X70/X73 Scan/Print/Copy + +usb:v043Dp002E* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp002F* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0030* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0031* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0032* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0033* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0034* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0035* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0036* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0037* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0038* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0039* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp003A* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp003B* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp003C* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp003D* + ID_MODEL_FROM_DATABASE=X83 Scan/Print/Copy + +usb:v043Dp003E* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp003F* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0040* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0041* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0042* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0043* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0044* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0045* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0046* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0047* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0048* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp0049* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp004A* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp004B* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp004C* + ID_MODEL_FROM_DATABASE=Scan Print Copy + +usb:v043Dp004D* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp004E* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp004F* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0050* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0051* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0052* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp0053* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0054* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0057* + ID_MODEL_FROM_DATABASE=Z35 Printer + +usb:v043Dp0058* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v043Dp005A* + ID_MODEL_FROM_DATABASE=X63 + +usb:v043Dp005C* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0060* + ID_MODEL_FROM_DATABASE=X74/X75 Scanner + +usb:v043Dp0061* + ID_MODEL_FROM_DATABASE=X74 Hub + +usb:v043Dp0065* + ID_MODEL_FROM_DATABASE=X5130 + +usb:v043Dp0069* + ID_MODEL_FROM_DATABASE=X74/X75 Printer + +usb:v043Dp006D* + ID_MODEL_FROM_DATABASE=X125 + +usb:v043Dp006E* + ID_MODEL_FROM_DATABASE=C510 + +usb:v043Dp0072* + ID_MODEL_FROM_DATABASE=X6170 Printer + +usb:v043Dp0073* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0078* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp0079* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp007A* + ID_MODEL_FROM_DATABASE=Generic Hub + +usb:v043Dp007B* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp007C* + ID_MODEL_FROM_DATABASE=X1110/X1130/X1140/X1150/X1170/X1180/X1185 + +usb:v043Dp007D* + ID_MODEL_FROM_DATABASE=Photo 3150 + +usb:v043Dp008A* + ID_MODEL_FROM_DATABASE=4200 series + +usb:v043Dp008B* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp008C* + ID_MODEL_FROM_DATABASE=to CF/SM/SD/MS Card Reader + +usb:v043Dp008E* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp008F* + ID_MODEL_FROM_DATABASE=X422 + +usb:v043Dp0093* + ID_MODEL_FROM_DATABASE=X5250 + +usb:v043Dp0095* + ID_MODEL_FROM_DATABASE=E220 Printer + +usb:v043Dp0096* + ID_MODEL_FROM_DATABASE=2200 series + +usb:v043Dp0097* + ID_MODEL_FROM_DATABASE=P6250 + +usb:v043Dp0098* + ID_MODEL_FROM_DATABASE=7100 series + +usb:v043Dp009E* + ID_MODEL_FROM_DATABASE=P910 series Human Interface Device + +usb:v043Dp009F* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp00A9* + ID_MODEL_FROM_DATABASE=IBM Infoprint 1410 MFP + +usb:v043Dp00AB* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp00B2* + ID_MODEL_FROM_DATABASE=3300 series + +usb:v043Dp00B8* + ID_MODEL_FROM_DATABASE=7300 series + +usb:v043Dp00B9* + ID_MODEL_FROM_DATABASE=8300 series + +usb:v043Dp00BA* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp00BB* + ID_MODEL_FROM_DATABASE=2300 series + +usb:v043Dp00BD* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00BE* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00BF* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00C0* + ID_MODEL_FROM_DATABASE=6300 series + +usb:v043Dp00C1* + ID_MODEL_FROM_DATABASE=4300 series + +usb:v043Dp00C7* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00C8* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00C9* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00CB* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00CC* + ID_MODEL_FROM_DATABASE=E120(n) + +usb:v043Dp00D0* + ID_MODEL_FROM_DATABASE=9300 series + +usb:v043Dp00D3* + ID_MODEL_FROM_DATABASE=X340 Scanner + +usb:v043Dp00D4* + ID_MODEL_FROM_DATABASE=X342n Scanner + +usb:v043Dp00D5* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v043Dp00D6* + ID_MODEL_FROM_DATABASE=X340 Scanner + +usb:v043Dp00E8* + ID_MODEL_FROM_DATABASE=X642e + +usb:v043Dp00E9* + ID_MODEL_FROM_DATABASE=2400 series + +usb:v043Dp00F6* + ID_MODEL_FROM_DATABASE=3400 series + +usb:v043Dp00F7* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp00FF* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v043Dp010B* + ID_MODEL_FROM_DATABASE=2500 series + +usb:v043Dp010D* + ID_MODEL_FROM_DATABASE=3500-4500 series + +usb:v043Dp010F* + ID_MODEL_FROM_DATABASE=6500 series + +usb:v043Dp0142* + ID_MODEL_FROM_DATABASE=X3650 (Printer, Scanner, Copier) + +usb:v043Dp01FA* + ID_MODEL_FROM_DATABASE=S310 series + +usb:v043Dp4303* + ID_MODEL_FROM_DATABASE=Xerox WorkCentre Pro 412 + +usb:v043E* + ID_VENDOR_FROM_DATABASE=LG Electronics USA, Inc. + +usb:v043Ep3001* + ID_MODEL_FROM_DATABASE=AN-WF100 802.11abgn Wireless Adapter [Broadcom BCM4323] + +usb:v043Ep42BD* + ID_MODEL_FROM_DATABASE=Flatron 795FT Plus Monitor + +usb:v043Ep4A4D* + ID_MODEL_FROM_DATABASE=Flatron 915FT Plus Monitor + +usb:v043Ep7001* + ID_MODEL_FROM_DATABASE=MF-PD100 Soul Digital MP3 Player + +usb:v043Ep7013* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v043Ep70D7* + ID_MODEL_FROM_DATABASE=Mouse Scanner LSM-150 [LG Smart Scan Mouse] + +usb:v043Ep70F5* + ID_MODEL_FROM_DATABASE=External HDD + +usb:v043Ep8484* + ID_MODEL_FROM_DATABASE=LPC-U30 Webcam II + +usb:v043Ep8585* + ID_MODEL_FROM_DATABASE=LPC-UC35 Webcam + +usb:v043Ep8888* + ID_MODEL_FROM_DATABASE=Electronics VCS Camera II(LPC-U20) + +usb:v043Ep9800* + ID_MODEL_FROM_DATABASE=Remote Control Receiver_iMON + +usb:v043Ep9803* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v043Ep9804* + ID_MODEL_FROM_DATABASE=DMB Receiver Control + +usb:v043Ep9C01* + ID_MODEL_FROM_DATABASE=LGE Sync + +usb:v043F* + ID_VENDOR_FROM_DATABASE=RadiSys Corp. + +usb:v0440* + ID_VENDOR_FROM_DATABASE=Eizo Nanao Corp. + +usb:v0441* + ID_VENDOR_FROM_DATABASE=Winbond Systems Lab. + +usb:v0441p1456* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0442* + ID_VENDOR_FROM_DATABASE=Ericsson, Inc. + +usb:v0442pABBA* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0443* + ID_VENDOR_FROM_DATABASE=Gateway, Inc. + +usb:v0443p000E* + ID_MODEL_FROM_DATABASE=Multimedia Keyboard + +usb:v0443p002E* + ID_MODEL_FROM_DATABASE=Millennium Keyboard + +usb:v0445* + ID_VENDOR_FROM_DATABASE=Lucent Technologies, Inc. + +usb:v0446* + ID_VENDOR_FROM_DATABASE=NMB Technologies Corp. + +usb:v0446p6781* + ID_MODEL_FROM_DATABASE=Keyboard with PS/2 Mouse Port + +usb:v0446p6782* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0447* + ID_VENDOR_FROM_DATABASE=Momentum Microsystems + +usb:v0449* + ID_VENDOR_FROM_DATABASE=Duta Multi Robotik + +usb:v0449p0128* + ID_MODEL_FROM_DATABASE=Menengah + +usb:v0449p0210* + ID_MODEL_FROM_DATABASE=Dasar + +usb:v0449p0612* + ID_MODEL_FROM_DATABASE=Lanjutan + +usb:v044A* + ID_VENDOR_FROM_DATABASE=Shamrock Tech. Co., Ltd + +usb:v044B* + ID_VENDOR_FROM_DATABASE=WSI + +usb:v044C* + ID_VENDOR_FROM_DATABASE=CCL/ITRI + +usb:v044D* + ID_VENDOR_FROM_DATABASE=Siemens Nixdorf AG + +usb:v044E* + ID_VENDOR_FROM_DATABASE=Alps Electric Co., Ltd + +usb:v044Ep1104* + ID_MODEL_FROM_DATABASE=Japanese Keyboard + +usb:v044Ep2002* + ID_MODEL_FROM_DATABASE=MD-5500 Printer + +usb:v044Ep2014* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v044Ep3001* + ID_MODEL_FROM_DATABASE=UGTZ4 Bluetooth + +usb:v044Ep3002* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v044Ep3003* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v044Ep3004* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v044Ep3005* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth Device + +usb:v044Ep3006* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v044Ep3007* + ID_MODEL_FROM_DATABASE=Bluetooth Controller (ALPS/UGX) + +usb:v044Ep300C* + ID_MODEL_FROM_DATABASE=Bluetooth Controller (ALPS/UGPZ6) + +usb:v044Ep300D* + ID_MODEL_FROM_DATABASE=Bluetooth Controller (ALPS/UGPZ6) + +usb:v044Ep3010* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v044Ep3017* + ID_MODEL_FROM_DATABASE=BCM2046 Bluetooth Device + +usb:v044EpFFFF* + ID_MODEL_FROM_DATABASE=Compaq Bluetooth Multiport Module + +usb:v044F* + ID_VENDOR_FROM_DATABASE=ThrustMaster, Inc. + +usb:v044Fp0400* + ID_MODEL_FROM_DATABASE=HOTAS Cougar + +usb:v044Fp044F* + ID_MODEL_FROM_DATABASE=GP XID + +usb:v044FpA003* + ID_MODEL_FROM_DATABASE=Rage 3D Game Pad + +usb:v044FpA01B* + ID_MODEL_FROM_DATABASE=PK-GP301 Driving Wheel + +usb:v044FpA0A0* + ID_MODEL_FROM_DATABASE=Top Gun Joystick + +usb:v044FpA0A1* + ID_MODEL_FROM_DATABASE=Top Gun Joystick (rev2) + +usb:v044FpA0A3* + ID_MODEL_FROM_DATABASE=Fusion Digital GamePad + +usb:v044FpA201* + ID_MODEL_FROM_DATABASE=PK-GP201 PlayStick + +usb:v044FpB108* + ID_MODEL_FROM_DATABASE=T-Flight Hotas X Flight Stick + +usb:v044FpB10A* + ID_MODEL_FROM_DATABASE=T.16000M Joystick + +usb:v044FpB203* + ID_MODEL_FROM_DATABASE=360 Modena Pro Wheel + +usb:v044FpB300* + ID_MODEL_FROM_DATABASE=Firestorm Dual Power + +usb:v044FpB303* + ID_MODEL_FROM_DATABASE=FireStorm Dual Analog 2 + +usb:v044FpB304* + ID_MODEL_FROM_DATABASE=Firestorm Dual Power + +usb:v044FpB307* + ID_MODEL_FROM_DATABASE=vibrating Upad + +usb:v044FpB30B* + ID_MODEL_FROM_DATABASE=Wireless VibrationPad + +usb:v044FpB315* + ID_MODEL_FROM_DATABASE=Firestorm Dual Analog 3 + +usb:v044FpB323* + ID_MODEL_FROM_DATABASE=Dual Trigger 3-in-1 (PC Mode) + +usb:v044FpB324* + ID_MODEL_FROM_DATABASE=Dual Trigger 3-in-1 (PS3 Mode) + +usb:v044FpB603* + ID_MODEL_FROM_DATABASE=force feedback Wheel + +usb:v044FpB605* + ID_MODEL_FROM_DATABASE=force feedback Racing Wheel + +usb:v044FpB651* + ID_MODEL_FROM_DATABASE=Ferrari GT Rumble Force Wheel + +usb:v044FpB653* + ID_MODEL_FROM_DATABASE=RGT Force Feedback Clutch Racing Wheel + +usb:v044FpB654* + ID_MODEL_FROM_DATABASE=Ferrari GT Force Feedback Wheel + +usb:v044FpB700* + ID_MODEL_FROM_DATABASE=Tacticalboard + +usb:v0450* + ID_VENDOR_FROM_DATABASE=DFI, Inc. + +usb:v0451* + ID_VENDOR_FROM_DATABASE=Texas Instruments, Inc. + +usb:v0451p1234* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0451p1428* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0451p1446* + ID_MODEL_FROM_DATABASE=TUSB2040/2070 Hub + +usb:v0451p16A6* + ID_MODEL_FROM_DATABASE=BM-USBD1 BlueRobin RF heart rate sensor receiver + +usb:v0451p2036* + ID_MODEL_FROM_DATABASE=TUSB2036 Hub + +usb:v0451p2046* + ID_MODEL_FROM_DATABASE=TUSB2046 Hub + +usb:v0451p2077* + ID_MODEL_FROM_DATABASE=TUSB2077 Hub + +usb:v0451p2F90* + ID_MODEL_FROM_DATABASE=SM-USB-DIG + +usb:v0451p3410* + ID_MODEL_FROM_DATABASE=TUSB3410 Microcontroller + +usb:v0451p3F00* + ID_MODEL_FROM_DATABASE=OMAP1610 + +usb:v0451p3F02* + ID_MODEL_FROM_DATABASE=SMC WSKP100 Wi-Fi Phone + +usb:v0451p5409* + ID_MODEL_FROM_DATABASE=Frontier Labs NEX IA+ Digital Audio Player + +usb:v0451p6000* + ID_MODEL_FROM_DATABASE=AU5 ADSL Modem (pre-reenum) + +usb:v0451p6001* + ID_MODEL_FROM_DATABASE=AU5 ADSL Modem + +usb:v0451p6060* + ID_MODEL_FROM_DATABASE=RNDIS/BeWAN ADSL2+ + +usb:v0451p6070* + ID_MODEL_FROM_DATABASE=RNDIS/BeWAN ADSL2+ + +usb:v0451p625F* + ID_MODEL_FROM_DATABASE=TUSB6250 ATA Bridge + +usb:v0451p8041* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0451p8042* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0451p8043* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0451p8140* + ID_MODEL_FROM_DATABASE=TUSB8041 4-Port Hub + +usb:v0451p8142* + ID_MODEL_FROM_DATABASE=TUSB8041 4-Port Hub + +usb:v0451p926B* + ID_MODEL_FROM_DATABASE=TUSB9260 Boot Loader + +usb:v0451pDBC0* + ID_MODEL_FROM_DATABASE=Device Bay Controller + +usb:v0451pE001* + ID_MODEL_FROM_DATABASE=GraphLink [SilverLink] + +usb:v0451pE003* + ID_MODEL_FROM_DATABASE=TI-84 Plus Calculator + +usb:v0451pE004* + ID_MODEL_FROM_DATABASE=TI-89 Titanium Calculator + +usb:v0451pE008* + ID_MODEL_FROM_DATABASE=TI-84 Plus Silver Calculator + +usb:v0451pE012* + ID_MODEL_FROM_DATABASE=TI-Nspire Calculator + +usb:v0451pF430* + ID_MODEL_FROM_DATABASE=MSP-FET430UIF JTAG Tool + +usb:v0451pF432* + ID_MODEL_FROM_DATABASE=eZ430 Development Tool + +usb:v0451pFFFF* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0452* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electronics America, Inc. + +usb:v0452p0021* + ID_MODEL_FROM_DATABASE=HID Monitor Controls + +usb:v0452p0050* + ID_MODEL_FROM_DATABASE=Diamond Pro 900u CRT Monitor + +usb:v0452p0051* + ID_MODEL_FROM_DATABASE=Integrated Hub + +usb:v0452p0100* + ID_MODEL_FROM_DATABASE=Control Panel for Leica TCS SP5 + +usb:v0453* + ID_VENDOR_FROM_DATABASE=CMD Technology + +usb:v0453p6781* + ID_MODEL_FROM_DATABASE=NMB Keyboard + +usb:v0453p6783* + ID_MODEL_FROM_DATABASE=Chicony Composite Keyboard + +usb:v0454* + ID_VENDOR_FROM_DATABASE=Vobis Microcomputer AG + +usb:v0455* + ID_VENDOR_FROM_DATABASE=Telematics International, Inc. + +usb:v0456* + ID_VENDOR_FROM_DATABASE=Analog Devices, Inc. + +usb:v0456pF000* + ID_MODEL_FROM_DATABASE=FT2232 JTAG ICE [gnICE] + +usb:v0456pF001* + ID_MODEL_FROM_DATABASE=FT2232H Hi-Speed JTAG ICE [gnICE+] + +usb:v0457* + ID_VENDOR_FROM_DATABASE=Silicon Integrated Systems Corp. + +usb:v0457p0150* + ID_MODEL_FROM_DATABASE=Super Talent 1GB Flash Drive + +usb:v0457p0151* + ID_MODEL_FROM_DATABASE=Super Flash 1GB / GXT 64MB Flash Drive + +usb:v0457p0162* + ID_MODEL_FROM_DATABASE=SiS162 usb Wireless LAN Adapter + +usb:v0457p0163* + ID_MODEL_FROM_DATABASE=SiS163U 802.11 Wireless LAN Adapter + +usb:v0457p0817* + ID_MODEL_FROM_DATABASE=SiS-184-ASUS-4352.17 touch panel + +usb:v0457p5401* + ID_MODEL_FROM_DATABASE=Wireless Adapter RO80211GS-USB + +usb:v0458* + ID_VENDOR_FROM_DATABASE=KYE Systems Corp. (Mouse Systems) + +usb:v0458p0001* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0458p0002* + ID_MODEL_FROM_DATABASE=Genius NetMouse Pro + +usb:v0458p0003* + ID_MODEL_FROM_DATABASE=Genius NetScroll+ + +usb:v0458p0006* + ID_MODEL_FROM_DATABASE=Easy Mouse+ + +usb:v0458p000B* + ID_MODEL_FROM_DATABASE=NetMouse Wheel(P+U) + +usb:v0458p000C* + ID_MODEL_FROM_DATABASE=TACOMA Fingerprint V1.06.01 + +usb:v0458p000E* + ID_MODEL_FROM_DATABASE=Genius NetScroll Optical + +usb:v0458p0013* + ID_MODEL_FROM_DATABASE=TACOMA Fingerprint Mouse V1.06.01 + +usb:v0458p001A* + ID_MODEL_FROM_DATABASE=Genius WebScroll+ + +usb:v0458p002E* + ID_MODEL_FROM_DATABASE=NetScroll + Traveler / NetScroll 110 + +usb:v0458p0036* + ID_MODEL_FROM_DATABASE=Pocket Mouse LE + +usb:v0458p0039* + ID_MODEL_FROM_DATABASE=NetScroll+ Superior + +usb:v0458p003A* + ID_MODEL_FROM_DATABASE=NetScroll+ Mini Traveler / Genius NetScroll 120 + +usb:v0458p004C* + ID_MODEL_FROM_DATABASE=Slimstar Pro Keyboard + +usb:v0458p0056* + ID_MODEL_FROM_DATABASE=Ergo 300 Mouse + +usb:v0458p0057* + ID_MODEL_FROM_DATABASE=Enhanced Gaming Device + +usb:v0458p0059* + ID_MODEL_FROM_DATABASE=Enhanced Laser Device + +usb:v0458p005A* + ID_MODEL_FROM_DATABASE=Enhanced Device + +usb:v0458p005B* + ID_MODEL_FROM_DATABASE=Enhanced Device + +usb:v0458p005C* + ID_MODEL_FROM_DATABASE=Enhanced Laser Gaming Device + +usb:v0458p005D* + ID_MODEL_FROM_DATABASE=Enhanced Device + +usb:v0458p0061* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0458p0066* + ID_MODEL_FROM_DATABASE=Genius Traveler 1000 Wireless Mouse + +usb:v0458p0072* + ID_MODEL_FROM_DATABASE=Navigator 335 + +usb:v0458p0083* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0458p0087* + ID_MODEL_FROM_DATABASE=Ergo 525V Laser Mouse + +usb:v0458p0089* + ID_MODEL_FROM_DATABASE=Genius Traveler 350 + +usb:v0458p00CA* + ID_MODEL_FROM_DATABASE=Pen Mouse + +usb:v0458p0100* + ID_MODEL_FROM_DATABASE=EasyPen Tablet + +usb:v0458p0101* + ID_MODEL_FROM_DATABASE=CueCat + +usb:v0458p011B* + ID_MODEL_FROM_DATABASE=NetScroll T220 + +usb:v0458p1001* + ID_MODEL_FROM_DATABASE=Joystick + +usb:v0458p1002* + ID_MODEL_FROM_DATABASE=Game Pad + +usb:v0458p1003* + ID_MODEL_FROM_DATABASE=Genius VideoCam + +usb:v0458p1004* + ID_MODEL_FROM_DATABASE=Flight2000 F-23 Joystick + +usb:v0458p100A* + ID_MODEL_FROM_DATABASE=Aashima Technology Trust Sight Fighter Vibration Feedback Joystick + +usb:v0458p2001* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid Pro Scanner + +usb:v0458p2004* + ID_MODEL_FROM_DATABASE=ColorPage-HR6 V1 Scanner + +usb:v0458p2005* + ID_MODEL_FROM_DATABASE=ColorPage-HR6/Vivid3 + +usb:v0458p2007* + ID_MODEL_FROM_DATABASE=ColorPage-HR6 V2 Scanner + +usb:v0458p2008* + ID_MODEL_FROM_DATABASE=ColorPage-HR6 V2 Scanner + +usb:v0458p2009* + ID_MODEL_FROM_DATABASE=ColorPage-HR6A Scanner + +usb:v0458p2011* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid3x Scanner + +usb:v0458p2012* + ID_MODEL_FROM_DATABASE=Plustek Scanner + +usb:v0458p2013* + ID_MODEL_FROM_DATABASE=ColorPage-HR7 Scanner + +usb:v0458p2014* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid4 + +usb:v0458p2015* + ID_MODEL_FROM_DATABASE=ColorPage-HR7LE Scanner + +usb:v0458p2016* + ID_MODEL_FROM_DATABASE=ColorPage-HR6X Scanner + +usb:v0458p2017* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid3xe + +usb:v0458p2018* + ID_MODEL_FROM_DATABASE=ColorPage-HR7X + +usb:v0458p2019* + ID_MODEL_FROM_DATABASE=ColorPage-HR6X Slim + +usb:v0458p201A* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid4xe + +usb:v0458p201B* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid4x + +usb:v0458p201C* + ID_MODEL_FROM_DATABASE=ColorPage-HR8 + +usb:v0458p201D* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid 1200 X + +usb:v0458p201E* + ID_MODEL_FROM_DATABASE=ColorPage-Slim 1200 + +usb:v0458p201F* + ID_MODEL_FROM_DATABASE=ColorPage-Vivid 1200 XE + +usb:v0458p2020* + ID_MODEL_FROM_DATABASE=ColorPage-Slim 1200 USB2 + +usb:v0458p2021* + ID_MODEL_FROM_DATABASE=ColorPage-SF600 + +usb:v0458p3017* + ID_MODEL_FROM_DATABASE=SPEED WHEEL 3 Vibration + +usb:v0458p3018* + ID_MODEL_FROM_DATABASE=Wireless 2.4Ghz Game Pad + +usb:v0458p3019* + ID_MODEL_FROM_DATABASE=10-Button USB Joystick with Vibration + +usb:v0458p301A* + ID_MODEL_FROM_DATABASE=MaxFire G-12U Vibration + +usb:v0458p301D* + ID_MODEL_FROM_DATABASE=Genius MaxFire MiniPad + +usb:v0458p400F* + ID_MODEL_FROM_DATABASE=Genius TVGo DVB-T02Q MCE + +usb:v0458p4012* + ID_MODEL_FROM_DATABASE=TVGo DVB-T03 [AF9015] + +usb:v0458p5003* + ID_MODEL_FROM_DATABASE=G-pen 560 Tablet + +usb:v0458p5004* + ID_MODEL_FROM_DATABASE=G-pen Tablet + +usb:v0458p505E* + ID_MODEL_FROM_DATABASE=Genius iSlim 330 + +usb:v0458p6001* + ID_MODEL_FROM_DATABASE=GF3000F Ethernet Adapter + +usb:v0458p7004* + ID_MODEL_FROM_DATABASE=VideoCAM Express V2 + +usb:v0458p7006* + ID_MODEL_FROM_DATABASE=Dsc 1.3 Smart Camera Device + +usb:v0458p7007* + ID_MODEL_FROM_DATABASE=VideoCAM Web + +usb:v0458p7009* + ID_MODEL_FROM_DATABASE=G-Shot G312 Still Camera Device + +usb:v0458p700C* + ID_MODEL_FROM_DATABASE=VideoCAM Web V3 + +usb:v0458p700D* + ID_MODEL_FROM_DATABASE=G-Shot G511 Composite Device + +usb:v0458p700F* + ID_MODEL_FROM_DATABASE=VideoCAM Web + +usb:v0458p7012* + ID_MODEL_FROM_DATABASE=WebCAM USB2.0 + +usb:v0458p7014* + ID_MODEL_FROM_DATABASE=VideoCAM Live V3 + +usb:v0458p701C* + ID_MODEL_FROM_DATABASE=G-Shot G512 Still Camera + +usb:v0458p7020* + ID_MODEL_FROM_DATABASE=Sim 321C + +usb:v0458p7025* + ID_MODEL_FROM_DATABASE=Eye 311Q Camera + +usb:v0458p7029* + ID_MODEL_FROM_DATABASE=Genius Look 320s (SN9C201 + HV7131R) + +usb:v0458p702F* + ID_MODEL_FROM_DATABASE=Genius Slim 322 + +usb:v0458p7035* + ID_MODEL_FROM_DATABASE=i-Look 325T Camera + +usb:v0458p7045* + ID_MODEL_FROM_DATABASE=Genius Look 1320 V2 + +usb:v0458p704C* + ID_MODEL_FROM_DATABASE=Genius i-Look 1321 + +usb:v0458p704D* + ID_MODEL_FROM_DATABASE=Slim 1322AF + +usb:v0458p7055* + ID_MODEL_FROM_DATABASE=Slim 2020AF camera + +usb:v0458p705A* + ID_MODEL_FROM_DATABASE=Asus USB2.0 Webcam + +usb:v0458p705C* + ID_MODEL_FROM_DATABASE=Genius iSlim 1300AF + +usb:v0458p7061* + ID_MODEL_FROM_DATABASE=Genius iLook 1321 V2 + +usb:v0458p7066* + ID_MODEL_FROM_DATABASE=Acer Crystal Eye Webcam + +usb:v0458p7067* + ID_MODEL_FROM_DATABASE=Genius iSlim 1300AF V2 + +usb:v0458p7068* + ID_MODEL_FROM_DATABASE=Genius eFace 1325R + +usb:v0458p706D* + ID_MODEL_FROM_DATABASE=Genius iSlim 2000AF V2 + +usb:v0458p7076* + ID_MODEL_FROM_DATABASE=Genius FaceCam 312 + +usb:v0458p7079* + ID_MODEL_FROM_DATABASE=FaceCam 2025R + +usb:v0458p707F* + ID_MODEL_FROM_DATABASE=TVGo DVB-T03 [RTL2832] + +usb:v0458p7088* + ID_MODEL_FROM_DATABASE=WideCam 1050 + +usb:v0458p7089* + ID_MODEL_FROM_DATABASE=Genius FaceCam 320 + +usb:v0458p708C* + ID_MODEL_FROM_DATABASE=Genius WideCam F100 + +usb:v0459* + ID_VENDOR_FROM_DATABASE=Adobe Systems, Inc. + +usb:v045A* + ID_VENDOR_FROM_DATABASE=SONICblue, Inc. + +usb:v045Ap07DA* + ID_MODEL_FROM_DATABASE=Supra Express 56K modem + +usb:v045Ap0B4A* + ID_MODEL_FROM_DATABASE=SupraMax 2890 56K Modem [Lucent Atlas] + +usb:v045Ap0B68* + ID_MODEL_FROM_DATABASE=SupraMax 56K Modem + +usb:v045Ap5001* + ID_MODEL_FROM_DATABASE=Rio 600 MP3 Player + +usb:v045Ap5002* + ID_MODEL_FROM_DATABASE=Rio 800 MP3 Player + +usb:v045Ap5003* + ID_MODEL_FROM_DATABASE=Nike Psa/Play MP3 Player + +usb:v045Ap5005* + ID_MODEL_FROM_DATABASE=Rio S10 MP3 Player + +usb:v045Ap5006* + ID_MODEL_FROM_DATABASE=Rio S50 MP3 Player + +usb:v045Ap5007* + ID_MODEL_FROM_DATABASE=Rio S35 MP3 Player + +usb:v045Ap5008* + ID_MODEL_FROM_DATABASE=Rio 900 MP3 Player + +usb:v045Ap5009* + ID_MODEL_FROM_DATABASE=Rio S30 MP3 Player + +usb:v045Ap500D* + ID_MODEL_FROM_DATABASE=Fuse MP3 Player + +usb:v045Ap500E* + ID_MODEL_FROM_DATABASE=Chiba MP3 Player + +usb:v045Ap500F* + ID_MODEL_FROM_DATABASE=Cali MP3 Player + +usb:v045Ap5010* + ID_MODEL_FROM_DATABASE=Rio S11 MP3 Player + +usb:v045Ap501C* + ID_MODEL_FROM_DATABASE=Virgin MPF-1000 + +usb:v045Ap501D* + ID_MODEL_FROM_DATABASE=Rio Fuse + +usb:v045Ap501E* + ID_MODEL_FROM_DATABASE=Rio Chiba + +usb:v045Ap501F* + ID_MODEL_FROM_DATABASE=Rio Cali + +usb:v045Ap503F* + ID_MODEL_FROM_DATABASE=Cali256 MP3 Player + +usb:v045Ap5202* + ID_MODEL_FROM_DATABASE=Rio Riot MP3 Player + +usb:v045Ap5210* + ID_MODEL_FROM_DATABASE=Rio Karma Music Player + +usb:v045Ap5220* + ID_MODEL_FROM_DATABASE=Rio Nitrus MP3 Player + +usb:v045Ap5221* + ID_MODEL_FROM_DATABASE=Rio Eigen + +usb:v045B* + ID_VENDOR_FROM_DATABASE=Hitachi, Ltd + +usb:v045Bp0053* + ID_MODEL_FROM_DATABASE=RX610 RX-Stick + +usb:v045D* + ID_VENDOR_FROM_DATABASE=Nortel Networks, Ltd + +usb:v045E* + ID_VENDOR_FROM_DATABASE=Microsoft Corp. + +usb:v045Ep0007* + ID_MODEL_FROM_DATABASE=SideWinder Game Pad + +usb:v045Ep0008* + ID_MODEL_FROM_DATABASE=SideWinder Precision Pro + +usb:v045Ep0009* + ID_MODEL_FROM_DATABASE=IntelliMouse + +usb:v045Ep000B* + ID_MODEL_FROM_DATABASE=Natural Keyboard Elite + +usb:v045Ep000E* + ID_MODEL_FROM_DATABASE=SideWinder® Freestyle Pro + +usb:v045Ep0014* + ID_MODEL_FROM_DATABASE=Digital Sound System 80 + +usb:v045Ep001A* + ID_MODEL_FROM_DATABASE=SideWinder Precision Racing Wheel + +usb:v045Ep001B* + ID_MODEL_FROM_DATABASE=SideWinder Force Feedback 2 Joystick + +usb:v045Ep001C* + ID_MODEL_FROM_DATABASE=Internet Keyboard Pro + +usb:v045Ep001D* + ID_MODEL_FROM_DATABASE=Natural Keyboard Pro + +usb:v045Ep001E* + ID_MODEL_FROM_DATABASE=IntelliMouse Explorer + +usb:v045Ep0023* + ID_MODEL_FROM_DATABASE=Trackball Optical + +usb:v045Ep0024* + ID_MODEL_FROM_DATABASE=Trackball Explorer + +usb:v045Ep0025* + ID_MODEL_FROM_DATABASE=IntelliEye Mouse + +usb:v045Ep0026* + ID_MODEL_FROM_DATABASE=SideWinder GamePad Pro + +usb:v045Ep0027* + ID_MODEL_FROM_DATABASE=SideWinder PnP GamePad + +usb:v045Ep0028* + ID_MODEL_FROM_DATABASE=SideWinder Dual Strike + +usb:v045Ep0029* + ID_MODEL_FROM_DATABASE=IntelliMouse Optical + +usb:v045Ep002B* + ID_MODEL_FROM_DATABASE=Internet Keyboard Pro + +usb:v045Ep002D* + ID_MODEL_FROM_DATABASE=Internet Keyboard + +usb:v045Ep002F* + ID_MODEL_FROM_DATABASE=Integrated Hub + +usb:v045Ep0033* + ID_MODEL_FROM_DATABASE=Sidewinder Strategic Commander + +usb:v045Ep0034* + ID_MODEL_FROM_DATABASE=SideWinder Force Feedback Wheel + +usb:v045Ep0038* + ID_MODEL_FROM_DATABASE=SideWinder Precision 2 + +usb:v045Ep0039* + ID_MODEL_FROM_DATABASE=IntelliMouse Optical + +usb:v045Ep003B* + ID_MODEL_FROM_DATABASE=SideWinder Game Voice + +usb:v045Ep003C* + ID_MODEL_FROM_DATABASE=SideWinder Joystick + +usb:v045Ep0040* + ID_MODEL_FROM_DATABASE=Wheel Mouse Optical + +usb:v045Ep0047* + ID_MODEL_FROM_DATABASE=IntelliMouse Explorer 3.0 + +usb:v045Ep0048* + ID_MODEL_FROM_DATABASE=Office Keyboard 1.0A + +usb:v045Ep0053* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v045Ep0059* + ID_MODEL_FROM_DATABASE=Wireless IntelliMouse Explorer + +usb:v045Ep005C* + ID_MODEL_FROM_DATABASE=Office Keyboard (106/109) + +usb:v045Ep005F* + ID_MODEL_FROM_DATABASE=Wireless MultiMedia Keyboard + +usb:v045Ep0061* + ID_MODEL_FROM_DATABASE=Wireless MultiMedia Keyboard (106/109) + +usb:v045Ep0063* + ID_MODEL_FROM_DATABASE=Wireless Natural MultiMedia Keyboard + +usb:v045Ep0065* + ID_MODEL_FROM_DATABASE=Wireless Natural MultiMedia Keyboard (106/109) + +usb:v045Ep006A* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse (IntelliPoint) + +usb:v045Ep006D* + ID_MODEL_FROM_DATABASE=eHome Remote Control Keyboard keys + +usb:v045Ep006E* + ID_MODEL_FROM_DATABASE=MN-510 802.11b Wireless Adapter [Intersil ISL3873B] + +usb:v045Ep006F* + ID_MODEL_FROM_DATABASE=Smart Display Reference Device + +usb:v045Ep0070* + ID_MODEL_FROM_DATABASE=Wireless MultiMedia Keyboard + +usb:v045Ep0071* + ID_MODEL_FROM_DATABASE=Wireless MultiMedia Keyboard (106/109) + +usb:v045Ep0072* + ID_MODEL_FROM_DATABASE=Wireless Natural MultiMedia Keyboard + +usb:v045Ep0073* + ID_MODEL_FROM_DATABASE=Wireless Natural MultiMedia Keyboard (106/109) + +usb:v045Ep0079* + ID_MODEL_FROM_DATABASE=IXI Ogo CT-17 handheld device + +usb:v045Ep007A* + ID_MODEL_FROM_DATABASE=10/100 USB NIC + +usb:v045Ep007D* + ID_MODEL_FROM_DATABASE=Notebook Optical Mouse + +usb:v045Ep007E* + ID_MODEL_FROM_DATABASE=Wireless Transceiver for Bluetooth + +usb:v045Ep0080* + ID_MODEL_FROM_DATABASE=Digital Media Pro Keyboard + +usb:v045Ep0083* + ID_MODEL_FROM_DATABASE=Basic Optical Mouse + +usb:v045Ep0084* + ID_MODEL_FROM_DATABASE=Basic Optical Mouse + +usb:v045Ep008A* + ID_MODEL_FROM_DATABASE=Wireless Keyboard and Mouse + +usb:v045Ep008B* + ID_MODEL_FROM_DATABASE=Dual Receiver Wireless Mouse (IntelliPoint) + +usb:v045Ep008C* + ID_MODEL_FROM_DATABASE=Wireless Intellimouse Explorer 2.0 + +usb:v045Ep0095* + ID_MODEL_FROM_DATABASE=IntelliMouse Explorer 4.0 (IntelliPoint) + +usb:v045Ep009C* + ID_MODEL_FROM_DATABASE=Wireless Transceiver for Bluetooth 2.0 + +usb:v045Ep009D* + ID_MODEL_FROM_DATABASE=Wireless Optical Desktop 3.0 + +usb:v045Ep00A0* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v045Ep00A4* + ID_MODEL_FROM_DATABASE=Compact Optical Mouse, model 1016 + +usb:v045Ep00B0* + ID_MODEL_FROM_DATABASE=Digital Media Pro Keyboard + +usb:v045Ep00B4* + ID_MODEL_FROM_DATABASE=Digital Media Keyboard 1.0A + +usb:v045Ep00B9* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse 3.0 + +usb:v045Ep00BB* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v045Ep00BC* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v045Ep00BD* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v045Ep00C2* + ID_MODEL_FROM_DATABASE=MN-710 802.11g Wireless Adapter [Intersil ISL3886] + +usb:v045Ep00C9* + ID_MODEL_FROM_DATABASE=MTP Device + +usb:v045Ep00CA* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v045Ep00CB* + ID_MODEL_FROM_DATABASE=Basic Optical Mouse v2.0 + +usb:v045Ep00CE* + ID_MODEL_FROM_DATABASE=Generic PPC Flash device + +usb:v045Ep00D1* + ID_MODEL_FROM_DATABASE=Optical Mouse with Tilt Wheel + +usb:v045Ep00DA* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v045Ep00DB* + ID_MODEL_FROM_DATABASE=Natural Ergonomic Keyboard 4000 V1.0 + +usb:v045Ep00DD* + ID_MODEL_FROM_DATABASE=Comfort Curve Keyboard 2000 V1.0 + +usb:v045Ep00E1* + ID_MODEL_FROM_DATABASE=Wireless Laser Mouse 6000 Receiver + +usb:v045Ep00F4* + ID_MODEL_FROM_DATABASE=LifeCam VX-6000 (SN9C20x + OV9650) + +usb:v045Ep00F5* + ID_MODEL_FROM_DATABASE=LifeCam VX-3000 + +usb:v045Ep00F6* + ID_MODEL_FROM_DATABASE=Comfort Optical Mouse 1000 + +usb:v045Ep00F7* + ID_MODEL_FROM_DATABASE=LifeCam VX-1000 + +usb:v045Ep00F8* + ID_MODEL_FROM_DATABASE=LifeCam NX-6000 + +usb:v045Ep00F9* + ID_MODEL_FROM_DATABASE=Wireless Desktop Receiver 3.1 + +usb:v045Ep0202* + ID_MODEL_FROM_DATABASE=Xbox Controller + +usb:v045Ep0280* + ID_MODEL_FROM_DATABASE=Xbox Memory Unit (8MB) + +usb:v045Ep0283* + ID_MODEL_FROM_DATABASE=Xbox Communicator + +usb:v045Ep0284* + ID_MODEL_FROM_DATABASE=Xbox DVD Playback Kit + +usb:v045Ep0285* + ID_MODEL_FROM_DATABASE=Xbox Controller S + +usb:v045Ep0288* + ID_MODEL_FROM_DATABASE=Xbox Controller S Hub + +usb:v045Ep0289* + ID_MODEL_FROM_DATABASE=Xbox Controller S + +usb:v045Ep028B* + ID_MODEL_FROM_DATABASE=Xbox360 DVD Emulator + +usb:v045Ep028D* + ID_MODEL_FROM_DATABASE=Xbox360 Memory Unit 64MB + +usb:v045Ep028E* + ID_MODEL_FROM_DATABASE=Xbox360 Controller + +usb:v045Ep028F* + ID_MODEL_FROM_DATABASE=Xbox360 Wireless Controller + +usb:v045Ep0290* + ID_MODEL_FROM_DATABASE=Xbox360 Performance Pipe (PIX) + +usb:v045Ep0291* + ID_MODEL_FROM_DATABASE=Xbox 360 Wireless Receiver for Windows + +usb:v045Ep0292* + ID_MODEL_FROM_DATABASE=Xbox360 Wireless Networking Adapter + +usb:v045Ep029C* + ID_MODEL_FROM_DATABASE=Xbox360 HD-DVD Drive + +usb:v045Ep029D* + ID_MODEL_FROM_DATABASE=Xbox360 HD-DVD Drive + +usb:v045Ep029E* + ID_MODEL_FROM_DATABASE=Xbox360 HD-DVD Memory Unit + +usb:v045Ep02A0* + ID_MODEL_FROM_DATABASE=Xbox360 Big Button IR + +usb:v045Ep02A1* + ID_MODEL_FROM_DATABASE=Xbox 360 Wireless Receiver for Windows + +usb:v045Ep02A8* + ID_MODEL_FROM_DATABASE=Xbox360 Wireless N Networking Adapter [Atheros AR7010+AR9280] + +usb:v045Ep02AD* + ID_MODEL_FROM_DATABASE=Xbox NUI Audio + +usb:v045Ep02AE* + ID_MODEL_FROM_DATABASE=Xbox NUI Camera + +usb:v045Ep02B0* + ID_MODEL_FROM_DATABASE=Xbox NUI Motor + +usb:v045Ep02B6* + ID_MODEL_FROM_DATABASE=Xbox360 Bluetooth Wireless Headset + +usb:v045Ep02BE* + ID_MODEL_FROM_DATABASE=Kinect for Windows NUI Audio + +usb:v045Ep02BF* + ID_MODEL_FROM_DATABASE=Kinect for Windows NUI Camera + +usb:v045Ep02C2* + ID_MODEL_FROM_DATABASE=Kinect for Windows NUI Motor + +usb:v045Ep02D1* + ID_MODEL_FROM_DATABASE=Xbox One Controller + +usb:v045Ep02D5* + ID_MODEL_FROM_DATABASE=Xbox One Digital TV Tuner + +usb:v045Ep02DD* + ID_MODEL_FROM_DATABASE=Xbox One Controller (Covert Forces/Firmware 2015) + +usb:v045Ep0400* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0401* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0402* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0403* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0404* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0405* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0406* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0407* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0408* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0409* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040C* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040D* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040E* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep040F* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0410* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0411* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0412* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0413* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0414* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0415* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0416* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0417* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2002 + +usb:v045Ep0432* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0433* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0434* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0435* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0436* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0437* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0438* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0439* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep043A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep043B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep043C* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep043D* + ID_MODEL_FROM_DATABASE=Becker Traffic Assist Highspeed 7934 + +usb:v045Ep043E* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep043F* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0440* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0441* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0442* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0443* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0444* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0445* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0446* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0447* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0448* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0449* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044C* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044D* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044E* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep044F* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0450* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0451* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0452* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0453* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0454* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0455* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0456* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0457* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0458* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0459* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045C* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045D* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045E* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep045F* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0460* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0461* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0462* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0463* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0464* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0465* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0466* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0467* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0468* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0469* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046C* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046D* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046E* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep046F* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0470* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0471* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0472* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0473* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0474* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0475* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0476* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0477* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0478* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep0479* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep047A* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep047B* + ID_MODEL_FROM_DATABASE=Windows Powered Pocket PC 2003 + +usb:v045Ep04C8* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04C9* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04CA* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04CB* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04CC* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04CD* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04CE* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2002 + +usb:v045Ep04D7* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04D8* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04D9* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DA* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DB* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DC* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DD* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DE* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04DF* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E0* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E1* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E2* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E3* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E4* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E5* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E6* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E7* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E8* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04E9* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04EA* + ID_MODEL_FROM_DATABASE=Windows Powered Smartphone 2003 + +usb:v045Ep04EC* + ID_MODEL_FROM_DATABASE=Windows Phone (Zune) + +usb:v045Ep063E* + ID_MODEL_FROM_DATABASE=Zune HD Media Player + +usb:v045Ep0640* + ID_MODEL_FROM_DATABASE=KIN Phone + +usb:v045Ep0641* + ID_MODEL_FROM_DATABASE=KIN Phone + +usb:v045Ep0642* + ID_MODEL_FROM_DATABASE=KIN Phone + +usb:v045Ep0707* + ID_MODEL_FROM_DATABASE=Wireless Laser Mouse 8000 + +usb:v045Ep0708* + ID_MODEL_FROM_DATABASE=Transceiver v 3.0 for Bluetooth + +usb:v045Ep070A* + ID_MODEL_FROM_DATABASE=Charon Bluetooth Dongle (DFU) + +usb:v045Ep070F* + ID_MODEL_FROM_DATABASE=LifeChat LX-3000 Headset + +usb:v045Ep0710* + ID_MODEL_FROM_DATABASE=Zune Media Player + +usb:v045Ep0713* + ID_MODEL_FROM_DATABASE=Wireless Presenter Mouse 8000 + +usb:v045Ep0719* + ID_MODEL_FROM_DATABASE=Xbox 360 Wireless Adapter + +usb:v045Ep071F* + ID_MODEL_FROM_DATABASE=Mouse/Keyboard 2.4GHz Transceiver V2.0 + +usb:v045Ep0721* + ID_MODEL_FROM_DATABASE=LifeCam NX-3000 (UVC-compliant) + +usb:v045Ep0723* + ID_MODEL_FROM_DATABASE=LifeCam VX-7000 (UVC-compliant) + +usb:v045Ep0724* + ID_MODEL_FROM_DATABASE=SideWinder Mouse + +usb:v045Ep0728* + ID_MODEL_FROM_DATABASE=LifeCam VX-5000 + +usb:v045Ep0730* + ID_MODEL_FROM_DATABASE=Digital Media Keyboard 3000 + +usb:v045Ep0734* + ID_MODEL_FROM_DATABASE=Wireless Optical Desktop 700 + +usb:v045Ep0736* + ID_MODEL_FROM_DATABASE=Sidewinder X5 Mouse + +usb:v045Ep0737* + ID_MODEL_FROM_DATABASE=Compact Optical Mouse 500 + +usb:v045Ep0745* + ID_MODEL_FROM_DATABASE=Nano Transceiver v1.0 for Bluetooth + +usb:v045Ep0750* + ID_MODEL_FROM_DATABASE=Wired Keyboard 600 + +usb:v045Ep0752* + ID_MODEL_FROM_DATABASE=Wired Keyboard 400 + +usb:v045Ep075D* + ID_MODEL_FROM_DATABASE=LifeCam Cinema + +usb:v045Ep0766* + ID_MODEL_FROM_DATABASE=LifeCam VX-800 + +usb:v045Ep0768* + ID_MODEL_FROM_DATABASE=Sidewinder X4 + +usb:v045Ep076C* + ID_MODEL_FROM_DATABASE=Comfort Mouse 4500 + +usb:v045Ep076D* + ID_MODEL_FROM_DATABASE=LifeCam HD-5000 + +usb:v045Ep0772* + ID_MODEL_FROM_DATABASE=LifeCam Studio + +usb:v045Ep0779* + ID_MODEL_FROM_DATABASE=LifeCam HD-3000 + +usb:v045Ep0780* + ID_MODEL_FROM_DATABASE=Comfort Curve Keyboard 3000 + +usb:v045Ep0797* + ID_MODEL_FROM_DATABASE=Optical Mouse 200 + +usb:v045Ep07A5* + ID_MODEL_FROM_DATABASE=Wireless Receiver 1461C + +usb:v045Ep07CA* + ID_MODEL_FROM_DATABASE=Surface Pro 3 Docking Station Audio Device + +usb:v045Ep07F8* + ID_MODEL_FROM_DATABASE=Wired Keyboard 600 (model 1576) + +usb:v045Ep07FD* + ID_MODEL_FROM_DATABASE=Nano Transceiver 1.1 + +usb:v045Ep930A* + ID_MODEL_FROM_DATABASE=ISOUSB.SYS Intel 82930 Isochronous IO Test Board + +usb:v045EpFFCA* + ID_MODEL_FROM_DATABASE=Catalina + +usb:v045EpFFF8* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v045EpFFFF* + ID_MODEL_FROM_DATABASE=Windows CE Mass Storage + +usb:v0460* + ID_VENDOR_FROM_DATABASE=Ace Cad Enterprise Co., Ltd + +usb:v0460p0004* + ID_MODEL_FROM_DATABASE=Tablet (5x3.75) + +usb:v0460p0006* + ID_MODEL_FROM_DATABASE=LCD Tablet (12x9) + +usb:v0460p0008* + ID_MODEL_FROM_DATABASE=Tablet (3x2.25) + +usb:v0461* + ID_VENDOR_FROM_DATABASE=Primax Electronics, Ltd + +usb:v0461p0010* + ID_MODEL_FROM_DATABASE=HP PR1101U / Primax PMX-KPR1101U Keyboard + +usb:v0461p0300* + ID_MODEL_FROM_DATABASE=G2-300 Scanner + +usb:v0461p0301* + ID_MODEL_FROM_DATABASE=G2E-300 Scanner + +usb:v0461p0302* + ID_MODEL_FROM_DATABASE=G2-300 #2 Scanner + +usb:v0461p0303* + ID_MODEL_FROM_DATABASE=G2E-300 #2 Scanner + +usb:v0461p0340* + ID_MODEL_FROM_DATABASE=Colorado 9600 Scanner + +usb:v0461p0341* + ID_MODEL_FROM_DATABASE=Colorado 600u Scanner + +usb:v0461p0345* + ID_MODEL_FROM_DATABASE=Visioneer 6200 Scanner + +usb:v0461p0346* + ID_MODEL_FROM_DATABASE=Memorex Maxx 6136u Scanner + +usb:v0461p0347* + ID_MODEL_FROM_DATABASE=Primascan Colorado 2600u/Visioneer 4400 Scanner + +usb:v0461p0360* + ID_MODEL_FROM_DATABASE=Colorado 19200 Scanner + +usb:v0461p0361* + ID_MODEL_FROM_DATABASE=Colorado 1200u Scanner + +usb:v0461p0363* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p0364* + ID_MODEL_FROM_DATABASE=LG Electronics Scanworks 600U Scanner + +usb:v0461p0365* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p0366* + ID_MODEL_FROM_DATABASE=6400 + +usb:v0461p0367* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p0371* + ID_MODEL_FROM_DATABASE=Visioneer Onetouch 8920 Scanner + +usb:v0461p0374* + ID_MODEL_FROM_DATABASE=UMAX Astra 2500 + +usb:v0461p0375* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p0377* + ID_MODEL_FROM_DATABASE=Medion MD 5345 Scanner + +usb:v0461p0378* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p037B* + ID_MODEL_FROM_DATABASE=Medion MD 6190 Scanner + +usb:v0461p037C* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v0461p0380* + ID_MODEL_FROM_DATABASE=G2-600 Scanner + +usb:v0461p0381* + ID_MODEL_FROM_DATABASE=ReadyScan 636i Scanner + +usb:v0461p0382* + ID_MODEL_FROM_DATABASE=G2-600 #2 Scanner + +usb:v0461p0383* + ID_MODEL_FROM_DATABASE=G2E-600 Scanner + +usb:v0461p038A* + ID_MODEL_FROM_DATABASE=UMAX Astra 3000/3600 + +usb:v0461p038B* + ID_MODEL_FROM_DATABASE=Xerox 2400 Onetouch + +usb:v0461p038C* + ID_MODEL_FROM_DATABASE=UMAX Astra 4100 + +usb:v0461p0392* + ID_MODEL_FROM_DATABASE=Medion/Lifetec/Tevion/Cytron MD 6190 + +usb:v0461p03A8* + ID_MODEL_FROM_DATABASE=9420M + +usb:v0461p0813* + ID_MODEL_FROM_DATABASE=IBM UltraPort Camera + +usb:v0461p0815* + ID_MODEL_FROM_DATABASE=Micro Innovations IC200 Webcam + +usb:v0461p0819* + ID_MODEL_FROM_DATABASE=Fujifilm IX-30 Camera [webcam mode] + +usb:v0461p081A* + ID_MODEL_FROM_DATABASE=Fujifilm IX-30 Camera [storage mode] + +usb:v0461p081C* + ID_MODEL_FROM_DATABASE=Elitegroup ECS-C11 Camera + +usb:v0461p081D* + ID_MODEL_FROM_DATABASE=Elitegroup ECS-C11 Storage + +usb:v0461p0A00* + ID_MODEL_FROM_DATABASE=Micro Innovations Web Cam 320 + +usb:v0461p4D01* + ID_MODEL_FROM_DATABASE=Comfort Keyboard + +usb:v0461p4D02* + ID_MODEL_FROM_DATABASE=Mouse-in-a-Box + +usb:v0461p4D03* + ID_MODEL_FROM_DATABASE=Kensington Mouse-in-a-box + +usb:v0461p4D04* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0461p4D06* + ID_MODEL_FROM_DATABASE=Balless Mouse (HID) + +usb:v0461p4D0F* + ID_MODEL_FROM_DATABASE=HP Optical Mouse + +usb:v0461p4D15* + ID_MODEL_FROM_DATABASE=Dell Optical Mouse + +usb:v0461p4D17* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v0461p4D20* + ID_MODEL_FROM_DATABASE=HP Optical Mouse + +usb:v0461p4D2A* + ID_MODEL_FROM_DATABASE=PoPo Elixir Mouse (HID) + +usb:v0461p4D2B* + ID_MODEL_FROM_DATABASE=Wireless Laser Mini Mouse (HID) + +usb:v0461p4D2C* + ID_MODEL_FROM_DATABASE=PoPo Mini Pointer Mouse (HID) + +usb:v0461p4D2E* + ID_MODEL_FROM_DATABASE=Optical Mobile Mouse (HID) + +usb:v0461p4D51* + ID_MODEL_FROM_DATABASE=0Y357C PMX-MMOCZUL (B) [Dell Laser Mouse] + +usb:v0461p4D62* + ID_MODEL_FROM_DATABASE=HP Laser Mobile Mini Mouse + +usb:v0461p4D75* + ID_MODEL_FROM_DATABASE=Rocketfish RF-FLBTAD Bluetooth Adapter + +usb:v0461p4D81* + ID_MODEL_FROM_DATABASE=Dell N889 Optical Mouse + +usb:v0461p4DE7* + ID_MODEL_FROM_DATABASE=webcam + +usb:v0463* + ID_VENDOR_FROM_DATABASE=MGE UPS Systems + +usb:v0463p0001* + ID_MODEL_FROM_DATABASE=UPS + +usb:v0463pFFFF* + ID_MODEL_FROM_DATABASE=UPS + +usb:v0464* + ID_VENDOR_FROM_DATABASE=AMP/Tycoelectronics Corp. + +usb:v0467* + ID_VENDOR_FROM_DATABASE=AT&T Paradyne + +usb:v0468* + ID_VENDOR_FROM_DATABASE=Wieson Technologies Co., Ltd + +usb:v046A* + ID_VENDOR_FROM_DATABASE=Cherry GmbH + +usb:v046Ap0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v046Ap0003* + ID_MODEL_FROM_DATABASE=My3000 Hub + +usb:v046Ap0004* + ID_MODEL_FROM_DATABASE=CyBoard Keyboard + +usb:v046Ap0005* + ID_MODEL_FROM_DATABASE=XX33 SmartCard Reader Keyboard + +usb:v046Ap0008* + ID_MODEL_FROM_DATABASE=Wireless Keyboard and Mouse + +usb:v046Ap0010* + ID_MODEL_FROM_DATABASE=SmartBoard XX44 + +usb:v046Ap0011* + ID_MODEL_FROM_DATABASE=G83 (RS 6000) Keyboard + +usb:v046Ap0021* + ID_MODEL_FROM_DATABASE=CyMotion Expert Combo + +usb:v046Ap0023* + ID_MODEL_FROM_DATABASE=CyMotion Master Linux Keyboard G230 + +usb:v046Ap0027* + ID_MODEL_FROM_DATABASE=CyMotion Master Solar Keyboard + +usb:v046Ap002A* + ID_MODEL_FROM_DATABASE=Wireless Mouse & Keyboard + +usb:v046Ap002D* + ID_MODEL_FROM_DATABASE=SmartTerminal XX44 + +usb:v046Ap003E* + ID_MODEL_FROM_DATABASE=SmartTerminal ST-2xxx + +usb:v046Ap0041* + ID_MODEL_FROM_DATABASE=G86 6240 Keyboard + +usb:v046Ap0080* + ID_MODEL_FROM_DATABASE=eHealth Terminal ST 1503 + +usb:v046Ap0081* + ID_MODEL_FROM_DATABASE=eHealth Keyboard G87 1504 + +usb:v046Ap0106* + ID_MODEL_FROM_DATABASE=R-300 Wireless Mouse Receiver + +usb:v046Ap010D* + ID_MODEL_FROM_DATABASE=MX-Board 3.0 Keyboard + +usb:v046B* + ID_VENDOR_FROM_DATABASE=American Megatrends, Inc. + +usb:v046Bp0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v046Bp0101* + ID_MODEL_FROM_DATABASE=PS/2 Keyboard, Mouse & Joystick Ports + +usb:v046Bp0301* + ID_MODEL_FROM_DATABASE=USB 1.0 Hub + +usb:v046Bp0500* + ID_MODEL_FROM_DATABASE=Serial & Parallel Ports + +usb:v046BpFF10* + ID_MODEL_FROM_DATABASE=Virtual Keyboard and Mouse + +usb:v046C* + ID_VENDOR_FROM_DATABASE=Toshiba Corp., Digital Media Equipment + +usb:v046D* + ID_VENDOR_FROM_DATABASE=Logitech, Inc. + +usb:v046Dp0082* + ID_MODEL_FROM_DATABASE=Acer Aspire 5672 Webcam + +usb:v046Dp0200* + ID_MODEL_FROM_DATABASE=WingMan Extreme Joystick + +usb:v046Dp0203* + ID_MODEL_FROM_DATABASE=M2452 Keyboard + +usb:v046Dp0301* + ID_MODEL_FROM_DATABASE=M4848 Mouse + +usb:v046Dp0401* + ID_MODEL_FROM_DATABASE=HP PageScan + +usb:v046Dp0402* + ID_MODEL_FROM_DATABASE=NEC PageScan + +usb:v046Dp040F* + ID_MODEL_FROM_DATABASE=Logitech/Storm PageScan + +usb:v046Dp0430* + ID_MODEL_FROM_DATABASE=Mic (Cordless) + +usb:v046Dp0801* + ID_MODEL_FROM_DATABASE=QuickCam Home + +usb:v046Dp0802* + ID_MODEL_FROM_DATABASE=Webcam C200 + +usb:v046Dp0804* + ID_MODEL_FROM_DATABASE=Webcam C250 + +usb:v046Dp0805* + ID_MODEL_FROM_DATABASE=Webcam C300 + +usb:v046Dp0807* + ID_MODEL_FROM_DATABASE=Webcam B500 + +usb:v046Dp0808* + ID_MODEL_FROM_DATABASE=Webcam C600 + +usb:v046Dp0809* + ID_MODEL_FROM_DATABASE=Webcam Pro 9000 + +usb:v046Dp080A* + ID_MODEL_FROM_DATABASE=Portable Webcam C905 + +usb:v046Dp080F* + ID_MODEL_FROM_DATABASE=Webcam C120 + +usb:v046Dp0810* + ID_MODEL_FROM_DATABASE=QuickCam Pro + +usb:v046Dp0819* + ID_MODEL_FROM_DATABASE=Webcam C210 + +usb:v046Dp081B* + ID_MODEL_FROM_DATABASE=Webcam C310 + +usb:v046Dp081D* + ID_MODEL_FROM_DATABASE=HD Webcam C510 + +usb:v046Dp0820* + ID_MODEL_FROM_DATABASE=QuickCam VC + +usb:v046Dp0821* + ID_MODEL_FROM_DATABASE=HD Webcam C910 + +usb:v046Dp0825* + ID_MODEL_FROM_DATABASE=Webcam C270 + +usb:v046Dp0826* + ID_MODEL_FROM_DATABASE=HD Webcam C525 + +usb:v046Dp0828* + ID_MODEL_FROM_DATABASE=HD Webcam B990 + +usb:v046Dp082B* + ID_MODEL_FROM_DATABASE=Webcam C170 + +usb:v046Dp082D* + ID_MODEL_FROM_DATABASE=HD Pro Webcam C920 + +usb:v046Dp0830* + ID_MODEL_FROM_DATABASE=QuickClip + +usb:v046Dp0836* + ID_MODEL_FROM_DATABASE=B525 HD Webcam + +usb:v046Dp0837* + ID_MODEL_FROM_DATABASE=BCC950 ConferenceCam + +usb:v046Dp0840* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp0843* + ID_MODEL_FROM_DATABASE=Webcam C930e + +usb:v046Dp0850* + ID_MODEL_FROM_DATABASE=QuickCam Web + +usb:v046Dp0870* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp0890* + ID_MODEL_FROM_DATABASE=QuickCam Traveler + +usb:v046Dp0892* + ID_MODEL_FROM_DATABASE=OrbiCam + +usb:v046Dp0894* + ID_MODEL_FROM_DATABASE=CrystalCam + +usb:v046Dp0895* + ID_MODEL_FROM_DATABASE=QuickCam for Dell Notebooks + +usb:v046Dp0896* + ID_MODEL_FROM_DATABASE=OrbiCam + +usb:v046Dp0897* + ID_MODEL_FROM_DATABASE=QuickCam for Dell Notebooks + +usb:v046Dp0899* + ID_MODEL_FROM_DATABASE=QuickCam for Dell Notebooks + +usb:v046Dp089D* + ID_MODEL_FROM_DATABASE=QuickCam E2500 series + +usb:v046Dp08A0* + ID_MODEL_FROM_DATABASE=QuickCam IM + +usb:v046Dp08A1* + ID_MODEL_FROM_DATABASE=QuickCam IM with sound + +usb:v046Dp08A2* + ID_MODEL_FROM_DATABASE=Labtec Webcam Pro + +usb:v046Dp08A3* + ID_MODEL_FROM_DATABASE=QuickCam QuickCam Chat + +usb:v046Dp08A6* + ID_MODEL_FROM_DATABASE=QuickCam IM + +usb:v046Dp08A7* + ID_MODEL_FROM_DATABASE=QuickCam Image + +usb:v046Dp08A9* + ID_MODEL_FROM_DATABASE=Notebook Deluxe + +usb:v046Dp08AA* + ID_MODEL_FROM_DATABASE=Labtec Notebooks + +usb:v046Dp08AC* + ID_MODEL_FROM_DATABASE=QuickCam Cool + +usb:v046Dp08AD* + ID_MODEL_FROM_DATABASE=QuickCam Communicate STX + +usb:v046Dp08AE* + ID_MODEL_FROM_DATABASE=QuickCam for Notebooks + +usb:v046Dp08AF* + ID_MODEL_FROM_DATABASE=QuickCam Easy/Cool + +usb:v046Dp08B0* + ID_MODEL_FROM_DATABASE=QuickCam 3000 Pro [pwc] + +usb:v046Dp08B1* + ID_MODEL_FROM_DATABASE=QuickCam Notebook Pro + +usb:v046Dp08B2* + ID_MODEL_FROM_DATABASE=QuickCam Pro 4000 + +usb:v046Dp08B3* + ID_MODEL_FROM_DATABASE=QuickCam Zoom + +usb:v046Dp08B4* + ID_MODEL_FROM_DATABASE=QuickCam Zoom + +usb:v046Dp08B5* + ID_MODEL_FROM_DATABASE=QuickCam Sphere + +usb:v046Dp08B9* + ID_MODEL_FROM_DATABASE=QuickCam IM + +usb:v046Dp08BD* + ID_MODEL_FROM_DATABASE=Microphone (Pro 4000) + +usb:v046Dp08C0* + ID_MODEL_FROM_DATABASE=QuickCam Pro 3000 + +usb:v046Dp08C1* + ID_MODEL_FROM_DATABASE=QuickCam Fusion + +usb:v046Dp08C2* + ID_MODEL_FROM_DATABASE=QuickCam PTZ + +usb:v046Dp08C3* + ID_MODEL_FROM_DATABASE=Camera (Notebooks Pro) + +usb:v046Dp08C5* + ID_MODEL_FROM_DATABASE=QuickCam Pro 5000 + +usb:v046Dp08C6* + ID_MODEL_FROM_DATABASE=QuickCam for DELL Notebooks + +usb:v046Dp08C7* + ID_MODEL_FROM_DATABASE=QuickCam OEM Cisco VT Camera II + +usb:v046Dp08C9* + ID_MODEL_FROM_DATABASE=QuickCam Ultra Vision + +usb:v046Dp08CA* + ID_MODEL_FROM_DATABASE=Mic (Fusion) + +usb:v046Dp08CB* + ID_MODEL_FROM_DATABASE=Mic (Notebooks Pro) + +usb:v046Dp08CC* + ID_MODEL_FROM_DATABASE=Mic (PTZ) + +usb:v046Dp08CE* + ID_MODEL_FROM_DATABASE=QuickCam Pro 5000 + +usb:v046Dp08CF* + ID_MODEL_FROM_DATABASE=QuickCam UpdateMe + +usb:v046Dp08D0* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp08D7* + ID_MODEL_FROM_DATABASE=QuickCam Communicate STX + +usb:v046Dp08D8* + ID_MODEL_FROM_DATABASE=QuickCam for Notebook Deluxe + +usb:v046Dp08D9* + ID_MODEL_FROM_DATABASE=QuickCam IM/Connect + +usb:v046Dp08DA* + ID_MODEL_FROM_DATABASE=QuickCam Messanger + +usb:v046Dp08DD* + ID_MODEL_FROM_DATABASE=QuickCam for Notebooks + +usb:v046Dp08E0* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp08E1* + ID_MODEL_FROM_DATABASE=Labtec Webcam + +usb:v046Dp08F0* + ID_MODEL_FROM_DATABASE=QuickCam Messenger + +usb:v046Dp08F1* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp08F2* + ID_MODEL_FROM_DATABASE=Microphone (Messenger) + +usb:v046Dp08F3* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp08F4* + ID_MODEL_FROM_DATABASE=Labtec Webcam + +usb:v046Dp08F5* + ID_MODEL_FROM_DATABASE=QuickCam Messenger Communicate + +usb:v046Dp08F6* + ID_MODEL_FROM_DATABASE=QuickCam Messenger Plus + +usb:v046Dp0900* + ID_MODEL_FROM_DATABASE=ClickSmart 310 + +usb:v046Dp0901* + ID_MODEL_FROM_DATABASE=ClickSmart 510 + +usb:v046Dp0903* + ID_MODEL_FROM_DATABASE=ClickSmart 820 + +usb:v046Dp0905* + ID_MODEL_FROM_DATABASE=ClickSmart 820 + +usb:v046Dp0910* + ID_MODEL_FROM_DATABASE=QuickCam Cordless + +usb:v046Dp0920* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp0921* + ID_MODEL_FROM_DATABASE=Labtec Webcam + +usb:v046Dp0922* + ID_MODEL_FROM_DATABASE=QuickCam Live + +usb:v046Dp0928* + ID_MODEL_FROM_DATABASE=QuickCam Express + +usb:v046Dp0929* + ID_MODEL_FROM_DATABASE=Labtec Webcam Pro + +usb:v046Dp092A* + ID_MODEL_FROM_DATABASE=QuickCam for Notebooks + +usb:v046Dp092B* + ID_MODEL_FROM_DATABASE=Labtec Webcam Plus + +usb:v046Dp092C* + ID_MODEL_FROM_DATABASE=QuickCam Chat + +usb:v046Dp092D* + ID_MODEL_FROM_DATABASE=QuickCam Express / Go + +usb:v046Dp092E* + ID_MODEL_FROM_DATABASE=QuickCam Chat + +usb:v046Dp092F* + ID_MODEL_FROM_DATABASE=QuickCam Express Plus + +usb:v046Dp0950* + ID_MODEL_FROM_DATABASE=Pocket Camera + +usb:v046Dp0960* + ID_MODEL_FROM_DATABASE=ClickSmart 420 + +usb:v046Dp0970* + ID_MODEL_FROM_DATABASE=Pocket750 + +usb:v046Dp0990* + ID_MODEL_FROM_DATABASE=QuickCam Pro 9000 + +usb:v046Dp0991* + ID_MODEL_FROM_DATABASE=QuickCam Pro for Notebooks + +usb:v046Dp0992* + ID_MODEL_FROM_DATABASE=QuickCam Communicate Deluxe + +usb:v046Dp0994* + ID_MODEL_FROM_DATABASE=QuickCam Orbit/Sphere AF + +usb:v046Dp09A1* + ID_MODEL_FROM_DATABASE=QuickCam Communicate MP/S5500 + +usb:v046Dp09A2* + ID_MODEL_FROM_DATABASE=QuickCam Communicate Deluxe/S7500 + +usb:v046Dp09A4* + ID_MODEL_FROM_DATABASE=QuickCam E 3500 + +usb:v046Dp09A5* + ID_MODEL_FROM_DATABASE=Quickcam 3000 For Business + +usb:v046Dp09A6* + ID_MODEL_FROM_DATABASE=QuickCam Vision Pro + +usb:v046Dp09B0* + ID_MODEL_FROM_DATABASE=Acer OrbiCam + +usb:v046Dp09B2* + ID_MODEL_FROM_DATABASE=Fujitsu Webcam + +usb:v046Dp09C0* + ID_MODEL_FROM_DATABASE=QuickCam for Dell Notebooks Mic + +usb:v046Dp09C1* + ID_MODEL_FROM_DATABASE=QuickCam Deluxe for Notebooks + +usb:v046Dp0A01* + ID_MODEL_FROM_DATABASE=USB Headset + +usb:v046Dp0A02* + ID_MODEL_FROM_DATABASE=Premium Stereo USB Headset 350 + +usb:v046Dp0A03* + ID_MODEL_FROM_DATABASE=Logitech USB Microphone + +usb:v046Dp0A04* + ID_MODEL_FROM_DATABASE=V20 portable speakers (USB powered) + +usb:v046Dp0A07* + ID_MODEL_FROM_DATABASE=Z-10 Speakers + +usb:v046Dp0A0B* + ID_MODEL_FROM_DATABASE=ClearChat Pro USB + +usb:v046Dp0A0C* + ID_MODEL_FROM_DATABASE=Clear Chat Comfort USB Headset + +usb:v046Dp0A13* + ID_MODEL_FROM_DATABASE=Z-5 Speakers + +usb:v046Dp0A14* + ID_MODEL_FROM_DATABASE=USB Headset + +usb:v046Dp0A15* + ID_MODEL_FROM_DATABASE=G35 Headset + +usb:v046Dp0A17* + ID_MODEL_FROM_DATABASE=G330 Headset + +usb:v046Dp0A1F* + ID_MODEL_FROM_DATABASE=G930 + +usb:v046Dp0A29* + ID_MODEL_FROM_DATABASE=H600 [Wireless Headset] + +usb:v046Dp0A37* + ID_MODEL_FROM_DATABASE=USB Headset H540 + +usb:v046Dp0A38* + ID_MODEL_FROM_DATABASE=Headset H340 + +usb:v046Dp0A44* + ID_MODEL_FROM_DATABASE=Headset H390 + +usb:v046Dp0A4D* + ID_MODEL_FROM_DATABASE=G430 Surround Sound Gaming Headset + +usb:v046Dp0B02* + ID_MODEL_FROM_DATABASE=C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode) + +usb:v046Dp8801* + ID_MODEL_FROM_DATABASE=Video Camera + +usb:v046DpB305* + ID_MODEL_FROM_DATABASE=BT Mini-Receiver + +usb:v046DpBFE4* + ID_MODEL_FROM_DATABASE=Premium Optical Wheel Mouse + +usb:v046DpC000* + ID_MODEL_FROM_DATABASE=N43 [Pilot Mouse] + +usb:v046DpC001* + ID_MODEL_FROM_DATABASE=N48/M-BB48/M-UK96A [FirstMouse Plus] + +usb:v046DpC002* + ID_MODEL_FROM_DATABASE=M-BA47 [MouseMan Plus] + +usb:v046DpC003* + ID_MODEL_FROM_DATABASE=MouseMan + +usb:v046DpC004* + ID_MODEL_FROM_DATABASE=WingMan Gaming Mouse + +usb:v046DpC005* + ID_MODEL_FROM_DATABASE=WingMan Gaming Wheel Mouse + +usb:v046DpC00B* + ID_MODEL_FROM_DATABASE=MouseMan Wheel + +usb:v046DpC00C* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse + +usb:v046DpC00D* + ID_MODEL_FROM_DATABASE=MouseMan Wheel+ + +usb:v046DpC00E* + ID_MODEL_FROM_DATABASE=M-BJ58/M-BJ69 Optical Wheel Mouse + +usb:v046DpC00F* + ID_MODEL_FROM_DATABASE=MouseMan Traveler/Mobile + +usb:v046DpC011* + ID_MODEL_FROM_DATABASE=Optical MouseMan + +usb:v046DpC012* + ID_MODEL_FROM_DATABASE=Mouseman Dual Optical + +usb:v046DpC014* + ID_MODEL_FROM_DATABASE=Corded Workstation Mouse + +usb:v046DpC015* + ID_MODEL_FROM_DATABASE=Corded Workstation Mouse + +usb:v046DpC016* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse + +usb:v046DpC018* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse + +usb:v046DpC019* + ID_MODEL_FROM_DATABASE=Optical Tilt Wheel Mouse + +usb:v046DpC01A* + ID_MODEL_FROM_DATABASE=M-BQ85 Optical Wheel Mouse + +usb:v046DpC01B* + ID_MODEL_FROM_DATABASE=MX310 Optical Mouse + +usb:v046DpC01C* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v046DpC01D* + ID_MODEL_FROM_DATABASE=MX510 Optical Mouse + +usb:v046DpC01E* + ID_MODEL_FROM_DATABASE=MX518 Optical Mouse + +usb:v046DpC024* + ID_MODEL_FROM_DATABASE=MX300 Optical Mouse + +usb:v046DpC025* + ID_MODEL_FROM_DATABASE=MX500 Optical Mouse + +usb:v046DpC030* + ID_MODEL_FROM_DATABASE=iFeel Mouse + +usb:v046DpC031* + ID_MODEL_FROM_DATABASE=iFeel Mouse+ + +usb:v046DpC032* + ID_MODEL_FROM_DATABASE=MouseMan iFeel + +usb:v046DpC033* + ID_MODEL_FROM_DATABASE=iFeel MouseMan+ + +usb:v046DpC034* + ID_MODEL_FROM_DATABASE=MouseMan Optical + +usb:v046DpC035* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v046DpC036* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v046DpC037* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v046DpC038* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v046DpC03D* + ID_MODEL_FROM_DATABASE=M-BT96a Pilot Optical Mouse + +usb:v046DpC03E* + ID_MODEL_FROM_DATABASE=Premium Optical Wheel Mouse (M-BT58) + +usb:v046DpC03F* + ID_MODEL_FROM_DATABASE=M-BT85 [UltraX Optical Mouse] + +usb:v046DpC040* + ID_MODEL_FROM_DATABASE=Corded Tilt-Wheel Mouse + +usb:v046DpC041* + ID_MODEL_FROM_DATABASE=G5 Laser Mouse + +usb:v046DpC042* + ID_MODEL_FROM_DATABASE=G3 Laser Mouse + +usb:v046DpC043* + ID_MODEL_FROM_DATABASE=MX320/MX400 Laser Mouse + +usb:v046DpC044* + ID_MODEL_FROM_DATABASE=LX3 Optical Mouse + +usb:v046DpC045* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v046DpC046* + ID_MODEL_FROM_DATABASE=RX1000 Laser Mouse + +usb:v046DpC047* + ID_MODEL_FROM_DATABASE=Laser Mouse M-UAL120 + +usb:v046DpC048* + ID_MODEL_FROM_DATABASE=G9 Laser Mouse + +usb:v046DpC049* + ID_MODEL_FROM_DATABASE=G5 Laser Mouse + +usb:v046DpC050* + ID_MODEL_FROM_DATABASE=RX 250 Optical Mouse + +usb:v046DpC051* + ID_MODEL_FROM_DATABASE=G3 (MX518) Optical Mouse + +usb:v046DpC053* + ID_MODEL_FROM_DATABASE=Laser Mouse + +usb:v046DpC054* + ID_MODEL_FROM_DATABASE=Bluetooth mini-receiver + +usb:v046DpC058* + ID_MODEL_FROM_DATABASE=M115 Mouse + +usb:v046DpC05A* + ID_MODEL_FROM_DATABASE=M90/M100 Optical Mouse + +usb:v046DpC05B* + ID_MODEL_FROM_DATABASE=M-U0004 810-001317 [B110 Optical USB Mouse] + +usb:v046DpC05D* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v046DpC05F* + ID_MODEL_FROM_DATABASE=M115 Optical Mouse + +usb:v046DpC061* + ID_MODEL_FROM_DATABASE=RX1500 Laser Mouse + +usb:v046DpC062* + ID_MODEL_FROM_DATABASE=M-UAS144 [LS1 Laser Mouse] + +usb:v046DpC063* + ID_MODEL_FROM_DATABASE=DELL Laser Mouse + +usb:v046DpC064* + ID_MODEL_FROM_DATABASE=M110 corded optical mouse (M-B0001) + +usb:v046DpC066* + ID_MODEL_FROM_DATABASE=G9x Laser Mouse + +usb:v046DpC068* + ID_MODEL_FROM_DATABASE=G500 Laser Mouse + +usb:v046DpC069* + ID_MODEL_FROM_DATABASE=M-U0007 [Corded Mouse M500] + +usb:v046DpC06A* + ID_MODEL_FROM_DATABASE=USB Optical Mouse + +usb:v046DpC06B* + ID_MODEL_FROM_DATABASE=G700 Wireless Gaming Mouse + +usb:v046DpC06C* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v046DpC077* + ID_MODEL_FROM_DATABASE=M105 Optical Mouse + +usb:v046DpC07C* + ID_MODEL_FROM_DATABASE=M-R0017 [G700s Rechargeable Gaming Mouse] + +usb:v046DpC07D* + ID_MODEL_FROM_DATABASE=G502 Mouse + +usb:v046DpC07E* + ID_MODEL_FROM_DATABASE=G402 Gaming Mouse + +usb:v046DpC101* + ID_MODEL_FROM_DATABASE=UltraX Media Remote + +usb:v046DpC110* + ID_MODEL_FROM_DATABASE=Harmony 785/880/885 Remote + +usb:v046DpC111* + ID_MODEL_FROM_DATABASE=Harmony 525 Remote + +usb:v046DpC112* + ID_MODEL_FROM_DATABASE=Harmony 890 Remote + +usb:v046DpC11F* + ID_MODEL_FROM_DATABASE=Harmony 900/1100 Remote + +usb:v046DpC121* + ID_MODEL_FROM_DATABASE=Harmony One Remote + +usb:v046DpC122* + ID_MODEL_FROM_DATABASE=Harmony 650/700 Remote + +usb:v046DpC124* + ID_MODEL_FROM_DATABASE=Harmony 300/350 Remote + +usb:v046DpC125* + ID_MODEL_FROM_DATABASE=Harmony 200 Remote + +usb:v046DpC126* + ID_MODEL_FROM_DATABASE=Harmony Link + +usb:v046DpC129* + ID_MODEL_FROM_DATABASE=Harmony Hub + +usb:v046DpC12B* + ID_MODEL_FROM_DATABASE=Harmony Touch/Ultimate Remote + +usb:v046DpC201* + ID_MODEL_FROM_DATABASE=WingMan Extreme Joystick with Throttle + +usb:v046DpC202* + ID_MODEL_FROM_DATABASE=WingMan Formula + +usb:v046DpC207* + ID_MODEL_FROM_DATABASE=WingMan Extreme Digital 3D + +usb:v046DpC208* + ID_MODEL_FROM_DATABASE=WingMan Gamepad Extreme + +usb:v046DpC209* + ID_MODEL_FROM_DATABASE=WingMan Gamepad + +usb:v046DpC20A* + ID_MODEL_FROM_DATABASE=WingMan RumblePad + +usb:v046DpC20B* + ID_MODEL_FROM_DATABASE=WingMan Action Pad + +usb:v046DpC20C* + ID_MODEL_FROM_DATABASE=WingMan Precision + +usb:v046DpC20D* + ID_MODEL_FROM_DATABASE=WingMan Attack 2 + +usb:v046DpC20E* + ID_MODEL_FROM_DATABASE=WingMan Formula GP + +usb:v046DpC211* + ID_MODEL_FROM_DATABASE=iTouch Cordless Reciever + +usb:v046DpC212* + ID_MODEL_FROM_DATABASE=WingMan Extreme Digital 3D + +usb:v046DpC213* + ID_MODEL_FROM_DATABASE=J-UH16 (Freedom 2.4 Cordless Joystick) + +usb:v046DpC214* + ID_MODEL_FROM_DATABASE=ATK3 (Attack III Joystick) + +usb:v046DpC215* + ID_MODEL_FROM_DATABASE=Extreme 3D Pro + +usb:v046DpC216* + ID_MODEL_FROM_DATABASE=Dual Action Gamepad + +usb:v046DpC218* + ID_MODEL_FROM_DATABASE=Logitech RumblePad 2 USB + +usb:v046DpC219* + ID_MODEL_FROM_DATABASE=Cordless RumblePad 2 + +usb:v046DpC21A* + ID_MODEL_FROM_DATABASE=Precision Gamepad + +usb:v046DpC21C* + ID_MODEL_FROM_DATABASE=G13 Advanced Gameboard + +usb:v046DpC21D* + ID_MODEL_FROM_DATABASE=F310 Gamepad [XInput Mode] + +usb:v046DpC21E* + ID_MODEL_FROM_DATABASE=F510 Gamepad [XInput Mode] + +usb:v046DpC21F* + ID_MODEL_FROM_DATABASE=F710 Wireless Gamepad [XInput Mode] + +usb:v046DpC221* + ID_MODEL_FROM_DATABASE=G11/G15 Keyboard / Keyboard + +usb:v046DpC222* + ID_MODEL_FROM_DATABASE=G15 Keyboard / LCD + +usb:v046DpC223* + ID_MODEL_FROM_DATABASE=G11/G15 Keyboard / USB Hub + +usb:v046DpC225* + ID_MODEL_FROM_DATABASE=G11/G15 Keyboard / G keys + +usb:v046DpC226* + ID_MODEL_FROM_DATABASE=G15 Refresh Keyboard + +usb:v046DpC227* + ID_MODEL_FROM_DATABASE=G15 Refresh Keyboard + +usb:v046DpC228* + ID_MODEL_FROM_DATABASE=G19 Gaming Keyboard + +usb:v046DpC229* + ID_MODEL_FROM_DATABASE=G19 Gaming Keyboard Macro Interface + +usb:v046DpC22A* + ID_MODEL_FROM_DATABASE=Gaming Keyboard G110 + +usb:v046DpC22B* + ID_MODEL_FROM_DATABASE=Gaming Keyboard G110 G-keys + +usb:v046DpC22D* + ID_MODEL_FROM_DATABASE=G510 Gaming Keyboard + +usb:v046DpC22E* + ID_MODEL_FROM_DATABASE=G510 Gaming Keyboard onboard audio + +usb:v046DpC245* + ID_MODEL_FROM_DATABASE=G400 Optical Mouse + +usb:v046DpC246* + ID_MODEL_FROM_DATABASE=Gaming Mouse G300 + +usb:v046DpC248* + ID_MODEL_FROM_DATABASE=G105 Gaming Keyboard + +usb:v046DpC24A* + ID_MODEL_FROM_DATABASE=G600 Gaming Mouse + +usb:v046DpC24C* + ID_MODEL_FROM_DATABASE=G400s Optical Mouse + +usb:v046DpC24D* + ID_MODEL_FROM_DATABASE=G710 Gaming Keyboard + +usb:v046DpC24E* + ID_MODEL_FROM_DATABASE=G500s Laser Gaming Mouse + +usb:v046DpC281* + ID_MODEL_FROM_DATABASE=WingMan Force + +usb:v046DpC283* + ID_MODEL_FROM_DATABASE=WingMan Force 3D + +usb:v046DpC285* + ID_MODEL_FROM_DATABASE=WingMan Strike Force 3D + +usb:v046DpC286* + ID_MODEL_FROM_DATABASE=Force 3D Pro + +usb:v046DpC287* + ID_MODEL_FROM_DATABASE=Flight System G940 + +usb:v046DpC291* + ID_MODEL_FROM_DATABASE=WingMan Formula Force + +usb:v046DpC293* + ID_MODEL_FROM_DATABASE=WingMan Formula Force GP + +usb:v046DpC294* + ID_MODEL_FROM_DATABASE=Driving Force + +usb:v046DpC295* + ID_MODEL_FROM_DATABASE=Momo Force Steering Wheel + +usb:v046DpC298* + ID_MODEL_FROM_DATABASE=Driving Force Pro + +usb:v046DpC299* + ID_MODEL_FROM_DATABASE=G25 Racing Wheel + +usb:v046DpC29B* + ID_MODEL_FROM_DATABASE=G27 Racing Wheel + +usb:v046DpC29C* + ID_MODEL_FROM_DATABASE=Speed Force Wireless Wheel for Wii + +usb:v046DpC2A0* + ID_MODEL_FROM_DATABASE=Wingman Force Feedback Mouse + +usb:v046DpC2A1* + ID_MODEL_FROM_DATABASE=WingMan Force Feedback Mouse + +usb:v046DpC301* + ID_MODEL_FROM_DATABASE=iTouch Keyboard + +usb:v046DpC302* + ID_MODEL_FROM_DATABASE=iTouch Pro Keyboard + +usb:v046DpC303* + ID_MODEL_FROM_DATABASE=iTouch Keyboard + +usb:v046DpC305* + ID_MODEL_FROM_DATABASE=Internet Keyboard + +usb:v046DpC307* + ID_MODEL_FROM_DATABASE=Internet Keyboard + +usb:v046DpC308* + ID_MODEL_FROM_DATABASE=Internet Navigator Keyboard + +usb:v046DpC309* + ID_MODEL_FROM_DATABASE=Y-BF37 [Internet Navigator Keyboard] + +usb:v046DpC30A* + ID_MODEL_FROM_DATABASE=iTouch Composite + +usb:v046DpC30B* + ID_MODEL_FROM_DATABASE=NetPlay Keyboard + +usb:v046DpC30C* + ID_MODEL_FROM_DATABASE=Internet Keys (X) + +usb:v046DpC30D* + ID_MODEL_FROM_DATABASE=Internet Keys + +usb:v046DpC30E* + ID_MODEL_FROM_DATABASE=UltraX Keyboard (Y-BL49) + +usb:v046DpC30F* + ID_MODEL_FROM_DATABASE=Logicool HID-Compliant Keyboard (106 key) + +usb:v046DpC311* + ID_MODEL_FROM_DATABASE=Y-UF49 [Internet Pro Keyboard] + +usb:v046DpC312* + ID_MODEL_FROM_DATABASE=DeLuxe 250 Keyboard + +usb:v046DpC313* + ID_MODEL_FROM_DATABASE=Internet 350 Keyboard + +usb:v046DpC315* + ID_MODEL_FROM_DATABASE=Classic Keyboard 200 + +usb:v046DpC316* + ID_MODEL_FROM_DATABASE=HID-Compliant Keyboard + +usb:v046DpC317* + ID_MODEL_FROM_DATABASE=Wave Corded Keyboard + +usb:v046DpC318* + ID_MODEL_FROM_DATABASE=Illuminated Keyboard + +usb:v046DpC31A* + ID_MODEL_FROM_DATABASE=Comfort Wave 450 + +usb:v046DpC31B* + ID_MODEL_FROM_DATABASE=Compact Keyboard K300 + +usb:v046DpC31C* + ID_MODEL_FROM_DATABASE=Keyboard K120 + +usb:v046DpC31D* + ID_MODEL_FROM_DATABASE=Media Keyboard K200 + +usb:v046DpC401* + ID_MODEL_FROM_DATABASE=TrackMan Marble Wheel + +usb:v046DpC402* + ID_MODEL_FROM_DATABASE=Marble Mouse (2-button) + +usb:v046DpC403* + ID_MODEL_FROM_DATABASE=Turbo TrackMan Marble FX + +usb:v046DpC404* + ID_MODEL_FROM_DATABASE=TrackMan Wheel + +usb:v046DpC408* + ID_MODEL_FROM_DATABASE=Marble Mouse (4-button) + +usb:v046DpC501* + ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver + +usb:v046DpC502* + ID_MODEL_FROM_DATABASE=Cordless Mouse & iTouch Keys + +usb:v046DpC503* + ID_MODEL_FROM_DATABASE=Cordless Mouse+Keyboard Receiver + +usb:v046DpC504* + ID_MODEL_FROM_DATABASE=Cordless Mouse+Keyboard Receiver + +usb:v046DpC505* + ID_MODEL_FROM_DATABASE=Cordless Mouse+Keyboard Receiver + +usb:v046DpC506* + ID_MODEL_FROM_DATABASE=MX700 Cordless Mouse Receiver + +usb:v046DpC508* + ID_MODEL_FROM_DATABASE=Cordless Trackball + +usb:v046DpC509* + ID_MODEL_FROM_DATABASE=Cordless Keyboard & Mouse + +usb:v046DpC50A* + ID_MODEL_FROM_DATABASE=Cordless Mouse + +usb:v046DpC50B* + ID_MODEL_FROM_DATABASE=Cordless Desktop Optical + +usb:v046DpC50C* + ID_MODEL_FROM_DATABASE=Cordless Desktop S510 + +usb:v046DpC50D* + ID_MODEL_FROM_DATABASE=Cordless Mouse + +usb:v046DpC50E* + ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver + +usb:v046DpC510* + ID_MODEL_FROM_DATABASE=Cordless Mouse + +usb:v046DpC512* + ID_MODEL_FROM_DATABASE=LX-700 Cordless Desktop Receiver + +usb:v046DpC513* + ID_MODEL_FROM_DATABASE=MX3000 Cordless Desktop Receiver + +usb:v046DpC514* + ID_MODEL_FROM_DATABASE=Cordless Mouse + +usb:v046DpC515* + ID_MODEL_FROM_DATABASE=Cordless 2.4 GHz Presenter Presentation remote control + +usb:v046DpC517* + ID_MODEL_FROM_DATABASE=LX710 Cordless Desktop Laser + +usb:v046DpC518* + ID_MODEL_FROM_DATABASE=MX610 Laser Cordless Mouse + +usb:v046DpC51A* + ID_MODEL_FROM_DATABASE=MX Revolution/G7 Cordless Mouse + +usb:v046DpC51B* + ID_MODEL_FROM_DATABASE=V220 Cordless Optical Mouse for Notebooks + +usb:v046DpC521* + ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver + +usb:v046DpC525* + ID_MODEL_FROM_DATABASE=MX Revolution Cordless Mouse + +usb:v046DpC526* + ID_MODEL_FROM_DATABASE=Nano Receiver + +usb:v046DpC529* + ID_MODEL_FROM_DATABASE=Logitech Keyboard + Mice + +usb:v046DpC52B* + ID_MODEL_FROM_DATABASE=Unifying Receiver + +usb:v046DpC52D* + ID_MODEL_FROM_DATABASE=R700 Remote Presenter receiver + +usb:v046DpC52E* + ID_MODEL_FROM_DATABASE=MK260 Wireless Combo Receiver + +usb:v046DpC52F* + ID_MODEL_FROM_DATABASE=Unifying Receiver + +usb:v046DpC531* + ID_MODEL_FROM_DATABASE=C-U0007 [Unifying Receiver] + +usb:v046DpC532* + ID_MODEL_FROM_DATABASE=Unifying Receiver + +usb:v046DpC534* + ID_MODEL_FROM_DATABASE=Unifying Receiver + +usb:v046DpC603* + ID_MODEL_FROM_DATABASE=3Dconnexion Spacemouse Plus XT + +usb:v046DpC605* + ID_MODEL_FROM_DATABASE=3Dconnexion CADman + +usb:v046DpC606* + ID_MODEL_FROM_DATABASE=3Dconnexion Spacemouse Classic + +usb:v046DpC621* + ID_MODEL_FROM_DATABASE=3Dconnexion Spaceball 5000 + +usb:v046DpC623* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Traveller 3D Mouse + +usb:v046DpC625* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Pilot 3D Mouse + +usb:v046DpC626* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Navigator 3D Mouse + +usb:v046DpC627* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Explorer 3D Mouse + +usb:v046DpC628* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Navigator for Notebooks + +usb:v046DpC629* + ID_MODEL_FROM_DATABASE=3Dconnexion SpacePilot Pro 3D Mouse + +usb:v046DpC62B* + ID_MODEL_FROM_DATABASE=3Dconnexion Space Mouse Pro + +usb:v046DpC640* + ID_MODEL_FROM_DATABASE=NuLOOQ navigator + +usb:v046DpC702* + ID_MODEL_FROM_DATABASE=Cordless Presenter + +usb:v046DpC703* + ID_MODEL_FROM_DATABASE=Elite Keyboard Y-RP20 + Mouse MX900 (Bluetooth) + +usb:v046DpC704* + ID_MODEL_FROM_DATABASE=diNovo Wireless Desktop + +usb:v046DpC705* + ID_MODEL_FROM_DATABASE=MX900 Bluetooth Wireless Hub (C-UJ16A) + +usb:v046DpC707* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC708* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC709* + ID_MODEL_FROM_DATABASE=BT Mini-Receiver (HCI mode) + +usb:v046DpC70A* + ID_MODEL_FROM_DATABASE=MX5000 Cordless Desktop + +usb:v046DpC70B* + ID_MODEL_FROM_DATABASE=BT Mini-Receiver (HID proxy mode) + +usb:v046DpC70C* + ID_MODEL_FROM_DATABASE=BT Mini-Receiver (HID proxy mode) + +usb:v046DpC70D* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC70E* + ID_MODEL_FROM_DATABASE=MX1000 Bluetooth Laser Mouse + +usb:v046DpC70F* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC712* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC714* + ID_MODEL_FROM_DATABASE=diNovo Edge Keyboard + +usb:v046DpC715* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC71A* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC71D* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpC71F* + ID_MODEL_FROM_DATABASE=diNovo Mini Wireless Keyboard + +usb:v046DpC720* + ID_MODEL_FROM_DATABASE=Bluetooth wireless hub + +usb:v046DpCA03* + ID_MODEL_FROM_DATABASE=MOMO Racing + +usb:v046DpCA04* + ID_MODEL_FROM_DATABASE=Formula Vibration Feedback Wheel + +usb:v046DpCAB1* + ID_MODEL_FROM_DATABASE=Cordless Keyboard for Wii HID Receiver + +usb:v046DpD001* + ID_MODEL_FROM_DATABASE=QuickCam Pro + +usb:v046E* + ID_VENDOR_FROM_DATABASE=Behavior Tech. Computer Corp. + +usb:v046Ep0100* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v046Ep3001* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v046Ep3002* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v046Ep3003* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v046Ep3005* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v046Ep3008* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v046Ep5250* + ID_MODEL_FROM_DATABASE=KeyMaestro Multimedia Keyboard + +usb:v046Ep5273* + ID_MODEL_FROM_DATABASE=KeyMaestro Multimedia Keyboard + +usb:v046Ep52E6* + ID_MODEL_FROM_DATABASE=Cordless Mouse + +usb:v046Ep5308* + ID_MODEL_FROM_DATABASE=KeyMaestro Keyboard + +usb:v046Ep5408* + ID_MODEL_FROM_DATABASE=KeyMaestro Multimedia Keyboard/Hub + +usb:v046Ep5500* + ID_MODEL_FROM_DATABASE=Portable Keyboard 86+9 keys (Model 6100C US) + +usb:v046Ep5550* + ID_MODEL_FROM_DATABASE=5 button optical mouse model M873U + +usb:v046Ep5720* + ID_MODEL_FROM_DATABASE=Smart Card Reader + +usb:v046Ep6782* + ID_MODEL_FROM_DATABASE=BTC 7932 mouse+keyboard + +usb:v046F* + ID_VENDOR_FROM_DATABASE=Crystal Semiconductor + +usb:v0471* + ID_VENDOR_FROM_DATABASE=Philips (or NXP) + +usb:v0471p0101* + ID_MODEL_FROM_DATABASE=DSS350 Digital Speaker System + +usb:v0471p0104* + ID_MODEL_FROM_DATABASE=DSS330 Digital Speaker System [uda1321] + +usb:v0471p0105* + ID_MODEL_FROM_DATABASE=UDA1321 + +usb:v0471p014F* + ID_MODEL_FROM_DATABASE=GoGear SA9200 + +usb:v0471p0160* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0471p0161* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0471p0163* + ID_MODEL_FROM_DATABASE=GoGear SA1100 + +usb:v0471p0164* + ID_MODEL_FROM_DATABASE=GoGear SA1110/02 + +usb:v0471p0165* + ID_MODEL_FROM_DATABASE=GoGear SA1330 + +usb:v0471p0201* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0471p0222* + ID_MODEL_FROM_DATABASE=Creative Nomad Jukebox + +usb:v0471p0302* + ID_MODEL_FROM_DATABASE=PCA645VC Webcam [pwc] + +usb:v0471p0303* + ID_MODEL_FROM_DATABASE=PCA646VC Webcam [pwc] + +usb:v0471p0304* + ID_MODEL_FROM_DATABASE=Askey VC010 Webcam [pwc] + +usb:v0471p0307* + ID_MODEL_FROM_DATABASE=PCVC675K Webcam [pwc] + +usb:v0471p0308* + ID_MODEL_FROM_DATABASE=PCVC680K Webcam [pwc] + +usb:v0471p030B* + ID_MODEL_FROM_DATABASE=PC VGA Camera (Vesta Fun) + +usb:v0471p030C* + ID_MODEL_FROM_DATABASE=PCVC690K Webcam [pwc] + +usb:v0471p0310* + ID_MODEL_FROM_DATABASE=PCVC730K Webcam [pwc] + +usb:v0471p0311* + ID_MODEL_FROM_DATABASE=PCVC740K ToUcam Pro [pwc] + +usb:v0471p0312* + ID_MODEL_FROM_DATABASE=PCVC750K Webcam [pwc] + +usb:v0471p0314* + ID_MODEL_FROM_DATABASE=DMVC 1000K + +usb:v0471p0316* + ID_MODEL_FROM_DATABASE=DMVC 2000K Video Capture + +usb:v0471p0321* + ID_MODEL_FROM_DATABASE=FunCam + +usb:v0471p0322* + ID_MODEL_FROM_DATABASE=DMVC1300K PC Camera + +usb:v0471p0325* + ID_MODEL_FROM_DATABASE=SPC 200NC PC Camera + +usb:v0471p0326* + ID_MODEL_FROM_DATABASE=SPC 300NC PC Camera + +usb:v0471p0327* + ID_MODEL_FROM_DATABASE=Webcam SPC 6000 NC (Webcam w/ mic) + +usb:v0471p0328* + ID_MODEL_FROM_DATABASE=SPC 700NC PC Camera + +usb:v0471p0329* + ID_MODEL_FROM_DATABASE=SPC 900NC PC Camera / ORITE CCD Webcam(PC370R) + +usb:v0471p032D* + ID_MODEL_FROM_DATABASE=SPC 210NC PC Camera + +usb:v0471p032E* + ID_MODEL_FROM_DATABASE=SPC 315NC PC Camera + +usb:v0471p0330* + ID_MODEL_FROM_DATABASE=SPC 710NC PC Camera + +usb:v0471p0331* + ID_MODEL_FROM_DATABASE=SPC 1300NC PC Camera + +usb:v0471p0332* + ID_MODEL_FROM_DATABASE=SPC 1000NC PC Camera + +usb:v0471p0333* + ID_MODEL_FROM_DATABASE=SPC 620NC PC Camera + +usb:v0471p0334* + ID_MODEL_FROM_DATABASE=SPC 520/525NC PC Camera + +usb:v0471p0401* + ID_MODEL_FROM_DATABASE=Semiconductors CICT Keyboard + +usb:v0471p0402* + ID_MODEL_FROM_DATABASE=PS/2 Mouse on Semiconductors CICT Keyboard + +usb:v0471p0406* + ID_MODEL_FROM_DATABASE=15 inch Detachable Monitor + +usb:v0471p0407* + ID_MODEL_FROM_DATABASE=10 inch Mobile Monitor + +usb:v0471p0408* + ID_MODEL_FROM_DATABASE=SG3WA1/74 802.11b WLAN Adapter [Atmel AT76C503A] + +usb:v0471p0471* + ID_MODEL_FROM_DATABASE=Digital Speaker System + +usb:v0471p0601* + ID_MODEL_FROM_DATABASE=OVU1020 IR Dongle (Kbd+Mouse) + +usb:v0471p0602* + ID_MODEL_FROM_DATABASE=ATI Remote Wonder II Input Device + +usb:v0471p0603* + ID_MODEL_FROM_DATABASE=ATI Remote Wonder II Controller + +usb:v0471p0608* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0471p060A* + ID_MODEL_FROM_DATABASE=TSU9600 Remote Control + +usb:v0471p060C* + ID_MODEL_FROM_DATABASE=Consumer Infrared Transceiver (HP) + +usb:v0471p060D* + ID_MODEL_FROM_DATABASE=Consumer Infrared Transceiver (SRM5100) + +usb:v0471p060E* + ID_MODEL_FROM_DATABASE=RF Dongle + +usb:v0471p060F* + ID_MODEL_FROM_DATABASE=Consumer Infrared Transceiver + +usb:v0471p0613* + ID_MODEL_FROM_DATABASE=Infrared Transceiver + +usb:v0471p0617* + ID_MODEL_FROM_DATABASE=IEEE802.15.4 RF Dongle + +usb:v0471p0619* + ID_MODEL_FROM_DATABASE=TSU9400 Remote Control + +usb:v0471p0666* + ID_MODEL_FROM_DATABASE=Hantek DDS-3005 Arbitrary Waveform Generator + +usb:v0471p0700* + ID_MODEL_FROM_DATABASE=Semiconductors CICT Hub + +usb:v0471p0701* + ID_MODEL_FROM_DATABASE=150P1 TFT Display + +usb:v0471p0809* + ID_MODEL_FROM_DATABASE=AVNET Bluetooth Device + +usb:v0471p0811* + ID_MODEL_FROM_DATABASE=JR24 CDRW + +usb:v0471p0814* + ID_MODEL_FROM_DATABASE=DCCX38/P data cable + +usb:v0471p0815* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0471p0844* + ID_MODEL_FROM_DATABASE=SA2111/02 1GB Flash Audio Player + +usb:v0471p084A* + ID_MODEL_FROM_DATABASE=GoGear SA3125 + +usb:v0471p084E* + ID_MODEL_FROM_DATABASE=GoGear SA60xx (mtp) + +usb:v0471p0888* + ID_MODEL_FROM_DATABASE=Hantek DDS-3005 Arbitrary Waveform Generator + +usb:v0471p1103* + ID_MODEL_FROM_DATABASE=Digital Speaker System + +usb:v0471p1120* + ID_MODEL_FROM_DATABASE=Creative Rhomba MP3 player + +usb:v0471p1125* + ID_MODEL_FROM_DATABASE=Nike psa[128max Player + +usb:v0471p1137* + ID_MODEL_FROM_DATABASE=HDD065 MP3 player + +usb:v0471p1201* + ID_MODEL_FROM_DATABASE=Arima Bluetooth Device + +usb:v0471p1230* + ID_MODEL_FROM_DATABASE=Wireless Adapter 11g + +usb:v0471p1232* + ID_MODEL_FROM_DATABASE=SNU6500 Wireless Adapter + +usb:v0471p1233* + ID_MODEL_FROM_DATABASE=Wireless Adapter Bootloader Download + +usb:v0471p1236* + ID_MODEL_FROM_DATABASE=SNU5600 802.11bg + +usb:v0471p1237* + ID_MODEL_FROM_DATABASE=TalkTalk SNU5630NS/05 802.11bg + +usb:v0471p1552* + ID_MODEL_FROM_DATABASE=ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit + +usb:v0471p1801* + ID_MODEL_FROM_DATABASE=Diva MP3 player + +usb:v0471p200A* + ID_MODEL_FROM_DATABASE=Wireless Network Adapter + +usb:v0471p200F* + ID_MODEL_FROM_DATABASE=802.11n Wireless Adapter + +usb:v0471p2021* + ID_MODEL_FROM_DATABASE=SDE3273FC/97 2.5" SATA HDD Enclosure [INIC-1608L] + +usb:v0471p2022* + ID_MODEL_FROM_DATABASE=GoGear SA52XX + +usb:v0471p2034* + ID_MODEL_FROM_DATABASE=Webcam SPC530NC + +usb:v0471p2036* + ID_MODEL_FROM_DATABASE=Webcam SPC1030NC + +usb:v0471p203F* + ID_MODEL_FROM_DATABASE=TSU9200 Remote Control + +usb:v0471p2046* + ID_MODEL_FROM_DATABASE=TSU9800 Remote Control + +usb:v0471p204E* + ID_MODEL_FROM_DATABASE=GoGear RaGa (SA1942/02) + +usb:v0471p205E* + ID_MODEL_FROM_DATABASE=TSU9300 Remote Control + +usb:v0471p206C* + ID_MODEL_FROM_DATABASE=MCE IR Receiver - Spinel plusf0r ASUS + +usb:v0471p2070* + ID_MODEL_FROM_DATABASE=GoGear Mix + +usb:v0471p2076* + ID_MODEL_FROM_DATABASE=GoGear Aria + +usb:v0471p2079* + ID_MODEL_FROM_DATABASE=GoGear Opus + +usb:v0471p2088* + ID_MODEL_FROM_DATABASE=MCE IR Receiver with ALS- Spinel plus for ASUS + +usb:v0471p209E* + ID_MODEL_FROM_DATABASE=PTA01 Wireless Adapter + +usb:v0471p20B6* + ID_MODEL_FROM_DATABASE=GoGear Vibe + +usb:v0471p20D0* + ID_MODEL_FROM_DATABASE=SPZ2000 Webcam [PixArt PAC7332] + +usb:v0471p20E3* + ID_MODEL_FROM_DATABASE=GoGear Raga + +usb:v0471p20E4* + ID_MODEL_FROM_DATABASE=GoGear ViBE 8GB + +usb:v0471p2160* + ID_MODEL_FROM_DATABASE=Mio LINK Heart Rate Monitor + +usb:v0471p262C* + ID_MODEL_FROM_DATABASE=SPC230NC Webcam + +usb:v0471p485D* + ID_MODEL_FROM_DATABASE=Senselock SenseIV v2.x + +usb:v0471pDF55* + ID_MODEL_FROM_DATABASE=LPCXpresso LPC-Link + +usb:v0472* + ID_VENDOR_FROM_DATABASE=Chicony Electronics Co., Ltd + +usb:v0472p0065* + ID_MODEL_FROM_DATABASE=PFU-65 Keyboard [Chicony] + +usb:v0472pB086* + ID_MODEL_FROM_DATABASE=Asus USB2.0 Webcam + +usb:v0472pB091* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v0473* + ID_VENDOR_FROM_DATABASE=Sanyo Information Business Co., Ltd + +usb:v0474* + ID_VENDOR_FROM_DATABASE=Sanyo Electric Co., Ltd + +usb:v0474p0110* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder R200 + +usb:v0474p0217* + ID_MODEL_FROM_DATABASE=Xacti J2 + +usb:v0474p022F* + ID_MODEL_FROM_DATABASE=C5 Digital Media Camera (mass storage mode) + +usb:v0474p0230* + ID_MODEL_FROM_DATABASE=C5 Digital Media Camera (PictBridge mode) + +usb:v0474p0231* + ID_MODEL_FROM_DATABASE=C5 Digital Media Camera (PC control mode) + +usb:v0474p0401* + ID_MODEL_FROM_DATABASE=Optical Drive + +usb:v0474p0701* + ID_MODEL_FROM_DATABASE=SCP-4900 Cellphone + +usb:v0474p071F* + ID_MODEL_FROM_DATABASE=Usb Com Port Enumerator + +usb:v0474p0722* + ID_MODEL_FROM_DATABASE=W33SA Camera + +usb:v0475* + ID_VENDOR_FROM_DATABASE=Relisys/Teco Information System + +usb:v0475p0100* + ID_MODEL_FROM_DATABASE=NEC Petiscan + +usb:v0475p0103* + ID_MODEL_FROM_DATABASE=Eclipse 1200U/Episode + +usb:v0475p0210* + ID_MODEL_FROM_DATABASE=Scorpio Ultra 3 + +usb:v0476* + ID_VENDOR_FROM_DATABASE=AESP + +usb:v0477* + ID_VENDOR_FROM_DATABASE=Seagate Technology, Inc. + +usb:v0478* + ID_VENDOR_FROM_DATABASE=Connectix Corp. + +usb:v0478p0001* + ID_MODEL_FROM_DATABASE=QuickCam + +usb:v0478p0002* + ID_MODEL_FROM_DATABASE=QuickClip + +usb:v0478p0003* + ID_MODEL_FROM_DATABASE=QuickCam Pro + +usb:v0479* + ID_VENDOR_FROM_DATABASE=Advanced Peripheral Laboratories + +usb:v047A* + ID_VENDOR_FROM_DATABASE=Semtech Corp. + +usb:v047Ap0004* + ID_MODEL_FROM_DATABASE=ScreenCoder UR7HCTS2-USB + +usb:v047B* + ID_VENDOR_FROM_DATABASE=Silitek Corp. + +usb:v047Bp0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v047Bp0002* + ID_MODEL_FROM_DATABASE=Keyboard and Mouse + +usb:v047Bp0011* + ID_MODEL_FROM_DATABASE=SK-1688U Keyboard + +usb:v047Bp00F9* + ID_MODEL_FROM_DATABASE=SK-1789u Keyboard + +usb:v047Bp0101* + ID_MODEL_FROM_DATABASE=BlueTooth Keyboard and Mouse + +usb:v047Bp020B* + ID_MODEL_FROM_DATABASE=SK-3105 SmartCard Reader + +usb:v047Bp050E* + ID_MODEL_FROM_DATABASE=Internet Compact Keyboard + +usb:v047Bp1000* + ID_MODEL_FROM_DATABASE=Trust Office Scan USB 19200 + +usb:v047Bp1002* + ID_MODEL_FROM_DATABASE=HP ScanJet 4300c Parallel Port + +usb:v047C* + ID_VENDOR_FROM_DATABASE=Dell Computer Corp. + +usb:v047CpFFFF* + ID_MODEL_FROM_DATABASE=UPS Tower 500W LV + +usb:v047D* + ID_VENDOR_FROM_DATABASE=Kensington + +usb:v047Dp1001* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box + +usb:v047Dp1002* + ID_MODEL_FROM_DATABASE=Expert Mouse Pro + +usb:v047Dp1003* + ID_MODEL_FROM_DATABASE=Orbit TrackBall + +usb:v047Dp1004* + ID_MODEL_FROM_DATABASE=MouseWorks + +usb:v047Dp1005* + ID_MODEL_FROM_DATABASE=TurboBall + +usb:v047Dp1006* + ID_MODEL_FROM_DATABASE=TurboRing + +usb:v047Dp1009* + ID_MODEL_FROM_DATABASE=Orbit TrackBall for Mac + +usb:v047Dp1012* + ID_MODEL_FROM_DATABASE=PocketMouse + +usb:v047Dp1013* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box Optical Pro + +usb:v047Dp1014* + ID_MODEL_FROM_DATABASE=Expert Mouse Pro Wireless + +usb:v047Dp1015* + ID_MODEL_FROM_DATABASE=Expert Mouse + +usb:v047Dp1016* + ID_MODEL_FROM_DATABASE=ADB/USB Orbit + +usb:v047Dp1018* + ID_MODEL_FROM_DATABASE=Studio Mouse + +usb:v047Dp101D* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box Optical Pro + +usb:v047Dp101E* + ID_MODEL_FROM_DATABASE=Studio Mouse Wireless + +usb:v047Dp101F* + ID_MODEL_FROM_DATABASE=PocketMouse Pro + +usb:v047Dp1020* + ID_MODEL_FROM_DATABASE=Expert Mouse Trackball + +usb:v047Dp1021* + ID_MODEL_FROM_DATABASE=Expert Mouse Wireless + +usb:v047Dp1022* + ID_MODEL_FROM_DATABASE=Orbit Optical + +usb:v047Dp1023* + ID_MODEL_FROM_DATABASE=Pocket Mouse Pro Wireless + +usb:v047Dp1024* + ID_MODEL_FROM_DATABASE=PocketMouse + +usb:v047Dp1025* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box Optical Elite Wireless + +usb:v047Dp1026* + ID_MODEL_FROM_DATABASE=Pocket Mouse Pro + +usb:v047Dp1027* + ID_MODEL_FROM_DATABASE=StudioMouse + +usb:v047Dp1028* + ID_MODEL_FROM_DATABASE=StudioMouse Wireless + +usb:v047Dp1029* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box Optical Elite + +usb:v047Dp102A* + ID_MODEL_FROM_DATABASE=Mouse*in*a*Box Optical + +usb:v047Dp102B* + ID_MODEL_FROM_DATABASE=PocketMouse + +usb:v047Dp102C* + ID_MODEL_FROM_DATABASE=Iridio + +usb:v047Dp102D* + ID_MODEL_FROM_DATABASE=Pilot Optical + +usb:v047Dp102E* + ID_MODEL_FROM_DATABASE=Pilot Optical Pro + +usb:v047Dp102F* + ID_MODEL_FROM_DATABASE=Pilot Optical Pro Wireless + +usb:v047Dp1042* + ID_MODEL_FROM_DATABASE=Ci25m Notebook Optical Mouse [Diamond Eye Precision] + +usb:v047Dp1043* + ID_MODEL_FROM_DATABASE=Ci65m Wireless Notebook Optical Mouse + +usb:v047Dp104A* + ID_MODEL_FROM_DATABASE=PilotMouse Mini Retractable + +usb:v047Dp105D* + ID_MODEL_FROM_DATABASE=PocketMouse Bluetooth + +usb:v047Dp105E* + ID_MODEL_FROM_DATABASE=Bluetooth EDR Dongle + +usb:v047Dp1061* + ID_MODEL_FROM_DATABASE=PocketMouse Grip + +usb:v047Dp1062* + ID_MODEL_FROM_DATABASE=PocketMouse Max + +usb:v047Dp1063* + ID_MODEL_FROM_DATABASE=PocketMouse Max Wireless + +usb:v047Dp1064* + ID_MODEL_FROM_DATABASE=PocketMouse 2.0 Wireless + +usb:v047Dp1065* + ID_MODEL_FROM_DATABASE=PocketMouse 2.0 + +usb:v047Dp1066* + ID_MODEL_FROM_DATABASE=PocketMouse Max Glow + +usb:v047Dp1067* + ID_MODEL_FROM_DATABASE=ValueMouse + +usb:v047Dp1068* + ID_MODEL_FROM_DATABASE=ValueOpt White + +usb:v047Dp1069* + ID_MODEL_FROM_DATABASE=ValueOpt Black + +usb:v047Dp106A* + ID_MODEL_FROM_DATABASE=PilotMouse Laser Wireless Mini + +usb:v047Dp106B* + ID_MODEL_FROM_DATABASE=PilotMouse Laser - 3 Button + +usb:v047Dp106C* + ID_MODEL_FROM_DATABASE=PilotMouse Laser - Gaming + +usb:v047Dp106D* + ID_MODEL_FROM_DATABASE=PilotMouse Laser - Wired + +usb:v047Dp106E* + ID_MODEL_FROM_DATABASE=PilotMouse Micro Laser + +usb:v047Dp1070* + ID_MODEL_FROM_DATABASE=ValueOpt Travel + +usb:v047Dp1071* + ID_MODEL_FROM_DATABASE=ValueOpt RF TX + +usb:v047Dp1072* + ID_MODEL_FROM_DATABASE=PocketMouse Colour + +usb:v047Dp1073* + ID_MODEL_FROM_DATABASE=PilotMouse Laser - 6 Button + +usb:v047Dp1074* + ID_MODEL_FROM_DATABASE=PilotMouse Laser Wireless Mini + +usb:v047Dp1075* + ID_MODEL_FROM_DATABASE=SlimBlade Presenter Media Mouse + +usb:v047Dp1076* + ID_MODEL_FROM_DATABASE=SlimBlade Media Mouse + +usb:v047Dp1077* + ID_MODEL_FROM_DATABASE=SlimBlade Presenter Mouse + +usb:v047Dp1152* + ID_MODEL_FROM_DATABASE=Bluetooth EDR Dongle + +usb:v047Dp2002* + ID_MODEL_FROM_DATABASE=Optical Elite Wireless + +usb:v047Dp2010* + ID_MODEL_FROM_DATABASE=Wireless Presentation Remote + +usb:v047Dp2012* + ID_MODEL_FROM_DATABASE=Wireless Presenter with Laser Pointer + +usb:v047Dp2021* + ID_MODEL_FROM_DATABASE=PilotBoard Wireless + +usb:v047Dp2030* + ID_MODEL_FROM_DATABASE=PilotBoard Wireless + +usb:v047Dp2034* + ID_MODEL_FROM_DATABASE=SlimBlade Media Notebook Set + +usb:v047Dp2041* + ID_MODEL_FROM_DATABASE=SlimBlade Trackball + +usb:v047Dp2048* + ID_MODEL_FROM_DATABASE=Orbit Trackball with Scroll Ring + +usb:v047Dp4003* + ID_MODEL_FROM_DATABASE=Gravis Xterminator Digital Gamepad + +usb:v047Dp4005* + ID_MODEL_FROM_DATABASE=Gravis Eliminator GamePad Pro + +usb:v047Dp4006* + ID_MODEL_FROM_DATABASE=Gravis Eliminator AfterShock + +usb:v047Dp4007* + ID_MODEL_FROM_DATABASE=Gravis Xterminator Force + +usb:v047Dp4008* + ID_MODEL_FROM_DATABASE=Gravis Destroyer TiltPad + +usb:v047Dp5001* + ID_MODEL_FROM_DATABASE=Cabo I Camera + +usb:v047Dp5002* + ID_MODEL_FROM_DATABASE=VideoCam CABO II + +usb:v047Dp5003* + ID_MODEL_FROM_DATABASE=VideoCam + +usb:v047E* + ID_VENDOR_FROM_DATABASE=Agere Systems, Inc. (Lucent) + +usb:v047Ep0300* + ID_MODEL_FROM_DATABASE=ORiNOCO Card + +usb:v047Ep1001* + ID_MODEL_FROM_DATABASE=USS720 Parallel Port + +usb:v047Ep2892* + ID_MODEL_FROM_DATABASE=Systems Soft Modem + +usb:v047EpBAD1* + ID_MODEL_FROM_DATABASE=Lucent 56k Modem + +usb:v047EpF101* + ID_MODEL_FROM_DATABASE=Atlas Modem + +usb:v047F* + ID_VENDOR_FROM_DATABASE=Plantronics, Inc. + +usb:v047Fp0101* + ID_MODEL_FROM_DATABASE=Bulk Driver + +usb:v047Fp0301* + ID_MODEL_FROM_DATABASE=Bulk Driver + +usb:v047Fp0411* + ID_MODEL_FROM_DATABASE=Savi Office Base Station + +usb:v047Fp0CA1* + ID_MODEL_FROM_DATABASE=USB DSP v4 Audio Interface + +usb:v047Fp4254* + ID_MODEL_FROM_DATABASE=BUA-100 Bluetooth Adapter + +usb:v047FpAC01* + ID_MODEL_FROM_DATABASE=Savi 7xx + +usb:v047FpAD01* + ID_MODEL_FROM_DATABASE=GameCom 777 5.1 Headset + +usb:v047FpC008* + ID_MODEL_FROM_DATABASE=Audio 655 DSP + +usb:v047FpC00E* + ID_MODEL_FROM_DATABASE=Blackwire C310 headset + +usb:v0480* + ID_VENDOR_FROM_DATABASE=Toshiba America Inc + +usb:v0480p0001* + ID_MODEL_FROM_DATABASE=InTouch Module + +usb:v0480p0004* + ID_MODEL_FROM_DATABASE=InTouch Module + +usb:v0480p0011* + ID_MODEL_FROM_DATABASE=InTouch Module + +usb:v0480p0014* + ID_MODEL_FROM_DATABASE=InTouch Module + +usb:v0480p0100* + ID_MODEL_FROM_DATABASE=Stor.E Slim USB 3.0 + +usb:v0480p0200* + ID_MODEL_FROM_DATABASE=External Disk + +usb:v0480pA006* + ID_MODEL_FROM_DATABASE=External Disk 1.5TB + +usb:v0480pA007* + ID_MODEL_FROM_DATABASE=External Disk USB 3.0 + +usb:v0480pA009* + ID_MODEL_FROM_DATABASE=Stor.E Basics + +usb:v0480pA00D* + ID_MODEL_FROM_DATABASE=STOR.E BASICS 500GB + +usb:v0480pA100* + ID_MODEL_FROM_DATABASE=Canvio Alu 2TB 2.5" Black External Disk Model HDTH320EK3CA + +usb:v0480pA202* + ID_MODEL_FROM_DATABASE=Canvio Basics HDD + +usb:v0480pA208* + ID_MODEL_FROM_DATABASE=Canvio Basics 2TB USB 3.0 Portable Hard Drive + +usb:v0480pB001* + ID_MODEL_FROM_DATABASE=Stor.E Partner + +usb:v0480pD000* + ID_MODEL_FROM_DATABASE=External Disk 2TB Model DT01ABA200 + +usb:v0480pD010* + ID_MODEL_FROM_DATABASE=External Disk 3TB + +usb:v0480pD011* + ID_MODEL_FROM_DATABASE=Canvio Desk + +usb:v0481* + ID_VENDOR_FROM_DATABASE=Zenith Data Systems + +usb:v0482* + ID_VENDOR_FROM_DATABASE=Kyocera Corp. + +usb:v0482p000E* + ID_MODEL_FROM_DATABASE=FS-1020D Printer + +usb:v0482p000F* + ID_MODEL_FROM_DATABASE=FS-1920 Mono Printer + +usb:v0482p0015* + ID_MODEL_FROM_DATABASE=FS-1030D printer + +usb:v0482p0100* + ID_MODEL_FROM_DATABASE=Finecam S3x + +usb:v0482p0101* + ID_MODEL_FROM_DATABASE=Finecam S4 + +usb:v0482p0103* + ID_MODEL_FROM_DATABASE=Finecam S5 + +usb:v0482p0105* + ID_MODEL_FROM_DATABASE=Finecam L3 + +usb:v0482p0106* + ID_MODEL_FROM_DATABASE=Finecam + +usb:v0482p0107* + ID_MODEL_FROM_DATABASE=Digital Camera Device + +usb:v0482p0108* + ID_MODEL_FROM_DATABASE=Digital Camera Device + +usb:v0482p0203* + ID_MODEL_FROM_DATABASE=AH-K3001V + +usb:v0482p0204* + ID_MODEL_FROM_DATABASE=iBurst Terminal + +usb:v0483* + ID_VENDOR_FROM_DATABASE=STMicroelectronics + +usb:v0483p0137* + ID_MODEL_FROM_DATABASE=BeWAN ADSL USB ST (blue or green) + +usb:v0483p0138* + ID_MODEL_FROM_DATABASE=Unicorn II (ST70138B + MTC-20174TQ chipset) + +usb:v0483p1307* + ID_MODEL_FROM_DATABASE=Cytronix 6in1 Card Reader + +usb:v0483p163D* + ID_MODEL_FROM_DATABASE=Cool Icam Digi-MP3 + +usb:v0483p2015* + ID_MODEL_FROM_DATABASE=TouchChip® Fingerprint Reader + +usb:v0483p2016* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v0483p2017* + ID_MODEL_FROM_DATABASE=Biometric Smart Card Reader + +usb:v0483p2018* + ID_MODEL_FROM_DATABASE=BioSimKey + +usb:v0483p2302* + ID_MODEL_FROM_DATABASE=Portable Flash Device (PFD) + +usb:v0483p3744* + ID_MODEL_FROM_DATABASE=ST-LINK/V1 + +usb:v0483p3747* + ID_MODEL_FROM_DATABASE=ST Micro Connect Lite + +usb:v0483p3748* + ID_MODEL_FROM_DATABASE=ST-LINK/V2 + +usb:v0483p374B* + ID_MODEL_FROM_DATABASE=ST-LINK/V2.1 + +usb:v0483p4810* + ID_MODEL_FROM_DATABASE=ISDN adapter + +usb:v0483p481D* + ID_MODEL_FROM_DATABASE=BT Digital Access adapter + +usb:v0483p5000* + ID_MODEL_FROM_DATABASE=ST Micro/Ergenic ERG BT-002 Bluetooth Adapter + +usb:v0483p5001* + ID_MODEL_FROM_DATABASE=ST Micro Bluetooth Device + +usb:v0483p5710* + ID_MODEL_FROM_DATABASE=Joystick in FS Mode + +usb:v0483p5720* + ID_MODEL_FROM_DATABASE=STM microSD Flash Device + +usb:v0483p5721* + ID_MODEL_FROM_DATABASE=Hantek DDS-3X25 Arbitrary Waveform Generator + +usb:v0483p5730* + ID_MODEL_FROM_DATABASE=STM32 Audio Streaming + +usb:v0483p5740* + ID_MODEL_FROM_DATABASE=STM32F407 + +usb:v0483p7270* + ID_MODEL_FROM_DATABASE=ST Micro Serial Bridge + +usb:v0483p7554* + ID_MODEL_FROM_DATABASE=56k SoftModem + +usb:v0483p91D1* + ID_MODEL_FROM_DATABASE=Sensor Hub + +usb:v0483pDF11* + ID_MODEL_FROM_DATABASE=STM Device in DFU Mode + +usb:v0483pFF10* + ID_MODEL_FROM_DATABASE=Swann ST56 Modem + +usb:v0484* + ID_VENDOR_FROM_DATABASE=Specialix + +usb:v0485* + ID_VENDOR_FROM_DATABASE=Nokia Monitors + +usb:v0486* + ID_VENDOR_FROM_DATABASE=ASUS Computers, Inc. + +usb:v0486p0185* + ID_MODEL_FROM_DATABASE=EeePC T91MT HID Touch Panel + +usb:v0487* + ID_VENDOR_FROM_DATABASE=Stewart Connector + +usb:v0488* + ID_VENDOR_FROM_DATABASE=Cirque Corp. + +usb:v0489* + ID_VENDOR_FROM_DATABASE=Foxconn / Hon Hai + +usb:v0489p0502* + ID_MODEL_FROM_DATABASE=SmartMedia Card Reader Firmware Loader + +usb:v0489p0503* + ID_MODEL_FROM_DATABASE=SmartMedia Card Reader + +usb:v0489pD00C* + ID_MODEL_FROM_DATABASE=Rollei Compactline (Storage Mode) + +usb:v0489pD00E* + ID_MODEL_FROM_DATABASE=Rollei Compactline (Video Mode) + +usb:v0489pE000* + ID_MODEL_FROM_DATABASE=T-Com TC 300 + +usb:v0489pE003* + ID_MODEL_FROM_DATABASE=Pirelli DP-L10 + +usb:v0489pE00D* + ID_MODEL_FROM_DATABASE=Broadcom Bluetooth 2.1 Device + +usb:v0489pE00F* + ID_MODEL_FROM_DATABASE=Foxconn T77H114 BCM2070 [Single-Chip Bluetooth 2.1 + EDR Adapter] + +usb:v0489pE011* + ID_MODEL_FROM_DATABASE=Acer Bluetooth module + +usb:v0489pE016* + ID_MODEL_FROM_DATABASE=Ubee PXU1900 WiMAX Adapter [Beceem BCSM250] + +usb:v0489pE02C* + ID_MODEL_FROM_DATABASE=Atheros AR5BBU12 Bluetooth Device + +usb:v0489pE032* + ID_MODEL_FROM_DATABASE=Broadcom BCM20702 Bluetooth + +usb:v0489pE042* + ID_MODEL_FROM_DATABASE=Broadcom BCM20702 Bluetooth + +usb:v0489pE04D* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v048A* + ID_VENDOR_FROM_DATABASE=S-MOS Systems, Inc. + +usb:v048C* + ID_VENDOR_FROM_DATABASE=Alps Electric Ireland, Ltd + +usb:v048D* + ID_VENDOR_FROM_DATABASE=Integrated Technology Express, Inc. + +usb:v048Dp1165* + ID_MODEL_FROM_DATABASE=IT1165 Flash Controller + +usb:v048Dp1336* + ID_MODEL_FROM_DATABASE=SD/MMC Cardreader + +usb:v048Dp1345* + ID_MODEL_FROM_DATABASE=Multi Cardreader + +usb:v048Dp9006* + ID_MODEL_FROM_DATABASE=IT9135 BDA Afatech DVB-T HDTV Dongle + +usb:v048Dp9009* + ID_MODEL_FROM_DATABASE=Zolid HD DVD Maker + +usb:v048Dp9135* + ID_MODEL_FROM_DATABASE=Zolid Mini DVB-T Stick + +usb:v048Dp9306* + ID_MODEL_FROM_DATABASE=IT930x DVB stick + +usb:v048Dp9503* + ID_MODEL_FROM_DATABASE=ITE it9503 feature-limited DVB-T transmission chip [ccHDtv] + +usb:v048Dp9507* + ID_MODEL_FROM_DATABASE=ITE it9507 full featured DVB-T transmission chip [ccHDtv] + +usb:v048F* + ID_VENDOR_FROM_DATABASE=Eicon Tech. + +usb:v0490* + ID_VENDOR_FROM_DATABASE=United Microelectronics Corp. + +usb:v0491* + ID_VENDOR_FROM_DATABASE=Capetronic + +usb:v0491p0003* + ID_MODEL_FROM_DATABASE=Taxan Monitor Control + +usb:v0492* + ID_VENDOR_FROM_DATABASE=Samsung SemiConductor, Inc. + +usb:v0492p0140* + ID_MODEL_FROM_DATABASE=MP3 player + +usb:v0492p0141* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0493* + ID_VENDOR_FROM_DATABASE=MAG Technology Co., Ltd + +usb:v0495* + ID_VENDOR_FROM_DATABASE=ESS Technology, Inc. + +usb:v0496* + ID_VENDOR_FROM_DATABASE=Micron Electronics + +usb:v0497* + ID_VENDOR_FROM_DATABASE=Smile International + +usb:v0497pC001* + ID_MODEL_FROM_DATABASE=Camera Device + +usb:v0498* + ID_VENDOR_FROM_DATABASE=Capetronic (Kaohsiung) Corp. + +usb:v0499* + ID_VENDOR_FROM_DATABASE=Yamaha Corp. + +usb:v0499p1000* + ID_MODEL_FROM_DATABASE=UX256 MIDI I/F + +usb:v0499p1001* + ID_MODEL_FROM_DATABASE=MU1000 + +usb:v0499p1002* + ID_MODEL_FROM_DATABASE=MU2000 + +usb:v0499p1003* + ID_MODEL_FROM_DATABASE=MU500 + +usb:v0499p1004* + ID_MODEL_FROM_DATABASE=UW500 + +usb:v0499p1005* + ID_MODEL_FROM_DATABASE=MOTIF6 + +usb:v0499p1006* + ID_MODEL_FROM_DATABASE=MOTIF7 + +usb:v0499p1007* + ID_MODEL_FROM_DATABASE=MOTIF8 + +usb:v0499p1008* + ID_MODEL_FROM_DATABASE=UX96 MIDI I/F + +usb:v0499p1009* + ID_MODEL_FROM_DATABASE=UX16 MIDI I/F + +usb:v0499p100A* + ID_MODEL_FROM_DATABASE=EOS BX + +usb:v0499p100C* + ID_MODEL_FROM_DATABASE=UC-MX + +usb:v0499p100D* + ID_MODEL_FROM_DATABASE=UC-KX + +usb:v0499p100E* + ID_MODEL_FROM_DATABASE=S08 + +usb:v0499p100F* + ID_MODEL_FROM_DATABASE=CLP-150 + +usb:v0499p1010* + ID_MODEL_FROM_DATABASE=CLP-170 + +usb:v0499p1011* + ID_MODEL_FROM_DATABASE=P-250 + +usb:v0499p1012* + ID_MODEL_FROM_DATABASE=TYROS + +usb:v0499p1013* + ID_MODEL_FROM_DATABASE=PF-500 + +usb:v0499p1014* + ID_MODEL_FROM_DATABASE=S90 + +usb:v0499p1015* + ID_MODEL_FROM_DATABASE=MOTIF-R + +usb:v0499p1016* + ID_MODEL_FROM_DATABASE=MDP-5 + +usb:v0499p1017* + ID_MODEL_FROM_DATABASE=CVP-204 + +usb:v0499p1018* + ID_MODEL_FROM_DATABASE=CVP-206 + +usb:v0499p1019* + ID_MODEL_FROM_DATABASE=CVP-208 + +usb:v0499p101A* + ID_MODEL_FROM_DATABASE=CVP-210 + +usb:v0499p101B* + ID_MODEL_FROM_DATABASE=PSR-1100 + +usb:v0499p101C* + ID_MODEL_FROM_DATABASE=PSR-2100 + +usb:v0499p101D* + ID_MODEL_FROM_DATABASE=CLP-175 + +usb:v0499p101E* + ID_MODEL_FROM_DATABASE=PSR-K1 + +usb:v0499p101F* + ID_MODEL_FROM_DATABASE=EZ-J24 + +usb:v0499p1020* + ID_MODEL_FROM_DATABASE=EZ-250i + +usb:v0499p1021* + ID_MODEL_FROM_DATABASE=MOTIF ES 6 + +usb:v0499p1022* + ID_MODEL_FROM_DATABASE=MOTIF ES 7 + +usb:v0499p1023* + ID_MODEL_FROM_DATABASE=MOTIF ES 8 + +usb:v0499p1024* + ID_MODEL_FROM_DATABASE=CVP-301 + +usb:v0499p1025* + ID_MODEL_FROM_DATABASE=CVP-303 + +usb:v0499p1026* + ID_MODEL_FROM_DATABASE=CVP-305 + +usb:v0499p1027* + ID_MODEL_FROM_DATABASE=CVP-307 + +usb:v0499p1028* + ID_MODEL_FROM_DATABASE=CVP-309 + +usb:v0499p1029* + ID_MODEL_FROM_DATABASE=CVP-309GP + +usb:v0499p102A* + ID_MODEL_FROM_DATABASE=PSR-1500 + +usb:v0499p102B* + ID_MODEL_FROM_DATABASE=PSR-3000 + +usb:v0499p102E* + ID_MODEL_FROM_DATABASE=ELS-01/01C + +usb:v0499p1030* + ID_MODEL_FROM_DATABASE=PSR-295/293 + +usb:v0499p1031* + ID_MODEL_FROM_DATABASE=DGX-205/203 + +usb:v0499p1032* + ID_MODEL_FROM_DATABASE=DGX-305 + +usb:v0499p1033* + ID_MODEL_FROM_DATABASE=DGX-505 + +usb:v0499p1037* + ID_MODEL_FROM_DATABASE=PSR-E403 + +usb:v0499p103C* + ID_MODEL_FROM_DATABASE=MOTIF-RACK ES + +usb:v0499p1054* + ID_MODEL_FROM_DATABASE=S90XS Keyboard/Music Synthesizer + +usb:v0499p160F* + ID_MODEL_FROM_DATABASE=P-105 + +usb:v0499p2000* + ID_MODEL_FROM_DATABASE=DGP-7 + +usb:v0499p2001* + ID_MODEL_FROM_DATABASE=DGP-5 + +usb:v0499p3001* + ID_MODEL_FROM_DATABASE=YST-MS55D USB Speaker + +usb:v0499p3003* + ID_MODEL_FROM_DATABASE=YST-M45D USB Speaker + +usb:v0499p4000* + ID_MODEL_FROM_DATABASE=NetVolante RTA54i Broadband&ISDN Router + +usb:v0499p4001* + ID_MODEL_FROM_DATABASE=NetVolante RTW65b Broadband Wireless Router + +usb:v0499p4002* + ID_MODEL_FROM_DATABASE=NetVolante RTW65i Broadband&ISDN Wireless Router + +usb:v0499p4004* + ID_MODEL_FROM_DATABASE=NetVolante RTA55i Broadband VoIP Router + +usb:v0499p5000* + ID_MODEL_FROM_DATABASE=CS1D + +usb:v0499p5001* + ID_MODEL_FROM_DATABASE=DSP1D + +usb:v0499p5002* + ID_MODEL_FROM_DATABASE=DME32 + +usb:v0499p5003* + ID_MODEL_FROM_DATABASE=DM2000 + +usb:v0499p5004* + ID_MODEL_FROM_DATABASE=02R96 + +usb:v0499p5005* + ID_MODEL_FROM_DATABASE=ACU16-C + +usb:v0499p5006* + ID_MODEL_FROM_DATABASE=NHB32-C + +usb:v0499p5007* + ID_MODEL_FROM_DATABASE=DM1000 + +usb:v0499p5008* + ID_MODEL_FROM_DATABASE=01V96 + +usb:v0499p5009* + ID_MODEL_FROM_DATABASE=SPX2000 + +usb:v0499p500A* + ID_MODEL_FROM_DATABASE=PM5D + +usb:v0499p500B* + ID_MODEL_FROM_DATABASE=DME64N + +usb:v0499p500C* + ID_MODEL_FROM_DATABASE=DME24N + +usb:v0499p6001* + ID_MODEL_FROM_DATABASE=CRW2200UX Lightspeed 2 External CD-RW Drive + +usb:v0499p7000* + ID_MODEL_FROM_DATABASE=DTX + +usb:v0499p7010* + ID_MODEL_FROM_DATABASE=UB99 + +usb:v049A* + ID_VENDOR_FROM_DATABASE=Gandalf Technologies, Ltd + +usb:v049B* + ID_VENDOR_FROM_DATABASE=Curtis Computer Products + +usb:v049C* + ID_VENDOR_FROM_DATABASE=Acer Advanced Labs, Inc. + +usb:v049Cp0002* + ID_MODEL_FROM_DATABASE=Keyboard (???) + +usb:v049D* + ID_VENDOR_FROM_DATABASE=VLSI Technology + +usb:v049F* + ID_VENDOR_FROM_DATABASE=Compaq Computer Corp. + +usb:v049Fp0002* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v049Fp0003* + ID_MODEL_FROM_DATABASE=iPAQ PocketPC + +usb:v049Fp000E* + ID_MODEL_FROM_DATABASE=Internet Keyboard + +usb:v049Fp0012* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v049Fp0018* + ID_MODEL_FROM_DATABASE=PA-1/PA-2 MP3 Player + +usb:v049Fp0019* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v049Fp001A* + ID_MODEL_FROM_DATABASE=S4 100 Scanner + +usb:v049Fp001E* + ID_MODEL_FROM_DATABASE=IJ650 Inkjet Printer + +usb:v049Fp001F* + ID_MODEL_FROM_DATABASE=WL215 Adapter + +usb:v049Fp0021* + ID_MODEL_FROM_DATABASE=S200 Scanner + +usb:v049Fp0027* + ID_MODEL_FROM_DATABASE=Bluetooth Multiport Module by Compaq + +usb:v049Fp002A* + ID_MODEL_FROM_DATABASE=1400P Inkjet Printer + +usb:v049Fp002B* + ID_MODEL_FROM_DATABASE=A3000 + +usb:v049Fp002C* + ID_MODEL_FROM_DATABASE=Lexmark X125 + +usb:v049Fp0032* + ID_MODEL_FROM_DATABASE=802.11b Adapter [ipaq h5400] + +usb:v049Fp0033* + ID_MODEL_FROM_DATABASE=Wireless LAN MultiPort W100 [Intersil PRISM 2.5] + +usb:v049Fp0036* + ID_MODEL_FROM_DATABASE=Bluetooth Multiport Module + +usb:v049Fp0051* + ID_MODEL_FROM_DATABASE=KU-0133 Easy Access Interner Keyboard + +usb:v049Fp0076* + ID_MODEL_FROM_DATABASE=Wireless LAN MultiPort W200 + +usb:v049Fp0080* + ID_MODEL_FROM_DATABASE=GPRS Multiport + +usb:v049Fp0086* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v049Fp504A* + ID_MODEL_FROM_DATABASE=Personal Jukebox PJB100 + +usb:v049Fp505A* + ID_MODEL_FROM_DATABASE=Linux-USB "CDC Subset" Device, or Itsy (experimental) + +usb:v049Fp8511* + ID_MODEL_FROM_DATABASE=iPAQ Networking 10/100 Ethernet [pegasus2] + +usb:v04A0* + ID_VENDOR_FROM_DATABASE=Digital Equipment Corp. + +usb:v04A1* + ID_VENDOR_FROM_DATABASE=SystemSoft Corp. + +usb:v04A1pFFF0* + ID_MODEL_FROM_DATABASE=Telex Composite Device + +usb:v04A2* + ID_VENDOR_FROM_DATABASE=FirePower Systems + +usb:v04A3* + ID_VENDOR_FROM_DATABASE=Trident Microsystems, Inc. + +usb:v04A4* + ID_VENDOR_FROM_DATABASE=Hitachi, Ltd + +usb:v04A4p0004* + ID_MODEL_FROM_DATABASE=DVD-CAM DZ-MV100A Camcorder + +usb:v04A4p001E* + ID_MODEL_FROM_DATABASE=DVDCAM USB HS Interface + +usb:v04A5* + ID_VENDOR_FROM_DATABASE=Acer Peripherals Inc. (now BenQ Corp.) + +usb:v04A5p0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04A5p0002* + ID_MODEL_FROM_DATABASE=API Ergo K/B + +usb:v04A5p0003* + ID_MODEL_FROM_DATABASE=API Generic K/B Mouse + +usb:v04A5p12A6* + ID_MODEL_FROM_DATABASE=AcerScan C310U + +usb:v04A5p1A20* + ID_MODEL_FROM_DATABASE=Prisa 310U + +usb:v04A5p1A2A* + ID_MODEL_FROM_DATABASE=Prisa 620U + +usb:v04A5p2022* + ID_MODEL_FROM_DATABASE=Prisa 320U/340U + +usb:v04A5p2040* + ID_MODEL_FROM_DATABASE=Prisa 620UT + +usb:v04A5p205E* + ID_MODEL_FROM_DATABASE=ScanPrisa 640BU + +usb:v04A5p2060* + ID_MODEL_FROM_DATABASE=Prisa 620U+/640U + +usb:v04A5p207E* + ID_MODEL_FROM_DATABASE=Prisa 640BU + +usb:v04A5p209E* + ID_MODEL_FROM_DATABASE=ScanPrisa 640BT + +usb:v04A5p20AE* + ID_MODEL_FROM_DATABASE=S2W 3000U + +usb:v04A5p20B0* + ID_MODEL_FROM_DATABASE=S2W 3300U/4300U + +usb:v04A5p20BE* + ID_MODEL_FROM_DATABASE=Prisa 640BT + +usb:v04A5p20C0* + ID_MODEL_FROM_DATABASE=Prisa 1240UT + +usb:v04A5p20DE* + ID_MODEL_FROM_DATABASE=S2W 4300U+ + +usb:v04A5p20F8* + ID_MODEL_FROM_DATABASE=Benq 5000 + +usb:v04A5p20FC* + ID_MODEL_FROM_DATABASE=Benq 5000 + +usb:v04A5p20FE* + ID_MODEL_FROM_DATABASE=SW2 5300U + +usb:v04A5p2137* + ID_MODEL_FROM_DATABASE=Benq 5150/5250 + +usb:v04A5p2202* + ID_MODEL_FROM_DATABASE=Benq 7400UT + +usb:v04A5p2311* + ID_MODEL_FROM_DATABASE=Benq 5560 + +usb:v04A5p3003* + ID_MODEL_FROM_DATABASE=Benq Webcam + +usb:v04A5p3008* + ID_MODEL_FROM_DATABASE=Benq 1500 + +usb:v04A5p300A* + ID_MODEL_FROM_DATABASE=Benq 3410 + +usb:v04A5p300C* + ID_MODEL_FROM_DATABASE=Benq 1016 + +usb:v04A5p3019* + ID_MODEL_FROM_DATABASE=Benq DC C40 + +usb:v04A5p4000* + ID_MODEL_FROM_DATABASE=P30 Composite Device + +usb:v04A5p4013* + ID_MODEL_FROM_DATABASE=BenQ-Siemens EF82/SL91 + +usb:v04A5p4044* + ID_MODEL_FROM_DATABASE=BenQ-Siemens SF71 + +usb:v04A5p4045* + ID_MODEL_FROM_DATABASE=BenQ-Siemens E81 + +usb:v04A5p4048* + ID_MODEL_FROM_DATABASE=BenQ M7 + +usb:v04A5p6001* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6002* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6003* + ID_MODEL_FROM_DATABASE=ATA/ATAPI Adapter + +usb:v04A5p6004* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6005* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6006* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6007* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6008* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6009* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600A* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600B* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600C* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600D* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600E* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p600F* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6010* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6011* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6012* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6013* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6014* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6015* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v04A5p6125* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v04A5p6180* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v04A5p6200* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v04A5p7500* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v04A5p9000* + ID_MODEL_FROM_DATABASE=AWL300 Wireless Adapter + +usb:v04A5p9001* + ID_MODEL_FROM_DATABASE=AWL400 Wireless Adapter + +usb:v04A5p9213* + ID_MODEL_FROM_DATABASE=Kbd Hub + +usb:v04A6* + ID_VENDOR_FROM_DATABASE=Nokia Display Products + +usb:v04A6p00B9* + ID_MODEL_FROM_DATABASE=Audio + +usb:v04A6p0180* + ID_MODEL_FROM_DATABASE=Hub Type P + +usb:v04A6p0181* + ID_MODEL_FROM_DATABASE=HID Monitor Controls + +usb:v04A7* + ID_VENDOR_FROM_DATABASE=Visioneer + +usb:v04A7p0100* + ID_MODEL_FROM_DATABASE=StrobePro + +usb:v04A7p0101* + ID_MODEL_FROM_DATABASE=Strobe Pro Scanner (1.01) + +usb:v04A7p0102* + ID_MODEL_FROM_DATABASE=StrobePro Scanner + +usb:v04A7p0211* + ID_MODEL_FROM_DATABASE=OneTouch 7600 Scanner + +usb:v04A7p0221* + ID_MODEL_FROM_DATABASE=OneTouch 5300 Scanner + +usb:v04A7p0223* + ID_MODEL_FROM_DATABASE=OneTouch 8200 + +usb:v04A7p0224* + ID_MODEL_FROM_DATABASE=OneTouch 4800 USB/Microtek Scanport 3000 + +usb:v04A7p0225* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v04A7p0226* + ID_MODEL_FROM_DATABASE=OneTouch 5300 USB + +usb:v04A7p0229* + ID_MODEL_FROM_DATABASE=OneTouch 7100 + +usb:v04A7p022A* + ID_MODEL_FROM_DATABASE=OneTouch 6600 + +usb:v04A7p022C* + ID_MODEL_FROM_DATABASE=OneTouch 9000/9020 + +usb:v04A7p0231* + ID_MODEL_FROM_DATABASE=6100 Scanner + +usb:v04A7p0311* + ID_MODEL_FROM_DATABASE=6200 EPP/USB Scanner + +usb:v04A7p0321* + ID_MODEL_FROM_DATABASE=OneTouch 8100 EPP/USB Scanner + +usb:v04A7p0331* + ID_MODEL_FROM_DATABASE=OneTouch 8600 EPP/USB Scanner + +usb:v04A7p0341* + ID_MODEL_FROM_DATABASE=6400 + +usb:v04A7p0361* + ID_MODEL_FROM_DATABASE=VistaScan Astra 3600(ENG) + +usb:v04A7p0362* + ID_MODEL_FROM_DATABASE=OneTouch 9320 + +usb:v04A7p0371* + ID_MODEL_FROM_DATABASE=OneTouch 8700/8920 + +usb:v04A7p0380* + ID_MODEL_FROM_DATABASE=OneTouch 7700 + +usb:v04A7p0382* + ID_MODEL_FROM_DATABASE=Photo Port 7700 + +usb:v04A7p0390* + ID_MODEL_FROM_DATABASE=9650 + +usb:v04A7p03A0* + ID_MODEL_FROM_DATABASE=Xerox 4800 One Touch + +usb:v04A7p0410* + ID_MODEL_FROM_DATABASE=OneTouch Pro 8800/8820 + +usb:v04A7p0421* + ID_MODEL_FROM_DATABASE=9450 USB + +usb:v04A7p0423* + ID_MODEL_FROM_DATABASE=9750 Scanner + +usb:v04A7p0424* + ID_MODEL_FROM_DATABASE=Strobe XP 450 + +usb:v04A7p0425* + ID_MODEL_FROM_DATABASE=Strobe XP 100 + +usb:v04A7p0426* + ID_MODEL_FROM_DATABASE=Strobe XP 200 + +usb:v04A7p0427* + ID_MODEL_FROM_DATABASE=Strobe XP 100 + +usb:v04A7p0444* + ID_MODEL_FROM_DATABASE=OneTouch 7300 + +usb:v04A7p0445* + ID_MODEL_FROM_DATABASE=CardReader 100 + +usb:v04A7p0446* + ID_MODEL_FROM_DATABASE=Xerox DocuMate 510 + +usb:v04A7p0447* + ID_MODEL_FROM_DATABASE=XEROX DocuMate 520 + +usb:v04A7p0448* + ID_MODEL_FROM_DATABASE=XEROX DocuMate 250 + +usb:v04A7p0449* + ID_MODEL_FROM_DATABASE=Xerox DocuMate 252 + +usb:v04A7p044A* + ID_MODEL_FROM_DATABASE=Xerox 6400 + +usb:v04A7p044C* + ID_MODEL_FROM_DATABASE=Xerox DocuMate 262 + +usb:v04A7p0474* + ID_MODEL_FROM_DATABASE=Strobe XP 300 + +usb:v04A7p0475* + ID_MODEL_FROM_DATABASE=Xerox DocuMate 272 + +usb:v04A7p0478* + ID_MODEL_FROM_DATABASE=Strobe XP 220 + +usb:v04A7p0479* + ID_MODEL_FROM_DATABASE=Strobe XP 470 + +usb:v04A7p047A* + ID_MODEL_FROM_DATABASE=9450 + +usb:v04A7p047B* + ID_MODEL_FROM_DATABASE=9650 + +usb:v04A7p047D* + ID_MODEL_FROM_DATABASE=9420 + +usb:v04A7p0480* + ID_MODEL_FROM_DATABASE=9520 + +usb:v04A7p048F* + ID_MODEL_FROM_DATABASE=Strobe XP 470 + +usb:v04A7p0491* + ID_MODEL_FROM_DATABASE=Strobe XP 450 + +usb:v04A7p0493* + ID_MODEL_FROM_DATABASE=9750 + +usb:v04A7p0494* + ID_MODEL_FROM_DATABASE=Strobe XP 120 + +usb:v04A7p0497* + ID_MODEL_FROM_DATABASE=Patriot 430 + +usb:v04A7p0498* + ID_MODEL_FROM_DATABASE=Patriot 680 + +usb:v04A7p0499* + ID_MODEL_FROM_DATABASE=Patriot 780 + +usb:v04A7p049B* + ID_MODEL_FROM_DATABASE=Strobe XP 100 + +usb:v04A7p04A0* + ID_MODEL_FROM_DATABASE=7400 + +usb:v04A7p04AC* + ID_MODEL_FROM_DATABASE=Xerox Travel Scanner 100 + +usb:v04A8* + ID_VENDOR_FROM_DATABASE=Multivideo Labs, Inc. + +usb:v04A8p0101* + ID_MODEL_FROM_DATABASE=Hub + +usb:v04A8p0303* + ID_MODEL_FROM_DATABASE=Peripheral Switch + +usb:v04A8p0404* + ID_MODEL_FROM_DATABASE=Peripheral Switch + +usb:v04A9* + ID_VENDOR_FROM_DATABASE=Canon, Inc. + +usb:v04A9p1005* + ID_MODEL_FROM_DATABASE=BJ Printer Hub + +usb:v04A9p1035* + ID_MODEL_FROM_DATABASE=PD Printer Storage + +usb:v04A9p1050* + ID_MODEL_FROM_DATABASE=BJC-8200 + +usb:v04A9p1051* + ID_MODEL_FROM_DATABASE=BJC-3000 Color Printer + +usb:v04A9p1052* + ID_MODEL_FROM_DATABASE=BJC-6100 + +usb:v04A9p1053* + ID_MODEL_FROM_DATABASE=BJC-6200 + +usb:v04A9p1054* + ID_MODEL_FROM_DATABASE=BJC-6500 + +usb:v04A9p1055* + ID_MODEL_FROM_DATABASE=BJC-85 + +usb:v04A9p1056* + ID_MODEL_FROM_DATABASE=BJC-2110 Color Printer + +usb:v04A9p1057* + ID_MODEL_FROM_DATABASE=LR1 + +usb:v04A9p105A* + ID_MODEL_FROM_DATABASE=BJC-55 + +usb:v04A9p105B* + ID_MODEL_FROM_DATABASE=S600 Printer + +usb:v04A9p105C* + ID_MODEL_FROM_DATABASE=S400 + +usb:v04A9p105D* + ID_MODEL_FROM_DATABASE=S450 Printer + +usb:v04A9p105E* + ID_MODEL_FROM_DATABASE=S800 + +usb:v04A9p1062* + ID_MODEL_FROM_DATABASE=S500 Printer + +usb:v04A9p1063* + ID_MODEL_FROM_DATABASE=S4500 + +usb:v04A9p1064* + ID_MODEL_FROM_DATABASE=S300 Printer + +usb:v04A9p1065* + ID_MODEL_FROM_DATABASE=S100 + +usb:v04A9p1066* + ID_MODEL_FROM_DATABASE=S630 + +usb:v04A9p1067* + ID_MODEL_FROM_DATABASE=S900 + +usb:v04A9p1068* + ID_MODEL_FROM_DATABASE=S9000 + +usb:v04A9p1069* + ID_MODEL_FROM_DATABASE=S820 + +usb:v04A9p106A* + ID_MODEL_FROM_DATABASE=S200 Printer + +usb:v04A9p106B* + ID_MODEL_FROM_DATABASE=S520 Printer + +usb:v04A9p106D* + ID_MODEL_FROM_DATABASE=S750 Printer + +usb:v04A9p106E* + ID_MODEL_FROM_DATABASE=S820D + +usb:v04A9p1070* + ID_MODEL_FROM_DATABASE=S530D + +usb:v04A9p1072* + ID_MODEL_FROM_DATABASE=I850 Printer + +usb:v04A9p1073* + ID_MODEL_FROM_DATABASE=I550 Printer + +usb:v04A9p1074* + ID_MODEL_FROM_DATABASE=S330 Printer + +usb:v04A9p1076* + ID_MODEL_FROM_DATABASE=i70 + +usb:v04A9p1077* + ID_MODEL_FROM_DATABASE=i950 + +usb:v04A9p107A* + ID_MODEL_FROM_DATABASE=S830D + +usb:v04A9p107B* + ID_MODEL_FROM_DATABASE=i320 + +usb:v04A9p107C* + ID_MODEL_FROM_DATABASE=i470D + +usb:v04A9p107D* + ID_MODEL_FROM_DATABASE=i9100 + +usb:v04A9p107E* + ID_MODEL_FROM_DATABASE=i450 + +usb:v04A9p107F* + ID_MODEL_FROM_DATABASE=i860 + +usb:v04A9p1082* + ID_MODEL_FROM_DATABASE=i350 + +usb:v04A9p1084* + ID_MODEL_FROM_DATABASE=i250 + +usb:v04A9p1085* + ID_MODEL_FROM_DATABASE=i255 + +usb:v04A9p1086* + ID_MODEL_FROM_DATABASE=i560 + +usb:v04A9p1088* + ID_MODEL_FROM_DATABASE=i965 + +usb:v04A9p108A* + ID_MODEL_FROM_DATABASE=i455 + +usb:v04A9p108B* + ID_MODEL_FROM_DATABASE=i900D + +usb:v04A9p108C* + ID_MODEL_FROM_DATABASE=i475D + +usb:v04A9p108D* + ID_MODEL_FROM_DATABASE=PIXMA iP2000 + +usb:v04A9p108F* + ID_MODEL_FROM_DATABASE=i80 + +usb:v04A9p1090* + ID_MODEL_FROM_DATABASE=i9900 Photo Printer + +usb:v04A9p1091* + ID_MODEL_FROM_DATABASE=PIXMA iP1500 + +usb:v04A9p1093* + ID_MODEL_FROM_DATABASE=PIXMA iP4000 + +usb:v04A9p1094* + ID_MODEL_FROM_DATABASE=PIXMA iP3000x Printer + +usb:v04A9p1095* + ID_MODEL_FROM_DATABASE=PIXMA iP6000D + +usb:v04A9p1097* + ID_MODEL_FROM_DATABASE=PIXMA iP5000 + +usb:v04A9p1098* + ID_MODEL_FROM_DATABASE=PIXMA iP1000 + +usb:v04A9p1099* + ID_MODEL_FROM_DATABASE=PIXMA iP8500 + +usb:v04A9p109C* + ID_MODEL_FROM_DATABASE=PIXMA iP4000R + +usb:v04A9p109D* + ID_MODEL_FROM_DATABASE=iP90 + +usb:v04A9p10A0* + ID_MODEL_FROM_DATABASE=PIXMA iP1600 Printer + +usb:v04A9p10A2* + ID_MODEL_FROM_DATABASE=iP4200 + +usb:v04A9p10A4* + ID_MODEL_FROM_DATABASE=iP5200R + +usb:v04A9p10A5* + ID_MODEL_FROM_DATABASE=iP5200 + +usb:v04A9p10A7* + ID_MODEL_FROM_DATABASE=iP6210D + +usb:v04A9p10A8* + ID_MODEL_FROM_DATABASE=iP6220D + +usb:v04A9p10A9* + ID_MODEL_FROM_DATABASE=iP6600D + +usb:v04A9p10B6* + ID_MODEL_FROM_DATABASE=PIXMA iP4300 Printer + +usb:v04A9p10C2* + ID_MODEL_FROM_DATABASE=PIXMA iP1800 Printer + +usb:v04A9p10C4* + ID_MODEL_FROM_DATABASE=Pixma iP4500 Printer + +usb:v04A9p10C9* + ID_MODEL_FROM_DATABASE=PIXIMA iP4600 Printer + +usb:v04A9p1404* + ID_MODEL_FROM_DATABASE=W6400PG + +usb:v04A9p1405* + ID_MODEL_FROM_DATABASE=W8400PG + +usb:v04A9p150F* + ID_MODEL_FROM_DATABASE=BIJ2350 PCL + +usb:v04A9p1510* + ID_MODEL_FROM_DATABASE=BIJ1350 PCL + +usb:v04A9p1512* + ID_MODEL_FROM_DATABASE=BIJ1350D PCL + +usb:v04A9p1601* + ID_MODEL_FROM_DATABASE=DR-2080C Scanner + +usb:v04A9p1607* + ID_MODEL_FROM_DATABASE=DR-6080 Scanner + +usb:v04A9p1700* + ID_MODEL_FROM_DATABASE=PIXMA MP110 Scanner + +usb:v04A9p1701* + ID_MODEL_FROM_DATABASE=PIXMA MP130 Scanner + +usb:v04A9p1702* + ID_MODEL_FROM_DATABASE=MP410 Composite + +usb:v04A9p1703* + ID_MODEL_FROM_DATABASE=MP430 Composite + +usb:v04A9p1704* + ID_MODEL_FROM_DATABASE=MP330 Composite + +usb:v04A9p1706* + ID_MODEL_FROM_DATABASE=PIXMA MP750 Scanner + +usb:v04A9p1707* + ID_MODEL_FROM_DATABASE=PIXMA MP780 Scanner + +usb:v04A9p1708* + ID_MODEL_FROM_DATABASE=PIXMA MP760 Scanner + +usb:v04A9p1709* + ID_MODEL_FROM_DATABASE=PIXMA MP150 Scanner + +usb:v04A9p170A* + ID_MODEL_FROM_DATABASE=PIXMA MP170 Scanner + +usb:v04A9p170B* + ID_MODEL_FROM_DATABASE=PIXMA MP450 Scanner + +usb:v04A9p170C* + ID_MODEL_FROM_DATABASE=PIXMA MP500 Scanner + +usb:v04A9p170D* + ID_MODEL_FROM_DATABASE=PIXMA MP800 Scanner + +usb:v04A9p170E* + ID_MODEL_FROM_DATABASE=MP800R + +usb:v04A9p1710* + ID_MODEL_FROM_DATABASE=MP950 + +usb:v04A9p1712* + ID_MODEL_FROM_DATABASE=MP530 + +usb:v04A9p1713* + ID_MODEL_FROM_DATABASE=PIXMA MP830 Scanner + +usb:v04A9p1714* + ID_MODEL_FROM_DATABASE=MP160 + +usb:v04A9p1715* + ID_MODEL_FROM_DATABASE=MP180 Storage + +usb:v04A9p1716* + ID_MODEL_FROM_DATABASE=MP460 Composite + +usb:v04A9p1717* + ID_MODEL_FROM_DATABASE=MP510 + +usb:v04A9p1718* + ID_MODEL_FROM_DATABASE=MP600 Storage + +usb:v04A9p171A* + ID_MODEL_FROM_DATABASE=MP810 Storage + +usb:v04A9p171B* + ID_MODEL_FROM_DATABASE=MP960 + +usb:v04A9p1721* + ID_MODEL_FROM_DATABASE=MP210 ser + +usb:v04A9p1723* + ID_MODEL_FROM_DATABASE=MP470 ser + +usb:v04A9p1724* + ID_MODEL_FROM_DATABASE=PIXMA MP520 series + +usb:v04A9p1725* + ID_MODEL_FROM_DATABASE=MP610 ser + +usb:v04A9p1726* + ID_MODEL_FROM_DATABASE=MP970 ser + +usb:v04A9p1727* + ID_MODEL_FROM_DATABASE=MX300 ser + +usb:v04A9p1728* + ID_MODEL_FROM_DATABASE=MX310 ser + +usb:v04A9p1729* + ID_MODEL_FROM_DATABASE=MX700 ser + +usb:v04A9p172B* + ID_MODEL_FROM_DATABASE=MP140 ser + +usb:v04A9p1736* + ID_MODEL_FROM_DATABASE=PIXMA MX320 series + +usb:v04A9p173A* + ID_MODEL_FROM_DATABASE=MP250 series printer + +usb:v04A9p173B* + ID_MODEL_FROM_DATABASE=PIXMA MP270 All-In-One Printer + +usb:v04A9p173E* + ID_MODEL_FROM_DATABASE=MP560 + +usb:v04A9p173F* + ID_MODEL_FROM_DATABASE=Pixma MP640 Multifunction device + +usb:v04A9p1748* + ID_MODEL_FROM_DATABASE=Pixma MG5150 + +usb:v04A9p174D* + ID_MODEL_FROM_DATABASE=MX360 ser + +usb:v04A9p176D* + ID_MODEL_FROM_DATABASE=PIXMA MG2550 + +usb:v04A9p178D* + ID_MODEL_FROM_DATABASE=PIXMA MG6853 + +usb:v04A9p1900* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 90 + +usb:v04A9p1901* + ID_MODEL_FROM_DATABASE=CanoScan 8800F + +usb:v04A9p1904* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 100 + +usb:v04A9p1905* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 200 + +usb:v04A9p1906* + ID_MODEL_FROM_DATABASE=CanoScan 5600F + +usb:v04A9p1907* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 700F + +usb:v04A9p1909* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 110 + +usb:v04A9p190A* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 210 + +usb:v04A9p190D* + ID_MODEL_FROM_DATABASE=CanoScan 9000F Mark II + +usb:v04A9p190E* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 120 + +usb:v04A9p190F* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 220 + +usb:v04A9p2200* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 25 + +usb:v04A9p2201* + ID_MODEL_FROM_DATABASE=CanoScan FB320U + +usb:v04A9p2202* + ID_MODEL_FROM_DATABASE=CanoScan FB620U + +usb:v04A9p2204* + ID_MODEL_FROM_DATABASE=CanoScan FB630U + +usb:v04A9p2205* + ID_MODEL_FROM_DATABASE=CanoScan FB1210U + +usb:v04A9p2206* + ID_MODEL_FROM_DATABASE=CanoScan N650U/N656U + +usb:v04A9p2207* + ID_MODEL_FROM_DATABASE=CanoScan 1220U + +usb:v04A9p2208* + ID_MODEL_FROM_DATABASE=CanoScan D660U + +usb:v04A9p220A* + ID_MODEL_FROM_DATABASE=CanoScan D2400UF + +usb:v04A9p220B* + ID_MODEL_FROM_DATABASE=CanoScan D646U + +usb:v04A9p220C* + ID_MODEL_FROM_DATABASE=CanoScan D1250U2 + +usb:v04A9p220D* + ID_MODEL_FROM_DATABASE=CanoScan N670U/N676U/LiDE 20 + +usb:v04A9p220E* + ID_MODEL_FROM_DATABASE=CanoScan N1240U/LiDE 30 + +usb:v04A9p220F* + ID_MODEL_FROM_DATABASE=CanoScan 8000F + +usb:v04A9p2210* + ID_MODEL_FROM_DATABASE=CanoScan 9900F + +usb:v04A9p2212* + ID_MODEL_FROM_DATABASE=CanoScan 5000F + +usb:v04A9p2213* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 50/LiDE 35/LiDE 40 + +usb:v04A9p2214* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 80 + +usb:v04A9p2215* + ID_MODEL_FROM_DATABASE=CanoScan 3000/3000F/3000ex + +usb:v04A9p2216* + ID_MODEL_FROM_DATABASE=CanoScan 3200F + +usb:v04A9p2217* + ID_MODEL_FROM_DATABASE=CanoScan 5200F + +usb:v04A9p2219* + ID_MODEL_FROM_DATABASE=CanoScan 9950F + +usb:v04A9p221B* + ID_MODEL_FROM_DATABASE=CanoScan 4200F + +usb:v04A9p221C* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 60 + +usb:v04A9p221E* + ID_MODEL_FROM_DATABASE=CanoScan 8400F + +usb:v04A9p221F* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 500F + +usb:v04A9p2220* + ID_MODEL_FROM_DATABASE=CanoScan LIDE 25 + +usb:v04A9p2224* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 600F + +usb:v04A9p2225* + ID_MODEL_FROM_DATABASE=CanoScan LiDE 70 + +usb:v04A9p2228* + ID_MODEL_FROM_DATABASE=CanoScan 4400F + +usb:v04A9p2229* + ID_MODEL_FROM_DATABASE=CanoScan 8600F + +usb:v04A9p2602* + ID_MODEL_FROM_DATABASE=MultiPASS C555 + +usb:v04A9p2603* + ID_MODEL_FROM_DATABASE=MultiPASS C755 + +usb:v04A9p260A* + ID_MODEL_FROM_DATABASE=CAPT Printer + +usb:v04A9p260E* + ID_MODEL_FROM_DATABASE=LBP-2000 + +usb:v04A9p2610* + ID_MODEL_FROM_DATABASE=MPC600F + +usb:v04A9p2611* + ID_MODEL_FROM_DATABASE=SmartBase MPC400 + +usb:v04A9p2612* + ID_MODEL_FROM_DATABASE=MultiPASS C855 + +usb:v04A9p2617* + ID_MODEL_FROM_DATABASE=CAPT Printer + +usb:v04A9p261A* + ID_MODEL_FROM_DATABASE=iR1600 + +usb:v04A9p261B* + ID_MODEL_FROM_DATABASE=iR1610 + +usb:v04A9p261C* + ID_MODEL_FROM_DATABASE=iC2300 + +usb:v04A9p261F* + ID_MODEL_FROM_DATABASE=MPC200 Printer + +usb:v04A9p2621* + ID_MODEL_FROM_DATABASE=iR2000 + +usb:v04A9p2622* + ID_MODEL_FROM_DATABASE=iR2010 + +usb:v04A9p2623* + ID_MODEL_FROM_DATABASE=FAX-B180C + +usb:v04A9p2629* + ID_MODEL_FROM_DATABASE=FAXPHONE L75 + +usb:v04A9p262B* + ID_MODEL_FROM_DATABASE=LaserShot LBP-1120 Printer + +usb:v04A9p262D* + ID_MODEL_FROM_DATABASE=iR C3200 + +usb:v04A9p262F* + ID_MODEL_FROM_DATABASE=MultiPASS MP730 + +usb:v04A9p2630* + ID_MODEL_FROM_DATABASE=MultiPASS MP700 + +usb:v04A9p2631* + ID_MODEL_FROM_DATABASE=LASER CLASS 700 + +usb:v04A9p2632* + ID_MODEL_FROM_DATABASE=FAX-L2000 + +usb:v04A9p2635* + ID_MODEL_FROM_DATABASE=MPC190 + +usb:v04A9p2637* + ID_MODEL_FROM_DATABASE=iR C6800 + +usb:v04A9p2638* + ID_MODEL_FROM_DATABASE=iR C3100 + +usb:v04A9p263C* + ID_MODEL_FROM_DATABASE=Smartbase MP360 + +usb:v04A9p263D* + ID_MODEL_FROM_DATABASE=MP370 + +usb:v04A9p263E* + ID_MODEL_FROM_DATABASE=MP390 FAX + +usb:v04A9p263F* + ID_MODEL_FROM_DATABASE=MP375 + +usb:v04A9p2646* + ID_MODEL_FROM_DATABASE=MF5530 Scanner Device V1.9.1 + +usb:v04A9p2647* + ID_MODEL_FROM_DATABASE=MF5550 Composite + +usb:v04A9p264D* + ID_MODEL_FROM_DATABASE=PIXMA MP710 + +usb:v04A9p264E* + ID_MODEL_FROM_DATABASE=MF5630 + +usb:v04A9p264F* + ID_MODEL_FROM_DATABASE=MF5650 (FAX) + +usb:v04A9p2650* + ID_MODEL_FROM_DATABASE=iR 6800C EUR + +usb:v04A9p2651* + ID_MODEL_FROM_DATABASE=iR 3100C EUR + +usb:v04A9p2655* + ID_MODEL_FROM_DATABASE=FP-L170/MF350/L380/L398 + +usb:v04A9p2656* + ID_MODEL_FROM_DATABASE=iR1510-1670 CAPT Printer + +usb:v04A9p2659* + ID_MODEL_FROM_DATABASE=MF8100 + +usb:v04A9p265B* + ID_MODEL_FROM_DATABASE=CAPT Printer + +usb:v04A9p265C* + ID_MODEL_FROM_DATABASE=iR C3220 + +usb:v04A9p265D* + ID_MODEL_FROM_DATABASE=MF5730 + +usb:v04A9p265E* + ID_MODEL_FROM_DATABASE=MF5750 + +usb:v04A9p265F* + ID_MODEL_FROM_DATABASE=MF5770 + +usb:v04A9p2660* + ID_MODEL_FROM_DATABASE=MF3110 + +usb:v04A9p2663* + ID_MODEL_FROM_DATABASE=iR3570/iR4570 + +usb:v04A9p2664* + ID_MODEL_FROM_DATABASE=iR2270/iR2870 + +usb:v04A9p2665* + ID_MODEL_FROM_DATABASE=iR C2620 + +usb:v04A9p2666* + ID_MODEL_FROM_DATABASE=iR C5800 + +usb:v04A9p2667* + ID_MODEL_FROM_DATABASE=iR85PLUS + +usb:v04A9p2669* + ID_MODEL_FROM_DATABASE=iR105PLUS + +usb:v04A9p266A* + ID_MODEL_FROM_DATABASE=CAPT Device + +usb:v04A9p266B* + ID_MODEL_FROM_DATABASE=iR8070 + +usb:v04A9p266C* + ID_MODEL_FROM_DATABASE=iR9070 + +usb:v04A9p266D* + ID_MODEL_FROM_DATABASE=iR 5800C EUR + +usb:v04A9p266E* + ID_MODEL_FROM_DATABASE=CAPT Device + +usb:v04A9p266F* + ID_MODEL_FROM_DATABASE=iR2230 + +usb:v04A9p2670* + ID_MODEL_FROM_DATABASE=iR3530 + +usb:v04A9p2671* + ID_MODEL_FROM_DATABASE=iR5570/iR6570 + +usb:v04A9p2672* + ID_MODEL_FROM_DATABASE=iR C3170 + +usb:v04A9p2673* + ID_MODEL_FROM_DATABASE=iR 3170C EUR + +usb:v04A9p2674* + ID_MODEL_FROM_DATABASE=L120 + +usb:v04A9p2675* + ID_MODEL_FROM_DATABASE=iR2830 + +usb:v04A9p2676* + ID_MODEL_FROM_DATABASE=CAPT Device + +usb:v04A9p2677* + ID_MODEL_FROM_DATABASE=iR C2570 + +usb:v04A9p2678* + ID_MODEL_FROM_DATABASE=iR 2570C EUR + +usb:v04A9p2679* + ID_MODEL_FROM_DATABASE=CAPT Device + +usb:v04A9p267A* + ID_MODEL_FROM_DATABASE=iR2016 + +usb:v04A9p267B* + ID_MODEL_FROM_DATABASE=iR2020 + +usb:v04A9p267D* + ID_MODEL_FROM_DATABASE=MF7100 series + +usb:v04A9p2684* + ID_MODEL_FROM_DATABASE=MF3200 series + +usb:v04A9p2686* + ID_MODEL_FROM_DATABASE=MF6500 series + +usb:v04A9p2687* + ID_MODEL_FROM_DATABASE=iR4530 + +usb:v04A9p2688* + ID_MODEL_FROM_DATABASE=LBP3460 + +usb:v04A9p268C* + ID_MODEL_FROM_DATABASE=iR C6870 + +usb:v04A9p268D* + ID_MODEL_FROM_DATABASE=iR 6870C EUR + +usb:v04A9p268E* + ID_MODEL_FROM_DATABASE=iR C5870 + +usb:v04A9p268F* + ID_MODEL_FROM_DATABASE=iR 5870C EUR + +usb:v04A9p2691* + ID_MODEL_FROM_DATABASE=iR7105 + +usb:v04A9p26A3* + ID_MODEL_FROM_DATABASE=MF4100 series + +usb:v04A9p26B0* + ID_MODEL_FROM_DATABASE=MF4600 series + +usb:v04A9p26B4* + ID_MODEL_FROM_DATABASE=MF4010 series + +usb:v04A9p26B5* + ID_MODEL_FROM_DATABASE=MF4200 series + +usb:v04A9p26DA* + ID_MODEL_FROM_DATABASE=LBP3010B printer + +usb:v04A9p26E6* + ID_MODEL_FROM_DATABASE=iR1024 + +usb:v04A9p2736* + ID_MODEL_FROM_DATABASE=I-SENSYS MF4550d + +usb:v04A9p2737* + ID_MODEL_FROM_DATABASE=MF4410 + +usb:v04A9p3041* + ID_MODEL_FROM_DATABASE=PowerShot S10 + +usb:v04A9p3042* + ID_MODEL_FROM_DATABASE=CanoScan FS4000US Film Scanner + +usb:v04A9p3043* + ID_MODEL_FROM_DATABASE=PowerShot S20 + +usb:v04A9p3044* + ID_MODEL_FROM_DATABASE=EOS D30 + +usb:v04A9p3045* + ID_MODEL_FROM_DATABASE=PowerShot S100 + +usb:v04A9p3046* + ID_MODEL_FROM_DATABASE=IXY Digital + +usb:v04A9p3047* + ID_MODEL_FROM_DATABASE=Digital IXUS + +usb:v04A9p3048* + ID_MODEL_FROM_DATABASE=PowerShot G1 + +usb:v04A9p3049* + ID_MODEL_FROM_DATABASE=PowerShot Pro90 IS + +usb:v04A9p304A* + ID_MODEL_FROM_DATABASE=CP-10 + +usb:v04A9p304B* + ID_MODEL_FROM_DATABASE=IXY Digital 300 + +usb:v04A9p304C* + ID_MODEL_FROM_DATABASE=PowerShot S300 + +usb:v04A9p304D* + ID_MODEL_FROM_DATABASE=Digital IXUS 300 + +usb:v04A9p304E* + ID_MODEL_FROM_DATABASE=PowerShot A20 + +usb:v04A9p304F* + ID_MODEL_FROM_DATABASE=PowerShot A10 + +usb:v04A9p3050* + ID_MODEL_FROM_DATABASE=PowerShot unknown 1 + +usb:v04A9p3051* + ID_MODEL_FROM_DATABASE=PowerShot S110 + +usb:v04A9p3052* + ID_MODEL_FROM_DATABASE=Digital IXUS V + +usb:v04A9p3055* + ID_MODEL_FROM_DATABASE=PowerShot G2 + +usb:v04A9p3056* + ID_MODEL_FROM_DATABASE=PowerShot S40 + +usb:v04A9p3057* + ID_MODEL_FROM_DATABASE=PowerShot S30 + +usb:v04A9p3058* + ID_MODEL_FROM_DATABASE=PowerShot A40 + +usb:v04A9p3059* + ID_MODEL_FROM_DATABASE=PowerShot A30 + +usb:v04A9p305B* + ID_MODEL_FROM_DATABASE=ZR45MC Digital Camcorder + +usb:v04A9p305C* + ID_MODEL_FROM_DATABASE=PowerShot unknown 2 + +usb:v04A9p3060* + ID_MODEL_FROM_DATABASE=EOS D60 + +usb:v04A9p3061* + ID_MODEL_FROM_DATABASE=PowerShot A100 + +usb:v04A9p3062* + ID_MODEL_FROM_DATABASE=PowerShot A200 + +usb:v04A9p3063* + ID_MODEL_FROM_DATABASE=CP-100 + +usb:v04A9p3065* + ID_MODEL_FROM_DATABASE=PowerShot S200 + +usb:v04A9p3066* + ID_MODEL_FROM_DATABASE=Digital IXUS 330 + +usb:v04A9p3067* + ID_MODEL_FROM_DATABASE=MV550i Digital Video Camera + +usb:v04A9p3069* + ID_MODEL_FROM_DATABASE=PowerShot G3 + +usb:v04A9p306A* + ID_MODEL_FROM_DATABASE=Digital unknown 3 + +usb:v04A9p306B* + ID_MODEL_FROM_DATABASE=MVX2i Digital Video Camera + +usb:v04A9p306C* + ID_MODEL_FROM_DATABASE=PowerShot S45 + +usb:v04A9p306D* + ID_MODEL_FROM_DATABASE=PowerShot S45 PtP Mode + +usb:v04A9p306E* + ID_MODEL_FROM_DATABASE=PowerShot G3 (normal mode) + +usb:v04A9p306F* + ID_MODEL_FROM_DATABASE=PowerShot G3 (ptp) + +usb:v04A9p3070* + ID_MODEL_FROM_DATABASE=PowerShot S230 + +usb:v04A9p3071* + ID_MODEL_FROM_DATABASE=PowerShot S230 (ptp) + +usb:v04A9p3072* + ID_MODEL_FROM_DATABASE=PowerShot SD100 / Digital IXUS II (ptp) + +usb:v04A9p3073* + ID_MODEL_FROM_DATABASE=PowerShot A70 (ptp) + +usb:v04A9p3074* + ID_MODEL_FROM_DATABASE=PowerShot A60 (ptp) + +usb:v04A9p3075* + ID_MODEL_FROM_DATABASE=IXUS 400 Camera + +usb:v04A9p3076* + ID_MODEL_FROM_DATABASE=PowerShot A300 + +usb:v04A9p3077* + ID_MODEL_FROM_DATABASE=PowerShot S50 + +usb:v04A9p3078* + ID_MODEL_FROM_DATABASE=ZR70MC Digital Camcorder + +usb:v04A9p307A* + ID_MODEL_FROM_DATABASE=MV650i (normal mode) + +usb:v04A9p307B* + ID_MODEL_FROM_DATABASE=MV630i Digital Video Camera + +usb:v04A9p307C* + ID_MODEL_FROM_DATABASE=CP-200 + +usb:v04A9p307D* + ID_MODEL_FROM_DATABASE=CP-300 + +usb:v04A9p307F* + ID_MODEL_FROM_DATABASE=Optura 20 + +usb:v04A9p3080* + ID_MODEL_FROM_DATABASE=MVX150i (normal mode) / Optura 20 (normal mode) + +usb:v04A9p3081* + ID_MODEL_FROM_DATABASE=Optura 10 + +usb:v04A9p3082* + ID_MODEL_FROM_DATABASE=MVX100i / Optura 10 + +usb:v04A9p3083* + ID_MODEL_FROM_DATABASE=EOS 10D + +usb:v04A9p3084* + ID_MODEL_FROM_DATABASE=EOS 300D / EOS Digital Rebel + +usb:v04A9p3085* + ID_MODEL_FROM_DATABASE=PowerShot G5 + +usb:v04A9p3087* + ID_MODEL_FROM_DATABASE=Elura 50 (PTP mode) + +usb:v04A9p3088* + ID_MODEL_FROM_DATABASE=Elura 50 (normal mode) + +usb:v04A9p308D* + ID_MODEL_FROM_DATABASE=MVX3i + +usb:v04A9p308E* + ID_MODEL_FROM_DATABASE=FV M1 (normal mode) / MVX 3i (normal mode) / Optura Xi (normal mode) + +usb:v04A9p3093* + ID_MODEL_FROM_DATABASE=Optura 300 + +usb:v04A9p3096* + ID_MODEL_FROM_DATABASE=IXY DV M2 (normal mode) / MVX 10i (normal mode) + +usb:v04A9p3099* + ID_MODEL_FROM_DATABASE=EOS 300D (ptp) + +usb:v04A9p309A* + ID_MODEL_FROM_DATABASE=PowerShot A80 + +usb:v04A9p309B* + ID_MODEL_FROM_DATABASE=Digital IXUS (ptp) + +usb:v04A9p309C* + ID_MODEL_FROM_DATABASE=PowerShot S1 IS + +usb:v04A9p309D* + ID_MODEL_FROM_DATABASE=Powershot Pro 1 + +usb:v04A9p309F* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04A9p30A0* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04A9p30A1* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04A9p30A2* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04A9p30A8* + ID_MODEL_FROM_DATABASE=Elura 60E/Optura 40 (ptp) + +usb:v04A9p30A9* + ID_MODEL_FROM_DATABASE=MVX25i (normal mode) / Optura 40 (normal mode) + +usb:v04A9p30B1* + ID_MODEL_FROM_DATABASE=PowerShot S70 (normal mode) / PowerShot S70 (PTP mode) + +usb:v04A9p30B2* + ID_MODEL_FROM_DATABASE=PowerShot S60 (normal mode) / PowerShot S60 (PTP mode) + +usb:v04A9p30B3* + ID_MODEL_FROM_DATABASE=PowerShot G6 (normal mode) / PowerShot G6 (PTP mode) + +usb:v04A9p30B4* + ID_MODEL_FROM_DATABASE=PowerShot S500 + +usb:v04A9p30B5* + ID_MODEL_FROM_DATABASE=PowerShot A75 + +usb:v04A9p30B6* + ID_MODEL_FROM_DATABASE=Digital IXUS II2 / Digital IXUS II2 (PTP mode) / PowerShot SD110 (PTP mode) / PowerShot SD110 Digital ELPH + +usb:v04A9p30B7* + ID_MODEL_FROM_DATABASE=PowerShot A400 / PowerShot A400 (PTP mode) + +usb:v04A9p30B8* + ID_MODEL_FROM_DATABASE=PowerShot A310 / PowerShot A310 (PTP mode) + +usb:v04A9p30B9* + ID_MODEL_FROM_DATABASE=Powershot A85 + +usb:v04A9p30BA* + ID_MODEL_FROM_DATABASE=PowerShot S410 Digital Elph + +usb:v04A9p30BB* + ID_MODEL_FROM_DATABASE=PowerShot A95 + +usb:v04A9p30BD* + ID_MODEL_FROM_DATABASE=CP-220 + +usb:v04A9p30BE* + ID_MODEL_FROM_DATABASE=CP-330 + +usb:v04A9p30BF* + ID_MODEL_FROM_DATABASE=Digital IXUS 40 + +usb:v04A9p30C0* + ID_MODEL_FROM_DATABASE=Digital IXUS 30 (PTP mode) / PowerShot SD200 (PTP mode) + +usb:v04A9p30C1* + ID_MODEL_FROM_DATABASE=Digital IXUS 50 (normal mode) / IXY Digital 55 (normal mode) / PowerShot A520 (PTP mode) / PowerShot SD400 (normal mode) + +usb:v04A9p30C2* + ID_MODEL_FROM_DATABASE=PowerShot A510 (normal mode) / PowerShot A510 (PTP mode) + +usb:v04A9p30C4* + ID_MODEL_FROM_DATABASE=Digital IXUS i5 (normal mode) / IXY Digital L2 (normal mode) / PowerShot SD20 (normal mode) + +usb:v04A9p30EA* + ID_MODEL_FROM_DATABASE=EOS 1D Mark II (PTP mode) + +usb:v04A9p30EB* + ID_MODEL_FROM_DATABASE=EOS 20D + +usb:v04A9p30EC* + ID_MODEL_FROM_DATABASE=EOS 20D (ptp) + +usb:v04A9p30EE* + ID_MODEL_FROM_DATABASE=EOS 350D + +usb:v04A9p30EF* + ID_MODEL_FROM_DATABASE=EOS 350D (ptp) + +usb:v04A9p30F0* + ID_MODEL_FROM_DATABASE=PowerShot S2 IS (PTP mode) + +usb:v04A9p30F2* + ID_MODEL_FROM_DATABASE=Digital IXUS 700 (normal mode) / Digital IXUS 700 (PTP mode) / IXY Digital 600 (normal mode) / PowerShot SD500 (normal mode) / PowerShot SD500 (PTP mode) + +usb:v04A9p30F4* + ID_MODEL_FROM_DATABASE=PowerShot SD30 / Ixus iZoom / IXY DIGITAL L3 + +usb:v04A9p30F5* + ID_MODEL_FROM_DATABASE=SELPHY CP500 + +usb:v04A9p30F6* + ID_MODEL_FROM_DATABASE=SELPHY CP400 + +usb:v04A9p30F8* + ID_MODEL_FROM_DATABASE=Powershot A430 + +usb:v04A9p30F9* + ID_MODEL_FROM_DATABASE=PowerShot A410 (PTP mode) + +usb:v04A9p30FA* + ID_MODEL_FROM_DATABASE=PowerShot S80 + +usb:v04A9p30FC* + ID_MODEL_FROM_DATABASE=PowerShot A620 (PTP mode) + +usb:v04A9p30FD* + ID_MODEL_FROM_DATABASE=PowerShot A610 (normal mode)/PowerShot A610 (PTP mode) + +usb:v04A9p30FE* + ID_MODEL_FROM_DATABASE=Digital IXUS 65 (PTP mode)/PowerShot SD630 (PTP mode) + +usb:v04A9p30FF* + ID_MODEL_FROM_DATABASE=Digital IXUS 55 (PTP mode)/PowerShot SD450 (PTP mode) + +usb:v04A9p3100* + ID_MODEL_FROM_DATABASE=PowerShot TX1 + +usb:v04A9p310B* + ID_MODEL_FROM_DATABASE=SELPHY CP600 + +usb:v04A9p310E* + ID_MODEL_FROM_DATABASE=Digital IXUS 50 (PTP mode) + +usb:v04A9p310F* + ID_MODEL_FROM_DATABASE=PowerShot A420 + +usb:v04A9p3110* + ID_MODEL_FROM_DATABASE=EOS Digital Rebel XTi + +usb:v04A9p3115* + ID_MODEL_FROM_DATABASE=PowerShot SD900 / Digital IXUS 900 Ti / IXY DIGITAL 1000 + +usb:v04A9p3116* + ID_MODEL_FROM_DATABASE=Digital IXUS 750 / PowerShot SD550 (PTP mode) + +usb:v04A9p3117* + ID_MODEL_FROM_DATABASE=PowerShot A700 + +usb:v04A9p3119* + ID_MODEL_FROM_DATABASE=PowerShot SD700 IS / Digital IXUS 800 IS / IXY Digital 800 IS + +usb:v04A9p311A* + ID_MODEL_FROM_DATABASE=PowerShot S3 IS + +usb:v04A9p311B* + ID_MODEL_FROM_DATABASE=PowerShot A540 + +usb:v04A9p311C* + ID_MODEL_FROM_DATABASE=PowerShot SD600 DIGITAL ELPH / DIGITAL IXUS 60 / IXY DIGITAL 70 + +usb:v04A9p3125* + ID_MODEL_FROM_DATABASE=PowerShot G7 + +usb:v04A9p3126* + ID_MODEL_FROM_DATABASE=PowerShot A530 + +usb:v04A9p3127* + ID_MODEL_FROM_DATABASE=SELPHY CP710 + +usb:v04A9p3128* + ID_MODEL_FROM_DATABASE=SELPHY CP510 + +usb:v04A9p312D* + ID_MODEL_FROM_DATABASE=Elura 100 + +usb:v04A9p3136* + ID_MODEL_FROM_DATABASE=PowerShot SD800 IS / Digital IXUS 850 IS / IXY DIGITAL 900 IS + +usb:v04A9p3137* + ID_MODEL_FROM_DATABASE=PowerShot SD40 / Digital IXUS i7 IXY / DIGITAL L4 + +usb:v04A9p3138* + ID_MODEL_FROM_DATABASE=PowerShot A710 IS + +usb:v04A9p3139* + ID_MODEL_FROM_DATABASE=PowerShot A640 + +usb:v04A9p313A* + ID_MODEL_FROM_DATABASE=PowerShot A630 + +usb:v04A9p3141* + ID_MODEL_FROM_DATABASE=SELPHY ES1 + +usb:v04A9p3142* + ID_MODEL_FROM_DATABASE=SELPHY CP730 + +usb:v04A9p3143* + ID_MODEL_FROM_DATABASE=SELPHY CP720 + +usb:v04A9p3145* + ID_MODEL_FROM_DATABASE=EOS 450D + +usb:v04A9p3146* + ID_MODEL_FROM_DATABASE=EOS 40D + +usb:v04A9p3147* + ID_MODEL_FROM_DATABASE=EOS 1Ds Mark III + +usb:v04A9p3148* + ID_MODEL_FROM_DATABASE=PowerShot S5 IS + +usb:v04A9p3149* + ID_MODEL_FROM_DATABASE=PowerShot A460 + +usb:v04A9p314B* + ID_MODEL_FROM_DATABASE=PowerShot SD850 IS DIGITAL ELPH / Digital IXUS 950 IS / IXY DIGITAL 810 IS + +usb:v04A9p314C* + ID_MODEL_FROM_DATABASE=PowerShot A570 IS + +usb:v04A9p314D* + ID_MODEL_FROM_DATABASE=PowerShot A560 + +usb:v04A9p314E* + ID_MODEL_FROM_DATABASE=PowerShot SD750 DIGITAL ELPH / DIGITAL IXUS 75 / IXY DIGITAL 90 + +usb:v04A9p314F* + ID_MODEL_FROM_DATABASE=PowerShot SD1000 DIGITAL ELPH / DIGITAL IXUS 70 / IXY DIGITAL 10 + +usb:v04A9p3150* + ID_MODEL_FROM_DATABASE=PowerShot A550 + +usb:v04A9p3155* + ID_MODEL_FROM_DATABASE=PowerShot A450 + +usb:v04A9p315A* + ID_MODEL_FROM_DATABASE=PowerShot G9 + +usb:v04A9p315B* + ID_MODEL_FROM_DATABASE=PowerShot A650 IS + +usb:v04A9p315D* + ID_MODEL_FROM_DATABASE=PowerShot A720 + +usb:v04A9p315E* + ID_MODEL_FROM_DATABASE=PowerShot SX100 IS + +usb:v04A9p315F* + ID_MODEL_FROM_DATABASE=PowerShot SD950 IS DIGITAL ELPH / DIGITAL IXUS 960 IS / IXY DIGITAL 2000 IS + +usb:v04A9p3160* + ID_MODEL_FROM_DATABASE=Digital IXUS 860 IS + +usb:v04A9p3170* + ID_MODEL_FROM_DATABASE=SELPHY CP750 + +usb:v04A9p3171* + ID_MODEL_FROM_DATABASE=SELPHY CP740 + +usb:v04A9p3172* + ID_MODEL_FROM_DATABASE=SELPHY CP520 + +usb:v04A9p3173* + ID_MODEL_FROM_DATABASE=PowerShot SD890 IS DIGITAL ELPH / Digital IXUS 970 IS / IXY DIGITAL 820 IS + +usb:v04A9p3174* + ID_MODEL_FROM_DATABASE=PowerShot SD790 IS DIGITAL ELPH / Digital IXUS 90 IS / IXY DIGITAL 95 IS + +usb:v04A9p3175* + ID_MODEL_FROM_DATABASE=IXY Digital 25 IS + +usb:v04A9p3176* + ID_MODEL_FROM_DATABASE=PowerShot A590 + +usb:v04A9p3177* + ID_MODEL_FROM_DATABASE=PowerShot A580 + +usb:v04A9p317A* + ID_MODEL_FROM_DATABASE=PC1267 [Powershot A470] + +usb:v04A9p3184* + ID_MODEL_FROM_DATABASE=Digital IXUS 80 IS (PTP mode) + +usb:v04A9p3185* + ID_MODEL_FROM_DATABASE=SELPHY ES2 + +usb:v04A9p3186* + ID_MODEL_FROM_DATABASE=SELPHY ES20 + +usb:v04A9p318D* + ID_MODEL_FROM_DATABASE=PowerShot SX100 IS + +usb:v04A9p318E* + ID_MODEL_FROM_DATABASE=PowerShot A1000 IS + +usb:v04A9p318F* + ID_MODEL_FROM_DATABASE=PowerShot G10 + +usb:v04A9p3191* + ID_MODEL_FROM_DATABASE=PowerShot A2000 IS + +usb:v04A9p3192* + ID_MODEL_FROM_DATABASE=PowerShot SX110 IS + +usb:v04A9p3193* + ID_MODEL_FROM_DATABASE=PowerShot SD990 IS DIGITAL ELPH / Digital IXUS 980 IS / IXY DIGITAL 3000 IS + +usb:v04A9p3195* + ID_MODEL_FROM_DATABASE=PowerShot SX1 IS + +usb:v04A9p3196* + ID_MODEL_FROM_DATABASE=PowerShot SD880 IS DIGITAL ELPH / Digital IXUS 870 IS / IXY DIGITAL 920 IS + +usb:v04A9p319A* + ID_MODEL_FROM_DATABASE=EOS 7D + +usb:v04A9p319B* + ID_MODEL_FROM_DATABASE=EOS 50D + +usb:v04A9p31AA* + ID_MODEL_FROM_DATABASE=SELPHY CP770 + +usb:v04A9p31AB* + ID_MODEL_FROM_DATABASE=SELPHY CP760 + +usb:v04A9p31AD* + ID_MODEL_FROM_DATABASE=PowerShot E1 + +usb:v04A9p31AF* + ID_MODEL_FROM_DATABASE=SELPHY ES3 + +usb:v04A9p31B0* + ID_MODEL_FROM_DATABASE=SELPHY ES30 + +usb:v04A9p31B1* + ID_MODEL_FROM_DATABASE=SELPHY CP530 + +usb:v04A9p31BC* + ID_MODEL_FROM_DATABASE=PowerShot D10 + +usb:v04A9p31BD* + ID_MODEL_FROM_DATABASE=PowerShot SD960 IS DIGITAL ELPH / Digital IXUS 110 IS / IXY DIGITAL 510 IS + +usb:v04A9p31BE* + ID_MODEL_FROM_DATABASE=PowerShot A2100 IS + +usb:v04A9p31BF* + ID_MODEL_FROM_DATABASE=PowerShot A480 + +usb:v04A9p31C0* + ID_MODEL_FROM_DATABASE=PowerShot SX200 IS + +usb:v04A9p31C1* + ID_MODEL_FROM_DATABASE=PowerShot SD970 IS DIGITAL ELPH / Digital IXUS 990 IS / IXY DIGITAL 830 IS + +usb:v04A9p31C2* + ID_MODEL_FROM_DATABASE=PowerShot SD780 IS DIGITAL ELPH / Digital IXUS 100 IS / IXY DIGITAL 210 IS + +usb:v04A9p31C3* + ID_MODEL_FROM_DATABASE=PowerShot A1100 IS + +usb:v04A9p31C4* + ID_MODEL_FROM_DATABASE=PowerShot SD1200 IS DIGITAL ELPH / Digital IXUS 95 IS / IXY DIGITAL 110 IS + +usb:v04A9p31CF* + ID_MODEL_FROM_DATABASE=EOS Rebel T1i / EOS 500D / EOS Kiss X3 + +usb:v04A9p31DD* + ID_MODEL_FROM_DATABASE=SELPHY CP780 + +usb:v04A9p31DF* + ID_MODEL_FROM_DATABASE=PowerShot G11 + +usb:v04A9p31E0* + ID_MODEL_FROM_DATABASE=PowerShot SX120 IS + +usb:v04A9p31E1* + ID_MODEL_FROM_DATABASE=PowerShot S90 + +usb:v04A9p31E4* + ID_MODEL_FROM_DATABASE=PowerShot SX20 IS + +usb:v04A9p31E5* + ID_MODEL_FROM_DATABASE=Digital IXUS 200 IS + +usb:v04A9p31E6* + ID_MODEL_FROM_DATABASE=PowerShot SD940 IS DIGITAL ELPH / Digital IXUS 120 IS / IXY DIGITAL 220 IS + +usb:v04A9p31E7* + ID_MODEL_FROM_DATABASE=SELPHY CP790 + +usb:v04A9p31EA* + ID_MODEL_FROM_DATABASE=EOS Rebel T2i / EOS 550D / EOS Kiss X4 + +usb:v04A9p31EE* + ID_MODEL_FROM_DATABASE=SELPHY ES40 + +usb:v04A9p31EF* + ID_MODEL_FROM_DATABASE=PowerShot A495 + +usb:v04A9p31F0* + ID_MODEL_FROM_DATABASE=PowerShot A490 + +usb:v04A9p31F1* + ID_MODEL_FROM_DATABASE=PowerShot A3100 IS / PowerShot A3150 IS + +usb:v04A9p31F2* + ID_MODEL_FROM_DATABASE=PowerShot A3000 IS + +usb:v04A9p31F3* + ID_MODEL_FROM_DATABASE=PowerShot Digital ELPH SD1400 IS + +usb:v04A9p31F4* + ID_MODEL_FROM_DATABASE=PowerShot SD1300 IS / IXUS 105 + +usb:v04A9p31F5* + ID_MODEL_FROM_DATABASE=Powershot SD3500 IS / IXUS 210 IS + +usb:v04A9p31F6* + ID_MODEL_FROM_DATABASE=PowerShot SX210 IS + +usb:v04A9p31F7* + ID_MODEL_FROM_DATABASE=Powershot SD4000 IS / IXUS 300 HS / IXY 30S + +usb:v04A9p31F8* + ID_MODEL_FROM_DATABASE=Powershot SD4500 IS / IXUS 1000 HS / IXY 50S + +usb:v04A9p31FF* + ID_MODEL_FROM_DATABASE=Digital IXUS 55 + +usb:v04A9p3209* + ID_MODEL_FROM_DATABASE=Vixia HF S21 A + +usb:v04A9p320F* + ID_MODEL_FROM_DATABASE=PowerShot G12 + +usb:v04A9p3210* + ID_MODEL_FROM_DATABASE=Powershot SX30 IS + +usb:v04A9p3211* + ID_MODEL_FROM_DATABASE=PowerShot SX130 IS + +usb:v04A9p3212* + ID_MODEL_FROM_DATABASE=Powershot S95 + +usb:v04A9p3214* + ID_MODEL_FROM_DATABASE=SELPHY CP800 + +usb:v04A9p3218* + ID_MODEL_FROM_DATABASE=EOS 600D / Rebel T3i (ptp) + +usb:v04A9p3223* + ID_MODEL_FROM_DATABASE=PowerShot A3300 IS + +usb:v04A9p3224* + ID_MODEL_FROM_DATABASE=PowerShot A3200 IS + +usb:v04A9p3225* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 500 HS / IXUS 310 HS + +usb:v04A9p3226* + ID_MODEL_FROM_DATABASE=PowerShow A800 + +usb:v04A9p3227* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 100 HS / IXUS 115 HS + +usb:v04A9p3228* + ID_MODEL_FROM_DATABASE=PowerShot SX230 HS + +usb:v04A9p3229* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 300 HS / IXUS 220 HS + +usb:v04A9p322A* + ID_MODEL_FROM_DATABASE=PowerShot A2200 + +usb:v04A9p322B* + ID_MODEL_FROM_DATABASE=Powershot A1200 + +usb:v04A9p322C* + ID_MODEL_FROM_DATABASE=PowerShot SX220 HS + +usb:v04A9p3233* + ID_MODEL_FROM_DATABASE=PowerShot G1 X + +usb:v04A9p3234* + ID_MODEL_FROM_DATABASE=PowerShot SX150 IS + +usb:v04A9p3235* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 510 HS / IXUS 1100 HS + +usb:v04A9p3236* + ID_MODEL_FROM_DATABASE=PowerShot S100 + +usb:v04A9p3237* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 310 HS / IXUS 230 HS + +usb:v04A9p3238* + ID_MODEL_FROM_DATABASE=PowerShot SX40 HS + +usb:v04A9p323B* + ID_MODEL_FROM_DATABASE=EOS Rebel T4i + +usb:v04A9p323E* + ID_MODEL_FROM_DATABASE=PowerShot A1300 + +usb:v04A9p323F* + ID_MODEL_FROM_DATABASE=PowerShot A810 + +usb:v04A9p3240* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 320 HS / IXUS 240 HS + +usb:v04A9p3241* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 110 HS / IXUS 125 HS + +usb:v04A9p3242* + ID_MODEL_FROM_DATABASE=PowerShot D20 + +usb:v04A9p3243* + ID_MODEL_FROM_DATABASE=PowerShot A4000 IS + +usb:v04A9p3244* + ID_MODEL_FROM_DATABASE=PowerShot SX260 HS + +usb:v04A9p3245* + ID_MODEL_FROM_DATABASE=PowerShot SX240 HS + +usb:v04A9p3247* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 520 HS / IXUS 500 HS + +usb:v04A9p3248* + ID_MODEL_FROM_DATABASE=PowerShot A3400 IS + +usb:v04A9p3249* + ID_MODEL_FROM_DATABASE=PowerShot A2400 IS + +usb:v04A9p324A* + ID_MODEL_FROM_DATABASE=PowerShot A2300 + +usb:v04A9p3255* + ID_MODEL_FROM_DATABASE=SELPHY CP900 + +usb:v04A9p3256* + ID_MODEL_FROM_DATABASE=SELPHY CP810 + +usb:v04A9p3258* + ID_MODEL_FROM_DATABASE=PowerShot G15 + +usb:v04A9p3259* + ID_MODEL_FROM_DATABASE=PowerShot SX50 HS + +usb:v04A9p325A* + ID_MODEL_FROM_DATABASE=PowerShot SX160 IS + +usb:v04A9p325B* + ID_MODEL_FROM_DATABASE=PowerShot S110 + +usb:v04A9p325C* + ID_MODEL_FROM_DATABASE=PowerShot SX500 IS + +usb:v04A9p325E* + ID_MODEL_FROM_DATABASE=PowerShot N + +usb:v04A9p325F* + ID_MODEL_FROM_DATABASE=PowerShot SX280 HS + +usb:v04A9p3260* + ID_MODEL_FROM_DATABASE=PowerShot SX270 HS + +usb:v04A9p3261* + ID_MODEL_FROM_DATABASE=PowerShot A3500 IS + +usb:v04A9p3262* + ID_MODEL_FROM_DATABASE=PowerShot A2600 + +usb:v04A9p3263* + ID_MODEL_FROM_DATABASE=PowerShot SX275 HS + +usb:v04A9p3264* + ID_MODEL_FROM_DATABASE=PowerShot A1400 + +usb:v04A9p3265* + ID_MODEL_FROM_DATABASE=Powershot ELPH 130 IS / IXUS 140 + +usb:v04A9p3266* + ID_MODEL_FROM_DATABASE=Powershot ELPH 120 IS / IXUS 135 + +usb:v04A9p3268* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 330 HS / IXUS 255 HS + +usb:v04A9p3271* + ID_MODEL_FROM_DATABASE=PowerShot A2500 + +usb:v04A9p3276* + ID_MODEL_FROM_DATABASE=PowerShot SX170 IS + +usb:v04A9p3277* + ID_MODEL_FROM_DATABASE=PowerShot SX510 HS + +usb:v04A9p3278* + ID_MODEL_FROM_DATABASE=PowerShot S200 + +usb:v04A9p327D* + ID_MODEL_FROM_DATABASE=Powershot ELPH 115 IS / IXUS 132 + +usb:v04A9p327F* + ID_MODEL_FROM_DATABASE=EOS Rebel T5 / EOS 1200D / EOS Kiss X70 + +usb:v04A9p3284* + ID_MODEL_FROM_DATABASE=PowerShot D30 + +usb:v04A9p3285* + ID_MODEL_FROM_DATABASE=PowerShot SX700 HS + +usb:v04A9p3286* + ID_MODEL_FROM_DATABASE=PowerShot SX600 HS + +usb:v04A9p3287* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 140 IS / IXUS 150 + +usb:v04A9p3288* + ID_MODEL_FROM_DATABASE=Powershot ELPH 135 / IXUS 145 + +usb:v04A9p3289* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 340 HS / IXUS 265 HS + +usb:v04A9p328A* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 150 IS / IXUS 155 + +usb:v04A9p328B* + ID_MODEL_FROM_DATABASE=PowerShot N Facebook(R) Ready + +usb:v04A9p3299* + ID_MODEL_FROM_DATABASE=EOS M3 + +usb:v04A9p329A* + ID_MODEL_FROM_DATABASE=PowerShot SX60 HS + +usb:v04A9p329B* + ID_MODEL_FROM_DATABASE=PowerShot SX520 HS + +usb:v04A9p329C* + ID_MODEL_FROM_DATABASE=PowerShot SX400 IS + +usb:v04A9p329D* + ID_MODEL_FROM_DATABASE=PowerShot G7 X + +usb:v04A9p329F* + ID_MODEL_FROM_DATABASE=PowerShot SX530 HS + +usb:v04A9p32A6* + ID_MODEL_FROM_DATABASE=PowerShot SX710 HS + +usb:v04A9p32AA* + ID_MODEL_FROM_DATABASE=Powershot ELPH 160 / IXUS 160 + +usb:v04A9p32AC* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 170 IS / IXUS 170 + +usb:v04A9p32AD* + ID_MODEL_FROM_DATABASE=PowerShot SX410 IS + +usb:v04A9p32C1* + ID_MODEL_FROM_DATABASE=PowerShot ELPH 180 / IXUS 175 + +usb:v04AA* + ID_VENDOR_FROM_DATABASE=DaeWoo Telecom, Ltd + +usb:v04AB* + ID_VENDOR_FROM_DATABASE=Chromatic Research + +usb:v04AC* + ID_VENDOR_FROM_DATABASE=Micro Audiometrics Corp. + +usb:v04AD* + ID_VENDOR_FROM_DATABASE=Dooin Electronics + +usb:v04ADp2501* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v04AF* + ID_VENDOR_FROM_DATABASE=Winnov L.P. + +usb:v04B0* + ID_VENDOR_FROM_DATABASE=Nikon Corp. + +usb:v04B0p0102* + ID_MODEL_FROM_DATABASE=Coolpix 990 + +usb:v04B0p0103* + ID_MODEL_FROM_DATABASE=Coolpix 880 + +usb:v04B0p0104* + ID_MODEL_FROM_DATABASE=Coolpix 995 + +usb:v04B0p0106* + ID_MODEL_FROM_DATABASE=Coolpix 775 + +usb:v04B0p0107* + ID_MODEL_FROM_DATABASE=Coolpix 5000 + +usb:v04B0p0108* + ID_MODEL_FROM_DATABASE=Coolpix 2500 + +usb:v04B0p0109* + ID_MODEL_FROM_DATABASE=Coolpix 2500 (ptp) + +usb:v04B0p010A* + ID_MODEL_FROM_DATABASE=Coolpix 4500 + +usb:v04B0p010B* + ID_MODEL_FROM_DATABASE=Coolpix 4500 (ptp) + +usb:v04B0p010D* + ID_MODEL_FROM_DATABASE=Coolpix 5700 (ptp) + +usb:v04B0p010E* + ID_MODEL_FROM_DATABASE=Coolpix 4300 (storage) + +usb:v04B0p010F* + ID_MODEL_FROM_DATABASE=Coolpix 4300 (ptp) + +usb:v04B0p0110* + ID_MODEL_FROM_DATABASE=Coolpix 3500 (Sierra Mode) + +usb:v04B0p0111* + ID_MODEL_FROM_DATABASE=Coolpix 3500 (ptp) + +usb:v04B0p0112* + ID_MODEL_FROM_DATABASE=Coolpix 885 (ptp) + +usb:v04B0p0113* + ID_MODEL_FROM_DATABASE=Coolpix 5000 (ptp) + +usb:v04B0p0114* + ID_MODEL_FROM_DATABASE=Coolpix 3100 (storage) + +usb:v04B0p0115* + ID_MODEL_FROM_DATABASE=Coolpix 3100 (ptp) + +usb:v04B0p0117* + ID_MODEL_FROM_DATABASE=Coolpix 2100 (ptp) + +usb:v04B0p0119* + ID_MODEL_FROM_DATABASE=Coolpix 5400 (ptp) + +usb:v04B0p011D* + ID_MODEL_FROM_DATABASE=Coolpix 3700 (ptp) + +usb:v04B0p0121* + ID_MODEL_FROM_DATABASE=Coolpix 3200 (ptp) + +usb:v04B0p0122* + ID_MODEL_FROM_DATABASE=Coolpix 2200 (ptp) + +usb:v04B0p0124* + ID_MODEL_FROM_DATABASE=Coolpix 8400 (mass storage mode) + +usb:v04B0p0125* + ID_MODEL_FROM_DATABASE=Coolpix 8400 (ptp) + +usb:v04B0p0126* + ID_MODEL_FROM_DATABASE=Coolpix 8800 + +usb:v04B0p0129* + ID_MODEL_FROM_DATABASE=Coolpix 4800 (ptp) + +usb:v04B0p012C* + ID_MODEL_FROM_DATABASE=Coolpix 4100 (storage) + +usb:v04B0p012D* + ID_MODEL_FROM_DATABASE=Coolpix 4100 (ptp) + +usb:v04B0p012E* + ID_MODEL_FROM_DATABASE=Coolpix 5600 (ptp) + +usb:v04B0p0130* + ID_MODEL_FROM_DATABASE=Coolpix 4600 (ptp) + +usb:v04B0p0135* + ID_MODEL_FROM_DATABASE=Coolpix 5900 (ptp) + +usb:v04B0p0136* + ID_MODEL_FROM_DATABASE=Coolpix 7900 (storage) + +usb:v04B0p0137* + ID_MODEL_FROM_DATABASE=Coolpix 7900 (ptp) + +usb:v04B0p013A* + ID_MODEL_FROM_DATABASE=Coolpix 100 (storage) + +usb:v04B0p013B* + ID_MODEL_FROM_DATABASE=Coolpix 100 (ptp) + +usb:v04B0p0141* + ID_MODEL_FROM_DATABASE=Coolpix P2 (storage) + +usb:v04B0p0142* + ID_MODEL_FROM_DATABASE=Coolpix P2 (ptp) + +usb:v04B0p0163* + ID_MODEL_FROM_DATABASE=Coolpix P5100 (ptp) + +usb:v04B0p0169* + ID_MODEL_FROM_DATABASE=Coolpix P50 (ptp) + +usb:v04B0p0202* + ID_MODEL_FROM_DATABASE=Coolpix SQ (ptp) + +usb:v04B0p0203* + ID_MODEL_FROM_DATABASE=Coolpix 4200 (mass storage mode) + +usb:v04B0p0204* + ID_MODEL_FROM_DATABASE=Coolpix 4200 (ptp) + +usb:v04B0p0205* + ID_MODEL_FROM_DATABASE=Coolpix 5200 (storage) + +usb:v04B0p0206* + ID_MODEL_FROM_DATABASE=Coolpix 5200 (ptp) + +usb:v04B0p0301* + ID_MODEL_FROM_DATABASE=Coolpix 2000 (storage) + +usb:v04B0p0302* + ID_MODEL_FROM_DATABASE=Coolpix 2000 (ptp) + +usb:v04B0p0317* + ID_MODEL_FROM_DATABASE=Coolpix L20 (ptp) + +usb:v04B0p0402* + ID_MODEL_FROM_DATABASE=DSC D100 (ptp) + +usb:v04B0p0403* + ID_MODEL_FROM_DATABASE=D2H (mass storage mode) + +usb:v04B0p0404* + ID_MODEL_FROM_DATABASE=D2H SLR (ptp) + +usb:v04B0p0405* + ID_MODEL_FROM_DATABASE=D70 (mass storage mode) + +usb:v04B0p0406* + ID_MODEL_FROM_DATABASE=DSC D70 (ptp) + +usb:v04B0p0408* + ID_MODEL_FROM_DATABASE=D2X SLR (ptp) + +usb:v04B0p0409* + ID_MODEL_FROM_DATABASE=D50 digital camera + +usb:v04B0p040A* + ID_MODEL_FROM_DATABASE=D50 (ptp) + +usb:v04B0p040C* + ID_MODEL_FROM_DATABASE=D2Hs + +usb:v04B0p040E* + ID_MODEL_FROM_DATABASE=DSC D70s (ptp) + +usb:v04B0p040F* + ID_MODEL_FROM_DATABASE=D200 (mass storage mode) + +usb:v04B0p0410* + ID_MODEL_FROM_DATABASE=D200 (ptp) + +usb:v04B0p0413* + ID_MODEL_FROM_DATABASE=D40 (mass storage mode) + +usb:v04B0p041E* + ID_MODEL_FROM_DATABASE=D60 digital camera (mass storage mode) + +usb:v04B0p0422* + ID_MODEL_FROM_DATABASE=D700 (ptp) + +usb:v04B0p0423* + ID_MODEL_FROM_DATABASE=D5000 + +usb:v04B0p0424* + ID_MODEL_FROM_DATABASE=D3000 + +usb:v04B0p0425* + ID_MODEL_FROM_DATABASE=D300S + +usb:v04B0p0428* + ID_MODEL_FROM_DATABASE=D7000 + +usb:v04B0p0429* + ID_MODEL_FROM_DATABASE=D5100 + +usb:v04B0p042A* + ID_MODEL_FROM_DATABASE=D800 (ptp) + +usb:v04B0p0F03* + ID_MODEL_FROM_DATABASE=PD-10 Wireless Printer Adapter + +usb:v04B0p4000* + ID_MODEL_FROM_DATABASE=Coolscan LS 40 ED + +usb:v04B0p4001* + ID_MODEL_FROM_DATABASE=LS 50 ED/Coolscan V ED + +usb:v04B0p4002* + ID_MODEL_FROM_DATABASE=Super Coolscan LS-5000 ED + +usb:v04B1* + ID_VENDOR_FROM_DATABASE=Pan International + +usb:v04B3* + ID_VENDOR_FROM_DATABASE=IBM Corp. + +usb:v04B3p3003* + ID_MODEL_FROM_DATABASE=Rapid Access III Keyboard + +usb:v04B3p3004* + ID_MODEL_FROM_DATABASE=Media Access Pro Keyboard + +usb:v04B3p300A* + ID_MODEL_FROM_DATABASE=Rapid Access IIIe Keyboard + +usb:v04B3p3016* + ID_MODEL_FROM_DATABASE=UltraNav Keyboard Hub + +usb:v04B3p3018* + ID_MODEL_FROM_DATABASE=UltraNav Keyboard + +usb:v04B3p301A* + ID_MODEL_FROM_DATABASE=2-port low-power hub + +usb:v04B3p301B* + ID_MODEL_FROM_DATABASE=SK-8815 Keyboard + +usb:v04B3p301C* + ID_MODEL_FROM_DATABASE=Enhanced Performance Keyboard + +usb:v04B3p3020* + ID_MODEL_FROM_DATABASE=Enhanced Performance Keyboard + +usb:v04B3p3025* + ID_MODEL_FROM_DATABASE=NetVista Full Width Keyboard + +usb:v04B3p3100* + ID_MODEL_FROM_DATABASE=NetVista Mouse + +usb:v04B3p3103* + ID_MODEL_FROM_DATABASE=ScrollPoint Pro Mouse + +usb:v04B3p3104* + ID_MODEL_FROM_DATABASE=ScrollPoint Wireless Mouse + +usb:v04B3p3105* + ID_MODEL_FROM_DATABASE=ScrollPoint Optical (HID) + +usb:v04B3p3107* + ID_MODEL_FROM_DATABASE=ThinkPad 800dpi Optical Travel Mouse + +usb:v04B3p3108* + ID_MODEL_FROM_DATABASE=800dpi Optical Mouse w/ Scroll Point + +usb:v04B3p3109* + ID_MODEL_FROM_DATABASE=Optical ScrollPoint Pro Mouse + +usb:v04B3p310B* + ID_MODEL_FROM_DATABASE=Red Wheel Mouse + +usb:v04B3p310C* + ID_MODEL_FROM_DATABASE=Wheel Mouse + +usb:v04B3p4427* + ID_MODEL_FROM_DATABASE=Portable CD ROM + +usb:v04B3p4482* + ID_MODEL_FROM_DATABASE=Serial Converter + +usb:v04B3p4484* + ID_MODEL_FROM_DATABASE=SMSC USB20H04 3-Port Hub [ThinkPad X4 UltraBase, Wistron S Note-3 Media Slice] + +usb:v04B3p4485* + ID_MODEL_FROM_DATABASE=ThinkPad Dock Hub + +usb:v04B3p4524* + ID_MODEL_FROM_DATABASE=40 Character Vacuum Fluorescent Display + +usb:v04B3p4525* + ID_MODEL_FROM_DATABASE=Double sided CRT + +usb:v04B3p4535* + ID_MODEL_FROM_DATABASE=4610 Suremark Printer + +usb:v04B3p4550* + ID_MODEL_FROM_DATABASE=NVRAM (128 KB) + +usb:v04B3p4554* + ID_MODEL_FROM_DATABASE=Cash Drawer + +usb:v04B3p4580* + ID_MODEL_FROM_DATABASE=Hub w/ NVRAM + +usb:v04B3p4581* + ID_MODEL_FROM_DATABASE=4800-2xx Hub w/ Cash Drawer + +usb:v04B3p4604* + ID_MODEL_FROM_DATABASE=Keyboard w/ Card Reader + +usb:v04B3p4671* + ID_MODEL_FROM_DATABASE=4820 LCD w/ MSR/KB + +usb:v04B4* + ID_VENDOR_FROM_DATABASE=Cypress Semiconductor Corp. + +usb:v04B4p0001* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v04B4p0002* + ID_MODEL_FROM_DATABASE=CY7C63x0x Thermometer + +usb:v04B4p0033* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v04B4p0060* + ID_MODEL_FROM_DATABASE=Wireless optical mouse + +usb:v04B4p0100* + ID_MODEL_FROM_DATABASE=Cino FuzzyScan F760-B + +usb:v04B4p0101* + ID_MODEL_FROM_DATABASE=Keyboard/Hub + +usb:v04B4p0102* + ID_MODEL_FROM_DATABASE=Keyboard with APM + +usb:v04B4p0130* + ID_MODEL_FROM_DATABASE=MyIRC Remote Receiver + +usb:v04B4p0306* + ID_MODEL_FROM_DATABASE=Telephone Receiver + +usb:v04B4p0407* + ID_MODEL_FROM_DATABASE=Optical Skype Mouse + +usb:v04B4p0BAD* + ID_MODEL_FROM_DATABASE=MetaGeek Wi-Spy + +usb:v04B4p1002* + ID_MODEL_FROM_DATABASE=CY7C63001 R100 FM Radio + +usb:v04B4p1006* + ID_MODEL_FROM_DATABASE=Human Interface Device + +usb:v04B4p2050* + ID_MODEL_FROM_DATABASE=hub + +usb:v04B4p2830* + ID_MODEL_FROM_DATABASE=Opera1 DVB-S (cold state) + +usb:v04B4p3813* + ID_MODEL_FROM_DATABASE=NANO BIOS Programmer + +usb:v04B4p4235* + ID_MODEL_FROM_DATABASE=Monitor 02 Driver + +usb:v04B4p4381* + ID_MODEL_FROM_DATABASE=SCAPS USC-1 Scanner Controller + +usb:v04B4p4611* + ID_MODEL_FROM_DATABASE=Storage Adapter FX2 (CY) + +usb:v04B4p4616* + ID_MODEL_FROM_DATABASE=Flash Disk (TPP) + +usb:v04B4p5201* + ID_MODEL_FROM_DATABASE=Combi Keyboard-Hub (Hub) + +usb:v04B4p5202* + ID_MODEL_FROM_DATABASE=Combi Keyboard-Hub (Keyboard) + +usb:v04B4p5500* + ID_MODEL_FROM_DATABASE=HID->COM RS232 Adapter + +usb:v04B4p5A9B* + ID_MODEL_FROM_DATABASE=Dacal CD/DVD Library D-101/DC-300/DC-016RW + +usb:v04B4p6370* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v04B4p6560* + ID_MODEL_FROM_DATABASE=CY7C65640 USB-2.0 "TetraHub" + +usb:v04B4p6830* + ID_MODEL_FROM_DATABASE=CY7C68300A EZ-USB AT2 USB 2.0 to ATA/ATAPI + +usb:v04B4p6831* + ID_MODEL_FROM_DATABASE=Storage Adapter ISD-300LP (CY) + +usb:v04B4p7417* + ID_MODEL_FROM_DATABASE=Wireless PC Lock/Ultra Mouse + +usb:v04B4p8329* + ID_MODEL_FROM_DATABASE=USB To keyboard/Mouse Converter + +usb:v04B4p8613* + ID_MODEL_FROM_DATABASE=CY7C68013 EZ-USB FX2 USB 2.0 Development Kit + +usb:v04B4p8614* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v04B4p861F* + ID_MODEL_FROM_DATABASE=Anysee E30 USB 2.0 DVB-T Receiver + +usb:v04B4pBCA1* + ID_MODEL_FROM_DATABASE=Barcode Reader + +usb:v04B4pCC04* + ID_MODEL_FROM_DATABASE=Centor USB RACIA-ALVAR USB PORT + +usb:v04B4pCC06* + ID_MODEL_FROM_DATABASE=Centor-P RACIA-ALVAR USB PORT + +usb:v04B4pD5D5* + ID_MODEL_FROM_DATABASE=CY7C63x0x Zoltrix Z-Boxer GamePad + +usb:v04B4pDE61* + ID_MODEL_FROM_DATABASE=Barcode Reader + +usb:v04B4pDE64* + ID_MODEL_FROM_DATABASE=Barcode Reader + +usb:v04B4pF000* + ID_MODEL_FROM_DATABASE=CY30700 Licorice evaluation board + +usb:v04B4pF111* + ID_MODEL_FROM_DATABASE=CY8CKIT-002 PSoC MiniProg3 Rev A Program and debug kit + +usb:v04B4pF115* + ID_MODEL_FROM_DATABASE=PSoC FirstTouch Programmer + +usb:v04B4pF232* + ID_MODEL_FROM_DATABASE=Mono embedded computer + +usb:v04B4pFD13* + ID_MODEL_FROM_DATABASE=Programmable power socket + +usb:v04B5* + ID_VENDOR_FROM_DATABASE=ROHM LSI Systems USA, LLC + +usb:v04B5p3064* + ID_MODEL_FROM_DATABASE=Hantek DSO-3064 + +usb:v04B6* + ID_VENDOR_FROM_DATABASE=Hint Corp. + +usb:v04B7* + ID_VENDOR_FROM_DATABASE=Compal Electronics, Inc. + +usb:v04B8* + ID_VENDOR_FROM_DATABASE=Seiko Epson Corp. + +usb:v04B8p0001* + ID_MODEL_FROM_DATABASE=Stylus Color 740 / Photo 750 + +usb:v04B8p0002* + ID_MODEL_FROM_DATABASE=ISD Smart Cable for Mac + +usb:v04B8p0003* + ID_MODEL_FROM_DATABASE=ISD Smart Cable + +usb:v04B8p0004* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04B8p0005* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04B8p0006* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04B8p0007* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04B8p0015* + ID_MODEL_FROM_DATABASE=Stylus Photo R3000 + +usb:v04B8p0101* + ID_MODEL_FROM_DATABASE=GT-7000U [Perfection 636] + +usb:v04B8p0102* + ID_MODEL_FROM_DATABASE=GT-2200 + +usb:v04B8p0103* + ID_MODEL_FROM_DATABASE=GT-6600U [Perfection 610] + +usb:v04B8p0104* + ID_MODEL_FROM_DATABASE=GT-7600UF [Perfection 1200U/1200U Photo] + +usb:v04B8p0105* + ID_MODEL_FROM_DATABASE=Stylus Scan 2000 + +usb:v04B8p0106* + ID_MODEL_FROM_DATABASE=Stylus Scan 2500 + +usb:v04B8p0107* + ID_MODEL_FROM_DATABASE=ES-2000 [Expression 1600U] + +usb:v04B8p0108* + ID_MODEL_FROM_DATABASE=CC-700 + +usb:v04B8p0109* + ID_MODEL_FROM_DATABASE=ES-8500 [Expression 1640 XL] + +usb:v04B8p010A* + ID_MODEL_FROM_DATABASE=GT-8700/GT-8700F [Perfection 1640SU/1640SU PHOTO] + +usb:v04B8p010B* + ID_MODEL_FROM_DATABASE=GT-7700U [Perfection 1240U] + +usb:v04B8p010C* + ID_MODEL_FROM_DATABASE=GT-6700U [Perfection 640] + +usb:v04B8p010D* + ID_MODEL_FROM_DATABASE=CC-500L + +usb:v04B8p010E* + ID_MODEL_FROM_DATABASE=ES-2200 [Perfection 1680] + +usb:v04B8p010F* + ID_MODEL_FROM_DATABASE=GT-7200U [Perfection 1250/1250 PHOTO] + +usb:v04B8p0110* + ID_MODEL_FROM_DATABASE=GT-8200U/GT-8200UF [Perfection 1650/1650 PHOTO] + +usb:v04B8p0112* + ID_MODEL_FROM_DATABASE=GT-9700F [Perfection 2450 PHOTO] + +usb:v04B8p0114* + ID_MODEL_FROM_DATABASE=Perfection 660 + +usb:v04B8p0116* + ID_MODEL_FROM_DATABASE=GT-9400UF [Perfection 3170] + +usb:v04B8p0118* + ID_MODEL_FROM_DATABASE=GT-F600 [Perfection 4180] + +usb:v04B8p0119* + ID_MODEL_FROM_DATABASE=GT-X750 [Perfection 4490 Photo] + +usb:v04B8p011A* + ID_MODEL_FROM_DATABASE=CC-550L [1000 ICS] + +usb:v04B8p011B* + ID_MODEL_FROM_DATABASE=GT-9300UF [Perfection 2400 PHOTO] + +usb:v04B8p011C* + ID_MODEL_FROM_DATABASE=GT-9800F [Perfection 3200] + +usb:v04B8p011D* + ID_MODEL_FROM_DATABASE=GT-7300U [Perfection 1260/1260 PHOTO] + +usb:v04B8p011E* + ID_MODEL_FROM_DATABASE=GT-8300UF [Perfection 1660 PHOTO] + +usb:v04B8p011F* + ID_MODEL_FROM_DATABASE=GT-8400UF [Perfection 1670/1670 PHOTO] + +usb:v04B8p0120* + ID_MODEL_FROM_DATABASE=GT-7400U [Perfection 1270] + +usb:v04B8p0121* + ID_MODEL_FROM_DATABASE=GT-F500/GT-F550 [Perfection 2480/2580 PHOTO] + +usb:v04B8p0122* + ID_MODEL_FROM_DATABASE=GT-F520/GT-F570 [Perfection 3590 PHOTO] + +usb:v04B8p0126* + ID_MODEL_FROM_DATABASE=ES-7000H [GT-15000] + +usb:v04B8p0128* + ID_MODEL_FROM_DATABASE=GT-X700 [Perfection 4870] + +usb:v04B8p0129* + ID_MODEL_FROM_DATABASE=ES-10000G [Expression 10000XL] + +usb:v04B8p012A* + ID_MODEL_FROM_DATABASE=GT-X800 [Perfection 4990 PHOTO] + +usb:v04B8p012B* + ID_MODEL_FROM_DATABASE=ES-H300 [GT-2500] + +usb:v04B8p012C* + ID_MODEL_FROM_DATABASE=GT-X900 [Perfection V700/V750 Photo] + +usb:v04B8p012D* + ID_MODEL_FROM_DATABASE=GT-F650 [GT-S600/Perfection V10/V100] + +usb:v04B8p012E* + ID_MODEL_FROM_DATABASE=GT-F670 [Perfection V200 Photo] + +usb:v04B8p012F* + ID_MODEL_FROM_DATABASE=GT-F700 [Perfection V350] + +usb:v04B8p0130* + ID_MODEL_FROM_DATABASE=GT-X770 [Perfection V500] + +usb:v04B8p0131* + ID_MODEL_FROM_DATABASE=GT-F720 [GT-S620/Perfection V30/V300 Photo] + +usb:v04B8p0133* + ID_MODEL_FROM_DATABASE=GT-1500 [GT-D1000] + +usb:v04B8p0135* + ID_MODEL_FROM_DATABASE=GT-X970 + +usb:v04B8p0136* + ID_MODEL_FROM_DATABASE=ES-D400 [GT-S80] + +usb:v04B8p0137* + ID_MODEL_FROM_DATABASE=ES-D200 [GT-S50] + +usb:v04B8p0138* + ID_MODEL_FROM_DATABASE=ES-H7200 [GT-20000] + +usb:v04B8p013A* + ID_MODEL_FROM_DATABASE=GT-X820 [Perfection V600 Photo] + +usb:v04B8p0142* + ID_MODEL_FROM_DATABASE=GT-F730 [GT-S630/Perfection V33/V330 Photo] + +usb:v04B8p0143* + ID_MODEL_FROM_DATABASE=GT-S55 + +usb:v04B8p0144* + ID_MODEL_FROM_DATABASE=GT-S85 + +usb:v04B8p0202* + ID_MODEL_FROM_DATABASE=Receipt Printer M129C/TM-T70 + +usb:v04B8p0401* + ID_MODEL_FROM_DATABASE=CP 800 Digital Camera + +usb:v04B8p0402* + ID_MODEL_FROM_DATABASE=PhotoPC 850z + +usb:v04B8p0403* + ID_MODEL_FROM_DATABASE=PhotoPC 3000z + +usb:v04B8p0509* + ID_MODEL_FROM_DATABASE=JVC PIX-MC10 + +usb:v04B8p0601* + ID_MODEL_FROM_DATABASE=Stylus Photo 875DC Card Reader + +usb:v04B8p0602* + ID_MODEL_FROM_DATABASE=Stylus Photo 895 Card Reader + +usb:v04B8p0801* + ID_MODEL_FROM_DATABASE=CC-600PX [Stylus CX5200/CX5400/CX6600] + +usb:v04B8p0802* + ID_MODEL_FROM_DATABASE=CC-570L [Stylus CX3100/CX3200] + +usb:v04B8p0803* + ID_MODEL_FROM_DATABASE=Printer (Composite Device) + +usb:v04B8p0804* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p0805* + ID_MODEL_FROM_DATABASE=Stylus CX6300/CX6400 + +usb:v04B8p0806* + ID_MODEL_FROM_DATABASE=PM-A850 [Stylus Photo RX600/610] + +usb:v04B8p0807* + ID_MODEL_FROM_DATABASE=Stylus Photo RX500/510 + +usb:v04B8p0808* + ID_MODEL_FROM_DATABASE=Stylus CX5200/CX5300/CX5400 + +usb:v04B8p0809* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p080A* + ID_MODEL_FROM_DATABASE=F-3200 + +usb:v04B8p080C* + ID_MODEL_FROM_DATABASE=ME100 [Stylus CX1500] + +usb:v04B8p080D* + ID_MODEL_FROM_DATABASE=Stylus CX4500/4600 + +usb:v04B8p080E* + ID_MODEL_FROM_DATABASE=PX-A550 [CX-3500/3600/3650 MFP] + +usb:v04B8p080F* + ID_MODEL_FROM_DATABASE=Stylus Photo RX420/RX425/RX430 + +usb:v04B8p0810* + ID_MODEL_FROM_DATABASE=PM-A900 [Stylus Photo RX700] + +usb:v04B8p0811* + ID_MODEL_FROM_DATABASE=PM-A870 [Stylus Photo RX620/RX630] + +usb:v04B8p0812* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p0813* + ID_MODEL_FROM_DATABASE=Stylus CX6500/6600 + +usb:v04B8p0814* + ID_MODEL_FROM_DATABASE=PM-A700 + +usb:v04B8p0815* + ID_MODEL_FROM_DATABASE=LP-A500 [AcuLaser CX1] + +usb:v04B8p0816* + ID_MODEL_FROM_DATABASE=Printer (Composite Device) + +usb:v04B8p0817* + ID_MODEL_FROM_DATABASE=LP-M5500/LP-M5500F + +usb:v04B8p0818* + ID_MODEL_FROM_DATABASE=Stylus CX3700/CX3800/DX3800 + +usb:v04B8p0819* + ID_MODEL_FROM_DATABASE=PX-A650 [Stylus CX4700/CX4800/DX4800/DX4850] + +usb:v04B8p081A* + ID_MODEL_FROM_DATABASE=PM-A750 [Stylus Photo RX520/RX530] + +usb:v04B8p081B* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p081C* + ID_MODEL_FROM_DATABASE=PM-A890 [Stylus Photo RX640/RX650] + +usb:v04B8p081D* + ID_MODEL_FROM_DATABASE=PM-A950 + +usb:v04B8p081E* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p081F* + ID_MODEL_FROM_DATABASE=Stylus CX7700/7800 + +usb:v04B8p0820* + ID_MODEL_FROM_DATABASE=Stylus CX4100/CX4200/DX4200 + +usb:v04B8p0821* + ID_MODEL_FROM_DATABASE=Stylus CX5700F/CX5800F + +usb:v04B8p0822* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p0823* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p0824* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p0825* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p0826* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p0827* + ID_MODEL_FROM_DATABASE=PM-A820 [Stylus Photo RX560/RX580/RX585/RX590] + +usb:v04B8p0828* + ID_MODEL_FROM_DATABASE=PM-A970 + +usb:v04B8p0829* + ID_MODEL_FROM_DATABASE=PM-T990 + +usb:v04B8p082A* + ID_MODEL_FROM_DATABASE=PM-A920 + +usb:v04B8p082B* + ID_MODEL_FROM_DATABASE=Stylus CX5900/CX5000/DX5000/DX5050 + +usb:v04B8p082C* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p082D* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04B8p082E* + ID_MODEL_FROM_DATABASE=PX-A720 [Stylus CX5900/CX6000/DX6000] + +usb:v04B8p082F* + ID_MODEL_FROM_DATABASE=PX-A620 [Stylus CX3900/DX4000/DX4050] + +usb:v04B8p0830* + ID_MODEL_FROM_DATABASE=ME 200 [Stylus CX2800/CX2900] + +usb:v04B8p0831* + ID_MODEL_FROM_DATABASE=Stylus CX6900F/CX7000F/DX7000F + +usb:v04B8p0832* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p0833* + ID_MODEL_FROM_DATABASE=LP-M5600 + +usb:v04B8p0834* + ID_MODEL_FROM_DATABASE=LP-M6000 + +usb:v04B8p0835* + ID_MODEL_FROM_DATABASE=AcuLaser CX21 + +usb:v04B8p0836* + ID_MODEL_FROM_DATABASE=PM-T960 + +usb:v04B8p0837* + ID_MODEL_FROM_DATABASE=PM-A940 [Stylus Photo RX680/RX685/RX690] + +usb:v04B8p0838* + ID_MODEL_FROM_DATABASE=PX-A640 [CX7300/CX7400/DX7400] + +usb:v04B8p0839* + ID_MODEL_FROM_DATABASE=PX-A740 [CX8300/CX8400/DX8400] + +usb:v04B8p083A* + ID_MODEL_FROM_DATABASE=PX-FA700 [CX9300F/CX9400Fax/DX9400F] + +usb:v04B8p083B* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p083C* + ID_MODEL_FROM_DATABASE=PM-A840S [Stylus Photo RX595/RX610] + +usb:v04B8p083D* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p083E* + ID_MODEL_FROM_DATABASE=MFP Composite Device + +usb:v04B8p083F* + ID_MODEL_FROM_DATABASE=Stylus CX4300/CX4400/CX5500/CX5600/DX4400/DX4450 + +usb:v04B8p0841* + ID_MODEL_FROM_DATABASE=PX-401A [ME 300/Stylus NX100] + +usb:v04B8p0843* + ID_MODEL_FROM_DATABASE=LP-M5000 + +usb:v04B8p0844* + ID_MODEL_FROM_DATABASE=EP-901A/EP-901F [Artisan 800/Stylus Photo PX800FW] + +usb:v04B8p0846* + ID_MODEL_FROM_DATABASE=EP-801A [Artisan 700/Stylus Photo PX700W/TX700W] + +usb:v04B8p0847* + ID_MODEL_FROM_DATABASE=PX-601F [ME Office 700FW/Stylus Office BX600FW/TX600FW] + +usb:v04B8p0848* + ID_MODEL_FROM_DATABASE=ME Office 600F/Stylus Office BX300F/TX300F + +usb:v04B8p0849* + ID_MODEL_FROM_DATABASE=Stylus SX205 + +usb:v04B8p084A* + ID_MODEL_FROM_DATABASE=PX-501A [Stylus NX400] + +usb:v04B8p084D* + ID_MODEL_FROM_DATABASE=PX-402A [Stylus SX115/Stylus NX110 Series] + +usb:v04B8p084F* + ID_MODEL_FROM_DATABASE=ME OFFICE 510 + +usb:v04B8p0850* + ID_MODEL_FROM_DATABASE=EP-702A [Stylus Photo PX650/TX650 Series] + +usb:v04B8p0851* + ID_MODEL_FROM_DATABASE=Stylus SX410 + +usb:v04B8p0852* + ID_MODEL_FROM_DATABASE=EP-802A [Artisan 710 Series/Stylus Photo PX710W/TX720W Series] + +usb:v04B8p0853* + ID_MODEL_FROM_DATABASE=EP-902A [Artisan 810 Series/Stylus Photo PX810FW Series] + +usb:v04B8p0854* + ID_MODEL_FROM_DATABASE=ME OFFICE 650FN Series/Stylus Office BX310FN/TX520FN Series + +usb:v04B8p0855* + ID_MODEL_FROM_DATABASE=PX-602F [Stylus Office BX610FW/TX620FW Series] + +usb:v04B8p0856* + ID_MODEL_FROM_DATABASE=PX-502A [Stylus SX515W] + +usb:v04B8p085C* + ID_MODEL_FROM_DATABASE=ME 320/330 Series [Stylus SX125] + +usb:v04B8p085D* + ID_MODEL_FROM_DATABASE=PX-603F [ME OFFICE 960FWD Series/Stylus Office BX625FWD/TX620FWD Series] + +usb:v04B8p085E* + ID_MODEL_FROM_DATABASE=PX-503A [ME OFFICE 900WD Series/Stylus Office BX525WD] + +usb:v04B8p085F* + ID_MODEL_FROM_DATABASE=Stylus Office BX320FW/TX525FW Series + +usb:v04B8p0860* + ID_MODEL_FROM_DATABASE=EP-903A/EP-903F [Artisan 835/Stylus Photo PX820FWD Series] + +usb:v04B8p0861* + ID_MODEL_FROM_DATABASE=EP-803A/EP-803AW [Artisan 725/Stylus Photo PX720WD/TX720WD Series] + +usb:v04B8p0862* + ID_MODEL_FROM_DATABASE=EP-703A [Stylus Photo PX660 Series] + +usb:v04B8p0863* + ID_MODEL_FROM_DATABASE=ME OFFICE 620F Series/Stylus Office BX305F/BX305FW/TX320F + +usb:v04B8p0864* + ID_MODEL_FROM_DATABASE=ME OFFICE 560W Series + +usb:v04B8p0865* + ID_MODEL_FROM_DATABASE=ME OFFICE 520 Series + +usb:v04B8p0866* + ID_MODEL_FROM_DATABASE=AcuLaser MX20DN/MX20DNF/MX21DNF + +usb:v04B8p0869* + ID_MODEL_FROM_DATABASE=PX-1600F + +usb:v04B8p086A* + ID_MODEL_FROM_DATABASE=PX-673F [Stylus Office BX925FWD] + +usb:v04B8p0870* + ID_MODEL_FROM_DATABASE=Stylus Office BX305FW Plus + +usb:v04B8p0871* + ID_MODEL_FROM_DATABASE=K200 Series + +usb:v04B8p0872* + ID_MODEL_FROM_DATABASE=K300 Series + +usb:v04B8p0873* + ID_MODEL_FROM_DATABASE=L200 Series + +usb:v04B8p0878* + ID_MODEL_FROM_DATABASE=EP-704A + +usb:v04B8p0879* + ID_MODEL_FROM_DATABASE=EP-904A/EP-904F [Artisan 837/Stylus Photo PX830FWD Series] + +usb:v04B8p087B* + ID_MODEL_FROM_DATABASE=EP-804A/EP-804AR/EP-804AW [Stylus Photo PX730WD/Artisan 730 Series] + +usb:v04B8p087C* + ID_MODEL_FROM_DATABASE=PX-1700F + +usb:v04B8p087D* + ID_MODEL_FROM_DATABASE=PX-B750F/WP-4525 Series + +usb:v04B8p087F* + ID_MODEL_FROM_DATABASE=PX-403A + +usb:v04B8p0880* + ID_MODEL_FROM_DATABASE=PX-434A [Stylus NX330 Series] + +usb:v04B8p0881* + ID_MODEL_FROM_DATABASE=PX-404A [ME OFFICE 535] + +usb:v04B8p0883* + ID_MODEL_FROM_DATABASE=ME 340 Series/Stylus NX130 Series + +usb:v04B8p0884* + ID_MODEL_FROM_DATABASE=Stylus NX430W Series + +usb:v04B8p0885* + ID_MODEL_FROM_DATABASE=Stylus NX230/SX235W Series + +usb:v04B8p088F* + ID_MODEL_FROM_DATABASE=Stylus Office BX635FWD + +usb:v04B8p0890* + ID_MODEL_FROM_DATABASE=ME OFFICE 940FW Series/Stylus Office BX630FW Series + +usb:v04B8p0891* + ID_MODEL_FROM_DATABASE=Stylus Office BX535WD + +usb:v04B8p0892* + ID_MODEL_FROM_DATABASE=Stylus Office BX935FWD + +usb:v04B8p0893* + ID_MODEL_FROM_DATABASE=EP-774A + +usb:v04B9* + ID_VENDOR_FROM_DATABASE=Rainbow Technologies, Inc. + +usb:v04B9p0300* + ID_MODEL_FROM_DATABASE=SafeNet USB SuperPro/UltraPro + +usb:v04B9p1000* + ID_MODEL_FROM_DATABASE=iKey 1000 Token + +usb:v04B9p1001* + ID_MODEL_FROM_DATABASE=iKey 1200 Token + +usb:v04B9p1002* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1003* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1004* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1005* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1006* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1200* + ID_MODEL_FROM_DATABASE=iKey 2000 Token + +usb:v04B9p1201* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1202* + ID_MODEL_FROM_DATABASE=iKey 2032 Token + +usb:v04B9p1203* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1204* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1205* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1206* + ID_MODEL_FROM_DATABASE=iKey 4000 Token + +usb:v04B9p1300* + ID_MODEL_FROM_DATABASE=iKey 3000 Token + +usb:v04B9p1301* + ID_MODEL_FROM_DATABASE=iKey 3000 + +usb:v04B9p1302* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1303* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1304* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1305* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04B9p1306* + ID_MODEL_FROM_DATABASE=iKey Token + +usb:v04BA* + ID_VENDOR_FROM_DATABASE=Toucan Systems, Ltd + +usb:v04BB* + ID_VENDOR_FROM_DATABASE=I-O Data Device, Inc. + +usb:v04BBp0101* + ID_MODEL_FROM_DATABASE=USB2-IDE/ATAPI Bridge Adapter + +usb:v04BBp0201* + ID_MODEL_FROM_DATABASE=USB2-IDE/ATAPI Bridge Adapter + +usb:v04BBp0204* + ID_MODEL_FROM_DATABASE=DVD Multi-plus unit iU-CD2 + +usb:v04BBp0206* + ID_MODEL_FROM_DATABASE=DVD Multi-plus unit DVR-UEH8 + +usb:v04BBp0301* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v04BBp0314* + ID_MODEL_FROM_DATABASE=USB-SSMRW SD-card + +usb:v04BBp0319* + ID_MODEL_FROM_DATABASE=USB2-IDE/ATAPI Bridge Adapter + +usb:v04BBp031A* + ID_MODEL_FROM_DATABASE=USB2-IDE/ATAPI Bridge Adapter + +usb:v04BBp031B* + ID_MODEL_FROM_DATABASE=USB2-IDE/ATAPI Bridge Adapter + +usb:v04BBp031E* + ID_MODEL_FROM_DATABASE=USB-SDRW SD-card + +usb:v04BBp0502* + ID_MODEL_FROM_DATABASE=Nogatech Live! (BT) + +usb:v04BBp0528* + ID_MODEL_FROM_DATABASE=GV-USB Video Capture + +usb:v04BBp0901* + ID_MODEL_FROM_DATABASE=USB ETT + +usb:v04BBp0904* + ID_MODEL_FROM_DATABASE=ET/TX Ethernet [pegasus] + +usb:v04BBp0913* + ID_MODEL_FROM_DATABASE=ET/TX-S Ethernet [pegasus2] + +usb:v04BBp0919* + ID_MODEL_FROM_DATABASE=USB WN-B11 + +usb:v04BBp0922* + ID_MODEL_FROM_DATABASE=IOData AirPort WN-B11/USBS 802.11b + +usb:v04BBp0930* + ID_MODEL_FROM_DATABASE=ETG-US2 + +usb:v04BBp0937* + ID_MODEL_FROM_DATABASE=WN-WAG/USL Wireless LAN Adapter + +usb:v04BBp0938* + ID_MODEL_FROM_DATABASE=WN-G54/USL Wireless LAN Adapter + +usb:v04BBp093B* + ID_MODEL_FROM_DATABASE=WN-GDN/USB + +usb:v04BBp093F* + ID_MODEL_FROM_DATABASE=WNGDNUS2 802.11n + +usb:v04BBp0944* + ID_MODEL_FROM_DATABASE=WHG-AGDN/US Wireless LAN Adapter + +usb:v04BBp0945* + ID_MODEL_FROM_DATABASE=WN-GDN/US3 Wireless LAN Adapter + +usb:v04BBp0947* + ID_MODEL_FROM_DATABASE=WN-G150U Wireless LAN Adapter + +usb:v04BBp0948* + ID_MODEL_FROM_DATABASE=WN-G300U Wireless LAN Adapter + +usb:v04BBp0A03* + ID_MODEL_FROM_DATABASE=Serial USB-RSAQ1 + +usb:v04BBp0A07* + ID_MODEL_FROM_DATABASE=USB2-iCN Adapter + +usb:v04BBp0A08* + ID_MODEL_FROM_DATABASE=USB2-iCN Adapter + +usb:v04BBp0C01* + ID_MODEL_FROM_DATABASE=FM-10 Pro Disk + +usb:v04BD* + ID_VENDOR_FROM_DATABASE=Toshiba Electronics Taiwan Corp. + +usb:v04BE* + ID_VENDOR_FROM_DATABASE=Telia Research AB + +usb:v04BF* + ID_VENDOR_FROM_DATABASE=TDK Corp. + +usb:v04BFp0100* + ID_MODEL_FROM_DATABASE=MediaReader CF + +usb:v04BFp0115* + ID_MODEL_FROM_DATABASE=USB-PDC Adapter UPA9664 + +usb:v04BFp0116* + ID_MODEL_FROM_DATABASE=USB-cdmaOne Adapter UCA1464 + +usb:v04BFp0117* + ID_MODEL_FROM_DATABASE=USB-PHS Adapter UHA6400 + +usb:v04BFp0118* + ID_MODEL_FROM_DATABASE=USB-PHS Adapter UPA6400 + +usb:v04BFp0135* + ID_MODEL_FROM_DATABASE=MediaReader Dual + +usb:v04BFp0202* + ID_MODEL_FROM_DATABASE=73S1121F Smart Card Reader- + +usb:v04BFp0309* + ID_MODEL_FROM_DATABASE=Bluetooth USB dongle + +usb:v04BFp030A* + ID_MODEL_FROM_DATABASE=IBM Bluetooth Ultraport Module + +usb:v04BFp030B* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v04BFp030C* + ID_MODEL_FROM_DATABASE=Ultraport Bluetooth Device + +usb:v04BFp0310* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth + +usb:v04BFp0311* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth Device + +usb:v04BFp0317* + ID_MODEL_FROM_DATABASE=Bluetooth UltraPort Module from IBM + +usb:v04BFp0318* + ID_MODEL_FROM_DATABASE=IBM Integrated Bluetooth + +usb:v04BFp0319* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v04BFp0320* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v04BFp0321* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v04BFp0A28* + ID_MODEL_FROM_DATABASE=INDI AV-IN Device + +usb:v04C1* + ID_VENDOR_FROM_DATABASE=U.S. Robotics (3Com) + +usb:v04C1p0020* + ID_MODEL_FROM_DATABASE=56K Voice Pro + +usb:v04C1p0022* + ID_MODEL_FROM_DATABASE=56K Voice Pro + +usb:v04C1p007E* + ID_MODEL_FROM_DATABASE=ISDN TA + +usb:v04C1p0082* + ID_MODEL_FROM_DATABASE=OfficeConnect Analog Modem + +usb:v04C1p008F* + ID_MODEL_FROM_DATABASE=Pro ISDN TA + +usb:v04C1p0097* + ID_MODEL_FROM_DATABASE=OfficeConnect Analog + +usb:v04C1p009D* + ID_MODEL_FROM_DATABASE=HomeConnect Webcam [vicam] + +usb:v04C1p00A9* + ID_MODEL_FROM_DATABASE=ISDN Pro TA-U + +usb:v04C1p00B9* + ID_MODEL_FROM_DATABASE=HomeConnect IDSL Modem + +usb:v04C1p3021* + ID_MODEL_FROM_DATABASE=56k Voice FaxModem Pro + +usb:v04C2* + ID_VENDOR_FROM_DATABASE=Methode Electronics Far East PTE, Ltd + +usb:v04C3* + ID_VENDOR_FROM_DATABASE=Maxi Switch, Inc. + +usb:v04C3p1102* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v04C3p2102* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v04C4* + ID_VENDOR_FROM_DATABASE=Lockheed Martin Energy Research + +usb:v04C5* + ID_VENDOR_FROM_DATABASE=Fujitsu, Ltd + +usb:v04C5p1029* + ID_MODEL_FROM_DATABASE=fi-4010c Scanner + +usb:v04C5p1033* + ID_MODEL_FROM_DATABASE=fi-4110CU + +usb:v04C5p1041* + ID_MODEL_FROM_DATABASE=fi-4120c Scanner + +usb:v04C5p1042* + ID_MODEL_FROM_DATABASE=fi-4220c Scanner + +usb:v04C5p105B* + ID_MODEL_FROM_DATABASE=AH-F401U Air H device + +usb:v04C5p1084* + ID_MODEL_FROM_DATABASE=PalmSecure Sensor V2 + +usb:v04C5p1096* + ID_MODEL_FROM_DATABASE=fi-5110EOX + +usb:v04C5p1097* + ID_MODEL_FROM_DATABASE=fi-5110C + +usb:v04C5p10AE* + ID_MODEL_FROM_DATABASE=fi-4120C2 + +usb:v04C5p10AF* + ID_MODEL_FROM_DATABASE=fi-4220C2 + +usb:v04C5p10C7* + ID_MODEL_FROM_DATABASE=fi-60f scanner + +usb:v04C5p10E0* + ID_MODEL_FROM_DATABASE=fi-5120c Scanner + +usb:v04C5p10E1* + ID_MODEL_FROM_DATABASE=fi-5220C + +usb:v04C5p10E7* + ID_MODEL_FROM_DATABASE=fi-5900C + +usb:v04C5p10FE* + ID_MODEL_FROM_DATABASE=S500 + +usb:v04C5p1150* + ID_MODEL_FROM_DATABASE=fi-6230 + +usb:v04C5p201D* + ID_MODEL_FROM_DATABASE=SATA 3.0 6Gbit/s Adaptor [GROOVY] + +usb:v04C6* + ID_VENDOR_FROM_DATABASE=Toshiba America Electronic Components + +usb:v04C7* + ID_VENDOR_FROM_DATABASE=Micro Macro Technologies + +usb:v04C8* + ID_VENDOR_FROM_DATABASE=Konica Corp. + +usb:v04C8p0720* + ID_MODEL_FROM_DATABASE=Digital Color Camera + +usb:v04C8p0721* + ID_MODEL_FROM_DATABASE=e-miniD Camera + +usb:v04C8p0722* + ID_MODEL_FROM_DATABASE=e-mini + +usb:v04C8p0723* + ID_MODEL_FROM_DATABASE=KD-200Z Camera + +usb:v04C8p0726* + ID_MODEL_FROM_DATABASE=KD-310Z Camera + +usb:v04C8p0728* + ID_MODEL_FROM_DATABASE=Revio C2 Mass Storage Device + +usb:v04C8p0729* + ID_MODEL_FROM_DATABASE=Revio C2 Digital Camera + +usb:v04C8p072C* + ID_MODEL_FROM_DATABASE=Revio KD20M + +usb:v04C8p072D* + ID_MODEL_FROM_DATABASE=Revio KD410Z + +usb:v04CA* + ID_VENDOR_FROM_DATABASE=Lite-On Technology Corp. + +usb:v04CAp004F* + ID_MODEL_FROM_DATABASE=SK-9020 keyboard + +usb:v04CAp1766* + ID_MODEL_FROM_DATABASE=HID Monitor Controls + +usb:v04CAp2004* + ID_MODEL_FROM_DATABASE=Bluetooth 4.0 [Broadcom BCM20702A0] + +usb:v04CAp2006* + ID_MODEL_FROM_DATABASE=Broadcom BCM43142A0 Bluetooth Device + +usb:v04CAp3005* + ID_MODEL_FROM_DATABASE=Atheros Bluetooth + +usb:v04CAp300B* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v04CAp300D* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v04CAp300F* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v04CAp3014* + ID_MODEL_FROM_DATABASE=Qualcomm Atheros Bluetooth + +usb:v04CAp7025* + ID_MODEL_FROM_DATABASE=HP HD Webcam + +usb:v04CAp7046* + ID_MODEL_FROM_DATABASE=TOSHIBA Web Camera - HD + +usb:v04CAp9304* + ID_MODEL_FROM_DATABASE=Hub + +usb:v04CApF01C* + ID_MODEL_FROM_DATABASE=TT1280DA DVB-T TV Tuner + +usb:v04CB* + ID_VENDOR_FROM_DATABASE=Fuji Photo Film Co., Ltd + +usb:v04CBp0100* + ID_MODEL_FROM_DATABASE=FinePix 30i/40i/50i, A101/201, 1300/2200, 1400/2400/2600/2800/4500/4700/4800/4900/6800/6900 Zoom + +usb:v04CBp0103* + ID_MODEL_FROM_DATABASE=FinePix NX-500/NX-700 printer + +usb:v04CBp0104* + ID_MODEL_FROM_DATABASE=FinePix A101, 2600/2800/4800/6800 Zoom (PC CAM) + +usb:v04CBp0108* + ID_MODEL_FROM_DATABASE=FinePix F601 Zoom (DSC) + +usb:v04CBp0109* + ID_MODEL_FROM_DATABASE=FinePix F601 Zoom (PC CAM) + +usb:v04CBp010A* + ID_MODEL_FROM_DATABASE=FinePix S602 (Pro) Zoom (DSC) + +usb:v04CBp010B* + ID_MODEL_FROM_DATABASE=FinePix S602 (Pro) Zoom (PC CAM) + +usb:v04CBp010D* + ID_MODEL_FROM_DATABASE=FinePix Digital Camera 020531 + +usb:v04CBp010E* + ID_MODEL_FROM_DATABASE=FinePix F402 Zoom (DSC) + +usb:v04CBp010F* + ID_MODEL_FROM_DATABASE=FinePix F402 Zoom (PC CAM) + +usb:v04CBp0110* + ID_MODEL_FROM_DATABASE=FinePix M603 Zoom (DSC) + +usb:v04CBp0111* + ID_MODEL_FROM_DATABASE=FinePix M603 Zoom (PC CAM) + +usb:v04CBp0112* + ID_MODEL_FROM_DATABASE=FinePix A202, A200 Zoom (DSC) + +usb:v04CBp0113* + ID_MODEL_FROM_DATABASE=FinePix A202, A200 Zoom (PC CAM) + +usb:v04CBp0114* + ID_MODEL_FROM_DATABASE=FinePix F401 Zoom (DSC) + +usb:v04CBp0115* + ID_MODEL_FROM_DATABASE=FinePix F401 Zoom (PC CAM) + +usb:v04CBp0116* + ID_MODEL_FROM_DATABASE=FinePix A203 Zoom (DSC) + +usb:v04CBp0117* + ID_MODEL_FROM_DATABASE=FinePix A203 Zoom (PC CAM) + +usb:v04CBp0118* + ID_MODEL_FROM_DATABASE=FinePix A303 Zoom (DSC) + +usb:v04CBp0119* + ID_MODEL_FROM_DATABASE=FinePix A303 Zoom (PC CAM) + +usb:v04CBp011A* + ID_MODEL_FROM_DATABASE=FinePix S304/3800 Zoom (DSC) + +usb:v04CBp011B* + ID_MODEL_FROM_DATABASE=FinePix S304/3800 Zoom (PC CAM) + +usb:v04CBp011C* + ID_MODEL_FROM_DATABASE=FinePix A204/2650 Zoom (DSC) + +usb:v04CBp011D* + ID_MODEL_FROM_DATABASE=FinePix A204/2650 Zoom (PC CAM) + +usb:v04CBp0120* + ID_MODEL_FROM_DATABASE=FinePix F700 Zoom (DSC) + +usb:v04CBp0121* + ID_MODEL_FROM_DATABASE=FinePix F700 Zoom (PC CAM) + +usb:v04CBp0122* + ID_MODEL_FROM_DATABASE=FinePix F410 Zoom (DSC) + +usb:v04CBp0123* + ID_MODEL_FROM_DATABASE=FinePix F410 Zoom (PC CAM) + +usb:v04CBp0124* + ID_MODEL_FROM_DATABASE=FinePix A310 Zoom (DSC) + +usb:v04CBp0125* + ID_MODEL_FROM_DATABASE=FinePix A310 Zoom (PC CAM) + +usb:v04CBp0126* + ID_MODEL_FROM_DATABASE=FinePix A210 Zoom (DSC) + +usb:v04CBp0127* + ID_MODEL_FROM_DATABASE=FinePix A210 Zoom (PC CAM) + +usb:v04CBp0128* + ID_MODEL_FROM_DATABASE=FinePix A205(S) Zoom (DSC) + +usb:v04CBp0129* + ID_MODEL_FROM_DATABASE=FinePix A205(S) Zoom (PC CAM) + +usb:v04CBp012A* + ID_MODEL_FROM_DATABASE=FinePix F610 Zoom (DSC) + +usb:v04CBp012B* + ID_MODEL_FROM_DATABASE=FinePix Digital Camera 030513 + +usb:v04CBp012C* + ID_MODEL_FROM_DATABASE=FinePix S7000 Zoom (DSC) + +usb:v04CBp012D* + ID_MODEL_FROM_DATABASE=FinePix S7000 Zoom (PC CAM) + +usb:v04CBp012F* + ID_MODEL_FROM_DATABASE=FinePix Digital Camera 030731 + +usb:v04CBp0130* + ID_MODEL_FROM_DATABASE=FinePix S5000 Zoom (DSC) + +usb:v04CBp0131* + ID_MODEL_FROM_DATABASE=FinePix S5000 Zoom (PC CAM) + +usb:v04CBp013B* + ID_MODEL_FROM_DATABASE=FinePix Digital Camera 030722 + +usb:v04CBp013C* + ID_MODEL_FROM_DATABASE=FinePix S3000 Zoom (DSC) + +usb:v04CBp013D* + ID_MODEL_FROM_DATABASE=FinePix S3000 Zoom (PC CAM) + +usb:v04CBp013E* + ID_MODEL_FROM_DATABASE=FinePix F420 Zoom (DSC) + +usb:v04CBp013F* + ID_MODEL_FROM_DATABASE=FinePix F420 Zoom (PC CAM) + +usb:v04CBp0142* + ID_MODEL_FROM_DATABASE=FinePix S7000 Zoom (PTP) + +usb:v04CBp0148* + ID_MODEL_FROM_DATABASE=FinePix A330 Zoom (DSC) + +usb:v04CBp0149* + ID_MODEL_FROM_DATABASE=FinePix A330 Zoom (UVC) + +usb:v04CBp014A* + ID_MODEL_FROM_DATABASE=FinePix A330 Zoom (PTP) + +usb:v04CBp014B* + ID_MODEL_FROM_DATABASE=FinePix A340 Zoom (DSC) + +usb:v04CBp014C* + ID_MODEL_FROM_DATABASE=FinePix A340 Zoom (UVC) + +usb:v04CBp0159* + ID_MODEL_FROM_DATABASE=FinePix F710 Zoom (DSC) + +usb:v04CBp0165* + ID_MODEL_FROM_DATABASE=FinePix S3500 Zoom (DSC) + +usb:v04CBp0168* + ID_MODEL_FROM_DATABASE=FinePix E500 Zoom (DSC) + +usb:v04CBp0169* + ID_MODEL_FROM_DATABASE=FinePix E500 Zoom (UVC) + +usb:v04CBp016B* + ID_MODEL_FROM_DATABASE=FinePix E510 Zoom (DSC) + +usb:v04CBp016C* + ID_MODEL_FROM_DATABASE=FinePix E510 Zoom (PC CAM) + +usb:v04CBp016E* + ID_MODEL_FROM_DATABASE=FinePix S5500 Zoom (DSC) + +usb:v04CBp016F* + ID_MODEL_FROM_DATABASE=FinePix S5500 Zoom (UVC) + +usb:v04CBp0171* + ID_MODEL_FROM_DATABASE=FinePix E550 Zoom (DSC) + +usb:v04CBp0172* + ID_MODEL_FROM_DATABASE=FinePix E550 Zoom (UVC) + +usb:v04CBp0177* + ID_MODEL_FROM_DATABASE=FinePix F10 (DSC) + +usb:v04CBp0179* + ID_MODEL_FROM_DATABASE=Finepix F10 (PTP) + +usb:v04CBp0186* + ID_MODEL_FROM_DATABASE=FinePix S5200/S5600 Zoom (DSC) + +usb:v04CBp0188* + ID_MODEL_FROM_DATABASE=FinePix S5200/S5600 Zoom (PTP) + +usb:v04CBp018E* + ID_MODEL_FROM_DATABASE=FinePix S9500 Zoom (DSC) + +usb:v04CBp018F* + ID_MODEL_FROM_DATABASE=FinePix S9500 Zoom (PTP) + +usb:v04CBp0192* + ID_MODEL_FROM_DATABASE=FinePix E900 Zoom (DSC) + +usb:v04CBp0193* + ID_MODEL_FROM_DATABASE=FinePix E900 Zoom (PTP) + +usb:v04CBp019B* + ID_MODEL_FROM_DATABASE=FinePix F30 (PTP) + +usb:v04CBp01AF* + ID_MODEL_FROM_DATABASE=FinePix A700 (PTP) + +usb:v04CBp01BF* + ID_MODEL_FROM_DATABASE=FinePix F6000fd/S6500fd Zoom (PTP) + +usb:v04CBp01C0* + ID_MODEL_FROM_DATABASE=FinePix F20 (PTP) + +usb:v04CBp01C1* + ID_MODEL_FROM_DATABASE=FinePix F31fd (PTP) + +usb:v04CBp01C4* + ID_MODEL_FROM_DATABASE=FinePix S5700 Zoom (PTP) + +usb:v04CBp01C5* + ID_MODEL_FROM_DATABASE=FinePix F40fd (PTP) + +usb:v04CBp01C6* + ID_MODEL_FROM_DATABASE=FinePix A820 Zoom (PTP) + +usb:v04CBp01D2* + ID_MODEL_FROM_DATABASE=FinePix A800 Zoom (PTP) + +usb:v04CBp01D3* + ID_MODEL_FROM_DATABASE=FinePix A920 (PTP) + +usb:v04CBp01D4* + ID_MODEL_FROM_DATABASE=FinePix F50fd (PTP) + +usb:v04CBp01D5* + ID_MODEL_FROM_DATABASE=FinePix F47 (PTP) + +usb:v04CBp01F7* + ID_MODEL_FROM_DATABASE=FinePix J250 (PTP) + +usb:v04CBp01FD* + ID_MODEL_FROM_DATABASE=A160 + +usb:v04CBp023E* + ID_MODEL_FROM_DATABASE=FinePix AX300 + +usb:v04CBp0240* + ID_MODEL_FROM_DATABASE=FinePix S2950 Digital Camera + +usb:v04CBp0241* + ID_MODEL_FROM_DATABASE=FinePix S3200 Digital Camera + +usb:v04CBp0278* + ID_MODEL_FROM_DATABASE=FinePix JV300 + +usb:v04CC* + ID_VENDOR_FROM_DATABASE=ST-Ericsson + +usb:v04CCp1122* + ID_MODEL_FROM_DATABASE=Hub + +usb:v04CCp1520* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub (Avocent KVM) + +usb:v04CCp1521* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v04CCp1A62* + ID_MODEL_FROM_DATABASE=GW Instek GSP-830 Spectrum Analyzer (HID) + +usb:v04CCp2323* + ID_MODEL_FROM_DATABASE=Ux500 serial debug port + +usb:v04CCp2533* + ID_MODEL_FROM_DATABASE=NFC device (PN533) + +usb:v04CCp8116* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04CD* + ID_VENDOR_FROM_DATABASE=Tatung Co. Of America + +usb:v04CE* + ID_VENDOR_FROM_DATABASE=ScanLogic Corp. + +usb:v04CEp0002* + ID_MODEL_FROM_DATABASE=SL11R-IDE IDE Bridge + +usb:v04CEp0100* + ID_MODEL_FROM_DATABASE=USB2PRN Printer Class + +usb:v04CEp0300* + ID_MODEL_FROM_DATABASE=Phantom 336CX - C3 scanner + +usb:v04CEp04CE* + ID_MODEL_FROM_DATABASE=SL11DEMO, VID: 0x4ce, PID: 0x4ce + +usb:v04CEp07D1* + ID_MODEL_FROM_DATABASE=SL11R, VID: 0x4ce, PID: 0x07D1 + +usb:v04CF* + ID_VENDOR_FROM_DATABASE=Myson Century, Inc. + +usb:v04CFp0022* + ID_MODEL_FROM_DATABASE=OCZ Alchemy Series Elixir II Keyboard + +usb:v04CFp0800* + ID_MODEL_FROM_DATABASE=MTP800 Mass Storage Device + +usb:v04CFp8810* + ID_MODEL_FROM_DATABASE=CS8810 Mass Storage Device + +usb:v04CFp8811* + ID_MODEL_FROM_DATABASE=CS8811 Mass Storage Device + +usb:v04CFp8813* + ID_MODEL_FROM_DATABASE=CS8813 Mass Storage Device + +usb:v04CFp8818* + ID_MODEL_FROM_DATABASE=USB2.0 to ATAPI Bridge Controller + +usb:v04CFp8819* + ID_MODEL_FROM_DATABASE=USB 2.0 SD/MMC Reader + +usb:v04CFp9920* + ID_MODEL_FROM_DATABASE=CS8819A2-114 Mass Storage Device + +usb:v04D0* + ID_VENDOR_FROM_DATABASE=Digi International + +usb:v04D1* + ID_VENDOR_FROM_DATABASE=ITT Canon + +usb:v04D2* + ID_VENDOR_FROM_DATABASE=Altec Lansing Technologies + +usb:v04D2p0070* + ID_MODEL_FROM_DATABASE=ADA70 Speakers + +usb:v04D2p0305* + ID_MODEL_FROM_DATABASE=Non-Compliant Audio Device + +usb:v04D2p0311* + ID_MODEL_FROM_DATABASE=ADA-310 Speakers + +usb:v04D2p2060* + ID_MODEL_FROM_DATABASE=Claritel-i750 - vp + +usb:v04D2pFF05* + ID_MODEL_FROM_DATABASE=ADA-305 Speakers + +usb:v04D2pFF47* + ID_MODEL_FROM_DATABASE=Lansing HID Audio Controls + +usb:v04D2pFF49* + ID_MODEL_FROM_DATABASE=Lansing HID Audio Controls + +usb:v04D3* + ID_VENDOR_FROM_DATABASE=VidUS, Inc. + +usb:v04D4* + ID_VENDOR_FROM_DATABASE=LSI Logic, Inc. + +usb:v04D5* + ID_VENDOR_FROM_DATABASE=Forte Technologies, Inc. + +usb:v04D6* + ID_VENDOR_FROM_DATABASE=Mentor Graphics + +usb:v04D7* + ID_VENDOR_FROM_DATABASE=Oki Semiconductor + +usb:v04D7p1BE4* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v04D8* + ID_VENDOR_FROM_DATABASE=Microchip Technology, Inc. + +usb:v04D8p0002* + ID_MODEL_FROM_DATABASE=PicoLCD 20x2 + +usb:v04D8p0003* + ID_MODEL_FROM_DATABASE=PICkit 2 Microcontroller Programmer + +usb:v04D8p000A* + ID_MODEL_FROM_DATABASE=CDC RS-232 Emulation Demo + +usb:v04D8p000B* + ID_MODEL_FROM_DATABASE=PIC18F2550 (32K Flashable 10 Channel, 10 Bit A/D USB Microcontroller) + +usb:v04D8p0032* + ID_MODEL_FROM_DATABASE=PICkit1 + +usb:v04D8p0033* + ID_MODEL_FROM_DATABASE=PICkit2 + +usb:v04D8p0036* + ID_MODEL_FROM_DATABASE=PICkit Serial Analyzer + +usb:v04D8p00E0* + ID_MODEL_FROM_DATABASE=PIC32 Starter Board + +usb:v04D8p04CD* + ID_MODEL_FROM_DATABASE=28Cxxx EEPROM Programmer + +usb:v04D8p0A04* + ID_MODEL_FROM_DATABASE=AGP LIN Serial Analyzer + +usb:v04D8p8000* + ID_MODEL_FROM_DATABASE=In-Circuit Debugger + +usb:v04D8p8001* + ID_MODEL_FROM_DATABASE=ICD2 in-circuit debugger + +usb:v04D8p8101* + ID_MODEL_FROM_DATABASE=PIC24F Starter Kit + +usb:v04D8p8107* + ID_MODEL_FROM_DATABASE=Microstick II + +usb:v04D8p8108* + ID_MODEL_FROM_DATABASE=ChipKit Pro MX7 (PIC32MX) + +usb:v04D8p9004* + ID_MODEL_FROM_DATABASE=Microchip REAL ICE + +usb:v04D8p900A* + ID_MODEL_FROM_DATABASE=PICkit3 + +usb:v04D8pC001* + ID_MODEL_FROM_DATABASE=PicoLCD 20x4 + +usb:v04D8pE11C* + ID_MODEL_FROM_DATABASE=TL866CS EEPROM Programmer [MiniPRO] + +usb:v04D8pF2C4* + ID_MODEL_FROM_DATABASE=Macareux-labs Hygrometry Temperature Sensor + +usb:v04D8pF3AA* + ID_MODEL_FROM_DATABASE=Macareux-labs Usbce Bootloader mode + +usb:v04D8pF437* + ID_MODEL_FROM_DATABASE=SBE Tech Ultrasonic Anemometer + +usb:v04D8pF4B5* + ID_MODEL_FROM_DATABASE=SmartScope + +usb:v04D8pF8DA* + ID_MODEL_FROM_DATABASE=Hughski Ltd. ColorHug + +usb:v04D8pF8E8* + ID_MODEL_FROM_DATABASE=Harmony 300/350 Remote + +usb:v04D8pF91C* + ID_MODEL_FROM_DATABASE=SPROG IIv3 + +usb:v04D8pFAFF* + ID_MODEL_FROM_DATABASE=Dangerous Prototypes BusPirate v4 Bootloader mode + +usb:v04D8pFB00* + ID_MODEL_FROM_DATABASE=Dangerous Prototypes BusPirate v4 + +usb:v04D8pFBB2* + ID_MODEL_FROM_DATABASE=GCUSB-nStep stepper motor controller + +usb:v04D8pFBBA* + ID_MODEL_FROM_DATABASE=DiscFerret Magnetic Disc Analyser (bootloader mode) + +usb:v04D8pFBBB* + ID_MODEL_FROM_DATABASE=DiscFerret Magnetic Disc Analyser (active mode) + +usb:v04D8pFC1E* + ID_MODEL_FROM_DATABASE=Bachrus Speedometer Interface + +usb:v04D8pFC92* + ID_MODEL_FROM_DATABASE=Open Bench Logic Sniffer + +usb:v04D8pFFEE* + ID_MODEL_FROM_DATABASE=Devantech USB-ISS + +usb:v04D8pFFEF* + ID_MODEL_FROM_DATABASE=PICoPLC [APStech] + +usb:v04D9* + ID_VENDOR_FROM_DATABASE=Holtek Semiconductor, Inc. + +usb:v04D9p0022* + ID_MODEL_FROM_DATABASE=Portable Keyboard + +usb:v04D9p048E* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v04D9p0499* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v04D9p1203* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04D9p1400* + ID_MODEL_FROM_DATABASE=PS/2 keyboard + mouse controller + +usb:v04D9p1503* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04D9p1603* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04D9p1702* + ID_MODEL_FROM_DATABASE=Keyboard LKS02 + +usb:v04D9p1818* + ID_MODEL_FROM_DATABASE=Keyboard [Diatec Filco Majestouch 2] + +usb:v04D9p2011* + ID_MODEL_FROM_DATABASE=Keyboard [Diatec Filco Majestouch 1] + +usb:v04D9p2013* + ID_MODEL_FROM_DATABASE=Keyboard [Das Keyboard] + +usb:v04D9p2221* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04D9p2323* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04D9p2519* + ID_MODEL_FROM_DATABASE=Shenzhen LogoTech 2.4GHz receiver + +usb:v04D9p2832* + ID_MODEL_FROM_DATABASE=HT82A832R Audio MCU + +usb:v04D9p2834* + ID_MODEL_FROM_DATABASE=HT82A834R Audio MCU + +usb:v04D9pA01C* + ID_MODEL_FROM_DATABASE=wireless multimedia keyboard with trackball [Trust ADURA 17911] + +usb:v04D9pA055* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v04DA* + ID_VENDOR_FROM_DATABASE=Panasonic (Matsushita) + +usb:v04DAp0901* + ID_MODEL_FROM_DATABASE=LS-120 Camera + +usb:v04DAp0912* + ID_MODEL_FROM_DATABASE=SDR-S10 + +usb:v04DAp0B01* + ID_MODEL_FROM_DATABASE=CD-R/RW Drive + +usb:v04DAp0B03* + ID_MODEL_FROM_DATABASE=SuperDisk 240MB + +usb:v04DAp0D01* + ID_MODEL_FROM_DATABASE=CD-R Drive KXL-840AN + +usb:v04DAp0D09* + ID_MODEL_FROM_DATABASE=CD-R Drive KXL-RW32AN + +usb:v04DAp0D0A* + ID_MODEL_FROM_DATABASE=CD-R Drive KXL-CB20AN + +usb:v04DAp0D0D* + ID_MODEL_FROM_DATABASE=CDRCB03 + +usb:v04DAp0D0E* + ID_MODEL_FROM_DATABASE=DVD-ROM & CD-R/RW + +usb:v04DAp0F40* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04DAp104D* + ID_MODEL_FROM_DATABASE=Elite Panaboard UB-T880 (HID) + +usb:v04DAp104E* + ID_MODEL_FROM_DATABASE=Elite Panaboard Pen Adaptor (HID) + +usb:v04DAp1500* + ID_MODEL_FROM_DATABASE=MFSUSB Driver + +usb:v04DAp1800* + ID_MODEL_FROM_DATABASE=DY-WL10 802.11abgn Adapter [Broadcom BCM4323] + +usb:v04DAp1B00* + ID_MODEL_FROM_DATABASE=MultiMediaCard + +usb:v04DAp2121* + ID_MODEL_FROM_DATABASE=EB-VS6 + +usb:v04DAp2316* + ID_MODEL_FROM_DATABASE=DVC Mass Storage Device + +usb:v04DAp2317* + ID_MODEL_FROM_DATABASE=DVC USB-SERIAL Driver for WinXP + +usb:v04DAp2318* + ID_MODEL_FROM_DATABASE=NV-GS11/230/250 (webcam mode) + +usb:v04DAp2319* + ID_MODEL_FROM_DATABASE=NV-GS15 (webcam mode) + +usb:v04DAp231A* + ID_MODEL_FROM_DATABASE=NV-GS11/230/250 (DV mode) + +usb:v04DAp231D* + ID_MODEL_FROM_DATABASE=DVC Web Camera Device + +usb:v04DAp231E* + ID_MODEL_FROM_DATABASE=DVC DV Stream Device + +usb:v04DAp2372* + ID_MODEL_FROM_DATABASE=Lumix Camera (Storage mode) + +usb:v04DAp2374* + ID_MODEL_FROM_DATABASE=Lumix Camera (PTP mode) + +usb:v04DAp2451* + ID_MODEL_FROM_DATABASE=HDC-SD9 + +usb:v04DAp245B* + ID_MODEL_FROM_DATABASE=HC-X920K (3MOS Full HD video camcorder) + +usb:v04DAp2497* + ID_MODEL_FROM_DATABASE=HDC-TM700 + +usb:v04DAp250C* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v04DAp250D* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v04DAp3904* + ID_MODEL_FROM_DATABASE=N5HBZ0000055 802.11abgn Wireless Adapter [Atheros AR7010+AR9280] + +usb:v04DAp3C04* + ID_MODEL_FROM_DATABASE=JT-P100MR-20 [ePassport Reader] + +usb:v04DB* + ID_VENDOR_FROM_DATABASE=Hypertec Pty, Ltd + +usb:v04DC* + ID_VENDOR_FROM_DATABASE=Huan Hsin Holdings, Ltd + +usb:v04DD* + ID_VENDOR_FROM_DATABASE=Sharp Corp. + +usb:v04DDp13A6* + ID_MODEL_FROM_DATABASE=MFC2000 + +usb:v04DDp6006* + ID_MODEL_FROM_DATABASE=AL-1216 + +usb:v04DDp6007* + ID_MODEL_FROM_DATABASE=AL-1045 + +usb:v04DDp6008* + ID_MODEL_FROM_DATABASE=AL-1255 + +usb:v04DDp6009* + ID_MODEL_FROM_DATABASE=AL-1530CS + +usb:v04DDp600A* + ID_MODEL_FROM_DATABASE=AL-1540CS + +usb:v04DDp600B* + ID_MODEL_FROM_DATABASE=AL-1456 + +usb:v04DDp600C* + ID_MODEL_FROM_DATABASE=AL-1555 + +usb:v04DDp600D* + ID_MODEL_FROM_DATABASE=AL-1225 + +usb:v04DDp600E* + ID_MODEL_FROM_DATABASE=AL-1551CS + +usb:v04DDp600F* + ID_MODEL_FROM_DATABASE=AR-122E + +usb:v04DDp6010* + ID_MODEL_FROM_DATABASE=AR-152E + +usb:v04DDp6011* + ID_MODEL_FROM_DATABASE=AR-157E + +usb:v04DDp6012* + ID_MODEL_FROM_DATABASE=SN-1045 + +usb:v04DDp6013* + ID_MODEL_FROM_DATABASE=SN-1255 + +usb:v04DDp6014* + ID_MODEL_FROM_DATABASE=SN-1456 + +usb:v04DDp6015* + ID_MODEL_FROM_DATABASE=SN-1555 + +usb:v04DDp6016* + ID_MODEL_FROM_DATABASE=AR-153E + +usb:v04DDp6017* + ID_MODEL_FROM_DATABASE=AR-122E N + +usb:v04DDp6018* + ID_MODEL_FROM_DATABASE=AR-153E N + +usb:v04DDp6019* + ID_MODEL_FROM_DATABASE=AR-152E N + +usb:v04DDp601A* + ID_MODEL_FROM_DATABASE=AR-157E N + +usb:v04DDp601B* + ID_MODEL_FROM_DATABASE=AL-1217 + +usb:v04DDp601C* + ID_MODEL_FROM_DATABASE=AL-1226 + +usb:v04DDp601D* + ID_MODEL_FROM_DATABASE=AR-123E + +usb:v04DDp6021* + ID_MODEL_FROM_DATABASE=IS01 + +usb:v04DDp7002* + ID_MODEL_FROM_DATABASE=DVC Ver.1.0 + +usb:v04DDp7004* + ID_MODEL_FROM_DATABASE=VE-CG40U Digital Still Camera + +usb:v04DDp7005* + ID_MODEL_FROM_DATABASE=VE-CG30 Digital Still Camera + +usb:v04DDp7007* + ID_MODEL_FROM_DATABASE=VL-Z7S Digital Camcorder + +usb:v04DDp8004* + ID_MODEL_FROM_DATABASE=Zaurus SL-5000D/SL-5500 PDA + +usb:v04DDp8005* + ID_MODEL_FROM_DATABASE=Zaurus A-300 + +usb:v04DDp8006* + ID_MODEL_FROM_DATABASE=Zaurus SL-B500/SL-5600 PDA + +usb:v04DDp8007* + ID_MODEL_FROM_DATABASE=Zaurus C-700 PDA + +usb:v04DDp9009* + ID_MODEL_FROM_DATABASE=AR-M160 + +usb:v04DDp9014* + ID_MODEL_FROM_DATABASE=IM-DR80 Portable NetMD Player + +usb:v04DDp9031* + ID_MODEL_FROM_DATABASE=Zaurus C-750/C-760/C-860/SL-C3000 PDA + +usb:v04DDp9032* + ID_MODEL_FROM_DATABASE=Zaurus SL-6000 + +usb:v04DDp903A* + ID_MODEL_FROM_DATABASE=GSM GPRS + +usb:v04DDp9050* + ID_MODEL_FROM_DATABASE=Zaurus C-860 PDA + +usb:v04DDp9056* + ID_MODEL_FROM_DATABASE=Viewcam Z + +usb:v04DDp9073* + ID_MODEL_FROM_DATABASE=AM-900 + +usb:v04DDp9074* + ID_MODEL_FROM_DATABASE=GSM GPRS + +usb:v04DDp90A9* + ID_MODEL_FROM_DATABASE=Sharp Composite + +usb:v04DDp90D0* + ID_MODEL_FROM_DATABASE=USB-to-Serial Comm. Port + +usb:v04DDp90F2* + ID_MODEL_FROM_DATABASE=Sharp 3G GSM USB Control + +usb:v04DDp9120* + ID_MODEL_FROM_DATABASE=WS004SH + +usb:v04DDp9122* + ID_MODEL_FROM_DATABASE=WS007SH + +usb:v04DDp9123* + ID_MODEL_FROM_DATABASE=W-ZERO3 ES Smartphone + +usb:v04DDp91A3* + ID_MODEL_FROM_DATABASE=922SH Internet Machine + +usb:v04DDp939A* + ID_MODEL_FROM_DATABASE=IS03 + +usb:v04DE* + ID_VENDOR_FROM_DATABASE=MindShare, Inc. + +usb:v04DF* + ID_VENDOR_FROM_DATABASE=Interlink Electronics + +usb:v04E1* + ID_VENDOR_FROM_DATABASE=Iiyama North America, Inc. + +usb:v04E1p0201* + ID_MODEL_FROM_DATABASE=Monitor Hub + +usb:v04E2* + ID_VENDOR_FROM_DATABASE=Exar Corp. + +usb:v04E2p1410* + ID_MODEL_FROM_DATABASE=XR21V1410 USB-UART IC + +usb:v04E3* + ID_VENDOR_FROM_DATABASE=Zilog, Inc. + +usb:v04E4* + ID_VENDOR_FROM_DATABASE=ACC Microelectronics + +usb:v04E5* + ID_VENDOR_FROM_DATABASE=Promise Technology + +usb:v04E6* + ID_VENDOR_FROM_DATABASE=SCM Microsystems, Inc. + +usb:v04E6p0001* + ID_MODEL_FROM_DATABASE=E-USB ATA Bridge + +usb:v04E6p0002* + ID_MODEL_FROM_DATABASE=eUSCSI SCSI Bridge + +usb:v04E6p0003* + ID_MODEL_FROM_DATABASE=eUSB SmartMedia Card Reader + +usb:v04E6p0005* + ID_MODEL_FROM_DATABASE=eUSB SmartMedia/CompactFlash Card Reader + +usb:v04E6p0006* + ID_MODEL_FROM_DATABASE=eUSB SmartMedia Card Reader + +usb:v04E6p0007* + ID_MODEL_FROM_DATABASE=Hifd + +usb:v04E6p0009* + ID_MODEL_FROM_DATABASE=eUSB ATA/ATAPI Adapter + +usb:v04E6p000A* + ID_MODEL_FROM_DATABASE=eUSB CompactFlash Adapter + +usb:v04E6p000B* + ID_MODEL_FROM_DATABASE=eUSCSI Bridge + +usb:v04E6p000C* + ID_MODEL_FROM_DATABASE=eUSCSI Bridge + +usb:v04E6p000D* + ID_MODEL_FROM_DATABASE=Dazzle MS + +usb:v04E6p0012* + ID_MODEL_FROM_DATABASE=Dazzle SD/MMC + +usb:v04E6p0101* + ID_MODEL_FROM_DATABASE=eUSB ATA Bridge (Sony Spressa USB CDRW) + +usb:v04E6p0311* + ID_MODEL_FROM_DATABASE=Dazzle DM-CF + +usb:v04E6p0312* + ID_MODEL_FROM_DATABASE=Dazzle DM-SD/MMC + +usb:v04E6p0313* + ID_MODEL_FROM_DATABASE=Dazzle SM + +usb:v04E6p0314* + ID_MODEL_FROM_DATABASE=Dazzle MS + +usb:v04E6p0322* + ID_MODEL_FROM_DATABASE=e-Film Reader-5 + +usb:v04E6p0325* + ID_MODEL_FROM_DATABASE=eUSB ORCA Quad Reader + +usb:v04E6p0327* + ID_MODEL_FROM_DATABASE=Digital Media Reader + +usb:v04E6p03FE* + ID_MODEL_FROM_DATABASE=DMHS2 DFU Adapter + +usb:v04E6p0406* + ID_MODEL_FROM_DATABASE=eUSB SmartDM Reader + +usb:v04E6p04E6* + ID_MODEL_FROM_DATABASE=eUSB DFU Adapter + +usb:v04E6p04E7* + ID_MODEL_FROM_DATABASE=STCII DFU Adapter + +usb:v04E6p04E8* + ID_MODEL_FROM_DATABASE=eUSBDM DFU Adapter + +usb:v04E6p04E9* + ID_MODEL_FROM_DATABASE=DM-E DFU Adapter + +usb:v04E6p0500* + ID_MODEL_FROM_DATABASE=Veridicom 5thSense Fingerprint Sensor and eUSB SmartCard + +usb:v04E6p0701* + ID_MODEL_FROM_DATABASE=DCS200 Loader Device + +usb:v04E6p0702* + ID_MODEL_FROM_DATABASE=DVD Creation Station 200 + +usb:v04E6p0703* + ID_MODEL_FROM_DATABASE=DVC100 Loader Device + +usb:v04E6p0704* + ID_MODEL_FROM_DATABASE=Digital Video Creator 100 + +usb:v04E6p1001* + ID_MODEL_FROM_DATABASE=SCR300 Smart Card Reader + +usb:v04E6p1010* + ID_MODEL_FROM_DATABASE=USBAT-2 CompactFlash Card Reader + +usb:v04E6p1014* + ID_MODEL_FROM_DATABASE=e-Film Reader-3 + +usb:v04E6p1020* + ID_MODEL_FROM_DATABASE=USBAT ATA/ATAPI Adapter + +usb:v04E6p2007* + ID_MODEL_FROM_DATABASE=RSA SecurID ComboReader + +usb:v04E6p2009* + ID_MODEL_FROM_DATABASE=Citibank Smart Card Reader + +usb:v04E6p200A* + ID_MODEL_FROM_DATABASE=Reflex v.2 Smart Card Reader + +usb:v04E6p200D* + ID_MODEL_FROM_DATABASE=STR391 Reader + +usb:v04E6p5111* + ID_MODEL_FROM_DATABASE=SCR331-DI SmartCard Reader + +usb:v04E6p5113* + ID_MODEL_FROM_DATABASE=SCR333 SmartCard Reader + +usb:v04E6p5114* + ID_MODEL_FROM_DATABASE=SCR331-DI SmartCard Reader + +usb:v04E6p5115* + ID_MODEL_FROM_DATABASE=SCR335 SmartCard Reader + +usb:v04E6p5116* + ID_MODEL_FROM_DATABASE=SCR331-LC1 / SCR3310 SmartCard Reader + +usb:v04E6p5117* + ID_MODEL_FROM_DATABASE=SCR3320 - Smart Card Reader + +usb:v04E6p5118* + ID_MODEL_FROM_DATABASE=Expresscard SIM Card Reader + +usb:v04E6p5119* + ID_MODEL_FROM_DATABASE=SCR3340 - ExpressCard54 Smart Card Reader + +usb:v04E6p511B* + ID_MODEL_FROM_DATABASE=SmartCard Reader + +usb:v04E6p511D* + ID_MODEL_FROM_DATABASE=SCR3311 Smart Card Reader + +usb:v04E6p5120* + ID_MODEL_FROM_DATABASE=SCR331-DI SmartCard Reader + +usb:v04E6p5121* + ID_MODEL_FROM_DATABASE=SDI010 Smart Card Reader + +usb:v04E6p5151* + ID_MODEL_FROM_DATABASE=SCR338 Keyboard Smart Card Reader + +usb:v04E6p5292* + ID_MODEL_FROM_DATABASE=SCL011 RFID reader + +usb:v04E6p5410* + ID_MODEL_FROM_DATABASE=SCR35xx Smart Card Reader + +usb:v04E6p5591* + ID_MODEL_FROM_DATABASE=SCL3711-NFC&RW + +usb:v04E6pE000* + ID_MODEL_FROM_DATABASE=SCRx31 Reader + +usb:v04E6pE001* + ID_MODEL_FROM_DATABASE=SCR331 SmartCard Reader + +usb:v04E6pE003* + ID_MODEL_FROM_DATABASE=SPR532 PinPad SmartCard Reader + +usb:v04E7* + ID_VENDOR_FROM_DATABASE=Elo TouchSystems + +usb:v04E7p0001* + ID_MODEL_FROM_DATABASE=TouchScreen + +usb:v04E7p0002* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface 2600 Rev 2 + +usb:v04E7p0004* + ID_MODEL_FROM_DATABASE=4000U CarrollTouch® Touchmonitor Interface + +usb:v04E7p0007* + ID_MODEL_FROM_DATABASE=2500U IntelliTouch® Touchmonitor Interface + +usb:v04E7p0008* + ID_MODEL_FROM_DATABASE=3000U AccuTouch® Touchmonitor Interface + +usb:v04E7p0009* + ID_MODEL_FROM_DATABASE=4000U CarrollTouch® Touchmonitor Interface + +usb:v04E7p0020* + ID_MODEL_FROM_DATABASE=Touchscreen Interface (2700) + +usb:v04E7p0021* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0030* + ID_MODEL_FROM_DATABASE=4500U CarrollTouch® Touchmonitor Interface + +usb:v04E7p0032* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0033* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0041* + ID_MODEL_FROM_DATABASE=5010 Surface Capacitive Touchmonitor Interface + +usb:v04E7p0042* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0050* + ID_MODEL_FROM_DATABASE=2216 AccuTouch® Touchmonitor Interface + +usb:v04E7p0071* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0072* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0081* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p0082* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E7p00FF* + ID_MODEL_FROM_DATABASE=Touchmonitor Interface + +usb:v04E8* + ID_VENDOR_FROM_DATABASE=Samsung Electronics Co., Ltd + +usb:v04E8p0100* + ID_MODEL_FROM_DATABASE=Kingston Flash Drive (128MB) + +usb:v04E8p0110* + ID_MODEL_FROM_DATABASE=Connect3D Flash Drive + +usb:v04E8p0111* + ID_MODEL_FROM_DATABASE=Connect3D Flash Drive + +usb:v04E8p0300* + ID_MODEL_FROM_DATABASE=E2530 / GT-C3350 Phones (Mass storage mode) + +usb:v04E8p1003* + ID_MODEL_FROM_DATABASE=MP3 Player and Recorder + +usb:v04E8p1006* + ID_MODEL_FROM_DATABASE=SDC-200Z + +usb:v04E8p130C* + ID_MODEL_FROM_DATABASE=NX100 + +usb:v04E8p1F05* + ID_MODEL_FROM_DATABASE=S2 Portable [JMicron] (500GB) + +usb:v04E8p1F06* + ID_MODEL_FROM_DATABASE=HX-MU064DA portable harddisk + +usb:v04E8p2018* + ID_MODEL_FROM_DATABASE=WIS09ABGN LinkStick Wireless LAN Adapter + +usb:v04E8p2035* + ID_MODEL_FROM_DATABASE=Digital Photo Frame Mass Storage + +usb:v04E8p2036* + ID_MODEL_FROM_DATABASE=Digital Photo Frame Mini Monitor + +usb:v04E8p3004* + ID_MODEL_FROM_DATABASE=ML-4600 + +usb:v04E8p3005* + ID_MODEL_FROM_DATABASE=Docuprint P1210 + +usb:v04E8p3008* + ID_MODEL_FROM_DATABASE=ML-6060 laser printer + +usb:v04E8p300C* + ID_MODEL_FROM_DATABASE=ML-1210 Printer + +usb:v04E8p300E* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3104* + ID_MODEL_FROM_DATABASE=ML-3550N + +usb:v04E8p3210* + ID_MODEL_FROM_DATABASE=ML-5200A Laser Printer + +usb:v04E8p3226* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3228* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p322A* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p322C* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3230* + ID_MODEL_FROM_DATABASE=ML-1440 + +usb:v04E8p3232* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3236* + ID_MODEL_FROM_DATABASE=ML-1450 + +usb:v04E8p3238* + ID_MODEL_FROM_DATABASE=ML-1430 + +usb:v04E8p323A* + ID_MODEL_FROM_DATABASE=ML-1710 Printer + +usb:v04E8p323B* + ID_MODEL_FROM_DATABASE=Phaser 3130 + +usb:v04E8p323C* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p323D* + ID_MODEL_FROM_DATABASE=Phaser 3120 + +usb:v04E8p323E* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3240* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p3242* + ID_MODEL_FROM_DATABASE=ML-1510 Laser Printer + +usb:v04E8p3248* + ID_MODEL_FROM_DATABASE=Color Laser Printer + +usb:v04E8p324A* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v04E8p324C* + ID_MODEL_FROM_DATABASE=ML-1740 Printer + +usb:v04E8p324D* + ID_MODEL_FROM_DATABASE=Phaser 3121 + +usb:v04E8p3256* + ID_MODEL_FROM_DATABASE=ML-1520 Laser Printer + +usb:v04E8p325B* + ID_MODEL_FROM_DATABASE=Xerox Phaser 3117 Laser Printer + +usb:v04E8p325F* + ID_MODEL_FROM_DATABASE=Phaser 3425 Laser Printer + +usb:v04E8p3260* + ID_MODEL_FROM_DATABASE=CLP-510 Color Laser Printer + +usb:v04E8p3268* + ID_MODEL_FROM_DATABASE=ML-1610 Mono Laser Printer + +usb:v04E8p326C* + ID_MODEL_FROM_DATABASE=ML-2010P Mono Laser Printer + +usb:v04E8p3276* + ID_MODEL_FROM_DATABASE=ML-3050/ML-3051 Laser Printer + +usb:v04E8p328E* + ID_MODEL_FROM_DATABASE=CLP-310 Color Laser Printer + +usb:v04E8p3292* + ID_MODEL_FROM_DATABASE=ML-1640 Series Laser Printer + +usb:v04E8p3296* + ID_MODEL_FROM_DATABASE=ML-2580N Mono Laser Printer + +usb:v04E8p3297* + ID_MODEL_FROM_DATABASE=ML-191x/ML-252x Laser Printer + +usb:v04E8p329F* + ID_MODEL_FROM_DATABASE=CLP-325 Color Laser Printer + +usb:v04E8p3301* + ID_MODEL_FROM_DATABASE=ML-1660 Series + +usb:v04E8p330C* + ID_MODEL_FROM_DATABASE=ML-1865 + +usb:v04E8p3310* + ID_MODEL_FROM_DATABASE=ML-331x Series Laser Printer + +usb:v04E8p3315* + ID_MODEL_FROM_DATABASE=ML-2540 Series Laser Printer + +usb:v04E8p331E* + ID_MODEL_FROM_DATABASE=M262x/M282x Xpress Series Laser Printer + +usb:v04E8p3409* + ID_MODEL_FROM_DATABASE=SCX-4216F Scanner + +usb:v04E8p340C* + ID_MODEL_FROM_DATABASE=SCX-5x15 series + +usb:v04E8p340D* + ID_MODEL_FROM_DATABASE=SCX-6x20 series + +usb:v04E8p340E* + ID_MODEL_FROM_DATABASE=MFP 560 series + +usb:v04E8p340F* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v04E8p3412* + ID_MODEL_FROM_DATABASE=SCX-4x20 series + +usb:v04E8p3413* + ID_MODEL_FROM_DATABASE=SCX-4100 Scanner + +usb:v04E8p3415* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p3419* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p341A* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v04E8p341B* + ID_MODEL_FROM_DATABASE=SCX-4200 series + +usb:v04E8p341C* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p341D* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p341F* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p3420* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04E8p3426* + ID_MODEL_FROM_DATABASE=SCX-4500 Laser Printer + +usb:v04E8p342D* + ID_MODEL_FROM_DATABASE=SCX-4x28 Series + +usb:v04E8p344F* + ID_MODEL_FROM_DATABASE=SCX-3400 Series + +usb:v04E8p3605* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v04E8p3606* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v04E8p3609* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v04E8p3902* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v04E8p3903* + ID_MODEL_FROM_DATABASE=Xerox WorkCentre XK50cx + +usb:v04E8p390F* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v04E8p3911* + ID_MODEL_FROM_DATABASE=SCX-1020 series + +usb:v04E8p4005* + ID_MODEL_FROM_DATABASE=GT-S8000 Jet (msc) + +usb:v04E8p4F1F* + ID_MODEL_FROM_DATABASE=GT-S8000 Jet (mtp) + +usb:v04E8p5000* + ID_MODEL_FROM_DATABASE=YP-MF series + +usb:v04E8p5001* + ID_MODEL_FROM_DATABASE=YP-100 + +usb:v04E8p5002* + ID_MODEL_FROM_DATABASE=YP-30 + +usb:v04E8p5003* + ID_MODEL_FROM_DATABASE=YP-700 + +usb:v04E8p5004* + ID_MODEL_FROM_DATABASE=YP-30 + +usb:v04E8p5005* + ID_MODEL_FROM_DATABASE=YP-300 + +usb:v04E8p5006* + ID_MODEL_FROM_DATABASE=YP-750 + +usb:v04E8p500D* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v04E8p5010* + ID_MODEL_FROM_DATABASE=Yepp YP-35 + +usb:v04E8p5011* + ID_MODEL_FROM_DATABASE=YP-780 + +usb:v04E8p5013* + ID_MODEL_FROM_DATABASE=YP-60 + +usb:v04E8p5015* + ID_MODEL_FROM_DATABASE=yepp upgrade + +usb:v04E8p501B* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v04E8p5021* + ID_MODEL_FROM_DATABASE=Yepp YP-ST5 + +usb:v04E8p5026* + ID_MODEL_FROM_DATABASE=YP-MT6V + +usb:v04E8p5027* + ID_MODEL_FROM_DATABASE=YP-T7 + +usb:v04E8p502B* + ID_MODEL_FROM_DATABASE=YP-F1 + +usb:v04E8p5032* + ID_MODEL_FROM_DATABASE=YP-J70 + +usb:v04E8p503B* + ID_MODEL_FROM_DATABASE=YP-U1 MP3 Player + +usb:v04E8p503D* + ID_MODEL_FROM_DATABASE=YP-T7F + +usb:v04E8p5041* + ID_MODEL_FROM_DATABASE=YP-Z5 + +usb:v04E8p5050* + ID_MODEL_FROM_DATABASE=YP-U2 MP3 Player + +usb:v04E8p5051* + ID_MODEL_FROM_DATABASE=YP-F2R + +usb:v04E8p5055* + ID_MODEL_FROM_DATABASE=YP-T9 + +usb:v04E8p507D* + ID_MODEL_FROM_DATABASE=YP-U3 (mtp) + +usb:v04E8p507F* + ID_MODEL_FROM_DATABASE=YP-T9J + +usb:v04E8p5080* + ID_MODEL_FROM_DATABASE=Yepp YP-K3 (msc) + +usb:v04E8p5081* + ID_MODEL_FROM_DATABASE=Yepp YP-K3 (mtp) + +usb:v04E8p5082* + ID_MODEL_FROM_DATABASE=YP-P2 (msc) + +usb:v04E8p5083* + ID_MODEL_FROM_DATABASE=YP-P2 (mtp) + +usb:v04E8p508A* + ID_MODEL_FROM_DATABASE=YP-T10 + +usb:v04E8p508B* + ID_MODEL_FROM_DATABASE=YP-S5 MP3 Player + +usb:v04E8p508C* + ID_MODEL_FROM_DATABASE=YP-S5 + +usb:v04E8p5090* + ID_MODEL_FROM_DATABASE=YP-S3 (msc) + +usb:v04E8p5091* + ID_MODEL_FROM_DATABASE=YP-S3 (mtp) + +usb:v04E8p5092* + ID_MODEL_FROM_DATABASE=YP-U4 (msc) + +usb:v04E8p5093* + ID_MODEL_FROM_DATABASE=YP-U4 (mtp) + +usb:v04E8p5095* + ID_MODEL_FROM_DATABASE=YP-S2 + +usb:v04E8p510F* + ID_MODEL_FROM_DATABASE=YP-R1 + +usb:v04E8p5119* + ID_MODEL_FROM_DATABASE=Yepp YP-P3 + +usb:v04E8p511C* + ID_MODEL_FROM_DATABASE=YP-Q2 + +usb:v04E8p5121* + ID_MODEL_FROM_DATABASE=YP-U5 + +usb:v04E8p5123* + ID_MODEL_FROM_DATABASE=Yepp YP-M1 + +usb:v04E8p5A00* + ID_MODEL_FROM_DATABASE=YP-NEU + +usb:v04E8p5A01* + ID_MODEL_FROM_DATABASE=YP-NDU + +usb:v04E8p5A03* + ID_MODEL_FROM_DATABASE=Yepp MP3 Player + +usb:v04E8p5A04* + ID_MODEL_FROM_DATABASE=YP-800 + +usb:v04E8p5A08* + ID_MODEL_FROM_DATABASE=YP-90 + +usb:v04E8p5A0F* + ID_MODEL_FROM_DATABASE=Meizu M6 MiniPlayer + +usb:v04E8p5B01* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v04E8p5B02* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v04E8p5B03* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v04E8p5B04* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v04E8p5B05* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v04E8p5B11* + ID_MODEL_FROM_DATABASE=SEW-2001u Card + +usb:v04E8p5F00* + ID_MODEL_FROM_DATABASE=NEXiO Sync + +usb:v04E8p5F01* + ID_MODEL_FROM_DATABASE=NEXiO Sync + +usb:v04E8p5F02* + ID_MODEL_FROM_DATABASE=NEXiO Sync + +usb:v04E8p5F03* + ID_MODEL_FROM_DATABASE=NEXiO Sync + +usb:v04E8p5F04* + ID_MODEL_FROM_DATABASE=NEXiO Sync + +usb:v04E8p5F05* + ID_MODEL_FROM_DATABASE=STORY Station 1TB + +usb:v04E8p6032* + ID_MODEL_FROM_DATABASE=G2 Portable hard drive + +usb:v04E8p6033* + ID_MODEL_FROM_DATABASE=G2 Portable device + +usb:v04E8p6034* + ID_MODEL_FROM_DATABASE=G2 Portable hard drive + +usb:v04E8p60B3* + ID_MODEL_FROM_DATABASE=M2 Portable Hard Drive + +usb:v04E8p60C4* + ID_MODEL_FROM_DATABASE=M2 Portable Hard Drive USB 3.0 + +usb:v04E8p6124* + ID_MODEL_FROM_DATABASE=D3 Station External Hard Drive + +usb:v04E8p6125* + ID_MODEL_FROM_DATABASE=D3 Station External Hard Drive + +usb:v04E8p61B5* + ID_MODEL_FROM_DATABASE=M3 Portable Hard Drive 2TB + +usb:v04E8p61B6* + ID_MODEL_FROM_DATABASE=M3 Portable Hard Drive 1TB + +usb:v04E8p61F3* + ID_MODEL_FROM_DATABASE=MU-PT500B [T3 500GB USB SSD] + +usb:v04E8p6601* + ID_MODEL_FROM_DATABASE=Mobile Phone + +usb:v04E8p6602* + ID_MODEL_FROM_DATABASE=Galaxy + +usb:v04E8p6603* + ID_MODEL_FROM_DATABASE=Galaxy + +usb:v04E8p6611* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6613* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6615* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6617* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6619* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p661B* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p661E* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v04E8p6620* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v04E8p6622* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v04E8p6624* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v04E8p662E* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6630* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p6632* + ID_MODEL_FROM_DATABASE=MITs Sync + +usb:v04E8p663E* + ID_MODEL_FROM_DATABASE=D900e/B2100 Phone + +usb:v04E8p663F* + ID_MODEL_FROM_DATABASE=SGH-E720/SGH-E840 + +usb:v04E8p6640* + ID_MODEL_FROM_DATABASE=Usb Modem Enumerator + +usb:v04E8p6651* + ID_MODEL_FROM_DATABASE=i8510 Innov8 + +usb:v04E8p6702* + ID_MODEL_FROM_DATABASE=X830 + +usb:v04E8p6708* + ID_MODEL_FROM_DATABASE=U600 Phone + +usb:v04E8p6709* + ID_MODEL_FROM_DATABASE=U600 + +usb:v04E8p6734* + ID_MODEL_FROM_DATABASE=Juke + +usb:v04E8p6759* + ID_MODEL_FROM_DATABASE=D900e/B2100 Media Player + +usb:v04E8p675A* + ID_MODEL_FROM_DATABASE=D900e/B2100 Mass Storage + +usb:v04E8p675B* + ID_MODEL_FROM_DATABASE=D900e Camera + +usb:v04E8p6772* + ID_MODEL_FROM_DATABASE=Standalone LTE device (Trial) + +usb:v04E8p6795* + ID_MODEL_FROM_DATABASE=S5230 + +usb:v04E8p6802* + ID_MODEL_FROM_DATABASE=Standalone HSPA device + +usb:v04E8p6806* + ID_MODEL_FROM_DATABASE=Composite LTE device (Trial) + +usb:v04E8p6807* + ID_MODEL_FROM_DATABASE=Composite HSPA device + +usb:v04E8p681C* + ID_MODEL_FROM_DATABASE=Galaxy Portal/Spica/S + +usb:v04E8p681D* + ID_MODEL_FROM_DATABASE=Galaxy Portal/Spica Android Phone + +usb:v04E8p6843* + ID_MODEL_FROM_DATABASE=E2530 Phone (Samsung Kies mode) + +usb:v04E8p684E* + ID_MODEL_FROM_DATABASE=Wave (GT-S8500) + +usb:v04E8p685B* + ID_MODEL_FROM_DATABASE=GT-I9100 Phone [Galaxy S II] (mass storage mode) + +usb:v04E8p685C* + ID_MODEL_FROM_DATABASE=GT-I9250 Phone [Galaxy Nexus] (Mass storage mode) + +usb:v04E8p685D* + ID_MODEL_FROM_DATABASE=GT-I9100 Phone [Galaxy S II] (Download mode) + +usb:v04E8p685E* + ID_MODEL_FROM_DATABASE=GT-I9100 / GT-C3350 Phones (USB Debugging mode) + +usb:v04E8p6860* + ID_MODEL_FROM_DATABASE=Galaxy (MTP) + +usb:v04E8p6863* + ID_MODEL_FROM_DATABASE=GT-I9500 [Galaxy S4] / GT-I9250 [Galaxy Nexus] (network tethering) + +usb:v04E8p6864* + ID_MODEL_FROM_DATABASE=GT-I9070 (network tethering, USB debugging enabled) + +usb:v04E8p6865* + ID_MODEL_FROM_DATABASE=GT-I9300 Phone [Galaxy S III] (PTP mode) + +usb:v04E8p6866* + ID_MODEL_FROM_DATABASE=GT-I9300 Phone [Galaxy S III] (debugging mode) + +usb:v04E8p6868* + ID_MODEL_FROM_DATABASE=Escape Composite driver for Android Phones: Modem+Diagnostic+ADB + +usb:v04E8p6875* + ID_MODEL_FROM_DATABASE=GT-B3710 Standalone LTE device (Commercial) + +usb:v04E8p6876* + ID_MODEL_FROM_DATABASE=GT-B3710 LTE Modem + +usb:v04E8p6877* + ID_MODEL_FROM_DATABASE=Galaxy S + +usb:v04E8p687A* + ID_MODEL_FROM_DATABASE=GT-E2370 mobile phone + +usb:v04E8p6888* + ID_MODEL_FROM_DATABASE=GT-B3730 Composite LTE device (Commercial) + +usb:v04E8p6889* + ID_MODEL_FROM_DATABASE=GT-B3730 Composite LTE device (Commercial) + +usb:v04E8p689A* + ID_MODEL_FROM_DATABASE=LTE Storage Driver [CMC2xx] + +usb:v04E8p689E* + ID_MODEL_FROM_DATABASE=GT-S5670 [Galaxy Fit] + +usb:v04E8p68AA* + ID_MODEL_FROM_DATABASE=Reality + +usb:v04E8p7011* + ID_MODEL_FROM_DATABASE=SEW-2003U Card + +usb:v04E8p7021* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v04E8p7061* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v04E8p7080* + ID_MODEL_FROM_DATABASE=Anycall SCH-W580 + +usb:v04E8p7081* + ID_MODEL_FROM_DATABASE=Human Interface Device + +usb:v04E8p8001* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v04E8pE020* + ID_MODEL_FROM_DATABASE=SERI E02 SCOM 6200 UMTS Phone + +usb:v04E8pE021* + ID_MODEL_FROM_DATABASE=SERI E02 SCOM 6200 Virtual UARTs + +usb:v04E8pE022* + ID_MODEL_FROM_DATABASE=SERI E02 SCOM 6200 Flash Load Disk + +usb:v04E8pF000* + ID_MODEL_FROM_DATABASE=Intensity 3 (Mass Storage Mode) + +usb:v04E8pFF30* + ID_MODEL_FROM_DATABASE=SG_iMON + +usb:v04E9* + ID_VENDOR_FROM_DATABASE=PC-Tel, Inc. + +usb:v04EA* + ID_VENDOR_FROM_DATABASE=Brooktree Corp. + +usb:v04EB* + ID_VENDOR_FROM_DATABASE=Northstar Systems, Inc. + +usb:v04EBpE004* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v04EC* + ID_VENDOR_FROM_DATABASE=Tokyo Electron Device, Ltd + +usb:v04ED* + ID_VENDOR_FROM_DATABASE=Annabooks + +usb:v04EF* + ID_VENDOR_FROM_DATABASE=Pacific Electronic International, Inc. + +usb:v04F0* + ID_VENDOR_FROM_DATABASE=Daewoo Electronics Co., Ltd + +usb:v04F1* + ID_VENDOR_FROM_DATABASE=Victor Company of Japan, Ltd + +usb:v04F1p0001* + ID_MODEL_FROM_DATABASE=GC-QX3 Digital Still Camera + +usb:v04F1p0004* + ID_MODEL_FROM_DATABASE=GR-DVL815U Digital Video Camera + +usb:v04F1p0006* + ID_MODEL_FROM_DATABASE=DV Camera Storage + +usb:v04F1p0008* + ID_MODEL_FROM_DATABASE=GZ-MG30AA/MC500E Digital Video Camera + +usb:v04F1p0009* + ID_MODEL_FROM_DATABASE=GR-DX25EK Digital Video Camera + +usb:v04F1p000A* + ID_MODEL_FROM_DATABASE=GR-D72 Digital Video Camera + +usb:v04F1p1001* + ID_MODEL_FROM_DATABASE=GC-A50 Camera Device + +usb:v04F1p3008* + ID_MODEL_FROM_DATABASE=MP-PRX1 Ethernet + +usb:v04F1p3009* + ID_MODEL_FROM_DATABASE=MP-XP7250 WLAN Adapter + +usb:v04F2* + ID_VENDOR_FROM_DATABASE=Chicony Electronics Co., Ltd + +usb:v04F2p0001* + ID_MODEL_FROM_DATABASE=KU-8933 Keyboard + +usb:v04F2p0002* + ID_MODEL_FROM_DATABASE=NT68P81 Keyboard + +usb:v04F2p0110* + ID_MODEL_FROM_DATABASE=KU-2971 Keyboard + +usb:v04F2p0111* + ID_MODEL_FROM_DATABASE=KU-9908 Keyboard + +usb:v04F2p0112* + ID_MODEL_FROM_DATABASE=KU-8933 Keyboard with PS/2 Mouse port + +usb:v04F2p0116* + ID_MODEL_FROM_DATABASE=KU-2971/KU-0325 Keyboard + +usb:v04F2p0200* + ID_MODEL_FROM_DATABASE=KBR-0108 + +usb:v04F2p0201* + ID_MODEL_FROM_DATABASE=Gaming Keyboard KPD0250 + +usb:v04F2p0220* + ID_MODEL_FROM_DATABASE=Wireless HID Receiver + +usb:v04F2p0402* + ID_MODEL_FROM_DATABASE=Genius LuxeMate i200 Keyboard + +usb:v04F2p0403* + ID_MODEL_FROM_DATABASE=KU-0420 keyboard + +usb:v04F2p0418* + ID_MODEL_FROM_DATABASE=KU-0418 Tactical Pad + +usb:v04F2p0618* + ID_MODEL_FROM_DATABASE=RG-0618U Wireless HID Receiver & KG-0609 Wireless Keyboard with Touchpad + +usb:v04F2p0718* + ID_MODEL_FROM_DATABASE=wired mouse + +usb:v04F2p0760* + ID_MODEL_FROM_DATABASE=Acer KU-0760 Keyboard + +usb:v04F2p0841* + ID_MODEL_FROM_DATABASE=HP Multimedia Keyboard + +usb:v04F2p0860* + ID_MODEL_FROM_DATABASE=2.4G Multimedia Wireless Kit + +usb:v04F2p1061* + ID_MODEL_FROM_DATABASE=HP KG-1061 Wireless Keyboard+Mouse + +usb:v04F2p1121* + ID_MODEL_FROM_DATABASE=Periboard 717 Mini Wireless Keyboard + +usb:v04F2pA001* + ID_MODEL_FROM_DATABASE=E-Video DC-100 Camera + +usb:v04F2pA120* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam(PC370R) + +usb:v04F2pA121* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam(PC370R) + +usb:v04F2pA122* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam(PC370R) + +usb:v04F2pA123* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam(PC370R) + +usb:v04F2pA124* + ID_MODEL_FROM_DATABASE=ORITE CCD Webcam(PC370R) + +usb:v04F2pA128* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C202 + OV7663 + EEPROM) + +usb:v04F2pA133* + ID_MODEL_FROM_DATABASE=Gateway Webcam + +usb:v04F2pA136* + ID_MODEL_FROM_DATABASE=LabTec Webcam 5500 + +usb:v04F2pA147* + ID_MODEL_FROM_DATABASE=Medion Webcam + +usb:v04F2pA204* + ID_MODEL_FROM_DATABASE=DSC WIA Device (1300) + +usb:v04F2pA208* + ID_MODEL_FROM_DATABASE=DSC WIA Device (2320) + +usb:v04F2pA209* + ID_MODEL_FROM_DATABASE=Labtec DC-2320 + +usb:v04F2pA20A* + ID_MODEL_FROM_DATABASE=DSC WIA Device (3310) + +usb:v04F2pA20C* + ID_MODEL_FROM_DATABASE=DSC WIA Device (3320) + +usb:v04F2pA210* + ID_MODEL_FROM_DATABASE=Audio Device + +usb:v04F2pB008* + ID_MODEL_FROM_DATABASE=USB 2.0 Camera + +usb:v04F2pB009* + ID_MODEL_FROM_DATABASE=Integrated Camera + +usb:v04F2pB010* + ID_MODEL_FROM_DATABASE=Integrated Camera + +usb:v04F2pB012* + ID_MODEL_FROM_DATABASE=1.3 MPixel UVC Webcam + +usb:v04F2pB013* + ID_MODEL_FROM_DATABASE=USB 2.0 Camera + +usb:v04F2pB015* + ID_MODEL_FROM_DATABASE=VGA 24fps UVC Webcam + +usb:v04F2pB016* + ID_MODEL_FROM_DATABASE=VGA 30fps UVC Webcam + +usb:v04F2pB018* + ID_MODEL_FROM_DATABASE=2M UVC Webcam + +usb:v04F2pB021* + ID_MODEL_FROM_DATABASE=ViewSonic 1.3M, USB2.0 Webcam + +usb:v04F2pB022* + ID_MODEL_FROM_DATABASE=Gateway USB 2.0 Webcam + +usb:v04F2pB023* + ID_MODEL_FROM_DATABASE=Gateway USB 2.0 Webcam + +usb:v04F2pB024* + ID_MODEL_FROM_DATABASE=USB 2.0 Webcam + +usb:v04F2pB025* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04F2pB027* + ID_MODEL_FROM_DATABASE=Gateway USB 2.0 Webcam + +usb:v04F2pB028* + ID_MODEL_FROM_DATABASE=VGA UVC Webcam + +usb:v04F2pB029* + ID_MODEL_FROM_DATABASE=1.3M UVC Webcam + +usb:v04F2pB036* + ID_MODEL_FROM_DATABASE=Asus Integrated 0.3M UVC Webcam + +usb:v04F2pB044* + ID_MODEL_FROM_DATABASE=Acer CrystalEye Webcam + +usb:v04F2pB057* + ID_MODEL_FROM_DATABASE=integrated USB webcam + +usb:v04F2pB059* + ID_MODEL_FROM_DATABASE=CKF7037 HP webcam + +usb:v04F2pB064* + ID_MODEL_FROM_DATABASE=CNA7137 Integrated Webcam + +usb:v04F2pB070* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04F2pB071* + ID_MODEL_FROM_DATABASE=2.0M UVC Webcam / CNF7129 + +usb:v04F2pB083* + ID_MODEL_FROM_DATABASE=CKF7063 Webcam (HP) + +usb:v04F2pB091* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v04F2pB104* + ID_MODEL_FROM_DATABASE=CNF7069 Webcam + +usb:v04F2pB107* + ID_MODEL_FROM_DATABASE=CNF7070 Webcam + +usb:v04F2pB14C* + ID_MODEL_FROM_DATABASE=CNF8050 Webcam + +usb:v04F2pB15C* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Camera + +usb:v04F2pB175* + ID_MODEL_FROM_DATABASE=4-Port Hub + +usb:v04F2pB1AA* + ID_MODEL_FROM_DATABASE=Webcam-101 + +usb:v04F2pB1B4* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Camera + +usb:v04F2pB1B9* + ID_MODEL_FROM_DATABASE=Asus Integrated Webcam + +usb:v04F2pB1CF* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Camera + +usb:v04F2pB1D6* + ID_MODEL_FROM_DATABASE=CNF9055 Toshiba Webcam + +usb:v04F2pB1E4* + ID_MODEL_FROM_DATABASE=Toshiba Integrated Webcam + +usb:v04F2pB213* + ID_MODEL_FROM_DATABASE=Fujitsu Integrated Camera + +usb:v04F2pB217* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Camera (0.3MP) + +usb:v04F2pB221* + ID_MODEL_FROM_DATABASE=integrated camera + +usb:v04F2pB230* + ID_MODEL_FROM_DATABASE=Integrated HP HD Webcam + +usb:v04F2pB257* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Camera + +usb:v04F2pB26B* + ID_MODEL_FROM_DATABASE=Sony Visual Communication Camera + +usb:v04F2pB272* + ID_MODEL_FROM_DATABASE=Lenovo EasyCamera + +usb:v04F2pB2B0* + ID_MODEL_FROM_DATABASE=Camera + +usb:v04F2pB2B9* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Camera UVC + +usb:v04F2pB2DA* + ID_MODEL_FROM_DATABASE=thinkpad t430s camera + +usb:v04F2pB2EA* + ID_MODEL_FROM_DATABASE=Integrated Camera [ThinkPad] + +usb:v04F2pB330* + ID_MODEL_FROM_DATABASE=Asus 720p CMOS webcam + +usb:v04F2pB354* + ID_MODEL_FROM_DATABASE=UVC 1.00 device HD UVC WebCam + +usb:v04F2pB394* + ID_MODEL_FROM_DATABASE=Integrated Camera + +usb:v04F2pB3F6* + ID_MODEL_FROM_DATABASE=HD WebCam (Acer) + +usb:v04F2pB40E* + ID_MODEL_FROM_DATABASE=HP Truevision HD camera + +usb:v04F2pB444* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v04F3* + ID_VENDOR_FROM_DATABASE=Elan Microelectronics Corp. + +usb:v04F3p000A* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v04F3p0103* + ID_MODEL_FROM_DATABASE=ActiveJet K-2024 Multimedia Keyboard + +usb:v04F3p01A4* + ID_MODEL_FROM_DATABASE=Wireless Keyboard + +usb:v04F3p0201* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v04F3p0210* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v04F3p0212* + ID_MODEL_FROM_DATABASE=Laser Mouse + +usb:v04F3p0214* + ID_MODEL_FROM_DATABASE=Lynx M9 Optical Mouse + +usb:v04F3p0230* + ID_MODEL_FROM_DATABASE=3D Optical Mouse + +usb:v04F3p0232* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v04F3p02F4* + ID_MODEL_FROM_DATABASE=2.4G Cordless Mouse + +usb:v04F3p0381* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v04F3p04A0* + ID_MODEL_FROM_DATABASE=Dream Cheeky Stress/Panic Button + +usb:v04F4* + ID_VENDOR_FROM_DATABASE=Harting Elektronik, Inc. + +usb:v04F5* + ID_VENDOR_FROM_DATABASE=Fujitsu-ICL Systems, Inc. + +usb:v04F6* + ID_VENDOR_FROM_DATABASE=Norand Corp. + +usb:v04F7* + ID_VENDOR_FROM_DATABASE=Newnex Technology Corp. + +usb:v04F8* + ID_VENDOR_FROM_DATABASE=FuturePlus Systems + +usb:v04F9* + ID_VENDOR_FROM_DATABASE=Brother Industries, Ltd + +usb:v04F9p0002* + ID_MODEL_FROM_DATABASE=HL-1050 Laser Printer + +usb:v04F9p0005* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0006* + ID_MODEL_FROM_DATABASE=HL-1240 Laser Printer + +usb:v04F9p0007* + ID_MODEL_FROM_DATABASE=HL-1250 Laser Printer + +usb:v04F9p0008* + ID_MODEL_FROM_DATABASE=HL-1270 Laser Printer + +usb:v04F9p0009* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p000A* + ID_MODEL_FROM_DATABASE=P2500 series + +usb:v04F9p000B* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p000C* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p000D* + ID_MODEL_FROM_DATABASE=HL-1440 Laser Printer + +usb:v04F9p000E* + ID_MODEL_FROM_DATABASE=HL-1450 series + +usb:v04F9p000F* + ID_MODEL_FROM_DATABASE=HL-1470N series + +usb:v04F9p0010* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0011* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0012* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0013* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0014* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0015* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0016* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0017* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0018* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p001A* + ID_MODEL_FROM_DATABASE=HL-1430 Laser Printer + +usb:v04F9p001C* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p001E* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0020* + ID_MODEL_FROM_DATABASE=HL-5130 series + +usb:v04F9p0021* + ID_MODEL_FROM_DATABASE=HL-5140 series + +usb:v04F9p0022* + ID_MODEL_FROM_DATABASE=HL-5150D series + +usb:v04F9p0023* + ID_MODEL_FROM_DATABASE=HL-5170DN series + +usb:v04F9p0024* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0025* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0027* + ID_MODEL_FROM_DATABASE=HL-2030 Laser Printer + +usb:v04F9p0028* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0029* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p002A* + ID_MODEL_FROM_DATABASE=HL-52x0 series + +usb:v04F9p002B* + ID_MODEL_FROM_DATABASE=HL-5250DN Printer + +usb:v04F9p002C* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p002D* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p0039* + ID_MODEL_FROM_DATABASE=HL-5340 series + +usb:v04F9p0042* + ID_MODEL_FROM_DATABASE=HL-2270DW Laser Printer + +usb:v04F9p0100* + ID_MODEL_FROM_DATABASE=MFC8600/9650 series + +usb:v04F9p0101* + ID_MODEL_FROM_DATABASE=MFC9600/9870 series + +usb:v04F9p0102* + ID_MODEL_FROM_DATABASE=MFC9750/1200 series + +usb:v04F9p0104* + ID_MODEL_FROM_DATABASE=MFC-8300J + +usb:v04F9p0105* + ID_MODEL_FROM_DATABASE=MFC-9600J + +usb:v04F9p0106* + ID_MODEL_FROM_DATABASE=MFC-7300C + +usb:v04F9p0107* + ID_MODEL_FROM_DATABASE=MFC-7400C + +usb:v04F9p0108* + ID_MODEL_FROM_DATABASE=MFC-9200C + +usb:v04F9p0109* + ID_MODEL_FROM_DATABASE=MFC-830 + +usb:v04F9p010A* + ID_MODEL_FROM_DATABASE=MFC-840 + +usb:v04F9p010B* + ID_MODEL_FROM_DATABASE=MFC-860 + +usb:v04F9p010C* + ID_MODEL_FROM_DATABASE=MFC-7400J + +usb:v04F9p010D* + ID_MODEL_FROM_DATABASE=MFC-9200J + +usb:v04F9p010E* + ID_MODEL_FROM_DATABASE=MFC-3100C Scanner + +usb:v04F9p010F* + ID_MODEL_FROM_DATABASE=MFC-5100C + +usb:v04F9p0110* + ID_MODEL_FROM_DATABASE=MFC-4800 Scanner + +usb:v04F9p0111* + ID_MODEL_FROM_DATABASE=MFC-6800 + +usb:v04F9p0112* + ID_MODEL_FROM_DATABASE=DCP1000 Port(FaxModem) + +usb:v04F9p0113* + ID_MODEL_FROM_DATABASE=MFC-8500 + +usb:v04F9p0114* + ID_MODEL_FROM_DATABASE=MFC9700 Port(FaxModem) + +usb:v04F9p0115* + ID_MODEL_FROM_DATABASE=MFC-9800 Scanner + +usb:v04F9p0116* + ID_MODEL_FROM_DATABASE=DCP1400 Scanner + +usb:v04F9p0119* + ID_MODEL_FROM_DATABASE=MFC-9660 + +usb:v04F9p011A* + ID_MODEL_FROM_DATABASE=MFC-9860 + +usb:v04F9p011B* + ID_MODEL_FROM_DATABASE=MFC-9880 + +usb:v04F9p011C* + ID_MODEL_FROM_DATABASE=MFC-9760 + +usb:v04F9p011D* + ID_MODEL_FROM_DATABASE=MFC-9070 + +usb:v04F9p011E* + ID_MODEL_FROM_DATABASE=MFC-9180 + +usb:v04F9p011F* + ID_MODEL_FROM_DATABASE=MFC-9160 + +usb:v04F9p0120* + ID_MODEL_FROM_DATABASE=MFC580 Port(FaxModem) + +usb:v04F9p0121* + ID_MODEL_FROM_DATABASE=MFC-590 + +usb:v04F9p0122* + ID_MODEL_FROM_DATABASE=MFC-5100J + +usb:v04F9p0124* + ID_MODEL_FROM_DATABASE=MFC-4800J + +usb:v04F9p0125* + ID_MODEL_FROM_DATABASE=MFC-6800J + +usb:v04F9p0127* + ID_MODEL_FROM_DATABASE=MFC-9800J + +usb:v04F9p0128* + ID_MODEL_FROM_DATABASE=MFC-8500J + +usb:v04F9p0129* + ID_MODEL_FROM_DATABASE=Imagistics 2500 (MFC-8640D clone) + +usb:v04F9p012B* + ID_MODEL_FROM_DATABASE=MFC-9030 + +usb:v04F9p012E* + ID_MODEL_FROM_DATABASE=FAX4100e IntelliFax 4100e + +usb:v04F9p012F* + ID_MODEL_FROM_DATABASE=FAX-4750e + +usb:v04F9p0130* + ID_MODEL_FROM_DATABASE=FAX-5750e + +usb:v04F9p0132* + ID_MODEL_FROM_DATABASE=MFC-5200C RemovableDisk + +usb:v04F9p0135* + ID_MODEL_FROM_DATABASE=MFC-100 Scanner + +usb:v04F9p0136* + ID_MODEL_FROM_DATABASE=MFC-150CL Scanner + +usb:v04F9p013C* + ID_MODEL_FROM_DATABASE=MFC-890 Port + +usb:v04F9p013D* + ID_MODEL_FROM_DATABASE=MFC-5200J + +usb:v04F9p013E* + ID_MODEL_FROM_DATABASE=MFC-4420C RemovableDisk + +usb:v04F9p013F* + ID_MODEL_FROM_DATABASE=MFC-4820C RemovableDisk + +usb:v04F9p0140* + ID_MODEL_FROM_DATABASE=DCP-8020 + +usb:v04F9p0141* + ID_MODEL_FROM_DATABASE=DCP-8025D + +usb:v04F9p0142* + ID_MODEL_FROM_DATABASE=MFC-8420 + +usb:v04F9p0143* + ID_MODEL_FROM_DATABASE=MFC-8820D + +usb:v04F9p0144* + ID_MODEL_FROM_DATABASE=DCP-4020C RemovableDisk + +usb:v04F9p0146* + ID_MODEL_FROM_DATABASE=MFC-3220C + +usb:v04F9p0147* + ID_MODEL_FROM_DATABASE=FAX-1820C Printer + +usb:v04F9p0148* + ID_MODEL_FROM_DATABASE=MFC-3320CN + +usb:v04F9p0149* + ID_MODEL_FROM_DATABASE=FAX-1920CN Printer + +usb:v04F9p014A* + ID_MODEL_FROM_DATABASE=MFC-3420C + +usb:v04F9p014B* + ID_MODEL_FROM_DATABASE=MFC-3820CN + +usb:v04F9p014C* + ID_MODEL_FROM_DATABASE=DCP-3020C + +usb:v04F9p014D* + ID_MODEL_FROM_DATABASE=FAX-1815C Printer + +usb:v04F9p014E* + ID_MODEL_FROM_DATABASE=MFC-8820J + +usb:v04F9p014F* + ID_MODEL_FROM_DATABASE=DCP-8025J + +usb:v04F9p0150* + ID_MODEL_FROM_DATABASE=MFC-8220 Port(FaxModem) + +usb:v04F9p0151* + ID_MODEL_FROM_DATABASE=MFC-8210J + +usb:v04F9p0153* + ID_MODEL_FROM_DATABASE=DCP-1000J + +usb:v04F9p0157* + ID_MODEL_FROM_DATABASE=MFC-3420J Printer + +usb:v04F9p0158* + ID_MODEL_FROM_DATABASE=MFC-3820JN Port(FaxModem) + +usb:v04F9p015D* + ID_MODEL_FROM_DATABASE=MFC Composite Device + +usb:v04F9p015E* + ID_MODEL_FROM_DATABASE=DCP-8045D + +usb:v04F9p015F* + ID_MODEL_FROM_DATABASE=MFC-8440 + +usb:v04F9p0160* + ID_MODEL_FROM_DATABASE=MFC-8840D + +usb:v04F9p0161* + ID_MODEL_FROM_DATABASE=MFC-210C + +usb:v04F9p0162* + ID_MODEL_FROM_DATABASE=MFC-420CN Remote Setup Port + +usb:v04F9p0163* + ID_MODEL_FROM_DATABASE=MFC-410CN RemovableDisk + +usb:v04F9p0165* + ID_MODEL_FROM_DATABASE=MFC-620CN + +usb:v04F9p0166* + ID_MODEL_FROM_DATABASE=MFC-610CLN RemovableDisk + +usb:v04F9p0168* + ID_MODEL_FROM_DATABASE=MFC-620CLN + +usb:v04F9p0169* + ID_MODEL_FROM_DATABASE=DCP-110C RemovableDisk + +usb:v04F9p016B* + ID_MODEL_FROM_DATABASE=DCP-310CN RemovableDisk + +usb:v04F9p016C* + ID_MODEL_FROM_DATABASE=FAX-2440C Printer + +usb:v04F9p016D* + ID_MODEL_FROM_DATABASE=MFC-5440CN + +usb:v04F9p016E* + ID_MODEL_FROM_DATABASE=MFC-5840CN Remote Setup Port + +usb:v04F9p0170* + ID_MODEL_FROM_DATABASE=FAX-1840C Printer + +usb:v04F9p0171* + ID_MODEL_FROM_DATABASE=FAX-1835C Printer + +usb:v04F9p0172* + ID_MODEL_FROM_DATABASE=FAX-1940CN Printer + +usb:v04F9p0173* + ID_MODEL_FROM_DATABASE=MFC-3240C Remote Setup Port + +usb:v04F9p0174* + ID_MODEL_FROM_DATABASE=MFC-3340CN RemovableDisk + +usb:v04F9p017B* + ID_MODEL_FROM_DATABASE=Imagistics sx2100 + +usb:v04F9p0180* + ID_MODEL_FROM_DATABASE=MFC-7420 + +usb:v04F9p0181* + ID_MODEL_FROM_DATABASE=MFC-7820N Port(FaxModem) + +usb:v04F9p0182* + ID_MODEL_FROM_DATABASE=DCP-7010 + +usb:v04F9p0183* + ID_MODEL_FROM_DATABASE=DCP-7020 + +usb:v04F9p0184* + ID_MODEL_FROM_DATABASE=DCP-7025 Printer + +usb:v04F9p0185* + ID_MODEL_FROM_DATABASE=MFC-7220 Printer + +usb:v04F9p0186* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04F9p0187* + ID_MODEL_FROM_DATABASE=FAX-2820 Printer + +usb:v04F9p0188* + ID_MODEL_FROM_DATABASE=FAX-2920 Printer + +usb:v04F9p018A* + ID_MODEL_FROM_DATABASE=MFC-9420CN + +usb:v04F9p018C* + ID_MODEL_FROM_DATABASE=DCP-115C + +usb:v04F9p018D* + ID_MODEL_FROM_DATABASE=DCP-116C + +usb:v04F9p018E* + ID_MODEL_FROM_DATABASE=DCP-117C + +usb:v04F9p018F* + ID_MODEL_FROM_DATABASE=DCP-118C + +usb:v04F9p0190* + ID_MODEL_FROM_DATABASE=DCP-120C + +usb:v04F9p0191* + ID_MODEL_FROM_DATABASE=DCP-315CN + +usb:v04F9p0192* + ID_MODEL_FROM_DATABASE=DCP-340CW + +usb:v04F9p0193* + ID_MODEL_FROM_DATABASE=MFC-215C + +usb:v04F9p0194* + ID_MODEL_FROM_DATABASE=MFC-425CN + +usb:v04F9p0195* + ID_MODEL_FROM_DATABASE=MFC-820CW Remote Setup Port + +usb:v04F9p0196* + ID_MODEL_FROM_DATABASE=MFC-820CN Remote Setup Port + +usb:v04F9p0197* + ID_MODEL_FROM_DATABASE=MFC-640CW + +usb:v04F9p019A* + ID_MODEL_FROM_DATABASE=MFC-840CLN Remote Setup Port + +usb:v04F9p01A2* + ID_MODEL_FROM_DATABASE=MFC-8640D + +usb:v04F9p01A3* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v04F9p01A4* + ID_MODEL_FROM_DATABASE=DCP-8065DN Printer + +usb:v04F9p01A5* + ID_MODEL_FROM_DATABASE=MFC-8460N Port(FaxModem) + +usb:v04F9p01A6* + ID_MODEL_FROM_DATABASE=MFC-8860DN Port(FaxModem) + +usb:v04F9p01A7* + ID_MODEL_FROM_DATABASE=MFC-8870DW Printer + +usb:v04F9p01A8* + ID_MODEL_FROM_DATABASE=DCP-130C + +usb:v04F9p01A9* + ID_MODEL_FROM_DATABASE=DCP-330C + +usb:v04F9p01AA* + ID_MODEL_FROM_DATABASE=DCP-540CN + +usb:v04F9p01AB* + ID_MODEL_FROM_DATABASE=MFC-240C + +usb:v04F9p01AE* + ID_MODEL_FROM_DATABASE=DCP-750CW RemovableDisk + +usb:v04F9p01AF* + ID_MODEL_FROM_DATABASE=MFC-440CN + +usb:v04F9p01B0* + ID_MODEL_FROM_DATABASE=MFC-660CN + +usb:v04F9p01B1* + ID_MODEL_FROM_DATABASE=MFC-665CW + +usb:v04F9p01B2* + ID_MODEL_FROM_DATABASE=MFC-845CW + +usb:v04F9p01B4* + ID_MODEL_FROM_DATABASE=MFC-460CN + +usb:v04F9p01B5* + ID_MODEL_FROM_DATABASE=MFC-630CD + +usb:v04F9p01B6* + ID_MODEL_FROM_DATABASE=MFC-850CDN + +usb:v04F9p01B7* + ID_MODEL_FROM_DATABASE=MFC-5460CN + +usb:v04F9p01B8* + ID_MODEL_FROM_DATABASE=MFC-5860CN + +usb:v04F9p01BA* + ID_MODEL_FROM_DATABASE=MFC-3360C + +usb:v04F9p01BD* + ID_MODEL_FROM_DATABASE=MFC-8660DN + +usb:v04F9p01BE* + ID_MODEL_FROM_DATABASE=DCP-750CN RemovableDisk + +usb:v04F9p01BF* + ID_MODEL_FROM_DATABASE=MFC-860CDN + +usb:v04F9p01C0* + ID_MODEL_FROM_DATABASE=DCP-128C + +usb:v04F9p01C1* + ID_MODEL_FROM_DATABASE=DCP-129C + +usb:v04F9p01C2* + ID_MODEL_FROM_DATABASE=DCP-131C + +usb:v04F9p01C3* + ID_MODEL_FROM_DATABASE=DCP-329C + +usb:v04F9p01C4* + ID_MODEL_FROM_DATABASE=DCP-331C + +usb:v04F9p01C5* + ID_MODEL_FROM_DATABASE=MFC-239C + +usb:v04F9p01C9* + ID_MODEL_FROM_DATABASE=DCP-9040CN + +usb:v04F9p01CA* + ID_MODEL_FROM_DATABASE=MFC-9440CN + +usb:v04F9p01CB* + ID_MODEL_FROM_DATABASE=DCP-9045CDN + +usb:v04F9p01CC* + ID_MODEL_FROM_DATABASE=MFC-9840CDW + +usb:v04F9p01CE* + ID_MODEL_FROM_DATABASE=DCP-135C + +usb:v04F9p01CF* + ID_MODEL_FROM_DATABASE=DCP-150C + +usb:v04F9p01D0* + ID_MODEL_FROM_DATABASE=DCP-350C + +usb:v04F9p01D1* + ID_MODEL_FROM_DATABASE=DCP-560CN + +usb:v04F9p01D2* + ID_MODEL_FROM_DATABASE=DCP-770CW + +usb:v04F9p01D3* + ID_MODEL_FROM_DATABASE=DCP-770CN + +usb:v04F9p01D4* + ID_MODEL_FROM_DATABASE=MFC-230C + +usb:v04F9p01D5* + ID_MODEL_FROM_DATABASE=MFC-235C + +usb:v04F9p01D6* + ID_MODEL_FROM_DATABASE=MFC-260C + +usb:v04F9p01D7* + ID_MODEL_FROM_DATABASE=MFC-465CN + +usb:v04F9p01D8* + ID_MODEL_FROM_DATABASE=MFC-680CN + +usb:v04F9p01D9* + ID_MODEL_FROM_DATABASE=MFC-685CW + +usb:v04F9p01DA* + ID_MODEL_FROM_DATABASE=MFC-885CW + +usb:v04F9p01DB* + ID_MODEL_FROM_DATABASE=MFC-480CN + +usb:v04F9p01DC* + ID_MODEL_FROM_DATABASE=MFC-650CD + +usb:v04F9p01DD* + ID_MODEL_FROM_DATABASE=MFC-870CDN + +usb:v04F9p01DE* + ID_MODEL_FROM_DATABASE=MFC-880CDN + +usb:v04F9p01DF* + ID_MODEL_FROM_DATABASE=DCP-155C + +usb:v04F9p01E0* + ID_MODEL_FROM_DATABASE=MFC-265C + +usb:v04F9p01E1* + ID_MODEL_FROM_DATABASE=DCP-153C + +usb:v04F9p01E2* + ID_MODEL_FROM_DATABASE=DCP-157C + +usb:v04F9p01E3* + ID_MODEL_FROM_DATABASE=DCP-353C + +usb:v04F9p01E4* + ID_MODEL_FROM_DATABASE=DCP-357C + +usb:v04F9p01E7* + ID_MODEL_FROM_DATABASE=MFC-7340 + +usb:v04F9p01E9* + ID_MODEL_FROM_DATABASE=DCP-7040 + +usb:v04F9p01EA* + ID_MODEL_FROM_DATABASE=DCP-7030 + +usb:v04F9p01EB* + ID_MODEL_FROM_DATABASE=MFC-7320 + +usb:v04F9p01EC* + ID_MODEL_FROM_DATABASE=MFC-9640CW + +usb:v04F9p01F4* + ID_MODEL_FROM_DATABASE=MFC-5890CN + +usb:v04F9p020A* + ID_MODEL_FROM_DATABASE=MFC-8670DN + +usb:v04F9p020C* + ID_MODEL_FROM_DATABASE=DCP-9042CDN + +usb:v04F9p020D* + ID_MODEL_FROM_DATABASE=MFC-9450CDN + +usb:v04F9p0216* + ID_MODEL_FROM_DATABASE=MFC-8880DN + +usb:v04F9p0217* + ID_MODEL_FROM_DATABASE=MFC-8480DN + +usb:v04F9p0219* + ID_MODEL_FROM_DATABASE=MFC-8380DN + +usb:v04F9p021A* + ID_MODEL_FROM_DATABASE=MFC-8370DN + +usb:v04F9p021B* + ID_MODEL_FROM_DATABASE=DCP-8070D + +usb:v04F9p021C* + ID_MODEL_FROM_DATABASE=MFC-9320CW + +usb:v04F9p021D* + ID_MODEL_FROM_DATABASE=MFC-9120CN + +usb:v04F9p021E* + ID_MODEL_FROM_DATABASE=DCP-9010CN + +usb:v04F9p0220* + ID_MODEL_FROM_DATABASE=MFC-9010CN + +usb:v04F9p0222* + ID_MODEL_FROM_DATABASE=DCP-195C + +usb:v04F9p0223* + ID_MODEL_FROM_DATABASE=DCP-365CN + +usb:v04F9p0224* + ID_MODEL_FROM_DATABASE=DCP-375CW + +usb:v04F9p0225* + ID_MODEL_FROM_DATABASE=DCP-395CN + +usb:v04F9p0227* + ID_MODEL_FROM_DATABASE=DCP-595CN + +usb:v04F9p0228* + ID_MODEL_FROM_DATABASE=MFC-255CW + +usb:v04F9p0229* + ID_MODEL_FROM_DATABASE=MFC-295CN + +usb:v04F9p022A* + ID_MODEL_FROM_DATABASE=MFC-495CW + +usb:v04F9p022B* + ID_MODEL_FROM_DATABASE=MFC-495CN + +usb:v04F9p022C* + ID_MODEL_FROM_DATABASE=MFC-795CW + +usb:v04F9p022D* + ID_MODEL_FROM_DATABASE=MFC-675CD + +usb:v04F9p022E* + ID_MODEL_FROM_DATABASE=MFC-695CDN + +usb:v04F9p022F* + ID_MODEL_FROM_DATABASE=MFC-735CD + +usb:v04F9p0230* + ID_MODEL_FROM_DATABASE=MFC-935CDN + +usb:v04F9p0234* + ID_MODEL_FROM_DATABASE=DCP-373CW + +usb:v04F9p0235* + ID_MODEL_FROM_DATABASE=DCP-377CW + +usb:v04F9p0236* + ID_MODEL_FROM_DATABASE=DCP-390CN + +usb:v04F9p0239* + ID_MODEL_FROM_DATABASE=MFC-253CW + +usb:v04F9p023A* + ID_MODEL_FROM_DATABASE=MFC-257CW + +usb:v04F9p023E* + ID_MODEL_FROM_DATABASE=DCP-197C + +usb:v04F9p023F* + ID_MODEL_FROM_DATABASE=MFC-8680DN + +usb:v04F9p0240* + ID_MODEL_FROM_DATABASE=MFC-J950DN + +usb:v04F9p0248* + ID_MODEL_FROM_DATABASE=DCP-7055 scanner/printer + +usb:v04F9p0253* + ID_MODEL_FROM_DATABASE=DCP-J125 + +usb:v04F9p0254* + ID_MODEL_FROM_DATABASE=DCP-J315W + +usb:v04F9p0255* + ID_MODEL_FROM_DATABASE=DCP-J515W + +usb:v04F9p0256* + ID_MODEL_FROM_DATABASE=DCP-J515N + +usb:v04F9p0257* + ID_MODEL_FROM_DATABASE=DCP-J715W + +usb:v04F9p0258* + ID_MODEL_FROM_DATABASE=DCP-J715N + +usb:v04F9p0259* + ID_MODEL_FROM_DATABASE=MFC-J220 + +usb:v04F9p025A* + ID_MODEL_FROM_DATABASE=MFC-J410 + +usb:v04F9p025B* + ID_MODEL_FROM_DATABASE=MFC-J265W + +usb:v04F9p025C* + ID_MODEL_FROM_DATABASE=MFC-J415W + +usb:v04F9p025D* + ID_MODEL_FROM_DATABASE=MFC-J615W + +usb:v04F9p025E* + ID_MODEL_FROM_DATABASE=MFC-J615N + +usb:v04F9p025F* + ID_MODEL_FROM_DATABASE=MFC-J700D + +usb:v04F9p0260* + ID_MODEL_FROM_DATABASE=MFC-J800D + +usb:v04F9p0261* + ID_MODEL_FROM_DATABASE=MFC-J850DN + +usb:v04F9p026B* + ID_MODEL_FROM_DATABASE=MFC-J630W + +usb:v04F9p026D* + ID_MODEL_FROM_DATABASE=MFC-J805D + +usb:v04F9p026E* + ID_MODEL_FROM_DATABASE=MFC-J855DN + +usb:v04F9p026F* + ID_MODEL_FROM_DATABASE=MFC-J270W + +usb:v04F9p0273* + ID_MODEL_FROM_DATABASE=DCP-7057 scanner/printer + +usb:v04F9p0276* + ID_MODEL_FROM_DATABASE=MFC-5895CW + +usb:v04F9p0278* + ID_MODEL_FROM_DATABASE=MFC-J410W + +usb:v04F9p0279* + ID_MODEL_FROM_DATABASE=DCP-J525W + +usb:v04F9p027A* + ID_MODEL_FROM_DATABASE=DCP-J525N + +usb:v04F9p027B* + ID_MODEL_FROM_DATABASE=DCP-J725DW + +usb:v04F9p027C* + ID_MODEL_FROM_DATABASE=DCP-J725N + +usb:v04F9p027D* + ID_MODEL_FROM_DATABASE=DCP-J925DW + +usb:v04F9p027E* + ID_MODEL_FROM_DATABASE=MFC-J955DN + +usb:v04F9p027F* + ID_MODEL_FROM_DATABASE=MFC-J280W + +usb:v04F9p0280* + ID_MODEL_FROM_DATABASE=MFC-J435W + +usb:v04F9p0281* + ID_MODEL_FROM_DATABASE=MFC-J430W + +usb:v04F9p0282* + ID_MODEL_FROM_DATABASE=MFC-J625DW + +usb:v04F9p0283* + ID_MODEL_FROM_DATABASE=MFC-J825DW + +usb:v04F9p0284* + ID_MODEL_FROM_DATABASE=MFC-J825N + +usb:v04F9p0285* + ID_MODEL_FROM_DATABASE=MFC-J705D + +usb:v04F9p0287* + ID_MODEL_FROM_DATABASE=MFC-J860DN + +usb:v04F9p0288* + ID_MODEL_FROM_DATABASE=MFC-J5910DW + +usb:v04F9p0289* + ID_MODEL_FROM_DATABASE=MFC-J5910CDW + +usb:v04F9p028A* + ID_MODEL_FROM_DATABASE=DCP-J925N + +usb:v04F9p028D* + ID_MODEL_FROM_DATABASE=MFC-J835DW + +usb:v04F9p028F* + ID_MODEL_FROM_DATABASE=MFC-J425W + +usb:v04F9p0290* + ID_MODEL_FROM_DATABASE=MFC-J432W + +usb:v04F9p0291* + ID_MODEL_FROM_DATABASE=DCP-8110DN + +usb:v04F9p0292* + ID_MODEL_FROM_DATABASE=DCP-8150DN + +usb:v04F9p0293* + ID_MODEL_FROM_DATABASE=DCP-8155DN + +usb:v04F9p0294* + ID_MODEL_FROM_DATABASE=DCP-8250DN + +usb:v04F9p0295* + ID_MODEL_FROM_DATABASE=MFC-8510DN + +usb:v04F9p0296* + ID_MODEL_FROM_DATABASE=MFC-8520DN + +usb:v04F9p0298* + ID_MODEL_FROM_DATABASE=MFC-8910DW + +usb:v04F9p0299* + ID_MODEL_FROM_DATABASE=MFC-8950DW + +usb:v04F9p029A* + ID_MODEL_FROM_DATABASE=MFC-8690DW + +usb:v04F9p029C* + ID_MODEL_FROM_DATABASE=MFC-8515DN + +usb:v04F9p029E* + ID_MODEL_FROM_DATABASE=MFC-9125CN + +usb:v04F9p029F* + ID_MODEL_FROM_DATABASE=MFC-9325CW + +usb:v04F9p02A0* + ID_MODEL_FROM_DATABASE=DCP-J140W + +usb:v04F9p02A5* + ID_MODEL_FROM_DATABASE=MFC-7240 + +usb:v04F9p02A6* + ID_MODEL_FROM_DATABASE=FAX-2940 + +usb:v04F9p02A7* + ID_MODEL_FROM_DATABASE=FAX-2950 + +usb:v04F9p02A8* + ID_MODEL_FROM_DATABASE=MFC-7290 + +usb:v04F9p02AB* + ID_MODEL_FROM_DATABASE=FAX-2990 + +usb:v04F9p02AC* + ID_MODEL_FROM_DATABASE=DCP-8110D + +usb:v04F9p02AD* + ID_MODEL_FROM_DATABASE=MFC-9130CW + +usb:v04F9p02AE* + ID_MODEL_FROM_DATABASE=MFC-9140CDN + +usb:v04F9p02AF* + ID_MODEL_FROM_DATABASE=MFC-9330CDW + +usb:v04F9p02B0* + ID_MODEL_FROM_DATABASE=MFC-9340CDW + +usb:v04F9p02B1* + ID_MODEL_FROM_DATABASE=DCP-9020CDN + +usb:v04F9p02B2* + ID_MODEL_FROM_DATABASE=MFC-J810DN + +usb:v04F9p02B3* + ID_MODEL_FROM_DATABASE=MFC-J4510DW + +usb:v04F9p02B4* + ID_MODEL_FROM_DATABASE=MFC-J4710DW + +usb:v04F9p02B5* + ID_MODEL_FROM_DATABASE=DCP-8112DN + +usb:v04F9p02B6* + ID_MODEL_FROM_DATABASE=DCP-8152DN + +usb:v04F9p02B7* + ID_MODEL_FROM_DATABASE=DCP-8157DN + +usb:v04F9p02B8* + ID_MODEL_FROM_DATABASE=MFC-8512DN + +usb:v04F9p02BA* + ID_MODEL_FROM_DATABASE=MFC-8912DW + +usb:v04F9p02BB* + ID_MODEL_FROM_DATABASE=MFC-8952DW + +usb:v04F9p02BC* + ID_MODEL_FROM_DATABASE=DCP-J540N + +usb:v04F9p02BD* + ID_MODEL_FROM_DATABASE=DCP-J740N + +usb:v04F9p02BE* + ID_MODEL_FROM_DATABASE=MFC-J710D + +usb:v04F9p02BF* + ID_MODEL_FROM_DATABASE=MFC-J840N + +usb:v04F9p02C0* + ID_MODEL_FROM_DATABASE=DCP-J940N + +usb:v04F9p02C1* + ID_MODEL_FROM_DATABASE=MFC-J960DN + +usb:v04F9p02C2* + ID_MODEL_FROM_DATABASE=DCP-J4110DW + +usb:v04F9p02C3* + ID_MODEL_FROM_DATABASE=MFC-J4310DW + +usb:v04F9p02C4* + ID_MODEL_FROM_DATABASE=MFC-J4410DW + +usb:v04F9p02C5* + ID_MODEL_FROM_DATABASE=MFC-J4610DW + +usb:v04F9p02C6* + ID_MODEL_FROM_DATABASE=DCP-J4210N + +usb:v04F9p02C7* + ID_MODEL_FROM_DATABASE=MFC-J4510N + +usb:v04F9p02C8* + ID_MODEL_FROM_DATABASE=MFC-J4910CDW + +usb:v04F9p02C9* + ID_MODEL_FROM_DATABASE=MFC-J4810DN + +usb:v04F9p02CA* + ID_MODEL_FROM_DATABASE=MFC-8712DW + +usb:v04F9p02CB* + ID_MODEL_FROM_DATABASE=MFC-8710DW + +usb:v04F9p02CC* + ID_MODEL_FROM_DATABASE=MFC-J2310 + +usb:v04F9p02CD* + ID_MODEL_FROM_DATABASE=MFC-J2510 + +usb:v04F9p02CE* + ID_MODEL_FROM_DATABASE=DCP-7055W + +usb:v04F9p02CF* + ID_MODEL_FROM_DATABASE=DCP-7057W + +usb:v04F9p02D0* + ID_MODEL_FROM_DATABASE=DCP-1510 + +usb:v04F9p02D1* + ID_MODEL_FROM_DATABASE=MFC-1810 + +usb:v04F9p02D3* + ID_MODEL_FROM_DATABASE=DCP-9020CDW + +usb:v04F9p02D4* + ID_MODEL_FROM_DATABASE=MFC-8810DW + +usb:v04F9p02DD* + ID_MODEL_FROM_DATABASE=DCP-J4215N + +usb:v04F9p02DE* + ID_MODEL_FROM_DATABASE=DCP-J132W + +usb:v04F9p02DF* + ID_MODEL_FROM_DATABASE=DCP-J152W + +usb:v04F9p02E0* + ID_MODEL_FROM_DATABASE=DCP-J152N + +usb:v04F9p02E1* + ID_MODEL_FROM_DATABASE=DCP-J172W + +usb:v04F9p02E2* + ID_MODEL_FROM_DATABASE=DCP-J552DW + +usb:v04F9p02E3* + ID_MODEL_FROM_DATABASE=DCP-J552N + +usb:v04F9p02E4* + ID_MODEL_FROM_DATABASE=DCP-J752DW + +usb:v04F9p02E5* + ID_MODEL_FROM_DATABASE=DCP-J752N + +usb:v04F9p02E6* + ID_MODEL_FROM_DATABASE=DCP-J952N + +usb:v04F9p02E7* + ID_MODEL_FROM_DATABASE=MFC-J245 + +usb:v04F9p02E8* + ID_MODEL_FROM_DATABASE=MFC-J470DW + +usb:v04F9p02E9* + ID_MODEL_FROM_DATABASE=MFC-J475DW + +usb:v04F9p02EA* + ID_MODEL_FROM_DATABASE=MFC-J285DW + +usb:v04F9p02EB* + ID_MODEL_FROM_DATABASE=MFC-J650DW + +usb:v04F9p02EC* + ID_MODEL_FROM_DATABASE=MFC-J870DW + +usb:v04F9p02ED* + ID_MODEL_FROM_DATABASE=MFC-J870N + +usb:v04F9p02EE* + ID_MODEL_FROM_DATABASE=MFC-J720D + +usb:v04F9p02EF* + ID_MODEL_FROM_DATABASE=MFC-J820DN + +usb:v04F9p02F0* + ID_MODEL_FROM_DATABASE=MFC-J980DN + +usb:v04F9p02F1* + ID_MODEL_FROM_DATABASE=MFC-J890DN + +usb:v04F9p02F2* + ID_MODEL_FROM_DATABASE=MFC-J6520DW + +usb:v04F9p02F3* + ID_MODEL_FROM_DATABASE=MFC-J6570CDW + +usb:v04F9p02F4* + ID_MODEL_FROM_DATABASE=MFC-J6720DW + +usb:v04F9p02F5* + ID_MODEL_FROM_DATABASE=MFC-J6920DW + +usb:v04F9p02F6* + ID_MODEL_FROM_DATABASE=MFC-J6970CDW + +usb:v04F9p02F7* + ID_MODEL_FROM_DATABASE=MFC-J6975CDW + +usb:v04F9p02F8* + ID_MODEL_FROM_DATABASE=MFC-J6770CDW + +usb:v04F9p02F9* + ID_MODEL_FROM_DATABASE=DCP-J132N + +usb:v04F9p02FA* + ID_MODEL_FROM_DATABASE=MFC-J450DW + +usb:v04F9p02FB* + ID_MODEL_FROM_DATABASE=MFC-J875DW + +usb:v04F9p02FC* + ID_MODEL_FROM_DATABASE=DCP-J100 + +usb:v04F9p02FD* + ID_MODEL_FROM_DATABASE=DCP-J105 + +usb:v04F9p02FE* + ID_MODEL_FROM_DATABASE=MFC-J200 + +usb:v04F9p02FF* + ID_MODEL_FROM_DATABASE=MFC-J3520 + +usb:v04F9p0300* + ID_MODEL_FROM_DATABASE=MFC-J3720 + +usb:v04F9p030F* + ID_MODEL_FROM_DATABASE=DCP-L8400CDN + +usb:v04F9p0310* + ID_MODEL_FROM_DATABASE=DCP-L8450CDW + +usb:v04F9p0311* + ID_MODEL_FROM_DATABASE=MFC-L8600CDW + +usb:v04F9p0312* + ID_MODEL_FROM_DATABASE=MFC-L8650CDW + +usb:v04F9p0313* + ID_MODEL_FROM_DATABASE=MFC-L8850CDW + +usb:v04F9p0314* + ID_MODEL_FROM_DATABASE=MFC-L9550CDW + +usb:v04F9p0318* + ID_MODEL_FROM_DATABASE=MFC-7365DN + +usb:v04F9p0320* + ID_MODEL_FROM_DATABASE=MFC-L2740DW + +usb:v04F9p0321* + ID_MODEL_FROM_DATABASE=DCP-L2500D + +usb:v04F9p0322* + ID_MODEL_FROM_DATABASE=DCP-L2520DW + +usb:v04F9p0324* + ID_MODEL_FROM_DATABASE=DCP-L2520D + +usb:v04F9p0326* + ID_MODEL_FROM_DATABASE=DCP-L2540DN + +usb:v04F9p0328* + ID_MODEL_FROM_DATABASE=DCP-L2540DW + +usb:v04F9p0329* + ID_MODEL_FROM_DATABASE=DCP-L2560DW + +usb:v04F9p0330* + ID_MODEL_FROM_DATABASE=HL-L2380DW + +usb:v04F9p0331* + ID_MODEL_FROM_DATABASE=MFC-L2700DW + +usb:v04F9p0335* + ID_MODEL_FROM_DATABASE=FAX-L2700DN + +usb:v04F9p0337* + ID_MODEL_FROM_DATABASE=MFC-L2720DW + +usb:v04F9p0338* + ID_MODEL_FROM_DATABASE=MFC-L2720DN + +usb:v04F9p0339* + ID_MODEL_FROM_DATABASE=DCP-J4120DW + +usb:v04F9p033A* + ID_MODEL_FROM_DATABASE=MFC-J4320DW + +usb:v04F9p033C* + ID_MODEL_FROM_DATABASE=MFC-J2320 + +usb:v04F9p033D* + ID_MODEL_FROM_DATABASE=MFC-J4420DW + +usb:v04F9p0340* + ID_MODEL_FROM_DATABASE=MFC-J4620DW + +usb:v04F9p0341* + ID_MODEL_FROM_DATABASE=MFC-J2720 + +usb:v04F9p0342* + ID_MODEL_FROM_DATABASE=MFC-J4625DW + +usb:v04F9p0343* + ID_MODEL_FROM_DATABASE=MFC-J5320DW + +usb:v04F9p0346* + ID_MODEL_FROM_DATABASE=MFC-J5620DW + +usb:v04F9p0347* + ID_MODEL_FROM_DATABASE=MFC-J5720DW + +usb:v04F9p0349* + ID_MODEL_FROM_DATABASE=DCP-J4220N + +usb:v04F9p034B* + ID_MODEL_FROM_DATABASE=MFC-J4720N + +usb:v04F9p034E* + ID_MODEL_FROM_DATABASE=MFC-J5720CDW + +usb:v04F9p034F* + ID_MODEL_FROM_DATABASE=MFC-J5820DN + +usb:v04F9p0350* + ID_MODEL_FROM_DATABASE=MFC-J5620CDW + +usb:v04F9p0351* + ID_MODEL_FROM_DATABASE=DCP-J137N + +usb:v04F9p0353* + ID_MODEL_FROM_DATABASE=DCP-J557N + +usb:v04F9p0354* + ID_MODEL_FROM_DATABASE=DCP-J757N + +usb:v04F9p0355* + ID_MODEL_FROM_DATABASE=DCP-J957N + +usb:v04F9p0356* + ID_MODEL_FROM_DATABASE=MFC-J877N + +usb:v04F9p0357* + ID_MODEL_FROM_DATABASE=MFC-J727D + +usb:v04F9p0358* + ID_MODEL_FROM_DATABASE=MFC-J987DN + +usb:v04F9p0359* + ID_MODEL_FROM_DATABASE=MFC-J827DN + +usb:v04F9p035A* + ID_MODEL_FROM_DATABASE=MFC-J897DN + +usb:v04F9p035B* + ID_MODEL_FROM_DATABASE=DCP-1610W + +usb:v04F9p035C* + ID_MODEL_FROM_DATABASE=DCP-1610NW + +usb:v04F9p035D* + ID_MODEL_FROM_DATABASE=MFC-1910W + +usb:v04F9p035E* + ID_MODEL_FROM_DATABASE=MFC-1910NW + +usb:v04F9p0360* + ID_MODEL_FROM_DATABASE=DCP-1618W + +usb:v04F9p0361* + ID_MODEL_FROM_DATABASE=MFC-1919NW + +usb:v04F9p0364* + ID_MODEL_FROM_DATABASE=MFC-J5625DW + +usb:v04F9p0365* + ID_MODEL_FROM_DATABASE=MFC-J4520DW + +usb:v04F9p0366* + ID_MODEL_FROM_DATABASE=MFC-J5520DW + +usb:v04F9p0367* + ID_MODEL_FROM_DATABASE=DCP-7080D + +usb:v04F9p0368* + ID_MODEL_FROM_DATABASE=DCP-7080 + +usb:v04F9p0369* + ID_MODEL_FROM_DATABASE=DCP-7180DN + +usb:v04F9p036A* + ID_MODEL_FROM_DATABASE=DCP-7189DW + +usb:v04F9p036B* + ID_MODEL_FROM_DATABASE=MFC-7380 + +usb:v04F9p036C* + ID_MODEL_FROM_DATABASE=MFC-7480D + +usb:v04F9p036D* + ID_MODEL_FROM_DATABASE=MFC-7880DN + +usb:v04F9p036E* + ID_MODEL_FROM_DATABASE=MFC-7889DW + +usb:v04F9p036F* + ID_MODEL_FROM_DATABASE=DCP-9022CDW + +usb:v04F9p0370* + ID_MODEL_FROM_DATABASE=MFC-9142CDN + +usb:v04F9p0371* + ID_MODEL_FROM_DATABASE=MFC-9332CDW + +usb:v04F9p0372* + ID_MODEL_FROM_DATABASE=MFC-9342CDW + +usb:v04F9p0373* + ID_MODEL_FROM_DATABASE=MFC-L2700D + +usb:v04F9p0376* + ID_MODEL_FROM_DATABASE=DCP-1600 + +usb:v04F9p0377* + ID_MODEL_FROM_DATABASE=MFC-1900 + +usb:v04F9p0378* + ID_MODEL_FROM_DATABASE=DCP-1608 + +usb:v04F9p0379* + ID_MODEL_FROM_DATABASE=DCP-1619 + +usb:v04F9p037A* + ID_MODEL_FROM_DATABASE=MFC-1906 + +usb:v04F9p037B* + ID_MODEL_FROM_DATABASE=MFC-1908 + +usb:v04F9p037C* + ID_MODEL_FROM_DATABASE=ADS-2000e + +usb:v04F9p037D* + ID_MODEL_FROM_DATABASE=ADS-2100e + +usb:v04F9p037E* + ID_MODEL_FROM_DATABASE=ADS-2500We + +usb:v04F9p037F* + ID_MODEL_FROM_DATABASE=ADS-2600We + +usb:v04F9p0380* + ID_MODEL_FROM_DATABASE=DCP-J562DW + +usb:v04F9p0381* + ID_MODEL_FROM_DATABASE=DCP-J562N + +usb:v04F9p0383* + ID_MODEL_FROM_DATABASE=DCP-J962N + +usb:v04F9p0384* + ID_MODEL_FROM_DATABASE=MFC-J480DW + +usb:v04F9p0385* + ID_MODEL_FROM_DATABASE=MFC-J485DW + +usb:v04F9p0386* + ID_MODEL_FROM_DATABASE=MFC-J460DW + +usb:v04F9p0388* + ID_MODEL_FROM_DATABASE=MFC-J680DW + +usb:v04F9p0389* + ID_MODEL_FROM_DATABASE=MFC-J880DW + +usb:v04F9p038A* + ID_MODEL_FROM_DATABASE=MFC-J885DW + +usb:v04F9p038B* + ID_MODEL_FROM_DATABASE=MFC-J880N + +usb:v04F9p038C* + ID_MODEL_FROM_DATABASE=MFC-J730DN + +usb:v04F9p038D* + ID_MODEL_FROM_DATABASE=MFC-J990DN + +usb:v04F9p038E* + ID_MODEL_FROM_DATABASE=MFC-J830DN + +usb:v04F9p038F* + ID_MODEL_FROM_DATABASE=MFC-J900DN + +usb:v04F9p0390* + ID_MODEL_FROM_DATABASE=MFC-J5920DW + +usb:v04F9p0392* + ID_MODEL_FROM_DATABASE=MFC-L2705DW + +usb:v04F9p0393* + ID_MODEL_FROM_DATABASE=DCP-T300 + +usb:v04F9p0394* + ID_MODEL_FROM_DATABASE=DCP-T500W + +usb:v04F9p0395* + ID_MODEL_FROM_DATABASE=DCP-T700W + +usb:v04F9p0396* + ID_MODEL_FROM_DATABASE=MFC-T800W + +usb:v04F9p0397* + ID_MODEL_FROM_DATABASE=DCP-J963N + +usb:v04F9p03B3* + ID_MODEL_FROM_DATABASE=MFC-J6925DW + +usb:v04F9p03B4* + ID_MODEL_FROM_DATABASE=MFC-J6573CDW + +usb:v04F9p03B5* + ID_MODEL_FROM_DATABASE=MFC-J6973CDW + +usb:v04F9p03B6* + ID_MODEL_FROM_DATABASE=MFC-J6990CDW + +usb:v04F9p03BB* + ID_MODEL_FROM_DATABASE=MFC-L2680W + +usb:v04F9p03BC* + ID_MODEL_FROM_DATABASE=MFC-L2700DN + +usb:v04F9p03BD* + ID_MODEL_FROM_DATABASE=DCP-J762N + +usb:v04F9p1000* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p1002* + ID_MODEL_FROM_DATABASE=Printer + +usb:v04F9p2002* + ID_MODEL_FROM_DATABASE=PTUSB Printing + +usb:v04F9p2004* + ID_MODEL_FROM_DATABASE=PT-2300/2310 p-Touch Laber Printer + +usb:v04F9p2015* + ID_MODEL_FROM_DATABASE=QL-500 P-touch label printer + +usb:v04F9p2016* + ID_MODEL_FROM_DATABASE=QL-550 P-touch label printer + +usb:v04F9p201A* + ID_MODEL_FROM_DATABASE=PT-18R P-touch label printer + +usb:v04F9p201B* + ID_MODEL_FROM_DATABASE=QL-650TD P-touch Label Printer + +usb:v04F9p2027* + ID_MODEL_FROM_DATABASE=QL-560 P-touch Label Printer + +usb:v04F9p2028* + ID_MODEL_FROM_DATABASE=QL-570 P-touch Label Printer + +usb:v04F9p202B* + ID_MODEL_FROM_DATABASE=PT-7600 P-touch Label Printer + +usb:v04F9p2100* + ID_MODEL_FROM_DATABASE=Card Reader Writer + +usb:v04F9p60A0* + ID_MODEL_FROM_DATABASE=ADS-2000 + +usb:v04F9p60A1* + ID_MODEL_FROM_DATABASE=ADS-2100 + +usb:v04F9p60A4* + ID_MODEL_FROM_DATABASE=ADS-2500W + +usb:v04F9p60A5* + ID_MODEL_FROM_DATABASE=ADS-2600W + +usb:v04F9p60A6* + ID_MODEL_FROM_DATABASE=ADS-1000W + +usb:v04F9p60A7* + ID_MODEL_FROM_DATABASE=ADS-1100W + +usb:v04F9p60A8* + ID_MODEL_FROM_DATABASE=ADS-1500W + +usb:v04F9p60A9* + ID_MODEL_FROM_DATABASE=ADS-1600W + +usb:v04FA* + ID_VENDOR_FROM_DATABASE=Dallas Semiconductor + +usb:v04FAp2490* + ID_MODEL_FROM_DATABASE=DS1490F 2-in-1 Fob, 1-Wire adapter + +usb:v04FAp4201* + ID_MODEL_FROM_DATABASE=DS4201 Audio DAC + +usb:v04FB* + ID_VENDOR_FROM_DATABASE=Biostar Microtech International Corp. + +usb:v04FC* + ID_VENDOR_FROM_DATABASE=Sunplus Technology Co., Ltd + +usb:v04FCp0003* + ID_MODEL_FROM_DATABASE=CM1092 / Wintech CM-5098 Optical Mouse + +usb:v04FCp0005* + ID_MODEL_FROM_DATABASE=USB OpticalWheel Mouse + +usb:v04FCp0013* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v04FCp0015* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v04FCp00D3* + ID_MODEL_FROM_DATABASE=00052486 / Laser Mouse M1052 [hama] + +usb:v04FCp0171* + ID_MODEL_FROM_DATABASE=SPCA1527A/SPCA1528 SD card camera (Mass Storage mode) + +usb:v04FCp0201* + ID_MODEL_FROM_DATABASE=SPCP825 RS232C Adapter + +usb:v04FCp0232* + ID_MODEL_FROM_DATABASE=Fingerprint + +usb:v04FCp0538* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse 2.4G [Bright] + +usb:v04FCp0561* + ID_MODEL_FROM_DATABASE=Flexcam 100 + +usb:v04FCp05D8* + ID_MODEL_FROM_DATABASE=Wireless keyboard/mouse + +usb:v04FCp05DA* + ID_MODEL_FROM_DATABASE=SPEEDLINK SNAPPY Wireless Mouse Nano + +usb:v04FCp0C15* + ID_MODEL_FROM_DATABASE=SPIF215A SATA bridge + +usb:v04FCp0C25* + ID_MODEL_FROM_DATABASE=SATALink SPIF225A + +usb:v04FCp1528* + ID_MODEL_FROM_DATABASE=SPCA1527A/SPCA1528 SD card camera (webcam mode) + +usb:v04FCp1533* + ID_MODEL_FROM_DATABASE=Mass Storage + +usb:v04FCp2080* + ID_MODEL_FROM_DATABASE=ASUS Webcam + +usb:v04FCp500C* + ID_MODEL_FROM_DATABASE=CA500C Digital Camera + +usb:v04FCp504A* + ID_MODEL_FROM_DATABASE=Aiptek Mini PenCam 1.3 + +usb:v04FCp504B* + ID_MODEL_FROM_DATABASE=Aiptek Mega PockerCam 1.3/Maxell MaxPocket LE 1.3 + +usb:v04FCp5330* + ID_MODEL_FROM_DATABASE=Digitrex 2110 + +usb:v04FCp5331* + ID_MODEL_FROM_DATABASE=Vivitar Vivicam 10 + +usb:v04FCp5360* + ID_MODEL_FROM_DATABASE=Sunplus Generic Digital Camera + +usb:v04FCp5563* + ID_MODEL_FROM_DATABASE=Digital Media Player MP3/WMA [The Sharper Image] + +usb:v04FCp5720* + ID_MODEL_FROM_DATABASE=Card Reader Driver + +usb:v04FCp6333* + ID_MODEL_FROM_DATABASE=Siri A9 UVC chipset + +usb:v04FCp7333* + ID_MODEL_FROM_DATABASE=Finet Technology Palmpix DC-85 + +usb:v04FCp757A* + ID_MODEL_FROM_DATABASE=Aiptek, MP315 MP3 Player + +usb:v04FCpFFFF* + ID_MODEL_FROM_DATABASE=PureDigital Ritz Disposable + +usb:v04FD* + ID_VENDOR_FROM_DATABASE=Soliton Systems, K.K. + +usb:v04FDp0003* + ID_MODEL_FROM_DATABASE=Smart Card Reader II + +usb:v04FE* + ID_VENDOR_FROM_DATABASE=PFU, Ltd + +usb:v04FF* + ID_VENDOR_FROM_DATABASE=E-CMOS Corp. + +usb:v0500* + ID_VENDOR_FROM_DATABASE=Siam United Hi-Tech + +usb:v0500p0001* + ID_MODEL_FROM_DATABASE=DART Keyboard Mouse + +usb:v0500p0002* + ID_MODEL_FROM_DATABASE=DART-2 Keyboard + +usb:v0501* + ID_VENDOR_FROM_DATABASE=Fujikura DDK, Ltd + +usb:v0502* + ID_VENDOR_FROM_DATABASE=Acer, Inc. + +usb:v0502p0001* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0502p0736* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0502p15B1* + ID_MODEL_FROM_DATABASE=PDA n311 + +usb:v0502p1631* + ID_MODEL_FROM_DATABASE=c10 Series + +usb:v0502p1632* + ID_MODEL_FROM_DATABASE=c20 Series + +usb:v0502p16E1* + ID_MODEL_FROM_DATABASE=n10 Handheld Sync + +usb:v0502p16E2* + ID_MODEL_FROM_DATABASE=n20 Pocket PC Sync + +usb:v0502p16E3* + ID_MODEL_FROM_DATABASE=n30 Handheld Sync + +usb:v0502p2008* + ID_MODEL_FROM_DATABASE=Liquid Gallant Duo E350 (preloader) + +usb:v0502p3202* + ID_MODEL_FROM_DATABASE=Liquid + +usb:v0502p3203* + ID_MODEL_FROM_DATABASE=Liquid (Debug mode) + +usb:v0502p3230* + ID_MODEL_FROM_DATABASE=BeTouch E120 + +usb:v0502p3317* + ID_MODEL_FROM_DATABASE=Liquid + +usb:v0502p3325* + ID_MODEL_FROM_DATABASE=Iconia tablet A500 + +usb:v0502p3341* + ID_MODEL_FROM_DATABASE=Iconia tablet A500 + +usb:v0502p33C3* + ID_MODEL_FROM_DATABASE=Liquid Gallant Duo E350 + +usb:v0502p33C4* + ID_MODEL_FROM_DATABASE=Liquid Gallant Duo E350 (debug mode) + +usb:v0502p33C7* + ID_MODEL_FROM_DATABASE=Liquid Gallant Duo E350 (USB tethering) + +usb:v0502p33C8* + ID_MODEL_FROM_DATABASE=Liquid Gallant Duo E350 (debug mode, USB tethering) + +usb:v0502pD001* + ID_MODEL_FROM_DATABASE=Divio NW801/DVC-V6+ Digital Camera + +usb:v0503* + ID_VENDOR_FROM_DATABASE=Hitachi America, Ltd + +usb:v0504* + ID_VENDOR_FROM_DATABASE=Hayes Microcomputer Products + +usb:v0506* + ID_VENDOR_FROM_DATABASE=3Com Corp. + +usb:v0506p009D* + ID_MODEL_FROM_DATABASE=HomeConnect Camera + +usb:v0506p00A0* + ID_MODEL_FROM_DATABASE=3CREB96 Bluetooth Adapter + +usb:v0506p00A1* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0506p00A2* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0506p00DF* + ID_MODEL_FROM_DATABASE=3Com Home Connect lite + +usb:v0506p0100* + ID_MODEL_FROM_DATABASE=HomeConnect ADSL Modem Driver + +usb:v0506p03E8* + ID_MODEL_FROM_DATABASE=3C19250 Ethernet [klsi] + +usb:v0506p0A01* + ID_MODEL_FROM_DATABASE=3CRSHEW696 Wireless Adapter + +usb:v0506p0A11* + ID_MODEL_FROM_DATABASE=3CRWE254G72 802.11g Adapter + +usb:v0506p11F8* + ID_MODEL_FROM_DATABASE=HomeConnect 3C460 + +usb:v0506p2922* + ID_MODEL_FROM_DATABASE=HomeConnect Cable Modem External with + +usb:v0506p3021* + ID_MODEL_FROM_DATABASE=U.S.Robotics 56000 Voice FaxModem Pro + +usb:v0506p4601* + ID_MODEL_FROM_DATABASE=3C460B 10/100 Ethernet Adapter + +usb:v0506pF002* + ID_MODEL_FROM_DATABASE=3CP4218 ADSL Modem (pre-init) + +usb:v0506pF003* + ID_MODEL_FROM_DATABASE=3CP4218 ADSL Modem + +usb:v0506pF100* + ID_MODEL_FROM_DATABASE=3CP4218 ADSL Modem (pre-init) + +usb:v0507* + ID_VENDOR_FROM_DATABASE=Hosiden Corp. + +usb:v0507p0011* + ID_MODEL_FROM_DATABASE=Konami ParaParaParadise Controller + +usb:v0508* + ID_VENDOR_FROM_DATABASE=Clarion Co., Ltd + +usb:v0509* + ID_VENDOR_FROM_DATABASE=Aztech Systems, Ltd + +usb:v0509p0801* + ID_MODEL_FROM_DATABASE=ADSL Modem + +usb:v0509p0802* + ID_MODEL_FROM_DATABASE=ADSL Modem (RFC1483) + +usb:v0509p0806* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0509p080F* + ID_MODEL_FROM_DATABASE=Binatone ADSL500 Modem Network Interface + +usb:v0509p0812* + ID_MODEL_FROM_DATABASE=Pirelli ADSL Modem Network Interface + +usb:v050A* + ID_VENDOR_FROM_DATABASE=Cinch Connectors + +usb:v050B* + ID_VENDOR_FROM_DATABASE=Cable System International + +usb:v050C* + ID_VENDOR_FROM_DATABASE=InnoMedia, Inc. + +usb:v050D* + ID_VENDOR_FROM_DATABASE=Belkin Components + +usb:v050Dp0004* + ID_MODEL_FROM_DATABASE=Direct Connect + +usb:v050Dp0012* + ID_MODEL_FROM_DATABASE=F8T012 Bluetooth Adapter + +usb:v050Dp0013* + ID_MODEL_FROM_DATABASE=F8T013 Bluetooth Adapter + +usb:v050Dp0017* + ID_MODEL_FROM_DATABASE=B8T017 Bluetooth+EDR 2.1 / F4U017 USB 2.0 7-port Hub + +usb:v050Dp003A* + ID_MODEL_FROM_DATABASE=Universal Media Reader + +usb:v050Dp0050* + ID_MODEL_FROM_DATABASE=F5D6050 802.11b Wireless Adapter v2000 [Atmel at76c503a] + +usb:v050Dp0081* + ID_MODEL_FROM_DATABASE=F8T001v2 Bluetooth + +usb:v050Dp0083* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v050Dp0084* + ID_MODEL_FROM_DATABASE=F8T003v2 Bluetooth + +usb:v050Dp0102* + ID_MODEL_FROM_DATABASE=Flip KVM + +usb:v050Dp0103* + ID_MODEL_FROM_DATABASE=F5U103 Serial Adapter [etek] + +usb:v050Dp0106* + ID_MODEL_FROM_DATABASE=VideoBus II Adapter, Video + +usb:v050Dp0108* + ID_MODEL_FROM_DATABASE=F1DE108B KVM + +usb:v050Dp0109* + ID_MODEL_FROM_DATABASE=F5U109/F5U409 PDA Adapter + +usb:v050Dp0115* + ID_MODEL_FROM_DATABASE=SCSI Adapter + +usb:v050Dp0119* + ID_MODEL_FROM_DATABASE=F5U120-PC Dual PS/2 Ports / F5U118-UNV ADB Adapter + +usb:v050Dp0121* + ID_MODEL_FROM_DATABASE=F5D5050 100Mbps Ethernet + +usb:v050Dp0122* + ID_MODEL_FROM_DATABASE=Ethernet Adapter + +usb:v050Dp0131* + ID_MODEL_FROM_DATABASE=Bluetooth Device with trace filter + +usb:v050Dp016A* + ID_MODEL_FROM_DATABASE=Bluetooth Mini Dongle + +usb:v050Dp0200* + ID_MODEL_FROM_DATABASE=Nostromo SpeedPad n52te Gaming Keyboard + +usb:v050Dp0201* + ID_MODEL_FROM_DATABASE=Peripheral Switch + +usb:v050Dp0208* + ID_MODEL_FROM_DATABASE=USBView II Video Adapter [nt1004] + +usb:v050Dp0210* + ID_MODEL_FROM_DATABASE=F5U228 Hi-Speed USB 2.0 DVD Creator + +usb:v050Dp0211* + ID_MODEL_FROM_DATABASE=F5U211 USB 2.0 15-in-1 Media Reader & Writer + +usb:v050Dp0224* + ID_MODEL_FROM_DATABASE=F5U224 USB 2.0 4-Port Hub + +usb:v050Dp0234* + ID_MODEL_FROM_DATABASE=F5U234 USB 2.0 4-Port Hub + +usb:v050Dp0237* + ID_MODEL_FROM_DATABASE=F5U237 USB 2.0 7-Port Hub + +usb:v050Dp0240* + ID_MODEL_FROM_DATABASE=F5U240 USB 2.0 CF Card Reader + +usb:v050Dp0249* + ID_MODEL_FROM_DATABASE=USB 2 Flash Media Device + +usb:v050Dp0257* + ID_MODEL_FROM_DATABASE=F5U257 Serial + +usb:v050Dp0304* + ID_MODEL_FROM_DATABASE=FSU304 USB 2.0 - 4 Ports Hub + +usb:v050Dp0307* + ID_MODEL_FROM_DATABASE=USB 2.0 - 7 ports Hub [FSU307] + +usb:v050Dp0409* + ID_MODEL_FROM_DATABASE=F5U409 Serial + +usb:v050Dp0416* + ID_MODEL_FROM_DATABASE=Staples 12416 7 port desktop hub + +usb:v050Dp0551* + ID_MODEL_FROM_DATABASE=F6C550-AVR UPS + +usb:v050Dp065A* + ID_MODEL_FROM_DATABASE=F8T065BF Mini Bluetooth 4.0 Adapter + +usb:v050Dp0706* + ID_MODEL_FROM_DATABASE=2-N-1 7-Port Hub (Lower half) + +usb:v050Dp0802* + ID_MODEL_FROM_DATABASE=Nostromo n40 Gamepad + +usb:v050Dp0803* + ID_MODEL_FROM_DATABASE=Nostromo 1745 GamePad + +usb:v050Dp0805* + ID_MODEL_FROM_DATABASE=Nostromo N50 GamePad + +usb:v050Dp0815* + ID_MODEL_FROM_DATABASE=Nostromo n52 HID SpeedPad Mouse Wheel + +usb:v050Dp0826* + ID_MODEL_FROM_DATABASE=ErgoFit Wireless Optical Mouse (HID) + +usb:v050Dp0980* + ID_MODEL_FROM_DATABASE=HID UPS Battery + +usb:v050Dp1004* + ID_MODEL_FROM_DATABASE=F9L1004 802.11n Surf N300 XR Wireless Adapter [Realtek RTL8192CU] + +usb:v050Dp1102* + ID_MODEL_FROM_DATABASE=F7D1102 N150/Surf Micro Wireless Adapter v1000 [Realtek RTL8188CUS] + +usb:v050Dp1103* + ID_MODEL_FROM_DATABASE=F9L1103 N750 DB 802.11abgn 2x3:3 [Ralink RT3573] + +usb:v050Dp1106* + ID_MODEL_FROM_DATABASE=F9L1106v1 802.11a/b/g/n/ac Wireless Adapter [Broadcom BCM43526] + +usb:v050Dp1109* + ID_MODEL_FROM_DATABASE=F9L1109v1 802.11a/b/g/n/ac Wireless Adapter [Realtek RTL8812AU] + +usb:v050Dp110A* + ID_MODEL_FROM_DATABASE=F9L1101v2 802.11abgn Wireless Adapter [Realtek RTL8192DU] + +usb:v050Dp11F2* + ID_MODEL_FROM_DATABASE=ISY Wireless Micro Adapter IWL 2000 [RTL8188CUS] + +usb:v050Dp1202* + ID_MODEL_FROM_DATABASE=F5U120-PC Parallel Printer Port + +usb:v050Dp1203* + ID_MODEL_FROM_DATABASE=F5U120-PC Serial Port + +usb:v050Dp2103* + ID_MODEL_FROM_DATABASE=F7D2102 802.11n N300 Micro Wireless Adapter v3000 [Realtek RTL8192CU] + +usb:v050Dp21F1* + ID_MODEL_FROM_DATABASE=N300 WLAN N Adapter [ISY] + +usb:v050Dp21F2* + ID_MODEL_FROM_DATABASE=RTL8192CU 802.11n WLAN Adapter [ISY IWL 4000] + +usb:v050Dp258A* + ID_MODEL_FROM_DATABASE=F5U258 Host to Host cable + +usb:v050Dp3101* + ID_MODEL_FROM_DATABASE=F1DF102U/F1DG102U Flip Hub + +usb:v050Dp3201* + ID_MODEL_FROM_DATABASE=F1DF102U/F1DG102U Flip KVM + +usb:v050Dp4050* + ID_MODEL_FROM_DATABASE=ZD1211B + +usb:v050Dp5055* + ID_MODEL_FROM_DATABASE=F5D5055 Gigabit Network Adapter [AX88xxx] + +usb:v050Dp6050* + ID_MODEL_FROM_DATABASE=F6D6050 802.11abgn Wireless Adapter [Broadcom BCM4323] + +usb:v050Dp6051* + ID_MODEL_FROM_DATABASE=F5D6051 802.11b Wireless Network Adapter [ZyDAS ZD1201] + +usb:v050Dp615A* + ID_MODEL_FROM_DATABASE=F7D4101 / F9L1101v1 802.11abgn Wireless Adapter [Broadcom BCM4323] + +usb:v050Dp7050* + ID_MODEL_FROM_DATABASE=F5D7050 Wireless G Adapter v1000/v2000 [Intersil ISL3887] + +usb:v050Dp7051* + ID_MODEL_FROM_DATABASE=F5D7051 802.11g Adapter v1000 [Broadcom 4320 USB] + +usb:v050Dp705A* + ID_MODEL_FROM_DATABASE=F5D7050 Wireless G Adapter v3000 [Ralink RT2571W] + +usb:v050Dp705B* + ID_MODEL_FROM_DATABASE=Wireless G Adapter + +usb:v050Dp705C* + ID_MODEL_FROM_DATABASE=F5D7050 Wireless G Adapter v4000 [Zydas ZD1211B] + +usb:v050Dp705E* + ID_MODEL_FROM_DATABASE=F5D7050 Wireless G Adapter v5000 [Realtek RTL8187B] + +usb:v050Dp706A* + ID_MODEL_FROM_DATABASE=2-N-1 7-Port Hub (Upper half) + +usb:v050Dp8053* + ID_MODEL_FROM_DATABASE=F5D8053 N Wireless USB Adapter v1000/v4000 [Ralink RT2870] + +usb:v050Dp805C* + ID_MODEL_FROM_DATABASE=F5D8053 N Wireless Adapter v3000 [Ralink RT2870] + +usb:v050Dp805E* + ID_MODEL_FROM_DATABASE=F5D8053 N Wireless USB Adapter v5000 [Realtek RTL8192U] + +usb:v050Dp815C* + ID_MODEL_FROM_DATABASE=F5D8053 N Wireless USB Adapter v3000 [Ralink RT2870] + +usb:v050Dp815F* + ID_MODEL_FROM_DATABASE=F5D8053 N Wireless USB Adapter v6000 [Realtek RTL8192SU] + +usb:v050Dp825A* + ID_MODEL_FROM_DATABASE=F5D8055 N+ Wireless Adapter v1000 [Ralink RT2870] + +usb:v050Dp825B* + ID_MODEL_FROM_DATABASE=F5D8055 N+ Wireless Adapter v2000 [Ralink RT3072] + +usb:v050Dp845A* + ID_MODEL_FROM_DATABASE=F7D2101 802.11n Surf & Share Wireless Adapter v1000 [Realtek RTL8192SU] + +usb:v050Dp905B* + ID_MODEL_FROM_DATABASE=F5D9050 Wireless G+ MIMO Network Adapter v3000 [Ralink RT2573] + +usb:v050Dp905C* + ID_MODEL_FROM_DATABASE=F5D9050 Wireless G+ MIMO Network Adapter v4000 [Ralink RT2573] + +usb:v050Dp935A* + ID_MODEL_FROM_DATABASE=F6D4050 N150 Enhanced Wireless Network Adapter v1000 [Ralink RT3070] + +usb:v050Dp935B* + ID_MODEL_FROM_DATABASE=F6D4050 N150 Enhanced Wireless Network Adapter v2000 [Ralink RT3070] + +usb:v050Dp945A* + ID_MODEL_FROM_DATABASE=F7D1101 v1 Basic Wireless Adapter [Realtek RTL8188SU] + +usb:v050Dp945B* + ID_MODEL_FROM_DATABASE=F7D1101 v2 Basic Wireless Adapter [Ralink RT3370] + +usb:v050DpD321* + ID_MODEL_FROM_DATABASE=Dynex DX-NUSB 802.11bgn Wireless Adapter [Broadcom BCM43231] + +usb:v050E* + ID_VENDOR_FROM_DATABASE=Neon Technology, Inc. + +usb:v050F* + ID_VENDOR_FROM_DATABASE=KC Technology, Inc. + +usb:v050Fp0001* + ID_MODEL_FROM_DATABASE=Hub + +usb:v050Fp0003* + ID_MODEL_FROM_DATABASE=KC82C160S Hub + +usb:v050Fp0180* + ID_MODEL_FROM_DATABASE=KC-180 IrDA Dongle + +usb:v050Fp0190* + ID_MODEL_FROM_DATABASE=KC2190 USB Host-to-Host cable + +usb:v0510* + ID_VENDOR_FROM_DATABASE=Sejin Electron, Inc. + +usb:v0510p0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0510p1000* + ID_MODEL_FROM_DATABASE=Keyboard with PS/2 Mouse Port + +usb:v0510pE001* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0511* + ID_VENDOR_FROM_DATABASE=N'Able (DataBook) Technologies, Inc. + +usb:v0511p002B* + ID_MODEL_FROM_DATABASE=AOC DVB + +usb:v0512* + ID_VENDOR_FROM_DATABASE=Hualon Microelectronics Corp. + +usb:v0513* + ID_VENDOR_FROM_DATABASE=digital-X, Inc. + +usb:v0514* + ID_VENDOR_FROM_DATABASE=FCI Electronics + +usb:v0515* + ID_VENDOR_FROM_DATABASE=ACTC + +usb:v0516* + ID_VENDOR_FROM_DATABASE=Longwell Electronics + +usb:v0517* + ID_VENDOR_FROM_DATABASE=Butterfly Communications + +usb:v0518* + ID_VENDOR_FROM_DATABASE=EzKEY Corp. + +usb:v0518p0001* + ID_MODEL_FROM_DATABASE=USB to PS2 Adaptor v1.09 + +usb:v0518p0002* + ID_MODEL_FROM_DATABASE=EZ-9900C Keyboard + +usb:v0519* + ID_VENDOR_FROM_DATABASE=Star Micronics Co., Ltd + +usb:v0519p0003* + ID_MODEL_FROM_DATABASE=TSP100ECO/TSP100II + +usb:v0519pC002* + ID_MODEL_FROM_DATABASE=Xlive Bluetooth XBM-100S MP3 Player + +usb:v051A* + ID_VENDOR_FROM_DATABASE=WYSE Technology + +usb:v051ApA005* + ID_MODEL_FROM_DATABASE=Smart Display Version 9973 + +usb:v051B* + ID_VENDOR_FROM_DATABASE=Silicon Graphics + +usb:v051C* + ID_VENDOR_FROM_DATABASE=Shuttle, Inc. + +usb:v051Cp0005* + ID_MODEL_FROM_DATABASE=VFD Module + +usb:v051CpC001* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v051CpC002* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v051D* + ID_VENDOR_FROM_DATABASE=American Power Conversion + +usb:v051Dp0001* + ID_MODEL_FROM_DATABASE=UPS + +usb:v051Dp0002* + ID_MODEL_FROM_DATABASE=Uninterruptible Power Supply + +usb:v051Dp0003* + ID_MODEL_FROM_DATABASE=UPS + +usb:v051E* + ID_VENDOR_FROM_DATABASE=Scientific Atlanta, Inc. + +usb:v051F* + ID_VENDOR_FROM_DATABASE=IO Systems (Elite Electronics), Inc. + +usb:v0520* + ID_VENDOR_FROM_DATABASE=Taiwan Semiconductor Manufacturing Co. + +usb:v0521* + ID_VENDOR_FROM_DATABASE=Airborn Connectors + +usb:v0522* + ID_VENDOR_FROM_DATABASE=Advanced Connectek, Inc. + +usb:v0523* + ID_VENDOR_FROM_DATABASE=ATEN GmbH + +usb:v0524* + ID_VENDOR_FROM_DATABASE=Sola Electronics + +usb:v0525* + ID_VENDOR_FROM_DATABASE=Netchip Technology, Inc. + +usb:v0525p100D* + ID_MODEL_FROM_DATABASE=RFMD Bluetooth Device + +usb:v0525p1080* + ID_MODEL_FROM_DATABASE=NET1080 USB-USB Bridge + +usb:v0525p1200* + ID_MODEL_FROM_DATABASE=SSDC Adapter II + +usb:v0525p1265* + ID_MODEL_FROM_DATABASE=File-backed Storage Gadget + +usb:v0525p3424* + ID_MODEL_FROM_DATABASE=Lumidigm Venus fingerprint sensor + +usb:v0525pA0F0* + ID_MODEL_FROM_DATABASE=Cambridge Electronic Devices Power1401 mk 2 + +usb:v0525pA140* + ID_MODEL_FROM_DATABASE=USB Clik! 40 + +usb:v0525pA141* + ID_MODEL_FROM_DATABASE=(OME) PocketZip 40 MP3 Player Driver + +usb:v0525pA220* + ID_MODEL_FROM_DATABASE=GVC Bluetooth Wireless Adapter + +usb:v0525pA4A0* + ID_MODEL_FROM_DATABASE=Linux-USB "Gadget Zero" + +usb:v0525pA4A1* + ID_MODEL_FROM_DATABASE=Linux-USB Ethernet Gadget + +usb:v0525pA4A2* + ID_MODEL_FROM_DATABASE=Linux-USB Ethernet/RNDIS Gadget + +usb:v0525pA4A3* + ID_MODEL_FROM_DATABASE=Linux-USB user-mode isochronous source/sink + +usb:v0525pA4A4* + ID_MODEL_FROM_DATABASE=Linux-USB user-mode bulk source/sink + +usb:v0525pA4A5* + ID_MODEL_FROM_DATABASE=Pocketbook Pro 903 + +usb:v0525pA4A6* + ID_MODEL_FROM_DATABASE=Linux-USB Serial Gadget + +usb:v0525pA4A7* + ID_MODEL_FROM_DATABASE=Linux-USB Serial Gadget (CDC ACM mode) + +usb:v0525pA4A8* + ID_MODEL_FROM_DATABASE=Linux-USB Printer Gadget + +usb:v0525pA4A9* + ID_MODEL_FROM_DATABASE=Linux-USB OBEX Gadget + +usb:v0525pA4AA* + ID_MODEL_FROM_DATABASE=Linux-USB CDC Composite Gadge (Ethernet and ACM) + +usb:v0526* + ID_VENDOR_FROM_DATABASE=Temic MHS S.A. + +usb:v0527* + ID_VENDOR_FROM_DATABASE=ALTRA + +usb:v0528* + ID_VENDOR_FROM_DATABASE=ATI Technologies, Inc. + +usb:v0528p7561* + ID_MODEL_FROM_DATABASE=TV Wonder + +usb:v0528p7562* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FN5) + +usb:v0528p7563* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FI) + +usb:v0528p7564* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FQ) + +usb:v0528p7565* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (NTSC+) + +usb:v0528p7566* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FN5) + +usb:v0528p7567* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FI) + +usb:v0528p7568* + ID_MODEL_FROM_DATABASE=TV Wonder, Edition (FQ) + +usb:v0528p7569* + ID_MODEL_FROM_DATABASE=Live! Pro (A) + +usb:v0528p756A* + ID_MODEL_FROM_DATABASE=Live! Pro Audio (O) + +usb:v0529* + ID_VENDOR_FROM_DATABASE=Aladdin Knowledge Systems + +usb:v0529p0001* + ID_MODEL_FROM_DATABASE=HASP copy protection dongle + +usb:v0529p030B* + ID_MODEL_FROM_DATABASE=eToken R1 v3.1.3.x + +usb:v0529p0313* + ID_MODEL_FROM_DATABASE=eToken R1 v3.2.3.x + +usb:v0529p031B* + ID_MODEL_FROM_DATABASE=eToken R1 v3.3.3.x + +usb:v0529p0323* + ID_MODEL_FROM_DATABASE=eToken R1 v3.4.3.x + +usb:v0529p0412* + ID_MODEL_FROM_DATABASE=eToken R2 v2.2.4.x + +usb:v0529p041A* + ID_MODEL_FROM_DATABASE=eToken R2 v2.2.4.x + +usb:v0529p0422* + ID_MODEL_FROM_DATABASE=eToken R2 v2.4.4.x + +usb:v0529p042A* + ID_MODEL_FROM_DATABASE=eToken R2 v2.5.4.x + +usb:v0529p050C* + ID_MODEL_FROM_DATABASE=eToken Pro v4.1.5.x + +usb:v0529p0514* + ID_MODEL_FROM_DATABASE=eToken Pro v4.2.5.4 + +usb:v0529p0600* + ID_MODEL_FROM_DATABASE=eToken Pro 64k (4.2) + +usb:v0529p0620* + ID_MODEL_FROM_DATABASE=Token JC + +usb:v052A* + ID_VENDOR_FROM_DATABASE=Crescent Heart Software + +usb:v052B* + ID_VENDOR_FROM_DATABASE=Tekom Technologies, Inc. + +usb:v052Bp0102* + ID_MODEL_FROM_DATABASE=Ca508A HP1020 Camera v.1.3.1.6 + +usb:v052Bp0801* + ID_MODEL_FROM_DATABASE=Yakumo MegaImage 37 + +usb:v052Bp1512* + ID_MODEL_FROM_DATABASE=Yakumo MegaImage IV + +usb:v052Bp1513* + ID_MODEL_FROM_DATABASE=Aosta CX100 Webcam + +usb:v052Bp1514* + ID_MODEL_FROM_DATABASE=Aosta CX100 Webcam Storage + +usb:v052Bp1905* + ID_MODEL_FROM_DATABASE=Yakumo MegaImage 47 + +usb:v052Bp1911* + ID_MODEL_FROM_DATABASE=Yakumo MegaImage 47 SL + +usb:v052Bp2202* + ID_MODEL_FROM_DATABASE=WDM Still Image Capture + +usb:v052Bp2203* + ID_MODEL_FROM_DATABASE=Sound Vision Stream Driver + +usb:v052Bp3A06* + ID_MODEL_FROM_DATABASE=DigiLife DDV-5120A + +usb:v052BpD001* + ID_MODEL_FROM_DATABASE=P35U Camera Capture + +usb:v052C* + ID_VENDOR_FROM_DATABASE=Canon Information Systems, Inc. + +usb:v052D* + ID_VENDOR_FROM_DATABASE=Avid Electronics Corp. + +usb:v052E* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp. + +usb:v052F* + ID_VENDOR_FROM_DATABASE=Unicore Software, Inc. + +usb:v0530* + ID_VENDOR_FROM_DATABASE=American Microsystems, Inc. + +usb:v0531* + ID_VENDOR_FROM_DATABASE=Wacom Technology Corp. + +usb:v0532* + ID_VENDOR_FROM_DATABASE=Systech Corp. + +usb:v0533* + ID_VENDOR_FROM_DATABASE=Alcatel Mobile Phones + +usb:v0534* + ID_VENDOR_FROM_DATABASE=Motorola, Inc. + +usb:v0535* + ID_VENDOR_FROM_DATABASE=LIH TZU Electric Co., Ltd + +usb:v0536* + ID_VENDOR_FROM_DATABASE=Hand Held Products (Welch Allyn, Inc.) + +usb:v0536p01A0* + ID_MODEL_FROM_DATABASE=PDT + +usb:v0537* + ID_VENDOR_FROM_DATABASE=Inventec Corp. + +usb:v0538* + ID_VENDOR_FROM_DATABASE=Caldera International, Inc. (SCO) + +usb:v0539* + ID_VENDOR_FROM_DATABASE=Shyh Shiun Terminals Co., Ltd + +usb:v053A* + ID_VENDOR_FROM_DATABASE=PrehKeyTec GmbH + +usb:v053Ap0B00* + ID_MODEL_FROM_DATABASE=Hub + +usb:v053Ap0B01* + ID_MODEL_FROM_DATABASE=Preh MCI 3100 + +usb:v053B* + ID_VENDOR_FROM_DATABASE=Global Village Communication + +usb:v053C* + ID_VENDOR_FROM_DATABASE=Institut of Microelectronic & Mechatronic Systems + +usb:v053D* + ID_VENDOR_FROM_DATABASE=Silicon Architect + +usb:v053E* + ID_VENDOR_FROM_DATABASE=Mobility Electronics + +usb:v053F* + ID_VENDOR_FROM_DATABASE=Synopsys, Inc. + +usb:v0540* + ID_VENDOR_FROM_DATABASE=UniAccess AB + +usb:v0540p0101* + ID_MODEL_FROM_DATABASE=Panache Surf ISDN TA + +usb:v0541* + ID_VENDOR_FROM_DATABASE=Sirf Technology, Inc. + +usb:v0543* + ID_VENDOR_FROM_DATABASE=ViewSonic Corp. + +usb:v0543p00FE* + ID_MODEL_FROM_DATABASE=G773 Monitor Hub + +usb:v0543p00FF* + ID_MODEL_FROM_DATABASE=P815 Monitor Hub + +usb:v0543p0BF2* + ID_MODEL_FROM_DATABASE=airpanel V150 Wireless Smart Display + +usb:v0543p0BF3* + ID_MODEL_FROM_DATABASE=airpanel V110 Wireless Smart Display + +usb:v0543p0ED9* + ID_MODEL_FROM_DATABASE=Color Pocket PC V35 + +usb:v0543p0F01* + ID_MODEL_FROM_DATABASE=airsync Wi-Fi Wireless Adapter + +usb:v0543p1527* + ID_MODEL_FROM_DATABASE=Color Pocket PC V36 + +usb:v0543p1529* + ID_MODEL_FROM_DATABASE=Color Pocket PC V37 + +usb:v0543p152B* + ID_MODEL_FROM_DATABASE=Color Pocket PC V38 + +usb:v0543p152E* + ID_MODEL_FROM_DATABASE=Pocket PC + +usb:v0543p1921* + ID_MODEL_FROM_DATABASE=Communicator Pocket PC + +usb:v0543p1922* + ID_MODEL_FROM_DATABASE=Smartphone + +usb:v0543p1923* + ID_MODEL_FROM_DATABASE=Pocket PC V30 + +usb:v0543p1A11* + ID_MODEL_FROM_DATABASE=Wireless 802.11g Adapter + +usb:v0543p1E60* + ID_MODEL_FROM_DATABASE=TA310 - ATSC/NTSC/PAL Driver(PCM4) + +usb:v0543p4153* + ID_MODEL_FROM_DATABASE=ViewSonic G773 Control (?) + +usb:v0544* + ID_VENDOR_FROM_DATABASE=Cristie Electronics, Ltd + +usb:v0545* + ID_VENDOR_FROM_DATABASE=Xirlink, Inc. + +usb:v0545p7333* + ID_MODEL_FROM_DATABASE=Trution Web Camera + +usb:v0545p8002* + ID_MODEL_FROM_DATABASE=IBM NetCamera + +usb:v0545p8009* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p800C* + ID_MODEL_FROM_DATABASE=Veo Stingray + +usb:v0545p800D* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p8080* + ID_MODEL_FROM_DATABASE=IBM C-It Webcam + +usb:v0545p808A* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p808B* + ID_MODEL_FROM_DATABASE=Veo Stingray + +usb:v0545p808D* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p810A* + ID_MODEL_FROM_DATABASE=Veo Advanced Connect Webcam + +usb:v0545p810B* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p810C* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p8135* + ID_MODEL_FROM_DATABASE=Veo Mobile/Advanced Web Camera + +usb:v0545p813A* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p813B* + ID_MODEL_FROM_DATABASE=Veo PC Camera + +usb:v0545p813C* + ID_MODEL_FROM_DATABASE=Veo Mobile/Advanced Web Camera + +usb:v0545p8333* + ID_MODEL_FROM_DATABASE=Veo Stingray/Connect Web Camera + +usb:v0545p888C* + ID_MODEL_FROM_DATABASE=eVision 123 digital camera + +usb:v0545p888D* + ID_MODEL_FROM_DATABASE=eVision 123 digital camera + +usb:v0546* + ID_VENDOR_FROM_DATABASE=Polaroid Corp. + +usb:v0546p0DAF* + ID_MODEL_FROM_DATABASE=PDC 2300Z + +usb:v0546p1BED* + ID_MODEL_FROM_DATABASE=PDC 1320 Camera + +usb:v0546p3097* + ID_MODEL_FROM_DATABASE=PDC 310 + +usb:v0546p3155* + ID_MODEL_FROM_DATABASE=PDC 3070 Camera + +usb:v0546p3187* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0546p3191* + ID_MODEL_FROM_DATABASE=Ion 80 Camera + +usb:v0546p3273* + ID_MODEL_FROM_DATABASE=PDC 2030 Camera + +usb:v0546p3304* + ID_MODEL_FROM_DATABASE=a500 Digital Camera + +usb:v0546pDCCF* + ID_MODEL_FROM_DATABASE=Sound Vision Stream Driver + +usb:v0547* + ID_VENDOR_FROM_DATABASE=Anchor Chips, Inc. + +usb:v0547p0001* + ID_MODEL_FROM_DATABASE=ICSI Bluetooth Device + +usb:v0547p1002* + ID_MODEL_FROM_DATABASE=Python2 WDM Encoder + +usb:v0547p1006* + ID_MODEL_FROM_DATABASE=Hantek DSO-2100 UF + +usb:v0547p2131* + ID_MODEL_FROM_DATABASE=AN2131 EZUSB Microcontroller + +usb:v0547p2235* + ID_MODEL_FROM_DATABASE=AN2235 EZUSB-FX Microcontroller + +usb:v0547p2710* + ID_MODEL_FROM_DATABASE=EZ-Link Loader (EZLNKLDR.SYS) + +usb:v0547p2720* + ID_MODEL_FROM_DATABASE=AN2720 USB-USB Bridge + +usb:v0547p2727* + ID_MODEL_FROM_DATABASE=Xircom PGUNET USB-USB Bridge + +usb:v0547p2750* + ID_MODEL_FROM_DATABASE=EZ-Link (EZLNKUSB.SYS) + +usb:v0547p2810* + ID_MODEL_FROM_DATABASE=Cypress ATAPI Bridge + +usb:v0547p4D90* + ID_MODEL_FROM_DATABASE=AmScope MD1900 camera + +usb:v0547p6510* + ID_MODEL_FROM_DATABASE=Touptek UCMOS05100KPA + +usb:v0547p7000* + ID_MODEL_FROM_DATABASE=PowerSpec MCE460 Front Panel LED Display + +usb:v0547p7777* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0547p9999* + ID_MODEL_FROM_DATABASE=AN2131 uninitialized (?) + +usb:v0548* + ID_VENDOR_FROM_DATABASE=Tyan Computer Corp. + +usb:v0548p1005* + ID_MODEL_FROM_DATABASE=EZ Cart II GameBoy Flash Programmer + +usb:v0549* + ID_VENDOR_FROM_DATABASE=Pixera Corp. + +usb:v054A* + ID_VENDOR_FROM_DATABASE=Fujitsu Microelectronics, Inc. + +usb:v054B* + ID_VENDOR_FROM_DATABASE=New Media Corp. + +usb:v054C* + ID_VENDOR_FROM_DATABASE=Sony Corp. + +usb:v054Cp0001* + ID_MODEL_FROM_DATABASE=HUB + +usb:v054Cp0002* + ID_MODEL_FROM_DATABASE=Standard HUB + +usb:v054Cp0010* + ID_MODEL_FROM_DATABASE=DSC-S30/S70/S75/F505V/F505/FD92/W1 Cybershot/Mavica Digital Camera + +usb:v054Cp0014* + ID_MODEL_FROM_DATABASE=Nogatech USBVision (SY) + +usb:v054Cp0022* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 (TPP) + +usb:v054Cp0023* + ID_MODEL_FROM_DATABASE=CD Writer + +usb:v054Cp0024* + ID_MODEL_FROM_DATABASE=Mavica CD-1000 Camera + +usb:v054Cp0025* + ID_MODEL_FROM_DATABASE=NW-MS7 Walkman MemoryStick Reader + +usb:v054Cp002B* + ID_MODEL_FROM_DATABASE=Portable USB Harddrive V2 + +usb:v054Cp002C* + ID_MODEL_FROM_DATABASE=USB Floppy Disk Drive + +usb:v054Cp002D* + ID_MODEL_FROM_DATABASE=MSAC-US1 MemoryStick Reader + +usb:v054Cp002E* + ID_MODEL_FROM_DATABASE=HandyCam MemoryStick Reader + +usb:v054Cp0030* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 (TPP) + +usb:v054Cp0032* + ID_MODEL_FROM_DATABASE=MemoryStick MSC-U01 Reader + +usb:v054Cp0035* + ID_MODEL_FROM_DATABASE=Network Walkman (E) + +usb:v054Cp0036* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0037* + ID_MODEL_FROM_DATABASE=MG Memory Stick Reader/Writer + +usb:v054Cp0038* + ID_MODEL_FROM_DATABASE=Clie PEG-S300/D PalmOS PDA + +usb:v054Cp0039* + ID_MODEL_FROM_DATABASE=Network Walkman (MS) + +usb:v054Cp003C* + ID_MODEL_FROM_DATABASE=VAIO-MX LCD Control + +usb:v054Cp0045* + ID_MODEL_FROM_DATABASE=Digital Imaging Video + +usb:v054Cp0046* + ID_MODEL_FROM_DATABASE=Network Walkman + +usb:v054Cp004A* + ID_MODEL_FROM_DATABASE=Memory Stick Hi-Fi System + +usb:v054Cp004B* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v054Cp004E* + ID_MODEL_FROM_DATABASE=DSC-xxx (ptp) + +usb:v054Cp0056* + ID_MODEL_FROM_DATABASE=MG Memory Stick Reader/Writer + +usb:v054Cp0058* + ID_MODEL_FROM_DATABASE=Clie PEG-N7x0C PalmOS PDA Mass Storage + +usb:v054Cp0066* + ID_MODEL_FROM_DATABASE=Clie PEG-N7x0C/PEG-T425 PalmOS PDA Serial + +usb:v054Cp0067* + ID_MODEL_FROM_DATABASE=CMR-PC3 Webcam + +usb:v054Cp0069* + ID_MODEL_FROM_DATABASE=Memorystick MSC-U03 Reader + +usb:v054Cp006C* + ID_MODEL_FROM_DATABASE=FeliCa S310 [PaSoRi] + +usb:v054Cp006D* + ID_MODEL_FROM_DATABASE=Clie PEG-T425 PDA Mass Storage + +usb:v054Cp006F* + ID_MODEL_FROM_DATABASE=Network Walkman (EV) + +usb:v054Cp0073* + ID_MODEL_FROM_DATABASE=Storage CRX1750U + +usb:v054Cp0075* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0076* + ID_MODEL_FROM_DATABASE=Storage Adapter ACR-U20 + +usb:v054Cp007C* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp007F* + ID_MODEL_FROM_DATABASE=IC Recorder (MS) + +usb:v054Cp0080* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0081* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0084* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0085* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0086* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp008B* + ID_MODEL_FROM_DATABASE=Micro Vault 64M Mass Storage + +usb:v054Cp0095* + ID_MODEL_FROM_DATABASE=Clie s360 + +usb:v054Cp0099* + ID_MODEL_FROM_DATABASE=Clie NR70 PDA Mass Storage + +usb:v054Cp009A* + ID_MODEL_FROM_DATABASE=Clie NR70 PDA Serial + +usb:v054Cp00AB* + ID_MODEL_FROM_DATABASE=Visual Communication Camera (PCGA-UVC10) + +usb:v054Cp00AF* + ID_MODEL_FROM_DATABASE=DPP-EX Series Digital Photo Printer + +usb:v054Cp00BF* + ID_MODEL_FROM_DATABASE=IC Recorder (S) + +usb:v054Cp00C0* + ID_MODEL_FROM_DATABASE=Handycam DCR-30 + +usb:v054Cp00C6* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp00C7* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp00C8* + ID_MODEL_FROM_DATABASE=MZ-N710 Minidisc Walkman + +usb:v054Cp00C9* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp00CA* + ID_MODEL_FROM_DATABASE=MZ-DN430 Minidisc Walkman + +usb:v054Cp00CB* + ID_MODEL_FROM_DATABASE=MSAC-US20 Memory Stick Reader + +usb:v054Cp00DA* + ID_MODEL_FROM_DATABASE=Clie nx60 + +usb:v054Cp00E8* + ID_MODEL_FROM_DATABASE=Network Walkman (MS) + +usb:v054Cp00E9* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v054Cp00EB* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0101* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0103* + ID_MODEL_FROM_DATABASE=IC Recorder (ST) + +usb:v054Cp0105* + ID_MODEL_FROM_DATABASE=Micro Vault Hub + +usb:v054Cp0107* + ID_MODEL_FROM_DATABASE=VCC-U01 Visual Communication Camera + +usb:v054Cp0110* + ID_MODEL_FROM_DATABASE=Digital Imaging Video + +usb:v054Cp0113* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0116* + ID_MODEL_FROM_DATABASE=IC Recorder (P) + +usb:v054Cp0144* + ID_MODEL_FROM_DATABASE=Clie PEG-TH55 PDA + +usb:v054Cp0147* + ID_MODEL_FROM_DATABASE=Visual Communication Camera (PCGA-UVC11) + +usb:v054Cp014C* + ID_MODEL_FROM_DATABASE=Aiwa AM-NX9 Net MD Music Recorder MDLP + +usb:v054Cp014D* + ID_MODEL_FROM_DATABASE=Memory Stick Reader/Writer + +usb:v054Cp0154* + ID_MODEL_FROM_DATABASE=Eyetoy Audio Device + +usb:v054Cp015F* + ID_MODEL_FROM_DATABASE=IC Recorder (BM) + +usb:v054Cp0169* + ID_MODEL_FROM_DATABASE=Clie PEG-TJ35 PDA Serial + +usb:v054Cp016A* + ID_MODEL_FROM_DATABASE=Clie PEG-TJ35 PDA Mass Storage + +usb:v054Cp016B* + ID_MODEL_FROM_DATABASE=Mobile HDD + +usb:v054Cp016D* + ID_MODEL_FROM_DATABASE=IC Recorder (SX) + +usb:v054Cp016E* + ID_MODEL_FROM_DATABASE=DPP-EX50 Digital Photo Printer + +usb:v054Cp0171* + ID_MODEL_FROM_DATABASE=Fingerprint Sensor 3500 + +usb:v054Cp017E* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp017F* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0180* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0181* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0182* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0183* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0184* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0185* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0186* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0187* + ID_MODEL_FROM_DATABASE=Hi-MD MZ-NH600 WALKMAN + +usb:v054Cp0188* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp018A* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp018B* + ID_MODEL_FROM_DATABASE=Hi-MD SOUND GATE + +usb:v054Cp019E* + ID_MODEL_FROM_DATABASE=Micro Vault 1.0G Mass Storage + +usb:v054Cp01AD* + ID_MODEL_FROM_DATABASE=ATRAC HDD PA + +usb:v054Cp01BB* + ID_MODEL_FROM_DATABASE=FeliCa S320 [PaSoRi] + +usb:v054Cp01BD* + ID_MODEL_FROM_DATABASE=MRW62E Multi-Card Reader/Writer + +usb:v054Cp01C3* + ID_MODEL_FROM_DATABASE=NW-E55 Network Walkman + +usb:v054Cp01C6* + ID_MODEL_FROM_DATABASE=MEMORY P-AUDIO + +usb:v054Cp01C7* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v054Cp01C8* + ID_MODEL_FROM_DATABASE=PSP Type A + +usb:v054Cp01C9* + ID_MODEL_FROM_DATABASE=PSP Type B + +usb:v054Cp01D0* + ID_MODEL_FROM_DATABASE=DVD+RW External Drive DRU-700A + +usb:v054Cp01D5* + ID_MODEL_FROM_DATABASE=IC RECORDER + +usb:v054Cp01DE* + ID_MODEL_FROM_DATABASE=VRD-VC10 [Video Capture] + +usb:v054Cp01E8* + ID_MODEL_FROM_DATABASE=UP-DR150 Photo Printer + +usb:v054Cp01E9* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp01EA* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp01EE* + ID_MODEL_FROM_DATABASE=IC RECORDER + +usb:v054Cp01FA* + ID_MODEL_FROM_DATABASE=IC Recorder (P) + +usb:v054Cp01FB* + ID_MODEL_FROM_DATABASE=NW-E405 Network Walkman + +usb:v054Cp020F* + ID_MODEL_FROM_DATABASE=Device + +usb:v054Cp0210* + ID_MODEL_FROM_DATABASE=ATRAC HDD PA + +usb:v054Cp0219* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp021A* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp021B* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp021C* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp021D* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0227* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v054Cp022C* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp022D* + ID_MODEL_FROM_DATABASE=Hi-MD AUDIO + +usb:v054Cp0233* + ID_MODEL_FROM_DATABASE=ATRAC HDD PA + +usb:v054Cp0236* + ID_MODEL_FROM_DATABASE=Mobile HDD + +usb:v054Cp023B* + ID_MODEL_FROM_DATABASE=DVD+RW External Drive DRU-800UL + +usb:v054Cp023C* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp023D* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0243* + ID_MODEL_FROM_DATABASE=MicroVault Flash Drive + +usb:v054Cp024B* + ID_MODEL_FROM_DATABASE=Vaio VGX Mouse + +usb:v054Cp0257* + ID_MODEL_FROM_DATABASE=IFU-WLM2 USB Wireless LAN Module (Wireless Mode) + +usb:v054Cp0258* + ID_MODEL_FROM_DATABASE=IFU-WLM2 USB Wireless LAN Module (Memory Mode) + +usb:v054Cp0259* + ID_MODEL_FROM_DATABASE=IC RECORDER + +usb:v054Cp0267* + ID_MODEL_FROM_DATABASE=Tachikoma Device + +usb:v054Cp0268* + ID_MODEL_FROM_DATABASE=Batoh Device / PlayStation 3 Controller + +usb:v054Cp0269* + ID_MODEL_FROM_DATABASE=HDD WALKMAN + +usb:v054Cp026A* + ID_MODEL_FROM_DATABASE=HDD WALKMAN + +usb:v054Cp0271* + ID_MODEL_FROM_DATABASE=IC Recorder (P) + +usb:v054Cp027C* + ID_MODEL_FROM_DATABASE=NETWORK WALKMAN + +usb:v054Cp027E* + ID_MODEL_FROM_DATABASE=SONY Communicator + +usb:v054Cp027F* + ID_MODEL_FROM_DATABASE=IC RECORDER + +usb:v054Cp0286* + ID_MODEL_FROM_DATABASE=Net MD + +usb:v054Cp0287* + ID_MODEL_FROM_DATABASE=Hi-MD WALKMAN + +usb:v054Cp0290* + ID_MODEL_FROM_DATABASE=VGP-UVC100 Visual Communication Camera + +usb:v054Cp029B* + ID_MODEL_FROM_DATABASE=PRS-500 eBook reader + +usb:v054Cp02A5* + ID_MODEL_FROM_DATABASE=MicroVault Flash Drive + +usb:v054Cp02AF* + ID_MODEL_FROM_DATABASE=Handycam DCR-DVD306E + +usb:v054Cp02C4* + ID_MODEL_FROM_DATABASE=Device + +usb:v054Cp02D1* + ID_MODEL_FROM_DATABASE=DVD RW + +usb:v054Cp02D2* + ID_MODEL_FROM_DATABASE=PSP Slim + +usb:v054Cp02D8* + ID_MODEL_FROM_DATABASE=SBAC-US10 SxS PRO memory card reader/writer + +usb:v054Cp02E1* + ID_MODEL_FROM_DATABASE=FeliCa S330 [PaSoRi] + +usb:v054Cp02EA* + ID_MODEL_FROM_DATABASE=PlayStation 3 Memory Card Adaptor + +usb:v054Cp02F9* + ID_MODEL_FROM_DATABASE=DSC-H9 + +usb:v054Cp0317* + ID_MODEL_FROM_DATABASE=WALKMAN + +usb:v054Cp031A* + ID_MODEL_FROM_DATABASE=Walkman NWD-B103F + +usb:v054Cp031E* + ID_MODEL_FROM_DATABASE=PRS-300/PRS-505 eBook reader + +usb:v054Cp0325* + ID_MODEL_FROM_DATABASE=NWZ-A818 + +usb:v054Cp033E* + ID_MODEL_FROM_DATABASE=DSC-W120/W290 + +usb:v054Cp0346* + ID_MODEL_FROM_DATABASE=Handycam DCR-SR55E + +usb:v054Cp0348* + ID_MODEL_FROM_DATABASE=HandyCam HDR-TG3E + +usb:v054Cp035B* + ID_MODEL_FROM_DATABASE=Walkman NWZ-A828 + +usb:v054Cp035C* + ID_MODEL_FROM_DATABASE=NWZ-A726/A728/A729 + +usb:v054Cp035F* + ID_MODEL_FROM_DATABASE=UP-DR200 Photo Printer + +usb:v054Cp0382* + ID_MODEL_FROM_DATABASE=Memory Stick PRO-HG Duo Adaptor (MSAC-UAH1) + +usb:v054Cp0385* + ID_MODEL_FROM_DATABASE=Walkman NWZ-E436F + +usb:v054Cp0387* + ID_MODEL_FROM_DATABASE=IC Recorder (P) + +usb:v054Cp03BC* + ID_MODEL_FROM_DATABASE=Webbie HD - MHS-CM1 + +usb:v054Cp03D1* + ID_MODEL_FROM_DATABASE=DPF-X95 + +usb:v054Cp03D3* + ID_MODEL_FROM_DATABASE=DR-BT100CX + +usb:v054Cp03D5* + ID_MODEL_FROM_DATABASE=PlayStation Move motion controller + +usb:v054Cp03FC* + ID_MODEL_FROM_DATABASE=WALKMAN [NWZ-E345] + +usb:v054Cp03FD* + ID_MODEL_FROM_DATABASE=Walkman NWZ-E443 + +usb:v054Cp042F* + ID_MODEL_FROM_DATABASE=PlayStation Move navigation controller + +usb:v054Cp0440* + ID_MODEL_FROM_DATABASE=DSC-H55 + +usb:v054Cp0485* + ID_MODEL_FROM_DATABASE=MHS-PM5 HD camcorder + +usb:v054Cp04CB* + ID_MODEL_FROM_DATABASE=WALKMAN NWZ-E354 + +usb:v054Cp0541* + ID_MODEL_FROM_DATABASE=DSC-HX100V [Cybershot Digital Still Camera] + +usb:v054Cp05C4* + ID_MODEL_FROM_DATABASE=DualShock 4 + +usb:v054Cp0689* + ID_MODEL_FROM_DATABASE=Walkman NWZ-B173F + +usb:v054Cp06BB* + ID_MODEL_FROM_DATABASE=WALKMAN NWZ-F805 + +usb:v054Cp088C* + ID_MODEL_FROM_DATABASE=Portable Headphone Amplifier + +usb:v054Cp1000* + ID_MODEL_FROM_DATABASE=Wireless Buzz! Receiver + +usb:v054D* + ID_VENDOR_FROM_DATABASE=Try Corp. + +usb:v054E* + ID_VENDOR_FROM_DATABASE=Proside Corp. + +usb:v054F* + ID_VENDOR_FROM_DATABASE=WYSE Technology Taiwan + +usb:v0550* + ID_VENDOR_FROM_DATABASE=Fuji Xerox Co., Ltd + +usb:v0550p0002* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0550p0004* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0550p0005* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0550p000B* + ID_MODEL_FROM_DATABASE=Workcentre 24 + +usb:v0550p014E* + ID_MODEL_FROM_DATABASE=CM215b Printer + +usb:v0550p0165* + ID_MODEL_FROM_DATABASE=DocuPrint M215b + +usb:v0551* + ID_VENDOR_FROM_DATABASE=CompuTrend Systems, Inc. + +usb:v0552* + ID_VENDOR_FROM_DATABASE=Philips Monitors + +usb:v0553* + ID_VENDOR_FROM_DATABASE=STMicroelectronics Imaging Division (VLSI Vision) + +usb:v0553p0001* + ID_MODEL_FROM_DATABASE=TerraCAM + +usb:v0553p0002* + ID_MODEL_FROM_DATABASE=CPiA Webcam + +usb:v0553p0100* + ID_MODEL_FROM_DATABASE=STV0672 Camera + +usb:v0553p0140* + ID_MODEL_FROM_DATABASE=Video Camera + +usb:v0553p0150* + ID_MODEL_FROM_DATABASE=CDE CAM 100 + +usb:v0553p0151* + ID_MODEL_FROM_DATABASE=Digital Blue QX5 Microscope + +usb:v0553p0200* + ID_MODEL_FROM_DATABASE=Dual-mode Camera0 + +usb:v0553p0201* + ID_MODEL_FROM_DATABASE=Dual-mode Camera1 + +usb:v0553p0202* + ID_MODEL_FROM_DATABASE=STV0680 Camera + +usb:v0553p0674* + ID_MODEL_FROM_DATABASE=Multi-mode Camera + +usb:v0553p0679* + ID_MODEL_FROM_DATABASE=NMS Video Camera (Webcam) + +usb:v0553p1002* + ID_MODEL_FROM_DATABASE=Che-ez! Splash + +usb:v0554* + ID_VENDOR_FROM_DATABASE=Dictaphone Corp. + +usb:v0555* + ID_VENDOR_FROM_DATABASE=ANAM S&T Co., Ltd + +usb:v0556* + ID_VENDOR_FROM_DATABASE=Asahi Kasei Microsystems Co., Ltd + +usb:v0556p0001* + ID_MODEL_FROM_DATABASE=AK5370 I/F A/D Converter + +usb:v0557* + ID_VENDOR_FROM_DATABASE=ATEN International Co., Ltd + +usb:v0557p2001* + ID_MODEL_FROM_DATABASE=UC-1284 Printer Port + +usb:v0557p2002* + ID_MODEL_FROM_DATABASE=10Mbps Ethernet [klsi] + +usb:v0557p2004* + ID_MODEL_FROM_DATABASE=UC-100KM PS/2 Mouse and Keyboard adapter + +usb:v0557p2006* + ID_MODEL_FROM_DATABASE=UC-1284B Printer Port + +usb:v0557p2007* + ID_MODEL_FROM_DATABASE=UC-110T 100Mbps Ethernet [pegasus] + +usb:v0557p2008* + ID_MODEL_FROM_DATABASE=UC-232A Serial Port [pl2303] + +usb:v0557p2009* + ID_MODEL_FROM_DATABASE=UC-210T Ethernet + +usb:v0557p2011* + ID_MODEL_FROM_DATABASE=UC-2324 4xSerial Ports [mos7840] + +usb:v0557p2202* + ID_MODEL_FROM_DATABASE=CS124U Miniview II KVM Switch + +usb:v0557p2213* + ID_MODEL_FROM_DATABASE=CS682 2-Port USB 2.0 DVI KVM Switch + +usb:v0557p2221* + ID_MODEL_FROM_DATABASE=Winbond Hermon + +usb:v0557p2404* + ID_MODEL_FROM_DATABASE=4-port switch + +usb:v0557p2600* + ID_MODEL_FROM_DATABASE=IDE Bridge + +usb:v0557p2701* + ID_MODEL_FROM_DATABASE=CE700A KVM Extender + +usb:v0557p4000* + ID_MODEL_FROM_DATABASE=DSB-650 10Mbps Ethernet [klsi] + +usb:v0557p7000* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0557p7820* + ID_MODEL_FROM_DATABASE=UC-2322 2xSerial Ports [mos7820] + +usb:v0557p8021* + ID_MODEL_FROM_DATABASE=CS1764A [CubiQ DVI KVMP Switch] + +usb:v0558* + ID_VENDOR_FROM_DATABASE=Truevision, Inc. + +usb:v0558p1009* + ID_MODEL_FROM_DATABASE=GW Instek GDS-1000 Oscilloscope + +usb:v0558p100A* + ID_MODEL_FROM_DATABASE=GW Instek GDS-1000A Oscilloscope + +usb:v0558p2009* + ID_MODEL_FROM_DATABASE=GW Instek GDS-2000 Oscilloscope + +usb:v0559* + ID_VENDOR_FROM_DATABASE=Cadence Design Systems, Inc. + +usb:v055A* + ID_VENDOR_FROM_DATABASE=Kenwood USA + +usb:v055B* + ID_VENDOR_FROM_DATABASE=KnowledgeTek, Inc. + +usb:v055C* + ID_VENDOR_FROM_DATABASE=Proton Electronic Ind. + +usb:v055D* + ID_VENDOR_FROM_DATABASE=Samsung Electro-Mechanics Co. + +usb:v055Dp0001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v055Dp0BB1* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v055Dp1030* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse (OMS3CB/OMGB30) + +usb:v055Dp1031* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse (OMA3CB/OMGI30) + +usb:v055Dp1040* + ID_MODEL_FROM_DATABASE=Mouse HID Device + +usb:v055Dp1050* + ID_MODEL_FROM_DATABASE=E-Mail Optical Wheel Mouse (OMS3CE) + +usb:v055Dp1080* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse (OMS3CH) + +usb:v055Dp2020* + ID_MODEL_FROM_DATABASE=Floppy Disk Drive + +usb:v055Dp6780* + ID_MODEL_FROM_DATABASE=Keyboard V1 + +usb:v055Dp6781* + ID_MODEL_FROM_DATABASE=Keyboard Mouse + +usb:v055Dp8001* + ID_MODEL_FROM_DATABASE=E.M. Hub + +usb:v055Dp9000* + ID_MODEL_FROM_DATABASE=AnyCam [pwc] + +usb:v055Dp9001* + ID_MODEL_FROM_DATABASE=MPC-C30 AnyCam Premium for Notebooks [pwc] + +usb:v055DpA000* + ID_MODEL_FROM_DATABASE=SWL-2100U + +usb:v055DpA010* + ID_MODEL_FROM_DATABASE=WLAN Adapter(SWL-2300) + +usb:v055DpA011* + ID_MODEL_FROM_DATABASE=Boot Device + +usb:v055DpA012* + ID_MODEL_FROM_DATABASE=WLAN Adapter(SWL-2300) + +usb:v055DpA013* + ID_MODEL_FROM_DATABASE=WLAN Adapter(SWL-2350) + +usb:v055DpA230* + ID_MODEL_FROM_DATABASE=Boot Device + +usb:v055DpB000* + ID_MODEL_FROM_DATABASE=11Mbps WLAN Mini Adapter + +usb:v055DpB230* + ID_MODEL_FROM_DATABASE=Netopia 802.11b WLAN Adapter + +usb:v055DpB231* + ID_MODEL_FROM_DATABASE=LG Wireless LAN 11b Adapter + +usb:v055E* + ID_VENDOR_FROM_DATABASE=CTX Opto-Electronics Corp. + +usb:v055F* + ID_VENDOR_FROM_DATABASE=Mustek Systems, Inc. + +usb:v055Fp0001* + ID_MODEL_FROM_DATABASE=ScanExpress 1200 CU + +usb:v055Fp0002* + ID_MODEL_FROM_DATABASE=ScanExpress 600 CU + +usb:v055Fp0003* + ID_MODEL_FROM_DATABASE=ScanExpress 1200 USB + +usb:v055Fp0006* + ID_MODEL_FROM_DATABASE=ScanExpress 1200 UB + +usb:v055Fp0007* + ID_MODEL_FROM_DATABASE=ScanExpress 1200 USB Plus + +usb:v055Fp0008* + ID_MODEL_FROM_DATABASE=ScanExpress 1200 CU Plus + +usb:v055Fp0010* + ID_MODEL_FROM_DATABASE=BearPaw 1200F + +usb:v055Fp0210* + ID_MODEL_FROM_DATABASE=ScanExpress A3 USB + +usb:v055Fp0218* + ID_MODEL_FROM_DATABASE=BearPaw 2400 TA + +usb:v055Fp0219* + ID_MODEL_FROM_DATABASE=BearPaw 2400 TA Plus + +usb:v055Fp021A* + ID_MODEL_FROM_DATABASE=BearPaw 2448 TA Plus + +usb:v055Fp021B* + ID_MODEL_FROM_DATABASE=BearPaw 1200 CU Plus + +usb:v055Fp021C* + ID_MODEL_FROM_DATABASE=BearPaw 1200 CU Plus + +usb:v055Fp021D* + ID_MODEL_FROM_DATABASE=BearPaw 2400 CU Plus + +usb:v055Fp021E* + ID_MODEL_FROM_DATABASE=BearPaw 1200 TA/CS + +usb:v055Fp021F* + ID_MODEL_FROM_DATABASE=SNAPSCAN e22 + +usb:v055Fp0400* + ID_MODEL_FROM_DATABASE=BearPaw 2400 TA Pro + +usb:v055Fp0401* + ID_MODEL_FROM_DATABASE=P 3600 A3 Pro + +usb:v055Fp0408* + ID_MODEL_FROM_DATABASE=BearPaw 2448 CU Pro + +usb:v055Fp0409* + ID_MODEL_FROM_DATABASE=BearPaw 2448 TA Pro + +usb:v055Fp040B* + ID_MODEL_FROM_DATABASE=ScanExpress A3 USB 1200 PRO + +usb:v055Fp0873* + ID_MODEL_FROM_DATABASE=ScanExpress 600 USB + +usb:v055Fp1000* + ID_MODEL_FROM_DATABASE=BearPaw 4800 TA Pro + +usb:v055FpA350* + ID_MODEL_FROM_DATABASE=gSmart 350 Camera + +usb:v055FpA800* + ID_MODEL_FROM_DATABASE=MDC 800 Camera + +usb:v055FpB500* + ID_MODEL_FROM_DATABASE=MDC 3000 Camera + +usb:v055FpC005* + ID_MODEL_FROM_DATABASE=PC CAM 300A + +usb:v055FpC200* + ID_MODEL_FROM_DATABASE=gSmart 300 + +usb:v055FpC211* + ID_MODEL_FROM_DATABASE=Kowa Bs888e Microcamera + +usb:v055FpC220* + ID_MODEL_FROM_DATABASE=gSmart mini + +usb:v055FpC230* + ID_MODEL_FROM_DATABASE=Digicam 330K + +usb:v055FpC232* + ID_MODEL_FROM_DATABASE=MDC3500 Camera + +usb:v055FpC360* + ID_MODEL_FROM_DATABASE=DV 4000 Camera + +usb:v055FpC420* + ID_MODEL_FROM_DATABASE=gSmart mini 2 Camera + +usb:v055FpC430* + ID_MODEL_FROM_DATABASE=gSmart LCD 2 Camera + +usb:v055FpC440* + ID_MODEL_FROM_DATABASE=DV 3000 Camera + +usb:v055FpC520* + ID_MODEL_FROM_DATABASE=gSmart mini 3 Camera + +usb:v055FpC530* + ID_MODEL_FROM_DATABASE=gSmart LCD 2 Camera + +usb:v055FpC540* + ID_MODEL_FROM_DATABASE=gSmart D30 Camera + +usb:v055FpC630* + ID_MODEL_FROM_DATABASE=MDC 4000 Camera + +usb:v055FpC631* + ID_MODEL_FROM_DATABASE=MDC 4000 Camera + +usb:v055FpC650* + ID_MODEL_FROM_DATABASE=MDC 5500Z Camera + +usb:v055FpD001* + ID_MODEL_FROM_DATABASE=WCam 300 + +usb:v055FpD003* + ID_MODEL_FROM_DATABASE=WCam 300A + +usb:v055FpD004* + ID_MODEL_FROM_DATABASE=WCam 300AN + +usb:v0560* + ID_VENDOR_FROM_DATABASE=Interface Corp. + +usb:v0561* + ID_VENDOR_FROM_DATABASE=Oasis Design, Inc. + +usb:v0562* + ID_VENDOR_FROM_DATABASE=Telex Communications, Inc. + +usb:v0562p0001* + ID_MODEL_FROM_DATABASE=Enhanced Microphone + +usb:v0562p0002* + ID_MODEL_FROM_DATABASE=Telex Microphone + +usb:v0563* + ID_VENDOR_FROM_DATABASE=Immersion Corp. + +usb:v0564* + ID_VENDOR_FROM_DATABASE=Kodak Digital Product Center, Japan Ltd. (formerly Chinon Industries Inc.) + +usb:v0565* + ID_VENDOR_FROM_DATABASE=Peracom Networks, Inc. + +usb:v0565p0001* + ID_MODEL_FROM_DATABASE=Serial Port [etek] + +usb:v0565p0002* + ID_MODEL_FROM_DATABASE=Enet Ethernet [klsi] + +usb:v0565p0003* + ID_MODEL_FROM_DATABASE=@Home Networks Ethernet [klsi] + +usb:v0565p0005* + ID_MODEL_FROM_DATABASE=Enet2 Ethernet [klsi] + +usb:v0565p0041* + ID_MODEL_FROM_DATABASE=Peracom Remote NDIS Ethernet Adapter + +usb:v0566* + ID_VENDOR_FROM_DATABASE=Monterey International Corp. + +usb:v0566p0110* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1001* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1002* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1003* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1004* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1005* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1006* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p1007* + ID_MODEL_FROM_DATABASE=ViewMate Desktop Mouse CC2201 + +usb:v0566p2800* + ID_MODEL_FROM_DATABASE=MIC K/B + +usb:v0566p2801* + ID_MODEL_FROM_DATABASE=MIC K/B Mouse + +usb:v0566p2802* + ID_MODEL_FROM_DATABASE=Kbd Hub + +usb:v0566p3002* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0566p3004* + ID_MODEL_FROM_DATABASE=Genius KB-29E + +usb:v0566p3107* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0567* + ID_VENDOR_FROM_DATABASE=Xyratex International, Ltd + +usb:v0568* + ID_VENDOR_FROM_DATABASE=Quartz Ingenierie + +usb:v0569* + ID_VENDOR_FROM_DATABASE=SegaSoft + +usb:v056A* + ID_VENDOR_FROM_DATABASE=Wacom Co., Ltd + +usb:v056Ap0000* + ID_MODEL_FROM_DATABASE=PenPartner + +usb:v056Ap0001* + ID_MODEL_FROM_DATABASE=PenPartner 4x5 + +usb:v056Ap0002* + ID_MODEL_FROM_DATABASE=PenPartner 6x8 + +usb:v056Ap0003* + ID_MODEL_FROM_DATABASE=PTU-600 [Cintiq Partner] + +usb:v056Ap0010* + ID_MODEL_FROM_DATABASE=ET-0405 [Graphire] + +usb:v056Ap0011* + ID_MODEL_FROM_DATABASE=ET-0405A [Graphire2 (4x5)] + +usb:v056Ap0012* + ID_MODEL_FROM_DATABASE=ET-0507A [Graphire2 (5x7)] + +usb:v056Ap0013* + ID_MODEL_FROM_DATABASE=CTE-430 [Graphire3 (4x5)] + +usb:v056Ap0014* + ID_MODEL_FROM_DATABASE=CTE-630 [Graphire3 (6x8)] + +usb:v056Ap0015* + ID_MODEL_FROM_DATABASE=CTE-440 [Graphire4 (4x5)] + +usb:v056Ap0016* + ID_MODEL_FROM_DATABASE=CTE-640 [Graphire4 (6x8)] + +usb:v056Ap0017* + ID_MODEL_FROM_DATABASE=CTE-450 [Bamboo Fun (small)] + +usb:v056Ap0018* + ID_MODEL_FROM_DATABASE=CTE-650 [Bamboo Fun (medium)] + +usb:v056Ap0019* + ID_MODEL_FROM_DATABASE=CTE-631 [Bamboo One] + +usb:v056Ap0020* + ID_MODEL_FROM_DATABASE=GD-0405 [Intuos (4x5)] + +usb:v056Ap0021* + ID_MODEL_FROM_DATABASE=GD-0608 [Intuos (6x8)] + +usb:v056Ap0022* + ID_MODEL_FROM_DATABASE=GD-0912 [Intuos (9x12)] + +usb:v056Ap0023* + ID_MODEL_FROM_DATABASE=GD-1212 [Intuos (12x12)] + +usb:v056Ap0024* + ID_MODEL_FROM_DATABASE=GD-1218 [Intuos (12x18)] + +usb:v056Ap0026* + ID_MODEL_FROM_DATABASE=PTH-450 [Intuos5 touch (S)] + +usb:v056Ap0027* + ID_MODEL_FROM_DATABASE=PTH-650 [Intuos5 touch (M)] + +usb:v056Ap0028* + ID_MODEL_FROM_DATABASE=PTH-850 [Intuos5 touch (L)] + +usb:v056Ap0029* + ID_MODEL_FROM_DATABASE=PTK-450 [Intuos5 (S)] + +usb:v056Ap002A* + ID_MODEL_FROM_DATABASE=PTK-650 [Intuos5 (M)] + +usb:v056Ap0030* + ID_MODEL_FROM_DATABASE=PL400 + +usb:v056Ap0031* + ID_MODEL_FROM_DATABASE=PL500 + +usb:v056Ap0032* + ID_MODEL_FROM_DATABASE=PL600 + +usb:v056Ap0033* + ID_MODEL_FROM_DATABASE=PL600SX + +usb:v056Ap0034* + ID_MODEL_FROM_DATABASE=PL550 + +usb:v056Ap0035* + ID_MODEL_FROM_DATABASE=PL800 + +usb:v056Ap0037* + ID_MODEL_FROM_DATABASE=PL700 + +usb:v056Ap0038* + ID_MODEL_FROM_DATABASE=PL510 + +usb:v056Ap0039* + ID_MODEL_FROM_DATABASE=DTU-710 + +usb:v056Ap003F* + ID_MODEL_FROM_DATABASE=DTZ-2100 [Cintiq 21UX] + +usb:v056Ap0041* + ID_MODEL_FROM_DATABASE=XD-0405-U [Intuos2 (4x5)] + +usb:v056Ap0042* + ID_MODEL_FROM_DATABASE=XD-0608-U [Intuos2 (6x8)] + +usb:v056Ap0043* + ID_MODEL_FROM_DATABASE=XD-0912-U [Intuos2 (9x12)] + +usb:v056Ap0044* + ID_MODEL_FROM_DATABASE=XD-1212-U [Intuos2 (12x12)] + +usb:v056Ap0045* + ID_MODEL_FROM_DATABASE=XD-1218-U [Intuos2 (12x18)] + +usb:v056Ap0047* + ID_MODEL_FROM_DATABASE=Intuos2 6x8 + +usb:v056Ap0057* + ID_MODEL_FROM_DATABASE=DTK-2241 + +usb:v056Ap0059* + ID_MODEL_FROM_DATABASE=DTH-2242 tablet + +usb:v056Ap005B* + ID_MODEL_FROM_DATABASE=DTH-2200 [Cintiq 22HD Touch] tablet + +usb:v056Ap005D* + ID_MODEL_FROM_DATABASE=DTH-2242 touchscreen + +usb:v056Ap005E* + ID_MODEL_FROM_DATABASE=DTH-2200 [Cintiq 22HD Touch] touchscreen + +usb:v056Ap0060* + ID_MODEL_FROM_DATABASE=FT-0405 [Volito, PenPartner, PenStation (4x5)] + +usb:v056Ap0061* + ID_MODEL_FROM_DATABASE=FT-0203 [Volito, PenPartner, PenStation (2x3)] + +usb:v056Ap0062* + ID_MODEL_FROM_DATABASE=CTF-420 [Volito2] + +usb:v056Ap0063* + ID_MODEL_FROM_DATABASE=CTF-220 [BizTablet] + +usb:v056Ap0064* + ID_MODEL_FROM_DATABASE=CTF-221 [PenPartner2] + +usb:v056Ap0065* + ID_MODEL_FROM_DATABASE=MTE-450 [Bamboo] + +usb:v056Ap0069* + ID_MODEL_FROM_DATABASE=CTF-430 [Bamboo One] + +usb:v056Ap006A* + ID_MODEL_FROM_DATABASE=CTE-460 [Bamboo One Pen (S)] + +usb:v056Ap006B* + ID_MODEL_FROM_DATABASE=CTE-660 [Bamboo One Pen (M)] + +usb:v056Ap0081* + ID_MODEL_FROM_DATABASE=CTE-630BT [Graphire Wireless (6x8)] + +usb:v056Ap0084* + ID_MODEL_FROM_DATABASE=Wireless adapter for Bamboo tablets + +usb:v056Ap0090* + ID_MODEL_FROM_DATABASE=TPC90 + +usb:v056Ap0093* + ID_MODEL_FROM_DATABASE=TPC93 + +usb:v056Ap0097* + ID_MODEL_FROM_DATABASE=TPC97 + +usb:v056Ap009A* + ID_MODEL_FROM_DATABASE=TPC9A + +usb:v056Ap00B0* + ID_MODEL_FROM_DATABASE=PTZ-430 [Intuos3 (4x5)] + +usb:v056Ap00B1* + ID_MODEL_FROM_DATABASE=PTZ-630 [Intuos3 (6x8)] + +usb:v056Ap00B2* + ID_MODEL_FROM_DATABASE=PTZ-930 [Intuos3 (9x12)] + +usb:v056Ap00B3* + ID_MODEL_FROM_DATABASE=PTZ-1230 [Intuos3 (12x12)] + +usb:v056Ap00B4* + ID_MODEL_FROM_DATABASE=PTZ-1231W [Intuos3 (12x19)] + +usb:v056Ap00B5* + ID_MODEL_FROM_DATABASE=PTZ-631W [Intuos3 (6x11)] + +usb:v056Ap00B7* + ID_MODEL_FROM_DATABASE=PTZ-431W [Intuos3 (4x6)] + +usb:v056Ap00B8* + ID_MODEL_FROM_DATABASE=PTK-440 [Intuos4 (4x6)] + +usb:v056Ap00B9* + ID_MODEL_FROM_DATABASE=PTK-640 [Intuos4 (6x9)] + +usb:v056Ap00BA* + ID_MODEL_FROM_DATABASE=PTK-840 [Intuos4 (8x13)] + +usb:v056Ap00BB* + ID_MODEL_FROM_DATABASE=PTK-1240 [Intuos4 (12x19)] + +usb:v056Ap00C0* + ID_MODEL_FROM_DATABASE=DTF-521 + +usb:v056Ap00C4* + ID_MODEL_FROM_DATABASE=DTF-720 + +usb:v056Ap00C5* + ID_MODEL_FROM_DATABASE=DTZ-2000W [Cintiq 20WSX] + +usb:v056Ap00C6* + ID_MODEL_FROM_DATABASE=DTZ-1200W [Cintiq 12WX] + +usb:v056Ap00C7* + ID_MODEL_FROM_DATABASE=DTU-1931 + +usb:v056Ap00CC* + ID_MODEL_FROM_DATABASE=DTK-2100 [Cintiq 21UX] + +usb:v056Ap00CE* + ID_MODEL_FROM_DATABASE=DTU-2231 + +usb:v056Ap00D0* + ID_MODEL_FROM_DATABASE=CTT-460 [Bamboo Touch] + +usb:v056Ap00D1* + ID_MODEL_FROM_DATABASE=CTH-460 [Bamboo Pen & Touch] + +usb:v056Ap00D2* + ID_MODEL_FROM_DATABASE=CTH-461 [Bamboo Fun/Craft/Comic Pen & Touch (S)] + +usb:v056Ap00D3* + ID_MODEL_FROM_DATABASE=CTH-661 [Bamboo Fun/Comic Pen & Touch (M)] + +usb:v056Ap00D4* + ID_MODEL_FROM_DATABASE=CTL-460 [Bamboo Pen (S)] + +usb:v056Ap00D5* + ID_MODEL_FROM_DATABASE=CTL-660 [Bamboo Pen (M)] + +usb:v056Ap00D6* + ID_MODEL_FROM_DATABASE=CTH-460 [Bamboo Pen & Touch] + +usb:v056Ap00D7* + ID_MODEL_FROM_DATABASE=CTH-461 [Bamboo Fun/Craft/Comic Pen & Touch (S)] + +usb:v056Ap00D8* + ID_MODEL_FROM_DATABASE=CTH-661 [Bamboo Fun/Comic Pen & Touch (M)] + +usb:v056Ap00D9* + ID_MODEL_FROM_DATABASE=CTT-460 [Bamboo Touch] + +usb:v056Ap00DA* + ID_MODEL_FROM_DATABASE=CTH-461SE [Bamboo Pen & Touch Special Edition (S)] + +usb:v056Ap00DB* + ID_MODEL_FROM_DATABASE=CTH-661SE [Bamboo Pen & Touch Special Edition (M)] + +usb:v056Ap00DC* + ID_MODEL_FROM_DATABASE=CTT-470 [Bamboo Touch] + +usb:v056Ap00DD* + ID_MODEL_FROM_DATABASE=CTL-470 [Bamboo Connect] + +usb:v056Ap00DE* + ID_MODEL_FROM_DATABASE=CTH-470 [Bamboo Fun Pen & Touch] + +usb:v056Ap00DF* + ID_MODEL_FROM_DATABASE=CTH-670 [Bamboo Create/Fun] + +usb:v056Ap00E2* + ID_MODEL_FROM_DATABASE=TPCE2 + +usb:v056Ap00E3* + ID_MODEL_FROM_DATABASE=TPCE3 + +usb:v056Ap00E5* + ID_MODEL_FROM_DATABASE=TPCE5 + +usb:v056Ap00E6* + ID_MODEL_FROM_DATABASE=TPCE6 + +usb:v056Ap00EC* + ID_MODEL_FROM_DATABASE=TPCEC + +usb:v056Ap00ED* + ID_MODEL_FROM_DATABASE=TPCED + +usb:v056Ap00EF* + ID_MODEL_FROM_DATABASE=TPCEF + +usb:v056Ap00F4* + ID_MODEL_FROM_DATABASE=DTK-2400 [Cintiq 24HD] tablet + +usb:v056Ap00F6* + ID_MODEL_FROM_DATABASE=DTH-2400 [Cintiq 24HD touch] touchscreen + +usb:v056Ap00F8* + ID_MODEL_FROM_DATABASE=DTH-2400 [Cintiq 24HD touch] tablet + +usb:v056Ap00FA* + ID_MODEL_FROM_DATABASE=DTK-2200 [Cintiq 22HD] tablet + +usb:v056Ap00FB* + ID_MODEL_FROM_DATABASE=DTU-1031 + +usb:v056Ap0100* + ID_MODEL_FROM_DATABASE=TPC100 + +usb:v056Ap0101* + ID_MODEL_FROM_DATABASE=TPC101 + +usb:v056Ap010D* + ID_MODEL_FROM_DATABASE=TPC10D + +usb:v056Ap010E* + ID_MODEL_FROM_DATABASE=TPC10E + +usb:v056Ap010F* + ID_MODEL_FROM_DATABASE=TPC10F + +usb:v056Ap0116* + ID_MODEL_FROM_DATABASE=TPC116 + +usb:v056Ap012C* + ID_MODEL_FROM_DATABASE=TPC12C + +usb:v056Ap0221* + ID_MODEL_FROM_DATABASE=MDP-123 [Inkling] + +usb:v056Ap0300* + ID_MODEL_FROM_DATABASE=CTL-471 [Bamboo Splash, One by Wacom (S)] + +usb:v056Ap0301* + ID_MODEL_FROM_DATABASE=CTL-671 [One by Wacom (M)] + +usb:v056Ap0302* + ID_MODEL_FROM_DATABASE=CTH-480 [Intuos Pen & Touch (S)] + +usb:v056Ap0303* + ID_MODEL_FROM_DATABASE=CTH-680 [Intuos Pen & Touch (M)] + +usb:v056Ap0304* + ID_MODEL_FROM_DATABASE=DTK-1300 [Cintiq 13HD] + +usb:v056Ap0307* + ID_MODEL_FROM_DATABASE=DTH-A1300 [Cintiq Companion Hybrid] tablet + +usb:v056Ap0309* + ID_MODEL_FROM_DATABASE=DTH-A1300 [Cintiq Companion Hybrid] touchscreen + +usb:v056Ap030E* + ID_MODEL_FROM_DATABASE=CTL-480 [Intuos Pen (S)] + +usb:v056Ap0314* + ID_MODEL_FROM_DATABASE=PTH-451 [Intuos pro (S)] + +usb:v056Ap0315* + ID_MODEL_FROM_DATABASE=PTH-651 [Intuos pro (M)] + +usb:v056Ap0317* + ID_MODEL_FROM_DATABASE=PTH-851 [Intuos pro (L)] + +usb:v056Ap0318* + ID_MODEL_FROM_DATABASE=CTH-301 [Bamboo] + +usb:v056Ap032F* + ID_MODEL_FROM_DATABASE=DTU-1031X + +usb:v056Ap0400* + ID_MODEL_FROM_DATABASE=PenPartner 4x5 + +usb:v056Ap4001* + ID_MODEL_FROM_DATABASE=TPC4001 + +usb:v056Ap4004* + ID_MODEL_FROM_DATABASE=TPC4004 + +usb:v056Ap4850* + ID_MODEL_FROM_DATABASE=PenPartner 6x8 + +usb:v056Ap5000* + ID_MODEL_FROM_DATABASE=TPC5000 + +usb:v056Ap5002* + ID_MODEL_FROM_DATABASE=TPC5002 + +usb:v056Ap5010* + ID_MODEL_FROM_DATABASE=TPC5010 + +usb:v056B* + ID_VENDOR_FROM_DATABASE=Decicon, Inc. + +usb:v056C* + ID_VENDOR_FROM_DATABASE=eTEK Labs + +usb:v056Cp0006* + ID_MODEL_FROM_DATABASE=KwikLink Host-Host Connector + +usb:v056Cp8007* + ID_MODEL_FROM_DATABASE=Kwik232 Serial Port + +usb:v056Cp8100* + ID_MODEL_FROM_DATABASE=KwikLink Host-Host Connector + +usb:v056Cp8101* + ID_MODEL_FROM_DATABASE=KwikLink USB-USB Bridge + +usb:v056D* + ID_VENDOR_FROM_DATABASE=EIZO Corp. + +usb:v056Dp0000* + ID_MODEL_FROM_DATABASE=Hub + +usb:v056Dp0001* + ID_MODEL_FROM_DATABASE=Monitor + +usb:v056Dp0002* + ID_MODEL_FROM_DATABASE=HID Monitor Controls + +usb:v056Dp0003* + ID_MODEL_FROM_DATABASE=Device Bay Controller + +usb:v056E* + ID_VENDOR_FROM_DATABASE=Elecom Co., Ltd + +usb:v056Ep0002* + ID_MODEL_FROM_DATABASE=29UO Mouse + +usb:v056Ep0072* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v056Ep200C* + ID_MODEL_FROM_DATABASE=LD-USB/TX + +usb:v056Ep4002* + ID_MODEL_FROM_DATABASE=Laneed 100Mbps Ethernet LD-USB/TX [pegasus] + +usb:v056Ep4005* + ID_MODEL_FROM_DATABASE=LD-USBL/TX + +usb:v056Ep400B* + ID_MODEL_FROM_DATABASE=LD-USB/TX + +usb:v056Ep4010* + ID_MODEL_FROM_DATABASE=LD-USB20 + +usb:v056Ep5003* + ID_MODEL_FROM_DATABASE=UC-SGT + +usb:v056Ep5004* + ID_MODEL_FROM_DATABASE=UC-SGT + +usb:v056Ep6008* + ID_MODEL_FROM_DATABASE=Flash Disk + +usb:v056EpABC1* + ID_MODEL_FROM_DATABASE=LD-USB/TX + +usb:v056F* + ID_VENDOR_FROM_DATABASE=Korea Data Systems Co., Ltd + +usb:v056FpCD00* + ID_MODEL_FROM_DATABASE=CDM-751 CD organizer + +usb:v0570* + ID_VENDOR_FROM_DATABASE=Epson America + +usb:v0571* + ID_VENDOR_FROM_DATABASE=Interex, Inc. + +usb:v0571p0002* + ID_MODEL_FROM_DATABASE=echoFX InterView Lite + +usb:v0572* + ID_VENDOR_FROM_DATABASE=Conexant Systems (Rockwell), Inc. + +usb:v0572p0001* + ID_MODEL_FROM_DATABASE=Ezcam II Webcam + +usb:v0572p0002* + ID_MODEL_FROM_DATABASE=Ezcam II Webcam + +usb:v0572p0040* + ID_MODEL_FROM_DATABASE=Wondereye CP-115 Webcam + +usb:v0572p0041* + ID_MODEL_FROM_DATABASE=Webcam Notebook + +usb:v0572p0042* + ID_MODEL_FROM_DATABASE=Webcam Notebook + +usb:v0572p0320* + ID_MODEL_FROM_DATABASE=DVBSky T330 DVB-T2/C tuner + +usb:v0572p1232* + ID_MODEL_FROM_DATABASE=V.90 modem + +usb:v0572p1234* + ID_MODEL_FROM_DATABASE=Typhoon Redfun Modem V90 56k + +usb:v0572p1252* + ID_MODEL_FROM_DATABASE=HCF V90 Data Fax Voice Modem + +usb:v0572p1253* + ID_MODEL_FROM_DATABASE=Zoom V.92 Faxmodem + +usb:v0572p1300* + ID_MODEL_FROM_DATABASE=SoftK56 Data Fax Voice CARP + +usb:v0572p1301* + ID_MODEL_FROM_DATABASE=Modem Enumerator + +usb:v0572p1328* + ID_MODEL_FROM_DATABASE=TrendNet TFM-561 modem + +usb:v0572p2000* + ID_MODEL_FROM_DATABASE=SoftGate 802.11 Adapter + +usb:v0572p2002* + ID_MODEL_FROM_DATABASE=SoftGate 802.11 Adapter + +usb:v0572p262A* + ID_MODEL_FROM_DATABASE=tm5600 Video & Audio Grabber Capture + +usb:v0572p680C* + ID_MODEL_FROM_DATABASE=DVBSky T680C DVB-T2/C tuner + +usb:v0572p6831* + ID_MODEL_FROM_DATABASE=DVBSky S960 DVB-S2 tuner + +usb:v0572p8390* + ID_MODEL_FROM_DATABASE=WinFast PalmTop/Novo TV Video + +usb:v0572p8392* + ID_MODEL_FROM_DATABASE=WinFast PalmTop/Novo TV Video + +usb:v0572p960C* + ID_MODEL_FROM_DATABASE=DVBSky S960C DVB-S2 tuner + +usb:v0572pC686* + ID_MODEL_FROM_DATABASE=Geniatech T220A DVB-T2 TV Stick + +usb:v0572pC688* + ID_MODEL_FROM_DATABASE=Geniatech T230 DVB-T2 TV Stick + +usb:v0572pCAFC* + ID_MODEL_FROM_DATABASE=CX861xx ROM Boot Loader + +usb:v0572pCAFD* + ID_MODEL_FROM_DATABASE=CX82310 ROM Boot Loader + +usb:v0572pCAFE* + ID_MODEL_FROM_DATABASE=AccessRunner ADSL Modem + +usb:v0572pCB00* + ID_MODEL_FROM_DATABASE=ADSL Modem + +usb:v0572pCB01* + ID_MODEL_FROM_DATABASE=ADSL Modem + +usb:v0572pCB06* + ID_MODEL_FROM_DATABASE=StarModem Network Interface + +usb:v0573* + ID_VENDOR_FROM_DATABASE=Zoran Co. Personal Media Division (Nogatech) + +usb:v0573p0003* + ID_MODEL_FROM_DATABASE=USBGear USBG-V1 + +usb:v0573p0400* + ID_MODEL_FROM_DATABASE=D-Link V100 + +usb:v0573p0600* + ID_MODEL_FROM_DATABASE=Dazzle USBVision (1006) + +usb:v0573p1300* + ID_MODEL_FROM_DATABASE=leadtek USBVision (1006) + +usb:v0573p2000* + ID_MODEL_FROM_DATABASE=X10 va10a Wireless Camera + +usb:v0573p2001* + ID_MODEL_FROM_DATABASE=Dazzle EmMe (2001) + +usb:v0573p2101* + ID_MODEL_FROM_DATABASE=Zoran Co. PMD (Nogatech) AV-grabber Manhattan + +usb:v0573p2D00* + ID_MODEL_FROM_DATABASE=Osprey 50 + +usb:v0573p2D01* + ID_MODEL_FROM_DATABASE=Hauppauge USB-Live Model 600 + +usb:v0573p3000* + ID_MODEL_FROM_DATABASE=Dazzle MicroCam (NTSC) + +usb:v0573p3001* + ID_MODEL_FROM_DATABASE=Dazzle MicroCam (PAL) + +usb:v0573p4000* + ID_MODEL_FROM_DATABASE=Nogatech TV! (NTSC) + +usb:v0573p4001* + ID_MODEL_FROM_DATABASE=Nogatech TV! (PAL) + +usb:v0573p4002* + ID_MODEL_FROM_DATABASE=Nogatech TV! (PAL-I-) + +usb:v0573p4003* + ID_MODEL_FROM_DATABASE=Nogatech TV! (MF-) + +usb:v0573p4008* + ID_MODEL_FROM_DATABASE=Nogatech TV! (NTSC) (T) + +usb:v0573p4009* + ID_MODEL_FROM_DATABASE=Nogatech TV! (PAL) (T) + +usb:v0573p4010* + ID_MODEL_FROM_DATABASE=Nogatech TV! (NTSC) (A) + +usb:v0573p4100* + ID_MODEL_FROM_DATABASE=USB-TV FM (NTSC) + +usb:v0573p4110* + ID_MODEL_FROM_DATABASE=PNY USB-TV (NTSC) FM + +usb:v0573p4400* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (NTSC) + +usb:v0573p4401* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (PAL) + +usb:v0573p4450* + ID_MODEL_FROM_DATABASE=PixelView PlayTv-USB PRO (PAL) FM + +usb:v0573p4451* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (PAL+) + +usb:v0573p4452* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (PAL-I+) + +usb:v0573p4500* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (NTSC) + +usb:v0573p4501* + ID_MODEL_FROM_DATABASE=Nogatech TV! Pro (PAL) + +usb:v0573p4550* + ID_MODEL_FROM_DATABASE=ZTV ZT-721 2.4GHz A/V Receiver + +usb:v0573p4551* + ID_MODEL_FROM_DATABASE=Dazzle TV! Pro Audio (P+) + +usb:v0573p4D00* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB USA + +usb:v0573p4D01* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB + +usb:v0573p4D02* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB UK + +usb:v0573p4D03* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB France + +usb:v0573p4D04* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV (PAL D/K) + +usb:v0573p4D10* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB with FM USA radio + +usb:v0573p4D11* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB (PAL) with FM radio + +usb:v0573p4D12* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB UK with FM Radio + +usb:v0573p4D14* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV (PAL D/K FM) + +usb:v0573p4D20* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB II (PAL) with FM radio + +usb:v0573p4D21* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB II (PAL) + +usb:v0573p4D22* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB II (PAL) Model 566 + +usb:v0573p4D23* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB France 4D23 + +usb:v0573p4D24* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV Pro (PAL D/K) + +usb:v0573p4D25* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40209 rev B234 + +usb:v0573p4D26* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40209 rev B243 + +usb:v0573p4D27* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40204 Rev B281 + +usb:v0573p4D28* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40204 rev B283 + +usb:v0573p4D29* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40205 rev B298 + +usb:v0573p4D2A* + ID_MODEL_FROM_DATABASE=Hauppague WinTV-USB Model 602 Rev B285 + +usb:v0573p4D2B* + ID_MODEL_FROM_DATABASE=Hauppague WinTV-USB Model 602 Rev B282 + +usb:v0573p4D2C* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV Pro (PAL/SECAM) + +usb:v0573p4D30* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB FM Model 40211 Rev B123 + +usb:v0573p4D31* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB III (PAL) with FM radio Model 568 + +usb:v0573p4D32* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB III (PAL) FM Model 573 + +usb:v0573p4D34* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV Pro (PAL D/K FM) + +usb:v0573p4D35* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB III (PAL) FM Model 597 + +usb:v0573p4D36* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV Pro (PAL B/G FM) + +usb:v0573p4D37* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV-USB Model 40219 rev E189 + +usb:v0573p4D38* + ID_MODEL_FROM_DATABASE=Hauppauge WinTV Pro (NTSC FM) + +usb:v0574* + ID_VENDOR_FROM_DATABASE=City University of Hong Kong + +usb:v0575* + ID_VENDOR_FROM_DATABASE=Philips Creative Display Solutions + +usb:v0576* + ID_VENDOR_FROM_DATABASE=BAFO/Quality Computer Accessories + +usb:v0577* + ID_VENDOR_FROM_DATABASE=ELSA + +usb:v0578* + ID_VENDOR_FROM_DATABASE=Intrinsix Corp. + +usb:v0579* + ID_VENDOR_FROM_DATABASE=GVC Corp. + +usb:v057A* + ID_VENDOR_FROM_DATABASE=Samsung Electronics America + +usb:v057B* + ID_VENDOR_FROM_DATABASE=Y-E Data, Inc. + +usb:v057Bp0000* + ID_MODEL_FROM_DATABASE=FlashBuster-U Floppy + +usb:v057Bp0001* + ID_MODEL_FROM_DATABASE=Tri-Media Reader Floppy + +usb:v057Bp0006* + ID_MODEL_FROM_DATABASE=Tri-Media Reader Card Reader + +usb:v057Bp0010* + ID_MODEL_FROM_DATABASE=Memory Stick Reader Writer + +usb:v057Bp0020* + ID_MODEL_FROM_DATABASE=HEXA Media Drive 6-in-1 Card Reader Writer + +usb:v057Bp0030* + ID_MODEL_FROM_DATABASE=Memory Card Viewer (TV) + +usb:v057C* + ID_VENDOR_FROM_DATABASE=AVM GmbH + +usb:v057Cp0B00* + ID_MODEL_FROM_DATABASE=ISDN-Controller B1 Family + +usb:v057Cp0C00* + ID_MODEL_FROM_DATABASE=ISDN-Controller FRITZ!Card + +usb:v057Cp1000* + ID_MODEL_FROM_DATABASE=ISDN-Controller FRITZ!Card v2.0 + +usb:v057Cp1900* + ID_MODEL_FROM_DATABASE=ISDN-Controller FRITZ!Card v2.1 + +usb:v057Cp2000* + ID_MODEL_FROM_DATABASE=ISDN-Connector FRITZ!X + +usb:v057Cp2200* + ID_MODEL_FROM_DATABASE=BlueFRITZ! + +usb:v057Cp2300* + ID_MODEL_FROM_DATABASE=Teledat X130 DSL + +usb:v057Cp2800* + ID_MODEL_FROM_DATABASE=ISDN-Connector TA + +usb:v057Cp3200* + ID_MODEL_FROM_DATABASE=Teledat X130 DSL + +usb:v057Cp3500* + ID_MODEL_FROM_DATABASE=FRITZ!Card DSL SL + +usb:v057Cp3701* + ID_MODEL_FROM_DATABASE=FRITZ!Box SL + +usb:v057Cp3702* + ID_MODEL_FROM_DATABASE=FRITZ!Box + +usb:v057Cp3800* + ID_MODEL_FROM_DATABASE=BlueFRITZ! Bluetooth Stick + +usb:v057Cp3A00* + ID_MODEL_FROM_DATABASE=FRITZ!Box Fon + +usb:v057Cp3C00* + ID_MODEL_FROM_DATABASE=FRITZ!Box WLAN + +usb:v057Cp3D00* + ID_MODEL_FROM_DATABASE=Fritz!Box + +usb:v057Cp3E01* + ID_MODEL_FROM_DATABASE=FRITZ!Box (Annex A) + +usb:v057Cp4001* + ID_MODEL_FROM_DATABASE=FRITZ!Box Fon (Annex A) + +usb:v057Cp4101* + ID_MODEL_FROM_DATABASE=FRITZ!Box WLAN (Annex A) + +usb:v057Cp4201* + ID_MODEL_FROM_DATABASE=FRITZ!Box Fon WLAN (Annex A) + +usb:v057Cp4601* + ID_MODEL_FROM_DATABASE=Eumex 5520PC (WinXP/2000) + +usb:v057Cp4602* + ID_MODEL_FROM_DATABASE=Eumex 400 (WinXP/2000) + +usb:v057Cp4701* + ID_MODEL_FROM_DATABASE=AVM FRITZ!Box Fon ata + +usb:v057Cp5401* + ID_MODEL_FROM_DATABASE=Eumex 300 IP + +usb:v057Cp5601* + ID_MODEL_FROM_DATABASE=AVM Fritz!WLAN [Texas Instruments TNETW1450] + +usb:v057Cp6201* + ID_MODEL_FROM_DATABASE=AVM Fritz!WLAN v1.1 [Texas Instruments TNETW1450] + +usb:v057Cp62FF* + ID_MODEL_FROM_DATABASE=AVM Fritz!WLAN USB (in CD-ROM-mode) + +usb:v057Cp8401* + ID_MODEL_FROM_DATABASE=Fritz!WLAN N [Atheros AR9001U] + +usb:v057Cp8402* + ID_MODEL_FROM_DATABASE=Fritz!WLAN N 2.4 [Atheros AR9001U] + +usb:v057Cp8403* + ID_MODEL_FROM_DATABASE=Fritz!WLAN N v2 [Atheros AR9271] + +usb:v057Cp84FF* + ID_MODEL_FROM_DATABASE=AVM Fritz!WLAN USB N (in CD-ROM-mode) + +usb:v057Cp8501* + ID_MODEL_FROM_DATABASE=FRITZ WLAN N v2 [RT5572/rt2870.bin] + +usb:v057D* + ID_VENDOR_FROM_DATABASE=Shark Multimedia, Inc. + +usb:v057E* + ID_VENDOR_FROM_DATABASE=Nintendo Co., Ltd + +usb:v057Ep0305* + ID_MODEL_FROM_DATABASE=Broadcom BCM2045A Bluetooth Radio [Nintendo Wii] + +usb:v057Ep0306* + ID_MODEL_FROM_DATABASE=Wii Remote Controller RVL-003 + +usb:v057F* + ID_VENDOR_FROM_DATABASE=QuickShot, Ltd + +usb:v057Fp6238* + ID_MODEL_FROM_DATABASE=USB StrikePad + +usb:v0580* + ID_VENDOR_FROM_DATABASE=Denron, Inc. + +usb:v0581* + ID_VENDOR_FROM_DATABASE=Racal Data Group + +usb:v0582* + ID_VENDOR_FROM_DATABASE=Roland Corp. + +usb:v0582p0000* + ID_MODEL_FROM_DATABASE=UA-100(G) + +usb:v0582p0002* + ID_MODEL_FROM_DATABASE=UM-4/MPU-64 MIDI Interface + +usb:v0582p0003* + ID_MODEL_FROM_DATABASE=SoundCanvas SC-8850 + +usb:v0582p0004* + ID_MODEL_FROM_DATABASE=U-8 + +usb:v0582p0005* + ID_MODEL_FROM_DATABASE=UM-2(C/EX) + +usb:v0582p0007* + ID_MODEL_FROM_DATABASE=SoundCanvas SC-8820 + +usb:v0582p0008* + ID_MODEL_FROM_DATABASE=PC-300 + +usb:v0582p0009* + ID_MODEL_FROM_DATABASE=UM-1(E/S/X) + +usb:v0582p000B* + ID_MODEL_FROM_DATABASE=SK-500 + +usb:v0582p000C* + ID_MODEL_FROM_DATABASE=SC-D70 + +usb:v0582p0010* + ID_MODEL_FROM_DATABASE=EDIROL UA-5 + +usb:v0582p0011* + ID_MODEL_FROM_DATABASE=Edirol UA-5 Sound Capture + +usb:v0582p0012* + ID_MODEL_FROM_DATABASE=XV-5050 + +usb:v0582p0013* + ID_MODEL_FROM_DATABASE=XV-5050 + +usb:v0582p0014* + ID_MODEL_FROM_DATABASE=EDIROL UM-880 MIDI I/F (native) + +usb:v0582p0015* + ID_MODEL_FROM_DATABASE=EDIROL UM-880 MIDI I/F (generic) + +usb:v0582p0016* + ID_MODEL_FROM_DATABASE=EDIROL SD-90 + +usb:v0582p0017* + ID_MODEL_FROM_DATABASE=EDIROL SD-90 + +usb:v0582p0018* + ID_MODEL_FROM_DATABASE=UA-1A + +usb:v0582p001B* + ID_MODEL_FROM_DATABASE=MMP-2 + +usb:v0582p001C* + ID_MODEL_FROM_DATABASE=MMP-2 + +usb:v0582p001D* + ID_MODEL_FROM_DATABASE=V-SYNTH + +usb:v0582p001E* + ID_MODEL_FROM_DATABASE=V-SYNTH + +usb:v0582p0023* + ID_MODEL_FROM_DATABASE=EDIROL UM-550 + +usb:v0582p0024* + ID_MODEL_FROM_DATABASE=EDIROL UM-550 + +usb:v0582p0025* + ID_MODEL_FROM_DATABASE=EDIROL UA-20 + +usb:v0582p0026* + ID_MODEL_FROM_DATABASE=EDIROL UA-20 + +usb:v0582p0027* + ID_MODEL_FROM_DATABASE=EDIROL SD-20 + +usb:v0582p0028* + ID_MODEL_FROM_DATABASE=EDIROL SD-20 + +usb:v0582p0029* + ID_MODEL_FROM_DATABASE=EDIROL SD-80 + +usb:v0582p002A* + ID_MODEL_FROM_DATABASE=EDIROL SD-80 + +usb:v0582p002B* + ID_MODEL_FROM_DATABASE=EDIROL UA-700 + +usb:v0582p002C* + ID_MODEL_FROM_DATABASE=EDIROL UA-700 + +usb:v0582p002D* + ID_MODEL_FROM_DATABASE=XV-2020 Synthesizer + +usb:v0582p002E* + ID_MODEL_FROM_DATABASE=XV-2020 Synthesizer + +usb:v0582p002F* + ID_MODEL_FROM_DATABASE=VariOS + +usb:v0582p0030* + ID_MODEL_FROM_DATABASE=VariOS + +usb:v0582p0033* + ID_MODEL_FROM_DATABASE=EDIROL PCR + +usb:v0582p0034* + ID_MODEL_FROM_DATABASE=EDIROL PCR + +usb:v0582p0035* + ID_MODEL_FROM_DATABASE=M-1000 + +usb:v0582p0037* + ID_MODEL_FROM_DATABASE=Digital Piano + +usb:v0582p0038* + ID_MODEL_FROM_DATABASE=Digital Piano + +usb:v0582p003B* + ID_MODEL_FROM_DATABASE=BOSS GS-10 + +usb:v0582p003C* + ID_MODEL_FROM_DATABASE=BOSS GS-10 + +usb:v0582p0040* + ID_MODEL_FROM_DATABASE=GI-20 + +usb:v0582p0041* + ID_MODEL_FROM_DATABASE=GI-20 + +usb:v0582p0042* + ID_MODEL_FROM_DATABASE=RS-70 + +usb:v0582p0043* + ID_MODEL_FROM_DATABASE=RS-70 + +usb:v0582p0044* + ID_MODEL_FROM_DATABASE=EDIROL UA-1000 + +usb:v0582p0047* + ID_MODEL_FROM_DATABASE=EDIROL UR-80 WAVE + +usb:v0582p0048* + ID_MODEL_FROM_DATABASE=EDIROL UR-80 MIDI + +usb:v0582p0049* + ID_MODEL_FROM_DATABASE=EDIROL UR-80 WAVE + +usb:v0582p004A* + ID_MODEL_FROM_DATABASE=EDIROL UR-80 MIDI + +usb:v0582p004B* + ID_MODEL_FROM_DATABASE=EDIROL M-100FX + +usb:v0582p004C* + ID_MODEL_FROM_DATABASE=EDIROL PCR-A WAVE + +usb:v0582p004D* + ID_MODEL_FROM_DATABASE=EDIROL PCR-A MIDI + +usb:v0582p004E* + ID_MODEL_FROM_DATABASE=EDIROL PCR-A WAVE + +usb:v0582p004F* + ID_MODEL_FROM_DATABASE=EDIROL PCR-A MIDI + +usb:v0582p0050* + ID_MODEL_FROM_DATABASE=EDIROL UA-3FX + +usb:v0582p0052* + ID_MODEL_FROM_DATABASE=EDIROL UM-1SX + +usb:v0582p0054* + ID_MODEL_FROM_DATABASE=Digital Piano + +usb:v0582p0060* + ID_MODEL_FROM_DATABASE=EXR Series + +usb:v0582p0064* + ID_MODEL_FROM_DATABASE=EDIROL PCR-1 WAVE + +usb:v0582p0065* + ID_MODEL_FROM_DATABASE=EDIROL PCR-1 MIDI + +usb:v0582p0066* + ID_MODEL_FROM_DATABASE=EDIROL PCR-1 WAVE + +usb:v0582p0067* + ID_MODEL_FROM_DATABASE=EDIROL PCR-1 MIDI + +usb:v0582p006A* + ID_MODEL_FROM_DATABASE=SP-606 + +usb:v0582p006B* + ID_MODEL_FROM_DATABASE=SP-606 + +usb:v0582p006D* + ID_MODEL_FROM_DATABASE=FANTOM-X + +usb:v0582p006E* + ID_MODEL_FROM_DATABASE=FANTOM-X + +usb:v0582p0073* + ID_MODEL_FROM_DATABASE=EDIROL UA-25 + +usb:v0582p0074* + ID_MODEL_FROM_DATABASE=EDIROL UA-25 + +usb:v0582p0075* + ID_MODEL_FROM_DATABASE=BOSS DR-880 + +usb:v0582p0076* + ID_MODEL_FROM_DATABASE=BOSS DR-880 + +usb:v0582p007A* + ID_MODEL_FROM_DATABASE=RD + +usb:v0582p007B* + ID_MODEL_FROM_DATABASE=RD + +usb:v0582p007D* + ID_MODEL_FROM_DATABASE=EDIROL UA-101 + +usb:v0582p0080* + ID_MODEL_FROM_DATABASE=G-70 + +usb:v0582p0081* + ID_MODEL_FROM_DATABASE=G-70 + +usb:v0582p0084* + ID_MODEL_FROM_DATABASE=V-SYNTH XT + +usb:v0582p0089* + ID_MODEL_FROM_DATABASE=BOSS GT-PRO + +usb:v0582p008B* + ID_MODEL_FROM_DATABASE=EDIROL PC-50 + +usb:v0582p008C* + ID_MODEL_FROM_DATABASE=EDIROL PC-50 + +usb:v0582p008D* + ID_MODEL_FROM_DATABASE=EDIROL UA-101 USB1 + +usb:v0582p0092* + ID_MODEL_FROM_DATABASE=EDIROL PC-80 WAVE + +usb:v0582p0093* + ID_MODEL_FROM_DATABASE=EDIROL PC-80 MIDI + +usb:v0582p0096* + ID_MODEL_FROM_DATABASE=EDIROL UA-1EX + +usb:v0582p009A* + ID_MODEL_FROM_DATABASE=EDIROL UM-3EX + +usb:v0582p009D* + ID_MODEL_FROM_DATABASE=EDIROL UM-1 + +usb:v0582p00A0* + ID_MODEL_FROM_DATABASE=MD-P1 + +usb:v0582p00A2* + ID_MODEL_FROM_DATABASE=Digital Piano + +usb:v0582p00A3* + ID_MODEL_FROM_DATABASE=EDIROL UA-4FX + +usb:v0582p00A6* + ID_MODEL_FROM_DATABASE=Juno-G + +usb:v0582p00A9* + ID_MODEL_FROM_DATABASE=MC-808 + +usb:v0582p00AD* + ID_MODEL_FROM_DATABASE=SH-201 + +usb:v0582p00B2* + ID_MODEL_FROM_DATABASE=VG-99 + +usb:v0582p00B3* + ID_MODEL_FROM_DATABASE=VG-99 + +usb:v0582p00B7* + ID_MODEL_FROM_DATABASE=BK-7m/VIMA JM-5/8 + +usb:v0582p00C2* + ID_MODEL_FROM_DATABASE=SonicCell + +usb:v0582p00C4* + ID_MODEL_FROM_DATABASE=EDIROL M-16DX + +usb:v0582p00C5* + ID_MODEL_FROM_DATABASE=SP-555 + +usb:v0582p00C7* + ID_MODEL_FROM_DATABASE=V-Synth GT + +usb:v0582p00D1* + ID_MODEL_FROM_DATABASE=Music Atelier + +usb:v0582p00D3* + ID_MODEL_FROM_DATABASE=M-380/400 + +usb:v0582p00DA* + ID_MODEL_FROM_DATABASE=BOSS GT-10 + +usb:v0582p00DB* + ID_MODEL_FROM_DATABASE=BOSS GT-10 Guitar Effects Processor + +usb:v0582p00DC* + ID_MODEL_FROM_DATABASE=BOSS GT-10B + +usb:v0582p00DE* + ID_MODEL_FROM_DATABASE=Fantom G + +usb:v0582p00E6* + ID_MODEL_FROM_DATABASE=EDIROL UA-25EX (Advanced mode) + +usb:v0582p00E7* + ID_MODEL_FROM_DATABASE=EDIROL UA-25EX + +usb:v0582p00E9* + ID_MODEL_FROM_DATABASE=UA-1G + +usb:v0582p00EB* + ID_MODEL_FROM_DATABASE=VS-100 + +usb:v0582p00F6* + ID_MODEL_FROM_DATABASE=GW-8/AX-Synth + +usb:v0582p00F8* + ID_MODEL_FROM_DATABASE=JUNO Series + +usb:v0582p00FC* + ID_MODEL_FROM_DATABASE=VS-700C + +usb:v0582p00FD* + ID_MODEL_FROM_DATABASE=VS-700 + +usb:v0582p00FE* + ID_MODEL_FROM_DATABASE=VS-700 M1 + +usb:v0582p00FF* + ID_MODEL_FROM_DATABASE=VS-700 M2 + +usb:v0582p0100* + ID_MODEL_FROM_DATABASE=VS-700 + +usb:v0582p0101* + ID_MODEL_FROM_DATABASE=VS-700 M2 + +usb:v0582p0102* + ID_MODEL_FROM_DATABASE=VB-99 + +usb:v0582p0104* + ID_MODEL_FROM_DATABASE=UM-1G + +usb:v0582p0106* + ID_MODEL_FROM_DATABASE=UM-2G + +usb:v0582p0108* + ID_MODEL_FROM_DATABASE=UM-3G + +usb:v0582p0109* + ID_MODEL_FROM_DATABASE=eBand JS-8 + +usb:v0582p010D* + ID_MODEL_FROM_DATABASE=A-500S + +usb:v0582p010F* + ID_MODEL_FROM_DATABASE=A-PRO + +usb:v0582p0110* + ID_MODEL_FROM_DATABASE=A-PRO + +usb:v0582p0111* + ID_MODEL_FROM_DATABASE=GAIA SH-01 + +usb:v0582p0113* + ID_MODEL_FROM_DATABASE=ME-25 + +usb:v0582p0114* + ID_MODEL_FROM_DATABASE=SD-50 + +usb:v0582p0116* + ID_MODEL_FROM_DATABASE=WAVE/MP3 RECORDER R-05 + +usb:v0582p0117* + ID_MODEL_FROM_DATABASE=VS-20 + +usb:v0582p0119* + ID_MODEL_FROM_DATABASE=OCTAPAD SPD-30 + +usb:v0582p011C* + ID_MODEL_FROM_DATABASE=Lucina AX-09 + +usb:v0582p011E* + ID_MODEL_FROM_DATABASE=BR-800 + +usb:v0582p0120* + ID_MODEL_FROM_DATABASE=OCTA-CAPTURE + +usb:v0582p0121* + ID_MODEL_FROM_DATABASE=OCTA-CAPTURE + +usb:v0582p0123* + ID_MODEL_FROM_DATABASE=JUNO-Gi + +usb:v0582p0124* + ID_MODEL_FROM_DATABASE=M-300 + +usb:v0582p0127* + ID_MODEL_FROM_DATABASE=GR-55 + +usb:v0582p012A* + ID_MODEL_FROM_DATABASE=UM-ONE + +usb:v0582p012B* + ID_MODEL_FROM_DATABASE=DUO-CAPTURE + +usb:v0582p012F* + ID_MODEL_FROM_DATABASE=QUAD-CAPTURE + +usb:v0582p0130* + ID_MODEL_FROM_DATABASE=MICRO BR BR-80 + +usb:v0582p0132* + ID_MODEL_FROM_DATABASE=TRI-CAPTURE + +usb:v0582p0134* + ID_MODEL_FROM_DATABASE=V-Mixer + +usb:v0582p0138* + ID_MODEL_FROM_DATABASE=Boss RC-300 (Audio mode) + +usb:v0582p0139* + ID_MODEL_FROM_DATABASE=Boss RC-300 (Storage mode) + +usb:v0582p013A* + ID_MODEL_FROM_DATABASE=JUPITER-80 + +usb:v0582p013E* + ID_MODEL_FROM_DATABASE=R-26 + +usb:v0582p0145* + ID_MODEL_FROM_DATABASE=SPD-SX + +usb:v0582p014B* + ID_MODEL_FROM_DATABASE=eBand JS-10 + +usb:v0582p014D* + ID_MODEL_FROM_DATABASE=GT-100 + +usb:v0582p0150* + ID_MODEL_FROM_DATABASE=TD-15 + +usb:v0582p0151* + ID_MODEL_FROM_DATABASE=TD-11 + +usb:v0582p0154* + ID_MODEL_FROM_DATABASE=JUPITER-50 + +usb:v0582p0156* + ID_MODEL_FROM_DATABASE=A-Series + +usb:v0582p0158* + ID_MODEL_FROM_DATABASE=TD-30 + +usb:v0582p0159* + ID_MODEL_FROM_DATABASE=DUO-CAPTURE EX + +usb:v0582p015B* + ID_MODEL_FROM_DATABASE=INTEGRA-7 + +usb:v0582p015D* + ID_MODEL_FROM_DATABASE=R-88 + +usb:v0582p0505* + ID_MODEL_FROM_DATABASE=EDIROL UA-101 + +usb:v0583* + ID_VENDOR_FROM_DATABASE=Padix Co., Ltd (Rockfire) + +usb:v0583p0001* + ID_MODEL_FROM_DATABASE=4 Axis 12 button +POV + +usb:v0583p0002* + ID_MODEL_FROM_DATABASE=4 Axis 12 button +POV + +usb:v0583p2030* + ID_MODEL_FROM_DATABASE=RM-203 USB Nest [mode 1] + +usb:v0583p2031* + ID_MODEL_FROM_DATABASE=RM-203 USB Nest [mode 2] + +usb:v0583p2032* + ID_MODEL_FROM_DATABASE=RM-203 USB Nest [mode 3] + +usb:v0583p2033* + ID_MODEL_FROM_DATABASE=RM-203 USB Nest [mode 4] + +usb:v0583p2050* + ID_MODEL_FROM_DATABASE=PX-205 PSX Bridge + +usb:v0583p205F* + ID_MODEL_FROM_DATABASE=PSX/USB converter + +usb:v0583p206F* + ID_MODEL_FROM_DATABASE=USB, 2-axis 8-button gamepad + +usb:v0583p3050* + ID_MODEL_FROM_DATABASE=QF-305u Gamepad + +usb:v0583p3379* + ID_MODEL_FROM_DATABASE=Rockfire X-Force + +usb:v0583p337F* + ID_MODEL_FROM_DATABASE=Rockfire USB RacingStar Vibra + +usb:v0583p509F* + ID_MODEL_FROM_DATABASE=USB,4-Axis,12-Button with POV + +usb:v0583p5259* + ID_MODEL_FROM_DATABASE=Rockfire USB SkyShuttle Vibra + +usb:v0583p525F* + ID_MODEL_FROM_DATABASE=USB Vibration Pad + +usb:v0583p5308* + ID_MODEL_FROM_DATABASE=USB Wireless VibrationPad + +usb:v0583p5359* + ID_MODEL_FROM_DATABASE=Rockfire USB SkyShuttle Pro + +usb:v0583p535F* + ID_MODEL_FROM_DATABASE=USB,real VibrationPad + +usb:v0583p5659* + ID_MODEL_FROM_DATABASE=Rockfire USB SkyShuttle Vibra + +usb:v0583p565F* + ID_MODEL_FROM_DATABASE=USB VibrationPad + +usb:v0583p6009* + ID_MODEL_FROM_DATABASE=Revenger + +usb:v0583p600F* + ID_MODEL_FROM_DATABASE=USB,GameBoard II + +usb:v0583p6258* + ID_MODEL_FROM_DATABASE=USB, 4-axis, 6-button joystick w/view finder + +usb:v0583p6889* + ID_MODEL_FROM_DATABASE=Windstorm Pro + +usb:v0583p688F* + ID_MODEL_FROM_DATABASE=QF-688uv Windstorm Pro Joystick + +usb:v0583p7070* + ID_MODEL_FROM_DATABASE=QF-707u Bazooka Joystick + +usb:v0583pA000* + ID_MODEL_FROM_DATABASE=MaxFire G-08XU Gamepad + +usb:v0583pA015* + ID_MODEL_FROM_DATABASE=4-Axis,16-Button with POV + +usb:v0583pA019* + ID_MODEL_FROM_DATABASE=USB, Vibration ,4-axis, 8-button joystick w/view finder + +usb:v0583pA020* + ID_MODEL_FROM_DATABASE=USB,4-Axis,10-Button with POV + +usb:v0583pA021* + ID_MODEL_FROM_DATABASE=USB,4-Axis,12-Button with POV + +usb:v0583pA022* + ID_MODEL_FROM_DATABASE=USB,4-Axis,14-Button with POV + +usb:v0583pA023* + ID_MODEL_FROM_DATABASE=USB,4-Axis,16-Button with POV + +usb:v0583pA024* + ID_MODEL_FROM_DATABASE=4axis,12button vibrition audio gamepad + +usb:v0583pA025* + ID_MODEL_FROM_DATABASE=4axis,12button vibrition audio gamepad + +usb:v0583pA130* + ID_MODEL_FROM_DATABASE=USB Wireless 2.4GHz Gamepad + +usb:v0583pA131* + ID_MODEL_FROM_DATABASE=USB Wireless 2.4GHz Joystick + +usb:v0583pA132* + ID_MODEL_FROM_DATABASE=USB Wireless 2.4GHz Wheelpad + +usb:v0583pA133* + ID_MODEL_FROM_DATABASE=USB Wireless 2.4GHz Wheel&Gamepad + +usb:v0583pA202* + ID_MODEL_FROM_DATABASE=ForceFeedbackWheel + +usb:v0583pA209* + ID_MODEL_FROM_DATABASE=MetalStrike FF + +usb:v0583pB000* + ID_MODEL_FROM_DATABASE=USB,4-Axis,12-Button with POV + +usb:v0583pB001* + ID_MODEL_FROM_DATABASE=USB,4-Axis,12-Button with POV + +usb:v0583pB002* + ID_MODEL_FROM_DATABASE=Vibration,12-Button USB Wheel + +usb:v0583pB005* + ID_MODEL_FROM_DATABASE=USB,12-Button Wheel + +usb:v0583pB008* + ID_MODEL_FROM_DATABASE=USB Wireless 2.4GHz Wheel + +usb:v0583pB009* + ID_MODEL_FROM_DATABASE=USB,12-Button Wheel + +usb:v0583pB00A* + ID_MODEL_FROM_DATABASE=PSX/USB converter + +usb:v0583pB00B* + ID_MODEL_FROM_DATABASE=PSX/USB converter + +usb:v0583pB00C* + ID_MODEL_FROM_DATABASE=PSX/USB converter + +usb:v0583pB00D* + ID_MODEL_FROM_DATABASE=PSX/USB converter + +usb:v0583pB00E* + ID_MODEL_FROM_DATABASE=4-Axis,12-Button with POV + +usb:v0583pB00F* + ID_MODEL_FROM_DATABASE=USB,5-Axis,10-Button with POV + +usb:v0583pB010* + ID_MODEL_FROM_DATABASE=MetalStrike Pro + +usb:v0583pB012* + ID_MODEL_FROM_DATABASE=Wireless MetalStrike + +usb:v0583pB013* + ID_MODEL_FROM_DATABASE=USB,Wiress 2.4GHZ Joystick + +usb:v0583pB016* + ID_MODEL_FROM_DATABASE=USB,5-Axis,10-Button with POV + +usb:v0583pB018* + ID_MODEL_FROM_DATABASE=TW6 Wheel + +usb:v0583pFF60* + ID_MODEL_FROM_DATABASE=USB Wireless VibrationPad + +usb:v0584* + ID_VENDOR_FROM_DATABASE=RATOC System, Inc. + +usb:v0584p0008* + ID_MODEL_FROM_DATABASE=Fujifilm MemoryCard ReaderWriter + +usb:v0584p0220* + ID_MODEL_FROM_DATABASE=U2SCX SCSI Converter + +usb:v0584p0304* + ID_MODEL_FROM_DATABASE=U2SCX-LVD (SCSI Converter) + +usb:v0584pB000* + ID_MODEL_FROM_DATABASE=REX-USB60 + +usb:v0584pB020* + ID_MODEL_FROM_DATABASE=REX-USB60F + +usb:v0585* + ID_VENDOR_FROM_DATABASE=FlashPoint Technology, Inc. + +usb:v0585p0001* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0002* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0003* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0004* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0005* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0006* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0007* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0008* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p0009* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000A* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000B* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000C* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000D* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000E* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0585p000F* + ID_MODEL_FROM_DATABASE=Digital Camera + +usb:v0586* + ID_VENDOR_FROM_DATABASE=ZyXEL Communications Corp. + +usb:v0586p0025* + ID_MODEL_FROM_DATABASE=802.11b/g/n USB Wireless Network Adapter + +usb:v0586p0100* + ID_MODEL_FROM_DATABASE=omni.net + +usb:v0586p0102* + ID_MODEL_FROM_DATABASE=omni.net II ISDN TA [HFC-S] + +usb:v0586p0110* + ID_MODEL_FROM_DATABASE=omni.net Plus + +usb:v0586p1000* + ID_MODEL_FROM_DATABASE=omni.net LCD Plus - ISDN TA + +usb:v0586p1500* + ID_MODEL_FROM_DATABASE=Omni 56K Plus + +usb:v0586p2011* + ID_MODEL_FROM_DATABASE=Scorpion-980N keyboard + +usb:v0586p3304* + ID_MODEL_FROM_DATABASE=LAN Modem + +usb:v0586p3309* + ID_MODEL_FROM_DATABASE=ADSL Modem Prestige 600 series + +usb:v0586p330A* + ID_MODEL_FROM_DATABASE=ADSL Modem Interface + +usb:v0586p330E* + ID_MODEL_FROM_DATABASE=USB Broadband ADSL Modem Rev 1.10 + +usb:v0586p3400* + ID_MODEL_FROM_DATABASE=ZyAIR B-220 IEEE 802.11b Adapter + +usb:v0586p3401* + ID_MODEL_FROM_DATABASE=ZyAIR G-220 802.11bg + +usb:v0586p3402* + ID_MODEL_FROM_DATABASE=ZyAIR G-220F 802.11bg + +usb:v0586p3403* + ID_MODEL_FROM_DATABASE=AG-200 802.11abg Wireless Adapter [Atheros AR5523] + +usb:v0586p3407* + ID_MODEL_FROM_DATABASE=G-200 v2 802.11bg + +usb:v0586p3408* + ID_MODEL_FROM_DATABASE=G-260 802.11bg + +usb:v0586p3409* + ID_MODEL_FROM_DATABASE=AG-225H 802.11bg + +usb:v0586p340A* + ID_MODEL_FROM_DATABASE=M-202 802.11bg + +usb:v0586p340C* + ID_MODEL_FROM_DATABASE=G-270S 802.11bg Wireless Adapter [Atheros AR5523] + +usb:v0586p340F* + ID_MODEL_FROM_DATABASE=G-220 v2 802.11bg + +usb:v0586p3410* + ID_MODEL_FROM_DATABASE=ZyAIR G-202 802.11bg + +usb:v0586p3412* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v0586p3413* + ID_MODEL_FROM_DATABASE=ZyAIR AG-225H v2 802.11bg + +usb:v0586p3415* + ID_MODEL_FROM_DATABASE=G-210H 802.11g Wireless Adapter + +usb:v0586p3416* + ID_MODEL_FROM_DATABASE=NWD-210N 802.11b/g/n-draft wireless adapter + +usb:v0586p3417* + ID_MODEL_FROM_DATABASE=NWD271N 802.11n Wireless Adapter [Atheros AR9001U-(2)NG] + +usb:v0586p3418* + ID_MODEL_FROM_DATABASE=NWD211AN 802.11abgn Wireless Adapter [Ralink RT2870] + +usb:v0586p3419* + ID_MODEL_FROM_DATABASE=G-220 v3 802.11bg Wireless Adapter [ZyDAS ZD1211B] + +usb:v0586p341A* + ID_MODEL_FROM_DATABASE=NWD-270N Wireless N-lite USB Adapter + +usb:v0586p341E* + ID_MODEL_FROM_DATABASE=NWD2105 802.11bgn Wireless Adapter [Ralink RT3070] + +usb:v0586p341F* + ID_MODEL_FROM_DATABASE=NWD2205 802.11n Wireless N Adapter [Realtek RTL8192CU] + +usb:v0586p3425* + ID_MODEL_FROM_DATABASE=NWD6505 802.11a/b/g/n/ac Wireless Adapter [MediaTek MT7610U] + +usb:v0586p343E* + ID_MODEL_FROM_DATABASE=N220 802.11bgn Wireless Adapter + +usb:v0587* + ID_VENDOR_FROM_DATABASE=America Kotobuki Electronics Industries, Inc. + +usb:v0588* + ID_VENDOR_FROM_DATABASE=Sapien Design + +usb:v0589* + ID_VENDOR_FROM_DATABASE=Victron + +usb:v058A* + ID_VENDOR_FROM_DATABASE=Nohau Corp. + +usb:v058B* + ID_VENDOR_FROM_DATABASE=Infineon Technologies + +usb:v058Bp0015* + ID_MODEL_FROM_DATABASE=Flash Loader utility + +usb:v058Bp001C* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v058Bp0041* + ID_MODEL_FROM_DATABASE=Flash Loader utility + +usb:v058C* + ID_VENDOR_FROM_DATABASE=In Focus Systems + +usb:v058Cp0007* + ID_MODEL_FROM_DATABASE=Flash + +usb:v058Cp0008* + ID_MODEL_FROM_DATABASE=LP130 + +usb:v058Cp000A* + ID_MODEL_FROM_DATABASE=LP530 + +usb:v058Cp0010* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0011* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0012* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0013* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0014* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0015* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0016* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0017* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0018* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp0019* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001A* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001B* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001C* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001D* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001E* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058Cp001F* + ID_MODEL_FROM_DATABASE=Projector + +usb:v058CpFFE5* + ID_MODEL_FROM_DATABASE=IN34 Projector + +usb:v058D* + ID_VENDOR_FROM_DATABASE=Micrel Semiconductor + +usb:v058E* + ID_VENDOR_FROM_DATABASE=Tripath Technology, Inc. + +usb:v058F* + ID_VENDOR_FROM_DATABASE=Alcor Micro Corp. + +usb:v058Fp1234* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v058Fp2412* + ID_MODEL_FROM_DATABASE=SCard R/W CSR-145 + +usb:v058Fp2802* + ID_MODEL_FROM_DATABASE=Monterey Keyboard + +usb:v058Fp5492* + ID_MODEL_FROM_DATABASE=Hub + +usb:v058Fp6232* + ID_MODEL_FROM_DATABASE=Hi-Speed 16-in-1 Flash Card Reader/Writer + +usb:v058Fp6254* + ID_MODEL_FROM_DATABASE=USB Hub + +usb:v058Fp6331* + ID_MODEL_FROM_DATABASE=SD/MMC/MS Card Reader + +usb:v058Fp6332* + ID_MODEL_FROM_DATABASE=Multi-Function Card Reader + +usb:v058Fp6335* + ID_MODEL_FROM_DATABASE=SD/MMC Card Reader + +usb:v058Fp6360* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v058Fp6361* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v058Fp6362* + ID_MODEL_FROM_DATABASE=Flash Card Reader/Writer + +usb:v058Fp6364* + ID_MODEL_FROM_DATABASE=AU6477 Card Reader Controller + +usb:v058Fp6366* + ID_MODEL_FROM_DATABASE=Multi Flash Reader + +usb:v058Fp6377* + ID_MODEL_FROM_DATABASE=AU6375 4-LUN card reader + +usb:v058Fp6386* + ID_MODEL_FROM_DATABASE=Memory Card + +usb:v058Fp6387* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v058Fp6390* + ID_MODEL_FROM_DATABASE=USB 2.0-IDE bridge + +usb:v058Fp6391* + ID_MODEL_FROM_DATABASE=IDE Bridge + +usb:v058Fp9213* + ID_MODEL_FROM_DATABASE=MacAlly Kbd Hub + +usb:v058Fp9215* + ID_MODEL_FROM_DATABASE=AU9814 Hub + +usb:v058Fp9254* + ID_MODEL_FROM_DATABASE=Hub + +usb:v058Fp9310* + ID_MODEL_FROM_DATABASE=Mass Storage (UID4/5A & UID7A) + +usb:v058Fp9320* + ID_MODEL_FROM_DATABASE=Micro Storage Driver for Win98 + +usb:v058Fp9321* + ID_MODEL_FROM_DATABASE=Micro Storage Driver for Win98 + +usb:v058Fp9330* + ID_MODEL_FROM_DATABASE=SD Reader + +usb:v058Fp9331* + ID_MODEL_FROM_DATABASE=Micro Storage Driver for Win98 + +usb:v058Fp9340* + ID_MODEL_FROM_DATABASE=Delkin eFilm Reader-32 + +usb:v058Fp9350* + ID_MODEL_FROM_DATABASE=Delkin eFilm Reader-32 + +usb:v058Fp9360* + ID_MODEL_FROM_DATABASE=8-in-1 Media Card Reader + +usb:v058Fp9361* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v058Fp9368* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v058Fp9380* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v058Fp9381* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v058Fp9382* + ID_MODEL_FROM_DATABASE=Acer/Sweex Flash drive + +usb:v058Fp9384* + ID_MODEL_FROM_DATABASE=qdi U2Disk T209M + +usb:v058Fp9410* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v058Fp9472* + ID_MODEL_FROM_DATABASE=Keyboard Hub + +usb:v058Fp9510* + ID_MODEL_FROM_DATABASE=ChunghwaTL USB02 Smartcard Reader + +usb:v058Fp9520* + ID_MODEL_FROM_DATABASE=Watchdata W 1981 + +usb:v058Fp9540* + ID_MODEL_FROM_DATABASE=AU9540 Smartcard Reader + +usb:v058Fp9720* + ID_MODEL_FROM_DATABASE=USB-Serial Adapter + +usb:v058FpA014* + ID_MODEL_FROM_DATABASE=Asus Integrated Webcam + +usb:v058FpB002* + ID_MODEL_FROM_DATABASE=Acer Integrated Webcam + +usb:v0590* + ID_VENDOR_FROM_DATABASE=Omron Corp. + +usb:v0590p0004* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v0590p000B* + ID_MODEL_FROM_DATABASE=MR56SVS + +usb:v0590p0028* + ID_MODEL_FROM_DATABASE=HJ-720IT / HEM-7080IT-E / HEM-790IT + +usb:v0591* + ID_VENDOR_FROM_DATABASE=Questra Consulting + +usb:v0592* + ID_VENDOR_FROM_DATABASE=Powerware Corp. + +usb:v0592p0002* + ID_MODEL_FROM_DATABASE=UPS (X-Slot) + +usb:v0593* + ID_VENDOR_FROM_DATABASE=Incite + +usb:v0594* + ID_VENDOR_FROM_DATABASE=Princeton Graphic Systems + +usb:v0595* + ID_VENDOR_FROM_DATABASE=Zoran Microelectronics, Ltd + +usb:v0595p1001* + ID_MODEL_FROM_DATABASE=Digitrex DSC-1300/DSC-2100 (mass storage mode) + +usb:v0595p2002* + ID_MODEL_FROM_DATABASE=DIGITAL STILL CAMERA 6M 4X + +usb:v0595p4343* + ID_MODEL_FROM_DATABASE=Digital Camera EX-20 DSC + +usb:v0596* + ID_VENDOR_FROM_DATABASE=MicroTouch Systems, Inc. + +usb:v0596p0001* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v0596p0002* + ID_MODEL_FROM_DATABASE=Touch Screen Controller + +usb:v0596p0500* + ID_MODEL_FROM_DATABASE=PCT Multitouch HID Controller + +usb:v0596p0543* + ID_MODEL_FROM_DATABASE=DELL XPS touchscreen + +usb:v0597* + ID_VENDOR_FROM_DATABASE=Trisignal Communications + +usb:v0598* + ID_VENDOR_FROM_DATABASE=Niigata Canotec Co., Inc. + +usb:v0599* + ID_VENDOR_FROM_DATABASE=Brilliance Semiconductor, Inc. + +usb:v059A* + ID_VENDOR_FROM_DATABASE=Spectrum Signal Processing, Inc. + +usb:v059B* + ID_VENDOR_FROM_DATABASE=Iomega Corp. + +usb:v059Bp0001* + ID_MODEL_FROM_DATABASE=Zip 100 (Type 1) + +usb:v059Bp000B* + ID_MODEL_FROM_DATABASE=Zip 100 (Type 2) + +usb:v059Bp0021* + ID_MODEL_FROM_DATABASE=Win98 Disk Controller + +usb:v059Bp0030* + ID_MODEL_FROM_DATABASE=Zip 250 (Ver 1) + +usb:v059Bp0031* + ID_MODEL_FROM_DATABASE=Zip 100 (Type 3) + +usb:v059Bp0032* + ID_MODEL_FROM_DATABASE=Zip 250 (Ver 2) + +usb:v059Bp0034* + ID_MODEL_FROM_DATABASE=Zip 100 Driver + +usb:v059Bp0037* + ID_MODEL_FROM_DATABASE=Zip 750 MB + +usb:v059Bp0040* + ID_MODEL_FROM_DATABASE=SCSI Bridge + +usb:v059Bp0042* + ID_MODEL_FROM_DATABASE=Rev 70 GB + +usb:v059Bp0050* + ID_MODEL_FROM_DATABASE=Zip CD 650 Writer + +usb:v059Bp0053* + ID_MODEL_FROM_DATABASE=CDRW55292EXT CD-RW External Drive + +usb:v059Bp0056* + ID_MODEL_FROM_DATABASE=External CD-RW Drive Enclosure + +usb:v059Bp0057* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v059Bp005D* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v059Bp005F* + ID_MODEL_FROM_DATABASE=CDRW64892EXT3-C CD-RW 52x24x52x External Drive + +usb:v059Bp0060* + ID_MODEL_FROM_DATABASE=PCMCIA PocketZip Dock + +usb:v059Bp0061* + ID_MODEL_FROM_DATABASE=Varo PocketZip 40 MP3 Player + +usb:v059Bp006D* + ID_MODEL_FROM_DATABASE=HipZip MP3 Player + +usb:v059Bp0070* + ID_MODEL_FROM_DATABASE=eGo Portable Hard Drive + +usb:v059Bp007C* + ID_MODEL_FROM_DATABASE=Ultra Max USB/1394 + +usb:v059Bp007D* + ID_MODEL_FROM_DATABASE=HTC42606 0G9AT00 [Iomega HDD] + +usb:v059Bp007E* + ID_MODEL_FROM_DATABASE=Mini 256MB/512MB Flash Drive [IOM2D5] + +usb:v059Bp00DB* + ID_MODEL_FROM_DATABASE=FotoShow Zip 250 Driver + +usb:v059Bp0150* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v059Bp015D* + ID_MODEL_FROM_DATABASE=Super DVD Writer + +usb:v059Bp0173* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp0174* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp0176* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp0177* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp0178* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp0179* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v059Bp017A* + ID_MODEL_FROM_DATABASE=HDD + +usb:v059Bp017B* + ID_MODEL_FROM_DATABASE=HDD/1394A + +usb:v059Bp017C* + ID_MODEL_FROM_DATABASE=HDD/1394B + +usb:v059Bp0251* + ID_MODEL_FROM_DATABASE=Optical + +usb:v059Bp0252* + ID_MODEL_FROM_DATABASE=Optical + +usb:v059Bp0275* + ID_MODEL_FROM_DATABASE=ST332082 0A + +usb:v059Bp0278* + ID_MODEL_FROM_DATABASE=LDHD-UPS [Professional Desktop Hard Drive eSATA / USB2.0] + +usb:v059Bp027A* + ID_MODEL_FROM_DATABASE=LPHD250-U [Portable Hard Drive Silver Series 250 Go] + +usb:v059Bp0470* + ID_MODEL_FROM_DATABASE=Prestige Portable Hard Drive + +usb:v059Bp047A* + ID_MODEL_FROM_DATABASE=Select Portable Hard Drive + +usb:v059Bp0571* + ID_MODEL_FROM_DATABASE=Prestige Portable Hard Drive + +usb:v059Bp0579* + ID_MODEL_FROM_DATABASE=eGo Portable Hard Drive + +usb:v059Bp1052* + ID_MODEL_FROM_DATABASE=DVD+RW External Drive + +usb:v059C* + ID_VENDOR_FROM_DATABASE=A-Trend Technology Co., Ltd + +usb:v059D* + ID_VENDOR_FROM_DATABASE=Advanced Input Devices + +usb:v059E* + ID_VENDOR_FROM_DATABASE=Intelligent Instrumentation + +usb:v059F* + ID_VENDOR_FROM_DATABASE=LaCie, Ltd + +usb:v059Fp0201* + ID_MODEL_FROM_DATABASE=StudioDrive USB2 + +usb:v059Fp0202* + ID_MODEL_FROM_DATABASE=StudioDrive USB2 + +usb:v059Fp0203* + ID_MODEL_FROM_DATABASE=StudioDrive USB2 + +usb:v059Fp0211* + ID_MODEL_FROM_DATABASE=PocketDrive + +usb:v059Fp0212* + ID_MODEL_FROM_DATABASE=PocketDrive + +usb:v059Fp0213* + ID_MODEL_FROM_DATABASE=PocketDrive USB2 + +usb:v059Fp0323* + ID_MODEL_FROM_DATABASE=LaCie d2 Drive USB2 + +usb:v059Fp0421* + ID_MODEL_FROM_DATABASE=Big Disk G465 + +usb:v059Fp0525* + ID_MODEL_FROM_DATABASE=BigDisk Extreme 500 + +usb:v059Fp0641* + ID_MODEL_FROM_DATABASE=Mobile Hard Drive + +usb:v059Fp0829* + ID_MODEL_FROM_DATABASE=BigDisk Extreme+ + +usb:v059Fp100C* + ID_MODEL_FROM_DATABASE=Rugged Triple Interface Mobile Hard Drive + +usb:v059Fp1010* + ID_MODEL_FROM_DATABASE=Desktop Hard Drive + +usb:v059Fp1016* + ID_MODEL_FROM_DATABASE=Desktop Hard Drive + +usb:v059Fp1018* + ID_MODEL_FROM_DATABASE=Desktop Hard Drive + +usb:v059Fp1019* + ID_MODEL_FROM_DATABASE=Desktop Hard Drive + +usb:v059Fp1021* + ID_MODEL_FROM_DATABASE=Little Disk + +usb:v059Fp1027* + ID_MODEL_FROM_DATABASE=iamaKey V2 + +usb:v059Fp102A* + ID_MODEL_FROM_DATABASE=Rikiki Hard Drive + +usb:v059Fp1049* + ID_MODEL_FROM_DATABASE=rikiki Harddrive + +usb:v059Fp1052* + ID_MODEL_FROM_DATABASE=P'9220 Mobile Drive + +usb:v059Fp1064* + ID_MODEL_FROM_DATABASE=Rugged 16 and 32 GB + +usb:v059Fp106D* + ID_MODEL_FROM_DATABASE=Porsche Design Mobile Drive + +usb:v059Fp106E* + ID_MODEL_FROM_DATABASE=Porsche Design Desktop Drive + +usb:v059FpA601* + ID_MODEL_FROM_DATABASE=HardDrive + +usb:v059FpA602* + ID_MODEL_FROM_DATABASE=CD R/W + +usb:v05A0* + ID_VENDOR_FROM_DATABASE=Vetronix Corp. + +usb:v05A1* + ID_VENDOR_FROM_DATABASE=USC Corp. + +usb:v05A2* + ID_VENDOR_FROM_DATABASE=Fuji Film Microdevices Co., Ltd + +usb:v05A3* + ID_VENDOR_FROM_DATABASE=ARC International + +usb:v05A3p8388* + ID_MODEL_FROM_DATABASE=Marvell 88W8388 802.11a/b/g WLAN + +usb:v05A4* + ID_VENDOR_FROM_DATABASE=Ortek Technology, Inc. + +usb:v05A4p1000* + ID_MODEL_FROM_DATABASE=WKB-1000S Wireless Ergo Keyboard with Touchpad + +usb:v05A4p2000* + ID_MODEL_FROM_DATABASE=WKB-2000 Wireless Keyboard with Touchpad + +usb:v05A4p9720* + ID_MODEL_FROM_DATABASE=Keyboard Mouse + +usb:v05A4p9722* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v05A4p9731* + ID_MODEL_FROM_DATABASE=MCK-600W/MCK-800USB Keyboard + +usb:v05A4p9783* + ID_MODEL_FROM_DATABASE=Wireless Keypad + +usb:v05A4p9837* + ID_MODEL_FROM_DATABASE=Targus Number Keypad + +usb:v05A4p9862* + ID_MODEL_FROM_DATABASE=Targus Number Keypad (Composite Device) + +usb:v05A4p9881* + ID_MODEL_FROM_DATABASE=IR receiver [VRC-1100 Vista MCE Remote Control] + +usb:v05A5* + ID_VENDOR_FROM_DATABASE=Sampo Technology Corp. + +usb:v05A6* + ID_VENDOR_FROM_DATABASE=Cisco Systems, Inc. + +usb:v05A6p0001* + ID_MODEL_FROM_DATABASE=CVA124 Cable Voice Adapter (WDM) + +usb:v05A6p0002* + ID_MODEL_FROM_DATABASE=CVA122 Cable Voice Adapter (WDM) + +usb:v05A6p0003* + ID_MODEL_FROM_DATABASE=CVA124E Cable Voice Adapter (WDM) + +usb:v05A6p0004* + ID_MODEL_FROM_DATABASE=CVA122E Cable Voice Adapter (WDM) + +usb:v05A7* + ID_VENDOR_FROM_DATABASE=Bose Corp. + +usb:v05A7p4000* + ID_MODEL_FROM_DATABASE=Bluetooth Headset + +usb:v05A7p4001* + ID_MODEL_FROM_DATABASE=Bluetooth Headset in DFU mode + +usb:v05A7p4002* + ID_MODEL_FROM_DATABASE=Bluetooth Headset Series 2 + +usb:v05A7p4003* + ID_MODEL_FROM_DATABASE=Bluetooth Headset Series 2 in DFU mode + +usb:v05A7pBC50* + ID_MODEL_FROM_DATABASE=SoundLink Wireless Mobile speaker + +usb:v05A7pBC51* + ID_MODEL_FROM_DATABASE=SoundLink Wireless Mobile speaker in DFU mode + +usb:v05A8* + ID_VENDOR_FROM_DATABASE=Spacetec IMC Corp. + +usb:v05A9* + ID_VENDOR_FROM_DATABASE=OmniVision Technologies, Inc. + +usb:v05A9p0511* + ID_MODEL_FROM_DATABASE=OV511 Webcam + +usb:v05A9p0518* + ID_MODEL_FROM_DATABASE=OV518 Webcam + +usb:v05A9p0519* + ID_MODEL_FROM_DATABASE=OV519 Microphone + +usb:v05A9p1550* + ID_MODEL_FROM_DATABASE=VEHO Filmscanner + +usb:v05A9p2640* + ID_MODEL_FROM_DATABASE=OV2640 Webcam + +usb:v05A9p2643* + ID_MODEL_FROM_DATABASE=Monitor Webcam + +usb:v05A9p264B* + ID_MODEL_FROM_DATABASE=Monitor Webcam + +usb:v05A9p2800* + ID_MODEL_FROM_DATABASE=SuperCAM + +usb:v05A9p4519* + ID_MODEL_FROM_DATABASE=Webcam Classic + +usb:v05A9p7670* + ID_MODEL_FROM_DATABASE=OV7670 Webcam + +usb:v05A9p8065* + ID_MODEL_FROM_DATABASE=GAIA Sensor FPGA Demo Board + +usb:v05A9p8519* + ID_MODEL_FROM_DATABASE=OV519 Webcam + +usb:v05A9pA511* + ID_MODEL_FROM_DATABASE=OV511+ Webcam + +usb:v05A9pA518* + ID_MODEL_FROM_DATABASE=D-Link DSB-C310 Webcam + +usb:v05AA* + ID_VENDOR_FROM_DATABASE=Utilux South China, Ltd + +usb:v05AB* + ID_VENDOR_FROM_DATABASE=In-System Design + +usb:v05ABp0002* + ID_MODEL_FROM_DATABASE=Parallel Port + +usb:v05ABp0030* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 (TPP) + +usb:v05ABp0031* + ID_MODEL_FROM_DATABASE=ATA Bridge + +usb:v05ABp0060* + ID_MODEL_FROM_DATABASE=USB 2.0 ATA Bridge + +usb:v05ABp0061* + ID_MODEL_FROM_DATABASE=Storage Adapter V3 (TPP-I) + +usb:v05ABp0101* + ID_MODEL_FROM_DATABASE=Storage Adapter (TPP) + +usb:v05ABp0130* + ID_MODEL_FROM_DATABASE=Compact Flash and Microdrive Reader (TPP) + +usb:v05ABp0200* + ID_MODEL_FROM_DATABASE=USS725 ATA Bridge + +usb:v05ABp0201* + ID_MODEL_FROM_DATABASE=Storage Adapter (TPP) + +usb:v05ABp0202* + ID_MODEL_FROM_DATABASE=ATA Bridge + +usb:v05ABp0300* + ID_MODEL_FROM_DATABASE=Portable Hard Drive (TPP) + +usb:v05ABp0301* + ID_MODEL_FROM_DATABASE=Portable Hard Drive V2 + +usb:v05ABp0350* + ID_MODEL_FROM_DATABASE=Portable Hard Drive (TPP) + +usb:v05ABp0351* + ID_MODEL_FROM_DATABASE=Portable Hard Drive V2 + +usb:v05ABp081A* + ID_MODEL_FROM_DATABASE=ATA Bridge + +usb:v05ABp0CDA* + ID_MODEL_FROM_DATABASE=ATA Bridge for CD-R/RW + +usb:v05ABp1001* + ID_MODEL_FROM_DATABASE=BAYI Printer Class Support + +usb:v05ABp5700* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 (TPP) + +usb:v05ABp5701* + ID_MODEL_FROM_DATABASE=USB Storage Adapter V2 + +usb:v05ABp5901* + ID_MODEL_FROM_DATABASE=Smart Board (TPP) + +usb:v05ABp5A01* + ID_MODEL_FROM_DATABASE=ATI Storage Adapter (TPP) + +usb:v05ABp5D01* + ID_MODEL_FROM_DATABASE=DataBook Adapter (TPP) + +usb:v05AC* + ID_VENDOR_FROM_DATABASE=Apple, Inc. + +usb:v05ACp0201* + ID_MODEL_FROM_DATABASE=USB Keyboard [Alps or Logitech, M2452] + +usb:v05ACp0202* + ID_MODEL_FROM_DATABASE=Keyboard [ALPS] + +usb:v05ACp0205* + ID_MODEL_FROM_DATABASE=Extended Keyboard [Mitsumi] + +usb:v05ACp0206* + ID_MODEL_FROM_DATABASE=Extended Keyboard [Mitsumi] + +usb:v05ACp020B* + ID_MODEL_FROM_DATABASE=Pro Keyboard [Mitsumi, A1048/US layout] + +usb:v05ACp020C* + ID_MODEL_FROM_DATABASE=Extended Keyboard [Mitsumi] + +usb:v05ACp020D* + ID_MODEL_FROM_DATABASE=Pro Keyboard [Mitsumi, A1048/JIS layout] + +usb:v05ACp020E* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp020F* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0214* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0215* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0216* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp0217* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0218* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0219* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp021A* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp021B* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp021C* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp021D* + ID_MODEL_FROM_DATABASE=Aluminum Mini Keyboard (ANSI) + +usb:v05ACp021E* + ID_MODEL_FROM_DATABASE=Aluminum Mini Keyboard (ISO) + +usb:v05ACp021F* + ID_MODEL_FROM_DATABASE=Aluminum Mini Keyboard (JIS) + +usb:v05ACp0220* + ID_MODEL_FROM_DATABASE=Aluminum Keyboard (ANSI) + +usb:v05ACp0221* + ID_MODEL_FROM_DATABASE=Aluminum Keyboard (ISO) + +usb:v05ACp0222* + ID_MODEL_FROM_DATABASE=Aluminum Keyboard (JIS) + +usb:v05ACp0223* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0224* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0225* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp0229* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp022A* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Pro) (ISO) + +usb:v05ACp022B* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Pro) (JIS) + +usb:v05ACp0230* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Pro 4,1) (ANSI) + +usb:v05ACp0231* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Pro 4,1) (ISO) + +usb:v05ACp0232* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Pro 4,1) (JIS) + +usb:v05ACp0236* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0237* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0238* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp023F* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0240* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0241* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp0242* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0243* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0244* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp0245* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0246* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0247* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp024A* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Air) (ISO) + +usb:v05ACp024D* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (MacBook Air) (ISO) + +usb:v05ACp0250* + ID_MODEL_FROM_DATABASE=Aluminium Keyboard (ISO) + +usb:v05ACp0252* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ANSI) + +usb:v05ACp0253* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0254* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (JIS) + +usb:v05ACp0259* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad + +usb:v05ACp0263* + ID_MODEL_FROM_DATABASE=Apple Internal Keyboard / Trackpad (MacBook Retina) + +usb:v05ACp0267* + ID_MODEL_FROM_DATABASE=Magic Keyboard A1644 + +usb:v05ACp0273* + ID_MODEL_FROM_DATABASE=Internal Keyboard/Trackpad (ISO) + +usb:v05ACp0301* + ID_MODEL_FROM_DATABASE=USB Mouse [Mitsumi, M4848] + +usb:v05ACp0302* + ID_MODEL_FROM_DATABASE=Optical Mouse [Fujitsu] + +usb:v05ACp0304* + ID_MODEL_FROM_DATABASE=Mighty Mouse [Mitsumi, M1152] + +usb:v05ACp0306* + ID_MODEL_FROM_DATABASE=Optical USB Mouse [Fujitsu] + +usb:v05ACp030A* + ID_MODEL_FROM_DATABASE=Internal Trackpad + +usb:v05ACp030B* + ID_MODEL_FROM_DATABASE=Internal Trackpad + +usb:v05ACp030D* + ID_MODEL_FROM_DATABASE=Magic Mouse + +usb:v05ACp030E* + ID_MODEL_FROM_DATABASE=MC380Z/A [Magic Trackpad] + +usb:v05ACp1000* + ID_MODEL_FROM_DATABASE=Bluetooth HCI MacBookPro (HID mode) + +usb:v05ACp1001* + ID_MODEL_FROM_DATABASE=Keyboard Hub [ALPS] + +usb:v05ACp1002* + ID_MODEL_FROM_DATABASE=Extended Keyboard Hub [Mitsumi] + +usb:v05ACp1003* + ID_MODEL_FROM_DATABASE=Hub in Pro Keyboard [Mitsumi, A1048] + +usb:v05ACp1006* + ID_MODEL_FROM_DATABASE=Hub in Aluminum Keyboard + +usb:v05ACp1008* + ID_MODEL_FROM_DATABASE=Mini DisplayPort to Dual-Link DVI Adapter + +usb:v05ACp1101* + ID_MODEL_FROM_DATABASE=Speakers + +usb:v05ACp1105* + ID_MODEL_FROM_DATABASE=Audio in LED Cinema Display + +usb:v05ACp1107* + ID_MODEL_FROM_DATABASE=Thunderbolt Display Audio + +usb:v05ACp1112* + ID_MODEL_FROM_DATABASE=FaceTime HD Camera (Display) + +usb:v05ACp1201* + ID_MODEL_FROM_DATABASE=3G iPod + +usb:v05ACp1202* + ID_MODEL_FROM_DATABASE=iPod 2G + +usb:v05ACp1203* + ID_MODEL_FROM_DATABASE=iPod 4.Gen Grayscale 40G + +usb:v05ACp1204* + ID_MODEL_FROM_DATABASE=iPod [Photo] + +usb:v05ACp1205* + ID_MODEL_FROM_DATABASE=iPod Mini 1.Gen/2.Gen + +usb:v05ACp1206* + ID_MODEL_FROM_DATABASE=iPod '06' + +usb:v05ACp1207* + ID_MODEL_FROM_DATABASE=iPod '07' + +usb:v05ACp1208* + ID_MODEL_FROM_DATABASE=iPod '08' + +usb:v05ACp1209* + ID_MODEL_FROM_DATABASE=iPod Video + +usb:v05ACp120A* + ID_MODEL_FROM_DATABASE=iPod Nano + +usb:v05ACp1223* + ID_MODEL_FROM_DATABASE=iPod Classic/Nano 3.Gen (DFU mode) + +usb:v05ACp1224* + ID_MODEL_FROM_DATABASE=iPod Nano 3.Gen (DFU mode) + +usb:v05ACp1225* + ID_MODEL_FROM_DATABASE=iPod Nano 4.Gen (DFU mode) + +usb:v05ACp1227* + ID_MODEL_FROM_DATABASE=Mobile Device (DFU Mode) + +usb:v05ACp1231* + ID_MODEL_FROM_DATABASE=iPod Nano 5.Gen (DFU mode) + +usb:v05ACp1240* + ID_MODEL_FROM_DATABASE=iPod Nano 2.Gen (DFU mode) + +usb:v05ACp1242* + ID_MODEL_FROM_DATABASE=iPod Nano 3.Gen (WTF mode) + +usb:v05ACp1243* + ID_MODEL_FROM_DATABASE=iPod Nano 4.Gen (WTF mode) + +usb:v05ACp1245* + ID_MODEL_FROM_DATABASE=iPod Classic 3.Gen (WTF mode) + +usb:v05ACp1246* + ID_MODEL_FROM_DATABASE=iPod Nano 5.Gen (WTF mode) + +usb:v05ACp1255* + ID_MODEL_FROM_DATABASE=iPod Nano 4.Gen (DFU mode) + +usb:v05ACp1260* + ID_MODEL_FROM_DATABASE=iPod Nano 2.Gen + +usb:v05ACp1261* + ID_MODEL_FROM_DATABASE=iPod Classic + +usb:v05ACp1262* + ID_MODEL_FROM_DATABASE=iPod Nano 3.Gen + +usb:v05ACp1263* + ID_MODEL_FROM_DATABASE=iPod Nano 4.Gen + +usb:v05ACp1265* + ID_MODEL_FROM_DATABASE=iPod Nano 5.Gen + +usb:v05ACp1266* + ID_MODEL_FROM_DATABASE=iPod Nano 6.Gen + +usb:v05ACp1267* + ID_MODEL_FROM_DATABASE=iPod Nano 7.Gen + +usb:v05ACp1281* + ID_MODEL_FROM_DATABASE=Apple Mobile Device [Recovery Mode] + +usb:v05ACp1290* + ID_MODEL_FROM_DATABASE=iPhone + +usb:v05ACp1291* + ID_MODEL_FROM_DATABASE=iPod Touch 1.Gen + +usb:v05ACp1292* + ID_MODEL_FROM_DATABASE=iPhone 3G + +usb:v05ACp1293* + ID_MODEL_FROM_DATABASE=iPod Touch 2.Gen + +usb:v05ACp1294* + ID_MODEL_FROM_DATABASE=iPhone 3GS + +usb:v05ACp1296* + ID_MODEL_FROM_DATABASE=iPod Touch 3.Gen (8GB) + +usb:v05ACp1297* + ID_MODEL_FROM_DATABASE=iPhone 4 + +usb:v05ACp1299* + ID_MODEL_FROM_DATABASE=iPod Touch 3.Gen + +usb:v05ACp129A* + ID_MODEL_FROM_DATABASE=iPad + +usb:v05ACp129C* + ID_MODEL_FROM_DATABASE=iPhone 4(CDMA) + +usb:v05ACp129E* + ID_MODEL_FROM_DATABASE=iPod Touch 4.Gen + +usb:v05ACp129F* + ID_MODEL_FROM_DATABASE=iPad 2 + +usb:v05ACp12A0* + ID_MODEL_FROM_DATABASE=iPhone 4S + +usb:v05ACp12A2* + ID_MODEL_FROM_DATABASE=iPad 2 (3G; 64GB) + +usb:v05ACp12A3* + ID_MODEL_FROM_DATABASE=iPad 2 (CDMA) + +usb:v05ACp12A4* + ID_MODEL_FROM_DATABASE=iPad 3 (wifi) + +usb:v05ACp12A5* + ID_MODEL_FROM_DATABASE=iPad 3 (CDMA) + +usb:v05ACp12A6* + ID_MODEL_FROM_DATABASE=iPad 3 (3G, 16 GB) + +usb:v05ACp12A8* + ID_MODEL_FROM_DATABASE=iPhone5/5C/5S/6 + +usb:v05ACp12A9* + ID_MODEL_FROM_DATABASE=iPad 2 + +usb:v05ACp12AA* + ID_MODEL_FROM_DATABASE=iPod Touch 5.Gen [A1421] + +usb:v05ACp12AB* + ID_MODEL_FROM_DATABASE=iPad 4/Mini1 + +usb:v05ACp1300* + ID_MODEL_FROM_DATABASE=iPod Shuffle + +usb:v05ACp1301* + ID_MODEL_FROM_DATABASE=iPod Shuffle 2.Gen + +usb:v05ACp1302* + ID_MODEL_FROM_DATABASE=iPod Shuffle 3.Gen + +usb:v05ACp1303* + ID_MODEL_FROM_DATABASE=iPod Shuffle 4.Gen + +usb:v05ACp1401* + ID_MODEL_FROM_DATABASE=Modem + +usb:v05ACp1402* + ID_MODEL_FROM_DATABASE=Ethernet Adapter [A1277] + +usb:v05ACp1500* + ID_MODEL_FROM_DATABASE=SuperDrive [A1379] + +usb:v05ACp8005* + ID_MODEL_FROM_DATABASE=OHCI Root Hub Simulation + +usb:v05ACp8006* + ID_MODEL_FROM_DATABASE=EHCI Root Hub Simulation + +usb:v05ACp8007* + ID_MODEL_FROM_DATABASE=XHCI Root Hub USB 2.0 Simulation + +usb:v05ACp8202* + ID_MODEL_FROM_DATABASE=HCF V.90 Data/Fax Modem + +usb:v05ACp8203* + ID_MODEL_FROM_DATABASE=Bluetooth HCI + +usb:v05ACp8204* + ID_MODEL_FROM_DATABASE=Built-in Bluetooth 2.0+EDR HCI + +usb:v05ACp8205* + ID_MODEL_FROM_DATABASE=Bluetooth HCI + +usb:v05ACp8206* + ID_MODEL_FROM_DATABASE=Bluetooth HCI + +usb:v05ACp820A* + ID_MODEL_FROM_DATABASE=Bluetooth HID Keyboard + +usb:v05ACp820B* + ID_MODEL_FROM_DATABASE=Bluetooth HID Mouse + +usb:v05ACp820F* + ID_MODEL_FROM_DATABASE=Bluetooth HCI + +usb:v05ACp8213* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp8215* + ID_MODEL_FROM_DATABASE=Built-in Bluetooth 2.0+EDR HCI + +usb:v05ACp8216* + ID_MODEL_FROM_DATABASE=Bluetooth USB Host Controller + +usb:v05ACp8217* + ID_MODEL_FROM_DATABASE=Bluetooth USB Host Controller + +usb:v05ACp8218* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp821A* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp821F* + ID_MODEL_FROM_DATABASE=Built-in Bluetooth 2.0+EDR HCI + +usb:v05ACp8240* + ID_MODEL_FROM_DATABASE=Built-in IR Receiver + +usb:v05ACp8241* + ID_MODEL_FROM_DATABASE=Built-in IR Receiver + +usb:v05ACp8242* + ID_MODEL_FROM_DATABASE=Built-in IR Receiver + +usb:v05ACp8281* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp8286* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp828C* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp8290* + ID_MODEL_FROM_DATABASE=Bluetooth Host Controller + +usb:v05ACp8300* + ID_MODEL_FROM_DATABASE=Built-in iSight (no firmware loaded) + +usb:v05ACp8403* + ID_MODEL_FROM_DATABASE=Internal Memory Card Reader + +usb:v05ACp8404* + ID_MODEL_FROM_DATABASE=Internal Memory Card Reader + +usb:v05ACp8501* + ID_MODEL_FROM_DATABASE=Built-in iSight [Micron] + +usb:v05ACp8502* + ID_MODEL_FROM_DATABASE=Built-in iSight + +usb:v05ACp8505* + ID_MODEL_FROM_DATABASE=Built-in iSight + +usb:v05ACp8507* + ID_MODEL_FROM_DATABASE=Built-in iSight + +usb:v05ACp8508* + ID_MODEL_FROM_DATABASE=iSight in LED Cinema Display + +usb:v05ACp8509* + ID_MODEL_FROM_DATABASE=FaceTime HD Camera + +usb:v05ACp850A* + ID_MODEL_FROM_DATABASE=FaceTime Camera + +usb:v05ACp8510* + ID_MODEL_FROM_DATABASE=FaceTime HD Camera (Built-in) + +usb:v05ACp911C* + ID_MODEL_FROM_DATABASE=Hub in A1082 [Cinema HD Display 23"] + +usb:v05ACp9127* + ID_MODEL_FROM_DATABASE=Hub in Thunderbolt Display + +usb:v05ACp912F* + ID_MODEL_FROM_DATABASE=Hub in 30" Cinema Display + +usb:v05ACp9215* + ID_MODEL_FROM_DATABASE=Studio Display 15" + +usb:v05ACp9217* + ID_MODEL_FROM_DATABASE=Studio Display 17" + +usb:v05ACp9218* + ID_MODEL_FROM_DATABASE=Cinema Display 23" + +usb:v05ACp9219* + ID_MODEL_FROM_DATABASE=Cinema Display 20" + +usb:v05ACp921C* + ID_MODEL_FROM_DATABASE=A1082 [Cinema HD Display 23"] + +usb:v05ACp921E* + ID_MODEL_FROM_DATABASE=Cinema Display 24" + +usb:v05ACp9221* + ID_MODEL_FROM_DATABASE=30" Cinema Display + +usb:v05ACp9226* + ID_MODEL_FROM_DATABASE=LED Cinema Display + +usb:v05ACp9227* + ID_MODEL_FROM_DATABASE=Thunderbolt Display + +usb:v05ACp9232* + ID_MODEL_FROM_DATABASE=Cinema HD Display 30" + +usb:v05ACpFFFF* + ID_MODEL_FROM_DATABASE=Bluetooth in DFU mode - Driver + +usb:v05AD* + ID_VENDOR_FROM_DATABASE=Y.C. Cable U.S.A., Inc. + +usb:v05AE* + ID_VENDOR_FROM_DATABASE=Synopsys, Inc. + +usb:v05AF* + ID_VENDOR_FROM_DATABASE=Jing-Mold Enterprise Co., Ltd + +usb:v05AFp0806* + ID_MODEL_FROM_DATABASE=HP SK806A Keyboard + +usb:v05AFp0809* + ID_MODEL_FROM_DATABASE=Wireless Keyboard and Mouse + +usb:v05AFp0821* + ID_MODEL_FROM_DATABASE=IDE to + +usb:v05AFp3062* + ID_MODEL_FROM_DATABASE=Cordless Keyboard + +usb:v05AFp9167* + ID_MODEL_FROM_DATABASE=KB 9151B - 678 + +usb:v05AFp9267* + ID_MODEL_FROM_DATABASE=KB 9251B - 678 Mouse + +usb:v05B0* + ID_VENDOR_FROM_DATABASE=Fountain Technologies, Inc. + +usb:v05B1* + ID_VENDOR_FROM_DATABASE=First International Computer, Inc. + +usb:v05B1p1389* + ID_MODEL_FROM_DATABASE=Bluetooth Wireless Adapter + +usb:v05B4* + ID_VENDOR_FROM_DATABASE=LG Semicon Co., Ltd + +usb:v05B4p4857* + ID_MODEL_FROM_DATABASE=M-Any DAH-210 + +usb:v05B4p6001* + ID_MODEL_FROM_DATABASE=HYUNDAI GDS30C6001 SSFDC / MMC I/F Controller + +usb:v05B5* + ID_VENDOR_FROM_DATABASE=Dialogic Corp. + +usb:v05B6* + ID_VENDOR_FROM_DATABASE=Proxima Corp. + +usb:v05B7* + ID_VENDOR_FROM_DATABASE=Medianix Semiconductor, Inc. + +usb:v05B8* + ID_VENDOR_FROM_DATABASE=Agiler, Inc. + +usb:v05B8p3002* + ID_MODEL_FROM_DATABASE=Scroll Mouse + +usb:v05B8p3223* + ID_MODEL_FROM_DATABASE=ISY Wireless Presenter + +usb:v05B9* + ID_VENDOR_FROM_DATABASE=Philips Research Laboratories + +usb:v05BA* + ID_VENDOR_FROM_DATABASE=DigitalPersona, Inc. + +usb:v05BAp0007* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v05BAp0008* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v05BAp000A* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v05BB* + ID_VENDOR_FROM_DATABASE=Grey Cell Systems + +usb:v05BC* + ID_VENDOR_FROM_DATABASE=3G Green Green Globe Co., Ltd + +usb:v05BCp0004* + ID_MODEL_FROM_DATABASE=Trackball + +usb:v05BD* + ID_VENDOR_FROM_DATABASE=RAFI GmbH & Co. KG + +usb:v05BE* + ID_VENDOR_FROM_DATABASE=Tyco Electronics (Raychem) + +usb:v05BF* + ID_VENDOR_FROM_DATABASE=S & S Research + +usb:v05C0* + ID_VENDOR_FROM_DATABASE=Keil Software + +usb:v05C1* + ID_VENDOR_FROM_DATABASE=Kawasaki Microelectronics, Inc. + +usb:v05C2* + ID_VENDOR_FROM_DATABASE=Media Phonics (Suisse) S.A. + +usb:v05C5* + ID_VENDOR_FROM_DATABASE=Digi International, Inc. + +usb:v05C5p0002* + ID_MODEL_FROM_DATABASE=AccelePort USB 2 + +usb:v05C5p0004* + ID_MODEL_FROM_DATABASE=AccelePort USB 4 + +usb:v05C5p0008* + ID_MODEL_FROM_DATABASE=AccelePort USB 8 + +usb:v05C6* + ID_VENDOR_FROM_DATABASE=Qualcomm, Inc. + +usb:v05C6p0114* + ID_MODEL_FROM_DATABASE=Select RW-200 CDMA Wireless Modem + +usb:v05C6p1000* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v05C6p3100* + ID_MODEL_FROM_DATABASE=CDMA Wireless Modem/Phone + +usb:v05C6p3196* + ID_MODEL_FROM_DATABASE=CDMA Wireless Modem + +usb:v05C6p3197* + ID_MODEL_FROM_DATABASE=CDMA Wireless Modem/Phone + +usb:v05C6p6000* + ID_MODEL_FROM_DATABASE=Siemens SG75 + +usb:v05C6p6503* + ID_MODEL_FROM_DATABASE=AnyData APE-540H + +usb:v05C6p6613* + ID_MODEL_FROM_DATABASE=Onda H600/N501HS ZTE MF330 + +usb:v05C6p6764* + ID_MODEL_FROM_DATABASE=A0001 Phone [OnePlus One] + +usb:v05C6p9000* + ID_MODEL_FROM_DATABASE=SIMCom SIM5218 modem + +usb:v05C6p9001* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v05C6p9002* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v05C6p9003* + ID_MODEL_FROM_DATABASE=Quectel UC20 + +usb:v05C6p9008* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v05C6p9018* + ID_MODEL_FROM_DATABASE=Qualcomm HSUSB Device + +usb:v05C6p9025* + ID_MODEL_FROM_DATABASE=Qualcomm HSUSB Device + +usb:v05C6p9201* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v05C6p9202* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v05C6p9203* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v05C6p9205* + ID_MODEL_FROM_DATABASE=Gobi 2000 + +usb:v05C6p9211* + ID_MODEL_FROM_DATABASE=Acer Gobi Wireless Modem (QDL mode) + +usb:v05C6p9212* + ID_MODEL_FROM_DATABASE=Acer Gobi Wireless Modem + +usb:v05C6p9214* + ID_MODEL_FROM_DATABASE=Acer Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9215* + ID_MODEL_FROM_DATABASE=Acer Gobi 2000 Wireless Modem + +usb:v05C6p9221* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v05C6p9222* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v05C6p9224* + ID_MODEL_FROM_DATABASE=Sony Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9225* + ID_MODEL_FROM_DATABASE=Sony Gobi 2000 Wireless Modem + +usb:v05C6p9231* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v05C6p9234* + ID_MODEL_FROM_DATABASE=Top Global Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9235* + ID_MODEL_FROM_DATABASE=Top Global Gobi 2000 Wireless Modem + +usb:v05C6p9244* + ID_MODEL_FROM_DATABASE=Samsung Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9245* + ID_MODEL_FROM_DATABASE=Samsung Gobi 2000 Wireless Modem + +usb:v05C6p9264* + ID_MODEL_FROM_DATABASE=Asus Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9265* + ID_MODEL_FROM_DATABASE=Asus Gobi 2000 Wireless Modem + +usb:v05C6p9274* + ID_MODEL_FROM_DATABASE=iRex Technologies Gobi 2000 Wireless Modem (QDL mode) + +usb:v05C6p9275* + ID_MODEL_FROM_DATABASE=iRex Technologies Gobi 2000 Wireless Modem + +usb:v05C7* + ID_VENDOR_FROM_DATABASE=Qtronix Corp. + +usb:v05C7p0113* + ID_MODEL_FROM_DATABASE=PC Line Mouse + +usb:v05C7p1001* + ID_MODEL_FROM_DATABASE=Lynx Mouse + +usb:v05C7p2001* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v05C7p2011* + ID_MODEL_FROM_DATABASE=SCorpius Keyboard + +usb:v05C7p6001* + ID_MODEL_FROM_DATABASE=Ten-Keypad + +usb:v05C8* + ID_VENDOR_FROM_DATABASE=Cheng Uei Precision Industry Co., Ltd (Foxlink) + +usb:v05C8p0103* + ID_MODEL_FROM_DATABASE=FO13FF-65 PC-CAM + +usb:v05C8p010B* + ID_MODEL_FROM_DATABASE=Webcam (UVC) + +usb:v05C8p021A* + ID_MODEL_FROM_DATABASE=HP Webcam + +usb:v05C8p0318* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v05C8p0361* + ID_MODEL_FROM_DATABASE=SunplusIT INC. HP Truevision HD Webcam + +usb:v05C8p036E* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v05C8p0403* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v05C8p041B* + ID_MODEL_FROM_DATABASE=HP 2.0MP High Definition Webcam + +usb:v05C9* + ID_VENDOR_FROM_DATABASE=Semtech Corp. + +usb:v05CA* + ID_VENDOR_FROM_DATABASE=Ricoh Co., Ltd + +usb:v05CAp0101* + ID_MODEL_FROM_DATABASE=RDC-5300 Camera + +usb:v05CAp0325* + ID_MODEL_FROM_DATABASE=Caplio GX (ptp) + +usb:v05CAp032D* + ID_MODEL_FROM_DATABASE=Caplio GX 8 (ptp) + +usb:v05CAp032F* + ID_MODEL_FROM_DATABASE=Caplio R3 (ptp) + +usb:v05CAp03A1* + ID_MODEL_FROM_DATABASE=IS200e + +usb:v05CAp0403* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v05CAp0405* + ID_MODEL_FROM_DATABASE=Type 101 + +usb:v05CAp0406* + ID_MODEL_FROM_DATABASE=Type 102 + +usb:v05CAp1803* + ID_MODEL_FROM_DATABASE=V5 camera [R5U870] + +usb:v05CAp1810* + ID_MODEL_FROM_DATABASE=Pavilion Webcam [R5U870] + +usb:v05CAp1812* + ID_MODEL_FROM_DATABASE=Pavilion Webcam + +usb:v05CAp1814* + ID_MODEL_FROM_DATABASE=HD Webcam + +usb:v05CAp1815* + ID_MODEL_FROM_DATABASE=Dell Laptop Integrated Webcam + +usb:v05CAp1820* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v05CAp1830* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC2 [R5U870] + +usb:v05CAp1832* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC3 [R5U870] + +usb:v05CAp1833* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC2 [R5U870] + +usb:v05CAp1834* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC2 [R5U870] + +usb:v05CAp1835* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC5 [R5U870] + +usb:v05CAp1836* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC4 [R5U870] + +usb:v05CAp1837* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC4 [R5U870] + +usb:v05CAp1839* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC6 [R5U870] + +usb:v05CAp183A* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC7 [R5U870] + +usb:v05CAp183B* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC8 [R5U870] + +usb:v05CAp183D* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Webcam + +usb:v05CAp183E* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC9 [R5U870] + +usb:v05CAp1841* + ID_MODEL_FROM_DATABASE=Fujitsu F01/ Lifebook U810 [R5U870] + +usb:v05CAp1870* + ID_MODEL_FROM_DATABASE=Webcam 1000 + +usb:v05CAp18B0* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Webcam + +usb:v05CAp18B1* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Webcam + +usb:v05CAp18B3* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Webcam + +usb:v05CAp18B5* + ID_MODEL_FROM_DATABASE=Sony Vaio Integrated Webcam + +usb:v05CAp2201* + ID_MODEL_FROM_DATABASE=RDC-7 Camera + +usb:v05CAp2202* + ID_MODEL_FROM_DATABASE=Caplio RR30 + +usb:v05CAp2203* + ID_MODEL_FROM_DATABASE=Caplio 300G + +usb:v05CAp2204* + ID_MODEL_FROM_DATABASE=Caplio G3 + +usb:v05CAp2205* + ID_MODEL_FROM_DATABASE=Caplio RR30 / Medion MD 6126 Camera + +usb:v05CAp2206* + ID_MODEL_FROM_DATABASE=Konica DG-3Z + +usb:v05CAp2207* + ID_MODEL_FROM_DATABASE=Caplio Pro G3 + +usb:v05CAp2208* + ID_MODEL_FROM_DATABASE=Caplio G4 + +usb:v05CAp2209* + ID_MODEL_FROM_DATABASE=Caplio 400G wide + +usb:v05CAp220A* + ID_MODEL_FROM_DATABASE=KONICA MINOLTA DG-4Wide + +usb:v05CAp220B* + ID_MODEL_FROM_DATABASE=Caplio RX + +usb:v05CAp220C* + ID_MODEL_FROM_DATABASE=Caplio GX + +usb:v05CAp220D* + ID_MODEL_FROM_DATABASE=Caplio R1/RZ1 + +usb:v05CAp220E* + ID_MODEL_FROM_DATABASE=Sea & Sea 5000G + +usb:v05CAp220F* + ID_MODEL_FROM_DATABASE=Rollei dr5 / Rollei dr5 (PTP mode) + +usb:v05CAp2211* + ID_MODEL_FROM_DATABASE=Caplio R1S + +usb:v05CAp2212* + ID_MODEL_FROM_DATABASE=Caplio R1v Camera + +usb:v05CAp2213* + ID_MODEL_FROM_DATABASE=Caplio R2 + +usb:v05CAp2214* + ID_MODEL_FROM_DATABASE=Caplio GX 8 + +usb:v05CAp2215* + ID_MODEL_FROM_DATABASE=DSC 725 + +usb:v05CAp2216* + ID_MODEL_FROM_DATABASE=Caplio R3 + +usb:v05CAp2222* + ID_MODEL_FROM_DATABASE=RDC-i500 + +usb:v05CB* + ID_VENDOR_FROM_DATABASE=PowerVision Technologies, Inc. + +usb:v05CBp1483* + ID_MODEL_FROM_DATABASE=PV8630 interface (scanners, webcams) + +usb:v05CC* + ID_VENDOR_FROM_DATABASE=ELSA AG + +usb:v05CCp2100* + ID_MODEL_FROM_DATABASE=MicroLink ISDN Office + +usb:v05CCp2219* + ID_MODEL_FROM_DATABASE=MicroLink ISDN + +usb:v05CCp2265* + ID_MODEL_FROM_DATABASE=MicroLink 56k + +usb:v05CCp2267* + ID_MODEL_FROM_DATABASE=MicroLink 56k (V.250) + +usb:v05CCp2280* + ID_MODEL_FROM_DATABASE=MicroLink 56k Fun + +usb:v05CCp3000* + ID_MODEL_FROM_DATABASE=Micolink USB2Ethernet [pegasus] + +usb:v05CCp3100* + ID_MODEL_FROM_DATABASE=AirLancer USB-11 + +usb:v05CCp3363* + ID_MODEL_FROM_DATABASE=MicroLink ADSL Fun + +usb:v05CD* + ID_VENDOR_FROM_DATABASE=Silicom, Ltd + +usb:v05CE* + ID_VENDOR_FROM_DATABASE=sci-worx GmbH + +usb:v05CF* + ID_VENDOR_FROM_DATABASE=Sung Forn Co., Ltd + +usb:v05D0* + ID_VENDOR_FROM_DATABASE=GE Medical Systems Lunar + +usb:v05D1* + ID_VENDOR_FROM_DATABASE=Brainboxes, Ltd + +usb:v05D1p0003* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter BL-554 + +usb:v05D2* + ID_VENDOR_FROM_DATABASE=Wave Systems Corp. + +usb:v05D3* + ID_VENDOR_FROM_DATABASE=Tohoku Ricoh Co., Ltd + +usb:v05D5* + ID_VENDOR_FROM_DATABASE=Super Gate Technology Co., Ltd + +usb:v05D6* + ID_VENDOR_FROM_DATABASE=Philips Semiconductors, CICT + +usb:v05D7* + ID_VENDOR_FROM_DATABASE=Thomas & Betts Corp. + +usb:v05D7p0099* + ID_MODEL_FROM_DATABASE=10Mbps Ethernet [klsi] + +usb:v05D8* + ID_VENDOR_FROM_DATABASE=Ultima Electronics Corp. + +usb:v05D8p4001* + ID_MODEL_FROM_DATABASE=Artec Ultima 2000 + +usb:v05D8p4002* + ID_MODEL_FROM_DATABASE=Artec Ultima 2000 (GT6801 based)/Lifetec LT9385/ScanMagic 1200 UB Plus Scanner + +usb:v05D8p4003* + ID_MODEL_FROM_DATABASE=Artec E+ 48U + +usb:v05D8p4004* + ID_MODEL_FROM_DATABASE=Artec E+ Pro + +usb:v05D8p4005* + ID_MODEL_FROM_DATABASE=MEM48U + +usb:v05D8p4006* + ID_MODEL_FROM_DATABASE=TRUST EASY WEBSCAN 19200 + +usb:v05D8p4007* + ID_MODEL_FROM_DATABASE=TRUST 240H EASY WEBSCAN GOLD + +usb:v05D8p4008* + ID_MODEL_FROM_DATABASE=Trust Easy Webscan 19200 + +usb:v05D8p4009* + ID_MODEL_FROM_DATABASE=Umax Astraslim + +usb:v05D8p4013* + ID_MODEL_FROM_DATABASE=IT Scan 1200 + +usb:v05D8p8105* + ID_MODEL_FROM_DATABASE=Artec T1 USB TVBOX (cold) + +usb:v05D8p8106* + ID_MODEL_FROM_DATABASE=Artec T1 USB TVBOX (warm) + +usb:v05D8p8107* + ID_MODEL_FROM_DATABASE=Artec T1 USB TVBOX with AN2235 (cold) + +usb:v05D8p8108* + ID_MODEL_FROM_DATABASE=Artec T1 USB TVBOX with AN2235 (warm) + +usb:v05D8p8109* + ID_MODEL_FROM_DATABASE=Artec T1 USB2.0 TVBOX (cold + +usb:v05D9* + ID_VENDOR_FROM_DATABASE=Axiohm Transaction Solutions + +usb:v05D9pA225* + ID_MODEL_FROM_DATABASE=A225 Printer + +usb:v05D9pA758* + ID_MODEL_FROM_DATABASE=A758 Printer + +usb:v05D9pA794* + ID_MODEL_FROM_DATABASE=A794 Printer + +usb:v05DA* + ID_VENDOR_FROM_DATABASE=Microtek International, Inc. + +usb:v05DAp0091* + ID_MODEL_FROM_DATABASE=ScanMaker X6u + +usb:v05DAp0093* + ID_MODEL_FROM_DATABASE=ScanMaker V6USL + +usb:v05DAp0094* + ID_MODEL_FROM_DATABASE=Phantom 336CX/C3 + +usb:v05DAp0099* + ID_MODEL_FROM_DATABASE=ScanMaker X6/X6U + +usb:v05DAp009A* + ID_MODEL_FROM_DATABASE=Phantom C6 + +usb:v05DAp00A0* + ID_MODEL_FROM_DATABASE=Phantom 336CX/C3 (#2) + +usb:v05DAp00A3* + ID_MODEL_FROM_DATABASE=ScanMaker V6USL + +usb:v05DAp00AC* + ID_MODEL_FROM_DATABASE=ScanMaker V6UL + +usb:v05DAp00B6* + ID_MODEL_FROM_DATABASE=ScanMaker V6UPL + +usb:v05DAp00EF* + ID_MODEL_FROM_DATABASE=ScanMaker V6UPL + +usb:v05DAp1006* + ID_MODEL_FROM_DATABASE=Jenoptik JD350 entrance + +usb:v05DAp1011* + ID_MODEL_FROM_DATABASE=NHJ Che-ez! Kiss Digital Camera + +usb:v05DAp1018* + ID_MODEL_FROM_DATABASE=Digital Dream Enigma 1.3 + +usb:v05DAp1020* + ID_MODEL_FROM_DATABASE=Digital Dream l'espion xtra + +usb:v05DAp1025* + ID_MODEL_FROM_DATABASE=Take-it Still Camera Device + +usb:v05DAp1026* + ID_MODEL_FROM_DATABASE=Take-it + +usb:v05DAp1043* + ID_MODEL_FROM_DATABASE=Take-It 1300 DSC Bulk Driver + +usb:v05DAp1045* + ID_MODEL_FROM_DATABASE=Take-it D1 + +usb:v05DAp1047* + ID_MODEL_FROM_DATABASE=Take-it Camera Composite Device + +usb:v05DAp1048* + ID_MODEL_FROM_DATABASE=Take-it Q3 + +usb:v05DAp1049* + ID_MODEL_FROM_DATABASE=3M Still Camera Device + +usb:v05DAp1051* + ID_MODEL_FROM_DATABASE=Camcorder Series + +usb:v05DAp1052* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v05DAp1053* + ID_MODEL_FROM_DATABASE=Take-it DV Composite Device + +usb:v05DAp1054* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v05DAp1055* + ID_MODEL_FROM_DATABASE=Digital Camera Series(536) + +usb:v05DAp1056* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v05DAp1057* + ID_MODEL_FROM_DATABASE=Take-it DSC Camera Device(536) + +usb:v05DAp1058* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v05DAp1059* + ID_MODEL_FROM_DATABASE=Camcorder DSC Series + +usb:v05DAp1060* + ID_MODEL_FROM_DATABASE=Microtek Take-it MV500 + +usb:v05DAp2007* + ID_MODEL_FROM_DATABASE=ArtixScan DI 1210 + +usb:v05DAp200C* + ID_MODEL_FROM_DATABASE=1394_USB2 Scanner + +usb:v05DAp200E* + ID_MODEL_FROM_DATABASE=ArtixScan DI 810 + +usb:v05DAp2017* + ID_MODEL_FROM_DATABASE=UF ICE Scanner + +usb:v05DAp201C* + ID_MODEL_FROM_DATABASE=4800 Scanner + +usb:v05DAp201D* + ID_MODEL_FROM_DATABASE=ArtixScan DI 1610 + +usb:v05DAp201F* + ID_MODEL_FROM_DATABASE=4800 Scanner-ICE + +usb:v05DAp202E* + ID_MODEL_FROM_DATABASE=ArtixScan DI 2020 + +usb:v05DAp208B* + ID_MODEL_FROM_DATABASE=ScanMaker 6800 + +usb:v05DAp208F* + ID_MODEL_FROM_DATABASE=ArtixScan DI 2010 + +usb:v05DAp209E* + ID_MODEL_FROM_DATABASE=ScanMaker 4700LP + +usb:v05DAp20A7* + ID_MODEL_FROM_DATABASE=ScanMaker 5600 + +usb:v05DAp20B0* + ID_MODEL_FROM_DATABASE=ScanMaker X12USL + +usb:v05DAp20B1* + ID_MODEL_FROM_DATABASE=ScanMaker 8700 + +usb:v05DAp20B4* + ID_MODEL_FROM_DATABASE=ScanMaker 4700 + +usb:v05DAp20BD* + ID_MODEL_FROM_DATABASE=ScanMaker 5700 + +usb:v05DAp20C9* + ID_MODEL_FROM_DATABASE=ScanMaker 6700 + +usb:v05DAp20D2* + ID_MODEL_FROM_DATABASE=Microtek ArtixScan 1800f + +usb:v05DAp20D6* + ID_MODEL_FROM_DATABASE=PS4000 + +usb:v05DAp20DE* + ID_MODEL_FROM_DATABASE=ScanMaker 9800XL + +usb:v05DAp20E0* + ID_MODEL_FROM_DATABASE=ScanMaker 9700XL + +usb:v05DAp20ED* + ID_MODEL_FROM_DATABASE=ScanMaker 4700 + +usb:v05DAp20EE* + ID_MODEL_FROM_DATABASE=Micortek ScanMaker X12USL + +usb:v05DAp2838* + ID_MODEL_FROM_DATABASE=RT2832U + +usb:v05DAp3008* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v05DAp300A* + ID_MODEL_FROM_DATABASE=4800 ICE Scanner + +usb:v05DAp300B* + ID_MODEL_FROM_DATABASE=4800 Scanner + +usb:v05DAp300F* + ID_MODEL_FROM_DATABASE=MiniScan C5 + +usb:v05DAp3020* + ID_MODEL_FROM_DATABASE=4800dpi Scanner + +usb:v05DAp3021* + ID_MODEL_FROM_DATABASE=1200dpi Scanner + +usb:v05DAp3022* + ID_MODEL_FROM_DATABASE=Scanner 4800dpi + +usb:v05DAp3023* + ID_MODEL_FROM_DATABASE=USB1200II Scanner + +usb:v05DAp30C1* + ID_MODEL_FROM_DATABASE=USB600 Scanner + +usb:v05DAp30CE* + ID_MODEL_FROM_DATABASE=ScanMaker 3800 + +usb:v05DAp30CF* + ID_MODEL_FROM_DATABASE=ScanMaker 4800 + +usb:v05DAp30D4* + ID_MODEL_FROM_DATABASE=USB1200 Scanner + +usb:v05DAp30D8* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v05DAp30D9* + ID_MODEL_FROM_DATABASE=USB2400 Scanner + +usb:v05DAp30E4* + ID_MODEL_FROM_DATABASE=ScanMaker 4100 + +usb:v05DAp30E5* + ID_MODEL_FROM_DATABASE=USB3200 Scanner + +usb:v05DAp30E6* + ID_MODEL_FROM_DATABASE=ScanMaker i320 + +usb:v05DAp40B3* + ID_MODEL_FROM_DATABASE=ScanMaker 3600 + +usb:v05DAp40B8* + ID_MODEL_FROM_DATABASE=ScanMaker 3700 + +usb:v05DAp40C7* + ID_MODEL_FROM_DATABASE=ScanMaker 4600 + +usb:v05DAp40CA* + ID_MODEL_FROM_DATABASE=ScanMaker 3600 + +usb:v05DAp40CB* + ID_MODEL_FROM_DATABASE=ScanMaker 3700 + +usb:v05DAp40DD* + ID_MODEL_FROM_DATABASE=ScanMaker 3750i + +usb:v05DAp40FF* + ID_MODEL_FROM_DATABASE=ScanMaker 3600 + +usb:v05DAp5003* + ID_MODEL_FROM_DATABASE=Goya + +usb:v05DAp5013* + ID_MODEL_FROM_DATABASE=3200 Scanner + +usb:v05DAp6072* + ID_MODEL_FROM_DATABASE=XT-3500 A4 HD Scanner + +usb:v05DAp80A3* + ID_MODEL_FROM_DATABASE=ScanMaker V6USL (#2) + +usb:v05DAp80AC* + ID_MODEL_FROM_DATABASE=ScanMaker V6UL/SpicyU + +usb:v05DB* + ID_VENDOR_FROM_DATABASE=Sun Corp. (Suntac?) + +usb:v05DBp0003* + ID_MODEL_FROM_DATABASE=SUNTAC U-Cable type D2 + +usb:v05DBp0005* + ID_MODEL_FROM_DATABASE=SUNTAC U-Cable type P1 + +usb:v05DBp0009* + ID_MODEL_FROM_DATABASE=SUNTAC Slipper U + +usb:v05DBp000A* + ID_MODEL_FROM_DATABASE=SUNTAC Ir-Trinity + +usb:v05DBp000B* + ID_MODEL_FROM_DATABASE=SUNTAC U-Cable type A3 + +usb:v05DBp0011* + ID_MODEL_FROM_DATABASE=SUNTAC U-Cable type A4 + +usb:v05DC* + ID_VENDOR_FROM_DATABASE=Lexar Media, Inc. + +usb:v05DCp0001* + ID_MODEL_FROM_DATABASE=jumpSHOT CompactFlash Reader + +usb:v05DCp0002* + ID_MODEL_FROM_DATABASE=JumpShot + +usb:v05DCp0003* + ID_MODEL_FROM_DATABASE=JumpShot + +usb:v05DCp0080* + ID_MODEL_FROM_DATABASE=Jumpdrive Secure 64MB + +usb:v05DCp0081* + ID_MODEL_FROM_DATABASE=RBC Compact Flash Drive + +usb:v05DCp00A7* + ID_MODEL_FROM_DATABASE=JumpDrive Impact + +usb:v05DCp0100* + ID_MODEL_FROM_DATABASE=JumpDrive PRO + +usb:v05DCp0200* + ID_MODEL_FROM_DATABASE=JumpDrive 2.0 Pro + +usb:v05DCp0300* + ID_MODEL_FROM_DATABASE=Jumpdrive Geysr + +usb:v05DCp0301* + ID_MODEL_FROM_DATABASE=JumpDrive Classic + +usb:v05DCp0302* + ID_MODEL_FROM_DATABASE=JD Micro + +usb:v05DCp0303* + ID_MODEL_FROM_DATABASE=JD Micro Pro + +usb:v05DCp0304* + ID_MODEL_FROM_DATABASE=JD Secure II + +usb:v05DCp0310* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0311* + ID_MODEL_FROM_DATABASE=JumpDrive Classic + +usb:v05DCp0312* + ID_MODEL_FROM_DATABASE=JD Micro + +usb:v05DCp0313* + ID_MODEL_FROM_DATABASE=JD Micro Pro + +usb:v05DCp0320* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0321* + ID_MODEL_FROM_DATABASE=JD Micro + +usb:v05DCp0322* + ID_MODEL_FROM_DATABASE=JD Micro Pro + +usb:v05DCp0323* + ID_MODEL_FROM_DATABASE=UFC + +usb:v05DCp0330* + ID_MODEL_FROM_DATABASE=JumpDrive Expression + +usb:v05DCp0340* + ID_MODEL_FROM_DATABASE=JumpDrive TAD + +usb:v05DCp0350* + ID_MODEL_FROM_DATABASE=Express Card + +usb:v05DCp0400* + ID_MODEL_FROM_DATABASE=UFDC + +usb:v05DCp0401* + ID_MODEL_FROM_DATABASE=UFDC + +usb:v05DCp0403* + ID_MODEL_FROM_DATABASE=Locked B Device + +usb:v05DCp0405* + ID_MODEL_FROM_DATABASE=Locked C Device + +usb:v05DCp0407* + ID_MODEL_FROM_DATABASE=Locked D Device + +usb:v05DCp0409* + ID_MODEL_FROM_DATABASE=Locked E Device + +usb:v05DCp040B* + ID_MODEL_FROM_DATABASE=Locked F Device + +usb:v05DCp040D* + ID_MODEL_FROM_DATABASE=Locked G Device + +usb:v05DCp040F* + ID_MODEL_FROM_DATABASE=Locked H Device + +usb:v05DCp0410* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0411* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0413* + ID_MODEL_FROM_DATABASE=Locked J Device + +usb:v05DCp0415* + ID_MODEL_FROM_DATABASE=Locked K Device + +usb:v05DCp0417* + ID_MODEL_FROM_DATABASE=Locked L Device + +usb:v05DCp0419* + ID_MODEL_FROM_DATABASE=Locked M Device + +usb:v05DCp041B* + ID_MODEL_FROM_DATABASE=Locked N Device + +usb:v05DCp041D* + ID_MODEL_FROM_DATABASE=Locked O Device + +usb:v05DCp041F* + ID_MODEL_FROM_DATABASE=Locked P Device + +usb:v05DCp0420* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0421* + ID_MODEL_FROM_DATABASE=JumpDrive + +usb:v05DCp0423* + ID_MODEL_FROM_DATABASE=Locked R Device + +usb:v05DCp0425* + ID_MODEL_FROM_DATABASE=Locked S Device + +usb:v05DCp0427* + ID_MODEL_FROM_DATABASE=Locked T Device + +usb:v05DCp0429* + ID_MODEL_FROM_DATABASE=Locked U Device + +usb:v05DCp042B* + ID_MODEL_FROM_DATABASE=Locked V Device + +usb:v05DCp042D* + ID_MODEL_FROM_DATABASE=Locked W Device + +usb:v05DCp042F* + ID_MODEL_FROM_DATABASE=Locked X Device + +usb:v05DCp0431* + ID_MODEL_FROM_DATABASE=Locked Y Device + +usb:v05DCp0433* + ID_MODEL_FROM_DATABASE=Locked Z Device + +usb:v05DCp4D02* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v05DCp4D12* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v05DCp4D30* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v05DCpA209* + ID_MODEL_FROM_DATABASE=JumpDrive S70 + +usb:v05DCpA300* + ID_MODEL_FROM_DATABASE=JumpDrive2 + +usb:v05DCpA400* + ID_MODEL_FROM_DATABASE=JumpDrive trade; Pro 40-501 + +usb:v05DCpA410* + ID_MODEL_FROM_DATABASE=JumpDrive 128MB/256MB + +usb:v05DCpA411* + ID_MODEL_FROM_DATABASE=JumpDrive Traveler + +usb:v05DCpA420* + ID_MODEL_FROM_DATABASE=JumpDrive Pro + +usb:v05DCpA421* + ID_MODEL_FROM_DATABASE=JumpDrive Pro II + +usb:v05DCpA422* + ID_MODEL_FROM_DATABASE=JumpDrive Micro Pro + +usb:v05DCpA430* + ID_MODEL_FROM_DATABASE=JumpDrive Secure + +usb:v05DCpA431* + ID_MODEL_FROM_DATABASE=JumpDrive Secure II + +usb:v05DCpA432* + ID_MODEL_FROM_DATABASE=JumpDrive Classic + +usb:v05DCpA440* + ID_MODEL_FROM_DATABASE=JumpDrive Lightning + +usb:v05DCpA450* + ID_MODEL_FROM_DATABASE=JumpDrive TouchGuard + +usb:v05DCpA460* + ID_MODEL_FROM_DATABASE=JD Mercury + +usb:v05DCpA501* + ID_MODEL_FROM_DATABASE=JumpDrive Classic + +usb:v05DCpA510* + ID_MODEL_FROM_DATABASE=JumpDrive Sport + +usb:v05DCpA530* + ID_MODEL_FROM_DATABASE=JumpDrive Expression + +usb:v05DCpA531* + ID_MODEL_FROM_DATABASE=JumpDrive Secure II + +usb:v05DCpA560* + ID_MODEL_FROM_DATABASE=JumpDrive FireFly + +usb:v05DCpA701* + ID_MODEL_FROM_DATABASE=JumpDrive FireFly + +usb:v05DCpA731* + ID_MODEL_FROM_DATABASE=JumpDrive FireFly + +usb:v05DCpA762* + ID_MODEL_FROM_DATABASE=JumpDrive FireFly + +usb:v05DCpA768* + ID_MODEL_FROM_DATABASE=JumpDrive Retrax + +usb:v05DCpA790* + ID_MODEL_FROM_DATABASE=JumpDrive 2GB + +usb:v05DCpA811* + ID_MODEL_FROM_DATABASE=16GB Gizmo! + +usb:v05DCpA813* + ID_MODEL_FROM_DATABASE=16gB flash thumb drive + +usb:v05DCpA815* + ID_MODEL_FROM_DATABASE=JumpDrive V10 + +usb:v05DCpA833* + ID_MODEL_FROM_DATABASE=JumpDrive S23 64GB + +usb:v05DCpB002* + ID_MODEL_FROM_DATABASE=USB CF Reader + +usb:v05DCpB018* + ID_MODEL_FROM_DATABASE=Multi-Card Reader + +usb:v05DCpB047* + ID_MODEL_FROM_DATABASE=SDHC Reader [RW047-7000] + +usb:v05DCpBA02* + ID_MODEL_FROM_DATABASE=Workflow CFR1 + +usb:v05DCpBA0A* + ID_MODEL_FROM_DATABASE=Workflow DD512 + +usb:v05DCpC753* + ID_MODEL_FROM_DATABASE=JumpDrive TwistTurn + +usb:v05DCpC75C* + ID_MODEL_FROM_DATABASE=JumpDrive V10 + +usb:v05DD* + ID_VENDOR_FROM_DATABASE=Delta Electronics, Inc. + +usb:v05DDpFF31* + ID_MODEL_FROM_DATABASE=AWU-120 + +usb:v05DDpFF32* + ID_MODEL_FROM_DATABASE=FriendlyNET AeroLAN AL2011 + +usb:v05DDpFF35* + ID_MODEL_FROM_DATABASE=PCW 100 - Wireless 802.11b Adapter + +usb:v05DDpFF91* + ID_MODEL_FROM_DATABASE=2Wire PC Port Phoneline 10Mbps Adapter + +usb:v05DF* + ID_VENDOR_FROM_DATABASE=Silicon Vision, Inc. + +usb:v05E0* + ID_VENDOR_FROM_DATABASE=Symbol Technologies + +usb:v05E0p0700* + ID_MODEL_FROM_DATABASE=Bar Code Scanner (CS1504) + +usb:v05E0p0800* + ID_MODEL_FROM_DATABASE=Spectrum24 Wireless LAN Adapter + +usb:v05E0p1200* + ID_MODEL_FROM_DATABASE=Bar Code Scanner + +usb:v05E0p1701* + ID_MODEL_FROM_DATABASE=Bar Code Scanner (CDC) + +usb:v05E0p1900* + ID_MODEL_FROM_DATABASE=SNAPI Imaging Device + +usb:v05E0p2000* + ID_MODEL_FROM_DATABASE=MC3090 Rugged Mobile Computer + +usb:v05E0p200D* + ID_MODEL_FROM_DATABASE=MC70 Rugged Mobile Computer + +usb:v05E1* + ID_VENDOR_FROM_DATABASE=Syntek Semiconductor Co., Ltd + +usb:v05E1p0100* + ID_MODEL_FROM_DATABASE=802.11g + Bluetooth Wireless Adapter + +usb:v05E1p0408* + ID_MODEL_FROM_DATABASE=STK1160 Video Capture Device + +usb:v05E1p0500* + ID_MODEL_FROM_DATABASE=DC-112X Webcam + +usb:v05E1p0501* + ID_MODEL_FROM_DATABASE=DC-1125 Webcam + +usb:v05E1p0890* + ID_MODEL_FROM_DATABASE=STK011 Camera + +usb:v05E1p0892* + ID_MODEL_FROM_DATABASE=STK013 Camera + +usb:v05E1p0895* + ID_MODEL_FROM_DATABASE=STK016 Camera + +usb:v05E1p0896* + ID_MODEL_FROM_DATABASE=STK017 Camera + +usb:v05E1p2010* + ID_MODEL_FROM_DATABASE=ARCTIC Sound P261 Headphones + +usb:v05E2* + ID_VENDOR_FROM_DATABASE=ElecVision, Inc. + +usb:v05E3* + ID_VENDOR_FROM_DATABASE=Genesys Logic, Inc. + +usb:v05E3p000A* + ID_MODEL_FROM_DATABASE=Keyboard with PS/2 Port + +usb:v05E3p000B* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v05E3p0100* + ID_MODEL_FROM_DATABASE=Nintendo Game Boy Advance SP + +usb:v05E3p0120* + ID_MODEL_FROM_DATABASE=Pacific Image Electronics PrimeFilm 1800u slide/negative scanner + +usb:v05E3p0131* + ID_MODEL_FROM_DATABASE=CF/SM Reader/Writer + +usb:v05E3p0142* + ID_MODEL_FROM_DATABASE=Multiple Slides Scanner-3600 + +usb:v05E3p0143* + ID_MODEL_FROM_DATABASE=Multiple Frames Film Scanner-36series + +usb:v05E3p0180* + ID_MODEL_FROM_DATABASE=Plustek Scanner + +usb:v05E3p0182* + ID_MODEL_FROM_DATABASE=Wize Media 1000 + +usb:v05E3p0189* + ID_MODEL_FROM_DATABASE=ScanJet 4600 series + +usb:v05E3p018A* + ID_MODEL_FROM_DATABASE=Xerox 6400 + +usb:v05E3p0300* + ID_MODEL_FROM_DATABASE=GLUSB98PT Parallel Port + +usb:v05E3p0301* + ID_MODEL_FROM_DATABASE=USB2LPT Cable Release2 + +usb:v05E3p0406* + ID_MODEL_FROM_DATABASE=Hub + +usb:v05E3p0501* + ID_MODEL_FROM_DATABASE=GL620USB Host-Host interface + +usb:v05E3p0502* + ID_MODEL_FROM_DATABASE=GL620USB-A GeneLink USB-USB Bridge + +usb:v05E3p0503* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v05E3p0504* + ID_MODEL_FROM_DATABASE=HID Keyboard Filter + +usb:v05E3p0604* + ID_MODEL_FROM_DATABASE=USB 1.1 Hub + +usb:v05E3p0605* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v05E3p0606* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub / D-Link DUB-H4 USB 2.0 Hub + +usb:v05E3p0607* + ID_MODEL_FROM_DATABASE=Logitech G110 Hub + +usb:v05E3p0608* + ID_MODEL_FROM_DATABASE=Hub + +usb:v05E3p0610* + ID_MODEL_FROM_DATABASE=4-port hub + +usb:v05E3p0612* + ID_MODEL_FROM_DATABASE=Hub + +usb:v05E3p0616* + ID_MODEL_FROM_DATABASE=hub + +usb:v05E3p0660* + ID_MODEL_FROM_DATABASE=USB 2.0 Hub + +usb:v05E3p0700* + ID_MODEL_FROM_DATABASE=SIIG US2256 CompactFlash Card Reader + +usb:v05E3p0701* + ID_MODEL_FROM_DATABASE=USB 2.0 IDE Adapter + +usb:v05E3p0702* + ID_MODEL_FROM_DATABASE=USB 2.0 IDE Adapter [GL811E] + +usb:v05E3p0703* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0704* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0705* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0706* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0707* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0708* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0709* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p070A* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v05E3p070B* + ID_MODEL_FROM_DATABASE=DMHS1B Rev 3 DFU Adapter + +usb:v05E3p070E* + ID_MODEL_FROM_DATABASE=USB 2.0 Card Reader + +usb:v05E3p070F* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v05E3p0710* + ID_MODEL_FROM_DATABASE=USB 2.0 33-in-1 Card Reader + +usb:v05E3p0711* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p0712* + ID_MODEL_FROM_DATABASE=Delkin Mass Storage Device + +usb:v05E3p0715* + ID_MODEL_FROM_DATABASE=USB 2.0 microSD Reader + +usb:v05E3p0716* + ID_MODEL_FROM_DATABASE=USB 2.0 Multislot Card Reader/Writer + +usb:v05E3p0717* + ID_MODEL_FROM_DATABASE=All-in-1 Card Reader + +usb:v05E3p0718* + ID_MODEL_FROM_DATABASE=IDE/SATA Adapter + +usb:v05E3p0719* + ID_MODEL_FROM_DATABASE=SATA adapter + +usb:v05E3p0722* + ID_MODEL_FROM_DATABASE=SD/MMC card reader + +usb:v05E3p0723* + ID_MODEL_FROM_DATABASE=GL827L SD/MMC/MS Flash Card Reader + +usb:v05E3p0726* + ID_MODEL_FROM_DATABASE=SD Card Reader + +usb:v05E3p0727* + ID_MODEL_FROM_DATABASE=microSD Reader/Writer + +usb:v05E3p0731* + ID_MODEL_FROM_DATABASE=GL3310 SATA 3Gb/s Bridge Controller + +usb:v05E3p0732* + ID_MODEL_FROM_DATABASE=All-in-One Cardreader + +usb:v05E3p0736* + ID_MODEL_FROM_DATABASE=microSD Reader/Writer + +usb:v05E3p0741* + ID_MODEL_FROM_DATABASE=microSD Card Reader + +usb:v05E3p0743* + ID_MODEL_FROM_DATABASE=SDXC and microSDXC CardReader + +usb:v05E3p0745* + ID_MODEL_FROM_DATABASE=Logilink CR0012 + +usb:v05E3p0751* + ID_MODEL_FROM_DATABASE=microSD Card Reader + +usb:v05E3p0760* + ID_MODEL_FROM_DATABASE=USB 2.0 Card Reader/Writer + +usb:v05E3p0761* + ID_MODEL_FROM_DATABASE=Genesys Mass Storage Device + +usb:v05E3p0780* + ID_MODEL_FROM_DATABASE=USBFS DFU Adapter + +usb:v05E3p07A0* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v05E3p0880* + ID_MODEL_FROM_DATABASE=Wasp (SL-6612) + +usb:v05E3p0927* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v05E3p1205* + ID_MODEL_FROM_DATABASE=Afilias Optical Mouse H3003 / Trust Optical USB MultiColour Mouse MI-2330 + +usb:v05E3pA700* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v05E3pF102* + ID_MODEL_FROM_DATABASE=VX7012 TV Box + +usb:v05E3pF103* + ID_MODEL_FROM_DATABASE=VX7012 TV Box + +usb:v05E3pF104* + ID_MODEL_FROM_DATABASE=VX7012 TV Box + +usb:v05E3pFD21* + ID_MODEL_FROM_DATABASE=3M TL20 Temperature Logger + +usb:v05E3pFE00* + ID_MODEL_FROM_DATABASE=Razer Mouse + +usb:v05E4* + ID_VENDOR_FROM_DATABASE=Red Wing Corp. + +usb:v05E5* + ID_VENDOR_FROM_DATABASE=Fuji Electric Co., Ltd + +usb:v05E6* + ID_VENDOR_FROM_DATABASE=Keithley Instruments + +usb:v05E8* + ID_VENDOR_FROM_DATABASE=ICC, Inc. + +usb:v05E9* + ID_VENDOR_FROM_DATABASE=Kawasaki LSI + +usb:v05E9p0008* + ID_MODEL_FROM_DATABASE=KL5KUSB101B Ethernet [klsi] + +usb:v05E9p0009* + ID_MODEL_FROM_DATABASE=Sony 10Mbps Ethernet [pegasus] + +usb:v05E9p000C* + ID_MODEL_FROM_DATABASE=USB-to-RS-232 + +usb:v05E9p000D* + ID_MODEL_FROM_DATABASE=USB-to-RS-232 + +usb:v05E9p0014* + ID_MODEL_FROM_DATABASE=RS-232 J104 + +usb:v05E9p0040* + ID_MODEL_FROM_DATABASE=Ethernet Adapter + +usb:v05E9p2008* + ID_MODEL_FROM_DATABASE=Ethernet Adapter + +usb:v05EB* + ID_VENDOR_FROM_DATABASE=FFC, Ltd + +usb:v05EC* + ID_VENDOR_FROM_DATABASE=COM21, Inc. + +usb:v05EE* + ID_VENDOR_FROM_DATABASE=Cytechinfo Inc. + +usb:v05EF* + ID_VENDOR_FROM_DATABASE=AVB, Inc. [anko?] + +usb:v05EFp020A* + ID_MODEL_FROM_DATABASE=Top Shot Pegasus Joystick + +usb:v05EFp8884* + ID_MODEL_FROM_DATABASE=Mag Turbo Force Wheel + +usb:v05EFp8888* + ID_MODEL_FROM_DATABASE=Top Shot Force Feedback Racing Wheel + +usb:v05F0* + ID_VENDOR_FROM_DATABASE=Canopus Co., Ltd + +usb:v05F0p0101* + ID_MODEL_FROM_DATABASE=DA-Port DAC + +usb:v05F1* + ID_VENDOR_FROM_DATABASE=Compass Communications + +usb:v05F2* + ID_VENDOR_FROM_DATABASE=Dexin Corp., Ltd + +usb:v05F2p0010* + ID_MODEL_FROM_DATABASE=AQ Mouse + +usb:v05F3* + ID_VENDOR_FROM_DATABASE=PI Engineering, Inc. + +usb:v05F3p0007* + ID_MODEL_FROM_DATABASE=Kinesis Advantage PRO MPC/USB Keyboard + +usb:v05F3p0081* + ID_MODEL_FROM_DATABASE=Kinesis Integrated Hub + +usb:v05F3p00FF* + ID_MODEL_FROM_DATABASE=VEC Footpedal + +usb:v05F3p0203* + ID_MODEL_FROM_DATABASE=Y-mouse Keyboard & Mouse Adapter + +usb:v05F3p020B* + ID_MODEL_FROM_DATABASE=PS2 Adapter + +usb:v05F3p0232* + ID_MODEL_FROM_DATABASE=X-Keys Switch Interface, Programming Mode + +usb:v05F3p0261* + ID_MODEL_FROM_DATABASE=X-Keys Switch Interface, SPLAT Mode + +usb:v05F3p0264* + ID_MODEL_FROM_DATABASE=X-Keys Switch Interface, Composite Mode + +usb:v05F5* + ID_VENDOR_FROM_DATABASE=Unixtar Technology, Inc. + +usb:v05F6* + ID_VENDOR_FROM_DATABASE=AOC International + +usb:v05F7* + ID_VENDOR_FROM_DATABASE=RFC Distribution(s) PTE, Ltd + +usb:v05F9* + ID_VENDOR_FROM_DATABASE=PSC Scanning, Inc. + +usb:v05F9p1104* + ID_MODEL_FROM_DATABASE=Magellan 2200VS + +usb:v05F9p1206* + ID_MODEL_FROM_DATABASE=Gryphon series (OEM mode) + +usb:v05F9p2202* + ID_MODEL_FROM_DATABASE=Point of Sale Handheld Scanner + +usb:v05F9p2206* + ID_MODEL_FROM_DATABASE=Gryphon series (keyboard emulation mode) + +usb:v05F9p220C* + ID_MODEL_FROM_DATABASE=Datalogic Gryphon GD4430 + +usb:v05F9p2601* + ID_MODEL_FROM_DATABASE=Datalogic Magellan 1000i Barcode Scanner + +usb:v05F9p2602* + ID_MODEL_FROM_DATABASE=Datalogic Magellan 1100i Barcode Scanner + +usb:v05F9p4204* + ID_MODEL_FROM_DATABASE=Gryphon series (RS-232 emulation mode) + +usb:v05F9p5204* + ID_MODEL_FROM_DATABASE=Datalogic Gryphon GFS4170 (config mode) + +usb:v05FA* + ID_VENDOR_FROM_DATABASE=Siemens Telecommunications Systems, Ltd + +usb:v05FAp3301* + ID_MODEL_FROM_DATABASE=Keyboard with PS/2 Mouse Port + +usb:v05FAp3302* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v05FAp3303* + ID_MODEL_FROM_DATABASE=Keyboard with PS/2 Mouse Port + +usb:v05FC* + ID_VENDOR_FROM_DATABASE=Harman + +usb:v05FCp0001* + ID_MODEL_FROM_DATABASE=Soundcraft Si Multi Digital Card + +usb:v05FCp7849* + ID_MODEL_FROM_DATABASE=Harman/Kardon SoundSticks + +usb:v05FD* + ID_VENDOR_FROM_DATABASE=InterAct, Inc. + +usb:v05FDp0239* + ID_MODEL_FROM_DATABASE=SV-239 HammerHead Digital + +usb:v05FDp0251* + ID_MODEL_FROM_DATABASE=Raider Pro + +usb:v05FDp0253* + ID_MODEL_FROM_DATABASE=ProPad 8 Digital + +usb:v05FDp0286* + ID_MODEL_FROM_DATABASE=SV-286 Cyclone Digital + +usb:v05FDp107A* + ID_MODEL_FROM_DATABASE=PowerPad Pro X-Box pad + +usb:v05FDp262A* + ID_MODEL_FROM_DATABASE=3dfx HammerHead FX + +usb:v05FDp262F* + ID_MODEL_FROM_DATABASE=HammerHead Fx + +usb:v05FDpDAAE* + ID_MODEL_FROM_DATABASE=Game Shark + +usb:v05FE* + ID_VENDOR_FROM_DATABASE=Chic Technology Corp. + +usb:v05FEp0001* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v05FEp0003* + ID_MODEL_FROM_DATABASE=Cypress USB Mouse + +usb:v05FEp0005* + ID_MODEL_FROM_DATABASE=Viewmaster 4D Browser Mouse + +usb:v05FEp0007* + ID_MODEL_FROM_DATABASE=Twinhead Mouse + +usb:v05FEp0009* + ID_MODEL_FROM_DATABASE=Inland Pro 4500/5000 Mouse + +usb:v05FEp0011* + ID_MODEL_FROM_DATABASE=Browser Mouse + +usb:v05FEp0014* + ID_MODEL_FROM_DATABASE=Gamepad + +usb:v05FEp1010* + ID_MODEL_FROM_DATABASE=Optical Wireless + +usb:v05FEp2001* + ID_MODEL_FROM_DATABASE=Microsoft Wireless Receiver 700 + +usb:v05FF* + ID_VENDOR_FROM_DATABASE=LeCroy Corp. + +usb:v0600* + ID_VENDOR_FROM_DATABASE=Barco Display Systems + +usb:v0601* + ID_VENDOR_FROM_DATABASE=Jazz Hipster Corp. + +usb:v0601p0003* + ID_MODEL_FROM_DATABASE=Internet Security Co., Ltd. SecureKey + +usb:v0602* + ID_VENDOR_FROM_DATABASE=Vista Imaging, Inc. + +usb:v0602p1001* + ID_MODEL_FROM_DATABASE=ViCam Webcam + +usb:v0603* + ID_VENDOR_FROM_DATABASE=Novatek Microelectronics Corp. + +usb:v0603p00F1* + ID_MODEL_FROM_DATABASE=Keyboard (Labtec Ultra Flat Keyboard) + +usb:v0603p00F2* + ID_MODEL_FROM_DATABASE=Keyboard (Labtec Ultra Flat Keyboard) + +usb:v0603p6871* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0604* + ID_VENDOR_FROM_DATABASE=Jean Co., Ltd + +usb:v0605* + ID_VENDOR_FROM_DATABASE=Anchor C&C Co., Ltd + +usb:v0606* + ID_VENDOR_FROM_DATABASE=Royal Information Electronics Co., Ltd + +usb:v0607* + ID_VENDOR_FROM_DATABASE=Bridge Information Co., Ltd + +usb:v0608* + ID_VENDOR_FROM_DATABASE=Genrad Ads + +usb:v0609* + ID_VENDOR_FROM_DATABASE=SMK Manufacturing, Inc. + +usb:v0609p031D* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0609p0322* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0609p0334* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0609pFF12* + ID_MODEL_FROM_DATABASE=SMK Bluetooth Device + +usb:v060A* + ID_VENDOR_FROM_DATABASE=Worthington Data Solutions, Inc. + +usb:v060B* + ID_VENDOR_FROM_DATABASE=Solid Year + +usb:v060Bp0001* + ID_MODEL_FROM_DATABASE=MacAlly Keyboard + +usb:v060Bp0230* + ID_MODEL_FROM_DATABASE=KSK-8003 UX Keyboard + +usb:v060Bp0540* + ID_MODEL_FROM_DATABASE=DeltaCo TB-106U Keyboard + +usb:v060Bp1006* + ID_MODEL_FROM_DATABASE=Japanese Keyboard - 260U + +usb:v060Bp2101* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v060Bp2231* + ID_MODEL_FROM_DATABASE=KSK-6001 UELX Keyboard + +usb:v060Bp2270* + ID_MODEL_FROM_DATABASE=Gigabyte K8100 Aivia Gaming Keyboard + +usb:v060Bp5253* + ID_MODEL_FROM_DATABASE=Thermaltake MEKA G-Unit Gaming Keyboard + +usb:v060Bp5811* + ID_MODEL_FROM_DATABASE=ACK-571U Wireless Keyboard + +usb:v060Bp5903* + ID_MODEL_FROM_DATABASE=Japanese Keyboard - 595U + +usb:v060Bp6001* + ID_MODEL_FROM_DATABASE=SolidTek USB 2p HUB + +usb:v060Bp6002* + ID_MODEL_FROM_DATABASE=SolidTek USB Keyboard + +usb:v060Bp6003* + ID_MODEL_FROM_DATABASE=Japanese Keyboard - 600HM + +usb:v060Bp6231* + ID_MODEL_FROM_DATABASE=Thermaltake eSPORTS Meka Keyboard + +usb:v060Bp8007* + ID_MODEL_FROM_DATABASE=P-W1G1F12 VER:1 [Macally MegaCam] + +usb:v060BpA001* + ID_MODEL_FROM_DATABASE=Maxwell Compact Pc PM3 + +usb:v060C* + ID_VENDOR_FROM_DATABASE=EEH Datalink GmbH + +usb:v060D* + ID_VENDOR_FROM_DATABASE=Auctor Corp. + +usb:v060E* + ID_VENDOR_FROM_DATABASE=Transmonde Technologies, Inc. + +usb:v060F* + ID_VENDOR_FROM_DATABASE=Joinsoon Electronics Mfg. Co., Ltd + +usb:v0610* + ID_VENDOR_FROM_DATABASE=Costar Electronics, Inc. + +usb:v0611* + ID_VENDOR_FROM_DATABASE=Totoku Electric Co., Ltd + +usb:v0613* + ID_VENDOR_FROM_DATABASE=TransAct Technologies, Inc. + +usb:v0614* + ID_VENDOR_FROM_DATABASE=Bio-Rad Laboratories + +usb:v0615* + ID_VENDOR_FROM_DATABASE=Quabbin Wire & Cable Co., Inc. + +usb:v0616* + ID_VENDOR_FROM_DATABASE=Future Techno Designs PVT, Ltd + +usb:v0617* + ID_VENDOR_FROM_DATABASE=Swiss Federal Insitute of Technology + +usb:v0618* + ID_VENDOR_FROM_DATABASE=MacAlly + +usb:v0618p0101* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0619* + ID_VENDOR_FROM_DATABASE=Seiko Instruments, Inc. + +usb:v0619p0101* + ID_MODEL_FROM_DATABASE=SLP-100 Driver + +usb:v0619p0102* + ID_MODEL_FROM_DATABASE=SLP-200 Driver + +usb:v0619p0103* + ID_MODEL_FROM_DATABASE=SLP-100N Driver + +usb:v0619p0104* + ID_MODEL_FROM_DATABASE=SLP-200N Driver + +usb:v0619p0105* + ID_MODEL_FROM_DATABASE=SLP-240 Driver + +usb:v0619p0501* + ID_MODEL_FROM_DATABASE=SLP-440 Driver + +usb:v0619p0502* + ID_MODEL_FROM_DATABASE=SLP-450 Driver + +usb:v061A* + ID_VENDOR_FROM_DATABASE=Veridicom International, Inc. + +usb:v061Ap0110* + ID_MODEL_FROM_DATABASE=5thSense Fingerprint Sensor + +usb:v061Ap0200* + ID_MODEL_FROM_DATABASE=FPS200 Fingerprint Sensor + +usb:v061Ap8200* + ID_MODEL_FROM_DATABASE=VKI-A Fingerprint Sensor/Flash Storage (dumb) + +usb:v061Ap9200* + ID_MODEL_FROM_DATABASE=VKI-B Fingerprint Sensor/Flash Storage (smart) + +usb:v061B* + ID_VENDOR_FROM_DATABASE=Promptus Communications, Inc. + +usb:v061C* + ID_VENDOR_FROM_DATABASE=Act Labs, Ltd + +usb:v061D* + ID_VENDOR_FROM_DATABASE=Quatech, Inc. + +usb:v061DpC020* + ID_MODEL_FROM_DATABASE=SSU-100 + +usb:v061E* + ID_VENDOR_FROM_DATABASE=Nissei Electric Co. + +usb:v061Ep0001* + ID_MODEL_FROM_DATABASE=nissei 128DE-USB - + +usb:v061Ep0010* + ID_MODEL_FROM_DATABASE=nissei 128DE-PNA - + +usb:v0620* + ID_VENDOR_FROM_DATABASE=Alaris, Inc. + +usb:v0620p0004* + ID_MODEL_FROM_DATABASE=QuickVideo weeCam + +usb:v0620p0007* + ID_MODEL_FROM_DATABASE=QuickVideo weeCam + +usb:v0620p000A* + ID_MODEL_FROM_DATABASE=QuickVideo weeCam + +usb:v0620p000B* + ID_MODEL_FROM_DATABASE=QuickVideo weeCam + +usb:v0621* + ID_VENDOR_FROM_DATABASE=ODU-Steckverbindungssysteme GmbH & Co. KG + +usb:v0622* + ID_VENDOR_FROM_DATABASE=Iotech, Inc. + +usb:v0623* + ID_VENDOR_FROM_DATABASE=Littelfuse, Inc. + +usb:v0624* + ID_VENDOR_FROM_DATABASE=Avocent Corp. + +usb:v0624p0248* + ID_MODEL_FROM_DATABASE=Virtual Hub + +usb:v0624p0249* + ID_MODEL_FROM_DATABASE=Virtual Keyboard/Mouse + +usb:v0624p0251* + ID_MODEL_FROM_DATABASE=Virtual Mass Storage + +usb:v0624p0294* + ID_MODEL_FROM_DATABASE=Dell 03R874 KVM dongle + +usb:v0624p0402* + ID_MODEL_FROM_DATABASE=Cisco Virtual Keyboard and Mouse + +usb:v0624p0403* + ID_MODEL_FROM_DATABASE=Cisco Virtual Mass Storage + +usb:v0625* + ID_VENDOR_FROM_DATABASE=TiMedia Technology Co., Ltd + +usb:v0626* + ID_VENDOR_FROM_DATABASE=Nippon Systems Development Co., Ltd + +usb:v0627* + ID_VENDOR_FROM_DATABASE=Adomax Technology Co., Ltd + +usb:v0628* + ID_VENDOR_FROM_DATABASE=Tasking Software, Inc. + +usb:v0629* + ID_VENDOR_FROM_DATABASE=Zida Technologies, Ltd + +usb:v062A* + ID_VENDOR_FROM_DATABASE=Creative Labs + +usb:v062Ap0000* + ID_MODEL_FROM_DATABASE=Optical mouse + +usb:v062Ap0001* + ID_MODEL_FROM_DATABASE=Notebook Optical Mouse + +usb:v062Ap0102* + ID_MODEL_FROM_DATABASE=Wireless Keyboard/Mouse Combo [MK1152WC] + +usb:v062Ap0201* + ID_MODEL_FROM_DATABASE=Defender Office Keyboard (K7310) S Zodiak KM-9010 + +usb:v062Ap0252* + ID_MODEL_FROM_DATABASE=Emerge Uni-retractable Laser Mouse + +usb:v062Ap2410* + ID_MODEL_FROM_DATABASE=Wireless PS3 gamepad + +usb:v062Ap3286* + ID_MODEL_FROM_DATABASE=Nano Receiver [Sandstrom Laser Mouse SMWLL11] + +usb:v062Ap4101* + ID_MODEL_FROM_DATABASE=Wireless Keyboard/Mouse + +usb:v062Ap6301* + ID_MODEL_FROM_DATABASE=Trust Wireless Optical Mouse MI-4150K + +usb:v062Ap9003* + ID_MODEL_FROM_DATABASE=VoIP Conference Hub (A16GH) + +usb:v062Ap9004* + ID_MODEL_FROM_DATABASE=USR9602 USB Internet Mini Phone + +usb:v062B* + ID_VENDOR_FROM_DATABASE=Greatlink Electronics Taiwan, Ltd + +usb:v062C* + ID_VENDOR_FROM_DATABASE=Institute for Information Industry + +usb:v062D* + ID_VENDOR_FROM_DATABASE=Taiwan Tai-Hao Enterprises Co., Ltd + +usb:v062E* + ID_VENDOR_FROM_DATABASE=Mainsuper Enterprises Co., Ltd + +usb:v062F* + ID_VENDOR_FROM_DATABASE=Sin Sheng Terminal & Machine, Inc. + +usb:v0631* + ID_VENDOR_FROM_DATABASE=JUJO Electronics Corp. + +usb:v0633* + ID_VENDOR_FROM_DATABASE=Cyrix Corp. + +usb:v0634* + ID_VENDOR_FROM_DATABASE=Micron Technology, Inc. + +usb:v0634p0655* + ID_MODEL_FROM_DATABASE=Embedded Mass Storage Drive [RealSSD] + +usb:v0635* + ID_VENDOR_FROM_DATABASE=Methode Electronics, Inc. + +usb:v0636* + ID_VENDOR_FROM_DATABASE=Sierra Imaging, Inc. + +usb:v0636p0003* + ID_MODEL_FROM_DATABASE=Vivicam 35Xx + +usb:v0638* + ID_VENDOR_FROM_DATABASE=Avision, Inc. + +usb:v0638p0268* + ID_MODEL_FROM_DATABASE=iVina 1200U Scanner + +usb:v0638p026A* + ID_MODEL_FROM_DATABASE=Minolta Dimage Scan Dual II AF-2820U (2886) + +usb:v0638p0A10* + ID_MODEL_FROM_DATABASE=iVina FB1600/UMAX Astra 4500 + +usb:v0638p0A13* + ID_MODEL_FROM_DATABASE=AV600U + +usb:v0638p0A15* + ID_MODEL_FROM_DATABASE=Konica Minolta SC-110 + +usb:v0638p0A16* + ID_MODEL_FROM_DATABASE=Konica Minolta SC-215 + +usb:v0638p0A30* + ID_MODEL_FROM_DATABASE=UMAX Astra 6700 Scanner + +usb:v0638p0A41* + ID_MODEL_FROM_DATABASE=Avision AM3000/MF3000 Series + +usb:v0638p0F01* + ID_MODEL_FROM_DATABASE=fi-4010CU + +usb:v0638p4004* + ID_MODEL_FROM_DATABASE=Minolta Dimage Scan Elite II AF-2920 (2888) + +usb:v0639* + ID_VENDOR_FROM_DATABASE=Chrontel, Inc. + +usb:v063A* + ID_VENDOR_FROM_DATABASE=Techwin Corp. + +usb:v063B* + ID_VENDOR_FROM_DATABASE=Taugagreining HF + +usb:v063C* + ID_VENDOR_FROM_DATABASE=Yamaichi Electronics Co., Ltd (Sakura) + +usb:v063D* + ID_VENDOR_FROM_DATABASE=Fong Kai Industrial Co., Ltd + +usb:v063E* + ID_VENDOR_FROM_DATABASE=RealMedia Technology, Inc. + +usb:v063F* + ID_VENDOR_FROM_DATABASE=New Technology Cable, Ltd + +usb:v0640* + ID_VENDOR_FROM_DATABASE=Hitex Development Tools + +usb:v0640p0026* + ID_MODEL_FROM_DATABASE=LPC-Stick + +usb:v0641* + ID_VENDOR_FROM_DATABASE=Woods Industries, Inc. + +usb:v0642* + ID_VENDOR_FROM_DATABASE=VIA Medical Corp. + +usb:v0644* + ID_VENDOR_FROM_DATABASE=TEAC Corp. + +usb:v0644p0000* + ID_MODEL_FROM_DATABASE=Floppy + +usb:v0644p0200* + ID_MODEL_FROM_DATABASE=All-In-One Multi-Card Reader CA200/B/S + +usb:v0644p1000* + ID_MODEL_FROM_DATABASE=CD-ROM Drive + +usb:v0644p800D* + ID_MODEL_FROM_DATABASE=TASCAM Portastudio DP-01FX + +usb:v0644p800E* + ID_MODEL_FROM_DATABASE=TASCAM US-122L + +usb:v0644p801D* + ID_MODEL_FROM_DATABASE=TASCAM DR-100 + +usb:v0644p8021* + ID_MODEL_FROM_DATABASE=TASCAM US-122mkII + +usb:v0644pD001* + ID_MODEL_FROM_DATABASE=CD-R/RW Unit + +usb:v0644pD002* + ID_MODEL_FROM_DATABASE=CD-R/RW Unit + +usb:v0644pD010* + ID_MODEL_FROM_DATABASE=CD-RW/DVD Unit + +usb:v0645* + ID_VENDOR_FROM_DATABASE=Who? Vision Systems, Inc. + +usb:v0646* + ID_VENDOR_FROM_DATABASE=UMAX + +usb:v0647* + ID_VENDOR_FROM_DATABASE=Acton Research Corp. + +usb:v0647p0100* + ID_MODEL_FROM_DATABASE=ARC SpectraPro UV/VIS/IR Monochromator/Spectrograph + +usb:v0647p0101* + ID_MODEL_FROM_DATABASE=ARC AM-VM Mono Airpath/Vacuum Monochromator/Spectrograph + +usb:v0647p0102* + ID_MODEL_FROM_DATABASE=ARC Inspectrum Mono + +usb:v0647p0103* + ID_MODEL_FROM_DATABASE=ARC Filterwheel + +usb:v0647p03E9* + ID_MODEL_FROM_DATABASE=Inspectrum 128x1024 F VIS Spectrograph + +usb:v0647p03EA* + ID_MODEL_FROM_DATABASE=Inspectrum 256x1024 F VIS Spectrograph + +usb:v0647p03EB* + ID_MODEL_FROM_DATABASE=Inspectrum 128x1024 B VIS Spectrograph + +usb:v0647p03EC* + ID_MODEL_FROM_DATABASE=Inspectrum 256x1024 B VIS Spectrograph + +usb:v0648* + ID_VENDOR_FROM_DATABASE=Inside Out Networks + +usb:v0649* + ID_VENDOR_FROM_DATABASE=Weli Science Co., Ltd + +usb:v064B* + ID_VENDOR_FROM_DATABASE=Analog Devices, Inc. (White Mountain DSP) + +usb:v064Bp0165* + ID_MODEL_FROM_DATABASE=Blackfin 535 [ADZS HPUSB ICE] + +usb:v064C* + ID_VENDOR_FROM_DATABASE=Ji-Haw Industrial Co., Ltd + +usb:v064D* + ID_VENDOR_FROM_DATABASE=TriTech Microelectronics, Ltd + +usb:v064E* + ID_VENDOR_FROM_DATABASE=Suyin Corp. + +usb:v064Ep2100* + ID_MODEL_FROM_DATABASE=Sony Visual Communication Camera + +usb:v064Ep9700* + ID_MODEL_FROM_DATABASE=Asus Integrated Webcam + +usb:v064EpA100* + ID_MODEL_FROM_DATABASE=Acer OrbiCam + +usb:v064EpA101* + ID_MODEL_FROM_DATABASE=Acer CrystalEye Webcam + +usb:v064EpA102* + ID_MODEL_FROM_DATABASE=Acer/Lenovo Webcam [CN0316] + +usb:v064EpA103* + ID_MODEL_FROM_DATABASE=Acer/HP Integrated Webcam [CN0314] + +usb:v064EpA110* + ID_MODEL_FROM_DATABASE=HP Webcam + +usb:v064EpA114* + ID_MODEL_FROM_DATABASE=Lemote Webcam + +usb:v064EpA116* + ID_MODEL_FROM_DATABASE=UVC 1.3MPixel WebCam + +usb:v064EpA136* + ID_MODEL_FROM_DATABASE=Asus Integrated Webcam [CN031B] + +usb:v064EpA219* + ID_MODEL_FROM_DATABASE=1.3M WebCam (notebook emachines E730, Acer sub-brand) + +usb:v064EpC107* + ID_MODEL_FROM_DATABASE=HP webcam [dv6-1190en] + +usb:v064EpC335* + ID_MODEL_FROM_DATABASE=HP TrueVision HD + +usb:v064EpD101* + ID_MODEL_FROM_DATABASE=Acer CrystalEye Webcam + +usb:v064EpD213* + ID_MODEL_FROM_DATABASE=UVC HD Webcam + +usb:v064EpD217* + ID_MODEL_FROM_DATABASE=HP TrueVision HD + +usb:v064EpE201* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v064EpE203* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v064EpE258* + ID_MODEL_FROM_DATABASE=HP TrueVision HD Integrated Webcam + +usb:v064EpE263* + ID_MODEL_FROM_DATABASE=HP TrueVision HD Integrated Webcam + +usb:v064EpF102* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam [R5U877] + +usb:v064EpF103* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam [R5U877] + +usb:v064EpF300* + ID_MODEL_FROM_DATABASE=UVC 0.3M Webcam + +usb:v064F* + ID_VENDOR_FROM_DATABASE=WIBU-Systems AG + +usb:v064Fp03E9* + ID_MODEL_FROM_DATABASE=CmStick (article no. 1001) + +usb:v064Fp03F2* + ID_MODEL_FROM_DATABASE=CmStick/M (article no. 1010) + +usb:v064Fp03F3* + ID_MODEL_FROM_DATABASE=CmStick/M (article no. 1011) + +usb:v064Fp0BD7* + ID_MODEL_FROM_DATABASE=BOX/U + +usb:v064Fp0BD8* + ID_MODEL_FROM_DATABASE=BOX/RU + +usb:v0650* + ID_VENDOR_FROM_DATABASE=Dynapro Systems + +usb:v0651* + ID_VENDOR_FROM_DATABASE=Likom Technology Sdn. Bhd. + +usb:v0652* + ID_VENDOR_FROM_DATABASE=Stargate Solutions, Inc. + +usb:v0653* + ID_VENDOR_FROM_DATABASE=CNF, Inc. + +usb:v0654* + ID_VENDOR_FROM_DATABASE=Granite Microsystems, Inc. + +usb:v0654p0005* + ID_MODEL_FROM_DATABASE=Device Bay Controller + +usb:v0654p0006* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0654p0007* + ID_MODEL_FROM_DATABASE=Device Bay Controller + +usb:v0654p0016* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0655* + ID_VENDOR_FROM_DATABASE=Space Shuttle Hi-Tech Co., Ltd + +usb:v0656* + ID_VENDOR_FROM_DATABASE=Glory Mark Electronic, Ltd + +usb:v0657* + ID_VENDOR_FROM_DATABASE=Tekcon Electronics Corp. + +usb:v0658* + ID_VENDOR_FROM_DATABASE=Sigma Designs, Inc. + +usb:v0659* + ID_VENDOR_FROM_DATABASE=Aethra + +usb:v065A* + ID_VENDOR_FROM_DATABASE=Optoelectronics Co., Ltd + +usb:v065Ap0001* + ID_MODEL_FROM_DATABASE=Opticon OPR-2001 / NLV-1001 (keyboard mode) + +usb:v065Ap0009* + ID_MODEL_FROM_DATABASE=NLV-1001 (serial mode) / OPN-2001 [Opticon] + +usb:v065B* + ID_VENDOR_FROM_DATABASE=Tracewell Systems + +usb:v065E* + ID_VENDOR_FROM_DATABASE=Silicon Graphics + +usb:v065F* + ID_VENDOR_FROM_DATABASE=Good Way Technology Co., Ltd & GWC technology Inc. + +usb:v0660* + ID_VENDOR_FROM_DATABASE=TSAY-E (BVI) International, Inc. + +usb:v0661* + ID_VENDOR_FROM_DATABASE=Hamamatsu Photonics K.K. + +usb:v0662* + ID_VENDOR_FROM_DATABASE=Kansai Electric Co., Ltd + +usb:v0663* + ID_VENDOR_FROM_DATABASE=Topmax Electronic Co., Ltd + +usb:v0663p0103* + ID_MODEL_FROM_DATABASE=CobraPad + +usb:v0664* + ID_VENDOR_FROM_DATABASE=ET&T Technology Co., Ltd. + +usb:v0664p0301* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0302* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0303* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0304* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0305* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0306* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0307* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0664p0309* + ID_MODEL_FROM_DATABASE=Groovy Technology Corp. GTouch Touch Screen + +usb:v0665* + ID_VENDOR_FROM_DATABASE=Cypress Semiconductor + +usb:v0665p5161* + ID_MODEL_FROM_DATABASE=USB to Serial + +usb:v0667* + ID_VENDOR_FROM_DATABASE=Aiwa Co., Ltd + +usb:v0667p0FA1* + ID_MODEL_FROM_DATABASE=TD-U8000 Tape Drive + +usb:v0668* + ID_VENDOR_FROM_DATABASE=WordWand + +usb:v0669* + ID_VENDOR_FROM_DATABASE=Oce' Printing Systems GmbH + +usb:v066A* + ID_VENDOR_FROM_DATABASE=Total Technologies, Ltd + +usb:v066B* + ID_VENDOR_FROM_DATABASE=Linksys, Inc. + +usb:v066Bp0105* + ID_MODEL_FROM_DATABASE=SCM eUSB SmartMedia Card Reader + +usb:v066Bp010A* + ID_MODEL_FROM_DATABASE=Melco MCR-U2 SmartMedia / CompactFlash Reader + +usb:v066Bp200C* + ID_MODEL_FROM_DATABASE=USB10TX + +usb:v066Bp2202* + ID_MODEL_FROM_DATABASE=USB10TX Ethernet [pegasus] + +usb:v066Bp2203* + ID_MODEL_FROM_DATABASE=USB100TX Ethernet [pegasus] + +usb:v066Bp2204* + ID_MODEL_FROM_DATABASE=USB100TX HomePNA Ethernet [pegasus] + +usb:v066Bp2206* + ID_MODEL_FROM_DATABASE=USB Ethernet [pegasus] + +usb:v066Bp2207* + ID_MODEL_FROM_DATABASE=HomeLink Phoneline 10M Network Adapter + +usb:v066Bp2211* + ID_MODEL_FROM_DATABASE=WUSB11 802.11b Adapter + +usb:v066Bp2212* + ID_MODEL_FROM_DATABASE=WUSB11v2.5 802.11b Adapter + +usb:v066Bp2213* + ID_MODEL_FROM_DATABASE=WUSB12v1.1 802.11b Adapter + +usb:v066Bp2219* + ID_MODEL_FROM_DATABASE=Instant Wireless Network Adapter + +usb:v066Bp400B* + ID_MODEL_FROM_DATABASE=USB10TX + +usb:v066D* + ID_VENDOR_FROM_DATABASE=Entrega, Inc. + +usb:v066E* + ID_VENDOR_FROM_DATABASE=Acer Semiconductor America, Inc. + +usb:v066F* + ID_VENDOR_FROM_DATABASE=SigmaTel, Inc. + +usb:v066Fp003B* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp003E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp003F* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp0040* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp0041* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp0042* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp0043* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp004B* + ID_MODEL_FROM_DATABASE=A-Max PA11 MP3 Player + +usb:v066Fp3400* + ID_MODEL_FROM_DATABASE=STMP3400 D-Major MP3 Player + +usb:v066Fp3410* + ID_MODEL_FROM_DATABASE=STMP3410 D-Major MP3 Player + +usb:v066Fp3500* + ID_MODEL_FROM_DATABASE=Player Recovery Device + +usb:v066Fp3780* + ID_MODEL_FROM_DATABASE=STMP3780/i.MX23 SystemOnChip in RecoveryMode + +usb:v066Fp4200* + ID_MODEL_FROM_DATABASE=STIr4200 IrDA Bridge + +usb:v066Fp4210* + ID_MODEL_FROM_DATABASE=STIr4210 IrDA Bridge + +usb:v066Fp8000* + ID_MODEL_FROM_DATABASE=MSCN MP3 Player + +usb:v066Fp8001* + ID_MODEL_FROM_DATABASE=SigmaTel MSCN Audio Player + +usb:v066Fp8004* + ID_MODEL_FROM_DATABASE=MSCNMMC MP3 Player + +usb:v066Fp8008* + ID_MODEL_FROM_DATABASE=i-Bead 100 MP3 Player + +usb:v066Fp8020* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8034* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8036* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8038* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8056* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8060* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8066* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp807E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8092* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8096* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp809A* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80AA* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80AC* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80B8* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80BA* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80BC* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80BF* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80C5* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80C8* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80CA* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp80CC* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8104* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8106* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8108* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp810A* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp810C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8122* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8124* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8126* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8128* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8134* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8136* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8138* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp813A* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp813E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8140* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8142* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8144* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8146* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8148* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp814C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8201* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8202* + ID_MODEL_FROM_DATABASE=Jens of Sweden / I-BEAD 150M/150H MP3 player + +usb:v066Fp8203* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8204* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8205* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8206* + ID_MODEL_FROM_DATABASE=Digital MP3 Music Player + +usb:v066Fp8207* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8208* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8209* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820A* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820B* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820D* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp820F* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8210* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8211* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8212* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8213* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8214* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8215* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8216* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8217* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8218* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8219* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821A* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821B* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821D* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp821F* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8220* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8221* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8222* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8223* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8224* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8225* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8226* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8227* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8228* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8229* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8230* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp829C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp82E0* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp8320* + ID_MODEL_FROM_DATABASE=TrekStor i.Beat fun + +usb:v066Fp835D* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp9000* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp9001* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v066Fp9002* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0670* + ID_VENDOR_FROM_DATABASE=Sequel Imaging + +usb:v0670p0001* + ID_MODEL_FROM_DATABASE=Calibrator + +usb:v0670p0005* + ID_MODEL_FROM_DATABASE=Enable Cable + +usb:v0672* + ID_VENDOR_FROM_DATABASE=Labtec, Inc. + +usb:v0672p1041* + ID_MODEL_FROM_DATABASE=LCS1040 Speaker System + +usb:v0672p5000* + ID_MODEL_FROM_DATABASE=SpaceBall 4000 FLX + +usb:v0673* + ID_VENDOR_FROM_DATABASE=HCL + +usb:v0673p5000* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0674* + ID_VENDOR_FROM_DATABASE=Key Mouse Electronic Enterprise Co., Ltd + +usb:v0675* + ID_VENDOR_FROM_DATABASE=DrayTek Corp. + +usb:v0675p0110* + ID_MODEL_FROM_DATABASE=Vigor 128 ISDN TA + +usb:v0675p0530* + ID_MODEL_FROM_DATABASE=Vigor530 IEEE 802.11G Adapter (ISL3880+NET2280) + +usb:v0675p0550* + ID_MODEL_FROM_DATABASE=Vigor550 + +usb:v0675p1688* + ID_MODEL_FROM_DATABASE=miniVigor 128 ISDN TA [HFC-S] + +usb:v0675p6694* + ID_MODEL_FROM_DATABASE=miniVigor 128 ISDN TA + +usb:v0676* + ID_VENDOR_FROM_DATABASE=Teles AG + +usb:v0677* + ID_VENDOR_FROM_DATABASE=Aiwa Co., Ltd + +usb:v0677p07D5* + ID_MODEL_FROM_DATABASE=TM-ED1285(USB) + +usb:v0677p0FA1* + ID_MODEL_FROM_DATABASE=TD-U8000 Tape Drive + +usb:v0678* + ID_VENDOR_FROM_DATABASE=ACard Technology Corp. + +usb:v067B* + ID_VENDOR_FROM_DATABASE=Prolific Technology, Inc. + +usb:v067Bp0000* + ID_MODEL_FROM_DATABASE=PL2301 USB-USB Bridge + +usb:v067Bp0001* + ID_MODEL_FROM_DATABASE=PL2302 USB-USB Bridge + +usb:v067Bp0307* + ID_MODEL_FROM_DATABASE=Motorola Serial Adapter + +usb:v067Bp04BB* + ID_MODEL_FROM_DATABASE=PL2303 Serial (IODATA USB-RSAQ2) + +usb:v067Bp0600* + ID_MODEL_FROM_DATABASE=IDE Bridge + +usb:v067Bp0610* + ID_MODEL_FROM_DATABASE=Onext EG210U MODEM + +usb:v067Bp0611* + ID_MODEL_FROM_DATABASE=AlDiga AL-11U Quad-band GSM/GPRS/EDGE modem + +usb:v067Bp2303* + ID_MODEL_FROM_DATABASE=PL2303 Serial Port + +usb:v067Bp2305* + ID_MODEL_FROM_DATABASE=PL2305 Parallel Port + +usb:v067Bp2306* + ID_MODEL_FROM_DATABASE=Raylink Bridge Controller + +usb:v067Bp2307* + ID_MODEL_FROM_DATABASE=PL2307 USB-ATAPI4 Bridge + +usb:v067Bp2313* + ID_MODEL_FROM_DATABASE=FITEL PHS U Cable Adaptor + +usb:v067Bp2315* + ID_MODEL_FROM_DATABASE=Flash Disk Embedded Hub + +usb:v067Bp2316* + ID_MODEL_FROM_DATABASE=Flash Disk Security Device + +usb:v067Bp2317* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v067Bp2501* + ID_MODEL_FROM_DATABASE=PL2501 USB-USB Bridge (USB 2.0) + +usb:v067Bp2506* + ID_MODEL_FROM_DATABASE=Kaser 8gB micro hard drive + +usb:v067Bp2507* + ID_MODEL_FROM_DATABASE=PL2507 Hi-speed USB to IDE bridge controller + +usb:v067Bp2515* + ID_MODEL_FROM_DATABASE=Flash Disk Embedded Hub + +usb:v067Bp2517* + ID_MODEL_FROM_DATABASE=Flash Disk Mass Storage Device + +usb:v067Bp2528* + ID_MODEL_FROM_DATABASE=Storage device (8gB thumb drive) + +usb:v067Bp25A1* + ID_MODEL_FROM_DATABASE=PL25A1 Host-Host Bridge + +usb:v067Bp2773* + ID_MODEL_FROM_DATABASE=PL2773 SATAII bridge controller + +usb:v067Bp3400* + ID_MODEL_FROM_DATABASE=Hi-Speed Flash Disk with TruePrint AES3400 + +usb:v067Bp3500* + ID_MODEL_FROM_DATABASE=Hi-Speed Flash Disk with TruePrint AES3500 + +usb:v067Bp3507* + ID_MODEL_FROM_DATABASE=PL3507 ATAPI6 Bridge + +usb:v067BpAAA0* + ID_MODEL_FROM_DATABASE=Prolific Pharos + +usb:v067BpAAA2* + ID_MODEL_FROM_DATABASE=PL2303 Serial Adapter (IODATA USB-RSAQ3) + +usb:v067BpAAA3* + ID_MODEL_FROM_DATABASE=PL2303x Serial Adapter + +usb:v067C* + ID_VENDOR_FROM_DATABASE=Efficient Networks, Inc. + +usb:v067Cp1001* + ID_MODEL_FROM_DATABASE=Siemens SpeedStream 100MBps Ethernet + +usb:v067Cp1022* + ID_MODEL_FROM_DATABASE=Siemens SpeedStream 1022 802.11b Adapter + +usb:v067Cp1023* + ID_MODEL_FROM_DATABASE=SpeedStream Wireless + +usb:v067Cp4020* + ID_MODEL_FROM_DATABASE=SpeedStream 4020 ATM/ADSL Installer + +usb:v067Cp4031* + ID_MODEL_FROM_DATABASE=Efficient ADSL Modem + +usb:v067Cp4032* + ID_MODEL_FROM_DATABASE=SpeedStream 4031 ATM/ADSL Installer + +usb:v067Cp4033* + ID_MODEL_FROM_DATABASE=SpeedStream 4031 ATM/ADSL Installer + +usb:v067Cp4060* + ID_MODEL_FROM_DATABASE=Alcatel Speedstream 4060 ADSL Modem + +usb:v067Cp4062* + ID_MODEL_FROM_DATABASE=Efficient Networks 4060 Loader + +usb:v067Cp5667* + ID_MODEL_FROM_DATABASE=Efficient Networks Virtual Bus for ADSL Modem + +usb:v067CpC031* + ID_MODEL_FROM_DATABASE=SpeedStream 4031 ATM/ADSL Installer + +usb:v067CpC032* + ID_MODEL_FROM_DATABASE=SpeedStream 4031 ATM/ADSL Installer + +usb:v067CpC033* + ID_MODEL_FROM_DATABASE=SpeedStream 4031 ATM/ADSL Installer + +usb:v067CpC060* + ID_MODEL_FROM_DATABASE=SpeedStream 4060 Miniport ATM/ADSL Adapter + +usb:v067CpD667* + ID_MODEL_FROM_DATABASE=Efficient Networks Virtual Bus for ADSL Modem + +usb:v067CpE240* + ID_MODEL_FROM_DATABASE=Speedstream Ethernet Adapter E240 + +usb:v067CpE540* + ID_MODEL_FROM_DATABASE=Speedstream Ethernet Adapter E240 + +usb:v067D* + ID_VENDOR_FROM_DATABASE=Hohner Corp. + +usb:v067E* + ID_VENDOR_FROM_DATABASE=Intermec Technologies Corp. + +usb:v067Ep0801* + ID_MODEL_FROM_DATABASE=HID Keyboard, Barcode scanner + +usb:v067Ep0803* + ID_MODEL_FROM_DATABASE=VCP, Barcode scanner + +usb:v067Ep0805* + ID_MODEL_FROM_DATABASE=VCP + UVC, Barcode scanner + +usb:v067Ep1001* + ID_MODEL_FROM_DATABASE=Mobile Computer + +usb:v067F* + ID_VENDOR_FROM_DATABASE=Virata, Ltd + +usb:v067Fp4552* + ID_MODEL_FROM_DATABASE=DSL-200 ADSL Modem + +usb:v067Fp6542* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v067Fp6549* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v067Fp7541* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0680* + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Corp., CPP Div. (Avance Logic) + +usb:v0680p0002* + ID_MODEL_FROM_DATABASE=Arowana Optical Wheel Mouse MSOP-01 + +usb:v0681* + ID_VENDOR_FROM_DATABASE=Siemens Information and Communication Products + +usb:v0681p0001* + ID_MODEL_FROM_DATABASE=Dect Base + +usb:v0681p0002* + ID_MODEL_FROM_DATABASE=Gigaset 3075 Passive ISDN + +usb:v0681p0005* + ID_MODEL_FROM_DATABASE=ID-Mouse with Fingerprint Reader + +usb:v0681p0012* + ID_MODEL_FROM_DATABASE=I-Gate 802.11b Adapter + +usb:v0681p001B* + ID_MODEL_FROM_DATABASE=WLL013 + +usb:v0681p001D* + ID_MODEL_FROM_DATABASE=Hipath 1000 + +usb:v0681p0022* + ID_MODEL_FROM_DATABASE=Gigaset SX353 ISDN + +usb:v0681p0026* + ID_MODEL_FROM_DATABASE=DECT Data - Gigaset M34 + +usb:v0681p002B* + ID_MODEL_FROM_DATABASE=A-100-I ADSL Modem + +usb:v0681p002E* + ID_MODEL_FROM_DATABASE=ADSL Router_S-141 + +usb:v0681p0034* + ID_MODEL_FROM_DATABASE=GSM module MC35/ES75 USB Modem + +usb:v0681p3C06* + ID_MODEL_FROM_DATABASE=54g USB Network Adapter + +usb:v0682* + ID_VENDOR_FROM_DATABASE=Victor Company of Japan, Ltd + +usb:v0684* + ID_VENDOR_FROM_DATABASE=Actiontec Electronics, Inc. + +usb:v0685* + ID_VENDOR_FROM_DATABASE=ZD Incorporated + +usb:v0685p7000* + ID_MODEL_FROM_DATABASE=HSDPA Modem + +usb:v0686* + ID_VENDOR_FROM_DATABASE=Minolta Co., Ltd + +usb:v0686p2001* + ID_MODEL_FROM_DATABASE=PagePro 4110W + +usb:v0686p2004* + ID_MODEL_FROM_DATABASE=PagePro 1200W + +usb:v0686p2005* + ID_MODEL_FROM_DATABASE=Magicolor 2300 DL + +usb:v0686p3001* + ID_MODEL_FROM_DATABASE=PagePro 4100 + +usb:v0686p3005* + ID_MODEL_FROM_DATABASE=PagePro 1250E + +usb:v0686p3006* + ID_MODEL_FROM_DATABASE=PagePro 1250W + +usb:v0686p3009* + ID_MODEL_FROM_DATABASE=Magicolor 2300W + +usb:v0686p300B* + ID_MODEL_FROM_DATABASE=PagePro 1350W + +usb:v0686p300C* + ID_MODEL_FROM_DATABASE=PagePro 1300W + +usb:v0686p302E* + ID_MODEL_FROM_DATABASE=Develop D 1650iD PCL + +usb:v0686p3034* + ID_MODEL_FROM_DATABASE=Develop D 2050iD PCL + +usb:v0686p4001* + ID_MODEL_FROM_DATABASE=Dimage 2300 + +usb:v0686p4003* + ID_MODEL_FROM_DATABASE=Dimage 2330 Zoom Camera + +usb:v0686p4004* + ID_MODEL_FROM_DATABASE=Dimage Scan Elite II AF-2920 (2888) + +usb:v0686p4005* + ID_MODEL_FROM_DATABASE=Minolta DiMAGE E201 Mass Storage Device + +usb:v0686p4006* + ID_MODEL_FROM_DATABASE=Dimage 7 Camera + +usb:v0686p4007* + ID_MODEL_FROM_DATABASE=Dimage S304 Camera + +usb:v0686p4008* + ID_MODEL_FROM_DATABASE=Dimage 5 Camera + +usb:v0686p4009* + ID_MODEL_FROM_DATABASE=Dimage X Camera + +usb:v0686p400A* + ID_MODEL_FROM_DATABASE=Dimage S404 Camera + +usb:v0686p400B* + ID_MODEL_FROM_DATABASE=Dimage 7i Camera + +usb:v0686p400C* + ID_MODEL_FROM_DATABASE=Dimage F100 Camera + +usb:v0686p400D* + ID_MODEL_FROM_DATABASE=Dimage Scan Dual III AF-2840 (2889) + +usb:v0686p400E* + ID_MODEL_FROM_DATABASE=Dimage Scan Elite 5400 (2890) + +usb:v0686p400F* + ID_MODEL_FROM_DATABASE=Dimage 7Hi Camera + +usb:v0686p4010* + ID_MODEL_FROM_DATABASE=Dimage Xi Camera + +usb:v0686p4011* + ID_MODEL_FROM_DATABASE=Dimage F300 Camera + +usb:v0686p4012* + ID_MODEL_FROM_DATABASE=Dimage F200 Camera + +usb:v0686p4014* + ID_MODEL_FROM_DATABASE=Dimage S414 Camera + +usb:v0686p4015* + ID_MODEL_FROM_DATABASE=Dimage XT Camera [storage] + +usb:v0686p4016* + ID_MODEL_FROM_DATABASE=Dimage XT Camera [remote mode] + +usb:v0686p4017* + ID_MODEL_FROM_DATABASE=Dimage E223 + +usb:v0686p4018* + ID_MODEL_FROM_DATABASE=Dimage Z1 Camera + +usb:v0686p4019* + ID_MODEL_FROM_DATABASE=Dimage A1 Camera [remote mode] + +usb:v0686p401A* + ID_MODEL_FROM_DATABASE=Dimage A1 Camera [storage] + +usb:v0686p401C* + ID_MODEL_FROM_DATABASE=Dimage X20 Camera + +usb:v0686p401E* + ID_MODEL_FROM_DATABASE=Dimage E323 Camera + +usb:v068A* + ID_VENDOR_FROM_DATABASE=Pertech, Inc. + +usb:v068B* + ID_VENDOR_FROM_DATABASE=Potrans International, Inc. + +usb:v068E* + ID_VENDOR_FROM_DATABASE=CH Products, Inc. + +usb:v068Ep00D3* + ID_MODEL_FROM_DATABASE=OEM 3 axis 5 button joystick + +usb:v068Ep00E2* + ID_MODEL_FROM_DATABASE=HFX OEM Joystick + +usb:v068Ep00F0* + ID_MODEL_FROM_DATABASE=Multi-Function Panel + +usb:v068Ep00F1* + ID_MODEL_FROM_DATABASE=Pro Throttle + +usb:v068Ep00F2* + ID_MODEL_FROM_DATABASE=Flight Sim Pedals + +usb:v068Ep00F3* + ID_MODEL_FROM_DATABASE=Fighterstick + +usb:v068Ep00F4* + ID_MODEL_FROM_DATABASE=Combatstick + +usb:v068Ep00FA* + ID_MODEL_FROM_DATABASE=Ch Throttle Quadrant + +usb:v068Ep00FF* + ID_MODEL_FROM_DATABASE=Flight Sim Yoke + +usb:v068Ep0500* + ID_MODEL_FROM_DATABASE=GameStick 3D + +usb:v068Ep0501* + ID_MODEL_FROM_DATABASE=CH Pro Pedals + +usb:v068Ep0504* + ID_MODEL_FROM_DATABASE=F-16 Combat Stick + +usb:v0690* + ID_VENDOR_FROM_DATABASE=Golden Bridge Electech, Inc. + +usb:v0693* + ID_VENDOR_FROM_DATABASE=Hagiwara Sys-Com Co., Ltd + +usb:v0693p0002* + ID_MODEL_FROM_DATABASE=FlashGate SmartMedia Card Reader + +usb:v0693p0003* + ID_MODEL_FROM_DATABASE=FlashGate CompactFlash Card Reader + +usb:v0693p0005* + ID_MODEL_FROM_DATABASE=FlashGate + +usb:v0693p0006* + ID_MODEL_FROM_DATABASE=SM PCCard R/W and SPD + +usb:v0693p0007* + ID_MODEL_FROM_DATABASE=FlashGate ME (Authenticated) + +usb:v0693p000A* + ID_MODEL_FROM_DATABASE=SDCard/MMC Reader/Writer + +usb:v0694* + ID_VENDOR_FROM_DATABASE=Lego Group + +usb:v0694p0001* + ID_MODEL_FROM_DATABASE=Mindstorms Tower + +usb:v0694p0002* + ID_MODEL_FROM_DATABASE=Mindstorms NXT + +usb:v0694p0005* + ID_MODEL_FROM_DATABASE=Mindstorms EV3 + +usb:v0694p0006* + ID_MODEL_FROM_DATABASE=Mindstorms EV3 Firmware Update + +usb:v0698* + ID_VENDOR_FROM_DATABASE=Chuntex (CTX) + +usb:v0698p1786* + ID_MODEL_FROM_DATABASE=1300ex Monitor + +usb:v0698p2003* + ID_MODEL_FROM_DATABASE=CTX M730V built in Camera + +usb:v0698p9999* + ID_MODEL_FROM_DATABASE=VLxxxx Monitor+Hub + +usb:v0699* + ID_VENDOR_FROM_DATABASE=Tektronix, Inc. + +usb:v0699p0347* + ID_MODEL_FROM_DATABASE=AFG 3022B + +usb:v0699p036A* + ID_MODEL_FROM_DATABASE=TDS 2024B + +usb:v069A* + ID_VENDOR_FROM_DATABASE=Askey Computer Corp. + +usb:v069Ap0001* + ID_MODEL_FROM_DATABASE=VC010 Webcam [pwc] + +usb:v069Ap0303* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v069Ap0311* + ID_MODEL_FROM_DATABASE=ADSL Router Remote NDIS Device + +usb:v069Ap0318* + ID_MODEL_FROM_DATABASE=Remote NDIS Device + +usb:v069Ap0319* + ID_MODEL_FROM_DATABASE=220V Remote NDIS Device + +usb:v069Ap0320* + ID_MODEL_FROM_DATABASE=IEEE 802.11b Wireless LAN Card + +usb:v069Ap0321* + ID_MODEL_FROM_DATABASE=Dynalink WLL013 / Compex WLU11A 802.11b Adapter + +usb:v069Ap0402* + ID_MODEL_FROM_DATABASE=Scientific Atlanta WebSTAR 100 & 200 series Cable Modem + +usb:v069Ap0811* + ID_MODEL_FROM_DATABASE=BT Virtual Bus for Helium + +usb:v069Ap0821* + ID_MODEL_FROM_DATABASE=BT Voyager 1010 802.11b Adapter + +usb:v069Ap4402* + ID_MODEL_FROM_DATABASE=Scientific Atlanta WebSTAR 2000 series Cable Modem + +usb:v069Ap4403* + ID_MODEL_FROM_DATABASE=Scientific Atlanta WebSTAR 300 series Cable Modem + +usb:v069Ap4501* + ID_MODEL_FROM_DATABASE=Scientific-Atlanta WebSTAR 2000 series Cable Modem + +usb:v069B* + ID_VENDOR_FROM_DATABASE=Thomson, Inc. + +usb:v069Bp0704* + ID_MODEL_FROM_DATABASE=DCM245 Cable Modem + +usb:v069Bp0705* + ID_MODEL_FROM_DATABASE=THG540K Cable Modem + +usb:v069Bp0709* + ID_MODEL_FROM_DATABASE=Lyra PDP2424 + +usb:v069Bp070C* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v069Bp070D* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v069Bp070E* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v069Bp070F* + ID_MODEL_FROM_DATABASE=RCA Lyra RD1071 MP3 Player + +usb:v069Bp0731* + ID_MODEL_FROM_DATABASE=Lyra M200E256 + +usb:v069Bp0761* + ID_MODEL_FROM_DATABASE=RCA H100A + +usb:v069Bp0778* + ID_MODEL_FROM_DATABASE=PEARL USB Device + +usb:v069Bp2220* + ID_MODEL_FROM_DATABASE=RCA Kazoo RD1000 MP3 Player + +usb:v069Bp300A* + ID_MODEL_FROM_DATABASE=RCA Lyra MP3 Player + +usb:v069Bp3012* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v069Bp3013* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v069Bp5557* + ID_MODEL_FROM_DATABASE=RCA CDS6300 + +usb:v069D* + ID_VENDOR_FROM_DATABASE=Hughes Network Systems (HNS) + +usb:v069Dp0001* + ID_MODEL_FROM_DATABASE=Satellite Receiver Device + +usb:v069Dp0002* + ID_MODEL_FROM_DATABASE=Satellite Device + +usb:v069E* + ID_VENDOR_FROM_DATABASE=Welcat Inc. + +usb:v069Ep0005* + ID_MODEL_FROM_DATABASE=Marx CryptoBox v1.2 + +usb:v069F* + ID_VENDOR_FROM_DATABASE=Allied Data Technologies BV + +usb:v069Fp0010* + ID_MODEL_FROM_DATABASE=Tornado Speakerphone FaxModem 56.0 + +usb:v069Fp0011* + ID_MODEL_FROM_DATABASE=Tornado Speakerphone FaxModem 56.0 + +usb:v069Fp1000* + ID_MODEL_FROM_DATABASE=ADT VvBus for CopperJet + +usb:v069Fp1004* + ID_MODEL_FROM_DATABASE=CopperJet 821 RouterPlus + +usb:v06A2* + ID_VENDOR_FROM_DATABASE=Topro Technology, Inc. + +usb:v06A2p0033* + ID_MODEL_FROM_DATABASE=USB Mouse + +usb:v06A3* + ID_VENDOR_FROM_DATABASE=Saitek PLC + +usb:v06A3p0006* + ID_MODEL_FROM_DATABASE=Cyborg Gold Joystick + +usb:v06A3p0109* + ID_MODEL_FROM_DATABASE=P880 Pad + +usb:v06A3p0160* + ID_MODEL_FROM_DATABASE=ST290 Pro + +usb:v06A3p0200* + ID_MODEL_FROM_DATABASE=Xbox Adrenalin Hub + +usb:v06A3p0241* + ID_MODEL_FROM_DATABASE=Xbox Adrenalin Gamepad + +usb:v06A3p0255* + ID_MODEL_FROM_DATABASE=X52 Flight Controller + +usb:v06A3p040B* + ID_MODEL_FROM_DATABASE=P990 Dual Analog Pad + +usb:v06A3p040C* + ID_MODEL_FROM_DATABASE=P2900 Wireless Pad + +usb:v06A3p0422* + ID_MODEL_FROM_DATABASE=ST90 Joystick + +usb:v06A3p0460* + ID_MODEL_FROM_DATABASE=ST290 Pro Flight Stick + +usb:v06A3p0463* + ID_MODEL_FROM_DATABASE=ST290 + +usb:v06A3p0464* + ID_MODEL_FROM_DATABASE=Cyborg Evo + +usb:v06A3p0471* + ID_MODEL_FROM_DATABASE=Cyborg Graphite Stick + +usb:v06A3p0501* + ID_MODEL_FROM_DATABASE=R100 Sports Wheel + +usb:v06A3p0502* + ID_MODEL_FROM_DATABASE=ST200 Stick + +usb:v06A3p0506* + ID_MODEL_FROM_DATABASE=R220 Digital Wheel + +usb:v06A3p051E* + ID_MODEL_FROM_DATABASE=Cyborg Digital II Stick + +usb:v06A3p052D* + ID_MODEL_FROM_DATABASE=P750 Gamepad + +usb:v06A3p053C* + ID_MODEL_FROM_DATABASE=X45 Flight Controller + +usb:v06A3p053F* + ID_MODEL_FROM_DATABASE=X36F Flightstick + +usb:v06A3p056C* + ID_MODEL_FROM_DATABASE=P2000 Tilt Pad + +usb:v06A3p056F* + ID_MODEL_FROM_DATABASE=P2000 Tilt Pad + +usb:v06A3p05D2* + ID_MODEL_FROM_DATABASE=PC Dash 2 + +usb:v06A3p075C* + ID_MODEL_FROM_DATABASE=X52 Flight Controller + +usb:v06A3p0762* + ID_MODEL_FROM_DATABASE=Saitek X52 Pro Flight Control System + +usb:v06A3p0763* + ID_MODEL_FROM_DATABASE=Pro Flight Rudder Pedals + +usb:v06A3p0764* + ID_MODEL_FROM_DATABASE=Flight Pro Combat Rudder + +usb:v06A3p0805* + ID_MODEL_FROM_DATABASE=R440 Force Wheel + +usb:v06A3p0B4E* + ID_MODEL_FROM_DATABASE=Pro Flight Backlit Information Panel + +usb:v06A3p0BAC* + ID_MODEL_FROM_DATABASE=Pro Flight Yoke + +usb:v06A3p0C2D* + ID_MODEL_FROM_DATABASE=Pro Flight Quadrant + +usb:v06A3p0D05* + ID_MODEL_FROM_DATABASE=Pro Flight Radio Panel + +usb:v06A3p0D06* + ID_MODEL_FROM_DATABASE=Flight Pro Multi Panel + +usb:v06A3p0D67* + ID_MODEL_FROM_DATABASE=Pro Flight Switch Panel + +usb:v06A3p1003* + ID_MODEL_FROM_DATABASE=GM2 Action Pad + +usb:v06A3p1009* + ID_MODEL_FROM_DATABASE=Action Pad + +usb:v06A3p100A* + ID_MODEL_FROM_DATABASE=SP550 Pad and Joystick Combo + +usb:v06A3p100B* + ID_MODEL_FROM_DATABASE=SP550 Pad + +usb:v06A3p1509* + ID_MODEL_FROM_DATABASE=P3000 Wireless Pad + +usb:v06A3p1589* + ID_MODEL_FROM_DATABASE=P3000 Wireless Pad + +usb:v06A3p2541* + ID_MODEL_FROM_DATABASE=X45 Flight Controller + +usb:v06A3p3509* + ID_MODEL_FROM_DATABASE=P3000 RF GamePad + +usb:v06A3p353E* + ID_MODEL_FROM_DATABASE=Cyborg Evo Wireless + +usb:v06A3p3589* + ID_MODEL_FROM_DATABASE=P3000 Wireless Pad + +usb:v06A3p35BE* + ID_MODEL_FROM_DATABASE=Cyborg Evo + +usb:v06A3p5509* + ID_MODEL_FROM_DATABASE=P3000 Wireless Pad + +usb:v06A3p712C* + ID_MODEL_FROM_DATABASE=Pro Flight Yoke integrated hub + +usb:v06A3p8000* + ID_MODEL_FROM_DATABASE=Gamers' Keyboard + +usb:v06A3p801E* + ID_MODEL_FROM_DATABASE=Cyborg 3D Digital Stick II + +usb:v06A3p8020* + ID_MODEL_FROM_DATABASE=Eclipse Keyboard + +usb:v06A3p8021* + ID_MODEL_FROM_DATABASE=Eclipse II Keyboard + +usb:v06A3p802D* + ID_MODEL_FROM_DATABASE=P750 Pad + +usb:v06A3p803F* + ID_MODEL_FROM_DATABASE=X36 Flight Controller + +usb:v06A3p806F* + ID_MODEL_FROM_DATABASE=P2000 Tilt Pad + +usb:v06A3p80C0* + ID_MODEL_FROM_DATABASE=Pro Gamer Command Unit + +usb:v06A3p80C1* + ID_MODEL_FROM_DATABASE=Cyborg Command Pad Unit + +usb:v06A3pA2AE* + ID_MODEL_FROM_DATABASE=Pro Flight Instrument Panel + +usb:v06A3pA502* + ID_MODEL_FROM_DATABASE=Gaming Mouse + +usb:v06A3pF518* + ID_MODEL_FROM_DATABASE=P3200 Rumble Force Game Pad + +usb:v06A3pFF04* + ID_MODEL_FROM_DATABASE=R440 Force Wheel + +usb:v06A3pFF0C* + ID_MODEL_FROM_DATABASE=Cyborg Force Rumble Pad + +usb:v06A3pFF0D* + ID_MODEL_FROM_DATABASE=P2600 Rumble Force Pad + +usb:v06A3pFF12* + ID_MODEL_FROM_DATABASE=Cyborg 3D Force Stick + +usb:v06A3pFF17* + ID_MODEL_FROM_DATABASE=ST 330 Rumble Force Stick + +usb:v06A3pFF52* + ID_MODEL_FROM_DATABASE=Cyborg 3D Rumble Force Joystick + +usb:v06A3pFFB5* + ID_MODEL_FROM_DATABASE=Cyborg Evo Force Joystick + +usb:v06A4* + ID_VENDOR_FROM_DATABASE=Xiamen Doowell Electron Co., Ltd + +usb:v06A5* + ID_VENDOR_FROM_DATABASE=Divio + +usb:v06A5p0000* + ID_MODEL_FROM_DATABASE=Typhoon Webcam 100k [nw8000] + +usb:v06A5pD001* + ID_MODEL_FROM_DATABASE=ProLink DS3303u Webcam + +usb:v06A5pD800* + ID_MODEL_FROM_DATABASE=Chicony TwinkleCam + +usb:v06A5pD820* + ID_MODEL_FROM_DATABASE=Wize Media 1000 + +usb:v06A7* + ID_VENDOR_FROM_DATABASE=MicroStore, Inc. + +usb:v06A8* + ID_VENDOR_FROM_DATABASE=Topaz Systems, Inc. + +usb:v06A8p0042* + ID_MODEL_FROM_DATABASE=SignatureGem 1X5 Pad + +usb:v06A8p0043* + ID_MODEL_FROM_DATABASE=SignatureGem 1X5-HID Pad + +usb:v06A9* + ID_VENDOR_FROM_DATABASE=Westell + +usb:v06A9p0005* + ID_MODEL_FROM_DATABASE=WireSpeed Dual Connect Modem + +usb:v06A9p0006* + ID_MODEL_FROM_DATABASE=WireSpeed Dual Connect Modem + +usb:v06A9p000A* + ID_MODEL_FROM_DATABASE=WireSpeed Dual Connect Modem + +usb:v06A9p000B* + ID_MODEL_FROM_DATABASE=WireSpeed Dual Connect Modem + +usb:v06A9p000E* + ID_MODEL_FROM_DATABASE=A90-211WG-01 802.11g Adapter [Intersil ISL3887] + +usb:v06AA* + ID_VENDOR_FROM_DATABASE=Sysgration, Ltd + +usb:v06AC* + ID_VENDOR_FROM_DATABASE=Fujitsu Laboratories of America, Inc. + +usb:v06AD* + ID_VENDOR_FROM_DATABASE=Greatland Electronics Taiwan, Ltd + +usb:v06AE* + ID_VENDOR_FROM_DATABASE=Professional Multimedia Testing Centre + +usb:v06AF* + ID_VENDOR_FROM_DATABASE=Harting, Inc. of North America + +usb:v06B8* + ID_VENDOR_FROM_DATABASE=Pixela Corp. + +usb:v06B9* + ID_VENDOR_FROM_DATABASE=Alcatel Telecom + +usb:v06B9p0120* + ID_MODEL_FROM_DATABASE=SpeedTouch 120g 802.11g Wireless Adapter [Intersil ISL3886] + +usb:v06B9p0121* + ID_MODEL_FROM_DATABASE=SpeedTouch 121g Wireless Dongle + +usb:v06B9p2001* + ID_MODEL_FROM_DATABASE=SPEED TOUCH Card + +usb:v06B9p4061* + ID_MODEL_FROM_DATABASE=SpeedTouch ISDN or ADSL Modem + +usb:v06B9p4062* + ID_MODEL_FROM_DATABASE=SpeedTouch ISDN or ADSL router + +usb:v06B9pA5A5* + ID_MODEL_FROM_DATABASE=DynaMiTe Modem + +usb:v06BA* + ID_VENDOR_FROM_DATABASE=Smooth Cord & Connector Co., Ltd + +usb:v06BB* + ID_VENDOR_FROM_DATABASE=EDA, Inc. + +usb:v06BC* + ID_VENDOR_FROM_DATABASE=Oki Data Corp. + +usb:v06BCp000B* + ID_MODEL_FROM_DATABASE=Okipage 14ex Printer + +usb:v06BCp0027* + ID_MODEL_FROM_DATABASE=Okipage 14e + +usb:v06BCp00F7* + ID_MODEL_FROM_DATABASE=OKI B4600 Mono Printer + +usb:v06BCp015E* + ID_MODEL_FROM_DATABASE=OKIPOS 411/412 POS Printer + +usb:v06BCp01C9* + ID_MODEL_FROM_DATABASE=OKI B430 Mono Printer + +usb:v06BCp020B* + ID_MODEL_FROM_DATABASE=OKI ES4140 Mono Printer + +usb:v06BCp02BB* + ID_MODEL_FROM_DATABASE=OKI PT390 POS Printer + +usb:v06BCp0A91* + ID_MODEL_FROM_DATABASE=B2500MFP (printer+scanner) + +usb:v06BCp3801* + ID_MODEL_FROM_DATABASE=B6100 Laser Printer + +usb:v06BD* + ID_VENDOR_FROM_DATABASE=AGFA-Gevaert NV + +usb:v06BDp0001* + ID_MODEL_FROM_DATABASE=SnapScan 1212U + +usb:v06BDp0002* + ID_MODEL_FROM_DATABASE=SnapScan 1236U + +usb:v06BDp0100* + ID_MODEL_FROM_DATABASE=SnapScan Touch + +usb:v06BDp0101* + ID_MODEL_FROM_DATABASE=SNAPSCAN ELITE + +usb:v06BDp0200* + ID_MODEL_FROM_DATABASE=ScanMaker 8700 + +usb:v06BDp02BF* + ID_MODEL_FROM_DATABASE=DUOSCAN f40 + +usb:v06BDp0400* + ID_MODEL_FROM_DATABASE=CL30 + +usb:v06BDp0401* + ID_MODEL_FROM_DATABASE=Mass Storage + +usb:v06BDp0403* + ID_MODEL_FROM_DATABASE=ePhoto CL18 Camera + +usb:v06BDp0404* + ID_MODEL_FROM_DATABASE=ePhoto CL20 Camera + +usb:v06BDp2061* + ID_MODEL_FROM_DATABASE=SnapScan 1212U (?) + +usb:v06BDp208D* + ID_MODEL_FROM_DATABASE=Snapscan e40 + +usb:v06BDp208F* + ID_MODEL_FROM_DATABASE=SnapScan e50 + +usb:v06BDp2091* + ID_MODEL_FROM_DATABASE=SnapScan e20 + +usb:v06BDp2093* + ID_MODEL_FROM_DATABASE=SnapScan e10 + +usb:v06BDp2095* + ID_MODEL_FROM_DATABASE=SnapScan e25 + +usb:v06BDp2097* + ID_MODEL_FROM_DATABASE=SnapScan e26 + +usb:v06BDp20FD* + ID_MODEL_FROM_DATABASE=SnapScan e52 + +usb:v06BDp20FF* + ID_MODEL_FROM_DATABASE=SnapScan e42 + +usb:v06BE* + ID_VENDOR_FROM_DATABASE=AME Optimedia Technology Co., Ltd + +usb:v06BEp0800* + ID_MODEL_FROM_DATABASE=Optimedia Camera + +usb:v06BEp1005* + ID_MODEL_FROM_DATABASE=Dazzle DPVM! (1005) + +usb:v06BEpD001* + ID_MODEL_FROM_DATABASE=P35U Camera Capture + +usb:v06BF* + ID_VENDOR_FROM_DATABASE=Leoco Corp. + +usb:v06C2* + ID_VENDOR_FROM_DATABASE=Phidgets Inc. (formerly GLAB) + +usb:v06C2p0030* + ID_MODEL_FROM_DATABASE=PhidgetRFID + +usb:v06C2p0031* + ID_MODEL_FROM_DATABASE=RFID reader + +usb:v06C2p0038* + ID_MODEL_FROM_DATABASE=4-Motor PhidgetServo v3.0 + +usb:v06C2p0039* + ID_MODEL_FROM_DATABASE=1-Motor PhidgetServo v3.0 + +usb:v06C2p003A* + ID_MODEL_FROM_DATABASE=8-Motor PhidgetAvancedServo + +usb:v06C2p0040* + ID_MODEL_FROM_DATABASE=PhidgetInterface Kit 0-0-4 + +usb:v06C2p0044* + ID_MODEL_FROM_DATABASE=PhidgetInterface Kit 0-16-16 + +usb:v06C2p0045* + ID_MODEL_FROM_DATABASE=PhidgetInterface Kit 8-8-8 + +usb:v06C2p0048* + ID_MODEL_FROM_DATABASE=PhidgetStepper (Under Development) + +usb:v06C2p0049* + ID_MODEL_FROM_DATABASE=PhidgetTextLED Ver 1.0 + +usb:v06C2p004A* + ID_MODEL_FROM_DATABASE=PhidgetLED Ver 1.0 + +usb:v06C2p004B* + ID_MODEL_FROM_DATABASE=PhidgetEncoder Ver 1.0 + +usb:v06C2p0051* + ID_MODEL_FROM_DATABASE=PhidgetInterface Kit 0-5-7 (Custom) + +usb:v06C2p0052* + ID_MODEL_FROM_DATABASE=PhidgetTextLCD + +usb:v06C2p0053* + ID_MODEL_FROM_DATABASE=PhidgetInterfaceKit 0-8-8 + +usb:v06C2p0058* + ID_MODEL_FROM_DATABASE=PhidgetMotorControl Ver 1.0 + +usb:v06C2p0070* + ID_MODEL_FROM_DATABASE=PhidgetTemperatureSensor Ver 1.0 + +usb:v06C2p0071* + ID_MODEL_FROM_DATABASE=PhidgetAccelerometer Ver 1.0 + +usb:v06C2p0072* + ID_MODEL_FROM_DATABASE=PhidgetWeightSensor Ver 1.0 + +usb:v06C2p0073* + ID_MODEL_FROM_DATABASE=PhidgetHumiditySensor + +usb:v06C2p0074* + ID_MODEL_FROM_DATABASE=PhidgetPHSensor + +usb:v06C2p0075* + ID_MODEL_FROM_DATABASE=PhidgetGyroscope + +usb:v06C4* + ID_VENDOR_FROM_DATABASE=Bizlink International Corp. + +usb:v06C5* + ID_VENDOR_FROM_DATABASE=Hagenuk, GmbH + +usb:v06C6* + ID_VENDOR_FROM_DATABASE=Infowave Software, Inc. + +usb:v06C8* + ID_VENDOR_FROM_DATABASE=SIIG, Inc. + +usb:v06C9* + ID_VENDOR_FROM_DATABASE=Taxan (Europe), Ltd + +usb:v06C9p0005* + ID_MODEL_FROM_DATABASE=Monitor Control + +usb:v06C9p0007* + ID_MODEL_FROM_DATABASE=Monitor Control + +usb:v06C9p0009* + ID_MODEL_FROM_DATABASE=Monitor Control + +usb:v06CA* + ID_VENDOR_FROM_DATABASE=Newer Technology, Inc. + +usb:v06CAp2003* + ID_MODEL_FROM_DATABASE=uSCSI + +usb:v06CB* + ID_VENDOR_FROM_DATABASE=Synaptics, Inc. + +usb:v06CBp0001* + ID_MODEL_FROM_DATABASE=TouchPad + +usb:v06CBp0002* + ID_MODEL_FROM_DATABASE=Integrated TouchPad + +usb:v06CBp0003* + ID_MODEL_FROM_DATABASE=cPad + +usb:v06CBp0005* + ID_MODEL_FROM_DATABASE=Touchpad/FPS + +usb:v06CBp0006* + ID_MODEL_FROM_DATABASE=TouchScreen + +usb:v06CBp0007* + ID_MODEL_FROM_DATABASE=USB Styk + +usb:v06CBp0008* + ID_MODEL_FROM_DATABASE=WheelPad + +usb:v06CBp0009* + ID_MODEL_FROM_DATABASE=Composite TouchPad and TrackPoint + +usb:v06CBp000E* + ID_MODEL_FROM_DATABASE=HID Device + +usb:v06CBp0010* + ID_MODEL_FROM_DATABASE=Wireless TouchPad + +usb:v06CBp0013* + ID_MODEL_FROM_DATABASE=DisplayPad + +usb:v06CBp2970* + ID_MODEL_FROM_DATABASE=touchpad + +usb:v06CC* + ID_VENDOR_FROM_DATABASE=Terayon Communication Systems + +usb:v06CCp0101* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v06CCp0102* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v06CCp0103* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v06CCp0104* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v06CCp0304* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v06CD* + ID_VENDOR_FROM_DATABASE=Keyspan + +usb:v06CDp0101* + ID_MODEL_FROM_DATABASE=USA-28 PDA [no firmware] + +usb:v06CDp0102* + ID_MODEL_FROM_DATABASE=USA-28X PDA [no firmware] + +usb:v06CDp0103* + ID_MODEL_FROM_DATABASE=USA-19 PDA [no firmware] + +usb:v06CDp0104* + ID_MODEL_FROM_DATABASE=PDA [prerenum] + +usb:v06CDp0105* + ID_MODEL_FROM_DATABASE=USA-18X PDA [no firmware] + +usb:v06CDp0106* + ID_MODEL_FROM_DATABASE=USA-19W PDA [no firmware] + +usb:v06CDp0107* + ID_MODEL_FROM_DATABASE=USA-19 PDA + +usb:v06CDp0108* + ID_MODEL_FROM_DATABASE=USA-19W PDA + +usb:v06CDp0109* + ID_MODEL_FROM_DATABASE=USA-49W serial adapter [no firmware] + +usb:v06CDp010A* + ID_MODEL_FROM_DATABASE=USA-49W serial adapter + +usb:v06CDp010B* + ID_MODEL_FROM_DATABASE=USA-19Qi serial adapter [no firmware] + +usb:v06CDp010C* + ID_MODEL_FROM_DATABASE=USA-19Qi serial adapter + +usb:v06CDp010D* + ID_MODEL_FROM_DATABASE=USA-19Q serial Adapter (no firmware) + +usb:v06CDp010E* + ID_MODEL_FROM_DATABASE=USA-19Q serial Adapter + +usb:v06CDp010F* + ID_MODEL_FROM_DATABASE=USA-28 PDA + +usb:v06CDp0110* + ID_MODEL_FROM_DATABASE=USA-28Xb PDA + +usb:v06CDp0111* + ID_MODEL_FROM_DATABASE=USA-18 serial Adapter + +usb:v06CDp0112* + ID_MODEL_FROM_DATABASE=USA-18X PDA + +usb:v06CDp0113* + ID_MODEL_FROM_DATABASE=USA-28Xb PDA [no firmware] + +usb:v06CDp0114* + ID_MODEL_FROM_DATABASE=USA-28Xa PDA [no firmware] + +usb:v06CDp0115* + ID_MODEL_FROM_DATABASE=USA-28Xa PDA + +usb:v06CDp0116* + ID_MODEL_FROM_DATABASE=USA-18XA serial Adapter (no firmware) + +usb:v06CDp0117* + ID_MODEL_FROM_DATABASE=USA-18XA serial Adapter + +usb:v06CDp0118* + ID_MODEL_FROM_DATABASE=USA-19QW PDA [no firmware] + +usb:v06CDp0119* + ID_MODEL_FROM_DATABASE=USA-19QW PDA + +usb:v06CDp011A* + ID_MODEL_FROM_DATABASE=USA-49Wlc serial adapter [no firmware] + +usb:v06CDp011B* + ID_MODEL_FROM_DATABASE=MPR Serial Preloader (MPRQI) + +usb:v06CDp011C* + ID_MODEL_FROM_DATABASE=MPR Serial (MPRQI) + +usb:v06CDp011D* + ID_MODEL_FROM_DATABASE=MPR Serial Preloader (MPRQ) + +usb:v06CDp011E* + ID_MODEL_FROM_DATABASE=MPR Serial (MPRQ) + +usb:v06CDp0121* + ID_MODEL_FROM_DATABASE=USA-19hs serial adapter + +usb:v06CDp012A* + ID_MODEL_FROM_DATABASE=USA-49Wlc serial adapter + +usb:v06CDp0201* + ID_MODEL_FROM_DATABASE=UIA-10 Digital Media Remote [Cypress AN2131SC] + +usb:v06CDp0202* + ID_MODEL_FROM_DATABASE=UIA-11 Digital Media Remote + +usb:v06CE* + ID_VENDOR_FROM_DATABASE=Contec + +usb:v06CEp8311* + ID_MODEL_FROM_DATABASE=COM-1(USB)H + +usb:v06CF* + ID_VENDOR_FROM_DATABASE=SpheronVR AG + +usb:v06CFp1010* + ID_MODEL_FROM_DATABASE=PanoCam 10 + +usb:v06CFp1012* + ID_MODEL_FROM_DATABASE=PanoCam 12/12X + +usb:v06D0* + ID_VENDOR_FROM_DATABASE=LapLink, Inc. + +usb:v06D0p0622* + ID_MODEL_FROM_DATABASE=LapLink Gold USB-USB Bridge [net1080] + +usb:v06D1* + ID_VENDOR_FROM_DATABASE=Daewoo Electronics Co., Ltd + +usb:v06D3* + ID_VENDOR_FROM_DATABASE=Mitsubishi Electric Corp. + +usb:v06D3p0284* + ID_MODEL_FROM_DATABASE=FX-USB-AW/-BD RS482 Converters + +usb:v06D3p0380* + ID_MODEL_FROM_DATABASE=CP8000D Port + +usb:v06D3p0381* + ID_MODEL_FROM_DATABASE=CP770D Port + +usb:v06D3p0385* + ID_MODEL_FROM_DATABASE=CP900D Port + +usb:v06D3p0387* + ID_MODEL_FROM_DATABASE=CP980D Port + +usb:v06D3p038B* + ID_MODEL_FROM_DATABASE=CP3020D Port + +usb:v06D3p038C* + ID_MODEL_FROM_DATABASE=CP900DW(ID) Port + +usb:v06D3p0393* + ID_MODEL_FROM_DATABASE=CP9500D/DW Port + +usb:v06D3p0394* + ID_MODEL_FROM_DATABASE=CP9000D/DW Port + +usb:v06D3p03A1* + ID_MODEL_FROM_DATABASE=CP9550D/DW Port + +usb:v06D3p03A5* + ID_MODEL_FROM_DATABASE=CP9550DW-S + +usb:v06D3p3B30* + ID_MODEL_FROM_DATABASE=CP-D70DW / CP-D707DW + +usb:v06D3p3B31* + ID_MODEL_FROM_DATABASE=CP-K60DW-S + +usb:v06D4* + ID_VENDOR_FROM_DATABASE=Cisco Systems + +usb:v06D5* + ID_VENDOR_FROM_DATABASE=Toshiba + +usb:v06D5p4000* + ID_MODEL_FROM_DATABASE=Japanese Keyboard + +usb:v06D6* + ID_VENDOR_FROM_DATABASE=Aashima Technology B.V. + +usb:v06D6p0025* + ID_MODEL_FROM_DATABASE=Gamepad + +usb:v06D6p0026* + ID_MODEL_FROM_DATABASE=Predator TH 400 Gamepad + +usb:v06D6p002D* + ID_MODEL_FROM_DATABASE=Trust PowerC@m 350FT + +usb:v06D6p002E* + ID_MODEL_FROM_DATABASE=Trust PowerC@m 350FS + +usb:v06D6p0030* + ID_MODEL_FROM_DATABASE=Trust 710 LCD POWERC@M ZOOM - MSD + +usb:v06D6p0031* + ID_MODEL_FROM_DATABASE=Trust 610/710 LCD POWERC@M ZOOM + +usb:v06D6p003A* + ID_MODEL_FROM_DATABASE=Trust PowerC@m 770Z (mass storage mode) + +usb:v06D6p003B* + ID_MODEL_FROM_DATABASE=Trust PowerC@m 770Z (webcam mode) + +usb:v06D6p003C* + ID_MODEL_FROM_DATABASE=Trust 910z PowerC@m + +usb:v06D6p003F* + ID_MODEL_FROM_DATABASE=Trust 735S POWERC@M ZOOM, WDM DSC Bulk Driver + +usb:v06D6p0050* + ID_MODEL_FROM_DATABASE=Trust 738AV LCD PV Digital Camera + +usb:v06D6p0062* + ID_MODEL_FROM_DATABASE=TRUST 782AV LCD P. V. Video Capture + +usb:v06D6p0066* + ID_MODEL_FROM_DATABASE=TRUST Digital PCTV and Movie Editor + +usb:v06D6p0067* + ID_MODEL_FROM_DATABASE=Trust 350FS POWERC@M FLASH + +usb:v06D6p006B* + ID_MODEL_FROM_DATABASE=TRUST AUDIO VIDEO EDITOR + +usb:v06D7* + ID_VENDOR_FROM_DATABASE=Network Computing Devices (NCD) + +usb:v06D8* + ID_VENDOR_FROM_DATABASE=Technical Marketing Research, Inc. + +usb:v06DA* + ID_VENDOR_FROM_DATABASE=Phoenixtec Power Co., Ltd + +usb:v06DAp0002* + ID_MODEL_FROM_DATABASE=UPS + +usb:v06DAp0003* + ID_MODEL_FROM_DATABASE=1300VA UPS + +usb:v06DB* + ID_VENDOR_FROM_DATABASE=Paradyne + +usb:v06DC* + ID_VENDOR_FROM_DATABASE=Foxlink Image Technology Co., Ltd + +usb:v06DCp0012* + ID_MODEL_FROM_DATABASE=Scan 1200c Scanner + +usb:v06DCp0014* + ID_MODEL_FROM_DATABASE=Prolink Winscan Pro 2448U + +usb:v06DE* + ID_VENDOR_FROM_DATABASE=Heisei Electronics Co., Ltd + +usb:v06E0* + ID_VENDOR_FROM_DATABASE=Multi-Tech Systems, Inc. + +usb:v06E0p0319* + ID_MODEL_FROM_DATABASE=MT9234ZBA-USB MultiModem ZBA + +usb:v06E0pF101* + ID_MODEL_FROM_DATABASE=MT5634ZBA-USB MultiModemUSB (old firmware) + +usb:v06E0pF103* + ID_MODEL_FROM_DATABASE=MT5634MU MultiMobileUSB + +usb:v06E0pF104* + ID_MODEL_FROM_DATABASE=MT5634ZBA-USB MultiModemUSB (new firmware) + +usb:v06E0pF107* + ID_MODEL_FROM_DATABASE=MT5634ZBA-USB-V92 MultiModemUSB + +usb:v06E0pF120* + ID_MODEL_FROM_DATABASE=MT9234ZBA-USB-CDC-ACM-XR MultiModem ZBA CDC-ACM-XR + +usb:v06E1* + ID_VENDOR_FROM_DATABASE=ADS Technologies, Inc. + +usb:v06E1p0008* + ID_MODEL_FROM_DATABASE=UBS-10BT Ethernet [klsi] + +usb:v06E1p0009* + ID_MODEL_FROM_DATABASE=UBS-10BT Ethernet + +usb:v06E1p0833* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v06E1pA155* + ID_MODEL_FROM_DATABASE=FM Radio Receiver/Instant FM Music (RDX-155-EF) + +usb:v06E1pA160* + ID_MODEL_FROM_DATABASE=Instant Video-To-Go RDX-160 (no firmware) + +usb:v06E1pA161* + ID_MODEL_FROM_DATABASE=Instant Video-To-Go RDX-160 + +usb:v06E1pA190* + ID_MODEL_FROM_DATABASE=Instand VCD Capture + +usb:v06E1pA191* + ID_MODEL_FROM_DATABASE=Instant VideoXpress + +usb:v06E1pA337* + ID_MODEL_FROM_DATABASE=Mini DigitalTV + +usb:v06E1pA701* + ID_MODEL_FROM_DATABASE=DVD Xpress + +usb:v06E1pA708* + ID_MODEL_FROM_DATABASE=saa7114H video input card (Instant VideoMPX) + +usb:v06E1pB337* + ID_MODEL_FROM_DATABASE=Mini DigitalTV + +usb:v06E1pB701* + ID_MODEL_FROM_DATABASE=DVD Xpress B + +usb:v06E4* + ID_VENDOR_FROM_DATABASE=Alcatel Microelectronics + +usb:v06E6* + ID_VENDOR_FROM_DATABASE=Tiger Jet Network, Inc. + +usb:v06E6p0200* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p0201* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p0202* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p0203* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p0210* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p0211* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p0212* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p031C* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p031D* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p031E* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p3200* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p3201* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p3202* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p3203* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p7200* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p7210* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p7250* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p825C* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p831C* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6p831D* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6p831E* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB200* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB201* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB202* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pB210* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pB211* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB212* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB250* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pB251* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pB252* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC200* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC201* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC202* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pC203* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC210* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pC211* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pC212* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pC213* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC25C* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pC290* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC291* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC292* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC293* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pC31C* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pC39C* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pC39D* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC39E* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC39F* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pC700* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC701* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC702* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pC703* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pC710* + ID_MODEL_FROM_DATABASE=VoIP Combo Device + +usb:v06E6pC711* + ID_MODEL_FROM_DATABASE=VoIP Combo + +usb:v06E6pC712* + ID_MODEL_FROM_DATABASE=VoIP Combo Device + +usb:v06E6pC713* + ID_MODEL_FROM_DATABASE=VoIP Combo Device + +usb:v06E6pCF00* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pCF01* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pCF02* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pCF03* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pD210* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pD211* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pD212* + ID_MODEL_FROM_DATABASE=PPG Device + +usb:v06E6pD213* + ID_MODEL_FROM_DATABASE=Personal PhoneGateway + +usb:v06E6pD700* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pD701* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pD702* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pD703* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pD710* + ID_MODEL_FROM_DATABASE=VoIP Combo + +usb:v06E6pD711* + ID_MODEL_FROM_DATABASE=VoIP Combo Device + +usb:v06E6pD712* + ID_MODEL_FROM_DATABASE=VoIP Combo + +usb:v06E6pD713* + ID_MODEL_FROM_DATABASE=VoIP Combo + +usb:v06E6pDF00* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pDF01* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pDF02* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pDF03* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF200* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF201* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF202* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pF203* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pF210* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF250* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06E6pF252* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF310* + ID_MODEL_FROM_DATABASE=Internet Phone + +usb:v06E6pF350* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v06EA* + ID_VENDOR_FROM_DATABASE=Sirius Technologies + +usb:v06EAp0001* + ID_MODEL_FROM_DATABASE=NetCom Roadster II 56k + +usb:v06EAp0002* + ID_MODEL_FROM_DATABASE=Roadster II 56k + +usb:v06EB* + ID_VENDOR_FROM_DATABASE=PC Expert Tech. Co., Ltd + +usb:v06EF* + ID_VENDOR_FROM_DATABASE=I.A.C. Geometrische Ingenieurs B.V. + +usb:v06F0* + ID_VENDOR_FROM_DATABASE=T.N.C Industrial Co., Ltd + +usb:v06F0pDE01* + ID_MODEL_FROM_DATABASE=DualCam Video Camera + +usb:v06F0pDE02* + ID_MODEL_FROM_DATABASE=DualCam Still Camera + +usb:v06F1* + ID_VENDOR_FROM_DATABASE=Opcode Systems, Inc. + +usb:v06F1pA011* + ID_MODEL_FROM_DATABASE=SonicPort + +usb:v06F1pA021* + ID_MODEL_FROM_DATABASE=SonicPort Optical + +usb:v06F2* + ID_VENDOR_FROM_DATABASE=Emine Technology Co. + +usb:v06F2p0011* + ID_MODEL_FROM_DATABASE=KVM Switch Keyboard + +usb:v06F6* + ID_VENDOR_FROM_DATABASE=Wintrend Technology Co., Ltd + +usb:v06F7* + ID_VENDOR_FROM_DATABASE=Wailly Technology Ltd + +usb:v06F7p0003* + ID_MODEL_FROM_DATABASE=USB->Din 4 Adaptor + +usb:v06F8* + ID_VENDOR_FROM_DATABASE=Guillemot Corp. + +usb:v06F8p3002* + ID_MODEL_FROM_DATABASE=Hercules Blog Webcam + +usb:v06F8p3004* + ID_MODEL_FROM_DATABASE=Hercules Classic Silver + +usb:v06F8p3005* + ID_MODEL_FROM_DATABASE=Hercules Dualpix Exchange + +usb:v06F8p3007* + ID_MODEL_FROM_DATABASE=Hercules Dualpix Chat and Show + +usb:v06F8p3020* + ID_MODEL_FROM_DATABASE=Hercules Webcam EC300 + +usb:v06F8pA300* + ID_MODEL_FROM_DATABASE=Dual Analog Leader GamePad + +usb:v06F8pB000* + ID_MODEL_FROM_DATABASE=Hercules DJ Console + +usb:v06F8pC000* + ID_MODEL_FROM_DATABASE=Hercules Muse Pocket + +usb:v06F8pD002* + ID_MODEL_FROM_DATABASE=Hercules DJ Console + +usb:v06F8pE000* + ID_MODEL_FROM_DATABASE=HWGUSB2-54 WLAN + +usb:v06F8pE010* + ID_MODEL_FROM_DATABASE=HWGUSB2-54-LB + +usb:v06F8pE020* + ID_MODEL_FROM_DATABASE=HWGUSB2-54V2-AP + +usb:v06F8pE031* + ID_MODEL_FROM_DATABASE=Hercules HWNUm-300 Wireless N mini [Realtek RTL8191SU] + +usb:v06F8pE032* + ID_MODEL_FROM_DATABASE=HWGUm-54 [Hercules Wireless G Ultra Mini Key] + +usb:v06F8pE033* + ID_MODEL_FROM_DATABASE=Hercules HWNUp-150 802.11n Wireless N Pico [Realtek RTL8188CUS] + +usb:v06F9* + ID_VENDOR_FROM_DATABASE=ASYST electronic d.o.o. + +usb:v06FA* + ID_VENDOR_FROM_DATABASE=HSD S.r.L + +usb:v06FC* + ID_VENDOR_FROM_DATABASE=Motorola Semiconductor Products Sector + +usb:v06FD* + ID_VENDOR_FROM_DATABASE=Boston Acoustics + +usb:v06FDp0101* + ID_MODEL_FROM_DATABASE=Audio Device + +usb:v06FDp0102* + ID_MODEL_FROM_DATABASE=Audio Device + +usb:v06FDp0201* + ID_MODEL_FROM_DATABASE=2-piece Audio Device + +usb:v06FE* + ID_VENDOR_FROM_DATABASE=Gallant Computer, Inc. + +usb:v0701* + ID_VENDOR_FROM_DATABASE=Supercomal Wire & Cable SDN. BHD. + +usb:v0703* + ID_VENDOR_FROM_DATABASE=Bvtech Industry, Inc. + +usb:v0705* + ID_VENDOR_FROM_DATABASE=NKK Corp. + +usb:v0706* + ID_VENDOR_FROM_DATABASE=Ariel Corp. + +usb:v0707* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp. + +usb:v0707p0100* + ID_MODEL_FROM_DATABASE=2202 Ethernet [klsi] + +usb:v0707p0200* + ID_MODEL_FROM_DATABASE=2202 Ethernet [pegasus] + +usb:v0707p0201* + ID_MODEL_FROM_DATABASE=EZ Connect USB Ethernet + +usb:v0707pEE04* + ID_MODEL_FROM_DATABASE=SMCWUSB32 802.11b Wireless LAN Card + +usb:v0707pEE06* + ID_MODEL_FROM_DATABASE=SMC2862W-G v1 EZ Connect 802.11g Adapter [Intersil ISL3886] + +usb:v0707pEE13* + ID_MODEL_FROM_DATABASE=SMC2862W-G v2 EZ Connect 802.11g Adapter [Intersil ISL3887] + +usb:v0708* + ID_VENDOR_FROM_DATABASE=Putercom Co., Ltd + +usb:v0708p047E* + ID_MODEL_FROM_DATABASE=USB-1284 BRIDGE + +usb:v0709* + ID_VENDOR_FROM_DATABASE=Silicon Systems, Ltd (SSL) + +usb:v070A* + ID_VENDOR_FROM_DATABASE=Oki Electric Industry Co., Ltd + +usb:v070Ap4002* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v070Ap4003* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v070D* + ID_VENDOR_FROM_DATABASE=Comoss Electronic Co., Ltd + +usb:v070E* + ID_VENDOR_FROM_DATABASE=Excel Cell Electronic Co., Ltd + +usb:v0710* + ID_VENDOR_FROM_DATABASE=Connect Tech, Inc. + +usb:v0710p0001* + ID_MODEL_FROM_DATABASE=WhiteHeat (fake ID) + +usb:v0710p8001* + ID_MODEL_FROM_DATABASE=WhiteHeat + +usb:v0711* + ID_VENDOR_FROM_DATABASE=Magic Control Technology Corp. + +usb:v0711p0100* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0711p0180* + ID_MODEL_FROM_DATABASE=IRXpress Infrared Device + +usb:v0711p0181* + ID_MODEL_FROM_DATABASE=IRXpress Infrared Device + +usb:v0711p0200* + ID_MODEL_FROM_DATABASE=BAY-3U1S1P Serial Port + +usb:v0711p0210* + ID_MODEL_FROM_DATABASE=MCT1S Serial Port + +usb:v0711p0230* + ID_MODEL_FROM_DATABASE=MCT-232 Serial Port + +usb:v0711p0231* + ID_MODEL_FROM_DATABASE=PS/2 Mouse Port + +usb:v0711p0232* + ID_MODEL_FROM_DATABASE=Serial On Port + +usb:v0711p0240* + ID_MODEL_FROM_DATABASE=PS/2 to USB Converter + +usb:v0711p0300* + ID_MODEL_FROM_DATABASE=BAY-3U1S1P Parallel Port + +usb:v0711p0302* + ID_MODEL_FROM_DATABASE=Parallel Port + +usb:v0711p0900* + ID_MODEL_FROM_DATABASE=SVGA Adapter + +usb:v0711p5001* + ID_MODEL_FROM_DATABASE=Trigger UV-002BD[Startech USBVGAE] + +usb:v0711p5100* + ID_MODEL_FROM_DATABASE=Magic Control Technology Corp. (USB2VGA dongle) + +usb:v0713* + ID_VENDOR_FROM_DATABASE=Interval Research Corp. + +usb:v0714* + ID_VENDOR_FROM_DATABASE=NewMotion, Inc. + +usb:v0714p0003* + ID_MODEL_FROM_DATABASE=ADB converter + +usb:v0717* + ID_VENDOR_FROM_DATABASE=ZNK Corp. + +usb:v0718* + ID_VENDOR_FROM_DATABASE=Imation Corp. + +usb:v0718p0002* + ID_MODEL_FROM_DATABASE=SuperDisk 120MB + +usb:v0718p0003* + ID_MODEL_FROM_DATABASE=SuperDisk 120MB (Authenticated) + +usb:v0718p0060* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0061* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0062* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0063* + ID_MODEL_FROM_DATABASE=Swivel Flash Drive + +usb:v0718p0064* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0065* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0066* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0067* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0068* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v0718p0084* + ID_MODEL_FROM_DATABASE=Flash Drive Mini + +usb:v0718p043C* + ID_MODEL_FROM_DATABASE=Flash drive 16GB [Nano Pro] + +usb:v0718p0582* + ID_MODEL_FROM_DATABASE=Revo Flash Drive + +usb:v0718p0622* + ID_MODEL_FROM_DATABASE=TDK Trans-It 4GB + +usb:v0718p0624* + ID_MODEL_FROM_DATABASE=TDK Trans-It 16GB + +usb:v0718p1120* + ID_MODEL_FROM_DATABASE=RDX External dock (redbud) + +usb:v0718pD000* + ID_MODEL_FROM_DATABASE=Disc Stakka CD/DVD Manager + +usb:v0719* + ID_VENDOR_FROM_DATABASE=Tremon Enterprises Co., Ltd + +usb:v071B* + ID_VENDOR_FROM_DATABASE=Domain Technologies, Inc. + +usb:v071Bp0002* + ID_MODEL_FROM_DATABASE=DTI-56362-USB Digital Interface Unit + +usb:v071Bp0101* + ID_MODEL_FROM_DATABASE=Audio4-USB DSP Data Acquisition Unit + +usb:v071Bp0184* + ID_MODEL_FROM_DATABASE=Archos 2 8GB EM184RB + +usb:v071Bp0201* + ID_MODEL_FROM_DATABASE=Audio4-5410 DSP Data Acquisition Unit + +usb:v071Bp0301* + ID_MODEL_FROM_DATABASE=SB-USB JTAG Emulator + +usb:v071Bp3203* + ID_MODEL_FROM_DATABASE=Rockchip Media Player + +usb:v071Bp32BB* + ID_MODEL_FROM_DATABASE=Music Mediatouch + +usb:v071C* + ID_VENDOR_FROM_DATABASE=Xionics Document Technologies, Inc. + +usb:v071D* + ID_VENDOR_FROM_DATABASE=Eicon Networks Corp. + +usb:v071Dp1000* + ID_MODEL_FROM_DATABASE=Diva 2.01 S/T [PSB2115F] + +usb:v071Dp1003* + ID_MODEL_FROM_DATABASE=Diva ISDN 2.0 + +usb:v071Dp1005* + ID_MODEL_FROM_DATABASE=Diva ISDN 4.0 [HFC-S] + +usb:v071Dp2000* + ID_MODEL_FROM_DATABASE=Teledat Surf + +usb:v071E* + ID_VENDOR_FROM_DATABASE=Ariston Technologies + +usb:v0723* + ID_VENDOR_FROM_DATABASE=Centillium Communications Corp. + +usb:v0723p0002* + ID_MODEL_FROM_DATABASE=Palladia 300/400 Adsl Modem + +usb:v0726* + ID_VENDOR_FROM_DATABASE=Vanguard International Semiconductor-America + +usb:v0729* + ID_VENDOR_FROM_DATABASE=Amitm + +usb:v0729p1000* + ID_MODEL_FROM_DATABASE=USC-1000 Serial Port + +usb:v072E* + ID_VENDOR_FROM_DATABASE=Sunix Co., Ltd + +usb:v072F* + ID_VENDOR_FROM_DATABASE=Advanced Card Systems, Ltd + +usb:v072Fp0001* + ID_MODEL_FROM_DATABASE=AC1030-based SmartCard Reader + +usb:v072Fp0008* + ID_MODEL_FROM_DATABASE=ACR 80 Smart Card Reader + +usb:v072Fp0100* + ID_MODEL_FROM_DATABASE=AET65 + +usb:v072Fp0101* + ID_MODEL_FROM_DATABASE=AET65 + +usb:v072Fp0102* + ID_MODEL_FROM_DATABASE=AET62 + +usb:v072Fp0103* + ID_MODEL_FROM_DATABASE=AET62 + +usb:v072Fp0901* + ID_MODEL_FROM_DATABASE=ACR1281U-C4 (BSI) + +usb:v072Fp1000* + ID_MODEL_FROM_DATABASE=PLDT Drive + +usb:v072Fp1001* + ID_MODEL_FROM_DATABASE=PLDT Drive + +usb:v072Fp2011* + ID_MODEL_FROM_DATABASE=ACR88U + +usb:v072Fp2100* + ID_MODEL_FROM_DATABASE=ACR128U + +usb:v072Fp2200* + ID_MODEL_FROM_DATABASE=ACR122U + +usb:v072Fp220A* + ID_MODEL_FROM_DATABASE=ACR1281U-C5 (BSI) + +usb:v072Fp220C* + ID_MODEL_FROM_DATABASE=ACR1283 Bootloader + +usb:v072Fp220F* + ID_MODEL_FROM_DATABASE=ACR1281U-C2 (qPBOC) + +usb:v072Fp2211* + ID_MODEL_FROM_DATABASE=ACR1261 1S Dual Reader + +usb:v072Fp2214* + ID_MODEL_FROM_DATABASE=ACR1222 1SAM PICC Reader + +usb:v072Fp2215* + ID_MODEL_FROM_DATABASE=ACR1281 2S CL Reader + +usb:v072Fp221A* + ID_MODEL_FROM_DATABASE=ACR1251U-A1 + +usb:v072Fp221B* + ID_MODEL_FROM_DATABASE=ACR1251U-C + +usb:v072Fp2224* + ID_MODEL_FROM_DATABASE=ACR1281 1S Dual Reader + +usb:v072Fp222B* + ID_MODEL_FROM_DATABASE=ACR1222U-C8 + +usb:v072Fp222C* + ID_MODEL_FROM_DATABASE=ACR1283L-D2 + +usb:v072Fp222D* + ID_MODEL_FROM_DATABASE=[OEM Reader] + +usb:v072Fp222E* + ID_MODEL_FROM_DATABASE=ACR123U + +usb:v072Fp2242* + ID_MODEL_FROM_DATABASE=ACR1251 1S Dual Reader + +usb:v072Fp8002* + ID_MODEL_FROM_DATABASE=AET63 BioTRUSTKey + +usb:v072Fp8003* + ID_MODEL_FROM_DATABASE=ACR120 + +usb:v072Fp8103* + ID_MODEL_FROM_DATABASE=ACR120 + +usb:v072Fp8201* + ID_MODEL_FROM_DATABASE=APG8201 + +usb:v072Fp8900* + ID_MODEL_FROM_DATABASE=ACR89U-A1 + +usb:v072Fp8901* + ID_MODEL_FROM_DATABASE=ACR89U-A2 + +usb:v072Fp8902* + ID_MODEL_FROM_DATABASE=ACR89U-A3 + +usb:v072Fp9000* + ID_MODEL_FROM_DATABASE=ACR38 AC1038-based Smart Card Reader + +usb:v072Fp9006* + ID_MODEL_FROM_DATABASE=CryptoMate + +usb:v072Fp90CC* + ID_MODEL_FROM_DATABASE=ACR38 SmartCard Reader + +usb:v072Fp90CE* + ID_MODEL_FROM_DATABASE=[OEM Reader] + +usb:v072Fp90CF* + ID_MODEL_FROM_DATABASE=ACR38 SAM Smart Card Reader + +usb:v072Fp90D0* + ID_MODEL_FROM_DATABASE=PertoSmart EMV - Card Reader + +usb:v072Fp90D2* + ID_MODEL_FROM_DATABASE=ACR83U + +usb:v072Fp90D8* + ID_MODEL_FROM_DATABASE=ACR3801 + +usb:v072Fp90DB* + ID_MODEL_FROM_DATABASE=CryptoMate64 + +usb:v072FpB000* + ID_MODEL_FROM_DATABASE=ACR3901U + +usb:v072FpB100* + ID_MODEL_FROM_DATABASE=ACR39U + +usb:v072FpB101* + ID_MODEL_FROM_DATABASE=ACR39K + +usb:v072FpB102* + ID_MODEL_FROM_DATABASE=ACR39T + +usb:v072FpB103* + ID_MODEL_FROM_DATABASE=ACR39F + +usb:v072FpB104* + ID_MODEL_FROM_DATABASE=ACR39U-SAM + +usb:v072FpB106* + ID_MODEL_FROM_DATABASE=ACOS5T2 + +usb:v072FpB200* + ID_MODEL_FROM_DATABASE=ACOS5T1 + +usb:v072FpB301* + ID_MODEL_FROM_DATABASE=ACR32-A1 + +usb:v0731* + ID_VENDOR_FROM_DATABASE=Susteen, Inc. + +usb:v0731p0528* + ID_MODEL_FROM_DATABASE=SonyEricsson DCU-11 Cable + +usb:v0732* + ID_VENDOR_FROM_DATABASE=Goldfull Electronics & Telecommunications Corp. + +usb:v0733* + ID_VENDOR_FROM_DATABASE=ViewQuest Technologies, Inc. + +usb:v0733p0101* + ID_MODEL_FROM_DATABASE=Digital Video Camera + +usb:v0733p0110* + ID_MODEL_FROM_DATABASE=VQ110 Video Camera + +usb:v0733p0401* + ID_MODEL_FROM_DATABASE=CS330 Webcam + +usb:v0733p0402* + ID_MODEL_FROM_DATABASE=M-318B Webcam + +usb:v0733p0430* + ID_MODEL_FROM_DATABASE=Intel Pro Share Webcam + +usb:v0733p0630* + ID_MODEL_FROM_DATABASE=VQ630 Dual Mode Digital Camera(Bulk) + +usb:v0733p0631* + ID_MODEL_FROM_DATABASE=Hercules Dualpix + +usb:v0733p0780* + ID_MODEL_FROM_DATABASE=Smart Cam Deluxe(composite) + +usb:v0733p1310* + ID_MODEL_FROM_DATABASE=Epsilon 1.3/Jenoptik JD C1.3/UMAX AstraPix 470 (mass storage mode) + +usb:v0733p1311* + ID_MODEL_FROM_DATABASE=Epsilon 1.3/Jenoptik JD C1.3/UMAX AstraPix 470 (PC Cam mode) + +usb:v0733p1314* + ID_MODEL_FROM_DATABASE=Mercury 2.1MEG Deluxe Classic Cam + +usb:v0733p2211* + ID_MODEL_FROM_DATABASE=Jenoptik jdc 21 LCD Camera + +usb:v0733p2221* + ID_MODEL_FROM_DATABASE=Mercury Digital Pro 3.1p + +usb:v0733p3261* + ID_MODEL_FROM_DATABASE=Concord 3045 spca536a Camera + +usb:v0733p3281* + ID_MODEL_FROM_DATABASE=Cyberpix S550V + +usb:v0734* + ID_VENDOR_FROM_DATABASE=Lasat Communications A/S + +usb:v0734p0001* + ID_MODEL_FROM_DATABASE=560V Modem + +usb:v0734p0002* + ID_MODEL_FROM_DATABASE=Lasat 560V Modem + +usb:v0734p043A* + ID_MODEL_FROM_DATABASE=DVS Audio + +usb:v0734p043B* + ID_MODEL_FROM_DATABASE=3DeMon USB Capture + +usb:v0735* + ID_VENDOR_FROM_DATABASE=Asuscom Network + +usb:v0735p2100* + ID_MODEL_FROM_DATABASE=ISDN Adapter + +usb:v0735p2101* + ID_MODEL_FROM_DATABASE=ISDN Adapter + +usb:v0735p6694* + ID_MODEL_FROM_DATABASE=ISDNlink 128K + +usb:v0735pC541* + ID_MODEL_FROM_DATABASE=ISDN TA 280 + +usb:v0736* + ID_VENDOR_FROM_DATABASE=Lorom Industrial Co., Ltd + +usb:v0738* + ID_VENDOR_FROM_DATABASE=Mad Catz, Inc. + +usb:v0738p4507* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4516* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4520* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4526* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4536* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4540* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4556* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4566* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4576* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4586* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p4588* + ID_MODEL_FROM_DATABASE=XBox Device + +usb:v0738p8818* + ID_MODEL_FROM_DATABASE=Street Fighter IV Arcade FightStick (PS3) + +usb:v073A* + ID_VENDOR_FROM_DATABASE=Chaplet Systems, Inc. + +usb:v073Ap2230* + ID_MODEL_FROM_DATABASE=infrared dongle for remote + +usb:v073B* + ID_VENDOR_FROM_DATABASE=Suncom Technologies + +usb:v073C* + ID_VENDOR_FROM_DATABASE=Industrial Electronic Engineers, Inc. + +usb:v073Cp0305* + ID_MODEL_FROM_DATABASE=Pole Display (PC305-3415 2 x 20 Line Display) + +usb:v073Cp0322* + ID_MODEL_FROM_DATABASE=Pole Display (PC322-3415 2 x 20 Line Display) + +usb:v073Cp0324* + ID_MODEL_FROM_DATABASE=Pole Display (LB324-USB 4 x 20 Line Display) + +usb:v073Cp0330* + ID_MODEL_FROM_DATABASE=Pole Display (P330-3415 2 x 20 Line Display) + +usb:v073Cp0424* + ID_MODEL_FROM_DATABASE=Pole Display (SP324-4415 4 x 20 Line Display) + +usb:v073Cp0450* + ID_MODEL_FROM_DATABASE=Pole Display (L450-USB Graphic Line Display) + +usb:v073Cp0505* + ID_MODEL_FROM_DATABASE=Pole Display (SPC505-3415 2 x 20 Line Display) + +usb:v073Cp0522* + ID_MODEL_FROM_DATABASE=Pole Display (SPC522-3415 2 x 20 Line Display) + +usb:v073Cp0624* + ID_MODEL_FROM_DATABASE=Pole Display (SP324-3415 4 x 20 Line Display) + +usb:v073D* + ID_VENDOR_FROM_DATABASE=Eutron S.p.a. + +usb:v073Dp0005* + ID_MODEL_FROM_DATABASE=Crypto Token + +usb:v073Dp0007* + ID_MODEL_FROM_DATABASE=CryptoIdentity CCID + +usb:v073Dp0025* + ID_MODEL_FROM_DATABASE=SmartKey 3 + +usb:v073Dp0C00* + ID_MODEL_FROM_DATABASE=Pocket Reader + +usb:v073Dp0D00* + ID_MODEL_FROM_DATABASE=StarSign Bio Token 3.0 EU + +usb:v073E* + ID_VENDOR_FROM_DATABASE=NEC, Inc. + +usb:v073Ep0301* + ID_MODEL_FROM_DATABASE=Game Pad + +usb:v0742* + ID_VENDOR_FROM_DATABASE=Stollmann + +usb:v0742p2008* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v0742p2009* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v0742p200A* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v0745* + ID_VENDOR_FROM_DATABASE=Syntech Information Co., Ltd + +usb:v0746* + ID_VENDOR_FROM_DATABASE=Onkyo Corp. + +usb:v0746p5500* + ID_MODEL_FROM_DATABASE=SE-U55 Audio Device + +usb:v0747* + ID_VENDOR_FROM_DATABASE=Labway Corp. + +usb:v0748* + ID_VENDOR_FROM_DATABASE=Strong Man Enterprise Co., Ltd + +usb:v0749* + ID_VENDOR_FROM_DATABASE=EVer Electronics Corp. + +usb:v074A* + ID_VENDOR_FROM_DATABASE=Ming Fortune Industry Co., Ltd + +usb:v074B* + ID_VENDOR_FROM_DATABASE=Polestar Tech. Corp. + +usb:v074C* + ID_VENDOR_FROM_DATABASE=C-C-C Group PLC + +usb:v074D* + ID_VENDOR_FROM_DATABASE=Micronas GmbH + +usb:v074Dp3553* + ID_MODEL_FROM_DATABASE=Composite USB-Device + +usb:v074Dp3554* + ID_MODEL_FROM_DATABASE=Composite USB-Device + +usb:v074Dp3556* + ID_MODEL_FROM_DATABASE=Composite USB-Device + +usb:v074E* + ID_VENDOR_FROM_DATABASE=Digital Stream Corp. + +usb:v074Ep0001* + ID_MODEL_FROM_DATABASE=PS/2 Adapter + +usb:v074Ep0002* + ID_MODEL_FROM_DATABASE=PS/2 Adapter + +usb:v0755* + ID_VENDOR_FROM_DATABASE=Aureal Semiconductor + +usb:v0757* + ID_VENDOR_FROM_DATABASE=Network Technologies, Inc. + +usb:v075B* + ID_VENDOR_FROM_DATABASE=Sophisticated Circuits, Inc. + +usb:v075Bp0001* + ID_MODEL_FROM_DATABASE=Kick-off! Watchdog + +usb:v0763* + ID_VENDOR_FROM_DATABASE=Midiman + +usb:v0763p0115* + ID_MODEL_FROM_DATABASE=O2 / KeyRig 25 + +usb:v0763p0117* + ID_MODEL_FROM_DATABASE=Trigger Finger + +usb:v0763p0119* + ID_MODEL_FROM_DATABASE=MidAir + +usb:v0763p0150* + ID_MODEL_FROM_DATABASE=M-Audio Uno + +usb:v0763p0160* + ID_MODEL_FROM_DATABASE=M-Audio 1x1 + +usb:v0763p0192* + ID_MODEL_FROM_DATABASE=M-Audio Keystation 88es + +usb:v0763p0193* + ID_MODEL_FROM_DATABASE=ProKeys 88 + +usb:v0763p0194* + ID_MODEL_FROM_DATABASE=ProKeys 88sx + +usb:v0763p0195* + ID_MODEL_FROM_DATABASE=Oxygen 8 v2 + +usb:v0763p0196* + ID_MODEL_FROM_DATABASE=Oxygen 49 + +usb:v0763p0197* + ID_MODEL_FROM_DATABASE=Oxygen 61 + +usb:v0763p0198* + ID_MODEL_FROM_DATABASE=Axiom 25 + +usb:v0763p0199* + ID_MODEL_FROM_DATABASE=Axiom 49 + +usb:v0763p019A* + ID_MODEL_FROM_DATABASE=Axiom 61 + +usb:v0763p019B* + ID_MODEL_FROM_DATABASE=KeyRig 49 + +usb:v0763p019C* + ID_MODEL_FROM_DATABASE=KeyStudio + +usb:v0763p1001* + ID_MODEL_FROM_DATABASE=MidiSport 2x2 + +usb:v0763p1002* + ID_MODEL_FROM_DATABASE=MidiSport 2x2 + +usb:v0763p1003* + ID_MODEL_FROM_DATABASE=MidiSport 2x2 + +usb:v0763p1010* + ID_MODEL_FROM_DATABASE=MidiSport 1x1 + +usb:v0763p1011* + ID_MODEL_FROM_DATABASE=MidiSport 1x1 + +usb:v0763p1014* + ID_MODEL_FROM_DATABASE=M-Audio Keystation Loader + +usb:v0763p1015* + ID_MODEL_FROM_DATABASE=M-Audio Keystation + +usb:v0763p1020* + ID_MODEL_FROM_DATABASE=Midisport 4x4 + +usb:v0763p1021* + ID_MODEL_FROM_DATABASE=MidiSport 4x4 + +usb:v0763p1030* + ID_MODEL_FROM_DATABASE=M-Audio MIDISPORT 8x8 + +usb:v0763p1031* + ID_MODEL_FROM_DATABASE=MidiSport 8x8/s Loader + +usb:v0763p1033* + ID_MODEL_FROM_DATABASE=MidiSport 8x8/s + +usb:v0763p1040* + ID_MODEL_FROM_DATABASE=M-Audio MidiSport 2x4 Loader + +usb:v0763p1041* + ID_MODEL_FROM_DATABASE=M-Audio MidiSport 2x4 + +usb:v0763p1110* + ID_MODEL_FROM_DATABASE=MidiSport 1x1 + +usb:v0763p2001* + ID_MODEL_FROM_DATABASE=M Audio Quattro + +usb:v0763p2002* + ID_MODEL_FROM_DATABASE=M Audio Duo + +usb:v0763p2003* + ID_MODEL_FROM_DATABASE=M Audio AudioPhile + +usb:v0763p2004* + ID_MODEL_FROM_DATABASE=M-Audio MobilePre + +usb:v0763p2006* + ID_MODEL_FROM_DATABASE=M-Audio Transit + +usb:v0763p2007* + ID_MODEL_FROM_DATABASE=M-Audio Sonica Theater + +usb:v0763p2008* + ID_MODEL_FROM_DATABASE=M-Audio Ozone + +usb:v0763p200D* + ID_MODEL_FROM_DATABASE=M-Audio OmniStudio + +usb:v0763p200F* + ID_MODEL_FROM_DATABASE=M-Audio MobilePre + +usb:v0763p2010* + ID_MODEL_FROM_DATABASE=M-Audio Fast Track + +usb:v0763p2012* + ID_MODEL_FROM_DATABASE=M-Audio Fast Track Pro + +usb:v0763p2013* + ID_MODEL_FROM_DATABASE=M-Audio JamLab + +usb:v0763p2015* + ID_MODEL_FROM_DATABASE=M-Audio RunTime DFU + +usb:v0763p2016* + ID_MODEL_FROM_DATABASE=M-Audio RunTime DFU + +usb:v0763p2019* + ID_MODEL_FROM_DATABASE=M-Audio Ozone Academic + +usb:v0763p201A* + ID_MODEL_FROM_DATABASE=M-Audio Micro + +usb:v0763p201B* + ID_MODEL_FROM_DATABASE=M-Audio RunTime DFU + +usb:v0763p201D* + ID_MODEL_FROM_DATABASE=M-Audio Producer + +usb:v0763p2024* + ID_MODEL_FROM_DATABASE=M-Audio Fast Track MKII + +usb:v0763p2080* + ID_MODEL_FROM_DATABASE=M-Audio Fast Track Ultra + +usb:v0763p2081* + ID_MODEL_FROM_DATABASE=M-Audio RunTime DFU / Fast Track Ultra 8R + +usb:v0763p2803* + ID_MODEL_FROM_DATABASE=M-Audio Audiophile DFU + +usb:v0763p2804* + ID_MODEL_FROM_DATABASE=M-Audio MobilePre DFU + +usb:v0763p2806* + ID_MODEL_FROM_DATABASE=M-Audio Transit DFU + +usb:v0763p2815* + ID_MODEL_FROM_DATABASE=M-Audio DFU + +usb:v0763p2816* + ID_MODEL_FROM_DATABASE=M-Audio DFU + +usb:v0763p281B* + ID_MODEL_FROM_DATABASE=M-Audio DFU + +usb:v0763p2880* + ID_MODEL_FROM_DATABASE=M-Audio DFU + +usb:v0763p2881* + ID_MODEL_FROM_DATABASE=M-Audio DFU + +usb:v0764* + ID_VENDOR_FROM_DATABASE=Cyber Power System, Inc. + +usb:v0764p0005* + ID_MODEL_FROM_DATABASE=Cyber Power UPS + +usb:v0764p0501* + ID_MODEL_FROM_DATABASE=CP1500 AVR UPS + +usb:v0764p0601* + ID_MODEL_FROM_DATABASE=PR1500LCDRT2U UPS + +usb:v0765* + ID_VENDOR_FROM_DATABASE=X-Rite, Inc. + +usb:v0765p5001* + ID_MODEL_FROM_DATABASE=Huey PRO Colorimeter + +usb:v0765p5010* + ID_MODEL_FROM_DATABASE=X-Rite Pantone Color Sensor + +usb:v0765p5020* + ID_MODEL_FROM_DATABASE=i1 Display Pro + +usb:v0765p6003* + ID_MODEL_FROM_DATABASE=ColorMunki Smile + +usb:v0765pD094* + ID_MODEL_FROM_DATABASE=X-Rite DTP94 [Quato Silver Haze Pro] + +usb:v0766* + ID_VENDOR_FROM_DATABASE=Jess-Link Products Co., Ltd + +usb:v0766p001B* + ID_MODEL_FROM_DATABASE=Packard Bell Go + +usb:v0766p0204* + ID_MODEL_FROM_DATABASE=TopSpeed Cyberlink Remote Control + +usb:v0767* + ID_VENDOR_FROM_DATABASE=Tokheim Corp. + +usb:v0768* + ID_VENDOR_FROM_DATABASE=Camtel Technology Corp. + +usb:v0768p0006* + ID_MODEL_FROM_DATABASE=Camtel Technology USB TV Genie Pro FM Model TVB330 + +usb:v0768p0023* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0769* + ID_VENDOR_FROM_DATABASE=Surecom Technology Corp. + +usb:v0769p11F2* + ID_MODEL_FROM_DATABASE=EP-9001-g 802.11g 54M WLAN Adapter + +usb:v0769p11F3* + ID_MODEL_FROM_DATABASE=RT2570 + +usb:v0769p11F7* + ID_MODEL_FROM_DATABASE=802.11g 54M WLAN Adapter + +usb:v0769p31F3* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v076A* + ID_VENDOR_FROM_DATABASE=Smart Technology Enablers, Inc. + +usb:v076B* + ID_VENDOR_FROM_DATABASE=OmniKey AG + +usb:v076Bp0596* + ID_MODEL_FROM_DATABASE=CardMan 2020 + +usb:v076Bp1021* + ID_MODEL_FROM_DATABASE=CardMan 1021 + +usb:v076Bp1221* + ID_MODEL_FROM_DATABASE=CardMan 1221 + +usb:v076Bp1784* + ID_MODEL_FROM_DATABASE=CardMan 6020 + +usb:v076Bp3021* + ID_MODEL_FROM_DATABASE=CardMan 3121 + +usb:v076Bp3022* + ID_MODEL_FROM_DATABASE=CardMan 3021 + +usb:v076Bp3610* + ID_MODEL_FROM_DATABASE=CardMan 3620 + +usb:v076Bp3621* + ID_MODEL_FROM_DATABASE=CardMan 3621 + +usb:v076Bp3821* + ID_MODEL_FROM_DATABASE=CardMan 3821 + +usb:v076Bp4321* + ID_MODEL_FROM_DATABASE=CardMan 4321 + +usb:v076Bp5121* + ID_MODEL_FROM_DATABASE=CardMan 5121 + +usb:v076Bp5125* + ID_MODEL_FROM_DATABASE=CardMan 5125 + +usb:v076Bp5321* + ID_MODEL_FROM_DATABASE=CardMan 5321 + +usb:v076Bp5340* + ID_MODEL_FROM_DATABASE=CardMan 5021 CL + +usb:v076Bp6622* + ID_MODEL_FROM_DATABASE=CardMan 6121 + +usb:v076BpA011* + ID_MODEL_FROM_DATABASE=CCID Smart Card Reader Keyboard + +usb:v076BpA021* + ID_MODEL_FROM_DATABASE=CCID Smart Card Reader + +usb:v076BpA022* + ID_MODEL_FROM_DATABASE=CardMan Smart@Link + +usb:v076BpC000* + ID_MODEL_FROM_DATABASE=CardMan 3x21 CS + +usb:v076BpC001* + ID_MODEL_FROM_DATABASE=CardMan 5121 CS + +usb:v076C* + ID_VENDOR_FROM_DATABASE=Partner Tech + +usb:v076D* + ID_VENDOR_FROM_DATABASE=Denso Corp. + +usb:v076E* + ID_VENDOR_FROM_DATABASE=Kuan Tech Enterprise Co., Ltd + +usb:v076F* + ID_VENDOR_FROM_DATABASE=Jhen Vei Electronic Co., Ltd + +usb:v0770* + ID_VENDOR_FROM_DATABASE=Welch Allyn, Inc - Medical Division + +usb:v0771* + ID_VENDOR_FROM_DATABASE=Observator Instruments BV + +usb:v0771p4455* + ID_MODEL_FROM_DATABASE=OMC45III + +usb:v0771pAE0F* + ID_MODEL_FROM_DATABASE=OMC45III + +usb:v0772* + ID_VENDOR_FROM_DATABASE=Your data Our Care + +usb:v0774* + ID_VENDOR_FROM_DATABASE=AmTRAN Technology Co., Ltd + +usb:v0775* + ID_VENDOR_FROM_DATABASE=Longshine Electronics Corp. + +usb:v0776* + ID_VENDOR_FROM_DATABASE=Inalways Corp. + +usb:v0777* + ID_VENDOR_FROM_DATABASE=Comda Enterprise Corp. + +usb:v0778* + ID_VENDOR_FROM_DATABASE=Volex, Inc. + +usb:v0779* + ID_VENDOR_FROM_DATABASE=Fairchild Semiconductor + +usb:v077A* + ID_VENDOR_FROM_DATABASE=Sankyo Seiki Mfg. Co., Ltd + +usb:v077B* + ID_VENDOR_FROM_DATABASE=Linksys + +usb:v077Bp08BE* + ID_MODEL_FROM_DATABASE=BEFCMU10 v4 Cable Modem + +usb:v077Bp2219* + ID_MODEL_FROM_DATABASE=WUSB11 V2.6 802.11b Adapter + +usb:v077Bp2226* + ID_MODEL_FROM_DATABASE=USB200M 100baseTX Adapter + +usb:v077Bp2227* + ID_MODEL_FROM_DATABASE=Network Everywhere NWU11B + +usb:v077C* + ID_VENDOR_FROM_DATABASE=Forward Electronics Co., Ltd + +usb:v077Cp0005* + ID_MODEL_FROM_DATABASE=NEC Keyboard + +usb:v077D* + ID_VENDOR_FROM_DATABASE=Griffin Technology + +usb:v077Dp0223* + ID_MODEL_FROM_DATABASE=IMic Audio In/Out + +usb:v077Dp0405* + ID_MODEL_FROM_DATABASE=iMate, ADB Adapter + +usb:v077Dp0410* + ID_MODEL_FROM_DATABASE=PowerMate + +usb:v077Dp041A* + ID_MODEL_FROM_DATABASE=PowerWave + +usb:v077Dp04AA* + ID_MODEL_FROM_DATABASE=SoundKnob + +usb:v077Dp07AF* + ID_MODEL_FROM_DATABASE=iMic + +usb:v077Dp1016* + ID_MODEL_FROM_DATABASE=AirClick + +usb:v077Dp627A* + ID_MODEL_FROM_DATABASE=Radio SHARK + +usb:v077F* + ID_VENDOR_FROM_DATABASE=Well Excellent & Most Corp. + +usb:v0780* + ID_VENDOR_FROM_DATABASE=Sagem Monetel GmbH + +usb:v0780p1202* + ID_MODEL_FROM_DATABASE=ORGA 900 Smart Card Terminal Virtual Com Port + +usb:v0780p1302* + ID_MODEL_FROM_DATABASE=ORGA 6000 Smart Card Terminal Virtual Com Port + +usb:v0780p1303* + ID_MODEL_FROM_DATABASE=ORGA 6000 Smart Card Terminal USB RNDIS + +usb:v0780pDF55* + ID_MODEL_FROM_DATABASE=ORGA 900/6000 Smart Card Terminal DFU + +usb:v0781* + ID_VENDOR_FROM_DATABASE=SanDisk Corp. + +usb:v0781p0001* + ID_MODEL_FROM_DATABASE=SDDR-05a ImageMate CompactFlash Reader + +usb:v0781p0002* + ID_MODEL_FROM_DATABASE=SDDR-31 ImageMate II CompactFlash Reader + +usb:v0781p0005* + ID_MODEL_FROM_DATABASE=SDDR-05b (CF II) ImageMate CompactFlash Reader + +usb:v0781p0100* + ID_MODEL_FROM_DATABASE=ImageMate SDDR-12 + +usb:v0781p0200* + ID_MODEL_FROM_DATABASE=SDDR-09 (SSFDC) ImageMate SmartMedia Reader [eusb] + +usb:v0781p0400* + ID_MODEL_FROM_DATABASE=SecureMate SD/MMC Reader + +usb:v0781p0621* + ID_MODEL_FROM_DATABASE=SDDR-86 Imagemate 6-in-1 Reader + +usb:v0781p0720* + ID_MODEL_FROM_DATABASE=Sansa C200 series in recovery mode + +usb:v0781p0729* + ID_MODEL_FROM_DATABASE=Sansa E200 series in recovery mode + +usb:v0781p0810* + ID_MODEL_FROM_DATABASE=SDDR-75 ImageMate CF-SM Reader + +usb:v0781p0830* + ID_MODEL_FROM_DATABASE=ImageMate CF/MMC/SD Reader + +usb:v0781p1234* + ID_MODEL_FROM_DATABASE=Cruzer Mini Flash Drive + +usb:v0781p5150* + ID_MODEL_FROM_DATABASE=SDCZ2 Cruzer Mini Flash Drive (thin) + +usb:v0781p5151* + ID_MODEL_FROM_DATABASE=Cruzer Micro Flash Drive + +usb:v0781p5153* + ID_MODEL_FROM_DATABASE=Cruzer Flash Drive + +usb:v0781p5204* + ID_MODEL_FROM_DATABASE=Cruzer Crossfire + +usb:v0781p5402* + ID_MODEL_FROM_DATABASE=U3 Cruzer Micro + +usb:v0781p5406* + ID_MODEL_FROM_DATABASE=Cruzer Micro U3 + +usb:v0781p5408* + ID_MODEL_FROM_DATABASE=Cruzer Titanium U3 + +usb:v0781p540E* + ID_MODEL_FROM_DATABASE=Cruzer Contour Flash Drive + +usb:v0781p5530* + ID_MODEL_FROM_DATABASE=Cruzer + +usb:v0781p5567* + ID_MODEL_FROM_DATABASE=Cruzer Blade + +usb:v0781p556B* + ID_MODEL_FROM_DATABASE=Cruzer Edge + +usb:v0781p556C* + ID_MODEL_FROM_DATABASE=Ultra + +usb:v0781p556D* + ID_MODEL_FROM_DATABASE=Memory Vault + +usb:v0781p5571* + ID_MODEL_FROM_DATABASE=Cruzer Fit + +usb:v0781p5575* + ID_MODEL_FROM_DATABASE=Cruzer Glide + +usb:v0781p5576* + ID_MODEL_FROM_DATABASE=Cruzer Facet + +usb:v0781p557D* + ID_MODEL_FROM_DATABASE=Cruzer Force (64GB) + +usb:v0781p5580* + ID_MODEL_FROM_DATABASE=SDCZ80 Flash Drive + +usb:v0781p5581* + ID_MODEL_FROM_DATABASE=Ultra + +usb:v0781p5E10* + ID_MODEL_FROM_DATABASE=Encrypted + +usb:v0781p6100* + ID_MODEL_FROM_DATABASE=Ultra II SD Plus 2GB + +usb:v0781p7100* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7101* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v0781p7102* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7103* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7104* + ID_MODEL_FROM_DATABASE=Cruzer Micro Mini 256MB Flash Drive + +usb:v0781p7105* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7106* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7112* + ID_MODEL_FROM_DATABASE=Cruzer Micro 128MB Flash Drive + +usb:v0781p7113* + ID_MODEL_FROM_DATABASE=Cruzer Micro 256MB Flash Drive + +usb:v0781p7114* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7115* + ID_MODEL_FROM_DATABASE=Cruzer Mini + +usb:v0781p7301* + ID_MODEL_FROM_DATABASE=Sansa e100 series (mtp) + +usb:v0781p7302* + ID_MODEL_FROM_DATABASE=Sansa e100 series (msc) + +usb:v0781p7400* + ID_MODEL_FROM_DATABASE=Sansa M200 series (mtp) + +usb:v0781p7401* + ID_MODEL_FROM_DATABASE=Sansa M200 series (msc) + +usb:v0781p7420* + ID_MODEL_FROM_DATABASE=Sansa E200 series (mtp) + +usb:v0781p7421* + ID_MODEL_FROM_DATABASE=Sansa E200 Series (msc) + +usb:v0781p7422* + ID_MODEL_FROM_DATABASE=Sansa E200 series v2 (mtp) + +usb:v0781p7423* + ID_MODEL_FROM_DATABASE=Sansa E200 series v2 (msc) + +usb:v0781p7430* + ID_MODEL_FROM_DATABASE=Sansa M200 series + +usb:v0781p7431* + ID_MODEL_FROM_DATABASE=Sansa M200 series V4 (msc) + +usb:v0781p7432* + ID_MODEL_FROM_DATABASE=Sansa Clip (mtp) + +usb:v0781p7433* + ID_MODEL_FROM_DATABASE=Sansa Clip (msc) + +usb:v0781p7434* + ID_MODEL_FROM_DATABASE=Sansa Clip V2 (mtp) + +usb:v0781p7435* + ID_MODEL_FROM_DATABASE=Sansa Clip V2 (msc) + +usb:v0781p7450* + ID_MODEL_FROM_DATABASE=Sansa C250 + +usb:v0781p7451* + ID_MODEL_FROM_DATABASE=Sansa C240 + +usb:v0781p7460* + ID_MODEL_FROM_DATABASE=Sansa Express + +usb:v0781p7480* + ID_MODEL_FROM_DATABASE=Sansa Connect + +usb:v0781p7481* + ID_MODEL_FROM_DATABASE=Sansa Connect (in recovery mode) + +usb:v0781p74B0* + ID_MODEL_FROM_DATABASE=Sansa View (msc) + +usb:v0781p74B1* + ID_MODEL_FROM_DATABASE=Sansa View (mtp) + +usb:v0781p74C0* + ID_MODEL_FROM_DATABASE=Sansa Fuze (mtp) + +usb:v0781p74C1* + ID_MODEL_FROM_DATABASE=Sansa Fuze (msc) + +usb:v0781p74C2* + ID_MODEL_FROM_DATABASE=Sansa Fuze V2 (mtp) + +usb:v0781p74C3* + ID_MODEL_FROM_DATABASE=Sansa Fuze V2 (msc) + +usb:v0781p74D0* + ID_MODEL_FROM_DATABASE=Sansa Clip+ (mtp) + +usb:v0781p74D1* + ID_MODEL_FROM_DATABASE=Sansa Clip+ (msc) + +usb:v0781p74E5* + ID_MODEL_FROM_DATABASE=Sansa Clip Zip + +usb:v0781p8181* + ID_MODEL_FROM_DATABASE=Pen Flash + +usb:v0781p8183* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v0781p8185* + ID_MODEL_FROM_DATABASE=SDCZ2 Cruzer Mini Flash Drive (older, thick) + +usb:v0781p8888* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0781p8889* + ID_MODEL_FROM_DATABASE=SDDR-88 Imagemate 8-in-1 Reader + +usb:v0781p8919* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0781p8989* + ID_MODEL_FROM_DATABASE=ImageMate 12-in-1 Reader + +usb:v0781p9191* + ID_MODEL_FROM_DATABASE=ImageMate CF + +usb:v0781p9219* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0781p9292* + ID_MODEL_FROM_DATABASE=ImageMate CF Reader/Writer + +usb:v0781p9393* + ID_MODEL_FROM_DATABASE=ImageMate SD-MMC + +usb:v0781p9595* + ID_MODEL_FROM_DATABASE=ImageMate xD-SM + +usb:v0781p9797* + ID_MODEL_FROM_DATABASE=ImageMate MS-PRO + +usb:v0781p9919* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0781p9999* + ID_MODEL_FROM_DATABASE=SDDR-99 5-in-1 Reader + +usb:v0781pA7C1* + ID_MODEL_FROM_DATABASE=Storage device (SD card reader) + +usb:v0781pA7E8* + ID_MODEL_FROM_DATABASE=SDDR-113 MicroMate SDHC Reader + +usb:v0781pB2B3* + ID_MODEL_FROM_DATABASE=SDDR-103 MobileMate SD+ Reader + +usb:v0781pB4B5* + ID_MODEL_FROM_DATABASE=SDDR-89 V4 ImageMate 12-in-1 Reader + +usb:v0781pB6BA* + ID_MODEL_FROM_DATABASE=CF SDDR-289 + +usb:v0782* + ID_VENDOR_FROM_DATABASE=Trackerball + +usb:v0783* + ID_VENDOR_FROM_DATABASE=C3PO + +usb:v0783p0003* + ID_MODEL_FROM_DATABASE=LTC31 SmartCard Reader + +usb:v0783p0006* + ID_MODEL_FROM_DATABASE=LTC31v2 + +usb:v0783p0009* + ID_MODEL_FROM_DATABASE=KBR36 + +usb:v0783p0010* + ID_MODEL_FROM_DATABASE=LTC32 + +usb:v0784* + ID_VENDOR_FROM_DATABASE=Vivitar, Inc. + +usb:v0784p0100* + ID_MODEL_FROM_DATABASE=Vivicam 2655 + +usb:v0784p1310* + ID_MODEL_FROM_DATABASE=Vivicam 3305 + +usb:v0784p1688* + ID_MODEL_FROM_DATABASE=Vivicam 3665 + +usb:v0784p1689* + ID_MODEL_FROM_DATABASE=Gateway DC-M42/Labtec DC-505/Vivitar Vivicam 3705 + +usb:v0784p2620* + ID_MODEL_FROM_DATABASE=AOL Photocam Plus + +usb:v0784p2888* + ID_MODEL_FROM_DATABASE=Polaroid DC700 + +usb:v0784p3330* + ID_MODEL_FROM_DATABASE=Nytec ND-3200 Camera + +usb:v0784p4300* + ID_MODEL_FROM_DATABASE=Traveler D1 + +usb:v0784p5260* + ID_MODEL_FROM_DATABASE=Werlisa Sport PX 100 / JVC GC-A33 Camera + +usb:v0784p5300* + ID_MODEL_FROM_DATABASE=Pretec dc530 + +usb:v0785* + ID_VENDOR_FROM_DATABASE=NTT-ME + +usb:v0785p0001* + ID_MODEL_FROM_DATABASE=MN128mini-V ISDN TA + +usb:v0785p0003* + ID_MODEL_FROM_DATABASE=MN128mini-J ISDN TA + +usb:v0789* + ID_VENDOR_FROM_DATABASE=Logitec Corp. + +usb:v0789p0026* + ID_MODEL_FROM_DATABASE=LHD Device + +usb:v0789p0033* + ID_MODEL_FROM_DATABASE=DVD Multi-plus unit LDR-H443SU2 + +usb:v0789p0063* + ID_MODEL_FROM_DATABASE=LDR Device + +usb:v0789p0064* + ID_MODEL_FROM_DATABASE=LDR-R Device + +usb:v0789p00B3* + ID_MODEL_FROM_DATABASE=DVD Multi-plus unit LDR-H443U2 + +usb:v0789p0105* + ID_MODEL_FROM_DATABASE=LAN-TX/U1H2 10/100 Ethernet Adapter [pegasus II] + +usb:v0789p010C* + ID_MODEL_FROM_DATABASE=Realtek RTL8187 Wireless 802.11g 54Mbps Network Adapter + +usb:v0789p0160* + ID_MODEL_FROM_DATABASE=LAN-GTJ/U2A + +usb:v0789p0162* + ID_MODEL_FROM_DATABASE=LAN-WN22/U2 Wireless LAN Adapter + +usb:v0789p0163* + ID_MODEL_FROM_DATABASE=LAN-WN12/U2 Wireless LAN Adapter + +usb:v0789p0164* + ID_MODEL_FROM_DATABASE=LAN-W150/U2M Wireless LAN Adapter + +usb:v0789p0166* + ID_MODEL_FROM_DATABASE=LAN-W300N/U2 Wireless LAN Adapter + +usb:v0789p0168* + ID_MODEL_FROM_DATABASE=LAN-W150N/U2 Wireless LAN Adapter + +usb:v0789p0170* + ID_MODEL_FROM_DATABASE=LAN-W300AN/U2 Wireless LAN Adapter + +usb:v078B* + ID_VENDOR_FROM_DATABASE=Happ Controls, Inc. + +usb:v078Bp0010* + ID_MODEL_FROM_DATABASE=Driving UGCI + +usb:v078Bp0020* + ID_MODEL_FROM_DATABASE=Flying UGCI + +usb:v078Bp0030* + ID_MODEL_FROM_DATABASE=Fighting UGCI + +usb:v078C* + ID_VENDOR_FROM_DATABASE=GTCO/CalComp + +usb:v078Cp0090* + ID_MODEL_FROM_DATABASE=Tablet Adapter + +usb:v078Cp0100* + ID_MODEL_FROM_DATABASE=Tablet Adapter + +usb:v078Cp0200* + ID_MODEL_FROM_DATABASE=Tablet Adapter + +usb:v078Cp0300* + ID_MODEL_FROM_DATABASE=Tablet Adapter + +usb:v078Cp0400* + ID_MODEL_FROM_DATABASE=Digitizer (Whiteboard) + +usb:v078E* + ID_VENDOR_FROM_DATABASE=Brincom, Inc. + +usb:v0790* + ID_VENDOR_FROM_DATABASE=Pro-Image Manufacturing Co., Ltd + +usb:v0791* + ID_VENDOR_FROM_DATABASE=Copartner Wire and Cable Mfg. Corp. + +usb:v0792* + ID_VENDOR_FROM_DATABASE=Axis Communications AB + +usb:v0793* + ID_VENDOR_FROM_DATABASE=Wha Yu Industrial Co., Ltd + +usb:v0794* + ID_VENDOR_FROM_DATABASE=ABL Electronics Corp. + +usb:v0795* + ID_VENDOR_FROM_DATABASE=RealChip, Inc. + +usb:v0796* + ID_VENDOR_FROM_DATABASE=Certicom Corp. + +usb:v0797* + ID_VENDOR_FROM_DATABASE=Grandtech Semiconductor Corp. + +usb:v0797p6801* + ID_MODEL_FROM_DATABASE=Flatbed Scanner + +usb:v0797p6802* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0797p8001* + ID_MODEL_FROM_DATABASE=SmartCam + +usb:v0797p801A* + ID_MODEL_FROM_DATABASE=Typhoon StyloCam + +usb:v0797p801C* + ID_MODEL_FROM_DATABASE=Meade Binoculars/Camera + +usb:v0797p8901* + ID_MODEL_FROM_DATABASE=ScanHex SX-35a + +usb:v0797p8909* + ID_MODEL_FROM_DATABASE=ScanHex SX-35b + +usb:v0797p8911* + ID_MODEL_FROM_DATABASE=ScanHex SX-35c + +usb:v0798* + ID_VENDOR_FROM_DATABASE=Optelec + +usb:v0798p0001* + ID_MODEL_FROM_DATABASE=Braille Voyager + +usb:v0798p0640* + ID_MODEL_FROM_DATABASE=BC640 + +usb:v0798p0680* + ID_MODEL_FROM_DATABASE=BC680 + +usb:v0799* + ID_VENDOR_FROM_DATABASE=Altera + +usb:v0799p7651* + ID_MODEL_FROM_DATABASE=Programming Unit + +usb:v079B* + ID_VENDOR_FROM_DATABASE=Sagem + +usb:v079Bp0024* + ID_MODEL_FROM_DATABASE=MSO300/MSO301 Fingerprint Sensor + +usb:v079Bp0026* + ID_MODEL_FROM_DATABASE=MSO350/MSO351 Fingerprint Sensor & SmartCard Reader + +usb:v079Bp0027* + ID_MODEL_FROM_DATABASE=USB-Serial Controller + +usb:v079Bp002F* + ID_MODEL_FROM_DATABASE=Mobile + +usb:v079Bp0030* + ID_MODEL_FROM_DATABASE=Mobile Communication Device + +usb:v079Bp0042* + ID_MODEL_FROM_DATABASE=Mobile + +usb:v079Bp0047* + ID_MODEL_FROM_DATABASE=CBM/MSO1300 Fingerprint Sensor + +usb:v079Bp004A* + ID_MODEL_FROM_DATABASE=XG-760A 802.11bg + +usb:v079Bp004B* + ID_MODEL_FROM_DATABASE=Wi-Fi 11g adapter + +usb:v079Bp0052* + ID_MODEL_FROM_DATABASE=MSO1350 Fingerprint Sensor & SmartCard Reader + +usb:v079Bp0056* + ID_MODEL_FROM_DATABASE=Agfa AP1100 Photo Printer + +usb:v079Bp005D* + ID_MODEL_FROM_DATABASE=Mobile Mass Storage + +usb:v079Bp0062* + ID_MODEL_FROM_DATABASE=XG-76NA 802.11bg + +usb:v079Bp0078* + ID_MODEL_FROM_DATABASE=Laser Pro Monochrome MFP + +usb:v079D* + ID_VENDOR_FROM_DATABASE=Alfadata Computer Corp. + +usb:v079Dp0201* + ID_MODEL_FROM_DATABASE=GamePort Adapter + +usb:v07A1* + ID_VENDOR_FROM_DATABASE=Digicom S.p.A. + +usb:v07A1pD952* + ID_MODEL_FROM_DATABASE=Palladio USB V.92 Modem + +usb:v07A2* + ID_VENDOR_FROM_DATABASE=National Technical Systems + +usb:v07A3* + ID_VENDOR_FROM_DATABASE=Onnto Corp. + +usb:v07A4* + ID_VENDOR_FROM_DATABASE=Be, Inc. + +usb:v07A6* + ID_VENDOR_FROM_DATABASE=ADMtek, Inc. + +usb:v07A6p07C2* + ID_MODEL_FROM_DATABASE=AN986A Ethernet + +usb:v07A6p0986* + ID_MODEL_FROM_DATABASE=AN986 Pegasus Ethernet + +usb:v07A6p8266* + ID_MODEL_FROM_DATABASE=Infineon WildCard-USB Wireless LAN Adapter + +usb:v07A6p8511* + ID_MODEL_FROM_DATABASE=ADM8511 Pegasus II Ethernet + +usb:v07A6p8513* + ID_MODEL_FROM_DATABASE=AN8513 Ethernet + +usb:v07A6p8515* + ID_MODEL_FROM_DATABASE=AN8515 Ethernet + +usb:v07AA* + ID_VENDOR_FROM_DATABASE=Corega K.K. + +usb:v07AAp0001* + ID_MODEL_FROM_DATABASE=Ether USB-T Ethernet [klsi] + +usb:v07AAp0004* + ID_MODEL_FROM_DATABASE=FEther USB-TX Ethernet [pegasus] + +usb:v07AAp000C* + ID_MODEL_FROM_DATABASE=WirelessLAN USB-11 + +usb:v07AAp000D* + ID_MODEL_FROM_DATABASE=FEther USB-TXS + +usb:v07AAp0011* + ID_MODEL_FROM_DATABASE=Wireless LAN USB-11 mini + +usb:v07AAp0012* + ID_MODEL_FROM_DATABASE=Stick-11 802.11b Adapter + +usb:v07AAp0017* + ID_MODEL_FROM_DATABASE=FEther USB2-TX + +usb:v07AAp0018* + ID_MODEL_FROM_DATABASE=Wireless LAN USB-11 mini 2 + +usb:v07AAp001A* + ID_MODEL_FROM_DATABASE=ULUSB-11 Key + +usb:v07AAp001C* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GT 802.11g Wireless Adapter [Intersil ISL3880] + +usb:v07AAp0020* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GTST 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v07AAp002E* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GPX [Ralink RT2571W] + +usb:v07AAp002F* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GNL + +usb:v07AAp0031* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GS 802.11bg [Atheros AR5523] + +usb:v07AAp003C* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GNL + +usb:v07AAp003F* + ID_MODEL_FROM_DATABASE=CG-WLUSB300AGN + +usb:v07AAp0041* + ID_MODEL_FROM_DATABASE=CG-WLUSB300GNS + +usb:v07AAp0042* + ID_MODEL_FROM_DATABASE=CG-WLUSB300GNM + +usb:v07AAp0043* + ID_MODEL_FROM_DATABASE=CG-WLUSB300N rev A2 [Realtek RTL8192U] + +usb:v07AAp0047* + ID_MODEL_FROM_DATABASE=CG-WLUSBNM + +usb:v07AAp0051* + ID_MODEL_FROM_DATABASE=CG-WLUSB300NM + +usb:v07AAp7613* + ID_MODEL_FROM_DATABASE=Stick-11 V2 802.11b Adapter + +usb:v07AAp9601* + ID_MODEL_FROM_DATABASE=FEther USB-TXC + +usb:v07AB* + ID_VENDOR_FROM_DATABASE=Freecom Technologies + +usb:v07ABpFC01* + ID_MODEL_FROM_DATABASE=IDE bridge + +usb:v07ABpFC02* + ID_MODEL_FROM_DATABASE=Cable II USB-2 + +usb:v07ABpFC03* + ID_MODEL_FROM_DATABASE=USB2-IDE IDE bridge + +usb:v07ABpFCD6* + ID_MODEL_FROM_DATABASE=Freecom HD Classic + +usb:v07ABpFCF6* + ID_MODEL_FROM_DATABASE=DataBar + +usb:v07ABpFCF8* + ID_MODEL_FROM_DATABASE=Freecom Classic SL Network Drive + +usb:v07ABpFCFE* + ID_MODEL_FROM_DATABASE=Hard Drive 80GB + +usb:v07AF* + ID_VENDOR_FROM_DATABASE=Microtech + +usb:v07AFp0004* + ID_MODEL_FROM_DATABASE=SCSI-DB25 SCSI Bridge [shuttle] + +usb:v07AFp0005* + ID_MODEL_FROM_DATABASE=SCSI-HD50 SCSI Bridge [shuttle] + +usb:v07AFp0006* + ID_MODEL_FROM_DATABASE=CameraMate SmartMedia and CompactFlash Card Reader [eusb/shuttle] + +usb:v07AFpFC01* + ID_MODEL_FROM_DATABASE=Freecom USB-IDE + +usb:v07B0* + ID_VENDOR_FROM_DATABASE=Trust Technologies + +usb:v07B0p0001* + ID_MODEL_FROM_DATABASE=ISDN TA + +usb:v07B0p0002* + ID_MODEL_FROM_DATABASE=ISDN TA128 Plus + +usb:v07B0p0003* + ID_MODEL_FROM_DATABASE=ISDN TA128 Deluxe + +usb:v07B0p0005* + ID_MODEL_FROM_DATABASE=ISDN TA128 SE + +usb:v07B0p0006* + ID_MODEL_FROM_DATABASE=ISDN TA 128 [HFC-S] + +usb:v07B0p0007* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v07B0p0008* + ID_MODEL_FROM_DATABASE=ISDN TA + +usb:v07B1* + ID_VENDOR_FROM_DATABASE=IMP, Inc. + +usb:v07B2* + ID_VENDOR_FROM_DATABASE=Motorola BCS, Inc. + +usb:v07B2p0100* + ID_MODEL_FROM_DATABASE=SURFboard Voice over IP Cable Modem + +usb:v07B2p0900* + ID_MODEL_FROM_DATABASE=SURFboard Gateway + +usb:v07B2p0950* + ID_MODEL_FROM_DATABASE=SURFboard SBG950 Gateway + +usb:v07B2p1000* + ID_MODEL_FROM_DATABASE=SURFboard SBG1000 Gateway + +usb:v07B2p4100* + ID_MODEL_FROM_DATABASE=SurfBoard SB4100 Cable Modem + +usb:v07B2p4200* + ID_MODEL_FROM_DATABASE=SurfBoard SB4200 Cable Modem + +usb:v07B2p4210* + ID_MODEL_FROM_DATABASE=SurfBoard 4210 Cable Modem + +usb:v07B2p4220* + ID_MODEL_FROM_DATABASE=SURFboard SB4220 Cable Modem + +usb:v07B2p4500* + ID_MODEL_FROM_DATABASE=CG4500 Communications Gateway + +usb:v07B2p450B* + ID_MODEL_FROM_DATABASE=CG4501 Communications Gateway + +usb:v07B2p450E* + ID_MODEL_FROM_DATABASE=CG4500E Communications Gateway + +usb:v07B2p5100* + ID_MODEL_FROM_DATABASE=SurfBoard SB5100 Cable Modem + +usb:v07B2p5101* + ID_MODEL_FROM_DATABASE=SurfBoard SB5101 Cable Modem + +usb:v07B2p5120* + ID_MODEL_FROM_DATABASE=SurfBoard SB5120 Cable Modem (RNDIS) + +usb:v07B2p5121* + ID_MODEL_FROM_DATABASE=Surfboard 5121 Cable Modem + +usb:v07B2p7030* + ID_MODEL_FROM_DATABASE=WU830G 802.11bg Wireless Adapter [Envara WiND512] + +usb:v07B3* + ID_VENDOR_FROM_DATABASE=Plustek, Inc. + +usb:v07B3p0001* + ID_MODEL_FROM_DATABASE=OpticPro 1212U Scanner + +usb:v07B3p0003* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0010* + ID_MODEL_FROM_DATABASE=OpticPro U12 Scanner + +usb:v07B3p0011* + ID_MODEL_FROM_DATABASE=OpticPro U24 Scanner + +usb:v07B3p0013* + ID_MODEL_FROM_DATABASE=OpticPro UT12 Scanner + +usb:v07B3p0014* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0015* + ID_MODEL_FROM_DATABASE=OpticPro U24 Scanner + +usb:v07B3p0017* + ID_MODEL_FROM_DATABASE=OpticPro UT12/16/24 Scanner + +usb:v07B3p0204* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0400* + ID_MODEL_FROM_DATABASE=OpticPro 1248U Scanner + +usb:v07B3p0401* + ID_MODEL_FROM_DATABASE=OpticPro 1248U Scanner #2 + +usb:v07B3p0403* + ID_MODEL_FROM_DATABASE=OpticPro U16B Scanner + +usb:v07B3p0404* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0405* + ID_MODEL_FROM_DATABASE=A8 Namecard-s Controller + +usb:v07B3p0406* + ID_MODEL_FROM_DATABASE=A8 Namecard-D Controller + +usb:v07B3p0410* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0412* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v07B3p0413* + ID_MODEL_FROM_DATABASE=OpticSlim 1200 Scanner + +usb:v07B3p0601* + ID_MODEL_FROM_DATABASE=OpticPro ST24 Scanner + +usb:v07B3p0800* + ID_MODEL_FROM_DATABASE=OpticPro ST48 Scanner + +usb:v07B3p0900* + ID_MODEL_FROM_DATABASE=OpticBook 3600 Scanner + +usb:v07B3p090C* + ID_MODEL_FROM_DATABASE=OpticBook 3600 Plus Scanner + +usb:v07B3p0A06* + ID_MODEL_FROM_DATABASE=TVcam VD100 + +usb:v07B3p0B00* + ID_MODEL_FROM_DATABASE=SmartPhoto F50 + +usb:v07B3p0C00* + ID_MODEL_FROM_DATABASE=OpticPro ST64 Scanner + +usb:v07B3p0C03* + ID_MODEL_FROM_DATABASE=OpticPro ST64+ Scanner + +usb:v07B3p0C04* + ID_MODEL_FROM_DATABASE=Optic Film 7200i scanner + +usb:v07B3p0C0C* + ID_MODEL_FROM_DATABASE=PL806 Scanner + +usb:v07B3p0C26* + ID_MODEL_FROM_DATABASE=OpticBook 4600 Scanner + +usb:v07B3p0C2B* + ID_MODEL_FROM_DATABASE=Mobile Office D428 Scanner + +usb:v07B3p0E08* + ID_MODEL_FROM_DATABASE=OpticBook A300 Scanner + +usb:v07B3p1300* + ID_MODEL_FROM_DATABASE=OpticBook 3800 Scanner + +usb:v07B3p1301* + ID_MODEL_FROM_DATABASE=OpticBook 4800 Scanner + +usb:v07B4* + ID_VENDOR_FROM_DATABASE=Olympus Optical Co., Ltd + +usb:v07B4p0100* + ID_MODEL_FROM_DATABASE=Camedia C-2100/C-3000 Ultra Zoom Camera + +usb:v07B4p0102* + ID_MODEL_FROM_DATABASE=Camedia E-10/C-220/C-50 Camera + +usb:v07B4p0105* + ID_MODEL_FROM_DATABASE=Camedia C-310Z/C-700/C-750UZ/C-755/C-765UZ/C-3040/C-4000/C-5050Z/D-560/C-3020Z Zoom Camera + +usb:v07B4p0109* + ID_MODEL_FROM_DATABASE=C-370Z/C-500Z/D-535Z/X-450 + +usb:v07B4p010A* + ID_MODEL_FROM_DATABASE=MAUSB-10 xD and SmartMedia Card Reader + +usb:v07B4p0112* + ID_MODEL_FROM_DATABASE=MAUSB-100 xD Card Reader + +usb:v07B4p0113* + ID_MODEL_FROM_DATABASE=Mju 500 / Stylus Digital Camera (PTP) + +usb:v07B4p0114* + ID_MODEL_FROM_DATABASE=C-350Z Camera + +usb:v07B4p0118* + ID_MODEL_FROM_DATABASE=Mju Mini Digital/Mju Digital 500 Camera / Stylus 850 SW + +usb:v07B4p0125* + ID_MODEL_FROM_DATABASE=Tough TG-1 Camera + +usb:v07B4p0184* + ID_MODEL_FROM_DATABASE=P-S100 port + +usb:v07B4p0202* + ID_MODEL_FROM_DATABASE=Foot Switch RS-26 + +usb:v07B4p0203* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DW-90 + +usb:v07B4p0206* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DS-330 + +usb:v07B4p0207* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder & Camera W-10 + +usb:v07B4p0209* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DM-20 + +usb:v07B4p020B* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DS-4000 + +usb:v07B4p020D* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder VN-240PC + +usb:v07B4p0211* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DS-2300 + +usb:v07B4p0218* + ID_MODEL_FROM_DATABASE=Foot Switch RS-28 + +usb:v07B4p0244* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder VN-8500PC + +usb:v07B4p024F* + ID_MODEL_FROM_DATABASE=Digital Voice Recorder DS-7000 + +usb:v07B4p0280* + ID_MODEL_FROM_DATABASE=m:robe 100 + +usb:v07B5* + ID_VENDOR_FROM_DATABASE=Mega World International, Ltd + +usb:v07B5p0017* + ID_MODEL_FROM_DATABASE=Joystick + +usb:v07B5p0213* + ID_MODEL_FROM_DATABASE=Thrustmaster Firestorm Digital 3 Gamepad + +usb:v07B5p0312* + ID_MODEL_FROM_DATABASE=Gamepad + +usb:v07B5p9902* + ID_MODEL_FROM_DATABASE=GamePad + +usb:v07B6* + ID_VENDOR_FROM_DATABASE=Marubun Corp. + +usb:v07B7* + ID_VENDOR_FROM_DATABASE=TIME Interconnect, Ltd + +usb:v07B8* + ID_VENDOR_FROM_DATABASE=AboCom Systems Inc + +usb:v07B8p110C* + ID_MODEL_FROM_DATABASE=XX1 + +usb:v07B8p1201* + ID_MODEL_FROM_DATABASE=IEEE 802.11b Adapter + +usb:v07B8p200C* + ID_MODEL_FROM_DATABASE=XX2 + +usb:v07B8p2573* + ID_MODEL_FROM_DATABASE=Wireless LAN Card + +usb:v07B8p2770* + ID_MODEL_FROM_DATABASE=802.11n/b/g Mini Wireless LAN USB2.0 Adapter + +usb:v07B8p2870* + ID_MODEL_FROM_DATABASE=802.11n/b/g Wireless LAN USB2.0 Adapter + +usb:v07B8p3070* + ID_MODEL_FROM_DATABASE=802.11n/b/g Mini Wireless LAN USB2.0 Adapter + +usb:v07B8p3071* + ID_MODEL_FROM_DATABASE=802.11n/b/g Mini Wireless LAN USB2.0 Adapter + +usb:v07B8p3072* + ID_MODEL_FROM_DATABASE=802.11n/b/g Mini Wireless LAN USB2.0 Adapter + +usb:v07B8p4000* + ID_MODEL_FROM_DATABASE=DU-E10 Ethernet [klsi] + +usb:v07B8p4002* + ID_MODEL_FROM_DATABASE=DU-E100 Ethernet [pegasus] + +usb:v07B8p4003* + ID_MODEL_FROM_DATABASE=1/10/100 Ethernet Adapter + +usb:v07B8p4004* + ID_MODEL_FROM_DATABASE=XX4 + +usb:v07B8p4007* + ID_MODEL_FROM_DATABASE=XX5 + +usb:v07B8p400B* + ID_MODEL_FROM_DATABASE=XX6 + +usb:v07B8p400C* + ID_MODEL_FROM_DATABASE=XX7 + +usb:v07B8p401A* + ID_MODEL_FROM_DATABASE=RTL8151 + +usb:v07B8p4102* + ID_MODEL_FROM_DATABASE=USB 1.1 10/100M Fast Ethernet Adapter + +usb:v07B8p4104* + ID_MODEL_FROM_DATABASE=XX9 + +usb:v07B8p420A* + ID_MODEL_FROM_DATABASE=UF200 Ethernet + +usb:v07B8p5301* + ID_MODEL_FROM_DATABASE=GW-US54ZGL 802.11bg + +usb:v07B8p6001* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v07B8p8188* + ID_MODEL_FROM_DATABASE=AboCom Systems Inc [WN2001 Prolink Wireless-N Nano Adapter] + +usb:v07B8pA001* + ID_MODEL_FROM_DATABASE=WUG2200 802.11g Wireless Adapter [Envara WiND512] + +usb:v07B8pABC1* + ID_MODEL_FROM_DATABASE=DU-E10 Ethernet [pegasus] + +usb:v07B8pB000* + ID_MODEL_FROM_DATABASE=BWU613 + +usb:v07B8pB02A* + ID_MODEL_FROM_DATABASE=AboCom Bluetooth Device + +usb:v07B8pB02B* + ID_MODEL_FROM_DATABASE=Bluetooth dongle + +usb:v07B8pB02C* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB02D* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB02E* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB030* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB031* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB032* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB033* + ID_MODEL_FROM_DATABASE=BCM92045DG-Flash with trace filter + +usb:v07B8pB21A* + ID_MODEL_FROM_DATABASE=WUG2400 802.11g Wireless Adapter [Texas Instruments TNETW1450] + +usb:v07B8pB21B* + ID_MODEL_FROM_DATABASE=HWU54DM + +usb:v07B8pB21C* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v07B8pB21D* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v07B8pB21E* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v07B8pB21F* + ID_MODEL_FROM_DATABASE=WUG2700 + +usb:v07B8pD011* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v07B8pE001* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE002* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE003* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE004* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE005* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE006* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE007* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE008* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE009* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE00A* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07B8pE4F0* + ID_MODEL_FROM_DATABASE=Card Reader Driver + +usb:v07B8pF101* + ID_MODEL_FROM_DATABASE=DSB-560 Modem [atlas] + +usb:v07BC* + ID_VENDOR_FROM_DATABASE=Canon Computer Systems, Inc. + +usb:v07BD* + ID_VENDOR_FROM_DATABASE=Webgear, Inc. + +usb:v07BE* + ID_VENDOR_FROM_DATABASE=Veridicom + +usb:v07C0* + ID_VENDOR_FROM_DATABASE=Code Mercenaries Hard- und Software GmbH + +usb:v07C0p1113* + ID_MODEL_FROM_DATABASE=JoyWarrior24F8 + +usb:v07C0p1116* + ID_MODEL_FROM_DATABASE=JoyWarrior24F14 + +usb:v07C0p1121* + ID_MODEL_FROM_DATABASE=The Claw + +usb:v07C0p1500* + ID_MODEL_FROM_DATABASE=IO-Warrior 40 + +usb:v07C0p1501* + ID_MODEL_FROM_DATABASE=IO-Warrior 24 + +usb:v07C0p1502* + ID_MODEL_FROM_DATABASE=IO-Warrior 48 + +usb:v07C0p1503* + ID_MODEL_FROM_DATABASE=IO-Warrior 28 + +usb:v07C0p1511* + ID_MODEL_FROM_DATABASE=IO-Warrior 24 Power Vampire + +usb:v07C0p1512* + ID_MODEL_FROM_DATABASE=IO-Warrior 24 Power Vampire + +usb:v07C1* + ID_VENDOR_FROM_DATABASE=Keisokugiken + +usb:v07C1p0068* + ID_MODEL_FROM_DATABASE=HKS-0200 USBDAQ + +usb:v07C4* + ID_VENDOR_FROM_DATABASE=Datafab Systems, Inc. + +usb:v07C4p0102* + ID_MODEL_FROM_DATABASE=USB to LS120 + +usb:v07C4p0103* + ID_MODEL_FROM_DATABASE=USB to IDE + +usb:v07C4p1234* + ID_MODEL_FROM_DATABASE=USB to ATAPI + +usb:v07C4pA000* + ID_MODEL_FROM_DATABASE=CompactFlash Card Reader + +usb:v07C4pA001* + ID_MODEL_FROM_DATABASE=CompactFlash & SmartMedia Card Reader [eusb] + +usb:v07C4pA002* + ID_MODEL_FROM_DATABASE=Disk Drive + +usb:v07C4pA003* + ID_MODEL_FROM_DATABASE=Datafab-based Reader + +usb:v07C4pA004* + ID_MODEL_FROM_DATABASE=USB to MMC Class Drive + +usb:v07C4pA005* + ID_MODEL_FROM_DATABASE=CompactFlash & SmartMedia Card Reader + +usb:v07C4pA006* + ID_MODEL_FROM_DATABASE=SmartMedia Card Reader + +usb:v07C4pA007* + ID_MODEL_FROM_DATABASE=Memory Stick Class Drive + +usb:v07C4pA103* + ID_MODEL_FROM_DATABASE=MDSM-B reader + +usb:v07C4pA107* + ID_MODEL_FROM_DATABASE=USB to Memory Stick (LC1) Drive + +usb:v07C4pA109* + ID_MODEL_FROM_DATABASE=LC1 CompactFlash & SmartMedia Card Reader + +usb:v07C4pA10B* + ID_MODEL_FROM_DATABASE=USB to CF+MS(LC1) + +usb:v07C4pA200* + ID_MODEL_FROM_DATABASE=DF-UT-06 Hama MMC/SD Reader + +usb:v07C4pA400* + ID_MODEL_FROM_DATABASE=CompactFlash & Microdrive Reader + +usb:v07C4pA600* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v07C4pA604* + ID_MODEL_FROM_DATABASE=12-in-1 Card Reader + +usb:v07C4pAD01* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07C4pAE01* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07C4pAF01* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v07C4pB000* + ID_MODEL_FROM_DATABASE=USB to CF(LC1) + +usb:v07C4pB001* + ID_MODEL_FROM_DATABASE=USB to CF+PCMCIA + +usb:v07C4pB004* + ID_MODEL_FROM_DATABASE=MMC/SD Reader + +usb:v07C4pB006* + ID_MODEL_FROM_DATABASE=USB to PCMCIA + +usb:v07C4pB00A* + ID_MODEL_FROM_DATABASE=USB to CF+SD Drive(LC1) + +usb:v07C4pB00B* + ID_MODEL_FROM_DATABASE=USB to Memory Stick(LC1) + +usb:v07C4pC010* + ID_MODEL_FROM_DATABASE=Kingston FCR-HS2/ATA Card Reader + +usb:v07C5* + ID_VENDOR_FROM_DATABASE=APG Cash Drawer + +usb:v07C5p0500* + ID_MODEL_FROM_DATABASE=Cash Drawer + +usb:v07C6* + ID_VENDOR_FROM_DATABASE=ShareWave, Inc. + +usb:v07C6p0002* + ID_MODEL_FROM_DATABASE=Bodega Wireless Access Point + +usb:v07C6p0003* + ID_MODEL_FROM_DATABASE=Bodega Wireless Network Adapter + +usb:v07C7* + ID_VENDOR_FROM_DATABASE=Powertech Industrial Co., Ltd + +usb:v07C8* + ID_VENDOR_FROM_DATABASE=B.U.G., Inc. + +usb:v07C8p0202* + ID_MODEL_FROM_DATABASE=MN128-SOHO PAL + +usb:v07C9* + ID_VENDOR_FROM_DATABASE=Allied Telesyn International + +usb:v07C9pB100* + ID_MODEL_FROM_DATABASE=AT-USB100 + +usb:v07CA* + ID_VENDOR_FROM_DATABASE=AVerMedia Technologies, Inc. + +usb:v07CAp0002* + ID_MODEL_FROM_DATABASE=AVerTV PVR USB/EZMaker Pro Device + +usb:v07CAp0026* + ID_MODEL_FROM_DATABASE=AVerTV + +usb:v07CAp0337* + ID_MODEL_FROM_DATABASE=A867 DVB-T dongle + +usb:v07CAp0837* + ID_MODEL_FROM_DATABASE=H837 Hybrid ATSC/QAM + +usb:v07CAp1228* + ID_MODEL_FROM_DATABASE=MPEG-2 Capture Device (M038) + +usb:v07CAp1830* + ID_MODEL_FROM_DATABASE=AVerTV Volar Video Capture (H830) + +usb:v07CAp3835* + ID_MODEL_FROM_DATABASE=AVerTV Volar Green HD (A835B) + +usb:v07CAp850A* + ID_MODEL_FROM_DATABASE=AverTV Volar Black HD (A850) + +usb:v07CAp850B* + ID_MODEL_FROM_DATABASE=AverTV Red HD+ (A850T) + +usb:v07CApA309* + ID_MODEL_FROM_DATABASE=AVerTV DVB-T (A309) + +usb:v07CApA801* + ID_MODEL_FROM_DATABASE=AVerTV DVB-T (A800) + +usb:v07CApA815* + ID_MODEL_FROM_DATABASE=AVerTV DVB-T Volar X (A815) + +usb:v07CApA827* + ID_MODEL_FROM_DATABASE=AVerTV Hybrid Volar HX (A827) + +usb:v07CApA867* + ID_MODEL_FROM_DATABASE=AVerTV DVB-T (A867) + +usb:v07CApB300* + ID_MODEL_FROM_DATABASE=A300 DVB-T TV receiver + +usb:v07CApB800* + ID_MODEL_FROM_DATABASE=MR800 FM Radio + +usb:v07CApE880* + ID_MODEL_FROM_DATABASE=MPEG-2 Capture Device (E880) + +usb:v07CApE882* + ID_MODEL_FROM_DATABASE=MPEG-2 Capture Device (E882) + +usb:v07CB* + ID_VENDOR_FROM_DATABASE=Kingmax Technology, Inc. + +usb:v07CC* + ID_VENDOR_FROM_DATABASE=Carry Computer Eng., Co., Ltd + +usb:v07CCp0000* + ID_MODEL_FROM_DATABASE=CF Card Reader + +usb:v07CCp0001* + ID_MODEL_FROM_DATABASE=Reader (UICSE) + +usb:v07CCp0002* + ID_MODEL_FROM_DATABASE=Reader (UIS) + +usb:v07CCp0003* + ID_MODEL_FROM_DATABASE=SM Card Reader + +usb:v07CCp0004* + ID_MODEL_FROM_DATABASE=SM/CF/PCMCIA Card Reader + +usb:v07CCp0005* + ID_MODEL_FROM_DATABASE=Reader (UISA2SE) + +usb:v07CCp0006* + ID_MODEL_FROM_DATABASE=SM/CF/PCMCIA Card Reader + +usb:v07CCp0007* + ID_MODEL_FROM_DATABASE=Reader (UISA6SE) + +usb:v07CCp000C* + ID_MODEL_FROM_DATABASE=SM/CF Card Reader + +usb:v07CCp000D* + ID_MODEL_FROM_DATABASE=SM/CF Card Reader + +usb:v07CCp000E* + ID_MODEL_FROM_DATABASE=Reader (UISDA) + +usb:v07CCp000F* + ID_MODEL_FROM_DATABASE=Reader (UICLIK) + +usb:v07CCp0010* + ID_MODEL_FROM_DATABASE=Reader (UISMA) + +usb:v07CCp0012* + ID_MODEL_FROM_DATABASE=Reader (UISC6SE-FLASH) + +usb:v07CCp0014* + ID_MODEL_FROM_DATABASE=Litronic Fortezza Reader + +usb:v07CCp0030* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC12S) + +usb:v07CCp0040* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC13S) + +usb:v07CCp0100* + ID_MODEL_FROM_DATABASE=Reader (UID) + +usb:v07CCp0101* + ID_MODEL_FROM_DATABASE=Reader (UIM) + +usb:v07CCp0102* + ID_MODEL_FROM_DATABASE=Reader (UISDMA) + +usb:v07CCp0103* + ID_MODEL_FROM_DATABASE=Reader (UISDMC) + +usb:v07CCp0104* + ID_MODEL_FROM_DATABASE=Reader (UISDM) + +usb:v07CCp0200* + ID_MODEL_FROM_DATABASE=6-in-1 Card Reader + +usb:v07CCp0201* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC1S & UISDMC3S) + +usb:v07CCp0202* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC5S) + +usb:v07CCp0203* + ID_MODEL_FROM_DATABASE=Mass Storage (UISMC5S) + +usb:v07CCp0204* + ID_MODEL_FROM_DATABASE=Mass Storage (UIM4/5S & UIM7S) + +usb:v07CCp0205* + ID_MODEL_FROM_DATABASE=Mass Storage (UIS4/5S & UIS7S) + +usb:v07CCp0206* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC10S & UISDMC11S) + +usb:v07CCp0207* + ID_MODEL_FROM_DATABASE=Mass Storage (UPIDMA) + +usb:v07CCp0208* + ID_MODEL_FROM_DATABASE=Mass Storage (UCFC II) + +usb:v07CCp0210* + ID_MODEL_FROM_DATABASE=Mass Storage (UPIXXA) + +usb:v07CCp0213* + ID_MODEL_FROM_DATABASE=Mass Storage (UPIDA) + +usb:v07CCp0214* + ID_MODEL_FROM_DATABASE=Mass Storage (UPIMA) + +usb:v07CCp0215* + ID_MODEL_FROM_DATABASE=Mass Storage (UPISA) + +usb:v07CCp0217* + ID_MODEL_FROM_DATABASE=Mass Storage (UPISDMA) + +usb:v07CCp0223* + ID_MODEL_FROM_DATABASE=Mass Storage (UCIDA) + +usb:v07CCp0224* + ID_MODEL_FROM_DATABASE=Mass Storage (UCIMA) + +usb:v07CCp0225* + ID_MODEL_FROM_DATABASE=Mass Storage (UIS7S) + +usb:v07CCp0227* + ID_MODEL_FROM_DATABASE=Mass Storage (UCIDMA) + +usb:v07CCp0234* + ID_MODEL_FROM_DATABASE=Mass Storage (UIM7S) + +usb:v07CCp0235* + ID_MODEL_FROM_DATABASE=Mass Storage (UIS4S-S) + +usb:v07CCp0237* + ID_MODEL_FROM_DATABASE=Velper (UISDMC4S) + +usb:v07CCp0300* + ID_MODEL_FROM_DATABASE=6-in-1 Card Reader + +usb:v07CCp0301* + ID_MODEL_FROM_DATABASE=6-in-1 Card Reader + +usb:v07CCp0303* + ID_MODEL_FROM_DATABASE=Mass Storage (UID10W) + +usb:v07CCp0304* + ID_MODEL_FROM_DATABASE=Mass Storage (UIM10W) + +usb:v07CCp0305* + ID_MODEL_FROM_DATABASE=Mass Storage (UIS10W) + +usb:v07CCp0308* + ID_MODEL_FROM_DATABASE=Mass Storage (UIC10W) + +usb:v07CCp0309* + ID_MODEL_FROM_DATABASE=Mass Storage (UISC3W) + +usb:v07CCp0310* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMA2W) + +usb:v07CCp0311* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC14W) + +usb:v07CCp0320* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC4W) + +usb:v07CCp0321* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC37W) + +usb:v07CCp0330* + ID_MODEL_FROM_DATABASE=WINTERREADER Reader + +usb:v07CCp0350* + ID_MODEL_FROM_DATABASE=9-in-1 Card Reader + +usb:v07CCp0500* + ID_MODEL_FROM_DATABASE=Mass Storage + +usb:v07CCp0501* + ID_MODEL_FROM_DATABASE=Mass Storage + +usb:v07CD* + ID_VENDOR_FROM_DATABASE=Elektor + +usb:v07CDp0001* + ID_MODEL_FROM_DATABASE=USBuart Serial Port + +usb:v07CF* + ID_VENDOR_FROM_DATABASE=Casio Computer Co., Ltd + +usb:v07CFp1001* + ID_MODEL_FROM_DATABASE=QV-8000SX/5700/3000EX Digicam; Exilim EX-M20 + +usb:v07CFp1003* + ID_MODEL_FROM_DATABASE=Exilim EX-S500 + +usb:v07CFp1004* + ID_MODEL_FROM_DATABASE=Exilim EX-Z120 + +usb:v07CFp1011* + ID_MODEL_FROM_DATABASE=USB-CASIO PC CAMERA + +usb:v07CFp1116* + ID_MODEL_FROM_DATABASE=EXILIM EX-Z19 + +usb:v07CFp1125* + ID_MODEL_FROM_DATABASE=Exilim EX-H10 Digital Camera (mass storage mode) + +usb:v07CFp1133* + ID_MODEL_FROM_DATABASE=Exilim EX-Z350 Digital Camera (mass storage mode) + +usb:v07CFp1225* + ID_MODEL_FROM_DATABASE=Exilim EX-H10 Digital Camera (PictBridge mode) + +usb:v07CFp1233* + ID_MODEL_FROM_DATABASE=Exilim EX-Z350 Digital Camera (PictBridge mode) + +usb:v07CFp2002* + ID_MODEL_FROM_DATABASE=E-125 Cassiopeia Pocket PC + +usb:v07CFp3801* + ID_MODEL_FROM_DATABASE=WMP-1 MP3-Watch + +usb:v07CFp4001* + ID_MODEL_FROM_DATABASE=Label Printer KL-P1000 + +usb:v07CFp4007* + ID_MODEL_FROM_DATABASE=CW50 Device + +usb:v07CFp4104* + ID_MODEL_FROM_DATABASE=Cw75 Device + +usb:v07CFp4107* + ID_MODEL_FROM_DATABASE=CW-L300 Device + +usb:v07CFp4500* + ID_MODEL_FROM_DATABASE=LV-20 Digital Camera + +usb:v07CFp6101* + ID_MODEL_FROM_DATABASE=fx-9750gII + +usb:v07CFp6102* + ID_MODEL_FROM_DATABASE=fx-CP400 + +usb:v07CFp6801* + ID_MODEL_FROM_DATABASE=PL-40R + +usb:v07CFp6802* + ID_MODEL_FROM_DATABASE=MIDI Keyboard + +usb:v07D0* + ID_VENDOR_FROM_DATABASE=Dazzle + +usb:v07D0p0001* + ID_MODEL_FROM_DATABASE=Digital Video Creator I + +usb:v07D0p0002* + ID_MODEL_FROM_DATABASE=Global Village VideoFX Grabber + +usb:v07D0p0003* + ID_MODEL_FROM_DATABASE=Fusion Model DVC-50 Rev 1 (NTSC) + +usb:v07D0p0004* + ID_MODEL_FROM_DATABASE=DVC-800 (PAL) Grabber + +usb:v07D0p0005* + ID_MODEL_FROM_DATABASE=Fusion Video and Audio Ports + +usb:v07D0p0006* + ID_MODEL_FROM_DATABASE=DVC 150 Loader Device + +usb:v07D0p0007* + ID_MODEL_FROM_DATABASE=DVC 150 + +usb:v07D0p0327* + ID_MODEL_FROM_DATABASE=Fusion Digital Media Reader + +usb:v07D0p1001* + ID_MODEL_FROM_DATABASE=DM-FLEX DFU Adapter + +usb:v07D0p1002* + ID_MODEL_FROM_DATABASE=DMHS2 DFU Adapter + +usb:v07D0p1102* + ID_MODEL_FROM_DATABASE=CF Reader/Writer + +usb:v07D0p1103* + ID_MODEL_FROM_DATABASE=SD Reader/Writer + +usb:v07D0p1104* + ID_MODEL_FROM_DATABASE=SM Reader/Writer + +usb:v07D0p1105* + ID_MODEL_FROM_DATABASE=MS Reader/Writer + +usb:v07D0p1106* + ID_MODEL_FROM_DATABASE=xD/SM Reader/Writer + +usb:v07D0p1202* + ID_MODEL_FROM_DATABASE=MultiSlot Reader/Writer + +usb:v07D0p2000* + ID_MODEL_FROM_DATABASE=FX2 DFU Adapter + +usb:v07D0p2001* + ID_MODEL_FROM_DATABASE=eUSB CompactFlash Reader + +usb:v07D0p4100* + ID_MODEL_FROM_DATABASE=Kingsun SF-620 Infrared Adapter + +usb:v07D0p4101* + ID_MODEL_FROM_DATABASE=Connectivity Cable (CA-42 clone) + +usb:v07D0p4959* + ID_MODEL_FROM_DATABASE=Kingsun KS-959 Infrared Adapter + +usb:v07D1* + ID_VENDOR_FROM_DATABASE=D-Link System + +usb:v07D1p13EC* + ID_MODEL_FROM_DATABASE=VvBus for Helium 2xx + +usb:v07D1p13ED* + ID_MODEL_FROM_DATABASE=VvBus for Helium 2xx + +usb:v07D1p13F1* + ID_MODEL_FROM_DATABASE=DSL-302G Modem + +usb:v07D1p13F2* + ID_MODEL_FROM_DATABASE=DSL-502G Router + +usb:v07D1p3300* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.E) [Realtek RTL8191SU] + +usb:v07D1p3302* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.C2) [Realtek RTL8191SU] + +usb:v07D1p3303* + ID_MODEL_FROM_DATABASE=DWA-131 802.11n Wireless N Nano Adapter(rev.A1) [Realtek RTL8192SU] + +usb:v07D1p3304* + ID_MODEL_FROM_DATABASE=FR-300USB 802.11bgn Wireless Adapter + +usb:v07D1p3A07* + ID_MODEL_FROM_DATABASE=WUA-2340 RangeBooster G Adapter(rev.A) [Atheros AR5523] + +usb:v07D1p3A08* + ID_MODEL_FROM_DATABASE=WUA-2340 RangeBooster G Adapter(rev.A) (no firmware) [Atheros AR5523] + +usb:v07D1p3A09* + ID_MODEL_FROM_DATABASE=DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A2) [Atheros AR9170+AR9104] + +usb:v07D1p3A0D* + ID_MODEL_FROM_DATABASE=DWA-120 802.11g Wireless 108G Adapter [Atheros AR5523] + +usb:v07D1p3A0F* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.D) [Atheros AR9170+AR9102] + +usb:v07D1p3A10* + ID_MODEL_FROM_DATABASE=DWA-126 802.11n Wireless Adapter [Atheros AR9271] + +usb:v07D1p3B01* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.D) [Marvell 88W8338+88W8010] + +usb:v07D1p3B10* + ID_MODEL_FROM_DATABASE=DWA-142 RangeBooster N Adapter [Marvell 88W8362+88W8060] + +usb:v07D1p3B11* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.A1) [Marvell 88W8362+88W8060] + +usb:v07D1p3C03* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.C1) [Ralink RT2571W] + +usb:v07D1p3C04* + ID_MODEL_FROM_DATABASE=WUA-1340 + +usb:v07D1p3C05* + ID_MODEL_FROM_DATABASE=EH103 Wireless G Adapter + +usb:v07D1p3C06* + ID_MODEL_FROM_DATABASE=DWA-111 802.11bg Wireless Adapter [Ralink RT2571W] + +usb:v07D1p3C07* + ID_MODEL_FROM_DATABASE=DWA-110 Wireless G Adapter(rev.A1) [Ralink RT2571W] + +usb:v07D1p3C09* + ID_MODEL_FROM_DATABASE=DWA-140 RangeBooster N Adapter(rev.B1) [Ralink RT2870] + +usb:v07D1p3C0A* + ID_MODEL_FROM_DATABASE=DWA-140 RangeBooster N Adapter(rev.B2) [Ralink RT3072] + +usb:v07D1p3C0B* + ID_MODEL_FROM_DATABASE=DWA-110 Wireless G Adapter(rev.B) [Ralink RT2870] + +usb:v07D1p3C0D* + ID_MODEL_FROM_DATABASE=DWA-125 Wireless N 150 Adapter(rev.A1) [Ralink RT3070] + +usb:v07D1p3C0E* + ID_MODEL_FROM_DATABASE=WUA-2340 RangeBooster G Adapter(rev.B) [Ralink RT2070] + +usb:v07D1p3C0F* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.E1) [Ralink RT2070] + +usb:v07D1p3C10* + ID_MODEL_FROM_DATABASE=DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.A1) [Atheros AR9170+AR9104] + +usb:v07D1p3C11* + ID_MODEL_FROM_DATABASE=DWA-160 Xtreme N Dual Band USB Adapter(rev.B) [Ralink RT2870] + +usb:v07D1p3C13* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.B) [Ralink RT2870] + +usb:v07D1p3C15* + ID_MODEL_FROM_DATABASE=DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT2870] + +usb:v07D1p3C16* + ID_MODEL_FROM_DATABASE=DWA-125 Wireless N 150 Adapter(rev.A2) [Ralink RT3070] + +usb:v07D1p3E02* + ID_MODEL_FROM_DATABASE=DWM-156 3.75G HSUPA Adapter + +usb:v07D1p5100* + ID_MODEL_FROM_DATABASE=Remote NDIS Device + +usb:v07D1pA800* + ID_MODEL_FROM_DATABASE=DWM-152 3.75G HSUPA Adapter + +usb:v07D1pF101* + ID_MODEL_FROM_DATABASE=DBT-122 Bluetooth + +usb:v07D1pFC01* + ID_MODEL_FROM_DATABASE=DBT-120 Bluetooth Adapter + +usb:v07D2* + ID_VENDOR_FROM_DATABASE=Aptio Products, Inc. + +usb:v07D3* + ID_VENDOR_FROM_DATABASE=Cyberdata Corp. + +usb:v07D5* + ID_VENDOR_FROM_DATABASE=Radiant Systems + +usb:v07D7* + ID_VENDOR_FROM_DATABASE=GCC Technologies, Inc. + +usb:v07DA* + ID_VENDOR_FROM_DATABASE=Arasan Chip Systems + +usb:v07DE* + ID_VENDOR_FROM_DATABASE=Diamond Multimedia + +usb:v07DEp2820* + ID_MODEL_FROM_DATABASE=VC500 Video Capture Dongle + +usb:v07DF* + ID_VENDOR_FROM_DATABASE=David Electronics Co., Ltd + +usb:v07E0* + ID_VENDOR_FROM_DATABASE=NCP engineering GmbH + +usb:v07E0p4742* + ID_MODEL_FROM_DATABASE=VPN GovNet Box + +usb:v07E1* + ID_VENDOR_FROM_DATABASE=Ambient Technologies, Inc. + +usb:v07E1p5201* + ID_MODEL_FROM_DATABASE=V.90 Modem + +usb:v07E2* + ID_VENDOR_FROM_DATABASE=Elmeg GmbH & Co., Ltd + +usb:v07E3* + ID_VENDOR_FROM_DATABASE=Planex Communications, Inc. + +usb:v07E4* + ID_VENDOR_FROM_DATABASE=Movado Enterprise Co., Ltd + +usb:v07E4p0967* + ID_MODEL_FROM_DATABASE=SCard R/W CSR-145 + +usb:v07E4p0968* + ID_MODEL_FROM_DATABASE=SCard R/W CSR-145 + +usb:v07E5* + ID_VENDOR_FROM_DATABASE=QPS, Inc. + +usb:v07E5p05C2* + ID_MODEL_FROM_DATABASE=IDE-to-USB2.0 PCA + +usb:v07E5p5C01* + ID_MODEL_FROM_DATABASE=Que! CDRW + +usb:v07E6* + ID_VENDOR_FROM_DATABASE=Allied Cable Corp. + +usb:v07E7* + ID_VENDOR_FROM_DATABASE=Mirvo Toys, Inc. + +usb:v07E8* + ID_VENDOR_FROM_DATABASE=Labsystems + +usb:v07EA* + ID_VENDOR_FROM_DATABASE=Iwatsu Electric Co., Ltd + +usb:v07EB* + ID_VENDOR_FROM_DATABASE=Double-H Technology Co., Ltd + +usb:v07EC* + ID_VENDOR_FROM_DATABASE=Taiyo Electric Wire & Cable Co., Ltd + +usb:v07EE* + ID_VENDOR_FROM_DATABASE=Torex Retail (formerly Logware) + +usb:v07EEp0002* + ID_MODEL_FROM_DATABASE=Cash Drawer I/F + +usb:v07EF* + ID_VENDOR_FROM_DATABASE=STSN + +usb:v07EFp0001* + ID_MODEL_FROM_DATABASE=Internet Access Device + +usb:v07F2* + ID_VENDOR_FROM_DATABASE=Microcomputer Applications, Inc. + +usb:v07F2p0001* + ID_MODEL_FROM_DATABASE=KEYLOK II + +usb:v07F6* + ID_VENDOR_FROM_DATABASE=Circuit Assembly Corp. + +usb:v07F7* + ID_VENDOR_FROM_DATABASE=Century Corp. + +usb:v07F7p0005* + ID_MODEL_FROM_DATABASE=ScanLogic/Century Corporation uATA + +usb:v07F7p011E* + ID_MODEL_FROM_DATABASE=Century USB Disk Enclosure + +usb:v07F9* + ID_VENDOR_FROM_DATABASE=Dotop Technology, Inc. + +usb:v07FA* + ID_VENDOR_FROM_DATABASE=DrayTek Corp. + +usb:v07FAp0778* + ID_MODEL_FROM_DATABASE=miniVigor 128 ISDN TA + +usb:v07FAp0846* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v07FAp0847* + ID_MODEL_FROM_DATABASE=ISDN TA [HFC-S] + +usb:v07FAp1012* + ID_MODEL_FROM_DATABASE=BeWAN ADSL USB ST (grey) + +usb:v07FAp1196* + ID_MODEL_FROM_DATABASE=BWIFI-USB54AR 802.11bg + +usb:v07FApA904* + ID_MODEL_FROM_DATABASE=BeWAN ADSL + +usb:v07FApA905* + ID_MODEL_FROM_DATABASE=BeWAN ADSL ST + +usb:v07FC* + ID_VENDOR_FROM_DATABASE=Thomann + +usb:v07FCp1113* + ID_MODEL_FROM_DATABASE=SWISSONIC EasyKeys61 Midikeyboard + +usb:v07FD* + ID_VENDOR_FROM_DATABASE=Mark of the Unicorn + +usb:v07FDp0000* + ID_MODEL_FROM_DATABASE=FastLane MIDI Interface + +usb:v07FDp0001* + ID_MODEL_FROM_DATABASE=MIDI Interface + +usb:v07FDp0002* + ID_MODEL_FROM_DATABASE=MOTU Audio for 64 bit + +usb:v07FF* + ID_VENDOR_FROM_DATABASE=Unknown + +usb:v07FFp00FF* + ID_MODEL_FROM_DATABASE=Portable Hard Drive + +usb:v0801* + ID_VENDOR_FROM_DATABASE=MagTek + +usb:v0801p0001* + ID_MODEL_FROM_DATABASE=Mini Swipe Reader (Keyboard Emulation) + +usb:v0801p0002* + ID_MODEL_FROM_DATABASE=Mini Swipe Reader + +usb:v0801p0003* + ID_MODEL_FROM_DATABASE=Magstripe Insert Reader + +usb:v0802* + ID_VENDOR_FROM_DATABASE=Mako Technologies, LLC + +usb:v0803* + ID_VENDOR_FROM_DATABASE=Zoom Telephonics, Inc. + +usb:v0803p1300* + ID_MODEL_FROM_DATABASE=V92 Faxmodem + +usb:v0803p3095* + ID_MODEL_FROM_DATABASE=V.92 56K Mini External Modem Model 3095 + +usb:v0803p4310* + ID_MODEL_FROM_DATABASE=4410a Wireless-G Adapter [Intersil ISL3887] + +usb:v0803p4410* + ID_MODEL_FROM_DATABASE=4410b Wireless-G Adapter [ZyDAS ZD1211B] + +usb:v0803p5241* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v0803p5551* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0803p9700* + ID_MODEL_FROM_DATABASE=2986L FaxModem + +usb:v0803p9800* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v0803pA312* + ID_MODEL_FROM_DATABASE=Wireless-G + +usb:v0809* + ID_VENDOR_FROM_DATABASE=Genicom Technology, Inc. + +usb:v080A* + ID_VENDOR_FROM_DATABASE=Evermuch Technology Co., Ltd + +usb:v080B* + ID_VENDOR_FROM_DATABASE=Cross Match Technologies + +usb:v080Bp0002* + ID_MODEL_FROM_DATABASE=Fingerprint Scanner (After ReNumeration) + +usb:v080Bp0010* + ID_MODEL_FROM_DATABASE=300LC Series Fingerprint Scanner (Before ReNumeration) + +usb:v080C* + ID_VENDOR_FROM_DATABASE=Datalogic S.p.A. + +usb:v080Cp0300* + ID_MODEL_FROM_DATABASE=Gryphon D120 Barcode Scanner + +usb:v080Cp0400* + ID_MODEL_FROM_DATABASE=Gryphon D120 Barcode Scanner + +usb:v080Cp0500* + ID_MODEL_FROM_DATABASE=Gryphon D120 Barcode Scanner + +usb:v080Cp0600* + ID_MODEL_FROM_DATABASE=Gryphon M100 Barcode Scanner + +usb:v080D* + ID_VENDOR_FROM_DATABASE=Teco Image Systems Co., Ltd + +usb:v080Dp0102* + ID_MODEL_FROM_DATABASE=Hercules Scan@home 48 + +usb:v080Dp0104* + ID_MODEL_FROM_DATABASE=3.2Slim + +usb:v080Dp0110* + ID_MODEL_FROM_DATABASE=UMAX AstraSlim 1200 Scanner + +usb:v0810* + ID_VENDOR_FROM_DATABASE=Personal Communication Systems, Inc. + +usb:v0810p0001* + ID_MODEL_FROM_DATABASE=Dual PSX Adaptor + +usb:v0810p0002* + ID_MODEL_FROM_DATABASE=Dual PCS Adaptor + +usb:v0810p0003* + ID_MODEL_FROM_DATABASE=PlayStation Gamepad + +usb:v0813* + ID_VENDOR_FROM_DATABASE=Mattel, Inc. + +usb:v0813p0001* + ID_MODEL_FROM_DATABASE=Intel Play QX3 Microscope + +usb:v0813p0002* + ID_MODEL_FROM_DATABASE=Dual Mode Camera Plus + +usb:v0819* + ID_VENDOR_FROM_DATABASE=eLicenser + +usb:v0819p0101* + ID_MODEL_FROM_DATABASE=License Management and Copy Protection + +usb:v081A* + ID_VENDOR_FROM_DATABASE=MG Logic + +usb:v081Ap1000* + ID_MODEL_FROM_DATABASE=Duo Pen Tablet + +usb:v081B* + ID_VENDOR_FROM_DATABASE=Indigita Corp. + +usb:v081Bp0600* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v081Bp0601* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v081C* + ID_VENDOR_FROM_DATABASE=Mipsys + +usb:v081E* + ID_VENDOR_FROM_DATABASE=AlphaSmart, Inc. + +usb:v081EpDF00* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0822* + ID_VENDOR_FROM_DATABASE=Reudo Corp. + +usb:v0822p2001* + ID_MODEL_FROM_DATABASE=IRXpress Infrared Device + +usb:v0825* + ID_VENDOR_FROM_DATABASE=GC Protronics + +usb:v0826* + ID_VENDOR_FROM_DATABASE=Data Transit + +usb:v0827* + ID_VENDOR_FROM_DATABASE=BroadLogic, Inc. + +usb:v0828* + ID_VENDOR_FROM_DATABASE=Sato Corp. + +usb:v0829* + ID_VENDOR_FROM_DATABASE=DirecTV Broadband, Inc. (Telocity) + +usb:v082D* + ID_VENDOR_FROM_DATABASE=Handspring + +usb:v082Dp0100* + ID_MODEL_FROM_DATABASE=Visor + +usb:v082Dp0200* + ID_MODEL_FROM_DATABASE=Treo + +usb:v082Dp0300* + ID_MODEL_FROM_DATABASE=Treo 600 + +usb:v082Dp0400* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v082Dp0500* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v082Dp0600* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830* + ID_VENDOR_FROM_DATABASE=Palm, Inc. + +usb:v0830p0001* + ID_MODEL_FROM_DATABASE=m500 + +usb:v0830p0002* + ID_MODEL_FROM_DATABASE=m505 + +usb:v0830p0003* + ID_MODEL_FROM_DATABASE=m515 + +usb:v0830p0004* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0005* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0006* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0010* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0011* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0012* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0013* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0014* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0020* + ID_MODEL_FROM_DATABASE=i705 + +usb:v0830p0021* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0022* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0023* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0024* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0030* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0031* + ID_MODEL_FROM_DATABASE=Tungsten W + +usb:v0830p0032* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0033* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0034* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0040* + ID_MODEL_FROM_DATABASE=m125 + +usb:v0830p0041* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0042* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0043* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0044* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0050* + ID_MODEL_FROM_DATABASE=m130 + +usb:v0830p0051* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0052* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0053* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0054* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0060* + ID_MODEL_FROM_DATABASE=Tungsten C/E/T/T2/T3 / Zire 71 + +usb:v0830p0061* + ID_MODEL_FROM_DATABASE=Lifedrive / Treo 650/680 / Tunsten E2/T5/TX / Centro / Zire 21/31/72 / Z22 + +usb:v0830p0062* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0063* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0064* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0070* + ID_MODEL_FROM_DATABASE=Zire + +usb:v0830p0071* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0072* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0080* + ID_MODEL_FROM_DATABASE=Serial Adapter [for Palm III] + +usb:v0830p0081* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p0082* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0830p00A0* + ID_MODEL_FROM_DATABASE=Treo 800w + +usb:v0830p0101* + ID_MODEL_FROM_DATABASE=Pre + +usb:v0832* + ID_VENDOR_FROM_DATABASE=Kouwell Electronics Corp. + +usb:v0832p5850* + ID_MODEL_FROM_DATABASE=Cable + +usb:v0833* + ID_VENDOR_FROM_DATABASE=Sourcenext Corp. + +usb:v0833p012E* + ID_MODEL_FROM_DATABASE=KeikaiDenwa 8 with charger + +usb:v0833p039F* + ID_MODEL_FROM_DATABASE=KeikaiDenwa 8 + +usb:v0835* + ID_VENDOR_FROM_DATABASE=Action Star Enterprise Co., Ltd + +usb:v0836* + ID_VENDOR_FROM_DATABASE=TrekStor + +usb:v0836p2836* + ID_MODEL_FROM_DATABASE=i.Beat mood + +usb:v0839* + ID_VENDOR_FROM_DATABASE=Samsung Techwin Co., Ltd + +usb:v0839p0005* + ID_MODEL_FROM_DATABASE=Digimax Camera + +usb:v0839p0008* + ID_MODEL_FROM_DATABASE=Digimax 230 Camera + +usb:v0839p0009* + ID_MODEL_FROM_DATABASE=Digimax 340 + +usb:v0839p000A* + ID_MODEL_FROM_DATABASE=Digimax 410 + +usb:v0839p000E* + ID_MODEL_FROM_DATABASE=Digimax 360 + +usb:v0839p0010* + ID_MODEL_FROM_DATABASE=Digimax 300 + +usb:v0839p1003* + ID_MODEL_FROM_DATABASE=Digimax 210SE + +usb:v0839p1005* + ID_MODEL_FROM_DATABASE=Digimax 220 + +usb:v0839p1009* + ID_MODEL_FROM_DATABASE=Digimax V4 + +usb:v0839p1012* + ID_MODEL_FROM_DATABASE=6500 Document Camera + +usb:v0839p1058* + ID_MODEL_FROM_DATABASE=S730 Camera + +usb:v0839p1064* + ID_MODEL_FROM_DATABASE=Digimax D830 Camera + +usb:v0839p1542* + ID_MODEL_FROM_DATABASE=Digimax 50 Duo + +usb:v0839p3000* + ID_MODEL_FROM_DATABASE=Digimax 35 MP3 + +usb:v083A* + ID_VENDOR_FROM_DATABASE=Accton Technology Corp. + +usb:v083Ap1046* + ID_MODEL_FROM_DATABASE=10/100 Ethernet [pegasus] + +usb:v083Ap1060* + ID_MODEL_FROM_DATABASE=HomeLine Adapter + +usb:v083Ap1F4D* + ID_MODEL_FROM_DATABASE=SMC8013WG Broadband Remote NDIS Device + +usb:v083Ap3046* + ID_MODEL_FROM_DATABASE=10/100 Series Adapter + +usb:v083Ap3060* + ID_MODEL_FROM_DATABASE=1/10/100 Adapter + +usb:v083Ap3501* + ID_MODEL_FROM_DATABASE=2664W + +usb:v083Ap3502* + ID_MODEL_FROM_DATABASE=WN3501D Wireless Adapter + +usb:v083Ap3503* + ID_MODEL_FROM_DATABASE=T-Sinus 111 Wireless Adapter + +usb:v083Ap4501* + ID_MODEL_FROM_DATABASE=T-Sinus 154data + +usb:v083Ap4502* + ID_MODEL_FROM_DATABASE=Siemens S30853-S1016-R107 802.11g Wireless Adapter [Intersil ISL3886] + +usb:v083Ap4505* + ID_MODEL_FROM_DATABASE=SMCWUSB-G 802.11bg + +usb:v083Ap4507* + ID_MODEL_FROM_DATABASE=SMCWUSBT-G2 802.11g Wireless Adapter [Atheros AR5523] + +usb:v083Ap4521* + ID_MODEL_FROM_DATABASE=Siemens S30863-S1016-R107-2 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v083Ap4531* + ID_MODEL_FROM_DATABASE=T-Com Sinus 154 data II [Intersil ISL3887] + +usb:v083Ap5046* + ID_MODEL_FROM_DATABASE=SpeedStream 10/100 Ethernet [pegasus] + +usb:v083Ap5501* + ID_MODEL_FROM_DATABASE=Wireless Adapter 11g + +usb:v083Ap6500* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v083Ap6618* + ID_MODEL_FROM_DATABASE=802.11n Wireless Adapter + +usb:v083Ap7511* + ID_MODEL_FROM_DATABASE=Arcadyan 802.11N Wireless Adapter + +usb:v083Ap7512* + ID_MODEL_FROM_DATABASE=Arcadyan 802.11N Wireless Adapter + +usb:v083Ap7522* + ID_MODEL_FROM_DATABASE=Arcadyan 802.11N Wireless Adapter + +usb:v083Ap8522* + ID_MODEL_FROM_DATABASE=Arcadyan 802.11N Wireless Adapter + +usb:v083Ap8541* + ID_MODEL_FROM_DATABASE=WN4501F 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v083ApA512* + ID_MODEL_FROM_DATABASE=Arcadyan 802.11N Wireless Adapter + +usb:v083ApA618* + ID_MODEL_FROM_DATABASE=SMCWUSBS-N EZ Connect N Draft 11n Wireless Adapter [Ralink RT2870] + +usb:v083ApA701* + ID_MODEL_FROM_DATABASE=SMCWUSBS-N3 EZ Connect N Wireless Adapter [Ralink RT3070] + +usb:v083ApB004* + ID_MODEL_FROM_DATABASE=CPWUE001 USB/Ethernet Adapter + +usb:v083ApB522* + ID_MODEL_FROM_DATABASE=SMCWUSBS-N2 EZ Connect N Wireless Adapter [Ralink RT2870] + +usb:v083ApBB01* + ID_MODEL_FROM_DATABASE=BlueExpert Bluetooth Device + +usb:v083ApC003* + ID_MODEL_FROM_DATABASE=802.11b Wireless Adapter + +usb:v083ApC501* + ID_MODEL_FROM_DATABASE=Zoom 4410 Wireless-G [Intersil ISL3887] + +usb:v083ApC561* + ID_MODEL_FROM_DATABASE=802.11a/g Wireless Adapter + +usb:v083ApD522* + ID_MODEL_FROM_DATABASE=Speedport W 102 Stick IEEE 802.11n USB 2.0 Adapter + +usb:v083ApE501* + ID_MODEL_FROM_DATABASE=ZD1211B + +usb:v083ApE503* + ID_MODEL_FROM_DATABASE=Arcadyan WN4501 802.11b/g + +usb:v083ApE506* + ID_MODEL_FROM_DATABASE=WUS-201 802.11bg + +usb:v083ApF501* + ID_MODEL_FROM_DATABASE=802.11g Wireless Adapter + +usb:v083ApF502* + ID_MODEL_FROM_DATABASE=802.11g Wireless Adapter + +usb:v083ApF522* + ID_MODEL_FROM_DATABASE=Arcadyan WN7512 802.11n + +usb:v083F* + ID_VENDOR_FROM_DATABASE=Global Village + +usb:v083FpB100* + ID_MODEL_FROM_DATABASE=TelePort V.90 Fax/Modem + +usb:v0840* + ID_VENDOR_FROM_DATABASE=Argosy Research, Inc. + +usb:v0840p0060* + ID_MODEL_FROM_DATABASE=Storage Adapter Bridge Module + +usb:v0841* + ID_VENDOR_FROM_DATABASE=Rioport.com, Inc. + +usb:v0841p0001* + ID_MODEL_FROM_DATABASE=Rio 500 + +usb:v0844* + ID_VENDOR_FROM_DATABASE=Welland Industrial Co., Ltd + +usb:v0846* + ID_VENDOR_FROM_DATABASE=NetGear, Inc. + +usb:v0846p1001* + ID_MODEL_FROM_DATABASE=EA101 10 Mbps 10BASE-T Ethernet [Kawasaki LSI KL5KLUSB101B] + +usb:v0846p1002* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v0846p1020* + ID_MODEL_FROM_DATABASE=FA101 Fast Ethernet USB 1.1 + +usb:v0846p1040* + ID_MODEL_FROM_DATABASE=FA120 Fast Ethernet USB 2.0 [Asix AX88172 / AX8817x] + +usb:v0846p1100* + ID_MODEL_FROM_DATABASE=Managed Switch M4100 series, M5300 series, M7100 series + +usb:v0846p4110* + ID_MODEL_FROM_DATABASE=MA111(v1) 802.11b Wireless [Intersil Prism 3.0] + +usb:v0846p4200* + ID_MODEL_FROM_DATABASE=WG121(v1) 54 Mbps Wireless [Intersil ISL3886] + +usb:v0846p4210* + ID_MODEL_FROM_DATABASE=WG121(v2) 54 Mbps Wireless [Intersil ISL3886] + +usb:v0846p4220* + ID_MODEL_FROM_DATABASE=WG111(v1) 54 Mbps Wireless [Intersil ISL3886] + +usb:v0846p4230* + ID_MODEL_FROM_DATABASE=MA111(v2) 802.11b Wireless [SIS SIS 162] + +usb:v0846p4240* + ID_MODEL_FROM_DATABASE=WG111(v1) rev 2 54 Mbps Wireless [Intersil ISL3887] + +usb:v0846p4260* + ID_MODEL_FROM_DATABASE=WG111v3 54 Mbps Wireless [realtek RTL8187B] + +usb:v0846p4300* + ID_MODEL_FROM_DATABASE=WG111U Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX] + +usb:v0846p4301* + ID_MODEL_FROM_DATABASE=WG111U (no firmware) Double 108 Mbps Wireless [Atheros AR5004X / AR5005UX] + +usb:v0846p5F00* + ID_MODEL_FROM_DATABASE=WPN111 802.11g Wireless Adapter [Atheros AR5523] + +usb:v0846p6A00* + ID_MODEL_FROM_DATABASE=WG111v2 54 Mbps Wireless [RealTek RTL8187L] + +usb:v0846p7100* + ID_MODEL_FROM_DATABASE=WN121T RangeMax Next Wireless-N [Marvell TopDog] + +usb:v0846p9000* + ID_MODEL_FROM_DATABASE=WN111(v1) RangeMax Next Wireless [Marvell 88W8362+88W8060] + +usb:v0846p9001* + ID_MODEL_FROM_DATABASE=WN111(v2) RangeMax Next Wireless [Atheros AR9170+AR9101] + +usb:v0846p9010* + ID_MODEL_FROM_DATABASE=WNDA3100v1 802.11abgn [Atheros AR9170+AR9104] + +usb:v0846p9011* + ID_MODEL_FROM_DATABASE=WNDA3100v2 802.11abgn [Broadcom BCM4323] + +usb:v0846p9012* + ID_MODEL_FROM_DATABASE=WNDA4100 802.11abgn 3x3:3 [Ralink RT3573] + +usb:v0846p9014* + ID_MODEL_FROM_DATABASE=WNDA3100v3 802.11abgn 2x2:2 [MediaTek MT7632U] + +usb:v0846p9018* + ID_MODEL_FROM_DATABASE=WNDA3200 802.11abgn Wireless Adapter [Atheros AR7010+AR9280] + +usb:v0846p9020* + ID_MODEL_FROM_DATABASE=WNA3100(v1) Wireless-N 300 [Broadcom BCM43231] + +usb:v0846p9021* + ID_MODEL_FROM_DATABASE=WNA3100M(v1) Wireless-N 300 [Realtek RTL8192CU] + +usb:v0846p9030* + ID_MODEL_FROM_DATABASE=WNA1100 Wireless-N 150 [Atheros AR9271] + +usb:v0846p9040* + ID_MODEL_FROM_DATABASE=WNA1000 Wireless-N 150 [Atheros AR9170+AR9101] + +usb:v0846p9041* + ID_MODEL_FROM_DATABASE=WNA1000M 802.11bgn [Realtek RTL8188CUS] + +usb:v0846p9042* + ID_MODEL_FROM_DATABASE=On Networks N150MA 802.11bgn [Realtek RTL8188CUS] + +usb:v0846p9043* + ID_MODEL_FROM_DATABASE=WNA1000Mv2 802.11bgn [Realtek RTL8188CUS?] + +usb:v0846p9050* + ID_MODEL_FROM_DATABASE=A6200 802.11a/b/g/n/ac Wireless Adapter [Broadcom BCM43526] + +usb:v0846p9052* + ID_MODEL_FROM_DATABASE=A6100 AC600 DB Wireless Adapter [Realtek RTL8811AU] + +usb:v0846pA001* + ID_MODEL_FROM_DATABASE=PA101 10 Mbps HPNA Home Phoneline RJ-1 + +usb:v0846pF001* + ID_MODEL_FROM_DATABASE=On Networks N300MA 802.11bgn [Realtek RTL8192CU] + +usb:v084D* + ID_VENDOR_FROM_DATABASE=Minton Optic Industry Co., Inc. + +usb:v084Dp0001* + ID_MODEL_FROM_DATABASE=Jenoptik JD800i + +usb:v084Dp0003* + ID_MODEL_FROM_DATABASE=S-Cam F5/D-Link DSC-350 Digital Camera + +usb:v084Dp0011* + ID_MODEL_FROM_DATABASE=Argus DC3500 Digital Camera + +usb:v084Dp0014* + ID_MODEL_FROM_DATABASE=Praktica DC 32 + +usb:v084Dp0019* + ID_MODEL_FROM_DATABASE=Praktica DPix3000 + +usb:v084Dp0025* + ID_MODEL_FROM_DATABASE=Praktica DC 60 + +usb:v084Dp1001* + ID_MODEL_FROM_DATABASE=ScanHex SX-35d + +usb:v084E* + ID_VENDOR_FROM_DATABASE=KB Gear + +usb:v084Ep0001* + ID_MODEL_FROM_DATABASE=JamCam Camera + +usb:v084Ep1001* + ID_MODEL_FROM_DATABASE=Jam Studio Tablet + +usb:v084Ep1002* + ID_MODEL_FROM_DATABASE=Pablo Tablet + +usb:v084F* + ID_VENDOR_FROM_DATABASE=Empeg + +usb:v084Fp0001* + ID_MODEL_FROM_DATABASE=Empeg-Car Mark I/II Player + +usb:v0850* + ID_VENDOR_FROM_DATABASE=Fast Point Technologies, Inc. + +usb:v0851* + ID_VENDOR_FROM_DATABASE=Macronix International Co., Ltd + +usb:v0851p1542* + ID_MODEL_FROM_DATABASE=SiPix Blink + +usb:v0851p1543* + ID_MODEL_FROM_DATABASE=Maxell WS30 Slim Digital Camera, or Pandigital PI8004W01 digital photo frame + +usb:v0851pA168* + ID_MODEL_FROM_DATABASE=MXIC + +usb:v0852* + ID_VENDOR_FROM_DATABASE=CSEM + +usb:v0853* + ID_VENDOR_FROM_DATABASE=Topre Corporation + +usb:v0853p0100* + ID_MODEL_FROM_DATABASE=HHKB Professional + +usb:v0854* + ID_VENDOR_FROM_DATABASE=ActiveWire, Inc. + +usb:v0854p0100* + ID_MODEL_FROM_DATABASE=I/O Board + +usb:v0854p0101* + ID_MODEL_FROM_DATABASE=I/O Board, rev1 + +usb:v0856* + ID_VENDOR_FROM_DATABASE=B&B Electronics + +usb:v0856pAC01* + ID_MODEL_FROM_DATABASE=uLinks USOTL4 RS422/485 Adapter + +usb:v0858* + ID_VENDOR_FROM_DATABASE=Hitachi Maxell, Ltd + +usb:v0858p3102* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0858pFFFF* + ID_MODEL_FROM_DATABASE=Maxell module with BlueCore in DFU mode + +usb:v0859* + ID_VENDOR_FROM_DATABASE=Minolta Systems Laboratory, Inc. + +usb:v085A* + ID_VENDOR_FROM_DATABASE=Xircom + +usb:v085Ap0001* + ID_MODEL_FROM_DATABASE=Portstation Dual Serial Port + +usb:v085Ap0003* + ID_MODEL_FROM_DATABASE=Portstation Paraller Port + +usb:v085Ap0008* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v085Ap0009* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v085Ap000B* + ID_MODEL_FROM_DATABASE=Portstation Dual PS/2 Port + +usb:v085Ap0021* + ID_MODEL_FROM_DATABASE=1 port to Serial Converter + +usb:v085Ap0022* + ID_MODEL_FROM_DATABASE=Parallel Port + +usb:v085Ap0023* + ID_MODEL_FROM_DATABASE=2 port to Serial Converter + +usb:v085Ap0024* + ID_MODEL_FROM_DATABASE=Parallel Port + +usb:v085Ap0026* + ID_MODEL_FROM_DATABASE=PortGear SCSI + +usb:v085Ap0027* + ID_MODEL_FROM_DATABASE=1 port to Serial Converter + +usb:v085Ap0028* + ID_MODEL_FROM_DATABASE=PortGear to SCSI Converter + +usb:v085Ap0032* + ID_MODEL_FROM_DATABASE=PortStation SCSI Module + +usb:v085Ap003C* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v085Ap0299* + ID_MODEL_FROM_DATABASE=Colorvision, Inc. Monitor Spyder + +usb:v085Ap8021* + ID_MODEL_FROM_DATABASE=1 port to Serial + +usb:v085Ap8023* + ID_MODEL_FROM_DATABASE=2 port to Serial + +usb:v085Ap8027* + ID_MODEL_FROM_DATABASE=PGSDB9 Serial Port + +usb:v085C* + ID_VENDOR_FROM_DATABASE=ColorVision, Inc. + +usb:v085Cp0100* + ID_MODEL_FROM_DATABASE=Spyder 1 + +usb:v085Cp0200* + ID_MODEL_FROM_DATABASE=Spyder 2 + +usb:v085Cp0300* + ID_MODEL_FROM_DATABASE=Spyder 3 + +usb:v085Cp0400* + ID_MODEL_FROM_DATABASE=Spyder 4 + +usb:v0862* + ID_VENDOR_FROM_DATABASE=Teletrol Systems, Inc. + +usb:v0863* + ID_VENDOR_FROM_DATABASE=Filanet Corp. + +usb:v0864* + ID_VENDOR_FROM_DATABASE=NetGear, Inc. + +usb:v0864p4100* + ID_MODEL_FROM_DATABASE=MA101 802.11b Adapter + +usb:v0864p4102* + ID_MODEL_FROM_DATABASE=MA101 802.11b Adapter + +usb:v0867* + ID_VENDOR_FROM_DATABASE=Data Translation, Inc. + +usb:v0867p9812* + ID_MODEL_FROM_DATABASE=ECON Data acquisition unit + +usb:v0867p9816* + ID_MODEL_FROM_DATABASE=DT9816 ECON data acquisition module + +usb:v0867p9836* + ID_MODEL_FROM_DATABASE=DT9836 data acquisition card + +usb:v086A* + ID_VENDOR_FROM_DATABASE=Emagic Soft- und Hardware GmbH + +usb:v086Ap0001* + ID_MODEL_FROM_DATABASE=Unitor8 + +usb:v086Ap0002* + ID_MODEL_FROM_DATABASE=AMT8 + +usb:v086Ap0003* + ID_MODEL_FROM_DATABASE=MT4 + +usb:v086C* + ID_VENDOR_FROM_DATABASE=DeTeWe - Deutsche Telephonwerke AG & Co. + +usb:v086Cp1001* + ID_MODEL_FROM_DATABASE=Eumex 504PC ISDN TA + +usb:v086Cp1002* + ID_MODEL_FROM_DATABASE=Eumex 504PC (FlashLoad) + +usb:v086Cp1003* + ID_MODEL_FROM_DATABASE=TA33 ISDN TA + +usb:v086Cp1004* + ID_MODEL_FROM_DATABASE=TA33 (FlashLoad) + +usb:v086Cp1005* + ID_MODEL_FROM_DATABASE=Eumex 604PC HomeNet + +usb:v086Cp1006* + ID_MODEL_FROM_DATABASE=Eumex 604PC HomeNet (FlashLoad) + +usb:v086Cp1007* + ID_MODEL_FROM_DATABASE=Eumex 704PC DSL + +usb:v086Cp1008* + ID_MODEL_FROM_DATABASE=Eumex 704PC DSL (FlashLoad) + +usb:v086Cp1009* + ID_MODEL_FROM_DATABASE=Eumex 724PC DSL + +usb:v086Cp100A* + ID_MODEL_FROM_DATABASE=Eumex 724PC DSL (FlashLoad) + +usb:v086Cp100B* + ID_MODEL_FROM_DATABASE=OpenCom 30 + +usb:v086Cp100C* + ID_MODEL_FROM_DATABASE=OpenCom 30 (FlashLoad) + +usb:v086Cp100D* + ID_MODEL_FROM_DATABASE=BeeTel Home 100 + +usb:v086Cp100E* + ID_MODEL_FROM_DATABASE=BeeTel Home 100 (FlashLoad) + +usb:v086Cp1011* + ID_MODEL_FROM_DATABASE=USB2DECT + +usb:v086Cp1012* + ID_MODEL_FROM_DATABASE=USB2DECT (FlashLoad) + +usb:v086Cp1013* + ID_MODEL_FROM_DATABASE=Eumex 704PC LAN + +usb:v086Cp1014* + ID_MODEL_FROM_DATABASE=Eumex 704PC LAN (FlashLoad) + +usb:v086Cp1019* + ID_MODEL_FROM_DATABASE=Eumex 504 SE + +usb:v086Cp101A* + ID_MODEL_FROM_DATABASE=Eumex 504 SE (Flash-Mode) + +usb:v086Cp1021* + ID_MODEL_FROM_DATABASE=OpenCom 40 + +usb:v086Cp1022* + ID_MODEL_FROM_DATABASE=OpenCom 40 (FlashLoad) + +usb:v086Cp1023* + ID_MODEL_FROM_DATABASE=OpenCom 45 + +usb:v086Cp1024* + ID_MODEL_FROM_DATABASE=OpenCom 45 (FlashLoad) + +usb:v086Cp1025* + ID_MODEL_FROM_DATABASE=Sinus 61 data + +usb:v086Cp1029* + ID_MODEL_FROM_DATABASE=dect BOX + +usb:v086Cp102C* + ID_MODEL_FROM_DATABASE=Eumex 604PC HomeNet [FlashLoad] + +usb:v086Cp1030* + ID_MODEL_FROM_DATABASE=Eumex 704PC DSL [FlashLoad] + +usb:v086Cp1032* + ID_MODEL_FROM_DATABASE=OpenCom 40 [FlashLoad] + +usb:v086Cp1033* + ID_MODEL_FROM_DATABASE=OpenCom 30 plus + +usb:v086Cp1034* + ID_MODEL_FROM_DATABASE=OpenCom 30 plus (FlashLoad) + +usb:v086Cp1041* + ID_MODEL_FROM_DATABASE=Eumex 220PC + +usb:v086Cp1042* + ID_MODEL_FROM_DATABASE=Eumex 220PC (FlashMode) + +usb:v086Cp1055* + ID_MODEL_FROM_DATABASE=Eumex 220 Version 2 ISDN TA + +usb:v086Cp1056* + ID_MODEL_FROM_DATABASE=Eumex 220 Version 2 ISDN TA (Flash-Mode) + +usb:v086Cp2000* + ID_MODEL_FROM_DATABASE=OpenCom 1000 + +usb:v086E* + ID_VENDOR_FROM_DATABASE=System TALKS, Inc. + +usb:v086Ep1920* + ID_MODEL_FROM_DATABASE=SGC-X2UL + +usb:v086F* + ID_VENDOR_FROM_DATABASE=MEC IMEX, Inc. + +usb:v0870* + ID_VENDOR_FROM_DATABASE=Metricom + +usb:v0870p0001* + ID_MODEL_FROM_DATABASE=Ricochet GS + +usb:v0871* + ID_VENDOR_FROM_DATABASE=SanDisk, Inc. + +usb:v0871p0001* + ID_MODEL_FROM_DATABASE=SDDR-01 Compact Flash Reader + +usb:v0871p0002* + ID_MODEL_FROM_DATABASE=SDDR-31 Compact Flash Reader + +usb:v0871p0005* + ID_MODEL_FROM_DATABASE=SDDR-05 Compact Flash Reader + +usb:v0873* + ID_VENDOR_FROM_DATABASE=Xpeed, Inc. + +usb:v0874* + ID_VENDOR_FROM_DATABASE=A-Tec Subsystem, Inc. + +usb:v0879* + ID_VENDOR_FROM_DATABASE=Comtrol Corp. + +usb:v087C* + ID_VENDOR_FROM_DATABASE=Adesso/Kbtek America, Inc. + +usb:v087D* + ID_VENDOR_FROM_DATABASE=Jaton Corp. + +usb:v087Dp5704* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v087E* + ID_VENDOR_FROM_DATABASE=Fujitsu Computer Products of America + +usb:v087F* + ID_VENDOR_FROM_DATABASE=QualCore Logic Inc. + +usb:v0880* + ID_VENDOR_FROM_DATABASE=APT Technologies, Inc. + +usb:v0883* + ID_VENDOR_FROM_DATABASE=Recording Industry Association of America (RIAA) + +usb:v0885* + ID_VENDOR_FROM_DATABASE=Boca Research, Inc. + +usb:v0886* + ID_VENDOR_FROM_DATABASE=XAC Automation Corp. + +usb:v0886p0630* + ID_MODEL_FROM_DATABASE=Intel PC Camera CS630 + +usb:v0887* + ID_VENDOR_FROM_DATABASE=Hannstar Electronics Corp. + +usb:v088A* + ID_VENDOR_FROM_DATABASE=TechTools + +usb:v088Ap1002* + ID_MODEL_FROM_DATABASE=DigiView DV3100 + +usb:v088B* + ID_VENDOR_FROM_DATABASE=MassWorks, Inc. + +usb:v088Bp4944* + ID_MODEL_FROM_DATABASE=MassWorks ID-75 TouchScreen + +usb:v088C* + ID_VENDOR_FROM_DATABASE=Swecoin AB + +usb:v088Cp2030* + ID_MODEL_FROM_DATABASE=Ticket Printer TTP 2030 + +usb:v088E* + ID_VENDOR_FROM_DATABASE=iLok + +usb:v088Ep5036* + ID_MODEL_FROM_DATABASE=Portable secure storage for software licenses + +usb:v0892* + ID_VENDOR_FROM_DATABASE=DioGraphy, Inc. + +usb:v0892p0101* + ID_MODEL_FROM_DATABASE=Smartdio Reader/Writer + +usb:v0894* + ID_VENDOR_FROM_DATABASE=TSI Incorporated + +usb:v0894p0010* + ID_MODEL_FROM_DATABASE=Remote NDIS Network Device + +usb:v0897* + ID_VENDOR_FROM_DATABASE=Lauterbach + +usb:v0897p0002* + ID_MODEL_FROM_DATABASE=Power Debug/Power Debug II + +usb:v089C* + ID_VENDOR_FROM_DATABASE=United Technologies Research Cntr. + +usb:v089D* + ID_VENDOR_FROM_DATABASE=Icron Technologies Corp. + +usb:v089E* + ID_VENDOR_FROM_DATABASE=NST Co., Ltd + +usb:v089F* + ID_VENDOR_FROM_DATABASE=Primex Aerospace Co. + +usb:v08A5* + ID_VENDOR_FROM_DATABASE=e9, Inc. + +usb:v08A6* + ID_VENDOR_FROM_DATABASE=Toshiba TEC + +usb:v08A6p0051* + ID_MODEL_FROM_DATABASE=B-SV4 + +usb:v08A8* + ID_VENDOR_FROM_DATABASE=Andrea Electronics + +usb:v08A9* + ID_VENDOR_FROM_DATABASE=CWAV Inc. + +usb:v08A9p0005* + ID_MODEL_FROM_DATABASE=USBee ZX + +usb:v08A9p0009* + ID_MODEL_FROM_DATABASE=USBee SX + +usb:v08A9p0012* + ID_MODEL_FROM_DATABASE=USBee AX-Standard + +usb:v08A9p0013* + ID_MODEL_FROM_DATABASE=USBee AX-Plus + +usb:v08A9p0014* + ID_MODEL_FROM_DATABASE=USBee AX-Pro + +usb:v08A9p0015* + ID_MODEL_FROM_DATABASE=USBee DX + +usb:v08AC* + ID_VENDOR_FROM_DATABASE=Macraigor Systems LLC + +usb:v08ACp2024* + ID_MODEL_FROM_DATABASE=usbWiggler + +usb:v08AE* + ID_VENDOR_FROM_DATABASE=Macally (Mace Group, Inc.) + +usb:v08B0* + ID_VENDOR_FROM_DATABASE=Metrohm + +usb:v08B0p0006* + ID_MODEL_FROM_DATABASE=814 Sample Processor + +usb:v08B0p0015* + ID_MODEL_FROM_DATABASE=857 Titrando + +usb:v08B0p001A* + ID_MODEL_FROM_DATABASE=852 Titrando + +usb:v08B4* + ID_VENDOR_FROM_DATABASE=Sorenson Vision, Inc. + +usb:v08B7* + ID_VENDOR_FROM_DATABASE=NATSU + +usb:v08B7p0001* + ID_MODEL_FROM_DATABASE=Playstation adapter + +usb:v08B8* + ID_VENDOR_FROM_DATABASE=J. Gordon Electronic Design, Inc. + +usb:v08B8p01F4* + ID_MODEL_FROM_DATABASE=USBSIMM1 + +usb:v08B9* + ID_VENDOR_FROM_DATABASE=RadioShack Corp. (Tandy) + +usb:v08BB* + ID_VENDOR_FROM_DATABASE=Texas Instruments + +usb:v08BBp2702* + ID_MODEL_FROM_DATABASE=Speakers + +usb:v08BBp2704* + ID_MODEL_FROM_DATABASE=Audio Codec + +usb:v08BBp2706* + ID_MODEL_FROM_DATABASE=PCM2706 Audio Codec + +usb:v08BBp2900* + ID_MODEL_FROM_DATABASE=PCM2900 Audio Codec + +usb:v08BBp2901* + ID_MODEL_FROM_DATABASE=PCM2901 Audio Codec + +usb:v08BBp2902* + ID_MODEL_FROM_DATABASE=PCM2902 Audio Codec + +usb:v08BBp2904* + ID_MODEL_FROM_DATABASE=PCM2904 Audio Codec + +usb:v08BBp2910* + ID_MODEL_FROM_DATABASE=PCM2912 Audio Codec + +usb:v08BBp29B0* + ID_MODEL_FROM_DATABASE=PCM2900B Audio CODEC + +usb:v08BBp29B2* + ID_MODEL_FROM_DATABASE=PCM2902 Audio CODEC + +usb:v08BBp29B3* + ID_MODEL_FROM_DATABASE=PCM2903B Audio CODEC + +usb:v08BBp29B6* + ID_MODEL_FROM_DATABASE=PCM2906B Audio CODEC + +usb:v08BBp29C0* + ID_MODEL_FROM_DATABASE=PCM2900C Audio CODEC + +usb:v08BBp29C2* + ID_MODEL_FROM_DATABASE=PCM2902C Audio CODEC + +usb:v08BBp29C3* + ID_MODEL_FROM_DATABASE=PCM2903C Audio CODEC + +usb:v08BBp29C6* + ID_MODEL_FROM_DATABASE=PCM2906C Audio CODEC + +usb:v08BD* + ID_VENDOR_FROM_DATABASE=Citizen Watch Co., Ltd + +usb:v08BDp0208* + ID_MODEL_FROM_DATABASE=CLP-521 Label Printer + +usb:v08BDp1100* + ID_MODEL_FROM_DATABASE=X1-USB Floppy + +usb:v08C3* + ID_VENDOR_FROM_DATABASE=Precise Biometrics + +usb:v08C3p0001* + ID_MODEL_FROM_DATABASE=100 SC + +usb:v08C3p0002* + ID_MODEL_FROM_DATABASE=100 A + +usb:v08C3p0003* + ID_MODEL_FROM_DATABASE=100 SC BioKeyboard + +usb:v08C3p0006* + ID_MODEL_FROM_DATABASE=100 A BioKeyboard + +usb:v08C3p0100* + ID_MODEL_FROM_DATABASE=100 MC ISP + +usb:v08C3p0101* + ID_MODEL_FROM_DATABASE=100 MC FingerPrint and SmartCard Reader + +usb:v08C3p0300* + ID_MODEL_FROM_DATABASE=100 AX + +usb:v08C3p0400* + ID_MODEL_FROM_DATABASE=100 SC + +usb:v08C3p0401* + ID_MODEL_FROM_DATABASE=150 MC + +usb:v08C3p0402* + ID_MODEL_FROM_DATABASE=200 MC FingerPrint and SmartCard Reader + +usb:v08C3p0404* + ID_MODEL_FROM_DATABASE=100 SC Upgrade + +usb:v08C3p0405* + ID_MODEL_FROM_DATABASE=150 MC Upgrade + +usb:v08C3p0406* + ID_MODEL_FROM_DATABASE=100 MC Upgrade + +usb:v08C4* + ID_VENDOR_FROM_DATABASE=Proxim, Inc. + +usb:v08C4p0100* + ID_MODEL_FROM_DATABASE=Skyline 802.11b Wireless Adapter + +usb:v08C4p02F2* + ID_MODEL_FROM_DATABASE=Farallon Home Phoneline Adapter + +usb:v08C7* + ID_VENDOR_FROM_DATABASE=Key Nice Enterprise Co., Ltd + +usb:v08C8* + ID_VENDOR_FROM_DATABASE=2Wire, Inc. + +usb:v08C9* + ID_VENDOR_FROM_DATABASE=Nippon Telegraph and Telephone Corp. + +usb:v08CA* + ID_VENDOR_FROM_DATABASE=Aiptek International, Inc. + +usb:v08CAp0001* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v08CAp0010* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v08CAp0020* + ID_MODEL_FROM_DATABASE=APT-6000U Tablet + +usb:v08CAp0021* + ID_MODEL_FROM_DATABASE=APT-2 Tablet + +usb:v08CAp0022* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v08CAp0023* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v08CAp0024* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v08CAp0100* + ID_MODEL_FROM_DATABASE=Pen Drive + +usb:v08CAp0102* + ID_MODEL_FROM_DATABASE=DualCam + +usb:v08CAp0103* + ID_MODEL_FROM_DATABASE=Pocket DV Digital Camera + +usb:v08CAp0104* + ID_MODEL_FROM_DATABASE=Pocket DVII + +usb:v08CAp0105* + ID_MODEL_FROM_DATABASE=Mega DV(Disk) + +usb:v08CAp0106* + ID_MODEL_FROM_DATABASE=Pocket DV3100+ + +usb:v08CAp0107* + ID_MODEL_FROM_DATABASE=Pocket DV3100 + +usb:v08CAp0109* + ID_MODEL_FROM_DATABASE=Nisis DV4 Digital Camera + +usb:v08CAp010A* + ID_MODEL_FROM_DATABASE=Trust 738AV LCD PV Mass Storage + +usb:v08CAp0111* + ID_MODEL_FROM_DATABASE=PenCam VGA Plus + +usb:v08CAp2008* + ID_MODEL_FROM_DATABASE=Mini PenCam 2 + +usb:v08CAp2010* + ID_MODEL_FROM_DATABASE=Pocket CAM 3 Mega (webcam) + +usb:v08CAp2011* + ID_MODEL_FROM_DATABASE=Pocket CAM 3 Mega (storage) + +usb:v08CAp2016* + ID_MODEL_FROM_DATABASE=PocketCam 2 Mega + +usb:v08CAp2018* + ID_MODEL_FROM_DATABASE=Pencam SD 2M + +usb:v08CAp2019* + ID_MODEL_FROM_DATABASE=Pencam SD 2M (mass storage mode) + +usb:v08CAp2020* + ID_MODEL_FROM_DATABASE=Slim 3000F + +usb:v08CAp2022* + ID_MODEL_FROM_DATABASE=Slim 3200 + +usb:v08CAp2024* + ID_MODEL_FROM_DATABASE=Pocket DV3500 + +usb:v08CAp2028* + ID_MODEL_FROM_DATABASE=Pocket Cam4M + +usb:v08CAp2040* + ID_MODEL_FROM_DATABASE=Pocket DV4100M + +usb:v08CAp2042* + ID_MODEL_FROM_DATABASE=Pocket DV5100M Composite Device + +usb:v08CAp2043* + ID_MODEL_FROM_DATABASE=Pocket DV5100M (Disk) + +usb:v08CAp2060* + ID_MODEL_FROM_DATABASE=Pocket DV5300 + +usb:v08CD* + ID_VENDOR_FROM_DATABASE=Jue Hsun Ind. Corp. + +usb:v08CE* + ID_VENDOR_FROM_DATABASE=Long Well Electronics Corp. + +usb:v08CF* + ID_VENDOR_FROM_DATABASE=Productivity Enhancement Products + +usb:v08D1* + ID_VENDOR_FROM_DATABASE=smartBridges, Inc. + +usb:v08D1p0001* + ID_MODEL_FROM_DATABASE=smartNIC Ethernet [catc] + +usb:v08D1p0003* + ID_MODEL_FROM_DATABASE=smartNIC 2 PnP Ethernet + +usb:v08D3* + ID_VENDOR_FROM_DATABASE=Virtual Ink + +usb:v08D4* + ID_VENDOR_FROM_DATABASE=Fujitsu Siemens Computers + +usb:v08D4p0009* + ID_MODEL_FROM_DATABASE=SCR SmartCard Reader + +usb:v08D8* + ID_VENDOR_FROM_DATABASE=IXXAT Automation GmbH + +usb:v08D8p0002* + ID_MODEL_FROM_DATABASE=USB-to-CAN compact + +usb:v08D8p0003* + ID_MODEL_FROM_DATABASE=USB-to-CAN II + +usb:v08D8p0100* + ID_MODEL_FROM_DATABASE=USB-to-CAN + +usb:v08D9* + ID_VENDOR_FROM_DATABASE=Increment P Corp. + +usb:v08DD* + ID_VENDOR_FROM_DATABASE=Billionton Systems, Inc. + +usb:v08DDp0112* + ID_MODEL_FROM_DATABASE=Wireless LAN Adapter + +usb:v08DDp0113* + ID_MODEL_FROM_DATABASE=Wireless LAN Adapter + +usb:v08DDp0986* + ID_MODEL_FROM_DATABASE=USB-100N Ethernet [pegasus] + +usb:v08DDp0987* + ID_MODEL_FROM_DATABASE=USBLP-100 HomePNA Ethernet [pegasus] + +usb:v08DDp0988* + ID_MODEL_FROM_DATABASE=USBEL-100 Ethernet [pegasus] + +usb:v08DDp1986* + ID_MODEL_FROM_DATABASE=10/100 LAN Adapter + +usb:v08DDp2103* + ID_MODEL_FROM_DATABASE=DVB-T TV-Tuner Card-R + +usb:v08DDp8511* + ID_MODEL_FROM_DATABASE=USBE-100 Ethernet [pegasus2] + +usb:v08DDp90FF* + ID_MODEL_FROM_DATABASE=USB2AR Ethernet + +usb:v08DE* + ID_VENDOR_FROM_DATABASE=??? + +usb:v08DEp7A01* + ID_MODEL_FROM_DATABASE=802.11b Adapter + +usb:v08DF* + ID_VENDOR_FROM_DATABASE=Spyrus, Inc. + +usb:v08DFp0001* + ID_MODEL_FROM_DATABASE=Rosetta Token V1 + +usb:v08DFp0002* + ID_MODEL_FROM_DATABASE=Rosetta Token V2 + +usb:v08DFp0003* + ID_MODEL_FROM_DATABASE=Rosetta Token V3 + +usb:v08DFp0A00* + ID_MODEL_FROM_DATABASE=Lynks Interface + +usb:v08E3* + ID_VENDOR_FROM_DATABASE=Olitec, Inc. + +usb:v08E3p0002* + ID_MODEL_FROM_DATABASE=USB-RS232 Bridge + +usb:v08E3p0100* + ID_MODEL_FROM_DATABASE=Interface ADSL + +usb:v08E3p0101* + ID_MODEL_FROM_DATABASE=Interface ADSL + +usb:v08E3p0102* + ID_MODEL_FROM_DATABASE=ADSL + +usb:v08E3p0301* + ID_MODEL_FROM_DATABASE=RNIS ISDN TA [HFC-S] + +usb:v08E4* + ID_VENDOR_FROM_DATABASE=Pioneer Corp. + +usb:v08E4p0184* + ID_MODEL_FROM_DATABASE=DDJ-WeGO + +usb:v08E4p0185* + ID_MODEL_FROM_DATABASE=DDJ-WeGO2 + +usb:v08E5* + ID_VENDOR_FROM_DATABASE=Litronic + +usb:v08E6* + ID_VENDOR_FROM_DATABASE=Gemalto (was Gemplus) + +usb:v08E6p0001* + ID_MODEL_FROM_DATABASE=GemPC-Touch 430 + +usb:v08E6p0430* + ID_MODEL_FROM_DATABASE=GemPC430 SmartCard Reader + +usb:v08E6p0432* + ID_MODEL_FROM_DATABASE=GemPC432 SmartCard Reader + +usb:v08E6p0435* + ID_MODEL_FROM_DATABASE=GemPC435 SmartCard Reader + +usb:v08E6p0437* + ID_MODEL_FROM_DATABASE=GemPC433 SL SmartCard Reader + +usb:v08E6p1359* + ID_MODEL_FROM_DATABASE=UA SECURE STORAGE TOKEN + +usb:v08E6p2202* + ID_MODEL_FROM_DATABASE=Gem e-Seal Pro Token + +usb:v08E6p3437* + ID_MODEL_FROM_DATABASE=GemPC Twin SmartCard Reader + +usb:v08E6p3438* + ID_MODEL_FROM_DATABASE=GemPC Key SmartCard Reader + +usb:v08E6p3478* + ID_MODEL_FROM_DATABASE=PinPad Smart Card Reader + +usb:v08E6p34EC* + ID_MODEL_FROM_DATABASE=Compact Smart Card Reader Writer + +usb:v08E6p4433* + ID_MODEL_FROM_DATABASE=GemPC433-Swap + +usb:v08E6p5501* + ID_MODEL_FROM_DATABASE=GemProx-PU Contactless Smart Card Reader + +usb:v08E6p5503* + ID_MODEL_FROM_DATABASE=Prox-DU Contactless Interface + +usb:v08E6pACE0* + ID_MODEL_FROM_DATABASE=UA HYBRID TOKEN + +usb:v08E7* + ID_VENDOR_FROM_DATABASE=Pan-International Wire & Cable + +usb:v08E8* + ID_VENDOR_FROM_DATABASE=Integrated Memory Logic + +usb:v08E9* + ID_VENDOR_FROM_DATABASE=Extended Systems, Inc. + +usb:v08E9p0100* + ID_MODEL_FROM_DATABASE=XTNDAccess IrDA Dongle + +usb:v08EA* + ID_VENDOR_FROM_DATABASE=Ericsson, Inc., Blue Ridge Labs + +usb:v08EAp00C9* + ID_MODEL_FROM_DATABASE=ADSL Modem HM120dp Loader + +usb:v08EAp00CA* + ID_MODEL_FROM_DATABASE=ADSL WAN Modem HM120dp + +usb:v08EAp00CE* + ID_MODEL_FROM_DATABASE=HM230d Virtual Bus for Helium + +usb:v08EApABBA* + ID_MODEL_FROM_DATABASE=USB Driver for Bluetooth Wireless Technology + +usb:v08EApABBB* + ID_MODEL_FROM_DATABASE=Bluetooth Device in DFU State + +usb:v08EC* + ID_VENDOR_FROM_DATABASE=M-Systems Flash Disk Pioneers + +usb:v08ECp0001* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0002* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0005* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0008* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0010* + ID_MODEL_FROM_DATABASE=DiskOnKey + +usb:v08ECp0011* + ID_MODEL_FROM_DATABASE=DiskOnKey + +usb:v08ECp0012* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0014* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0015* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler ELITE + +usb:v08ECp0016* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler U3 + +usb:v08ECp0020* + ID_MODEL_FROM_DATABASE=TravelDrive Intuix U3 2GB + +usb:v08ECp0021* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0022* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0023* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0024* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0025* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0026* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0027* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0028* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0029* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0030* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp0822* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp0832* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v08ECp0834* + ID_MODEL_FROM_DATABASE=M-Disk 220 + +usb:v08ECp0998* + ID_MODEL_FROM_DATABASE=Kingston Data Traveler2.0 Disk Driver + +usb:v08ECp0999* + ID_MODEL_FROM_DATABASE=Kingston Data Traveler2.0 Disk Driver + +usb:v08ECp1000* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp2000* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v08ECp2038* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp2039* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp204A* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ECp204B* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v08ED* + ID_VENDOR_FROM_DATABASE=MediaTek Inc. + +usb:v08EDp0002* + ID_MODEL_FROM_DATABASE=CECT M800 memory card + +usb:v08EE* + ID_VENDOR_FROM_DATABASE=CCSI/Hesso + +usb:v08F0* + ID_VENDOR_FROM_DATABASE=Corex Technologies + +usb:v08F0p0005* + ID_MODEL_FROM_DATABASE=CardScan 800c + +usb:v08F1* + ID_VENDOR_FROM_DATABASE=CTI Electronics Corp. + +usb:v08F2* + ID_VENDOR_FROM_DATABASE=Gotop Information Inc. + +usb:v08F2p007F* + ID_MODEL_FROM_DATABASE=Super Q2 Tablet + +usb:v08F5* + ID_VENDOR_FROM_DATABASE=SysTec Co., Ltd + +usb:v08F6* + ID_VENDOR_FROM_DATABASE=Logic 3 International, Ltd + +usb:v08F7* + ID_VENDOR_FROM_DATABASE=Vernier + +usb:v08F7p0001* + ID_MODEL_FROM_DATABASE=LabPro + +usb:v08F7p0002* + ID_MODEL_FROM_DATABASE=EasyTemp/Go!Temp + +usb:v08F7p0003* + ID_MODEL_FROM_DATABASE=Go!Link + +usb:v08F7p0004* + ID_MODEL_FROM_DATABASE=Go!Motion + +usb:v08F8* + ID_VENDOR_FROM_DATABASE=Keen Top International Enterprise Co., Ltd + +usb:v08F9* + ID_VENDOR_FROM_DATABASE=Wipro Technologies + +usb:v08FA* + ID_VENDOR_FROM_DATABASE=Caere + +usb:v08FB* + ID_VENDOR_FROM_DATABASE=Socket Communications + +usb:v08FC* + ID_VENDOR_FROM_DATABASE=Sicon Cable Technology Co., Ltd + +usb:v08FD* + ID_VENDOR_FROM_DATABASE=Digianswer A/S + +usb:v08FDp0001* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v08FF* + ID_VENDOR_FROM_DATABASE=AuthenTec, Inc. + +usb:v08FFp1600* + ID_MODEL_FROM_DATABASE=AES1600 + +usb:v08FFp1610* + ID_MODEL_FROM_DATABASE=AES1600 + +usb:v08FFp1660* + ID_MODEL_FROM_DATABASE=AES1660 Fingerprint Sensor + +usb:v08FFp1680* + ID_MODEL_FROM_DATABASE=AES1660 Fingerprint Sensor + +usb:v08FFp168F* + ID_MODEL_FROM_DATABASE=AES1660 Fingerprint Sensor + +usb:v08FFp2500* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2501* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2502* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2503* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2504* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2505* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2506* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2507* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2508* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2509* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250A* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250B* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250C* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250D* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250E* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp250F* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2510* + ID_MODEL_FROM_DATABASE=AES2510 + +usb:v08FFp2550* + ID_MODEL_FROM_DATABASE=AES2550 Fingerprint Sensor + +usb:v08FFp2580* + ID_MODEL_FROM_DATABASE=AES2501 Fingerprint Sensor + +usb:v08FFp2588* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2589* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258A* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258B* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258C* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258D* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258E* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp258F* + ID_MODEL_FROM_DATABASE=AES2501 + +usb:v08FFp2660* + ID_MODEL_FROM_DATABASE=AES2660 Fingerprint Sensor + +usb:v08FFp2680* + ID_MODEL_FROM_DATABASE=AES2660 Fingerprint Sensor + +usb:v08FFp268F* + ID_MODEL_FROM_DATABASE=AES2660 Fingerprint Sensor + +usb:v08FFp2810* + ID_MODEL_FROM_DATABASE=AES2810 + +usb:v08FFp3400* + ID_MODEL_FROM_DATABASE=AES3400 TruePrint Sensor + +usb:v08FFp3401* + ID_MODEL_FROM_DATABASE=AES3400 Sensor + +usb:v08FFp3402* + ID_MODEL_FROM_DATABASE=AES3400 Sensor + +usb:v08FFp3403* + ID_MODEL_FROM_DATABASE=AES3400 Sensor + +usb:v08FFp3404* + ID_MODEL_FROM_DATABASE=AES3400 TruePrint Sensor + +usb:v08FFp3405* + ID_MODEL_FROM_DATABASE=AES3400 TruePrint Sensor + +usb:v08FFp3406* + ID_MODEL_FROM_DATABASE=AES3400 TruePrint Sensor + +usb:v08FFp3407* + ID_MODEL_FROM_DATABASE=AES3400 TruePrint Sensor + +usb:v08FFp4902* + ID_MODEL_FROM_DATABASE=BioMV with TruePrint AES3500 + +usb:v08FFp4903* + ID_MODEL_FROM_DATABASE=BioMV with TruePrint AES3400 + +usb:v08FFp5500* + ID_MODEL_FROM_DATABASE=AES4000 + +usb:v08FFp5501* + ID_MODEL_FROM_DATABASE=AES4000 TruePrint Sensor + +usb:v08FFp5503* + ID_MODEL_FROM_DATABASE=AES4000 TruePrint Sensor + +usb:v08FFp5505* + ID_MODEL_FROM_DATABASE=AES4000 TruePrint Sensor + +usb:v08FFp5507* + ID_MODEL_FROM_DATABASE=AES4000 TruePrint Sensor + +usb:v08FFp55FF* + ID_MODEL_FROM_DATABASE=AES4000 TruePrint Sensor. + +usb:v08FFp5700* + ID_MODEL_FROM_DATABASE=AES3500 Fingerprint Reader + +usb:v08FFp5701* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5702* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5703* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5704* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5705* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5706* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5707* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5710* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5711* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5712* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5713* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5714* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5715* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5716* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5717* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5730* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5731* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5732* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5733* + ID_MODEL_FROM_DATABASE=AES3500 TruePrint Sensor + +usb:v08FFp5734* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5735* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5736* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFp5737* + ID_MODEL_FROM_DATABASE=AES3500-BZ TruePrint Sensor + +usb:v08FFpAFE3* + ID_MODEL_FROM_DATABASE=FingerLoc Sensor Module (Anchor) + +usb:v08FFpAFE4* + ID_MODEL_FROM_DATABASE=FingerLoc Sensor Module (Anchor) + +usb:v08FFpAFE5* + ID_MODEL_FROM_DATABASE=FingerLoc Sensor Module (Anchor) + +usb:v08FFpAFE6* + ID_MODEL_FROM_DATABASE=FingerLoc Sensor Module (Anchor) + +usb:v08FFpFFFD* + ID_MODEL_FROM_DATABASE=AES2510 Sensor (USB Emulator) + +usb:v08FFpFFFF* + ID_MODEL_FROM_DATABASE=Sensor (Emulator) + +usb:v0900* + ID_VENDOR_FROM_DATABASE=Pinnacle Systems, Inc. + +usb:v0901* + ID_VENDOR_FROM_DATABASE=VST Technologies + +usb:v0901p0001* + ID_MODEL_FROM_DATABASE=Hard Drive Adapter (TPP) + +usb:v0901p0002* + ID_MODEL_FROM_DATABASE=SigmaDrive Adapter (TPP) + +usb:v0906* + ID_VENDOR_FROM_DATABASE=Faraday Technology Corp. + +usb:v0908* + ID_VENDOR_FROM_DATABASE=Siemens AG + +usb:v0908p01F4* + ID_MODEL_FROM_DATABASE=SIMATIC NET CP 5711 + +usb:v0908p01FE* + ID_MODEL_FROM_DATABASE=SIMATIC NET PC Adapter A2 + +usb:v0908p04B1* + ID_MODEL_FROM_DATABASE=MediSET + +usb:v0908p04B2* + ID_MODEL_FROM_DATABASE=NC interface + +usb:v0908p2701* + ID_MODEL_FROM_DATABASE=ShenZhen SANZHAI Technology Co.,Ltd Spy Pen VGA + +usb:v0909* + ID_VENDOR_FROM_DATABASE=Audio-Technica Corp. + +usb:v090A* + ID_VENDOR_FROM_DATABASE=Trumpion Microelectronics, Inc. + +usb:v090Ap1001* + ID_MODEL_FROM_DATABASE=T33520 Flash Card Controller + +usb:v090Ap1100* + ID_MODEL_FROM_DATABASE=Comotron C3310 MP3 player + +usb:v090Ap1200* + ID_MODEL_FROM_DATABASE=MP3 player + +usb:v090Ap1540* + ID_MODEL_FROM_DATABASE=Digitex Container Flash Disk + +usb:v090B* + ID_VENDOR_FROM_DATABASE=Neurosmith + +usb:v090C* + ID_VENDOR_FROM_DATABASE=Silicon Motion, Inc. - Taiwan (formerly Feiya Technology Corp.) + +usb:v090Cp0371* + ID_MODEL_FROM_DATABASE=Silicon Motion SM371 Camera + +usb:v090Cp0373* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp037A* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp037B* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp037C* + ID_MODEL_FROM_DATABASE=300k Pixel Camera + +usb:v090Cp1000* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v090Cp1132* + ID_MODEL_FROM_DATABASE=5-in-1 Card Reader + +usb:v090Cp337B* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp3710* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp3720* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp37BC* + ID_MODEL_FROM_DATABASE=HP Webcam-101 Integrated Camera + +usb:v090Cp37C0* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp6000* + ID_MODEL_FROM_DATABASE=SD/SDHC Card Reader (SG365 / FlexiDrive XC+) + +usb:v090Cp6200* + ID_MODEL_FROM_DATABASE=microSD card reader + +usb:v090Cp71B3* + ID_MODEL_FROM_DATABASE=SM731 Camera + +usb:v090Cp837B* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090Cp937B* + ID_MODEL_FROM_DATABASE=Silicon Motion Camera + +usb:v090CpB370* + ID_MODEL_FROM_DATABASE=Silicon Motion SM370 Camera + +usb:v090CpB371* + ID_MODEL_FROM_DATABASE=Silicon Motion SM371 Camera + +usb:v090CpF37D* + ID_MODEL_FROM_DATABASE=Endoscope camera + +usb:v090D* + ID_VENDOR_FROM_DATABASE=Multiport Computer Vertriebs GmbH + +usb:v090E* + ID_VENDOR_FROM_DATABASE=Shining Technology, Inc. + +usb:v090F* + ID_VENDOR_FROM_DATABASE=Fujitsu Devices, Inc. + +usb:v0910* + ID_VENDOR_FROM_DATABASE=Alation Systems, Inc. + +usb:v0911* + ID_VENDOR_FROM_DATABASE=Philips Speech Processing + +usb:v0911p149A* + ID_MODEL_FROM_DATABASE=SpeechMike II Pro Plus LFH5276 + +usb:v0911p2512* + ID_MODEL_FROM_DATABASE=SpeechMike Pro + +usb:v0912* + ID_VENDOR_FROM_DATABASE=Voquette, Inc. + +usb:v0915* + ID_VENDOR_FROM_DATABASE=GlobeSpan, Inc. + +usb:v0915p0001* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0915p0002* + ID_MODEL_FROM_DATABASE=ADSL ATM Modem + +usb:v0915p0005* + ID_MODEL_FROM_DATABASE=LAN Modem + +usb:v0915p2000* + ID_MODEL_FROM_DATABASE=802.11 Adapter + +usb:v0915p2002* + ID_MODEL_FROM_DATABASE=802.11 Adapter + +usb:v0915p8000* + ID_MODEL_FROM_DATABASE=ADSL LAN Modem + +usb:v0915p8005* + ID_MODEL_FROM_DATABASE=DSL-302G Modem + +usb:v0915p8101* + ID_MODEL_FROM_DATABASE=ADSL WAN Modem + +usb:v0915p8102* + ID_MODEL_FROM_DATABASE=DSL-200 ADSL Modem + +usb:v0915p8103* + ID_MODEL_FROM_DATABASE=DSL-200 ADSL Modem + +usb:v0915p8104* + ID_MODEL_FROM_DATABASE=DSL-200 Modem + +usb:v0915p8400* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0915p8401* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0915p8402* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0915p8500* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0915p8501* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v0917* + ID_VENDOR_FROM_DATABASE=SmartDisk Corp. + +usb:v0917p0001* + ID_MODEL_FROM_DATABASE=eFilm Reader-11 SM/CF + +usb:v0917p0002* + ID_MODEL_FROM_DATABASE=eFilm Reader-11 SM + +usb:v0917p0003* + ID_MODEL_FROM_DATABASE=eFilm Reader-11 CF + +usb:v0917p0200* + ID_MODEL_FROM_DATABASE=FireFly + +usb:v0917p0201* + ID_MODEL_FROM_DATABASE=FireLite + +usb:v0917p0202* + ID_MODEL_FROM_DATABASE=STORAGE ADAPTER (FirePower) + +usb:v0917p0204* + ID_MODEL_FROM_DATABASE=FlashTrax Storage + +usb:v0917p0205* + ID_MODEL_FROM_DATABASE=STORAGE ADAPTER (CrossFire) + +usb:v0917p0206* + ID_MODEL_FROM_DATABASE=FireFly 20G HDD + +usb:v0917p0207* + ID_MODEL_FROM_DATABASE=FireLite + +usb:v0917p020F* + ID_MODEL_FROM_DATABASE=STORAGE ADAPTER (FireLite) + +usb:v0917pDA01* + ID_MODEL_FROM_DATABASE=eFilm Reader-11 Test + +usb:v0917pFFFF* + ID_MODEL_FROM_DATABASE=eFilm Reader-11 (Class/PDR) + +usb:v0919* + ID_VENDOR_FROM_DATABASE=Tiger Electronics + +usb:v0919p0100* + ID_MODEL_FROM_DATABASE=Fast Flicks Digital Camera + +usb:v091E* + ID_VENDOR_FROM_DATABASE=Garmin International + +usb:v091Ep0003* + ID_MODEL_FROM_DATABASE=GPS (various models) + +usb:v091Ep0004* + ID_MODEL_FROM_DATABASE=iQue 3600 + +usb:v091Ep0200* + ID_MODEL_FROM_DATABASE=Data Card Programmer (install) + +usb:v091Ep1200* + ID_MODEL_FROM_DATABASE=Data Card Programmer + +usb:v091Ep21A5* + ID_MODEL_FROM_DATABASE=etrex Cx (msc) + +usb:v091Ep2236* + ID_MODEL_FROM_DATABASE=nuvi 360 + +usb:v091Ep2271* + ID_MODEL_FROM_DATABASE=Edge 605/705 + +usb:v091Ep2295* + ID_MODEL_FROM_DATABASE=Colorado 300 + +usb:v091Ep22B6* + ID_MODEL_FROM_DATABASE=eTrex Vista HCx (Mass Storage mode) + +usb:v091Ep231B* + ID_MODEL_FROM_DATABASE=Oregon 400t + +usb:v091Ep2353* + ID_MODEL_FROM_DATABASE=Nüvi 205T + +usb:v091Ep2380* + ID_MODEL_FROM_DATABASE=Oregon series + +usb:v091Ep23CC* + ID_MODEL_FROM_DATABASE=nüvi 1350 + +usb:v091Ep2459* + ID_MODEL_FROM_DATABASE=GPSmap 62/78 series + +usb:v091Ep2491* + ID_MODEL_FROM_DATABASE=Edge 800 + +usb:v091Ep2519* + ID_MODEL_FROM_DATABASE=eTrex 30 + +usb:v091Ep2535* + ID_MODEL_FROM_DATABASE=Edge 800 + +usb:v091Ep253C* + ID_MODEL_FROM_DATABASE=GPSmap 62sc + +usb:v091Ep255B* + ID_MODEL_FROM_DATABASE=Nuvi 2505LM + +usb:v091Ep26A1* + ID_MODEL_FROM_DATABASE=Nuvi 55 + +usb:v0920* + ID_VENDOR_FROM_DATABASE=Echelon Co. + +usb:v0920p7500* + ID_MODEL_FROM_DATABASE=Network Interface + +usb:v0921* + ID_VENDOR_FROM_DATABASE=GoHubs, Inc. + +usb:v0921p1001* + ID_MODEL_FROM_DATABASE=GoCOM232 Serial + +usb:v0922* + ID_VENDOR_FROM_DATABASE=Dymo-CoStar Corp. + +usb:v0922p0007* + ID_MODEL_FROM_DATABASE=LabelWriter 330 + +usb:v0922p0009* + ID_MODEL_FROM_DATABASE=LabelWriter 310 + +usb:v0922p0019* + ID_MODEL_FROM_DATABASE=LabelWriter 400 + +usb:v0922p001A* + ID_MODEL_FROM_DATABASE=LabelWriter 400 Turbo + +usb:v0922p0020* + ID_MODEL_FROM_DATABASE=LabelWriter 450 + +usb:v0922p1001* + ID_MODEL_FROM_DATABASE=LabelManager PnP + +usb:v0922p8004* + ID_MODEL_FROM_DATABASE=M25 Digital Postal Scale + +usb:v0923* + ID_VENDOR_FROM_DATABASE=IC Media Corp. + +usb:v0923p010F* + ID_MODEL_FROM_DATABASE=SIIG MobileCam + +usb:v0924* + ID_VENDOR_FROM_DATABASE=Xerox + +usb:v0924p23DD* + ID_MODEL_FROM_DATABASE=DocuPrint M760 (X760_USB) + +usb:v0924p3CE8* + ID_MODEL_FROM_DATABASE=Phaser 3428 Printer + +usb:v0924p3CEA* + ID_MODEL_FROM_DATABASE=Phaser 3125 + +usb:v0924p3D5B* + ID_MODEL_FROM_DATABASE=Phaser 6115MFP TWAIN Scanner + +usb:v0924p3D6D* + ID_MODEL_FROM_DATABASE=WorkCentre 6015N/NI + +usb:v0924p420F* + ID_MODEL_FROM_DATABASE=WorkCentre PE220 Series + +usb:v0924p421F* + ID_MODEL_FROM_DATABASE=M20 Scanner + +usb:v0924p423B* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v0924p4274* + ID_MODEL_FROM_DATABASE=Xerox Phaser 3635MFPX + +usb:v0924pFFEF* + ID_MODEL_FROM_DATABASE=WorkCenter M15 + +usb:v0924pFFFB* + ID_MODEL_FROM_DATABASE=DocuPrint M750 (X750_USB) + +usb:v0925* + ID_VENDOR_FROM_DATABASE=Lakeview Research + +usb:v0925p0005* + ID_MODEL_FROM_DATABASE=Gamtec.,Ltd SmartJoy PLUS Adapter + +usb:v0925p03E8* + ID_MODEL_FROM_DATABASE=Wii Classic Controller Adapter + +usb:v0925p3881* + ID_MODEL_FROM_DATABASE=Saleae Logic + +usb:v0925p8101* + ID_MODEL_FROM_DATABASE=Phidgets, Inc., 1-Motor PhidgetServo v2.0 + +usb:v0925p8104* + ID_MODEL_FROM_DATABASE=Phidgets, Inc., 4-Motor PhidgetServo v2.0 + +usb:v0925p8800* + ID_MODEL_FROM_DATABASE=WiseGroup Ltd, MP-8800 Quad Joypad + +usb:v0925p8866* + ID_MODEL_FROM_DATABASE=WiseGroup Ltd, MP-8866 Dual Joypad + +usb:v0927* + ID_VENDOR_FROM_DATABASE=Summus, Ltd + +usb:v0928* + ID_VENDOR_FROM_DATABASE=PLX Technology, Inc. (formerly Oxford Semiconductor, Ltd) + +usb:v0928p8000* + ID_MODEL_FROM_DATABASE=Firmware uploader + +usb:v0928pFFFF* + ID_MODEL_FROM_DATABASE=Blank Oxford Device + +usb:v0929* + ID_VENDOR_FROM_DATABASE=American Biometric Co. + +usb:v092A* + ID_VENDOR_FROM_DATABASE=Toshiba Information & Industrial Sys. And Services + +usb:v092B* + ID_VENDOR_FROM_DATABASE=Sena Technologies, Inc. + +usb:v092F* + ID_VENDOR_FROM_DATABASE=Northern Embedded Science/CAVNEX + +usb:v092Fp0004* + ID_MODEL_FROM_DATABASE=JTAG-4 + +usb:v092Fp0005* + ID_MODEL_FROM_DATABASE=JTAG-5 + +usb:v0930* + ID_VENDOR_FROM_DATABASE=Toshiba Corp. + +usb:v0930p0009* + ID_MODEL_FROM_DATABASE=Gigabeat F/X (HDD audio player) + +usb:v0930p000C* + ID_MODEL_FROM_DATABASE=Gigabeat F (mtp) + +usb:v0930p0010* + ID_MODEL_FROM_DATABASE=Gigabeat S (mtp) + +usb:v0930p0200* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth (Taiyo Yuden) + +usb:v0930p021C* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v0930p0301* + ID_MODEL_FROM_DATABASE=PCX1100U Cable Modem (WDM) + +usb:v0930p0302* + ID_MODEL_FROM_DATABASE=PCX2000 Cable Modem (WDM) + +usb:v0930p0305* + ID_MODEL_FROM_DATABASE=Cable Modem PCX3000 + +usb:v0930p0307* + ID_MODEL_FROM_DATABASE=Cable Modem PCX2500 + +usb:v0930p0308* + ID_MODEL_FROM_DATABASE=PCX2200 Cable Modem (WDM) + +usb:v0930p0309* + ID_MODEL_FROM_DATABASE=PCX5000 Cable Modem (WDM) + +usb:v0930p030B* + ID_MODEL_FROM_DATABASE=Cable Modem PCX2600 + +usb:v0930p0501* + ID_MODEL_FROM_DATABASE=Bluetooth Controller + +usb:v0930p0502* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth + +usb:v0930p0503* + ID_MODEL_FROM_DATABASE=Bluetooth Controller + +usb:v0930p0505* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth + +usb:v0930p0506* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth + +usb:v0930p0507* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v0930p0508* + ID_MODEL_FROM_DATABASE=Integrated Bluetooth HCI + +usb:v0930p0509* + ID_MODEL_FROM_DATABASE=BT EDR Dongle + +usb:v0930p0706* + ID_MODEL_FROM_DATABASE=PocketPC e740 + +usb:v0930p0707* + ID_MODEL_FROM_DATABASE=Pocket PC e330 Series + +usb:v0930p0708* + ID_MODEL_FROM_DATABASE=Pocket PC e350 Series + +usb:v0930p0709* + ID_MODEL_FROM_DATABASE=Pocket PC e750 Series + +usb:v0930p070A* + ID_MODEL_FROM_DATABASE=Pocket PC e400 Series + +usb:v0930p070B* + ID_MODEL_FROM_DATABASE=Pocket PC e800 Series + +usb:v0930p0A07* + ID_MODEL_FROM_DATABASE=WLM-10U1 802.11abgn Wireless Adapter [Ralink RT3572] + +usb:v0930p0A08* + ID_MODEL_FROM_DATABASE=WLM-20U2/GN-1080 802.11abgn Wireless Adapter [Atheros AR7010+AR9280] + +usb:v0930p0A13* + ID_MODEL_FROM_DATABASE=AX88179 Gigabit Ethernet [Toshiba] + +usb:v0930p0B05* + ID_MODEL_FROM_DATABASE=PX1220E-1G25 External hard drive + +usb:v0930p0B09* + ID_MODEL_FROM_DATABASE=PX1396E-3T01 External hard drive + +usb:v0930p0B1A* + ID_MODEL_FROM_DATABASE=STOR.E ALU 2S + +usb:v0930p1300* + ID_MODEL_FROM_DATABASE=Wireless Broadband (CDMA EV-DO) SM-Bus Minicard Status Port + +usb:v0930p1301* + ID_MODEL_FROM_DATABASE=Wireless Broadband (CDMA EV-DO) Minicard Status Port + +usb:v0930p1302* + ID_MODEL_FROM_DATABASE=Wireless Broadband (3G HSDPA) SM-Bus Minicard Status Port + +usb:v0930p1303* + ID_MODEL_FROM_DATABASE=Wireless Broadband (3G HSDPA) Minicard Status Port + +usb:v0930p1308* + ID_MODEL_FROM_DATABASE=Broadband (3G HSDPA) SM-Bus Minicard Diagnostics Port + +usb:v0930p130B* + ID_MODEL_FROM_DATABASE=F3507g Mobile Broadband Module + +usb:v0930p130C* + ID_MODEL_FROM_DATABASE=F3607gw Mobile Broadband Module + +usb:v0930p1311* + ID_MODEL_FROM_DATABASE=F3607gw v2 Mobile Broadband Module + +usb:v0930p1400* + ID_MODEL_FROM_DATABASE=Memory Stick 2GB + +usb:v0930p642F* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6506* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6507* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6508* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6509* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6510* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6517* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6518* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6519* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler 2.0 USB Stick + +usb:v0930p651A* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p651B* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p651C* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p651D* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p651E* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p651F* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6520* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6521* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6522* + ID_MODEL_FROM_DATABASE=TravelDrive 2C + +usb:v0930p6523* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6524* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6525* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6526* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6527* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6528* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6529* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p652A* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p652B* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p652C* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p652D* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p652F* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6530* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6531* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p6532* + ID_MODEL_FROM_DATABASE=256M Stick + +usb:v0930p6533* + ID_MODEL_FROM_DATABASE=512M Stick + +usb:v0930p6534* + ID_MODEL_FROM_DATABASE=TravelDrive + +usb:v0930p653C* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler 2.0 Stick (512M) + +usb:v0930p653D* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler 2.0 Stick (1GB) + +usb:v0930p653E* + ID_MODEL_FROM_DATABASE=Flash Memory + +usb:v0930p6540* + ID_MODEL_FROM_DATABASE=TransMemory Flash Memory + +usb:v0930p6544* + ID_MODEL_FROM_DATABASE=TransMemory-Mini / Kingston DataTraveler 2.0 Stick + +usb:v0930p6545* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler 102/2.0 / HEMA Flash Drive 2 GB / PNY Attache 4GB Stick + +usb:v0931* + ID_VENDOR_FROM_DATABASE=Harmonic Data Systems, Ltd + +usb:v0932* + ID_VENDOR_FROM_DATABASE=Crescentec Corp. + +usb:v0932p0300* + ID_MODEL_FROM_DATABASE=VideoAdvantage + +usb:v0932p0302* + ID_MODEL_FROM_DATABASE=Syntek DC-112X + +usb:v0932p0320* + ID_MODEL_FROM_DATABASE=VideoAdvantage + +usb:v0932p0482* + ID_MODEL_FROM_DATABASE=USB2.0 TVBOX + +usb:v0932p1100* + ID_MODEL_FROM_DATABASE=DC-1100 Video Enhamcement Device + +usb:v0932p1112* + ID_MODEL_FROM_DATABASE=Veo Web Camera + +usb:v0932pA311* + ID_MODEL_FROM_DATABASE=Video Enhancement Device + +usb:v0933* + ID_VENDOR_FROM_DATABASE=Quantum Corp. + +usb:v0934* + ID_VENDOR_FROM_DATABASE=Spirent Communications + +usb:v0936* + ID_VENDOR_FROM_DATABASE=NuTesla + +usb:v0936p000C* + ID_MODEL_FROM_DATABASE=Rhythmedics 6 BioData Integrator + +usb:v0936p0030* + ID_MODEL_FROM_DATABASE=Composite Device, Mass Storage Device (Flash Drive) amd HID + +usb:v0936p003C* + ID_MODEL_FROM_DATABASE=Rhythmedics HID Bootloader + +usb:v0939* + ID_VENDOR_FROM_DATABASE=Lumberg, Inc. + +usb:v0939p0B15* + ID_MODEL_FROM_DATABASE=Toshiba Stor.E Alu 2 + +usb:v093A* + ID_VENDOR_FROM_DATABASE=Pixart Imaging, Inc. + +usb:v093Ap0007* + ID_MODEL_FROM_DATABASE=CMOS 100K-R Rev. 1.90 + +usb:v093Ap010E* + ID_MODEL_FROM_DATABASE=Digital camera, CD302N/Elta Medi@ digi-cam/HE-501A + +usb:v093Ap010F* + ID_MODEL_FROM_DATABASE=Argus DC-1610/DC-1620/Emprex PCD3600/Philips P44417B keychain camera/Precision Mini,Model HA513A/Vivitar Vivicam 55 + +usb:v093Ap020F* + ID_MODEL_FROM_DATABASE=Bullet Line Photo Viewer + +usb:v093Ap050F* + ID_MODEL_FROM_DATABASE=Mars-Semi Pc-Camera + +usb:v093Ap2460* + ID_MODEL_FROM_DATABASE=Q-TEC WEBCAM 100 + +usb:v093Ap2468* + ID_MODEL_FROM_DATABASE=SoC PC-Camera + +usb:v093Ap2470* + ID_MODEL_FROM_DATABASE=SoC PC-Camera + +usb:v093Ap2471* + ID_MODEL_FROM_DATABASE=SoC PC-Camera + +usb:v093Ap2500* + ID_MODEL_FROM_DATABASE=USB Optical Mouse + +usb:v093Ap2510* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v093Ap2521* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v093Ap2600* + ID_MODEL_FROM_DATABASE=Typhoon Easycam USB 330K (newer)/Typhoon Easycam USB 2.0 VGA 1.3M/Sansun SN-508 + +usb:v093Ap2601* + ID_MODEL_FROM_DATABASE=SPC 610NC Laptop Camera + +usb:v093Ap2603* + ID_MODEL_FROM_DATABASE=PAC7312 Camera + +usb:v093Ap2608* + ID_MODEL_FROM_DATABASE=PAC7311 Trust WB-3300p + +usb:v093Ap260E* + ID_MODEL_FROM_DATABASE=PAC7311 Gigaware VGA PC Camera:Trust WB-3350p:SIGMA cam 2350 + +usb:v093Ap260F* + ID_MODEL_FROM_DATABASE=PAC7311 SnakeCam + +usb:v093Ap2621* + ID_MODEL_FROM_DATABASE=PAC731x Trust Webcam + +usb:v093Ap2622* + ID_MODEL_FROM_DATABASE=Webcam Genius + +usb:v093Ap2624* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v093B* + ID_VENDOR_FROM_DATABASE=Plextor Corp. + +usb:v093Bp0010* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v093Bp0011* + ID_MODEL_FROM_DATABASE=PlexWriter 40/12/40U + +usb:v093Bp0041* + ID_MODEL_FROM_DATABASE=PX-708A DVD RW + +usb:v093Bp0042* + ID_MODEL_FROM_DATABASE=PX-712UF DVD RW + +usb:v093BpA002* + ID_MODEL_FROM_DATABASE=ConvertX M402U XLOADER + +usb:v093BpA003* + ID_MODEL_FROM_DATABASE=ConvertX AV100U A/V Capture Audio + +usb:v093BpA004* + ID_MODEL_FROM_DATABASE=ConvertX TV402U XLOADER + +usb:v093BpA005* + ID_MODEL_FROM_DATABASE=ConvertX TV100U A/V Capture + +usb:v093BpA102* + ID_MODEL_FROM_DATABASE=ConvertX M402U A/V Capture + +usb:v093BpA104* + ID_MODEL_FROM_DATABASE=ConvertX PX-TV402U/NA + +usb:v093C* + ID_VENDOR_FROM_DATABASE=Intrepid Control Systems, Inc. + +usb:v093Cp0601* + ID_MODEL_FROM_DATABASE=ValueCAN + +usb:v093Cp0701* + ID_MODEL_FROM_DATABASE=NeoVI Blue vehicle bus interface + +usb:v093D* + ID_VENDOR_FROM_DATABASE=InnoSync, Inc. + +usb:v093E* + ID_VENDOR_FROM_DATABASE=J.S.T. Mfg. Co., Ltd + +usb:v093F* + ID_VENDOR_FROM_DATABASE=Olympia Telecom Vertriebs GmbH + +usb:v0940* + ID_VENDOR_FROM_DATABASE=Japan Storage Battery Co., Ltd + +usb:v0941* + ID_VENDOR_FROM_DATABASE=Photobit Corp. + +usb:v0942* + ID_VENDOR_FROM_DATABASE=i2Go.com, LLC + +usb:v0943* + ID_VENDOR_FROM_DATABASE=HCL Technologies India Private, Ltd + +usb:v0944* + ID_VENDOR_FROM_DATABASE=KORG, Inc. + +usb:v0944p0001* + ID_MODEL_FROM_DATABASE=PXR4 4-Track Digital Recorder + +usb:v0944p0020* + ID_MODEL_FROM_DATABASE=KAOSS Pad KP3 Dynamic Effect/Sampler + +usb:v0944p0023* + ID_MODEL_FROM_DATABASE=KAOSSILATOR PRO Dynamic Phrase Synthesizer + +usb:v0944p010D* + ID_MODEL_FROM_DATABASE=nanoKEY MIDI keyboard + +usb:v0944p010E* + ID_MODEL_FROM_DATABASE=nanoPAD pad controller + +usb:v0944p010F* + ID_MODEL_FROM_DATABASE=nanoKONTROL studio controller + +usb:v0944p0117* + ID_MODEL_FROM_DATABASE=nanoKONTROL2 MIDI Controller + +usb:v0944p0F03* + ID_MODEL_FROM_DATABASE=K-Series K61P MIDI studio controller + +usb:v0945* + ID_VENDOR_FROM_DATABASE=Pasco Scientific + +usb:v0948* + ID_VENDOR_FROM_DATABASE=Kronauer music in digital + +usb:v0948p0301* + ID_MODEL_FROM_DATABASE=USB Pro (24/48) + +usb:v0948p0302* + ID_MODEL_FROM_DATABASE=USB Pro (24/96 playback) + +usb:v0948p0303* + ID_MODEL_FROM_DATABASE=USB Pro (24/96 record) + +usb:v0948p0304* + ID_MODEL_FROM_DATABASE=USB Pro (16/48) + +usb:v0948p1105* + ID_MODEL_FROM_DATABASE=USB One + +usb:v094B* + ID_VENDOR_FROM_DATABASE=Linkup Systems Corp. + +usb:v094Bp0001* + ID_MODEL_FROM_DATABASE=neonode N2 + +usb:v094D* + ID_VENDOR_FROM_DATABASE=Cable Television Laboratories + +usb:v094F* + ID_VENDOR_FROM_DATABASE=Yano + +usb:v094Fp0101* + ID_MODEL_FROM_DATABASE=U640MO-03 + +usb:v094Fp05FC* + ID_MODEL_FROM_DATABASE=METALWEAR-HDD + +usb:v0951* + ID_VENDOR_FROM_DATABASE=Kingston Technology + +usb:v0951p0008* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v0951p000A* + ID_MODEL_FROM_DATABASE=KNU101TX 100baseTX Ethernet + +usb:v0951p1600* + ID_MODEL_FROM_DATABASE=DataTraveler II Pen Drive + +usb:v0951p1601* + ID_MODEL_FROM_DATABASE=DataTraveler II+ Pen Drive + +usb:v0951p1602* + ID_MODEL_FROM_DATABASE=DataTraveler Mini + +usb:v0951p1603* + ID_MODEL_FROM_DATABASE=DataTraveler 1GB/2GB Pen Drive + +usb:v0951p1606* + ID_MODEL_FROM_DATABASE=Eee PC 701 SD Card Reader [ENE UB6225] + +usb:v0951p1607* + ID_MODEL_FROM_DATABASE=DataTraveler 100 + +usb:v0951p160B* + ID_MODEL_FROM_DATABASE=DataTraveler 2.0 (2GB) + +usb:v0951p160D* + ID_MODEL_FROM_DATABASE=DataTraveler Vault Privacy + +usb:v0951p160E* + ID_MODEL_FROM_DATABASE=DT110P/1GB Capless + +usb:v0951p1613* + ID_MODEL_FROM_DATABASE=DataTraveler DT101C Flash Drive + +usb:v0951p1616* + ID_MODEL_FROM_DATABASE=DataTraveler Locker 4GB + +usb:v0951p161A* + ID_MODEL_FROM_DATABASE=Dell HyperVisor internal flash drive + +usb:v0951p1621* + ID_MODEL_FROM_DATABASE=DataTraveler 150 (32GB) + +usb:v0951p1624* + ID_MODEL_FROM_DATABASE=DataTraveler G2 + +usb:v0951p1625* + ID_MODEL_FROM_DATABASE=DataTraveler 101 II + +usb:v0951p162A* + ID_MODEL_FROM_DATABASE=DataTraveler 112 4GB Pen Drive + +usb:v0951p162D* + ID_MODEL_FROM_DATABASE=DataTraveler 102 + +usb:v0951p1630* + ID_MODEL_FROM_DATABASE=DataTraveler 200 (32GB) + +usb:v0951p1642* + ID_MODEL_FROM_DATABASE=DT101 G2 + +usb:v0951p1643* + ID_MODEL_FROM_DATABASE=DataTraveler G3 + +usb:v0951p1653* + ID_MODEL_FROM_DATABASE=Data Traveler 100 G2 8 GiB + +usb:v0951p1656* + ID_MODEL_FROM_DATABASE=DataTraveler Ultimate G2 + +usb:v0951p1660* + ID_MODEL_FROM_DATABASE=Data Traveller 108 + +usb:v0951p1665* + ID_MODEL_FROM_DATABASE=Digital DataTraveler SE9 64GB + +usb:v0951p1666* + ID_MODEL_FROM_DATABASE=DataTraveler 100 G3/G4/SE9 G2 + +usb:v0951p1689* + ID_MODEL_FROM_DATABASE=DataTraveler SE9 + +usb:v0951p168A* + ID_MODEL_FROM_DATABASE=DataTraveler Micro + +usb:v0951p168C* + ID_MODEL_FROM_DATABASE=DT Elite 3.0 + +usb:v0954* + ID_VENDOR_FROM_DATABASE=RPM Systems Corp. + +usb:v0955* + ID_VENDOR_FROM_DATABASE=NVidia Corp. + +usb:v0955p7030* + ID_MODEL_FROM_DATABASE=Tegra 3 (recovery mode) + +usb:v0955p7100* + ID_MODEL_FROM_DATABASE=Tegra Device + +usb:v0955p7820* + ID_MODEL_FROM_DATABASE=Tegra 2 AC100 developer mode + +usb:v0955pB400* + ID_MODEL_FROM_DATABASE=SHIELD (debug) + +usb:v0955pB401* + ID_MODEL_FROM_DATABASE=SHIELD + +usb:v0955pCF05* + ID_MODEL_FROM_DATABASE=SHIELD Tablet (debug) + +usb:v0955pCF06* + ID_MODEL_FROM_DATABASE=SHIELD Tablet + +usb:v0955pCF07* + ID_MODEL_FROM_DATABASE=SHIELD Tablet + +usb:v0955pCF08* + ID_MODEL_FROM_DATABASE=SHIELD Tablet + +usb:v0955pCF09* + ID_MODEL_FROM_DATABASE=SHIELD Tablet + +usb:v0956* + ID_VENDOR_FROM_DATABASE=BSquare Corp. + +usb:v0957* + ID_VENDOR_FROM_DATABASE=Agilent Technologies, Inc. + +usb:v0957p0200* + ID_MODEL_FROM_DATABASE=E-Video DC-350 Camera + +usb:v0957p0202* + ID_MODEL_FROM_DATABASE=E-Video DC-350 Camera + +usb:v0957p0407* + ID_MODEL_FROM_DATABASE=33220A Waveform Generator + +usb:v0957p0518* + ID_MODEL_FROM_DATABASE=82357B GPIB Interface + +usb:v0957p0A07* + ID_MODEL_FROM_DATABASE=34411A Multimeter + +usb:v0957p1507* + ID_MODEL_FROM_DATABASE=33210A Waveform Generator + +usb:v0957p1745* + ID_MODEL_FROM_DATABASE=Test and Measurement Device (IVI) + +usb:v0957p2918* + ID_MODEL_FROM_DATABASE=U2702A oscilloscope + +usb:v0957pFB18* + ID_MODEL_FROM_DATABASE=LC Device + +usb:v0958* + ID_VENDOR_FROM_DATABASE=CompuLink Research, Inc. + +usb:v0959* + ID_VENDOR_FROM_DATABASE=Cologne Chip AG + +usb:v0959p2BD0* + ID_MODEL_FROM_DATABASE=Intelligent ISDN (Ver. 3.60.04) [HFC-S] + +usb:v095A* + ID_VENDOR_FROM_DATABASE=Portsmith + +usb:v095Ap3003* + ID_MODEL_FROM_DATABASE=Express Ethernet + +usb:v095B* + ID_VENDOR_FROM_DATABASE=Medialogic Corp. + +usb:v095C* + ID_VENDOR_FROM_DATABASE=K-Tec Electronics + +usb:v095D* + ID_VENDOR_FROM_DATABASE=Polycom, Inc. + +usb:v095Dp0001* + ID_MODEL_FROM_DATABASE=Polycom ViaVideo + +usb:v0967* + ID_VENDOR_FROM_DATABASE=Acer NeWeb Corp. + +usb:v0967p0204* + ID_MODEL_FROM_DATABASE=WarpLink 802.11b Adapter + +usb:v0968* + ID_VENDOR_FROM_DATABASE=Catalyst Enterprises, Inc. + +usb:v096E* + ID_VENDOR_FROM_DATABASE=Feitian Technologies, Inc. + +usb:v096Ep0120* + ID_MODEL_FROM_DATABASE=Microcosm Ltd Dinkey + +usb:v096Ep0802* + ID_MODEL_FROM_DATABASE=ePass2000 (G&D STARCOS SPK 2.4) + +usb:v096Ep0807* + ID_MODEL_FROM_DATABASE=ePass2003 + +usb:v0971* + ID_VENDOR_FROM_DATABASE=Gretag-Macbeth AG + +usb:v0971p2000* + ID_MODEL_FROM_DATABASE=i1 Pro + +usb:v0971p2001* + ID_MODEL_FROM_DATABASE=i1 Monitor + +usb:v0971p2003* + ID_MODEL_FROM_DATABASE=Eye-One display + +usb:v0971p2005* + ID_MODEL_FROM_DATABASE=Huey + +usb:v0971p2007* + ID_MODEL_FROM_DATABASE=ColorMunki Photo + +usb:v0973* + ID_VENDOR_FROM_DATABASE=Schlumberger + +usb:v0973p0001* + ID_MODEL_FROM_DATABASE=e-gate Smart Card + +usb:v0974* + ID_VENDOR_FROM_DATABASE=Datagraphix, a business unit of Anacomp + +usb:v0975* + ID_VENDOR_FROM_DATABASE=OL'E Communications, Inc. + +usb:v0976* + ID_VENDOR_FROM_DATABASE=Adirondack Wire & Cable + +usb:v0977* + ID_VENDOR_FROM_DATABASE=Lightsurf Technologies + +usb:v0978* + ID_VENDOR_FROM_DATABASE=Beckhoff GmbH + +usb:v0979* + ID_VENDOR_FROM_DATABASE=Jeilin Technology Corp., Ltd + +usb:v0979p0222* + ID_MODEL_FROM_DATABASE=Keychain Display + +usb:v0979p0224* + ID_MODEL_FROM_DATABASE=JL2005A Toy Camera + +usb:v0979p0226* + ID_MODEL_FROM_DATABASE=JL2005A Toy Camera + +usb:v0979p0227* + ID_MODEL_FROM_DATABASE=JL2005B/C/D Toy Camera + +usb:v097A* + ID_VENDOR_FROM_DATABASE=Minds At Work LLC + +usb:v097Ap0001* + ID_MODEL_FROM_DATABASE=Digital Wallet + +usb:v097B* + ID_VENDOR_FROM_DATABASE=Knudsen Engineering, Ltd + +usb:v097C* + ID_VENDOR_FROM_DATABASE=Marunix Co., Ltd + +usb:v097D* + ID_VENDOR_FROM_DATABASE=Rosun Technologies, Inc. + +usb:v097E* + ID_VENDOR_FROM_DATABASE=Biopac Systems Inc. + +usb:v097Ep0035* + ID_MODEL_FROM_DATABASE=MP35 v1.0 + +usb:v097F* + ID_VENDOR_FROM_DATABASE=Barun Electronics Co., Ltd + +usb:v0981* + ID_VENDOR_FROM_DATABASE=Oak Technology, Ltd + +usb:v0984* + ID_VENDOR_FROM_DATABASE=Apricorn + +usb:v0984p0040* + ID_MODEL_FROM_DATABASE=SATA Wire (2.5") + +usb:v0984p0200* + ID_MODEL_FROM_DATABASE=Hard Drive Storage (TPP) + +usb:v0985* + ID_VENDOR_FROM_DATABASE=cab Produkttechnik GmbH & Co KG + +usb:v0985p0045* + ID_MODEL_FROM_DATABASE=Mach4/200 Label Printer + +usb:v0985p00A3* + ID_MODEL_FROM_DATABASE=A3/200 or A3/300 Label Printer + +usb:v0986* + ID_VENDOR_FROM_DATABASE=Matsushita Electric Works, Ltd. + +usb:v098C* + ID_VENDOR_FROM_DATABASE=Vitana Corp. + +usb:v098D* + ID_VENDOR_FROM_DATABASE=INDesign + +usb:v098E* + ID_VENDOR_FROM_DATABASE=Integrated Intellectual Property, Inc. + +usb:v098F* + ID_VENDOR_FROM_DATABASE=Kenwood TMI Corp. + +usb:v0993* + ID_VENDOR_FROM_DATABASE=Gemstar eBook Group, Ltd + +usb:v0993p0001* + ID_MODEL_FROM_DATABASE=REB1100 eBook Reader + +usb:v0993p0002* + ID_MODEL_FROM_DATABASE=eBook + +usb:v0996* + ID_VENDOR_FROM_DATABASE=Integrated Telecom Express, Inc. + +usb:v099A* + ID_VENDOR_FROM_DATABASE=Zippy Technology Corp. + +usb:v099Ap0638* + ID_MODEL_FROM_DATABASE=Sanwa Supply Inc. Small Keyboard + +usb:v099Ap610C* + ID_MODEL_FROM_DATABASE=EL-610 Super Mini Electron luminescent Keyboard + +usb:v099Ap713A* + ID_MODEL_FROM_DATABASE=WK-713 Multimedia Keyboard + +usb:v099Ap7160* + ID_MODEL_FROM_DATABASE=Hyper Slim Keyboard + +usb:v09A3* + ID_VENDOR_FROM_DATABASE=PairGain Technologies + +usb:v09A4* + ID_VENDOR_FROM_DATABASE=Contech Research, Inc. + +usb:v09A5* + ID_VENDOR_FROM_DATABASE=VCON Telecommunications + +usb:v09A6* + ID_VENDOR_FROM_DATABASE=Poinchips + +usb:v09A6p8001* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v09A7* + ID_VENDOR_FROM_DATABASE=Data Transmission Network Corp. + +usb:v09A8* + ID_VENDOR_FROM_DATABASE=Lin Shiung Enterprise Co., Ltd + +usb:v09A9* + ID_VENDOR_FROM_DATABASE=Smart Card Technologies Co., Ltd + +usb:v09AA* + ID_VENDOR_FROM_DATABASE=Intersil Corp. + +usb:v09AAp1000* + ID_MODEL_FROM_DATABASE=Prism GT 802.11b/g Adapter + +usb:v09AAp3642* + ID_MODEL_FROM_DATABASE=Prism 2.x 802.11b Adapter + +usb:v09AB* + ID_VENDOR_FROM_DATABASE=Japan Cash Machine Co., Ltd. + +usb:v09AE* + ID_VENDOR_FROM_DATABASE=Tripp Lite + +usb:v09B2* + ID_VENDOR_FROM_DATABASE=Franklin Electronic Publishers, Inc. + +usb:v09B2p0001* + ID_MODEL_FROM_DATABASE=eBookman Palm Computer + +usb:v09B3* + ID_VENDOR_FROM_DATABASE=Altius Solutions, Inc. + +usb:v09B4* + ID_VENDOR_FROM_DATABASE=MDS Telephone Systems + +usb:v09B5* + ID_VENDOR_FROM_DATABASE=Celltrix Technology Co., Ltd + +usb:v09BC* + ID_VENDOR_FROM_DATABASE=Grundig + +usb:v09BCp0002* + ID_MODEL_FROM_DATABASE=MPaxx MP150 MP3 Player + +usb:v09BE* + ID_VENDOR_FROM_DATABASE=MySmart.Com + +usb:v09BEp0001* + ID_MODEL_FROM_DATABASE=MySmartPad + +usb:v09BF* + ID_VENDOR_FROM_DATABASE=Auerswald GmbH & Co. KG + +usb:v09BFp00C0* + ID_MODEL_FROM_DATABASE=COMpact 2104 ISDN PBX + +usb:v09BFp00DB* + ID_MODEL_FROM_DATABASE=COMpact 4410/2206 ISDN + +usb:v09BFp00DC* + ID_MODEL_FROM_DATABASE=COMpact 4406 DSL (PBX) + +usb:v09BFp00DD* + ID_MODEL_FROM_DATABASE=COMpact 2204 (PBX) + +usb:v09BFp00DE* + ID_MODEL_FROM_DATABASE=COMpact 2104 (Rev.2 PBX) + +usb:v09BFp00E0* + ID_MODEL_FROM_DATABASE=COMmander Business (PBX) + +usb:v09BFp00E2* + ID_MODEL_FROM_DATABASE=COMmander Basic.2 (PBX) + +usb:v09BFp00F1* + ID_MODEL_FROM_DATABASE=COMfort 2000 (System telephone) + +usb:v09BFp00F2* + ID_MODEL_FROM_DATABASE=COMfort 1200 (System telephone) + +usb:v09BFp00F5* + ID_MODEL_FROM_DATABASE=COMfortel 2500 (System telephone) + +usb:v09BFp8000* + ID_MODEL_FROM_DATABASE=COMpact 2104 DSL (DSL modem) + +usb:v09BFp8001* + ID_MODEL_FROM_DATABASE=COMpact 4406 DSL (DSL modem) + +usb:v09BFp8002* + ID_MODEL_FROM_DATABASE=Analog/ISDN Converter (Line converter) + +usb:v09BFp8005* + ID_MODEL_FROM_DATABASE=WG-640 (Automatic event dialer) + +usb:v09C0* + ID_VENDOR_FROM_DATABASE=Genpix Electronics, LLC + +usb:v09C0p0136* + ID_MODEL_FROM_DATABASE=Axon CNS, MultiClamp 700B + +usb:v09C0p0202* + ID_MODEL_FROM_DATABASE=8PSK DVB-S tuner + +usb:v09C0p0203* + ID_MODEL_FROM_DATABASE=Skywalker-1 DVB-S tuner + +usb:v09C0p0204* + ID_MODEL_FROM_DATABASE=Skywalker-CW3K DVB-S tuner + +usb:v09C0p0205* + ID_MODEL_FROM_DATABASE=Skywalker-CW3K DVB-S tuner + +usb:v09C0p0206* + ID_MODEL_FROM_DATABASE=Skywalker-2 DVB-S tuner + +usb:v09C1* + ID_VENDOR_FROM_DATABASE=Arris Interactive LLC + +usb:v09C1p1337* + ID_MODEL_FROM_DATABASE=TOUCHSTONE DEVICE + +usb:v09C2* + ID_VENDOR_FROM_DATABASE=Nisca Corp. + +usb:v09C3* + ID_VENDOR_FROM_DATABASE=ActivCard, Inc. + +usb:v09C3p0007* + ID_MODEL_FROM_DATABASE=Reader V2 + +usb:v09C3p0008* + ID_MODEL_FROM_DATABASE=ZFG-9800-AC SmartCard Reader + +usb:v09C3p0014* + ID_MODEL_FROM_DATABASE=ActivIdentity ActivKey SIM USB Token + +usb:v09C4* + ID_VENDOR_FROM_DATABASE=ACTiSYS Corp. + +usb:v09C4p0011* + ID_MODEL_FROM_DATABASE=ACT-IR2000U IrDA Dongle + +usb:v09C5* + ID_VENDOR_FROM_DATABASE=Memory Corp. + +usb:v09CA* + ID_VENDOR_FROM_DATABASE=BMC Messsysteme GmbH + +usb:v09CAp5544* + ID_MODEL_FROM_DATABASE=PIO + +usb:v09CC* + ID_VENDOR_FROM_DATABASE=Workbit Corp. + +usb:v09CCp0404* + ID_MODEL_FROM_DATABASE=BAFO USB-ATA/ATAPI Bridge Controller + +usb:v09CD* + ID_VENDOR_FROM_DATABASE=Psion Dacom Home Networks, Ltd + +usb:v09CDp2001* + ID_MODEL_FROM_DATABASE=Psion WaveFinder DAB radio receiver + +usb:v09CE* + ID_VENDOR_FROM_DATABASE=City Electronics, Ltd + +usb:v09CF* + ID_VENDOR_FROM_DATABASE=Electronics Testing Center, Taiwan + +usb:v09D1* + ID_VENDOR_FROM_DATABASE=NeoMagic, Inc. + +usb:v09D2* + ID_VENDOR_FROM_DATABASE=Vreelin Engineering, Inc. + +usb:v09D3* + ID_VENDOR_FROM_DATABASE=Com One + +usb:v09D3p0001* + ID_MODEL_FROM_DATABASE=ISDN TA / Light Rider 128K + +usb:v09D3p000B* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter class 1 [BlueLight] + +usb:v09D7* + ID_VENDOR_FROM_DATABASE=Novatel Wireless + +usb:v09D7p0100* + ID_MODEL_FROM_DATABASE=NovAtel FlexPack GPS receiver + +usb:v09D9* + ID_VENDOR_FROM_DATABASE=KRF Tech, Ltd + +usb:v09DA* + ID_VENDOR_FROM_DATABASE=A4Tech Co., Ltd. + +usb:v09DAp0006* + ID_MODEL_FROM_DATABASE=Optical Mouse WOP-35 / Trust 450L Optical Mouse + +usb:v09DAp000A* + ID_MODEL_FROM_DATABASE=Optical Mouse Opto 510D / OP-620D + +usb:v09DAp000E* + ID_MODEL_FROM_DATABASE=X-F710F Optical Mouse 3xFire Gaming Mouse + +usb:v09DAp0018* + ID_MODEL_FROM_DATABASE=Trust Human Interface Device + +usb:v09DAp001A* + ID_MODEL_FROM_DATABASE=Wireless Mouse & RXM-15 Receiver + +usb:v09DAp002A* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse NB-30 + +usb:v09DAp022B* + ID_MODEL_FROM_DATABASE=Wireless Mouse (Battery Free) + +usb:v09DAp024F* + ID_MODEL_FROM_DATABASE=RF Receiver and G6-20D Wireless Optical Mouse + +usb:v09DAp0260* + ID_MODEL_FROM_DATABASE=KV-300H Isolation Keyboard + +usb:v09DAp032B* + ID_MODEL_FROM_DATABASE=Wireless Mouse (Battery Free) + +usb:v09DAp8090* + ID_MODEL_FROM_DATABASE=X-718BK Oscar Optical Gaming Mouse + +usb:v09DAp9033* + ID_MODEL_FROM_DATABASE=X-718BK Optical Mouse + +usb:v09DAp9066* + ID_MODEL_FROM_DATABASE=F3 V-Track Gaming Mouse + +usb:v09DAp9090* + ID_MODEL_FROM_DATABASE=XL-730K / XL-750BK / XL-755BK Mice + +usb:v09DB* + ID_VENDOR_FROM_DATABASE=Measurement Computing Corp. + +usb:v09DBp0075* + ID_MODEL_FROM_DATABASE=MiniLab 1008 + +usb:v09DBp0076* + ID_MODEL_FROM_DATABASE=PMD-1024 + +usb:v09DBp007A* + ID_MODEL_FROM_DATABASE=PMD-1208LS + +usb:v09DBp0081* + ID_MODEL_FROM_DATABASE=USB-1616FS + +usb:v09DBp0082* + ID_MODEL_FROM_DATABASE=USB-1208FS + +usb:v09DBp0088* + ID_MODEL_FROM_DATABASE=USB-1616FS internal hub + +usb:v09DC* + ID_VENDOR_FROM_DATABASE=Aimex Corp. + +usb:v09DD* + ID_VENDOR_FROM_DATABASE=Fellowes, Inc. + +usb:v09DF* + ID_VENDOR_FROM_DATABASE=Addonics Technologies Corp. + +usb:v09E1* + ID_VENDOR_FROM_DATABASE=Intellon Corp. + +usb:v09E1p5121* + ID_MODEL_FROM_DATABASE=MicroLink dLAN + +usb:v09E5* + ID_VENDOR_FROM_DATABASE=Jo-Dan International, Inc. + +usb:v09E6* + ID_VENDOR_FROM_DATABASE=Silutia, Inc. + +usb:v09E7* + ID_VENDOR_FROM_DATABASE=Real 3D, Inc. + +usb:v09E8* + ID_VENDOR_FROM_DATABASE=AKAI Professional M.I. Corp. + +usb:v09E8p0062* + ID_MODEL_FROM_DATABASE=MPD16 MIDI Pad Controller Unit + +usb:v09E8p006D* + ID_MODEL_FROM_DATABASE=EWI electronic wind instrument + +usb:v09E8p0071* + ID_MODEL_FROM_DATABASE=MPK25 MIDI Keyboard + +usb:v09E8p0076* + ID_MODEL_FROM_DATABASE=LPK25 MIDI Keyboard + +usb:v09E9* + ID_VENDOR_FROM_DATABASE=Chen-Source, Inc. + +usb:v09EB* + ID_VENDOR_FROM_DATABASE=IM Networks, Inc. + +usb:v09EBp4331* + ID_MODEL_FROM_DATABASE=iRhythm Tuner Remote + +usb:v09EF* + ID_VENDOR_FROM_DATABASE=Xitel + +usb:v09EFp0101* + ID_MODEL_FROM_DATABASE=MD-Port DG2 MiniDisc Interface + +usb:v09F3* + ID_VENDOR_FROM_DATABASE=GoFlight, Inc. + +usb:v09F3p0018* + ID_MODEL_FROM_DATABASE=GF-46 Multi-Mode Display Module + +usb:v09F3p0028* + ID_MODEL_FROM_DATABASE=RP-48 Combination Pushbutton-Rotary Module + +usb:v09F3p0048* + ID_MODEL_FROM_DATABASE=LGTII - Landing Gear and Trim Control Module + +usb:v09F3p0064* + ID_MODEL_FROM_DATABASE=MCPPro - Airliner Mode Control Panel (Autopilot) + +usb:v09F3p0300* + ID_MODEL_FROM_DATABASE=EFIS - Electronic Flight Information System + +usb:v09F5* + ID_VENDOR_FROM_DATABASE=AresCom + +usb:v09F5p0168* + ID_MODEL_FROM_DATABASE=Network Adapter + +usb:v09F5p0188* + ID_MODEL_FROM_DATABASE=LAN Adapter + +usb:v09F5p0850* + ID_MODEL_FROM_DATABASE=Adapter + +usb:v09F6* + ID_VENDOR_FROM_DATABASE=RocketChips, Inc. + +usb:v09F7* + ID_VENDOR_FROM_DATABASE=Edu-Science (H.K.), Ltd + +usb:v09F8* + ID_VENDOR_FROM_DATABASE=SoftConnex Technologies, Inc. + +usb:v09F9* + ID_VENDOR_FROM_DATABASE=Bay Associates + +usb:v09FA* + ID_VENDOR_FROM_DATABASE=Mtek Vision + +usb:v09FB* + ID_VENDOR_FROM_DATABASE=Altera + +usb:v09FBp6001* + ID_MODEL_FROM_DATABASE=Blaster + +usb:v09FF* + ID_VENDOR_FROM_DATABASE=Gain Technology Corp. + +usb:v0A00* + ID_VENDOR_FROM_DATABASE=Liquid Audio + +usb:v0A01* + ID_VENDOR_FROM_DATABASE=ViA, Inc. + +usb:v0A05* + ID_VENDOR_FROM_DATABASE=Unknown Manufacturer + +usb:v0A05p0001* + ID_MODEL_FROM_DATABASE=Hub + +usb:v0A05p7211* + ID_MODEL_FROM_DATABASE=hub + +usb:v0A07* + ID_VENDOR_FROM_DATABASE=Ontrak Control Systems Inc. + +usb:v0A07p0064* + ID_MODEL_FROM_DATABASE=ADU100 Data Acquisition Interface + +usb:v0A07p0078* + ID_MODEL_FROM_DATABASE=ADU120 Data Acquisition Interface + +usb:v0A07p0082* + ID_MODEL_FROM_DATABASE=ADU130 Data Acquisition Interface + +usb:v0A07p00C8* + ID_MODEL_FROM_DATABASE=ADU200 Relay I/O Interface + +usb:v0A07p00D0* + ID_MODEL_FROM_DATABASE=ADU208 Relay I/O Interface + +usb:v0A07p00DA* + ID_MODEL_FROM_DATABASE=ADU218 Solid-State Relay I/O Interface + +usb:v0A0B* + ID_VENDOR_FROM_DATABASE=Cybex Computer Products Co. + +usb:v0A0D* + ID_VENDOR_FROM_DATABASE=Servergy, Inc + +usb:v0A0Dp2514* + ID_MODEL_FROM_DATABASE=CTS-1000 Internal Hub + +usb:v0A11* + ID_VENDOR_FROM_DATABASE=Xentec, Inc. + +usb:v0A12* + ID_VENDOR_FROM_DATABASE=Cambridge Silicon Radio, Ltd + +usb:v0A12p0001* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HCI mode) + +usb:v0A12p0002* + ID_MODEL_FROM_DATABASE=Frontline Test Equipment Bluetooth Device + +usb:v0A12p0003* + ID_MODEL_FROM_DATABASE=Nanosira + +usb:v0A12p0004* + ID_MODEL_FROM_DATABASE=Nanosira WHQL Reference Radio + +usb:v0A12p0005* + ID_MODEL_FROM_DATABASE=Nanosira-Multimedia + +usb:v0A12p0006* + ID_MODEL_FROM_DATABASE=Nanosira-Multimedia WHQL Reference Radio + +usb:v0A12p0007* + ID_MODEL_FROM_DATABASE=Nanosira3-ROM + +usb:v0A12p0008* + ID_MODEL_FROM_DATABASE=Nanosira3-ROM + +usb:v0A12p0009* + ID_MODEL_FROM_DATABASE=Nanosira4-EDR WHQL Reference Radio + +usb:v0A12p000A* + ID_MODEL_FROM_DATABASE=Nanosira4-EDR-ROM + +usb:v0A12p000B* + ID_MODEL_FROM_DATABASE=Nanosira5-ROM + +usb:v0A12p0042* + ID_MODEL_FROM_DATABASE=SPI Converter + +usb:v0A12p0043* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A12p0100* + ID_MODEL_FROM_DATABASE=Casira with BlueCore2-External Module + +usb:v0A12p0101* + ID_MODEL_FROM_DATABASE=Casira with BlueCore2-Flash Module + +usb:v0A12p0102* + ID_MODEL_FROM_DATABASE=Casira with BlueCore3-Multimedia Module + +usb:v0A12p0103* + ID_MODEL_FROM_DATABASE=Casira with BlueCore3-Flash Module + +usb:v0A12p0104* + ID_MODEL_FROM_DATABASE=Casira with BlueCore4-External Module + +usb:v0A12p0105* + ID_MODEL_FROM_DATABASE=Casira with BlueCore4-Multimedia Module + +usb:v0A12p1000* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle (HID proxy mode) + +usb:v0A12p1010* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A12p1011* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A12p1012* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A12pFFFF* + ID_MODEL_FROM_DATABASE=USB Bluetooth Device in DFU State + +usb:v0A13* + ID_VENDOR_FROM_DATABASE=Telebyte, Inc. + +usb:v0A14* + ID_VENDOR_FROM_DATABASE=Spacelabs Medical, Inc. + +usb:v0A15* + ID_VENDOR_FROM_DATABASE=Scalar Corp. + +usb:v0A16* + ID_VENDOR_FROM_DATABASE=Trek Technology (S) PTE, Ltd + +usb:v0A16p1111* + ID_MODEL_FROM_DATABASE=ThumbDrive + +usb:v0A16p8888* + ID_MODEL_FROM_DATABASE=IBM USB Memory Key + +usb:v0A16p9988* + ID_MODEL_FROM_DATABASE=Trek2000 TD-G2 + +usb:v0A17* + ID_VENDOR_FROM_DATABASE=Pentax Corp. + +usb:v0A17p0004* + ID_MODEL_FROM_DATABASE=Optio 330 + +usb:v0A17p0006* + ID_MODEL_FROM_DATABASE=Optio S / S4 + +usb:v0A17p0007* + ID_MODEL_FROM_DATABASE=Optio 550 + +usb:v0A17p0009* + ID_MODEL_FROM_DATABASE=Optio 33WR + +usb:v0A17p000A* + ID_MODEL_FROM_DATABASE=Optio 555 + +usb:v0A17p000C* + ID_MODEL_FROM_DATABASE=Optio 43WR (mass storage mode) + +usb:v0A17p000D* + ID_MODEL_FROM_DATABASE=Optio 43WR + +usb:v0A17p0015* + ID_MODEL_FROM_DATABASE=Optio S40/S5i + +usb:v0A17p003B* + ID_MODEL_FROM_DATABASE=Optio 50 (mass storage mode) + +usb:v0A17p003D* + ID_MODEL_FROM_DATABASE=Optio S55 + +usb:v0A17p0041* + ID_MODEL_FROM_DATABASE=Optio S5z + +usb:v0A17p0043* + ID_MODEL_FROM_DATABASE=*ist DL + +usb:v0A17p0047* + ID_MODEL_FROM_DATABASE=Optio S60 + +usb:v0A17p0052* + ID_MODEL_FROM_DATABASE=Optio 60 Digital Camera + +usb:v0A17p006E* + ID_MODEL_FROM_DATABASE=K10D + +usb:v0A17p0070* + ID_MODEL_FROM_DATABASE=K100D + +usb:v0A17p0093* + ID_MODEL_FROM_DATABASE=K200D + +usb:v0A17p00A7* + ID_MODEL_FROM_DATABASE=Optio E50 + +usb:v0A17p1001* + ID_MODEL_FROM_DATABASE=EI2000 Camera powered by Digita! + +usb:v0A18* + ID_VENDOR_FROM_DATABASE=Heidelberger Druckmaschinen AG + +usb:v0A19* + ID_VENDOR_FROM_DATABASE=Hua Geng Technologies, Inc. + +usb:v0A21* + ID_VENDOR_FROM_DATABASE=Medtronic Physio Control Corp. + +usb:v0A21p8001* + ID_MODEL_FROM_DATABASE=MMT-7305WW [Medtronic Minimed CareLink] + +usb:v0A22* + ID_VENDOR_FROM_DATABASE=Century Semiconductor USA, Inc. + +usb:v0A27* + ID_VENDOR_FROM_DATABASE=Datacard Group + +usb:v0A27p0102* + ID_MODEL_FROM_DATABASE=SP35 + +usb:v0A2C* + ID_VENDOR_FROM_DATABASE=AK-Modul-Bus Computer GmbH + +usb:v0A2Cp0008* + ID_MODEL_FROM_DATABASE=GPIO Ports + +usb:v0A34* + ID_VENDOR_FROM_DATABASE=TG3 Electronics, Inc. + +usb:v0A34p0101* + ID_MODEL_FROM_DATABASE=TG82tp + +usb:v0A34p0110* + ID_MODEL_FROM_DATABASE=Deck 82-key backlit keyboard + +usb:v0A35* + ID_VENDOR_FROM_DATABASE=Radikal Technologies + +usb:v0A35p002A* + ID_MODEL_FROM_DATABASE=SAC - Software Assigned Controller + +usb:v0A35p008A* + ID_MODEL_FROM_DATABASE=SAC Hub + +usb:v0A39* + ID_VENDOR_FROM_DATABASE=Gilat Satellite Networks, Ltd + +usb:v0A3A* + ID_VENDOR_FROM_DATABASE=PentaMedia Co., Ltd + +usb:v0A3Ap0163* + ID_MODEL_FROM_DATABASE=KN-W510U 1.0 Wireless LAN Adapter + +usb:v0A3C* + ID_VENDOR_FROM_DATABASE=NTT DoCoMo, Inc. + +usb:v0A3D* + ID_VENDOR_FROM_DATABASE=Varo Vision + +usb:v0A3F* + ID_VENDOR_FROM_DATABASE=Swissonic AG + +usb:v0A43* + ID_VENDOR_FROM_DATABASE=Boca Systems, Inc. + +usb:v0A46* + ID_VENDOR_FROM_DATABASE=Davicom Semiconductor, Inc. + +usb:v0A46p0268* + ID_MODEL_FROM_DATABASE=ST268 + +usb:v0A46p6688* + ID_MODEL_FROM_DATABASE=ZT6688 Fast Ethernet Adapter + +usb:v0A46p8515* + ID_MODEL_FROM_DATABASE=ADMtek ADM8515 NIC + +usb:v0A46p9000* + ID_MODEL_FROM_DATABASE=DM9000E Fast Ethernet Adapter + +usb:v0A46p9601* + ID_MODEL_FROM_DATABASE=DM9601 Fast Ethernet Adapter + +usb:v0A47* + ID_VENDOR_FROM_DATABASE=Hirose Electric + +usb:v0A48* + ID_VENDOR_FROM_DATABASE=I/O Interconnect + +usb:v0A48p3233* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v0A48p3239* + ID_MODEL_FROM_DATABASE=Multimedia Card Reader + +usb:v0A48p3258* + ID_MODEL_FROM_DATABASE=Dane Elec zMate SD Reader + +usb:v0A48p3259* + ID_MODEL_FROM_DATABASE=Dane Elec zMate CF Reader + +usb:v0A48p5000* + ID_MODEL_FROM_DATABASE=MediaGear xD-SM + +usb:v0A48p500A* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p500F* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5010* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5011* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5014* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5020* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5021* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5022* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5023* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5024* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A48p5025* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0A4A* + ID_VENDOR_FROM_DATABASE=Ploytec GmbH + +usb:v0A4B* + ID_VENDOR_FROM_DATABASE=Fujitsu Media Devices, Ltd + +usb:v0A4C* + ID_VENDOR_FROM_DATABASE=Computex Co., Ltd + +usb:v0A4Cp15D9* + ID_MODEL_FROM_DATABASE=OPTICAL MOUSE + +usb:v0A4D* + ID_VENDOR_FROM_DATABASE=Evolution Electronics, Ltd + +usb:v0A4Dp0064* + ID_MODEL_FROM_DATABASE=MK-225 Driver + +usb:v0A4Dp0065* + ID_MODEL_FROM_DATABASE=MK-225C Driver + +usb:v0A4Dp0066* + ID_MODEL_FROM_DATABASE=MK-225C Driver + +usb:v0A4Dp0067* + ID_MODEL_FROM_DATABASE=MK-425C Driver + +usb:v0A4Dp0078* + ID_MODEL_FROM_DATABASE=MK-37 Driver + +usb:v0A4Dp0079* + ID_MODEL_FROM_DATABASE=MK-37C Driver + +usb:v0A4Dp007A* + ID_MODEL_FROM_DATABASE=MK-37C Driver + +usb:v0A4Dp008C* + ID_MODEL_FROM_DATABASE=TerraTec MIDI MASTER + +usb:v0A4Dp008D* + ID_MODEL_FROM_DATABASE=MK-249C Driver + +usb:v0A4Dp008E* + ID_MODEL_FROM_DATABASE=MK-249C MIDI Keyboard + +usb:v0A4Dp008F* + ID_MODEL_FROM_DATABASE=MK-449C Driver + +usb:v0A4Dp0090* + ID_MODEL_FROM_DATABASE=Keystation 49e Driver + +usb:v0A4Dp0091* + ID_MODEL_FROM_DATABASE=Keystation 61es Driver + +usb:v0A4Dp00A0* + ID_MODEL_FROM_DATABASE=MK-361 Driver + +usb:v0A4Dp00A1* + ID_MODEL_FROM_DATABASE=MK-361C Driver + +usb:v0A4Dp00A2* + ID_MODEL_FROM_DATABASE=MK-361C Driver + +usb:v0A4Dp00A3* + ID_MODEL_FROM_DATABASE=MK-461C MIDI Keyboard + +usb:v0A4Dp00B5* + ID_MODEL_FROM_DATABASE=Keystation Pro 88 Driver + +usb:v0A4Dp00D2* + ID_MODEL_FROM_DATABASE=E-Keys Driver + +usb:v0A4Dp00F0* + ID_MODEL_FROM_DATABASE=UC-16 Driver + +usb:v0A4Dp00F1* + ID_MODEL_FROM_DATABASE=X-Session Driver + +usb:v0A4Dp00F5* + ID_MODEL_FROM_DATABASE=UC-33e MIDI Controller + +usb:v0A4E* + ID_VENDOR_FROM_DATABASE=Steinberg Soft-und Hardware GmbH + +usb:v0A4F* + ID_VENDOR_FROM_DATABASE=Litton Systems, Inc. + +usb:v0A50* + ID_VENDOR_FROM_DATABASE=Mimaki Engineering Co., Ltd + +usb:v0A51* + ID_VENDOR_FROM_DATABASE=Sony Electronics, Inc. + +usb:v0A52* + ID_VENDOR_FROM_DATABASE=Jebsee Electronics Co., Ltd + +usb:v0A53* + ID_VENDOR_FROM_DATABASE=Portable Peripheral Co., Ltd + +usb:v0A53p1000* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v0A53p2000* + ID_MODEL_FROM_DATABASE=Q-Scan A6 Scanner + +usb:v0A53p2001* + ID_MODEL_FROM_DATABASE=Q-Scan A6 Scanner + +usb:v0A53p2013* + ID_MODEL_FROM_DATABASE=Media Drive A6 Scanner + +usb:v0A53p2014* + ID_MODEL_FROM_DATABASE=Media Drive A6 Scanner + +usb:v0A53p2015* + ID_MODEL_FROM_DATABASE=BizCardReader 600C + +usb:v0A53p2016* + ID_MODEL_FROM_DATABASE=BizCardReader 600C + +usb:v0A53p202A* + ID_MODEL_FROM_DATABASE=Scanshell-CSSN + +usb:v0A53p3000* + ID_MODEL_FROM_DATABASE=Q-Scan A8 Scanner + +usb:v0A53p3002* + ID_MODEL_FROM_DATABASE=Q-Scan A8 Reader + +usb:v0A53p3015* + ID_MODEL_FROM_DATABASE=BizCardReader 300G + +usb:v0A53p302A* + ID_MODEL_FROM_DATABASE=LM9832 - PA570 Mini Business Card Scanner [Targus] + +usb:v0A53p5001* + ID_MODEL_FROM_DATABASE=BizCardReader 900C + +usb:v0A5A* + ID_VENDOR_FROM_DATABASE=Electronics For Imaging, Inc. + +usb:v0A5B* + ID_VENDOR_FROM_DATABASE=EAsics NV + +usb:v0A5C* + ID_VENDOR_FROM_DATABASE=Broadcom Corp. + +usb:v0A5Cp0201* + ID_MODEL_FROM_DATABASE=iLine10(tm) Network Adapter + +usb:v0A5Cp2000* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp2001* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp2009* + ID_MODEL_FROM_DATABASE=BCM2035 Bluetooth + +usb:v0A5Cp200A* + ID_MODEL_FROM_DATABASE=BCM2035 Bluetooth dongle + +usb:v0A5Cp200F* + ID_MODEL_FROM_DATABASE=Bluetooth Controller + +usb:v0A5Cp201D* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp201E* + ID_MODEL_FROM_DATABASE=IBM Integrated Bluetooth IV + +usb:v0A5Cp2020* + ID_MODEL_FROM_DATABASE=Bluetooth dongle + +usb:v0A5Cp2021* + ID_MODEL_FROM_DATABASE=BCM2035B3 Bluetooth Adapter + +usb:v0A5Cp2033* + ID_MODEL_FROM_DATABASE=BCM2033 Bluetooth + +usb:v0A5Cp2035* + ID_MODEL_FROM_DATABASE=BCM2035 Bluetooth + +usb:v0A5Cp2038* + ID_MODEL_FROM_DATABASE=Blutonium Device + +usb:v0A5Cp2039* + ID_MODEL_FROM_DATABASE=BCM2045 Bluetooth + +usb:v0A5Cp2045* + ID_MODEL_FROM_DATABASE=Bluetooth Controller + +usb:v0A5Cp2046* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp2047* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp205E* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0A5Cp2100* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0+eDR dongle + +usb:v0A5Cp2101* + ID_MODEL_FROM_DATABASE=BCM2045 Bluetooth + +usb:v0A5Cp2102* + ID_MODEL_FROM_DATABASE=ANYCOM Blue USB-200/250 + +usb:v0A5Cp2110* + ID_MODEL_FROM_DATABASE=BCM2045B (BDC-2) [Bluetooth Controller] + +usb:v0A5Cp2111* + ID_MODEL_FROM_DATABASE=ANYCOM Blue USB-UHE 200/250 + +usb:v0A5Cp2120* + ID_MODEL_FROM_DATABASE=2045 Bluetooth 2.0 USB-UHE Device with trace filter + +usb:v0A5Cp2121* + ID_MODEL_FROM_DATABASE=BCM2210 Bluetooth + +usb:v0A5Cp2122* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0+EDR dongle + +usb:v0A5Cp2123* + ID_MODEL_FROM_DATABASE=Bluetooth dongle + +usb:v0A5Cp2130* + ID_MODEL_FROM_DATABASE=2045 Bluetooth 2.0 USB-UHE Device with trace filter + +usb:v0A5Cp2131* + ID_MODEL_FROM_DATABASE=2045 Bluetooth 2.0 Device with trace filter + +usb:v0A5Cp2145* + ID_MODEL_FROM_DATABASE=BCM2045B (BDC-2.1) [Bluetooth Controller] + +usb:v0A5Cp2148* + ID_MODEL_FROM_DATABASE=BCM92046DG-CL1ROM Bluetooth 2.1 Adapter + +usb:v0A5Cp2150* + ID_MODEL_FROM_DATABASE=BCM2046 Bluetooth Device + +usb:v0A5Cp2151* + ID_MODEL_FROM_DATABASE=Bluetooth + +usb:v0A5Cp2154* + ID_MODEL_FROM_DATABASE=BCM92046DG-CL1ROM Bluetooth 2.1 UHE Dongle + +usb:v0A5Cp216C* + ID_MODEL_FROM_DATABASE=BCM43142A0 Bluetooth Device + +usb:v0A5Cp216F* + ID_MODEL_FROM_DATABASE=BCM20702A0 Bluetooth + +usb:v0A5Cp217D* + ID_MODEL_FROM_DATABASE=HP Bluethunder + +usb:v0A5Cp217F* + ID_MODEL_FROM_DATABASE=BCM2045B (BDC-2.1) + +usb:v0A5Cp2198* + ID_MODEL_FROM_DATABASE=Bluetooth 3.0 Device + +usb:v0A5Cp219B* + ID_MODEL_FROM_DATABASE=Bluetooth 2.1 Device + +usb:v0A5Cp21B1* + ID_MODEL_FROM_DATABASE=HP Bluetooth Module + +usb:v0A5Cp21B4* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21B9* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21BA* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21BB* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21BC* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21BD* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 2.1 + EDR + +usb:v0A5Cp21D7* + ID_MODEL_FROM_DATABASE=BCM43142 Bluetooth 4.0 + +usb:v0A5Cp21E1* + ID_MODEL_FROM_DATABASE=HP Portable SoftSailing + +usb:v0A5Cp21E3* + ID_MODEL_FROM_DATABASE=HP Portable Valentine + +usb:v0A5Cp21E6* + ID_MODEL_FROM_DATABASE=BCM20702 Bluetooth 4.0 [ThinkPad] + +usb:v0A5Cp21E8* + ID_MODEL_FROM_DATABASE=BCM20702A0 Bluetooth 4.0 + +usb:v0A5Cp21F1* + ID_MODEL_FROM_DATABASE=HP Portable Bumble Bee + +usb:v0A5Cp22BE* + ID_MODEL_FROM_DATABASE=BCM2070 Bluetooth 3.0 + HS + +usb:v0A5Cp4500* + ID_MODEL_FROM_DATABASE=BCM2046B1 USB 2.0 Hub (part of BCM2046 Bluetooth) + +usb:v0A5Cp4502* + ID_MODEL_FROM_DATABASE=Keyboard (Boot Interface Subclass) + +usb:v0A5Cp4503* + ID_MODEL_FROM_DATABASE=Mouse (Boot Interface Subclass) + +usb:v0A5Cp5800* + ID_MODEL_FROM_DATABASE=BCM5880 Secure Applications Processor + +usb:v0A5Cp5801* + ID_MODEL_FROM_DATABASE=BCM5880 Secure Applications Processor with fingerprint swipe sensor + +usb:v0A5Cp5802* + ID_MODEL_FROM_DATABASE=BCM5880 Secure Applications Processor with fingerprint touch sensor + +usb:v0A5Cp5803* + ID_MODEL_FROM_DATABASE=BCM5880 Secure Applications Processor with secure keyboard + +usb:v0A5Cp5804* + ID_MODEL_FROM_DATABASE=BCM5880 Secure Applications Processor with fingerprint swipe sensor + +usb:v0A5Cp6300* + ID_MODEL_FROM_DATABASE=Pirelli Remote NDIS Device + +usb:v0A5CpBD11* + ID_MODEL_FROM_DATABASE=TiVo AG0100 802.11bg Wireless Adapter [Broadcom BCM4320] + +usb:v0A5CpBD13* + ID_MODEL_FROM_DATABASE=BCM4323 802.11abgn Wireless Adapter + +usb:v0A5CpBD16* + ID_MODEL_FROM_DATABASE=BCM4319 802.11bgn Wireless Adapter + +usb:v0A5CpBD17* + ID_MODEL_FROM_DATABASE=BCM43236 802.11abgn Wireless Adapter + +usb:v0A5CpD11B* + ID_MODEL_FROM_DATABASE=Eminent EM4045 [Broadcom 4320 USB] + +usb:v0A5D* + ID_VENDOR_FROM_DATABASE=Diatrend Corp. + +usb:v0A5F* + ID_VENDOR_FROM_DATABASE=Zebra + +usb:v0A5Fp0009* + ID_MODEL_FROM_DATABASE=LP2844 Printer + +usb:v0A5Fp0081* + ID_MODEL_FROM_DATABASE=GK420t Label Printer + +usb:v0A5Fp008B* + ID_MODEL_FROM_DATABASE=HC100 wristbands Printer + +usb:v0A5Fp008C* + ID_MODEL_FROM_DATABASE=ZP 450 Printer + +usb:v0A5Fp00D1* + ID_MODEL_FROM_DATABASE=Zebra GC420d Label Printer + +usb:v0A5Fp930A* + ID_MODEL_FROM_DATABASE=Printer + +usb:v0A62* + ID_VENDOR_FROM_DATABASE=MPMan + +usb:v0A62p0010* + ID_MODEL_FROM_DATABASE=MPMan MP-F40 MP3 Player + +usb:v0A66* + ID_VENDOR_FROM_DATABASE=ClearCube Technology + +usb:v0A67* + ID_VENDOR_FROM_DATABASE=Medeli Electronics Co., Ltd + +usb:v0A68* + ID_VENDOR_FROM_DATABASE=Comaide Corp. + +usb:v0A69* + ID_VENDOR_FROM_DATABASE=Chroma ate, Inc. + +usb:v0A6B* + ID_VENDOR_FROM_DATABASE=Green House Co., Ltd + +usb:v0A6Bp0001* + ID_MODEL_FROM_DATABASE=Compact Flash R/W with MP3 player + +usb:v0A6Bp000F* + ID_MODEL_FROM_DATABASE=FlashDisk + +usb:v0A6C* + ID_VENDOR_FROM_DATABASE=Integrated Circuit Systems, Inc. + +usb:v0A6D* + ID_VENDOR_FROM_DATABASE=UPS Manufacturing + +usb:v0A6E* + ID_VENDOR_FROM_DATABASE=Benwin + +usb:v0A6F* + ID_VENDOR_FROM_DATABASE=Core Technology, Inc. + +usb:v0A6Fp0400* + ID_MODEL_FROM_DATABASE=Xanboo + +usb:v0A70* + ID_VENDOR_FROM_DATABASE=International Game Technology + +usb:v0A71* + ID_VENDOR_FROM_DATABASE=VIPColor Technologies USA, Inc. + +usb:v0A71p0001* + ID_MODEL_FROM_DATABASE=VP485 Printer + +usb:v0A72* + ID_VENDOR_FROM_DATABASE=Sanwa Denshi + +usb:v0A73* + ID_VENDOR_FROM_DATABASE=Mackie Designs + +usb:v0A73p0002* + ID_MODEL_FROM_DATABASE=XD-2 [Spike] + +usb:v0A7D* + ID_VENDOR_FROM_DATABASE=NSTL, Inc. + +usb:v0A7E* + ID_VENDOR_FROM_DATABASE=Octagon Systems Corp. + +usb:v0A80* + ID_VENDOR_FROM_DATABASE=Rexon Technology Corp., Ltd + +usb:v0A81* + ID_VENDOR_FROM_DATABASE=Chesen Electronics Corp. + +usb:v0A81p0101* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0A81p0103* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0A81p0203* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0A81p0205* + ID_MODEL_FROM_DATABASE=PS/2 Keyboard+Mouse Adapter + +usb:v0A81p0701* + ID_MODEL_FROM_DATABASE=USB Missile Launcher + +usb:v0A81pFF01* + ID_MODEL_FROM_DATABASE=Wireless Missile Launcher + +usb:v0A82* + ID_VENDOR_FROM_DATABASE=Syscan + +usb:v0A82p4600* + ID_MODEL_FROM_DATABASE=TravelScan 460/464 + +usb:v0A83* + ID_VENDOR_FROM_DATABASE=NextComm, Inc. + +usb:v0A84* + ID_VENDOR_FROM_DATABASE=Maui Innovative Peripherals + +usb:v0A85* + ID_VENDOR_FROM_DATABASE=Idexx Labs + +usb:v0A86* + ID_VENDOR_FROM_DATABASE=NITGen Co., Ltd + +usb:v0A89* + ID_VENDOR_FROM_DATABASE=Aktiv + +usb:v0A89p0001* + ID_MODEL_FROM_DATABASE=Guardant Stealth/Net + +usb:v0A89p0002* + ID_MODEL_FROM_DATABASE=Guardant ID + +usb:v0A89p0003* + ID_MODEL_FROM_DATABASE=Guardant Stealth 2 + +usb:v0A89p0004* + ID_MODEL_FROM_DATABASE=Rutoken + +usb:v0A89p0005* + ID_MODEL_FROM_DATABASE=Guardant Fidus + +usb:v0A89p0006* + ID_MODEL_FROM_DATABASE=Guardant Stealth 3 + +usb:v0A89p0007* + ID_MODEL_FROM_DATABASE=Guardant Stealth 2 + +usb:v0A89p0008* + ID_MODEL_FROM_DATABASE=Guardant Stealth 3 Sign/Time + +usb:v0A89p0009* + ID_MODEL_FROM_DATABASE=Guardant Code + +usb:v0A89p000A* + ID_MODEL_FROM_DATABASE=Guardant Sign Pro + +usb:v0A89p000B* + ID_MODEL_FROM_DATABASE=Guardant Sign Pro HID + +usb:v0A89p000C* + ID_MODEL_FROM_DATABASE=Guardant Stealth 3 Sign/Time + +usb:v0A89p000D* + ID_MODEL_FROM_DATABASE=Guardant Code HID + +usb:v0A89p000F* + ID_MODEL_FROM_DATABASE=Guardant System Firmware Update + +usb:v0A89p0020* + ID_MODEL_FROM_DATABASE=Rutoken S + +usb:v0A89p0025* + ID_MODEL_FROM_DATABASE=Rutoken lite + +usb:v0A89p0026* + ID_MODEL_FROM_DATABASE=Rutoken lite HID + +usb:v0A89p002A* + ID_MODEL_FROM_DATABASE=Rutoken Mass Storage + +usb:v0A89p002B* + ID_MODEL_FROM_DATABASE=Guardant Mass Storage + +usb:v0A89p0030* + ID_MODEL_FROM_DATABASE=Rutoken ECP + +usb:v0A89p0040* + ID_MODEL_FROM_DATABASE=Rutoken ECP HID + +usb:v0A89p0060* + ID_MODEL_FROM_DATABASE=Rutoken Magistra + +usb:v0A89p0061* + ID_MODEL_FROM_DATABASE=Rutoken Magistra + +usb:v0A89p0069* + ID_MODEL_FROM_DATABASE=Reader + +usb:v0A89p0080* + ID_MODEL_FROM_DATABASE=Rutoken PinPad Ex + +usb:v0A89p0081* + ID_MODEL_FROM_DATABASE=Rutoken PinPad In + +usb:v0A89p0082* + ID_MODEL_FROM_DATABASE=Rutoken PinPad 2 + +usb:v0A8D* + ID_VENDOR_FROM_DATABASE=Picturetel + +usb:v0A8E* + ID_VENDOR_FROM_DATABASE=Japan Aviation Electronics Industry, Ltd + +usb:v0A8Ep2011* + ID_MODEL_FROM_DATABASE=Filter Driver For JAE XMC R/W + +usb:v0A90* + ID_VENDOR_FROM_DATABASE=Candy Technology Co., Ltd + +usb:v0A91* + ID_VENDOR_FROM_DATABASE=Globlink Technology, Inc. + +usb:v0A91p3801* + ID_MODEL_FROM_DATABASE=Targus PAKP003 Mouse + +usb:v0A92* + ID_VENDOR_FROM_DATABASE=EGO SYStems, Inc. + +usb:v0A92p0011* + ID_MODEL_FROM_DATABASE=SYS WaveTerminal U2A + +usb:v0A92p0021* + ID_MODEL_FROM_DATABASE=GIGAPort + +usb:v0A92p0031* + ID_MODEL_FROM_DATABASE=GIGAPortAG + +usb:v0A92p0053* + ID_MODEL_FROM_DATABASE=AudioTrak Optoplay + +usb:v0A92p0061* + ID_MODEL_FROM_DATABASE=Waveterminal U24 + +usb:v0A92p0071* + ID_MODEL_FROM_DATABASE=MAYA EX7 + +usb:v0A92p0091* + ID_MODEL_FROM_DATABASE=Maya 44 + +usb:v0A92p00B1* + ID_MODEL_FROM_DATABASE=MAYA EX5 + +usb:v0A92p1000* + ID_MODEL_FROM_DATABASE=MIDI Mate + +usb:v0A92p1010* + ID_MODEL_FROM_DATABASE=RoMI/O + +usb:v0A92p1020* + ID_MODEL_FROM_DATABASE=M4U + +usb:v0A92p1030* + ID_MODEL_FROM_DATABASE=M8U + +usb:v0A92p1090* + ID_MODEL_FROM_DATABASE=KeyControl49 + +usb:v0A92p10A0* + ID_MODEL_FROM_DATABASE=KeyControl25 + +usb:v0A93* + ID_VENDOR_FROM_DATABASE=C Technologies AB + +usb:v0A93p0002* + ID_MODEL_FROM_DATABASE=C-Pen 10 + +usb:v0A93p0005* + ID_MODEL_FROM_DATABASE=MyPen Light + +usb:v0A93p000D* + ID_MODEL_FROM_DATABASE=Input Pen + +usb:v0A93p0010* + ID_MODEL_FROM_DATABASE=C-Pen 20 + +usb:v0A93p0A93* + ID_MODEL_FROM_DATABASE=PayPen + +usb:v0A94* + ID_VENDOR_FROM_DATABASE=Intersense + +usb:v0AA3* + ID_VENDOR_FROM_DATABASE=Lava Computer Mfg., Inc. + +usb:v0AA4* + ID_VENDOR_FROM_DATABASE=Develco Elektronik + +usb:v0AA5* + ID_VENDOR_FROM_DATABASE=First International Digital + +usb:v0AA5p0002* + ID_MODEL_FROM_DATABASE=irock! 500 Series + +usb:v0AA5p0801* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0AA6* + ID_VENDOR_FROM_DATABASE=Perception Digital, Ltd + +usb:v0AA6p0101* + ID_MODEL_FROM_DATABASE=Hercules Jukebox + +usb:v0AA6p1501* + ID_MODEL_FROM_DATABASE=Store 'n' Go HD Drive + +usb:v0AA7* + ID_VENDOR_FROM_DATABASE=Wincor Nixdorf International GmbH + +usb:v0AA7p0100* + ID_MODEL_FROM_DATABASE=POS Keyboard, TA58P-USB + +usb:v0AA7p0101* + ID_MODEL_FROM_DATABASE=POS Keyboard, TA85P-USB + +usb:v0AA7p0102* + ID_MODEL_FROM_DATABASE=POS Keyboard, TA59-USB + +usb:v0AA7p0103* + ID_MODEL_FROM_DATABASE=POS Keyboard, TA60-USB + +usb:v0AA7p0104* + ID_MODEL_FROM_DATABASE=SNIkey Keyboard, SNIKey-KB-USB + +usb:v0AA7p0200* + ID_MODEL_FROM_DATABASE=Operator Display, BA63-USB + +usb:v0AA7p0201* + ID_MODEL_FROM_DATABASE=Operator Display, BA66-USB + +usb:v0AA7p0202* + ID_MODEL_FROM_DATABASE=Operator Display & Scanner, XiCheck-BA63 + +usb:v0AA7p0203* + ID_MODEL_FROM_DATABASE=Operator Display & Scanner, XiCheck-BA66 + +usb:v0AA7p0204* + ID_MODEL_FROM_DATABASE=Graphics Operator Display, BA63GV + +usb:v0AA7p0300* + ID_MODEL_FROM_DATABASE=POS Printer (printer class mode), TH210 + +usb:v0AA7p0301* + ID_MODEL_FROM_DATABASE=POS Printer (native mode), TH210 + +usb:v0AA7p0302* + ID_MODEL_FROM_DATABASE=POS Printer (printer class mode), TH220 + +usb:v0AA7p0303* + ID_MODEL_FROM_DATABASE=POS Printer (native mode), TH220 + +usb:v0AA7p0304* + ID_MODEL_FROM_DATABASE=POS Printer, TH230 + +usb:v0AA7p0305* + ID_MODEL_FROM_DATABASE=Lottery Printer, XiPrintPlus + +usb:v0AA7p0306* + ID_MODEL_FROM_DATABASE=POS Printer (printer class mode), TH320 + +usb:v0AA7p0307* + ID_MODEL_FROM_DATABASE=POS Printer (native mode), TH320 + +usb:v0AA7p0308* + ID_MODEL_FROM_DATABASE=POS Printer (printer class mode), TH420 + +usb:v0AA7p0309* + ID_MODEL_FROM_DATABASE=POS Printer (native mode), TH420 + +usb:v0AA7p030A* + ID_MODEL_FROM_DATABASE=POS Printer, TH200B + +usb:v0AA7p0400* + ID_MODEL_FROM_DATABASE=Lottery Scanner, Xiscan S + +usb:v0AA7p0401* + ID_MODEL_FROM_DATABASE=Lottery Scanner, Xiscan 3 + +usb:v0AA7p0402* + ID_MODEL_FROM_DATABASE=Programmable Magnetic Swipe Card Reader, MSRP-USB + +usb:v0AA7p0500* + ID_MODEL_FROM_DATABASE=IDE Adapter + +usb:v0AA7p0501* + ID_MODEL_FROM_DATABASE=Hub Printer Interface + +usb:v0AA7p0502* + ID_MODEL_FROM_DATABASE=Hub SNIKey Keyboard + +usb:v0AA7p4304* + ID_MODEL_FROM_DATABASE=Banking Printer TP07 + +usb:v0AA7p4305* + ID_MODEL_FROM_DATABASE=Banking Printer TP07c + +usb:v0AA7p4500* + ID_MODEL_FROM_DATABASE=WN Central Special Electronics + +usb:v0AA8* + ID_VENDOR_FROM_DATABASE=TriGem Computer, Inc. + +usb:v0AA8p0060* + ID_MODEL_FROM_DATABASE=TG 11Mbps WLAN Mini Adapter + +usb:v0AA8p1001* + ID_MODEL_FROM_DATABASE=DreamComboM4100 + +usb:v0AA8p3002* + ID_MODEL_FROM_DATABASE=InkJet Color Printer + +usb:v0AA8p8001* + ID_MODEL_FROM_DATABASE=TG_iMON + +usb:v0AA8p8002* + ID_MODEL_FROM_DATABASE=TG_KLOSS + +usb:v0AA8pA001* + ID_MODEL_FROM_DATABASE=TG_X2 + +usb:v0AA8pA002* + ID_MODEL_FROM_DATABASE=TGVFD_KLOSS + +usb:v0AA8pFFDA* + ID_MODEL_FROM_DATABASE=iMON_VFD + +usb:v0AA9* + ID_VENDOR_FROM_DATABASE=Baromtec Co. + +usb:v0AA9pF01B* + ID_MODEL_FROM_DATABASE=Medion MD 6242 MP3 Player + +usb:v0AAA* + ID_VENDOR_FROM_DATABASE=Japan CBM Corp. + +usb:v0AAB* + ID_VENDOR_FROM_DATABASE=Vision Shape Europe SA + +usb:v0AAC* + ID_VENDOR_FROM_DATABASE=iCompression, Inc. + +usb:v0AAD* + ID_VENDOR_FROM_DATABASE=Rohde & Schwarz GmbH & Co. KG + +usb:v0AADp0003* + ID_MODEL_FROM_DATABASE=NRP-Z21 + +usb:v0AADp000C* + ID_MODEL_FROM_DATABASE=NRP-Z11 + +usb:v0AADp0013* + ID_MODEL_FROM_DATABASE=NRP-Z22 + +usb:v0AADp0014* + ID_MODEL_FROM_DATABASE=NRP-Z23 + +usb:v0AADp0015* + ID_MODEL_FROM_DATABASE=NRP-Z24 + +usb:v0AADp0016* + ID_MODEL_FROM_DATABASE=NRP-Z51 + +usb:v0AADp0017* + ID_MODEL_FROM_DATABASE=NRP-Z52 + +usb:v0AADp0018* + ID_MODEL_FROM_DATABASE=NRP-Z55 + +usb:v0AADp0019* + ID_MODEL_FROM_DATABASE=NRP-Z56 + +usb:v0AADp0021* + ID_MODEL_FROM_DATABASE=NRP-Z91 + +usb:v0AADp0023* + ID_MODEL_FROM_DATABASE=NRP-Z81 + +usb:v0AADp002C* + ID_MODEL_FROM_DATABASE=NRP-Z31 + +usb:v0AADp002D* + ID_MODEL_FROM_DATABASE=NRP-Z37 + +usb:v0AADp002F* + ID_MODEL_FROM_DATABASE=NRP-Z27 + +usb:v0AADp0051* + ID_MODEL_FROM_DATABASE=NRP-Z28 + +usb:v0AADp0052* + ID_MODEL_FROM_DATABASE=NRP-Z98 + +usb:v0AADp0062* + ID_MODEL_FROM_DATABASE=NRP-Z92 + +usb:v0AADp0070* + ID_MODEL_FROM_DATABASE=NRP-Z57 + +usb:v0AADp0083* + ID_MODEL_FROM_DATABASE=NRP-Z85 + +usb:v0AADp0095* + ID_MODEL_FROM_DATABASE=NRP-Z86 + +usb:v0AAE* + ID_VENDOR_FROM_DATABASE=NEC infrontia Corp. (Nitsuko) + +usb:v0AAF* + ID_VENDOR_FROM_DATABASE=Digitalway Co., Ltd + +usb:v0AB0* + ID_VENDOR_FROM_DATABASE=Arrow Strong Electronics Co., Ltd + +usb:v0AB1* + ID_VENDOR_FROM_DATABASE=FEIG ELECTRONIC GmbH + +usb:v0AB1p0002* + ID_MODEL_FROM_DATABASE=OBID RFID-Reader + +usb:v0AB1p0004* + ID_MODEL_FROM_DATABASE=OBID classic-pro + +usb:v0ABA* + ID_VENDOR_FROM_DATABASE=Ellisys + +usb:v0ABAp8001* + ID_MODEL_FROM_DATABASE=Tracker 110 Protocol Analyzer + +usb:v0ABAp8002* + ID_MODEL_FROM_DATABASE=Explorer 200 Protocol Analyzer + +usb:v0ABE* + ID_VENDOR_FROM_DATABASE=Stereo-Link + +usb:v0ABEp0101* + ID_MODEL_FROM_DATABASE=SL1200 DAC + +usb:v0ABF* + ID_VENDOR_FROM_DATABASE=Diolan + +usb:v0ABFp3370* + ID_MODEL_FROM_DATABASE=I2C/SPI Adapter - U2C-12 + +usb:v0AC3* + ID_VENDOR_FROM_DATABASE=Sanyo Semiconductor Company Micro + +usb:v0AC4* + ID_VENDOR_FROM_DATABASE=Leco Corp. + +usb:v0AC5* + ID_VENDOR_FROM_DATABASE=I & C Corp. + +usb:v0AC6* + ID_VENDOR_FROM_DATABASE=Singing Electrons, Inc. + +usb:v0AC7* + ID_VENDOR_FROM_DATABASE=Panwest Corp. + +usb:v0AC8* + ID_VENDOR_FROM_DATABASE=Z-Star Microelectronics Corp. + +usb:v0AC8p0301* + ID_MODEL_FROM_DATABASE=Web Camera + +usb:v0AC8p0302* + ID_MODEL_FROM_DATABASE=ZC0302 Webcam + +usb:v0AC8p0321* + ID_MODEL_FROM_DATABASE=Vimicro generic vc0321 Camera + +usb:v0AC8p0323* + ID_MODEL_FROM_DATABASE=Luxya WC-1200 USB 2.0 Webcam + +usb:v0AC8p0328* + ID_MODEL_FROM_DATABASE=A4Tech PK-130MG + +usb:v0AC8p0336* + ID_MODEL_FROM_DATABASE=Elecom UCAM-DLQ30 + +usb:v0AC8p301B* + ID_MODEL_FROM_DATABASE=ZC0301 Webcam + +usb:v0AC8p303B* + ID_MODEL_FROM_DATABASE=ZC0303 Webcam + +usb:v0AC8p305B* + ID_MODEL_FROM_DATABASE=ZC0305 Webcam + +usb:v0AC8p307B* + ID_MODEL_FROM_DATABASE=USB 1.1 Webcam + +usb:v0AC8p332D* + ID_MODEL_FROM_DATABASE=Vega USB 2.0 Camera + +usb:v0AC8p3343* + ID_MODEL_FROM_DATABASE=Sirius USB 2.0 Camera + +usb:v0AC8p3370* + ID_MODEL_FROM_DATABASE=Traveler TV 6500 SF Dia-scanner + +usb:v0AC8p3420* + ID_MODEL_FROM_DATABASE=Venus USB2.0 Camera + +usb:v0AC8pC001* + ID_MODEL_FROM_DATABASE=Sony embedded vimicro Camera + +usb:v0AC8pC002* + ID_MODEL_FROM_DATABASE=Visual Communication Camera VGP-VCC1 + +usb:v0AC8pC302* + ID_MODEL_FROM_DATABASE=Vega USB 2.0 Camera + +usb:v0AC8pC303* + ID_MODEL_FROM_DATABASE=Saturn USB 2.0 Camera + +usb:v0AC8pC326* + ID_MODEL_FROM_DATABASE=Namuga 1.3M Webcam + +usb:v0AC8pC33F* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v0AC8pC429* + ID_MODEL_FROM_DATABASE=Lenovo ThinkCentre Web Camera + +usb:v0AC8pC42D* + ID_MODEL_FROM_DATABASE=Lenovo IdeaCentre Web Camera + +usb:v0AC9* + ID_VENDOR_FROM_DATABASE=Micro Solutions, Inc. + +usb:v0AC9p0000* + ID_MODEL_FROM_DATABASE=Backpack CD-ReWriter + +usb:v0AC9p0001* + ID_MODEL_FROM_DATABASE=BACKPACK 2 Cable + +usb:v0AC9p0010* + ID_MODEL_FROM_DATABASE=BACKPACK + +usb:v0AC9p0011* + ID_MODEL_FROM_DATABASE=Backpack 40GB Hard Drive + +usb:v0AC9p0110* + ID_MODEL_FROM_DATABASE=BACKPACK + +usb:v0AC9p0111* + ID_MODEL_FROM_DATABASE=BackPack + +usb:v0AC9p1234* + ID_MODEL_FROM_DATABASE=BACKPACK + +usb:v0ACA* + ID_VENDOR_FROM_DATABASE=OPEN Networks Ltd + +usb:v0ACAp1060* + ID_MODEL_FROM_DATABASE=OPEN NT1 Plus II + +usb:v0ACC* + ID_VENDOR_FROM_DATABASE=Koga Electronics Co. + +usb:v0ACD* + ID_VENDOR_FROM_DATABASE=ID Tech + +usb:v0ACDp0300* + ID_MODEL_FROM_DATABASE=IDT1221U RS-232 Adapter + +usb:v0ACDp0401* + ID_MODEL_FROM_DATABASE=Spectrum III Hybrid Smartcard Reader + +usb:v0ACDp0630* + ID_MODEL_FROM_DATABASE=Spectrum III Mag-Only Insert Reader (SPT3-355 Series) USB-CDC + +usb:v0ACDp0810* + ID_MODEL_FROM_DATABASE=SecurePIN (IDPA-506100Y) PIN Pad + +usb:v0ACDp2030* + ID_MODEL_FROM_DATABASE=ValueMag Magnetic Stripe Reader + +usb:v0ACE* + ID_VENDOR_FROM_DATABASE=ZyDAS + +usb:v0ACEp1201* + ID_MODEL_FROM_DATABASE=ZD1201 802.11b + +usb:v0ACEp1211* + ID_MODEL_FROM_DATABASE=ZD1211 802.11g + +usb:v0ACEp1215* + ID_MODEL_FROM_DATABASE=ZD1211B 802.11g + +usb:v0ACEp1221* + ID_MODEL_FROM_DATABASE=ZD1221 802.11n + +usb:v0ACEp1602* + ID_MODEL_FROM_DATABASE=ZyXEL Omni FaxModem 56K + +usb:v0ACEp1608* + ID_MODEL_FROM_DATABASE=ZyXEL Omni FaxModem 56K UNO + +usb:v0ACEp1611* + ID_MODEL_FROM_DATABASE=ZyXEL Omni FaxModem 56K Plus + +usb:v0ACEp2011* + ID_MODEL_FROM_DATABASE=Virtual media for 802.11bg + +usb:v0ACEp20FF* + ID_MODEL_FROM_DATABASE=Virtual media for 802.11bg + +usb:v0ACEpA211* + ID_MODEL_FROM_DATABASE=ZD1211 802.11b/g Wireless Adapter + +usb:v0ACEpB215* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v0ACF* + ID_VENDOR_FROM_DATABASE=Intoto, Inc. + +usb:v0AD0* + ID_VENDOR_FROM_DATABASE=Intellix Corp. + +usb:v0AD1* + ID_VENDOR_FROM_DATABASE=Remotec Technology, Ltd + +usb:v0AD2* + ID_VENDOR_FROM_DATABASE=Service & Quality Technology Co., Ltd + +usb:v0ADA* + ID_VENDOR_FROM_DATABASE=Data Encryption Systems Ltd. + +usb:v0ADAp0005* + ID_MODEL_FROM_DATABASE=DK2 + +usb:v0AE3* + ID_VENDOR_FROM_DATABASE=Allion Computer, Inc. + +usb:v0AE4* + ID_VENDOR_FROM_DATABASE=Taito Corp. + +usb:v0AE7* + ID_VENDOR_FROM_DATABASE=Neodym Systems, Inc. + +usb:v0AE8* + ID_VENDOR_FROM_DATABASE=System Support Co., Ltd + +usb:v0AE9* + ID_VENDOR_FROM_DATABASE=North Shore Circuit Design L.L.P. + +usb:v0AEA* + ID_VENDOR_FROM_DATABASE=SciEssence, LLC + +usb:v0AEB* + ID_VENDOR_FROM_DATABASE=TTP Communications, Ltd + +usb:v0AEC* + ID_VENDOR_FROM_DATABASE=Neodio Technologies Corp. + +usb:v0AECp2101* + ID_MODEL_FROM_DATABASE=SmartMedia Card Reader + +usb:v0AECp2102* + ID_MODEL_FROM_DATABASE=CompactFlash Card Reader + +usb:v0AECp2103* + ID_MODEL_FROM_DATABASE=MMC/SD Card Reader + +usb:v0AECp2104* + ID_MODEL_FROM_DATABASE=MemoryStick Card Reader + +usb:v0AECp2201* + ID_MODEL_FROM_DATABASE=SmartMedia+CompactFlash Card Reader + +usb:v0AECp2202* + ID_MODEL_FROM_DATABASE=SmartMedia+MMC/SD Card Reader + +usb:v0AECp2203* + ID_MODEL_FROM_DATABASE=SmartMedia+MemoryStick Card Reader + +usb:v0AECp2204* + ID_MODEL_FROM_DATABASE=CompactFlash+MMC/SD Card Reader + +usb:v0AECp2205* + ID_MODEL_FROM_DATABASE=CompactFlash+MemoryStick Card Reader + +usb:v0AECp2206* + ID_MODEL_FROM_DATABASE=MMC/SD+MemoryStick Card Reader + +usb:v0AECp2301* + ID_MODEL_FROM_DATABASE=SmartMedia+CompactFlash+MMC/SD Card Reader + +usb:v0AECp2302* + ID_MODEL_FROM_DATABASE=SmartMedia+CompactFlash+MemoryStick Card Reader + +usb:v0AECp2303* + ID_MODEL_FROM_DATABASE=SmartMedia+MMC/SD+MemoryStick Card Reader + +usb:v0AECp2304* + ID_MODEL_FROM_DATABASE=CompactFlash+MMC/SD+MemoryStick Card Reader + +usb:v0AECp3016* + ID_MODEL_FROM_DATABASE=MMC/SD+Memory Stick Card Reader + +usb:v0AECp3050* + ID_MODEL_FROM_DATABASE=ND3050 8-in-1 Card Reader + +usb:v0AECp3060* + ID_MODEL_FROM_DATABASE=1.1 FS Card Reader + +usb:v0AECp3101* + ID_MODEL_FROM_DATABASE=MMC/SD Card Reader + +usb:v0AECp3102* + ID_MODEL_FROM_DATABASE=MemoryStick Card Reader + +usb:v0AECp3201* + ID_MODEL_FROM_DATABASE=MMC/SD+MemoryStick Card Reader + +usb:v0AECp3216* + ID_MODEL_FROM_DATABASE=HS Card Reader + +usb:v0AECp3260* + ID_MODEL_FROM_DATABASE=7-in-1 Card Reader + +usb:v0AECp5010* + ID_MODEL_FROM_DATABASE=ND5010 Card Reader + +usb:v0AF0* + ID_VENDOR_FROM_DATABASE=Option + +usb:v0AF0p5000* + ID_MODEL_FROM_DATABASE=UMTS Card + +usb:v0AF0p6000* + ID_MODEL_FROM_DATABASE=GlobeTrotter 3G datacard + +usb:v0AF0p6300* + ID_MODEL_FROM_DATABASE=GT 3G Quad UMTS/GPRS Card + +usb:v0AF0p6600* + ID_MODEL_FROM_DATABASE=GlobeTrotter 3G+ datacard + +usb:v0AF0p6711* + ID_MODEL_FROM_DATABASE=GlobeTrotter Express 7.2 v2 + +usb:v0AF0p6971* + ID_MODEL_FROM_DATABASE=Globetrotter HSDPA Modem + +usb:v0AF0p7251* + ID_MODEL_FROM_DATABASE=Globetrotter HSUPA Modem (aka iCON HSUPA E) + +usb:v0AF0p7501* + ID_MODEL_FROM_DATABASE=Globetrotter HSUPA Modem (icon 411 aka "Vodafone K3760") + +usb:v0AF0p7601* + ID_MODEL_FROM_DATABASE=Globetrotter MO40x 3G Modem (GTM 382) + +usb:v0AF0p7701* + ID_MODEL_FROM_DATABASE=Globetrotter HSUPA Modem (aka icon 451) + +usb:v0AF0pD055* + ID_MODEL_FROM_DATABASE=Globetrotter GI0505 [iCON 505] + +usb:v0AF6* + ID_VENDOR_FROM_DATABASE=Silver I Co., Ltd + +usb:v0AF7* + ID_VENDOR_FROM_DATABASE=B2C2, Inc. + +usb:v0AF7p0101* + ID_MODEL_FROM_DATABASE=Digital TV USB Receiver (DVB-S/T/C / ATSC) + +usb:v0AF9* + ID_VENDOR_FROM_DATABASE=Hama, Inc. + +usb:v0AF9p0010* + ID_MODEL_FROM_DATABASE=USB SightCam 100 + +usb:v0AF9p0011* + ID_MODEL_FROM_DATABASE=Micro Innovations IC50C Webcam + +usb:v0AFA* + ID_VENDOR_FROM_DATABASE=DMC Co., Ltd. + +usb:v0AFAp07D2* + ID_MODEL_FROM_DATABASE=Controller Board for Projected Capacitive Touch Screen DUS3000 + +usb:v0AFC* + ID_VENDOR_FROM_DATABASE=Zaptronix Ltd + +usb:v0AFD* + ID_VENDOR_FROM_DATABASE=Tateno Dennou, Inc. + +usb:v0AFE* + ID_VENDOR_FROM_DATABASE=Cummins Engine Co. + +usb:v0AFF* + ID_VENDOR_FROM_DATABASE=Jump Zone Network Products, Inc. + +usb:v0B00* + ID_VENDOR_FROM_DATABASE=INGENICO + +usb:v0B05* + ID_VENDOR_FROM_DATABASE=ASUSTek Computer, Inc. + +usb:v0B05p0001* + ID_MODEL_FROM_DATABASE=MeMO Pad HD 7 (CD-ROM mode) + +usb:v0B05p1101* + ID_MODEL_FROM_DATABASE=Mass Storage (UISDMC4S) + +usb:v0B05p1706* + ID_MODEL_FROM_DATABASE=WL-167G v1 802.11g Adapter [Ralink RT2571] + +usb:v0B05p1707* + ID_MODEL_FROM_DATABASE=WL-167G v1 802.11g Adapter [Ralink RT2571] + +usb:v0B05p1708* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0B05p170B* + ID_MODEL_FROM_DATABASE=Multi card reader + +usb:v0B05p170C* + ID_MODEL_FROM_DATABASE=WL-159g 802.11bg + +usb:v0B05p170D* + ID_MODEL_FROM_DATABASE=802.11b/g Wireless Network Adapter + +usb:v0B05p1712* + ID_MODEL_FROM_DATABASE=BT-183 Bluetooth 2.0+EDR adapter + +usb:v0B05p1715* + ID_MODEL_FROM_DATABASE=2045 Bluetooth 2.0 Device with trace filter + +usb:v0B05p1716* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0B05p1717* + ID_MODEL_FROM_DATABASE=WL169gE 802.11g Adapter [Broadcom 4320 USB] + +usb:v0B05p171B* + ID_MODEL_FROM_DATABASE=A9T wireless 802.11bg + +usb:v0B05p171C* + ID_MODEL_FROM_DATABASE=802.11b/g Wireless Network Adapter + +usb:v0B05p171F* + ID_MODEL_FROM_DATABASE=My Cinema U3000 Mini [DiBcom DiB7700P] + +usb:v0B05p1723* + ID_MODEL_FROM_DATABASE=WL-167G v2 802.11g Adapter [Ralink RT2571W] + +usb:v0B05p1724* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0B05p1726* + ID_MODEL_FROM_DATABASE=Laptop OLED Display + +usb:v0B05p172A* + ID_MODEL_FROM_DATABASE=ASUS 802.11n Network Adapter + +usb:v0B05p172B* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0B05p1731* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0B05p1732* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0B05p1734* + ID_MODEL_FROM_DATABASE=ASUS AF-200 + +usb:v0B05p173C* + ID_MODEL_FROM_DATABASE=BT-183 Bluetooth 2.0 + +usb:v0B05p173F* + ID_MODEL_FROM_DATABASE=My Cinema U3100 Mini + +usb:v0B05p1742* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0B05p1743* + ID_MODEL_FROM_DATABASE=Xonar U1 Audio Station + +usb:v0B05p1751* + ID_MODEL_FROM_DATABASE=BT-253 Bluetooth Adapter + +usb:v0B05p175B* + ID_MODEL_FROM_DATABASE=Laptop OLED Display + +usb:v0B05p1760* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter + +usb:v0B05p1761* + ID_MODEL_FROM_DATABASE=USB-N11 802.11n Network Adapter [Ralink RT2870] + +usb:v0B05p1774* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v0B05p1776* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v0B05p1779* + ID_MODEL_FROM_DATABASE=My Cinema U3100 Mini Plus [AF9035A] + +usb:v0B05p1784* + ID_MODEL_FROM_DATABASE=USB-N13 802.11n Network Adapter (rev. A1) [Ralink RT3072] + +usb:v0B05p1786* + ID_MODEL_FROM_DATABASE=USB-N10 802.11n Network Adapter [Realtek RTL8188SU] + +usb:v0B05p1788* + ID_MODEL_FROM_DATABASE=BT-270 Bluetooth Adapter + +usb:v0B05p1791* + ID_MODEL_FROM_DATABASE=WL-167G v3 802.11n Adapter [Realtek RTL8188SU] + +usb:v0B05p179D* + ID_MODEL_FROM_DATABASE=USB-N53 802.11abgn Network Adapter [Ralink RT3572] + +usb:v0B05p179E* + ID_MODEL_FROM_DATABASE=Eee Note EA800 (network mode) + +usb:v0B05p179F* + ID_MODEL_FROM_DATABASE=Eee Note EA800 (tablet mode) + +usb:v0B05p17A0* + ID_MODEL_FROM_DATABASE=Xonar U3 sound card + +usb:v0B05p17A1* + ID_MODEL_FROM_DATABASE=Eee Note EA800 (mass storage mode) + +usb:v0B05p17AB* + ID_MODEL_FROM_DATABASE=USB-N13 802.11n Network Adapter (rev. B1) [Realtek RTL8192CU] + +usb:v0B05p17BA* + ID_MODEL_FROM_DATABASE=N10 Nano 802.11n Network Adapter [Realtek RTL8192CU] + +usb:v0B05p17C7* + ID_MODEL_FROM_DATABASE=WL-330NUL + +usb:v0B05p17C9* + ID_MODEL_FROM_DATABASE=USB-AC53 802.11a/b/g/n/ac Wireless Adapter [Broadcom BCM43526] + +usb:v0B05p17CB* + ID_MODEL_FROM_DATABASE=Broadcom BCM20702A0 Bluetooth + +usb:v0B05p17D1* + ID_MODEL_FROM_DATABASE=AC51 802.11a/b/g/n/ac Wireless Adapter [Mediatek MT7610/Ralink RT2870] + +usb:v0B05p4C80* + ID_MODEL_FROM_DATABASE=Transformer Pad TF300TG + +usb:v0B05p4C90* + ID_MODEL_FROM_DATABASE=Transformer Pad Infinity TF700 + +usb:v0B05p4C91* + ID_MODEL_FROM_DATABASE=Transformer Pad Infinity TF700 (Debug mode) + +usb:v0B05p4CA0* + ID_MODEL_FROM_DATABASE=Transformer Pad TF701T + +usb:v0B05p4CA1* + ID_MODEL_FROM_DATABASE=Transformer Pad TF701T (Debug mode) + +usb:v0B05p4D00* + ID_MODEL_FROM_DATABASE=Transformer Prime TF201 + +usb:v0B05p4D01* + ID_MODEL_FROM_DATABASE=Transformer Prime TF201 (debug mode) + +usb:v0B05p4DAF* + ID_MODEL_FROM_DATABASE=Transformer Pad Infinity TF700 (Fastboot) + +usb:v0B05p5410* + ID_MODEL_FROM_DATABASE=MeMO Pad HD 7 (MTP mode) + +usb:v0B05p5412* + ID_MODEL_FROM_DATABASE=MeMO Pad HD 7 (PTP mode) + +usb:v0B05p550F* + ID_MODEL_FROM_DATABASE=ASUS fonepad 7 + +usb:v0B05p6101* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v0B05p620A* + ID_MODEL_FROM_DATABASE=Remote NDIS Device + +usb:v0B05pB700* + ID_MODEL_FROM_DATABASE=Broadcom Bluetooth 2.1 + +usb:v0B0B* + ID_VENDOR_FROM_DATABASE=Datamax-O'Neil + +usb:v0B0Bp106E* + ID_MODEL_FROM_DATABASE=Datamax E-4304 + +usb:v0B0C* + ID_VENDOR_FROM_DATABASE=Todos AB + +usb:v0B0Cp0009* + ID_MODEL_FROM_DATABASE=Todos Argos Mini II Smart Card Reader + +usb:v0B0Cp001E* + ID_MODEL_FROM_DATABASE=e.dentifier2 (ABN AMRO electronic banking card reader NL) + +usb:v0B0Cp002E* + ID_MODEL_FROM_DATABASE=C200 smartcard controller (Nordea card reader) + +usb:v0B0Cp003F* + ID_MODEL_FROM_DATABASE=Todos C400 smartcard controller (Handelsbanken card reader) + +usb:v0B0Cp0050* + ID_MODEL_FROM_DATABASE=Argos Mini II Smart Card Reader (CCID) + +usb:v0B0D* + ID_VENDOR_FROM_DATABASE=ProjectLab + +usb:v0B0Dp0000* + ID_MODEL_FROM_DATABASE=CenturyCD + +usb:v0B0E* + ID_VENDOR_FROM_DATABASE=GN Netcom + +usb:v0B0Ep034C* + ID_MODEL_FROM_DATABASE=Jabra UC Voice 750 MS + +usb:v0B0Ep0420* + ID_MODEL_FROM_DATABASE=Jabra SPEAK 510 + +usb:v0B0Ep094D* + ID_MODEL_FROM_DATABASE=GN Netcom / Jabra REVO Wireless + +usb:v0B0Ep1022* + ID_MODEL_FROM_DATABASE=Jabra PRO 9450, Type 9400BS (DECT Headset) + +usb:v0B0Ep2007* + ID_MODEL_FROM_DATABASE=GN 2000 Stereo Corded Headset + +usb:v0B0Ep620C* + ID_MODEL_FROM_DATABASE=Jabra BT620s + +usb:v0B0Ep9330* + ID_MODEL_FROM_DATABASE=Jabra GN9330 Headset + +usb:v0B0F* + ID_VENDOR_FROM_DATABASE=AVID Technology + +usb:v0B10* + ID_VENDOR_FROM_DATABASE=Pcally + +usb:v0B11* + ID_VENDOR_FROM_DATABASE=I Tech Solutions Co., Ltd + +usb:v0B1E* + ID_VENDOR_FROM_DATABASE=Electronic Warfare Assoc., Inc. (EWA) + +usb:v0B1Ep8007* + ID_MODEL_FROM_DATABASE=Blackhawk USB560-BP JTAG Emulator + +usb:v0B1F* + ID_VENDOR_FROM_DATABASE=Insyde Software Corp. + +usb:v0B20* + ID_VENDOR_FROM_DATABASE=TransDimension, Inc. + +usb:v0B21* + ID_VENDOR_FROM_DATABASE=Yokogawa Electric Corp. + +usb:v0B22* + ID_VENDOR_FROM_DATABASE=Japan System Development Co., Ltd + +usb:v0B23* + ID_VENDOR_FROM_DATABASE=Pan-Asia Electronics Co., Ltd + +usb:v0B24* + ID_VENDOR_FROM_DATABASE=Link Evolution Corp. + +usb:v0B27* + ID_VENDOR_FROM_DATABASE=Ritek Corp. + +usb:v0B28* + ID_VENDOR_FROM_DATABASE=Kenwood Corp. + +usb:v0B2C* + ID_VENDOR_FROM_DATABASE=Village Center, Inc. + +usb:v0B30* + ID_VENDOR_FROM_DATABASE=PNY Technologies, Inc. + +usb:v0B30p0006* + ID_MODEL_FROM_DATABASE=SM Media-Shuttle Card Reader + +usb:v0B33* + ID_VENDOR_FROM_DATABASE=Contour Design, Inc. + +usb:v0B33p0020* + ID_MODEL_FROM_DATABASE=ShuttleXpress + +usb:v0B33p0700* + ID_MODEL_FROM_DATABASE=RollerMouse Pro + +usb:v0B37* + ID_VENDOR_FROM_DATABASE=Hitachi ULSI Systems Co., Ltd + +usb:v0B38* + ID_VENDOR_FROM_DATABASE=Gear Head + +usb:v0B38p0003* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0B38p0010* + ID_MODEL_FROM_DATABASE=107-Key Keyboard + +usb:v0B39* + ID_VENDOR_FROM_DATABASE=Omnidirectional Control Technology, Inc. + +usb:v0B39p0001* + ID_MODEL_FROM_DATABASE=Composite USB PS2 Converter + +usb:v0B39p0109* + ID_MODEL_FROM_DATABASE=USB TO Ethernet + +usb:v0B39p0421* + ID_MODEL_FROM_DATABASE=Serial + +usb:v0B39p0801* + ID_MODEL_FROM_DATABASE=USB-Parallel Bridge + +usb:v0B39p0901* + ID_MODEL_FROM_DATABASE=OCT To Fast Ethernet Converter + +usb:v0B39p0C03* + ID_MODEL_FROM_DATABASE=LAN DOCK Serial Converter + +usb:v0B3A* + ID_VENDOR_FROM_DATABASE=IPaxess + +usb:v0B3B* + ID_VENDOR_FROM_DATABASE=Tekram Technology Co., Ltd + +usb:v0B3Bp0163* + ID_MODEL_FROM_DATABASE=TL-WN320G 1.0 WLAN Adapter + +usb:v0B3Bp1601* + ID_MODEL_FROM_DATABASE=Allnet 0193 802.11b Adapter + +usb:v0B3Bp1602* + ID_MODEL_FROM_DATABASE=ZyXEL ZyAIR B200 802.11b Adapter + +usb:v0B3Bp1612* + ID_MODEL_FROM_DATABASE=AIR.Mate 2@net 802.11b Adapter + +usb:v0B3Bp1613* + ID_MODEL_FROM_DATABASE=802.11b Wireless LAN Adapter + +usb:v0B3Bp1620* + ID_MODEL_FROM_DATABASE=Allnet Wireless Network Adapter [Envara WiND512] + +usb:v0B3Bp1630* + ID_MODEL_FROM_DATABASE=QuickWLAN 802.11bg + +usb:v0B3Bp5630* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v0B3Bp6630* + ID_MODEL_FROM_DATABASE=ZD1211 + +usb:v0B3C* + ID_VENDOR_FROM_DATABASE=Olivetti Techcenter + +usb:v0B3CpA010* + ID_MODEL_FROM_DATABASE=Simple_Way Printer/Scanner/Copier + +usb:v0B3CpC000* + ID_MODEL_FROM_DATABASE=Olicard 100 + +usb:v0B3CpC700* + ID_MODEL_FROM_DATABASE=Olicard 100 (Mass Storage mode) + +usb:v0B3E* + ID_VENDOR_FROM_DATABASE=Kikusui Electronics Corp. + +usb:v0B41* + ID_VENDOR_FROM_DATABASE=Hal Corp. + +usb:v0B41p0011* + ID_MODEL_FROM_DATABASE=Crossam2+USB IR commander + +usb:v0B43* + ID_VENDOR_FROM_DATABASE=Play.com, Inc. + +usb:v0B43p0003* + ID_MODEL_FROM_DATABASE=PS2 Controller Converter + +usb:v0B43p0005* + ID_MODEL_FROM_DATABASE=GameCube Adaptor + +usb:v0B47* + ID_VENDOR_FROM_DATABASE=Sportbug.com, Inc. + +usb:v0B48* + ID_VENDOR_FROM_DATABASE=TechnoTrend AG + +usb:v0B48p1003* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge USB-Nova + +usb:v0B48p1004* + ID_MODEL_FROM_DATABASE=TT-PCline + +usb:v0B48p1005* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge USB-Nova + +usb:v0B48p1006* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge DEC3000-s + +usb:v0B48p1007* + ID_MODEL_FROM_DATABASE=TT-micro plus Device + +usb:v0B48p1008* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge DEC2000-t + +usb:v0B48p1009* + ID_MODEL_FROM_DATABASE=Technotrend/Hauppauge DEC2540-t + +usb:v0B48p3001* + ID_MODEL_FROM_DATABASE=DVB-S receiver + +usb:v0B48p3002* + ID_MODEL_FROM_DATABASE=DVB-C receiver + +usb:v0B48p3003* + ID_MODEL_FROM_DATABASE=DVB-T receiver + +usb:v0B48p3004* + ID_MODEL_FROM_DATABASE=TT TV-Stick + +usb:v0B48p3005* + ID_MODEL_FROM_DATABASE=TT TV-Stick (8kB EEPROM) + +usb:v0B48p3006* + ID_MODEL_FROM_DATABASE=TT-connect S-2400 DVB-S receiver + +usb:v0B48p3007* + ID_MODEL_FROM_DATABASE=TT-connect S2-3600 + +usb:v0B48p3008* + ID_MODEL_FROM_DATABASE=TT-connect + +usb:v0B48p3009* + ID_MODEL_FROM_DATABASE=TT-connect S-2400 DVB-S receiver (8kB EEPROM) + +usb:v0B48p300A* + ID_MODEL_FROM_DATABASE=TT-connect S2-3650 CI + +usb:v0B48p300B* + ID_MODEL_FROM_DATABASE=TT-connect C-3650 CI + +usb:v0B48p300C* + ID_MODEL_FROM_DATABASE=TT-connect T-3650 CI + +usb:v0B48p300D* + ID_MODEL_FROM_DATABASE=TT-connect CT-3650 CI + +usb:v0B48p300E* + ID_MODEL_FROM_DATABASE=TT-connect C-2400 + +usb:v0B48p3011* + ID_MODEL_FROM_DATABASE=TT-connect S2-4600 + +usb:v0B48p3012* + ID_MODEL_FROM_DATABASE=TT-connect CT2-4650 CI + +usb:v0B48p3014* + ID_MODEL_FROM_DATABASE=TT-TVStick CT2-4400 + +usb:v0B49* + ID_VENDOR_FROM_DATABASE=ASCII Corp. + +usb:v0B49p064F* + ID_MODEL_FROM_DATABASE=Trance Vibrator + +usb:v0B4B* + ID_VENDOR_FROM_DATABASE=Pine Corp. Ltd. + +usb:v0B4Bp0100* + ID_MODEL_FROM_DATABASE=D'music MP3 Player + +usb:v0B4D* + ID_VENDOR_FROM_DATABASE=Graphtec America, Inc. + +usb:v0B4Dp110A* + ID_MODEL_FROM_DATABASE=Graphtec CC200-20 + +usb:v0B4E* + ID_VENDOR_FROM_DATABASE=Musical Electronics, Ltd + +usb:v0B4Ep6500* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0B4Ep8028* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0B4Ep8920* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0B50* + ID_VENDOR_FROM_DATABASE=Dumpries Co., Ltd + +usb:v0B51* + ID_VENDOR_FROM_DATABASE=Comfort Keyboard Co. + +usb:v0B51p0020* + ID_MODEL_FROM_DATABASE=Comfort Keyboard + +usb:v0B52* + ID_VENDOR_FROM_DATABASE=Colorado MicroDisplay, Inc. + +usb:v0B54* + ID_VENDOR_FROM_DATABASE=Sinbon Electronics Co., Ltd + +usb:v0B56* + ID_VENDOR_FROM_DATABASE=TYI Systems, Ltd + +usb:v0B57* + ID_VENDOR_FROM_DATABASE=Beijing HanwangTechnology Co., Ltd + +usb:v0B59* + ID_VENDOR_FROM_DATABASE=Lake Communications, Ltd + +usb:v0B5A* + ID_VENDOR_FROM_DATABASE=Corel Corp. + +usb:v0B5F* + ID_VENDOR_FROM_DATABASE=Green Electronics Co., Ltd + +usb:v0B60* + ID_VENDOR_FROM_DATABASE=Nsine, Ltd + +usb:v0B61* + ID_VENDOR_FROM_DATABASE=NEC Viewtechnology, Ltd + +usb:v0B62* + ID_VENDOR_FROM_DATABASE=Orange Micro, Inc. + +usb:v0B62p000B* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0B62p0059* + ID_MODEL_FROM_DATABASE=iBOT2 Webcam + +usb:v0B63* + ID_VENDOR_FROM_DATABASE=ADLink Technology, Inc. + +usb:v0B64* + ID_VENDOR_FROM_DATABASE=Wonderful Wire Cable Co., Ltd + +usb:v0B65* + ID_VENDOR_FROM_DATABASE=Expert Magnetics Corp. + +usb:v0B66* + ID_VENDOR_FROM_DATABASE=Cybiko Inc. + +usb:v0B66p0041* + ID_MODEL_FROM_DATABASE=Xtreme + +usb:v0B67* + ID_VENDOR_FROM_DATABASE=Fairbanks Scales + +usb:v0B67p555E* + ID_MODEL_FROM_DATABASE=SCB-R9000 + +usb:v0B69* + ID_VENDOR_FROM_DATABASE=CacheVision + +usb:v0B6A* + ID_VENDOR_FROM_DATABASE=Maxim Integrated Products + +usb:v0B6ApA132* + ID_MODEL_FROM_DATABASE=WUP-005 [Nintendo Wii U Pro Controller] + +usb:v0B6F* + ID_VENDOR_FROM_DATABASE=Nagano Japan Radio Co., Ltd + +usb:v0B70* + ID_VENDOR_FROM_DATABASE=PortalPlayer, Inc. + +usb:v0B70p00BA* + ID_MODEL_FROM_DATABASE=iRiver H10 20GB + +usb:v0B71* + ID_VENDOR_FROM_DATABASE=SHIN-EI Sangyo Co., Ltd + +usb:v0B72* + ID_VENDOR_FROM_DATABASE=Embedded Wireless Technology Co., Ltd + +usb:v0B73* + ID_VENDOR_FROM_DATABASE=Computone Corp. + +usb:v0B75* + ID_VENDOR_FROM_DATABASE=Roland DG Corp. + +usb:v0B79* + ID_VENDOR_FROM_DATABASE=Sunrise Telecom, Inc. + +usb:v0B7A* + ID_VENDOR_FROM_DATABASE=Zeevo, Inc. + +usb:v0B7Ap07D0* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0B7B* + ID_VENDOR_FROM_DATABASE=Taiko Denki Co., Ltd + +usb:v0B7C* + ID_VENDOR_FROM_DATABASE=ITRAN Communications, Ltd + +usb:v0B7D* + ID_VENDOR_FROM_DATABASE=Astrodesign, Inc. + +usb:v0B81* + ID_VENDOR_FROM_DATABASE=id3 Technologies + +usb:v0B81p0001* + ID_MODEL_FROM_DATABASE=Biothentic II smartcard reader with fingerprint sensor + +usb:v0B81p0002* + ID_MODEL_FROM_DATABASE=DFU-Enabled Devices (DFU) + +usb:v0B81p0012* + ID_MODEL_FROM_DATABASE=BioPAD biometric module (DFU + CDC) + +usb:v0B81p0102* + ID_MODEL_FROM_DATABASE=Certis V1 fingerprint reader + +usb:v0B81p0103* + ID_MODEL_FROM_DATABASE=Certis V2 fingerprint reader + +usb:v0B81p0200* + ID_MODEL_FROM_DATABASE=CL1356T / CL1356T5 / CL1356A smartcard readers (CCID) + +usb:v0B81p0201* + ID_MODEL_FROM_DATABASE=CL1356T / CL1356T5 / CL1356A smartcard readers (DFU + CCID) + +usb:v0B81p0220* + ID_MODEL_FROM_DATABASE=CL1356A FFPJP smartcard reader (CCID + HID) + +usb:v0B81p0221* + ID_MODEL_FROM_DATABASE=CL1356A smartcard reader (DFU + CCID + HID) + +usb:v0B84* + ID_VENDOR_FROM_DATABASE=Rextron Technology, Inc. + +usb:v0B85* + ID_VENDOR_FROM_DATABASE=Elkat Electronics, Sdn., Bhd. + +usb:v0B86* + ID_VENDOR_FROM_DATABASE=Exputer Systems, Inc. + +usb:v0B86p5100* + ID_MODEL_FROM_DATABASE=XMC5100 Zippy Drive + +usb:v0B86p5110* + ID_MODEL_FROM_DATABASE=XMC5110 Flash Drive + +usb:v0B86p5200* + ID_MODEL_FROM_DATABASE=XMC5200 Zippy Drive + +usb:v0B86p5201* + ID_MODEL_FROM_DATABASE=XMC5200 Zippy Drive + +usb:v0B86p5202* + ID_MODEL_FROM_DATABASE=XMC5200 Zippy Drive + +usb:v0B86p5280* + ID_MODEL_FROM_DATABASE=XMC5280 Storage Drive + +usb:v0B86pFFF0* + ID_MODEL_FROM_DATABASE=ISP5200 Debugger + +usb:v0B87* + ID_VENDOR_FROM_DATABASE=Plus-One I & T, Inc. + +usb:v0B88* + ID_VENDOR_FROM_DATABASE=Sigma Koki Co., Ltd, Technology Center + +usb:v0B89* + ID_VENDOR_FROM_DATABASE=Advanced Digital Broadcast, Ltd + +usb:v0B8C* + ID_VENDOR_FROM_DATABASE=SMART Technologies Inc. + +usb:v0B8Cp0001* + ID_MODEL_FROM_DATABASE=Interactive Whiteboard Controller (SB6) (HID) + +usb:v0B8Cp00C3* + ID_MODEL_FROM_DATABASE=Sympodium ID350 + +usb:v0B95* + ID_VENDOR_FROM_DATABASE=ASIX Electronics Corp. + +usb:v0B95p1720* + ID_MODEL_FROM_DATABASE=10/100 Ethernet + +usb:v0B95p1780* + ID_MODEL_FROM_DATABASE=AX88178 + +usb:v0B95p1790* + ID_MODEL_FROM_DATABASE=AX88179 Gigabit Ethernet + +usb:v0B95p7720* + ID_MODEL_FROM_DATABASE=AX88772 + +usb:v0B95p772A* + ID_MODEL_FROM_DATABASE=AX88772A Fast Ethernet + +usb:v0B95p772B* + ID_MODEL_FROM_DATABASE=AX88772B + +usb:v0B95p7E2B* + ID_MODEL_FROM_DATABASE=AX88772B + +usb:v0B96* + ID_VENDOR_FROM_DATABASE=Sewon Telecom + +usb:v0B97* + ID_VENDOR_FROM_DATABASE=O2 Micro, Inc. + +usb:v0B97p7732* + ID_MODEL_FROM_DATABASE=Smart Card Reader + +usb:v0B97p7761* + ID_MODEL_FROM_DATABASE=Oz776 1.1 Hub + +usb:v0B97p7762* + ID_MODEL_FROM_DATABASE=Oz776 SmartCard Reader + +usb:v0B97p7772* + ID_MODEL_FROM_DATABASE=OZ776 CCID Smartcard Reader + +usb:v0B98* + ID_VENDOR_FROM_DATABASE=Playmates Toys, Inc. + +usb:v0B99* + ID_VENDOR_FROM_DATABASE=Audio International, Inc. + +usb:v0B9B* + ID_VENDOR_FROM_DATABASE=Dipl.-Ing. Stefan Kunde + +usb:v0B9Bp4012* + ID_MODEL_FROM_DATABASE=Reflex RC-controller Interface + +usb:v0B9D* + ID_VENDOR_FROM_DATABASE=Softprotec Co. + +usb:v0B9F* + ID_VENDOR_FROM_DATABASE=Chippo Technologies + +usb:v0BAF* + ID_VENDOR_FROM_DATABASE=U.S. Robotics + +usb:v0BAFp00E5* + ID_MODEL_FROM_DATABASE=USR6000 + +usb:v0BAFp00EB* + ID_MODEL_FROM_DATABASE=USR1120 802.11b Adapter + +usb:v0BAFp00EC* + ID_MODEL_FROM_DATABASE=56K Faxmodem + +usb:v0BAFp00F1* + ID_MODEL_FROM_DATABASE=SureConnect ADSL ATM Adapter + +usb:v0BAFp00F2* + ID_MODEL_FROM_DATABASE=SureConnect ADSL Loader + +usb:v0BAFp00F5* + ID_MODEL_FROM_DATABASE=SureConnect ADSL ATM Adapter + +usb:v0BAFp00F6* + ID_MODEL_FROM_DATABASE=SureConnect ADSL Loader + +usb:v0BAFp00F7* + ID_MODEL_FROM_DATABASE=SureConnect ADSL ATM Adapter + +usb:v0BAFp00F8* + ID_MODEL_FROM_DATABASE=SureConnect ADSL Loader + +usb:v0BAFp00F9* + ID_MODEL_FROM_DATABASE=SureConnect ADSL ATM Adapter + +usb:v0BAFp00FA* + ID_MODEL_FROM_DATABASE=SureConnect ADSL Loader + +usb:v0BAFp00FB* + ID_MODEL_FROM_DATABASE=SureConnect ADSL Ethernet/USB Router + +usb:v0BAFp0111* + ID_MODEL_FROM_DATABASE=USR5420 802.11g Adapter [Broadcom 4320 USB] + +usb:v0BAFp0118* + ID_MODEL_FROM_DATABASE=U5 802.11g Adapter + +usb:v0BAFp011B* + ID_MODEL_FROM_DATABASE=Wireless MAXg Adapter [Broadcom 4320] + +usb:v0BAFp0121* + ID_MODEL_FROM_DATABASE=USR5423 802.11bg Wireless Adapter [ZyDAS ZD1211B] + +usb:v0BAFp0303* + ID_MODEL_FROM_DATABASE=USR5637 56K Faxmodem + +usb:v0BAFp6112* + ID_MODEL_FROM_DATABASE=FaxModem Model 5633 + +usb:v0BB0* + ID_VENDOR_FROM_DATABASE=Concord Camera Corp. + +usb:v0BB0p0100* + ID_MODEL_FROM_DATABASE=Sound Vision Stream + +usb:v0BB0p5007* + ID_MODEL_FROM_DATABASE=3340z/Rollei DC3100 + +usb:v0BB1* + ID_VENDOR_FROM_DATABASE=Infinilink Corp. + +usb:v0BB2* + ID_VENDOR_FROM_DATABASE=Ambit Microsystems Corp. + +usb:v0BB2p0302* + ID_MODEL_FROM_DATABASE=U10H010 802.11b Wireless Adapter [Intersil PRISM 3] + +usb:v0BB2p6098* + ID_MODEL_FROM_DATABASE=USB Cable Modem + +usb:v0BB3* + ID_VENDOR_FROM_DATABASE=Ofuji Technology + +usb:v0BB4* + ID_VENDOR_FROM_DATABASE=HTC (High Tech Computer Corp.) + +usb:v0BB4p0001* + ID_MODEL_FROM_DATABASE=Android Phone via mass storage [Wiko Cink Peax 2] + +usb:v0BB4p00CE* + ID_MODEL_FROM_DATABASE=mmO2 XDA GSM/GPRS Pocket PC + +usb:v0BB4p00CF* + ID_MODEL_FROM_DATABASE=SPV C500 Smart Phone + +usb:v0BB4p0A01* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A02* + ID_MODEL_FROM_DATABASE=Himalaya GSM/GPRS Pocket PC + +usb:v0BB4p0A03* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A04* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A05* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A06* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A07* + ID_MODEL_FROM_DATABASE=Magician PocketPC SmartPhone / O2 XDA + +usb:v0BB4p0A08* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A09* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0A* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0B* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0C* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0D* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0E* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A0F* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A10* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A11* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A12* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A13* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A14* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A15* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A16* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A17* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A18* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A19* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1A* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1B* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1C* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1D* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1E* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A1F* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A20* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A21* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A22* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A23* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A24* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A25* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A26* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A27* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A28* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A29* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2A* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2B* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2C* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2D* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2E* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A2F* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A30* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A31* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A32* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A33* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A34* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A35* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A36* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A37* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A38* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A39* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3A* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3B* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3C* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3D* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3E* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A3F* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A40* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A41* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A42* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A43* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A44* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A45* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A46* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A47* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A48* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A49* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4A* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4B* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4C* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4D* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4E* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A4F* + ID_MODEL_FROM_DATABASE=PocketPC Sync + +usb:v0BB4p0A50* + ID_MODEL_FROM_DATABASE=SmartPhone (MTP) + +usb:v0BB4p0A51* + ID_MODEL_FROM_DATABASE=SPV C400 / T-Mobile SDA GSM/GPRS Pocket PC + +usb:v0BB4p0A52* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A53* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A54* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A55* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A56* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A57* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A58* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A59* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5A* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5B* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5C* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5D* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5E* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A5F* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A60* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A61* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A62* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A63* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A64* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A65* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A66* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A67* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A68* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A69* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6A* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6B* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6C* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6D* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6E* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A6F* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A70* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A71* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A72* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A73* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A74* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A75* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A76* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A77* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A78* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A79* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7A* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7B* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7C* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7D* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7E* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A7F* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A80* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A81* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A82* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A83* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A84* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A85* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A86* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A87* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A88* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A89* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8A* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8B* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8C* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8D* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8E* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A8F* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A90* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A91* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A92* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A93* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A94* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A95* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A96* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A97* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A98* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A99* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9A* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9B* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9C* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9D* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9E* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0A9F* + ID_MODEL_FROM_DATABASE=SmartPhone Sync + +usb:v0BB4p0B03* + ID_MODEL_FROM_DATABASE=Ozone Mobile Broadband + +usb:v0BB4p0B04* + ID_MODEL_FROM_DATABASE=Hermes / TyTN / T-Mobile MDA Vario II / O2 Xda Trion + +usb:v0BB4p0B05* + ID_MODEL_FROM_DATABASE=P3600 + +usb:v0BB4p0B06* + ID_MODEL_FROM_DATABASE=Athena / Advantage x7500 / Dopod U1000 / T-Mobile AMEO + +usb:v0BB4p0B0C* + ID_MODEL_FROM_DATABASE=Elf / Touch / P3450 / T-Mobile MDA Touch / O2 Xda Nova / Dopod S1 + +usb:v0BB4p0B1F* + ID_MODEL_FROM_DATABASE=Sony Ericsson XPERIA X1 + +usb:v0BB4p0B2F* + ID_MODEL_FROM_DATABASE=Rhodium + +usb:v0BB4p0B51* + ID_MODEL_FROM_DATABASE=Qtek 8310 mobile phone [Tornado Noble] + +usb:v0BB4p0BCE* + ID_MODEL_FROM_DATABASE=Vario MDA + +usb:v0BB4p0C01* + ID_MODEL_FROM_DATABASE=Dream / ADP1 / G1 / Magic / Tattoo + +usb:v0BB4p0C02* + ID_MODEL_FROM_DATABASE=Dream / ADP1 / G1 / Magic / Tattoo (Debug) + +usb:v0BB4p0C03* + ID_MODEL_FROM_DATABASE=Android Phone [Fairphone First Edition (FP1)] + +usb:v0BB4p0C13* + ID_MODEL_FROM_DATABASE=Diamond + +usb:v0BB4p0C1F* + ID_MODEL_FROM_DATABASE=Sony Ericsson XPERIA X1 + +usb:v0BB4p0C5F* + ID_MODEL_FROM_DATABASE=Snap + +usb:v0BB4p0C86* + ID_MODEL_FROM_DATABASE=Sensation + +usb:v0BB4p0C87* + ID_MODEL_FROM_DATABASE=Desire (debug) + +usb:v0BB4p0C8D* + ID_MODEL_FROM_DATABASE=EVO 4G (debug) + +usb:v0BB4p0C91* + ID_MODEL_FROM_DATABASE=Vision + +usb:v0BB4p0C94* + ID_MODEL_FROM_DATABASE=Vision + +usb:v0BB4p0C97* + ID_MODEL_FROM_DATABASE=Legend + +usb:v0BB4p0C99* + ID_MODEL_FROM_DATABASE=Desire (debug) + +usb:v0BB4p0C9E* + ID_MODEL_FROM_DATABASE=Incredible + +usb:v0BB4p0CA2* + ID_MODEL_FROM_DATABASE=Desire HD (debug mode) + +usb:v0BB4p0CA5* + ID_MODEL_FROM_DATABASE=Android Phone [Evo Shift 4G] + +usb:v0BB4p0CAE* + ID_MODEL_FROM_DATABASE=T-Mobile MyTouch 4G Slide [Doubleshot] + +usb:v0BB4p0DEA* + ID_MODEL_FROM_DATABASE=M7_UL [HTC One] + +usb:v0BB4p0F25* + ID_MODEL_FROM_DATABASE=One M8 + +usb:v0BB4p0F64* + ID_MODEL_FROM_DATABASE=Desire 601 + +usb:v0BB4p0FF8* + ID_MODEL_FROM_DATABASE=Desire HD (Tethering Mode) + +usb:v0BB4p0FF9* + ID_MODEL_FROM_DATABASE=Desire / Desire HD / Hero / Thunderbolt (Charge Mode) + +usb:v0BB4p0FFE* + ID_MODEL_FROM_DATABASE=Desire HD (modem mode) + +usb:v0BB4p0FFF* + ID_MODEL_FROM_DATABASE=Android Fastboot Bootloader + +usb:v0BB4p2008* + ID_MODEL_FROM_DATABASE=Android Phone via MTP [Wiko Cink Peax 2] + +usb:v0BB4p200B* + ID_MODEL_FROM_DATABASE=Android Phone via PTP [Wiko Cink Peax 2] + +usb:v0BB5* + ID_VENDOR_FROM_DATABASE=Murata Manufacturing Co., Ltd + +usb:v0BB6* + ID_VENDOR_FROM_DATABASE=Network Alchemy + +usb:v0BB7* + ID_VENDOR_FROM_DATABASE=Joytech Computer Co., Ltd + +usb:v0BB8* + ID_VENDOR_FROM_DATABASE=Hitachi Semiconductor and Devices Sales Co., Ltd + +usb:v0BB9* + ID_VENDOR_FROM_DATABASE=Eiger M&C Co., Ltd + +usb:v0BBA* + ID_VENDOR_FROM_DATABASE=ZAccess Systems + +usb:v0BBB* + ID_VENDOR_FROM_DATABASE=General Meters Corp. + +usb:v0BBC* + ID_VENDOR_FROM_DATABASE=Assistive Technology, Inc. + +usb:v0BBD* + ID_VENDOR_FROM_DATABASE=System Connection, Inc. + +usb:v0BC0* + ID_VENDOR_FROM_DATABASE=Knilink Technology, Inc. + +usb:v0BC1* + ID_VENDOR_FROM_DATABASE=Fuw Yng Electronics Co., Ltd + +usb:v0BC2* + ID_VENDOR_FROM_DATABASE=Seagate RSS LLC + +usb:v0BC2p0502* + ID_MODEL_FROM_DATABASE=ST3300601CB-RK 300 GB External Hard Drive + +usb:v0BC2p0503* + ID_MODEL_FROM_DATABASE=ST3250824A [Barracuda 7200.9] + +usb:v0BC2p2000* + ID_MODEL_FROM_DATABASE=Storage Adapter V3 (TPP) + +usb:v0BC2p2100* + ID_MODEL_FROM_DATABASE=FreeAgent Go + +usb:v0BC2p2200* + ID_MODEL_FROM_DATABASE=FreeAgent Go FW + +usb:v0BC2p2300* + ID_MODEL_FROM_DATABASE=Expansion Portable + +usb:v0BC2p2320* + ID_MODEL_FROM_DATABASE=USB 3.0 bridge [Portable Expansion Drive] + +usb:v0BC2p2321* + ID_MODEL_FROM_DATABASE=Expansion Portable + +usb:v0BC2p2340* + ID_MODEL_FROM_DATABASE=FreeAgent External Hard Drive + +usb:v0BC2p3000* + ID_MODEL_FROM_DATABASE=FreeAgent Desktop + +usb:v0BC2p3008* + ID_MODEL_FROM_DATABASE=FreeAgent Desk 1TB + +usb:v0BC2p3101* + ID_MODEL_FROM_DATABASE=FreeAgent XTreme 640GB + +usb:v0BC2p3312* + ID_MODEL_FROM_DATABASE=SRD00F2 Expansion Desktop Drive (STBV) + +usb:v0BC2p3320* + ID_MODEL_FROM_DATABASE=SRD00F2 [Expansion Desktop Drive] + +usb:v0BC2p3332* + ID_MODEL_FROM_DATABASE=Expansion + +usb:v0BC2p5020* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex + +usb:v0BC2p5021* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex USB 2.0 + +usb:v0BC2p5030* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex Upgrade Cable STAE104 + +usb:v0BC2p5031* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex USB 3.0 + +usb:v0BC2p5070* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex Desk + +usb:v0BC2p5071* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex Desk + +usb:v0BC2p50A1* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex Desk + +usb:v0BC2p50A5* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex Desk USB 3.0 + +usb:v0BC2p5121* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex + +usb:v0BC2p5161* + ID_MODEL_FROM_DATABASE=FreeAgent GoFlex dock + +usb:v0BC2pA003* + ID_MODEL_FROM_DATABASE=Backup Plus + +usb:v0BC2pA0A1* + ID_MODEL_FROM_DATABASE=Backup Plus Desktop + +usb:v0BC2pA0A4* + ID_MODEL_FROM_DATABASE=Backup Plus Desktop Drive + +usb:v0BC2pAB00* + ID_MODEL_FROM_DATABASE=Slim Portable Drive + +usb:v0BC2pAB20* + ID_MODEL_FROM_DATABASE=Backup Plus Portable Drive + +usb:v0BC2pAB21* + ID_MODEL_FROM_DATABASE=Backup Plus Slim + +usb:v0BC2pAB31* + ID_MODEL_FROM_DATABASE=Backup Plus Desktop Drive (5TB) + +usb:v0BC3* + ID_VENDOR_FROM_DATABASE=IPWireless, Inc. + +usb:v0BC3p0001* + ID_MODEL_FROM_DATABASE=UMTS-TDD (TD-CDMA) modem + +usb:v0BC4* + ID_VENDOR_FROM_DATABASE=Microcube Corp. + +usb:v0BC5* + ID_VENDOR_FROM_DATABASE=JCN Co., Ltd + +usb:v0BC6* + ID_VENDOR_FROM_DATABASE=ExWAY, Inc. + +usb:v0BC7* + ID_VENDOR_FROM_DATABASE=X10 Wireless Technology, Inc. + +usb:v0BC7p0001* + ID_MODEL_FROM_DATABASE=ActiveHome (ACPI-compliant) + +usb:v0BC7p0002* + ID_MODEL_FROM_DATABASE=Firecracker Interface (ACPI-compliant) + +usb:v0BC7p0003* + ID_MODEL_FROM_DATABASE=VGA Video Sender (ACPI-compliant) + +usb:v0BC7p0004* + ID_MODEL_FROM_DATABASE=X10 Receiver + +usb:v0BC7p0005* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p0006* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p0007* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p0008* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p0009* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p000A* + ID_MODEL_FROM_DATABASE=Wireless Transceiver (ACPI-compliant) + +usb:v0BC7p000B* + ID_MODEL_FROM_DATABASE=Transceiver (ACPI-compliant) + +usb:v0BC7p000C* + ID_MODEL_FROM_DATABASE=Transceiver (ACPI-compliant) + +usb:v0BC7p000D* + ID_MODEL_FROM_DATABASE=Transceiver (ACPI-compliant) + +usb:v0BC7p000E* + ID_MODEL_FROM_DATABASE=Transceiver (ACPI-compliant) + +usb:v0BC7p000F* + ID_MODEL_FROM_DATABASE=Transceiver (ACPI-compliant) + +usb:v0BC8* + ID_VENDOR_FROM_DATABASE=Telmax Communications + +usb:v0BC9* + ID_VENDOR_FROM_DATABASE=ECI Telecom, Ltd + +usb:v0BCA* + ID_VENDOR_FROM_DATABASE=Startek Engineering, Inc. + +usb:v0BCB* + ID_VENDOR_FROM_DATABASE=Perfect Technic Enterprise Co., Ltd + +usb:v0BD7* + ID_VENDOR_FROM_DATABASE=Andrew Pargeter & Associates + +usb:v0BD7pA021* + ID_MODEL_FROM_DATABASE=Amptek DP4 multichannel signal analyzer + +usb:v0BDA* + ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Corp. + +usb:v0BDAp0103* + ID_MODEL_FROM_DATABASE=USB 2.0 Card Reader + +usb:v0BDAp0104* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0106* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0107* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0108* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0111* + ID_MODEL_FROM_DATABASE=RTS5111 Card Reader Controller + +usb:v0BDAp0113* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0115* + ID_MODEL_FROM_DATABASE=Mass Storage Device (Multicard Reader) + +usb:v0BDAp0116* + ID_MODEL_FROM_DATABASE=RTS5116 Card Reader Controller + +usb:v0BDAp0117* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0118* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0119* + ID_MODEL_FROM_DATABASE=Storage Device (SD card reader) + +usb:v0BDAp0129* + ID_MODEL_FROM_DATABASE=RTS5129 Card Reader Controller + +usb:v0BDAp0138* + ID_MODEL_FROM_DATABASE=RTS5138 Card Reader Controller + +usb:v0BDAp0139* + ID_MODEL_FROM_DATABASE=RTS5139 Card Reader Controller + +usb:v0BDAp0151* + ID_MODEL_FROM_DATABASE=Mass Storage Device (Multicard Reader) + +usb:v0BDAp0152* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0153* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0156* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0157* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0158* + ID_MODEL_FROM_DATABASE=USB 2.0 multicard reader + +usb:v0BDAp0159* + ID_MODEL_FROM_DATABASE=RTS5159 Card Reader Controller + +usb:v0BDAp0161* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0168* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0169* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0171* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0176* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0178* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0BDAp0179* + ID_MODEL_FROM_DATABASE=RTL8188ETV Wireless LAN 802.11n Network Adapter + +usb:v0BDAp0184* + ID_MODEL_FROM_DATABASE=RTS5182 Card Reader + +usb:v0BDAp0186* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0BDAp0301* + ID_MODEL_FROM_DATABASE=multicard reader + +usb:v0BDAp1724* + ID_MODEL_FROM_DATABASE=RTL8723AU 802.11n WLAN Adapter + +usb:v0BDAp2831* + ID_MODEL_FROM_DATABASE=RTL2831U DVB-T + +usb:v0BDAp2832* + ID_MODEL_FROM_DATABASE=RTL2832U DVB-T + +usb:v0BDAp2838* + ID_MODEL_FROM_DATABASE=RTL2838 DVB-T + +usb:v0BDAp5401* + ID_MODEL_FROM_DATABASE=RTL 8153 USB 3.0 hub with gigabit ethernet + +usb:v0BDAp5730* + ID_MODEL_FROM_DATABASE=HP 2.0MP High Definition Webcam + +usb:v0BDAp5775* + ID_MODEL_FROM_DATABASE=HP "Truevision HD" laptop camera + +usb:v0BDAp8150* + ID_MODEL_FROM_DATABASE=RTL8150 Fast Ethernet Adapter + +usb:v0BDAp8151* + ID_MODEL_FROM_DATABASE=RTL8151 Adapteon Business Mobile Networks BV + +usb:v0BDAp8171* + ID_MODEL_FROM_DATABASE=RTL8188SU 802.11n WLAN Adapter + +usb:v0BDAp8172* + ID_MODEL_FROM_DATABASE=RTL8191SU 802.11n WLAN Adapter + +usb:v0BDAp8174* + ID_MODEL_FROM_DATABASE=RTL8192SU 802.11n WLAN Adapter + +usb:v0BDAp8176* + ID_MODEL_FROM_DATABASE=RTL8188CUS 802.11n WLAN Adapter + +usb:v0BDAp8178* + ID_MODEL_FROM_DATABASE=RTL8192CU 802.11n WLAN Adapter + +usb:v0BDAp8179* + ID_MODEL_FROM_DATABASE=RTL8188EUS 802.11n Wireless Network Adapter + +usb:v0BDAp817F* + ID_MODEL_FROM_DATABASE=RTL8188RU 802.11n WLAN Adapter + +usb:v0BDAp8187* + ID_MODEL_FROM_DATABASE=RTL8187 Wireless Adapter + +usb:v0BDAp8189* + ID_MODEL_FROM_DATABASE=RTL8187B Wireless 802.11g 54Mbps Network Adapter + +usb:v0BDAp8192* + ID_MODEL_FROM_DATABASE=RTL8191SU 802.11n Wireless Adapter + +usb:v0BDAp8193* + ID_MODEL_FROM_DATABASE=RTL8192DU 802.11an WLAN Adapter + +usb:v0BDAp8197* + ID_MODEL_FROM_DATABASE=RTL8187B Wireless Adapter + +usb:v0BDAp8198* + ID_MODEL_FROM_DATABASE=RTL8187B Wireless Adapter + +usb:v0BDAp8199* + ID_MODEL_FROM_DATABASE=RTL8187SU 802.11g WLAN Adapter + +usb:v0BDAp8812* + ID_MODEL_FROM_DATABASE=RTL8812AU 802.11a/b/g/n/ac WLAN Adapter + +usb:v0BDB* + ID_VENDOR_FROM_DATABASE=Ericsson Business Mobile Networks BV + +usb:v0BDBp1000* + ID_MODEL_FROM_DATABASE=BV Bluetooth Device + +usb:v0BDBp1002* + ID_MODEL_FROM_DATABASE=Bluetooth Device 1.2 + +usb:v0BDBp1049* + ID_MODEL_FROM_DATABASE=C3607w Mobile Broadband Module + +usb:v0BDBp1900* + ID_MODEL_FROM_DATABASE=F3507g Mobile Broadband Module + +usb:v0BDBp1902* + ID_MODEL_FROM_DATABASE=F3507g v2 Mobile Broadband Module + +usb:v0BDBp1904* + ID_MODEL_FROM_DATABASE=F3607gw Mobile Broadband Module + +usb:v0BDBp1905* + ID_MODEL_FROM_DATABASE=F3607gw v2 Mobile Broadband Module + +usb:v0BDBp1906* + ID_MODEL_FROM_DATABASE=F3607gw v3 Mobile Broadband Module + +usb:v0BDBp1909* + ID_MODEL_FROM_DATABASE=F3307 v2 Mobile Broadband Module + +usb:v0BDBp190A* + ID_MODEL_FROM_DATABASE=F3307 Mobile Broadband Module + +usb:v0BDBp190B* + ID_MODEL_FROM_DATABASE=C3607w v2 Mobile Broadband Module + +usb:v0BDC* + ID_VENDOR_FROM_DATABASE=Y Media Corp. + +usb:v0BDD* + ID_VENDOR_FROM_DATABASE=Orange PCS + +usb:v0BE2* + ID_VENDOR_FROM_DATABASE=Kanda Tsushin Kogyo Co., Ltd + +usb:v0BE3* + ID_VENDOR_FROM_DATABASE=TOYO Corp. + +usb:v0BE4* + ID_VENDOR_FROM_DATABASE=Elka International, Ltd + +usb:v0BE5* + ID_VENDOR_FROM_DATABASE=DOME imaging systems, Inc. + +usb:v0BE6* + ID_VENDOR_FROM_DATABASE=Dong Guan Humen Wonderful Wire Cable Factory + +usb:v0BED* + ID_VENDOR_FROM_DATABASE=MEI + +usb:v0BEDp1100* + ID_MODEL_FROM_DATABASE=CASHFLOW SC + +usb:v0BEDp1101* + ID_MODEL_FROM_DATABASE=Series 2000 Combo Acceptor + +usb:v0BEE* + ID_VENDOR_FROM_DATABASE=LTK Industries, Ltd + +usb:v0BEF* + ID_VENDOR_FROM_DATABASE=Way2Call Communications + +usb:v0BF0* + ID_VENDOR_FROM_DATABASE=Pace Micro Technology PLC + +usb:v0BF1* + ID_VENDOR_FROM_DATABASE=Intracom S.A. + +usb:v0BF1p0001* + ID_MODEL_FROM_DATABASE=netMod Driver Ver 2.4.17 (CAPI) + +usb:v0BF1p0002* + ID_MODEL_FROM_DATABASE=netMod Driver Ver 2.4 (CAPI) + +usb:v0BF1p0003* + ID_MODEL_FROM_DATABASE=netMod Driver Ver 2.4 (CAPI) + +usb:v0BF2* + ID_VENDOR_FROM_DATABASE=Konexx + +usb:v0BF6* + ID_VENDOR_FROM_DATABASE=Addonics Technologies, Inc. + +usb:v0BF6p0103* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v0BF6p1234* + ID_MODEL_FROM_DATABASE=Storage Device + +usb:v0BF6pA000* + ID_MODEL_FROM_DATABASE=Cable 205 (TPP) + +usb:v0BF6pA001* + ID_MODEL_FROM_DATABASE=Cable 205 + +usb:v0BF6pA002* + ID_MODEL_FROM_DATABASE=IDE Bridge + +usb:v0BF7* + ID_VENDOR_FROM_DATABASE=Sunny Giken, Inc. + +usb:v0BF8* + ID_VENDOR_FROM_DATABASE=Fujitsu Siemens Computers + +usb:v0BF8p1001* + ID_MODEL_FROM_DATABASE=Fujitsu Pocket Loox 600 PDA + +usb:v0BF8p1006* + ID_MODEL_FROM_DATABASE=SmartCard Reader 2A + +usb:v0BF8p1007* + ID_MODEL_FROM_DATABASE=Connect2Air E-5400 802.11g Wireless Adapter + +usb:v0BF8p1009* + ID_MODEL_FROM_DATABASE=Connect2Air E-5400 D1700 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v0BF8p100C* + ID_MODEL_FROM_DATABASE=Keyboard FSC KBPC PX + +usb:v0BF8p100F* + ID_MODEL_FROM_DATABASE=miniCard D2301 802.11bg Wireless Module [SiS 163U] + +usb:v0BF8p1017* + ID_MODEL_FROM_DATABASE=Keyboard KB SCR + +usb:v0BFD* + ID_VENDOR_FROM_DATABASE=Kvaser AB + +usb:v0BFDp0004* + ID_MODEL_FROM_DATABASE=USBcan II + +usb:v0BFDp000B* + ID_MODEL_FROM_DATABASE=Leaf Light HS + +usb:v0BFDp000E* + ID_MODEL_FROM_DATABASE=Leaf SemiPro HS + +usb:v0C04* + ID_VENDOR_FROM_DATABASE=MOTO Development Group, Inc. + +usb:v0C05* + ID_VENDOR_FROM_DATABASE=Appian Graphics + +usb:v0C06* + ID_VENDOR_FROM_DATABASE=Hasbro Games, Inc. + +usb:v0C07* + ID_VENDOR_FROM_DATABASE=Infinite Data Storage, Ltd + +usb:v0C08* + ID_VENDOR_FROM_DATABASE=Agate + +usb:v0C08p0378* + ID_MODEL_FROM_DATABASE=Q 16MB Storage Device + +usb:v0C09* + ID_VENDOR_FROM_DATABASE=Comjet Information System + +usb:v0C09pA5A5* + ID_MODEL_FROM_DATABASE=Litto Version USB2.0 + +usb:v0C0A* + ID_VENDOR_FROM_DATABASE=Highpoint Technologies, Inc. + +usb:v0C0B* + ID_VENDOR_FROM_DATABASE=Dura Micro, Inc. (Acomdata) + +usb:v0C0Bp27CB* + ID_MODEL_FROM_DATABASE=6-in-1 Flash Reader and Writer + +usb:v0C0Bp27D7* + ID_MODEL_FROM_DATABASE=Multi Memory reader/writer MD-005 + +usb:v0C0Bp27DA* + ID_MODEL_FROM_DATABASE=Multi Memory reader/writer MD-005 + +usb:v0C0Bp27DC* + ID_MODEL_FROM_DATABASE=Multi Memory reader/writer MD-005 + +usb:v0C0Bp27E7* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v0C0Bp27EE* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v0C0Bp2814* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v0C0Bp2815* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v0C0Bp281D* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v0C0Bp5FAB* + ID_MODEL_FROM_DATABASE=Storage Adaptor + +usb:v0C0BpA109* + ID_MODEL_FROM_DATABASE=CF/SM Reader and Writer + +usb:v0C0BpA10C* + ID_MODEL_FROM_DATABASE=SD/MS Reader and Writer + +usb:v0C0BpB001* + ID_MODEL_FROM_DATABASE=USB 2.0 Mass Storage IDE adapter + +usb:v0C0BpB004* + ID_MODEL_FROM_DATABASE=MMC/SD Reader and Writer + +usb:v0C12* + ID_VENDOR_FROM_DATABASE=Zeroplus + +usb:v0C12p0005* + ID_MODEL_FROM_DATABASE=PSX Vibration Feedback Converter + +usb:v0C12p0030* + ID_MODEL_FROM_DATABASE=PSX Vibration Feedback Converter + +usb:v0C12p700E* + ID_MODEL_FROM_DATABASE=Logic Analyzer (LAP-C-16032) + +usb:v0C12p8801* + ID_MODEL_FROM_DATABASE=Xbox Controller + +usb:v0C12p8802* + ID_MODEL_FROM_DATABASE=Xbox Controller + +usb:v0C12p8809* + ID_MODEL_FROM_DATABASE=Red Octane Ignition Xbox DDR Pad + +usb:v0C12p880A* + ID_MODEL_FROM_DATABASE=Pelican Eclipse PL-2023 + +usb:v0C12p8810* + ID_MODEL_FROM_DATABASE=Xbox Controller + +usb:v0C12p9902* + ID_MODEL_FROM_DATABASE=VibraX + +usb:v0C15* + ID_VENDOR_FROM_DATABASE=Iris Graphics + +usb:v0C16* + ID_VENDOR_FROM_DATABASE=Gyration, Inc. + +usb:v0C16p0002* + ID_MODEL_FROM_DATABASE=RF Technology Receiver + +usb:v0C16p0003* + ID_MODEL_FROM_DATABASE=RF Technology Receiver + +usb:v0C16p0008* + ID_MODEL_FROM_DATABASE=RF Technology Receiver + +usb:v0C16p0080* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0C16p0081* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0C17* + ID_VENDOR_FROM_DATABASE=Cyberboard A/S + +usb:v0C18* + ID_VENDOR_FROM_DATABASE=SynerTek Korea, Inc. + +usb:v0C19* + ID_VENDOR_FROM_DATABASE=cyberPIXIE, Inc. + +usb:v0C1A* + ID_VENDOR_FROM_DATABASE=Silicon Motion, Inc. + +usb:v0C1B* + ID_VENDOR_FROM_DATABASE=MIPS Technologies + +usb:v0C1C* + ID_VENDOR_FROM_DATABASE=Hang Zhou Silan Electronics Co., Ltd + +usb:v0C22* + ID_VENDOR_FROM_DATABASE=Tally Printer Corp. + +usb:v0C23* + ID_VENDOR_FROM_DATABASE=Lernout + Hauspie + +usb:v0C24* + ID_VENDOR_FROM_DATABASE=Taiyo Yuden + +usb:v0C24p0001* + ID_MODEL_FROM_DATABASE=Bluetooth Adaptor + +usb:v0C24p0002* + ID_MODEL_FROM_DATABASE=Bluetooth Device2 + +usb:v0C24p0005* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p000B* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p000C* + ID_MODEL_FROM_DATABASE=Bluetooth Adaptor + +usb:v0C24p000E* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p000F* + ID_MODEL_FROM_DATABASE=Bluetooth Device (V2.0+EDR) + +usb:v0C24p0010* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p0012* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p0018* + ID_MODEL_FROM_DATABASE=Bluetooth Device(BC04-External) + +usb:v0C24p0019* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0C24p0021* + ID_MODEL_FROM_DATABASE=Bluetooth Device (V2.1+EDR) + +usb:v0C24p0C24* + ID_MODEL_FROM_DATABASE=Bluetooth Device(SAMPLE) + +usb:v0C24pFFFF* + ID_MODEL_FROM_DATABASE=Bluetooth module with BlueCore in DFU mode + +usb:v0C25* + ID_VENDOR_FROM_DATABASE=Sampo Corp. + +usb:v0C25p0310* + ID_MODEL_FROM_DATABASE=Scream Cam + +usb:v0C26* + ID_VENDOR_FROM_DATABASE=Prolific Technology Inc. + +usb:v0C26p0018* + ID_MODEL_FROM_DATABASE=USB-Serial Controller [Icom Inc. OPC-478UC] + +usb:v0C27* + ID_VENDOR_FROM_DATABASE=RFIDeas, Inc + +usb:v0C27p3BFA* + ID_MODEL_FROM_DATABASE=pcProx Card Reader + +usb:v0C2E* + ID_VENDOR_FROM_DATABASE=Metrologic Instruments + +usb:v0C2Ep0007* + ID_MODEL_FROM_DATABASE=Metrologic MS7120 Barcode Scanner (IBM SurePOS mode) + +usb:v0C2Ep0200* + ID_MODEL_FROM_DATABASE=MS7120 Barcode Scanner + +usb:v0C2Ep0204* + ID_MODEL_FROM_DATABASE=Metrologic MS7120 Barcode Scanner (keyboard mode) + +usb:v0C2Ep0206* + ID_MODEL_FROM_DATABASE=Metrologic MS4980 Barcode Scanner + +usb:v0C2Ep0700* + ID_MODEL_FROM_DATABASE=Metrologic MS7120 Barcode Scanner (uni-directional serial mode) + +usb:v0C2Ep0720* + ID_MODEL_FROM_DATABASE=Metrologic MS7120 Barcode Scanner (bi-directional serial mode) + +usb:v0C2Ep0B61* + ID_MODEL_FROM_DATABASE=Vuquest 3310g + +usb:v0C2Ep0B6A* + ID_MODEL_FROM_DATABASE=Vuquest 3310 Area-Imaging Scanner + +usb:v0C2Ep0B81* + ID_MODEL_FROM_DATABASE=Barcode scanner Voyager 1400g Series + +usb:v0C35* + ID_VENDOR_FROM_DATABASE=Eagletron, Inc. + +usb:v0C36* + ID_VENDOR_FROM_DATABASE=E Ink Corp. + +usb:v0C37* + ID_VENDOR_FROM_DATABASE=e.Digital + +usb:v0C38* + ID_VENDOR_FROM_DATABASE=Der An Electric Wire & Cable Co., Ltd + +usb:v0C39* + ID_VENDOR_FROM_DATABASE=IFR + +usb:v0C3A* + ID_VENDOR_FROM_DATABASE=Furui Precise Component (Kunshan) Co., Ltd + +usb:v0C3B* + ID_VENDOR_FROM_DATABASE=Komatsu, Ltd + +usb:v0C3C* + ID_VENDOR_FROM_DATABASE=Radius Co., Ltd + +usb:v0C3D* + ID_VENDOR_FROM_DATABASE=Innocom, Inc. + +usb:v0C3E* + ID_VENDOR_FROM_DATABASE=Nextcell, Inc. + +usb:v0C44* + ID_VENDOR_FROM_DATABASE=Motorola iDEN + +usb:v0C44p0021* + ID_MODEL_FROM_DATABASE=iDEN P2k0 Device + +usb:v0C44p0022* + ID_MODEL_FROM_DATABASE=iDEN P2k1 Device + +usb:v0C44p03A2* + ID_MODEL_FROM_DATABASE=iDEN Smartphone + +usb:v0C44p41D9* + ID_MODEL_FROM_DATABASE=i1 phone + +usb:v0C45* + ID_VENDOR_FROM_DATABASE=Microdia + +usb:v0C45p0011* + ID_MODEL_FROM_DATABASE=EBUDDY + +usb:v0C45p0520* + ID_MODEL_FROM_DATABASE=MaxTrack Wireless Mouse + +usb:v0C45p1018* + ID_MODEL_FROM_DATABASE=Compact Flash storage memory card reader + +usb:v0C45p1020* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1028* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1030* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1031* + ID_MODEL_FROM_DATABASE=Sonix Mass Storage Device + +usb:v0C45p1032* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1033* + ID_MODEL_FROM_DATABASE=Sonix Mass Storage Device + +usb:v0C45p1034* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1035* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1036* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1037* + ID_MODEL_FROM_DATABASE=Sonix Mass Storage Device + +usb:v0C45p1050* + ID_MODEL_FROM_DATABASE=CF Card Reader + +usb:v0C45p1058* + ID_MODEL_FROM_DATABASE=HDD Reader + +usb:v0C45p1060* + ID_MODEL_FROM_DATABASE=iFlash SM-Direct Card Reader + +usb:v0C45p1061* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1062* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1063* + ID_MODEL_FROM_DATABASE=Sonix Mass Storage Device + +usb:v0C45p1064* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1065* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1066* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1067* + ID_MODEL_FROM_DATABASE=Mass Storage Reader + +usb:v0C45p1158* + ID_MODEL_FROM_DATABASE=A56AK + +usb:v0C45p184C* + ID_MODEL_FROM_DATABASE=VoIP Phone + +usb:v0C45p6001* + ID_MODEL_FROM_DATABASE=Genius VideoCAM NB + +usb:v0C45p6005* + ID_MODEL_FROM_DATABASE=Sweex Mini Webcam + +usb:v0C45p6007* + ID_MODEL_FROM_DATABASE=VideoCAM Eye + +usb:v0C45p6009* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p600D* + ID_MODEL_FROM_DATABASE=TwinkleCam USB camera + +usb:v0C45p6011* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C102) + +usb:v0C45p6019* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C102) + +usb:v0C45p6024* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p6025* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p6028* + ID_MODEL_FROM_DATABASE=Typhoon Easycam USB 330K (older) + +usb:v0C45p6029* + ID_MODEL_FROM_DATABASE=Triplex i-mini PC Camera + +usb:v0C45p602A* + ID_MODEL_FROM_DATABASE=Meade ETX-105EC Camera + +usb:v0C45p602B* + ID_MODEL_FROM_DATABASE=VideoCAM NB 300 + +usb:v0C45p602C* + ID_MODEL_FROM_DATABASE=Clas Ohlson TWC-30XOP Webcam + +usb:v0C45p602D* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p602E* + ID_MODEL_FROM_DATABASE=VideoCAM Messenger + +usb:v0C45p6030* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p603F* + ID_MODEL_FROM_DATABASE=VideoCAM ExpressII + +usb:v0C45p6040* + ID_MODEL_FROM_DATABASE=CCD PC Camera (PC390A) + +usb:v0C45p606A* + ID_MODEL_FROM_DATABASE=CCD PC Camera (PC390A) + +usb:v0C45p607A* + ID_MODEL_FROM_DATABASE=CCD PC Camera (PC390A) + +usb:v0C45p607B* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p607C* + ID_MODEL_FROM_DATABASE=CCD PC Camera (PC390A) + +usb:v0C45p607E* + ID_MODEL_FROM_DATABASE=CCD PC Camera (PC390A) + +usb:v0C45p6080* + ID_MODEL_FROM_DATABASE=Audio (Microphone) + +usb:v0C45p6082* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p6083* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p608C* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p608E* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p608F* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C103 + OV7630) + +usb:v0C45p60A8* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p60AA* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p60AB* + ID_MODEL_FROM_DATABASE=PC Camera + +usb:v0C45p60AF* + ID_MODEL_FROM_DATABASE=VideoCAM Look + +usb:v0C45p60B0* + ID_MODEL_FROM_DATABASE=Genius VideoCam Look + +usb:v0C45p60C0* + ID_MODEL_FROM_DATABASE=PC Camera with Mic (SN9C105) + +usb:v0C45p60C8* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p60CC* + ID_MODEL_FROM_DATABASE=PC Camera with Mic (SN9C105) + +usb:v0C45p60EC* + ID_MODEL_FROM_DATABASE=PC Camera with Mic (SN9C105) + +usb:v0C45p60EF* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p60FA* + ID_MODEL_FROM_DATABASE=PC Camera with Mic (SN9C105) + +usb:v0C45p60FB* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v0C45p60FC* + ID_MODEL_FROM_DATABASE=PC Camera with Mic (SN9C105) + +usb:v0C45p60FE* + ID_MODEL_FROM_DATABASE=Audio (Microphone) + +usb:v0C45p6108* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p6122* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C110) + +usb:v0C45p6123* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C110) + +usb:v0C45p6128* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C325 + OM6802) + +usb:v0C45p612A* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C325) + +usb:v0C45p612C* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C110) + +usb:v0C45p612E* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C110) + +usb:v0C45p612F* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C110) + +usb:v0C45p6130* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C120) + +usb:v0C45p6138* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p613A* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C120) + +usb:v0C45p613B* + ID_MODEL_FROM_DATABASE=Win2 PC Camera + +usb:v0C45p613C* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C120) + +usb:v0C45p613E* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C120) + +usb:v0C45p6143* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C120 + SP80708) + +usb:v0C45p6240* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + MI1300) + +usb:v0C45p6242* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + MI1310) + +usb:v0C45p6243* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + S5K4AAFX) + +usb:v0C45p6248* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV9655) + +usb:v0C45p624B* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + CX1332) + +usb:v0C45p624C* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + MI1320) + +usb:v0C45p624E* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + SOI968) + +usb:v0C45p624F* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV9650) + +usb:v0C45p6251* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV9650) + +usb:v0C45p6253* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV9650) + +usb:v0C45p6260* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV7670ISP) + +usb:v0C45p6262* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OM6802) + +usb:v0C45p6270* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + MI0360/MT9V011 or MI0360SOC/MT9V111) U-CAM PC Camera NE878, Whitcom WHC017, ... + +usb:v0C45p627A* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + S5K53BEB) + +usb:v0C45p627B* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV7660) + +usb:v0C45p627C* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + HV7131R) + +usb:v0C45p627F* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV965x + EEPROM) + +usb:v0C45p6280* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + MI1300) + +usb:v0C45p6282* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + MI1310) + +usb:v0C45p6283* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + S5K4AAFX) + +usb:v0C45p6288* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV9655) + +usb:v0C45p628A* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + ICM107) + +usb:v0C45p628B* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + CX1332) + +usb:v0C45p628C* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + MI1320) + +usb:v0C45p628E* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + SOI968) + +usb:v0C45p628F* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV9650) + +usb:v0C45p62A0* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV7670ISP) + +usb:v0C45p62A2* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OM6802) + +usb:v0C45p62B0* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + MI0360/MT9V011 or MI0360SOC/MT9V111) + +usb:v0C45p62B3* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV9655) + +usb:v0C45p62BA* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + S5K53BEB) + +usb:v0C45p62BB* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV7660) + +usb:v0C45p62BC* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + HV7131R) + +usb:v0C45p62BE* + ID_MODEL_FROM_DATABASE=PC Camera with Microphone (SN9C202 + OV7663) + +usb:v0C45p62C0* + ID_MODEL_FROM_DATABASE=Sonix USB 2.0 Camera + +usb:v0C45p62E0* + ID_MODEL_FROM_DATABASE=MSI Starcam Racer + +usb:v0C45p6300* + ID_MODEL_FROM_DATABASE=PC Microscope camera + +usb:v0C45p6310* + ID_MODEL_FROM_DATABASE=Sonix USB 2.0 Camera + +usb:v0C45p6340* + ID_MODEL_FROM_DATABASE=Camera + +usb:v0C45p6341* + ID_MODEL_FROM_DATABASE=Defender G-Lens 2577 HD720p Camera + +usb:v0C45p63E0* + ID_MODEL_FROM_DATABASE=Sonix Integrated Webcam + +usb:v0C45p63F1* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v0C45p63F8* + ID_MODEL_FROM_DATABASE=Sonix Integrated Webcam + +usb:v0C45p6409* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v0C45p6413* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v0C45p6417* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v0C45p6419* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v0C45p641D* + ID_MODEL_FROM_DATABASE=1.3 MPixel Integrated Webcam + +usb:v0C45p643F* + ID_MODEL_FROM_DATABASE=Dell Integrated HD Webcam + +usb:v0C45p644D* + ID_MODEL_FROM_DATABASE=1.3 MPixel Integrated Webcam + +usb:v0C45p6480* + ID_MODEL_FROM_DATABASE=Sonix 1.3 MP Laptop Integrated Webcam + +usb:v0C45p648B* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v0C45p64BD* + ID_MODEL_FROM_DATABASE=Sony Visual Communication Camera + +usb:v0C45p7401* + ID_MODEL_FROM_DATABASE=TEMPer Temperature Sensor + +usb:v0C45p7402* + ID_MODEL_FROM_DATABASE=TEMPerHUM Temperature & Humidity Sensor + +usb:v0C45p7403* + ID_MODEL_FROM_DATABASE=Foot Switch + +usb:v0C45p8000* + ID_MODEL_FROM_DATABASE=DC31VC + +usb:v0C45p8006* + ID_MODEL_FROM_DATABASE=Dual Mode Camera (8006 VGA) + +usb:v0C45p800A* + ID_MODEL_FROM_DATABASE=Vivitar Vivicam3350B + +usb:v0C46* + ID_VENDOR_FROM_DATABASE=WaveRider Communications, Inc. + +usb:v0C4A* + ID_VENDOR_FROM_DATABASE=ALGE-TIMING GmbH + +usb:v0C4Ap0889* + ID_MODEL_FROM_DATABASE=Timy + +usb:v0C4Ap088A* + ID_MODEL_FROM_DATABASE=Timy 2 + +usb:v0C4B* + ID_VENDOR_FROM_DATABASE=Reiner SCT Kartensysteme GmbH + +usb:v0C4Bp0100* + ID_MODEL_FROM_DATABASE=cyberJack e-com/pinpad + +usb:v0C4Bp0300* + ID_MODEL_FROM_DATABASE=cyberJack pinpad(a) + +usb:v0C4Bp0400* + ID_MODEL_FROM_DATABASE=cyberJack e-com(a) + +usb:v0C4Bp0401* + ID_MODEL_FROM_DATABASE=cyberJack pinpad(a2) + +usb:v0C4Bp0500* + ID_MODEL_FROM_DATABASE=cyberJack RFID standard dual interface smartcard reader + +usb:v0C4Bp0501* + ID_MODEL_FROM_DATABASE=cyberJack RFID comfort dual interface smartcard reader + +usb:v0C4Bp0502* + ID_MODEL_FROM_DATABASE=cyberJack compact + +usb:v0C4Bp0504* + ID_MODEL_FROM_DATABASE=cyberJack go / go plus + +usb:v0C4Bp0505* + ID_MODEL_FROM_DATABASE=cyberJack wave + +usb:v0C4Bp9102* + ID_MODEL_FROM_DATABASE=cyberJack RFID basis contactless smartcard reader + +usb:v0C4C* + ID_VENDOR_FROM_DATABASE=Needham's Electronics + +usb:v0C4Cp0021* + ID_MODEL_FROM_DATABASE=EMP-21 Universal Programmer + +usb:v0C52* + ID_VENDOR_FROM_DATABASE=Sealevel Systems, Inc. + +usb:v0C52p2101* + ID_MODEL_FROM_DATABASE=SeaLINK+232 + +usb:v0C52p2102* + ID_MODEL_FROM_DATABASE=SeaLINK+485 + +usb:v0C52p2103* + ID_MODEL_FROM_DATABASE=SeaLINK+232I + +usb:v0C52p2104* + ID_MODEL_FROM_DATABASE=SeaLINK+485I + +usb:v0C52p2211* + ID_MODEL_FROM_DATABASE=SeaPORT+2/232 (Port 1) + +usb:v0C52p2212* + ID_MODEL_FROM_DATABASE=SeaPORT+2/485 (Port 1) + +usb:v0C52p2213* + ID_MODEL_FROM_DATABASE=SeaPORT+2 (Port 1) + +usb:v0C52p2221* + ID_MODEL_FROM_DATABASE=SeaPORT+2/232 (Port 2) + +usb:v0C52p2222* + ID_MODEL_FROM_DATABASE=SeaPORT+2/485 (Port 2) + +usb:v0C52p2223* + ID_MODEL_FROM_DATABASE=SeaPORT+2 (Port 2) + +usb:v0C52p2411* + ID_MODEL_FROM_DATABASE=SeaPORT+4/232 (Port 1) + +usb:v0C52p2412* + ID_MODEL_FROM_DATABASE=SeaPORT+4/485 (Port 1) + +usb:v0C52p2413* + ID_MODEL_FROM_DATABASE=SeaPORT+4 (Port 1) + +usb:v0C52p2421* + ID_MODEL_FROM_DATABASE=SeaPORT+4/232 (Port 2) + +usb:v0C52p2422* + ID_MODEL_FROM_DATABASE=SeaPORT+4/485 (Port 2) + +usb:v0C52p2423* + ID_MODEL_FROM_DATABASE=SeaPORT+4 (Port 2) + +usb:v0C52p2431* + ID_MODEL_FROM_DATABASE=SeaPORT+4/232 (Port 3) + +usb:v0C52p2432* + ID_MODEL_FROM_DATABASE=SeaPORT+4/485 (Port 3) + +usb:v0C52p2433* + ID_MODEL_FROM_DATABASE=SeaPORT+4 (Port 3) + +usb:v0C52p2441* + ID_MODEL_FROM_DATABASE=SeaPORT+4/232 (Port 4) + +usb:v0C52p2442* + ID_MODEL_FROM_DATABASE=SeaPORT+4/485 (Port 4) + +usb:v0C52p2443* + ID_MODEL_FROM_DATABASE=SeaPORT+4 (Port 4) + +usb:v0C52p2811* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 1) + +usb:v0C52p2812* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 1) + +usb:v0C52p2813* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 1) + +usb:v0C52p2821* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 2) + +usb:v0C52p2822* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 2) + +usb:v0C52p2823* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 2) + +usb:v0C52p2831* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 3) + +usb:v0C52p2832* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 3) + +usb:v0C52p2833* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 3) + +usb:v0C52p2841* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 4) + +usb:v0C52p2842* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 4) + +usb:v0C52p2843* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 4) + +usb:v0C52p2851* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 5) + +usb:v0C52p2852* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 5) + +usb:v0C52p2853* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 5) + +usb:v0C52p2861* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 6) + +usb:v0C52p2862* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 6) + +usb:v0C52p2863* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 6) + +usb:v0C52p2871* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 7) + +usb:v0C52p2872* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 7) + +usb:v0C52p2873* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 7) + +usb:v0C52p2881* + ID_MODEL_FROM_DATABASE=SeaLINK+8/232 (Port 8) + +usb:v0C52p2882* + ID_MODEL_FROM_DATABASE=SeaLINK+8/485 (Port 8) + +usb:v0C52p2883* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 8) + +usb:v0C52p9020* + ID_MODEL_FROM_DATABASE=SeaLINK+422 + +usb:v0C52pA02A* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 1+2) + +usb:v0C52pA02B* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 3+4) + +usb:v0C52pA02C* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 5+6) + +usb:v0C52pA02D* + ID_MODEL_FROM_DATABASE=SeaLINK+8 (Port 7+8) + +usb:v0C53* + ID_VENDOR_FROM_DATABASE=ViewPLUS, Inc. + +usb:v0C54* + ID_VENDOR_FROM_DATABASE=Glory, Ltd + +usb:v0C55* + ID_VENDOR_FROM_DATABASE=Spectrum Digital, Inc. + +usb:v0C55p0510* + ID_MODEL_FROM_DATABASE=Spectrum Digital XDS510 JTAG Debugger + +usb:v0C55p0540* + ID_MODEL_FROM_DATABASE=SPI540 + +usb:v0C55p5416* + ID_MODEL_FROM_DATABASE=TMS320C5416 DSK + +usb:v0C55p6416* + ID_MODEL_FROM_DATABASE=TMS320C6416 DDB + +usb:v0C56* + ID_VENDOR_FROM_DATABASE=Billion Bright, Ltd + +usb:v0C57* + ID_VENDOR_FROM_DATABASE=Imaginative Design Operation Co., Ltd + +usb:v0C58* + ID_VENDOR_FROM_DATABASE=Vidar Systems Corp. + +usb:v0C59* + ID_VENDOR_FROM_DATABASE=Dong Guan Shinko Wire Co., Ltd + +usb:v0C5A* + ID_VENDOR_FROM_DATABASE=TRS International Mfg., Inc. + +usb:v0C5E* + ID_VENDOR_FROM_DATABASE=Xytronix Research & Design + +usb:v0C60* + ID_VENDOR_FROM_DATABASE=Apogee Electronics Corp. + +usb:v0C60p0001* + ID_MODEL_FROM_DATABASE=MiniMe + +usb:v0C60p0002* + ID_MODEL_FROM_DATABASE=MiniDAC + +usb:v0C60p0003* + ID_MODEL_FROM_DATABASE=ONE + +usb:v0C60p0004* + ID_MODEL_FROM_DATABASE=GiO + +usb:v0C60p0007* + ID_MODEL_FROM_DATABASE=Duet + +usb:v0C60p0009* + ID_MODEL_FROM_DATABASE=Jam + +usb:v0C60p000A* + ID_MODEL_FROM_DATABASE=Jam Bootloader + +usb:v0C60p000B* + ID_MODEL_FROM_DATABASE=MiC + +usb:v0C60p000C* + ID_MODEL_FROM_DATABASE=MiC Bootloader + +usb:v0C60p8007* + ID_MODEL_FROM_DATABASE=Duet DFU Mode + +usb:v0C62* + ID_VENDOR_FROM_DATABASE=Chant Sincere Co., Ltd + +usb:v0C63* + ID_VENDOR_FROM_DATABASE=Toko, Inc. + +usb:v0C64* + ID_VENDOR_FROM_DATABASE=Signality System Engineering Co., Ltd + +usb:v0C65* + ID_VENDOR_FROM_DATABASE=Eminence Enterprise Co., Ltd + +usb:v0C66* + ID_VENDOR_FROM_DATABASE=Rexon Electronics Corp. + +usb:v0C67* + ID_VENDOR_FROM_DATABASE=Concept Telecom, Ltd + +usb:v0C6A* + ID_VENDOR_FROM_DATABASE=ACS + +usb:v0C6Ap0005* + ID_MODEL_FROM_DATABASE=Color 320 x 240 LCD Display Terminal with Touchscreen + +usb:v0C6C* + ID_VENDOR_FROM_DATABASE=JETI Technische Instrumente GmbH + +usb:v0C6Cp04B2* + ID_MODEL_FROM_DATABASE=Specbos 1201 + +usb:v0C70* + ID_VENDOR_FROM_DATABASE=MCT Elektronikladen + +usb:v0C70p0000* + ID_MODEL_FROM_DATABASE=USB08 Development board + +usb:v0C70p0747* + ID_MODEL_FROM_DATABASE=Eye Movement Recorder [Visagraph]/[ReadAlyzer] + +usb:v0C72* + ID_VENDOR_FROM_DATABASE=PEAK System + +usb:v0C72p000C* + ID_MODEL_FROM_DATABASE=PCAN-USB + +usb:v0C72p000D* + ID_MODEL_FROM_DATABASE=PCAN Pro + +usb:v0C74* + ID_VENDOR_FROM_DATABASE=Optronic Laboratories Inc. + +usb:v0C74p0002* + ID_MODEL_FROM_DATABASE=OL 700-30 Goniometer + +usb:v0C76* + ID_VENDOR_FROM_DATABASE=JMTek, LLC. + +usb:v0C76p0001* + ID_MODEL_FROM_DATABASE=Mass Storage Controller + +usb:v0C76p0002* + ID_MODEL_FROM_DATABASE=Mass Storage Controller + +usb:v0C76p0003* + ID_MODEL_FROM_DATABASE=USBdisk + +usb:v0C76p0004* + ID_MODEL_FROM_DATABASE=Mass Storage Controller + +usb:v0C76p0005* + ID_MODEL_FROM_DATABASE=Transcend Flash disk + +usb:v0C76p0006* + ID_MODEL_FROM_DATABASE=Transcend JetFlash + +usb:v0C76p0007* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0C76p1600* + ID_MODEL_FROM_DATABASE=Ion Quick Play LP turntable + +usb:v0C76p1605* + ID_MODEL_FROM_DATABASE=SSS Headphone Set + +usb:v0C76p1607* + ID_MODEL_FROM_DATABASE=audio controller + +usb:v0C77* + ID_VENDOR_FROM_DATABASE=Sipix Group, Ltd + +usb:v0C77p1001* + ID_MODEL_FROM_DATABASE=SiPix Web2 + +usb:v0C77p1002* + ID_MODEL_FROM_DATABASE=SiPix SC2100 + +usb:v0C77p1010* + ID_MODEL_FROM_DATABASE=SiPix Snap + +usb:v0C77p1011* + ID_MODEL_FROM_DATABASE=SiPix Blink 2 + +usb:v0C77p1015* + ID_MODEL_FROM_DATABASE=SiPix CAMeleon + +usb:v0C78* + ID_VENDOR_FROM_DATABASE=Detto Corp. + +usb:v0C79* + ID_VENDOR_FROM_DATABASE=NuConnex Technologies Pte., Ltd + +usb:v0C7A* + ID_VENDOR_FROM_DATABASE=Wing-Span Enterprise Co., Ltd + +usb:v0C86* + ID_VENDOR_FROM_DATABASE=NDA Technologies, Inc. + +usb:v0C88* + ID_VENDOR_FROM_DATABASE=Kyocera Wireless Corp. + +usb:v0C88p0021* + ID_MODEL_FROM_DATABASE=Handheld + +usb:v0C88p17DA* + ID_MODEL_FROM_DATABASE=Qualcomm Kyocera CDMA Technologies MSM + +usb:v0C89* + ID_VENDOR_FROM_DATABASE=Honda Tsushin Kogyo Co., Ltd + +usb:v0C8A* + ID_VENDOR_FROM_DATABASE=Pathway Connectivity, Inc. + +usb:v0C8B* + ID_VENDOR_FROM_DATABASE=Wavefly Corp. + +usb:v0C8C* + ID_VENDOR_FROM_DATABASE=Coactive Networks + +usb:v0C8D* + ID_VENDOR_FROM_DATABASE=Tempo + +usb:v0C8E* + ID_VENDOR_FROM_DATABASE=Cesscom Co., Ltd + +usb:v0C8Ep6000* + ID_MODEL_FROM_DATABASE=Luxian Series + +usb:v0C8F* + ID_VENDOR_FROM_DATABASE=Applied Microsystems + +usb:v0C94* + ID_VENDOR_FROM_DATABASE=Cryptera + +usb:v0C94pA000* + ID_MODEL_FROM_DATABASE=EPP 1217 + +usb:v0C98* + ID_VENDOR_FROM_DATABASE=Berkshire Products, Inc. + +usb:v0C98p1140* + ID_MODEL_FROM_DATABASE=USB PC Watchdog + +usb:v0C99* + ID_VENDOR_FROM_DATABASE=Innochips Co., Ltd + +usb:v0C9A* + ID_VENDOR_FROM_DATABASE=Hanwool Robotics Corp. + +usb:v0C9B* + ID_VENDOR_FROM_DATABASE=Jobin Yvon, Inc. + +usb:v0C9D* + ID_VENDOR_FROM_DATABASE=SemTek + +usb:v0C9Dp0170* + ID_MODEL_FROM_DATABASE=3873 Manual Insert card reader + +usb:v0CA2* + ID_VENDOR_FROM_DATABASE=Zyfer + +usb:v0CA3* + ID_VENDOR_FROM_DATABASE=Sega Corp. + +usb:v0CA4* + ID_VENDOR_FROM_DATABASE=ST&T Instrument Corp. + +usb:v0CA5* + ID_VENDOR_FROM_DATABASE=BAE Systems Canada, Inc. + +usb:v0CA6* + ID_VENDOR_FROM_DATABASE=Castles Technology Co., Ltd + +usb:v0CA6p0010* + ID_MODEL_FROM_DATABASE=EZUSB PC/SC Smart Card Reader + +usb:v0CA6p0050* + ID_MODEL_FROM_DATABASE=EZ220PU Reader Controller + +usb:v0CA6p1077* + ID_MODEL_FROM_DATABASE=Bludrive Family Smart Card Reader + +usb:v0CA6p107E* + ID_MODEL_FROM_DATABASE=Reader Controller + +usb:v0CA6p2010* + ID_MODEL_FROM_DATABASE=myPad110 PC/SC Smart Card Reader + +usb:v0CA6p3050* + ID_MODEL_FROM_DATABASE=EZ710 Smart Card Reader + +usb:v0CA7* + ID_VENDOR_FROM_DATABASE=Information Systems Laboratories + +usb:v0CAD* + ID_VENDOR_FROM_DATABASE=Motorola CGISS + +usb:v0CADp9001* + ID_MODEL_FROM_DATABASE=PowerPad Pocket PC Device + +usb:v0CAE* + ID_VENDOR_FROM_DATABASE=Ascom Business Systems, Ltd + +usb:v0CAF* + ID_VENDOR_FROM_DATABASE=Buslink + +usb:v0CAFp2507* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v0CAFp2515* + ID_MODEL_FROM_DATABASE=Flash Disk Embedded Hub + +usb:v0CAFp2516* + ID_MODEL_FROM_DATABASE=Flash Disk Security Device + +usb:v0CAFp2517* + ID_MODEL_FROM_DATABASE=Flash Disk Mass Storage Device + +usb:v0CAFp25C7* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v0CAFp3A00* + ID_MODEL_FROM_DATABASE=Hard Drive + +usb:v0CAFp3A20* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0CAFp3ACD* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0CB0* + ID_VENDOR_FROM_DATABASE=Flying Pig Systems + +usb:v0CB1* + ID_VENDOR_FROM_DATABASE=Innovonics, Inc. + +usb:v0CB6* + ID_VENDOR_FROM_DATABASE=Celestix Networks, Pte., Ltd + +usb:v0CB7* + ID_VENDOR_FROM_DATABASE=Singatron Enterprise Co., Ltd + +usb:v0CB8* + ID_VENDOR_FROM_DATABASE=Opticis Co., Ltd + +usb:v0CBA* + ID_VENDOR_FROM_DATABASE=Trust Electronic (Shanghai) Co., Ltd + +usb:v0CBB* + ID_VENDOR_FROM_DATABASE=Shanghai Darong Electronics Co., Ltd + +usb:v0CBC* + ID_VENDOR_FROM_DATABASE=Palmax Technology Co., Ltd + +usb:v0CBCp0101* + ID_MODEL_FROM_DATABASE=Pocket PC P6C + +usb:v0CBCp0201* + ID_MODEL_FROM_DATABASE=Personal Digital Assistant + +usb:v0CBCp0301* + ID_MODEL_FROM_DATABASE=Personal Digital Assistant P6M+ + +usb:v0CBCp0401* + ID_MODEL_FROM_DATABASE=Pocket PC + +usb:v0CBD* + ID_VENDOR_FROM_DATABASE=Pentel Co., Ltd (Electronics Equipment Div.) + +usb:v0CBE* + ID_VENDOR_FROM_DATABASE=Keryx Technologies, Inc. + +usb:v0CBF* + ID_VENDOR_FROM_DATABASE=Union Genius Computer Co., Ltd + +usb:v0CC0* + ID_VENDOR_FROM_DATABASE=Kuon Yi Industrial Corp. + +usb:v0CC1* + ID_VENDOR_FROM_DATABASE=Given Imaging, Ltd + +usb:v0CC2* + ID_VENDOR_FROM_DATABASE=Timex Corp. + +usb:v0CC3* + ID_VENDOR_FROM_DATABASE=Rimage Corp. + +usb:v0CC4* + ID_VENDOR_FROM_DATABASE=emsys GmbH + +usb:v0CC5* + ID_VENDOR_FROM_DATABASE=Sendo + +usb:v0CC6* + ID_VENDOR_FROM_DATABASE=Intermagic Corp. + +usb:v0CC7* + ID_VENDOR_FROM_DATABASE=Kontron Medical AG + +usb:v0CC8* + ID_VENDOR_FROM_DATABASE=Technotools Corp. + +usb:v0CC9* + ID_VENDOR_FROM_DATABASE=BroadMAX Technologies, Inc. + +usb:v0CCA* + ID_VENDOR_FROM_DATABASE=Amphenol + +usb:v0CCB* + ID_VENDOR_FROM_DATABASE=SKNet Co., Ltd + +usb:v0CCC* + ID_VENDOR_FROM_DATABASE=Domex Technology Corp. + +usb:v0CCD* + ID_VENDOR_FROM_DATABASE=TerraTec Electronic GmbH + +usb:v0CCDp0012* + ID_MODEL_FROM_DATABASE=PHASE 26 + +usb:v0CCDp0013* + ID_MODEL_FROM_DATABASE=PHASE 26 + +usb:v0CCDp0014* + ID_MODEL_FROM_DATABASE=PHASE 26 + +usb:v0CCDp0015* + ID_MODEL_FROM_DATABASE=Flash Update for TerraTec PHASE 26 + +usb:v0CCDp0021* + ID_MODEL_FROM_DATABASE=Cameo Grabster 200 + +usb:v0CCDp0023* + ID_MODEL_FROM_DATABASE=Mystify Claw + +usb:v0CCDp0028* + ID_MODEL_FROM_DATABASE=Aureon 5.1 MkII + +usb:v0CCDp0032* + ID_MODEL_FROM_DATABASE=MIDI HUBBLE + +usb:v0CCDp0035* + ID_MODEL_FROM_DATABASE=Miditech Play'n Roll + +usb:v0CCDp0036* + ID_MODEL_FROM_DATABASE=Cinergy 250 Audio + +usb:v0CCDp0037* + ID_MODEL_FROM_DATABASE=Cinergy 250 Audio + +usb:v0CCDp0038* + ID_MODEL_FROM_DATABASE=Cinergy T² DVB-T Receiver + +usb:v0CCDp0039* + ID_MODEL_FROM_DATABASE=Grabster AV 400 + +usb:v0CCDp003B* + ID_MODEL_FROM_DATABASE=Cinergy 400 + +usb:v0CCDp003C* + ID_MODEL_FROM_DATABASE=Grabster AV 250 + +usb:v0CCDp0042* + ID_MODEL_FROM_DATABASE=Cinergy Hybrid T XS + +usb:v0CCDp0043* + ID_MODEL_FROM_DATABASE=Cinergy T XS + +usb:v0CCDp004E* + ID_MODEL_FROM_DATABASE=Cinergy T XS + +usb:v0CCDp004F* + ID_MODEL_FROM_DATABASE=Cinergy Analog XS + +usb:v0CCDp0055* + ID_MODEL_FROM_DATABASE=Cinergy T XE (Version 1, AF9005) + +usb:v0CCDp005C* + ID_MODEL_FROM_DATABASE=Cinergy T² + +usb:v0CCDp0069* + ID_MODEL_FROM_DATABASE=Cinergy T XE (Version 2, AF9015) + +usb:v0CCDp006B* + ID_MODEL_FROM_DATABASE=Cinergy HT PVR (EU) + +usb:v0CCDp0072* + ID_MODEL_FROM_DATABASE=Cinergy Hybrid T + +usb:v0CCDp0077* + ID_MODEL_FROM_DATABASE=Aureon Dual USB + +usb:v0CCDp0078* + ID_MODEL_FROM_DATABASE=Cinergy T XXS + +usb:v0CCDp0086* + ID_MODEL_FROM_DATABASE=Cinergy Hybrid XE + +usb:v0CCDp008E* + ID_MODEL_FROM_DATABASE=Cinergy HTC XS + +usb:v0CCDp0097* + ID_MODEL_FROM_DATABASE=Cinergy T RC MKII + +usb:v0CCDp0099* + ID_MODEL_FROM_DATABASE=AfaTech 9015 [Cinergy T Stick Dual] + +usb:v0CCDp00A5* + ID_MODEL_FROM_DATABASE=Cinergy Hybrid Stick + +usb:v0CCDp00A9* + ID_MODEL_FROM_DATABASE=RTL2838 DVB-T COFDM Demodulator [TerraTec Cinergy T Stick Black] + +usb:v0CCDp00B3* + ID_MODEL_FROM_DATABASE=NOXON DAB/DAB+ Stick + +usb:v0CCDp00E0* + ID_MODEL_FROM_DATABASE=NOXON DAB/DAB+ Stick V2 + +usb:v0CCDp10A7* + ID_MODEL_FROM_DATABASE=TerraTec G3 + +usb:v0CD4* + ID_VENDOR_FROM_DATABASE=Bang Olufsen + +usb:v0CD4p0101* + ID_MODEL_FROM_DATABASE=BeolinkPC2 + +usb:v0CD5* + ID_VENDOR_FROM_DATABASE=LabJack Corporation + +usb:v0CD5p0003* + ID_MODEL_FROM_DATABASE=U3 + +usb:v0CD5p0009* + ID_MODEL_FROM_DATABASE=UE9 + +usb:v0CD7* + ID_VENDOR_FROM_DATABASE=NewChip S.r.l. + +usb:v0CD8* + ID_VENDOR_FROM_DATABASE=JS Digitech, Inc. + +usb:v0CD8p2007* + ID_MODEL_FROM_DATABASE=Smart Card Reader/JSTU-9700 + +usb:v0CD9* + ID_VENDOR_FROM_DATABASE=Hitachi Shin Din Cable, Ltd + +usb:v0CDE* + ID_VENDOR_FROM_DATABASE=Z-Com + +usb:v0CDEp0001* + ID_MODEL_FROM_DATABASE=XI-750 802.11b Wireless Adapter [Atmel AT76C503A] + +usb:v0CDEp0002* + ID_MODEL_FROM_DATABASE=XI-725/726 Prism2.5 802.11b Adapter + +usb:v0CDEp0003* + ID_MODEL_FROM_DATABASE=Sagem 802.11b Dongle + +usb:v0CDEp0004* + ID_MODEL_FROM_DATABASE=Sagem 802.11b Dongle + +usb:v0CDEp0005* + ID_MODEL_FROM_DATABASE=XI-735 Prism3 802.11b Adapter + +usb:v0CDEp0006* + ID_MODEL_FROM_DATABASE=XG-300 802.11b Adapter + +usb:v0CDEp0008* + ID_MODEL_FROM_DATABASE=XG-703A 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v0CDEp0009* + ID_MODEL_FROM_DATABASE=(ZD1211)IEEE 802.11b+g Adapter + +usb:v0CDEp0011* + ID_MODEL_FROM_DATABASE=ZD1211 + +usb:v0CDEp0012* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0CDEp0013* + ID_MODEL_FROM_DATABASE=AR5523 driver (no firmware) + +usb:v0CDEp0014* + ID_MODEL_FROM_DATABASE=NB 802.11g Wireless LAN Adapter(3887A) + +usb:v0CDEp0015* + ID_MODEL_FROM_DATABASE=XG-705A 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v0CDEp0016* + ID_MODEL_FROM_DATABASE=NB 802.11g Wireless LAN Adapter(3887A) + +usb:v0CDEp0018* + ID_MODEL_FROM_DATABASE=NB 802.11a/b/g Wireless LAN Adapter(3887A) + +usb:v0CDEp001A* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v0CDEp001C* + ID_MODEL_FROM_DATABASE=802.11b/g Wireless Network Adapter + +usb:v0CDEp0020* + ID_MODEL_FROM_DATABASE=AG-760A 802.11abg Wireless Adapter [ZyDAS ZD1211B] + +usb:v0CDEp0022* + ID_MODEL_FROM_DATABASE=802.11b/g/n Wireless Network Adapter + +usb:v0CDEp0023* + ID_MODEL_FROM_DATABASE=UB81 802.11bgn + +usb:v0CDEp0025* + ID_MODEL_FROM_DATABASE=802.11b/g/n USB Wireless Network Adapter + +usb:v0CDEp0026* + ID_MODEL_FROM_DATABASE=UB82 802.11abgn + +usb:v0CDEp0027* + ID_MODEL_FROM_DATABASE=Sphairon Homelink 1202 802.11n Wireless Adapter [Atheros AR9170] + +usb:v0CE5* + ID_VENDOR_FROM_DATABASE=Validation Technologies International + +usb:v0CE5p0003* + ID_MODEL_FROM_DATABASE=Matrix + +usb:v0CE9* + ID_VENDOR_FROM_DATABASE=Pico Technology + +usb:v0CE9p1001* + ID_MODEL_FROM_DATABASE=PicoScope3000 series PC Oscilloscope + +usb:v0CE9p1007* + ID_MODEL_FROM_DATABASE=PicoScope 2000 series PC Oscilloscope + +usb:v0CE9p1008* + ID_MODEL_FROM_DATABASE=PicoScope 5000 series PC Oscilloscope + +usb:v0CE9p1009* + ID_MODEL_FROM_DATABASE=PicoScope 4000 series PC Oscilloscope + +usb:v0CE9p100E* + ID_MODEL_FROM_DATABASE=PicoScope 6000 series PC Oscilloscope + +usb:v0CE9p1012* + ID_MODEL_FROM_DATABASE=PicoScope 3000A series PC Oscilloscope + +usb:v0CE9p1016* + ID_MODEL_FROM_DATABASE=PicoScope 2000A series PC Oscilloscope + +usb:v0CE9p1018* + ID_MODEL_FROM_DATABASE=PicoScope 4000A series PC Oscilloscope + +usb:v0CE9p1200* + ID_MODEL_FROM_DATABASE=PicoScope 2000 series PC Oscilloscope + +usb:v0CE9p1201* + ID_MODEL_FROM_DATABASE=PicoScope 3000 series PC Oscilloscope + +usb:v0CE9p1202* + ID_MODEL_FROM_DATABASE=PicoScope 4000 series PC Oscilloscope + +usb:v0CE9p1203* + ID_MODEL_FROM_DATABASE=PicoScope 5000 series PC Oscilloscope + +usb:v0CE9p1204* + ID_MODEL_FROM_DATABASE=PicoScope 6000 series PC Oscilloscope + +usb:v0CE9p1211* + ID_MODEL_FROM_DATABASE=PicoScope 3000 series PC Oscilloscope + +usb:v0CE9p1212* + ID_MODEL_FROM_DATABASE=PicoScope 4000 series PC Oscilloscope + +usb:v0CF1* + ID_VENDOR_FROM_DATABASE=e-Conn Electronic Co., Ltd + +usb:v0CF2* + ID_VENDOR_FROM_DATABASE=ENE Technology, Inc. + +usb:v0CF2p6220* + ID_MODEL_FROM_DATABASE=SD Card Reader (SG361) + +usb:v0CF2p6225* + ID_MODEL_FROM_DATABASE=SD card reader (UB6225) + +usb:v0CF2p6230* + ID_MODEL_FROM_DATABASE=SD Card Reader (UB623X) + +usb:v0CF2p6250* + ID_MODEL_FROM_DATABASE=SD card reader (UB6250) + +usb:v0CF3* + ID_VENDOR_FROM_DATABASE=Atheros Communications, Inc. + +usb:v0CF3p0001* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0CF3p0002* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v0CF3p0003* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0CF3p0004* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v0CF3p0005* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0CF3p0006* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v0CF3p1001* + ID_MODEL_FROM_DATABASE=Thomson TG121N [Atheros AR9001U-(2)NG] + +usb:v0CF3p1002* + ID_MODEL_FROM_DATABASE=TP-Link TL-WN821N v2 / TL-WN822N v1 802.11n [Atheros AR9170] + +usb:v0CF3p1006* + ID_MODEL_FROM_DATABASE=TP-Link TL-WN322G v3 / TL-WN422G v2 802.11g [Atheros AR9271] + +usb:v0CF3p1010* + ID_MODEL_FROM_DATABASE=3Com 3CRUSBN275 802.11abgn Wireless Adapter [Atheros AR9170] + +usb:v0CF3p20FF* + ID_MODEL_FROM_DATABASE=AR7010 (no firmware) + +usb:v0CF3p3000* + ID_MODEL_FROM_DATABASE=AR3011 Bluetooth (no firmware) + +usb:v0CF3p3002* + ID_MODEL_FROM_DATABASE=AR3011 Bluetooth + +usb:v0CF3p3004* + ID_MODEL_FROM_DATABASE=AR3012 Bluetooth 4.0 + +usb:v0CF3p3005* + ID_MODEL_FROM_DATABASE=AR3011 Bluetooth + +usb:v0CF3p3008* + ID_MODEL_FROM_DATABASE=Bluetooth (AR3011) + +usb:v0CF3p7015* + ID_MODEL_FROM_DATABASE=TP-Link TL-WN821N v3 / TL-WN822N v2 802.11n [Atheros AR7010+AR9287] + +usb:v0CF3p9170* + ID_MODEL_FROM_DATABASE=AR9170 802.11n + +usb:v0CF3p9271* + ID_MODEL_FROM_DATABASE=AR9271 802.11n + +usb:v0CF3pB002* + ID_MODEL_FROM_DATABASE=Ubiquiti WiFiStation 802.11n [Atheros AR9271] + +usb:v0CF3pB003* + ID_MODEL_FROM_DATABASE=Ubiquiti WiFiStationEXT 802.11n [Atheros AR9271] + +usb:v0CF4* + ID_VENDOR_FROM_DATABASE=Fomtex Corp. + +usb:v0CF5* + ID_VENDOR_FROM_DATABASE=Cellink Co., Ltd + +usb:v0CF6* + ID_VENDOR_FROM_DATABASE=Compucable Corp. + +usb:v0CF7* + ID_VENDOR_FROM_DATABASE=ishoni Networks + +usb:v0CF8* + ID_VENDOR_FROM_DATABASE=Clarisys, Inc. + +usb:v0CF8p0750* + ID_MODEL_FROM_DATABASE=Claritel-i750 - vp + +usb:v0CF9* + ID_VENDOR_FROM_DATABASE=Central System Research Co., Ltd + +usb:v0CFA* + ID_VENDOR_FROM_DATABASE=Inviso, Inc. + +usb:v0CFC* + ID_VENDOR_FROM_DATABASE=Minolta-QMS, Inc. + +usb:v0CFCp2301* + ID_MODEL_FROM_DATABASE=Magicolor 2300 DL + +usb:v0CFCp2350* + ID_MODEL_FROM_DATABASE=Magicolor 2350EN/3300 + +usb:v0CFCp3100* + ID_MODEL_FROM_DATABASE=Magicolor 3100 + +usb:v0CFCp7300* + ID_MODEL_FROM_DATABASE=Magicolor 5450/5550 + +usb:v0CFF* + ID_VENDOR_FROM_DATABASE=SAFA MEDIA Co., Ltd. + +usb:v0CFFp0320* + ID_MODEL_FROM_DATABASE=SR-380N + +usb:v0D06* + ID_VENDOR_FROM_DATABASE=telos EDV Systementwicklung GmbH + +usb:v0D08* + ID_VENDOR_FROM_DATABASE=UTStarcom + +usb:v0D08p0602* + ID_MODEL_FROM_DATABASE=DV007 [serial] + +usb:v0D08p0603* + ID_MODEL_FROM_DATABASE=DV007 [storage] + +usb:v0D0B* + ID_VENDOR_FROM_DATABASE=Contemporary Controls + +usb:v0D0C* + ID_VENDOR_FROM_DATABASE=Astron Electronics Co., Ltd + +usb:v0D0D* + ID_VENDOR_FROM_DATABASE=MKNet Corp. + +usb:v0D0E* + ID_VENDOR_FROM_DATABASE=Hybrid Networks, Inc. + +usb:v0D0F* + ID_VENDOR_FROM_DATABASE=Feng Shin Cable Co., Ltd + +usb:v0D10* + ID_VENDOR_FROM_DATABASE=Elastic Networks + +usb:v0D10p0001* + ID_MODEL_FROM_DATABASE=StormPort (WDM) + +usb:v0D11* + ID_VENDOR_FROM_DATABASE=Maspro Denkoh Corp. + +usb:v0D12* + ID_VENDOR_FROM_DATABASE=Hansol Electronics, Inc. + +usb:v0D13* + ID_VENDOR_FROM_DATABASE=BMF Corp. + +usb:v0D14* + ID_VENDOR_FROM_DATABASE=Array Comm, Inc. + +usb:v0D15* + ID_VENDOR_FROM_DATABASE=OnStream b.v. + +usb:v0D16* + ID_VENDOR_FROM_DATABASE=Hi-Touch Imaging Technologies Co., Ltd + +usb:v0D16p0001* + ID_MODEL_FROM_DATABASE=PhotoShuttle + +usb:v0D16p0002* + ID_MODEL_FROM_DATABASE=Photo Printer 730 series + +usb:v0D16p0004* + ID_MODEL_FROM_DATABASE=Photo Printer 63xPL/PS + +usb:v0D16p0100* + ID_MODEL_FROM_DATABASE=Photo Printer 63xPL/PS + +usb:v0D16p0102* + ID_MODEL_FROM_DATABASE=Photo Printer 64xPS + +usb:v0D16p0103* + ID_MODEL_FROM_DATABASE=Photo Printer 730 series + +usb:v0D16p0104* + ID_MODEL_FROM_DATABASE=Photo Printer 63xPL/PS + +usb:v0D16p0105* + ID_MODEL_FROM_DATABASE=Photo Printer 64xPS + +usb:v0D16p0200* + ID_MODEL_FROM_DATABASE=Photo Printer 64xDL + +usb:v0D17* + ID_VENDOR_FROM_DATABASE=NALTEC, Inc. + +usb:v0D18* + ID_VENDOR_FROM_DATABASE=coaXmedia + +usb:v0D19* + ID_VENDOR_FROM_DATABASE=Hank Connection Industrial Co., Ltd + +usb:v0D28* + ID_VENDOR_FROM_DATABASE=NXP + +usb:v0D28p0204* + ID_MODEL_FROM_DATABASE=LPC1768 + +usb:v0D32* + ID_VENDOR_FROM_DATABASE=Leo Hui Electric Wire & Cable Co., Ltd + +usb:v0D33* + ID_VENDOR_FROM_DATABASE=AirSpeak, Inc. + +usb:v0D34* + ID_VENDOR_FROM_DATABASE=Rearden Steel Technologies + +usb:v0D35* + ID_VENDOR_FROM_DATABASE=Dah Kun Co., Ltd + +usb:v0D3A* + ID_VENDOR_FROM_DATABASE=Posiflex Technologies, Inc. + +usb:v0D3Ap0206* + ID_MODEL_FROM_DATABASE=Series 3xxx Cash Drawer + +usb:v0D3Ap0207* + ID_MODEL_FROM_DATABASE=Series 3xxx Cash Drawer + +usb:v0D3Ap0500* + ID_MODEL_FROM_DATABASE=Magnetic Stripe Reader + +usb:v0D3C* + ID_VENDOR_FROM_DATABASE=Sri Cable Technology, Ltd + +usb:v0D3D* + ID_VENDOR_FROM_DATABASE=Tangtop Technology Co., Ltd + +usb:v0D3Dp0001* + ID_MODEL_FROM_DATABASE=HID Keyboard + +usb:v0D3Dp0040* + ID_MODEL_FROM_DATABASE=PS/2 Adapter + +usb:v0D3E* + ID_VENDOR_FROM_DATABASE=Fitcom, inc. + +usb:v0D3F* + ID_VENDOR_FROM_DATABASE=MTS Systems Corp. + +usb:v0D40* + ID_VENDOR_FROM_DATABASE=Ascor, Inc. + +usb:v0D41* + ID_VENDOR_FROM_DATABASE=Ta Yun Terminals Industrial Co., Ltd + +usb:v0D42* + ID_VENDOR_FROM_DATABASE=Full Der Co., Ltd + +usb:v0D46* + ID_VENDOR_FROM_DATABASE=Kobil Systems GmbH + +usb:v0D46p2012* + ID_MODEL_FROM_DATABASE=KAAN Standard Plus (Smartcard reader) + +usb:v0D46p3003* + ID_MODEL_FROM_DATABASE=mIDentity Light / KAAN SIM III + +usb:v0D46p4000* + ID_MODEL_FROM_DATABASE=mIDentity (mass storage) + +usb:v0D46p4001* + ID_MODEL_FROM_DATABASE=mIDentity Basic/Classic (composite device) + +usb:v0D46p4081* + ID_MODEL_FROM_DATABASE=mIDentity Basic/Classic (installationless) + +usb:v0D48* + ID_VENDOR_FROM_DATABASE=Promethean Limited + +usb:v0D48p0001* + ID_MODEL_FROM_DATABASE=ACTIVboard + +usb:v0D48p0004* + ID_MODEL_FROM_DATABASE=ACTIVboard + +usb:v0D48p0100* + ID_MODEL_FROM_DATABASE=Audio + +usb:v0D49* + ID_VENDOR_FROM_DATABASE=Maxtor + +usb:v0D49p3000* + ID_MODEL_FROM_DATABASE=Drive + +usb:v0D49p3010* + ID_MODEL_FROM_DATABASE=3000LE Drive + +usb:v0D49p3100* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-IDE Bridge Controller + +usb:v0D49p3200* + ID_MODEL_FROM_DATABASE=Personal Storage 3200 + +usb:v0D49p5000* + ID_MODEL_FROM_DATABASE=5000XT Drive + +usb:v0D49p5010* + ID_MODEL_FROM_DATABASE=5000LE Drive + +usb:v0D49p5020* + ID_MODEL_FROM_DATABASE=Mobile Hard Disk Drive + +usb:v0D49p7000* + ID_MODEL_FROM_DATABASE=OneTouch + +usb:v0D49p7010* + ID_MODEL_FROM_DATABASE=OneTouch + +usb:v0D49p7100* + ID_MODEL_FROM_DATABASE=OneTouch II 300GB External Hard Disk + +usb:v0D49p7310* + ID_MODEL_FROM_DATABASE=OneTouch 4 + +usb:v0D49p7410* + ID_MODEL_FROM_DATABASE=Mobile Hard Disk Drive (1TB) + +usb:v0D49p7450* + ID_MODEL_FROM_DATABASE=Basics Portable USB Device + +usb:v0D4A* + ID_VENDOR_FROM_DATABASE=NF Corp. + +usb:v0D4B* + ID_VENDOR_FROM_DATABASE=Grape Systems, Inc. + +usb:v0D4C* + ID_VENDOR_FROM_DATABASE=Tedas AG + +usb:v0D4D* + ID_VENDOR_FROM_DATABASE=Coherent, Inc. + +usb:v0D4E* + ID_VENDOR_FROM_DATABASE=Agere Systems Netherland BV + +usb:v0D4Ep047A* + ID_MODEL_FROM_DATABASE=WLAN Card + +usb:v0D4Ep1000* + ID_MODEL_FROM_DATABASE=Wireless Card Model 0801 + +usb:v0D4Ep1001* + ID_MODEL_FROM_DATABASE=Wireless Card Model 0802 + +usb:v0D4F* + ID_VENDOR_FROM_DATABASE=EADS Airbus France + +usb:v0D50* + ID_VENDOR_FROM_DATABASE=Cleware GmbH + +usb:v0D50p0011* + ID_MODEL_FROM_DATABASE=USB-Temp2 Thermometer + +usb:v0D50p0040* + ID_MODEL_FROM_DATABASE=F4 foot switch + +usb:v0D51* + ID_VENDOR_FROM_DATABASE=Volex (Asia) Pte., Ltd + +usb:v0D53* + ID_VENDOR_FROM_DATABASE=HMI Co., Ltd + +usb:v0D54* + ID_VENDOR_FROM_DATABASE=Holon Corp. + +usb:v0D55* + ID_VENDOR_FROM_DATABASE=ASKA Technologies, Inc. + +usb:v0D56* + ID_VENDOR_FROM_DATABASE=AVLAB Technology, Inc. + +usb:v0D57* + ID_VENDOR_FROM_DATABASE=Solomon Microtech, Ltd + +usb:v0D5C* + ID_VENDOR_FROM_DATABASE=SMC Networks, Inc. + +usb:v0D5CpA001* + ID_MODEL_FROM_DATABASE=SMC2662W (v1) EZ Connect 802.11b Wireless Adapter [Atmel AT76C503A] + +usb:v0D5CpA002* + ID_MODEL_FROM_DATABASE=SMC2662W v2 / SMC2662W-AR / Belkin F5D6050 [Atmel at76c503a] + +usb:v0D5E* + ID_VENDOR_FROM_DATABASE=Myacom, Ltd + +usb:v0D5Ep2346* + ID_MODEL_FROM_DATABASE=BT Digital Access adapter + +usb:v0D5F* + ID_VENDOR_FROM_DATABASE=CSI, Inc. + +usb:v0D60* + ID_VENDOR_FROM_DATABASE=IVL Technologies, Ltd + +usb:v0D61* + ID_VENDOR_FROM_DATABASE=Meilu Electronics (Shenzhen) Co., Ltd + +usb:v0D62* + ID_VENDOR_FROM_DATABASE=Darfon Electronics Corp. + +usb:v0D62p0003* + ID_MODEL_FROM_DATABASE=Smartcard Reader + +usb:v0D62p0004* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0D62p001B* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v0D62p001C* + ID_MODEL_FROM_DATABASE=Benq X120 Internet Keyboard Pro + +usb:v0D62p0306* + ID_MODEL_FROM_DATABASE=M530 Mouse + +usb:v0D62p0800* + ID_MODEL_FROM_DATABASE=Magic Wheel + +usb:v0D62p2021* + ID_MODEL_FROM_DATABASE=AM805 Keyboard + +usb:v0D62p2026* + ID_MODEL_FROM_DATABASE=TECOM Bluetooth Device + +usb:v0D62p2050* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v0D62p2106* + ID_MODEL_FROM_DATABASE=Dell L20U Multimedia Keyboard + +usb:v0D62pA100* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v0D63* + ID_VENDOR_FROM_DATABASE=Fritz Gegauf AG + +usb:v0D64* + ID_VENDOR_FROM_DATABASE=DXG Technology Corp. + +usb:v0D64p0105* + ID_MODEL_FROM_DATABASE=Dual Mode Digital Camera 1.3M + +usb:v0D64p0107* + ID_MODEL_FROM_DATABASE=Horus MT-409 Camera + +usb:v0D64p0108* + ID_MODEL_FROM_DATABASE=Dual Mode Digital Camera + +usb:v0D64p0202* + ID_MODEL_FROM_DATABASE=Dual Mode Video Camera Device + +usb:v0D64p0303* + ID_MODEL_FROM_DATABASE=DXG-305V Camera + +usb:v0D64p1001* + ID_MODEL_FROM_DATABASE=SiPix Stylecam/UMAX AstraPix 320s + +usb:v0D64p1002* + ID_MODEL_FROM_DATABASE=Fashion Cam 01 Dual-Mode DSC (Video Camera) + +usb:v0D64p1003* + ID_MODEL_FROM_DATABASE=Fashion Cam Dual-Mode DSC (Controller) + +usb:v0D64p1021* + ID_MODEL_FROM_DATABASE=D-Link DSC 350F + +usb:v0D64p1208* + ID_MODEL_FROM_DATABASE=Dual Mode Still Camera Device + +usb:v0D64p2208* + ID_MODEL_FROM_DATABASE=Mass Storage + +usb:v0D64p3105* + ID_MODEL_FROM_DATABASE=Dual Mode Digital Camera Disk + +usb:v0D64p3108* + ID_MODEL_FROM_DATABASE=Digicam Mass Storage Device + +usb:v0D65* + ID_VENDOR_FROM_DATABASE=KMJP Co., Ltd + +usb:v0D66* + ID_VENDOR_FROM_DATABASE=TMT + +usb:v0D67* + ID_VENDOR_FROM_DATABASE=Advanet, Inc. + +usb:v0D68* + ID_VENDOR_FROM_DATABASE=Super Link Electronics Co., Ltd + +usb:v0D69* + ID_VENDOR_FROM_DATABASE=NSI + +usb:v0D6A* + ID_VENDOR_FROM_DATABASE=Megapower International Corp. + +usb:v0D6B* + ID_VENDOR_FROM_DATABASE=And-Or Logic + +usb:v0D70* + ID_VENDOR_FROM_DATABASE=Try Computer Co., Ltd + +usb:v0D71* + ID_VENDOR_FROM_DATABASE=Hirakawa Hewtech Corp. + +usb:v0D72* + ID_VENDOR_FROM_DATABASE=Winmate Communication, Inc. + +usb:v0D73* + ID_VENDOR_FROM_DATABASE=Hit's Communications, Inc. + +usb:v0D76* + ID_VENDOR_FROM_DATABASE=MFP Korea, Inc. + +usb:v0D77* + ID_VENDOR_FROM_DATABASE=Power Sentry/Newpoint + +usb:v0D78* + ID_VENDOR_FROM_DATABASE=Japan Distributor Corp. + +usb:v0D7A* + ID_VENDOR_FROM_DATABASE=MARX Datentechnik GmbH + +usb:v0D7Ap0001* + ID_MODEL_FROM_DATABASE=CrypToken + +usb:v0D7B* + ID_VENDOR_FROM_DATABASE=Wellco Technology Co., Ltd + +usb:v0D7C* + ID_VENDOR_FROM_DATABASE=Taiwan Line Tek Electronic Co., Ltd + +usb:v0D7D* + ID_VENDOR_FROM_DATABASE=Phison Electronics Corp. + +usb:v0D7Dp0100* + ID_MODEL_FROM_DATABASE=PS1001/1011/1006/1026 Flash Disk + +usb:v0D7Dp0110* + ID_MODEL_FROM_DATABASE=Gigabyte FlexDrive + +usb:v0D7Dp0120* + ID_MODEL_FROM_DATABASE=Disk Pro 64MB + +usb:v0D7Dp0124* + ID_MODEL_FROM_DATABASE=GIGABYTE Disk + +usb:v0D7Dp0240* + ID_MODEL_FROM_DATABASE=I/O-Magic/Transcend 6-in-1 Card Reader + +usb:v0D7Dp110E* + ID_MODEL_FROM_DATABASE=NEC uPD720121/130 USB-ATA/ATAPI Bridge + +usb:v0D7Dp1240* + ID_MODEL_FROM_DATABASE=Apacer 6-in-1 Card Reader 2.0 + +usb:v0D7Dp1270* + ID_MODEL_FROM_DATABASE=Wolverine SixPac 6000 + +usb:v0D7Dp1300* + ID_MODEL_FROM_DATABASE=Flash Disk + +usb:v0D7Dp1320* + ID_MODEL_FROM_DATABASE=PS2031 Flash Disk + +usb:v0D7Dp1400* + ID_MODEL_FROM_DATABASE=Attache 256MB USB 2.0 Flash Drive + +usb:v0D7Dp1420* + ID_MODEL_FROM_DATABASE=PS2044 Pen Drive + +usb:v0D7Dp1470* + ID_MODEL_FROM_DATABASE=Vosonic X's-Drive II+ VP2160 + +usb:v0D7Dp1620* + ID_MODEL_FROM_DATABASE=USB Disk Pro + +usb:v0D7Dp1900* + ID_MODEL_FROM_DATABASE=USB Thumb Drive + +usb:v0D7E* + ID_VENDOR_FROM_DATABASE=American Computer & Digital Components + +usb:v0D7Ep2507* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v0D7Ep2517* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v0D7Ep25C7* + ID_MODEL_FROM_DATABASE=Hi-Speed USB-to-IDE Bridge Controller + +usb:v0D7F* + ID_VENDOR_FROM_DATABASE=Essential Reality LLC + +usb:v0D7Fp0100* + ID_MODEL_FROM_DATABASE=P5 Glove glove controller + +usb:v0D80* + ID_VENDOR_FROM_DATABASE=H.R. Silvine Electronics, Inc. + +usb:v0D81* + ID_VENDOR_FROM_DATABASE=TechnoVision + +usb:v0D83* + ID_VENDOR_FROM_DATABASE=Think Outside, Inc. + +usb:v0D87* + ID_VENDOR_FROM_DATABASE=Dolby Laboratories Inc. + +usb:v0D89* + ID_VENDOR_FROM_DATABASE=Oz Software + +usb:v0D8A* + ID_VENDOR_FROM_DATABASE=King Jim Co., Ltd + +usb:v0D8Ap0101* + ID_MODEL_FROM_DATABASE=TEPRA PRO + +usb:v0D8B* + ID_VENDOR_FROM_DATABASE=Ascom Telecommunications, Ltd + +usb:v0D8C* + ID_VENDOR_FROM_DATABASE=C-Media Electronics, Inc. + +usb:v0D8Cp0001* + ID_MODEL_FROM_DATABASE=Audio Device + +usb:v0D8Cp0002* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v0D8Cp0003* + ID_MODEL_FROM_DATABASE=Sound Device + +usb:v0D8Cp0006* + ID_MODEL_FROM_DATABASE=Storm HP-USB500 5.1 Headset + +usb:v0D8Cp000C* + ID_MODEL_FROM_DATABASE=Audio Adapter + +usb:v0D8Cp000D* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v0D8Cp000E* + ID_MODEL_FROM_DATABASE=Audio Adapter (Planet UP-100, Genius G-Talk) + +usb:v0D8Cp001F* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp0102* + ID_MODEL_FROM_DATABASE=CM106 Like Sound Device + +usb:v0D8Cp0103* + ID_MODEL_FROM_DATABASE=CM102-A+/102S+ Audio Controller + +usb:v0D8Cp0104* + ID_MODEL_FROM_DATABASE=CM103+ Audio Controller + +usb:v0D8Cp0105* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp0107* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp010F* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp0115* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp0139* + ID_MODEL_FROM_DATABASE=Multimedia Headset [Gigaware by Ignition L.P.] + +usb:v0D8Cp013C* + ID_MODEL_FROM_DATABASE=CM108 Audio Controller + +usb:v0D8Cp0201* + ID_MODEL_FROM_DATABASE=CM6501 + +usb:v0D8Cp5000* + ID_MODEL_FROM_DATABASE=Mass Storage Controller + +usb:v0D8Cp5200* + ID_MODEL_FROM_DATABASE=Mass Storage Controller(0D8C,5200) + +usb:v0D8CpB213* + ID_MODEL_FROM_DATABASE=USB Phone CM109 (aka CT2000,VPT1000) + +usb:v0D8D* + ID_VENDOR_FROM_DATABASE=Promotion & Display Technology, Ltd + +usb:v0D8Dp0234* + ID_MODEL_FROM_DATABASE=V-234 Composite Device + +usb:v0D8Dp0550* + ID_MODEL_FROM_DATABASE=V-550 Composite Device + +usb:v0D8Dp0551* + ID_MODEL_FROM_DATABASE=V-551 Composite Device + +usb:v0D8Dp0552* + ID_MODEL_FROM_DATABASE=V-552 Composite Device + +usb:v0D8Dp0651* + ID_MODEL_FROM_DATABASE=V-651 Composite Device + +usb:v0D8Dp0652* + ID_MODEL_FROM_DATABASE=V-652 Composite Device + +usb:v0D8Dp0653* + ID_MODEL_FROM_DATABASE=V-653 Composite Device + +usb:v0D8Dp0654* + ID_MODEL_FROM_DATABASE=V-654 Composite Device + +usb:v0D8Dp0655* + ID_MODEL_FROM_DATABASE=V-655 Composite Device + +usb:v0D8Dp0656* + ID_MODEL_FROM_DATABASE=V-656 Composite Device + +usb:v0D8Dp0657* + ID_MODEL_FROM_DATABASE=V-657 Composite Device + +usb:v0D8Dp0658* + ID_MODEL_FROM_DATABASE=V-658 Composite Device + +usb:v0D8Dp0659* + ID_MODEL_FROM_DATABASE=V-659 Composite Device + +usb:v0D8Dp0660* + ID_MODEL_FROM_DATABASE=V-660 Composite Device + +usb:v0D8Dp0661* + ID_MODEL_FROM_DATABASE=V-661 Composite Device + +usb:v0D8Dp0662* + ID_MODEL_FROM_DATABASE=V-662 Composite Device + +usb:v0D8Dp0850* + ID_MODEL_FROM_DATABASE=V-850 Composite Device + +usb:v0D8Dp0851* + ID_MODEL_FROM_DATABASE=V-851 Composite Device + +usb:v0D8Dp0852* + ID_MODEL_FROM_DATABASE=V-852 Composite Device + +usb:v0D8Dp0901* + ID_MODEL_FROM_DATABASE=V-901 Composite Device + +usb:v0D8Dp0902* + ID_MODEL_FROM_DATABASE=V-902 Composite Device + +usb:v0D8Dp0903* + ID_MODEL_FROM_DATABASE=V-903 Composite Device + +usb:v0D8Dp4754* + ID_MODEL_FROM_DATABASE=Voyager DMP Composite Device + +usb:v0D8DpBB00* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpBB01* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpBB02* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpBB03* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpBB04* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpBB05* + ID_MODEL_FROM_DATABASE=Bloomberg Composite Device + +usb:v0D8DpFFFE* + ID_MODEL_FROM_DATABASE=Global Tuner Composite Device + +usb:v0D8DpFFFF* + ID_MODEL_FROM_DATABASE=Voyager DMP Composite Device + +usb:v0D8E* + ID_VENDOR_FROM_DATABASE=Global Sun Technology, Inc. + +usb:v0D8Ep0163* + ID_MODEL_FROM_DATABASE=802.11g 54 Mbps Wireless Dongle + +usb:v0D8Ep1621* + ID_MODEL_FROM_DATABASE=802.11b Wireless Adapter + +usb:v0D8Ep3762* + ID_MODEL_FROM_DATABASE=Cohiba 802.11g Wireless Mini adapter [Intersil ISL3887] + +usb:v0D8Ep3763* + ID_MODEL_FROM_DATABASE=802.11g Wireless dongle + +usb:v0D8Ep7100* + ID_MODEL_FROM_DATABASE=802.11b Adapter + +usb:v0D8Ep7110* + ID_MODEL_FROM_DATABASE=WL-210 / WU210P 802.11b Wireless Adapter [Atmel AT76C503A] + +usb:v0D8Ep7605* + ID_MODEL_FROM_DATABASE=TRENDnet TEW-224UB 802.11b Wireless Adapter [Atmel AT76C503A] + +usb:v0D8Ep7801* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0D8Ep7802* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v0D8Ep7811* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v0D8Ep7812* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v0D8Ep7A01* + ID_MODEL_FROM_DATABASE=PRISM25 802.11b Adapter + +usb:v0D8F* + ID_VENDOR_FROM_DATABASE=Pitney Bowes + +usb:v0D90* + ID_VENDOR_FROM_DATABASE=Sure-Fire Electrical Corp. + +usb:v0D96* + ID_VENDOR_FROM_DATABASE=Skanhex Technology, Inc. + +usb:v0D96p0000* + ID_MODEL_FROM_DATABASE=Jenoptik JD350 video + +usb:v0D96p3300* + ID_MODEL_FROM_DATABASE=SX330z Camera + +usb:v0D96p4100* + ID_MODEL_FROM_DATABASE=SX410z Camera + +usb:v0D96p4102* + ID_MODEL_FROM_DATABASE=MD 9700 Camera + +usb:v0D96p4104* + ID_MODEL_FROM_DATABASE=Jenoptik JD-4100z3s + +usb:v0D96p410A* + ID_MODEL_FROM_DATABASE=Medion 9801/Novatech SX-410z + +usb:v0D96p5200* + ID_MODEL_FROM_DATABASE=SX-520z Camera + +usb:v0D97* + ID_VENDOR_FROM_DATABASE=Santa Barbara Instrument Group + +usb:v0D97p0001* + ID_MODEL_FROM_DATABASE=SBIG Astronomy Camera (without firmware) + +usb:v0D97p0101* + ID_MODEL_FROM_DATABASE=SBIG Astronomy Camera (with firmware) + +usb:v0D98* + ID_VENDOR_FROM_DATABASE=Mars Semiconductor Corp. + +usb:v0D98p0300* + ID_MODEL_FROM_DATABASE=Avaya Wireless Card + +usb:v0D98p1007* + ID_MODEL_FROM_DATABASE=Discovery Kids Digital Camera + +usb:v0D99* + ID_VENDOR_FROM_DATABASE=Trazer Technologies, Inc. + +usb:v0D9A* + ID_VENDOR_FROM_DATABASE=RTX Telecom AS + +usb:v0D9Ap0001* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0D9B* + ID_VENDOR_FROM_DATABASE=Tat Shing Electrical Co. + +usb:v0D9C* + ID_VENDOR_FROM_DATABASE=Chee Chen Hi-Technology Co., Ltd + +usb:v0D9D* + ID_VENDOR_FROM_DATABASE=Sanwa Supply, Inc. + +usb:v0D9E* + ID_VENDOR_FROM_DATABASE=Avaya + +usb:v0D9Ep0300* + ID_MODEL_FROM_DATABASE=Wireless Card + +usb:v0D9F* + ID_VENDOR_FROM_DATABASE=Powercom Co., Ltd + +usb:v0D9Fp0001* + ID_MODEL_FROM_DATABASE=Uninterruptible Power Supply + +usb:v0D9Fp0002* + ID_MODEL_FROM_DATABASE=Black Knight PRO / WOW Uninterruptible Power Supply (Cypress HID->COM RS232) + +usb:v0D9Fp00A2* + ID_MODEL_FROM_DATABASE=Imperial Uninterruptible Power Supply (HID PDC) + +usb:v0D9Fp00A3* + ID_MODEL_FROM_DATABASE=Smart King PRO Uninterruptible Power Supply (HID PDC) + +usb:v0D9Fp00A4* + ID_MODEL_FROM_DATABASE=WOW Uninterruptible Power Supply (HID PDC) + +usb:v0D9Fp00A5* + ID_MODEL_FROM_DATABASE=Vanguard Uninterruptible Power Supply (HID PDC) + +usb:v0D9Fp00A6* + ID_MODEL_FROM_DATABASE=Black Knight PRO Uninterruptible Power Supply (HID PDC) + +usb:v0DA0* + ID_VENDOR_FROM_DATABASE=Danger Research + +usb:v0DA1* + ID_VENDOR_FROM_DATABASE=Suzhou Peter's Precise Industrial Co., Ltd + +usb:v0DA2* + ID_VENDOR_FROM_DATABASE=Land Instruments International, Ltd + +usb:v0DA3* + ID_VENDOR_FROM_DATABASE=Nippon Electro-Sensory Devices Corp. + +usb:v0DA4* + ID_VENDOR_FROM_DATABASE=Polar Electro Oy + +usb:v0DA4p0001* + ID_MODEL_FROM_DATABASE=Interface + +usb:v0DA4p0008* + ID_MODEL_FROM_DATABASE=Loop + +usb:v0DA7* + ID_VENDOR_FROM_DATABASE=IOGear, Inc. + +usb:v0DA8* + ID_VENDOR_FROM_DATABASE=softDSP Co., Ltd + +usb:v0DA8p0001* + ID_MODEL_FROM_DATABASE=SDS 200A Oscilloscope + +usb:v0DAB* + ID_VENDOR_FROM_DATABASE=Cubig Group + +usb:v0DABp0100* + ID_MODEL_FROM_DATABASE=DVR/CVR-M140 MP3 Player + +usb:v0DAD* + ID_VENDOR_FROM_DATABASE=Westover Scientific + +usb:v0DB0* + ID_VENDOR_FROM_DATABASE=Micro Star International + +usb:v0DB0p1020* + ID_MODEL_FROM_DATABASE=PC2PC WLAN Card + +usb:v0DB0p1967* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0DB0p3713* + ID_MODEL_FROM_DATABASE=Primo 73 + +usb:v0DB0p3801* + ID_MODEL_FROM_DATABASE=Motorola Bluetooth 2.1+EDR Device + +usb:v0DB0p4011* + ID_MODEL_FROM_DATABASE=Medion Flash XL V2.0 Card Reader + +usb:v0DB0p4023* + ID_MODEL_FROM_DATABASE=Lexar Mobile Card Reader + +usb:v0DB0p4600* + ID_MODEL_FROM_DATABASE=802.11b/g Turbo Wireless Adapter + +usb:v0DB0p5501* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0DB0p5502* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0DB0p5513* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0DB0p5515* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0DB0p5516* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0DB0p5580* + ID_MODEL_FROM_DATABASE=Mega Sky 580 DVB-T Tuner [M902x] + +usb:v0DB0p5581* + ID_MODEL_FROM_DATABASE=Mega Sky 580 DVB-T Tuner [GL861] + +usb:v0DB0p6823* + ID_MODEL_FROM_DATABASE=UB11B/MS-6823 802.11b Wi-Fi adapter + +usb:v0DB0p6826* + ID_MODEL_FROM_DATABASE=IEEE 802.11g Wireless Network Adapter + +usb:v0DB0p6855* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0DB0p6861* + ID_MODEL_FROM_DATABASE=MSI-6861 802.11g WiFi adapter + +usb:v0DB0p6865* + ID_MODEL_FROM_DATABASE=RT2570 + +usb:v0DB0p6869* + ID_MODEL_FROM_DATABASE=RT2570 + +usb:v0DB0p6874* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0DB0p6877* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0DB0p6881* + ID_MODEL_FROM_DATABASE=Bluetooth Class I EDR Device + +usb:v0DB0p688A* + ID_MODEL_FROM_DATABASE=Bluetooth Class I EDR Device + +usb:v0DB0p6899* + ID_MODEL_FROM_DATABASE=802.11bgn 1T1R Mini Card Wireless Adapter + +usb:v0DB0p6970* + ID_MODEL_FROM_DATABASE=MS-6970 BToes Bluetooth adapter + +usb:v0DB0p697A* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0DB0p6982* + ID_MODEL_FROM_DATABASE=Medion Flash XL Card Reader + +usb:v0DB0pA861* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0DB0pA874* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0DB0pA970* + ID_MODEL_FROM_DATABASE=Bluetooth dongle + +usb:v0DB0pA97A* + ID_MODEL_FROM_DATABASE=Bluetooth EDR Device + +usb:v0DB0pB970* + ID_MODEL_FROM_DATABASE=Bluetooth EDR Device + +usb:v0DB0pB97A* + ID_MODEL_FROM_DATABASE=Bluetooth EDR Device + +usb:v0DB1* + ID_VENDOR_FROM_DATABASE=Wen Te Electronics Co., Ltd + +usb:v0DB2* + ID_VENDOR_FROM_DATABASE=Shian Hwi Plug Parts, Plastic Factory + +usb:v0DB3* + ID_VENDOR_FROM_DATABASE=Tekram Technology Co., Ltd + +usb:v0DB4* + ID_VENDOR_FROM_DATABASE=Chung Fu Chen Yeh Enterprise Corp. + +usb:v0DB5* + ID_VENDOR_FROM_DATABASE=Access IS + +usb:v0DB5p0139* + ID_MODEL_FROM_DATABASE=Barcode Module - CDC serial + +usb:v0DB5p013A* + ID_MODEL_FROM_DATABASE=Barcode Module - Virtual Keyboard + +usb:v0DB5p013B* + ID_MODEL_FROM_DATABASE=Barcode Module - HID + +usb:v0DB5p0160* + ID_MODEL_FROM_DATABASE=NFC and Smartcard Module (NSM) + +usb:v0DB7* + ID_VENDOR_FROM_DATABASE=ELCON Systemtechnik + +usb:v0DB7p0002* + ID_MODEL_FROM_DATABASE=Goldpfeil P-LAN + +usb:v0DBA* + ID_VENDOR_FROM_DATABASE=Digidesign + +usb:v0DBAp1000* + ID_MODEL_FROM_DATABASE=Mbox 1 [Mbox] + +usb:v0DBAp3000* + ID_MODEL_FROM_DATABASE=Mbox 2 + +usb:v0DBC* + ID_VENDOR_FROM_DATABASE=A&D Medical + +usb:v0DBCp0003* + ID_MODEL_FROM_DATABASE=AND Serial Cable [AND Smart Cable] + +usb:v0DBE* + ID_VENDOR_FROM_DATABASE=Jiuh Shiuh Precision Industry Co., Ltd + +usb:v0DBF* + ID_VENDOR_FROM_DATABASE=Jess-Link International + +usb:v0DBFp0002* + ID_MODEL_FROM_DATABASE=SmartDongle Security Key + +usb:v0DBFp0200* + ID_MODEL_FROM_DATABASE=HDD Storage Solution + +usb:v0DBFp021B* + ID_MODEL_FROM_DATABASE=USB-2.0 IDE Adapter + +usb:v0DBFp0300* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v0DBFp0333* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v0DBFp0707* + ID_MODEL_FROM_DATABASE=ZIV Drive + +usb:v0DC0* + ID_VENDOR_FROM_DATABASE=G7 Solutions (formerly Great Notions) + +usb:v0DC1* + ID_VENDOR_FROM_DATABASE=Tamagawa Seiki Co., Ltd + +usb:v0DC3* + ID_VENDOR_FROM_DATABASE=Athena Smartcard Solutions, Inc. + +usb:v0DC3p0801* + ID_MODEL_FROM_DATABASE=ASEDrive III + +usb:v0DC3p0802* + ID_MODEL_FROM_DATABASE=ASEDrive IIIe + +usb:v0DC3p1104* + ID_MODEL_FROM_DATABASE=ASEDrive IIIe KB + +usb:v0DC3p1701* + ID_MODEL_FROM_DATABASE=ASEKey + +usb:v0DC3p1702* + ID_MODEL_FROM_DATABASE=ASEKey + +usb:v0DC4* + ID_VENDOR_FROM_DATABASE=Macpower Peripherals, Ltd + +usb:v0DC4p0040* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0DC4p0041* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0DC4p0042* + ID_MODEL_FROM_DATABASE=Mass Storage Device + +usb:v0DC4p0101* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v0DC4p0209* + ID_MODEL_FROM_DATABASE=SK-3500 S2 + +usb:v0DC4p020A* + ID_MODEL_FROM_DATABASE=Oyen Digital MiniPro 2.5" hard drive enclosure + +usb:v0DC5* + ID_VENDOR_FROM_DATABASE=SDK Co., Ltd + +usb:v0DC6* + ID_VENDOR_FROM_DATABASE=Precision Squared Technology Corp. + +usb:v0DC6p2301* + ID_MODEL_FROM_DATABASE=Wireless Touchpad Keyboard + +usb:v0DC7* + ID_VENDOR_FROM_DATABASE=First Cable Line, Inc. + +usb:v0DCD* + ID_VENDOR_FROM_DATABASE=NetworkFab Corp. + +usb:v0DCDp0001* + ID_MODEL_FROM_DATABASE=Remote Interface Adapter + +usb:v0DCDp0002* + ID_MODEL_FROM_DATABASE=High Bandwidth Codec + +usb:v0DD0* + ID_VENDOR_FROM_DATABASE=Access Solutions + +usb:v0DD0p1002* + ID_MODEL_FROM_DATABASE=Triple Talk Speech Synthesizer + +usb:v0DD1* + ID_VENDOR_FROM_DATABASE=Contek Electronics Co., Ltd + +usb:v0DD2* + ID_VENDOR_FROM_DATABASE=Power Quotient International Co., Ltd + +usb:v0DD2p0003* + ID_MODEL_FROM_DATABASE=Mass Storage (P) + +usb:v0DD3* + ID_VENDOR_FROM_DATABASE=MediaQ + +usb:v0DD4* + ID_VENDOR_FROM_DATABASE=Custom Engineering SPA + +usb:v0DD5* + ID_VENDOR_FROM_DATABASE=California Micro Devices + +usb:v0DD7* + ID_VENDOR_FROM_DATABASE=Kocom Co., Ltd + +usb:v0DD8* + ID_VENDOR_FROM_DATABASE=Netac Technology Co., Ltd + +usb:v0DD8p1060* + ID_MODEL_FROM_DATABASE=USB-CF-Card + +usb:v0DD8pE007* + ID_MODEL_FROM_DATABASE=OnlyDisk U222 Pendrive + +usb:v0DD8pF607* + ID_MODEL_FROM_DATABASE=OnlyDisk U208 1G flash drive [U-SAFE] + +usb:v0DD9* + ID_VENDOR_FROM_DATABASE=HighSpeed Surfing + +usb:v0DDA* + ID_VENDOR_FROM_DATABASE=Integrated Circuit Solution, Inc. + +usb:v0DDAp0001* + ID_MODEL_FROM_DATABASE=Multi-Card Reader 6in1 + +usb:v0DDAp0002* + ID_MODEL_FROM_DATABASE=Multi-Card Reader 7in1 + +usb:v0DDAp0003* + ID_MODEL_FROM_DATABASE=Flash Disk + +usb:v0DDAp0005* + ID_MODEL_FROM_DATABASE=Internal Multi-Card Reader 6in1 + +usb:v0DDAp0008* + ID_MODEL_FROM_DATABASE=SD single card reader + +usb:v0DDAp0009* + ID_MODEL_FROM_DATABASE=MS single card reader + +usb:v0DDAp000A* + ID_MODEL_FROM_DATABASE=MS+SD Dual Card Reader + +usb:v0DDAp000B* + ID_MODEL_FROM_DATABASE=SM single card reader + +usb:v0DDAp0101* + ID_MODEL_FROM_DATABASE=All-In-One Card Reader + +usb:v0DDAp0102* + ID_MODEL_FROM_DATABASE=All-In-One Card Reader + +usb:v0DDAp0301* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0DDAp0302* + ID_MODEL_FROM_DATABASE=Multi-Card MP3 Player + +usb:v0DDAp1001* + ID_MODEL_FROM_DATABASE=Multi-Flash Disk + +usb:v0DDAp2001* + ID_MODEL_FROM_DATABASE=Multi-Card Reader + +usb:v0DDAp2002* + ID_MODEL_FROM_DATABASE=Q018 default PID + +usb:v0DDAp2003* + ID_MODEL_FROM_DATABASE=Multi-Card Reader + +usb:v0DDAp2005* + ID_MODEL_FROM_DATABASE=Datalux DLX-1611 16in1 Card Reader + +usb:v0DDAp2006* + ID_MODEL_FROM_DATABASE=All-In-One Card Reader + +usb:v0DDAp2007* + ID_MODEL_FROM_DATABASE=USB to ATAPI bridge + +usb:v0DDAp2008* + ID_MODEL_FROM_DATABASE=All-In-One Card Reader + +usb:v0DDAp2013* + ID_MODEL_FROM_DATABASE=SD/MS Combo Card Reader + +usb:v0DDAp2014* + ID_MODEL_FROM_DATABASE=SD/MS Single Card Reader + +usb:v0DDAp2023* + ID_MODEL_FROM_DATABASE=card reader SD/MS DEMO board with ICSI brand name (MaskROM version) + +usb:v0DDAp2024* + ID_MODEL_FROM_DATABASE=card reader SD/MS DEMO board with Generic brand name (MaskROM version) + +usb:v0DDAp2026* + ID_MODEL_FROM_DATABASE=USB2.0 Card Reader + +usb:v0DDAp2027* + ID_MODEL_FROM_DATABASE=USB 2.0 Card Reader + +usb:v0DDAp2315* + ID_MODEL_FROM_DATABASE=UFD MP3 player (model 2) + +usb:v0DDAp2318* + ID_MODEL_FROM_DATABASE=UFD MP3 player (model 1) + +usb:v0DDAp2321* + ID_MODEL_FROM_DATABASE=UFD MP3 player + +usb:v0DDB* + ID_VENDOR_FROM_DATABASE=Tamarack, Inc. + +usb:v0DDD* + ID_VENDOR_FROM_DATABASE=Datelink Technology Co., Ltd + +usb:v0DDE* + ID_VENDOR_FROM_DATABASE=Ubicom, Inc. + +usb:v0DE0* + ID_VENDOR_FROM_DATABASE=BD Consumer Healthcare + +usb:v0DE7* + ID_VENDOR_FROM_DATABASE=USBmicro + +usb:v0DE7p0191* + ID_MODEL_FROM_DATABASE=U401 Interface card + +usb:v0DE7p01A5* + ID_MODEL_FROM_DATABASE=U421 interface card + +usb:v0DE7p01C3* + ID_MODEL_FROM_DATABASE=U451 relay interface card + +usb:v0DEA* + ID_VENDOR_FROM_DATABASE=UTECH Electronic (D.G.) Co., Ltd. + +usb:v0DED* + ID_VENDOR_FROM_DATABASE=Novasonics + +usb:v0DEE* + ID_VENDOR_FROM_DATABASE=Lifetime Memory Products + +usb:v0DEEp4010* + ID_MODEL_FROM_DATABASE=Storage Adapter + +usb:v0DEF* + ID_VENDOR_FROM_DATABASE=Full Rise Electronic Co., Ltd + +usb:v0DF4* + ID_VENDOR_FROM_DATABASE=NET&SYS + +usb:v0DF4p0201* + ID_MODEL_FROM_DATABASE=MNG-2005 + +usb:v0DF6* + ID_VENDOR_FROM_DATABASE=Sitecom Europe B.V. + +usb:v0DF6p0001* + ID_MODEL_FROM_DATABASE=C-Media VOIP Device + +usb:v0DF6p0004* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 Adapter 100m + +usb:v0DF6p0007* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 Adapter 10m + +usb:v0DF6p000B* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 Adapter DFU + +usb:v0DF6p000D* + ID_MODEL_FROM_DATABASE=WL-168 Wireless Network Adapter 54g + +usb:v0DF6p0017* + ID_MODEL_FROM_DATABASE=WL-182 Wireless-N Network USB Card + +usb:v0DF6p0019* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 adapter 10m CN-512v2 001 + +usb:v0DF6p001A* + ID_MODEL_FROM_DATABASE=Bluetooth 2.0 adapter 100m CN-521v2 001 + +usb:v0DF6p002B* + ID_MODEL_FROM_DATABASE=WL-188 Wireless Network 300N USB Adapter + +usb:v0DF6p002C* + ID_MODEL_FROM_DATABASE=WL-301 Wireless Network 300N USB Adapter + +usb:v0DF6p002D* + ID_MODEL_FROM_DATABASE=WL-302 Wireless Network 300N USB dongle + +usb:v0DF6p0036* + ID_MODEL_FROM_DATABASE=WL-603 Wireless Adapter + +usb:v0DF6p0039* + ID_MODEL_FROM_DATABASE=WL-315 Wireless-N USB Adapter + +usb:v0DF6p003B* + ID_MODEL_FROM_DATABASE=WL-321 Wireless USB Gaming Adapter 300N + +usb:v0DF6p003C* + ID_MODEL_FROM_DATABASE=WL-323 Wireless-N USB Adapter + +usb:v0DF6p003D* + ID_MODEL_FROM_DATABASE=WL-324 Wireless USB Adapter 300N + +usb:v0DF6p003E* + ID_MODEL_FROM_DATABASE=WL-343 Wireless USB Adapter 150N X1 + +usb:v0DF6p003F* + ID_MODEL_FROM_DATABASE=WL-608 Wireless USB Adapter 54g + +usb:v0DF6p0040* + ID_MODEL_FROM_DATABASE=WL-344 Wireless Adapter 300N X2 [Ralink RT3071] + +usb:v0DF6p0041* + ID_MODEL_FROM_DATABASE=WL-329 Wireless Dualband USB adapter 300N + +usb:v0DF6p0042* + ID_MODEL_FROM_DATABASE=WL-345 Wireless USB adapter 300N X3 + +usb:v0DF6p0045* + ID_MODEL_FROM_DATABASE=WL-353 Wireless USB Adapter 150N Nano + +usb:v0DF6p0047* + ID_MODEL_FROM_DATABASE=WL-352v1 Wireless USB Adapter 300N 002 + +usb:v0DF6p0048* + ID_MODEL_FROM_DATABASE=WL-349v1 Wireless Adapter 150N 002 [Ralink RT3070] + +usb:v0DF6p0049* + ID_MODEL_FROM_DATABASE=WL-356 Wireless Adapter 300N + +usb:v0DF6p004A* + ID_MODEL_FROM_DATABASE=WL-358v1 Wireless Micro USB Adapter 300N X3 002 + +usb:v0DF6p004B* + ID_MODEL_FROM_DATABASE=WL-349v3 Wireless Micro Adapter 150N X1 [Realtek RTL8192SU] + +usb:v0DF6p004C* + ID_MODEL_FROM_DATABASE=WL-352 802.11n Adapter [Realtek RTL8191SU] + +usb:v0DF6p0050* + ID_MODEL_FROM_DATABASE=WL-349v4 Wireless Micro Adapter 150N X1 [Ralink RT3370] + +usb:v0DF6p0056* + ID_MODEL_FROM_DATABASE=LN-031 10/100/1000 Ethernet Adapter + +usb:v0DF6p005D* + ID_MODEL_FROM_DATABASE=WLA-2000 v1.001 WLAN [RTL8191SU] + +usb:v0DF6p0060* + ID_MODEL_FROM_DATABASE=WLA-4000 802.11bgn [Ralink RT3072] + +usb:v0DF6p0062* + ID_MODEL_FROM_DATABASE=WLA-5000 802.11abgn [Ralink RT3572] + +usb:v0DF6p0072* + ID_MODEL_FROM_DATABASE=AX88179 Gigabit Ethernet [Sitecom] + +usb:v0DF6p061C* + ID_MODEL_FROM_DATABASE=LN-028 Network USB 2.0 Adapter + +usb:v0DF6p21F4* + ID_MODEL_FROM_DATABASE=44 St Bluetooth Device + +usb:v0DF6p2200* + ID_MODEL_FROM_DATABASE=Sitecom bluetooth2.0 class 2 dongle CN-512 + +usb:v0DF6p2208* + ID_MODEL_FROM_DATABASE=Sitecom bluetooth2.0 class 2 dongle CN-520 + +usb:v0DF6p2209* + ID_MODEL_FROM_DATABASE=Sitecom bluetooth2.0 class 1 dongle CN-521 + +usb:v0DF6p3068* + ID_MODEL_FROM_DATABASE=DC-104v2 ISDN Adapter [HFC-S] + +usb:v0DF6p9071* + ID_MODEL_FROM_DATABASE=WL-113 rev 1 Wireless Network USB Adapter + +usb:v0DF6p9075* + ID_MODEL_FROM_DATABASE=WL-117 Hi-Speed USB Adapter + +usb:v0DF6p90AC* + ID_MODEL_FROM_DATABASE=WL-172 Wireless Network USB Adapter 54g Turbo + +usb:v0DF6p9712* + ID_MODEL_FROM_DATABASE=WL-113 rev 2 Wireless Network USB Adapter + +usb:v0DF7* + ID_VENDOR_FROM_DATABASE=Mobile Action Technology, Inc. + +usb:v0DF7p0620* + ID_MODEL_FROM_DATABASE=MA-620 Infrared Adapter + +usb:v0DF7p0700* + ID_MODEL_FROM_DATABASE=MA-700 Bluetooth Adapter + +usb:v0DF7p0720* + ID_MODEL_FROM_DATABASE=MA-720 Bluetooth Adapter + +usb:v0DF7p0722* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0DF7p0730* + ID_MODEL_FROM_DATABASE=MA-730/MA-730G Bluetooth Adapter + +usb:v0DF7p0800* + ID_MODEL_FROM_DATABASE=Data Cable + +usb:v0DF7p0820* + ID_MODEL_FROM_DATABASE=Data Cable + +usb:v0DF7p0900* + ID_MODEL_FROM_DATABASE=MA i-gotU Travel Logger GPS + +usb:v0DF7p1800* + ID_MODEL_FROM_DATABASE=Generic Card Reader + +usb:v0DF7p1802* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v0DFA* + ID_VENDOR_FROM_DATABASE=Toyo Communication Equipment Co., Ltd + +usb:v0DFC* + ID_VENDOR_FROM_DATABASE=GeneralTouch Technology Co., Ltd + +usb:v0DFCp0001* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v0E03* + ID_VENDOR_FROM_DATABASE=Nippon Systemware Co., Ltd + +usb:v0E08* + ID_VENDOR_FROM_DATABASE=Winbest Technology Co., Ltd + +usb:v0E0B* + ID_VENDOR_FROM_DATABASE=Amigo Technology Inc. + +usb:v0E0Bp9031* + ID_MODEL_FROM_DATABASE=802.11n Wireless USB Card + +usb:v0E0Bp9041* + ID_MODEL_FROM_DATABASE=802.11n Wireless USB Card + +usb:v0E0C* + ID_VENDOR_FROM_DATABASE=Gesytec + +usb:v0E0Cp0101* + ID_MODEL_FROM_DATABASE=LonUSB LonTalk Network Adapter + +usb:v0E0D* + ID_VENDOR_FROM_DATABASE=PicoQuant GmbH + +usb:v0E0Dp0003* + ID_MODEL_FROM_DATABASE=PicoHarp 300 + +usb:v0E0F* + ID_VENDOR_FROM_DATABASE=VMware, Inc. + +usb:v0E0Fp0001* + ID_MODEL_FROM_DATABASE=Device + +usb:v0E0Fp0002* + ID_MODEL_FROM_DATABASE=Virtual USB Hub + +usb:v0E0Fp0003* + ID_MODEL_FROM_DATABASE=Virtual Mouse + +usb:v0E0Fp0004* + ID_MODEL_FROM_DATABASE=Virtual CCID + +usb:v0E0Fp0005* + ID_MODEL_FROM_DATABASE=Virtual Mass Storage + +usb:v0E0Fp0006* + ID_MODEL_FROM_DATABASE=Virtual Keyboard + +usb:v0E0FpF80A* + ID_MODEL_FROM_DATABASE=Smoker FX2 + +usb:v0E16* + ID_VENDOR_FROM_DATABASE=JMTek, LLC + +usb:v0E17* + ID_VENDOR_FROM_DATABASE=Walex Electronic, Ltd + +usb:v0E1A* + ID_VENDOR_FROM_DATABASE=Unisys + +usb:v0E1B* + ID_VENDOR_FROM_DATABASE=Crewave + +usb:v0E20* + ID_VENDOR_FROM_DATABASE=Pegasus Technologies Ltd. + +usb:v0E20p0101* + ID_MODEL_FROM_DATABASE=NoteTaker + +usb:v0E20p0200* + ID_MODEL_FROM_DATABASE=Seiko Instruments InkLink Handwriting System + +usb:v0E21* + ID_VENDOR_FROM_DATABASE=Cowon Systems, Inc. + +usb:v0E21p0300* + ID_MODEL_FROM_DATABASE=iAudio CW200 + +usb:v0E21p0400* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0E21p0500* + ID_MODEL_FROM_DATABASE=iAudio M3 + +usb:v0E21p0510* + ID_MODEL_FROM_DATABASE=iAudio X5, subpack USB port + +usb:v0E21p0513* + ID_MODEL_FROM_DATABASE=iAudio X5, side USB port + +usb:v0E21p0520* + ID_MODEL_FROM_DATABASE=iAudio M5, side USB port + +usb:v0E21p0601* + ID_MODEL_FROM_DATABASE=iAudio G3 + +usb:v0E21p0681* + ID_MODEL_FROM_DATABASE=iAUDIO E2 + +usb:v0E21p0700* + ID_MODEL_FROM_DATABASE=iAudio U3 + +usb:v0E21p0751* + ID_MODEL_FROM_DATABASE=iAudio 7 + +usb:v0E21p0760* + ID_MODEL_FROM_DATABASE=iAUDIO U5 / iAUDIO G2 + +usb:v0E21p0800* + ID_MODEL_FROM_DATABASE=Cowon D2 (UMS mode) + +usb:v0E21p0801* + ID_MODEL_FROM_DATABASE=Cowon D2 (MTP mode) + +usb:v0E21p0910* + ID_MODEL_FROM_DATABASE=iAUDIO 9 + +usb:v0E21p0920* + ID_MODEL_FROM_DATABASE=J3 + +usb:v0E22* + ID_VENDOR_FROM_DATABASE=Symbian Ltd. + +usb:v0E23* + ID_VENDOR_FROM_DATABASE=Liou Yuane Enterprise Co., Ltd + +usb:v0E25* + ID_VENDOR_FROM_DATABASE=VinChip Systems, Inc. + +usb:v0E26* + ID_VENDOR_FROM_DATABASE=J-Phone East Co., Ltd + +usb:v0E30* + ID_VENDOR_FROM_DATABASE=HeartMath LLC + +usb:v0E34* + ID_VENDOR_FROM_DATABASE=Micro Computer Control Corp. + +usb:v0E35* + ID_VENDOR_FROM_DATABASE=3Pea Technologies, Inc. + +usb:v0E36* + ID_VENDOR_FROM_DATABASE=TiePie engineering + +usb:v0E36p0008* + ID_MODEL_FROM_DATABASE=Handyscope HS3 + +usb:v0E36p0009* + ID_MODEL_FROM_DATABASE=Handyscope HS3 (br) + +usb:v0E36p000A* + ID_MODEL_FROM_DATABASE=Handyscope HS4 + +usb:v0E36p000B* + ID_MODEL_FROM_DATABASE=Handyscope HS4 (br) + +usb:v0E36p000E* + ID_MODEL_FROM_DATABASE=Handyscope HS4-DIFF + +usb:v0E36p000F* + ID_MODEL_FROM_DATABASE=Handyscope HS4-DIFF (br) + +usb:v0E36p0010* + ID_MODEL_FROM_DATABASE=Handyscope HS2 + +usb:v0E36p0011* + ID_MODEL_FROM_DATABASE=TiePieSCOPE HS805 (br) + +usb:v0E36p0012* + ID_MODEL_FROM_DATABASE=TiePieSCOPE HS805 + +usb:v0E36p0013* + ID_MODEL_FROM_DATABASE=Handyprobe HP3 + +usb:v0E36p0014* + ID_MODEL_FROM_DATABASE=Handyprobe HP3 + +usb:v0E36p0018* + ID_MODEL_FROM_DATABASE=Handyprobe HP2 + +usb:v0E36p001B* + ID_MODEL_FROM_DATABASE=Handyscope HS5 + +usb:v0E36p0042* + ID_MODEL_FROM_DATABASE=TiePieSCOPE HS801 + +usb:v0E36p00FD* + ID_MODEL_FROM_DATABASE=USB To Parallel adapter + +usb:v0E36p00FE* + ID_MODEL_FROM_DATABASE=USB To Parallel adapter + +usb:v0E38* + ID_VENDOR_FROM_DATABASE=Stratitec, Inc. + +usb:v0E39* + ID_VENDOR_FROM_DATABASE=Smart Modular Technologies, Inc. + +usb:v0E39p0137* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v0E3A* + ID_VENDOR_FROM_DATABASE=Neostar Technology Co., Ltd + +usb:v0E3Ap1100* + ID_MODEL_FROM_DATABASE=CW-1100 Wireless Network Adapter + +usb:v0E3B* + ID_VENDOR_FROM_DATABASE=Mansella, Ltd + +usb:v0E41* + ID_VENDOR_FROM_DATABASE=Line6, Inc. + +usb:v0E41p4147* + ID_MODEL_FROM_DATABASE=TonePort GX + +usb:v0E41p414D* + ID_MODEL_FROM_DATABASE=Pod HD500 + +usb:v0E41p4156* + ID_MODEL_FROM_DATABASE=POD HD Desktop + +usb:v0E41p4250* + ID_MODEL_FROM_DATABASE=BassPODxt + +usb:v0E41p4252* + ID_MODEL_FROM_DATABASE=BassPODxt Pro + +usb:v0E41p4642* + ID_MODEL_FROM_DATABASE=BassPODxt Live + +usb:v0E41p4650* + ID_MODEL_FROM_DATABASE=PODxt Live + +usb:v0E41p4750* + ID_MODEL_FROM_DATABASE=GuitarPort + +usb:v0E41p5044* + ID_MODEL_FROM_DATABASE=PODxt + +usb:v0E41p5050* + ID_MODEL_FROM_DATABASE=PODxt Pro + +usb:v0E41p534D* + ID_MODEL_FROM_DATABASE=SeaMonkey + +usb:v0E44* + ID_VENDOR_FROM_DATABASE=Sun-Riseful Technology Co., Ltd. + +usb:v0E48* + ID_VENDOR_FROM_DATABASE=Julia Corp., Ltd + +usb:v0E48p0100* + ID_MODEL_FROM_DATABASE=CardPro SmartCard Reader + +usb:v0E4A* + ID_VENDOR_FROM_DATABASE=Shenzhen Bao Hing Electric Wire & Cable Mfr. Co. + +usb:v0E4C* + ID_VENDOR_FROM_DATABASE=Radica Games, Ltd + +usb:v0E4Cp1097* + ID_MODEL_FROM_DATABASE=Gamester Controller + +usb:v0E4Cp2390* + ID_MODEL_FROM_DATABASE=Games Jtech Controller + +usb:v0E4Cp7288* + ID_MODEL_FROM_DATABASE=funkey reader + +usb:v0E50* + ID_VENDOR_FROM_DATABASE=TechnoData Interware + +usb:v0E50p0002* + ID_MODEL_FROM_DATABASE=Matrixlock Dongle (HID) + +usb:v0E55* + ID_VENDOR_FROM_DATABASE=Speed Dragon Multimedia, Ltd + +usb:v0E55p110A* + ID_MODEL_FROM_DATABASE=Tanic S110-SG1 + ISSC IS1002N [Slow Infra-Red (SIR) & Bluetooth 1.2 (Class 2) Adapter] + +usb:v0E55p110B* + ID_MODEL_FROM_DATABASE=MS3303H USB-to-Serial Bridge + +usb:v0E56* + ID_VENDOR_FROM_DATABASE=Kingston Technology Company, Inc. + +usb:v0E56p6021* + ID_MODEL_FROM_DATABASE=K-PEX 100 + +usb:v0E5A* + ID_VENDOR_FROM_DATABASE=Active Co., Ltd + +usb:v0E5B* + ID_VENDOR_FROM_DATABASE=Union Power Information Industrial Co., Ltd + +usb:v0E5C* + ID_VENDOR_FROM_DATABASE=Bitland Information Technology Co., Ltd + +usb:v0E5Cp6118* + ID_MODEL_FROM_DATABASE=LCD Device + +usb:v0E5Cp6119* + ID_MODEL_FROM_DATABASE=remote receive and control device + +usb:v0E5Cp6441* + ID_MODEL_FROM_DATABASE=C-Media Sound Device + +usb:v0E5D* + ID_VENDOR_FROM_DATABASE=Neltron Industrial Co., Ltd + +usb:v0E5E* + ID_VENDOR_FROM_DATABASE=Conwise Technology Co., Ltd. + +usb:v0E5Ep6622* + ID_MODEL_FROM_DATABASE=CW6622 + +usb:v0E66* + ID_VENDOR_FROM_DATABASE=Hawking Technologies + +usb:v0E66p0001* + ID_MODEL_FROM_DATABASE=HWUN1 Hi-Gain Wireless-300N Adapter w/ Upgradable Antenna [Ralink RT2870] + +usb:v0E66p0003* + ID_MODEL_FROM_DATABASE=HWDN1 Hi-Gain Wireless-300N Dish Adapter [Ralink RT2870] + +usb:v0E66p0009* + ID_MODEL_FROM_DATABASE=HWUN2 Hi-Gain Wireless-150N Adapter w/ Upgradable Antenna [Ralink RT2770] + +usb:v0E66p000B* + ID_MODEL_FROM_DATABASE=HWDN2 Hi-Gain Wireless-150N Dish Adapter [Ralink RT2770] + +usb:v0E66p0013* + ID_MODEL_FROM_DATABASE=HWUN3 Hi-Gain Wireless-N Adapter [Ralink RT3070] + +usb:v0E66p0015* + ID_MODEL_FROM_DATABASE=HWDN2 Rev. E Hi-Gain Wireless-150N Dish Adapter [Realtek RTL8191SU] + +usb:v0E66p0017* + ID_MODEL_FROM_DATABASE=HAWNU1 Hi-Gain Wireless-150N Network Adapter with Range Amplifier [Ralink RT3070] + +usb:v0E66p0018* + ID_MODEL_FROM_DATABASE=Wireless-N Network Adapter [Ralink RT2870] + +usb:v0E66p400B* + ID_MODEL_FROM_DATABASE=UF100 10/100 Network Adapter + +usb:v0E66p400C* + ID_MODEL_FROM_DATABASE=UF100 Ethernet [pegasus2] + +usb:v0E67* + ID_VENDOR_FROM_DATABASE=Fossil, Inc. + +usb:v0E67p0002* + ID_MODEL_FROM_DATABASE=Wrist PDA + +usb:v0E6A* + ID_VENDOR_FROM_DATABASE=Megawin Technology Co., Ltd + +usb:v0E6Ap0101* + ID_MODEL_FROM_DATABASE=MA100 [USB-UART Bridge IC] + +usb:v0E6Ap030B* + ID_MODEL_FROM_DATABASE=Truly Ergonomic Computer Keyboard (Device Firmware Update mode) + +usb:v0E6Ap030C* + ID_MODEL_FROM_DATABASE=Truly Ergonomic Computer Keyboard + +usb:v0E6Ap6001* + ID_MODEL_FROM_DATABASE=GEMBIRD Flexible keyboard KB-109F-B-DE + +usb:v0E6F* + ID_VENDOR_FROM_DATABASE=Logic3 + +usb:v0E6Fp0003* + ID_MODEL_FROM_DATABASE=Freebird wireless Controller + +usb:v0E6Fp0005* + ID_MODEL_FROM_DATABASE=Eclipse wireless Controller + +usb:v0E6Fp0006* + ID_MODEL_FROM_DATABASE=Edge wireless Controller + +usb:v0E6Fp0128* + ID_MODEL_FROM_DATABASE=Wireless PS3 Controller + +usb:v0E70* + ID_VENDOR_FROM_DATABASE=Tokyo Electronic Industry Co., Ltd + +usb:v0E72* + ID_VENDOR_FROM_DATABASE=Hsi-Chin Electronics Co., Ltd + +usb:v0E75* + ID_VENDOR_FROM_DATABASE=TVS Electronics, Ltd + +usb:v0E79* + ID_VENDOR_FROM_DATABASE=Archos, Inc. + +usb:v0E79p1106* + ID_MODEL_FROM_DATABASE=Pocket Media Assistant - PMA400 + +usb:v0E79p1204* + ID_MODEL_FROM_DATABASE=Gmini XS 200 + +usb:v0E79p1306* + ID_MODEL_FROM_DATABASE=504 Portable Multimedia Player + +usb:v0E79p1330* + ID_MODEL_FROM_DATABASE=5 Tablet + +usb:v0E79p1332* + ID_MODEL_FROM_DATABASE=5 IMT + +usb:v0E79p1416* + ID_MODEL_FROM_DATABASE=32 IT + +usb:v0E79p1417* + ID_MODEL_FROM_DATABASE=A43 IT + +usb:v0E79p14AD* + ID_MODEL_FROM_DATABASE=97 Titanium HD + +usb:v0E79p150E* + ID_MODEL_FROM_DATABASE=80 G9 + +usb:v0E79p3001* + ID_MODEL_FROM_DATABASE=40 Titanium + +usb:v0E7B* + ID_VENDOR_FROM_DATABASE=On-Tech Industry Co., Ltd + +usb:v0E7E* + ID_VENDOR_FROM_DATABASE=Gmate, Inc. + +usb:v0E7Ep0001* + ID_MODEL_FROM_DATABASE=Yopy 3000 PDA + +usb:v0E7Ep1001* + ID_MODEL_FROM_DATABASE=YP3X00 PDA + +usb:v0E82* + ID_VENDOR_FROM_DATABASE=Ching Tai Electric Wire & Cable Co., Ltd + +usb:v0E83* + ID_VENDOR_FROM_DATABASE=Shin An Wire & Cable Co. + +usb:v0E8C* + ID_VENDOR_FROM_DATABASE=Well Force Electronic Co., Ltd + +usb:v0E8D* + ID_VENDOR_FROM_DATABASE=MediaTek Inc. + +usb:v0E8Dp0003* + ID_MODEL_FROM_DATABASE=MT6227 phone + +usb:v0E8Dp0004* + ID_MODEL_FROM_DATABASE=MT6227 phone + +usb:v0E8Dp0023* + ID_MODEL_FROM_DATABASE=S103 + +usb:v0E8Dp1806* + ID_MODEL_FROM_DATABASE=Samsung SE-208 Slim Portable DVD Writer + +usb:v0E8Dp1836* + ID_MODEL_FROM_DATABASE=Samsung SE-S084 Super WriteMaster Slim External DVD writer + +usb:v0E8Dp2000* + ID_MODEL_FROM_DATABASE=MT65xx Preloader + +usb:v0E8Dp3329* + ID_MODEL_FROM_DATABASE=Qstarz BT-Q1000XT + +usb:v0E8Dp763E* + ID_MODEL_FROM_DATABASE=MT7630e Bluetooth Adapter + +usb:v0E8F* + ID_VENDOR_FROM_DATABASE=GreenAsia Inc. + +usb:v0E8Fp0003* + ID_MODEL_FROM_DATABASE=MaxFire Blaze2 + +usb:v0E8Fp0012* + ID_MODEL_FROM_DATABASE=Joystick/Gamepad + +usb:v0E8Fp0016* + ID_MODEL_FROM_DATABASE=4 port USB 1.1 hub UH-174 + +usb:v0E8Fp0020* + ID_MODEL_FROM_DATABASE=USB to PS/2 Adapter + +usb:v0E8Fp0021* + ID_MODEL_FROM_DATABASE=Multimedia Keyboard Controller + +usb:v0E8Fp0022* + ID_MODEL_FROM_DATABASE=multimedia keyboard controller + +usb:v0E8Fp0201* + ID_MODEL_FROM_DATABASE=SmartJoy Frag Xpad/PS2 adaptor + +usb:v0E90* + ID_VENDOR_FROM_DATABASE=WiebeTech, LLC + +usb:v0E90p0100* + ID_MODEL_FROM_DATABASE=Storage Adapter V1 + +usb:v0E91* + ID_VENDOR_FROM_DATABASE=VTech Engineering Canada, Ltd + +usb:v0E92* + ID_VENDOR_FROM_DATABASE=C's Glory Enterprise Co., Ltd + +usb:v0E93* + ID_VENDOR_FROM_DATABASE=eM Technics Co., Ltd + +usb:v0E95* + ID_VENDOR_FROM_DATABASE=Future Technology Co., Ltd + +usb:v0E96* + ID_VENDOR_FROM_DATABASE=Aplux Communications, Ltd + +usb:v0E96pC001* + ID_MODEL_FROM_DATABASE=TRUST 380 USB2 SPACEC@M + +usb:v0E97* + ID_VENDOR_FROM_DATABASE=Fingerworks, Inc. + +usb:v0E97p0908* + ID_MODEL_FROM_DATABASE=Composite HID (Keyboard and Mouse) + +usb:v0E98* + ID_VENDOR_FROM_DATABASE=Advanced Analogic Technologies, Inc. + +usb:v0E99* + ID_VENDOR_FROM_DATABASE=Parallel Dice Co., Ltd + +usb:v0E9A* + ID_VENDOR_FROM_DATABASE=TA HSING Industries, Ltd + +usb:v0E9B* + ID_VENDOR_FROM_DATABASE=ADTEC Corp. + +usb:v0E9C* + ID_VENDOR_FROM_DATABASE=Streamzap, Inc. + +usb:v0E9Cp0000* + ID_MODEL_FROM_DATABASE=Streamzap Remote Control + +usb:v0E9F* + ID_VENDOR_FROM_DATABASE=Tamura Corp. + +usb:v0EA0* + ID_VENDOR_FROM_DATABASE=Ours Technology, Inc. + +usb:v0EA0p2126* + ID_MODEL_FROM_DATABASE=7-in-1 Card Reader + +usb:v0EA0p2153* + ID_MODEL_FROM_DATABASE=SD Card Reader Key + +usb:v0EA0p2168* + ID_MODEL_FROM_DATABASE=Transcend JetFlash 2.0 / Astone USB Drive / Intellegent Stick 2.0 + +usb:v0EA0p6803* + ID_MODEL_FROM_DATABASE=OTI-6803 Flash Disk + +usb:v0EA0p6808* + ID_MODEL_FROM_DATABASE=OTI-6808 Flash Disk + +usb:v0EA0p6828* + ID_MODEL_FROM_DATABASE=OTI-6828 Flash Disk + +usb:v0EA0p6858* + ID_MODEL_FROM_DATABASE=OTi-6858 serial adapter + +usb:v0EA6* + ID_VENDOR_FROM_DATABASE=Nihon Computer Co., Ltd + +usb:v0EA7* + ID_VENDOR_FROM_DATABASE=MSL Enterprises Corp. + +usb:v0EA8* + ID_VENDOR_FROM_DATABASE=CenDyne, Inc. + +usb:v0EAD* + ID_VENDOR_FROM_DATABASE=Humax Co., Ltd + +usb:v0EB0* + ID_VENDOR_FROM_DATABASE=NovaTech + +usb:v0EB0p9020* + ID_MODEL_FROM_DATABASE=NovaTech NV-902W + +usb:v0EB0p9021* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v0EB1* + ID_VENDOR_FROM_DATABASE=WIS Technologies, Inc. + +usb:v0EB1p6666* + ID_MODEL_FROM_DATABASE=WinFast WalkieTV TV Loader + +usb:v0EB1p6668* + ID_MODEL_FROM_DATABASE=WinFast WalkieTV TV Loader + +usb:v0EB1p7007* + ID_MODEL_FROM_DATABASE=WinFast WalkieTV WDM Capture + +usb:v0EB2* + ID_VENDOR_FROM_DATABASE=Y-S Electronic Co., Ltd + +usb:v0EB3* + ID_VENDOR_FROM_DATABASE=Saint Technology Corp. + +usb:v0EB7* + ID_VENDOR_FROM_DATABASE=Endor AG + +usb:v0EB8* + ID_VENDOR_FROM_DATABASE=Mettler Toledo + +usb:v0EB8p2200* + ID_MODEL_FROM_DATABASE=Ariva Scale + +usb:v0EB8pF000* + ID_MODEL_FROM_DATABASE=PS60 Scale + +usb:v0EBB* + ID_VENDOR_FROM_DATABASE=Thermo Fisher Scientific + +usb:v0EBBp0002* + ID_MODEL_FROM_DATABASE=FT-IR Spectrometer + +usb:v0EBE* + ID_VENDOR_FROM_DATABASE=VWeb Corp. + +usb:v0EBF* + ID_VENDOR_FROM_DATABASE=Omega Technology of Taiwan, Inc. + +usb:v0EC0* + ID_VENDOR_FROM_DATABASE=LHI Technology (China) Co., Ltd + +usb:v0EC1* + ID_VENDOR_FROM_DATABASE=Abit Computer Corp. + +usb:v0EC2* + ID_VENDOR_FROM_DATABASE=Sweetray Industrial, Ltd + +usb:v0EC3* + ID_VENDOR_FROM_DATABASE=Axell Co., Ltd + +usb:v0EC4* + ID_VENDOR_FROM_DATABASE=Ballracing Developments, Ltd + +usb:v0EC5* + ID_VENDOR_FROM_DATABASE=GT Information System Co., Ltd + +usb:v0EC6* + ID_VENDOR_FROM_DATABASE=InnoVISION Multimedia, Ltd + +usb:v0EC7* + ID_VENDOR_FROM_DATABASE=Theta Link Corp. + +usb:v0EC7p1008* + ID_MODEL_FROM_DATABASE=So., Show 301 Digital Camera + +usb:v0ECD* + ID_VENDOR_FROM_DATABASE=Lite-On IT Corp. + +usb:v0ECDp1400* + ID_MODEL_FROM_DATABASE=CD\RW 40X + +usb:v0ECDpA100* + ID_MODEL_FROM_DATABASE=LDW-411SX DVD/CD Rewritable Drive + +usb:v0ECE* + ID_VENDOR_FROM_DATABASE=TaiSol Electronics Co., Ltd + +usb:v0ECF* + ID_VENDOR_FROM_DATABASE=Phogenix Imaging, LLC + +usb:v0ED1* + ID_VENDOR_FROM_DATABASE=WinMaxGroup + +usb:v0ED1p6660* + ID_MODEL_FROM_DATABASE=Flash Disk 64M-C + +usb:v0ED1p6680* + ID_MODEL_FROM_DATABASE=Flash Disk 64M-B + +usb:v0ED1p7634* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v0ED2* + ID_VENDOR_FROM_DATABASE=Kyoto Micro Computer Co., Ltd + +usb:v0ED3* + ID_VENDOR_FROM_DATABASE=Wing-Tech Enterprise Co., Ltd + +usb:v0ED5* + ID_VENDOR_FROM_DATABASE=Fiberbyte + +usb:v0ED5pE000* + ID_MODEL_FROM_DATABASE=USB-inSync Device + +usb:v0ED5pF000* + ID_MODEL_FROM_DATABASE=Fiberbyte USB-inSync Device + +usb:v0ED5pF201* + ID_MODEL_FROM_DATABASE=Fiberbyte USB-inSync DAQ-2500X + +usb:v0EDA* + ID_VENDOR_FROM_DATABASE=Noriake Itron Corp. + +usb:v0EDF* + ID_VENDOR_FROM_DATABASE=e-MDT Co., Ltd + +usb:v0EDFp2060* + ID_MODEL_FROM_DATABASE=FID irock! 100 Series + +usb:v0EE0* + ID_VENDOR_FROM_DATABASE=Shima Seiki Mfg., Ltd + +usb:v0EE1* + ID_VENDOR_FROM_DATABASE=Sarotech Co., Ltd + +usb:v0EE2* + ID_VENDOR_FROM_DATABASE=AMI Semiconductor, Inc. + +usb:v0EE3* + ID_VENDOR_FROM_DATABASE=ComTrue Technology Corp. + +usb:v0EE3p1000* + ID_MODEL_FROM_DATABASE=Image Tank 1.5 + +usb:v0EE4* + ID_VENDOR_FROM_DATABASE=Sunrich Technology, Ltd + +usb:v0EE4p0690* + ID_MODEL_FROM_DATABASE=SATA 3 Adapter + +usb:v0EEE* + ID_VENDOR_FROM_DATABASE=Digital Stream Technology, Inc. + +usb:v0EEEp8810* + ID_MODEL_FROM_DATABASE=Mass Storage Drive + +usb:v0EEF* + ID_VENDOR_FROM_DATABASE=D-WAV Scientific Co., Ltd + +usb:v0EEFp0001* + ID_MODEL_FROM_DATABASE=eGalax TouchScreen + +usb:v0EEFp0002* + ID_MODEL_FROM_DATABASE=Touchscreen Controller(Professional) + +usb:v0EEFp7200* + ID_MODEL_FROM_DATABASE=Touchscreen Controller + +usb:v0EEFpA802* + ID_MODEL_FROM_DATABASE=eGalaxTouch EXC7920 + +usb:v0EF0* + ID_VENDOR_FROM_DATABASE=Hitachi Cable, Ltd + +usb:v0EF1* + ID_VENDOR_FROM_DATABASE=Aichi Micro Intelligent Corp. + +usb:v0EF2* + ID_VENDOR_FROM_DATABASE=I/O Magic Corp. + +usb:v0EF3* + ID_VENDOR_FROM_DATABASE=Lynn Products, Inc. + +usb:v0EF4* + ID_VENDOR_FROM_DATABASE=DSI Datotech + +usb:v0EF5* + ID_VENDOR_FROM_DATABASE=PointChips + +usb:v0EF5p2202* + ID_MODEL_FROM_DATABASE=Flash Disk + +usb:v0EF5p2366* + ID_MODEL_FROM_DATABASE=Flash Disk + +usb:v0EF6* + ID_VENDOR_FROM_DATABASE=Yield Microelectronics Corp. + +usb:v0EF7* + ID_VENDOR_FROM_DATABASE=SM Tech Co., Ltd (Tulip) + +usb:v0EFD* + ID_VENDOR_FROM_DATABASE=Oasis Semiconductor + +usb:v0EFE* + ID_VENDOR_FROM_DATABASE=Wem Technology, Inc. + +usb:v0F03* + ID_VENDOR_FROM_DATABASE=Unitek UPS Systems + +usb:v0F03p0001* + ID_MODEL_FROM_DATABASE=Alpha 1200Sx + +usb:v0F06* + ID_VENDOR_FROM_DATABASE=Visual Frontier Enterprise Co., Ltd + +usb:v0F08* + ID_VENDOR_FROM_DATABASE=CSL Wire & Plug (Shen Zhen) Co. + +usb:v0F0C* + ID_VENDOR_FROM_DATABASE=CAS Corp. + +usb:v0F0D* + ID_VENDOR_FROM_DATABASE=Hori Co., Ltd + +usb:v0F0Dp0011* + ID_MODEL_FROM_DATABASE=Real Arcade Pro 3 + +usb:v0F0E* + ID_VENDOR_FROM_DATABASE=Energy Full Corp. + +usb:v0F11* + ID_VENDOR_FROM_DATABASE=LD Didactic GmbH + +usb:v0F11p1000* + ID_MODEL_FROM_DATABASE=CASSY-S + +usb:v0F11p1010* + ID_MODEL_FROM_DATABASE=Pocket-CASSY + +usb:v0F11p1020* + ID_MODEL_FROM_DATABASE=Mobile-CASSY + +usb:v0F11p1080* + ID_MODEL_FROM_DATABASE=Joule and Wattmeter + +usb:v0F11p1081* + ID_MODEL_FROM_DATABASE=Digital Multimeter P + +usb:v0F11p1090* + ID_MODEL_FROM_DATABASE=UMI P + +usb:v0F11p1100* + ID_MODEL_FROM_DATABASE=X-Ray Apparatus + +usb:v0F11p1101* + ID_MODEL_FROM_DATABASE=X-Ray Apparatus + +usb:v0F11p1200* + ID_MODEL_FROM_DATABASE=VideoCom + +usb:v0F11p2000* + ID_MODEL_FROM_DATABASE=COM3LAB + +usb:v0F11p2010* + ID_MODEL_FROM_DATABASE=Terminal Adapter + +usb:v0F11p2020* + ID_MODEL_FROM_DATABASE=Network Analyser + +usb:v0F11p2030* + ID_MODEL_FROM_DATABASE=Converter Control Unit + +usb:v0F11p2040* + ID_MODEL_FROM_DATABASE=Machine Test System + +usb:v0F12* + ID_VENDOR_FROM_DATABASE=Mars Engineering Corp. + +usb:v0F13* + ID_VENDOR_FROM_DATABASE=Acetek Technology Co., Ltd + +usb:v0F14* + ID_VENDOR_FROM_DATABASE=Ingenico + +usb:v0F14p0012* + ID_MODEL_FROM_DATABASE=Vital'Act 3S + +usb:v0F18* + ID_VENDOR_FROM_DATABASE=Finger Lakes Instrumentation + +usb:v0F18p0002* + ID_MODEL_FROM_DATABASE=CCD + +usb:v0F18p0006* + ID_MODEL_FROM_DATABASE=Focuser + +usb:v0F18p0007* + ID_MODEL_FROM_DATABASE=Filter Wheel + +usb:v0F18p000A* + ID_MODEL_FROM_DATABASE=ProLine CCD + +usb:v0F18p000B* + ID_MODEL_FROM_DATABASE=Color Filter Wheel 4 + +usb:v0F18p000C* + ID_MODEL_FROM_DATABASE=PDF2 + +usb:v0F18p000D* + ID_MODEL_FROM_DATABASE=Guider + +usb:v0F19* + ID_VENDOR_FROM_DATABASE=Oracom Co., Ltd + +usb:v0F1B* + ID_VENDOR_FROM_DATABASE=Onset Computer Corp. + +usb:v0F1C* + ID_VENDOR_FROM_DATABASE=Funai Electric Co., Ltd + +usb:v0F1D* + ID_VENDOR_FROM_DATABASE=Iwill Corp. + +usb:v0F21* + ID_VENDOR_FROM_DATABASE=IOI Technology Corp. + +usb:v0F22* + ID_VENDOR_FROM_DATABASE=Senior Industries, Inc. + +usb:v0F23* + ID_VENDOR_FROM_DATABASE=Leader Tech Manufacturer Co., Ltd + +usb:v0F24* + ID_VENDOR_FROM_DATABASE=Flex-P Industries, Snd., Bhd. + +usb:v0F2D* + ID_VENDOR_FROM_DATABASE=ViPower, Inc. + +usb:v0F2E* + ID_VENDOR_FROM_DATABASE=Geniality Maple Technology Co., Ltd + +usb:v0F2F* + ID_VENDOR_FROM_DATABASE=Priva Design Services + +usb:v0F30* + ID_VENDOR_FROM_DATABASE=Jess Technology Co., Ltd + +usb:v0F30p001C* + ID_MODEL_FROM_DATABASE=PS3 Guitar Controller Dongle + +usb:v0F30p0110* + ID_MODEL_FROM_DATABASE=Dual Analog Rumble Pad + +usb:v0F30p0111* + ID_MODEL_FROM_DATABASE=Colour Rumble Pad + +usb:v0F30p0208* + ID_MODEL_FROM_DATABASE=Xbox & PC Gamepad + +usb:v0F31* + ID_VENDOR_FROM_DATABASE=Chrysalis Development + +usb:v0F32* + ID_VENDOR_FROM_DATABASE=YFC-BonEagle Electric Co., Ltd + +usb:v0F37* + ID_VENDOR_FROM_DATABASE=Kokuyo Co., Ltd + +usb:v0F38* + ID_VENDOR_FROM_DATABASE=Nien-Yi Industrial Corp. + +usb:v0F39* + ID_VENDOR_FROM_DATABASE=TG3 Electronics + +usb:v0F39p0876* + ID_MODEL_FROM_DATABASE=Keyboard [87 Francium Pro] + +usb:v0F39p1086* + ID_MODEL_FROM_DATABASE=DK2108SZ Keyboard [Ducky Zero] + +usb:v0F3D* + ID_VENDOR_FROM_DATABASE=Airprime, Incorporated + +usb:v0F3Dp0112* + ID_MODEL_FROM_DATABASE=CDMA 1xEVDO PC Card, PC 5220 + +usb:v0F41* + ID_VENDOR_FROM_DATABASE=RDC Semiconductor Co., Ltd + +usb:v0F42* + ID_VENDOR_FROM_DATABASE=Nital Consulting Services, Inc. + +usb:v0F44* + ID_VENDOR_FROM_DATABASE=Polhemus + +usb:v0F44pEF11* + ID_MODEL_FROM_DATABASE=Patriot (firmware not loaded) + +usb:v0F44pEF12* + ID_MODEL_FROM_DATABASE=Patriot + +usb:v0F44pFF11* + ID_MODEL_FROM_DATABASE=Liberty (firmware not loaded) + +usb:v0F44pFF12* + ID_MODEL_FROM_DATABASE=Liberty + +usb:v0F4B* + ID_VENDOR_FROM_DATABASE=St. John Technology Co., Ltd + +usb:v0F4C* + ID_VENDOR_FROM_DATABASE=WorldWide Cable Opto Corp. + +usb:v0F4D* + ID_VENDOR_FROM_DATABASE=Microtune, Inc. + +usb:v0F4Dp1000* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v0F4E* + ID_VENDOR_FROM_DATABASE=Freedom Scientific + +usb:v0F52* + ID_VENDOR_FROM_DATABASE=Wing Key Electrical Co., Ltd + +usb:v0F53* + ID_VENDOR_FROM_DATABASE=Dongguan White Horse Cable Factory, Ltd + +usb:v0F54* + ID_VENDOR_FROM_DATABASE=Kawai Musical Instruments Mfg. Co., Ltd + +usb:v0F54p0101* + ID_MODEL_FROM_DATABASE=MP6 Stage Piano + +usb:v0F55* + ID_VENDOR_FROM_DATABASE=AmbiCom, Inc. + +usb:v0F5C* + ID_VENDOR_FROM_DATABASE=Prairiecomm, Inc. + +usb:v0F5D* + ID_VENDOR_FROM_DATABASE=NewAge International, LLC + +usb:v0F5Dp9455* + ID_MODEL_FROM_DATABASE=Compact Drive + +usb:v0F5F* + ID_VENDOR_FROM_DATABASE=Key Technology Corp. + +usb:v0F60* + ID_VENDOR_FROM_DATABASE=NTK, Ltd + +usb:v0F61* + ID_VENDOR_FROM_DATABASE=Varian, Inc. + +usb:v0F62* + ID_VENDOR_FROM_DATABASE=Acrox Technologies Co., Ltd + +usb:v0F62p1001* + ID_MODEL_FROM_DATABASE=Targus Mini Trackball Optical Mouse + +usb:v0F63* + ID_VENDOR_FROM_DATABASE=LeapFrog Enterprises + +usb:v0F63p0010* + ID_MODEL_FROM_DATABASE=Leapster Explorer + +usb:v0F63p0022* + ID_MODEL_FROM_DATABASE=Leap Reader + +usb:v0F63p0500* + ID_MODEL_FROM_DATABASE=Fly Fusion + +usb:v0F63p0600* + ID_MODEL_FROM_DATABASE=Leap Port Turbo + +usb:v0F63p0700* + ID_MODEL_FROM_DATABASE=POGO + +usb:v0F63p0800* + ID_MODEL_FROM_DATABASE=Didj + +usb:v0F63p0900* + ID_MODEL_FROM_DATABASE=TAGSchool + +usb:v0F63p0A00* + ID_MODEL_FROM_DATABASE=Leapster 2 + +usb:v0F63p0B00* + ID_MODEL_FROM_DATABASE=Crammer + +usb:v0F63p0C00* + ID_MODEL_FROM_DATABASE=Tag Jr + +usb:v0F63p0D00* + ID_MODEL_FROM_DATABASE=My Pal Scout + +usb:v0F63p0E00* + ID_MODEL_FROM_DATABASE=Tag32 + +usb:v0F63p0F00* + ID_MODEL_FROM_DATABASE=Tag64 + +usb:v0F63p1000* + ID_MODEL_FROM_DATABASE=Kiwi16 + +usb:v0F63p1100* + ID_MODEL_FROM_DATABASE=Leapster L2x + +usb:v0F63p1111* + ID_MODEL_FROM_DATABASE=Fly Fusion + +usb:v0F63p1300* + ID_MODEL_FROM_DATABASE=Didj UK/France (Leapster Advance) + +usb:v0F68* + ID_VENDOR_FROM_DATABASE=Kobe Steel, Ltd + +usb:v0F69* + ID_VENDOR_FROM_DATABASE=Dionex Corp. + +usb:v0F6A* + ID_VENDOR_FROM_DATABASE=Vibren Technologies, Inc. + +usb:v0F6E* + ID_VENDOR_FROM_DATABASE=INTELLIGENT SYSTEMS + +usb:v0F6Ep0100* + ID_MODEL_FROM_DATABASE=GameBoy Color Emulator + +usb:v0F6Ep0201* + ID_MODEL_FROM_DATABASE=GameBoy Advance Flash Gang Writer + +usb:v0F6Ep0202* + ID_MODEL_FROM_DATABASE=GameBoy Advance Capture + +usb:v0F6Ep0300* + ID_MODEL_FROM_DATABASE=Gamecube DOL Viewer + +usb:v0F6Ep0400* + ID_MODEL_FROM_DATABASE=NDS Emulator + +usb:v0F6Ep0401* + ID_MODEL_FROM_DATABASE=NDS UIC + +usb:v0F6Ep0402* + ID_MODEL_FROM_DATABASE=NDS Writer + +usb:v0F6Ep0403* + ID_MODEL_FROM_DATABASE=NDS Capture + +usb:v0F6Ep0404* + ID_MODEL_FROM_DATABASE=NDS Emulator (Lite) + +usb:v0F73* + ID_VENDOR_FROM_DATABASE=DFI + +usb:v0F78* + ID_VENDOR_FROM_DATABASE=Guntermann & Drunck GmbH + +usb:v0F7C* + ID_VENDOR_FROM_DATABASE=DQ Technology, Inc. + +usb:v0F7D* + ID_VENDOR_FROM_DATABASE=NetBotz, Inc. + +usb:v0F7E* + ID_VENDOR_FROM_DATABASE=Fluke Corp. + +usb:v0F88* + ID_VENDOR_FROM_DATABASE=VTech Holdings, Ltd + +usb:v0F88p3012* + ID_MODEL_FROM_DATABASE=RT2570 + +usb:v0F88p3014* + ID_MODEL_FROM_DATABASE=ZD1211B + +usb:v0F8B* + ID_VENDOR_FROM_DATABASE=Yazaki Corp. + +usb:v0F8C* + ID_VENDOR_FROM_DATABASE=Young Generation International Corp. + +usb:v0F8D* + ID_VENDOR_FROM_DATABASE=Uniwill Computer Corp. + +usb:v0F8E* + ID_VENDOR_FROM_DATABASE=Kingnet Technology Co., Ltd + +usb:v0F8F* + ID_VENDOR_FROM_DATABASE=Soma Networks + +usb:v0F97* + ID_VENDOR_FROM_DATABASE=CviLux Corp. + +usb:v0F98* + ID_VENDOR_FROM_DATABASE=CyberBank Corp. + +usb:v0F9C* + ID_VENDOR_FROM_DATABASE=Hyun Won, Inc. + +usb:v0F9Cp0301* + ID_MODEL_FROM_DATABASE=M-Any Premium DAH-610 MP3/WMA Player + +usb:v0F9Cp0332* + ID_MODEL_FROM_DATABASE=mobiBLU DAH-1200 MP3/Ogg Player + +usb:v0F9E* + ID_VENDOR_FROM_DATABASE=Lucent Technologies + +usb:v0FA3* + ID_VENDOR_FROM_DATABASE=Starconn Electronic Co., Ltd + +usb:v0FA4* + ID_VENDOR_FROM_DATABASE=ATL Technology + +usb:v0FA5* + ID_VENDOR_FROM_DATABASE=Sotec Co., Ltd + +usb:v0FA7* + ID_VENDOR_FROM_DATABASE=Epox Computer Co., Ltd + +usb:v0FA8* + ID_VENDOR_FROM_DATABASE=Logic Controls, Inc. + +usb:v0FAF* + ID_VENDOR_FROM_DATABASE=Winpoint Electronic Corp. + +usb:v0FB0* + ID_VENDOR_FROM_DATABASE=Haurtian Wire & Cable Co., Ltd + +usb:v0FB1* + ID_VENDOR_FROM_DATABASE=Inclose Design, Inc. + +usb:v0FB2* + ID_VENDOR_FROM_DATABASE=Juan-Chern Industrial Co., Ltd + +usb:v0FB6* + ID_VENDOR_FROM_DATABASE=Heber Ltd + +usb:v0FB6p3FC3* + ID_MODEL_FROM_DATABASE=Firefly X10i I/O Board (with firmware) + +usb:v0FB6p3FC4* + ID_MODEL_FROM_DATABASE=Firefly X10i I/O Board (without firmware) + +usb:v0FB8* + ID_VENDOR_FROM_DATABASE=Wistron Corp. + +usb:v0FB8p0002* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v0FB9* + ID_VENDOR_FROM_DATABASE=AACom Corp. + +usb:v0FBA* + ID_VENDOR_FROM_DATABASE=San Shing Electronics Co., Ltd + +usb:v0FBB* + ID_VENDOR_FROM_DATABASE=Bitwise Systems, Inc. + +usb:v0FC1* + ID_VENDOR_FROM_DATABASE=Mitac Internatinal Corp. + +usb:v0FC2* + ID_VENDOR_FROM_DATABASE=Plug and Jack Industrial, Inc. + +usb:v0FC5* + ID_VENDOR_FROM_DATABASE=Delcom Engineering + +usb:v0FC5p1222* + ID_MODEL_FROM_DATABASE=I/O Development Board + +usb:v0FC6* + ID_VENDOR_FROM_DATABASE=Dataplus Supplies, Inc. + +usb:v0FCA* + ID_VENDOR_FROM_DATABASE=Research In Motion, Ltd. + +usb:v0FCAp0001* + ID_MODEL_FROM_DATABASE=Blackberry Handheld + +usb:v0FCAp0004* + ID_MODEL_FROM_DATABASE=Blackberry Handheld + +usb:v0FCAp0006* + ID_MODEL_FROM_DATABASE=Blackberry Pearl + +usb:v0FCAp0008* + ID_MODEL_FROM_DATABASE=Blackberry Pearl + +usb:v0FCAp8001* + ID_MODEL_FROM_DATABASE=Blackberry Handheld + +usb:v0FCAp8004* + ID_MODEL_FROM_DATABASE=Blackberry + +usb:v0FCAp8007* + ID_MODEL_FROM_DATABASE=Blackberry Handheld + +usb:v0FCAp8010* + ID_MODEL_FROM_DATABASE=Blackberry Playbook (Connect to Windows mode) + +usb:v0FCAp8011* + ID_MODEL_FROM_DATABASE=Blackberry Playbook (Connect to Mac mode) + +usb:v0FCAp8020* + ID_MODEL_FROM_DATABASE=Blackberry Playbook (CD-Rom mode) + +usb:v0FCE* + ID_VENDOR_FROM_DATABASE=Sony Ericsson Mobile Communications AB + +usb:v0FCEp0076* + ID_MODEL_FROM_DATABASE=W910i (Multimedia mode) + +usb:v0FCEp00AF* + ID_MODEL_FROM_DATABASE=V640i Phone [PTP Camera] + +usb:v0FCEp00D4* + ID_MODEL_FROM_DATABASE=C902 [MTP] + +usb:v0FCEp00D9* + ID_MODEL_FROM_DATABASE=C702 Phone + +usb:v0FCEp0112* + ID_MODEL_FROM_DATABASE=W995 Walkman Phone + +usb:v0FCEp014E* + ID_MODEL_FROM_DATABASE=J108i Cedar (MTP mode) + +usb:v0FCEp015A* + ID_MODEL_FROM_DATABASE=Xperia Pro [Media Transfer Protocol] + +usb:v0FCEp0166* + ID_MODEL_FROM_DATABASE=Xperia Mini Pro + +usb:v0FCEp0167* + ID_MODEL_FROM_DATABASE=ST15i (Xperia mini) + +usb:v0FCEp0169* + ID_MODEL_FROM_DATABASE=Xperia S + +usb:v0FCEp0172* + ID_MODEL_FROM_DATABASE=Xperia P + +usb:v0FCEp0177* + ID_MODEL_FROM_DATABASE=Xperia Ion [Mass Storage] + +usb:v0FCEp01BB* + ID_MODEL_FROM_DATABASE=D5803 [Xperia Z3 Compact] (MTP mode) + +usb:v0FCEp0DDE* + ID_MODEL_FROM_DATABASE=Xperia Mini Pro Bootloader + +usb:v0FCEp1010* + ID_MODEL_FROM_DATABASE=WMC Modem + +usb:v0FCEp10AF* + ID_MODEL_FROM_DATABASE=V640i Phone [PictBridge] + +usb:v0FCEp10D4* + ID_MODEL_FROM_DATABASE=C902 Phone [PictBridge] + +usb:v0FCEp2105* + ID_MODEL_FROM_DATABASE=W715 Phone + +usb:v0FCEp2137* + ID_MODEL_FROM_DATABASE=Xperia X10 mini (USB debug) + +usb:v0FCEp2138* + ID_MODEL_FROM_DATABASE=Xperia X10 mini pro (Debug) + +usb:v0FCEp2149* + ID_MODEL_FROM_DATABASE=Xperia X8 (debug) + +usb:v0FCEp214E* + ID_MODEL_FROM_DATABASE=J108i Cedar (Windows-driver mode) + +usb:v0FCEp3137* + ID_MODEL_FROM_DATABASE=Xperia X10 mini + +usb:v0FCEp3138* + ID_MODEL_FROM_DATABASE=Xperia X10 mini pro + +usb:v0FCEp3149* + ID_MODEL_FROM_DATABASE=Xperia X8 + +usb:v0FCEp514F* + ID_MODEL_FROM_DATABASE=Xperia arc S [Adb-Enable Mode] + +usb:v0FCEp5169* + ID_MODEL_FROM_DATABASE=Xperia S [Adb-Enable Mode] + +usb:v0FCEp5177* + ID_MODEL_FROM_DATABASE=Xperia Ion [Debug Mode] + +usb:v0FCEp518C* + ID_MODEL_FROM_DATABASE=C1605 [Xperia E dual] MTD mode + +usb:v0FCEp614F* + ID_MODEL_FROM_DATABASE=Xperia X12 (debug mode) + +usb:v0FCEp6166* + ID_MODEL_FROM_DATABASE=Xperia Mini Pro + +usb:v0FCEp618C* + ID_MODEL_FROM_DATABASE=C1605 [Xperia E dual] MSC mode + +usb:v0FCEp715A* + ID_MODEL_FROM_DATABASE=Xperia Pro [Tethering] + +usb:v0FCEp7166* + ID_MODEL_FROM_DATABASE=Xperia Mini Pro (Tethering mode) + +usb:v0FCEp7177* + ID_MODEL_FROM_DATABASE=Xperia Ion [Tethering] + +usb:v0FCEp8004* + ID_MODEL_FROM_DATABASE=9000 Phone [Mass Storage] + +usb:v0FCEpADDE* + ID_MODEL_FROM_DATABASE=C2005 (Xperia M dual) in service mode + +usb:v0FCEpD008* + ID_MODEL_FROM_DATABASE=V800-Vodafone 802SE Phone + +usb:v0FCEpD016* + ID_MODEL_FROM_DATABASE=K750i Phone + +usb:v0FCEpD017* + ID_MODEL_FROM_DATABASE=K608i Phone + +usb:v0FCEpD019* + ID_MODEL_FROM_DATABASE=VDC EGPRS Modem + +usb:v0FCEpD025* + ID_MODEL_FROM_DATABASE=520 WMC Data Modem + +usb:v0FCEpD028* + ID_MODEL_FROM_DATABASE=W800i + +usb:v0FCEpD038* + ID_MODEL_FROM_DATABASE=W850i Phone + +usb:v0FCEpD039* + ID_MODEL_FROM_DATABASE=K800i (phone mode) + +usb:v0FCEpD041* + ID_MODEL_FROM_DATABASE=K510i Phone + +usb:v0FCEpD042* + ID_MODEL_FROM_DATABASE=W810i Phone + +usb:v0FCEpD043* + ID_MODEL_FROM_DATABASE=V630i Phone + +usb:v0FCEpD046* + ID_MODEL_FROM_DATABASE=K610i Phone + +usb:v0FCEpD065* + ID_MODEL_FROM_DATABASE=W960i Phone (PC Suite) + +usb:v0FCEpD076* + ID_MODEL_FROM_DATABASE=W910i (Phone mode) + +usb:v0FCEpD089* + ID_MODEL_FROM_DATABASE=W580i Phone (mass storage) + +usb:v0FCEpD0A1* + ID_MODEL_FROM_DATABASE=K810 + +usb:v0FCEpD0AF* + ID_MODEL_FROM_DATABASE=V640i Phone + +usb:v0FCEpD0CF* + ID_MODEL_FROM_DATABASE=MD300 Mobile Broadband Modem + +usb:v0FCEpD0D4* + ID_MODEL_FROM_DATABASE=C902 Phone [Modem] + +usb:v0FCEpD0E1* + ID_MODEL_FROM_DATABASE=MD400 Mobile Broadband Modem + +usb:v0FCEpD12A* + ID_MODEL_FROM_DATABASE=U100i Yari Phone + +usb:v0FCEpD12E* + ID_MODEL_FROM_DATABASE=Xperia X10 + +usb:v0FCEpD14E* + ID_MODEL_FROM_DATABASE=J108i Cedar (modem mode) + +usb:v0FCEpE000* + ID_MODEL_FROM_DATABASE=K810 (PictBridge mode) + +usb:v0FCEpE039* + ID_MODEL_FROM_DATABASE=K800i (msc mode) + +usb:v0FCEpE042* + ID_MODEL_FROM_DATABASE=W810i Phone + +usb:v0FCEpE043* + ID_MODEL_FROM_DATABASE=V630i Phone [Mass Storage] + +usb:v0FCEpE075* + ID_MODEL_FROM_DATABASE=K850i + +usb:v0FCEpE076* + ID_MODEL_FROM_DATABASE=W910i (Mass storage) + +usb:v0FCEpE089* + ID_MODEL_FROM_DATABASE=W580i Phone + +usb:v0FCEpE090* + ID_MODEL_FROM_DATABASE=W200 Phone (Mass Storage) + +usb:v0FCEpE0A1* + ID_MODEL_FROM_DATABASE=K810 (Mass Storage mode) + +usb:v0FCEpE0A3* + ID_MODEL_FROM_DATABASE=W660i + +usb:v0FCEpE0AF* + ID_MODEL_FROM_DATABASE=V640i Phone [Mass Storage] + +usb:v0FCEpE0D4* + ID_MODEL_FROM_DATABASE=C902 Phone [Mass Storage] + +usb:v0FCEpE0EF* + ID_MODEL_FROM_DATABASE=C905 Phone [Mass Storage] + +usb:v0FCEpE0F3* + ID_MODEL_FROM_DATABASE=W595 + +usb:v0FCEpE105* + ID_MODEL_FROM_DATABASE=W705 + +usb:v0FCEpE112* + ID_MODEL_FROM_DATABASE=W995 Phone (Mass Storage) + +usb:v0FCEpE12E* + ID_MODEL_FROM_DATABASE=X10i Phone + +usb:v0FCEpE133* + ID_MODEL_FROM_DATABASE=Vivaz + +usb:v0FCEpE14E* + ID_MODEL_FROM_DATABASE=J108i Cedar (mass-storage mode) + +usb:v0FCEpE14F* + ID_MODEL_FROM_DATABASE=Xperia Arc/X12 + +usb:v0FCEpE15A* + ID_MODEL_FROM_DATABASE=Xperia Pro [Mass Storage Class] + +usb:v0FCEpE161* + ID_MODEL_FROM_DATABASE=Xperia Ray + +usb:v0FCEpE166* + ID_MODEL_FROM_DATABASE=Xperia Mini Pro + +usb:v0FCEpE167* + ID_MODEL_FROM_DATABASE=XPERIA mini + +usb:v0FCEpE19B* + ID_MODEL_FROM_DATABASE=C2005 [Xperia M dual] (Mass Storage) + +usb:v0FCEpF0FA* + ID_MODEL_FROM_DATABASE=MN800 / Smartwatch 2 (DFU mode) + +usb:v0FCF* + ID_VENDOR_FROM_DATABASE=Dynastream Innovations, Inc. + +usb:v0FCFp1003* + ID_MODEL_FROM_DATABASE=ANT Development Board + +usb:v0FCFp1004* + ID_MODEL_FROM_DATABASE=ANTUSB Stick + +usb:v0FCFp1006* + ID_MODEL_FROM_DATABASE=ANT Development Board + +usb:v0FCFp1008* + ID_MODEL_FROM_DATABASE=ANTUSB2 Stick + +usb:v0FCFp1009* + ID_MODEL_FROM_DATABASE=ANTUSB-m Stick + +usb:v0FD0* + ID_VENDOR_FROM_DATABASE=Tulip Computers B.V. + +usb:v0FD1* + ID_VENDOR_FROM_DATABASE=Giant Electronics Ltd. + +usb:v0FD2* + ID_VENDOR_FROM_DATABASE=Seac Banche + +usb:v0FD2p0001* + ID_MODEL_FROM_DATABASE=RDS 6000 + +usb:v0FD4* + ID_VENDOR_FROM_DATABASE=Tenovis GmbH & Co., KG + +usb:v0FD5* + ID_VENDOR_FROM_DATABASE=Direct Access Technology, Inc. + +usb:v0FD9* + ID_VENDOR_FROM_DATABASE=Elgato Systems GmbH + +usb:v0FD9p0011* + ID_MODEL_FROM_DATABASE=EyeTV Diversity + +usb:v0FD9p0018* + ID_MODEL_FROM_DATABASE=EyeTV Hybrid + +usb:v0FD9p0020* + ID_MODEL_FROM_DATABASE=EyeTV DTT Deluxe + +usb:v0FD9p0021* + ID_MODEL_FROM_DATABASE=EyeTV DTT + +usb:v0FD9p002A* + ID_MODEL_FROM_DATABASE=EyeTV Sat + +usb:v0FD9p002C* + ID_MODEL_FROM_DATABASE=EyeTV DTT Deluxe v2 + +usb:v0FD9p0033* + ID_MODEL_FROM_DATABASE=Video Capture + +usb:v0FD9p0037* + ID_MODEL_FROM_DATABASE=Video Capture v2 + +usb:v0FDA* + ID_VENDOR_FROM_DATABASE=Quantec Networks GmbH + +usb:v0FDAp0100* + ID_MODEL_FROM_DATABASE=quanton flight control + +usb:v0FDC* + ID_VENDOR_FROM_DATABASE=Micro Plus + +usb:v0FDE* + ID_VENDOR_FROM_DATABASE=Oregon Scientific + +usb:v0FDEpCA01* + ID_MODEL_FROM_DATABASE=WMRS200 weather station + +usb:v0FDEpCA05* + ID_MODEL_FROM_DATABASE=CM160 + +usb:v0FE0* + ID_VENDOR_FROM_DATABASE=Osterhout Design Group + +usb:v0FE0p0100* + ID_MODEL_FROM_DATABASE=Bluetooth Mouse + +usb:v0FE0p0101* + ID_MODEL_FROM_DATABASE=Bluetooth IMU + +usb:v0FE0p0200* + ID_MODEL_FROM_DATABASE=Bluetooth Keypad + +usb:v0FE4* + ID_VENDOR_FROM_DATABASE=IN-Tech Electronics, Ltd + +usb:v0FE5* + ID_VENDOR_FROM_DATABASE=Greenconn (U.S.A.), Inc. + +usb:v0FE6* + ID_VENDOR_FROM_DATABASE=Kontron (Industrial Computer Source / ICS Advent) + +usb:v0FE6p8101* + ID_MODEL_FROM_DATABASE=DM9601 Fast Ethernet Adapter + +usb:v0FE6p811E* + ID_MODEL_FROM_DATABASE=Parallel Adapter + +usb:v0FE6p9700* + ID_MODEL_FROM_DATABASE=DM9601 Fast Ethernet Adapter + +usb:v0FE9* + ID_VENDOR_FROM_DATABASE=DVICO + +usb:v0FE9p4020* + ID_MODEL_FROM_DATABASE=TViX M-6500 + +usb:v0FE9pDB00* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T (MT352+LgZ201) (uninitialized) + +usb:v0FE9pDB01* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T (MT352+LgZ201) (initialized) + +usb:v0FE9pDB10* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T (MT352+Thomson7579) (uninitialized) + +usb:v0FE9pDB11* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T (MT352+Thomson7579) (initialized) + +usb:v0FE9pDB78* + ID_MODEL_FROM_DATABASE=FusionHDTV DVB-T Dual Digital 4 (ZL10353+xc2028/xc3028) (initialized) + +usb:v0FEA* + ID_VENDOR_FROM_DATABASE=United Computer Accessories + +usb:v0FEB* + ID_VENDOR_FROM_DATABASE=CRS Electronic Co., Ltd + +usb:v0FEC* + ID_VENDOR_FROM_DATABASE=UMC Electronics Co., Ltd + +usb:v0FED* + ID_VENDOR_FROM_DATABASE=Access Co., Ltd + +usb:v0FEE* + ID_VENDOR_FROM_DATABASE=Xsido Corp. + +usb:v0FEF* + ID_VENDOR_FROM_DATABASE=MJ Research, Inc. + +usb:v0FF6* + ID_VENDOR_FROM_DATABASE=Core Valley Co., Ltd + +usb:v0FF7* + ID_VENDOR_FROM_DATABASE=CHI SHING Computer Accessories Co., Ltd + +usb:v0FFC* + ID_VENDOR_FROM_DATABASE=Clavia DMI AB + +usb:v0FFCp0021* + ID_MODEL_FROM_DATABASE=Nord Stage 2 + +usb:v0FFD* + ID_VENDOR_FROM_DATABASE=EarlySense + +usb:v0FFDpFF00* + ID_MODEL_FROM_DATABASE=OEM + +usb:v0FFF* + ID_VENDOR_FROM_DATABASE=Aopen, Inc. + +usb:v1000* + ID_VENDOR_FROM_DATABASE=Speed Tech Corp. + +usb:v1000p153B* + ID_MODEL_FROM_DATABASE=TerraTec Electronic GmbH + +usb:v1001* + ID_VENDOR_FROM_DATABASE=Ritronics Components (S) Pte., Ltd + +usb:v1003* + ID_VENDOR_FROM_DATABASE=Sigma Corp. + +usb:v1003p0003* + ID_MODEL_FROM_DATABASE=SD14 + +usb:v1003p0100* + ID_MODEL_FROM_DATABASE=SD9/SD10 + +usb:v1004* + ID_VENDOR_FROM_DATABASE=LG Electronics, Inc. + +usb:v1004p1FAE* + ID_MODEL_FROM_DATABASE=U8120 3G Cellphone + +usb:v1004p6000* + ID_MODEL_FROM_DATABASE=Various Mobile Phones + +usb:v1004p6005* + ID_MODEL_FROM_DATABASE=T5100 + +usb:v1004p6018* + ID_MODEL_FROM_DATABASE=GM360/GD510/GW520/KP501 + +usb:v1004p618E* + ID_MODEL_FROM_DATABASE=Ally/Optimus One/Vortex (debug mode) + +usb:v1004p618F* + ID_MODEL_FROM_DATABASE=Ally/Optimus One + +usb:v1004p61C5* + ID_MODEL_FROM_DATABASE=P880 / Charge only + +usb:v1004p61C6* + ID_MODEL_FROM_DATABASE=Vortex (msc) + +usb:v1004p61CC* + ID_MODEL_FROM_DATABASE=Optimus S + +usb:v1004p61DA* + ID_MODEL_FROM_DATABASE=G2 Android Phone [tethering mode] + +usb:v1004p61F1* + ID_MODEL_FROM_DATABASE=Optimus Android Phone [LG Software mode] + +usb:v1004p61F9* + ID_MODEL_FROM_DATABASE=Optimus (Various Models) MTP Mode + +usb:v1004p61FC* + ID_MODEL_FROM_DATABASE=Optimus 3 + +usb:v1004p61FE* + ID_MODEL_FROM_DATABASE=Optimus Android Phone [USB tethering mode] + +usb:v1004p6300* + ID_MODEL_FROM_DATABASE=G2/Optimus Android Phone + +usb:v1004p631C* + ID_MODEL_FROM_DATABASE=G2/Optimus Android Phone [MTP mode] + +usb:v1004p631D* + ID_MODEL_FROM_DATABASE=Optimus Android Phone (Camera/PTP Mode) + +usb:v1004p631E* + ID_MODEL_FROM_DATABASE=G2/Optimus Android Phone [Camera/PTP mode] + +usb:v1004p631F* + ID_MODEL_FROM_DATABASE=Optimus Android Phone (Charge Mode) + +usb:v1004p633E* + ID_MODEL_FROM_DATABASE=G2 Android Phone [MTP mode] + +usb:v1004p6344* + ID_MODEL_FROM_DATABASE=G2 Android Phone [tethering mode] + +usb:v1004p6356* + ID_MODEL_FROM_DATABASE=Optimus Android Phone [Virtual CD mode] + +usb:v1004p6800* + ID_MODEL_FROM_DATABASE=CDMA Modem + +usb:v1004p7000* + ID_MODEL_FROM_DATABASE=LG LDP-7024D(LD)USB + +usb:v1004p91C8* + ID_MODEL_FROM_DATABASE=P880 / USB tethering + +usb:v1004pA400* + ID_MODEL_FROM_DATABASE=Renoir (KC910) + +usb:v1005* + ID_VENDOR_FROM_DATABASE=Apacer Technology, Inc. + +usb:v1005p1001* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v1005p1004* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v1005p1006* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v1005pB113* + ID_MODEL_FROM_DATABASE=Handy Steno/AH123 / Handy Steno 2.0/HT203 + +usb:v1005pB223* + ID_MODEL_FROM_DATABASE=CD-RW + 6in1 Card Reader Digital Storage / Converter + +usb:v1006* + ID_VENDOR_FROM_DATABASE=iRiver, Ltd. + +usb:v1006p3001* + ID_MODEL_FROM_DATABASE=iHP-100 + +usb:v1006p3002* + ID_MODEL_FROM_DATABASE=iHP-120/140 MP3 Player + +usb:v1006p3003* + ID_MODEL_FROM_DATABASE=H320/H340 + +usb:v1006p3004* + ID_MODEL_FROM_DATABASE=H340 (mtp) + +usb:v1009* + ID_VENDOR_FROM_DATABASE=Emuzed, Inc. + +usb:v1009p000E* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v1009p0013* + ID_MODEL_FROM_DATABASE=Angel MPEG Device + +usb:v1009p0015* + ID_MODEL_FROM_DATABASE=Lumanate Wave PAL SECAM DVBT Device + +usb:v1009p0016* + ID_MODEL_FROM_DATABASE=Lumanate Wave NTSC/ATSC Combo Device + +usb:v100A* + ID_VENDOR_FROM_DATABASE=AV Chaseway, Ltd + +usb:v100Ap2402* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v100Ap2404* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v100Ap2405* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v100Ap2406* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v100ApA0C0* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v100B* + ID_VENDOR_FROM_DATABASE=Chou Chin Industrial Co., Ltd + +usb:v100D* + ID_VENDOR_FROM_DATABASE=Netopia, Inc. + +usb:v100Dp3342* + ID_MODEL_FROM_DATABASE=Cayman 3352 DSL Modem + +usb:v100Dp3382* + ID_MODEL_FROM_DATABASE=3380 Series Network Interface + +usb:v100Dp6072* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v100Dp9031* + ID_MODEL_FROM_DATABASE=Motorola 802.11n Dualband USB Wireless Adapter + +usb:v100Dp9032* + ID_MODEL_FROM_DATABASE=Motorola 802.11n 5G USB Wireless Adapter + +usb:v100DpCB01* + ID_MODEL_FROM_DATABASE=Cayman 3341 Ethernet DSL Router + +usb:v1010* + ID_VENDOR_FROM_DATABASE=Fukuda Denshi Co., Ltd + +usb:v1011* + ID_VENDOR_FROM_DATABASE=Mobile Media Tech. + +usb:v1011p0001* + ID_MODEL_FROM_DATABASE=AccFast Mp3 + +usb:v1012* + ID_VENDOR_FROM_DATABASE=SDKM Fibres, Wires & Cables Berhad + +usb:v1013* + ID_VENDOR_FROM_DATABASE=TST-Touchless Sensor Technology AG + +usb:v1014* + ID_VENDOR_FROM_DATABASE=Densitron Technologies PLC + +usb:v1015* + ID_VENDOR_FROM_DATABASE=Softronics Pty., Ltd + +usb:v1016* + ID_VENDOR_FROM_DATABASE=Xiamen Hung's Enterprise Co., Ltd + +usb:v1017* + ID_VENDOR_FROM_DATABASE=Speedy Industrial Supplies, Pte., Ltd + +usb:v1019* + ID_VENDOR_FROM_DATABASE=Elitegroup Computer Systems (ECS) + +usb:v1019p0C55* + ID_MODEL_FROM_DATABASE=Flash Reader, Desknote UCR-61S2B + +usb:v1019p0F38* + ID_MODEL_FROM_DATABASE=Infrared Receiver + +usb:v1020* + ID_VENDOR_FROM_DATABASE=Labtec + +usb:v1020p0006* + ID_MODEL_FROM_DATABASE=Wireless Keyboard + +usb:v1020p000A* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse + +usb:v1020p0106* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse + +usb:v1022* + ID_VENDOR_FROM_DATABASE=Shinko Shoji Co., Ltd + +usb:v1025* + ID_VENDOR_FROM_DATABASE=Hyper-Paltek + +usb:v1025p005E* + ID_MODEL_FROM_DATABASE=USB DVB-T device + +usb:v1025p005F* + ID_MODEL_FROM_DATABASE=USB DVB-T device + +usb:v1025p0300* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v1025p0350* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v1026* + ID_VENDOR_FROM_DATABASE=Newly Corp. + +usb:v1027* + ID_VENDOR_FROM_DATABASE=Time Domain + +usb:v1028* + ID_VENDOR_FROM_DATABASE=Inovys Corp. + +usb:v1029* + ID_VENDOR_FROM_DATABASE=Atlantic Coast Telesys + +usb:v102A* + ID_VENDOR_FROM_DATABASE=Ramos Technology Co., Ltd + +usb:v102B* + ID_VENDOR_FROM_DATABASE=Infotronic America, Inc. + +usb:v102C* + ID_VENDOR_FROM_DATABASE=Etoms Electronics Corp. + +usb:v102Cp6151* + ID_MODEL_FROM_DATABASE=Q-Cam Sangha CIF + +usb:v102Cp6251* + ID_MODEL_FROM_DATABASE=Q-Cam VGA + +usb:v102D* + ID_VENDOR_FROM_DATABASE=Winic Corp. + +usb:v1031* + ID_VENDOR_FROM_DATABASE=Comax Technology, Inc. + +usb:v1032* + ID_VENDOR_FROM_DATABASE=C-One Technology Corp. + +usb:v1033* + ID_VENDOR_FROM_DATABASE=Nucam Corp. + +usb:v1033p0068* + ID_MODEL_FROM_DATABASE=3,5'' HDD case MD-231 + +usb:v1038* + ID_VENDOR_FROM_DATABASE=SteelSeries ApS + +usb:v1038p0100* + ID_MODEL_FROM_DATABASE=Ideazon Zboard + +usb:v1038p1361* + ID_MODEL_FROM_DATABASE=Ideazon Sensei + +usb:v1039* + ID_VENDOR_FROM_DATABASE=devolo AG + +usb:v1039p0824* + ID_MODEL_FROM_DATABASE=1866 802.11bg [Texas Instruments TNETW1450] + +usb:v1039p2140* + ID_MODEL_FROM_DATABASE=dsl+ 1100 duo + +usb:v103A* + ID_VENDOR_FROM_DATABASE=PSA + +usb:v103ApF000* + ID_MODEL_FROM_DATABASE=Actia Evo XS + +usb:v103D* + ID_VENDOR_FROM_DATABASE=Stanton + +usb:v103Dp0100* + ID_MODEL_FROM_DATABASE=ScratchAmp + +usb:v103Dp0101* + ID_MODEL_FROM_DATABASE=ScratchAmp + +usb:v1043* + ID_VENDOR_FROM_DATABASE=iCreate Technologies Corp. + +usb:v1043p160F* + ID_MODEL_FROM_DATABASE=Wireless Network Adapter + +usb:v1043p4901* + ID_MODEL_FROM_DATABASE=AV-836 Video Capture Device + +usb:v1043p8006* + ID_MODEL_FROM_DATABASE=Flash Disk 32-256 MB + +usb:v1043p8012* + ID_MODEL_FROM_DATABASE=Flash Disk 256 MB + +usb:v1044* + ID_VENDOR_FROM_DATABASE=Chu Yuen Enterprise Co., Ltd + +usb:v1044p7001* + ID_MODEL_FROM_DATABASE=Gigabyte U7000 DVB-T tuner + +usb:v1044p7002* + ID_MODEL_FROM_DATABASE=Gigabyte U8000 DVB-T tuner + +usb:v1044p7004* + ID_MODEL_FROM_DATABASE=Gigabyte U7100 DVB-T tuner + +usb:v1044p7005* + ID_MODEL_FROM_DATABASE=Gigabyte U7200 DVB-T tuner [AF9035] + +usb:v1044p7006* + ID_MODEL_FROM_DATABASE=Gigabyte U6000 DVB-T tuner [em2863] + +usb:v1044p8001* + ID_MODEL_FROM_DATABASE=GN-54G + +usb:v1044p8002* + ID_MODEL_FROM_DATABASE=GN-BR402W + +usb:v1044p8003* + ID_MODEL_FROM_DATABASE=GN-WLBM101 + +usb:v1044p8004* + ID_MODEL_FROM_DATABASE=GN-WLBZ101 802.11b Adapter + +usb:v1044p8005* + ID_MODEL_FROM_DATABASE=GN-WLBZ201 802.11b Adapter + +usb:v1044p8006* + ID_MODEL_FROM_DATABASE=GN-WBZB-M 802.11b Adapter + +usb:v1044p8007* + ID_MODEL_FROM_DATABASE=GN-WBKG + +usb:v1044p8008* + ID_MODEL_FROM_DATABASE=GN-WB01GS + +usb:v1044p800A* + ID_MODEL_FROM_DATABASE=GN-WI05GS + +usb:v1044p800B* + ID_MODEL_FROM_DATABASE=GN-WB30N 802.11n WLAN Card + +usb:v1044p800C* + ID_MODEL_FROM_DATABASE=GN-WB31N 802.11n USB WLAN Card + +usb:v1044p800D* + ID_MODEL_FROM_DATABASE=GN-WB32L 802.11n USB WLAN Card + +usb:v1046* + ID_VENDOR_FROM_DATABASE=Winbond Electronics Corp. [hex] + +usb:v1046p6694* + ID_MODEL_FROM_DATABASE=Generic W6694 USB + +usb:v1046p8901* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1046p9967* + ID_MODEL_FROM_DATABASE=W9967CF/W9968CF Webcam IC + +usb:v1048* + ID_VENDOR_FROM_DATABASE=Targus Group International + +usb:v1048p2010* + ID_MODEL_FROM_DATABASE=4-Port hub + +usb:v104B* + ID_VENDOR_FROM_DATABASE=Mylex / Buslogic + +usb:v104C* + ID_VENDOR_FROM_DATABASE=AMCO TEC International, Inc. + +usb:v104D* + ID_VENDOR_FROM_DATABASE=Newport Corporation + +usb:v104Dp1003* + ID_MODEL_FROM_DATABASE=Model-52 LED Light Source Power Supply and Driver + +usb:v104F* + ID_VENDOR_FROM_DATABASE=WB Electronics + +usb:v104Fp0001* + ID_MODEL_FROM_DATABASE=Infinity Phoenix + +usb:v104Fp0002* + ID_MODEL_FROM_DATABASE=Smartmouse + +usb:v104Fp0003* + ID_MODEL_FROM_DATABASE=FunProgrammer + +usb:v104Fp0004* + ID_MODEL_FROM_DATABASE=Infinity Unlimited + +usb:v104Fp0006* + ID_MODEL_FROM_DATABASE=Infinity Smart + +usb:v104Fp0007* + ID_MODEL_FROM_DATABASE=Infinity Smart module + +usb:v104Fp0008* + ID_MODEL_FROM_DATABASE=Infinity CryptoKey + +usb:v104Fp0009* + ID_MODEL_FROM_DATABASE=RE-BL PlayStation 3 IR-to-Bluetooth converter + +usb:v1050* + ID_VENDOR_FROM_DATABASE=Yubico.com + +usb:v1050p0010* + ID_MODEL_FROM_DATABASE=Yubikey (v1 or v2) + +usb:v1050p0110* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) OTP + +usb:v1050p0111* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) OTP+CCID + +usb:v1050p0112* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) CCID + +usb:v1050p0113* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) U2F + +usb:v1050p0114* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) OTP+U2F + +usb:v1050p0115* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) U2F+CCID + +usb:v1050p0116* + ID_MODEL_FROM_DATABASE=Yubikey NEO(-N) OTP+U2F+CCID + +usb:v1050p0120* + ID_MODEL_FROM_DATABASE=Yubikey Touch U2F Security Key + +usb:v1050p0200* + ID_MODEL_FROM_DATABASE=Gnubby U2F + +usb:v1050p0211* + ID_MODEL_FROM_DATABASE=Gnubby + +usb:v1050p0401* + ID_MODEL_FROM_DATABASE=Yubikey 4 OTP + +usb:v1050p0402* + ID_MODEL_FROM_DATABASE=Yubikey 4 U2F + +usb:v1050p0403* + ID_MODEL_FROM_DATABASE=Yubikey 4 OTP+U2F + +usb:v1050p0404* + ID_MODEL_FROM_DATABASE=Yubikey 4 CCID + +usb:v1050p0405* + ID_MODEL_FROM_DATABASE=Yubikey 4 OTP+CCID + +usb:v1050p0406* + ID_MODEL_FROM_DATABASE=Yubikey 4 U2F+CCID + +usb:v1050p0407* + ID_MODEL_FROM_DATABASE=Yubikey 4 OTP+U2F+CCID + +usb:v1050p0410* + ID_MODEL_FROM_DATABASE=Yubikey plus OTP+U2F + +usb:v1053* + ID_VENDOR_FROM_DATABASE=Immanuel Electronics Co., Ltd + +usb:v1054* + ID_VENDOR_FROM_DATABASE=BMS International Beheer N.V. + +usb:v1054p5004* + ID_MODEL_FROM_DATABASE=DSL 7420 Loader + +usb:v1054p5005* + ID_MODEL_FROM_DATABASE=DSL 7420 LAN Modem + +usb:v1055* + ID_VENDOR_FROM_DATABASE=Complex Micro Interconnection Co., Ltd + +usb:v1056* + ID_VENDOR_FROM_DATABASE=Hsin Chen Ent Co., Ltd + +usb:v1057* + ID_VENDOR_FROM_DATABASE=ON Semiconductor + +usb:v1058* + ID_VENDOR_FROM_DATABASE=Western Digital Technologies, Inc. + +usb:v1058p0200* + ID_MODEL_FROM_DATABASE=FireWire USB Combo + +usb:v1058p0400* + ID_MODEL_FROM_DATABASE=External HDD + +usb:v1058p0500* + ID_MODEL_FROM_DATABASE=hub + +usb:v1058p0701* + ID_MODEL_FROM_DATABASE=WD Passport (WDXMS) + +usb:v1058p0702* + ID_MODEL_FROM_DATABASE=WD Passport (WDXMS) + +usb:v1058p0704* + ID_MODEL_FROM_DATABASE=My Passport Essential (WDME) + +usb:v1058p0705* + ID_MODEL_FROM_DATABASE=My Passport Elite (WDML) + +usb:v1058p070A* + ID_MODEL_FROM_DATABASE=My Passport Essential (WDBAAA), My Passport for Mac (WDBAAB), My Passport Essential SE (WDBABM), My Passport SE for Mac (WDBABW) + +usb:v1058p070B* + ID_MODEL_FROM_DATABASE=My Passport Elite (WDBAAC) + +usb:v1058p070C* + ID_MODEL_FROM_DATABASE=My Passport Studio (WDBAAE) + +usb:v1058p071A* + ID_MODEL_FROM_DATABASE=My Passport Essential (WDBAAA) + +usb:v1058p071D* + ID_MODEL_FROM_DATABASE=My Passport Studio (WDBALG) + +usb:v1058p0730* + ID_MODEL_FROM_DATABASE=My Passport Essential (WDBACY) + +usb:v1058p0732* + ID_MODEL_FROM_DATABASE=My Passport Essential SE (WDBGYS) + +usb:v1058p0740* + ID_MODEL_FROM_DATABASE=My Passport Essential (WDBACY) + +usb:v1058p0741* + ID_MODEL_FROM_DATABASE=My Passport Ultra + +usb:v1058p0742* + ID_MODEL_FROM_DATABASE=My Passport Essential SE (WDBGYS) + +usb:v1058p0748* + ID_MODEL_FROM_DATABASE=My Passport (WDBKXH, WDBY8L) + +usb:v1058p07A8* + ID_MODEL_FROM_DATABASE=My Passport (WDBBEP), My Passport for Mac (WDBLUZ) + +usb:v1058p0810* + ID_MODEL_FROM_DATABASE=My Passport Ultra (WDBZFP) + +usb:v1058p0820* + ID_MODEL_FROM_DATABASE=My Passport Ultra (WDBMWV, WDBZFP) + +usb:v1058p0830* + ID_MODEL_FROM_DATABASE=My Passport Ultra (WDBZFP) + +usb:v1058p0900* + ID_MODEL_FROM_DATABASE=MyBook Essential External HDD + +usb:v1058p0901* + ID_MODEL_FROM_DATABASE=My Book Essential Edition (Green Ring) (WDG1U) + +usb:v1058p0902* + ID_MODEL_FROM_DATABASE=My Book Pro Edition (WDG1T) + +usb:v1058p0903* + ID_MODEL_FROM_DATABASE=My Book Premium Edition + +usb:v1058p0910* + ID_MODEL_FROM_DATABASE=My Book Essential Edition (Green Ring) (WDG1U) + +usb:v1058p1001* + ID_MODEL_FROM_DATABASE=Elements Desktop (WDE1U) + +usb:v1058p1003* + ID_MODEL_FROM_DATABASE=WD Elements Desktop (WDE1UBK) + +usb:v1058p1010* + ID_MODEL_FROM_DATABASE=Elements Portable (WDBAAR) + +usb:v1058p1021* + ID_MODEL_FROM_DATABASE=Elements Desktop (WDBAAU) + +usb:v1058p1023* + ID_MODEL_FROM_DATABASE=Elements SE Portable (WDBABV) + +usb:v1058p1042* + ID_MODEL_FROM_DATABASE=Elements SE Portable (WDBPCK) + +usb:v1058p1048* + ID_MODEL_FROM_DATABASE=Elements Portable (WDBU6Y) + +usb:v1058p107C* + ID_MODEL_FROM_DATABASE=Elements Desktop (WDBWLG) + +usb:v1058p10A2* + ID_MODEL_FROM_DATABASE=Elements SE Portable (WDBPCK) + +usb:v1058p10A8* + ID_MODEL_FROM_DATABASE=Elements Portable (WDBUZG) + +usb:v1058p10B8* + ID_MODEL_FROM_DATABASE=Elements Portable (WDBU6Y, WDBUZG) + +usb:v1058p1100* + ID_MODEL_FROM_DATABASE=My Book Essential Edition 2.0 (WDH1U) + +usb:v1058p1102* + ID_MODEL_FROM_DATABASE=My Book Home Edition (WDH1CS) + +usb:v1058p1103* + ID_MODEL_FROM_DATABASE=My Book Studio + +usb:v1058p1104* + ID_MODEL_FROM_DATABASE=My Book Mirror Edition (WDH2U) + +usb:v1058p1105* + ID_MODEL_FROM_DATABASE=My Book Studio II + +usb:v1058p1110* + ID_MODEL_FROM_DATABASE=My Book Essential (WDBAAF), My Book for Mac (WDBAAG) + +usb:v1058p1111* + ID_MODEL_FROM_DATABASE=My Book Elite (WDBAAH) + +usb:v1058p1112* + ID_MODEL_FROM_DATABASE=My Book Studio (WDBAAJ), My Book Studio LX (WDBACH) + +usb:v1058p1123* + ID_MODEL_FROM_DATABASE=My Book 3.0 (WDBABP) + +usb:v1058p1130* + ID_MODEL_FROM_DATABASE=My Book Essential (WDBACW) + +usb:v1058p1140* + ID_MODEL_FROM_DATABASE=My Book Essential (WDBACW) + +usb:v1058p1230* + ID_MODEL_FROM_DATABASE=My Book (WDBFJK0030HBK) + +usb:v1059* + ID_VENDOR_FROM_DATABASE=Giesecke & Devrient GmbH + +usb:v1059p000B* + ID_MODEL_FROM_DATABASE=StarSign Bio Token 3.0 + +usb:v105B* + ID_VENDOR_FROM_DATABASE=Foxconn International, Inc. + +usb:v105BpE065* + ID_MODEL_FROM_DATABASE=BCM43142A0 Bluetooth module + +usb:v105C* + ID_VENDOR_FROM_DATABASE=Hong Ji Electric Wire & Cable (Dongguan) Co., Ltd + +usb:v105D* + ID_VENDOR_FROM_DATABASE=Delkin Devices, Inc. + +usb:v105E* + ID_VENDOR_FROM_DATABASE=Valence Semiconductor Design, Ltd + +usb:v105F* + ID_VENDOR_FROM_DATABASE=Chin Shong Enterprise Co., Ltd + +usb:v1060* + ID_VENDOR_FROM_DATABASE=Easthome Industrial Co., Ltd + +usb:v1063* + ID_VENDOR_FROM_DATABASE=Motorola Electronics Taiwan, Ltd [hex] + +usb:v1063p1555* + ID_MODEL_FROM_DATABASE=MC141555 Hub + +usb:v1063p4100* + ID_MODEL_FROM_DATABASE=SB4100 USB Cable Modem + +usb:v1065* + ID_VENDOR_FROM_DATABASE=CCYU Technology + +usb:v1065p0020* + ID_MODEL_FROM_DATABASE=USB-DVR2 Dev Board + +usb:v1065p2136* + ID_MODEL_FROM_DATABASE=EasyDisk ED1064 + +usb:v106A* + ID_VENDOR_FROM_DATABASE=Loyal Legend, Ltd + +usb:v106C* + ID_VENDOR_FROM_DATABASE=Curitel Communications, Inc. + +usb:v106Cp1101* + ID_MODEL_FROM_DATABASE=CDMA 2000 1xRTT USB modem (HX-550C) + +usb:v106Cp1102* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1103* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1104* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1105* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp1106* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1301* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp1302* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1303* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1304* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1401* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp1402* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1403* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1501* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1502* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1503* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1601* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp1602* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp1603* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2101* + ID_MODEL_FROM_DATABASE=AudioVox 8900 Cell Phone + +usb:v106Cp2102* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2103* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2301* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2302* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2303* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2401* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2402* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2403* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2501* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2502* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2503* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2601* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp2602* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp2603* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp3701* + ID_MODEL_FROM_DATABASE=Broadband Wireless modem + +usb:v106Cp3702* + ID_MODEL_FROM_DATABASE=Pantech PX-500 + +usb:v106Cp3714* + ID_MODEL_FROM_DATABASE=PANTECH USB MODEM [UM175] + +usb:v106Cp3716* + ID_MODEL_FROM_DATABASE=UMW190 Modem + +usb:v106Cp3721* + ID_MODEL_FROM_DATABASE=Option Beemo (GI0801) LTE surfstick + +usb:v106Cp3B14* + ID_MODEL_FROM_DATABASE=Option Beemo (GI0801) LTE surfstick + +usb:v106Cp3EB4* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp4101* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp4102* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp4301* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp4302* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp4401* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp4402* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp4501* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp4502* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp4601* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp4602* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5101* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp5102* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5301* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5302* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp5401* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp5402* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5501* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5502* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp5601* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106Cp5602* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106Cp7101* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v106Cp7102* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106CpA000* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106CpA001* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106CpC100* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106CpC200* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106CpC500* + ID_MODEL_FROM_DATABASE=Packet Service Diagnostic Serial Port (WDM) + +usb:v106CpE200* + ID_MODEL_FROM_DATABASE=Packet Service + +usb:v106D* + ID_VENDOR_FROM_DATABASE=San Chieh Manufacturing, Ltd + +usb:v106E* + ID_VENDOR_FROM_DATABASE=ConectL + +usb:v106F* + ID_VENDOR_FROM_DATABASE=Money Controls + +usb:v106Fp0009* + ID_MODEL_FROM_DATABASE=CT10x Coin Transaction + +usb:v106Fp000A* + ID_MODEL_FROM_DATABASE=CR10x Coin Recycler + +usb:v106Fp000C* + ID_MODEL_FROM_DATABASE=Xchange + +usb:v1076* + ID_VENDOR_FROM_DATABASE=GCT Semiconductor, Inc. + +usb:v1076p0031* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1076p0032* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1076p8002* + ID_MODEL_FROM_DATABASE=LU150 LTE Modem [Yota LU150] + +usb:v107B* + ID_VENDOR_FROM_DATABASE=Gateway, Inc. + +usb:v107Bp3009* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v107Bp55B2* + ID_MODEL_FROM_DATABASE=WBU-110 802.11b Wireless Adapter [Intersil PRISM 3] + +usb:v107Bp55F2* + ID_MODEL_FROM_DATABASE=WGU-210 802.11g Adapter [Intersil ISL3886] + +usb:v107D* + ID_VENDOR_FROM_DATABASE=Arlec Australia, Ltd + +usb:v107E* + ID_VENDOR_FROM_DATABASE=Midoriya Electric Co., Ltd + +usb:v107F* + ID_VENDOR_FROM_DATABASE=KidzMouse, Inc. + +usb:v1082* + ID_VENDOR_FROM_DATABASE=Shin-Etsukaken Co., Ltd + +usb:v1083* + ID_VENDOR_FROM_DATABASE=Canon Electronics, Inc. + +usb:v1083p161B* + ID_MODEL_FROM_DATABASE=DR-2010C Scanner + +usb:v1083p162C* + ID_MODEL_FROM_DATABASE=P-150 Scanner + +usb:v1084* + ID_VENDOR_FROM_DATABASE=Pantech Co., Ltd + +usb:v108A* + ID_VENDOR_FROM_DATABASE=Chloride Power Protection + +usb:v108B* + ID_VENDOR_FROM_DATABASE=Grand-tek Technology Co., Ltd + +usb:v108Bp0005* + ID_MODEL_FROM_DATABASE=HID Keyboard/Mouse PS/2 Translator + +usb:v108C* + ID_VENDOR_FROM_DATABASE=Robert Bosch GmbH + +usb:v108E* + ID_VENDOR_FROM_DATABASE=Lotes Co., Ltd. + +usb:v1099* + ID_VENDOR_FROM_DATABASE=Surface Optics Corp. + +usb:v109A* + ID_VENDOR_FROM_DATABASE=DATASOFT Systems GmbH + +usb:v109B* + ID_VENDOR_FROM_DATABASE=Hisense + +usb:v109Bp9118* + ID_MODEL_FROM_DATABASE=Medion P4013 Mobile + +usb:v109F* + ID_VENDOR_FROM_DATABASE=eSOL Co., Ltd + +usb:v109Fp3163* + ID_MODEL_FROM_DATABASE=Trigem Mobile SmartDisplay84 + +usb:v109Fp3164* + ID_MODEL_FROM_DATABASE=Trigem Mobile SmartDisplay121 + +usb:v10A0* + ID_VENDOR_FROM_DATABASE=Hirotech, Inc. + +usb:v10A3* + ID_VENDOR_FROM_DATABASE=Mitsubishi Materials Corp. + +usb:v10A9* + ID_VENDOR_FROM_DATABASE=SK Teletech Co., Ltd + +usb:v10A9p1102* + ID_MODEL_FROM_DATABASE=Sky Love Actually IM-U460K + +usb:v10A9p1104* + ID_MODEL_FROM_DATABASE=Sky Vega IM-A650S + +usb:v10A9p1105* + ID_MODEL_FROM_DATABASE=VEGA Android composite + +usb:v10A9p1106* + ID_MODEL_FROM_DATABASE=VEGA Android composite + +usb:v10A9p1107* + ID_MODEL_FROM_DATABASE=VEGA Android composite + +usb:v10A9p1108* + ID_MODEL_FROM_DATABASE=VEGA Android composite + +usb:v10A9p1109* + ID_MODEL_FROM_DATABASE=VEGA Android composite + +usb:v10A9p6021* + ID_MODEL_FROM_DATABASE=SIRIUS alpha + +usb:v10A9p6031* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6032* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6033* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6034* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6035* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6036* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6037* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6050* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6051* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6052* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6053* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6054* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6055* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6056* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6057* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6058* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6059* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p6080* + ID_MODEL_FROM_DATABASE=MHS291LVW LTE Modem [Verizon Jetpack 4G LTE Mobile Hotspot MHS291L] (Zero CD Mode) + +usb:v10A9p6085* + ID_MODEL_FROM_DATABASE=MHS291LVW LTE Modem [Verizon Jetpack 4G LTE Mobile Hotspot MHS291L] (Modem Mode) + +usb:v10A9p7031* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7032* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7033* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7034* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7035* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7036* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10A9p7037* + ID_MODEL_FROM_DATABASE=Pantech Android composite + +usb:v10AA* + ID_VENDOR_FROM_DATABASE=Cables To Go + +usb:v10AB* + ID_VENDOR_FROM_DATABASE=USI Co., Ltd + +usb:v10ABp1002* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v10ABp1003* + ID_MODEL_FROM_DATABASE=BC02-EXT in DFU + +usb:v10ABp1005* + ID_MODEL_FROM_DATABASE=Bluetooth Adptr + +usb:v10ABp1006* + ID_MODEL_FROM_DATABASE=BC04-EXT in DFU + +usb:v10ABp10C5* + ID_MODEL_FROM_DATABASE=Sony-Ericsson / Samsung DataCable + +usb:v10AC* + ID_VENDOR_FROM_DATABASE=Honeywell, Inc. + +usb:v10AE* + ID_VENDOR_FROM_DATABASE=Princeton Technology Corp. + +usb:v10AF* + ID_VENDOR_FROM_DATABASE=Liebert Corp. + +usb:v10AFp0000* + ID_MODEL_FROM_DATABASE=UPS + +usb:v10AFp0001* + ID_MODEL_FROM_DATABASE=PowerSure PSA UPS + +usb:v10AFp0002* + ID_MODEL_FROM_DATABASE=PowerSure PST UPS + +usb:v10AFp0003* + ID_MODEL_FROM_DATABASE=PowerSure PSP UPS + +usb:v10AFp0004* + ID_MODEL_FROM_DATABASE=PowerSure PSI UPS + +usb:v10AFp0005* + ID_MODEL_FROM_DATABASE=UPStation GXT 2U UPS + +usb:v10AFp0006* + ID_MODEL_FROM_DATABASE=UPStation GXT UPS + +usb:v10AFp0007* + ID_MODEL_FROM_DATABASE=Nfinity Power Systems UPS + +usb:v10AFp0008* + ID_MODEL_FROM_DATABASE=PowerSure Interactive UPS + +usb:v10B5* + ID_VENDOR_FROM_DATABASE=Comodo (PLX?) + +usb:v10B5p9060* + ID_MODEL_FROM_DATABASE=Test Board + +usb:v10B8* + ID_VENDOR_FROM_DATABASE=DiBcom + +usb:v10B8p0BB8* + ID_MODEL_FROM_DATABASE=DiBcom USB DVB-T reference design (MOD300) (cold) + +usb:v10B8p0BB9* + ID_MODEL_FROM_DATABASE=DiBcom USB DVB-T reference design (MOD300) (warm) + +usb:v10B8p0BC6* + ID_MODEL_FROM_DATABASE=DiBcom USB2.0 DVB-T reference design (MOD3000P) (cold) + +usb:v10B8p0BC7* + ID_MODEL_FROM_DATABASE=DiBcom USB2.0 DVB-T reference design (MOD3000P) (warm) + +usb:v10BB* + ID_VENDOR_FROM_DATABASE=TM Technology, Inc. + +usb:v10BC* + ID_VENDOR_FROM_DATABASE=Dinging Technology Co., Ltd + +usb:v10BD* + ID_VENDOR_FROM_DATABASE=TMT Technology, Inc. + +usb:v10BDp1427* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v10BF* + ID_VENDOR_FROM_DATABASE=SmartHome + +usb:v10BFp0001* + ID_MODEL_FROM_DATABASE=SmartHome PowerLinc + +usb:v10C3* + ID_VENDOR_FROM_DATABASE=Universal Laser Systems, Inc. + +usb:v10C3p00A4* + ID_MODEL_FROM_DATABASE=ULS PLS Series Laser Engraver Firmware Loader + +usb:v10C3p00A5* + ID_MODEL_FROM_DATABASE=ULS Print Support + +usb:v10C4* + ID_VENDOR_FROM_DATABASE=Cygnal Integrated Products, Inc. + +usb:v10C4p0002* + ID_MODEL_FROM_DATABASE=F32x USBXpress Device + +usb:v10C4p0003* + ID_MODEL_FROM_DATABASE=CommandIR + +usb:v10C4p8030* + ID_MODEL_FROM_DATABASE=K4JRG Ham Radio devices + +usb:v10C4p8044* + ID_MODEL_FROM_DATABASE=USB Debug Adapter + +usb:v10C4p804E* + ID_MODEL_FROM_DATABASE=Software Bisque Paramount ME + +usb:v10C4p80A9* + ID_MODEL_FROM_DATABASE=CP210x to UART Bridge Controller + +usb:v10C4p80CA* + ID_MODEL_FROM_DATABASE=ATM2400 Sensor Device + +usb:v10C4p813F* + ID_MODEL_FROM_DATABASE=tams EasyControl + +usb:v10C4p8149* + ID_MODEL_FROM_DATABASE=West Mountain Radio Computerized Battery Analyzer + +usb:v10C4p814A* + ID_MODEL_FROM_DATABASE=West Mountain Radio RIGblaster P&P + +usb:v10C4p814B* + ID_MODEL_FROM_DATABASE=West Mountain Radio RIGtalk + +usb:v10C4p818A* + ID_MODEL_FROM_DATABASE=Silicon Labs FM Radio Reference Design + +usb:v10C4p81E8* + ID_MODEL_FROM_DATABASE=Zephyr BioHarness + +usb:v10C4p8460* + ID_MODEL_FROM_DATABASE=Sangoma Wanpipe VoiceTime + +usb:v10C4p8461* + ID_MODEL_FROM_DATABASE=Sangoma U100 + +usb:v10C4p8477* + ID_MODEL_FROM_DATABASE=Balluff RFID Reader + +usb:v10C4p8496* + ID_MODEL_FROM_DATABASE=SiLabs Cypress FW downloader + +usb:v10C4p8497* + ID_MODEL_FROM_DATABASE=SiLabs Cypress EVB + +usb:v10C4p8605* + ID_MODEL_FROM_DATABASE=dilitronics ESoLUX solar lighting controller + +usb:v10C4p86BC* + ID_MODEL_FROM_DATABASE=C8051F34x AudioDelay [AD-340] + +usb:v10C4p8789* + ID_MODEL_FROM_DATABASE=C8051F34x Extender & EDID MGR [EMX-DVI] + +usb:v10C4p87BE* + ID_MODEL_FROM_DATABASE=C8051F34x HDMI Audio Extractor [EMX-HD-AUD] + +usb:v10C4p8863* + ID_MODEL_FROM_DATABASE=C8051F34x Bootloader + +usb:v10C4p8897* + ID_MODEL_FROM_DATABASE=C8051F38x HDMI Splitter [UHBX] + +usb:v10C4p8918* + ID_MODEL_FROM_DATABASE=C8051F38x HDMI Audio Extractor [VSA-HA-DP] + +usb:v10C4p8973* + ID_MODEL_FROM_DATABASE=C8051F38x HDMI Extender [UHBX-8X] + +usb:v10C4p89E1* + ID_MODEL_FROM_DATABASE=C8051F38x HDMI Extender [UHBX-SW3-WP] + +usb:v10C4pEA60* + ID_MODEL_FROM_DATABASE=CP210x UART Bridge / myAVR mySmartUSB light + +usb:v10C4pEA61* + ID_MODEL_FROM_DATABASE=CP210x UART Bridge + +usb:v10C4pEA70* + ID_MODEL_FROM_DATABASE=CP210x UART Bridge + +usb:v10C4pEA80* + ID_MODEL_FROM_DATABASE=CP210x UART Bridge + +usb:v10C5* + ID_VENDOR_FROM_DATABASE=Sanei Electric, Inc. + +usb:v10C5p819A* + ID_MODEL_FROM_DATABASE=FM Radio + +usb:v10C6* + ID_VENDOR_FROM_DATABASE=Intec, Inc. + +usb:v10CB* + ID_VENDOR_FROM_DATABASE=Eratech + +usb:v10CC* + ID_VENDOR_FROM_DATABASE=GBM Connector Co., Ltd + +usb:v10CCp1101* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v10CD* + ID_VENDOR_FROM_DATABASE=Kycon, Inc. + +usb:v10CE* + ID_VENDOR_FROM_DATABASE=Silicon Labs + +usb:v10CEp000E* + ID_MODEL_FROM_DATABASE=Shinko/Sinfonia CHC-S2145 + +usb:v10CEpEA6A* + ID_MODEL_FROM_DATABASE=MobiData EDGE USB Modem + +usb:v10CF* + ID_VENDOR_FROM_DATABASE=Velleman Components, Inc. + +usb:v10CFp2011* + ID_MODEL_FROM_DATABASE=R-Engine MPEG2 encoder/decoder + +usb:v10CFp5500* + ID_MODEL_FROM_DATABASE=8055 Experiment Interface Board (address=0) + +usb:v10CFp5501* + ID_MODEL_FROM_DATABASE=8055 Experiment Interface Board (address=1) + +usb:v10CFp5502* + ID_MODEL_FROM_DATABASE=8055 Experiment Interface Board (address=2) + +usb:v10CFp5503* + ID_MODEL_FROM_DATABASE=8055 Experiment Interface Board (address=3) + +usb:v10D1* + ID_VENDOR_FROM_DATABASE=Hottinger Baldwin Measurement + +usb:v10D1p0101* + ID_MODEL_FROM_DATABASE=USB-Module for Spider8, CP32 + +usb:v10D1p0202* + ID_MODEL_FROM_DATABASE=CP22 - Communication Processor + +usb:v10D1p0301* + ID_MODEL_FROM_DATABASE=CP42 - Communication Processor + +usb:v10D2* + ID_VENDOR_FROM_DATABASE=RayComposer - R. Adams + +usb:v10D2p5243* + ID_MODEL_FROM_DATABASE=RayComposer + +usb:v10D4* + ID_VENDOR_FROM_DATABASE=Man Boon Manufactory, Ltd + +usb:v10D5* + ID_VENDOR_FROM_DATABASE=Uni Class Technology Co., Ltd + +usb:v10D5p0004* + ID_MODEL_FROM_DATABASE=PS/2 Converter + +usb:v10D5p5552* + ID_MODEL_FROM_DATABASE=KVM Human Interface Composite Device (Keyboard/Mouse ports) + +usb:v10D5p55A2* + ID_MODEL_FROM_DATABASE=2Port KVMSwitcher + +usb:v10D6* + ID_VENDOR_FROM_DATABASE=Actions Semiconductor Co., Ltd + +usb:v10D6p0C02* + ID_MODEL_FROM_DATABASE=BioniQ 1001 Tablet + +usb:v10D6p1000* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v10D6p1100* + ID_MODEL_FROM_DATABASE=MPMan MP-Ki 128 MP3 Player/Recorder + +usb:v10D6p1101* + ID_MODEL_FROM_DATABASE=D-Wave 2GB MP4 Player / AK1025 MP3/MP4 Player + +usb:v10D6p2200* + ID_MODEL_FROM_DATABASE=Acer MP-120 MP3 player + +usb:v10D6p8888* + ID_MODEL_FROM_DATABASE=ADFU Device + +usb:v10D6pFF51* + ID_MODEL_FROM_DATABASE=ADFU Device + +usb:v10D6pFF61* + ID_MODEL_FROM_DATABASE=MP4 Player + +usb:v10D6pFF66* + ID_MODEL_FROM_DATABASE=Craig 2GB MP3/Video Player + +usb:v10DE* + ID_VENDOR_FROM_DATABASE=Authenex, Inc. + +usb:v10DF* + ID_VENDOR_FROM_DATABASE=In-Win Development, Inc. + +usb:v10DFp0500* + ID_MODEL_FROM_DATABASE=iAPP CR-e500 Card reader + +usb:v10E0* + ID_VENDOR_FROM_DATABASE=Post-Op Video, Inc. + +usb:v10E1* + ID_VENDOR_FROM_DATABASE=CablePlus, Ltd + +usb:v10E2* + ID_VENDOR_FROM_DATABASE=Nada Electronics, Ltd + +usb:v10EC* + ID_VENDOR_FROM_DATABASE=Vast Technologies, Inc. + +usb:v10F0* + ID_VENDOR_FROM_DATABASE=Nexio Co., Ltd + +usb:v10F0p2002* + ID_MODEL_FROM_DATABASE=iNexio Touchscreen controller + +usb:v10F1* + ID_VENDOR_FROM_DATABASE=Importek + +usb:v10F1p1A08* + ID_MODEL_FROM_DATABASE=Internal Webcam + +usb:v10F1p1A1E* + ID_MODEL_FROM_DATABASE=Laptop Integrated Webcam 1.3M + +usb:v10F1p1A2A* + ID_MODEL_FROM_DATABASE=Laptop Integrated Webcam + +usb:v10F5* + ID_VENDOR_FROM_DATABASE=Turtle Beach + +usb:v10F5p0200* + ID_MODEL_FROM_DATABASE=Audio Advantage Roadie + +usb:v10FB* + ID_VENDOR_FROM_DATABASE=Pictos Technologies, Inc. + +usb:v10FD* + ID_VENDOR_FROM_DATABASE=Anubis Electronics, Ltd + +usb:v10FDp7E50* + ID_MODEL_FROM_DATABASE=FlyCam Usb 100 + +usb:v10FDp804D* + ID_MODEL_FROM_DATABASE=Typhoon Webshot II Webcam [zc0301] + +usb:v10FDp8050* + ID_MODEL_FROM_DATABASE=FlyCAM-USB 300 XP2 + +usb:v10FDpDE00* + ID_MODEL_FROM_DATABASE=WinFast WalkieTV WDM Capture Driver. + +usb:v10FE* + ID_VENDOR_FROM_DATABASE=Thrane & Thrane + +usb:v10FEp000C* + ID_MODEL_FROM_DATABASE=TT-3750 BGAN-XL Radio Module + +usb:v1100* + ID_VENDOR_FROM_DATABASE=VirTouch, Ltd + +usb:v1100p0001* + ID_MODEL_FROM_DATABASE=VTPlayer VTP-1 Braille Mouse + +usb:v1101* + ID_VENDOR_FROM_DATABASE=EasyPass Industrial Co., Ltd + +usb:v1101p0001* + ID_MODEL_FROM_DATABASE=FSK Electronics Super GSM Reader + +usb:v1108* + ID_VENDOR_FROM_DATABASE=Brightcom Technologies, Ltd + +usb:v110A* + ID_VENDOR_FROM_DATABASE=Moxa Technologies Co., Ltd. + +usb:v110Ap1250* + ID_MODEL_FROM_DATABASE=UPort 1250 2-Port RS-232/422/485 + +usb:v110Ap1251* + ID_MODEL_FROM_DATABASE=UPort 1250I 2-Port RS-232/422/485 with Isolation + +usb:v110Ap1410* + ID_MODEL_FROM_DATABASE=UPort 1410 4-Port RS-232 + +usb:v110Ap1450* + ID_MODEL_FROM_DATABASE=UPort 1450 4-Port RS-232/422/485 + +usb:v110Ap1451* + ID_MODEL_FROM_DATABASE=UPort 1450I 4-Port RS-232/422/485 with Isolation + +usb:v110Ap1613* + ID_MODEL_FROM_DATABASE=UPort 1610-16 16-Port RS-232 + +usb:v110Ap1618* + ID_MODEL_FROM_DATABASE=UPort 1610-8 8-Port RS-232 + +usb:v110Ap1653* + ID_MODEL_FROM_DATABASE=UPort 1650-16 16-Port RS-232/422/485 + +usb:v110Ap1658* + ID_MODEL_FROM_DATABASE=UPort 1650-8 8-Port RS-232/422/485 + +usb:v1110* + ID_VENDOR_FROM_DATABASE=Analog Devices Canada, Ltd (Allied Telesyn) + +usb:v1110p5C01* + ID_MODEL_FROM_DATABASE=Huawei MT-882 Remote NDIS Network Device + +usb:v1110p6489* + ID_MODEL_FROM_DATABASE=ADSL ETH/USB RTR + +usb:v1110p9000* + ID_MODEL_FROM_DATABASE=ADSL LAN Adapter + +usb:v1110p9001* + ID_MODEL_FROM_DATABASE=ADSL Loader + +usb:v1110p900F* + ID_MODEL_FROM_DATABASE=AT-AR215 DSL Modem + +usb:v1110p9010* + ID_MODEL_FROM_DATABASE=AT-AR215 DSL Modem + +usb:v1110p9021* + ID_MODEL_FROM_DATABASE=ADSL WAN Adapter + +usb:v1110p9022* + ID_MODEL_FROM_DATABASE=ADSL Loader + +usb:v1110p9023* + ID_MODEL_FROM_DATABASE=ADSL WAN Adapter + +usb:v1110p9024* + ID_MODEL_FROM_DATABASE=ADSL Loader + +usb:v1110p9031* + ID_MODEL_FROM_DATABASE=ADSL LAN Adapter + +usb:v1110p9032* + ID_MODEL_FROM_DATABASE=ADSL Loader + +usb:v1111* + ID_VENDOR_FROM_DATABASE=Pandora International Ltd. + +usb:v1111p8888* + ID_MODEL_FROM_DATABASE=Evolution Device + +usb:v1112* + ID_VENDOR_FROM_DATABASE=YM ELECTRIC CO., Ltd + +usb:v1113* + ID_VENDOR_FROM_DATABASE=Medion AG + +usb:v1113pA0A2* + ID_MODEL_FROM_DATABASE=Active Sync device + +usb:v111E* + ID_VENDOR_FROM_DATABASE=VSO Electric Co., Ltd + +usb:v112A* + ID_VENDOR_FROM_DATABASE=RedRat + +usb:v112Ap0001* + ID_MODEL_FROM_DATABASE=RedRat3 IR Transceiver + +usb:v112Ap0005* + ID_MODEL_FROM_DATABASE=RedRat3II IR Transceiver + +usb:v112E* + ID_VENDOR_FROM_DATABASE=Master Hill Electric Wire and Cable Co., Ltd + +usb:v112F* + ID_VENDOR_FROM_DATABASE=Cellon International, Inc. + +usb:v1130* + ID_VENDOR_FROM_DATABASE=Tenx Technology, Inc. + +usb:v1130p0001* + ID_MODEL_FROM_DATABASE=BlyncLight + +usb:v1130p0002* + ID_MODEL_FROM_DATABASE=iBuddy + +usb:v1130p0202* + ID_MODEL_FROM_DATABASE=Rocket Launcher + +usb:v1130p6604* + ID_MODEL_FROM_DATABASE=MCE IR-Receiver + +usb:v1130p660C* + ID_MODEL_FROM_DATABASE=Foot Pedal/Thermometer + +usb:v1130p6806* + ID_MODEL_FROM_DATABASE=Keychain photo frame + +usb:v1130pC301* + ID_MODEL_FROM_DATABASE=Digital Photo viewer [Wallet Pix] + +usb:v1130pF211* + ID_MODEL_FROM_DATABASE=TP6911 Audio Headset + +usb:v1131* + ID_VENDOR_FROM_DATABASE=Integrated System Solution Corp. + +usb:v1131p1001* + ID_MODEL_FROM_DATABASE=KY-BT100 Bluetooth Adapter + +usb:v1131p1002* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1131p1003* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1131p1004* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v1132* + ID_VENDOR_FROM_DATABASE=Toshiba Corp., Digital Media Equipment [hex] + +usb:v1132p4331* + ID_MODEL_FROM_DATABASE=PDR-M4/M5/M70 Digital Camera + +usb:v1132p4332* + ID_MODEL_FROM_DATABASE=PDR-M60 Digital Camera + +usb:v1132p4333* + ID_MODEL_FROM_DATABASE=PDR-M2300/PDR-M700 + +usb:v1132p4334* + ID_MODEL_FROM_DATABASE=PDR-M65 + +usb:v1132p4335* + ID_MODEL_FROM_DATABASE=PDR-M61 + +usb:v1132p4337* + ID_MODEL_FROM_DATABASE=PDR-M11 + +usb:v1132p4338* + ID_MODEL_FROM_DATABASE=PDR-M25 + +usb:v1136* + ID_VENDOR_FROM_DATABASE=CTS Electronincs + +usb:v1136p3131* + ID_MODEL_FROM_DATABASE=CTS LS515 + +usb:v113C* + ID_VENDOR_FROM_DATABASE=Arin Tech Co., Ltd + +usb:v113D* + ID_VENDOR_FROM_DATABASE=Mapower Electronics Co., Ltd + +usb:v1141* + ID_VENDOR_FROM_DATABASE=V One Multimedia, Pte., Ltd + +usb:v1142* + ID_VENDOR_FROM_DATABASE=CyberScan Technologies, Inc. + +usb:v1142p0709* + ID_MODEL_FROM_DATABASE=Cyberview High Speed Scanner + +usb:v1145* + ID_VENDOR_FROM_DATABASE=Japan Radio Company + +usb:v1145p0001* + ID_MODEL_FROM_DATABASE=AirH PHONE AH-J3001V/J3002V + +usb:v1146* + ID_VENDOR_FROM_DATABASE=Shimane SANYO Electric Co., Ltd. + +usb:v1147* + ID_VENDOR_FROM_DATABASE=Ever Great Electric Wire and Cable Co., Ltd + +usb:v114B* + ID_VENDOR_FROM_DATABASE=Sphairon Access Systems GmbH + +usb:v114Bp0110* + ID_MODEL_FROM_DATABASE=Turbolink UB801R WLAN Adapter + +usb:v114Bp0150* + ID_MODEL_FROM_DATABASE=Turbolink UB801RE Wireless 802.11g 54Mbps Network Adapter [RTL8187] + +usb:v114C* + ID_VENDOR_FROM_DATABASE=Tinius Olsen Testing Machine Co., Inc. + +usb:v114D* + ID_VENDOR_FROM_DATABASE=Alpha Imaging Technology Corp. + +usb:v114F* + ID_VENDOR_FROM_DATABASE=Wavecom + +usb:v114Fp1234* + ID_MODEL_FROM_DATABASE=Fastrack Xtend FXT001 Modem + +usb:v115B* + ID_VENDOR_FROM_DATABASE=Salix Technology Co., Ltd. + +usb:v1162* + ID_VENDOR_FROM_DATABASE=Secugen Corp. + +usb:v1163* + ID_VENDOR_FROM_DATABASE=DeLorme Publishing, Inc. + +usb:v1163p0100* + ID_MODEL_FROM_DATABASE=Earthmate GPS (orig) + +usb:v1163p0200* + ID_MODEL_FROM_DATABASE=Earthmate GPS (LT-20, LT-40) + +usb:v1163p2020* + ID_MODEL_FROM_DATABASE=Earthmate GPS (PN-40) + +usb:v1164* + ID_VENDOR_FROM_DATABASE=YUAN High-Tech Development Co., Ltd + +usb:v1164p0300* + ID_MODEL_FROM_DATABASE=ELSAVISION 460D + +usb:v1164p0601* + ID_MODEL_FROM_DATABASE=Analog TV Tuner + +usb:v1164p0900* + ID_MODEL_FROM_DATABASE=TigerBird BMP837 USB2.0 WDM Encoder + +usb:v1164p0BC7* + ID_MODEL_FROM_DATABASE=Digital TV Tuner + +usb:v1164p521B* + ID_MODEL_FROM_DATABASE=MC521A mini Card ATSC Tuner + +usb:v1164p6601* + ID_MODEL_FROM_DATABASE=Digital TV Tuner Card [RTL2832U] + +usb:v1165* + ID_VENDOR_FROM_DATABASE=Telson Electronics Co., Ltd + +usb:v1166* + ID_VENDOR_FROM_DATABASE=Bantam Interactive Technologies + +usb:v1167* + ID_VENDOR_FROM_DATABASE=Salient Systems Corp. + +usb:v1168* + ID_VENDOR_FROM_DATABASE=BizConn International Corp. + +usb:v116E* + ID_VENDOR_FROM_DATABASE=Gigastorage Corp. + +usb:v116F* + ID_VENDOR_FROM_DATABASE=Silicon 10 Technology Corp. + +usb:v116Fp0005* + ID_MODEL_FROM_DATABASE=Flash Card Reader + +usb:v116FpC108* + ID_MODEL_FROM_DATABASE=Flash Card Reader + +usb:v116FpC109* + ID_MODEL_FROM_DATABASE=Flash Card Reader + +usb:v1175* + ID_VENDOR_FROM_DATABASE=Shengyih Steel Mold Co., Ltd + +usb:v117D* + ID_VENDOR_FROM_DATABASE=Santa Electronic, Inc. + +usb:v117E* + ID_VENDOR_FROM_DATABASE=JNC, Inc. + +usb:v1182* + ID_VENDOR_FROM_DATABASE=Venture Corp., Ltd + +usb:v1183* + ID_VENDOR_FROM_DATABASE=Compaq Computer Corp. [hex] (Digital Dream ??) + +usb:v1183p0001* + ID_MODEL_FROM_DATABASE=DigitalDream l'espion XS + +usb:v1183p19C7* + ID_MODEL_FROM_DATABASE=ISDN TA + +usb:v1183p4008* + ID_MODEL_FROM_DATABASE=56k FaxModem + +usb:v1183p504A* + ID_MODEL_FROM_DATABASE=PJB-100 Personal Jukebox + +usb:v1184* + ID_VENDOR_FROM_DATABASE=Kyocera Elco Corp. + +usb:v1188* + ID_VENDOR_FROM_DATABASE=Bloomberg L.P. + +usb:v1189* + ID_VENDOR_FROM_DATABASE=Acer Communications & Multimedia + +usb:v1189p0893* + ID_MODEL_FROM_DATABASE=EP-1427X-2 Ethernet Adapter [Acer] + +usb:v118F* + ID_VENDOR_FROM_DATABASE=You Yang Technology Co., Ltd + +usb:v1190* + ID_VENDOR_FROM_DATABASE=Tripace + +usb:v1191* + ID_VENDOR_FROM_DATABASE=Loyalty Founder Enterprise Co., Ltd + +usb:v1196* + ID_VENDOR_FROM_DATABASE=Yankee Robotics, LLC + +usb:v1196p0010* + ID_MODEL_FROM_DATABASE=Trifid Camera without code + +usb:v1196p0011* + ID_MODEL_FROM_DATABASE=Trifid Camera + +usb:v1197* + ID_VENDOR_FROM_DATABASE=Technoimagia Co., Ltd + +usb:v1198* + ID_VENDOR_FROM_DATABASE=StarShine Technology Corp. + +usb:v1199* + ID_VENDOR_FROM_DATABASE=Sierra Wireless, Inc. + +usb:v1199p0019* + ID_MODEL_FROM_DATABASE=AC595U + +usb:v1199p0021* + ID_MODEL_FROM_DATABASE=AC597E + +usb:v1199p0024* + ID_MODEL_FROM_DATABASE=MC5727 CDMA modem + +usb:v1199p0110* + ID_MODEL_FROM_DATABASE=Composite Device + +usb:v1199p0112* + ID_MODEL_FROM_DATABASE=CDMA 1xEVDO PC Card, AirCard 580 + +usb:v1199p0120* + ID_MODEL_FROM_DATABASE=AC595U + +usb:v1199p0218* + ID_MODEL_FROM_DATABASE=MC5720 Wireless Modem + +usb:v1199p6467* + ID_MODEL_FROM_DATABASE=MP Series Network Adapter + +usb:v1199p6468* + ID_MODEL_FROM_DATABASE=MP Series Network Adapter + +usb:v1199p6469* + ID_MODEL_FROM_DATABASE=MP Series Network Adapter + +usb:v1199p6802* + ID_MODEL_FROM_DATABASE=MC8755 Device + +usb:v1199p6803* + ID_MODEL_FROM_DATABASE=MC8765 Device + +usb:v1199p6804* + ID_MODEL_FROM_DATABASE=MC8755 Device + +usb:v1199p6805* + ID_MODEL_FROM_DATABASE=MC8765 Device + +usb:v1199p6812* + ID_MODEL_FROM_DATABASE=MC8775 Device + +usb:v1199p6820* + ID_MODEL_FROM_DATABASE=AC875 Device + +usb:v1199p6832* + ID_MODEL_FROM_DATABASE=MC8780 Device + +usb:v1199p6833* + ID_MODEL_FROM_DATABASE=MC8781 Device + +usb:v1199p683A* + ID_MODEL_FROM_DATABASE=MC8785 Device + +usb:v1199p683C* + ID_MODEL_FROM_DATABASE=Mobile Broadband 3G/UMTS (MC8790 Device) + +usb:v1199p6850* + ID_MODEL_FROM_DATABASE=AirCard 880 Device + +usb:v1199p6851* + ID_MODEL_FROM_DATABASE=AirCard 881 Device + +usb:v1199p6852* + ID_MODEL_FROM_DATABASE=AirCard 880E Device + +usb:v1199p6853* + ID_MODEL_FROM_DATABASE=AirCard 881E Device + +usb:v1199p6854* + ID_MODEL_FROM_DATABASE=AirCard 885 Device + +usb:v1199p6856* + ID_MODEL_FROM_DATABASE=ATT "USB Connect 881" + +usb:v1199p6870* + ID_MODEL_FROM_DATABASE=MC8780 Device + +usb:v1199p6871* + ID_MODEL_FROM_DATABASE=MC8781 Device + +usb:v1199p6893* + ID_MODEL_FROM_DATABASE=MC8777 Device + +usb:v1199p68A3* + ID_MODEL_FROM_DATABASE=MC8700 Modem + +usb:v1199p68AA* + ID_MODEL_FROM_DATABASE=4G LTE adapter + +usb:v1199p9000* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem (QDL mode) + +usb:v1199p9001* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9002* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9003* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9004* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9005* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9006* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9007* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9008* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9009* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p900A* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v1199p9055* + ID_MODEL_FROM_DATABASE=Gobi 9x15 Multimode 3G/4G LTE Modem (NAT mode) + +usb:v1199p9057* + ID_MODEL_FROM_DATABASE=Gobi 9x15 Multimode 3G/4G LTE Modem (IP passthrough mode) + +usb:v119A* + ID_VENDOR_FROM_DATABASE=ZHAN QI Technology Co., Ltd + +usb:v119B* + ID_VENDOR_FROM_DATABASE=ruwido austria GmbH + +usb:v119Bp0400* + ID_MODEL_FROM_DATABASE=Infrared Keyboard V2.01 + +usb:v11A0* + ID_VENDOR_FROM_DATABASE=Chipcon AS + +usb:v11A0pEB11* + ID_MODEL_FROM_DATABASE=CC2400EB 2.0 ZigBee Sniffer + +usb:v11A3* + ID_VENDOR_FROM_DATABASE=Technovas Co., Ltd + +usb:v11A3p8031* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v11A3p8032* + ID_MODEL_FROM_DATABASE=MP3 Player + +usb:v11AA* + ID_VENDOR_FROM_DATABASE=GlobalMedia Group, LLC + +usb:v11AAp1518* + ID_MODEL_FROM_DATABASE=iREZ K2 + +usb:v11AB* + ID_VENDOR_FROM_DATABASE=Exito Electronics Co., Ltd + +usb:v11AC* + ID_VENDOR_FROM_DATABASE=Nike + +usb:v11ACp6565* + ID_MODEL_FROM_DATABASE=FuelBand + +usb:v11B0* + ID_VENDOR_FROM_DATABASE=ATECH FLASH TECHNOLOGY + +usb:v11B0p6208* + ID_MODEL_FROM_DATABASE=PRO-28U + +usb:v11BE* + ID_VENDOR_FROM_DATABASE=R&D International NV + +usb:v11BEpF0A0* + ID_MODEL_FROM_DATABASE=Martin Maxxyz DMX + +usb:v11C5* + ID_VENDOR_FROM_DATABASE=Inmax + +usb:v11C5p0521* + ID_MODEL_FROM_DATABASE=IMT-0521 Smartcard Reader + +usb:v11CA* + ID_VENDOR_FROM_DATABASE=VeriFone Inc + +usb:v11CAp0207* + ID_MODEL_FROM_DATABASE=PIN Pad VX 810 + +usb:v11CAp0220* + ID_MODEL_FROM_DATABASE=PIN Pad VX 805 + +usb:v11DB* + ID_VENDOR_FROM_DATABASE=Topfield Co., Ltd. + +usb:v11DBp1000* + ID_MODEL_FROM_DATABASE=PVR + +usb:v11DBp1100* + ID_MODEL_FROM_DATABASE=PVR + +usb:v11E6* + ID_VENDOR_FROM_DATABASE=K.I. Technology Co. Ltd. + +usb:v11F5* + ID_VENDOR_FROM_DATABASE=Siemens AG + +usb:v11F5p0001* + ID_MODEL_FROM_DATABASE=SX1 + +usb:v11F5p0003* + ID_MODEL_FROM_DATABASE=Mobile phone USB cable + +usb:v11F5p0004* + ID_MODEL_FROM_DATABASE=X75 + +usb:v11F5p0005* + ID_MODEL_FROM_DATABASE=SXG75/EF81 + +usb:v11F5p0008* + ID_MODEL_FROM_DATABASE=UMTS/HSDPA Data Card + +usb:v11F5p0101* + ID_MODEL_FROM_DATABASE=RCU Connect + +usb:v11F6* + ID_VENDOR_FROM_DATABASE=Prolific + +usb:v11F6p2001* + ID_MODEL_FROM_DATABASE=Willcom WSIM + +usb:v11F7* + ID_VENDOR_FROM_DATABASE=Alcatel (?) + +usb:v11F7p02DF* + ID_MODEL_FROM_DATABASE=Serial cable (v2) for TD-10 Mobile Phone + +usb:v1203* + ID_VENDOR_FROM_DATABASE=TSC Auto ID Technology Co., Ltd + +usb:v1203p0140* + ID_MODEL_FROM_DATABASE=TTP-245C + +usb:v1209* + ID_VENDOR_FROM_DATABASE=InterBiometrics + +usb:v1209p1001* + ID_MODEL_FROM_DATABASE=USB Hub + +usb:v1209p1002* + ID_MODEL_FROM_DATABASE=USB Relais + +usb:v1209p1003* + ID_MODEL_FROM_DATABASE=IBSecureCam-P + +usb:v1209p1004* + ID_MODEL_FROM_DATABASE=IBSecureCam-O + +usb:v1209p1005* + ID_MODEL_FROM_DATABASE=IBSecureCam-N + +usb:v1209p1006* + ID_MODEL_FROM_DATABASE=Mini IO-Board + +usb:v1209p2000* + ID_MODEL_FROM_DATABASE=Zygmunt Krynicki Lantern Brightness Sensor + +usb:v1209p2048* + ID_MODEL_FROM_DATABASE=Housedillon.com MRF49XA Transciever + +usb:v1209p2222* + ID_MODEL_FROM_DATABASE=LabConnect Signalgenerator + +usb:v1209p2300* + ID_MODEL_FROM_DATABASE=Keyboardio Keyboardio Model 01 Bootloader + +usb:v1209p2301* + ID_MODEL_FROM_DATABASE=Keyboardio Keyboardio Model 01 + +usb:v1209p2337* + ID_MODEL_FROM_DATABASE=/Dev or SlashDev /Net + +usb:v1209p3000* + ID_MODEL_FROM_DATABASE=lloyd3000 + +usb:v1209p3333* + ID_MODEL_FROM_DATABASE=LabConnect Digitalnetzteil + +usb:v1209p5222* + ID_MODEL_FROM_DATABASE=telavivmakers attami + +usb:v1209p5A22* + ID_MODEL_FROM_DATABASE=ikari_01 sd2snes + +usb:v1209p7BD0* + ID_MODEL_FROM_DATABASE=pokey9000 Tiny Bit Dingus + +usb:v1209pABD0* + ID_MODEL_FROM_DATABASE=tibounise ADB converter + +usb:v1209pBEEF* + ID_MODEL_FROM_DATABASE=Modal MC-USB + +usb:v1209pC0F5* + ID_MODEL_FROM_DATABASE=unethi PERswitch + +usb:v1209pCA1C* + ID_MODEL_FROM_DATABASE=KnightOS Hub + +usb:v1209pCA1D* + ID_MODEL_FROM_DATABASE=KnightOS MTP Device + +usb:v1209pCAFE* + ID_MODEL_FROM_DATABASE=ii iigadget + +usb:v1209pDADA* + ID_MODEL_FROM_DATABASE=Rebel Technology OWL + +usb:v1209pDEAD* + ID_MODEL_FROM_DATABASE=chaosfield.at AVR-Ruler + +usb:v1209pFA11* + ID_MODEL_FROM_DATABASE=moonglow OpenXHC + +usb:v1209pFEED* + ID_MODEL_FROM_DATABASE=ProgramGyar AVR-IR Sender + +usb:v120E* + ID_VENDOR_FROM_DATABASE=Hudson Soft Co., Ltd + +usb:v120F* + ID_VENDOR_FROM_DATABASE=Magellan + +usb:v120Fp524E* + ID_MODEL_FROM_DATABASE=RoadMate 1475T + +usb:v120Fp5260* + ID_MODEL_FROM_DATABASE=Triton Handheld GPS Receiver (300/400/500/1500/2000) + +usb:v1210* + ID_VENDOR_FROM_DATABASE=DigiTech + +usb:v1210p0016* + ID_MODEL_FROM_DATABASE=RP500 Guitar Multi-Effects Processor + +usb:v1210p001B* + ID_MODEL_FROM_DATABASE=RP155 Guitar Multi-Effects Processor + +usb:v1210p001C* + ID_MODEL_FROM_DATABASE=RP255 Guitar Multi-Effects Processor + +usb:v121E* + ID_VENDOR_FROM_DATABASE=Jungsoft Co., Ltd + +usb:v121Ep3403* + ID_MODEL_FROM_DATABASE=Muzio JM250 Audio Player + +usb:v1221* + ID_VENDOR_FROM_DATABASE=Unknown manufacturer + +usb:v1221p3234* + ID_MODEL_FROM_DATABASE=Disk (Thumb drive) + +usb:v1223* + ID_VENDOR_FROM_DATABASE=SKYCABLE ENTERPRISE. CO., LTD. + +usb:v1228* + ID_VENDOR_FROM_DATABASE=Datapaq Limited + +usb:v1228p0012* + ID_MODEL_FROM_DATABASE=Q18 Data Logger + +usb:v1228p0015* + ID_MODEL_FROM_DATABASE=TPaq21/MPaq21 Datalogger + +usb:v1228p584C* + ID_MODEL_FROM_DATABASE=XL2 Logger + +usb:v1230* + ID_VENDOR_FROM_DATABASE=Chipidea-Microelectronica, S.A. + +usb:v1233* + ID_VENDOR_FROM_DATABASE=Denver Electronics + +usb:v1233p5677* + ID_MODEL_FROM_DATABASE=FUSB200 mp3 player + +usb:v1234* + ID_VENDOR_FROM_DATABASE=Brain Actuated Technologies + +usb:v1234p0000* + ID_MODEL_FROM_DATABASE=Neural Impulse Actuator Prototype 1.0 [NIA] + +usb:v1234p4321* + ID_MODEL_FROM_DATABASE=Human Interface Device + +usb:v1234pED02* + ID_MODEL_FROM_DATABASE=Emotiv EPOC Developer Headset Wireless Dongle + +usb:v1235* + ID_VENDOR_FROM_DATABASE=Focusrite-Novation + +usb:v1235p0001* + ID_MODEL_FROM_DATABASE=ReMOTE Audio/XStation First Edition + +usb:v1235p0002* + ID_MODEL_FROM_DATABASE=Speedio + +usb:v1235p0003* + ID_MODEL_FROM_DATABASE=RemoteSL + ZeroSL + +usb:v1235p0004* + ID_MODEL_FROM_DATABASE=ReMOTE LE + +usb:v1235p0005* + ID_MODEL_FROM_DATABASE=XIOSynth [First Edition] + +usb:v1235p0006* + ID_MODEL_FROM_DATABASE=XStation + +usb:v1235p0007* + ID_MODEL_FROM_DATABASE=XIOSynth + +usb:v1235p0008* + ID_MODEL_FROM_DATABASE=ReMOTE SL Compact + +usb:v1235p0009* + ID_MODEL_FROM_DATABASE=nIO + +usb:v1235p000A* + ID_MODEL_FROM_DATABASE=Nocturn + +usb:v1235p000B* + ID_MODEL_FROM_DATABASE=ReMOTE SL MkII + +usb:v1235p000C* + ID_MODEL_FROM_DATABASE=ZeRO MkII + +usb:v1235p000E* + ID_MODEL_FROM_DATABASE=Launchpad + +usb:v1235p0010* + ID_MODEL_FROM_DATABASE=Saffire 6 + +usb:v1235p0011* + ID_MODEL_FROM_DATABASE=Ultranova + +usb:v1235p0012* + ID_MODEL_FROM_DATABASE=Nocturn Keyboard + +usb:v1235p0013* + ID_MODEL_FROM_DATABASE=VRM Box + +usb:v1235p0014* + ID_MODEL_FROM_DATABASE=VRM Box Audio Class (2-out) + +usb:v1235p0015* + ID_MODEL_FROM_DATABASE=Dicer + +usb:v1235p0016* + ID_MODEL_FROM_DATABASE=Ultranova + +usb:v1235p0018* + ID_MODEL_FROM_DATABASE=Twitch + +usb:v1235p0019* + ID_MODEL_FROM_DATABASE=Impulse 25 + +usb:v1235p001A* + ID_MODEL_FROM_DATABASE=Impulse 49 + +usb:v1235p001B* + ID_MODEL_FROM_DATABASE=Impulse 61 + +usb:v1235p4661* + ID_MODEL_FROM_DATABASE=ReMOTE25 + +usb:v1235p8000* + ID_MODEL_FROM_DATABASE=Scarlett 18i6 + +usb:v1235p8002* + ID_MODEL_FROM_DATABASE=Scarlett 8i6 + +usb:v1235p8006* + ID_MODEL_FROM_DATABASE=Focusrite Scarlett 2i2 + +usb:v1235p8008* + ID_MODEL_FROM_DATABASE=Saffire 6 + +usb:v1235p800A* + ID_MODEL_FROM_DATABASE=Scarlett 2i4 + +usb:v1235p800C* + ID_MODEL_FROM_DATABASE=Scarlett 18i20 + +usb:v1235p800E* + ID_MODEL_FROM_DATABASE=iTrack Solo + +usb:v1235p8010* + ID_MODEL_FROM_DATABASE=Forte + +usb:v1235p8012* + ID_MODEL_FROM_DATABASE=Scarlett 6i6 + +usb:v1235p8014* + ID_MODEL_FROM_DATABASE=Scarlett 18i8 + +usb:v1241* + ID_VENDOR_FROM_DATABASE=Belkin + +usb:v1241p0504* + ID_MODEL_FROM_DATABASE=Wireless Trackball Keyboard + +usb:v1241p1111* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v1241p1122* + ID_MODEL_FROM_DATABASE=Typhoon Stream Optical Mouse USB+PS/2 + +usb:v1241p1155* + ID_MODEL_FROM_DATABASE=Memorex Optical ScrollPro Mouse SE MX4600 + +usb:v1241p1166* + ID_MODEL_FROM_DATABASE=MI-2150 Trust Mouse + +usb:v1241p1177* + ID_MODEL_FROM_DATABASE=Mouse [HT82M21A] + +usb:v1241p1503* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v1241p1603* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v1241pF767* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v124A* + ID_VENDOR_FROM_DATABASE=AirVast + +usb:v124Ap168B* + ID_MODEL_FROM_DATABASE=PRISM3 WLAN Adapter + +usb:v124Ap4017* + ID_MODEL_FROM_DATABASE=PC-Chips 802.11b Adapter + +usb:v124Ap4023* + ID_MODEL_FROM_DATABASE=WM168g 802.11bg Wireless Adapter [Intersil ISL3886] + +usb:v124Ap4025* + ID_MODEL_FROM_DATABASE=IOGear GWU513 v2 802.11bg Wireless Adapter [Intersil ISL3887] + +usb:v124B* + ID_VENDOR_FROM_DATABASE=Nyko (Honey Bee) + +usb:v124Bp4D01* + ID_MODEL_FROM_DATABASE=Airflo EX Joystick + +usb:v124C* + ID_VENDOR_FROM_DATABASE=MXI - Memory Experts International, Inc. + +usb:v124Cp3200* + ID_MODEL_FROM_DATABASE=Stealth MXP 1GB + +usb:v125C* + ID_VENDOR_FROM_DATABASE=Apogee Inc. + +usb:v125Cp0010* + ID_MODEL_FROM_DATABASE=Alta series CCD + +usb:v125F* + ID_VENDOR_FROM_DATABASE=A-DATA Technology Co., Ltd. + +usb:v125Fp312A* + ID_MODEL_FROM_DATABASE=Superior S102 + +usb:v125Fp312B* + ID_MODEL_FROM_DATABASE=Superior S102 Pro + +usb:v125FpA15A* + ID_MODEL_FROM_DATABASE=DashDrive Durable HD710 portable HDD various size + +usb:v125FpA22A* + ID_MODEL_FROM_DATABASE=DashDrive Elite HE720 500GB + +usb:v125FpA91A* + ID_MODEL_FROM_DATABASE=Portable HDD CH91 + +usb:v125FpC08A* + ID_MODEL_FROM_DATABASE=C008 Flash Drive + +usb:v125FpC81A* + ID_MODEL_FROM_DATABASE=Flash drive + +usb:v125FpC93A* + ID_MODEL_FROM_DATABASE=4GB Pen Drive + +usb:v125FpC96A* + ID_MODEL_FROM_DATABASE=C906 Flash Drive + +usb:v125FpCB10* + ID_MODEL_FROM_DATABASE=Dash Drive UV100 + +usb:v1260* + ID_VENDOR_FROM_DATABASE=Standard Microsystems Corp. + +usb:v1260pEE22* + ID_MODEL_FROM_DATABASE=SMC2862W-G v3 EZ Connect 802.11g Adapter [Intersil ISL3887] + +usb:v1264* + ID_VENDOR_FROM_DATABASE=Covidien Energy-based Devices + +usb:v1266* + ID_VENDOR_FROM_DATABASE=Pirelli Broadband Solutions + +usb:v1266p6302* + ID_MODEL_FROM_DATABASE=Fastweb DRG A226M ADSL Router + +usb:v1267* + ID_VENDOR_FROM_DATABASE=Logic3 / SpectraVideo plc + +usb:v1267p0103* + ID_MODEL_FROM_DATABASE=G-720 Keyboard + +usb:v1267p0201* + ID_MODEL_FROM_DATABASE=A4Tech SWOP-3 Mouse + +usb:v1267p0210* + ID_MODEL_FROM_DATABASE=LG Optical Mouse 3D-310 + +usb:v1267pA001* + ID_MODEL_FROM_DATABASE=JP260 PC Game Pad + +usb:v1267pC002* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse + +usb:v126C* + ID_VENDOR_FROM_DATABASE=Aristocrat Technologies + +usb:v126D* + ID_VENDOR_FROM_DATABASE=Bel Stewart + +usb:v126E* + ID_VENDOR_FROM_DATABASE=Strobe Data, Inc. + +usb:v126F* + ID_VENDOR_FROM_DATABASE=TwinMOS + +usb:v126Fp0163* + ID_MODEL_FROM_DATABASE=Storage device (2gB thumb drive) + +usb:v126Fp1325* + ID_MODEL_FROM_DATABASE=Mobile Disk + +usb:v126Fp2168* + ID_MODEL_FROM_DATABASE=Mobile Disk III + +usb:v126FpA006* + ID_MODEL_FROM_DATABASE=G240 802.11bg + +usb:v1274* + ID_VENDOR_FROM_DATABASE=Ensoniq + +usb:v1275* + ID_VENDOR_FROM_DATABASE=Xaxero Marine Software Engineering, Ltd. + +usb:v1275p0002* + ID_MODEL_FROM_DATABASE=WeatherFax 2000 Demodulator + +usb:v1275p0080* + ID_MODEL_FROM_DATABASE=SkyEye Weather Satellite Receiver + +usb:v1278* + ID_VENDOR_FROM_DATABASE=Starlight Xpress + +usb:v1278p0105* + ID_MODEL_FROM_DATABASE=SXV-M5 + +usb:v1278p0107* + ID_MODEL_FROM_DATABASE=SXV-M7 + +usb:v1278p0109* + ID_MODEL_FROM_DATABASE=SXV-M9 + +usb:v1278p0110* + ID_MODEL_FROM_DATABASE=SXVF-H16 + +usb:v1278p0115* + ID_MODEL_FROM_DATABASE=SXVF-H5 + +usb:v1278p0119* + ID_MODEL_FROM_DATABASE=SXV-H9 + +usb:v1278p0135* + ID_MODEL_FROM_DATABASE=SXVF-H35 + +usb:v1278p0136* + ID_MODEL_FROM_DATABASE=SXVF-H36 + +usb:v1278p0200* + ID_MODEL_FROM_DATABASE=SXV interface for paraller MX cameras + +usb:v1278p0305* + ID_MODEL_FROM_DATABASE=SXV-M5C + +usb:v1278p0307* + ID_MODEL_FROM_DATABASE=SXV-M7C + +usb:v1278p0319* + ID_MODEL_FROM_DATABASE=SXV-H9C + +usb:v1278p0325* + ID_MODEL_FROM_DATABASE=SXV-M25C + +usb:v1278p0326* + ID_MODEL_FROM_DATABASE=SXVR-M26C + +usb:v1278p0507* + ID_MODEL_FROM_DATABASE=Lodestar autoguider + +usb:v1278p0517* + ID_MODEL_FROM_DATABASE=CoStar + +usb:v1283* + ID_VENDOR_FROM_DATABASE=zebris Medical GmbH + +usb:v1283p0100* + ID_MODEL_FROM_DATABASE=USB-RS232 Adaptor + +usb:v1283p0110* + ID_MODEL_FROM_DATABASE=CMS20 + +usb:v1283p0111* + ID_MODEL_FROM_DATABASE=CMS 10 + +usb:v1283p0112* + ID_MODEL_FROM_DATABASE=CMS 05 + +usb:v1283p0114* + ID_MODEL_FROM_DATABASE=ARCUS digma PC-Interface + +usb:v1283p0115* + ID_MODEL_FROM_DATABASE=SAM Axioquick recorder + +usb:v1283p0116* + ID_MODEL_FROM_DATABASE=SAM Axioquick recorder + +usb:v1283p0120* + ID_MODEL_FROM_DATABASE=emed-X + +usb:v1283p0121* + ID_MODEL_FROM_DATABASE=emed-AT + +usb:v1283p0130* + ID_MODEL_FROM_DATABASE=PDM + +usb:v1283p0150* + ID_MODEL_FROM_DATABASE=CMS10GI (Golf) + +usb:v1286* + ID_VENDOR_FROM_DATABASE=Marvell Semiconductor, Inc. + +usb:v1286p00BC* + ID_MODEL_FROM_DATABASE=Marvell JTAG Probe + +usb:v1286p1FAB* + ID_MODEL_FROM_DATABASE=88W8338 [Libertas] 802.11g + +usb:v1286p2001* + ID_MODEL_FROM_DATABASE=88W8388 802.11a/b/g WLAN + +usb:v1286p2006* + ID_MODEL_FROM_DATABASE=88W8362 802.11n WLAN + +usb:v1286p8001* + ID_MODEL_FROM_DATABASE=BLOB boot loader firmware + +usb:v1291* + ID_VENDOR_FROM_DATABASE=Qualcomm Flarion Technologies, Inc. / Leadtek Research, Inc. + +usb:v1291p0010* + ID_MODEL_FROM_DATABASE=FDM 2xxx Flash-OFDM modem + +usb:v1291p0011* + ID_MODEL_FROM_DATABASE=LR7F06/LR7F14 Flash-OFDM modem + +usb:v1292* + ID_VENDOR_FROM_DATABASE=Innomedia + +usb:v1292p0258* + ID_MODEL_FROM_DATABASE=Creative Labs VoIP Blaster + +usb:v1293* + ID_VENDOR_FROM_DATABASE=Belkin Components [hex] + +usb:v1293p0002* + ID_MODEL_FROM_DATABASE=F5U002 Parallel Port [uss720] + +usb:v1293p2101* + ID_MODEL_FROM_DATABASE=104-key keyboard + +usb:v1294* + ID_VENDOR_FROM_DATABASE=RISO KAGAKU CORP. + +usb:v1294p1320* + ID_MODEL_FROM_DATABASE=Webmail Notifier + +usb:v129B* + ID_VENDOR_FROM_DATABASE=CyberTAN Technology + +usb:v129Bp160B* + ID_MODEL_FROM_DATABASE=Siemens S30853-S1031-R351 802.11g Wireless Adapter [Atheros AR5523] + +usb:v129Bp160C* + ID_MODEL_FROM_DATABASE=Siemens S30853-S1038-R351 802.11g Wireless Adapter [Atheros AR5523] + +usb:v129Bp1666* + ID_MODEL_FROM_DATABASE=TG54USB 802.11bg + +usb:v129Bp1667* + ID_MODEL_FROM_DATABASE=802.11bg + +usb:v129Bp1828* + ID_MODEL_FROM_DATABASE=Gigaset USB Adapter 300 + +usb:v12A7* + ID_VENDOR_FROM_DATABASE=Trendchip Technologies Corp. + +usb:v12AB* + ID_VENDOR_FROM_DATABASE=Honey Bee Electronic International Ltd. + +usb:v12B8* + ID_VENDOR_FROM_DATABASE=Zhejiang Xinya Electronic Technology Co., Ltd. + +usb:v12B9* + ID_VENDOR_FROM_DATABASE=E28 + +usb:v12BA* + ID_VENDOR_FROM_DATABASE=Licensed by Sony Computer Entertainment America + +usb:v12BAp00FF* + ID_MODEL_FROM_DATABASE=Rocksmith Guitar Adapter + +usb:v12BAp0100* + ID_MODEL_FROM_DATABASE=RedOctane Guitar for PlayStation(R)3 + +usb:v12BAp0120* + ID_MODEL_FROM_DATABASE=RedOctane Drum Kit for PlayStation(R)3 + +usb:v12BAp0200* + ID_MODEL_FROM_DATABASE=Harmonix Guitar for PlayStation(R)3 + +usb:v12BAp0210* + ID_MODEL_FROM_DATABASE=Harmonix Drum Kit for PlayStation(R)3 + +usb:v12BD* + ID_VENDOR_FROM_DATABASE=Gembird + +usb:v12BDpD012* + ID_MODEL_FROM_DATABASE=JPD Shockforce gamepad + +usb:v12C4* + ID_VENDOR_FROM_DATABASE=Autocue Group Ltd + +usb:v12C4p0006* + ID_MODEL_FROM_DATABASE=Teleprompter Two-button Hand Control (v1) + +usb:v12C4p0008* + ID_MODEL_FROM_DATABASE=Teleprompter Foot Control (v1) + +usb:v12CF* + ID_VENDOR_FROM_DATABASE=DEXIN + +usb:v12CFp0170* + ID_MODEL_FROM_DATABASE=Tt eSPORTS BLACK Gaming mouse + +usb:v12D1* + ID_VENDOR_FROM_DATABASE=Huawei Technologies Co., Ltd. + +usb:v12D1p1001* + ID_MODEL_FROM_DATABASE=E169/E620/E800 HSDPA Modem + +usb:v12D1p1003* + ID_MODEL_FROM_DATABASE=E220 HSDPA Modem / E230/E270/E870 HSDPA/HSUPA Modem + +usb:v12D1p1004* + ID_MODEL_FROM_DATABASE=E220 (bis) + +usb:v12D1p1009* + ID_MODEL_FROM_DATABASE=U120 + +usb:v12D1p1010* + ID_MODEL_FROM_DATABASE=ETS2252+ CDMA Fixed Wireless Terminal + +usb:v12D1p1021* + ID_MODEL_FROM_DATABASE=U8520 + +usb:v12D1p1035* + ID_MODEL_FROM_DATABASE=U8120 + +usb:v12D1p1037* + ID_MODEL_FROM_DATABASE=Ideos + +usb:v12D1p1038* + ID_MODEL_FROM_DATABASE=Ideos (debug mode) + +usb:v12D1p1039* + ID_MODEL_FROM_DATABASE=Ideos (tethering mode) + +usb:v12D1p1404* + ID_MODEL_FROM_DATABASE=EM770W miniPCI WCDMA Modem + +usb:v12D1p1406* + ID_MODEL_FROM_DATABASE=E1750 + +usb:v12D1p140B* + ID_MODEL_FROM_DATABASE=EC1260 Wireless Data Modem HSD USB Card + +usb:v12D1p140C* + ID_MODEL_FROM_DATABASE=E180v + +usb:v12D1p1412* + ID_MODEL_FROM_DATABASE=EC168c + +usb:v12D1p1436* + ID_MODEL_FROM_DATABASE=Broadband stick + +usb:v12D1p1446* + ID_MODEL_FROM_DATABASE=Broadband stick (modem on) + +usb:v12D1p1465* + ID_MODEL_FROM_DATABASE=K3765 HSPA + +usb:v12D1p14C3* + ID_MODEL_FROM_DATABASE=K5005 Vodafone LTE/UMTS/GSM Modem/Networkcard + +usb:v12D1p14C8* + ID_MODEL_FROM_DATABASE=K5005 Vodafone LTE/UMTS/GSM MOdem/Networkcard + +usb:v12D1p14C9* + ID_MODEL_FROM_DATABASE=K3770 3G Modem + +usb:v12D1p14CF* + ID_MODEL_FROM_DATABASE=K3772 + +usb:v12D1p14D1* + ID_MODEL_FROM_DATABASE=K3770 3G Modem (Mass Storage Mode) + +usb:v12D1p14DB* + ID_MODEL_FROM_DATABASE=E353/E3131 + +usb:v12D1p14F1* + ID_MODEL_FROM_DATABASE=Gobi 3000 HSPA+ Modem + +usb:v12D1p14FE* + ID_MODEL_FROM_DATABASE=Modem (Mass Storage Mode) + +usb:v12D1p1501* + ID_MODEL_FROM_DATABASE=Pulse + +usb:v12D1p1505* + ID_MODEL_FROM_DATABASE=E398 LTE/UMTS/GSM Modem/Networkcard + +usb:v12D1p1506* + ID_MODEL_FROM_DATABASE=Modem/Networkcard + +usb:v12D1p150A* + ID_MODEL_FROM_DATABASE=E398 LTE/UMTS/GSM Modem/Networkcard + +usb:v12D1p1520* + ID_MODEL_FROM_DATABASE=K3765 HSPA + +usb:v12D1p1521* + ID_MODEL_FROM_DATABASE=K4505 HSPA+ + +usb:v12D1p155A* + ID_MODEL_FROM_DATABASE=R205 Mobile WiFi (CD-ROM mode) + +usb:v12D1p1575* + ID_MODEL_FROM_DATABASE=K5150 LTE modem + +usb:v12D1p15CA* + ID_MODEL_FROM_DATABASE=E3131 3G/UMTS/HSPA+ Modem (Mass Storage Mode) + +usb:v12D1p1805* + ID_MODEL_FROM_DATABASE=AT&T Go Phone U2800A phone + +usb:v12D1p1C05* + ID_MODEL_FROM_DATABASE=Broadband stick (modem on) + +usb:v12D1p1C0B* + ID_MODEL_FROM_DATABASE=E173s 3G broadband stick (modem off) + +usb:v12D1p1C20* + ID_MODEL_FROM_DATABASE=R205 Mobile WiFi (Charging) + +usb:v12D1p1D50* + ID_MODEL_FROM_DATABASE=ET302s TD-SCDMA/TD-HSDPA Mobile Broadband + +usb:v12D1p1F01* + ID_MODEL_FROM_DATABASE=E353/E3131 (Mass storage mode) + +usb:v12D1p1F16* + ID_MODEL_FROM_DATABASE=K5150 LTE modem (Mass Storage Mode) + +usb:v12D1p380B* + ID_MODEL_FROM_DATABASE=WiMAX USB modem(s) + +usb:v12D2* + ID_VENDOR_FROM_DATABASE=LINE TECH INDUSTRIAL CO., LTD. + +usb:v12D6* + ID_VENDOR_FROM_DATABASE=EMS Dr. Thomas Wuensche + +usb:v12D6p0444* + ID_MODEL_FROM_DATABASE=CPC-USB/ARM7 + +usb:v12D6p0888* + ID_MODEL_FROM_DATABASE=CPC-USB/M16C + +usb:v12D7* + ID_VENDOR_FROM_DATABASE=BETTER WIRE FACTORY CO., LTD. + +usb:v12D8* + ID_VENDOR_FROM_DATABASE=Araneus Information Systems Oy + +usb:v12D8p0001* + ID_MODEL_FROM_DATABASE=Alea I True Random Number Generator + +usb:v12E6* + ID_VENDOR_FROM_DATABASE=Waldorf Music GmbH + +usb:v12E6p0013* + ID_MODEL_FROM_DATABASE=Blofeld + +usb:v12EF* + ID_VENDOR_FROM_DATABASE=Tapwave, Inc. + +usb:v12EFp0100* + ID_MODEL_FROM_DATABASE=Tapwave Handheld [Tapwave Zodiac] + +usb:v12F5* + ID_VENDOR_FROM_DATABASE=Dynamic System Electronics Corp. + +usb:v12F7* + ID_VENDOR_FROM_DATABASE=Memorex Products, Inc. + +usb:v12F7p1A00* + ID_MODEL_FROM_DATABASE=TD Classic 003B + +usb:v12F7p1E23* + ID_MODEL_FROM_DATABASE=TravelDrive 2007 Flash Drive + +usb:v12FD* + ID_VENDOR_FROM_DATABASE=AIN Comm. Technology Co., Ltd + +usb:v12FDp1001* + ID_MODEL_FROM_DATABASE=AWU2000b 802.11b Stick + +usb:v12FF* + ID_VENDOR_FROM_DATABASE=Fascinating Electronics, Inc. + +usb:v12FFp0101* + ID_MODEL_FROM_DATABASE=Advanced RC Servo Controller + +usb:v1307* + ID_VENDOR_FROM_DATABASE=Transcend Information, Inc. + +usb:v1307p0163* + ID_MODEL_FROM_DATABASE=256MB/512MB/1GB Flash Drive + +usb:v1307p0165* + ID_MODEL_FROM_DATABASE=2GB/4GB/8GB Flash Drive + +usb:v1307p0190* + ID_MODEL_FROM_DATABASE=Ut190 8 GB Flash Drive with MicroSD reader + +usb:v1307p0310* + ID_MODEL_FROM_DATABASE=SD/MicroSD CardReader [hama] + +usb:v1307p0330* + ID_MODEL_FROM_DATABASE=63-in-1 Multi-Card Reader/Writer + +usb:v1307p0361* + ID_MODEL_FROM_DATABASE=CR-75: 51-in-1 Card Reader/Writer [Sakar] + +usb:v1307p1169* + ID_MODEL_FROM_DATABASE=TS2GJF210 JetFlash 210 2GB + +usb:v1307p1171* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v1308* + ID_VENDOR_FROM_DATABASE=Shuttle, Inc. + +usb:v1308p0003* + ID_MODEL_FROM_DATABASE=VFD Module + +usb:v1308pC001* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1310* + ID_VENDOR_FROM_DATABASE=Roper + +usb:v1310p0001* + ID_MODEL_FROM_DATABASE=Class 1 Bluetooth Dongle + +usb:v1312* + ID_VENDOR_FROM_DATABASE=ICS Electronics + +usb:v1313* + ID_VENDOR_FROM_DATABASE=ThorLabs + +usb:v1313p0010* + ID_MODEL_FROM_DATABASE=LC1 Linear Camera (Jungo) + +usb:v1313p0011* + ID_MODEL_FROM_DATABASE=SP1 Spectrometer (Jungo) + +usb:v1313p0012* + ID_MODEL_FROM_DATABASE=SP2 Spectrometer (Jungo) + +usb:v1313p0110* + ID_MODEL_FROM_DATABASE=LC1 Linear Camera (VISA) + +usb:v1313p0111* + ID_MODEL_FROM_DATABASE=SP1 Spectrometer (VISA) + +usb:v1313p0112* + ID_MODEL_FROM_DATABASE=SP2 Spectrometer (VISA) + +usb:v1313p8001* + ID_MODEL_FROM_DATABASE=TXP-Series Slot (TXP5001, TXP5004) + +usb:v1313p8012* + ID_MODEL_FROM_DATABASE=BC106 Camera Beam Profiler + +usb:v1313p8013* + ID_MODEL_FROM_DATABASE=WFS10 Wavefront Sensor + +usb:v1313p8017* + ID_MODEL_FROM_DATABASE=BC206 Camera Beam Profiler + +usb:v1313p8019* + ID_MODEL_FROM_DATABASE=BP2 Multi Slit Beam Profiler + +usb:v1313p8020* + ID_MODEL_FROM_DATABASE=PM300 Optical Power Meter + +usb:v1313p8021* + ID_MODEL_FROM_DATABASE=PM300E Optical Power and Energy Meter + +usb:v1313p8022* + ID_MODEL_FROM_DATABASE=PM320E Optical Power and Energy Meter + +usb:v1313p8030* + ID_MODEL_FROM_DATABASE=ER100 Extinction Ratio Meter + +usb:v1313p8070* + ID_MODEL_FROM_DATABASE=PM100D + +usb:v131D* + ID_VENDOR_FROM_DATABASE=Natural Point + +usb:v131Dp0155* + ID_MODEL_FROM_DATABASE=TrackIR 3 Pro Head Tracker + +usb:v131Dp0156* + ID_MODEL_FROM_DATABASE=TrackIR 4 Pro Head Tracker + +usb:v132A* + ID_VENDOR_FROM_DATABASE=Envara Inc. + +usb:v132Ap1502* + ID_MODEL_FROM_DATABASE=WiND 802.11abg / 802.11bg WLAN + +usb:v132B* + ID_VENDOR_FROM_DATABASE=Konica Minolta + +usb:v132Bp0000* + ID_MODEL_FROM_DATABASE=Dimage A2 Camera + +usb:v132Bp0001* + ID_MODEL_FROM_DATABASE=Minolta DiMAGE A2 (ptp) + +usb:v132Bp0003* + ID_MODEL_FROM_DATABASE=Dimage Xg Camera + +usb:v132Bp0006* + ID_MODEL_FROM_DATABASE=Dimage Z2 Camera + +usb:v132Bp0007* + ID_MODEL_FROM_DATABASE=Minolta DiMAGE Z2 (PictBridge mode) + +usb:v132Bp0008* + ID_MODEL_FROM_DATABASE=Dimage X21 Camera + +usb:v132Bp000A* + ID_MODEL_FROM_DATABASE=Dimage Scan Dual IV AF-3200 (2891) + +usb:v132Bp000B* + ID_MODEL_FROM_DATABASE=Dimage Z10 Camera + +usb:v132Bp000D* + ID_MODEL_FROM_DATABASE=Dimage X50 Camera [storage?] + +usb:v132Bp000F* + ID_MODEL_FROM_DATABASE=Dimage X50 Camera [p2p?] + +usb:v132Bp0010* + ID_MODEL_FROM_DATABASE=Dimage G600 Camera + +usb:v132Bp0012* + ID_MODEL_FROM_DATABASE=Dimage Scan Elite 5400 II (2892) + +usb:v132Bp0013* + ID_MODEL_FROM_DATABASE=Dimage X31 Camera + +usb:v132Bp0015* + ID_MODEL_FROM_DATABASE=Dimage G530 Camera + +usb:v132Bp0017* + ID_MODEL_FROM_DATABASE=Dimage Z3 Camera + +usb:v132Bp0018* + ID_MODEL_FROM_DATABASE=Minolta DiMAGE Z3 (PictBridge mode) + +usb:v132Bp0019* + ID_MODEL_FROM_DATABASE=Dimage A200 Camera + +usb:v132Bp0021* + ID_MODEL_FROM_DATABASE=Dimage Z5 Camera + +usb:v132Bp0022* + ID_MODEL_FROM_DATABASE=Minolta DiMAGE Z5 (PictBridge mode) + +usb:v132Bp002C* + ID_MODEL_FROM_DATABASE=Dynax 5D camera + +usb:v132Bp2001* + ID_MODEL_FROM_DATABASE=Magicolor 2400w + +usb:v132Bp2004* + ID_MODEL_FROM_DATABASE=Magicolor 5430DL + +usb:v132Bp2005* + ID_MODEL_FROM_DATABASE=Magicolor 2430 DL + +usb:v132Bp2029* + ID_MODEL_FROM_DATABASE=Magicolor 5440DL + +usb:v132Bp2030* + ID_MODEL_FROM_DATABASE=PagePro 1350E(N) + +usb:v132Bp2033* + ID_MODEL_FROM_DATABASE=PagePro 1400W + +usb:v132Bp2043* + ID_MODEL_FROM_DATABASE=Magicolor 2530DL + +usb:v132Bp2045* + ID_MODEL_FROM_DATABASE=Magicolor 2500W + +usb:v132Bp2049* + ID_MODEL_FROM_DATABASE=Magicolor 2490MF + +usb:v133E* + ID_VENDOR_FROM_DATABASE=Kemper Digital GmbH + +usb:v133Ep0815* + ID_MODEL_FROM_DATABASE=Virus TI Desktop + +usb:v1342* + ID_VENDOR_FROM_DATABASE=Mobility + +usb:v1342p0200* + ID_MODEL_FROM_DATABASE=EasiDock 200 Hub + +usb:v1342p0201* + ID_MODEL_FROM_DATABASE=EasiDock 200 Keyboard and Mouse Port + +usb:v1342p0202* + ID_MODEL_FROM_DATABASE=EasiDock 200 Serial Port + +usb:v1342p0203* + ID_MODEL_FROM_DATABASE=EasiDock 200 Printer Port + +usb:v1342p0204* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v1342p0304* + ID_MODEL_FROM_DATABASE=EasiDock Ethernet + +usb:v1343* + ID_VENDOR_FROM_DATABASE=Citizen Systems + +usb:v1343p0003* + ID_MODEL_FROM_DATABASE=CX / DNP DS40 + +usb:v1343p0004* + ID_MODEL_FROM_DATABASE=CX-W / DNP DS80 + +usb:v1343p0005* + ID_MODEL_FROM_DATABASE=CY / DNP DSRX + +usb:v1345* + ID_VENDOR_FROM_DATABASE=Sino Lite Technology Corp. + +usb:v1345p001C* + ID_MODEL_FROM_DATABASE=Xbox Controller Hub + +usb:v1345p6006* + ID_MODEL_FROM_DATABASE=Defender Wireless Controller + +usb:v1347* + ID_VENDOR_FROM_DATABASE=Moravian Instruments + +usb:v1347p0400* + ID_MODEL_FROM_DATABASE=G2CCD USB 1.1 obsolete + +usb:v1347p0401* + ID_MODEL_FROM_DATABASE=G2CCD-S with Sony ICX285 CCD + +usb:v1347p0402* + ID_MODEL_FROM_DATABASE=G2CCD2 + +usb:v1347p0403* + ID_MODEL_FROM_DATABASE=G2/G3CCD-I KAI CCD + +usb:v1347p0404* + ID_MODEL_FROM_DATABASE=G2/G3/G4 CCD-F KAF CCD + +usb:v1347p0405* + ID_MODEL_FROM_DATABASE=Gx CCD-I CCD + +usb:v1347p0406* + ID_MODEL_FROM_DATABASE=Gx CCD-F CCD + +usb:v1347p0410* + ID_MODEL_FROM_DATABASE=G1-0400 CCD + +usb:v1347p0411* + ID_MODEL_FROM_DATABASE=G1-0800 CCD + +usb:v1347p0412* + ID_MODEL_FROM_DATABASE=G1-0300 CCD + +usb:v1347p0413* + ID_MODEL_FROM_DATABASE=G1-2000 CCD + +usb:v1347p0414* + ID_MODEL_FROM_DATABASE=G1-1400 CCD + +usb:v1348* + ID_VENDOR_FROM_DATABASE=Katsuragawa Electric Co., Ltd. + +usb:v134C* + ID_VENDOR_FROM_DATABASE=PanJit International Inc. + +usb:v134Cp0001* + ID_MODEL_FROM_DATABASE=Touch Panel Controller + +usb:v134Cp0002* + ID_MODEL_FROM_DATABASE=Touch Panel Controller + +usb:v134Cp0003* + ID_MODEL_FROM_DATABASE=Touch Panel Controller + +usb:v134Cp0004* + ID_MODEL_FROM_DATABASE=Touch Panel Controller + +usb:v134E* + ID_VENDOR_FROM_DATABASE=Digby's Bitpile, Inc. DBA D Bit + +usb:v1357* + ID_VENDOR_FROM_DATABASE=P&E Microcomputer Systems + +usb:v1357p0089* + ID_MODEL_FROM_DATABASE=OpenSDA - CDC Serial Port + +usb:v1357p0503* + ID_MODEL_FROM_DATABASE=USB-ML-12 HCS08/HCS12 Multilink + +usb:v1357p0504* + ID_MODEL_FROM_DATABASE=DEMOJM + +usb:v135F* + ID_VENDOR_FROM_DATABASE=Control Development Inc. + +usb:v135Fp0110* + ID_MODEL_FROM_DATABASE=Linear Spectrograph + +usb:v135Fp0111* + ID_MODEL_FROM_DATABASE=Spectrograph - Renumerated + +usb:v135Fp0200* + ID_MODEL_FROM_DATABASE=Linear Spectrograph + +usb:v135Fp0201* + ID_MODEL_FROM_DATABASE=Spectrograph - Renumerated + +usb:v135Fp0240* + ID_MODEL_FROM_DATABASE=MPP Spectrograph + +usb:v1366* + ID_VENDOR_FROM_DATABASE=SEGGER + +usb:v1366p0101* + ID_MODEL_FROM_DATABASE=J-Link PLUS + +usb:v136B* + ID_VENDOR_FROM_DATABASE=STEC + +usb:v136E* + ID_VENDOR_FROM_DATABASE=Andor Technology Ltd. + +usb:v136Ep0014* + ID_MODEL_FROM_DATABASE=Zyla 5.5 sCMOS camera + +usb:v1370* + ID_VENDOR_FROM_DATABASE=Swissbit + +usb:v1370p0323* + ID_MODEL_FROM_DATABASE=Swissmemory cirrusWHITE + +usb:v1370p6828* + ID_MODEL_FROM_DATABASE=Victorinox Flash Drive + +usb:v1371* + ID_VENDOR_FROM_DATABASE=CNet Technology Inc. + +usb:v1371p0001* + ID_MODEL_FROM_DATABASE=CNUSB-611AR Wireless Adapter-G [AT76C503] + +usb:v1371p0002* + ID_MODEL_FROM_DATABASE=CNUSB-611AR Wireless Adapter-G [AT76C503] (FiberLine WL-240U) + +usb:v1371p0013* + ID_MODEL_FROM_DATABASE=CNUSB-611 Wireless Adapter [AT76C505] + +usb:v1371p0014* + ID_MODEL_FROM_DATABASE=CNUSB-611 Wireless Adapter [AT76C505] (FiberLine WL-240U) + +usb:v1371p5743* + ID_MODEL_FROM_DATABASE=CNUSB-611 (D) Wireless Adapter [AT76C503] + +usb:v1371p9022* + ID_MODEL_FROM_DATABASE=CWD-854 [RT2573] + +usb:v1371p9032* + ID_MODEL_FROM_DATABASE=CWD-854 rev F + +usb:v1371p9401* + ID_MODEL_FROM_DATABASE=CWD-854 Wireless 802.11g 54Mbps Network Adapter [RTL8187] + +usb:v1376* + ID_VENDOR_FROM_DATABASE=Vimtron Electronics Co., Ltd. + +usb:v137B* + ID_VENDOR_FROM_DATABASE=SCAPS GmbH + +usb:v137Bp0002* + ID_MODEL_FROM_DATABASE=SCAPS USC-2 Scanner Controller + +usb:v1385* + ID_VENDOR_FROM_DATABASE=Netgear, Inc + +usb:v1385p4250* + ID_MODEL_FROM_DATABASE=WG111T + +usb:v1385p4251* + ID_MODEL_FROM_DATABASE=WG111T (no firmware) + +usb:v1385p5F00* + ID_MODEL_FROM_DATABASE=WPN111 RangeMax(TM) Wireless USB 2.0 Adapter + +usb:v1385p5F01* + ID_MODEL_FROM_DATABASE=WPN111 (no firmware) + +usb:v1385p5F02* + ID_MODEL_FROM_DATABASE=WPN111 (no firmware) + +usb:v1385p6E00* + ID_MODEL_FROM_DATABASE=WPNT121 802.11g 240Mbps Wireless Adapter [Airgo AGN300] + +usb:v138A* + ID_VENDOR_FROM_DATABASE=Validity Sensors, Inc. + +usb:v138Ap0001* + ID_MODEL_FROM_DATABASE=VFS101 Fingerprint Reader + +usb:v138Ap0005* + ID_MODEL_FROM_DATABASE=VFS301 Fingerprint Reader + +usb:v138Ap0007* + ID_MODEL_FROM_DATABASE=VFS451 Fingerprint Reader + +usb:v138Ap0008* + ID_MODEL_FROM_DATABASE=VFS300 Fingerprint Reader + +usb:v138Ap0010* + ID_MODEL_FROM_DATABASE=VFS Fingerprint sensor + +usb:v138Ap0011* + ID_MODEL_FROM_DATABASE=VFS5011 Fingerprint Reader + +usb:v138Ap0017* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v138Ap0018* + ID_MODEL_FROM_DATABASE=Fingerprint scanner + +usb:v138Ap003C* + ID_MODEL_FROM_DATABASE=VFS471 Fingerprint Reader + +usb:v138Ap003D* + ID_MODEL_FROM_DATABASE=VFS491 + +usb:v138Ap003F* + ID_MODEL_FROM_DATABASE=VFS495 Fingerprint Reader + +usb:v138Ap0050* + ID_MODEL_FROM_DATABASE=Swipe Fingerprint Sensor + +usb:v138E* + ID_VENDOR_FROM_DATABASE=Jungo LTD + +usb:v138Ep9000* + ID_MODEL_FROM_DATABASE=Raisonance S.A. STM32 ARM evaluation board + +usb:v1390* + ID_VENDOR_FROM_DATABASE=TOMTOM B.V. + +usb:v1390p0001* + ID_MODEL_FROM_DATABASE=GO 520 T/GO 630/ONE XL (v9) + +usb:v1390p5454* + ID_MODEL_FROM_DATABASE=Blue & Me 2 + +usb:v1390p7474* + ID_MODEL_FROM_DATABASE=GPS Sport Watch [Runner, Multi-Sport] + +usb:v1391* + ID_VENDOR_FROM_DATABASE=IdealTEK, Inc. + +usb:v1391p1000* + ID_MODEL_FROM_DATABASE=URTC-1000 + +usb:v1395* + ID_VENDOR_FROM_DATABASE=Sennheiser Communications + +usb:v1395p3556* + ID_MODEL_FROM_DATABASE=USB Headset + +usb:v1397* + ID_VENDOR_FROM_DATABASE=BEHRINGER International GmbH + +usb:v1397p00BC* + ID_MODEL_FROM_DATABASE=BCF2000 + +usb:v1398* + ID_VENDOR_FROM_DATABASE=Q-tec + +usb:v1398p2103* + ID_MODEL_FROM_DATABASE=USB 2.0 Storage Device + +usb:v13AD* + ID_VENDOR_FROM_DATABASE=Baltech + +usb:v13ADp9999* + ID_MODEL_FROM_DATABASE=Card reader + +usb:v13B0* + ID_VENDOR_FROM_DATABASE=PerkinElmer Optoelectronics + +usb:v13B0p000A* + ID_MODEL_FROM_DATABASE=Alesis Photon X25 MIDI Controller + +usb:v13B1* + ID_VENDOR_FROM_DATABASE=Linksys + +usb:v13B1p000A* + ID_MODEL_FROM_DATABASE=WUSB54G v2 802.11g Adapter [Intersil ISL3887] + +usb:v13B1p000B* + ID_MODEL_FROM_DATABASE=WUSB11 v4.0 802.11b Adapter [ALi M4301] + +usb:v13B1p000C* + ID_MODEL_FROM_DATABASE=WUSB54AG 802.11a/g Adapter [Intersil ISL3887] + +usb:v13B1p000D* + ID_MODEL_FROM_DATABASE=WUSB54G v4 802.11g Adapter [Ralink RT2500USB] + +usb:v13B1p000E* + ID_MODEL_FROM_DATABASE=WUSB54GS v1 802.11g Adapter [Broadcom 4320 USB] + +usb:v13B1p0011* + ID_MODEL_FROM_DATABASE=WUSB54GP v4.0 802.11g Adapter [Ralink RT2500USB] + +usb:v13B1p0014* + ID_MODEL_FROM_DATABASE=WUSB54GS v2 802.11g Adapter [Broadcom 4320 USB] + +usb:v13B1p0018* + ID_MODEL_FROM_DATABASE=USB200M 10/100 Ethernet Adapter + +usb:v13B1p001A* + ID_MODEL_FROM_DATABASE=HU200TS Wireless Adapter + +usb:v13B1p001E* + ID_MODEL_FROM_DATABASE=WUSBF54G 802.11bg + +usb:v13B1p0020* + ID_MODEL_FROM_DATABASE=WUSB54GC v1 802.11g Adapter [Ralink RT73] + +usb:v13B1p0022* + ID_MODEL_FROM_DATABASE=WUSB54GX4 802.11g 240Mbps Wireless Adapter [Airgo AGN300] + +usb:v13B1p0023* + ID_MODEL_FROM_DATABASE=WUSB54GR + +usb:v13B1p0024* + ID_MODEL_FROM_DATABASE=WUSBF54G v1.1 802.11bg + +usb:v13B1p0026* + ID_MODEL_FROM_DATABASE=WUSB54GSC v1 802.11g Adapter [Broadcom 4320 USB] + +usb:v13B1p0028* + ID_MODEL_FROM_DATABASE=WUSB200 802.11g Adapter [Ralink RT2671] + +usb:v13B1p0029* + ID_MODEL_FROM_DATABASE=WUSB300N 802.11bgn Wireless Adapter [Marvell 88W8362+88W8060] + +usb:v13B1p002F* + ID_MODEL_FROM_DATABASE=AE1000 v1 802.11n [Ralink RT3572] + +usb:v13B1p0031* + ID_MODEL_FROM_DATABASE=AM10 v1 802.11n [Ralink RT3072] + +usb:v13B1p0039* + ID_MODEL_FROM_DATABASE=AE1200 802.11bgn Wireless Adapter [Broadcom BCM43235] + +usb:v13B1p003A* + ID_MODEL_FROM_DATABASE=AE2500 802.11abgn Wireless Adapter [Broadcom BCM43236] + +usb:v13B1p003B* + ID_MODEL_FROM_DATABASE=AE3000 802.11abgn (3x3) Wireless Adapter [Ralink RT3573] + +usb:v13B1p003E* + ID_MODEL_FROM_DATABASE=AE6000 802.11a/b/g/n/ac Wireless Adapter [MediaTek MT7610U] + +usb:v13B1p003F* + ID_MODEL_FROM_DATABASE=WUSB6300 802.11a/b/g/n/ac Wireless Adapter [Realtek RTL8812AU] + +usb:v13B1p13B1* + ID_MODEL_FROM_DATABASE=WUSB200: Wireless-G Business Network Adapter with Rangebooster + +usb:v13B2* + ID_VENDOR_FROM_DATABASE=Alesis + +usb:v13B2p0030* + ID_MODEL_FROM_DATABASE=Multimix 8 + +usb:v13B3* + ID_VENDOR_FROM_DATABASE=Nippon Dics Co., Ltd. + +usb:v13BA* + ID_VENDOR_FROM_DATABASE=PCPlay + +usb:v13BAp0001* + ID_MODEL_FROM_DATABASE=Konig Electronic CMP-KEYPAD12 Numeric Keypad + +usb:v13BAp0017* + ID_MODEL_FROM_DATABASE=PS/2 Keyboard+Mouse Adapter + +usb:v13BAp0018* + ID_MODEL_FROM_DATABASE=Barcode PCP-BCG4209 + +usb:v13BE* + ID_VENDOR_FROM_DATABASE=Ricoh Printing Systems, Ltd. + +usb:v13CA* + ID_VENDOR_FROM_DATABASE=JyeTai Precision Industrial Co., Ltd. + +usb:v13CF* + ID_VENDOR_FROM_DATABASE=Wisair Ltd. + +usb:v13CFp1200* + ID_MODEL_FROM_DATABASE=Olidata Wireless Multimedia Adapter + +usb:v13D0* + ID_VENDOR_FROM_DATABASE=Techsan Electronics Co., Ltd. + +usb:v13D0p2282* + ID_MODEL_FROM_DATABASE=TechniSat DVB-PC TV Star 2 + +usb:v13D1* + ID_VENDOR_FROM_DATABASE=A-Max Technology Macao Commercial Offshore Co. Ltd. + +usb:v13D1p7019* + ID_MODEL_FROM_DATABASE=MD 82288 + +usb:v13D1pABE6* + ID_MODEL_FROM_DATABASE=Wireless 802.11g 54Mbps Network Adapter [RTL8187] + +usb:v13D2* + ID_VENDOR_FROM_DATABASE=Shark Multimedia + +usb:v13D2p0400* + ID_MODEL_FROM_DATABASE=Pocket Ethernet [klsi] + +usb:v13D3* + ID_VENDOR_FROM_DATABASE=IMC Networks + +usb:v13D3p3201* + ID_MODEL_FROM_DATABASE=VisionDTV USB-Ter/HAMA USB DVB-T device cold + +usb:v13D3p3202* + ID_MODEL_FROM_DATABASE=VisionDTV USB-Ter/HAMA USB DVB-T device warm + +usb:v13D3p3203* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v13D3p3204* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v13D3p3205* + ID_MODEL_FROM_DATABASE=DNTV Live! Tiny USB2 BDA (No Remote) + +usb:v13D3p3206* + ID_MODEL_FROM_DATABASE=DNTV Live! Tiny USB2 BDA (No Remote) + +usb:v13D3p3207* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v13D3p3208* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v13D3p3209* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7022BDA DVB-S Box(Without HID) + +usb:v13D3p3211* + ID_MODEL_FROM_DATABASE=DTV-DVB Hybrid Analog/Capture / Pinnacle PCTV 310e + +usb:v13D3p3212* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT704C - DVBT/NTSC/PAL Driver(PCM4) + +usb:v13D3p3213* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT704D - DVBT/NTSC/PAL Driver (PCM4) + +usb:v13D3p3214* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT704F -(MiniCard) DVBT/NTSC/PAL Driver(Without HID) + +usb:v13D3p3215* + ID_MODEL_FROM_DATABASE=DTV-DVB UDAT7240 - ATSC/NTSC/PAL Driver(PCM4) + +usb:v13D3p3216* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT 7047-USB 2.0 DVB-T Driver + +usb:v13D3p3217* + ID_MODEL_FROM_DATABASE=Digital-TV Receiver. + +usb:v13D3p3219* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT7049 - DVB-T Driver(Without HID) + +usb:v13D3p3220* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT 7047M-USB 2.0 DVB-T Driver + +usb:v13D3p3223* + ID_MODEL_FROM_DATABASE=DNTV Live! Tiny USB2 BDA (No Remote) + +usb:v13D3p3224* + ID_MODEL_FROM_DATABASE=DNTV Live! Tiny USB2 BDA (No Remote) + +usb:v13D3p3226* + ID_MODEL_FROM_DATABASE=DigitalNow TinyTwin DVB-T Receiver + +usb:v13D3p3234* + ID_MODEL_FROM_DATABASE=DVB-T FTA Half Minicard [RTL2832U] + +usb:v13D3p3236* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT 7047A-USB 2.0 DVB-T Driver + +usb:v13D3p3237* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT 704J - dual DVB-T Driver + +usb:v13D3p3239* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT704D - DVBT/NTSC/PAL Driver(Without HID) + +usb:v13D3p3240* + ID_MODEL_FROM_DATABASE=DTV-DVB UDXTTM6010 - A/D Driver(Without HID) + +usb:v13D3p3241* + ID_MODEL_FROM_DATABASE=DTV-DVB UDXTTM6010 - A/D Driver(Without HID) + +usb:v13D3p3242* + ID_MODEL_FROM_DATABASE=DTV-DVB UDAT7240LP - ATSC/NTSC/PAL Driver(Without HID) + +usb:v13D3p3243* + ID_MODEL_FROM_DATABASE=DTV-DVB UDXTTM6010 - A/D Driver(Without HID) + +usb:v13D3p3244* + ID_MODEL_FROM_DATABASE=DTV-DVB UDTT 7047Z-USB 2.0 DVB-T Driver + +usb:v13D3p3247* + ID_MODEL_FROM_DATABASE=802.11 n/g/b Wireless LAN Adapter + +usb:v13D3p3249* + ID_MODEL_FROM_DATABASE=Internal Bluetooth + +usb:v13D3p3262* + ID_MODEL_FROM_DATABASE=802.11 n/g/b Wireless LAN USB Adapter + +usb:v13D3p3273* + ID_MODEL_FROM_DATABASE=802.11 n/g/b Wireless LAN USB Mini-Card + +usb:v13D3p3274* + ID_MODEL_FROM_DATABASE=DVB-T Dongle [RTL2832U] + +usb:v13D3p3282* + ID_MODEL_FROM_DATABASE=DVB-T + GPS Minicard [RTL2832U] + +usb:v13D3p3284* + ID_MODEL_FROM_DATABASE=Wireless LAN USB Mini-Card + +usb:v13D3p3304* + ID_MODEL_FROM_DATABASE=Asus Integrated Bluetooth module [AR3011] + +usb:v13D3p3306* + ID_MODEL_FROM_DATABASE=Mediao 802.11n WLAN [Realtek RTL8191SU] + +usb:v13D3p3315* + ID_MODEL_FROM_DATABASE=Bluetooth module + +usb:v13D3p3362* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth 4.0 Adapter + +usb:v13D3p3375* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth 4.0 Adapter + +usb:v13D3p3392* + ID_MODEL_FROM_DATABASE=Azurewave 43228+20702 + +usb:v13D3p3394* + ID_MODEL_FROM_DATABASE=Bluetooth + +usb:v13D3p3474* + ID_MODEL_FROM_DATABASE=Atheros AR3012 Bluetooth + +usb:v13D3p5070* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v13D3p5111* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v13D3p5115* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v13D3p5116* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v13D3p5122* + ID_MODEL_FROM_DATABASE=2M Integrated Webcam + +usb:v13D3p5126* + ID_MODEL_FROM_DATABASE=PC Cam + +usb:v13D3p5130* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v13D3p5702* + ID_MODEL_FROM_DATABASE=UVC VGA Webcam + +usb:v13D3p5710* + ID_MODEL_FROM_DATABASE=UVC VGA Webcam + +usb:v13D3p5716* + ID_MODEL_FROM_DATABASE=UVC VGA Webcam + +usb:v13D3p7020* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7020BDA DVB-S Box(DVBS for MCE2005) + +usb:v13D3p7022* + ID_MODEL_FROM_DATABASE=DTV-DVB UDST7022BDA DVB-S Box(Without HID) + +usb:v13D7* + ID_VENDOR_FROM_DATABASE=Guidance Software, Inc. + +usb:v13D7p0001* + ID_MODEL_FROM_DATABASE=T5 PATA forensic bridge + +usb:v13DC* + ID_VENDOR_FROM_DATABASE=ALEREON, INC. + +usb:v13DD* + ID_VENDOR_FROM_DATABASE=i.Tech Dynamic Limited + +usb:v13E1* + ID_VENDOR_FROM_DATABASE=Kaibo Wire & Cable (Shenzhen) Co., Ltd. + +usb:v13E5* + ID_VENDOR_FROM_DATABASE=Rane + +usb:v13E5p0001* + ID_MODEL_FROM_DATABASE=SL-1 + +usb:v13E5p0003* + ID_MODEL_FROM_DATABASE=TTM 57SL + +usb:v13E6* + ID_VENDOR_FROM_DATABASE=TechnoScope Co., Ltd. + +usb:v13EA* + ID_VENDOR_FROM_DATABASE=Hengstler + +usb:v13EAp0001* + ID_MODEL_FROM_DATABASE=C-56 Thermal Printer + +usb:v13EC* + ID_VENDOR_FROM_DATABASE=Zydacron + +usb:v13ECp0006* + ID_MODEL_FROM_DATABASE=HID Remote Control + +usb:v13EE* + ID_VENDOR_FROM_DATABASE=MosArt + +usb:v13EEp0001* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v13EEp0003* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v13FD* + ID_VENDOR_FROM_DATABASE=Initio Corporation + +usb:v13FDp0840* + ID_MODEL_FROM_DATABASE=INIC-1618L SATA + +usb:v13FDp0841* + ID_MODEL_FROM_DATABASE=Samsung SE-T084M DVD-RW + +usb:v13FDp1040* + ID_MODEL_FROM_DATABASE=INIC-1511L PATA Bridge + +usb:v13FDp1340* + ID_MODEL_FROM_DATABASE=Hi-Speed USB to SATA Bridge + +usb:v13FDp160F* + ID_MODEL_FROM_DATABASE=RocketFish SATA Bridge [INIC-1611] + +usb:v13FDp1640* + ID_MODEL_FROM_DATABASE=INIC-1610L SATA Bridge + +usb:v13FDp1669* + ID_MODEL_FROM_DATABASE=INIC-1609PN + +usb:v13FDp1840* + ID_MODEL_FROM_DATABASE=INIC-1608 SATA bridge + +usb:v13FDp1E40* + ID_MODEL_FROM_DATABASE=INIC-1610P SATA bridge + +usb:v13FE* + ID_VENDOR_FROM_DATABASE=Kingston Technology Company Inc. + +usb:v13FEp1A00* + ID_MODEL_FROM_DATABASE=512MB/1GB Flash Drive + +usb:v13FEp1A23* + ID_MODEL_FROM_DATABASE=512MB Flash Drive + +usb:v13FEp1D00* + ID_MODEL_FROM_DATABASE=DataTraveler 2.0 1GB/4GB Flash Drive / Patriot Xporter 4GB Flash Drive + +usb:v13FEp1E00* + ID_MODEL_FROM_DATABASE=Flash Drive 2 GB [ICIDU 2 GB] + +usb:v13FEp1E50* + ID_MODEL_FROM_DATABASE=U3 Smart Drive + +usb:v13FEp1F00* + ID_MODEL_FROM_DATABASE=Kingston DataTraveler / Patriot Xporter + +usb:v13FEp1F23* + ID_MODEL_FROM_DATABASE=PS2232 flash drive controller + +usb:v13FEp2240* + ID_MODEL_FROM_DATABASE=microSD card reader + +usb:v13FEp3100* + ID_MODEL_FROM_DATABASE=2/4 GB stick + +usb:v13FEp3123* + ID_MODEL_FROM_DATABASE=Verbatim STORE N GO 4GB + +usb:v13FEp3600* + ID_MODEL_FROM_DATABASE=flash drive (4GB, EMTEC) + +usb:v13FEp3800* + ID_MODEL_FROM_DATABASE=Rage XT Flash Drive + +usb:v13FEp3E00* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v13FEp4100* + ID_MODEL_FROM_DATABASE=Flash drive + +usb:v13FEp5000* + ID_MODEL_FROM_DATABASE=USB flash drive (32 GB SHARKOON Accelerate) + +usb:v13FEp5100* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v1400* + ID_VENDOR_FROM_DATABASE=Axxion Group Corp. + +usb:v1402* + ID_VENDOR_FROM_DATABASE=Bowe Bell & Howell + +usb:v1403* + ID_VENDOR_FROM_DATABASE=Sitronix + +usb:v1403p0001* + ID_MODEL_FROM_DATABASE=Digital Photo Frame + +usb:v1409* + ID_VENDOR_FROM_DATABASE=IDS Imaging Development Systems GmbH + +usb:v1409p1000* + ID_MODEL_FROM_DATABASE=generic (firmware not loaded yet) + +usb:v1409p1485* + ID_MODEL_FROM_DATABASE=uEye UI1485 + +usb:v140E* + ID_VENDOR_FROM_DATABASE=Telechips, Inc. + +usb:v140EpB011* + ID_MODEL_FROM_DATABASE=TCC780X-based player (USB Boot mode) + +usb:v140EpB021* + ID_MODEL_FROM_DATABASE=TCC77X-based players (USB Boot mode) + +usb:v1410* + ID_VENDOR_FROM_DATABASE=Novatel Wireless + +usb:v1410p1110* + ID_MODEL_FROM_DATABASE=Merlin S620 + +usb:v1410p1120* + ID_MODEL_FROM_DATABASE=Merlin EX720 + +usb:v1410p1130* + ID_MODEL_FROM_DATABASE=Merlin S720 + +usb:v1410p1400* + ID_MODEL_FROM_DATABASE=Merlin U730/U740 (Vodafone) + +usb:v1410p1410* + ID_MODEL_FROM_DATABASE=Merlin U740 (non-Vodafone) + +usb:v1410p1430* + ID_MODEL_FROM_DATABASE=Merlin XU870 + +usb:v1410p1450* + ID_MODEL_FROM_DATABASE=Merlin X950D + +usb:v1410p2110* + ID_MODEL_FROM_DATABASE=Ovation U720/MCD3000 + +usb:v1410p2410* + ID_MODEL_FROM_DATABASE=Expedite EU740 + +usb:v1410p2420* + ID_MODEL_FROM_DATABASE=Expedite EU850D/EU860D/EU870D + +usb:v1410p4100* + ID_MODEL_FROM_DATABASE=U727 + +usb:v1410p4400* + ID_MODEL_FROM_DATABASE=Ovation MC930D/MC950D + +usb:v1410p9010* + ID_MODEL_FROM_DATABASE=Expedite E362 + +usb:v1410pA001* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v1410pA008* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v1410pB001* + ID_MODEL_FROM_DATABASE=Ovation MC551 + +usb:v1415* + ID_VENDOR_FROM_DATABASE=Nam Tai E&E Products Ltd. or OmniVision Technologies, Inc. + +usb:v1415p0000* + ID_MODEL_FROM_DATABASE=Sony SingStar USBMIC + +usb:v1415p0020* + ID_MODEL_FROM_DATABASE=Sony Wireless SingStar + +usb:v1415p2000* + ID_MODEL_FROM_DATABASE=Sony Playstation Eye + +usb:v1419* + ID_VENDOR_FROM_DATABASE=ABILITY ENTERPRISE CO., LTD. + +usb:v1421* + ID_VENDOR_FROM_DATABASE=Sensor Technology + +usb:v1421p0605* + ID_MODEL_FROM_DATABASE=Sentech Camera + +usb:v1429* + ID_VENDOR_FROM_DATABASE=Vega Technologies Industrial (Austria) Co. + +usb:v142A* + ID_VENDOR_FROM_DATABASE=Thales E-Transactions + +usb:v142Ap0003* + ID_MODEL_FROM_DATABASE=Artema Hybrid + +usb:v142Ap0005* + ID_MODEL_FROM_DATABASE=Artema Modular + +usb:v142Ap0043* + ID_MODEL_FROM_DATABASE=medCompact + +usb:v142B* + ID_VENDOR_FROM_DATABASE=Arbiter Systems, Inc. + +usb:v142Bp03A5* + ID_MODEL_FROM_DATABASE=933A Portable Power Sentinel + +usb:v1430* + ID_VENDOR_FROM_DATABASE=RedOctane + +usb:v1430p0150* + ID_MODEL_FROM_DATABASE=wireless receiver for skylanders wii + +usb:v1430p4734* + ID_MODEL_FROM_DATABASE=Guitar Hero4 hub + +usb:v1430p474B* + ID_MODEL_FROM_DATABASE=Guitar Hero MIDI interface + +usb:v1431* + ID_VENDOR_FROM_DATABASE=Pertech Resources, Inc. + +usb:v1435* + ID_VENDOR_FROM_DATABASE=Wistron NeWeb + +usb:v1435p0427* + ID_MODEL_FROM_DATABASE=UR054g 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v1435p0711* + ID_MODEL_FROM_DATABASE=UR055G 802.11bg + +usb:v1435p0804* + ID_MODEL_FROM_DATABASE=AR9170+AR9104 802.11abgn Wireless Adapter + +usb:v1435p0826* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v1435p0827* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v1435p0828* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v1435p0829* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v1436* + ID_VENDOR_FROM_DATABASE=Denali Software, Inc. + +usb:v143C* + ID_VENDOR_FROM_DATABASE=Altek Corporation + +usb:v1443* + ID_VENDOR_FROM_DATABASE=Digilent + +usb:v1443p0007* + ID_MODEL_FROM_DATABASE=Development board JTAG + +usb:v1446* + ID_VENDOR_FROM_DATABASE=X.J.GROUP + +usb:v1446p6A73* + ID_MODEL_FROM_DATABASE=Stamps.com Model 510 5LB Scale + +usb:v1446p6A78* + ID_MODEL_FROM_DATABASE=DYMO Endicia 75lb Digital Scale + +usb:v1453* + ID_VENDOR_FROM_DATABASE=Radio Shack + +usb:v1453p4026* + ID_MODEL_FROM_DATABASE=26-183 Serial Cable + +usb:v1456* + ID_VENDOR_FROM_DATABASE=Extending Wire & Cable Co., Ltd. + +usb:v1457* + ID_VENDOR_FROM_DATABASE=First International Computer, Inc. + +usb:v1457p5117* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 kernel usbnet (g_ether, CDC Ethernet) mode + +usb:v1457p5118* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 Debug board (V2+) + +usb:v1457p5119* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 u-boot cdc_acm serial port + +usb:v1457p511A* + ID_MODEL_FROM_DATABASE=HXD8 u-boot usbtty CDC ACM Mode + +usb:v1457p511B* + ID_MODEL_FROM_DATABASE=SMDK2440 u-boot usbtty CDC ACM mode + +usb:v1457p511C* + ID_MODEL_FROM_DATABASE=SMDK2443 u-boot usbtty CDC ACM mode + +usb:v1457p511D* + ID_MODEL_FROM_DATABASE=QT2410 u-boot usbtty CDC ACM mode + +usb:v1457p5120* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 u-boot usbtty generic serial + +usb:v1457p5121* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 kernel mass storage (g_storage) mode + +usb:v1457p5122* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 / Neo Freerunner kernel cdc_ether USB network + +usb:v1457p5123* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 internal USB CSR4 module + +usb:v1457p5124* + ID_MODEL_FROM_DATABASE=OpenMoko Neo1973 Bluetooth Device ID service + +usb:v145F* + ID_VENDOR_FROM_DATABASE=Trust + +usb:v145Fp0106* + ID_MODEL_FROM_DATABASE=Trust K56 V92 USB Modem + +usb:v145Fp013D* + ID_MODEL_FROM_DATABASE=PC Camera (SN9C201 + OV7660) + +usb:v145Fp013F* + ID_MODEL_FROM_DATABASE=Megapixel Auto Focus Webcam + +usb:v145Fp0142* + ID_MODEL_FROM_DATABASE=WB-6250X Webcam + +usb:v145Fp015A* + ID_MODEL_FROM_DATABASE=WB-8300X 2MP Webcam + +usb:v145Fp0161* + ID_MODEL_FROM_DATABASE=15901 802.11bg Wireless Adapter [Realtek RTL8187L] + +usb:v145Fp0167* + ID_MODEL_FROM_DATABASE=Widescreen 3MP Webcam + +usb:v145Fp0176* + ID_MODEL_FROM_DATABASE=Isla Keyboard + +usb:v1460* + ID_VENDOR_FROM_DATABASE=Tatung Co. + +usb:v1460p9150* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1461* + ID_VENDOR_FROM_DATABASE=Staccato Communications + +usb:v1462* + ID_VENDOR_FROM_DATABASE=Micro Star International + +usb:v1462p5512* + ID_MODEL_FROM_DATABASE=MegaStick-1 Flash Stick + +usb:v1462p8807* + ID_MODEL_FROM_DATABASE=DIGIVOX mini III [af9015] + +usb:v1472* + ID_VENDOR_FROM_DATABASE=Huawei-3Com + +usb:v1472p0007* + ID_MODEL_FROM_DATABASE=Aolynk WUB300g [ZyDAS ZD1211] + +usb:v1472p0009* + ID_MODEL_FROM_DATABASE=Aolynk WUB320g + +usb:v147A* + ID_VENDOR_FROM_DATABASE=Formosa Industrial Computing, Inc. + +usb:v147ApE015* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE016* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE017* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE018* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE02C* + ID_MODEL_FROM_DATABASE=Infrared Receiver + +usb:v147ApE03A* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE03C* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v147ApE03D* + ID_MODEL_FROM_DATABASE=2 Channel Audio + +usb:v147ApE03E* + ID_MODEL_FROM_DATABASE=Infrared Receiver [IR605A/Q] + +usb:v147E* + ID_VENDOR_FROM_DATABASE=Upek + +usb:v147Ep1000* + ID_MODEL_FROM_DATABASE=Biometric Touchchip/Touchstrip Fingerprint Sensor + +usb:v147Ep1001* + ID_MODEL_FROM_DATABASE=TCS5B Fingerprint sensor + +usb:v147Ep1002* + ID_MODEL_FROM_DATABASE=Biometric Touchchip/Touchstrip Fingerprint Sensor + +usb:v147Ep2016* + ID_MODEL_FROM_DATABASE=Biometric Touchchip/Touchstrip Fingerprint Sensor + +usb:v147Ep2020* + ID_MODEL_FROM_DATABASE=TouchChip Fingerprint Coprocessor (WBF advanced mode) + +usb:v147Ep3000* + ID_MODEL_FROM_DATABASE=TCS1C EIM/Cypress Fingerprint sensor + +usb:v147Ep3001* + ID_MODEL_FROM_DATABASE=TCS1C EIM/STM32 Fingerprint sensor + +usb:v147F* + ID_VENDOR_FROM_DATABASE=Hama GmbH & Co., KG + +usb:v1482* + ID_VENDOR_FROM_DATABASE=Vaillant + +usb:v1482p1005* + ID_MODEL_FROM_DATABASE=VRD PC-Interface + +usb:v1484* + ID_VENDOR_FROM_DATABASE=Elsa AG [hex] + +usb:v1484p1746* + ID_MODEL_FROM_DATABASE=Ecomo 19H99 Monitor + +usb:v1484p7616* + ID_MODEL_FROM_DATABASE=Elsa Hub + +usb:v1485* + ID_VENDOR_FROM_DATABASE=Silicom + +usb:v1485p0001* + ID_MODEL_FROM_DATABASE=U2E + +usb:v1485p0002* + ID_MODEL_FROM_DATABASE=Psion Gold Port Ethernet + +usb:v1487* + ID_VENDOR_FROM_DATABASE=DSP Group, Ltd. + +usb:v148E* + ID_VENDOR_FROM_DATABASE=EVATRONIX SA + +usb:v148F* + ID_VENDOR_FROM_DATABASE=Ralink Technology, Corp. + +usb:v148Fp1000* + ID_MODEL_FROM_DATABASE=Motorola BC4 Bluetooth 3.0+HS Adapter + +usb:v148Fp1706* + ID_MODEL_FROM_DATABASE=RT2500USB Wireless Adapter + +usb:v148Fp2070* + ID_MODEL_FROM_DATABASE=RT2070 Wireless Adapter + +usb:v148Fp2570* + ID_MODEL_FROM_DATABASE=RT2570 Wireless Adapter + +usb:v148Fp2573* + ID_MODEL_FROM_DATABASE=RT2501/RT2573 Wireless Adapter + +usb:v148Fp2671* + ID_MODEL_FROM_DATABASE=RT2601/RT2671 Wireless Adapter + +usb:v148Fp2770* + ID_MODEL_FROM_DATABASE=RT2770 Wireless Adapter + +usb:v148Fp2870* + ID_MODEL_FROM_DATABASE=RT2870 Wireless Adapter + +usb:v148Fp3070* + ID_MODEL_FROM_DATABASE=RT2870/RT3070 Wireless Adapter + +usb:v148Fp3071* + ID_MODEL_FROM_DATABASE=RT3071 Wireless Adapter + +usb:v148Fp3072* + ID_MODEL_FROM_DATABASE=RT3072 Wireless Adapter + +usb:v148Fp3370* + ID_MODEL_FROM_DATABASE=RT3370 Wireless Adapter + +usb:v148Fp3572* + ID_MODEL_FROM_DATABASE=RT3572 Wireless Adapter + +usb:v148Fp3573* + ID_MODEL_FROM_DATABASE=RT3573 Wireless Adapter + +usb:v148Fp5370* + ID_MODEL_FROM_DATABASE=RT5370 Wireless Adapter + +usb:v148Fp5372* + ID_MODEL_FROM_DATABASE=RT5372 Wireless Adapter + +usb:v148Fp5572* + ID_MODEL_FROM_DATABASE=RT5572 Wireless Adapter + +usb:v148Fp7601* + ID_MODEL_FROM_DATABASE=MT7601U Wireless Adapter + +usb:v148Fp760B* + ID_MODEL_FROM_DATABASE=MT7601U Wireless Adapter + +usb:v148Fp9020* + ID_MODEL_FROM_DATABASE=RT2500USB Wireless Adapter + +usb:v148Fp9021* + ID_MODEL_FROM_DATABASE=RT2501USB Wireless Adapter + +usb:v1491* + ID_VENDOR_FROM_DATABASE=Futronic Technology Co. Ltd. + +usb:v1491p0020* + ID_MODEL_FROM_DATABASE=FS81 Fingerprint Scanner Module + +usb:v1493* + ID_VENDOR_FROM_DATABASE=Suunto + +usb:v1493p0010* + ID_MODEL_FROM_DATABASE=Bluebird [Ambit] + +usb:v1493p0019* + ID_MODEL_FROM_DATABASE=Duck [Ambit2] + +usb:v1493p001A* + ID_MODEL_FROM_DATABASE=Colibri [Ambit2 S] + +usb:v1493p001B* + ID_MODEL_FROM_DATABASE=Emu [Ambit3 Peak] + +usb:v1493p001C* + ID_MODEL_FROM_DATABASE=Finch [Ambit3 Sport] + +usb:v1493p001D* + ID_MODEL_FROM_DATABASE=Greentit [Ambit2 R] + +usb:v1497* + ID_VENDOR_FROM_DATABASE=Panstrong Company Ltd. + +usb:v1498* + ID_VENDOR_FROM_DATABASE=Microtek International Inc. + +usb:v1498pA090* + ID_MODEL_FROM_DATABASE=DVB-T Tuner + +usb:v149A* + ID_VENDOR_FROM_DATABASE=Imagination Technologies + +usb:v149Ap2107* + ID_MODEL_FROM_DATABASE=DBX1 DSP core + +usb:v14AA* + ID_VENDOR_FROM_DATABASE=WideView Technology Inc. + +usb:v14AAp0001* + ID_MODEL_FROM_DATABASE=Avermedia AverTV DVBT USB1.1 (cold) + +usb:v14AAp0002* + ID_MODEL_FROM_DATABASE=Avermedia AverTV DVBT USB1.1 (warm) + +usb:v14AAp0201* + ID_MODEL_FROM_DATABASE=AVermedia/Yakumo/Hama/Typhoon DVB-T USB2.0 (cold) + +usb:v14AAp0221* + ID_MODEL_FROM_DATABASE=WT-220U DVB-T dongle + +usb:v14AAp022B* + ID_MODEL_FROM_DATABASE=WT-220U DVB-T dongle + +usb:v14AAp0301* + ID_MODEL_FROM_DATABASE=AVermedia/Yakumo/Hama/Typhoon DVB-T USB2.0 (warm) + +usb:v14AD* + ID_VENDOR_FROM_DATABASE=CTK Corporation + +usb:v14AE* + ID_VENDOR_FROM_DATABASE=Printronix Inc. + +usb:v14AF* + ID_VENDOR_FROM_DATABASE=ATP Electronics Inc. + +usb:v14B0* + ID_VENDOR_FROM_DATABASE=StarTech.com Ltd. + +usb:v14B2* + ID_VENDOR_FROM_DATABASE=Ralink Technology, Corp. + +usb:v14B2p3A93* + ID_MODEL_FROM_DATABASE=Topcom 802.11bg Wireless Adapter [Atheros AR5523] + +usb:v14B2p3A95* + ID_MODEL_FROM_DATABASE=Toshiba WUS-G06G-JT 802.11bg Wireless Adapter [Atheros AR5523] + +usb:v14B2p3A98* + ID_MODEL_FROM_DATABASE=Airlink101 AWLL4130 802.11bg Wireless Adapter [Atheros AR5523] + +usb:v14B2p3C02* + ID_MODEL_FROM_DATABASE=Conceptronic C54RU v2 802.11bg Wireless Adapter [Ralink RT2571] + +usb:v14B2p3C05* + ID_MODEL_FROM_DATABASE=rt2570 802.11g WLAN + +usb:v14B2p3C06* + ID_MODEL_FROM_DATABASE=Conceptronic C300RU v1 802.11bgn Wireless Adapter [Ralink RT2870] + +usb:v14B2p3C07* + ID_MODEL_FROM_DATABASE=802.11n adapter + +usb:v14B2p3C09* + ID_MODEL_FROM_DATABASE=802.11n adapter + +usb:v14B2p3C22* + ID_MODEL_FROM_DATABASE=Conceptronic C54RU v3 802.11bg Wireless Adapter [Ralink RT2571W] + +usb:v14B2p3C23* + ID_MODEL_FROM_DATABASE=Airlink101 AWLL6080 802.11bgn Wireless Adapter [Ralink RT2870] + +usb:v14B2p3C24* + ID_MODEL_FROM_DATABASE=NEC NP01LM 802.11abg Wireless Adapter [Ralink RT2571W] + +usb:v14B2p3C25* + ID_MODEL_FROM_DATABASE=DrayTek Vigor N61 802.11bgn Wireless Adapter [Ralink RT2870] + +usb:v14B2p3C27* + ID_MODEL_FROM_DATABASE=Airlink101 AWLL6070 802.11bgn Wireless Adapter [Ralink RT2770] + +usb:v14B2p3C28* + ID_MODEL_FROM_DATABASE=Conceptronic C300RU v2 802.11bgn Wireless Adapter [Ralink RT2770] + +usb:v14B2p3C2B* + ID_MODEL_FROM_DATABASE=NEC NP02LM 802.11bgn Wireless Adapter [Ralink RT3072] + +usb:v14B2p3C2C* + ID_MODEL_FROM_DATABASE=Keebox W150NU 802.11bgn Wireless Adapter [Ralink RT3070] + +usb:v14C0* + ID_VENDOR_FROM_DATABASE=Rockwell Automation, Inc. + +usb:v14C2* + ID_VENDOR_FROM_DATABASE=Gemlight Computer, Ltd + +usb:v14C2p0250* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 + +usb:v14C2p0350* + ID_MODEL_FROM_DATABASE=Storage Adapter V2 + +usb:v14C8* + ID_VENDOR_FROM_DATABASE=Zytronic + +usb:v14CD* + ID_VENDOR_FROM_DATABASE=Super Top + +usb:v14CDp1212* + ID_MODEL_FROM_DATABASE=microSD card reader (SY-T18) + +usb:v14CDp121C* + ID_MODEL_FROM_DATABASE=microSD card reader + +usb:v14CDp121F* + ID_MODEL_FROM_DATABASE=microSD CardReader SY-T18 + +usb:v14CDp123A* + ID_MODEL_FROM_DATABASE=SD/MMC/RS-MMC Card Reader + +usb:v14CDp125C* + ID_MODEL_FROM_DATABASE=SD card reader + +usb:v14CDp127B* + ID_MODEL_FROM_DATABASE=SDXC Reader + +usb:v14CDp6116* + ID_MODEL_FROM_DATABASE=M6116 SATA Bridge + +usb:v14CDp6600* + ID_MODEL_FROM_DATABASE=M110E PATA bridge + +usb:v14CDp6700* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v14CDp6900* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v14CDp8123* + ID_MODEL_FROM_DATABASE=SD MMC Reader + +usb:v14CDp8125* + ID_MODEL_FROM_DATABASE=SD MMC Reader + +usb:v14D8* + ID_VENDOR_FROM_DATABASE=JAMER INDUSTRIES CO., LTD. + +usb:v14DD* + ID_VENDOR_FROM_DATABASE=Raritan Computer, Inc. + +usb:v14DDp1007* + ID_MODEL_FROM_DATABASE=D2CIM-VUSB KVM connector + +usb:v14E0* + ID_VENDOR_FROM_DATABASE=WiNRADiO Communications + +usb:v14E0p0501* + ID_MODEL_FROM_DATABASE=WR-G528e 'CHEETAH' + +usb:v14E1* + ID_VENDOR_FROM_DATABASE=Dialogue Technology Corp. + +usb:v14E1p5000* + ID_MODEL_FROM_DATABASE=PenMount 5000 Touch Controller + +usb:v14E5* + ID_VENDOR_FROM_DATABASE=SAIN Information & Communications Co., Ltd. + +usb:v14EA* + ID_VENDOR_FROM_DATABASE=Planex Communications + +usb:v14EApAB10* + ID_MODEL_FROM_DATABASE=GW-US54GZ + +usb:v14EApAB11* + ID_MODEL_FROM_DATABASE=GU-1000T + +usb:v14EApAB13* + ID_MODEL_FROM_DATABASE=GW-US54Mini 802.11bg + +usb:v14ED* + ID_VENDOR_FROM_DATABASE=Shure Inc. + +usb:v14EDp29B6* + ID_MODEL_FROM_DATABASE=X2u Adapter + +usb:v14F7* + ID_VENDOR_FROM_DATABASE=TechniSat Digital GmbH + +usb:v14F7p0001* + ID_MODEL_FROM_DATABASE=SkyStar 2 HD CI + +usb:v14F7p0002* + ID_MODEL_FROM_DATABASE=SkyStar 2 HD CI + +usb:v14F7p0003* + ID_MODEL_FROM_DATABASE=CableStar Combo HD CI + +usb:v14F7p0004* + ID_MODEL_FROM_DATABASE=AirStar TeleStick 2 + +usb:v14F7p0500* + ID_MODEL_FROM_DATABASE=DVB-PC TV Star HD + +usb:v1500* + ID_VENDOR_FROM_DATABASE=Ellisys + +usb:v1501* + ID_VENDOR_FROM_DATABASE=Pine-Tum Enterprise Co., Ltd. + +usb:v1509* + ID_VENDOR_FROM_DATABASE=First International Computer, Inc. + +usb:v1509p0A01* + ID_MODEL_FROM_DATABASE=LI-3100 Area Meter + +usb:v1509p0A02* + ID_MODEL_FROM_DATABASE=LI-7000 CO2/H2O Gas Analyzer + +usb:v1509p0A03* + ID_MODEL_FROM_DATABASE=C-DiGit Blot Scanner + +usb:v1509p9242* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1513* + ID_VENDOR_FROM_DATABASE=medMobile + +usb:v1513p0444* + ID_MODEL_FROM_DATABASE=medMobile + +usb:v1514* + ID_VENDOR_FROM_DATABASE=Actel + +usb:v1514p2003* + ID_MODEL_FROM_DATABASE=FlashPro3 Programmer + +usb:v1514p2004* + ID_MODEL_FROM_DATABASE=FlashPro3 Programmer + +usb:v1514p2005* + ID_MODEL_FROM_DATABASE=FlashPro3 Programmer + +usb:v1516* + ID_VENDOR_FROM_DATABASE=CompUSA + +usb:v1516p1603* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v1516p8628* + ID_MODEL_FROM_DATABASE=Pen Drive + +usb:v1518* + ID_VENDOR_FROM_DATABASE=Cheshire Engineering Corp. + +usb:v1518p0001* + ID_MODEL_FROM_DATABASE=HDReye High Dynamic Range Camera + +usb:v1518p0002* + ID_MODEL_FROM_DATABASE=HDReye (before firmware loads) + +usb:v1519* + ID_VENDOR_FROM_DATABASE=Comneon + +usb:v1519p0020* + ID_MODEL_FROM_DATABASE=HSIC Device + +usb:v1520* + ID_VENDOR_FROM_DATABASE=Bitwire Corp. + +usb:v1524* + ID_VENDOR_FROM_DATABASE=ENE Technology Inc + +usb:v1524p6680* + ID_MODEL_FROM_DATABASE=UTS 6680 + +usb:v1527* + ID_VENDOR_FROM_DATABASE=Silicon Portals + +usb:v1527p0200* + ID_MODEL_FROM_DATABASE=YAP Phone (no firmware) + +usb:v1527p0201* + ID_MODEL_FROM_DATABASE=YAP Phone + +usb:v1529* + ID_VENDOR_FROM_DATABASE=UBIQUAM Co., Ltd. + +usb:v1529p3100* + ID_MODEL_FROM_DATABASE=CDMA 1xRTT USB Modem (U-100/105/200/300/520) + +usb:v152A* + ID_VENDOR_FROM_DATABASE=Thesycon Systemsoftware & Consulting GmbH + +usb:v152Ap8350* + ID_MODEL_FROM_DATABASE=NET Gmbh iCube Camera + +usb:v152Ap8400* + ID_MODEL_FROM_DATABASE=INI DVS128 + +usb:v152Ap840D* + ID_MODEL_FROM_DATABASE=INI DAViS + +usb:v152Ap841A* + ID_MODEL_FROM_DATABASE=INI DAViS FX3 + +usb:v152B* + ID_VENDOR_FROM_DATABASE=MIR Srl + +usb:v152Bp0001* + ID_MODEL_FROM_DATABASE=spirobank II + +usb:v152Bp0002* + ID_MODEL_FROM_DATABASE=spirolab III + +usb:v152Bp0003* + ID_MODEL_FROM_DATABASE=MiniSpir + +usb:v152Bp0004* + ID_MODEL_FROM_DATABASE=Oxi + +usb:v152Bp0005* + ID_MODEL_FROM_DATABASE=spiros II + +usb:v152Bp0006* + ID_MODEL_FROM_DATABASE=smiths spirobank II + +usb:v152Bp0007* + ID_MODEL_FROM_DATABASE=smiths spirobank G-USB + +usb:v152Bp0008* + ID_MODEL_FROM_DATABASE=smiths MiniSpir + +usb:v152Bp0009* + ID_MODEL_FROM_DATABASE=spirobank G-USB + +usb:v152Bp000A* + ID_MODEL_FROM_DATABASE=smiths Oxi + +usb:v152Bp000B* + ID_MODEL_FROM_DATABASE=smiths spirolab III + +usb:v152Bp000C* + ID_MODEL_FROM_DATABASE=chorus III + +usb:v152Bp000D* + ID_MODEL_FROM_DATABASE=spirolab III Bw + +usb:v152Bp000E* + ID_MODEL_FROM_DATABASE=spirolab III + +usb:v152Bp000F* + ID_MODEL_FROM_DATABASE=easySpiro + +usb:v152Bp0010* + ID_MODEL_FROM_DATABASE=Spirotel converter + +usb:v152Bp0011* + ID_MODEL_FROM_DATABASE=spirobank + +usb:v152Bp0012* + ID_MODEL_FROM_DATABASE=spiro3 Zimmer + +usb:v152Bp0013* + ID_MODEL_FROM_DATABASE=spirotel serial + +usb:v152Bp0014* + ID_MODEL_FROM_DATABASE=spirotel II + +usb:v152Bp0015* + ID_MODEL_FROM_DATABASE=spirodoc + +usb:v152D* + ID_VENDOR_FROM_DATABASE=JMicron Technology Corp. / JMicron USA Technology Corp. + +usb:v152Dp0539* + ID_MODEL_FROM_DATABASE=JMS539/567 SuperSpeed SATA II/III 3.0G/6.0G Bridge + +usb:v152Dp0567* + ID_MODEL_FROM_DATABASE=JMS567 SATA 6Gb/s bridge + +usb:v152Dp0770* + ID_MODEL_FROM_DATABASE=Alienware Integrated Webcam + +usb:v152Dp2329* + ID_MODEL_FROM_DATABASE=JM20329 SATA Bridge + +usb:v152Dp2335* + ID_MODEL_FROM_DATABASE=ATA/ATAPI Bridge + +usb:v152Dp2336* + ID_MODEL_FROM_DATABASE=Hard Disk Drive + +usb:v152Dp2337* + ID_MODEL_FROM_DATABASE=ATA/ATAPI Bridge + +usb:v152Dp2338* + ID_MODEL_FROM_DATABASE=JM20337 Hi-Speed USB to SATA & PATA Combo Bridge + +usb:v152Dp2339* + ID_MODEL_FROM_DATABASE=JM20339 SATA Bridge + +usb:v152Dp2352* + ID_MODEL_FROM_DATABASE=ATA/ATAPI Bridge + +usb:v152Dp2509* + ID_MODEL_FROM_DATABASE=JMS539 SuperSpeed SATA II 3.0G Bridge + +usb:v152Dp2551* + ID_MODEL_FROM_DATABASE=JMS551 SATA 3Gb/s bridge + +usb:v152Dp2566* + ID_MODEL_FROM_DATABASE=JMS566 SATA 3Gb/s bridge + +usb:v152Dp2590* + ID_MODEL_FROM_DATABASE=Seatay ATA/ATAPI Bridge + +usb:v152Dp3562* + ID_MODEL_FROM_DATABASE=JMS567 SATA 6Gb/s bridge + +usb:v152Dp3569* + ID_MODEL_FROM_DATABASE=JMS566 SATA 3Gb/s bridge + +usb:v152E* + ID_VENDOR_FROM_DATABASE=LG (HLDS) + +usb:v152Ep2507* + ID_MODEL_FROM_DATABASE=PL-2507 IDE Controller + +usb:v152EpE001* + ID_MODEL_FROM_DATABASE=GSA-5120D DVD-RW + +usb:v1532* + ID_VENDOR_FROM_DATABASE=Razer USA, Ltd + +usb:v1532p0001* + ID_MODEL_FROM_DATABASE=RZ01-020300 Optical Mouse [Diamondback] + +usb:v1532p0003* + ID_MODEL_FROM_DATABASE=Krait Mouse + +usb:v1532p0007* + ID_MODEL_FROM_DATABASE=DeathAdder Mouse + +usb:v1532p0013* + ID_MODEL_FROM_DATABASE=Orochi mouse + +usb:v1532p0015* + ID_MODEL_FROM_DATABASE=Naga Mouse + +usb:v1532p0016* + ID_MODEL_FROM_DATABASE=DeathAdder Mouse + +usb:v1532p0017* + ID_MODEL_FROM_DATABASE=RZ01-0035 Laser Gaming Mouse [Imperator] + +usb:v1532p001C* + ID_MODEL_FROM_DATABASE=RZ01-0036 Optical Gaming Mouse [Abyssus] + +usb:v1532p0024* + ID_MODEL_FROM_DATABASE=Razer Mamba + +usb:v1532p002E* + ID_MODEL_FROM_DATABASE=RZ01-0058 Gaming Mouse [Naga] + +usb:v1532p0036* + ID_MODEL_FROM_DATABASE=RZ01-0075, Gaming Mouse [Naga Hex] + +usb:v1532p0101* + ID_MODEL_FROM_DATABASE=Copperhead Mouse + +usb:v1532p0102* + ID_MODEL_FROM_DATABASE=Tarantula Keyboard + +usb:v1532p0109* + ID_MODEL_FROM_DATABASE=Lycosa Keyboard + +usb:v1532p0113* + ID_MODEL_FROM_DATABASE=RZ07-0074 Gaming Keypad [Orbweaver] + +usb:v1532p0300* + ID_MODEL_FROM_DATABASE=RZ06-0063 Motion Sensing Controllers [Hydra] + +usb:v153B* + ID_VENDOR_FROM_DATABASE=TerraTec Electronic GmbH + +usb:v153Bp1181* + ID_MODEL_FROM_DATABASE=Cinergy S2 PCIe Dual Port 1 + +usb:v153Bp1182* + ID_MODEL_FROM_DATABASE=Cinergy S2 PCIe Dual Port 2 + +usb:v1546* + ID_VENDOR_FROM_DATABASE=U-Blox AG + +usb:v1546p01A5* + ID_MODEL_FROM_DATABASE=NL-402U + +usb:v1547* + ID_VENDOR_FROM_DATABASE=SG Intec Ltd & Co KG + +usb:v1547p1000* + ID_MODEL_FROM_DATABASE=SG-Lock[U2] + +usb:v154A* + ID_VENDOR_FROM_DATABASE=Celectronic GmbH + +usb:v154Ap8180* + ID_MODEL_FROM_DATABASE=CARD STAR/medic2 + +usb:v154B* + ID_VENDOR_FROM_DATABASE=PNY + +usb:v154Bp0010* + ID_MODEL_FROM_DATABASE=USB 2.0 Flash Drive + +usb:v154Bp0048* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v154Bp004D* + ID_MODEL_FROM_DATABASE=8 GB Flash Drive + +usb:v154Bp0053* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v154Bp0057* + ID_MODEL_FROM_DATABASE=32GB Micro Slide Attache Flash Drive + +usb:v154Bp005B* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v154Bp0062* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v154Bp007A* + ID_MODEL_FROM_DATABASE=Classic Attache Flash Drive + +usb:v154Bp6545* + ID_MODEL_FROM_DATABASE=FD Device + +usb:v154BpFA05* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v154D* + ID_VENDOR_FROM_DATABASE=ConnectCounty Holdings Berhad + +usb:v154E* + ID_VENDOR_FROM_DATABASE=D&M Holdings, Inc. (Denon/Marantz) + +usb:v154Ep3000* + ID_MODEL_FROM_DATABASE=Marantz RC9001 Remote Control + +usb:v154F* + ID_VENDOR_FROM_DATABASE=SNBC CO., Ltd + +usb:v1554* + ID_VENDOR_FROM_DATABASE=Prolink Microsystems Corp. + +usb:v1554p5010* + ID_MODEL_FROM_DATABASE=PV-D231U(RN)-F [PixelView PlayTV SBTVD Full-Seg] + +usb:v1557* + ID_VENDOR_FROM_DATABASE=OQO + +usb:v1557p0002* + ID_MODEL_FROM_DATABASE=model 01 WiFi interface + +usb:v1557p0003* + ID_MODEL_FROM_DATABASE=model 01 Bluetooth interface + +usb:v1557p0A80* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v1557p7720* + ID_MODEL_FROM_DATABASE=model 01+ Ethernet + +usb:v1557p8150* + ID_MODEL_FROM_DATABASE=model 01 Ethernet interface + +usb:v1568* + ID_VENDOR_FROM_DATABASE=Sunf Pu Technology Co., Ltd + +usb:v156F* + ID_VENDOR_FROM_DATABASE=Quantum Corporation + +usb:v1570* + ID_VENDOR_FROM_DATABASE=ALLTOP TECHNOLOGY CO., LTD. + +usb:v157B* + ID_VENDOR_FROM_DATABASE=Ketron SRL + +usb:v157E* + ID_VENDOR_FROM_DATABASE=TRENDnet + +usb:v157Ep3006* + ID_MODEL_FROM_DATABASE=TEW-444UB EU [TRENDnet] + +usb:v157Ep3007* + ID_MODEL_FROM_DATABASE=TEW-444UB EU (no firmware) + +usb:v157Ep300A* + ID_MODEL_FROM_DATABASE=TEW-429UB 802.11bg + +usb:v157Ep300B* + ID_MODEL_FROM_DATABASE=TEW-429UB 802.11bg + +usb:v157Ep300C* + ID_MODEL_FROM_DATABASE=TEW-429UF A1 802.11bg Wireless Adapter [ZyDAS ZD1211B] + +usb:v157Ep300D* + ID_MODEL_FROM_DATABASE=TEW-429UB C1 802.11bg + +usb:v157Ep300E* + ID_MODEL_FROM_DATABASE=SMC SMCWUSB-N 802.11bgn 2x2:2 Wireless Adapter [Ralink RT2870] + +usb:v157Ep3012* + ID_MODEL_FROM_DATABASE=TEW-604UB 802.11bg Wireless Adapter [Atheros AR5523] + +usb:v157Ep3013* + ID_MODEL_FROM_DATABASE=TEW-645UB 802.11bgn 1x2:2 Wireless Adapter [Ralink RT2770] + +usb:v157Ep3204* + ID_MODEL_FROM_DATABASE=Allnet ALL0298 v2 802.11bg + +usb:v157Ep3205* + ID_MODEL_FROM_DATABASE=Allnet ALL0283 [AR5523] + +usb:v157Ep3206* + ID_MODEL_FROM_DATABASE=Allnet ALL0283 [AR5523](no firmware) + +usb:v157Ep3207* + ID_MODEL_FROM_DATABASE=TEW-509UB A1 802.11abg Wireless Adapter [ZyDAS ZD1211] + +usb:v157Ep3208* + ID_MODEL_FROM_DATABASE=TEW-509UB 1.1R 802.11abg Wireless Adapter + +usb:v1582* + ID_VENDOR_FROM_DATABASE=Fiberline + +usb:v1582p6003* + ID_MODEL_FROM_DATABASE=WL-430U 802.11bg + +usb:v1587* + ID_VENDOR_FROM_DATABASE=SMA Technologie AG + +usb:v158D* + ID_VENDOR_FROM_DATABASE=Oakley Inc. + +usb:v158E* + ID_VENDOR_FROM_DATABASE=JDS Uniphase Corporation (JDSU) + +usb:v158Ep0820* + ID_MODEL_FROM_DATABASE=SmartPocket Class Device + +usb:v1598* + ID_VENDOR_FROM_DATABASE=Kunshan Guoji Electronics Co., Ltd. + +usb:v15A2* + ID_VENDOR_FROM_DATABASE=Freescale Semiconductor, Inc. + +usb:v15A2p0038* + ID_MODEL_FROM_DATABASE=9S08JS Bootloader + +usb:v15A2p003B* + ID_MODEL_FROM_DATABASE=USB2CAN Application for ColdFire DEMOJM board + +usb:v15A2p0042* + ID_MODEL_FROM_DATABASE=OSBDM - Debug Port + +usb:v15A2p004F* + ID_MODEL_FROM_DATABASE=i.MX28 SystemOnChip in RecoveryMode + +usb:v15A2p0052* + ID_MODEL_FROM_DATABASE=i.MX50 SystemOnChip in RecoveryMode + +usb:v15A2p0054* + ID_MODEL_FROM_DATABASE=i.MX 6Dual/6Quad SystemOnChip in RecoveryMode + +usb:v15A2p0061* + ID_MODEL_FROM_DATABASE=i.MX 6Solo/6DualLite SystemOnChip in RecoveryMode + +usb:v15A4* + ID_VENDOR_FROM_DATABASE=Afatech Technologies, Inc. + +usb:v15A4p1000* + ID_MODEL_FROM_DATABASE=AF9015/AF9035 DVB-T stick + +usb:v15A4p1001* + ID_MODEL_FROM_DATABASE=AF9015/AF9035 DVB-T stick + +usb:v15A4p1336* + ID_MODEL_FROM_DATABASE=SDHC/MicroSD/MMC/MS/M2/CF/XD Flash Card Reader + +usb:v15A4p9015* + ID_MODEL_FROM_DATABASE=AF9015 DVB-T USB2.0 stick + +usb:v15A4p9016* + ID_MODEL_FROM_DATABASE=AF9015 DVB-T USB2.0 stick + +usb:v15A8* + ID_VENDOR_FROM_DATABASE=Teams Power Limited + +usb:v15A9* + ID_VENDOR_FROM_DATABASE=Gemtek + +usb:v15A9p0002* + ID_MODEL_FROM_DATABASE=SparkLAN WL-682 802.11bg Wireless Adapter [Intersil ISL3887] + +usb:v15A9p0004* + ID_MODEL_FROM_DATABASE=WUBR-177G [Ralink RT2571W] + +usb:v15A9p0006* + ID_MODEL_FROM_DATABASE=Wireless 11n USB Adapter + +usb:v15A9p0010* + ID_MODEL_FROM_DATABASE=802.11n USB Wireless Card + +usb:v15A9p0012* + ID_MODEL_FROM_DATABASE=WUBR-208N 802.11abgn Wireless Adapter [Ralink RT2870] + +usb:v15A9p002D* + ID_MODEL_FROM_DATABASE=WLTUBA-107 [Yota 4G LTE] + +usb:v15AA* + ID_VENDOR_FROM_DATABASE=Gearway Electronics (Dong Guan) Co., Ltd. + +usb:v15AD* + ID_VENDOR_FROM_DATABASE=VMware Inc. + +usb:v15BA* + ID_VENDOR_FROM_DATABASE=Olimex Ltd. + +usb:v15BAp0003* + ID_MODEL_FROM_DATABASE=OpenOCD JTAG + +usb:v15BAp0004* + ID_MODEL_FROM_DATABASE=OpenOCD JTAG TINY + +usb:v15BAp002A* + ID_MODEL_FROM_DATABASE=ARM-USB-TINY-H JTAG interface + +usb:v15BAp002B* + ID_MODEL_FROM_DATABASE=ARM-USB-OCD-H JTAG+RS232 + +usb:v15C0* + ID_VENDOR_FROM_DATABASE=XL Imaging + +usb:v15C0p0001* + ID_MODEL_FROM_DATABASE=2M pixel Microscope Camera + +usb:v15C0p0002* + ID_MODEL_FROM_DATABASE=3M pixel Microscope Camera + +usb:v15C0p0003* + ID_MODEL_FROM_DATABASE=1.3M pixel Microscope Camera (mono) + +usb:v15C0p0004* + ID_MODEL_FROM_DATABASE=1.3M pixel Microscope Camera (colour) + +usb:v15C0p0005* + ID_MODEL_FROM_DATABASE=3M pixel Microscope Camera (Mk 2) + +usb:v15C0p0006* + ID_MODEL_FROM_DATABASE=2M pixel Microscope Camera (with capture button) + +usb:v15C0p0007* + ID_MODEL_FROM_DATABASE=3M pixel Microscope Camera (with capture button) + +usb:v15C0p0008* + ID_MODEL_FROM_DATABASE=1.3M pixel Microscope Camera (colour, with capture button) + +usb:v15C0p0009* + ID_MODEL_FROM_DATABASE=1.3M pixel Microscope Camera (colour, with capture button) + +usb:v15C0p000A* + ID_MODEL_FROM_DATABASE=2M pixel Microscope Camera (Mk 2) + +usb:v15C0p0010* + ID_MODEL_FROM_DATABASE=1.3M pixel "Tinycam" + +usb:v15C0p0101* + ID_MODEL_FROM_DATABASE=3M pixel Microscope Camera + +usb:v15C2* + ID_VENDOR_FROM_DATABASE=SoundGraph Inc. + +usb:v15C2p0036* + ID_MODEL_FROM_DATABASE=LC16M VFD Display/IR Receiver + +usb:v15C2p0038* + ID_MODEL_FROM_DATABASE=GD01 MX LCD Display/IR Receiver + +usb:v15C2p0042* + ID_MODEL_FROM_DATABASE=Antec Veris Multimedia Station E-Z IR Receiver + +usb:v15C2pFFDA* + ID_MODEL_FROM_DATABASE=iMON PAD Remote Controller + +usb:v15C2pFFDC* + ID_MODEL_FROM_DATABASE=iMON PAD Remote Controller + +usb:v15C5* + ID_VENDOR_FROM_DATABASE=Advance Multimedia Internet Technology Inc. (AMIT) + +usb:v15C5p0008* + ID_MODEL_FROM_DATABASE=WL532U 802.11g Adapter + +usb:v15C6* + ID_VENDOR_FROM_DATABASE=Laboratoires MXM + +usb:v15C6p1000* + ID_MODEL_FROM_DATABASE=DigistimSP (cold) + +usb:v15C6p1001* + ID_MODEL_FROM_DATABASE=DigistimSP (warm) + +usb:v15C6p1002* + ID_MODEL_FROM_DATABASE=DigimapSP USB (cold) + +usb:v15C6p1003* + ID_MODEL_FROM_DATABASE=DigimapSP USB (warm) + +usb:v15C6p1004* + ID_MODEL_FROM_DATABASE=DigistimSP (cold) + +usb:v15C6p1005* + ID_MODEL_FROM_DATABASE=DigistimSP (warm) + +usb:v15C6p1100* + ID_MODEL_FROM_DATABASE=Odyssee (cold) + +usb:v15C6p1101* + ID_MODEL_FROM_DATABASE=Odyssee (warm) + +usb:v15C6p1200* + ID_MODEL_FROM_DATABASE=Digispy + +usb:v15C8* + ID_VENDOR_FROM_DATABASE=KTF Technologies + +usb:v15C8p3201* + ID_MODEL_FROM_DATABASE=EVER EV-W100/EV-W250 + +usb:v15C9* + ID_VENDOR_FROM_DATABASE=D-Box Technologies + +usb:v15CA* + ID_VENDOR_FROM_DATABASE=Textech International Ltd. + +usb:v15CAp00C3* + ID_MODEL_FROM_DATABASE=Mini Optical Mouse + +usb:v15CAp0101* + ID_MODEL_FROM_DATABASE=MIDI Interface cable + +usb:v15CAp1806* + ID_MODEL_FROM_DATABASE=MIDI Interface cable + +usb:v15D5* + ID_VENDOR_FROM_DATABASE=Coulomb Electronics Ltd. + +usb:v15D9* + ID_VENDOR_FROM_DATABASE=Trust International B.V. + +usb:v15D9p0A33* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v15D9p0A37* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v15D9p0A41* + ID_MODEL_FROM_DATABASE=MI-2540D [Optical mouse] + +usb:v15D9p0A4C* + ID_MODEL_FROM_DATABASE=USB+PS/2 Optical Mouse + +usb:v15D9p0A4D* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v15D9p0A4F* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v15DC* + ID_VENDOR_FROM_DATABASE=Hynix Semiconductor Inc. + +usb:v15E0* + ID_VENDOR_FROM_DATABASE=Seong Ji Industrial Co., Ltd. + +usb:v15E1* + ID_VENDOR_FROM_DATABASE=RSA + +usb:v15E1p2007* + ID_MODEL_FROM_DATABASE=RSA SecurID (R) Authenticator + +usb:v15E4* + ID_VENDOR_FROM_DATABASE=Numark + +usb:v15E4p0024* + ID_MODEL_FROM_DATABASE=Mixtrack + +usb:v15E4p0140* + ID_MODEL_FROM_DATABASE=ION VCR 2 PC / Video 2 PC + +usb:v15E8* + ID_VENDOR_FROM_DATABASE=SohoWare + +usb:v15E8p9100* + ID_MODEL_FROM_DATABASE=NUB100 Ethernet [pegasus] + +usb:v15E8p9110* + ID_MODEL_FROM_DATABASE=10/100 USB Ethernet + +usb:v15E9* + ID_VENDOR_FROM_DATABASE=Pacific Digital Corp. + +usb:v15E9p04CE* + ID_MODEL_FROM_DATABASE=MemoryFrame MF-570 + +usb:v15E9p1968* + ID_MODEL_FROM_DATABASE=MemoryFrame MF-570 + +usb:v15E9p1969* + ID_MODEL_FROM_DATABASE=Digital Frame + +usb:v15EC* + ID_VENDOR_FROM_DATABASE=Belcarra Technologies Corp. + +usb:v15F4* + ID_VENDOR_FROM_DATABASE=HanfTek + +usb:v15F4p0001* + ID_MODEL_FROM_DATABASE=HanfTek UMT-010 USB2.0 DVB-T (cold) + +usb:v15F4p0025* + ID_MODEL_FROM_DATABASE=HanfTek UMT-010 USB2.0 DVB-T (warm) + +usb:v1604* + ID_VENDOR_FROM_DATABASE=Tascam + +usb:v1604p8000* + ID_MODEL_FROM_DATABASE=US-428 Audio/Midi Controller (without fw) + +usb:v1604p8001* + ID_MODEL_FROM_DATABASE=US-428 Audio/Midi Controller + +usb:v1604p8004* + ID_MODEL_FROM_DATABASE=US-224 Audio/Midi Controller (without fw) + +usb:v1604p8005* + ID_MODEL_FROM_DATABASE=US-224 Audio/Midi Controller + +usb:v1604p8006* + ID_MODEL_FROM_DATABASE=US-122 Audio/Midi Interface (without fw) + +usb:v1604p8007* + ID_MODEL_FROM_DATABASE=US-122 Audio/Midi Interface + +usb:v1606* + ID_VENDOR_FROM_DATABASE=Umax + +usb:v1606p0002* + ID_MODEL_FROM_DATABASE=Astra 1236U Scanner + +usb:v1606p0010* + ID_MODEL_FROM_DATABASE=Astra 1220U + +usb:v1606p0030* + ID_MODEL_FROM_DATABASE=Astra 1600U/2000U + +usb:v1606p0050* + ID_MODEL_FROM_DATABASE=Scanner + +usb:v1606p0060* + ID_MODEL_FROM_DATABASE=Astra 3400/3450 + +usb:v1606p0070* + ID_MODEL_FROM_DATABASE=Astra 4400/4450 + +usb:v1606p0130* + ID_MODEL_FROM_DATABASE=Astra 2100U + +usb:v1606p0160* + ID_MODEL_FROM_DATABASE=Astra 5400U + +usb:v1606p0170* + ID_MODEL_FROM_DATABASE=Uniscan D50 + +usb:v1606p0230* + ID_MODEL_FROM_DATABASE=Astra 2200/2200SU + +usb:v1606p0350* + ID_MODEL_FROM_DATABASE=Astra 4800/4850 Scanner + +usb:v1606p1030* + ID_MODEL_FROM_DATABASE=Astra 4000U + +usb:v1606p1220* + ID_MODEL_FROM_DATABASE=Genesys Logic Scanner Controller NT5.0 + +usb:v1606p2010* + ID_MODEL_FROM_DATABASE=AstraCam Digital Camera + +usb:v1606p2020* + ID_MODEL_FROM_DATABASE=AstraCam 1000 + +usb:v1606p2030* + ID_MODEL_FROM_DATABASE=AstraCam 1800 Digital Camera + +usb:v1608* + ID_VENDOR_FROM_DATABASE=Inside Out Networks [hex] + +usb:v1608p0001* + ID_MODEL_FROM_DATABASE=EdgePort/4 Serial Port + +usb:v1608p0002* + ID_MODEL_FROM_DATABASE=Edgeport/8 + +usb:v1608p0003* + ID_MODEL_FROM_DATABASE=Rapidport/4 + +usb:v1608p0004* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0005* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0006* + ID_MODEL_FROM_DATABASE=Edgeport/4i + +usb:v1608p0007* + ID_MODEL_FROM_DATABASE=Edgeport/2i + +usb:v1608p0008* + ID_MODEL_FROM_DATABASE=Edgeport/8 + +usb:v1608p000C* + ID_MODEL_FROM_DATABASE=Edgeport/421 + +usb:v1608p000D* + ID_MODEL_FROM_DATABASE=Edgeport/21 + +usb:v1608p000E* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p000F* + ID_MODEL_FROM_DATABASE=Edgeport/8 + +usb:v1608p0010* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0011* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0012* + ID_MODEL_FROM_DATABASE=Edgeport/416 + +usb:v1608p0014* + ID_MODEL_FROM_DATABASE=Edgeport/8i + +usb:v1608p0018* + ID_MODEL_FROM_DATABASE=Edgeport/412 + +usb:v1608p0019* + ID_MODEL_FROM_DATABASE=Edgeport/412 + +usb:v1608p001A* + ID_MODEL_FROM_DATABASE=Edgeport/2+2i + +usb:v1608p0101* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0105* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0106* + ID_MODEL_FROM_DATABASE=Edgeport/4i + +usb:v1608p0107* + ID_MODEL_FROM_DATABASE=Edgeport/2i + +usb:v1608p010C* + ID_MODEL_FROM_DATABASE=Edgeport/421 + +usb:v1608p010D* + ID_MODEL_FROM_DATABASE=Edgeport/21 + +usb:v1608p0110* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0111* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0112* + ID_MODEL_FROM_DATABASE=Edgeport/416 + +usb:v1608p0114* + ID_MODEL_FROM_DATABASE=Edgeport/8i + +usb:v1608p0201* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0203* + ID_MODEL_FROM_DATABASE=Rapidport/4 + +usb:v1608p0204* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0205* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0206* + ID_MODEL_FROM_DATABASE=Edgeport/4i + +usb:v1608p0207* + ID_MODEL_FROM_DATABASE=Edgeport/2i + +usb:v1608p020C* + ID_MODEL_FROM_DATABASE=Edgeport/421 + +usb:v1608p020D* + ID_MODEL_FROM_DATABASE=Edgeport/21 + +usb:v1608p020E* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p020F* + ID_MODEL_FROM_DATABASE=Edgeport/8 + +usb:v1608p0210* + ID_MODEL_FROM_DATABASE=Edgeport/2 + +usb:v1608p0211* + ID_MODEL_FROM_DATABASE=Edgeport/4 + +usb:v1608p0212* + ID_MODEL_FROM_DATABASE=Edgeport/416 + +usb:v1608p0214* + ID_MODEL_FROM_DATABASE=Edgeport/8i + +usb:v1608p0215* + ID_MODEL_FROM_DATABASE=Edgeport/1 + +usb:v1608p0216* + ID_MODEL_FROM_DATABASE=EPOS/44 + +usb:v1608p0217* + ID_MODEL_FROM_DATABASE=Edgeport/42 + +usb:v1608p021A* + ID_MODEL_FROM_DATABASE=Edgeport/2+2i + +usb:v1608p021B* + ID_MODEL_FROM_DATABASE=Edgeport/2c + +usb:v1608p021C* + ID_MODEL_FROM_DATABASE=Edgeport/221c + +usb:v1608p021D* + ID_MODEL_FROM_DATABASE=Edgeport/22c + +usb:v1608p021E* + ID_MODEL_FROM_DATABASE=Edgeport/21c + +usb:v1608p021F* + ID_MODEL_FROM_DATABASE=Edgeport/62 + +usb:v1608p0240* + ID_MODEL_FROM_DATABASE=Edgeport/1 + +usb:v1608p0241* + ID_MODEL_FROM_DATABASE=Edgeport/1i + +usb:v1608p0242* + ID_MODEL_FROM_DATABASE=Edgeport/4s + +usb:v1608p0243* + ID_MODEL_FROM_DATABASE=Edgeport/8s + +usb:v1608p0244* + ID_MODEL_FROM_DATABASE=Edgeport/8 + +usb:v1608p0245* + ID_MODEL_FROM_DATABASE=Edgeport/22c + +usb:v1608p0301* + ID_MODEL_FROM_DATABASE=Watchport/P + +usb:v1608p0302* + ID_MODEL_FROM_DATABASE=Watchport/M + +usb:v1608p0303* + ID_MODEL_FROM_DATABASE=Watchport/W + +usb:v1608p0304* + ID_MODEL_FROM_DATABASE=Watchport/T + +usb:v1608p0305* + ID_MODEL_FROM_DATABASE=Watchport/H + +usb:v1608p0306* + ID_MODEL_FROM_DATABASE=Watchport/E + +usb:v1608p0307* + ID_MODEL_FROM_DATABASE=Watchport/L + +usb:v1608p0308* + ID_MODEL_FROM_DATABASE=Watchport/R + +usb:v1608p0309* + ID_MODEL_FROM_DATABASE=Watchport/A + +usb:v1608p030A* + ID_MODEL_FROM_DATABASE=Watchport/D + +usb:v1608p030B* + ID_MODEL_FROM_DATABASE=Watchport/D + +usb:v1608p030C* + ID_MODEL_FROM_DATABASE=Power Management Port + +usb:v1608p030E* + ID_MODEL_FROM_DATABASE=Power Management Port + +usb:v1608p030F* + ID_MODEL_FROM_DATABASE=Watchport/G + +usb:v1608p0310* + ID_MODEL_FROM_DATABASE=Watchport/Tc + +usb:v1608p0311* + ID_MODEL_FROM_DATABASE=Watchport/Hc + +usb:v1608p1403* + ID_MODEL_FROM_DATABASE=MultiTech Systems MT4X56 Modem + +usb:v1608p1A17* + ID_MODEL_FROM_DATABASE=Agilent Technologies (E6473) + +usb:v160A* + ID_VENDOR_FROM_DATABASE=VIA Technologies, Inc. + +usb:v160Ap3184* + ID_MODEL_FROM_DATABASE=VIA VNT-6656 [WiFi 802.11b/g USB Dongle] + +usb:v160E* + ID_VENDOR_FROM_DATABASE=INRO + +usb:v160Ep0001* + ID_MODEL_FROM_DATABASE=E2USBKey + +usb:v1614* + ID_VENDOR_FROM_DATABASE=Amoi Electronics + +usb:v1614p0404* + ID_MODEL_FROM_DATABASE=WMA9109 UMTS Phone + +usb:v1614p0600* + ID_MODEL_FROM_DATABASE=Vodafone VDA GPS / Toschiba Protege G710 + +usb:v1614p0804* + ID_MODEL_FROM_DATABASE=WP-S1 Phone + +usb:v1617* + ID_VENDOR_FROM_DATABASE=Sony Corp. + +usb:v1617p2002* + ID_MODEL_FROM_DATABASE=NVX-P1 Personal Navigation System + +usb:v1619* + ID_VENDOR_FROM_DATABASE=L & K Precision Technology Co., Ltd. + +usb:v1621* + ID_VENDOR_FROM_DATABASE=Wionics Research + +usb:v1628* + ID_VENDOR_FROM_DATABASE=Stonestreet One, Inc. + +usb:v162A* + ID_VENDOR_FROM_DATABASE=Airgo Networks Inc. + +usb:v162F* + ID_VENDOR_FROM_DATABASE=WiQuest Communications, Inc. + +usb:v1630* + ID_VENDOR_FROM_DATABASE=2Wire, Inc. + +usb:v1630p0005* + ID_MODEL_FROM_DATABASE=802.11g Wireless Adapter [Intersil ISL3886] + +usb:v1630p0011* + ID_MODEL_FROM_DATABASE=PC Port 10 Mps Adapter + +usb:v1630pFF81* + ID_MODEL_FROM_DATABASE=802.11b Wireless Adapter [Lucent/Agere Hermes I] + +usb:v1631* + ID_VENDOR_FROM_DATABASE=Good Way Technology + +usb:v1631p6200* + ID_MODEL_FROM_DATABASE=GWUSB2E + +usb:v1631pC019* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v1645* + ID_VENDOR_FROM_DATABASE=Entrega [hex] + +usb:v1645p0001* + ID_MODEL_FROM_DATABASE=1S Serial Port + +usb:v1645p0002* + ID_MODEL_FROM_DATABASE=2S Serial Port + +usb:v1645p0003* + ID_MODEL_FROM_DATABASE=1S25 Serial Port + +usb:v1645p0004* + ID_MODEL_FROM_DATABASE=4S Serial Port + +usb:v1645p0005* + ID_MODEL_FROM_DATABASE=E45 Ethernet [klsi] + +usb:v1645p0006* + ID_MODEL_FROM_DATABASE=Parallel Port + +usb:v1645p0007* + ID_MODEL_FROM_DATABASE=U1-SC25 SCSI + +usb:v1645p0008* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v1645p0016* + ID_MODEL_FROM_DATABASE=Bi-directional to Parallel Printer Converter + +usb:v1645p0080* + ID_MODEL_FROM_DATABASE=1 port to Serial Converter + +usb:v1645p0081* + ID_MODEL_FROM_DATABASE=1 port to Serial Converter + +usb:v1645p0093* + ID_MODEL_FROM_DATABASE=1S9 Serial Port + +usb:v1645p8000* + ID_MODEL_FROM_DATABASE=EZ-USB + +usb:v1645p8001* + ID_MODEL_FROM_DATABASE=1 port to Serial + +usb:v1645p8002* + ID_MODEL_FROM_DATABASE=2x Serial Port + +usb:v1645p8003* + ID_MODEL_FROM_DATABASE=1 port to Serial + +usb:v1645p8004* + ID_MODEL_FROM_DATABASE=2U4S serial/usb hub + +usb:v1645p8005* + ID_MODEL_FROM_DATABASE=Ethernet + +usb:v1645p8080* + ID_MODEL_FROM_DATABASE=1 port to Serial + +usb:v1645p8081* + ID_MODEL_FROM_DATABASE=1 port to Serial + +usb:v1645p8093* + ID_MODEL_FROM_DATABASE=PortGear Serial Port + +usb:v1649* + ID_VENDOR_FROM_DATABASE=SofTec Microsystems + +usb:v1649p0102* + ID_MODEL_FROM_DATABASE=uDART In-Circuit Debugger + +usb:v1649p0200* + ID_MODEL_FROM_DATABASE=SpYder USBSPYDER08 + +usb:v164A* + ID_VENDOR_FROM_DATABASE=ChipX + +usb:v164C* + ID_VENDOR_FROM_DATABASE=Matrix Vision GmbH + +usb:v164Cp0101* + ID_MODEL_FROM_DATABASE=mvBlueFOX camera (no firmware) + +usb:v164Cp0103* + ID_MODEL_FROM_DATABASE=mvBlueFOX camera + +usb:v164Cp0201* + ID_MODEL_FROM_DATABASE=mvBlueLYNX-X intelligent camera (bootloader) + +usb:v164Cp0203* + ID_MODEL_FROM_DATABASE=mvBlueLYNX-X intelligent camera + +usb:v1657* + ID_VENDOR_FROM_DATABASE=Struck Innovative Systeme GmbH + +usb:v1657p3150* + ID_MODEL_FROM_DATABASE=SIS3150 USB2.0 to VME interface + +usb:v165B* + ID_VENDOR_FROM_DATABASE=Frontier Design Group + +usb:v165Bp8101* + ID_MODEL_FROM_DATABASE=Tranzport Control Surface + +usb:v165BpFAD1* + ID_MODEL_FROM_DATABASE=Alphatrack Control Surface + +usb:v165C* + ID_VENDOR_FROM_DATABASE=Kondo Kagaku + +usb:v165Cp0002* + ID_MODEL_FROM_DATABASE=Serial Adapter + +usb:v1660* + ID_VENDOR_FROM_DATABASE=Creatix Polymedia GmbH + +usb:v1667* + ID_VENDOR_FROM_DATABASE=GIGA-TMS INC. + +usb:v1667p0005* + ID_MODEL_FROM_DATABASE=PCR330A RFID Reader (125 kHz, keyboard emulation) + +usb:v1668* + ID_VENDOR_FROM_DATABASE=Actiontec Electronics, Inc. [hex] + +usb:v1668p0009* + ID_MODEL_FROM_DATABASE=Gateway + +usb:v1668p0333* + ID_MODEL_FROM_DATABASE=Modem + +usb:v1668p0358* + ID_MODEL_FROM_DATABASE=InternetPhoneWizard + +usb:v1668p0405* + ID_MODEL_FROM_DATABASE=Gateway + +usb:v1668p0408* + ID_MODEL_FROM_DATABASE=Prism2.5 802.11b Adapter + +usb:v1668p0413* + ID_MODEL_FROM_DATABASE=Gateway + +usb:v1668p0421* + ID_MODEL_FROM_DATABASE=Prism2.5 802.11b Adapter + +usb:v1668p0441* + ID_MODEL_FROM_DATABASE=IBM Integrated Bluetooth II + +usb:v1668p0500* + ID_MODEL_FROM_DATABASE=BTM200B BlueTooth Adapter + +usb:v1668p1050* + ID_MODEL_FROM_DATABASE=802UIG-1 802.11g Wireless Mini Adapter [Intersil ISL3887] + +usb:v1668p1200* + ID_MODEL_FROM_DATABASE=802AIN Wireless N Network Adapter [Atheros AR9170+AR9101] + +usb:v1668p1441* + ID_MODEL_FROM_DATABASE=IBM Integrated Bluetooth II + +usb:v1668p2441* + ID_MODEL_FROM_DATABASE=BMDC-2 IBM Bluetooth III w.56k + +usb:v1668p3441* + ID_MODEL_FROM_DATABASE=IBM Integrated Bluetooth III + +usb:v1668p6010* + ID_MODEL_FROM_DATABASE=Gateway + +usb:v1668p6097* + ID_MODEL_FROM_DATABASE=802.11b Wireless Adapter + +usb:v1668p6106* + ID_MODEL_FROM_DATABASE=802UI3(B) 802.11b Wireless Adapter [Intersil PRISM 3] + +usb:v1668p7605* + ID_MODEL_FROM_DATABASE=UAT1 Wireless Ethernet Adapter + +usb:v1669* + ID_VENDOR_FROM_DATABASE=PiKRON Ltd. [hex] + +usb:v1669p1001* + ID_MODEL_FROM_DATABASE=uLan2USB Converter - PS1 protocol + +usb:v166A* + ID_VENDOR_FROM_DATABASE=Clipsal + +usb:v166Ap0101* + ID_MODEL_FROM_DATABASE=C-Bus Multi-room Audio Matrix Switcher + +usb:v166Ap0201* + ID_MODEL_FROM_DATABASE=C-Bus Pascal Automation Controller + +usb:v166Ap0301* + ID_MODEL_FROM_DATABASE=C-Bus Wireless PC Interface + +usb:v166Ap0303* + ID_MODEL_FROM_DATABASE=C-Bus interface + +usb:v166Ap0304* + ID_MODEL_FROM_DATABASE=C-Bus Black and White Touchscreen + +usb:v166Ap0305* + ID_MODEL_FROM_DATABASE=C-Bus Spectrum Colour Touchscreen + +usb:v166Ap0401* + ID_MODEL_FROM_DATABASE=C-Bus Architectural Dimmer + +usb:v1677* + ID_VENDOR_FROM_DATABASE=China Huada Integrated Circuit Design (Group) Co., Ltd. (CIDC Group) + +usb:v1677p0103* + ID_MODEL_FROM_DATABASE=Token + +usb:v1679* + ID_VENDOR_FROM_DATABASE=Total Phase + +usb:v1679p2001* + ID_MODEL_FROM_DATABASE=Beagle Protocol Analyzer + +usb:v1679p2002* + ID_MODEL_FROM_DATABASE=Cheetah SPI Host Adapter + +usb:v1680* + ID_VENDOR_FROM_DATABASE=Golden Bridge Electech Inc. + +usb:v1680pA332* + ID_MODEL_FROM_DATABASE=DVB-T Dongle [RTL2832U] + +usb:v1681* + ID_VENDOR_FROM_DATABASE=Prevo Technologies, Inc. + +usb:v1681p0001* + ID_MODEL_FROM_DATABASE=Tuner's Dashboard + +usb:v1681p0002* + ID_MODEL_FROM_DATABASE=Tubachron + +usb:v1682* + ID_VENDOR_FROM_DATABASE=Maxwise Production Enterprise Ltd. + +usb:v1684* + ID_VENDOR_FROM_DATABASE=Godspeed Computer Corp. + +usb:v1685* + ID_VENDOR_FROM_DATABASE=Delock + +usb:v1685p0200* + ID_MODEL_FROM_DATABASE=Infrared adapter + +usb:v1686* + ID_VENDOR_FROM_DATABASE=ZOOM Corporation + +usb:v1686p0045* + ID_MODEL_FROM_DATABASE=H4 Digital Recorder + +usb:v1687* + ID_VENDOR_FROM_DATABASE=Kingmax Digital Inc. + +usb:v1687p5289* + ID_MODEL_FROM_DATABASE=FlashDisk + +usb:v1687p6211* + ID_MODEL_FROM_DATABASE=FlashDisk + +usb:v1687p6213* + ID_MODEL_FROM_DATABASE=FlashDisk + +usb:v1688* + ID_VENDOR_FROM_DATABASE=Saab AB + +usb:v1689* + ID_VENDOR_FROM_DATABASE=Razer USA, Ltd + +usb:v1689pFD00* + ID_MODEL_FROM_DATABASE=Onza Tournament Edition controller + +usb:v168C* + ID_VENDOR_FROM_DATABASE=Atheros Communications + +usb:v168Cp0001* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v168Cp0002* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v1690* + ID_VENDOR_FROM_DATABASE=Askey Computer Corp. [hex] + +usb:v1690p0001* + ID_MODEL_FROM_DATABASE=Arcaze Gamepad + +usb:v1690p0101* + ID_MODEL_FROM_DATABASE=Creative Modem Blaster DE5670 + +usb:v1690p0102* + ID_MODEL_FROM_DATABASE=V1456 VQE-R2 Modem [conexant] + +usb:v1690p0103* + ID_MODEL_FROM_DATABASE=1456 VQE-R3 Modem [conexant] + +usb:v1690p0104* + ID_MODEL_FROM_DATABASE=HCF V90 Data Fax RTAD Modem + +usb:v1690p0107* + ID_MODEL_FROM_DATABASE=HCF V.90 Data,Fax,RTAD Modem + +usb:v1690p0109* + ID_MODEL_FROM_DATABASE=MagicXpress V.90 Pocket Modem [conexant] + +usb:v1690p0203* + ID_MODEL_FROM_DATABASE=Voyager ADSL Modem Loader + +usb:v1690p0204* + ID_MODEL_FROM_DATABASE=Voyager ADSL Modem + +usb:v1690p0205* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v1690p0206* + ID_MODEL_FROM_DATABASE=GlobeSpan ADSL WAN Modem + +usb:v1690p0208* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v1690p0209* + ID_MODEL_FROM_DATABASE=Voyager 100 ADSL Modem + +usb:v1690p0211* + ID_MODEL_FROM_DATABASE=Globespan Virata ADSL LAN Modem + +usb:v1690p0212* + ID_MODEL_FROM_DATABASE=DSL Modem + +usb:v1690p0213* + ID_MODEL_FROM_DATABASE=HM121d DSL Modem + +usb:v1690p0214* + ID_MODEL_FROM_DATABASE=HM121d DSL Modem + +usb:v1690p0215* + ID_MODEL_FROM_DATABASE=Voyager 105 ADSL Modem + +usb:v1690p0701* + ID_MODEL_FROM_DATABASE=WLAN + +usb:v1690p0710* + ID_MODEL_FROM_DATABASE=SMCWUSBT-G + +usb:v1690p0711* + ID_MODEL_FROM_DATABASE=SMCWUSBT-G (no firmware) + +usb:v1690p0712* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v1690p0713* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v1690p0715* + ID_MODEL_FROM_DATABASE=Name: Voyager 1055 Laptop 802.11g Adapter [Broadcom 4320] + +usb:v1690p0722* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v1690p0726* + ID_MODEL_FROM_DATABASE=Wi-Fi Wireless LAN Adapter + +usb:v1690p0740* + ID_MODEL_FROM_DATABASE=802.11n Wireless LAN Card + +usb:v1690p0901* + ID_MODEL_FROM_DATABASE=Voyager 205 ADSL Router + +usb:v1690p2000* + ID_MODEL_FROM_DATABASE=naturaSign Pad Standard + +usb:v1690p2001* + ID_MODEL_FROM_DATABASE=naturaSign Pad Standard + +usb:v1690pFE12* + ID_MODEL_FROM_DATABASE=Bootloader + +usb:v1696* + ID_VENDOR_FROM_DATABASE=Hitachi Video and Information System, Inc. + +usb:v1697* + ID_VENDOR_FROM_DATABASE=VTec Test, Inc. + +usb:v16A5* + ID_VENDOR_FROM_DATABASE=Shenzhen Zhengerya Cable Co., Ltd. + +usb:v16A6* + ID_VENDOR_FROM_DATABASE=Unigraf + +usb:v16A6p3000* + ID_MODEL_FROM_DATABASE=VTG-3xxx Video Test Generator family + +usb:v16A6p4000* + ID_MODEL_FROM_DATABASE=VTG-4xxx Video Test Generator family + +usb:v16A6p5000* + ID_MODEL_FROM_DATABASE=VTG-5xxx Video Test Generator family + +usb:v16A6p5001* + ID_MODEL_FROM_DATABASE=VTG-5xxx Special (update) mode of VTG-5xxx family + +usb:v16AB* + ID_VENDOR_FROM_DATABASE=Global Sun Technology + +usb:v16ABp7801* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v16ABp7802* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v16ABp7811* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:v16ABp7812* + ID_MODEL_FROM_DATABASE=AR5523 (no firmware) + +usb:v16AC* + ID_VENDOR_FROM_DATABASE=Dongguan ChingLung Wire & Cable Co., Ltd. + +usb:v16B4* + ID_VENDOR_FROM_DATABASE=iStation + +usb:v16B4p0801* + ID_MODEL_FROM_DATABASE=U43 + +usb:v16B5* + ID_VENDOR_FROM_DATABASE=Persentec, Inc. + +usb:v16B5p0002* + ID_MODEL_FROM_DATABASE=Otto driving companion + +usb:v16C0* + ID_VENDOR_FROM_DATABASE=Van Ooijen Technische Informatica + +usb:v16C0p03E8* + ID_MODEL_FROM_DATABASE=free for internal lab use 1000 + +usb:v16C0p03E9* + ID_MODEL_FROM_DATABASE=free for internal lab use 1001 + +usb:v16C0p03EA* + ID_MODEL_FROM_DATABASE=free for internal lab use 1002 + +usb:v16C0p03EB* + ID_MODEL_FROM_DATABASE=free for internal lab use 1003 + +usb:v16C0p03EC* + ID_MODEL_FROM_DATABASE=free for internal lab use 1004 + +usb:v16C0p03ED* + ID_MODEL_FROM_DATABASE=free for internal lab use 1005 + +usb:v16C0p03EE* + ID_MODEL_FROM_DATABASE=free for internal lab use 1006 + +usb:v16C0p03EF* + ID_MODEL_FROM_DATABASE=free for internal lab use 1007 + +usb:v16C0p03F0* + ID_MODEL_FROM_DATABASE=free for internal lab use 1008 + +usb:v16C0p03F1* + ID_MODEL_FROM_DATABASE=free for internal lab use 1009 + +usb:v16C0p0477* + ID_MODEL_FROM_DATABASE=Teensy Rebootor + +usb:v16C0p0478* + ID_MODEL_FROM_DATABASE=Teensy Halfkay Bootloader + +usb:v16C0p0479* + ID_MODEL_FROM_DATABASE=Teensy Debug + +usb:v16C0p047A* + ID_MODEL_FROM_DATABASE=Teensy Serial + +usb:v16C0p047B* + ID_MODEL_FROM_DATABASE=Teensy Serial+Debug + +usb:v16C0p047C* + ID_MODEL_FROM_DATABASE=Teensy Keyboard + +usb:v16C0p047D* + ID_MODEL_FROM_DATABASE=Teensy Keyboard+Debug + +usb:v16C0p047E* + ID_MODEL_FROM_DATABASE=Teensy Mouse + +usb:v16C0p047F* + ID_MODEL_FROM_DATABASE=Teensy Mouse+Debug + +usb:v16C0p0480* + ID_MODEL_FROM_DATABASE=Teensy RawHID + +usb:v16C0p0481* + ID_MODEL_FROM_DATABASE=Teensy RawHID+Debug + +usb:v16C0p0482* + ID_MODEL_FROM_DATABASE=Teensyduino Keyboard+Mouse+Joystick + +usb:v16C0p0483* + ID_MODEL_FROM_DATABASE=Teensyduino Serial + +usb:v16C0p0484* + ID_MODEL_FROM_DATABASE=Teensyduino Disk + +usb:v16C0p0485* + ID_MODEL_FROM_DATABASE=Teensyduino MIDI + +usb:v16C0p0486* + ID_MODEL_FROM_DATABASE=Teensyduino RawHID + +usb:v16C0p0487* + ID_MODEL_FROM_DATABASE=Teensyduino Serial+Keyboard+Mouse+Joystick + +usb:v16C0p0488* + ID_MODEL_FROM_DATABASE=Teensyduino Flight Sim Controls + +usb:v16C0p05DC* + ID_MODEL_FROM_DATABASE=shared ID for use with libusb + +usb:v16C0p05DD* + ID_MODEL_FROM_DATABASE=BlackcatUSB2 + +usb:v16C0p05DF* + ID_MODEL_FROM_DATABASE=HID device except mice, keyboards, and joysticks + +usb:v16C0p05E1* + ID_MODEL_FROM_DATABASE=Free shared USB VID/PID pair for CDC devices + +usb:v16C0p05E4* + ID_MODEL_FROM_DATABASE=Free shared USB VID/PID pair for MIDI devices + +usb:v16C0p06B4* + ID_MODEL_FROM_DATABASE=USB2LPT with 2 interfaces + +usb:v16C0p06B5* + ID_MODEL_FROM_DATABASE=USB2LPT with 3 interfaces (native, HID, printer) + +usb:v16C0p074E* + ID_MODEL_FROM_DATABASE=DSP-Weuffen USB-HPI-Programmer + +usb:v16C0p074F* + ID_MODEL_FROM_DATABASE=DSP-Weuffen USB2-HPI-Programmer + +usb:v16C0p0762* + ID_MODEL_FROM_DATABASE=Osmocom SIMtrace + +usb:v16C0p076B* + ID_MODEL_FROM_DATABASE=OpenPCD 13.56MHz RFID Reader + +usb:v16C0p076C* + ID_MODEL_FROM_DATABASE=OpenPICC 13.56MHz RFID Simulator (native) + +usb:v16C0p08AC* + ID_MODEL_FROM_DATABASE=OpenBeacon USB stick + +usb:v16C0p08CA* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Universal Display + +usb:v16C0p08CB* + ID_MODEL_FROM_DATABASE=Alpermann+Velte Studio Clock + +usb:v16C0p08CC* + ID_MODEL_FROM_DATABASE=Alpermann+Velte SAM7S MT Boot Loader + +usb:v16C0p08CD* + ID_MODEL_FROM_DATABASE=Alpermann+Velte SAM7X MT Boot Loader + +usb:v16C0p0A32* + ID_MODEL_FROM_DATABASE=jbmedia Light-Manager Pro + +usb:v16C0p27D8* + ID_MODEL_FROM_DATABASE=libusb-bound devices + +usb:v16C0p27D9* + ID_MODEL_FROM_DATABASE=HID device except mice, keyboards, and joysticks + +usb:v16C0p27DA* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v16C0p27DB* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v16C0p27DC* + ID_MODEL_FROM_DATABASE=Joystick + +usb:v16C0p27DD* + ID_MODEL_FROM_DATABASE=CDC-ACM class devices (modems) + +usb:v16C0p27DE* + ID_MODEL_FROM_DATABASE=MIDI class devices + +usb:v16C0p294A* + ID_MODEL_FROM_DATABASE=Eye Movement Recorder [Visagraph] + +usb:v16C0p294B* + ID_MODEL_FROM_DATABASE=Eye Movement Recorder [ReadAlyzer] + +usb:v16CA* + ID_VENDOR_FROM_DATABASE=Wireless Cables, Inc. + +usb:v16CAp1502* + ID_MODEL_FROM_DATABASE=Bluetooth Dongle + +usb:v16CC* + ID_VENDOR_FROM_DATABASE=silex technology, Inc. + +usb:v16D0* + ID_VENDOR_FROM_DATABASE=MCS + +usb:v16D0p0498* + ID_MODEL_FROM_DATABASE=Braintechnology USB-LPS + +usb:v16D0p0504* + ID_MODEL_FROM_DATABASE=RETRO Innovations ZoomFloppy + +usb:v16D0p054B* + ID_MODEL_FROM_DATABASE=GrauTec ReelBox OLED Display (external) + +usb:v16D0p05BE* + ID_MODEL_FROM_DATABASE=EasyLogic Board + +usb:v16D0p06F9* + ID_MODEL_FROM_DATABASE=Gabotronics Xminilab + +usb:v16D0p0753* + ID_MODEL_FROM_DATABASE=Digistump DigiSpark + +usb:v16D0p075C* + ID_MODEL_FROM_DATABASE=AB-1.x UAC1 [Audio Widget] + +usb:v16D0p075D* + ID_MODEL_FROM_DATABASE=AB-1.x UAC2 [Audio Widget] + +usb:v16D0p080A* + ID_MODEL_FROM_DATABASE=S2E1 Interface + +usb:v16D0p0870* + ID_MODEL_FROM_DATABASE=Kaufmann Automotive GmbH, RKS+CAN Interface + +usb:v16D1* + ID_VENDOR_FROM_DATABASE=Suprema Inc. + +usb:v16D1p0401* + ID_MODEL_FROM_DATABASE=SUP-SFR400(A) BioMini Fingerprint Reader + +usb:v16D3* + ID_VENDOR_FROM_DATABASE=Frontline Test Equipment, Inc. + +usb:v16D5* + ID_VENDOR_FROM_DATABASE=AnyDATA Corporation + +usb:v16D5p6202* + ID_MODEL_FROM_DATABASE=CDMA/UMTS/GPRS modem + +usb:v16D5p6501* + ID_MODEL_FROM_DATABASE=CDMA 2000 1xRTT/EV-DO Modem + +usb:v16D5p6502* + ID_MODEL_FROM_DATABASE=CDMA/UMTS/GPRS modem + +usb:v16D5p6603* + ID_MODEL_FROM_DATABASE=ADU-890WH modem + +usb:v16D6* + ID_VENDOR_FROM_DATABASE=JABLOCOM s.r.o. + +usb:v16D6p8000* + ID_MODEL_FROM_DATABASE=GDP-04 desktop phone + +usb:v16D6p8001* + ID_MODEL_FROM_DATABASE=EYE-02 + +usb:v16D6p8003* + ID_MODEL_FROM_DATABASE=GDP-04 modem + +usb:v16D6p8004* + ID_MODEL_FROM_DATABASE=Bootloader + +usb:v16D6p8005* + ID_MODEL_FROM_DATABASE=GDP-04i + +usb:v16D6p8007* + ID_MODEL_FROM_DATABASE=BTP-06 modem + +usb:v16D8* + ID_VENDOR_FROM_DATABASE=CMOTECH Co., Ltd. + +usb:v16D8p5141* + ID_MODEL_FROM_DATABASE=CMOTECH CDMA Technologies modem + +usb:v16D8p5533* + ID_MODEL_FROM_DATABASE=CCU-550 CDMA EV-DO modem + +usb:v16D8p5543* + ID_MODEL_FROM_DATABASE=CDMA 2000 1xRTT/1xEVDO modem + +usb:v16D8p6280* + ID_MODEL_FROM_DATABASE=CMOTECH CDMA Technologies modem + +usb:v16D8p6803* + ID_MODEL_FROM_DATABASE=CNU-680 CDMA EV-DO modem + +usb:v16D8p8001* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem (QDL mode) + +usb:v16D8p8002* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v16DC* + ID_VENDOR_FROM_DATABASE=Wiener, Plein & Baus + +usb:v16DCp0001* + ID_MODEL_FROM_DATABASE=CC + +usb:v16DCp000B* + ID_MODEL_FROM_DATABASE=VM + +usb:v16DCp0010* + ID_MODEL_FROM_DATABASE=PL512 Power Supply System + +usb:v16DCp0011* + ID_MODEL_FROM_DATABASE=MARATON Power Supply System + +usb:v16DCp0012* + ID_MODEL_FROM_DATABASE=MPOD Multi Channel Power Supply System + +usb:v16DCp0015* + ID_MODEL_FROM_DATABASE=CML Control, Measurement and Data Logging System + +usb:v16DF* + ID_VENDOR_FROM_DATABASE=King Billion Electronics Co., Ltd. + +usb:v16F0* + ID_VENDOR_FROM_DATABASE=GN ReSound A/S + +usb:v16F0p0001* + ID_MODEL_FROM_DATABASE=Speedlink Programming Interface + +usb:v16F0p0003* + ID_MODEL_FROM_DATABASE=Airlink Wireless Programming Interface + +usb:v16F5* + ID_VENDOR_FROM_DATABASE=Futurelogic Inc. + +usb:v1706* + ID_VENDOR_FROM_DATABASE=BlueView Technologies, Inc. + +usb:v1707* + ID_VENDOR_FROM_DATABASE=ARTIMI + +usb:v170B* + ID_VENDOR_FROM_DATABASE=Swissonic + +usb:v170Bp0011* + ID_MODEL_FROM_DATABASE=MIDI-USB 1x1 + +usb:v170D* + ID_VENDOR_FROM_DATABASE=Avnera + +usb:v1711* + ID_VENDOR_FROM_DATABASE=Leica Microsystems + +usb:v1711p0101* + ID_MODEL_FROM_DATABASE=DFC-365FX camera + +usb:v1711p3020* + ID_MODEL_FROM_DATABASE=IC80 HD Camera + +usb:v1724* + ID_VENDOR_FROM_DATABASE=Meyer Instruments (MIS) + +usb:v1724p0115* + ID_MODEL_FROM_DATABASE=PAXcam5 + +usb:v1725* + ID_VENDOR_FROM_DATABASE=Vitesse Semiconductor + +usb:v1726* + ID_VENDOR_FROM_DATABASE=Axesstel, Inc. + +usb:v1726p1000* + ID_MODEL_FROM_DATABASE=wireless modem + +usb:v1726p2000* + ID_MODEL_FROM_DATABASE=wireless modem + +usb:v1726p3000* + ID_MODEL_FROM_DATABASE=wireless modem + +usb:v172F* + ID_VENDOR_FROM_DATABASE=Waltop International Corp. + +usb:v172Fp0022* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v172Fp0024* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v172Fp0025* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v172Fp0026* + ID_MODEL_FROM_DATABASE=Tablet + +usb:v172Fp0031* + ID_MODEL_FROM_DATABASE=Slim Tablet 12.1" + +usb:v172Fp0032* + ID_MODEL_FROM_DATABASE=Slim Tablet 5.8" + +usb:v172Fp0034* + ID_MODEL_FROM_DATABASE=Slim Tablet 12.1" + +usb:v172Fp0038* + ID_MODEL_FROM_DATABASE=Genius G-Pen F509 + +usb:v172Fp0500* + ID_MODEL_FROM_DATABASE=Media Tablet 14.1" + +usb:v172Fp0501* + ID_MODEL_FROM_DATABASE=Media Tablet 10.6" + +usb:v172Fp0502* + ID_MODEL_FROM_DATABASE=Sirius Battery Free Tablet + +usb:v1733* + ID_VENDOR_FROM_DATABASE=Cellink Technology Co., Ltd + +usb:v1733p0101* + ID_MODEL_FROM_DATABASE=RF Wireless Optical Mouse OP-701 + +usb:v1736* + ID_VENDOR_FROM_DATABASE=CANON IMAGING SYSTEM TECHNOLOGIES INC. + +usb:v1737* + ID_VENDOR_FROM_DATABASE=Linksys + +usb:v1737p0039* + ID_MODEL_FROM_DATABASE=USB1000 Gigabit Notebook Adapter + +usb:v1737p0070* + ID_MODEL_FROM_DATABASE=WUSB100 v1 RangePlus Wireless Network Adapter [Ralink RT2870] + +usb:v1737p0071* + ID_MODEL_FROM_DATABASE=WUSB600N v1 Dual-Band Wireless-N Network Adapter [Ralink RT2870] + +usb:v1737p0073* + ID_MODEL_FROM_DATABASE=WUSB54GC v2 802.11g Adapter [Realtek RTL8187B] + +usb:v1737p0075* + ID_MODEL_FROM_DATABASE=WUSB54GSC v2 802.11g Adapter [Broadcom 4326U] + +usb:v1737p0077* + ID_MODEL_FROM_DATABASE=WUSB54GC v3 802.11g Adapter [Ralink RT2070L] + +usb:v1737p0078* + ID_MODEL_FROM_DATABASE=WUSB100 v2 RangePlus Wireless Network Adapter [Ralink RT3070] + +usb:v1737p0079* + ID_MODEL_FROM_DATABASE=WUSB600N v2 Dual-Band Wireless-N Network Adapter [Ralink RT3572] + +usb:v173D* + ID_VENDOR_FROM_DATABASE=QSENN + +usb:v173Dp0002* + ID_MODEL_FROM_DATABASE=GP-K7000 keyboard + +usb:v1740* + ID_VENDOR_FROM_DATABASE=Senao + +usb:v1740p0100* + ID_MODEL_FROM_DATABASE=EUB1200AC AC1200 DB Wireless Adapter [Realtek RTL8812AU] + +usb:v1740p0600* + ID_MODEL_FROM_DATABASE=EUB600v1 802.11abgn Wireless Adapter [Ralink RT3572] + +usb:v1740p0605* + ID_MODEL_FROM_DATABASE=LevelOne WUA-0605 N_Max Wireless USB Adapter + +usb:v1740p0615* + ID_MODEL_FROM_DATABASE=LevelOne WUA-0615 N_Max Wireless USB Adapter + +usb:v1740p1000* + ID_MODEL_FROM_DATABASE=NUB-350 802.11g Wireless Adapter [Intersil ISL3887] + +usb:v1740p2000* + ID_MODEL_FROM_DATABASE=NUB-8301 802.11bg + +usb:v1740p3701* + ID_MODEL_FROM_DATABASE=EUB-3701 EXT 802.11g Wireless Adapter [Ralink RT2571W] + +usb:v1740p9603* + ID_MODEL_FROM_DATABASE=RTL8188S WLAN Adapter + +usb:v1740p9701* + ID_MODEL_FROM_DATABASE=EnGenius 802.11n Wireless USB Adapter + +usb:v1740p9702* + ID_MODEL_FROM_DATABASE=EnGenius 802.11n Wireless USB Adapter + +usb:v1740p9703* + ID_MODEL_FROM_DATABASE=EnGenius 802.11n Wireless USB Adapter + +usb:v1740p9705* + ID_MODEL_FROM_DATABASE=EnGenius 802.11n Wireless USB Adapter + +usb:v1740p9706* + ID_MODEL_FROM_DATABASE=EUB9706 802.11n Wireless Adapter [Ralink RT3072] + +usb:v1740p9801* + ID_MODEL_FROM_DATABASE=EUB9801 802.11abgn Wireless Adapter [Ralink RT3572] + +usb:v1743* + ID_VENDOR_FROM_DATABASE=General Atomics + +usb:v1748* + ID_VENDOR_FROM_DATABASE=MQP Electronics + +usb:v1748p0101* + ID_MODEL_FROM_DATABASE=Packet-Master USB12 + +usb:v174C* + ID_VENDOR_FROM_DATABASE=ASMedia Technology Inc. + +usb:v174Cp1153* + ID_MODEL_FROM_DATABASE=ASM2115 SATA 6Gb/s bridge + +usb:v174Cp2074* + ID_MODEL_FROM_DATABASE=ASM1074 High-Speed hub + +usb:v174Cp3074* + ID_MODEL_FROM_DATABASE=ASM1074 SuperSpeed hub + +usb:v174Cp5106* + ID_MODEL_FROM_DATABASE=ASM1051 SATA 3Gb/s bridge + +usb:v174Cp5136* + ID_MODEL_FROM_DATABASE=ASM1053 SATA 6Gb/s bridge + +usb:v174Cp55AA* + ID_MODEL_FROM_DATABASE=ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge + +usb:v174F* + ID_VENDOR_FROM_DATABASE=Syntek + +usb:v174Fp1105* + ID_MODEL_FROM_DATABASE=SM-MS/Pro-MMC-XD Card Reader + +usb:v174Fp110B* + ID_MODEL_FROM_DATABASE=HP Webcam + +usb:v174Fp1403* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v174Fp1404* + ID_MODEL_FROM_DATABASE=USB Camera device, 1.3 MPixel Web Cam + +usb:v174Fp5212* + ID_MODEL_FROM_DATABASE=USB 2.0 UVC PC Camera + +usb:v174Fp5A11* + ID_MODEL_FROM_DATABASE=PC Camera + +usb:v174Fp5A31* + ID_MODEL_FROM_DATABASE=Sonix USB 2.0 Camera + +usb:v174Fp5A35* + ID_MODEL_FROM_DATABASE=Sonix 1.3MPixel USB 2.0 Camera + +usb:v174Fp6A31* + ID_MODEL_FROM_DATABASE=Web Cam - Asus A8J, F3S, F5R, VX2S, V1S + +usb:v174Fp6A33* + ID_MODEL_FROM_DATABASE=Web Cam - Asus F3SA, F9J, F9S + +usb:v174Fp6A51* + ID_MODEL_FROM_DATABASE=2.0MPixel Web Cam - Asus Z96J, Z96S, S96S + +usb:v174Fp6A54* + ID_MODEL_FROM_DATABASE=Web Cam + +usb:v174Fp6D51* + ID_MODEL_FROM_DATABASE=2.0Mpixel Web Cam - Eurocom D900C + +usb:v174Fp8A12* + ID_MODEL_FROM_DATABASE=Syntek 0.3MPixel USB 2.0 UVC PC Camera + +usb:v174Fp8A33* + ID_MODEL_FROM_DATABASE=Syntek USB 2.0 UVC PC Camera + +usb:v174FpA311* + ID_MODEL_FROM_DATABASE=1.3MPixel Web Cam - Asus A3A, A6J, A6K, A6M, A6R, A6T, A6V, A7T, A7sv, A7U + +usb:v174FpA312* + ID_MODEL_FROM_DATABASE=1.3MPixel Web Cam + +usb:v174FpA821* + ID_MODEL_FROM_DATABASE=Web Cam - Packard Bell BU45, PB Easynote MX66-208W + +usb:v174FpAA11* + ID_MODEL_FROM_DATABASE=Web Cam + +usb:v1753* + ID_VENDOR_FROM_DATABASE=GERTEC Telecomunicacoes Ltda. + +usb:v1753pC901* + ID_MODEL_FROM_DATABASE=PPC900 Pinpad Terminal + +usb:v1756* + ID_VENDOR_FROM_DATABASE=ENENSYS Technologies + +usb:v1756p0006* + ID_MODEL_FROM_DATABASE=DiviPitch + +usb:v1759* + ID_VENDOR_FROM_DATABASE=LucidPort Technology, Inc. + +usb:v1761* + ID_VENDOR_FROM_DATABASE=ASUSTek Computer, Inc. (wrong ID) + +usb:v1761p0B05* + ID_MODEL_FROM_DATABASE=802.11n Network Adapter (wrong ID - swapped vendor and device) + +usb:v1772* + ID_VENDOR_FROM_DATABASE=System Level Solutions, Inc. + +usb:v1776* + ID_VENDOR_FROM_DATABASE=Arowana + +usb:v1776p501C* + ID_MODEL_FROM_DATABASE=300K CMOS Camera + +usb:v177F* + ID_VENDOR_FROM_DATABASE=Sweex + +usb:v177Fp0004* + ID_MODEL_FROM_DATABASE=MM004V5 Photo Key Chain (Digital Photo Frame) 1.5" + +usb:v177Fp0153* + ID_MODEL_FROM_DATABASE=LW153 802.11n Adapter [ralink rt3070] + +usb:v177Fp0154* + ID_MODEL_FROM_DATABASE=LW154 802.11bgn (1x1:1) Wireless Adapter [Realtek RTL8188SU] + +usb:v177Fp0313* + ID_MODEL_FROM_DATABASE=LW313 802.11n Adapter [ralink rt2770 + rt2720] + +usb:v1781* + ID_VENDOR_FROM_DATABASE=Multiple Vendors + +usb:v1781p083E* + ID_MODEL_FROM_DATABASE=MetaGeek Wi-Spy + +usb:v1781p083F* + ID_MODEL_FROM_DATABASE=MetaGeek Wi-Spy 2.4x + +usb:v1781p0938* + ID_MODEL_FROM_DATABASE=Iguanaworks USB IR Transceiver + +usb:v1781p0A96* + ID_MODEL_FROM_DATABASE=raphnet.net usb_game12 + +usb:v1781p0A97* + ID_MODEL_FROM_DATABASE=raphnet.net SNES mouse adapter + +usb:v1781p0A98* + ID_MODEL_FROM_DATABASE=raphnet.net USBTenki + +usb:v1781p0A99* + ID_MODEL_FROM_DATABASE=raphnet.net NES + +usb:v1781p0A9A* + ID_MODEL_FROM_DATABASE=raphnet.net Gamecube/N64 controller + +usb:v1781p0A9B* + ID_MODEL_FROM_DATABASE=raphnet.net DB9Joy + +usb:v1781p0A9C* + ID_MODEL_FROM_DATABASE=raphnet.net Intellivision + +usb:v1781p0A9D* + ID_MODEL_FROM_DATABASE=raphnet.net 4nes4snes + +usb:v1781p0A9E* + ID_MODEL_FROM_DATABASE=raphnet.net Megadrive multitap + +usb:v1781p0A9F* + ID_MODEL_FROM_DATABASE=raphnet.net MultiDB9joy + +usb:v1781p0C30* + ID_MODEL_FROM_DATABASE=Telldus TellStick + +usb:v1781p0C31* + ID_MODEL_FROM_DATABASE=Telldus TellStick Duo + +usb:v1781p0C9F* + ID_MODEL_FROM_DATABASE=USBtiny + +usb:v1781p1EEF* + ID_MODEL_FROM_DATABASE=OpenAPC SecuKey + +usb:v1781p1EF0* + ID_MODEL_FROM_DATABASE=E1701 Modular Controller Card + +usb:v1781p1EF1* + ID_MODEL_FROM_DATABASE=E1701 Modular Controller Card + +usb:v1782* + ID_VENDOR_FROM_DATABASE=Spreadtrum Communications Inc. + +usb:v1784* + ID_VENDOR_FROM_DATABASE=TopSeed Technology Corp. + +usb:v1784p0001* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1784p0004* + ID_MODEL_FROM_DATABASE=RF Combo Device + +usb:v1784p0006* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1784p0007* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1784p0008* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1784p000A* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1784p0011* + ID_MODEL_FROM_DATABASE=eHome Infrared Transceiver + +usb:v1787* + ID_VENDOR_FROM_DATABASE=ATI AIB + +usb:v1788* + ID_VENDOR_FROM_DATABASE=ShenZhen Litkconn Technology Co., Ltd. + +usb:v1796* + ID_VENDOR_FROM_DATABASE=Printrex, Inc. + +usb:v1797* + ID_VENDOR_FROM_DATABASE=JALCO CO., LTD. + +usb:v1799* + ID_VENDOR_FROM_DATABASE=Thales Norway A/S + +usb:v1799p7051* + ID_MODEL_FROM_DATABASE=Belkin F5D7051 802.11g Adapter v1000 [Broadcom 4320] + +usb:v1799p8051* + ID_MODEL_FROM_DATABASE=Belkin F5D8051 v2 802.11bgn Wireless Adapter [Marvell 88W8362] + +usb:v179D* + ID_VENDOR_FROM_DATABASE=Ricavision International, Inc. + +usb:v179Dp0010* + ID_MODEL_FROM_DATABASE=Internal Infrared Transceiver + +usb:v17A0* + ID_VENDOR_FROM_DATABASE=Samson Technologies Corp. + +usb:v17A0p0001* + ID_MODEL_FROM_DATABASE=C01U condenser microphone + +usb:v17A0p0002* + ID_MODEL_FROM_DATABASE=Q1U dynamic microphone + +usb:v17A0p0100* + ID_MODEL_FROM_DATABASE=C03U multi-pattern microphone + +usb:v17A0p0101* + ID_MODEL_FROM_DATABASE=UB1 boundary microphone + +usb:v17A0p0120* + ID_MODEL_FROM_DATABASE=Meteorite condenser microphone + +usb:v17A0p0200* + ID_MODEL_FROM_DATABASE=StudioDock monitors (internal hub) + +usb:v17A0p0201* + ID_MODEL_FROM_DATABASE=StudioDock monitors (audio) + +usb:v17A0p0210* + ID_MODEL_FROM_DATABASE=StudioGT monitors + +usb:v17A0p0301* + ID_MODEL_FROM_DATABASE=Q2U handheld microphone with XLR + +usb:v17A0p0302* + ID_MODEL_FROM_DATABASE=GoMic compact condenser microphone + +usb:v17A0p0303* + ID_MODEL_FROM_DATABASE=C01U Pro condenser microphone + +usb:v17A0p0304* + ID_MODEL_FROM_DATABASE=Q2U handheld mic with XLR + +usb:v17A0p0305* + ID_MODEL_FROM_DATABASE=GoMic compact condenser mic + +usb:v17A0p0310* + ID_MODEL_FROM_DATABASE=Meteor condenser microphone + +usb:v17A4* + ID_VENDOR_FROM_DATABASE=Concept2 + +usb:v17A4p0001* + ID_MODEL_FROM_DATABASE=Performance Monitor 3 + +usb:v17A4p0002* + ID_MODEL_FROM_DATABASE=Performance Monitor 4 + +usb:v17A5* + ID_VENDOR_FROM_DATABASE=Advanced Connection Technology Inc. + +usb:v17A7* + ID_VENDOR_FROM_DATABASE=MICOMSOFT CO., LTD. + +usb:v17A8* + ID_VENDOR_FROM_DATABASE=Kamstrup A/S + +usb:v17A8p0001* + ID_MODEL_FROM_DATABASE=Optical Eye/3-wire + +usb:v17A8p0005* + ID_MODEL_FROM_DATABASE=M-Bus Master MultiPort 250D + +usb:v17B3* + ID_VENDOR_FROM_DATABASE=Grey Innovation + +usb:v17B3p0004* + ID_MODEL_FROM_DATABASE=Linux-USB Midi Gadget + +usb:v17B5* + ID_VENDOR_FROM_DATABASE=Lunatone + +usb:v17B5p0010* + ID_MODEL_FROM_DATABASE=MFT Sensor + +usb:v17BA* + ID_VENDOR_FROM_DATABASE=SAURIS GmbH + +usb:v17BAp0001* + ID_MODEL_FROM_DATABASE=SAU510-USB [no firmware] + +usb:v17BAp0510* + ID_MODEL_FROM_DATABASE=SAU510-USB and SAU510-USB plus JTAG Emulators + +usb:v17BAp0511* + ID_MODEL_FROM_DATABASE=SAU510-USB Iso Plus JTAG Emulator + +usb:v17BAp0520* + ID_MODEL_FROM_DATABASE=SAU510-USB Nano JTAG Emulator + +usb:v17BAp1511* + ID_MODEL_FROM_DATABASE=Onboard Emulator on SAUModule development kit + +usb:v17C3* + ID_VENDOR_FROM_DATABASE=Singim International Corp. + +usb:v17CC* + ID_VENDOR_FROM_DATABASE=Native Instruments + +usb:v17CCp041C* + ID_MODEL_FROM_DATABASE=Audio 2 DJ + +usb:v17CCp0808* + ID_MODEL_FROM_DATABASE=Maschine Controller + +usb:v17CCp0815* + ID_MODEL_FROM_DATABASE=Audio Kontrol 1 + +usb:v17CCp0839* + ID_MODEL_FROM_DATABASE=Audio 4 DJ + +usb:v17CCp0D8D* + ID_MODEL_FROM_DATABASE=Guitarrig Mobile + +usb:v17CCp1915* + ID_MODEL_FROM_DATABASE=Session I/O + +usb:v17CCp1940* + ID_MODEL_FROM_DATABASE=RigKontrol3 + +usb:v17CCp1969* + ID_MODEL_FROM_DATABASE=RigKontrol2 + +usb:v17CCp1978* + ID_MODEL_FROM_DATABASE=Audio 8 DJ + +usb:v17CCp2280* + ID_MODEL_FROM_DATABASE=Medion MDPNA1500 in card reader mode + +usb:v17CCp2305* + ID_MODEL_FROM_DATABASE=Traktor Kontrol X1 + +usb:v17CCp4711* + ID_MODEL_FROM_DATABASE=Kore Controller + +usb:v17CCp4712* + ID_MODEL_FROM_DATABASE=Kore Controller 2 + +usb:v17CCpBAFF* + ID_MODEL_FROM_DATABASE=Traktor Kontrol S4 + +usb:v17CF* + ID_VENDOR_FROM_DATABASE=Hip Hing Cable & Plug Mfy. Ltd. + +usb:v17D0* + ID_VENDOR_FROM_DATABASE=Sanford L.P. + +usb:v17D3* + ID_VENDOR_FROM_DATABASE=Korea Techtron Co., Ltd. + +usb:v17E9* + ID_VENDOR_FROM_DATABASE=DisplayLink + +usb:v17E9p0051* + ID_MODEL_FROM_DATABASE=USB VGA Adaptor + +usb:v17E9p030B* + ID_MODEL_FROM_DATABASE=HP T100 + +usb:v17E9p0377* + ID_MODEL_FROM_DATABASE=Plugable UD-160-A (M) + +usb:v17E9p0378* + ID_MODEL_FROM_DATABASE=Plugable UGA-2K-A + +usb:v17E9p0379* + ID_MODEL_FROM_DATABASE=Plugable UGA-125 + +usb:v17E9p037A* + ID_MODEL_FROM_DATABASE=Plugable UGA-165 + +usb:v17E9p037B* + ID_MODEL_FROM_DATABASE=Plugable USB-VGA-165 + +usb:v17E9p037C* + ID_MODEL_FROM_DATABASE=Plugable DC-125 + +usb:v17E9p037D* + ID_MODEL_FROM_DATABASE=Plugable USB2-HDMI-165 + +usb:v17E9p410A* + ID_MODEL_FROM_DATABASE=HDMI Adapter + +usb:v17E9p430A* + ID_MODEL_FROM_DATABASE=HP Port Replicator (Composite Device) + +usb:v17E9p4312* + ID_MODEL_FROM_DATABASE=S2340T + +usb:v17EB* + ID_VENDOR_FROM_DATABASE=Cornice, Inc. + +usb:v17EF* + ID_VENDOR_FROM_DATABASE=Lenovo + +usb:v17EFp1000* + ID_MODEL_FROM_DATABASE=Hub + +usb:v17EFp1003* + ID_MODEL_FROM_DATABASE=Integrated Smart Card Reader + +usb:v17EFp1004* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v17EFp1008* + ID_MODEL_FROM_DATABASE=Hub + +usb:v17EFp100A* + ID_MODEL_FROM_DATABASE=ThinkPad Mini Dock Plus Series 3 + +usb:v17EFp304B* + ID_MODEL_FROM_DATABASE=AX88179 Gigabit Ethernet [ThinkPad OneLink GigaLAN] + +usb:v17EFp3815* + ID_MODEL_FROM_DATABASE=ChipsBnk 2GB USB Stick + +usb:v17EFp4802* + ID_MODEL_FROM_DATABASE=Lenovo Vc0323+MI1310_SOC Camera + +usb:v17EFp4807* + ID_MODEL_FROM_DATABASE=UVC Camera + +usb:v17EFp480C* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v17EFp480D* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp480E* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp480F* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4810* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4811* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4812* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4813* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4814* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4815* + ID_MODEL_FROM_DATABASE=Integrated Webcam [R5U877] + +usb:v17EFp4816* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v17EFp481C* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v17EFp481D* + ID_MODEL_FROM_DATABASE=Integrated Webcam + +usb:v17EFp6004* + ID_MODEL_FROM_DATABASE=ISD-V4 Tablet Pen + +usb:v17EFp6007* + ID_MODEL_FROM_DATABASE=Smartcard Keyboard + +usb:v17EFp6009* + ID_MODEL_FROM_DATABASE=ThinkPad Keyboard with TrackPoint + +usb:v17EFp6014* + ID_MODEL_FROM_DATABASE=Mini Wireless Keyboard N5901 + +usb:v17EFp6025* + ID_MODEL_FROM_DATABASE=ThinkPad Travel Mouse + +usb:v17EFp7203* + ID_MODEL_FROM_DATABASE=Ethernet adapter [U2L 100P-Y1] + +usb:v17EFp7423* + ID_MODEL_FROM_DATABASE=IdeaPad A1 Tablet + +usb:v17EFp7435* + ID_MODEL_FROM_DATABASE=A789 (Mass Storage mode, with debug) + +usb:v17EFp743A* + ID_MODEL_FROM_DATABASE=A789 (Mass Storage mode) + +usb:v17EFp7497* + ID_MODEL_FROM_DATABASE=A789 (MTP mode) + +usb:v17EFp7498* + ID_MODEL_FROM_DATABASE=A789 (MTP mode, with debug) + +usb:v17EFp749A* + ID_MODEL_FROM_DATABASE=A789 (PTP mode) + +usb:v17EFp749B* + ID_MODEL_FROM_DATABASE=A789 (PTP mode, with debug) + +usb:v17F4* + ID_VENDOR_FROM_DATABASE=WaveSense + +usb:v17F4pAAAA* + ID_MODEL_FROM_DATABASE=Jazz Blood Glucose Meter + +usb:v17F5* + ID_VENDOR_FROM_DATABASE=K.K. Rocky + +usb:v17F6* + ID_VENDOR_FROM_DATABASE=Unicomp, Inc + +usb:v17F6p0709* + ID_MODEL_FROM_DATABASE=Model M Keyboard + +usb:v1809* + ID_VENDOR_FROM_DATABASE=Advantech + +usb:v1809p4604* + ID_MODEL_FROM_DATABASE=USB-4604 + +usb:v1809p4761* + ID_MODEL_FROM_DATABASE=USB-4761 Portable Data Acquisition Module + +usb:v1822* + ID_VENDOR_FROM_DATABASE=Twinhan + +usb:v1822p3201* + ID_MODEL_FROM_DATABASE=VisionDTV USB-Ter/HAMA USB DVB-T device cold + +usb:v1822p3202* + ID_MODEL_FROM_DATABASE=VisionDTV USB-Ter/HAMA USB DVB-T device warm + +usb:v1831* + ID_VENDOR_FROM_DATABASE=Gwo Jinn Industries Co., Ltd. + +usb:v1832* + ID_VENDOR_FROM_DATABASE=Huizhou Shenghua Industrial Co., Ltd. + +usb:v183D* + ID_VENDOR_FROM_DATABASE=VIVOphone + +usb:v183Dp0010* + ID_MODEL_FROM_DATABASE=VoiceKey + +usb:v1843* + ID_VENDOR_FROM_DATABASE=Vaisala + +usb:v1849* + ID_VENDOR_FROM_DATABASE=ASRock Incorporation + +usb:v1852* + ID_VENDOR_FROM_DATABASE=GYROCOM C&C Co., LTD + +usb:v1852p7922* + ID_MODEL_FROM_DATABASE=Audiotrak DR.DAC2 DX [GYROCOM C&C] + +usb:v1854* + ID_VENDOR_FROM_DATABASE=Memory Devices Ltd. + +usb:v185B* + ID_VENDOR_FROM_DATABASE=Compro + +usb:v185Bp3020* + ID_MODEL_FROM_DATABASE=K100 Infrared Receiver + +usb:v185Bp3082* + ID_MODEL_FROM_DATABASE=K100 Infrared Receiver v2 + +usb:v185BpD000* + ID_MODEL_FROM_DATABASE=Compro Videomate DVB-U2000 - DVB-T USB cold + +usb:v185BpD001* + ID_MODEL_FROM_DATABASE=Compro Videomate DVB-U2000 - DVB-T USB warm + +usb:v1861* + ID_VENDOR_FROM_DATABASE=Tech Technology Industrial Company + +usb:v1862* + ID_VENDOR_FROM_DATABASE=Teridian Semiconductor Corp. + +usb:v1870* + ID_VENDOR_FROM_DATABASE=Nexio Co., Ltd + +usb:v1870p0001* + ID_MODEL_FROM_DATABASE=iNexio Touchscreen controller + +usb:v1871* + ID_VENDOR_FROM_DATABASE=Aveo Technology Corp. + +usb:v1871p0101* + ID_MODEL_FROM_DATABASE=UVC camera (Bresser microscope) + +usb:v1871p0141* + ID_MODEL_FROM_DATABASE=Camera + +usb:v1871p0D01* + ID_MODEL_FROM_DATABASE=USB2.0 Camera + +usb:v1873* + ID_VENDOR_FROM_DATABASE=Navilock + +usb:v1873pEE93* + ID_MODEL_FROM_DATABASE=EasyLogger + +usb:v187C* + ID_VENDOR_FROM_DATABASE=Alienware Corporation + +usb:v187Cp0511* + ID_MODEL_FROM_DATABASE=AlienFX Mobile lighting + +usb:v187Cp0600* + ID_MODEL_FROM_DATABASE=Dual Compatible Game Pad + +usb:v187F* + ID_VENDOR_FROM_DATABASE=Siano Mobile Silicon + +usb:v187Fp0010* + ID_MODEL_FROM_DATABASE=Stallar Board + +usb:v187Fp0100* + ID_MODEL_FROM_DATABASE=Stallar Board + +usb:v187Fp0200* + ID_MODEL_FROM_DATABASE=Nova A + +usb:v187Fp0201* + ID_MODEL_FROM_DATABASE=Nova B + +usb:v187Fp0202* + ID_MODEL_FROM_DATABASE=Nice + +usb:v187Fp0300* + ID_MODEL_FROM_DATABASE=Vega + +usb:v187Fp0301* + ID_MODEL_FROM_DATABASE=VeNice + +usb:v1892* + ID_VENDOR_FROM_DATABASE=Vast Technologies, Inc. + +usb:v1894* + ID_VENDOR_FROM_DATABASE=Topseed + +usb:v1894p5632* + ID_MODEL_FROM_DATABASE=Atek Tote Remote + +usb:v1894p5641* + ID_MODEL_FROM_DATABASE=TSAM-004 Presentation Remote + +usb:v1897* + ID_VENDOR_FROM_DATABASE=Evertop Wire Cable Co. + +usb:v189F* + ID_VENDOR_FROM_DATABASE=3Shape A/S + +usb:v189Fp0002* + ID_MODEL_FROM_DATABASE=Legato2 3D Scanner + +usb:v18A4* + ID_VENDOR_FROM_DATABASE=CSSN + +usb:v18A4p0001* + ID_MODEL_FROM_DATABASE=Snapshell IDR + +usb:v18A5* + ID_VENDOR_FROM_DATABASE=Verbatim, Ltd + +usb:v18A5p0214* + ID_MODEL_FROM_DATABASE=Portable Hard Drive + +usb:v18A5p0216* + ID_MODEL_FROM_DATABASE=External Hard Drive + +usb:v18A5p0218* + ID_MODEL_FROM_DATABASE=External Hard Drive + +usb:v18A5p0224* + ID_MODEL_FROM_DATABASE=Store 'n' Go Micro Plus + +usb:v18A5p0227* + ID_MODEL_FROM_DATABASE=Pocket Hard Drive + +usb:v18A5p022B* + ID_MODEL_FROM_DATABASE=Portable Hard Drive (Store'n'Go) + +usb:v18A5p0237* + ID_MODEL_FROM_DATABASE=Portable Harddrive + +usb:v18A5p0243* + ID_MODEL_FROM_DATABASE=Flash Drive (Store'n'Go) + +usb:v18A5p0302* + ID_MODEL_FROM_DATABASE=Flash Drive + +usb:v18A5p0304* + ID_MODEL_FROM_DATABASE=Store 'n' Go + +usb:v18A5p4123* + ID_MODEL_FROM_DATABASE=Store N Go + +usb:v18B1* + ID_VENDOR_FROM_DATABASE=Petalynx + +usb:v18B1p0037* + ID_MODEL_FROM_DATABASE=Maxter Remote Control + +usb:v18B4* + ID_VENDOR_FROM_DATABASE=e3C Technologies + +usb:v18B4p1001* + ID_MODEL_FROM_DATABASE=DUTV007 + +usb:v18B4p1002* + ID_MODEL_FROM_DATABASE=EC168 (v5) based USB DVB-T receiver + +usb:v18B4p1689* + ID_MODEL_FROM_DATABASE=DUTV009 + +usb:v18B4pFFFA* + ID_MODEL_FROM_DATABASE=EC168 (v2) based USB DVB-T receiver + +usb:v18B4pFFFB* + ID_MODEL_FROM_DATABASE=EC168 (v3) based USB DVB-T receiver + +usb:v18B6* + ID_VENDOR_FROM_DATABASE=Mikkon Technology Limited + +usb:v18B7* + ID_VENDOR_FROM_DATABASE=Zotek Electronic Co., Ltd. + +usb:v18C5* + ID_VENDOR_FROM_DATABASE=AMIT Technology, Inc. + +usb:v18C5p0002* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GO + +usb:v18C5p0008* + ID_MODEL_FROM_DATABASE=CG-WLUSB2GNR Corega Wireless USB Adapter + +usb:v18C5p0012* + ID_MODEL_FROM_DATABASE=CG-WLUSB10 Corega Wireless USB Adapter + +usb:v18CD* + ID_VENDOR_FROM_DATABASE=Ecamm + +usb:v18CDpCAFE* + ID_MODEL_FROM_DATABASE=Pico iMage + +usb:v18D1* + ID_VENDOR_FROM_DATABASE=Google Inc. + +usb:v18D1p0001* + ID_MODEL_FROM_DATABASE=Onda V972 (storage access) + +usb:v18D1p0003* + ID_MODEL_FROM_DATABASE=Android-powered device using AllWinner Technology SoC + +usb:v18D1p0006* + ID_MODEL_FROM_DATABASE=Onda V972 MTP + +usb:v18D1p0008* + ID_MODEL_FROM_DATABASE=Onda V972 PTP (camera) + +usb:v18D1p0D02* + ID_MODEL_FROM_DATABASE=Celkon A88 + +usb:v18D1p2D00* + ID_MODEL_FROM_DATABASE=Android-powered device in accessory mode + +usb:v18D1p2D01* + ID_MODEL_FROM_DATABASE=Android-powered device in accessory mode with ADB support + +usb:v18D1p4E11* + ID_MODEL_FROM_DATABASE=Nexus One + +usb:v18D1p4E12* + ID_MODEL_FROM_DATABASE=Nexus One (debug) + +usb:v18D1p4E13* + ID_MODEL_FROM_DATABASE=Nexus One (tether) + +usb:v18D1p4E20* + ID_MODEL_FROM_DATABASE=Nexus S (fastboot) + +usb:v18D1p4E21* + ID_MODEL_FROM_DATABASE=Nexus S + +usb:v18D1p4E22* + ID_MODEL_FROM_DATABASE=Nexus S (debug) + +usb:v18D1p4E24* + ID_MODEL_FROM_DATABASE=Nexus S (tether) + +usb:v18D1p4E30* + ID_MODEL_FROM_DATABASE=Galaxy Nexus (fastboot) + +usb:v18D1p4E40* + ID_MODEL_FROM_DATABASE=Nexus 7 (fastboot) + +usb:v18D1p4E41* + ID_MODEL_FROM_DATABASE=Nexus 7 (MTP) + +usb:v18D1p4E42* + ID_MODEL_FROM_DATABASE=Nexus 7 (debug) + +usb:v18D1p4E43* + ID_MODEL_FROM_DATABASE=Nexus 7 (PTP) + +usb:v18D1p4E44* + ID_MODEL_FROM_DATABASE=Nexus 7 2012 (PTP) + +usb:v18D1p4EE0* + ID_MODEL_FROM_DATABASE=Nexus 4 (bootloader) + +usb:v18D1p4EE1* + ID_MODEL_FROM_DATABASE=Nexus Device (MTP) + +usb:v18D1p4EE2* + ID_MODEL_FROM_DATABASE=Nexus Device (debug) + +usb:v18D1p4EE3* + ID_MODEL_FROM_DATABASE=Nexus 4/5/7/10 (tether) + +usb:v18D1p4EE4* + ID_MODEL_FROM_DATABASE=Nexus 4/5/7/10 (debug + tether) + +usb:v18D1p4EE5* + ID_MODEL_FROM_DATABASE=Nexus 4 (PTP) + +usb:v18D1p4EE6* + ID_MODEL_FROM_DATABASE=Nexus 4/5 (PTP + debug) + +usb:v18D1p7102* + ID_MODEL_FROM_DATABASE=Toshiba Thrive tablet + +usb:v18D1pB004* + ID_MODEL_FROM_DATABASE=Pandigital / B&N Novel 9" tablet + +usb:v18D1pD001* + ID_MODEL_FROM_DATABASE=Nexus 4 (fastboot) + +usb:v18D1pD002* + ID_MODEL_FROM_DATABASE=Nexus 4 (debug) + +usb:v18D1pD109* + ID_MODEL_FROM_DATABASE=LG G2x MTP + +usb:v18D1pD10A* + ID_MODEL_FROM_DATABASE=LG G2x MTP (debug) + +usb:v18D5* + ID_VENDOR_FROM_DATABASE=Starline International Group Limited + +usb:v18D9* + ID_VENDOR_FROM_DATABASE=Kaba + +usb:v18D9p01A0* + ID_MODEL_FROM_DATABASE=B-Net 91 07 + +usb:v18DC* + ID_VENDOR_FROM_DATABASE=LKC Technologies, Inc. + +usb:v18DD* + ID_VENDOR_FROM_DATABASE=Planon System Solutions Inc. + +usb:v18DDp1000* + ID_MODEL_FROM_DATABASE=DocuPen RC800 + +usb:v18E3* + ID_VENDOR_FROM_DATABASE=Fitipower Integrated Technology Inc + +usb:v18E3p7102* + ID_MODEL_FROM_DATABASE=Multi Card Reader (Internal) + +usb:v18E3p9101* + ID_MODEL_FROM_DATABASE=All-in-1 Card Reader + +usb:v18E3p9102* + ID_MODEL_FROM_DATABASE=Multi Card Reader + +usb:v18E3p9512* + ID_MODEL_FROM_DATABASE=Webcam + +usb:v18E8* + ID_VENDOR_FROM_DATABASE=Qcom + +usb:v18E8p6144* + ID_MODEL_FROM_DATABASE=LR802UA 802.11b Wireless Adapter [ALi M4301AU] + +usb:v18E8p6196* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v18E8p6229* + ID_MODEL_FROM_DATABASE=RT2573 + +usb:v18E8p6232* + ID_MODEL_FROM_DATABASE=Wireless 802.11g 54Mbps Network Adapter [RTL8187] + +usb:v18EA* + ID_VENDOR_FROM_DATABASE=Matrox Graphics, Inc. + +usb:v18EAp0002* + ID_MODEL_FROM_DATABASE=DualHead2Go [Analog Edition] + +usb:v18EAp0004* + ID_MODEL_FROM_DATABASE=TripleHead2Go [Digital Edition] + +usb:v18EC* + ID_VENDOR_FROM_DATABASE=Arkmicro Technologies Inc. + +usb:v18ECp3118* + ID_MODEL_FROM_DATABASE=USB to IrDA adapter [ARK3116T] + +usb:v18ECp3188* + ID_MODEL_FROM_DATABASE=ARK3188 UVC Webcam + +usb:v18ECp3299* + ID_MODEL_FROM_DATABASE=Webcam Carrefour + +usb:v18ECp3366* + ID_MODEL_FROM_DATABASE=Bresser Biolux NV + +usb:v18FD* + ID_VENDOR_FROM_DATABASE=FineArch Inc. + +usb:v1908* + ID_VENDOR_FROM_DATABASE=GEMBIRD + +usb:v1908p1320* + ID_MODEL_FROM_DATABASE=PhotoFrame PF-15-1 + +usb:v190D* + ID_VENDOR_FROM_DATABASE=Motorola GSG + +usb:v1914* + ID_VENDOR_FROM_DATABASE=Alco Digital Devices Limited + +usb:v1915* + ID_VENDOR_FROM_DATABASE=Nordic Semiconductor ASA + +usb:v1915p000C* + ID_MODEL_FROM_DATABASE=Wireless Desktop nRF24L01 CX-1766 + +usb:v1915p2233* + ID_MODEL_FROM_DATABASE=Linksys WUSB11 v2.8 802.11b Adapter [Atmel AT76C505] + +usb:v1915p2234* + ID_MODEL_FROM_DATABASE=Linksys WUSB54G v1 OEM 802.11g Adapter [Intersil ISL3886] + +usb:v1915p2235* + ID_MODEL_FROM_DATABASE=Linksys WUSB54GP v1 OEM 802.11g Adapter [Intersil ISL3886] + +usb:v1915p2236* + ID_MODEL_FROM_DATABASE=Linksys WUSB11 v3.0 802.11b Adapter [Intersil PRISM 3] + +usb:v1923* + ID_VENDOR_FROM_DATABASE=FitLinxx + +usb:v1923p0002* + ID_MODEL_FROM_DATABASE=Personal SyncPoint + +usb:v1926* + ID_VENDOR_FROM_DATABASE=NextWindow + +usb:v1926p0003* + ID_MODEL_FROM_DATABASE=1900 HID Touchscreen + +usb:v1926p0006* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0064* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0065* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0066* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0067* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0068* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0069* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0071* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0072* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0073* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0074* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0075* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0076* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0077* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0078* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0079* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p007A* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p007E* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p007F* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0080* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0081* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0082* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0083* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0084* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0085* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0086* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0087* + ID_MODEL_FROM_DATABASE=1950 HID Touchscreen + +usb:v1926p0DC2* + ID_MODEL_FROM_DATABASE=HID Touchscreen + +usb:v192F* + ID_VENDOR_FROM_DATABASE=Avago Technologies, Pte. + +usb:v192Fp0000* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v192Fp0416* + ID_MODEL_FROM_DATABASE=ADNS-5700 Optical Mouse Controller (3-button) + +usb:v192Fp0616* + ID_MODEL_FROM_DATABASE=ADNS-5700 Optical Mouse Controller (5-button) + +usb:v1930* + ID_VENDOR_FROM_DATABASE=Shenzhen Xianhe Technology Co., Ltd. + +usb:v1931* + ID_VENDOR_FROM_DATABASE=Ningbo Broad Telecommunication Co., Ltd. + +usb:v1934* + ID_VENDOR_FROM_DATABASE=Feature Integration Technology Inc. (Fintek) + +usb:v1934p0602* + ID_MODEL_FROM_DATABASE=F71610 or F71612 Consumer Infrared Receiver/Transceiver + +usb:v1934p0702* + ID_MODEL_FROM_DATABASE=Integrated Consumer Infrared Receiver/Transceiver + +usb:v1934p5168* + ID_MODEL_FROM_DATABASE=F71610A or F71612A Consumer Infrared Receiver/Transceiver + +usb:v1941* + ID_VENDOR_FROM_DATABASE=Dream Link + +usb:v1941p8021* + ID_MODEL_FROM_DATABASE=WH1080 Weather Station / USB Missile Launcher + +usb:v1943* + ID_VENDOR_FROM_DATABASE=Sensoray Co., Inc. + +usb:v1943p2250* + ID_MODEL_FROM_DATABASE=Model 2250 MPEG and JPEG Capture Card + +usb:v1943p2253* + ID_MODEL_FROM_DATABASE=Model 2253 Audio/Video Codec Card + +usb:v1943p2255* + ID_MODEL_FROM_DATABASE=Model 2255 4 Channel Capture Card + +usb:v1943p2257* + ID_MODEL_FROM_DATABASE=Model 2257 4 Channel Capture Card + +usb:v1943pA250* + ID_MODEL_FROM_DATABASE=Model 2250 MPEG and JPEG Capture Card (cold) + +usb:v1943pA253* + ID_MODEL_FROM_DATABASE=Model 2253 Audio/Video Codec Card (cold) + +usb:v1949* + ID_VENDOR_FROM_DATABASE=Lab126, Inc. + +usb:v1949p0002* + ID_MODEL_FROM_DATABASE=Amazon Kindle + +usb:v1949p0004* + ID_MODEL_FROM_DATABASE=Amazon Kindle 3/4/Paperwhite + +usb:v1949p0006* + ID_MODEL_FROM_DATABASE=Kindle Fire + +usb:v1949p0008* + ID_MODEL_FROM_DATABASE=Amazon Kindle Fire HD 8.9" + +usb:v194F* + ID_VENDOR_FROM_DATABASE=PreSonus Audio Electronics, Inc. + +usb:v194Fp0101* + ID_MODEL_FROM_DATABASE=AudioBox 22 VSL + +usb:v194Fp0102* + ID_MODEL_FROM_DATABASE=AudioBox 44 VSL + +usb:v194Fp0103* + ID_MODEL_FROM_DATABASE=AudioBox 1818 VSL + +usb:v194Fp0301* + ID_MODEL_FROM_DATABASE=AudioBox + +usb:v1951* + ID_VENDOR_FROM_DATABASE=Hyperstone AG + +usb:v1953* + ID_VENDOR_FROM_DATABASE=Ironkey Inc. + +usb:v1953p0202* + ID_MODEL_FROM_DATABASE=S200 2GB Rev. 1 + +usb:v1954* + ID_VENDOR_FROM_DATABASE=Radiient Technologies + +usb:v195D* + ID_VENDOR_FROM_DATABASE=Itron Technology iONE + +usb:v195Dp7002* + ID_MODEL_FROM_DATABASE=Libra-Q11 IR remote + +usb:v195Dp7006* + ID_MODEL_FROM_DATABASE=Libra-Q26 / 1.0 Remote + +usb:v195Dp7777* + ID_MODEL_FROM_DATABASE=Scorpius wireless keyboard + +usb:v195Dp7779* + ID_MODEL_FROM_DATABASE=Scorpius-P20MT + +usb:v1965* + ID_VENDOR_FROM_DATABASE=Uniden Corporation + +usb:v1965p0016* + ID_MODEL_FROM_DATABASE=HomePatrol-1 + +usb:v1967* + ID_VENDOR_FROM_DATABASE=CASIO HITACHI Mobile Communications Co., Ltd. + +usb:v196B* + ID_VENDOR_FROM_DATABASE=Wispro Technology Inc. + +usb:v1970* + ID_VENDOR_FROM_DATABASE=Dane-Elec Corp. USA + +usb:v1970p0000* + ID_MODEL_FROM_DATABASE=Z Mate 16GB + +usb:v1975* + ID_VENDOR_FROM_DATABASE=Dongguan Guneetal Wire & Cable Co., Ltd. + +usb:v1976* + ID_VENDOR_FROM_DATABASE=Chipsbrand Microelectronics (HK) Co., Ltd. + +usb:v1976p6025* + ID_MODEL_FROM_DATABASE=Flash Drive 512 MB + +usb:v1977* + ID_VENDOR_FROM_DATABASE=T-Logic + +usb:v1977p0111* + ID_MODEL_FROM_DATABASE=TL203 MP3 Player and Voice Recorder + +usb:v197D* + ID_VENDOR_FROM_DATABASE=Leuze electronic + +usb:v197Dp0222* + ID_MODEL_FROM_DATABASE=BCL 508i + +usb:v1989* + ID_VENDOR_FROM_DATABASE=Nuconn Technology Corp. + +usb:v198F* + ID_VENDOR_FROM_DATABASE=Beceem Communications Inc. + +usb:v198Fp0210* + ID_MODEL_FROM_DATABASE=BCS200 WiMAX Adapter + +usb:v198Fp0220* + ID_MODEL_FROM_DATABASE=BCSM250 WiMAX Adapter + +usb:v1990* + ID_VENDOR_FROM_DATABASE=Acron Precision Industrial Co., Ltd. + +usb:v1995* + ID_VENDOR_FROM_DATABASE=Trillium Technology Pty. Ltd. + +usb:v1995p3202* + ID_MODEL_FROM_DATABASE=REC-ADPT-USB (recorder) + +usb:v1995p3203* + ID_MODEL_FROM_DATABASE=REC-A-ADPT-USB (recorder) + +usb:v1996* + ID_VENDOR_FROM_DATABASE=PixeLINK + +usb:v1996p3010* + ID_MODEL_FROM_DATABASE=Camera Release 4 + +usb:v1996p3011* + ID_MODEL_FROM_DATABASE=OEM Camera + +usb:v1996p3012* + ID_MODEL_FROM_DATABASE=e-ImageData Corp. ScanPro + +usb:v199B* + ID_VENDOR_FROM_DATABASE=MicroStrain, Inc. + +usb:v199Bp3065* + ID_MODEL_FROM_DATABASE=3DM-GX3-25 Orientation Sensor + +usb:v199E* + ID_VENDOR_FROM_DATABASE=The Imaging Source Europe GmbH + +usb:v199Ep8101* + ID_MODEL_FROM_DATABASE=DFx 21BU04 Camera + +usb:v199F* + ID_VENDOR_FROM_DATABASE=Benica Corporation + +usb:v19A8* + ID_VENDOR_FROM_DATABASE=Biforst Technology Inc. + +usb:v19AB* + ID_VENDOR_FROM_DATABASE=Bodelin + +usb:v19ABp1000* + ID_MODEL_FROM_DATABASE=ProScope HR + +usb:v19AF* + ID_VENDOR_FROM_DATABASE=S Life + +usb:v19AFp6611* + ID_MODEL_FROM_DATABASE=Celestia VoIP Phone + +usb:v19B2* + ID_VENDOR_FROM_DATABASE=Batronix + +usb:v19B2p0010* + ID_MODEL_FROM_DATABASE=BX32 Batupo + +usb:v19B2p0011* + ID_MODEL_FROM_DATABASE=BX32P Barlino + +usb:v19B2p0012* + ID_MODEL_FROM_DATABASE=BX40 Bagero + +usb:v19B2p0013* + ID_MODEL_FROM_DATABASE=BX48 Batego + +usb:v19B4* + ID_VENDOR_FROM_DATABASE=Celestron + +usb:v19B4p0002* + ID_MODEL_FROM_DATABASE=SkyScout Personal Planetarium + +usb:v19B4p0101* + ID_MODEL_FROM_DATABASE=Handheld Digital Microscope 44302 + +usb:v19B5* + ID_VENDOR_FROM_DATABASE=B & W Group + +usb:v19B6* + ID_VENDOR_FROM_DATABASE=Infotech Logistic, LLC + +usb:v19B9* + ID_VENDOR_FROM_DATABASE=Data Robotics + +usb:v19B9p8D20* + ID_MODEL_FROM_DATABASE=Drobo Elite + +usb:v19C2* + ID_VENDOR_FROM_DATABASE=Futuba + +usb:v19C2p6A11* + ID_MODEL_FROM_DATABASE=MDM166A Fluorescent Display + +usb:v19CA* + ID_VENDOR_FROM_DATABASE=Mindtribe + +usb:v19CAp0001* + ID_MODEL_FROM_DATABASE=Sandio 3D HID Mouse + +usb:v19CF* + ID_VENDOR_FROM_DATABASE=Parrot SA + +usb:v19D2* + ID_VENDOR_FROM_DATABASE=ZTE WCDMA Technologies MSM + +usb:v19D2p0001* + ID_MODEL_FROM_DATABASE=CDMA Wireless Modem + +usb:v19D2p0002* + ID_MODEL_FROM_DATABASE=MF632/ONDA ET502HS/MT505UP + +usb:v19D2p0007* + ID_MODEL_FROM_DATABASE=TU25 WiMAX Adapter [Beceem BCS200] + +usb:v19D2p0031* + ID_MODEL_FROM_DATABASE=MF110/MF627/MF636 + +usb:v19D2p0063* + ID_MODEL_FROM_DATABASE=K3565-Z HSDPA + +usb:v19D2p0064* + ID_MODEL_FROM_DATABASE=MF627 AU + +usb:v19D2p0083* + ID_MODEL_FROM_DATABASE=MF190 + +usb:v19D2p0103* + ID_MODEL_FROM_DATABASE=MF112 + +usb:v19D2p0104* + ID_MODEL_FROM_DATABASE=K4505-Z + +usb:v19D2p0146* + ID_MODEL_FROM_DATABASE=MF 195E (HSPA+ Modem) + +usb:v19D2p0167* + ID_MODEL_FROM_DATABASE=MF820 4G LTE + +usb:v19D2p0172* + ID_MODEL_FROM_DATABASE=AX226 WIMAX MODEM (After Modeswitch) + +usb:v19D2p0325* + ID_MODEL_FROM_DATABASE=LTE4G O2 ZTE MF821D LTE/UMTS/GSM Modem/Networkcard + +usb:v19D2p0326* + ID_MODEL_FROM_DATABASE=LTE4G O2 ZTE MF821D LTE/UMTS/GSM Modem/Networkcard + +usb:v19D2p1008* + ID_MODEL_FROM_DATABASE=K3570-Z + +usb:v19D2p1010* + ID_MODEL_FROM_DATABASE=K3571-Z + +usb:v19D2p1017* + ID_MODEL_FROM_DATABASE=K5006-Z vodafone LTE/UMTS/GSM Modem/Networkcard + +usb:v19D2p1018* + ID_MODEL_FROM_DATABASE=K5006-Z vodafone LTE/UMTS/GSM Modem/Networkcard + +usb:v19D2p1203* + ID_MODEL_FROM_DATABASE=MF691 [ T-Mobile webConnect Rocket 2.0] + +usb:v19D2p1217* + ID_MODEL_FROM_DATABASE=MF652 + +usb:v19D2p1218* + ID_MODEL_FROM_DATABASE=MF652 + +usb:v19D2p2000* + ID_MODEL_FROM_DATABASE=MF627/MF628/MF628+/MF636+ HSDPA/HSUPA + +usb:v19D2pFFF2* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v19D2pFFF3* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v19DB* + ID_VENDOR_FROM_DATABASE=KFI Printers + +usb:v19DBp02F1* + ID_MODEL_FROM_DATABASE=NAUT324C + +usb:v19E1* + ID_VENDOR_FROM_DATABASE=WeiDuan Electronic Accessory (S.Z.) Co., Ltd. + +usb:v19E8* + ID_VENDOR_FROM_DATABASE=Industrial Technology Research Institute + +usb:v19EF* + ID_VENDOR_FROM_DATABASE=Pak Heng Technology (Shenzhen) Co., Ltd. + +usb:v19F7* + ID_VENDOR_FROM_DATABASE=RODE Microphones + +usb:v19F7p0001* + ID_MODEL_FROM_DATABASE=Podcaster + +usb:v19FA* + ID_VENDOR_FROM_DATABASE=Gampaq Co.Ltd + +usb:v19FAp0703* + ID_MODEL_FROM_DATABASE=Steering Wheel + +usb:v19FF* + ID_VENDOR_FROM_DATABASE=Dynex + +usb:v19FFp0102* + ID_MODEL_FROM_DATABASE=1.3MP Webcam + +usb:v19FFp0201* + ID_MODEL_FROM_DATABASE=Rocketfish Wireless 2.4G Laser Mouse + +usb:v19FFp0238* + ID_MODEL_FROM_DATABASE=DX-WRM1401 Mouse + +usb:v1A08* + ID_VENDOR_FROM_DATABASE=Bellwood International, Inc. + +usb:v1A0A* + ID_VENDOR_FROM_DATABASE=USB-IF non-workshop + +usb:v1A0ApBADD* + ID_MODEL_FROM_DATABASE=USB OTG Compliance test device + +usb:v1A12* + ID_VENDOR_FROM_DATABASE=KES Co., Ltd. + +usb:v1A1D* + ID_VENDOR_FROM_DATABASE=Veho + +usb:v1A1Dp0407* + ID_MODEL_FROM_DATABASE=Mimi WiFi speakers + +usb:v1A25* + ID_VENDOR_FROM_DATABASE=Amphenol East Asia Ltd. + +usb:v1A2A* + ID_VENDOR_FROM_DATABASE=Seagate Branded Solutions + +usb:v1A2C* + ID_VENDOR_FROM_DATABASE=China Resource Semico Co., Ltd + +usb:v1A2Cp0021* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v1A2Cp0024* + ID_MODEL_FROM_DATABASE=Multimedia Keyboard + +usb:v1A32* + ID_VENDOR_FROM_DATABASE=Quanta Microsystems, Inc. + +usb:v1A32p0304* + ID_MODEL_FROM_DATABASE=802.11n Wireless LAN Card + +usb:v1A34* + ID_VENDOR_FROM_DATABASE=ACRUX + +usb:v1A34p0802* + ID_MODEL_FROM_DATABASE=Gamepad + +usb:v1A36* + ID_VENDOR_FROM_DATABASE=Biwin Technology Ltd. + +usb:v1A40* + ID_VENDOR_FROM_DATABASE=Terminus Technology Inc. + +usb:v1A40p0101* + ID_MODEL_FROM_DATABASE=Hub + +usb:v1A40p0201* + ID_MODEL_FROM_DATABASE=FE 2.1 7-port Hub + +usb:v1A41* + ID_VENDOR_FROM_DATABASE=Action Electronics Co., Ltd. + +usb:v1A44* + ID_VENDOR_FROM_DATABASE=VASCO Data Security International + +usb:v1A44p0001* + ID_MODEL_FROM_DATABASE=Digipass 905 SmartCard Reader + +usb:v1A4A* + ID_VENDOR_FROM_DATABASE=Silicon Image + +usb:v1A4B* + ID_VENDOR_FROM_DATABASE=SafeBoot International B.V. + +usb:v1A5A* + ID_VENDOR_FROM_DATABASE=Tandberg Data + +usb:v1A61* + ID_VENDOR_FROM_DATABASE=Abbott Diabetes Care + +usb:v1A61p3410* + ID_MODEL_FROM_DATABASE=CoPilot System Cable + +usb:v1A6A* + ID_VENDOR_FROM_DATABASE=Spansion Inc. + +usb:v1A6D* + ID_VENDOR_FROM_DATABASE=SamYoung Electronics Co., Ltd + +usb:v1A6E* + ID_VENDOR_FROM_DATABASE=Global Unichip Corp. + +usb:v1A6F* + ID_VENDOR_FROM_DATABASE=Sagem Orga GmbH + +usb:v1A72* + ID_VENDOR_FROM_DATABASE=Physik Instrumente + +usb:v1A72p1008* + ID_MODEL_FROM_DATABASE=E-861 PiezoWalk NEXACT Controller + +usb:v1A79* + ID_VENDOR_FROM_DATABASE=Bayer Health Care LLC + +usb:v1A79p6002* + ID_MODEL_FROM_DATABASE=Contour + +usb:v1A79p7410* + ID_MODEL_FROM_DATABASE=Contour Next + +usb:v1A7B* + ID_VENDOR_FROM_DATABASE=Lumberg Connect GmbH & Co. KG + +usb:v1A7C* + ID_VENDOR_FROM_DATABASE=Evoluent + +usb:v1A7Cp0068* + ID_MODEL_FROM_DATABASE=VerticalMouse 3 + +usb:v1A7Cp0168* + ID_MODEL_FROM_DATABASE=VerticalMouse 3 Wireless + +usb:v1A7Cp0191* + ID_MODEL_FROM_DATABASE=VerticalMouse 4 + +usb:v1A81* + ID_VENDOR_FROM_DATABASE=Holtek Semiconductor, Inc. + +usb:v1A81p2203* + ID_MODEL_FROM_DATABASE=Laser Gaming mouse + +usb:v1A81p2204* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v1A81p2205* + ID_MODEL_FROM_DATABASE=Laser Mouse + +usb:v1A86* + ID_VENDOR_FROM_DATABASE=QinHeng Electronics + +usb:v1A86p5512* + ID_MODEL_FROM_DATABASE=CH341 in EPP/MEM/I2C mode, EPP/I2C adapter + +usb:v1A86p5523* + ID_MODEL_FROM_DATABASE=CH341 in serial mode, usb to serial port converter + +usb:v1A86p5584* + ID_MODEL_FROM_DATABASE=CH341 in parallel mode, usb to printer port converter + +usb:v1A86p7523* + ID_MODEL_FROM_DATABASE=HL-340 USB-Serial adapter + +usb:v1A86p752D* + ID_MODEL_FROM_DATABASE=CH345 MIDI adapter + +usb:v1A86p7584* + ID_MODEL_FROM_DATABASE=CH340S + +usb:v1A86pE008* + ID_MODEL_FROM_DATABASE=HID-based serial adapater + +usb:v1A89* + ID_VENDOR_FROM_DATABASE=Dynalith Systems Co., Ltd. + +usb:v1A8B* + ID_VENDOR_FROM_DATABASE=SGS Taiwan Ltd. + +usb:v1A8D* + ID_VENDOR_FROM_DATABASE=BandRich, Inc. + +usb:v1A8Dp1002* + ID_MODEL_FROM_DATABASE=BandLuxe 3.5G HSDPA Adapter + +usb:v1A8Dp1009* + ID_MODEL_FROM_DATABASE=BandLuxe 3.5G HSPA Adapter + +usb:v1A8Dp100D* + ID_MODEL_FROM_DATABASE=4G LTE adapter + +usb:v1A98* + ID_VENDOR_FROM_DATABASE=Leica Camera AG + +usb:v1AA4* + ID_VENDOR_FROM_DATABASE=Data Drive Thru, Inc. + +usb:v1AA5* + ID_VENDOR_FROM_DATABASE=UBeacon Technologies, Inc. + +usb:v1AA6* + ID_VENDOR_FROM_DATABASE=eFortune Technology Corp. + +usb:v1AAD* + ID_VENDOR_FROM_DATABASE=KeeTouch + +usb:v1AADp0001* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v1AB1* + ID_VENDOR_FROM_DATABASE=Rigol Technologies + +usb:v1AB1p0588* + ID_MODEL_FROM_DATABASE=DS1000 SERIES + +usb:v1ACB* + ID_VENDOR_FROM_DATABASE=Salcomp Plc + +usb:v1ACC* + ID_VENDOR_FROM_DATABASE=Midiplus Co, Ltd. + +usb:v1ACCp0103* + ID_MODEL_FROM_DATABASE=AudioLink plus 4x4 2.9.28 + +usb:v1AD1* + ID_VENDOR_FROM_DATABASE=Desay Wire Co., Ltd. + +usb:v1AD4* + ID_VENDOR_FROM_DATABASE=APS + +usb:v1AD4p0002* + ID_MODEL_FROM_DATABASE=KM290-HRS + +usb:v1ADB* + ID_VENDOR_FROM_DATABASE=SEL C662 Serial Cable + +usb:v1AE4* + ID_VENDOR_FROM_DATABASE=ic-design Reinhard Gottinger GmbH + +usb:v1AE7* + ID_VENDOR_FROM_DATABASE=X-TENSIONS + +usb:v1AE7p0381* + ID_MODEL_FROM_DATABASE=VS-DVB-T 380U (af9015 based) + +usb:v1AE7p2001* + ID_MODEL_FROM_DATABASE=SpeedLink Snappy Mic webcam (SL-6825-SBK) + +usb:v1AE7p9003* + ID_MODEL_FROM_DATABASE=SpeedLink Vicious And Devine Laplace webcam, white (VD-1504-SWT) + +usb:v1AE7p9004* + ID_MODEL_FROM_DATABASE=SpeedLink Vicious And Devine Laplace webcam, black (VD-1504-SBK) + +usb:v1AED* + ID_VENDOR_FROM_DATABASE=High Top Precision Electronic Co., Ltd. + +usb:v1AEF* + ID_VENDOR_FROM_DATABASE=Conntech Electronic (Suzhou) Corporation + +usb:v1AF1* + ID_VENDOR_FROM_DATABASE=Connect One Ltd. + +usb:v1AFE* + ID_VENDOR_FROM_DATABASE=A. Eberle GmbH & Co. KG + +usb:v1AFEp0001* + ID_MODEL_FROM_DATABASE=PQ Box 100 + +usb:v1B04* + ID_VENDOR_FROM_DATABASE=Meilhaus Electronic GmbH + +usb:v1B04p0630* + ID_MODEL_FROM_DATABASE=ME-630 + +usb:v1B04p0940* + ID_MODEL_FROM_DATABASE=ME-94 + +usb:v1B04p0950* + ID_MODEL_FROM_DATABASE=ME-95 + +usb:v1B04p0960* + ID_MODEL_FROM_DATABASE=ME-96 + +usb:v1B04p1000* + ID_MODEL_FROM_DATABASE=ME-1000 + +usb:v1B04p100A* + ID_MODEL_FROM_DATABASE=ME-1000 + +usb:v1B04p100B* + ID_MODEL_FROM_DATABASE=ME-1000 + +usb:v1B04p1400* + ID_MODEL_FROM_DATABASE=ME-1400 + +usb:v1B04p140A* + ID_MODEL_FROM_DATABASE=ME-1400A + +usb:v1B04p140B* + ID_MODEL_FROM_DATABASE=ME-1400B + +usb:v1B04p140C* + ID_MODEL_FROM_DATABASE=ME-1400C + +usb:v1B04p140D* + ID_MODEL_FROM_DATABASE=ME-1400D + +usb:v1B04p140E* + ID_MODEL_FROM_DATABASE=ME-1400E + +usb:v1B04p14EA* + ID_MODEL_FROM_DATABASE=ME-1400EA + +usb:v1B04p14EB* + ID_MODEL_FROM_DATABASE=ME-1400EB + +usb:v1B04p1604* + ID_MODEL_FROM_DATABASE=ME-1600/4U + +usb:v1B04p1608* + ID_MODEL_FROM_DATABASE=ME-1600/8U + +usb:v1B04p160C* + ID_MODEL_FROM_DATABASE=ME-1600/12U + +usb:v1B04p160F* + ID_MODEL_FROM_DATABASE=ME-1600/16U + +usb:v1B04p168F* + ID_MODEL_FROM_DATABASE=ME-1600/16U8I + +usb:v1B04p4610* + ID_MODEL_FROM_DATABASE=ME-4610 + +usb:v1B04p4650* + ID_MODEL_FROM_DATABASE=ME-4650 + +usb:v1B04p4660* + ID_MODEL_FROM_DATABASE=ME-4660 + +usb:v1B04p4661* + ID_MODEL_FROM_DATABASE=ME-4660I + +usb:v1B04p4662* + ID_MODEL_FROM_DATABASE=ME-4660 + +usb:v1B04p4663* + ID_MODEL_FROM_DATABASE=ME-4660I + +usb:v1B04p4670* + ID_MODEL_FROM_DATABASE=ME-4670 + +usb:v1B04p4671* + ID_MODEL_FROM_DATABASE=ME-4670I + +usb:v1B04p4672* + ID_MODEL_FROM_DATABASE=ME-4670S + +usb:v1B04p4673* + ID_MODEL_FROM_DATABASE=ME-4670IS + +usb:v1B04p4680* + ID_MODEL_FROM_DATABASE=ME-4680 + +usb:v1B04p4681* + ID_MODEL_FROM_DATABASE=ME-4680I + +usb:v1B04p4682* + ID_MODEL_FROM_DATABASE=ME-4680S + +usb:v1B04p4683* + ID_MODEL_FROM_DATABASE=ME-4680IS + +usb:v1B04p6004* + ID_MODEL_FROM_DATABASE=ME-6000/4 + +usb:v1B04p6008* + ID_MODEL_FROM_DATABASE=ME-6000/8 + +usb:v1B04p600F* + ID_MODEL_FROM_DATABASE=ME-6000/16 + +usb:v1B04p6014* + ID_MODEL_FROM_DATABASE=ME-6000I/4 + +usb:v1B04p6018* + ID_MODEL_FROM_DATABASE=ME-6000I/8 + +usb:v1B04p601F* + ID_MODEL_FROM_DATABASE=ME-6000I/16 + +usb:v1B04p6034* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/4 + +usb:v1B04p6038* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/8 + +usb:v1B04p603F* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/16 + +usb:v1B04p6044* + ID_MODEL_FROM_DATABASE=ME-6000/4/DIO + +usb:v1B04p6048* + ID_MODEL_FROM_DATABASE=ME-6000/8/DIO + +usb:v1B04p604F* + ID_MODEL_FROM_DATABASE=ME-6000/16/DIO + +usb:v1B04p6054* + ID_MODEL_FROM_DATABASE=ME-6000I/4/DIO + +usb:v1B04p6058* + ID_MODEL_FROM_DATABASE=ME-6000I/8/DIO + +usb:v1B04p605F* + ID_MODEL_FROM_DATABASE=ME-6000I/16/DIO + +usb:v1B04p6074* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/4/DIO + +usb:v1B04p6078* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/8/DIO + +usb:v1B04p607F* + ID_MODEL_FROM_DATABASE=ME-6000ISLE/16/DIO + +usb:v1B04p6104* + ID_MODEL_FROM_DATABASE=ME-6100/4 + +usb:v1B04p6108* + ID_MODEL_FROM_DATABASE=ME-6100/8 + +usb:v1B04p610F* + ID_MODEL_FROM_DATABASE=ME-6100/16 + +usb:v1B04p6114* + ID_MODEL_FROM_DATABASE=ME-6100I/4 + +usb:v1B04p6118* + ID_MODEL_FROM_DATABASE=ME-6100I/8 + +usb:v1B04p611F* + ID_MODEL_FROM_DATABASE=ME-6100I/16 + +usb:v1B04p6134* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/4 + +usb:v1B04p6138* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/8 + +usb:v1B04p613F* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/16 + +usb:v1B04p6144* + ID_MODEL_FROM_DATABASE=ME-6100/4/DIO + +usb:v1B04p6148* + ID_MODEL_FROM_DATABASE=ME-6100/8/DIO + +usb:v1B04p614F* + ID_MODEL_FROM_DATABASE=ME-6100/16/DIO + +usb:v1B04p6154* + ID_MODEL_FROM_DATABASE=ME-6100I/4/DIO + +usb:v1B04p6158* + ID_MODEL_FROM_DATABASE=ME-6100I/8/DIO + +usb:v1B04p615F* + ID_MODEL_FROM_DATABASE=ME-6100I/16/DIO + +usb:v1B04p6174* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/4/DIO + +usb:v1B04p6178* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/8/DIO + +usb:v1B04p617F* + ID_MODEL_FROM_DATABASE=ME-6100ISLE/16/DIO + +usb:v1B04p6259* + ID_MODEL_FROM_DATABASE=ME-6200I/9/DIO + +usb:v1B04p6359* + ID_MODEL_FROM_DATABASE=ME-6300I/9/DIO + +usb:v1B04p810A* + ID_MODEL_FROM_DATABASE=ME-8100A + +usb:v1B04p810B* + ID_MODEL_FROM_DATABASE=ME-8100B + +usb:v1B04p820A* + ID_MODEL_FROM_DATABASE=ME-8200A + +usb:v1B04p820B* + ID_MODEL_FROM_DATABASE=ME-8200B + +usb:v1B0E* + ID_VENDOR_FROM_DATABASE=BLUTRONICS S.r.l. + +usb:v1B0Ep1078* + ID_MODEL_FROM_DATABASE=BLUDRIVE II CCID + +usb:v1B0Ep1079* + ID_MODEL_FROM_DATABASE=BLUDRIVE II CCID + +usb:v1B0Ep1080* + ID_MODEL_FROM_DATABASE=WRITECHIP II CCID + +usb:v1B1C* + ID_VENDOR_FROM_DATABASE=Corsair + +usb:v1B1Cp0890* + ID_MODEL_FROM_DATABASE=Flash Padlock + +usb:v1B1Cp0A00* + ID_MODEL_FROM_DATABASE=SP2500 Speakers + +usb:v1B1Cp0A60* + ID_MODEL_FROM_DATABASE=Vengeance K60 Keyboard + +usb:v1B1Cp1A01* + ID_MODEL_FROM_DATABASE=Flash Voyager GT + +usb:v1B1Cp1A0A* + ID_MODEL_FROM_DATABASE=Survivor Stealth Flash Drive + +usb:v1B1Cp1A90* + ID_MODEL_FROM_DATABASE=Flash Voyager GT + +usb:v1B1F* + ID_VENDOR_FROM_DATABASE=eQ-3 Entwicklung GmbH + +usb:v1B1FpC00F* + ID_MODEL_FROM_DATABASE=HM-CFG-USB/HM-CFG-USB-2 [HomeMatic Configuration adapter] + +usb:v1B20* + ID_VENDOR_FROM_DATABASE=MStar Semiconductor, Inc. + +usb:v1B22* + ID_VENDOR_FROM_DATABASE=WiLinx Corp. + +usb:v1B26* + ID_VENDOR_FROM_DATABASE=Cellex Power Products, Inc. + +usb:v1B27* + ID_VENDOR_FROM_DATABASE=Current Electronics Inc. + +usb:v1B28* + ID_VENDOR_FROM_DATABASE=NAVIsis Inc. + +usb:v1B32* + ID_VENDOR_FROM_DATABASE=Ugobe Life Forms, Inc. + +usb:v1B32p0064* + ID_MODEL_FROM_DATABASE=Pleo robotic dinosaur + +usb:v1B36* + ID_VENDOR_FROM_DATABASE=ViXS Systems, Inc. + +usb:v1B3B* + ID_VENDOR_FROM_DATABASE=iPassion Technology Inc. + +usb:v1B3Bp2933* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2935* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2936* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2937* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2938* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2939* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2950* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2951* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2952* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2953* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2955* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2956* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2957* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2958* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2959* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2960* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2961* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2962* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2963* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2965* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2966* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2967* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2968* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3Bp2969* + ID_MODEL_FROM_DATABASE=PC Camera/Webcam controller + +usb:v1B3F* + ID_VENDOR_FROM_DATABASE=Generalplus Technology Inc. + +usb:v1B3Fp0C52* + ID_MODEL_FROM_DATABASE=808 Camera #9 (mass storage mode) + +usb:v1B3Fp2002* + ID_MODEL_FROM_DATABASE=808 Camera #9 (web-cam mode) + +usb:v1B47* + ID_VENDOR_FROM_DATABASE=Energizer Holdings, Inc. + +usb:v1B47p0001* + ID_MODEL_FROM_DATABASE=CHUSB Duo Charger (NiMH AA/AAA USB smart charger) + +usb:v1B48* + ID_VENDOR_FROM_DATABASE=Plastron Precision Co., Ltd. + +usb:v1B52* + ID_VENDOR_FROM_DATABASE=ARH Inc. + +usb:v1B52p2101* + ID_MODEL_FROM_DATABASE=FXMC Neural Network Controller + +usb:v1B52p2102* + ID_MODEL_FROM_DATABASE=FXMC Neural Network Controller V2 + +usb:v1B52p2103* + ID_MODEL_FROM_DATABASE=FXMC Neural Network Controller V3 + +usb:v1B52p4101* + ID_MODEL_FROM_DATABASE=Passport Reader CLR device + +usb:v1B52p4201* + ID_MODEL_FROM_DATABASE=Passport Reader PRM device + +usb:v1B52p4202* + ID_MODEL_FROM_DATABASE=Passport Reader PRM extension device + +usb:v1B52p4203* + ID_MODEL_FROM_DATABASE=Passport Reader PRM DSP device + +usb:v1B52p4204* + ID_MODEL_FROM_DATABASE=Passport Reader PRMC device + +usb:v1B52p4205* + ID_MODEL_FROM_DATABASE=Passport Reader CSHR device + +usb:v1B52p4206* + ID_MODEL_FROM_DATABASE=Passport Reader PRMC V2 device + +usb:v1B52p4301* + ID_MODEL_FROM_DATABASE=Passport Reader MRZ device + +usb:v1B52p4302* + ID_MODEL_FROM_DATABASE=Passport Reader MRZ DSP device + +usb:v1B52p4303* + ID_MODEL_FROM_DATABASE=Passport Reader CSLR device + +usb:v1B52p4401* + ID_MODEL_FROM_DATABASE=Card Reader + +usb:v1B52p4501* + ID_MODEL_FROM_DATABASE=Passport Reader RFID device + +usb:v1B52p4502* + ID_MODEL_FROM_DATABASE=Passport Reader RFID AIG device + +usb:v1B52p6101* + ID_MODEL_FROM_DATABASE=Neural Network Controller + +usb:v1B52p6202* + ID_MODEL_FROM_DATABASE=Fingerprint Reader device + +usb:v1B52p6203* + ID_MODEL_FROM_DATABASE=Fingerprint Scanner device + +usb:v1B52p8101* + ID_MODEL_FROM_DATABASE=Camera V1 + +usb:v1B52p8102* + ID_MODEL_FROM_DATABASE=Recovery / Camera V2 + +usb:v1B52p8103* + ID_MODEL_FROM_DATABASE=Camera V3 + +usb:v1B59* + ID_VENDOR_FROM_DATABASE=K.S. Terminals Inc. + +usb:v1B5A* + ID_VENDOR_FROM_DATABASE=Chao Zhou Kai Yuan Electric Co., Ltd. + +usb:v1B65* + ID_VENDOR_FROM_DATABASE=The Hong Kong Standards and Testing Centre Ltd. + +usb:v1B71* + ID_VENDOR_FROM_DATABASE=Fushicai + +usb:v1B71p3002* + ID_MODEL_FROM_DATABASE=USBTV007 Video Grabber [EasyCAP] + +usb:v1B72* + ID_VENDOR_FROM_DATABASE=ATERGI TECHNOLOGY CO., LTD. + +usb:v1B73* + ID_VENDOR_FROM_DATABASE=Fresco Logic + +usb:v1B73p1000* + ID_MODEL_FROM_DATABASE=xHC1 Controller + +usb:v1B75* + ID_VENDOR_FROM_DATABASE=Ovislink Corp. + +usb:v1B75p3072* + ID_MODEL_FROM_DATABASE=AirLive WN-360USB adapter + +usb:v1B75p8171* + ID_MODEL_FROM_DATABASE=WN-370USB 802.11bgn Wireless Adapter [Realtek RTL8188SU] + +usb:v1B75p8187* + ID_MODEL_FROM_DATABASE=AirLive WL-1600USB 802.11g Adapter [Realtek RTL8187L] + +usb:v1B75p9170* + ID_MODEL_FROM_DATABASE=AirLive X.USB 802.11abgn [Atheros AR9170+AR9104] + +usb:v1B75pA200* + ID_MODEL_FROM_DATABASE=AirLive WN-200USB wireless 11b/g/n dongle + +usb:v1B76* + ID_VENDOR_FROM_DATABASE=Legend Silicon Corp. + +usb:v1B80* + ID_VENDOR_FROM_DATABASE=Afatech + +usb:v1B80pC810* + ID_MODEL_FROM_DATABASE=MC810 [af9015] + +usb:v1B80pD393* + ID_MODEL_FROM_DATABASE=DVB-T receiver [RTL2832U] + +usb:v1B80pD396* + ID_MODEL_FROM_DATABASE=UB396-T [RTL2832U] + +usb:v1B80pD397* + ID_MODEL_FROM_DATABASE=DVB-T receiver [RTL2832U] + +usb:v1B80pD398* + ID_MODEL_FROM_DATABASE=DVB-T receiver [RTL2832U] + +usb:v1B80pD700* + ID_MODEL_FROM_DATABASE=FM Radio SnapMusic Mobile 700 (FM700) + +usb:v1B80pE297* + ID_MODEL_FROM_DATABASE=Conceptronic DVB-T CTVDIGRCU V3.0 + +usb:v1B80pE383* + ID_MODEL_FROM_DATABASE=DVB-T UB383-T [af9015] + +usb:v1B80pE385* + ID_MODEL_FROM_DATABASE=DVB-T UB385-T [af9015] + +usb:v1B80pE386* + ID_MODEL_FROM_DATABASE=DVB-T UB385-T [af9015] + +usb:v1B80pE399* + ID_MODEL_FROM_DATABASE=DVB-T KWorld PlusTV 399U [af9015] + +usb:v1B80pE39A* + ID_MODEL_FROM_DATABASE=DVB-T395U [af9015] + +usb:v1B80pE39B* + ID_MODEL_FROM_DATABASE=DVB-T395U [af9015] + +usb:v1B80pE401* + ID_MODEL_FROM_DATABASE=Sveon STV22 DVB-T [af9015] + +usb:v1B80pE409* + ID_MODEL_FROM_DATABASE=IT9137FN Dual DVB-T [KWorld UB499-2T] + +usb:v1B86* + ID_VENDOR_FROM_DATABASE=Dongguan Guanshang Electronics Co., Ltd. + +usb:v1B88* + ID_VENDOR_FROM_DATABASE=ShenMing Electron (Dong Guan) Co., Ltd. + +usb:v1B8C* + ID_VENDOR_FROM_DATABASE=Altium Limited + +usb:v1B8D* + ID_VENDOR_FROM_DATABASE=e-MOVE Technology Co., Ltd. + +usb:v1B8E* + ID_VENDOR_FROM_DATABASE=Amlogic, Inc. + +usb:v1B8F* + ID_VENDOR_FROM_DATABASE=MA LABS, Inc. + +usb:v1B96* + ID_VENDOR_FROM_DATABASE=N-Trig + +usb:v1B96p0001* + ID_MODEL_FROM_DATABASE=Duosense Transparent Electromagnetic Digitizer + +usb:v1B98* + ID_VENDOR_FROM_DATABASE=YMax Communications Corp. + +usb:v1B99* + ID_VENDOR_FROM_DATABASE=Shenzhen Yuanchuan Electronic + +usb:v1BA1* + ID_VENDOR_FROM_DATABASE=JINQ CHERN ENTERPRISE CO., LTD. + +usb:v1BA2* + ID_VENDOR_FROM_DATABASE=Lite Metals & Plastic (Shenzhen) Co., Ltd. + +usb:v1BA4* + ID_VENDOR_FROM_DATABASE=Ember Corporation + +usb:v1BA4p0001* + ID_MODEL_FROM_DATABASE=InSight USB Link + +usb:v1BA6* + ID_VENDOR_FROM_DATABASE=Abilis Systems + +usb:v1BA8* + ID_VENDOR_FROM_DATABASE=China Telecommunication Technology Labs + +usb:v1BAD* + ID_VENDOR_FROM_DATABASE=Harmonix Music + +usb:v1BADp0002* + ID_MODEL_FROM_DATABASE=Guitar for Xbox 360 + +usb:v1BADp0003* + ID_MODEL_FROM_DATABASE=Drum Kit for Xbox 360 + +usb:v1BAE* + ID_VENDOR_FROM_DATABASE=Vuzix Corporation + +usb:v1BAEp0002* + ID_MODEL_FROM_DATABASE=VR920 Immersive Eyewear + +usb:v1BBB* + ID_VENDOR_FROM_DATABASE=T & A Mobile Phones + +usb:v1BBBp011E* + ID_MODEL_FROM_DATABASE=Alcatel One Touch L100V / Telekom Speedstick LTE II + +usb:v1BBBpF017* + ID_MODEL_FROM_DATABASE=Alcatel One Touch L100V / Telekom Speedstick LTE II + +usb:v1BC4* + ID_VENDOR_FROM_DATABASE=Ford Motor Co. + +usb:v1BC5* + ID_VENDOR_FROM_DATABASE=AVIXE Technology (China) Ltd. + +usb:v1BC7* + ID_VENDOR_FROM_DATABASE=Telit Wireless Solutions + +usb:v1BC7p0020* + ID_MODEL_FROM_DATABASE=HE863 + +usb:v1BC7p0021* + ID_MODEL_FROM_DATABASE=HE910 + +usb:v1BC7p0023* + ID_MODEL_FROM_DATABASE=HE910-D ECM + +usb:v1BC7p1003* + ID_MODEL_FROM_DATABASE=UC864-E + +usb:v1BC7p1004* + ID_MODEL_FROM_DATABASE=UC864-G + +usb:v1BC7p1005* + ID_MODEL_FROM_DATABASE=CC864-DUAL + +usb:v1BC7p1006* + ID_MODEL_FROM_DATABASE=CC864-SINGLE + +usb:v1BC7p1010* + ID_MODEL_FROM_DATABASE=DE910-DUAL + +usb:v1BC7p1011* + ID_MODEL_FROM_DATABASE=CE910-DUAL + +usb:v1BC7p1200* + ID_MODEL_FROM_DATABASE=LE920 + +usb:v1BCE* + ID_VENDOR_FROM_DATABASE=Contac Cable Industrial Limited + +usb:v1BCF* + ID_VENDOR_FROM_DATABASE=Sunplus Innovation Technology Inc. + +usb:v1BCFp0005* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v1BCFp0007* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v1BCFp053A* + ID_MODEL_FROM_DATABASE=Targa Silvercrest OMC807-C optische Funkmaus + +usb:v1BCFp05C5* + ID_MODEL_FROM_DATABASE=SPRF2413A [2.4GHz Wireless Keyboard/Mouse Receiver] + +usb:v1BCFp05CF* + ID_MODEL_FROM_DATABASE=Micro keyboard & mouse receiver + +usb:v1BCFp0C31* + ID_MODEL_FROM_DATABASE=SPIF30x Serial-ATA bridge + +usb:v1BCFp2880* + ID_MODEL_FROM_DATABASE=Dell HD Webcam + +usb:v1BCFp2885* + ID_MODEL_FROM_DATABASE=ASUS Webcam + +usb:v1BCFp2888* + ID_MODEL_FROM_DATABASE=HP Universal Camera + +usb:v1BCFp28A2* + ID_MODEL_FROM_DATABASE=Dell Integrated Webcam + +usb:v1BCFp28A6* + ID_MODEL_FROM_DATABASE=DELL XPS Integrated Webcam + +usb:v1BCFp28AE* + ID_MODEL_FROM_DATABASE=Laptop Integrated Webcam HD + +usb:v1BCFp28BD* + ID_MODEL_FROM_DATABASE=Dell Integrated HD Webcam + +usb:v1BCFp2985* + ID_MODEL_FROM_DATABASE=Laptop Integrated Webcam HD + +usb:v1BCFp2B83* + ID_MODEL_FROM_DATABASE=Laptop Integrated Webcam FHD + +usb:v1BD0* + ID_VENDOR_FROM_DATABASE=Hangzhou Riyue Electronic Co., Ltd. + +usb:v1BD5* + ID_VENDOR_FROM_DATABASE=BG Systems, Inc. + +usb:v1BDE* + ID_VENDOR_FROM_DATABASE=P-TWO INDUSTRIES, INC. + +usb:v1BEF* + ID_VENDOR_FROM_DATABASE=Shenzhen Tongyuan Network-Communication Cables Co., Ltd + +usb:v1BF0* + ID_VENDOR_FROM_DATABASE=RealVision Inc. + +usb:v1BF5* + ID_VENDOR_FROM_DATABASE=Extranet Systems Inc. + +usb:v1BF6* + ID_VENDOR_FROM_DATABASE=Orient Semiconductor Electronics, Ltd. + +usb:v1BFD* + ID_VENDOR_FROM_DATABASE=TouchPack + +usb:v1BFDp1268* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v1BFDp1368* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v1BFDp1568* + ID_MODEL_FROM_DATABASE=Capacitive Touch Screen + +usb:v1BFDp1668* + ID_MODEL_FROM_DATABASE=IR Touch Screen + +usb:v1BFDp1688* + ID_MODEL_FROM_DATABASE=Resistive Touch Screen + +usb:v1BFDp2968* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v1BFDp5968* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v1BFDp6968* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v1C02* + ID_VENDOR_FROM_DATABASE=Kreton Corporation + +usb:v1C04* + ID_VENDOR_FROM_DATABASE=QNAP System Inc. + +usb:v1C0C* + ID_VENDOR_FROM_DATABASE=Ionics EMS, Inc. + +usb:v1C0Cp0102* + ID_MODEL_FROM_DATABASE=Plug Computer + +usb:v1C0D* + ID_VENDOR_FROM_DATABASE=Relm Wireless + +usb:v1C10* + ID_VENDOR_FROM_DATABASE=Lanterra Industrial Co., Ltd. + +usb:v1C13* + ID_VENDOR_FROM_DATABASE=ALECTRONIC LIMITED + +usb:v1C1A* + ID_VENDOR_FROM_DATABASE=Datel Electronics Ltd. + +usb:v1C1B* + ID_VENDOR_FROM_DATABASE=Volkswagen of America, Inc. + +usb:v1C1F* + ID_VENDOR_FROM_DATABASE=Goldvish S.A. + +usb:v1C20* + ID_VENDOR_FROM_DATABASE=Fuji Electric Device Technology Co., Ltd. + +usb:v1C21* + ID_VENDOR_FROM_DATABASE=ADDMM LLC + +usb:v1C22* + ID_VENDOR_FROM_DATABASE=ZHONGSHAN CHIANG YU ELECTRIC CO., LTD. + +usb:v1C26* + ID_VENDOR_FROM_DATABASE=Shanghai Haiying Electronics Co., Ltd. + +usb:v1C27* + ID_VENDOR_FROM_DATABASE=HuiYang D & S Cable Co., Ltd. + +usb:v1C29* + ID_VENDOR_FROM_DATABASE=Elster GmbH + +usb:v1C29p0001* + ID_MODEL_FROM_DATABASE=ExMFE5 Simulator + +usb:v1C29p10FC* + ID_MODEL_FROM_DATABASE=enCore device + +usb:v1C31* + ID_VENDOR_FROM_DATABASE=LS Cable Ltd. + +usb:v1C34* + ID_VENDOR_FROM_DATABASE=SpringCard + +usb:v1C34p7241* + ID_MODEL_FROM_DATABASE=Prox'N'Roll RFID Scanner + +usb:v1C37* + ID_VENDOR_FROM_DATABASE=Authorizer Technologies, Inc. + +usb:v1C3D* + ID_VENDOR_FROM_DATABASE=NONIN MEDICAL INC. + +usb:v1C3E* + ID_VENDOR_FROM_DATABASE=Wep Peripherals + +usb:v1C40* + ID_VENDOR_FROM_DATABASE=EZPrototypes + +usb:v1C40p0533* + ID_MODEL_FROM_DATABASE=TiltStick + +usb:v1C40p0534* + ID_MODEL_FROM_DATABASE=i2c-tiny-usb interface + +usb:v1C40p0535* + ID_MODEL_FROM_DATABASE=glcd2usb interface + +usb:v1C40p0536* + ID_MODEL_FROM_DATABASE=Swiss ColorPAL + +usb:v1C49* + ID_VENDOR_FROM_DATABASE=Cherng Weei Technology Corp. + +usb:v1C4F* + ID_VENDOR_FROM_DATABASE=SiGma Micro + +usb:v1C4Fp0002* + ID_MODEL_FROM_DATABASE=Keyboard TRACER Gamma Ivory + +usb:v1C4Fp0003* + ID_MODEL_FROM_DATABASE=HID controller + +usb:v1C4Fp000E* + ID_MODEL_FROM_DATABASE=Genius KB-120 Keyboard + +usb:v1C4Fp0026* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v1C4Fp3000* + ID_MODEL_FROM_DATABASE=Micro USB Web Camera + +usb:v1C4Fp3002* + ID_MODEL_FROM_DATABASE=WebCam SiGma Micro + +usb:v1C6B* + ID_VENDOR_FROM_DATABASE=Philips & Lite-ON Digital Solutions Corporation + +usb:v1C6BpA222* + ID_MODEL_FROM_DATABASE=DVD Writer Slimtype eTAU108 + +usb:v1C6C* + ID_VENDOR_FROM_DATABASE=Skydigital Inc. + +usb:v1C73* + ID_VENDOR_FROM_DATABASE=AMT + +usb:v1C73p861F* + ID_MODEL_FROM_DATABASE=Anysee E30 USB 2.0 DVB-T Receiver + +usb:v1C77* + ID_VENDOR_FROM_DATABASE=Kaetat Industrial Co., Ltd. + +usb:v1C78* + ID_VENDOR_FROM_DATABASE=Datascope Corp. + +usb:v1C79* + ID_VENDOR_FROM_DATABASE=Unigen Corporation + +usb:v1C7A* + ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc. + +usb:v1C7Ap0801* + ID_MODEL_FROM_DATABASE=Fingerprint Reader + +usb:v1C7B* + ID_VENDOR_FROM_DATABASE=LUXSHARE PRECISION INDUSTRY (SHENZHEN) CO., LTD. + +usb:v1C83* + ID_VENDOR_FROM_DATABASE=Schomaecker GmbH + +usb:v1C83p0001* + ID_MODEL_FROM_DATABASE=RS150 V2 + +usb:v1C87* + ID_VENDOR_FROM_DATABASE=2N TELEKOMUNIKACE a.s. + +usb:v1C88* + ID_VENDOR_FROM_DATABASE=Somagic, Inc. + +usb:v1C88p0007* + ID_MODEL_FROM_DATABASE=SMI Grabber (EasyCAP DC60+ clone) (no firmware) [SMI-2021CBE] + +usb:v1C88p003C* + ID_MODEL_FROM_DATABASE=SMI Grabber (EasyCAP DC60+ clone) [SMI-2021CBE] + +usb:v1C89* + ID_VENDOR_FROM_DATABASE=HONGKONG WEIDIDA ELECTRON LIMITED + +usb:v1C8E* + ID_VENDOR_FROM_DATABASE=ASTRON INTERNATIONAL CORP. + +usb:v1C98* + ID_VENDOR_FROM_DATABASE=ALPINE ELECTRONICS, INC. + +usb:v1C9E* + ID_VENDOR_FROM_DATABASE=OMEGA TECHNOLOGY + +usb:v1C9Ep6061* + ID_MODEL_FROM_DATABASE=WL-72B 3.5G MODEM + +usb:v1CA0* + ID_VENDOR_FROM_DATABASE=ACCARIO Inc. + +usb:v1CA1* + ID_VENDOR_FROM_DATABASE=Symwave + +usb:v1CA1p18AB* + ID_MODEL_FROM_DATABASE=SATA bridge + +usb:v1CAC* + ID_VENDOR_FROM_DATABASE=Kinstone + +usb:v1CACpA332* + ID_MODEL_FROM_DATABASE=C8 Webcam + +usb:v1CACpB288* + ID_MODEL_FROM_DATABASE=C18 Webcam + +usb:v1CB3* + ID_VENDOR_FROM_DATABASE=Aces Electronic Co., Ltd. + +usb:v1CB4* + ID_VENDOR_FROM_DATABASE=OPEX CORPORATION + +usb:v1CB6* + ID_VENDOR_FROM_DATABASE=IdeaCom Technology Inc. + +usb:v1CB6p6681* + ID_MODEL_FROM_DATABASE=IDC6681 + +usb:v1CBE* + ID_VENDOR_FROM_DATABASE=Luminary Micro Inc. + +usb:v1CBEp00FD* + ID_MODEL_FROM_DATABASE=In-Circuit Debug Interface + +usb:v1CBEp00FF* + ID_MODEL_FROM_DATABASE=Stellaris ROM DFU Bootloader + +usb:v1CBEp0166* + ID_MODEL_FROM_DATABASE=CANAL USB2CAN + +usb:v1CBF* + ID_VENDOR_FROM_DATABASE=FORTAT SKYMARK INDUSTRIAL COMPANY + +usb:v1CC0* + ID_VENDOR_FROM_DATABASE=PlantSense + +usb:v1CCA* + ID_VENDOR_FROM_DATABASE=NextWave Broadband Inc. + +usb:v1CCD* + ID_VENDOR_FROM_DATABASE=Bodatong Technology (Shenzhen) Co., Ltd. + +usb:v1CD4* + ID_VENDOR_FROM_DATABASE=adp corporation + +usb:v1CD5* + ID_VENDOR_FROM_DATABASE=Firecomms Ltd. + +usb:v1CD6* + ID_VENDOR_FROM_DATABASE=Antonio Precise Products Manufactory Ltd. + +usb:v1CDE* + ID_VENDOR_FROM_DATABASE=Telecommunications Technology Association (TTA) + +usb:v1CDF* + ID_VENDOR_FROM_DATABASE=WonTen Technology Co., Ltd. + +usb:v1CE0* + ID_VENDOR_FROM_DATABASE=EDIMAX TECHNOLOGY CO., LTD. + +usb:v1CE1* + ID_VENDOR_FROM_DATABASE=Amphenol KAE + +usb:v1CF1* + ID_VENDOR_FROM_DATABASE=Dresden Elektronik + +usb:v1CF1p0001* + ID_MODEL_FROM_DATABASE=Sensor Terminal Board + +usb:v1CF1p0004* + ID_MODEL_FROM_DATABASE=Wireless Handheld Terminal + +usb:v1CF1p0017* + ID_MODEL_FROM_DATABASE=deRFusbSniffer 2.4 GHz + +usb:v1CF1p0018* + ID_MODEL_FROM_DATABASE=deRFusb24E001 + +usb:v1CF1p0019* + ID_MODEL_FROM_DATABASE=deRFusb14E001 + +usb:v1CF1p001A* + ID_MODEL_FROM_DATABASE=deRFusb23E00 + +usb:v1CF1p001B* + ID_MODEL_FROM_DATABASE=deRFusb13E00 + +usb:v1CF1p001C* + ID_MODEL_FROM_DATABASE=deRFnode + +usb:v1CF1p001D* + ID_MODEL_FROM_DATABASE=deRFnode / gateway + +usb:v1CF1p0022* + ID_MODEL_FROM_DATABASE=deUSB level shifter + +usb:v1CF1p0023* + ID_MODEL_FROM_DATABASE=deRFusbSniffer Sub-GHz + +usb:v1CF1p0025* + ID_MODEL_FROM_DATABASE=deRFusb23E06 + +usb:v1CF1p0027* + ID_MODEL_FROM_DATABASE=deRFusb13E06 + +usb:v1CFC* + ID_VENDOR_FROM_DATABASE=ANDES TECHNOLOGY CORPORATION + +usb:v1CFD* + ID_VENDOR_FROM_DATABASE=Flextronics Digital Design Japan, LTD. + +usb:v1D03* + ID_VENDOR_FROM_DATABASE=iCON + +usb:v1D03p0028* + ID_MODEL_FROM_DATABASE=iCreativ MIDI Controller + +usb:v1D07* + ID_VENDOR_FROM_DATABASE=Solid-Motion + +usb:v1D08* + ID_VENDOR_FROM_DATABASE=NINGBO HENTEK DRAGON ELECTRONICS CO., LTD. + +usb:v1D09* + ID_VENDOR_FROM_DATABASE=TechFaith Wireless Technology Limited + +usb:v1D09p1026* + ID_MODEL_FROM_DATABASE=HSUPA Modem FLYING-LARK46-VER0.07 [Flying Angel] + +usb:v1D0A* + ID_VENDOR_FROM_DATABASE=Johnson Controls, Inc. The Automotive Business Unit + +usb:v1D0B* + ID_VENDOR_FROM_DATABASE=HAN HUA CABLE & WIRE TECHNOLOGY (J.X.) CO., LTD. + +usb:v1D0F* + ID_VENDOR_FROM_DATABASE=Sonix Technology Co., Ltd. + +usb:v1D14* + ID_VENDOR_FROM_DATABASE=ALPHA-SAT TECHNOLOGY LIMITED + +usb:v1D17* + ID_VENDOR_FROM_DATABASE=C-Thru Music Ltd. + +usb:v1D17p0001* + ID_MODEL_FROM_DATABASE=AXiS-49 Harmonic Table MIDI Keyboard + +usb:v1D19* + ID_VENDOR_FROM_DATABASE=Dexatek Technology Ltd. + +usb:v1D19p1101* + ID_MODEL_FROM_DATABASE=DK DVB-T Dongle + +usb:v1D19p1102* + ID_MODEL_FROM_DATABASE=DK mini DVB-T Dongle + +usb:v1D19p1103* + ID_MODEL_FROM_DATABASE=DK 5217 DVB-T Dongle + +usb:v1D19p6105* + ID_MODEL_FROM_DATABASE=Video grabber + +usb:v1D19p8202* + ID_MODEL_FROM_DATABASE=DK DVBC/T DONGLE + +usb:v1D1F* + ID_VENDOR_FROM_DATABASE=Diostech Co., Ltd. + +usb:v1D20* + ID_VENDOR_FROM_DATABASE=SAMTACK INC. + +usb:v1D27* + ID_VENDOR_FROM_DATABASE=ASUS + +usb:v1D34* + ID_VENDOR_FROM_DATABASE=Dream Cheeky + +usb:v1D34p0001* + ID_MODEL_FROM_DATABASE=Dream Cheeky Fidget + +usb:v1D34p0004* + ID_MODEL_FROM_DATABASE=Dream Cheeky Webmail Notifier + +usb:v1D34p0008* + ID_MODEL_FROM_DATABASE=Dream Cheeky button + +usb:v1D34p000A* + ID_MODEL_FROM_DATABASE=Dream Cheeky Mailbox Friends Alert + +usb:v1D34p000D* + ID_MODEL_FROM_DATABASE=Dream Cheeky Big Red Button + +usb:v1D34p0013* + ID_MODEL_FROM_DATABASE=Dream Cheeky LED Message Board + +usb:v1D45* + ID_VENDOR_FROM_DATABASE=Touch + +usb:v1D45p1D45* + ID_MODEL_FROM_DATABASE=Foxlink Optical touch sensor + +usb:v1D4D* + ID_VENDOR_FROM_DATABASE=PEGATRON CORPORATION + +usb:v1D4Dp0002* + ID_MODEL_FROM_DATABASE=Ralink RT2770/2720 802.11b/g/n Wireless LAN Mini-USB Device + +usb:v1D4Dp000C* + ID_MODEL_FROM_DATABASE=Ralink RT3070 802.11b/g/n Wireless Lan USB Device + +usb:v1D4Dp000E* + ID_MODEL_FROM_DATABASE=Ralink RT3070 802.11b/g/n Wireless Lan USB Device + +usb:v1D50* + ID_VENDOR_FROM_DATABASE=OpenMoko, Inc. + +usb:v1D50p1DB5* + ID_MODEL_FROM_DATABASE=IDBG DFU + +usb:v1D50p1DB6* + ID_MODEL_FROM_DATABASE=IDBG + +usb:v1D50p5117* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner kernel usbnet (g_ether, CDC Ethernet) Mode + +usb:v1D50p5118* + ID_MODEL_FROM_DATABASE=Debug Board (FT2232D) for Neo1973/FreeRunner + +usb:v1D50p5119* + ID_MODEL_FROM_DATABASE=GTA01/GTA02 U-Boot Bootloader + +usb:v1D50p511A* + ID_MODEL_FROM_DATABASE=HXD8 u-boot usbtty CDC ACM Mode + +usb:v1D50p511B* + ID_MODEL_FROM_DATABASE=SMDK2440 u-boot usbtty CDC ACM mode + +usb:v1D50p511C* + ID_MODEL_FROM_DATABASE=SMDK2443 u-boot usbtty CDC ACM mode + +usb:v1D50p511D* + ID_MODEL_FROM_DATABASE=QT2410 u-boot usbtty CDC ACM mode + +usb:v1D50p5120* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner u-boot generic serial mode + +usb:v1D50p5121* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner kernel mass storage (g_storage) mode + +usb:v1D50p5122* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner kernel usbnet (g_ether, RNDIS) mode + +usb:v1D50p5123* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner internal Bluetooth CSR4 module + +usb:v1D50p5124* + ID_MODEL_FROM_DATABASE=Neo1973/FreeRunner Bluetooth Device ID service + +usb:v1D50p6000* + ID_MODEL_FROM_DATABASE=Ubertooth Zero + +usb:v1D50p6001* + ID_MODEL_FROM_DATABASE=Ubertooth Zero DFU + +usb:v1D50p6002* + ID_MODEL_FROM_DATABASE=Ubertooth One + +usb:v1D50p6003* + ID_MODEL_FROM_DATABASE=Ubertooth One DFU + +usb:v1D50p6004* + ID_MODEL_FROM_DATABASE=LeoLipo + +usb:v1D50p6005* + ID_MODEL_FROM_DATABASE=LED Flower S + +usb:v1D50p6006* + ID_MODEL_FROM_DATABASE=LED Cube + +usb:v1D50p6007* + ID_MODEL_FROM_DATABASE=LED Flower + +usb:v1D50p6008* + ID_MODEL_FROM_DATABASE=Kisbee 802.15.4 transceiver + +usb:v1D50p6009* + ID_MODEL_FROM_DATABASE=Adjacent Reality Tracker + +usb:v1D50p6028* + ID_MODEL_FROM_DATABASE=Teensy 2.0 Development Board [ErgoDox Keyboard] + +usb:v1D50p602B* + ID_MODEL_FROM_DATABASE=FPGALink + +usb:v1D50p604B* + ID_MODEL_FROM_DATABASE=HackRF Jawbreaker Software-Defined Radio + +usb:v1D50p6053* + ID_MODEL_FROM_DATABASE=Darkgame Controller + +usb:v1D50p6089* + ID_MODEL_FROM_DATABASE=Great Scott Gadgets HackRF One + +usb:v1D50p60A1* + ID_MODEL_FROM_DATABASE=Airspy + +usb:v1D50pCC15* + ID_MODEL_FROM_DATABASE=CCCAMP2015 rad1o badge + +usb:v1D57* + ID_VENDOR_FROM_DATABASE=Xenta + +usb:v1D57p0005* + ID_MODEL_FROM_DATABASE=Wireless Receiver (Keyboard and Mouse) + +usb:v1D57p0006* + ID_MODEL_FROM_DATABASE=Wireless Receiver (RC Laser Pointer) + +usb:v1D57p000C* + ID_MODEL_FROM_DATABASE=Optical Mouse + +usb:v1D57p2400* + ID_MODEL_FROM_DATABASE=Wireless Mouse Receiver + +usb:v1D57p32DA* + ID_MODEL_FROM_DATABASE=2.4GHz Receiver (Keyboard and Mouse) + +usb:v1D57p83D0* + ID_MODEL_FROM_DATABASE=Click-mouse! + +usb:v1D57pAC01* + ID_MODEL_FROM_DATABASE=Wireless Receiver (Keyboard and Mouse) + +usb:v1D57pAD02* + ID_MODEL_FROM_DATABASE=SE340D PC Remote Control + +usb:v1D57pAF01* + ID_MODEL_FROM_DATABASE=AUVIO Universal Remote Receiver for PlayStation 3 + +usb:v1D5B* + ID_VENDOR_FROM_DATABASE=Smartronix, Inc. + +usb:v1D6B* + ID_VENDOR_FROM_DATABASE=Linux Foundation + +usb:v1D6Bp0001* + ID_MODEL_FROM_DATABASE=1.1 root hub + +usb:v1D6Bp0002* + ID_MODEL_FROM_DATABASE=2.0 root hub + +usb:v1D6Bp0003* + ID_MODEL_FROM_DATABASE=3.0 root hub + +usb:v1D6Bp0100* + ID_MODEL_FROM_DATABASE=PTP Gadget + +usb:v1D6Bp0101* + ID_MODEL_FROM_DATABASE=Audio Gadget + +usb:v1D6Bp0102* + ID_MODEL_FROM_DATABASE=EEM Gadget + +usb:v1D6Bp0103* + ID_MODEL_FROM_DATABASE=NCM (Ethernet) Gadget + +usb:v1D6Bp0104* + ID_MODEL_FROM_DATABASE=Multifunction Composite Gadget + +usb:v1D6Bp0105* + ID_MODEL_FROM_DATABASE=FunctionFS Gadget + +usb:v1D6Bp0200* + ID_MODEL_FROM_DATABASE=Qemu Audio Device + +usb:v1D90* + ID_VENDOR_FROM_DATABASE=Citizen + +usb:v1D90p201E* + ID_MODEL_FROM_DATABASE=PPU-700 + +usb:v1DE1* + ID_VENDOR_FROM_DATABASE=Actions Microelectronics Co. + +usb:v1DE1p1101* + ID_MODEL_FROM_DATABASE=Generic Display Device (Mass storage mode) + +usb:v1DE1pC101* + ID_MODEL_FROM_DATABASE=Generic Display Device + +usb:v1E0E* + ID_VENDOR_FROM_DATABASE=Qualcomm / Option + +usb:v1E0EpF000* + ID_MODEL_FROM_DATABASE=iCON 210 UMTS Surfstick + +usb:v1E10* + ID_VENDOR_FROM_DATABASE=Point Grey Research, Inc. + +usb:v1E10p2004* + ID_MODEL_FROM_DATABASE=Sony 1.3MP 1/3" ICX445 IIDC video camera [Chameleon] + +usb:v1E17* + ID_VENDOR_FROM_DATABASE=Mirion Technologies Dosimetry Services Division + +usb:v1E17p0001* + ID_MODEL_FROM_DATABASE=instadose dosimeter + +usb:v1E1D* + ID_VENDOR_FROM_DATABASE=Lumension Security + +usb:v1E1Dp0165* + ID_MODEL_FROM_DATABASE=Secure Pen drive + +usb:v1E1F* + ID_VENDOR_FROM_DATABASE=INVIA + +usb:v1E29* + ID_VENDOR_FROM_DATABASE=Festo AG & Co. KG + +usb:v1E29p0101* + ID_MODEL_FROM_DATABASE=CPX Adapter + +usb:v1E29p0102* + ID_MODEL_FROM_DATABASE=CPX Adapter >=HW10.09 [CP2102] + +usb:v1E29p0401* + ID_MODEL_FROM_DATABASE=iL3-TP [AT90USB646] + +usb:v1E29p0402* + ID_MODEL_FROM_DATABASE=FTDI232 [EasyPort] + +usb:v1E29p0403* + ID_MODEL_FROM_DATABASE=FTDI232 [EasyPort Mini] + +usb:v1E29p0404* + ID_MODEL_FROM_DATABASE=FTDI232 [Netzteil-GL] + +usb:v1E29p0405* + ID_MODEL_FROM_DATABASE=FTDI232 [MotorPrüfstand] + +usb:v1E29p0406* + ID_MODEL_FROM_DATABASE=STM32F103 [EasyKit] + +usb:v1E29p0407* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino] + +usb:v1E29p0408* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino-Arm] + +usb:v1E29p0409* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino-Arm Bootloader] + +usb:v1E29p040A* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino Bootloader] + +usb:v1E29p040B* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino XT] + +usb:v1E29p040C* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino XT Bootloader] + +usb:v1E29p040D* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino 3] + +usb:v1E29p040E* + ID_MODEL_FROM_DATABASE=LPC2378 [Robotino 3 Bootloader] + +usb:v1E29p0501* + ID_MODEL_FROM_DATABASE=CP2102 [CMSP] + +usb:v1E29p0601* + ID_MODEL_FROM_DATABASE=CMMP-AS + +usb:v1E3D* + ID_VENDOR_FROM_DATABASE=Chipsbank Microelectronics Co., Ltd + +usb:v1E3Dp2093* + ID_MODEL_FROM_DATABASE=CBM209x Flash Drive (OEM) + +usb:v1E3Dp4082* + ID_MODEL_FROM_DATABASE=CBM4082 SD Card Reader + +usb:v1E41* + ID_VENDOR_FROM_DATABASE=Cleverscope + +usb:v1E41p0001* + ID_MODEL_FROM_DATABASE=CS328A PC Oscilloscope + +usb:v1E4E* + ID_VENDOR_FROM_DATABASE=Cubeternet + +usb:v1E4Ep0100* + ID_MODEL_FROM_DATABASE=WebCam + +usb:v1E4Ep0102* + ID_MODEL_FROM_DATABASE=GL-UPC822 UVC WebCam + +usb:v1E54* + ID_VENDOR_FROM_DATABASE=TypeMatrix + +usb:v1E54p2030* + ID_MODEL_FROM_DATABASE=2030 USB Keyboard + +usb:v1E68* + ID_VENDOR_FROM_DATABASE=TrekStor GmbH & Co. KG + +usb:v1E68p001B* + ID_MODEL_FROM_DATABASE=DataStation maxi g.u + +usb:v1E68p0050* + ID_MODEL_FROM_DATABASE=DataStation maxi light + +usb:v1E71* + ID_VENDOR_FROM_DATABASE=NZXT + +usb:v1E71p0001* + ID_MODEL_FROM_DATABASE=Avatar Optical Mouse + +usb:v1E74* + ID_VENDOR_FROM_DATABASE=Coby Electronics Corporation + +usb:v1E74p2211* + ID_MODEL_FROM_DATABASE=MP300 + +usb:v1E74p2647* + ID_MODEL_FROM_DATABASE=2 GB 2 Go Video MP3 Player [MP601-2G] + +usb:v1E74p2659* + ID_MODEL_FROM_DATABASE=Coby 4GB Go Video MP3 Player [MP620-4G] + +usb:v1E74p4641* + ID_MODEL_FROM_DATABASE=A8705 MP3/Video Player + +usb:v1E74p6511* + ID_MODEL_FROM_DATABASE=MP705-8G MP3 player + +usb:v1E74p6512* + ID_MODEL_FROM_DATABASE=MP705-4G + +usb:v1E74p7111* + ID_MODEL_FROM_DATABASE=MP957 Music and Video Player + +usb:v1E7D* + ID_VENDOR_FROM_DATABASE=ROCCAT + +usb:v1E7Dp2C24* + ID_MODEL_FROM_DATABASE=Pyra Mouse (wired) + +usb:v1E7Dp2CED* + ID_MODEL_FROM_DATABASE=Kone Mouse + +usb:v1E7Dp2CF6* + ID_MODEL_FROM_DATABASE=Pyra Mouse (wireless) + +usb:v1E7Dp2D50* + ID_MODEL_FROM_DATABASE=Kova+ Mouse + +usb:v1E7Dp2D51* + ID_MODEL_FROM_DATABASE=Kone+ Mouse + +usb:v1E7Dp30D4* + ID_MODEL_FROM_DATABASE=Arvo Keyboard + +usb:v1EBB* + ID_VENDOR_FROM_DATABASE=NuCORE Technology, Inc. + +usb:v1EDA* + ID_VENDOR_FROM_DATABASE=AirTies Wireless Networks + +usb:v1EDAp2012* + ID_MODEL_FROM_DATABASE=Air2210 54 Mbps Wireless Adapter + +usb:v1EDAp2210* + ID_MODEL_FROM_DATABASE=Air2210 54 Mbps Wireless Adapter + +usb:v1EDAp2310* + ID_MODEL_FROM_DATABASE=Air2310 150 Mbps Wireless Adapter + +usb:v1EDAp2410* + ID_MODEL_FROM_DATABASE=Air2410 300 Mbps Wireless Adapter + +usb:v1EDB* + ID_VENDOR_FROM_DATABASE=Blackmagic design + +usb:v1EDBpBD3B* + ID_MODEL_FROM_DATABASE=Intensity Shuttle + +usb:v1EE8* + ID_VENDOR_FROM_DATABASE=ONDA COMMUNICATION S.p.a. + +usb:v1EE8p0014* + ID_MODEL_FROM_DATABASE=MT833UP + +usb:v1EF6* + ID_VENDOR_FROM_DATABASE=EADS Deutschland GmbH + +usb:v1EF6p2233* + ID_MODEL_FROM_DATABASE=Cassidian NH90 STTE + +usb:v1EF6p5064* + ID_MODEL_FROM_DATABASE=FDR Interface + +usb:v1EF6p5523* + ID_MODEL_FROM_DATABASE=Cassidian SSDC Adapter II + +usb:v1EF6p5545* + ID_MODEL_FROM_DATABASE=Cassidian SSDC Adapter III + +usb:v1EF6p5648* + ID_MODEL_FROM_DATABASE=RIU CSMU/BSD + +usb:v1EF6p564A* + ID_MODEL_FROM_DATABASE=Cassidian RIU CSMU/BSD Simulator + +usb:v1F28* + ID_VENDOR_FROM_DATABASE=Cal-Comp + +usb:v1F28p0020* + ID_MODEL_FROM_DATABASE=CDMA USB Modem A600 + +usb:v1F28p0021* + ID_MODEL_FROM_DATABASE=CD INSTALLER USB Device + +usb:v1F3A* + ID_VENDOR_FROM_DATABASE=Onda (unverified) + +usb:v1F3ApEFE8* + ID_MODEL_FROM_DATABASE=V972 tablet in flashing mode + +usb:v1F44* + ID_VENDOR_FROM_DATABASE=The Neat Company + +usb:v1F44p0001* + ID_MODEL_FROM_DATABASE=NM-1000 scanner + +usb:v1F48* + ID_VENDOR_FROM_DATABASE=H-TRONIC GmbH + +usb:v1F48p0627* + ID_MODEL_FROM_DATABASE=Data capturing system + +usb:v1F48p0628* + ID_MODEL_FROM_DATABASE=Data capturing and control module + +usb:v1F4D* + ID_VENDOR_FROM_DATABASE=G-Tek Electronics Group + +usb:v1F4DpB803* + ID_MODEL_FROM_DATABASE=Lifeview LV5TDLX DVB-T [RTL2832U] + +usb:v1F4DpD220* + ID_MODEL_FROM_DATABASE=Geniatech T220 DVB-T2 TV Stick + +usb:v1F6F* + ID_VENDOR_FROM_DATABASE=Aliph + +usb:v1F6Fp0023* + ID_MODEL_FROM_DATABASE=Jawbone Jambox + +usb:v1F6Fp8000* + ID_MODEL_FROM_DATABASE=Jawbone Jambox - Updating + +usb:v1F75* + ID_VENDOR_FROM_DATABASE=Innostor Technology Corporation + +usb:v1F75p0888* + ID_MODEL_FROM_DATABASE=IS888 SATA Storage Controller + +usb:v1F75p0902* + ID_MODEL_FROM_DATABASE=IS902 UFD controller + +usb:v1F82* + ID_VENDOR_FROM_DATABASE=TANDBERG + +usb:v1F82p0001* + ID_MODEL_FROM_DATABASE=PrecisionHD Camera + +usb:v1F84* + ID_VENDOR_FROM_DATABASE=Alere, Inc. + +usb:v1F87* + ID_VENDOR_FROM_DATABASE=Stantum + +usb:v1F87p0002* + ID_MODEL_FROM_DATABASE=Multi-touch HID Controller + +usb:v1F9B* + ID_VENDOR_FROM_DATABASE=Ubiquiti Networks, Inc. + +usb:v1F9Bp0241* + ID_MODEL_FROM_DATABASE=AirView2-EXT + +usb:v1FAB* + ID_VENDOR_FROM_DATABASE=Samsung Opto-Electroncs Co., Ltd. + +usb:v1FABp104D* + ID_MODEL_FROM_DATABASE=ES65 + +usb:v1FBD* + ID_VENDOR_FROM_DATABASE=Delphin Technology AG + +usb:v1FBDp0001* + ID_MODEL_FROM_DATABASE=Expert Key - Data aquisition system + +usb:v1FC9* + ID_VENDOR_FROM_DATABASE=NXP Semiconductors + +usb:v1FC9p0003* + ID_MODEL_FROM_DATABASE=LPC1343 + +usb:v1FC9p010B* + ID_MODEL_FROM_DATABASE=PR533 + +usb:v1FDE* + ID_VENDOR_FROM_DATABASE=ILX Lightwave Corporation + +usb:v1FDEp0001* + ID_MODEL_FROM_DATABASE=UART Bridge + +usb:v1FE7* + ID_VENDOR_FROM_DATABASE=Vertex Wireless Co., Ltd. + +usb:v1FE7p1000* + ID_MODEL_FROM_DATABASE=VW100 series CDMA EV-DO Rev.A modem + +usb:v1FF7* + ID_VENDOR_FROM_DATABASE=CVT Electronics.Co.,Ltd + +usb:v1FF7p0013* + ID_MODEL_FROM_DATABASE=CVTouch Screen (HID) + +usb:v1FF7p001A* + ID_MODEL_FROM_DATABASE=Human Interface Device + +usb:v1FFF* + ID_VENDOR_FROM_DATABASE=Ideofy Inc. + +usb:v2001* + ID_VENDOR_FROM_DATABASE=D-Link Corp. + +usb:v2001p0001* + ID_MODEL_FROM_DATABASE=DWL-120 WIRELESS ADAPTER + +usb:v2001p0201* + ID_MODEL_FROM_DATABASE=DHN-120 10Mb Home Phoneline Adapter + +usb:v2001p1A00* + ID_MODEL_FROM_DATABASE=DUB-E100 Fast Ethernet Adapter(rev.A) [ASIX AX88172] + +usb:v2001p1A02* + ID_MODEL_FROM_DATABASE=DUB-E100 Fast Ethernet Adapter(rev.C1) [ASIX AX88772] + +usb:v2001p200C* + ID_MODEL_FROM_DATABASE=10/100 Ethernet + +usb:v2001p3200* + ID_MODEL_FROM_DATABASE=DWL-120 802.11b Wireless Adapter(rev.E1) [Atmel at76c503a] + +usb:v2001p3301* + ID_MODEL_FROM_DATABASE=DWA-130 802.11n Wireless N Adapter(rev.C1) [Realtek RTL8192U] + +usb:v2001p3306* + ID_MODEL_FROM_DATABASE=DWL-G122 Wireless Adapter(rev.F1) [Realtek RTL8188SU] + +usb:v2001p3308* + ID_MODEL_FROM_DATABASE=DWA-121 802.11n Wireless N 150 Pico Adapter [Realtek RTL8188CUS] + +usb:v2001p3309* + ID_MODEL_FROM_DATABASE=DWA-135 802.11n Wireless N Adapter(rev.A1) [Realtek RTL8192CU] + +usb:v2001p330A* + ID_MODEL_FROM_DATABASE=DWA-133 802.11n Wireless N Adapter [Realtek RTL8192CU] + +usb:v2001p3500* + ID_MODEL_FROM_DATABASE=Elitegroup Computer Systems WLAN card WL-162 + +usb:v2001p3700* + ID_MODEL_FROM_DATABASE=DWL-122 802.11b [Intersil Prism 3] + +usb:v2001p3701* + ID_MODEL_FROM_DATABASE=DWL-G120 Spinnaker 802.11g [Intersil ISL3886] + +usb:v2001p3702* + ID_MODEL_FROM_DATABASE=DWL-120 802.11b Wireless Adapter(rev.F) [Intersil ISL3871] + +usb:v2001p3703* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.A1) [Intersil ISL3880] + +usb:v2001p3704* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.A2) [Intersil ISL3887] + +usb:v2001p3705* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G120 Wireless Adapter(rev.C) [Intersil ISL3887] + +usb:v2001p3761* + ID_MODEL_FROM_DATABASE=IEEE 802.11g USB2.0 Wireless Network Adapter-PN + +usb:v2001p3A00* + ID_MODEL_FROM_DATABASE=DWL-AG132 [Atheros AR5523] + +usb:v2001p3A01* + ID_MODEL_FROM_DATABASE=DWL-AG132 (no firmware) [Atheros AR5523] + +usb:v2001p3A02* + ID_MODEL_FROM_DATABASE=DWL-G132 [Atheros AR5523] + +usb:v2001p3A03* + ID_MODEL_FROM_DATABASE=DWL-G132 (no firmware) [Atheros AR5523] + +usb:v2001p3A04* + ID_MODEL_FROM_DATABASE=DWL-AG122 [Atheros AR5523] + +usb:v2001p3A05* + ID_MODEL_FROM_DATABASE=DWL-AG122 (no firmware) [Atheros AR5523] + +usb:v2001p3A80* + ID_MODEL_FROM_DATABASE=AirPlus Xtreme G DWL-G132 Wireless Adapter + +usb:v2001p3A81* + ID_MODEL_FROM_DATABASE=predator Bootloader Download + +usb:v2001p3A82* + ID_MODEL_FROM_DATABASE=AirPremier AG DWL-AG132 Wireless Adapter + +usb:v2001p3A83* + ID_MODEL_FROM_DATABASE=predator Bootloader Download + +usb:v2001p3B00* + ID_MODEL_FROM_DATABASE=AirPlus DWL-120+ Wireless Adapter [Texas Instruments ACX100USB] + +usb:v2001p3B01* + ID_MODEL_FROM_DATABASE=WLAN Boot Device + +usb:v2001p3C00* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter(rev.B1) [Ralink RT2571] + +usb:v2001p3C01* + ID_MODEL_FROM_DATABASE=AirPlus AG DWL-AG122 Wireless Adapter + +usb:v2001p3C02* + ID_MODEL_FROM_DATABASE=AirPlus G DWL-G122 Wireless Adapter + +usb:v2001p3C05* + ID_MODEL_FROM_DATABASE=DUB-E100 Fast Ethernet Adapter(rev.B1) [ASIX AX88772] + +usb:v2001p3C15* + ID_MODEL_FROM_DATABASE=DWA-140 RangeBooster N Adapter(rev.B3) [Ralink RT5372] + +usb:v2001p3C17* + ID_MODEL_FROM_DATABASE=DWA-123 Wireless N 150 Adapter(rev.A1) [Ralink RT3370] + +usb:v2001p3C19* + ID_MODEL_FROM_DATABASE=DWA-125 Wireless N 150 Adapter(rev.A3) [Ralink RT5370] + +usb:v2001p3C1A* + ID_MODEL_FROM_DATABASE=DWA-160 802.11abgn Xtreme N Dual Band Adapter(rev.B2) [Ralink RT5572] + +usb:v2001p3C1B* + ID_MODEL_FROM_DATABASE=DWA-127 Wireless N 150 High-Gain Adapter(rev.A1) [Ralink RT3070] + +usb:v2001p4000* + ID_MODEL_FROM_DATABASE=DSB-650C Ethernet [klsi] + +usb:v2001p4001* + ID_MODEL_FROM_DATABASE=DSB-650TX Ethernet [pegasus] + +usb:v2001p4002* + ID_MODEL_FROM_DATABASE=DSB-650TX Ethernet [pegasus] + +usb:v2001p4003* + ID_MODEL_FROM_DATABASE=DSB-650TX-PNA Ethernet [pegasus] + +usb:v2001p400B* + ID_MODEL_FROM_DATABASE=10/100 Ethernet + +usb:v2001p4102* + ID_MODEL_FROM_DATABASE=10/100 Ethernet + +usb:v2001p5100* + ID_MODEL_FROM_DATABASE=DSL-200 ADSL ATM Modem + +usb:v2001p5102* + ID_MODEL_FROM_DATABASE=DSL-200 ADSL Loader + +usb:v2001p5B00* + ID_MODEL_FROM_DATABASE=Remote NDIS Network Device + +usb:v2001p9414* + ID_MODEL_FROM_DATABASE=Cable Modem + +usb:v2001p9B00* + ID_MODEL_FROM_DATABASE=Broadband Cable Modem Remote NDIS Device + +usb:v2001pABC1* + ID_MODEL_FROM_DATABASE=DSB-650 Ethernet [pegasus] + +usb:v2001pF013* + ID_MODEL_FROM_DATABASE=DLink 7 port USB2.0 Hub + +usb:v2001pF103* + ID_MODEL_FROM_DATABASE=DUB-H7 7-port USB 2.0 hub + +usb:v2001pF10D* + ID_MODEL_FROM_DATABASE=Accent Communications Modem + +usb:v2001pF110* + ID_MODEL_FROM_DATABASE=DUB-AV300 A/V Capture + +usb:v2001pF111* + ID_MODEL_FROM_DATABASE=DBT-122 Bluetooth adapter + +usb:v2001pF112* + ID_MODEL_FROM_DATABASE=DUB-T210 Audio Device + +usb:v2001pF116* + ID_MODEL_FROM_DATABASE=Formosa 2 + +usb:v2001pF117* + ID_MODEL_FROM_DATABASE=Formosa 3 + +usb:v2001pF118* + ID_MODEL_FROM_DATABASE=Formosa 4 + +usb:v2002* + ID_VENDOR_FROM_DATABASE=DAP Technologies + +usb:v2003* + ID_VENDOR_FROM_DATABASE=detectomat + +usb:v2003pEA61* + ID_MODEL_FROM_DATABASE=dc3500 + +usb:v200C* + ID_VENDOR_FROM_DATABASE=Reloop + +usb:v200Cp100B* + ID_MODEL_FROM_DATABASE=Play audio soundcard + +usb:v2013* + ID_VENDOR_FROM_DATABASE=PCTV Systems + +usb:v2013p0245* + ID_MODEL_FROM_DATABASE=PCTV 73ESE + +usb:v2013p0246* + ID_MODEL_FROM_DATABASE=PCTV 74E + +usb:v2013p0248* + ID_MODEL_FROM_DATABASE=PCTV 282E + +usb:v2013p024F* + ID_MODEL_FROM_DATABASE=nanoStick T2 290e + +usb:v2019* + ID_VENDOR_FROM_DATABASE=PLANEX + +usb:v2019p3220* + ID_MODEL_FROM_DATABASE=GW-US11S WLAN [Atmel AT76C503A] + +usb:v2019p4901* + ID_MODEL_FROM_DATABASE=GW-USSuper300 802.11bgn Wireless Adapter [Realtek RTL8191SU] + +usb:v2019p4903* + ID_MODEL_FROM_DATABASE=GW-USFang300 802.11abgn Wireless Adapter [Realtek RTL8192DU] + +usb:v2019p4904* + ID_MODEL_FROM_DATABASE=GW-USUltra300 802.11abgn Wireless Adapter [Realtek RTL8192DU] + +usb:v2019p5303* + ID_MODEL_FROM_DATABASE=GW-US54GXS 802.11bg + +usb:v2019p5304* + ID_MODEL_FROM_DATABASE=GWUS300 802.11n + +usb:v2019pAB01* + ID_MODEL_FROM_DATABASE=GW-US54HP + +usb:v2019pAB24* + ID_MODEL_FROM_DATABASE=GW-US300MiniS + +usb:v2019pAB25* + ID_MODEL_FROM_DATABASE=GW-USMini2N 802.11n Wireless Adapter [Ralink RT2870] + +usb:v2019pAB28* + ID_MODEL_FROM_DATABASE=GW-USNano + +usb:v2019pAB29* + ID_MODEL_FROM_DATABASE=GW-USMicro300 + +usb:v2019pAB2A* + ID_MODEL_FROM_DATABASE=GW-USNano2 802.11n Wireless Adapter [Realtek RTL8188CUS] + +usb:v2019pAB2B* + ID_MODEL_FROM_DATABASE=GW-USEco300 802.11bgn Wireless Adapter [Realtek RTL8192CU] + +usb:v2019pAB2C* + ID_MODEL_FROM_DATABASE=GW-USDual300 802.11abgn Wireless Adapter [Realtek RTL8192DU] + +usb:v2019pAB50* + ID_MODEL_FROM_DATABASE=GW-US54Mini2 + +usb:v2019pC002* + ID_MODEL_FROM_DATABASE=GW-US54SG + +usb:v2019pC007* + ID_MODEL_FROM_DATABASE=GW-US54GZL + +usb:v2019pED02* + ID_MODEL_FROM_DATABASE=GW-USMM + +usb:v2019pED06* + ID_MODEL_FROM_DATABASE=GW-US300MiniW 802.11bgn Wireless Adapter + +usb:v2019pED10* + ID_MODEL_FROM_DATABASE=GW-US300Mini2 + +usb:v2019pED14* + ID_MODEL_FROM_DATABASE=GW-USMicroN + +usb:v2019pED16* + ID_MODEL_FROM_DATABASE=GW-USMicroN2W 802.11bgn Wireless Adapter [Realtek RTL8188SU] + +usb:v2019pED17* + ID_MODEL_FROM_DATABASE=GW-USValue-EZ 802.11n Wireless Adapter [Realtek RTL8188CUS] + +usb:v2019pED18* + ID_MODEL_FROM_DATABASE=GW-USHyper300 / GW-USH300N 802.11bgn Wireless Adapter [Realtek RTL8191SU] + +usb:v203D* + ID_VENDOR_FROM_DATABASE=Encore Electronics Inc. + +usb:v203Dp1480* + ID_MODEL_FROM_DATABASE=ENUWI-N3 [802.11n Wireless N150 Adapter] + +usb:v2040* + ID_VENDOR_FROM_DATABASE=Hauppauge + +usb:v2040p0C80* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p0C90* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p1700* + ID_MODEL_FROM_DATABASE=CataMount + +usb:v2040p1800* + ID_MODEL_FROM_DATABASE=Okemo A + +usb:v2040p1801* + ID_MODEL_FROM_DATABASE=Okemo B + +usb:v2040p2000* + ID_MODEL_FROM_DATABASE=Tiger Minicard + +usb:v2040p2009* + ID_MODEL_FROM_DATABASE=Tiger Minicard R2 + +usb:v2040p200A* + ID_MODEL_FROM_DATABASE=Tiger Minicard + +usb:v2040p2010* + ID_MODEL_FROM_DATABASE=Tiger Minicard + +usb:v2040p2011* + ID_MODEL_FROM_DATABASE=WinTV MiniCard [Dell Digital TV Receiver] + +usb:v2040p2019* + ID_MODEL_FROM_DATABASE=Tiger Minicard + +usb:v2040p2400* + ID_MODEL_FROM_DATABASE=WinTV PVR USB2 (Model 24019) + +usb:v2040p4700* + ID_MODEL_FROM_DATABASE=WinTV Nova-S-USB2 + +usb:v2040p4902* + ID_MODEL_FROM_DATABASE=HD PVR + +usb:v2040p4903* + ID_MODEL_FROM_DATABASE=HS PVR + +usb:v2040p4982* + ID_MODEL_FROM_DATABASE=HD PVR + +usb:v2040p5500* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p5510* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p5520* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p5530* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p5580* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p5590* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040p6500* + ID_MODEL_FROM_DATABASE=WinTV HVR-900 + +usb:v2040p6502* + ID_MODEL_FROM_DATABASE=WinTV HVR-900 + +usb:v2040p6503* + ID_MODEL_FROM_DATABASE=WinTV HVR-930 + +usb:v2040p6513* + ID_MODEL_FROM_DATABASE=WinTV HVR-980 + +usb:v2040p7050* + ID_MODEL_FROM_DATABASE=Nova-T Stick + +usb:v2040p7060* + ID_MODEL_FROM_DATABASE=Nova-T Stick 2 + +usb:v2040p7070* + ID_MODEL_FROM_DATABASE=Nova-T Stick 3 + +usb:v2040p7240* + ID_MODEL_FROM_DATABASE=WinTV HVR-850 + +usb:v2040p8400* + ID_MODEL_FROM_DATABASE=WinTV Nova-T-500 + +usb:v2040p9300* + ID_MODEL_FROM_DATABASE=WinTV NOVA-T USB2 (cold) + +usb:v2040p9301* + ID_MODEL_FROM_DATABASE=WinTV NOVA-T USB2 (warm) + +usb:v2040p9941* + ID_MODEL_FROM_DATABASE=WinTV Nova-T-500 + +usb:v2040p9950* + ID_MODEL_FROM_DATABASE=WinTV Nova-T-500 + +usb:v2040pB910* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040pB980* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040pB990* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040pC000* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2040pC010* + ID_MODEL_FROM_DATABASE=Windham + +usb:v2047* + ID_VENDOR_FROM_DATABASE=Texas Instruments + +usb:v2047p0200* + ID_MODEL_FROM_DATABASE=MSP430 USB HID Bootstrap Loader + +usb:v2047p0855* + ID_MODEL_FROM_DATABASE=Invensense Embedded MotionApp HID Sensor + +usb:v2047p0964* + ID_MODEL_FROM_DATABASE=Inventio Software MSP430 + +usb:v2058* + ID_VENDOR_FROM_DATABASE=Nano River Technology + +usb:v2058p2058* + ID_MODEL_FROM_DATABASE=ViperBoard I2C, SPI, GPIO interface + +usb:v2077* + ID_VENDOR_FROM_DATABASE=Taicang T&W Electronics Co. Ltd + +usb:v2077p9002* + ID_MODEL_FROM_DATABASE=W1M100 HSPA/WCDMA Module + +usb:v2080* + ID_VENDOR_FROM_DATABASE=Barnes & Noble + +usb:v2080p0001* + ID_MODEL_FROM_DATABASE=nook + +usb:v2080p0002* + ID_MODEL_FROM_DATABASE=NOOKcolor + +usb:v2080p0003* + ID_MODEL_FROM_DATABASE=NOOK Simple Touch + +usb:v2080p0004* + ID_MODEL_FROM_DATABASE=NOOK Tablet + +usb:v2086* + ID_VENDOR_FROM_DATABASE=SIMPASS + +usb:v2087* + ID_VENDOR_FROM_DATABASE=Cando + +usb:v2087p0A01* + ID_MODEL_FROM_DATABASE=Multi Touch Panel + +usb:v2087p0A02* + ID_MODEL_FROM_DATABASE=Multi Touch Panel + +usb:v2087p0B03* + ID_MODEL_FROM_DATABASE=Multi Touch Panel + +usb:v20A0* + ID_VENDOR_FROM_DATABASE=Clay Logic + +usb:v20A0p4123* + ID_MODEL_FROM_DATABASE=IKALOGIC SCANALOGIC 2 + +usb:v20A0p414A* + ID_MODEL_FROM_DATABASE=MDE SPI Interface + +usb:v20A0p415A* + ID_MODEL_FROM_DATABASE=OpenPilot + +usb:v20A0p415B* + ID_MODEL_FROM_DATABASE=CopterControl + +usb:v20A0p415C* + ID_MODEL_FROM_DATABASE=PipXtreme + +usb:v20B1* + ID_VENDOR_FROM_DATABASE=XMOS Ltd + +usb:v20B1p10AD* + ID_MODEL_FROM_DATABASE=XUSB Loader + +usb:v20B1pF7D1* + ID_MODEL_FROM_DATABASE=XTAG2 - JTAG Adapter + +usb:v20B3* + ID_VENDOR_FROM_DATABASE=Hanvon + +usb:v20B3p0A18* + ID_MODEL_FROM_DATABASE=10.1 Touch screen overlay + +usb:v20B7* + ID_VENDOR_FROM_DATABASE=Qi Hardware + +usb:v20B7p0713* + ID_MODEL_FROM_DATABASE=Milkymist JTAG/serial + +usb:v20B7p1540* + ID_MODEL_FROM_DATABASE=ben-wpan, AT86RF230-based + +usb:v20B7p1DB5* + ID_MODEL_FROM_DATABASE=IDBG in DFU mode + +usb:v20B7p1DB6* + ID_MODEL_FROM_DATABASE=IDBG in normal mode + +usb:v20B7pC25B* + ID_MODEL_FROM_DATABASE=C2 Dongle + +usb:v20B7pCB72* + ID_MODEL_FROM_DATABASE=ben-wpan, cntr + +usb:v20CE* + ID_VENDOR_FROM_DATABASE=Minicircuits + +usb:v20CEp0012* + ID_MODEL_FROM_DATABASE=RF Sythesizer 250-4200MHz model SSG-4000LH + +usb:v20CEp0021* + ID_MODEL_FROM_DATABASE=RF Switch Matrix + +usb:v20CEp0022* + ID_MODEL_FROM_DATABASE=I/O Controller + +usb:v20DF* + ID_VENDOR_FROM_DATABASE=Simtec Electronics + +usb:v20DFp0001* + ID_MODEL_FROM_DATABASE=Entropy Key [UDEKEY01] + +usb:v20F1* + ID_VENDOR_FROM_DATABASE=NET New Electronic Technology GmbH + +usb:v20F1p0101* + ID_MODEL_FROM_DATABASE=iCube3 Camera + +usb:v20F4* + ID_VENDOR_FROM_DATABASE=TRENDnet + +usb:v20F4p648B* + ID_MODEL_FROM_DATABASE=TEW-648UBM 802.11n 150Mbps Micro Wireless N Adapter [Realtek RTL8188CUS] + +usb:v20F7* + ID_VENDOR_FROM_DATABASE=XIMEA + +usb:v20F7p3001* + ID_MODEL_FROM_DATABASE=Camera with CMOS sensor [MQ] + +usb:v20F7p3021* + ID_MODEL_FROM_DATABASE=Camera with CCD sensor [MD] + +usb:v20F7p30B3* + ID_MODEL_FROM_DATABASE=Camera with CMOS sensor in Vision mode [MQ] + +usb:v20F7pA003* + ID_MODEL_FROM_DATABASE=Subminiature 5Mpix B/W Camera, MU9PM-MH + +usb:v2100* + ID_VENDOR_FROM_DATABASE=RT Systems + +usb:v2100p9E52* + ID_MODEL_FROM_DATABASE=Yaesu VX-7 + +usb:v2100p9E54* + ID_MODEL_FROM_DATABASE=CT29B Radio Cable + +usb:v2100p9E57* + ID_MODEL_FROM_DATABASE=RTS01 Radio Cable + +usb:v2100p9E5D* + ID_MODEL_FROM_DATABASE=K4Y Radio Cable + +usb:v2100p9E5F* + ID_MODEL_FROM_DATABASE=FT232RL [RTS05 Serial Cable] + +usb:v2101* + ID_VENDOR_FROM_DATABASE=ActionStar + +usb:v2101p0201* + ID_MODEL_FROM_DATABASE=SIIG 4-to-2 Printer Switch + +usb:v2109* + ID_VENDOR_FROM_DATABASE=VIA Labs, Inc. + +usb:v2109p0700* + ID_MODEL_FROM_DATABASE=VL700 SATA 3Gb/s bridge + +usb:v2109p0701* + ID_MODEL_FROM_DATABASE=VL701 SATA 3Gb/s bridge + +usb:v2109p0810* + ID_MODEL_FROM_DATABASE=VL81x Hub + +usb:v2109p0811* + ID_MODEL_FROM_DATABASE=Hub + +usb:v2109p0812* + ID_MODEL_FROM_DATABASE=VL812 Hub + +usb:v2109p2811* + ID_MODEL_FROM_DATABASE=Hub + +usb:v2109p2812* + ID_MODEL_FROM_DATABASE=VL812 Hub + +usb:v2109p3431* + ID_MODEL_FROM_DATABASE=Hub + +usb:v2109p8110* + ID_MODEL_FROM_DATABASE=Hub + +usb:v2113* + ID_VENDOR_FROM_DATABASE=Softkinetic + +usb:v2113p0137* + ID_MODEL_FROM_DATABASE=DepthSense 311 (3D) + +usb:v2113p0145* + ID_MODEL_FROM_DATABASE=DepthSense 325 + +usb:v2113p8000* + ID_MODEL_FROM_DATABASE=DepthSense 311 (Color) + +usb:v2149* + ID_VENDOR_FROM_DATABASE=Advanced Silicon S.A. + +usb:v2149p211B* + ID_MODEL_FROM_DATABASE=Touchscreen Controller + +usb:v2149p2703* + ID_MODEL_FROM_DATABASE=TS58xxA/TC56xxA [CoolTouch] + +usb:v2162* + ID_VENDOR_FROM_DATABASE=Creative (?) + +usb:v2162p2031* + ID_MODEL_FROM_DATABASE=Network Blaster Wireless Adapter + +usb:v2162p500C* + ID_MODEL_FROM_DATABASE=DE5771 Modem Blaster + +usb:v2162p8001* + ID_MODEL_FROM_DATABASE=Broadxent BritePort DSL Bridge 8010U + +usb:v2184* + ID_VENDOR_FROM_DATABASE=GW Instek + +usb:v2184p0005* + ID_MODEL_FROM_DATABASE=GDS-3000 Oscilloscope + +usb:v2184p0006* + ID_MODEL_FROM_DATABASE=GDS-3000 Oscilloscope + +usb:v2184p0011* + ID_MODEL_FROM_DATABASE=AFG Function Generator (CDC) + +usb:v21A1* + ID_VENDOR_FROM_DATABASE=Emotiv Systems Pty. Ltd. + +usb:v21A1p0001* + ID_MODEL_FROM_DATABASE=EPOC Consumer Headset Wireless Dongle + +usb:v21D6* + ID_VENDOR_FROM_DATABASE=Agecodagis SARL + +usb:v21D6p0002* + ID_MODEL_FROM_DATABASE=Seismic recorder [Tellus] + +usb:v2222* + ID_VENDOR_FROM_DATABASE=MacAlly + +usb:v2222p0004* + ID_MODEL_FROM_DATABASE=iWebKey Keyboard + +usb:v2222p2520* + ID_MODEL_FROM_DATABASE=Mini Tablet + +usb:v2222p4050* + ID_MODEL_FROM_DATABASE=AirStick joystick + +usb:v2227* + ID_VENDOR_FROM_DATABASE=SAMWOO Enterprise + +usb:v2227p3105* + ID_MODEL_FROM_DATABASE=SKYDATA SKD-U100 + +usb:v2232* + ID_VENDOR_FROM_DATABASE=Silicon Motion + +usb:v2232p1005* + ID_MODEL_FROM_DATABASE=WebCam SCB-0385N + +usb:v2232p1028* + ID_MODEL_FROM_DATABASE=WebCam SC-03FFL11939N + +usb:v2232p1029* + ID_MODEL_FROM_DATABASE=WebCam SC-13HDL11939N + +usb:v2232p1037* + ID_MODEL_FROM_DATABASE=WebCam SC-03FFM12339N + +usb:v2233* + ID_VENDOR_FROM_DATABASE=RadioShack Corporation + +usb:v2233p6323* + ID_MODEL_FROM_DATABASE=USB Electronic Scale + +usb:v2237* + ID_VENDOR_FROM_DATABASE=Kobo Inc. + +usb:v2237p4161* + ID_MODEL_FROM_DATABASE=eReader White + +usb:v225D* + ID_VENDOR_FROM_DATABASE=Morpho + +usb:v225Dp0001* + ID_MODEL_FROM_DATABASE=FINGER VP Multimodal Biometric Sensor + +usb:v225Dp0008* + ID_MODEL_FROM_DATABASE=CBM-E3 Fingerprint Sensor + +usb:v225Dp0009* + ID_MODEL_FROM_DATABASE=CBM Fingerprint Sensor [CBM-V3] + +usb:v225Dp000A* + ID_MODEL_FROM_DATABASE=MSO1300-E3 Fingerprint Sensor + +usb:v225Dp000B* + ID_MODEL_FROM_DATABASE=MSO1300 Fingerprint Sensor [MSO1300-V3] + +usb:v225Dp000C* + ID_MODEL_FROM_DATABASE=MSO1350-E3 Fingerprint Sensor & SmartCard Reader + +usb:v225Dp000D* + ID_MODEL_FROM_DATABASE=MSO1350 Fingerprint Sensor & SmartCard Reader [MSO1350-V3] + +usb:v225Dp000E* + ID_MODEL_FROM_DATABASE=MorphoAccess SIGMA Biometric Access Control Terminal + +usb:v228D* + ID_VENDOR_FROM_DATABASE=8D Technologies inc. + +usb:v228Dp0001* + ID_MODEL_FROM_DATABASE=Terminal Bike Key Reader + +usb:v22A6* + ID_VENDOR_FROM_DATABASE=Pie Digital, Inc. + +usb:v22A6pFFFF* + ID_MODEL_FROM_DATABASE=PieKey "beta" 4GB model 4E4F41482E4F5247 (SM3251Q BB) + +usb:v22B8* + ID_VENDOR_FROM_DATABASE=Motorola PCS + +usb:v22B8p0001* + ID_MODEL_FROM_DATABASE=Wally 2.2 chipset + +usb:v22B8p0002* + ID_MODEL_FROM_DATABASE=Wally 2.4 chipset + +usb:v22B8p0005* + ID_MODEL_FROM_DATABASE=V.60c/V.60i GSM Phone + +usb:v22B8p0830* + ID_MODEL_FROM_DATABASE=2386C-HT820 + +usb:v22B8p0833* + ID_MODEL_FROM_DATABASE=2386C-HT820 [Flash Mode] + +usb:v22B8p0850* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v22B8p1001* + ID_MODEL_FROM_DATABASE=Patriot 1.0 (GSM) chipset + +usb:v22B8p1002* + ID_MODEL_FROM_DATABASE=Patriot 2.0 chipset + +usb:v22B8p1005* + ID_MODEL_FROM_DATABASE=T280e GSM/GPRS Phone + +usb:v22B8p1101* + ID_MODEL_FROM_DATABASE=Patriot 1.0 (TDMA) chipset + +usb:v22B8p1801* + ID_MODEL_FROM_DATABASE=Rainbow chipset flash + +usb:v22B8p2035* + ID_MODEL_FROM_DATABASE=Bluetooth Device + +usb:v22B8p2805* + ID_MODEL_FROM_DATABASE=GSM Modem + +usb:v22B8p2821* + ID_MODEL_FROM_DATABASE=T720 GSM Phone + +usb:v22B8p2822* + ID_MODEL_FROM_DATABASE=V.120e GSM Phone + +usb:v22B8p2823* + ID_MODEL_FROM_DATABASE=Flash Interface + +usb:v22B8p2A01* + ID_MODEL_FROM_DATABASE=MSM6050 chipset + +usb:v22B8p2A02* + ID_MODEL_FROM_DATABASE=CDMA modem + +usb:v22B8p2A03* + ID_MODEL_FROM_DATABASE=MSM6050 chipset flash + +usb:v22B8p2A21* + ID_MODEL_FROM_DATABASE=V710 GSM Phone (P2K) + +usb:v22B8p2A22* + ID_MODEL_FROM_DATABASE=V710 GSM Phone (AT) + +usb:v22B8p2A23* + ID_MODEL_FROM_DATABASE=MSM6100 chipset flash + +usb:v22B8p2A41* + ID_MODEL_FROM_DATABASE=MSM6300 chipset + +usb:v22B8p2A42* + ID_MODEL_FROM_DATABASE=Usb Modem + +usb:v22B8p2A43* + ID_MODEL_FROM_DATABASE=MSM6300 chipset flash + +usb:v22B8p2A61* + ID_MODEL_FROM_DATABASE=E815 GSM Phone (P2K) + +usb:v22B8p2A62* + ID_MODEL_FROM_DATABASE=E815 GSM Phone (AT) + +usb:v22B8p2A63* + ID_MODEL_FROM_DATABASE=MSM6500 chipset flash + +usb:v22B8p2A81* + ID_MODEL_FROM_DATABASE=MSM6025 chipset + +usb:v22B8p2A83* + ID_MODEL_FROM_DATABASE=MSM6025 chipset flash + +usb:v22B8p2AC1* + ID_MODEL_FROM_DATABASE=MSM6100 chipset + +usb:v22B8p2AC3* + ID_MODEL_FROM_DATABASE=MSM6100 chipset flash + +usb:v22B8p2D78* + ID_MODEL_FROM_DATABASE=XT300[SPICE] + +usb:v22B8p3001* + ID_MODEL_FROM_DATABASE=A835/E1000 GSM Phone (P2K) + +usb:v22B8p3002* + ID_MODEL_FROM_DATABASE=A835/E1000 GSM Phone (AT) + +usb:v22B8p3801* + ID_MODEL_FROM_DATABASE=C350L/C450 (P2K) + +usb:v22B8p3802* + ID_MODEL_FROM_DATABASE=C330/C350L/C450/EZX GSM Phone (AT) + +usb:v22B8p3803* + ID_MODEL_FROM_DATABASE=Neptune LT chipset flash + +usb:v22B8p4001* + ID_MODEL_FROM_DATABASE=OMAP 1.0 chipset + +usb:v22B8p4002* + ID_MODEL_FROM_DATABASE=A920/A925 UMTS Phone + +usb:v22B8p4003* + ID_MODEL_FROM_DATABASE=OMAP 1.0 chipset flash + +usb:v22B8p4008* + ID_MODEL_FROM_DATABASE=OMAP 1.0 chipset RDL + +usb:v22B8p41D6* + ID_MODEL_FROM_DATABASE=Droid X (Windows media mode) + +usb:v22B8p41D9* + ID_MODEL_FROM_DATABASE=Droid/Milestone + +usb:v22B8p41DB* + ID_MODEL_FROM_DATABASE=Droid/Milestone (Debug mode) + +usb:v22B8p41DE* + ID_MODEL_FROM_DATABASE=Droid X (PC mode) + +usb:v22B8p4204* + ID_MODEL_FROM_DATABASE=MPx200 Smartphone + +usb:v22B8p4214* + ID_MODEL_FROM_DATABASE=MPc GSM + +usb:v22B8p4224* + ID_MODEL_FROM_DATABASE=MPx220 Smartphone + +usb:v22B8p4234* + ID_MODEL_FROM_DATABASE=MPc CDMA + +usb:v22B8p4244* + ID_MODEL_FROM_DATABASE=MPx100 Smartphone + +usb:v22B8p4285* + ID_MODEL_FROM_DATABASE=Droid X (Mass storage) + +usb:v22B8p4801* + ID_MODEL_FROM_DATABASE=Neptune LTS chipset + +usb:v22B8p4803* + ID_MODEL_FROM_DATABASE=Neptune LTS chipset flash + +usb:v22B8p4810* + ID_MODEL_FROM_DATABASE=Triplet GSM Phone (storage) + +usb:v22B8p4901* + ID_MODEL_FROM_DATABASE=Triplet GSM Phone (P2K) + +usb:v22B8p4902* + ID_MODEL_FROM_DATABASE=Triplet GSM Phone (AT) + +usb:v22B8p4903* + ID_MODEL_FROM_DATABASE=Neptune LTE chipset flash + +usb:v22B8p4A01* + ID_MODEL_FROM_DATABASE=Neptune LTX chipset + +usb:v22B8p4A03* + ID_MODEL_FROM_DATABASE=Neptune LTX chipset flash + +usb:v22B8p4A32* + ID_MODEL_FROM_DATABASE=L6-imode Phone + +usb:v22B8p5801* + ID_MODEL_FROM_DATABASE=Neptune ULS chipset + +usb:v22B8p5803* + ID_MODEL_FROM_DATABASE=Neptune ULS chipset flash + +usb:v22B8p5901* + ID_MODEL_FROM_DATABASE=Neptune VLT chipset + +usb:v22B8p5903* + ID_MODEL_FROM_DATABASE=Neptune VLT chipset flash + +usb:v22B8p6001* + ID_MODEL_FROM_DATABASE=Dalhart EZX + +usb:v22B8p6003* + ID_MODEL_FROM_DATABASE=Dalhart flash + +usb:v22B8p6004* + ID_MODEL_FROM_DATABASE=EZX GSM Phone (CDC Net) + +usb:v22B8p6006* + ID_MODEL_FROM_DATABASE=MOTOROKR E6 + +usb:v22B8p6008* + ID_MODEL_FROM_DATABASE=Dalhart RDL + +usb:v22B8p6009* + ID_MODEL_FROM_DATABASE=EZX GSM Phone (P2K) + +usb:v22B8p600A* + ID_MODEL_FROM_DATABASE=Dalhart EZX config 17 + +usb:v22B8p600B* + ID_MODEL_FROM_DATABASE=Dalhart EZX config 18 + +usb:v22B8p600C* + ID_MODEL_FROM_DATABASE=EZX GSM Phone (USBLAN) + +usb:v22B8p6021* + ID_MODEL_FROM_DATABASE=JUIX chipset + +usb:v22B8p6023* + ID_MODEL_FROM_DATABASE=JUIX chipset flash + +usb:v22B8p6026* + ID_MODEL_FROM_DATABASE=Flash RAM Downloader/miniOS + +usb:v22B8p6027* + ID_MODEL_FROM_DATABASE=USBLAN + +usb:v22B8p604C* + ID_MODEL_FROM_DATABASE=EZX GSM Phone (Storage) + +usb:v22B8p6101* + ID_MODEL_FROM_DATABASE=Talon integrated chipset + +usb:v22B8p6401* + ID_MODEL_FROM_DATABASE=Argon chipset + +usb:v22B8p6403* + ID_MODEL_FROM_DATABASE=Argon chipset flash + +usb:v22B8p6415* + ID_MODEL_FROM_DATABASE=ROKR Z6 (MTP mode) + +usb:v22B8p6604* + ID_MODEL_FROM_DATABASE=Washington CDMA Phone + +usb:v22B8p6631* + ID_MODEL_FROM_DATABASE=CDC Modem + +usb:v22B8p7001* + ID_MODEL_FROM_DATABASE=Q Smartphone + +usb:v22B8pFE01* + ID_MODEL_FROM_DATABASE=StarTAC III MS900 + +usb:v22B9* + ID_VENDOR_FROM_DATABASE=eTurboTouch Technology, Inc. + +usb:v22B9p0006* + ID_MODEL_FROM_DATABASE=Touch Screen + +usb:v22BA* + ID_VENDOR_FROM_DATABASE=Technology Innovation Holdings, Ltd + +usb:v2304* + ID_VENDOR_FROM_DATABASE=Pinnacle Systems, Inc. + +usb:v2304p0109* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (SECAM) + +usb:v2304p0110* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (PAL) + +usb:v2304p0111* + ID_MODEL_FROM_DATABASE=Miro PCTV USB + +usb:v2304p0112* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (NTSC) with FM radio + +usb:v2304p0201* + ID_MODEL_FROM_DATABASE=Systems MovieBox Device + +usb:v2304p0204* + ID_MODEL_FROM_DATABASE=MovieBox USB_B + +usb:v2304p0205* + ID_MODEL_FROM_DATABASE=DVC 150B + +usb:v2304p0206* + ID_MODEL_FROM_DATABASE=Systems MovieBox Deluxe Device + +usb:v2304p0207* + ID_MODEL_FROM_DATABASE=Dazzle DVC90 Video Device + +usb:v2304p0208* + ID_MODEL_FROM_DATABASE=Studio PCTV USB2 + +usb:v2304p020E* + ID_MODEL_FROM_DATABASE=PCTV 200e + +usb:v2304p020F* + ID_MODEL_FROM_DATABASE=PCTV 400e BDA Device + +usb:v2304p0210* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (PAL) with FM radio + +usb:v2304p0212* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (NTSC) + +usb:v2304p0213* + ID_MODEL_FROM_DATABASE=500-USB Device + +usb:v2304p0214* + ID_MODEL_FROM_DATABASE=Studio PCTV USB (PAL) with FM radio + +usb:v2304p0216* + ID_MODEL_FROM_DATABASE=PCTV 60e + +usb:v2304p0219* + ID_MODEL_FROM_DATABASE=PCTV 260e + +usb:v2304p021A* + ID_MODEL_FROM_DATABASE=Dazzle DVC100 Audio Device + +usb:v2304p021B* + ID_MODEL_FROM_DATABASE=Dazzle DVC130/DVC170 + +usb:v2304p021D* + ID_MODEL_FROM_DATABASE=Dazzle DVC130 + +usb:v2304p021E* + ID_MODEL_FROM_DATABASE=Dazzle DVC170 + +usb:v2304p021F* + ID_MODEL_FROM_DATABASE=PCTV Sat HDTV Pro BDA Device + +usb:v2304p0222* + ID_MODEL_FROM_DATABASE=PCTV Sat Pro BDA Device + +usb:v2304p0223* + ID_MODEL_FROM_DATABASE=DazzleTV Sat BDA Device + +usb:v2304p0225* + ID_MODEL_FROM_DATABASE=Remote Kit Infrared Transceiver + +usb:v2304p0226* + ID_MODEL_FROM_DATABASE=PCTV 330e + +usb:v2304p0227* + ID_MODEL_FROM_DATABASE=PCTV for Mac, HD Stick + +usb:v2304p0228* + ID_MODEL_FROM_DATABASE=PCTV DVB-T Flash Stick + +usb:v2304p0229* + ID_MODEL_FROM_DATABASE=PCTV Dual DVB-T 2001e + +usb:v2304p022A* + ID_MODEL_FROM_DATABASE=PCTV 160e + +usb:v2304p022B* + ID_MODEL_FROM_DATABASE=PCTV 71e [Afatech AF9015] + +usb:v2304p0232* + ID_MODEL_FROM_DATABASE=PCTV 170e + +usb:v2304p0236* + ID_MODEL_FROM_DATABASE=PCTV 72e [DiBcom DiB7000PC] + +usb:v2304p0237* + ID_MODEL_FROM_DATABASE=PCTV 73e [DiBcom DiB7000PC] + +usb:v2304p023A* + ID_MODEL_FROM_DATABASE=PCTV 801e + +usb:v2304p023B* + ID_MODEL_FROM_DATABASE=PCTV 801e SE + +usb:v2304p023D* + ID_MODEL_FROM_DATABASE=PCTV 340e + +usb:v2304p023E* + ID_MODEL_FROM_DATABASE=PCTV 340e SE + +usb:v2304p0300* + ID_MODEL_FROM_DATABASE=Studio Linx Video input cable (NTSC) + +usb:v2304p0301* + ID_MODEL_FROM_DATABASE=Studio Linx Video input cable (PAL) + +usb:v2304p0302* + ID_MODEL_FROM_DATABASE=Dazzle DVC120 + +usb:v2304p0419* + ID_MODEL_FROM_DATABASE=PCTV Bungee USB (PAL) with FM radio + +usb:v2304p061D* + ID_MODEL_FROM_DATABASE=PCTV Deluxe (NTSC) Device + +usb:v2304p061E* + ID_MODEL_FROM_DATABASE=PCTV Deluxe (PAL) Device + +usb:v2318* + ID_VENDOR_FROM_DATABASE=Shining Technologies, Inc. [hex] + +usb:v2318p0011* + ID_MODEL_FROM_DATABASE=CitiDISK Jr. IDE Enclosure + +usb:v2341* + ID_VENDOR_FROM_DATABASE=Arduino SA + +usb:v2341p0001* + ID_MODEL_FROM_DATABASE=Uno (CDC ACM) + +usb:v2341p0010* + ID_MODEL_FROM_DATABASE=Mega 2560 (CDC ACM) + +usb:v2341p003B* + ID_MODEL_FROM_DATABASE=Serial Adapter (CDC ACM) + +usb:v2341p003F* + ID_MODEL_FROM_DATABASE=Mega ADK (CDC ACM) + +usb:v2341p0042* + ID_MODEL_FROM_DATABASE=Mega 2560 R3 (CDC ACM) + +usb:v2341p0043* + ID_MODEL_FROM_DATABASE=Uno R3 (CDC ACM) + +usb:v2341p0044* + ID_MODEL_FROM_DATABASE=Mega ADK R3 (CDC ACM) + +usb:v2341p0045* + ID_MODEL_FROM_DATABASE=Serial R3 (CDC ACM) + +usb:v2341p8036* + ID_MODEL_FROM_DATABASE=Leonardo (CDC ACM, HID) + +usb:v2373* + ID_VENDOR_FROM_DATABASE=Pumatronix Ltda + +usb:v2373p0001* + ID_MODEL_FROM_DATABASE=5 MegaPixel Digital Still Camera [DSC5M] + +usb:v2375* + ID_VENDOR_FROM_DATABASE=Digit@lway, Inc. + +usb:v2375p0001* + ID_MODEL_FROM_DATABASE=Digital Audio Player + +usb:v2406* + ID_VENDOR_FROM_DATABASE=SANHO Digital Electronics Co., Ltd. + +usb:v2406p6688* + ID_MODEL_FROM_DATABASE=PD7X Portable Storage + +usb:v2443* + ID_VENDOR_FROM_DATABASE=Aessent Technology Ltd + +usb:v2443p00DC* + ID_MODEL_FROM_DATABASE=aes220 FPGA Mini-Module + +usb:v2478* + ID_VENDOR_FROM_DATABASE=Tripp-Lite + +usb:v2478p2008* + ID_MODEL_FROM_DATABASE=U209-000-R Serial Port + +usb:v248A* + ID_VENDOR_FROM_DATABASE=Maxxter + +usb:v248Ap8366* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse ACT-MUSW-002 + +usb:v249C* + ID_VENDOR_FROM_DATABASE=M2Tech s.r.l. + +usb:v24E1* + ID_VENDOR_FROM_DATABASE=Paratronic + +usb:v24E1p3001* + ID_MODEL_FROM_DATABASE=Adp-usb + +usb:v24E1p3005* + ID_MODEL_FROM_DATABASE=Radius + +usb:v2632* + ID_VENDOR_FROM_DATABASE=TwinMOS + +usb:v2632p3209* + ID_MODEL_FROM_DATABASE=7-in-1 Card Reader + +usb:v2639* + ID_VENDOR_FROM_DATABASE=Xsens + +usb:v2639p0001* + ID_MODEL_FROM_DATABASE=MTi-10 IMU + +usb:v2639p0002* + ID_MODEL_FROM_DATABASE=MTi-20 VRU + +usb:v2639p0003* + ID_MODEL_FROM_DATABASE=MTi-30 AHRS + +usb:v2639p0011* + ID_MODEL_FROM_DATABASE=MTi-100 IMU + +usb:v2639p0012* + ID_MODEL_FROM_DATABASE=MTi-200 VRU + +usb:v2639p0013* + ID_MODEL_FROM_DATABASE=MTi-300 AHRS + +usb:v2639p0017* + ID_MODEL_FROM_DATABASE=MTi-G 7xx GNSS/INS + +usb:v2639p0100* + ID_MODEL_FROM_DATABASE=Body Pack + +usb:v2639p0101* + ID_MODEL_FROM_DATABASE=Awinda Station + +usb:v2639p0102* + ID_MODEL_FROM_DATABASE=Awinda Dongle + +usb:v2639p0103* + ID_MODEL_FROM_DATABASE=Sync Station + +usb:v2639p0200* + ID_MODEL_FROM_DATABASE=MTw + +usb:v2639pD00D* + ID_MODEL_FROM_DATABASE=Wireless Receiver + +usb:v2650* + ID_VENDOR_FROM_DATABASE=Electronics For Imaging, Inc. [hex] + +usb:v2659* + ID_VENDOR_FROM_DATABASE=Sundtek + +usb:v2659p1101* + ID_MODEL_FROM_DATABASE=TNT DVB-T/DAB/DAB+/FM + +usb:v2659p1201* + ID_MODEL_FROM_DATABASE=FM Transmitter/Receiver + +usb:v2659p1202* + ID_MODEL_FROM_DATABASE=MediaTV Analog/FM/DVB-T + +usb:v2659p1203* + ID_MODEL_FROM_DATABASE=MediaTV Analog/FM/DVB-T MiniPCIe + +usb:v2659p1204* + ID_MODEL_FROM_DATABASE=MediaTV Analog/FM/ATSC + +usb:v2659p1205* + ID_MODEL_FROM_DATABASE=SkyTV Ultimate V + +usb:v2659p1206* + ID_MODEL_FROM_DATABASE=MediaTV DVB-T MiniPCIe + +usb:v2659p1207* + ID_MODEL_FROM_DATABASE=Sundtek HD Capture + +usb:v2659p1208* + ID_MODEL_FROM_DATABASE=Sundtek SkyTV Ultimate III + +usb:v2659p1209* + ID_MODEL_FROM_DATABASE=MediaTV Analog/FM/ATSC MiniPCIe + +usb:v2659p1210* + ID_MODEL_FROM_DATABASE=MediaTV Pro III (EU) + +usb:v2659p1211* + ID_MODEL_FROM_DATABASE=MediaTV Pro III (US) + +usb:v2659p1212* + ID_MODEL_FROM_DATABASE=MediaTV Pro III MiniPCIe (EU) + +usb:v2659p1213* + ID_MODEL_FROM_DATABASE=MediaTV Pro III MiniPCIe (US) + +usb:v2676* + ID_VENDOR_FROM_DATABASE=Basler AG + +usb:v2676pBA02* + ID_MODEL_FROM_DATABASE=ace + +usb:v2730* + ID_VENDOR_FROM_DATABASE=Citizen + +usb:v2730p200F* + ID_MODEL_FROM_DATABASE=CT-S310 Label printer + +usb:v2735* + ID_VENDOR_FROM_DATABASE=DigitalWay + +usb:v2735p0003* + ID_MODEL_FROM_DATABASE=MPIO HS100 + +usb:v2735p1001* + ID_MODEL_FROM_DATABASE=MPIO FY200 + +usb:v2735p1002* + ID_MODEL_FROM_DATABASE=MPIO FL100 + +usb:v2735p1003* + ID_MODEL_FROM_DATABASE=MPIO FD100 + +usb:v2735p1004* + ID_MODEL_FROM_DATABASE=MPIO HD200 + +usb:v2735p1005* + ID_MODEL_FROM_DATABASE=MPIO HD300 + +usb:v2735p1006* + ID_MODEL_FROM_DATABASE=MPIO FG100 + +usb:v2735p1007* + ID_MODEL_FROM_DATABASE=MPIO FG130 + +usb:v2735p1008* + ID_MODEL_FROM_DATABASE=MPIO FY300 + +usb:v2735p1009* + ID_MODEL_FROM_DATABASE=MPIO FY400 + +usb:v2735p100A* + ID_MODEL_FROM_DATABASE=MPIO FL300 + +usb:v2735p100B* + ID_MODEL_FROM_DATABASE=MPIO HS200 + +usb:v2735p100C* + ID_MODEL_FROM_DATABASE=MPIO FL350 + +usb:v2735p100D* + ID_MODEL_FROM_DATABASE=MPIO FY500 + +usb:v2735p100E* + ID_MODEL_FROM_DATABASE=MPIO FY500 + +usb:v2735p100F* + ID_MODEL_FROM_DATABASE=MPIO FY600 + +usb:v2735p1012* + ID_MODEL_FROM_DATABASE=MPIO FL400 + +usb:v2735p1013* + ID_MODEL_FROM_DATABASE=MPIO HD400 + +usb:v2735p1014* + ID_MODEL_FROM_DATABASE=MPIO HD400 + +usb:v2735p1016* + ID_MODEL_FROM_DATABASE=MPIO FY700 + +usb:v2735p1017* + ID_MODEL_FROM_DATABASE=MPIO FY700 + +usb:v2735p1018* + ID_MODEL_FROM_DATABASE=MPIO FY800 + +usb:v2735p1019* + ID_MODEL_FROM_DATABASE=MPIO FY800 + +usb:v2735p101A* + ID_MODEL_FROM_DATABASE=MPIO FY900 + +usb:v2735p101B* + ID_MODEL_FROM_DATABASE=MPIO FY900 + +usb:v2735p102B* + ID_MODEL_FROM_DATABASE=MPIO FL500 + +usb:v2735p102C* + ID_MODEL_FROM_DATABASE=MPIO FL500 + +usb:v2735p103F* + ID_MODEL_FROM_DATABASE=MPIO FY570 + +usb:v2735p1040* + ID_MODEL_FROM_DATABASE=MPIO FY570 + +usb:v2735p1041* + ID_MODEL_FROM_DATABASE=MPIO FY670 + +usb:v2735p1042* + ID_MODEL_FROM_DATABASE=MPIO FY670 + +usb:v2735p1043* + ID_MODEL_FROM_DATABASE=HCT HMD-180A + +usb:v2735p1044* + ID_MODEL_FROM_DATABASE=HCT HMD-180A + +usb:v2770* + ID_VENDOR_FROM_DATABASE=NHJ, Ltd + +usb:v2770p0A01* + ID_MODEL_FROM_DATABASE=ScanJet 4600 series + +usb:v2770p905C* + ID_MODEL_FROM_DATABASE=Che-Ez Snap SNAP-U/Digigr8/Soundstar TDC-35 + +usb:v2770p9060* + ID_MODEL_FROM_DATABASE=A130 + +usb:v2770p9120* + ID_MODEL_FROM_DATABASE=Che-ez! Snap / iClick Tiny VGA Digital Camera + +usb:v2770p9130* + ID_MODEL_FROM_DATABASE=TCG 501 + +usb:v2770p913C* + ID_MODEL_FROM_DATABASE=Argus DC-1730 + +usb:v2770p9150* + ID_MODEL_FROM_DATABASE=Mini Cam + +usb:v2770p9153* + ID_MODEL_FROM_DATABASE=iClick 5X + +usb:v2770p915D* + ID_MODEL_FROM_DATABASE=Cyberpix S-210S / Little Tikes My Real Digital Camera + +usb:v2770p930B* + ID_MODEL_FROM_DATABASE=CCD Webcam(PC370R) + +usb:v2770p930C* + ID_MODEL_FROM_DATABASE=CCD Webcam(PC370R) + +usb:v27B8* + ID_VENDOR_FROM_DATABASE=ThingM + +usb:v27B8p01ED* + ID_MODEL_FROM_DATABASE=blink(1) + +usb:v2821* + ID_VENDOR_FROM_DATABASE=ASUSTek Computer Inc. + +usb:v2821p0161* + ID_MODEL_FROM_DATABASE=WL-161 802.11b Wireless Adapter [SiS 162U] + +usb:v2821p160F* + ID_MODEL_FROM_DATABASE=WL-160g 802.11g Wireless Adapter [Envara WiND512] + +usb:v2821p3300* + ID_MODEL_FROM_DATABASE=WL-140 / Hawking HWU36D 802.11b Wireless Adapter [Intersil PRISM 3] + +usb:v2899* + ID_VENDOR_FROM_DATABASE=Toptronic Industrial Co., Ltd + +usb:v2899p012C* + ID_MODEL_FROM_DATABASE=Camera Device + +usb:v289B* + ID_VENDOR_FROM_DATABASE=Dracal/Raphnet technologies + +usb:v289Bp0001* + ID_MODEL_FROM_DATABASE=Gamecube/N64 controller v2.2 + +usb:v289Bp0002* + ID_MODEL_FROM_DATABASE=2nes2snes + +usb:v289Bp0003* + ID_MODEL_FROM_DATABASE=4nes4snes + +usb:v289Bp0004* + ID_MODEL_FROM_DATABASE=Gamecube/N64 controller v2.3 + +usb:v289Bp0005* + ID_MODEL_FROM_DATABASE=Saturn (Joystick mode) + +usb:v289Bp0006* + ID_MODEL_FROM_DATABASE=Saturn (Mouse mode) + +usb:v289Bp0007* + ID_MODEL_FROM_DATABASE=Famicom controller + +usb:v289Bp0008* + ID_MODEL_FROM_DATABASE=Dreamcast (Joystick mode) + +usb:v289Bp0009* + ID_MODEL_FROM_DATABASE=Dreamcast (Mouse mode) + +usb:v289Bp000A* + ID_MODEL_FROM_DATABASE=Dreamcast (Keyboard mode) + +usb:v289Bp000B* + ID_MODEL_FROM_DATABASE=Gamecube/N64 controller v2.9 (Keyboard mode) + +usb:v289Bp000C* + ID_MODEL_FROM_DATABASE=Gamecube/N64 controller v2.9 (Joystick mode) + +usb:v289Bp0100* + ID_MODEL_FROM_DATABASE=Dual-relay board + +usb:v289Bp0500* + ID_MODEL_FROM_DATABASE=Energy meter + +usb:v289Bp0502* + ID_MODEL_FROM_DATABASE=Precision barometer + +usb:v2931* + ID_VENDOR_FROM_DATABASE=Jolla Oy + +usb:v2931p0A01* + ID_MODEL_FROM_DATABASE=Jolla Phone MTP + +usb:v2931p0A02* + ID_MODEL_FROM_DATABASE=Jolla Phone Developer + +usb:v2931p0A05* + ID_MODEL_FROM_DATABASE=Jolla PC connection + +usb:v2931p0AFE* + ID_MODEL_FROM_DATABASE=Jolla charging only + +usb:v2A03* + ID_VENDOR_FROM_DATABASE=dog hunter AG + +usb:v2A03p0001* + ID_MODEL_FROM_DATABASE=Linino ONE (bootloader) + +usb:v2A03p0036* + ID_MODEL_FROM_DATABASE=Arduino Leonardo (bootloader) + +usb:v2A03p0037* + ID_MODEL_FROM_DATABASE=Arduino Micro (bootloader) + +usb:v2A03p0038* + ID_MODEL_FROM_DATABASE=Arduino Robot Control (bootloader) + +usb:v2A03p0039* + ID_MODEL_FROM_DATABASE=Arduino Robot Motor (bootloader) + +usb:v2A03p003A* + ID_MODEL_FROM_DATABASE=Arduino Micro ADK rev3 (bootloader) + +usb:v2A03p003B* + ID_MODEL_FROM_DATABASE=Arduino Serial + +usb:v2A03p003C* + ID_MODEL_FROM_DATABASE=Arduino Explora (bootloader) + +usb:v2A03p003D* + ID_MODEL_FROM_DATABASE=Arduino Due (usb2serial) + +usb:v2A03p003E* + ID_MODEL_FROM_DATABASE=Arduino Due + +usb:v2A03p0041* + ID_MODEL_FROM_DATABASE=Arduino Yun (bootloader) + +usb:v2A03p0042* + ID_MODEL_FROM_DATABASE=Arduino Mega 2560 Rev3 + +usb:v2A03p0043* + ID_MODEL_FROM_DATABASE=Arduino Uno Rev3 + +usb:v2A03p004D* + ID_MODEL_FROM_DATABASE=Arduino Zero Pro (bootloader) + +usb:v2A03p8001* + ID_MODEL_FROM_DATABASE=Linino ONE (CDC ACM) + +usb:v2A03p8036* + ID_MODEL_FROM_DATABASE=Arduino Leonardo (CDC ACM) + +usb:v2A03p8037* + ID_MODEL_FROM_DATABASE=Arduino Micro (CDC ACM) + +usb:v2A03p8038* + ID_MODEL_FROM_DATABASE=Arduino Robot Control (CDC ACM) + +usb:v2A03p8039* + ID_MODEL_FROM_DATABASE=Arduino Robot Motor (CDC ACM) + +usb:v2A03p803A* + ID_MODEL_FROM_DATABASE=Arduino Micro ADK rev3 (CDC ACM) + +usb:v2A03p803C* + ID_MODEL_FROM_DATABASE=Arduino Explora (CDC ACM) + +usb:v2A03p8041* + ID_MODEL_FROM_DATABASE=Arduino Yun (CDC ACM) + +usb:v2A03p804D* + ID_MODEL_FROM_DATABASE=Arduino Zero Pro (CDC ACM) + +usb:v2A37* + ID_VENDOR_FROM_DATABASE=RTD Embedded Technologies, Inc. + +usb:v2A37p5110* + ID_MODEL_FROM_DATABASE=UPS35110/UPS25110 + +usb:v2A45* + ID_VENDOR_FROM_DATABASE=Meizu Corp. + +usb:v2A45p0001* + ID_MODEL_FROM_DATABASE=MX Phone (BICR) + +usb:v2A45p0C02* + ID_MODEL_FROM_DATABASE=MX Phone (MTP & ADB) + +usb:v2A45p0C03* + ID_MODEL_FROM_DATABASE=MX Phone (BICR & ADB) + +usb:v2A45p2008* + ID_MODEL_FROM_DATABASE=MX Phone (MTP) + +usb:v2A45p200A* + ID_MODEL_FROM_DATABASE=MX Phone (MTP & ACM & ADB) + +usb:v2A45p200B* + ID_MODEL_FROM_DATABASE=MX Phone (PTP) + +usb:v2A45p200C* + ID_MODEL_FROM_DATABASE=MX Phone (PTP & ADB) + +usb:v2A45p2012* + ID_MODEL_FROM_DATABASE=MX Phone (MTP & ACM) + +usb:v2C02* + ID_VENDOR_FROM_DATABASE=Planex Communications + +usb:v2C02p14EA* + ID_MODEL_FROM_DATABASE=GW-US11H WLAN + +usb:v2C1A* + ID_VENDOR_FROM_DATABASE=Dolphin Peripherals + +usb:v2C1Ap0000* + ID_MODEL_FROM_DATABASE=Wireless Optical Mouse + +usb:v2FB2* + ID_VENDOR_FROM_DATABASE=Fujitsu, Ltd + +usb:v3125* + ID_VENDOR_FROM_DATABASE=Eagletron + +usb:v3125p0001* + ID_MODEL_FROM_DATABASE=TrackerPod Camera Stand + +usb:v3136* + ID_VENDOR_FROM_DATABASE=Navini Networks + +usb:v3176* + ID_VENDOR_FROM_DATABASE=Whanam Electronics Co., Ltd + +usb:v3195* + ID_VENDOR_FROM_DATABASE=Link Instruments + +usb:v3195pF190* + ID_MODEL_FROM_DATABASE=MSO-19 + +usb:v3195pF280* + ID_MODEL_FROM_DATABASE=MSO-28 + +usb:v3195pF281* + ID_MODEL_FROM_DATABASE=MSO-28 + +usb:v3275* + ID_VENDOR_FROM_DATABASE=VidzMedia Pte Ltd + +usb:v3275p4FB1* + ID_MODEL_FROM_DATABASE=MonsterTV P2H + +usb:v3333* + ID_VENDOR_FROM_DATABASE=InLine + +usb:v3333p3333* + ID_MODEL_FROM_DATABASE=2 port KVM switch model 60652K + +usb:v3334* + ID_VENDOR_FROM_DATABASE=AEI + +usb:v3334p1701* + ID_MODEL_FROM_DATABASE=Fast Ethernet + +usb:v3340* + ID_VENDOR_FROM_DATABASE=Yakumo + +usb:v3340p043A* + ID_MODEL_FROM_DATABASE=Mio A701 DigiWalker PPCPhone + +usb:v3340p0E3A* + ID_MODEL_FROM_DATABASE=Pocket PC 300 GPS SL / Typhoon MyGuide 3500 + +usb:v3340pA0A3* + ID_MODEL_FROM_DATABASE=deltaX 5 BT (D) PDA + +usb:v3344* + ID_VENDOR_FROM_DATABASE=Leaguer Microelectronics (LME) + +usb:v3344p3744* + ID_MODEL_FROM_DATABASE=OEM PC Remote + +usb:v3504* + ID_VENDOR_FROM_DATABASE=Micro Star + +usb:v3504pF110* + ID_MODEL_FROM_DATABASE=Security Key + +usb:v3538* + ID_VENDOR_FROM_DATABASE=Power Quotient International Co., Ltd + +usb:v3538p0001* + ID_MODEL_FROM_DATABASE=Travel Flash + +usb:v3538p0015* + ID_MODEL_FROM_DATABASE=Mass Storge Device + +usb:v3538p0022* + ID_MODEL_FROM_DATABASE=Hi-Speed Mass Storage Device + +usb:v3538p0042* + ID_MODEL_FROM_DATABASE=Cool Drive U339 Flash Disk + +usb:v3538p0054* + ID_MODEL_FROM_DATABASE=Flash Drive (2GB) + +usb:v3579* + ID_VENDOR_FROM_DATABASE=DIVA + +usb:v3579p6901* + ID_MODEL_FROM_DATABASE=Media Reader + +usb:v357D* + ID_VENDOR_FROM_DATABASE=Sharkoon + +usb:v357Dp7788* + ID_MODEL_FROM_DATABASE=QuickPort XT + +usb:v3636* + ID_VENDOR_FROM_DATABASE=InVibro + +usb:v3838* + ID_VENDOR_FROM_DATABASE=WEM + +usb:v3838p0001* + ID_MODEL_FROM_DATABASE=5-in-1 Card Reader + +usb:v3923* + ID_VENDOR_FROM_DATABASE=National Instruments Corp. + +usb:v3923p12C0* + ID_MODEL_FROM_DATABASE=DAQPad-6020E + +usb:v3923p12D0* + ID_MODEL_FROM_DATABASE=DAQPad-6507 + +usb:v3923p12E0* + ID_MODEL_FROM_DATABASE=NI 4350 + +usb:v3923p12F0* + ID_MODEL_FROM_DATABASE=NI 5102 + +usb:v3923p1750* + ID_MODEL_FROM_DATABASE=DAQPad-6508 + +usb:v3923p17B0* + ID_MODEL_FROM_DATABASE=USB-ISA-Bridge + +usb:v3923p1820* + ID_MODEL_FROM_DATABASE=DAQPad-6020E (68 pin I/O) + +usb:v3923p1830* + ID_MODEL_FROM_DATABASE=DAQPad-6020E (BNC) + +usb:v3923p1F00* + ID_MODEL_FROM_DATABASE=DAQPad-6024E + +usb:v3923p1F10* + ID_MODEL_FROM_DATABASE=DAQPad-6024E + +usb:v3923p1F20* + ID_MODEL_FROM_DATABASE=DAQPad-6025E + +usb:v3923p1F30* + ID_MODEL_FROM_DATABASE=DAQPad-6025E + +usb:v3923p1F40* + ID_MODEL_FROM_DATABASE=DAQPad-6036E + +usb:v3923p1F50* + ID_MODEL_FROM_DATABASE=DAQPad-6036E + +usb:v3923p2F80* + ID_MODEL_FROM_DATABASE=DAQPad-6052E + +usb:v3923p2F90* + ID_MODEL_FROM_DATABASE=DAQPad-6052E + +usb:v3923p702B* + ID_MODEL_FROM_DATABASE=GPIB-USB-B + +usb:v3923p703C* + ID_MODEL_FROM_DATABASE=USB-485 RS485 Cable + +usb:v3923p709B* + ID_MODEL_FROM_DATABASE=GPIB-USB-HS + +usb:v3923p7254* + ID_MODEL_FROM_DATABASE=NI MIO (data acquisition card) firmware updater + +usb:v3923p729E* + ID_MODEL_FROM_DATABASE=USB-6251 (OEM) data acquisition card + +usb:v40BB* + ID_VENDOR_FROM_DATABASE=I-O Data + +usb:v40BBp0A09* + ID_MODEL_FROM_DATABASE=USB2.0-SCSI Bridge USB2-SC + +usb:v4101* + ID_VENDOR_FROM_DATABASE=i-rocks + +usb:v4101p1301* + ID_MODEL_FROM_DATABASE=IR-2510 usb phone + +usb:v4102* + ID_VENDOR_FROM_DATABASE=iRiver, Ltd. + +usb:v4102p1001* + ID_MODEL_FROM_DATABASE=iFP-100 series mp3 player + +usb:v4102p1003* + ID_MODEL_FROM_DATABASE=iFP-300 series mp3 player + +usb:v4102p1005* + ID_MODEL_FROM_DATABASE=iFP-500 series mp3 player + +usb:v4102p1007* + ID_MODEL_FROM_DATABASE=iFP-700 series mp3/ogg vorbis player + +usb:v4102p1008* + ID_MODEL_FROM_DATABASE=iFP-800 series mp3/ogg vorbis player + +usb:v4102p100A* + ID_MODEL_FROM_DATABASE=iFP-1000 series mp3/ogg vorbis player + +usb:v4102p1014* + ID_MODEL_FROM_DATABASE=T20 series mp3/ogg vorbis player (ums firmware) + +usb:v4102p1019* + ID_MODEL_FROM_DATABASE=T30 + +usb:v4102p1034* + ID_MODEL_FROM_DATABASE=T60 + +usb:v4102p1040* + ID_MODEL_FROM_DATABASE=M1Player + +usb:v4102p1041* + ID_MODEL_FROM_DATABASE=E100 (ums) + +usb:v4102p1101* + ID_MODEL_FROM_DATABASE=iFP-100 series mp3 player (ums firmware) + +usb:v4102p1103* + ID_MODEL_FROM_DATABASE=iFP-300 series mp3 player (ums firmware) + +usb:v4102p1105* + ID_MODEL_FROM_DATABASE=iFP-500 series mp3 player (ums firmware) + +usb:v4102p1113* + ID_MODEL_FROM_DATABASE=T10 (alternate) + +usb:v4102p1117* + ID_MODEL_FROM_DATABASE=T10 + +usb:v4102p1119* + ID_MODEL_FROM_DATABASE=T30 series mp3/ogg/wma player + +usb:v4102p1141* + ID_MODEL_FROM_DATABASE=E100 (mtp) + +usb:v4102p2002* + ID_MODEL_FROM_DATABASE=H10 6GB + +usb:v4102p2101* + ID_MODEL_FROM_DATABASE=H10 20GB (mtp) + +usb:v4102p2102* + ID_MODEL_FROM_DATABASE=H10 5GB (mtp) + +usb:v4102p2105* + ID_MODEL_FROM_DATABASE=H10 5/6GB (mtp) + +usb:v413C* + ID_VENDOR_FROM_DATABASE=Dell Computer Corp. + +usb:v413Cp0000* + ID_MODEL_FROM_DATABASE=DRAC 5 Virtual Keyboard and Mouse + +usb:v413Cp0001* + ID_MODEL_FROM_DATABASE=DRAC 5 Virtual Media + +usb:v413Cp0058* + ID_MODEL_FROM_DATABASE=Port Replicator + +usb:v413Cp1001* + ID_MODEL_FROM_DATABASE=Keyboard Hub + +usb:v413Cp1002* + ID_MODEL_FROM_DATABASE=Keyboard Hub + +usb:v413Cp1003* + ID_MODEL_FROM_DATABASE=Keyboard Hub + +usb:v413Cp1005* + ID_MODEL_FROM_DATABASE=Multimedia Pro Keyboard Hub + +usb:v413Cp2001* + ID_MODEL_FROM_DATABASE=Keyboard HID Support + +usb:v413Cp2002* + ID_MODEL_FROM_DATABASE=SK-8125 Keyboard + +usb:v413Cp2003* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v413Cp2005* + ID_MODEL_FROM_DATABASE=RT7D50 Keyboard + +usb:v413Cp2010* + ID_MODEL_FROM_DATABASE=Keyboard + +usb:v413Cp2011* + ID_MODEL_FROM_DATABASE=Multimedia Pro Keyboard + +usb:v413Cp2100* + ID_MODEL_FROM_DATABASE=SK-3106 Keyboard + +usb:v413Cp2101* + ID_MODEL_FROM_DATABASE=SmartCard Reader Keyboard + +usb:v413Cp2105* + ID_MODEL_FROM_DATABASE=Model L100 Keyboard + +usb:v413Cp2106* + ID_MODEL_FROM_DATABASE=Dell QuietKey Keyboard + +usb:v413Cp2500* + ID_MODEL_FROM_DATABASE=DRAC4 Remote Access Card + +usb:v413Cp2513* + ID_MODEL_FROM_DATABASE=internal USB Hub of E-Port Replicator + +usb:v413Cp3010* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse + +usb:v413Cp3012* + ID_MODEL_FROM_DATABASE=Optical Wheel Mouse + +usb:v413Cp3016* + ID_MODEL_FROM_DATABASE=Optical 5-Button Wheel Mouse + +usb:v413Cp3200* + ID_MODEL_FROM_DATABASE=Mouse + +usb:v413Cp4001* + ID_MODEL_FROM_DATABASE=Axim X5 + +usb:v413Cp4002* + ID_MODEL_FROM_DATABASE=Axim X3 + +usb:v413Cp4003* + ID_MODEL_FROM_DATABASE=Axim X30 + +usb:v413Cp4004* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4005* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4006* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4007* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4008* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4009* + ID_MODEL_FROM_DATABASE=Axim Sync + +usb:v413Cp4011* + ID_MODEL_FROM_DATABASE=Axim X51v + +usb:v413Cp5103* + ID_MODEL_FROM_DATABASE=AIO Printer A940 + +usb:v413Cp5105* + ID_MODEL_FROM_DATABASE=AIO Printer A920 + +usb:v413Cp5107* + ID_MODEL_FROM_DATABASE=AIO Printer A960 + +usb:v413Cp5109* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 922 + +usb:v413Cp5110* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 962 + +usb:v413Cp5111* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 942 + +usb:v413Cp5112* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 924 + +usb:v413Cp5113* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 944 + +usb:v413Cp5114* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 964 + +usb:v413Cp5115* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 926 + +usb:v413Cp5116* + ID_MODEL_FROM_DATABASE=AIO Printer 946 + +usb:v413Cp5117* + ID_MODEL_FROM_DATABASE=Photo AIO Printer 966 + +usb:v413Cp5118* + ID_MODEL_FROM_DATABASE=AIO 810 + +usb:v413Cp5124* + ID_MODEL_FROM_DATABASE=Laser MFP 1815 + +usb:v413Cp5128* + ID_MODEL_FROM_DATABASE=Photo AIO 928 + +usb:v413Cp5200* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v413Cp5202* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp5203* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp5210* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp5211* + ID_MODEL_FROM_DATABASE=1110 Laser Printer + +usb:v413Cp5220* + ID_MODEL_FROM_DATABASE=Laser MFP 1600n + +usb:v413Cp5225* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp5226* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp5300* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v413Cp5400* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v413Cp5401* + ID_MODEL_FROM_DATABASE=Laser Printer + +usb:v413Cp5513* + ID_MODEL_FROM_DATABASE=WLA3310 Wireless Adapter [Intersil ISL3887] + +usb:v413Cp5601* + ID_MODEL_FROM_DATABASE=Laser Printer 3100cn + +usb:v413Cp5602* + ID_MODEL_FROM_DATABASE=Laser Printer 3000cn + +usb:v413Cp5631* + ID_MODEL_FROM_DATABASE=Laser Printer 5100cn + +usb:v413Cp5905* + ID_MODEL_FROM_DATABASE=Printing Support + +usb:v413Cp8000* + ID_MODEL_FROM_DATABASE=BC02 Bluetooth Adapter + +usb:v413Cp8010* + ID_MODEL_FROM_DATABASE=TrueMobile Bluetooth Module in + +usb:v413Cp8100* + ID_MODEL_FROM_DATABASE=TrueMobile 1180 802.11b Adapter [Intersil PRISM 3] + +usb:v413Cp8102* + ID_MODEL_FROM_DATABASE=TrueMobile 1300 802.11g Wireless Adapter [Intersil ISL3880] + +usb:v413Cp8103* + ID_MODEL_FROM_DATABASE=Wireless 350 Bluetooth + +usb:v413Cp8104* + ID_MODEL_FROM_DATABASE=Wireless 1450 Dual-band (802.11a/b/g) Adapter [Intersil ISL3887] + +usb:v413Cp8105* + ID_MODEL_FROM_DATABASE=U2 in HID - Driver + +usb:v413Cp8106* + ID_MODEL_FROM_DATABASE=Wireless 350 Bluetooth Internal Card in + +usb:v413Cp8110* + ID_MODEL_FROM_DATABASE=Wireless 3xx Bluetooth Internal Card + +usb:v413Cp8111* + ID_MODEL_FROM_DATABASE=Wireless 3xx Bluetooth Internal Card in + +usb:v413Cp8114* + ID_MODEL_FROM_DATABASE=Wireless 5700 Mobile Broadband (CDMA EV-DO) Minicard Modem + +usb:v413Cp8115* + ID_MODEL_FROM_DATABASE=Wireless 5500 Mobile Broadband (3G HSDPA) Minicard Modem + +usb:v413Cp8116* + ID_MODEL_FROM_DATABASE=Wireless 5505 Mobile Broadband (3G HSDPA) Minicard Modem + +usb:v413Cp8117* + ID_MODEL_FROM_DATABASE=Wireless 5700 Mobile Broadband (CDMA EV-DO) Expresscard Modem + +usb:v413Cp8118* + ID_MODEL_FROM_DATABASE=Wireless 5510 Mobile Broadband (3G HSDPA) Expresscard Status Port + +usb:v413Cp8120* + ID_MODEL_FROM_DATABASE=Bluetooth adapter + +usb:v413Cp8121* + ID_MODEL_FROM_DATABASE=Eastfold in HID + +usb:v413Cp8122* + ID_MODEL_FROM_DATABASE=Eastfold in DFU + +usb:v413Cp8123* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v413Cp8124* + ID_MODEL_FROM_DATABASE=eHome Infrared Receiver + +usb:v413Cp8126* + ID_MODEL_FROM_DATABASE=Wireless 355 Bluetooth + +usb:v413Cp8127* + ID_MODEL_FROM_DATABASE=Wireless 355 Module with Bluetooth 2.0 + EDR Technology. + +usb:v413Cp8128* + ID_MODEL_FROM_DATABASE=Wireless 5700-Sprint Mobile Broadband (CDMA EV-DO) Mini-Card Status Port + +usb:v413Cp8129* + ID_MODEL_FROM_DATABASE=Wireless 5700-Telus Mobile Broadband (CDMA EV-DO) Mini-Card Status Port + +usb:v413Cp8131* + ID_MODEL_FROM_DATABASE=Wireless 360 Bluetooth 2.0 + EDR module. + +usb:v413Cp8133* + ID_MODEL_FROM_DATABASE=Wireless 5720 VZW Mobile Broadband (EVDO Rev-A) Minicard GPS Port + +usb:v413Cp8134* + ID_MODEL_FROM_DATABASE=Wireless 5720 Sprint Mobile Broadband (EVDO Rev-A) Minicard Status Port + +usb:v413Cp8135* + ID_MODEL_FROM_DATABASE=Wireless 5720 TELUS Mobile Broadband (EVDO Rev-A) Minicard Diagnostics Port + +usb:v413Cp8136* + ID_MODEL_FROM_DATABASE=Wireless 5520 Cingular Mobile Broadband (3G HSDPA) Minicard Diagnostics Port + +usb:v413Cp8137* + ID_MODEL_FROM_DATABASE=Wireless 5520 Voda L Mobile Broadband (3G HSDPA) Minicard Status Port + +usb:v413Cp8138* + ID_MODEL_FROM_DATABASE=Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard EAP-SIM Port + +usb:v413Cp8140* + ID_MODEL_FROM_DATABASE=Wireless 360 Bluetooth + +usb:v413Cp8142* + ID_MODEL_FROM_DATABASE=Mobile 360 in DFU + +usb:v413Cp8147* + ID_MODEL_FROM_DATABASE=F3507g Mobile Broadband Module + +usb:v413Cp8156* + ID_MODEL_FROM_DATABASE=Wireless 370 Bluetooth Mini-card + +usb:v413Cp8157* + ID_MODEL_FROM_DATABASE=Integrated Keyboard + +usb:v413Cp8158* + ID_MODEL_FROM_DATABASE=Integrated Touchpad / Trackstick + +usb:v413Cp8160* + ID_MODEL_FROM_DATABASE=Wireless 365 Bluetooth + +usb:v413Cp8161* + ID_MODEL_FROM_DATABASE=Integrated Keyboard + +usb:v413Cp8162* + ID_MODEL_FROM_DATABASE=Integrated Touchpad [Synaptics] + +usb:v413Cp8171* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem (QDL mode) + +usb:v413Cp8172* + ID_MODEL_FROM_DATABASE=Gobi Wireless Modem + +usb:v413Cp8183* + ID_MODEL_FROM_DATABASE=F3607gw Mobile Broadband Module + +usb:v413Cp8184* + ID_MODEL_FROM_DATABASE=F3607gw v2 Mobile Broadband Module + +usb:v413Cp8185* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem (QDL mode) + +usb:v413Cp8186* + ID_MODEL_FROM_DATABASE=Gobi 2000 Wireless Modem + +usb:v413Cp8187* + ID_MODEL_FROM_DATABASE=DW375 Bluetooth Module + +usb:v413Cp8501* + ID_MODEL_FROM_DATABASE=Bluetooth Adapter + +usb:v413Cp9500* + ID_MODEL_FROM_DATABASE=USB CP210x UART Bridge Controller [DW700] + +usb:v413CpA001* + ID_MODEL_FROM_DATABASE=Hub + +usb:v413CpA005* + ID_MODEL_FROM_DATABASE=Internal 2.0 Hub + +usb:v413CpA700* + ID_MODEL_FROM_DATABASE=Hub (in 1905FP LCD Monitor) + +usb:v4146* + ID_VENDOR_FROM_DATABASE=USBest Technology + +usb:v4146p9281* + ID_MODEL_FROM_DATABASE=Iomega Micro Mini 128MB Flash Drive + +usb:v4146pBA01* + ID_MODEL_FROM_DATABASE=Intuix Flash Drive + +usb:v4168* + ID_VENDOR_FROM_DATABASE=Targus + +usb:v4168p1010* + ID_MODEL_FROM_DATABASE=Wireless Compact Laser Mouse + +usb:v4242* + ID_VENDOR_FROM_DATABASE=USB Design by Example + +usb:v4242p4201* + ID_MODEL_FROM_DATABASE=Buttons and Lights HID device + +usb:v4242p4220* + ID_MODEL_FROM_DATABASE=Echo 1 Camera + +usb:v4255* + ID_VENDOR_FROM_DATABASE=GoPro + +usb:v4255p1000* + ID_MODEL_FROM_DATABASE=9FF2 [Digital Photo Display] + +usb:v4255p2000* + ID_MODEL_FROM_DATABASE=HD2-14 [Hero 2 Camera] + +usb:v4317* + ID_VENDOR_FROM_DATABASE=Broadcom Corp. + +usb:v4317p0700* + ID_MODEL_FROM_DATABASE=U.S. Robotics USR5426 802.11g Adapter + +usb:v4317p0701* + ID_MODEL_FROM_DATABASE=U.S. Robotics USR5425 Wireless MAXg Adapter + +usb:v4317p0711* + ID_MODEL_FROM_DATABASE=Belkin F5D7051 v3000 802.11g + +usb:v4317p0720* + ID_MODEL_FROM_DATABASE=Dynex DX-BUSB + +usb:v4348* + ID_VENDOR_FROM_DATABASE=WinChipHead + +usb:v4348p5523* + ID_MODEL_FROM_DATABASE=USB->RS 232 adapter with Prolifec PL 2303 chipset + +usb:v4348p5537* + ID_MODEL_FROM_DATABASE=13.56Mhz RFID Card Reader and Writer + +usb:v4348p5584* + ID_MODEL_FROM_DATABASE=CH34x printer adapter cable + +usb:v4572* + ID_VENDOR_FROM_DATABASE=Shuttle, Inc. + +usb:v4572p4572* + ID_MODEL_FROM_DATABASE=Shuttle PN31 Remote + +usb:v4586* + ID_VENDOR_FROM_DATABASE=Panram + +usb:v4586p1026* + ID_MODEL_FROM_DATABASE=Crystal Bar Flash Drive + +usb:v4670* + ID_VENDOR_FROM_DATABASE=EMS Production + +usb:v4670p9394* + ID_MODEL_FROM_DATABASE=Game Cube USB Memory Adaptor 64M + +usb:v4752* + ID_VENDOR_FROM_DATABASE=Miditech + +usb:v4752p0011* + ID_MODEL_FROM_DATABASE=Midistart-2 + +usb:v4757* + ID_VENDOR_FROM_DATABASE=GW Instek + +usb:v4757p2009* + ID_MODEL_FROM_DATABASE=PEL-2000 Series Electronic Load (CDC) + +usb:v4757p2010* + ID_MODEL_FROM_DATABASE=PEL-2000 Series Electronic Load (CDC) + +usb:v4766* + ID_VENDOR_FROM_DATABASE=Aceeca + +usb:v4766p0001* + ID_MODEL_FROM_DATABASE=MEZ1000 RDA + +usb:v4855* + ID_VENDOR_FROM_DATABASE=Memorex + +usb:v4855p7288* + ID_MODEL_FROM_DATABASE=Ultra Traveldrive 160G 2.5" HDD + +usb:v4971* + ID_VENDOR_FROM_DATABASE=SimpleTech + +usb:v4971pCB01* + ID_MODEL_FROM_DATABASE=SP-U25/120G + +usb:v4971pCE17* + ID_MODEL_FROM_DATABASE=1TB SimpleDrive II USB External Hard Drive + +usb:v4D46* + ID_VENDOR_FROM_DATABASE=Musical Fidelity + +usb:v4D46p0001* + ID_MODEL_FROM_DATABASE=V-Link + +usb:v4D46p0002* + ID_MODEL_FROM_DATABASE=V-DAC II + +usb:v5032* + ID_VENDOR_FROM_DATABASE=Grandtec + +usb:v5032p0BB8* + ID_MODEL_FROM_DATABASE=Grandtec USB1.1 DVB-T (cold) + +usb:v5032p0BB9* + ID_MODEL_FROM_DATABASE=Grandtec USB1.1 DVB-T (warm) + +usb:v5032p0FA0* + ID_MODEL_FROM_DATABASE=Grandtec USB1.1 DVB-T (cold) + +usb:v5032p0FA1* + ID_MODEL_FROM_DATABASE=Grandtec USB1.1 DVB-T (warm) + +usb:v5041* + ID_VENDOR_FROM_DATABASE=Linksys (?) + +usb:v5041p2234* + ID_MODEL_FROM_DATABASE=WUSB54G v1 802.11g Adapter [Intersil ISL3886] + +usb:v5041p2235* + ID_MODEL_FROM_DATABASE=WUSB54GP v1 802.11g Adapter [Intersil ISL3886] + +usb:v50C2* + ID_VENDOR_FROM_DATABASE=Averatec (?) + +usb:v50C2p4013* + ID_MODEL_FROM_DATABASE=WLAN Adapter + +usb:v5173* + ID_VENDOR_FROM_DATABASE=Sweex + +usb:v5173p1809* + ID_MODEL_FROM_DATABASE=ZD1211 + +usb:v5219* + ID_VENDOR_FROM_DATABASE=I-Tetra + +usb:v5219p1001* + ID_MODEL_FROM_DATABASE=Cetus CDC Device + +usb:v5345* + ID_VENDOR_FROM_DATABASE=Owon + +usb:v5345p1234* + ID_MODEL_FROM_DATABASE=PDS6062T Oscilloscope + +usb:v534C* + ID_VENDOR_FROM_DATABASE=SatoshiLabs + +usb:v534Cp0001* + ID_MODEL_FROM_DATABASE=Bitcoin Wallet [TREZOR] + +usb:v5354* + ID_VENDOR_FROM_DATABASE=Meyer Instruments (MIS) + +usb:v5354p0017* + ID_MODEL_FROM_DATABASE=PAXcam2 + +usb:v544D* + ID_VENDOR_FROM_DATABASE=Transmeta Corp. + +usb:v5543* + ID_VENDOR_FROM_DATABASE=UC-Logic Technology Corp. + +usb:v5543p0002* + ID_MODEL_FROM_DATABASE=SuperPen WP3325U Tablet + +usb:v5543p0003* + ID_MODEL_FROM_DATABASE=Tablet WP4030U + +usb:v5543p0004* + ID_MODEL_FROM_DATABASE=Tablet WP5540U + +usb:v5543p0005* + ID_MODEL_FROM_DATABASE=Tablet WP8060U + +usb:v5543p0041* + ID_MODEL_FROM_DATABASE=Genius PenSketch 6x8 Tablet + +usb:v5543p0042* + ID_MODEL_FROM_DATABASE=Tablet PF1209 + +usb:v5543p0064* + ID_MODEL_FROM_DATABASE=Aiptek HyperPen 10000U + +usb:v5555* + ID_VENDOR_FROM_DATABASE=Epiphan Systems Inc. + +usb:v5555p1110* + ID_MODEL_FROM_DATABASE=VGA2USB + +usb:v5555p1120* + ID_MODEL_FROM_DATABASE=KVM2USB + +usb:v5555p2222* + ID_MODEL_FROM_DATABASE=DVI2USB + +usb:v5555p3333* + ID_MODEL_FROM_DATABASE=VGA2USB Pro + +usb:v5555p3337* + ID_MODEL_FROM_DATABASE=KVM2USB Pro + +usb:v5555p3340* + ID_MODEL_FROM_DATABASE=VGA2USB LR + +usb:v5555p3344* + ID_MODEL_FROM_DATABASE=KVM2USB LR + +usb:v5555p3411* + ID_MODEL_FROM_DATABASE=DVI2USB Solo + +usb:v5555p3422* + ID_MODEL_FROM_DATABASE=DVI2USB Duo + +usb:v55AA* + ID_VENDOR_FROM_DATABASE=OnSpec Electronic, Inc. + +usb:v55AAp0015* + ID_MODEL_FROM_DATABASE=Hard Drive + +usb:v55AAp0102* + ID_MODEL_FROM_DATABASE=SuperDisk + +usb:v55AAp0103* + ID_MODEL_FROM_DATABASE=IDE Hard Drive + +usb:v55AAp0201* + ID_MODEL_FROM_DATABASE=DDI to Reader-19 + +usb:v55AAp1234* + ID_MODEL_FROM_DATABASE=ATAPI Bridge + +usb:v55AApA103* + ID_MODEL_FROM_DATABASE=Sandisk SDDR-55 SmartMedia Card Reader + +usb:v55AApB000* + ID_MODEL_FROM_DATABASE=USB to CompactFlash Card Reader + +usb:v55AApB004* + ID_MODEL_FROM_DATABASE=OnSpec MMC/SD Reader/Writer + +usb:v55AApB00B* + ID_MODEL_FROM_DATABASE=USB to Memory Stick Card Reader + +usb:v55AApB00C* + ID_MODEL_FROM_DATABASE=USB to SmartMedia Card Reader + +usb:v55AApB012* + ID_MODEL_FROM_DATABASE=Mitsumi FA402M 8-in-2 Card Reader + +usb:v55AApB200* + ID_MODEL_FROM_DATABASE=Compact Flash Reader + +usb:v55AApB204* + ID_MODEL_FROM_DATABASE=MMC/ SD Reader + +usb:v55AApB207* + ID_MODEL_FROM_DATABASE=Memory Stick Reader + +usb:v5654* + ID_VENDOR_FROM_DATABASE=Gotview + +usb:v5654pCA42* + ID_MODEL_FROM_DATABASE=MasterHD 3 + +usb:v5656* + ID_VENDOR_FROM_DATABASE=Uni-Trend Group Limited + +usb:v5656p0832* + ID_MODEL_FROM_DATABASE=UT2000/UT3000 Digital Storage Oscilloscope + +usb:v595A* + ID_VENDOR_FROM_DATABASE=IRTOUCHSYSTEMS Co. Ltd. + +usb:v595Ap0001* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v5986* + ID_VENDOR_FROM_DATABASE=Acer, Inc + +usb:v5986p0100* + ID_MODEL_FROM_DATABASE=Orbicam + +usb:v5986p0101* + ID_MODEL_FROM_DATABASE=USB2.0 Camera + +usb:v5986p0102* + ID_MODEL_FROM_DATABASE=Crystal Eye Webcam + +usb:v5986p01A6* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v5986p01A7* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v5986p01A9* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam + +usb:v5986p0200* + ID_MODEL_FROM_DATABASE=OrbiCam + +usb:v5986p0203* + ID_MODEL_FROM_DATABASE=BisonCam NB Pro 1300 + +usb:v5986p0241* + ID_MODEL_FROM_DATABASE=BisonCam, NB Pro + +usb:v5986p02D0* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam [R5U877] + +usb:v5986p03D0* + ID_MODEL_FROM_DATABASE=Lenovo Integrated Webcam [R5U877] + +usb:v59E3* + ID_VENDOR_FROM_DATABASE=Nonolith Labs + +usb:v5A57* + ID_VENDOR_FROM_DATABASE=Zinwell + +usb:v5A57p0260* + ID_MODEL_FROM_DATABASE=RT2570 + +usb:v5A57p0280* + ID_MODEL_FROM_DATABASE=802.11a/b/g/n USB Wireless LAN Card + +usb:v5A57p0282* + ID_MODEL_FROM_DATABASE=802.11b/g/n USB Wireless LAN Card + +usb:v5A57p0283* + ID_MODEL_FROM_DATABASE=802.11b/g/n USB Wireless LAN Card + +usb:v5A57p0284* + ID_MODEL_FROM_DATABASE=802.11a/b/g/n USB Wireless LAN Card + +usb:v5A57p0290* + ID_MODEL_FROM_DATABASE=ZW-N290 802.11n [Realtek RTL8192SU] + +usb:v5A57p5257* + ID_MODEL_FROM_DATABASE=Metronic 495257 wifi 802.11ng + +usb:v6000* + ID_VENDOR_FROM_DATABASE=Beholder International Ltd. + +usb:v6000pDEC0* + ID_MODEL_FROM_DATABASE=TV Wander + +usb:v6000pDEC1* + ID_MODEL_FROM_DATABASE=TV Voyage + +usb:v601A* + ID_VENDOR_FROM_DATABASE=Ingenic Semiconductor Ltd. + +usb:v601Ap4740* + ID_MODEL_FROM_DATABASE=XBurst Jz4740 boot mode + +usb:v6189* + ID_VENDOR_FROM_DATABASE=Sitecom + +usb:v6189p182D* + ID_MODEL_FROM_DATABASE=USB 2.0 Ethernet + +usb:v6189p2068* + ID_MODEL_FROM_DATABASE=USB to serial cable (v2) + +usb:v6244* + ID_VENDOR_FROM_DATABASE=LightingSoft AG + +usb:v6244p0101* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI5A + +usb:v6244p0201* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI5C + +usb:v6244p0300* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI6 Firmware download + +usb:v6244p0301* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI6C + +usb:v6244p0302* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI6A + +usb:v6244p0303* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI6D + +usb:v6244p0400* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK1A + +usb:v6244p0401* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK1A + +usb:v6244p0410* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI7 Firmware Download + +usb:v6244p0411* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI7A + +usb:v6244p0420* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI8A Firmware Download + +usb:v6244p0421* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI8A + +usb:v6244p0430* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI8C Firmware Download + +usb:v6244p0431* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI8C + +usb:v6244p0440* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9A Firmware Download + +usb:v6244p0441* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9A + +usb:v6244p0450* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9C Firmware Download + +usb:v6244p0451* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9C + +usb:v6244p0460* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK2 Firmware download + +usb:v6244p0461* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK2 + +usb:v6244p0470* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK1B Firmware download + +usb:v6244p0471* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK1B + +usb:v6244p0480* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK3 Firmware download + +usb:v6244p0481* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK3 + +usb:v6244p0490* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9D Firmware Download + +usb:v6244p0491* + ID_MODEL_FROM_DATABASE=Intelligent Usb Dmx Interface SIUDI9D + +usb:v6244p0500* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK2B Firmware download + +usb:v6244p0501* + ID_MODEL_FROM_DATABASE=Touch Sensitive Intelligent Control Keypad STICK2B + +usb:v6253* + ID_VENDOR_FROM_DATABASE=TwinHan Technology Co., Ltd + +usb:v6253p0100* + ID_MODEL_FROM_DATABASE=Ir reciver f. remote control + +usb:v636C* + ID_VENDOR_FROM_DATABASE=CoreLogic, Inc. + +usb:v6472* + ID_VENDOR_FROM_DATABASE=Unknown (Sony?) + +usb:v6472p01C8* + ID_MODEL_FROM_DATABASE=PlayStation Portable [Mass Storage] + +usb:v6547* + ID_VENDOR_FROM_DATABASE=Arkmicro Technologies Inc. + +usb:v6547p0232* + ID_MODEL_FROM_DATABASE=ARK3116 Serial + +usb:v6615* + ID_VENDOR_FROM_DATABASE=IRTOUCHSYSTEMS Co. Ltd. + +usb:v6615p0001* + ID_MODEL_FROM_DATABASE=Touchscreen + +usb:v6666* + ID_VENDOR_FROM_DATABASE=Prototype product Vendor ID + +usb:v6666p0667* + ID_MODEL_FROM_DATABASE=WiseGroup Smart Joy PSX, PS-PC Smart JoyPad + +usb:v6666p2667* + ID_MODEL_FROM_DATABASE=JCOP BlueZ Smartcard reader + +usb:v6666p8802* + ID_MODEL_FROM_DATABASE=SmartJoy Dual Plus PS2 converter + +usb:v6666p8804* + ID_MODEL_FROM_DATABASE=WiseGroup SuperJoy Box 5 + +usb:v6677* + ID_VENDOR_FROM_DATABASE=WiseGroup, Ltd. + +usb:v6677p8802* + ID_MODEL_FROM_DATABASE=SmartJoy Dual Plus PS2 converter + +usb:v6677p8811* + ID_MODEL_FROM_DATABASE=Deluxe Dance Mat + +usb:v6891* + ID_VENDOR_FROM_DATABASE=3Com + +usb:v6891pA727* + ID_MODEL_FROM_DATABASE=3CRUSB10075 802.11bg [ZyDAS ZD1211] + +usb:v695C* + ID_VENDOR_FROM_DATABASE=Opera1 + +usb:v695Cp3829* + ID_MODEL_FROM_DATABASE=Opera1 DVB-S (warm state) + +usb:v6993* + ID_VENDOR_FROM_DATABASE=Yealink Network Technology Co., Ltd. + +usb:v6993pB001* + ID_MODEL_FROM_DATABASE=VoIP Phone + +usb:v6A75* + ID_VENDOR_FROM_DATABASE=Shanghai Jujo Electronics Co., Ltd + +usb:v7104* + ID_VENDOR_FROM_DATABASE=CME (Central Music Co.) + +usb:v7104p2202* + ID_MODEL_FROM_DATABASE=UF5/UF6/UF7/UF8 MIDI Master Keyboard + +usb:v726C* + ID_VENDOR_FROM_DATABASE=StackFoundry LLC + +usb:v726Cp2149* + ID_MODEL_FROM_DATABASE=EntropyKing Random Number Generator + +usb:v734C* + ID_VENDOR_FROM_DATABASE=TBS Technologies China + +usb:v734Cp5920* + ID_MODEL_FROM_DATABASE=Q-Box II DVB-S2 HD + +usb:v734Cp5928* + ID_MODEL_FROM_DATABASE=Q-Box II DVB-S2 HD + +usb:v7373* + ID_VENDOR_FROM_DATABASE=Beijing STONE Technology Co. Ltd. + +usb:v7373p5740* + ID_MODEL_FROM_DATABASE=Intelligent TFT-LCD Module + +usb:v7392* + ID_VENDOR_FROM_DATABASE=Edimax Technology Co., Ltd + +usb:v7392p7711* + ID_MODEL_FROM_DATABASE=EW-7711UTn nLite Wireless Adapter [Ralink RT2870] + +usb:v7392p7717* + ID_MODEL_FROM_DATABASE=EW-7717UN 802.11n Wireless Adapter [Ralink RT2870] + +usb:v7392p7718* + ID_MODEL_FROM_DATABASE=EW-7718UN 802.11n Wireless Adapter [Ralink RT2870] + +usb:v7392p7722* + ID_MODEL_FROM_DATABASE=EW-7722UTn 802.11n Wireless Adapter [Ralink RT307x] + +usb:v7392p7811* + ID_MODEL_FROM_DATABASE=EW-7811Un 802.11n Wireless Adapter [Realtek RTL8188CUS] + +usb:v8086* + ID_VENDOR_FROM_DATABASE=Intel Corp. + +usb:v8086p0001* + ID_MODEL_FROM_DATABASE=AnyPoint (TM) Home Network 1.6 Mbps Wireless Adapter + +usb:v8086p0044* + ID_MODEL_FROM_DATABASE=CPU DRAM Controller + +usb:v8086p0046* + ID_MODEL_FROM_DATABASE=HD Graphics + +usb:v8086p0100* + ID_MODEL_FROM_DATABASE=Personal Audio Player 3000 + +usb:v8086p0101* + ID_MODEL_FROM_DATABASE=Personal Audio Player 3000 + +usb:v8086p0110* + ID_MODEL_FROM_DATABASE=Easy PC Camera + +usb:v8086p0120* + ID_MODEL_FROM_DATABASE=PC Camera CS120 + +usb:v8086p0180* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p0181* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p0182* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p0186* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p0188* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p0200* + ID_MODEL_FROM_DATABASE=AnyPoint(TM) Wireless II Network 11Mbps Adapter [Atmel AT76C503A] + +usb:v8086p0431* + ID_MODEL_FROM_DATABASE=Intel Pro Video PC Camera + +usb:v8086p0510* + ID_MODEL_FROM_DATABASE=Digital Movie Creator + +usb:v8086p0630* + ID_MODEL_FROM_DATABASE=Pocket PC Camera + +usb:v8086p0780* + ID_MODEL_FROM_DATABASE=CS780 Microphone Input + +usb:v8086p07D3* + ID_MODEL_FROM_DATABASE=BLOB boot loader firmware + +usb:v8086p0DAD* + ID_MODEL_FROM_DATABASE=Cherry MiniatureCard Keyboard + +usb:v8086p1010* + ID_MODEL_FROM_DATABASE=AnyPoint(TM) Home Network 10 Mbps Phoneline Adapter + +usb:v8086p110A* + ID_MODEL_FROM_DATABASE=Bluetooth Controller from (Ericsson P4A) + +usb:v8086p110B* + ID_MODEL_FROM_DATABASE=Bluetooth Controller from (Intel/CSR) + +usb:v8086p1110* + ID_MODEL_FROM_DATABASE=PRO/Wireless LAN Module + +usb:v8086p1111* + ID_MODEL_FROM_DATABASE=PRO/Wireless 2011B 802.11b Adapter [Intersil PRISM 2.5] + +usb:v8086p1134* + ID_MODEL_FROM_DATABASE=Hollister Mobile Monitor + +usb:v8086p1139* + ID_MODEL_FROM_DATABASE=In-Target Probe (ITP) + +usb:v8086p1234* + ID_MODEL_FROM_DATABASE=Prototype Reader/Writer + +usb:v8086p1403* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p1405* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p1406* + ID_MODEL_FROM_DATABASE=WiMAX Connection 2400m + +usb:v8086p2448* + ID_MODEL_FROM_DATABASE=82801 PCI Bridge + +usb:v8086p3100* + ID_MODEL_FROM_DATABASE=PRO/DSL 3220 Modem - WAN + +usb:v8086p3101* + ID_MODEL_FROM_DATABASE=PRO/DSL 3220 Modem + +usb:v8086p3240* + ID_MODEL_FROM_DATABASE=AnyPoint® 3240 Modem - WAN + +usb:v8086p3241* + ID_MODEL_FROM_DATABASE=AnyPoint® 3240 Modem + +usb:v8086p8602* + ID_MODEL_FROM_DATABASE=Miniature Card Slot + +usb:v8086p9303* + ID_MODEL_FROM_DATABASE=Intel 8x930Hx Hub + +usb:v8086p9500* + ID_MODEL_FROM_DATABASE=CE 9500 DVB-T + +usb:v8086p9890* + ID_MODEL_FROM_DATABASE=82930 Test Board + +usb:v8086pBEEF* + ID_MODEL_FROM_DATABASE=SCM Miniature Card Reader/Writer + +usb:v8086pC013* + ID_MODEL_FROM_DATABASE=Wireless HID Station + +usb:v8086pF001* + ID_MODEL_FROM_DATABASE=XScale PXA27x Bulverde flash + +usb:v8086pF1A5* + ID_MODEL_FROM_DATABASE=Z-U130 [Value Solid State Drive] + +usb:v8087* + ID_VENDOR_FROM_DATABASE=Intel Corp. + +usb:v8087p0020* + ID_MODEL_FROM_DATABASE=Integrated Rate Matching Hub + +usb:v8087p0024* + ID_MODEL_FROM_DATABASE=Integrated Rate Matching Hub + +usb:v80EE* + ID_VENDOR_FROM_DATABASE=VirtualBox + +usb:v80EEp0021* + ID_MODEL_FROM_DATABASE=USB Tablet + +usb:v8282* + ID_VENDOR_FROM_DATABASE=Keio + +usb:v8282p3201* + ID_MODEL_FROM_DATABASE=Retro Adapter + +usb:v8282p3301* + ID_MODEL_FROM_DATABASE=Retro Adapter Mouse + +usb:v8341* + ID_VENDOR_FROM_DATABASE=EGO Systems, Inc. + +usb:v8341p2000* + ID_MODEL_FROM_DATABASE=Flashdisk + +usb:v8564* + ID_VENDOR_FROM_DATABASE=Transcend Information, Inc. + +usb:v8564p1000* + ID_MODEL_FROM_DATABASE=JetFlash + +usb:v8564p4000* + ID_MODEL_FROM_DATABASE=RDF8 + +usb:v8644* + ID_VENDOR_FROM_DATABASE=Intenso GmbG + +usb:v8644p8003* + ID_MODEL_FROM_DATABASE=Micro Line + +usb:v8644p800B* + ID_MODEL_FROM_DATABASE=Micro Line (4GB) + +usb:v8E06* + ID_VENDOR_FROM_DATABASE=CH Products, Inc. + +usb:v8E06pF700* + ID_MODEL_FROM_DATABASE=DT225 Trackball + +usb:v9016* + ID_VENDOR_FROM_DATABASE=Sitecom + +usb:v9016p182D* + ID_MODEL_FROM_DATABASE=WL-022 802.11b Adapter + +usb:v9022* + ID_VENDOR_FROM_DATABASE=TeVii Technology Ltd. + +usb:v9022pD630* + ID_MODEL_FROM_DATABASE=DVB-S S630 + +usb:v9022pD650* + ID_MODEL_FROM_DATABASE=DVB-S2 S650 + +usb:v9022pD660* + ID_MODEL_FROM_DATABASE=DVB-S2 S660 + +usb:v9148* + ID_VENDOR_FROM_DATABASE=GeoLab, Ltd + +usb:v9148p0004* + ID_MODEL_FROM_DATABASE=R3 Compatible Device + +usb:v9710* + ID_VENDOR_FROM_DATABASE=MosChip Semiconductor + +usb:v9710p7703* + ID_MODEL_FROM_DATABASE=MCS7703 Serial Port Adapter + +usb:v9710p7705* + ID_MODEL_FROM_DATABASE=MCS7705 Parallel port adapter + +usb:v9710p7715* + ID_MODEL_FROM_DATABASE=MCS7715 Parallel and serial port adapter + +usb:v9710p7717* + ID_MODEL_FROM_DATABASE=MCS7717 3-port hub with serial and parallel adapter + +usb:v9710p7720* + ID_MODEL_FROM_DATABASE=MCS7720 Dual serial port adapter + +usb:v9710p7730* + ID_MODEL_FROM_DATABASE=MCS7730 10/100 Mbps Ethernet adapter + +usb:v9710p7780* + ID_MODEL_FROM_DATABASE=MCS7780 4Mbps Fast IrDA Adapter + +usb:v9710p7830* + ID_MODEL_FROM_DATABASE=MCS7830 10/100 Mbps Ethernet adapter + +usb:v9710p7832* + ID_MODEL_FROM_DATABASE=MCS7832 10/100 Mbps Ethernet adapter + +usb:v9710p7840* + ID_MODEL_FROM_DATABASE=MCS7820/MCS7840 2/4 port serial adapter + +usb:v9849* + ID_VENDOR_FROM_DATABASE=Bestmedia CD Recordable GmbH & Co. KG + +usb:v9849p0701* + ID_MODEL_FROM_DATABASE=Platinum MyDrive HP + +usb:v9999* + ID_VENDOR_FROM_DATABASE=Odeon + +usb:v9999p0001* + ID_MODEL_FROM_DATABASE=JAF Mobile Phone Flasher Interface + +usb:v99FA* + ID_VENDOR_FROM_DATABASE=Grandtec + +usb:v99FAp8988* + ID_MODEL_FROM_DATABASE=V.cap Camera Device + +usb:v9AC4* + ID_VENDOR_FROM_DATABASE=J. Westhues + +usb:v9AC4p4B8F* + ID_MODEL_FROM_DATABASE=ProxMark-3 RFID Instrument + +usb:v9E88* + ID_VENDOR_FROM_DATABASE=Marvell Semiconductor, Inc. + +usb:v9E88p9E8F* + ID_MODEL_FROM_DATABASE=Plug Computer Basic [SheevaPlug] + +usb:vA128* + ID_VENDOR_FROM_DATABASE=AnMo Electronics Corp. / Dino-Lite (?) + +usb:vA128p0610* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + HV7131R) + +usb:vA128p0611* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + HV7131R) + +usb:vA128p0612* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C120 + HV7131R) + +usb:vA128p0613* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + HV7131R) + +usb:vA128p0614* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + MI1310/MT9M111) + +usb:vA128p0615* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + MI1310/MT9M111) + +usb:vA128p0616* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C120 + HV7131R) + +usb:vA128p0617* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + MI1310/MT9M111) + +usb:vA128p0618* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope (SN9C201 + HV7131R) + +usb:vA168* + ID_VENDOR_FROM_DATABASE=AnMo Electronics Corporation + +usb:vA168p0610* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope + +usb:vA168p0611* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope + +usb:vA168p0613* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope + +usb:vA168p0614* + ID_MODEL_FROM_DATABASE=Dino-Lite Pro Digital Microscope + +usb:vA168p0615* + ID_MODEL_FROM_DATABASE=Dino-Lite Pro Digital Microscope + +usb:vA168p0617* + ID_MODEL_FROM_DATABASE=Dino-Lite Pro Digital Microscope + +usb:vA168p0618* + ID_MODEL_FROM_DATABASE=Dino-Lite Digital Microscope + +usb:vA600* + ID_VENDOR_FROM_DATABASE=Asix + +usb:vA600pE110* + ID_MODEL_FROM_DATABASE=OK1ZIA Davac 4.x + +usb:vA727* + ID_VENDOR_FROM_DATABASE=3Com + +usb:vA727p6893* + ID_MODEL_FROM_DATABASE=3CRUSB20075 OfficeConnect Wireless 108Mbps 11g Adapter [Atheros AR5523] + +usb:vA727p6895* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:vA727p6897* + ID_MODEL_FROM_DATABASE=AR5523 + +usb:vAAAA* + ID_VENDOR_FROM_DATABASE=MXT + +usb:vAAAAp8815* + ID_MODEL_FROM_DATABASE=microSD CardReader + +usb:vABCD* + ID_VENDOR_FROM_DATABASE=Unknown + +usb:vABCDpCDEE* + ID_MODEL_FROM_DATABASE=Petcam + +usb:vB58E* + ID_VENDOR_FROM_DATABASE=Blue Microphones + +usb:vB58Ep9E84* + ID_MODEL_FROM_DATABASE=Yeti Stereo Microphone + +usb:vC216* + ID_VENDOR_FROM_DATABASE=Card Device Expert Co., LTD + +usb:vC216p0180* + ID_MODEL_FROM_DATABASE=MSR90 MagStripe reader + +usb:vC251* + ID_VENDOR_FROM_DATABASE=Keil Software, Inc. + +usb:vC251p2710* + ID_MODEL_FROM_DATABASE=ULink + +usb:vCACE* + ID_VENDOR_FROM_DATABASE=CACE Technologies Inc. + +usb:vCACEp0002* + ID_MODEL_FROM_DATABASE=AirPCAP Classic 802.11 packet capture adapter + +usb:vCACEp0300* + ID_MODEL_FROM_DATABASE=AirPcap NX [Atheros AR9001U-(2)NG] + +usb:vCD12* + ID_VENDOR_FROM_DATABASE=SMART TECHNOLOGY INDUSTRIAL LTD. + +usb:vD208* + ID_VENDOR_FROM_DATABASE=Ultimarc + +usb:vD208p0310* + ID_MODEL_FROM_DATABASE=Mini-PAC Arcade Control Interface + +usb:vD209* + ID_VENDOR_FROM_DATABASE=Ultimarc + +usb:vD209p0301* + ID_MODEL_FROM_DATABASE=I-PAC Arcade Control Interface + +usb:vD209p0501* + ID_MODEL_FROM_DATABASE=Ultra-Stik Ultimarc Ultra-Stik Player 1 + +usb:vD904* + ID_VENDOR_FROM_DATABASE=LogiLink + +usb:vD904p0003* + ID_MODEL_FROM_DATABASE=Laser Mouse (ID0009A) + +usb:vE4E4* + ID_VENDOR_FROM_DATABASE=Xorcom Ltd. + +usb:vE4E4p1130* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1131* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1132* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1140* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1141* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1142* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1150* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1151* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1152* + ID_MODEL_FROM_DATABASE=Astribank series + +usb:vE4E4p1160* + ID_MODEL_FROM_DATABASE=Astribank 2 series + +usb:vE4E4p1161* + ID_MODEL_FROM_DATABASE=Astribank 2 series + +usb:vE4E4p1162* + ID_MODEL_FROM_DATABASE=Astribank 2 series + +usb:vEB03* + ID_VENDOR_FROM_DATABASE=MakingThings + +usb:vEB03p0920* + ID_MODEL_FROM_DATABASE=Make Controller Kit + +usb:vEB1A* + ID_VENDOR_FROM_DATABASE=eMPIA Technology, Inc. + +usb:vEB1Ap17DE* + ID_MODEL_FROM_DATABASE=KWorld V-Stream XPERT DTV - DVB-T USB cold + +usb:vEB1Ap17DF* + ID_MODEL_FROM_DATABASE=KWorld V-Stream XPERT DTV - DVB-T USB warm + +usb:vEB1Ap2571* + ID_MODEL_FROM_DATABASE=M035 Compact Web Cam + +usb:vEB1Ap2710* + ID_MODEL_FROM_DATABASE=SilverCrest Webcam + +usb:vEB1Ap2750* + ID_MODEL_FROM_DATABASE=ECS Elitegroup G220 integrated Webcam + +usb:vEB1Ap2761* + ID_MODEL_FROM_DATABASE=EeePC 701 integrated Webcam + +usb:vEB1Ap2776* + ID_MODEL_FROM_DATABASE=Combined audio and video input device + +usb:vEB1Ap2800* + ID_MODEL_FROM_DATABASE=Terratec Cinergy 200 + +usb:vEB1Ap2801* + ID_MODEL_FROM_DATABASE=GrabBeeX+ Video Encoder + +usb:vEB1Ap2863* + ID_MODEL_FROM_DATABASE=Video Grabber + +usb:vEB1Ap2870* + ID_MODEL_FROM_DATABASE=Pinnacle PCTV Stick + +usb:vEB1Ap2881* + ID_MODEL_FROM_DATABASE=EM2881 Video Controller + +usb:vEB1Ap50A3* + ID_MODEL_FROM_DATABASE=Gadmei UTV380 TV Box + +usb:vEB1Ap50A6* + ID_MODEL_FROM_DATABASE=Gadmei UTV330 TV Box + +usb:vEB1ApE355* + ID_MODEL_FROM_DATABASE=KWorld DVB-T 355U Digital TV Dongle + +usb:vEB2A* + ID_VENDOR_FROM_DATABASE=KWorld + +usb:vEF18* + ID_VENDOR_FROM_DATABASE=SMART TECHNOLOGY INDUSTRIAL LTD. + +usb:vF003* + ID_VENDOR_FROM_DATABASE=Hewlett Packard + +usb:vF003p6002* + ID_MODEL_FROM_DATABASE=PhotoSmart C500 + +usb:vF182* + ID_VENDOR_FROM_DATABASE=Leap Motion + +usb:vF182p0003* + ID_MODEL_FROM_DATABASE=Controller + +usb:vF4EC* + ID_VENDOR_FROM_DATABASE=Atten Electronics / Siglent Technologies + +usb:vF4ECpEE38* + ID_MODEL_FROM_DATABASE=Digital Storage Oscilloscope + +usb:vF4ED* + ID_VENDOR_FROM_DATABASE=Shenzhen Siglent Co., Ltd. + +usb:vF4EDpEE37* + ID_MODEL_FROM_DATABASE=SDG1010 Waveform Generator + +usb:vF4EDpEE3A* + ID_MODEL_FROM_DATABASE=SDG1010 Waveform Generator (TMC mode) + +usb:vF766* + ID_VENDOR_FROM_DATABASE=Hama + +usb:vF766p0001* + ID_MODEL_FROM_DATABASE=PC-Gamepad "Greystorm" + +usb:vFC08* + ID_VENDOR_FROM_DATABASE=Conrad Electronic SE + +usb:vFC08p0101* + ID_MODEL_FROM_DATABASE=MIDI Cable UA0037 + +usb:vFFEE* + ID_VENDOR_FROM_DATABASE=FNK Tech + +usb:vFFEEp0100* + ID_MODEL_FROM_DATABASE=Card Reader Controller RTS5101/RTS5111/RTS5116 diff --git a/src/grp-udev/hwdb/60-evdev.hwdb b/src/grp-udev/hwdb/60-evdev.hwdb new file mode 100644 index 0000000000..d4cd61c24d --- /dev/null +++ b/src/grp-udev/hwdb/60-evdev.hwdb @@ -0,0 +1,238 @@ +# This file is part of systemd. +# +# The lookup keys are composed in: +# 60-evdev.rules +# +# Note: The format of the "evdev:" prefix match key is a +# contract between the rules file and the hardware data, it might +# change in later revisions to support more or better matches, it +# is not necessarily expected to be a stable ABI. +# +# Match string formats: +# evdev: +# evdev:name::dmi: +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/61-evdev-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the device in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX. +# +# Allowed properties are: +# EVDEV_ABS_=:::: +# +# where is the hexadecimal EV_ABS code as listed in linux/input.h +# and min, max, res, fuzz, flat are the decimal values to the respective +# fields of the struct input_absinfo as listed in linux/input.h. +# If a field is missing the field will be left as-is. Not all fields need to +# be present. e.g. ::45 sets the resolution to 45 units/mm. + +# +# Sort by brand, model + +######################################### +# Apple +######################################### + +# Macbook2,1 (late 2006), single-button touchpad +evdev:input:b0003v05ACp021B* + EVDEV_ABS_00=256:1471:12 + EVDEV_ABS_01=256:831:12 + +# Macbook5,1 (unibody), aka wellspring3 +evdev:input:b0003v05ACp0236* +evdev:input:b0003v05ACp0237* +evdev:input:b0003v05ACp0238* + EVDEV_ABS_00=::92 + EVDEV_ABS_01=::90 + EVDEV_ABS_35=::92 + EVDEV_ABS_36=::90 + +# Macbook8 (unibody, March 2011) +evdev:input:b0003v05ACp0245* +evdev:input:b0003v05ACp0246* +evdev:input:b0003v05ACp0247* + EVDEV_ABS_00=::92 + EVDEV_ABS_01=::91 + EVDEV_ABS_35=::92 + EVDEV_ABS_36=::91 + +# Macbook8,2 (unibody) +evdev:input:b0003v05ACp0252* +evdev:input:b0003v05ACp0253* +evdev:input:b0003v05ACp0254* + EVDEV_ABS_00=::94 + EVDEV_ABS_01=::92 + EVDEV_ABS_35=::94 + EVDEV_ABS_36=::92 + +# MacbookPro10,1 (unibody, June 2012) +evdev:input:b0003v05ACp0259* +evdev:input:b0003v05ACp025A* +evdev:input:b0003v05ACp025B* +# MacbookPro10,2 (unibody, October 2012) +evdev:input:b0003v05ACp0259* +evdev:input:b0003v05ACp025A* +evdev:input:b0003v05ACp025B* + EVDEV_ABS_00=::94 + EVDEV_ABS_01=::92 + EVDEV_ABS_35=::94 + EVDEV_ABS_36=::92 + +######################################### +# ASUS +######################################### +# Asus K52JT +evdev:name:ETPS/2 Elantech Touchpad:dmi:bvn*:bvr*:bd*:svnASUSTeKComputerInc.:pnK52JT:* + EVDEV_ABS_00=::18 + EVDEV_ABS_01=::16 + EVDEV_ABS_35=::18 + EVDEV_ABS_36=::16 + +evdev:name:ETPS/2 Elantech Touchpad:dmi:*:svnASUSTeKCOMPUTERINC.:pnX550CC:* + EVDEV_ABS_00=::31 + EVDEV_ABS_01=::30 + EVDEV_ABS_35=::31 + EVDEV_ABS_36=::30 + +# Asus UX305 +evdev:name:Elan Touchpad:dmi:*:svnASUSTeKCOMPUTERINC.:pnUX305UA:* + EVDEV_ABS_00=0:3097:32 + EVDEV_ABS_01=0:2119:33 + EVDEV_ABS_35=0:3097:32 + EVDEV_ABS_36=0:2119:33 + +######################################### +# Dell +######################################### + +# Dell Vostro 1510 +evdev:name:AlpsPS/2 ALPS GlidePoint*:dmi:bvn*:bvr*:bd*:svnDellInc.:pnVostro1510* + EVDEV_ABS_00=::14 + EVDEV_ABS_01=::18 + +# Dell Inspiron N5040 +evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnInspironN5040* + EVDEV_ABS_00=25:2000:22 + EVDEV_ABS_01=0:1351:28 + EVDEV_ABS_35=25:2000:22 + EVDEV_ABS_36=0:1351:28 + +# Dell Latitude E6220 +evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6220* + EVDEV_ABS_00=76:1815:22 + EVDEV_ABS_01=131:1330:30 + EVDEV_ABS_35=76:1815:22 + EVDEV_ABS_36=131:1330:30 + +# Dell Precision M4700 +evdev:name:AlpsPS/2 ALPS DualPoint TouchPad:dmi:*svnDellInc.:pnPrecisionM4700* + EVDEV_ABS_00=0:1960:24 + EVDEV_ABS_01=113:1436:30 + EVDEV_ABS_35=0:1960:24 + EVDEV_ABS_36=113:1436:30 + +# Dell XPS15 9550 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:bvn*:bvr*:bd*:svnDellInc.:pnXPS159550* + EVDEV_ABS_00=::41 + EVDEV_ABS_01=::43 + EVDEV_ABS_35=::41 + EVDEV_ABS_36=::43 + +######################################### +# Google +######################################### + +# Chromebook Pixel (2015) - Samus +evdev:name:Atmel maXTouch Touch*:dmi:bvn*:bvr*:bd*:svnGOOGLE:pnSamus* + EVDEV_ABS_00=::10 + EVDEV_ABS_01=::10 + EVDEV_ABS_35=::10 + EVDEV_ABS_36=::10 + +######################################### +# HP +######################################### + +# HP Pavilion dm4 +evdev:name:SynPS/2 Synaptics TouchPad*:dmi:*svnHewlett-Packard:pnHPPaviliondm4* + EVDEV_ABS_00=1360:5563:47 + EVDEV_ABS_01=1269:4618:61 + EVDEV_ABS_35=1360:5563:47 + EVDEV_ABS_36=1269:4618:61 + +######################################### +# Lenovo +######################################### + +# Lenovo E530 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:pn*ThinkPadEdgeE530* + EVDEV_ABS_00=1241:5703:49 + EVDEV_ABS_01=1105:4820:68 + EVDEV_ABS_35=1241:5703:49 + EVDEV_ABS_36=1105:4820:68 + +# Lenovo P50 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*P50* + EVDEV_ABS_00=::44 + EVDEV_ABS_01=::67 + EVDEV_ABS_35=::44 + EVDEV_ABS_36=::67 + +# Lenovo T460 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*T460* + EVDEV_ABS_00=1266:5677:44 + EVDEV_ABS_01=1093:4832:65 + EVDEV_ABS_35=1266:5677:44 + EVDEV_ABS_36=1093:4832:65 + +# Lenovo T510 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*T510* + EVDEV_ABS_00=778:6239:72 + EVDEV_ABS_01=841:5330:100 + EVDEV_ABS_35=778:6239:72 + EVDEV_ABS_36=841:5330:100 + +# Lenovo V360 +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*pvrLenovoV360* + EVDEV_ABS_00=1243:5927:60 + EVDEV_ABS_01=902:5330:108 + +# Lenovo X220 series +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO:*pvrThinkPadX220* + EVDEV_ABS_00=1316:5627:58 + EVDEV_ABS_01=1355:4826:81 + EVDEV_ABS_35=1316:5627:58 + EVDEV_ABS_36=1355:4826:81 + +# Lenovo X230 series +evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnLENOVO*:pn*ThinkPad*X230* + EVDEV_ABS_01=::100 + EVDEV_ABS_36=::100 + +# Lenovo Y700-14ISK +evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoideapadY700-14ISK* + EVDEV_ABS_00=::27 + EVDEV_ABS_01=::29 + EVDEV_ABS_35=::27 + EVDEV_ABS_36=::29 + +######################################### +# Samsung +######################################### + +# Samsung 305V4 +evdev:name:ETPS/2 Elantech Touchpad:dmi:*svnSAMSUNGELECTRONICSCO.,LTD.:pn305V4A/305V5A* + EVDEV_ABS_00=0:2480:28 + EVDEV_ABS_01=0:1116:24 + EVDEV_ABS_35=0:2480:28 + EVDEV_ABS_36=0:1116:24 diff --git a/src/grp-udev/hwdb/60-keyboard.hwdb b/src/grp-udev/hwdb/60-keyboard.hwdb new file mode 100644 index 0000000000..fd49b03493 --- /dev/null +++ b/src/grp-udev/hwdb/60-keyboard.hwdb @@ -0,0 +1,1234 @@ +# This file is part of systemd. +# +# Keyboard mapping of scan codes to key codes, and +# scan codes to add to the AT keyboard's 'force-release' list. +# +# The lookup keys are composed in: +# 60-keyboard.rules +# +# Note: The format of the "evdev:" prefix match key is a +# contract between the rules file and the hardware data, it might +# change in later revisions to support more or better matches, it +# is not necessarily expected to be a stable ABI. +# +# Supported hardware matches are: +# - Generic input devices match: +# evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV +# This matches on the kernel modalias of the input-device, mainly: +# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and +# WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV +# is an arbitrary length input-modalias describing the device capabilities. +# +# - AT keyboard DMI data matches: +# evdev:atkbd:dmi:bvn*:bvr*:bd*:svn:pn:pvr* +# and are the firmware-provided strings +# exported by the kernel DMI modalias. +# +# - Input driver device name and DMI data match: +# evdev:name::dmi:bvn*:bvr*:bd*:svn:pn* +# is the name device specified by the +# driver, is the firmware-provided string exported +# by the kernel DMI modalias. +# +# Scan codes are specified as: +# KEYBOARD_KEY_= +# The scan code should be expressed in hex lowercase. The key codes +# are retrieved and normalized from the kernel input API header. +# +# An '!' as the first character of the key identifier string +# will add the scan code to the AT keyboard's list of scan codes +# where the driver will synthesize a release event and not expect +# it to be generated by the hardware. +# +# To debug key presses and access scan code mapping data of +# an input device use the commonly available tool: evtest(1). +# +# To update this file, create a new file +# /etc/udev/hwdb.d/70-keyboard.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the keyboard in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX. + +########################################## +# Acer +########################################## + +# common keys +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGateway*:pnA0A1*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svneMachines:pneMachines*E725:pvr* + KEYBOARD_KEY_a5=help # Fn+F1 + KEYBOARD_KEY_a6=setup # Fn+F2 Acer eSettings + KEYBOARD_KEY_a7=battery # Fn+F3 Power Management + KEYBOARD_KEY_a9=switchvideomode # Fn+F5 + KEYBOARD_KEY_b2=www + KEYBOARD_KEY_b3=euro + KEYBOARD_KEY_b4=dollar + KEYBOARD_KEY_ce=brightnessup # Fn+Right + KEYBOARD_KEY_d4=bluetooth # (toggle) off-to-on + KEYBOARD_KEY_d5=wlan # (toggle) on-to-off + KEYBOARD_KEY_d6=wlan # (toggle) off-to-on + KEYBOARD_KEY_d7=bluetooth # (toggle) on-to-off + KEYBOARD_KEY_d8=bluetooth # (toggle) off-to-on + KEYBOARD_KEY_d9=brightnessup # Fn+Right + KEYBOARD_KEY_ee=brightnessup # Fn+Right + KEYBOARD_KEY_ef=brightnessdown # Fn+Left + KEYBOARD_KEY_f1=f22 # Fn+F7 Touchpad toggle (off-to-on) + KEYBOARD_KEY_f2=f23 # Fn+F7 Touchpad toggle (on-to-off) + KEYBOARD_KEY_f3=prog2 # "P2" programmable button + KEYBOARD_KEY_f4=prog1 # "P1" programmable button + KEYBOARD_KEY_f5=presentation + KEYBOARD_KEY_f8=fn + KEYBOARD_KEY_f9=prog1 # Launch NTI shadow + +# Acer kernel driver +evdev:name:Acer WMI hotkeys:dmi:bvn*:bvr*:bd*:svn*:pnAcer*:pvr* + KEYBOARD_KEY_82=f21 # Touchpad toggle + +# Aspire models +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*:pvr* + KEYBOARD_KEY_84=bluetooth # sent when bluetooth module missing, and key pressed + KEYBOARD_KEY_d9=bluetooth # Bluetooth off + KEYBOARD_KEY_92=media # Acer arcade + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*5720*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnZG8*:pvr* + KEYBOARD_KEY_f4=prog3 # e-key + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*5920G:* + KEYBOARD_KEY_8a=media + KEYBOARD_KEY_a6=setup + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*6920:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*8930:* + KEYBOARD_KEY_ca=prog3 # key 'HOLD' on CineDash Media Console + KEYBOARD_KEY_83=rewind + KEYBOARD_KEY_89=fastforward + KEYBOARD_KEY_9e=back + +# Travelmate C300 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*C3[01]0*:pvr* + KEYBOARD_KEY_67=f24 # FIXME: rotate screen + KEYBOARD_KEY_68=up + KEYBOARD_KEY_69=down + KEYBOARD_KEY_6b=fn + KEYBOARD_KEY_6c=screenlock # FIXME: lock tablet device/buttons + +# on some models this isn't brightnessup +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*5210*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*5220*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*5610*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*5620*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pn*5720*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*4720*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*6593:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*1640:* + KEYBOARD_KEY_ee=screenlock + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAOA*:pvr* + KEYBOARD_KEY_a9=!switchvideomode # Fn+F5 + +########################################################### +# Alienware +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAlienware*:pn* + KEYBOARD_KEY_8a=ejectcd + +# Alienware/Dell reserves these keys; safe to apply on all their devices +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAlienware*:pn*:pvr* + KEYBOARD_KEY_bf=!prog1 #graphics amplifier, cable plug-in event + KEYBOARD_KEY_c1=!prog2 #graphics amplifier, undock-button event + KEYBOARD_KEY_c2=!power #graphics amplifier, surprise undock event + +########################################################### +# Asus +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnASUS:pn* + KEYBOARD_KEY_ed=volumeup + KEYBOARD_KEY_ee=volumedown + KEYBOARD_KEY_ef=mute + +evdev:name:Asus WMI hotkeys:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:pvr* +evdev:name:Eee PC WMI hotkeys:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:pvr* +evdev:name:Asus Laptop extra buttons:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:pvr* + KEYBOARD_KEY_6b=f21 # Touchpad Toggle + +########################################################### +# BenQ +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*BenQ*:pn*Joybook*R22*:pvr* + KEYBOARD_KEY_6e=wlan + +########################################################### +# Compal +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnCOMPAL:pnHEL80I:* + KEYBOARD_KEY_84=wlan + +########################################################### +# COMPAQ +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnCompaq*:pn*E500*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnCompaq*:pn*Evo*N*:pvr* + KEYBOARD_KEY_a3=www # I key + KEYBOARD_KEY_9a=search + KEYBOARD_KEY_9e=email + KEYBOARD_KEY_9f=homepage + +evdev:input:b0003v049Fp0051* + KEYBOARD_KEY_0c0011=presentation + KEYBOARD_KEY_0c0012=addressbook + KEYBOARD_KEY_0c0013=info + KEYBOARD_KEY_0c0014=prog1 + KEYBOARD_KEY_0c0015=messenger + +########################################################### +# Dell +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pn* + KEYBOARD_KEY_81=playpause # Play/Pause + KEYBOARD_KEY_82=stopcd # Stop + KEYBOARD_KEY_83=previoussong # Previous song + KEYBOARD_KEY_84=nextsong # Next song + KEYBOARD_KEY_85=brightnessdown # Fn+Down Brightness Down + KEYBOARD_KEY_86=brightnessup # Fn+Up Brightness Up + KEYBOARD_KEY_87=battery # Fn+F3 battery icon + KEYBOARD_KEY_88=unknown # Fn+F2 Turn On/Off Wireless - handled in hardware + KEYBOARD_KEY_89=ejectclosecd # Fn+F10 Eject CD + KEYBOARD_KEY_8a=suspend # Fn+F1 hibernate + KEYBOARD_KEY_8b=switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle") + KEYBOARD_KEY_8c=unknown # Fn+Right Auto Brightness + KEYBOARD_KEY_8F=switchvideomode # Fn+F7 aspect ratio + KEYBOARD_KEY_90=previoussong # Front panel previous song + KEYBOARD_KEY_91=prog1 # Wi-Fi Catcher (Dell-specific) + KEYBOARD_KEY_92=media # MediaDirect button (house icon) + KEYBOARD_KEY_93=unknown # FIXME Fn+Left Auto Brightness + KEYBOARD_KEY_95=camera # Shutter button - Takes a picture if optional camera available + KEYBOARD_KEY_97=email # Tablet email button + KEYBOARD_KEY_98=f21 # FIXME: Tablet screen rotation + KEYBOARD_KEY_99=nextsong # Front panel next song + KEYBOARD_KEY_9a=setup # Tablet tools button + KEYBOARD_KEY_9b=switchvideomode # Display toggle button + KEYBOARD_KEY_9e=f21 # Touchpad toggle + KEYBOARD_KEY_a2=playpause # Front panel play/pause + KEYBOARD_KEY_a4=stopcd # Front panel stop + KEYBOARD_KEY_ed=media # MediaDirect button + KEYBOARD_KEY_d8=screenlock # FIXME: Tablet lock button + KEYBOARD_KEY_d9=f21 # Touchpad toggle + +# +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron*910:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron*101[012]:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron*1110:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron*1210:pvr* + KEYBOARD_KEY_84=wlan + +# Dell Inspiron 1520 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnInspiron*1520:pvr* + KEYBOARD_KEY_85=unknown # Brightness Down, also emitted by acpi-video, ignore + KEYBOARD_KEY_86=unknown # Brightness Up, also emitted by acpi-video, ignore + +# Latitude XT2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*XT2:pvr* + KEYBOARD_KEY_9b=up # tablet rocker up + KEYBOARD_KEY_9e=enter # tablet rocker press + KEYBOARD_KEY_9f=back # tablet back + KEYBOARD_KEY_a3=down # tablet rocker down + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnStudio*155[78]:pvr* + KEYBOARD_KEY_a0=! # mute + KEYBOARD_KEY_ae=! # volume down + KEYBOARD_KEY_b0=! # volume up + +# Dell Touchpad +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnPrecision*:pvr* + KEYBOARD_KEY_88=! # wireless switch + KEYBOARD_KEY_9e=!f21 + +# Dell XPS +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnXPS*:pvr* + KEYBOARD_KEY_8c=!unknown + +# Dell XPS12 9Q33 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDell*:pnXPS12-9Q33*:pvr* + KEYBOARD_KEY_88=wlan + KEYBOARD_KEY_65=direction # Screen Rotate + +# Dell Latitude microphone mute +evdev:name:Dell WMI hotkeys:dmi:bvn*:bvr*:bd*:svnDell*:pnLatitude* +# Dell Precision microphone mute +evdev:name:Dell WMI hotkeys:dmi:bvn*:bvr*:bd*:svnDell*:pnPrecision* + KEYBOARD_KEY_150=f20 # Mic mute toggle, should be micmute + +########################################################### +# Everex +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnEverex:pnXT5000*:pvr* + KEYBOARD_KEY_5c=media + KEYBOARD_KEY_65=f21 # Fn+F5 Touchpad toggle + KEYBOARD_KEY_67=prog3 # Fan speed control button + KEYBOARD_KEY_6f=brightnessup + KEYBOARD_KEY_7f=brightnessdown + KEYBOARD_KEY_b2=www + KEYBOARD_KEY_ec=mail + +########################################## +# Fujitsu +########################################## + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pnAMILO*M*:pvr* + KEYBOARD_KEY_97=prog2 + KEYBOARD_KEY_9f=prog1 + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pnAmilo*Li*1718:* + KEYBOARD_KEY_d6=wlan + +# Amilo Li 2732 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pnAMILO*Li*2732:* + KEYBOARD_KEY_d9=brightnessdown # Fn+F8 brightness down + KEYBOARD_KEY_ef=brightnessup # Fn+F9 brightness up + KEYBOARD_KEY_a9=switchvideomode # Fn+F10 Cycle between available video outputs + +# Amilo Pa 2548 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*AMILO*Pa*2548*:pvr* + KEYBOARD_KEY_e0=volumedown + KEYBOARD_KEY_e1=volumeup + KEYBOARD_KEY_e5=prog1 + +# Amilo Pro Edition V3505 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*AMILO*Pro*Edition*V3505*:pvr* + KEYBOARD_KEY_a5=help # Fn+F1 + KEYBOARD_KEY_a9=switchvideomode # Fn+F3 + KEYBOARD_KEY_d9=brightnessdown # Fn+F8 + KEYBOARD_KEY_e0=brightnessup # Fn+F9 + +# Amilo Pro v3205 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*AMILO*Pro*V3205*:pvr* + KEYBOARD_KEY_f4=f21 # FIXME: silent-mode decrease CPU/GPU clock + KEYBOARD_KEY_f7=switchvideomode # Fn+F3 + +# Amilo Si 1520 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*Amilo*Si*1520*:pvr* + KEYBOARD_KEY_e1=wlan + KEYBOARD_KEY_f3=wlan + KEYBOARD_KEY_ee=brightnessdown + KEYBOARD_KEY_e0=brightnessup + KEYBOARD_KEY_e2=bluetooth + KEYBOARD_KEY_f7=video + +# Esprimo Mobile V5 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*ESPRIMO*Mobile*V5*:pvr* + KEYBOARD_KEY_a9=switchvideomode + KEYBOARD_KEY_d9=brightnessdown + KEYBOARD_KEY_df=sleep + KEYBOARD_KEY_ef=brightnessup + +# Esprimo Mobile V6 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*:pn*ESPRIMO*Mobile*V6*:pvr* + KEYBOARD_KEY_ce=brightnessup + KEYBOARD_KEY_ef=brightnessdown + +########################################################### +# GIGABYTE +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGIGABYTE:pnU2442:* + KEYBOARD_KEY_a0=! # mute + +########################################################### +# Genius +########################################################### + +# Slimstar 320 +evdev:input:b0003v0458p0708* + KEYBOARD_KEY_0900f0=scrollup + KEYBOARD_KEY_0900f1=scrolldown + KEYBOARD_KEY_0900f3=back + KEYBOARD_KEY_0900f2=forward + KEYBOARD_KEY_0900f5=wordprocessor + KEYBOARD_KEY_0900f6=spreadsheet + KEYBOARD_KEY_0900f4=presentation + KEYBOARD_KEY_0c0223=www + KEYBOARD_KEY_0900f7=chat + KEYBOARD_KEY_0900fb=prog1 + KEYBOARD_KEY_0900f8=close + KEYBOARD_KEY_0900f9=graphicseditor + KEYBOARD_KEY_0900fd=scale + KEYBOARD_KEY_0900fc=screenlock + +########################################################### +# Hewlett Packard +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pn*:pvr* + KEYBOARD_KEY_81=fn_esc + KEYBOARD_KEY_89=battery # Fn+F8 + KEYBOARD_KEY_8a=screenlock # Fn+F6 + KEYBOARD_KEY_8b=camera + KEYBOARD_KEY_8c=media # music + KEYBOARD_KEY_8e=dvd + KEYBOARD_KEY_b1=help + KEYBOARD_KEY_b3=unknown # FIXME: Auto brightness + KEYBOARD_KEY_d7=wlan + KEYBOARD_KEY_92=brightnessdown # Fn+F7 (Fn+F9 on 6730b) + KEYBOARD_KEY_97=brightnessup # Fn+F8 (Fn+F10 on 6730b) + KEYBOARD_KEY_ee=switchvideomode # Fn+F4 + +# Tablet +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][aA][bB][lL][eE][tT]*:pvr* + KEYBOARD_KEY_82=prog2 # Funny Key + KEYBOARD_KEY_83=prog1 # Q + KEYBOARD_KEY_84=tab + KEYBOARD_KEY_85=esc + KEYBOARD_KEY_86=pageup + KEYBOARD_KEY_87=pagedown + +# Pavilion +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*:pvr* + KEYBOARD_KEY_88=media # FIXME: quick play + KEYBOARD_KEY_b7=print + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*Pavilion*dv7*Notebook*PC:pvr* + KEYBOARD_KEY_b7=print + KEYBOARD_KEY_c2=media # FIXME: quick play + KEYBOARD_KEY_c6=break + KEYBOARD_KEY_94=reserved + +# Elitebook +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2230s*:pvr* + KEYBOARD_KEY_88=presentation + KEYBOARD_KEY_d9=help # I key (high keycode: "info") + +# Presario +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Presario*CQ*:pvr* + KEYBOARD_KEY_d8=f21 + KEYBOARD_KEY_d9=f21 + +# 2510p 2530p +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2510p*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2530p*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*G60*Notebook*PC:pvr* + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + +# 2570p +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2570p*:pvr* + KEYBOARD_KEY_f8=wlan # Wireless HW switch button + +# TX2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:pvr* + KEYBOARD_KEY_c2=media + KEYBOARD_KEY_d8=!f23 # Toggle touchpad button on tx2 (OFF) + KEYBOARD_KEY_d9=!f22 # Toggle touchpad button on tx2 (ON) + +# Presario 2100 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnPresario*2100*:pvr* + KEYBOARD_KEY_f0=help + KEYBOARD_KEY_f1=screenlock + KEYBOARD_KEY_f3=search + +# Elitebook 8440p +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*EliteBook*8440p:pvr* + KEYBOARD_KEY_88=www + KEYBOARD_KEY_a0=mute + KEYBOARD_KEY_ae=volumedown + KEYBOARD_KEY_b0=volumeup + KEYBOARD_KEY_ec=mail + +# Elitebook 8460p +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*EliteBook*8460p:pvr* + KEYBOARD_KEY_f8=wlan # Wireless HW switch button + KEYBOARD_KEY_b3=prog1 # Fn+F11 - Ambient Light Sensor button + KEYBOARD_KEY_b1=prog2 # Fn+ESC - System information button + +# HDX9494nr +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHDX9494NR:pvr* + KEYBOARD_KEY_b2=www # Fn+F3 + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + +# Chromebook 14 +# Top row keys (between ESC and power button) +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnFalco:pvr* + KEYBOARD_KEY_3b=back + KEYBOARD_KEY_3c=forward + KEYBOARD_KEY_3d=refresh + KEYBOARD_KEY_3f=switchvideomode + KEYBOARD_KEY_40=brightnessdown + KEYBOARD_KEY_41=brightnessup + KEYBOARD_KEY_42=mute + KEYBOARD_KEY_43=volumedown + KEYBOARD_KEY_44=volumeup + KEYBOARD_KEY_db=search # Same position as caps lock key on most keyboards +# KEYBOARD_KEY_3e=fullscreen, no defined key sym + + +# HP EliteBook 725 G2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPLicrice:pvr* +# HP ProBook 440 G2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:pvr* +# several HP ProBooks 4xx +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*ProBook4*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:pvr* +# HP ZBook +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr* + KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute + +# HP Folio 1040g2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr* + KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + +# HP ProBook 6555b +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard:pnHPProBook6555b:* + KEYBOARD_KEY_b2=www # Earth + +# HP ProBook 440 G3 +evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*440*G3* +# HP ProBook 640 G2 +evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*640*G2* + KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev + KEYBOARD_KEY_f8=unknown # rf kill; also reported via special evdev + +########################################################### +# IBM +########################################################### + +# thinkpad_acpi driver +evdev:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnIBM*:pn*:pvr* + KEYBOARD_KEY_01=battery # Fn+F2 + KEYBOARD_KEY_02=screenlock # Fn+F3 + KEYBOARD_KEY_03=sleep # Fn+F4 + KEYBOARD_KEY_04=wlan # Fn+F5 + KEYBOARD_KEY_06=switchvideomode # Fn+F7 + KEYBOARD_KEY_07=zoom # Fn+F8 screen expand + KEYBOARD_KEY_08=f24 # Fn+F9 undock + KEYBOARD_KEY_0b=suspend # Fn+F12 + KEYBOARD_KEY_0f=brightnessup # Fn+Home + KEYBOARD_KEY_10=brightnessdown # Fn+End + KEYBOARD_KEY_11=kbdillumtoggle # Fn+PgUp - ThinkLight + KEYBOARD_KEY_13=zoom # Fn+Space + KEYBOARD_KEY_14=volumeup + KEYBOARD_KEY_15=volumedown + KEYBOARD_KEY_16=mute + KEYBOARD_KEY_17=prog1 # ThinkPad/ThinkVantage button (high keycode: "vendor") + +# IBM Thinkpad USB Keyboard Trackpoint +evdev:input:b0003v04B3p301[89]* + KEYBOARD_KEY_900f0=screenlock + KEYBOARD_KEY_900f1=wlan + KEYBOARD_KEY_900f2=switchvideomode + KEYBOARD_KEY_900f3=suspend + KEYBOARD_KEY_900f4=brightnessup + KEYBOARD_KEY_900f5=brightnessdown + KEYBOARD_KEY_900f8=zoom + +########################################################### +# Inventec +########################################################### + +# Symphony +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnINVENTEC:pnSYMPHONY*6.0/7.0:pvr* + KEYBOARD_KEY_f3=prog2 + KEYBOARD_KEY_f4=prog1 + +########################################################### +# Lenovo +########################################################### + +# thinkpad_acpi driver +evdev:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn* + KEYBOARD_KEY_01=screenlock + KEYBOARD_KEY_02=battery + KEYBOARD_KEY_03=sleep + KEYBOARD_KEY_04=wlan + KEYBOARD_KEY_06=switchvideomode + KEYBOARD_KEY_07=f21 + KEYBOARD_KEY_08=f24 + KEYBOARD_KEY_0b=suspend + KEYBOARD_KEY_0f=brightnessup + KEYBOARD_KEY_10=brightnessdown + KEYBOARD_KEY_11=kbdillumtoggle + KEYBOARD_KEY_13=zoom + KEYBOARD_KEY_14=volumeup + KEYBOARD_KEY_15=volumedown + KEYBOARD_KEY_16=mute + KEYBOARD_KEY_17=prog1 + KEYBOARD_KEY_1a=f20 # Microphone mute button; should be micmute + +# ThinkPad Keyboard with TrackPoint +evdev:input:b0003v17EFp6009* + KEYBOARD_KEY_090012=screenlock # Fn+F2 + KEYBOARD_KEY_090013=battery # Fn+F3 + KEYBOARD_KEY_090014=wlan # Fn+F5 + KEYBOARD_KEY_090016=switchvideomode # Fn+F7 + KEYBOARD_KEY_090017=f21 # Fn+F8 touchpad toggle + KEYBOARD_KEY_090019=suspend # Fn+F12 + KEYBOARD_KEY_09001a=brightnessup # Fn+Home + KEYBOARD_KEY_09001b=brightnessdown # Fn+End + KEYBOARD_KEY_09001d=zoom # Fn+Space + KEYBOARD_KEY_090011=prog1 # ThinkVantage button + KEYBOARD_KEY_090015=camera # Fn+F6 headset/camera VoIP key ?? + KEYBOARD_KEY_090010=f20 # Microphone mute button; should be micmute + +# Lenovo 3000 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*3000*:pvr* + KEYBOARD_KEY_8b=switchvideomode # Fn+F7 video + KEYBOARD_KEY_96=wlan # Fn+F5 wireless + KEYBOARD_KEY_97=sleep # Fn+F4 suspend + KEYBOARD_KEY_98=suspend # Fn+F12 hibernate + KEYBOARD_KEY_b4=prog1 # Lenovo Care + +# "Lenovo Care" Key of the 3000 N200 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO:pn0769AP2:pvr3000N200:* + KEYBOARD_KEY_b4=prog1 + +# lenovo-ideapad +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pnS10-*:pvr* + KEYBOARD_KEY_81=rfkill # does nothing in BIOS + KEYBOARD_KEY_83=display_off # BIOS toggles screen state + KEYBOARD_KEY_b9=brightnessup # does nothing in BIOS + KEYBOARD_KEY_ba=brightnessdown # does nothing in BIOS + KEYBOARD_KEY_f1=camera # BIOS toggles camera power + KEYBOARD_KEY_f2=f21 # touchpad toggle (key alternately emits F2 and F3) + KEYBOARD_KEY_f3=f21 + +# Thinkpad X200_Tablet +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X2*Tablet* + KEYBOARD_KEY_5d=menu + KEYBOARD_KEY_63=fn + KEYBOARD_KEY_66=screenlock + KEYBOARD_KEY_67=cyclewindows # bezel circular arrow + KEYBOARD_KEY_68=setup # bezel setup / menu + KEYBOARD_KEY_6c=direction # rotate screen + +# ThinkPad X6 Tablet +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X6*Tablet* + KEYBOARD_KEY_6c=direction # rotate + KEYBOARD_KEY_68=leftmeta # toolbox + KEYBOARD_KEY_6b=esc # escape + KEYBOARD_KEY_6d=right # right on d-pad + KEYBOARD_KEY_6e=left # left on d-pad + KEYBOARD_KEY_71=up # up on d-pad + KEYBOARD_KEY_6f=down # down on d-pad + KEYBOARD_KEY_69=enter # enter on d-pad + +# ThinkPad X41 Tablet +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnIBM*:pn18666TU:pvr* + KEYBOARD_KEY_6c=direction # rotate + KEYBOARD_KEY_68=leftmeta # toolbox + KEYBOARD_KEY_6b=esc # escape + KEYBOARD_KEY_69=enter # enter on d-pad + +# IdeaPad +evdev:name:Ideapad extra buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn* + KEYBOARD_KEY_42=f23 + KEYBOARD_KEY_43=f22 + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*Y550*:pvr* + KEYBOARD_KEY_95=media + KEYBOARD_KEY_a3=play + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*U300s*:pvr* + KEYBOARD_KEY_f1=f21 + KEYBOARD_KEY_ce=f20 # micmute + +evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr* + KEYBOARD_KEY_a0=!mute + KEYBOARD_KEY_ae=!volumedown + KEYBOARD_KEY_b0=!volumeup + +# V480 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:pvr* + KEYBOARD_KEY_f1=f21 + +# Lenovo Thinkcentre M800z AIO machine +# key_scancode 00 is KEY_MICMUTE +keyboard:name:Microphone Mute Button:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn* + KEYBOARD_KEY_00=f20 + +# enhanced USB keyboard +evdev:input:b0003v04B3p301B* + KEYBOARD_KEY_90001=prog1 # ThinkVantage + KEYBOARD_KEY_90002=screenlock + KEYBOARD_KEY_90003=file + KEYBOARD_KEY_90004=wordprocessor + KEYBOARD_KEY_90005=spreadsheet + KEYBOARD_KEY_90006=calc + KEYBOARD_KEY_90007=mail + KEYBOARD_KEY_90008=www + + +########################################################### +# Logitech +########################################################### + +# iTouch +evdev:input:b0003v046DpC308* + KEYBOARD_KEY_90001=shop # Shopping + KEYBOARD_KEY_90002=config # iTouch + KEYBOARD_KEY_90003=finance # Finance + KEYBOARD_KEY_90004=prog1 # My Sites + KEYBOARD_KEY_90005=prog2 # Community + KEYBOARD_KEY_C0183=media # Media + +# Cordless Desktop S510 +evdev:input:b0003v046DpC50C* + KEYBOARD_KEY_d4=zoomin + KEYBOARD_KEY_cc=zoomout + +# Wave cordless +evdev:input:b0003v046DpC317* + KEYBOARD_KEY_9001c=scale # expo + KEYBOARD_KEY_9001f=zoomout + KEYBOARD_KEY_90020=zoomin + KEYBOARD_KEY_9003d=prog1 # gadget + KEYBOARD_KEY_90005=camera + KEYBOARD_KEY_90018=media + KEYBOARD_KEY_90041=wordprocessor + KEYBOARD_KEY_90042=spreadsheet + KEYBOARD_KEY_90043=calendar + KEYBOARD_KEY_90044=prog2 # fn+f4 (program a) + KEYBOARD_KEY_90045=prog3 # fn+f5 (program b) + KEYBOARD_KEY_90046=prog4 # fn+f6 (program c) + KEYBOARD_KEY_90048=messenger # fn+f8 (msn messenger) + KEYBOARD_KEY_9002d=search # fn+f10 (search www) + KEYBOARD_KEY_9004b=find # fn+f11 (search pc) + KEYBOARD_KEY_9004c=ejectclosecd + +# Wave cordless +evdev:input:b0003v046DpC517* + KEYBOARD_KEY_c101f=zoomout + KEYBOARD_KEY_c1020=zoomin + KEYBOARD_KEY_c1005=camera + KEYBOARD_KEY_c0183=media + KEYBOARD_KEY_c1041=wordprocessor + KEYBOARD_KEY_c1042=spreadsheet + KEYBOARD_KEY_c1043=calendar + KEYBOARD_KEY_c1044=prog2 # fn+f4 (program a) + KEYBOARD_KEY_c1045=prog3 # fn+f5 (program b) + KEYBOARD_KEY_c1046=prog4 # fn+f6 (program c) + KEYBOARD_KEY_c1048=messenger # fn+f8 (msn messenger) + KEYBOARD_KEY_c104a=find # fn+f10 (search www) + KEYBOARD_KEY_c104c=ejectclosecd + +# Cordless Wave Pro +evdev:input:b0003v046DpC52[9B]* + KEYBOARD_KEY_0c01b6=camera + KEYBOARD_KEY_0c0183=media + KEYBOARD_KEY_0c0184=wordprocessor + KEYBOARD_KEY_0c0186=spreadsheet + KEYBOARD_KEY_0c018e=calendar + KEYBOARD_KEY_0c0223=homepage + KEYBOARD_KEY_0c01bc=messenger + KEYBOARD_KEY_0c018a=mail + KEYBOARD_KEY_0c0221=search + KEYBOARD_KEY_0c00b8=ejectcd + KEYBOARD_KEY_0c022d=zoomin + KEYBOARD_KEY_0c022e=zoomout + +# Logitech Presenter R400 +evdev:input:b0003v046DpC52D* + KEYBOARD_KEY_070029=presentation + KEYBOARD_KEY_07003e=presentation + KEYBOARD_KEY_070037=displaytoggle + +# Internet Navigator +evdev:input:b0003v046DpC309* + KEYBOARD_KEY_90001=chat # Messenger/SMS + KEYBOARD_KEY_90002=camera # webcam + KEYBOARD_KEY_90003=prog1 # iTouch + KEYBOARD_KEY_90004=shop # Shopping + KEYBOARD_KEY_C0201=new # New (F1) + KEYBOARD_KEY_C0289=reply # Reply mail (F2) + KEYBOARD_KEY_C028B=forwardmail # Forward mail (F3) + KEYBOARD_KEY_C028C=send # Send (F4) + KEYBOARD_KEY_C021A=undo # Undo (F5). + KEYBOARD_KEY_C0279=redo # Redo (F6). + KEYBOARD_KEY_C0208=print # Print (F7) + KEYBOARD_KEY_C0207=save # Save (F8) + KEYBOARD_KEY_C0194=file # My Computer (F9) + KEYBOARD_KEY_C01A7=documents # My Documents (F10) + KEYBOARD_KEY_C01B6=images # My Pictures (F11) ?? + KEYBOARD_KEY_C01B7=sound # My Music (F12) ?? + + +########################################################### +# Maxdata +########################################################### + +# Pro 7000 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMAXDATA:pnPro*7000*:pvr* + KEYBOARD_KEY_97=prog2 + KEYBOARD_KEY_9f=prog1 + KEYBOARD_KEY_a0=mute # Fn+F5 + KEYBOARD_KEY_82=www + KEYBOARD_KEY_ec=email + KEYBOARD_KEY_ae=volumedown # Fn+Down + KEYBOARD_KEY_b0=volumeup # Fn+Up + KEYBOARD_KEY_df=suspend # Fn+F2 + KEYBOARD_KEY_f5=help + +########################################################### +# Medion +########################################################### + +# FID2060 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMEDION*:pn*FID2060*:pvr* + KEYBOARD_KEY_6b=channeldown # Thottle Down + KEYBOARD_KEY_6d=channelup # Thottle Up + +# NB-A555 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMEDIONNB:pnA555*:pvr* + KEYBOARD_KEY_63=www # N button + KEYBOARD_KEY_66=prog1 # link 1 button + KEYBOARD_KEY_67=email # envelope button + KEYBOARD_KEY_69=prog2 # link 2 button + +########################################################### +# Microsoft +########################################################### + +# Microsoft Natural Ergonomic Keyboard 4000 +evdev:input:b0003v045Ep00DB* + KEYBOARD_KEY_c022d=zoomin + KEYBOARD_KEY_c022e=zoomout + +########################################################### +# Micro Star +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pn* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn* + KEYBOARD_KEY_a0=mute # Fn+F9 + KEYBOARD_KEY_ae=volumedown # Fn+F7 + KEYBOARD_KEY_b0=volumeup # Fn+F8 + KEYBOARD_KEY_b2=www # e button + KEYBOARD_KEY_df=sleep # Fn+F12 + KEYBOARD_KEY_e2=bluetooth # satellite dish2 + KEYBOARD_KEY_e4=f21 # Fn+F3 Touchpad disable + KEYBOARD_KEY_ec=email # envelope button + KEYBOARD_KEY_ee=camera # Fn+F6 camera disable + KEYBOARD_KEY_f6=wlan # satellite dish1 + KEYBOARD_KEY_f7=brightnessdown # Fn+F4 + KEYBOARD_KEY_f8=brightnessup # Fn+F5 + KEYBOARD_KEY_f9=search + +# +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pnGE60*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pnGE70*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-Star*:pn*:pvr* + KEYBOARD_KEY_c2=ejectcd + +# some MSI models generate ACPI/input events on the LNXVIDEO input devices, +# plus some extra synthesized ones on atkbd as an echo of actually changing the +# brightness; so ignore those atkbd ones, to avoid loops +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pn*U-100*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pn*U100*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pn*N033:* + KEYBOARD_KEY_f7=reserved + KEYBOARD_KEY_f8=reserved + +# MSI Wind U90/U100 generates separate touchpad on/off keycodes so ignore touchpad toggle keycode +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMICRO-STAR*:pnU90/U100:* + KEYBOARD_KEY_e4=reserved + +########################################################### +# MSI +########################################################### + +evdev:name:MSI Laptop hotkeys:dmi:bvn*:bvr*:bd*:svn*:pnM[iI][cC][rR][oO]-S[tT][aA][rR]*:pvr* + KEYBOARD_KEY_0213=f22 + KEYBOARD_KEY_0214=f23 + +########################################################### +# OLPC +########################################################### + +# XO +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnOLPC:pnXO:* + KEYBOARD_KEY_59=fn + KEYBOARD_KEY_81=fn_esc + KEYBOARD_KEY_f9=camera + KEYBOARD_KEY_f8=sound # Fn+CAMERA = Mic + KEYBOARD_KEY_43=brightnessdown + KEYBOARD_KEY_44=brightnessup + KEYBOARD_KEY_57=volumedown + KEYBOARD_KEY_58=volumeup + KEYBOARD_KEY_bb=f1 + KEYBOARD_KEY_bc=f2 + KEYBOARD_KEY_bd=f3 + KEYBOARD_KEY_be=f4 + KEYBOARD_KEY_bf=f5 + KEYBOARD_KEY_c0=f6 + KEYBOARD_KEY_c1=f7 + KEYBOARD_KEY_c2=f8 + KEYBOARD_KEY_c3=f9 + KEYBOARD_KEY_c4=f10 + KEYBOARD_KEY_c7=f11 + KEYBOARD_KEY_d8=f12 + KEYBOARD_KEY_f7=f13 + KEYBOARD_KEY_f6=f14 + KEYBOARD_KEY_f5=f15 + KEYBOARD_KEY_f4=f16 + KEYBOARD_KEY_f3=f17 + KEYBOARD_KEY_f2=f18 + KEYBOARD_KEY_f1=f19 + KEYBOARD_KEY_f0=f20 # micmute + KEYBOARD_KEY_ef=f21 + KEYBOARD_KEY_ee=chat + KEYBOARD_KEY_e4=chat + KEYBOARD_KEY_dd=menu # Frame + KEYBOARD_KEY_da=prog1 # Fn+Frame + KEYBOARD_KEY_d3=delete + KEYBOARD_KEY_d2=insert + KEYBOARD_KEY_c9=pageup + KEYBOARD_KEY_d1=pagedown + KEYBOARD_KEY_c7=home + KEYBOARD_KEY_cF=end + KEYBOARD_KEY_73=hp + KEYBOARD_KEY_7e=hp + KEYBOARD_KEY_db=leftmeta # left grab + KEYBOARD_KEY_dc=rightmeta # right grab + KEYBOARD_KEY_85=rightmeta # Right grab releases on a different scancode + KEYBOARD_KEY_d6=kbdillumtoggle # Fn+Space + KEYBOARD_KEY_69=switchvideomode # Brightness key + KEYBOARD_KEY_65=kp8 # up + KEYBOARD_KEY_66=kp2 # down + KEYBOARD_KEY_67=kp4 # left + KEYBOARD_KEY_68=kp6 # right + KEYBOARD_KEY_e5=kp9 # pgup + KEYBOARD_KEY_e6=kp3 # pgdn + KEYBOARD_KEY_e7=kp7 # home + KEYBOARD_KEY_e8=kp1 # end + +########################################################### +# Onkyo +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnONKYO*CORPORATION:pnONKYOPC:* + KEYBOARD_KEY_a0=mute # Fn+D + KEYBOARD_KEY_ae=volumedown # Fn+F + KEYBOARD_KEY_b0=volumeup # Fn+G + KEYBOARD_KEY_df=sleep # Fn+W + KEYBOARD_KEY_e0=bluetooth # Fn+H + KEYBOARD_KEY_e2=cyclewindows # Fn+Esc + KEYBOARD_KEY_ee=battery # Fn+Q + KEYBOARD_KEY_f0=media # Fn+R + KEYBOARD_KEY_f5=switchvideomode # Fn+E + KEYBOARD_KEY_f6=camera # Fn+T + KEYBOARD_KEY_f7=f21 # Fn+Y (touchpad toggle) + KEYBOARD_KEY_f8=brightnessup # Fn+S + KEYBOARD_KEY_f9=brightnessdown # Fn+A + KEYBOARD_KEY_fb=wlan # Fn+J + +########################################################### +# OQO +########################################################### + +# Model 2 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnOQO*Inc.*:pnOQO*Model*2*:pvr* + KEYBOARD_KEY_8e=wlan + KEYBOARD_KEY_f0=switchvideomode + KEYBOARD_KEY_f1=mute + KEYBOARD_KEY_f2=volumedown + KEYBOARD_KEY_f3=volumeup + +########################################################### +# Plantronics +########################################################### + +# Plantronics .Audio 626 DSP +evdev:input:b0003v047FpC006* + KEYBOARD_KEY_b002f=f20 # Microphone mute button; should be micmute + +########################################################### +# Quanta +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*:rvnQuanta:rn30B7:rvr65.2B:* + KEYBOARD_KEY_88=media # "quick play + +########################################################### +# Samsung +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn* + KEYBOARD_KEY_74=prog1 # User key + KEYBOARD_KEY_75=www + KEYBOARD_KEY_78=mail + KEYBOARD_KEY_82=!switchvideomode # Fn+F4 CRT/LCD (high keycode: "displaytoggle") + KEYBOARD_KEY_83=!battery # Fn+F2 + KEYBOARD_KEY_84=!prog1 # Fn+F5 backlight on/off + KEYBOARD_KEY_86=!wlan # Fn+F9 + KEYBOARD_KEY_88=!brightnessup # Fn+Up + KEYBOARD_KEY_89=!brightnessdown # Fn+Down + KEYBOARD_KEY_b1=!prog2 # Fn+F7 run Samsung Magic Doctor (keypressed event is generated twice) + KEYBOARD_KEY_b3=!prog3 # Fn+F8 switch power mode (battery/dynamic/performance) + KEYBOARD_KEY_b4=!wlan # Fn+F9 (X60P) + KEYBOARD_KEY_c5=!prog3 # Fn+F8 switch power mode (battery/dynamic/performance) + KEYBOARD_KEY_d5=!wlan # Fn+F12 wlan/airplane switch + KEYBOARD_KEY_f7=!f22 # Fn+F10 Touchpad on + KEYBOARD_KEY_f9=!f23 # Fn+F10 Touchpad off + +# Series 3 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*300E[457]*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*200E[45]*:pvr* + KEYBOARD_KEY_ce=! # Fn+F1 launch control setting + +# Series 5 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*530U*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_a8=! # Fn Lock - Function lock on + KEYBOARD_KEY_a9=! # Fn Lock - Function lock off + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*550P*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_a8=! # Fn Lock - Function lock on + KEYBOARD_KEY_a9=! # Fn Lock - Function lock off + +# Series 7 / 9 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*350V*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*670Z*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*700Z*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*700G*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*900X[34]*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*940X3G*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_a0=!mute # Fn+F6 mute + KEYBOARD_KEY_ae=!volumedown # Fn+F7 + KEYBOARD_KEY_b0=!volumeup # Fn+F8 + KEYBOARD_KEY_97=!kbdillumdown # Fn+F9 keyboard backlight down + KEYBOARD_KEY_96=!kbdillumup # Fn+F10 keyboard backlight up + KEYBOARD_KEY_b3=!prog3 # Fn+F11 fan/cooling mode changer + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*900X[34][AB]*:pvr* + KEYBOARD_KEY_ce=! # Fn+F8 keyboard backlight up + KEYBOARD_KEY_8d=! # Fn+F7 keyboard backlight down + KEYBOARD_KEY_96=! # Fn+F1 performance mode (?) + KEYBOARD_KEY_97=! # Fn+F12 Wi-Fi toggle + KEYBOARD_KEY_d5=! # Fn+F6 battery life extender + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*90X3A*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_8d=!prog3 # Fn+F6 performance mode + KEYBOARD_KEY_97=!kbdillumdown # Fn+F7 keyboard backlight down + KEYBOARD_KEY_96=!kbdillumup # Fn+F8 keyboard backlight up + KEYBOARD_KEY_d5=!wlan # Fn+F12 Wi-Fi toggle + +# Series 7 Ultra +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*7[34]0U3E*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_97=!kbdillumdown # Fn+F9 keyboard backlight down + KEYBOARD_KEY_96=!kbdillumup # Fn+F10 keyboard backlight up + KEYBOARD_KEY_b3=!prog3 # Fn+F11 fan/cooling mode changer + KEYBOARD_KEY_d5=!wlan # Fn+F12 wlan/airplane switch + +# ATIV Book 6 / 8 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*[68][78]0Z*:pvr* + KEYBOARD_KEY_ce=!prog1 # Fn+F1 launch settings + KEYBOARD_KEY_96=!kbdillumup # Fn+F10 keyboard backlight up + KEYBOARD_KEY_97=!kbdillumdown # Fn+F9 keyboard backlight down + +# SQ1US +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pnSQ1US:pvr* + KEYBOARD_KEY_d4=menu + KEYBOARD_KEY_d8=f1 + KEYBOARD_KEY_d9=f10 + KEYBOARD_KEY_d6=f3 + KEYBOARD_KEY_d7=f9 + KEYBOARD_KEY_e4=f5 + KEYBOARD_KEY_ee=f11 + +# SX20S +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*SX20S*:pvr* + KEYBOARD_KEY_74=mute + KEYBOARD_KEY_75=mute + KEYBOARD_KEY_77=f22 # Touchpad on + KEYBOARD_KEY_79=f23 # Touchpad off + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svn[sS][aA][mM][sS][uU][nN][gG]*:pn*700T*:pvr* + KEYBOARD_KEY_ad=leftmeta + +########################################################### +# SONY +########################################################### + +# sony-laptop driver +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn* + KEYBOARD_KEY_06=mute # Fn+F2 + KEYBOARD_KEY_07=volumedown # Fn+F3 + KEYBOARD_KEY_08=volumeup # Fn+F4 + KEYBOARD_KEY_09=brightnessdown # Fn+F5 + KEYBOARD_KEY_0a=brightnessup # Fn+F6 + KEYBOARD_KEY_0b=switchvideomode # Fn+F7 + KEYBOARD_KEY_0e=zoom # Fn+F10 + KEYBOARD_KEY_10=suspend # Fn+F12 + +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-C1*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-K25*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-F[1-6]*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-FX*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-FRV*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-GR*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-TR*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-NV*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*PCG-Z*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pn*VGN-S360*:pvr* + KEYBOARD_KEY_06=battery + KEYBOARD_KEY_07=mute + +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pnVGN-AR71*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pnVGN-FW*:pvr* +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pnVGN-Z21*:pvr* + KEYBOARD_KEY_00=brightnessdown # Fn+F5 + KEYBOARD_KEY_10=brightnessup # Fn+F6 + KEYBOARD_KEY_11=switchvideomode # Fn+F7 + KEYBOARD_KEY_12=zoomout + KEYBOARD_KEY_14=zoomin + KEYBOARD_KEY_15=suspend # Fn+F12 + KEYBOARD_KEY_17=prog1 + KEYBOARD_KEY_20=media + +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pnVGN-FW250*:pvr* + KEYBOARD_KEY_10=suspend # Fn+F12 + +evdev:name:Sony Vaio Keys:dmi:bvn*:bvr*:bd*:svnSony*:pnVPC*:pvr* + KEYBOARD_KEY_05=f21 # Fn+F1 -> KEY_F21 (The actual touchpad toggle) + KEYBOARD_KEY_0d=zoomout # Fn+F9 + KEYBOARD_KEY_0e=zoomin # Fn+F10 + +########################################################### +# Toshiba +########################################################### + +# Satellite A100 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSATELLITE*A100:pvr* + KEYBOARD_KEY_a4=stopcd + KEYBOARD_KEY_b2=www + +# Satellite A110 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSatellite*A110:pvr* + KEYBOARD_KEY_92=stop + KEYBOARD_KEY_93=www + KEYBOARD_KEY_94=media + KEYBOARD_KEY_9e=f22 # Touchpad on + KEYBOARD_KEY_9f=f23 # Touchpad off + KEYBOARD_KEY_b9=nextsong + KEYBOARD_KEY_d9=brightnessup + KEYBOARD_KEY_ee=screenlock + KEYBOARD_KEY_f4=previoussong + KEYBOARD_KEY_f7=playpause + +# Satellite M30X +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSatellite*M30X:pvr* + KEYBOARD_KEY_ef=brightnessdown + KEYBOARD_KEY_d9=brightnessup + KEYBOARD_KEY_ee=screenlock + KEYBOARD_KEY_93=media + KEYBOARD_KEY_9e=f22 # touchpad enable + KEYBOARD_KEY_9f=f23 # touchpad disable + +# Satellite P75-A +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSatellite*P75-A:pvr* + KEYBOARD_KEY_ef=brightnessdown + KEYBOARD_KEY_ee=brightnessup + KEYBOARD_KEY_a9=switchvideomode # switch display outputs + KEYBOARD_KEY_d4=wlan # RF Switch Off + +# Satellite U940 +evdev:name:Toshiba*input*device:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSATELLITEU940:pvr* + KEYBOARD_KEY_13c=brightnessdown + KEYBOARD_KEY_13d=brightnessup + KEYBOARD_KEY_13e=switchvideomode + KEYBOARD_KEY_13f=f21 # Touchpad toggle + +# Satellite P75-A7200 +evdev:name:Toshiba*input*device:dmi:bvn*:bvr*:bd*:svnTOSHIBA*:pnSatellite*P75-A:pvr* + KEYBOARD_KEY_13c=brightnessdown + KEYBOARD_KEY_13d=brightnessup + KEYBOARD_KEY_13e=switchvideomode + KEYBOARD_KEY_13f=f21 # Touchpad toggle + KEYBOARD_KEY_9e=wlan + +########################################################### +# VIA +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnVIA:pnK8N800:pvr* + KEYBOARD_KEY_81=prog1 + +########################################################### +# Zepto +########################################################### + +# Znote +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnZepto:pnZnote:* + KEYBOARD_KEY_93=switchvideomode # Fn+F3 Toggle Video Output + KEYBOARD_KEY_95=brightnessdown # Fn+F4 Brightness Down + KEYBOARD_KEY_91=brightnessup # Fn+F5 Brightness Up + KEYBOARD_KEY_a5=f23 # Fn+F6 Disable Touchpad + KEYBOARD_KEY_a6=f22 # Fn+F6 Enable Touchpad + KEYBOARD_KEY_a7=bluetooth # Fn+F10 Enable Bluetooth + KEYBOARD_KEY_a9=bluetooth # Fn+F10 Disable Bluetooth + KEYBOARD_KEY_f1=wlan # RF Switch Off + KEYBOARD_KEY_f2=wlan # RF Switch On + KEYBOARD_KEY_f4=prog1 # P1 Button + KEYBOARD_KEY_f3=prog2 # P2 Button + KEYBOARD_KEY_a0=! # mute + KEYBOARD_KEY_ae=! # volume down + KEYBOARD_KEY_b0=! # volume up + +# Znote 6615WD +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnZepto:pnZnote*6615WD:* + KEYBOARD_KEY_a0=! # mute + KEYBOARD_KEY_ae=! # volume down + KEYBOARD_KEY_b0=! # volume up + +########################################################### +# Other +########################################################### + +# Common Volume Keys +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFUJITSU*SIEMENS:pnAMILO*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnFOXCONN:pnQBOOK:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMTC:pn*:pvrA0:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMio*Technology:pnN890:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnPEGATRON*CORP.:pnSpring*Peak:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA:pnSatellite*[uU][35]0[05]*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA:pnSATELLITE*[uU][35]0[05]*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA:pnSatellite*Pro*[uU]300*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnTOSHIBA:pnEQUIUM [uU][35]0[05]*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnViooo*Corporation:pnPT17:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHANNspree:pnSN10E100:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnGIGABYTE:pni1520M:* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnBenQ:pn*nScreen*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnBenQ:pnJoybook*Lite*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnDIXONSP:pnDIXON*:pvr* + KEYBOARD_KEY_a0=! # mute + KEYBOARD_KEY_ae=! # volume down + KEYBOARD_KEY_b0=! # volume up diff --git a/src/grp-udev/hwdb/70-mouse.hwdb b/src/grp-udev/hwdb/70-mouse.hwdb new file mode 100644 index 0000000000..a5b39dc41e --- /dev/null +++ b/src/grp-udev/hwdb/70-mouse.hwdb @@ -0,0 +1,485 @@ +# This file is part of systemd. +# +# Database for the DPI setting of mice, trackballs, other pointer devices that +# cannot be queried directly. +# +# The lookup keys are composed in: +# 70-mouse.rules +# +# Note: The format of the "mouse:" prefix match key is a +# contract between the rules file and the hardware data, it might +# change in later revisions to support more or better matches, it +# is not necessarily expected to be a stable ABI. +# +# Match string format: +# mouse::vp:name:: +# +# Supported subsystems: usb, bluetooth +# vid/pid as 4-digit hex lowercase vendor/product +# +# if vid/pid is unavailable, use +# mouse:*:name:: +# if name is unavailable, use +# mouse::vp:* +# +# For example, the following 5 matches all match the same mouse: +# mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse: +# mouse:usb:*:name:Lenovo Optical USB Mouse: +# mouse:usb:v17efp6019:* +# mouse:*:name:Lenovo Optical USB Mouse: +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/71-mouse-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the mouse in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX. +# +# Allowed properties are: +# MOUSE_DPI +# MOUSE_WHEEL_CLICK_ANGLE +# +######################################### +# MOUSE_DPI # +######################################### +# +# DPI settings are specified as +# MOUSE_DPI=[@] +# +# Where is the resolution in dots per inch, and the +# sampling frequency in Hz (optional). If a device supports dynamic +# frequency scaling, the maximum frequency should be used. For devices +# supporting multiple fixed frequencies, see below. +# +# The value of MOUSE_DPI is: +# - a single integer for single-resolution mice, e.g. +# MOUSE_DPI=800 +# or, if the frequency is known: +# MOUSE_DPI=800@120 +# - a space-separated list of resolutions for multi-resolution mice. +# The default resolution must be prefixed by an asterisk, the resolutions +# in the database must be as shipped by the manufacturer. e.g. +# MOUSE_DPI=400 *800 2000 +# +# The order of resolutions is as configured by the HW manufacturer or in +# ascending order, whichever appropriate. +# +# The frequency must be given to either none or all resolutions. If the +# device supports multiple fixed frequencies, the order of items is +# MOUSE_DPI=r1@f1 r2@f1 r3@f1 r1@f2 r2@f2 r3@f2 +# +# If the default manufacturer-set resolution is unclear, a resolution of +# 800 or 1000 should be set as default, if available. If neither is +# available, choose the "middle" resolution value of those available. +# +# The list may contain a single item which must be marked with an +# asterisk. +# +# Local changes to the a non-default resolution of the mouse (e.g. through +# third-party software) must not be entered into this file, use a local +# hwdb instead. +# +######################################### +# MOUSE_WHEEL_CLICK_ANGLE # +######################################### +# +# The angle in degrees per mouse wheel 'click', specified as +# MOUSE_WHEEL_CLICK_ANGLE= +# +# Most mice have a 15 degree click stop (24 clicks per full rotation). +# + +# +# Sort by brand, type (usb, bluetooth), DPI, frequency. +# For mice with switchable resolution, sort by the starred entry. + +########################################## +# Apple +########################################## + +# Apple MagicMouse +# Note: this device changes name once connected to a mac, the name ends up +# as $username`s mouse +mouse:bluetooth:v05acp030d:name:*: + MOUSE_DPI=1300@1000 + +########################################## +# Chicony +########################################## + +# Chicony 2.4G Multimedia Wireless Kit MG-0919 +mouse:usb:v04f2p0963:name:Chicony 2.4G Multimedia Wireless Kit: + MOUSE_DPI=1000@142 + +########################################## +# Dell +########################################## + +# Dell MUAR DEL7 +mouse:usb:v413cp3012:name:Dell Dell USB Optical Mouse: + MOUSE_DPI=400@166 + +# Dell USB Laser Mouse +mouse:usb:v046dpc063:name:DELL DELL USB Laser Mouse: + MOUSE_DPI=1000@125 + +########################################## +# Dynex +######################################### + +# Dynex Wired Optical Mouse (DX-WMSE2) +mouse:usb:v0461p4d46:name:USB Optical Mouse: + MOUSE_DPI=1000@125 + +########################################## +# Fujitsu Siemens +########################################## + +mouse:usb:v0461p4d16:name:USB Optical Mouse: + MOUSE_DPI=500@125 + +########################################## +# HandShoe Mouse +########################################## + +# HandShoe Mouse +mouse:usb:v192fp0916:name:USB Optical Mouse: + MOUSE_DPI=1000@128 + +########################################## +# HoverStop +########################################## + +# Hoverstop active ergonomic mouse +mouse:usb:v088dp1234:name:HoverStop NL Hoverstop active ergonomic mouse: + MOUSE_DPI=400@129 + +########################################## +# HP +########################################## + +# HP USB 1000dpi Laser Mouse +mouse:usb:v0458p0133:name:Mouse Laser Mouse: + MOUSE_DPI=1000@125 + MOUSE_WHEEL_CLICK_ANGLE=15 + +# HP X1000 +mouse:usb:v093ap2510:name:PixArt USB Optical Mouse: +mouse:usb:v093ap2510:name:PIXART USB OPTICAL MOUSE: + MOUSE_DPI=1000@125 + +########################################## +# Lenovo +########################################## + +# Lenovo Optical USB Mouse +mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse: + MOUSE_DPI=1000@125 + +# Lenovo M-U0025-O +mouse:usb:v17efp6019:name:Logitech Lenovo USB Optical Mouse: + MOUSE_DPI=1000@166 + +# ThinkPad USB Laser Mouse +mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse: + MOUSE_DPI=1200@125 + +# Lenovo Precision USB Mouse +mouse:usb:v17efp6050:name:Lenovo Precision USB Mouse: + MOUSE_DPI=1200@127 + +# Lenovo MOBGUL +mouse:usb:v17efp601d:name:Primax Lenovo Laser Mouse: +# Lenovo MOBGULA +mouse:usb:v17efp6045:name:Lenovo USB Laser Mouse: + MOUSE_DPI=1600@125 + + +########################################## +# Logitech +########################################## + +# Note: devices using the Logitech Unifying receiver will need two entries, +# one for pre 3.19 with the wireless PID in the name, one for 3.19 with the +# model name. The usb vid/pid is the same for all those devices. +# Until 3.19 is available, this list just has the Wireless PID entry. + +# Logitech M-BJ58 Optical Mouse +mouse:usb:v046dpc00e:name:Logitech USB-PS/2 Optical Mouse: +# Logitech Mini Optical Mouse +mouse:usb:v046dpc016:name:Logitech Optical USB Mouse: +# Logitech MX310 Optical Mouse +mouse:usb:v046dpc01b:name:Logitech USB-PS/2 Optical Mouse: +# Logitech USB-PS/2 M-BT58 +mouse:usb:v046dpc03e:name:Logitech USB-PS/2 Optical Mouse: +# Logitech TrackMan Marble Wheel USB +mouse:usb:v046dpc401:name:Logitech USB-PS/2 Trackball: + MOUSE_DPI=400@125 + +# Lenovo USB mouse model MO28UOL +mouse:usb:v04b3p310c:name:USB Optical Mouse: + MOUSE_DPI=400@142 + +# Logitech M570 trackball +mouse:usb:v046dp1028:name:Logitech M570: + MOUSE_DPI=540@167 + +# Logitech USB-PS/2 M-BZ96C +mouse:usb:v046dpc045:name:Logitech USB-PS/2 Optical Mouse: + MOUSE_DPI=600@125 + +# Logitech Wireless Mouse M325 +mouse:usb:v046dp400a:name:Logitech M325: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:400a: + MOUSE_DPI=600@166 + MOUSE_WHEEL_CLICK_ANGLE=20 + +# Logitech MX400 Performance Laser Mouse +mouse:usb:v046dpc043:name:Logitech USB-PS/2 Optical Mouse: +# Logitech MX1000 Laser Cordless Mouse +mouse:usb:v046dpc50e:name:Logitech USB RECEIVER: +# Logitech Cordless Click! Plus +mouse:usb:v046dpc50e:name:Logitech USB Receiver: +# Logitech, Inc. RX 300 Optical Mouse +mouse:usb:v046dpc040:name:Logitech USB-PS/2 Optical Mouse: + MOUSE_DPI=800@125 + +# Logitech MX 518 +mouse:usb:v046dpc01e:name:Logitech USB-PS/2 Optical Mouse: + MOUSE_DPI=400@125 *800@125 1600@125 + +# Logitech, Inc. RX 250 Optical Mouse +mouse:usb:v046dpc050:name:Logitech USB-PS/2 Optical Mouse: + MOUSE_DPI=1000@142 + +# Logitech Wireless Mouse M185 +mouse:usb:v046dp4008:name:Logitech M185: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4008: +# Logitech Wireless Mouse M510 +mouse:usb:v046dp1025:name:Logitech M510: +# Logitech M705 (marathon mouse) +mouse:usb:v046dp101b:name:Logitech M705: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b: + MOUSE_DPI=800@166 + +# Logitech MX Revolution +mouse:usb:v046dpc51a:name:Logitech USB Receiver: + MOUSE_DPI=800@200 + +# Logitech G5 Laser Mouse +mouse:usb:v046dpc049:name:Logitech USB Gaming Mouse: +# Logitech G500s Laser Gaming Mouse +mouse:usb:v046dpc24e:name:Logitech G500s Laser Gaming Mouse: + MOUSE_DPI=400@500 *800@500 2000@500 + + # Logitech G9 +mouse:usb:v046dpc048:name:Logitech G9 Laser Mouse: + MOUSE_DPI=400@1000 800@1000 *1600@1000 + +# Logitech G9x [Call of Duty MW3 Edition] +mouse:usb:v046dpc249:name:Logitech G9x Laser Mouse: + MOUSE_DPI=400@1000 800@1000 *1600@1000 3200@1000 + +# Logitech G400 (Wired) +mouse:usb:v046dpc245:name:Logitech Gaming Mouse G400: + MOUSE_DPI=400@1000 *800@1000 1800@1000 3600@1000 + +# Logitech G400s (Wired) +mouse:usb:v046dpc24c:name:Logitech G400s Optical Gaming Mouse: + MOUSE_DPI=400@1000 *800@1000 2000@1000 4000@1000 + +# Logitech G402 Hyperion Fury +mouse:usb:v046dpc07e:name:Logitech Gaming Mouse G402: + MOUSE_DPI=400@1000 *800@1000 1600@1000 3200@1000 + +# Logitech B605 Wireless Mouse (also M505) +mouse:usb:v046dp101d:name:Logitech B605: +mouse:usb:v046dp101d:name:Logitech M505: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101d: + MOUSE_DPI=900@166 + +# Logitech Cordless Desktop Wave Mouse +mouse:usb:v046dpc517:name:Logitech USB Receiver: + MOUSE_DPI=950@125 + +# Logitech RX1000 Laser Mouse +mouse:usb:v046dpc046:name:Logitech USB Optical Mouse: +# Logitech M100 Optical Mouse +mouse:usb:v046dpc05a:name:Logitech USB Optical Mouse: +# Logitech USB Laser Mouse M-U0011-O rebranded as "terra Laser" +mouse:usb:v046dpc065:name:Logitech USB Laser Mouse: +# Logitech USB Laser Mouse M-U0007 [M500] +mouse:usb:v046dpc069:name:Logitech USB Laser Mouse: +# Logitech V500 Cordless Notebook Mouse +mouse:usb:v046dpc510:name:Logitech USB Receiver: +# Logitech M560 Wireless Mouse +mouse:usb:v046dp402d:name:Logitech M560: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:402d: + MOUSE_DPI=1000@125 + +# Logitech V220 Cordless Optical Mouse +mouse:usb:v046dpc51b:name:Logitech USB Receiver: +# Logitech Performance MX +mouse:usb:v046dp101a:name:Logitech Performance MX: +# Logitech MX Master +mouse:usb:v046dp4041:name:Logitech MX Master: + MOUSE_DPI=1000@166 + +# Logitech MK260 Wireless Combo Receiver aka M-R0011 +mouse:usb:v046dpc52e:name:Logitech USB Receiver: + MOUSE_DPI=1000@200 + +# Logitech G700 Laser Mouse (Wired) +mouse:usb:v046dpc06b:name:Logitech G700 Laser Mouse: +# Logitech G700 Laser Mouse (Wireless) +mouse:usb:v046dpc531:name:Logitech USB Receiver: + MOUSE_DPI=*1000@500 3800@500 500@1000 1500@1000 2000@1000 + +# Logitech Wireless Mouse M310 +mouse:usb:v046dp1024:name:Logitech M310: + MOUSE_DPI=1100@168 + +# Logitech USB Laser Mouse M-UAS144 [LS1 Laser Mouse] +mouse:usb:v046dpc062:name:Logitech USB Laser Mouse: + MOUSE_DPI=1200@125 + +# Logitech T620 (or, the soap) +mouse:usb:v046dp4027:name:Logitech T620: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4027: + MOUSE_DPI=1200@250 + +# Logitech LX8 Cordless Laser Mouse +mouse:usb:v046dpc51b:name:Logitech USB Receiver: + MOUSE_DPI=1300@125 + MOUSE_WHEEL_CLICK_ANGLE=15 + +# Logitech ZoneTouch Mouse T400 +mouse:usb:v046dp4026:name:Logitech T400: +mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4026: + MOUSE_DPI=1300@166 + +# Logitech G500 Mouse +mouse:usb:v046dpc068:name:Logitech G500: + MOUSE_DPI=*1600@500 2600@500 3600@500 + +# Logitech MX1000 Laser Cordless Mouse +mouse:bluetooth:v046dpb003:name:Logitech MX1000 mouse: + MOUSE_DPI=800@80 + +# Logitech Ultrathin Touch Mouse +mouse:bluetooth:v046dpb00d:name:Ultrathin Touch Mouse: + MOUSE_DPI=1000@1000 + +# ImPS/2 Logitech Wheel Mouse +mouse:ps2:*:name:ImPS/2 Logitech Wheel Mouse: + MOUSE_DPI=400@100 + +# ImExPS/2 Logitech Wheel Mouse +mouse:ps2:*:name:ImExPS/2 Logitech Wheel Mouse: + MOUSE_DPI=400@250 + +########################################## +# Microsoft +########################################## + +mouse:usb:v045ep0040:name:Microsoft Microsoft 3-Button Mouse with IntelliEye(TM): + MOUSE_DPI=400@125 + +# Note: unsure that these work, it's likely that all devices on these +# receivers show up with the same vid/pid/name + +# Microsoft Wireless Mouse 5000 +mouse:usb:v045ep0745:name:Microsoft Microsoft® 2.4GHz Transceiver v6.0: + MOUSE_DPI=800@142 + +# Microsoft Wireless Mobile Mouse 4000 +mouse:usb:v045ep0745:name:Microsoft Microsoft® Nano Transceiver v2.0: +# Microsoft Sculpt Ergonomic Mouse +mouse:usb:v045ep07a5:name:Microsoft Microsoft® 2.4GHz Transceiver v9.0: + MOUSE_DPI=1000@142 + +# Microsoft Arc Touch Mouse USB +mouse:usb:v045ep07b1:name:Microsoft Microsoft® Nano Transceiver v1.0: + MOUSE_DPI=1400@142 + +# Microsoft Wireless Laser Mouse 8000 +mouse:bluetooth:v045ep0702:name:Microsoft Wireless Laser Mouse 8000: + MOUSE_DPI=1000@1000 + +# Microsoft Arc Touch Mouse SE: +mouse:bluetooth:v045ep07f3:name:Arc Touch Mouse SE: + MOUSE_DPI=1000@2000 + +########################################## +# Mionix +########################################## + +#Mionix Avior 7000 +mouse:usb:v22d4p1308:name:Laview Technology Mionix Avior 7000: + MOUSE_DPI=400@1000 *1600@1000 7000@1000 + MOUSE_WHEEL_CLICK_ANGLE=15 + +########################################## +# MODECOM +########################################## + +# MODECOM MC-WM4 Wireless Optical Mouse +mouse:usb:v0e8fp00a7:name:DaKai 2.4G RX: + MOUSE_DPI=*800@126 1600@126 + +########################################## +# Oklick +########################################## + +# Oklick 406S Bluetooth Laser Mouse +mouse:bluetooth:v056ep0061:name:Laser BTmouse: + MOUSE_DPI=*800@333 1600@333 + +########################################## +# Razer +########################################## + +# Razer Abyssus +mouse:usb:v1532p0042:name:Razer Razer Abyssus: + MOUSE_DPI=3500@1000 + +########################################## +# Roccat +########################################## + +# Roccat Lua (ROC-11-310) +mouse:usb:v1e7dp2c2e:name:ROCCAT ROCCAT Lua: + MOUSE_DPI=250@125 500@125 1000@125 1250@125 1500@125 1750@125 2000@125 250@250 500@250 1000@250 1250@250 1500@250 1750@250 2000@250 250@500 500@500 1000@500 1250@500 1500@500 1750@500 2000@500 250@1000 500@1000 *1000@1000 1250@1000 1500@1000 1750@1000 2000@1000 + MOUSE_WHEEL_CLICK_ANGLE=15 + +########################################## +# Sharkoon +########################################## + +# Sharkoon Shark Force Gaming Mouse +mouse:usb:v093ap2521:name:USB OPTICAL MOUSE: + MOUSE_DPI=*1000@125 1600@125 600@125 + +########################################## +# SteelSeries +########################################## + +# SteelSeries Sensei Raw +mouse:usb:v1038p1369:name:SteelSeries Sensei Raw Gaming Mouse: + MOUSE_DPI=1000@1022 + +########################################## +# Trust +########################################## + +# Trust illuminated mouse gxt 152 +mouse:usb:v145fp01ac:name:HID-compliant Mouse Trust Gaming Mouse: + MOUSE_DPI=*800@528 1200@537 1600@536 2400@521 diff --git a/src/grp-udev/hwdb/70-pointingstick.hwdb b/src/grp-udev/hwdb/70-pointingstick.hwdb new file mode 100644 index 0000000000..ec166ead40 --- /dev/null +++ b/src/grp-udev/hwdb/70-pointingstick.hwdb @@ -0,0 +1,119 @@ +# This file is part of systemd. +# +# Pointingstick const-accel configuration, to make different brand / model +# laptop pointingsticks have the same speed / feel, and per model adjustment +# of the IBM TrackPoint driver's sensitivity setting +# +# The lookup keys are composed in: +# 60-evdev.rules +# +# Note: The format of the "evdev:" prefix match key is a contract between the +# rules file and the hardware data, it might change in later revisions to +# support more or better matches, it is not necessarily a stable ABI. +# +# Supported hardware matches are: +# - Generic input devices match: +# evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV +# This matches on the kernel modalias of the input-device, mainly: +# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and +# WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV +# is an arbitrary length input-modalias describing the device capabilities. +# +# - Input driver device name and DMI data match: +# evdev:name::dmi:bvn*:bvr*:bd*:svn:pn* +# is the name device specified by the driver, +# is the firmware-provided string from the kernel DMI modalias. +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/71-pointingstick-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the pointingstick in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX. +# +# Allowed properties are: +# POINTINGSTICK_CONST_ACCEL +# POINTINGSTICK_SENSITIVITY +# +# Entries should be sorted with growing _SENSITIVITY and _CONST_ACCEL. +# +######################################### +# POINTINGSTICK_CONST_ACCEL # +######################################### +# +# Trackpoint const accel settings are specified as +# POINTINGSTICK_CONST_ACCEL= +# +# Where is a floating point number, using a '.' seperator, specifying +# by how much to multiply deltas generated by the pointingstick to get +# normalized deltas. +# +######################################### +# POINTINGSTICK_SENSITIVITY # +######################################### +# +# TPPS/2 IBM TrackPoint driver sensitivity sysfs setting +# POINTINGSTICK_SENSITIVITY= +# +# Where is a number between 0 and 255, note this property +# only applies to TPPS/2 IBM TrackPoint devices, see +# drivers/input/mouse/trackpoint.c in the Linux kernel sources. +# + +# +# Sort by brand, model + +######################################### +# Dell +######################################### + +# Latitude D620 +evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeD620*:pvr* + POINTINGSTICK_CONST_ACCEL=0.5 + +# Latitude E6320 +evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6320*:pvr* + POINTINGSTICK_CONST_ACCEL=2.0 + +# Latitude E6400 +evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6400*:pvr* + POINTINGSTICK_CONST_ACCEL=1.5 + +######################################### +# Lenovo +######################################### + +# Lenovo Thinkpad X230 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230:* +# Lenovo Thinkpad X230 tablet +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:* +# Lenovo Thinkpad X240 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX240:* +# Lenovo Thinkpad T440s +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440s:* +# Lenovo Thinkpad T540p +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT540p:* +# Lenovo Thinkpad T550 / W550s +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* +# Lenovo Thinkpad X1 Carbon 3rd gen +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:* +# Lenovo Thinkpad X1 Carbon 4th gen +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon4th:* + POINTINGSTICK_SENSITIVITY=200 + POINTINGSTICK_CONST_ACCEL=1.0 + +# Lenovo Thinkpad X200s / X201s +# Note these come with 2 revisions of keyboard, with the trackpoints having a +# different sensitivity in the different revisions. 1.25 is a bit slow for the +# least sensitive revision, but it is better to be a bit slow than too fast. +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX20?s:* + POINTINGSTICK_SENSITIVITY=200 + POINTINGSTICK_CONST_ACCEL=1.25 diff --git a/src/grp-udev/hwdb/70-touchpad.hwdb b/src/grp-udev/hwdb/70-touchpad.hwdb new file mode 100644 index 0000000000..11f3f96f04 --- /dev/null +++ b/src/grp-udev/hwdb/70-touchpad.hwdb @@ -0,0 +1,49 @@ +# This file is part of systemd. +# +# Database for touchpad device information that cannot be queried directly. +# +# The lookup keys are composed in: +# 70-touchpad.rules +# +# Note: The format of the "touchpad:" prefix match key is a +# contract between the rules file and the hardware data, it might +# change in later revisions to support more or better matches, it +# is not necessarily expected to be a stable ABI. +# +# Match string format: +# touchpad::vp:name:: +# +# vid/pid as 4-digit hex lowercase vendor/product +# +# To add local entries, create a new file +# /etc/udev/hwdb.d/71-touchpad-local.hwdb +# and add your rules there. To load the new rules execute (as root): +# udevadm hwdb --update +# udevadm trigger /dev/input/eventXX +# where /dev/input/eventXX is the touchpad in question. If in +# doubt, simply use /dev/input/event* to reload all input rules. +# +# If your changes are generally applicable, preferably send them as a pull +# request to +# https://github.com/systemd/systemd +# or create a bug report on https://github.com/systemd/systemd/issues and +# include your new rules, a description of the device, and the output of +# udevadm info /dev/input/eventXX. +# +# Permitted keys: +# Specify if a touchpad is a built-in one or external: +# ID_INPUT_TOUCHPAD_INTEGRATION=internal|external + +touchpad:i8042:* +touchpad:rmi:* + ID_INPUT_TOUCHPAD_INTEGRATION=internal + +touchpad:bluetooth:* +touchpad:usb:* + ID_INPUT_TOUCHPAD_INTEGRATION=external + +########################################################### +# Apple +########################################################### +touchpad:usb:v05ac* + ID_INPUT_TOUCHPAD_INTEGRATION=internal diff --git a/src/grp-udev/hwdb/Makefile b/src/grp-udev/hwdb/Makefile new file mode 100644 index 0000000000..42f6f501f3 --- /dev/null +++ b/src/grp-udev/hwdb/Makefile @@ -0,0 +1,40 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +.PHONY: hwdb-update +hwdb-update: + ( cd $(top_srcdir)/hwdb && \ + wget -O usb.ids 'http://www.linux-usb.org/usb.ids' && \ + wget -O pci.ids 'http://pci-ids.ucw.cz/v2.2/pci.ids' && \ + wget -O ma-large.txt 'http://standards.ieee.org/develop/regauth/oui/oui.txt' && \ + wget -O ma-medium.txt 'http://standards.ieee.org/develop/regauth/oui28/mam.txt' && \ + wget -O ma-small.txt 'http://standards.ieee.org/develop/regauth/oui36/oui36.txt' && \ + wget -O pnp_id_registry.html 'http://www.uefi.org/uefi-pnp-export' && \ + wget -O acpi_id_registry.html 'http://www.uefi.org/uefi-acpi-export' && \ + ./ids-update.pl && \ + ./acpi-update.py > 20-acpi-vendor.hwdb.base && \ + patch -p0 -o- 20-acpi-vendor.hwdb.base < 20-acpi-vendor.hwdb.patch > 20-acpi-vendor.hwdb ) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/hwdb/acpi-update.py b/src/grp-udev/hwdb/acpi-update.py new file mode 100755 index 0000000000..2dc8c7c064 --- /dev/null +++ b/src/grp-udev/hwdb/acpi-update.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 + +from html.parser import HTMLParser +from enum import Enum + +class State(Enum): + NOWHERE = 0 + COMPANY = 1 + AFTER_COMPANY = 2 + PNPID = 3 + AFTER_PNPID = 4 + DATE = 5 + +class PNPTableParser(HTMLParser): + + def __init__(self): + HTMLParser.__init__(self) + self.state = State.NOWHERE + self.data = "" + self.pnpid = None + self.company = None + self.table = [] + + def handle_starttag(self, tag, attrs): + + if tag == "td": + if self.state == State.NOWHERE: + self.state = State.COMPANY + elif self.state == State.AFTER_COMPANY: + self.state = State.PNPID + elif self.state == State.AFTER_PNPID: + self.state = State.DATE + else: + raise Error("Unexpected field") + + self.data = "" + + def handle_endtag(self, tag): + + if tag == "td": + if self.state == State.COMPANY: + self.company = ' '.join(self.data.strip().split()) + self.state = State.AFTER_COMPANY + elif self.state == State.PNPID: + self.pnpid = self.data.strip() + self.state = State.AFTER_PNPID + self.table.append((self.pnpid, self.company)) + elif self.state == State.DATE: + self.state = State.NOWHERE + else: + raise Error("Unexpected field") + + def handle_data(self, data): + self.data += data + +def read_table(a): + + parser = PNPTableParser() + + for line in a: + parser.feed(line) + + parser.close() + parser.table.sort() + + for pnpid, company in parser.table: + print("\nacpi:{0}*:\n ID_VENDOR_FROM_DATABASE={1}".format(pnpid, company)) + +a = open("acpi_id_registry.html") +b = open("pnp_id_registry.html") + +print('# This file is part of systemd.\n' + '#\n' + '# Data imported from:\n' + '# http://www.uefi.org/uefi-pnp-export\n' + '# http://www.uefi.org/uefi-acpi-export') + +read_table(a) +read_table(b) diff --git a/src/grp-udev/hwdb/ids-update.pl b/src/grp-udev/hwdb/ids-update.pl new file mode 100755 index 0000000000..03dd00b38d --- /dev/null +++ b/src/grp-udev/hwdb/ids-update.pl @@ -0,0 +1,375 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +sub usb_vendor { + my $vendor; + + open(IN, "<", "usb.ids"); + open(OUT, ">", "20-usb-vendor-model.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: http://www.linux-usb.org/usb.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + $line =~ m/^([0-9a-f]{4})\s*(.+)$/; + if (defined $1) { + $vendor = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "usb:v" . $vendor . "*\n"); + print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n"); + next; + } + + $line =~ m/^\t([0-9a-f]{4})\s*(.+)$/; + if (defined $1) { + my $model = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "usb:v" . $vendor . "p" . $model . "*\n"); + print(OUT " ID_MODEL_FROM_DATABASE=" . $text . "\n"); + } + } + + close(IN); + close(OUT); +} + +sub usb_classes { + my $class; + my $subclass; + my $protocol; + + open(IN, "<", "usb.ids"); + open(OUT, ">", "20-usb-classes.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: http://www.linux-usb.org/usb.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + + $line =~ m/^C\ ([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $class = uc $1; + if ($class =~ m/^00$/) { + next; + } + my $text = $2; + print(OUT "\n"); + print(OUT "usb:v*p*d*dc" . $class . "*\n"); + print(OUT " ID_USB_CLASS_FROM_DATABASE=" . $text . "\n"); + next; + } + + if (not defined $class) { + next; + } elsif ($line =~ m/^$/) { + last; + } + + $line =~ m/^\t([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $subclass = uc $1; + if ($subclass =~ m/^00$/) { + next; + } + my $text = $2; + if ($text =~ m/^(\?|None|Unused)$/) { + next; + } + print(OUT "\n"); + print(OUT "usb:v*p*d*dc" . $class . "dsc" . $subclass . "*\n"); + print(OUT " ID_USB_SUBCLASS_FROM_DATABASE=" . $text . "\n"); + next; + } + + $line =~ m/^\t\t([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $protocol = uc $1; + my $text = $2; + if ($text =~ m/^(\?|None|Unused)$/) { + next; + } + print(OUT "\n"); + print(OUT "usb:v*p*d*dc" . $class . "dsc" . $subclass . "dp" . $protocol . "*\n"); + print(OUT " ID_USB_PROTOCOL_FROM_DATABASE=" . $text . "\n"); + } + } + + close(IN); + close(OUT); +} + +sub pci_vendor { + my $vendor; + my $device; + my $device_text; + + open(IN, "<", "pci.ids"); + open(OUT, ">", "20-pci-vendor-model.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: http://pci-ids.ucw.cz/v2.2/pci.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + $line =~ m/^([0-9a-f]{4})\s*(.+)$/; + + if (defined $1) { + $vendor = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "pci:v0000" . $vendor . "*\n"); + print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n"); + next; + } + + $line =~ m/^\t([0-9a-f]{4})\s*(.+)$/; + if (defined $1) { + $device = uc $1; + $device_text = $2; + print(OUT "\n"); + print(OUT "pci:v0000" . $vendor . "d0000" . $device . "*\n"); + print(OUT " ID_MODEL_FROM_DATABASE=" . $device_text . "\n"); + next; + } + + $line =~ m/^\t\t([0-9a-f]{4})\s*([0-9a-f]{4})\s*(.*)$/; + if (defined $1) { + my $sub_vendor = uc $1; + my $sub_device = uc $2; + my $sub_text = $3; + $sub_text =~ s/^\Q$device_text\E\s*//; + $sub_text =~ s/(.+)/\ ($1)/; + print(OUT "\n"); + print(OUT "pci:v0000" . $vendor . "d0000" . $device . "sv0000" . $sub_vendor . "sd0000" . $sub_device . "*\n"); + print(OUT " ID_MODEL_FROM_DATABASE=" . $device_text . $sub_text . "\n"); + } + } + + close(IN); + close(OUT); +} + +sub pci_classes { + my $class; + my $subclass; + my $interface; + + open(IN, "<", "pci.ids"); + open(OUT, ">", "20-pci-classes.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: http://pci-ids.ucw.cz/v2.2/pci.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + + $line =~ m/^C\ ([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $class = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "pci:v*d*sv*sd*bc" . $class . "*\n"); + print(OUT " ID_PCI_CLASS_FROM_DATABASE=" . $text . "\n"); + next; + } + + if (not defined $class) { + next; + } elsif ($line =~ m/^$/) { + last; + } + + $line =~ m/^\t([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $subclass = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "pci:v*d*sv*sd*bc" . $class . "sc" . $subclass . "*\n"); + print(OUT " ID_PCI_SUBCLASS_FROM_DATABASE=" . $text . "\n"); + next; + } + + $line =~ m/^\t\t([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $interface = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "pci:v*d*sv*sd*bc" . $class . "sc" . $subclass . "i" . $interface . "*\n"); + print(OUT " ID_PCI_INTERFACE_FROM_DATABASE=" . $text . "\n"); + } + } + + close(IN); + close(OUT); +} + +sub sdio_vendor { + my $vendor; + my $device; + + open(IN, "<", "sdio.ids"); + open(OUT, ">", "20-sdio-vendor-model.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: hwdb/sdio.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + $line =~ m/^([0-9a-f]{4})\s*(.+)$/; + + if (defined $1) { + $vendor = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "sdio:c*v" . $vendor . "*\n"); + print(OUT " ID_VENDOR_FROM_DATABASE=" . $text . "\n"); + next; + } + + $line =~ m/^\t([0-9a-f]{4})\s*(.+)$/; + if (defined $1) { + $device = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "sdio:c*v" . $vendor . "d" . $device . "*\n"); + print(OUT " ID_MODEL_FROM_DATABASE=" . $text . "\n"); + next; + } + } + + close(IN); + close(OUT); +} + +sub sdio_classes { + my $class; + my $subclass; + my $interface; + + open(IN, "<", "sdio.ids"); + open(OUT, ">", "20-sdio-classes.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from: hwdb/sdio.ids\n"); + + while (my $line = ) { + $line =~ s/\s+$//; + + $line =~ m/^C\ ([0-9a-f]{2})\s*(.+)$/; + if (defined $1) { + $class = uc $1; + my $text = $2; + print(OUT "\n"); + print(OUT "sdio:c" . $class . "v*d*\n"); + print(OUT " ID_SDIO_CLASS_FROM_DATABASE=" . $text . "\n"); + next; + } + } + + close(IN); + close(OUT); +} + +# MAC Address Block Large/Medium/Small +# Large MA-L 24/24 bit (OUI) +# Medium MA-M 28/20 bit (OUI prefix owned by IEEE) +# Small MA-S 36/12 bit (OUI prefix owned by IEEE) +sub oui { + my $prefix; + my %ieee_prefixes = (); + + open(OUT, ">", "20-OUI.hwdb"); + print(OUT "# This file is part of systemd.\n" . + "#\n" . + "# Data imported from:\n" . + "# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-L&format=txt\n" . + "# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-M&format=txt\n" . + "# https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MA-S&format=txt\n"); + + open(IN, "<", "ma-small.txt"); + while (my $line = ) { + $line =~ s/^ +//; + $line =~ s/\s+$//; + $line =~ m/^([0-9A-F]{2})-([0-9A-F]{2})-([0-9A-F]{2})\s*\(hex\)\s*.+$/; + if (defined $1) { + $prefix = $1 . $2 . $3; + $ieee_prefixes{ $prefix } = 1; + next; + } + + $line =~ m/^([0-9A-F]{3})000-\g1FFF\s*\(base 16\)\s*(.+)$/; + if (defined $1) { + my $vendor = uc $1; + my $text = $2; + + print(OUT "\n"); + print(OUT "OUI:" . $prefix . $vendor . "*\n"); + print(OUT " ID_OUI_FROM_DATABASE=" . $text . "\n"); + } + } + close(IN); + + open(IN, "<", "ma-medium.txt"); + while (my $line = ) { + $line =~ s/^ +//; + $line =~ s/\s+$//; + $line =~ m/^([0-9A-F]{2})-([0-9A-F]{2})-([0-9A-F]{2})\s*\(hex\)\s*.+$/; + if (defined $1) { + $prefix = $1 . $2 . $3; + $ieee_prefixes{ $prefix } = 1; + next; + } + + $line =~ m/^([0-9A-F])00000-\g1FFFFF\s*\(base 16\)\s*(.+)$/; + if (defined $1) { + my $vendor = uc $1; + my $text = $2; + + print(OUT "\n"); + print(OUT "OUI:" . $prefix . $vendor . "*\n"); + print(OUT " ID_OUI_FROM_DATABASE=" . $text . "\n"); + } + } + + open(IN, "<", "ma-large.txt"); + while (my $line = ) { + $line =~ s/^ +//; + $line =~ s/\s+$//; + $line =~ m/^([0-9A-F]{6})\s*\(base 16\)\s*(.+)$/; + if (defined $1) { + my $vendor = uc $1; + my $text = $2; + + if ($text =~ m/^IEEE REGISTRATION AUTHORITY/) { + next; + } + + # skip the IEEE owned prefixes + if (! exists $ieee_prefixes{ $vendor }) { + print(OUT "\n"); + print(OUT "OUI:" . $vendor . "*\n"); + print(OUT " ID_OUI_FROM_DATABASE=" . $text . "\n"); + } + } + } + close(IN); + + close(OUT); +} + +usb_vendor(); +usb_classes(); + +pci_vendor(); +pci_classes(); + +sdio_vendor(); +sdio_classes(); + +oui(); diff --git a/src/grp-udev/hwdb/sdio.ids b/src/grp-udev/hwdb/sdio.ids new file mode 100644 index 0000000000..d61729744e --- /dev/null +++ b/src/grp-udev/hwdb/sdio.ids @@ -0,0 +1,94 @@ +# +# List of SDIO ID's +# + +# Vendors and devices. Please keep sorted. + +# Syntax: +# vendor vendor_name +# device device_name <-- single tab + +0020 ST-Ericsson + 2280 CW1200 +0089 Intel Corp. +0092 C-guys, Inc. + 0001 SD-Link11b WiFi Card (TI ACX100) + 0004 EW-CG1102GC + 0005 SD FM Radio 2 + 5544 SD FM Radio +0097 Texas Instruments, Inc. + 4076 WL1271 +0098 Toshiba Corp. + 0001 SD BT Card 1 + 0002 SD BT Card 2 + 0003 SD BT Card 3 +0104 Socket Communications, Inc. + 005e SD Scanner + 00c5 Bluetooth SDIO Card +0271 Atheros Communications, Inc. + 0108 AR6001 + 0109 AR6001 + 010a AR6001 + 010b AR6001 +0296 GCT Semiconductor, Inc. + 5347 GDM72xx WiMAX +02d0 Broadcom Corp. + 044b Nintendo Wii WLAN daughter card + a887 BCM43143 WLAN card + 4324 BCM43241 WLAN card + 4329 BCM4329 WLAN card + 4330 BCM4330 WLAN card + 4334 BCM4334 WLAN card + a94c BCM43340 WLAN card + a94d BCM43341 WLAN card + 4335 BCM4335/BCM4339 WLAN card + a962 BCM43362 WLAN card + 4354 BCM4354 WLAN card +02db SyChip Inc. + 0002 Pegasus WLAN SDIO Card (6060SD) +02df Marvell Technology Group Ltd. + 9103 Libertas + 9104 SD8688 WLAN + 9105 SD8688 BT + 9116 SD8786 WLAN + 9119 SD8787 WLAN + 911a SD8787 BT + 911b SD8787 BT AMP + 9129 SD8797 WLAN + 912a SD8797 BT + 912e SD8897 BT + 912d SD8897 WLAN +02fe Spectec Computer Co., Ltd + 2128 SDIO WLAN Card (SDW820) +032a Cambridge Silicon Radio + 0001 UniFi 1 + 0002 UniFi 2 + 0007 UniFi 3 + 0008 UniFi 4 +037a MediaTek Inc. + 5911 Spectec WLAN-11b/g +039a Siano Mobile Silicon +0501 Globalsat Technology Co. + f501 SD-501 GPS Card +104c Texas Instruments, Inc. + 9066 WL1251 +1180 Ricoh Co., Ltd + e823 MMC card reader +13d1 AboCom Systems, Inc. + ac02 SDW11G + +# List of known interface classes +# +# Syntax: +# C class class_name + +C 00 Not a SDIO standard interface +C 01 UART standard interface +C 02 Bluetooth Type-A standard interface +C 03 Bluetooth Type-B standard interface +C 04 GPS standard interface +C 05 Camera standard interface +C 06 PHS standard interface +C 07 WLAN interface +C 08 Embedded SDIO-ATA standard interface +C 09 Bluetooth AMP standard interface diff --git a/src/grp-udev/libudev-core/Makefile b/src/grp-udev/libudev-core/Makefile new file mode 100644 index 0000000000..38c05ffcb1 --- /dev/null +++ b/src/grp-udev/libudev-core/Makefile @@ -0,0 +1,99 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libudev-core.la + +$(outdir)/keyboard-keys-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM -include linux/input.h - < /dev/null | $(AWK) '/^#define[ \t]+KEY_[^ ]+[ \t]+[0-9K]/ { if ($$2 != "KEY_MAX") { print $$2 } }' > $@ + +$(outdir)/keyboard-keys-from-name.gperf: $(outdir)/keyboard-keys-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print tolower(substr($$1 ,5)) ", " $$1 }' < $< > $@ + +$(outdir)/keyboard-keys-from-name.h: $(outdir)/keyboard-keys-from-name.gperf + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t -N keyboard_lookup_key -H hash_key_name -p -C < $< > $@ + +gperf_txt_sources += \ + src/udev/keyboard-keys-list.txt + +libudev_core_la_SOURCES = \ + src/udev/udev.h \ + src/udev/udev-event.c \ + src/udev/udev-watch.c \ + src/udev/udev-node.c \ + src/udev/udev-rules.c \ + src/udev/udev-ctrl.c \ + src/udev/udev-builtin.c \ + src/udev/udev-builtin-btrfs.c \ + src/udev/udev-builtin-hwdb.c \ + src/udev/udev-builtin-input_id.c \ + src/udev/udev-builtin-keyboard.c \ + src/udev/udev-builtin-net_id.c \ + src/udev/udev-builtin-net_setup_link.c \ + src/udev/udev-builtin-path_id.c \ + src/udev/udev-builtin-usb_id.c \ + src/udev/net/link-config.h \ + src/udev/net/link-config.c \ + src/udev/net/ethtool-util.h \ + src/udev/net/ethtool-util.c + +nodist_libudev_core_la_SOURCES = \ + src/udev/keyboard-keys-from-name.h \ + src/udev/net/link-config-gperf.c + +gperf_gperf_sources += \ + src/udev/net/link-config-gperf.gperf + +libudev_core_la_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(KMOD_CFLAGS) + +libudev_core_la_LIBADD = \ + libsystemd-network.la \ + libshared.la \ + $(BLKID_LIBS) \ + $(KMOD_LIBS) + +ifneq ($(HAVE_KMOD),) +libudev_core_la_SOURCES += \ + src/udev/udev-builtin-kmod.c +endif # HAVE_KMOD + +ifneq ($(HAVE_BLKID),) +libudev_core_la_SOURCES += \ + src/udev/udev-builtin-blkid.c +endif # HAVE_BLKID + +ifneq ($(HAVE_ACL),) +libudev_core_la_SOURCES += \ + src/udev/udev-builtin-uaccess.c \ + src/login/logind-acl.c \ + src/libsystemd/sd-login/sd-login.c \ + src/systemd/sd-login.h +endif # HAVE_ACL + +nested.subdirs += net + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/libudev-core/logind-acl.c b/src/grp-udev/libudev-core/logind-acl.c new file mode 120000 index 0000000000..dd15b7973f --- /dev/null +++ b/src/grp-udev/libudev-core/logind-acl.c @@ -0,0 +1 @@ +../../grp-login/systemd-logind/logind-acl.c \ No newline at end of file diff --git a/src/grp-udev/libudev-core/logind-acl.h b/src/grp-udev/libudev-core/logind-acl.h new file mode 120000 index 0000000000..6065dde301 --- /dev/null +++ b/src/grp-udev/libudev-core/logind-acl.h @@ -0,0 +1 @@ +../../grp-login/systemd-logind/logind-acl.h \ No newline at end of file diff --git a/src/grp-udev/libudev-core/net/.gitignore b/src/grp-udev/libudev-core/net/.gitignore new file mode 100644 index 0000000000..9ca85bacc9 --- /dev/null +++ b/src/grp-udev/libudev-core/net/.gitignore @@ -0,0 +1 @@ +/link-config-gperf.c diff --git a/src/grp-udev/libudev-core/net/Makefile b/src/grp-udev/libudev-core/net/Makefile new file mode 100644 index 0000000000..b094ad75cf --- /dev/null +++ b/src/grp-udev/libudev-core/net/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +sd.CPPFLAGS += $(libshared.CPPFLAGS) +sd.CPPFLAGS += $(libsystemd-network.CPPFLAGS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/libudev-core/net/ethtool-util.c b/src/grp-udev/libudev-core/net/ethtool-util.c new file mode 100644 index 0000000000..b381284a6b --- /dev/null +++ b/src/grp-udev/libudev-core/net/ethtool-util.c @@ -0,0 +1,210 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include + +#include +#include + +#include "basic/log.h" +#include "basic/string-table.h" +#include "basic/strxcpyx.h" +#include "basic/util.h" +#include "shared/conf-parser.h" + +#include "ethtool-util.h" + +static const char* const duplex_table[_DUP_MAX] = { + [DUP_FULL] = "full", + [DUP_HALF] = "half" +}; + +DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); + +static const char* const wol_table[_WOL_MAX] = { + [WOL_PHY] = "phy", + [WOL_MAGIC] = "magic", + [WOL_OFF] = "off" +}; + +DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); +DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); + +int ethtool_connect(int *ret) { + int fd; + + assert_return(ret, -EINVAL); + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return -errno; + + *ret = fd; + + return 0; +} + +int ethtool_get_driver(int *fd, const char *ifname, char **ret) { + struct ethtool_drvinfo ecmd = { + .cmd = ETHTOOL_GDRVINFO + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + char *d; + int r; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + d = strdup(ecmd.driver); + if (!d) + return -ENOMEM; + + *ret = d; + return 0; +} + +int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { + struct ethtool_cmd ecmd = { + .cmd = ETHTOOL_GSET + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + bool need_update = false; + int r; + + if (speed == 0 && duplex == _DUP_INVALID) + return 0; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + if (ethtool_cmd_speed(&ecmd) != speed) { + ethtool_cmd_speed_set(&ecmd, speed); + need_update = true; + } + + switch (duplex) { + case DUP_HALF: + if (ecmd.duplex != DUPLEX_HALF) { + ecmd.duplex = DUPLEX_HALF; + need_update = true; + } + break; + case DUP_FULL: + if (ecmd.duplex != DUPLEX_FULL) { + ecmd.duplex = DUPLEX_FULL; + need_update = true; + } + break; + default: + break; + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SSET; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} + +int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { + struct ethtool_wolinfo ecmd = { + .cmd = ETHTOOL_GWOL + }; + struct ifreq ifr = { + .ifr_data = (void*) &ecmd + }; + bool need_update = false; + int r; + + if (wol == _WOL_INVALID) + return 0; + + if (*fd < 0) { + r = ethtool_connect(fd); + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); + } + + strscpy(ifr.ifr_name, IFNAMSIZ, ifname); + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + + switch (wol) { + case WOL_PHY: + if (ecmd.wolopts != WAKE_PHY) { + ecmd.wolopts = WAKE_PHY; + need_update = true; + } + break; + case WOL_MAGIC: + if (ecmd.wolopts != WAKE_MAGIC) { + ecmd.wolopts = WAKE_MAGIC; + need_update = true; + } + break; + case WOL_OFF: + if (ecmd.wolopts != 0) { + ecmd.wolopts = 0; + need_update = true; + } + break; + default: + break; + } + + if (need_update) { + ecmd.cmd = ETHTOOL_SWOL; + + r = ioctl(*fd, SIOCETHTOOL, &ifr); + if (r < 0) + return -errno; + } + + return 0; +} diff --git a/src/grp-udev/libudev-core/net/ethtool-util.h b/src/grp-udev/libudev-core/net/ethtool-util.h new file mode 100644 index 0000000000..7d103d9f2f --- /dev/null +++ b/src/grp-udev/libudev-core/net/ethtool-util.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include "basic/macro.h" + +/* we can't use DUPLEX_ prefix, as it + * clashes with */ +typedef enum Duplex { + DUP_FULL, + DUP_HALF, + _DUP_MAX, + _DUP_INVALID = -1 +} Duplex; + +typedef enum WakeOnLan { + WOL_PHY, + WOL_MAGIC, + WOL_OFF, + _WOL_MAX, + _WOL_INVALID = -1 +} WakeOnLan; + +int ethtool_connect(int *ret); + +int ethtool_get_driver(int *fd, const char *ifname, char **ret); +int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); +int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); + +const char *duplex_to_string(Duplex d) _const_; +Duplex duplex_from_string(const char *d) _pure_; + +const char *wol_to_string(WakeOnLan wol) _const_; +WakeOnLan wol_from_string(const char *wol) _pure_; + +int config_parse_duplex(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_wol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-udev/libudev-core/net/link-config-gperf.gperf b/src/grp-udev/libudev-core/net/link-config-gperf.gperf new file mode 100644 index 0000000000..c84f4599ce --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config-gperf.gperf @@ -0,0 +1,39 @@ +%{ +#include + +#include "shared/conf-parser.h" +#include "systemd-network/network-internal.h" + +#include "ethtool-util.h" +#include "link-config.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name link_config_gperf_hash +%define lookup-function-name link_config_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) +Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) +Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) +Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) +Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) +Link.Description, config_parse_string, 0, offsetof(link_config, description) +Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) +Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) +Link.Name, config_parse_ifname, 0, offsetof(link_config, name) +Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) +Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) +Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) +Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) diff --git a/src/grp-udev/libudev-core/net/link-config.c b/src/grp-udev/libudev-core/net/link-config.c new file mode 100644 index 0000000000..f6803e00df --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config.c @@ -0,0 +1,519 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/random-util.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "libudev-private.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/conf-parser.h" +#include "systemd-network/network-internal.h" + +#include "ethtool-util.h" +#include "link-config.h" + +struct link_config_ctx { + LIST_HEAD(link_config, links); + + int ethtool_fd; + + bool enable_name_policy; + + sd_netlink *rtnl; + + usec_t link_dirs_ts_usec; +}; + +static const char* const link_dirs[] = { + "/etc/systemd/network", + "/run/systemd/network", + "/usr/lib/systemd/network", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/network", +#endif + NULL}; + +static void link_config_free(link_config *link) { + if (!link) + return; + + free(link->filename); + + free(link->match_mac); + strv_free(link->match_path); + strv_free(link->match_driver); + strv_free(link->match_type); + free(link->match_name); + free(link->match_host); + free(link->match_virt); + free(link->match_kernel); + free(link->match_arch); + + free(link->description); + free(link->mac); + free(link->name_policy); + free(link->name); + free(link->alias); + + free(link); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(link_config*, link_config_free); + +static void link_configs_free(link_config_ctx *ctx) { + link_config *link, *link_next; + + if (!ctx) + return; + + LIST_FOREACH_SAFE(links, link, link_next, ctx->links) + link_config_free(link); +} + +void link_config_ctx_free(link_config_ctx *ctx) { + if (!ctx) + return; + + safe_close(ctx->ethtool_fd); + + sd_netlink_unref(ctx->rtnl); + + link_configs_free(ctx); + + free(ctx); + + return; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); + +int link_config_ctx_new(link_config_ctx **ret) { + _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; + + if (!ret) + return -EINVAL; + + ctx = new0(link_config_ctx, 1); + if (!ctx) + return -ENOMEM; + + LIST_HEAD_INIT(ctx->links); + + ctx->ethtool_fd = -1; + + ctx->enable_name_policy = true; + + *ret = ctx; + ctx = NULL; + + return 0; +} + +static int load_link(link_config_ctx *ctx, const char *filename) { + _cleanup_(link_config_freep) link_config *link = NULL; + _cleanup_fclose_ FILE *file = NULL; + int r; + + assert(ctx); + assert(filename); + + file = fopen(filename, "re"); + if (!file) { + if (errno == ENOENT) + return 0; + else + return -errno; + } + + if (null_or_empty_fd(fileno(file))) { + log_debug("Skipping empty file: %s", filename); + return 0; + } + + link = new0(link_config, 1); + if (!link) + return log_oom(); + + link->mac_policy = _MACPOLICY_INVALID; + link->wol = _WOL_INVALID; + link->duplex = _DUP_INVALID; + + r = config_parse(NULL, filename, file, + "Match\0Link\0Ethernet\0", + config_item_perf_lookup, link_config_gperf_lookup, + false, false, true, link); + if (r < 0) + return r; + else + log_debug("Parsed configuration file %s", filename); + + if (link->mtu > UINT_MAX || link->speed > UINT_MAX) + return -ERANGE; + + link->filename = strdup(filename); + + LIST_PREPEND(links, ctx->links, link); + link = NULL; + + return 0; +} + +static bool enable_name_policy(void) { + _cleanup_free_ char *line = NULL; + const char *word, *state; + int r; + size_t l; + + r = proc_cmdline(&line); + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); + return true; + } + + FOREACH_WORD_QUOTED(word, l, line, state) + if (strneq(word, "net.ifnames=0", l)) + return false; + + return true; +} + +int link_config_load(link_config_ctx *ctx) { + int r; + _cleanup_strv_free_ char **files; + char **f; + + link_configs_free(ctx); + + if (!enable_name_policy()) { + ctx->enable_name_policy = false; + log_info("Network interface NamePolicy= disabled on kernel command line, ignoring."); + } + + /* update timestamp */ + paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); + + r = conf_files_list_strv(&files, ".link", NULL, link_dirs); + if (r < 0) + return log_error_errno(r, "failed to enumerate link files: %m"); + + STRV_FOREACH_BACKWARDS(f, files) { + r = load_link(ctx, *f); + if (r < 0) + return r; + } + + return 0; +} + +bool link_config_should_reload(link_config_ctx *ctx) { + return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false); +} + +int link_config_get(link_config_ctx *ctx, struct udev_device *device, + link_config **ret) { + link_config *link; + + assert(ctx); + assert(device); + assert(ret); + + LIST_FOREACH(links, link, ctx->links) { + const char* attr_value; + + attr_value = udev_device_get_sysattr_value(device, "address"); + + if (net_match_config(link->match_mac, link->match_path, link->match_driver, + link->match_type, link->match_name, link->match_host, + link->match_virt, link->match_kernel, link->match_arch, + attr_value ? ether_aton(attr_value) : NULL, + udev_device_get_property_value(device, "ID_PATH"), + udev_device_get_driver(udev_device_get_parent(device)), + udev_device_get_property_value(device, "ID_NET_DRIVER"), + udev_device_get_devtype(device), + udev_device_get_sysname(device))) { + if (link->match_name) { + unsigned char name_assign_type = NET_NAME_UNKNOWN; + + attr_value = udev_device_get_sysattr_value(device, "name_assign_type"); + if (attr_value) + (void) safe_atou8(attr_value, &name_assign_type); + + if (name_assign_type == NET_NAME_ENUM) { + log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'", + link->filename, udev_device_get_sysname(device)); + *ret = link; + + return 0; + } else if (name_assign_type == NET_NAME_RENAMED) { + log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", + link->filename, udev_device_get_sysname(device)); + + continue; + } + } + + log_debug("Config file %s applies to device %s", + link->filename, udev_device_get_sysname(device)); + + *ret = link; + + return 0; + } + } + + *ret = NULL; + + return -ENOENT; +} + +static bool mac_is_random(struct udev_device *device) { + const char *s; + unsigned type; + int r; + + /* if we can't get the assign type, assume it is not random */ + s = udev_device_get_sysattr_value(device, "addr_assign_type"); + if (!s) + return false; + + r = safe_atou(s, &type); + if (r < 0) + return false; + + return type == NET_ADDR_RANDOM; +} + +static bool should_rename(struct udev_device *device, bool respect_predictable) { + const char *s; + unsigned type; + int r; + + /* if we can't get the assgin type, assume we should rename */ + s = udev_device_get_sysattr_value(device, "name_assign_type"); + if (!s) + return true; + + r = safe_atou(s, &type); + if (r < 0) + return true; + + switch (type) { + case NET_NAME_USER: + case NET_NAME_RENAMED: + /* these were already named by userspace, do not touch again */ + return false; + case NET_NAME_PREDICTABLE: + /* the kernel claims to have given a predictable name */ + if (respect_predictable) + return false; + /* fall through */ + case NET_NAME_ENUM: + default: + /* the name is known to be bad, or of an unknown type */ + return true; + } +} + +static int get_mac(struct udev_device *device, bool want_random, + struct ether_addr *mac) { + int r; + + if (want_random) + random_bytes(mac->ether_addr_octet, ETH_ALEN); + else { + uint64_t 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); + } + + /* see eth_random_addr in the kernel */ + mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + return 0; +} + +int link_config_apply(link_config_ctx *ctx, link_config *config, + struct udev_device *device, const char **name) { + const char *old_name; + const char *new_name = NULL; + struct ether_addr generated_mac; + struct ether_addr *mac = NULL; + bool respect_predictable = false; + int r, ifindex; + + assert(ctx); + assert(config); + assert(device); + assert(name); + + old_name = udev_device_get_sysname(device); + if (!old_name) + return -EINVAL; + + r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); + if (r < 0) + log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m", + old_name, config->speed / 1024, + duplex_to_string(config->duplex)); + + r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); + if (r < 0) + log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m", + old_name, wol_to_string(config->wol)); + + ifindex = udev_device_get_ifindex(device); + if (ifindex <= 0) { + log_warning("Could not find ifindex"); + return -ENODEV; + } + + if (ctx->enable_name_policy && config->name_policy) { + NamePolicy *policy; + + for (policy = config->name_policy; + !new_name && *policy != _NAMEPOLICY_INVALID; policy++) { + switch (*policy) { + case NAMEPOLICY_KERNEL: + respect_predictable = true; + break; + case NAMEPOLICY_DATABASE: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE"); + break; + case NAMEPOLICY_ONBOARD: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"); + break; + case NAMEPOLICY_SLOT: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT"); + break; + case NAMEPOLICY_PATH: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH"); + break; + case NAMEPOLICY_MAC: + new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC"); + break; + default: + break; + } + } + } + + if (should_rename(device, respect_predictable)) { + /* if not set by policy, fall back manually set name */ + if (!new_name) + new_name = config->name; + } else + new_name = NULL; + + switch (config->mac_policy) { + case MACPOLICY_PERSISTENT: + if (mac_is_random(device)) { + r = get_mac(device, false, &generated_mac); + if (r == -ENOENT) { + log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); + break; + } else if (r < 0) + return r; + mac = &generated_mac; + } + break; + case MACPOLICY_RANDOM: + if (!mac_is_random(device)) { + r = get_mac(device, true, &generated_mac); + if (r == -ENOENT) { + log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); + break; + } else if (r < 0) + return r; + mac = &generated_mac; + } + break; + case MACPOLICY_NONE: + default: + mac = config->mac; + } + + r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); + if (r < 0) + return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name); + + *name = new_name; + + return 0; +} + +int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) { + const char *name; + char *driver = NULL; + int r; + + name = udev_device_get_sysname(device); + if (!name) + return -EINVAL; + + r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver); + if (r < 0) + return r; + + *ret = driver; + return 0; +} + +static const char* const mac_policy_table[_MACPOLICY_MAX] = { + [MACPOLICY_PERSISTENT] = "persistent", + [MACPOLICY_RANDOM] = "random", + [MACPOLICY_NONE] = "none" +}; + +DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, + "Failed to parse MAC address policy"); + +static const char* const name_policy_table[_NAMEPOLICY_MAX] = { + [NAMEPOLICY_KERNEL] = "kernel", + [NAMEPOLICY_DATABASE] = "database", + [NAMEPOLICY_ONBOARD] = "onboard", + [NAMEPOLICY_SLOT] = "slot", + [NAMEPOLICY_PATH] = "path", + [NAMEPOLICY_MAC] = "mac" +}; + +DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy); +DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, + _NAMEPOLICY_INVALID, + "Failed to parse interface name policy"); diff --git a/src/grp-udev/libudev-core/net/link-config.h b/src/grp-udev/libudev-core/net/link-config.h new file mode 100644 index 0000000000..ad5211610a --- /dev/null +++ b/src/grp-udev/libudev-core/net/link-config.h @@ -0,0 +1,99 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include + +#include "basic/list.h" +#include "shared/condition.h" + +#include "ethtool-util.h" + +typedef struct link_config_ctx link_config_ctx; +typedef struct link_config link_config; + +typedef enum MACPolicy { + MACPOLICY_PERSISTENT, + MACPOLICY_RANDOM, + MACPOLICY_NONE, + _MACPOLICY_MAX, + _MACPOLICY_INVALID = -1 +} MACPolicy; + +typedef enum NamePolicy { + NAMEPOLICY_KERNEL, + NAMEPOLICY_DATABASE, + NAMEPOLICY_ONBOARD, + NAMEPOLICY_SLOT, + NAMEPOLICY_PATH, + NAMEPOLICY_MAC, + _NAMEPOLICY_MAX, + _NAMEPOLICY_INVALID = -1 +} NamePolicy; + +struct link_config { + char *filename; + + struct ether_addr *match_mac; + char **match_path; + char **match_driver; + char **match_type; + char **match_name; + Condition *match_host; + Condition *match_virt; + Condition *match_kernel; + Condition *match_arch; + + char *description; + struct ether_addr *mac; + MACPolicy mac_policy; + NamePolicy *name_policy; + char *name; + char *alias; + size_t mtu; + size_t speed; + Duplex duplex; + WakeOnLan wol; + + LIST_FIELDS(link_config, links); +}; + +int link_config_ctx_new(link_config_ctx **ret); +void link_config_ctx_free(link_config_ctx *ctx); + +int link_config_load(link_config_ctx *ctx); +bool link_config_should_reload(link_config_ctx *ctx); + +int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret); +int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device, const char **name); + +int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret); + +const char *name_policy_to_string(NamePolicy p) _const_; +NamePolicy name_policy_from_string(const char *p) _pure_; + +const char *mac_policy_to_string(MACPolicy p) _const_; +MACPolicy mac_policy_from_string(const char *p) _pure_; + +/* gperf lookup function */ +const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, unsigned length); + +int config_parse_mac_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_name_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/grp-udev/libudev-core/sd-login.c b/src/grp-udev/libudev-core/sd-login.c new file mode 120000 index 0000000000..913dcedc6a --- /dev/null +++ b/src/grp-udev/libudev-core/sd-login.c @@ -0,0 +1 @@ +../../libsystemd/src/sd-login/sd-login.c \ No newline at end of file diff --git a/src/grp-udev/libudev-core/udev-builtin-blkid.c b/src/grp-udev/libudev-core/udev-builtin-blkid.c new file mode 100644 index 0000000000..a2784aa03e --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-blkid.c @@ -0,0 +1,337 @@ +/* + * probe disks for filesystems and partitions + * + * Copyright (C) 2011 Kay Sievers + * Copyright (C) 2011 Karel Zak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "shared/efivars.h" +#include "shared/gpt.h" +#include "udev.h" + +static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) { + char s[256]; + + s[0] = '\0'; + + if (streq(name, "TYPE")) { + udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); + + } else if (streq(name, "USAGE")) { + udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); + + } else if (streq(name, "VERSION")) { + udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); + + } else if (streq(name, "UUID")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); + + } else if (streq(name, "UUID_SUB")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); + + } else if (streq(name, "LABEL")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); + + } else if (streq(name, "PTTYPE")) { + udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); + + } else if (streq(name, "PTUUID")) { + udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value); + + } else if (streq(name, "PART_ENTRY_NAME")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); + + } else if (streq(name, "PART_ENTRY_TYPE")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); + + } else if (startswith(name, "PART_ENTRY_")) { + strscpyl(s, sizeof(s), "ID_", name, NULL); + udev_builtin_add_property(dev, test, s, value); + + } else if (streq(name, "SYSTEM_ID")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s); + + } else if (streq(name, "PUBLISHER_ID")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s); + + } else if (streq(name, "APPLICATION_ID")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s); + + } else if (streq(name, "BOOT_SYSTEM_ID")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s); + } +} + +static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) { + +#if defined(GPT_ROOT_NATIVE) && defined(ENABLE_EFI) + + _cleanup_free_ char *root_id = NULL; + bool found_esp = false; + blkid_partlist pl; + int i, nvals, r; + + assert(pr); + + /* Iterate through the partitions on this disk, and see if the + * EFI ESP we booted from is on it. If so, find the first root + * disk, and add a property indicating its partition UUID. */ + + errno = 0; + pl = blkid_probe_get_partitions(pr); + if (!pl) + return errno > 0 ? -errno : -ENOMEM; + + nvals = blkid_partlist_numof_partitions(pl); + for (i = 0; i < nvals; i++) { + blkid_partition pp; + const char *stype, *sid; + sd_id128_t type; + + pp = blkid_partlist_get_partition(pl, i); + if (!pp) + continue; + + sid = blkid_partition_get_uuid(pp); + if (!sid) + continue; + + stype = blkid_partition_get_type_string(pp); + if (!stype) + continue; + + if (sd_id128_from_string(stype, &type) < 0) + continue; + + if (sd_id128_equal(type, GPT_ESP)) { + sd_id128_t id, esp; + + /* We found an ESP, let's see if it matches + * the ESP we booted from. */ + + if (sd_id128_from_string(sid, &id) < 0) + continue; + + r = efi_loader_get_device_part_uuid(&esp); + if (r < 0) + return r; + + if (sd_id128_equal(id, esp)) + found_esp = true; + + } else if (sd_id128_equal(type, GPT_ROOT_NATIVE)) { + unsigned long long flags; + + flags = blkid_partition_get_flags(pp); + if (flags & GPT_FLAG_NO_AUTO) + continue; + + /* We found a suitable root partition, let's + * remember the first one. */ + + if (!root_id) { + root_id = strdup(sid); + if (!root_id) + return -ENOMEM; + } + } + } + + /* We found the ESP on this disk, and also found a root + * partition, nice! Let's export its UUID */ + if (found_esp && root_id) + udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", root_id); +#endif + + return 0; +} + +static int probe_superblocks(blkid_probe pr) { + struct stat st; + int rc; + + if (fstat(blkid_probe_get_fd(pr), &st)) + return -1; + + blkid_probe_enable_partitions(pr, 1); + + if (!S_ISCHR(st.st_mode) && + blkid_probe_get_size(pr) <= 1024 * 1440 && + blkid_probe_is_wholedisk(pr)) { + /* + * check if the small disk is partitioned, if yes then + * don't probe for filesystems. + */ + blkid_probe_enable_superblocks(pr, 0); + + rc = blkid_do_fullprobe(pr); + if (rc < 0) + return rc; /* -1 = error, 1 = nothing, 0 = success */ + + if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) + return 0; /* partition table detected */ + } + + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + blkid_probe_enable_superblocks(pr, 1); + + return blkid_do_safeprobe(pr); +} + +static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) { + const char *root_partition; + int64_t offset = 0; + bool noraid = false; + _cleanup_close_ int fd = -1; + blkid_probe pr; + const char *data; + const char *name; + const char *prtype = NULL; + int nvals; + int i; + int err = 0; + bool is_gpt = false; + + static const struct option options[] = { + { "offset", optional_argument, NULL, 'o' }, + { "noraid", no_argument, NULL, 'R' }, + {} + }; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "oR", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'o': + offset = strtoull(optarg, NULL, 0); + break; + case 'R': + noraid = true; + break; + } + } + + pr = blkid_new_probe(); + if (!pr) + return EXIT_FAILURE; + + blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | + BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION | + BLKID_SUBLKS_BADCSUM); + + if (noraid) + blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); + + fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev)); + goto out; + } + + err = blkid_probe_set_device(pr, fd, offset, 0); + if (err < 0) + goto out; + + log_debug("probe %s %sraid offset=%"PRIi64, + udev_device_get_devnode(dev), + noraid ? "no" : "", offset); + + err = probe_superblocks(pr); + if (err < 0) + goto out; + if (blkid_probe_has_value(pr, "SBBADCSUM")) { + if (!blkid_probe_lookup_value(pr, "TYPE", &prtype, NULL)) + log_warning("incorrect %s checksum on %s", + prtype, udev_device_get_devnode(dev)); + else + log_warning("incorrect checksum on %s", + udev_device_get_devnode(dev)); + goto out; + } + + /* If we are a partition then our parent passed on the root + * partition UUID to us */ + root_partition = udev_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID"); + + nvals = blkid_probe_numof_values(pr); + for (i = 0; i < nvals; i++) { + if (blkid_probe_get_value(pr, i, &name, &data, NULL)) + continue; + + print_property(dev, test, name, data); + + /* Is this a disk with GPT partition table? */ + if (streq(name, "PTTYPE") && streq(data, "gpt")) + is_gpt = true; + + /* Is this a partition that matches the root partition + * property we inherited from our parent? */ + if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition)) + udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1"); + } + + if (is_gpt) + find_gpt_root(dev, pr, test); + + blkid_free_probe(pr); +out: + if (err < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_blkid = { + .name = "blkid", + .cmd = builtin_blkid, + .help = "Filesystem and partition probing", + .run_once = true, +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-btrfs.c b/src/grp-udev/libudev-core/udev-builtin-btrfs.c new file mode 100644 index 0000000000..333229de55 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-btrfs.c @@ -0,0 +1,58 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_LINUX_BTRFS_H +#include +#endif + +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/string-util.h" +#include "udev.h" + +static int builtin_btrfs(struct udev_device *dev, int argc, char *argv[], bool test) { + struct btrfs_ioctl_vol_args args = {}; + _cleanup_close_ int fd = -1; + int err; + + if (argc != 3 || !streq(argv[1], "ready")) + return EXIT_FAILURE; + + fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC); + if (fd < 0) + return EXIT_FAILURE; + + strscpy(args.name, sizeof(args.name), argv[2]); + err = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args); + if (err < 0) + return EXIT_FAILURE; + + udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(err == 0)); + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_btrfs = { + .name = "btrfs", + .cmd = builtin_btrfs, + .help = "btrfs volume management", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-hwdb.c b/src/grp-udev/libudev-core/udev-builtin-hwdb.c new file mode 100644 index 0000000000..d2d95bf394 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-hwdb.c @@ -0,0 +1,222 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "sd-hwdb/hwdb-util.h" +#include "sd-hwdb/sd-hwdb.h" +#include "shared/udev-util.h" +#include "udev.h" + +static sd_hwdb *hwdb; + +int udev_builtin_hwdb_lookup(struct udev_device *dev, + const char *prefix, const char *modalias, + const char *filter, bool test) { + _cleanup_free_ char *lookup = NULL; + const char *key, *value; + int n = 0; + + if (!hwdb) + return -ENOENT; + + if (prefix) { + lookup = strjoin(prefix, modalias, NULL); + if (!lookup) + return -ENOMEM; + modalias = lookup; + } + + SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) { + if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0) + continue; + + if (udev_builtin_add_property(dev, test, key, value) < 0) + return -ENOMEM; + n++; + } + return n; +} + +static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) { + const char *v, *p; + int vn, pn; + + v = udev_device_get_sysattr_value(dev, "idVendor"); + if (!v) + return NULL; + p = udev_device_get_sysattr_value(dev, "idProduct"); + if (!p) + return NULL; + vn = strtol(v, NULL, 16); + if (vn <= 0) + return NULL; + pn = strtol(p, NULL, 16); + if (pn <= 0) + return NULL; + snprintf(s, size, "usb:v%04Xp%04X*", vn, pn); + return s; +} + +static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev, + const char *subsystem, const char *prefix, + const char *filter, bool test) { + struct udev_device *d; + char s[16]; + bool last = false; + int r = 0; + + assert(dev); + + if (!srcdev) + srcdev = dev; + + for (d = srcdev; d && !last; d = udev_device_get_parent(d)) { + const char *dsubsys; + const char *modalias = NULL; + + dsubsys = udev_device_get_subsystem(d); + if (!dsubsys) + continue; + + /* look only at devices of a specific subsystem */ + if (subsystem && !streq(dsubsys, subsystem)) + continue; + + modalias = udev_device_get_property_value(d, "MODALIAS"); + + if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) { + /* if the usb_device does not have a modalias, compose one */ + if (!modalias) + modalias = modalias_usb(d, s, sizeof(s)); + + /* avoid looking at any parent device, they are usually just a USB hub */ + last = true; + } + + if (!modalias) + continue; + + r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test); + if (r > 0) + break; + } + + return r; +} + +static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) { + static const struct option options[] = { + { "filter", required_argument, NULL, 'f' }, + { "device", required_argument, NULL, 'd' }, + { "subsystem", required_argument, NULL, 's' }, + { "lookup-prefix", required_argument, NULL, 'p' }, + {} + }; + const char *filter = NULL; + const char *device = NULL; + const char *subsystem = NULL; + const char *prefix = NULL; + _cleanup_udev_device_unref_ struct udev_device *srcdev = NULL; + + if (!hwdb) + return EXIT_FAILURE; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "f:d:s:p:", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'f': + filter = optarg; + break; + + case 'd': + device = optarg; + break; + + case 's': + subsystem = optarg; + break; + + case 'p': + prefix = optarg; + break; + } + } + + /* query a specific key given as argument */ + if (argv[optind]) { + if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0) + return EXIT_SUCCESS; + return EXIT_FAILURE; + } + + /* read data from another device than the device we will store the data */ + if (device) { + srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device); + if (!srcdev) + return EXIT_FAILURE; + } + + if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +/* called at udev startup and reload */ +static int builtin_hwdb_init(struct udev *udev) { + int r; + + if (hwdb) + return 0; + + r = sd_hwdb_new(&hwdb); + if (r < 0) + return r; + + return 0; +} + +/* called on udev shutdown and reload request */ +static void builtin_hwdb_exit(struct udev *udev) { + hwdb = sd_hwdb_unref(hwdb); +} + +/* called every couple of seconds during event activity; 'true' if config has changed */ +static bool builtin_hwdb_validate(struct udev *udev) { + return hwdb_validate(hwdb); +} + +const struct udev_builtin udev_builtin_hwdb = { + .name = "hwdb", + .cmd = builtin_hwdb, + .init = builtin_hwdb_init, + .exit = builtin_hwdb_exit, + .validate = builtin_hwdb_validate, + .help = "Hardware database", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-input_id.c b/src/grp-udev/libudev-core/udev-builtin-input_id.c new file mode 100644 index 0000000000..79f45f873c --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-input_id.c @@ -0,0 +1,341 @@ +/* + * expose input properties via udev + * + * Copyright (C) 2009 Martin Pitt + * Portions Copyright (C) 2004 David Zeuthen, + * Copyright (C) 2011 Kay Sievers + * Copyright (C) 2014 Carlos Garnacho + * Copyright (C) 2014 David Herrmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/fd-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "udev.h" + +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + +static inline int abs_size_mm(const struct input_absinfo *absinfo) { + /* Resolution is defined to be in units/mm for ABS_X/Y */ + return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; +} + +static void extract_info(struct udev_device *dev, const char *devpath, bool test) { + char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)]; + struct input_absinfo xabsinfo = {}, yabsinfo = {}; + _cleanup_close_ int fd = -1; + + fd = open(devpath, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return; + + if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 || + ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0) + return; + + if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0) + return; + + 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); +} + +/* + * Read a capability attribute and return bitmask. + * @param dev udev_device + * @param attr sysfs attribute name (e. g. "capabilities/key") + * @param bitmask: Output array which has a sizeof of bitmask_size + */ +static void get_cap_mask(struct udev_device *dev, + struct udev_device *pdev, const char* attr, + unsigned long *bitmask, size_t bitmask_size, + bool test) { + const char *v; + char text[4096]; + unsigned i; + char* word; + unsigned long val; + + v = udev_device_get_sysattr_value(pdev, attr); + if (!v) + v = ""; + + xsprintf(text, "%s", v); + log_debug("%s raw kernel attribute: %s", attr, text); + + memzero(bitmask, bitmask_size); + i = 0; + while ((word = strrchr(text, ' ')) != NULL) { + val = strtoul (word+1, NULL, 16); + if (i < bitmask_size/sizeof(unsigned long)) + bitmask[i] = val; + else + log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); + *word = '\0'; + ++i; + } + val = strtoul (text, NULL, 16); + if (i < bitmask_size / sizeof(unsigned long)) + bitmask[i] = val; + else + log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); + + if (test) { + /* printf pattern with the right unsigned long number of hex chars */ + 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 */ + while (bitmask[val-1] == 0 && val > 0) + --val; + for (i = 0; i < val; ++i) { + DISABLE_WARNING_FORMAT_NONLITERAL; + log_debug(text, i * BITS_PER_LONG, bitmask[i]); + REENABLE_WARNING; + } + } +} + +/* pointer devices */ +static bool test_pointers(struct udev_device *dev, + const unsigned long* bitmask_ev, + const unsigned long* bitmask_abs, + const unsigned long* bitmask_key, + const unsigned long* bitmask_rel, + const unsigned long* bitmask_props, + bool test) { + bool has_abs_coordinates = false; + bool has_rel_coordinates = false; + bool has_mt_coordinates = false; + bool has_joystick_axes_or_buttons = false; + bool is_direct = false; + bool has_touch = false; + bool has_3d_coordinates = false; + bool has_keys = false; + bool stylus_or_pen = false; + bool finger_but_no_pen = false; + bool has_mouse_button = false; + bool is_mouse = false; + bool is_touchpad = false; + bool is_touchscreen = false; + bool is_tablet = false; + bool is_joystick = false; + bool is_accelerometer = false; + bool is_pointing_stick= false; + + has_keys = test_bit(EV_KEY, bitmask_ev); + has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs); + has_3d_coordinates = has_abs_coordinates && test_bit(ABS_Z, bitmask_abs); + is_accelerometer = test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props); + + if (!has_keys && has_3d_coordinates) + is_accelerometer = true; + + if (is_accelerometer) { + udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); + return true; + } + + is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props); + stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key); + finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key); + has_mouse_button = test_bit(BTN_LEFT, bitmask_key); + has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel); + has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs); + + /* unset has_mt_coordinates if devices claims to have all abs axis */ + if (has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs)) + has_mt_coordinates = false; + is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props); + has_touch = test_bit(BTN_TOUCH, bitmask_key); + /* joysticks don't necessarily have buttons; e. g. + * rudders/pedals are joystick-like, but buttonless; they have + * other fancy axes */ + has_joystick_axes_or_buttons = test_bit(BTN_TRIGGER, bitmask_key) || + test_bit(BTN_A, bitmask_key) || + test_bit(BTN_1, bitmask_key) || + test_bit(ABS_RX, bitmask_abs) || + test_bit(ABS_RY, bitmask_abs) || + test_bit(ABS_RZ, bitmask_abs) || + test_bit(ABS_THROTTLE, bitmask_abs) || + test_bit(ABS_RUDDER, bitmask_abs) || + test_bit(ABS_WHEEL, bitmask_abs) || + test_bit(ABS_GAS, bitmask_abs) || + test_bit(ABS_BRAKE, bitmask_abs); + + if (has_abs_coordinates) { + if (stylus_or_pen) + is_tablet = true; + else if (finger_but_no_pen && !is_direct) + is_touchpad = true; + else if (has_mouse_button) + /* This path is taken by VMware's USB mouse, which has + * absolute axes, but no touch/pressure button. */ + is_mouse = true; + else if (has_touch || is_direct) + is_touchscreen = true; + else if (has_joystick_axes_or_buttons) + is_joystick = true; + } + if (has_mt_coordinates) { + if (stylus_or_pen) + is_tablet = true; + else if (finger_but_no_pen && !is_direct) + is_touchpad = true; + else if (has_touch || is_direct) + is_touchscreen = true; + } + + if (has_rel_coordinates && has_mouse_button) + is_mouse = true; + + if (is_pointing_stick) + udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1"); + if (is_mouse) + udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); + if (is_touchpad) + udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); + if (is_touchscreen) + udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); + if (is_joystick) + udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); + if (is_tablet) + udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); + + return is_tablet || is_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick; +} + +/* key like devices */ +static bool test_key(struct udev_device *dev, + const unsigned long* bitmask_ev, + const unsigned long* bitmask_key, + bool test) { + unsigned i; + unsigned long found; + unsigned long mask; + bool ret = false; + + /* do we have any KEY_* capability? */ + if (!test_bit(EV_KEY, bitmask_ev)) { + log_debug("test_key: no EV_KEY capability"); + return false; + } + + /* only consider KEY_* here, not BTN_* */ + found = 0; + for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { + found |= bitmask_key[i]; + log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0); + } + /* If there are no keys in the lower block, check the higher block */ + if (!found) { + for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { + if (test_bit(i, bitmask_key)) { + log_debug("test_key: Found key %x in high block", i); + found = 1; + break; + } + } + } + + if (found > 0) { + udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); + ret = true; + } + + /* the first 32 bits are ESC, numbers, and Q to D; if we have all of + * those, consider it a full keyboard; do not test KEY_RESERVED, though */ + mask = 0xFFFFFFFE; + if ((bitmask_key[0] & mask) == mask) { + udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); + ret = true; + } + + return ret; +} + +static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) { + struct udev_device *pdev; + unsigned long bitmask_ev[NBITS(EV_MAX)]; + unsigned long bitmask_abs[NBITS(ABS_MAX)]; + unsigned long bitmask_key[NBITS(KEY_MAX)]; + unsigned long bitmask_rel[NBITS(REL_MAX)]; + unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)]; + const char *sysname, *devnode; + bool is_pointer; + bool is_key; + + assert(dev); + + /* walk up the parental chain until we find the real input device; the + * argument is very likely a subdevice of this, like eventN */ + pdev = dev; + while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) + pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); + + if (pdev) { + /* Use this as a flag that input devices were detected, so that this + * program doesn't need to be called more than once per device */ + udev_builtin_add_property(dev, test, "ID_INPUT", "1"); + get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); + get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); + get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); + get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); + get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test); + is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs, + bitmask_key, bitmask_rel, + bitmask_props, test); + is_key = test_key(dev, bitmask_ev, bitmask_key, test); + /* Some evdev nodes have only a scrollwheel */ + if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) && + (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel))) + udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); + } + + devnode = udev_device_get_devnode(dev); + sysname = udev_device_get_sysname(dev); + if (devnode && sysname && startswith(sysname, "event")) + extract_info(dev, devnode, test); + + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_input_id = { + .name = "input_id", + .cmd = builtin_input_id, + .help = "Input device properties", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-keyboard.c b/src/grp-udev/libudev-core/udev-builtin-keyboard.c new file mode 100644 index 0000000000..d07eff08d6 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-keyboard.c @@ -0,0 +1,278 @@ +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "udev.h" + +static const struct key *keyboard_lookup_key(const char *str, unsigned len); +#include "keyboard-keys-from-name.h" + +static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) { + struct udev_device *atkbd; + const char *cur; + char codes[4096]; + char *s; + size_t l; + unsigned i; + int ret; + + assert(dev); + assert(release); + + atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); + if (!atkbd) + return -ENODEV; + + cur = udev_device_get_sysattr_value(atkbd, "force_release"); + if (!cur) + return -ENODEV; + + s = codes; + l = sizeof(codes); + + /* copy current content */ + l = strpcpy(&s, l, cur); + + /* append new codes */ + for (i = 0; i < release_count; i++) + l = strpcpyf(&s, l, ",%u", release[i]); + + log_debug("keyboard: updating force-release list with '%s'", codes); + ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); + if (ret < 0) + log_error_errno(ret, "Error writing force-release attribute: %m"); + return ret; +} + +static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode) +{ + struct { + unsigned scan; + unsigned key; + } map; + char *endptr; + const struct key *k; + unsigned keycode_num; + + /* translate identifier to key code */ + k = keyboard_lookup_key(keycode, strlen(keycode)); + if (k) { + keycode_num = k->id; + } else { + /* check if it's a numeric code already */ + keycode_num = strtoul(keycode, &endptr, 0); + if (endptr[0] !='\0') { + log_error("Unknown key identifier '%s'", keycode); + return; + } + } + + map.scan = scancode; + map.key = keycode_num; + + log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)", + map.scan, map.scan, map.key, map.key); + + if (ioctl(fd, EVIOCSKEYCODE, &map) < 0) + log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key); +} + +static inline char* parse_token(const char *current, int32_t *val_out) { + char *next; + int32_t val; + + if (!current) + return NULL; + + val = strtol(current, &next, 0); + if (*next && *next != ':') + return NULL; + + if (next != current) + *val_out = val; + + if (*next) + next++; + + return next; +} + +static void override_abs(int fd, const char *devnode, + unsigned evcode, const char *value) { + struct input_absinfo absinfo; + int rc; + char *next; + + rc = ioctl(fd, EVIOCGABS(evcode), &absinfo); + if (rc < 0) { + log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode); + return; + } + + next = parse_token(value, &absinfo.minimum); + next = parse_token(next, &absinfo.maximum); + next = parse_token(next, &absinfo.resolution); + next = parse_token(next, &absinfo.fuzz); + next = parse_token(next, &absinfo.flat); + if (!next) { + log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode); + return; + } + + log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"", + evcode, + absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat, + devnode); + rc = ioctl(fd, EVIOCSABS(evcode), &absinfo); + if (rc < 0) + log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode); +} + +static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value) +{ + struct udev_device *pdev; + char val_s[DECIMAL_STR_MAX(int)]; + int r, val_i; + + assert(dev); + assert(value); + + /* The sensitivity sysfs attr belongs to the serio parent device */ + pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); + if (!pdev) { + log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev)); + return; + } + + r = safe_atoi(value, &val_i); + if (r < 0) { + log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev)); + return; + } + + xsprintf(val_s, "%d", val_i); + + r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s); + if (r < 0) + log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev)); +} + +static int open_device(const char *devnode) { + int fd; + + fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Error opening device \"%s\": %m", devnode); + + return fd; +} + +static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) { + struct udev_list_entry *entry; + unsigned release[1024]; + unsigned release_count = 0; + _cleanup_close_ int fd = -1; + const char *node; + + node = udev_device_get_devnode(dev); + if (!node) { + log_error("No device node for \"%s\"", udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) { + const char *key; + char *endptr; + + key = udev_list_entry_get_name(entry); + if (startswith(key, "KEYBOARD_KEY_")) { + const char *keycode; + unsigned scancode; + + /* KEYBOARD_KEY_= */ + scancode = strtoul(key + 13, &endptr, 16); + if (endptr[0] != '\0') { + log_warning("Unable to parse scan code from \"%s\"", key); + continue; + } + + keycode = udev_list_entry_get_value(entry); + + /* a leading '!' needs a force-release entry */ + if (keycode[0] == '!') { + keycode++; + + release[release_count] = scancode; + if (release_count < ELEMENTSOF(release)-1) + release_count++; + + if (keycode[0] == '\0') + continue; + } + + if (fd == -1) { + fd = open_device(node); + if (fd < 0) + return EXIT_FAILURE; + } + + map_keycode(fd, node, scancode, keycode); + } else if (startswith(key, "EVDEV_ABS_")) { + unsigned evcode; + + /* EVDEV_ABS_=:::: */ + evcode = strtoul(key + 10, &endptr, 16); + if (endptr[0] != '\0') { + log_warning("Unable to parse EV_ABS code from \"%s\"", key); + continue; + } + + if (fd == -1) { + fd = open_device(node); + if (fd < 0) + return EXIT_FAILURE; + } + + override_abs(fd, node, evcode, udev_list_entry_get_value(entry)); + } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) + set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry)); + } + + /* install list of force-release codes */ + if (release_count > 0) + install_force_release(dev, release, release_count); + + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_keyboard = { + .name = "keyboard", + .cmd = builtin_keyboard, + .help = "Keyboard scan code to key mapping", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-kmod.c b/src/grp-udev/libudev-core/udev-builtin-kmod.c new file mode 100644 index 0000000000..15d23c0f0e --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-kmod.c @@ -0,0 +1,123 @@ +/* + * load kernel modules + * + * Copyright (C) 2011-2012 Kay Sievers + * Copyright (C) 2011 ProFUSION embedded systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "basic/string-util.h" +#include "udev.h" + +static struct kmod_ctx *ctx = NULL; + +static int load_module(struct udev *udev, const char *alias) { + struct kmod_list *list = NULL; + struct kmod_list *l; + int err; + + err = kmod_module_new_from_lookup(ctx, alias, &list); + if (err < 0) + return err; + + if (list == NULL) + log_debug("No module matches '%s'", alias); + + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + + err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); + if (err == KMOD_PROBE_APPLY_BLACKLIST) + log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod)); + else if (err == 0) + log_debug("Inserted '%s'", kmod_module_get_name(mod)); + else + log_debug("Failed to insert '%s'", kmod_module_get_name(mod)); + + kmod_module_unref(mod); + } + + kmod_module_unref_list(list); + return err; +} + +_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { + log_internalv(priority, 0, file, line, fn, format, args); +} + +static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) { + struct udev *udev = udev_device_get_udev(dev); + int i; + + if (!ctx) + return 0; + + if (argc < 3 || !streq(argv[1], "load")) { + log_error("expect: %s load ", argv[0]); + return EXIT_FAILURE; + } + + for (i = 2; argv[i]; i++) { + log_debug("Execute '%s' '%s'", argv[1], argv[i]); + load_module(udev, argv[i]); + } + + return EXIT_SUCCESS; +} + +/* called at udev startup and reload */ +static int builtin_kmod_init(struct udev *udev) { + if (ctx) + return 0; + + ctx = kmod_new(NULL, NULL); + if (!ctx) + return -ENOMEM; + + log_debug("Load module index"); + kmod_set_log_fn(ctx, udev_kmod_log, udev); + kmod_load_resources(ctx); + return 0; +} + +/* called on udev shutdown and reload request */ +static void builtin_kmod_exit(struct udev *udev) { + log_debug("Unload module index"); + ctx = kmod_unref(ctx); +} + +/* called every couple of seconds during event activity; 'true' if config has changed */ +static bool builtin_kmod_validate(struct udev *udev) { + log_debug("Validate module index"); + if (!ctx) + return false; + return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK); +} + +const struct udev_builtin udev_builtin_kmod = { + .name = "kmod", + .cmd = builtin_kmod, + .init = builtin_kmod_init, + .exit = builtin_kmod_exit, + .validate = builtin_kmod_validate, + .help = "Kernel module loader", + .run_once = false, +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-net_id.c b/src/grp-udev/libudev-core/udev-builtin-net_id.c new file mode 100644 index 0000000000..51f734d2c1 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-net_id.c @@ -0,0 +1,624 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* + * Predictable network interface device names based on: + * - firmware/bios-provided index numbers for on-board devices + * - firmware-provided pci-express hotplug slot index number + * - physical/geographical location of the hardware + * - the interface's MAC address + * + * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames + * + * Two character prefixes based on the type of interface: + * en — Ethernet + * sl — serial line IP (slip) + * wl — wlan + * ww — wwan + * + * Type of names: + * b — BCMA bus core number + * c — CCW bus group name, without leading zeros [s390] + * o[d] — on-board device index number + * s[f][d] — hotplug slot index number + * x — MAC address + * [P]ps[f][d] + * — PCI geographical location + * [P]ps[f][u][..][c][i] + * — USB port number chain + * + * All multi-function PCI devices will carry the [f] number in the + * device name, including the function 0 device. + * + * When using PCI geography, The PCI domain is only prepended when it is not 0. + * + * For USB devices the full chain of port numbers of hubs is composed. If the + * name gets longer than the maximum number of 15 characters, the name is not + * exported. + * The usual USB configuration == 1 and interface == 0 values are suppressed. + * + * 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: + * /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: + * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 + * ID_NET_NAME_MAC=enx78e7d1ea46da + * ID_NET_NAME_PATH=enp2s0f0 + * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 + * ID_NET_NAME_MAC=enx78e7d1ea46dc + * ID_NET_NAME_PATH=enp2s0f1 + * + * PCI wlan card: + * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 + * ID_NET_NAME_MAC=wlx0024d7e31130 + * ID_NET_NAME_PATH=wlp3s0 + * + * USB built-in 3G modem: + * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 + * ID_NET_NAME_MAC=wwx028037ec0200 + * ID_NET_NAME_PATH=wwp0s29u1u4i6 + * + * USB Android phone: + * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 + * ID_NET_NAME_MAC=enxd626b3450fb5 + * ID_NET_NAME_PATH=enp0s29u1u2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "udev.h" + +#define ONBOARD_INDEX_MAX (16*1024-1) + +enum netname_type{ + NET_UNDEF, + NET_PCI, + NET_USB, + NET_BCMA, + NET_VIRTIO, + NET_CCWGROUP, +}; + +struct netnames { + enum netname_type type; + + uint8_t mac[6]; + bool mac_valid; + + struct udev_device *pcidev; + char pci_slot[IFNAMSIZ]; + char pci_path[IFNAMSIZ]; + char pci_onboard[IFNAMSIZ]; + const char *pci_onboard_label; + + char usb_ports[IFNAMSIZ]; + char bcma_core[IFNAMSIZ]; + char ccw_group[IFNAMSIZ]; +}; + +/* retrieve on-board index number and label from firmware */ +static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { + unsigned dev_port = 0; + size_t l; + char *s; + const char *attr; + int idx; + + /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ + attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index"); + /* SMBIOS type 41 — Onboard Devices Extended Information */ + if (!attr) + attr = udev_device_get_sysattr_value(names->pcidev, "index"); + if (!attr) + return -ENOENT; + + idx = strtoul(attr, NULL, 0); + if (idx <= 0) + return -EINVAL; + + /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for + * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary + * cut-off, which is somewhere beyond the realistic number of physical network interface a system might + * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */ + if (idx > ONBOARD_INDEX_MAX) + return -ENOENT; + + /* kernel provided port index for multiple ports on a single PCI function */ + attr = udev_device_get_sysattr_value(dev, "dev_port"); + if (attr) + dev_port = strtol(attr, NULL, 10); + + s = names->pci_onboard; + l = sizeof(names->pci_onboard); + l = strpcpyf(&s, l, "o%d", idx); + if (dev_port > 0) + l = strpcpyf(&s, l, "d%d", dev_port); + if (l == 0) + names->pci_onboard[0] = '\0'; + + names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label"); + + return 0; +} + +/* read the 256 bytes PCI configuration space to check the multi-function bit */ +static bool is_pci_multifunction(struct udev_device *dev) { + _cleanup_close_ int fd = -1; + const char *filename; + uint8_t config[64]; + + filename = strjoina(udev_device_get_syspath(dev), "/config"); + fd = open(filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return false; + if (read(fd, &config, sizeof(config)) != sizeof(config)) + return false; + + /* bit 0-6 header type, bit 7 multi/single function device */ + if ((config[PCI_HEADER_TYPE] & 0x80) != 0) + return true; + + return false; +} + +static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + struct udev *udev = udev_device_get_udev(names->pcidev); + unsigned domain, bus, slot, func, dev_port = 0; + size_t l; + char *s; + const char *attr; + struct udev_device *pci = NULL; + char slots[256], str[256]; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + int hotplug_slot = 0, err = 0; + + if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4) + return -ENOENT; + + /* kernel provided port index for multiple ports on a single PCI function */ + attr = udev_device_get_sysattr_value(dev, "dev_port"); + if (attr) + dev_port = strtol(attr, NULL, 10); + + /* compose a name based on the raw kernel's PCI bus, slot numbers */ + s = names->pci_path; + l = sizeof(names->pci_path); + if (domain > 0) + l = strpcpyf(&s, l, "P%u", domain); + l = strpcpyf(&s, l, "p%us%u", bus, slot); + if (func > 0 || is_pci_multifunction(names->pcidev)) + l = strpcpyf(&s, l, "f%u", func); + if (dev_port > 0) + l = strpcpyf(&s, l, "d%u", dev_port); + if (l == 0) + names->pci_path[0] = '\0'; + + /* ACPI _SUN — slot user number */ + pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); + if (!pci) { + err = -ENOENT; + goto out; + } + xsprintf(slots, "%s/slots", udev_device_get_syspath(pci)); + dir = opendir(slots); + if (!dir) { + err = -errno; + goto out; + } + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + int i; + char *rest; + char *address; + + if (dent->d_name[0] == '.') + continue; + i = strtol(dent->d_name, &rest, 10); + if (rest[0] != '\0') + continue; + if (i < 1) + continue; + 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))) + hotplug_slot = i; + free(address); + } + + if (hotplug_slot > 0) + break; + } + + if (hotplug_slot > 0) { + s = names->pci_slot; + l = sizeof(names->pci_slot); + if (domain > 0) + l = strpcpyf(&s, l, "P%d", domain); + l = strpcpyf(&s, l, "s%d", hotplug_slot); + if (func > 0 || is_pci_multifunction(names->pcidev)) + l = strpcpyf(&s, l, "f%d", func); + if (dev_port > 0) + l = strpcpyf(&s, l, "d%d", dev_port); + if (l == 0) + names->pci_slot[0] = '\0'; + } +out: + udev_device_unref(pci); + return err; +} + +static int names_pci(struct udev_device *dev, struct netnames *names) { + struct udev_device *parent; + + assert(dev); + assert(names); + + parent = udev_device_get_parent(dev); + + /* there can only ever be one virtio bus per parent device, so we can + safely ignore any virtio buses. see + */ + while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) + parent = udev_device_get_parent(parent); + + if (!parent) + return -ENOENT; + + /* check if our direct parent is a PCI device with no other bus in-between */ + if (streq_ptr("pci", udev_device_get_subsystem(parent))) { + names->type = NET_PCI; + names->pcidev = parent; + } else { + names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); + if (!names->pcidev) + return -ENOENT; + } + dev_pci_onboard(dev, names); + dev_pci_slot(dev, names); + return 0; +} + +static int names_usb(struct udev_device *dev, struct netnames *names) { + struct udev_device *usbdev; + char name[256]; + char *ports; + char *config; + char *interf; + size_t l; + char *s; + + assert(dev); + assert(names); + + usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); + if (!usbdev) + return -ENOENT; + + /* get USB port number chain, configuration, interface */ + strscpy(name, sizeof(name), udev_device_get_sysname(usbdev)); + s = strchr(name, '-'); + if (!s) + return -EINVAL; + ports = s+1; + + s = strchr(ports, ':'); + if (!s) + return -EINVAL; + s[0] = '\0'; + config = s+1; + + s = strchr(config, '.'); + if (!s) + return -EINVAL; + s[0] = '\0'; + interf = s+1; + + /* prefix every port number in the chain with "u" */ + s = ports; + while ((s = strchr(s, '.'))) + s[0] = 'u'; + s = names->usb_ports; + l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL); + + /* append USB config number, suppress the common config == 1 */ + if (!streq(config, "1")) + l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL); + + /* append USB interface number, suppress the interface == 0 */ + if (!streq(interf, "0")) + l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL); + if (l == 0) + return -ENAMETOOLONG; + + names->type = NET_USB; + return 0; +} + +static int names_bcma(struct udev_device *dev, struct netnames *names) { + struct udev_device *bcmadev; + unsigned int core; + + assert(dev); + assert(names); + + bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL); + if (!bcmadev) + return -ENOENT; + + /* bus num:core num */ + if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1) + return -EINVAL; + /* suppress the common core == 0 */ + if (core > 0) + xsprintf(names->bcma_core, "b%u", core); + + names->type = NET_BCMA; + return 0; +} + +static int names_ccw(struct udev_device *dev, struct netnames *names) { + struct udev_device *cdev; + const char *bus_id; + size_t bus_id_len; + int rc; + + assert(dev); + assert(names); + + /* Retrieve the associated CCW device */ + cdev = udev_device_get_parent(dev); + if (!cdev) + return -ENOENT; + + /* Network devices are always grouped CCW devices */ + if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev))) + return -ENOENT; + + /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely + * identifies the network device on the Linux on System z channel + * subsystem. Note that the bus-ID contains lowercase characters. + */ + bus_id = udev_device_get_sysname(cdev); + if (!bus_id) + return -ENOENT; + + /* Check the length of the bus-ID. Rely on that the kernel provides + * a correct bus-ID; alternatively, improve this check and parse and + * verify each bus-ID part... + */ + bus_id_len = strlen(bus_id); + if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) + return -EINVAL; + + /* Strip leading zeros from the bus id for aesthetic purposes. This + * keeps the ccw names stable, yet much shorter in general case of + * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is + * not prepended when it is zero. + */ + bus_id += strspn(bus_id, ".0"); + + /* Store the CCW bus-ID for use as network device name */ + rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id); + if (rc >= 0 && rc < (int)sizeof(names->ccw_group)) + names->type = NET_CCWGROUP; + return 0; +} + +static int names_mac(struct udev_device *dev, struct netnames *names) { + const char *s; + unsigned int i; + unsigned int a1, a2, a3, a4, a5, a6; + + /* check for NET_ADDR_PERM, skip random MAC addresses */ + s = udev_device_get_sysattr_value(dev, "addr_assign_type"); + if (!s) + return EXIT_FAILURE; + i = strtoul(s, NULL, 0); + if (i != 0) + return 0; + + s = udev_device_get_sysattr_value(dev, "address"); + if (!s) + return -ENOENT; + if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) + return -EINVAL; + + /* skip empty MAC addresses */ + if (a1 + a2 + a3 + a4 + a5 + a6 == 0) + return -EINVAL; + + names->mac[0] = a1; + names->mac[1] = a2; + names->mac[2] = a3; + names->mac[3] = a4; + names->mac[4] = a5; + names->mac[5] = a6; + names->mac_valid = true; + return 0; +} + +/* IEEE Organizationally Unique Identifier vendor string */ +static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) { + char str[32]; + + if (!names->mac_valid) + return -ENOENT; + /* skip commonly misused 00:00:00 (Xerox) prefix */ + if (memcmp(names->mac, "\0\0\0", 3) == 0) + return -EINVAL; + 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; +} + +static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) { + const char *s; + const char *p; + unsigned int i; + const char *devtype; + const char *prefix = "en"; + struct netnames names = {}; + int err; + + /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */ + s = udev_device_get_sysattr_value(dev, "type"); + if (!s) + return EXIT_FAILURE; + i = strtoul(s, NULL, 0); + switch (i) { + case ARPHRD_ETHER: + prefix = "en"; + break; + case ARPHRD_SLIP: + prefix = "sl"; + break; + default: + return 0; + } + + /* skip stacked devices, like VLANs, ... */ + s = udev_device_get_sysattr_value(dev, "ifindex"); + if (!s) + return EXIT_FAILURE; + p = udev_device_get_sysattr_value(dev, "iflink"); + if (!p) + return EXIT_FAILURE; + if (!streq(s, p)) + return 0; + + devtype = udev_device_get_devtype(dev); + if (devtype) { + if (streq("wlan", devtype)) + prefix = "wl"; + else if (streq("wwan", devtype)) + prefix = "ww"; + } + + err = names_mac(dev, &names); + if (err >= 0 && names.mac_valid) { + char str[IFNAMSIZ]; + + 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); + + ieee_oui(dev, &names, test); + } + + /* get path names for Linux on System z network devices */ + err = names_ccw(dev, &names); + if (err >= 0 && names.type == NET_CCWGROUP) { + char str[IFNAMSIZ]; + + if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + goto out; + } + + /* get PCI based path names, we compose only PCI based paths */ + err = names_pci(dev, &names); + if (err < 0) + goto out; + + /* plain PCI device */ + if (names.type == NET_PCI) { + char str[IFNAMSIZ]; + + if (names.pci_onboard[0]) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str); + + if (names.pci_onboard_label) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str); + + if (names.pci_path[0]) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + + if (names.pci_slot[0]) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); + goto out; + } + + /* USB device */ + err = names_usb(dev, &names); + if (err >= 0 && names.type == NET_USB) { + char str[IFNAMSIZ]; + + if (names.pci_path[0]) + if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + + if (names.pci_slot[0]) + if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); + goto out; + } + + /* Broadcom bus */ + err = names_bcma(dev, &names); + if (err >= 0 && names.type == NET_BCMA) { + char str[IFNAMSIZ]; + + if (names.pci_path[0]) + if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + + if (names.pci_slot[0]) + if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); + goto out; + } +out: + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_net_id = { + .name = "net_id", + .cmd = builtin_net_id, + .help = "Network device properties", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-net_setup_link.c b/src/grp-udev/libudev-core/udev-builtin-net_setup_link.c new file mode 100644 index 0000000000..bf36b42957 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-net_setup_link.c @@ -0,0 +1,107 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "link-config.h" +#include "udev.h" + +static link_config_ctx *ctx = NULL; + +static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv, bool test) { + _cleanup_free_ char *driver = NULL; + const char *name = NULL; + link_config *link; + int r; + + if (argc > 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + r = link_get_driver(ctx, dev, &driver); + if (r >= 0) + udev_builtin_add_property(dev, test, "ID_NET_DRIVER", driver); + + r = link_config_get(ctx, dev, &link); + if (r < 0) { + if (r == -ENOENT) { + log_debug("No matching link configuration found."); + return EXIT_SUCCESS; + } else { + log_error_errno(r, "Could not get link config: %m"); + return EXIT_FAILURE; + } + } + + r = link_config_apply(ctx, link, dev, &name); + if (r < 0) { + log_error_errno(r, "Could not apply link config to %s: %m", udev_device_get_sysname(dev)); + return EXIT_FAILURE; + } + + udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->filename); + + if (name) + udev_builtin_add_property(dev, test, "ID_NET_NAME", name); + + return EXIT_SUCCESS; +} + +static int builtin_net_setup_link_init(struct udev *udev) { + int r; + + if (ctx) + return 0; + + r = link_config_ctx_new(&ctx); + if (r < 0) + return r; + + r = link_config_load(ctx); + if (r < 0) + return r; + + log_debug("Created link configuration context."); + return 0; +} + +static void builtin_net_setup_link_exit(struct udev *udev) { + link_config_ctx_free(ctx); + ctx = NULL; + log_debug("Unloaded link configuration context."); +} + +static bool builtin_net_setup_link_validate(struct udev *udev) { + log_debug("Check if link configuration needs reloading."); + if (!ctx) + return false; + + return link_config_should_reload(ctx); +} + +const struct udev_builtin udev_builtin_net_setup_link = { + .name = "net_setup_link", + .cmd = builtin_net_setup_link, + .init = builtin_net_setup_link_init, + .exit = builtin_net_setup_link_exit, + .validate = builtin_net_setup_link_validate, + .help = "Configure network link", + .run_once = false, +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-path_id.c b/src/grp-udev/libudev-core/udev-builtin-path_id.c new file mode 100644 index 0000000000..4476b90c72 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-path_id.c @@ -0,0 +1,761 @@ +/* + * compose persistent device path + * + * Copyright (C) 2009-2011 Kay Sievers + * + * Logic based on Hannes Reinecke's shell script. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "udev.h" + +_printf_(2,3) +static int path_prepend(char **path, const char *fmt, ...) { + va_list va; + char *pre; + int err = 0; + + va_start(va, fmt); + err = vasprintf(&pre, fmt, va); + va_end(va); + if (err < 0) + goto out; + + if (*path != NULL) { + char *new; + + err = asprintf(&new, "%s-%s", pre, *path); + free(pre); + if (err < 0) + goto out; + free(*path); + *path = new; + } else { + *path = pre; + } +out: + return err; +} + +/* +** Linux only supports 32 bit luns. +** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. +*/ +static int format_lun_number(struct udev_device *dev, char **path) { + unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); + + /* address method 0, peripheral device addressing with bus id of zero */ + if (lun < 256) + return path_prepend(path, "lun-%lu", lun); + /* handle all other lun addressing methods by using a variant of the original lun format */ + return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff); +} + +static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) { + struct udev_device *parent = dev; + + assert(dev); + assert(subsys); + + while (parent != NULL) { + const char *subsystem; + + subsystem = udev_device_get_subsystem(parent); + if (subsystem == NULL || !streq(subsystem, subsys)) + break; + dev = parent; + parent = udev_device_get_parent(parent); + } + return dev; +} + +static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) { + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *fcdev = NULL; + const char *port; + char *lun = NULL; + + assert(parent); + assert(path); + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); + if (fcdev == NULL) + return NULL; + port = udev_device_get_sysattr_value(fcdev, "port_name"); + if (port == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "fc-%s-%s", port, lun); + free(lun); +out: + udev_device_unref(fcdev); + return parent; +} + +static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) { + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *target_parent; + struct udev_device *sasdev; + const char *sas_address; + char *lun = NULL; + + assert(parent); + assert(path); + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + target_parent = udev_device_get_parent(targetdev); + if (target_parent == NULL) + return NULL; + + sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", + udev_device_get_sysname(target_parent)); + if (sasdev == NULL) + return NULL; + + sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); + if (sas_address == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "sas-%s-%s", sas_address, lun); + free(lun); +out: + udev_device_unref(sasdev); + return parent; +} + +static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) +{ + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *target_parent; + struct udev_device *port; + struct udev_device *expander; + struct udev_device *target_sasdev = NULL; + struct udev_device *expander_sasdev = NULL; + struct udev_device *port_sasdev = NULL; + const char *sas_address = NULL; + const char *phy_id; + const char *phy_count; + char *lun = NULL; + + assert(parent); + assert(path); + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + target_parent = udev_device_get_parent(targetdev); + if (target_parent == NULL) + return NULL; + + /* Get sas device */ + target_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_device", udev_device_get_sysname(target_parent)); + if (target_sasdev == NULL) + return NULL; + + /* The next parent is sas port */ + port = udev_device_get_parent(target_parent); + if (port == NULL) { + parent = NULL; + goto out; + } + + /* Get port device */ + port_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_port", udev_device_get_sysname(port)); + + phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys"); + if (phy_count == NULL) { + parent = NULL; + goto out; + } + + /* Check if we are simple disk */ + if (strncmp(phy_count, "1", 2) != 0) { + parent = handle_scsi_sas_wide_port(parent, path); + goto out; + } + + /* Get connected phy */ + phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier"); + if (phy_id == NULL) { + parent = NULL; + goto out; + } + + /* The port's parent is either hba or expander */ + expander = udev_device_get_parent(port); + if (expander == NULL) { + parent = NULL; + goto out; + } + + /* Get expander device */ + expander_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_device", udev_device_get_sysname(expander)); + if (expander_sasdev != NULL) { + /* Get expander's address */ + sas_address = udev_device_get_sysattr_value(expander_sasdev, + "sas_address"); + if (sas_address == NULL) { + parent = NULL; + goto out; + } + } + + format_lun_number(parent, &lun); + if (sas_address) + path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun); + else + path_prepend(path, "sas-phy%s-%s", phy_id, lun); + + free(lun); +out: + udev_device_unref(target_sasdev); + udev_device_unref(expander_sasdev); + udev_device_unref(port_sasdev); + return parent; +} + +static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) { + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *transportdev; + struct udev_device *sessiondev = NULL; + const char *target; + char *connname; + struct udev_device *conndev = NULL; + const char *addr; + const char *port; + char *lun = NULL; + + assert(parent); + assert(path); + + /* find iscsi session */ + transportdev = parent; + for (;;) { + transportdev = udev_device_get_parent(transportdev); + if (transportdev == NULL) + return NULL; + if (startswith(udev_device_get_sysname(transportdev), "session")) + break; + } + + /* find iscsi session device */ + sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); + if (sessiondev == NULL) + return NULL; + target = udev_device_get_sysattr_value(sessiondev, "targetname"); + if (target == NULL) { + parent = NULL; + goto out; + } + + if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { + parent = NULL; + goto out; + } + conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); + free(connname); + if (conndev == NULL) { + parent = NULL; + goto out; + } + addr = udev_device_get_sysattr_value(conndev, "persistent_address"); + port = udev_device_get_sysattr_value(conndev, "persistent_port"); + if (addr == NULL || port == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); + free(lun); +out: + udev_device_unref(sessiondev); + udev_device_unref(conndev); + return parent; +} + +static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) { + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *target_parent; + struct udev_device *atadev; + const char *port_no; + + assert(parent); + assert(path); + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); + if (!targetdev) + return NULL; + + target_parent = udev_device_get_parent(targetdev); + if (!target_parent) + return NULL; + + atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent)); + if (!atadev) + return NULL; + + port_no = udev_device_get_sysattr_value(atadev, "port_no"); + if (!port_no) { + parent = NULL; + goto out; + } + path_prepend(path, "ata-%s", port_no); +out: + udev_device_unref(atadev); + return parent; +} + +static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) { + struct udev_device *hostdev; + int host, bus, target, lun; + const char *name; + char *base; + char *pos; + DIR *dir; + struct dirent *dent; + int basenum; + + assert(parent); + assert(path); + + hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); + if (hostdev == NULL) + return NULL; + + name = udev_device_get_sysname(parent); + if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) + return NULL; + + /* + * Rebase host offset to get the local relative number + * + * Note: This is by definition racy, unreliable and too simple. + * Please do not copy this model anywhere. It's just a left-over + * from the time we had no idea how things should look like in + * the end. + * + * Making assumptions about a global in-kernel counter and use + * that to calculate a local offset is a very broken concept. It + * can only work as long as things are in strict order. + * + * The kernel needs to export the instance/port number of a + * controller directly, without the need for rebase magic like + * this. Manual driver unbind/bind, parallel hotplug/unplug will + * get into the way of this "I hope it works" logic. + */ + basenum = -1; + base = strdup(udev_device_get_syspath(hostdev)); + if (base == NULL) + return NULL; + pos = strrchr(base, '/'); + if (pos == NULL) { + parent = NULL; + goto out; + } + pos[0] = '\0'; + dir = opendir(base); + if (dir == NULL) { + parent = NULL; + goto out; + } + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *rest; + int i; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) + continue; + if (!startswith(dent->d_name, "host")) + continue; + i = strtoul(&dent->d_name[4], &rest, 10); + if (rest[0] != '\0') + continue; + /* + * find the smallest number; the host really needs to export its + * own instance number per parent device; relying on the global host + * enumeration and plainly rebasing the numbers sounds unreliable + */ + if (basenum == -1 || i < basenum) + basenum = i; + } + closedir(dir); + if (basenum == -1) { + parent = NULL; + goto out; + } + host -= basenum; + + path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); +out: + free(base); + return hostdev; +} + +static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) { + struct udev_device *hostdev; + struct udev_device *vmbusdev; + const char *guid_str; + char *lun = NULL; + char guid[38]; + size_t i, k; + + assert(parent); + assert(path); + + hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); + if (!hostdev) + return NULL; + + vmbusdev = udev_device_get_parent(hostdev); + if (!vmbusdev) + return NULL; + + guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id"); + if (!guid_str) + return NULL; + + if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}') + return NULL; + + for (i = 1, k = 0; i < 36; i++) { + if (guid_str[i] == '-') + continue; + guid[k++] = guid_str[i]; + } + guid[k] = '\0'; + + format_lun_number(parent, &lun); + path_prepend(path, "vmbus-%s-%s", guid, lun); + free(lun); + return parent; +} + +static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) { + const char *devtype; + const char *name; + const char *id; + + devtype = udev_device_get_devtype(parent); + if (devtype == NULL || !streq(devtype, "scsi_device")) + return parent; + + /* firewire */ + id = udev_device_get_sysattr_value(parent, "ieee1394_id"); + if (id != NULL) { + parent = skip_subsystem(parent, "scsi"); + path_prepend(path, "ieee1394-0x%s", id); + *supported_parent = true; + goto out; + } + + /* scsi sysfs does not have a "subsystem" for the transport */ + name = udev_device_get_syspath(parent); + + if (strstr(name, "/rport-") != NULL) { + parent = handle_scsi_fibre_channel(parent, path); + *supported_parent = true; + goto out; + } + + if (strstr(name, "/end_device-") != NULL) { + parent = handle_scsi_sas(parent, path); + *supported_parent = true; + goto out; + } + + if (strstr(name, "/session") != NULL) { + parent = handle_scsi_iscsi(parent, path); + *supported_parent = true; + goto out; + } + + if (strstr(name, "/ata") != NULL) { + parent = handle_scsi_ata(parent, path); + goto out; + } + + if (strstr(name, "/vmbus_") != NULL) { + parent = handle_scsi_hyperv(parent, path); + goto out; + } + + parent = handle_scsi_default(parent, path); +out: + return parent; +} + +static struct udev_device *handle_cciss(struct udev_device *parent, char **path) { + const char *str; + unsigned int controller, disk; + + str = udev_device_get_sysname(parent); + if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) + return NULL; + + path_prepend(path, "cciss-disk%u", disk); + parent = skip_subsystem(parent, "cciss"); + return parent; +} + +static void handle_scsi_tape(struct udev_device *dev, char **path) { + const char *name; + + /* must be the last device in the syspath */ + if (*path != NULL) + return; + + name = udev_device_get_sysname(dev); + if (startswith(name, "nst") && strchr("lma", name[3]) != NULL) + path_prepend(path, "nst%c", name[3]); + else if (startswith(name, "st") && strchr("lma", name[2]) != NULL) + path_prepend(path, "st%c", name[2]); +} + +static struct udev_device *handle_usb(struct udev_device *parent, char **path) { + const char *devtype; + const char *str; + const char *port; + + devtype = udev_device_get_devtype(parent); + if (devtype == NULL) + return parent; + if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device")) + return parent; + + str = udev_device_get_sysname(parent); + port = strchr(str, '-'); + if (port == NULL) + return parent; + port++; + + parent = skip_subsystem(parent, "usb"); + path_prepend(path, "usb-0:%s", port); + return parent; +} + +static struct udev_device *handle_bcma(struct udev_device *parent, char **path) { + const char *sysname; + unsigned int core; + + sysname = udev_device_get_sysname(parent); + if (sscanf(sysname, "bcma%*u:%u", &core) != 1) + return NULL; + + path_prepend(path, "bcma-%u", core); + return parent; +} + +/* 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(path); + + type = udev_device_get_sysattr_value(parent, "type"); + func = udev_device_get_sysattr_value(parent, "ap_functions"); + + 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, "ap"); + return parent; +} + +static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) { + struct udev_device *parent; + char *path = NULL; + bool supported_transport = false; + bool supported_parent = false; + + assert(dev); + + /* walk up the chain of devices and compose path */ + parent = dev; + while (parent != NULL) { + const char *subsys; + + subsys = udev_device_get_subsystem(parent); + if (subsys == NULL) { + ; + } else if (streq(subsys, "scsi_tape")) { + handle_scsi_tape(parent, &path); + } else if (streq(subsys, "scsi")) { + parent = handle_scsi(parent, &path, &supported_parent); + supported_transport = true; + } else if (streq(subsys, "cciss")) { + parent = handle_cciss(parent, &path); + supported_transport = true; + } else if (streq(subsys, "usb")) { + parent = handle_usb(parent, &path); + supported_transport = true; + } else if (streq(subsys, "bcma")) { + parent = handle_bcma(parent, &path); + supported_transport = true; + } else if (streq(subsys, "serio")) { + path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); + parent = skip_subsystem(parent, "serio"); + } else if (streq(subsys, "pci")) { + path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "pci"); + supported_parent = true; + } else if (streq(subsys, "platform")) { + path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "platform"); + supported_transport = true; + supported_parent = true; + } else if (streq(subsys, "acpi")) { + path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "acpi"); + supported_parent = true; + } else if (streq(subsys, "xen")) { + path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "xen"); + supported_parent = true; + } else if (streq(subsys, "virtio")) { + while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) + parent = udev_device_get_parent(parent); + path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); + supported_transport = true; + supported_parent = true; + } else if (streq(subsys, "scm")) { + path_prepend(&path, "scm-%s", udev_device_get_sysname(parent)); + 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) + parent = udev_device_get_parent(parent); + } + + /* + * Do not return devices with an unknown parent device type. They + * might produce conflicting IDs if the parent does not provide a + * unique and predictable name. + */ + if (!supported_parent) + path = mfree(path); + + /* + * Do not return block devices without a well-known transport. Some + * devices do not expose their buses and do not provide a unique + * and predictable name that way. + */ + if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport) + path = mfree(path); + + if (path != NULL) { + char tag[UTIL_NAME_SIZE]; + size_t i; + const char *p; + + /* compose valid udev tag name */ + for (p = path, i = 0; *p; p++) { + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z') || + *p == '-') { + tag[i++] = *p; + continue; + } + + /* skip all leading '_' */ + if (i == 0) + continue; + + /* avoid second '_' */ + if (tag[i-1] == '_') + continue; + + tag[i++] = '_'; + } + /* strip trailing '_' */ + while (i > 0 && tag[i-1] == '_') + i--; + tag[i] = '\0'; + + udev_builtin_add_property(dev, test, "ID_PATH", path); + udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); + free(path); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +const struct udev_builtin udev_builtin_path_id = { + .name = "path_id", + .cmd = builtin_path_id, + .help = "Compose persistent device path", + .run_once = true, +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-uaccess.c b/src/grp-udev/libudev-core/udev-builtin-uaccess.c new file mode 100644 index 0000000000..aab1c376de --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-uaccess.c @@ -0,0 +1,89 @@ +/* + * manage device node user ACL + * + * Copyright 2010-2012 Kay Sievers + * Copyright 2010 Lennart Poettering + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include + +#include "basic/login-util.h" +#include "basic/util.h" +#include "udev.h" + +#include "logind-acl.h" + +static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool test) { + int r; + const char *path = NULL, *seat; + bool changed_acl = false; + uid_t uid; + + umask(0022); + + /* don't muck around with ACLs when the system is not running systemd */ + if (!logind_running()) + return 0; + + path = udev_device_get_devnode(dev); + seat = udev_device_get_property_value(dev, "ID_SEAT"); + if (!seat) + seat = "seat0"; + + r = sd_seat_get_active(seat, NULL, &uid); + if (r == -ENXIO || r == -ENODATA) { + /* No active session on this seat */ + r = 0; + goto finish; + } else if (r < 0) { + log_error("Failed to determine active user on seat %s.", seat); + goto finish; + } + + r = devnode_acl(path, true, false, 0, true, uid); + if (r < 0) { + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path); + goto finish; + } + + changed_acl = true; + r = 0; + +finish: + if (path && !changed_acl) { + int k; + + /* Better be safe than sorry and reset ACL */ + k = devnode_acl(path, true, false, 0, false, 0); + if (k < 0) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL on %s: %m", path); + if (r >= 0) + r = k; + } + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_uaccess = { + .name = "uaccess", + .cmd = builtin_uaccess, + .help = "Manage device node user ACL", +}; diff --git a/src/grp-udev/libudev-core/udev-builtin-usb_id.c b/src/grp-udev/libudev-core/udev-builtin-usb_id.c new file mode 100644 index 0000000000..fcaf8551d8 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin-usb_id.c @@ -0,0 +1,473 @@ +/* + * USB device properties and persistent device path + * + * Copyright (c) 2005 SUSE Linux Products GmbH, Germany + * Author: Hannes Reinecke + * + * Copyright (C) 2005-2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "udev.h" + +static void set_usb_iftype(char *to, int if_class_num, size_t len) { + const char *type = "generic"; + + switch (if_class_num) { + case 1: + type = "audio"; + break; + case 2: /* CDC-Control */ + break; + case 3: + type = "hid"; + break; + case 5: /* Physical */ + break; + case 6: + type = "media"; + break; + case 7: + type = "printer"; + break; + case 8: + type = "storage"; + break; + case 9: + type = "hub"; + break; + case 0x0a: /* CDC-Data */ + break; + case 0x0b: /* Chip/Smart Card */ + break; + case 0x0d: /* Content Security */ + break; + case 0x0e: + type = "video"; + break; + case 0xdc: /* Diagnostic Device */ + break; + case 0xe0: /* Wireless Controller */ + break; + case 0xfe: /* Application-specific */ + break; + case 0xff: /* Vendor-specific */ + break; + default: + break; + } + strncpy(to, type, len); + to[len-1] = '\0'; +} + +static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) { + int type_num = 0; + char *eptr; + const char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 1: /* RBC devices */ + type = "rbc"; + break; + case 2: + type = "atapi"; + break; + case 3: + type = "tape"; + break; + case 4: /* UFI */ + type = "floppy"; + break; + case 6: /* Transparent SPC-2 devices */ + type = "scsi"; + break; + default: + break; + } + } + strscpy(to, len, type); + return type_num; +} + +static void set_scsi_type(char *to, const char *from, size_t len) { + int type_num; + char *eptr; + const char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 0: + case 0xe: + type = "disk"; + break; + case 1: + type = "tape"; + break; + case 4: + case 7: + case 0xf: + type = "optical"; + break; + case 5: + type = "cd"; + break; + default: + break; + } + } + strscpy(to, len, type); +} + +#define USB_DT_DEVICE 0x01 +#define USB_DT_INTERFACE 0x04 + +static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) { + _cleanup_free_ char *filename = NULL; + _cleanup_close_ int fd = -1; + ssize_t size; + unsigned char buf[18 + 65535]; + size_t pos = 0; + unsigned strpos = 0; + struct usb_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + } _packed_; + + if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) + return log_oom(); + + fd = open(filename, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m"); + + size = read(fd, buf, sizeof(buf)); + if (size < 18 || size == sizeof(buf)) + return -EIO; + + ifs_str[0] = '\0'; + while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size && + strpos + 7 < len - 2) { + + struct usb_interface_descriptor *desc; + char if_str[8]; + + desc = (struct usb_interface_descriptor *) &buf[pos]; + if (desc->bLength < 3) + break; + pos += desc->bLength; + + if (desc->bDescriptorType != USB_DT_INTERFACE) + continue; + + if (snprintf(if_str, 8, ":%02x%02x%02x", + desc->bInterfaceClass, + desc->bInterfaceSubClass, + desc->bInterfaceProtocol) != 7) + continue; + + if (strstr(ifs_str, if_str) != NULL) + continue; + + memcpy(&ifs_str[strpos], if_str, 8), + strpos += 7; + } + + if (strpos > 0) { + ifs_str[strpos++] = ':'; + ifs_str[strpos++] = '\0'; + } + + return 0; +} + +/* + * A unique USB identification is generated like this: + * + * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass + * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC', + * use the SCSI vendor and model as USB-Vendor and USB-model. + * 3.) Otherwise, use the USB manufacturer and product as + * USB-Vendor and USB-model. Any non-printable characters + * in those strings will be skipped; a slash '/' will be converted + * into a full stop '.'. + * 4.) If that fails, too, we will use idVendor and idProduct + * as USB-Vendor and USB-model. + * 5.) The USB identification is the USB-vendor and USB-model + * string concatenated with an underscore '_'. + * 6.) If the device supplies a serial number, this number + * is concatenated with the identification with an underscore '_'. + */ +static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) { + char vendor_str[64] = ""; + char vendor_str_enc[256]; + const char *vendor_id; + char model_str[64] = ""; + char model_str_enc[256]; + const char *product_id; + char serial_str[UTIL_NAME_SIZE] = ""; + char packed_if_str[UTIL_NAME_SIZE] = ""; + char revision_str[64] = ""; + char type_str[64] = ""; + char instance_str[64] = ""; + const char *ifnum = NULL; + const char *driver = NULL; + char serial[256]; + + struct udev_device *dev_interface = NULL; + struct udev_device *dev_usb = NULL; + const char *if_class, *if_subclass; + int if_class_num; + int protocol = 0; + size_t l; + char *s; + + assert(dev); + + /* shortcut, if we are called directly for a "usb_device" type */ + if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) { + dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); + dev_usb = dev; + goto fallback; + } + + /* usb interface directory */ + dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); + if (dev_interface == NULL) { + log_debug("unable to access usb_interface device of '%s'", + udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); + driver = udev_device_get_sysattr_value(dev_interface, "driver"); + + if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); + if (!if_class) { + log_debug("%s: cannot get bInterfaceClass attribute", + udev_device_get_sysname(dev)); + return EXIT_FAILURE; + } + + if_class_num = strtoul(if_class, NULL, 16); + if (if_class_num == 8) { + /* mass storage */ + if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); + if (if_subclass != NULL) + protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); + } else { + set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); + } + + log_debug("%s: if_class %d protocol %d", + udev_device_get_syspath(dev_interface), if_class_num, protocol); + + /* usb device directory */ + dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); + if (!dev_usb) { + log_debug("unable to find parent 'usb' device of '%s'", + udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + /* all interfaces of the device in a single string */ + dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); + + /* mass storage : SCSI or ATAPI */ + if (protocol == 6 || protocol == 2) { + struct udev_device *dev_scsi; + const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; + int host, bus, target, lun; + + /* get scsi device */ + dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (dev_scsi == NULL) { + log_debug("unable to find parent 'scsi' device of '%s'", + udev_device_get_syspath(dev)); + goto fallback; + } + if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { + log_debug("invalid scsi device '%s'", udev_device_get_sysname(dev_scsi)); + goto fallback; + } + + /* Generic SPC-2 device */ + scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); + if (!scsi_vendor) { + log_debug("%s: cannot get SCSI vendor attribute", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); + util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); + util_replace_chars(vendor_str, NULL); + + scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); + if (!scsi_model) { + log_debug("%s: cannot get SCSI model attribute", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); + util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); + util_replace_chars(model_str, NULL); + + scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); + if (!scsi_type) { + log_debug("%s: cannot get SCSI type attribute", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); + + scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); + if (!scsi_rev) { + log_debug("%s: cannot get SCSI revision attribute", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); + util_replace_chars(revision_str, NULL); + + /* + * some broken devices have the same identifiers + * for all luns, export the target:lun number + */ + sprintf(instance_str, "%d:%d", target, lun); + } + +fallback: + vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); + product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); + + /* fallback to USB vendor & device */ + if (vendor_str[0] == '\0') { + const char *usb_vendor = NULL; + + usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); + if (!usb_vendor) + usb_vendor = vendor_id; + if (!usb_vendor) { + log_debug("No USB vendor information available"); + return EXIT_FAILURE; + } + udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); + util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); + util_replace_chars(vendor_str, NULL); + } + + if (model_str[0] == '\0') { + const char *usb_model = NULL; + + usb_model = udev_device_get_sysattr_value(dev_usb, "product"); + if (!usb_model) + usb_model = product_id; + if (!usb_model) + return EXIT_FAILURE; + udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); + util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); + util_replace_chars(model_str, NULL); + } + + if (revision_str[0] == '\0') { + const char *usb_rev; + + usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); + if (usb_rev) { + util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); + util_replace_chars(revision_str, NULL); + } + } + + if (serial_str[0] == '\0') { + const char *usb_serial; + + usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); + if (usb_serial) { + const unsigned char *p; + + /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */ + for (p = (unsigned char *)usb_serial; *p != '\0'; p++) + if (*p < 0x20 || *p > 0x7f || *p == ',') { + usb_serial = NULL; + break; + } + } + + if (usb_serial) { + util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); + util_replace_chars(serial_str, NULL); + } + } + + s = serial; + l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); + if (!isempty(serial_str)) + l = strpcpyl(&s, l, "_", serial_str, NULL); + + if (!isempty(instance_str)) + strpcpyl(&s, l, "-", instance_str, NULL); + + udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); + udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); + udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); + udev_builtin_add_property(dev, test, "ID_MODEL", model_str); + udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); + udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); + udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); + udev_builtin_add_property(dev, test, "ID_SERIAL", serial); + if (!isempty(serial_str)) + udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); + if (!isempty(type_str)) + udev_builtin_add_property(dev, test, "ID_TYPE", type_str); + if (!isempty(instance_str)) + udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); + udev_builtin_add_property(dev, test, "ID_BUS", "usb"); + if (!isempty(packed_if_str)) + udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); + if (ifnum != NULL) + udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); + if (driver != NULL) + udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_usb_id = { + .name = "usb_id", + .cmd = builtin_usb_id, + .help = "USB device properties", + .run_once = true, +}; diff --git a/src/grp-udev/libudev-core/udev-builtin.c b/src/grp-udev/libudev-core/udev-builtin.c new file mode 100644 index 0000000000..13d3cd4ee0 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-builtin.c @@ -0,0 +1,142 @@ +/*** + This file is part of systemd. + + Copyright 2007-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/string-util.h" +#include "udev.h" + +static bool initialized; + +static const struct udev_builtin *builtins[] = { +#ifdef HAVE_BLKID + [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, +#endif + [UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs, + [UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb, + [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, + [UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard, +#ifdef HAVE_KMOD + [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, +#endif + [UDEV_BUILTIN_NET_ID] = &udev_builtin_net_id, + [UDEV_BUILTIN_NET_LINK] = &udev_builtin_net_setup_link, + [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, + [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, +#ifdef HAVE_ACL + [UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess, +#endif +}; + +void udev_builtin_init(struct udev *udev) { + unsigned int i; + + if (initialized) + return; + + for (i = 0; i < ELEMENTSOF(builtins); i++) + if (builtins[i] && builtins[i]->init) + builtins[i]->init(udev); + + initialized = true; +} + +void udev_builtin_exit(struct udev *udev) { + unsigned int i; + + if (!initialized) + return; + + for (i = 0; i < ELEMENTSOF(builtins); i++) + if (builtins[i] && builtins[i]->exit) + builtins[i]->exit(udev); + + initialized = false; +} + +bool udev_builtin_validate(struct udev *udev) { + unsigned int i; + + for (i = 0; i < ELEMENTSOF(builtins); i++) + if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev)) + return true; + return false; +} + +void udev_builtin_list(struct udev *udev) { + unsigned int i; + + for (i = 0; i < ELEMENTSOF(builtins); i++) + if (builtins[i]) + fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help); +} + +const char *udev_builtin_name(enum udev_builtin_cmd cmd) { + if (!builtins[cmd]) + return NULL; + + return builtins[cmd]->name; +} + +bool udev_builtin_run_once(enum udev_builtin_cmd cmd) { + if (!builtins[cmd]) + return false; + + return builtins[cmd]->run_once; +} + +enum udev_builtin_cmd udev_builtin_lookup(const char *command) { + char name[UTIL_PATH_SIZE]; + enum udev_builtin_cmd i; + char *pos; + + strscpy(name, sizeof(name), command); + pos = strchr(name, ' '); + if (pos) + pos[0] = '\0'; + for (i = 0; i < ELEMENTSOF(builtins); i++) + if (builtins[i] && streq(builtins[i]->name, name)) + return i; + return UDEV_BUILTIN_MAX; +} + +int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) { + char arg[UTIL_PATH_SIZE]; + int argc; + char *argv[128]; + + if (!builtins[cmd]) + return -EOPNOTSUPP; + + /* we need '0' here to reset the internal state */ + optind = 0; + strscpy(arg, sizeof(arg), command); + udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); + return builtins[cmd]->cmd(dev, argc, argv, test); +} + +int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) { + udev_device_add_property(dev, key, val); + + if (test) + printf("%s=%s\n", key, val); + return 0; +} diff --git a/src/grp-udev/libudev-core/udev-ctrl.c b/src/grp-udev/libudev-core/udev-ctrl.c new file mode 100644 index 0000000000..4336aa58ab --- /dev/null +++ b/src/grp-udev/libudev-core/udev-ctrl.c @@ -0,0 +1,462 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/socket-util.h" +#include "udev.h" + +/* wire protocol magic must match */ +#define UDEV_CTRL_MAGIC 0xdead1dea + +enum udev_ctrl_msg_type { + UDEV_CTRL_UNKNOWN, + UDEV_CTRL_SET_LOG_LEVEL, + UDEV_CTRL_STOP_EXEC_QUEUE, + UDEV_CTRL_START_EXEC_QUEUE, + UDEV_CTRL_RELOAD, + UDEV_CTRL_SET_ENV, + UDEV_CTRL_SET_CHILDREN_MAX, + UDEV_CTRL_PING, + UDEV_CTRL_EXIT, +}; + +struct udev_ctrl_msg_wire { + char version[16]; + unsigned int magic; + enum udev_ctrl_msg_type type; + union { + int intval; + char buf[256]; + }; +}; + +struct udev_ctrl_msg { + int refcount; + struct udev_ctrl_connection *conn; + struct udev_ctrl_msg_wire ctrl_msg_wire; +}; + +struct udev_ctrl { + int refcount; + struct udev *udev; + int sock; + union sockaddr_union saddr; + socklen_t addrlen; + bool bound; + bool cleanup_socket; + bool connected; +}; + +struct udev_ctrl_connection { + int refcount; + struct udev_ctrl *uctrl; + int sock; +}; + +struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { + struct udev_ctrl *uctrl; + const int on = 1; + int r; + + uctrl = new0(struct udev_ctrl, 1); + if (uctrl == NULL) + return NULL; + uctrl->refcount = 1; + uctrl->udev = udev; + + if (fd < 0) { + uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (uctrl->sock < 0) { + log_error_errno(errno, "error getting socket: %m"); + udev_ctrl_unref(uctrl); + return NULL; + } + } else { + uctrl->bound = true; + uctrl->sock = fd; + } + + /* + * FIXME: remove it as soon as we can depend on this: + * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949 + */ + r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + if (r < 0) + log_warning_errno(errno, "could not set SO_PASSCRED: %m"); + + uctrl->saddr.un.sun_family = AF_LOCAL; + strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control"); + uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); + return uctrl; +} + +struct udev_ctrl *udev_ctrl_new(struct udev *udev) { + return udev_ctrl_new_from_fd(udev, -1); +} + +int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { + int err; + + if (!uctrl->bound) { + err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); + if (err < 0 && errno == EADDRINUSE) { + unlink(uctrl->saddr.un.sun_path); + err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); + } + + if (err < 0) + return log_error_errno(errno, "bind failed: %m"); + + err = listen(uctrl->sock, 0); + if (err < 0) + return log_error_errno(errno, "listen failed: %m"); + + uctrl->bound = true; + uctrl->cleanup_socket = true; + } + return 0; +} + +struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) { + return uctrl->udev; +} + +static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) { + if (uctrl) + uctrl->refcount++; + + return uctrl; +} + +struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) { + if (uctrl && -- uctrl->refcount == 0) { + if (uctrl->sock >= 0) + close(uctrl->sock); + free(uctrl); + } + + return NULL; +} + +int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { + if (uctrl == NULL) + return 0; + if (uctrl->cleanup_socket) + unlink(uctrl->saddr.un.sun_path); + return 0; +} + +int udev_ctrl_get_fd(struct udev_ctrl *uctrl) { + if (uctrl == NULL) + return -EINVAL; + return uctrl->sock; +} + +struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { + struct udev_ctrl_connection *conn; + struct ucred ucred = {}; + const int on = 1; + int r; + + conn = new(struct udev_ctrl_connection, 1); + if (conn == NULL) + return NULL; + conn->refcount = 1; + conn->uctrl = uctrl; + + conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + if (conn->sock < 0) { + if (errno != EINTR) + log_error_errno(errno, "unable to receive ctrl connection: %m"); + goto err; + } + + /* check peer credential of connection */ + r = getpeercred(conn->sock, &ucred); + if (r < 0) { + log_error_errno(r, "unable to receive credentials of ctrl connection: %m"); + goto err; + } + if (ucred.uid > 0) { + log_error("sender uid="UID_FMT", message ignored", ucred.uid); + goto err; + } + + /* enable receiving of the sender credentials in the messages */ + r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + if (r < 0) + log_warning_errno(errno, "could not set SO_PASSCRED: %m"); + + udev_ctrl_ref(uctrl); + return conn; +err: + if (conn->sock >= 0) + close(conn->sock); + free(conn); + return NULL; +} + +struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) { + if (conn == NULL) + return NULL; + conn->refcount++; + return conn; +} + +struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) { + if (conn && -- conn->refcount == 0) { + if (conn->sock >= 0) + close(conn->sock); + + udev_ctrl_unref(conn->uctrl); + + free(conn); + } + + return NULL; +} + +static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) { + struct udev_ctrl_msg_wire ctrl_msg_wire; + int err = 0; + + memzero(&ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); + strcpy(ctrl_msg_wire.version, "udev-" VERSION); + ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; + ctrl_msg_wire.type = type; + + if (buf != NULL) + strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); + else + ctrl_msg_wire.intval = intval; + + if (!uctrl->connected) { + if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) { + err = -errno; + goto out; + } + uctrl->connected = true; + } + if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { + err = -errno; + goto out; + } + + /* wait for peer message handling or disconnect */ + for (;;) { + struct pollfd pfd[1]; + int r; + + pfd[0].fd = uctrl->sock; + pfd[0].events = POLLIN; + r = poll(pfd, 1, timeout * MSEC_PER_SEC); + if (r < 0) { + if (errno == EINTR) + continue; + err = -errno; + break; + } + + if (r > 0 && pfd[0].revents & POLLERR) { + err = -EIO; + break; + } + + if (r == 0) + err = -ETIMEDOUT; + break; + } +out: + return err; +} + +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); +} + +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); +} + +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); +} + +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); +} + +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); +} + +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); +} + +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); +} + +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) { + return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); +} + +struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { + struct udev_ctrl_msg *uctrl_msg; + ssize_t size; + struct cmsghdr *cmsg; + struct iovec iov; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + struct msghdr smsg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cred_msg, + .msg_controllen = sizeof(cred_msg), + }; + struct ucred *cred; + + uctrl_msg = new0(struct udev_ctrl_msg, 1); + if (uctrl_msg == NULL) + return NULL; + uctrl_msg->refcount = 1; + uctrl_msg->conn = conn; + udev_ctrl_connection_ref(conn); + + /* wait for the incoming message */ + for (;;) { + struct pollfd pfd[1]; + int r; + + pfd[0].fd = conn->sock; + pfd[0].events = POLLIN; + + r = poll(pfd, 1, 10000); + if (r < 0) { + if (errno == EINTR) + continue; + goto err; + } else if (r == 0) { + log_error("timeout waiting for ctrl message"); + goto err; + } else { + if (!(pfd[0].revents & POLLIN)) { + log_error_errno(errno, "ctrl connection error: %m"); + goto err; + } + } + + break; + } + + iov.iov_base = &uctrl_msg->ctrl_msg_wire; + iov.iov_len = sizeof(struct udev_ctrl_msg_wire); + + size = recvmsg(conn->sock, &smsg, 0); + if (size < 0) { + log_error_errno(errno, "unable to receive ctrl message: %m"); + goto err; + } + + cmsg_close_all(&smsg); + + cmsg = CMSG_FIRSTHDR(&smsg); + + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + log_error("no sender credentials received, message ignored"); + goto err; + } + + cred = (struct ucred *) CMSG_DATA(cmsg); + + if (cred->uid != 0) { + log_error("sender uid="UID_FMT", message ignored", cred->uid); + goto err; + } + + if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { + log_error("message magic 0x%08x doesn't match, ignore it", uctrl_msg->ctrl_msg_wire.magic); + goto err; + } + + return uctrl_msg; +err: + udev_ctrl_msg_unref(uctrl_msg); + return NULL; +} + +struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg && -- ctrl_msg->refcount == 0) { + udev_ctrl_connection_unref(ctrl_msg->conn); + free(ctrl_msg); + } + + return NULL; +} + +int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) + return ctrl_msg->ctrl_msg_wire.intval; + return -1; +} + +int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) + return 1; + return -1; +} + +int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) + return 1; + return -1; +} + +int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) + return 1; + return -1; +} + +const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) + return ctrl_msg->ctrl_msg_wire.buf; + return NULL; +} + +int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) + return ctrl_msg->ctrl_msg_wire.intval; + return -1; +} + +int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) + return 1; + return -1; +} + +int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) { + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) + return 1; + return -1; +} diff --git a/src/grp-udev/libudev-core/udev-event.c b/src/grp-udev/libudev-core/udev-event.c new file mode 100644 index 0000000000..5bdf5aad02 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-event.c @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2003-2013 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "sd-netlink/netlink-util.h" +#include "udev.h" + +typedef struct Spawn { + const char *cmd; + pid_t pid; + usec_t timeout_warn; + usec_t timeout; + bool accept_failure; +} Spawn; + +struct udev_event *udev_event_new(struct udev_device *dev) { + struct udev *udev = udev_device_get_udev(dev); + struct udev_event *event; + + event = new0(struct udev_event, 1); + if (event == NULL) + return NULL; + event->dev = dev; + event->udev = udev; + udev_list_init(udev, &event->run_list, false); + udev_list_init(udev, &event->seclabel_list, false); + event->birth_usec = clock_boottime_or_monotonic(); + return event; +} + +void udev_event_unref(struct udev_event *event) { + if (event == NULL) + return; + sd_netlink_unref(event->rtnl); + udev_list_cleanup(&event->run_list); + udev_list_cleanup(&event->seclabel_list); + free(event->program_result); + free(event->name); + free(event); +} + +size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) { + struct udev_device *dev = event->dev; + enum subst_type { + SUBST_UNKNOWN, + SUBST_DEVNODE, + SUBST_ATTR, + SUBST_ENV, + SUBST_KERNEL, + SUBST_KERNEL_NUMBER, + SUBST_DRIVER, + SUBST_DEVPATH, + SUBST_ID, + SUBST_MAJOR, + SUBST_MINOR, + SUBST_RESULT, + SUBST_PARENT, + SUBST_NAME, + SUBST_LINKS, + SUBST_ROOT, + SUBST_SYS, + }; + static const struct subst_map { + const char *name; + const char fmt; + enum subst_type type; + } map[] = { + { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, + { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, + { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, + { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, + { .name = "env", .fmt = 'E', .type = SUBST_ENV }, + { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, + { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, + { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, + { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, + { .name = "id", .fmt = 'b', .type = SUBST_ID }, + { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, + { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, + { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, + { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, + { .name = "name", .fmt = 'D', .type = SUBST_NAME }, + { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, + { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, + { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, + }; + const char *from; + char *s; + size_t l; + + assert(dev); + + from = src; + s = dest; + l = size; + + for (;;) { + enum subst_type type = SUBST_UNKNOWN; + char attrbuf[UTIL_PATH_SIZE]; + char *attr = NULL; + + while (from[0] != '\0') { + if (from[0] == '$') { + /* substitute named variable */ + unsigned int i; + + if (from[1] == '$') { + from++; + goto copy; + } + + for (i = 0; i < ELEMENTSOF(map); i++) { + if (startswith(&from[1], map[i].name)) { + type = map[i].type; + from += strlen(map[i].name)+1; + goto subst; + } + } + } else if (from[0] == '%') { + /* substitute format char */ + unsigned int i; + + if (from[1] == '%') { + from++; + goto copy; + } + + for (i = 0; i < ELEMENTSOF(map); i++) { + if (from[1] == map[i].fmt) { + type = map[i].type; + from += 2; + goto subst; + } + } + } +copy: + /* copy char */ + if (l == 0) + goto out; + s[0] = from[0]; + from++; + s++; + l--; + } + + goto out; +subst: + /* extract possible $format{attr} */ + if (from[0] == '{') { + unsigned int i; + + from++; + for (i = 0; from[i] != '}'; i++) { + if (from[i] == '\0') { + log_error("missing closing brace for format '%s'", src); + goto out; + } + } + if (i >= sizeof(attrbuf)) + goto out; + memcpy(attrbuf, from, i); + attrbuf[i] = '\0'; + from += i+1; + attr = attrbuf; + } else { + attr = NULL; + } + + switch (type) { + case SUBST_DEVPATH: + l = strpcpy(&s, l, udev_device_get_devpath(dev)); + break; + case SUBST_KERNEL: + l = strpcpy(&s, l, udev_device_get_sysname(dev)); + break; + case SUBST_KERNEL_NUMBER: + if (udev_device_get_sysnum(dev) == NULL) + break; + l = strpcpy(&s, l, udev_device_get_sysnum(dev)); + break; + case SUBST_ID: + if (event->dev_parent == NULL) + break; + l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); + break; + case SUBST_DRIVER: { + const char *driver; + + if (event->dev_parent == NULL) + break; + + driver = udev_device_get_driver(event->dev_parent); + if (driver == NULL) + break; + l = strpcpy(&s, l, driver); + break; + } + case SUBST_MAJOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%u", major(udev_device_get_devnum(dev))); + l = strpcpy(&s, l, num); + break; + } + case SUBST_MINOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%u", minor(udev_device_get_devnum(dev))); + l = strpcpy(&s, l, num); + break; + } + case SUBST_RESULT: { + char *rest; + int i; + + if (event->program_result == NULL) + break; + /* get part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + char result[UTIL_PATH_SIZE]; + char tmp[UTIL_PATH_SIZE]; + char *cpos; + + strscpy(result, sizeof(result), event->program_result); + cpos = result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + if (cpos[0] == '\0') + break; + } + if (i > 0) { + log_error("requested part of result string not found"); + break; + } + strscpy(tmp, sizeof(tmp), cpos); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(tmp, ' '); + if (cpos) + cpos[0] = '\0'; + } + l = strpcpy(&s, l, tmp); + } else { + l = strpcpy(&s, l, event->program_result); + } + break; + } + case SUBST_ATTR: { + const char *value = NULL; + char vbuf[UTIL_NAME_SIZE]; + size_t len; + int count; + + if (attr == NULL) { + log_error("missing file parameter for attr"); + break; + } + + /* try to read the value specified by "[dmi/id]product_name" */ + if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) + value = vbuf; + + /* try to read the attribute the device */ + if (value == NULL) + value = udev_device_get_sysattr_value(event->dev, attr); + + /* try to read the attribute of the parent device, other matches have selected */ + if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) + value = udev_device_get_sysattr_value(event->dev_parent, attr); + + if (value == NULL) + break; + + /* strip trailing whitespace, and replace unwanted characters */ + if (value != vbuf) + strscpy(vbuf, sizeof(vbuf), value); + len = strlen(vbuf); + while (len > 0 && isspace(vbuf[--len])) + vbuf[len] = '\0'; + count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); + if (count > 0) + log_debug("%i character(s) replaced" , count); + l = strpcpy(&s, l, vbuf); + break; + } + case SUBST_PARENT: { + struct udev_device *dev_parent; + const char *devnode; + + dev_parent = udev_device_get_parent(event->dev); + if (dev_parent == NULL) + break; + devnode = udev_device_get_devnode(dev_parent); + if (devnode != NULL) + l = strpcpy(&s, l, devnode + strlen("/dev/")); + break; + } + case SUBST_DEVNODE: + if (udev_device_get_devnode(dev) != NULL) + l = strpcpy(&s, l, udev_device_get_devnode(dev)); + break; + case SUBST_NAME: + if (event->name != NULL) + l = strpcpy(&s, l, event->name); + else if (udev_device_get_devnode(dev) != NULL) + l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); + else + l = strpcpy(&s, l, udev_device_get_sysname(dev)); + break; + case SUBST_LINKS: { + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_devlinks_list_entry(dev); + if (list_entry == NULL) + break; + l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); + udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) + l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); + break; + } + case SUBST_ROOT: + l = strpcpy(&s, l, "/dev"); + break; + case SUBST_SYS: + l = strpcpy(&s, l, "/sys"); + break; + case SUBST_ENV: + if (attr == NULL) { + break; + } else { + const char *value; + + value = udev_device_get_property_value(event->dev, attr); + if (value == NULL) + break; + l = strpcpy(&s, l, value); + break; + } + default: + log_error("unknown substitution type=%i", type); + break; + } + } + +out: + s[0] = '\0'; + return l; +} + +static int spawn_exec(struct udev_event *event, + const char *cmd, char *const argv[], char **envp, + int fd_stdout, int fd_stderr) { + _cleanup_close_ int fd = -1; + int r; + + /* discard child output or connect to pipe */ + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + r = dup2(fd, STDIN_FILENO); + if (r < 0) + log_warning_errno(errno, "redirecting stdin failed: %m"); + + if (fd_stdout < 0) { + r = dup2(fd, STDOUT_FILENO); + if (r < 0) + log_warning_errno(errno, "redirecting stdout failed: %m"); + } + + if (fd_stderr < 0) { + r = dup2(fd, STDERR_FILENO); + if (r < 0) + log_warning_errno(errno, "redirecting stderr failed: %m"); + } + } else + log_warning_errno(errno, "open /dev/null failed: %m"); + + /* connect pipes to std{out,err} */ + if (fd_stdout >= 0) { + r = dup2(fd_stdout, STDOUT_FILENO); + if (r < 0) + log_warning_errno(errno, "redirecting stdout failed: %m"); + + fd_stdout = safe_close(fd_stdout); + } + + if (fd_stderr >= 0) { + r = dup2(fd_stderr, STDERR_FILENO); + if (r < 0) + log_warning_errno(errno, "redirecting stdout failed: %m"); + + fd_stderr = safe_close(fd_stderr); + } + + /* terminate child in case parent goes away */ + prctl(PR_SET_PDEATHSIG, SIGTERM); + + /* restore sigmask before exec */ + (void) reset_signal_mask(); + + execve(argv[0], argv, envp); + + /* exec failed */ + return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); +} + +static void spawn_read(struct udev_event *event, + usec_t timeout_usec, + const char *cmd, + int fd_stdout, int fd_stderr, + char *result, size_t ressize) { + _cleanup_close_ int fd_ep = -1; + struct epoll_event ep_outpipe = { + .events = EPOLLIN, + .data.ptr = &fd_stdout, + }; + struct epoll_event ep_errpipe = { + .events = EPOLLIN, + .data.ptr = &fd_stderr, + }; + size_t respos = 0; + int r; + + /* read from child if requested */ + if (fd_stdout < 0 && fd_stderr < 0) + return; + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + log_error_errno(errno, "error creating epoll fd: %m"); + return; + } + + if (fd_stdout >= 0) { + r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); + if (r < 0) { + log_error_errno(errno, "fail to add stdout fd to epoll: %m"); + return; + } + } + + if (fd_stderr >= 0) { + r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); + if (r < 0) { + log_error_errno(errno, "fail to add stderr fd to epoll: %m"); + return; + } + } + + /* read child output */ + while (fd_stdout >= 0 || fd_stderr >= 0) { + int timeout; + int fdcount; + struct epoll_event ev[4]; + int i; + + if (timeout_usec > 0) { + usec_t age_usec; + + age_usec = clock_boottime_or_monotonic() - event->birth_usec; + if (age_usec >= timeout_usec) { + log_error("timeout '%s'", cmd); + return; + } + timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC; + } else { + timeout = -1; + } + + fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout); + if (fdcount < 0) { + if (errno == EINTR) + continue; + log_error_errno(errno, "failed to poll: %m"); + return; + } else if (fdcount == 0) { + log_error("timeout '%s'", cmd); + return; + } + + for (i = 0; i < fdcount; i++) { + int *fd = (int *)ev[i].data.ptr; + + if (*fd < 0) + continue; + + if (ev[i].events & EPOLLIN) { + ssize_t count; + char buf[4096]; + + count = read(*fd, buf, sizeof(buf)-1); + if (count <= 0) + continue; + buf[count] = '\0'; + + /* store stdout result */ + if (result != NULL && *fd == fd_stdout) { + if (respos + count < ressize) { + memcpy(&result[respos], buf, count); + respos += count; + } else { + log_error("'%s' ressize %zu too short", cmd, ressize); + } + } + + /* log debug output only if we watch stderr */ + if (fd_stderr >= 0) { + char *pos; + char *line; + + pos = buf; + while ((line = strsep(&pos, "\n"))) { + if (pos != NULL || line[0] != '\0') + log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line); + } + } + } else if (ev[i].events & EPOLLHUP) { + r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); + if (r < 0) { + log_error_errno(errno, "failed to remove fd from epoll: %m"); + return; + } + *fd = -1; + } + } + } + + /* return the child's stdout string */ + if (result != NULL) + result[respos] = '\0'; +} + +static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + Spawn *spawn = userdata; + char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; + + assert(spawn); + + kill_and_sigcont(spawn->pid, SIGKILL); + + log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, + format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); + + return 1; +} + +static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { + Spawn *spawn = userdata; + char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; + + assert(spawn); + + log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid, + format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); + + return 1; +} + +static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) { + Spawn *spawn = userdata; + + assert(spawn); + + switch (si->si_code) { + case CLD_EXITED: + if (si->si_status == 0) { + log_debug("Process '%s' succeeded.", spawn->cmd); + sd_event_exit(sd_event_source_get_event(s), 0); + + return 1; + } else if (spawn->accept_failure) + log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); + else + log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); + + break; + case CLD_KILLED: + case CLD_DUMPED: + log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status)); + + break; + default: + log_error("Process '%s' failed due to unknown reason.", spawn->cmd); + } + + sd_event_exit(sd_event_source_get_event(s), -EIO); + + return 1; +} + +static int spawn_wait(struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + const char *cmd, pid_t pid, + bool accept_failure) { + Spawn spawn = { + .cmd = cmd, + .pid = pid, + .accept_failure = accept_failure, + }; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + int r, ret; + + r = sd_event_new(&e); + if (r < 0) + return r; + + if (timeout_usec > 0) { + usec_t usec, age_usec; + + usec = now(clock_boottime_or_monotonic()); + age_usec = usec - event->birth_usec; + if (age_usec < timeout_usec) { + if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) { + spawn.timeout_warn = timeout_warn_usec - age_usec; + + r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + usec + spawn.timeout_warn, USEC_PER_SEC, + on_spawn_timeout_warning, &spawn); + if (r < 0) + return r; + } + + spawn.timeout = timeout_usec - age_usec; + + r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn); + if (r < 0) + return r; + } + } + + r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn); + if (r < 0) + return r; + + r = sd_event_loop(e); + if (r < 0) + return r; + + r = sd_event_get_exit_code(e, &ret); + if (r < 0) + return r; + + return ret; +} + +int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) { + int i = 0; + char *pos; + + if (strchr(cmd, ' ') == NULL) { + argv[i++] = cmd; + goto out; + } + + pos = cmd; + while (pos != NULL && pos[0] != '\0') { + if (pos[0] == '\'') { + /* do not separate quotes */ + pos++; + argv[i] = strsep(&pos, "\'"); + if (pos != NULL) + while (pos[0] == ' ') + pos++; + } else { + argv[i] = strsep(&pos, " "); + if (pos != NULL) + while (pos[0] == ' ') + pos++; + } + i++; + } +out: + argv[i] = NULL; + if (argc) + *argc = i; + return 0; +} + +int udev_event_spawn(struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + bool accept_failure, + const char *cmd, + char *result, size_t ressize) { + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + pid_t pid; + int err = 0; + + /* pipes from child to parent */ + if (result != NULL || log_get_max_level() >= LOG_INFO) { + if (pipe2(outpipe, O_NONBLOCK) != 0) { + err = log_error_errno(errno, "pipe failed: %m"); + goto out; + } + } + if (log_get_max_level() >= LOG_INFO) { + if (pipe2(errpipe, O_NONBLOCK) != 0) { + err = log_error_errno(errno, "pipe failed: %m"); + goto out; + } + } + + pid = fork(); + switch(pid) { + case 0: + { + char arg[UTIL_PATH_SIZE]; + char *argv[128]; + char program[UTIL_PATH_SIZE]; + + /* child closes parent's ends of pipes */ + outpipe[READ_END] = safe_close(outpipe[READ_END]); + errpipe[READ_END] = safe_close(errpipe[READ_END]); + + strscpy(arg, sizeof(arg), cmd); + udev_build_argv(event->udev, arg, NULL, argv); + + /* allow programs in /usr/lib/udev/ to be called without the path */ + if (argv[0][0] != '/') { + strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); + argv[0] = program; + } + + log_debug("starting '%s'", cmd); + + spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev), + outpipe[WRITE_END], errpipe[WRITE_END]); + + _exit(2); + } + case -1: + log_error_errno(errno, "fork of '%s' failed: %m", cmd); + err = -1; + goto out; + default: + /* parent closed child's ends of pipes */ + outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); + errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); + + spawn_read(event, + timeout_usec, + cmd, + outpipe[READ_END], errpipe[READ_END], + result, ressize); + + err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); + } + +out: + if (outpipe[READ_END] >= 0) + close(outpipe[READ_END]); + if (outpipe[WRITE_END] >= 0) + close(outpipe[WRITE_END]); + if (errpipe[READ_END] >= 0) + close(errpipe[READ_END]); + if (errpipe[WRITE_END] >= 0) + close(errpipe[WRITE_END]); + return err; +} + +static int rename_netif(struct udev_event *event) { + struct udev_device *dev = event->dev; + char name[IFNAMSIZ]; + const char *oldname; + int r; + + oldname = udev_device_get_sysname(dev); + + strscpy(name, IFNAMSIZ, event->name); + + r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); + if (r < 0) + return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); + + log_debug("renamed network interface '%s' to '%s'", oldname, name); + + return 0; +} + +void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, + struct udev_rules *rules) { + struct udev_device *dev = event->dev; + + if (udev_device_get_subsystem(dev) == NULL) + return; + + if (streq(udev_device_get_action(dev), "remove")) { + udev_device_read_db(dev); + udev_device_tag_index(dev, NULL, false); + udev_device_delete_db(dev); + + if (major(udev_device_get_devnum(dev)) != 0) + udev_watch_end(event->udev, dev); + + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list); + + if (major(udev_device_get_devnum(dev)) != 0) + udev_node_remove(dev); + } else { + event->dev_db = udev_device_clone_with_db(dev); + if (event->dev_db != NULL) { + /* 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); + } + + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list); + + /* rename a new network interface, if needed */ + if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && + event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { + int r; + + r = rename_netif(event); + if (r < 0) + log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev), + udev_device_get_sysname(dev), event->name); + else { + r = udev_device_rename(dev, event->name); + if (r < 0) + log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m", + udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name); + else + log_debug("changed devpath to '%s'", udev_device_get_devpath(dev)); + } + } + + if (major(udev_device_get_devnum(dev)) > 0) { + bool apply; + + /* remove/update possible left-over symlinks from old database entry */ + if (event->dev_db != NULL) + udev_node_update_old_links(dev, event->dev_db); + + if (!event->owner_set) + event->uid = udev_device_get_devnode_uid(dev); + + if (!event->group_set) + event->gid = udev_device_get_devnode_gid(dev); + + if (!event->mode_set) { + if (udev_device_get_devnode_mode(dev) > 0) { + /* kernel supplied value */ + event->mode = udev_device_get_devnode_mode(dev); + } else if (event->gid > 0) { + /* default 0660 if a group is assigned */ + event->mode = 0660; + } else { + /* default 0600 */ + event->mode = 0600; + } + } + + apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; + udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); + } + + /* preserve old, or get new initialization timestamp */ + udev_device_ensure_usec_initialized(event->dev, event->dev_db); + + /* (re)write database file */ + udev_device_tag_index(dev, event->dev_db, true); + udev_device_update_db(dev); + udev_device_set_is_initialized(dev); + + event->dev_db = udev_device_unref(event->dev_db); + } +} + +void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) { + struct udev_list_entry *list_entry; + + udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { + char command[UTIL_PATH_SIZE]; + const char *cmd = udev_list_entry_get_name(list_entry); + enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry); + + udev_event_apply_format(event, cmd, command, sizeof(command)); + + if (builtin_cmd < UDEV_BUILTIN_MAX) + udev_builtin_run(event->dev, builtin_cmd, command, false); + else { + if (event->exec_delay > 0) { + log_debug("delay execution of '%s'", command); + sleep(event->exec_delay); + } + + udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0); + } + } +} diff --git a/src/grp-udev/libudev-core/udev-node.c b/src/grp-udev/libudev-core/udev-node.c new file mode 100644 index 0000000000..3a2daa89ad --- /dev/null +++ b/src/grp-udev/libudev-core/udev-node.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2003-2013 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "udev.h" + +static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { + struct stat stats; + char target[UTIL_PATH_SIZE]; + char *s; + size_t l; + char slink_tmp[UTIL_PATH_SIZE + 32]; + int i = 0; + int tail = 0; + int err = 0; + + /* use relative link */ + target[0] = '\0'; + while (node[i] && (node[i] == slink[i])) { + if (node[i] == '/') + tail = i+1; + i++; + } + s = target; + l = sizeof(target); + while (slink[i] != '\0') { + if (slink[i] == '/') + l = strpcpy(&s, l, "../"); + i++; + } + l = strscpy(s, l, &node[tail]); + if (l == 0) { + err = -EINVAL; + goto exit; + } + + /* preserve link with correct target, do not replace node of other device */ + if (lstat(slink, &stats) == 0) { + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node); + goto exit; + } else if (S_ISLNK(stats.st_mode)) { + char buf[UTIL_PATH_SIZE]; + int len; + + len = readlink(slink, buf, sizeof(buf)); + if (len > 0 && len < (int)sizeof(buf)) { + buf[len] = '\0'; + if (streq(target, buf)) { + log_debug("preserve already existing symlink '%s' to '%s'", slink, target); + label_fix(slink, true, false); + utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); + goto exit; + } + } + } + } else { + log_debug("creating symlink '%s' to '%s'", slink, target); + do { + err = mkdir_parents_label(slink, 0755); + if (err != 0 && err != -ENOENT) + break; + mac_selinux_create_file_prepare(slink, S_IFLNK); + err = symlink(target, slink); + if (err != 0) + err = -errno; + mac_selinux_create_file_clear(); + } while (err == -ENOENT); + if (err == 0) + goto exit; + } + + log_debug("atomically replace '%s'", slink); + strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); + unlink(slink_tmp); + do { + err = mkdir_parents_label(slink_tmp, 0755); + if (err != 0 && err != -ENOENT) + break; + mac_selinux_create_file_prepare(slink_tmp, S_IFLNK); + err = symlink(target, slink_tmp); + if (err != 0) + err = -errno; + mac_selinux_create_file_clear(); + } while (err == -ENOENT); + if (err != 0) { + log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp); + goto exit; + } + err = rename(slink_tmp, slink); + if (err != 0) { + log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); + unlink(slink_tmp); + } +exit: + return err; +} + +/* find device node of device with highest priority */ +static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { + struct udev *udev = udev_device_get_udev(dev); + DIR *dir; + int priority = 0; + const char *target = NULL; + + if (add) { + priority = udev_device_get_devlink_priority(dev); + strscpy(buf, bufsize, udev_device_get_devnode(dev)); + target = buf; + } + + dir = opendir(stackdir); + if (dir == NULL) + return target; + for (;;) { + struct udev_device *dev_db; + struct dirent *dent; + + dent = readdir(dir); + if (dent == NULL || dent->d_name[0] == '\0') + break; + if (dent->d_name[0] == '.') + continue; + + log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); + + /* did we find ourself? */ + if (streq(dent->d_name, udev_device_get_id_filename(dev))) + continue; + + dev_db = udev_device_new_from_device_id(udev, dent->d_name); + if (dev_db != NULL) { + const char *devnode; + + devnode = udev_device_get_devnode(dev_db); + if (devnode != NULL) { + if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { + log_debug("'%s' claims priority %i for '%s'", + udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); + priority = udev_device_get_devlink_priority(dev_db); + strscpy(buf, bufsize, devnode); + target = buf; + } + } + udev_device_unref(dev_db); + } + } + closedir(dir); + return target; +} + +/* manage "stack of names" with possibly specified device priorities */ +static void link_update(struct udev_device *dev, const char *slink, bool add) { + char name_enc[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE * 2]; + char dirname[UTIL_PATH_SIZE]; + const char *target; + char buf[UTIL_PATH_SIZE]; + + util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); + strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); + strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); + + if (!add && unlink(filename) == 0) + rmdir(dirname); + + target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); + if (target == NULL) { + log_debug("no reference left, remove '%s'", slink); + if (unlink(slink) == 0) + rmdir_parents(slink, "/"); + } else { + log_debug("creating link '%s' to '%s'", slink, target); + node_symlink(dev, target, slink); + } + + if (add) { + int err; + + do { + int fd; + + err = mkdir_parents(filename, 0755); + if (err != 0 && err != -ENOENT) + break; + fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); + if (fd >= 0) + close(fd); + else + err = -errno; + } while (err == -ENOENT); + } +} + +void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { + struct udev_list_entry *list_entry; + + /* update possible left-over symlinks */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { + const char *name = udev_list_entry_get_name(list_entry); + struct udev_list_entry *list_entry_current; + int found; + + /* check if old link name still belongs to this device */ + found = 0; + udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { + const char *name_current = udev_list_entry_get_name(list_entry_current); + + if (streq(name, name_current)) { + found = 1; + break; + } + } + if (found) + continue; + + log_debug("update old name, '%s' no longer belonging to '%s'", + name, udev_device_get_devpath(dev)); + link_update(dev, name, false); + } +} + +static int node_permissions_apply(struct udev_device *dev, bool apply, + mode_t mode, uid_t uid, gid_t gid, + struct udev_list *seclabel_list) { + const char *devnode = udev_device_get_devnode(dev); + dev_t devnum = udev_device_get_devnum(dev); + struct stat stats; + struct udev_list_entry *entry; + int err = 0; + + if (streq(udev_device_get_subsystem(dev), "block")) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (lstat(devnode, &stats) != 0) { + err = log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode); + goto out; + } + + if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { + err = -EEXIST; + log_debug("found node '%s' with non-matching devnum %s, skip handling", + udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); + goto out; + } + + if (apply) { + bool selinux = false; + bool smack = false; + + if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { + log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); + err = chmod(devnode, mode); + if (err < 0) + log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode); + err = chown(devnode, uid, gid); + if (err < 0) + log_warning_errno(errno, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid); + } else { + log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); + } + + /* apply SECLABEL{$module}=$label */ + udev_list_entry_foreach(entry, udev_list_get_entry(seclabel_list)) { + const char *name, *label; + int r; + + name = udev_list_entry_get_name(entry); + label = udev_list_entry_get_value(entry); + + if (streq(name, "selinux")) { + selinux = true; + + r = mac_selinux_apply(devnode, label); + if (r < 0) + log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label); + else + log_debug("SECLABEL: set SELinux label '%s'", label); + + } else if (streq(name, "smack")) { + smack = true; + + r = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label); + if (r < 0) + log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label); + else + log_debug("SECLABEL: set SMACK label '%s'", label); + + } else + log_error("SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label); + } + + /* set the defaults */ + if (!selinux) + mac_selinux_fix(devnode, true, false); + if (!smack) + mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL); + } + + /* always update timestamp when we re-use the node, like on media change events */ + utimensat(AT_FDCWD, devnode, NULL, 0); +out: + return err; +} + +void udev_node_add(struct udev_device *dev, bool apply, + mode_t mode, uid_t uid, gid_t gid, + struct udev_list *seclabel_list) { + char filename[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + + log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT, + udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); + + if (node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list) < 0) + return; + + /* always add /dev/{block,char}/$major:$minor */ + 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))); + node_symlink(dev, udev_device_get_devnode(dev), filename); + + /* create/update symlinks, add symlinks to name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) + link_update(dev, udev_list_entry_get_name(list_entry), true); +} + +void udev_node_remove(struct udev_device *dev) { + struct udev_list_entry *list_entry; + char filename[UTIL_PATH_SIZE]; + + /* remove/update symlinks, remove symlinks from name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) + link_update(dev, udev_list_entry_get_name(list_entry), false); + + /* remove /dev/{block,char}/$major:$minor */ + 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))); + unlink(filename); +} diff --git a/src/grp-udev/libudev-core/udev-rules.c b/src/grp-udev/libudev-core/udev-rules.c new file mode 100644 index 0000000000..134d269fd8 --- /dev/null +++ b/src/grp-udev/libudev-core/udev-rules.c @@ -0,0 +1,2577 @@ +/* + * Copyright (C) 2003-2012 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/glob-util.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/strbuf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/sysctl-util.h" +#include "udev.h" + +#define PREALLOC_TOKEN 2048 + +struct uid_gid { + unsigned int name_off; + union { + uid_t uid; + gid_t gid; + }; +}; + +static const char* const rules_dirs[] = { + "/etc/udev/rules.d", + "/run/udev/rules.d", + UDEVLIBEXECDIR "/rules.d", + NULL +}; + +struct udev_rules { + struct udev *udev; + usec_t dirs_ts_usec; + int resolve_names; + + /* every key in the rules file becomes a token */ + struct token *tokens; + unsigned int token_cur; + unsigned int token_max; + + /* all key strings are copied and de-duplicated in a single continuous string buffer */ + struct strbuf *strbuf; + + /* during rule parsing, uid/gid lookup results are cached */ + struct uid_gid *uids; + unsigned int uids_cur; + unsigned int uids_max; + struct uid_gid *gids; + unsigned int gids_cur; + unsigned int gids_max; +}; + +static char *rules_str(struct udev_rules *rules, unsigned int off) { + return rules->strbuf->buf + off; +} + +static unsigned int rules_add_string(struct udev_rules *rules, const char *s) { + return strbuf_add_string(rules->strbuf, s, strlen(s)); +} + +/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */ +enum operation_type { + OP_UNSET, + + OP_MATCH, + OP_NOMATCH, + OP_MATCH_MAX, + + OP_ADD, + OP_REMOVE, + OP_ASSIGN, + OP_ASSIGN_FINAL, +}; + +enum string_glob_type { + GL_UNSET, + GL_PLAIN, /* no special chars */ + GL_GLOB, /* shell globs ?,*,[] */ + GL_SPLIT, /* multi-value A|B */ + GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ + GL_SOMETHING, /* commonly used "?*" */ +}; + +enum string_subst_type { + SB_UNSET, + SB_NONE, + SB_FORMAT, + SB_SUBSYS, +}; + +/* tokens of a rule are sorted/handled in this order */ +enum token_type { + TK_UNSET, + TK_RULE, + + TK_M_ACTION, /* val */ + TK_M_DEVPATH, /* val */ + TK_M_KERNEL, /* val */ + TK_M_DEVLINK, /* val */ + TK_M_NAME, /* val */ + TK_M_ENV, /* val, attr */ + TK_M_TAG, /* val */ + TK_M_SUBSYSTEM, /* val */ + TK_M_DRIVER, /* val */ + TK_M_WAITFOR, /* val */ + TK_M_ATTR, /* val, attr */ + TK_M_SYSCTL, /* val, attr */ + + TK_M_PARENTS_MIN, + TK_M_KERNELS, /* val */ + TK_M_SUBSYSTEMS, /* val */ + TK_M_DRIVERS, /* val */ + TK_M_ATTRS, /* val, attr */ + TK_M_TAGS, /* val */ + TK_M_PARENTS_MAX, + + TK_M_TEST, /* val, mode_t */ + TK_M_PROGRAM, /* val */ + TK_M_IMPORT_FILE, /* val */ + TK_M_IMPORT_PROG, /* val */ + TK_M_IMPORT_BUILTIN, /* val */ + TK_M_IMPORT_DB, /* val */ + TK_M_IMPORT_CMDLINE, /* val */ + TK_M_IMPORT_PARENT, /* val */ + TK_M_RESULT, /* val */ + TK_M_MAX, + + TK_A_STRING_ESCAPE_NONE, + TK_A_STRING_ESCAPE_REPLACE, + TK_A_DB_PERSIST, + TK_A_INOTIFY_WATCH, /* int */ + TK_A_DEVLINK_PRIO, /* int */ + TK_A_OWNER, /* val */ + TK_A_GROUP, /* val */ + TK_A_MODE, /* val */ + TK_A_OWNER_ID, /* uid_t */ + TK_A_GROUP_ID, /* gid_t */ + TK_A_MODE_ID, /* mode_t */ + TK_A_TAG, /* val */ + TK_A_STATIC_NODE, /* val */ + TK_A_SECLABEL, /* val, attr */ + TK_A_ENV, /* val, attr */ + TK_A_NAME, /* val */ + TK_A_DEVLINK, /* val */ + TK_A_ATTR, /* val, attr */ + TK_A_SYSCTL, /* val, attr */ + TK_A_RUN_BUILTIN, /* val, bool */ + TK_A_RUN_PROGRAM, /* val, bool */ + TK_A_GOTO, /* size_t */ + + TK_END, +}; + +/* we try to pack stuff in a way that we take only 12 bytes per token */ +struct token { + union { + unsigned char type; /* same in rule and key */ + struct { + enum token_type type:8; + bool can_set_name:1; + bool has_static_node:1; + unsigned int unused:6; + unsigned short token_count; + unsigned int label_off; + unsigned short filename_off; + unsigned short filename_line; + } rule; + struct { + enum token_type type:8; + enum operation_type op:8; + enum string_glob_type glob:8; + enum string_subst_type subst:4; + enum string_subst_type attrsubst:4; + unsigned int value_off; + union { + unsigned int attr_off; + unsigned int rule_goto; + mode_t mode; + uid_t uid; + gid_t gid; + int devlink_prio; + int watch; + enum udev_builtin_cmd builtin_cmd; + }; + } key; + }; +}; + +#define MAX_TK 64 +struct rule_tmp { + struct udev_rules *rules; + struct token rule; + struct token token[MAX_TK]; + unsigned int token_cur; +}; + +#ifdef DEBUG +static const char *operation_str(enum operation_type type) { + static const char *operation_strs[] = { + [OP_UNSET] = "UNSET", + [OP_MATCH] = "match", + [OP_NOMATCH] = "nomatch", + [OP_MATCH_MAX] = "MATCH_MAX", + + [OP_ADD] = "add", + [OP_REMOVE] = "remove", + [OP_ASSIGN] = "assign", + [OP_ASSIGN_FINAL] = "assign-final", +} ; + + return operation_strs[type]; +} + +static const char *string_glob_str(enum string_glob_type type) { + static const char *string_glob_strs[] = { + [GL_UNSET] = "UNSET", + [GL_PLAIN] = "plain", + [GL_GLOB] = "glob", + [GL_SPLIT] = "split", + [GL_SPLIT_GLOB] = "split-glob", + [GL_SOMETHING] = "split-glob", + }; + + return string_glob_strs[type]; +} + +static const char *token_str(enum token_type type) { + static const char *token_strs[] = { + [TK_UNSET] = "UNSET", + [TK_RULE] = "RULE", + + [TK_M_ACTION] = "M ACTION", + [TK_M_DEVPATH] = "M DEVPATH", + [TK_M_KERNEL] = "M KERNEL", + [TK_M_DEVLINK] = "M DEVLINK", + [TK_M_NAME] = "M NAME", + [TK_M_ENV] = "M ENV", + [TK_M_TAG] = "M TAG", + [TK_M_SUBSYSTEM] = "M SUBSYSTEM", + [TK_M_DRIVER] = "M DRIVER", + [TK_M_WAITFOR] = "M WAITFOR", + [TK_M_ATTR] = "M ATTR", + [TK_M_SYSCTL] = "M SYSCTL", + + [TK_M_PARENTS_MIN] = "M PARENTS_MIN", + [TK_M_KERNELS] = "M KERNELS", + [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", + [TK_M_DRIVERS] = "M DRIVERS", + [TK_M_ATTRS] = "M ATTRS", + [TK_M_TAGS] = "M TAGS", + [TK_M_PARENTS_MAX] = "M PARENTS_MAX", + + [TK_M_TEST] = "M TEST", + [TK_M_PROGRAM] = "M PROGRAM", + [TK_M_IMPORT_FILE] = "M IMPORT_FILE", + [TK_M_IMPORT_PROG] = "M IMPORT_PROG", + [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", + [TK_M_IMPORT_DB] = "M IMPORT_DB", + [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", + [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", + [TK_M_RESULT] = "M RESULT", + [TK_M_MAX] = "M MAX", + + [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", + [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", + [TK_A_DB_PERSIST] = "A DB_PERSIST", + [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", + [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", + [TK_A_OWNER] = "A OWNER", + [TK_A_GROUP] = "A GROUP", + [TK_A_MODE] = "A MODE", + [TK_A_OWNER_ID] = "A OWNER_ID", + [TK_A_GROUP_ID] = "A GROUP_ID", + [TK_A_STATIC_NODE] = "A STATIC_NODE", + [TK_A_SECLABEL] = "A SECLABEL", + [TK_A_MODE_ID] = "A MODE_ID", + [TK_A_ENV] = "A ENV", + [TK_A_TAG] = "A ENV", + [TK_A_NAME] = "A NAME", + [TK_A_DEVLINK] = "A DEVLINK", + [TK_A_ATTR] = "A ATTR", + [TK_A_SYSCTL] = "A SYSCTL", + [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN", + [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM", + [TK_A_GOTO] = "A GOTO", + + [TK_END] = "END", + }; + + return token_strs[type]; +} + +static void dump_token(struct udev_rules *rules, struct token *token) { + enum token_type type = token->type; + enum operation_type op = token->key.op; + enum string_glob_type glob = token->key.glob; + const char *value = rules_str(rules, token->key.value_off); + const char *attr = &rules->strbuf->buf[token->key.attr_off]; + + switch (type) { + case TK_RULE: + { + const char *tks_ptr = (char *)rules->tokens; + const char *tk_ptr = (char *)token; + unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); + + log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", + &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line, + idx, token->rule.token_count, + &rules->strbuf->buf[token->rule.label_off]); + break; + } + case TK_M_ACTION: + case TK_M_DEVPATH: + case TK_M_KERNEL: + case TK_M_SUBSYSTEM: + case TK_M_DRIVER: + case TK_M_WAITFOR: + case TK_M_DEVLINK: + case TK_M_NAME: + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_TAGS: + case TK_M_PROGRAM: + case TK_M_IMPORT_FILE: + case TK_M_IMPORT_PROG: + case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: + case TK_M_IMPORT_PARENT: + case TK_M_RESULT: + case TK_A_NAME: + case TK_A_DEVLINK: + case TK_A_OWNER: + case TK_A_GROUP: + case TK_A_MODE: + case TK_A_RUN_BUILTIN: + case TK_A_RUN_PROGRAM: + log_debug("%s %s '%s'(%s)", + token_str(type), operation_str(op), value, string_glob_str(glob)); + break; + case TK_M_IMPORT_BUILTIN: + log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value); + break; + case TK_M_ATTR: + case TK_M_SYSCTL: + case TK_M_ATTRS: + case TK_M_ENV: + case TK_A_ATTR: + case TK_A_SYSCTL: + case TK_A_ENV: + log_debug("%s %s '%s' '%s'(%s)", + token_str(type), operation_str(op), attr, value, string_glob_str(glob)); + break; + case TK_M_TAG: + case TK_A_TAG: + log_debug("%s %s '%s'", token_str(type), operation_str(op), value); + break; + case TK_A_STRING_ESCAPE_NONE: + case TK_A_STRING_ESCAPE_REPLACE: + case TK_A_DB_PERSIST: + log_debug("%s", token_str(type)); + break; + case TK_M_TEST: + log_debug("%s %s '%s'(%s) %#o", + token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); + break; + case TK_A_INOTIFY_WATCH: + log_debug("%s %u", token_str(type), token->key.watch); + break; + case TK_A_DEVLINK_PRIO: + log_debug("%s %u", token_str(type), token->key.devlink_prio); + break; + case TK_A_OWNER_ID: + log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid); + break; + case TK_A_GROUP_ID: + log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid); + break; + case TK_A_MODE_ID: + log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode); + break; + case TK_A_STATIC_NODE: + log_debug("%s '%s'", token_str(type), value); + break; + case TK_A_SECLABEL: + log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value); + break; + case TK_A_GOTO: + log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto); + break; + case TK_END: + log_debug("* %s", token_str(type)); + break; + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_UNSET: + log_debug("unknown type %u", type); + break; + } +} + +static void dump_rules(struct udev_rules *rules) { + unsigned int i; + + log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", + rules->token_cur, + rules->token_cur * sizeof(struct token), + rules->strbuf->nodes_count, + rules->strbuf->len); + for (i = 0; i < rules->token_cur; i++) + dump_token(rules, &rules->tokens[i]); +} +#else +static inline void dump_token(struct udev_rules *rules, struct token *token) {} +static inline void dump_rules(struct udev_rules *rules) {} +#endif /* DEBUG */ + +static int add_token(struct udev_rules *rules, struct token *token) { + /* grow buffer if needed */ + if (rules->token_cur+1 >= rules->token_max) { + struct token *tokens; + unsigned int add; + + /* double the buffer size */ + add = rules->token_max; + if (add < 8) + add = 8; + + tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); + if (tokens == NULL) + return -1; + rules->tokens = tokens; + rules->token_max += add; + } + memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); + rules->token_cur++; + return 0; +} + +static uid_t add_uid(struct udev_rules *rules, const char *owner) { + unsigned int i; + uid_t uid = 0; + unsigned int off; + int r; + + /* lookup, if we know it already */ + for (i = 0; i < rules->uids_cur; i++) { + off = rules->uids[i].name_off; + if (streq(rules_str(rules, off), owner)) { + uid = rules->uids[i].uid; + return uid; + } + } + r = get_user_creds(&owner, &uid, NULL, NULL, NULL); + if (r < 0) { + if (r == -ENOENT || r == -ESRCH) + log_error("specified user '%s' unknown", owner); + else + log_error_errno(r, "error resolving user '%s': %m", owner); + } + + /* grow buffer if needed */ + if (rules->uids_cur+1 >= rules->uids_max) { + struct uid_gid *uids; + unsigned int add; + + /* double the buffer size */ + add = rules->uids_max; + if (add < 1) + add = 8; + + uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); + if (uids == NULL) + return uid; + rules->uids = uids; + rules->uids_max += add; + } + rules->uids[rules->uids_cur].uid = uid; + off = rules_add_string(rules, owner); + if (off <= 0) + return uid; + rules->uids[rules->uids_cur].name_off = off; + rules->uids_cur++; + return uid; +} + +static gid_t add_gid(struct udev_rules *rules, const char *group) { + unsigned int i; + gid_t gid = 0; + unsigned int off; + int r; + + /* lookup, if we know it already */ + for (i = 0; i < rules->gids_cur; i++) { + off = rules->gids[i].name_off; + if (streq(rules_str(rules, off), group)) { + gid = rules->gids[i].gid; + return gid; + } + } + r = get_group_creds(&group, &gid); + if (r < 0) { + if (r == -ENOENT || r == -ESRCH) + log_error("specified group '%s' unknown", group); + else + log_error_errno(r, "error resolving group '%s': %m", group); + } + + /* grow buffer if needed */ + if (rules->gids_cur+1 >= rules->gids_max) { + struct uid_gid *gids; + unsigned int add; + + /* double the buffer size */ + add = rules->gids_max; + if (add < 1) + add = 8; + + gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); + if (gids == NULL) + return gid; + rules->gids = gids; + rules->gids_max += add; + } + rules->gids[rules->gids_cur].gid = gid; + off = rules_add_string(rules, group); + if (off <= 0) + return gid; + rules->gids[rules->gids_cur].name_off = off; + rules->gids_cur++; + return gid; +} + +static int import_property_from_string(struct udev_device *dev, char *line) { + char *key; + char *val; + size_t len; + + /* find key */ + key = line; + while (isspace(key[0])) + key++; + + /* comment or empty line */ + if (key[0] == '#' || key[0] == '\0') + return -1; + + /* split key/value */ + val = strchr(key, '='); + if (val == NULL) + return -1; + val[0] = '\0'; + val++; + + /* find value */ + while (isspace(val[0])) + val++; + + /* terminate key */ + len = strlen(key); + if (len == 0) + return -1; + while (isspace(key[len-1])) + len--; + key[len] = '\0'; + + /* terminate value */ + len = strlen(val); + if (len == 0) + return -1; + while (isspace(val[len-1])) + len--; + val[len] = '\0'; + + if (len == 0) + return -1; + + /* unquote */ + if (val[0] == '"' || val[0] == '\'') { + if (val[len-1] != val[0]) { + log_debug("inconsistent quoting: '%s', skip", line); + return -1; + } + val[len-1] = '\0'; + val++; + } + + udev_device_add_property(dev, key, val); + + return 0; +} + +static int import_file_into_properties(struct udev_device *dev, const char *filename) { + FILE *f; + char line[UTIL_LINE_SIZE]; + + f = fopen(filename, "re"); + if (f == NULL) + return -1; + while (fgets(line, sizeof(line), f) != NULL) + import_property_from_string(dev, line); + fclose(f); + return 0; +} + +static int import_program_into_properties(struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + const char *program) { + char result[UTIL_LINE_SIZE]; + char *line; + int err; + + err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)); + if (err < 0) + return err; + + line = result; + while (line != NULL) { + char *pos; + + pos = strchr(line, '\n'); + if (pos != NULL) { + pos[0] = '\0'; + pos = &pos[1]; + } + import_property_from_string(event->dev, line); + line = pos; + } + return 0; +} + +static int import_parent_into_properties(struct udev_device *dev, const char *filter) { + struct udev_device *dev_parent; + struct udev_list_entry *list_entry; + + assert(dev); + assert(filter); + + dev_parent = udev_device_get_parent(dev); + if (dev_parent == NULL) + return -1; + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { + const char *key = udev_list_entry_get_name(list_entry); + const char *val = udev_list_entry_get_value(list_entry); + + if (fnmatch(filter, key, 0) == 0) + udev_device_add_property(dev, key, val); + } + return 0; +} + +static void attr_subst_subdir(char *attr, size_t len) { + const char *pos, *tail, *path; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + + pos = strstr(attr, "/*/"); + if (!pos) + return; + + tail = pos + 2; + path = strndupa(attr, pos - attr + 1); /* include slash at end */ + dir = opendir(path); + if (dir == NULL) + return; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) + if (dent->d_name[0] != '.') { + char n[strlen(dent->d_name) + strlen(tail) + 1]; + + strscpyl(n, sizeof n, dent->d_name, tail, NULL); + if (faccessat(dirfd(dir), n, F_OK, 0) == 0) { + strscpyl(attr, len, path, n, NULL); + break; + } + } +} + +static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) { + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL || linepos[0] == '\0') + return -1; + + /* skip whitespace */ + while (isspace(linepos[0]) || linepos[0] == ',') + linepos++; + + /* get the key */ + if (linepos[0] == '\0') + return -1; + *key = linepos; + + for (;;) { + linepos++; + if (linepos[0] == '\0') + return -1; + if (isspace(linepos[0])) + break; + if (linepos[0] == '=') + break; + if ((linepos[0] == '+') || (linepos[0] == '-') || (linepos[0] == '!') || (linepos[0] == ':')) + if (linepos[1] == '=') + break; + } + + /* remember end of key */ + temp = linepos; + + /* skip whitespace after key */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get operation type */ + if (linepos[0] == '=' && linepos[1] == '=') { + *op = OP_MATCH; + linepos += 2; + } else if (linepos[0] == '!' && linepos[1] == '=') { + *op = OP_NOMATCH; + linepos += 2; + } else if (linepos[0] == '+' && linepos[1] == '=') { + *op = OP_ADD; + linepos += 2; + } else if (linepos[0] == '-' && linepos[1] == '=') { + *op = OP_REMOVE; + linepos += 2; + } else if (linepos[0] == '=') { + *op = OP_ASSIGN; + linepos++; + } else if (linepos[0] == ':' && linepos[1] == '=') { + *op = OP_ASSIGN_FINAL; + linepos += 2; + } else + return -1; + + /* terminate key */ + temp[0] = '\0'; + + /* skip whitespace after operator */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get the value */ + if (linepos[0] == '"') + linepos++; + else + return -1; + *value = linepos; + + /* terminate */ + temp = strchr(linepos, '"'); + if (!temp) + return -1; + temp[0] = '\0'; + temp++; + + /* move line to next key */ + *line = temp; + return 0; +} + +/* extract possible KEY{attr} */ +static const char *get_key_attribute(struct udev *udev, char *str) { + char *pos; + char *attr; + + attr = strchr(str, '{'); + if (attr != NULL) { + attr++; + pos = strchr(attr, '}'); + if (pos == NULL) { + log_error("missing closing brace for format"); + return NULL; + } + pos[0] = '\0'; + return attr; + } + return NULL; +} + +static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, + enum operation_type op, + const char *value, const void *data) { + struct token *token = rule_tmp->token + rule_tmp->token_cur; + const char *attr = NULL; + + assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token)); + memzero(token, sizeof(struct token)); + + switch (type) { + case TK_M_ACTION: + case TK_M_DEVPATH: + case TK_M_KERNEL: + case TK_M_SUBSYSTEM: + case TK_M_DRIVER: + case TK_M_WAITFOR: + case TK_M_DEVLINK: + case TK_M_NAME: + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_TAGS: + case TK_M_PROGRAM: + case TK_M_IMPORT_FILE: + case TK_M_IMPORT_PROG: + case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: + case TK_M_IMPORT_PARENT: + case TK_M_RESULT: + case TK_A_OWNER: + case TK_A_GROUP: + case TK_A_MODE: + case TK_A_DEVLINK: + case TK_A_NAME: + case TK_A_GOTO: + case TK_M_TAG: + case TK_A_TAG: + case TK_A_STATIC_NODE: + token->key.value_off = rules_add_string(rule_tmp->rules, value); + break; + case TK_M_IMPORT_BUILTIN: + token->key.value_off = rules_add_string(rule_tmp->rules, value); + token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; + break; + case TK_M_ENV: + case TK_M_ATTR: + case TK_M_SYSCTL: + case TK_M_ATTRS: + case TK_A_ATTR: + case TK_A_SYSCTL: + case TK_A_ENV: + case TK_A_SECLABEL: + attr = data; + token->key.value_off = rules_add_string(rule_tmp->rules, value); + token->key.attr_off = rules_add_string(rule_tmp->rules, attr); + break; + case TK_M_TEST: + token->key.value_off = rules_add_string(rule_tmp->rules, value); + if (data != NULL) + token->key.mode = *(mode_t *)data; + break; + case TK_A_STRING_ESCAPE_NONE: + case TK_A_STRING_ESCAPE_REPLACE: + case TK_A_DB_PERSIST: + break; + case TK_A_RUN_BUILTIN: + case TK_A_RUN_PROGRAM: + token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; + token->key.value_off = rules_add_string(rule_tmp->rules, value); + break; + case TK_A_INOTIFY_WATCH: + case TK_A_DEVLINK_PRIO: + token->key.devlink_prio = *(int *)data; + break; + case TK_A_OWNER_ID: + token->key.uid = *(uid_t *)data; + break; + case TK_A_GROUP_ID: + token->key.gid = *(gid_t *)data; + break; + case TK_A_MODE_ID: + token->key.mode = *(mode_t *)data; + break; + case TK_RULE: + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_END: + case TK_UNSET: + assert_not_reached("wrong type"); + } + + if (value != NULL && type < TK_M_MAX) { + /* check if we need to split or call fnmatch() while matching rules */ + enum string_glob_type glob; + int has_split; + int has_glob; + + has_split = (strchr(value, '|') != NULL); + has_glob = string_is_glob(value); + if (has_split && has_glob) { + glob = GL_SPLIT_GLOB; + } else if (has_split) { + glob = GL_SPLIT; + } else if (has_glob) { + if (streq(value, "?*")) + glob = GL_SOMETHING; + else + glob = GL_GLOB; + } else { + glob = GL_PLAIN; + } + token->key.glob = glob; + } + + if (value != NULL && type > TK_M_MAX) { + /* check if assigned value has substitution chars */ + if (value[0] == '[') + token->key.subst = SB_SUBSYS; + else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) + token->key.subst = SB_FORMAT; + else + token->key.subst = SB_NONE; + } + + if (attr != NULL) { + /* check if property/attribute name has substitution chars */ + if (attr[0] == '[') + token->key.attrsubst = SB_SUBSYS; + else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) + token->key.attrsubst = SB_FORMAT; + else + token->key.attrsubst = SB_NONE; + } + + token->key.type = type; + token->key.op = op; + rule_tmp->token_cur++; +} + +static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { + unsigned int i; + unsigned int start = 0; + unsigned int end = rule_tmp->token_cur; + + for (i = 0; i < rule_tmp->token_cur; i++) { + enum token_type next_val = TK_UNSET; + unsigned int next_idx = 0; + unsigned int j; + + /* find smallest value */ + for (j = start; j < end; j++) { + if (rule_tmp->token[j].type == TK_UNSET) + continue; + if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { + next_val = rule_tmp->token[j].type; + next_idx = j; + } + } + + /* add token and mark done */ + if (add_token(rules, &rule_tmp->token[next_idx]) != 0) + return -1; + rule_tmp->token[next_idx].type = TK_UNSET; + + /* shrink range */ + if (next_idx == start) + start++; + if (next_idx+1 == end) + end--; + } + return 0; +} + +#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; } + +static void add_rule(struct udev_rules *rules, char *line, + const char *filename, unsigned int filename_off, unsigned int lineno) { + char *linepos; + const char *attr; + struct rule_tmp rule_tmp = { + .rules = rules, + .rule.type = TK_RULE, + }; + + /* the offset in the rule is limited to unsigned short */ + if (filename_off < USHRT_MAX) + rule_tmp.rule.rule.filename_off = filename_off; + rule_tmp.rule.rule.filename_line = lineno; + + linepos = line; + for (;;) { + char *key; + char *value; + enum operation_type op; + + if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) { + /* Avoid erroring on trailing whitespace. This is probably rare + * so save the work for the error case instead of always trying + * to strip the trailing whitespace with strstrip(). */ + while (isblank(*linepos)) + linepos++; + + /* If we aren't at the end of the line, this is a parsing error. + * Make a best effort to describe where the problem is. */ + if (!strchr(NEWLINE, *linepos)) { + char buf[2] = {*linepos}; + _cleanup_free_ char *tmp; + + tmp = cescape(buf); + log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')", + filename, lineno, linepos - line + 1, tmp); + if (*linepos == '#') + log_error("hint: comments can only start at beginning of line"); + } + break; + } + + if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token)) + LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur); + + if (streq(key, "ACTION")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); + + } else if (streq(key, "DEVPATH")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); + + } else if (streq(key, "KERNEL")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); + + } else if (streq(key, "SUBSYSTEM")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + /* bus, class, subsystem events should all be the same */ + if (STR_IN_SET(value, "subsystem", "bus", "class")) { + if (!streq(value, "subsystem")) + LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value); + + rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); + } else + rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); + + } else if (streq(key, "DRIVER")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); + + } else if (startswith(key, "ATTR{")) { + attr = get_key_attribute(rules->udev, key + strlen("ATTR")); + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTR"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ATTR"); + + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); + else + rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); + + } else if (startswith(key, "SYSCTL{")) { + attr = get_key_attribute(rules->udev, key + strlen("SYSCTL")); + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTR"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ATTR"); + + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr); + else + rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr); + + } else if (startswith(key, "SECLABEL{")) { + attr = get_key_attribute(rules->udev, key + strlen("SECLABEL")); + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "SECLABEL"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "SECLABEL"); + + rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr); + + } else if (streq(key, "KERNELS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); + + } else if (streq(key, "SUBSYSTEMS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); + + } else if (streq(key, "DRIVERS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); + + } else if (startswith(key, "ATTRS{")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", "ATTRS"); + + attr = get_key_attribute(rules->udev, key + strlen("ATTRS")); + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTRS"); + + if (startswith(attr, "device/")) + LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix"); + if (strstr(attr, "../") != NULL) + LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix"); + rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); + + } else if (streq(key, "TAGS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); + + } else if (startswith(key, "ENV{")) { + attr = get_key_attribute(rules->udev, key + strlen("ENV")); + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ENV"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ENV"); + + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr); + else { + if (STR_IN_SET(attr, + "ACTION", + "SUBSYSTEM", + "DEVTYPE", + "MAJOR", + "MINOR", + "DRIVER", + "IFINDEX", + "DEVNAME", + "DEVLINKS", + "DEVPATH", + "TAGS")) + LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr); + + rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr); + } + + } else if (streq(key, "TAG")) { + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); + else + rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); + + } else if (streq(key, "PROGRAM")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); + + } else if (streq(key, "RESULT")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); + + } else if (startswith(key, "IMPORT")) { + attr = get_key_attribute(rules->udev, key + strlen("IMPORT")); + if (attr == NULL) { + LOG_RULE_WARNING("ignoring IMPORT{} with missing type"); + continue; + } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "IMPORT"); + + if (streq(attr, "program")) { + /* find known built-in command */ + if (value[0] != '/') { + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + + if (cmd < UDEV_BUILTIN_MAX) { + LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value); + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + continue; + } + } + rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); + } else if (streq(attr, "builtin")) { + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + + if (cmd >= UDEV_BUILTIN_MAX) + LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value); + else + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + } else if (streq(attr, "file")) + rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); + else if (streq(attr, "db")) + rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); + else if (streq(attr, "cmdline")) + rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); + else if (streq(attr, "parent")) + rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); + else + LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr); + + } else if (startswith(key, "TEST")) { + mode_t mode = 0; + + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", "TEST"); + + attr = get_key_attribute(rules->udev, key + strlen("TEST")); + if (attr != NULL) { + mode = strtol(attr, NULL, 8); + rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); + } else + rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); + + } else if (startswith(key, "RUN")) { + attr = get_key_attribute(rules->udev, key + strlen("RUN")); + if (attr == NULL) + attr = "program"; + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "RUN"); + + if (streq(attr, "builtin")) { + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + + if (cmd < UDEV_BUILTIN_MAX) + rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd); + else + LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value); + } else if (streq(attr, "program")) { + const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; + + rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); + } else + LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr); + + } else if (streq(key, "LABEL")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + rule_tmp.rule.rule.label_off = rules_add_string(rules, value); + + } else if (streq(key, "GOTO")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); + + } else if (startswith(key, "NAME")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); + else { + if (streq(value, "%k")) { + LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove"); + continue; + } + if (isempty(value)) { + LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove"); + continue; + } + rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); + } + rule_tmp.rule.rule.can_set_name = true; + + } else if (streq(key, "SYMLINK")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); + else + rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); + rule_tmp.rule.rule.can_set_name = true; + + } else if (streq(key, "OWNER")) { + uid_t uid; + char *endptr; + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + uid = strtoul(value, &endptr, 10); + if (endptr[0] == '\0') + rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); + else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) { + uid = add_uid(rules, value); + rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); + } else if (rules->resolve_names >= 0) + rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); + + rule_tmp.rule.rule.can_set_name = true; + + } else if (streq(key, "GROUP")) { + gid_t gid; + char *endptr; + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + gid = strtoul(value, &endptr, 10); + if (endptr[0] == '\0') + rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); + else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { + gid = add_gid(rules, value); + rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); + } else if (rules->resolve_names >= 0) + rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); + + rule_tmp.rule.rule.can_set_name = true; + + } else if (streq(key, "MODE")) { + mode_t mode; + char *endptr; + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + mode = strtol(value, &endptr, 8); + if (endptr[0] == '\0') + rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); + else + rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); + rule_tmp.rule.rule.can_set_name = true; + + } else if (streq(key, "OPTIONS")) { + const char *pos; + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + pos = strstr(value, "link_priority="); + if (pos != NULL) { + int prio = atoi(pos + strlen("link_priority=")); + + rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); + } + + pos = strstr(value, "string_escape="); + if (pos != NULL) { + pos += strlen("string_escape="); + if (startswith(pos, "none")) + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); + else if (startswith(pos, "replace")) + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); + } + + pos = strstr(value, "db_persist"); + if (pos != NULL) + rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); + + pos = strstr(value, "nowatch"); + if (pos != NULL) { + const int off = 0; + + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); + } else { + pos = strstr(value, "watch"); + if (pos != NULL) { + const int on = 1; + + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); + } + } + + pos = strstr(value, "static_node="); + if (pos != NULL) { + pos += strlen("static_node="); + rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL); + rule_tmp.rule.rule.has_static_node = true; + } + + } else + LOG_AND_RETURN("unknown key '%s'", key); + } + + /* add rule token and sort tokens */ + rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; + if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0) + LOG_RULE_ERROR("failed to add rule token"); +} + +static int parse_file(struct udev_rules *rules, const char *filename) { + _cleanup_fclose_ FILE *f = NULL; + unsigned int first_token; + unsigned int filename_off; + char line[UTIL_LINE_SIZE]; + int line_nr = 0; + unsigned int i; + + f = fopen(filename, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + else + return -errno; + } + + if (null_or_empty_fd(fileno(f))) { + log_debug("Skipping empty file: %s", filename); + return 0; + } else + log_debug("Reading rules file: %s", filename); + + first_token = rules->token_cur; + filename_off = rules_add_string(rules, filename); + + while (fgets(line, sizeof(line), f) != NULL) { + char *key; + size_t len; + + /* skip whitespace */ + line_nr++; + key = line; + while (isspace(key[0])) + key++; + + /* comment */ + if (key[0] == '#') + continue; + + len = strlen(line); + if (len < 3) + continue; + + /* continue reading if backslash+newline is found */ + while (line[len-2] == '\\') { + if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) + break; + if (strlen(&line[len-2]) < 2) + break; + line_nr++; + len = strlen(line); + } + + if (len+1 >= sizeof(line)) { + log_error("line too long '%s':%u, ignored", filename, line_nr); + continue; + } + add_rule(rules, key, filename, filename_off, line_nr); + } + + /* link GOTOs to LABEL rules in this file to be able to fast-forward */ + for (i = first_token+1; i < rules->token_cur; i++) { + if (rules->tokens[i].type == TK_A_GOTO) { + char *label = rules_str(rules, rules->tokens[i].key.value_off); + unsigned int j; + + for (j = i+1; j < rules->token_cur; j++) { + if (rules->tokens[j].type != TK_RULE) + continue; + if (rules->tokens[j].rule.label_off == 0) + continue; + if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off))) + continue; + rules->tokens[i].key.rule_goto = j; + break; + } + if (rules->tokens[i].key.rule_goto == 0) + log_error("GOTO '%s' has no matching label in: '%s'", label, filename); + } + } + return 0; +} + +struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { + struct udev_rules *rules; + struct udev_list file_list; + struct token end_token; + char **files, **f; + int r; + + rules = new0(struct udev_rules, 1); + if (rules == NULL) + return NULL; + rules->udev = udev; + rules->resolve_names = resolve_names; + udev_list_init(udev, &file_list, true); + + /* init token array and string buffer */ + rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); + if (rules->tokens == NULL) + return udev_rules_unref(rules); + rules->token_max = PREALLOC_TOKEN; + + rules->strbuf = strbuf_new(); + if (!rules->strbuf) + return udev_rules_unref(rules); + + udev_rules_check_timestamp(rules); + + r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); + if (r < 0) { + log_error_errno(r, "failed to enumerate rules files: %m"); + return udev_rules_unref(rules); + } + + /* + * The offset value in the rules strct is limited; add all + * rules file names to the beginning of the string buffer. + */ + STRV_FOREACH(f, files) + rules_add_string(rules, *f); + + STRV_FOREACH(f, files) + parse_file(rules, *f); + + strv_free(files); + + memzero(&end_token, sizeof(struct token)); + end_token.type = TK_END; + add_token(rules, &end_token); + log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings", + rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len); + + /* cleanup temporary strbuf data */ + log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used", + rules->strbuf->in_count, rules->strbuf->in_len, + rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count); + strbuf_complete(rules->strbuf); + + /* cleanup uid/gid cache */ + rules->uids = mfree(rules->uids); + rules->uids_cur = 0; + rules->uids_max = 0; + rules->gids = mfree(rules->gids); + rules->gids_cur = 0; + rules->gids_max = 0; + + dump_rules(rules); + return rules; +} + +struct udev_rules *udev_rules_unref(struct udev_rules *rules) { + if (rules == NULL) + return NULL; + free(rules->tokens); + strbuf_cleanup(rules->strbuf); + free(rules->uids); + free(rules->gids); + free(rules); + return NULL; +} + +bool udev_rules_check_timestamp(struct udev_rules *rules) { + if (!rules) + return false; + + return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true); +} + +static int match_key(struct udev_rules *rules, struct token *token, const char *val) { + char *key_value = rules_str(rules, token->key.value_off); + char *pos; + bool match = false; + + if (val == NULL) + val = ""; + + switch (token->key.glob) { + case GL_PLAIN: + match = (streq(key_value, val)); + break; + case GL_GLOB: + match = (fnmatch(key_value, val, 0) == 0); + break; + case GL_SPLIT: + { + const char *s; + size_t len; + + s = rules_str(rules, token->key.value_off); + len = strlen(val); + for (;;) { + const char *next; + + next = strchr(s, '|'); + if (next != NULL) { + size_t matchlen = (size_t)(next - s); + + match = (matchlen == len && strneq(s, val, matchlen)); + if (match) + break; + } else { + match = (streq(s, val)); + break; + } + s = &next[1]; + } + break; + } + case GL_SPLIT_GLOB: + { + char value[UTIL_PATH_SIZE]; + + strscpy(value, sizeof(value), rules_str(rules, token->key.value_off)); + key_value = value; + while (key_value != NULL) { + pos = strchr(key_value, '|'); + if (pos != NULL) { + pos[0] = '\0'; + pos = &pos[1]; + } + match = (fnmatch(key_value, val, 0) == 0); + if (match) + break; + key_value = pos; + } + break; + } + case GL_SOMETHING: + match = (val[0] != '\0'); + break; + case GL_UNSET: + return -1; + } + + if (match && (token->key.op == OP_MATCH)) + return 0; + if (!match && (token->key.op == OP_NOMATCH)) + return 0; + return -1; +} + +static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) { + const char *name; + char nbuf[UTIL_NAME_SIZE]; + const char *value; + char vbuf[UTIL_NAME_SIZE]; + size_t len; + + name = rules_str(rules, cur->key.attr_off); + switch (cur->key.attrsubst) { + case SB_FORMAT: + udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); + name = nbuf; + /* fall through */ + case SB_NONE: + value = udev_device_get_sysattr_value(dev, name); + if (value == NULL) + return -1; + break; + case SB_SUBSYS: + if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) + return -1; + value = vbuf; + break; + default: + return -1; + } + + /* remove trailing whitespace, if not asked to match for it */ + len = strlen(value); + if (len > 0 && isspace(value[len-1])) { + const char *key_value; + size_t klen; + + key_value = rules_str(rules, cur->key.value_off); + klen = strlen(key_value); + if (klen > 0 && !isspace(key_value[klen-1])) { + if (value != vbuf) { + strscpy(vbuf, sizeof(vbuf), value); + value = vbuf; + } + while (len > 0 && isspace(vbuf[--len])) + vbuf[len] = '\0'; + } + } + + return match_key(rules, cur, value); +} + +enum escape_type { + ESCAPE_UNSET, + ESCAPE_NONE, + ESCAPE_REPLACE, +}; + +void udev_rules_apply_to_event(struct udev_rules *rules, + struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + struct udev_list *properties_list) { + struct token *cur; + struct token *rule; + enum escape_type esc = ESCAPE_UNSET; + bool can_set_name; + + if (rules->tokens == NULL) + return; + + can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) && + (major(udev_device_get_devnum(event->dev)) > 0 || + udev_device_get_ifindex(event->dev) > 0)); + + /* loop through token list, match, run actions or forward to next rule */ + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + dump_token(rules, cur); + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ + if (!can_set_name && rule->rule.can_set_name) + goto nomatch; + esc = ESCAPE_UNSET; + break; + case TK_M_ACTION: + if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DEVPATH: + if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) + goto nomatch; + break; + case TK_M_KERNEL: + if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DEVLINK: { + struct udev_list_entry *list_entry; + bool match = false; + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { + const char *devlink; + + devlink = udev_list_entry_get_name(list_entry) + strlen("/dev/"); + if (match_key(rules, cur, devlink) == 0) { + match = true; + break; + } + } + if (!match) + goto nomatch; + break; + } + case TK_M_NAME: + if (match_key(rules, cur, event->name) != 0) + goto nomatch; + break; + case TK_M_ENV: { + const char *key_name = rules_str(rules, cur->key.attr_off); + const char *value; + + value = udev_device_get_property_value(event->dev, key_name); + + /* check global properties */ + if (!value && properties_list) { + struct udev_list_entry *list_entry; + + list_entry = udev_list_get_entry(properties_list); + list_entry = udev_list_entry_get_by_name(list_entry, key_name); + if (list_entry != NULL) + value = udev_list_entry_get_value(list_entry); + } + + if (!value) + value = ""; + if (match_key(rules, cur, value)) + goto nomatch; + break; + } + case TK_M_TAG: { + struct udev_list_entry *list_entry; + bool match = false; + + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { + if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) { + match = true; + break; + } + } + if ((!match && (cur->key.op != OP_NOMATCH)) || + (match && (cur->key.op == OP_NOMATCH))) + goto nomatch; + break; + } + case TK_M_SUBSYSTEM: + if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DRIVER: + if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) + goto nomatch; + break; + case TK_M_ATTR: + if (match_attr(rules, event->dev, event, cur) != 0) + goto nomatch; + break; + case TK_M_SYSCTL: { + char filename[UTIL_PATH_SIZE]; + _cleanup_free_ char *value = NULL; + size_t len; + + udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename)); + sysctl_normalize(filename); + if (sysctl_read(filename, &value) < 0) + goto nomatch; + + len = strlen(value); + while (len > 0 && isspace(value[--len])) + value[len] = '\0'; + if (match_key(rules, cur, value) != 0) + goto nomatch; + break; + } + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_ATTRS: + case TK_M_TAGS: { + struct token *next; + + /* get whole sequence of parent matches */ + next = cur; + while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) + next++; + + /* loop over parents */ + event->dev_parent = event->dev; + for (;;) { + struct token *key; + + /* loop over sequence of parent match keys */ + for (key = cur; key < next; key++ ) { + dump_token(rules, key); + switch(key->type) { + case TK_M_KERNELS: + if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_SUBSYSTEMS: + if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_DRIVERS: + if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_ATTRS: + if (match_attr(rules, event->dev_parent, event, key) != 0) + goto try_parent; + break; + case TK_M_TAGS: { + bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off)); + + if (match && key->key.op == OP_NOMATCH) + goto try_parent; + if (!match && key->key.op == OP_MATCH) + goto try_parent; + break; + } + default: + goto nomatch; + } + } + break; + + try_parent: + event->dev_parent = udev_device_get_parent(event->dev_parent); + if (event->dev_parent == NULL) + goto nomatch; + } + /* move behind our sequence of parent match keys */ + cur = next; + continue; + } + case TK_M_TEST: { + char filename[UTIL_PATH_SIZE]; + struct stat statbuf; + int match; + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename)); + if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { + if (filename[0] != '/') { + char tmp[UTIL_PATH_SIZE]; + + strscpy(tmp, sizeof(tmp), filename); + strscpyl(filename, sizeof(filename), + udev_device_get_syspath(event->dev), "/", tmp, NULL); + } + } + attr_subst_subdir(filename, sizeof(filename)); + + match = (stat(filename, &statbuf) == 0); + if (match && cur->key.mode > 0) + match = ((statbuf.st_mode & cur->key.mode) > 0); + if (match && cur->key.op == OP_NOMATCH) + goto nomatch; + if (!match && cur->key.op == OP_MATCH) + goto nomatch; + break; + } + case TK_M_PROGRAM: { + char program[UTIL_PATH_SIZE]; + char result[UTIL_LINE_SIZE]; + + event->program_result = mfree(event->program_result); + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program)); + log_debug("PROGRAM '%s' %s:%u", + program, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + + if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) { + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } else { + int count; + + util_remove_trailing_chars(result, '\n'); + if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { + count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); + if (count > 0) + log_debug("%i character(s) replaced" , count); + } + event->program_result = strdup(result); + if (cur->key.op == OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_FILE: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); + if (import_file_into_properties(event->dev, import) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_PROG: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); + log_debug("IMPORT '%s' %s:%u", + import, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + + if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_BUILTIN: { + char command[UTIL_PATH_SIZE]; + + if (udev_builtin_run_once(cur->key.builtin_cmd)) { + /* check if we ran already */ + if (event->builtin_run & (1 << cur->key.builtin_cmd)) { + log_debug("IMPORT builtin skip '%s' %s:%u", + udev_builtin_name(cur->key.builtin_cmd), + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + /* return the result from earlier run */ + if (event->builtin_ret & (1 << cur->key.builtin_cmd)) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + /* mark as ran */ + event->builtin_run |= (1 << cur->key.builtin_cmd); + } + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command)); + log_debug("IMPORT builtin '%s' %s:%u", + udev_builtin_name(cur->key.builtin_cmd), + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + + if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { + /* remember failure */ + log_debug("IMPORT builtin '%s' returned non-zero", + udev_builtin_name(cur->key.builtin_cmd)); + event->builtin_ret |= (1 << cur->key.builtin_cmd); + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_DB: { + const char *key = rules_str(rules, cur->key.value_off); + const char *value; + + value = udev_device_get_property_value(event->dev_db, key); + if (value != NULL) + udev_device_add_property(event->dev, key, value); + else { + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_CMDLINE: { + _cleanup_fclose_ FILE *f = NULL; + bool imported = false; + + f = fopen("/proc/cmdline", "re"); + if (f != NULL) { + char cmdline[4096]; + + if (fgets(cmdline, sizeof(cmdline), f) != NULL) { + const char *key = rules_str(rules, cur->key.value_off); + char *pos; + + pos = strstr(cmdline, key); + if (pos != NULL) { + imported = true; + pos += strlen(key); + if (pos[0] == '\0' || isspace(pos[0])) + /* we import simple flags as 'FLAG=1' */ + udev_device_add_property(event->dev, key, "1"); + else if (pos[0] == '=') { + const char *value; + + pos++; + value = pos; + while (pos[0] != '\0' && !isspace(pos[0])) + pos++; + pos[0] = '\0'; + udev_device_add_property(event->dev, key, value); + } + } + } + } + if (!imported && cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_PARENT: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); + if (import_parent_into_properties(event->dev, import) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_RESULT: + if (match_key(rules, cur, event->program_result) != 0) + goto nomatch; + break; + case TK_A_STRING_ESCAPE_NONE: + esc = ESCAPE_NONE; + break; + case TK_A_STRING_ESCAPE_REPLACE: + esc = ESCAPE_REPLACE; + break; + case TK_A_DB_PERSIST: + udev_device_set_db_persist(event->dev); + break; + case TK_A_INOTIFY_WATCH: + if (event->inotify_watch_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->inotify_watch_final = true; + event->inotify_watch = cur->key.watch; + break; + case TK_A_DEVLINK_PRIO: + udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); + break; + case TK_A_OWNER: { + char owner[UTIL_NAME_SIZE]; + const char *ow = owner; + int r; + + if (event->owner_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->owner_final = true; + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner)); + event->owner_set = true; + r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); + if (r < 0) { + if (r == -ENOENT || r == -ESRCH) + log_error("specified user '%s' unknown", owner); + else + log_error_errno(r, "error resolving user '%s': %m", owner); + + event->uid = 0; + } + log_debug("OWNER %u %s:%u", + event->uid, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + case TK_A_GROUP: { + char group[UTIL_NAME_SIZE]; + const char *gr = group; + int r; + + if (event->group_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->group_final = true; + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group)); + event->group_set = true; + r = get_group_creds(&gr, &event->gid); + if (r < 0) { + if (r == -ENOENT || r == -ESRCH) + log_error("specified group '%s' unknown", group); + else + log_error_errno(r, "error resolving group '%s': %m", group); + + event->gid = 0; + } + log_debug("GROUP %u %s:%u", + event->gid, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + case TK_A_MODE: { + char mode_str[UTIL_NAME_SIZE]; + mode_t mode; + char *endptr; + + if (event->mode_final) + break; + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str)); + mode = strtol(mode_str, &endptr, 8); + if (endptr[0] != '\0') { + log_error("ignoring invalid mode '%s'", mode_str); + break; + } + if (cur->key.op == OP_ASSIGN_FINAL) + event->mode_final = true; + event->mode_set = true; + event->mode = mode; + log_debug("MODE %#o %s:%u", + event->mode, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + case TK_A_OWNER_ID: + if (event->owner_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->owner_final = true; + event->owner_set = true; + event->uid = cur->key.uid; + log_debug("OWNER %u %s:%u", + event->uid, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + case TK_A_GROUP_ID: + if (event->group_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->group_final = true; + event->group_set = true; + event->gid = cur->key.gid; + log_debug("GROUP %u %s:%u", + event->gid, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + case TK_A_MODE_ID: + if (event->mode_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->mode_final = true; + event->mode_set = true; + event->mode = cur->key.mode; + log_debug("MODE %#o %s:%u", + event->mode, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + case TK_A_SECLABEL: { + const char *name, *label; + + name = rules_str(rules, cur->key.attr_off); + label = rules_str(rules, cur->key.value_off); + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_list_cleanup(&event->seclabel_list); + udev_list_entry_add(&event->seclabel_list, name, label); + log_debug("SECLABEL{%s}='%s' %s:%u", + name, label, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + case TK_A_ENV: { + const char *name = rules_str(rules, cur->key.attr_off); + char *value = rules_str(rules, cur->key.value_off); + char value_new[UTIL_NAME_SIZE]; + const char *value_old = NULL; + + if (value[0] == '\0') { + if (cur->key.op == OP_ADD) + break; + udev_device_add_property(event->dev, name, NULL); + break; + } + + if (cur->key.op == OP_ADD) + value_old = udev_device_get_property_value(event->dev, name); + if (value_old) { + char temp[UTIL_NAME_SIZE]; + + /* append value separated by space */ + udev_event_apply_format(event, value, temp, sizeof(temp)); + strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL); + } else + udev_event_apply_format(event, value, value_new, sizeof(value_new)); + + udev_device_add_property(event->dev, name, value_new); + break; + } + case TK_A_TAG: { + char tag[UTIL_PATH_SIZE]; + const char *p; + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag)); + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_device_cleanup_tags_list(event->dev); + for (p = tag; *p != '\0'; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + continue; + log_error("ignoring invalid tag name '%s'", tag); + break; + } + if (cur->key.op == OP_REMOVE) + udev_device_remove_tag(event->dev, tag); + else + udev_device_add_tag(event->dev, tag); + break; + } + case TK_A_NAME: { + const char *name = rules_str(rules, cur->key.value_off); + + char name_str[UTIL_PATH_SIZE]; + int count; + + if (event->name_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->name_final = true; + udev_event_apply_format(event, name, name_str, sizeof(name_str)); + if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { + count = util_replace_chars(name_str, "/"); + if (count > 0) + log_debug("%i character(s) replaced", count); + } + if (major(udev_device_get_devnum(event->dev)) && + !streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/"))) { + log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n", + name, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + if (free_and_strdup(&event->name, name_str) < 0) { + log_oom(); + return; + } + log_debug("NAME '%s' %s:%u", + event->name, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + break; + } + case TK_A_DEVLINK: { + char temp[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + char *pos, *next; + int count = 0; + + if (event->devlink_final) + break; + if (major(udev_device_get_devnum(event->dev)) == 0) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->devlink_final = true; + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_device_cleanup_devlinks_list(event->dev); + + /* allow multiple symlinks separated by spaces */ + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp)); + if (esc == ESCAPE_UNSET) + count = util_replace_chars(temp, "/ "); + else if (esc == ESCAPE_REPLACE) + count = util_replace_chars(temp, "/"); + if (count > 0) + log_debug("%i character(s) replaced" , count); + pos = temp; + while (isspace(pos[0])) + pos++; + next = strchr(pos, ' '); + while (next != NULL) { + next[0] = '\0'; + log_debug("LINK '%s' %s:%u", pos, + rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); + strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); + udev_device_add_devlink(event->dev, filename); + while (isspace(next[1])) + next++; + pos = &next[1]; + next = strchr(pos, ' '); + } + if (pos[0] != '\0') { + log_debug("LINK '%s' %s:%u", pos, + rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); + strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); + udev_device_add_devlink(event->dev, filename); + } + break; + } + case TK_A_ATTR: { + const char *key_name = rules_str(rules, cur->key.attr_off); + char attr[UTIL_PATH_SIZE]; + char value[UTIL_NAME_SIZE]; + _cleanup_fclose_ FILE *f = NULL; + + if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) + strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); + attr_subst_subdir(attr, sizeof(attr)); + + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value)); + log_debug("ATTR '%s' writing '%s' %s:%u", attr, value, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + f = fopen(attr, "we"); + if (f == NULL) + log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); + else if (fprintf(f, "%s", value) <= 0) + log_error_errno(errno, "error writing ATTR{%s}: %m", attr); + break; + } + case TK_A_SYSCTL: { + char filename[UTIL_PATH_SIZE]; + char value[UTIL_NAME_SIZE]; + int r; + + udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename)); + sysctl_normalize(filename); + udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value)); + log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value, + rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); + r = sysctl_write(filename, value); + if (r < 0) + log_error_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value); + break; + } + case TK_A_RUN_BUILTIN: + case TK_A_RUN_PROGRAM: { + struct udev_list_entry *entry; + + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_list_cleanup(&event->run_list); + log_debug("RUN '%s' %s:%u", + rules_str(rules, cur->key.value_off), + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL); + udev_list_entry_set_num(entry, cur->key.builtin_cmd); + break; + } + case TK_A_GOTO: + if (cur->key.rule_goto == 0) + break; + cur = &rules->tokens[cur->key.rule_goto]; + continue; + case TK_END: + return; + + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_UNSET: + log_error("wrong type %u", cur->type); + goto nomatch; + } + + cur++; + continue; + nomatch: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + } +} + +int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { + struct token *cur; + struct token *rule; + uid_t uid = 0; + gid_t gid = 0; + mode_t mode = 0; + _cleanup_strv_free_ char **tags = NULL; + char **t; + FILE *f = NULL; + _cleanup_free_ char *path = NULL; + int r; + + if (rules->tokens == NULL) + return 0; + + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + + /* skip rules without a static_node tag */ + if (!rule->rule.has_static_node) + goto next; + + uid = 0; + gid = 0; + mode = 0; + tags = strv_free(tags); + break; + case TK_A_OWNER_ID: + uid = cur->key.uid; + break; + case TK_A_GROUP_ID: + gid = cur->key.gid; + break; + case TK_A_MODE_ID: + mode = cur->key.mode; + break; + case TK_A_TAG: + r = strv_extend(&tags, rules_str(rules, cur->key.value_off)); + if (r < 0) + goto finish; + + break; + case TK_A_STATIC_NODE: { + char device_node[UTIL_PATH_SIZE]; + char tags_dir[UTIL_PATH_SIZE]; + char tag_symlink[UTIL_PATH_SIZE]; + struct stat stats; + + /* we assure, that the permissions tokens are sorted before the static token */ + + if (mode == 0 && uid == 0 && gid == 0 && tags == NULL) + goto next; + + strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL); + if (stat(device_node, &stats) != 0) + break; + if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) + break; + + /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */ + if (tags) { + STRV_FOREACH(t, tags) { + _cleanup_free_ char *unescaped_filename = NULL; + + strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL); + r = mkdir_p(tags_dir, 0755); + if (r < 0) + return log_error_errno(r, "failed to create %s: %m", tags_dir); + + unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/."); + + strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL); + r = symlink(device_node, tag_symlink); + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "failed to create symlink %s -> %s: %m", + tag_symlink, device_node); + } + } + + /* don't touch the permissions if only the tags were set */ + if (mode == 0 && uid == 0 && gid == 0) + break; + + if (mode == 0) { + if (gid > 0) + mode = 0660; + else + mode = 0600; + } + if (mode != (stats.st_mode & 01777)) { + r = chmod(device_node, mode); + if (r < 0) { + log_error("failed to chmod '%s' %#o", device_node, mode); + return -errno; + } else + log_debug("chmod '%s' %#o", device_node, mode); + } + + if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { + r = chown(device_node, uid, gid); + if (r < 0) { + log_error("failed to chown '%s' %u %u ", device_node, uid, gid); + return -errno; + } else + log_debug("chown '%s' %u %u", device_node, uid, gid); + } + + utimensat(AT_FDCWD, device_node, NULL, 0); + break; + } + case TK_END: + goto finish; + } + + cur++; + continue; +next: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + continue; + } + +finish: + if (f) { + fflush(f); + fchmod(fileno(f), 0644); + if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) { + unlink_noerrno("/run/udev/static_node-tags"); + unlink_noerrno(path); + return -errno; + } + } + + return 0; +} diff --git a/src/grp-udev/libudev-core/udev-watch.c b/src/grp-udev/libudev-core/udev-watch.c new file mode 100644 index 0000000000..7dcc71556e --- /dev/null +++ b/src/grp-udev/libudev-core/udev-watch.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2004-2012 Kay Sievers + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "basic/stdio-util.h" +#include "udev.h" + +static int inotify_fd = -1; + +/* inotify descriptor, will be shared with rules directory; + * set to cloexec since we need our children to be able to add + * watches for us + */ +int udev_watch_init(struct udev *udev) { + inotify_fd = inotify_init1(IN_CLOEXEC); + if (inotify_fd < 0) + log_error_errno(errno, "inotify_init failed: %m"); + return inotify_fd; +} + +/* move any old watches directory out of the way, and then restore + * the watches + */ +void udev_watch_restore(struct udev *udev) { + if (inotify_fd < 0) + return; + + if (rename("/run/udev/watch", "/run/udev/watch.old") == 0) { + DIR *dir; + struct dirent *ent; + + dir = opendir("/run/udev/watch.old"); + if (dir == NULL) { + log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m"); + return; + } + + for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { + char device[UTIL_PATH_SIZE]; + ssize_t len; + struct udev_device *dev; + + if (ent->d_name[0] == '.') + continue; + + len = readlinkat(dirfd(dir), ent->d_name, device, sizeof(device)); + if (len <= 0 || len == (ssize_t)sizeof(device)) + goto unlink; + device[len] = '\0'; + + dev = udev_device_new_from_device_id(udev, device); + if (dev == NULL) + goto unlink; + + log_debug("restoring old watch on '%s'", udev_device_get_devnode(dev)); + udev_watch_begin(udev, dev); + udev_device_unref(dev); +unlink: + unlinkat(dirfd(dir), ent->d_name, 0); + } + + closedir(dir); + rmdir("/run/udev/watch.old"); + + } else if (errno != ENOENT) + log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m"); +} + +void udev_watch_begin(struct udev *udev, struct udev_device *dev) { + char filename[UTIL_PATH_SIZE]; + int wd; + int r; + + if (inotify_fd < 0) + return; + + log_debug("adding watch on '%s'", udev_device_get_devnode(dev)); + wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + if (wd < 0) { + log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m", + inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + return; + } + + xsprintf(filename, "/run/udev/watch/%d", wd); + mkdir_parents(filename, 0755); + unlink(filename); + r = symlink(udev_device_get_id_filename(dev), filename); + if (r < 0) + log_error_errno(errno, "Failed to create symlink %s: %m", filename); + + udev_device_set_watch_handle(dev, wd); +} + +void udev_watch_end(struct udev *udev, struct udev_device *dev) { + int wd; + char filename[UTIL_PATH_SIZE]; + + if (inotify_fd < 0) + return; + + wd = udev_device_get_watch_handle(dev); + if (wd < 0) + return; + + log_debug("removing watch on '%s'", udev_device_get_devnode(dev)); + inotify_rm_watch(inotify_fd, wd); + + xsprintf(filename, "/run/udev/watch/%d", wd); + unlink(filename); + + udev_device_set_watch_handle(dev, -1); +} + +struct udev_device *udev_watch_lookup(struct udev *udev, int wd) { + char filename[UTIL_PATH_SIZE]; + char device[UTIL_NAME_SIZE]; + ssize_t len; + + if (inotify_fd < 0 || wd < 0) + return NULL; + + xsprintf(filename, "/run/udev/watch/%d", wd); + len = readlink(filename, device, sizeof(device)); + if (len <= 0 || (size_t)len == sizeof(device)) + return NULL; + device[len] = '\0'; + + return udev_device_new_from_device_id(udev, device); +} diff --git a/src/grp-udev/mtd_probe/75-probe_mtd.rules b/src/grp-udev/mtd_probe/75-probe_mtd.rules new file mode 100644 index 0000000000..8848aeeaed --- /dev/null +++ b/src/grp-udev/mtd_probe/75-probe_mtd.rules @@ -0,0 +1,7 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add", GOTO="mtd_probe_end" + +KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode" + +LABEL="mtd_probe_end" diff --git a/src/grp-udev/mtd_probe/Makefile b/src/grp-udev/mtd_probe/Makefile new file mode 100644 index 0000000000..d7392a8a3b --- /dev/null +++ b/src/grp-udev/mtd_probe/Makefile @@ -0,0 +1,37 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +mtd_probe_SOURCES = \ + src/udev/mtd_probe/mtd_probe.c \ + src/udev/mtd_probe/mtd_probe.h \ + src/udev/mtd_probe/probe_smartmedia.c + +dist_udevrules_DATA += \ + rules/75-probe_mtd.rules + +udevlibexec_PROGRAMS += \ + mtd_probe + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/mtd_probe/mtd_probe.c b/src/grp-udev/mtd_probe/mtd_probe.c new file mode 100644 index 0000000000..462fab7623 --- /dev/null +++ b/src/grp-udev/mtd_probe/mtd_probe.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtd_probe.h" + +int main(int argc, char** argv) +{ + int mtd_fd; + int error; + mtd_info_t mtd_info; + + if (argc != 2) { + printf("usage: mtd_probe /dev/mtd[n]\n"); + return 1; + } + + mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC); + if (mtd_fd == -1) { + perror("open"); + exit(-1); + } + + error = ioctl(mtd_fd, MEMGETINFO, &mtd_info); + if (error == -1) { + perror("ioctl"); + exit(-1); + } + + probe_smart_media(mtd_fd, &mtd_info); + return -1; +} diff --git a/src/grp-udev/mtd_probe/mtd_probe.h b/src/grp-udev/mtd_probe/mtd_probe.h new file mode 100644 index 0000000000..83f241ac94 --- /dev/null +++ b/src/grp-udev/mtd_probe/mtd_probe.h @@ -0,0 +1,51 @@ +#pragma once + +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include + +#include "basic/macro.h" + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} _packed_; + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + +void probe_smart_media(int mtd_fd, mtd_info_t *info); diff --git a/src/grp-udev/mtd_probe/probe_smartmedia.c b/src/grp-udev/mtd_probe/probe_smartmedia.c new file mode 100644 index 0000000000..2a7ba17637 --- /dev/null +++ b/src/grp-udev/mtd_probe/probe_smartmedia.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtd_probe.h" + +static const uint8_t cis_signature[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; + + +void probe_smart_media(int mtd_fd, mtd_info_t* info) +{ + int sector_size; + int block_size; + int size_in_megs; + int spare_count; + char* cis_buffer = malloc(SM_SECTOR_SIZE); + int offset; + int cis_found = 0; + + if (!cis_buffer) + return; + + if (info->type != MTD_NANDFLASH) + goto exit; + + sector_size = info->writesize; + block_size = info->erasesize; + size_in_megs = info->size / (1024 * 1024); + + if (sector_size != SM_SECTOR_SIZE && sector_size != SM_SMALL_PAGE) + goto exit; + + switch(size_in_megs) { + case 1: + case 2: + spare_count = 6; + break; + case 4: + spare_count = 12; + break; + default: + spare_count = 24; + break; + } + + for (offset = 0 ; offset < block_size * spare_count ; + offset += sector_size) { + lseek(mtd_fd, SEEK_SET, offset); + if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE) { + cis_found = 1; + break; + } + } + + if (!cis_found) + goto exit; + + if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 && + (memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, + sizeof(cis_signature)) != 0)) + goto exit; + + printf("MTD_FTL=smartmedia\n"); + free(cis_buffer); + exit(0); +exit: + free(cis_buffer); + return; +} diff --git a/src/grp-udev/rules/.gitignore b/src/grp-udev/rules/.gitignore new file mode 100644 index 0000000000..93a50ddd80 --- /dev/null +++ b/src/grp-udev/rules/.gitignore @@ -0,0 +1 @@ +/99-systemd.rules diff --git a/src/grp-udev/rules/50-udev-default.rules b/src/grp-udev/rules/50-udev-default.rules new file mode 100644 index 0000000000..e9eeb8518e --- /dev/null +++ b/src/grp-udev/rules/50-udev-default.rules @@ -0,0 +1,77 @@ +# do not edit this file, it will be overwritten on update + +# run a command on remove events +ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" +ACTION=="remove", GOTO="default_end" + +SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" + +# select "system RTC" or just use the first one +SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" +SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100" + +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" +SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" +ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" + +ACTION!="add", GOTO="default_end" + +SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666" +SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666" +SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620" +SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty" +KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" + +SUBSYSTEM=="mem", KERNEL=="mem|kmem|port", GROUP="kmem", MODE="0640" + +SUBSYSTEM=="input", GROUP="input" +SUBSYSTEM=="input", KERNEL=="js[0-9]*", MODE="0664" + +SUBSYSTEM=="video4linux", GROUP="video" +SUBSYSTEM=="graphics", GROUP="video" +SUBSYSTEM=="drm", GROUP="video" +SUBSYSTEM=="dvb", GROUP="video" + +SUBSYSTEM=="sound", GROUP="audio", \ + OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" + +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" + +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" + +KERNEL=="parport[0-9]*", GROUP="lp" +SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" +SUBSYSTEM=="ppdev", GROUP="lp" +KERNEL=="lp[0-9]*", GROUP="lp" +KERNEL=="irlpt[0-9]*", GROUP="lp" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" + +SUBSYSTEM=="block", GROUP="disk" +SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" +KERNEL=="sch[0-9]*", GROUP="cdrom" +KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" +KERNEL=="pktcdvd", GROUP="cdrom" + +SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" +KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" +KERNEL=="loop-control", GROUP="disk", OPTIONS+="static_node=loop-control" +KERNEL=="btrfs-control", GROUP="disk" +KERNEL=="rawctl", GROUP="disk" +SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" +SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" +SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" + +KERNEL=="rfkill", MODE="0664" +KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" + +KERNEL=="fuse", MODE="0666", OPTIONS+="static_node=fuse" + +LABEL="default_end" diff --git a/src/grp-udev/rules/60-block.rules b/src/grp-udev/rules/60-block.rules new file mode 100644 index 0000000000..42c75974a5 --- /dev/null +++ b/src/grp-udev/rules/60-block.rules @@ -0,0 +1,11 @@ +# do not edit this file, it will be overwritten on update + +# enable in-kernel media-presence polling +ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", \ + ATTR{parameters/events_dfl_poll_msecs}="2000" + +# forward scsi device event to corresponding block device +ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" + +# watch metadata changes, caused by tools closing the device node which was opened for writing +ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*", OPTIONS+="watch" diff --git a/src/grp-udev/rules/60-drm.rules b/src/grp-udev/rules/60-drm.rules new file mode 100644 index 0000000000..1ed3e445f2 --- /dev/null +++ b/src/grp-udev/rules/60-drm.rules @@ -0,0 +1,3 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id" diff --git a/src/grp-udev/rules/60-evdev.rules b/src/grp-udev/rules/60-evdev.rules new file mode 100644 index 0000000000..ade7e7f646 --- /dev/null +++ b/src/grp-udev/rules/60-evdev.rules @@ -0,0 +1,19 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="evdev_end" +KERNEL!="event*", GOTO="evdev_end" + +# skip later rules when we find something for this input device +IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +# AT keyboard matching by the machine's DMI data +ENV{ID_INPUT_KEY}=="?*", DRIVERS=="atkbd", \ + IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +# device matching the input device name and the machine's DMI data +KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \ + RUN{builtin}+="keyboard", GOTO="evdev_end" + +LABEL="evdev_end" diff --git a/src/grp-udev/rules/60-persistent-alsa.rules b/src/grp-udev/rules/60-persistent-alsa.rules new file mode 100644 index 0000000000..8154e2dbb5 --- /dev/null +++ b/src/grp-udev/rules/60-persistent-alsa.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_alsa_end" +SUBSYSTEM!="sound", GOTO="persistent_alsa_end" +KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" + +LABEL="persistent_alsa_end" diff --git a/src/grp-udev/rules/60-persistent-input.rules b/src/grp-udev/rules/60-persistent-input.rules new file mode 100644 index 0000000000..607144bf8a --- /dev/null +++ b/src/grp-udev/rules/60-persistent-input.rules @@ -0,0 +1,40 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_input_end" +SUBSYSTEM!="input", GOTO="persistent_input_end" +SUBSYSTEMS=="bluetooth", ENV{ID_BUS}="bluetooth", GOTO="persistent_input_end" +SUBSYSTEMS=="rmi4", ENV{ID_BUS}="rmi", GOTO="persistent_input_end" +SUBSYSTEMS=="serio", ENV{ID_BUS}="i8042", GOTO="persistent_input_end" + +SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" + +# determine class name for persistent symlinks +ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" +ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" +DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" +ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" + +# fill empty serial number +ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" + +# by-id links +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="|00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" +# allow empty class for USB devices, by appending the interface number +SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ + SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" + +# by-path +SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" +ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" +# allow empty class for platform and usb devices; platform supports only a single interface that way +SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ + SYMLINK+="input/by-path/$env{ID_PATH}-event" + +LABEL="persistent_input_end" diff --git a/src/grp-udev/rules/60-persistent-storage-tape.rules b/src/grp-udev/rules/60-persistent-storage-tape.rules new file mode 100644 index 0000000000..b604864ee8 --- /dev/null +++ b/src/grp-udev/rules/60-persistent-storage-tape.rules @@ -0,0 +1,26 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/tape/{by-id,by-path} + +ACTION=="remove", GOTO="persistent_storage_tape_end" +ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_tape_end" + +# type 8 devices are "Medium Changers" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ + SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" + +SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" + +KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" +KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" + +# by-path (parent device path) +KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" +KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" +KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" + +LABEL="persistent_storage_tape_end" diff --git a/src/grp-udev/rules/60-persistent-storage.rules b/src/grp-udev/rules/60-persistent-storage.rules new file mode 100644 index 0000000000..d7bbbf9866 --- /dev/null +++ b/src/grp-udev/rules/60-persistent-storage.rules @@ -0,0 +1,88 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} +# scheme based on "Linux persistent device names", 2004, Hannes Reinecke + +ACTION=="remove", GOTO="persistent_storage_end" +ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_end" + +SUBSYSTEM!="block", GOTO="persistent_storage_end" +KERNEL!="loop*|mmcblk*[0-9]|msblk*[0-9]|mspblk*[0-9]|nvme*|sd*|sr*|vd*|xvd*|bcache*|cciss*|dasd*|ubd*|scm*|pmem*", GOTO="persistent_storage_end" + +# ignore partitions that span the entire disk +TEST=="whole_disk", GOTO="persistent_storage_end" + +# for partitions import parent information +ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" + +# NVMe +KERNEL=="nvme*[0-9]n*[0-9]", ATTR{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}" +KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{wwid}=="?*", SYMLINK+="disk/by-id/nvme-$attr{wwid}-part%n" + +# virtio-blk +KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" +KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" + +# ATA +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" + +# ATAPI devices (SPC-3 or later) +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" + +# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" + +# Fall back usb_id for USB devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" + +# SCSI devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" +KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" +KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" + +# FireWire +KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" +KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" + +# MMC +KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ + ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + +# Memstick +KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ + ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="msblk[0-9]p[0-9]|mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + +# by-path +ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" +ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" + +# probe filesystem metadata of optical drives which have a media inserted +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ + IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" +# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ + IMPORT{builtin}="blkid --noraid" + +# probe filesystem metadata of disks +KERNEL!="sr*", IMPORT{builtin}="blkid" + +# by-label/by-uuid links (filesystem metadata) +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" +ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" + +# by-id (World Wide Name) +ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" +ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" + +# by-partlabel/by-partuuid links (partition metadata) +ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" + +# add symlink to GPT root disk +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root" + +LABEL="persistent_storage_end" diff --git a/src/grp-udev/rules/60-serial.rules b/src/grp-udev/rules/60-serial.rules new file mode 100644 index 0000000000..f303e27fd5 --- /dev/null +++ b/src/grp-udev/rules/60-serial.rules @@ -0,0 +1,26 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="serial_end" +SUBSYSTEM!="tty", GOTO="serial_end" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" + +# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices +KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" + +SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" + +IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="", GOTO="serial_end" +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" +ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end" +ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" +ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" + +LABEL="serial_end" diff --git a/src/grp-udev/rules/64-btrfs.rules b/src/grp-udev/rules/64-btrfs.rules new file mode 100644 index 0000000000..fe0100131e --- /dev/null +++ b/src/grp-udev/rules/64-btrfs.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="block", GOTO="btrfs_end" +ACTION=="remove", GOTO="btrfs_end" +ENV{ID_FS_TYPE}!="btrfs", GOTO="btrfs_end" + +# let the kernel know about this btrfs filesystem, and check if it is complete +IMPORT{builtin}="btrfs ready $devnode" + +# mark the device as not ready to be used by the system +ENV{ID_BTRFS_READY}=="0", ENV{SYSTEMD_READY}="0" + +LABEL="btrfs_end" diff --git a/src/grp-udev/rules/70-mouse.rules b/src/grp-udev/rules/70-mouse.rules new file mode 100644 index 0000000000..3ea743aff9 --- /dev/null +++ b/src/grp-udev/rules/70-mouse.rules @@ -0,0 +1,18 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="mouse_end" +KERNEL!="event*", GOTO="mouse_end" +ENV{ID_INPUT_MOUSE}=="", GOTO="mouse_end" + +# mouse::vp:name::* +KERNELS=="input*", ENV{ID_BUS}=="usb", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +KERNELS=="input*", ENV{ID_BUS}=="bluetooth", \ + IMPORT{builtin}="hwdb 'mouse:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="mouse_end" +DRIVERS=="psmouse", SUBSYSTEMS=="serio", \ + IMPORT{builtin}="hwdb 'mouse:ps2::name:$attr{device/name}:'", \ + GOTO="mouse_end" + +LABEL="mouse_end" diff --git a/src/grp-udev/rules/70-touchpad.rules b/src/grp-udev/rules/70-touchpad.rules new file mode 100644 index 0000000000..7bede02dec --- /dev/null +++ b/src/grp-udev/rules/70-touchpad.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="touchpad_end" +ENV{ID_INPUT}=="", GOTO="touchpad_end" +ENV{ID_INPUT_TOUCHPAD}=="", GOTO="touchpad_end" +KERNEL!="event*", GOTO="touchpad_end" + +# touchpad::vp:name::* +KERNELS=="input*", ENV{ID_BUS}!="", \ + IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \ + GOTO="touchpad_end" + +LABEL="touchpad_end" diff --git a/src/grp-udev/rules/75-net-description.rules b/src/grp-udev/rules/75-net-description.rules new file mode 100644 index 0000000000..7e62f8b26b --- /dev/null +++ b/src/grp-udev/rules/75-net-description.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="net_end" +SUBSYSTEM!="net", GOTO="net_end" + +IMPORT{builtin}="net_id" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" +SUBSYSTEMS=="usb", GOTO="net_end" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" + +LABEL="net_end" diff --git a/src/grp-udev/rules/78-sound-card.rules b/src/grp-udev/rules/78-sound-card.rules new file mode 100644 index 0000000000..04740e8b97 --- /dev/null +++ b/src/grp-udev/rules/78-sound-card.rules @@ -0,0 +1,89 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="sound_end" + +ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" +ACTION!="change", GOTO="sound_end" + +# Ok, we probably need a little explanation here for what the two lines above +# are good for. +# +# The story goes like this: when ALSA registers a new sound card it emits a +# series of 'add' events to userspace, for the main card device and for all the +# child device nodes that belong to it. udev relays those to applications, +# however only maintains the order between father and child, but not between +# the siblings. The control device node creation can be used as synchronization +# point. All other devices that belong to a card are created in the kernel +# before it. However unfortunately due to the fact that siblings are forwarded +# out of order by udev this fact is lost to applications. +# +# OTOH before an application can open a device it needs to make sure that all +# its device nodes are completely created and set up. +# +# As a workaround for this issue we have added the udev rule above which will +# generate a 'change' event on the main card device from the 'add' event of the +# card's control device. Due to the ordering semantics of udev this event will +# only be relayed after all child devices have finished processing properly. +# When an application needs to listen for appearing devices it can hence look +# for 'change' events only, and ignore the actual 'add' events. +# +# When the application is initialized at the same time as a device is plugged +# in it may need to figure out if the 'change' event has already been triggered +# or not for a card. To find that out we store the flag environment variable +# SOUND_INITIALIZED on the device which simply tells us if the card 'change' +# event has already been processed. + +KERNEL!="card*", GOTO="sound_end" + +ENV{SOUND_INITIALIZED}="1" + +IMPORT{builtin}="hwdb" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", GOTO="skip_pci" + +SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", \ + ENV{ID_BUS}="firewire", ENV{ID_SERIAL}="$attr{guid}", ENV{ID_SERIAL_SHORT}="$attr{guid}", \ + ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{model}", \ + ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" +SUBSYSTEMS=="firewire", GOTO="skip_pci" + +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +LABEL="skip_pci" + +# Define ID_ID if ID_BUS and ID_SERIAL are set. This will work for both +# USB and firewire. +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}" + +IMPORT{builtin}="path_id" + +# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept +# in sync with those defined for PulseAudio's src/pulse/proplist.h +# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. + +# If the first PCM device of this card has the pcm class 'modem', then the card is a modem +ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" + +# Identify cards on the internal PCI bus as internal +SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" + +# Devices that also support Image/Video interfaces are most likely webcams +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" + +# Matching on the model strings is a bit ugly, I admit +ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" + +LABEL="sound_end" diff --git a/src/grp-udev/rules/80-drivers.rules b/src/grp-udev/rules/80-drivers.rules new file mode 100644 index 0000000000..8551f47a4b --- /dev/null +++ b/src/grp-udev/rules/80-drivers.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="drivers_end" + +ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", RUN{builtin}+="kmod load tifm_ms" +SUBSYSTEM=="memstick", RUN{builtin}+="kmod load ms_block mspro_block" +SUBSYSTEM=="i2o", RUN{builtin}+="kmod load i2o_block" +SUBSYSTEM=="module", KERNEL=="parport_pc", RUN{builtin}+="kmod load ppdev" +KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", RUN{builtin}+="kmod load sm_ftl" + +LABEL="drivers_end" diff --git a/src/grp-udev/rules/80-net-setup-link.rules b/src/grp-udev/rules/80-net-setup-link.rules new file mode 100644 index 0000000000..6e411a91f0 --- /dev/null +++ b/src/grp-udev/rules/80-net-setup-link.rules @@ -0,0 +1,13 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="net", GOTO="net_setup_link_end" + +IMPORT{builtin}="path_id" + +ACTION!="add", GOTO="net_setup_link_end" + +IMPORT{builtin}="net_setup_link" + +NAME=="", ENV{ID_NET_NAME}!="", NAME="$env{ID_NET_NAME}" + +LABEL="net_setup_link_end" diff --git a/src/grp-udev/rules/99-systemd.rules.in b/src/grp-udev/rules/99-systemd.rules.in new file mode 100644 index 0000000000..ca52cf165b --- /dev/null +++ b/src/grp-udev/rules/99-systemd.rules.in @@ -0,0 +1,66 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="systemd_end" + +SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd" +KERNEL=="vport*", TAG+="systemd" + +SUBSYSTEM=="block", TAG+="systemd" +SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" + +# Ignore encrypted devices with no identified superblock on it, since +# we are probably still calling mke2fs or mkswap on it. +SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" + +# Ignore raid devices that are not yet assembled and started +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0" +SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0" + +# Ignore loop devices that don't have any file attached +SUBSYSTEM=="block", KERNEL=="loop[0-9]*", ENV{DEVTYPE}=="disk", TEST!="loop/backing_file", ENV{SYSTEMD_READY}="0" + +# Ignore nbd devices until the PID file exists (which signals a connected device) +SUBSYSTEM=="block", KERNEL=="nbd*", ENV{DEVTYPE}=="disk", TEST!="pid", ENV{SYSTEMD_READY}="0" + +# We need a hardware independent way to identify network devices. We +# use the /sys/subsystem/ path for this. Kernel "bus" and "class" names +# should be treated as one namespace, like udev handles it. This is mostly +# just an identification string for systemd, so whether the path actually is +# accessible or not does not matter as long as it is unique and in the +# filesystem namespace. +# +# http://cgit.freedesktop.org/systemd/systemd/tree/src/libudev/libudev-enumerate.c#n955 + +SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/$name" +SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k" + +SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}+="bluetooth.target" +ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target" +SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target" + +SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target" +SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="printer.target" + +# Apply sysctl variables to network devices (and only to those) as they appear. +ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name" + +# Pull in backlight save/restore for all backlight devices and +# keyboard backlights +SUBSYSTEM=="backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@backlight:$name.service" +SUBSYSTEM=="leds", KERNEL=="*kbd_backlight", TAG+="systemd", IMPORT{builtin}="path_id", ENV{SYSTEMD_WANTS}+="systemd-backlight@leds:$name.service" + +# Pull in rfkill save/restore for all rfkill devices +SUBSYSTEM=="rfkill", ENV{SYSTEMD_RFKILL}="1", IMPORT{builtin}="path_id" +SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="systemd-rfkill.socket" + +# Asynchronously mount file systems implemented by these modules as soon as they are loaded. +SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount" +SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount" + +LABEL="systemd_end" diff --git a/src/grp-udev/scsi_id/.gitignore b/src/grp-udev/scsi_id/.gitignore new file mode 100644 index 0000000000..6aebddd809 --- /dev/null +++ b/src/grp-udev/scsi_id/.gitignore @@ -0,0 +1 @@ +scsi_id_version.h diff --git a/src/grp-udev/scsi_id/Makefile b/src/grp-udev/scsi_id/Makefile new file mode 100644 index 0000000000..7064a864f7 --- /dev/null +++ b/src/grp-udev/scsi_id/Makefile @@ -0,0 +1,41 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +scsi_id_SOURCES =\ + src/udev/scsi_id/scsi_id.c \ + src/udev/scsi_id/scsi_serial.c \ + src/udev/scsi_id/scsi.h \ + src/udev/scsi_id/scsi_id.h + +scsi_id_LDADD = \ + libshared.la + +udevlibexec_PROGRAMS += \ + scsi_id + +EXTRA_DIST += \ + src/udev/scsi_id/README + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/scsi_id/README b/src/grp-udev/scsi_id/README new file mode 100644 index 0000000000..9cfe73991c --- /dev/null +++ b/src/grp-udev/scsi_id/README @@ -0,0 +1,4 @@ +scsi_id - generate a SCSI unique identifier for a given SCSI device + +Please send questions, comments or patches to or +. diff --git a/src/grp-udev/scsi_id/scsi.h b/src/grp-udev/scsi_id/scsi.h new file mode 100644 index 0000000000..a27a84a40a --- /dev/null +++ b/src/grp-udev/scsi_id/scsi.h @@ -0,0 +1,99 @@ +#pragma once + +/* + * scsi.h + * + * General scsi and linux scsi specific defines and structs. + * + * Copyright (C) IBM Corp. 2003 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + */ + +#include + +struct scsi_ioctl_command { + unsigned int inlen; /* excluding scsi command length */ + unsigned int outlen; + unsigned char data[1]; + /* on input, scsi command starts here then opt. data */ +}; + +/* + * Default 5 second timeout + */ +#define DEF_TIMEOUT 5000 + +#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 + +/* + * SCSI INQUIRY vendor and model (really product) lengths. + */ +#define VENDOR_LENGTH 8 +#define MODEL_LENGTH 16 + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +/* + * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the + * SCSI Primary Commands specification for details. + */ + +/* + * 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_TGTGROUP 5 +#define SCSI_ID_LUNGROUP 6 +#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 0x05 +#define SCSI_ID_NAA_IEEE_REG_EXTENDED 0x06 + +/* + * Supported Code Set values. + */ +#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; +}; + +/* + * Following are the "true" SCSI status codes. Linux has traditionally + * used a 1 bit right and masked version of these. So now CHECK_CONDITION + * and friends (in ) are deprecated. + */ +#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 diff --git a/src/grp-udev/scsi_id/scsi_id.c b/src/grp-udev/scsi_id/scsi_id.c new file mode 100644 index 0000000000..83011e918e --- /dev/null +++ b/src/grp-udev/scsi_id/scsi_id.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) IBM Corp. 2003 + * Copyright (C) SUSE Linux Products GmbH, 2006 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "libudev-private.h" +#include "shared/udev-util.h" + +#include "scsi_id.h" + +static const struct option options[] = { + { "device", required_argument, NULL, 'd' }, + { "config", required_argument, NULL, 'f' }, + { "page", required_argument, NULL, 'p' }, + { "blacklisted", no_argument, NULL, 'b' }, + { "whitelisted", no_argument, NULL, 'g' }, + { "replace-whitespace", no_argument, NULL, 'u' }, + { "sg-version", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, /* don't advertise -V */ + { "export", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + {} +}; + +static bool all_good = false; +static bool dev_specified = false; +static char config_file[MAX_PATH_LEN] = "/etc/scsi_id.config"; +static enum page_code default_page_code = PAGE_UNSPECIFIED; +static int sg_version = 4; +static bool reformat_serial = false; +static bool export = false; +static char vendor_str[64]; +static char model_str[64]; +static char vendor_enc_str[256]; +static char model_enc_str[256]; +static char revision_str[16]; +static char type_str[16]; + +static void set_type(const char *from, char *to, size_t len) +{ + int type_num; + char *eptr; + const char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 0: + type = "disk"; + break; + case 1: + type = "tape"; + break; + case 4: + type = "optical"; + break; + case 5: + type = "cd"; + break; + case 7: + type = "optical"; + break; + case 0xe: + type = "disk"; + break; + case 0xf: + type = "optical"; + break; + default: + break; + } + } + strscpy(to, len, type); +} + +/* + * get_value: + * + * buf points to an '=' followed by a quoted string ("foo") or a string ending + * with a space or ','. + * + * Return a pointer to the NUL terminated string, returns NULL if no + * matches. + */ +static char *get_value(char **buffer) +{ + static const char *quote_string = "\"\n"; + static const char *comma_string = ",\n"; + char *val; + const char *end; + + if (**buffer == '"') { + /* + * skip leading quote, terminate when quote seen + */ + (*buffer)++; + end = quote_string; + } else { + end = comma_string; + } + val = strsep(buffer, end); + if (val && end == quote_string) + /* + * skip trailing quote + */ + (*buffer)++; + + while (isspace(**buffer)) + (*buffer)++; + + return val; +} + +static int argc_count(char *opts) +{ + int i = 0; + while (*opts != '\0') + if (*opts++ == ' ') + i++; + return i; +} + +/* + * get_file_options: + * + * If vendor == NULL, find a line in the config file with only "OPTIONS="; + * if vendor and model are set find the first OPTIONS line in the config + * file that matches. Set argc and argv to match the OPTIONS string. + * + * vendor and model can end in '\n'. + */ +static int get_file_options(struct udev *udev, + const char *vendor, const char *model, + int *argc, char ***newargv) +{ + char *buffer; + _cleanup_fclose_ FILE *f; + char *buf; + char *str1; + char *vendor_in, *model_in, *options_in; /* read in from file */ + int lineno; + int c; + int retval = 0; + + f = fopen(config_file, "re"); + if (f == NULL) { + if (errno == ENOENT) + return 1; + else { + log_error_errno(errno, "can't open %s: %m", config_file); + return -1; + } + } + + /* + * Allocate a buffer rather than put it on the stack so we can + * keep it around to parse any options (any allocated newargv + * points into this buffer for its strings). + */ + buffer = malloc(MAX_BUFFER_LEN); + if (!buffer) + return log_oom(); + + *newargv = NULL; + lineno = 0; + for (;;) { + vendor_in = model_in = options_in = NULL; + + buf = fgets(buffer, MAX_BUFFER_LEN, f); + if (buf == NULL) + break; + lineno++; + if (buf[strlen(buffer) - 1] != '\n') { + log_error("Config file line %d too long", lineno); + break; + } + + while (isspace(*buf)) + buf++; + + /* blank or all whitespace line */ + if (*buf == '\0') + continue; + + /* comment line */ + if (*buf == '#') + continue; + + str1 = strsep(&buf, "="); + if (str1 && strcaseeq(str1, "VENDOR")) { + str1 = get_value(&buf); + if (!str1) { + retval = log_oom(); + break; + } + vendor_in = str1; + + str1 = strsep(&buf, "="); + if (str1 && strcaseeq(str1, "MODEL")) { + str1 = get_value(&buf); + if (!str1) { + retval = log_oom(); + break; + } + model_in = str1; + str1 = strsep(&buf, "="); + } + } + + if (str1 && strcaseeq(str1, "OPTIONS")) { + str1 = get_value(&buf); + if (!str1) { + retval = log_oom(); + break; + } + options_in = str1; + } + + /* + * Only allow: [vendor=foo[,model=bar]]options=stuff + */ + if (!options_in || (!vendor_in && model_in)) { + log_error("Error parsing config file line %d '%s'", lineno, buffer); + retval = -1; + break; + } + if (vendor == NULL) { + if (vendor_in == NULL) + break; + } else if (vendor_in && + strneq(vendor, vendor_in, strlen(vendor_in)) && + (!model_in || + (strneq(model, model_in, strlen(model_in))))) { + /* + * Matched vendor and optionally model. + * + * Note: a short vendor_in or model_in can + * give a partial match (that is FOO + * matches FOOBAR). + */ + break; + } + } + + if (retval == 0) { + if (vendor_in != NULL || model_in != NULL || + options_in != NULL) { + /* + * Something matched. Allocate newargv, and store + * values found in options_in. + */ + strcpy(buffer, options_in); + c = argc_count(buffer) + 2; + *newargv = calloc(c, sizeof(**newargv)); + if (!*newargv) + retval = log_oom(); + else { + *argc = c; + c = 0; + /* + * argv[0] at 0 is skipped by getopt, but + * store the buffer address there for + * later freeing + */ + (*newargv)[c] = buffer; + for (c = 1; c < *argc; c++) + (*newargv)[c] = strsep(&buffer, " \t"); + } + } else { + /* No matches */ + retval = 1; + } + } + if (retval != 0) + free(buffer); + return retval; +} + +static void help(void) { + printf("Usage: %s [OPTION...] DEVICE\n\n" + "SCSI device identification.\n\n" + " -h --help Print this message\n" + " --version Print version of the program\n\n" + " -d --device= Device node for SG_IO commands\n" + " -f --config= Location of config file\n" + " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" + " -s --sg-version=3|4 Use SGv3 or SGv4\n" + " -b --blacklisted Treat device as blacklisted\n" + " -g --whitelisted Treat device as whitelisted\n" + " -u --replace-whitespace Replace all whitespace by underscores\n" + " -v --verbose Verbose logging\n" + " -x --export Print values as environment keys\n" + , program_invocation_short_name); + +} + +static int set_options(struct udev *udev, + int argc, char **argv, + char *maj_min_dev) +{ + int option; + + /* + * optind is a global extern used by getopt. Since we can call + * set_options twice (once for command line, and once for config + * file) we have to reset this back to 1. + */ + optind = 1; + while ((option = getopt_long(argc, argv, "d:f:gp:uvVxh", options, NULL)) >= 0) + switch (option) { + case 'b': + all_good = false; + break; + + case 'd': + dev_specified = true; + strscpy(maj_min_dev, MAX_PATH_LEN, optarg); + break; + + case 'f': + strscpy(config_file, MAX_PATH_LEN, optarg); + break; + + case 'g': + all_good = true; + break; + + case 'h': + help(); + exit(0); + + case 'p': + if (streq(optarg, "0x80")) + default_page_code = PAGE_80; + else if (streq(optarg, "0x83")) + default_page_code = PAGE_83; + else if (streq(optarg, "pre-spc3-83")) + default_page_code = PAGE_83_PRE_SPC3; + else { + log_error("Unknown page code '%s'", optarg); + return -1; + } + break; + + case 's': + sg_version = atoi(optarg); + if (sg_version < 3 || sg_version > 4) { + log_error("Unknown SG version '%s'", optarg); + return -1; + } + break; + + case 'u': + reformat_serial = true; + break; + + case 'v': + log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_DEBUG); + log_open(); + break; + + case 'V': + printf("%s\n", VERSION); + exit(0); + + case 'x': + export = true; + break; + + case '?': + return -1; + + default: + assert_not_reached("Unknown option"); + } + + if (optind < argc && !dev_specified) { + dev_specified = true; + strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]); + } + + return 0; +} + +static int per_dev_options(struct udev *udev, + struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) +{ + int retval; + int newargc; + char **newargv = NULL; + int option; + + *good_bad = all_good; + *page_code = default_page_code; + + retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); + + optind = 1; /* reset this global extern */ + while (retval == 0) { + option = getopt_long(newargc, newargv, "bgp:", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'b': + *good_bad = 0; + break; + + case 'g': + *good_bad = 1; + break; + + case 'p': + if (streq(optarg, "0x80")) { + *page_code = PAGE_80; + } else if (streq(optarg, "0x83")) { + *page_code = PAGE_83; + } else if (streq(optarg, "pre-spc3-83")) { + *page_code = PAGE_83_PRE_SPC3; + } else { + log_error("Unknown page code '%s'", optarg); + retval = -1; + } + break; + + default: + log_error("Unknown or bad option '%c' (0x%x)", option, option); + retval = -1; + break; + } + } + + if (newargv) { + free(newargv[0]); + free(newargv); + } + return retval; +} + +static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) +{ + int retval; + + dev_scsi->use_sg = sg_version; + + retval = scsi_std_inquiry(udev, dev_scsi, path); + if (retval) + return retval; + + udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); + udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); + + util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); + util_replace_chars(vendor_str, NULL); + util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); + util_replace_chars(model_str, NULL); + set_type(dev_scsi->type, type_str, sizeof(type_str)); + util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); + util_replace_chars(revision_str, NULL); + return 0; +} + +/* + * scsi_id: try to get an id, if one is found, printf it to stdout. + * returns a value passed to exit() - 0 if printed an id, else 1. + */ +static int scsi_id(struct udev *udev, char *maj_min_dev) +{ + struct scsi_id_device dev_scsi = {}; + int good_dev; + int page_code; + int retval = 0; + + if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { + retval = 1; + goto out; + } + + /* get per device (vendor + model) options from the config file */ + per_dev_options(udev, &dev_scsi, &good_dev, &page_code); + if (!good_dev) { + retval = 1; + goto out; + } + + /* read serial number from mode pages (no values for optical drives) */ + scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); + + if (export) { + char serial_str[MAX_SERIAL_LEN]; + + printf("ID_SCSI=1\n"); + printf("ID_VENDOR=%s\n", vendor_str); + printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); + printf("ID_MODEL=%s\n", model_str); + printf("ID_MODEL_ENC=%s\n", model_enc_str); + printf("ID_REVISION=%s\n", revision_str); + printf("ID_TYPE=%s\n", type_str); + if (dev_scsi.serial[0] != '\0') { + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("ID_SERIAL=%s\n", serial_str); + util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("ID_SERIAL_SHORT=%s\n", serial_str); + } + if (dev_scsi.wwn[0] != '\0') { + printf("ID_WWN=0x%s\n", dev_scsi.wwn); + if (dev_scsi.wwn_vendor_extension[0] != '\0') { + printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); + printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); + } else + printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); + } + if (dev_scsi.tgpt_group[0] != '\0') + printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); + if (dev_scsi.unit_serial_number[0] != '\0') + printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); + goto out; + } + + if (dev_scsi.serial[0] == '\0') { + retval = 1; + goto out; + } + + if (reformat_serial) { + char serial_str[MAX_SERIAL_LEN]; + + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("%s\n", serial_str); + goto out; + } + + printf("%s\n", dev_scsi.serial); +out: + return retval; +} + +int main(int argc, char **argv) +{ + _cleanup_udev_unref_ struct udev *udev; + int retval = 0; + char maj_min_dev[MAX_PATH_LEN]; + int newargc; + char **newargv = NULL; + + log_parse_environment(); + log_open(); + + udev = udev_new(); + if (udev == NULL) + goto exit; + + /* + * Get config file options. + */ + retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); + if (retval < 0) { + retval = 1; + goto exit; + } + if (retval == 0) { + assert(newargv); + + if (set_options(udev, newargc, newargv, maj_min_dev) < 0) { + retval = 2; + goto exit; + } + } + + /* + * Get command line options (overriding any config file settings). + */ + if (set_options(udev, argc, argv, maj_min_dev) < 0) + exit(1); + + if (!dev_specified) { + log_error("No device specified."); + retval = 1; + goto exit; + } + + retval = scsi_id(udev, maj_min_dev); + +exit: + if (newargv) { + free(newargv[0]); + free(newargv); + } + log_close(); + return retval; +} diff --git a/src/grp-udev/scsi_id/scsi_id.h b/src/grp-udev/scsi_id/scsi_id.h new file mode 100644 index 0000000000..5c2e1c28ee --- /dev/null +++ b/src/grp-udev/scsi_id/scsi_id.h @@ -0,0 +1,75 @@ +#pragma once + +/* + * Copyright (C) IBM Corp. 2003 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define MAX_PATH_LEN 512 + +/* + * MAX_ATTR_LEN: maximum length of the result of reading a sysfs + * attribute. + */ +#define MAX_ATTR_LEN 256 + +/* + * MAX_SERIAL_LEN: the maximum length of the serial number, including + * added prefixes such as vendor and product (model) strings. + */ +#define MAX_SERIAL_LEN 256 + +/* + * MAX_BUFFER_LEN: maximum buffer size and line length used while reading + * the config file. + */ +#define MAX_BUFFER_LEN 256 + +struct scsi_id_device { + char vendor[9]; + char model[17]; + char revision[5]; + char type[33]; + char kernel[64]; + char serial[MAX_SERIAL_LEN]; + char serial_short[MAX_SERIAL_LEN]; + int use_sg; + + /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */ + char unit_serial_number[MAX_SERIAL_LEN]; + + /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */ + char wwn[17]; + + /* NULs if not set - otherwise hex encoding using lower-case e.g. '0xe00000d80000' */ + char wwn_vendor_extension[17]; + + /* NULs if not set - otherwise decimal number */ + char tgpt_group[8]; +}; + +int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname); +int scsi_get_serial(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname, + int page_code, int len); + +/* + * Page code values. + */ +enum page_code { + PAGE_83_PRE_SPC3 = -0x83, + PAGE_UNSPECIFIED = 0x00, + PAGE_80 = 0x80, + PAGE_83 = 0x83, +}; diff --git a/src/grp-udev/scsi_id/scsi_serial.c b/src/grp-udev/scsi_id/scsi_serial.c new file mode 100644 index 0000000000..544fcde108 --- /dev/null +++ b/src/grp-udev/scsi_id/scsi_serial.c @@ -0,0 +1,966 @@ +/* + * Copyright (C) IBM Corp. 2003 + * + * Author: Patrick Mansfield + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "libudev-private.h" + +#include "scsi.h" +#include "scsi_id.h" + +/* + * A priority based list of id, naa, and binary/ascii for the identifier + * descriptor in VPD page 0x83. + * + * Brute force search for a match starting with the first value in the + * following id_search_list. This is not a performance issue, since there + * 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 }, + /* + * Devices already exist using NAA values that are now marked + * reserved. These should not conflict with other values, or it is + * a bug in the device. As long as we find the IEEE extended one + * first, we really don't care what other ones are used. Using + * don't care here means that a device that returns multiple + * 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 }, +}; + +static const char hex_str[]="0123456789abcdef"; + +/* + * Values returned in the result/status, only the ones used by the code + * 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 */ + +/* 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 */ + +static int do_scsi_page80_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int max_len); + +static int sg_err_category_new(struct udev *udev, + int scsi_status, int msg_status, int + host_status, int driver_status, const + unsigned char *sense_buffer, int sb_len) +{ + scsi_status &= 0x7e; + + /* + * XXX change to return only two values - failed or OK. + */ + + if (!scsi_status && !host_status && !driver_status) + return SG_ERR_CAT_CLEAN; + + if ((scsi_status == SCSI_CHECK_CONDITION) || + (scsi_status == SCSI_COMMAND_TERMINATED) || + ((driver_status & 0xf) == DRIVER_SENSE)) { + if (sense_buffer && (sb_len > 2)) { + int sense_key; + unsigned char asc; + + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + } else { + sense_key = sense_buffer[2] & 0xf; + asc = (sb_len > 12) ? sense_buffer[12] : 0; + } + + if (sense_key == RECOVERED_ERROR) + return SG_ERR_CAT_RECOVERED; + else if (sense_key == UNIT_ATTENTION) { + if (0x28 == asc) + return SG_ERR_CAT_MEDIA_CHANGED; + if (0x29 == asc) + return SG_ERR_CAT_RESET; + } else if (sense_key == ILLEGAL_REQUEST) + return SG_ERR_CAT_NOTSUPPORTED; + } + return SG_ERR_CAT_SENSE; + } + if (host_status) { + if ((host_status == DID_NO_CONNECT) || + (host_status == DID_BUS_BUSY) || + (host_status == DID_TIME_OUT)) + return SG_ERR_CAT_TIMEOUT; + } + if (driver_status) { + if (driver_status == DRIVER_TIMEOUT) + return SG_ERR_CAT_TIMEOUT; + } + return SG_ERR_CAT_OTHER; +} + +static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp) +{ + return sg_err_category_new(udev, + hp->status, hp->msg_status, + hp->host_status, hp->driver_status, + hp->sbp, hp->sb_len_wr); +} + +static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp) +{ + return sg_err_category_new(udev, hp->device_status, 0, + hp->transport_status, hp->driver_status, + (unsigned char *)(uintptr_t)hp->response, + hp->response_len); +} + +static int scsi_dump_sense(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *sense_buffer, int sb_len) +{ + int s; + int code; + int sense_class; + int sense_key; + int asc, ascq; +#ifdef DUMP_SENSE + char out_buffer[256]; + int i, j; +#endif + + /* + * Figure out and print the sense key, asc and ascq. + * + * If you want to suppress these for a particular drive model, add + * a black list entry in the scsi_id config file. + * + * XXX We probably need to: lookup the sense/asc/ascq in a retry + * table, and if found return 1 (after dumping the sense, asc, and + * ascq). So, if/when we get something like a power on/reset, + * we'll retry the command. + */ + + if (sb_len < 1) { + log_debug("%s: sense buffer empty", dev_scsi->kernel); + return -1; + } + + sense_class = (sense_buffer[0] >> 4) & 0x07; + code = sense_buffer[0] & 0xf; + + if (sense_class == 7) { + /* + * extended sense data. + */ + 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); + return -1; + } + if ((code == 0x0) || (code == 0x1)) { + sense_key = sense_buffer[2] & 0xf; + if (s < 14) { + /* + * Possible? + */ + log_debug("%s: sense result too" " small %d bytes", + dev_scsi->kernel, s); + return -1; + } + asc = sense_buffer[12]; + ascq = sense_buffer[13]; + } else if ((code == 0x2) || (code == 0x3)) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + ascq = sense_buffer[3]; + } else { + log_debug("%s: invalid sense code 0x%x", + 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); + } 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); + return -1; + } + + if (sense_buffer[0] < 15) + 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]); + log_debug("%s: non-extended sense class %d code 0x%0x", + dev_scsi->kernel, sense_class, code); + + } + +#ifdef DUMP_SENSE + for (i = 0, j = 0; (i < s) && (j < 254); i++) { + out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4]; + out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f]; + out_buffer[j++] = ' '; + } + out_buffer[j] = '\0'; + log_debug("%s: sense dump:", dev_scsi->kernel); + log_debug("%s: %s", dev_scsi->kernel, out_buffer); + +#endif + return -1; +} + +static int scsi_dump(struct udev *udev, + struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) +{ + if (!io->status && !io->host_status && !io->msg_status && + !io->driver_status) { + /* + * Impossible, should not be called. + */ + log_debug("%s: called with no error", __FUNCTION__); + return -1; + } + + 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); + if (io->status == SCSI_CHECK_CONDITION) + return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); + else + return -1; +} + +static int scsi_dump_v4(struct udev *udev, + struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) +{ + if (!io->device_status && !io->transport_status && + !io->driver_status) { + /* + * Impossible, should not be called. + */ + log_debug("%s: called with no error", __FUNCTION__); + return -1; + } + + 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); + if (io->device_status == SCSI_CHECK_CONDITION) + return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, + io->response_len); + else + return -1; +} + +static int scsi_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + unsigned char evpd, unsigned char page, + unsigned char *buf, unsigned int buflen) +{ + unsigned char inq_cmd[INQUIRY_CMDLEN] = + { INQUIRY_CMD, evpd, page, 0, buflen, 0 }; + unsigned char sense[SENSE_BUFF_LEN]; + void *io_buf; + struct sg_io_v4 io_v4; + struct sg_io_hdr io_hdr; + int retry = 3; /* rather random */ + int retval; + + if (buflen > SCSI_INQ_BUFF_LEN) { + log_debug("buflen %d too long", buflen); + return -1; + } + +resend: + if (dev_scsi->use_sg == 4) { + memzero(&io_v4, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof(inq_cmd); + io_v4.request = (uintptr_t)inq_cmd; + io_v4.max_response_len = sizeof(sense); + io_v4.response = (uintptr_t)sense; + io_v4.din_xfer_len = buflen; + io_v4.din_xferp = (uintptr_t)buf; + io_buf = (void *)&io_v4; + } else { + memzero(&io_hdr, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(inq_cmd); + io_hdr.mx_sb_len = sizeof(sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = buflen; + io_hdr.dxferp = buf; + io_hdr.cmdp = inq_cmd; + io_hdr.sbp = sense; + io_hdr.timeout = DEF_TIMEOUT; + io_buf = (void *)&io_hdr; + } + + retval = ioctl(fd, SG_IO, io_buf); + if (retval < 0) { + if ((errno == EINVAL || errno == ENOSYS) && dev_scsi->use_sg == 4) { + dev_scsi->use_sg = 3; + goto resend; + } + log_debug_errno(errno, "%s: ioctl failed: %m", dev_scsi->kernel); + goto error; + } + + if (dev_scsi->use_sg == 4) + retval = sg_err_category4(udev, io_buf); + else + retval = sg_err_category3(udev, io_buf); + + switch (retval) { + case SG_ERR_CAT_NOTSUPPORTED: + buf[1] = 0; + /* Fallthrough */ + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + retval = 0; + break; + + default: + if (dev_scsi->use_sg == 4) + retval = scsi_dump_v4(udev, dev_scsi, io_buf); + else + retval = scsi_dump(udev, dev_scsi, io_buf); + } + + if (!retval) { + retval = buflen; + } else if (retval > 0) { + if (--retry > 0) + goto resend; + retval = -1; + } + +error: + if (retval < 0) + log_debug("%s: Unable to get INQUIRY vpd %d page 0x%x.", + dev_scsi->kernel, evpd, page); + + return retval; +} + +/* Get list of supported EVPD pages */ +static int do_scsi_page0_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + unsigned char *buffer, unsigned int len) +{ + int retval; + + memzero(buffer, len); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len); + if (retval < 0) + return 1; + + if (buffer[1] != 0) { + log_debug("%s: page 0 not available.", dev_scsi->kernel); + return 1; + } + if (buffer[3] > len) { + log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]); + return 1; + } + + /* + * Following check is based on code once included in the 2.5.x + * kernel. + * + * Some ill behaved devices return the standard inquiry here + * rather than the evpd data, snoop the data to verify. + */ + if (buffer[3] > MODEL_LENGTH) { + /* + * If the vendor id appears in the page assume the page is + * invalid. + */ + if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { + log_debug("%s: invalid page0 data", dev_scsi->kernel); + return 1; + } + } + return 0; +} + +/* + * The caller checks that serial is long enough to include the vendor + + * model. + */ +static int prepend_vendor_model(struct udev *udev, + struct scsi_id_device *dev_scsi, char *serial) +{ + int ind; + + strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); + strncat(serial, dev_scsi->model, MODEL_LENGTH); + ind = strlen(serial); + + /* + * This is not a complete check, since we are using strncat/cpy + * above, ind will never be too large. + */ + if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { + log_debug("%s: expected length %d, got length %d", + dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); + return -1; + } + return ind; +} + +/* + * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill + * serial number. + */ +static int check_fill_0x83_id(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *page_83, + const struct scsi_id_search_values + *id_search, char *serial, char *serial_short, + int max_len, char *wwn, + char *wwn_vendor_extension, char *tgpt_group) +{ + int i, j, s, len; + + /* + * ASSOCIATION must be with the device (value 0) + * or with the target port for SCSI_ID_TGTPORT + */ + if ((page_83[1] & 0x30) == 0x10) { + if (id_search->id_type != SCSI_ID_TGTGROUP) + return 1; + } else if ((page_83[1] & 0x30) != 0) + return 1; + + if ((page_83[1] & 0x0f) != id_search->id_type) + return 1; + + /* + * Possibly check NAA sub-type. + */ + if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && + (id_search->naa_type != (page_83[4] & 0xf0) >> 4)) + return 1; + + /* + * Check for matching code set - ASCII or BINARY. + */ + if ((page_83[0] & 0x0f) != id_search->code_set) + return 1; + + /* + * page_83[3]: identifier length + */ + len = page_83[3]; + if ((page_83[0] & 0x0f) != SCSI_ID_ASCII) + /* + * If not ASCII, use two bytes for each binary value. + */ + len *= 2; + + /* + * Add one byte for the NUL termination, and one for the id_type. + */ + len += 2; + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) + len += VENDOR_LENGTH + MODEL_LENGTH; + + if (max_len < len) { + log_debug("%s: length %d too short - need %d", + dev_scsi->kernel, max_len, len); + return 1; + } + + if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) { + unsigned int group; + + group = ((unsigned int)page_83[6] << 8) | page_83[7]; + sprintf(tgpt_group,"%x", group); + return 1; + } + + serial[0] = hex_str[id_search->id_type]; + + /* + * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before + * the id since it is not unique across all vendors and models, + * this differs from SCSI_ID_T10_VENDOR, where the vendor is + * included in the identifier. + */ + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) + if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0) + return 1; + + i = 4; /* offset to the start of the identifier */ + s = j = strlen(serial); + if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) { + /* + * ASCII descriptor. + */ + while (i < (4 + page_83[3])) + serial[j++] = page_83[i++]; + } else { + /* + * Binary descriptor, convert to ASCII, using two bytes of + * ASCII for each byte in the page_83. + */ + while (i < (4 + page_83[3])) { + serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; + serial[j++] = hex_str[page_83[i] & 0x0f]; + i++; + } + } + + strcpy(serial_short, &serial[s]); + + if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { + strncpy(wwn, &serial[s], 16); + if (wwn_vendor_extension != NULL) + strncpy(wwn_vendor_extension, &serial[s + 16], 16); + } + + return 0; +} + +/* Extract the raw binary from VPD 0x83 pre-SPC devices */ +static int check_fill_0x83_prespc3(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *page_83, + const struct scsi_id_search_values + *id_search, char *serial, char *serial_short, int max_len) +{ + int i, j; + + serial[0] = hex_str[id_search->id_type]; + /* serial has been memset to zero before */ + j = strlen(serial); /* j = 1; */ + + for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { + serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4]; + serial[j++] = hex_str[ page_83[4+i] & 0x0f]; + } + serial[max_len-1] = 0; + strncpy(serial_short, serial, max_len-1); + return 0; +} + + +/* Get device identification VPD page */ +static int do_scsi_page83_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int len, + char *unit_serial_number, char *wwn, + char *wwn_vendor_extension, char *tgpt_group) +{ + int retval; + unsigned int id_ind, j; + unsigned char page_83[SCSI_INQ_BUFF_LEN]; + + /* also pick up the page 80 serial number */ + do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); + + memzero(page_83, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, + SCSI_INQ_BUFF_LEN); + if (retval < 0) + return 1; + + if (page_83[1] != PAGE_83) { + log_debug("%s: Invalid page 0x83", dev_scsi->kernel); + return 1; + } + + /* + * XXX Some devices (IBM 3542) return all spaces for an identifier if + * the LUN is not actually configured. This leads to identifiers of + * the form: "1 ". + */ + + /* + * Model 4, 5, and (some) model 6 EMC Symmetrix devices return + * a page 83 reply according to SCSI-2 format instead of SPC-2/3. + * + * The SCSI-2 page 83 format returns an IEEE WWN in binary + * encoded hexi-decimal in the 16 bytes following the initial + * 4-byte page 83 reply header. + * + * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part + * of an Identification descriptor. The 3rd byte of the first + * Identification descriptor is a reserved (BSZ) byte field. + * + * Reference the 7th byte of the page 83 reply to determine + * whether the reply is compliant with SCSI-2 or SPC-2/3 + * specifications. A zero value in the 7th byte indicates + * an SPC-2/3 conformant reply, (i.e., the reserved field of the + * first Identification descriptor). This byte will be non-zero + * for a SCSI-2 conformant page 83 reply from these EMC + * Symmetrix models since the 7th byte of the reply corresponds + * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, + * 0x006048. + */ + + if (page_83[6] != 0) + return check_fill_0x83_prespc3(udev, + dev_scsi, page_83, id_search_list, + serial, serial_short, len); + + /* + * Search for a match in the prioritized id_search_list - since WWN ids + * come first we can pick up the WWN in check_fill_0x83_id(). + */ + for (id_ind = 0; + id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]); + id_ind++) { + /* + * Examine each descriptor returned. There is normally only + * one or a small number of descriptors. + */ + for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { + retval = check_fill_0x83_id(udev, + dev_scsi, &page_83[j], + &id_search_list[id_ind], + serial, serial_short, len, + wwn, wwn_vendor_extension, + tgpt_group); + if (!retval) + return retval; + else if (retval < 0) + return retval; + } + } + return 1; +} + +/* + * Get device identification VPD page for older SCSI-2 device which is not + * compliant with either SPC-2 or SPC-3 format. + * + * Return the hard coded error code value 2 if the page 83 reply is not + * conformant to the SCSI-2 format. + */ +static int do_scsi_page83_prespc3_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int len) +{ + int retval; + int i, j; + unsigned char page_83[SCSI_INQ_BUFF_LEN]; + + memzero(page_83, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); + if (retval < 0) + return 1; + + if (page_83[1] != PAGE_83) { + log_debug("%s: Invalid page 0x83", dev_scsi->kernel); + return 1; + } + /* + * Model 4, 5, and (some) model 6 EMC Symmetrix devices return + * a page 83 reply according to SCSI-2 format instead of SPC-2/3. + * + * The SCSI-2 page 83 format returns an IEEE WWN in binary + * encoded hexi-decimal in the 16 bytes following the initial + * 4-byte page 83 reply header. + * + * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part + * of an Identification descriptor. The 3rd byte of the first + * Identification descriptor is a reserved (BSZ) byte field. + * + * Reference the 7th byte of the page 83 reply to determine + * whether the reply is compliant with SCSI-2 or SPC-2/3 + * specifications. A zero value in the 7th byte indicates + * an SPC-2/3 conformant reply, (i.e., the reserved field of the + * first Identification descriptor). This byte will be non-zero + * for a SCSI-2 conformant page 83 reply from these EMC + * Symmetrix models since the 7th byte of the reply corresponds + * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, + * 0x006048. + */ + if (page_83[6] == 0) + return 2; + + serial[0] = hex_str[id_search_list[0].id_type]; + /* + * The first four bytes contain data, not a descriptor. + */ + i = 4; + j = strlen(serial); + /* + * Binary descriptor, convert to ASCII, + * using two bytes of ASCII for each byte + * in the page_83. + */ + while (i < (page_83[3]+4)) { + serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; + serial[j++] = hex_str[page_83[i] & 0x0f]; + i++; + } + return 0; +} + +/* Get unit serial number VPD page */ +static int do_scsi_page80_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int max_len) +{ + int retval; + int ser_ind; + int i; + int len; + unsigned char buf[SCSI_INQ_BUFF_LEN]; + + memzero(buf, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); + if (retval < 0) + return retval; + + if (buf[1] != PAGE_80) { + log_debug("%s: Invalid page 0x80", dev_scsi->kernel); + return 1; + } + + 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); + return 1; + } + /* + * Prepend 'S' to avoid unlikely collision with page 0x83 vendor + * specific type where we prepend '0' + vendor + model. + */ + len = buf[3]; + if (serial != NULL) { + serial[0] = 'S'; + ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]); + if (ser_ind < 0) + return 1; + ser_ind++; /* for the leading 'S' */ + for (i = 4; i < len + 4; i++, ser_ind++) + serial[ser_ind] = buf[i]; + } + if (serial_short != NULL) { + memcpy(serial_short, &buf[4], len); + serial_short[len] = '\0'; + } + return 0; +} + +int scsi_std_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, const char *devname) +{ + int fd; + unsigned char buf[SCSI_INQ_BUFF_LEN]; + struct stat statbuf; + int err = 0; + + fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fd < 0) { + log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname); + return 1; + } + + if (fstat(fd, &statbuf) < 0) { + log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname); + err = 2; + goto out; + } + sprintf(dev_scsi->kernel,"%d:%d", major(statbuf.st_rdev), + minor(statbuf.st_rdev)); + + memzero(buf, SCSI_INQ_BUFF_LEN); + err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN); + if (err < 0) + goto out; + + err = 0; + memcpy(dev_scsi->vendor, buf + 8, 8); + dev_scsi->vendor[8] = '\0'; + memcpy(dev_scsi->model, buf + 16, 16); + dev_scsi->model[16] = '\0'; + memcpy(dev_scsi->revision, buf + 32, 4); + dev_scsi->revision[4] = '\0'; + sprintf(dev_scsi->type,"%x", buf[0] & 0x1f); + +out: + close(fd); + return err; +} + +int scsi_get_serial(struct udev *udev, + struct scsi_id_device *dev_scsi, const char *devname, + int page_code, int len) +{ + unsigned char page0[SCSI_INQ_BUFF_LEN]; + int fd = -1; + int cnt; + int ind; + int retval; + + memzero(dev_scsi->serial, len); + initialize_srand(); + for (cnt = 20; cnt > 0; cnt--) { + struct timespec duration; + + fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fd >= 0 || errno != EBUSY) + break; + duration.tv_sec = 0; + duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); + nanosleep(&duration, NULL); + } + if (fd < 0) + return 1; + + if (page_code == PAGE_80) { + if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } else if (page_code == PAGE_83) { + if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } else if (page_code == PAGE_83_PRE_SPC3) { + retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len); + if (retval) { + /* + * Fallback to servicing a SPC-2/3 compliant page 83 + * inquiry if the page 83 reply format does not + * conform to pre-SPC3 expectations. + */ + if (retval == 2) { + if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } + else { + retval = 1; + goto completed; + } + } else { + retval = 0; + goto completed; + } + } else if (page_code != 0x00) { + log_debug("%s: unsupported page code 0x%d", dev_scsi->kernel, page_code); + retval = 1; + goto completed; + } + + /* + * Get page 0, the page of the pages. By default, try from best to + * worst of supported pages: 0x83 then 0x80. + */ + if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) { + /* + * Don't try anything else. Black list if a specific page + * should be used for this vendor+model, or maybe have an + * optional fall-back to page 0x80 or page 0x83. + */ + retval = 1; + goto completed; + } + + for (ind = 4; ind <= page0[3] + 3; ind++) + if (page0[ind] == PAGE_83) + if (!do_scsi_page83_inquiry(udev, dev_scsi, fd, + dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + /* + * Success + */ + retval = 0; + goto completed; + } + + for (ind = 4; ind <= page0[3] + 3; ind++) + if (page0[ind] == PAGE_80) + if (!do_scsi_page80_inquiry(udev, dev_scsi, fd, + dev_scsi->serial, dev_scsi->serial_short, len)) { + /* + * Success + */ + retval = 0; + goto completed; + } + retval = 1; + +completed: + close(fd); + return retval; +} diff --git a/src/grp-udev/systemd-hwdb/Makefile b/src/grp-udev/systemd-hwdb/Makefile new file mode 100644 index 0000000000..2ca5211490 --- /dev/null +++ b/src/grp-udev/systemd-hwdb/Makefile @@ -0,0 +1,77 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_HWDB),) +INSTALL_DIRS += \ + $(sysconfdir)/udev/hwdb.d + +systemd_hwdb_SOURCES = \ + src/libsystemd/sd-hwdb/hwdb-internal.h \ + src/hwdb/hwdb.c + +systemd_hwdb_LDADD = \ + libshared.la + +rootbin_PROGRAMS += \ + systemd-hwdb + +dist_udevhwdb_DATA = \ + hwdb/20-pci-vendor-model.hwdb \ + hwdb/20-pci-classes.hwdb \ + hwdb/20-usb-vendor-model.hwdb \ + hwdb/20-usb-classes.hwdb \ + hwdb/20-sdio-vendor-model.hwdb \ + hwdb/20-sdio-classes.hwdb \ + hwdb/20-bluetooth-vendor-product.hwdb \ + hwdb/20-acpi-vendor.hwdb \ + hwdb/20-OUI.hwdb \ + hwdb/20-net-ifname.hwdb \ + hwdb/60-evdev.hwdb \ + hwdb/60-keyboard.hwdb \ + hwdb/70-mouse.hwdb \ + hwdb/70-pointingstick.hwdb \ + hwdb/70-touchpad.hwdb + +SYSINIT_TARGET_WANTS += \ + systemd-hwdb-update.service + +# Update hwdb on installation. Do not bother if installing +# in DESTDIR, since this is likely for packaging purposes. +hwdb-update-hook: + -test -n "$(DESTDIR)" || $(rootbindir)/systemd-hwdb update + +INSTALL_DATA_HOOKS += \ + hwdb-update-hook + +hwdb-remove-hook: + -test -n "$(DESTDIR)" || rm -f /etc/udev/hwdb.bin +endif # ENABLE_HWDB + +EXTRA_DIST += \ + units/systemd-hwdb-update.service.in \ + hwdb/ids-update.pl \ + hwdb/sdio.ids + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/systemd-hwdb/hwdb.c b/src/grp-udev/systemd-hwdb/hwdb.c new file mode 100644 index 0000000000..2e843249ae --- /dev/null +++ b/src/grp-udev/systemd-hwdb/hwdb.c @@ -0,0 +1,743 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/selinux-util.h" +#include "basic/strbuf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "basic/verbs.h" +#include "sd-hwdb/hwdb-internal.h" +#include "sd-hwdb/hwdb-util.h" + +/* + * Generic udev properties, key/value database based on modalias strings. + * Uses a Patricia/radix trie to index all matches for efficient lookup. + */ + +static const char *arg_hwdb_bin_dir = "/etc/udev"; +static const char *arg_root = ""; + +static const char * const conf_file_dirs[] = { + "/etc/udev/hwdb.d", + UDEVLIBEXECDIR "/hwdb.d", + NULL +}; + +/* in-memory trie objects */ +struct trie { + struct trie_node *root; + struct strbuf *strings; + + size_t nodes_count; + size_t children_count; + size_t values_count; +}; + +struct trie_node { + /* prefix, common part for all children of this node */ + size_t prefix_off; + + /* sorted array of pointers to children nodes */ + struct trie_child_entry *children; + uint8_t children_count; + + /* sorted array of key/value pairs */ + struct trie_value_entry *values; + size_t values_count; +}; + +/* children array item with char (0-255) index */ +struct trie_child_entry { + uint8_t c; + struct trie_node *child; +}; + +/* value array item with key/value pairs */ +struct trie_value_entry { + size_t key_off; + size_t value_off; +}; + +static int trie_children_cmp(const void *v1, const void *v2) { + const struct trie_child_entry *n1 = v1; + const struct trie_child_entry *n2 = v2; + + return n1->c - n2->c; +} + +static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) { + struct trie_child_entry *child; + + /* extend array, add new entry, sort for bisection */ + child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry)); + if (!child) + return -ENOMEM; + + node->children = child; + trie->children_count++; + node->children[node->children_count].c = c; + node->children[node->children_count].child = node_child; + node->children_count++; + qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); + trie->nodes_count++; + + return 0; +} + +static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) { + struct trie_child_entry *child; + struct trie_child_entry search; + + search.c = c; + child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); + if (child) + return child->child; + return NULL; +} + +static void trie_node_cleanup(struct trie_node *node) { + size_t i; + + for (i = 0; i < node->children_count; i++) + trie_node_cleanup(node->children[i].child); + free(node->children); + free(node->values); + free(node); +} + +static void trie_free(struct trie *trie) { + if (!trie) + return; + + if (trie->root) + trie_node_cleanup(trie->root); + + strbuf_cleanup(trie->strings); + free(trie); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free); + +static int trie_values_cmp(const void *v1, const void *v2, void *arg) { + const struct trie_value_entry *val1 = v1; + const struct trie_value_entry *val2 = v2; + struct trie *trie = arg; + + return strcmp(trie->strings->buf + val1->key_off, + trie->strings->buf + val2->key_off); +} + +static int trie_node_add_value(struct trie *trie, struct trie_node *node, + const char *key, const char *value) { + ssize_t k, v; + struct trie_value_entry *val; + + k = strbuf_add_string(trie->strings, key, strlen(key)); + if (k < 0) + return k; + v = strbuf_add_string(trie->strings, value, strlen(value)); + if (v < 0) + return v; + + if (node->values_count) { + struct trie_value_entry search = { + .key_off = k, + .value_off = v, + }; + + val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); + if (val) { + /* replace existing earlier key with new value */ + val->value_off = v; + return 0; + } + } + + /* extend array, add new entry, sort for bisection */ + val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry)); + if (!val) + return -ENOMEM; + trie->values_count++; + node->values = val; + node->values[node->values_count].key_off = k; + node->values[node->values_count].value_off = v; + node->values_count++; + qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); + return 0; +} + +static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, + const char *key, const char *value) { + size_t i = 0; + int err = 0; + + for (;;) { + size_t p; + uint8_t c; + struct trie_node *child; + + for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) { + _cleanup_free_ char *s = NULL; + ssize_t off; + _cleanup_free_ struct trie_node *new_child = NULL; + + if (c == search[i + p]) + continue; + + /* split node */ + new_child = new0(struct trie_node, 1); + if (!new_child) + return -ENOMEM; + + /* move values from parent to child */ + new_child->prefix_off = node->prefix_off + p+1; + new_child->children = node->children; + new_child->children_count = node->children_count; + new_child->values = node->values; + new_child->values_count = node->values_count; + + /* update parent; use strdup() because the source gets realloc()d */ + s = strndup(trie->strings->buf + node->prefix_off, p); + if (!s) + return -ENOMEM; + + off = strbuf_add_string(trie->strings, s, p); + if (off < 0) + return off; + + node->prefix_off = off; + node->children = NULL; + node->children_count = 0; + node->values = NULL; + node->values_count = 0; + err = node_add_child(trie, node, new_child, c); + if (err < 0) + return err; + + new_child = NULL; /* avoid cleanup */ + break; + } + i += p; + + c = search[i]; + if (c == '\0') + return trie_node_add_value(trie, node, key, value); + + child = node_lookup(node, c); + if (!child) { + ssize_t off; + + /* new child */ + child = new0(struct trie_node, 1); + if (!child) + return -ENOMEM; + + off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1)); + if (off < 0) { + free(child); + return off; + } + + child->prefix_off = off; + err = node_add_child(trie, node, child, c); + if (err < 0) { + free(child); + return err; + } + + return trie_node_add_value(trie, child, key, value); + } + + node = child; + i++; + } +} + +struct trie_f { + FILE *f; + struct trie *trie; + uint64_t strings_off; + + uint64_t nodes_count; + uint64_t children_count; + uint64_t values_count; +}; + +/* calculate the storage space for the nodes, children arrays, value arrays */ +static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { + uint64_t i; + + for (i = 0; i < node->children_count; i++) + trie_store_nodes_size(trie, node->children[i].child); + + trie->strings_off += sizeof(struct trie_node_f); + for (i = 0; i < node->children_count; i++) + trie->strings_off += sizeof(struct trie_child_entry_f); + for (i = 0; i < node->values_count; i++) + trie->strings_off += sizeof(struct trie_value_entry_f); +} + +static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { + uint64_t i; + struct trie_node_f n = { + .prefix_off = htole64(trie->strings_off + node->prefix_off), + .children_count = node->children_count, + .values_count = htole64(node->values_count), + }; + struct trie_child_entry_f *children = NULL; + int64_t node_off; + + if (node->children_count) { + children = new0(struct trie_child_entry_f, node->children_count); + if (!children) + return -ENOMEM; + } + + /* post-order recursion */ + for (i = 0; i < node->children_count; i++) { + int64_t child_off; + + child_off = trie_store_nodes(trie, node->children[i].child); + if (child_off < 0) { + free(children); + return child_off; + } + children[i].c = node->children[i].c; + children[i].child_off = htole64(child_off); + } + + /* write node */ + node_off = ftello(trie->f); + fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); + trie->nodes_count++; + + /* append children array */ + if (node->children_count) { + fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); + trie->children_count += node->children_count; + free(children); + } + + /* append values array */ + for (i = 0; i < node->values_count; i++) { + struct trie_value_entry_f v = { + .key_off = htole64(trie->strings_off + node->values[i].key_off), + .value_off = htole64(trie->strings_off + node->values[i].value_off), + }; + + fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); + trie->values_count++; + } + + return node_off; +} + +static int trie_store(struct trie *trie, const char *filename) { + struct trie_f t = { + .trie = trie, + }; + _cleanup_free_ char *filename_tmp = NULL; + int64_t pos; + int64_t root_off; + int64_t size; + struct trie_header_f h = { + .signature = HWDB_SIG, + .tool_version = htole64(atoi(VERSION)), + .header_size = htole64(sizeof(struct trie_header_f)), + .node_size = htole64(sizeof(struct trie_node_f)), + .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), + .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), + }; + int err; + + /* calculate size of header, nodes, children entries, value entries */ + t.strings_off = sizeof(struct trie_header_f); + trie_store_nodes_size(&t, trie->root); + + err = fopen_temporary(filename , &t.f, &filename_tmp); + if (err < 0) + return err; + fchmod(fileno(t.f), 0444); + + /* write nodes */ + err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } + root_off = trie_store_nodes(&t, trie->root); + h.nodes_root_off = htole64(root_off); + pos = ftello(t.f); + h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); + + /* write string buffer */ + fwrite(trie->strings->buf, trie->strings->len, 1, t.f); + h.strings_len = htole64(trie->strings->len); + + /* write header */ + size = ftello(t.f); + h.file_size = htole64(size); + err = fseeko(t.f, 0, SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } + fwrite(&h, sizeof(struct trie_header_f), 1, t.f); + err = ferror(t.f); + if (err) + err = -errno; + fclose(t.f); + if (err < 0 || rename(filename_tmp, filename) < 0) { + unlink_noerrno(filename_tmp); + return err < 0 ? err : -errno; + } + + log_debug("=== trie on-disk ==="); + log_debug("size: %8"PRIi64" bytes", size); + log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); + log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", + t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); + log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.children_count * sizeof(struct trie_child_entry_f), t.children_count); + log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.values_count * sizeof(struct trie_value_entry_f), t.values_count); + log_debug("string store: %8zu bytes", trie->strings->len); + log_debug("strings start: %8"PRIu64, t.strings_off); + + return 0; +} + +static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename) { + char *value, **entry; + + value = strchr(line, '='); + if (!value) { + log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); + return -EINVAL; + } + + value[0] = '\0'; + value++; + + /* libudev requires properties to start with a space */ + while (isblank(line[0]) && isblank(line[1])) + line++; + + if (line[0] == '\0' || value[0] == '\0') { + log_error("Error, empty key or value '%s' in '%s':", line, filename); + return -EINVAL; + } + + STRV_FOREACH(entry, match_list) + trie_insert(trie, trie->root, *entry, line, value); + + return 0; +} + +static int import_file(struct trie *trie, const char *filename) { + enum { + HW_NONE, + HW_MATCH, + HW_DATA, + } state = HW_NONE; + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + _cleanup_strv_free_ char **match_list = NULL; + char *match = NULL; + int r; + + f = fopen(filename, "re"); + if (!f) + return -errno; + + while (fgets(line, sizeof(line), f)) { + size_t len; + char *pos; + + /* comment line */ + if (line[0] == '#') + continue; + + /* strip trailing comment */ + pos = strchr(line, '#'); + if (pos) + pos[0] = '\0'; + + /* strip trailing whitespace */ + len = strlen(line); + while (len > 0 && isspace(line[len-1])) + len--; + line[len] = '\0'; + + switch (state) { + case HW_NONE: + if (len == 0) + break; + + if (line[0] == ' ') { + log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); + break; + } + + /* start of record, first match */ + state = HW_MATCH; + + match = strdup(line); + if (!match) + return -ENOMEM; + + r = strv_consume(&match_list, match); + if (r < 0) + return r; + + break; + + case HW_MATCH: + if (len == 0) { + log_error("Error, DATA expected but got empty line in '%s':", filename); + state = HW_NONE; + strv_clear(match_list); + break; + } + + /* another match */ + if (line[0] != ' ') { + match = strdup(line); + if (!match) + return -ENOMEM; + + r = strv_consume(&match_list, match); + if (r < 0) + return r; + + break; + } + + /* first data */ + state = HW_DATA; + insert_data(trie, match_list, line, filename); + break; + + case HW_DATA: + /* end of record */ + if (len == 0) { + state = HW_NONE; + strv_clear(match_list); + break; + } + + if (line[0] != ' ') { + log_error("Error, DATA expected but got '%s' in '%s':", line, filename); + state = HW_NONE; + strv_clear(match_list); + break; + } + + insert_data(trie, match_list, line, filename); + break; + }; + } + + return 0; +} + +static int hwdb_query(int argc, char *argv[], void *userdata) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + const char *key, *value; + const char *modalias; + int r; + + assert(argc >= 2); + assert(argv); + + modalias = argv[1]; + + r = sd_hwdb_new(&hwdb); + if (r < 0) + return r; + + SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) + printf("%s=%s\n", key, value); + + return 0; +} + +static int hwdb_update(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *hwdb_bin = NULL; + _cleanup_(trie_freep) struct trie *trie = NULL; + char **files, **f; + int r; + + trie = new0(struct trie, 1); + if (!trie) + return -ENOMEM; + + /* string store */ + trie->strings = strbuf_new(); + if (!trie->strings) + return -ENOMEM; + + /* index */ + trie->root = new0(struct trie_node, 1); + if (!trie->root) + return -ENOMEM; + + trie->nodes_count++; + + r = conf_files_list_strv(&files, ".hwdb", arg_root, conf_file_dirs); + if (r < 0) + return log_error_errno(r, "failed to enumerate hwdb files: %m"); + + STRV_FOREACH(f, files) { + log_debug("reading file '%s'", *f); + import_file(trie, *f); + } + strv_free(files); + + strbuf_complete(trie->strings); + + log_debug("=== trie in-memory ==="); + log_debug("nodes: %8zu bytes (%8zu)", + trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); + log_debug("children arrays: %8zu bytes (%8zu)", + trie->children_count * sizeof(struct trie_child_entry), trie->children_count); + log_debug("values arrays: %8zu bytes (%8zu)", + trie->values_count * sizeof(struct trie_value_entry), trie->values_count); + log_debug("strings: %8zu bytes", + trie->strings->len); + log_debug("strings incoming: %8zu bytes (%8zu)", + trie->strings->in_len, trie->strings->in_count); + log_debug("strings dedup'ed: %8zu bytes (%8zu)", + trie->strings->dedup_len, trie->strings->dedup_count); + + hwdb_bin = strjoin(arg_root, "/", arg_hwdb_bin_dir, "/hwdb.bin", NULL); + if (!hwdb_bin) + return -ENOMEM; + + mkdir_parents_label(hwdb_bin, 0755); + r = trie_store(trie, hwdb_bin); + if (r < 0) + return log_error_errno(r, "Failure writing database %s: %m", hwdb_bin); + + return label_fix(hwdb_bin, false, false); +} + +static void help(void) { + printf("Usage: %s OPTIONS COMMAND\n\n" + "Update or query the hardware database.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --usr Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" + " -r --root=PATH Alternative root path in the filesystem\n\n" + "Commands:\n" + " update Update the hwdb database\n" + " query MODALIAS Query database and print result\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_USR, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "usr", no_argument, NULL, ARG_USR }, + { "root", required_argument, NULL, 'r' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) { + switch(c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_USR: + arg_hwdb_bin_dir = UDEVLIBEXECDIR; + break; + + case 'r': + arg_root = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unknown option"); + } + } + + return 1; +} + +static int hwdb_main(int argc, char *argv[]) { + const Verb verbs[] = { + { "update", 1, 1, 0, hwdb_update }, + { "query", 2, 2, 0, hwdb_query }, + {}, + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +int main (int argc, char *argv[]) { + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + mac_selinux_init(); + + r = hwdb_main(argc, argv); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-udev/systemd-hwdb/hwdb.xml b/src/grp-udev/systemd-hwdb/hwdb.xml new file mode 100644 index 0000000000..2b1e60fb22 --- /dev/null +++ b/src/grp-udev/systemd-hwdb/hwdb.xml @@ -0,0 +1,85 @@ + + + + + + hwdb + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + hwdb + 7 + + + + hwdb + Hardware Database + + + Description + The hardware database is a key-value store for associating modalias-like keys to + udev-property-like values. It is used primarily by udev to add the relevant properties + to matching devices, but it can also be queried directly. + + + Hardware Database Files + The hwdb files are read from the files located in the + system hwdb directory /usr/lib/udev/hwdb.d and + the local administration directory /etc/udev/hwdb.d. + All hwdb files are collectively sorted and processed in lexical order, + regardless of the directories in which they live. However, files with + identical filenames replace each other. Files in /etc + have the highest priority and take precedence over files with the same + name in /usr/lib. This can be used to override a + system-supplied hwdb file with a local file if needed; + a symlink in /etc with the same name as a hwdb file in + /usr/lib, pointing to /dev/null, + disables the hwdb file entirely. hwdb files must have the extension + .hwdb; other extensions are ignored. + + The hwdb file contains data records consisting of matches and + associated key-value pairs. Every record in the hwdb starts with one or + more match strings, specifying a shell glob to compare the database + lookup string against. Multiple match lines are specified in additional + consecutive lines. Every match line is compared individually, and they are + combined by OR. Every match line must start at the first character of + the line. + + The match lines are followed by one or more key-value pair lines, which + are recognized by a leading space character. The key name and value are separated + by =. An empty line signifies the end + of a record. Lines beginning with # are ignored. + + The content of all hwdb files is read by + systemd-hwdb8 + and compiled to a binary database located at /etc/udev/hwdb.bin, + or alternatively /usr/lib/udev/hwdb.bin if you want ship the compiled + database in an immutable image. + During runtime, only the binary database is used. + + + + See Also + + + systemd-hwdb8 + + + + diff --git a/src/grp-udev/systemd-hwdb/systemd-hwdb.xml b/src/grp-udev/systemd-hwdb/systemd-hwdb.xml new file mode 100644 index 0000000000..2b363c77f2 --- /dev/null +++ b/src/grp-udev/systemd-hwdb/systemd-hwdb.xml @@ -0,0 +1,93 @@ + + + + + + systemd-hwdb + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd-hwdb + 8 + + + + systemd-hwdbhardware database management tool + + + + + systemd-hwdb options update + + + systemd-hwdb options query modalias + + + + Description + systemd-hwdb expects a command and command + specific arguments. It manages the binary hardware database. + + + Options + + + + + + Print help text. + + + + + + Generate in /usr/lib/udev instead of /etc/udev. + + + + + + + Alternate root path in the filesystem. + + + + + systemd-hwdb + <arg choice="opt"><replaceable>options</replaceable></arg> + update + Update the binary database. + + + systemd-hwdb + <arg choice="opt"><replaceable>options</replaceable></arg> + query + <arg><replaceable>MODALIAS</replaceable></arg> + + Query database and print result. + + + + + See Also + + hwdb7 + + + diff --git a/src/grp-udev/systemd-udevd/Makefile b/src/grp-udev/systemd-udevd/Makefile new file mode 100644 index 0000000000..0ef78eb339 --- /dev/null +++ b/src/grp-udev/systemd-udevd/Makefile @@ -0,0 +1,36 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += \ + systemd-udevd + +systemd_udevd_SOURCES = \ + src/udev/udevd.c + +systemd_udevd_LDADD = \ + libudev-core.la \ + libbasic.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/systemd-udevd/systemd-udevd.service.in b/src/grp-udev/systemd-udevd/systemd-udevd.service.in new file mode 100644 index 0000000000..67e4c5fcd7 --- /dev/null +++ b/src/grp-udev/systemd-udevd/systemd-udevd.service.in @@ -0,0 +1,27 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=udev Kernel Device Manager +Documentation=man:systemd-udevd.service(8) man:udev(7) +DefaultDependencies=no +Wants=systemd-udevd-control.socket systemd-udevd-kernel.socket +After=systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-sysusers.service +Before=sysinit.target +ConditionPathIsReadWrite=/sys + +[Service] +Type=notify +OOMScoreAdjust=-1000 +Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-udevd +MountFlags=slave +KillMode=mixed +WatchdogSec=3min +TasksMax=infinity diff --git a/src/grp-udev/systemd-udevd/systemd-udevd.service.xml b/src/grp-udev/systemd-udevd/systemd-udevd.service.xml new file mode 100644 index 0000000000..243fd06471 --- /dev/null +++ b/src/grp-udev/systemd-udevd/systemd-udevd.service.xml @@ -0,0 +1,188 @@ + + + + + + + systemd-udevd.service + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + systemd-udevd.service + 8 + + + + systemd-udevd.service + systemd-udevd-control.socket + systemd-udevd-kernel.socket + systemd-udevd + Device event managing daemon + + + + systemd-udevd.service + systemd-udevd-control.socket + systemd-udevd-kernel.socket + + + /usr/lib/systemd/systemd-udevd + + + + + + + + + + + + + Description + systemd-udevd listens to kernel uevents. + For every event, systemd-udevd executes matching instructions + specified in udev rules. See + udev7 + . + + The behavior of the daemon can be configured using + udev.conf5, + its command line options, environment variables, and on the kernel + command line, or changed dynamically with udevadm + control. + + + + Options + + + + + Detach and run in the background. + + + + + + + Print debug messages to standard error. + + + + + + + Limit the number of events executed in parallel. + + + + + + + Delay the execution of RUN + instructions by the given number of seconds. This option + might be useful when debugging system crashes during + coldplug caused by loading non-working kernel + modules. + + + + + + + Set the number of seconds to wait for events to finish. After + this time, the event will be terminated. The default is 180 seconds. + + + + + + + Specify when systemd-udevd should resolve names of users and groups. + When set to (the default), names will be + resolved when the rules are parsed. When set to + , names will be resolved for every event. + When set to , names will never be resolved + and all devices will be owned by root. + + + + + + + + + + + + + Kernel command line + + Parameters starting with "rd." will be read when + systemd-udevd is used in an initrd. + + udev.log-priority= + rd.udev.log-priority= + + Set the log level. + + + + udev.children-max= + rd.udev.children-max= + + Limit the number of events executed in parallel. + + + + udev.exec-delay= + rd.udev.exec-delay= + + Delay the execution of RUN instructions by the given + number of seconds. This option might be useful when + debugging system crashes during coldplug caused by loading + non-working kernel modules. + + + + udev.event-timeout= + rd.udev.event-timeout= + + Wait for events to finish up to the given number + of seconds. This option might be useful if events are + terminated due to kernel drivers taking too long to initialize. + + + + net.ifnames= + + Network interfaces are renamed to give them predictable names + when possible. It is enabled by default; specifying 0 disables it. + + + + + + + + See Also + + udev.conf5, + udev7, + udevadm8 + + + diff --git a/src/grp-udev/systemd-udevd/udev.conf b/src/grp-udev/systemd-udevd/udev.conf new file mode 100644 index 0000000000..47d1433002 --- /dev/null +++ b/src/grp-udev/systemd-udevd/udev.conf @@ -0,0 +1,3 @@ +# see udev.conf(5) for details + +#udev_log="info" diff --git a/src/grp-udev/systemd-udevd/udev.conf.xml b/src/grp-udev/systemd-udevd/udev.conf.xml new file mode 100644 index 0000000000..e104e53f5d --- /dev/null +++ b/src/grp-udev/systemd-udevd/udev.conf.xml @@ -0,0 +1,94 @@ + + + + + + + + + udev.conf + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + udev.conf + 5 + + + + udev.conf + Configuration for device event managing daemon + + + + /etc/udev/udev.conf + + + + Description + + + systemd-udevd8 + expects its main configuration file at + /etc/udev/udev.conf. It consists of a set + of variables allowing the user to override default udev + values. All empty lines or lines beginning with '#' are + ignored. The following variables can be set: + + + + + udev_log + + + The log level. Valid values are the numerical + syslog priorities or their textual representations: + , and + . + + + + + + In addition, systemd-udevd can be configured + by command line options and the kernel command line (see + systemd-udevd8). + + + + + See Also + + systemd-udevd8, + udev7, + udevadm8 + + + diff --git a/src/grp-udev/systemd-udevd/udevd.c b/src/grp-udev/systemd-udevd/udevd.c new file mode 100644 index 0000000000..23cd4759fc --- /dev/null +++ b/src/grp-udev/systemd-udevd/udevd.c @@ -0,0 +1,1764 @@ +/* + * Copyright (C) 2004-2012 Kay Sievers + * Copyright (C) 2004 Chris Friesen + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/cpu-set-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/dev-setup.h" +#include "shared/udev-util.h" +#include "udev.h" + +static bool arg_debug = false; +static int arg_daemonize = false; +static int arg_resolve_names = 1; +static unsigned arg_children_max; +static int arg_exec_delay; +static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; +static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3; + +typedef struct Manager { + struct udev *udev; + sd_event *event; + Hashmap *workers; + struct udev_list_node events; + const char *cgroup; + pid_t pid; /* the process that originally allocated the manager object */ + + struct udev_rules *rules; + struct udev_list properties; + + struct udev_monitor *monitor; + struct udev_ctrl *ctrl; + struct udev_ctrl_connection *ctrl_conn_blocking; + int fd_inotify; + int worker_watch[2]; + + sd_event_source *ctrl_event; + sd_event_source *uevent_event; + sd_event_source *inotify_event; + + usec_t last_usec; + + bool stop_exec_queue:1; + bool exit:1; +} Manager; + +enum event_state { + EVENT_UNDEF, + EVENT_QUEUED, + EVENT_RUNNING, +}; + +struct event { + struct udev_list_node node; + Manager *manager; + struct udev *udev; + struct udev_device *dev; + struct udev_device *dev_kernel; + struct worker *worker; + enum event_state state; + unsigned long long int delaying_seqnum; + unsigned long long int seqnum; + const char *devpath; + size_t devpath_len; + const char *devpath_old; + dev_t devnum; + int ifindex; + bool is_block; + sd_event_source *timeout_warning; + sd_event_source *timeout; +}; + +static inline struct event *node_to_event(struct udev_list_node *node) { + return container_of(node, struct event, node); +} + +static void event_queue_cleanup(Manager *manager, enum event_state type); + +enum worker_state { + WORKER_UNDEF, + WORKER_RUNNING, + WORKER_IDLE, + WORKER_KILLED, +}; + +struct worker { + Manager *manager; + struct udev_list_node node; + int refcount; + pid_t pid; + struct udev_monitor *monitor; + enum worker_state state; + struct event *event; +}; + +/* passed from worker to main process */ +struct worker_message { +}; + +static void event_free(struct event *event) { + int r; + + if (!event) + return; + + udev_list_node_remove(&event->node); + udev_device_unref(event->dev); + udev_device_unref(event->dev_kernel); + + sd_event_source_unref(event->timeout_warning); + sd_event_source_unref(event->timeout); + + if (event->worker) + event->worker->event = NULL; + + assert(event->manager); + + if (udev_list_node_is_empty(&event->manager->events)) { + /* only clean up the queue from the process that created it */ + if (event->manager->pid == getpid()) { + r = unlink("/run/udev/queue"); + if (r < 0) + log_warning_errno(errno, "could not unlink /run/udev/queue: %m"); + } + } + + free(event); +} + +static void worker_free(struct worker *worker) { + if (!worker) + return; + + assert(worker->manager); + + hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid)); + udev_monitor_unref(worker->monitor); + event_free(worker->event); + + free(worker); +} + +static void manager_workers_free(Manager *manager) { + struct worker *worker; + Iterator i; + + assert(manager); + + HASHMAP_FOREACH(worker, manager->workers, i) + worker_free(worker); + + manager->workers = hashmap_free(manager->workers); +} + +static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) { + _cleanup_free_ struct worker *worker = NULL; + int r; + + assert(ret); + assert(manager); + assert(worker_monitor); + assert(pid > 1); + + worker = new0(struct worker, 1); + if (!worker) + return -ENOMEM; + + worker->refcount = 1; + worker->manager = manager; + /* close monitor, but keep address around */ + udev_monitor_disconnect(worker_monitor); + worker->monitor = udev_monitor_ref(worker_monitor); + worker->pid = pid; + + r = hashmap_ensure_allocated(&manager->workers, NULL); + if (r < 0) + return r; + + r = hashmap_put(manager->workers, PID_TO_PTR(pid), worker); + if (r < 0) + return r; + + *ret = worker; + worker = NULL; + + return 0; +} + +static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + struct event *event = userdata; + + assert(event); + assert(event->worker); + + kill_and_sigcont(event->worker->pid, SIGKILL); + event->worker->state = WORKER_KILLED; + + log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath); + + return 1; +} + +static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { + struct event *event = userdata; + + assert(event); + + log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath); + + return 1; +} + +static void worker_attach_event(struct worker *worker, struct event *event) { + sd_event *e; + uint64_t usec; + + assert(worker); + assert(worker->manager); + assert(event); + assert(!event->worker); + assert(!worker->event); + + worker->state = WORKER_RUNNING; + worker->event = event; + event->state = EVENT_RUNNING; + event->worker = worker; + + e = worker->manager->event; + + assert_se(sd_event_now(e, clock_boottime_or_monotonic(), &usec) >= 0); + + (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(), + usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event); + + (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(), + usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event); +} + +static void manager_free(Manager *manager) { + if (!manager) + return; + + udev_builtin_exit(manager->udev); + + sd_event_source_unref(manager->ctrl_event); + sd_event_source_unref(manager->uevent_event); + sd_event_source_unref(manager->inotify_event); + + udev_unref(manager->udev); + sd_event_unref(manager->event); + manager_workers_free(manager); + event_queue_cleanup(manager, EVENT_UNDEF); + + udev_monitor_unref(manager->monitor); + udev_ctrl_unref(manager->ctrl); + udev_ctrl_connection_unref(manager->ctrl_conn_blocking); + + udev_list_cleanup(&manager->properties); + udev_rules_unref(manager->rules); + + safe_close(manager->fd_inotify); + safe_close_pair(manager->worker_watch); + + free(manager); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); + +static int worker_send_message(int fd) { + struct worker_message message = {}; + + return loop_write(fd, &message, sizeof(message), false); +} + +static void worker_spawn(Manager *manager, struct event *event) { + struct udev *udev = event->udev; + _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL; + pid_t pid; + int r = 0; + + /* listen for new events */ + worker_monitor = udev_monitor_new_from_netlink(udev, NULL); + if (worker_monitor == NULL) + return; + /* allow the main daemon netlink address to send devices to the worker */ + udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor); + r = udev_monitor_enable_receiving(worker_monitor); + if (r < 0) + log_error_errno(r, "worker: could not enable receiving of device: %m"); + + pid = fork(); + switch (pid) { + case 0: { + struct udev_device *dev = 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 }; + struct epoll_event ep_monitor = { .events = EPOLLIN }; + sigset_t mask; + + /* take initial device from queue */ + dev = event->dev; + event->dev = NULL; + + unsetenv("NOTIFY_SOCKET"); + + manager_workers_free(manager); + event_queue_cleanup(manager, EVENT_UNDEF); + + manager->monitor = udev_monitor_unref(manager->monitor); + manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking); + manager->ctrl = udev_ctrl_unref(manager->ctrl); + manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]); + + manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); + manager->uevent_event = sd_event_source_unref(manager->uevent_event); + manager->inotify_event = sd_event_source_unref(manager->inotify_event); + + manager->event = sd_event_unref(manager->event); + + sigfillset(&mask); + fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (fd_signal < 0) { + r = log_error_errno(errno, "error creating signalfd %m"); + goto out; + } + ep_signal.data.fd = fd_signal; + + fd_monitor = udev_monitor_get_fd(worker_monitor); + ep_monitor.data.fd = fd_monitor; + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + r = log_error_errno(errno, "error creating epoll fd: %m"); + goto out; + } + + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { + r = log_error_errno(errno, "fail to add fds to epoll: %m"); + goto out; + } + + /* Request TERM signal if parent exits. + Ignore error, not much we can do in that case. */ + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); + + /* Reset OOM score, we only protect the main daemon. */ + write_string_file("/proc/self/oom_score_adj", "0", 0); + + for (;;) { + struct udev_event *udev_event; + int fd_lock = -1; + + assert(dev); + + log_debug("seq %llu running", udev_device_get_seqnum(dev)); + udev_event = udev_event_new(dev); + if (udev_event == NULL) { + r = -ENOMEM; + goto out; + } + + if (arg_exec_delay > 0) + udev_event->exec_delay = arg_exec_delay; + + /* + * Take a shared lock on the device node; this establishes + * a concept of device "ownership" to serialize device + * access. External processes holding an exclusive lock will + * cause udev to skip the event handling; in the case udev + * acquired the lock, the external process can block until + * udev has finished its event handling. + */ + if (!streq_ptr(udev_device_get_action(dev), "remove") && + streq_ptr("block", udev_device_get_subsystem(dev)) && + !startswith(udev_device_get_sysname(dev), "dm-") && + !startswith(udev_device_get_sysname(dev), "md")) { + struct udev_device *d = dev; + + if (streq_ptr("partition", udev_device_get_devtype(d))) + d = udev_device_get_parent(d); + + if (d) { + fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) { + log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d)); + fd_lock = safe_close(fd_lock); + goto skip; + } + } + } + + /* needed for renaming netifs */ + udev_event->rtnl = rtnl; + + /* apply rules, create node, symlinks */ + udev_event_execute_rules(udev_event, + arg_event_timeout_usec, arg_event_timeout_warn_usec, + &manager->properties, + manager->rules); + + udev_event_execute_run(udev_event, + arg_event_timeout_usec, arg_event_timeout_warn_usec); + + if (udev_event->rtnl) + /* in case rtnl was initialized */ + rtnl = sd_netlink_ref(udev_event->rtnl); + + /* apply/restore inotify watch */ + if (udev_event->inotify_watch) { + udev_watch_begin(udev, dev); + udev_device_update_db(dev); + } + + safe_close(fd_lock); + + /* send processed event back to libudev listeners */ + udev_monitor_send_device(worker_monitor, NULL, dev); + +skip: + log_debug("seq %llu processed", udev_device_get_seqnum(dev)); + + /* send udevd the result of the event execution */ + r = worker_send_message(manager->worker_watch[WRITE_END]); + if (r < 0) + log_error_errno(r, "failed to send result of seq %llu to main daemon: %m", + udev_device_get_seqnum(dev)); + + udev_device_unref(dev); + dev = NULL; + + udev_event_unref(udev_event); + + /* wait for more device messages from main udevd, or term signal */ + while (dev == NULL) { + struct epoll_event ev[4]; + int fdcount; + int i; + + fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); + if (fdcount < 0) { + if (errno == EINTR) + continue; + r = log_error_errno(errno, "failed to poll: %m"); + goto out; + } + + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { + dev = udev_monitor_receive_device(worker_monitor); + break; + } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { + struct signalfd_siginfo fdsi; + ssize_t size; + + size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); + if (size != sizeof(struct signalfd_siginfo)) + continue; + switch (fdsi.ssi_signo) { + case SIGTERM: + goto out; + } + } + } + } + } +out: + udev_device_unref(dev); + manager_free(manager); + log_close(); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + } + case -1: + event->state = EVENT_QUEUED; + log_error_errno(errno, "fork of child failed: %m"); + break; + default: + { + struct worker *worker; + + r = worker_new(&worker, manager, worker_monitor, pid); + if (r < 0) + return; + + worker_attach_event(worker, event); + + log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid); + break; + } + } +} + +static void event_run(Manager *manager, struct event *event) { + struct worker *worker; + Iterator i; + + assert(manager); + assert(event); + + HASHMAP_FOREACH(worker, manager->workers, i) { + ssize_t count; + + if (worker->state != WORKER_IDLE) + continue; + + count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev); + if (count < 0) { + log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it", + worker->pid, count); + kill(worker->pid, SIGKILL); + worker->state = WORKER_KILLED; + continue; + } + worker_attach_event(worker, event); + return; + } + + if (hashmap_size(manager->workers) >= arg_children_max) { + if (arg_children_max > 1) + log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers)); + return; + } + + /* start new worker and pass initial device */ + worker_spawn(manager, event); +} + +static int event_queue_insert(Manager *manager, struct udev_device *dev) { + struct event *event; + int r; + + assert(manager); + assert(dev); + + /* only one process can add events to the queue */ + if (manager->pid == 0) + manager->pid = getpid(); + + assert(manager->pid == getpid()); + + event = new0(struct event, 1); + if (!event) + return -ENOMEM; + + event->udev = udev_device_get_udev(dev); + event->manager = manager; + event->dev = dev; + event->dev_kernel = udev_device_shallow_clone(dev); + udev_device_copy_properties(event->dev_kernel, dev); + event->seqnum = udev_device_get_seqnum(dev); + event->devpath = udev_device_get_devpath(dev); + event->devpath_len = strlen(event->devpath); + event->devpath_old = udev_device_get_devpath_old(dev); + event->devnum = udev_device_get_devnum(dev); + event->is_block = streq("block", udev_device_get_subsystem(dev)); + event->ifindex = udev_device_get_ifindex(dev); + + log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev), + udev_device_get_action(dev), udev_device_get_subsystem(dev)); + + event->state = EVENT_QUEUED; + + if (udev_list_node_is_empty(&manager->events)) { + r = touch("/run/udev/queue"); + if (r < 0) + log_warning_errno(r, "could not touch /run/udev/queue: %m"); + } + + udev_list_node_append(&event->node, &manager->events); + + return 0; +} + +static void manager_kill_workers(Manager *manager) { + struct worker *worker; + Iterator i; + + assert(manager); + + HASHMAP_FOREACH(worker, manager->workers, i) { + if (worker->state == WORKER_KILLED) + continue; + + worker->state = WORKER_KILLED; + kill(worker->pid, SIGTERM); + } +} + +/* lookup event for identical, parent, child device */ +static bool is_devpath_busy(Manager *manager, struct event *event) { + struct udev_list_node *loop; + size_t common; + + /* check if queue contains events we depend on */ + udev_list_node_foreach(loop, &manager->events) { + struct event *loop_event = node_to_event(loop); + + /* we already found a later event, earlier can not block us, no need to check again */ + if (loop_event->seqnum < event->delaying_seqnum) + continue; + + /* event we checked earlier still exists, no need to check again */ + if (loop_event->seqnum == event->delaying_seqnum) + return true; + + /* found ourself, no later event can block us */ + if (loop_event->seqnum >= event->seqnum) + break; + + /* check major/minor */ + if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) + return true; + + /* check network device ifindex */ + if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) + return true; + + /* check our old name */ + if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* compare devpath */ + common = MIN(loop_event->devpath_len, event->devpath_len); + + /* one devpath is contained in the other? */ + if (memcmp(loop_event->devpath, event->devpath, common) != 0) + continue; + + /* identical device event found */ + if (loop_event->devpath_len == event->devpath_len) { + /* devices names might have changed/swapped in the meantime */ + if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) + continue; + if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) + continue; + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* parent device event found */ + if (event->devpath[common] == '/') { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* child device event found */ + if (loop_event->devpath[common] == '/') { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* no matching device */ + continue; + } + + return false; +} + +static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + Manager *manager = userdata; + + assert(manager); + + log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish"); + + sd_event_exit(manager->event, -ETIMEDOUT); + + return 1; +} + +static void manager_exit(Manager *manager) { + uint64_t usec; + int r; + + assert(manager); + + manager->exit = true; + + sd_notify(false, + "STOPPING=1\n" + "STATUS=Starting shutdown..."); + + /* close sources of new events and discard buffered events */ + manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); + manager->ctrl = udev_ctrl_unref(manager->ctrl); + + manager->inotify_event = sd_event_source_unref(manager->inotify_event); + manager->fd_inotify = safe_close(manager->fd_inotify); + + manager->uevent_event = sd_event_source_unref(manager->uevent_event); + manager->monitor = udev_monitor_unref(manager->monitor); + + /* discard queued events and kill workers */ + event_queue_cleanup(manager, EVENT_QUEUED); + manager_kill_workers(manager); + + assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); + + r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(), + usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager); + if (r < 0) + return; +} + +/* reload requested, HUP signal received, rules changed, builtin changed */ +static void manager_reload(Manager *manager) { + + assert(manager); + + sd_notify(false, + "RELOADING=1\n" + "STATUS=Flushing configuration..."); + + manager_kill_workers(manager); + manager->rules = udev_rules_unref(manager->rules); + udev_builtin_exit(manager->udev); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing..."); +} + +static void event_queue_start(Manager *manager) { + struct udev_list_node *loop; + usec_t usec; + + assert(manager); + + if (udev_list_node_is_empty(&manager->events) || + manager->exit || manager->stop_exec_queue) + return; + + assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); + /* check for changed config, every 3 seconds at most */ + if (manager->last_usec == 0 || + (usec - manager->last_usec) > 3 * USEC_PER_SEC) { + if (udev_rules_check_timestamp(manager->rules) || + udev_builtin_validate(manager->udev)) + manager_reload(manager); + + manager->last_usec = usec; + } + + udev_builtin_init(manager->udev); + + if (!manager->rules) { + manager->rules = udev_rules_new(manager->udev, arg_resolve_names); + if (!manager->rules) + return; + } + + udev_list_node_foreach(loop, &manager->events) { + struct event *event = node_to_event(loop); + + if (event->state != EVENT_QUEUED) + continue; + + /* do not start event if parent or child event is still running */ + if (is_devpath_busy(manager, event)) + continue; + + event_run(manager, event); + } +} + +static void event_queue_cleanup(Manager *manager, enum event_state match_type) { + struct udev_list_node *loop, *tmp; + + udev_list_node_foreach_safe(loop, tmp, &manager->events) { + struct event *event = node_to_event(loop); + + if (match_type != EVENT_UNDEF && match_type != event->state) + continue; + + event_free(event); + } +} + +static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *manager = userdata; + + assert(manager); + + for (;;) { + struct worker_message msg; + struct iovec iovec = { + .iov_base = &msg, + .iov_len = sizeof(msg), + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control = {}; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + ssize_t size; + struct ucred *ucred = NULL; + struct worker *worker; + + size = recvmsg(fd, &msghdr, MSG_DONTWAIT); + if (size < 0) { + if (errno == EINTR) + continue; + else if (errno == EAGAIN) + /* nothing more to read */ + break; + + return log_error_errno(errno, "failed to receive message: %m"); + } else if (size != sizeof(struct worker_message)) { + log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size); + continue; + } + + 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 (!ucred || ucred->pid <= 0) { + log_warning_errno(EIO, "ignoring worker message without valid PID"); + continue; + } + + /* lookup worker who sent the signal */ + worker = hashmap_get(manager->workers, PID_TO_PTR(ucred->pid)); + if (!worker) { + log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid); + continue; + } + + if (worker->state != WORKER_KILLED) + worker->state = WORKER_IDLE; + + /* worker returned */ + event_free(worker->event); + } + + /* we have free workers, try to schedule events */ + event_queue_start(manager); + + return 1; +} + +static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *manager = userdata; + struct udev_device *dev; + int r; + + assert(manager); + + dev = udev_monitor_receive_device(manager->monitor); + if (dev) { + udev_device_ensure_usec_initialized(dev, NULL); + r = event_queue_insert(manager, dev); + if (r < 0) + udev_device_unref(dev); + else + /* we have fresh events, try to schedule them */ + event_queue_start(manager); + } + + return 1; +} + +/* receive the udevd message from userspace */ +static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *manager = userdata; + _cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL; + _cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL; + const char *str; + int i; + + assert(manager); + + ctrl_conn = udev_ctrl_get_connection(manager->ctrl); + if (!ctrl_conn) + return 1; + + ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); + if (!ctrl_msg) + return 1; + + i = udev_ctrl_get_set_log_level(ctrl_msg); + if (i >= 0) { + log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i); + log_set_max_level(i); + manager_kill_workers(manager); + } + + if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { + log_debug("udevd message (STOP_EXEC_QUEUE) received"); + manager->stop_exec_queue = true; + } + + if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { + log_debug("udevd message (START_EXEC_QUEUE) received"); + manager->stop_exec_queue = false; + event_queue_start(manager); + } + + if (udev_ctrl_get_reload(ctrl_msg) > 0) { + log_debug("udevd message (RELOAD) received"); + manager_reload(manager); + } + + str = udev_ctrl_get_set_env(ctrl_msg); + if (str != NULL) { + _cleanup_free_ char *key = NULL; + + key = strdup(str); + if (key) { + char *val; + + val = strchr(key, '='); + if (val != NULL) { + val[0] = '\0'; + val = &val[1]; + if (val[0] == '\0') { + log_debug("udevd message (ENV) received, unset '%s'", key); + udev_list_entry_add(&manager->properties, key, NULL); + } else { + log_debug("udevd message (ENV) received, set '%s=%s'", key, val); + udev_list_entry_add(&manager->properties, key, val); + } + } else + log_error("wrong key format '%s'", key); + } + manager_kill_workers(manager); + } + + i = udev_ctrl_get_set_children_max(ctrl_msg); + if (i >= 0) { + log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i); + arg_children_max = i; + } + + if (udev_ctrl_get_ping(ctrl_msg) > 0) + log_debug("udevd message (SYNC) received"); + + if (udev_ctrl_get_exit(ctrl_msg) > 0) { + log_debug("udevd message (EXIT) received"); + manager_exit(manager); + /* keep reference to block the client until we exit + TODO: deal with several blocking exit requests */ + manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn); + } + + return 1; +} + +static int synthesize_change(struct udev_device *dev) { + char filename[UTIL_PATH_SIZE]; + int r; + + if (streq_ptr("block", udev_device_get_subsystem(dev)) && + streq_ptr("disk", udev_device_get_devtype(dev)) && + !startswith(udev_device_get_sysname(dev), "dm-")) { + bool part_table_read = false; + bool has_partitions = false; + int fd; + struct udev *udev = udev_device_get_udev(dev); + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + struct udev_list_entry *item; + + /* + * Try to re-read the partition table. This only succeeds if + * none of the devices is busy. The kernel returns 0 if no + * partition table is found, and we will not get an event for + * the disk. + */ + fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd >= 0) { + r = flock(fd, LOCK_EX|LOCK_NB); + if (r >= 0) + r = ioctl(fd, BLKRRPART, 0); + + close(fd); + if (r >= 0) + part_table_read = true; + } + + /* search for partitions */ + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_parent(e, dev); + if (r < 0) + return r; + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return r; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return r; + + udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + continue; + + if (!streq_ptr("partition", udev_device_get_devtype(d))) + continue; + + has_partitions = true; + break; + } + + /* + * We have partitions and re-read the table, the kernel already sent + * out a "change" event for the disk, and "remove/add" for all + * partitions. + */ + if (part_table_read && has_partitions) + return 0; + + /* + * We have partitions but re-reading the partition table did not + * work, synthesize "change" for the disk and all partitions. + */ + log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); + strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); + write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); + + udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + continue; + + if (!streq_ptr("partition", udev_device_get_devtype(d))) + continue; + + log_debug("device %s closed, synthesising partition '%s' 'change'", + udev_device_get_devnode(dev), udev_device_get_devnode(d)); + strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL); + write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); + } + + return 0; + } + + log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); + strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); + write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); + + return 0; +} + +static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *manager = userdata; + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + assert(manager); + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 1; + + return log_error_errno(errno, "Failed to read inotify fd: %m"); + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + + dev = udev_watch_lookup(manager->udev, e->wd); + if (!dev) + continue; + + log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev)); + if (e->mask & IN_CLOSE_WRITE) { + synthesize_change(dev); + + /* settle might be waiting on us to determine the queue + * state. If we just handled an inotify event, we might have + * generated a "change" event, but we won't have queued up + * the resultant uevent yet. Do that. + */ + on_uevent(NULL, -1, 0, manager); + } else if (e->mask & IN_IGNORED) + udev_watch_end(manager->udev, dev); + } + + return 1; +} + +static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *manager = userdata; + + assert(manager); + + manager_exit(manager); + + return 1; +} + +static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *manager = userdata; + + assert(manager); + + manager_reload(manager); + + return 1; +} + +static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Manager *manager = userdata; + + assert(manager); + + for (;;) { + pid_t pid; + int status; + struct worker *worker; + + pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + + worker = hashmap_get(manager->workers, PID_TO_PTR(pid)); + if (!worker) { + log_warning("worker ["PID_FMT"] is unknown, ignoring", pid); + continue; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) + log_debug("worker ["PID_FMT"] exited", pid); + else + log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + } else if (WIFSTOPPED(status)) { + log_info("worker ["PID_FMT"] stopped", pid); + continue; + } else if (WIFCONTINUED(status)) { + log_info("worker ["PID_FMT"] continued", pid); + continue; + } else + log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (worker->event) { + log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath); + /* delete state from disk */ + udev_device_delete_db(worker->event->dev); + udev_device_tag_index(worker->event->dev, NULL, false); + /* forward kernel event without amending it */ + udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel); + } + } + + worker_free(worker); + } + + /* we can start new workers, try to schedule events */ + event_queue_start(manager); + + return 1; +} + +static int on_post(sd_event_source *s, void *userdata) { + Manager *manager = userdata; + int r; + + assert(manager); + + if (udev_list_node_is_empty(&manager->events)) { + /* no pending events */ + if (!hashmap_isempty(manager->workers)) { + /* there are idle workers */ + log_debug("cleanup idle workers"); + manager_kill_workers(manager); + } else { + /* we are idle */ + if (manager->exit) { + r = sd_event_exit(manager->event, 0); + if (r < 0) + return r; + } else if (manager->cgroup) + /* cleanup possible left-over processes in our cgroup */ + cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL); + } + } + + return 1; +} + +static int listen_fds(int *rctrl, int *rnetlink) { + _cleanup_udev_unref_ struct udev *udev = NULL; + int ctrl_fd = -1, netlink_fd = -1; + int fd, n, r; + + assert(rctrl); + assert(rnetlink); + + n = sd_listen_fds(true); + if (n < 0) + return n; + + for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { + if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { + if (ctrl_fd >= 0) + return -EINVAL; + ctrl_fd = fd; + continue; + } + + if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { + if (netlink_fd >= 0) + return -EINVAL; + netlink_fd = fd; + continue; + } + + return -EINVAL; + } + + if (ctrl_fd < 0) { + _cleanup_udev_ctrl_unref_ struct udev_ctrl *ctrl = NULL; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + ctrl = udev_ctrl_new(udev); + if (!ctrl) + return log_error_errno(EINVAL, "error initializing udev control socket"); + + r = udev_ctrl_enable_receiving(ctrl); + if (r < 0) + return log_error_errno(EINVAL, "error binding udev control socket"); + + fd = udev_ctrl_get_fd(ctrl); + if (fd < 0) + return log_error_errno(EIO, "could not get ctrl fd"); + + ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (ctrl_fd < 0) + return log_error_errno(errno, "could not dup ctrl fd: %m"); + } + + if (netlink_fd < 0) { + _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; + + if (!udev) { + udev = udev_new(); + if (!udev) + return -ENOMEM; + } + + monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (!monitor) + return log_error_errno(EINVAL, "error initializing netlink socket"); + + (void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024); + + r = udev_monitor_enable_receiving(monitor); + if (r < 0) + return log_error_errno(EINVAL, "error binding netlink socket"); + + fd = udev_monitor_get_fd(monitor); + if (fd < 0) + return log_error_errno(netlink_fd, "could not get uevent fd: %m"); + + netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (ctrl_fd < 0) + return log_error_errno(errno, "could not dup netlink fd: %m"); + } + + *rctrl = ctrl_fd; + *rnetlink = netlink_fd; + + return 0; +} + +/* + * read the kernel command line, in case we need to get into debug mode + * udev.log-priority= syslog priority + * udev.children-max= events are fully serialized if set to 1 + * udev.exec-delay= delay execution of every executed program + * udev.event-timeout= seconds to wait before terminating an event + */ +static int parse_proc_cmdline_item(const char *key, const char *value) { + const char *full_key = key; + int r; + + assert(key); + + if (!value) + return 0; + + if (startswith(key, "rd.")) + key += strlen("rd."); + + if (startswith(key, "udev.")) + key += strlen("udev."); + else + return 0; + + if (streq(key, "log-priority")) { + int prio; + + prio = util_log_priority(value); + if (prio < 0) + goto invalid; + log_set_max_level(prio); + } else if (streq(key, "children-max")) { + r = safe_atou(value, &arg_children_max); + if (r < 0) + goto invalid; + } else if (streq(key, "exec-delay")) { + r = safe_atoi(value, &arg_exec_delay); + if (r < 0) + goto invalid; + } else if (streq(key, "event-timeout")) { + r = safe_atou64(value, &arg_event_timeout_usec); + if (r < 0) + goto invalid; + arg_event_timeout_usec *= USEC_PER_SEC; + arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; + } + + return 0; +invalid: + log_warning("invalid %s ignored: %s", full_key, value); + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Manages devices.\n\n" + " -h --help Print this message\n" + " --version Print version of the program\n" + " --daemon Detach and run in the background\n" + " --debug Enable debug output\n" + " --children-max=INT Set maximum number of workers\n" + " --exec-delay=SECONDS Seconds to wait before executing RUN=\n" + " --event-timeout=SECONDS Seconds to wait before terminating an event\n" + " --resolve-names=early|late|never\n" + " When to resolve users and groups\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + static const struct option options[] = { + { "daemon", no_argument, NULL, 'd' }, + { "debug", no_argument, NULL, 'D' }, + { "children-max", required_argument, NULL, 'c' }, + { "exec-delay", required_argument, NULL, 'e' }, + { "event-timeout", required_argument, NULL, 't' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { + int r; + + switch (c) { + + case 'd': + arg_daemonize = true; + break; + case 'c': + r = safe_atou(optarg, &arg_children_max); + if (r < 0) + log_warning("Invalid --children-max ignored: %s", optarg); + break; + case 'e': + r = safe_atoi(optarg, &arg_exec_delay); + if (r < 0) + log_warning("Invalid --exec-delay ignored: %s", optarg); + break; + case 't': + r = safe_atou64(optarg, &arg_event_timeout_usec); + if (r < 0) + log_warning("Invalid --event-timeout ignored: %s", optarg); + else { + arg_event_timeout_usec *= USEC_PER_SEC; + arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; + } + break; + case 'D': + arg_debug = true; + break; + case 'N': + if (streq(optarg, "early")) { + arg_resolve_names = 1; + } else if (streq(optarg, "late")) { + arg_resolve_names = 0; + } else if (streq(optarg, "never")) { + arg_resolve_names = -1; + } else { + log_error("resolve-names must be early, late or never"); + return 0; + } + break; + case 'h': + help(); + return 0; + case 'V': + printf("%s\n", VERSION); + return 0; + case '?': + return -EINVAL; + default: + assert_not_reached("Unhandled option"); + + } + } + + return 1; +} + +static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) { + _cleanup_(manager_freep) Manager *manager = NULL; + int r, fd_worker, one = 1; + + assert(ret); + assert(fd_ctrl >= 0); + assert(fd_uevent >= 0); + + manager = new0(Manager, 1); + if (!manager) + return log_oom(); + + manager->fd_inotify = -1; + manager->worker_watch[WRITE_END] = -1; + manager->worker_watch[READ_END] = -1; + + manager->udev = udev_new(); + if (!manager->udev) + return log_error_errno(errno, "could not allocate udev context: %m"); + + udev_builtin_init(manager->udev); + + manager->rules = udev_rules_new(manager->udev, arg_resolve_names); + if (!manager->rules) + return log_error_errno(ENOMEM, "error reading rules"); + + udev_list_node_init(&manager->events); + udev_list_init(manager->udev, &manager->properties, true); + + manager->cgroup = cgroup; + + manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl); + if (!manager->ctrl) + return log_error_errno(EINVAL, "error taking over udev control socket"); + + manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent); + if (!manager->monitor) + return log_error_errno(EINVAL, "error taking over netlink socket"); + + /* unnamed socket from workers to the main daemon */ + r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch); + if (r < 0) + return log_error_errno(errno, "error creating socketpair: %m"); + + fd_worker = manager->worker_watch[READ_END]; + + r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "could not enable SO_PASSCRED: %m"); + + manager->fd_inotify = udev_watch_init(manager->udev); + if (manager->fd_inotify < 0) + return log_error_errno(ENOMEM, "error initializing inotify"); + + udev_watch_restore(manager->udev); + + /* block and listen to all signals on signalfd */ + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0); + + r = sd_event_default(&manager->event); + if (r < 0) + 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) + return log_error_errno(r, "error creating sigint event source: %m"); + + r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager); + if (r < 0) + return log_error_errno(r, "error creating sigterm event source: %m"); + + r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager); + if (r < 0) + return log_error_errno(r, "error creating sighup event source: %m"); + + r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager); + if (r < 0) + return log_error_errno(r, "error creating sigchld event source: %m"); + + r = sd_event_set_watchdog(manager->event, true); + if (r < 0) + return log_error_errno(r, "error creating watchdog event source: %m"); + + r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager); + if (r < 0) + return log_error_errno(r, "error creating ctrl event source: %m"); + + /* This needs to be after the inotify and uevent handling, to make sure + * that the ping is send back after fully processing the pending uevents + * (including the synthetic ones we may create due to inotify events). + */ + r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m"); + + r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager); + if (r < 0) + return log_error_errno(r, "error creating inotify event source: %m"); + + r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager); + if (r < 0) + return log_error_errno(r, "error creating uevent event source: %m"); + + r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager); + if (r < 0) + return log_error_errno(r, "error creating worker event source: %m"); + + r = sd_event_add_post(manager->event, NULL, on_post, manager); + if (r < 0) + return log_error_errno(r, "error creating post event source: %m"); + + *ret = manager; + manager = NULL; + + return 0; +} + +static int run(int fd_ctrl, int fd_uevent, const char *cgroup) { + _cleanup_(manager_freep) Manager *manager = NULL; + int r; + + r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup); + if (r < 0) { + r = log_error_errno(r, "failed to allocate manager object: %m"); + goto exit; + } + + r = udev_rules_apply_static_dev_perms(manager->rules); + if (r < 0) + log_error_errno(r, "failed to apply permissions on static device nodes: %m"); + + (void) sd_notify(false, + "READY=1\n" + "STATUS=Processing..."); + + r = sd_event_loop(manager->event); + if (r < 0) { + log_error_errno(r, "event loop failed: %m"); + goto exit; + } + + sd_event_get_exit_code(manager->event, &r); + +exit: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + if (manager) + udev_ctrl_cleanup(manager->ctrl); + return r; +} + +int main(int argc, char *argv[]) { + _cleanup_free_ char *cgroup = NULL; + int fd_ctrl = -1, fd_uevent = -1; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto exit; + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "failed to parse kernel command line, ignoring: %m"); + + if (arg_debug) { + log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_DEBUG); + } + + if (getuid() != 0) { + r = log_error_errno(EPERM, "root privileges required"); + goto exit; + } + + if (arg_children_max == 0) { + cpu_set_t cpu_set; + + arg_children_max = 8; + + if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0) + arg_children_max += CPU_COUNT(&cpu_set) * 2; + + log_debug("set children_max to %u", arg_children_max); + } + + /* set umask before creating any file/directory */ + r = chdir("/"); + if (r < 0) { + r = log_error_errno(errno, "could not change dir to /: %m"); + goto exit; + } + + umask(022); + + r = mac_selinux_init(); + if (r < 0) { + log_error_errno(r, "could not initialize labelling: %m"); + goto exit; + } + + r = mkdir("/run/udev", 0755); + if (r < 0 && errno != EEXIST) { + r = log_error_errno(errno, "could not create /run/udev: %m"); + goto exit; + } + + dev_setup(NULL, UID_INVALID, GID_INVALID); + + if (getppid() == 1) { + /* get our own cgroup, we regularly kill everything udev has left behind + we only do this on systemd systems, and only if we are directly spawned + by PID1. otherwise we are not guaranteed to have a dedicated cgroup */ + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); + if (r < 0) { + if (r == -ENOENT || r == -ENOMEDIUM) + log_debug_errno(r, "did not find dedicated cgroup: %m"); + else + log_warning_errno(r, "failed to get cgroup: %m"); + } + } + + r = listen_fds(&fd_ctrl, &fd_uevent); + if (r < 0) { + r = log_error_errno(r, "could not listen on fds: %m"); + goto exit; + } + + if (arg_daemonize) { + pid_t pid; + + log_info("starting version " VERSION); + + /* connect /dev/null to stdin, stdout, stderr */ + if (log_get_max_level() < LOG_DEBUG) + (void) make_null_stdio(); + + pid = fork(); + switch (pid) { + case 0: + break; + case -1: + r = log_error_errno(errno, "fork of daemon failed: %m"); + goto exit; + default: + mac_selinux_finish(); + log_close(); + _exit(EXIT_SUCCESS); + } + + setsid(); + + write_string_file("/proc/self/oom_score_adj", "-1000", 0); + } + + r = run(fd_ctrl, fd_uevent, cgroup); + +exit: + mac_selinux_finish(); + log_close(); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-udev/udev.pc.in b/src/grp-udev/udev.pc.in new file mode 100644 index 0000000000..a0c2e82d47 --- /dev/null +++ b/src/grp-udev/udev.pc.in @@ -0,0 +1,5 @@ +Name: udev +Description: udev +Version: @VERSION@ + +udevdir=@udevlibexecdir@ diff --git a/src/grp-udev/udev.xml b/src/grp-udev/udev.xml new file mode 100644 index 0000000000..dd5563605c --- /dev/null +++ b/src/grp-udev/udev.xml @@ -0,0 +1,755 @@ + + + + + + udev + systemd + + + Developer + Greg + Kroah-Hartmann + greg@kroah.com + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + udev + 7 + + + + udev + Dynamic device management + + + Description + udev supplies the system software with device events, manages permissions + of device nodes and may create additional symlinks in the /dev + directory, or renames network interfaces. The kernel usually just assigns unpredictable + device names based on the order of discovery. Meaningful symlinks or network device + names provide a way to reliably identify devices based on their properties or + current configuration. + + The udev daemon, systemd-udevd.service + 8, receives device uevents directly from + the kernel whenever a device is added or removed from the system, or it changes its + state. When udev receives a device event, it matches its configured set of rules + against various device attributes to identify the device. Rules that match may + provide additional device information to be stored in the udev database or + to be used to create meaningful symlink names. + + All device information udev processes is stored in the udev database and + sent out to possible event subscribers. Access to all stored data and the event + sources is provided by the library libudev. + + + Rules Files + The udev rules are read from the files located in the + system rules directory /usr/lib/udev/rules.d, + the volatile runtime directory /run/udev/rules.d + and the local administration directory /etc/udev/rules.d. + All rules files are collectively sorted and processed in lexical order, + regardless of the directories in which they live. However, files with + identical filenames replace each other. Files in /etc + have the highest priority, files in /run take precedence + over files with the same name in /usr/lib. This can be + used to override a system-supplied rules file with a local file if needed; + a symlink in /etc with the same name as a rules file in + /usr/lib, pointing to /dev/null, + disables the rules file entirely. Rule files must have the extension + .rules; other extensions are ignored. + + Every line in the rules file contains at least one key-value pair. + Except for empty lines or lines beginning with #, which are ignored. + There are two kinds of keys: match and assignment. + If all match keys match against their values, the rule gets applied and the + assignment keys get the specified values assigned. + + A matching rule may rename a network interface, add symlinks + pointing to the device node, or run a specified program as part of + the event handling. + + A rule consists of a comma-separated list of one or more key-value pairs. + Each key has a distinct operation, depending on the used operator. Valid + operators are: + + + == + + Compare for equality. + + + + + != + + Compare for inequality. + + + + + = + + Assign a value to a key. Keys that represent a list are reset + and only this single value is assigned. + + + + + += + + Add the value to a key that holds a list of entries. + + + + + -= + + Remove the value from a key that holds a list of entries. + + + + + := + + Assign a value to a key finally; disallow any later changes. + + + + + The following key names can be used to match against device properties. + Some of the keys also match against properties of the parent devices in sysfs, + not only the device that has generated the event. If multiple keys that match + a parent device are specified in a single rule, all these keys must match at + one and the same parent device. + + + ACTION + + Match the name of the event action. + + + + + DEVPATH + + Match the devpath of the event device. + + + + + KERNEL + + Match the name of the event device. + + + + + NAME + + Match the name of a network interface. It can be used once the + NAME key has been set in one of the preceding rules. + + + + + SYMLINK + + Match the name of a symlink targeting the node. It can + be used once a SYMLINK key has been set in one of the preceding + rules. There may be multiple symlinks; only one needs to match. + + + + + + SUBSYSTEM + + Match the subsystem of the event device. + + + + DRIVER + + Match the driver name of the event device. Only set this key for devices + which are bound to a driver at the time the event is generated. + + + + ATTR{filename} + + Match sysfs attribute values of the event device. Trailing + whitespace in the attribute values is ignored unless the specified match + value itself contains trailing whitespace. + + + SYSCTL{kernel parameter} + + Match a kernel parameter value. + + + + + + KERNELS + + Search the devpath upwards for a matching device name. + + + + + SUBSYSTEMS + + Search the devpath upwards for a matching device subsystem name. + + + + + DRIVERS + + Search the devpath upwards for a matching device driver name. + + + + + ATTRS{filename} + + Search the devpath upwards for a device with matching sysfs attribute values. + If multiple ATTRS matches are specified, all of them + must match on the same device. Trailing whitespace in the attribute values is ignored + unless the specified match value itself contains trailing whitespace. + + + + + TAGS + + Search the devpath upwards for a device with matching tag. + + + + + ENV{key} + + Match against a device property value. + + + + + TAG + + Match against a device tag. + + + + + TEST{octal mode mask} + + Test the existence of a file. An octal mode mask can be specified + if needed. + + + + + PROGRAM + + Execute a program to determine whether there + is a match; the key is true if the program returns + successfully. The device properties are made available to the + executed program in the environment. The program's standard output + is available in the RESULT key. + This can only be used for very short-running foreground tasks. For details, + see RUN. + + + + + RESULT + + Match the returned string of the last PROGRAM call. + This key can be used in the same or in any later rule after a + PROGRAM call. + + + + + Most of the fields support shell glob pattern matching and + alternate patterns. The following special characters are supported: + + + * + + Matches zero or more characters. + + + + ? + + Matches any single character. + + + + [] + + Matches any single character specified within the brackets. For + example, the pattern string tty[SR] + would match either ttyS or ttyR. + Ranges are also supported via the - character. + For example, to match on the range of all digits, the pattern + [0-9] could be used. If the first character + following the [ is a !, + any characters not enclosed are matched. + + + + | + + Separates alternative patterns. For example, the pattern string + abc|x* would match either abc + or x*. + + + + + The following keys can get values assigned: + + + NAME + + The name to use for a network interface. See + systemd.link5 + for a higher-level mechanism for setting the interface name. + The name of a device node cannot be changed by udev, only additional + symlinks can be created. + + + + + SYMLINK + + The name of a symlink targeting the node. Every matching rule adds + this value to the list of symlinks to be created. + The set of characters to name a symlink is limited. Allowed + characters are 0-9A-Za-z#+-.:=@_/, valid UTF-8 character + sequences, and \x00 hex encoding. All other + characters are replaced by a _ character. + Multiple symlinks may be specified by separating the names by the + space character. In case multiple devices claim the same name, the link + always points to the device with the highest link_priority. If the current + device goes away, the links are re-evaluated and the device with the + next highest link_priority becomes the owner of the link. If no + link_priority is specified, the order of the devices (and which one of + them owns the link) is undefined. + Symlink names must never conflict with the kernel's default device + node names, as that would result in unpredictable behavior. + + + + + + OWNER, GROUP, MODE + + The permissions for the device node. Every specified value overrides + the compiled-in default value. + + + + + SECLABEL{module} + + Applies the specified Linux Security Module label to the device node. + + + + + ATTR{key} + + The value that should be written to a sysfs attribute of the + event device. + + + + + SYSCTL{kernel parameter} + + The value that should be written to kernel parameter. + + + + + ENV{key} + + Set a device property value. Property names with a leading . + are neither stored in the database nor exported to events or + external tools (run by, for example, the PROGRAM + match key). + + + + + TAG + + Attach a tag to a device. This is used to filter events for users + of libudev's monitor functionality, or to enumerate a group of tagged + devices. The implementation can only work efficiently if only a few + tags are attached to a device. It is only meant to be used in + contexts with specific device filter requirements, and not as a + general-purpose flag. Excessive use might result in inefficient event + handling. + + + + + RUN{type} + + Add a program to the list of programs to be executed after + processing all the rules for a specific event, depending on + type: + + + program + + Execute an external program specified as the assigned + value. If no absolute path is given, the program is expected + to live in /usr/lib/udev; otherwise, the + absolute path must be specified. + This is the default if no type + is specified. + + + + builtin + + As program, but use one of the + built-in programs rather than an external one. + + + + The program name and following arguments are separated by spaces. + Single quotes can be used to specify arguments with spaces. + This can only be used for very short-running foreground tasks. Running an + event process for a long period of time may block all further events for + this or a dependent device. + Starting daemons or other long-running processes is not appropriate + for udev; the forked processes, detached or not, will be unconditionally + killed after the event handling has finished. + + + + + LABEL + + A named label to which a GOTO may jump. + + + + + GOTO + + Jumps to the next LABEL with a matching name. + + + + + IMPORT{type} + + Import a set of variables as device properties, + depending on type: + + + program + + Execute an external program specified as the assigned + value and, if it returns successfully, + import its output, which must be in environment key + format. Path specification, command/argument separation, + and quoting work like in RUN. + + + + builtin + + Similar to program, but use one of the + built-in programs rather than an external one. + + + + file + + Import a text file specified as the assigned value, the content + of which must be in environment key format. + + + + db + + Import a single property specified as the assigned value from the + current device database. This works only if the database is already populated + by an earlier event. + + + + cmdline + + Import a single property from the kernel command line. For simple flags + the value of the property is set to 1. + + + + parent + + Import the stored keys from the parent device by reading + the database entry of the parent device. The value assigned to + is used as a filter of key names + to import (with the same shell glob pattern matching used for + comparisons). + + + + This can only be used for very short-running foreground tasks. For details + see . + + + + + OPTIONS + + Rule and device options: + + + + + Specify the priority of the created symlinks. Devices with higher + priorities overwrite existing symlinks of other devices. The default is 0. + + + + + + Usually, control and other possibly unsafe characters are replaced + in strings used for device naming. The mode of replacement can be specified + with this option. + + + + + + Apply the permissions specified in this rule to the + static device node with the specified name. Also, for every + tag specified in this rule, create a symlink + in the directory + /run/udev/static_node-tags/tag + pointing at the static device node with the specified name. + Static device node creation is performed by systemd-tmpfiles + before systemd-udevd is started. The static nodes might not + have a corresponding kernel device; they are used to trigger + automatic kernel module loading when they are accessed. + + + + + + Watch the device node with inotify; when the node is + closed after being opened for writing, a change uevent is + synthesized. + + + + + + Disable the watching of a device node with inotify. + + + + + + + + The NAME, SYMLINK, + PROGRAM, OWNER, + GROUP, MODE, and + RUN fields support simple string substitutions. + The RUN substitutions are performed after all rules + have been processed, right before the program is executed, allowing for + the use of device properties set by earlier matching rules. For all other + fields, substitutions are performed while the individual rule is being + processed. The available substitutions are: + + + , + + The kernel name for this device. + + + + + , + + The kernel number for this device. For example, + sda3 has kernel number 3. + + + + + + , + + The devpath of the device. + + + + + , + + The name of the device matched while searching the devpath + upwards for , , + , and . + + + + + + + + The driver name of the device matched while searching the + devpath upwards for , + , , and + . + + + + + + , + + The value of a sysfs attribute found at the device where + all keys of the rule have matched. If the matching device does not + have such an attribute, and a previous , + , , or + test selected a parent device, then the + attribute from that parent device is used. + + If the attribute is a symlink, the last element of the + symlink target is returned as the value. + + + + + + , + + A device property value. + + + + + , + + The kernel major number for the device. + + + + + , + + The kernel minor number for the device. + + + + + , + + The string returned by the external program requested with + PROGRAM. + A single part of the string, separated by a space character, may be selected + by specifying the part number as an attribute: %c{N}. + If the number is followed by the + character, this part plus all remaining parts + of the result string are substituted: %c{N+}. + + + + + , + + The node name of the parent device. + + + + + + + The current name of the device. If not changed by a rule, it is the + name of the kernel device. + + + + + + + A space-separated list of the current symlinks. The value is + only set during a remove event or if an earlier rule assigned a value. + + + + + , + + The udev_root value. + + + + + , + + The sysfs mount point. + + + + + , + + The name of the device node. + + + + + + + The % character itself. + + + + + + + The $ character itself. + + + + + + + See Also + + + systemd-udevd.service8 + , + + udevadm8 + , + + systemd.link5 + + + + diff --git a/src/grp-udev/udevadm/Makefile b/src/grp-udev/udevadm/Makefile new file mode 100644 index 0000000000..1638572ffc --- /dev/null +++ b/src/grp-udev/udevadm/Makefile @@ -0,0 +1,46 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += \ + udevadm + +udevadm_SOURCES = \ + src/udev/udevadm.c \ + src/udev/udevadm-info.c \ + src/udev/udevadm-control.c \ + src/udev/udevadm-monitor.c \ + src/udev/udevadm-hwdb.c \ + src/udev/udevadm-settle.c \ + src/udev/udevadm-trigger.c \ + src/udev/udevadm-test.c \ + src/udev/udevadm-test-builtin.c \ + src/udev/udevadm-util.c \ + src/udev/udevadm-util.h + +udevadm_LDADD = \ + libudev-core.la \ + libbasic.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/udevadm/udevadm-control.c b/src/grp-udev/udevadm/udevadm-control.c new file mode 100644 index 0000000000..8aaef5fa14 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-control.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "shared/udev-util.h" +#include "udev.h" + +static void print_help(void) { + printf("%s control COMMAND\n\n" + "Control the udev daemon.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -e --exit Instruct the daemon to cleanup and exit\n" + " -l --log-priority=LEVEL Set the udev log level for the daemon\n" + " -s --stop-exec-queue Do not execute events, queue only\n" + " -S --start-exec-queue Execute events, flush queue\n" + " -R --reload Reload rules and databases\n" + " -p --property=KEY=VALUE Set a global property for all events\n" + " -m --children-max=N Maximum number of children\n" + " --timeout=SECONDS Maximum time to block for a reply\n" + , program_invocation_short_name); +} + +static int adm_control(struct udev *udev, int argc, char *argv[]) { + _cleanup_udev_ctrl_unref_ struct udev_ctrl *uctrl = NULL; + int timeout = 60; + int rc = 1, c; + + static const struct option options[] = { + { "exit", no_argument, NULL, 'e' }, + { "log-priority", required_argument, NULL, 'l' }, + { "stop-exec-queue", no_argument, NULL, 's' }, + { "start-exec-queue", no_argument, NULL, 'S' }, + { "reload", no_argument, NULL, 'R' }, + { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */ + { "property", required_argument, NULL, 'p' }, + { "env", required_argument, NULL, 'p' }, /* alias for -p */ + { "children-max", required_argument, NULL, 'm' }, + { "timeout", required_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + return 1; + } + + uctrl = udev_ctrl_new(udev); + if (uctrl == NULL) + return 2; + + while ((c = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL)) >= 0) + switch (c) { + case 'e': + if (udev_ctrl_send_exit(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'l': { + int i; + + i = util_log_priority(optarg); + if (i < 0) { + fprintf(stderr, "invalid number '%s'\n", optarg); + return rc; + } + if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) + rc = 2; + else + rc = 0; + break; + } + case 's': + if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'S': + if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'R': + if (udev_ctrl_send_reload(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'p': + if (strchr(optarg, '=') == NULL) { + fprintf(stderr, "expect = instead of '%s'\n", optarg); + return rc; + } + if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'm': { + char *endp; + int i; + + i = strtoul(optarg, &endp, 0); + if (endp[0] != '\0' || i < 1) { + fprintf(stderr, "invalid number '%s'\n", optarg); + return rc; + } + if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) + rc = 2; + else + rc = 0; + break; + } + case 't': { + int seconds; + + seconds = atoi(optarg); + if (seconds >= 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + break; + } + case 'h': + print_help(); + rc = 0; + break; + } + + if (optind < argc) + fprintf(stderr, "Extraneous argument: %s\n", argv[optind]); + else if (optind == 1) + fprintf(stderr, "Option missing\n"); + return rc; +} + +const struct udevadm_cmd udevadm_control = { + .name = "control", + .cmd = adm_control, + .help = "Control the udev daemon", +}; diff --git a/src/grp-udev/udevadm/udevadm-hwdb.c b/src/grp-udev/udevadm/udevadm-hwdb.c new file mode 100644 index 0000000000..2b5444f439 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-hwdb.c @@ -0,0 +1,698 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/strbuf.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-hwdb/hwdb-internal.h" +#include "sd-hwdb/hwdb-util.h" +#include "udev.h" + +/* + * Generic udev properties, key/value database based on modalias strings. + * Uses a Patricia/radix trie to index all matches for efficient lookup. + */ + +static const char * const conf_file_dirs[] = { + "/etc/udev/hwdb.d", + UDEVLIBEXECDIR "/hwdb.d", + NULL +}; + +/* in-memory trie objects */ +struct trie { + struct trie_node *root; + struct strbuf *strings; + + size_t nodes_count; + size_t children_count; + size_t values_count; +}; + +struct trie_node { + /* prefix, common part for all children of this node */ + size_t prefix_off; + + /* sorted array of pointers to children nodes */ + struct trie_child_entry *children; + uint8_t children_count; + + /* sorted array of key/value pairs */ + struct trie_value_entry *values; + size_t values_count; +}; + +/* children array item with char (0-255) index */ +struct trie_child_entry { + uint8_t c; + struct trie_node *child; +}; + +/* value array item with key/value pairs */ +struct trie_value_entry { + size_t key_off; + size_t value_off; +}; + +static int trie_children_cmp(const void *v1, const void *v2) { + const struct trie_child_entry *n1 = v1; + const struct trie_child_entry *n2 = v2; + + return n1->c - n2->c; +} + +static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) { + struct trie_child_entry *child; + + /* extend array, add new entry, sort for bisection */ + child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry)); + if (!child) + return -ENOMEM; + + node->children = child; + trie->children_count++; + node->children[node->children_count].c = c; + node->children[node->children_count].child = node_child; + node->children_count++; + qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); + trie->nodes_count++; + + return 0; +} + +static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) { + struct trie_child_entry *child; + struct trie_child_entry search; + + search.c = c; + child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); + if (child) + return child->child; + return NULL; +} + +static void trie_node_cleanup(struct trie_node *node) { + size_t i; + + for (i = 0; i < node->children_count; i++) + trie_node_cleanup(node->children[i].child); + free(node->children); + free(node->values); + free(node); +} + +static int trie_values_cmp(const void *v1, const void *v2, void *arg) { + const struct trie_value_entry *val1 = v1; + const struct trie_value_entry *val2 = v2; + struct trie *trie = arg; + + return strcmp(trie->strings->buf + val1->key_off, + trie->strings->buf + val2->key_off); +} + +static int trie_node_add_value(struct trie *trie, struct trie_node *node, + const char *key, const char *value) { + ssize_t k, v; + struct trie_value_entry *val; + + k = strbuf_add_string(trie->strings, key, strlen(key)); + if (k < 0) + return k; + v = strbuf_add_string(trie->strings, value, strlen(value)); + if (v < 0) + return v; + + if (node->values_count) { + struct trie_value_entry search = { + .key_off = k, + .value_off = v, + }; + + val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); + if (val) { + /* replace existing earlier key with new value */ + val->value_off = v; + return 0; + } + } + + /* extend array, add new entry, sort for bisection */ + val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry)); + if (!val) + return -ENOMEM; + trie->values_count++; + node->values = val; + node->values[node->values_count].key_off = k; + node->values[node->values_count].value_off = v; + node->values_count++; + qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); + return 0; +} + +static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, + const char *key, const char *value) { + size_t i = 0; + int err = 0; + + for (;;) { + size_t p; + uint8_t c; + struct trie_node *child; + + for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) { + _cleanup_free_ char *s = NULL; + ssize_t off; + _cleanup_free_ struct trie_node *new_child = NULL; + + if (c == search[i + p]) + continue; + + /* split node */ + new_child = new0(struct trie_node, 1); + if (!new_child) + return -ENOMEM; + + /* move values from parent to child */ + new_child->prefix_off = node->prefix_off + p+1; + new_child->children = node->children; + new_child->children_count = node->children_count; + new_child->values = node->values; + new_child->values_count = node->values_count; + + /* update parent; use strdup() because the source gets realloc()d */ + s = strndup(trie->strings->buf + node->prefix_off, p); + if (!s) + return -ENOMEM; + + off = strbuf_add_string(trie->strings, s, p); + if (off < 0) + return off; + + node->prefix_off = off; + node->children = NULL; + node->children_count = 0; + node->values = NULL; + node->values_count = 0; + err = node_add_child(trie, node, new_child, c); + if (err) + return err; + + new_child = NULL; /* avoid cleanup */ + break; + } + i += p; + + c = search[i]; + if (c == '\0') + return trie_node_add_value(trie, node, key, value); + + child = node_lookup(node, c); + if (!child) { + ssize_t off; + + /* new child */ + child = new0(struct trie_node, 1); + if (!child) + return -ENOMEM; + + off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1)); + if (off < 0) { + free(child); + return off; + } + + child->prefix_off = off; + err = node_add_child(trie, node, child, c); + if (err) { + free(child); + return err; + } + + return trie_node_add_value(trie, child, key, value); + } + + node = child; + i++; + } +} + +struct trie_f { + FILE *f; + struct trie *trie; + uint64_t strings_off; + + uint64_t nodes_count; + uint64_t children_count; + uint64_t values_count; +}; + +/* calculate the storage space for the nodes, children arrays, value arrays */ +static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { + uint64_t i; + + for (i = 0; i < node->children_count; i++) + trie_store_nodes_size(trie, node->children[i].child); + + trie->strings_off += sizeof(struct trie_node_f); + for (i = 0; i < node->children_count; i++) + trie->strings_off += sizeof(struct trie_child_entry_f); + for (i = 0; i < node->values_count; i++) + trie->strings_off += sizeof(struct trie_value_entry_f); +} + +static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { + uint64_t i; + struct trie_node_f n = { + .prefix_off = htole64(trie->strings_off + node->prefix_off), + .children_count = node->children_count, + .values_count = htole64(node->values_count), + }; + struct trie_child_entry_f *children = NULL; + int64_t node_off; + + if (node->children_count) { + children = new0(struct trie_child_entry_f, node->children_count); + if (!children) + return -ENOMEM; + } + + /* post-order recursion */ + for (i = 0; i < node->children_count; i++) { + int64_t child_off; + + child_off = trie_store_nodes(trie, node->children[i].child); + if (child_off < 0) { + free(children); + return child_off; + } + children[i].c = node->children[i].c; + children[i].child_off = htole64(child_off); + } + + /* write node */ + node_off = ftello(trie->f); + fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); + trie->nodes_count++; + + /* append children array */ + if (node->children_count) { + fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); + trie->children_count += node->children_count; + free(children); + } + + /* append values array */ + for (i = 0; i < node->values_count; i++) { + struct trie_value_entry_f v = { + .key_off = htole64(trie->strings_off + node->values[i].key_off), + .value_off = htole64(trie->strings_off + node->values[i].value_off), + }; + + fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); + trie->values_count++; + } + + return node_off; +} + +static int trie_store(struct trie *trie, const char *filename) { + struct trie_f t = { + .trie = trie, + }; + _cleanup_free_ char *filename_tmp = NULL; + int64_t pos; + int64_t root_off; + int64_t size; + struct trie_header_f h = { + .signature = HWDB_SIG, + .tool_version = htole64(atoi(VERSION)), + .header_size = htole64(sizeof(struct trie_header_f)), + .node_size = htole64(sizeof(struct trie_node_f)), + .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), + .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), + }; + int err; + + /* calculate size of header, nodes, children entries, value entries */ + t.strings_off = sizeof(struct trie_header_f); + trie_store_nodes_size(&t, trie->root); + + err = fopen_temporary(filename , &t.f, &filename_tmp); + if (err < 0) + return err; + fchmod(fileno(t.f), 0444); + + /* write nodes */ + err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } + root_off = trie_store_nodes(&t, trie->root); + h.nodes_root_off = htole64(root_off); + pos = ftello(t.f); + h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); + + /* write string buffer */ + fwrite(trie->strings->buf, trie->strings->len, 1, t.f); + h.strings_len = htole64(trie->strings->len); + + /* write header */ + size = ftello(t.f); + h.file_size = htole64(size); + err = fseeko(t.f, 0, SEEK_SET); + if (err < 0) { + fclose(t.f); + unlink_noerrno(filename_tmp); + return -errno; + } + fwrite(&h, sizeof(struct trie_header_f), 1, t.f); + err = ferror(t.f); + if (err) + err = -errno; + fclose(t.f); + if (err < 0 || rename(filename_tmp, filename) < 0) { + unlink_noerrno(filename_tmp); + return err < 0 ? err : -errno; + } + + log_debug("=== trie on-disk ==="); + log_debug("size: %8"PRIi64" bytes", size); + log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); + log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", + t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); + log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.children_count * sizeof(struct trie_child_entry_f), t.children_count); + log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", + t.values_count * sizeof(struct trie_value_entry_f), t.values_count); + log_debug("string store: %8zu bytes", trie->strings->len); + log_debug("strings start: %8"PRIu64, t.strings_off); + + return 0; +} + +static int insert_data(struct trie *trie, struct udev_list *match_list, + char *line, const char *filename) { + char *value; + struct udev_list_entry *entry; + + value = strchr(line, '='); + if (!value) { + log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); + return -EINVAL; + } + + value[0] = '\0'; + value++; + + /* libudev requires properties to start with a space */ + while (isblank(line[0]) && isblank(line[1])) + line++; + + if (line[0] == '\0' || value[0] == '\0') { + log_error("Error, empty key or value '%s' in '%s':", line, filename); + return -EINVAL; + } + + udev_list_entry_foreach(entry, udev_list_get_entry(match_list)) + trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value); + + return 0; +} + +static int import_file(struct udev *udev, struct trie *trie, const char *filename) { + enum { + HW_MATCH, + HW_DATA, + HW_NONE, + } state = HW_NONE; + FILE *f; + char line[LINE_MAX]; + struct udev_list match_list; + + udev_list_init(udev, &match_list, false); + + f = fopen(filename, "re"); + if (f == NULL) + return -errno; + + while (fgets(line, sizeof(line), f)) { + size_t len; + char *pos; + + /* comment line */ + if (line[0] == '#') + continue; + + /* strip trailing comment */ + pos = strchr(line, '#'); + if (pos) + pos[0] = '\0'; + + /* strip trailing whitespace */ + len = strlen(line); + while (len > 0 && isspace(line[len-1])) + len--; + line[len] = '\0'; + + switch (state) { + case HW_NONE: + if (len == 0) + break; + + if (line[0] == ' ') { + log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); + break; + } + + /* start of record, first match */ + state = HW_MATCH; + udev_list_entry_add(&match_list, line, NULL); + break; + + case HW_MATCH: + if (len == 0) { + log_error("Error, DATA expected but got empty line in '%s':", filename); + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } + + /* another match */ + if (line[0] != ' ') { + udev_list_entry_add(&match_list, line, NULL); + break; + } + + /* first data */ + state = HW_DATA; + insert_data(trie, &match_list, line, filename); + break; + + case HW_DATA: + /* end of record */ + if (len == 0) { + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } + + if (line[0] != ' ') { + log_error("Error, DATA expected but got '%s' in '%s':", line, filename); + state = HW_NONE; + udev_list_cleanup(&match_list); + break; + } + + insert_data(trie, &match_list, line, filename); + break; + }; + } + + fclose(f); + udev_list_cleanup(&match_list); + return 0; +} + +static void help(void) { + printf("Usage: udevadm hwdb OPTIONS\n" + " -u,--update update the hardware database\n" + " --usr generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" + " -t,--test=MODALIAS query database and print result\n" + " -r,--root=PATH alternative root path in the filesystem\n" + " -h,--help\n\n"); +} + +static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { + enum { + ARG_USR = 0x100, + }; + + static const struct option options[] = { + { "update", no_argument, NULL, 'u' }, + { "usr", no_argument, NULL, ARG_USR }, + { "test", required_argument, NULL, 't' }, + { "root", required_argument, NULL, 'r' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + const char *test = NULL; + const char *root = ""; + const char *hwdb_bin_dir = "/etc/udev"; + bool update = false; + struct trie *trie = NULL; + int err, c; + int rc = EXIT_SUCCESS; + + while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) + switch(c) { + case 'u': + update = true; + break; + case ARG_USR: + hwdb_bin_dir = UDEVLIBEXECDIR; + break; + case 't': + test = optarg; + break; + case 'r': + root = optarg; + break; + case 'h': + help(); + return EXIT_SUCCESS; + case '?': + return EXIT_FAILURE; + default: + assert_not_reached("Unknown option"); + } + + if (!update && !test) { + log_error("Either --update or --test must be used"); + return EXIT_FAILURE; + } + + if (update) { + char **files, **f; + _cleanup_free_ char *hwdb_bin = NULL; + + trie = new0(struct trie, 1); + if (!trie) { + rc = EXIT_FAILURE; + goto out; + } + + /* string store */ + trie->strings = strbuf_new(); + if (!trie->strings) { + rc = EXIT_FAILURE; + goto out; + } + + /* index */ + trie->root = new0(struct trie_node, 1); + if (!trie->root) { + rc = EXIT_FAILURE; + goto out; + } + trie->nodes_count++; + + err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); + if (err < 0) { + log_error_errno(err, "failed to enumerate hwdb files: %m"); + rc = EXIT_FAILURE; + goto out; + } + STRV_FOREACH(f, files) { + log_debug("reading file '%s'", *f); + import_file(udev, trie, *f); + } + strv_free(files); + + strbuf_complete(trie->strings); + + log_debug("=== trie in-memory ==="); + log_debug("nodes: %8zu bytes (%8zu)", + trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); + log_debug("children arrays: %8zu bytes (%8zu)", + trie->children_count * sizeof(struct trie_child_entry), trie->children_count); + log_debug("values arrays: %8zu bytes (%8zu)", + trie->values_count * sizeof(struct trie_value_entry), trie->values_count); + log_debug("strings: %8zu bytes", + trie->strings->len); + log_debug("strings incoming: %8zu bytes (%8zu)", + trie->strings->in_len, trie->strings->in_count); + log_debug("strings dedup'ed: %8zu bytes (%8zu)", + trie->strings->dedup_len, trie->strings->dedup_count); + + hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin", NULL); + if (!hwdb_bin) { + rc = EXIT_FAILURE; + goto out; + } + + mkdir_parents_label(hwdb_bin, 0755); + + err = trie_store(trie, hwdb_bin); + if (err < 0) { + log_error_errno(err, "Failure writing database %s: %m", hwdb_bin); + rc = EXIT_FAILURE; + } + + label_fix(hwdb_bin, false, false); + } + + if (test) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + int r; + + r = sd_hwdb_new(&hwdb); + if (r >= 0) { + const char *key, *value; + + SD_HWDB_FOREACH_PROPERTY(hwdb, test, key, value) + printf("%s=%s\n", key, value); + } + } +out: + if (trie) { + if (trie->root) + trie_node_cleanup(trie->root); + strbuf_cleanup(trie->strings); + free(trie); + } + return rc; +} + +const struct udevadm_cmd udevadm_hwdb = { + .name = "hwdb", + .cmd = adm_hwdb, +}; diff --git a/src/grp-udev/udevadm/udevadm-info.c b/src/grp-udev/udevadm/udevadm-info.c new file mode 100644 index 0000000000..9746d82aa4 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-info.c @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2004-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/string-util.h" +#include "shared/udev-util.h" +#include "udev.h" + +#include "udevadm-util.h" + +static bool skip_attribute(const char *name) { + static const char* const skip[] = { + "uevent", + "dev", + "modalias", + "resource", + "driver", + "subsystem", + "module", + }; + unsigned int i; + + for (i = 0; i < ELEMENTSOF(skip); i++) + if (streq(name, skip[i])) + return true; + return false; +} + +static void print_all_attributes(struct udev_device *device, const char *key) { + struct udev_list_entry *sysattr; + + udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { + const char *name; + const char *value; + size_t len; + + name = udev_list_entry_get_name(sysattr); + if (skip_attribute(name)) + continue; + + value = udev_device_get_sysattr_value(device, name); + if (value == NULL) + continue; + + /* skip any values that look like a path */ + if (value[0] == '/') + continue; + + /* skip nonprintable attributes */ + len = strlen(value); + while (len > 0 && isprint(value[len-1])) + len--; + if (len > 0) + continue; + + printf(" %s{%s}==\"%s\"\n", key, name, value); + } + printf("\n"); +} + +static int print_device_chain(struct udev_device *device) { + struct udev_device *device_parent; + const char *str; + + printf("\n" + "Udevadm info starts with the device specified by the devpath and then\n" + "walks up the chain of parent devices. It prints for every device\n" + "found, all possible attributes in the udev rules key format.\n" + "A rule to match, can be composed by the attributes of the device\n" + "and the attributes from one single parent device.\n" + "\n"); + + printf(" looking at device '%s':\n", udev_device_get_devpath(device)); + printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); + str = udev_device_get_subsystem(device); + if (str == NULL) + str = ""; + printf(" SUBSYSTEM==\"%s\"\n", str); + str = udev_device_get_driver(device); + if (str == NULL) + str = ""; + printf(" DRIVER==\"%s\"\n", str); + print_all_attributes(device, "ATTR"); + + device_parent = device; + do { + device_parent = udev_device_get_parent(device_parent); + if (device_parent == NULL) + break; + printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); + printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); + str = udev_device_get_subsystem(device_parent); + if (str == NULL) + str = ""; + printf(" SUBSYSTEMS==\"%s\"\n", str); + str = udev_device_get_driver(device_parent); + if (str == NULL) + str = ""; + printf(" DRIVERS==\"%s\"\n", str); + print_all_attributes(device_parent, "ATTRS"); + } while (device_parent != NULL); + + return 0; +} + +static void print_record(struct udev_device *device) { + const char *str; + int i; + struct udev_list_entry *list_entry; + + printf("P: %s\n", udev_device_get_devpath(device)); + + str = udev_device_get_devnode(device); + if (str != NULL) + printf("N: %s\n", str + strlen("/dev/")); + + i = udev_device_get_devlink_priority(device); + if (i != 0) + printf("L: %i\n", i); + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) + printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/")); + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) + printf("E: %s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + printf("\n"); +} + +static int stat_device(const char *name, bool export, const char *prefix) { + struct stat statbuf; + + if (stat(name, &statbuf) != 0) + return -errno; + + if (export) { + if (prefix == NULL) + prefix = "INFO_"; + printf("%sMAJOR=%u\n" + "%sMINOR=%u\n", + prefix, major(statbuf.st_dev), + prefix, minor(statbuf.st_dev)); + } else + printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev)); + return 0; +} + +static int export_devices(struct udev *udev) { + _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate; + struct udev_list_entry *list_entry; + + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -ENOMEM; + + udev_enumerate_scan_devices(udev_enumerate); + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { + _cleanup_udev_device_unref_ struct udev_device *device; + + device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); + if (device != NULL) + print_record(device); + } + + return 0; +} + +static void cleanup_dir(DIR *dir, mode_t mask, int depth) { + struct dirent *dent; + + if (depth <= 0) + return; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) + continue; + if ((stats.st_mode & mask) != 0) + continue; + if (S_ISDIR(stats.st_mode)) { + _cleanup_closedir_ DIR *dir2; + + dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2 != NULL) + cleanup_dir(dir2, mask, depth-1); + + (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); + } else + (void) unlinkat(dirfd(dir), dent->d_name, 0); + } +} + +static void cleanup_db(struct udev *udev) { + _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL; + + (void) unlink("/run/udev/queue.bin"); + + dir1 = opendir("/run/udev/data"); + if (dir1 != NULL) + cleanup_dir(dir1, S_ISVTX, 1); + + dir2 = opendir("/run/udev/links"); + if (dir2 != NULL) + cleanup_dir(dir2, 0, 2); + + dir3 = opendir("/run/udev/tags"); + if (dir3 != NULL) + cleanup_dir(dir3, 0, 2); + + dir4 = opendir("/run/udev/static_node-tags"); + if (dir4 != NULL) + cleanup_dir(dir4, 0, 2); + + dir5 = opendir("/run/udev/watch"); + if (dir5 != NULL) + cleanup_dir(dir5, 0, 1); +} + +static void help(void) { + + printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n" + "Query sysfs or the udev database.\n\n" + " -h --help Print this message\n" + " --version Print version of the program\n" + " -q --query=TYPE Query device information:\n" + " name Name of device node\n" + " symlink Pointing to node\n" + " path sysfs device path\n" + " property The device properties\n" + " all All values\n" + " -p --path=SYSPATH sysfs device path used for query or attribute walk\n" + " -n --name=NAME Node or symlink name used for query or attribute walk\n" + " -r --root Prepend dev directory to path names\n" + " -a --attribute-walk Print all key matches walking along the chain\n" + " of parent devices\n" + " -d --device-id-of-file=FILE Print major:minor of device containing this file\n" + " -x --export Export key/value pairs\n" + " -P --export-prefix Export the key name with a prefix\n" + " -e --export-db Export the content of the udev database\n" + " -c --cleanup-db Clean up the udev database\n" + , program_invocation_short_name); +} + +static int uinfo(struct udev *udev, int argc, char *argv[]) { + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + bool root = 0; + bool export = 0; + const char *export_prefix = NULL; + char name[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + int c; + + static const struct option options[] = { + { "name", required_argument, NULL, 'n' }, + { "path", required_argument, NULL, 'p' }, + { "query", required_argument, NULL, 'q' }, + { "attribute-walk", no_argument, NULL, 'a' }, + { "cleanup-db", no_argument, NULL, 'c' }, + { "export-db", no_argument, NULL, 'e' }, + { "root", no_argument, NULL, 'r' }, + { "device-id-of-file", required_argument, NULL, 'd' }, + { "export", no_argument, NULL, 'x' }, + { "export-prefix", required_argument, NULL, 'P' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + enum action_type { + ACTION_QUERY, + ACTION_ATTRIBUTE_WALK, + ACTION_DEVICE_ID_FILE, + } action = ACTION_QUERY; + + enum query_type { + QUERY_NAME, + QUERY_PATH, + QUERY_SYMLINK, + QUERY_PROPERTY, + QUERY_ALL, + } query = QUERY_ALL; + + while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0) + switch (c) { + case 'n': { + if (device != NULL) { + fprintf(stderr, "device already specified\n"); + return 2; + } + + device = find_device(udev, optarg, "/dev/"); + if (device == NULL) { + fprintf(stderr, "device node not found\n"); + return 2; + } + break; + } + case 'p': + if (device != NULL) { + fprintf(stderr, "device already specified\n"); + return 2; + } + + device = find_device(udev, optarg, "/sys"); + if (device == NULL) { + fprintf(stderr, "syspath not found\n"); + return 2; + } + break; + case 'q': + action = ACTION_QUERY; + if (streq(optarg, "property") || streq(optarg, "env")) + query = QUERY_PROPERTY; + else if (streq(optarg, "name")) + query = QUERY_NAME; + else if (streq(optarg, "symlink")) + query = QUERY_SYMLINK; + else if (streq(optarg, "path")) + query = QUERY_PATH; + else if (streq(optarg, "all")) + query = QUERY_ALL; + else { + fprintf(stderr, "unknown query type\n"); + return 3; + } + break; + case 'r': + root = true; + break; + case 'd': + action = ACTION_DEVICE_ID_FILE; + strscpy(name, sizeof(name), optarg); + break; + case 'a': + action = ACTION_ATTRIBUTE_WALK; + break; + case 'e': + if (export_devices(udev) < 0) + return 1; + return 0; + case 'c': + cleanup_db(udev); + return 0; + case 'x': + export = true; + break; + case 'P': + export_prefix = optarg; + break; + case 'V': + printf("%s\n", VERSION); + return 0; + case 'h': + help(); + return 0; + default: + return 1; + } + + switch (action) { + case ACTION_QUERY: + if (!device) { + if (!argv[optind]) { + help(); + return 2; + } + device = find_device(udev, argv[optind], NULL); + if (!device) { + fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); + return 4; + } + } + + switch(query) { + case QUERY_NAME: { + const char *node = udev_device_get_devnode(device); + + if (node == NULL) { + fprintf(stderr, "no device node found\n"); + return 5; + } + + if (root) + printf("%s\n", udev_device_get_devnode(device)); + else + printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/")); + break; + } + case QUERY_SYMLINK: + list_entry = udev_device_get_devlinks_list_entry(device); + while (list_entry != NULL) { + if (root) + printf("%s", udev_list_entry_get_name(list_entry)); + else + printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/")); + list_entry = udev_list_entry_get_next(list_entry); + if (list_entry != NULL) + printf(" "); + } + printf("\n"); + break; + case QUERY_PATH: + printf("%s\n", udev_device_get_devpath(device)); + return 0; + case QUERY_PROPERTY: + list_entry = udev_device_get_properties_list_entry(device); + while (list_entry != NULL) { + if (export) + printf("%s%s='%s'\n", strempty(export_prefix), + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + else + printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + + list_entry = udev_list_entry_get_next(list_entry); + } + break; + case QUERY_ALL: + print_record(device); + break; + default: + assert_not_reached("unknown query type"); + } + break; + case ACTION_ATTRIBUTE_WALK: + if (!device && argv[optind]) { + device = find_device(udev, argv[optind], NULL); + if (!device) { + fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n"); + return 4; + } + } + if (!device) { + fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); + return 4; + } + print_device_chain(device); + break; + case ACTION_DEVICE_ID_FILE: + if (stat_device(name, export, export_prefix) != 0) + return 1; + break; + } + + return 0; +} + +const struct udevadm_cmd udevadm_info = { + .name = "info", + .cmd = uinfo, + .help = "Query sysfs or the udev database", +}; diff --git a/src/grp-udev/udevadm/udevadm-monitor.c b/src/grp-udev/udevadm/udevadm-monitor.c new file mode 100644 index 0000000000..c6002ce416 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-monitor.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2004-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "shared/udev-util.h" +#include "udev.h" + +static bool udev_exit; + +static void sig_handler(int signum) { + if (signum == SIGINT || signum == SIGTERM) + udev_exit = true; +} + +static void print_device(struct udev_device *device, const char *source, int prop) { + struct timespec ts; + + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", + source, + ts.tv_sec, ts.tv_nsec/1000, + udev_device_get_action(device), + udev_device_get_devpath(device), + udev_device_get_subsystem(device)); + if (prop) { + struct udev_list_entry *list_entry; + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) + printf("%s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + printf("\n"); + } +} + +static void help(void) { + printf("%s monitor [--property] [--kernel] [--udev] [--help]\n\n" + "Listen to kernel and udev events.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -p --property Print the event properties\n" + " -k --kernel Print kernel uevents\n" + " -u --udev Print udev events\n" + " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n" + " -t --tag-match=TAG Filter events by tag\n" + , program_invocation_short_name); +} + +static int adm_monitor(struct udev *udev, int argc, char *argv[]) { + struct sigaction act = {}; + sigset_t mask; + bool prop = false; + bool print_kernel = false; + bool print_udev = false; + _cleanup_udev_list_cleanup_ struct udev_list subsystem_match_list; + _cleanup_udev_list_cleanup_ struct udev_list tag_match_list; + _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL; + _cleanup_udev_monitor_unref_ struct udev_monitor *kernel_monitor = NULL; + _cleanup_close_ int fd_ep = -1; + int fd_kernel = -1, fd_udev = -1; + struct epoll_event ep_kernel, ep_udev; + int c; + + static const struct option options[] = { + { "property", no_argument, NULL, 'p' }, + { "environment", no_argument, NULL, 'e' }, /* alias for -p */ + { "kernel", no_argument, NULL, 'k' }, + { "udev", no_argument, NULL, 'u' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "tag-match", required_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + udev_list_init(udev, &subsystem_match_list, true); + udev_list_init(udev, &tag_match_list, true); + + while ((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0) + switch (c) { + case 'p': + case 'e': + prop = true; + break; + case 'k': + print_kernel = true; + break; + case 'u': + print_udev = true; + break; + case 's': + { + char subsys[UTIL_NAME_SIZE]; + char *devtype; + + strscpy(subsys, sizeof(subsys), optarg); + devtype = strchr(subsys, '/'); + if (devtype != NULL) { + devtype[0] = '\0'; + devtype++; + } + udev_list_entry_add(&subsystem_match_list, subsys, devtype); + break; + } + case 't': + udev_list_entry_add(&tag_match_list, optarg, NULL); + break; + case 'h': + help(); + return 0; + default: + return 1; + } + + if (!print_kernel && !print_udev) { + print_kernel = true; + print_udev = true; + } + + /* set signal handlers */ + act.sa_handler = sig_handler; + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + /* Callers are expecting to see events as they happen: Line buffering */ + setlinebuf(stdout); + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + log_error_errno(errno, "error creating epoll fd: %m"); + return 1; + } + + printf("monitor will print the received events for:\n"); + if (print_udev) { + struct udev_list_entry *entry; + + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (udev_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + return 1; + } + udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); + fd_udev = udev_monitor_get_fd(udev_monitor); + + udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { + const char *subsys = udev_list_entry_get_name(entry); + const char *devtype = udev_list_entry_get_value(entry); + + if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) + fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); + } + + udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { + const char *tag = udev_list_entry_get_name(entry); + + if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) + fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); + } + + if (udev_monitor_enable_receiving(udev_monitor) < 0) { + fprintf(stderr, "error: unable to subscribe to udev events\n"); + return 2; + } + + memzero(&ep_udev, sizeof(struct epoll_event)); + ep_udev.events = EPOLLIN; + ep_udev.data.fd = fd_udev; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { + log_error_errno(errno, "fail to add fd to epoll: %m"); + return 2; + } + + printf("UDEV - the event which udev sends out after rule processing\n"); + } + + if (print_kernel) { + struct udev_list_entry *entry; + + kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (kernel_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + return 3; + } + udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); + fd_kernel = udev_monitor_get_fd(kernel_monitor); + + udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { + const char *subsys = udev_list_entry_get_name(entry); + + if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) + fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); + } + + if (udev_monitor_enable_receiving(kernel_monitor) < 0) { + fprintf(stderr, "error: unable to subscribe to kernel events\n"); + return 4; + } + + memzero(&ep_kernel, sizeof(struct epoll_event)); + ep_kernel.events = EPOLLIN; + ep_kernel.data.fd = fd_kernel; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { + log_error_errno(errno, "fail to add fd to epoll: %m"); + return 5; + } + + printf("KERNEL - the kernel uevent\n"); + } + printf("\n"); + + while (!udev_exit) { + int fdcount; + struct epoll_event ev[4]; + int i; + + fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); + if (fdcount < 0) { + if (errno != EINTR) + fprintf(stderr, "error receiving uevent message: %m\n"); + continue; + } + + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { + struct udev_device *device; + + device = udev_monitor_receive_device(kernel_monitor); + if (device == NULL) + continue; + print_device(device, "KERNEL", prop); + udev_device_unref(device); + } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { + struct udev_device *device; + + device = udev_monitor_receive_device(udev_monitor); + if (device == NULL) + continue; + print_device(device, "UDEV", prop); + udev_device_unref(device); + } + } + } + + return 0; +} + +const struct udevadm_cmd udevadm_monitor = { + .name = "monitor", + .cmd = adm_monitor, + .help = "Listen to kernel and udev events", +}; diff --git a/src/grp-udev/udevadm/udevadm-settle.c b/src/grp-udev/udevadm/udevadm-settle.c new file mode 100644 index 0000000000..2a7a150517 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-settle.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2006-2009 Kay Sievers + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/parse-util.h" +#include "basic/util.h" +#include "udev.h" + +static void help(void) { + printf("%s settle OPTIONS\n\n" + "Wait for pending udev events.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -t --timeout=SECONDS Maximum time to wait for events\n" + " -E --exit-if-exists=FILE Stop waiting if file exists\n" + , program_invocation_short_name); +} + +static int adm_settle(struct udev *udev, int argc, char *argv[]) { + static const struct option options[] = { + { "timeout", required_argument, NULL, 't' }, + { "exit-if-exists", required_argument, NULL, 'E' }, + { "help", no_argument, NULL, 'h' }, + { "seq-start", required_argument, NULL, 's' }, /* removed */ + { "seq-end", required_argument, NULL, 'e' }, /* removed */ + { "quiet", no_argument, NULL, 'q' }, /* removed */ + {} + }; + usec_t deadline; + const char *exists = NULL; + unsigned int timeout = 120; + struct pollfd pfd[1] = { {.fd = -1}, }; + int c; + struct udev_queue *queue; + int rc = EXIT_FAILURE; + + while ((c = getopt_long(argc, argv, "t:E:hs:e:q", options, NULL)) >= 0) { + switch (c) { + + case 't': { + int r; + + r = safe_atou(optarg, &timeout); + if (r < 0) { + log_error_errno(r, "Invalid timeout value '%s': %m", optarg); + return EXIT_FAILURE; + } + break; + } + + case 'E': + exists = optarg; + break; + + case 'h': + help(); + return EXIT_SUCCESS; + + case 's': + case 'e': + case 'q': + log_info("Option -%c no longer supported.", c); + return EXIT_FAILURE; + + case '?': + return EXIT_FAILURE; + + default: + assert_not_reached("Unknown argument"); + } + } + + if (optind < argc) { + fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]); + return EXIT_FAILURE; + } + + deadline = now(CLOCK_MONOTONIC) + timeout * USEC_PER_SEC; + + /* guarantee that the udev daemon isn't pre-processing */ + if (getuid() == 0) { + struct udev_ctrl *uctrl; + + uctrl = udev_ctrl_new(udev); + if (uctrl != NULL) { + if (udev_ctrl_send_ping(uctrl, MAX(5U, timeout)) < 0) { + log_debug("no connection to daemon"); + udev_ctrl_unref(uctrl); + return EXIT_SUCCESS; + } + udev_ctrl_unref(uctrl); + } + } + + queue = udev_queue_new(udev); + if (!queue) { + log_error("unable to get udev queue"); + return EXIT_FAILURE; + } + + pfd[0].events = POLLIN; + pfd[0].fd = udev_queue_get_fd(queue); + if (pfd[0].fd < 0) { + log_debug("queue is empty, nothing to watch"); + rc = EXIT_SUCCESS; + goto out; + } + + for (;;) { + if (exists && access(exists, F_OK) >= 0) { + rc = EXIT_SUCCESS; + break; + } + + /* exit if queue is empty */ + if (udev_queue_get_queue_is_empty(queue)) { + rc = EXIT_SUCCESS; + break; + } + + if (now(CLOCK_MONOTONIC) >= deadline) + break; + + /* wake up when queue is empty */ + if (poll(pfd, 1, MSEC_PER_SEC) > 0 && pfd[0].revents & POLLIN) + udev_queue_flush(queue); + } + +out: + udev_queue_unref(queue); + return rc; +} + +const struct udevadm_cmd udevadm_settle = { + .name = "settle", + .cmd = adm_settle, + .help = "Wait for pending udev events", +}; diff --git a/src/grp-udev/udevadm/udevadm-test-builtin.c b/src/grp-udev/udevadm/udevadm-test-builtin.c new file mode 100644 index 0000000000..a3a66e05d7 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-test-builtin.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "basic/string-util.h" +#include "udev.h" + +static void help(struct udev *udev) { + printf("%s builtin [--help] COMMAND SYSPATH\n\n" + "Test a built-in command.\n\n" + " -h --help Print this message\n" + " --version Print version of the program\n\n" + "Commands:\n" + , program_invocation_short_name); + + udev_builtin_list(udev); +} + +static int adm_builtin(struct udev *udev, int argc, char *argv[]) { + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + {} + }; + char *command = NULL; + char *syspath = NULL; + char filename[UTIL_PATH_SIZE]; + struct udev_device *dev = NULL; + enum udev_builtin_cmd cmd; + int rc = EXIT_SUCCESS, c; + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch (c) { + case 'h': + help(udev); + goto out; + } + + command = argv[optind++]; + if (command == NULL) { + fprintf(stderr, "command missing\n"); + help(udev); + rc = 2; + goto out; + } + + syspath = argv[optind++]; + if (syspath == NULL) { + fprintf(stderr, "syspath missing\n"); + rc = 3; + goto out; + } + + udev_builtin_init(udev); + + cmd = udev_builtin_lookup(command); + if (cmd >= UDEV_BUILTIN_MAX) { + fprintf(stderr, "unknown command '%s'\n", command); + help(udev); + rc = 5; + goto out; + } + + /* add /sys if needed */ + if (!startswith(syspath, "/sys")) + strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); + else + strscpy(filename, sizeof(filename), syspath); + util_remove_trailing_chars(filename, '/'); + + dev = udev_device_new_from_syspath(udev, filename); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n\n", filename); + rc = 4; + goto out; + } + + rc = udev_builtin_run(dev, cmd, command, true); + if (rc < 0) { + fprintf(stderr, "error executing '%s', exit code %i\n\n", command, rc); + rc = 6; + } +out: + udev_device_unref(dev); + udev_builtin_exit(udev); + return rc; +} + +const struct udevadm_cmd udevadm_test_builtin = { + .name = "test-builtin", + .cmd = adm_builtin, + .help = "Test a built-in command", + .debug = true, +}; diff --git a/src/grp-udev/udevadm/udevadm-test.c b/src/grp-udev/udevadm/udevadm-test.c new file mode 100644 index 0000000000..ed919ec244 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-test.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/string-util.h" +#include "shared/udev-util.h" +#include "udev.h" + +static void help(void) { + + printf("%s test OPTIONS \n\n" + "Test an event run.\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -a --action=ACTION Set action string\n" + " -N --resolve-names=early|late|never When to resolve names\n" + , program_invocation_short_name); +} + +static int adm_test(struct udev *udev, int argc, char *argv[]) { + int resolve_names = 1; + char filename[UTIL_PATH_SIZE]; + const char *action = "add"; + const char *syspath = NULL; + struct udev_list_entry *entry; + _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL; + _cleanup_udev_device_unref_ struct udev_device *dev = NULL; + _cleanup_udev_event_unref_ struct udev_event *event = NULL; + sigset_t mask, sigmask_orig; + int rc = 0, c; + + static const struct option options[] = { + { "action", required_argument, NULL, 'a' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + log_debug("version %s", VERSION); + + while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) + switch (c) { + case 'a': + action = optarg; + break; + case 'N': + if (streq (optarg, "early")) { + resolve_names = 1; + } else if (streq (optarg, "late")) { + resolve_names = 0; + } else if (streq (optarg, "never")) { + resolve_names = -1; + } else { + fprintf(stderr, "resolve-names must be early, late or never\n"); + log_error("resolve-names must be early, late or never"); + exit(EXIT_FAILURE); + } + break; + case 'h': + help(); + exit(EXIT_SUCCESS); + case '?': + exit(EXIT_FAILURE); + default: + assert_not_reached("Unknown option"); + } + + syspath = argv[optind]; + if (syspath == NULL) { + fprintf(stderr, "syspath parameter missing\n"); + rc = 2; + goto out; + } + + printf("This program is for debugging only, it does not run any program\n" + "specified by a RUN key. It may show incorrect results, because\n" + "some values may be different, or not available at a simulation run.\n" + "\n"); + + sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); + + udev_builtin_init(udev); + + rules = udev_rules_new(udev, resolve_names); + if (rules == NULL) { + fprintf(stderr, "error reading rules\n"); + rc = 3; + goto out; + } + + /* add /sys if needed */ + if (!startswith(syspath, "/sys")) + strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); + else + strscpy(filename, sizeof(filename), syspath); + util_remove_trailing_chars(filename, '/'); + + dev = udev_device_new_from_synthetic_event(udev, filename, action); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n", filename); + rc = 4; + goto out; + } + + /* don't read info from the db */ + udev_device_set_info_loaded(dev); + + event = udev_event_new(dev); + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); + + udev_event_execute_rules(event, + 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, + NULL, + rules); + + udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) + printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); + + udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { + char program[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); + printf("run: '%s'\n", program); + } +out: + udev_builtin_exit(udev); + return rc; +} + +const struct udevadm_cmd udevadm_test = { + .name = "test", + .cmd = adm_test, + .help = "Test an event run", + .debug = true, +}; diff --git a/src/grp-udev/udevadm/udevadm-trigger.c b/src/grp-udev/udevadm/udevadm-trigger.c new file mode 100644 index 0000000000..280f348fae --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-trigger.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2008-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" +#include "udev.h" + +#include "udevadm-util.h" + +static int verbose; +static int dry_run; + +static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { + struct udev_list_entry *entry; + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { + char filename[UTIL_PATH_SIZE]; + int fd; + + if (verbose) + printf("%s\n", udev_list_entry_get_name(entry)); + if (dry_run) + continue; + strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); + fd = open(filename, O_WRONLY|O_CLOEXEC); + if (fd < 0) + continue; + if (write(fd, action, strlen(action)) < 0) + log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); + close(fd); + } +} + +static const char *keyval(const char *str, const char **val, char *buf, size_t size) { + char *pos; + + strscpy(buf, size,str); + pos = strchr(buf, '='); + if (pos != NULL) { + pos[0] = 0; + pos++; + } + *val = pos; + return buf; +} + +static void help(void) { + printf("%s trigger OPTIONS\n\n" + "Request events from the kernel.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -v --verbose Print the list of devices while running\n" + " -n --dry-run Do not actually trigger the events\n" + " -t --type= Type of events to trigger\n" + " devices sysfs devices (default)\n" + " subsystems sysfs subsystems and drivers\n" + " -c --action=ACTION Event action value, default is \"change\"\n" + " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" + " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" + " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" + " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" + " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" + " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" + " -y --sysname-match=NAME Trigger devices with this /sys path\n" + " --name-match=NAME Trigger devices with this /dev name\n" + " -b --parent-match=NAME Trigger devices with that parent device\n" + , program_invocation_short_name); +} + +static int adm_trigger(struct udev *udev, int argc, char *argv[]) { + enum { + ARG_NAME = 0x100, + }; + + static const struct option options[] = { + { "verbose", no_argument, NULL, 'v' }, + { "dry-run", no_argument, NULL, 'n' }, + { "type", required_argument, NULL, 't' }, + { "action", required_argument, NULL, 'c' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "subsystem-nomatch", required_argument, NULL, 'S' }, + { "attr-match", required_argument, NULL, 'a' }, + { "attr-nomatch", required_argument, NULL, 'A' }, + { "property-match", required_argument, NULL, 'p' }, + { "tag-match", required_argument, NULL, 'g' }, + { "sysname-match", required_argument, NULL, 'y' }, + { "name-match", required_argument, NULL, ARG_NAME }, + { "parent-match", required_argument, NULL, 'b' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + enum { + TYPE_DEVICES, + TYPE_SUBSYSTEMS, + } device_type = TYPE_DEVICES; + const char *action = "change"; + _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL; + int c, r; + + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return 1; + + while ((c = getopt_long(argc, argv, "vno:t:c:s:S:a:A:p:g:y:b:h", options, NULL)) >= 0) { + const char *key; + const char *val; + char buf[UTIL_PATH_SIZE]; + + switch (c) { + case 'v': + verbose = 1; + break; + case 'n': + dry_run = 1; + break; + case 't': + if (streq(optarg, "devices")) + device_type = TYPE_DEVICES; + else if (streq(optarg, "subsystems")) + device_type = TYPE_SUBSYSTEMS; + else { + log_error("unknown type --type=%s", optarg); + return 2; + } + break; + case 'c': + if (!nulstr_contains("add\0" "remove\0" "change\0", optarg)) { + log_error("unknown action '%s'", optarg); + return 2; + } else + action = optarg; + + break; + case 's': + r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg); + if (r < 0) { + log_error_errno(r, "could not add subsystem match '%s': %m", optarg); + return 2; + } + break; + case 'S': + r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); + if (r < 0) { + log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg); + return 2; + } + break; + case 'a': + key = keyval(optarg, &val, buf, sizeof(buf)); + r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val); + if (r < 0) { + log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val); + return 2; + } + break; + case 'A': + key = keyval(optarg, &val, buf, sizeof(buf)); + r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); + if (r < 0) { + log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val); + return 2; + } + break; + case 'p': + key = keyval(optarg, &val, buf, sizeof(buf)); + r = udev_enumerate_add_match_property(udev_enumerate, key, val); + if (r < 0) { + log_error_errno(r, "could not add property match '%s=%s': %m", key, val); + return 2; + } + break; + case 'g': + r = udev_enumerate_add_match_tag(udev_enumerate, optarg); + if (r < 0) { + log_error_errno(r, "could not add tag match '%s': %m", optarg); + return 2; + } + break; + case 'y': + r = udev_enumerate_add_match_sysname(udev_enumerate, optarg); + if (r < 0) { + log_error_errno(r, "could not add sysname match '%s': %m", optarg); + return 2; + } + break; + case 'b': { + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, optarg, "/sys"); + if (dev == NULL) { + log_error("unable to open the device '%s'", optarg); + return 2; + } + + r = udev_enumerate_add_match_parent(udev_enumerate, dev); + if (r < 0) { + log_error_errno(r, "could not add parent match '%s': %m", optarg); + return 2; + } + break; + } + + case ARG_NAME: { + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, optarg, "/dev/"); + if (dev == NULL) { + log_error("unable to open the device '%s'", optarg); + return 2; + } + + r = udev_enumerate_add_match_parent(udev_enumerate, dev); + if (r < 0) { + log_error_errno(r, "could not add parent match '%s': %m", optarg); + return 2; + } + break; + } + + case 'h': + help(); + return 0; + case '?': + return 1; + default: + assert_not_reached("Unknown option"); + } + } + + for (; optind < argc; optind++) { + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, argv[optind], NULL); + if (dev == NULL) { + log_error("unable to open the device '%s'", argv[optind]); + return 2; + } + + r = udev_enumerate_add_match_parent(udev_enumerate, dev); + if (r < 0) { + log_error_errno(r, "could not add tag match '%s': %m", optarg); + return 2; + } + } + + switch (device_type) { + case TYPE_SUBSYSTEMS: + udev_enumerate_scan_subsystems(udev_enumerate); + exec_list(udev_enumerate, action); + return 0; + case TYPE_DEVICES: + udev_enumerate_scan_devices(udev_enumerate); + exec_list(udev_enumerate, action); + return 0; + default: + assert_not_reached("device_type"); + } +} + +const struct udevadm_cmd udevadm_trigger = { + .name = "trigger", + .cmd = adm_trigger, + .help = "Request events from the kernel", +}; diff --git a/src/grp-udev/udevadm/udevadm-util.c b/src/grp-udev/udevadm/udevadm-util.c new file mode 100644 index 0000000000..8dc871cb10 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-util.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "basic/string-util.h" + +#include "udevadm-util.h" + +struct udev_device *find_device(struct udev *udev, + const char *id, + const char *prefix) { + + assert(udev); + assert(id); + + if (prefix && !startswith(id, prefix)) + id = strjoina(prefix, id); + + if (startswith(id, "/dev/")) { + struct stat statbuf; + char type; + + if (stat(id, &statbuf) < 0) + return NULL; + + if (S_ISBLK(statbuf.st_mode)) + type = 'b'; + else if (S_ISCHR(statbuf.st_mode)) + type = 'c'; + else + return NULL; + + return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); + } else if (startswith(id, "/sys/")) + return udev_device_new_from_syspath(udev, id); + else + return NULL; +} diff --git a/src/grp-udev/udevadm/udevadm-util.h b/src/grp-udev/udevadm/udevadm-util.h new file mode 100644 index 0000000000..dc712b0d93 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm-util.h @@ -0,0 +1,24 @@ +#pragma once + +/* + * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "udev.h" + +struct udev_device *find_device(struct udev *udev, + const char *id, + const char *prefix); diff --git a/src/grp-udev/udevadm/udevadm.c b/src/grp-udev/udevadm/udevadm.c new file mode 100644 index 0000000000..555d269275 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007-2012 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "udev.h" + +static int adm_version(struct udev *udev, int argc, char *argv[]) { + printf("%s\n", VERSION); + return 0; +} + +static const struct udevadm_cmd udevadm_version = { + .name = "version", + .cmd = adm_version, +}; + +static int adm_help(struct udev *udev, int argc, char *argv[]); + +static const struct udevadm_cmd udevadm_help = { + .name = "help", + .cmd = adm_help, +}; + +static const struct udevadm_cmd *udevadm_cmds[] = { + &udevadm_info, + &udevadm_trigger, + &udevadm_settle, + &udevadm_control, + &udevadm_monitor, + &udevadm_hwdb, + &udevadm_test, + &udevadm_test_builtin, + &udevadm_version, + &udevadm_help, +}; + +static int adm_help(struct udev *udev, int argc, char *argv[]) { + unsigned int i; + + printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n" + "Send control commands or test the device manager.\n\n" + "Commands:\n" + , program_invocation_short_name); + + for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) + if (udevadm_cmds[i]->help != NULL) + printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); + return 0; +} + +static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) { + if (cmd->debug) + log_set_max_level(LOG_DEBUG); + log_debug("calling: %s", cmd->name); + return cmd->cmd(udev, argc, argv); +} + +int main(int argc, char *argv[]) { + struct udev *udev; + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + const char *command; + unsigned int i; + int rc = 1, c; + + udev = udev_new(); + if (udev == NULL) + goto out; + + log_parse_environment(); + log_open(); + mac_selinux_init(); + + while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0) + switch (c) { + + case 'd': + log_set_max_level(LOG_DEBUG); + break; + + case 'h': + rc = adm_help(udev, argc, argv); + goto out; + + case 'V': + rc = adm_version(udev, argc, argv); + goto out; + + default: + goto out; + } + + command = argv[optind]; + + if (command != NULL) + for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) + if (streq(udevadm_cmds[i]->name, command)) { + argc -= optind; + argv += optind; + /* we need '0' here to reset the internal state */ + optind = 0; + rc = run_command(udev, udevadm_cmds[i], argc, argv); + goto out; + } + + fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name); + rc = 2; +out: + mac_selinux_finish(); + udev_unref(udev); + log_close(); + return rc; +} diff --git a/src/grp-udev/udevadm/udevadm.completion.bash b/src/grp-udev/udevadm/udevadm.completion.bash new file mode 100644 index 0000000000..b828b8dd7c --- /dev/null +++ b/src/grp-udev/udevadm/udevadm.completion.bash @@ -0,0 +1,97 @@ +# udevadm(8) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2010 Ran Benita +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_all_sysdevs() { + local -a devs=(/sys/bus/*/devices/*/ /sys/class/*/*/) + printf '%s\n' "${devs[@]%/}" +} + +_udevadm() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --debug' + + local verbs=(info trigger settle control monitor hwdb test-builtin test) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}" && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]} ${verbs[*]}' -- "$cur") ) + return 0 + fi + + case $verb in + 'info') + if [[ $cur = -* ]]; then + comps='--help --query= --path= --name= --root --attribute-walk --export-db --cleanup-db' + else + comps=$( __get_all_sysdevs ) + fi + ;; + 'trigger') + comps='--help --verbose --dry-run --type= --action= --subsystem-match= + --subsystem-nomatch= --attr-match= --attr-nomatch= --property-match= + --tag-match= --sysname-match= --parent-match=' + ;; + 'settle') + comps='--help --timeout= --seq-start= --seq-end= --exit-if-exists= --quiet' + ;; + 'control') + comps='--help --exit --log-priority= --stop-exec-queue --start-exec-queue + --reload --property= --children-max= --timeout=' + ;; + 'monitor') + comps='--help --kernel --udev --property --subsystem-match= --tag-match=' + ;; + 'hwdb') + comps='--help --update --test=' + ;; + 'test') + if [[ $cur = -* ]]; then + comps='--help --action=' + else + comps=$( __get_all_sysdevs ) + fi + ;; + 'test-builtin') + comps='blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess' + ;; + *) + comps=${VERBS[*]} + ;; + esac + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _udevadm udevadm diff --git a/src/grp-udev/udevadm/udevadm.completion.zsh b/src/grp-udev/udevadm/udevadm.completion.zsh new file mode 100644 index 0000000000..bb23e64d24 --- /dev/null +++ b/src/grp-udev/udevadm/udevadm.completion.zsh @@ -0,0 +1,141 @@ +#compdef udevadm + +_udevadm_info(){ + _arguments \ + '--query=[Query the database for specified type of device data. It needs the --path or --name to identify the specified device.]:type:(name symlink path property all)' \ + '--path=[The devpath of the device to query.]:sys files:_files -P /sys/ -W /sys' \ + '--name=[The name of the device node or a symlink to query]:device files:_files -P /dev/ -W /dev' \ + '--root[Print absolute paths in name or symlink query.]' \ + '--attribute-walk[Print all sysfs properties of the specified device that can be used in udev rules to match the specified device]' \ + '--export[Print output as key/value pairs.]' \ + '--export-prefix=[Add a prefix to the key name of exported values.]:prefix' \ + '--device-id-of-file=[Print major/minor numbers of the underlying device, where the file lives on.]:files:_udevadm_mounts' \ + '--export-db[Export the content of the udev database.]' \ + '--cleanup-db[Cleanup the udev database.]' +} + +_udevadm_trigger(){ + _arguments \ + '--verbose[Print the list of devices which will be triggered.]' \ + '--dry-run[Do not actually trigger the event.]' \ + '--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \ + '--action=[Type of event to be triggered.]:actions:(add change remove)' \ + '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \ + '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \ + '--attr-match=attribute=[Trigger events for devices with a matching sysfs attribute.]' \ + '--attr-nomatch=attribute=[Do not trigger events for devices with a matching sysfs attribute.]' \ + '--property-match=[Trigger events for devices with a matching property value.]' \ + '--tag-match=property[Trigger events for devices with a matching tag.]' \ + '--sysname-match=[Trigger events for devices with a matching sys device name.]' \ + '--parent-match=[Trigger events for all children of a given device.]' +} + +_udevadm_settle(){ + _arguments \ + '--timeout=[Maximum number of seconds to wait for the event queue to become empty.]' \ + '--seq-start=[Wait only for events after the given sequence number.]' \ + '--seq-end=[Wait only for events before the given sequence number.]' \ + '--exit-if-exists=[Stop waiting if file exists.]:files:_files' \ + '--quiet[Do not print any output, like the remaining queue entries when reaching the timeout.]' \ + '--help[Print help text.]' +} + +_udevadm_control(){ + _arguments \ + '--exit[Signal and wait for systemd-udevd to exit.]' \ + '--log-priority=[Set the internal log level of systemd-udevd.]:priorities:(err info debug)' \ + '--stop-exec-queue[Signal systemd-udevd to stop executing new events. Incoming events will be queued.]' \ + '--start-exec-queue[Signal systemd-udevd to enable the execution of events.]' \ + '--reload[Signal systemd-udevd to reload the rules files and other databases like the kernel module index.]' \ + '--property=[Set a global property for all events.]' \ + '--children-max=[Set the maximum number of events.]' \ + '--timeout=[The maximum number of seconds to wait for a reply from systemd-udevd.]' \ + '--help[Print help text.]' +} + +_udevadm_monitor(){ + _arguments \ + '--kernel[Print the kernel uevents.]' \ + '--udev[Print the udev event after the rule processing.]' \ + '--property[Also print the properties of the event.]' \ + '--subsystem-match=[Filter events by subsystem/\[devtype\].]' \ + '--tag-match=[Filter events by property.]' \ + '--help[Print help text.]' +} + +_udevadm_test(){ + _arguments \ + '--action=[The action string.]:actions:(add change remove)' \ + '--subsystem=[The subsystem string.]' \ + '--help[Print help text.]' \ + '*::devpath:_files -P /sys/ -W /sys' +} + +_udevadm_test-builtin(){ + if (( CURRENT == 2 )); then + _arguments \ + '--help[Print help text]' \ + '*::builtins:(blkid btrfs hwdb input_id net_id net_setup_link kmod path_id usb_id uaccess)' + elif (( CURRENT == 3 )); then + _arguments \ + '--help[Print help text]' \ + '*::syspath:_files -P /sys -W /sys' + else + _arguments \ + '--help[Print help text]' + fi +} + +_udevadm_mounts(){ + local dev_tmp dpath_tmp mp_tmp mline + + tmp=( "${(@f)$(< /proc/self/mounts)}" ) + dev_tmp=( "${(@)${(@)tmp%% *}:#none}" ) + mp_tmp=( "${(@)${(@)tmp#* }%% *}" ) + + local MATCH + mp_tmp=("${(@q)mp_tmp//(#m)\\[0-7](#c3)/${(#)$(( 8#${MATCH[2,-1]} ))}}") + dpath_tmp=( "${(@Mq)dev_tmp:#/*}" ) + dev_tmp=( "${(@q)dev_tmp:#/*}" ) + + _alternative \ + 'device-paths: device path:compadd -a dpath_tmp' \ + 'directories:mount point:compadd -a mp_tmp' +} + + +_udevadm_command(){ + local -a _udevadm_cmds + _udevadm_cmds=( + 'info:query sysfs or the udev database' + 'trigger:request events from the kernel' + 'settle:wait for the event queue to finish' + 'control:control the udev daemon' + 'monitor:listen to kernel and udev events' + 'test:test an event run' + 'test-builtin:test a built-in command' + ) + + if ((CURRENT == 1)); then + _describe -t commands 'udevadm commands' _udevadm_cmds + else + local curcontext="$curcontext" + cmd="${${_udevadm_cmds[(r)$words[1]:*]%%:*}}" + if (($#cmd)); then + if (( $+functions[_udevadm_$cmd] )); then + _udevadm_$cmd + else + _message "no options for $cmd" + fi + else + _message "no more options" + fi + fi +} + + +_arguments \ + '--debug[Print debug messages to stderr]' \ + '--version[Print version number]' \ + '--help[Print help text]' \ + '*::udevadm commands:_udevadm_command' diff --git a/src/grp-udev/udevadm/udevadm.xml b/src/grp-udev/udevadm/udevadm.xml new file mode 100644 index 0000000000..1c7921f5bd --- /dev/null +++ b/src/grp-udev/udevadm/udevadm.xml @@ -0,0 +1,576 @@ + + + + + + udevadm + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + udevadm + 8 + + + + udevadmudev management tool + + + + + udevadm + + + + + + udevadm info options + + + udevadm trigger options + + + udevadm settle options + + + udevadm control command + + + udevadm monitor options + + + udevadm test options devpath + + + udevadm test-builtin options command devpath + + + + Description + udevadm expects a command and command + specific options. It controls the runtime behavior of + systemd-udevd, requests kernel events, manages + the event queue, and provides simple debugging mechanisms. + + + Options + + + + + Print debug messages to standard error. + + + + + + Print version number. + + + + + + + Print help text. + + + + + udevadm info + <arg choice="opt"><replaceable>options</replaceable></arg> + <arg choice="opt"><replaceable>devpath</replaceable>|<replaceable>file</replaceable></arg> + + + Queries the udev database for device information + stored in the udev database. It can also query the properties + of a device from its sysfs representation to help creating udev + rules that match this device. + + + + + + Query the database for the specified type of device + data. It needs the or + to identify the specified device. + Valid TYPEs are: + name, symlink, + path, property, + all. + + + + + + + The /sys path of the device to + query, e.g. + /sys/class/block/sda. + Note that this option usually is not very useful, since + udev can guess the type of the + argument, so udevadm + --devpath=/class/block/sda is equivalent to + udevadm /sys/class/block/sda. + + + + + + + The name of the device node or a symlink to query, + e.g. /dev/sda. + Note that this option usually is not very useful, since + udev can guess the type of the + argument, so udevadm --name=sda is + equivalent to udevadm /dev/sda. + + + + + + + Print absolute paths in name or symlink + query. + + + + + + + Print all sysfs properties of the specified device that can be used + in udev rules to match the specified device. It prints all devices + along the chain, up to the root of sysfs that can be used in udev rules. + + + + + + + Print output as key/value pairs. Values are enclosed in single quotes. + + + + + + + Add a prefix to the key name of exported values. + + + + + + + Print major/minor numbers of the underlying device, where the file + lives on. + + + + + + + Export the content of the udev database. + + + + + + + Cleanup the udev database. + + + + + + Print version. + + + + + + + Print help text. + + + + + In addition, an optional positional argument can be used + to specify a device name or a sys path. It must start with + /dev or /sys + respectively. + + + udevadm trigger + <arg choice="opt"><replaceable>options</replaceable></arg> + <arg choice="opt" rep="repeat"><replaceable>devpath</replaceable>|<replaceable>file</replaceable></arg> + Request device events from the kernel. Primarily used to replay events at system coldplug time. + + + + + + Print the list of devices which will be triggered. + + + + + + + Do not actually trigger the event. + + + + + + + Trigger a specific type of devices. Valid types are: + devices, subsystems. + The default value is devices. + + + + + + + Type of event to be triggered. The default value is + change. + + + + + + + Trigger events for devices which belong to a + matching subsystem. This option can be specified multiple + times and supports shell style pattern matching. + + + + + + + Do not trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching. + + + + + + + Trigger events for devices with a matching sysfs + attribute. If a value is specified along with the + attribute name, the content of the attribute is matched + against the given value using shell style pattern + matching. If no value is specified, the existence of the + sysfs attribute is checked. This option can be specified + multiple times. + + + + + + + Do not trigger events for devices with a matching + sysfs attribute. If a value is specified along with the + attribute name, the content of the attribute is matched + against the given value using shell style pattern + matching. If no value is specified, the existence of the + sysfs attribute is checked. This option can be specified + multiple times. + + + + + + + Trigger events for devices with a matching property + value. This option can be specified multiple times and + supports shell style pattern matching. + + + + + + + Trigger events for devices with a matching tag. This + option can be specified multiple times. + + + + + + + Trigger events for devices with a matching sys + device path. This option can be specified multiple times + and supports shell style pattern matching. + + + + + + Trigger events for devices with a matching + device path. This option can be specified multiple + times. + + + + + + + Trigger events for all children of a given + device. + + + + + + + Print help text. + + + + + In addition, optional positional arguments can be used + to specify device names or sys paths. They must start with + /dev or /sys + respectively. + + + udevadm settle + <arg choice="opt"><replaceable>options</replaceable></arg> + + Watches the udev event queue, and exits if all current events are handled. + + + + + + Maximum number of seconds to wait for the event + queue to become empty. The default value is 120 seconds. A + value of 0 will check if the queue is empty and always + return immediately. + + + + + + + Stop waiting if file exists. + + + + + + + Print help text. + + + + + + udevadm control <replaceable>command</replaceable> + Modify the internal state of the running udev daemon. + + + + + + Signal and wait for systemd-udevd to exit. + + + + + + + Set the internal log level of + systemd-udevd. Valid values are the + numerical syslog priorities or their textual + representations: , + , , + , , + , , and + . + + + + + + + Signal systemd-udevd to stop executing new events. Incoming events + will be queued. + + + + + + + Signal systemd-udevd to enable the execution of events. + + + + + + + Signal systemd-udevd to reload the rules files and other databases like the kernel + module index. Reloading rules and databases does not apply any changes to already + existing devices; the new configuration will only be applied to new events. + + + + + + + Set a global property for all events. + + + + + value + + Set the maximum number of events, systemd-udevd will handle at the + same time. + + + + seconds + + The maximum number of seconds to wait for a reply from systemd-udevd. + + + + + + + Print help text. + + + + + + udevadm monitor + <arg choice="opt"><replaceable>options</replaceable></arg> + + Listens to the kernel uevents and events sent out by a udev rule + and prints the devpath of the event to the console. It can be used to analyze the + event timing, by comparing the timestamps of the kernel uevent and the udev event. + + + + + + + Print the kernel uevents. + + + + + + + Print the udev event after the rule processing. + + + + + + + Also print the properties of the event. + + + + + + + Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass. + + + + + + + Filter events by property. Only udev events with a given tag attached will pass. + + + + + + + Print help text. + + + + + + udevadm test + <arg choice="opt"><replaceable>options</replaceable></arg> + <arg><replaceable>devpath</replaceable></arg> + + Simulate a udev event run for the given device, and print debug output. + + + + + + The action string. + + + + + + + Specify when udevadm should resolve names of users + and groups. When set to early (the + default), names will be resolved when the rules are + parsed. When set to late, names will + be resolved for every event. When set to + never, names will never be resolved + and all devices will be owned by root. + + + + + + + Print help text. + + + + + + udevadm test-builtin + <arg choice="opt"><replaceable>options</replaceable></arg> + <arg><replaceable>command</replaceable></arg> + <arg><replaceable>devpath</replaceable></arg> + + Run a built-in command COMMAND + for device DEVPATH, and print debug + output. + + + + + + Print help text. + + + + + + + + See Also + + udev7 + , + + systemd-udevd.service8 + + + diff --git a/src/grp-udev/v4l_id/60-persistent-v4l.rules b/src/grp-udev/v4l_id/60-persistent-v4l.rules new file mode 100644 index 0000000000..93c5ee8c27 --- /dev/null +++ b/src/grp-udev/v4l_id/60-persistent-v4l.rules @@ -0,0 +1,20 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_v4l_end" +SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end" +ENV{MAJOR}=="", GOTO="persistent_v4l_end" + +IMPORT{program}="v4l_id $devnode" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}" + +# check for valid "index" number +TEST!="index", GOTO="persistent_v4l_end" +ATTR{index}!="?*", GOTO="persistent_v4l_end" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}" +ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}" + +LABEL="persistent_v4l_end" diff --git a/src/grp-udev/v4l_id/Makefile b/src/grp-udev/v4l_id/Makefile new file mode 100644 index 0000000000..0641af8065 --- /dev/null +++ b/src/grp-udev/v4l_id/Makefile @@ -0,0 +1,38 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +v4l_id_SOURCES = \ + src/udev/v4l_id/v4l_id.c + +v4l_id_LDADD = \ + libshared.la + +udevlibexec_PROGRAMS += \ + v4l_id + +dist_udevrules_DATA += \ + rules/60-persistent-v4l.rules + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-udev/v4l_id/v4l_id.c b/src/grp-udev/v4l_id/v4l_id.c new file mode 100644 index 0000000000..d1aad403b9 --- /dev/null +++ b/src/grp-udev/v4l_id/v4l_id.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 Kay Sievers + * Copyright (c) 2009 Filippo Argiolas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/util.h" + +int main(int argc, char *argv[]) { + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + {} + }; + _cleanup_close_ int fd = -1; + char *device; + struct v4l2_capability v2cap; + int c; + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + case 'h': + printf("%s [-h,--help] \n\n" + "Video4Linux device identification.\n\n" + " -h Print this message\n" + , program_invocation_short_name); + return 0; + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + device = argv[optind]; + if (device == NULL) + return 2; + + fd = open(device, O_RDONLY); + if (fd < 0) + return 3; + + if (ioctl(fd, VIDIOC_QUERYCAP, &v2cap) == 0) { + printf("ID_V4L_VERSION=2\n"); + printf("ID_V4L_PRODUCT=%s\n", v2cap.card); + printf("ID_V4L_CAPABILITIES=:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) + printf("capture:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) + printf("video_output:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) + printf("video_overlay:"); + if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) + printf("audio:"); + if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) + printf("tuner:"); + if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) + printf("radio:"); + printf("\n"); + } + + return 0; +} diff --git a/src/grp-utils/Makefile b/src/grp-utils/Makefile new file mode 100644 index 0000000000..9d510cf3cc --- /dev/null +++ b/src/grp-utils/Makefile @@ -0,0 +1,32 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += systemd-ac-power +nested.subdirs += systemd-escape +nested.subdirs += systemd-notify +nested.subdirs += systemd-path +nested.subdirs += systemd-socket-activate + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-ac-power/Makefile b/src/grp-utils/systemd-ac-power/Makefile new file mode 100644 index 0000000000..4586f01612 --- /dev/null +++ b/src/grp-utils/systemd-ac-power/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-ac-power +systemd_ac_power_SOURCES = \ + src/ac-power/ac-power.c + +systemd_ac_power_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-ac-power/ac-power.c b/src/grp-utils/systemd-ac-power/ac-power.c new file mode 100644 index 0000000000..945c318f66 --- /dev/null +++ b/src/grp-utils/systemd-ac-power/ac-power.c @@ -0,0 +1,35 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/util.h" + +int main(int argc, char *argv[]) { + int r; + + /* This is mostly intended to be used for scripts which want + * to detect whether AC power is plugged in or not. */ + + r = on_ac_power(); + if (r < 0) { + log_error_errno(r, "Failed to read AC status: %m"); + return EXIT_FAILURE; + } + + return r != 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/grp-utils/systemd-escape/Makefile b/src/grp-utils/systemd-escape/Makefile new file mode 100644 index 0000000000..b59575db9b --- /dev/null +++ b/src/grp-utils/systemd-escape/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemd-escape + +systemd_escape_SOURCES = \ + src/escape/escape.c + +systemd_escape_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-escape/escape.c b/src/grp-utils/systemd-escape/escape.c new file mode 100644 index 0000000000..479ea0e87c --- /dev/null +++ b/src/grp-utils/systemd-escape/escape.c @@ -0,0 +1,237 @@ +/*** + This file is part of systemd. + + Copyright 2014 Michael Biebl + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" + +static enum { + ACTION_ESCAPE, + ACTION_UNESCAPE, + ACTION_MANGLE +} arg_action = ACTION_ESCAPE; +static const char *arg_suffix = NULL; +static const char *arg_template = NULL; +static bool arg_path = false; + +static void help(void) { + printf("%s [OPTIONS...] [NAME...]\n\n" + "Show system and user paths.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --suffix=SUFFIX Unit suffix to append to escaped strings\n" + " --template=TEMPLATE Insert strings as instance into template\n" + " -u --unescape Unescape strings\n" + " -m --mangle Mangle strings\n" + " -p --path When escaping/unescaping assume the string is a path\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_SUFFIX, + ARG_TEMPLATE + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "suffix", required_argument, NULL, ARG_SUFFIX }, + { "template", required_argument, NULL, ARG_TEMPLATE }, + { "unescape", no_argument, NULL, 'u' }, + { "mangle", no_argument, NULL, 'm' }, + { "path", no_argument, NULL, 'p' }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hump", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_SUFFIX: + + if (unit_type_from_string(optarg) < 0) { + log_error("Invalid unit suffix type %s.", optarg); + return -EINVAL; + } + + arg_suffix = optarg; + break; + + case ARG_TEMPLATE: + + if (!unit_name_is_valid(optarg, UNIT_NAME_TEMPLATE)) { + log_error("Template name %s is not valid.", optarg); + return -EINVAL; + } + + arg_template = optarg; + break; + + case 'u': + arg_action = ACTION_UNESCAPE; + break; + + case 'm': + arg_action = ACTION_MANGLE; + break; + + case 'p': + arg_path = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind >= argc) { + log_error("Not enough arguments."); + return -EINVAL; + } + + if (arg_template && arg_suffix) { + log_error("--suffix= and --template= may not be combined."); + return -EINVAL; + } + + if ((arg_template || arg_suffix) && arg_action != ACTION_ESCAPE) { + log_error("--suffix= and --template= are not compatible with --unescape or --mangle."); + return -EINVAL; + } + + if (arg_path && !IN_SET(arg_action, ACTION_ESCAPE, ACTION_UNESCAPE)) { + log_error("--path may not be combined with --mangle."); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + char **i; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + STRV_FOREACH(i, argv + optind) { + _cleanup_free_ char *e = NULL; + + switch (arg_action) { + + case ACTION_ESCAPE: + if (arg_path) { + r = unit_name_path_escape(*i, &e); + if (r < 0) { + log_error_errno(r, "Failed to escape string: %m"); + goto finish; + } + } else { + e = unit_name_escape(*i); + if (!e) { + r = log_oom(); + goto finish; + } + } + + if (arg_template) { + char *x; + + r = unit_name_replace_instance(arg_template, e, &x); + if (r < 0) { + log_error_errno(r, "Failed to replace instance: %m"); + goto finish; + } + + free(e); + e = x; + } else if (arg_suffix) { + char *x; + + x = strjoin(e, ".", arg_suffix, NULL); + if (!x) { + r = log_oom(); + goto finish; + } + + free(e); + e = x; + } + + break; + + case ACTION_UNESCAPE: + if (arg_path) + r = unit_name_path_unescape(*i, &e); + else + r = unit_name_unescape(*i, &e); + + if (r < 0) { + log_error_errno(r, "Failed to unescape string: %m"); + goto finish; + } + break; + + case ACTION_MANGLE: + r = unit_name_mangle(*i, UNIT_NAME_NOGLOB, &e); + if (r < 0) { + log_error_errno(r, "Failed to mangle name: %m"); + goto finish; + } + break; + } + + if (i != argv+optind) + fputc(' ', stdout); + + fputs(e, stdout); + } + + fputc('\n', stdout); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-utils/systemd-escape/systemd-escape.xml b/src/grp-utils/systemd-escape/systemd-escape.xml new file mode 100644 index 0000000000..dbb3869a24 --- /dev/null +++ b/src/grp-utils/systemd-escape/systemd-escape.xml @@ -0,0 +1,178 @@ + + + + + + + + + systemd-escape + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-escape + 1 + + + + systemd-escape + Escape strings for usage in system unit names + + + + + systemd-escape + OPTIONS + STRING + + + + + Description + + systemd-escape may be used to escape + strings for inclusion in systemd unit names. The command may be + used to escape and to undo escaping of strings. + + The command takes any number of strings on the command line, + and will process them individually, one after another. It will + output them separated by spaces to stdout. + + By default, this command will escape the strings passed, + unless is passed which results in the + inverse operation being applied. If is given, a + special mode of escaping is applied instead, which assumes the + string is already escaped but will escape everything that + appears obviously non-escaped. + + + + Options + + The following options are understood: + + + + + + Appends the specified unit type suffix to the + escaped string. Takes one of the unit types supported by + systemd, such as .service or + .mount. May not be used in conjunction with + , or + . + + + + + + Inserts the escaped strings in a unit name + template. Takes a unit name template such as + foobar@.service May not be used in + conjunction with , + or + . + + + + + + + When escaping or unescaping a string, assume + it refers to a file system path. This enables special + processing of the initial / of the + path. + + + + + + Instead of escaping the specified strings, + undo the escaping, reversing the operation. May not be used in + conjunction with , + or + . + + + + + + Like , but only + escape characters that are obviously not escaped yet, and + possibly automatically append an appropriate unit type suffix + to the string. May not be used in conjunction with + , or + . + + + + + + + + + + Examples + + Escape a single string: + $ systemd-escape 'Hallöchen, Meister' +Hall\xc3\xb6chen\x2c\x20Meister + + To undo escaping on a single string: + $ systemd-escape -u 'Hall\xc3\xb6chen\x2c\x20Meister' +Hallöchen, Meister + + To generate the mount unit for a path: + $ systemd-escape -p --suffix=mount "/tmp//waldi/foobar/" +tmp-waldi-foobar.mount + + To generate instance names of three strings + $ systemd-escape --template=systemd-nspawn@.service 'My Container 1' 'containerb' 'container/III' +systemd-nspawn@My\x20Container\x201.service systemd-nspawn@containerb.service systemd-nspawn@container-III.service + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemctl1 + + + + diff --git a/src/grp-utils/systemd-notify/Makefile b/src/grp-utils/systemd-notify/Makefile new file mode 100644 index 0000000000..c46897b9e7 --- /dev/null +++ b/src/grp-utils/systemd-notify/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemd-notify +systemd_notify_SOURCES = \ + src/notify/notify.c + +systemd_notify_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-notify/notify.c b/src/grp-utils/systemd-notify/notify.c new file mode 100644 index 0000000000..4db49508b3 --- /dev/null +++ b/src/grp-utils/systemd-notify/notify.c @@ -0,0 +1,203 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/env-util.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static bool arg_ready = false; +static pid_t arg_pid = 0; +static const char *arg_status = NULL; +static bool arg_booted = false; + +static void help(void) { + printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n" + "Notify the init system about service status updates.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --ready Inform the init system about service start-up completion\n" + " --pid[=PID] Set main pid of daemon\n" + " --status=TEXT Set status text\n" + " --booted Check if the system was booted up with systemd\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_READY = 0x100, + ARG_VERSION, + ARG_PID, + ARG_STATUS, + ARG_BOOTED, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "ready", no_argument, NULL, ARG_READY }, + { "pid", optional_argument, NULL, ARG_PID }, + { "status", required_argument, NULL, ARG_STATUS }, + { "booted", no_argument, NULL, ARG_BOOTED }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_READY: + arg_ready = true; + break; + + case ARG_PID: + + if (optarg) { + if (parse_pid(optarg, &arg_pid) < 0) { + log_error("Failed to parse PID %s.", optarg); + return -EINVAL; + } + } else + arg_pid = getppid(); + + break; + + case ARG_STATUS: + arg_status = optarg; + break; + + case ARG_BOOTED: + arg_booted = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + } + + if (optind >= argc && + !arg_ready && + !arg_status && + !arg_pid && + !arg_booted) { + help(); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char* argv[]) { + _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL; + _cleanup_strv_free_ char **final_env = NULL; + char* our_env[4]; + unsigned i = 0; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_booted) + return sd_booted() <= 0; + + if (arg_ready) + our_env[i++] = (char*) "READY=1"; + + if (arg_status) { + status = strappend("STATUS=", arg_status); + if (!status) { + r = log_oom(); + goto finish; + } + + our_env[i++] = status; + } + + if (arg_pid > 0) { + if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0) { + r = log_oom(); + goto finish; + } + + our_env[i++] = cpid; + } + + our_env[i++] = NULL; + + final_env = strv_env_merge(2, our_env, argv + optind); + if (!final_env) { + r = log_oom(); + goto finish; + } + + if (strv_length(final_env) <= 0) { + r = 0; + goto finish; + } + + n = strv_join(final_env, "\n"); + if (!n) { + r = log_oom(); + goto finish; + } + + r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n); + if (r < 0) { + log_error_errno(r, "Failed to notify init system: %m"); + goto finish; + } else if (r == 0) { + log_error("No status data could be sent: $NOTIFY_SOCKET was not set"); + r = -EOPNOTSUPP; + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-utils/systemd-notify/systemd-notify.completion.zsh b/src/grp-utils/systemd-notify/systemd-notify.completion.zsh new file mode 100644 index 0000000000..910ddfa34c --- /dev/null +++ b/src/grp-utils/systemd-notify/systemd-notify.completion.zsh @@ -0,0 +1,12 @@ +#compdef systemd-notify + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--ready[Inform the init system about service start-up completion.]' \ + '--pid=[Inform the init system about the main PID of the daemon]:daemon main PID:_pids' \ + '--status=[Send a free-form status string for the daemon to the init systemd]:status string:' \ + '--booted[Returns 0 if the system was booted up with systemd]' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/grp-utils/systemd-notify/systemd-notify.xml b/src/grp-utils/systemd-notify/systemd-notify.xml new file mode 100644 index 0000000000..a5f4077166 --- /dev/null +++ b/src/grp-utils/systemd-notify/systemd-notify.xml @@ -0,0 +1,185 @@ + + + + + + + + + systemd-notify + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-notify + 1 + + + + systemd-notify + Notify service manager about start-up completion and other daemon status changes + + + + + systemd-notify OPTIONS VARIABLE=VALUE + + + + + Description + + systemd-notify may be called by daemon + scripts to notify the init system about status changes. It can be + used to send arbitrary information, encoded in an + environment-block-like list of strings. Most importantly, it can be + used for start-up completion notification. + + This is mostly just a wrapper around + sd_notify() and makes this functionality + available to shell scripts. For details see + sd_notify3. + + + The command line may carry a list of environment variables + to send as part of the status update. + + Note that systemd will refuse reception of status updates + from this command unless NotifyAccess=all is + set for the service unit this command is called from. + + + + + Options + + The following options are understood: + + + + + + Inform the init system about service start-up + completion. This is equivalent to systemd-notify + READY=1. For details about the semantics of this + option see + sd_notify3. + + + + + + Inform the init system about the main PID of + the daemon. Takes a PID as argument. If the argument is + omitted, the PID of the process that invoked + systemd-notify is used. This is equivalent + to systemd-notify MAINPID=$PID. For details + about the semantics of this option see + sd_notify3. + + + + + + Send a free-form status string for the daemon + to the init systemd. This option takes the status string as + argument. This is equivalent to systemd-notify + STATUS=.... For details about the semantics of this + option see + sd_notify3. + + + + + + Returns 0 if the system was booted up with + systemd, non-zero otherwise. If this option is passed, no + message is sent. This option is hence unrelated to the other + options. For details about the semantics of this option, see + sd_booted3. An + alternate way to check for this state is to call + systemctl1 + with the is-system-running command. It will + return offline if the system was not booted + with systemd. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + Example + + + Start-up Notification and Status Updates + + A simple shell daemon that sends start-up notifications + after having set up its communication channel. During runtime it + sends further status updates to the init system: + + #!/bin/bash + +mkfifo /tmp/waldo +systemd-notify --ready --status="Waiting for data..." + +while : ; do + read a < /tmp/waldo + systemd-notify --status="Processing $a" + + # Do something with $a ... + + systemd-notify --status="Waiting for data..." +done + + + + + See Also + + systemd1, + systemctl1, + systemd.unit5, + sd_notify3, + sd_booted3 + + + + diff --git a/src/grp-utils/systemd-path/Makefile b/src/grp-utils/systemd-path/Makefile new file mode 100644 index 0000000000..e2e40e0121 --- /dev/null +++ b/src/grp-utils/systemd-path/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-path + +systemd_path_SOURCES = \ + src/path/path.c + +systemd_path_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-path/_sd-common.h b/src/grp-utils/systemd-path/_sd-common.h new file mode 120000 index 0000000000..d2b5d6f4e4 --- /dev/null +++ b/src/grp-utils/systemd-path/_sd-common.h @@ -0,0 +1 @@ +../../libsystemd/include/systemd/_sd-common.h \ No newline at end of file diff --git a/src/grp-utils/systemd-path/path.c b/src/grp-utils/systemd-path/path.c new file mode 100644 index 0000000000..ed824fa857 --- /dev/null +++ b/src/grp-utils/systemd-path/path.c @@ -0,0 +1,198 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "sd-path.h" + +static const char *arg_suffix = NULL; + +static const char* const path_table[_SD_PATH_MAX] = { + [SD_PATH_TEMPORARY] = "temporary", + [SD_PATH_TEMPORARY_LARGE] = "temporary-large", + [SD_PATH_SYSTEM_BINARIES] = "system-binaries", + [SD_PATH_SYSTEM_INCLUDE] = "system-include", + [SD_PATH_SYSTEM_LIBRARY_PRIVATE] = "system-library-private", + [SD_PATH_SYSTEM_LIBRARY_ARCH] = "system-library-arch", + [SD_PATH_SYSTEM_SHARED] = "system-shared", + [SD_PATH_SYSTEM_CONFIGURATION_FACTORY] = "system-configuration-factory", + [SD_PATH_SYSTEM_STATE_FACTORY] = "system-state-factory", + [SD_PATH_SYSTEM_CONFIGURATION] = "system-configuration", + [SD_PATH_SYSTEM_RUNTIME] = "system-runtime", + [SD_PATH_SYSTEM_RUNTIME_LOGS] = "system-runtime-logs", + [SD_PATH_SYSTEM_STATE_PRIVATE] = "system-state-private", + [SD_PATH_SYSTEM_STATE_LOGS] = "system-state-logs", + [SD_PATH_SYSTEM_STATE_CACHE] = "system-state-cache", + [SD_PATH_SYSTEM_STATE_SPOOL] = "system-state-spool", + [SD_PATH_USER_BINARIES] = "user-binaries", + [SD_PATH_USER_LIBRARY_PRIVATE] = "user-library-private", + [SD_PATH_USER_LIBRARY_ARCH] = "user-library-arch", + [SD_PATH_USER_SHARED] = "user-shared", + [SD_PATH_USER_CONFIGURATION] = "user-configuration", + [SD_PATH_USER_RUNTIME] = "user-runtime", + [SD_PATH_USER_STATE_CACHE] = "user-state-cache", + [SD_PATH_USER] = "user", + [SD_PATH_USER_DOCUMENTS] = "user-documents", + [SD_PATH_USER_MUSIC] = "user-music", + [SD_PATH_USER_PICTURES] = "user-pictures", + [SD_PATH_USER_VIDEOS] = "user-videos", + [SD_PATH_USER_DOWNLOAD] = "user-download", + [SD_PATH_USER_PUBLIC] = "user-public", + [SD_PATH_USER_TEMPLATES] = "user-templates", + [SD_PATH_USER_DESKTOP] = "user-desktop", + [SD_PATH_SEARCH_BINARIES] = "search-binaries", + [SD_PATH_SEARCH_LIBRARY_PRIVATE] = "search-library-private", + [SD_PATH_SEARCH_LIBRARY_ARCH] = "search-library-arch", + [SD_PATH_SEARCH_SHARED] = "search-shared", + [SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory", + [SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory", + [SD_PATH_SEARCH_CONFIGURATION] = "search-configuration", +}; + +static int list_homes(void) { + uint64_t i = 0; + int r = 0; + + for (i = 0; i < ELEMENTSOF(path_table); i++) { + _cleanup_free_ char *p = NULL; + int q; + + q = sd_path_home(i, arg_suffix, &p); + if (q == -ENXIO) + continue; + if (q < 0) { + log_error_errno(r, "Failed to query %s: %m", path_table[i]); + r = q; + continue; + } + + printf("%s: %s\n", path_table[i], p); + } + + return r; +} + +static int print_home(const char *n) { + uint64_t i = 0; + int r; + + for (i = 0; i < ELEMENTSOF(path_table); i++) { + if (streq(path_table[i], n)) { + _cleanup_free_ char *p = NULL; + + r = sd_path_home(i, arg_suffix, &p); + if (r < 0) + return log_error_errno(r, "Failed to query %s: %m", n); + + printf("%s\n", p); + return 0; + } + } + + log_error("Path %s not known.", n); + return -EOPNOTSUPP; +} + +static void help(void) { + printf("%s [OPTIONS...] [NAME...]\n\n" + "Show system and user paths.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --suffix=SUFFIX Suffix to append to paths\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_SUFFIX, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "suffix", required_argument, NULL, ARG_SUFFIX }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_SUFFIX: + arg_suffix = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +int main(int argc, char* argv[]) { + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (argc > optind) { + int i, q; + + for (i = optind; i < argc; i++) { + q = print_home(argv[i]); + if (q < 0) + r = q; + } + } else + r = list_homes(); + + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/grp-utils/systemd-path/sd-path.c b/src/grp-utils/systemd-path/sd-path.c new file mode 100644 index 0000000000..2e0a0a7cfd --- /dev/null +++ b/src/grp-utils/systemd-path/sd-path.c @@ -0,0 +1,638 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/missing.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "sd-path.h" + +static int from_environment(const char *envname, const char *fallback, const char **ret) { + assert(ret); + + if (envname) { + const char *e; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + if (fallback) { + *ret = fallback; + return 0; + } + + return -ENXIO; +} + +static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { + _cleanup_free_ char *h = NULL; + char *cc = NULL; + int r; + + assert(suffix); + assert(buffer); + assert(ret); + + if (envname) { + const char *e = NULL; + + e = secure_getenv(envname); + if (e && path_is_absolute(e)) { + *ret = e; + return 0; + } + } + + r = get_home_dir(&h); + if (r < 0) + return r; + + if (endswith(h, "/")) + cc = strappend(h, suffix); + else + cc = strjoin(h, "/", suffix, NULL); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + return 0; +} + +static int from_user_dir(const char *field, char **buffer, const char **ret) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *b = NULL; + _cleanup_free_ const char *fn = NULL; + const char *c = NULL; + char line[LINE_MAX]; + size_t n; + int r; + + assert(field); + assert(buffer); + assert(ret); + + r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); + if (r < 0) + return r; + + fn = strappend(c, "/user-dirs.dirs"); + if (!fn) + return -ENOMEM; + + f = fopen(fn, "re"); + if (!f) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + /* This is an awful parse, but it follows closely what + * xdg-user-dirs does upstream */ + + n = strlen(field); + FOREACH_LINE(line, f, return -errno) { + char *l, *p, *e; + + l = strstrip(line); + + if (!strneq(l, field, n)) + continue; + + p = l + n; + p += strspn(p, WHITESPACE); + + if (*p != '=') + continue; + p++; + + p += strspn(p, WHITESPACE); + + if (*p != '"') + continue; + p++; + + e = strrchr(p, '"'); + if (!e) + continue; + *e = 0; + + /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */ + if (startswith(p, "$HOME/")) { + _cleanup_free_ char *h = NULL; + char *cc; + + r = get_home_dir(&h); + if (r < 0) + return r; + + cc = strappend(h, p+5); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + return 0; + } else if (streq(p, "$HOME")) { + + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + } else if (path_is_absolute(p)) { + char *copy; + + copy = strdup(p); + if (!copy) + return -ENOMEM; + + *buffer = copy; + *ret = copy; + return 0; + } + } + +fallback: + /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */ + if (streq(field, "XDG_DESKTOP_DIR")) { + _cleanup_free_ char *h = NULL; + char *cc; + + r = get_home_dir(&h); + if (r < 0) + return r; + + cc = strappend(h, "/Desktop"); + if (!cc) + return -ENOMEM; + + *buffer = cc; + *ret = cc; + } else { + + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + } + + return 0; +} + +static int get_path(uint64_t type, char **buffer, const char **ret) { + int r; + + assert(buffer); + assert(ret); + + switch (type) { + + case SD_PATH_TEMPORARY: + return from_environment("TMPDIR", "/tmp", ret); + + case SD_PATH_TEMPORARY_LARGE: + return from_environment("TMPDIR", "/var/tmp", ret); + + case SD_PATH_SYSTEM_BINARIES: + *ret = "/usr/bin"; + return 0; + + case SD_PATH_SYSTEM_INCLUDE: + *ret = "/usr/include"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_PRIVATE: + *ret = "/usr/lib"; + return 0; + + case SD_PATH_SYSTEM_LIBRARY_ARCH: + *ret = LIBDIR; + return 0; + + case SD_PATH_SYSTEM_SHARED: + *ret = "/usr/share"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION_FACTORY: + *ret = "/usr/share/factory/etc"; + return 0; + + case SD_PATH_SYSTEM_STATE_FACTORY: + *ret = "/usr/share/factory/var"; + return 0; + + case SD_PATH_SYSTEM_CONFIGURATION: + *ret = "/etc"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME: + *ret = "/run"; + return 0; + + case SD_PATH_SYSTEM_RUNTIME_LOGS: + *ret = "/run/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_PRIVATE: + *ret = "/var/lib"; + return 0; + + case SD_PATH_SYSTEM_STATE_LOGS: + *ret = "/var/log"; + return 0; + + case SD_PATH_SYSTEM_STATE_CACHE: + *ret = "/var/cache"; + return 0; + + case SD_PATH_SYSTEM_STATE_SPOOL: + *ret = "/var/spool"; + return 0; + + case SD_PATH_USER_BINARIES: + return from_home_dir(NULL, ".local/bin", buffer, ret); + + case SD_PATH_USER_LIBRARY_PRIVATE: + return from_home_dir(NULL, ".local/lib", buffer, ret); + + case SD_PATH_USER_LIBRARY_ARCH: + return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret); + + case SD_PATH_USER_SHARED: + return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret); + + case SD_PATH_USER_CONFIGURATION: + return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret); + + case SD_PATH_USER_RUNTIME: + return from_environment("XDG_RUNTIME_DIR", NULL, ret); + + case SD_PATH_USER_STATE_CACHE: + return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret); + + case SD_PATH_USER: + r = get_home_dir(buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + + case SD_PATH_USER_DOCUMENTS: + return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret); + + case SD_PATH_USER_MUSIC: + return from_user_dir("XDG_MUSIC_DIR", buffer, ret); + + case SD_PATH_USER_PICTURES: + return from_user_dir("XDG_PICTURES_DIR", buffer, ret); + + case SD_PATH_USER_VIDEOS: + return from_user_dir("XDG_VIDEOS_DIR", buffer, ret); + + case SD_PATH_USER_DOWNLOAD: + return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret); + + case SD_PATH_USER_PUBLIC: + return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret); + + case SD_PATH_USER_TEMPLATES: + return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret); + + case SD_PATH_USER_DESKTOP: + return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); + } + + return -EOPNOTSUPP; +} + +_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { + char *buffer = NULL, *cc; + const char *ret; + int r; + + assert_return(path, -EINVAL); + + if (IN_SET(type, + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION)) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_path_search(type, suffix, &l); + if (r < 0) + return r; + + buffer = strv_join(l, ":"); + if (!buffer) + return -ENOMEM; + + *path = buffer; + return 0; + } + + r = get_path(type, &buffer, &ret); + if (r < 0) + return r; + + if (!suffix) { + if (!buffer) { + buffer = strdup(ret); + if (!buffer) + return -ENOMEM; + } + + *path = buffer; + return 0; + } + + suffix += strspn(suffix, "/"); + + if (endswith(ret, "/")) + cc = strappend(ret, suffix); + else + cc = strjoin(ret, "/", suffix, NULL); + + free(buffer); + + if (!cc) + return -ENOMEM; + + *path = cc; + return 0; +} + +static int search_from_environment( + char ***list, + const char *env_home, + const char *home_suffix, + const char *env_search, + bool env_search_sufficient, + const char *first, ...) { + + const char *e; + char *h = NULL; + char **l = NULL; + int r; + + assert(list); + + if (env_search) { + e = secure_getenv(env_search); + if (e) { + l = strv_split(e, ":"); + if (!l) + return -ENOMEM; + + if (env_search_sufficient) { + *list = l; + return 0; + } + } + } + + if (!l && first) { + va_list ap; + + va_start(ap, first); + l = strv_new_ap(first, ap); + va_end(ap); + + if (!l) + return -ENOMEM; + } + + if (env_home) { + e = secure_getenv(env_home); + if (e && path_is_absolute(e)) { + h = strdup(e); + if (!h) { + strv_free(l); + return -ENOMEM; + } + } + } + + if (!h && home_suffix) { + e = secure_getenv("HOME"); + if (e && path_is_absolute(e)) { + if (endswith(e, "/")) + h = strappend(e, home_suffix); + else + h = strjoin(e, "/", home_suffix, NULL); + + if (!h) { + strv_free(l); + return -ENOMEM; + } + } + } + + if (h) { + r = strv_consume_prepend(&l, h); + if (r < 0) { + strv_free(l); + return -ENOMEM; + } + } + + *list = l; + return 0; +} + +static int get_search(uint64_t type, char ***list) { + + assert(list); + + switch(type) { + + case SD_PATH_SEARCH_BINARIES: + return search_from_environment(list, + NULL, + ".local/bin", + "PATH", + true, + "/usr/local/sbin", + "/usr/local/bin", + "/usr/sbin", + "/usr/bin", +#ifdef HAVE_SPLIT_USR + "/sbin", + "/bin", +#endif + NULL); + + case SD_PATH_SEARCH_LIBRARY_PRIVATE: + return search_from_environment(list, + NULL, + ".local/lib", + NULL, + false, + "/usr/local/lib", + "/usr/lib", +#ifdef HAVE_SPLIT_USR + "/lib", +#endif + NULL); + + case SD_PATH_SEARCH_LIBRARY_ARCH: + return search_from_environment(list, + NULL, + ".local/lib/" LIB_ARCH_TUPLE, + "LD_LIBRARY_PATH", + true, + LIBDIR, +#ifdef HAVE_SPLIT_USR + ROOTLIBDIR, +#endif + NULL); + + case SD_PATH_SEARCH_SHARED: + return search_from_environment(list, + "XDG_DATA_HOME", + ".local/share", + "XDG_DATA_DIRS", + false, + "/usr/local/share", + "/usr/share", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/etc", + "/usr/share/factory/etc", + NULL); + + case SD_PATH_SEARCH_STATE_FACTORY: + return search_from_environment(list, + NULL, + NULL, + NULL, + false, + "/usr/local/share/factory/var", + "/usr/share/factory/var", + NULL); + + case SD_PATH_SEARCH_CONFIGURATION: + return search_from_environment(list, + "XDG_CONFIG_HOME", + ".config", + "XDG_CONFIG_DIRS", + false, + "/etc", + NULL); + } + + return -EOPNOTSUPP; +} + +_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { + char **l, **i, **j, **n; + int r; + + assert_return(paths, -EINVAL); + + if (!IN_SET(type, + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION)) { + + char *p; + + r = sd_path_home(type, suffix, &p); + if (r < 0) + return r; + + l = new(char*, 2); + if (!l) { + free(p); + return -ENOMEM; + } + + l[0] = p; + l[1] = NULL; + + *paths = l; + return 0; + } + + r = get_search(type, &l); + if (r < 0) + return r; + + if (!suffix) { + *paths = l; + return 0; + } + + n = new(char*, strv_length(l)+1); + if (!n) { + strv_free(l); + return -ENOMEM; + } + + j = n; + STRV_FOREACH(i, l) { + + if (endswith(*i, "/")) + *j = strappend(*i, suffix); + else + *j = strjoin(*i, "/", suffix, NULL); + + if (!*j) { + strv_free(l); + strv_free(n); + return -ENOMEM; + } + + j++; + } + + *j = NULL; + *paths = n; + return 0; +} diff --git a/src/grp-utils/systemd-path/sd-path.h b/src/grp-utils/systemd-path/sd-path.h new file mode 100644 index 0000000000..be6abdcd03 --- /dev/null +++ b/src/grp-utils/systemd-path/sd-path.h @@ -0,0 +1,91 @@ +#ifndef foosdpathhfoo +#define foosdpathhfoo + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + /* Temporary files */ + SD_PATH_TEMPORARY = 0x0ULL, + SD_PATH_TEMPORARY_LARGE, + + /* Vendor supplied data */ + SD_PATH_SYSTEM_BINARIES, + SD_PATH_SYSTEM_INCLUDE, + SD_PATH_SYSTEM_LIBRARY_PRIVATE, + SD_PATH_SYSTEM_LIBRARY_ARCH, + SD_PATH_SYSTEM_SHARED, + SD_PATH_SYSTEM_CONFIGURATION_FACTORY, + SD_PATH_SYSTEM_STATE_FACTORY, + + /* System configuration, runtime, state, ... */ + SD_PATH_SYSTEM_CONFIGURATION, + SD_PATH_SYSTEM_RUNTIME, + SD_PATH_SYSTEM_RUNTIME_LOGS, + SD_PATH_SYSTEM_STATE_PRIVATE, + SD_PATH_SYSTEM_STATE_LOGS, + SD_PATH_SYSTEM_STATE_CACHE, + SD_PATH_SYSTEM_STATE_SPOOL, + + /* Vendor supplied data */ + SD_PATH_USER_BINARIES, + SD_PATH_USER_LIBRARY_PRIVATE, + SD_PATH_USER_LIBRARY_ARCH, + SD_PATH_USER_SHARED, + + /* User configuration, state, runtime ... */ + SD_PATH_USER_CONFIGURATION, /* takes both actual configuration (like /etc) and state (like /var/lib) */ + SD_PATH_USER_RUNTIME, + SD_PATH_USER_STATE_CACHE, + + /* User resources */ + SD_PATH_USER, /* $HOME itself */ + SD_PATH_USER_DOCUMENTS, + SD_PATH_USER_MUSIC, + SD_PATH_USER_PICTURES, + SD_PATH_USER_VIDEOS, + SD_PATH_USER_DOWNLOAD, + SD_PATH_USER_PUBLIC, + SD_PATH_USER_TEMPLATES, + SD_PATH_USER_DESKTOP, + + /* Search paths */ + SD_PATH_SEARCH_BINARIES, + SD_PATH_SEARCH_LIBRARY_PRIVATE, + SD_PATH_SEARCH_LIBRARY_ARCH, + SD_PATH_SEARCH_SHARED, + SD_PATH_SEARCH_CONFIGURATION_FACTORY, + SD_PATH_SEARCH_STATE_FACTORY, + SD_PATH_SEARCH_CONFIGURATION, + + _SD_PATH_MAX, +}; + +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/grp-utils/systemd-path/systemd-path.completion.bash b/src/grp-utils/systemd-path/systemd-path.completion.bash new file mode 100644 index 0000000000..2f0c5f5bd7 --- /dev/null +++ b/src/grp-utils/systemd-path/systemd-path.completion.bash @@ -0,0 +1,60 @@ +# systemd-path(1) completion -*- shell-script -*- +# +# 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 +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__get_names() { + systemd-path | { while IFS=: read -r a b; do echo " $a"; done; } +} + +_systemd_path() { + local comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version' + [ARG]='--suffix' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --suffix) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + comps=$( __get_names ) + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd_path systemd-path diff --git a/src/grp-utils/systemd-path/systemd-path.xml b/src/grp-utils/systemd-path/systemd-path.xml new file mode 100644 index 0000000000..e2b23eec51 --- /dev/null +++ b/src/grp-utils/systemd-path/systemd-path.xml @@ -0,0 +1,107 @@ + + + + + + + + + systemd-path + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-path + 1 + + + + systemd-path + List and query system and user paths + + + + + systemd-path OPTIONS NAME + + + + + Description + + systemd-path may be used to query system + and user paths. The tool makes many of the paths described in + file-hierarchy7 + available for querying. + + When invoked without arguments, a list of known paths and + their current values is shown. When at least one argument is + passed, the path with this name is queried and its value shown. + The variables whose name begins with search- + do not refer to individual paths, but instead to a list of + colon-separated search paths, in their order of precedence. + + + + Options + + The following options are understood: + + + + + + The printed paths are suffixed by the + specified string. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + file-hierarchy7 + + + + diff --git a/src/grp-utils/systemd-socket-activate/Makefile b/src/grp-utils/systemd-socket-activate/Makefile new file mode 100644 index 0000000000..dd3e95a5e8 --- /dev/null +++ b/src/grp-utils/systemd-socket-activate/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += \ + systemd-socket-activate + +systemd_socket_activate_SOURCES = \ + src/activate/activate.c + +systemd_socket_activate_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/grp-utils/systemd-socket-activate/activate.c b/src/grp-utils/systemd-socket-activate/activate.c new file mode 100644 index 0000000000..2ad205af17 --- /dev/null +++ b/src/grp-utils/systemd-socket-activate/activate.c @@ -0,0 +1,545 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" + +static char** arg_listen = NULL; +static bool arg_accept = false; +static int arg_socket_type = SOCK_STREAM; +static char** arg_args = NULL; +static char** arg_setenv = NULL; +static char **arg_fdnames = NULL; +static bool arg_inetd = false; + +static int add_epoll(int epoll_fd, int fd) { + struct epoll_event ev = { + .events = EPOLLIN + }; + int r; + + assert(epoll_fd >= 0); + assert(fd >= 0); + + ev.data.fd = fd; + r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (r < 0) + return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); + + return 0; +} + +static int open_sockets(int *epoll_fd, bool accept) { + char **address; + int n, fd, r; + int count = 0; + + n = sd_listen_fds(true); + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + if (n > 0) { + log_info("Received %i descriptors via the environment.", n); + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + r = fd_cloexec(fd, arg_accept); + if (r < 0) + return r; + + count++; + } + } + + /* Close logging and all other descriptors */ + if (arg_listen) { + int except[3 + n]; + + for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++) + except[fd] = fd; + + log_close(); + close_all_fds(except, 3 + n); + } + + /** Note: we leak some fd's on error here. I doesn't matter + * much, since the program will exit immediately anyway, but + * would be a pain to fix. + */ + + STRV_FOREACH(address, arg_listen) { + fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept*SOCK_CLOEXEC)); + if (fd < 0) { + log_open(); + return log_error_errno(fd, "Failed to open '%s': %m", *address); + } + + assert(fd == SD_LISTEN_FDS_START + count); + count++; + } + + if (arg_listen) + log_open(); + + *epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (*epoll_fd < 0) + return log_error_errno(errno, "Failed to create epoll object: %m"); + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) { + _cleanup_free_ char *name = NULL; + + getsockname_pretty(fd, &name); + log_info("Listening on %s as %i.", strna(name), fd); + + r = add_epoll(*epoll_fd, fd); + if (r < 0) + return r; + } + + return count; +} + +static int exec_process(const char* name, char **argv, char **env, int start_fd, int n_fds) { + + _cleanup_strv_free_ char **envp = NULL; + _cleanup_free_ char *joined = NULL; + unsigned n_env = 0, length; + const char *tocopy; + char **s; + int r; + + if (arg_inetd && n_fds != 1) { + log_error("--inetd only supported for single file descriptors."); + return -EINVAL; + } + + length = strv_length(arg_setenv); + + /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */ + envp = new0(char *, length + 8); + if (!envp) + return log_oom(); + + STRV_FOREACH(s, arg_setenv) { + + if (strchr(*s, '=')) { + char *k; + + k = strdup(*s); + if (!k) + return log_oom(); + + envp[n_env++] = k; + } else { + _cleanup_free_ char *p; + const char *n; + + p = strappend(*s, "="); + if (!p) + return log_oom(); + + n = strv_find_prefix(env, p); + if (!n) + continue; + + envp[n_env] = strdup(n); + if (!envp[n_env]) + return log_oom(); + + n_env++; + } + } + + FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") { + const char *n; + + n = strv_find_prefix(env, tocopy); + if (!n) + continue; + + envp[n_env] = strdup(n); + if (!envp[n_env]) + return log_oom(); + + n_env++; + } + + if (arg_inetd) { + assert(n_fds == 1); + + r = dup2(start_fd, STDIN_FILENO); + if (r < 0) + return log_error_errno(errno, "Failed to dup connection to stdin: %m"); + + r = dup2(start_fd, STDOUT_FILENO); + if (r < 0) + return log_error_errno(errno, "Failed to dup connection to stdout: %m"); + + start_fd = safe_close(start_fd); + } else { + if (start_fd != SD_LISTEN_FDS_START) { + assert(n_fds == 1); + + r = dup2(start_fd, SD_LISTEN_FDS_START); + if (r < 0) + return log_error_errno(errno, "Failed to dup connection: %m"); + + safe_close(start_fd); + start_fd = SD_LISTEN_FDS_START; + } + + if (asprintf((char**)(envp + n_env++), "LISTEN_FDS=%i", n_fds) < 0) + return log_oom(); + + if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0) + return log_oom(); + + if (arg_fdnames) { + _cleanup_free_ char *names = NULL; + size_t len; + char *e; + int i; + + len = strv_length(arg_fdnames); + if (len == 1) + for (i = 1; i < n_fds; i++) { + r = strv_extend(&arg_fdnames, arg_fdnames[0]); + if (r < 0) + return log_error_errno(r, "Failed to extend strv: %m"); + } + else if (len != (unsigned) n_fds) + log_warning("The number of fd names is different than number of fds: %zu vs %d", + len, n_fds); + + names = strv_join(arg_fdnames, ":"); + if (!names) + return log_oom(); + + e = strappend("LISTEN_FDNAMES=", names); + if (!e) + return log_oom(); + + envp[n_env++] = e; + } + } + + joined = strv_join(argv, " "); + if (!joined) + return log_oom(); + + log_info("Execing %s (%s)", name, joined); + execvpe(name, argv, envp); + + return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined); +} + +static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) { + _cleanup_free_ char *joined = NULL; + pid_t parent_pid, child_pid; + + joined = strv_join(argv, " "); + if (!joined) + return log_oom(); + + parent_pid = getpid(); + + child_pid = fork(); + if (child_pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + + /* In the child */ + if (child_pid == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + /* Make sure the child goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + exec_process(child, argv, env, fd, 1); + _exit(EXIT_FAILURE); + } + + log_info("Spawned %s (%s) as PID %d", child, joined, child_pid); + return 0; +} + +static int do_accept(const char* name, char **argv, char **envp, int fd) { + _cleanup_free_ char *local = NULL, *peer = NULL; + _cleanup_close_ int fd_accepted = -1; + + fd_accepted = accept4(fd, NULL, NULL, 0); + if (fd_accepted < 0) + return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd); + + getsockname_pretty(fd_accepted, &local); + getpeername_pretty(fd_accepted, true, &peer); + log_info("Connection from %s to %s", strna(peer), strna(local)); + + return fork_and_exec_process(name, argv, envp, fd_accepted); +} + +/* SIGCHLD handler. */ +static void sigchld_hdl(int sig) { + PROTECT_ERRNO; + + for (;;) { + siginfo_t si; + int r; + + si.si_pid = 0; + r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); + if (r < 0) { + if (errno != ECHILD) + log_error_errno(errno, "Failed to reap children: %m"); + return; + } + if (si.si_pid == 0) + return; + + log_info("Child %d died with code %d", si.si_pid, si.si_status); + } +} + +static int install_chld_handler(void) { + static const struct sigaction act = { + .sa_flags = SA_NOCLDSTOP, + .sa_handler = sigchld_hdl, + }; + + int r; + + r = sigaction(SIGCHLD, &act, 0); + if (r < 0) + return log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); + + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Listen on sockets and launch child on connection.\n\n" + "Options:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " -l --listen=ADDR Listen for raw connections at ADDR\n" + " -d --datagram Listen on datagram instead of stream socket\n" + " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n" + " -a --accept Spawn separate child for each connection\n" + " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" + " --fdname=NAME[:NAME...] Specify names for file descriptors\n" + " --inetd Enable inetd file descriptor passing protocol\n" + "\n" + "Note: file descriptors from sd_listen_fds() will be passed through.\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_FDNAME, + ARG_SEQPACKET, + ARG_INETD, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "datagram", no_argument, NULL, 'd' }, + { "seqpacket", no_argument, NULL, ARG_SEQPACKET }, + { "listen", required_argument, NULL, 'l' }, + { "accept", no_argument, NULL, 'a' }, + { "setenv", required_argument, NULL, 'E' }, + { "environment", required_argument, NULL, 'E' }, /* legacy alias */ + { "fdname", required_argument, NULL, ARG_FDNAME }, + { "inetd", no_argument, NULL, ARG_INETD }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'l': + r = strv_extend(&arg_listen, optarg); + if (r < 0) + return log_oom(); + + break; + + case 'd': + if (arg_socket_type == SOCK_SEQPACKET) { + log_error("--datagram may not be combined with --seqpacket."); + return -EINVAL; + } + + arg_socket_type = SOCK_DGRAM; + break; + + case ARG_SEQPACKET: + if (arg_socket_type == SOCK_DGRAM) { + log_error("--seqpacket may not be combined with --datagram."); + return -EINVAL; + } + + arg_socket_type = SOCK_SEQPACKET; + break; + + case 'a': + arg_accept = true; + break; + + case 'E': + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + + break; + + case ARG_FDNAME: { + _cleanup_strv_free_ char **names; + char **s; + + names = strv_split(optarg, ":"); + if (!names) + return log_oom(); + + STRV_FOREACH(s, names) + if (!fdname_is_valid(*s)) { + _cleanup_free_ char *esc; + + esc = cescape(*s); + log_warning("File descriptor name \"%s\" is not valid.", esc); + } + + /* Empty optargs means one empty name */ + r = strv_extend_strv(&arg_fdnames, + strv_isempty(names) ? STRV_MAKE("") : names, + false); + if (r < 0) + return log_error_errno(r, "strv_extend_strv: %m"); + break; + } + + case ARG_INETD: + arg_inetd = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind == argc) { + log_error("%s: command to execute is missing.", + program_invocation_short_name); + return -EINVAL; + } + + if (arg_socket_type == SOCK_DGRAM && arg_accept) { + log_error("Datagram sockets do not accept connections. " + "The --datagram and --accept options may not be combined."); + return -EINVAL; + } + + arg_args = argv + optind; + + return 1 /* work to do */; +} + +int main(int argc, char **argv, char **envp) { + int r, n; + int epoll_fd = -1; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + + r = install_chld_handler(); + if (r < 0) + return EXIT_FAILURE; + + n = open_sockets(&epoll_fd, arg_accept); + if (n < 0) + return EXIT_FAILURE; + if (n == 0) { + log_error("No sockets to listen on specified or passed in."); + return EXIT_FAILURE; + } + + for (;;) { + struct epoll_event event; + + r = epoll_wait(epoll_fd, &event, 1, -1); + if (r < 0) { + if (errno == EINTR) + continue; + + log_error_errno(errno, "epoll_wait() failed: %m"); + return EXIT_FAILURE; + } + + log_info("Communication attempt on fd %i.", event.data.fd); + if (arg_accept) { + r = do_accept(argv[optind], argv + optind, envp, event.data.fd); + if (r < 0) + return EXIT_FAILURE; + } else + break; + } + + exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, n); + + return EXIT_SUCCESS; +} diff --git a/src/grp-utils/systemd-socket-activate/systemd-socket-activate.xml b/src/grp-utils/systemd-socket-activate/systemd-socket-activate.xml new file mode 100644 index 0000000000..2cf3a7d377 --- /dev/null +++ b/src/grp-utils/systemd-socket-activate/systemd-socket-activate.xml @@ -0,0 +1,206 @@ + + + + + + + + + systemd-socket-activate + systemd + + + + Developer + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + systemd-socket-activate + 1 + + + + systemd-socket-activate + Test socket activation of daemons + + + + + systemd-socket-activate + OPTIONS + daemon + OPTIONS + + + + + Description + + systemd-socket-activate may be used to launch a socket-activated service binary from the command + line for testing purposes. It may also be used to launch individual instances of the service binary per connection. + + + The daemon to launch and its options should be specified + after options intended for systemd-socket-activate. + + + If the option is given, the socket file descriptor will be used as the standard + input and output of the launched process. Otherwise, standard input and output will be inherited, and sockets will + be passed through file descriptors 3 and higher. Sockets passed through $LISTEN_FDS to + systemd-socket-activate will be passed through to the daemon, in the original positions. Other sockets + specified with will use consecutive descriptors. By default, + systemd-socket-activate listens on a stream socket, use and + to listen on datagram or sequential packet sockets instead (see below). + + + + + Options + + + + + + Listen on this address. + Takes a string like 2000 or + 127.0.0.1:2001. + + + + + + + + Launch an instance of the service binary for each connection and pass the connection + socket. + + + + + + + Listen on a datagram socket (SOCK_DGRAM), instead of a stream socket + (SOCK_STREAM). May not be combined with . + + + + + + Listen on a sequential packet socket (SOCK_SEQPACKET), instead of a stream + socket (SOCK_STREAM). May not be combined with + . + + + + + + Use the inetd protocol for passing file descriptors, i.e. as standard input and standard + output, instead of the new-style protocol for passing file descriptors using $LISTEN_FDS + (see above). + + + + + + + Add this variable to the environment of the + launched process. If VAR is + followed by =, assume that it is a + variable–value pair. Otherwise, obtain the value from the + environment of systemd-socket-activate itself. + + + + + NAME:NAME... + + Specify names for the file descriptors passed. This is equivalent to setting + FileDescriptorName= in socket unit files, and enables use of + sd_listen_fds_with_names3. + Multiple entries may be specifies using separate options or by separating names with colons + (:) in one option. In case more names are given than descriptors, superfluous ones willl be + ignored. In case less names are given than descriptors, the remaining file descriptors will be unnamed. + + + + + + + + + + Environment variables + + + $LISTEN_FDS + $LISTEN_PID + $LISTEN_FDNAMES + + See + sd_listen_fds3. + + + + $SYSTEMD_LOG_TARGET + $SYSTEMD_LOG_LEVEL + $SYSTEMD_LOG_COLOR + $SYSTEMD_LOG_LOCATION + + Same as in + systemd1. + + + + + + Examples + + + Run an echo server on port 2000 + + $ systemd-socket-activate -l 2000 --inetd -a cat + + + + Run a socket-activated instance of <citerefentry><refentrytitle>systemd-journal-gatewayd</refentrytitle><manvolnum>8</manvolnum></citerefentry> + + $ systemd-socket-activate -l 19531 /usr/lib/systemd/systemd-journal-gatewayd + + + + + See Also + + systemd1, + systemd.socket5, + systemd.service5, + sd_listen_fds3, + sd_listen_fds_with_names3, + cat1 + + + diff --git a/src/hibernate-resume/Makefile b/src/hibernate-resume/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/hibernate-resume/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c deleted file mode 100644 index d7ee80d58f..0000000000 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ /dev/null @@ -1,99 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Ivan Shapovalov - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "fstab-util.h" -#include "log.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; - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - if (streq(key, "resume") && value) { - free(arg_resume_dev); - arg_resume_dev = fstab_node_to_udev_node(value); - if (!arg_resume_dev) - return log_oom(); - } - - return 0; -} - -static int process_resume(void) { - _cleanup_free_ char *name = NULL, *lnk = NULL; - int r; - - if (!arg_resume_dev) - return 0; - - r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); - if (!lnk) - return log_oom(); - - mkdir_parents_label(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - - return 0; -} - -int main(int argc, char *argv[]) { - int r = 0; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Don't even consider resuming outside of initramfs. */ - if (!in_initrd()) - return EXIT_SUCCESS; - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - r = process_resume(); - free(arg_resume_dev); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c deleted file mode 100644 index 21df3c4461..0000000000 --- a/src/hibernate-resume/hibernate-resume.c +++ /dev/null @@ -1,82 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Ivan Shapovalov - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fileio.h" -#include "log.h" -#include "util.h" - -int main(int argc, char *argv[]) { - struct stat st; - const char *device; - _cleanup_free_ char *major_minor = NULL; - int r; - - if (argc != 2) { - log_error("This program expects one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Refuse to run unless we are in an initrd() */ - if (!in_initrd()) - return EXIT_SUCCESS; - - device = argv[1]; - - if (stat(device, &st) < 0) { - log_error_errno(errno, "Failed to stat '%s': %m", device); - return EXIT_FAILURE; - } - - if (!S_ISBLK(st.st_mode)) { - log_error("Resume device '%s' is not a block device.", device); - return EXIT_FAILURE; - } - - if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) { - log_oom(); - return EXIT_FAILURE; - } - - r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_CREATE); - if (r < 0) { - log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor); - return EXIT_FAILURE; - } - - /* - * The write above shall not return. - * - * However, failed resume is a normal condition (may mean that there is - * no hibernation image). - */ - - log_info("Could not resume from '%s' (%s).", device, major_minor); - return EXIT_SUCCESS; -} diff --git a/src/hostname/.gitignore b/src/hostname/.gitignore deleted file mode 100644 index 1ff281b231..0000000000 --- a/src/hostname/.gitignore +++ /dev/null @@ -1 +0,0 @@ -org.freedesktop.hostname1.policy diff --git a/src/hostname/Makefile b/src/hostname/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/hostname/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c deleted file mode 100644 index c16a324232..0000000000 --- a/src/hostname/hostnamectl.c +++ /dev/null @@ -1,529 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "architecture.h" -#include "bus-error.h" -#include "bus-util.h" -#include "hostname-util.h" -#include "spawn-polkit-agent.h" -#include "util.h" - -static bool arg_ask_password = true; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_transient = false; -static bool arg_pretty = false; -static bool arg_static = false; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -typedef struct StatusInfo { - char *hostname; - char *static_hostname; - char *pretty_hostname; - char *icon_name; - char *chassis; - char *deployment; - char *location; - char *kernel_name; - char *kernel_release; - char *os_pretty_name; - char *os_cpe_name; - char *virtualization; - char *architecture; -} StatusInfo; - -static void print_status_info(StatusInfo *i) { - sd_id128_t mid = {}, bid = {}; - int r; - - assert(i); - - printf(" Static hostname: %s\n", strna(i->static_hostname)); - - if (!isempty(i->pretty_hostname) && - !streq_ptr(i->pretty_hostname, i->static_hostname)) - printf(" Pretty hostname: %s\n", i->pretty_hostname); - - if (!isempty(i->hostname) && - !streq_ptr(i->hostname, i->static_hostname)) - printf("Transient hostname: %s\n", i->hostname); - - if (!isempty(i->icon_name)) - printf(" Icon name: %s\n", - strna(i->icon_name)); - - if (!isempty(i->chassis)) - printf(" Chassis: %s\n", - strna(i->chassis)); - - if (!isempty(i->deployment)) - printf(" Deployment: %s\n", i->deployment); - - if (!isempty(i->location)) - printf(" Location: %s\n", i->location); - - r = sd_id128_get_machine(&mid); - if (r >= 0) - printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid)); - - r = sd_id128_get_boot(&bid); - if (r >= 0) - printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid)); - - if (!isempty(i->virtualization)) - printf(" Virtualization: %s\n", i->virtualization); - - if (!isempty(i->os_pretty_name)) - printf(" Operating System: %s\n", i->os_pretty_name); - - if (!isempty(i->os_cpe_name)) - printf(" CPE OS Name: %s\n", i->os_cpe_name); - - if (!isempty(i->kernel_name) && !isempty(i->kernel_release)) - printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release); - - if (!isempty(i->architecture)) - printf(" Architecture: %s\n", i->architecture); - -} - -static int show_one_name(sd_bus *bus, const char* attr) { - _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; - - r = sd_bus_get_property( - bus, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - attr, - &error, &reply, "s"); - if (r < 0) { - log_error("Could not get property: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &s); - if (r < 0) - return bus_log_parse_error(r); - - printf("%s\n", s); - - return 0; -} - -static int show_all_names(sd_bus *bus) { - StatusInfo info = {}; - - static const struct bus_properties_map hostname_map[] = { - { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) }, - { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) }, - { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) }, - { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) }, - { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) }, - { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) }, - { "Location", "s", NULL, offsetof(StatusInfo, location) }, - { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) }, - { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) }, - { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) }, - { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) }, - {} - }; - - static const struct bus_properties_map manager_map[] = { - { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) }, - { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) }, - {} - }; - - int r; - - r = bus_map_all_properties(bus, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - hostname_map, - &info); - if (r < 0) - goto fail; - - bus_map_all_properties(bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - manager_map, - &info); - - print_status_info(&info); - -fail: - free(info.hostname); - free(info.static_hostname); - free(info.pretty_hostname); - free(info.icon_name); - free(info.chassis); - free(info.deployment); - free(info.location); - free(info.kernel_name); - free(info.kernel_release); - free(info.os_pretty_name); - free(info.os_cpe_name); - free(info.virtualization); - free(info.architecture); - - return r; -} - -static int show_status(sd_bus *bus, char **args, unsigned n) { - assert(args); - - if (arg_pretty || arg_static || arg_transient) { - const char *attr; - - if (!!arg_static + !!arg_pretty + !!arg_transient > 1) { - log_error("Cannot query more than one name type at a time"); - return -EINVAL; - } - - attr = arg_pretty ? "PrettyHostname" : - arg_static ? "StaticHostname" : "Hostname"; - - return show_one_name(bus, attr); - } else - return show_all_names(bus); -} - -static int set_simple_string(sd_bus *bus, const char *method, const char *value) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r = 0; - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - method, - &error, NULL, - "sb", value, arg_ask_password); - if (r < 0) - log_error("Could not set property: %s", bus_error_message(&error, -r)); - return r; -} - -static int set_hostname(sd_bus *bus, char **args, unsigned n) { - _cleanup_free_ char *h = NULL; - char *hostname = args[1]; - int r; - - assert(args); - assert(n == 2); - - if (!arg_pretty && !arg_static && !arg_transient) - arg_pretty = arg_static = arg_transient = true; - - if (arg_pretty) { - const char *p; - - /* If the passed hostname is already valid, then - * assume the user doesn't know anything about pretty - * hostnames, so let's unset the pretty hostname, and - * just set the passed hostname as static/dynamic - * hostname. */ - - if (arg_static && hostname_is_valid(hostname, true)) { - p = ""; - /* maybe get rid of trailing dot */ - hostname = hostname_cleanup(hostname); - } else { - p = h = strdup(hostname); - if (!p) - return log_oom(); - - hostname_cleanup(hostname); - } - - r = set_simple_string(bus, "SetPrettyHostname", p); - if (r < 0) - return r; - } - - if (arg_static) { - r = set_simple_string(bus, "SetStaticHostname", hostname); - if (r < 0) - return r; - } - - if (arg_transient) { - r = set_simple_string(bus, "SetHostname", hostname); - if (r < 0) - return r; - } - - return 0; -} - -static int set_icon_name(sd_bus *bus, char **args, unsigned n) { - assert(args); - assert(n == 2); - - return set_simple_string(bus, "SetIconName", args[1]); -} - -static int set_chassis(sd_bus *bus, char **args, unsigned n) { - assert(args); - assert(n == 2); - - return set_simple_string(bus, "SetChassis", args[1]); -} - -static int set_deployment(sd_bus *bus, char **args, unsigned n) { - assert(args); - assert(n == 2); - - return set_simple_string(bus, "SetDeployment", args[1]); -} - -static int set_location(sd_bus *bus, char **args, unsigned n) { - assert(args); - assert(n == 2); - - return set_simple_string(bus, "SetLocation", args[1]); -} - -static void help(void) { - printf("%s [OPTIONS...] COMMAND ...\n\n" - "Query or change system hostname.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-ask-password Do not prompt for password\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --transient Only set transient hostname\n" - " --static Only set static hostname\n" - " --pretty Only set pretty hostname\n\n" - "Commands:\n" - " status Show current hostname settings\n" - " set-hostname NAME Set system hostname\n" - " set-icon-name NAME Set icon name for host\n" - " set-chassis NAME Set chassis type for host\n" - " set-deployment NAME Set deployment environment for host\n" - " set-location NAME Set location for host\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_ASK_PASSWORD, - ARG_TRANSIENT, - ARG_STATIC, - ARG_PRETTY - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "transient", no_argument, NULL, ARG_TRANSIENT }, - { "static", no_argument, NULL, ARG_STATIC }, - { "pretty", no_argument, NULL, ARG_PRETTY }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_TRANSIENT: - arg_transient = true; - break; - - case ARG_PRETTY: - arg_pretty = true; - break; - - case ARG_STATIC: - arg_static = true; - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) { - - static const struct { - const char* verb; - const enum { - MORE, - LESS, - EQUAL - } argc_cmp; - const int argc; - int (* const dispatch)(sd_bus *bus, char **args, unsigned n); - } verbs[] = { - { "status", LESS, 1, show_status }, - { "set-hostname", EQUAL, 2, set_hostname }, - { "set-icon-name", EQUAL, 2, set_icon_name }, - { "set-chassis", EQUAL, 2, set_chassis }, - { "set-deployment", EQUAL, 2, set_deployment }, - { "set-location", EQUAL, 2, set_location }, - }; - - int left; - unsigned i; - - assert(argc >= 0); - assert(argv); - - left = argc - optind; - - if (left <= 0) - /* Special rule: no arguments means "status" */ - i = 0; - else { - if (streq(argv[optind], "help")) { - help(); - return 0; - } - - for (i = 0; i < ELEMENTSOF(verbs); i++) - if (streq(argv[optind], verbs[i].verb)) - break; - - if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); - return -EINVAL; - } - } - - switch (verbs[i].argc_cmp) { - - case EQUAL: - if (left != verbs[i].argc) { - log_error("Invalid number of arguments."); - return -EINVAL; - } - - break; - - case MORE: - if (left < verbs[i].argc) { - log_error("Too few arguments."); - return -EINVAL; - } - - break; - - case LESS: - if (left > verbs[i].argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Unknown comparison operator."); - } - - return verbs[i].dispatch(bus, argv + optind, left); -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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; - } - - r = hostnamectl_main(bus, argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c deleted file mode 100644 index fe8bb62752..0000000000 --- a/src/hostname/hostnamed.c +++ /dev/null @@ -1,743 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bus-util.h" -#include "def.h" -#include "env-util.h" -#include "fileio-label.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 "-.:") - -enum { - PROP_HOSTNAME, - PROP_STATIC_HOSTNAME, - PROP_PRETTY_HOSTNAME, - PROP_ICON_NAME, - PROP_CHASSIS, - PROP_DEPLOYMENT, - PROP_LOCATION, - PROP_KERNEL_NAME, - PROP_KERNEL_RELEASE, - PROP_KERNEL_VERSION, - PROP_OS_PRETTY_NAME, - PROP_OS_CPE_NAME, - _PROP_MAX -}; - -typedef struct Context { - char *data[_PROP_MAX]; - Hashmap *polkit_registry; -} Context; - -static void context_reset(Context *c) { - int p; - - assert(c); - - for (p = 0; p < _PROP_MAX; p++) - c->data[p] = mfree(c->data[p]); -} - -static void context_free(Context *c) { - assert(c); - - context_reset(c); - bus_verify_polkit_async_registry_free(c->polkit_registry); -} - -static int context_read_data(Context *c) { - int r; - struct utsname u; - - assert(c); - - context_reset(c); - - assert_se(uname(&u) >= 0); - c->data[PROP_KERNEL_NAME] = strdup(u.sysname); - c->data[PROP_KERNEL_RELEASE] = strdup(u.release); - c->data[PROP_KERNEL_VERSION] = strdup(u.version); - if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] || - !c->data[PROP_KERNEL_VERSION]) - return -ENOMEM; - - c->data[PROP_HOSTNAME] = gethostname_malloc(); - if (!c->data[PROP_HOSTNAME]) - return -ENOMEM; - - r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]); - if (r < 0 && r != -ENOENT) - return r; - - r = parse_env_file("/etc/machine-info", NEWLINE, - "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], - "ICON_NAME", &c->data[PROP_ICON_NAME], - "CHASSIS", &c->data[PROP_CHASSIS], - "DEPLOYMENT", &c->data[PROP_DEPLOYMENT], - "LOCATION", &c->data[PROP_LOCATION], - NULL); - if (r < 0 && r != -ENOENT) - return r; - - r = parse_env_file("/etc/os-release", NEWLINE, - "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], - "CPE_NAME", &c->data[PROP_OS_CPE_NAME], - NULL); - if (r == -ENOENT) - r = parse_env_file("/usr/lib/os-release", NEWLINE, - "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME], - "CPE_NAME", &c->data[PROP_OS_CPE_NAME], - NULL); - - if (r < 0 && r != -ENOENT) - return r; - - return 0; -} - -static bool valid_chassis(const char *chassis) { - assert(chassis); - - return nulstr_contains( - "vm\0" - "container\0" - "desktop\0" - "laptop\0" - "server\0" - "tablet\0" - "handset\0" - "watch\0" - "embedded\0", - chassis); -} - -static bool valid_deployment(const char *deployment) { - assert(deployment); - - return in_charset(deployment, VALID_DEPLOYMENT_CHARS); -} - -static const char* fallback_chassis(void) { - int r; - char *type; - unsigned t; - int v; - - v = detect_virtualization(); - - if (VIRTUALIZATION_IS_VM(v)) - return "vm"; - if (VIRTUALIZATION_IS_CONTAINER(v)) - return "container"; - - r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type); - if (r < 0) - goto try_dmi; - - r = safe_atou(type, &t); - free(type); - if (r < 0) - goto try_dmi; - - /* We only list the really obvious cases here as the ACPI data - * is not really super reliable. - * - * See the ACPI 5.0 Spec Section 5.2.9.1 for details: - * - * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf - */ - - switch(t) { - - case 1: - case 3: - case 6: - return "desktop"; - - case 2: - return "laptop"; - - case 4: - case 5: - case 7: - return "server"; - - case 8: - return "tablet"; - } - -try_dmi: - r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type); - if (r < 0) - return NULL; - - r = safe_atou(type, &t); - free(type); - if (r < 0) - return NULL; - - /* We only list the really obvious cases here. The DMI data is - unreliable enough, so let's not do any additional guesswork - on top of that. - - See the SMBIOS Specification 3.0 section 7.4.1 for - details about the values listed here: - - https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf - */ - - switch (t) { - - case 0x3: - case 0x4: - case 0x6: - case 0x7: - return "desktop"; - - case 0x8: - case 0x9: - case 0xA: - case 0xE: - return "laptop"; - - case 0xB: - return "handset"; - - case 0x11: - case 0x1C: - case 0x1D: - return "server"; - - case 0x1E: - return "tablet"; - } - - return NULL; -} - -static char* context_fallback_icon_name(Context *c) { - const char *chassis; - - assert(c); - - if (!isempty(c->data[PROP_CHASSIS])) - return strappend("computer-", c->data[PROP_CHASSIS]); - - chassis = fallback_chassis(); - if (chassis) - return strappend("computer-", chassis); - - return strdup("computer"); -} - - -static bool hostname_is_useful(const char *hn) { - return !isempty(hn) && !is_localhost(hn); -} - -static int context_update_kernel_hostname(Context *c) { - const char *static_hn; - const char *hn; - - assert(c); - - static_hn = c->data[PROP_STATIC_HOSTNAME]; - - /* /etc/hostname with something other than "localhost" - * has the highest preference ... */ - if (hostname_is_useful(static_hn)) - hn = static_hn; - - /* ... the transient host name, (ie: DHCP) comes next ... */ - else if (!isempty(c->data[PROP_HOSTNAME])) - hn = c->data[PROP_HOSTNAME]; - - /* ... fallback to static "localhost.*" ignored above ... */ - else if (!isempty(static_hn)) - hn = static_hn; - - /* ... and the ultimate fallback */ - else - hn = "localhost"; - - if (sethostname_idempotent(hn) < 0) - return -errno; - - return 0; -} - -static int context_write_data_static_hostname(Context *c) { - - assert(c); - - if (isempty(c->data[PROP_STATIC_HOSTNAME])) { - - if (unlink("/etc/hostname") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]); -} - -static int context_write_data_machine_info(Context *c) { - - static const char * const name[_PROP_MAX] = { - [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME", - [PROP_ICON_NAME] = "ICON_NAME", - [PROP_CHASSIS] = "CHASSIS", - [PROP_DEPLOYMENT] = "DEPLOYMENT", - [PROP_LOCATION] = "LOCATION", - }; - - _cleanup_strv_free_ char **l = NULL; - int r, p; - - assert(c); - - r = load_env_file(NULL, "/etc/machine-info", NULL, &l); - if (r < 0 && r != -ENOENT) - return r; - - for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) { - _cleanup_free_ char *t = NULL; - char **u; - - assert(name[p]); - - if (isempty(c->data[p])) { - strv_env_unset(l, name[p]); - continue; - } - - t = strjoin(name[p], "=", c->data[p], NULL); - if (!t) - return -ENOMEM; - - u = strv_env_set(l, t); - if (!u) - return -ENOMEM; - - strv_free(l); - l = u; - } - - if (strv_isempty(l)) { - if (unlink("/etc/machine-info") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - - return write_env_file_label("/etc/machine-info", l); -} - -static int property_get_icon_name( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *n = NULL; - Context *c = userdata; - const char *name; - - if (isempty(c->data[PROP_ICON_NAME])) - name = n = context_fallback_icon_name(c); - else - name = c->data[PROP_ICON_NAME]; - - if (!name) - return -ENOMEM; - - return sd_bus_message_append(reply, "s", name); -} - -static int property_get_chassis( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Context *c = userdata; - const char *name; - - if (isempty(c->data[PROP_CHASSIS])) - name = fallback_chassis(); - else - name = c->data[PROP_CHASSIS]; - - return sd_bus_message_append(reply, "s", name); -} - -static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - const char *name; - int interactive; - char *h; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "sb", &name, &interactive); - if (r < 0) - return r; - - if (isempty(name)) - name = c->data[PROP_STATIC_HOSTNAME]; - - if (isempty(name)) - name = "localhost"; - - if (!hostname_is_valid(name, false)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); - - if (streq_ptr(name, c->data[PROP_HOSTNAME])) - return sd_bus_reply_method_return(m, NULL); - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - "org.freedesktop.hostname1.set-hostname", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - h = strdup(name); - if (!h) - return -ENOMEM; - - free(c->data[PROP_HOSTNAME]); - c->data[PROP_HOSTNAME] = h; - - r = context_update_kernel_hostname(c); - if (r < 0) { - log_error_errno(r, "Failed to set host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); - } - - log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME])); - - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - const char *name; - int interactive; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "sb", &name, &interactive); - if (r < 0) - return r; - - name = empty_to_null(name); - - if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME])) - return sd_bus_reply_method_return(m, NULL); - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - "org.freedesktop.hostname1.set-static-hostname", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - if (isempty(name)) - c->data[PROP_STATIC_HOSTNAME] = mfree(c->data[PROP_STATIC_HOSTNAME]); - else { - char *h; - - if (!hostname_is_valid(name, false)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name); - - h = strdup(name); - if (!h) - return -ENOMEM; - - free(c->data[PROP_STATIC_HOSTNAME]); - c->data[PROP_STATIC_HOSTNAME] = h; - } - - r = context_update_kernel_hostname(c); - if (r < 0) { - log_error_errno(r, "Failed to set host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); - } - - r = context_write_data_static_hostname(c); - if (r < 0) { - log_error_errno(r, "Failed to write static host name: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r)); - } - - log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME])); - - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) { - int interactive; - const char *name; - int r; - - assert(c); - assert(m); - - r = sd_bus_message_read(m, "sb", &name, &interactive); - if (r < 0) - return r; - - name = empty_to_null(name); - - if (streq_ptr(name, c->data[prop])) - return sd_bus_reply_method_return(m, NULL); - - /* Since the pretty hostname should always be changed at the - * same time as the static one, use the same policy action for - * both... */ - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - if (isempty(name)) - c->data[prop] = mfree(c->data[prop]); - else { - char *h; - - /* The icon name might ultimately be used as file - * name, so better be safe than sorry */ - - if (prop == PROP_ICON_NAME && !filename_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name); - if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name); - if (prop == PROP_CHASSIS && !valid_chassis(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name); - if (prop == PROP_DEPLOYMENT && !valid_deployment(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name); - if (prop == PROP_LOCATION && string_has_cc(name, NULL)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name); - - h = strdup(name); - if (!h) - return -ENOMEM; - - free(c->data[prop]); - c->data[prop] = h; - } - - r = context_write_data_machine_info(c); - if (r < 0) { - log_error_errno(r, "Failed to write machine info: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r)); - } - - log_info("Changed %s to '%s'", - prop == PROP_PRETTY_HOSTNAME ? "pretty host name" : - prop == PROP_DEPLOYMENT ? "deployment" : - prop == PROP_LOCATION ? "location" : - prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop])); - - (void) sd_bus_emit_properties_changed( - sd_bus_message_get_bus(m), - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" : - prop == PROP_DEPLOYMENT ? "Deployment" : - prop == PROP_LOCATION ? "Location" : - prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { - return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error); -} - -static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) { - return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error); -} - -static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) { - return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error); -} - -static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) { - return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error); -} - -static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) { - return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error); -} - -static const sd_bus_vtable hostname_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END, -}; - -static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(c); - assert(event); - assert(_bus); - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get system bus connection: %m"); - - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - *_bus = bus; - bus = NULL; - - return 0; -} - -int main(int argc, char *argv[]) { - Context context = {}; - _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); - log_parse_environment(); - log_open(); - - umask(0022); - mac_selinux_init(); - - 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"); - goto finish; - } - - sd_event_set_watchdog(event, true); - - r = connect_bus(&context, event, &bus); - if (r < 0) - goto finish; - - r = context_read_data(&context); - if (r < 0) { - log_error_errno(r, "Failed to read hostname and machine information: %m"); - goto finish; - } - - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - -finish: - context_free(&context); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/hostname/org.freedesktop.hostname1.conf b/src/hostname/org.freedesktop.hostname1.conf deleted file mode 100644 index 46b4aadc83..0000000000 --- a/src/hostname/org.freedesktop.hostname1.conf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/hostname/org.freedesktop.hostname1.policy.in b/src/hostname/org.freedesktop.hostname1.policy.in deleted file mode 100644 index c32c1d4fda..0000000000 --- a/src/hostname/org.freedesktop.hostname1.policy.in +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Set host name - <_message>Authentication is required to set the local host name. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set static host name - <_message>Authentication is required to set the statically configured local host name, as well as the pretty host name. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.hostname1.set-hostname org.freedesktop.hostname1.set-machine-info - - - - <_description>Set machine information - <_message>Authentication is required to set local machine information. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/hostname/org.freedesktop.hostname1.service b/src/hostname/org.freedesktop.hostname1.service deleted file mode 100644 index 6041ed60ca..0000000000 --- a/src/hostname/org.freedesktop.hostname1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.hostname1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.hostname1.service diff --git a/src/hwdb/Makefile b/src/hwdb/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/hwdb/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c deleted file mode 100644 index e12cd93d1c..0000000000 --- a/src/hwdb/hwdb.c +++ /dev/null @@ -1,743 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hwdb-internal.h" -#include "hwdb-util.h" -#include "label.h" -#include "mkdir.h" -#include "selinux-util.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. - * Uses a Patricia/radix trie to index all matches for efficient lookup. - */ - -static const char *arg_hwdb_bin_dir = "/etc/udev"; -static const char *arg_root = ""; - -static const char * const conf_file_dirs[] = { - "/etc/udev/hwdb.d", - UDEVLIBEXECDIR "/hwdb.d", - NULL -}; - -/* in-memory trie objects */ -struct trie { - struct trie_node *root; - struct strbuf *strings; - - size_t nodes_count; - size_t children_count; - size_t values_count; -}; - -struct trie_node { - /* prefix, common part for all children of this node */ - size_t prefix_off; - - /* sorted array of pointers to children nodes */ - struct trie_child_entry *children; - uint8_t children_count; - - /* sorted array of key/value pairs */ - struct trie_value_entry *values; - size_t values_count; -}; - -/* children array item with char (0-255) index */ -struct trie_child_entry { - uint8_t c; - struct trie_node *child; -}; - -/* value array item with key/value pairs */ -struct trie_value_entry { - size_t key_off; - size_t value_off; -}; - -static int trie_children_cmp(const void *v1, const void *v2) { - const struct trie_child_entry *n1 = v1; - const struct trie_child_entry *n2 = v2; - - return n1->c - n2->c; -} - -static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) { - struct trie_child_entry *child; - - /* extend array, add new entry, sort for bisection */ - child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry)); - if (!child) - return -ENOMEM; - - node->children = child; - trie->children_count++; - node->children[node->children_count].c = c; - node->children[node->children_count].child = node_child; - node->children_count++; - qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - trie->nodes_count++; - - return 0; -} - -static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) { - struct trie_child_entry *child; - struct trie_child_entry search; - - search.c = c; - child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - if (child) - return child->child; - return NULL; -} - -static void trie_node_cleanup(struct trie_node *node) { - size_t i; - - for (i = 0; i < node->children_count; i++) - trie_node_cleanup(node->children[i].child); - free(node->children); - free(node->values); - free(node); -} - -static void trie_free(struct trie *trie) { - if (!trie) - return; - - if (trie->root) - trie_node_cleanup(trie->root); - - strbuf_cleanup(trie->strings); - free(trie); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free); - -static int trie_values_cmp(const void *v1, const void *v2, void *arg) { - const struct trie_value_entry *val1 = v1; - const struct trie_value_entry *val2 = v2; - struct trie *trie = arg; - - return strcmp(trie->strings->buf + val1->key_off, - trie->strings->buf + val2->key_off); -} - -static int trie_node_add_value(struct trie *trie, struct trie_node *node, - const char *key, const char *value) { - ssize_t k, v; - struct trie_value_entry *val; - - k = strbuf_add_string(trie->strings, key, strlen(key)); - if (k < 0) - return k; - v = strbuf_add_string(trie->strings, value, strlen(value)); - if (v < 0) - return v; - - if (node->values_count) { - struct trie_value_entry search = { - .key_off = k, - .value_off = v, - }; - - val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - if (val) { - /* replace existing earlier key with new value */ - val->value_off = v; - return 0; - } - } - - /* extend array, add new entry, sort for bisection */ - val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry)); - if (!val) - return -ENOMEM; - trie->values_count++; - node->values = val; - node->values[node->values_count].key_off = k; - node->values[node->values_count].value_off = v; - node->values_count++; - qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - return 0; -} - -static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, - const char *key, const char *value) { - size_t i = 0; - int err = 0; - - for (;;) { - size_t p; - uint8_t c; - struct trie_node *child; - - for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) { - _cleanup_free_ char *s = NULL; - ssize_t off; - _cleanup_free_ struct trie_node *new_child = NULL; - - if (c == search[i + p]) - continue; - - /* split node */ - new_child = new0(struct trie_node, 1); - if (!new_child) - return -ENOMEM; - - /* move values from parent to child */ - new_child->prefix_off = node->prefix_off + p+1; - new_child->children = node->children; - new_child->children_count = node->children_count; - new_child->values = node->values; - new_child->values_count = node->values_count; - - /* update parent; use strdup() because the source gets realloc()d */ - s = strndup(trie->strings->buf + node->prefix_off, p); - if (!s) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, s, p); - if (off < 0) - return off; - - node->prefix_off = off; - node->children = NULL; - node->children_count = 0; - node->values = NULL; - node->values_count = 0; - err = node_add_child(trie, node, new_child, c); - if (err < 0) - return err; - - new_child = NULL; /* avoid cleanup */ - break; - } - i += p; - - c = search[i]; - if (c == '\0') - return trie_node_add_value(trie, node, key, value); - - child = node_lookup(node, c); - if (!child) { - ssize_t off; - - /* new child */ - child = new0(struct trie_node, 1); - if (!child) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1)); - if (off < 0) { - free(child); - return off; - } - - child->prefix_off = off; - err = node_add_child(trie, node, child, c); - if (err < 0) { - free(child); - return err; - } - - return trie_node_add_value(trie, child, key, value); - } - - node = child; - i++; - } -} - -struct trie_f { - FILE *f; - struct trie *trie; - uint64_t strings_off; - - uint64_t nodes_count; - uint64_t children_count; - uint64_t values_count; -}; - -/* calculate the storage space for the nodes, children arrays, value arrays */ -static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - - for (i = 0; i < node->children_count; i++) - trie_store_nodes_size(trie, node->children[i].child); - - trie->strings_off += sizeof(struct trie_node_f); - for (i = 0; i < node->children_count; i++) - trie->strings_off += sizeof(struct trie_child_entry_f); - for (i = 0; i < node->values_count; i++) - trie->strings_off += sizeof(struct trie_value_entry_f); -} - -static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - struct trie_node_f n = { - .prefix_off = htole64(trie->strings_off + node->prefix_off), - .children_count = node->children_count, - .values_count = htole64(node->values_count), - }; - struct trie_child_entry_f *children = NULL; - int64_t node_off; - - if (node->children_count) { - children = new0(struct trie_child_entry_f, node->children_count); - if (!children) - return -ENOMEM; - } - - /* post-order recursion */ - for (i = 0; i < node->children_count; i++) { - int64_t child_off; - - child_off = trie_store_nodes(trie, node->children[i].child); - if (child_off < 0) { - free(children); - return child_off; - } - children[i].c = node->children[i].c; - children[i].child_off = htole64(child_off); - } - - /* write node */ - node_off = ftello(trie->f); - fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); - trie->nodes_count++; - - /* append children array */ - if (node->children_count) { - fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); - trie->children_count += node->children_count; - free(children); - } - - /* append values array */ - for (i = 0; i < node->values_count; i++) { - struct trie_value_entry_f v = { - .key_off = htole64(trie->strings_off + node->values[i].key_off), - .value_off = htole64(trie->strings_off + node->values[i].value_off), - }; - - fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); - trie->values_count++; - } - - return node_off; -} - -static int trie_store(struct trie *trie, const char *filename) { - struct trie_f t = { - .trie = trie, - }; - _cleanup_free_ char *filename_tmp = NULL; - int64_t pos; - int64_t root_off; - int64_t size; - struct trie_header_f h = { - .signature = HWDB_SIG, - .tool_version = htole64(atoi(VERSION)), - .header_size = htole64(sizeof(struct trie_header_f)), - .node_size = htole64(sizeof(struct trie_node_f)), - .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), - .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), - }; - int err; - - /* calculate size of header, nodes, children entries, value entries */ - t.strings_off = sizeof(struct trie_header_f); - trie_store_nodes_size(&t, trie->root); - - err = fopen_temporary(filename , &t.f, &filename_tmp); - if (err < 0) - return err; - fchmod(fileno(t.f), 0444); - - /* write nodes */ - err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - root_off = trie_store_nodes(&t, trie->root); - h.nodes_root_off = htole64(root_off); - pos = ftello(t.f); - h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); - - /* write string buffer */ - fwrite(trie->strings->buf, trie->strings->len, 1, t.f); - h.strings_len = htole64(trie->strings->len); - - /* write header */ - size = ftello(t.f); - h.file_size = htole64(size); - err = fseeko(t.f, 0, SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - fwrite(&h, sizeof(struct trie_header_f), 1, t.f); - err = ferror(t.f); - if (err) - err = -errno; - fclose(t.f); - if (err < 0 || rename(filename_tmp, filename) < 0) { - unlink_noerrno(filename_tmp); - return err < 0 ? err : -errno; - } - - log_debug("=== trie on-disk ==="); - log_debug("size: %8"PRIi64" bytes", size); - log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); - log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", - t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); - log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.children_count * sizeof(struct trie_child_entry_f), t.children_count); - log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.values_count * sizeof(struct trie_value_entry_f), t.values_count); - log_debug("string store: %8zu bytes", trie->strings->len); - log_debug("strings start: %8"PRIu64, t.strings_off); - - return 0; -} - -static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename) { - char *value, **entry; - - value = strchr(line, '='); - if (!value) { - log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); - return -EINVAL; - } - - value[0] = '\0'; - value++; - - /* libudev requires properties to start with a space */ - while (isblank(line[0]) && isblank(line[1])) - line++; - - if (line[0] == '\0' || value[0] == '\0') { - log_error("Error, empty key or value '%s' in '%s':", line, filename); - return -EINVAL; - } - - STRV_FOREACH(entry, match_list) - trie_insert(trie, trie->root, *entry, line, value); - - return 0; -} - -static int import_file(struct trie *trie, const char *filename) { - enum { - HW_NONE, - HW_MATCH, - HW_DATA, - } state = HW_NONE; - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - _cleanup_strv_free_ char **match_list = NULL; - char *match = NULL; - int r; - - f = fopen(filename, "re"); - if (!f) - return -errno; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *pos; - - /* comment line */ - if (line[0] == '#') - continue; - - /* strip trailing comment */ - pos = strchr(line, '#'); - if (pos) - pos[0] = '\0'; - - /* strip trailing whitespace */ - len = strlen(line); - while (len > 0 && isspace(line[len-1])) - len--; - line[len] = '\0'; - - switch (state) { - case HW_NONE: - if (len == 0) - break; - - if (line[0] == ' ') { - log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); - break; - } - - /* start of record, first match */ - state = HW_MATCH; - - match = strdup(line); - if (!match) - return -ENOMEM; - - r = strv_consume(&match_list, match); - if (r < 0) - return r; - - break; - - case HW_MATCH: - if (len == 0) { - log_error("Error, DATA expected but got empty line in '%s':", filename); - state = HW_NONE; - strv_clear(match_list); - break; - } - - /* another match */ - if (line[0] != ' ') { - match = strdup(line); - if (!match) - return -ENOMEM; - - r = strv_consume(&match_list, match); - if (r < 0) - return r; - - break; - } - - /* first data */ - state = HW_DATA; - insert_data(trie, match_list, line, filename); - break; - - case HW_DATA: - /* end of record */ - if (len == 0) { - state = HW_NONE; - strv_clear(match_list); - break; - } - - if (line[0] != ' ') { - log_error("Error, DATA expected but got '%s' in '%s':", line, filename); - state = HW_NONE; - strv_clear(match_list); - break; - } - - insert_data(trie, match_list, line, filename); - break; - }; - } - - return 0; -} - -static int hwdb_query(int argc, char *argv[], void *userdata) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - const char *key, *value; - const char *modalias; - int r; - - assert(argc >= 2); - assert(argv); - - modalias = argv[1]; - - r = sd_hwdb_new(&hwdb); - if (r < 0) - return r; - - SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) - printf("%s=%s\n", key, value); - - return 0; -} - -static int hwdb_update(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *hwdb_bin = NULL; - _cleanup_(trie_freep) struct trie *trie = NULL; - char **files, **f; - int r; - - trie = new0(struct trie, 1); - if (!trie) - return -ENOMEM; - - /* string store */ - trie->strings = strbuf_new(); - if (!trie->strings) - return -ENOMEM; - - /* index */ - trie->root = new0(struct trie_node, 1); - if (!trie->root) - return -ENOMEM; - - trie->nodes_count++; - - r = conf_files_list_strv(&files, ".hwdb", arg_root, conf_file_dirs); - if (r < 0) - return log_error_errno(r, "failed to enumerate hwdb files: %m"); - - STRV_FOREACH(f, files) { - log_debug("reading file '%s'", *f); - import_file(trie, *f); - } - strv_free(files); - - strbuf_complete(trie->strings); - - log_debug("=== trie in-memory ==="); - log_debug("nodes: %8zu bytes (%8zu)", - trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); - log_debug("children arrays: %8zu bytes (%8zu)", - trie->children_count * sizeof(struct trie_child_entry), trie->children_count); - log_debug("values arrays: %8zu bytes (%8zu)", - trie->values_count * sizeof(struct trie_value_entry), trie->values_count); - log_debug("strings: %8zu bytes", - trie->strings->len); - log_debug("strings incoming: %8zu bytes (%8zu)", - trie->strings->in_len, trie->strings->in_count); - log_debug("strings dedup'ed: %8zu bytes (%8zu)", - trie->strings->dedup_len, trie->strings->dedup_count); - - hwdb_bin = strjoin(arg_root, "/", arg_hwdb_bin_dir, "/hwdb.bin", NULL); - if (!hwdb_bin) - return -ENOMEM; - - mkdir_parents_label(hwdb_bin, 0755); - r = trie_store(trie, hwdb_bin); - if (r < 0) - return log_error_errno(r, "Failure writing database %s: %m", hwdb_bin); - - return label_fix(hwdb_bin, false, false); -} - -static void help(void) { - printf("Usage: %s OPTIONS COMMAND\n\n" - "Update or query the hardware database.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --usr Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" - " -r --root=PATH Alternative root path in the filesystem\n\n" - "Commands:\n" - " update Update the hwdb database\n" - " query MODALIAS Query database and print result\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_USR, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "usr", no_argument, NULL, ARG_USR }, - { "root", required_argument, NULL, 'r' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) { - switch(c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_USR: - arg_hwdb_bin_dir = UDEVLIBEXECDIR; - break; - - case 'r': - arg_root = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unknown option"); - } - } - - return 1; -} - -static int hwdb_main(int argc, char *argv[]) { - const Verb verbs[] = { - { "update", 1, 1, 0, hwdb_update }, - { "query", 2, 2, 0, hwdb_query }, - {}, - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main (int argc, char *argv[]) { - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - mac_selinux_init(); - - r = hwdb_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/.gitignore b/src/import/.gitignore deleted file mode 100644 index 01106e2e68..0000000000 --- a/src/import/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/org.freedesktop.import1.policy diff --git a/src/import/Makefile b/src/import/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/import/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/import/curl-util.c b/src/import/curl-util.c deleted file mode 100644 index 6990c47f48..0000000000 --- a/src/import/curl-util.c +++ /dev/null @@ -1,448 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - int k = 0; - - assert(g); - - msg = curl_multi_info_read(g->curl, &k); - if (!msg) - return; - - if (msg->msg != CURLMSG_DONE) - return; - - if (g->on_finished) - g->on_finished(g, msg->easy_handle, msg->data.result); -} - -static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - CurlGlue *g = userdata; - int action, k = 0, translated_fd; - - assert(s); - assert(g); - - translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd))); - - if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT)) - action = CURL_POLL_INOUT; - else if (revents & EPOLLIN) - action = CURL_POLL_IN; - else if (revents & EPOLLOUT) - action = CURL_POLL_OUT; - else - action = 0; - - if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) { - log_debug("Failed to propagate IO event."); - return -EINVAL; - } - - curl_glue_check_finished(g); - return 0; -} - -static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) { - sd_event_source *io; - CurlGlue *g = userdata; - uint32_t events = 0; - int r; - - assert(curl); - assert(g); - - io = hashmap_get(g->ios, FD_TO_PTR(s)); - - if (action == CURL_POLL_REMOVE) { - if (io) { - int fd; - - fd = sd_event_source_get_io_fd(io); - assert(fd >= 0); - - sd_event_source_set_enabled(io, SD_EVENT_OFF); - sd_event_source_unref(io); - - hashmap_remove(g->ios, FD_TO_PTR(s)); - hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); - - safe_close(fd); - } - - return 0; - } - - r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops); - if (r < 0) { - log_oom(); - return -1; - } - - r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops); - if (r < 0) { - log_oom(); - return -1; - } - - if (action == CURL_POLL_IN) - events = EPOLLIN; - else if (action == CURL_POLL_OUT) - events = EPOLLOUT; - else if (action == CURL_POLL_INOUT) - events = EPOLLIN|EPOLLOUT; - - if (io) { - if (sd_event_source_set_io_events(io, events) < 0) - return -1; - - if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0) - return -1; - } else { - _cleanup_close_ int fd = -1; - - /* When curl needs to remove an fd from us it closes - * the fd first, and only then calls into us. This is - * nasty, since we cannot pass the fd on to epoll() - * anymore. Hence, duplicate the fds here, and keep a - * copy for epoll which we control after use. */ - - fd = fcntl(s, F_DUPFD_CLOEXEC, 3); - if (fd < 0) - return -1; - - if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0) - return -1; - - (void) sd_event_source_set_description(io, "curl-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, FD_TO_PTR(fd), FD_TO_PTR(s)); - if (r < 0) { - log_oom(); - hashmap_remove(g->ios, FD_TO_PTR(s)); - sd_event_source_unref(io); - return -1; - } - - fd = -1; - } - - return 0; -} - -static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) { - CurlGlue *g = userdata; - int k = 0; - - assert(s); - assert(g); - - if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) { - log_debug("Failed to propagate timeout."); - return -EINVAL; - } - - curl_glue_check_finished(g); - return 0; -} - -static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) { - CurlGlue *g = userdata; - usec_t usec; - - assert(curl); - assert(g); - - if (timeout_ms < 0) { - if (g->timer) { - if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0) - return -1; - } - - return 0; - } - - usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1; - - if (g->timer) { - if (sd_event_source_set_time(g->timer, usec) < 0) - return -1; - - if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0) - return -1; - } else { - if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) - return -1; - - (void) sd_event_source_set_description(g->timer, "curl-timer"); - } - - return 0; -} - -CurlGlue *curl_glue_unref(CurlGlue *g) { - sd_event_source *io; - - if (!g) - return NULL; - - if (g->curl) - curl_multi_cleanup(g->curl); - - while ((io = hashmap_steal_first(g->ios))) { - int fd; - - fd = sd_event_source_get_io_fd(io); - assert(fd >= 0); - - hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); - - safe_close(fd); - sd_event_source_unref(io); - } - - hashmap_free(g->ios); - - sd_event_source_unref(g->timer); - sd_event_unref(g->event); - free(g); - - return NULL; -} - -int curl_glue_new(CurlGlue **glue, sd_event *event) { - _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL; - int r; - - g = new0(CurlGlue, 1); - if (!g) - return -ENOMEM; - - if (event) - g->event = sd_event_ref(event); - else { - r = sd_event_default(&g->event); - if (r < 0) - return r; - } - - g->curl = curl_multi_init(); - if (!g->curl) - return -ENOMEM; - - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK) - return -EINVAL; - - *glue = g; - g = NULL; - - return 0; -} - -int curl_glue_make(CURL **ret, const char *url, void *userdata) { - const char *useragent; - CURL *c; - int r; - - assert(ret); - assert(url); - - c = curl_easy_init(); - if (!c) - return -ENOMEM; - - /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */ - - if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) { - r = -EIO; - goto fail; - } - - if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) { - r = -EIO; - goto fail; - } - - useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION); - if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) { - r = -EIO; - goto fail; - } - - if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) { - r = -EIO; - goto fail; - } - - *ret = c; - return 0; - -fail: - curl_easy_cleanup(c); - return r; -} - -int curl_glue_add(CurlGlue *g, CURL *c) { - assert(g); - assert(c); - - if (curl_multi_add_handle(g->curl, c) != CURLM_OK) - return -EIO; - - return 0; -} - -void curl_glue_remove_and_free(CurlGlue *g, CURL *c) { - assert(g); - - if (!c) - return; - - if (g->curl) - curl_multi_remove_handle(g->curl, c); - - curl_easy_cleanup(c); -} - -struct curl_slist *curl_slist_new(const char *first, ...) { - struct curl_slist *l; - va_list ap; - - if (!first) - return NULL; - - l = curl_slist_append(NULL, first); - if (!l) - return NULL; - - va_start(ap, first); - - for (;;) { - struct curl_slist *n; - const char *i; - - i = va_arg(ap, const char*); - if (!i) - break; - - n = curl_slist_append(l, i); - if (!n) { - va_end(ap); - curl_slist_free_all(l); - return NULL; - } - - l = n; - } - - va_end(ap); - return l; -} - -int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) { - const char *p = contents; - size_t l; - char *s; - - l = strlen(field); - if (sz < l) - return 0; - - if (memcmp(p, field, l) != 0) - return 0; - - p += l; - sz -= l; - - if (memchr(p, 0, sz)) - return 0; - - /* Skip over preceeding whitespace */ - while (sz > 0 && strchr(WHITESPACE, p[0])) { - p++; - sz--; - } - - /* Truncate trailing whitespace*/ - while (sz > 0 && strchr(WHITESPACE, p[sz-1])) - sz--; - - s = strndup(p, sz); - if (!s) - return -ENOMEM; - - *value = s; - return 1; -} - -int curl_parse_http_time(const char *t, usec_t *ret) { - const char *e; - locale_t loc; - struct tm tm; - time_t v; - - assert(t); - assert(ret); - - loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0); - if (loc == (locale_t) 0) - return -errno; - - /* RFC822 */ - e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc); - if (!e || *e != 0) - /* RFC 850 */ - e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc); - if (!e || *e != 0) - /* ANSI C */ - e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc); - freelocale(loc); - if (!e || *e != 0) - return -EINVAL; - - v = timegm(&tm); - if (v == (time_t) -1) - return -EINVAL; - - *ret = (usec_t) v * USEC_PER_SEC; - return 0; -} diff --git a/src/import/curl-util.h b/src/import/curl-util.h deleted file mode 100644 index a758cc5640..0000000000 --- a/src/import/curl-util.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-event.h" - -#include "hashmap.h" - -typedef struct CurlGlue CurlGlue; - -struct CurlGlue { - sd_event *event; - CURLM *curl; - sd_event_source *timer; - Hashmap *ios; - Hashmap *translate_fds; - - void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code); - void *userdata; -}; - -int curl_glue_new(CurlGlue **glue, sd_event *event); -CurlGlue* curl_glue_unref(CurlGlue *glue); - -DEFINE_TRIVIAL_CLEANUP_FUNC(CurlGlue*, curl_glue_unref); - -int curl_glue_make(CURL **ret, const char *url, void *userdata); -int curl_glue_add(CurlGlue *g, CURL *c); -void curl_glue_remove_and_free(CurlGlue *g, CURL *c); - -struct curl_slist *curl_slist_new(const char *first, ...) _sentinel_; -int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value); -int curl_parse_http_time(const char *t, usec_t *ret); - -DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_easy_cleanup); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct curl_slist*, curl_slist_free_all); diff --git a/src/import/export-raw.c b/src/import/export-raw.c deleted file mode 100644 index db06e11b87..0000000000 --- a/src/import/export-raw.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - 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 . -***/ - -#include - -/* 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 -#undef basename - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "copy.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) - -struct RawExport { - sd_event *event; - - RawExportFinished on_finished; - void *userdata; - - char *path; - - int input_fd; - int output_fd; - - ImportCompress compress; - - sd_event_source *output_event_source; - - void *buffer; - size_t buffer_size; - size_t buffer_allocated; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - unsigned last_percent; - RateLimit progress_rate_limit; - - struct stat st; - - bool eof; - bool tried_reflink; - bool tried_sendfile; -}; - -RawExport *raw_export_unref(RawExport *e) { - if (!e) - return NULL; - - sd_event_source_unref(e->output_event_source); - - import_compress_free(&e->compress); - - sd_event_unref(e->event); - - safe_close(e->input_fd); - - free(e->buffer); - free(e->path); - free(e); - - return NULL; -} - -int raw_export_new( - RawExport **ret, - sd_event *event, - RawExportFinished on_finished, - void *userdata) { - - _cleanup_(raw_export_unrefp) RawExport *e = NULL; - int r; - - assert(ret); - - e = new0(RawExport, 1); - if (!e) - return -ENOMEM; - - e->output_fd = e->input_fd = -1; - e->on_finished = on_finished; - e->userdata = userdata; - - RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - e->last_percent = (unsigned) -1; - - if (event) - e->event = sd_event_ref(event); - else { - r = sd_event_default(&e->event); - if (r < 0) - return r; - } - - *ret = e; - e = NULL; - - return 0; -} - -static void raw_export_report_progress(RawExport *e) { - unsigned percent; - assert(e); - - if (e->written_uncompressed >= (uint64_t) e->st.st_size) - percent = 100; - else - percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size); - - if (percent == e->last_percent) - return; - - if (!ratelimit_test(&e->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Exported %u%%.", percent); - - e->last_percent = percent; -} - -static int raw_export_process(RawExport *e) { - ssize_t l; - int r; - - assert(e); - - if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - /* If we shall take an uncompressed snapshot we can - * reflink source to destination directly. Let's see - * if this works. */ - - r = btrfs_reflink(e->input_fd, e->output_fd); - if (r >= 0) { - r = 0; - goto finish; - } - - e->tried_reflink = true; - } - - if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - e->tried_sendfile = true; - } else if (l == 0) { - r = 0; - goto finish; - } else { - e->written_uncompressed += l; - e->written_compressed += l; - - raw_export_report_progress(e); - - return 0; - } - } - - while (e->buffer_size <= 0) { - uint8_t input[COPY_BUFFER_SIZE]; - - if (e->eof) { - r = 0; - goto finish; - } - - l = read(e->input_fd, input, sizeof(input)); - if (l < 0) { - r = log_error_errno(errno, "Failed to read raw file: %m"); - goto finish; - } - - if (l == 0) { - e->eof = true; - r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } else { - e->written_uncompressed += l; - r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } - if (r < 0) { - r = log_error_errno(r, "Failed to encode: %m"); - goto finish; - } - } - - l = write(e->output_fd, e->buffer, e->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to write output file: %m"); - goto finish; - } - - assert((size_t) l <= e->buffer_size); - memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); - e->buffer_size -= l; - e->written_compressed += l; - - raw_export_report_progress(e); - - return 0; - -finish: - if (r >= 0) { - (void) copy_times(e->input_fd, e->output_fd); - (void) copy_xattr(e->input_fd, e->output_fd); - } - - if (e->on_finished) - e->on_finished(e, r, e->userdata); - else - sd_event_exit(e->event, r); - - return 0; -} - -static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - RawExport *i = userdata; - - return raw_export_process(i); -} - -static int raw_export_on_defer(sd_event_source *s, void *userdata) { - RawExport *i = userdata; - - return raw_export_process(i); -} - -static int reflink_snapshot(int fd, const char *path) { - char *p, *d; - int new_fd, r; - - p = strdupa(path); - d = dirname(p); - - new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600); - if (new_fd < 0) { - _cleanup_free_ char *t = NULL; - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600); - if (new_fd < 0) - return -errno; - - (void) unlink(t); - } - - r = btrfs_reflink(fd, new_fd); - if (r < 0) { - safe_close(new_fd); - return r; - } - - return new_fd; -} - -int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) { - _cleanup_close_ int sfd = -1, tfd = -1; - int r; - - assert(e); - assert(path); - assert(fd >= 0); - assert(compress < _IMPORT_COMPRESS_TYPE_MAX); - assert(compress != IMPORT_COMPRESS_UNKNOWN); - - if (e->output_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&e->path, path); - if (r < 0) - return r; - - sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (sfd < 0) - return -errno; - - if (fstat(sfd, &e->st) < 0) - return -errno; - if (!S_ISREG(e->st.st_mode)) - return -ENOTTY; - - /* Try to take a reflink snapshot of the file, if we can t make the export atomic */ - tfd = reflink_snapshot(sfd, path); - if (tfd >= 0) { - e->input_fd = tfd; - tfd = -1; - } else { - e->input_fd = sfd; - sfd = -1; - } - - r = import_compress_init(&e->compress, compress); - if (r < 0) - return r; - - r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e); - if (r == -EPERM) { - r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - e->output_fd = fd; - return r; -} diff --git a/src/import/export-raw.h b/src/import/export-raw.h deleted file mode 100644 index 8e723d4908..0000000000 --- a/src/import/export-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include "sd-event.h" - -#include "import-compress.h" -#include "macro.h" - -typedef struct RawExport RawExport; - -typedef void (*RawExportFinished)(RawExport *export, int error, void *userdata); - -int raw_export_new(RawExport **export, sd_event *event, RawExportFinished on_finished, void *userdata); -RawExport* raw_export_unref(RawExport *export); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawExport*, raw_export_unref); - -int raw_export_start(RawExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/import/export-tar.c b/src/import/export-tar.c deleted file mode 100644 index d79c27f2d0..0000000000 --- a/src/import/export-tar.c +++ /dev/null @@ -1,328 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.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) - -struct TarExport { - sd_event *event; - - TarExportFinished on_finished; - void *userdata; - - char *path; - char *temp_path; - - int output_fd; - int tar_fd; - - ImportCompress compress; - - sd_event_source *output_event_source; - - void *buffer; - size_t buffer_size; - size_t buffer_allocated; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - pid_t tar_pid; - - struct stat st; - uint64_t quota_referenced; - - unsigned last_percent; - RateLimit progress_rate_limit; - - bool eof; - bool tried_splice; -}; - -TarExport *tar_export_unref(TarExport *e) { - if (!e) - return NULL; - - sd_event_source_unref(e->output_event_source); - - if (e->tar_pid > 1) { - (void) kill_and_sigcont(e->tar_pid, SIGKILL); - (void) wait_for_terminate(e->tar_pid, NULL); - } - - if (e->temp_path) { - (void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA); - free(e->temp_path); - } - - import_compress_free(&e->compress); - - sd_event_unref(e->event); - - safe_close(e->tar_fd); - - free(e->buffer); - free(e->path); - free(e); - - return NULL; -} - -int tar_export_new( - TarExport **ret, - sd_event *event, - TarExportFinished on_finished, - void *userdata) { - - _cleanup_(tar_export_unrefp) TarExport *e = NULL; - int r; - - assert(ret); - - e = new0(TarExport, 1); - if (!e) - return -ENOMEM; - - e->output_fd = e->tar_fd = -1; - e->on_finished = on_finished; - e->userdata = userdata; - e->quota_referenced = (uint64_t) -1; - - RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - e->last_percent = (unsigned) -1; - - if (event) - e->event = sd_event_ref(event); - else { - r = sd_event_default(&e->event); - if (r < 0) - return r; - } - - *ret = e; - e = NULL; - - return 0; -} - -static void tar_export_report_progress(TarExport *e) { - unsigned percent; - assert(e); - - /* Do we have any quota info? If not, we don't know anything about the progress */ - if (e->quota_referenced == (uint64_t) -1) - return; - - if (e->written_uncompressed >= e->quota_referenced) - percent = 100; - else - percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced); - - if (percent == e->last_percent) - return; - - if (!ratelimit_test(&e->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Exported %u%%.", percent); - - e->last_percent = percent; -} - -static int tar_export_process(TarExport *e) { - ssize_t l; - int r; - - assert(e); - - if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - e->tried_splice = true; - } else if (l == 0) { - r = 0; - goto finish; - } else { - e->written_uncompressed += l; - e->written_compressed += l; - - tar_export_report_progress(e); - - return 0; - } - } - - while (e->buffer_size <= 0) { - uint8_t input[COPY_BUFFER_SIZE]; - - if (e->eof) { - r = 0; - goto finish; - } - - l = read(e->tar_fd, input, sizeof(input)); - if (l < 0) { - r = log_error_errno(errno, "Failed to read tar file: %m"); - goto finish; - } - - if (l == 0) { - e->eof = true; - r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } else { - e->written_uncompressed += l; - r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } - if (r < 0) { - r = log_error_errno(r, "Failed to encode: %m"); - goto finish; - } - } - - l = write(e->output_fd, e->buffer, e->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to write output file: %m"); - goto finish; - } - - assert((size_t) l <= e->buffer_size); - memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); - e->buffer_size -= l; - e->written_compressed += l; - - tar_export_report_progress(e); - - return 0; - -finish: - if (e->on_finished) - e->on_finished(e, r, e->userdata); - else - sd_event_exit(e->event, r); - - return 0; -} - -static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - TarExport *i = userdata; - - return tar_export_process(i); -} - -static int tar_export_on_defer(sd_event_source *s, void *userdata) { - TarExport *i = userdata; - - return tar_export_process(i); -} - -int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) { - _cleanup_close_ int sfd = -1; - int r; - - assert(e); - assert(path); - assert(fd >= 0); - assert(compress < _IMPORT_COMPRESS_TYPE_MAX); - assert(compress != IMPORT_COMPRESS_UNKNOWN); - - if (e->output_fd >= 0) - return -EBUSY; - - sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (sfd < 0) - return -errno; - - if (fstat(sfd, &e->st) < 0) - return -errno; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&e->path, path); - if (r < 0) - return r; - - e->quota_referenced = (uint64_t) -1; - - if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ - BtrfsQuotaInfo q; - - r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); - if (r >= 0) - e->quota_referenced = q.referenced; - - e->temp_path = mfree(e->temp_path); - - r = tempfn_random(path, NULL, &e->temp_path); - if (r < 0) - return r; - - /* Let's try to make a snapshot, if we can, so that the export is atomic */ - r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); - if (r < 0) { - log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); - e->temp_path = mfree(e->temp_path); - } - } - - r = import_compress_init(&e->compress, compress); - if (r < 0) - return r; - - r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e); - if (r == -EPERM) { - r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid); - if (e->tar_fd < 0) { - e->output_event_source = sd_event_source_unref(e->output_event_source); - return e->tar_fd; - } - - e->output_fd = fd; - return r; -} diff --git a/src/import/export-tar.h b/src/import/export-tar.h deleted file mode 100644 index 1e3c8bb80c..0000000000 --- a/src/import/export-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include "sd-event.h" - -#include "import-compress.h" -#include "macro.h" - -typedef struct TarExport TarExport; - -typedef void (*TarExportFinished)(TarExport *export, int error, void *userdata); - -int tar_export_new(TarExport **export, sd_event *event, TarExportFinished on_finished, void *userdata); -TarExport* tar_export_unref(TarExport *export); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref); - -int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/import/export.c b/src/import/export.c deleted file mode 100644 index cc98c33ef6..0000000000 --- a/src/import/export.c +++ /dev/null @@ -1,320 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-event.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 "import-util.h" -#include "machine-image.h" -#include "signal-util.h" -#include "string-util.h" -#include "verbs.h" - -static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN; - -static void determine_compression_from_filename(const char *p) { - - if (arg_compress != IMPORT_COMPRESS_UNKNOWN) - return; - - if (!p) { - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; - return; - } - - if (endswith(p, ".xz")) - arg_compress = IMPORT_COMPRESS_XZ; - else if (endswith(p, ".gz")) - arg_compress = IMPORT_COMPRESS_GZIP; - else if (endswith(p, ".bz2")) - arg_compress = IMPORT_COMPRESS_BZIP2; - else - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; -} - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarExport *export, int error, void *userdata) { - sd_event *event = userdata; - assert(export); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int export_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_export_unrefp) TarExport *export = NULL; - _cleanup_(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; - int r, fd; - - if (machine_name_is_valid(argv[1])) { - r = image_find(argv[1], &image); - if (r < 0) - return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); - if (r == 0) { - log_error("Machine image %s not found.", argv[1]); - return -ENOENT; - } - - local = image->path; - } else - local = argv[1]; - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - determine_compression_from_filename(path); - - if (path) { - open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open tar image for export: %m"); - - fd = open_fd; - - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDOUT_FILENO; - - (void) readlink_malloc("/proc/self/fd/1", &pretty); - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, 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 = tar_export_new(&export, event, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate exporter: %m"); - - r = tar_export_start(export, local, fd, arg_compress); - if (r < 0) - return log_error_errno(r, "Failed to export image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static void on_raw_finished(RawExport *export, int error, void *userdata) { - sd_event *event = userdata; - assert(export); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int export_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_export_unrefp) RawExport *export = NULL; - _cleanup_(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; - int r, fd; - - if (machine_name_is_valid(argv[1])) { - r = image_find(argv[1], &image); - if (r < 0) - return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); - if (r == 0) { - log_error("Machine image %s not found.", argv[1]); - return -ENOENT; - } - - local = image->path; - } else - local = argv[1]; - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - determine_compression_from_filename(path); - - if (path) { - open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open raw image for export: %m"); - - fd = open_fd; - - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDOUT_FILENO; - - (void) readlink_malloc("/proc/self/fd/1", &pretty); - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, 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 = raw_export_new(&export, event, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate exporter: %m"); - - r = raw_export_start(export, local, fd, arg_compress); - if (r < 0) - return log_error_errno(r, "Failed to export image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Export container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --format=FORMAT Select format\n\n" - "Commands:\n" - " tar NAME [FILE] Export a TAR image\n" - " raw NAME [FILE] Export a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORMAT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "format", required_argument, NULL, ARG_FORMAT }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORMAT: - if (streq(optarg, "uncompressed")) - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; - else if (streq(optarg, "xz")) - arg_compress = IMPORT_COMPRESS_XZ; - else if (streq(optarg, "gzip")) - arg_compress = IMPORT_COMPRESS_GZIP; - else if (streq(optarg, "bzip2")) - arg_compress = IMPORT_COMPRESS_BZIP2; - else { - log_error("Unknown format: %s", optarg); - return -EINVAL; - } - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int export_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, export_tar }, - { "raw", 2, 3, 0, export_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = export_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/import-common.c b/src/import/import-common.c deleted file mode 100644 index 81209cdaf6..0000000000 --- a/src/import/import-common.c +++ /dev/null @@ -1,221 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#include "btrfs-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; - - assert(fd >= 0); - - /* First, let's make this a read-only subvolume if it refers - * to a subvolume */ - r = btrfs_subvol_set_read_only_fd(fd, true); - if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) { - struct stat st; - - /* This doesn't refer to a subvolume, or the file - * system isn't even btrfs. In that, case fall back to - * chmod()ing */ - - r = fstat(fd, &st); - if (r < 0) - return log_error_errno(errno, "Failed to stat temporary image: %m"); - - /* Drop "w" flag */ - if (fchmod(fd, st.st_mode & 07555) < 0) - return log_error_errno(errno, "Failed to chmod() final image: %m"); - - return 0; - - } else if (r < 0) - return log_error_errno(r, "Failed to make subvolume read-only: %m"); - - return 0; -} - -int import_make_read_only(const char *path) { - _cleanup_close_ int fd = 1; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - - return import_make_read_only_fd(fd); -} - -int import_fork_tar_x(const char *path, pid_t *ret) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - pid_t pid; - int r; - - assert(path); - assert(ret); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to create pipe for tar: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { - int null_fd; - uint64_t retain = - (1ULL << CAP_CHOWN) | - (1ULL << CAP_FOWNER) | - (1ULL << CAP_FSETID) | - (1ULL << CAP_MKNOD) | - (1ULL << CAP_SETFCAP) | - (1ULL << CAP_DAC_OVERRIDE); - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[1] = safe_close(pipefd[1]); - - if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (pipefd[0] != STDIN_FILENO) - pipefd[0] = safe_close(pipefd[0]); - - null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDOUT_FILENO) - null_fd = safe_close(null_fd); - - stdio_unset_cloexec(); - - 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); - if (r < 0) - log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - - execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); - log_error_errno(errno, "Failed to execute tar: %m"); - _exit(EXIT_FAILURE); - } - - pipefd[0] = safe_close(pipefd[0]); - r = pipefd[1]; - pipefd[1] = -1; - - *ret = pid; - - return r; -} - -int import_fork_tar_c(const char *path, pid_t *ret) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - pid_t pid; - int r; - - assert(path); - assert(ret); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to create pipe for tar: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { - int null_fd; - uint64_t retain = (1ULL << CAP_DAC_OVERRIDE); - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[0] = safe_close(pipefd[0]); - - if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (pipefd[1] != STDOUT_FILENO) - pipefd[1] = safe_close(pipefd[1]); - - null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDIN_FILENO) - null_fd = safe_close(null_fd); - - stdio_unset_cloexec(); - - 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); - if (r < 0) - log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - - execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL); - log_error_errno(errno, "Failed to execute tar: %m"); - _exit(EXIT_FAILURE); - } - - pipefd[1] = safe_close(pipefd[1]); - r = pipefd[0]; - pipefd[0] = -1; - - *ret = pid; - - return r; -} diff --git a/src/import/import-common.h b/src/import/import-common.h deleted file mode 100644 index 07d3250e71..0000000000 --- a/src/import/import-common.h +++ /dev/null @@ -1,26 +0,0 @@ -#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 . -***/ - -int import_make_read_only_fd(int fd); -int import_make_read_only(const char *path); - -int import_fork_tar_c(const char *path, pid_t *ret); -int import_fork_tar_x(const char *path, pid_t *ret); diff --git a/src/import/import-compress.c b/src/import/import-compress.c deleted file mode 100644 index f1766bbe3b..0000000000 --- a/src/import/import-compress.c +++ /dev/null @@ -1,469 +0,0 @@ -/*** - 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 . -***/ - -#include "import-compress.h" -#include "string-table.h" -#include "util.h" - -void import_compress_free(ImportCompress *c) { - assert(c); - - if (c->type == IMPORT_COMPRESS_XZ) - lzma_end(&c->xz); - else if (c->type == IMPORT_COMPRESS_GZIP) { - if (c->encoding) - deflateEnd(&c->gzip); - else - inflateEnd(&c->gzip); - } else if (c->type == IMPORT_COMPRESS_BZIP2) { - if (c->encoding) - BZ2_bzCompressEnd(&c->bzip2); - else - BZ2_bzDecompressEnd(&c->bzip2); - } - - c->type = IMPORT_COMPRESS_UNKNOWN; -} - -int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) { - static const uint8_t xz_signature[] = { - 0xfd, '7', 'z', 'X', 'Z', 0x00 - }; - static const uint8_t gzip_signature[] = { - 0x1f, 0x8b - }; - static const uint8_t bzip2_signature[] = { - 'B', 'Z', 'h' - }; - - int r; - - assert(c); - - if (c->type != IMPORT_COMPRESS_UNKNOWN) - return 1; - - if (size < MAX3(sizeof(xz_signature), - sizeof(gzip_signature), - sizeof(bzip2_signature))) - return 0; - - assert(data); - - if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) { - lzma_ret xzr; - - xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); - if (xzr != LZMA_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_XZ; - - } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) { - r = inflateInit2(&c->gzip, 15+16); - if (r != Z_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_GZIP; - - } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) { - r = BZ2_bzDecompressInit(&c->bzip2, 0, 0); - if (r != BZ_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_BZIP2; - } else - c->type = IMPORT_COMPRESS_UNCOMPRESSED; - - c->encoding = false; - - return 1; -} - -int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) { - int r; - - assert(c); - assert(callback); - - r = import_uncompress_detect(c, data, size); - if (r <= 0) - return r; - - if (c->encoding) - return -EINVAL; - - if (size <= 0) - return 1; - - assert(data); - - switch (c->type) { - - case IMPORT_COMPRESS_UNCOMPRESSED: - r = callback(data, size, userdata); - if (r < 0) - return r; - - break; - - case IMPORT_COMPRESS_XZ: - c->xz.next_in = data; - c->xz.avail_in = size; - - while (c->xz.avail_in > 0) { - uint8_t buffer[16 * 1024]; - lzma_ret lzr; - - c->xz.next_out = buffer; - c->xz.avail_out = sizeof(buffer); - - lzr = lzma_code(&c->xz, LZMA_RUN); - if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - case IMPORT_COMPRESS_GZIP: - c->gzip.next_in = (void*) data; - c->gzip.avail_in = size; - - while (c->gzip.avail_in > 0) { - uint8_t buffer[16 * 1024]; - - c->gzip.next_out = buffer; - c->gzip.avail_out = sizeof(buffer); - - r = inflate(&c->gzip, Z_NO_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - case IMPORT_COMPRESS_BZIP2: - c->bzip2.next_in = (void*) data; - c->bzip2.avail_in = size; - - while (c->bzip2.avail_in > 0) { - uint8_t buffer[16 * 1024]; - - c->bzip2.next_out = (char*) buffer; - c->bzip2.avail_out = sizeof(buffer); - - r = BZ2_bzDecompress(&c->bzip2); - if (r != BZ_OK && r != BZ_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - default: - assert_not_reached("Unknown compression"); - } - - return 1; -} - -int import_compress_init(ImportCompress *c, ImportCompressType t) { - int r; - - assert(c); - - switch (t) { - - case IMPORT_COMPRESS_XZ: { - lzma_ret xzr; - - xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); - if (xzr != LZMA_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_XZ; - break; - } - - case IMPORT_COMPRESS_GZIP: - r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); - if (r != Z_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_GZIP; - break; - - case IMPORT_COMPRESS_BZIP2: - r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0); - if (r != BZ_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_BZIP2; - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - c->type = IMPORT_COMPRESS_UNCOMPRESSED; - break; - - default: - return -EOPNOTSUPP; - } - - c->encoding = true; - return 0; -} - -static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - size_t l; - void *p; - - if (*buffer_allocated > *buffer_size) - return 0; - - l = MAX(16*1024U, (*buffer_size * 2)); - p = realloc(*buffer, l); - if (!p) - return -ENOMEM; - - *buffer = p; - *buffer_allocated = l; - - return 1; -} - -int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - int r; - - assert(c); - assert(buffer); - assert(buffer_size); - assert(buffer_allocated); - - if (!c->encoding) - return -EINVAL; - - if (size <= 0) - return 0; - - assert(data); - - *buffer_size = 0; - - switch (c->type) { - - case IMPORT_COMPRESS_XZ: - - c->xz.next_in = data; - c->xz.avail_in = size; - - while (c->xz.avail_in > 0) { - lzma_ret lzr; - - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->xz.next_out = (uint8_t*) *buffer + *buffer_size; - c->xz.avail_out = *buffer_allocated - *buffer_size; - - lzr = lzma_code(&c->xz, LZMA_RUN); - if (lzr != LZMA_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; - } - - break; - - case IMPORT_COMPRESS_GZIP: - - c->gzip.next_in = (void*) data; - c->gzip.avail_in = size; - - while (c->gzip.avail_in > 0) { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; - c->gzip.avail_out = *buffer_allocated - *buffer_size; - - r = deflate(&c->gzip, Z_NO_FLUSH); - if (r != Z_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; - } - - break; - - case IMPORT_COMPRESS_BZIP2: - - c->bzip2.next_in = (void*) data; - c->bzip2.avail_in = size; - - while (c->bzip2.avail_in > 0) { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); - c->bzip2.avail_out = *buffer_allocated - *buffer_size; - - r = BZ2_bzCompress(&c->bzip2, BZ_RUN); - if (r != BZ_RUN_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; - } - - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - - if (*buffer_allocated < size) { - void *p; - - p = realloc(*buffer, size); - if (!p) - return -ENOMEM; - - *buffer = p; - *buffer_allocated = size; - } - - memcpy(*buffer, data, size); - *buffer_size = size; - break; - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - int r; - - assert(c); - assert(buffer); - assert(buffer_size); - assert(buffer_allocated); - - if (!c->encoding) - return -EINVAL; - - *buffer_size = 0; - - switch (c->type) { - - case IMPORT_COMPRESS_XZ: { - lzma_ret lzr; - - c->xz.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->xz.next_out = (uint8_t*) *buffer + *buffer_size; - c->xz.avail_out = *buffer_allocated - *buffer_size; - - lzr = lzma_code(&c->xz, LZMA_FINISH); - if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; - } while (lzr != LZMA_STREAM_END); - - break; - } - - case IMPORT_COMPRESS_GZIP: - c->gzip.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; - c->gzip.avail_out = *buffer_allocated - *buffer_size; - - r = deflate(&c->gzip, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; - } while (r != Z_STREAM_END); - - break; - - case IMPORT_COMPRESS_BZIP2: - c->bzip2.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); - c->bzip2.avail_out = *buffer_allocated - *buffer_size; - - r = BZ2_bzCompress(&c->bzip2, BZ_FINISH); - if (r != BZ_FINISH_OK && r != BZ_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; - } while (r != BZ_STREAM_END); - - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - break; - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = { - [IMPORT_COMPRESS_UNKNOWN] = "unknown", - [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed", - [IMPORT_COMPRESS_XZ] = "xz", - [IMPORT_COMPRESS_GZIP] = "gzip", - [IMPORT_COMPRESS_BZIP2] = "bzip2", -}; - -DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType); diff --git a/src/import/import-compress.h b/src/import/import-compress.h deleted file mode 100644 index 6b59d0724b..0000000000 --- a/src/import/import-compress.h +++ /dev/null @@ -1,61 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#include "macro.h" - -typedef enum ImportCompressType { - IMPORT_COMPRESS_UNKNOWN, - IMPORT_COMPRESS_UNCOMPRESSED, - IMPORT_COMPRESS_XZ, - IMPORT_COMPRESS_GZIP, - IMPORT_COMPRESS_BZIP2, - _IMPORT_COMPRESS_TYPE_MAX, - _IMPORT_COMPRESS_TYPE_INVALID = -1, -} ImportCompressType; - -typedef struct ImportCompress { - ImportCompressType type; - bool encoding; - union { - lzma_stream xz; - z_stream gzip; - bz_stream bzip2; - }; -} ImportCompress; - -typedef int (*ImportCompressCallback)(const void *data, size_t size, void *userdata); - -void import_compress_free(ImportCompress *c); - -int import_uncompress_detect(ImportCompress *c, const void *data, size_t size); -int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata); - -int import_compress_init(ImportCompress *c, ImportCompressType t); -int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated); -int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated); - -const char* import_compress_type_to_string(ImportCompressType t) _const_; -ImportCompressType import_compress_type_from_string(const char *s) _pure_; diff --git a/src/import/import-pubring.gpg b/src/import/import-pubring.gpg deleted file mode 100644 index be27776896..0000000000 Binary files a/src/import/import-pubring.gpg and /dev/null differ diff --git a/src/import/import-raw.c b/src/import/import-raw.c deleted file mode 100644 index fd6b9f7703..0000000000 --- a/src/import/import-raw.c +++ /dev/null @@ -1,467 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "chattr-util.h" -#include "copy.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; - - char *image_root; - - RawImportFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool read_only; - bool grow_machine_directory; - - char *temp_path; - char *final_path; - - int input_fd; - int output_fd; - - ImportCompress compress; - - uint64_t written_since_last_grow; - - sd_event_source *input_event_source; - - uint8_t buffer[16*1024]; - size_t buffer_size; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - struct stat st; - - unsigned last_percent; - RateLimit progress_rate_limit; -}; - -RawImport* raw_import_unref(RawImport *i) { - if (!i) - return NULL; - - sd_event_unref(i->event); - - if (i->temp_path) { - (void) unlink(i->temp_path); - free(i->temp_path); - } - - import_compress_free(&i->compress); - - sd_event_source_unref(i->input_event_source); - - safe_close(i->output_fd); - - free(i->final_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int raw_import_new( - RawImport **ret, - sd_event *event, - const char *image_root, - RawImportFinished on_finished, - void *userdata) { - - _cleanup_(raw_import_unrefp) RawImport *i = NULL; - int r; - - assert(ret); - - i = new0(RawImport, 1); - if (!i) - return -ENOMEM; - - i->input_fd = i->output_fd = -1; - i->on_finished = on_finished; - i->userdata = userdata; - - RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - i->last_percent = (unsigned) -1; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - *ret = i; - i = NULL; - - return 0; -} - -static void raw_import_report_progress(RawImport *i) { - unsigned percent; - assert(i); - - /* We have no size information, unless the source is a regular file */ - if (!S_ISREG(i->st.st_mode)) - return; - - if (i->written_compressed >= (uint64_t) i->st.st_size) - percent = 100; - else - percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); - - if (percent == i->last_percent) - return; - - if (!ratelimit_test(&i->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Imported %u%%.", percent); - - i->last_percent = percent; -} - -static int raw_import_maybe_convert_qcow2(RawImport *i) { - _cleanup_close_ int converted_fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - assert(i); - - r = qcow2_detect(i->output_fd); - if (r < 0) - return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); - if (r == 0) - return 0; - - /* This is a QCOW2 image, let's convert it */ - r = tempfn_random(i->final_path, NULL, &t); - if (r < 0) - return log_oom(); - - converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (converted_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", t); - - r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", t); - - log_info("Unpacking QCOW2 file."); - - r = qcow2_convert(i->output_fd, converted_fd); - if (r < 0) { - unlink(t); - return log_error_errno(r, "Failed to convert qcow2 image: %m"); - } - - (void) unlink(i->temp_path); - free(i->temp_path); - i->temp_path = t; - t = NULL; - - safe_close(i->output_fd); - i->output_fd = converted_fd; - converted_fd = -1; - - return 1; -} - -static int raw_import_finish(RawImport *i) { - int r; - - assert(i); - assert(i->output_fd >= 0); - assert(i->temp_path); - assert(i->final_path); - - /* In case this was a sparse file, make sure the file system is right */ - if (i->written_uncompressed > 0) { - if (ftruncate(i->output_fd, i->written_uncompressed) < 0) - return log_error_errno(errno, "Failed to truncate file: %m"); - } - - r = raw_import_maybe_convert_qcow2(i); - if (r < 0) - return r; - - if (S_ISREG(i->st.st_mode)) { - (void) copy_times(i->input_fd, i->output_fd); - (void) copy_xattr(i->input_fd, i->output_fd); - } - - if (i->read_only) { - r = import_make_read_only_fd(i->output_fd); - if (r < 0) - return r; - } - - if (i->force_local) - (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) - return log_error_errno(r, "Failed to move image into place: %m"); - - i->temp_path = mfree(i->temp_path); - - return 0; -} - -static int raw_import_open_disk(RawImport *i) { - int r; - - assert(i); - - assert(!i->final_path); - assert(!i->temp_path); - assert(i->output_fd < 0); - - i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL); - if (!i->final_path) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (i->output_fd < 0) - return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path); - - r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); - - return 0; -} - -static int raw_import_try_reflink(RawImport *i) { - off_t p; - int r; - - assert(i); - assert(i->input_fd >= 0); - assert(i->output_fd >= 0); - - if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED) - return 0; - - if (!S_ISREG(i->st.st_mode)) - return 0; - - p = lseek(i->input_fd, 0, SEEK_CUR); - if (p == (off_t) -1) - return log_error_errno(errno, "Failed to read file offset of input file: %m"); - - /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */ - if ((uint64_t) p != (uint64_t) i->buffer_size) - return 0; - - r = btrfs_reflink(i->input_fd, i->output_fd); - if (r >= 0) - return 1; - - return 0; -} - -static int raw_import_write(const void *p, size_t sz, void *userdata) { - RawImport *i = userdata; - ssize_t n; - - if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { - i->written_since_last_grow = 0; - grow_machine_directory(); - } - - n = sparse_write(i->output_fd, p, sz, 64); - if (n < 0) - return -errno; - if ((size_t) n < sz) - return -EIO; - - i->written_uncompressed += sz; - i->written_since_last_grow += sz; - - return 0; -} - -static int raw_import_process(RawImport *i) { - ssize_t l; - int r; - - assert(i); - assert(i->buffer_size < sizeof(i->buffer)); - - l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to read input file: %m"); - goto finish; - } - if (l == 0) { - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); - r = -EIO; - goto finish; - } - - r = raw_import_finish(i); - goto finish; - } - - i->buffer_size += l; - - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); - if (r < 0) { - log_error("Failed to detect file compression: %m"); - goto finish; - } - if (r == 0) /* Need more data */ - return 0; - - r = raw_import_open_disk(i); - if (r < 0) - goto finish; - - r = raw_import_try_reflink(i); - if (r < 0) - goto finish; - if (r > 0) { - r = raw_import_finish(i); - goto finish; - } - } - - r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i); - if (r < 0) { - log_error_errno(r, "Failed to decode and write: %m"); - goto finish; - } - - i->written_compressed += i->buffer_size; - i->buffer_size = 0; - - raw_import_report_progress(i); - - return 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); - - return 0; -} - -static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - RawImport *i = userdata; - - return raw_import_process(i); -} - -static int raw_import_on_defer(sd_event_source *s, void *userdata) { - RawImport *i = userdata; - - return raw_import_process(i); -} - -int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) { - int r; - - assert(i); - assert(fd >= 0); - assert(local); - - if (!machine_name_is_valid(local)) - return -EINVAL; - - if (i->input_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - i->force_local = force_local; - i->read_only = read_only; - - if (fstat(fd, &i->st) < 0) - return -errno; - - r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i); - if (r == -EPERM) { - /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ - r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - i->input_fd = fd; - return r; -} diff --git a/src/import/import-raw.h b/src/import/import-raw.h deleted file mode 100644 index 4f543e0883..0000000000 --- a/src/import/import-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct RawImport RawImport; - -typedef void (*RawImportFinished)(RawImport *import, int error, void *userdata); - -int raw_import_new(RawImport **import, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata); -RawImport* raw_import_unref(RawImport *import); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref); - -int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/import/import-tar.c b/src/import/import-tar.c deleted file mode 100644 index 8b81324fde..0000000000 --- a/src/import/import-tar.c +++ /dev/null @@ -1,388 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "copy.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; - - char *image_root; - - TarImportFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool read_only; - bool grow_machine_directory; - - char *temp_path; - char *final_path; - - int input_fd; - int tar_fd; - - ImportCompress compress; - - uint64_t written_since_last_grow; - - sd_event_source *input_event_source; - - uint8_t buffer[16*1024]; - size_t buffer_size; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - struct stat st; - - pid_t tar_pid; - - unsigned last_percent; - RateLimit progress_rate_limit; -}; - -TarImport* tar_import_unref(TarImport *i) { - if (!i) - return NULL; - - sd_event_source_unref(i->input_event_source); - - if (i->tar_pid > 1) { - (void) kill_and_sigcont(i->tar_pid, SIGKILL); - (void) wait_for_terminate(i->tar_pid, NULL); - } - - if (i->temp_path) { - (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - free(i->temp_path); - } - - import_compress_free(&i->compress); - - sd_event_unref(i->event); - - safe_close(i->tar_fd); - - free(i->final_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int tar_import_new( - TarImport **ret, - sd_event *event, - const char *image_root, - TarImportFinished on_finished, - void *userdata) { - - _cleanup_(tar_import_unrefp) TarImport *i = NULL; - int r; - - assert(ret); - - i = new0(TarImport, 1); - if (!i) - return -ENOMEM; - - i->input_fd = i->tar_fd = -1; - i->on_finished = on_finished; - i->userdata = userdata; - - RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - i->last_percent = (unsigned) -1; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - *ret = i; - i = NULL; - - return 0; -} - -static void tar_import_report_progress(TarImport *i) { - unsigned percent; - assert(i); - - /* We have no size information, unless the source is a regular file */ - if (!S_ISREG(i->st.st_mode)) - return; - - if (i->written_compressed >= (uint64_t) i->st.st_size) - percent = 100; - else - percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); - - if (percent == i->last_percent) - return; - - if (!ratelimit_test(&i->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Imported %u%%.", percent); - - i->last_percent = percent; -} - -static int tar_import_finish(TarImport *i) { - int r; - - assert(i); - assert(i->tar_fd >= 0); - assert(i->temp_path); - assert(i->final_path); - - i->tar_fd = safe_close(i->tar_fd); - - if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); - i->tar_pid = 0; - if (r < 0) - return r; - } - - if (i->read_only) { - r = import_make_read_only(i->temp_path); - if (r < 0) - return r; - } - - if (i->force_local) - (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) - return log_error_errno(r, "Failed to move image into place: %m"); - - i->temp_path = mfree(i->temp_path); - - return 0; -} - -static int tar_import_fork_tar(TarImport *i) { - int r; - - assert(i); - - assert(!i->final_path); - assert(!i->temp_path); - assert(i->tar_fd < 0); - - i->final_path = strjoin(i->image_root, "/", i->local, NULL); - if (!i->final_path) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(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) - return i->tar_fd; - - return 0; -} - -static int tar_import_write(const void *p, size_t sz, void *userdata) { - TarImport *i = userdata; - int r; - - if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { - i->written_since_last_grow = 0; - grow_machine_directory(); - } - - r = loop_write(i->tar_fd, p, sz, false); - if (r < 0) - return r; - - i->written_uncompressed += sz; - i->written_since_last_grow += sz; - - return 0; -} - -static int tar_import_process(TarImport *i) { - ssize_t l; - int r; - - assert(i); - assert(i->buffer_size < sizeof(i->buffer)); - - l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to read input file: %m"); - goto finish; - } - if (l == 0) { - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); - r = -EIO; - goto finish; - } - - r = tar_import_finish(i); - goto finish; - } - - i->buffer_size += l; - - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); - if (r < 0) { - log_error("Failed to detect file compression: %m"); - goto finish; - } - if (r == 0) /* Need more data */ - return 0; - - r = tar_import_fork_tar(i); - if (r < 0) - goto finish; - } - - r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i); - if (r < 0) { - log_error_errno(r, "Failed to decode and write: %m"); - goto finish; - } - - i->written_compressed += i->buffer_size; - i->buffer_size = 0; - - tar_import_report_progress(i); - - return 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); - - return 0; -} - -static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - TarImport *i = userdata; - - return tar_import_process(i); -} - -static int tar_import_on_defer(sd_event_source *s, void *userdata) { - TarImport *i = userdata; - - return tar_import_process(i); -} - -int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) { - int r; - - assert(i); - assert(fd >= 0); - assert(local); - - if (!machine_name_is_valid(local)) - return -EINVAL; - - if (i->input_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - i->force_local = force_local; - i->read_only = read_only; - - if (fstat(fd, &i->st) < 0) - return -errno; - - r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i); - if (r == -EPERM) { - /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ - r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - i->input_fd = fd; - return r; -} diff --git a/src/import/import-tar.h b/src/import/import-tar.h deleted file mode 100644 index 24abe06c8f..0000000000 --- a/src/import/import-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct TarImport TarImport; - -typedef void (*TarImportFinished)(TarImport *import, int error, void *userdata); - -int tar_import_new(TarImport **import, sd_event *event, const char *image_root, TarImportFinished on_finished, void *userdata); -TarImport* tar_import_unref(TarImport *import); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarImport*, tar_import_unref); - -int tar_import_start(TarImport *import, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/import/import.c b/src/import/import.c deleted file mode 100644 index 2b6ca24af8..0000000000 --- a/src/import/import.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-event.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "hostname-util.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; -static const char *arg_image_root = "/var/lib/machines"; - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarImport *import, int error, void *userdata) { - sd_event *event = userdata; - assert(import); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int import_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_import_unrefp) TarImport *import = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *path = NULL, *local = NULL; - _cleanup_free_ char *ll = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error("Image '%s' already exists.", local); - return -EEXIST; - } - } - } else - local = "imported"; - - if (path) { - open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open tar image to import: %m"); - - fd = open_fd; - - log_info("Importing '%s', saving as '%s'.", path, local); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDIN_FILENO; - - (void) readlink_malloc("/proc/self/fd/0", &pretty); - log_info("Importing '%s', saving as '%s'.", strna(pretty), local); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, 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 = tar_import_new(&import, event, arg_image_root, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate importer: %m"); - - r = tar_import_start(import, fd, local, arg_force, arg_read_only); - if (r < 0) - return log_error_errno(r, "Failed to import image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static void on_raw_finished(RawImport *import, int error, void *userdata) { - sd_event *event = userdata; - assert(import); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int import_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_import_unrefp) RawImport *import = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *path = NULL, *local = NULL; - _cleanup_free_ char *ll = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error("Image '%s' already exists.", local); - return -EEXIST; - } - } - } else - local = "imported"; - - if (path) { - open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open raw image to import: %m"); - - fd = open_fd; - - log_info("Importing '%s', saving as '%s'.", path, local); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDIN_FILENO; - - (void) readlink_malloc("/proc/self/fd/0", &pretty); - log_info("Importing '%s', saving as '%s'.", pretty, local); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, 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 = raw_import_new(&import, event, arg_image_root, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate importer: %m"); - - r = raw_import_start(import, fd, local, arg_force, arg_read_only); - if (r < 0) - return log_error_errno(r, "Failed to import image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Import container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --force Force creation of image\n" - " --image-root=PATH Image root directory\n" - " --read-only Create a read-only image\n\n" - "Commands:\n" - " tar FILE [NAME] Import a TAR image\n" - " raw FILE [NAME] Import a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORCE, - ARG_IMAGE_ROOT, - ARG_READ_ONLY, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "force", no_argument, NULL, ARG_FORCE }, - { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORCE: - arg_force = true; - break; - - case ARG_IMAGE_ROOT: - arg_image_root = optarg; - break; - - case ARG_READ_ONLY: - arg_read_only = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int import_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, import_tar }, - { "raw", 2, 3, 0, import_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = import_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/importd.c b/src/import/importd.c deleted file mode 100644 index 28b4302cb3..0000000000 --- a/src/import/importd.c +++ /dev/null @@ -1,1219 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "def.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 "process-util.h" -#include "signal-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; - -typedef enum TransferType { - TRANSFER_IMPORT_TAR, - TRANSFER_IMPORT_RAW, - TRANSFER_EXPORT_TAR, - TRANSFER_EXPORT_RAW, - TRANSFER_PULL_TAR, - TRANSFER_PULL_RAW, - _TRANSFER_TYPE_MAX, - _TRANSFER_TYPE_INVALID = -1, -} TransferType; - -struct Transfer { - Manager *manager; - - uint32_t id; - char *object_path; - - TransferType type; - ImportVerify verify; - - char *remote; - char *local; - bool force_local; - bool read_only; - - char *format; - - pid_t pid; - - int log_fd; - - char log_message[LINE_MAX]; - size_t log_message_size; - - sd_event_source *pid_event_source; - sd_event_source *log_event_source; - - unsigned n_canceled; - unsigned progress_percent; - - int stdin_fd; - int stdout_fd; -}; - -struct Manager { - sd_event *event; - sd_bus *bus; - - uint32_t current_transfer_id; - Hashmap *transfers; - - Hashmap *polkit_registry; - - int notify_fd; - - sd_event_source *notify_event_source; -}; - -#define TRANSFERS_MAX 64 - -static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = { - [TRANSFER_IMPORT_TAR] = "import-tar", - [TRANSFER_IMPORT_RAW] = "import-raw", - [TRANSFER_EXPORT_TAR] = "export-tar", - [TRANSFER_EXPORT_RAW] = "export-raw", - [TRANSFER_PULL_TAR] = "pull-tar", - [TRANSFER_PULL_RAW] = "pull-raw", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType); - -static Transfer *transfer_unref(Transfer *t) { - if (!t) - return NULL; - - if (t->manager) - hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id)); - - sd_event_source_unref(t->pid_event_source); - sd_event_source_unref(t->log_event_source); - - free(t->remote); - free(t->local); - free(t->format); - free(t->object_path); - - if (t->pid > 0) { - (void) kill_and_sigcont(t->pid, SIGKILL); - (void) wait_for_terminate(t->pid, NULL); - } - - safe_close(t->log_fd); - safe_close(t->stdin_fd); - safe_close(t->stdout_fd); - - free(t); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref); - -static int transfer_new(Manager *m, Transfer **ret) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - uint32_t id; - int r; - - assert(m); - assert(ret); - - if (hashmap_size(m->transfers) >= TRANSFERS_MAX) - return -E2BIG; - - r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops); - if (r < 0) - return r; - - t = new0(Transfer, 1); - if (!t) - return -ENOMEM; - - t->type = _TRANSFER_TYPE_INVALID; - t->log_fd = -1; - t->stdin_fd = -1; - t->stdout_fd = -1; - t->verify = _IMPORT_VERIFY_INVALID; - - id = m->current_transfer_id + 1; - - if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0) - return -ENOMEM; - - r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t); - if (r < 0) - return r; - - m->current_transfer_id = id; - - t->manager = m; - t->id = id; - - *ret = t; - t = NULL; - - return 0; -} - -static void transfer_send_log_line(Transfer *t, const char *line) { - int r, priority = LOG_INFO; - - assert(t); - assert(line); - - syslog_parse_priority(&line, &priority, true); - - log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line); - - r = sd_bus_emit_signal( - t->manager->bus, - t->object_path, - "org.freedesktop.import1.Transfer", - "LogMessage", - "us", - priority, - line); - if (r < 0) - log_error_errno(r, "Cannot emit message: %m"); - } - -static void transfer_send_logs(Transfer *t, bool flush) { - assert(t); - - /* Try to send out all log messages, if we can. But if we - * can't we remove the messages from the buffer, but don't - * fail */ - - while (t->log_message_size > 0) { - _cleanup_free_ char *n = NULL; - char *e; - - if (t->log_message_size >= sizeof(t->log_message)) - e = t->log_message + sizeof(t->log_message); - else { - char *a, *b; - - a = memchr(t->log_message, 0, t->log_message_size); - b = memchr(t->log_message, '\n', t->log_message_size); - - if (a && b) - e = a < b ? a : b; - else if (a) - e = a; - else - e = b; - } - - if (!e) { - if (!flush) - return; - - e = t->log_message + t->log_message_size; - } - - n = strndup(t->log_message, e - t->log_message); - - /* Skip over NUL and newlines */ - while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n')) - e++; - - memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e); - t->log_message_size -= e - t->log_message; - - if (!n) { - log_oom(); - continue; - } - - if (isempty(n)) - continue; - - transfer_send_log_line(t, n); - } -} - -static int transfer_finalize(Transfer *t, bool success) { - int r; - - assert(t); - - transfer_send_logs(t, true); - - r = sd_bus_emit_signal( - t->manager->bus, - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "TransferRemoved", - "uos", - t->id, - t->object_path, - success ? "done" : - t->n_canceled > 0 ? "canceled" : "failed"); - - if (r < 0) - log_error_errno(r, "Cannot emit message: %m"); - - transfer_unref(t); - return 0; -} - -static int transfer_cancel(Transfer *t) { - int r; - - assert(t); - - r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL); - if (r < 0) - return r; - - t->n_canceled++; - return 0; -} - -static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) { - Transfer *t = userdata; - bool success = false; - - assert(s); - assert(t); - - if (si->si_code == CLD_EXITED) { - if (si->si_status != 0) - log_error("Import process failed with exit code %i.", si->si_status); - else { - log_debug("Import process succeeded."); - success = true; - } - - } else if (si->si_code == CLD_KILLED || - si->si_code == CLD_DUMPED) - - log_error("Import process terminated by signal %s.", signal_to_string(si->si_status)); - else - log_error("Import process failed due to unknown reason."); - - t->pid = 0; - - return transfer_finalize(t, success); -} - -static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Transfer *t = userdata; - ssize_t l; - - assert(s); - assert(t); - - l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size); - if (l <= 0) { - /* EOF/read error. We just close the pipe here, and - * close the watch, waiting for the SIGCHLD to arrive, - * before we do anything else. */ - - if (l < 0) - log_error_errno(errno, "Failed to read log message: %m"); - - t->log_event_source = sd_event_source_unref(t->log_event_source); - return 0; - } - - t->log_message_size += l; - - transfer_send_logs(t, false); - - return 0; -} - -static int transfer_start(Transfer *t) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - int r; - - assert(t); - assert(t->pid <= 0); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return -errno; - - t->pid = fork(); - if (t->pid < 0) - return -errno; - if (t->pid == 0) { - const char *cmd[] = { - NULL, /* systemd-import, systemd-export or systemd-pull */ - NULL, /* tar, raw */ - NULL, /* --verify= */ - NULL, /* verify argument */ - NULL, /* maybe --force */ - NULL, /* maybe --read-only */ - NULL, /* if so: the actual URL */ - NULL, /* maybe --format= */ - NULL, /* if so: the actual format */ - NULL, /* remote */ - NULL, /* local */ - NULL - }; - unsigned k = 0; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[0] = safe_close(pipefd[0]); - - if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdout_fd >= 0) { - if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdout_fd != STDOUT_FILENO) - safe_close(t->stdout_fd); - } else { - if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - } - - if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO) - pipefd[1] = safe_close(pipefd[1]); - - if (t->stdin_fd >= 0) { - if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdin_fd != STDIN_FILENO) - safe_close(t->stdin_fd); - } else { - int null_fd; - - null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDIN_FILENO) - safe_close(null_fd); - } - - stdio_unset_cloexec(); - - setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1); - setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1); - - if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW)) - cmd[k++] = SYSTEMD_IMPORT_PATH; - else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) - cmd[k++] = SYSTEMD_EXPORT_PATH; - else - cmd[k++] = SYSTEMD_PULL_PATH; - - if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR)) - cmd[k++] = "tar"; - else - cmd[k++] = "raw"; - - if (t->verify != _IMPORT_VERIFY_INVALID) { - cmd[k++] = "--verify"; - cmd[k++] = import_verify_to_string(t->verify); - } - - if (t->force_local) - cmd[k++] = "--force"; - if (t->read_only) - cmd[k++] = "--read-only"; - - if (t->format) { - cmd[k++] = "--format"; - cmd[k++] = t->format; - } - - if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) { - if (t->remote) - cmd[k++] = t->remote; - else - cmd[k++] = "-"; - } - - if (t->local) - cmd[k++] = t->local; - cmd[k] = NULL; - - execv(cmd[0], (char * const *) cmd); - log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]); - _exit(EXIT_FAILURE); - } - - pipefd[1] = safe_close(pipefd[1]); - t->log_fd = pipefd[0]; - pipefd[0] = -1; - - t->stdin_fd = safe_close(t->stdin_fd); - - r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t); - if (r < 0) - return r; - - r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t); - if (r < 0) - return r; - - /* Make sure always process logging before SIGCHLD */ - r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5); - if (r < 0) - return r; - - r = sd_bus_emit_signal( - t->manager->bus, - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "TransferNew", - "uo", - t->id, - t->object_path); - if (r < 0) - return r; - - return 0; -} - -static Manager *manager_unref(Manager *m) { - Transfer *t; - - if (!m) - return NULL; - - sd_event_source_unref(m->notify_event_source); - safe_close(m->notify_fd); - - while ((t = hashmap_first(m->transfers))) - transfer_unref(t); - - hashmap_free(m->transfers); - - bus_verify_polkit_async_registry_free(m->polkit_registry); - - m->bus = sd_bus_flush_close_unref(m->bus); - sd_event_unref(m->event); - - free(m); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref); - -static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *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 ucred *ucred = NULL; - Manager *m = userdata; - struct cmsghdr *cmsg; - unsigned percent; - char *p, *e; - Transfer *t; - Iterator i; - ssize_t n; - int r; - - n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - 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))) - ucred = (struct ucred*) CMSG_DATA(cmsg); - - if (msghdr.msg_flags & MSG_TRUNC) { - log_warning("Got overly long notification datagram, ignoring."); - return 0; - } - - if (!ucred || ucred->pid <= 0) { - log_warning("Got notification datagram lacking credential information, ignoring."); - return 0; - } - - HASHMAP_FOREACH(t, m->transfers, i) - if (ucred->pid == t->pid) - break; - - if (!t) { - log_warning("Got notification datagram from unexpected peer, ignoring."); - return 0; - } - - buf[n] = 0; - - p = startswith(buf, "X_IMPORT_PROGRESS="); - if (!p) { - p = strstr(buf, "\nX_IMPORT_PROGRESS="); - if (!p) - return 0; - - p += 19; - } - - e = strchrnul(p, '\n'); - *e = 0; - - r = safe_atou(p, &percent); - if (r < 0 || percent > 100) { - log_warning("Got invalid percent value, ignoring."); - return 0; - } - - t->progress_percent = percent; - - log_debug("Got percentage from client: %u%%", percent); - return 0; -} - -static int manager_new(Manager **ret) { - _cleanup_(manager_unrefp) Manager *m = NULL; - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/import/notify", - }; - static const int one = 1; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_set_watchdog(m->event, true); - - r = sd_bus_default_system(&m->bus); - if (r < 0) - return r; - - m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->notify_fd < 0) - return -errno; - - (void) mkdir_parents_label(sa.un.sun_path, 0755); - (void) unlink(sa.un.sun_path); - - if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - - if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - return -errno; - - r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m); - if (r < 0) - return r; - - *ret = m; - m = NULL; - - return 0; -} - -static Transfer *manager_find(Manager *m, TransferType type, const char *remote) { - Transfer *t; - Iterator i; - - assert(m); - assert(type >= 0); - assert(type < _TRANSFER_TYPE_MAX); - - HASHMAP_FOREACH(t, m->transfers, i) { - - if (t->type == type && - streq_ptr(t->remote, remote)) - return t; - } - - return NULL; -} - -static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - int fd, force, read_only, r; - const char *local, *object; - Manager *m = userdata; - TransferType type; - uint32_t id; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.import", - 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, "hsbb", &fd, &local, &force, &read_only); - if (r < 0) - return r; - - if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); - - r = setup_machine_directory((uint64_t) -1, error); - if (r < 0) - return r; - - type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW; - - r = transfer_new(m, &t); - if (r < 0) - return r; - - t->type = type; - t->force_local = force; - t->read_only = read_only; - - t->local = strdup(local); - if (!t->local) - return -ENOMEM; - - t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (t->stdin_fd < 0) - return -errno; - - r = transfer_start(t); - if (r < 0) - return r; - - object = t->object_path; - id = t->id; - t = NULL; - - return sd_bus_reply_method_return(msg, "uo", id, object); -} - -static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - int fd, r; - const char *local, *object, *format; - Manager *m = userdata; - TransferType type; - uint32_t id; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.export", - 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, "shs", &local, &fd, &format); - if (r < 0) - return r; - - if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); - - type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; - - r = transfer_new(m, &t); - if (r < 0) - return r; - - t->type = type; - - if (!isempty(format)) { - t->format = strdup(format); - if (!t->format) - return -ENOMEM; - } - - t->local = strdup(local); - if (!t->local) - return -ENOMEM; - - t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (t->stdout_fd < 0) - return -errno; - - r = transfer_start(t); - if (r < 0) - return r; - - object = t->object_path; - id = t->id; - t = NULL; - - return sd_bus_reply_method_return(msg, "uo", id, object); -} - -static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - const char *remote, *local, *verify, *object; - Manager *m = userdata; - ImportVerify v; - TransferType type; - 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, "sssb", &remote, &local, &verify, &force); - if (r < 0) - return r; - - if (!http_url_is_valid(remote)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote); - - 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); - - r = setup_machine_directory((uint64_t) -1, error); - if (r < 0) - return r; - - type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW; - - if (manager_find(m, type, 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 = type; - t->verify = v; - t->force_local = force; - - t->remote = strdup(remote); - 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Transfer *t; - Iterator i; - int r; - - assert(msg); - assert(m); - - r = sd_bus_message_new_method_return(msg, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(usssdo)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(t, m->transfers, i) { - - r = sd_bus_message_append( - reply, - "(usssdo)", - t->id, - transfer_type_to_string(t->type), - t->remote, - t->local, - (double) t->progress_percent / 100.0, - t->object_path); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - Transfer *t = userdata; - int r; - - assert(msg); - assert(t); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, - &t->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = transfer_cancel(t); - if (r < 0) - return r; - - return sd_bus_reply_method_return(msg, NULL); -} - -static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Transfer *t; - uint32_t id; - int r; - - assert(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, "u", &id); - if (r < 0) - return r; - if (id <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id"); - - t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); - if (!t) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id); - - r = transfer_cancel(t); - if (r < 0) - return r; - - return sd_bus_reply_method_return(msg, NULL); -} - -static int property_get_progress( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Transfer *t = userdata; - - assert(bus); - assert(reply); - assert(t); - - return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify); - -static const sd_bus_vtable transfer_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), - SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("LogMessage", "us", 0), - SD_BUS_VTABLE_END, -}; - -static const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("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), - SD_BUS_SIGNAL("TransferRemoved", "uos", 0), - SD_BUS_VTABLE_END, -}; - -static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Transfer *t; - const char *p; - uint32_t id; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - p = startswith(path, "/org/freedesktop/import1/transfer/_"); - if (!p) - return 0; - - r = safe_atou32(p, &id); - if (r < 0 || id == 0) - return 0; - - t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); - if (!t) - return 0; - - *found = t; - return 1; -} - -static int transfer_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; - Transfer *t; - unsigned k = 0; - Iterator i; - - l = new0(char*, hashmap_size(m->transfers) + 1); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(t, m->transfers, i) { - - l[k] = strdup(t->object_path); - if (!l[k]) - return -ENOMEM; - - k++; - } - - *nodes = l; - l = NULL; - - return 1; -} - -static int manager_add_bus_objects(Manager *m) { - int r; - - assert(m); - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, 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/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add transfer enumerator: %m"); - - r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - return 0; -} - -static bool manager_check_idle(void *userdata) { - Manager *m = userdata; - - return hashmap_isempty(m->transfers); -} - -static int manager_run(Manager *m) { - assert(m); - - return bus_event_loop_with_idle( - m->event, - m->bus, - "org.freedesktop.import1", - DEFAULT_EXIT_USEC, - manager_check_idle, - m); -} - -int main(int argc, char *argv[]) { - _cleanup_(manager_unrefp) Manager *m = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Failed to allocate manager object: %m"); - goto finish; - } - - r = manager_add_bus_objects(m); - if (r < 0) - goto finish; - - r = manager_run(m); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/org.freedesktop.import1.conf b/src/import/org.freedesktop.import1.conf deleted file mode 100644 index ed2539a03b..0000000000 --- a/src/import/org.freedesktop.import1.conf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/import/org.freedesktop.import1.policy.in b/src/import/org.freedesktop.import1.policy.in deleted file mode 100644 index 85924ed743..0000000000 --- a/src/import/org.freedesktop.import1.policy.in +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Import a VM or container image - <_message>Authentication is required to import a VM or container image - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Export a VM or container image - <_message>Authentication is required to export a VM or container image - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Download a VM or container image - <_message>Authentication is required to download a VM or container image - - auth_admin - auth_admin - auth_admin_keep - - - - diff --git a/src/import/org.freedesktop.import1.service b/src/import/org.freedesktop.import1.service deleted file mode 100644 index 8fc4c47881..0000000000 --- a/src/import/org.freedesktop.import1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.import1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.import1.service diff --git a/src/import/pull-common.c b/src/import/pull-common.c deleted file mode 100644 index 2ae2a4174c..0000000000 --- a/src/import/pull-common.c +++ /dev/null @@ -1,547 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "alloc-util.h" -#include "btrfs-util.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, - const char *image_root, - int dt, - const char *prefix, - const char *suffix, - char ***etags) { - - _cleanup_free_ char *escaped_url = NULL; - _cleanup_closedir_ DIR *d = NULL; - _cleanup_strv_free_ char **l = NULL; - struct dirent *de; - int r; - - assert(url); - assert(etags); - - if (!image_root) - image_root = "/var/lib/machines"; - - escaped_url = xescape(url, FILENAME_ESCAPE); - if (!escaped_url) - return -ENOMEM; - - d = opendir(image_root); - if (!d) { - if (errno == ENOENT) { - *etags = NULL; - return 0; - } - - return -errno; - } - - FOREACH_DIRENT_ALL(de, d, return -errno) { - const char *a, *b; - char *u; - - if (de->d_type != DT_UNKNOWN && - de->d_type != dt) - continue; - - if (prefix) { - a = startswith(de->d_name, prefix); - if (!a) - continue; - } else - a = de->d_name; - - a = startswith(a, escaped_url); - if (!a) - continue; - - a = startswith(a, "."); - if (!a) - continue; - - if (suffix) { - b = endswith(de->d_name, suffix); - if (!b) - continue; - } else - b = strchr(de->d_name, 0); - - if (a >= b) - continue; - - r = cunescape_length(a, b - a, 0, &u); - if (r < 0) - return r; - - if (!http_etag_is_valid(u)) { - free(u); - continue; - } - - r = strv_consume(&l, u); - if (r < 0) - return r; - } - - *etags = l; - l = NULL; - - return 0; -} - -int pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) { - const char *p; - int r; - - assert(final); - assert(local); - - if (!image_root) - image_root = "/var/lib/machines"; - - p = strjoina(image_root, "/", local); - - if (force_local) - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA); - if (r == -ENOTTY) { - r = copy_tree(final, p, false); - if (r < 0) - return log_error_errno(r, "Failed to copy image: %m"); - } else if (r < 0) - return log_error_errno(r, "Failed to create local image: %m"); - - log_info("Created new local image '%s'.", local); - - return 0; -} - -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, *escaped_etag = NULL; - char *path; - - assert(url); - assert(ret); - - if (!image_root) - image_root = "/var/lib/machines"; - - escaped_url = xescape(url, FILENAME_ESCAPE); - if (!escaped_url) - return -ENOMEM; - - if (etag) { - escaped_etag = xescape(etag, FILENAME_ESCAPE); - if (!escaped_etag) - return -ENOMEM; - } - - 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; -} - -int pull_make_settings_job( - PullJob **ret, - const char *url, - CurlGlue *glue, - PullJobFinished on_finished, - void *userdata) { - - _cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL; - _cleanup_(pull_job_unrefp) PullJob *job = NULL; - const char *q; - int r; - - assert(ret); - assert(url); - assert(glue); - - r = import_url_last_component(url, &last_component); - if (r < 0) - return r; - - r = tar_strip_suffixes(last_component, &ll); - if (r < 0) - return r; - - q = strjoina(ll, ".nspawn"); - - r = import_url_change_last_component(url, q, &settings_url); - if (r < 0) - return r; - - r = pull_job_new(&job, settings_url, glue, userdata); - if (r < 0) - return r; - - job->on_finished = on_finished; - job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL; - - *ret = job; - job = NULL; - - return 0; -} - -int pull_make_verification_jobs( - PullJob **ret_checksum_job, - PullJob **ret_signature_job, - ImportVerify verify, - const char *url, - CurlGlue *glue, - PullJobFinished on_finished, - void *userdata) { - - _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL; - int r; - - assert(ret_checksum_job); - assert(ret_signature_job); - assert(verify >= 0); - assert(verify < _IMPORT_VERIFY_MAX); - assert(url); - assert(glue); - - if (verify != IMPORT_VERIFY_NO) { - _cleanup_free_ char *checksum_url = NULL; - - /* Queue job for the SHA256SUMS file for the image */ - r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url); - if (r < 0) - return r; - - r = pull_job_new(&checksum_job, checksum_url, glue, userdata); - if (r < 0) - return r; - - checksum_job->on_finished = on_finished; - checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL; - } - - if (verify == IMPORT_VERIFY_SIGNATURE) { - _cleanup_free_ char *signature_url = NULL; - - /* Queue job for the SHA256SUMS.gpg file for the image. */ - r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url); - if (r < 0) - return r; - - r = pull_job_new(&signature_job, signature_url, glue, userdata); - if (r < 0) - return r; - - signature_job->on_finished = on_finished; - signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL; - } - - *ret_checksum_job = checksum_job; - *ret_signature_job = signature_job; - - checksum_job = signature_job = NULL; - - return 0; -} - -int pull_verify(PullJob *main_job, - PullJob *settings_job, - PullJob *checksum_job, - PullJob *signature_job) { - - _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; - _cleanup_free_ char *fn = NULL; - _cleanup_close_ int sig_file = -1; - const char *p, *line; - char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_(sigkill_waitp) pid_t pid = 0; - bool gpg_home_created = false; - int r; - - assert(main_job); - assert(main_job->state == PULL_JOB_DONE); - - if (!checksum_job) - return 0; - - assert(main_job->calc_checksum); - assert(main_job->checksum); - assert(checksum_job->state == PULL_JOB_DONE); - - if (!checksum_job->payload || checksum_job->payload_size <= 0) { - log_error("Checksum is empty, cannot verify."); - return -EBADMSG; - } - - r = import_url_last_component(main_job->url, &fn); - if (r < 0) - return log_oom(); - - if (!filename_is_valid(fn)) { - log_error("Cannot verify checksum, could not determine valid server-side file name."); - return -EBADMSG; - } - - line = strjoina(main_job->checksum, " *", fn, "\n"); - - p = memmem(checksum_job->payload, - checksum_job->payload_size, - line, - strlen(line)); - - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with."); - return -EBADMSG; - } - - log_info("SHA256 checksum of %s is valid.", main_job->url); - - 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) { - - _cleanup_free_ char *settings_fn = NULL; - - assert(settings_job->calc_checksum); - assert(settings_job->checksum); - - r = import_url_last_component(settings_job->url, &settings_fn); - if (r < 0) - return log_oom(); - - if (!filename_is_valid(settings_fn)) { - log_error("Cannot verify checksum, could not determine server-side settings file name."); - return -EBADMSG; - } - - line = strjoina(settings_job->checksum, " *", settings_fn, "\n"); - - p = memmem(checksum_job->payload, - checksum_job->payload_size, - line, - strlen(line)); - - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with."); - return -EBADMSG; - } - - log_info("SHA256 checksum of %s is valid.", settings_job->url); - } - - if (!signature_job) - return 0; - - assert(signature_job->state == PULL_JOB_DONE); - - if (!signature_job->payload || signature_job->payload_size <= 0) { - log_error("Signature is empty, cannot verify."); - return -EBADMSG; - } - - r = pipe2(gpg_pipe, O_CLOEXEC); - if (r < 0) - return log_error_errno(errno, "Failed to create pipe for gpg: %m"); - - sig_file = mkostemp(sig_file_path, O_RDWR); - if (sig_file < 0) - return log_error_errno(errno, "Failed to create temporary file: %m"); - - r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write to temporary file: %m"); - goto finish; - } - - if (!mkdtemp(gpg_home)) { - r = log_error_errno(errno, "Failed to create tempory home for gpg: %m"); - goto finish; - } - - gpg_home_created = true; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off gpg: %m"); - if (pid == 0) { - const char *cmd[] = { - "gpg", - "--no-options", - "--no-default-keyring", - "--no-auto-key-locate", - "--no-auto-check-trustdb", - "--batch", - "--trust-model=always", - NULL, /* --homedir= */ - NULL, /* --keyring= */ - NULL, /* --verify */ - NULL, /* signature file */ - NULL, /* dash */ - NULL /* trailing NULL */ - }; - unsigned k = ELEMENTSOF(cmd) - 6; - int null_fd; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - gpg_pipe[1] = safe_close(gpg_pipe[1]); - - if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (gpg_pipe[0] != STDIN_FILENO) - gpg_pipe[0] = safe_close(gpg_pipe[0]); - - null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDOUT_FILENO) - null_fd = safe_close(null_fd); - - cmd[k++] = strjoina("--homedir=", gpg_home); - - /* We add the user keyring only to the command line - * arguments, if it's around since gpg fails - * otherwise. */ - if (access(USER_KEYRING_PATH, F_OK) >= 0) - cmd[k++] = "--keyring=" USER_KEYRING_PATH; - else - cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH; - - cmd[k++] = "--verify"; - cmd[k++] = sig_file_path; - cmd[k++] = "-"; - cmd[k++] = NULL; - - stdio_unset_cloexec(); - - execvp("gpg2", (char * const *) cmd); - execvp("gpg", (char * const *) cmd); - log_error_errno(errno, "Failed to execute gpg: %m"); - _exit(EXIT_FAILURE); - } - - gpg_pipe[0] = safe_close(gpg_pipe[0]); - - r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write to pipe: %m"); - goto finish; - } - - gpg_pipe[1] = safe_close(gpg_pipe[1]); - - r = wait_for_terminate_and_warn("gpg", pid, true); - pid = 0; - if (r < 0) - goto finish; - if (r > 0) { - log_error("DOWNLOAD INVALID: Signature verification failed."); - r = -EBADMSG; - } else { - log_info("Signature verification succeeded."); - r = 0; - } - -finish: - if (sig_file >= 0) - (void) unlink(sig_file_path); - - if (gpg_home_created) - (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL); - - return r; -} diff --git a/src/import/pull-common.h b/src/import/pull-common.h deleted file mode 100644 index 929a131c88..0000000000 --- a/src/import/pull-common.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include - -#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); - -int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags); - -int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); - -int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); -int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); - -int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); diff --git a/src/import/pull-job.c b/src/import/pull-job.c deleted file mode 100644 index 6bcf35ef4e..0000000000 --- a/src/import/pull-job.c +++ /dev/null @@ -1,618 +0,0 @@ -/*** - 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 . -***/ - -#include - -#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) - return NULL; - - curl_glue_remove_and_free(j->glue, j->curl); - curl_slist_free_all(j->request_header); - - safe_close(j->disk_fd); - - import_compress_free(&j->compress); - - if (j->checksum_context) - gcry_md_close(j->checksum_context); - - free(j->url); - free(j->etag); - strv_free(j->old_etags); - free(j->payload); - free(j->checksum); - - free(j); - - return NULL; -} - -static void pull_job_finish(PullJob *j, int ret) { - assert(j); - - if (j->state == PULL_JOB_DONE || - j->state == PULL_JOB_FAILED) - return; - - if (ret == 0) { - j->state = PULL_JOB_DONE; - j->progress_percent = 100; - log_info("Download of %s complete.", j->url); - } else { - j->state = PULL_JOB_FAILED; - j->error = ret; - } - - if (j->on_finished) - j->on_finished(j); -} - -void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { - PullJob *j = NULL; - CURLcode code; - long status; - int r; - - if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK) - return; - - if (!j || j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) - return; - - if (result != CURLE_OK) { - log_error("Transfer failed: %s", curl_easy_strerror(result)); - r = -EIO; - goto finish; - } - - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); - if (code != CURLE_OK) { - log_error("Failed to retrieve response code: %s", curl_easy_strerror(code)); - r = -EIO; - goto finish; - } else if (status == 304) { - log_info("Image already downloaded. Skipping download."); - j->etag_exists = true; - r = 0; - goto finish; - } else if (status >= 300) { - log_error("HTTP request to %s failed with code %li.", j->url, status); - r = -EIO; - goto finish; - } else if (status < 200) { - log_error("HTTP request to %s finished with unexpected code %li.", j->url, status); - r = -EIO; - goto finish; - } - - if (j->state != PULL_JOB_RUNNING) { - log_error("Premature connection termination."); - r = -EIO; - goto finish; - } - - if (j->content_length != (uint64_t) -1 && - j->content_length != j->written_compressed) { - log_error("Download truncated."); - r = -EIO; - goto finish; - } - - if (j->checksum_context) { - uint8_t *k; - - k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256); - if (!k) { - log_error("Failed to get checksum."); - r = -EIO; - goto finish; - } - - j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256)); - if (!j->checksum) { - r = log_oom(); - goto finish; - } - - log_debug("SHA256 of %s is %s.", j->url, j->checksum); - } - - if (j->disk_fd >= 0 && j->allow_sparse) { - /* Make sure the file size is right, in case the file was - * sparse and we just seeked for the last part */ - - if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) { - r = log_error_errno(errno, "Failed to truncate file: %m"); - goto finish; - } - - if (j->etag) - (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0); - if (j->url) - (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0); - - if (j->mtime != 0) { - struct timespec ut[2]; - - timespec_store(&ut[0], j->mtime); - ut[1] = ut[0]; - (void) futimens(j->disk_fd, ut); - - (void) fd_setcrtime(j->disk_fd, j->mtime); - } - } - - r = 0; - -finish: - pull_job_finish(j, r); -} - -static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) { - PullJob *j = userdata; - ssize_t n; - - assert(j); - assert(p); - - if (sz <= 0) - return 0; - - if (j->written_uncompressed + sz < j->written_uncompressed) { - log_error("File too large, overflow"); - return -EOVERFLOW; - } - - if (j->written_uncompressed + sz > j->uncompressed_max) { - log_error("File overly large, refusing"); - return -EFBIG; - } - - if (j->disk_fd >= 0) { - - if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) { - j->written_since_last_grow = 0; - grow_machine_directory(); - } - - if (j->allow_sparse) - n = sparse_write(j->disk_fd, p, sz, 64); - else - n = write(j->disk_fd, p, sz); - if (n < 0) - return log_error_errno(errno, "Failed to write file: %m"); - if ((size_t) n < sz) { - log_error("Short write"); - return -EIO; - } - } else { - - if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) - return log_oom(); - - memcpy(j->payload + j->payload_size, p, sz); - j->payload_size += sz; - } - - j->written_uncompressed += sz; - j->written_since_last_grow += sz; - - return 0; -} - -static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { - int r; - - assert(j); - assert(p); - - if (sz <= 0) - return 0; - - if (j->written_compressed + sz < j->written_compressed) { - log_error("File too large, overflow"); - return -EOVERFLOW; - } - - if (j->written_compressed + sz > j->compressed_max) { - log_error("File overly large, refusing."); - return -EFBIG; - } - - if (j->content_length != (uint64_t) -1 && - j->written_compressed + sz > j->content_length) { - log_error("Content length incorrect."); - return -EFBIG; - } - - if (j->checksum_context) - gcry_md_write(j->checksum_context, p, sz); - - r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); - if (r < 0) - return r; - - j->written_compressed += sz; - - return 0; -} - -static int pull_job_open_disk(PullJob *j) { - int r; - - assert(j); - - if (j->on_open_disk) { - r = j->on_open_disk(j); - if (r < 0) - return r; - } - - if (j->disk_fd >= 0) { - /* Check if we can do sparse files */ - - if (lseek(j->disk_fd, SEEK_SET, 0) == 0) - j->allow_sparse = true; - else { - if (errno != ESPIPE) - return log_error_errno(errno, "Failed to seek on file descriptor: %m"); - - j->allow_sparse = false; - } - } - - if (j->calc_checksum) { - if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) { - log_error("Failed to initialize hash context."); - return -EIO; - } - } - - return 0; -} - -static int pull_job_detect_compression(PullJob *j) { - _cleanup_free_ uint8_t *stub = NULL; - size_t stub_size; - - int r; - - assert(j); - - r = import_uncompress_detect(&j->compress, j->payload, j->payload_size); - if (r < 0) - return log_error_errno(r, "Failed to initialize compressor: %m"); - if (r == 0) - return 0; - - log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type)); - - r = pull_job_open_disk(j); - if (r < 0) - return r; - - /* Now, take the payload we read so far, and decompress it */ - stub = j->payload; - stub_size = j->payload_size; - - j->payload = NULL; - j->payload_size = 0; - j->payload_allocated = 0; - - j->state = PULL_JOB_RUNNING; - - r = pull_job_write_compressed(j, stub, stub_size); - if (r < 0) - return r; - - return 0; -} - -static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) { - PullJob *j = userdata; - size_t sz = size * nmemb; - int r; - - assert(contents); - assert(j); - - switch (j->state) { - - case PULL_JOB_ANALYZING: - /* Let's first check what it actually is */ - - if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) { - r = log_oom(); - goto fail; - } - - memcpy(j->payload + j->payload_size, contents, sz); - j->payload_size += sz; - - r = pull_job_detect_compression(j); - if (r < 0) - goto fail; - - break; - - case PULL_JOB_RUNNING: - - r = pull_job_write_compressed(j, contents, sz); - if (r < 0) - goto fail; - - break; - - case PULL_JOB_DONE: - case PULL_JOB_FAILED: - r = -ESTALE; - goto fail; - - default: - assert_not_reached("Impossible state."); - } - - return sz; - -fail: - pull_job_finish(j, r); - return 0; -} - -static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { - PullJob *j = userdata; - size_t sz = size * nmemb; - _cleanup_free_ char *length = NULL, *last_modified = NULL; - char *etag; - int r; - - assert(contents); - assert(j); - - if (j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) { - r = -ESTALE; - goto fail; - } - - assert(j->state == PULL_JOB_ANALYZING); - - r = curl_header_strdup(contents, sz, "ETag:", &etag); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - free(j->etag); - j->etag = etag; - - if (strv_contains(j->old_etags, j->etag)) { - log_info("Image already downloaded. Skipping download."); - j->etag_exists = true; - pull_job_finish(j, 0); - return sz; - } - - return sz; - } - - r = curl_header_strdup(contents, sz, "Content-Length:", &length); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - (void) safe_atou64(length, &j->content_length); - - if (j->content_length != (uint64_t) -1) { - char bytes[FORMAT_BYTES_MAX]; - - if (j->content_length > j->compressed_max) { - log_error("Content too large."); - r = -EFBIG; - goto fail; - } - - log_info("Downloading %s for %s.", format_bytes(bytes, sizeof(bytes), j->content_length), j->url); - } - - return sz; - } - - r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - (void) curl_parse_http_time(last_modified, &j->mtime); - return sz; - } - - if (j->on_header) { - r = j->on_header(j, contents, sz); - if (r < 0) - goto fail; - } - - return sz; - -fail: - pull_job_finish(j, r); - return 0; -} - -static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - PullJob *j = userdata; - unsigned percent; - usec_t n; - - assert(j); - - if (dltotal <= 0) - return 0; - - percent = ((100 * dlnow) / dltotal); - n = now(CLOCK_MONOTONIC); - - if (n > j->last_status_usec + USEC_PER_SEC && - percent != j->progress_percent && - dlnow < dltotal) { - char buf[FORMAT_TIMESPAN_MAX]; - - if (n - j->start_usec > USEC_PER_SEC && dlnow > 0) { - char y[FORMAT_BYTES_MAX]; - usec_t left, done; - - done = n - j->start_usec; - left = (usec_t) (((double) done * (double) dltotal) / dlnow) - done; - - log_info("Got %u%% of %s. %s left at %s/s.", - percent, - j->url, - format_timespan(buf, sizeof(buf), left, USEC_PER_SEC), - format_bytes(y, sizeof(y), (uint64_t) ((double) dlnow / ((double) done / (double) USEC_PER_SEC)))); - } else - log_info("Got %u%% of %s.", percent, j->url); - - j->progress_percent = percent; - j->last_status_usec = n; - - if (j->on_progress) - j->on_progress(j); - } - - return 0; -} - -int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) { - _cleanup_(pull_job_unrefp) PullJob *j = NULL; - - assert(url); - assert(glue); - assert(ret); - - j = new0(PullJob, 1); - if (!j) - return -ENOMEM; - - j->state = PULL_JOB_INIT; - j->disk_fd = -1; - j->userdata = userdata; - j->glue = glue; - j->content_length = (uint64_t) -1; - j->start_usec = now(CLOCK_MONOTONIC); - j->compressed_max = j->uncompressed_max = 8LLU * 1024LLU * 1024LLU * 1024LLU; /* 8GB */ - - j->url = strdup(url); - if (!j->url) - return -ENOMEM; - - *ret = j; - j = NULL; - - return 0; -} - -int pull_job_begin(PullJob *j) { - int r; - - assert(j); - - if (j->state != PULL_JOB_INIT) - return -EBUSY; - - if (j->grow_machine_directory) - grow_machine_directory(); - - r = curl_glue_make(&j->curl, j->url, j); - if (r < 0) - return r; - - if (!strv_isempty(j->old_etags)) { - _cleanup_free_ char *cc = NULL, *hdr = NULL; - - cc = strv_join(j->old_etags, ", "); - if (!cc) - return -ENOMEM; - - hdr = strappend("If-None-Match: ", cc); - if (!hdr) - return -ENOMEM; - - if (!j->request_header) { - j->request_header = curl_slist_new(hdr, NULL); - if (!j->request_header) - return -ENOMEM; - } else { - struct curl_slist *l; - - l = curl_slist_append(j->request_header, hdr); - if (!l) - return -ENOMEM; - - j->request_header = l; - } - } - - if (j->request_header) { - if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK) - return -EIO; - } - - if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK) - return -EIO; - - r = curl_glue_add(j->glue, j->curl); - if (r < 0) - return r; - - j->state = PULL_JOB_ANALYZING; - - return 0; -} diff --git a/src/import/pull-job.h b/src/import/pull-job.h deleted file mode 100644 index 3a152a50e3..0000000000 --- a/src/import/pull-job.h +++ /dev/null @@ -1,106 +0,0 @@ -#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 . -***/ - -#include - -#include "curl-util.h" -#include "import-compress.h" -#include "macro.h" - -typedef struct PullJob PullJob; - -typedef void (*PullJobFinished)(PullJob *job); -typedef int (*PullJobOpenDisk)(PullJob *job); -typedef int (*PullJobHeader)(PullJob *job, const char *header, size_t sz); -typedef void (*PullJobProgress)(PullJob *job); - -typedef enum PullJobState { - PULL_JOB_INIT, - PULL_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */ - PULL_JOB_RUNNING, /* Writing to destination */ - PULL_JOB_DONE, - PULL_JOB_FAILED, - _PULL_JOB_STATE_MAX, - _PULL_JOB_STATE_INVALID = -1, -} PullJobState; - -#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) - -struct PullJob { - PullJobState state; - int error; - - char *url; - - void *userdata; - PullJobFinished on_finished; - PullJobOpenDisk on_open_disk; - PullJobHeader on_header; - PullJobProgress on_progress; - - CurlGlue *glue; - CURL *curl; - struct curl_slist *request_header; - - char *etag; - char **old_etags; - bool etag_exists; - - uint64_t content_length; - uint64_t written_compressed; - uint64_t written_uncompressed; - - uint64_t uncompressed_max; - uint64_t compressed_max; - - uint8_t *payload; - size_t payload_size; - size_t payload_allocated; - - int disk_fd; - - usec_t mtime; - - ImportCompress compress; - - unsigned progress_percent; - usec_t start_usec; - usec_t last_status_usec; - - bool allow_sparse; - - bool calc_checksum; - gcry_md_hd_t checksum_context; - - char *checksum; - - bool grow_machine_directory; - uint64_t written_since_last_grow; -}; - -int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata); -PullJob* pull_job_unref(PullJob *job); - -int pull_job_begin(PullJob *j); - -void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result); - -DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref); diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c deleted file mode 100644 index 8993402821..0000000000 --- a/src/import/pull-raw.c +++ /dev/null @@ -1,651 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-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 "path-util.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, - RAW_VERIFYING, - RAW_UNPACKING, - RAW_FINALIZING, - RAW_COPYING, -} RawProgress; - -struct RawPull { - sd_event *event; - CurlGlue *glue; - - char *image_root; - - PullJob *raw_job; - PullJob *settings_job; - PullJob *checksum_job; - PullJob *signature_job; - - RawPullFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool grow_machine_directory; - bool settings; - - char *final_path; - char *temp_path; - - char *settings_path; - char *settings_temp_path; - - ImportVerify verify; -}; - -RawPull* raw_pull_unref(RawPull *i) { - if (!i) - return NULL; - - pull_job_unref(i->raw_job); - pull_job_unref(i->settings_job); - pull_job_unref(i->checksum_job); - pull_job_unref(i->signature_job); - - curl_glue_unref(i->glue); - sd_event_unref(i->event); - - if (i->temp_path) { - (void) unlink(i->temp_path); - free(i->temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } - - free(i->final_path); - free(i->settings_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int raw_pull_new( - RawPull **ret, - sd_event *event, - const char *image_root, - RawPullFinished on_finished, - void *userdata) { - - _cleanup_(raw_pull_unrefp) RawPull *i = NULL; - int r; - - assert(ret); - - i = new0(RawPull, 1); - if (!i) - return -ENOMEM; - - i->on_finished = on_finished; - i->userdata = userdata; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - r = curl_glue_new(&i->glue, i->event); - if (r < 0) - return r; - - i->glue->on_finished = pull_job_curl_on_finished; - i->glue->userdata = i; - - *ret = i; - i = NULL; - - return 0; -} - -static void raw_pull_report_progress(RawPull *i, RawProgress p) { - unsigned percent; - - assert(i); - - switch (p) { - - case RAW_DOWNLOADING: { - unsigned remain = 80; - - percent = 0; - - if (i->settings_job) { - percent += i->settings_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->checksum_job) { - percent += i->checksum_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->signature_job) { - percent += i->signature_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->raw_job) - percent += i->raw_job->progress_percent * remain / 100; - break; - } - - case RAW_VERIFYING: - percent = 80; - break; - - case RAW_UNPACKING: - percent = 85; - break; - - case RAW_FINALIZING: - percent = 90; - break; - - case RAW_COPYING: - percent = 95; - break; - - default: - assert_not_reached("Unknown progress state"); - } - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_debug("Combined progress %u%%", percent); -} - -static int raw_pull_maybe_convert_qcow2(RawPull *i) { - _cleanup_close_ int converted_fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - assert(i); - assert(i->raw_job); - - r = qcow2_detect(i->raw_job->disk_fd); - if (r < 0) - return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); - if (r == 0) - return 0; - - /* This is a QCOW2 image, let's convert it */ - r = tempfn_random(i->final_path, NULL, &t); - if (r < 0) - return log_oom(); - - converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (converted_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", t); - - r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", t); - - log_info("Unpacking QCOW2 file."); - - r = qcow2_convert(i->raw_job->disk_fd, converted_fd); - if (r < 0) { - unlink(t); - return log_error_errno(r, "Failed to convert qcow2 image: %m"); - } - - (void) unlink(i->temp_path); - free(i->temp_path); - i->temp_path = t; - t = NULL; - - safe_close(i->raw_job->disk_fd); - i->raw_job->disk_fd = converted_fd; - converted_fd = -1; - - return 1; -} - -static int raw_pull_make_local_copy(RawPull *i) { - _cleanup_free_ char *tp = NULL; - _cleanup_close_ int dfd = -1; - const char *p; - int r; - - assert(i); - assert(i->raw_job); - - if (!i->local) - return 0; - - if (!i->final_path) { - r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - } - - if (i->raw_job->etag_exists) { - /* We have downloaded this one previously, reopen it */ - - assert(i->raw_job->disk_fd < 0); - - i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (i->raw_job->disk_fd < 0) - return log_error_errno(errno, "Failed to open vendor image: %m"); - } else { - /* We freshly downloaded the image, use it */ - - assert(i->raw_job->disk_fd >= 0); - - if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1) - return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m"); - } - - p = strjoina(i->image_root, "/", i->local, ".raw"); - - if (i->force_local) - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = tempfn_random(p, NULL, &tp); - if (r < 0) - return log_oom(); - - dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (dfd < 0) - return log_error_errno(errno, "Failed to create writable copy of image: %m"); - - /* Turn off COW writing. This should greatly improve - * performance on COW file systems like btrfs, since it - * reduces fragmentation caused by not allowing in-place - * writes. */ - r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(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) { - unlink(tp); - return log_error_errno(r, "Failed to make writable copy of image: %m"); - } - - (void) copy_times(i->raw_job->disk_fd, dfd); - (void) copy_xattr(i->raw_job->disk_fd, dfd); - - dfd = safe_close(dfd); - - r = rename(tp, p); - if (r < 0) { - r = log_error_errno(errno, "Failed to move writable image into place: %m"); - unlink(tp); - return r; - } - - log_info("Created new local image '%s'.", i->local); - - if (i->settings) { - const char *local_settings; - assert(i->settings_job); - - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } - - local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - - r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0); - if (r == -EEXIST) - log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r == -ENOENT) - log_debug_errno(r, "Skipping creation of settings file, since none was found."); - else if (r < 0) - log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); - else - log_info("Created new settings file %s.", local_settings); - } - - return 0; -} - -static bool raw_pull_is_done(RawPull *i) { - assert(i); - assert(i->raw_job); - - if (!PULL_JOB_IS_COMPLETE(i->raw_job)) - return false; - if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) - return false; - if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) - return false; - if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) - return false; - - return true; -} - -static void raw_pull_job_on_finished(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - if (j == i->settings_job) { - if (j->error != 0) - log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { - if (j == i->checksum_job) - log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); - else - log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); - - r = j->error; - goto finish; - } - - /* This is invoked if either the download completed - * successfully, or the download was skipped because we - * already have the etag. In this case ->etag_exists is - * true. - * - * We only do something when we got all three files */ - - if (!raw_pull_is_done(i)) - return; - - if (i->settings_job) - i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); - - if (!i->raw_job->etag_exists) { - /* This is a new download, verify it, and move it into place */ - assert(i->raw_job->disk_fd >= 0); - - raw_pull_report_progress(i, RAW_VERIFYING); - - r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job); - if (r < 0) - goto finish; - - raw_pull_report_progress(i, RAW_UNPACKING); - - r = raw_pull_maybe_convert_qcow2(i); - if (r < 0) - goto finish; - - raw_pull_report_progress(i, RAW_FINALIZING); - - r = import_make_read_only_fd(i->raw_job->disk_fd); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) { - log_error_errno(r, "Failed to move RAW file into place: %m"); - goto finish; - } - - i->temp_path = mfree(i->temp_path); - - if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { - - assert(i->settings_temp_path); - assert(i->settings_path); - - r = import_make_read_only(i->settings_temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); - if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); - goto finish; - } - - i->settings_temp_path = mfree(i->settings_temp_path); - } - } - - raw_pull_report_progress(i, RAW_COPYING); - - r = raw_pull_make_local_copy(i); - if (r < 0) - goto finish; - - r = 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); -} - -static int raw_pull_job_on_open_disk_raw(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->raw_job == j); - assert(!i->final_path); - assert(!i->temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->temp_path); - - r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); - - return 0; -} - -static int raw_pull_job_on_open_disk_settings(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->settings_temp_path, 0700); - - j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); - - return 0; -} - -static void raw_pull_job_on_progress(PullJob *j) { - RawPull *i; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - raw_pull_report_progress(i, RAW_DOWNLOADING); -} - -int raw_pull_start( - RawPull *i, - const char *url, - const char *local, - bool force_local, - ImportVerify verify, - bool settings) { - - int r; - - assert(i); - assert(verify < _IMPORT_VERIFY_MAX); - assert(verify >= 0); - - if (!http_url_is_valid(url)) - return -EINVAL; - - if (local && !machine_name_is_valid(local)) - return -EINVAL; - - if (i->raw_job) - return -EBUSY; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - - i->force_local = force_local; - i->verify = verify; - i->settings = settings; - - /* Queue job for the image itself */ - r = pull_job_new(&i->raw_job, url, i->glue, i); - if (r < 0) - return r; - - i->raw_job->on_finished = raw_pull_job_on_finished; - i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; - i->raw_job->on_progress = raw_pull_job_on_progress; - i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO; - i->raw_job->grow_machine_directory = i->grow_machine_directory; - - r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags); - if (r < 0) - return r; - - if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i); - if (r < 0) - return r; - - i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; - i->settings_job->on_progress = raw_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; - } - - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); - if (r < 0) - return r; - - r = pull_job_begin(i->raw_job); - if (r < 0) - return r; - - if (i->settings_job) { - r = pull_job_begin(i->settings_job); - if (r < 0) - return r; - } - - if (i->checksum_job) { - i->checksum_job->on_progress = raw_pull_job_on_progress; - - r = pull_job_begin(i->checksum_job); - if (r < 0) - return r; - } - - if (i->signature_job) { - i->signature_job->on_progress = raw_pull_job_on_progress; - - r = pull_job_begin(i->signature_job); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h deleted file mode 100644 index 8f6d16eb3a..0000000000 --- a/src/import/pull-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct RawPull RawPull; - -typedef void (*RawPullFinished)(RawPull *pull, int error, void *userdata); - -int raw_pull_new(RawPull **pull, sd_event *event, const char *image_root, RawPullFinished on_finished, void *userdata); -RawPull* raw_pull_unref(RawPull *pull); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); - -int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c deleted file mode 100644 index 8c61c46f73..0000000000 --- a/src/import/pull-tar.c +++ /dev/null @@ -1,563 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-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 "path-util.h" -#include "process-util.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, - TAR_VERIFYING, - TAR_FINALIZING, - TAR_COPYING, -} TarProgress; - -struct TarPull { - sd_event *event; - CurlGlue *glue; - - char *image_root; - - PullJob *tar_job; - PullJob *settings_job; - PullJob *checksum_job; - PullJob *signature_job; - - TarPullFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool grow_machine_directory; - bool settings; - - pid_t tar_pid; - - char *final_path; - char *temp_path; - - char *settings_path; - char *settings_temp_path; - - ImportVerify verify; -}; - -TarPull* tar_pull_unref(TarPull *i) { - if (!i) - return NULL; - - if (i->tar_pid > 1) { - (void) kill_and_sigcont(i->tar_pid, SIGKILL); - (void) wait_for_terminate(i->tar_pid, NULL); - } - - pull_job_unref(i->tar_job); - pull_job_unref(i->settings_job); - pull_job_unref(i->checksum_job); - pull_job_unref(i->signature_job); - - curl_glue_unref(i->glue); - sd_event_unref(i->event); - - if (i->temp_path) { - (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - free(i->temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } - - free(i->final_path); - free(i->settings_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int tar_pull_new( - TarPull **ret, - sd_event *event, - const char *image_root, - TarPullFinished on_finished, - void *userdata) { - - _cleanup_(tar_pull_unrefp) TarPull *i = NULL; - int r; - - assert(ret); - - i = new0(TarPull, 1); - if (!i) - return -ENOMEM; - - i->on_finished = on_finished; - i->userdata = userdata; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - r = curl_glue_new(&i->glue, i->event); - if (r < 0) - return r; - - i->glue->on_finished = pull_job_curl_on_finished; - i->glue->userdata = i; - - *ret = i; - i = NULL; - - return 0; -} - -static void tar_pull_report_progress(TarPull *i, TarProgress p) { - unsigned percent; - - assert(i); - - switch (p) { - - case TAR_DOWNLOADING: { - unsigned remain = 85; - - percent = 0; - - if (i->settings_job) { - percent += i->settings_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->checksum_job) { - percent += i->checksum_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->signature_job) { - percent += i->signature_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->tar_job) - percent += i->tar_job->progress_percent * remain / 100; - break; - } - - case TAR_VERIFYING: - percent = 85; - break; - - case TAR_FINALIZING: - percent = 90; - break; - - case TAR_COPYING: - percent = 95; - break; - - default: - assert_not_reached("Unknown progress state"); - } - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_debug("Combined progress %u%%", percent); -} - -static int tar_pull_make_local_copy(TarPull *i) { - int r; - - assert(i); - assert(i->tar_job); - - if (!i->local) - return 0; - - if (!i->final_path) { - r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - } - - r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); - if (r < 0) - return r; - - if (i->settings) { - const char *local_settings; - assert(i->settings_job); - - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } - - local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - - r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0); - if (r == -EEXIST) - log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r == -ENOENT) - log_debug_errno(r, "Skipping creation of settings file, since none was found."); - else if (r < 0) - log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); - else - log_info("Created new settings file %s.", local_settings); - } - - return 0; -} - -static bool tar_pull_is_done(TarPull *i) { - assert(i); - assert(i->tar_job); - - if (!PULL_JOB_IS_COMPLETE(i->tar_job)) - return false; - if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) - return false; - if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) - return false; - if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) - return false; - - return true; -} - -static void tar_pull_job_on_finished(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - if (j == i->settings_job) { - if (j->error != 0) - log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { - if (j == i->checksum_job) - log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); - else - log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); - - r = j->error; - goto finish; - } - - /* This is invoked if either the download completed - * successfully, or the download was skipped because we - * already have the etag. */ - - if (!tar_pull_is_done(i)) - return; - - i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd); - if (i->settings_job) - i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); - - if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); - i->tar_pid = 0; - if (r < 0) - goto finish; - if (r > 0) { - r = -EIO; - goto finish; - } - } - - if (!i->tar_job->etag_exists) { - /* This is a new download, verify it, and move it into place */ - - tar_pull_report_progress(i, TAR_VERIFYING); - - r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job); - if (r < 0) - goto finish; - - tar_pull_report_progress(i, TAR_FINALIZING); - - r = import_make_read_only(i->temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) { - log_error_errno(r, "Failed to rename to final image name: %m"); - goto finish; - } - - i->temp_path = mfree(i->temp_path); - - if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { - - assert(i->settings_temp_path); - assert(i->settings_path); - - /* Also move the settings file into place, if - * it exist. Note that we do so only if we - * also moved the tar file in place, to keep - * things strictly in sync. */ - - r = import_make_read_only(i->settings_temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); - if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); - goto finish; - } - - i->settings_temp_path = mfree(i->settings_temp_path); - } - } - - tar_pull_report_progress(i, TAR_COPYING); - - r = tar_pull_make_local_copy(i); - if (r < 0) - goto finish; - - r = 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); -} - -static int tar_pull_job_on_open_disk_tar(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->tar_job == j); - assert(!i->final_path); - assert(!i->temp_path); - assert(i->tar_pid <= 0); - - r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->temp_path, 0700); - - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(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) - return j->disk_fd; - - return 0; -} - -static int tar_pull_job_on_open_disk_settings(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->settings_temp_path, 0700); - - j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); - - return 0; -} - -static void tar_pull_job_on_progress(PullJob *j) { - TarPull *i; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - tar_pull_report_progress(i, TAR_DOWNLOADING); -} - -int tar_pull_start( - TarPull *i, - const char *url, - const char *local, - bool force_local, - ImportVerify verify, - bool settings) { - - int r; - - assert(i); - assert(verify < _IMPORT_VERIFY_MAX); - assert(verify >= 0); - - if (!http_url_is_valid(url)) - return -EINVAL; - - if (local && !machine_name_is_valid(local)) - return -EINVAL; - - if (i->tar_job) - return -EBUSY; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - - i->force_local = force_local; - i->verify = verify; - i->settings = settings; - - /* Set up download job for TAR file */ - r = pull_job_new(&i->tar_job, url, i->glue, i); - if (r < 0) - return r; - - i->tar_job->on_finished = tar_pull_job_on_finished; - i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; - i->tar_job->on_progress = tar_pull_job_on_progress; - i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; - i->tar_job->grow_machine_directory = i->grow_machine_directory; - - r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); - if (r < 0) - return r; - - /* Set up download job for the settings file (.nspawn) */ - if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i); - if (r < 0) - return r; - - i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings; - i->settings_job->on_progress = tar_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; - } - - /* Set up download of checksum/signature files */ - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); - if (r < 0) - return r; - - r = pull_job_begin(i->tar_job); - if (r < 0) - return r; - - if (i->settings_job) { - r = pull_job_begin(i->settings_job); - if (r < 0) - return r; - } - - if (i->checksum_job) { - i->checksum_job->on_progress = tar_pull_job_on_progress; - - r = pull_job_begin(i->checksum_job); - if (r < 0) - return r; - } - - if (i->signature_job) { - i->signature_job->on_progress = tar_pull_job_on_progress; - - r = pull_job_begin(i->signature_job); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h deleted file mode 100644 index 7e63e496d8..0000000000 --- a/src/import/pull-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 . -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct TarPull TarPull; - -typedef void (*TarPullFinished)(TarPull *pull, int error, void *userdata); - -int tar_pull_new(TarPull **pull, sd_event *event, const char *image_root, TarPullFinished on_finished, void *userdata); -TarPull* tar_pull_unref(TarPull *pull); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); - -int tar_pull_start(TarPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/import/pull.c b/src/import/pull.c deleted file mode 100644 index 53b1211965..0000000000 --- a/src/import/pull.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-event.h" - -#include "alloc-util.h" -#include "hostname-util.h" -#include "import-util.h" -#include "machine-image.h" -#include "parse-util.h" -#include "pull-raw.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 bool arg_settings = true; - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarPull *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_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_pull_unrefp) TarPull *pull = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *url, *local; - _cleanup_free_ char *l = NULL, *ll = NULL; - int r; - - url = argv[1]; - if (!http_url_is_valid(url)) { - log_error("URL '%s' is not valid.", url); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(url, &l); - if (r < 0) - return log_error_errno(r, "Failed get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error("Image '%s' already exists.", local); - return -EEXIST; - } - } - - log_info("Pulling '%s', saving as '%s'.", url, local); - } else - log_info("Pulling '%s'.", url); - - 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 = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate puller: %m"); - - r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); - 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 void on_raw_finished(RawPull *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_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_pull_unrefp) RawPull *pull = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *url, *local; - _cleanup_free_ char *l = NULL, *ll = NULL; - int r; - - url = argv[1]; - if (!http_url_is_valid(url)) { - log_error("URL '%s' is not valid.", url); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(url, &l); - if (r < 0) - return log_error_errno(r, "Failed get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error("Image '%s' already exists.", local); - return -EEXIST; - } - } - - log_info("Pulling '%s', saving as '%s'.", url, local); - } else - log_info("Pulling '%s'.", url); - - 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 = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate puller: %m"); - - r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); - 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" - "Download container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --force Force creation of image\n" - " --verify=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\n" - "Commands:\n" - " tar URL [NAME] Download a TAR image\n" - " raw URL [NAME] Download a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORCE, - ARG_IMAGE_ROOT, - ARG_VERIFY, - ARG_SETTINGS, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "force", no_argument, NULL, ARG_FORCE }, - { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, - { "verify", required_argument, NULL, ARG_VERIFY }, - { "settings", required_argument, NULL, ARG_SETTINGS }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORCE: - arg_force = true; - break; - - case ARG_IMAGE_ROOT: - arg_image_root = optarg; - break; - - case ARG_VERIFY: - arg_verify = import_verify_from_string(optarg); - if (arg_verify < 0) { - log_error("Invalid verification setting '%s'", optarg); - return -EINVAL; - } - - break; - - case ARG_SETTINGS: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg); - - arg_settings = r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int pull_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, pull_tar }, - { "raw", 2, 3, 0, pull_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = pull_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c deleted file mode 100644 index ee2121cc36..0000000000 --- a/src/import/qcow2-util.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "qcow2-util.h" -#include "sparse-endian.h" -#include "util.h" - -#define QCOW2_MAGIC 0x514649fb - -#define QCOW2_COPIED (1ULL << 63) -#define QCOW2_COMPRESSED (1ULL << 62) -#define QCOW2_ZERO (1ULL << 0) - -typedef struct _packed_ Header { - be32_t magic; - be32_t version; - - be64_t backing_file_offset; - be32_t backing_file_size; - - be32_t cluster_bits; - be64_t size; - be32_t crypt_method; - - be32_t l1_size; - be64_t l1_table_offset; - - be64_t refcount_table_offset; - be32_t refcount_table_clusters; - - be32_t nb_snapshots; - be64_t snapshots_offset; - - /* The remainder is only present on QCOW3 */ - be64_t incompatible_features; - be64_t compatible_features; - be64_t autoclear_features; - - be32_t refcount_order; - be32_t header_length; -} Header; - -#define HEADER_MAGIC(header) be32toh((header)->magic) -#define HEADER_VERSION(header) be32toh((header)->version) -#define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits) -#define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header)) -#define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3) -#define HEADER_SIZE(header) be64toh((header)->size) -#define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method) -#define HEADER_L1_SIZE(header) be32toh((header)->l1_size) -#define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t)) -#define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset) - -static uint32_t HEADER_HEADER_LENGTH(const Header *h) { - if (HEADER_VERSION(h) < 3) - return offsetof(Header, incompatible_features); - - return be32toh(h->header_length); -} - -static int copy_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t cluster_size, - void *buffer) { - - ssize_t l; - int r; - - r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size); - if (r >= 0) - return r; - - l = pread(sfd, buffer, cluster_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int decompress_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t compressed_size, - uint64_t cluster_size, - void *buffer1, - void *buffer2) { - - _cleanup_free_ void *large_buffer = NULL; - z_stream s = {}; - uint64_t sz; - ssize_t l; - int r; - - if (compressed_size > cluster_size) { - /* The usual cluster buffer doesn't suffice, let's - * allocate a larger one, temporarily */ - - large_buffer = malloc(compressed_size); - if (!large_buffer) - return -ENOMEM; - - buffer1 = large_buffer; - } - - l = pread(sfd, buffer1, compressed_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != compressed_size) - return -EIO; - - s.next_in = buffer1; - s.avail_in = compressed_size; - s.next_out = buffer2; - s.avail_out = cluster_size; - - r = inflateInit2(&s, -12); - if (r != Z_OK) - return -EIO; - - r = inflate(&s, Z_FINISH); - sz = (uint8_t*) s.next_out - (uint8_t*) buffer2; - inflateEnd(&s); - if (r != Z_STREAM_END || sz != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer2, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int normalize_offset( - const Header *header, - uint64_t p, - uint64_t *ret, - bool *compressed, - uint64_t *compressed_size) { - - uint64_t q; - - q = be64toh(p); - - if (q & QCOW2_COMPRESSED) { - uint64_t sz, csize_shift, csize_mask; - - if (!compressed) - return -EOPNOTSUPP; - - csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8); - csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1; - sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511); - q &= ((1ULL << csize_shift) - 1); - - if (compressed_size) - *compressed_size = sz; - - *compressed = true; - - } else { - if (compressed) { - *compressed = false; - *compressed_size = 0; - } - - if (q & QCOW2_ZERO) { - /* We make no distinction between zero blocks and holes */ - *ret = 0; - return 0; - } - - q &= ~QCOW2_COPIED; - } - - *ret = q; - return q > 0; /* returns positive if not a hole */ -} - -static int verify_header(const Header *header) { - assert(header); - - if (HEADER_MAGIC(header) != QCOW2_MAGIC) - return -EBADMSG; - - if (HEADER_VERSION(header) != 2 && - HEADER_VERSION(header) != 3) - return -EOPNOTSUPP; - - if (HEADER_CRYPT_METHOD(header) != 0) - return -EOPNOTSUPP; - - if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */ - return -EBADMSG; - - if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */ - return -EBADMSG; - - if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0) - return -EBADMSG; - - if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */ - return -EBADMSG; - - if (HEADER_VERSION(header) == 3) { - - if (header->incompatible_features != 0) - return -EOPNOTSUPP; - - if (HEADER_HEADER_LENGTH(header) < sizeof(Header)) - return -EBADMSG; - } - - return 0; -} - -int qcow2_convert(int qcow2_fd, int raw_fd) { - _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL; - _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL; - uint64_t sz, i; - Header header; - ssize_t l; - int r; - - l = pread(qcow2_fd, &header, sizeof(header), 0); - if (l < 0) - return -errno; - if (l != sizeof(header)) - return -EIO; - - r = verify_header(&header); - if (r < 0) - return r; - - l1_table = new(be64_t, HEADER_L1_SIZE(&header)); - if (!l1_table) - return -ENOMEM; - - l2_table = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!l2_table) - return -ENOMEM; - - buffer1 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer1) - return -ENOMEM; - - buffer2 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer2) - return -ENOMEM; - - /* Empty the file if it exists, we rely on zero bits */ - if (ftruncate(raw_fd, 0) < 0) - return -errno; - - if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0) - return -errno; - - sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header); - l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header)); - if (l < 0) - return -errno; - if ((uint64_t) l != sz) - return -EIO; - - for (i = 0; i < HEADER_L1_SIZE(&header); i ++) { - uint64_t l2_begin, j; - - r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL); - if (r < 0) - return r; - if (r == 0) - continue; - - l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin); - if (l < 0) - return -errno; - if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header)) - return -EIO; - - for (j = 0; j < HEADER_L2_SIZE(&header); j++) { - uint64_t data_begin, p, compressed_size; - bool compressed; - - p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header); - - r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size); - if (r < 0) - return r; - if (r == 0) - continue; - - if (compressed) - r = decompress_cluster( - qcow2_fd, data_begin, - raw_fd, p, - compressed_size, HEADER_CLUSTER_SIZE(&header), - buffer1, buffer2); - else - r = copy_cluster( - qcow2_fd, data_begin, - raw_fd, p, - HEADER_CLUSTER_SIZE(&header), buffer1); - if (r < 0) - return r; - } - } - - return 0; -} - -int qcow2_detect(int fd) { - be32_t id; - ssize_t l; - - l = pread(fd, &id, sizeof(id), 0); - if (l < 0) - return -errno; - if (l != sizeof(id)) - return -EIO; - - return htobe32(QCOW2_MAGIC) == id; -} diff --git a/src/import/qcow2-util.h b/src/import/qcow2-util.h deleted file mode 100644 index 6dddac8cdf..0000000000 --- a/src/import/qcow2-util.h +++ /dev/null @@ -1,23 +0,0 @@ -#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 . -***/ - -int qcow2_detect(int fd); -int qcow2_convert(int qcow2_fd, int raw_fd); diff --git a/src/import/test-qcow2.c b/src/import/test-qcow2.c deleted file mode 100644 index b820253d71..0000000000 --- a/src/import/test-qcow2.c +++ /dev/null @@ -1,53 +0,0 @@ -/*** - 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 . -***/ - -#include "fd-util.h" -#include "log.h" -#include "qcow2-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - _cleanup_close_ int sfd = -1, dfd = -1; - int r; - - if (argc != 3) { - log_error("Needs two arguments."); - return EXIT_FAILURE; - } - - sfd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (sfd < 0) { - log_error_errno(errno, "Can't open source file: %m"); - return EXIT_FAILURE; - } - - dfd = open(argv[2], O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666); - if (dfd < 0) { - log_error_errno(errno, "Can't open destination file: %m"); - return EXIT_FAILURE; - } - - r = qcow2_convert(sfd, dfd); - if (r < 0) { - log_error_errno(r, "Failed to unpack: %m"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/src/initctl/Makefile b/src/initctl/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/initctl/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c deleted file mode 100644 index 41b2237d16..0000000000 --- a/src/initctl/initctl.c +++ /dev/null @@ -1,428 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.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)) - -typedef struct Fifo Fifo; - -typedef struct Server { - int epoll_fd; - - LIST_HEAD(Fifo, fifos); - unsigned n_fifos; - - sd_bus *bus; - - bool quit; -} Server; - -struct Fifo { - Server *server; - - int fd; - - struct init_request buffer; - size_t bytes_read; - - LIST_FIELDS(Fifo, fifo); -}; - -static const char *translate_runlevel(int runlevel, bool *isolate) { - static const struct { - const int runlevel; - const char *special; - bool isolate; - } table[] = { - { '0', SPECIAL_POWEROFF_TARGET, false }, - { '1', SPECIAL_RESCUE_TARGET, true }, - { 's', SPECIAL_RESCUE_TARGET, true }, - { 'S', SPECIAL_RESCUE_TARGET, true }, - { '2', SPECIAL_MULTI_USER_TARGET, true }, - { '3', SPECIAL_MULTI_USER_TARGET, true }, - { '4', SPECIAL_MULTI_USER_TARGET, true }, - { '5', SPECIAL_GRAPHICAL_TARGET, true }, - { '6', SPECIAL_REBOOT_TARGET, false }, - }; - - unsigned i; - - assert(isolate); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (table[i].runlevel == runlevel) { - *isolate = table[i].isolate; - if (runlevel == '6' && kexec_loaded()) - return SPECIAL_KEXEC_TARGET; - return table[i].special; - } - - return NULL; -} - -static void change_runlevel(Server *s, int runlevel) { - const char *target; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *mode; - bool isolate = false; - int r; - - assert(s); - - target = translate_runlevel(runlevel, &isolate); - if (!target) { - log_warning("Got request for unknown runlevel %c, ignoring.", runlevel); - return; - } - - if (isolate) - mode = "isolate"; - else - mode = "replace-irreversibly"; - - log_debug("Running request %s/start/%s", target, mode); - - r = sd_bus_call_method( - s->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - &error, - NULL, - "ss", target, mode); - if (r < 0) { - log_error("Failed to change runlevel: %s", bus_error_message(&error, -r)); - return; - } -} - -static void request_process(Server *s, const struct init_request *req) { - assert(s); - assert(req); - - if (req->magic != INIT_MAGIC) { - log_error("Got initctl request with invalid magic. Ignoring."); - return; - } - - switch (req->cmd) { - - case INIT_CMD_RUNLVL: - if (!isprint(req->runlevel)) - log_error("Got invalid runlevel. Ignoring."); - else - switch (req->runlevel) { - - /* we are async anyway, so just use kill for reexec/reload */ - case 'u': - case 'U': - if (kill(1, SIGTERM) < 0) - log_error_errno(errno, "kill() failed: %m"); - - /* The bus connection will be - * terminated if PID 1 is reexecuted, - * hence let's just exit here, and - * rely on that we'll be restarted on - * the next request */ - s->quit = true; - break; - - case 'q': - case 'Q': - if (kill(1, SIGHUP) < 0) - log_error_errno(errno, "kill() failed: %m"); - break; - - default: - change_runlevel(s, req->runlevel); - } - return; - - case INIT_CMD_POWERFAIL: - case INIT_CMD_POWERFAILNOW: - case INIT_CMD_POWEROK: - log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!"); - return; - - case INIT_CMD_CHANGECONS: - log_warning("Received console change initctl request. This is not implemented in systemd."); - return; - - case INIT_CMD_SETENV: - case INIT_CMD_UNSETENV: - log_warning("Received environment initctl request. This is not implemented in systemd."); - return; - - default: - log_warning("Received unknown initctl request. Ignoring."); - return; - } -} - -static int fifo_process(Fifo *f) { - ssize_t l; - - assert(f); - - errno = EIO; - l = read(f->fd, - ((uint8_t*) &f->buffer) + f->bytes_read, - sizeof(f->buffer) - f->bytes_read); - if (l <= 0) { - if (errno == EAGAIN) - return 0; - - return log_warning_errno(errno, "Failed to read from fifo: %m"); - } - - f->bytes_read += l; - assert(f->bytes_read <= sizeof(f->buffer)); - - if (f->bytes_read == sizeof(f->buffer)) { - request_process(f->server, &f->buffer); - f->bytes_read = 0; - } - - return 0; -} - -static void fifo_free(Fifo *f) { - assert(f); - - if (f->server) { - assert(f->server->n_fifos > 0); - f->server->n_fifos--; - LIST_REMOVE(fifo, f->server->fifos, f); - } - - if (f->fd >= 0) { - if (f->server) - epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL); - - safe_close(f->fd); - } - - free(f); -} - -static void server_done(Server *s) { - assert(s); - - while (s->fifos) - fifo_free(s->fifos); - - safe_close(s->epoll_fd); - - if (s->bus) { - sd_bus_flush(s->bus); - sd_bus_unref(s->bus); - } -} - -static int server_init(Server *s, unsigned n_sockets) { - int r; - unsigned i; - - assert(s); - assert(n_sockets > 0); - - zero(*s); - - s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (s->epoll_fd < 0) { - r = log_error_errno(errno, - "Failed to create epoll object: %m"); - goto fail; - } - - for (i = 0; i < n_sockets; i++) { - struct epoll_event ev; - Fifo *f; - int fd; - - fd = SD_LISTEN_FDS_START+i; - - r = sd_is_fifo(fd, NULL); - if (r < 0) { - log_error_errno(r, "Failed to determine file descriptor type: %m"); - goto fail; - } - - if (!r) { - log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; - } - - f = new0(Fifo, 1); - if (!f) { - r = -ENOMEM; - log_error_errno(errno, "Failed to create fifo object: %m"); - goto fail; - } - - f->fd = -1; - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = f; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - fifo_free(f); - log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); - goto fail; - } - - f->fd = fd; - LIST_PREPEND(fifo, s->fifos, f); - f->server = s; - s->n_fifos++; - } - - r = bus_connect_system_systemd(&s->bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - r = -EIO; - goto fail; - } - - return 0; - -fail: - server_done(s); - - return r; -} - -static int process_event(Server *s, struct epoll_event *ev) { - int r; - Fifo *f; - - assert(s); - - if (!(ev->events & EPOLLIN)) { - log_info("Got invalid event from epoll. (3)"); - return -EIO; - } - - f = (Fifo*) ev->data.ptr; - r = fifo_process(f); - if (r < 0) { - log_info_errno(r, "Got error on fifo: %m"); - fifo_free(f); - return r; - } - - return 0; -} - -int main(int argc, char *argv[]) { - Server server; - int r = EXIT_FAILURE, n; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - n = sd_listen_fds(true); - if (n < 0) { - log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); - return EXIT_FAILURE; - } - - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } - - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; - - log_debug("systemd-initctl running as pid "PID_FMT, getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - while (!server.quit) { - struct epoll_event event; - int k; - - k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC); - if (k < 0) { - if (errno == EINTR) - continue; - log_error_errno(errno, "epoll_wait() failed: %m"); - goto fail; - } - - if (k <= 0) - break; - - if (process_event(&server, &event) < 0) - goto fail; - } - - r = EXIT_SUCCESS; - - log_debug("systemd-initctl stopped as pid "PID_FMT, getpid()); - -fail: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - server_done(&server); - - return r; -} diff --git a/src/journal-remote/.gitignore b/src/journal-remote/.gitignore deleted file mode 100644 index 06847b65d4..0000000000 --- a/src/journal-remote/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/journal-remote.conf -/journal-upload.conf diff --git a/src/journal-remote/Makefile b/src/journal-remote/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/journal-remote/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/journal-remote/browse.html b/src/journal-remote/browse.html deleted file mode 100644 index 32848c7673..0000000000 --- a/src/journal-remote/browse.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - Journal - - - - - - - -

- -
-
-
-
-
-
- -
- -      - Only current boot -
- -
- -
- -
- - - - -      - - -
- -
- g: First Page      - ←, k, BACKSPACE: Previous Page      - →, j, SPACE: Next Page      - G: Last Page      - +: More entries      - -: Fewer entries -
- - - - diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c deleted file mode 100644 index e265027a04..0000000000 --- a/src/journal-remote/journal-gatewayd.c +++ /dev/null @@ -1,1077 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#ifdef HAVE_GNUTLS -#include -#endif -#include -#include -#include -#include - -#include "sd-bus.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 "parse-util.h" -#include "sigbus.h" -#include "util.h" - -#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC) - -static char *arg_key_pem = NULL; -static char *arg_cert_pem = NULL; -static char *arg_trust_pem = NULL; - -typedef struct RequestMeta { - sd_journal *journal; - - OutputMode mode; - - char *cursor; - int64_t n_skip; - uint64_t n_entries; - bool n_entries_set; - - FILE *tmp; - uint64_t delta, size; - - int argument_parse_error; - - bool follow; - bool discrete; - - uint64_t n_fields; - bool n_fields_set; -} RequestMeta; - -static const char* const mime_types[_OUTPUT_MODE_MAX] = { - [OUTPUT_SHORT] = "text/plain", - [OUTPUT_JSON] = "application/json", - [OUTPUT_JSON_SSE] = "text/event-stream", - [OUTPUT_EXPORT] = "application/vnd.fdo.journal", -}; - -static RequestMeta *request_meta(void **connection_cls) { - RequestMeta *m; - - assert(connection_cls); - if (*connection_cls) - return *connection_cls; - - m = new0(RequestMeta, 1); - if (!m) - return NULL; - - *connection_cls = m; - return m; -} - -static void request_meta_free( - void *cls, - struct MHD_Connection *connection, - void **connection_cls, - enum MHD_RequestTerminationCode toe) { - - RequestMeta *m = *connection_cls; - - if (!m) - return; - - sd_journal_close(m->journal); - - safe_fclose(m->tmp); - - free(m->cursor); - free(m); -} - -static int open_journal(RequestMeta *m) { - assert(m); - - if (m->journal) - return 0; - - return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); -} - -static int request_meta_ensure_tmp(RequestMeta *m) { - assert(m); - - if (m->tmp) - rewind(m->tmp); - else { - int fd; - - fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC); - if (fd < 0) - return fd; - - m->tmp = fdopen(fd, "w+"); - if (!m->tmp) { - safe_close(fd); - return -errno; - } - } - - return 0; -} - -static ssize_t request_reader_entries( - void *cls, - uint64_t pos, - char *buf, - size_t max) { - - RequestMeta *m = cls; - int r; - size_t n, k; - - assert(m); - assert(buf); - assert(max > 0); - assert(pos >= m->delta); - - pos -= m->delta; - - while (pos >= m->size) { - off_t sz; - - /* End of this entry, so let's serialize the next - * one */ - - if (m->n_entries_set && - m->n_entries <= 0) - return MHD_CONTENT_READER_END_OF_STREAM; - - if (m->n_skip < 0) - r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip + 1); - else if (m->n_skip > 0) - r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1); - else - r = sd_journal_next(m->journal); - - if (r < 0) { - log_error_errno(r, "Failed to advance journal pointer: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } else if (r == 0) { - - if (m->follow) { - 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; - } - - return MHD_CONTENT_READER_END_OF_STREAM; - } - - if (m->discrete) { - assert(m->cursor); - - r = sd_journal_test_cursor(m->journal, m->cursor); - if (r < 0) { - log_error_errno(r, "Failed to test cursor: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - if (r == 0) - return MHD_CONTENT_READER_END_OF_STREAM; - } - - pos -= m->size; - m->delta += m->size; - - if (m->n_entries_set) - m->n_entries -= 1; - - m->n_skip = 0; - - r = request_meta_ensure_tmp(m); - if (r < 0) { - log_error_errno(r, "Failed to create temporary file: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL); - if (r < 0) { - log_error_errno(r, "Failed to serialize item: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - sz = ftello(m->tmp); - if (sz == (off_t) -1) { - log_error_errno(errno, "Failed to retrieve file position: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - m->size = (uint64_t) sz; - } - - if (fseeko(m->tmp, pos, SEEK_SET) < 0) { - log_error_errno(errno, "Failed to seek to position: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - n = m->size - pos; - if (n < 1) - return 0; - if (n > max) - n = max; - - errno = 0; - k = fread(buf, 1, n, m->tmp); - if (k != n) { - log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - return (ssize_t) k; -} - -static int request_parse_accept( - RequestMeta *m, - struct MHD_Connection *connection) { - - const char *header; - - assert(m); - assert(connection); - - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept"); - if (!header) - return 0; - - if (streq(header, mime_types[OUTPUT_JSON])) - m->mode = OUTPUT_JSON; - else if (streq(header, mime_types[OUTPUT_JSON_SSE])) - m->mode = OUTPUT_JSON_SSE; - else if (streq(header, mime_types[OUTPUT_EXPORT])) - m->mode = OUTPUT_EXPORT; - else - m->mode = OUTPUT_SHORT; - - return 0; -} - -static int request_parse_range( - RequestMeta *m, - struct MHD_Connection *connection) { - - const char *range, *colon, *colon2; - int r; - - assert(m); - assert(connection); - - range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); - if (!range) - return 0; - - if (!startswith(range, "entries=")) - return 0; - - range += 8; - range += strspn(range, WHITESPACE); - - colon = strchr(range, ':'); - if (!colon) - m->cursor = strdup(range); - else { - const char *p; - - colon2 = strchr(colon + 1, ':'); - if (colon2) { - _cleanup_free_ char *t; - - t = strndup(colon + 1, colon2 - colon - 1); - if (!t) - return -ENOMEM; - - r = safe_atoi64(t, &m->n_skip); - if (r < 0) - return r; - } - - p = (colon2 ? colon2 : colon) + 1; - if (*p) { - r = safe_atou64(p, &m->n_entries); - if (r < 0) - return r; - - if (m->n_entries <= 0) - return -EINVAL; - - m->n_entries_set = true; - } - - m->cursor = strndup(range, colon - range); - } - - if (!m->cursor) - return -ENOMEM; - - m->cursor[strcspn(m->cursor, WHITESPACE)] = 0; - if (isempty(m->cursor)) - m->cursor = mfree(m->cursor); - - return 0; -} - -static int request_parse_arguments_iterator( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value) { - - RequestMeta *m = cls; - _cleanup_free_ char *p = NULL; - int r; - - assert(m); - - if (isempty(key)) { - m->argument_parse_error = -EINVAL; - return MHD_NO; - } - - if (streq(key, "follow")) { - if (isempty(value)) { - m->follow = true; - return MHD_YES; - } - - r = parse_boolean(value); - if (r < 0) { - m->argument_parse_error = r; - return MHD_NO; - } - - m->follow = r; - return MHD_YES; - } - - if (streq(key, "discrete")) { - if (isempty(value)) { - m->discrete = true; - return MHD_YES; - } - - r = parse_boolean(value); - if (r < 0) { - m->argument_parse_error = r; - return MHD_NO; - } - - m->discrete = r; - return MHD_YES; - } - - if (streq(key, "boot")) { - if (isempty(value)) - r = true; - else { - r = parse_boolean(value); - if (r < 0) { - m->argument_parse_error = r; - return MHD_NO; - } - } - - if (r) { - char match[9 + 32 + 1] = "_BOOT_ID="; - sd_id128_t bid; - - r = sd_id128_get_boot(&bid); - if (r < 0) { - log_error_errno(r, "Failed to get boot ID: %m"); - return MHD_NO; - } - - sd_id128_to_string(bid, match + 9); - r = sd_journal_add_match(m->journal, match, sizeof(match)-1); - if (r < 0) { - m->argument_parse_error = r; - return MHD_NO; - } - } - - return MHD_YES; - } - - p = strjoin(key, "=", strempty(value), NULL); - if (!p) { - m->argument_parse_error = log_oom(); - return MHD_NO; - } - - r = sd_journal_add_match(m->journal, p, 0); - if (r < 0) { - m->argument_parse_error = r; - return MHD_NO; - } - - return MHD_YES; -} - -static int request_parse_arguments( - RequestMeta *m, - struct MHD_Connection *connection) { - - assert(m); - assert(connection); - - m->argument_parse_error = 0; - MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m); - - return m->argument_parse_error; -} - -static int request_handler_entries( - struct MHD_Connection *connection, - void *connection_cls) { - - struct MHD_Response *response; - RequestMeta *m = connection_cls; - int r; - - assert(connection); - assert(m); - - r = open_journal(m); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); - - if (request_parse_accept(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); - - if (request_parse_range(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n"); - - if (request_parse_arguments(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n"); - - if (m->discrete) { - if (!m->cursor) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n"); - - m->n_entries = 1; - m->n_entries_set = true; - } - - if (m->cursor) - r = sd_journal_seek_cursor(m->journal, m->cursor); - else if (m->n_skip >= 0) - r = sd_journal_seek_head(m->journal); - else if (m->n_skip < 0) - r = sd_journal_seek_tail(m->journal); - if (r < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n"); - - response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); - if (!response) - return respond_oom(connection); - - MHD_add_response_header(response, "Content-Type", mime_types[m->mode]); - - r = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - - return r; -} - -static int output_field(FILE *f, OutputMode m, const char *d, size_t l) { - const char *eq; - size_t j; - - eq = memchr(d, '=', l); - if (!eq) - return -EINVAL; - - j = l - (eq - d + 1); - - if (m == OUTPUT_JSON) { - fprintf(f, "{ \"%.*s\" : ", (int) (eq - d), d); - json_escape(f, eq+1, j, OUTPUT_FULL_WIDTH); - fputs(" }\n", f); - } else { - fwrite(eq+1, 1, j, f); - fputc('\n', f); - } - - return 0; -} - -static ssize_t request_reader_fields( - void *cls, - uint64_t pos, - char *buf, - size_t max) { - - RequestMeta *m = cls; - int r; - size_t n, k; - - assert(m); - assert(buf); - assert(max > 0); - assert(pos >= m->delta); - - pos -= m->delta; - - while (pos >= m->size) { - off_t sz; - const void *d; - size_t l; - - /* End of this field, so let's serialize the next - * one */ - - if (m->n_fields_set && - m->n_fields <= 0) - return MHD_CONTENT_READER_END_OF_STREAM; - - r = sd_journal_enumerate_unique(m->journal, &d, &l); - if (r < 0) { - log_error_errno(r, "Failed to advance field index: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } else if (r == 0) - return MHD_CONTENT_READER_END_OF_STREAM; - - pos -= m->size; - m->delta += m->size; - - if (m->n_fields_set) - m->n_fields -= 1; - - r = request_meta_ensure_tmp(m); - if (r < 0) { - log_error_errno(r, "Failed to create temporary file: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - r = output_field(m->tmp, m->mode, d, l); - if (r < 0) { - log_error_errno(r, "Failed to serialize item: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - sz = ftello(m->tmp); - if (sz == (off_t) -1) { - log_error_errno(errno, "Failed to retrieve file position: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - m->size = (uint64_t) sz; - } - - if (fseeko(m->tmp, pos, SEEK_SET) < 0) { - log_error_errno(errno, "Failed to seek to position: %m"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - n = m->size - pos; - if (n > max) - n = max; - - errno = 0; - k = fread(buf, 1, n, m->tmp); - if (k != n) { - log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF"); - return MHD_CONTENT_READER_END_WITH_ERROR; - } - - return (ssize_t) k; -} - -static int request_handler_fields( - struct MHD_Connection *connection, - const char *field, - void *connection_cls) { - - struct MHD_Response *response; - RequestMeta *m = connection_cls; - int r; - - assert(connection); - assert(m); - - r = open_journal(m); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); - - if (request_parse_accept(m, connection) < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); - - r = sd_journal_query_unique(m->journal, field); - if (r < 0) - return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n"); - - response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL); - if (!response) - return respond_oom(connection); - - MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]); - - r = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - - return r; -} - -static int request_handler_redirect( - struct MHD_Connection *connection, - const char *target) { - - char *page; - struct MHD_Response *response; - int ret; - - assert(connection); - assert(target); - - if (asprintf(&page, "Please continue to the journal browser.", target) < 0) - return respond_oom(connection); - - response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE); - if (!response) { - free(page); - return respond_oom(connection); - } - - MHD_add_response_header(response, "Content-Type", "text/html"); - MHD_add_response_header(response, "Location", target); - - ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); - MHD_destroy_response(response); - - return ret; -} - -static int request_handler_file( - struct MHD_Connection *connection, - const char *path, - const char *mime_type) { - - struct MHD_Response *response; - int ret; - _cleanup_close_ int fd = -1; - struct stat st; - - assert(connection); - assert(path); - assert(mime_type); - - fd = open(path, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return mhd_respondf(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path); - - 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_offset64(st.st_size, fd, 0); - if (!response) - return respond_oom(connection); - - fd = -1; - - MHD_add_response_header(response, "Content-Type", mime_type); - - ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - - return ret; -} - -static int get_virtualization(char **v) { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - char *b = NULL; - int r; - - r = sd_bus_default_system(&bus); - if (r < 0) - return r; - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Virtualization", - NULL, - &b); - if (r < 0) - return r; - - if (isempty(b)) { - free(b); - *v = NULL; - return 0; - } - - *v = b; - return 1; -} - -static int request_handler_machine( - struct MHD_Connection *connection, - void *connection_cls) { - - struct MHD_Response *response; - RequestMeta *m = connection_cls; - int r; - _cleanup_free_ char* hostname = NULL, *os_name = NULL; - uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0; - char *json; - sd_id128_t mid, bid; - _cleanup_free_ char *v = NULL; - - assert(connection); - assert(m); - - r = open_journal(m); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); - - r = sd_id128_get_machine(&mid); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r)); - - r = sd_id128_get_boot(&bid); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r)); - - hostname = gethostname_malloc(); - if (!hostname) - return respond_oom(connection); - - r = sd_journal_get_usage(m->journal, &usage); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); - - r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to); - if (r < 0) - return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); - - if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL) == -ENOENT) - (void) parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); - - get_virtualization(&v); - - r = asprintf(&json, - "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\"," - "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\"," - "\"hostname\" : \"%s\"," - "\"os_pretty_name\" : \"%s\"," - "\"virtualization\" : \"%s\"," - "\"usage\" : \"%"PRIu64"\"," - "\"cutoff_from_realtime\" : \"%"PRIu64"\"," - "\"cutoff_to_realtime\" : \"%"PRIu64"\" }\n", - SD_ID128_FORMAT_VAL(mid), - SD_ID128_FORMAT_VAL(bid), - hostname_cleanup(hostname), - os_name ? os_name : "GNU/Linux", - v ? v : "bare", - usage, - cutoff_from, - cutoff_to); - - if (r < 0) - return respond_oom(connection); - - response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); - if (!response) { - free(json); - return respond_oom(connection); - } - - MHD_add_response_header(response, "Content-Type", "application/json"); - r = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); - - return r; -} - -static int request_handler( - void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **connection_cls) { - int r, code; - - assert(connection); - assert(connection_cls); - assert(url); - assert(method); - - if (!streq(method, "GET")) - return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, - "Unsupported method.\n"); - - - if (!*connection_cls) { - if (!request_meta(connection_cls)) - return respond_oom(connection); - return MHD_YES; - } - - if (arg_trust_pem) { - r = check_permissions(connection, &code, NULL); - if (r < 0) - return code; - } - - if (streq(url, "/")) - return request_handler_redirect(connection, "/browse"); - - if (streq(url, "/entries")) - return request_handler_entries(connection, *connection_cls); - - if (startswith(url, "/fields/")) - return request_handler_fields(connection, url + 8, *connection_cls); - - if (streq(url, "/browse")) - return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html"); - - if (streq(url, "/machine")) - return request_handler_machine(connection, *connection_cls); - - return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.\n"); -} - -static void help(void) { - printf("%s [OPTIONS...] ...\n\n" - "HTTP server for journal events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --cert=CERT.PEM Server certificate in PEM format\n" - " --key=KEY.PEM Server key in PEM format\n" - " --trust=CERT.PEM Certificat authority certificate in PEM format\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_KEY, - ARG_CERT, - ARG_TRUST, - }; - - int r, c; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, - {} - }; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch(c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_KEY: - if (arg_key_pem) { - log_error("Key file specified twice"); - return -EINVAL; - } - r = read_full_file(optarg, &arg_key_pem, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read key file: %m"); - assert(arg_key_pem); - break; - - case ARG_CERT: - if (arg_cert_pem) { - log_error("Certificate file specified twice"); - return -EINVAL; - } - r = read_full_file(optarg, &arg_cert_pem, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read certificate file: %m"); - assert(arg_cert_pem); - break; - - case ARG_TRUST: -#ifdef HAVE_GNUTLS - if (arg_trust_pem) { - log_error("CA certificate file specified twice"); - return -EINVAL; - } - r = read_full_file(optarg, &arg_trust_pem, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read CA certificate file: %m"); - assert(arg_trust_pem); - break; -#else - log_error("Option --trust is not available."); -#endif - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind < argc) { - log_error("This program does not take arguments."); - return -EINVAL; - } - - if (!!arg_key_pem != !!arg_cert_pem) { - log_error("Certificate and key files must be specified together"); - return -EINVAL; - } - - if (arg_trust_pem && !arg_key_pem) { - log_error("CA certificate can only be used with certificate file"); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - struct MHD_Daemon *d = NULL; - int r, n; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r < 0) - return EXIT_FAILURE; - if (r == 0) - return EXIT_SUCCESS; - - sigbus_install(); - - r = setup_gnutls_logger(NULL); - if (r < 0) - return EXIT_FAILURE; - - n = sd_listen_fds(1); - if (n < 0) { - log_error_errno(n, "Failed to determine passed sockets: %m"); - goto finish; - } else if (n > 1) { - log_error("Can't listen on more than one socket."); - goto finish; - } else { - struct MHD_OptionItem opts[] = { - { MHD_OPTION_NOTIFY_COMPLETED, - (intptr_t) request_meta_free, NULL }, - { MHD_OPTION_EXTERNAL_LOGGER, - (intptr_t) microhttpd_logger, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }, - { MHD_OPTION_END, 0, NULL }}; - int opts_pos = 2; - - /* 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) - {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START}; - if (arg_key_pem) { - assert(arg_cert_pem); - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem}; - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem}; - flags |= MHD_USE_SSL; - } - if (arg_trust_pem) { - assert(flags & MHD_USE_SSL); - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem}; - } - - d = MHD_start_daemon(flags, 19531, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_ARRAY, opts, - MHD_OPTION_END); - } - - if (!d) { - log_error("Failed to start daemon!"); - goto finish; - } - - pause(); - - r = EXIT_SUCCESS; - -finish: - if (d) - MHD_stop_daemon(d); - - return r; -} diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c deleted file mode 100644 index 9ba9ee3fc0..0000000000 --- a/src/journal-remote/journal-remote-parse.c +++ /dev/null @@ -1,506 +0,0 @@ -/*** - 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 . -***/ - -#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 - -void source_free(RemoteSource *source) { - if (!source) - return; - - if (source->fd >= 0 && !source->passive_fd) { - log_debug("Closing fd:%d (%s)", source->fd, source->name); - safe_close(source->fd); - } - - free(source->name); - free(source->buf); - iovw_free_contents(&source->iovw); - - log_debug("Writer ref count %i", source->writer->n_ref); - writer_unref(source->writer); - - sd_event_source_unref(source->event); - sd_event_source_unref(source->buffer_event); - - free(source); -} - -/** - * Initialize zero-filled source with given values. On success, takes - * ownerhship of fd and writer, otherwise does not touch them. - */ -RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) { - - RemoteSource *source; - - log_debug("Creating source for %sfd:%d (%s)", - passive_fd ? "passive " : "", fd, name); - - assert(fd >= 0); - - source = new0(RemoteSource, 1); - if (!source) - return NULL; - - source->fd = fd; - source->passive_fd = passive_fd; - source->name = name; - source->writer = writer; - - return source; -} - -static char* realloc_buffer(RemoteSource *source, size_t size) { - char *b, *old = source->buf; - - b = GREEDY_REALLOC(source->buf, source->size, size); - if (!b) - return NULL; - - iovw_rebase(&source->iovw, old, source->buf); - - return b; -} - -static int get_line(RemoteSource *source, char **line, size_t *size) { - ssize_t n; - char *c = NULL; - - assert(source); - assert(source->state == STATE_LINE); - assert(source->offset <= source->filled); - assert(source->filled <= source->size); - assert(source->buf == NULL || source->size > 0); - assert(source->fd >= 0); - - for (;;) { - if (source->buf) { - size_t start = MAX(source->scanned, source->offset); - - c = memchr(source->buf + start, '\n', - source->filled - start); - if (c != NULL) - break; - } - - source->scanned = source->filled; - if (source->scanned >= DATA_SIZE_MAX) { - log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX); - return -E2BIG; - } - - if (source->passive_fd) - /* we have to wait for some data to come to us */ - return -EAGAIN; - - /* We know that source->filled is at most DATA_SIZE_MAX, so if - we reallocate it, we'll increase the size at least a bit. */ - assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX); - if (source->size - source->filled < LINE_CHUNK && - !realloc_buffer(source, MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX))) - return log_oom(); - - assert(source->buf); - assert(source->size - source->filled >= LINE_CHUNK || - source->size == ENTRY_SIZE_MAX); - - n = read(source->fd, - source->buf + source->filled, - source->size - source->filled); - if (n < 0) { - if (errno != EAGAIN) - log_error_errno(errno, "read(%d, ..., %zu): %m", - source->fd, - source->size - source->filled); - return -errno; - } else if (n == 0) - return 0; - - source->filled += n; - } - - *line = source->buf + source->offset; - *size = c + 1 - source->buf - source->offset; - source->offset += *size; - - return 1; -} - -int push_data(RemoteSource *source, const char *data, size_t size) { - assert(source); - assert(source->state != STATE_EOF); - - if (!realloc_buffer(source, source->filled + size)) { - log_error("Failed to store received data of size %zu " - "(in addition to existing %zu bytes with %zu filled): %s", - size, source->size, source->filled, strerror(ENOMEM)); - return -ENOMEM; - } - - memcpy(source->buf + source->filled, data, size); - source->filled += size; - - return 0; -} - -static int fill_fixed_size(RemoteSource *source, void **data, size_t size) { - - assert(source); - assert(source->state == STATE_DATA_START || - source->state == STATE_DATA || - source->state == STATE_DATA_FINISH); - assert(size <= DATA_SIZE_MAX); - assert(source->offset <= source->filled); - assert(source->filled <= source->size); - assert(source->buf != NULL || source->size == 0); - assert(source->buf == NULL || source->size > 0); - assert(source->fd >= 0); - assert(data); - - while (source->filled - source->offset < size) { - int n; - - if (source->passive_fd) - /* we have to wait for some data to come to us */ - return -EAGAIN; - - if (!realloc_buffer(source, source->offset + size)) - return log_oom(); - - n = read(source->fd, source->buf + source->filled, - source->size - source->filled); - if (n < 0) { - if (errno != EAGAIN) - log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd, - source->size - source->filled); - return -errno; - } else if (n == 0) - return 0; - - source->filled += n; - } - - *data = source->buf + source->offset; - source->offset += size; - - return 1; -} - -static int get_data_size(RemoteSource *source) { - int r; - void *data; - - assert(source); - assert(source->state == STATE_DATA_START); - assert(source->data_size == 0); - - r = fill_fixed_size(source, &data, sizeof(uint64_t)); - if (r <= 0) - return r; - - source->data_size = le64toh( *(uint64_t *) data ); - if (source->data_size > DATA_SIZE_MAX) { - log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u", - source->data_size, DATA_SIZE_MAX); - return -EINVAL; - } - if (source->data_size == 0) - log_warning("Binary field with zero length"); - - return 1; -} - -static int get_data_data(RemoteSource *source, void **data) { - int r; - - assert(source); - assert(data); - assert(source->state == STATE_DATA); - - r = fill_fixed_size(source, data, source->data_size); - if (r <= 0) - return r; - - return 1; -} - -static int get_data_newline(RemoteSource *source) { - int r; - char *data; - - assert(source); - assert(source->state == STATE_DATA_FINISH); - - r = fill_fixed_size(source, (void**) &data, 1); - if (r <= 0) - return r; - - assert(data); - if (*data != '\n') { - log_error("expected newline, got '%c'", *data); - return -EINVAL; - } - - return 1; -} - -static int process_dunder(RemoteSource *source, char *line, size_t n) { - const char *timestamp; - int r; - - assert(line); - assert(n > 0); - assert(line[n-1] == '\n'); - - /* XXX: is it worth to support timestamps in extended format? - * We don't produce them, but who knows... */ - - timestamp = startswith(line, "__CURSOR="); - if (timestamp) - /* ignore __CURSOR */ - return 1; - - timestamp = startswith(line, "__REALTIME_TIMESTAMP="); - if (timestamp) { - long long unsigned x; - line[n-1] = '\0'; - r = safe_atollu(timestamp, &x); - if (r < 0) - log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp); - else - source->ts.realtime = x; - return r < 0 ? r : 1; - } - - timestamp = startswith(line, "__MONOTONIC_TIMESTAMP="); - if (timestamp) { - long long unsigned x; - line[n-1] = '\0'; - r = safe_atollu(timestamp, &x); - if (r < 0) - log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp); - else - source->ts.monotonic = x; - return r < 0 ? r : 1; - } - - timestamp = startswith(line, "__"); - if (timestamp) { - log_notice("Unknown dunder line %s", line); - return 1; - } - - /* no dunder */ - return 0; -} - -static int process_data(RemoteSource *source) { - int r; - - switch(source->state) { - case STATE_LINE: { - char *line, *sep; - size_t n = 0; - - assert(source->data_size == 0); - - r = get_line(source, &line, &n); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return r; - } - assert(n > 0); - assert(line[n-1] == '\n'); - - if (n == 1) { - log_trace("Received empty line, event is ready"); - return 1; - } - - r = process_dunder(source, line, n); - if (r != 0) - return r < 0 ? r : 0; - - /* MESSAGE=xxx\n - or - COREDUMP\n - LLLLLLLL0011223344...\n - */ - sep = memchr(line, '=', n); - if (sep) { - /* chomp newline */ - n--; - - r = iovw_put(&source->iovw, line, n); - if (r < 0) - return r; - } else { - /* replace \n with = */ - line[n-1] = '='; - - source->field_len = n; - source->state = STATE_DATA_START; - - /* we cannot put the field in iovec until we have all data */ - } - - log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary"); - - return 0; /* continue */ - } - - case STATE_DATA_START: - assert(source->data_size == 0); - - r = get_data_size(source); - // log_debug("get_data_size() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - source->state = source->data_size > 0 ? - STATE_DATA : STATE_DATA_FINISH; - - return 0; /* continue */ - - case STATE_DATA: { - void *data; - char *field; - - assert(source->data_size > 0); - - r = get_data_data(source, &data); - // log_debug("get_data_data() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - assert(data); - - field = (char*) data - sizeof(uint64_t) - source->field_len; - memmove(field + sizeof(uint64_t), field, source->field_len); - - r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size); - if (r < 0) - return r; - - source->state = STATE_DATA_FINISH; - - return 0; /* continue */ - } - - case STATE_DATA_FINISH: - r = get_data_newline(source); - // log_debug("get_data_newline() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - source->data_size = 0; - source->state = STATE_LINE; - - return 0; /* continue */ - default: - assert_not_reached("wtf?"); - } -} - -int process_source(RemoteSource *source, bool compress, bool seal) { - size_t remain, target; - int r; - - assert(source); - assert(source->writer); - - r = process_data(source); - if (r <= 0) - return r; - - /* We have a full event */ - log_trace("Received full event from source@%p fd:%d (%s)", - source, source->fd, source->name); - - if (!source->iovw.count) { - log_warning("Entry with no payload, skipping"); - goto freeing; - } - - assert(source->iovw.iovec); - assert(source->iovw.count); - - r = writer_write(source->writer, &source->iovw, &source->ts, compress, seal); - if (r < 0) - log_error_errno(r, "Failed to write entry of %zu bytes: %m", - iovw_size(&source->iovw)); - else - r = 1; - - freeing: - iovw_free_contents(&source->iovw); - - /* possibly reset buffer position */ - remain = source->filled - source->offset; - - if (remain == 0) /* no brainer */ - source->offset = source->scanned = source->filled = 0; - else if (source->offset > source->size - source->filled && - source->offset > remain) { - memcpy(source->buf, source->buf + source->offset, remain); - source->offset = source->scanned = 0; - source->filled = remain; - } - - target = source->size; - while (target > 16 * LINE_CHUNK && source->filled < target / 2) - target /= 2; - if (target < source->size) { - char *tmp; - - tmp = realloc(source->buf, target); - if (!tmp) - log_warning("Failed to reallocate buffer to (smaller) size %zu", - target); - else { - log_debug("Reallocated buffer from %zu to %zu bytes", - source->size, target); - source->buf = tmp; - source->size = target; - } - } - - return r; -} diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h deleted file mode 100644 index 1740a21f92..0000000000 --- a/src/journal-remote/journal-remote-parse.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-event.h" - -#include "journal-remote-write.h" - -typedef enum { - STATE_LINE = 0, /* waiting to read, or reading line */ - STATE_DATA_START, /* reading binary data header */ - STATE_DATA, /* reading binary data */ - STATE_DATA_FINISH, /* expecting newline */ - STATE_EOF, /* done */ -} source_state; - -typedef struct RemoteSource { - char *name; - int fd; - bool passive_fd; - - char *buf; - size_t size; /* total size of the buffer */ - size_t offset; /* offset to the beginning of live data in the buffer */ - size_t scanned; /* number of bytes since the beginning of data without a newline */ - size_t filled; /* total number of bytes in the buffer */ - - size_t field_len; /* used for binary fields: the field name length */ - size_t data_size; /* and the size of the binary data chunk being processed */ - - struct iovec_wrapper iovw; - - source_state state; - dual_timestamp ts; - - Writer *writer; - - sd_event_source *event; - sd_event_source *buffer_event; -} RemoteSource; - -RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer); - -static inline size_t source_non_empty(RemoteSource *source) { - assert(source); - - return source->filled; -} - -void source_free(RemoteSource *source); -int push_data(RemoteSource *source, const char *data, size_t size); -int process_source(RemoteSource *source, bool compress, bool seal); diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c deleted file mode 100644 index 7bba52566e..0000000000 --- a/src/journal-remote/journal-remote-write.c +++ /dev/null @@ -1,168 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "journal-remote.h" - -int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) { - if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) - return log_oom(); - - iovw->iovec[iovw->count++] = (struct iovec) {data, len}; - return 0; -} - -void iovw_free_contents(struct iovec_wrapper *iovw) { - iovw->iovec = mfree(iovw->iovec); - iovw->size_bytes = iovw->count = 0; -} - -size_t iovw_size(struct iovec_wrapper *iovw) { - size_t n = 0, i; - - for (i = 0; i < iovw->count; i++) - n += iovw->iovec[i].iov_len; - - return n; -} - -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { - size_t i; - - for (i = 0; i < iovw->count; i++) - iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new; -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int do_rotate(JournalFile **f, bool compress, bool seal) { - int r = journal_file_rotate(f, compress, seal, NULL); - if (r < 0) { - if (*f) - log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); - else - log_error_errno(r, "Failed to create rotated journal: %m"); - } - - return r; -} - -Writer* writer_new(RemoteServer *server) { - Writer *w; - - w = new0(Writer, 1); - if (!w) - return NULL; - - memset(&w->metrics, 0xFF, sizeof(w->metrics)); - - w->mmap = mmap_cache_new(); - if (!w->mmap) { - free(w); - return NULL; - } - - w->n_ref = 1; - w->server = server; - - return w; -} - -Writer* writer_free(Writer *w) { - if (!w) - return NULL; - - if (w->journal) { - log_debug("Closing journal file %s.", w->journal->path); - journal_file_close(w->journal); - } - - if (w->server && w->hashmap_key) - hashmap_remove(w->server->writers, w->hashmap_key); - - free(w->hashmap_key); - - if (w->mmap) - mmap_cache_unref(w->mmap); - - free(w); - - return NULL; -} - -Writer* writer_unref(Writer *w) { - if (w && (-- w->n_ref <= 0)) - writer_free(w); - - return NULL; -} - -Writer* writer_ref(Writer *w) { - if (w) - assert_se(++ w->n_ref >= 2); - - return w; -} - -int writer_write(Writer *w, - struct iovec_wrapper *iovw, - dual_timestamp *ts, - bool compress, - bool seal) { - int r; - - assert(w); - assert(iovw); - assert(iovw->count > 0); - - if (journal_file_rotate_suggested(w->journal, 0)) { - log_info("%s: Journal header limits reached or header out-of-date, rotating", - w->journal->path); - r = do_rotate(&w->journal, compress, seal); - if (r < 0) - return r; - } - - r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count, - &w->seqnum, NULL, NULL); - if (r >= 0) { - if (w->server) - w->server->event_count += 1; - return 1; - } - - log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->path); - r = do_rotate(&w->journal, compress, seal); - if (r < 0) - return r; - else - log_debug("%s: Successfully rotated journal", w->journal->path); - - log_debug("Retrying write."); - r = journal_file_append_entry(w->journal, ts, iovw->iovec, iovw->count, - &w->seqnum, NULL, NULL); - if (r < 0) - return r; - - if (w->server) - w->server->event_count += 1; - return 1; -} diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h deleted file mode 100644 index 53ba45fc04..0000000000 --- a/src/journal-remote/journal-remote-write.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "journal-file.h" - -typedef struct RemoteServer RemoteServer; - -struct iovec_wrapper { - struct iovec *iovec; - size_t size_bytes; - size_t count; -}; - -int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len); -void iovw_free_contents(struct iovec_wrapper *iovw); -size_t iovw_size(struct iovec_wrapper *iovw); -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); - -typedef struct Writer { - JournalFile *journal; - JournalMetrics metrics; - - MMapCache *mmap; - RemoteServer *server; - char *hashmap_key; - - uint64_t seqnum; - - int n_ref; -} Writer; - -Writer* writer_new(RemoteServer* server); -Writer* writer_free(Writer *w); - -Writer* writer_ref(Writer *w); -Writer* writer_unref(Writer *w); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Writer*, writer_unref); -#define _cleanup_writer_unref_ _cleanup_(writer_unrefp) - -int writer_write(Writer *s, - struct iovec_wrapper *iovw, - dual_timestamp *ts, - bool compress, - bool seal); - -typedef enum JournalWriteSplitMode { - JOURNAL_WRITE_SPLIT_NONE, - JOURNAL_WRITE_SPLIT_HOST, - _JOURNAL_WRITE_SPLIT_MAX, - _JOURNAL_WRITE_SPLIT_INVALID = -1 -} JournalWriteSplitMode; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c deleted file mode 100644 index 35a1e55f9e..0000000000 --- a/src/journal-remote/journal-remote.c +++ /dev/null @@ -1,1601 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_GNUTLS -#include -#endif - -#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" - -#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem" -#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem" -#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem" - -static char* arg_url = NULL; -static char* arg_getter = NULL; -static char* arg_listen_raw = NULL; -static char* arg_listen_http = NULL; -static char* arg_listen_https = NULL; -static char** arg_files = NULL; -static int arg_compress = true; -static int arg_seal = false; -static int http_socket = -1, https_socket = -1; -static char** arg_gnutls_log = NULL; - -static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST; -static char* arg_output = NULL; - -static char *arg_key = NULL; -static char *arg_cert = NULL; -static char *arg_trust = NULL; -static bool arg_trust_all = false; - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int spawn_child(const char* child, char** argv) { - int fd[2]; - pid_t parent_pid, child_pid; - int r; - - if (pipe(fd) < 0) - return log_error_errno(errno, "Failed to create pager pipe: %m"); - - parent_pid = getpid(); - - child_pid = fork(); - if (child_pid < 0) { - r = log_error_errno(errno, "Failed to fork: %m"); - safe_close_pair(fd); - return r; - } - - /* In the child */ - if (child_pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - r = dup2(fd[1], STDOUT_FILENO); - if (r < 0) { - log_error_errno(errno, "Failed to dup pipe to stdout: %m"); - _exit(EXIT_FAILURE); - } - - safe_close_pair(fd); - - /* Make sure the child goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - execvp(child, argv); - log_error_errno(errno, "Failed to exec child %s: %m", child); - _exit(EXIT_FAILURE); - } - - r = close(fd[1]); - if (r < 0) - log_warning_errno(errno, "Failed to close write end of pipe: %m"); - - return fd[0]; -} - -static int spawn_curl(const char* url) { - char **argv = STRV_MAKE("curl", - "-HAccept: application/vnd.fdo.journal", - "--silent", - "--show-error", - url); - int r; - - r = spawn_child("curl", argv); - if (r < 0) - log_error_errno(r, "Failed to spawn curl: %m"); - return r; -} - -static int spawn_getter(const char *getter) { - int r; - _cleanup_strv_free_ char **words = NULL; - - assert(getter); - r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to split getter option: %m"); - - r = spawn_child(words[0], words); - if (r < 0) - log_error_errno(r, "Failed to spawn getter %s: %m", getter); - - return r; -} - -#define filename_escape(s) xescape((s), "/ ") - -static int open_output(Writer *w, const char* host) { - _cleanup_free_ char *_output = NULL; - const char *output; - int r; - - switch (arg_split_mode) { - case JOURNAL_WRITE_SPLIT_NONE: - output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal"; - break; - - case JOURNAL_WRITE_SPLIT_HOST: { - _cleanup_free_ char *name; - - assert(host); - - name = filename_escape(host); - if (!name) - return log_oom(); - - r = asprintf(&_output, "%s/remote-%s.journal", - arg_output ?: REMOTE_JOURNAL_PATH, - name); - if (r < 0) - return log_oom(); - - output = _output; - break; - } - - default: - assert_not_reached("what?"); - } - - r = journal_file_open_reliably(output, - O_RDWR|O_CREAT, 0640, - arg_compress, arg_seal, - &w->metrics, - w->mmap, NULL, - NULL, &w->journal); - if (r < 0) - log_error_errno(r, "Failed to open output journal %s: %m", - output); - else - log_debug("Opened output file %s", w->journal->path); - return r; -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int init_writer_hashmap(RemoteServer *s) { - static const struct hash_ops *hash_ops[] = { - [JOURNAL_WRITE_SPLIT_NONE] = NULL, - [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops, - }; - - assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops)); - - s->writers = hashmap_new(hash_ops[arg_split_mode]); - if (!s->writers) - return log_oom(); - - return 0; -} - -static int get_writer(RemoteServer *s, const char *host, - Writer **writer) { - const void *key; - _cleanup_writer_unref_ Writer *w = NULL; - int r; - - switch(arg_split_mode) { - case JOURNAL_WRITE_SPLIT_NONE: - key = "one and only"; - break; - - case JOURNAL_WRITE_SPLIT_HOST: - assert(host); - key = host; - break; - - default: - assert_not_reached("what split mode?"); - } - - w = hashmap_get(s->writers, key); - if (w) - writer_ref(w); - else { - w = writer_new(s); - if (!w) - return log_oom(); - - if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) { - w->hashmap_key = strdup(key); - if (!w->hashmap_key) - return log_oom(); - } - - r = open_output(w, host); - if (r < 0) - return r; - - r = hashmap_put(s->writers, w->hashmap_key ?: key, w); - if (r < 0) - return r; - } - - *writer = w; - w = NULL; - return 0; -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -/* This should go away as soon as µhttpd allows state to be passed around. */ -static RemoteServer *server; - -static int dispatch_raw_source_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata); -static int dispatch_raw_source_until_block(sd_event_source *event, - void *userdata); -static int dispatch_blocking_source_event(sd_event_source *event, - void *userdata); -static int dispatch_raw_connection_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata); -static int dispatch_http_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata); - -static int get_source_for_fd(RemoteServer *s, - int fd, char *name, RemoteSource **source) { - Writer *writer; - int r; - - /* This takes ownership of name, but only on success. */ - - assert(fd >= 0); - assert(source); - - if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1)) - return log_oom(); - - r = get_writer(s, name, &writer); - if (r < 0) - return log_warning_errno(r, "Failed to get writer for source %s: %m", - name); - - if (s->sources[fd] == NULL) { - s->sources[fd] = source_new(fd, false, name, writer); - if (!s->sources[fd]) { - writer_unref(writer); - return log_oom(); - } - - s->active++; - } - - *source = s->sources[fd]; - return 0; -} - -static int remove_source(RemoteServer *s, int fd) { - RemoteSource *source; - - assert(s); - assert(fd >= 0 && fd < (ssize_t) s->sources_size); - - source = s->sources[fd]; - if (source) { - /* this closes fd too */ - source_free(source); - s->sources[fd] = NULL; - s->active--; - } - - return 0; -} - -static int add_source(RemoteServer *s, int fd, char* name, bool own_name) { - - RemoteSource *source = NULL; - int r; - - /* This takes ownership of name, even on failure, if own_name is true. */ - - assert(s); - assert(fd >= 0); - assert(name); - - if (!own_name) { - name = strdup(name); - if (!name) - return log_oom(); - } - - r = get_source_for_fd(s, fd, name, &source); - if (r < 0) { - log_error_errno(r, "Failed to create source for fd:%d (%s): %m", - fd, name); - free(name); - return r; - } - - r = sd_event_add_io(s->events, &source->event, - fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI, - dispatch_raw_source_event, source); - if (r == 0) { - /* Add additional source for buffer processing. It will be - * enabled later. */ - r = sd_event_add_defer(s->events, &source->buffer_event, - dispatch_raw_source_until_block, source); - if (r == 0) - sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF); - } else if (r == -EPERM) { - log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name); - r = sd_event_add_defer(s->events, &source->event, - dispatch_blocking_source_event, source); - if (r == 0) - sd_event_source_set_enabled(source->event, SD_EVENT_ON); - } - if (r < 0) { - log_error_errno(r, "Failed to register event source for fd:%d: %m", - fd); - goto error; - } - - r = sd_event_source_set_description(source->event, name); - if (r < 0) { - log_error_errno(r, "Failed to set source name for fd:%d: %m", fd); - goto error; - } - - return 1; /* work to do */ - - error: - remove_source(s, fd); - return r; -} - -static int add_raw_socket(RemoteServer *s, int fd) { - int r; - _cleanup_close_ int fd_ = fd; - char name[sizeof("raw-socket-")-1 + DECIMAL_STR_MAX(int) + 1]; - - assert(fd >= 0); - - r = sd_event_add_io(s->events, &s->listen_event, - fd, EPOLLIN, - dispatch_raw_connection_event, s); - if (r < 0) - return r; - - xsprintf(name, "raw-socket-%d", fd); - - r = sd_event_source_set_description(s->listen_event, name); - if (r < 0) - return r; - - fd_ = -1; - s->active++; - return 0; -} - -static int setup_raw_socket(RemoteServer *s, const char *address) { - int fd; - - fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC); - if (fd < 0) - return fd; - - return add_raw_socket(s, fd); -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int request_meta(void **connection_cls, int fd, char *hostname) { - RemoteSource *source; - Writer *writer; - int r; - - assert(connection_cls); - if (*connection_cls) - return 0; - - r = get_writer(server, hostname, &writer); - if (r < 0) - return log_warning_errno(r, "Failed to get writer for source %s: %m", - hostname); - - source = source_new(fd, true, hostname, writer); - if (!source) { - writer_unref(writer); - return log_oom(); - } - - log_debug("Added RemoteSource as connection metadata %p", source); - - *connection_cls = source; - return 0; -} - -static void request_meta_free(void *cls, - struct MHD_Connection *connection, - void **connection_cls, - enum MHD_RequestTerminationCode toe) { - RemoteSource *s; - - assert(connection_cls); - s = *connection_cls; - - if (s) { - log_debug("Cleaning up connection metadata %p", s); - source_free(s); - *connection_cls = NULL; - } -} - -static int process_http_upload( - struct MHD_Connection *connection, - const char *upload_data, - size_t *upload_data_size, - RemoteSource *source) { - - bool finished = false; - size_t remaining; - int r; - - assert(source); - - log_trace("%s: connection %p, %zu bytes", - __func__, connection, *upload_data_size); - - if (*upload_data_size) { - log_trace("Received %zu bytes", *upload_data_size); - - r = push_data(source, upload_data, *upload_data_size); - if (r < 0) - return mhd_respond_oom(connection); - - *upload_data_size = 0; - } else - finished = true; - - for (;;) { - r = process_source(source, arg_compress, arg_seal); - if (r == -EAGAIN) - break; - else if (r < 0) { - log_warning("Failed to process data for connection %p", connection); - if (r == -E2BIG) - return mhd_respondf(connection, - MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, - "Entry is too large, maximum is %u bytes.\n", - DATA_SIZE_MAX); - else - return mhd_respondf(connection, - MHD_HTTP_UNPROCESSABLE_ENTITY, - "Processing failed: %s.", strerror(-r)); - } - } - - if (!finished) - return MHD_YES; - - /* The upload is finished */ - - remaining = source_non_empty(source); - if (remaining > 0) { - log_warning("Premature EOFbyte. %zu bytes lost.", remaining); - return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED, - "Premature EOF. %zu bytes of trailing data not processed.", - remaining); - } - - return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n"); -}; - -static int request_handler( - void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **connection_cls) { - - const char *header; - int r, code, fd; - _cleanup_free_ char *hostname = NULL; - - assert(connection); - assert(connection_cls); - assert(url); - assert(method); - - log_trace("Handling a connection %s %s %s", method, url, version); - - if (*connection_cls) - return process_http_upload(connection, - upload_data, upload_data_size, - *connection_cls); - - if (!streq(method, "POST")) - return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, - "Unsupported method.\n"); - - if (!streq(url, "/upload")) - return mhd_respond(connection, MHD_HTTP_NOT_FOUND, - "Not found.\n"); - - header = MHD_lookup_connection_value(connection, - MHD_HEADER_KIND, "Content-Type"); - if (!header || !streq(header, "application/vnd.fdo.journal")) - return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, - "Content-Type: application/vnd.fdo.journal" - " is required.\n"); - - { - const union MHD_ConnectionInfo *ci; - - ci = MHD_get_connection_info(connection, - MHD_CONNECTION_INFO_CONNECTION_FD); - if (!ci) { - log_error("MHD_get_connection_info failed: cannot get remote fd"); - return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - "Cannot check remote address"); - } - - fd = ci->connect_fd; - assert(fd >= 0); - } - - if (server->check_trust) { - r = check_permissions(connection, &code, &hostname); - if (r < 0) - return code; - } else { - r = getpeername_pretty(fd, false, &hostname); - if (r < 0) - return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - "Cannot check remote hostname"); - } - - assert(hostname); - - r = request_meta(connection_cls, fd, hostname); - if (r == -ENOMEM) - return respond_oom(connection); - else if (r < 0) - return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - strerror(-r)); - - hostname = NULL; - return MHD_YES; -} - -static int setup_microhttpd_server(RemoteServer *s, - int fd, - const char *key, - const char *cert, - const char *trust) { - struct MHD_OptionItem opts[] = { - { 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, 128*1024}, - { MHD_OPTION_END}, - { MHD_OPTION_END}, - { MHD_OPTION_END}, - { MHD_OPTION_END}}; - int opts_pos = 4; - int flags = - MHD_USE_DEBUG | - MHD_USE_DUAL_STACK | - MHD_USE_EPOLL_LINUX_ONLY | - MHD_USE_PEDANTIC_CHECKS | - MHD_USE_PIPE_FOR_SHUTDOWN; - - const union MHD_DaemonInfo *info; - int r, epoll_fd; - MHDDaemonWrapper *d; - - assert(fd >= 0); - - r = fd_nonblock(fd, true); - if (r < 0) - return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd); - - if (key) { - assert(cert); - - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key}; - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert}; - - flags |= MHD_USE_SSL; - - if (trust) - opts[opts_pos++] = (struct MHD_OptionItem) - {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust}; - } - - d = new(MHDDaemonWrapper, 1); - if (!d) - return log_oom(); - - d->fd = (uint64_t) fd; - - d->daemon = MHD_start_daemon(flags, 0, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_ARRAY, opts, - MHD_OPTION_END); - if (!d->daemon) { - log_error("Failed to start µhttp daemon"); - r = -EINVAL; - goto error; - } - - log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)", - key ? "HTTPS" : "HTTP", fd, d); - - - info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); - if (!info) { - log_error("µhttp returned NULL daemon info"); - r = -EOPNOTSUPP; - goto error; - } - - epoll_fd = info->listen_fd; - if (epoll_fd < 0) { - log_error("µhttp epoll fd is invalid"); - r = -EUCLEAN; - goto error; - } - - r = sd_event_add_io(s->events, &d->event, - epoll_fd, EPOLLIN, - dispatch_http_event, d); - if (r < 0) { - log_error_errno(r, "Failed to add event callback: %m"); - goto error; - } - - r = sd_event_source_set_description(d->event, "epoll-fd"); - if (r < 0) { - log_error_errno(r, "Failed to set source name: %m"); - goto error; - } - - r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops); - if (r < 0) { - log_oom(); - goto error; - } - - r = hashmap_put(s->daemons, &d->fd, d); - if (r < 0) { - log_error_errno(r, "Failed to add daemon to hashmap: %m"); - goto error; - } - - s->active++; - return 0; - -error: - MHD_stop_daemon(d->daemon); - free(d->daemon); - free(d); - return r; -} - -static int setup_microhttpd_socket(RemoteServer *s, - const char *address, - const char *key, - const char *cert, - const char *trust) { - int fd; - - fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC); - if (fd < 0) - return fd; - - return setup_microhttpd_server(s, fd, key, cert, trust); -} - -static int dispatch_http_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata) { - MHDDaemonWrapper *d = userdata; - int r; - - assert(d); - - r = MHD_run(d->daemon); - if (r == MHD_NO) { - log_error("MHD_run failed!"); - // XXX: unregister daemon - return -EINVAL; - } - - return 1; /* work to do */ -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int setup_signals(RemoteServer *s) { - int r; - - assert(s); - - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0); - - r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s); - if (r < 0) - return r; - - r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s); - if (r < 0) - return r; - - return 0; -} - -static int negative_fd(const char *spec) { - /* Return a non-positive number as its inverse, -EINVAL otherwise. */ - - int fd, r; - - r = safe_atoi(spec, &fd); - if (r < 0) - return r; - - if (fd > 0) - return -EINVAL; - else - return -fd; -} - -static int remoteserver_init(RemoteServer *s, - const char* key, - const char* cert, - const char* trust) { - int r, n, fd; - char **file; - - assert(s); - - if ((arg_listen_raw || arg_listen_http) && trust) { - log_error("Option --trust makes all non-HTTPS connections untrusted."); - return -EINVAL; - } - - r = sd_event_default(&s->events); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - setup_signals(s); - - assert(server == NULL); - server = s; - - r = init_writer_hashmap(s); - if (r < 0) - return r; - - n = sd_listen_fds(true); - if (n < 0) - return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); - else - log_debug("Received %d descriptors", n); - - if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) { - log_error("Received fewer sockets than expected"); - return -EBADFD; - } - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - if (sd_is_socket(fd, AF_UNSPEC, 0, true)) { - log_debug("Received a listening socket (fd:%d)", fd); - - if (fd == http_socket) - r = setup_microhttpd_server(s, fd, NULL, NULL, NULL); - else if (fd == https_socket) - r = setup_microhttpd_server(s, fd, key, cert, trust); - else - r = add_raw_socket(s, fd); - } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) { - char *hostname; - - r = getpeername_pretty(fd, false, &hostname); - if (r < 0) - return log_error_errno(r, "Failed to retrieve remote name: %m"); - - log_debug("Received a connection socket (fd:%d) from %s", fd, hostname); - - r = add_source(s, fd, hostname, true); - } else { - log_error("Unknown socket passed on fd:%d", fd); - - return -EINVAL; - } - - if (r < 0) - return log_error_errno(r, "Failed to register socket (fd:%d): %m", - fd); - } - - if (arg_getter) { - log_info("Spawning getter %s...", arg_getter); - fd = spawn_getter(arg_getter); - if (fd < 0) - return fd; - - r = add_source(s, fd, (char*) arg_output, false); - if (r < 0) - return r; - } - - if (arg_url) { - const char *url; - char *hostname, *p; - - if (!strstr(arg_url, "/entries")) { - if (endswith(arg_url, "/")) - url = strjoina(arg_url, "entries"); - else - url = strjoina(arg_url, "/entries"); - } - else - url = strdupa(arg_url); - - log_info("Spawning curl %s...", url); - fd = spawn_curl(url); - if (fd < 0) - return fd; - - hostname = - startswith(arg_url, "https://") ?: - startswith(arg_url, "http://") ?: - arg_url; - - hostname = strdupa(hostname); - if ((p = strchr(hostname, '/'))) - *p = '\0'; - if ((p = strchr(hostname, ':'))) - *p = '\0'; - - r = add_source(s, fd, hostname, false); - if (r < 0) - return r; - } - - if (arg_listen_raw) { - log_debug("Listening on a socket..."); - r = setup_raw_socket(s, arg_listen_raw); - if (r < 0) - return r; - } - - if (arg_listen_http) { - r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL); - if (r < 0) - return r; - } - - if (arg_listen_https) { - r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust); - if (r < 0) - return r; - } - - STRV_FOREACH(file, arg_files) { - const char *output_name; - - if (streq(*file, "-")) { - log_debug("Using standard input as source."); - - fd = STDIN_FILENO; - output_name = "stdin"; - } else { - log_debug("Reading file %s...", *file); - - fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", *file); - output_name = *file; - } - - r = add_source(s, fd, (char*) output_name, false); - if (r < 0) - return r; - } - - if (s->active == 0) { - log_error("Zero sources specified"); - return -EINVAL; - } - - if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) { - /* In this case we know what the writer will be - called, so we can create it and verify that we can - create output as expected. */ - r = get_writer(s, NULL, &s->_single_writer); - if (r < 0) - return r; - } - - return 0; -} - -static void server_destroy(RemoteServer *s) { - size_t i; - MHDDaemonWrapper *d; - - while ((d = hashmap_steal_first(s->daemons))) { - MHD_stop_daemon(d->daemon); - sd_event_source_unref(d->event); - free(d); - } - - hashmap_free(s->daemons); - - assert(s->sources_size == 0 || s->sources); - for (i = 0; i < s->sources_size; i++) - remove_source(s, i); - free(s->sources); - - writer_unref(s->_single_writer); - hashmap_free(s->writers); - - sd_event_source_unref(s->sigterm_event); - sd_event_source_unref(s->sigint_event); - sd_event_source_unref(s->listen_event); - sd_event_unref(s->events); - - /* fds that we're listening on remain open... */ -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static int handle_raw_source(sd_event_source *event, - int fd, - uint32_t revents, - RemoteServer *s) { - - RemoteSource *source; - int r; - - /* Returns 1 if there might be more data pending, - * 0 if data is currently exhausted, negative on error. - */ - - assert(fd >= 0 && fd < (ssize_t) s->sources_size); - source = s->sources[fd]; - assert(source->fd == fd); - - r = process_source(source, arg_compress, arg_seal); - if (source->state == STATE_EOF) { - size_t remaining; - - log_debug("EOF reached with source fd:%d (%s)", - source->fd, source->name); - - remaining = source_non_empty(source); - if (remaining > 0) - log_notice("Premature EOF. %zu bytes lost.", remaining); - remove_source(s, source->fd); - log_debug("%zu active sources remaining", s->active); - return 0; - } else if (r == -E2BIG) { - log_notice_errno(E2BIG, "Entry too big, skipped"); - return 1; - } else if (r == -EAGAIN) { - return 0; - } else if (r < 0) { - log_debug_errno(r, "Closing connection: %m"); - remove_source(server, fd); - return 0; - } else - return 1; -} - -static int dispatch_raw_source_until_block(sd_event_source *event, - void *userdata) { - RemoteSource *source = userdata; - int r; - - /* Make sure event stays around even if source is destroyed */ - sd_event_source_ref(event); - - r = handle_raw_source(event, source->fd, EPOLLIN, server); - if (r != 1) - /* No more data for now */ - sd_event_source_set_enabled(event, SD_EVENT_OFF); - - sd_event_source_unref(event); - - return r; -} - -static int dispatch_raw_source_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata) { - RemoteSource *source = userdata; - int r; - - assert(source->event); - assert(source->buffer_event); - - r = handle_raw_source(event, fd, EPOLLIN, server); - if (r == 1) - /* Might have more data. We need to rerun the handler - * until we are sure the buffer is exhausted. */ - sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON); - - return r; -} - -static int dispatch_blocking_source_event(sd_event_source *event, - void *userdata) { - RemoteSource *source = userdata; - - return handle_raw_source(event, source->fd, EPOLLIN, server); -} - -static int accept_connection(const char* type, int fd, - SocketAddress *addr, char **hostname) { - int fd2, r; - - log_debug("Accepting new %s connection on fd:%d", type, fd); - fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (fd2 < 0) - return log_error_errno(errno, "accept() on fd:%d failed: %m", fd); - - switch(socket_address_family(addr)) { - case AF_INET: - case AF_INET6: { - _cleanup_free_ char *a = NULL; - char *b; - - r = socket_address_print(addr, &a); - if (r < 0) { - log_error_errno(r, "socket_address_print(): %m"); - close(fd2); - return r; - } - - r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b); - if (r < 0) { - log_error_errno(r, "Resolving hostname failed: %m"); - close(fd2); - return r; - } - - log_debug("Accepted %s %s connection from %s", - type, - socket_address_family(addr) == AF_INET ? "IP" : "IPv6", - a); - - *hostname = b; - - return fd2; - }; - default: - log_error("Rejected %s connection with unsupported family %d", - type, socket_address_family(addr)); - close(fd2); - - return -EINVAL; - } -} - -static int dispatch_raw_connection_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata) { - RemoteServer *s = userdata; - int fd2; - SocketAddress addr = { - .size = sizeof(union sockaddr_union), - .type = SOCK_STREAM, - }; - char *hostname = NULL; - - fd2 = accept_connection("raw", fd, &addr, &hostname); - if (fd2 < 0) - return fd2; - - return add_source(s, fd2, hostname, true); -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - -static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = { - [JOURNAL_WRITE_SPLIT_NONE] = "none", - [JOURNAL_WRITE_SPLIT_HOST] = "host", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode); -static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode, - journal_write_split_mode, - JournalWriteSplitMode, - "Failed to parse split mode setting"); - -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 }, - { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, - {}}; - - return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf", - CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), - "Remote\0", config_item_table_lookup, items, - false, NULL); -} - -static void help(void) { - printf("%s [OPTIONS...] {FILE|-}...\n\n" - "Write external journal events to journal file(s).\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --url=URL Read events from systemd-journal-gatewayd at URL\n" - " --getter=COMMAND Read events from the output of COMMAND\n" - " --listen-raw=ADDR Listen for connections at ADDR\n" - " --listen-http=ADDR Listen for HTTP connections at ADDR\n" - " --listen-https=ADDR Listen for HTTPS connections at ADDR\n" - " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n" - " --compress[=BOOL] XZ-compress the output journal (default: yes)\n" - " --seal[=BOOL] Use event sealing (default: no)\n" - " --key=FILENAME SSL key in PEM format (default:\n" - " \"" PRIV_KEY_FILE "\")\n" - " --cert=FILENAME SSL certificate in PEM format (default:\n" - " \"" CERT_FILE "\")\n" - " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n" - " \"" TRUST_FILE "\")\n" - " --gnutls-log=CATEGORY...\n" - " Specify a list of gnutls logging categories\n" - " --split-mode=none|host How many output files to create\n" - "\n" - "Note: file descriptors from sd_listen_fds() will be consumed, too.\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_URL, - ARG_LISTEN_RAW, - ARG_LISTEN_HTTP, - ARG_LISTEN_HTTPS, - ARG_GETTER, - ARG_SPLIT_MODE, - ARG_COMPRESS, - ARG_SEAL, - ARG_KEY, - ARG_CERT, - ARG_TRUST, - ARG_GNUTLS_LOG, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "url", required_argument, NULL, ARG_URL }, - { "getter", required_argument, NULL, ARG_GETTER }, - { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW }, - { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP }, - { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS }, - { "output", required_argument, NULL, 'o' }, - { "split-mode", required_argument, NULL, ARG_SPLIT_MODE }, - { "compress", optional_argument, NULL, ARG_COMPRESS }, - { "seal", optional_argument, NULL, ARG_SEAL }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, - { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG }, - {} - }; - - int c, r; - bool type_a, type_b; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0 /* done */; - - case ARG_VERSION: - return version(); - - case ARG_URL: - if (arg_url) { - log_error("cannot currently set more than one --url"); - return -EINVAL; - } - - arg_url = optarg; - break; - - case ARG_GETTER: - if (arg_getter) { - log_error("cannot currently use --getter more than once"); - return -EINVAL; - } - - arg_getter = optarg; - break; - - case ARG_LISTEN_RAW: - if (arg_listen_raw) { - log_error("cannot currently use --listen-raw more than once"); - return -EINVAL; - } - - arg_listen_raw = optarg; - break; - - case ARG_LISTEN_HTTP: - if (arg_listen_http || http_socket >= 0) { - log_error("cannot currently use --listen-http more than once"); - return -EINVAL; - } - - r = negative_fd(optarg); - if (r >= 0) - http_socket = r; - else - arg_listen_http = optarg; - break; - - case ARG_LISTEN_HTTPS: - if (arg_listen_https || https_socket >= 0) { - log_error("cannot currently use --listen-https more than once"); - return -EINVAL; - } - - r = negative_fd(optarg); - if (r >= 0) - https_socket = r; - else - arg_listen_https = optarg; - - break; - - case ARG_KEY: - if (arg_key) { - log_error("Key file specified twice"); - return -EINVAL; - } - - arg_key = strdup(optarg); - if (!arg_key) - return log_oom(); - - break; - - case ARG_CERT: - if (arg_cert) { - log_error("Certificate file specified twice"); - return -EINVAL; - } - - arg_cert = strdup(optarg); - if (!arg_cert) - return log_oom(); - - break; - - case ARG_TRUST: - if (arg_trust || arg_trust_all) { - log_error("Confusing trusted CA configuration"); - return -EINVAL; - } - - if (streq(optarg, "all")) - arg_trust_all = true; - else { -#ifdef HAVE_GNUTLS - arg_trust = strdup(optarg); - if (!arg_trust) - return log_oom(); -#else - log_error("Option --trust is not available."); - return -EINVAL; -#endif - } - - break; - - case 'o': - if (arg_output) { - log_error("cannot use --output/-o more than once"); - return -EINVAL; - } - - arg_output = optarg; - break; - - case ARG_SPLIT_MODE: - arg_split_mode = journal_write_split_mode_from_string(optarg); - if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) { - log_error("Invalid split mode: %s", optarg); - return -EINVAL; - } - break; - - case ARG_COMPRESS: - if (optarg) { - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --compress= parameter."); - return -EINVAL; - } - - arg_compress = !!r; - } else - arg_compress = true; - - break; - - case ARG_SEAL: - if (optarg) { - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --seal= parameter."); - return -EINVAL; - } - - arg_seal = !!r; - } else - arg_seal = true; - - break; - - case ARG_GNUTLS_LOG: { -#ifdef HAVE_GNUTLS - const char* p = optarg; - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m"); - - if (r == 0) - break; - - if (strv_push(&arg_gnutls_log, word) < 0) - return log_oom(); - - word = NULL; - } - break; -#else - log_error("Option --gnutls-log is not available."); - return -EINVAL; -#endif - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unknown option code."); - } - - if (optind < argc) - arg_files = argv + optind; - - type_a = arg_getter || !strv_isempty(arg_files); - type_b = arg_url - || arg_listen_raw - || arg_listen_http || arg_listen_https - || sd_listen_fds(false) > 0; - if (type_a && type_b) { - log_error("Cannot use file input or --getter with " - "--arg-listen-... or socket activation."); - return -EINVAL; - } - if (type_a) { - if (!arg_output) { - log_error("Option --output must be specified with file input or --getter."); - return -EINVAL; - } - - arg_split_mode = JOURNAL_WRITE_SPLIT_NONE; - } - - if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE - && arg_output && is_dir(arg_output, true) > 0) { - log_error("For SplitMode=none, output must be a file."); - return -EINVAL; - } - - if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST - && arg_output && is_dir(arg_output, true) <= 0) { - log_error("For SplitMode=host, output must be a directory."); - return -EINVAL; - } - - log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s", - journal_write_split_mode_to_string(arg_split_mode), - strna(arg_key), - strna(arg_cert), - strna(arg_trust)); - - return 1 /* work to do */; -} - -static int load_certificates(char **key, char **cert, char **trust) { - int r; - - r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read key from file '%s': %m", - arg_key ?: PRIV_KEY_FILE); - - r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read certificate from file '%s': %m", - arg_cert ?: CERT_FILE); - - if (arg_trust_all) - log_info("Certificate checking disabled."); - else { - r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL); - if (r < 0) - return log_error_errno(r, "Failed to read CA certificate file '%s': %m", - arg_trust ?: TRUST_FILE); - } - - return 0; -} - -int main(int argc, char **argv) { - RemoteServer s = {}; - int r; - _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; - - log_show_color(true); - log_parse_environment(); - - r = parse_config(); - if (r < 0) - return EXIT_FAILURE; - - r = parse_argv(argc, argv); - if (r <= 0) - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; - - - if (arg_listen_http || arg_listen_https) { - r = setup_gnutls_logger(arg_gnutls_log); - if (r < 0) - return EXIT_FAILURE; - } - - if (arg_listen_https || https_socket >= 0) - if (load_certificates(&key, &cert, &trust) < 0) - return EXIT_FAILURE; - - if (remoteserver_init(&s, key, cert, trust) < 0) - return EXIT_FAILURE; - - r = sd_event_set_watchdog(s.events, true); - if (r < 0) - log_error_errno(r, "Failed to enable watchdog: %m"); - else - log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled"); - - log_debug("%s running as pid "PID_FMT, - program_invocation_short_name, getpid()); - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - while (s.active) { - r = sd_event_get_state(s.events); - if (r < 0) - break; - if (r == SD_EVENT_FINISHED) - break; - - r = sd_event_run(s.events, -1); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - break; - } - } - - sd_notifyf(false, - "STOPPING=1\n" - "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count); - log_info("Finishing after writing %" PRIu64 " entries", s.event_count); - - server_destroy(&s); - - free(arg_key); - free(arg_cert); - free(arg_trust); - - return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/journal-remote/journal-remote.conf.in b/src/journal-remote/journal-remote.conf.in deleted file mode 100644 index 7122d63362..0000000000 --- a/src/journal-remote/journal-remote.conf.in +++ /dev/null @@ -1,6 +0,0 @@ -[Remote] -# Seal=false -# SplitMode=host -# ServerKeyFile=@CERTIFICATEROOT@/private/journal-remote.pem -# ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-remote.pem -# TrustedCertificateFile=@CERTIFICATEROOT@/ca/trusted.pem diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h deleted file mode 100644 index 30ad7df996..0000000000 --- a/src/journal-remote/journal-remote.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-event.h" - -#include "hashmap.h" -#include "journal-remote-parse.h" -#include "journal-remote-write.h" -#include "microhttpd-util.h" - -typedef struct MHDDaemonWrapper MHDDaemonWrapper; - -struct MHDDaemonWrapper { - uint64_t fd; - struct MHD_Daemon *daemon; - - sd_event_source *event; -}; - -struct RemoteServer { - RemoteSource **sources; - size_t sources_size; - size_t active; - - sd_event *events; - sd_event_source *sigterm_event, *sigint_event, *listen_event; - - Hashmap *writers; - Writer *_single_writer; - uint64_t event_count; - - bool check_trust; - Hashmap *daemons; -}; diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c deleted file mode 100644 index 8ce8e1895e..0000000000 --- a/src/journal-remote/journal-upload-journal.c +++ /dev/null @@ -1,422 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "alloc-util.h" -#include "journal-upload.h" -#include "log.h" -#include "utf8.h" -#include "util.h" -#include "sd-daemon.h" - -/** - * Write up to size bytes to buf. Return negative on error, and number of - * bytes written otherwise. The last case is a kind of an error too. - */ -static ssize_t write_entry(char *buf, size_t size, Uploader *u) { - int r; - size_t pos = 0; - - assert(size <= SSIZE_MAX); - - for (;;) { - - switch(u->entry_state) { - case ENTRY_CURSOR: { - u->current_cursor = mfree(u->current_cursor); - - r = sd_journal_get_cursor(u->journal, &u->current_cursor); - if (r < 0) - return log_error_errno(r, "Failed to get cursor: %m"); - - r = snprintf(buf + pos, size - pos, - "__CURSOR=%s\n", u->current_cursor); - if (pos + r > size) - /* not enough space */ - return pos; - - u->entry_state++; - - if (pos + r == size) { - /* exactly one character short, but we don't need it */ - buf[size - 1] = '\n'; - return size; - } - - pos += r; - } /* fall through */ - - case ENTRY_REALTIME: { - usec_t realtime; - - r = sd_journal_get_realtime_usec(u->journal, &realtime); - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - r = snprintf(buf + pos, size - pos, - "__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime); - if (r + pos > size) - /* not enough space */ - return pos; - - u->entry_state++; - - if (r + pos == size) { - /* exactly one character short, but we don't need it */ - buf[size - 1] = '\n'; - return size; - } - - pos += r; - } /* fall through */ - - case ENTRY_MONOTONIC: { - usec_t monotonic; - sd_id128_t boot_id; - - r = sd_journal_get_monotonic_usec(u->journal, &monotonic, &boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - r = snprintf(buf + pos, size - pos, - "__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic); - if (r + pos > size) - /* not enough space */ - return pos; - - u->entry_state++; - - if (r + pos == size) { - /* exactly one character short, but we don't need it */ - buf[size - 1] = '\n'; - return size; - } - - pos += r; - } /* fall through */ - - case ENTRY_BOOT_ID: { - sd_id128_t boot_id; - char sid[33]; - - r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - r = snprintf(buf + pos, size - pos, - "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid)); - if (r + pos > size) - /* not enough space */ - return pos; - - u->entry_state++; - - if (r + pos == size) { - /* exactly one character short, but we don't need it */ - buf[size - 1] = '\n'; - return size; - } - - pos += r; - } /* fall through */ - - case ENTRY_NEW_FIELD: { - u->field_pos = 0; - - r = sd_journal_enumerate_data(u->journal, - &u->field_data, - &u->field_length); - if (r < 0) - return log_error_errno(r, "Failed to move to next field in entry: %m"); - else if (r == 0) { - u->entry_state = ENTRY_OUTRO; - continue; - } - - if (!utf8_is_printable_newline(u->field_data, - u->field_length, false)) { - u->entry_state = ENTRY_BINARY_FIELD_START; - continue; - } - - u->entry_state++; - } /* fall through */ - - case ENTRY_TEXT_FIELD: - case ENTRY_BINARY_FIELD: { - bool done; - size_t tocopy; - - done = size - pos > u->field_length - u->field_pos; - if (done) - tocopy = u->field_length - u->field_pos; - else - tocopy = size - pos; - - memcpy(buf + pos, - (char*) u->field_data + u->field_pos, - tocopy); - - if (done) { - buf[pos + tocopy] = '\n'; - pos += tocopy + 1; - u->entry_state = ENTRY_NEW_FIELD; - continue; - } else { - u->field_pos += tocopy; - return size; - } - } - - case ENTRY_BINARY_FIELD_START: { - const char *c; - size_t len; - - c = memchr(u->field_data, '=', u->field_length); - if (!c || c == u->field_data) { - log_error("Invalid field."); - return -EINVAL; - } - - len = c - (const char*)u->field_data; - - /* need space for label + '\n' */ - if (size - pos < len + 1) - return pos; - - memcpy(buf + pos, u->field_data, len); - buf[pos + len] = '\n'; - pos += len + 1; - - u->field_pos = len + 1; - u->entry_state++; - } /* fall through */ - - case ENTRY_BINARY_FIELD_SIZE: { - uint64_t le64; - - /* need space for uint64_t */ - if (size - pos < 8) - return pos; - - le64 = htole64(u->field_length - u->field_pos); - memcpy(buf + pos, &le64, 8); - pos += 8; - - u->entry_state++; - continue; - } - - case ENTRY_OUTRO: - /* need space for '\n' */ - if (size - pos < 1) - return pos; - - buf[pos++] = '\n'; - u->entry_state++; - u->entries_sent++; - - return pos; - - default: - assert_not_reached("WTF?"); - } - } - assert_not_reached("WTF?"); -} - -static inline void check_update_watchdog(Uploader *u) { - usec_t after; - usec_t elapsed_time; - - if (u->watchdog_usec <= 0) - return; - - after = now(CLOCK_MONOTONIC); - elapsed_time = usec_sub(after, u->watchdog_timestamp); - if (elapsed_time > u->watchdog_usec / 2) { - log_debug("Update watchdog timer"); - sd_notify(false, "WATCHDOG=1"); - u->watchdog_timestamp = after; - } -} - -static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void *userp) { - Uploader *u = userp; - int r; - sd_journal *j; - size_t filled = 0; - ssize_t w; - - assert(u); - assert(nmemb <= SSIZE_MAX / size); - - check_update_watchdog(u); - - j = u->journal; - - while (j && filled < size * nmemb) { - if (u->entry_state == ENTRY_DONE) { - r = sd_journal_next(j); - if (r < 0) { - log_error_errno(r, "Failed to move to next entry in journal: %m"); - return CURL_READFUNC_ABORT; - } else if (r == 0) { - if (u->input_event) - log_debug("No more entries, waiting for journal."); - else { - log_info("No more entries, closing journal."); - close_journal_input(u); - } - - u->uploading = false; - - break; - } - - u->entry_state = ENTRY_CURSOR; - } - - w = write_entry((char*)buf + filled, size * nmemb - filled, u); - if (w < 0) - return CURL_READFUNC_ABORT; - filled += w; - - if (filled == 0) { - log_error("Buffer space is too small to write entry."); - return CURL_READFUNC_ABORT; - } else if (u->entry_state != ENTRY_DONE) - /* This means that all available space was used up */ - break; - - log_debug("Entry %zu (%s) has been uploaded.", - u->entries_sent, u->current_cursor); - } - - return filled; -} - -void close_journal_input(Uploader *u) { - assert(u); - - if (u->journal) { - log_debug("Closing journal input."); - - sd_journal_close(u->journal); - u->journal = NULL; - } - u->timeout = 0; -} - -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"); - else if (r < skip) - return 0; - - /* have data */ - u->entry_state = ENTRY_CURSOR; - return start_upload(u, journal_input_callback, u); -} - -int check_journal_input(Uploader *u) { - if (u->input_event) { - int r; - - r = sd_journal_process(u->journal); - if (r < 0) { - log_error_errno(r, "Failed to process journal: %m"); - close_journal_input(u); - return r; - } - - if (r == SD_JOURNAL_NOP) - return 0; - } - - return process_journal_input(u, 1); -} - -static int dispatch_journal_input(sd_event_source *event, - int fd, - uint32_t revents, - void *userp) { - Uploader *u = userp; - - assert(u); - - if (u->uploading) - return 0; - - log_debug("Detected journal input, checking for new data."); - return check_journal_input(u); -} - -int open_journal_for_upload(Uploader *u, - sd_journal *j, - const char *cursor, - bool after_cursor, - bool follow) { - int fd, r, events; - - u->journal = j; - - sd_journal_set_data_threshold(j, 0); - - if (follow) { - fd = sd_journal_get_fd(j); - if (fd < 0) - return log_error_errno(fd, "sd_journal_get_fd failed: %m"); - - events = sd_journal_get_events(j); - - r = sd_journal_reliable_fd(j); - assert(r >= 0); - if (r > 0) - u->timeout = -1; - else - u->timeout = JOURNAL_UPLOAD_POLL_TIMEOUT; - - r = sd_event_add_io(u->events, &u->input_event, - fd, events, dispatch_journal_input, u); - if (r < 0) - return log_error_errno(r, "Failed to register input event: %m"); - - log_debug("Listening for journal events on fd:%d, timeout %d", - fd, u->timeout == (uint64_t) -1 ? -1 : (int) u->timeout); - } else - log_debug("Not listening for journal events."); - - if (cursor) { - r = sd_journal_seek_cursor(j, cursor); - if (r < 0) - return log_error_errno(r, "Failed to seek to cursor %s: %m", - cursor); - } - - return process_journal_input(u, 1 + !!after_cursor); -} diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c deleted file mode 100644 index 4647cfdeb3..0000000000 --- a/src/journal-remote/journal-upload.c +++ /dev/null @@ -1,880 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-daemon.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 "parse-util.h" -#include "sigbus.h" -#include "signal-util.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" -#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem" -#define DEFAULT_PORT 19532 - -static const char* arg_url = NULL; -static const char *arg_key = NULL; -static const char *arg_cert = NULL; -static const char *arg_trust = NULL; -static const char *arg_directory = NULL; -static char **arg_file = NULL; -static const char *arg_cursor = NULL; -static bool arg_after_cursor = false; -static int arg_journal_type = 0; -static const char *arg_machine = NULL; -static bool arg_merge = false; -static int arg_follow = -1; -static const char *arg_save_state = NULL; - -static void close_fd_input(Uploader *u); - -#define SERVER_ANSWER_KEEP 2048 - -#define STATE_FILE "/var/lib/systemd/journal-upload/state" - -#define easy_setopt(curl, opt, value, level, cmd) \ - do { \ - code = curl_easy_setopt(curl, opt, value); \ - if (code) { \ - log_full(level, \ - "curl_easy_setopt " #opt " failed: %s", \ - curl_easy_strerror(code)); \ - cmd; \ - } \ - } while (0) - -static size_t output_callback(char *buf, - size_t size, - size_t nmemb, - void *userp) { - Uploader *u = userp; - - assert(u); - - log_debug("The server answers (%zu bytes): %.*s", - size*nmemb, (int)(size*nmemb), buf); - - if (nmemb && !u->answer) { - u->answer = strndup(buf, size*nmemb); - if (!u->answer) - log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m", - size*nmemb); - } - - return size * nmemb; -} - -static int check_cursor_updating(Uploader *u) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - if (!u->state_file) - return 0; - - r = mkdir_parents(u->state_file, 0755); - if (r < 0) - return log_error_errno(r, "Cannot create parent directory of state file %s: %m", - u->state_file); - - r = fopen_temporary(u->state_file, &f, &temp_path); - if (r < 0) - return log_error_errno(r, "Cannot save state to %s: %m", - u->state_file); - unlink(temp_path); - - return 0; -} - -static int update_cursor_state(Uploader *u) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - if (!u->state_file || !u->last_cursor) - return 0; - - r = fopen_temporary(u->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fprintf(f, - "# This is private data. Do not parse.\n" - "LAST_CURSOR=%s\n", - u->last_cursor); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, u->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - if (temp_path) - (void) unlink(temp_path); - - (void) unlink(u->state_file); - - return log_error_errno(r, "Failed to save state %s: %m", u->state_file); -} - -static int load_cursor_state(Uploader *u) { - int r; - - if (!u->state_file) - return 0; - - r = parse_env_file(u->state_file, NEWLINE, - "LAST_CURSOR", &u->last_cursor, - NULL); - - if (r == -ENOENT) - log_debug("State file %s is not present.", u->state_file); - else if (r < 0) - return log_error_errno(r, "Failed to read state file %s: %m", - u->state_file); - else - log_debug("Last cursor was %s", u->last_cursor); - - return 0; -} - - - -int start_upload(Uploader *u, - size_t (*input_callback)(void *ptr, - size_t size, - size_t nmemb, - void *userdata), - void *data) { - CURLcode code; - - assert(u); - assert(input_callback); - - if (!u->header) { - struct curl_slist *h; - - h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal"); - if (!h) - return log_oom(); - - h = curl_slist_append(h, "Transfer-Encoding: chunked"); - if (!h) { - curl_slist_free_all(h); - return log_oom(); - } - - h = curl_slist_append(h, "Accept: text/plain"); - if (!h) { - curl_slist_free_all(h); - return log_oom(); - } - - u->header = h; - } - - if (!u->easy) { - CURL *curl; - - curl = curl_easy_init(); - if (!curl) { - log_error("Call to curl_easy_init failed."); - return -ENOSR; - } - - /* tell it to POST to the URL */ - easy_setopt(curl, CURLOPT_POST, 1L, - LOG_ERR, return -EXFULL); - - easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error, - LOG_ERR, return -EXFULL); - - /* set where to write to */ - easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback, - LOG_ERR, return -EXFULL); - - easy_setopt(curl, CURLOPT_WRITEDATA, data, - LOG_ERR, return -EXFULL); - - /* set where to read from */ - easy_setopt(curl, CURLOPT_READFUNCTION, input_callback, - LOG_ERR, return -EXFULL); - - easy_setopt(curl, CURLOPT_READDATA, data, - LOG_ERR, return -EXFULL); - - /* use our special own mime type and chunked transfer */ - easy_setopt(curl, CURLOPT_HTTPHEADER, u->header, - LOG_ERR, return -EXFULL); - - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) - /* enable verbose for easier tracing */ - easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, ); - - easy_setopt(curl, CURLOPT_USERAGENT, - "systemd-journal-upload " PACKAGE_STRING, - LOG_WARNING, ); - - if (arg_key || startswith(u->url, "https://")) { - easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE, - LOG_ERR, return -EXFULL); - easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE, - LOG_ERR, return -EXFULL); - } - - if (streq_ptr(arg_trust, "all")) - easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0, - LOG_ERR, return -EUCLEAN); - else if (arg_trust || startswith(u->url, "https://")) - easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE, - LOG_ERR, return -EXFULL); - - if (arg_key || arg_trust) - easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1, - LOG_WARNING, ); - - u->easy = curl; - } else { - /* truncate the potential old error message */ - u->error[0] = '\0'; - - free(u->answer); - u->answer = 0; - } - - /* upload to this place */ - code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url); - if (code) { - log_error("curl_easy_setopt CURLOPT_URL failed: %s", - curl_easy_strerror(code)); - return -EXFULL; - } - - u->uploading = true; - - return 0; -} - -static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) { - Uploader *u = userp; - - ssize_t r; - - assert(u); - assert(nmemb <= SSIZE_MAX / size); - - if (u->input < 0) - return 0; - - r = read(u->input, buf, size * nmemb); - log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, r); - - if (r > 0) - return r; - - u->uploading = false; - if (r == 0) { - log_debug("Reached EOF"); - close_fd_input(u); - return 0; - } else { - log_error_errno(errno, "Aborting transfer after read error on input: %m."); - return CURL_READFUNC_ABORT; - } -} - -static void close_fd_input(Uploader *u) { - assert(u); - - if (u->input >= 0) - close_nointr(u->input); - u->input = -1; - u->timeout = 0; -} - -static int dispatch_fd_input(sd_event_source *event, - int fd, - uint32_t revents, - void *userp) { - Uploader *u = userp; - - assert(u); - assert(fd >= 0); - - if (revents & EPOLLHUP) { - log_debug("Received HUP"); - close_fd_input(u); - return 0; - } - - if (!(revents & EPOLLIN)) { - log_warning("Unexpected poll event %"PRIu32".", revents); - return -EINVAL; - } - - if (u->uploading) { - log_warning("dispatch_fd_input called when uploading, ignoring."); - return 0; - } - - return start_upload(u, fd_input_callback, u); -} - -static int open_file_for_upload(Uploader *u, const char *filename) { - int fd, r = 0; - - if (streq(filename, "-")) - fd = STDIN_FILENO; - else { - fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", filename); - } - - u->input = fd; - - if (arg_follow) { - r = sd_event_add_io(u->events, &u->input_event, - fd, EPOLLIN, dispatch_fd_input, u); - if (r < 0) { - if (r != -EPERM || arg_follow > 0) - return log_error_errno(r, "Failed to register input event: %m"); - - /* Normal files should just be consumed without polling. */ - r = start_upload(u, fd_input_callback, u); - } - } - - return r; -} - -static int dispatch_sigterm(sd_event_source *event, - const struct signalfd_siginfo *si, - void *userdata) { - Uploader *u = userdata; - - assert(u); - - log_received_signal(LOG_INFO, si); - - close_fd_input(u); - close_journal_input(u); - - sd_event_exit(u->events, 0); - return 0; -} - -static int setup_signals(Uploader *u) { - int r; - - assert(u); - - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0); - - r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u); - if (r < 0) - return r; - - r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u); - if (r < 0) - return r; - - return 0; -} - -static int setup_uploader(Uploader *u, const char *url, const char *state_file) { - int r; - const char *host, *proto = ""; - - assert(u); - assert(url); - - memzero(u, sizeof(Uploader)); - u->input = -1; - - if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) { - host = url; - proto = "https://"; - } - - if (strchr(host, ':')) - u->url = strjoin(proto, url, "/upload", NULL); - else { - char *t; - size_t x; - - t = strdupa(url); - x = strlen(t); - while (x > 0 && t[x - 1] == '/') - t[x - 1] = '\0'; - - u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL); - } - if (!u->url) - return log_oom(); - - u->state_file = state_file; - - r = sd_event_default(&u->events); - if (r < 0) - return log_error_errno(r, "sd_event_default failed: %m"); - - r = setup_signals(u); - if (r < 0) - return log_error_errno(r, "Failed to set up signals: %m"); - - (void) sd_watchdog_enabled(false, &u->watchdog_usec); - - return load_cursor_state(u); -} - -static void destroy_uploader(Uploader *u) { - assert(u); - - curl_easy_cleanup(u->easy); - curl_slist_free_all(u->header); - free(u->answer); - - free(u->last_cursor); - free(u->current_cursor); - - free(u->url); - - u->input_event = sd_event_source_unref(u->input_event); - - close_fd_input(u); - close_journal_input(u); - - sd_event_source_unref(u->sigterm_event); - sd_event_source_unref(u->sigint_event); - sd_event_unref(u->events); -} - -static int perform_upload(Uploader *u) { - CURLcode code; - long status; - - assert(u); - - u->watchdog_timestamp = now(CLOCK_MONOTONIC); - code = curl_easy_perform(u->easy); - if (code) { - if (u->error[0]) - log_error("Upload to %s failed: %.*s", - u->url, (int) sizeof(u->error), u->error); - else - log_error("Upload to %s failed: %s", - u->url, curl_easy_strerror(code)); - return -EIO; - } - - code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status); - if (code) { - log_error("Failed to retrieve response code: %s", - curl_easy_strerror(code)); - return -EUCLEAN; - } - - if (status >= 300) { - log_error("Upload to %s failed with code %ld: %s", - u->url, status, strna(u->answer)); - return -EIO; - } else if (status < 200) { - log_error("Upload to %s finished with unexpected code %ld: %s", - u->url, status, strna(u->answer)); - return -EIO; - } else - log_debug("Upload finished successfully with code %ld: %s", - status, strna(u->answer)); - - free(u->last_cursor); - u->last_cursor = u->current_cursor; - u->current_cursor = NULL; - - return update_cursor_state(u); -} - -static int parse_config(void) { - const ConfigTableItem items[] = { - { "Upload", "URL", config_parse_string, 0, &arg_url }, - { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key }, - { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert }, - { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, - {}}; - - return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf", - CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), - "Upload\0", config_item_table_lookup, items, - false, NULL); -} - -static void help(void) { - printf("%s -u URL {FILE|-}...\n\n" - "Upload journal events to a remote server.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -u --url=URL Upload to this address (default port " - STRINGIFY(DEFAULT_PORT) ")\n" - " --key=FILENAME Specify key in PEM format (default:\n" - " \"" PRIV_KEY_FILE "\")\n" - " --cert=FILENAME Specify certificate in PEM format (default:\n" - " \"" CERT_FILE "\")\n" - " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n" - " \"" TRUST_FILE "\")\n" - " --system Use the system journal\n" - " --user Use the user journal for the current user\n" - " -m --merge Use all available journals\n" - " -M --machine=CONTAINER Operate on local container\n" - " -D --directory=PATH Use journal files from directory\n" - " --file=PATH Use this journal file\n" - " --cursor=CURSOR Start at the specified cursor\n" - " --after-cursor=CURSOR Start after the specified cursor\n" - " --follow[=BOOL] Do [not] wait for input\n" - " --save-state[=FILE] Save uploaded cursors (default \n" - " " STATE_FILE ")\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - ARG_KEY, - ARG_CERT, - ARG_TRUST, - ARG_USER, - ARG_SYSTEM, - ARG_FILE, - ARG_CURSOR, - ARG_AFTER_CURSOR, - ARG_FOLLOW, - ARG_SAVE_STATE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "url", required_argument, NULL, 'u' }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "merge", no_argument, NULL, 'm' }, - { "machine", required_argument, NULL, 'M' }, - { "directory", required_argument, NULL, 'D' }, - { "file", required_argument, NULL, ARG_FILE }, - { "cursor", required_argument, NULL, ARG_CURSOR }, - { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, - { "follow", optional_argument, NULL, ARG_FOLLOW }, - { "save-state", optional_argument, NULL, ARG_SAVE_STATE }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - opterr = 0; - - while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0 /* done */; - - case ARG_VERSION: - return version(); - - case 'u': - if (arg_url) { - log_error("cannot use more than one --url"); - return -EINVAL; - } - - arg_url = optarg; - break; - - case ARG_KEY: - if (arg_key) { - log_error("cannot use more than one --key"); - return -EINVAL; - } - - arg_key = optarg; - break; - - case ARG_CERT: - if (arg_cert) { - log_error("cannot use more than one --cert"); - return -EINVAL; - } - - arg_cert = optarg; - break; - - case ARG_TRUST: - if (arg_trust) { - log_error("cannot use more than one --trust"); - return -EINVAL; - } - - arg_trust = optarg; - break; - - case ARG_SYSTEM: - arg_journal_type |= SD_JOURNAL_SYSTEM; - break; - - case ARG_USER: - arg_journal_type |= SD_JOURNAL_CURRENT_USER; - break; - - case 'm': - arg_merge = true; - break; - - case 'M': - if (arg_machine) { - log_error("cannot use more than one --machine/-M"); - return -EINVAL; - } - - arg_machine = optarg; - break; - - case 'D': - if (arg_directory) { - log_error("cannot use more than one --directory/-D"); - return -EINVAL; - } - - arg_directory = optarg; - break; - - case ARG_FILE: - r = glob_extend(&arg_file, optarg); - if (r < 0) - return log_error_errno(r, "Failed to add paths: %m"); - break; - - case ARG_CURSOR: - if (arg_cursor) { - log_error("cannot use more than one --cursor/--after-cursor"); - return -EINVAL; - } - - arg_cursor = optarg; - break; - - case ARG_AFTER_CURSOR: - if (arg_cursor) { - log_error("cannot use more than one --cursor/--after-cursor"); - return -EINVAL; - } - - arg_cursor = optarg; - arg_after_cursor = true; - break; - - case ARG_FOLLOW: - if (optarg) { - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --follow= parameter."); - return -EINVAL; - } - - arg_follow = !!r; - } else - arg_follow = true; - - break; - - case ARG_SAVE_STATE: - arg_save_state = optarg ?: STATE_FILE; - break; - - case '?': - log_error("Unknown option %s.", argv[optind-1]); - return -EINVAL; - - case ':': - log_error("Missing argument to %s.", argv[optind-1]); - return -EINVAL; - - default: - assert_not_reached("Unhandled option code."); - } - - if (!arg_url) { - log_error("Required --url/-u option missing."); - return -EINVAL; - } - - if (!!arg_key != !!arg_cert) { - log_error("Options --key and --cert must be used together."); - return -EINVAL; - } - - if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) { - log_error("Input arguments make no sense with journal input."); - return -EINVAL; - } - - return 1; -} - -static int open_journal(sd_journal **j) { - int r; - - if (arg_directory) - r = sd_journal_open_directory(j, arg_directory, arg_journal_type); - else if (arg_file) - r = sd_journal_open_files(j, (const char**) arg_file, 0); - else if (arg_machine) - r = sd_journal_open_container(j, arg_machine, 0); - 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"); - return r; -} - -int main(int argc, char **argv) { - Uploader u; - int r; - bool use_journal; - - log_show_color(true); - log_parse_environment(); - - r = parse_config(); - if (r < 0) - goto finish; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - sigbus_install(); - - r = setup_uploader(&u, arg_url, arg_save_state); - if (r < 0) - goto cleanup; - - sd_event_set_watchdog(u.events, true); - - r = check_cursor_updating(&u); - if (r < 0) - goto cleanup; - - log_debug("%s running as pid "PID_FMT, - program_invocation_short_name, getpid()); - - use_journal = optind >= argc; - if (use_journal) { - sd_journal *j; - r = open_journal(&j); - if (r < 0) - goto finish; - r = open_journal_for_upload(&u, j, - arg_cursor ?: u.last_cursor, - arg_cursor ? arg_after_cursor : true, - !!arg_follow); - if (r < 0) - goto finish; - } - - sd_notify(false, - "READY=1\n" - "STATUS=Processing input..."); - - for (;;) { - r = sd_event_get_state(u.events); - if (r < 0) - break; - if (r == SD_EVENT_FINISHED) - break; - - if (use_journal) { - if (!u.journal) - break; - - r = check_journal_input(&u); - } else if (u.input < 0 && !use_journal) { - if (optind >= argc) - break; - - log_debug("Using %s as input.", argv[optind]); - r = open_file_for_upload(&u, argv[optind++]); - } - if (r < 0) - goto cleanup; - - if (u.uploading) { - r = perform_upload(&u); - if (r < 0) - break; - } - - r = sd_event_run(u.events, u.timeout); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - break; - } - } - -cleanup: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - destroy_uploader(&u); - -finish: - return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/journal-remote/journal-upload.conf.in b/src/journal-remote/journal-upload.conf.in deleted file mode 100644 index c5670682e8..0000000000 --- a/src/journal-remote/journal-upload.conf.in +++ /dev/null @@ -1,5 +0,0 @@ -[Upload] -# URL= -# ServerKeyFile=@CERTIFICATEROOT@/private/journal-upload.pem -# ServerCertificateFile=@CERTIFICATEROOT@/certs/journal-upload.pem -# TrustedCertificateFile=@CERTIFICATEROOT@/ca/trusted.pem diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h deleted file mode 100644 index 5711905f86..0000000000 --- a/src/journal-remote/journal-upload.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include - -#include "sd-event.h" -#include "sd-journal.h" -#include "time-util.h" - -typedef enum { - ENTRY_CURSOR = 0, /* Nothing actually written yet. */ - ENTRY_REALTIME, - ENTRY_MONOTONIC, - ENTRY_BOOT_ID, - ENTRY_NEW_FIELD, /* In between fields. */ - ENTRY_TEXT_FIELD, /* In the middle of a text field. */ - ENTRY_BINARY_FIELD_START, /* Writing the name of a binary field. */ - ENTRY_BINARY_FIELD_SIZE, /* Writing the size of a binary field. */ - ENTRY_BINARY_FIELD, /* In the middle of a binary field. */ - ENTRY_OUTRO, /* Writing '\n' */ - ENTRY_DONE, /* Need to move to a new field. */ -} entry_state; - -typedef struct Uploader { - sd_event *events; - sd_event_source *sigint_event, *sigterm_event; - - char *url; - CURL *easy; - bool uploading; - char error[CURL_ERROR_SIZE]; - struct curl_slist *header; - char *answer; - - sd_event_source *input_event; - uint64_t timeout; - - /* fd stuff */ - int input; - - /* journal stuff */ - sd_journal* journal; - - entry_state entry_state; - const void *field_data; - size_t field_pos, field_length; - - /* general metrics */ - const char *state_file; - - size_t entries_sent; - char *last_cursor, *current_cursor; - usec_t watchdog_timestamp; - usec_t watchdog_usec; -} Uploader; - -#define JOURNAL_UPLOAD_POLL_TIMEOUT (10 * USEC_PER_SEC) - -int start_upload(Uploader *u, - size_t (*input_callback)(void *ptr, - size_t size, - size_t nmemb, - void *userdata), - void *data); - -int open_journal_for_upload(Uploader *u, - sd_journal *j, - const char *cursor, - bool after_cursor, - bool follow); -void close_journal_input(Uploader *u); -int check_journal_input(Uploader *u); diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py deleted file mode 100755 index fd6964e758..0000000000 --- a/src/journal-remote/log-generator.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/python -from __future__ import print_function -import sys -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 = """\ -__CURSOR=s=6863c726210b4560b7048889d8ada5c5;i=3e931;b=f446871715504074bf7049ef0718fa93;m={m:x};t=4fd05c -__REALTIME_TIMESTAMP={realtime_ts} -__MONOTONIC_TIMESTAMP={monotonic_ts} -_BOOT_ID=f446871715504074bf7049ef0718fa93 -_TRANSPORT=syslog -PRIORITY={priority} -SYSLOG_FACILITY={facility} -SYSLOG_IDENTIFIER=/USR/SBIN/CRON -MESSAGE={message} -_UID=0 -_GID=0 -_MACHINE_ID=69121ca41d12c1b69a7960174c27b618 -_HOSTNAME=hostname -SYSLOG_PID=25721 -_PID=25721 -_SOURCE_REALTIME_TIMESTAMP={source_realtime_ts} -DATA={data} -""" - -m = 0x198603b12d7 -realtime_ts = 1404101101501873 -monotonic_ts = 1753961140951 -source_realtime_ts = 1404101101483516 -priority = 3 -facility = 6 - -src = open('/dev/urandom', 'rb') - -bytes = 0 -counter = 0 - -for i in range(OPTIONS.n): - message = repr(src.read(2000)) - 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, - monotonic_ts=monotonic_ts, - source_realtime_ts=source_realtime_ts, - priority=priority, - facility=facility, - message=message, - data=data) - m += 1 - realtime_ts += 1 - monotonic_ts += 1 - source_realtime_ts += 1 - - bytes += len(entry) - - print(entry) - - if OPTIONS.dots: - print('.', file=sys.stderr, end='', flush=True) - -if OPTIONS.dots: - print(file=sys.stderr) -print('Wrote {} bytes'.format(bytes), file=sys.stderr) diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c deleted file mode 100644 index 2f16b02e9a..0000000000 --- a/src/journal-remote/microhttpd-util.c +++ /dev/null @@ -1,327 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_GNUTLS -#include -#include -#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; - - f = strjoina("microhttpd: ", fmt); - - DISABLE_WARNING_FORMAT_NONLITERAL; - log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap); - REENABLE_WARNING; -} - - -static int mhd_respond_internal(struct MHD_Connection *connection, - enum MHD_RequestTerminationCode code, - char *buffer, - size_t size, - enum MHD_ResponseMemoryMode mode) { - struct MHD_Response *response; - int r; - - assert(connection); - - response = MHD_create_response_from_buffer(size, buffer, mode); - if (!response) - return MHD_NO; - - log_debug("Queueing response %u: %s", code, buffer); - MHD_add_response_header(response, "Content-Type", "text/plain"); - r = MHD_queue_response(connection, code, response); - MHD_destroy_response(response); - - return r; -} - -int mhd_respond(struct MHD_Connection *connection, - enum MHD_RequestTerminationCode code, - const char *message) { - - return mhd_respond_internal(connection, code, - (char*) message, strlen(message), - MHD_RESPMEM_PERSISTENT); -} - -int mhd_respond_oom(struct MHD_Connection *connection) { - return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n"); -} - -int mhd_respondf(struct MHD_Connection *connection, - enum MHD_RequestTerminationCode code, - const char *format, ...) { - - char *m; - int r; - va_list ap; - - assert(connection); - assert(format); - - va_start(ap, format); - r = vasprintf(&m, format, ap); - va_end(ap); - - if (r < 0) - return respond_oom(connection); - - return mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE); -} - -#ifdef HAVE_GNUTLS - -static struct { - const char *const names[4]; - int level; - bool enabled; -} gnutls_log_map[] = { - { {"0"}, LOG_DEBUG }, - { {"1", "audit"}, LOG_WARNING, true}, /* gnutls session audit */ - { {"2", "assert"}, LOG_DEBUG }, /* gnutls assert log */ - { {"3", "hsk", "ext"}, LOG_DEBUG }, /* gnutls handshake log */ - { {"4", "rec"}, LOG_DEBUG }, /* gnutls record log */ - { {"5", "dtls"}, LOG_DEBUG }, /* gnutls DTLS log */ - { {"6", "buf"}, LOG_DEBUG }, - { {"7", "write", "read"}, LOG_DEBUG }, - { {"8"}, LOG_DEBUG }, - { {"9", "enc", "int"}, LOG_DEBUG }, -}; - -static void log_func_gnutls(int level, const char *message) { - assert_se(message); - - if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) { - if (gnutls_log_map[level].enabled) - log_internal(gnutls_log_map[level].level, 0, NULL, 0, NULL, "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message); - } else { - log_debug("Received GNUTLS message with unknown level %d.", level); - log_internal(LOG_DEBUG, 0, NULL, 0, NULL, "gnutls: %s", message); - } -} - -static void log_reset_gnutls_level(void) { - int i; - - for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--) - if (gnutls_log_map[i].enabled) { - log_debug("Setting gnutls log level to %d", i); - gnutls_global_set_log_level(i); - break; - } -} - -static int log_enable_gnutls_category(const char *cat) { - unsigned i; - - if (streq(cat, "all")) { - for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++) - gnutls_log_map[i].enabled = true; - log_reset_gnutls_level(); - return 0; - } else - for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++) - if (strv_contains((char**)gnutls_log_map[i].names, cat)) { - gnutls_log_map[i].enabled = true; - log_reset_gnutls_level(); - return 0; - } - log_error("No such log category: %s", cat); - return -EINVAL; -} - -int setup_gnutls_logger(char **categories) { - char **cat; - int r; - - gnutls_global_set_log_function(log_func_gnutls); - - if (categories) { - STRV_FOREACH(cat, categories) { - r = log_enable_gnutls_category(*cat); - if (r < 0) - return r; - } - } else - log_reset_gnutls_level(); - - return 0; -} - -static int verify_cert_authorized(gnutls_session_t session) { - unsigned status; - gnutls_certificate_type_t type; - gnutls_datum_t out; - int r; - - r = gnutls_certificate_verify_peers2(session, &status); - if (r < 0) - return log_error_errno(r, "gnutls_certificate_verify_peers2 failed: %m"); - - type = gnutls_certificate_type_get(session); - r = gnutls_certificate_verification_status_print(status, type, &out, 0); - if (r < 0) - return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m"); - - log_debug("Certificate status: %s", out.data); - gnutls_free(out.data); - - return status == 0 ? 0 : -EPERM; -} - -static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) { - const gnutls_datum_t *pcert; - unsigned listsize; - gnutls_x509_crt_t cert; - int r; - - assert(session); - assert(client_cert); - - pcert = gnutls_certificate_get_peers(session, &listsize); - if (!pcert || !listsize) { - log_error("Failed to retrieve certificate chain"); - return -EINVAL; - } - - r = gnutls_x509_crt_init(&cert); - if (r < 0) { - log_error("Failed to initialize client certificate"); - return r; - } - - /* Note that by passing values between 0 and listsize here, you - can get access to the CA's certs */ - r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER); - if (r < 0) { - log_error("Failed to import client certificate"); - gnutls_x509_crt_deinit(cert); - return r; - } - - *client_cert = cert; - return 0; -} - -static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) { - size_t len = 0; - int r; - - assert(buf); - assert(*buf == NULL); - - r = gnutls_x509_crt_get_dn(client_cert, NULL, &len); - if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) { - log_error("gnutls_x509_crt_get_dn failed"); - return r; - } - - *buf = malloc(len); - if (!*buf) - return log_oom(); - - gnutls_x509_crt_get_dn(client_cert, *buf, &len); - return 0; -} - -static inline void gnutls_x509_crt_deinitp(gnutls_x509_crt_t *p) { - gnutls_x509_crt_deinit(*p); -} - -int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { - const union MHD_ConnectionInfo *ci; - gnutls_session_t session; - _cleanup_(gnutls_x509_crt_deinitp) gnutls_x509_crt_t client_cert = NULL; - _cleanup_free_ char *buf = NULL; - int r; - - assert(connection); - assert(code); - - *code = 0; - - ci = MHD_get_connection_info(connection, - MHD_CONNECTION_INFO_GNUTLS_SESSION); - if (!ci) { - log_error("MHD_get_connection_info failed: session is unencrypted"); - *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN, - "Encrypted connection is required"); - return -EPERM; - } - session = ci->tls_session; - assert(session); - - r = get_client_cert(session, &client_cert); - if (r < 0) { - *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, - "Authorization through certificate is required"); - return -EPERM; - } - - r = get_auth_dn(client_cert, &buf); - if (r < 0) { - *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, - "Failed to determine distinguished name from certificate"); - return -EPERM; - } - - log_debug("Connection from %s", buf); - - if (hostname) { - *hostname = buf; - buf = NULL; - } - - r = verify_cert_authorized(session); - if (r < 0) { - log_warning("Client is not authorized"); - *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED, - "Client certificate not signed by recognized authority"); - } - return r; -} - -#else -int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { - return -EPERM; -} - -int setup_gnutls_logger(char **categories) { - if (categories) - log_notice("Ignoring specified gnutls logging categories — gnutls not available."); - return 0; -} -#endif diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h deleted file mode 100644 index ea160f212b..0000000000 --- a/src/journal-remote/microhttpd-util.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#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. */ -#define respond_oom(connection) log_oom(), mhd_respond_oom(connection) - -int mhd_respondf(struct MHD_Connection *connection, - unsigned code, - const char *format, ...) _printf_(3,4); - -int mhd_respond(struct MHD_Connection *connection, - unsigned code, - const char *message); - -int mhd_respond_oom(struct MHD_Connection *connection); - -int check_permissions(struct MHD_Connection *connection, int *code, char **hostname); - -/* Set gnutls internal logging function to a callback which uses our - * own logging framework. - * - * gnutls categories are additionally filtered by our internal log - * level, so it should be set fairly high to capture all potentially - * interesting events without overwhelming detail. - */ -int setup_gnutls_logger(char **categories); diff --git a/src/journal/.gitignore b/src/journal/.gitignore deleted file mode 100644 index 04d5852547..0000000000 --- a/src/journal/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/journald-gperf.c -/libsystemd-journal.pc -/audit_type-list.txt -/audit_type-*-name.* diff --git a/src/journal/Makefile b/src/journal/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/journal/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c deleted file mode 100644 index 71e8790ca8..0000000000 --- a/src/journal/audit-type.c +++ /dev/null @@ -1,29 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#ifdef HAVE_AUDIT -# include -#endif - -#include "missing.h" -#include "audit-type.h" -#include "audit_type-to-name.h" -#include "macro.h" diff --git a/src/journal/audit-type.h b/src/journal/audit-type.h deleted file mode 100644 index 1dd2163707..0000000000 --- a/src/journal/audit-type.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "macro.h" - -const char *audit_type_to_string(int type); -int audit_type_from_string(const char *s); - -/* This is inspired by DNS TYPEnnn formatting */ -#define audit_type_name_alloca(type) \ - ({ \ - const char *_s_; \ - _s_ = audit_type_to_string(type); \ - if (!_s_) { \ - _s_ = alloca(strlen("AUDIT") + DECIMAL_STR_MAX(int)); \ - sprintf((char*) _s_, "AUDIT%04i", type); \ - } \ - _s_; \ - }) diff --git a/src/journal/cat.c b/src/journal/cat.c deleted file mode 100644 index 08c844d44f..0000000000 --- a/src/journal/cat.c +++ /dev/null @@ -1,161 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-journal.h" - -#include "fd-util.h" -#include "parse-util.h" -#include "string-util.h" -#include "syslog-util.h" -#include "util.h" - -static const char *arg_identifier = NULL; -static int arg_priority = LOG_INFO; -static bool arg_level_prefix = true; - -static void help(void) { - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Execute process with stdout/stderr connected to the journal.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -t --identifier=STRING Set syslog identifier\n" - " -p --priority=PRIORITY Set priority value (0..7)\n" - " --level-prefix=BOOL Control whether level prefix shall be parsed\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_LEVEL_PREFIX - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "identifier", required_argument, NULL, 't' }, - { "priority", required_argument, NULL, 'p' }, - { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 't': - if (isempty(optarg)) - arg_identifier = NULL; - else - arg_identifier = optarg; - break; - - case 'p': - arg_priority = log_level_from_string(optarg); - if (arg_priority < 0) { - log_error("Failed to parse priority value."); - return -EINVAL; - } - break; - - case ARG_LEVEL_PREFIX: { - int k; - - k = parse_boolean(optarg); - if (k < 0) - return log_error_errno(k, "Failed to parse level prefix value."); - - arg_level_prefix = k; - break; - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - _cleanup_close_ int fd = -1, saved_stderr = -1; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); - if (fd < 0) { - r = log_error_errno(fd, "Failed to create stream fd: %m"); - goto finish; - } - - saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); - - if (dup3(fd, STDOUT_FILENO, 0) < 0 || - dup3(fd, STDERR_FILENO, 0) < 0) { - r = log_error_errno(errno, "Failed to duplicate fd: %m"); - goto finish; - } - - if (fd >= 3) - safe_close(fd); - fd = -1; - - if (argc <= optind) - (void) execl("/bin/cat", "/bin/cat", NULL); - else - (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) - (void) dup3(saved_stderr, STDERR_FILENO, 0); - - log_error_errno(r, "Failed to execute process: %m"); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/journal/catalog.c b/src/journal/catalog.c deleted file mode 100644 index 886f6efd8b..0000000000 --- a/src/journal/catalog.c +++ /dev/null @@ -1,767 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-id128.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 "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/", - "/usr/lib/systemd/catalog/", - NULL -}; - -#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' } - -typedef struct CatalogHeader { - uint8_t signature[8]; /* "RHHHKSLP" */ - le32_t compatible_flags; - le32_t incompatible_flags; - le64_t header_size; - le64_t n_items; - le64_t catalog_item_size; -} CatalogHeader; - -typedef struct CatalogItem { - sd_id128_t id; - char language[32]; - le64_t offset; -} CatalogItem; - -static void catalog_hash_func(const void *p, struct siphash *state) { - const CatalogItem *i = p; - - 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) { - const CatalogItem *i = a, *j = b; - unsigned k; - - for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) { - if (i->id.bytes[k] < j->id.bytes[k]) - return -1; - if (i->id.bytes[k] > j->id.bytes[k]) - return 1; - } - - return strcmp(i->language, j->language); -} - -const struct hash_ops catalog_hash_ops = { - .hash = catalog_hash_func, - .compare = catalog_compare_func -}; - -static bool next_header(const char **s) { - const char *e; - - e = strchr(*s, '\n'); - - /* Unexpected end */ - if (!e) - return false; - - /* End of headers */ - if (e == *s) - return false; - - *s = e + 1; - return true; -} - -static const char *skip_header(const char *s) { - while (next_header(&s)) - ; - return s; -} - -static char *combine_entries(const char *one, const char *two) { - const char *b1, *b2; - size_t l1, l2, n; - char *dest, *p; - - /* Find split point of headers to body */ - b1 = skip_header(one); - b2 = skip_header(two); - - l1 = strlen(one); - l2 = strlen(two); - dest = new(char, l1 + l2 + 1); - if (!dest) { - log_oom(); - return NULL; - } - - p = dest; - - /* Headers from @one */ - n = b1 - one; - p = mempcpy(p, one, n); - - /* Headers from @two, these will only be found if not present above */ - n = b2 - two; - p = mempcpy(p, two, n); - - /* Body from @one */ - n = l1 - (b1 - one); - if (n > 0) { - memcpy(p, b1, n); - p += n; - - /* Body from @two */ - } else { - n = l2 - (b2 - two); - memcpy(p, b2, n); - p += n; - } - - assert(p - dest <= (ptrdiff_t)(l1 + l2)); - p[0] = '\0'; - return dest; -} - -static int finish_item( - Hashmap *h, - sd_id128_t id, - const char *language, - char *payload, size_t payload_size) { - - _cleanup_free_ CatalogItem *i = NULL; - _cleanup_free_ char *prev = NULL, *combined = NULL; - - assert(h); - assert(payload); - assert(payload_size > 0); - - i = new0(CatalogItem, 1); - if (!i) - return log_oom(); - - i->id = id; - if (language) { - assert(strlen(language) > 1 && strlen(language) < 32); - strcpy(i->language, language); - } - - prev = hashmap_get(h, i); - if (prev) { - /* Already have such an item, combine them */ - combined = combine_entries(payload, prev); - if (!combined) - return log_oom(); - - if (hashmap_update(h, i, combined) < 0) - return log_oom(); - combined = NULL; - } else { - /* A new item */ - combined = memdup(payload, payload_size + 1); - if (!combined) - return log_oom(); - - if (hashmap_put(h, i, combined) < 0) - return log_oom(); - i = NULL; - combined = NULL; - } - - return 0; -} - -int catalog_file_lang(const char* filename, char **lang) { - char *beg, *end, *_lang; - - end = endswith(filename, ".catalog"); - if (!end) - return 0; - - beg = end - 1; - while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32) - beg--; - - if (*beg != '.' || end <= beg + 1) - return 0; - - _lang = strndup(beg + 1, end - beg - 1); - if (!_lang) - return -ENOMEM; - - *lang = _lang; - return 1; -} - -static int catalog_entry_lang(const char* filename, int line, - const char* t, const char* deflang, char **lang) { - size_t c; - - c = strlen(t); - if (c == 0) { - log_error("[%s:%u] Language too short.", filename, line); - return -EINVAL; - } - if (c > 31) { - log_error("[%s:%u] language too long.", filename, line); - return -EINVAL; - } - - if (deflang) { - if (streq(t, deflang)) { - log_warning("[%s:%u] language specified unnecessarily", - filename, line); - return 0; - } else - log_warning("[%s:%u] language differs from default for file", - filename, line); - } - - *lang = strdup(t); - if (!*lang) - return -ENOMEM; - - return 0; -} - -int catalog_import_file(Hashmap *h, const char *path) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *payload = NULL; - size_t payload_size = 0, payload_allocated = 0; - unsigned n = 0; - sd_id128_t id; - _cleanup_free_ char *deflang = NULL, *lang = NULL; - bool got_id = false, empty_line = true; - int r; - - assert(h); - assert(path); - - f = fopen(path, "re"); - if (!f) - return log_error_errno(errno, "Failed to open file %s: %m", path); - - r = catalog_file_lang(path, &deflang); - if (r < 0) - 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); - - for (;;) { - char line[LINE_MAX]; - size_t line_len; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file %s: %m", path); - } - - n++; - - truncate_nl(line); - - if (line[0] == 0) { - empty_line = true; - continue; - } - - if (strchr(COMMENTS "\n", line[0])) - continue; - - if (empty_line && - strlen(line) >= 2+1+32 && - line[0] == '-' && - line[1] == '-' && - line[2] == ' ' && - (line[2+1+32] == ' ' || line[2+1+32] == '\0')) { - - bool with_language; - sd_id128_t jd; - - /* New entry */ - - with_language = line[2+1+32] != '\0'; - line[2+1+32] = '\0'; - - if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { - - if (got_id) { - if (payload_size == 0) { - log_error("[%s:%u] No payload text.", path, n); - return -EINVAL; - } - - r = finish_item(h, id, lang ?: deflang, payload, payload_size); - if (r < 0) - return r; - - lang = mfree(lang); - payload_size = 0; - } - - if (with_language) { - char *t; - - t = strstrip(line + 2 + 1 + 32 + 1); - r = catalog_entry_lang(path, n, t, deflang, &lang); - if (r < 0) - return r; - } - - got_id = true; - empty_line = false; - id = jd; - - continue; - } - } - - /* Payload */ - if (!got_id) { - log_error("[%s:%u] Got payload before ID.", path, n); - return -EINVAL; - } - - line_len = strlen(line); - if (!GREEDY_REALLOC(payload, payload_allocated, - payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1)) - return log_oom(); - - if (empty_line) - payload[payload_size++] = '\n'; - memcpy(payload + payload_size, line, line_len); - payload_size += line_len; - payload[payload_size++] = '\n'; - payload[payload_size] = '\0'; - - empty_line = false; - } - - if (got_id) { - if (payload_size == 0) { - log_error("[%s:%u] No payload text.", path, n); - return -EINVAL; - } - - r = finish_item(h, id, lang ?: deflang, payload, payload_size); - if (r < 0) - return r; - } - - return 0; -} - -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; - _cleanup_free_ char *d, *p = NULL; - size_t k; - - d = dirname_malloc(database); - if (!d) - return log_oom(); - - r = mkdir_p(d, 0775); - if (r < 0) - return log_error_errno(r, "Recursive mkdir %s: %m", d); - - r = fopen_temporary(database, &w, &p); - if (r < 0) - return log_error_errno(r, "Failed to open database for writing: %s: %m", - database); - - zero(header); - memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); - header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); - header.catalog_item_size = htole64(sizeof(CatalogItem)); - header.n_items = htole64(n); - - r = -EIO; - - k = fwrite(&header, 1, sizeof(header), w); - if (k != sizeof(header)) { - log_error("%s: failed to write header.", p); - goto error; - } - - k = fwrite(items, 1, n * sizeof(CatalogItem), w); - if (k != n * sizeof(CatalogItem)) { - log_error("%s: failed to write database.", p); - goto error; - } - - k = fwrite(sb->buf, 1, sb->len, w); - if (k != sb->len) { - log_error("%s: failed to write strings.", p); - goto error; - } - - r = fflush_and_check(w); - if (r < 0) { - log_error_errno(r, "%s: failed to write database: %m", p); - goto error; - } - - fchmod(fileno(w), 0644); - - if (rename(p, database) < 0) { - r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); - goto error; - } - - return ftello(w); - -error: - (void) unlink(p); - return r; -} - -int catalog_update(const char* database, const char* root, const char* const* dirs) { - _cleanup_strv_free_ char **files = NULL; - char **f; - struct strbuf *sb = NULL; - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - _cleanup_free_ CatalogItem *items = NULL; - ssize_t offset; - char *payload; - CatalogItem *i; - Iterator j; - unsigned n; - int r; - int64_t sz; - - h = hashmap_new(&catalog_hash_ops); - sb = strbuf_new(); - - if (!h || !sb) { - r = log_oom(); - goto finish; - } - - r = conf_files_list_strv(&files, ".catalog", root, dirs); - if (r < 0) { - log_error_errno(r, "Failed to get catalog files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - log_debug("Reading file '%s'", *f); - r = catalog_import_file(h, *f); - if (r < 0) { - log_error_errno(r, "Failed to import file '%s': %m", *f); - goto finish; - } - } - - if (hashmap_size(h) <= 0) { - log_info("No items in catalog."); - goto finish; - } else - log_debug("Found %u items in catalog.", hashmap_size(h)); - - items = new(CatalogItem, hashmap_size(h)); - if (!items) { - r = log_oom(); - goto finish; - } - - n = 0; - HASHMAP_FOREACH_KEY(payload, i, h, j) { - log_debug("Found " SD_ID128_FORMAT_STR ", language %s", - SD_ID128_FORMAT_VAL(i->id), - isempty(i->language) ? "C" : i->language); - - offset = strbuf_add_string(sb, payload, strlen(payload)); - if (offset < 0) { - r = log_oom(); - goto finish; - } - i->offset = htole64((uint64_t) offset); - items[n++] = *i; - } - - assert(n == hashmap_size(h)); - qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func); - - strbuf_complete(sb); - - 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: - strbuf_cleanup(sb); - - return r; -} - -static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) { - const CatalogHeader *h; - int fd; - void *p; - struct stat st; - - assert(_fd); - assert(_st); - assert(_p); - - fd = open(database, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (fstat(fd, &st) < 0) { - safe_close(fd); - return -errno; - } - - if (st.st_size < (off_t) sizeof(CatalogHeader)) { - safe_close(fd); - return -EINVAL; - } - - p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) { - safe_close(fd); - return -errno; - } - - h = p; - if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 || - le64toh(h->header_size) < sizeof(CatalogHeader) || - le64toh(h->catalog_item_size) < sizeof(CatalogItem) || - h->incompatible_flags != 0 || - le64toh(h->n_items) <= 0 || - st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) { - safe_close(fd); - munmap(p, st.st_size); - return -EBADMSG; - } - - *_fd = fd; - *_st = st; - *_p = p; - - return 0; -} - -static const char *find_id(void *p, sd_id128_t id) { - CatalogItem key, *f = NULL; - const CatalogHeader *h = p; - const char *loc; - - zero(key); - key.id = id; - - loc = setlocale(LC_MESSAGES, NULL); - if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) { - strncpy(key.language, loc, sizeof(key.language)); - key.language[strcspn(key.language, ".@")] = 0; - - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); - if (!f) { - char *e; - - e = strchr(key.language, '_'); - if (e) { - *e = 0; - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); - } - } - } - - if (!f) { - zero(key.language); - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); - } - - if (!f) - return NULL; - - return (const char*) p + - le64toh(h->header_size) + - le64toh(h->n_items) * le64toh(h->catalog_item_size) + - le64toh(f->offset); -} - -int catalog_get(const char* database, sd_id128_t id, char **_text) { - _cleanup_close_ int fd = -1; - void *p = NULL; - struct stat st = {}; - char *text = NULL; - int r; - const char *s; - - assert(_text); - - r = open_mmap(database, &fd, &st, &p); - if (r < 0) - return r; - - s = find_id(p, id); - if (!s) { - r = -ENOENT; - goto finish; - } - - text = strdup(s); - if (!text) { - r = -ENOMEM; - goto finish; - } - - *_text = text; - r = 0; - -finish: - if (p) - munmap(p, st.st_size); - - return r; -} - -static char *find_header(const char *s, const char *header) { - - for (;;) { - const char *v; - - v = startswith(s, header); - if (v) { - v += strspn(v, WHITESPACE); - return strndup(v, strcspn(v, NEWLINE)); - } - - if (!next_header(&s)) - return NULL; - } -} - -static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) { - if (oneline) { - _cleanup_free_ char *subject = NULL, *defined_by = NULL; - - subject = find_header(s, "Subject:"); - defined_by = find_header(s, "Defined-By:"); - - fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", - SD_ID128_FORMAT_VAL(id), - strna(defined_by), strna(subject)); - } else - fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n", - SD_ID128_FORMAT_VAL(id), s); -} - - -int catalog_list(FILE *f, const char *database, bool oneline) { - _cleanup_close_ int fd = -1; - void *p = NULL; - struct stat st; - const CatalogHeader *h; - const CatalogItem *items; - int r; - unsigned n; - sd_id128_t last_id; - bool last_id_set = false; - - r = open_mmap(database, &fd, &st, &p); - if (r < 0) - return r; - - h = p; - items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size)); - - for (n = 0; n < le64toh(h->n_items); n++) { - const char *s; - - if (last_id_set && sd_id128_equal(last_id, items[n].id)) - continue; - - assert_se(s = find_id(p, items[n].id)); - - dump_catalog_entry(f, items[n].id, s, oneline); - - last_id_set = true; - last_id = items[n].id; - } - - munmap(p, st.st_size); - - return 0; -} - -int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) { - char **item; - int r = 0; - - STRV_FOREACH(item, items) { - sd_id128_t id; - int k; - _cleanup_free_ char *msg = NULL; - - k = sd_id128_from_string(*item, &id); - if (k < 0) { - log_error_errno(k, "Failed to parse id128 '%s': %m", *item); - if (r == 0) - r = k; - continue; - } - - k = catalog_get(database, id, &msg); - if (k < 0) { - 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; - } - - dump_catalog_entry(f, id, msg, oneline); - } - - return r; -} diff --git a/src/journal/catalog.h b/src/journal/catalog.h deleted file mode 100644 index 1b1014b335..0000000000 --- a/src/journal/catalog.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-id128.h" - -#include "hashmap.h" -#include "strbuf.h" - -int catalog_import_file(Hashmap *h, const char *path); -int catalog_update(const char* database, const char* root, const char* const* dirs); -int catalog_get(const char* database, sd_id128_t id, char **data); -int catalog_list(FILE *f, const char* database, bool oneline); -int catalog_list_items(FILE *f, const char* database, bool oneline, char **items); -int catalog_file_lang(const char *filename, char **lang); -extern const char * const catalog_file_dirs[]; -extern const struct hash_ops catalog_hash_ops; diff --git a/src/journal/compress.c b/src/journal/compress.c deleted file mode 100644 index ba734b5561..0000000000 --- a/src/journal/compress.c +++ /dev/null @@ -1,683 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#ifdef HAVE_XZ -#include -#endif - -#ifdef HAVE_LZ4 -#include -#include -#endif - -#include "alloc-util.h" -#include "compress.h" -#include "fd-util.h" -#include "io-util.h" -#include "journal-def.h" -#include "macro.h" -#include "sparse-endian.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)) - -static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = { - [OBJECT_COMPRESSED_XZ] = "XZ", - [OBJECT_COMPRESSED_LZ4] = "LZ4", -}; - -DEFINE_STRING_TABLE_LOOKUP(object_compressed, int); - -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[] = { - { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt }, - { LZMA_VLI_UNKNOWN, NULL } - }; - lzma_ret ret; - size_t out_pos = 0; - - 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 - * compressed result is longer than the original */ - - if (src_size < 80) - return -ENOBUFS; - - ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL, - src, src_size, dst, &out_pos, dst_alloc_size); - if (ret != LZMA_OK) - return -ENOBUFS; - - *dst_size = out_pos; - return 0; -#else - return -EPROTONOSUPPORT; -#endif -} - -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 - * compressed result is longer than the original */ - - if (src_size < 9) - return -ENOBUFS; - - r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); - if (r <= 0) - return -ENOBUFS; - - *(le64_t*) dst = htole64(src_size); - *dst_size = r + 8; - - return 0; -#else - return -EPROTONOSUPPORT; -#endif -} - - -int decompress_blob_xz(const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { - -#ifdef HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - lzma_ret ret; - size_t space; - - assert(src); - assert(src_size > 0); - assert(dst); - assert(dst_alloc_size); - assert(dst_size); - assert(*dst_alloc_size == 0 || *dst); - - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); - if (ret != LZMA_OK) - return -ENOMEM; - - space = MIN(src_size * 2, dst_max ?: (size_t) -1); - if (!greedy_realloc(dst, dst_alloc_size, space, 1)) - return -ENOMEM; - - s.next_in = src; - s.avail_in = src_size; - - s.next_out = *dst; - s.avail_out = space; - - for (;;) { - size_t used; - - ret = lzma_code(&s, LZMA_FINISH); - - if (ret == LZMA_STREAM_END) - break; - else if (ret != LZMA_OK) - return -ENOMEM; - - if (dst_max > 0 && (space - s.avail_out) >= dst_max) - break; - else if (dst_max > 0 && space == dst_max) - return -ENOBUFS; - - used = space - s.avail_out; - space = MIN(2 * space, dst_max ?: (size_t) -1); - if (!greedy_realloc(dst, dst_alloc_size, space, 1)) - return -ENOMEM; - - s.avail_out = space - used; - s.next_out = *(uint8_t**)dst + used; - } - - *dst_size = space - s.avail_out; - return 0; -#else - return -EPROTONOSUPPORT; -#endif -} - -int decompress_blob_lz4(const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { - -#ifdef HAVE_LZ4 - char* out; - int r, size; /* LZ4 uses int for size */ - - assert(src); - assert(src_size > 0); - assert(dst); - assert(dst_alloc_size); - assert(dst_size); - assert(*dst_alloc_size == 0 || *dst); - - if (src_size <= 8) - return -EBADMSG; - - size = le64toh( *(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); - if (!out) - return -ENOMEM; - *dst = out; - *dst_alloc_size = size; - } else - out = *dst; - - r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); - if (r < 0 || r != size) - return -EBADMSG; - - *dst_size = size; - return 0; -#else - return -EPROTONOSUPPORT; -#endif -} - -int decompress_blob(int compression, - const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { - if (compression == OBJECT_COMPRESSED_XZ) - return decompress_blob_xz(src, src_size, - dst, dst_alloc_size, dst_size, dst_max); - else if (compression == OBJECT_COMPRESSED_LZ4) - return decompress_blob_lz4(src, src_size, - dst, dst_alloc_size, dst_size, dst_max); - else - return -EBADMSG; -} - - -int decompress_startswith_xz(const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra) { - -#ifdef HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - lzma_ret ret; - - /* Checks whether the decompressed blob starts with the - * mentioned prefix. The byte extra needs to follow the - * prefix */ - - assert(src); - assert(src_size > 0); - assert(buffer); - assert(buffer_size); - assert(prefix); - assert(*buffer_size == 0 || *buffer); - - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); - if (ret != LZMA_OK) - return -EBADMSG; - - if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) - return -ENOMEM; - - s.next_in = src; - s.avail_in = src_size; - - s.next_out = *buffer; - s.avail_out = *buffer_size; - - for (;;) { - ret = lzma_code(&s, LZMA_FINISH); - - if (ret != LZMA_STREAM_END && ret != LZMA_OK) - return -EBADMSG; - - if (*buffer_size - s.avail_out >= prefix_len + 1) - return memcmp(*buffer, prefix, prefix_len) == 0 && - ((const uint8_t*) *buffer)[prefix_len] == extra; - - if (ret == LZMA_STREAM_END) - return 0; - - s.avail_out += *buffer_size; - - if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1))) - return -ENOMEM; - - s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out; - } - -#else - return -EPROTONOSUPPORT; -#endif -} - -int decompress_startswith_lz4(const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra) { -#ifdef HAVE_LZ4 - /* Checks whether the decompressed blob starts with the - * mentioned prefix. The byte extra needs to follow the - * prefix */ - - int r; - size_t size; - - assert(src); - assert(src_size > 0); - assert(buffer); - assert(buffer_size); - assert(prefix); - assert(*buffer_size == 0 || *buffer); - - if (src_size <= 8) - return -EBADMSG; - - if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) - return -ENOMEM; - - r = LZ4_decompress_safe_partial((char*)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 (size >= prefix_len + 1) - return memcmp(*buffer, prefix, prefix_len) == 0 && - ((const uint8_t*) *buffer)[prefix_len] == extra; - else - return 0; - -#else - return -EPROTONOSUPPORT; -#endif -} - -int decompress_startswith(int compression, - const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra) { - if (compression == OBJECT_COMPRESSED_XZ) - return decompress_startswith_xz(src, src_size, - buffer, buffer_size, - prefix, prefix_len, - extra); - else if (compression == OBJECT_COMPRESSED_LZ4) - return decompress_startswith_lz4(src, src_size, - buffer, buffer_size, - prefix, prefix_len, - extra); - else - return -EBADMSG; -} - -int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { -#ifdef HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - lzma_ret ret; - uint8_t buf[BUFSIZ], out[BUFSIZ]; - lzma_action action = LZMA_RUN; - - assert(fdf >= 0); - assert(fdt >= 0); - - ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); - if (ret != LZMA_OK) { - log_error("Failed to initialize XZ encoder: code %u", ret); - return -EINVAL; - } - - for (;;) { - if (s.avail_in == 0 && action == LZMA_RUN) { - size_t m = sizeof(buf); - ssize_t n; - - if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes) - m = (size_t) max_bytes; - - n = read(fdf, buf, m); - if (n < 0) - return -errno; - if (n == 0) - action = LZMA_FINISH; - else { - s.next_in = buf; - s.avail_in = n; - - if (max_bytes != (uint64_t) -1) { - assert(max_bytes >= (uint64_t) n); - max_bytes -= n; - } - } - } - - if (s.avail_out == 0) { - s.next_out = out; - s.avail_out = sizeof(out); - } - - ret = lzma_code(&s, action); - if (ret != LZMA_OK && ret != LZMA_STREAM_END) { - log_error("Compression failed: code %u", ret); - return -EBADMSG; - } - - if (s.avail_out == 0 || ret == LZMA_STREAM_END) { - ssize_t n, k; - - n = sizeof(out) - s.avail_out; - - k = loop_write(fdt, out, n, false); - if (k < 0) - return k; - - if (ret == LZMA_STREAM_END) { - log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", - s.total_in, s.total_out, - (double) s.total_out / s.total_in * 100); - - return 0; - } - } - } -#else - return -EPROTONOSUPPORT; -#endif -} - -#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, - }; - - c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); - if (LZ4F_isError(c)) - return -ENOMEM; - - if (fstat(fdf, &st) < 0) - return log_debug_errno(errno, "fstat() failed: %m"); - - 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; - - n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences); - if (LZ4F_isError(n)) - return -EINVAL; - - src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0); - if (src == MAP_FAILED) - return -errno; - - log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n); - - while (total_in < (size_t) st.st_size) { - ssize_t k; - - 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; - } - - total_in += k; - offset += n; - total_out += n; - - if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { - log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes); - return -EFBIG; - } - - if (size - offset < frame_size + 4) { - k = loop_write(fdt, buf, offset, false); - if (k < 0) { - r = k; - goto cleanup; - } - offset = 0; - } - } - - n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options); - if (LZ4F_isError(n)) { - r = -ENOTRECOVERABLE; - goto cleanup; - } - - 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); - cleanup: - munmap(src, st.st_size); - return r; -#else - return -EPROTONOSUPPORT; -#endif -} - -int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { - -#ifdef HAVE_XZ - _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; - lzma_ret ret; - - uint8_t buf[BUFSIZ], out[BUFSIZ]; - lzma_action action = LZMA_RUN; - - assert(fdf >= 0); - assert(fdt >= 0); - - ret = lzma_stream_decoder(&s, UINT64_MAX, 0); - if (ret != LZMA_OK) { - log_debug("Failed to initialize XZ decoder: code %u", ret); - return -ENOMEM; - } - - for (;;) { - if (s.avail_in == 0 && action == LZMA_RUN) { - ssize_t n; - - n = read(fdf, buf, sizeof(buf)); - if (n < 0) - return -errno; - if (n == 0) - action = LZMA_FINISH; - else { - s.next_in = buf; - s.avail_in = n; - } - } - - if (s.avail_out == 0) { - s.next_out = out; - s.avail_out = sizeof(out); - } - - ret = lzma_code(&s, action); - if (ret != LZMA_OK && ret != LZMA_STREAM_END) { - log_debug("Decompression failed: code %u", ret); - return -EBADMSG; - } - - if (s.avail_out == 0 || ret == LZMA_STREAM_END) { - ssize_t n, k; - - n = sizeof(out) - s.avail_out; - - if (max_bytes != (uint64_t) -1) { - if (max_bytes < (uint64_t) n) - return -EFBIG; - - max_bytes -= n; - } - - k = loop_write(fdt, out, n, false); - if (k < 0) - return k; - - if (ret == LZMA_STREAM_END) { - log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", - s.total_in, s.total_out, - (double) s.total_out / s.total_in * 100); - - return 0; - } - } - } -#else - log_debug("Cannot decompress file. Compiled without XZ support."); - return -EPROTONOSUPPORT; -#endif -} - -int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { -#ifdef HAVE_LZ4 - 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; - - if (fstat(in, &st) < 0) - return log_debug_errno(errno, "fstat() failed: %m"); - - buf = malloc(LZ4_BUFSIZE); - if (!buf) - return -ENOMEM; - - src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0); - if (src == MAP_FAILED) - return -errno; - - while (total_in < (size_t) st.st_size) { - size_t produced = LZ4_BUFSIZE; - size_t used = st.st_size - total_in; - - c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); - if (LZ4F_isError(c)) { - r = -EBADMSG; - goto cleanup; - } - - total_in += used; - total_out += produced; - - if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { - log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes); - r = -EFBIG; - goto cleanup; - } - - r = loop_write(out, buf, produced, false); - if (r < 0) - goto cleanup; - } - - log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", - total_in, total_out, - (double) total_out / total_in * 100); - cleanup: - munmap(src, st.st_size); - return r; -#else - log_debug("Cannot decompress file. Compiled without LZ4 support."); - return -EPROTONOSUPPORT; -#endif -} - -int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { - - if (endswith(filename, ".lz4")) - return decompress_stream_lz4(fdf, fdt, max_bytes); - else if (endswith(filename, ".xz")) - return decompress_stream_xz(fdf, fdt, max_bytes); - else - return -EPROTONOSUPPORT; -} diff --git a/src/journal/compress.h b/src/journal/compress.h deleted file mode 100644 index c138099d9a..0000000000 --- a/src/journal/compress.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "journal-def.h" - -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_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_alloc_size, size_t *dst_size) { - int r; -#ifdef HAVE_LZ4 - 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_alloc_size, dst_size); - if (r == 0) - return OBJECT_COMPRESSED_XZ; -#endif - return r; -} - -int decompress_blob_xz(const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); -int decompress_blob_lz4(const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); -int decompress_blob(int compression, - const void *src, uint64_t src_size, - void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); - -int decompress_startswith_xz(const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra); -int decompress_startswith_lz4(const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra); -int decompress_startswith(int compression, - const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra); - -int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes); -int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes); - -int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); -int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); - -#ifdef HAVE_LZ4 -# define compress_stream compress_stream_lz4 -# define COMPRESSED_EXT ".lz4" -#else -# define compress_stream compress_stream_xz -# define COMPRESSED_EXT ".xz" -#endif - -int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes); diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c deleted file mode 100644 index 612b10f3a9..0000000000 --- a/src/journal/fsprg.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * fsprg v0.1 - (seekable) forward-secure pseudorandom generator - * Copyright (C) 2012 B. Poettering - * Contact: fsprg@point-at-infinity.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -/* - * See "Practical Secure Logging: Seekable Sequential Key Generators" - * by G. A. Marson, B. Poettering for details: - * - * http://eprint.iacr.org/2013/397 - */ - -#include -#include - -#include "fsprg.h" -#include "gcrypt-util.h" - -#define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384)) -#define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar)); - -#define RND_HASH GCRY_MD_SHA256 -#define RND_GEN_P 0x01 -#define RND_GEN_Q 0x02 -#define RND_GEN_X 0x03 - -/******************************************************************************/ - -static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) { - unsigned len; - size_t nwritten; - - assert(gcry_mpi_cmp_ui(x, 0) >= 0); - len = (gcry_mpi_get_nbits(x) + 7) / 8; - assert(len <= buflen); - memzero(buf, buflen); - gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x); - assert(nwritten == len); -} - -static gcry_mpi_t mpi_import(const void *buf, size_t buflen) { - gcry_mpi_t h; - unsigned len; - - assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0); - len = (gcry_mpi_get_nbits(h) + 7) / 8; - assert(len <= buflen); - assert(gcry_mpi_cmp_ui(h, 0) >= 0); - - return h; -} - -static void uint64_export(void *buf, size_t buflen, uint64_t x) { - assert(buflen == 8); - ((uint8_t*) buf)[0] = (x >> 56) & 0xff; - ((uint8_t*) buf)[1] = (x >> 48) & 0xff; - ((uint8_t*) buf)[2] = (x >> 40) & 0xff; - ((uint8_t*) buf)[3] = (x >> 32) & 0xff; - ((uint8_t*) buf)[4] = (x >> 24) & 0xff; - ((uint8_t*) buf)[5] = (x >> 16) & 0xff; - ((uint8_t*) buf)[6] = (x >> 8) & 0xff; - ((uint8_t*) buf)[7] = (x >> 0) & 0xff; -} - -_pure_ static uint64_t uint64_import(const void *buf, size_t buflen) { - assert(buflen == 8); - return - (uint64_t)(((uint8_t*) buf)[0]) << 56 | - (uint64_t)(((uint8_t*) buf)[1]) << 48 | - (uint64_t)(((uint8_t*) buf)[2]) << 40 | - (uint64_t)(((uint8_t*) buf)[3]) << 32 | - (uint64_t)(((uint8_t*) buf)[4]) << 24 | - (uint64_t)(((uint8_t*) buf)[5]) << 16 | - (uint64_t)(((uint8_t*) buf)[6]) << 8 | - (uint64_t)(((uint8_t*) buf)[7]) << 0; -} - -/* deterministically generate from seed/idx a string of buflen pseudorandom bytes */ -static void det_randomize(void *buf, size_t buflen, const void *seed, size_t seedlen, uint32_t idx) { - gcry_md_hd_t hd, hd2; - size_t olen, cpylen; - uint32_t ctr; - - olen = gcry_md_get_algo_dlen(RND_HASH); - gcry_md_open(&hd, RND_HASH, 0); - gcry_md_write(hd, seed, seedlen); - gcry_md_putc(hd, (idx >> 24) & 0xff); - gcry_md_putc(hd, (idx >> 16) & 0xff); - gcry_md_putc(hd, (idx >> 8) & 0xff); - gcry_md_putc(hd, (idx >> 0) & 0xff); - - for (ctr = 0; buflen; ctr++) { - gcry_md_copy(&hd2, hd); - gcry_md_putc(hd2, (ctr >> 24) & 0xff); - gcry_md_putc(hd2, (ctr >> 16) & 0xff); - gcry_md_putc(hd2, (ctr >> 8) & 0xff); - gcry_md_putc(hd2, (ctr >> 0) & 0xff); - gcry_md_final(hd2); - cpylen = (buflen < olen) ? buflen : olen; - memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen); - gcry_md_close(hd2); - buf += cpylen; - buflen -= cpylen; - } - gcry_md_close(hd); -} - -/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */ -static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint32_t idx) { - size_t buflen = bits / 8; - uint8_t buf[buflen]; - gcry_mpi_t p; - - assert(bits % 8 == 0); - assert(buflen > 0); - - det_randomize(buf, buflen, seed, seedlen, idx); - buf[0] |= 0xc0; /* set upper two bits, so that n=pq has maximum size */ - buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */ - - p = mpi_import(buf, buflen); - while (gcry_prime_check(p, 0)) - gcry_mpi_add_ui(p, p, 4); - - return p; -} - -/* deterministically generate from seed/idx a quadratic residue (mod n) */ -static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen, uint32_t idx, unsigned secpar) { - size_t buflen = secpar / 8; - uint8_t buf[buflen]; - gcry_mpi_t x; - - det_randomize(buf, buflen, seed, seedlen, idx); - buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */ - x = mpi_import(buf, buflen); - assert(gcry_mpi_cmp(x, n) < 0); - gcry_mpi_mulm(x, x, x, n); - return x; -} - -/* compute 2^m (mod phi(p)), for a prime p */ -static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) { - gcry_mpi_t phi, r; - int n; - - phi = gcry_mpi_new(0); - gcry_mpi_sub_ui(phi, p, 1); - - /* count number of used bits in m */ - for (n = 0; (1ULL << n) <= m; n++) - ; - - r = gcry_mpi_new(0); - gcry_mpi_set_ui(r, 1); - while (n) { /* square and multiply algorithm for fast exponentiation */ - n--; - gcry_mpi_mulm(r, r, r, phi); - if (m & ((uint64_t)1 << n)) { - gcry_mpi_add(r, r, r); - if (gcry_mpi_cmp(r, phi) >= 0) - gcry_mpi_sub(r, r, phi); - } - } - - gcry_mpi_release(phi); - return r; -} - -/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */ -static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) { - *xp = gcry_mpi_new(0); - *xq = gcry_mpi_new(0); - gcry_mpi_mod(*xp, x, p); - gcry_mpi_mod(*xq, x, q); -} - -/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */ -static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) { - gcry_mpi_t a, u; - - a = gcry_mpi_new(0); - u = gcry_mpi_new(0); - *x = gcry_mpi_new(0); - gcry_mpi_subm(a, xq, xp, q); - gcry_mpi_invm(u, p, q); - gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */ - gcry_mpi_mul(*x, p, a); - gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */ - gcry_mpi_release(a); - gcry_mpi_release(u); -} - -/******************************************************************************/ - -size_t FSPRG_mskinbytes(unsigned _secpar) { - VALIDATE_SECPAR(_secpar); - return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */ -} - -size_t FSPRG_mpkinbytes(unsigned _secpar) { - VALIDATE_SECPAR(_secpar); - return 2 + _secpar / 8; /* to store header,n */ -} - -size_t FSPRG_stateinbytes(unsigned _secpar) { - VALIDATE_SECPAR(_secpar); - return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */ -} - -static void store_secpar(void *buf, uint16_t secpar) { - secpar = secpar / 16 - 1; - ((uint8_t*) buf)[0] = (secpar >> 8) & 0xff; - ((uint8_t*) buf)[1] = (secpar >> 0) & 0xff; -} - -static uint16_t read_secpar(const void *buf) { - uint16_t secpar; - secpar = - (uint16_t)(((uint8_t*) buf)[0]) << 8 | - (uint16_t)(((uint8_t*) buf)[1]) << 0; - return 16 * (secpar + 1); -} - -void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) { - uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN]; - gcry_mpi_t n, p, q; - uint16_t secpar; - - VALIDATE_SECPAR(_secpar); - secpar = _secpar; - - initialize_libgcrypt(false); - - if (!seed) { - gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM); - seed = iseed; - seedlen = FSPRG_RECOMMENDED_SEEDLEN; - } - - p = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_P); - q = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_Q); - - if (msk) { - store_secpar(msk + 0, secpar); - mpi_export(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8, p); - mpi_export(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8, q); - } - - if (mpk) { - n = gcry_mpi_new(0); - gcry_mpi_mul(n, p, q); - assert(gcry_mpi_get_nbits(n) == secpar); - - store_secpar(mpk + 0, secpar); - mpi_export(mpk + 2, secpar / 8, n); - - gcry_mpi_release(n); - } - - gcry_mpi_release(p); - gcry_mpi_release(q); -} - -void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) { - gcry_mpi_t n, x; - uint16_t secpar; - - initialize_libgcrypt(false); - - secpar = read_secpar(mpk + 0); - n = mpi_import(mpk + 2, secpar / 8); - x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); - - memcpy(state, mpk, 2 + secpar / 8); - mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); - memzero(state + 2 + 2 * secpar / 8, 8); - - gcry_mpi_release(n); - gcry_mpi_release(x); -} - -void FSPRG_Evolve(void *state) { - gcry_mpi_t n, x; - uint16_t secpar; - uint64_t epoch; - - initialize_libgcrypt(false); - - secpar = read_secpar(state + 0); - n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8); - x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8); - epoch = uint64_import(state + 2 + 2 * secpar / 8, 8); - - gcry_mpi_mulm(x, x, x, n); - epoch++; - - mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); - uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); - - gcry_mpi_release(n); - gcry_mpi_release(x); -} - -uint64_t FSPRG_GetEpoch(const void *state) { - uint16_t secpar; - secpar = read_secpar(state + 0); - return uint64_import(state + 2 + 2 * secpar / 8, 8); -} - -void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) { - gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm; - uint16_t secpar; - - initialize_libgcrypt(false); - - secpar = read_secpar(msk + 0); - p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8); - q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8); - - n = gcry_mpi_new(0); - gcry_mpi_mul(n, p, q); - - x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); - CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */ - - kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */ - kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */ - - gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */ - gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */ - - CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */ - - store_secpar(state + 0, secpar); - mpi_export(state + 2 + 0 * secpar / 8, secpar / 8, n); - mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm); - uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); - - gcry_mpi_release(p); - gcry_mpi_release(q); - gcry_mpi_release(n); - gcry_mpi_release(x); - gcry_mpi_release(xp); - gcry_mpi_release(xq); - gcry_mpi_release(kp); - gcry_mpi_release(kq); - gcry_mpi_release(xm); -} - -void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) { - uint16_t secpar; - - initialize_libgcrypt(false); - - secpar = read_secpar(state + 0); - det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx); -} diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h deleted file mode 100644 index 829b56e240..0000000000 --- a/src/journal/fsprg.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef __fsprgh__ -#define __fsprgh__ - -/* - * fsprg v0.1 - (seekable) forward-secure pseudorandom generator - * Copyright (C) 2012 B. Poettering - * Contact: fsprg@point-at-infinity.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include - -#include "macro.h" -#include "util.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define FSPRG_RECOMMENDED_SECPAR 1536 -#define FSPRG_RECOMMENDED_SEEDLEN (96/8) - -size_t FSPRG_mskinbytes(unsigned secpar) _const_; -size_t FSPRG_mpkinbytes(unsigned secpar) _const_; -size_t FSPRG_stateinbytes(unsigned secpar) _const_; - -/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */ -void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar); - -/* Initialize state deterministically in dependence on seed. */ -/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use - the same seed for both GenMK and GenState0. -*/ -void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen); - -void FSPRG_Evolve(void *state); - -uint64_t FSPRG_GetEpoch(const void *state) _pure_; - -/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */ -void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen); - -void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c deleted file mode 100644 index d8af113d3f..0000000000 --- a/src/journal/journal-authenticate.c +++ /dev/null @@ -1,551 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fd-util.h" -#include "fsprg.h" -#include "gcrypt-util.h" -#include "hexdecoct.h" -#include "journal-authenticate.h" -#include "journal-def.h" -#include "journal-file.h" - -static uint64_t journal_file_tag_seqnum(JournalFile *f) { - uint64_t r; - - assert(f); - - r = le64toh(f->header->n_tags) + 1; - f->header->n_tags = htole64(r); - - return r; -} - -int journal_file_append_tag(JournalFile *f) { - Object *o; - uint64_t p; - int r; - - assert(f); - - if (!f->seal) - return 0; - - if (!f->hmac_running) - return 0; - - assert(f->hmac); - - r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); - if (r < 0) - return r; - - o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); - o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state)); - - log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"", - le64toh(o->tag.seqnum), - FSPRG_GetEpoch(f->fsprg_state)); - - /* Add the tag object itself, so that we can protect its - * header. This will exclude the actual hash value in it */ - r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p); - if (r < 0) - return r; - - /* Get the HMAC tag and store it in the object */ - memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH); - f->hmac_running = false; - - return 0; -} - -int journal_file_hmac_start(JournalFile *f) { - uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ - assert(f); - - if (!f->seal) - return 0; - - if (f->hmac_running) - return 0; - - /* Prepare HMAC for next cycle */ - gcry_md_reset(f->hmac); - FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0); - gcry_md_setkey(f->hmac, key, sizeof(key)); - - f->hmac_running = true; - - return 0; -} - -static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { - uint64_t t; - - assert(f); - assert(epoch); - assert(f->seal); - - if (f->fss_start_usec == 0 || - f->fss_interval_usec == 0) - return -EOPNOTSUPP; - - if (realtime < f->fss_start_usec) - return -ESTALE; - - t = realtime - f->fss_start_usec; - t = t / f->fss_interval_usec; - - *epoch = t; - return 0; -} - -static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; - assert(f); - - if (!f->seal) - return 0; - - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; - - epoch = FSPRG_GetEpoch(f->fsprg_state); - if (epoch > goal) - return -ESTALE; - - return epoch != goal; -} - -int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; - - assert(f); - - if (!f->seal) - return 0; - - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; - - epoch = FSPRG_GetEpoch(f->fsprg_state); - if (epoch < goal) - log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal); - - for (;;) { - if (epoch > goal) - return -ESTALE; - if (epoch == goal) - return 0; - - FSPRG_Evolve(f->fsprg_state); - epoch = FSPRG_GetEpoch(f->fsprg_state); - } -} - -int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { - void *msk; - uint64_t epoch; - - assert(f); - - if (!f->seal) - return 0; - - assert(f->fsprg_seed); - - if (f->fsprg_state) { - /* Cheaper... */ - - epoch = FSPRG_GetEpoch(f->fsprg_state); - if (goal == epoch) - return 0; - - if (goal == epoch+1) { - FSPRG_Evolve(f->fsprg_state); - return 0; - } - } else { - f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); - f->fsprg_state = malloc(f->fsprg_state_size); - - if (!f->fsprg_state) - return -ENOMEM; - } - - log_debug("Seeking FSPRG key to %"PRIu64".", goal); - - msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)); - FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR); - FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size); - return 0; -} - -int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { - int r; - - assert(f); - - if (!f->seal) - return 0; - - if (realtime <= 0) - realtime = now(CLOCK_REALTIME); - - r = journal_file_fsprg_need_evolve(f, realtime); - if (r <= 0) - return 0; - - r = journal_file_append_tag(f); - if (r < 0) - return r; - - r = journal_file_fsprg_evolve(f, realtime); - if (r < 0) - return r; - - return 0; -} - -int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { - int r; - - assert(f); - - if (!f->seal) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - if (!o) { - r = journal_file_move_to_object(f, type, p, &o); - if (r < 0) - return r; - } else { - if (type > OBJECT_UNUSED && o->object.type != type) - return -EBADMSG; - } - - gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); - - switch (o->object.type) { - - case OBJECT_DATA: - /* All but hash and payload are mutable */ - gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); - gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); - break; - - case OBJECT_FIELD: - /* Same here */ - gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash)); - gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload)); - break; - - case OBJECT_ENTRY: - /* All */ - gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); - break; - - case OBJECT_FIELD_HASH_TABLE: - case OBJECT_DATA_HASH_TABLE: - case OBJECT_ENTRY_ARRAY: - /* Nothing: everything is mutable */ - break; - - case OBJECT_TAG: - /* All but the tag itself */ - gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); - gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); - break; - default: - return -EINVAL; - } - - return 0; -} - -int journal_file_hmac_put_header(JournalFile *f) { - int r; - - assert(f); - - if (!f->seal) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - /* All but state+reserved, boot_id, arena_size, - * tail_object_offset, n_objects, n_entries, - * tail_entry_seqnum, head_entry_seqnum, entry_array_offset, - * head_entry_realtime, tail_entry_realtime, - * tail_entry_monotonic, n_data, n_fields, n_tags, - * n_entry_arrays. */ - - gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); - gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); - gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); - gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); - - return 0; -} - -int journal_file_fss_load(JournalFile *f) { - int r, fd = -1; - char *p = NULL; - struct stat st; - FSSHeader *m = NULL; - sd_id128_t machine; - - assert(f); - - if (!f->seal) - return 0; - - r = sd_id128_get_machine(&machine); - if (r < 0) - return r; - - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", - SD_ID128_FORMAT_VAL(machine)) < 0) - return -ENOMEM; - - fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) { - if (errno != ENOENT) - log_error_errno(errno, "Failed to open %s: %m", p); - - r = -errno; - goto finish; - } - - if (fstat(fd, &st) < 0) { - r = -errno; - goto finish; - } - - if (st.st_size < (off_t) sizeof(FSSHeader)) { - r = -ENODATA; - goto finish; - } - - m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); - if (m == MAP_FAILED) { - m = NULL; - r = -errno; - goto finish; - } - - if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) { - r = -EBADMSG; - goto finish; - } - - if (m->incompatible_flags != 0) { - r = -EPROTONOSUPPORT; - goto finish; - } - - if (le64toh(m->header_size) < sizeof(FSSHeader)) { - r = -EBADMSG; - goto finish; - } - - if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) { - r = -EBADMSG; - goto finish; - } - - f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size); - if ((uint64_t) st.st_size < f->fss_file_size) { - r = -ENODATA; - goto finish; - } - - if (!sd_id128_equal(machine, m->machine_id)) { - r = -EHOSTDOWN; - goto finish; - } - - if (le64toh(m->start_usec) <= 0 || - le64toh(m->interval_usec) <= 0) { - r = -EBADMSG; - goto finish; - } - - f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (f->fss_file == MAP_FAILED) { - f->fss_file = NULL; - r = -errno; - goto finish; - } - - f->fss_start_usec = le64toh(f->fss_file->start_usec); - f->fss_interval_usec = le64toh(f->fss_file->interval_usec); - - f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size); - f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size); - - r = 0; - -finish: - if (m) - munmap(m, PAGE_ALIGN(sizeof(FSSHeader))); - - safe_close(fd); - free(p); - - return r; -} - -int journal_file_hmac_setup(JournalFile *f) { - gcry_error_t e; - - if (!f->seal) - return 0; - - initialize_libgcrypt(true); - - e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); - if (e != 0) - return -EOPNOTSUPP; - - return 0; -} - -int journal_file_append_first_tag(JournalFile *f) { - int r; - uint64_t p; - - if (!f->seal) - return 0; - - log_debug("Calculating first tag..."); - - r = journal_file_hmac_put_header(f); - if (r < 0) - return r; - - p = le64toh(f->header->field_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); - if (r < 0) - return r; - - p = le64toh(f->header->data_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); - if (r < 0) - return r; - - r = journal_file_append_tag(f); - if (r < 0) - return r; - - return 0; -} - -int journal_file_parse_verification_key(JournalFile *f, const char *key) { - uint8_t *seed; - size_t seed_size, c; - const char *k; - int r; - unsigned long long start, interval; - - seed_size = FSPRG_RECOMMENDED_SEEDLEN; - seed = malloc(seed_size); - if (!seed) - return -ENOMEM; - - k = key; - for (c = 0; c < seed_size; c++) { - int x, y; - - while (*k == '-') - k++; - - x = unhexchar(*k); - if (x < 0) { - free(seed); - return -EINVAL; - } - k++; - y = unhexchar(*k); - if (y < 0) { - free(seed); - return -EINVAL; - } - k++; - - seed[c] = (uint8_t) (x * 16 + y); - } - - if (*k != '/') { - free(seed); - return -EINVAL; - } - k++; - - r = sscanf(k, "%llx-%llx", &start, &interval); - if (r != 2) { - free(seed); - return -EINVAL; - } - - f->fsprg_seed = seed; - f->fsprg_seed_size = seed_size; - - f->fss_start_usec = start * interval; - f->fss_interval_usec = interval; - - return 0; -} - -bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) { - uint64_t epoch; - - assert(f); - assert(u); - - if (!f->seal) - return false; - - epoch = FSPRG_GetEpoch(f->fsprg_state); - - *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec); - - return true; -} diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h deleted file mode 100644 index 6c87319ede..0000000000 --- a/src/journal/journal-authenticate.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "journal-file.h" - -int journal_file_append_tag(JournalFile *f); -int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); -int journal_file_append_first_tag(JournalFile *f); - -int journal_file_hmac_setup(JournalFile *f); -int journal_file_hmac_start(JournalFile *f); -int journal_file_hmac_put_header(JournalFile *f); -int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); - -int journal_file_fss_load(JournalFile *f); -int journal_file_parse_verification_key(JournalFile *f, const char *key); - -int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime); -int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch); - -bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u); diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h deleted file mode 100644 index 67edb43960..0000000000 --- a/src/journal/journal-def.h +++ /dev/null @@ -1,237 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-id128.h" - -#include "macro.h" -#include "sparse-endian.h" - -/* - * If you change this file you probably should also change its documentation: - * - * http://www.freedesktop.org/wiki/Software/systemd/journal-files - * - */ - -typedef struct Header Header; - -typedef struct ObjectHeader ObjectHeader; -typedef union Object Object; - -typedef struct DataObject DataObject; -typedef struct FieldObject FieldObject; -typedef struct EntryObject EntryObject; -typedef struct HashTableObject HashTableObject; -typedef struct EntryArrayObject EntryArrayObject; -typedef struct TagObject TagObject; - -typedef struct EntryItem EntryItem; -typedef struct HashItem HashItem; - -typedef struct FSSHeader FSSHeader; - -/* Object types */ -typedef enum ObjectType { - OBJECT_UNUSED, /* also serves as "any type" or "additional context" */ - OBJECT_DATA, - OBJECT_FIELD, - OBJECT_ENTRY, - OBJECT_DATA_HASH_TABLE, - OBJECT_FIELD_HASH_TABLE, - OBJECT_ENTRY_ARRAY, - OBJECT_TAG, - _OBJECT_TYPE_MAX -} ObjectType; - -/* Object flags */ -enum { - OBJECT_COMPRESSED_XZ = 1 << 0, - OBJECT_COMPRESSED_LZ4 = 1 << 1, - _OBJECT_COMPRESSED_MAX -}; - -#define OBJECT_COMPRESSION_MASK (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4) - -struct ObjectHeader { - uint8_t type; - uint8_t flags; - uint8_t reserved[6]; - le64_t size; - uint8_t payload[]; -} _packed_; - -struct DataObject { - ObjectHeader object; - le64_t hash; - le64_t next_hash_offset; - le64_t next_field_offset; - le64_t entry_offset; /* the first array entry we store inline */ - le64_t entry_array_offset; - le64_t n_entries; - uint8_t payload[]; -} _packed_; - -struct FieldObject { - ObjectHeader object; - le64_t hash; - le64_t next_hash_offset; - le64_t head_data_offset; - uint8_t payload[]; -} _packed_; - -struct EntryItem { - le64_t object_offset; - le64_t hash; -} _packed_; - -struct EntryObject { - ObjectHeader object; - le64_t seqnum; - le64_t realtime; - le64_t monotonic; - sd_id128_t boot_id; - le64_t xor_hash; - EntryItem items[]; -} _packed_; - -struct HashItem { - le64_t head_hash_offset; - le64_t tail_hash_offset; -} _packed_; - -struct HashTableObject { - ObjectHeader object; - HashItem items[]; -} _packed_; - -struct EntryArrayObject { - ObjectHeader object; - le64_t next_entry_array_offset; - le64_t items[]; -} _packed_; - -#define TAG_LENGTH (256/8) - -struct TagObject { - ObjectHeader object; - le64_t seqnum; - le64_t epoch; - uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */ -} _packed_; - -union Object { - ObjectHeader object; - DataObject data; - FieldObject field; - EntryObject entry; - HashTableObject hash_table; - EntryArrayObject entry_array; - TagObject tag; -}; - -enum { - STATE_OFFLINE = 0, - STATE_ONLINE = 1, - STATE_ARCHIVED = 2, - _STATE_MAX -}; - -/* Header flags */ -enum { - HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, - HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, -}; - -#define HEADER_INCOMPATIBLE_ANY (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4) - -#if defined(HAVE_XZ) && defined(HAVE_LZ4) -# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_ANY -#elif defined(HAVE_XZ) -# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_XZ -#elif defined(HAVE_LZ4) -# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_LZ4 -#else -# define HEADER_INCOMPATIBLE_SUPPORTED 0 -#endif - -enum { - HEADER_COMPATIBLE_SEALED = 1 -}; - -#define HEADER_COMPATIBLE_ANY HEADER_COMPATIBLE_SEALED -#ifdef HAVE_GCRYPT -# define HEADER_COMPATIBLE_SUPPORTED HEADER_COMPATIBLE_SEALED -#else -# define HEADER_COMPATIBLE_SUPPORTED 0 -#endif - -#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) - -struct Header { - uint8_t signature[8]; /* "LPKSHHRH" */ - le32_t compatible_flags; - le32_t incompatible_flags; - uint8_t state; - uint8_t reserved[7]; - sd_id128_t file_id; - sd_id128_t machine_id; - sd_id128_t boot_id; /* last writer */ - sd_id128_t seqnum_id; - le64_t header_size; - le64_t arena_size; - le64_t data_hash_table_offset; - le64_t data_hash_table_size; - le64_t field_hash_table_offset; - le64_t field_hash_table_size; - le64_t tail_object_offset; - le64_t n_objects; - le64_t n_entries; - le64_t tail_entry_seqnum; - le64_t head_entry_seqnum; - le64_t entry_array_offset; - le64_t head_entry_realtime; - le64_t tail_entry_realtime; - le64_t tail_entry_monotonic; - /* Added in 187 */ - le64_t n_data; - le64_t n_fields; - /* Added in 189 */ - le64_t n_tags; - le64_t n_entry_arrays; - - /* Size: 240 */ -} _packed_; - -#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) - -struct FSSHeader { - uint8_t signature[8]; /* "KSHHRHLP" */ - le32_t compatible_flags; - le32_t incompatible_flags; - sd_id128_t machine_id; - sd_id128_t boot_id; /* last writer */ - le64_t header_size; - le64_t start_usec; - le64_t interval_usec; - le16_t fsprg_secpar; - le16_t reserved[3]; - le64_t fsprg_state_size; -} _packed_; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c deleted file mode 100644 index 7504326bff..0000000000 --- a/src/journal/journal-file.c +++ /dev/null @@ -1,3616 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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 "lookup3.h" -#include "parse-util.h" -#include "path-util.h" -#include "random-util.h" -#include "sd-event.h" -#include "set.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)) - -#define COMPRESSION_SIZE_THRESHOLD (512ULL) - -/* This is the minimum journal file size */ -#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 */ - -/* This is the upper bound if we deduce the keep_free value from the - * file system size */ -#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ - -/* This is the keep_free value when we can't determine the system - * size */ -#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */ - -/* 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)) - -/* How many entries to keep in the entry array chain cache at max */ -#define CHAIN_CACHE_MAX 20 - -/* How much to increase the journal file size at once each time we allocate something new. */ -#define FILE_SIZE_INCREASE (8ULL*1024ULL*1024ULL) /* 8MB */ - -/* Reread fstat() of the file for detecting deletions at least this often */ -#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC) - -/* The mmap context to use for the header we pick as one above the last defined typed */ -#define CONTEXT_HEADER _OBJECT_TYPE_MAX - -/* This may be called from a separate thread to prevent blocking the caller for the duration of fsync(). - * As a result we use atomic operations on f->offline_state for inter-thread communications with - * journal_file_set_offline() and journal_file_set_online(). */ -static void journal_file_set_offline_internal(JournalFile *f) { - assert(f); - assert(f->fd >= 0); - assert(f->header); - - for (;;) { - switch (f->offline_state) { - case OFFLINE_CANCEL: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_DONE)) - continue; - return; - - case OFFLINE_AGAIN_FROM_SYNCING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING)) - continue; - break; - - case OFFLINE_AGAIN_FROM_OFFLINING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING)) - continue; - break; - - case OFFLINE_SYNCING: - (void) fsync(f->fd); - - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING)) - continue; - - f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE; - (void) fsync(f->fd); - break; - - case OFFLINE_OFFLINING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE)) - continue; - /* fall through */ - - case OFFLINE_DONE: - return; - - case OFFLINE_JOINED: - log_debug("OFFLINE_JOINED unexpected offline state for journal_file_set_offline_internal()"); - return; - } - } -} - -static void * journal_file_set_offline_thread(void *arg) { - JournalFile *f = arg; - - journal_file_set_offline_internal(f); - - return NULL; -} - -static int journal_file_set_offline_thread_join(JournalFile *f) { - int r; - - assert(f); - - if (f->offline_state == OFFLINE_JOINED) - return 0; - - r = pthread_join(f->offline_thread, NULL); - if (r) - return -r; - - f->offline_state = OFFLINE_JOINED; - - if (mmap_cache_got_sigbus(f->mmap, f->fd)) - return -EIO; - - return 0; -} - -/* Trigger a restart if the offline thread is mid-flight in a restartable state. */ -static bool journal_file_set_offline_try_restart(JournalFile *f) { - for (;;) { - switch (f->offline_state) { - case OFFLINE_AGAIN_FROM_SYNCING: - case OFFLINE_AGAIN_FROM_OFFLINING: - return true; - - case OFFLINE_CANCEL: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING)) - continue; - return true; - - case OFFLINE_SYNCING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING)) - continue; - return true; - - case OFFLINE_OFFLINING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING)) - continue; - return true; - - default: - return false; - } - } -} - -/* Sets a journal offline. - * - * If wait is false then an offline is dispatched in a separate thread for a - * subsequent journal_file_set_offline() or journal_file_set_online() of the - * same journal to synchronize with. - * - * If wait is true, then either an existing offline thread will be restarted - * and joined, or if none exists the offline is simply performed in this - * context without involving another thread. - */ -int journal_file_set_offline(JournalFile *f, bool wait) { - bool restarted; - int r; - - assert(f); - - if (!f->writable) - return -EPERM; - - if (!(f->fd >= 0 && f->header)) - return -EINVAL; - - /* An offlining journal is implicitly online and may modify f->header->state, - * we must also join any potentially lingering offline thread when not online. */ - if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) - return journal_file_set_offline_thread_join(f); - - /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ - restarted = journal_file_set_offline_try_restart(f); - if ((restarted && wait) || !restarted) { - r = journal_file_set_offline_thread_join(f); - if (r < 0) - return r; - } - - if (restarted) - return 0; - - /* Initiate a new offline. */ - f->offline_state = OFFLINE_SYNCING; - - if (wait) /* Without using a thread if waiting. */ - journal_file_set_offline_internal(f); - else { - r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); - if (r > 0) { - f->offline_state = OFFLINE_JOINED; - return -r; - } - } - - return 0; -} - -static int journal_file_set_online(JournalFile *f) { - bool joined = false; - - assert(f); - - if (!f->writable) - return -EPERM; - - if (!(f->fd >= 0 && f->header)) - return -EINVAL; - - while (!joined) { - switch (f->offline_state) { - case OFFLINE_JOINED: - /* No offline thread, no need to wait. */ - joined = true; - break; - - case OFFLINE_SYNCING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL)) - continue; - /* Canceled syncing prior to offlining, no need to wait. */ - break; - - case OFFLINE_AGAIN_FROM_SYNCING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL)) - continue; - /* Canceled restart from syncing, no need to wait. */ - break; - - case OFFLINE_AGAIN_FROM_OFFLINING: - if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL)) - continue; - /* Canceled restart from offlining, must wait for offlining to complete however. */ - - /* fall through to wait */ - default: { - int r; - - r = journal_file_set_offline_thread_join(f); - if (r < 0) - return r; - - joined = true; - break; - } - } - } - - if (mmap_cache_got_sigbus(f->mmap, f->fd)) - return -EIO; - - switch (f->header->state) { - case STATE_ONLINE: - return 0; - - case STATE_OFFLINE: - f->header->state = STATE_ONLINE; - (void) fsync(f->fd); - return 0; - - default: - return -EINVAL; - } -} - -bool journal_file_is_offlining(JournalFile *f) { - assert(f); - - __sync_synchronize(); - - if (f->offline_state == OFFLINE_DONE || - f->offline_state == OFFLINE_JOINED) - return false; - - return true; -} - -JournalFile* journal_file_close(JournalFile *f) { - assert(f); - -#ifdef HAVE_GCRYPT - /* Write the final tag */ - if (f->seal && f->writable) - journal_file_append_tag(f); -#endif - - if (f->post_change_timer) { - int enabled; - - if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0) - if (enabled == SD_EVENT_ONESHOT) - journal_file_post_change(f); - - (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF); - sd_event_source_unref(f->post_change_timer); - } - - journal_file_set_offline(f, true); - - if (f->mmap && f->fd >= 0) - mmap_cache_close_fd(f->mmap, f->fd); - - if (f->fd >= 0 && f->defrag_on_close) { - - /* Be friendly to btrfs: turn COW back on again now, - * and defragment the file. We won't write to the file - * ever again, hence remove all fragmentation, and - * reenable all the good bits COW usually provides - * (such as data checksumming). */ - - (void) chattr_fd(f->fd, 0, FS_NOCOW_FL); - (void) btrfs_defrag_fd(f->fd); - } - - if (f->close_fd) - safe_close(f->fd); - free(f->path); - - mmap_cache_unref(f->mmap); - - ordered_hashmap_free_free(f->chain_cache); - -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - free(f->compress_buffer); -#endif - -#ifdef HAVE_GCRYPT - if (f->fss_file) - munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size)); - else - free(f->fsprg_state); - - free(f->fsprg_seed); - - if (f->hmac) - gcry_md_close(f->hmac); -#endif - - free(f); - return NULL; -} - -void journal_file_close_set(Set *s) { - JournalFile *f; - - assert(s); - - while ((f = set_steal_first(s))) - (void) journal_file_close(f); -} - -static int journal_file_init_header(JournalFile *f, JournalFile *template) { - Header h = {}; - ssize_t k; - int r; - - assert(f); - - memcpy(h.signature, HEADER_SIGNATURE, 8); - h.header_size = htole64(ALIGN64(sizeof(h))); - - h.incompatible_flags |= htole32( - f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ | - f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4); - - h.compatible_flags = htole32( - f->seal * HEADER_COMPATIBLE_SEALED); - - r = sd_id128_randomize(&h.file_id); - if (r < 0) - return r; - - if (template) { - h.seqnum_id = template->header->seqnum_id; - h.tail_entry_seqnum = template->header->tail_entry_seqnum; - } else - h.seqnum_id = h.file_id; - - k = pwrite(f->fd, &h, sizeof(h), 0); - if (k < 0) - return -errno; - - if (k != sizeof(h)) - return -EIO; - - return 0; -} - -static int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL, *dn = NULL; - _cleanup_close_ int dfd = -1; - struct stat st; - int r; - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EBADFD; - - r = fd_get_path(fd, &path); - if (r < 0) - return r; - - if (!path_is_absolute(path)) - return -EINVAL; - - dn = dirname_malloc(path); - if (!dn) - return -ENOMEM; - - dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (dfd < 0) - return -errno; - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} - -static int journal_file_refresh_header(JournalFile *f) { - sd_id128_t boot_id; - int r; - - assert(f); - assert(f->header); - - r = sd_id128_get_machine(&f->header->machine_id); - if (r < 0) - return r; - - r = sd_id128_get_boot(&boot_id); - if (r < 0) - return r; - - if (sd_id128_equal(boot_id, f->header->boot_id)) - f->tail_entry_monotonic_valid = true; - - f->header->boot_id = boot_id; - - r = journal_file_set_online(f); - - /* Sync the online state to disk */ - (void) fsync(f->fd); - - /* We likely just created a new file, also sync the directory this file is located in. */ - (void) fsync_directory_of_file(f->fd); - - return r; -} - -static int journal_file_verify_header(JournalFile *f) { - uint32_t flags; - - assert(f); - assert(f->header); - - if (memcmp(f->header->signature, HEADER_SIGNATURE, 8)) - return -EBADMSG; - - /* In both read and write mode we refuse to open files with - * incompatible flags we don't know */ - flags = le32toh(f->header->incompatible_flags); - if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) { - if (flags & ~HEADER_INCOMPATIBLE_ANY) - log_debug("Journal file %s has unknown incompatible flags %"PRIx32, - f->path, flags & ~HEADER_INCOMPATIBLE_ANY); - flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED; - if (flags) - log_debug("Journal file %s uses incompatible flags %"PRIx32 - " disabled at compilation time.", f->path, flags); - return -EPROTONOSUPPORT; - } - - /* When open for writing we refuse to open files with - * compatible flags, too */ - flags = le32toh(f->header->compatible_flags); - if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) { - if (flags & ~HEADER_COMPATIBLE_ANY) - log_debug("Journal file %s has unknown compatible flags %"PRIx32, - f->path, flags & ~HEADER_COMPATIBLE_ANY); - flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED; - if (flags) - log_debug("Journal file %s uses compatible flags %"PRIx32 - " disabled at compilation time.", f->path, flags); - return -EPROTONOSUPPORT; - } - - if (f->header->state >= _STATE_MAX) - return -EBADMSG; - - /* The first addition was n_data, so check that we are at least this large */ - if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) - return -EBADMSG; - - if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) - return -EBADMSG; - - if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) - return -ENODATA; - - if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) - return -ENODATA; - - if (!VALID64(le64toh(f->header->data_hash_table_offset)) || - !VALID64(le64toh(f->header->field_hash_table_offset)) || - !VALID64(le64toh(f->header->tail_object_offset)) || - !VALID64(le64toh(f->header->entry_array_offset))) - return -ENODATA; - - if (f->writable) { - uint8_t state; - sd_id128_t machine_id; - int r; - - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; - - if (!sd_id128_equal(machine_id, f->header->machine_id)) - return -EHOSTDOWN; - - state = f->header->state; - - if (state == STATE_ONLINE) { - log_debug("Journal file %s is already online. Assuming unclean closing.", f->path); - return -EBUSY; - } else if (state == STATE_ARCHIVED) - return -ESHUTDOWN; - else if (state != STATE_OFFLINE) { - log_debug("Journal file %s has unknown state %i.", f->path, state); - return -EBUSY; - } - } - - f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header); - f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header); - - f->seal = JOURNAL_HEADER_SEALED(f->header); - - return 0; -} - -static int journal_file_fstat(JournalFile *f) { - assert(f); - assert(f->fd >= 0); - - if (fstat(f->fd, &f->last_stat) < 0) - return -errno; - - f->last_stat_usec = now(CLOCK_MONOTONIC); - - /* Refuse appending to files that are already deleted */ - if (f->last_stat.st_nlink <= 0) - return -EIDRM; - - return 0; -} - -static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { - uint64_t old_size, new_size; - int r; - - assert(f); - assert(f->header); - - /* We assume that this file is not sparse, and we know that - * for sure, since we always call posix_fallocate() - * ourselves */ - - if (mmap_cache_got_sigbus(f->mmap, f->fd)) - return -EIO; - - old_size = - le64toh(f->header->header_size) + - le64toh(f->header->arena_size); - - new_size = PAGE_ALIGN(offset + size); - if (new_size < le64toh(f->header->header_size)) - new_size = le64toh(f->header->header_size); - - if (new_size <= old_size) { - - /* We already pre-allocated enough space, but before - * we write to it, let's check with fstat() if the - * file got deleted, in order make sure we don't throw - * away the data immediately. Don't check fstat() for - * all writes though, but only once ever 10s. */ - - if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC)) - return 0; - - return journal_file_fstat(f); - } - - /* Allocate more space. */ - - if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) - return -E2BIG; - - if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) { - struct statvfs svfs; - - if (fstatvfs(f->fd, &svfs) >= 0) { - uint64_t available; - - available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free); - - if (new_size - old_size > available) - return -E2BIG; - } - } - - /* Increase by larger blocks at once */ - new_size = ((new_size+FILE_SIZE_INCREASE-1) / FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE; - if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) - new_size = f->metrics.max_size; - - /* Note that the glibc fallocate() fallback is very - inefficient, hence we try to minimize the allocation area - as we can. */ - r = posix_fallocate(f->fd, old_size, new_size - old_size); - if (r != 0) - return -r; - - f->header->arena_size = htole64(new_size - le64toh(f->header->header_size)); - - return journal_file_fstat(f); -} - -static unsigned type_to_context(ObjectType type) { - /* One context for each type, plus one catch-all for the rest */ - assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS); - assert_cc(CONTEXT_HEADER < MMAP_CACHE_MAX_CONTEXTS); - return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0; -} - -static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) { - int r; - - assert(f); - assert(ret); - - if (size <= 0) - return -EINVAL; - - /* Avoid SIGBUS on invalid accesses */ - if (offset + size > (uint64_t) f->last_stat.st_size) { - /* Hmm, out of range? Let's refresh the fstat() data - * first, before we trust that check. */ - - r = journal_file_fstat(f); - if (r < 0) - return r; - - if (offset + size > (uint64_t) f->last_stat.st_size) - return -EADDRNOTAVAIL; - } - - return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); -} - -static uint64_t minimum_header_size(Object *o) { - - static const uint64_t table[] = { - [OBJECT_DATA] = sizeof(DataObject), - [OBJECT_FIELD] = sizeof(FieldObject), - [OBJECT_ENTRY] = sizeof(EntryObject), - [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject), - [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject), - [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject), - [OBJECT_TAG] = sizeof(TagObject), - }; - - if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0) - return sizeof(ObjectHeader); - - return table[o->object.type]; -} - -int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) { - int r; - void *t; - Object *o; - uint64_t s; - - assert(f); - assert(ret); - - /* Objects may only be located at multiple of 64 bit */ - if (!VALID64(offset)) - return -EBADMSG; - - /* Object may not be located in the file header */ - if (offset < le64toh(f->header->header_size)) - return -EBADMSG; - - r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); - if (r < 0) - return r; - - o = (Object*) t; - s = le64toh(o->object.size); - - if (s < sizeof(ObjectHeader)) - return -EBADMSG; - - if (o->object.type <= OBJECT_UNUSED) - return -EBADMSG; - - if (s < minimum_header_size(o)) - return -EBADMSG; - - if (type > OBJECT_UNUSED && o->object.type != type) - return -EBADMSG; - - if (s > sizeof(ObjectHeader)) { - r = journal_file_move_to(f, type, false, offset, s, &t); - if (r < 0) - return r; - - o = (Object*) t; - } - - *ret = o; - return 0; -} - -static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { - uint64_t r; - - assert(f); - assert(f->header); - - r = le64toh(f->header->tail_entry_seqnum) + 1; - - if (seqnum) { - /* If an external seqnum counter was passed, we update - * both the local and the external one, and set it to - * the maximum of both */ - - if (*seqnum + 1 > r) - r = *seqnum + 1; - - *seqnum = r; - } - - f->header->tail_entry_seqnum = htole64(r); - - if (f->header->head_entry_seqnum == 0) - f->header->head_entry_seqnum = htole64(r); - - return r; -} - -int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) { - int r; - uint64_t p; - Object *tail, *o; - void *t; - - assert(f); - assert(f->header); - assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX); - assert(size >= sizeof(ObjectHeader)); - assert(offset); - assert(ret); - - r = journal_file_set_online(f); - if (r < 0) - return r; - - p = le64toh(f->header->tail_object_offset); - if (p == 0) - p = le64toh(f->header->header_size); - else { - r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); - if (r < 0) - return r; - - p += ALIGN64(le64toh(tail->object.size)); - } - - r = journal_file_allocate(f, p, size); - if (r < 0) - return r; - - r = journal_file_move_to(f, type, false, p, size, &t); - if (r < 0) - return r; - - o = (Object*) t; - - zero(o->object); - o->object.type = type; - o->object.size = htole64(size); - - f->header->tail_object_offset = htole64(p); - f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); - - *ret = o; - *offset = p; - - return 0; -} - -static int journal_file_setup_data_hash_table(JournalFile *f) { - uint64_t s, p; - Object *o; - int r; - - assert(f); - assert(f->header); - - /* 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) - s = DEFAULT_DATA_HASH_TABLE_SIZE; - - log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem)); - - r = journal_file_append_object(f, - OBJECT_DATA_HASH_TABLE, - offsetof(Object, hash_table.items) + s, - &o, &p); - if (r < 0) - return r; - - memzero(o->hash_table.items, s); - - f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); - f->header->data_hash_table_size = htole64(s); - - return 0; -} - -static int journal_file_setup_field_hash_table(JournalFile *f) { - uint64_t s, p; - Object *o; - int r; - - assert(f); - assert(f->header); - - /* We use a fixed size hash table for the fields as this - * number should grow very slowly only */ - - s = DEFAULT_FIELD_HASH_TABLE_SIZE; - r = journal_file_append_object(f, - OBJECT_FIELD_HASH_TABLE, - offsetof(Object, hash_table.items) + s, - &o, &p); - if (r < 0) - return r; - - memzero(o->hash_table.items, s); - - f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); - f->header->field_hash_table_size = htole64(s); - - return 0; -} - -int journal_file_map_data_hash_table(JournalFile *f) { - uint64_t s, p; - void *t; - int r; - - assert(f); - assert(f->header); - - if (f->data_hash_table) - return 0; - - p = le64toh(f->header->data_hash_table_offset); - s = le64toh(f->header->data_hash_table_size); - - r = journal_file_move_to(f, - OBJECT_DATA_HASH_TABLE, - true, - p, s, - &t); - if (r < 0) - return r; - - f->data_hash_table = t; - return 0; -} - -int journal_file_map_field_hash_table(JournalFile *f) { - uint64_t s, p; - void *t; - int r; - - assert(f); - assert(f->header); - - if (f->field_hash_table) - return 0; - - p = le64toh(f->header->field_hash_table_offset); - s = le64toh(f->header->field_hash_table_size); - - r = journal_file_move_to(f, - OBJECT_FIELD_HASH_TABLE, - true, - p, s, - &t); - if (r < 0) - return r; - - f->field_hash_table = t; - return 0; -} - -static int journal_file_link_field( - JournalFile *f, - Object *o, - uint64_t offset, - uint64_t hash) { - - uint64_t p, h, m; - int r; - - assert(f); - assert(f->header); - assert(f->field_hash_table); - assert(o); - assert(offset > 0); - - if (o->object.type != OBJECT_FIELD) - return -EINVAL; - - m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); - if (m <= 0) - return -EBADMSG; - - /* This might alter the window we are looking at */ - o->field.next_hash_offset = o->field.head_data_offset = 0; - - h = hash % m; - p = le64toh(f->field_hash_table[h].tail_hash_offset); - if (p == 0) - f->field_hash_table[h].head_hash_offset = htole64(offset); - else { - r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); - if (r < 0) - return r; - - o->field.next_hash_offset = htole64(offset); - } - - f->field_hash_table[h].tail_hash_offset = htole64(offset); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) - f->header->n_fields = htole64(le64toh(f->header->n_fields) + 1); - - return 0; -} - -static int journal_file_link_data( - JournalFile *f, - Object *o, - uint64_t offset, - uint64_t hash) { - - uint64_t p, h, m; - int r; - - assert(f); - assert(f->header); - assert(f->data_hash_table); - assert(o); - assert(offset > 0); - - if (o->object.type != OBJECT_DATA) - return -EINVAL; - - m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); - if (m <= 0) - return -EBADMSG; - - /* This might alter the window we are looking at */ - o->data.next_hash_offset = o->data.next_field_offset = 0; - o->data.entry_offset = o->data.entry_array_offset = 0; - o->data.n_entries = 0; - - h = hash % m; - p = le64toh(f->data_hash_table[h].tail_hash_offset); - if (p == 0) - /* Only entry in the hash table is easy */ - f->data_hash_table[h].head_hash_offset = htole64(offset); - else { - /* Move back to the previous data object, to patch in - * pointer */ - - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - o->data.next_hash_offset = htole64(offset); - } - - f->data_hash_table[h].tail_hash_offset = htole64(offset); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) - f->header->n_data = htole64(le64toh(f->header->n_data) + 1); - - return 0; -} - -int journal_file_find_field_object_with_hash( - JournalFile *f, - const void *field, uint64_t size, uint64_t hash, - Object **ret, uint64_t *offset) { - - uint64_t p, osize, h, m; - int r; - - assert(f); - assert(f->header); - assert(field && size > 0); - - /* If the field hash table is empty, we can't find anything */ - if (le64toh(f->header->field_hash_table_size) <= 0) - return 0; - - /* Map the field hash table, if it isn't mapped yet. */ - r = journal_file_map_field_hash_table(f); - if (r < 0) - return r; - - osize = offsetof(Object, field.payload) + size; - - m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); - if (m <= 0) - return -EBADMSG; - - h = hash % m; - p = le64toh(f->field_hash_table[h].head_hash_offset); - - while (p > 0) { - Object *o; - - r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); - if (r < 0) - return r; - - if (le64toh(o->field.hash) == hash && - le64toh(o->object.size) == osize && - memcmp(o->field.payload, field, size) == 0) { - - if (ret) - *ret = o; - if (offset) - *offset = p; - - return 1; - } - - p = le64toh(o->field.next_hash_offset); - } - - return 0; -} - -int journal_file_find_field_object( - JournalFile *f, - const void *field, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash; - - assert(f); - assert(field && size > 0); - - hash = hash64(field, size); - - return journal_file_find_field_object_with_hash(f, - field, size, hash, - ret, offset); -} - -int journal_file_find_data_object_with_hash( - JournalFile *f, - const void *data, uint64_t size, uint64_t hash, - Object **ret, uint64_t *offset) { - - uint64_t p, osize, h, m; - int r; - - assert(f); - assert(f->header); - assert(data || size == 0); - - /* If there's no data hash table, then there's no entry. */ - if (le64toh(f->header->data_hash_table_size) <= 0) - return 0; - - /* Map the data hash table, if it isn't mapped yet. */ - r = journal_file_map_data_hash_table(f); - if (r < 0) - return r; - - osize = offsetof(Object, data.payload) + size; - - m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); - if (m <= 0) - return -EBADMSG; - - h = hash % m; - p = le64toh(f->data_hash_table[h].head_hash_offset); - - while (p > 0) { - Object *o; - - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - if (le64toh(o->data.hash) != hash) - goto next; - - if (o->object.flags & OBJECT_COMPRESSION_MASK) { -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - uint64_t l; - size_t rsize = 0; - - l = le64toh(o->object.size); - if (l <= offsetof(Object, data.payload)) - return -EBADMSG; - - l -= offsetof(Object, data.payload); - - r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, - o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0); - if (r < 0) - return r; - - if (rsize == size && - memcmp(f->compress_buffer, data, size) == 0) { - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } -#else - return -EPROTONOSUPPORT; -#endif - } else if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } - - next: - p = le64toh(o->data.next_hash_offset); - } - - return 0; -} - -int journal_file_find_data_object( - JournalFile *f, - const void *data, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash; - - assert(f); - assert(data || size == 0); - - hash = hash64(data, size); - - return journal_file_find_data_object_with_hash(f, - data, size, hash, - ret, offset); -} - -static int journal_file_append_field( - JournalFile *f, - const void *field, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash, p; - uint64_t osize; - Object *o; - int r; - - assert(f); - assert(field && size > 0); - - hash = hash64(field, size); - - r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p); - if (r < 0) - return r; - else if (r > 0) { - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 0; - } - - osize = offsetof(Object, field.payload) + size; - r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p); - if (r < 0) - return r; - - o->field.hash = htole64(hash); - memcpy(o->field.payload, field, size); - - r = journal_file_link_field(f, o, p, hash); - if (r < 0) - return r; - - /* The linking might have altered the window, so let's - * refresh our pointer */ - r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); - if (r < 0) - return r; - -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p); - if (r < 0) - return r; -#endif - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 0; -} - -static int journal_file_append_data( - JournalFile *f, - const void *data, uint64_t size, - Object **ret, uint64_t *offset) { - - uint64_t hash, p; - uint64_t osize; - Object *o; - int r, compression = 0; - const void *eq; - - assert(f); - assert(data || size == 0); - - hash = hash64(data, size); - - r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); - if (r < 0) - return r; - if (r > 0) { - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 0; - } - - osize = offsetof(Object, data.payload) + size; - r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p); - if (r < 0) - return r; - - o->data.hash = htole64(hash); - -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) { - size_t rsize = 0; - - compression = compress_blob(data, size, o->data.payload, size - 1, &rsize); - - 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 == 0) - memcpy_safe(o->data.payload, data, size); - - r = journal_file_link_data(f, o, p, hash); - if (r < 0) - return r; - - /* The linking might have altered the window, so let's - * refresh our pointer */ - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - if (!data) - eq = NULL; - else - eq = memchr(data, '=', size); - if (eq && eq > data) { - Object *fo = NULL; - uint64_t fp; - - /* Create field object ... */ - r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp); - if (r < 0) - return r; - - /* ... and link it in. */ - o->data.next_field_offset = fo->field.head_data_offset; - fo->field.head_data_offset = le64toh(p); - } - -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); - if (r < 0) - return r; -#endif - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 0; -} - -uint64_t journal_file_entry_n_items(Object *o) { - assert(o); - - if (o->object.type != OBJECT_ENTRY) - return 0; - - return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); -} - -uint64_t journal_file_entry_array_n_items(Object *o) { - assert(o); - - if (o->object.type != OBJECT_ENTRY_ARRAY) - return 0; - - return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); -} - -uint64_t journal_file_hash_table_n_items(Object *o) { - assert(o); - - if (o->object.type != OBJECT_DATA_HASH_TABLE && - o->object.type != OBJECT_FIELD_HASH_TABLE) - return 0; - - return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem); -} - -static int link_entry_into_array(JournalFile *f, - le64_t *first, - le64_t *idx, - uint64_t p) { - int r; - uint64_t n = 0, ap = 0, q, i, a, hidx; - Object *o; - - assert(f); - assert(f->header); - assert(first); - assert(idx); - assert(p > 0); - - a = le64toh(*first); - i = hidx = le64toh(*idx); - while (a > 0) { - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - n = journal_file_entry_array_n_items(o); - if (i < n) { - o->entry_array.items[i] = htole64(p); - *idx = htole64(hidx + 1); - return 0; - } - - i -= n; - ap = a; - a = le64toh(o->entry_array.next_entry_array_offset); - } - - if (hidx > n) - n = (hidx+1) * 2; - else - n = n * 2; - - if (n < 4) - n = 4; - - r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY, - offsetof(Object, entry_array.items) + n * sizeof(uint64_t), - &o, &q); - if (r < 0) - return r; - -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q); - if (r < 0) - return r; -#endif - - o->entry_array.items[i] = htole64(p); - - if (ap == 0) - *first = htole64(q); - else { - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o); - if (r < 0) - return r; - - o->entry_array.next_entry_array_offset = htole64(q); - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) - f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1); - - *idx = htole64(hidx + 1); - - return 0; -} - -static int link_entry_into_array_plus_one(JournalFile *f, - le64_t *extra, - le64_t *first, - le64_t *idx, - uint64_t p) { - - int r; - - assert(f); - assert(extra); - assert(first); - assert(idx); - assert(p > 0); - - if (*idx == 0) - *extra = htole64(p); - else { - le64_t i; - - i = htole64(le64toh(*idx) - 1); - r = link_entry_into_array(f, first, &i, p); - if (r < 0) - return r; - } - - *idx = htole64(le64toh(*idx) + 1); - return 0; -} - -static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { - uint64_t p; - int r; - assert(f); - assert(o); - assert(offset > 0); - - p = le64toh(o->entry.items[i].object_offset); - if (p == 0) - return -EINVAL; - - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - return link_entry_into_array_plus_one(f, - &o->data.entry_offset, - &o->data.entry_array_offset, - &o->data.n_entries, - offset); -} - -static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { - uint64_t n, i; - int r; - - assert(f); - assert(f->header); - assert(o); - assert(offset > 0); - - if (o->object.type != OBJECT_ENTRY) - return -EINVAL; - - __sync_synchronize(); - - /* Link up the entry itself */ - r = link_entry_into_array(f, - &f->header->entry_array_offset, - &f->header->n_entries, - offset); - if (r < 0) - return r; - - /* log_debug("=> %s seqnr=%"PRIu64" n_entries=%"PRIu64, f->path, o->entry.seqnum, f->header->n_entries); */ - - if (f->header->head_entry_realtime == 0) - f->header->head_entry_realtime = o->entry.realtime; - - f->header->tail_entry_realtime = o->entry.realtime; - f->header->tail_entry_monotonic = o->entry.monotonic; - - f->tail_entry_monotonic_valid = true; - - /* Link up the items */ - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) { - r = journal_file_link_entry_item(f, o, offset, i); - if (r < 0) - return r; - } - - return 0; -} - -static int journal_file_append_entry_internal( - JournalFile *f, - const dual_timestamp *ts, - uint64_t xor_hash, - const EntryItem items[], unsigned n_items, - uint64_t *seqnum, - Object **ret, uint64_t *offset) { - uint64_t np; - uint64_t osize; - Object *o; - int r; - - assert(f); - assert(f->header); - assert(items || n_items == 0); - assert(ts); - - osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); - - r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np); - if (r < 0) - return r; - - o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum)); - memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem)); - o->entry.realtime = htole64(ts->realtime); - o->entry.monotonic = htole64(ts->monotonic); - o->entry.xor_hash = htole64(xor_hash); - o->entry.boot_id = f->header->boot_id; - -#ifdef HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); - if (r < 0) - return r; -#endif - - r = journal_file_link_entry(f, o, np); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = np; - - return 0; -} - -void journal_file_post_change(JournalFile *f) { - assert(f); - - /* inotify() does not receive IN_MODIFY events from file - * accesses done via mmap(). After each access we hence - * trigger IN_MODIFY by truncating the journal file to its - * current size which triggers IN_MODIFY. */ - - __sync_synchronize(); - - if (ftruncate(f->fd, f->last_stat.st_size) < 0) - log_debug_errno(errno, "Failed to truncate file to its own size: %m"); -} - -static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) { - assert(userdata); - - journal_file_post_change(userdata); - - return 1; -} - -static void schedule_post_change(JournalFile *f) { - sd_event_source *timer; - int enabled, r; - uint64_t now; - - assert(f); - assert(f->post_change_timer); - - timer = f->post_change_timer; - - r = sd_event_source_get_enabled(timer, &enabled); - if (r < 0) { - log_debug_errno(r, "Failed to get ftruncate timer state: %m"); - goto fail; - } - - if (enabled == SD_EVENT_ONESHOT) - return; - - r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now); - if (r < 0) { - log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m"); - goto fail; - } - - r = sd_event_source_set_time(timer, now+f->post_change_timer_period); - if (r < 0) { - log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m"); - goto fail; - } - - r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT); - if (r < 0) { - log_debug_errno(r, "Failed to enable scheduled ftruncate: %m"); - goto fail; - } - - return; - -fail: - /* On failure, let's simply post the change immediately. */ - journal_file_post_change(f); -} - -/* Enable coalesced change posting in a timer on the provided sd_event instance */ -int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) { - _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; - int r; - - assert(f); - assert_return(!f->post_change_timer, -EINVAL); - assert(e); - assert(t); - - r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(timer, SD_EVENT_OFF); - if (r < 0) - return r; - - f->post_change_timer = timer; - timer = NULL; - f->post_change_timer_period = t; - - return r; -} - -static int entry_item_cmp(const void *_a, const void *_b) { - const EntryItem *a = _a, *b = _b; - - if (le64toh(a->object_offset) < le64toh(b->object_offset)) - return -1; - if (le64toh(a->object_offset) > le64toh(b->object_offset)) - return 1; - return 0; -} - -int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) { - unsigned i; - EntryItem *items; - int r; - uint64_t xor_hash = 0; - struct dual_timestamp _ts; - - assert(f); - assert(f->header); - assert(iovec || n_iovec == 0); - - if (!ts) { - dual_timestamp_get(&_ts); - ts = &_ts; - } - -#ifdef HAVE_GCRYPT - r = journal_file_maybe_append_tag(f, ts->realtime); - if (r < 0) - return r; -#endif - - /* alloca() can't take 0, hence let's allocate at least one */ - items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec)); - - for (i = 0; i < n_iovec; i++) { - uint64_t p; - Object *o; - - r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); - if (r < 0) - return r; - - xor_hash ^= le64toh(o->data.hash); - items[i].object_offset = htole64(p); - items[i].hash = o->data.hash; - } - - /* Order by the position on disk, in order to improve seek - * times for rotating media. */ - qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp); - - r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset); - - /* If the memory mapping triggered a SIGBUS then we return an - * IO error and ignore the error code passed down to us, since - * it is very likely just an effect of a nullified replacement - * mapping page */ - - if (mmap_cache_got_sigbus(f->mmap, f->fd)) - r = -EIO; - - if (f->post_change_timer) - schedule_post_change(f); - else - journal_file_post_change(f); - - return r; -} - -typedef struct ChainCacheItem { - uint64_t first; /* the array at the beginning of the chain */ - uint64_t array; /* the cached array */ - uint64_t begin; /* the first item in the cached array */ - uint64_t total; /* the total number of items in all arrays before this one in the chain */ - uint64_t last_index; /* the last index we looked at, to optimize locality when bisecting */ -} ChainCacheItem; - -static void chain_cache_put( - OrderedHashmap *h, - ChainCacheItem *ci, - uint64_t first, - uint64_t array, - uint64_t begin, - uint64_t total, - uint64_t last_index) { - - if (!ci) { - /* If the chain item to cache for this chain is the - * first one it's not worth caching anything */ - if (array == first) - return; - - if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) { - ci = ordered_hashmap_steal_first(h); - assert(ci); - } else { - ci = new(ChainCacheItem, 1); - if (!ci) - return; - } - - ci->first = first; - - if (ordered_hashmap_put(h, &ci->first, ci) < 0) { - free(ci); - return; - } - } else - assert(ci->first == first); - - ci->array = array; - ci->begin = begin; - ci->total = total; - ci->last_index = last_index; -} - -static int generic_array_get( - JournalFile *f, - uint64_t first, - uint64_t i, - Object **ret, uint64_t *offset) { - - Object *o; - uint64_t p = 0, a, t = 0; - int r; - ChainCacheItem *ci; - - assert(f); - - a = first; - - /* Try the chain cache first */ - ci = ordered_hashmap_get(f->chain_cache, &first); - if (ci && i > ci->total) { - a = ci->array; - i -= ci->total; - t = ci->total; - } - - while (a > 0) { - uint64_t k; - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - k = journal_file_entry_array_n_items(o); - if (i < k) { - p = le64toh(o->entry_array.items[i]); - goto found; - } - - i -= k; - t += k; - a = le64toh(o->entry_array.next_entry_array_offset); - } - - return 0; - -found: - /* Let's cache this item for the next invocation */ - chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; -} - -static int generic_array_get_plus_one( - JournalFile *f, - uint64_t extra, - uint64_t first, - uint64_t i, - Object **ret, uint64_t *offset) { - - Object *o; - - assert(f); - - if (i == 0) { - int r; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = extra; - - return 1; - } - - return generic_array_get(f, first, i-1, ret, offset); -} - -enum { - TEST_FOUND, - TEST_LEFT, - TEST_RIGHT -}; - -static int generic_array_bisect( - JournalFile *f, - uint64_t first, - uint64_t n, - uint64_t needle, - int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), - direction_t direction, - Object **ret, - uint64_t *offset, - uint64_t *idx) { - - uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1; - bool subtract_one = false; - Object *o, *array = NULL; - int r; - ChainCacheItem *ci; - - assert(f); - assert(test_object); - - /* Start with the first array in the chain */ - a = first; - - ci = ordered_hashmap_get(f->chain_cache, &first); - if (ci && n > ci->total) { - /* Ah, we have iterated this bisection array chain - * previously! Let's see if we can skip ahead in the - * chain, as far as the last time. But we can't jump - * backwards in the chain, so let's check that - * first. */ - - r = test_object(f, ci->begin, needle); - if (r < 0) - return r; - - if (r == TEST_LEFT) { - /* OK, what we are looking for is right of the - * begin of this EntryArray, so let's jump - * straight to previously cached array in the - * chain */ - - a = ci->array; - n -= ci->total; - t = ci->total; - last_index = ci->last_index; - } - } - - while (a > 0) { - uint64_t left, right, k, lp; - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); - if (r < 0) - return r; - - k = journal_file_entry_array_n_items(array); - right = MIN(k, n); - if (right <= 0) - return 0; - - i = right - 1; - lp = p = le64toh(array->entry_array.items[i]); - if (p <= 0) - r = -EBADMSG; - else - r = test_object(f, p, needle); - if (r == -EBADMSG) { - log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); - n = i; - continue; - } - if (r < 0) - return r; - - if (r == TEST_FOUND) - r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - - if (r == TEST_RIGHT) { - left = 0; - right -= 1; - - if (last_index != (uint64_t) -1) { - assert(last_index <= right); - - /* If we cached the last index we - * looked at, let's try to not to jump - * too wildly around and see if we can - * limit the range to look at early to - * the immediate neighbors of the last - * index we looked at. */ - - if (last_index > 0) { - uint64_t x = last_index - 1; - - p = le64toh(array->entry_array.items[x]); - if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); - if (r < 0) - return r; - - if (r == TEST_FOUND) - r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - - if (r == TEST_RIGHT) - right = x; - else - left = x + 1; - } - - if (last_index < right) { - uint64_t y = last_index + 1; - - p = le64toh(array->entry_array.items[y]); - if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); - if (r < 0) - return r; - - if (r == TEST_FOUND) - r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - - if (r == TEST_RIGHT) - right = y; - else - left = y + 1; - } - } - - for (;;) { - if (left == right) { - if (direction == DIRECTION_UP) - subtract_one = true; - - i = left; - goto found; - } - - assert(left < right); - i = (left + right) / 2; - - p = le64toh(array->entry_array.items[i]); - if (p <= 0) - r = -EBADMSG; - else - r = test_object(f, p, needle); - if (r == -EBADMSG) { - log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); - right = n = i; - continue; - } - if (r < 0) - return r; - - if (r == TEST_FOUND) - r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - - if (r == TEST_RIGHT) - right = i; - else - left = i + 1; - } - } - - if (k >= n) { - if (direction == DIRECTION_UP) { - i = n; - subtract_one = true; - goto found; - } - - return 0; - } - - last_p = lp; - - n -= k; - t += k; - last_index = (uint64_t) -1; - a = le64toh(array->entry_array.next_entry_array_offset); - } - - return 0; - -found: - if (subtract_one && t == 0 && i == 0) - return 0; - - /* Let's cache this item for the next invocation */ - chain_cache_put(f->chain_cache, ci, first, a, le64toh(array->entry_array.items[0]), t, subtract_one ? (i > 0 ? i-1 : (uint64_t) -1) : i); - - if (subtract_one && i == 0) - p = last_p; - else if (subtract_one) - p = le64toh(array->entry_array.items[i-1]); - else - p = le64toh(array->entry_array.items[i]); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - if (idx) - *idx = t + i + (subtract_one ? -1 : 0); - - return 1; -} - -static int generic_array_bisect_plus_one( - JournalFile *f, - uint64_t extra, - uint64_t first, - uint64_t n, - uint64_t needle, - int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), - direction_t direction, - Object **ret, - uint64_t *offset, - uint64_t *idx) { - - int r; - bool step_back = false; - Object *o; - - assert(f); - assert(test_object); - - if (n <= 0) - return 0; - - /* This bisects the array in object 'first', but first checks - * an extra */ - r = test_object(f, extra, needle); - if (r < 0) - return r; - - if (r == TEST_FOUND) - r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - - /* if we are looking with DIRECTION_UP then we need to first - see if in the actual array there is a matching entry, and - return the last one of that. But if there isn't any we need - to return this one. Hence remember this, and return it - below. */ - if (r == TEST_LEFT) - step_back = direction == DIRECTION_UP; - - if (r == TEST_RIGHT) { - if (direction == DIRECTION_DOWN) - goto found; - else - return 0; - } - - r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); - - if (r == 0 && step_back) - goto found; - - if (r > 0 && idx) - (*idx)++; - - return r; - -found: - r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = extra; - - if (idx) - *idx = 0; - - return 1; -} - -_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { - assert(f); - assert(p > 0); - - if (p == needle) - return TEST_FOUND; - else if (p < needle) - return TEST_LEFT; - else - return TEST_RIGHT; -} - -static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { - Object *o; - int r; - - assert(f); - assert(p > 0); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (le64toh(o->entry.seqnum) == needle) - return TEST_FOUND; - else if (le64toh(o->entry.seqnum) < needle) - return TEST_LEFT; - else - return TEST_RIGHT; -} - -int journal_file_move_to_entry_by_seqnum( - JournalFile *f, - uint64_t seqnum, - direction_t direction, - Object **ret, - uint64_t *offset) { - assert(f); - assert(f->header); - - return generic_array_bisect(f, - le64toh(f->header->entry_array_offset), - le64toh(f->header->n_entries), - seqnum, - test_object_seqnum, - direction, - ret, offset, NULL); -} - -static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { - Object *o; - int r; - - assert(f); - assert(p > 0); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (le64toh(o->entry.realtime) == needle) - return TEST_FOUND; - else if (le64toh(o->entry.realtime) < needle) - return TEST_LEFT; - else - return TEST_RIGHT; -} - -int journal_file_move_to_entry_by_realtime( - JournalFile *f, - uint64_t realtime, - direction_t direction, - Object **ret, - uint64_t *offset) { - assert(f); - assert(f->header); - - return generic_array_bisect(f, - le64toh(f->header->entry_array_offset), - le64toh(f->header->n_entries), - realtime, - test_object_realtime, - direction, - ret, offset, NULL); -} - -static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { - Object *o; - int r; - - assert(f); - assert(p > 0); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (le64toh(o->entry.monotonic) == needle) - return TEST_FOUND; - else if (le64toh(o->entry.monotonic) < needle) - return TEST_LEFT; - else - return TEST_RIGHT; -} - -static int find_data_object_by_boot_id( - JournalFile *f, - sd_id128_t boot_id, - Object **o, - uint64_t *b) { - - char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID="; - - sd_id128_to_string(boot_id, t + 9); - return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b); -} - -int journal_file_move_to_entry_by_monotonic( - JournalFile *f, - sd_id128_t boot_id, - uint64_t monotonic, - direction_t direction, - Object **ret, - uint64_t *offset) { - - Object *o; - int r; - - assert(f); - - r = find_data_object_by_boot_id(f, boot_id, &o, NULL); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; - - return generic_array_bisect_plus_one(f, - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - le64toh(o->data.n_entries), - monotonic, - test_object_monotonic, - direction, - ret, offset, NULL); -} - -void journal_file_reset_location(JournalFile *f) { - f->location_type = LOCATION_HEAD; - f->current_offset = 0; - f->current_seqnum = 0; - f->current_realtime = 0; - f->current_monotonic = 0; - zero(f->current_boot_id); - f->current_xor_hash = 0; -} - -void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) { - f->location_type = LOCATION_SEEK; - f->current_offset = offset; - f->current_seqnum = le64toh(o->entry.seqnum); - f->current_realtime = le64toh(o->entry.realtime); - f->current_monotonic = le64toh(o->entry.monotonic); - f->current_boot_id = o->entry.boot_id; - f->current_xor_hash = le64toh(o->entry.xor_hash); -} - -int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { - assert(af); - assert(af->header); - assert(bf); - assert(bf->header); - assert(af->location_type == LOCATION_SEEK); - assert(bf->location_type == LOCATION_SEEK); - - /* If contents and timestamps match, these entries are - * identical, even if the seqnum does not match */ - if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) && - af->current_monotonic == bf->current_monotonic && - af->current_realtime == bf->current_realtime && - af->current_xor_hash == bf->current_xor_hash) - return 0; - - if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { - - /* If this is from the same seqnum source, compare - * seqnums */ - if (af->current_seqnum < bf->current_seqnum) - return -1; - if (af->current_seqnum > bf->current_seqnum) - return 1; - - /* Wow! This is weird, different data but the same - * seqnums? Something is borked, but let's make the - * best of it and compare by time. */ - } - - if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) { - - /* If the boot id matches, compare monotonic time */ - if (af->current_monotonic < bf->current_monotonic) - return -1; - if (af->current_monotonic > bf->current_monotonic) - return 1; - } - - /* Otherwise, compare UTC time */ - if (af->current_realtime < bf->current_realtime) - return -1; - if (af->current_realtime > bf->current_realtime) - return 1; - - /* Finally, compare by contents */ - if (af->current_xor_hash < bf->current_xor_hash) - return -1; - if (af->current_xor_hash > bf->current_xor_hash) - return 1; - - return 0; -} - -int journal_file_next_entry( - JournalFile *f, - uint64_t p, - direction_t direction, - Object **ret, uint64_t *offset) { - - uint64_t i, n, ofs; - int r; - - assert(f); - assert(f->header); - - n = le64toh(f->header->n_entries); - if (n <= 0) - return 0; - - if (p == 0) - i = direction == DIRECTION_DOWN ? 0 : n - 1; - else { - r = generic_array_bisect(f, - le64toh(f->header->entry_array_offset), - le64toh(f->header->n_entries), - p, - test_object_offset, - DIRECTION_DOWN, - NULL, NULL, - &i); - if (r <= 0) - return r; - - if (direction == DIRECTION_DOWN) { - if (i >= n - 1) - return 0; - - i++; - } else { - if (i <= 0) - return 0; - - i--; - } - } - - /* And jump to it */ - r = generic_array_get(f, - le64toh(f->header->entry_array_offset), - i, - ret, &ofs); - if (r == -EBADMSG && direction == DIRECTION_DOWN) { - /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, - * consider this the end of the journal file. */ - log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); - return 0; - } - if (r <= 0) - return r; - - if (p > 0 && - (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { - log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); - return -EBADMSG; - } - - if (offset) - *offset = ofs; - - return 1; -} - -int journal_file_next_entry_for_data( - JournalFile *f, - Object *o, uint64_t p, - uint64_t data_offset, - direction_t direction, - Object **ret, uint64_t *offset) { - - uint64_t n, i; - int r; - Object *d; - - assert(f); - assert(p > 0 || !o); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - - n = le64toh(d->data.n_entries); - if (n <= 0) - return n; - - if (!o) - i = direction == DIRECTION_DOWN ? 0 : n - 1; - else { - if (o->object.type != OBJECT_ENTRY) - return -EINVAL; - - r = generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - p, - test_object_offset, - DIRECTION_DOWN, - NULL, NULL, - &i); - - if (r <= 0) - return r; - - if (direction == DIRECTION_DOWN) { - if (i >= n - 1) - return 0; - - i++; - } else { - if (i <= 0) - return 0; - - i--; - } - - } - - return generic_array_get_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - i, - ret, offset); -} - -int journal_file_move_to_entry_by_offset_for_data( - JournalFile *f, - uint64_t data_offset, - uint64_t p, - direction_t direction, - Object **ret, uint64_t *offset) { - - int r; - Object *d; - - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - p, - test_object_offset, - direction, - ret, offset, NULL); -} - -int journal_file_move_to_entry_by_monotonic_for_data( - JournalFile *f, - uint64_t data_offset, - sd_id128_t boot_id, - uint64_t monotonic, - direction_t direction, - Object **ret, uint64_t *offset) { - - Object *o, *d; - int r; - uint64_t b, z; - - assert(f); - - /* First, seek by time */ - r = find_data_object_by_boot_id(f, boot_id, &o, &b); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; - - r = generic_array_bisect_plus_one(f, - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - le64toh(o->data.n_entries), - monotonic, - test_object_monotonic, - direction, - NULL, &z, NULL); - if (r <= 0) - return r; - - /* And now, continue seeking until we find an entry that - * exists in both bisection arrays */ - - for (;;) { - Object *qo; - uint64_t p, q; - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - - r = generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - z, - test_object_offset, - direction, - NULL, &p, NULL); - if (r <= 0) - return r; - - r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); - if (r < 0) - return r; - - r = generic_array_bisect_plus_one(f, - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - le64toh(o->data.n_entries), - p, - test_object_offset, - direction, - &qo, &q, NULL); - - if (r <= 0) - return r; - - if (p == q) { - if (ret) - *ret = qo; - if (offset) - *offset = q; - - return 1; - } - - z = q; - } -} - -int journal_file_move_to_entry_by_seqnum_for_data( - JournalFile *f, - uint64_t data_offset, - uint64_t seqnum, - direction_t direction, - Object **ret, uint64_t *offset) { - - Object *d; - int r; - - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - seqnum, - test_object_seqnum, - direction, - ret, offset, NULL); -} - -int journal_file_move_to_entry_by_realtime_for_data( - JournalFile *f, - uint64_t data_offset, - uint64_t realtime, - direction_t direction, - Object **ret, uint64_t *offset) { - - Object *d; - int r; - - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - - return generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), - realtime, - test_object_realtime, - direction, - ret, offset, NULL); -} - -void journal_file_dump(JournalFile *f) { - Object *o; - int r; - uint64_t p; - - assert(f); - assert(f->header); - - journal_file_print_header(f); - - p = le64toh(f->header->header_size); - while (p != 0) { - r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); - if (r < 0) - goto fail; - - switch (o->object.type) { - - case OBJECT_UNUSED: - printf("Type: OBJECT_UNUSED\n"); - break; - - case OBJECT_DATA: - printf("Type: OBJECT_DATA\n"); - break; - - case OBJECT_FIELD: - printf("Type: OBJECT_FIELD\n"); - break; - - case OBJECT_ENTRY: - printf("Type: OBJECT_ENTRY seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n", - le64toh(o->entry.seqnum), - le64toh(o->entry.monotonic), - le64toh(o->entry.realtime)); - break; - - case OBJECT_FIELD_HASH_TABLE: - printf("Type: OBJECT_FIELD_HASH_TABLE\n"); - break; - - case OBJECT_DATA_HASH_TABLE: - printf("Type: OBJECT_DATA_HASH_TABLE\n"); - break; - - case OBJECT_ENTRY_ARRAY: - printf("Type: OBJECT_ENTRY_ARRAY\n"); - break; - - case OBJECT_TAG: - printf("Type: OBJECT_TAG seqnum=%"PRIu64" epoch=%"PRIu64"\n", - le64toh(o->tag.seqnum), - le64toh(o->tag.epoch)); - break; - - default: - printf("Type: unknown (%i)\n", o->object.type); - break; - } - - if (o->object.flags & OBJECT_COMPRESSION_MASK) - printf("Flags: %s\n", - object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK)); - - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } - - return; -fail: - log_error("File corrupt"); -} - -static const char* format_timestamp_safe(char *buf, size_t l, usec_t t) { - const char *x; - - x = format_timestamp(buf, l, t); - if (x) - return x; - return " --- "; -} - -void journal_file_print_header(JournalFile *f) { - char a[33], b[33], c[33], d[33]; - char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX], z[FORMAT_TIMESTAMP_MAX]; - struct stat st; - char bytes[FORMAT_BYTES_MAX]; - - assert(f); - assert(f->header); - - printf("File Path: %s\n" - "File ID: %s\n" - "Machine ID: %s\n" - "Boot ID: %s\n" - "Sequential Number ID: %s\n" - "State: %s\n" - "Compatible Flags:%s%s\n" - "Incompatible Flags:%s%s%s\n" - "Header size: %"PRIu64"\n" - "Arena size: %"PRIu64"\n" - "Data Hash Table Size: %"PRIu64"\n" - "Field Hash Table Size: %"PRIu64"\n" - "Rotate Suggested: %s\n" - "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" - "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" - "Head Realtime Timestamp: %s (%"PRIx64")\n" - "Tail Realtime Timestamp: %s (%"PRIx64")\n" - "Tail Monotonic Timestamp: %s (%"PRIx64")\n" - "Objects: %"PRIu64"\n" - "Entry Objects: %"PRIu64"\n", - f->path, - sd_id128_to_string(f->header->file_id, a), - sd_id128_to_string(f->header->machine_id, b), - sd_id128_to_string(f->header->boot_id, c), - sd_id128_to_string(f->header->seqnum_id, d), - f->header->state == STATE_OFFLINE ? "OFFLINE" : - f->header->state == STATE_ONLINE ? "ONLINE" : - f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN", - JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "", - (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "", - JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "", - JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "", - (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "", - le64toh(f->header->header_size), - le64toh(f->header->arena_size), - le64toh(f->header->data_hash_table_size) / sizeof(HashItem), - le64toh(f->header->field_hash_table_size) / sizeof(HashItem), - yes_no(journal_file_rotate_suggested(f, 0)), - le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), - le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), - format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), - format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), - format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), - le64toh(f->header->n_objects), - le64toh(f->header->n_entries)); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) - printf("Data Objects: %"PRIu64"\n" - "Data Hash Table Fill: %.1f%%\n", - le64toh(f->header->n_data), - 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)))); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) - printf("Field Objects: %"PRIu64"\n" - "Field Hash Table Fill: %.1f%%\n", - le64toh(f->header->n_fields), - 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_tags)) - printf("Tag Objects: %"PRIu64"\n", - le64toh(f->header->n_tags)); - if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) - printf("Entry Array Objects: %"PRIu64"\n", - le64toh(f->header->n_entry_arrays)); - - if (fstat(f->fd, &st) >= 0) - printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL)); -} - -static int journal_file_warn_btrfs(JournalFile *f) { - unsigned attrs; - int r; - - assert(f); - - /* Before we write anything, check if the COW logic is turned - * off on btrfs. Given our write pattern that is quite - * unfriendly to COW file systems this should greatly improve - * performance on COW file systems, such as btrfs, at the - * expense of data integrity features (which shouldn't be too - * bad, given that we do our own checksumming). */ - - r = btrfs_is_filesystem(f->fd); - if (r < 0) - return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m"); - if (!r) - return 0; - - r = read_attr_fd(f->fd, &attrs); - if (r < 0) - return log_warning_errno(r, "Failed to read file attributes: %m"); - - if (attrs & FS_NOCOW_FL) { - log_debug("Detected btrfs file system with copy-on-write disabled, all is good."); - return 0; - } - - log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. " - "This is likely to slow down journal access substantially, please consider turning " - "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path); - - return 1; -} - -int journal_file_open( - int fd, - const char *fname, - int flags, - mode_t mode, - bool compress, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournalFile *template, - JournalFile **ret) { - - bool newly_created = false; - JournalFile *f; - void *h; - int r; - - assert(ret); - assert(fd >= 0 || fname); - - if ((flags & O_ACCMODE) != O_RDONLY && - (flags & O_ACCMODE) != O_RDWR) - return -EINVAL; - - if (fname) { - if (!endswith(fname, ".journal") && - !endswith(fname, ".journal~")) - return -EINVAL; - } - - f = new0(JournalFile, 1); - if (!f) - return -ENOMEM; - - f->fd = fd; - f->mode = mode; - - f->flags = flags; - f->prot = prot_from_flags(flags); - f->writable = (flags & O_ACCMODE) != O_RDONLY; -#if defined(HAVE_LZ4) - f->compress_lz4 = compress; -#elif defined(HAVE_XZ) - f->compress_xz = compress; -#endif -#ifdef HAVE_GCRYPT - f->seal = seal; -#endif - - if (mmap_cache) - f->mmap = mmap_cache_ref(mmap_cache); - else { - f->mmap = mmap_cache_new(); - if (!f->mmap) { - r = -ENOMEM; - goto fail; - } - } - - if (fname) - f->path = strdup(fname); - else /* If we don't know the path, fill in something explanatory and vaguely useful */ - asprintf(&f->path, "/proc/self/%i", fd); - if (!f->path) { - r = -ENOMEM; - goto fail; - } - - f->chain_cache = ordered_hashmap_new(&uint64_hash_ops); - if (!f->chain_cache) { - r = -ENOMEM; - goto fail; - } - - if (f->fd < 0) { - f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); - if (f->fd < 0) { - r = -errno; - goto fail; - } - - /* fds we opened here by us should also be closed by us. */ - f->close_fd = true; - } - - r = journal_file_fstat(f); - if (r < 0) - goto fail; - - if (f->last_stat.st_size == 0 && f->writable) { - - (void) journal_file_warn_btrfs(f); - - /* Let's attach the creation time to the journal file, - * so that the vacuuming code knows the age of this - * file even if the file might end up corrupted one - * day... Ideally we'd just use the creation time many - * file systems maintain for each file, but there is - * currently no usable API to query this, hence let's - * emulate this via extended attributes. If extended - * attributes are not supported we'll just skip this, - * and rely solely on mtime/atime/ctime of the file. */ - - fd_setcrtime(f->fd, 0); - -#ifdef HAVE_GCRYPT - /* Try to load the FSPRG state, and if we can't, then - * just don't do sealing */ - if (f->seal) { - r = journal_file_fss_load(f); - if (r < 0) - f->seal = false; - } -#endif - - r = journal_file_init_header(f, template); - if (r < 0) - goto fail; - - r = journal_file_fstat(f); - if (r < 0) - goto fail; - - newly_created = true; - } - - if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { - r = -ENODATA; - goto fail; - } - - r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h); - if (r < 0) - goto fail; - - f->header = h; - - if (!newly_created) { - if (deferred_closes) - journal_file_close_set(deferred_closes); - - r = journal_file_verify_header(f); - if (r < 0) - goto fail; - } - -#ifdef HAVE_GCRYPT - if (!newly_created && f->writable) { - r = journal_file_fss_load(f); - if (r < 0) - goto fail; - } -#endif - - if (f->writable) { - if (metrics) { - journal_default_metrics(metrics, f->fd); - f->metrics = *metrics; - } else if (template) - f->metrics = template->metrics; - - r = journal_file_refresh_header(f); - if (r < 0) - goto fail; - } - -#ifdef HAVE_GCRYPT - r = journal_file_hmac_setup(f); - if (r < 0) - goto fail; -#endif - - if (newly_created) { - r = journal_file_setup_field_hash_table(f); - if (r < 0) - goto fail; - - r = journal_file_setup_data_hash_table(f); - if (r < 0) - goto fail; - -#ifdef HAVE_GCRYPT - r = journal_file_append_first_tag(f); - if (r < 0) - goto fail; -#endif - } - - if (mmap_cache_got_sigbus(f->mmap, f->fd)) { - r = -EIO; - goto fail; - } - - if (template && template->post_change_timer) { - r = journal_file_enable_post_change_timer( - f, - sd_event_source_get_event(template->post_change_timer), - template->post_change_timer_period); - - if (r < 0) - goto fail; - } - - /* The file is opened now successfully, thus we take possession of any passed in fd. */ - f->close_fd = true; - - *ret = f; - return 0; - -fail: - if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd)) - r = -EIO; - - (void) journal_file_close(f); - - return r; -} - -int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes) { - _cleanup_free_ char *p = NULL; - size_t l; - JournalFile *old_file, *new_file = NULL; - int r; - - assert(f); - assert(*f); - - old_file = *f; - - if (!old_file->writable) - return -EINVAL; - - /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse - * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ - if (path_startswith(old_file->path, "/proc/self/fd")) - return -EINVAL; - - if (!endswith(old_file->path, ".journal")) - return -EINVAL; - - l = strlen(old_file->path); - r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal", - (int) l - 8, old_file->path, - SD_ID128_FORMAT_VAL(old_file->header->seqnum_id), - le64toh((*f)->header->head_entry_seqnum), - le64toh((*f)->header->head_entry_realtime)); - if (r < 0) - return -ENOMEM; - - /* Try to rename the file to the archived version. If the file - * already was deleted, we'll get ENOENT, let's ignore that - * case. */ - r = rename(old_file->path, p); - if (r < 0 && errno != ENOENT) - return -errno; - - /* Sync the rename to disk */ - (void) fsync_directory_of_file(old_file->fd); - - /* Set as archive so offlining commits w/state=STATE_ARCHIVED. - * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, - * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which - * would result in the rotated journal never getting fsync() called before closing. - * Now we simply queue the archive state by setting an archive bit, leaving the state - * as STATE_ONLINE so proper offlining occurs. */ - old_file->archive = true; - - /* Currently, btrfs is not very good with out write patterns - * and fragments heavily. Let's defrag our journal files when - * we archive them */ - old_file->defrag_on_close = true; - - r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); - - if (deferred_closes && - set_put(deferred_closes, old_file) >= 0) - (void) journal_file_set_offline(old_file, false); - else - (void) journal_file_close(old_file); - - *f = new_file; - return r; -} - -int journal_file_open_reliably( - const char *fname, - int flags, - mode_t mode, - bool compress, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournalFile *template, - JournalFile **ret) { - - int r; - size_t l; - _cleanup_free_ char *p = NULL; - - r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); - if (!IN_SET(r, - -EBADMSG, /* corrupted */ - -ENODATA, /* truncated */ - -EHOSTDOWN, /* other machine */ - -EPROTONOSUPPORT, /* incompatible feature */ - -EBUSY, /* unclean shutdown */ - -ESHUTDOWN, /* already archived */ - -EIO, /* IO error, including SIGBUS on mmap */ - -EIDRM /* File has been deleted */)) - return r; - - if ((flags & O_ACCMODE) == O_RDONLY) - return r; - - if (!(flags & O_CREAT)) - return r; - - if (!endswith(fname, ".journal")) - return r; - - /* The file is corrupted. Rotate it away and try it again (but only once) */ - - l = strlen(fname); - if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~", - (int) l - 8, fname, - now(CLOCK_REALTIME), - random_u64()) < 0) - return -ENOMEM; - - if (rename(fname, p) < 0) - return -errno; - - /* btrfs doesn't cope well with our write pattern and - * fragments heavily. Let's defrag all files we rotate */ - - (void) chattr_path(p, 0, FS_NOCOW_FL); - (void) btrfs_defrag(p); - - log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); - - return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); -} - -int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { - uint64_t i, n; - uint64_t q, xor_hash = 0; - int r; - EntryItem *items; - dual_timestamp ts; - - assert(from); - assert(to); - assert(o); - assert(p); - - if (!to->writable) - return -EPERM; - - ts.monotonic = le64toh(o->entry.monotonic); - ts.realtime = le64toh(o->entry.realtime); - - n = journal_file_entry_n_items(o); - /* alloca() can't take 0, hence let's allocate at least one */ - items = alloca(sizeof(EntryItem) * MAX(1u, n)); - - for (i = 0; i < n; i++) { - uint64_t l, h; - le64_t le_hash; - size_t t; - void *data; - Object *u; - - q = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[i].hash; - - r = journal_file_move_to_object(from, OBJECT_DATA, q, &o); - if (r < 0) - return r; - - if (le_hash != o->data.hash) - return -EBADMSG; - - l = le64toh(o->object.size) - offsetof(Object, data.payload); - t = (size_t) l; - - /* We hit the limit on 32bit machines */ - if ((uint64_t) t != l) - return -E2BIG; - - if (o->object.flags & OBJECT_COMPRESSION_MASK) { -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - size_t rsize = 0; - - r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, - o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0); - if (r < 0) - return r; - - data = from->compress_buffer; - l = rsize; -#else - return -EPROTONOSUPPORT; -#endif - } else - data = o->data.payload; - - r = journal_file_append_data(to, data, l, &u, &h); - if (r < 0) - return r; - - xor_hash ^= le64toh(u->data.hash); - items[i].object_offset = htole64(h); - items[i].hash = u->data.hash; - - r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - } - - r = journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset); - - if (mmap_cache_got_sigbus(to->mmap, to->fd)) - return -EIO; - - 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) { - char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX]; - struct statvfs ss; - 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) { - - if (fs_size > 0) { - m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ - - if (m->max_use > DEFAULT_MAX_USE_UPPER) - m->max_use = DEFAULT_MAX_USE_UPPER; - - if (m->max_use < DEFAULT_MAX_USE_LOWER) - m->max_use = DEFAULT_MAX_USE_LOWER; - } else - m->max_use = DEFAULT_MAX_USE_LOWER; - } else { - m->max_use = PAGE_ALIGN(m->max_use); - - if (m->max_use != 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 */ - - if (m->max_size > DEFAULT_MAX_SIZE_UPPER) - m->max_size = DEFAULT_MAX_SIZE_UPPER; - } else - m->max_size = PAGE_ALIGN(m->max_size); - - if (m->max_size != 0) { - if (m->max_size < JOURNAL_FILE_SIZE_MIN) - m->max_size = JOURNAL_FILE_SIZE_MIN; - - 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; - else { - m->min_size = PAGE_ALIGN(m->min_size); - - if (m->min_size < JOURNAL_FILE_SIZE_MIN) - m->min_size = JOURNAL_FILE_SIZE_MIN; - - if (m->max_size != 0 && m->min_size > m->max_size) - m->max_size = m->min_size; - } - - if (m->keep_free == (uint64_t) -1) { - - if (fs_size > 0) { - m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ - - if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) - m->keep_free = DEFAULT_KEEP_FREE_UPPER; - - } else - m->keep_free = DEFAULT_KEEP_FREE; - } - - 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) { - assert(f); - assert(f->header); - assert(from || to); - - if (from) { - if (f->header->head_entry_realtime == 0) - return -ENOENT; - - *from = le64toh(f->header->head_entry_realtime); - } - - if (to) { - if (f->header->tail_entry_realtime == 0) - return -ENOENT; - - *to = le64toh(f->header->tail_entry_realtime); - } - - return 1; -} - -int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) { - Object *o; - uint64_t p; - int r; - - assert(f); - assert(from || to); - - r = find_data_object_by_boot_id(f, boot_id, &o, &p); - if (r <= 0) - return r; - - if (le64toh(o->data.n_entries) <= 0) - return 0; - - if (from) { - r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o); - if (r < 0) - return r; - - *from = le64toh(o->entry.monotonic); - } - - if (to) { - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - r = generic_array_get_plus_one(f, - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - le64toh(o->data.n_entries)-1, - &o, NULL); - if (r <= 0) - return r; - - *to = le64toh(o->entry.monotonic); - } - - return 1; -} - -bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { - assert(f); - assert(f->header); - - /* If we gained new header fields we gained new features, - * hence suggest a rotation */ - if (le64toh(f->header->header_size) < sizeof(Header)) { - log_debug("%s uses an outdated header, suggesting rotation.", f->path); - return true; - } - - /* Let's check if the hash tables grew over a certain fill - * level (75%, borrowing this value from Java's hash table - * implementation), and if so suggest a rotation. To calculate - * the fill level we need the n_data field, which only exists - * in newer versions. */ - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) - if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) { - log_debug("Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.", - f->path, - 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))), - le64toh(f->header->n_data), - le64toh(f->header->data_hash_table_size) / sizeof(HashItem), - (unsigned long long) f->last_stat.st_size, - f->last_stat.st_size / le64toh(f->header->n_data)); - return true; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) - if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) { - log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.", - f->path, - 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))), - le64toh(f->header->n_fields), - le64toh(f->header->field_hash_table_size) / sizeof(HashItem)); - return true; - } - - /* Are the data objects properly indexed by field objects? */ - if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && - JOURNAL_HEADER_CONTAINS(f->header, n_fields) && - le64toh(f->header->n_data) > 0 && - le64toh(f->header->n_fields) == 0) - return true; - - if (max_file_usec > 0) { - usec_t t, h; - - h = le64toh(f->header->head_entry_realtime); - t = now(CLOCK_REALTIME); - - if (h > 0 && t > h + max_file_usec) - return true; - } - - return false; -} diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h deleted file mode 100644 index 564e1a8179..0000000000 --- a/src/journal/journal-file.h +++ /dev/null @@ -1,265 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#ifdef HAVE_GCRYPT -#include -#endif - -#include "sd-id128.h" - -#include "hashmap.h" -#include "journal-def.h" -#include "macro.h" -#include "mmap-cache.h" -#include "sd-event.h" -#include "sparse-endian.h" - -typedef struct JournalMetrics { - /* 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 { - DIRECTION_UP, - DIRECTION_DOWN -} direction_t; - -typedef enum LocationType { - /* The first and last entries, resp. */ - LOCATION_HEAD, - LOCATION_TAIL, - - /* We already read the entry we currently point to, and the - * next one to read should probably not be this one again. */ - LOCATION_DISCRETE, - - /* We should seek to the precise location specified, and - * return it, as we haven't read it yet. */ - LOCATION_SEEK -} LocationType; - -typedef enum OfflineState { - OFFLINE_JOINED, - OFFLINE_SYNCING, - OFFLINE_OFFLINING, - OFFLINE_CANCEL, - OFFLINE_AGAIN_FROM_SYNCING, - OFFLINE_AGAIN_FROM_OFFLINING, - OFFLINE_DONE -} OfflineState; - -typedef struct JournalFile { - int fd; - - mode_t mode; - - int flags; - int prot; - bool writable:1; - bool compress_xz:1; - bool compress_lz4:1; - bool seal:1; - bool defrag_on_close:1; - bool close_fd:1; - bool archive:1; - - bool tail_entry_monotonic_valid:1; - - direction_t last_direction; - LocationType location_type; - uint64_t last_n_entries; - - char *path; - struct stat last_stat; - usec_t last_stat_usec; - - Header *header; - HashItem *data_hash_table; - HashItem *field_hash_table; - - uint64_t current_offset; - uint64_t current_seqnum; - uint64_t current_realtime; - uint64_t current_monotonic; - sd_id128_t current_boot_id; - uint64_t current_xor_hash; - - JournalMetrics metrics; - MMapCache *mmap; - - sd_event_source *post_change_timer; - usec_t post_change_timer_period; - - OrderedHashmap *chain_cache; - - pthread_t offline_thread; - volatile OfflineState offline_state; - -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - void *compress_buffer; - size_t compress_buffer_size; -#endif - -#ifdef HAVE_GCRYPT - gcry_md_hd_t hmac; - bool hmac_running; - - FSSHeader *fss_file; - size_t fss_file_size; - - uint64_t fss_start_usec; - uint64_t fss_interval_usec; - - void *fsprg_state; - size_t fsprg_state_size; - - void *fsprg_seed; - size_t fsprg_seed_size; -#endif -} JournalFile; - -int journal_file_open( - int fd, - const char *fname, - int flags, - mode_t mode, - bool compress, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournalFile *template, - JournalFile **ret); - -int journal_file_set_offline(JournalFile *f, bool wait); -bool journal_file_is_offlining(JournalFile *f); -JournalFile* journal_file_close(JournalFile *j); -void journal_file_close_set(Set *s); - -int journal_file_open_reliably( - const char *fname, - int flags, - mode_t mode, - bool compress, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournalFile *template, - JournalFile **ret); - -#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) -#define VALID64(x) (((x) & 7ULL) == 0ULL) - -/* Use six characters to cover the offsets common in smallish journal - * files without adding too many zeros. */ -#define OFSfmt "%06"PRIx64 - -static inline bool VALID_REALTIME(uint64_t u) { - /* This considers timestamps until the year 3112 valid. That should be plenty room... */ - return u > 0 && u < (1ULL << 55); -} - -static inline bool VALID_MONOTONIC(uint64_t u) { - /* This considers timestamps until 1142 years of runtime valid. */ - return u < (1ULL << 55); -} - -static inline bool VALID_EPOCH(uint64_t u) { - /* This allows changing the key for 1142 years, every usec. */ - return u < (1ULL << 55); -} - -#define JOURNAL_HEADER_CONTAINS(h, field) \ - (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) - -#define JOURNAL_HEADER_SEALED(h) \ - (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED)) - -#define JOURNAL_HEADER_COMPRESSED_XZ(h) \ - (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) - -#define JOURNAL_HEADER_COMPRESSED_LZ4(h) \ - (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) - -int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); - -uint64_t journal_file_entry_n_items(Object *o) _pure_; -uint64_t journal_file_entry_array_n_items(Object *o) _pure_; -uint64_t journal_file_hash_table_n_items(Object *o) _pure_; - -int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); - -int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); - -int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); - -void journal_file_reset_location(JournalFile *f); -void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset); -int journal_file_compare_locations(JournalFile *af, JournalFile *bf); -int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); - -int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); - -int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); - -int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); - -int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset); - -void journal_file_dump(JournalFile *f); -void journal_file_print_header(JournalFile *f); - -int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes); - -void journal_file_post_change(JournalFile *f); -int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); - -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); -int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); - -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 deleted file mode 100644 index 34a48141f5..0000000000 --- a/src/journal/journal-internal.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-id128.h" -#include "sd-journal.h" - -#include "hashmap.h" -#include "journal-def.h" -#include "journal-file.h" -#include "list.h" -#include "set.h" - -typedef struct Match Match; -typedef struct Location Location; -typedef struct Directory Directory; - -typedef enum MatchType { - MATCH_DISCRETE, - MATCH_OR_TERM, - MATCH_AND_TERM -} MatchType; - -struct Match { - MatchType type; - Match *parent; - LIST_FIELDS(Match, matches); - - /* For concrete matches */ - char *data; - size_t size; - le64_t le_hash; - - /* For terms */ - LIST_HEAD(Match, matches); -}; - -struct Location { - LocationType type; - - bool seqnum_set; - bool realtime_set; - bool monotonic_set; - bool xor_hash_set; - - uint64_t seqnum; - sd_id128_t seqnum_id; - - uint64_t realtime; - - uint64_t monotonic; - sd_id128_t boot_id; - - uint64_t xor_hash; -}; - -struct Directory { - char *path; - int wd; - bool is_root; -}; - -struct sd_journal { - int toplevel_fd; - - char *path; - char *prefix; - - OrderedHashmap *files; - MMapCache *mmap; - - Location current_location; - - JournalFile *current_file; - uint64_t current_field; - - Match *level0, *level1, *level2; - - pid_t original_pid; - - int inotify_fd; - unsigned current_invalidate_counter, last_invalidate_counter; - usec_t last_process_usec; - - /* Iterating through unique fields and their data values */ - char *unique_field; - JournalFile *unique_file; - uint64_t unique_offset; - - /* Iterating through known fields */ - JournalFile *fields_file; - uint64_t fields_offset; - uint64_t fields_hash_table_index; - char *fields_buffer; - size_t fields_buffer_allocated; - - int flags; - - bool on_network:1; - bool no_new_files:1; - bool no_inotify:1; - bool unique_file_lost:1; /* File we were iterating over got - removed, and there were no more - files, so sd_j_enumerate_unique - will return a value equal to 0. */ - bool fields_file_lost:1; - bool has_runtime_files:1; - bool has_persistent_files:1; - - size_t data_threshold; - - Hashmap *directories_by_path; - Hashmap *directories_by_wd; - - Hashmap *errors; -}; - -char *journal_make_match_string(sd_journal *j); -void journal_print_header(sd_journal *j); - -#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 deleted file mode 100644 index e38730d65c..0000000000 --- a/src/journal/journal-qrcode.c +++ /dev/null @@ -1,135 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "journal-qrcode.h" - -#define WHITE_ON_BLACK "\033[40;37;1m" -#define NORMAL "\033[0m" - -static void print_border(FILE *output, unsigned width) { - unsigned x, y; - - /* Four rows of border */ - for (y = 0; y < 4; y += 2) { - fputs(WHITE_ON_BLACK, output); - - for (x = 0; x < 4 + width + 4; x++) - fputs("\342\226\210", output); - - fputs(NORMAL "\n", output); - } -} - -int print_qr_code( - FILE *output, - const void *seed, - size_t seed_size, - uint64_t start, - uint64_t interval, - const char *hn, - sd_id128_t machine) { - - FILE *f; - char *url = NULL; - size_t url_size = 0, i; - QRcode* qr; - unsigned x, y; - - assert(seed); - assert(seed_size > 0); - - f = open_memstream(&url, &url_size); - if (!f) - return -ENOMEM; - - fputs("fss://", f); - - for (i = 0; i < seed_size; i++) { - if (i > 0 && i % 3 == 0) - fputc('-', f); - fprintf(f, "%02x", ((uint8_t*) seed)[i]); - } - - fprintf(f, "/%"PRIx64"-%"PRIx64"?machine=" SD_ID128_FORMAT_STR, - start, - interval, - SD_ID128_FORMAT_VAL(machine)); - - if (hn) - fprintf(f, ";hostname=%s", hn); - - if (ferror(f)) { - fclose(f); - free(url); - return -ENOMEM; - } - - fclose(f); - - qr = QRcode_encodeString(url, 0, QR_ECLEVEL_L, QR_MODE_8, 1); - free(url); - - if (!qr) - return -ENOMEM; - - print_border(output, qr->width); - - for (y = 0; y < (unsigned) qr->width; y += 2) { - const uint8_t *row1, *row2; - - row1 = qr->data + qr->width * y; - row2 = row1 + qr->width; - - fputs(WHITE_ON_BLACK, output); - for (x = 0; x < 4; x++) - fputs("\342\226\210", output); - - for (x = 0; x < (unsigned) qr->width; x ++) { - bool a, b; - - a = row1[x] & 1; - b = (y+1) < (unsigned) qr->width ? (row2[x] & 1) : false; - - if (a && b) - fputc(' ', output); - else if (a) - fputs("\342\226\204", output); - else if (b) - fputs("\342\226\200", output); - else - fputs("\342\226\210", output); - } - - for (x = 0; x < 4; x++) - fputs("\342\226\210", output); - fputs(NORMAL "\n", output); - } - - print_border(output, qr->width); - - QRcode_free(qr); - return 0; -} diff --git a/src/journal/journal-qrcode.h b/src/journal/journal-qrcode.h deleted file mode 100644 index ef39085561..0000000000 --- a/src/journal/journal-qrcode.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#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 deleted file mode 100644 index 440fba67ca..0000000000 --- a/src/journal/journal-send.c +++ /dev/null @@ -1,575 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#define SD_JOURNAL_SUPPRESS_LOCATION - -#include "sd-journal.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) - -#define ALLOCA_CODE_FUNC(f, func) \ - do { \ - size_t _fl; \ - const char *_func = (func); \ - char **_f = &(f); \ - _fl = strlen(_func) + 1; \ - *_f = alloca(_fl + 10); \ - memcpy(*_f, "CODE_FUNC=", 10); \ - memcpy(*_f + 10, _func, _fl); \ - } while (false) - -/* We open a single fd, and we'll share it with the current process, - * all its threads, and all its subprocesses. This means we need to - * initialize it atomically, and need to operate on it atomically - * never assuming we are the only user */ - -static int journal_fd(void) { - int fd; - static int fd_plus_one = 0; - -retry: - if (fd_plus_one > 0) - return fd_plus_one - 1; - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - fd_inc_sndbuf(fd, SNDBUF_SIZE); - - if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) { - safe_close(fd); - goto retry; - } - - return fd; -} - -_public_ int sd_journal_print(int priority, const char *format, ...) { - int r; - va_list ap; - - va_start(ap, format); - r = sd_journal_printv(priority, format, ap); - va_end(ap); - - return r; -} - -_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { - - /* FIXME: Instead of limiting things to LINE_MAX we could do a - C99 variable-length array on the stack here in a loop. */ - - char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; - struct iovec iov[2]; - - assert_return(priority >= 0, -EINVAL); - assert_return(priority <= 7, -EINVAL); - assert_return(format, -EINVAL); - - xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); - - memcpy(buffer, "MESSAGE=", 8); - vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); - - /* Strip trailing whitespace, keep prefix whitespace. */ - (void) strstrip(buffer); - - /* Suppress empty lines */ - if (isempty(buffer+8)) - return 0; - - zero(iov); - IOVEC_SET_STRING(iov[0], buffer); - IOVEC_SET_STRING(iov[1], p); - - return sd_journal_sendv(iov, 2); -} - -_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { - PROTECT_ERRNO; - int r, n = 0, i = 0, j; - struct iovec *iov = NULL; - - assert(_iov); - - if (extra > 0) { - n = MAX(extra * 2, extra + 4); - iov = malloc0(n * sizeof(struct iovec)); - if (!iov) { - r = -ENOMEM; - goto fail; - } - - i = extra; - } - - while (format) { - struct iovec *c; - char *buffer; - va_list aq; - - if (i >= n) { - n = MAX(i*2, 4); - c = realloc(iov, n * sizeof(struct iovec)); - if (!c) { - r = -ENOMEM; - goto fail; - } - - iov = c; - } - - va_copy(aq, ap); - if (vasprintf(&buffer, format, aq) < 0) { - va_end(aq); - r = -ENOMEM; - goto fail; - } - va_end(aq); - - VA_FORMAT_ADVANCE(format, ap); - - (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */ - - IOVEC_SET_STRING(iov[i++], buffer); - - format = va_arg(ap, char *); - } - - *_iov = iov; - - return i; - -fail: - for (j = 0; j < i; j++) - free(iov[j].iov_base); - - free(iov); - - return r; -} - -_public_ int sd_journal_send(const char *format, ...) { - int r, i, j; - va_list ap; - struct iovec *iov = NULL; - - va_start(ap, format); - i = fill_iovec_sprintf(format, ap, 0, &iov); - va_end(ap); - - if (_unlikely_(i < 0)) { - r = i; - goto finish; - } - - r = sd_journal_sendv(iov, i); - -finish: - for (j = 0; j < i; j++) - free(iov[j].iov_base); - - free(iov); - - return r; -} - -_public_ int sd_journal_sendv(const struct iovec *iov, int n) { - PROTECT_ERRNO; - int fd, r; - _cleanup_close_ int buffer_fd = -1; - struct iovec *w; - uint64_t *l; - int i, j = 0; - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - struct msghdr mh = { - .msg_name = (struct sockaddr*) &sa.sa, - .msg_namelen = SOCKADDR_UN_LEN(sa.un), - }; - ssize_t k; - bool have_syslog_identifier = false; - bool seal = true; - - assert_return(iov, -EINVAL); - assert_return(n > 0, -EINVAL); - - w = newa(struct iovec, n * 5 + 3); - l = newa(uint64_t, n); - - for (i = 0; i < n; i++) { - char *c, *nl; - - if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) - return -EINVAL; - - c = memchr(iov[i].iov_base, '=', iov[i].iov_len); - if (_unlikely_(!c || c == iov[i].iov_base)) - return -EINVAL; - - have_syslog_identifier = have_syslog_identifier || - (c == (char *) iov[i].iov_base + 17 && - startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER")); - - nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); - if (nl) { - if (_unlikely_(nl < c)) - return -EINVAL; - - /* Already includes a newline? Bummer, then - * let's write the variable name, then a - * newline, then the size (64bit LE), followed - * by the data and a final newline */ - - w[j].iov_base = iov[i].iov_base; - w[j].iov_len = c - (char*) iov[i].iov_base; - j++; - - IOVEC_SET_STRING(w[j++], "\n"); - - l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); - w[j].iov_base = &l[i]; - w[j].iov_len = sizeof(uint64_t); - j++; - - w[j].iov_base = c + 1; - w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; - j++; - - } else - /* Nothing special? Then just add the line and - * append a newline */ - w[j++] = iov[i]; - - IOVEC_SET_STRING(w[j++], "\n"); - } - - if (!have_syslog_identifier && - string_is_safe(program_invocation_short_name)) { - - /* Implicitly add program_invocation_short_name, if it - * is not set explicitly. We only do this for - * program_invocation_short_name, and nothing else - * since everything else is much nicer to retrieve - * from the outside. */ - - IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER="); - IOVEC_SET_STRING(w[j++], program_invocation_short_name); - IOVEC_SET_STRING(w[j++], "\n"); - } - - fd = journal_fd(); - if (_unlikely_(fd < 0)) - return fd; - - mh.msg_iov = w; - mh.msg_iovlen = j; - - k = sendmsg(fd, &mh, MSG_NOSIGNAL); - if (k >= 0) - return 0; - - /* Fail silently if the journal is not available */ - if (errno == ENOENT) - return 0; - - if (errno != EMSGSIZE && errno != ENOBUFS) - return -errno; - - /* Message doesn't fit... Let's dump the data in a memfd or - * temporary file and just pass a file descriptor of it to the - * other side. - * - * For the temporary files we use /dev/shm instead of /tmp - * here, since we want this to be a tmpfs, and one that is - * available from early boot on and where unprivileged users - * can create files. */ - buffer_fd = memfd_new(NULL); - if (buffer_fd < 0) { - if (buffer_fd == -ENOSYS) { - buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); - if (buffer_fd < 0) - return buffer_fd; - - seal = false; - } else - return buffer_fd; - } - - n = writev(buffer_fd, w, j); - if (n < 0) - return -errno; - - if (seal) { - r = memfd_set_sealed(buffer_fd); - if (r < 0) - return r; - } - - 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[]) { - PROTECT_ERRNO; - size_t n, k; - - k = isempty(message) ? 0 : strlen(message) + 2; - n = 8 + k + 256 + 1; - - for (;;) { - char buffer[n]; - char* j; - - errno = 0; - j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); - if (errno == 0) { - char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1]; - - if (j != buffer + 8 + k) - memmove(buffer + 8 + k, j, strlen(j)+1); - - memcpy(buffer, "MESSAGE=", 8); - - if (k > 0) { - memcpy(buffer + 8, message, k - 2); - memcpy(buffer + 8 + k - 2, ": ", 2); - } - - xsprintf(error, "ERRNO=%i", _saved_errno_); - - assert_cc(3 == LOG_ERR); - IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); - IOVEC_SET_STRING(iov[skip+1], buffer); - IOVEC_SET_STRING(iov[skip+2], error); - - return sd_journal_sendv(iov, skip + 3); - } - - if (errno != ERANGE) - return -errno; - - n *= 2; - } -} - -_public_ int sd_journal_perror(const char *message) { - struct iovec iovec[3]; - - return fill_iovec_perror_and_send(message, 0, iovec); -} - -_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; - _cleanup_close_ int fd = -1; - char *header; - size_t l; - int r; - - assert_return(priority >= 0, -EINVAL); - assert_return(priority <= 7, -EINVAL); - - fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return -errno; - - if (shutdown(fd, SHUT_RD) < 0) - return -errno; - - fd_inc_sndbuf(fd, SNDBUF_SIZE); - - if (!identifier) - identifier = ""; - - l = strlen(identifier); - header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2); - - memcpy(header, identifier, l); - header[l++] = '\n'; - header[l++] = '\n'; /* unit id */ - header[l++] = '0' + priority; - header[l++] = '\n'; - header[l++] = '0' + !!level_prefix; - header[l++] = '\n'; - header[l++] = '0'; - header[l++] = '\n'; - header[l++] = '0'; - header[l++] = '\n'; - header[l++] = '0'; - header[l++] = '\n'; - - r = loop_write(fd, header, l, false); - if (r < 0) - return r; - - r = fd; - fd = -1; - return r; -} - -_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { - int r; - va_list ap; - - va_start(ap, format); - r = sd_journal_printv_with_location(priority, file, line, func, format, ap); - va_end(ap); - - return r; -} - -_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { - char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; - struct iovec iov[5]; - char *f; - - assert_return(priority >= 0, -EINVAL); - assert_return(priority <= 7, -EINVAL); - assert_return(format, -EINVAL); - - xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); - - memcpy(buffer, "MESSAGE=", 8); - vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); - - /* Strip trailing whitespace, keep prefixing whitespace */ - (void) strstrip(buffer); - - /* Suppress empty lines */ - if (isempty(buffer+8)) - return 0; - - /* func is initialized from __func__ which is not a macro, but - * a static const char[], hence cannot easily be prefixed with - * CODE_FUNC=, hence let's do it manually here. */ - ALLOCA_CODE_FUNC(f, func); - - zero(iov); - IOVEC_SET_STRING(iov[0], buffer); - IOVEC_SET_STRING(iov[1], p); - IOVEC_SET_STRING(iov[2], file); - IOVEC_SET_STRING(iov[3], line); - IOVEC_SET_STRING(iov[4], f); - - return sd_journal_sendv(iov, ELEMENTSOF(iov)); -} - -_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) { - int r, i, j; - va_list ap; - struct iovec *iov = NULL; - char *f; - - va_start(ap, format); - i = fill_iovec_sprintf(format, ap, 3, &iov); - va_end(ap); - - if (_unlikely_(i < 0)) { - r = i; - goto finish; - } - - ALLOCA_CODE_FUNC(f, func); - - IOVEC_SET_STRING(iov[0], file); - IOVEC_SET_STRING(iov[1], line); - IOVEC_SET_STRING(iov[2], f); - - r = sd_journal_sendv(iov, i); - -finish: - for (j = 3; j < i; j++) - free(iov[j].iov_base); - - free(iov); - - return r; -} - -_public_ int sd_journal_sendv_with_location( - const char *file, const char *line, - const char *func, - const struct iovec *iov, int n) { - - struct iovec *niov; - char *f; - - assert_return(iov, -EINVAL); - assert_return(n > 0, -EINVAL); - - niov = alloca(sizeof(struct iovec) * (n + 3)); - memcpy(niov, iov, sizeof(struct iovec) * n); - - ALLOCA_CODE_FUNC(f, func); - - IOVEC_SET_STRING(niov[n++], file); - IOVEC_SET_STRING(niov[n++], line); - IOVEC_SET_STRING(niov[n++], f); - - return sd_journal_sendv(niov, n); -} - -_public_ int sd_journal_perror_with_location( - const char *file, const char *line, - const char *func, - const char *message) { - - struct iovec iov[6]; - char *f; - - ALLOCA_CODE_FUNC(f, func); - - IOVEC_SET_STRING(iov[0], file); - IOVEC_SET_STRING(iov[1], line); - IOVEC_SET_STRING(iov[2], f); - - return fill_iovec_perror_and_send(message, 3, iov); -} diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c deleted file mode 100644 index f09dc66e03..0000000000 --- a/src/journal/journal-vacuum.c +++ /dev/null @@ -1,349 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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 "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; -}; - -static int vacuum_compare(const void *_a, const void *_b) { - const struct vacuum_info *a, *b; - - a = _a; - b = _b; - - if (a->have_seqnum && b->have_seqnum && - sd_id128_equal(a->seqnum_id, b->seqnum_id)) { - if (a->seqnum < b->seqnum) - return -1; - else if (a->seqnum > b->seqnum) - return 1; - else - return 0; - } - - if (a->realtime < b->realtime) - return -1; - else if (a->realtime > b->realtime) - return 1; - else if (a->have_seqnum && b->have_seqnum) - return memcmp(&a->seqnum_id, &b->seqnum_id, 16); - else - return strcmp(a->filename, b->filename); -} - -static void patch_realtime( - int fd, - const char *fn, - const struct stat *st, - unsigned long long *realtime) { - - 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(fd >= 0); - assert(fn); - assert(st); - assert(realtime); - - x = timespec_load(&st->st_ctim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) - *realtime = x; - - x = timespec_load(&st->st_atim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) - *realtime = x; - - x = timespec_load(&st->st_mtim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) - *realtime = x; - - /* Let's read the original creation time, if possible. Ideally - * we'd just query the creation time the FS might provide, but - * unfortunately there's currently no sane API to query - * it. Hence let's implement this manually... */ - - if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) { - if (crtime < *realtime) - *realtime = crtime; - } -} - -static int journal_file_empty(int dir_fd, const char *name) { - _cleanup_close_ int fd; - struct stat st; - le64_t n_entries; - ssize_t n; - - 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; - - /* If an offline file doesn't even have a header we consider it empty */ - if (st.st_size < (off_t) sizeof(Header)) - return 1; - - /* If the number of entries is empty, we consider it empty, too */ - n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)); - if (n < 0) - return -errno; - if (n != sizeof(n_entries)) - return -EIO; - - return le64toh(n_entries) <= 0; -} - -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; - struct vacuum_info *list = NULL; - 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 && n_max_files <= 0) - return 0; - - if (max_retention_usec > 0) { - retention_limit = now(CLOCK_REALTIME); - if (retention_limit > max_retention_usec) - retention_limit -= max_retention_usec; - else - max_retention_usec = retention_limit = 0; - } - - d = opendir(directory); - if (!d) - return -errno; - - 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; - - 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; - - q = strlen(de->d_name); - - if (endswith(de->d_name, ".journal")) { - - /* Vacuum archived files. Active files are - * left around */ - - 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] != '@') { - n_active_files++; - continue; - } - - p = strdup(de->d_name); - if (!p) { - r = -ENOMEM; - goto finish; - } - - de->d_name[q-8-16-1-16-1] = 0; - if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { - n_active_files++; - continue; - } - - if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { - n_active_files++; - continue; - } - - have_seqnum = true; - - } else if (endswith(de->d_name, ".journal~")) { - unsigned long long tmp; - - /* Vacuum corrupted files */ - - 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] != '@') { - n_active_files++; - continue; - } - - p = strdup(de->d_name); - if (!p) { - r = -ENOMEM; - goto finish; - } - - if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { - n_active_files++; - continue; - } - - have_seqnum = false; - } else { - /* We do not vacuum unknown files! */ - log_debug("Not vacuuming unknown file %s.", de->d_name); - continue; - } - - 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)); - - freed += size; - } else if (errno != ENOENT) - log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); - - continue; - } - - patch_realtime(dirfd(d), p, &st, &realtime); - - if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { - r = -ENOMEM; - goto finish; - } - - list[n_list].filename = p; - 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; - 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) && - (n_max_files <= 0 || left <= n_max_files)) - break; - - if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { - log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)); - freed += list[i].usage; - - if (list[i].usage < sum) - sum -= list[i].usage; - else - sum = 0; - - } else if (errno != ENOENT) - log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename); - } - - 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); - free(list); - - log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes, sizeof(sbytes), freed)); - - return r; -} diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h deleted file mode 100644 index 1e750a2170..0000000000 --- a/src/journal/journal-vacuum.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 deleted file mode 100644 index f61f158e8a..0000000000 --- a/src/journal/journal-verify.c +++ /dev/null @@ -1,1308 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "compress.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "journal-authenticate.h" -#include "journal-def.h" -#include "journal-file.h" -#include "journal-verify.h" -#include "lookup3.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; - usec_t z, x; - - if (!on_tty()) - return; - - z = now(CLOCK_MONOTONIC); - x = *last_usec; - - if (x != 0 && x + 40 * USEC_PER_MSEC > z) - return; - - *last_usec = z; - - n = (3 * columns()) / 4; - j = (n * (unsigned) p) / 65535ULL; - k = n - j; - - fputs("\r", stdout); - if (colors_enabled()) - fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout); - - for (i = 0; i < j; i++) - fputs("\xe2\x96\x88", stdout); - - fputs(ANSI_NORMAL, stdout); - - for (i = 0; i < k; i++) - fputs("\xe2\x96\x91", stdout); - - printf(" %3"PRIu64"%%", 100U * p / 65535U); - - fputs("\r", stdout); - if (colors_enabled()) - fputs("\x1B[?25h", stdout); - - fflush(stdout); -} - -static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) { - - /* Calculates scale * p / m, but handles m == 0 safely, and saturates */ - - if (p >= m || m == 0) - return scale; - - return scale * p / m; -} - -static void flush_progress(void) { - unsigned n, i; - - if (!on_tty()) - return; - - n = (3 * columns()) / 4; - - putchar('\r'); - - for (i = 0; i < n + 5; i++) - putchar(' '); - - putchar('\r'); - fflush(stdout); -} - -#define debug(_offset, _fmt, ...) do { \ - flush_progress(); \ - log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ - } while (0) - -#define warning(_offset, _fmt, ...) do { \ - flush_progress(); \ - log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ - } while (0) - -#define error(_offset, _fmt, ...) do { \ - flush_progress(); \ - log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ - } while (0) - -static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { - uint64_t i; - - assert(f); - assert(offset); - assert(o); - - /* This does various superficial tests about the length an - * possible field values. It does not follow any references to - * other objects. */ - - if ((o->object.flags & OBJECT_COMPRESSED_XZ) && - o->object.type != OBJECT_DATA) { - error(offset, "Found compressed object that isn't of type DATA, which is not allowed."); - return -EBADMSG; - } - - switch (o->object.type) { - - case OBJECT_DATA: { - uint64_t h1, h2; - int compression, r; - - if (le64toh(o->data.entry_offset) == 0) - warning(offset, "Unused data (entry_offset==0)"); - - if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) { - error(offset, "Bad n_entries: %"PRIu64, o->data.n_entries); - return -EBADMSG; - } - - if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) { - error(offset, "Bad object size (<= %zu): %"PRIu64, - offsetof(DataObject, payload), - le64toh(o->object.size)); - return -EBADMSG; - } - - h1 = le64toh(o->data.hash); - - compression = o->object.flags & OBJECT_COMPRESSION_MASK; - if (compression) { - _cleanup_free_ void *b = NULL; - size_t alloc = 0, b_size; - - r = decompress_blob(compression, - o->data.payload, - le64toh(o->object.size) - offsetof(Object, data.payload), - &b, &alloc, &b_size, 0); - if (r < 0) { - error(offset, "%s decompression failed: %s", - object_compressed_to_string(compression), strerror(-r)); - return r; - } - - h2 = hash64(b, b_size); - } else - h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); - - if (h1 != h2) { - error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2); - return -EBADMSG; - } - - if (!VALID64(o->data.next_hash_offset) || - !VALID64(o->data.next_field_offset) || - !VALID64(o->data.entry_offset) || - !VALID64(o->data.entry_array_offset)) { - error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt, - o->data.next_hash_offset, - o->data.next_field_offset, - o->data.entry_offset, - o->data.entry_array_offset); - return -EBADMSG; - } - - break; - } - - case OBJECT_FIELD: - if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) { - error(offset, - "Bad field size (<= %zu): %"PRIu64, - offsetof(FieldObject, payload), - le64toh(o->object.size)); - return -EBADMSG; - } - - if (!VALID64(o->field.next_hash_offset) || - !VALID64(o->field.head_data_offset)) { - error(offset, - "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt, - o->field.next_hash_offset, - o->field.head_data_offset); - return -EBADMSG; - } - break; - - case OBJECT_ENTRY: - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) { - error(offset, - "Bad entry size (<= %zu): %"PRIu64, - offsetof(EntryObject, items), - le64toh(o->object.size)); - return -EBADMSG; - } - - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) { - error(offset, - "Invalid number items in entry: %"PRIu64, - (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem)); - return -EBADMSG; - } - - if (le64toh(o->entry.seqnum) <= 0) { - error(offset, - "Invalid entry seqnum: %"PRIx64, - le64toh(o->entry.seqnum)); - return -EBADMSG; - } - - if (!VALID_REALTIME(le64toh(o->entry.realtime))) { - error(offset, - "Invalid entry realtime timestamp: %"PRIu64, - le64toh(o->entry.realtime)); - return -EBADMSG; - } - - if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) { - error(offset, - "Invalid entry monotonic timestamp: %"PRIu64, - le64toh(o->entry.monotonic)); - return -EBADMSG; - } - - for (i = 0; i < journal_file_entry_n_items(o); i++) { - if (o->entry.items[i].object_offset == 0 || - !VALID64(o->entry.items[i].object_offset)) { - error(offset, - "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt, - i, journal_file_entry_n_items(o), - o->entry.items[i].object_offset); - return -EBADMSG; - } - } - - break; - - case OBJECT_DATA_HASH_TABLE: - case OBJECT_FIELD_HASH_TABLE: - if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || - (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) { - error(offset, - "Invalid %s hash table size: %"PRIu64, - o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - le64toh(o->object.size)); - return -EBADMSG; - } - - for (i = 0; i < journal_file_hash_table_n_items(o); i++) { - if (o->hash_table.items[i].head_hash_offset != 0 && - !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) { - error(offset, - "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt, - o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - i, journal_file_hash_table_n_items(o), - le64toh(o->hash_table.items[i].head_hash_offset)); - return -EBADMSG; - } - if (o->hash_table.items[i].tail_hash_offset != 0 && - !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) { - error(offset, - "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt, - o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - i, journal_file_hash_table_n_items(o), - le64toh(o->hash_table.items[i].tail_hash_offset)); - return -EBADMSG; - } - - if ((o->hash_table.items[i].head_hash_offset != 0) != - (o->hash_table.items[i].tail_hash_offset != 0)) { - error(offset, - "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt, - o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - i, journal_file_hash_table_n_items(o), - le64toh(o->hash_table.items[i].head_hash_offset), - le64toh(o->hash_table.items[i].tail_hash_offset)); - return -EBADMSG; - } - } - - break; - - case OBJECT_ENTRY_ARRAY: - if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || - (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) { - error(offset, - "Invalid object entry array size: %"PRIu64, - le64toh(o->object.size)); - return -EBADMSG; - } - - if (!VALID64(o->entry_array.next_entry_array_offset)) { - error(offset, - "Invalid object entry array next_entry_array_offset: "OFSfmt, - o->entry_array.next_entry_array_offset); - return -EBADMSG; - } - - for (i = 0; i < journal_file_entry_array_n_items(o); i++) - if (le64toh(o->entry_array.items[i]) != 0 && - !VALID64(le64toh(o->entry_array.items[i]))) { - error(offset, - "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt, - i, journal_file_entry_array_n_items(o), - le64toh(o->entry_array.items[i])); - return -EBADMSG; - } - - break; - - case OBJECT_TAG: - if (le64toh(o->object.size) != sizeof(TagObject)) { - error(offset, - "Invalid object tag size: %"PRIu64, - le64toh(o->object.size)); - return -EBADMSG; - } - - if (!VALID_EPOCH(o->tag.epoch)) { - error(offset, - "Invalid object tag epoch: %"PRIu64, - o->tag.epoch); - return -EBADMSG; - } - - break; - } - - return 0; -} - -static int write_uint64(int fd, uint64_t p) { - ssize_t k; - - k = write(fd, &p, sizeof(p)); - if (k < 0) - return -errno; - if (k != sizeof(p)) - return -EIO; - - return 0; -} - -static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { - uint64_t a, b; - int r; - - assert(m); - assert(fd >= 0); - - /* Bisection ... */ - - a = 0; b = n; - while (a < b) { - uint64_t c, *z; - - c = (a + b) / 2; - - r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); - if (r < 0) - return r; - - if (*z == p) - return 1; - - if (a + 1 >= b) - return 0; - - if (p < *z) - b = c; - else - a = c; - } - - return 0; -} - -static int entry_points_to_data( - JournalFile *f, - int entry_fd, - uint64_t n_entries, - uint64_t entry_p, - uint64_t data_p) { - - int r; - uint64_t i, n, a; - Object *o; - bool found = false; - - assert(f); - assert(entry_fd >= 0); - - if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) { - error(data_p, "Data object references invalid entry at "OFSfmt, entry_p); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o); - if (r < 0) - return r; - - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) - if (le64toh(o->entry.items[i].object_offset) == data_p) { - found = true; - break; - } - - if (!found) { - error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p); - return -EBADMSG; - } - - /* Check if this entry is also in main entry array. Since the - * main entry array has already been verified we can rely on - * its consistency. */ - - i = 0; - n = le64toh(f->header->n_entries); - a = le64toh(f->header->entry_array_offset); - - while (i < n) { - uint64_t m, u; - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - m = journal_file_entry_array_n_items(o); - u = MIN(n - i, m); - - if (entry_p <= le64toh(o->entry_array.items[u-1])) { - uint64_t x, y, z; - - x = 0; - y = u; - - while (x < y) { - z = (x + y) / 2; - - if (le64toh(o->entry_array.items[z]) == entry_p) - return 0; - - if (x + 1 >= y) - break; - - if (entry_p < le64toh(o->entry_array.items[z])) - y = z; - else - x = z; - } - - error(entry_p, "Entry object doesn't exist in main entry array"); - return -EBADMSG; - } - - i += u; - a = le64toh(o->entry_array.next_entry_array_offset); - } - - return 0; -} - -static int verify_data( - JournalFile *f, - Object *o, uint64_t p, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays) { - - uint64_t i, n, a, last, q; - int r; - - assert(f); - assert(o); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); - - n = le64toh(o->data.n_entries); - a = le64toh(o->data.entry_array_offset); - - /* Entry array means at least two objects */ - if (a && n < 2) { - error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n); - return -EBADMSG; - } - - if (n == 0) - return 0; - - /* We already checked that earlier */ - assert(o->data.entry_offset); - - last = q = le64toh(o->data.entry_offset); - r = entry_points_to_data(f, entry_fd, n_entries, q, p); - if (r < 0) - return r; - - i = 1; - while (i < n) { - uint64_t next, m, j; - - if (a == 0) { - error(p, "Array chain too short"); - return -EBADMSG; - } - - if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { - error(p, "Invalid array offset "OFSfmt, a); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - next = le64toh(o->entry_array.next_entry_array_offset); - if (next != 0 && next <= a) { - error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next); - return -EBADMSG; - } - - m = journal_file_entry_array_n_items(o); - for (j = 0; i < n && j < m; i++, j++) { - - q = le64toh(o->entry_array.items[j]); - if (q <= last) { - error(p, "Data object's entry array not sorted"); - return -EBADMSG; - } - last = q; - - r = entry_points_to_data(f, entry_fd, n_entries, q, p); - if (r < 0) - return r; - - /* Pointer might have moved, reposition */ - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - } - - a = next; - } - - return 0; -} - -static int verify_hash_table( - JournalFile *f, - int data_fd, uint64_t n_data, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays, - usec_t *last_usec, - bool show_progress) { - - uint64_t i, n; - int r; - - assert(f); - assert(data_fd >= 0); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); - assert(last_usec); - - n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); - if (n <= 0) - return 0; - - r = journal_file_map_data_hash_table(f); - if (r < 0) - return log_error_errno(r, "Failed to map data hash table: %m"); - - for (i = 0; i < n; i++) { - uint64_t last = 0, p; - - if (show_progress) - draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec); - - p = le64toh(f->data_hash_table[i].head_hash_offset); - while (p != 0) { - Object *o; - uint64_t next; - - if (!contains_uint64(f->mmap, data_fd, n_data, p)) { - error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - next = le64toh(o->data.next_hash_offset); - if (next != 0 && next <= p) { - error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - if (le64toh(o->data.hash) % n != i) { - error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays); - if (r < 0) - return r; - - last = p; - p = next; - } - - if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) { - error(p, "Tail hash pointer mismatch in hash table"); - return -EBADMSG; - } - } - - return 0; -} - -static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) { - uint64_t n, h, q; - int r; - assert(f); - - n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); - if (n <= 0) - return 0; - - r = journal_file_map_data_hash_table(f); - if (r < 0) - return log_error_errno(r, "Failed to map data hash table: %m"); - - h = hash % n; - - q = le64toh(f->data_hash_table[h].head_hash_offset); - while (q != 0) { - Object *o; - - if (p == q) - return 1; - - r = journal_file_move_to_object(f, OBJECT_DATA, q, &o); - if (r < 0) - return r; - - q = le64toh(o->data.next_hash_offset); - } - - return 0; -} - -static int verify_entry( - JournalFile *f, - Object *o, uint64_t p, - int data_fd, uint64_t n_data) { - - uint64_t i, n; - int r; - - assert(f); - assert(o); - assert(data_fd >= 0); - - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) { - uint64_t q, h; - Object *u; - - q = le64toh(o->entry.items[i].object_offset); - h = le64toh(o->entry.items[i].hash); - - if (!contains_uint64(f->mmap, data_fd, n_data, q)) { - error(p, "Invalid data object of entry"); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_DATA, q, &u); - if (r < 0) - return r; - - if (le64toh(u->data.hash) != h) { - error(p, "Hash mismatch for data object of entry"); - return -EBADMSG; - } - - r = data_object_in_hash_table(f, h, q); - if (r < 0) - return r; - if (r == 0) { - error(p, "Data object missing from hash table"); - return -EBADMSG; - } - } - - return 0; -} - -static int verify_entry_array( - JournalFile *f, - int data_fd, uint64_t n_data, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays, - usec_t *last_usec, - bool show_progress) { - - uint64_t i = 0, a, n, last = 0; - int r; - - assert(f); - assert(data_fd >= 0); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); - assert(last_usec); - - n = le64toh(f->header->n_entries); - a = le64toh(f->header->entry_array_offset); - while (i < n) { - uint64_t next, m, j; - Object *o; - - if (show_progress) - draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec); - - if (a == 0) { - error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { - error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - next = le64toh(o->entry_array.next_entry_array_offset); - if (next != 0 && next <= a) { - error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next); - return -EBADMSG; - } - - m = journal_file_entry_array_n_items(o); - for (j = 0; i < n && j < m; i++, j++) { - uint64_t p; - - p = le64toh(o->entry_array.items[j]); - if (p <= last) { - error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - last = p; - - if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { - error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - r = verify_entry(f, o, p, data_fd, n_data); - if (r < 0) - return r; - - /* Pointer might have moved, reposition */ - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - } - - a = next; - } - - return 0; -} - -int journal_file_verify( - JournalFile *f, - const char *key, - usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, - bool show_progress) { - int r; - Object *o; - uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; - - uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; - sd_id128_t entry_boot_id; - bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; - uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; - usec_t last_usec = 0; - int data_fd = -1, entry_fd = -1, entry_array_fd = -1; - unsigned i; - bool found_last = false; - _cleanup_free_ char *tmp_dir = NULL; - -#ifdef HAVE_GCRYPT - uint64_t last_tag = 0; -#endif - assert(f); - - if (key) { -#ifdef HAVE_GCRYPT - r = journal_file_parse_verification_key(f, key); - if (r < 0) { - log_error("Failed to parse seed."); - return r; - } -#else - return -EOPNOTSUPP; -#endif - } else if (f->seal) - return -ENOKEY; - - r = var_tmp(&tmp_dir); - if (r < 0) { - log_error_errno(r, "Failed to determine temporary directory: %m"); - goto fail; - } - - data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); - if (data_fd < 0) { - r = log_error_errno(data_fd, "Failed to create data file: %m"); - goto fail; - } - - entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); - if (entry_fd < 0) { - r = log_error_errno(entry_fd, "Failed to create entry file: %m"); - goto fail; - } - - entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); - if (entry_array_fd < 0) { - r = log_error_errno(entry_array_fd, - "Failed to create entry array file: %m"); - goto fail; - } - - if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) { - log_error("Cannot verify file with unknown extensions."); - r = -EOPNOTSUPP; - goto fail; - } - - for (i = 0; i < sizeof(f->header->reserved); i++) - if (f->header->reserved[i] != 0) { - error(offsetof(Header, reserved[i]), "Reserved field is non-zero"); - r = -EBADMSG; - goto fail; - } - - /* First iteration: we go through all objects, verify the - * superficial structure, headers, hashes. */ - - p = le64toh(f->header->header_size); - for (;;) { - /* Early exit if there are no objects in the file, at all */ - if (le64toh(f->header->tail_object_offset) == 0) - break; - - if (show_progress) - draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec); - - r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); - if (r < 0) { - error(p, "Invalid object"); - goto fail; - } - - if (p > le64toh(f->header->tail_object_offset)) { - error(offsetof(Header, tail_object_offset), "Invalid tail object pointer"); - r = -EBADMSG; - goto fail; - } - - n_objects++; - - r = journal_file_object_verify(f, p, o); - if (r < 0) { - error(p, "Invalid object contents: %s", strerror(-r)); - goto fail; - } - - if ((o->object.flags & OBJECT_COMPRESSED_XZ) && - (o->object.flags & OBJECT_COMPRESSED_LZ4)) { - error(p, "Objected with double compression"); - r = -EINVAL; - goto fail; - } - - if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) { - error(p, "XZ compressed object in file without XZ compression"); - r = -EBADMSG; - goto fail; - } - - if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) { - error(p, "LZ4 compressed object in file without LZ4 compression"); - r = -EBADMSG; - goto fail; - } - - switch (o->object.type) { - - case OBJECT_DATA: - r = write_uint64(data_fd, p); - if (r < 0) - goto fail; - - n_data++; - break; - - case OBJECT_FIELD: - n_fields++; - break; - - case OBJECT_ENTRY: - if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) { - error(p, "First entry before first tag"); - r = -EBADMSG; - goto fail; - } - - r = write_uint64(entry_fd, p); - if (r < 0) - goto fail; - - if (le64toh(o->entry.realtime) < last_tag_realtime) { - error(p, "Older entry after newer tag"); - r = -EBADMSG; - goto fail; - } - - if (!entry_seqnum_set && - le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { - error(p, "Head entry sequence number incorrect"); - r = -EBADMSG; - goto fail; - } - - if (entry_seqnum_set && - entry_seqnum >= le64toh(o->entry.seqnum)) { - error(p, "Entry sequence number out of synchronization"); - r = -EBADMSG; - goto fail; - } - - entry_seqnum = le64toh(o->entry.seqnum); - entry_seqnum_set = true; - - if (entry_monotonic_set && - sd_id128_equal(entry_boot_id, o->entry.boot_id) && - entry_monotonic > le64toh(o->entry.monotonic)) { - error(p, "Entry timestamp out of synchronization"); - r = -EBADMSG; - goto fail; - } - - entry_monotonic = le64toh(o->entry.monotonic); - entry_boot_id = o->entry.boot_id; - entry_monotonic_set = true; - - if (!entry_realtime_set && - le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { - error(p, "Head entry realtime timestamp incorrect"); - r = -EBADMSG; - goto fail; - } - - entry_realtime = le64toh(o->entry.realtime); - entry_realtime_set = true; - - n_entries++; - break; - - case OBJECT_DATA_HASH_TABLE: - if (n_data_hash_tables > 1) { - error(p, "More than one data hash table"); - r = -EBADMSG; - goto fail; - } - - if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || - le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - error(p, "header fields for data hash table invalid"); - r = -EBADMSG; - goto fail; - } - - n_data_hash_tables++; - break; - - case OBJECT_FIELD_HASH_TABLE: - if (n_field_hash_tables > 1) { - error(p, "More than one field hash table"); - r = -EBADMSG; - goto fail; - } - - if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || - le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - error(p, "Header fields for field hash table invalid"); - r = -EBADMSG; - goto fail; - } - - n_field_hash_tables++; - break; - - case OBJECT_ENTRY_ARRAY: - r = write_uint64(entry_array_fd, p); - if (r < 0) - goto fail; - - if (p == le64toh(f->header->entry_array_offset)) { - if (found_main_entry_array) { - error(p, "More than one main entry array"); - r = -EBADMSG; - goto fail; - } - - found_main_entry_array = true; - } - - n_entry_arrays++; - break; - - case OBJECT_TAG: - if (!JOURNAL_HEADER_SEALED(f->header)) { - error(p, "Tag object in file without sealing"); - r = -EBADMSG; - goto fail; - } - - if (le64toh(o->tag.seqnum) != n_tags + 1) { - error(p, "Tag sequence number out of synchronization"); - r = -EBADMSG; - goto fail; - } - - if (le64toh(o->tag.epoch) < last_epoch) { - error(p, "Epoch sequence out of synchronization"); - r = -EBADMSG; - goto fail; - } - -#ifdef HAVE_GCRYPT - if (f->seal) { - uint64_t q, rt; - - debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum)); - - rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; - if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) { - error(p, "tag/entry realtime timestamp out of synchronization"); - r = -EBADMSG; - goto fail; - } - - /* OK, now we know the epoch. So let's now set - * it, and calculate the HMAC for everything - * since the last tag. */ - r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch)); - if (r < 0) - goto fail; - - r = journal_file_hmac_start(f); - if (r < 0) - goto fail; - - if (last_tag == 0) { - r = journal_file_hmac_put_header(f); - if (r < 0) - goto fail; - - q = le64toh(f->header->header_size); - } else - q = last_tag; - - while (q <= p) { - r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o); - if (r < 0) - goto fail; - - r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); - if (r < 0) - goto fail; - - q = q + ALIGN64(le64toh(o->object.size)); - } - - /* Position might have changed, let's reposition things */ - r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); - if (r < 0) - goto fail; - - if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { - error(p, "Tag failed verification"); - r = -EBADMSG; - goto fail; - } - - f->hmac_running = false; - last_tag_realtime = rt; - last_sealed_realtime = entry_realtime; - } - - last_tag = p + ALIGN64(le64toh(o->object.size)); -#endif - - last_epoch = le64toh(o->tag.epoch); - - n_tags++; - break; - - default: - n_weird++; - } - - if (p == le64toh(f->header->tail_object_offset)) { - found_last = true; - break; - } - - p = p + ALIGN64(le64toh(o->object.size)); - }; - - if (!found_last && le64toh(f->header->tail_object_offset) != 0) { - error(le64toh(f->header->tail_object_offset), "Tail object pointer dead"); - r = -EBADMSG; - goto fail; - } - - if (n_objects != le64toh(f->header->n_objects)) { - error(offsetof(Header, n_objects), "Object number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (n_entries != le64toh(f->header->n_entries)) { - error(offsetof(Header, n_entries), "Entry number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && - n_data != le64toh(f->header->n_data)) { - error(offsetof(Header, n_data), "Data number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && - n_fields != le64toh(f->header->n_fields)) { - error(offsetof(Header, n_fields), "Field number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && - n_tags != le64toh(f->header->n_tags)) { - error(offsetof(Header, n_tags), "Tag number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) && - n_entry_arrays != le64toh(f->header->n_entry_arrays)) { - error(offsetof(Header, n_entry_arrays), "Entry array number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) { - error(0, "Missing entry array"); - r = -EBADMSG; - goto fail; - } - - if (entry_seqnum_set && - entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { - error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum"); - r = -EBADMSG; - goto fail; - } - - if (entry_monotonic_set && - (!sd_id128_equal(entry_boot_id, f->header->boot_id) || - entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { - error(0, "Invalid tail monotonic timestamp"); - r = -EBADMSG; - goto fail; - } - - if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { - error(0, "Invalid tail realtime timestamp"); - r = -EBADMSG; - goto fail; - } - - /* Second iteration: we follow all objects referenced from the - * two entry points: the object hash table and the entry - * array. We also check that everything referenced (directly - * or indirectly) in the data hash table also exists in the - * entry array, and vice versa. Note that we do not care for - * unreferenced objects. We only care that everything that is - * referenced is consistent. */ - - r = verify_entry_array(f, - data_fd, n_data, - entry_fd, n_entries, - entry_array_fd, n_entry_arrays, - &last_usec, - show_progress); - if (r < 0) - goto fail; - - r = verify_hash_table(f, - data_fd, n_data, - entry_fd, n_entries, - entry_array_fd, n_entry_arrays, - &last_usec, - show_progress); - if (r < 0) - goto fail; - - if (show_progress) - flush_progress(); - - mmap_cache_close_fd(f->mmap, data_fd); - mmap_cache_close_fd(f->mmap, entry_fd); - mmap_cache_close_fd(f->mmap, entry_array_fd); - - safe_close(data_fd); - safe_close(entry_fd); - safe_close(entry_array_fd); - - if (first_contained) - *first_contained = le64toh(f->header->head_entry_realtime); - if (last_validated) - *last_validated = last_sealed_realtime; - if (last_contained) - *last_contained = le64toh(f->header->tail_entry_realtime); - - return 0; - -fail: - if (show_progress) - flush_progress(); - - log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).", - f->path, - p, - (unsigned long long) f->last_stat.st_size, - 100 * p / f->last_stat.st_size); - - if (data_fd >= 0) { - mmap_cache_close_fd(f->mmap, data_fd); - safe_close(data_fd); - } - - if (entry_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_fd); - safe_close(entry_fd); - } - - if (entry_array_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_array_fd); - safe_close(entry_array_fd); - } - - return r; -} diff --git a/src/journal/journal-verify.h b/src/journal/journal-verify.h deleted file mode 100644 index 8f0eaf6daa..0000000000 --- a/src/journal/journal-verify.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journal-file.h" - -int journal_file_verify(JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c deleted file mode 100644 index 53c6180864..0000000000 --- a/src/journal/journalctl.c +++ /dev/null @@ -1,2615 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-journal.h" - -#include "acl-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 "fs-util.h" -#include "fsprg.h" -#include "glob-util.h" -#include "hostname-util.h" -#include "io-util.h" -#include "journal-def.h" -#include "journal-internal.h" -#include "journal-qrcode.h" -#include "journal-vacuum.h" -#include "journal-verify.h" -#include "locale-util.h" -#include "log.h" -#include "logs-show.h" -#include "mkdir.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 "udev.h" -#include "udev-util.h" -#include "unit-name.h" -#include "user-util.h" - -#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) - -enum { - /* Special values for arg_lines */ - ARG_LINES_DEFAULT = -2, - ARG_LINES_ALL = -1, -}; - -static OutputMode arg_output = OUTPUT_SHORT; -static bool arg_utc = false; -static bool arg_pager_end = false; -static bool arg_follow = false; -static bool arg_full = true; -static bool arg_all = false; -static bool arg_no_pager = false; -static int arg_lines = ARG_LINES_DEFAULT; -static bool arg_no_tail = false; -static bool arg_quiet = false; -static bool arg_merge = false; -static bool arg_boot = false; -static sd_id128_t arg_boot_id = {}; -static int arg_boot_offset = 0; -static bool arg_dmesg = false; -static bool arg_no_hostname = false; -static const char *arg_cursor = NULL; -static const char *arg_after_cursor = NULL; -static bool arg_show_cursor = false; -static const char *arg_directory = NULL; -static char **arg_file = NULL; -static bool arg_file_stdin = false; -static int arg_priorities = 0xFF; -static const char *arg_verify_key = NULL; -#ifdef HAVE_GCRYPT -static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; -static bool arg_force = false; -#endif -static usec_t arg_since, arg_until; -static bool arg_since_set = false, arg_until_set = false; -static char **arg_syslog_identifier = NULL; -static char **arg_system_units = NULL; -static char **arg_user_units = NULL; -static const char *arg_field = NULL; -static bool arg_catalog = false; -static bool arg_reverse = false; -static int arg_journal_type = 0; -static char *arg_root = NULL; -static const char *arg_machine = NULL; -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, - ACTION_NEW_ID128, - ACTION_PRINT_HEADER, - ACTION_SETUP_KEYS, - ACTION_VERIFY, - ACTION_DISK_USAGE, - ACTION_LIST_CATALOG, - ACTION_DUMP_CATALOG, - ACTION_UPDATE_CATALOG, - ACTION_LIST_BOOTS, - ACTION_FLUSH, - ACTION_SYNC, - ACTION_ROTATE, - ACTION_VACUUM, - ACTION_LIST_FIELDS, - ACTION_LIST_FIELD_NAMES, -} arg_action = ACTION_SHOW; - -typedef struct BootId { - sd_id128_t id; - uint64_t first; - uint64_t last; - LIST_FIELDS(struct BootId, boot_list); -} BootId; - -static int add_matches_for_device(sd_journal *j, const char *devpath) { - int r; - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - struct udev_device *d = NULL; - struct stat st; - - assert(j); - assert(devpath); - - if (!path_startswith(devpath, "/dev/")) { - log_error("Devpath does not start with /dev/"); - return -EINVAL; - } - - udev = udev_new(); - if (!udev) - return log_oom(); - - r = stat(devpath, &st); - if (r < 0) - log_error_errno(errno, "Couldn't stat file: %m"); - - d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev); - if (!device) - return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); - - while (d) { - _cleanup_free_ char *match = NULL; - const char *subsys, *sysname, *devnode; - - subsys = udev_device_get_subsystem(d); - if (!subsys) { - d = udev_device_get_parent(d); - continue; - } - - sysname = udev_device_get_sysname(d); - if (!sysname) { - d = udev_device_get_parent(d); - continue; - } - - match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname, NULL); - if (!match) - return log_oom(); - - r = sd_journal_add_match(j, match, 0); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - devnode = udev_device_get_devnode(d); - if (devnode) { - _cleanup_free_ char *match1 = NULL; - - r = stat(devnode, &st); - if (r < 0) - return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode); - - r = asprintf(&match1, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st.st_mode) ? 'b' : 'c', major(st.st_rdev), minor(st.st_rdev)); - if (r < 0) - return log_oom(); - - r = sd_journal_add_match(j, match1, 0); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - } - - d = udev_device_get_parent(d); - } - - r = add_match_this_boot(j, arg_machine); - if (r < 0) - return log_error_errno(r, "Failed to add match for the current boot: %m"); - - return 0; -} - -static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) { - - if (arg_utc) - return format_timestamp_utc(buf, l, t); - - return format_timestamp(buf, l, t); -} - -static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) { - sd_id128_t id = SD_ID128_NULL; - int off = 0, r; - - if (strlen(x) >= 32) { - char *t; - - t = strndupa(x, 32); - r = sd_id128_from_string(t, &id); - if (r >= 0) - x += 32; - - if (*x != '-' && *x != '+' && *x != 0) - return -EINVAL; - - if (*x != 0) { - r = safe_atoi(x, &off); - if (r < 0) - return r; - } - } else { - r = safe_atoi(x, &off); - if (r < 0) - return r; - } - - if (boot_id) - *boot_id = id; - - if (offset) - *offset = off; - - return 0; -} - -static void help(void) { - - pager_open(arg_no_pager, arg_pager_end); - - printf("%s [OPTIONS...] [MATCHES...]\n\n" - "Query the journal.\n\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" - " -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" - " -b --boot[=ID] Show current boot or the specified boot\n" - " --list-boots Show terse information about recorded boots\n" - " -k --dmesg Show kernel message log from the current boot\n" - " -u --unit=UNIT Show logs from the specified unit\n" - " --user-unit=UNIT Show logs from the specified user unit\n" - " -t --identifier=STRING Show entries with the specified syslog identifier\n" - " -p --priority=RANGE Show entries with the specified priority\n" - " -e --pager-end Immediately jump to the end in the pager\n" - " -f --follow Follow the journal\n" - " -n --lines[=INTEGER] Number of journal entries to show\n" - " --no-tail Show all lines, even in follow mode\n" - " -r --reverse Show the newest entries first\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" - " --utc Express time in Coordinated Universal Time (UTC)\n" - " -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 info messages and privilege warning\n" - " --no-pager Do not pipe output into a pager\n" - " --no-hostname Suppress output of hostname field\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 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" - " --force Override of the FSS key pair with --setup-keys\n" -#endif - "\nCommands:\n" - " -h --help Show this help text\n" - " --version Show package version\n" - " -N --fields List all field names currently used\n" - " -F --field=FIELD List all values that a specified field takes\n" - " --disk-usage Show total disk usage of all journal files\n" - " --vacuum-size=BYTES Reduce disk usage below specified size\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" -#endif - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_FULL, - ARG_NO_TAIL, - ARG_NEW_ID128, - ARG_THIS_BOOT, - ARG_LIST_BOOTS, - ARG_USER, - ARG_SYSTEM, - ARG_ROOT, - ARG_HEADER, - ARG_SETUP_KEYS, - ARG_FILE, - ARG_INTERVAL, - ARG_VERIFY, - ARG_VERIFY_KEY, - ARG_DISK_USAGE, - ARG_AFTER_CURSOR, - ARG_SHOW_CURSOR, - ARG_USER_UNIT, - ARG_LIST_CATALOG, - ARG_DUMP_CATALOG, - ARG_UPDATE_CATALOG, - ARG_FORCE, - ARG_UTC, - ARG_SYNC, - ARG_FLUSH, - ARG_ROTATE, - ARG_VACUUM_SIZE, - ARG_VACUUM_FILES, - ARG_VACUUM_TIME, - ARG_NO_HOSTNAME, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version" , no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "pager-end", no_argument, NULL, 'e' }, - { "follow", no_argument, NULL, 'f' }, - { "force", no_argument, NULL, ARG_FORCE }, - { "output", required_argument, NULL, 'o' }, - { "all", no_argument, NULL, 'a' }, - { "full", no_argument, NULL, 'l' }, - { "no-full", no_argument, NULL, ARG_NO_FULL }, - { "lines", optional_argument, NULL, 'n' }, - { "no-tail", no_argument, NULL, ARG_NO_TAIL }, - { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, - { "quiet", no_argument, NULL, 'q' }, - { "merge", no_argument, NULL, 'm' }, - { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ - { "boot", optional_argument, NULL, 'b' }, - { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, - { "dmesg", no_argument, NULL, 'k' }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "directory", required_argument, NULL, 'D' }, - { "file", required_argument, NULL, ARG_FILE }, - { "root", required_argument, NULL, ARG_ROOT }, - { "header", no_argument, NULL, ARG_HEADER }, - { "identifier", required_argument, NULL, 't' }, - { "priority", required_argument, NULL, 'p' }, - { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, - { "interval", required_argument, NULL, ARG_INTERVAL }, - { "verify", no_argument, NULL, ARG_VERIFY }, - { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, - { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, - { "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, 'S' }, - { "until", required_argument, NULL, 'U' }, - { "unit", required_argument, NULL, 'u' }, - { "user-unit", required_argument, NULL, ARG_USER_UNIT }, - { "field", required_argument, NULL, 'F' }, - { "fields", no_argument, NULL, 'N' }, - { "catalog", no_argument, NULL, 'x' }, - { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, - { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, - { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, - { "reverse", no_argument, NULL, 'r' }, - { "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 }, - { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case 'e': - arg_pager_end = true; - - if (arg_lines == ARG_LINES_DEFAULT) - arg_lines = 1000; - - break; - - case 'f': - arg_follow = true; - break; - - case 'o': - arg_output = output_mode_from_string(optarg); - if (arg_output < 0) { - log_error("Unknown output format '%s'.", optarg); - return -EINVAL; - } - - if (arg_output == OUTPUT_EXPORT || - arg_output == OUTPUT_JSON || - arg_output == OUTPUT_JSON_PRETTY || - arg_output == OUTPUT_JSON_SSE || - arg_output == OUTPUT_CAT) - arg_quiet = true; - - break; - - case 'l': - arg_full = true; - break; - - case ARG_NO_FULL: - arg_full = false; - break; - - case 'a': - arg_all = true; - break; - - case 'n': - if (optarg) { - if (streq(optarg, "all")) - arg_lines = ARG_LINES_ALL; - else { - r = safe_atoi(optarg, &arg_lines); - if (r < 0 || arg_lines < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } - } - } else { - arg_lines = 10; - - /* Hmm, no argument? Maybe the next - * word on the command line is - * supposed to be the argument? Let's - * see if there is one, and is - * parsable. */ - if (optind < argc) { - int n; - if (streq(argv[optind], "all")) { - arg_lines = ARG_LINES_ALL; - optind++; - } else if (safe_atoi(argv[optind], &n) >= 0 && n >= 0) { - arg_lines = n; - optind++; - } - } - } - - break; - - case ARG_NO_TAIL: - arg_no_tail = true; - break; - - case ARG_NEW_ID128: - arg_action = ACTION_NEW_ID128; - break; - - case 'q': - arg_quiet = true; - break; - - case 'm': - arg_merge = true; - break; - - case ARG_THIS_BOOT: - arg_boot = true; - break; - - case 'b': - arg_boot = true; - - if (optarg) { - r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset); - if (r < 0) { - log_error("Failed to parse boot descriptor '%s'", optarg); - return -EINVAL; - } - } else { - - /* Hmm, no argument? Maybe the next - * word on the command line is - * supposed to be the argument? Let's - * see if there is one and is parsable - * as a boot descriptor... */ - - if (optind < argc && - parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset) >= 0) - optind++; - } - - break; - - case ARG_LIST_BOOTS: - arg_action = ACTION_LIST_BOOTS; - break; - - case 'k': - arg_boot = arg_dmesg = true; - break; - - case ARG_SYSTEM: - arg_journal_type |= SD_JOURNAL_SYSTEM; - break; - - case ARG_USER: - arg_journal_type |= SD_JOURNAL_CURRENT_USER; - break; - - case 'M': - arg_machine = optarg; - break; - - case 'D': - arg_directory = optarg; - break; - - case ARG_FILE: - if (streq(optarg, "-")) - /* An undocumented feature: we can read journal files from STDIN. We don't document - * this though, since after all we only support this for mmap-able, seekable files, and - * not for example pipes which are probably the primary usecase for reading things from - * STDIN. To avoid confusion we hence don't document this feature. */ - arg_file_stdin = true; - else { - r = glob_extend(&arg_file, optarg); - if (r < 0) - return log_error_errno(r, "Failed to add paths: %m"); - } - break; - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case 'c': - arg_cursor = optarg; - break; - - case ARG_AFTER_CURSOR: - arg_after_cursor = optarg; - break; - - case ARG_SHOW_CURSOR: - arg_show_cursor = true; - break; - - case ARG_HEADER: - arg_action = ACTION_PRINT_HEADER; - break; - - case ARG_VERIFY: - arg_action = ACTION_VERIFY; - break; - - case ARG_DISK_USAGE: - arg_action = ACTION_DISK_USAGE; - break; - - case ARG_VACUUM_SIZE: - r = parse_size(optarg, 1024, &arg_vacuum_size); - if (r < 0) { - log_error("Failed to parse vacuum size: %s", optarg); - return r; - } - - 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) { - log_error("Failed to parse vacuum time: %s", optarg); - return r; - } - - arg_action = ACTION_VACUUM; - break; - -#ifdef HAVE_GCRYPT - case ARG_FORCE: - arg_force = true; - break; - - case ARG_SETUP_KEYS: - arg_action = ACTION_SETUP_KEYS; - break; - - - case ARG_VERIFY_KEY: - arg_action = ACTION_VERIFY; - arg_verify_key = optarg; - arg_merge = false; - break; - - case ARG_INTERVAL: - r = parse_sec(optarg, &arg_interval); - if (r < 0 || arg_interval <= 0) { - log_error("Failed to parse sealing key change interval: %s", optarg); - return -EINVAL; - } - break; -#else - case ARG_SETUP_KEYS: - case ARG_VERIFY_KEY: - case ARG_INTERVAL: - case ARG_FORCE: - log_error("Forward-secure sealing not available."); - return -EOPNOTSUPP; -#endif - - case 'p': { - const char *dots; - - dots = strstr(optarg, ".."); - if (dots) { - char *a; - int from, to, i; - - /* a range */ - a = strndup(optarg, dots - optarg); - if (!a) - return log_oom(); - - from = log_level_from_string(a); - to = log_level_from_string(dots + 2); - free(a); - - if (from < 0 || to < 0) { - log_error("Failed to parse log level range %s", optarg); - return -EINVAL; - } - - arg_priorities = 0; - - if (from < to) { - for (i = from; i <= to; i++) - arg_priorities |= 1 << i; - } else { - for (i = to; i <= from; i++) - arg_priorities |= 1 << i; - } - - } else { - int p, i; - - p = log_level_from_string(optarg); - if (p < 0) { - log_error("Unknown log level %s", optarg); - return -EINVAL; - } - - arg_priorities = 0; - - for (i = 0; i <= p; i++) - arg_priorities |= 1 << i; - } - - break; - } - - case 'S': - r = parse_timestamp(optarg, &arg_since); - if (r < 0) { - log_error("Failed to parse timestamp: %s", optarg); - return -EINVAL; - } - arg_since_set = true; - break; - - case 'U': - r = parse_timestamp(optarg, &arg_until); - if (r < 0) { - log_error("Failed to parse timestamp: %s", optarg); - return -EINVAL; - } - arg_until_set = true; - break; - - case 't': - r = strv_extend(&arg_syslog_identifier, optarg); - if (r < 0) - return log_oom(); - break; - - case 'u': - r = strv_extend(&arg_system_units, optarg); - if (r < 0) - return log_oom(); - break; - - case ARG_USER_UNIT: - r = strv_extend(&arg_user_units, optarg); - if (r < 0) - return log_oom(); - break; - - case 'F': - arg_action = ACTION_LIST_FIELDS; - arg_field = optarg; - break; - - case 'N': - arg_action = ACTION_LIST_FIELD_NAMES; - break; - - case ARG_NO_HOSTNAME: - arg_no_hostname = true; - break; - - case 'x': - arg_catalog = true; - break; - - case ARG_LIST_CATALOG: - arg_action = ACTION_LIST_CATALOG; - break; - - case ARG_DUMP_CATALOG: - arg_action = ACTION_DUMP_CATALOG; - break; - - case ARG_UPDATE_CATALOG: - arg_action = ACTION_UPDATE_CATALOG; - break; - - case 'r': - arg_reverse = true; - break; - - case ARG_UTC: - arg_utc = true; - break; - - case ARG_FLUSH: - arg_action = ACTION_FLUSH; - break; - - case ARG_ROTATE: - arg_action = ACTION_ROTATE; - break; - - case ARG_SYNC: - arg_action = ACTION_SYNC; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT) - arg_lines = 10; - - if (!!arg_directory + !!arg_file + !!arg_machine > 1) { - log_error("Please specify either -D/--directory= or --file= or -M/--machine=, not more than one."); - return -EINVAL; - } - - if (arg_since_set && arg_until_set && arg_since > arg_until) { - log_error("--since= must be before --until=."); - return -EINVAL; - } - - if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) { - log_error("Please specify only one of --since=, --cursor=, and --after-cursor."); - return -EINVAL; - } - - if (arg_follow && arg_reverse) { - log_error("Please specify either --reverse= or --follow=, not both."); - return -EINVAL; - } - - 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; - } - - if ((arg_boot || arg_action == ACTION_LIST_BOOTS) && arg_merge) { - log_error("Using --boot or --list-boots with --merge is not supported."); - return -EINVAL; - } - - if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) { - - /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user - * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice - * to users, and automatically turn --unit= into --user-unit= if combined with --user. */ - r = strv_extend_strv(&arg_user_units, arg_system_units, true); - if (r < 0) - return -ENOMEM; - - arg_system_units = strv_free(arg_system_units); - } - - return 1; -} - -static int generate_new_id128(void) { - sd_id128_t id; - int r; - unsigned i; - - r = sd_id128_randomize(&id); - if (r < 0) - return log_error_errno(r, "Failed to generate ID: %m"); - - printf("As string:\n" - SD_ID128_FORMAT_STR "\n\n" - "As UUID:\n" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" - "As macro:\n" - "#define MESSAGE_XYZ SD_ID128_MAKE(", - SD_ID128_FORMAT_VAL(id), - SD_ID128_FORMAT_VAL(id)); - for (i = 0; i < 16; i++) - printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); - fputs(")\n\n", stdout); - - printf("As Python constant:\n" - ">>> import uuid\n" - ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n", - SD_ID128_FORMAT_VAL(id)); - - return 0; -} - -static int add_matches(sd_journal *j, char **args) { - char **i; - bool have_term = false; - - assert(j); - - STRV_FOREACH(i, args) { - int r; - - if (streq(*i, "+")) { - if (!have_term) - break; - r = sd_journal_add_disjunction(j); - have_term = false; - - } else if (path_is_absolute(*i)) { - _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL; - const char *path; - struct stat st; - - p = canonicalize_file_name(*i); - path = p ?: *i; - - if (lstat(path, &st) < 0) - return log_error_errno(errno, "Couldn't stat file: %m"); - - if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) { - if (executable_is_script(path, &interpreter) > 0) { - _cleanup_free_ char *comm; - - comm = strndup(basename(path), 15); - if (!comm) - return log_oom(); - - t = strappend("_COMM=", comm); - if (!t) - return log_oom(); - - /* Append _EXE only if the interpreter is not a link. - Otherwise, it might be outdated often. */ - if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) { - t2 = strappend("_EXE=", interpreter); - if (!t2) - return log_oom(); - } - } else { - t = strappend("_EXE=", path); - if (!t) - return log_oom(); - } - - r = sd_journal_add_match(j, t, 0); - - if (r >=0 && t2) - r = sd_journal_add_match(j, t2, 0); - - } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { - r = add_matches_for_device(j, path); - if (r < 0) - return r; - } else { - log_error("File is neither a device node, nor regular file, nor executable: %s", *i); - return -EINVAL; - } - - have_term = true; - } else { - r = sd_journal_add_match(j, *i, 0); - have_term = true; - } - - if (r < 0) - return log_error_errno(r, "Failed to add match '%s': %m", *i); - } - - if (!strv_isempty(args) && !have_term) { - log_error("\"+\" can only be used between terms"); - return -EINVAL; - } - - return 0; -} - -static void boot_id_free_all(BootId *l) { - - while (l) { - BootId *i = l; - LIST_REMOVE(boot_list, l, i); - free(i); - } -} - -static int discover_next_boot(sd_journal *j, - sd_id128_t previous_boot_id, - bool advance_older, - BootId **ret) { - - _cleanup_free_ BootId *next_boot = NULL; - char match[9+32+1] = "_BOOT_ID="; - sd_id128_t boot_id; - int r; - - assert(j); - assert(ret); - - /* We expect the journal to be on the last position of a boot - * (in relation to the direction we are going), so that the next - * invocation of sd_journal_next/previous will be from a different - * boot. We then collect any information we desire and then jump - * to the last location of the new boot by using a _BOOT_ID match - * coming from the other journal direction. */ - - /* Make sure we aren't restricted by any _BOOT_ID matches, so that - * we can actually advance to a *different* boot. */ - sd_journal_flush_matches(j); - - do { - if (advance_older) - r = sd_journal_previous(j); - else - r = sd_journal_next(j); - if (r < 0) - return r; - else if (r == 0) - return 0; /* End of journal, yay. */ - - r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); - if (r < 0) - return r; - - /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that - * normally, this will only require a single iteration, as we seeked to the last entry of the previous - * boot entry already. However, it might happen that the per-journal-field entry arrays are less - * complete than the main entry array, and hence might reference an entry that's not actually the last - * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to - * speed things up, but let's not trust that it is complete, and hence, manually advance as - * necessary. */ - - } while (sd_id128_equal(boot_id, previous_boot_id)); - - next_boot = new0(BootId, 1); - if (!next_boot) - return -ENOMEM; - - next_boot->id = boot_id; - - r = sd_journal_get_realtime_usec(j, &next_boot->first); - if (r < 0) - return r; - - /* Now seek to the last occurrence of this boot ID. */ - sd_id128_to_string(next_boot->id, match + 9); - r = sd_journal_add_match(j, match, sizeof(match) - 1); - if (r < 0) - return r; - - if (advance_older) - r = sd_journal_seek_head(j); - else - r = sd_journal_seek_tail(j); - if (r < 0) - return r; - - if (advance_older) - r = sd_journal_next(j); - else - r = sd_journal_previous(j); - if (r < 0) - return r; - else if (r == 0) - return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ - - r = sd_journal_get_realtime_usec(j, &next_boot->last); - if (r < 0) - return r; - - *ret = next_boot; - next_boot = NULL; - - return 0; -} - -static int get_boots( - sd_journal *j, - BootId **boots, - sd_id128_t *boot_id, - int offset) { - - bool skip_once; - int r, count = 0; - BootId *head = NULL, *tail = NULL; - const bool advance_older = boot_id && offset <= 0; - sd_id128_t previous_boot_id; - - assert(j); - - /* Adjust for the asymmetry that offset 0 is - * the last (and current) boot, while 1 is considered the - * (chronological) first boot in the journal. */ - skip_once = boot_id && sd_id128_is_null(*boot_id) && offset <= 0; - - /* Advance to the earliest/latest occurrence of our reference - * boot ID (taking our lookup direction into account), so that - * discover_next_boot() can do its job. - * If no reference is given, the journal head/tail will do, - * they're "virtual" boots after all. */ - if (boot_id && !sd_id128_is_null(*boot_id)) { - char match[9+32+1] = "_BOOT_ID="; - - sd_journal_flush_matches(j); - - sd_id128_to_string(*boot_id, match + 9); - r = sd_journal_add_match(j, match, sizeof(match) - 1); - if (r < 0) - return r; - - if (advance_older) - r = sd_journal_seek_head(j); /* seek to oldest */ - else - r = sd_journal_seek_tail(j); /* seek to newest */ - if (r < 0) - return r; - - if (advance_older) - r = sd_journal_next(j); /* read the oldest entry */ - else - r = sd_journal_previous(j); /* read the most recently added entry */ - if (r < 0) - return r; - else if (r == 0) - goto finish; - else if (offset == 0) { - count = 1; - goto finish; - } - - /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot - * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at - * the following entry, which must then have an older/newer boot ID */ - } else { - - if (advance_older) - r = sd_journal_seek_tail(j); /* seek to newest */ - else - r = sd_journal_seek_head(j); /* seek to oldest */ - if (r < 0) - return r; - - /* No sd_journal_next()/_previous() here. - * - * At this point the read pointer is positioned after the newest/before the oldest entry in the whole - * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest - * entry we have. */ - } - - previous_boot_id = SD_ID128_NULL; - for (;;) { - _cleanup_free_ BootId *current = NULL; - - r = discover_next_boot(j, previous_boot_id, advance_older, ¤t); - if (r < 0) { - boot_id_free_all(head); - return r; - } - - if (!current) - break; - - previous_boot_id = current->id; - - if (boot_id) { - if (!skip_once) - offset += advance_older ? 1 : -1; - skip_once = false; - - if (offset == 0) { - count = 1; - *boot_id = current->id; - break; - } - } else { - LIST_INSERT_AFTER(boot_list, head, tail, current); - tail = current; - current = NULL; - count++; - } - } - -finish: - if (boots) - *boots = head; - - sd_journal_flush_matches(j); - - return count; -} - -static int list_boots(sd_journal *j) { - int w, i, count; - BootId *id, *all_ids; - - assert(j); - - count = get_boots(j, &all_ids, NULL, 0); - if (count < 0) - return log_error_errno(count, "Failed to determine boots: %m"); - if (count == 0) - return count; - - pager_open(arg_no_pager, arg_pager_end); - - /* numbers are one less, but we need an extra char for the sign */ - w = DECIMAL_STR_WIDTH(count - 1) + 1; - - i = 0; - LIST_FOREACH(boot_list, id, all_ids) { - char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; - - printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n", - w, i - count + 1, - SD_ID128_FORMAT_VAL(id->id), - format_timestamp_maybe_utc(a, sizeof(a), id->first), - format_timestamp_maybe_utc(b, sizeof(b), id->last)); - i++; - } - - boot_id_free_all(all_ids); - - return 0; -} - -static int add_boot(sd_journal *j) { - char match[9+32+1] = "_BOOT_ID="; - sd_id128_t boot_id; - int r; - - assert(j); - - if (!arg_boot) - return 0; - - /* Take a shortcut and use the current boot_id, which we can do very quickly. - * We can do this only when we logs are coming from the current machine, - * so take the slow path if log location is specified. */ - if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) && - !arg_directory && !arg_file) - - return add_match_this_boot(j, arg_machine); - - boot_id = arg_boot_id; - r = get_boots(j, NULL, &boot_id, arg_boot_offset); - assert(r <= 1); - if (r <= 0) { - const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r); - - if (sd_id128_is_null(arg_boot_id)) - log_error("Data from the specified boot (%+i) is not available: %s", - arg_boot_offset, reason); - else - log_error("Data from the specified boot ("SD_ID128_FORMAT_STR") is not available: %s", - SD_ID128_FORMAT_VAL(arg_boot_id), reason); - - return r == 0 ? -ENODATA : r; - } - - sd_id128_to_string(boot_id, match + 9); - - r = sd_journal_add_match(j, match, sizeof(match) - 1); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - r = sd_journal_add_conjunction(j); - if (r < 0) - return log_error_errno(r, "Failed to add conjunction: %m"); - - return 0; -} - -static int add_dmesg(sd_journal *j) { - int r; - assert(j); - - if (!arg_dmesg) - return 0; - - r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel")); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - r = sd_journal_add_conjunction(j); - if (r < 0) - return log_error_errno(r, "Failed to add conjunction: %m"); - - return 0; -} - -static int get_possible_units( - sd_journal *j, - const char *fields, - char **patterns, - Set **units) { - - _cleanup_set_free_free_ Set *found; - const char *field; - int r; - - found = set_new(&string_hash_ops); - if (!found) - return -ENOMEM; - - NULSTR_FOREACH(field, fields) { - const void *data; - size_t size; - - r = sd_journal_query_unique(j, field); - if (r < 0) - return r; - - SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { - char **pattern, *eq; - size_t prefix; - _cleanup_free_ char *u = NULL; - - eq = memchr(data, '=', size); - if (eq) - prefix = eq - (char*) data + 1; - else - prefix = 0; - - u = strndup((char*) data + prefix, size - prefix); - if (!u) - return -ENOMEM; - - STRV_FOREACH(pattern, patterns) - if (fnmatch(*pattern, u, FNM_NOESCAPE) == 0) { - log_debug("Matched %s with pattern %s=%s", u, field, *pattern); - - r = set_consume(found, u); - u = NULL; - if (r < 0 && r != -EEXIST) - return r; - - break; - } - } - } - - *units = found; - found = NULL; - return 0; -} - -/* This list is supposed to return the superset of unit names - * possibly matched by rules added with add_matches_for_unit... */ -#define SYSTEM_UNITS \ - "_SYSTEMD_UNIT\0" \ - "COREDUMP_UNIT\0" \ - "UNIT\0" \ - "OBJECT_SYSTEMD_UNIT\0" \ - "_SYSTEMD_SLICE\0" - -/* ... and add_matches_for_user_unit */ -#define USER_UNITS \ - "_SYSTEMD_USER_UNIT\0" \ - "USER_UNIT\0" \ - "COREDUMP_USER_UNIT\0" \ - "OBJECT_SYSTEMD_USER_UNIT\0" - -static int add_units(sd_journal *j) { - _cleanup_strv_free_ char **patterns = NULL; - int r, count = 0; - char **i; - - assert(j); - - STRV_FOREACH(i, arg_system_units) { - _cleanup_free_ char *u = NULL; - - r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); - if (r < 0) - return r; - - if (string_is_glob(u)) { - r = strv_push(&patterns, u); - if (r < 0) - return r; - u = NULL; - } else { - r = add_matches_for_unit(j, u); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - count++; - } - } - - if (!strv_isempty(patterns)) { - _cleanup_set_free_free_ Set *units = NULL; - Iterator it; - char *u; - - r = get_possible_units(j, SYSTEM_UNITS, patterns, &units); - if (r < 0) - return r; - - SET_FOREACH(u, units, it) { - r = add_matches_for_unit(j, u); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - count++; - } - } - - patterns = strv_free(patterns); - - STRV_FOREACH(i, arg_user_units) { - _cleanup_free_ char *u = NULL; - - r = unit_name_mangle(*i, UNIT_NAME_GLOB, &u); - if (r < 0) - return r; - - if (string_is_glob(u)) { - r = strv_push(&patterns, u); - if (r < 0) - return r; - u = NULL; - } else { - r = add_matches_for_user_unit(j, u, getuid()); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - count++; - } - } - - if (!strv_isempty(patterns)) { - _cleanup_set_free_free_ Set *units = NULL; - Iterator it; - char *u; - - r = get_possible_units(j, USER_UNITS, patterns, &units); - if (r < 0) - return r; - - SET_FOREACH(u, units, it) { - r = add_matches_for_user_unit(j, u, getuid()); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - count++; - } - } - - /* Complain if the user request matches but nothing whatsoever was - * found, since otherwise everything would be matched. */ - if (!(strv_isempty(arg_system_units) && strv_isempty(arg_user_units)) && count == 0) - return -ENODATA; - - r = sd_journal_add_conjunction(j); - if (r < 0) - return r; - - return 0; -} - -static int add_priorities(sd_journal *j) { - char match[] = "PRIORITY=0"; - int i, r; - assert(j); - - if (arg_priorities == 0xFF) - return 0; - - for (i = LOG_EMERG; i <= LOG_DEBUG; i++) - if (arg_priorities & (1 << i)) { - match[sizeof(match)-2] = '0' + i; - - r = sd_journal_add_match(j, match, strlen(match)); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - } - - r = sd_journal_add_conjunction(j); - if (r < 0) - return log_error_errno(r, "Failed to add conjunction: %m"); - - return 0; -} - - -static int add_syslog_identifier(sd_journal *j) { - int r; - char **i; - - assert(j); - - STRV_FOREACH(i, arg_syslog_identifier) { - char *u; - - u = strjoina("SYSLOG_IDENTIFIER=", *i); - r = sd_journal_add_match(j, u, 0); - if (r < 0) - return r; - r = sd_journal_add_disjunction(j); - if (r < 0) - return r; - } - - r = sd_journal_add_conjunction(j); - if (r < 0) - return r; - - return 0; -} - -static int setup_keys(void) { -#ifdef HAVE_GCRYPT - size_t mpk_size, seed_size, state_size, i; - uint8_t *mpk, *seed, *state; - int fd = -1, r; - sd_id128_t machine, boot; - char *p = NULL, *k = NULL; - struct FSSHeader h; - uint64_t n; - struct stat st; - - r = stat("/var/log/journal", &st); - if (r < 0 && errno != ENOENT && errno != ENOTDIR) - return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal"); - - if (r < 0 || !S_ISDIR(st.st_mode)) { - log_error("%s is not a directory, must be using persistent logging for FSS.", - "/var/log/journal"); - return r < 0 ? -errno : -ENOTDIR; - } - - r = sd_id128_get_machine(&machine); - if (r < 0) - return log_error_errno(r, "Failed to get machine ID: %m"); - - r = sd_id128_get_boot(&boot); - if (r < 0) - return log_error_errno(r, "Failed to get boot ID: %m"); - - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", - SD_ID128_FORMAT_VAL(machine)) < 0) - return log_oom(); - - if (arg_force) { - r = unlink(p); - if (r < 0 && errno != ENOENT) { - r = log_error_errno(errno, "unlink(\"%s\") failed: %m", p); - goto finish; - } - } else if (access(p, F_OK) >= 0) { - log_error("Sealing key file %s exists already. Use --force to recreate.", p); - r = -EEXIST; - goto finish; - } - - if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX", - SD_ID128_FORMAT_VAL(machine)) < 0) { - r = log_oom(); - goto finish; - } - - mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR); - mpk = alloca(mpk_size); - - seed_size = FSPRG_RECOMMENDED_SEEDLEN; - seed = alloca(seed_size); - - state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); - state = alloca(state_size); - - fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - r = log_error_errno(errno, "Failed to open /dev/random: %m"); - goto finish; - } - - log_info("Generating seed..."); - r = loop_read_exact(fd, seed, seed_size, true); - if (r < 0) { - log_error_errno(r, "Failed to read random seed: %m"); - goto finish; - } - - log_info("Generating key pair..."); - FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR); - - log_info("Generating sealing key..."); - FSPRG_GenState0(state, mpk, seed, seed_size); - - assert(arg_interval > 0); - - n = now(CLOCK_REALTIME); - n /= arg_interval; - - safe_close(fd); - fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC); - if (fd < 0) { - r = log_error_errno(fd, "Failed to open %s: %m", k); - goto finish; - } - - /* Enable secure remove, exclusion from dump, synchronous - * 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(r, "Failed to set file attributes: %m"); - - zero(h); - memcpy(h.signature, "KSHHRHLP", 8); - h.machine_id = machine; - h.boot_id = boot; - h.header_size = htole64(sizeof(h)); - h.start_usec = htole64(n * arg_interval); - h.interval_usec = htole64(arg_interval); - h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR); - h.fsprg_state_size = htole64(state_size); - - r = loop_write(fd, &h, sizeof(h), false); - if (r < 0) { - log_error_errno(r, "Failed to write header: %m"); - goto finish; - } - - r = loop_write(fd, state, state_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write state: %m"); - goto finish; - } - - if (link(k, p) < 0) { - r = log_error_errno(errno, "Failed to link file: %m"); - goto finish; - } - - if (on_tty()) { - fprintf(stderr, - "\n" - "The new key pair has been generated. The %ssecret sealing key%s has been written to\n" - "the following local file. This key file is automatically updated when the\n" - "sealing key is advanced. It should not be used on multiple hosts.\n" - "\n" - "\t%s\n" - "\n" - "Please write down the following %ssecret verification key%s. It should be stored\n" - "at a safe location and should not be saved locally on disk.\n" - "\n\t%s", - ansi_highlight(), ansi_normal(), - ansi_highlight(), ansi_normal(), - ansi_highlight_red(), - p); - fflush(stderr); - } - for (i = 0; i < seed_size; i++) { - if (i > 0 && i % 3 == 0) - putchar('-'); - printf("%02x", ((uint8_t*) seed)[i]); - } - - printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval); - - if (on_tty()) { - char tsb[FORMAT_TIMESPAN_MAX], *hn; - - fprintf(stderr, - "%s\n" - "The sealing key is automatically changed every %s.\n", - ansi_normal(), - format_timespan(tsb, sizeof(tsb), arg_interval, 0)); - - hn = gethostname_malloc(); - - if (hn) { - hostname_cleanup(hn); - fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine)); - } else - fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine)); - -#ifdef HAVE_QRENCODE - /* If this is not an UTF-8 system don't print any QR codes */ - if (is_locale_utf8()) { - fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr); - print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine); - } -#endif - free(hn); - } - - r = 0; - -finish: - safe_close(fd); - - if (k) { - unlink(k); - free(k); - } - - free(p); - - return r; -#else - log_error("Forward-secure sealing not available."); - return -EOPNOTSUPP; -#endif -} - -static int verify(sd_journal *j) { - int r = 0; - Iterator i; - JournalFile *f; - - assert(j); - - log_show_color(true); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - int k; - usec_t first = 0, validated = 0, last = 0; - -#ifdef HAVE_GCRYPT - if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) - log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path); -#endif - - k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true); - if (k == -EINVAL) { - /* If the key was invalid give up right-away. */ - return k; - } else if (k < 0) { - 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]; - log_info("PASS: %s", f->path); - - if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) { - if (validated > 0) { - log_info("=> Validated from %s to %s, final %s entries not sealed.", - format_timestamp_maybe_utc(a, sizeof(a), first), - format_timestamp_maybe_utc(b, sizeof(b), validated), - format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0)); - } else if (last > 0) - log_info("=> No sealing yet, %s of entries not sealed.", - format_timespan(c, sizeof(c), last - first, 0)); - else - log_info("=> No sealing yet, no entries in file."); - } - } - } - - return r; -} - -static int access_check_var_log_journal(sd_journal *j) { -#ifdef HAVE_ACL - _cleanup_strv_free_ char **g = NULL; - const char* dir; -#endif - int r; - - assert(j); - - if (arg_quiet) - return 0; - - /* If we are root, we should have access, don't warn. */ - if (getuid() == 0) - return 0; - - /* If we are in the 'systemd-journal' group, we should have - * access too. */ - r = in_group("systemd-journal"); - if (r < 0) - return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); - if (r > 0) - return 0; - -#ifdef HAVE_ACL - if (laccess("/run/log/journal", F_OK) >= 0) - dir = "/run/log/journal"; - else - dir = "/var/log/journal"; - - /* If we are in any of the groups listed in the journal ACLs, - * then all is good, too. Let's enumerate all groups from the - * default ACL of the directory, which generally should allow - * access to most journal files too. */ - r = acl_search_groups(dir, &g); - if (r < 0) - return log_error_errno(r, "Failed to search journal ACL: %m"); - if (r > 0) - return 0; - - /* Print a pretty list, if there were ACLs set. */ - if (!strv_isempty(g)) { - _cleanup_free_ char *s = NULL; - - /* Thre are groups in the ACL, let's list them */ - r = strv_extend(&g, "systemd-journal"); - if (r < 0) - return log_oom(); - - strv_sort(g); - strv_uniq(g); - - s = strv_join(g, "', '"); - if (!s) - return log_oom(); - - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in groups '%s' can see all messages.\n" - " Pass -q to turn off this notice.", s); - return 1; - } -#endif - - /* If no ACLs were found, print a short version of the message. */ - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" - " turn off this notice."); - - return 1; -} - -static int access_check(sd_journal *j) { - Iterator it; - void *code; - char *path; - int r = 0; - - assert(j); - - if (hashmap_isempty(j->errors)) { - if (ordered_hashmap_isempty(j->files)) - log_notice("No journal files were found."); - - return 0; - } - - 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."); - } - - HASHMAP_FOREACH_KEY(path, code, j->errors, it) { - int err; - - err = abs(PTR_TO_INT(code)); - - switch (err) { - case EACCES: - continue; - - 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 or directory %s, ignoring file: %m", path); - break; - } - } - - return r; -} - -static int flush_to_var(void) { - _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_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", SIGUSR1); - 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); - - 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_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); - if (r < 0) - return log_error_errno(errno, "Failed to watch journal directory: %m"); - - for (;;) { - if (access("/run/systemd/journal/flushed", F_OK) >= 0) - break; - - if (errno != ENOENT) - return log_error_errno(errno, "Failed to check for existence of /run/systemd/journal/flushed: %m"); - - 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 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_(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; - int n_shown = 0; - bool ellipsized = false; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - signal(SIGWINCH, columns_lines_cache_reset); - sigbus_install(); - - /* Increase max number of open files to 16K if we can, we - * might needs this when browsing journal files, which might - * be split up into many files. */ - setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); - - switch (arg_action) { - - case ACTION_NEW_ID128: - r = generate_new_id128(); - goto finish; - - case ACTION_SETUP_KEYS: - r = setup_keys(); - goto finish; - - case ACTION_LIST_CATALOG: - case ACTION_DUMP_CATALOG: - case ACTION_UPDATE_CATALOG: { - _cleanup_free_ char *database; - - database = path_join(arg_root, CATALOG_DATABASE, NULL); - if (!database) { - r = log_oom(); - goto finish; - } - - if (arg_action == ACTION_UPDATE_CATALOG) { - r = catalog_update(database, arg_root, catalog_file_dirs); - if (r < 0) - log_error_errno(r, "Failed to list catalog: %m"); - } else { - bool oneline = arg_action == ACTION_LIST_CATALOG; - - pager_open(arg_no_pager, arg_pager_end); - - if (optind < argc) - r = catalog_list_items(stdout, database, oneline, argv + optind); - else - r = catalog_list(stdout, database, oneline); - if (r < 0) - log_error_errno(r, "Failed to list catalog: %m"); - } - - 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: - case ACTION_LIST_FIELDS: - case ACTION_LIST_FIELD_NAMES: - /* 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_stdin) { - int ifd = STDIN_FILENO; - r = sd_journal_open_files_fd(&j, &ifd, 1, 0); - } else if (arg_file) - r = sd_journal_open_files(&j, (const char**) arg_file, 0); - else if (arg_machine) { - _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; - int fd; - - if (geteuid() != 0) { - /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of - * the container, thus we need root privileges to override them. */ - log_error("Using the --machine= switch requires root privileges."); - r = -EPERM; - goto finish; - } - - r = sd_bus_open_system(&bus); - if (r < 0) { - log_error_errno(r, "Failed to open system bus: %m"); - goto finish; - } - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachineRootDirectory", - &error, - &reply, - "s", arg_machine); - if (r < 0) { - log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r)); - goto finish; - } - - r = sd_bus_message_read(reply, "h", &fd); - if (r < 0) { - bus_log_parse_error(r); - goto finish; - } - - fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (fd < 0) { - r = log_error_errno(errno, "Failed to duplicate file descriptor: %m"); - goto finish; - } - - r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT); - if (r < 0) - safe_close(fd); - } 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_file ? "files" : "journal"); - goto finish; - } - - r = access_check(j); - if (r < 0) - goto finish; - - switch (arg_action) { - - 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; - - case ACTION_VERIFY: - r = verify(j); - goto finish; - - case ACTION_DISK_USAGE: { - uint64_t bytes = 0; - char sbytes[FORMAT_BYTES_MAX]; - - r = sd_journal_get_usage(j, &bytes); - if (r < 0) - goto finish; - - printf("Archived and active journals take up %s on disk.\n", - format_bytes(sbytes, sizeof(sbytes), bytes)); - goto finish; - } - - case ACTION_LIST_BOOTS: - r = list_boots(j); - goto finish; - - case ACTION_VACUUM: { - Directory *d; - Iterator i; - - HASHMAP_FOREACH(d, j->directories_by_path, i) { - int q; - - if (d->is_root) - continue; - - 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 %s: %m", d->path); - r = q; - } - } - - goto finish; - } - - case ACTION_LIST_FIELD_NAMES: { - const char *field; - - SD_JOURNAL_FOREACH_FIELD(j, field) { - printf("%s\n", field); - n_shown++; - } - - r = 0; - goto finish; - } - - case ACTION_SHOW: - case ACTION_LIST_FIELDS: - break; - - default: - assert_not_reached("Unknown action"); - } - - if (arg_boot_offset != 0 && - sd_journal_has_runtime_files(j) > 0 && - sd_journal_has_persistent_files(j) == 0) { - log_info("Specifying boot ID has no effect, no persistent journal was found"); - r = 0; - goto finish; - } - /* add_boot() must be called first! - * It may need to seek the journal to find parent boot IDs. */ - r = add_boot(j); - if (r < 0) - goto finish; - - r = add_dmesg(j); - if (r < 0) - goto finish; - - r = add_units(j); - if (r < 0) { - log_error_errno(r, "Failed to add filter for units: %m"); - goto finish; - } - - r = add_syslog_identifier(j); - if (r < 0) { - log_error_errno(r, "Failed to add filter for syslog identifiers: %m"); - goto finish; - } - - r = add_priorities(j); - if (r < 0) - goto finish; - - r = add_matches(j, argv + optind); - if (r < 0) - goto finish; - - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { - _cleanup_free_ char *filter; - - filter = journal_make_match_string(j); - if (!filter) - return log_oom(); - - log_debug("Journal filter: %s", filter); - } - - if (arg_action == ACTION_LIST_FIELDS) { - const void *data; - size_t size; - - assert(arg_field); - - r = sd_journal_set_data_threshold(j, 0); - if (r < 0) { - log_error_errno(r, "Failed to unset data size threshold: %m"); - goto finish; - } - - r = sd_journal_query_unique(j, arg_field); - if (r < 0) { - log_error_errno(r, "Failed to query unique data objects: %m"); - goto finish; - } - - SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { - const void *eq; - - if (arg_lines >= 0 && n_shown >= arg_lines) - break; - - eq = memchr(data, '=', size); - if (eq) - printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1); - else - printf("%.*s\n", (int) size, (const char*) data); - - n_shown++; - } - - r = 0; - goto finish; - } - - /* Opening the fd now means the first sd_journal_wait() will actually wait */ - if (arg_follow) { - r = sd_journal_get_fd(j); - if (r == -EMEDIUMTYPE) { - log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); - goto finish; - } - if (r < 0) { - log_error_errno(r, "Failed to get journal fd: %m"); - goto finish; - } - } - - if (arg_cursor || arg_after_cursor) { - r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor); - if (r < 0) { - log_error_errno(r, "Failed to seek to cursor: %m"); - goto finish; - } - - if (!arg_reverse) - r = sd_journal_next_skip(j, 1 + !!arg_after_cursor); - else - r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor); - - if (arg_after_cursor && r < 2) { - /* We couldn't find the next entry after the cursor. */ - if (arg_follow) - need_seek = true; - else - arg_lines = 0; - } - - } else if (arg_since_set && !arg_reverse) { - r = sd_journal_seek_realtime_usec(j, arg_since); - if (r < 0) { - log_error_errno(r, "Failed to seek to date: %m"); - goto finish; - } - r = sd_journal_next(j); - - } else if (arg_until_set && arg_reverse) { - r = sd_journal_seek_realtime_usec(j, arg_until); - if (r < 0) { - log_error_errno(r, "Failed to seek to date: %m"); - goto finish; - } - r = sd_journal_previous(j); - - } else if (arg_lines >= 0) { - r = sd_journal_seek_tail(j); - if (r < 0) { - log_error_errno(r, "Failed to seek to tail: %m"); - goto finish; - } - - r = sd_journal_previous_skip(j, arg_lines); - - } else if (arg_reverse) { - r = sd_journal_seek_tail(j); - if (r < 0) { - log_error_errno(r, "Failed to seek to tail: %m"); - goto finish; - } - - r = sd_journal_previous(j); - - } else { - r = sd_journal_seek_head(j); - if (r < 0) { - log_error_errno(r, "Failed to seek to head: %m"); - goto finish; - } - - r = sd_journal_next(j); - } - - if (r < 0) { - log_error_errno(r, "Failed to iterate through journal: %m"); - goto finish; - } - if (r == 0) { - if (arg_follow) - need_seek = true; - else { - if (!arg_quiet) - printf("-- No entries --\n"); - goto finish; - } - } - - if (!arg_follow) - pager_open(arg_no_pager, arg_pager_end); - - if (!arg_quiet) { - usec_t start, end; - char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; - - r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); - if (r < 0) { - log_error_errno(r, "Failed to get cutoff: %m"); - goto finish; - } - - if (r > 0) { - if (arg_follow) - printf("-- Logs begin at %s. --\n", - format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start)); - else - printf("-- Logs begin at %s, end at %s. --\n", - format_timestamp_maybe_utc(start_buf, sizeof(start_buf), start), - format_timestamp_maybe_utc(end_buf, sizeof(end_buf), end)); - } - } - - for (;;) { - while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) { - int flags; - - if (need_seek) { - if (!arg_reverse) - r = sd_journal_next(j); - else - r = sd_journal_previous(j); - if (r < 0) { - log_error_errno(r, "Failed to iterate through journal: %m"); - goto finish; - } - if (r == 0) - break; - } - - if (arg_until_set && !arg_reverse) { - usec_t usec; - - r = sd_journal_get_realtime_usec(j, &usec); - if (r < 0) { - log_error_errno(r, "Failed to determine timestamp: %m"); - goto finish; - } - if (usec > arg_until) - goto finish; - } - - if (arg_since_set && arg_reverse) { - usec_t usec; - - r = sd_journal_get_realtime_usec(j, &usec); - if (r < 0) { - log_error_errno(r, "Failed to determine timestamp: %m"); - goto finish; - } - if (usec < arg_since) - goto finish; - } - - if (!arg_merge && !arg_quiet) { - sd_id128_t boot_id; - - r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); - if (r >= 0) { - if (previous_boot_id_valid && - !sd_id128_equal(boot_id, previous_boot_id)) - printf("%s-- Reboot --%s\n", - ansi_highlight(), ansi_normal()); - - previous_boot_id = boot_id; - previous_boot_id_valid = true; - } - } - - flags = - arg_all * OUTPUT_SHOW_ALL | - arg_full * OUTPUT_FULL_WIDTH | - colors_enabled() * OUTPUT_COLOR | - arg_catalog * OUTPUT_CATALOG | - arg_utc * OUTPUT_UTC | - arg_no_hostname * OUTPUT_NO_HOSTNAME; - - r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized); - need_seek = true; - if (r == -EADDRNOTAVAIL) - break; - else if (r < 0 || ferror(stdout)) - goto finish; - - n_shown++; - } - - if (!arg_follow) { - if (arg_show_cursor) { - _cleanup_free_ char *cursor = NULL; - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0 && r != -EADDRNOTAVAIL) - log_error_errno(r, "Failed to get cursor: %m"); - else if (r >= 0) - printf("-- cursor: %s\n", cursor); - } - - break; - } - - r = sd_journal_wait(j, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Couldn't wait for journal event: %m"); - goto finish; - } - - first_line = false; - } - -finish: - pager_close(); - - strv_free(arg_file); - - strv_free(arg_syslog_identifier); - 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 deleted file mode 100644 index a433c91c54..0000000000 --- a/src/journal/journald-audit.c +++ /dev/null @@ -1,564 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - const char *journal_field; - int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov); -} MapField; - -static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { - _cleanup_free_ char *c = NULL; - size_t l = 0, allocated = 0; - const char *e; - - assert(field); - assert(p); - assert(iov); - assert(n_iov); - - l = strlen(field); - allocated = l + 1; - c = malloc(allocated); - if (!c) - return -ENOMEM; - - memcpy(c, field, l); - for (e = *p; *e != ' ' && *e != 0; e++) { - if (!GREEDY_REALLOC(c, allocated, l+2)) - return -ENOMEM; - - c[l++] = *e; - } - - c[l] = 0; - - if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) - return -ENOMEM; - - (*iov)[*n_iov].iov_base = c; - (*iov)[*n_iov].iov_len = l; - (*n_iov)++; - - *p = e; - c = NULL; - - return 1; -} - -static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) { - _cleanup_free_ char *c = NULL; - const char *s, *e; - size_t l; - - assert(field); - assert(p); - assert(iov); - assert(n_iov); - - /* The kernel formats string fields in one of two formats. */ - - if (**p == '"') { - /* Normal quoted syntax */ - s = *p + 1; - e = strchr(s, '"'); - if (!e) - return 0; - - l = strlen(field) + (e - s); - c = malloc(l+1); - if (!c) - return -ENOMEM; - - *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0; - - e += 1; - - } else if (unhexchar(**p) >= 0) { - /* Hexadecimal escaping */ - size_t allocated = 0; - - l = strlen(field); - allocated = l + 2; - c = malloc(allocated); - if (!c) - return -ENOMEM; - - memcpy(c, field, l); - for (e = *p; *e != ' ' && *e != 0; e += 2) { - int a, b; - uint8_t x; - - a = unhexchar(e[0]); - if (a < 0) - return 0; - - b = unhexchar(e[1]); - if (b < 0) - return 0; - - x = ((uint8_t) a << 4 | (uint8_t) b); - - if (filter_printable && x < (uint8_t) ' ') - x = (uint8_t) ' '; - - if (!GREEDY_REALLOC(c, allocated, l+2)) - return -ENOMEM; - - c[l++] = (char) x; - } - - c[l] = 0; - } else - return 0; - - if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) - return -ENOMEM; - - (*iov)[*n_iov].iov_base = c; - (*iov)[*n_iov].iov_len = l; - (*n_iov)++; - - *p = e; - c = NULL; - - return 1; -} - -static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { - return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false); -} - -static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { - return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true); -} - -static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { - const char *e, *f; - char *c, *t; - int r; - - /* Implements fallback mappings for all fields we don't know */ - - for (e = *p; e < *p + 16; e++) { - - if (*e == 0 || *e == ' ') - return 0; - - if (*e == '=') - break; - - if (!((*e >= 'a' && *e <= 'z') || - (*e >= 'A' && *e <= 'Z') || - (*e >= '0' && *e <= '9') || - *e == '_' || *e == '-')) - return 0; - } - - if (e <= *p || e >= *p + 16) - return 0; - - c = alloca(strlen(prefix) + (e - *p) + 2); - - t = stpcpy(c, prefix); - for (f = *p; f < e; f++) { - char x; - - if (*f >= 'a' && *f <= 'z') - x = (*f - 'a') + 'A'; /* uppercase */ - else if (*f == '-') - x = '_'; /* dashes → underscores */ - else - x = *f; - - *(t++) = x; - } - strcpy(t, "="); - - e++; - - r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov); - if (r < 0) - return r; - - *p = e; - return r; -} - -/* Kernel fields are those occurring in the audit string before - * msg='. All of these fields are trusted, hence carry the "_" prefix. - * We try to translate the fields we know into our native names. The - * other's are generically mapped to _AUDIT_FIELD_XYZ= */ -static const MapField map_fields_kernel[] = { - - /* First, we map certain well-known audit fields into native - * well-known fields */ - { "pid=", "_PID=", map_simple_field }, - { "ppid=", "_PPID=", map_simple_field }, - { "uid=", "_UID=", map_simple_field }, - { "euid=", "_EUID=", map_simple_field }, - { "fsuid=", "_FSUID=", map_simple_field }, - { "gid=", "_GID=", map_simple_field }, - { "egid=", "_EGID=", map_simple_field }, - { "fsgid=", "_FSGID=", map_simple_field }, - { "tty=", "_TTY=", map_simple_field }, - { "ses=", "_AUDIT_SESSION=", map_simple_field }, - { "auid=", "_AUDIT_LOGINUID=", map_simple_field }, - { "subj=", "_SELINUX_CONTEXT=", map_simple_field }, - { "comm=", "_COMM=", map_string_field }, - { "exe=", "_EXE=", map_string_field }, - { "proctitle=", "_CMDLINE=", map_string_field_printable }, - - /* Some fields don't map to native well-known fields. However, - * we know that they are string fields, hence let's undo - * string field escaping for them, though we stick to the - * generic field names. */ - { "path=", "_AUDIT_FIELD_PATH=", map_string_field }, - { "dev=", "_AUDIT_FIELD_DEV=", map_string_field }, - { "name=", "_AUDIT_FIELD_NAME=", map_string_field }, - {} -}; - -/* Userspace fields are those occurring in the audit string after - * msg='. All of these fields are untrusted, hence carry no "_" - * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */ -static const MapField map_fields_userspace[] = { - { "cwd=", "AUDIT_FIELD_CWD=", map_string_field }, - { "cmd=", "AUDIT_FIELD_CMD=", map_string_field }, - { "acct=", "AUDIT_FIELD_ACCT=", map_string_field }, - { "exe=", "AUDIT_FIELD_EXE=", map_string_field }, - { "comm=", "AUDIT_FIELD_COMM=", map_string_field }, - {} -}; - -static int map_all_fields( - const char *p, - const MapField map_fields[], - const char *prefix, - bool handle_msg, - struct iovec **iov, - size_t *n_iov_allocated, - unsigned *n_iov) { - - int r; - - assert(p); - assert(iov); - assert(n_iov_allocated); - assert(n_iov); - - for (;;) { - bool mapped = false; - const MapField *m; - const char *v; - - p += strspn(p, WHITESPACE); - - if (*p == 0) - return 0; - - if (handle_msg) { - v = startswith(p, "msg='"); - if (v) { - const char *e; - char *c; - - /* Userspace message. It's enclosed in - simple quotation marks, is not - escaped, but the last field in the - line, hence let's remove the - quotation mark, and apply the - userspace mapping instead of the - kernel mapping. */ - - e = endswith(v, "'"); - if (!e) - return 0; /* don't continue splitting up if the final quotation mark is missing */ - - c = strndupa(v, e - v); - return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov); - } - } - - /* Try to map the kernel fields to our own names */ - for (m = map_fields; m->audit_field; m++) { - v = startswith(p, m->audit_field); - if (!v) - continue; - - r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov); - if (r < 0) - return log_debug_errno(r, "Failed to parse audit array: %m"); - - if (r > 0) { - mapped = true; - p = v; - break; - } - } - - if (!mapped) { - r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov); - if (r < 0) - return log_debug_errno(r, "Failed to parse audit array: %m"); - - if (r == 0) - /* Couldn't process as generic field, let's just skip over it */ - p += strcspn(p, WHITESPACE); - } - } -} - -static void process_audit_string(Server *s, int type, const char *data, size_t size) { - _cleanup_free_ struct iovec *iov = NULL; - size_t n_iov_allocated = 0; - unsigned n_iov = 0, k; - uint64_t seconds, msec, id; - const char *p, *type_name; - unsigned z; - char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)], - type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)], - source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)]; - char *m; - - assert(s); - - if (size <= 0) - return; - - if (!data) - return; - - /* Note that the input buffer is NUL terminated, but let's - * check whether there is a spurious NUL byte */ - if (memchr(data, 0, size)) - return; - - p = startswith(data, "audit"); - if (!p) - return; - - if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n", - &seconds, - &msec, - &id, - &k) != 3) - return; - - p += k; - p += strspn(p, WHITESPACE); - - if (isempty(p)) - return; - - n_iov_allocated = N_IOVEC_META_FIELDS + 7; - iov = new(struct iovec, n_iov_allocated); - if (!iov) { - log_oom(); - return; - } - - IOVEC_SET_STRING(iov[n_iov++], "_TRANSPORT=audit"); - - sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64, - (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC); - IOVEC_SET_STRING(iov[n_iov++], source_time_field); - - sprintf(type_field, "_AUDIT_TYPE=%i", type); - IOVEC_SET_STRING(iov[n_iov++], type_field); - - sprintf(id_field, "_AUDIT_ID=%" PRIu64, id); - IOVEC_SET_STRING(iov[n_iov++], id_field); - - assert_cc(4 == LOG_FAC(LOG_AUTH)); - IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=4"); - IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit"); - - type_name = audit_type_name_alloca(type); - - m = strjoina("MESSAGE=", type_name, " ", p); - IOVEC_SET_STRING(iov[n_iov++], m); - - z = n_iov; - - map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov); - - if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) { - log_oom(); - goto finish; - } - - server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0); - -finish: - /* free() all entries that map_all_fields() added. All others - * are allocated on the stack or are constant. */ - - for (; z < n_iov; z++) - free(iov[z].iov_base); -} - -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) { - - const struct nlmsghdr *nl = buffer; - - assert(s); - - if (buffer_size < ALIGN(sizeof(struct nlmsghdr))) - return; - - assert(buffer); - - /* Filter out fake data */ - if (!sa || - salen != sizeof(struct sockaddr_nl) || - sa->nl.nl_family != AF_NETLINK || - sa->nl.nl_pid != 0) { - log_debug("Audit netlink message from invalid sender."); - return; - } - - if (!ucred || ucred->pid != 0) { - log_debug("Audit netlink message with invalid credentials."); - return; - } - - if (!NLMSG_OK(nl, buffer_size)) { - log_error("Audit netlink message truncated."); - return; - } - - /* Ignore special Netlink messages */ - if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR)) - return; - - /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */ - if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG) - return; - - process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr))); -} - -static int enable_audit(int fd, bool b) { - struct { - union { - struct nlmsghdr header; - uint8_t header_space[NLMSG_HDRLEN]; - }; - struct audit_status body; - } _packed_ request = { - .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)), - .header.nlmsg_type = AUDIT_SET, - .header.nlmsg_flags = NLM_F_REQUEST, - .header.nlmsg_seq = 1, - .header.nlmsg_pid = 0, - .body.mask = AUDIT_STATUS_ENABLED, - .body.enabled = b, - }; - union sockaddr_union sa = { - .nl.nl_family = AF_NETLINK, - .nl.nl_pid = 0, - }; - struct iovec iovec = { - .iov_base = &request, - .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)), - }; - struct msghdr mh = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_name = &sa.sa, - .msg_namelen = sizeof(sa.nl), - }; - - ssize_t n; - - n = sendmsg(fd, &mh, MSG_NOSIGNAL); - if (n < 0) - return -errno; - if (n != NLMSG_LENGTH(sizeof(struct audit_status))) - return -EIO; - - /* We don't wait for the result here, we can't do anything - * about it anyway */ - - return 0; -} - -int server_open_audit(Server *s) { - static const int one = 1; - int r; - - if (s->audit_fd < 0) { - static const union sockaddr_union sa = { - .nl.nl_family = AF_NETLINK, - .nl.nl_pid = 0, - .nl.nl_groups = AUDIT_NLGRP_READLOG, - }; - - s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); - if (s->audit_fd < 0) { - if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) - log_debug("Audit not supported in the kernel."); - else - log_warning_errno(errno, "Failed to create audit socket, ignoring: %m"); - - return 0; - } - - if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) { - log_warning_errno(errno, - "Failed to join audit multicast group. " - "The kernel is probably too old or multicast reading is not supported. " - "Ignoring: %m"); - s->audit_fd = safe_close(s->audit_fd); - return 0; - } - } else - fd_nonblock(s->audit_fd, 1); - - r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m"); - - r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s); - if (r < 0) - return log_error_errno(r, "Failed to add audit fd to event loop: %m"); - - /* We are listening now, try to enable audit */ - r = enable_audit(s->audit_fd, true); - if (r < 0) - log_warning_errno(r, "Failed to issue audit enable call: %m"); - - return 0; -} diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h deleted file mode 100644 index 8c7457778c..0000000000 --- a/src/journal/journald-audit.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - -int server_open_audit(Server*s); diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c deleted file mode 100644 index fcc9f25814..0000000000 --- a/src/journal/journald-console.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.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) { - - static int cached_printk_time = -1; - - if (_unlikely_(cached_printk_time < 0)) { - _cleanup_free_ char *p = NULL; - - cached_printk_time = - read_one_line_file("/sys/module/printk/parameters/time", &p) >= 0 - && parse_boolean(p) > 0; - } - - return cached_printk_time; -} - -void server_forward_console( - Server *s, - int priority, - const char *identifier, - const char *message, - const struct ucred *ucred) { - - struct iovec iovec[5]; - struct timespec ts; - char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1]; - char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)]; - int n = 0, fd; - _cleanup_free_ char *ident_buf = NULL; - const char *tty; - - assert(s); - assert(message); - - if (LOG_PRI(priority) > s->max_level_console) - return; - - /* First: timestamp */ - if (prefix_timestamp()) { - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ", - ts.tv_sec, - ts.tv_nsec / 1000); - IOVEC_SET_STRING(iovec[n++], tbuf); - } - - /* Second: identifier and PID */ - if (ucred) { - if (!identifier) { - get_process_comm(ucred->pid, &ident_buf); - identifier = ident_buf; - } - - xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); - - if (identifier) - IOVEC_SET_STRING(iovec[n++], identifier); - - IOVEC_SET_STRING(iovec[n++], header_pid); - } else if (identifier) { - IOVEC_SET_STRING(iovec[n++], identifier); - IOVEC_SET_STRING(iovec[n++], ": "); - } - - /* Fourth: message */ - IOVEC_SET_STRING(iovec[n++], message); - IOVEC_SET_STRING(iovec[n++], "\n"); - - tty = s->tty_path ? s->tty_path : "/dev/console"; - - fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - log_debug_errno(fd, "Failed to open %s for logging: %m", tty); - return; - } - - if (writev(fd, iovec, n) < 0) - log_debug_errno(errno, "Failed to write to %s for logging: %m", tty); - - safe_close(fd); -} diff --git a/src/journal/journald-console.h b/src/journal/journald-console.h deleted file mode 100644 index dda07e2c28..0000000000 --- a/src/journal/journald-console.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journald-server.h" - -void server_forward_console(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf deleted file mode 100644 index 7fecd7a964..0000000000 --- a/src/journal/journald-gperf.gperf +++ /dev/null @@ -1,46 +0,0 @@ -%{ -#include -#include -#include "conf-parser.h" -#include "journald-server.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name journald_gperf_hash -%define lookup-function-name journald_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Journal.Storage, config_parse_storage, 0, offsetof(Server, storage) -Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) -Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) -Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) -# The following is a legacy name for compatibility -Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) -Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval) -Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) -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) -Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Server, forward_to_kmsg) -Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Server, forward_to_console) -Journal.ForwardToWall, config_parse_bool, 0, offsetof(Server, forward_to_wall) -Journal.TTYPath, config_parse_path, 0, offsetof(Server, tty_path) -Journal.MaxLevelStore, config_parse_log_level, 0, offsetof(Server, max_level_store) -Journal.MaxLevelSyslog, config_parse_log_level, 0, offsetof(Server, max_level_syslog) -Journal.MaxLevelKMsg, config_parse_log_level, 0, offsetof(Server, max_level_kmsg) -Journal.MaxLevelConsole, config_parse_log_level, 0, offsetof(Server, max_level_console) -Journal.MaxLevelWall, config_parse_log_level, 0, offsetof(Server, max_level_wall) -Journal.SplitMode, config_parse_split_mode, 0, offsetof(Server, split_mode) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c deleted file mode 100644 index f64abdd431..0000000000 --- a/src/journal/journald-kmsg.c +++ /dev/null @@ -1,473 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "sd-messages.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 "parse-util.h" -#include "process-util.h" -#include "stdio-util.h" -#include "string-util.h" - -void server_forward_kmsg( - Server *s, - int priority, - const char *identifier, - const char *message, - const struct ucred *ucred) { - - struct iovec iovec[5]; - char header_priority[DECIMAL_STR_MAX(priority) + 3], - header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1]; - int n = 0; - char *ident_buf = NULL; - - assert(s); - assert(priority >= 0); - assert(priority <= 999); - assert(message); - - if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg)) - return; - - if (_unlikely_(s->dev_kmsg_fd < 0)) - return; - - /* Never allow messages with kernel facility to be written to - * kmsg, regardless where the data comes from. */ - priority = syslog_fixup_facility(priority); - - /* First: priority field */ - xsprintf(header_priority, "<%i>", priority); - IOVEC_SET_STRING(iovec[n++], header_priority); - - /* Second: identifier and PID */ - if (ucred) { - if (!identifier) { - get_process_comm(ucred->pid, &ident_buf); - identifier = ident_buf; - } - - xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); - - if (identifier) - IOVEC_SET_STRING(iovec[n++], identifier); - - IOVEC_SET_STRING(iovec[n++], header_pid); - } else if (identifier) { - IOVEC_SET_STRING(iovec[n++], identifier); - IOVEC_SET_STRING(iovec[n++], ": "); - } - - /* Fourth: message */ - IOVEC_SET_STRING(iovec[n++], message); - IOVEC_SET_STRING(iovec[n++], "\n"); - - if (writev(s->dev_kmsg_fd, iovec, n) < 0) - log_debug_errno(errno, "Failed to write to /dev/kmsg for logging: %m"); - - free(ident_buf); -} - -static bool is_us(const char *pid) { - pid_t t; - - assert(pid); - - if (parse_pid(pid, &t) < 0) - return false; - - return t == getpid(); -} - -static void dev_kmsg_record(Server *s, const char *p, size_t l) { - struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; - char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL; - int priority, r; - unsigned n = 0, z = 0, j; - unsigned long long usec; - char *identifier = NULL, *pid = NULL, *e, *f, *k; - uint64_t serial; - size_t pl; - char *kernel_device = NULL; - - assert(s); - assert(p); - - if (l <= 0) - return; - - e = memchr(p, ',', l); - if (!e) - return; - *e = 0; - - r = safe_atoi(p, &priority); - if (r < 0 || priority < 0 || priority > 999) - return; - - if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) - return; - - l -= (e - p) + 1; - p = e + 1; - e = memchr(p, ',', l); - if (!e) - return; - *e = 0; - - r = safe_atou64(p, &serial); - if (r < 0) - return; - - if (s->kernel_seqnum) { - /* We already read this one? */ - if (serial < *s->kernel_seqnum) - return; - - /* Did we lose any? */ - if (serial > *s->kernel_seqnum) - server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, - LOG_MESSAGE("Missed %"PRIu64" kernel messages", - serial - *s->kernel_seqnum), - NULL); - - /* Make sure we never read this one again. Note that - * we always store the next message serial we expect - * here, simply because this makes handling the first - * message with serial 0 easy. */ - *s->kernel_seqnum = serial + 1; - } - - l -= (e - p) + 1; - p = e + 1; - f = memchr(p, ';', l); - if (!f) - return; - /* Kernel 3.6 has the flags field, kernel 3.5 lacks that */ - e = memchr(p, ',', l); - if (!e || f < e) - e = f; - *e = 0; - - r = safe_atollu(p, &usec); - if (r < 0) - return; - - l -= (f - p) + 1; - p = f + 1; - e = memchr(p, '\n', l); - if (!e) - return; - *e = 0; - - pl = e - p; - l -= (e - p) + 1; - k = e + 1; - - for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) { - char *m; - /* Metadata fields attached */ - - if (*k != ' ') - break; - - k++, l--; - - e = memchr(k, '\n', l); - if (!e) - return; - - *e = 0; - - if (cunescape_length_with_prefix(k, e - k, "_KERNEL_", UNESCAPE_RELAX, &m) < 0) - break; - - if (startswith(m, "_KERNEL_DEVICE=")) - kernel_device = m + 15; - - IOVEC_SET_STRING(iovec[n++], m); - z++; - - l -= (e - k) + 1; - k = e + 1; - } - - if (kernel_device) { - struct udev_device *ud; - - ud = udev_device_new_from_device_id(s->udev, kernel_device); - if (ud) { - const char *g; - struct udev_list_entry *ll; - char *b; - - g = udev_device_get_devnode(ud); - if (g) { - b = strappend("_UDEV_DEVNODE=", g); - if (b) { - IOVEC_SET_STRING(iovec[n++], b); - z++; - } - } - - g = udev_device_get_sysname(ud); - if (g) { - b = strappend("_UDEV_SYSNAME=", g); - if (b) { - IOVEC_SET_STRING(iovec[n++], b); - z++; - } - } - - j = 0; - ll = udev_device_get_devlinks_list_entry(ud); - udev_list_entry_foreach(ll, ll) { - - if (j > N_IOVEC_UDEV_FIELDS) - break; - - g = udev_list_entry_get_name(ll); - if (g) { - b = strappend("_UDEV_DEVLINK=", g); - if (b) { - IOVEC_SET_STRING(iovec[n++], b); - z++; - } - } - - j++; - } - - udev_device_unref(ud); - } - } - - if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0) - IOVEC_SET_STRING(iovec[n++], source_time); - - IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel"); - - if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_priority); - - if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_facility); - - if ((priority & LOG_FACMASK) == LOG_KERN) - IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel"); - else { - pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid); - - /* Avoid any messages we generated ourselves via - * log_info() and friends. */ - if (pid && is_us(pid)) - goto finish; - - if (identifier) { - syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); - if (syslog_identifier) - IOVEC_SET_STRING(iovec[n++], syslog_identifier); - } - - if (pid) { - syslog_pid = strappend("SYSLOG_PID=", pid); - if (syslog_pid) - IOVEC_SET_STRING(iovec[n++], syslog_pid); - } - } - - if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0) - IOVEC_SET_STRING(iovec[n++], message); - - server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0); - -finish: - for (j = 0; j < z; j++) - free(iovec[j].iov_base); - - free(message); - free(syslog_priority); - free(syslog_identifier); - free(syslog_pid); - free(syslog_facility); - free(source_time); - free(identifier); - free(pid); -} - -static int server_read_dev_kmsg(Server *s) { - char buffer[8192+1]; /* the kernel-side limit per record is 8K currently */ - ssize_t l; - - assert(s); - assert(s->dev_kmsg_fd >= 0); - - l = read(s->dev_kmsg_fd, buffer, sizeof(buffer) - 1); - if (l == 0) - return 0; - if (l < 0) { - /* Old kernels who don't allow reading from /dev/kmsg - * return EINVAL when we try. So handle this cleanly, - * but don' try to ever read from it again. */ - if (errno == EINVAL) { - s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source); - return 0; - } - - if (errno == EAGAIN || errno == EINTR || errno == EPIPE) - return 0; - - return log_error_errno(errno, "Failed to read from kernel: %m"); - } - - dev_kmsg_record(s, buffer, l); - return 1; -} - -int server_flush_dev_kmsg(Server *s) { - int r; - - assert(s); - - if (s->dev_kmsg_fd < 0) - return 0; - - if (!s->dev_kmsg_readable) - return 0; - - log_debug("Flushing /dev/kmsg..."); - - for (;;) { - r = server_read_dev_kmsg(s); - if (r < 0) - return r; - - if (r == 0) - break; - } - - return 0; -} - -static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Server *s = userdata; - - assert(es); - assert(fd == s->dev_kmsg_fd); - assert(s); - - if (revents & EPOLLERR) - log_warning("/dev/kmsg buffer overrun, some messages lost."); - - if (!(revents & EPOLLIN)) - log_error("Got invalid event from epoll for /dev/kmsg: %"PRIx32, revents); - - return server_read_dev_kmsg(s); -} - -int server_open_dev_kmsg(Server *s) { - int r; - - assert(s); - - s->dev_kmsg_fd = open("/dev/kmsg", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (s->dev_kmsg_fd < 0) { - log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "Failed to open /dev/kmsg, ignoring: %m"); - return 0; - } - - r = sd_event_add_io(s->event, &s->dev_kmsg_event_source, s->dev_kmsg_fd, EPOLLIN, dispatch_dev_kmsg, s); - if (r < 0) { - - /* This will fail with EPERM on older kernels where - * /dev/kmsg is not readable. */ - if (r == -EPERM) { - r = 0; - goto fail; - } - - log_error_errno(r, "Failed to add /dev/kmsg fd to event loop: %m"); - goto fail; - } - - r = sd_event_source_set_priority(s->dev_kmsg_event_source, SD_EVENT_PRIORITY_IMPORTANT+10); - if (r < 0) { - log_error_errno(r, "Failed to adjust priority of kmsg event source: %m"); - goto fail; - } - - s->dev_kmsg_readable = true; - - return 0; - -fail: - s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source); - s->dev_kmsg_fd = safe_close(s->dev_kmsg_fd); - - return r; -} - -int server_open_kernel_seqnum(Server *s) { - _cleanup_close_ int fd; - uint64_t *p; - int r; - - assert(s); - - /* We store the seqnum we last read in an mmaped file. That - * way we can just use it like a variable, but it is - * persistent and automatically flushed at reboot. */ - - fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); - if (fd < 0) { - log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m"); - return 0; - } - - 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; - } - - p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) { - log_error_errno(errno, "Failed to map sequential number file, ignoring: %m"); - return 0; - } - - s->kernel_seqnum = p; - - return 0; -} diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h deleted file mode 100644 index dab49f1e8c..0000000000 --- a/src/journal/journald-kmsg.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journald-server.h" - -int server_open_dev_kmsg(Server *s); -int server_flush_dev_kmsg(Server *s); - -void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); - -int server_open_kernel_seqnum(Server *s); diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c deleted file mode 100644 index 0a1ce205c2..0000000000 --- a/src/journal/journald-native.c +++ /dev/null @@ -1,501 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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; - - /* We kinda enforce POSIX syntax recommendations for - environment variables here, but make a couple of additional - requirements. - - http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */ - - /* No empty field names */ - if (l <= 0) - return false; - - /* Don't allow names longer than 64 chars */ - if (l > 64) - return false; - - /* Variables starting with an underscore are protected */ - if (!allow_protected && p[0] == '_') - return false; - - /* Don't allow digits as first character */ - if (p[0] >= '0' && p[0] <= '9') - return false; - - /* Only allow A-Z0-9 and '_' */ - for (a = p; a < p + l; a++) - if ((*a < 'A' || *a > 'Z') && - (*a < '0' || *a > '9') && - *a != '_') - return false; - - return true; -} - -static bool allow_object_pid(const struct ucred *ucred) { - return ucred && ucred->uid == 0; -} - -void server_process_native_message( - Server *s, - const void *buffer, size_t buffer_size, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, size_t label_len) { - - struct iovec *iovec = NULL; - unsigned n = 0, j, tn = (unsigned) -1; - const char *p; - size_t remaining, m = 0, entry_size = 0; - int priority = LOG_INFO; - char *identifier = NULL, *message = NULL; - pid_t object_pid = 0; - - assert(s); - assert(buffer || buffer_size == 0); - - p = buffer; - remaining = buffer_size; - - while (remaining > 0) { - const char *e, *q; - - e = memchr(p, '\n', remaining); - - if (!e) { - /* Trailing noise, let's ignore it, and flush what we collected */ - log_debug("Received message with trailing noise, ignoring."); - break; - } - - if (e == p) { - /* Entry separator */ - - if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ - log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); - continue; - } - - server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); - n = 0; - priority = LOG_INFO; - entry_size = 0; - - p++; - remaining--; - continue; - } - - if (*p == '.' || *p == '#') { - /* Ignore control commands for now, and - * comments too. */ - remaining -= (e - p) + 1; - p = e + 1; - continue; - } - - /* A property follows */ - - /* n existing properties, 1 new, +1 for _TRANSPORT */ - if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { - log_oom(); - break; - } - - q = memchr(p, '=', e - p); - if (q) { - if (valid_user_field(p, q - p, false)) { - size_t l; - - l = e - p; - - /* If the field name starts with an - * underscore, skip the variable, - * since that indidates a trusted - * field */ - iovec[n].iov_base = (char*) p; - iovec[n].iov_len = l; - entry_size += iovec[n].iov_len; - n++; - - /* We need to determine the priority - * of this entry for the rate limiting - * logic */ - if (l == 10 && - startswith(p, "PRIORITY=") && - p[9] >= '0' && p[9] <= '9') - priority = (priority & LOG_FACMASK) | (p[9] - '0'); - - else if (l == 17 && - startswith(p, "SYSLOG_FACILITY=") && - p[16] >= '0' && p[16] <= '9') - priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3); - - else if (l == 18 && - startswith(p, "SYSLOG_FACILITY=") && - p[16] >= '0' && p[16] <= '9' && - p[17] >= '0' && p[17] <= '9') - priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); - - else if (l >= 19 && - startswith(p, "SYSLOG_IDENTIFIER=")) { - char *t; - - t = strndup(p + 18, l - 18); - if (t) { - free(identifier); - identifier = t; - } - - } else if (l >= 8 && - startswith(p, "MESSAGE=")) { - char *t; - - t = strndup(p + 8, l - 8); - if (t) { - free(message); - message = t; - } - - } else if (l > strlen("OBJECT_PID=") && - l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) && - startswith(p, "OBJECT_PID=") && - allow_object_pid(ucred)) { - char buf[DECIMAL_STR_MAX(pid_t)]; - memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); - buf[l-strlen("OBJECT_PID=")] = '\0'; - - /* ignore error */ - parse_pid(buf, &object_pid); - } - } - - remaining -= (e - p) + 1; - p = e + 1; - continue; - } else { - le64_t l_le; - uint64_t l; - char *k; - - if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { - log_debug("Failed to parse message, ignoring."); - break; - } - - memcpy(&l_le, e + 1, sizeof(uint64_t)); - l = le64toh(l_le); - - if (l > DATA_SIZE_MAX) { - log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); - break; - } - - if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || - e[1+sizeof(uint64_t)+l] != '\n') { - log_debug("Failed to parse message, ignoring."); - break; - } - - k = malloc((e - p) + 1 + l); - if (!k) { - log_oom(); - break; - } - - memcpy(k, p, e - p); - k[e - p] = '='; - memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); - - if (valid_user_field(p, e - p, false)) { - iovec[n].iov_base = k; - iovec[n].iov_len = (e - p) + 1 + l; - entry_size += iovec[n].iov_len; - n++; - } else - free(k); - - remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; - p = e + 1 + sizeof(uint64_t) + l + 1; - } - } - - if (n <= 0) - goto finish; - - tn = n++; - IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); - entry_size += strlen("_TRANSPORT=journal"); - - if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ - log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", - n, entry_size); - goto finish; - } - - if (message) { - if (s->forward_to_syslog) - server_forward_syslog(s, priority, identifier, message, ucred, tv); - - if (s->forward_to_kmsg) - server_forward_kmsg(s, priority, identifier, message, ucred); - - if (s->forward_to_console) - server_forward_console(s, priority, identifier, message, ucred); - - if (s->forward_to_wall) - server_forward_wall(s, priority, identifier, message, ucred); - } - - server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); - -finish: - for (j = 0; j < n; j++) { - if (j == tn) - continue; - - if (iovec[j].iov_base < buffer || - (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) - free(iovec[j].iov_base); - } - - free(iovec); - free(identifier); - free(message); -} - -void server_process_native_file( - Server *s, - int fd, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, size_t label_len) { - - struct stat st; - bool sealed; - int r; - - /* Data is in the passed fd, since it didn't fit in a - * datagram. */ - - assert(s); - assert(fd >= 0); - - /* If it's a memfd, check if it is sealed. If so, we can just - * use map it and use it, and do not need to copy the data - * out. */ - sealed = memfd_get_sealed(fd) > 0; - - if (!sealed && (!ucred || ucred->uid != 0)) { - _cleanup_free_ char *sl = NULL, *k = NULL; - const char *e; - - /* If this is not a sealed memfd, and the peer is unknown or - * unprivileged, then verify the path. */ - - if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) { - log_oom(); - return; - } - - r = readlink_malloc(sl, &k); - if (r < 0) { - log_error_errno(r, "readlink(%s) failed: %m", sl); - return; - } - - e = path_startswith(k, "/dev/shm/"); - if (!e) - e = path_startswith(k, "/tmp/"); - if (!e) - e = path_startswith(k, "/var/tmp/"); - if (!e) { - log_error("Received file outside of allowed directories. Refusing."); - return; - } - - if (!filename_is_valid(e)) { - log_error("Received file in subdirectory of allowed directories. Refusing."); - return; - } - } - - if (fstat(fd, &st) < 0) { - log_error_errno(errno, "Failed to stat passed file, ignoring: %m"); - return; - } - - if (!S_ISREG(st.st_mode)) { - log_error("File passed is not regular. Ignoring."); - return; - } - - if (st.st_size <= 0) - return; - - if (st.st_size > ENTRY_SIZE_MAX) { - log_error("File passed too large. Ignoring."); - return; - } - - if (sealed) { - void *p; - size_t ps; - - /* The file is sealed, we can just map it and use it. */ - - ps = PAGE_ALIGN(st.st_size); - p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0); - if (p == MAP_FAILED) { - log_error_errno(errno, "Failed to map memfd, ignoring: %m"); - return; - } - - server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len); - 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 */ - - p = malloc(st.st_size); - if (!p) { - log_oom(); - return; - } - - n = pread(fd, p, st.st_size, 0); - if (n < 0) - 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); - } -} - -int server_open_native_socket(Server*s) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - static const int one = 1; - int r; - - assert(s); - - if (s->native_fd < 0) { - s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->native_fd < 0) - return log_error_errno(errno, "socket() failed: %m"); - - (void) unlink(sa.un.sun_path); - - r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - - (void) chmod(sa.un.sun_path, 0666); - } else - fd_nonblock(s->native_fd, 1); - - r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "SO_PASSCRED failed: %m"); - -#ifdef HAVE_SELINUX - 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"); - } -#endif - - r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); - - r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, 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-native.h b/src/journal/journald-native.h deleted file mode 100644 index c13b80aa4f..0000000000 --- a/src/journal/journald-native.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journald-server.h" - -/* Make sure not to make this smaller than the maximum coredump - * size. See COREDUMP_MAX in coredump.c */ -#define ENTRY_SIZE_MAX (1024*1024*770u) -#define DATA_SIZE_MAX (1024*1024*768u) - -bool valid_user_field(const char *p, size_t l, bool allow_protected); - -void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); - -void server_process_native_file(Server *s, int fd, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); - -int server_open_native_socket(Server*s); diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c deleted file mode 100644 index fce799a6ce..0000000000 --- a/src/journal/journald-rate-limit.c +++ /dev/null @@ -1,271 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "hashmap.h" -#include "journald-rate-limit.h" -#include "list.h" -#include "random-util.h" -#include "string-util.h" -#include "util.h" - -#define POOLS_MAX 5 -#define BUCKETS_MAX 127 -#define GROUPS_MAX 2047 - -static const int priority_map[] = { - [LOG_EMERG] = 0, - [LOG_ALERT] = 0, - [LOG_CRIT] = 0, - [LOG_ERR] = 1, - [LOG_WARNING] = 2, - [LOG_NOTICE] = 3, - [LOG_INFO] = 3, - [LOG_DEBUG] = 4 -}; - -typedef struct JournalRateLimitPool JournalRateLimitPool; -typedef struct JournalRateLimitGroup JournalRateLimitGroup; - -struct JournalRateLimitPool { - usec_t begin; - unsigned num; - unsigned suppressed; -}; - -struct JournalRateLimitGroup { - JournalRateLimit *parent; - - char *id; - JournalRateLimitPool pools[POOLS_MAX]; - uint64_t hash; - - LIST_FIELDS(JournalRateLimitGroup, bucket); - LIST_FIELDS(JournalRateLimitGroup, lru); -}; - -struct JournalRateLimit { - usec_t interval; - unsigned burst; - - JournalRateLimitGroup* buckets[BUCKETS_MAX]; - JournalRateLimitGroup *lru, *lru_tail; - - unsigned n_groups; - - uint8_t hash_key[16]; -}; - -JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { - JournalRateLimit *r; - - assert(interval > 0 || burst == 0); - - r = new0(JournalRateLimit, 1); - if (!r) - return NULL; - - r->interval = interval; - r->burst = burst; - - random_bytes(r->hash_key, sizeof(r->hash_key)); - - return r; -} - -static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { - assert(g); - - if (g->parent) { - assert(g->parent->n_groups > 0); - - if (g->parent->lru_tail == g) - g->parent->lru_tail = g->lru_prev; - - LIST_REMOVE(lru, g->parent->lru, g); - LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g); - - g->parent->n_groups--; - } - - free(g->id); - free(g); -} - -void journal_rate_limit_free(JournalRateLimit *r) { - assert(r); - - while (r->lru) - journal_rate_limit_group_free(r->lru); - - free(r); -} - -_pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { - unsigned i; - - assert(g); - - for (i = 0; i < POOLS_MAX; i++) - if (g->pools[i].begin + g->parent->interval >= ts) - return false; - - return true; -} - -static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { - assert(r); - - /* Makes room for at least one new item, but drop all - * expored items too. */ - - while (r->n_groups >= GROUPS_MAX || - (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts))) - journal_rate_limit_group_free(r->lru_tail); -} - -static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { - JournalRateLimitGroup *g; - struct siphash state; - - assert(r); - assert(id); - - g = new0(JournalRateLimitGroup, 1); - if (!g) - return NULL; - - g->id = strdup(id); - if (!g->id) - goto fail; - - siphash24_init(&state, r->hash_key); - string_hash_func(g->id, &state); - g->hash = siphash24_finalize(&state); - - journal_rate_limit_vacuum(r, ts); - - LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g); - LIST_PREPEND(lru, r->lru, g); - if (!g->lru_next) - r->lru_tail = g; - r->n_groups++; - - g->parent = r; - return g; - -fail: - journal_rate_limit_group_free(g); - return NULL; -} - -static unsigned burst_modulate(unsigned burst, uint64_t available) { - unsigned k; - - /* Modulates the burst rate a bit with the amount of available - * disk space */ - - k = u64log2(available); - - /* 1MB */ - if (k <= 20) - return burst; - - burst = (burst * (k-20)) / 4; - - /* - * Example: - * - * <= 1MB = rate * 1 - * 16MB = rate * 2 - * 256MB = rate * 3 - * 4GB = rate * 4 - * 64GB = rate * 5 - * 1TB = rate * 6 - */ - - return burst; -} - -int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { - uint64_t h; - JournalRateLimitGroup *g; - JournalRateLimitPool *p; - struct siphash state; - unsigned burst; - usec_t ts; - - assert(id); - - if (!r) - return 1; - - if (r->interval == 0 || r->burst == 0) - return 1; - - burst = burst_modulate(r->burst, available); - - ts = now(CLOCK_MONOTONIC); - - 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) - if (streq(g->id, id)) - break; - - if (!g) { - g = journal_rate_limit_group_new(r, id, ts); - if (!g) - return -ENOMEM; - } - - p = &g->pools[priority_map[priority]]; - - if (p->begin <= 0) { - p->suppressed = 0; - p->num = 1; - p->begin = ts; - return 1; - } - - if (p->begin + r->interval < ts) { - unsigned s; - - s = p->suppressed; - p->suppressed = 0; - p->num = 1; - p->begin = ts; - - return 1 + s; - } - - if (p->num <= burst) { - p->num++; - return 1; - } - - p->suppressed++; - return 0; -} diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h deleted file mode 100644 index bb0abb7ee9..0000000000 --- a/src/journal/journald-rate-limit.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "util.h" - -typedef struct JournalRateLimit JournalRateLimit; - -JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); -void journal_rate_limit_free(JournalRateLimit *r); -int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c deleted file mode 100644 index 587c343b31..0000000000 --- a/src/journal/journald-server.c +++ /dev/null @@ -1,2007 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_SELINUX -#include -#endif -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "sd-daemon.h" -#include "sd-journal.h" -#include "sd-messages.h" - -#include "acl-util.h" -#include "alloc-util.h" -#include "audit-util.h" -#include "cgroup-util.h" -#include "conf-parser.h" -#include "dirent-util.h" -#include "extract-word.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-authenticate.h" -#include "journal-file.h" -#include "journal-internal.h" -#include "journal-vacuum.h" -#include "journald-audit.h" -#include "journald-kmsg.h" -#include "journald-native.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 "stdio-util.h" -#include "string-table.h" -#include "string-util.h" -#include "user-util.h" -#include "log.h" - -#define USER_JOURNALS_MAX 1024 - -#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE) -#define DEFAULT_RATE_LIMIT_INTERVAL (30*USEC_PER_SEC) -#define DEFAULT_RATE_LIMIT_BURST 1000 -#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH - -#define RECHECK_SPACE_USEC (30*USEC_PER_SEC) - -#define NOTIFY_SNDBUF_SIZE (8*1024*1024) - -/* The period to insert between posting changes for coalescing */ -#define POST_CHANGE_TIMER_INTERVAL_USEC (250*USEC_PER_MSEC) - -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; - - assert(s); - assert(metrics); - assert(path); - assert(name); - - ts = now(CLOCK_MONOTONIC); - - if (!verbose && s->cached_space_timestamp + RECHECK_SPACE_USEC > ts) { - - if (available) - *available = s->cached_space_available; - if (limit) - *limit = s->cached_space_limit; - - return 0; - } - - p = strjoina(path, SERVER_MACHINE_ID(s)); - d = opendir(p); - if (!d) - return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open %s: %m", p); - - if (fstatvfs(dirfd(d), &ss) < 0) - return log_error_errno(errno, "Failed to fstatvfs(%s): %m", p); - - FOREACH_DIRENT_ALL(de, d, break) { - struct stat st; - - if (!endswith(de->d_name, ".journal") && - !endswith(de->d_name, ".journal~")) - continue; - - 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; - - sum += (uint64_t) st.st_blocks * 512UL; - } - - /* If requested, 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 (patch_min_use) - metrics->min_use = MAX(metrics->min_use, sum); - - ss_avail = ss.f_bsize * ss.f_bavail; - avail = LESS_BY(ss_avail, metrics->keep_free); - - 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], fb6[FORMAT_BYTES_MAX]; - format_bytes(fb1, sizeof(fb1), sum); - 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_space_limit); - format_bytes(fb6, sizeof(fb6), s->cached_space_available); - - server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE, - LOG_MESSAGE("%s (%s) is %s, max %s, %s free.", - name, path, fb1, fb5, fb6), - "JOURNAL_NAME=%s", name, - "JOURNAL_PATH=%s", path, - "CURRENT_USE=%"PRIu64, sum, - "CURRENT_USE_PRETTY=%s", fb1, - "MAX_USE=%"PRIu64, metrics->max_use, - "MAX_USE_PRETTY=%s", fb2, - "DISK_KEEP_FREE=%"PRIu64, metrics->keep_free, - "DISK_KEEP_FREE_PRETTY=%s", fb3, - "DISK_AVAILABLE=%"PRIu64, ss_avail, - "DISK_AVAILABLE_PRETTY=%s", fb4, - "LIMIT=%"PRIu64, s->cached_space_limit, - "LIMIT_PRETTY=%s", fb5, - "AVAILABLE=%"PRIu64, s->cached_space_available, - "AVAILABLE_PRETTY=%s", fb6, - NULL); - } - - if (available) - *available = s->cached_space_available; - if (limit) - *limit = s->cached_space_limit; - - return 1; -} - -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 - int r; -#endif - assert(f); - -#ifdef HAVE_ACL - if (uid <= SYSTEM_UID_MAX) - return; - - 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 -} - -static int open_journal( - Server *s, - bool reliably, - const char *fname, - int flags, - bool seal, - JournalMetrics *metrics, - JournalFile **ret) { - int r; - JournalFile *f; - - assert(s); - assert(fname); - assert(ret); - - if (reliably) - r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); - else - r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); - if (r < 0) - return r; - - r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC); - if (r < 0) { - (void) journal_file_close(f); - return r; - } - - *ret = f; - return r; -} - -static JournalFile* find_journal(Server *s, uid_t uid) { - _cleanup_free_ char *p = NULL; - int r; - JournalFile *f; - sd_id128_t machine; - - assert(s); - - /* We split up user logs only on /var, not on /run. If the - * runtime file is open, we write to it exclusively, in order - * to guarantee proper order as soon as we flush /run to - * /var and close the runtime file. */ - - if (s->runtime_journal) - return s->runtime_journal; - - if (uid <= SYSTEM_UID_MAX) - return s->system_journal; - - r = sd_id128_get_machine(&machine); - if (r < 0) - return s->system_journal; - - f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid)); - if (f) - return f; - - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal", - SD_ID128_FORMAT_VAL(machine), uid) < 0) - return s->system_journal; - - while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { - /* Too many open? Then let's close one */ - f = ordered_hashmap_steal_first(s->user_journals); - assert(f); - (void) journal_file_close(f); - } - - r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &f); - if (r < 0) - return s->system_journal; - - server_add_acls(f, uid); - - r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); - if (r < 0) { - (void) journal_file_close(f); - return s->system_journal; - } - - return f; -} - -static int do_rotate( - Server *s, - JournalFile **f, - const char* name, - bool seal, - uint32_t uid) { - - int r; - assert(s); - - if (!*f) - return -EINVAL; - - r = journal_file_rotate(f, s->compress, seal, s->deferred_closes); - if (r < 0) - if (*f) - log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); - else - log_error_errno(r, "Failed to create new %s journal: %m", name); - else - server_add_acls(*f, uid); - - return r; -} - -void server_rotate(Server *s) { - JournalFile *f; - void *k; - Iterator i; - int r; - - log_debug("Rotating..."); - - (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_UID(k)); - if (r >= 0) - ordered_hashmap_replace(s->user_journals, k, f); - else if (!f) - /* Old file has been closed and deallocated */ - ordered_hashmap_remove(s->user_journals, k); - } - - /* Perform any deferred closes which aren't still offlining. */ - SET_FOREACH(f, s->deferred_closes, i) - if (!journal_file_is_offlining(f)) { - (void) set_remove(s->deferred_closes, f); - (void) journal_file_close(f); - } -} - -void server_sync(Server *s) { - JournalFile *f; - Iterator i; - int r; - - if (s->system_journal) { - r = journal_file_set_offline(s->system_journal, false); - if (r < 0) - log_warning_errno(r, "Failed to sync system journal, ignoring: %m"); - } - - ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) { - r = journal_file_set_offline(f, false); - if (r < 0) - log_warning_errno(r, "Failed to sync user journal, ignoring: %m"); - } - - if (s->sync_event_source) { - r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF); - if (r < 0) - log_error_errno(r, "Failed to disable sync timer source: %m"); - } - - s->sync_scheduled = false; -} - -static void do_vacuum( - Server *s, - JournalFile *f, - 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, 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_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p); -} - -int server_vacuum(Server *s, bool verbose, bool patch_min_use) { - assert(s); - - log_debug("Vacuuming..."); - - s->oldest_file_usec = 0; - - 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); - - s->cached_space_limit = 0; - s->cached_space_available = 0; - s->cached_space_timestamp = 0; - - return 0; -} - -static void server_cache_machine_id(Server *s) { - sd_id128_t id; - int r; - - assert(s); - - r = sd_id128_get_machine(&id); - if (r < 0) - return; - - sd_id128_to_string(id, stpcpy(s->machine_id_field, "_MACHINE_ID=")); -} - -static void server_cache_boot_id(Server *s) { - sd_id128_t id; - int r; - - assert(s); - - r = sd_id128_get_boot(&id); - if (r < 0) - return; - - sd_id128_to_string(id, stpcpy(s->boot_id_field, "_BOOT_ID=")); -} - -static void server_cache_hostname(Server *s) { - _cleanup_free_ char *t = NULL; - char *x; - - assert(s); - - t = gethostname_malloc(); - if (!t) - return; - - x = strappend("_HOSTNAME=", t); - if (!x) - return; - - free(s->hostname_field); - s->hostname_field = x; -} - -static bool shall_try_append_again(JournalFile *f, int r) { - switch(r) { - case -E2BIG: /* Hit configured limit */ - case -EFBIG: /* Hit fs limit */ - case -EDQUOT: /* Quota limit hit */ - case -ENOSPC: /* Disk full */ - log_debug("%s: Allocation limit reached, rotating.", f->path); - return true; - case -EIO: /* I/O error of some kind (mmap) */ - log_warning("%s: IO error, rotating.", f->path); - return true; - case -EHOSTDOWN: /* Other machine */ - log_info("%s: Journal file from other machine, rotating.", f->path); - return true; - case -EBUSY: /* Unclean shutdown */ - log_info("%s: Unclean shutdown, rotating.", f->path); - return true; - case -EPROTONOSUPPORT: /* Unsupported feature */ - log_info("%s: Unsupported feature, rotating.", f->path); - return true; - case -EBADMSG: /* Corrupted */ - case -ENODATA: /* Truncated */ - case -ESHUTDOWN: /* Already archived */ - log_warning("%s: Journal file corrupted, rotating.", f->path); - return true; - case -EIDRM: /* Journal file has been deleted */ - log_warning("%s: Journal file has been deleted, rotating.", f->path); - return true; - default: - return false; - } -} - -static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n, int priority) { - JournalFile *f; - bool vacuumed = false; - int r; - - assert(s); - assert(iovec); - assert(n > 0); - - f = find_journal(s, uid); - if (!f) - return; - - 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, false, false); - vacuumed = true; - - f = find_journal(s, uid); - if (!f) - return; - } - - r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); - if (r >= 0) { - server_schedule_sync(s, priority); - return; - } - - if (vacuumed || !shall_try_append_again(f, r)) { - log_error_errno(r, "Failed to write entry (%d items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n)); - return; - } - - server_rotate(s); - server_vacuum(s, false, false); - - f = find_journal(s, uid); - if (!f) - return; - - log_debug("Retrying write."); - r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); - if (r < 0) - log_error_errno(r, "Failed to write entry (%d items, %zu bytes) despite vacuuming, ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n)); - else - server_schedule_sync(s, priority); -} - -static void dispatch_message_real( - Server *s, - struct iovec *iovec, unsigned n, unsigned m, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, size_t label_len, - const char *unit_id, - int priority, - pid_t object_pid) { - - char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)], - uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)], - gid[sizeof("_GID=") + DECIMAL_STR_MAX(gid_t)], - owner_uid[sizeof("_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)], - source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)], - o_uid[sizeof("OBJECT_UID=") + DECIMAL_STR_MAX(uid_t)], - o_gid[sizeof("OBJECT_GID=") + DECIMAL_STR_MAX(gid_t)], - o_owner_uid[sizeof("OBJECT_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)]; - uid_t object_uid; - gid_t object_gid; - char *x; - int r; - char *t, *c; - uid_t realuid = 0, owner = 0, journal_uid; - bool owner_valid = false; -#ifdef HAVE_AUDIT - char audit_session[sizeof("_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)], - audit_loginuid[sizeof("_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)], - o_audit_session[sizeof("OBJECT_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)], - o_audit_loginuid[sizeof("OBJECT_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)]; - - uint32_t audit; - uid_t loginuid; -#endif - - assert(s); - assert(iovec); - assert(n > 0); - assert(n + N_IOVEC_META_FIELDS + (object_pid ? N_IOVEC_OBJECT_FIELDS : 0) <= m); - - if (ucred) { - realuid = ucred->uid; - - sprintf(pid, "_PID="PID_FMT, ucred->pid); - IOVEC_SET_STRING(iovec[n++], pid); - - sprintf(uid, "_UID="UID_FMT, ucred->uid); - IOVEC_SET_STRING(iovec[n++], uid); - - sprintf(gid, "_GID="GID_FMT, ucred->gid); - IOVEC_SET_STRING(iovec[n++], gid); - - r = get_process_comm(ucred->pid, &t); - if (r >= 0) { - x = strjoina("_COMM=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - r = get_process_exe(ucred->pid, &t); - if (r >= 0) { - x = strjoina("_EXE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - r = get_process_cmdline(ucred->pid, 0, false, &t); - if (r >= 0) { - x = strjoina("_CMDLINE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - r = get_process_capeff(ucred->pid, &t); - if (r >= 0) { - x = strjoina("_CAP_EFFECTIVE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - -#ifdef HAVE_AUDIT - r = audit_session_from_pid(ucred->pid, &audit); - if (r >= 0) { - sprintf(audit_session, "_AUDIT_SESSION=%"PRIu32, audit); - IOVEC_SET_STRING(iovec[n++], audit_session); - } - - r = audit_loginuid_from_pid(ucred->pid, &loginuid); - if (r >= 0) { - sprintf(audit_loginuid, "_AUDIT_LOGINUID="UID_FMT, loginuid); - IOVEC_SET_STRING(iovec[n++], audit_loginuid); - } -#endif - - r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c); - if (r >= 0) { - char *session = NULL; - - x = strjoina("_SYSTEMD_CGROUP=", c); - IOVEC_SET_STRING(iovec[n++], x); - - r = cg_path_get_session(c, &t); - if (r >= 0) { - session = strjoina("_SYSTEMD_SESSION=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], session); - } - - if (cg_path_get_owner_uid(c, &owner) >= 0) { - owner_valid = true; - - sprintf(owner_uid, "_SYSTEMD_OWNER_UID="UID_FMT, owner); - IOVEC_SET_STRING(iovec[n++], owner_uid); - } - - if (cg_path_get_unit(c, &t) >= 0) { - x = strjoina("_SYSTEMD_UNIT=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } else if (unit_id && !session) { - x = strjoina("_SYSTEMD_UNIT=", unit_id); - IOVEC_SET_STRING(iovec[n++], x); - } - - if (cg_path_get_user_unit(c, &t) >= 0) { - x = strjoina("_SYSTEMD_USER_UNIT=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } else if (unit_id && session) { - x = strjoina("_SYSTEMD_USER_UNIT=", unit_id); - IOVEC_SET_STRING(iovec[n++], x); - } - - if (cg_path_get_slice(c, &t) >= 0) { - x = strjoina("_SYSTEMD_SLICE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - free(c); - } else if (unit_id) { - x = strjoina("_SYSTEMD_UNIT=", unit_id); - IOVEC_SET_STRING(iovec[n++], x); - } - -#ifdef HAVE_SELINUX - if (mac_selinux_have()) { - if (label) { - x = alloca(strlen("_SELINUX_CONTEXT=") + label_len + 1); - - *((char*) mempcpy(stpcpy(x, "_SELINUX_CONTEXT="), label, label_len)) = 0; - IOVEC_SET_STRING(iovec[n++], x); - } else { - char *con; - - if (getpidcon(ucred->pid, &con) >= 0) { - x = strjoina("_SELINUX_CONTEXT=", con); - - freecon(con); - IOVEC_SET_STRING(iovec[n++], x); - } - } - } -#endif - } - assert(n <= m); - - if (object_pid) { - r = get_process_uid(object_pid, &object_uid); - if (r >= 0) { - sprintf(o_uid, "OBJECT_UID="UID_FMT, object_uid); - IOVEC_SET_STRING(iovec[n++], o_uid); - } - - r = get_process_gid(object_pid, &object_gid); - if (r >= 0) { - sprintf(o_gid, "OBJECT_GID="GID_FMT, object_gid); - IOVEC_SET_STRING(iovec[n++], o_gid); - } - - r = get_process_comm(object_pid, &t); - if (r >= 0) { - x = strjoina("OBJECT_COMM=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - r = get_process_exe(object_pid, &t); - if (r >= 0) { - x = strjoina("OBJECT_EXE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - r = get_process_cmdline(object_pid, 0, false, &t); - if (r >= 0) { - x = strjoina("OBJECT_CMDLINE=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - -#ifdef HAVE_AUDIT - r = audit_session_from_pid(object_pid, &audit); - if (r >= 0) { - sprintf(o_audit_session, "OBJECT_AUDIT_SESSION=%"PRIu32, audit); - IOVEC_SET_STRING(iovec[n++], o_audit_session); - } - - r = audit_loginuid_from_pid(object_pid, &loginuid); - if (r >= 0) { - sprintf(o_audit_loginuid, "OBJECT_AUDIT_LOGINUID="UID_FMT, loginuid); - IOVEC_SET_STRING(iovec[n++], o_audit_loginuid); - } -#endif - - r = cg_pid_get_path_shifted(object_pid, s->cgroup_root, &c); - if (r >= 0) { - x = strjoina("OBJECT_SYSTEMD_CGROUP=", c); - IOVEC_SET_STRING(iovec[n++], x); - - r = cg_path_get_session(c, &t); - if (r >= 0) { - x = strjoina("OBJECT_SYSTEMD_SESSION=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - if (cg_path_get_owner_uid(c, &owner) >= 0) { - sprintf(o_owner_uid, "OBJECT_SYSTEMD_OWNER_UID="UID_FMT, owner); - IOVEC_SET_STRING(iovec[n++], o_owner_uid); - } - - if (cg_path_get_unit(c, &t) >= 0) { - x = strjoina("OBJECT_SYSTEMD_UNIT=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - if (cg_path_get_user_unit(c, &t) >= 0) { - x = strjoina("OBJECT_SYSTEMD_USER_UNIT=", t); - free(t); - IOVEC_SET_STRING(iovec[n++], x); - } - - free(c); - } - } - assert(n <= m); - - if (tv) { - sprintf(source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv)); - IOVEC_SET_STRING(iovec[n++], source_time); - } - - /* Note that strictly speaking storing the boot id here is - * redundant since the entry includes this in-line - * anyway. However, we need this indexed, too. */ - if (!isempty(s->boot_id_field)) - IOVEC_SET_STRING(iovec[n++], s->boot_id_field); - - if (!isempty(s->machine_id_field)) - IOVEC_SET_STRING(iovec[n++], s->machine_id_field); - - if (!isempty(s->hostname_field)) - IOVEC_SET_STRING(iovec[n++], s->hostname_field); - - assert(n <= m); - - if (s->split_mode == SPLIT_UID && realuid > 0) - /* Split up strictly by any UID */ - journal_uid = realuid; - else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0) - /* Split up by login UIDs. We do this only if the - * realuid is not root, in order not to accidentally - * leak privileged information to the user that is - * logged by a privileged process that is part of an - * unprivileged session. */ - journal_uid = owner; - else - journal_uid = 0; - - write_to_journal(s, journal_uid, iovec, n, priority); -} - -void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) { - char mid[11 + 32 + 1]; - struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS]; - unsigned n = 0, m; - int r; - va_list ap; - struct ucred ucred = {}; - - assert(s); - assert(format); - - assert_cc(3 == LOG_FAC(LOG_DAEMON)); - IOVEC_SET_STRING(iovec[n++], "SYSLOG_FACILITY=3"); - IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=systemd-journald"); - - IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver"); - assert_cc(6 == LOG_INFO); - IOVEC_SET_STRING(iovec[n++], "PRIORITY=6"); - - if (!sd_id128_is_null(message_id)) { - snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id)); - IOVEC_SET_STRING(iovec[n++], mid); - } - - m = n; - - va_start(ap, format); - r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, false, 0, format, ap); - /* Error handling below */ - va_end(ap); - - ucred.pid = getpid(); - ucred.uid = getuid(); - ucred.gid = getgid(); - - if (r >= 0) - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); - - while (m < n) - free(iovec[m++].iov_base); - - if (r < 0) { - /* We failed to format the message. Emit a warning instead. */ - char buf[LINE_MAX]; - - xsprintf(buf, "MESSAGE=Entry printing failed: %s", strerror(-r)); - - n = 3; - IOVEC_SET_STRING(iovec[n++], "PRIORITY=4"); - IOVEC_SET_STRING(iovec[n++], buf); - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); - } -} - -void server_dispatch_message( - Server *s, - struct iovec *iovec, unsigned n, unsigned m, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, size_t label_len, - const char *unit_id, - int priority, - pid_t object_pid) { - - int rl, r; - _cleanup_free_ char *path = NULL; - uint64_t available = 0; - char *c; - - assert(s); - assert(iovec || n == 0); - - if (n == 0) - return; - - if (LOG_PRI(priority) > s->max_level_store) - return; - - /* Stop early in case the information will not be stored - * in a journal. */ - if (s->storage == STORAGE_NONE) - return; - - if (!ucred) - goto finish; - - r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &path); - if (r < 0) - goto finish; - - /* example: /user/lennart/3/foobar - * /system/dbus.service/foobar - * - * So let's cut of everything past the third /, since that is - * where user directories start */ - - c = strchr(path, '/'); - if (c) { - c = strchr(c+1, '/'); - if (c) { - c = strchr(c+1, '/'); - if (c) - *c = 0; - } - } - - (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; - - /* Write a suppression message if we suppressed something */ - if (rl > 1) - server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, - LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path), - NULL); - -finish: - dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); -} - - -static int system_journal_open(Server *s, bool flush_requested) { - const char *fn; - int r = 0; - - if (!s->system_journal && - (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && - (flush_requested - || access("/run/systemd/journal/flushed", F_OK) >= 0)) { - - /* If in auto mode: first try to create the machine - * path, but not the prefix. - * - * If in persistent mode: create /var/log/journal and - * the machine path */ - - if (s->storage == STORAGE_PERSISTENT) - (void) mkdir_p("/var/log/journal/", 0755); - - fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s)); - (void) mkdir(fn, 0755); - - fn = strjoina(fn, "/system.journal"); - r = open_journal(s, true, fn, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &s->system_journal); - 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"); - - r = 0; - } - } - - if (!s->runtime_journal && - (s->storage != STORAGE_NONE)) { - - fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal"); - - if (s->system_journal) { - - /* Try to open the runtime journal, but only - * if it already exists, so that we can flush - * it into the system journal */ - - r = open_journal(s, false, fn, O_RDWR, false, &s->runtime_metrics, &s->runtime_journal); - if (r < 0) { - if (r != -ENOENT) - log_warning_errno(r, "Failed to open runtime journal: %m"); - - r = 0; - } - - } else { - - /* OK, we really need the runtime journal, so create - * it if necessary. */ - - (void) mkdir("/run/log", 0755); - (void) mkdir("/run/log/journal", 0755); - (void) mkdir_parents(fn, 0750); - - r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_metrics, &s->runtime_journal); - if (r < 0) - return log_error_errno(r, "Failed to open runtime journal: %m"); - } - - 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); - } - } - - return r; -} - -int server_flush_to_var(Server *s) { - sd_id128_t machine; - sd_journal *j = NULL; - char ts[FORMAT_TIMESPAN_MAX]; - usec_t start; - unsigned n = 0; - int r; - - assert(s); - - if (s->storage != STORAGE_AUTO && - s->storage != STORAGE_PERSISTENT) - return 0; - - if (!s->runtime_journal) - return 0; - - (void) system_journal_open(s, true); - - if (!s->system_journal) - return 0; - - log_debug("Flushing to /var..."); - - start = now(CLOCK_MONOTONIC); - - r = sd_id128_get_machine(&machine); - if (r < 0) - return r; - - r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY); - if (r < 0) - return log_error_errno(r, "Failed to read runtime journal: %m"); - - sd_journal_set_data_threshold(j, 0); - - SD_JOURNAL_FOREACH(j) { - Object *o = NULL; - JournalFile *f; - - f = j->current_file; - assert(f && f->current_offset > 0); - - n++; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) { - log_error_errno(r, "Can't read entry: %m"); - goto finish; - } - - r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); - if (r >= 0) - continue; - - if (!shall_try_append_again(s->system_journal, r)) { - log_error_errno(r, "Can't write entry: %m"); - goto finish; - } - - server_rotate(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."); - r = -EIO; - goto finish; - } - - log_debug("Retrying write."); - r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Can't write entry: %m"); - goto finish; - } - } - - r = 0; - -finish: - journal_file_post_change(s->system_journal); - - s->runtime_journal = journal_file_close(s->runtime_journal); - - if (r >= 0) - (void) rm_rf("/run/log/journal", REMOVE_ROOT); - - sd_journal_close(j); - - server_driver_message(s, SD_ID128_NULL, - LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.", - format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), - n), - NULL); - - return r; -} - -int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Server *s = userdata; - struct ucred *ucred = NULL; - struct timeval *tv = NULL; - struct cmsghdr *cmsg; - char *label = NULL; - size_t label_len = 0, m; - struct iovec iovec; - ssize_t n; - int *fds = NULL, v = 0; - unsigned n_fds = 0; - - union { - struct cmsghdr cmsghdr; - - /* We use NAME_MAX space for the SELinux label - * here. The kernel currently enforces no - * limit, but according to suggestions from - * the SELinux people this will change and it - * will probably be identical to NAME_MAX. For - * now we use that, but this should be updated - * one day when the final limit is known. */ - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(struct timeval)) + - CMSG_SPACE(sizeof(int)) + /* fd */ - CMSG_SPACE(NAME_MAX)]; /* selinux label */ - } control = {}; - - union sockaddr_union sa = {}; - - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_name = &sa, - .msg_namelen = sizeof(sa), - }; - - assert(s); - assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd); - - if (revents != EPOLLIN) { - log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents); - return -EIO; - } - - /* Try to get the right size, if we can. (Not all - * sockets support SIOCINQ, hence we just try, but - * don't rely on it. */ - (void) ioctl(fd, SIOCINQ, &v); - - /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */ - m = PAGE_ALIGN(MAX3((size_t) v + 1, - (size_t) LINE_MAX, - ALIGN(sizeof(struct nlmsghdr)) + ALIGN((size_t) MAX_AUDIT_MESSAGE_LENGTH)) + 1); - - if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m)) - return log_oom(); - - iovec.iov_base = s->buffer; - iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */ - - n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EINTR || errno == EAGAIN) - return 0; - - return log_error_errno(errno, "recvmsg() failed: %m"); - } - - 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); - else if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_SECURITY) { - label = (char*) CMSG_DATA(cmsg); - label_len = cmsg->cmsg_len - CMSG_LEN(0); - } else if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SO_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - tv = (struct timeval*) CMSG_DATA(cmsg); - else if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - fds = (int*) CMSG_DATA(cmsg); - n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - } - } - - /* And a trailing NUL, just in case */ - s->buffer[n] = 0; - - if (fd == s->syslog_fd) { - if (n > 0 && n_fds == 0) - server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len); - else if (n_fds > 0) - log_warning("Got file descriptors via syslog socket. Ignoring."); - - } else if (fd == s->native_fd) { - if (n > 0 && n_fds == 0) - server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len); - else if (n == 0 && n_fds == 1) - server_process_native_file(s, fds[0], ucred, tv, label, label_len); - else if (n_fds > 0) - log_warning("Got too many file descriptors via native socket. Ignoring."); - - } else { - assert(fd == s->audit_fd); - - if (n > 0 && n_fds == 0) - server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen); - else if (n_fds > 0) - log_warning("Got file descriptors via audit socket. Ignoring."); - } - - close_many(fds, n_fds); - return 0; -} - -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 " PID_FMT, si->ssi_pid); - - server_flush_to_var(s); - server_sync(s); - server_vacuum(s, false, false); - - 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 " PID_FMT, si->ssi_pid); - server_rotate(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; -} - -static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { - Server *s = userdata; - - assert(s); - - log_received_signal(LOG_INFO, si); - - sd_event_exit(s->event, 0); - 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, SIGRTMIN+1, -1) >= 0); - - r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s); - if (r < 0) - return r; - - r = sd_event_add_signal(s->event, &s->sigusr2_event_source, SIGUSR2, dispatch_sigusr2, s); - if (r < 0) - return r; - - r = sd_event_add_signal(s->event, &s->sigterm_event_source, SIGTERM, dispatch_sigterm, 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 *p; - int r; - - r = proc_cmdline(&line); - if (r < 0) { - log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); - return 0; - } - - p = line; - for (;;) { - _cleanup_free_ char *word = NULL; - - 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); - if (r < 0) - log_warning("Failed to parse forward to syslog switch %s. Ignoring.", word + 35); - else - s->forward_to_syslog = r; - } else if (startswith(word, "systemd.journald.forward_to_kmsg=")) { - r = parse_boolean(word + 33); - if (r < 0) - log_warning("Failed to parse forward to kmsg switch %s. Ignoring.", word + 33); - else - s->forward_to_kmsg = r; - } else if (startswith(word, "systemd.journald.forward_to_console=")) { - r = parse_boolean(word + 36); - if (r < 0) - log_warning("Failed to parse forward to console switch %s. Ignoring.", word + 36); - else - s->forward_to_console = r; - } else if (startswith(word, "systemd.journald.forward_to_wall=")) { - r = parse_boolean(word + 33); - if (r < 0) - log_warning("Failed to parse forward to wall switch %s. Ignoring.", word + 33); - else - s->forward_to_wall = r; - } else if (startswith(word, "systemd.journald")) - log_warning("Invalid systemd.journald parameter. Ignoring."); - } - - /* 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(PKGSYSCONFDIR "/journald.conf", - CONF_PATHS_NULSTR("systemd/journald.conf.d"), - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - false, s); -} - -static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { - Server *s = userdata; - - assert(s); - - server_sync(s); - return 0; -} - -int server_schedule_sync(Server *s, int priority) { - int r; - - assert(s); - - if (priority <= LOG_CRIT) { - /* Immediately sync to disk when this is of priority CRIT, ALERT, EMERG */ - server_sync(s); - return 0; - } - - if (s->sync_scheduled) - return 0; - - if (s->sync_interval_usec > 0) { - usec_t when; - - r = sd_event_now(s->event, CLOCK_MONOTONIC, &when); - if (r < 0) - return r; - - when += s->sync_interval_usec; - - if (!s->sync_event_source) { - r = sd_event_add_time( - s->event, - &s->sync_event_source, - CLOCK_MONOTONIC, - when, 0, - server_dispatch_sync, s); - if (r < 0) - return r; - - r = sd_event_source_set_priority(s->sync_event_source, SD_EVENT_PRIORITY_IMPORTANT); - } else { - r = sd_event_source_set_time(s->sync_event_source, when); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_ONESHOT); - } - if (r < 0) - return r; - - s->sync_scheduled = true; - } - - return 0; -} - -static int dispatch_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Server *s = userdata; - - assert(s); - - server_cache_hostname(s); - return 0; -} - -static int server_open_hostname(Server *s) { - int r; - - assert(s); - - s->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); - if (s->hostname_fd < 0) - return log_error_errno(errno, "Failed to open /proc/sys/kernel/hostname: %m"); - - r = sd_event_add_io(s->event, &s->hostname_event_source, s->hostname_fd, 0, dispatch_hostname_change, s); - if (r < 0) { - /* kernels prior to 3.2 don't support polling this file. Ignore - * the failure. */ - if (r == -EPERM) { - log_warning_errno(r, "Failed to register hostname fd in event loop, ignoring: %m"); - s->hostname_fd = safe_close(s->hostname_fd); - return 0; - } - - return log_error_errno(r, "Failed to register hostname fd in event loop: %m"); - } - - r = sd_event_source_set_priority(s->hostname_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); - if (r < 0) - return log_error_errno(r, "Failed to adjust priority of host name event source: %m"); - - 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 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, SOCKADDR_UN_LEN(sa.un)); - 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 = 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; - - s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL; - s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST; - - s->forward_to_wall = true; - - s->max_file_usec = DEFAULT_MAX_FILE_USEC; - - s->max_level_store = LOG_DEBUG; - s->max_level_syslog = LOG_DEBUG; - s->max_level_kmsg = LOG_NOTICE; - s->max_level_console = LOG_INFO; - s->max_level_wall = LOG_EMERG; - - 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; - } - - (void) mkdir_p("/run/systemd/journal", 0755); - - s->user_journals = ordered_hashmap_new(NULL); - if (!s->user_journals) - return log_oom(); - - s->mmap = mmap_cache_new(); - if (!s->mmap) - return log_oom(); - - s->deferred_closes = set_new(NULL); - if (!s->deferred_closes) - return log_oom(); - - r = sd_event_default(&s->event); - if (r < 0) - return log_error_errno(r, "Failed to create event loop: %m"); - - n = sd_listen_fds(true); - if (n < 0) - return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - - if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) { - - if (s->native_fd >= 0) { - log_error("Too many native sockets passed."); - return -EINVAL; - } - - s->native_fd = fd; - - } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) { - - if (s->stdout_fd >= 0) { - log_error("Too many stdout sockets passed."); - return -EINVAL; - } - - s->stdout_fd = fd; - - } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 || - sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) { - - if (s->syslog_fd >= 0) { - log_error("Too many /dev/log sockets passed."); - return -EINVAL; - } - - s->syslog_fd = fd; - - } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { - - if (s->audit_fd >= 0) { - log_error("Too many audit sockets passed."); - return -EINVAL; - } - - s->audit_fd = fd; - - } else { - - if (!fds) { - fds = fdset_new(); - if (!fds) - return log_oom(); - } - - r = fdset_put(fds, fd); - if (r < 0) - return log_oom(); - } - } - - /* 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; - - /* 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) - return r; - - r = server_open_hostname(s); - if (r < 0) - return r; - - r = setup_signals(s); - if (r < 0) - return r; - - s->udev = udev_new(); - if (!s->udev) - return -ENOMEM; - - s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); - if (!s->rate_limit) - return -ENOMEM; - - r = cg_get_root_path(&s->cgroup_root); - if (r < 0) - return r; - - server_cache_hostname(s); - server_cache_boot_id(s); - server_cache_machine_id(s); - - (void) server_connect_notify(s); - - return system_journal_open(s, false); -} - -void server_maybe_append_tags(Server *s) { -#ifdef HAVE_GCRYPT - JournalFile *f; - Iterator i; - usec_t n; - - n = now(CLOCK_REALTIME); - - if (s->system_journal) - journal_file_maybe_append_tag(s->system_journal, n); - - ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) - journal_file_maybe_append_tag(f, n); -#endif -} - -void server_done(Server *s) { - JournalFile *f; - assert(s); - - if (s->deferred_closes) { - journal_file_close_set(s->deferred_closes); - set_free(s->deferred_closes); - } - - while (s->stdout_streams) - stdout_stream_free(s->stdout_streams); - - if (s->system_journal) - (void) journal_file_close(s->system_journal); - - if (s->runtime_journal) - (void) journal_file_close(s->runtime_journal); - - while ((f = ordered_hashmap_steal_first(s->user_journals))) - (void) journal_file_close(f); - - ordered_hashmap_free(s->user_journals); - - sd_event_source_unref(s->syslog_event_source); - sd_event_source_unref(s->native_event_source); - sd_event_source_unref(s->stdout_event_source); - sd_event_source_unref(s->dev_kmsg_event_source); - sd_event_source_unref(s->audit_event_source); - sd_event_source_unref(s->sync_event_source); - sd_event_source_unref(s->sigusr1_event_source); - 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); - safe_close(s->native_fd); - safe_close(s->stdout_fd); - 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); - - if (s->kernel_seqnum) - munmap(s->kernel_seqnum, sizeof(uint64_t)); - - free(s->buffer); - free(s->tty_path); - free(s->cgroup_root); - free(s->hostname_field); - - if (s->mmap) - mmap_cache_unref(s->mmap); - - 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 deleted file mode 100644 index e025a4cf90..0000000000 --- a/src/journal/journald-server.h +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-event.h" - -typedef struct Server Server; - -#include "hashmap.h" -#include "journal-file.h" -#include "journald-rate-limit.h" -#include "journald-stream.h" -#include "list.h" - -typedef enum Storage { - STORAGE_AUTO, - STORAGE_VOLATILE, - STORAGE_PERSISTENT, - STORAGE_NONE, - _STORAGE_MAX, - _STORAGE_INVALID = -1 -} Storage; - -typedef enum SplitMode { - SPLIT_UID, - SPLIT_LOGIN, - SPLIT_NONE, - _SPLIT_MAX, - _SPLIT_INVALID = -1 -} SplitMode; - -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; - - sd_event_source *syslog_event_source; - sd_event_source *native_event_source; - sd_event_source *stdout_event_source; - sd_event_source *dev_kmsg_event_source; - sd_event_source *audit_event_source; - sd_event_source *sync_event_source; - sd_event_source *sigusr1_event_source; - 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; - OrderedHashmap *user_journals; - - uint64_t seqnum; - - char *buffer; - size_t buffer_size; - - JournalRateLimit *rate_limit; - usec_t sync_interval_usec; - usec_t rate_limit_interval; - unsigned rate_limit_burst; - - JournalMetrics runtime_metrics; - JournalMetrics system_metrics; - - bool compress; - bool seal; - - bool forward_to_kmsg; - bool forward_to_syslog; - bool forward_to_console; - bool forward_to_wall; - - unsigned n_forward_syslog_missed; - usec_t last_warn_forward_syslog_missed; - - uint64_t cached_space_available; - uint64_t cached_space_limit; - usec_t cached_space_timestamp; - - uint64_t var_available_timestamp; - - usec_t max_retention_usec; - usec_t max_file_usec; - usec_t oldest_file_usec; - - LIST_HEAD(StdoutStream, stdout_streams); - LIST_HEAD(StdoutStream, stdout_streams_notify_queue); - unsigned n_stdout_streams; - - char *tty_path; - - int max_level_store; - int max_level_syslog; - int max_level_kmsg; - int max_level_console; - int max_level_wall; - - Storage storage; - SplitMode split_mode; - - MMapCache *mmap; - - Set *deferred_closes; - - struct udev *udev; - - uint64_t *kernel_seqnum; - bool dev_kmsg_readable:1; - - 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]; - char *hostname_field; - - /* Cached cgroup root, so that we don't have to query that all the time */ - char *cgroup_root; - - 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 -#define N_IOVEC_UDEV_FIELDS 32 -#define N_IOVEC_OBJECT_FIELDS 12 -#define N_IOVEC_PAYLOAD_FIELDS 15 - -void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid); -void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_; - -/* gperf lookup function */ -const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length); - -int config_parse_storage(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 *storage_to_string(Storage s) _const_; -Storage storage_from_string(const char *s) _pure_; - -int config_parse_split_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 *split_mode_to_string(SplitMode s) _const_; -SplitMode split_mode_from_string(const char *s) _pure_; - -int server_init(Server *s); -void server_done(Server *s); -void server_sync(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); -void server_maybe_append_tags(Server *s); -int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c deleted file mode 100644 index 4ad16ee41c..0000000000 --- a/src/journal/journald-stream.c +++ /dev/null @@ -1,785 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#ifdef HAVE_SELINUX -#include -#endif - -#include "sd-daemon.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-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 - -typedef enum StdoutStreamState { - STDOUT_STREAM_IDENTIFIER, - STDOUT_STREAM_UNIT_ID, - STDOUT_STREAM_PRIORITY, - STDOUT_STREAM_LEVEL_PREFIX, - STDOUT_STREAM_FORWARD_TO_SYSLOG, - STDOUT_STREAM_FORWARD_TO_KMSG, - STDOUT_STREAM_FORWARD_TO_CONSOLE, - STDOUT_STREAM_RUNNING -} StdoutStreamState; - -struct StdoutStream { - Server *server; - StdoutStreamState state; - - int fd; - - struct ucred ucred; - char *label; - char *identifier; - char *unit_id; - int priority; - bool level_prefix:1; - bool forward_to_syslog:1; - bool forward_to_kmsg:1; - bool forward_to_console:1; - - bool fdstore:1; - bool in_notify_queue:1; - - char buffer[LINE_MAX+1]; - size_t length; - - sd_event_source *event_source; - - char *state_file; - - LIST_FIELDS(StdoutStream, stdout_stream); - LIST_FIELDS(StdoutStream, stdout_stream_notify_queue); -}; - -void stdout_stream_free(StdoutStream *s) { - if (!s) - return; - - if (s->server) { - 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) { - sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF); - s->event_source = sd_event_source_unref(s->event_source); - } - - safe_close(s->fd); - free(s->label); - free(s->identifier); - free(s->unit_id); - free(s->state_file); - - free(s); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free); - -static void stdout_stream_destroy(StdoutStream *s) { - if (!s) - return; - - if (s->state_file) - (void) unlink(s->state_file); - - stdout_stream_free(s); -} - -static int stdout_stream_save(StdoutStream *s) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(s); - - if (s->state != STDOUT_STREAM_RUNNING) - return 0; - - if (!s->state_file) { - struct stat st; - - r = fstat(s->fd, &st); - if (r < 0) - return log_warning_errno(errno, "Failed to stat connected stream: %m"); - - /* We use device and inode numbers as identifier for the stream */ - if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) - return log_oom(); - } - - mkdir_p("/run/systemd/journal/streams", 0755); - - r = fopen_temporary(s->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fprintf(f, - "# This is private data. Do not parse\n" - "PRIORITY=%i\n" - "LEVEL_PREFIX=%i\n" - "FORWARD_TO_SYSLOG=%i\n" - "FORWARD_TO_KMSG=%i\n" - "FORWARD_TO_CONSOLE=%i\n", - s->priority, - s->level_prefix, - s->forward_to_syslog, - s->forward_to_kmsg, - s->forward_to_console); - - if (!isempty(s->identifier)) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->identifier); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "IDENTIFIER=%s\n", escaped); - } - - if (!isempty(s->unit_id)) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->unit_id); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "UNIT=%s\n", escaped); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, s->state_file) < 0) { - r = -errno; - goto fail; - } - - 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; - -fail: - (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); -} - -static int stdout_stream_log(StdoutStream *s, const char *p) { - struct iovec iovec[N_IOVEC_META_FIELDS + 5]; - int priority; - char syslog_priority[] = "PRIORITY=\0"; - char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1]; - _cleanup_free_ char *message = NULL, *syslog_identifier = NULL; - unsigned n = 0; - size_t label_len; - - assert(s); - assert(p); - - 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); - - if (s->forward_to_kmsg || s->server->forward_to_kmsg) - server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred); - - if (s->forward_to_console || s->server->forward_to_console) - server_forward_console(s->server, priority, s->identifier, p, &s->ucred); - - if (s->server->forward_to_wall) - server_forward_wall(s->server, priority, s->identifier, p, &s->ucred); - - IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout"); - - syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority); - IOVEC_SET_STRING(iovec[n++], syslog_priority); - - if (priority & LOG_FACMASK) { - xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)); - IOVEC_SET_STRING(iovec[n++], syslog_facility); - } - - if (s->identifier) { - syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier); - if (syslog_identifier) - IOVEC_SET_STRING(iovec[n++], syslog_identifier); - } - - message = strappend("MESSAGE=", p); - if (message) - IOVEC_SET_STRING(iovec[n++], message); - - label_len = s->label ? strlen(s->label) : 0; - server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, s->label, label_len, s->unit_id, priority, 0); - return 0; -} - -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) { - - case STDOUT_STREAM_IDENTIFIER: - if (isempty(p)) - s->identifier = NULL; - else { - s->identifier = strdup(p); - if (!s->identifier) - return log_oom(); - } - - s->state = STDOUT_STREAM_UNIT_ID; - return 0; - - case STDOUT_STREAM_UNIT_ID: - if (s->ucred.uid == 0) { - if (isempty(p)) - s->unit_id = NULL; - else { - s->unit_id = strdup(p); - if (!s->unit_id) - return log_oom(); - } - } - - s->state = STDOUT_STREAM_PRIORITY; - return 0; - - case STDOUT_STREAM_PRIORITY: - r = safe_atoi(p, &s->priority); - if (r < 0 || s->priority < 0 || s->priority > 999) { - log_warning("Failed to parse log priority line."); - return -EINVAL; - } - - s->state = STDOUT_STREAM_LEVEL_PREFIX; - return 0; - - case STDOUT_STREAM_LEVEL_PREFIX: - r = parse_boolean(p); - if (r < 0) { - log_warning("Failed to parse level prefix line."); - return -EINVAL; - } - - s->level_prefix = !!r; - s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG; - return 0; - - case STDOUT_STREAM_FORWARD_TO_SYSLOG: - r = parse_boolean(p); - if (r < 0) { - log_warning("Failed to parse forward to syslog line."); - return -EINVAL; - } - - s->forward_to_syslog = !!r; - s->state = STDOUT_STREAM_FORWARD_TO_KMSG; - return 0; - - case STDOUT_STREAM_FORWARD_TO_KMSG: - r = parse_boolean(p); - if (r < 0) { - log_warning("Failed to parse copy to kmsg line."); - return -EINVAL; - } - - s->forward_to_kmsg = !!r; - s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE; - return 0; - - case STDOUT_STREAM_FORWARD_TO_CONSOLE: - r = parse_boolean(p); - if (r < 0) { - log_warning("Failed to parse copy to console line."); - return -EINVAL; - } - - s->forward_to_console = !!r; - s->state = STDOUT_STREAM_RUNNING; - - /* Try to save the stream, so that journald can be restarted and we can recover */ - (void) stdout_stream_save(s); - return 0; - - case STDOUT_STREAM_RUNNING: - return stdout_stream_log(s, orig); - } - - assert_not_reached("Unknown stream state"); -} - -static int stdout_stream_scan(StdoutStream *s, bool force_flush) { - char *p; - size_t remaining; - int r; - - assert(s); - - p = s->buffer; - remaining = s->length; - for (;;) { - char *end; - size_t skip; - - end = memchr(p, '\n', remaining); - if (end) - skip = end - p + 1; - else if (remaining >= sizeof(s->buffer) - 1) { - end = p + sizeof(s->buffer) - 1; - skip = remaining; - } else - break; - - *end = 0; - - r = stdout_stream_line(s, p); - if (r < 0) - return r; - - remaining -= skip; - p += skip; - } - - if (force_flush && remaining > 0) { - p[remaining] = 0; - r = stdout_stream_line(s, p); - if (r < 0) - return r; - - p += remaining; - remaining = 0; - } - - if (p > s->buffer) { - memmove(s->buffer, p, remaining); - s->length = remaining; - } - - return 0; -} - -static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - StdoutStream *s = userdata; - ssize_t l; - int r; - - assert(s); - - if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) { - log_error("Got invalid event from epoll for stdout stream: %"PRIx32, revents); - goto terminate; - } - - l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length); - if (l < 0) { - - if (errno == EAGAIN) - return 0; - - log_warning_errno(errno, "Failed to read from stream: %m"); - goto terminate; - } - - if (l == 0) { - stdout_stream_scan(s, true); - goto terminate; - } - - s->length += l; - r = stdout_stream_scan(s, false); - if (r < 0) - goto terminate; - - return 1; - -terminate: - stdout_stream_destroy(s); - return 0; -} - -static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { - _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL; - int r; - - assert(s); - assert(fd >= 0); - - stream = new0(StdoutStream, 1); - if (!stream) - return log_oom(); - - stream->fd = -1; - stream->priority = LOG_INFO; - - r = getpeercred(fd, &stream->ucred); - if (r < 0) - return log_error_errno(r, "Failed to determine peer credentials: %m"); - - 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"); - } - - (void) shutdown(fd, SHUT_WR); - - r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream); - if (r < 0) - return log_error_errno(r, "Failed to add stream to event loop: %m"); - - r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5); - if (r < 0) - return log_error_errno(r, "Failed to adjust stdout event source priority: %m"); - - stream->fd = fd; - - stream->server = s; - LIST_PREPEND(stdout_stream, s->stdout_streams, stream); - s->n_stdout_streams++; - - if (ret) - *ret = stream; - - stream = NULL; - - return 0; -} - -static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) { - _cleanup_close_ int fd = -1; - Server *s = userdata; - int r; - - assert(s); - - if (revents != EPOLLIN) { - log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents); - return -EIO; - } - - fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (fd < 0) { - if (errno == EAGAIN) - return 0; - - return log_error_errno(errno, "Failed to accept stdout connection: %m"); - } - - if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { - log_warning("Too many stdout streams, refusing connection."); - return 0; - } - - r = stdout_stream_install(s, fd, NULL); - if (r < 0) - return r; - - fd = -1; - return 0; -} - -static int stdout_stream_load(StdoutStream *stream, const char *fname) { - _cleanup_free_ char - *priority = NULL, - *level_prefix = NULL, - *forward_to_syslog = NULL, - *forward_to_kmsg = NULL, - *forward_to_console = NULL; - int r; - - assert(stream); - assert(fname); - - if (!stream->state_file) { - stream->state_file = strappend("/run/systemd/journal/streams/", fname); - if (!stream->state_file) - return log_oom(); - } - - r = parse_env_file(stream->state_file, NEWLINE, - "PRIORITY", &priority, - "LEVEL_PREFIX", &level_prefix, - "FORWARD_TO_SYSLOG", &forward_to_syslog, - "FORWARD_TO_KMSG", &forward_to_kmsg, - "FORWARD_TO_CONSOLE", &forward_to_console, - "IDENTIFIER", &stream->identifier, - "UNIT", &stream->unit_id, - NULL); - if (r < 0) - return log_error_errno(r, "Failed to read: %s", stream->state_file); - - if (priority) { - int p; - - p = log_level_from_string(priority); - if (p >= 0) - stream->priority = p; - } - - if (level_prefix) { - r = parse_boolean(level_prefix); - if (r >= 0) - stream->level_prefix = r; - } - - if (forward_to_syslog) { - r = parse_boolean(forward_to_syslog); - if (r >= 0) - stream->forward_to_syslog = r; - } - - if (forward_to_kmsg) { - r = parse_boolean(forward_to_kmsg); - if (r >= 0) - stream->forward_to_kmsg = r; - } - - if (forward_to_console) { - r = parse_boolean(forward_to_console); - if (r >= 0) - stream->forward_to_console = r; - } - - return 0; -} - -static int stdout_stream_restore(Server *s, const char *fname, int fd) { - StdoutStream *stream; - int r; - - assert(s); - assert(fname); - assert(fd >= 0); - - if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { - log_warning("Too many stdout streams, refusing restoring of stream."); - return -ENOBUFS; - } - - r = stdout_stream_install(s, fd, &stream); - if (r < 0) - return r; - - stream->state = STDOUT_STREAM_RUNNING; - stream->fdstore = true; - - /* Ignore all parsing errors */ - (void) stdout_stream_load(stream, fname); - - return 0; -} - -int server_restore_streams(Server *s, FDSet *fds) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r; - - d = opendir("/run/systemd/journal/streams"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m"); - } - - FOREACH_DIRENT(de, d, goto fail) { - unsigned long st_dev, st_ino; - bool found = false; - Iterator i; - int fd; - - if (sscanf(de->d_name, "%lu:%lu", &st_dev, &st_ino) != 2) - continue; - - FDSET_FOREACH(fd, fds, i) { - struct stat st; - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Failed to stat %s: %m", de->d_name); - - if (S_ISSOCK(st.st_mode) && st.st_dev == st_dev && st.st_ino == st_ino) { - found = true; - break; - } - } - - if (!found) { - /* No file descriptor? Then let's delete the state file */ - log_debug("Cannot restore stream file %s", de->d_name); - unlinkat(dirfd(d), de->d_name, 0); - continue; - } - - fdset_remove(fds, fd); - - r = stdout_stream_restore(s, de->d_name, fd); - if (r < 0) - safe_close(fd); - } - - return 0; - -fail: - return log_error_errno(errno, "Failed to read streams directory: %m"); -} - -int server_open_stdout_socket(Server *s) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; - int r; - - assert(s); - - if (s->stdout_fd < 0) { - s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->stdout_fd < 0) - return log_error_errno(errno, "socket() failed: %m"); - - (void) unlink(sa.un.sun_path); - - r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - - (void) chmod(sa.un.sun_path, 0666); - - if (listen(s->stdout_fd, SOMAXCONN) < 0) - return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path); - } else - fd_nonblock(s->stdout_fd, 1); - - r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s); - 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+5); - if (r < 0) - return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m"); - - 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 deleted file mode 100644 index db4c67fae3..0000000000 --- a/src/journal/journald-stream.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct StdoutStream StdoutStream; - -#include "fdset.h" -#include "journald-server.h" - -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 deleted file mode 100644 index 0609b4b694..0000000000 --- a/src/journal/journald-syslog.c +++ /dev/null @@ -1,454 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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-wall.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) - -static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/syslog", - }; - struct msghdr msghdr = { - .msg_iov = (struct iovec *) iovec, - .msg_iovlen = n_iovec, - .msg_name = (struct sockaddr*) &sa.sa, - .msg_namelen = SOCKADDR_UN_LEN(sa.un), - }; - struct cmsghdr *cmsg; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - - assert(s); - assert(iovec); - assert(n_iovec > 0); - - if (ucred) { - zero(control); - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred)); - msghdr.msg_controllen = cmsg->cmsg_len; - } - - /* Forward the syslog message we received via /dev/log to - * /run/systemd/syslog. Unfortunately we currently can't set - * the SO_TIMESTAMP auxiliary data, and hence we don't. */ - - if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) - return; - - /* The socket is full? I guess the syslog implementation is - * too slow, and we shouldn't wait for that... */ - if (errno == EAGAIN) { - s->n_forward_syslog_missed++; - return; - } - - if (ucred && (errno == ESRCH || errno == EPERM)) { - struct ucred u; - - /* Hmm, presumably the sender process vanished - * by now, or we don't have CAP_SYS_AMDIN, so - * let's fix it as good as we can, and retry */ - - u = *ucred; - u.pid = getpid(); - memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred)); - - if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) - return; - - if (errno == EAGAIN) { - s->n_forward_syslog_missed++; - return; - } - } - - if (errno != ENOENT) - log_debug_errno(errno, "Failed to forward syslog message: %m"); -} - -static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) { - struct iovec iovec; - - assert(s); - assert(buffer); - - if (LOG_PRI(priority) > s->max_level_syslog) - return; - - IOVEC_SET_STRING(iovec, buffer); - forward_syslog_iovec(s, &iovec, 1, ucred, tv); -} - -void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) { - struct iovec iovec[5]; - char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64], - header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1]; - int n = 0; - time_t t; - struct tm *tm; - char *ident_buf = NULL; - - assert(s); - assert(priority >= 0); - assert(priority <= 999); - assert(message); - - if (LOG_PRI(priority) > s->max_level_syslog) - return; - - /* First: priority field */ - xsprintf(header_priority, "<%i>", priority); - IOVEC_SET_STRING(iovec[n++], header_priority); - - /* Second: timestamp */ - t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); - tm = localtime(&t); - if (!tm) - return; - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return; - IOVEC_SET_STRING(iovec[n++], header_time); - - /* Third: identifier and PID */ - if (ucred) { - if (!identifier) { - get_process_comm(ucred->pid, &ident_buf); - identifier = ident_buf; - } - - xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); - - if (identifier) - IOVEC_SET_STRING(iovec[n++], identifier); - - IOVEC_SET_STRING(iovec[n++], header_pid); - } else if (identifier) { - IOVEC_SET_STRING(iovec[n++], identifier); - IOVEC_SET_STRING(iovec[n++], ": "); - } - - /* Fourth: message */ - IOVEC_SET_STRING(iovec[n++], message); - - forward_syslog_iovec(s, iovec, n, ucred, tv); - - free(ident_buf); -} - -int syslog_fixup_facility(int priority) { - - if ((priority & LOG_FACMASK) == 0) - return (priority & LOG_PRIMASK) | LOG_USER; - - return priority; -} - -size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) { - const char *p; - char *t; - size_t l, e; - - assert(buf); - assert(identifier); - assert(pid); - - p = *buf; - - p += strspn(p, WHITESPACE); - l = strcspn(p, WHITESPACE); - - if (l <= 0 || - p[l-1] != ':') - return 0; - - e = l; - l--; - - if (p[l-1] == ']') { - size_t k = l-1; - - for (;;) { - - if (p[k] == '[') { - t = strndup(p+k+1, l-k-2); - if (t) - *pid = t; - - l = k; - break; - } - - if (k == 0) - break; - - k--; - } - } - - t = strndup(p, l); - if (t) - *identifier = t; - - if (strchr(WHITESPACE, p[e])) - e++; - *buf = p + e; - return e; -} - -static void syslog_skip_date(char **buf) { - enum { - LETTER, - SPACE, - NUMBER, - SPACE_OR_NUMBER, - COLON - } sequence[] = { - LETTER, LETTER, LETTER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - SPACE - }; - - char *p; - unsigned i; - - assert(buf); - assert(*buf); - - p = *buf; - - for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { - - if (!*p) - return; - - switch (sequence[i]) { - - case SPACE: - if (*p != ' ') - return; - break; - - case SPACE_OR_NUMBER: - if (*p == ' ') - break; - - /* fall through */ - - case NUMBER: - if (*p < '0' || *p > '9') - return; - - break; - - case LETTER: - if (!(*p >= 'A' && *p <= 'Z') && - !(*p >= 'a' && *p <= 'z')) - return; - - break; - - case COLON: - if (*p != ':') - return; - break; - - } - } - - *buf = p; -} - -void server_process_syslog_message( - Server *s, - const char *buf, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, - size_t label_len) { - - char syslog_priority[sizeof("PRIORITY=") + 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; - int priority = LOG_USER | LOG_INFO; - _cleanup_free_ char *identifier = NULL, *pid = NULL; - const char *orig; - - assert(s); - assert(buf); - - orig = buf; - syslog_parse_priority(&buf, &priority, true); - - if (s->forward_to_syslog) - forward_syslog_raw(s, priority, orig, ucred, tv); - - syslog_skip_date((char**) &buf); - syslog_parse_identifier(&buf, &identifier, &pid); - - if (s->forward_to_kmsg) - server_forward_kmsg(s, priority, identifier, buf, ucred); - - if (s->forward_to_console) - server_forward_console(s, priority, identifier, buf, ucred); - - if (s->forward_to_wall) - server_forward_wall(s, priority, identifier, buf, ucred); - - IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog"); - - xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK); - IOVEC_SET_STRING(iovec[n++], syslog_priority); - - if (priority & LOG_FACMASK) { - xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)); - IOVEC_SET_STRING(iovec[n++], syslog_facility); - } - - if (identifier) { - syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier); - IOVEC_SET_STRING(iovec[n++], syslog_identifier); - } - - if (pid) { - syslog_pid = strjoina("SYSLOG_PID=", pid); - IOVEC_SET_STRING(iovec[n++], syslog_pid); - } - - message = strjoina("MESSAGE=", buf); - if (message) - IOVEC_SET_STRING(iovec[n++], message); - - server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0); -} - -int server_open_syslog_socket(Server *s) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/dev-log", - }; - static const int one = 1; - int r; - - assert(s); - - if (s->syslog_fd < 0) { - s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->syslog_fd < 0) - return log_error_errno(errno, "socket() failed: %m"); - - (void) unlink(sa.un.sun_path); - - r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - - (void) chmod(sa.un.sun_path, 0666); - } else - fd_nonblock(s->syslog_fd, 1); - - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "SO_PASSCRED failed: %m"); - -#ifdef HAVE_SELINUX - 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"); - } -#endif - - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); - - r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, 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; -} - -void server_maybe_warn_forward_syslog_missed(Server *s) { - usec_t n; - - assert(s); - - if (s->n_forward_syslog_missed <= 0) - return; - - n = now(CLOCK_MONOTONIC); - if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n) - return; - - server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, - LOG_MESSAGE("Forwarding to syslog missed %u messages.", - s->n_forward_syslog_missed), - NULL); - - s->n_forward_syslog_missed = 0; - s->last_warn_forward_syslog_missed = n; -} diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h deleted file mode 100644 index 46ad715314..0000000000 --- a/src/journal/journald-syslog.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journald-server.h" - -int syslog_fixup_facility(int priority) _const_; - -size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); - -void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); - -void server_process_syslog_message(Server *s, const char *buf, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); -int server_open_syslog_socket(Server *s); - -void server_maybe_warn_forward_syslog_missed(Server *s); diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c deleted file mode 100644 index 4d91fafffe..0000000000 --- a/src/journal/journald-wall.c +++ /dev/null @@ -1,71 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Sebastian Thorarensen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "formats-util.h" -#include "journald-server.h" -#include "journald-wall.h" -#include "process-util.h" -#include "string-util.h" -#include "utmp-wtmp.h" - -void server_forward_wall( - Server *s, - int priority, - const char *identifier, - const char *message, - const struct ucred *ucred) { - - _cleanup_free_ char *ident_buf = NULL, *l_buf = NULL; - const char *l; - int r; - - assert(s); - assert(message); - - if (LOG_PRI(priority) > s->max_level_wall) - return; - - if (ucred) { - if (!identifier) { - get_process_comm(ucred->pid, &ident_buf); - identifier = ident_buf; - } - - if (asprintf(&l_buf, "%s["PID_FMT"]: %s", strempty(identifier), ucred->pid, message) < 0) { - log_oom(); - return; - } - - l = l_buf; - - } else if (identifier) { - - l = l_buf = strjoin(identifier, ": ", message, NULL); - if (!l_buf) { - log_oom(); - return; - } - } else - l = message; - - r = utmp_wall(l, "systemd-journald", NULL, NULL, NULL); - if (r < 0) - log_debug_errno(r, "Failed to send wall message: %m"); -} diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h deleted file mode 100644 index ebc2b89fa8..0000000000 --- a/src/journal/journald-wall.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Sebastian Thorarensen - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "journald-server.h" - -void server_forward_wall(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/journal/journald.c b/src/journal/journald.c deleted file mode 100644 index 272acb71c4..0000000000 --- a/src/journal/journald.c +++ /dev/null @@ -1,120 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-daemon.h" -#include "sd-messages.h" - -#include "formats-util.h" -#include "journal-authenticate.h" -#include "journald-kmsg.h" -#include "journald-server.h" -#include "journald-syslog.h" -#include "sigbus.h" - -int main(int argc, char *argv[]) { - Server server; - int r; - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_SAFE); - log_set_facility(LOG_SYSLOG); - log_parse_environment(); - log_open(); - - umask(0022); - - sigbus_install(); - - r = server_init(&server); - if (r < 0) - goto finish; - - 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, - LOG_MESSAGE("Journal started"), - NULL); - - for (;;) { - usec_t t = USEC_INFINITY, n; - - r = sd_event_get_state(server.event); - if (r < 0) - goto finish; - if (r == SD_EVENT_FINISHED) - break; - - n = now(CLOCK_REALTIME); - - if (server.max_retention_usec > 0 && server.oldest_file_usec > 0) { - - /* The retention time is reached, so let's vacuum! */ - if (server.oldest_file_usec + server.max_retention_usec < n) { - log_info("Retention time reached."); - server_rotate(&server); - server_vacuum(&server, false, false); - continue; - } - - /* Calculate when to rotate the next time */ - t = server.oldest_file_usec + server.max_retention_usec - n; - } - -#ifdef HAVE_GCRYPT - if (server.system_journal) { - usec_t u; - - if (journal_file_next_evolve_usec(server.system_journal, &u)) { - if (n >= u) - t = 0; - else - t = MIN(t, u - n); - } - } -#endif - - r = sd_event_run(server.event, t); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - - server_maybe_append_tags(&server); - server_maybe_warn_forward_syslog_missed(&server); - } - - log_debug("systemd-journald stopped as pid "PID_FMT, getpid()); - server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, - LOG_MESSAGE("Journal stopped"), - NULL); - -finish: - server_done(&server); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/journal/journald.conf b/src/journal/journald.conf deleted file mode 100644 index 2541b949be..0000000000 --- a/src/journal/journald.conf +++ /dev/null @@ -1,41 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See journald.conf(5) for details. - -[Journal] -#Storage=auto -#Compress=yes -#Seal=yes -#SplitMode=uid -#SyncIntervalSec=5m -#RateLimitIntervalSec=30s -#RateLimitBurst=1000 -#SystemMaxUse= -#SystemKeepFree= -#SystemMaxFileSize= -#SystemMaxFiles=100 -#RuntimeMaxUse= -#RuntimeKeepFree= -#RuntimeMaxFileSize= -#RuntimeMaxFiles=100 -#MaxRetentionSec= -#MaxFileSec=1month -#ForwardToSyslog=no -#ForwardToKMsg=no -#ForwardToConsole=no -#ForwardToWall=yes -#TTYPath=/dev/console -#MaxLevelStore=debug -#MaxLevelSyslog=debug -#MaxLevelKMsg=notice -#MaxLevelConsole=info -#MaxLevelWall=emerg diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c deleted file mode 100644 index 3d791234f4..0000000000 --- a/src/journal/lookup3.c +++ /dev/null @@ -1,1009 +0,0 @@ -/* Slightly modified by Lennart Poettering, to avoid name clashes, and - * unexport a few functions. */ - -#include "lookup3.h" - -/* -------------------------------------------------------------------------------- -lookup3.c, by Bob Jenkins, May 2006, Public Domain. - -These are functions for producing 32-bit hashes for hash table lookup. -hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() -are externally useful functions. Routines to test the hash are included -if SELF_TEST is defined. You can use this free for any purpose. It's in -the public domain. It has no warranty. - -You probably want to use hashlittle(). hashlittle() and hashbig() -hash byte arrays. hashlittle() is faster than hashbig() on -little-endian machines. Intel and AMD are little-endian machines. -On second thought, you probably want hashlittle2(), which is identical to -hashlittle() except it returns two 32-bit hashes for the price of one. -You could implement hashbig2() if you wanted but I haven't bothered here. - -If you want to find a hash of, say, exactly 7 integers, do - a = i1; b = i2; c = i3; - mix(a,b,c); - a += i4; b += i5; c += i6; - mix(a,b,c); - a += i7; - final(a,b,c); -then use c as the hash value. If you have a variable length array of -4-byte integers to hash, use hashword(). If you have a byte array (like -a character string), use hashlittle(). If you have several byte arrays, or -a mix of things, see the comments above hashlittle(). - -Why is this so big? I read 12 bytes at a time into 3 4-byte integers, -then mix those integers. This is fast (you can do a lot more thorough -mixing with 12*3 instructions on 3 integers than you can with 3 instructions -on 1 byte), but shoehorning those bytes into integers efficiently is messy. -------------------------------------------------------------------------------- -*/ -/* #define SELF_TEST 1 */ - -#include /* defines uint32_t etc */ -#include /* defines printf for tests */ -#include /* attempt to define endianness */ -#include /* defines time_t for timings in the test */ -#ifdef linux -# include /* attempt to define endianness */ -#endif - -/* - * My best guess at if you are big-endian or little-endian. This may - * need adjustment. - */ -#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ - __BYTE_ORDER == __LITTLE_ENDIAN) || \ - (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) -# define HASH_LITTLE_ENDIAN 1 -# define HASH_BIG_ENDIAN 0 -#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ - __BYTE_ORDER == __BIG_ENDIAN) || \ - (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 1 -#else -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 0 -#endif - -#define hashsize(n) ((uint32_t)1<<(n)) -#define hashmask(n) (hashsize(n)-1) -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -/* -------------------------------------------------------------------------------- -mix -- mix 3 32-bit values reversibly. - -This is reversible, so any information in (a,b,c) before mix() is -still in (a,b,c) after mix(). - -If four pairs of (a,b,c) inputs are run through mix(), or through -mix() in reverse, there are at least 32 bits of the output that -are sometimes the same for one pair and different for another pair. -This was tested for: -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - is commonly produced by subtraction) look like a single 1-bit - difference. -* the base values were pseudorandom, all zero but one bit set, or - all zero plus a counter that starts at zero. - -Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that -satisfy this are - 4 6 8 16 19 4 - 9 15 3 18 27 15 - 14 9 3 7 17 3 -Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing -for "differ" defined as + with a one-bit base and a two-bit delta. I -used http://burtleburtle.net/bob/hash/avalanche.html to choose -the operations, constants, and arrangements of the variables. - -This does not achieve avalanche. There are input bits of (a,b,c) -that fail to affect some output bits of (a,b,c), especially of a. The -most thoroughly mixed value is c, but it doesn't really even achieve -avalanche in c. - -This allows some parallelism. Read-after-writes are good at doubling -the number of bits affected, so the goal of mixing pulls in the opposite -direction as the goal of parallelism. I did what I could. Rotates -seem to cost as much as shifts on every machine I could lay my hands -on, and rotates are much kinder to the top and bottom bits, so I used -rotates. -------------------------------------------------------------------------------- -*/ -#define mix(a,b,c) \ -{ \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c,16); c += b; \ - b -= a; b ^= rot(a,19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} - -/* -------------------------------------------------------------------------------- -final -- final mixing of 3 32-bit values (a,b,c) into c - -Pairs of (a,b,c) values differing in only a few bits will usually -produce values of c that look totally different. This was tested for -* pairs that differed by one bit, by two bits, in any combination - of top bits of (a,b,c), or in any combination of bottom bits of - (a,b,c). -* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - is commonly produced by subtraction) look like a single 1-bit - difference. -* the base values were pseudorandom, all zero but one bit set, or - all zero plus a counter that starts at zero. - -These constants passed: - 14 11 25 16 4 14 24 - 12 14 25 16 4 14 24 -and these came close: - 4 8 15 26 3 22 24 - 10 8 15 26 3 22 24 - 11 8 15 26 3 22 24 -------------------------------------------------------------------------------- -*/ -#define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - -/* --------------------------------------------------------------------- - This works on all machines. To be useful, it requires - -- that the key be an array of uint32_t's, and - -- that the length be the number of uint32_t's in the key - - The function hashword() is identical to hashlittle() on little-endian - machines, and identical to hashbig() on big-endian machines, - except that the length has to be measured in uint32_ts rather than in - bytes. hashlittle() is more complicated than hashword() only because - hashlittle() has to dance around fitting the key bytes into registers. --------------------------------------------------------------------- -*/ -uint32_t jenkins_hashword( -const uint32_t *k, /* the key, an array of uint32_t values */ -size_t length, /* the length of the key, in uint32_ts */ -uint32_t initval) /* the previous hash, or an arbitrary value */ -{ - uint32_t a,b,c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; - - /*------------------------------------------------- handle most of the key */ - while (length > 3) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 3; - k += 3; - } - - /*------------------------------------------- handle the last 3 uint32_t's */ - switch(length) /* all the case statements fall through */ - { - case 3 : c+=k[2]; - case 2 : b+=k[1]; - case 1 : a+=k[0]; - final(a,b,c); - case 0: /* case 0: nothing left to add */ - break; - } - /*------------------------------------------------------ report the result */ - return c; -} - - -/* --------------------------------------------------------------------- -hashword2() -- same as hashword(), but take two seeds and return two -32-bit values. pc and pb must both be nonnull, and *pc and *pb must -both be initialized with seeds. If you pass in (*pb)==0, the output -(*pc) will be the same as the return value from hashword(). --------------------------------------------------------------------- -*/ -void jenkins_hashword2 ( -const uint32_t *k, /* the key, an array of uint32_t values */ -size_t length, /* the length of the key, in uint32_ts */ -uint32_t *pc, /* IN: seed OUT: primary hash value */ -uint32_t *pb) /* IN: more seed OUT: secondary hash value */ -{ - uint32_t a,b,c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; - c += *pb; - - /*------------------------------------------------- handle most of the key */ - while (length > 3) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 3; - k += 3; - } - - /*------------------------------------------- handle the last 3 uint32_t's */ - switch(length) /* all the case statements fall through */ - { - case 3 : c+=k[2]; - case 2 : b+=k[1]; - case 1 : a+=k[0]; - final(a,b,c); - case 0: /* case 0: nothing left to add */ - break; - } - /*------------------------------------------------------ report the result */ - *pc=c; *pb=b; -} - - -/* -------------------------------------------------------------------------------- -hashlittle() -- hash a variable-length key into a 32-bit value - k : the key (the unaligned variable-length array of bytes) - length : the length of the key, counting by bytes - initval : can be any 4-byte value -Returns a 32-bit value. Every bit of the key affects every bit of -the return value. Two keys differing by one or two bits will have -totally different hash values. - -The best hash table sizes are powers of 2. There is no need to do -mod a prime (mod is sooo slow!). If you need less than 32 bits, -use a bitmask. For example, if you need only 10 bits, do - h = (h & hashmask(10)); -In which case, the hash table should have hashsize(10) elements. - -If you are hashing n strings (uint8_t **)k, do it like this: - for (i=0, h=0; i 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticeably faster for short strings (like English words). - */ -#ifndef VALGRIND - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : return c; /* zero length strings require no mixing */ - } - -#else /* make valgrind happy */ - { - const uint8_t *k8 = (const uint8_t *) k; - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ - case 1 : a+=k8[0]; break; - case 0 : return c; - } - } - -#endif /* !valgrind */ - - } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) - { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=k[0]; - break; - case 1 : a+=k8[0]; - break; - case 0 : return c; /* zero length requires no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch(length) /* all the case statements fall through */ - { - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9 : c+=k[8]; - case 8 : b+=((uint32_t)k[7])<<24; - case 7 : b+=((uint32_t)k[6])<<16; - case 6 : b+=((uint32_t)k[5])<<8; - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3])<<24; - case 3 : a+=((uint32_t)k[2])<<16; - case 2 : a+=((uint32_t)k[1])<<8; - case 1 : a+=k[0]; - break; - case 0 : return c; - } - } - - final(a,b,c); - return c; -} - - -/* - * hashlittle2: return 2 32-bit hash values - * - * This is identical to hashlittle(), except it returns two 32-bit hash - * values instead of just one. This is good enough for hash table - * lookup with 2^^64 buckets, or if you want a second hash if you're not - * happy with the first, or if you want a probably-unique 64-bit ID for - * the key. *pc is better mixed than *pb, so use *pc first. If you want - * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". - */ -void jenkins_hashlittle2( - const void *key, /* the key to hash */ - size_t length, /* length of the key */ - uint32_t *pc, /* IN: primary initval, OUT: primary hash */ - uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ -{ - uint32_t a,b,c; /* internal state */ - union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; - c += *pb; - - u.ptr = key; - if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { - const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ - - /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticeably faster for short strings (like English words). - */ -#ifndef VALGRIND - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ - } - -#else /* make valgrind happy */ - - { - const uint8_t *k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ - case 1 : a+=k8[0]; break; - case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ - } - } - -#endif /* !valgrind */ - - } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) - { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - /*----------------------------- handle the last (probably partial) block */ - k8 = (const uint8_t *)k; - switch(length) - { - case 12: c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=k[0]; - break; - case 1 : a+=k8[0]; - break; - case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch(length) /* all the case statements fall through */ - { - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9 : c+=k[8]; - case 8 : b+=((uint32_t)k[7])<<24; - case 7 : b+=((uint32_t)k[6])<<16; - case 6 : b+=((uint32_t)k[5])<<8; - case 5 : b+=k[4]; - case 4 : a+=((uint32_t)k[3])<<24; - case 3 : a+=((uint32_t)k[2])<<16; - case 2 : a+=((uint32_t)k[1])<<8; - case 1 : a+=k[0]; - break; - case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ - } - } - - final(a,b,c); - *pc=c; *pb=b; -} - - - -/* - * hashbig(): - * This is the same as hashword() on big-endian machines. It is different - * from hashlittle() on all machines. hashbig() takes advantage of - * big-endian byte ordering. - */ -uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) -{ - uint32_t a,b,c; - union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; - - u.ptr = key; - if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { - const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ - - /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ - while (length > 12) - { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /*----------------------------- handle the last (probably partial) block */ - /* - * "k[2]<<8" actually reads beyond the end of the string, but - * then shifts out the part it's not allowed to read. Because the - * string is aligned, the illegal read is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticeably faster for short strings (like English words). - */ -#ifndef VALGRIND - - switch(length) - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; - case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; - case 5 : b+=k[1]&0xff000000; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff00; break; - case 2 : a+=k[0]&0xffff0000; break; - case 1 : a+=k[0]&0xff000000; break; - case 0 : return c; /* zero length strings require no mixing */ - } - -#else /* make valgrind happy */ - - { - const uint8_t *k8 = (const uint8_t *)k; - switch(length) /* all the case statements fall through */ - { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ - case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ - case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ - case 1 : a+=((uint32_t)k8[0])<<24; break; - case 0 : return c; - } - } - -#endif /* !VALGRIND */ - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ - while (length > 12) - { - a += ((uint32_t)k[0])<<24; - a += ((uint32_t)k[1])<<16; - a += ((uint32_t)k[2])<<8; - a += ((uint32_t)k[3]); - b += ((uint32_t)k[4])<<24; - b += ((uint32_t)k[5])<<16; - b += ((uint32_t)k[6])<<8; - b += ((uint32_t)k[7]); - c += ((uint32_t)k[8])<<24; - c += ((uint32_t)k[9])<<16; - c += ((uint32_t)k[10])<<8; - c += ((uint32_t)k[11]); - mix(a,b,c); - length -= 12; - k += 12; - } - - /*-------------------------------- last block: affect all 32 bits of (c) */ - switch(length) /* all the case statements fall through */ - { - case 12: c+=k[11]; - case 11: c+=((uint32_t)k[10])<<8; - case 10: c+=((uint32_t)k[9])<<16; - case 9 : c+=((uint32_t)k[8])<<24; - case 8 : b+=k[7]; - case 7 : b+=((uint32_t)k[6])<<8; - case 6 : b+=((uint32_t)k[5])<<16; - case 5 : b+=((uint32_t)k[4])<<24; - case 4 : a+=k[3]; - case 3 : a+=((uint32_t)k[2])<<8; - case 2 : a+=((uint32_t)k[1])<<16; - case 1 : a+=((uint32_t)k[0])<<24; - break; - case 0 : return c; - } - } - - final(a,b,c); - return c; -} - - -#ifdef SELF_TEST - -/* used for timings */ -void driver1() -{ - uint8_t buf[256]; - uint32_t i; - uint32_t h=0; - time_t a,z; - - time(&a); - for (i=0; i<256; ++i) buf[i] = 'x'; - for (i=0; i<1; ++i) - { - h = hashlittle(&buf[0],1,h); - } - time(&z); - if (z-a > 0) printf("time %d %.8x\n", z-a, h); -} - -/* check that every input bit changes every output bit half the time */ -#define HASHSTATE 1 -#define HASHLEN 1 -#define MAXPAIR 60 -#define MAXLEN 70 -void driver2() -{ - uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; - uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; - uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; - uint32_t x[HASHSTATE],y[HASHSTATE]; - uint32_t hlen; - - printf("No more than %d trials should ever be needed \n",MAXPAIR/2); - for (hlen=0; hlen < MAXLEN; ++hlen) - { - z=0; - for (i=0; i>(8-j)); - c[0] = hashlittle(a, hlen, m); - b[i] ^= ((k+1)<>(8-j)); - d[0] = hashlittle(b, hlen, m); - /* check every bit is 1, 0, set, and not set at least once */ - for (l=0; lz) z=k; - if (k==MAXPAIR) - { - printf("Some bit didn't change: "); - printf("%.8x %.8x %.8x %.8x %.8x %.8x ", - e[0],f[0],g[0],h[0],x[0],y[0]); - printf("i %d j %d m %d len %d\n", i, j, m, hlen); - } - if (z==MAXPAIR) goto done; - } - } - } - done: - if (z < MAXPAIR) - { - printf("Mix success %2d bytes %2d initvals ",i,m); - printf("required %d trials\n", z/2); - } - } - printf("\n"); -} - -/* Check for reading beyond the end of the buffer and alignment problems */ -void driver3() -{ - uint8_t buf[MAXLEN+20], *b; - uint32_t len; - uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; - uint32_t h; - uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; - uint32_t i; - uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; - uint32_t j; - uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; - uint32_t ref,x,y; - uint8_t *p; - - printf("Endianness. These lines should all be the same (for values filled in):\n"); - printf("%.8x %.8x %.8x\n", - hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), - hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), - hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); - p = q; - printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", - hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), - hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), - hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), - hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), - hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), - hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); - p = &qq[1]; - printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", - hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), - hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), - hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), - hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), - hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), - hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); - p = &qqq[2]; - printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", - hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), - hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), - hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), - hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), - hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), - hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); - p = &qqqq[3]; - printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", - hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), - hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), - hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), - hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), - hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), - hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); - printf("\n"); - - /* check that hashlittle2 and hashlittle produce the same results */ - i=47; j=0; - hashlittle2(q, sizeof(q), &i, &j); - if (hashlittle(q, sizeof(q), 47) != i) - printf("hashlittle2 and hashlittle mismatch\n"); - - /* check that hashword2 and hashword produce the same results */ - len = 0xdeadbeef; - i=47, j=0; - hashword2(&len, 1, &i, &j); - if (hashword(&len, 1, 47) != i) - printf("hashword2 and hashword mismatch %x %x\n", - i, hashword(&len, 1, 47)); - - /* check hashlittle doesn't read before or after the ends of the string */ - for (h=0, b=buf+1; h<8; ++h, ++b) - { - for (i=0; i -#include - -#include "macro.h" - -uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval) _pure_; -void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); - -uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval) _pure_; -void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); - -uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_; - -static inline uint64_t hash64(const void *data, size_t length) { - uint32_t a = 0, b = 0; - - jenkins_hashlittle2(data, length, &a, &b); - - return ((uint64_t) a << 32ULL) | (uint64_t) b; -} diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c deleted file mode 100644 index 293d27053a..0000000000 --- a/src/journal/mmap-cache.c +++ /dev/null @@ -1,725 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "list.h" -#include "log.h" -#include "macro.h" -#include "mmap-cache.h" -#include "sigbus.h" -#include "util.h" - -typedef struct Window Window; -typedef struct Context Context; -typedef struct FileDescriptor FileDescriptor; - -struct Window { - MMapCache *cache; - - bool invalidated:1; - bool keep_always:1; - bool in_unused:1; - - int prot; - void *ptr; - uint64_t offset; - size_t size; - - FileDescriptor *fd; - - LIST_FIELDS(Window, by_fd); - LIST_FIELDS(Window, unused); - - LIST_HEAD(Context, contexts); -}; - -struct Context { - MMapCache *cache; - unsigned id; - Window *window; - - LIST_FIELDS(Context, by_window); -}; - -struct FileDescriptor { - MMapCache *cache; - int fd; - bool sigbus; - LIST_HEAD(Window, windows); -}; - -struct MMapCache { - int n_ref; - unsigned n_windows; - - unsigned n_hit, n_missed; - - Hashmap *fds; - Context *contexts[MMAP_CACHE_MAX_CONTEXTS]; - - LIST_HEAD(Window, unused); - Window *last_unused; -}; - -#define WINDOWS_MIN 64 - -#ifdef ENABLE_DEBUG_MMAP_CACHE -/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */ -# define WINDOW_SIZE (page_size()) -#else -# define WINDOW_SIZE (8ULL*1024ULL*1024ULL) -#endif - -MMapCache* mmap_cache_new(void) { - MMapCache *m; - - m = new0(MMapCache, 1); - if (!m) - return NULL; - - m->n_ref = 1; - return m; -} - -MMapCache* mmap_cache_ref(MMapCache *m) { - assert(m); - assert(m->n_ref > 0); - - m->n_ref++; - return m; -} - -static void window_unlink(Window *w) { - Context *c; - - assert(w); - - if (w->ptr) - munmap(w->ptr, w->size); - - if (w->fd) - LIST_REMOVE(by_fd, w->fd->windows, w); - - if (w->in_unused) { - if (w->cache->last_unused == w) - w->cache->last_unused = w->unused_prev; - - LIST_REMOVE(unused, w->cache->unused, w); - } - - LIST_FOREACH(by_window, c, w->contexts) { - assert(c->window == w); - c->window = NULL; - } -} - -static void window_invalidate(Window *w) { - assert(w); - - if (w->invalidated) - return; - - /* Replace the window with anonymous pages. This is useful - * when we hit a SIGBUS and want to make sure the file cannot - * trigger any further SIGBUS, possibly overrunning the sigbus - * queue. */ - - assert_se(mmap(w->ptr, w->size, w->prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == w->ptr); - w->invalidated = true; -} - -static void window_free(Window *w) { - assert(w); - - window_unlink(w); - w->cache->n_windows--; - free(w); -} - -_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { - assert(w); - assert(fd >= 0); - assert(size > 0); - - return - w->fd && - fd == w->fd->fd && - prot == w->prot && - offset >= w->offset && - offset + size <= w->offset + w->size; -} - -static Window *window_add(MMapCache *m, FileDescriptor *fd, int prot, bool keep_always, uint64_t offset, size_t size, void *ptr) { - Window *w; - - assert(m); - assert(fd); - - if (!m->last_unused || m->n_windows <= WINDOWS_MIN) { - - /* Allocate a new window */ - w = new0(Window, 1); - if (!w) - return NULL; - m->n_windows++; - } else { - - /* Reuse an existing one */ - w = m->last_unused; - window_unlink(w); - zero(*w); - } - - w->cache = m; - w->fd = fd; - w->prot = prot; - w->keep_always = keep_always; - w->offset = offset; - w->size = size; - w->ptr = ptr; - - LIST_PREPEND(by_fd, fd->windows, w); - - return w; -} - -static void context_detach_window(Context *c) { - Window *w; - - assert(c); - - if (!c->window) - return; - - w = c->window; - c->window = NULL; - LIST_REMOVE(by_window, w->contexts, c); - - if (!w->contexts && !w->keep_always) { - /* Not used anymore? */ -#ifdef ENABLE_DEBUG_MMAP_CACHE - /* Unmap unused windows immediately to expose use-after-unmap - * by SIGSEGV. */ - window_free(w); -#else - LIST_PREPEND(unused, c->cache->unused, w); - if (!c->cache->last_unused) - c->cache->last_unused = w; - - w->in_unused = true; -#endif - } -} - -static void context_attach_window(Context *c, Window *w) { - assert(c); - assert(w); - - if (c->window == w) - return; - - context_detach_window(c); - - if (w->in_unused) { - /* Used again? */ - LIST_REMOVE(unused, c->cache->unused, w); - if (c->cache->last_unused == w) - c->cache->last_unused = w->unused_prev; - - w->in_unused = false; - } - - c->window = w; - LIST_PREPEND(by_window, w->contexts, c); -} - -static Context *context_add(MMapCache *m, unsigned id) { - Context *c; - - assert(m); - - c = m->contexts[id]; - if (c) - return c; - - c = new0(Context, 1); - if (!c) - return NULL; - - c->cache = m; - c->id = id; - - assert(!m->contexts[id]); - m->contexts[id] = c; - - return c; -} - -static void context_free(Context *c) { - assert(c); - - context_detach_window(c); - - if (c->cache) { - assert(c->cache->contexts[c->id] == c); - c->cache->contexts[c->id] = NULL; - } - - free(c); -} - -static void fd_free(FileDescriptor *f) { - assert(f); - - while (f->windows) - window_free(f->windows); - - if (f->cache) - assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); - - free(f); -} - -static FileDescriptor* fd_add(MMapCache *m, int fd) { - FileDescriptor *f; - int r; - - assert(m); - assert(fd >= 0); - - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (f) - return f; - - r = hashmap_ensure_allocated(&m->fds, NULL); - if (r < 0) - return NULL; - - f = new0(FileDescriptor, 1); - if (!f) - return NULL; - - f->cache = m; - f->fd = fd; - - r = hashmap_put(m->fds, FD_TO_PTR(fd), f); - if (r < 0) { - free(f); - return NULL; - } - - return f; -} - -static void mmap_cache_free(MMapCache *m) { - FileDescriptor *f; - int i; - - assert(m); - - for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++) - if (m->contexts[i]) - context_free(m->contexts[i]); - - while ((f = hashmap_first(m->fds))) - fd_free(f); - - hashmap_free(m->fds); - - while (m->unused) - window_free(m->unused); - - free(m); -} - -MMapCache* mmap_cache_unref(MMapCache *m) { - - if (!m) - return NULL; - - assert(m->n_ref > 0); - - m->n_ref--; - if (m->n_ref == 0) - mmap_cache_free(m); - - return NULL; -} - -static int make_room(MMapCache *m) { - assert(m); - - if (!m->last_unused) - return 0; - - window_free(m->last_unused); - return 1; -} - -static int try_context( - MMapCache *m, - int fd, - int prot, - unsigned context, - bool keep_always, - uint64_t offset, - size_t size, - void **ret) { - - Context *c; - - assert(m); - assert(m->n_ref > 0); - assert(fd >= 0); - assert(size > 0); - assert(ret); - - c = m->contexts[context]; - if (!c) - return 0; - - assert(c->id == context); - - if (!c->window) - return 0; - - if (!window_matches(c->window, fd, prot, offset, size)) { - - /* Drop the reference to the window, since it's unnecessary now */ - context_detach_window(c); - return 0; - } - - if (c->window->fd->sigbus) - return -EIO; - - c->window->keep_always = c->window->keep_always || keep_always; - - *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); - return 1; -} - -static int find_mmap( - MMapCache *m, - int fd, - int prot, - unsigned context, - bool keep_always, - uint64_t offset, - size_t size, - void **ret) { - - FileDescriptor *f; - Window *w; - Context *c; - - assert(m); - assert(m->n_ref > 0); - assert(fd >= 0); - assert(size > 0); - - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return 0; - - assert(f->fd == fd); - - if (f->sigbus) - return -EIO; - - LIST_FOREACH(by_fd, w, f->windows) - if (window_matches(w, fd, prot, offset, size)) - break; - - if (!w) - return 0; - - c = context_add(m, context); - if (!c) - return -ENOMEM; - - context_attach_window(c, w); - w->keep_always = w->keep_always || keep_always; - - *ret = (uint8_t*) w->ptr + (offset - w->offset); - return 1; -} - -static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags, uint64_t offset, size_t size, void **res) { - void *ptr; - - assert(m); - assert(fd >= 0); - assert(res); - - for (;;) { - int r; - - ptr = mmap(addr, size, prot, flags, fd, offset); - if (ptr != MAP_FAILED) - break; - if (errno != ENOMEM) - return negative_errno(); - - r = make_room(m); - if (r < 0) - return r; - if (r == 0) - return -ENOMEM; - } - - *res = ptr; - return 0; -} - -static int add_mmap( - MMapCache *m, - int fd, - int prot, - unsigned context, - bool keep_always, - uint64_t offset, - size_t size, - struct stat *st, - void **ret) { - - uint64_t woffset, wsize; - Context *c; - FileDescriptor *f; - Window *w; - void *d; - int r; - - assert(m); - assert(m->n_ref > 0); - assert(fd >= 0); - assert(size > 0); - assert(ret); - - woffset = offset & ~((uint64_t) page_size() - 1ULL); - wsize = size + (offset - woffset); - wsize = PAGE_ALIGN(wsize); - - if (wsize < WINDOW_SIZE) { - uint64_t delta; - - delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2); - - if (delta > offset) - woffset = 0; - else - woffset -= delta; - - wsize = WINDOW_SIZE; - } - - if (st) { - /* Memory maps that are larger then the files - underneath have undefined behavior. Hence, clamp - things to the file size if we know it */ - - if (woffset >= (uint64_t) st->st_size) - return -EADDRNOTAVAIL; - - if (woffset + wsize > (uint64_t) st->st_size) - wsize = PAGE_ALIGN(st->st_size - woffset); - } - - r = mmap_try_harder(m, NULL, fd, prot, MAP_SHARED, woffset, wsize, &d); - if (r < 0) - return r; - - c = context_add(m, context); - if (!c) - goto outofmem; - - f = fd_add(m, fd); - if (!f) - goto outofmem; - - w = window_add(m, f, prot, keep_always, woffset, wsize, d); - if (!w) - goto outofmem; - - context_detach_window(c); - c->window = w; - LIST_PREPEND(by_window, w->contexts, c); - - *ret = (uint8_t*) w->ptr + (offset - w->offset); - return 1; - -outofmem: - (void) munmap(d, wsize); - return -ENOMEM; -} - -int mmap_cache_get( - MMapCache *m, - int fd, - int prot, - unsigned context, - bool keep_always, - uint64_t offset, - size_t size, - struct stat *st, - void **ret) { - - int r; - - assert(m); - assert(m->n_ref > 0); - assert(fd >= 0); - assert(size > 0); - assert(ret); - assert(context < MMAP_CACHE_MAX_CONTEXTS); - - /* Check whether the current context is the right one already */ - r = try_context(m, fd, prot, context, keep_always, offset, size, ret); - if (r != 0) { - m->n_hit++; - return r; - } - - /* Search for a matching mmap */ - r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); - if (r != 0) { - m->n_hit++; - return r; - } - - m->n_missed++; - - /* Create a new mmap */ - return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret); -} - -unsigned mmap_cache_get_hit(MMapCache *m) { - assert(m); - - return m->n_hit; -} - -unsigned mmap_cache_get_missed(MMapCache *m) { - assert(m); - - return m->n_missed; -} - -static void mmap_cache_process_sigbus(MMapCache *m) { - bool found = false; - FileDescriptor *f; - Iterator i; - int r; - - assert(m); - - /* Iterate through all triggered pages and mark their files as - * invalidated */ - for (;;) { - bool ours; - void *addr; - - r = sigbus_pop(&addr); - if (_likely_(r == 0)) - break; - if (r < 0) { - log_error_errno(r, "SIGBUS handling failed: %m"); - abort(); - } - - ours = false; - HASHMAP_FOREACH(f, m->fds, i) { - Window *w; - - LIST_FOREACH(by_fd, w, f->windows) { - if ((uint8_t*) addr >= (uint8_t*) w->ptr && - (uint8_t*) addr < (uint8_t*) w->ptr + w->size) { - found = ours = f->sigbus = true; - break; - } - } - - if (ours) - break; - } - - /* Didn't find a matching window, give up */ - if (!ours) { - log_error("Unknown SIGBUS page, aborting."); - abort(); - } - } - - /* The list of triggered pages is now empty. Now, let's remap - * all windows of the triggered file to anonymous maps, so - * that no page of the file in question is triggered again, so - * that we can be sure not to hit the queue size limit. */ - if (_likely_(!found)) - return; - - HASHMAP_FOREACH(f, m->fds, i) { - Window *w; - - if (!f->sigbus) - continue; - - LIST_FOREACH(by_fd, w, f->windows) - window_invalidate(w); - } -} - -bool mmap_cache_got_sigbus(MMapCache *m, int fd) { - FileDescriptor *f; - - assert(m); - assert(fd >= 0); - - mmap_cache_process_sigbus(m); - - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return false; - - return f->sigbus; -} - -void mmap_cache_close_fd(MMapCache *m, int fd) { - FileDescriptor *f; - - assert(m); - assert(fd >= 0); - - /* Make sure that any queued SIGBUS are first dispatched, so - * that we don't end up with a SIGBUS entry we cannot relate - * to any existing memory map */ - - mmap_cache_process_sigbus(m); - - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return; - - fd_free(f); -} diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h deleted file mode 100644 index 199d944647..0000000000 --- a/src/journal/mmap-cache.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -/* One context per object type, plus one of the header, plus one "additional" one */ -#define MMAP_CACHE_MAX_CONTEXTS 9 - -typedef struct MMapCache MMapCache; - -MMapCache* mmap_cache_new(void); -MMapCache* mmap_cache_ref(MMapCache *m); -MMapCache* mmap_cache_unref(MMapCache *m); - -int mmap_cache_get( - MMapCache *m, - int fd, - int prot, - unsigned context, - bool keep_always, - uint64_t offset, - size_t size, - struct stat *st, - void **ret); -void mmap_cache_close_fd(MMapCache *m, int fd); - -unsigned mmap_cache_get_hit(MMapCache *m); -unsigned mmap_cache_get_missed(MMapCache *m); - -bool mmap_cache_got_sigbus(MMapCache *m, int fd); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c deleted file mode 100644 index 75a0ffb49b..0000000000 --- a/src/journal/sd-journal.c +++ /dev/null @@ -1,2985 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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 "journal-internal.h" -#include "list.h" -#include "lookup3.h" -#include "missing.h" -#include "path-util.h" -#include "replace-var.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" - -#define JOURNAL_FILES_MAX 7168 - -#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC) - -#define REPLACE_VAR_MAX 256 - -#define DEFAULT_DATA_THRESHOLD (64*1024) - -static void remove_file_real(sd_journal *j, JournalFile *f); - -static bool journal_pid_changed(sd_journal *j) { - assert(j); - - /* We don't support people creating a journal object and - * keeping it around over a fork(). Let's complain. */ - - return j->original_pid != getpid(); -} - -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 = hashmap_ensure_allocated(&j->errors, NULL); - if (k < 0) - return k; - - 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) { - Iterator i; - JournalFile *f; - - assert(j); - - j->current_file = NULL; - j->current_field = 0; - - ORDERED_HASHMAP_FOREACH(f, j->files, i) - journal_file_reset_location(f); -} - -static void reset_location(sd_journal *j) { - assert(j); - - detach_location(j); - zero(j->current_location); -} - -static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) { - assert(l); - assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); - assert(f); - assert(o->object.type == OBJECT_ENTRY); - - l->type = type; - l->seqnum = le64toh(o->entry.seqnum); - l->seqnum_id = f->header->seqnum_id; - l->realtime = le64toh(o->entry.realtime); - l->monotonic = le64toh(o->entry.monotonic); - l->boot_id = o->entry.boot_id; - l->xor_hash = le64toh(o->entry.xor_hash); - - l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; -} - -static void set_location(sd_journal *j, JournalFile *f, Object *o) { - assert(j); - assert(f); - assert(o); - - init_location(&j->current_location, LOCATION_DISCRETE, f, o); - - j->current_file = f; - j->current_field = 0; - - /* Let f know its candidate entry was picked. */ - assert(f->location_type == LOCATION_SEEK); - f->location_type = LOCATION_DISCRETE; -} - -static int match_is_valid(const void *data, size_t size) { - const char *b, *p; - - assert(data); - - if (size < 2) - return false; - - if (startswith(data, "__")) - return false; - - b = data; - for (p = b; p < b + size; p++) { - - if (*p == '=') - return p > b; - - if (*p == '_') - continue; - - if (*p >= 'A' && *p <= 'Z') - continue; - - if (*p >= '0' && *p <= '9') - continue; - - return false; - } - - return false; -} - -static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { - const uint8_t *a = _a, *b = _b; - size_t j; - - for (j = 0; j < s && j < t; j++) { - - if (a[j] != b[j]) - return false; - - if (a[j] == '=') - return true; - } - - assert_not_reached("\"=\" not found"); -} - -static Match *match_new(Match *p, MatchType t) { - Match *m; - - m = new0(Match, 1); - if (!m) - return NULL; - - m->type = t; - - if (p) { - m->parent = p; - LIST_PREPEND(matches, p->matches, m); - } - - return m; -} - -static void match_free(Match *m) { - assert(m); - - while (m->matches) - match_free(m->matches); - - if (m->parent) - LIST_REMOVE(matches, m->parent->matches, m); - - free(m->data); - free(m); -} - -static void match_free_if_empty(Match *m) { - if (!m || m->matches) - return; - - match_free(m); -} - -_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *l3, *l4, *add_here = NULL, *m; - le64_t le_hash; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(data, -EINVAL); - - if (size == 0) - size = strlen(data); - - assert_return(match_is_valid(data, size), -EINVAL); - - /* level 0: AND term - * level 1: OR terms - * level 2: AND terms - * level 3: OR terms - * level 4: concrete matches */ - - if (!j->level0) { - j->level0 = match_new(NULL, MATCH_AND_TERM); - if (!j->level0) - return -ENOMEM; - } - - if (!j->level1) { - j->level1 = match_new(j->level0, MATCH_OR_TERM); - if (!j->level1) - return -ENOMEM; - } - - if (!j->level2) { - j->level2 = match_new(j->level1, MATCH_AND_TERM); - if (!j->level2) - return -ENOMEM; - } - - assert(j->level0->type == MATCH_AND_TERM); - assert(j->level1->type == MATCH_OR_TERM); - assert(j->level2->type == MATCH_AND_TERM); - - le_hash = htole64(hash64(data, size)); - - LIST_FOREACH(matches, l3, j->level2->matches) { - assert(l3->type == MATCH_OR_TERM); - - LIST_FOREACH(matches, l4, l3->matches) { - assert(l4->type == MATCH_DISCRETE); - - /* Exactly the same match already? Then ignore - * this addition */ - if (l4->le_hash == le_hash && - l4->size == size && - memcmp(l4->data, data, size) == 0) - return 0; - - /* Same field? Then let's add this to this OR term */ - if (same_field(data, size, l4->data, l4->size)) { - add_here = l3; - break; - } - } - - if (add_here) - break; - } - - if (!add_here) { - add_here = match_new(j->level2, MATCH_OR_TERM); - if (!add_here) - goto fail; - } - - m = match_new(add_here, MATCH_DISCRETE); - if (!m) - goto fail; - - m->le_hash = le_hash; - m->size = size; - m->data = memdup(data, size); - if (!m->data) - goto fail; - - detach_location(j); - - return 0; - -fail: - match_free_if_empty(add_here); - match_free_if_empty(j->level2); - match_free_if_empty(j->level1); - match_free_if_empty(j->level0); - - return -ENOMEM; -} - -_public_ int sd_journal_add_conjunction(sd_journal *j) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - if (!j->level0) - return 0; - - if (!j->level1) - return 0; - - if (!j->level1->matches) - return 0; - - j->level1 = NULL; - j->level2 = NULL; - - return 0; -} - -_public_ int sd_journal_add_disjunction(sd_journal *j) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - if (!j->level0) - return 0; - - if (!j->level1) - return 0; - - if (!j->level2) - return 0; - - if (!j->level2->matches) - return 0; - - j->level2 = NULL; - return 0; -} - -static char *match_make_string(Match *m) { - char *p, *r; - Match *i; - bool enclose = false; - - if (!m) - return strdup("none"); - - if (m->type == MATCH_DISCRETE) - return strndup(m->data, m->size); - - p = NULL; - LIST_FOREACH(matches, i, m->matches) { - char *t, *k; - - t = match_make_string(i); - if (!t) { - free(p); - return NULL; - } - - if (p) { - k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL); - free(p); - free(t); - - if (!k) - return NULL; - - p = k; - - enclose = true; - } else - p = t; - } - - if (enclose) { - r = strjoin("(", p, ")", NULL); - free(p); - return r; - } - - return p; -} - -char *journal_make_match_string(sd_journal *j) { - assert(j); - - return match_make_string(j->level0); -} - -_public_ void sd_journal_flush_matches(sd_journal *j) { - if (!j) - return; - - if (j->level0) - match_free(j->level0); - - j->level0 = j->level1 = j->level2 = NULL; - - detach_location(j); -} - -_pure_ static int compare_with_location(JournalFile *f, Location *l) { - assert(f); - assert(l); - assert(f->location_type == LOCATION_SEEK); - assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK); - - if (l->monotonic_set && - sd_id128_equal(f->current_boot_id, l->boot_id) && - l->realtime_set && - f->current_realtime == l->realtime && - l->xor_hash_set && - f->current_xor_hash == l->xor_hash) - return 0; - - if (l->seqnum_set && - sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) { - - if (f->current_seqnum < l->seqnum) - return -1; - if (f->current_seqnum > l->seqnum) - return 1; - } - - if (l->monotonic_set && - sd_id128_equal(f->current_boot_id, l->boot_id)) { - - if (f->current_monotonic < l->monotonic) - return -1; - if (f->current_monotonic > l->monotonic) - return 1; - } - - if (l->realtime_set) { - - if (f->current_realtime < l->realtime) - return -1; - if (f->current_realtime > l->realtime) - return 1; - } - - if (l->xor_hash_set) { - - if (f->current_xor_hash < l->xor_hash) - return -1; - if (f->current_xor_hash > l->xor_hash) - return 1; - } - - return 0; -} - -static int next_for_match( - sd_journal *j, - Match *m, - JournalFile *f, - uint64_t after_offset, - direction_t direction, - Object **ret, - uint64_t *offset) { - - int r; - uint64_t np = 0; - Object *n; - - assert(j); - assert(m); - assert(f); - - if (m->type == MATCH_DISCRETE) { - uint64_t dp; - - r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); - if (r <= 0) - return r; - - return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset); - - } else if (m->type == MATCH_OR_TERM) { - Match *i; - - /* Find the earliest match beyond after_offset */ - - LIST_FOREACH(matches, i, m->matches) { - uint64_t cp; - - r = next_for_match(j, i, f, after_offset, direction, NULL, &cp); - if (r < 0) - return r; - else if (r > 0) { - if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np)) - np = cp; - } - } - - if (np == 0) - return 0; - - } else if (m->type == MATCH_AND_TERM) { - Match *i, *last_moved; - - /* Always jump to the next matching entry and repeat - * this until we find an offset that matches for all - * matches. */ - - if (!m->matches) - return 0; - - r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np); - if (r <= 0) - return r; - - assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset); - last_moved = m->matches; - - LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) { - uint64_t cp; - - r = next_for_match(j, i, f, np, direction, NULL, &cp); - if (r <= 0) - return r; - - assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np); - if (direction == DIRECTION_DOWN ? cp > np : cp < np) { - np = cp; - last_moved = i; - } - } - } - - assert(np > 0); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); - if (r < 0) - return r; - - if (ret) - *ret = n; - if (offset) - *offset = np; - - return 1; -} - -static int find_location_for_match( - sd_journal *j, - Match *m, - JournalFile *f, - direction_t direction, - Object **ret, - uint64_t *offset) { - - int r; - - assert(j); - assert(m); - assert(f); - - if (m->type == MATCH_DISCRETE) { - uint64_t dp; - - r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); - if (r <= 0) - return r; - - /* FIXME: missing: find by monotonic */ - - if (j->current_location.type == LOCATION_HEAD) - return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset); - if (j->current_location.type == LOCATION_TAIL) - return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset); - if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) - return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset); - if (j->current_location.monotonic_set) { - r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); - if (r != -ENOENT) - return r; - } - if (j->current_location.realtime_set) - return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset); - - return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset); - - } else if (m->type == MATCH_OR_TERM) { - uint64_t np = 0; - Object *n; - Match *i; - - /* Find the earliest match */ - - LIST_FOREACH(matches, i, m->matches) { - uint64_t cp; - - r = find_location_for_match(j, i, f, direction, NULL, &cp); - if (r < 0) - return r; - else if (r > 0) { - if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) - np = cp; - } - } - - if (np == 0) - return 0; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); - if (r < 0) - return r; - - if (ret) - *ret = n; - if (offset) - *offset = np; - - return 1; - - } else { - Match *i; - uint64_t np = 0; - - assert(m->type == MATCH_AND_TERM); - - /* First jump to the last match, and then find the - * next one where all matches match */ - - if (!m->matches) - return 0; - - LIST_FOREACH(matches, i, m->matches) { - uint64_t cp; - - r = find_location_for_match(j, i, f, direction, NULL, &cp); - if (r <= 0) - return r; - - if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np)) - np = cp; - } - - return next_for_match(j, m, f, np, direction, ret, offset); - } -} - -static int find_location_with_matches( - sd_journal *j, - JournalFile *f, - direction_t direction, - Object **ret, - uint64_t *offset) { - - int r; - - assert(j); - assert(f); - assert(ret); - assert(offset); - - if (!j->level0) { - /* No matches is simple */ - - if (j->current_location.type == LOCATION_HEAD) - return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset); - if (j->current_location.type == LOCATION_TAIL) - return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset); - if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) - return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset); - if (j->current_location.monotonic_set) { - r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); - if (r != -ENOENT) - return r; - } - if (j->current_location.realtime_set) - return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset); - - return journal_file_next_entry(f, 0, direction, ret, offset); - } else - return find_location_for_match(j, j->level0, f, direction, ret, offset); -} - -static int next_with_matches( - sd_journal *j, - JournalFile *f, - direction_t direction, - Object **ret, - uint64_t *offset) { - - assert(j); - assert(f); - assert(ret); - assert(offset); - - /* No matches is easy. We simple advance the file - * pointer by one. */ - if (!j->level0) - return journal_file_next_entry(f, f->current_offset, direction, ret, offset); - - /* If we have a match then we look for the next matching entry - * with an offset at least one step larger */ - return next_for_match(j, j->level0, f, - direction == DIRECTION_DOWN ? f->current_offset + 1 - : f->current_offset - 1, - direction, ret, offset); -} - -static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) { - Object *c; - uint64_t cp, n_entries; - int r; - - assert(j); - assert(f); - - n_entries = le64toh(f->header->n_entries); - - /* If we hit EOF before, we don't need to look into this file again - * unless direction changed or new entries appeared. */ - if (f->last_direction == direction && f->location_type == LOCATION_TAIL && - n_entries == f->last_n_entries) - return 0; - - f->last_n_entries = n_entries; - - if (f->last_direction == direction && f->current_offset > 0) { - /* LOCATION_SEEK here means we did the work in a previous - * iteration and the current location already points to a - * candidate entry. */ - if (f->location_type != LOCATION_SEEK) { - r = next_with_matches(j, f, direction, &c, &cp); - if (r <= 0) - return r; - - journal_file_save_location(f, c, cp); - } - } else { - f->last_direction = direction; - - r = find_location_with_matches(j, f, direction, &c, &cp); - if (r <= 0) - return r; - - journal_file_save_location(f, c, cp); - } - - /* OK, we found the spot, now let's advance until an entry - * that is actually different from what we were previously - * looking at. This is necessary to handle entries which exist - * in two (or more) journal files, and which shall all be - * suppressed but one. */ - - for (;;) { - bool found; - - if (j->current_location.type == LOCATION_DISCRETE) { - int k; - - k = compare_with_location(f, &j->current_location); - - found = direction == DIRECTION_DOWN ? k > 0 : k < 0; - } else - found = true; - - if (found) - return 1; - - r = next_with_matches(j, f, direction, &c, &cp); - if (r <= 0) - return r; - - journal_file_save_location(f, c, cp); - } -} - -static int real_journal_next(sd_journal *j, direction_t direction) { - JournalFile *f, *new_file = NULL; - Iterator i; - Object *o; - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - bool found; - - r = next_beyond_location(j, f, direction); - if (r < 0) { - log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path); - remove_file_real(j, f); - continue; - } else if (r == 0) { - f->location_type = LOCATION_TAIL; - continue; - } - - if (!new_file) - found = true; - else { - int k; - - k = journal_file_compare_locations(f, new_file); - - found = direction == DIRECTION_DOWN ? k < 0 : k > 0; - } - - if (found) - new_file = f; - } - - if (!new_file) - return 0; - - r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o); - if (r < 0) - return r; - - set_location(j, new_file, o); - - return 1; -} - -_public_ int sd_journal_next(sd_journal *j) { - return real_journal_next(j, DIRECTION_DOWN); -} - -_public_ int sd_journal_previous(sd_journal *j) { - return real_journal_next(j, DIRECTION_UP); -} - -static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { - int c = 0, r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - if (skip == 0) { - /* If this is not a discrete skip, then at least - * resolve the current location */ - if (j->current_location.type != LOCATION_DISCRETE) - return real_journal_next(j, direction); - - return 0; - } - - do { - r = real_journal_next(j, direction); - if (r < 0) - return r; - - if (r == 0) - return c; - - skip--; - c++; - } while (skip > 0); - - return c; -} - -_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { - return real_journal_next_skip(j, DIRECTION_DOWN, skip); -} - -_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { - return real_journal_next_skip(j, DIRECTION_UP, skip); -} - -_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { - Object *o; - int r; - char bid[33], sid[33]; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(cursor, -EINVAL); - - if (!j->current_file || j->current_file->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); - if (r < 0) - return r; - - sd_id128_to_string(j->current_file->header->seqnum_id, sid); - sd_id128_to_string(o->entry.boot_id, bid); - - if (asprintf(cursor, - "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64, - sid, le64toh(o->entry.seqnum), - bid, le64toh(o->entry.monotonic), - le64toh(o->entry.realtime), - le64toh(o->entry.xor_hash)) < 0) - return -ENOMEM; - - return 0; -} - -_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { - const char *word, *state; - size_t l; - unsigned long long seqnum, monotonic, realtime, xor_hash; - bool - seqnum_id_set = false, - seqnum_set = false, - boot_id_set = false, - monotonic_set = false, - realtime_set = false, - xor_hash_set = false; - sd_id128_t seqnum_id, boot_id; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(!isempty(cursor), -EINVAL); - - FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { - char *item; - int k = 0; - - if (l < 2 || word[1] != '=') - return -EINVAL; - - item = strndup(word, l); - if (!item) - return -ENOMEM; - - switch (word[0]) { - - case 's': - seqnum_id_set = true; - k = sd_id128_from_string(item+2, &seqnum_id); - break; - - case 'i': - seqnum_set = true; - if (sscanf(item+2, "%llx", &seqnum) != 1) - k = -EINVAL; - break; - - case 'b': - boot_id_set = true; - k = sd_id128_from_string(item+2, &boot_id); - break; - - case 'm': - monotonic_set = true; - if (sscanf(item+2, "%llx", &monotonic) != 1) - k = -EINVAL; - break; - - case 't': - realtime_set = true; - if (sscanf(item+2, "%llx", &realtime) != 1) - k = -EINVAL; - break; - - case 'x': - xor_hash_set = true; - if (sscanf(item+2, "%llx", &xor_hash) != 1) - k = -EINVAL; - break; - } - - free(item); - - if (k < 0) - return k; - } - - if ((!seqnum_set || !seqnum_id_set) && - (!monotonic_set || !boot_id_set) && - !realtime_set) - return -EINVAL; - - reset_location(j); - - j->current_location.type = LOCATION_SEEK; - - if (realtime_set) { - j->current_location.realtime = (uint64_t) realtime; - j->current_location.realtime_set = true; - } - - if (seqnum_set && seqnum_id_set) { - j->current_location.seqnum = (uint64_t) seqnum; - j->current_location.seqnum_id = seqnum_id; - j->current_location.seqnum_set = true; - } - - if (monotonic_set && boot_id_set) { - j->current_location.monotonic = (uint64_t) monotonic; - j->current_location.boot_id = boot_id; - j->current_location.monotonic_set = true; - } - - if (xor_hash_set) { - j->current_location.xor_hash = (uint64_t) xor_hash; - j->current_location.xor_hash_set = true; - } - - return 0; -} - -_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { - int r; - Object *o; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(!isempty(cursor), -EINVAL); - - if (!j->current_file || j->current_file->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); - if (r < 0) - return r; - - for (;;) { - _cleanup_free_ char *item = NULL; - unsigned long long ll; - sd_id128_t id; - int k = 0; - - r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - - if (r == 0) - break; - - if (strlen(item) < 2 || item[1] != '=') - return -EINVAL; - - switch (item[0]) { - - case 's': - k = sd_id128_from_string(item+2, &id); - if (k < 0) - return k; - if (!sd_id128_equal(id, j->current_file->header->seqnum_id)) - return 0; - break; - - case 'i': - if (sscanf(item+2, "%llx", &ll) != 1) - return -EINVAL; - if (ll != le64toh(o->entry.seqnum)) - return 0; - break; - - case 'b': - k = sd_id128_from_string(item+2, &id); - if (k < 0) - return k; - if (!sd_id128_equal(id, o->entry.boot_id)) - return 0; - break; - - case 'm': - if (sscanf(item+2, "%llx", &ll) != 1) - return -EINVAL; - if (ll != le64toh(o->entry.monotonic)) - return 0; - break; - - case 't': - if (sscanf(item+2, "%llx", &ll) != 1) - return -EINVAL; - if (ll != le64toh(o->entry.realtime)) - return 0; - break; - - case 'x': - if (sscanf(item+2, "%llx", &ll) != 1) - return -EINVAL; - if (ll != le64toh(o->entry.xor_hash)) - return 0; - break; - } - } - - return 1; -} - - -_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - reset_location(j); - j->current_location.type = LOCATION_SEEK; - j->current_location.boot_id = boot_id; - j->current_location.monotonic = usec; - j->current_location.monotonic_set = true; - - return 0; -} - -_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - reset_location(j); - j->current_location.type = LOCATION_SEEK; - j->current_location.realtime = usec; - j->current_location.realtime_set = true; - - return 0; -} - -_public_ int sd_journal_seek_head(sd_journal *j) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - reset_location(j); - j->current_location.type = LOCATION_HEAD; - - return 0; -} - -_public_ int sd_journal_seek_tail(sd_journal *j) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - reset_location(j); - j->current_location.type = LOCATION_TAIL; - - return 0; -} - -static void check_network(sd_journal *j, int fd) { - struct statfs sfs; - - assert(j); - - if (j->on_network) - return; - - if (fstatfs(fd, &sfs) < 0) - return; - - j->on_network = - F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) || - F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC); -} - -static bool file_has_type_prefix(const char *prefix, const char *filename) { - const char *full, *tilded, *atted; - - full = strjoina(prefix, ".journal"); - tilded = strjoina(full, "~"); - atted = strjoina(prefix, "@"); - - return streq(filename, full) || - streq(filename, tilded) || - startswith(filename, atted); -} - -static bool file_type_wanted(int flags, const char *filename) { - assert(filename); - - if (!endswith(filename, ".journal") && !endswith(filename, ".journal~")) - return false; - - /* no flags set → every type is OK */ - if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER))) - return true; - - if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename)) - return true; - - if (flags & SD_JOURNAL_CURRENT_USER) { - char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1]; - - xsprintf(prefix, "user-"UID_FMT, getuid()); - - if (file_has_type_prefix(prefix, filename)) - return true; - } - - return false; -} - -static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { - assert(j); - assert(path); - assert(prefix); - - if (j->toplevel_fd >= 0) - return false; - - return path_startswith(path, prefix); -} - -static const char *skip_slash(const char *p) { - - if (!p) - return NULL; - - while (*p == '/') - p++; - - return p; -} - -static int add_any_file(sd_journal *j, int fd, const char *path) { - JournalFile *f = NULL; - bool close_fd = false; - int r, k; - - assert(j); - assert(fd >= 0 || path); - - if (path && ordered_hashmap_get(j->files, path)) - return 0; - - if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { - log_debug("Too many open journal files, not adding %s.", path); - r = -ETOOMANYREFS; - goto fail; - } - - if (fd < 0 && j->toplevel_fd >= 0) { - - /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, - * explicitly, since otherwise openat() ignores the first argument.) */ - - fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); - if (fd < 0) { - r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); - goto fail; - } - - close_fd = true; - } - - r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); - if (r < 0) { - if (close_fd) - safe_close(fd); - 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) { - f->close_fd = close_fd; - (void) journal_file_close(f); - goto fail; - } - - if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) - j->has_runtime_files = true; - else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) - j->has_persistent_files = true; - - log_debug("File %s added.", f->path); - - check_network(j, f->fd); - - 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) { - const char *path; - - assert(j); - assert(prefix); - assert(filename); - - if (j->no_new_files) - return 0; - - if (!file_type_wanted(j->flags, filename)) - return 0; - - path = strjoina(prefix, "/", filename); - return add_any_file(j, -1, 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 = strjoina(prefix, "/", filename); - f = ordered_hashmap_get(j->files, path); - if (!f) - return; - - remove_file_real(j, f); -} - -static void remove_file_real(sd_journal *j, JournalFile *f) { - assert(j); - assert(f); - - ordered_hashmap_remove(j->files, f->path); - - log_debug("File %s removed.", f->path); - - if (j->current_file == f) { - j->current_file = NULL; - j->current_field = 0; - } - - if (j->unique_file == f) { - /* Jump to the next unique_file or NULL if that one was last */ - j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); - j->unique_offset = 0; - if (!j->unique_file) - j->unique_file_lost = true; - } - - if (j->fields_file == f) { - j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path); - j->fields_offset = 0; - if (!j->fields_file) - j->fields_file_lost = true; - } - - (void) journal_file_close(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; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de = NULL; - Directory *m; - int r, k; - - assert(j); - assert(prefix); - - /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch - * and reenumerates directory contents */ - - if (dirname) - path = strjoin(prefix, "/", dirname, NULL); - else - path = strdup(prefix); - if (!path) { - r = -ENOMEM; - goto fail; - } - - log_debug("Considering directory %s.", path); - - /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ - if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && - !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) - return 0; - - - if (j->toplevel_fd < 0) - d = opendir(path); - else - /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is - * relative, by dropping the initial slash */ - d = xopendirat(j->toplevel_fd, skip_slash(path), 0); - if (!d) { - 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) { - r = -ENOMEM; - goto fail; - } - - m->is_root = false; - m->path = path; - - if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - free(m); - r = -ENOMEM; - goto fail; - } - - path = NULL; /* avoid freeing in cleanup */ - j->current_invalidate_counter++; - - log_debug("Directory %s added.", m->path); - - } else if (m->is_root) - return 0; - - if (m->wd <= 0 && j->inotify_fd >= 0) { - /* Watch this directory, if it not being watched yet. */ - - m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| - IN_ONLYDIR); - - if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) - inotify_rm_watch(j->inotify_fd, m->wd); - } - - FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { - - if (dirent_is_file_with_suffix(de, ".journal") || - 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 ?: prefix); - if (k < 0) - return k; - - return r; -} - -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, k; - - assert(j); - - /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we - * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially - * populate the set, as well as to update it later. */ - - if (p) { - /* If there's a path specified, use it. */ - - if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && - !path_has_prefix(j, p, "/run")) - return -EINVAL; - - if (j->prefix) - p = strjoina(j->prefix, p); - - if (j->toplevel_fd < 0) - d = opendir(p); - else - d = xopendirat(j->toplevel_fd, skip_slash(p), 0); - - if (!d) { - if (errno == ENOENT && missing_ok) - return 0; - - r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); - goto fail; - } - } else { - int dfd; - - /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since - * opendir() will take possession of the fd, and close it, which we don't want. */ - - p = "."; /* store this as "." in the directories hashmap */ - - dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); - if (dfd < 0) { - r = -errno; - goto fail; - } - - d = fdopendir(dfd); - if (!d) { - r = -errno; - safe_close(dfd); - goto fail; - } - - rewinddir(d); - } - - m = hashmap_get(j->directories_by_path, p); - if (!m) { - m = new0(Directory, 1); - if (!m) { - r = -ENOMEM; - goto fail; - } - - m->is_root = true; - - m->path = strdup(p); - if (!m->path) { - free(m); - r = -ENOMEM; - goto fail; - } - - if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - free(m->path); - free(m); - r = -ENOMEM; - goto fail; - } - - j->current_invalidate_counter++; - - log_debug("Root directory %s added.", m->path); - - } else if (!m->is_root) - return 0; - - if (m->wd <= 0 && j->inotify_fd >= 0) { - - m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_ONLYDIR); - - if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) - inotify_rm_watch(j->inotify_fd, m->wd); - } - - if (j->no_new_files) - return 0; - - FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { - sd_id128_t id; - - if (dirent_is_file_with_suffix(de, ".journal") || - 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) { - assert(j); - - if (d->wd > 0) { - hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd)); - - if (j->inotify_fd >= 0) - inotify_rm_watch(j->inotify_fd, d->wd); - } - - hashmap_remove(j->directories_by_path, d->path); - - if (d->is_root) - log_debug("Root directory %s removed.", d->path); - else - log_debug("Directory %s removed.", d->path); - - free(d->path); - free(d); -} - -static int add_search_paths(sd_journal *j) { - - static const char search_paths[] = - "/run/log/journal\0" - "/var/log/journal\0"; - const char *p; - - assert(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) - (void) add_root_directory(j, p, true); - - return 0; -} - -static int add_current_paths(sd_journal *j) { - Iterator i; - JournalFile *f; - - assert(j); - assert(j->no_new_files); - - /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we - * treat them as fatal. */ - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - _cleanup_free_ char *dir; - int r; - - dir = dirname_malloc(f->path); - if (!dir) - return -ENOMEM; - - r = add_directory(j, dir, NULL); - if (r < 0) - return r; - } - - return 0; -} - -static int allocate_inotify(sd_journal *j) { - assert(j); - - if (j->inotify_fd < 0) { - j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (j->inotify_fd < 0) - return -errno; - } - - return hashmap_ensure_allocated(&j->directories_by_wd, NULL); -} - -static sd_journal *journal_new(int flags, const char *path) { - sd_journal *j; - - j = new0(sd_journal, 1); - if (!j) - return NULL; - - j->original_pid = getpid(); - j->toplevel_fd = -1; - j->inotify_fd = -1; - j->flags = flags; - j->data_threshold = DEFAULT_DATA_THRESHOLD; - - if (path) { - j->path = strdup(path); - if (!j->path) - goto fail; - } - - j->files = ordered_hashmap_new(&string_hash_ops); - j->directories_by_path = hashmap_new(&string_hash_ops); - j->mmap = mmap_cache_new(); - if (!j->files || !j->directories_by_path || !j->mmap) - goto fail; - - return j; - -fail: - sd_journal_close(j); - return NULL; -} - -_public_ int sd_journal_open(sd_journal **ret, int flags) { - sd_journal *j; - int r; - - assert_return(ret, -EINVAL); - assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); - - j = journal_new(flags, NULL); - if (!j) - return -ENOMEM; - - r = add_search_paths(j); - if (r < 0) - goto fail; - - *ret = j; - return 0; - -fail: - sd_journal_close(j); - - return r; -} - -_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { - _cleanup_free_ char *root = NULL, *class = NULL; - sd_journal *j; - char *p; - int r; - - /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in - * combination with sd_journal_open_directory_fd(). */ - - assert_return(machine, -EINVAL); - assert_return(ret, -EINVAL); - assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); - assert_return(machine_name_is_valid(machine), -EINVAL); - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!root) - return -ENODATA; - - if (!streq_ptr(class, "container")) - return -EIO; - - j = journal_new(flags, NULL); - if (!j) - return -ENOMEM; - - j->prefix = root; - root = NULL; - - r = add_search_paths(j); - if (r < 0) - goto fail; - - *ret = j; - return 0; - -fail: - sd_journal_close(j); - return r; -} - -_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { - sd_journal *j; - int r; - - assert_return(ret, -EINVAL); - assert_return(path, -EINVAL); - assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); - - j = journal_new(flags, path); - if (!j) - return -ENOMEM; - - if (flags & SD_JOURNAL_OS_ROOT) - r = add_search_paths(j); - else - r = add_root_directory(j, path, false); - if (r < 0) - goto fail; - - *ret = j; - return 0; - -fail: - sd_journal_close(j); - return r; -} - -_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { - sd_journal *j; - const char **path; - int r; - - assert_return(ret, -EINVAL); - assert_return(flags == 0, -EINVAL); - - j = journal_new(flags, NULL); - if (!j) - return -ENOMEM; - - STRV_FOREACH(path, paths) { - r = add_any_file(j, -1, *path); - if (r < 0) - goto fail; - } - - j->no_new_files = true; - - *ret = j; - return 0; - -fail: - sd_journal_close(j); - return r; -} - -_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { - sd_journal *j; - struct stat st; - int r; - - assert_return(ret, -EINVAL); - assert_return(fd >= 0, -EBADF); - assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -EBADFD; - - j = journal_new(flags, NULL); - if (!j) - return -ENOMEM; - - j->toplevel_fd = fd; - - if (flags & SD_JOURNAL_OS_ROOT) - r = add_search_paths(j); - else - r = add_root_directory(j, NULL, false); - if (r < 0) - goto fail; - - *ret = j; - return 0; - -fail: - sd_journal_close(j); - return r; -} - -_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { - Iterator iterator; - JournalFile *f; - sd_journal *j; - unsigned i; - int r; - - assert_return(ret, -EINVAL); - assert_return(n_fds > 0, -EBADF); - assert_return(flags == 0, -EINVAL); - - j = journal_new(flags, NULL); - if (!j) - return -ENOMEM; - - for (i = 0; i < n_fds; i++) { - struct stat st; - - if (fds[i] < 0) { - r = -EBADF; - goto fail; - } - - if (fstat(fds[i], &st) < 0) { - r = -errno; - goto fail; - } - - if (!S_ISREG(st.st_mode)) { - r = -EBADFD; - goto fail; - } - - r = add_any_file(j, fds[i], NULL); - if (r < 0) - goto fail; - } - - j->no_new_files = true; - j->no_inotify = true; - - *ret = j; - return 0; - -fail: - /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they - * remain open */ - ORDERED_HASHMAP_FOREACH(f, j->files, iterator) - f->close_fd = false; - - sd_journal_close(j); - return r; -} - -_public_ void sd_journal_close(sd_journal *j) { - Directory *d; - JournalFile *f; - char *p; - - if (!j) - return; - - sd_journal_flush_matches(j); - - while ((f = ordered_hashmap_steal_first(j->files))) - (void) journal_file_close(f); - - ordered_hashmap_free(j->files); - - while ((d = hashmap_first(j->directories_by_path))) - remove_directory(j, d); - - while ((d = hashmap_first(j->directories_by_wd))) - remove_directory(j, d); - - hashmap_free(j->directories_by_path); - hashmap_free(j->directories_by_wd); - - safe_close(j->inotify_fd); - - if (j->mmap) { - log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap)); - 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); - free(j->fields_buffer); - free(j); -} - -_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { - Object *o; - JournalFile *f; - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(ret, -EINVAL); - - f = j->current_file; - if (!f) - return -EADDRNOTAVAIL; - - if (f->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) - return r; - - *ret = le64toh(o->entry.realtime); - return 0; -} - -_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { - Object *o; - JournalFile *f; - int r; - sd_id128_t id; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - f = j->current_file; - if (!f) - return -EADDRNOTAVAIL; - - if (f->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) - return r; - - if (ret_boot_id) - *ret_boot_id = o->entry.boot_id; - else { - r = sd_id128_get_boot(&id); - if (r < 0) - return r; - - if (!sd_id128_equal(id, o->entry.boot_id)) - return -ESTALE; - } - - if (ret) - *ret = le64toh(o->entry.monotonic); - - return 0; -} - -static bool field_is_valid(const char *field) { - const char *p; - - assert(field); - - if (isempty(field)) - return false; - - if (startswith(field, "__")) - return false; - - for (p = field; *p; p++) { - - if (*p == '_') - continue; - - if (*p >= 'A' && *p <= 'Z') - continue; - - if (*p >= '0' && *p <= '9') - continue; - - return false; - } - - return true; -} - -_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { - JournalFile *f; - uint64_t i, n; - size_t field_length; - int r; - Object *o; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(field, -EINVAL); - assert_return(data, -EINVAL); - assert_return(size, -EINVAL); - assert_return(field_is_valid(field), -EINVAL); - - f = j->current_file; - if (!f) - return -EADDRNOTAVAIL; - - if (f->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) - return r; - - field_length = strlen(field); - - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) { - uint64_t p, l; - le64_t le_hash; - size_t t; - int compression; - - p = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[i].hash; - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - if (le_hash != o->data.hash) - return -EBADMSG; - - l = le64toh(o->object.size) - offsetof(Object, data.payload); - - compression = o->object.flags & OBJECT_COMPRESSION_MASK; - if (compression) { -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - r = decompress_startswith(compression, - o->data.payload, l, - &f->compress_buffer, &f->compress_buffer_size, - field, field_length, '='); - if (r < 0) - log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m", - object_compressed_to_string(compression), l, p); - else if (r > 0) { - - size_t rsize; - - r = decompress_blob(compression, - o->data.payload, l, - &f->compress_buffer, &f->compress_buffer_size, &rsize, - j->data_threshold); - if (r < 0) - return r; - - *data = f->compress_buffer; - *size = (size_t) rsize; - - return 0; - } -#else - return -EPROTONOSUPPORT; -#endif - } else if (l >= field_length+1 && - memcmp(o->data.payload, field, field_length) == 0 && - o->data.payload[field_length] == '=') { - - t = (size_t) l; - - if ((uint64_t) t != l) - return -E2BIG; - - *data = o->data.payload; - *size = t; - - return 0; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) - return r; - } - - return -ENOENT; -} - -static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) { - size_t t; - uint64_t l; - int compression; - - l = le64toh(o->object.size) - offsetof(Object, data.payload); - t = (size_t) l; - - /* We can't read objects larger than 4G on a 32bit machine */ - if ((uint64_t) t != l) - return -E2BIG; - - compression = o->object.flags & OBJECT_COMPRESSION_MASK; - if (compression) { -#if defined(HAVE_XZ) || defined(HAVE_LZ4) - size_t rsize; - int r; - - r = decompress_blob(compression, - o->data.payload, l, &f->compress_buffer, - &f->compress_buffer_size, &rsize, j->data_threshold); - if (r < 0) - return r; - - *data = f->compress_buffer; - *size = (size_t) rsize; -#else - return -EPROTONOSUPPORT; -#endif - } else { - *data = o->data.payload; - *size = t; - } - - return 0; -} - -_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { - JournalFile *f; - uint64_t p, n; - le64_t le_hash; - int r; - Object *o; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(data, -EINVAL); - assert_return(size, -EINVAL); - - f = j->current_file; - if (!f) - return -EADDRNOTAVAIL; - - if (f->current_offset <= 0) - return -EADDRNOTAVAIL; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - if (r < 0) - return r; - - n = journal_file_entry_n_items(o); - if (j->current_field >= n) - return 0; - - p = le64toh(o->entry.items[j->current_field].object_offset); - le_hash = o->entry.items[j->current_field].hash; - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - - if (le_hash != o->data.hash) - return -EBADMSG; - - r = return_data(j, f, o, data, size); - if (r < 0) - return r; - - j->current_field++; - - return 1; -} - -_public_ void sd_journal_restart_data(sd_journal *j) { - if (!j) - return; - - j->current_field = 0; -} - -_public_ int sd_journal_get_fd(sd_journal *j) { - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - if (j->no_inotify) - return -EMEDIUMTYPE; - - if (j->inotify_fd >= 0) - return j->inotify_fd; - - r = allocate_inotify(j); - if (r < 0) - return r; - - log_debug("Reiterating files to get inotify watches established"); - - /* Iterate through all dirs again, to add them to the - * inotify */ - if (j->no_new_files) - r = add_current_paths(j); - else if (j->toplevel_fd >= 0) - r = add_root_directory(j, NULL, false); - else if (j->path) - r = add_root_directory(j, j->path, true); - else - r = add_search_paths(j); - if (r < 0) - return r; - - return j->inotify_fd; -} - -_public_ int sd_journal_get_events(sd_journal *j) { - int fd; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - fd = sd_journal_get_fd(j); - if (fd < 0) - return fd; - - return POLLIN; -} - -_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { - int fd; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(timeout_usec, -EINVAL); - - fd = sd_journal_get_fd(j); - if (fd < 0) - return fd; - - if (!j->on_network) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - /* If we are on the network we need to regularly check for - * changes manually */ - - *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC; - return 1; -} - -static void process_inotify_event(sd_journal *j, struct inotify_event *e) { - Directory *d; - - assert(j); - assert(e); - - /* Is this a subdirectory we watch? */ - d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd)); - if (d) { - sd_id128_t id; - - if (!(e->mask & IN_ISDIR) && e->len > 0 && - (endswith(e->name, ".journal") || - endswith(e->name, ".journal~"))) { - - /* Event for a journal file */ - - 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) { - - /* Event for a subdirectory */ - - if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) - remove_directory(j, d); - - } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { - - /* Event for root directory */ - - if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) - (void) add_directory(j, d->path, e->name); - } - - return; - } - - if (e->mask & IN_IGNORED) - return; - - log_debug("Unknown inotify event."); -} - -static int determine_change(sd_journal *j) { - bool b; - - assert(j); - - b = j->current_invalidate_counter != j->last_invalidate_counter; - j->last_invalidate_counter = j->current_invalidate_counter; - - return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND; -} - -_public_ int sd_journal_process(sd_journal *j) { - bool got_something = false; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - j->last_process_usec = now(CLOCK_MONOTONIC); - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - l = read(j->inotify_fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return got_something ? determine_change(j) : SD_JOURNAL_NOP; - - return -errno; - } - - got_something = true; - - FOREACH_INOTIFY_EVENT(e, buffer, l) - process_inotify_event(j, e); - } -} - -_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { - int r; - uint64_t t; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - if (j->inotify_fd < 0) { - - /* This is the first invocation, hence create the - * inotify watch */ - r = sd_journal_get_fd(j); - if (r < 0) - return r; - - /* The journal might have changed since the context - * object was created and we weren't watching before, - * hence don't wait for anything, and return - * immediately. */ - return determine_change(j); - } - - r = sd_journal_get_timeout(j, &t); - if (r < 0) - return r; - - if (t != (uint64_t) -1) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - t = t > n ? t - n : 0; - - if (timeout_usec == (uint64_t) -1 || timeout_usec > t) - timeout_usec = t; - } - - do { - r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec); - } while (r == -EINTR); - - if (r < 0) - return r; - - return sd_journal_process(j); -} - -_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) { - Iterator i; - JournalFile *f; - bool first = true; - uint64_t fmin = 0, tmax = 0; - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(from || to, -EINVAL); - assert_return(from != to, -EINVAL); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - usec_t fr, t; - - r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - if (r == 0) - continue; - - if (first) { - fmin = fr; - tmax = t; - first = false; - } else { - fmin = MIN(fr, fmin); - tmax = MAX(t, tmax); - } - } - - if (from) - *from = fmin; - if (to) - *to = tmax; - - return first ? 0 : 1; -} - -_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) { - Iterator i; - JournalFile *f; - bool found = false; - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(from || to, -EINVAL); - assert_return(from != to, -EINVAL); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - usec_t fr, t; - - r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - if (r == 0) - continue; - - if (found) { - if (from) - *from = MIN(fr, *from); - if (to) - *to = MAX(t, *to); - } else { - if (from) - *from = fr; - if (to) - *to = t; - found = true; - } - } - - return found; -} - -void journal_print_header(sd_journal *j) { - Iterator i; - JournalFile *f; - bool newline = false; - - assert(j); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - if (newline) - putchar('\n'); - else - newline = true; - - journal_file_print_header(f); - } -} - -_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { - Iterator i; - JournalFile *f; - uint64_t sum = 0; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(bytes, -EINVAL); - - ORDERED_HASHMAP_FOREACH(f, j->files, i) { - struct stat st; - - if (fstat(f->fd, &st) < 0) - return -errno; - - sum += (uint64_t) st.st_blocks * 512ULL; - } - - *bytes = sum; - return 0; -} - -_public_ int sd_journal_query_unique(sd_journal *j, const char *field) { - char *f; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(!isempty(field), -EINVAL); - assert_return(field_is_valid(field), -EINVAL); - - f = strdup(field); - if (!f) - return -ENOMEM; - - free(j->unique_field); - j->unique_field = f; - j->unique_file = NULL; - j->unique_offset = 0; - j->unique_file_lost = false; - - return 0; -} - -_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { - size_t k; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(data, -EINVAL); - assert_return(l, -EINVAL); - assert_return(j->unique_field, -EINVAL); - - k = strlen(j->unique_field); - - if (!j->unique_file) { - if (j->unique_file_lost) - return 0; - - j->unique_file = ordered_hashmap_first(j->files); - if (!j->unique_file) - return 0; - - j->unique_offset = 0; - } - - for (;;) { - JournalFile *of; - Iterator i; - Object *o; - const void *odata; - size_t ol; - bool found; - int r; - - /* Proceed to next data object in the field's linked list */ - if (j->unique_offset == 0) { - r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL); - if (r < 0) - return r; - - j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0; - } else { - r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o); - if (r < 0) - return r; - - j->unique_offset = le64toh(o->data.next_field_offset); - } - - /* We reached the end of the list? Then start again, with the next file */ - if (j->unique_offset == 0) { - j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); - if (!j->unique_file) - return 0; - - continue; - } - - /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED - * instead, so that we can look at this data object at the same - * time as one on another file */ - r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o); - if (r < 0) - return r; - - /* Let's do the type check by hand, since we used 0 context above. */ - if (o->object.type != OBJECT_DATA) { - log_debug("%s:offset " OFSfmt ": object has type %d, expected %d", - j->unique_file->path, j->unique_offset, - o->object.type, OBJECT_DATA); - return -EBADMSG; - } - - r = return_data(j, j->unique_file, o, &odata, &ol); - if (r < 0) - return r; - - /* Check if we have at least the field name and "=". */ - if (ol <= k) { - log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu", - j->unique_file->path, j->unique_offset, - ol, k + 1); - return -EBADMSG; - } - - if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') { - log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"", - j->unique_file->path, j->unique_offset, - j->unique_field); - return -EBADMSG; - } - - /* OK, now let's see if we already returned this data - * object by checking if it exists in the earlier - * traversed files. */ - found = false; - ORDERED_HASHMAP_FOREACH(of, j->files, i) { - if (of == j->unique_file) - break; - - /* Skip this file it didn't have any fields indexed */ - if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) - continue; - - r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL); - if (r < 0) - return r; - if (r > 0) { - found = true; - break; - } - } - - if (found) - continue; - - r = return_data(j, j->unique_file, o, data, l); - if (r < 0) - return r; - - return 1; - } -} - -_public_ void sd_journal_restart_unique(sd_journal *j) { - if (!j) - return; - - j->unique_file = NULL; - j->unique_offset = 0; - j->unique_file_lost = false; -} - -_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) { - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(field, -EINVAL); - - if (!j->fields_file) { - if (j->fields_file_lost) - return 0; - - j->fields_file = ordered_hashmap_first(j->files); - if (!j->fields_file) - return 0; - - j->fields_hash_table_index = 0; - j->fields_offset = 0; - } - - for (;;) { - JournalFile *f, *of; - Iterator i; - uint64_t m; - Object *o; - size_t sz; - bool found; - - f = j->fields_file; - - if (j->fields_offset == 0) { - bool eof = false; - - /* We are not yet positioned at any field. Let's pick the first one */ - r = journal_file_map_field_hash_table(f); - if (r < 0) - return r; - - m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); - for (;;) { - if (j->fields_hash_table_index >= m) { - /* Reached the end of the hash table, go to the next file. */ - eof = true; - break; - } - - j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset); - - if (j->fields_offset != 0) - break; - - /* Empty hash table bucket, go to next one */ - j->fields_hash_table_index++; - } - - if (eof) { - /* Proceed with next file */ - j->fields_file = ordered_hashmap_next(j->files, f->path); - if (!j->fields_file) { - *field = NULL; - return 0; - } - - j->fields_offset = 0; - j->fields_hash_table_index = 0; - continue; - } - - } else { - /* We are already positioned at a field. If so, let's figure out the next field from it */ - - r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o); - if (r < 0) - return r; - - j->fields_offset = le64toh(o->field.next_hash_offset); - if (j->fields_offset == 0) { - /* Reached the end of the hash table chain */ - j->fields_hash_table_index++; - continue; - } - } - - /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */ - r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o); - if (r < 0) - return r; - - /* Because we used OBJECT_UNUSED above, we need to do our type check manually */ - if (o->object.type != OBJECT_FIELD) { - log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD); - return -EBADMSG; - } - - sz = le64toh(o->object.size) - offsetof(Object, field.payload); - - /* Let's see if we already returned this field name before. */ - found = false; - ORDERED_HASHMAP_FOREACH(of, j->files, i) { - if (of == f) - break; - - /* Skip this file it didn't have any fields indexed */ - if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) - continue; - - r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL); - if (r < 0) - return r; - if (r > 0) { - found = true; - break; - } - } - - if (found) - continue; - - /* Check if this is really a valid string containing no NUL byte */ - if (memchr(o->field.payload, 0, sz)) - return -EBADMSG; - - if (sz > j->data_threshold) - sz = j->data_threshold; - - if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1)) - return -ENOMEM; - - memcpy(j->fields_buffer, o->field.payload, sz); - j->fields_buffer[sz] = 0; - - if (!field_is_valid(j->fields_buffer)) - return -EBADMSG; - - *field = j->fields_buffer; - return 1; - } -} - -_public_ void sd_journal_restart_fields(sd_journal *j) { - if (!j) - return; - - j->fields_file = NULL; - j->fields_hash_table_index = 0; - j->fields_offset = 0; - j->fields_file_lost = false; -} - -_public_ int sd_journal_reliable_fd(sd_journal *j) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - return !j->on_network; -} - -static char *lookup_field(const char *field, void *userdata) { - sd_journal *j = userdata; - const void *data; - size_t size, d; - int r; - - assert(field); - assert(j); - - r = sd_journal_get_data(j, field, &data, &size); - if (r < 0 || - size > REPLACE_VAR_MAX) - return strdup(field); - - d = strlen(field) + 1; - - return strndup((const char*) data + d, size - d); -} - -_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { - const void *data; - size_t size; - sd_id128_t id; - _cleanup_free_ char *text = NULL, *cid = NULL; - char *t; - int r; - - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(ret, -EINVAL); - - r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size); - if (r < 0) - return r; - - cid = strndup((const char*) data + 11, size - 11); - if (!cid) - return -ENOMEM; - - r = sd_id128_from_string(cid, &id); - if (r < 0) - return r; - - r = catalog_get(CATALOG_DATABASE, id, &text); - if (r < 0) - return r; - - t = replace_var(text, lookup_field, j); - if (!t) - return -ENOMEM; - - *ret = t; - return 0; -} - -_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) { - assert_return(ret, -EINVAL); - - return catalog_get(CATALOG_DATABASE, id, ret); -} - -_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - - j->data_threshold = sz; - return 0; -} - -_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) { - assert_return(j, -EINVAL); - assert_return(!journal_pid_changed(j), -ECHILD); - assert_return(sz, -EINVAL); - - *sz = j->data_threshold; - return 0; -} - -_public_ int sd_journal_has_runtime_files(sd_journal *j) { - assert_return(j, -EINVAL); - - return j->has_runtime_files; -} - -_public_ int sd_journal_has_persistent_files(sd_journal *j) { - assert_return(j, -EINVAL); - - return j->has_persistent_files; -} diff --git a/src/journal/test-audit-type.c b/src/journal/test-audit-type.c deleted file mode 100644 index 88a2e6d9d9..0000000000 --- a/src/journal/test-audit-type.c +++ /dev/null @@ -1,42 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "audit-type.h" - -static void print_audit_label(int i) { - const char *name; - - name = audit_type_name_alloca(i); - /* This is a separate function only because of alloca */ - printf("%i → %s → %s\n", i, audit_type_to_string(i), name); -} - -static void test_audit_type(void) { - int i; - - for (i = 0; i <= AUDIT_KERNEL; i++) - print_audit_label(i); -} - -int main(int argc, char **argv) { - test_audit_type(); -} diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c deleted file mode 100644 index 898c876450..0000000000 --- a/src/journal/test-catalog.c +++ /dev/null @@ -1,264 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "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, - NULL, -}; - -static const char *no_catalog_dirs[] = { - "/bin/hopefully/with/no/catalog", - NULL -}; - -static Hashmap * test_import(const char* contents, ssize_t size, int code) { - int r; - char name[] = "/tmp/test-catalog.XXXXXX"; - _cleanup_close_ int fd; - Hashmap *h; - - if (size < 0) - size = strlen(contents); - - assert_se(h = hashmap_new(&catalog_hash_ops)); - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - assert_se(write(fd, contents, size) == size); - - r = catalog_import_file(h, name); - assert_se(r == code); - - unlink(name); - - return h; -} - -static void test_catalog_import_invalid(void) { - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - - h = test_import("xxx", -1, -EINVAL); - assert_se(hashmap_isempty(h)); -} - -static void test_catalog_import_badid(void) { - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - const char *input = -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededede\n" \ -"Subject: message\n" \ -"\n" \ -"payload\n"; - h = test_import(input, -1, -EINVAL); -} - -static void test_catalog_import_one(void) { - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - char *payload; - Iterator j; - - const char *input = -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ -"Subject: message\n" \ -"\n" \ -"payload\n"; - const char *expect = -"Subject: message\n" \ -"\n" \ -"payload\n"; - - h = test_import(input, -1, 0); - assert_se(hashmap_size(h) == 1); - - HASHMAP_FOREACH(payload, h, j) { - printf("expect: %s\n", expect); - printf("actual: %s\n", payload); - assert_se(streq(expect, payload)); - } -} - -static void test_catalog_import_merge(void) { - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - char *payload; - Iterator j; - - const char *input = -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ -"Subject: message\n" \ -"Defined-By: me\n" \ -"\n" \ -"payload\n" \ -"\n" \ -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ -"Subject: override subject\n" \ -"X-Header: hello\n" \ -"\n" \ -"override payload\n"; - - const char *combined = -"Subject: override subject\n" \ -"X-Header: hello\n" \ -"Subject: message\n" \ -"Defined-By: me\n" \ -"\n" \ -"override payload\n"; - - h = test_import(input, -1, 0); - assert_se(hashmap_size(h) == 1); - - HASHMAP_FOREACH(payload, h, j) { - assert_se(streq(combined, payload)); - } -} - -static void test_catalog_import_merge_no_body(void) { - _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; - char *payload; - Iterator j; - - const char *input = -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ -"Subject: message\n" \ -"Defined-By: me\n" \ -"\n" \ -"payload\n" \ -"\n" \ -"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ -"Subject: override subject\n" \ -"X-Header: hello\n" \ -"\n"; - - const char *combined = -"Subject: override subject\n" \ -"X-Header: hello\n" \ -"Subject: message\n" \ -"Defined-By: me\n" \ -"\n" \ -"payload\n"; - - h = test_import(input, -1, 0); - assert_se(hashmap_size(h) == 1); - - HASHMAP_FOREACH(payload, h, j) { - assert_se(streq(combined, payload)); - } -} - -static const char* database = NULL; - -static void test_catalog_update(void) { - static char name[] = "/tmp/test-catalog.XXXXXX"; - int r; - - r = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(r >= 0); - - database = name; - - /* Test what happens if there are no files. */ - r = catalog_update(database, NULL, NULL); - assert_se(r >= 0); - - /* Test what happens if there are no files in the directory. */ - r = catalog_update(database, NULL, no_catalog_dirs); - assert_se(r >= 0); - - /* Make sure that we at least have some files loaded or the - catalog_list below will fail. */ - r = catalog_update(database, NULL, catalog_dirs); - assert_se(r >= 0); -} - -static void test_catalog_file_lang(void) { - _cleanup_free_ char *lang = NULL, *lang2 = NULL, *lang3 = NULL, *lang4 = NULL; - - assert_se(catalog_file_lang("systemd.de_DE.catalog", &lang) == 1); - assert_se(streq(lang, "de_DE")); - - assert_se(catalog_file_lang("systemd..catalog", &lang2) == 0); - assert_se(lang2 == NULL); - - assert_se(catalog_file_lang("systemd.fr.catalog", &lang2) == 1); - assert_se(streq(lang2, "fr")); - - assert_se(catalog_file_lang("systemd.fr.catalog.gz", &lang3) == 0); - assert_se(lang3 == NULL); - - assert_se(catalog_file_lang("systemd.01234567890123456789012345678901.catalog", &lang3) == 0); - assert_se(lang3 == NULL); - - assert_se(catalog_file_lang("systemd.0123456789012345678901234567890.catalog", &lang3) == 1); - assert_se(streq(lang3, "0123456789012345678901234567890")); - - assert_se(catalog_file_lang("/x/y/systemd.catalog", &lang4) == 0); - assert_se(lang4 == NULL); - - assert_se(catalog_file_lang("/x/y/systemd.ru_RU.catalog", &lang4) == 1); - assert_se(streq(lang4, "ru_RU")); -} - -int main(int argc, char *argv[]) { - _cleanup_free_ char *text = NULL; - int r; - - setlocale(LC_ALL, "de_DE.UTF-8"); - - log_parse_environment(); - log_open(); - - test_catalog_file_lang(); - - test_catalog_import_invalid(); - test_catalog_import_badid(); - test_catalog_import_one(); - test_catalog_import_merge(); - test_catalog_import_merge_no_body(); - - test_catalog_update(); - - r = catalog_list(stdout, database, true); - assert_se(r >= 0); - - r = catalog_list(stdout, database, false); - assert_se(r >= 0); - - assert_se(catalog_get(database, SD_MESSAGE_COREDUMP, &text) >= 0); - printf(">>>%s<<<\n", text); - - if (database) - unlink(database); - - return 0; -} diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c deleted file mode 100644 index 6f6d71435d..0000000000 --- a/src/journal/test-compress-benchmark.c +++ /dev/null @@ -1,180 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "compress.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_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, const char *type) { - char *buf; - size_t i; - - buf = malloc(count); - assert_se(buf); - - 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, const char* type, - compress_t compress, decompress_t decompress) { - usec_t n, n2 = 0; - float dt; - - _cleanup_free_ char *text, *buf; - _cleanup_free_ void *buf2 = NULL; - size_t buf2_allocated = 0; - size_t skipped = 0, compressed = 0, total = 0; - - text = make_buf(MAX_SIZE, type); - buf = calloc(MAX_SIZE + 1, 1); - assert_se(text && buf); - - n = now(CLOCK_MONOTONIC); - - for (size_t i = 0; i <= MAX_SIZE; i++) { - size_t j = 0, k = 0, size; - int r; - - size = permute(i); - if (size == 0) - continue; - - 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[size] == 0); - if (r != 0) { - skipped += size; - continue; - } - - assert_se(j > 0); - 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 == size); - - assert_se(memcmp(text, buf2, size) == 0); - - total += size; - compressed += j; - - n2 = now(CLOCK_MONOTONIC); - if (n2 - n > arg_duration) - break; - } - - dt = (n2-n) / 1e6; - - log_info("%s/%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), " - "mean compresion %.2f%%, skipped %zu bytes", - 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_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_atozu(argv[2], &arg_start); - else - arg_start = getpid(); - - NULSTR_FOREACH(i, "zeros\0simple\0random\0") { -#ifdef HAVE_XZ - test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz); -#endif -#ifdef HAVE_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 deleted file mode 100644 index 68c9a4d76c..0000000000 --- a/src/journal/test-compress.c +++ /dev/null @@ -1,308 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2014 Ronny Chevalier - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_LZ4 -#include -#endif - -#include "alloc-util.h" -#include "compress.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 -#else -# define XZ_OK -EPROTONOSUPPORT -#endif - -#ifdef HAVE_LZ4 -# define LZ4_OK 0 -#else -# define LZ4_OK -EPROTONOSUPPORT -#endif - -typedef int (compress_blob_t)(const void *src, uint64_t src_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); -typedef int (decompress_sw_t)(const void *src, uint64_t src_size, - void **buffer, size_t *buffer_size, - const void *prefix, size_t prefix_len, - uint8_t extra); - -typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes); -typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); - -static void test_compress_decompress(int compression, - compress_blob_t compress, - decompress_blob_t decompress, - const char *data, - size_t data_len, - bool may_fail) { - char compressed[512]; - 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, sizeof(compressed), &csize); - if (r == -ENOBUFS) { - log_info_errno(r, "compression failed: %m"); - assert_se(may_fail); - } else { - assert_se(r == 0); - r = decompress(compressed, csize, - (void **) &decompressed, &usize, &csize, 0); - assert_se(r == 0); - assert_se(decompressed); - assert_se(memcmp(decompressed, data, data_len) == 0); - } - - r = decompress("garbage", 7, - (void **) &decompressed, &usize, &csize, 0); - assert_se(r < 0); - - /* make sure to have the minimal lz4 compressed size */ - r = decompress("00000000\1g", 9, - (void **) &decompressed, &usize, &csize, 0); - assert_se(r < 0); - - r = decompress("\100000000g", 9, - (void **) &decompressed, &usize, &csize, 0); - assert_se(r < 0); - - memzero(decompressed, usize); -} - -static void test_decompress_startswith(int compression, - compress_blob_t compress, - decompress_sw_t decompress_sw, - const char *data, - size_t data_len, - bool may_fail) { - - 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 %.20s text*/", - object_compressed_to_string(compression), data); - -#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); - - compressed = compressed2 = malloc(BUFSIZE_2); - assert_se(compressed2); - r = compress(data, data_len, compressed, BUFSIZE_2, &csize); - assert(r == 0); - } - assert_se(r == 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, - const char* cat, - compress_stream_t compress, - decompress_stream_t decompress, - const char *srcfile) { - - _cleanup_close_ int src = -1, dst = -1, dst2 = -1; - char pattern[] = "/tmp/systemd-test.compressed.XXXXXX", - pattern2[] = "/tmp/systemd-test.compressed.XXXXXX"; - int r; - _cleanup_free_ char *cmd = NULL, *cmd2; - struct stat st = {}; - - log_debug("/* testing %s compression */", - object_compressed_to_string(compression)); - - log_debug("/* create source from %s */", srcfile); - - assert_se((src = open(srcfile, O_RDONLY|O_CLOEXEC)) >= 0); - - log_debug("/* test compression */"); - - assert_se((dst = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC)) >= 0); - - assert_se(compress(src, dst, -1) == 0); - - if (cat) { - assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0); - assert_se(system(cmd) == 0); - } - - log_debug("/* test decompression */"); - - assert_se((dst2 = mkostemp_safe(pattern2, O_RDWR|O_CLOEXEC)) >= 0); - - assert_se(stat(srcfile, &st) == 0); - - assert_se(lseek(dst, 0, SEEK_SET) == 0); - r = decompress(dst, dst2, st.st_size); - assert_se(r == 0); - - assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0); - assert_se(system(cmd2) == 0); - - log_debug("/* test faulty decompression */"); - - assert_se(lseek(dst, 1, SEEK_SET) == 1); - r = decompress(dst, dst2, st.st_size); - assert_se(r == -EBADMSG || r == 0); - - assert_se(lseek(dst, 0, SEEK_SET) == 0); - assert_se(lseek(dst2, 0, SEEK_SET) == 0); - r = decompress(dst, dst2, st.st_size - 1); - assert_se(r == -EFBIG); - - assert_se(unlink(pattern) == 0); - 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" - "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"; - - 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); - -#ifdef HAVE_XZ - test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz, - 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 - log_info("/* XZ test skipped */"); -#endif - -#ifdef HAVE_LZ4 - test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4, - 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); - - 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 - - return 0; -} diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c deleted file mode 100644 index 354c2c3c00..0000000000 --- a/src/journal/test-journal-enum.c +++ /dev/null @@ -1,53 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-journal.h" - -#include "journal-internal.h" -#include "log.h" -#include "macro.h" - -int main(int argc, char *argv[]) { - unsigned n = 0; - _cleanup_(sd_journal_closep) sd_journal*j = NULL; - - log_set_max_level(LOG_DEBUG); - - assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0); - - assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0); - assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0); - - SD_JOURNAL_FOREACH_BACKWARDS(j) { - const void *d; - size_t l; - - assert_se(sd_journal_get_data(j, "MESSAGE", &d, &l) >= 0); - - printf("%.*s\n", (int) l, (char*) d); - - n++; - if (n >= 10) - break; - } - - return 0; -} diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c deleted file mode 100644 index ba8b20b228..0000000000 --- a/src/journal/test-journal-flush.c +++ /dev/null @@ -1,75 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-journal.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; - char dn[] = "/var/tmp/test-journal-flush.XXXXXX"; - JournalFile *new_journal = NULL; - sd_journal *j = NULL; - unsigned n = 0; - int r; - - assert_se(mkdtemp(dn)); - fn = strappend(dn, "/test.journal"); - - r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); - assert_se(r >= 0); - - r = sd_journal_open(&j, 0); - assert_se(r >= 0); - - sd_journal_set_data_threshold(j, 0); - - SD_JOURNAL_FOREACH(j) { - Object *o; - JournalFile *f; - - f = j->current_file; - assert_se(f && f->current_offset > 0); - - r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); - assert_se(r >= 0); - - r = journal_file_copy_entry(f, new_journal, o, f->current_offset, NULL, NULL, NULL); - assert_se(r >= 0); - - n++; - if (n > 10000) - break; - } - - sd_journal_close(j); - - (void) journal_file_close(new_journal); - - unlink(fn); - assert_se(rmdir(dn) == 0); - - return 0; -} diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c deleted file mode 100644 index ef21e2d05f..0000000000 --- a/src/journal/test-journal-init.c +++ /dev/null @@ -1,64 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-journal.h" - -#include "log.h" -#include "parse-util.h" -#include "rm-rf.h" -#include "util.h" - -int main(int argc, char *argv[]) { - sd_journal *j; - int r, i, I = 100; - char t[] = "/tmp/journal-stream-XXXXXX"; - - log_set_max_level(LOG_DEBUG); - - if (argc >= 2) { - r = safe_atoi(argv[1], &I); - if (r < 0) - log_info("Could not parse loop count argument. Using default."); - } - - log_info("Running %d loops", I); - - assert_se(mkdtemp(t)); - - for (i = 0; i < I; i++) { - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); - assert_se(r == 0); - - sd_journal_close(j); - - r = sd_journal_open_directory(&j, t, 0); - assert_se(r == 0); - - sd_journal_close(j); - - j = NULL; - r = sd_journal_open_directory(&j, t, SD_JOURNAL_LOCAL_ONLY); - assert_se(r == -EINVAL); - assert_se(j == NULL); - } - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - - return 0; -} diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c deleted file mode 100644 index 5e063f4d04..0000000000 --- a/src/journal/test-journal-interleaving.c +++ /dev/null @@ -1,307 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Marius Vollmer - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-journal.h" - -#include "alloc-util.h" -#include "journal-file.h" -#include "journal-vacuum.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. - */ - -static bool arg_keep = false; - -noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) { - log_internal(LOG_CRIT, 0, file, line, func, - "'%s' failed at %s:%u (%s): %s.", - text, file, line, func, strerror(eno)); - abort(); -} - -#define assert_ret(expr) \ - do { \ - int _r_ = (expr); \ - if (_unlikely_(_r_ < 0)) \ - log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -static JournalFile *test_open(const char *name) { - JournalFile *f; - assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); - return f; -} - -static void test_close(JournalFile *f) { - (void) journal_file_close (f); -} - -static void append_number(JournalFile *f, int n, uint64_t *seqnum) { - char *p; - dual_timestamp ts; - static dual_timestamp previous_ts = {}; - struct iovec iovec[1]; - - dual_timestamp_get(&ts); - - if (ts.monotonic <= previous_ts.monotonic) - ts.monotonic = previous_ts.monotonic + 1; - - if (ts.realtime <= previous_ts.realtime) - ts.realtime = previous_ts.realtime + 1; - - previous_ts = ts; - - assert_se(asprintf(&p, "NUMBER=%d", n) >= 0); - iovec[0].iov_base = p; - iovec[0].iov_len = strlen(p); - assert_ret(journal_file_append_entry(f, &ts, iovec, 1, seqnum, NULL, NULL)); - free(p); -} - -static void test_check_number (sd_journal *j, int n) { - const void *d; - _cleanup_free_ char *k; - size_t l; - int x; - - assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l)); - assert_se(k = strndup(d, l)); - printf("%s\n", k); - - assert_se(safe_atoi(k + 7, &x) >= 0); - assert_se(n == x); -} - -static void test_check_numbers_down (sd_journal *j, int count) { - int i; - - for (i = 1; i <= count; i++) { - int r; - test_check_number(j, i); - assert_ret(r = sd_journal_next(j)); - if (i == count) - assert_se(r == 0); - else - assert_se(r == 1); - } - -} - -static void test_check_numbers_up (sd_journal *j, int count) { - for (int i = count; i >= 1; i--) { - int r; - test_check_number(j, i); - assert_ret(r = sd_journal_previous(j)); - if (i == 1) - assert_se(r == 0); - else - assert_se(r == 1); - } - -} - -static void setup_sequential(void) { - JournalFile *one, *two; - one = test_open("one.journal"); - two = test_open("two.journal"); - append_number(one, 1, NULL); - append_number(one, 2, NULL); - append_number(two, 3, NULL); - append_number(two, 4, NULL); - test_close(one); - test_close(two); -} - -static void setup_interleaved(void) { - JournalFile *one, *two; - one = test_open("one.journal"); - two = test_open("two.journal"); - append_number(one, 1, NULL); - append_number(two, 2, NULL); - append_number(one, 3, NULL); - append_number(two, 4, NULL); - test_close(one); - test_close(two); -} - -static void test_skip(void (*setup)(void)) { - char t[] = "/tmp/journal-skip-XXXXXX"; - sd_journal *j; - int r; - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - setup(); - - /* Seek to head, iterate down. - */ - assert_ret(sd_journal_open_directory(&j, t, 0)); - assert_ret(sd_journal_seek_head(j)); - assert_ret(sd_journal_next(j)); - test_check_numbers_down(j, 4); - sd_journal_close(j); - - /* Seek to tail, iterate up. - */ - assert_ret(sd_journal_open_directory(&j, t, 0)); - assert_ret(sd_journal_seek_tail(j)); - assert_ret(sd_journal_previous(j)); - test_check_numbers_up(j, 4); - sd_journal_close(j); - - /* Seek to tail, skip to head, iterate down. - */ - assert_ret(sd_journal_open_directory(&j, t, 0)); - assert_ret(sd_journal_seek_tail(j)); - assert_ret(r = sd_journal_previous_skip(j, 4)); - assert_se(r == 4); - test_check_numbers_down(j, 4); - sd_journal_close(j); - - /* Seek to head, skip to tail, iterate up. - */ - assert_ret(sd_journal_open_directory(&j, t, 0)); - assert_ret(sd_journal_seek_head(j)); - assert_ret(r = sd_journal_next_skip(j, 4)); - assert_se(r == 4); - test_check_numbers_up(j, 4); - sd_journal_close(j); - - log_info("Done..."); - - if (arg_keep) - log_info("Not removing %s", t); - else { - journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - } - - puts("------------------------------------------------------------"); -} - -static void test_sequence_numbers(void) { - - char t[] = "/tmp/journal-seq-XXXXXX"; - JournalFile *one, *two; - uint64_t seqnum = 0; - sd_id128_t seqnum_id; - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, - true, false, NULL, NULL, NULL, NULL, &one) == 0); - - append_number(one, 1, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 1); - append_number(one, 2, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 2); - - assert_se(one->header->state == STATE_ONLINE); - assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id)); - assert_se(!sd_id128_equal(one->header->file_id, one->header->boot_id)); - assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id)); - - memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t)); - - assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, - true, false, NULL, NULL, NULL, one, &two) == 0); - - assert_se(two->header->state == STATE_ONLINE); - assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id)); - assert_se(sd_id128_equal(one->header->machine_id, one->header->machine_id)); - assert_se(sd_id128_equal(one->header->boot_id, one->header->boot_id)); - assert_se(sd_id128_equal(one->header->seqnum_id, one->header->seqnum_id)); - - append_number(two, 3, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 3); - append_number(two, 4, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 4); - - test_close(two); - - append_number(one, 5, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 5); - - append_number(one, 6, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 6); - - test_close(one); - - /* restart server */ - seqnum = 0; - - assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0, - true, false, NULL, NULL, NULL, NULL, &two) == 0); - - assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id)); - - append_number(two, 7, &seqnum); - printf("seqnum=%"PRIu64"\n", seqnum); - assert_se(seqnum == 5); - - /* So..., here we have the same seqnum in two files with the - * same seqnum_id. */ - - test_close(two); - - log_info("Done..."); - - if (arg_keep) - log_info("Not removing %s", t); - else { - journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - } -} - -int main(int argc, char *argv[]) { - log_set_max_level(LOG_DEBUG); - - /* journal_file_open requires a valid machine id */ - if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; - - arg_keep = argc > 1; - - test_skip(setup_sequential); - test_skip(setup_interleaved); - - test_sequence_numbers(); - - return 0; -} diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c deleted file mode 100644 index 3ab554b9b0..0000000000 --- a/src/journal/test-journal-match.c +++ /dev/null @@ -1,76 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-journal.h" - -#include "alloc-util.h" -#include "journal-internal.h" -#include "log.h" -#include "string-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - _cleanup_(sd_journal_closep) sd_journal*j = NULL; - _cleanup_free_ char *t; - - log_set_max_level(LOG_DEBUG); - - assert_se(sd_journal_open(&j, 0) >= 0); - - assert_se(sd_journal_add_match(j, "foobar", 0) < 0); - assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0); - assert_se(sd_journal_add_match(j, "", 0) < 0); - assert_se(sd_journal_add_match(j, "=", 0) < 0); - assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0); - assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0); - assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0); - assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); - assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0); - assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); - assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0); - assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0); - - assert_se(sd_journal_add_disjunction(j) >= 0); - - assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0); - assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0); - assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0); - - assert_se(sd_journal_add_conjunction(j) >= 0); - - assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0); - assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0); - assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0); - assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0); - - assert_se(sd_journal_add_disjunction(j) >= 0); - - assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0); - assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0); - - assert_se(t = journal_make_match_string(j)); - - printf("resulting match expression is: %s\n", t); - - assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))")); - - return 0; -} diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c deleted file mode 100644 index d70f0b0bc8..0000000000 --- a/src/journal/test-journal-send.c +++ /dev/null @@ -1,102 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-journal.h" - -#include "macro.h" - -int main(int argc, char *argv[]) { - char huge[4096*1024]; - - /* 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; - assert_se(sd_journal_perror("Foobar") == 0); - - assert_se(sd_journal_perror("") == 0); - - memset(huge, 'x', sizeof(huge)); - memcpy(huge, "HUGE=", 5); - char_array_0(huge); - - assert_se(sd_journal_send("MESSAGE=Huge field attached", - huge, - NULL) == 0); - - 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!"); - - 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); - - return 0; -} diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c deleted file mode 100644 index 7e5a980719..0000000000 --- a/src/journal/test-journal-stream.c +++ /dev/null @@ -1,196 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-journal.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 "util.h" - -#define N_ENTRIES 200 - -static void verify_contents(sd_journal *j, unsigned skip) { - unsigned i; - - assert_se(j); - - i = 0; - SD_JOURNAL_FOREACH(j) { - const void *d; - char *k, *c; - size_t l; - unsigned u = 0; - - assert_se(sd_journal_get_cursor(j, &k) >= 0); - printf("cursor: %s\n", k); - free(k); - - assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0); - printf("\t%.*s\n", (int) l, (const char*) d); - - assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); - assert_se(k = strndup(d, l)); - printf("\t%s\n", k); - - if (skip > 0) { - assert_se(safe_atou(k + 7, &u) >= 0); - assert_se(i == u); - i += skip; - } - - free(k); - - assert_se(sd_journal_get_cursor(j, &c) >= 0); - assert_se(sd_journal_test_cursor(j, c) > 0); - free(c); - } - - if (skip > 0) - assert_se(i == N_ENTRIES); -} - -int main(int argc, char *argv[]) { - JournalFile *one, *two, *three; - char t[] = "/tmp/journal-stream-XXXXXX"; - unsigned i; - _cleanup_(sd_journal_closep) sd_journal *j = NULL; - char *z; - const void *data; - size_t l; - dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL; - - /* journal_file_open requires a valid machine id */ - if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; - - log_set_max_level(LOG_DEBUG); - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); - assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); - assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); - - for (i = 0; i < N_ENTRIES; i++) { - char *p, *q; - dual_timestamp ts; - struct iovec iovec[2]; - - dual_timestamp_get(&ts); - - if (ts.monotonic <= previous_ts.monotonic) - ts.monotonic = previous_ts.monotonic + 1; - - if (ts.realtime <= previous_ts.realtime) - ts.realtime = previous_ts.realtime + 1; - - previous_ts = ts; - - assert_se(asprintf(&p, "NUMBER=%u", i) >= 0); - iovec[0].iov_base = p; - iovec[0].iov_len = strlen(p); - - assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0); - - iovec[1].iov_base = q; - iovec[1].iov_len = strlen(q); - - if (i % 10 == 0) - assert_se(journal_file_append_entry(three, &ts, iovec, 2, NULL, NULL, NULL) == 0); - else { - if (i % 3 == 0) - assert_se(journal_file_append_entry(two, &ts, iovec, 2, NULL, NULL, NULL) == 0); - - assert_se(journal_file_append_entry(one, &ts, iovec, 2, NULL, NULL, NULL) == 0); - } - - free(p); - free(q); - } - - (void) journal_file_close(one); - (void) journal_file_close(two); - (void) journal_file_close(three); - - assert_se(sd_journal_open_directory(&j, t, 0) >= 0); - - assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); - SD_JOURNAL_FOREACH_BACKWARDS(j) { - _cleanup_free_ char *c; - - assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); - printf("\t%.*s\n", (int) l, (const char*) data); - - assert_se(sd_journal_get_cursor(j, &c) >= 0); - assert_se(sd_journal_test_cursor(j, c) > 0); - } - - SD_JOURNAL_FOREACH(j) { - _cleanup_free_ char *c; - - assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); - printf("\t%.*s\n", (int) l, (const char*) data); - - assert_se(sd_journal_get_cursor(j, &c) >= 0); - assert_se(sd_journal_test_cursor(j, c) > 0); - } - - sd_journal_flush_matches(j); - - verify_contents(j, 1); - - printf("NEXT TEST\n"); - assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); - - assert_se(z = journal_make_match_string(j)); - printf("resulting match expression is: %s\n", z); - free(z); - - verify_contents(j, 5); - - printf("NEXT TEST\n"); - sd_journal_flush_matches(j); - assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0); - assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0); - assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0); - assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0); - - assert_se(z = journal_make_match_string(j)); - printf("resulting match expression is: %s\n", z); - free(z); - - verify_contents(j, 0); - - assert_se(sd_journal_query_unique(j, "NUMBER") >= 0); - SD_JOURNAL_FOREACH_UNIQUE(j, data, l) - printf("%.*s\n", (int) l, (const char*) data); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - - return 0; -} diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c deleted file mode 100644 index 4ff7f3ec2e..0000000000 --- a/src/journal/test-journal-syslog.c +++ /dev/null @@ -1,44 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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) { - const char *buf = str; - _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; - int ret2; - - ret2 = syslog_parse_identifier(&buf, &ident2, &pid2); - - assert_se(ret == ret2); - assert_se(ident == ident2 || streq_ptr(ident, ident2)); - assert_se(pid == pid2 || streq_ptr(pid, pid2)); -} - -int main(void) { - test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); - test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); - test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); - - return 0; -} diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c deleted file mode 100644 index 3d2312fc55..0000000000 --- a/src/journal/test-journal-verify.c +++ /dev/null @@ -1,150 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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 - -static void bit_toggle(const char *fn, uint64_t p) { - uint8_t b; - ssize_t r; - int fd; - - fd = open(fn, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - - r = pread(fd, &b, 1, p/8); - assert_se(r == 1); - - b ^= 1 << (p % 8); - - r = pwrite(fd, &b, 1, p/8); - assert_se(r == 1); - - safe_close(fd); -} - -static int raw_verify(const char *fn, const char *verification_key) { - JournalFile *f; - int r; - - r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); - if (r < 0) - return r; - - r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false); - (void) journal_file_close(f); - - return r; -} - -int main(int argc, char *argv[]) { - char t[] = "/tmp/journal-XXXXXX"; - unsigned n; - JournalFile *f; - const char *verification_key = argv[1]; - usec_t from = 0, to = 0, total = 0; - char a[FORMAT_TIMESTAMP_MAX]; - char b[FORMAT_TIMESTAMP_MAX]; - char c[FORMAT_TIMESPAN_MAX]; - struct stat st; - uint64_t p; - - /* journal_file_open requires a valid machine id */ - if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; - - log_set_max_level(LOG_DEBUG); - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - log_info("Generating..."); - - assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); - - for (n = 0; n < N_ENTRIES; n++) { - struct iovec iovec; - struct dual_timestamp ts; - char *test; - - dual_timestamp_get(&ts); - - assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE)); - - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); - - assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); - - free(test); - } - - (void) journal_file_close(f); - - log_info("Verifying..."); - - assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); - /* journal_file_print_header(f); */ - journal_file_dump(f); - - assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0); - - if (verification_key && JOURNAL_HEADER_SEALED(f->header)) - log_info("=> Validated from %s to %s, %s missing", - format_timestamp(a, sizeof(a), from), - format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0, 0)); - - (void) journal_file_close(f); - - if (verification_key) { - log_info("Toggling bits..."); - - assert_se(stat("test.journal", &st) >= 0); - - for (p = 38448*8+0; p < ((uint64_t) st.st_size * 8); p ++) { - bit_toggle("test.journal", p); - - log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8); - - if (raw_verify("test.journal", verification_key) >= 0) - log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8); - - bit_toggle("test.journal", p); - } - } - - log_info("Exiting..."); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - - return 0; -} diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c deleted file mode 100644 index 2543d64b5b..0000000000 --- a/src/journal/test-journal.c +++ /dev/null @@ -1,178 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "journal-authenticate.h" -#include "journal-file.h" -#include "journal-vacuum.h" -#include "log.h" -#include "rm-rf.h" - -static bool arg_keep = false; - -static void test_non_empty(void) { - dual_timestamp ts; - JournalFile *f; - struct iovec iovec; - static const char test[] = "TEST1=1", test2[] = "TEST2=2"; - Object *o; - uint64_t p; - char t[] = "/tmp/journal-XXXXXX"; - - log_set_max_level(LOG_DEBUG); - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); - - dual_timestamp_get(&ts); - - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); - assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); - - iovec.iov_base = (void*) test2; - iovec.iov_len = strlen(test2); - assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); - - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); - assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); - -#ifdef HAVE_GCRYPT - journal_file_append_tag(f); -#endif - journal_file_dump(f); - - assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1); - assert_se(le64toh(o->entry.seqnum) == 1); - - assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1); - assert_se(le64toh(o->entry.seqnum) == 2); - - assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1); - assert_se(le64toh(o->entry.seqnum) == 3); - - assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 0); - - assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1); - assert_se(le64toh(o->entry.seqnum) == 1); - - assert_se(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); - assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 1); - - assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 3); - - assert_se(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); - assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 2); - - assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 2); - - assert_se(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); - - assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 1); - - assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 3); - - assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); - assert_se(le64toh(o->entry.seqnum) == 2); - - assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); - - journal_file_rotate(&f, true, true, NULL); - journal_file_rotate(&f, true, true, NULL); - - (void) journal_file_close(f); - - log_info("Done..."); - - if (arg_keep) - log_info("Not removing %s", t); - else { - journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - } - - puts("------------------------------------------------------------"); -} - -static void test_empty(void) { - JournalFile *f1, *f2, *f3, *f4; - char t[] = "/tmp/journal-XXXXXX"; - - log_set_max_level(LOG_DEBUG); - - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); - - assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); - - assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); - - assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); - - assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); - - journal_file_print_header(f1); - puts(""); - journal_file_print_header(f2); - puts(""); - journal_file_print_header(f3); - puts(""); - journal_file_print_header(f4); - puts(""); - - log_info("Done..."); - - if (arg_keep) - log_info("Not removing %s", t); - else { - journal_directory_vacuum(".", 3000000, 0, 0, NULL, true); - - assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); - } - - (void) journal_file_close(f1); - (void) journal_file_close(f2); - (void) journal_file_close(f3); - (void) journal_file_close(f4); -} - -int main(int argc, char *argv[]) { - arg_keep = argc > 1; - - /* journal_file_open requires a valid machine id */ - if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; - - test_non_empty(); - test_empty(); - - return 0; -} diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c deleted file mode 100644 index 009aabf55e..0000000000 --- a/src/journal/test-mmap-cache.c +++ /dev/null @@ -1,79 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" -#include "mmap-cache.h" -#include "util.h" - -int main(int argc, char *argv[]) { - int x, y, z, r; - char px[] = "/tmp/testmmapXXXXXXX", py[] = "/tmp/testmmapYXXXXXX", pz[] = "/tmp/testmmapZXXXXXX"; - MMapCache *m; - void *p, *q; - - assert_se(m = mmap_cache_new()); - - x = mkostemp_safe(px, O_RDWR|O_CLOEXEC); - assert_se(x >= 0); - unlink(px); - - y = mkostemp_safe(py, O_RDWR|O_CLOEXEC); - assert_se(y >= 0); - unlink(py); - - z = mkostemp_safe(pz, O_RDWR|O_CLOEXEC); - assert_se(z >= 0); - unlink(pz); - - r = mmap_cache_get(m, x, PROT_READ, 0, false, 1, 2, NULL, &p); - assert_se(r >= 0); - - r = mmap_cache_get(m, x, PROT_READ, 0, false, 2, 2, NULL, &q); - assert_se(r >= 0); - - assert_se((uint8_t*) p + 1 == (uint8_t*) q); - - r = mmap_cache_get(m, x, PROT_READ, 1, false, 3, 2, NULL, &q); - assert_se(r >= 0); - - assert_se((uint8_t*) p + 2 == (uint8_t*) q); - - r = mmap_cache_get(m, x, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p); - assert_se(r >= 0); - - r = mmap_cache_get(m, x, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q); - assert_se(r >= 0); - - assert_se((uint8_t*) p + 1 == (uint8_t*) q); - - mmap_cache_unref(m); - - safe_close(x); - safe_close(y); - safe_close(z); - - return 0; -} diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install deleted file mode 100644 index 68c24bed7a..0000000000 --- a/src/kernel-install/50-depmod.install +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh - -[[ $1 == "add" ]] || exit 0 -[[ $2 ]] || exit 1 - -exec depmod -a "$2" diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install deleted file mode 100644 index af9f0f9ccd..0000000000 --- a/src/kernel-install/90-loaderentry.install +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh - -COMMAND="$1" -KERNEL_VERSION="$2" -BOOT_DIR_ABS="$3" -KERNEL_IMAGE="$4" - -if [[ -f /etc/machine-id ]]; then - read MACHINE_ID < /etc/machine-id -fi - -if ! [[ $MACHINE_ID ]]; then - exit 1 -fi - -BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION" -BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR} -LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" - -if [[ $COMMAND == remove ]]; then - exec rm -f "$LOADER_ENTRY" -fi - -if ! [[ $COMMAND == add ]]; then - exit 1 -fi - -if ! [[ $KERNEL_IMAGE ]]; then - exit 1 -fi - -if [[ -f /etc/os-release ]]; then - . /etc/os-release -elif [[ -f /usr/lib/os-release ]]; then - . /usr/lib/os-release -fi - -if ! [[ $PRETTY_NAME ]]; then - PRETTY_NAME="GNU/Linux $KERNEL_VERSION" -fi - -declare -a BOOT_OPTIONS - -if [[ -f /etc/kernel/cmdline ]]; then - read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline -fi - -if ! [[ ${BOOT_OPTIONS[*]} ]]; then - read -r -d '' -a line < /proc/cmdline - for i in "${line[@]}"; do - [[ "${i#initrd=*}" != "$i" ]] && continue - BOOT_OPTIONS+=("$i") - done -fi - -if ! [[ ${BOOT_OPTIONS[*]} ]]; then - echo "Could not determine the kernel command line parameters." >&2 - echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2 - exit 1 -fi - -cp "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" && - chown root:root "$BOOT_DIR_ABS/linux" && - chmod 0644 "$BOOT_DIR_ABS/linux" || { - echo "Could not copy '$KERNEL_IMAGE to '$BOOT_DIR_ABS/linux'." >&2 - exit 1 -} - -mkdir -p "${LOADER_ENTRY%/*}" || { - echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2 - exit 1 -} - -{ - echo "title $PRETTY_NAME" - echo "version $KERNEL_VERSION" - echo "machine-id $MACHINE_ID" - echo "options ${BOOT_OPTIONS[*]}" - echo "linux $BOOT_DIR/linux" - [[ -f $BOOT_DIR_ABS/initrd ]] && \ - echo "initrd $BOOT_DIR/initrd" - : -} > "$LOADER_ENTRY" || { - echo "Could not create loader entry '$LOADER_ENTRY'." >&2 - exit 1 -} -exit 0 diff --git a/src/kernel-install/Makefile b/src/kernel-install/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/kernel-install/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install deleted file mode 100644 index 1159dc384d..0000000000 --- a/src/kernel-install/kernel-install +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- -# ex: ts=8 sw=4 sts=4 et filetype=sh -# -# This file is part of systemd. -# -# Copyright 2013 Harald Hoyer -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# systemd is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with systemd; If not, see . - -usage() -{ - echo "Usage:" - echo " $0 add KERNEL-VERSION KERNEL-IMAGE" - echo " $0 remove KERNEL-VERSION" -} - -dropindirs_sort() -{ - local suffix=$1; shift - local -a files - local f d i - - readarray -t files < <( - for d in "$@"; do - for i in "$d/"*"$suffix"; do - if [[ -e "$i" ]]; then - echo "${i##*/}" - fi - done - done | sort -Vu - ) - - for f in "${files[@]}"; do - for d in "$@"; do - if [[ -e "$d/$f" ]]; then - echo "$d/$f" - continue 2 - fi - done - done -} - -export LC_COLLATE=C - -for i in "$@"; do - if [ "$i" == "--help" -o "$i" == "-h" ]; then - usage - exit 0 - fi -done - -if [[ "${0##*/}" == 'installkernel' ]]; then - COMMAND='add' -else - COMMAND="$1" - shift -fi - -KERNEL_VERSION="$1" -KERNEL_IMAGE="$2" - -if [[ -f /etc/machine-id ]]; then - read MACHINE_ID < /etc/machine-id -fi - -if ! [[ $MACHINE_ID ]]; then - echo "Could not determine your machine ID from /etc/machine-id." >&2 - echo "Please run 'systemd-machine-id-setup' as root. See man:machine-id(5)" >&2 - exit 1 -fi - -if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then - echo "Not enough arguments" >&2 - exit 1 -fi - -if [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then - BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" -elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]] \ - || mountpoint -q /boot/efi; then - BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" -else - BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" -fi - -ret=0 - -readarray -t PLUGINS < <( - dropindirs_sort ".install" \ - "/etc/kernel/install.d" \ - "/usr/lib/kernel/install.d" -) - -case $COMMAND in - add) - if [[ ! "$KERNEL_IMAGE" ]]; then - echo "Command 'add' requires an argument" >&2 - exit 1 - fi - - mkdir -p "$BOOT_DIR_ABS" || { - echo "Could not create boot directory '$BOOT_DIR_ABS'." >&2 - exit 1 - } - - for f in "${PLUGINS[@]}"; do - if [[ -x $f ]]; then - "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE" - ((ret+=$?)) - fi - done - ;; - - remove) - for f in "${PLUGINS[@]}"; do - if [[ -x $f ]]; then - "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS" - ((ret+=$?)) - fi - done - - rm -rf "$BOOT_DIR_ABS" - ((ret+=$?)) - ;; - - *) - echo "Unknown command '$COMMAND'" >&2 - exit 1 - ;; -esac - -exit $ret diff --git a/src/libbasic/Makefile b/src/libbasic/Makefile new file mode 100644 index 0000000000..3778a42f06 --- /dev/null +++ b/src/libbasic/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += include +nested.subdirs += src + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libbasic/include/Makefile b/src/libbasic/include/Makefile new file mode 100644 index 0000000000..ac4e6e896a --- /dev/null +++ b/src/libbasic/include/Makefile @@ -0,0 +1,28 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += basic + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libbasic/include/basic/.gitignore b/src/libbasic/include/basic/.gitignore new file mode 100644 index 0000000000..e22411e484 --- /dev/null +++ b/src/libbasic/include/basic/.gitignore @@ -0,0 +1,16 @@ +/cap-from-name.gperf +/cap-from-name.h +/cap-list.txt +/cap-to-name.h +/errno-from-name.gperf +/errno-from-name.h +/errno-list.txt +/errno-to-name.h +/af-from-name.gperf +/af-from-name.h +/af-list.txt +/af-to-name.h +/arphrd-from-name.gperf +/arphrd-from-name.h +/arphrd-list.txt +/arphrd-to-name.h diff --git a/src/libbasic/include/basic/Makefile b/src/libbasic/include/basic/Makefile new file mode 100644 index 0000000000..d442808156 --- /dev/null +++ b/src/libbasic/include/basic/Makefile @@ -0,0 +1,59 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +$(outdir)/errno-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM -include errno.h - $@ + +$(outdir)/errno-to-name.h: $(outdir)/errno-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const errno_names[] = { "} !/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@ + +$(outdir)/af-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM -include sys/socket.h - $@ + +$(outdir)/af-to-name.h: $(outdir)/af-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const af_names[] = { "} !/AF_FILE/ && !/AF_ROUTE/ && !/AF_LOCAL/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@ + +$(outdir)/arphrd-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM -include net/if_arp.h - $@ + +$(outdir)/arphrd-to-name.h: $(outdir)/arphrd-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const arphrd_names[] = { "} !/CISCO/ { printf "[ARPHRD_%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@ + +$(outdir)/arphrd-from-name.gperf: $(outdir)/arphrd-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct arphrd_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, ARPHRD_%s\n", $$1, $$1 }' <$< >$@ + +$(outdir)/cap-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM -include linux/capability.h -include missing.h - $@ + +$(outdir)/cap-to-name.h: $(outdir)/cap-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const capability_names[] = { "} { printf "[%s] = \"%s\",\n", $$1, tolower($$1) } END{print "};"}' <$< >$@ + +$(outdir)/cap-from-name.gperf: $(outdir)/cap-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct capability_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, %s\n", $$1, $$1 }' <$< >$@ + +$(outdir)/cap-from-name.h: $(outdir)/cap-from-name.gperf + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_capability -H hash_capability_name -p -C <$< >$@ + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libbasic/include/basic/MurmurHash2.h b/src/libbasic/include/basic/MurmurHash2.h new file mode 100644 index 0000000000..93362dd485 --- /dev/null +++ b/src/libbasic/include/basic/MurmurHash2.h @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// MurmurHash2 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#ifndef _MURMURHASH2_H_ +#define _MURMURHASH2_H_ + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +typedef unsigned char uint8_t; +typedef unsigned long uint32_t; +typedef unsigned __int64 uint64_t; + +// Other compilers + +#else // defined(_MSC_VER) + +#include + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ); + +//----------------------------------------------------------------------------- + +#endif // _MURMURHASH2_H_ diff --git a/src/libbasic/include/basic/af-list.h b/src/libbasic/include/basic/af-list.h new file mode 100644 index 0000000000..6a4cc03839 --- /dev/null +++ b/src/libbasic/include/basic/af-list.h @@ -0,0 +1,41 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "string-util.h" + +const char *af_to_name(int id); +int af_from_name(const char *name); + +static inline const char* af_to_name_short(int id) { + const char *f; + + if (id == AF_UNSPEC) + return "*"; + + f = af_to_name(id); + if (!f) + return "unknown"; + + assert(startswith(f, "AF_")); + return f + 3; +} + +int af_max(void); diff --git a/src/libbasic/include/basic/alloc-util.h b/src/libbasic/include/basic/alloc-util.h new file mode 100644 index 0000000000..ceeee519b7 --- /dev/null +++ b/src/libbasic/include/basic/alloc-util.h @@ -0,0 +1,111 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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) + +static inline bool size_multiply_overflow(size_t size, size_t need) { + return _unlikely_(need != 0 && size > (SIZE_MAX / need)); +} + +_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { + if (size_multiply_overflow(size, need)) + return NULL; + + return malloc(size * need); +} + +_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) + return NULL; + + return realloc(p, size * need); +} + +_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) + return NULL; + + return memdup(p, size * need); +} + +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/libbasic/include/basic/architecture.h b/src/libbasic/include/basic/architecture.h new file mode 100644 index 0000000000..b3e4d85906 --- /dev/null +++ b/src/libbasic/include/basic/architecture.h @@ -0,0 +1,199 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" +#include "util.h" + +/* A cleaned up architecture definition. We don't want to get lost in + * processor features, models, generations or even ABIs. Hence we + * focus on general family, and distinguish word width and + * endianness. */ + +enum { + ARCHITECTURE_X86 = 0, + ARCHITECTURE_X86_64, + ARCHITECTURE_PPC, + ARCHITECTURE_PPC_LE, + ARCHITECTURE_PPC64, + ARCHITECTURE_PPC64_LE, + ARCHITECTURE_IA64, + ARCHITECTURE_PARISC, + ARCHITECTURE_PARISC64, + ARCHITECTURE_S390, + ARCHITECTURE_S390X, + ARCHITECTURE_SPARC, + ARCHITECTURE_SPARC64, + ARCHITECTURE_MIPS, + ARCHITECTURE_MIPS_LE, + ARCHITECTURE_MIPS64, + ARCHITECTURE_MIPS64_LE, + ARCHITECTURE_ALPHA, + ARCHITECTURE_ARM, + ARCHITECTURE_ARM_BE, + ARCHITECTURE_ARM64, + ARCHITECTURE_ARM64_BE, + ARCHITECTURE_SH, + ARCHITECTURE_SH64, + ARCHITECTURE_M68K, + ARCHITECTURE_TILEGX, + ARCHITECTURE_CRIS, + ARCHITECTURE_NIOS2, + _ARCHITECTURE_MAX, + _ARCHITECTURE_INVALID = -1 +}; + +int uname_architecture(void); + +/* + * LIB_ARCH_TUPLE should resolve to the local library path + * architecture tuple systemd is built for, according to the Debian + * tuple list: + * + * https://wiki.debian.org/Multiarch/Tuples + * + * This is used in library search paths that should understand + * Debian's paths on all distributions. + */ + +#if defined(__x86_64__) +# define native_architecture() ARCHITECTURE_X86_64 +# define LIB_ARCH_TUPLE "x86_64-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_X86 +#elif defined(__i386__) +# define native_architecture() ARCHITECTURE_X86 +# define LIB_ARCH_TUPLE "i386-linux-gnu" +#elif defined(__powerpc64__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_PPC64 +# define LIB_ARCH_TUPLE "ppc64-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC +# else +# define native_architecture() ARCHITECTURE_PPC64_LE +# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC_LE +# endif +#elif defined(__powerpc__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_PPC +# define LIB_ARCH_TUPLE "powerpc-linux-gnu" +# else +# define native_architecture() ARCHITECTURE_PPC_LE +# error "Missing LIB_ARCH_TUPLE for PPCLE" +# endif +#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" +#elif defined(__hppa__) +# define native_architecture() ARCHITECTURE_PARISC +# define LIB_ARCH_TUPLE "hppa‑linux‑gnu" +#elif defined(__s390x__) +# define native_architecture() ARCHITECTURE_S390X +# define LIB_ARCH_TUPLE "s390x-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_S390 +#elif defined(__s390__) +# define native_architecture() ARCHITECTURE_S390 +# define LIB_ARCH_TUPLE "s390-linux-gnu" +#elif defined(__sparc__) && defined (__arch64__) +# define native_architecture() ARCHITECTURE_SPARC64 +# define LIB_ARCH_TUPLE "sparc64-linux-gnu" +#elif defined(__sparc__) +# define native_architecture() ARCHITECTURE_SPARC +# define LIB_ARCH_TUPLE "sparc-linux-gnu" +#elif defined(__mips64__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_MIPS64 +# error "Missing LIB_ARCH_TUPLE for MIPS64" +# else +# define native_architecture() ARCHITECTURE_MIPS64_LE +# error "Missing LIB_ARCH_TUPLE for MIPS64_LE" +# endif +#elif defined(__mips__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_MIPS +# define LIB_ARCH_TUPLE "mips-linux-gnu" +# else +# define native_architecture() ARCHITECTURE_MIPS_LE +# define LIB_ARCH_TUPLE "mipsel-linux-gnu" +# endif +#elif defined(__alpha__) +# define native_architecture() ARCHITECTURE_ALPHA +# define LIB_ARCH_TUPLE "alpha-linux-gnu" +#elif defined(__aarch64__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_ARM64_BE +# define LIB_ARCH_TUPLE "aarch64_be-linux-gnu" +# else +# define native_architecture() ARCHITECTURE_ARM64 +# define LIB_ARCH_TUPLE "aarch64-linux-gnu" +# endif +#elif defined(__arm__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_ARM_BE +# if defined(__ARM_EABI__) +# if defined(__ARM_PCS_VFP) +# define LIB_ARCH_TUPLE "armeb-linux-gnueabihf" +# else +# define LIB_ARCH_TUPLE "armeb-linux-gnueabi" +# endif +# else +# define LIB_ARCH_TUPLE "armeb-linux-gnu" +# endif +# else +# define native_architecture() ARCHITECTURE_ARM +# if defined(__ARM_EABI__) +# if defined(__ARM_PCS_VFP) +# define LIB_ARCH_TUPLE "arm-linux-gnueabihf" +# else +# define LIB_ARCH_TUPLE "arm-linux-gnueabi" +# endif +# else +# define LIB_ARCH_TUPLE "arm-linux-gnu" +# endif +# endif +#elif defined(__sh64__) +# define native_architecture() ARCHITECTURE_SH64 +# error "Missing LIB_ARCH_TUPLE for SH64" +#elif defined(__sh__) +# define native_architecture() ARCHITECTURE_SH +# define LIB_ARCH_TUPLE "sh4-linux-gnu" +#elif defined(__m68k__) +# define native_architecture() ARCHITECTURE_M68K +# define LIB_ARCH_TUPLE "m68k-linux-gnu" +#elif defined(__tilegx__) +# define native_architecture() ARCHITECTURE_TILEGX +# error "Missing LIB_ARCH_TUPLE for TILEGX" +#elif defined(__cris__) +# define native_architecture() ARCHITECTURE_CRIS +# error "Missing LIB_ARCH_TUPLE for CRIS" +#elif defined(__nios2__) +# define native_architecture() ARCHITECTURE_NIOS2 +# define LIB_ARCH_TUPLE "nios2-linux-gnu" +#else +# error "Please register your architecture here!" +#endif + +const char *architecture_to_string(int a) _const_; +int architecture_from_string(const char *s) _pure_; diff --git a/src/libbasic/include/basic/arphrd-list.h b/src/libbasic/include/basic/arphrd-list.h new file mode 100644 index 0000000000..c0f8758dbe --- /dev/null +++ b/src/libbasic/include/basic/arphrd-list.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + 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 . +***/ + +const char *arphrd_to_name(int id); +int arphrd_from_name(const char *name); + +int arphrd_max(void); diff --git a/src/libbasic/include/basic/async.h b/src/libbasic/include/basic/async.h new file mode 100644 index 0000000000..9bd13ff6e0 --- /dev/null +++ b/src/libbasic/include/basic/async.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int asynchronous_job(void* (*func)(void *p), void *arg); + +int asynchronous_sync(void); +int asynchronous_close(int fd); diff --git a/src/libbasic/include/basic/audit-util.h b/src/libbasic/include/basic/audit-util.h new file mode 100644 index 0000000000..e048503991 --- /dev/null +++ b/src/libbasic/include/basic/audit-util.h @@ -0,0 +1,31 @@ +#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 . +***/ + +#include +#include +#include + +#define AUDIT_SESSION_INVALID ((uint32_t) -1) + +int audit_session_from_pid(pid_t pid, uint32_t *id); +int audit_loginuid_from_pid(pid_t pid, uid_t *uid); + +bool use_audit(void); diff --git a/src/libbasic/include/basic/barrier.h b/src/libbasic/include/basic/barrier.h new file mode 100644 index 0000000000..6347fddc4d --- /dev/null +++ b/src/libbasic/include/basic/barrier.h @@ -0,0 +1,91 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "macro.h" + +/* See source file for an API description. */ + +typedef struct Barrier Barrier; + +enum { + BARRIER_SINGLE = 1LL, + BARRIER_ABORTION = INT64_MAX, + + /* bias values to store state; keep @WE < @THEY < @I */ + BARRIER_BIAS = INT64_MIN, + BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL, + BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL, + BARRIER_I_ABORTED = BARRIER_BIAS + 3LL, +}; + +enum { + BARRIER_PARENT, + BARRIER_CHILD, +}; + +struct Barrier { + int me; + int them; + int pipe[2]; + int64_t barriers; +}; + +#define BARRIER_NULL {-1, -1, {-1, -1}, 0} + +int barrier_create(Barrier *obj); +void barrier_destroy(Barrier *b); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy); + +void barrier_set_role(Barrier *b, unsigned int role); + +bool barrier_place(Barrier *b); +bool barrier_abort(Barrier *b); + +bool barrier_wait_next(Barrier *b); +bool barrier_wait_abortion(Barrier *b); +bool barrier_sync_next(Barrier *b); +bool barrier_sync(Barrier *b); + +static inline bool barrier_i_aborted(Barrier *b) { + return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED; +} + +static inline bool barrier_they_aborted(Barrier *b) { + return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; +} + +static inline bool barrier_we_aborted(Barrier *b) { + return b->barriers == BARRIER_WE_ABORTED; +} + +static inline bool barrier_is_aborted(Barrier *b) { + return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; +} + +static inline bool barrier_place_and_sync(Barrier *b) { + (void) barrier_place(b); + return barrier_sync(b); +} diff --git a/src/libbasic/include/basic/bitmap.h b/src/libbasic/include/basic/bitmap.h new file mode 100644 index 0000000000..63fdbe8bea --- /dev/null +++ b/src/libbasic/include/basic/bitmap.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + 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 . +***/ + +#include + +#include "hashmap.h" +#include "macro.h" + +typedef struct Bitmap Bitmap; + +Bitmap *bitmap_new(void); +Bitmap *bitmap_copy(Bitmap *b); +int bitmap_ensure_allocated(Bitmap **b); +void bitmap_free(Bitmap *b); + +int bitmap_set(Bitmap *b, unsigned n); +void bitmap_unset(Bitmap *b, unsigned n); +bool bitmap_isset(Bitmap *b, unsigned n); +bool bitmap_isclear(Bitmap *b); +void bitmap_clear(Bitmap *b); + +bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n); + +bool bitmap_equal(Bitmap *a, Bitmap *b); + +#define BITMAP_FOREACH(n, b, i) \ + for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); ) + +DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free); + +#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep) diff --git a/src/libbasic/include/basic/blkid-util.h b/src/libbasic/include/basic/blkid-util.h new file mode 100644 index 0000000000..7aa75eb091 --- /dev/null +++ b/src/libbasic/include/basic/blkid-util.h @@ -0,0 +1,31 @@ +#pragma once + +/*** + 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 . +***/ + +#ifdef HAVE_BLKID +#include +#endif + +#include "util.h" + +#ifdef HAVE_BLKID +DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe); +#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep) +#endif diff --git a/src/libbasic/include/basic/btrfs-ctree.h b/src/libbasic/include/basic/btrfs-ctree.h new file mode 100644 index 0000000000..66bdf9736e --- /dev/null +++ b/src/libbasic/include/basic/btrfs-ctree.h @@ -0,0 +1,96 @@ +#pragma once + +#include "macro.h" +#include "sparse-endian.h" + +/* Stolen from btrfs' ctree.h */ + +struct btrfs_timespec { + le64_t sec; + le32_t nsec; +} _packed_; + +struct btrfs_disk_key { + le64_t objectid; + uint8_t type; + le64_t offset; +} _packed_; + +struct btrfs_inode_item { + le64_t generation; + le64_t transid; + le64_t size; + le64_t nbytes; + le64_t block_group; + le32_t nlink; + le32_t uid; + le32_t gid; + le32_t mode; + le64_t rdev; + le64_t flags; + le64_t sequence; + le64_t reserved[4]; + struct btrfs_timespec atime; + struct btrfs_timespec ctime; + struct btrfs_timespec mtime; + struct btrfs_timespec otime; +} _packed_; + +struct btrfs_root_item { + struct btrfs_inode_item inode; + le64_t generation; + le64_t root_dirid; + le64_t bytenr; + le64_t byte_limit; + le64_t bytes_used; + le64_t last_snapshot; + le64_t flags; + le32_t refs; + struct btrfs_disk_key drop_progress; + uint8_t drop_level; + uint8_t level; + le64_t generation_v2; + uint8_t uuid[BTRFS_UUID_SIZE]; + uint8_t parent_uuid[BTRFS_UUID_SIZE]; + uint8_t received_uuid[BTRFS_UUID_SIZE]; + le64_t ctransid; + le64_t otransid; + le64_t stransid; + le64_t rtransid; + struct btrfs_timespec ctime; + struct btrfs_timespec otime; + struct btrfs_timespec stime; + struct btrfs_timespec rtime; + le64_t reserved[8]; +} _packed_; + +#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) + +struct btrfs_qgroup_info_item { + le64_t generation; + le64_t rfer; + le64_t rfer_cmpr; + le64_t excl; + le64_t excl_cmpr; +} _packed_; + +#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0) +#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1) +#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2) +#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3) +#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4) +#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5) + +struct btrfs_qgroup_limit_item { + le64_t flags; + le64_t max_rfer; + le64_t max_excl; + le64_t rsv_rfer; + le64_t rsv_excl; +} _packed_; + +struct btrfs_root_ref { + le64_t dirid; + le64_t sequence; + le16_t name_len; +} _packed_; diff --git a/src/libbasic/include/basic/btrfs-util.h b/src/libbasic/include/basic/btrfs-util.h new file mode 100644 index 0000000000..db431f5b74 --- /dev/null +++ b/src/libbasic/include/basic/btrfs-util.h @@ -0,0 +1,131 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "time-util.h" + +typedef struct BtrfsSubvolInfo { + uint64_t subvol_id; + usec_t otime; + + sd_id128_t uuid; + sd_id128_t parent_uuid; + + bool read_only; +} BtrfsSubvolInfo; + +typedef struct BtrfsQuotaInfo { + uint64_t referenced; + uint64_t exclusive; + uint64_t referenced_max; + uint64_t exclusive_max; +} BtrfsQuotaInfo; + +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_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); + +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_parent(int fd, uint64_t subvol_id, uint64_t *ret); + +int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info); + +int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret); + +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_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_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_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_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/libbasic/include/basic/build.h b/src/libbasic/include/basic/build.h new file mode 100644 index 0000000000..633c2aaccb --- /dev/null +++ b/src/libbasic/include/basic/build.h @@ -0,0 +1,155 @@ +#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 . +***/ + +#ifdef HAVE_PAM +#define _PAM_FEATURE_ "+PAM" +#else +#define _PAM_FEATURE_ "-PAM" +#endif + +#ifdef HAVE_AUDIT +#define _AUDIT_FEATURE_ "+AUDIT" +#else +#define _AUDIT_FEATURE_ "-AUDIT" +#endif + +#ifdef HAVE_SELINUX +#define _SELINUX_FEATURE_ "+SELINUX" +#else +#define _SELINUX_FEATURE_ "-SELINUX" +#endif + +#ifdef HAVE_APPARMOR +#define _APPARMOR_FEATURE_ "+APPARMOR" +#else +#define _APPARMOR_FEATURE_ "-APPARMOR" +#endif + +#ifdef HAVE_IMA +#define _IMA_FEATURE_ "+IMA" +#else +#define _IMA_FEATURE_ "-IMA" +#endif + +#ifdef HAVE_SMACK +#define _SMACK_FEATURE_ "+SMACK" +#else +#define _SMACK_FEATURE_ "-SMACK" +#endif + +#ifdef HAVE_SYSV_COMPAT +#define _SYSVINIT_FEATURE_ "+SYSVINIT" +#else +#define _SYSVINIT_FEATURE_ "-SYSVINIT" +#endif + +#ifdef HAVE_UTMP +#define _UTMP_FEATURE_ "+UTMP" +#else +#define _UTMP_FEATURE_ "-UTMP" +#endif + +#ifdef HAVE_LIBCRYPTSETUP +#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" +#else +#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" +#endif + +#ifdef HAVE_GCRYPT +#define _GCRYPT_FEATURE_ "+GCRYPT" +#else +#define _GCRYPT_FEATURE_ "-GCRYPT" +#endif + +#ifdef HAVE_GNUTLS +#define _GNUTLS_FEATURE_ "+GNUTLS" +#else +#define _GNUTLS_FEATURE_ "-GNUTLS" +#endif + +#ifdef HAVE_ACL +#define _ACL_FEATURE_ "+ACL" +#else +#define _ACL_FEATURE_ "-ACL" +#endif + +#ifdef HAVE_XZ +#define _XZ_FEATURE_ "+XZ" +#else +#define _XZ_FEATURE_ "-XZ" +#endif + +#ifdef HAVE_LZ4 +#define _LZ4_FEATURE_ "+LZ4" +#else +#define _LZ4_FEATURE_ "-LZ4" +#endif + +#ifdef HAVE_SECCOMP +#define _SECCOMP_FEATURE_ "+SECCOMP" +#else +#define _SECCOMP_FEATURE_ "-SECCOMP" +#endif + +#ifdef HAVE_BLKID +#define _BLKID_FEATURE_ "+BLKID" +#else +#define _BLKID_FEATURE_ "-BLKID" +#endif + +#ifdef HAVE_ELFUTILS +#define _ELFUTILS_FEATURE_ "+ELFUTILS" +#else +#define _ELFUTILS_FEATURE_ "-ELFUTILS" +#endif + +#ifdef HAVE_KMOD +#define _KMOD_FEATURE_ "+KMOD" +#else +#define _KMOD_FEATURE_ "-KMOD" +#endif + +#ifdef HAVE_LIBIDN +#define _IDN_FEATURE_ "+IDN" +#else +#define _IDN_FEATURE_ "-IDN" +#endif + +#define SYSTEMD_FEATURES \ + _PAM_FEATURE_ " " \ + _AUDIT_FEATURE_ " " \ + _SELINUX_FEATURE_ " " \ + _IMA_FEATURE_ " " \ + _APPARMOR_FEATURE_ " " \ + _SMACK_FEATURE_ " " \ + _SYSVINIT_FEATURE_ " " \ + _UTMP_FEATURE_ " " \ + _LIBCRYPTSETUP_FEATURE_ " " \ + _GCRYPT_FEATURE_ " " \ + _GNUTLS_FEATURE_ " " \ + _ACL_FEATURE_ " " \ + _XZ_FEATURE_ " " \ + _LZ4_FEATURE_ " " \ + _SECCOMP_FEATURE_ " " \ + _BLKID_FEATURE_ " " \ + _ELFUTILS_FEATURE_ " " \ + _KMOD_FEATURE_ " " \ + _IDN_FEATURE_ diff --git a/src/libbasic/include/basic/bus-label.h b/src/libbasic/include/basic/bus-label.h new file mode 100644 index 0000000000..62fb2c450c --- /dev/null +++ b/src/libbasic/include/basic/bus-label.h @@ -0,0 +1,31 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +char *bus_label_escape(const char *s); +char *bus_label_unescape_n(const char *f, size_t l); + +static inline char *bus_label_unescape(const char *f) { + return bus_label_unescape_n(f, f ? strlen(f) : 0); +} diff --git a/src/libbasic/include/basic/calendarspec.h b/src/libbasic/include/basic/calendarspec.h new file mode 100644 index 0000000000..f6472c1244 --- /dev/null +++ b/src/libbasic/include/basic/calendarspec.h @@ -0,0 +1,58 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* A structure for specifying (possibly repetitive) points in calendar + * time, a la cron */ + +#include + +#include "time-util.h" +#include "util.h" + +typedef struct CalendarComponent { + int value; + int repeat; + + struct CalendarComponent *next; +} CalendarComponent; + +typedef struct CalendarSpec { + int weekdays_bits; + bool utc; + + CalendarComponent *year; + CalendarComponent *month; + CalendarComponent *day; + + CalendarComponent *hour; + CalendarComponent *minute; + CalendarComponent *microsecond; +} CalendarSpec; + +void calendar_spec_free(CalendarSpec *c); + +int calendar_spec_normalize(CalendarSpec *spec); +bool calendar_spec_valid(CalendarSpec *spec); + +int calendar_spec_to_string(const CalendarSpec *spec, char **p); +int calendar_spec_from_string(const char *p, CalendarSpec **spec); + +int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next); diff --git a/src/libbasic/include/basic/cap-list.h b/src/libbasic/include/basic/cap-list.h new file mode 100644 index 0000000000..c1f6b94ad3 --- /dev/null +++ b/src/libbasic/include/basic/cap-list.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + 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 . +***/ + +const char *capability_to_name(int id); +int capability_from_name(const char *name); +int capability_list_length(void); diff --git a/src/libbasic/include/basic/capability-util.h b/src/libbasic/include/basic/capability-util.h new file mode 100644 index 0000000000..35a896e229 --- /dev/null +++ b/src/libbasic/include/basic/capability-util.h @@ -0,0 +1,57 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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 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); + +int drop_capability(cap_value_t cv); + +DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free); +#define _cleanup_cap_free_ _cleanup_(cap_freep) + +static inline void cap_free_charpp(char **p) { + if (*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/libbasic/include/basic/cgroup-util.h b/src/libbasic/include/basic/cgroup-util.h new file mode 100644 index 0000000000..14ebde5fc9 --- /dev/null +++ b/src/libbasic/include/basic/cgroup-util.h @@ -0,0 +1,236 @@ +#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 . +***/ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "hashmap.h" +#include "macro.h" +#include "set.h" + +/* An enum of well known cgroup controllers */ +typedef enum CGroupController { + CGROUP_CONTROLLER_CPU, + CGROUP_CONTROLLER_CPUACCT, + CGROUP_CONTROLLER_IO, + CGROUP_CONTROLLER_BLKIO, + CGROUP_CONTROLLER_MEMORY, + CGROUP_CONTROLLER_DEVICES, + CGROUP_CONTROLLER_PIDS, + _CGROUP_CONTROLLER_MAX, + _CGROUP_CONTROLLER_INVALID = -1, +} CGroupController; + +#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c)) + +/* A bit mask of well known cgroup controllers */ +typedef enum CGroupMask { + CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), + CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), + CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), + CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), + CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), + CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), + CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), + _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 +} CGroupMask; + +/* Special values for all weight knobs on unified hierarchy */ +#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_WEIGHT_MIN UINT64_C(1) +#define CGROUP_WEIGHT_MAX UINT64_C(10000) +#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) + +#define CGROUP_LIMIT_MIN UINT64_C(0) +#define CGROUP_LIMIT_MAX ((uint64_t) -1) + +static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { + return + x == CGROUP_WEIGHT_INVALID || + (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); +} + +/* IO limits on unified hierarchy */ +typedef enum CGroupIOLimitType { + CGROUP_IO_RBPS_MAX, + CGROUP_IO_WBPS_MAX, + CGROUP_IO_RIOPS_MAX, + CGROUP_IO_WIOPS_MAX, + + _CGROUP_IO_LIMIT_TYPE_MAX, + _CGROUP_IO_LIMIT_TYPE_INVALID = -1 +} CGroupIOLimitType; + +extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; + +const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; +CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; + +/* Special values for the cpu.shares attribute */ +#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) +#define CGROUP_CPU_SHARES_MIN UINT64_C(2) +#define CGROUP_CPU_SHARES_MAX UINT64_C(262144) +#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) + +static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { + return + x == CGROUP_CPU_SHARES_INVALID || + (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); +} + +/* Special values for the blkio.weight attribute */ +#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) +#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) +#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) + +static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { + return + x == CGROUP_BLKIO_WEIGHT_INVALID || + (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); +} + +/* + * General rules: + * + * We accept named hierarchies in the syntax "foo" and "name=foo". + * + * We expect that named hierarchies do not conflict in name with a + * kernel hierarchy, modulo the "name=" prefix. + * + * We always generate "normalized" controller names, i.e. without the + * "name=" prefix. + * + * We require absolute cgroup paths. When returning, we will always + * generate paths with multiple adjacent / removed. + */ + +int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); +int cg_read_pid(FILE *f, pid_t *_pid); +int cg_read_event(const char *controller, const char *path, const char *event, + char **val); + +int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); +int cg_read_subgroup(DIR *d, char **fn); + +typedef enum CGroupFlags { + CGROUP_SIGCONT = 1, + CGROUP_IGNORE_SELF = 2, + CGROUP_REMOVE = 4, +} CGroupFlags; + +typedef void (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); + +int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); +int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); + +int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); +int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); +int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); + +int cg_split_spec(const char *spec, char **controller, char **path); +int cg_mangle_path(const char *path, char **result); + +int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); +int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs); + +int cg_pid_get_path(const char *controller, pid_t pid, char **path); + +int cg_trim(const char *controller, const char *path, bool delete_root); + +int cg_rmdir(const char *controller, const char *path); + +int cg_create(const char *controller, const char *path); +int cg_attach(const char *controller, const char *path, pid_t pid); +int cg_attach_fallback(const char *controller, const char *path, pid_t pid); +int cg_create_and_attach(const char *controller, const char *path, pid_t pid); + +int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); +int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); + +int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); + +int cg_install_release_agent(const char *controller, const char *agent); +int cg_uninstall_release_agent(const char *controller); + +int cg_is_empty(const char *controller, const char *path); +int cg_is_empty_recursive(const char *controller, const char *path); + +int cg_get_root_path(char **path); + +int cg_path_get_session(const char *path, char **session); +int cg_path_get_owner_uid(const char *path, uid_t *uid); +int cg_path_get_unit(const char *path, char **unit); +int cg_path_get_user_unit(const char *path, char **unit); +int cg_path_get_machine_name(const char *path, char **machine); +int cg_path_get_slice(const char *path, char **slice); +int cg_path_get_user_slice(const char *path, char **slice); + +int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted); +int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup); + +int cg_pid_get_session(pid_t pid, char **session); +int cg_pid_get_owner_uid(pid_t pid, uid_t *uid); +int cg_pid_get_unit(pid_t pid, char **unit); +int cg_pid_get_user_unit(pid_t pid, char **unit); +int cg_pid_get_machine_name(pid_t pid, char **machine); +int cg_pid_get_slice(pid_t pid, char **slice); +int cg_pid_get_user_slice(pid_t pid, char **slice); + +int cg_path_decode_unit(const char *cgroup, char **unit); + +char *cg_escape(const char *p); +char *cg_unescape(const char *p) _pure_; + +bool cg_controller_is_valid(const char *p); + +int cg_slice_to_path(const char *unit, char **ret); + +typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); + +int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path); +int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); +int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); +int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); +int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root); +int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p); + +int cg_mask_supported(CGroupMask *ret); + +int cg_kernel_controllers(Set *controllers); + +int cg_unified(void); +void cg_unified_flush(void); + +bool cg_is_unified_wanted(void); +bool cg_is_legacy_wanted(void); + +const char* cgroup_controller_to_string(CGroupController c) _const_; +CGroupController cgroup_controller_from_string(const char *s) _pure_; + +int cg_weight_parse(const char *s, uint64_t *ret); +int cg_cpu_shares_parse(const char *s, uint64_t *ret); +int cg_blkio_weight_parse(const char *s, uint64_t *ret); diff --git a/src/libbasic/include/basic/chattr-util.h b/src/libbasic/include/basic/chattr-util.h new file mode 100644 index 0000000000..960cf6d5b3 --- /dev/null +++ b/src/libbasic/include/basic/chattr-util.h @@ -0,0 +1,26 @@ +#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 . +***/ + +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/libbasic/include/basic/clock-util.h b/src/libbasic/include/basic/clock-util.h new file mode 100644 index 0000000000..8830cd2f38 --- /dev/null +++ b/src/libbasic/include/basic/clock-util.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include + +int clock_is_localtime(const char* adjtime_path); +int clock_set_timezone(int *min); +int clock_reset_timewarp(void); +int clock_get_hwclock(struct tm *tm); +int clock_set_hwclock(const struct tm *tm); +int clock_apply_epoch(void); diff --git a/src/libbasic/include/basic/conf-files.h b/src/libbasic/include/basic/conf-files.h new file mode 100644 index 0000000000..e00e0e81fb --- /dev/null +++ b/src/libbasic/include/basic/conf-files.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2012 Lennart Poettering + Copyright 2010-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); +int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); +int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); diff --git a/src/libbasic/include/basic/copy.h b/src/libbasic/include/basic/copy.h new file mode 100644 index 0000000000..b5d08ebafe --- /dev/null +++ b/src/libbasic/include/basic/copy.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +int copy_file_fd(const char *from, int to, bool try_reflink); +int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags); +int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags); +int copy_tree(const char *from, const char *to, bool merge); +int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); +int copy_directory_fd(int dirfd, const char *to, bool merge); +int copy_directory(const char *from, const char *to, bool merge); +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); +int copy_times(int fdf, int fdt); +int copy_xattr(int fdf, int fdt); diff --git a/src/libbasic/include/basic/cpu-set-util.h b/src/libbasic/include/basic/cpu-set-util.h new file mode 100644 index 0000000000..6f49d9afb0 --- /dev/null +++ b/src/libbasic/include/basic/cpu-set-util.h @@ -0,0 +1,32 @@ +#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 . +***/ + +#include + +#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/libbasic/include/basic/def.h b/src/libbasic/include/basic/def.h new file mode 100644 index 0000000000..1a7a0f4928 --- /dev/null +++ b/src/libbasic/include/basic/def.h @@ -0,0 +1,90 @@ +#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 . +***/ + +#include "util.h" + +#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC) +#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) +#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) + +#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) +#define DEFAULT_START_LIMIT_BURST 5 + +/* The default time after which exit-on-idle services exit. This + * should be kept lower than the watchdog timeout, because otherwise + * 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 + +#ifdef HAVE_SPLIT_USR +#define KBD_KEYMAP_DIRS \ + "/usr/share/keymaps/\0" \ + "/usr/share/kbd/keymaps/\0" \ + "/usr/lib/kbd/keymaps/\0" \ + "/lib/kbd/keymaps/\0" +#else +#define KBD_KEYMAP_DIRS \ + "/usr/share/keymaps/\0" \ + "/usr/share/kbd/keymaps/\0" \ + "/usr/lib/kbd/keymaps/\0" +#endif + +#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" +#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" +#define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS +#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" +#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" + +#define PLYMOUTH_SOCKET { \ + .un.sun_family = AF_UNIX, \ + .un.sun_path = "\0/org/freedesktop/plymouthd", \ + } + +#ifndef TTY_GID +#define TTY_GID 5 +#endif + +#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/libbasic/include/basic/device-nodes.h b/src/libbasic/include/basic/device-nodes.h new file mode 100644 index 0000000000..94f385abcb --- /dev/null +++ b/src/libbasic/include/basic/device-nodes.h @@ -0,0 +1,26 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +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/libbasic/include/basic/dirent-util.h b/src/libbasic/include/basic/dirent-util.h new file mode 100644 index 0000000000..b91d04908f --- /dev/null +++ b/src/libbasic/include/basic/dirent-util.h @@ -0,0 +1,52 @@ +#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 . +***/ + +#include +#include +#include + +#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_or_backup_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/libbasic/include/basic/env-util.h b/src/libbasic/include/basic/env-util.h new file mode 100644 index 0000000000..b1fef704c2 --- /dev/null +++ b/src/libbasic/include/basic/env-util.h @@ -0,0 +1,51 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "macro.h" + +bool env_name_is_valid(const char *e); +bool env_value_is_valid(const char *e); +bool env_assignment_is_valid(const char *e); + +char *replace_env(const char *format, char **env); +char **replace_env_argv(char **argv, char **env); + +bool strv_env_is_valid(char **e); +#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) +char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); + +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, ...); +char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */ + +char **strv_env_set(char **x, const char *p); /* New copy ... */ +char **strv_env_unset(char **l, const char *p); /* In place ... */ +char **strv_env_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/libbasic/include/basic/errno-list.h b/src/libbasic/include/basic/errno-list.h new file mode 100644 index 0000000000..4eec0cc786 --- /dev/null +++ b/src/libbasic/include/basic/errno-list.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +const char *errno_to_name(int id); +int errno_from_name(const char *name); + +int errno_max(void); diff --git a/src/libbasic/include/basic/escape.h b/src/libbasic/include/basic/escape.h new file mode 100644 index 0000000000..6e58f61e19 --- /dev/null +++ b/src/libbasic/include/basic/escape.h @@ -0,0 +1,54 @@ +#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 . +***/ + +#include +#include +#include +#include +#include + +#include "missing.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, char32_t *ret, bool *eight_bit); + +char *xescape(const char *s, const char *bad); +char *octescape(const char *s, size_t len); + +char *shell_escape(const char *s, const char *bad); +char *shell_maybe_quote(const char *s); diff --git a/src/libbasic/include/basic/ether-addr-util.h b/src/libbasic/include/basic/ether-addr-util.h new file mode 100644 index 0000000000..74e125a95f --- /dev/null +++ b/src/libbasic/include/basic/ether-addr-util.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + 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 . +***/ + +#include +#include + +#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]); + +bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); + +#define ETHER_ADDR_NULL ((const struct ether_addr){}) + +static inline bool ether_addr_is_null(const struct ether_addr *addr) { + return ether_addr_equal(addr, ÐER_ADDR_NULL); +} + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); diff --git a/src/libbasic/include/basic/exit-status.h b/src/libbasic/include/basic/exit-status.h new file mode 100644 index 0000000000..2309f68815 --- /dev/null +++ b/src/libbasic/include/basic/exit-status.h @@ -0,0 +1,106 @@ +#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 . +***/ + +#include + +#include "hashmap.h" +#include "macro.h" +#include "set.h" + +/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB + * 'status' verb exit codes which are defined very differently. For details see: + * + * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html + */ + +typedef enum ExitStatus { + /* EXIT_SUCCESS defined by libc */ + /* EXIT_FAILURE defined by libc */ + EXIT_INVALIDARGUMENT = 2, + EXIT_NOTIMPLEMENTED = 3, + EXIT_NOPERMISSION = 4, + EXIT_NOTINSTALLED = 5, + EXIT_NOTCONFIGURED = 6, + EXIT_NOTRUNNING = 7, + + /* The LSB suggests that error codes >= 200 are "reserved". We + * use them here under the assumption that they hence are + * unused by init scripts. */ + + EXIT_CHDIR = 200, + EXIT_NICE, + EXIT_FDS, + EXIT_EXEC, + EXIT_MEMORY, + EXIT_LIMITS, + EXIT_OOM_ADJUST, + EXIT_SIGNAL_MASK, + EXIT_STDIN, + EXIT_STDOUT, + EXIT_CHROOT, /* 210 */ + EXIT_IOPRIO, + EXIT_TIMERSLACK, + EXIT_SECUREBITS, + EXIT_SETSCHEDULER, + EXIT_CPUAFFINITY, + EXIT_GROUP, + EXIT_USER, + EXIT_CAPABILITIES, + EXIT_CGROUP, + EXIT_SETSID, /* 220 */ + EXIT_CONFIRM, + EXIT_STDERR, + _EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */ + EXIT_PAM, + EXIT_NETWORK, + EXIT_NAMESPACE, + EXIT_NO_NEW_PRIVILEGES, + EXIT_SECCOMP, + EXIT_SELINUX_CONTEXT, + EXIT_PERSONALITY, /* 230 */ + EXIT_APPARMOR_PROFILE, + EXIT_ADDRESS_FAMILIES, + EXIT_RUNTIME_DIRECTORY, + EXIT_MAKE_STARTER, + EXIT_CHOWN, + EXIT_SMACK_PROCESS_LABEL, +} ExitStatus; + +typedef enum ExitStatusLevel { + EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */ + EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */ + EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */ + EXIT_STATUS_FULL = EXIT_STATUS_LSB +} ExitStatusLevel; + +typedef struct ExitStatusSet { + Set *status; + Set *signal; +} ExitStatusSet; + +const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_; + +bool is_clean_exit(int code, int status, ExitStatusSet *success_status); +bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status); + +void exit_status_set_free(ExitStatusSet *x); +bool exit_status_set_is_empty(ExitStatusSet *x); +bool exit_status_set_test(ExitStatusSet *x, int code, int status); diff --git a/src/libbasic/include/basic/extract-word.h b/src/libbasic/include/basic/extract-word.h new file mode 100644 index 0000000000..21db5ef33f --- /dev/null +++ b/src/libbasic/include/basic/extract-word.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#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/libbasic/include/basic/fd-util.h b/src/libbasic/include/basic/fd-util.h new file mode 100644 index 0000000000..34b98d4aec --- /dev/null +++ b/src/libbasic/include/basic/fd-util.h @@ -0,0 +1,80 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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); +void stdio_unset_cloexec(void); + +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); + +int fd_get_path(int fd, char **ret); + +/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ +#define ERRNO_IS_DISCONNECT(r) \ + IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) diff --git a/src/libbasic/include/basic/fileio-label.h b/src/libbasic/include/basic/fileio-label.h new file mode 100644 index 0000000000..fe7543013d --- /dev/null +++ b/src/libbasic/include/basic/fileio-label.h @@ -0,0 +1,30 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2010 Harald Hoyer + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "fileio.h" + +int write_string_file_atomic_label(const char *fn, const char *line); +int write_env_file_label(const char *fname, char **l); +int fopen_temporary_label(const char *target, + const char *path, FILE **f, char **temp_path); diff --git a/src/libbasic/include/basic/fileio.h b/src/libbasic/include/basic/fileio.h new file mode 100644 index 0000000000..9ac497d9eb --- /dev/null +++ b/src/libbasic/include/basic/fileio.h @@ -0,0 +1,90 @@ +#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 . +***/ + +#include +#include +#include +#include +#include + +#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); +int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); + +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_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); +int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); + +int write_env_file(const char *fname, char **l); + +int executable_is_script(const char *path, char **interpreter); + +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 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); + +int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); + +int open_tmpfile_unlinkable(const char *directory, int flags); +int open_tmpfile_linkable(const char *target, int flags, char **ret_path); + +int link_tmpfile(int fd, const char *path, const char *target); + +int read_nul_string(FILE *f, char **ret); diff --git a/src/libbasic/include/basic/formats-util.h b/src/libbasic/include/basic/formats-util.h new file mode 100644 index 0000000000..39a185f59b --- /dev/null +++ b/src/libbasic/include/basic/formats-util.h @@ -0,0 +1,79 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Ronny Chevalier + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#if SIZEOF_PID_T == 4 +# define PID_PRI PRIi32 +#elif SIZEOF_PID_T == 2 +# define PID_PRI PRIi16 +#else +# error Unknown pid_t size +#endif +#define PID_FMT "%" PID_PRI + +#if SIZEOF_UID_T == 4 +# define UID_FMT "%" PRIu32 +#elif SIZEOF_UID_T == 2 +# define UID_FMT "%" PRIu16 +#else +# error Unknown uid_t size +#endif + +#if SIZEOF_GID_T == 4 +# define GID_FMT "%" PRIu32 +#elif SIZEOF_GID_T == 2 +# define GID_FMT "%" PRIu16 +#else +# error Unknown gid_t size +#endif + +#if SIZEOF_TIME_T == 8 +# define PRI_TIME PRIi64 +#elif SIZEOF_TIME_T == 4 +# define PRI_TIME "li" +#else +# error Unknown time_t size +#endif + +#if SIZEOF_RLIM_T == 8 +# define RLIM_FMT "%" PRIu64 +#elif SIZEOF_RLIM_T == 4 +# define RLIM_FMT "%" PRIu32 +#else +# error Unknown rlim_t size +#endif + +#if SIZEOF_DEV_T == 8 +# define DEV_FMT "%" PRIu64 +#elif SIZEOF_DEV_T == 4 +# define DEV_FMT "%" PRIu32 +#else +# error Unknown dev_t size +#endif + +#if SIZEOF_INO_T == 8 +# define INO_FMT "%" PRIu64 +#elif SIZEOF_INO_T == 4 +# define INO_FMT "%" PRIu32 +#else +# error Unknown ino_t size +#endif diff --git a/src/libbasic/include/basic/fs-util.h b/src/libbasic/include/basic/fs-util.h new file mode 100644 index 0000000000..075e5942b1 --- /dev/null +++ b/src/libbasic/include/basic/fs-util.h @@ -0,0 +1,78 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#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_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); + +int var_tmp(char **ret); + +#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]; +}; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); diff --git a/src/libbasic/include/basic/glob-util.h b/src/libbasic/include/basic/glob-util.h new file mode 100644 index 0000000000..5d8fb47a26 --- /dev/null +++ b/src/libbasic/include/basic/glob-util.h @@ -0,0 +1,36 @@ +#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 . +***/ + +#include +#include + +#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/libbasic/include/basic/gunicode.h b/src/libbasic/include/basic/gunicode.h new file mode 100644 index 0000000000..5975bc8fc9 --- /dev/null +++ b/src/libbasic/include/basic/gunicode.h @@ -0,0 +1,30 @@ +#pragma once + +/* gunicode.h - Unicode manipulation functions + * + * Copyright (C) 1999, 2000 Tom Tromey + * Copyright 2000, 2005 Red Hat, Inc. + */ + +#include +#include +#include + +char *utf8_prev_char (const char *p); + +extern const char utf8_skip_data[256]; + +/** + * g_utf8_next_char: + * @p: Pointer to the start of a valid UTF-8 character + * + * Skips to the next character in a UTF-8 string. The string must be + * valid; this macro is as fast as possible, and has no error-checking. + * You would use this macro to iterate over a string character by + * character. The macro returns the start of the next UTF-8 character. + * Before using this macro, use g_utf8_validate() to validate strings + * that may contain invalid UTF-8. + */ +#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)]) + +bool unichar_iswide (uint32_t c); diff --git a/src/libbasic/include/basic/hash-funcs.h b/src/libbasic/include/basic/hash-funcs.h new file mode 100644 index 0000000000..299189d143 --- /dev/null +++ b/src/libbasic/include/basic/hash-funcs.h @@ -0,0 +1,65 @@ +#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 . +***/ + +#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/libbasic/include/basic/hashmap.h b/src/libbasic/include/basic/hashmap.h new file mode 100644 index 0000000000..6d1ae48b21 --- /dev/null +++ b/src/libbasic/include/basic/hashmap.h @@ -0,0 +1,372 @@ +#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 . +***/ + +#include +#include +#include + +#include "hash-funcs.h" +#include "macro.h" +#include "util.h" + +/* + * A hash table implementation. As a minor optimization a NULL hashmap object + * will be treated as empty hashmap for all read operations. That way it is not + * necessary to instantiate an object for each Hashmap use. + * + * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), + * the implemention will: + * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) + * - perform extra checks for invalid use of iterators + */ + +#define HASH_KEY_SIZE 16 + +/* The base type for all hashmap and set types. Many functions in the + * implementation take (HashmapBase*) parameters and are run-time polymorphic, + * though the API is not meant to be polymorphic (do not call functions + * internal_*() directly). */ +typedef struct HashmapBase HashmapBase; + +/* Specific hashmap/set types */ +typedef struct Hashmap Hashmap; /* Maps keys to values */ +typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ +typedef struct Set Set; /* Stores just keys */ + +/* Ideally the Iterator would be an opaque struct, but it is instantiated + * by hashmap users, so the definition has to be here. Do not use its fields + * directly. */ +typedef struct { + unsigned idx; /* index of an entry to be iterated next */ + const void *next_key; /* expected value of that entry's key pointer */ +#ifdef ENABLE_DEBUG_HASHMAP + unsigned put_count; /* hashmap's put_count recorded at start of iteration */ + unsigned rem_count; /* hashmap's rem_count in previous iteration */ + unsigned prev_idx; /* idx in previous iteration */ +#endif +} Iterator; + +#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) +#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) + +/* Macros for type checking */ +#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ + (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ + __builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ + __builtin_types_compatible_p(typeof(h), Set*)) + +#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ + (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ + +#define HASHMAP_BASE(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ + (HashmapBase*)(h), \ + (void)0) + +#define PLAIN_HASHMAP(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ + (Hashmap*)(h), \ + (void)0) + +#ifdef ENABLE_DEBUG_HASHMAP +# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line +# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ +# define HASHMAP_DEBUG_PASS_ARGS , func, file, line +#else +# define HASHMAP_DEBUG_PARAMS +# define HASHMAP_DEBUG_SRC_ARGS +# define HASHMAP_DEBUG_PASS_ARGS +#endif + +Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) + +HashmapBase *internal_hashmap_free(HashmapBase *h); +static inline Hashmap *hashmap_free(Hashmap *h) { + return (void*)internal_hashmap_free(HASHMAP_BASE(h)); +} +static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { + return (void*)internal_hashmap_free(HASHMAP_BASE(h)); +} + +HashmapBase *internal_hashmap_free_free(HashmapBase *h); +static inline Hashmap *hashmap_free_free(Hashmap *h) { + return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); +} +static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { + return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); +} + +Hashmap *hashmap_free_free_free(Hashmap *h); +static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { + return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); +} + +HashmapBase *internal_hashmap_copy(HashmapBase *h); +static inline Hashmap *hashmap_copy(Hashmap *h) { + return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); +} +static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { + return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); +} + +int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) + +int hashmap_put(Hashmap *h, const void *key, void *value); +static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { + return hashmap_put(PLAIN_HASHMAP(h), key, value); +} + +int hashmap_update(Hashmap *h, const void *key, void *value); +static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { + return hashmap_update(PLAIN_HASHMAP(h), key, value); +} + +int hashmap_replace(Hashmap *h, const void *key, void *value); +static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { + return hashmap_replace(PLAIN_HASHMAP(h), key, value); +} + +void *internal_hashmap_get(HashmapBase *h, const void *key); +static inline void *hashmap_get(Hashmap *h, const void *key) { + return internal_hashmap_get(HASHMAP_BASE(h), key); +} +static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { + return internal_hashmap_get(HASHMAP_BASE(h), key); +} + +void *hashmap_get2(Hashmap *h, const void *key, void **rkey); +static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { + return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); +} + +bool internal_hashmap_contains(HashmapBase *h, const void *key); +static inline bool hashmap_contains(Hashmap *h, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(h), key); +} +static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(h), key); +} + +void *internal_hashmap_remove(HashmapBase *h, const void *key); +static inline void *hashmap_remove(Hashmap *h, const void *key) { + return internal_hashmap_remove(HASHMAP_BASE(h), key); +} +static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { + return internal_hashmap_remove(HASHMAP_BASE(h), key); +} + +void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); +static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { + return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); +} + +void *hashmap_remove_value(Hashmap *h, const void *key, void *value); +static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { + return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); +} + +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); +static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { + return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); +} + +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); +static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { + return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); +} + +/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa + * should just work, allow this by having looser type-checking here. */ +int internal_hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) + +int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} +static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} + +int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ +static inline int hashmap_move(Hashmap *h, Hashmap *other) { + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); +} +static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); +} + +int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); +} +static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); +} + +unsigned internal_hashmap_size(HashmapBase *h) _pure_; +static inline unsigned hashmap_size(Hashmap *h) { + return internal_hashmap_size(HASHMAP_BASE(h)); +} +static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { + return internal_hashmap_size(HASHMAP_BASE(h)); +} + +static inline bool hashmap_isempty(Hashmap *h) { + return hashmap_size(h) == 0; +} +static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { + return ordered_hashmap_size(h) == 0; +} + +unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +static inline unsigned hashmap_buckets(Hashmap *h) { + return internal_hashmap_buckets(HASHMAP_BASE(h)); +} +static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { + return internal_hashmap_buckets(HASHMAP_BASE(h)); +} + +bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); +static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { + return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); +} +static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { + return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); +} + +void internal_hashmap_clear(HashmapBase *h); +static inline void hashmap_clear(Hashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h)); +} +static inline void ordered_hashmap_clear(OrderedHashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h)); +} + +void internal_hashmap_clear_free(HashmapBase *h); +static inline void hashmap_clear_free(Hashmap *h) { + internal_hashmap_clear_free(HASHMAP_BASE(h)); +} +static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { + internal_hashmap_clear_free(HASHMAP_BASE(h)); +} + +void hashmap_clear_free_free(Hashmap *h); +static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { + hashmap_clear_free_free(PLAIN_HASHMAP(h)); +} + +/* + * Note about all *_first*() functions + * + * For plain Hashmaps and Sets the order of entries is undefined. + * The functions find whatever entry is first in the implementation + * internal order. + * + * Only for OrderedHashmaps the order is well defined and finding + * the first entry is O(1). + */ + +void *internal_hashmap_steal_first(HashmapBase *h); +static inline void *hashmap_steal_first(Hashmap *h) { + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} +static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} + +void *internal_hashmap_steal_first_key(HashmapBase *h); +static inline void *hashmap_steal_first_key(Hashmap *h) { + return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); +} +static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { + return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); +} + +void *internal_hashmap_first_key(HashmapBase *h) _pure_; +static inline void *hashmap_first_key(Hashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h)); +} +static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h)); +} + +void *internal_hashmap_first(HashmapBase *h) _pure_; +static inline void *hashmap_first(Hashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); +} +static inline void *ordered_hashmap_first(OrderedHashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); +} + +/* no hashmap_next */ +void *ordered_hashmap_next(OrderedHashmap *h, const void *key); + +char **internal_hashmap_get_strv(HashmapBase *h); +static inline char **hashmap_get_strv(Hashmap *h) { + return internal_hashmap_get_strv(HASHMAP_BASE(h)); +} +static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { + return internal_hashmap_get_strv(HASHMAP_BASE(h)); +} + +/* + * Hashmaps are iterated in unpredictable order. + * OrderedHashmaps are an exception to this. They are iterated in the order + * the entries were inserted. + * It is safe to remove the current entry. + */ +#define HASHMAP_FOREACH(e, h, i) \ + for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) + +#define ORDERED_HASHMAP_FOREACH(e, h, i) \ + for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) + +#define HASHMAP_FOREACH_KEY(e, k, h, i) \ + for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) + +#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ + for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) + +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); + +#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) +#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) +#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) +#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) +#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) +#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) diff --git a/src/libbasic/include/basic/hexdecoct.h b/src/libbasic/include/basic/hexdecoct.h new file mode 100644 index 0000000000..1ba2f69ebd --- /dev/null +++ b/src/libbasic/include/basic/hexdecoct.h @@ -0,0 +1,56 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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); + +ssize_t base64mem(const void *p, size_t l, char **out); +int base64_append(char **prefix, int plen, + const void *p, size_t l, + int margin, int width); +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/libbasic/include/basic/hostname-util.h b/src/libbasic/include/basic/hostname-util.h new file mode 100644 index 0000000000..7af4e6c7ec --- /dev/null +++ b/src/libbasic/include/basic/hostname-util.h @@ -0,0 +1,41 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" + +bool hostname_is_set(void); + +char* gethostname_malloc(void); +int gethostname_strict(char **ret); + +bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; +char* hostname_cleanup(char *s); + +#define machine_name_is_valid(s) hostname_is_valid(s, false) + +bool is_localhost(const char *hostname); +bool is_gateway_hostname(const char *hostname); + +int sethostname_idempotent(const char *s); + +int read_hostname_config(const char *path, char **hostname); diff --git a/src/libbasic/include/basic/in-addr-util.h b/src/libbasic/include/basic/in-addr-util.h new file mode 100644 index 0000000000..d60064aef8 --- /dev/null +++ b/src/libbasic/include/basic/in-addr-util.h @@ -0,0 +1,64 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "macro.h" +#include "util.h" + +union in_addr_union { + struct in_addr in; + struct in6_addr in6; +}; + +struct in_addr_data { + int family; + union in_addr_union address; +}; + +bool in4_addr_is_null(const struct in_addr *a); +bool in6_addr_is_null(const struct in6_addr *a); + +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); +int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); +int in_addr_from_string(int family, const char *s, union in_addr_union *ret); +int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); +int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); +unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); +struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); +int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); +int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); +int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); + +static inline size_t FAMILY_ADDRESS_SIZE(int family) { + assert(family == AF_INET || family == AF_INET6); + return family == AF_INET6 ? 16 : 4; +} + +#define IN_ADDR_NULL ((union in_addr_union) {}) diff --git a/src/libbasic/include/basic/io-util.h b/src/libbasic/include/basic/io-util.h new file mode 100644 index 0000000000..4684ed3bfc --- /dev/null +++ b/src/libbasic/include/basic/io-util.h @@ -0,0 +1,95 @@ +#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 . +***/ + +#include +#include +#include +#include +#include + +#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; +} + +static inline bool FILE_SIZE_VALID(uint64_t l) { + /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than + * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ + + return (l >> 63) == 0; +} + +static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { + + /* Same as above, but allows one extra value: -1 as indication for infinity. */ + + if (l == (uint64_t) -1) + return true; + + return FILE_SIZE_VALID(l); + +} diff --git a/src/libbasic/include/basic/ioprio.h b/src/libbasic/include/basic/ioprio.h new file mode 100644 index 0000000000..d8bb6eb497 --- /dev/null +++ b/src/libbasic/include/basic/ioprio.h @@ -0,0 +1,55 @@ +#ifndef IOPRIO_H +#define IOPRIO_H + +/* This is minimal version of Linux' linux/ioprio.h header file, which + * is licensed GPL2 */ + +#include +#include + +/* + * Gives us 8 prio classes with 13-bits of data for each class + */ +#define IOPRIO_BITS (16) +#define IOPRIO_CLASS_SHIFT (13) +#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) + +#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) +#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) +#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) + +#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) + +/* + * These are the io priority groups as implemented by CFQ. RT is the realtime + * class, it always gets premium service. BE is the best-effort scheduling + * class, the default for any process. IDLE is the idle scheduling class, it + * is only served when no one else is using the disk. + */ +enum { + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE, +}; + +/* + * 8 best effort priority levels are supported + */ +#define IOPRIO_BE_NR (8) + +enum { + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, +}; + +static inline int ioprio_set(int which, int who, int ioprio) { + return syscall(__NR_ioprio_set, which, who, ioprio); +} + +static inline int ioprio_get(int which, int who) { + return syscall(__NR_ioprio_get, which, who); +} + +#endif diff --git a/src/libbasic/include/basic/label.h b/src/libbasic/include/basic/label.h new file mode 100644 index 0000000000..3e9251aa71 --- /dev/null +++ b/src/libbasic/include/basic/label.h @@ -0,0 +1,28 @@ +#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 . +***/ + +#include +#include + +int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); + +int mkdir_label(const char *path, mode_t mode); +int symlink_label(const char *old_path, const char *new_path); diff --git a/src/libbasic/include/basic/list.h b/src/libbasic/include/basic/list.h new file mode 100644 index 0000000000..5962aa4211 --- /dev/null +++ b/src/libbasic/include/basic/list.h @@ -0,0 +1,182 @@ +#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 . +***/ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define LIST_HEAD(t,name) \ + t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define LIST_FIELDS(t,name) \ + t *name##_next, *name##_prev + +/* Initialize the list's head */ +#define LIST_HEAD_INIT(head) \ + do { \ + (head) = NULL; } \ + while (false) + +/* Initialize a list item */ +#define LIST_INIT(name,item) \ + do { \ + typeof(*(item)) *_item = (item); \ + assert(_item); \ + _item->name##_prev = _item->name##_next = NULL; \ + } while (false) + +/* Prepend an item to the list */ +#define LIST_PREPEND(name,head,item) \ + do { \ + typeof(*(head)) **_head = &(head), *_item = (item); \ + assert(_item); \ + if ((_item->name##_next = *_head)) \ + _item->name##_next->name##_prev = _item; \ + _item->name##_prev = NULL; \ + *_head = _item; \ + } while (false) + +/* Append an item to the list */ +#define LIST_APPEND(name,head,item) \ + do { \ + typeof(*(head)) *_tail; \ + LIST_FIND_TAIL(name,head,_tail); \ + LIST_INSERT_AFTER(name,head,_tail,item); \ + } while (false) + +/* Remove an item from the list */ +#define LIST_REMOVE(name,head,item) \ + do { \ + typeof(*(head)) **_head = &(head), *_item = (item); \ + assert(_item); \ + if (_item->name##_next) \ + _item->name##_next->name##_prev = _item->name##_prev; \ + if (_item->name##_prev) \ + _item->name##_prev->name##_next = _item->name##_next; \ + else { \ + assert(*_head == _item); \ + *_head = _item->name##_next; \ + } \ + _item->name##_next = _item->name##_prev = NULL; \ + } while (false) + +/* Find the head of the list */ +#define LIST_FIND_HEAD(name,item,head) \ + do { \ + typeof(*(item)) *_item = (item); \ + if (!_item) \ + (head) = NULL; \ + else { \ + while (_item->name##_prev) \ + _item = _item->name##_prev; \ + (head) = _item; \ + } \ + } while (false) + +/* Find the tail of the list */ +#define LIST_FIND_TAIL(name,item,tail) \ + do { \ + typeof(*(item)) *_item = (item); \ + if (!_item) \ + (tail) = NULL; \ + else { \ + while (_item->name##_next) \ + _item = _item->name##_next; \ + (tail) = _item; \ + } \ + } while (false) + +/* Insert an item after another one (a = where, b = what) */ +#define LIST_INSERT_AFTER(name,head,a,b) \ + do { \ + typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ + assert(_b); \ + if (!_a) { \ + if ((_b->name##_next = *_head)) \ + _b->name##_next->name##_prev = _b; \ + _b->name##_prev = NULL; \ + *_head = _b; \ + } else { \ + if ((_b->name##_next = _a->name##_next)) \ + _b->name##_next->name##_prev = _b; \ + _b->name##_prev = _a; \ + _a->name##_next = _b; \ + } \ + } while (false) + +/* Insert an item before another one (a = where, b = what) */ +#define LIST_INSERT_BEFORE(name,head,a,b) \ + do { \ + typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ + assert(_b); \ + if (!_a) { \ + if (!*_head) { \ + _b->name##_next = NULL; \ + _b->name##_prev = NULL; \ + *_head = _b; \ + } else { \ + typeof(*(head)) *_tail = (head); \ + while (_tail->name##_next) \ + _tail = _tail->name##_next; \ + _b->name##_next = NULL; \ + _b->name##_prev = _tail; \ + _tail->name##_next = _b; \ + } \ + } else { \ + if ((_b->name##_prev = _a->name##_prev)) \ + _b->name##_prev->name##_next = _b; \ + _b->name##_next = _a; \ + _a->name##_prev = _b; \ + } \ + } while (false) + +#define LIST_JUST_US(name,item) \ + (!(item)->name##_prev && !(item)->name##_next) \ + +#define LIST_FOREACH(name,i,head) \ + for ((i) = (head); (i); (i) = (i)->name##_next) + +#define LIST_FOREACH_SAFE(name,i,n,head) \ + for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) + +#define LIST_FOREACH_BEFORE(name,i,p) \ + for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) + +#define LIST_FOREACH_AFTER(name,i,p) \ + for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) + +/* Iterate through all the members of the list p is included in, but skip over p */ +#define LIST_FOREACH_OTHERS(name,i,p) \ + for (({ \ + (i) = (p); \ + while ((i) && (i)->name##_prev) \ + (i) = (i)->name##_prev; \ + if ((i) == (p)) \ + (i) = (p)->name##_next; \ + }); \ + (i); \ + (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) + +/* Loop starting from p->next until p->prev. + p can be adjusted meanwhile. */ +#define LIST_LOOP_BUT_ONE(name,i,head,p) \ + for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ + (i) != (p); \ + (i) = (i)->name##_next ? (i)->name##_next : (head)) diff --git a/src/libbasic/include/basic/locale-util.h b/src/libbasic/include/basic/locale-util.h new file mode 100644 index 0000000000..0630a034ab --- /dev/null +++ b/src/libbasic/include/basic/locale-util.h @@ -0,0 +1,73 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "macro.h" + +typedef enum LocaleVariable { + /* We don't list LC_ALL here on purpose. People should be + * using LANG instead. */ + + VARIABLE_LANG, + VARIABLE_LANGUAGE, + VARIABLE_LC_CTYPE, + VARIABLE_LC_NUMERIC, + VARIABLE_LC_TIME, + VARIABLE_LC_COLLATE, + VARIABLE_LC_MONETARY, + VARIABLE_LC_MESSAGES, + VARIABLE_LC_PAPER, + VARIABLE_LC_NAME, + VARIABLE_LC_ADDRESS, + VARIABLE_LC_TELEPHONE, + VARIABLE_LC_MEASUREMENT, + VARIABLE_LC_IDENTIFICATION, + _VARIABLE_LC_MAX, + _VARIABLE_LC_INVALID = -1 +} 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 { + TREE_VERTICAL, + TREE_BRANCH, + TREE_RIGHT, + TREE_SPACE, + TRIANGULAR_BULLET, + BLACK_CIRCLE, + ARROW, + MDASH, + _SPECIAL_GLYPH_MAX +} SpecialGlyph; + +const char *special_glyph(SpecialGlyph code) _const_; + +const char* locale_variable_to_string(LocaleVariable i) _const_; +LocaleVariable locale_variable_from_string(const char *s) _pure_; diff --git a/src/libbasic/include/basic/lockfile-util.h b/src/libbasic/include/basic/lockfile-util.h new file mode 100644 index 0000000000..22491ee8e1 --- /dev/null +++ b/src/libbasic/include/basic/lockfile-util.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" +#include "missing.h" + +typedef struct LockFile { + char *path; + int fd; + int operation; +} LockFile; + +int make_lock_file(const char *p, int operation, LockFile *ret); +int make_lock_file_for(const char *p, int operation, LockFile *ret); +void release_lock_file(LockFile *f); + +#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) + +#define LOCK_FILE_INIT { .fd = -1, .path = NULL } diff --git a/src/libbasic/include/basic/log.h b/src/libbasic/include/basic/log.h new file mode 100644 index 0000000000..d2a22b5829 --- /dev/null +++ b/src/libbasic/include/basic/log.h @@ -0,0 +1,249 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "macro.h" + +typedef enum LogTarget{ + LOG_TARGET_CONSOLE, + LOG_TARGET_CONSOLE_PREFIXED, + LOG_TARGET_KMSG, + LOG_TARGET_JOURNAL, + LOG_TARGET_JOURNAL_OR_KMSG, + LOG_TARGET_SYSLOG, + LOG_TARGET_SYSLOG_OR_KMSG, + LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ + LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ + LOG_TARGET_NULL, + _LOG_TARGET_MAX, + _LOG_TARGET_INVALID = -1 +} LogTarget; + +void log_set_target(LogTarget target); +void log_set_max_level(int level); +void log_set_facility(int facility); + +int log_set_target_from_string(const char *e); +int log_set_max_level_from_string(const char *e); + +void log_show_color(bool b); +bool log_get_show_color(void) _pure_; +void log_show_location(bool b); +bool log_get_show_location(void) _pure_; + +int log_show_color_from_string(const char *e); +int log_show_location_from_string(const char *e); + +LogTarget log_get_target(void) _pure_; +int log_get_max_level(void) _pure_; + +int log_open(void); +void log_close(void); +void log_forget_fds(void); + +void log_close_syslog(void); +void log_close_journal(void); +void log_close_kmsg(void); +void log_close_console(void); + +void log_parse_environment(void); + +int log_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) _printf_(6,7); + +int log_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, + va_list ap) _printf_(6,0); + +int log_object_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, ...) _printf_(8,9); + +int log_object_internalv( + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, + va_list ap) _printf_(8,0); + +int log_struct_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) _printf_(6,0) _sentinel_; + +int log_oom_internal( + const char *file, + int line, + const char *func); + +int log_format_iovec( + struct iovec *iovec, + unsigned iovec_len, + unsigned *n, + bool newline_separator, + int error, + const char *format, + va_list ap); + +/* This modifies the buffer passed! */ +int log_dump_internal( + int level, + int error, + const char *file, + int line, + const char *func, + char *buffer); + +/* Logging for various assertions */ +noreturn void log_assert_failed( + const char *text, + const char *file, + int line, + const char *func); + +noreturn void log_assert_failed_unreachable( + const char *text, + const char *file, + int line, + const char *func); + +void log_assert_failed_return( + const char *text, + const char *file, + int line, + const char *func); + +/* Logging with level */ +#define log_full_errno(level, error, ...) \ + ({ \ + int _level = (level), _e = (error); \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + : -abs(_e); \ + }) + +#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) + +/* Normal logging */ +#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) +#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) +#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) +#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) +#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) + +/* Logging triggered by an errno-like error */ +#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) +#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) +#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) +#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) +#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) + +#ifdef LOG_TRACE +# define log_trace(...) log_debug(__VA_ARGS__) +#else +# define log_trace(...) do {} while (0) +#endif + +/* Structured logging */ +#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) + +/* This modifies the buffer passed! */ +#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) + +#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) + +bool log_on_console(void) _pure_; + +const char *log_target_to_string(LogTarget target) _const_; +LogTarget log_target_from_string(const char *s) _pure_; + +/* Helpers to prepare various fields for structured logging */ +#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ +#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) + +void log_received_signal(int level, const struct signalfd_siginfo *si); + +void log_set_upgrade_syslog_to_journal(bool b); + +int log_syntax_internal( + const char *unit, + int level, + const char *config_file, + unsigned config_line, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) _printf_(9, 10); + +#define log_syntax(unit, level, config_file, config_line, error, ...) \ + ({ \ + int _level = (level), _e = (error); \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + : -abs(_e); \ + }) + +#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)); \ + } \ + }) diff --git a/src/libbasic/include/basic/login-util.h b/src/libbasic/include/basic/login-util.h new file mode 100644 index 0000000000..b01ee25c88 --- /dev/null +++ b/src/libbasic/include/basic/login-util.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +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/libbasic/include/basic/macro.h b/src/libbasic/include/basic/macro.h new file mode 100644 index 0000000000..6b2aeb933f --- /dev/null +++ b/src/libbasic/include/basic/macro.h @@ -0,0 +1,415 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include + +#define _printf_(a,b) __attribute__ ((format (printf, a, b))) +#ifdef __clang__ +# define _alloc_(...) +#else +# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) +#endif +#define _sentinel_ __attribute__ ((sentinel)) +#define _unused_ __attribute__ ((unused)) +#define _destructor_ __attribute__ ((destructor)) +#define _pure_ __attribute__ ((pure)) +#define _const_ __attribute__ ((const)) +#define _deprecated_ __attribute__ ((deprecated)) +#define _packed_ __attribute__ ((packed)) +#define _malloc_ __attribute__ ((malloc)) +#define _weak_ __attribute__ ((weak)) +#define _likely_(x) (__builtin_expect(!!(x),1)) +#define _unlikely_(x) (__builtin_expect(!!(x),0)) +#define _public_ __attribute__ ((visibility("default"))) +#define _hidden_ __attribute__ ((visibility("hidden"))) +#define _weakref_(x) __attribute__((weakref(#x))) +#define _alignas_(x) __attribute__((aligned(__alignof(x)))) +#define _cleanup_(x) __attribute__((cleanup(x))) + +/* Temporarily disable some warnings */ +#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") + +#define DISABLE_WARNING_FORMAT_NONLITERAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") + +#define DISABLE_WARNING_MISSING_PROTOTYPES \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") + +#define DISABLE_WARNING_NONNULL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wnonnull\"") + +#define DISABLE_WARNING_SHADOW \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wshadow\"") + +#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") + +#define REENABLE_WARNING \ + _Pragma("GCC diagnostic pop") + +/* automake test harness */ +#define EXIT_TEST_SKIP 77 + +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + +#define XCONCATENATE(x, y) x ## y +#define CONCATENATE(x, y) XCONCATENATE(x, y) + +#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) +#define UNIQ __COUNTER__ + +/* builtins */ +#if __SIZEOF_INT__ == 4 +#define BUILTIN_FFS_U32(x) __builtin_ffs(x); +#elif __SIZEOF_LONG__ == 4 +#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); +#else +#error "neither int nor long are four bytes long?!?" +#endif + +/* Rounds up */ + +#define ALIGN4(l) (((l) + 3) & ~3) +#define ALIGN8(l) (((l) + 7) & ~7) + +#if __SIZEOF_POINTER__ == 8 +#define ALIGN(l) ALIGN8(l) +#elif __SIZEOF_POINTER__ == 4 +#define ALIGN(l) ALIGN4(l) +#else +#error "Wut? Pointers are neither 4 nor 8 bytes long?" +#endif + +#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) +#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) +#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) + +static inline size_t ALIGN_TO(size_t l, size_t ali) { + return ((l + ali - 1) & ~(ali - 1)); +} + +#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) + +/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ +static inline unsigned long ALIGN_POWER2(unsigned long u) { + /* clz(0) is undefined */ + if (u == 1) + return 1; + + /* left-shift overflow is undefined */ + if (__builtin_clzl(u - 1UL) < 1) + return 0; + + return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); +} + +#define ELEMENTSOF(x) \ + __extension__ (__builtin_choose_expr( \ + !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ + sizeof(x)/sizeof((x)[0]), \ + (void)0)) +/* + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + */ +#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) +#define __container_of(uniq, ptr, type, member) \ + __extension__ ({ \ + const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ + (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \ + }) + +#undef MAX +#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) +#define __MAX(aq, a, bq, b) \ + __extension__ ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MAX(_A, _B) \ + __extension__ (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) > (_B)) ? (_A) : (_B), \ + (void)0)) + +/* takes two types and returns the size of the larger one */ +#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) + +#define MAX3(x,y,z) \ + __extension__ ({ \ + const typeof(x) _c = MAX(x,y); \ + MAX(_c, z); \ + }) + +#undef MIN +#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) +#define __MIN(aq, a, bq, b) \ + __extension__ ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ + }) + +#define MIN3(x,y,z) \ + __extension__ ({ \ + const typeof(x) _c = MIN(x,y); \ + MIN(_c, z); \ + }) + +#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) +#define __LESS_BY(aq, a, bq, b) \ + __extension__ ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \ + }) + +#undef CLAMP +#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) +#define __CLAMP(xq, x, lowq, low, highq, high) \ + __extension__ ({ \ + const typeof(x) UNIQ_T(X,xq) = (x); \ + const typeof(low) UNIQ_T(LOW,lowq) = (low); \ + const typeof(high) UNIQ_T(HIGH,highq) = (high); \ + UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \ + UNIQ_T(HIGH,highq) : \ + UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \ + UNIQ_T(LOW,lowq) : \ + UNIQ_T(X,xq); \ + }) + +/* [(x + y - 1) / y] suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the + * quotient and the remainder, so both should be equally fast. */ +#define DIV_ROUND_UP(_x, _y) \ + __extension__ ({ \ + const typeof(_x) __x = (_x); \ + const typeof(_y) __y = (_y); \ + (__x / __y + !!(__x % __y)); \ + }) + +#define assert_message_se(expr, message) \ + do { \ + if (_unlikely_(!(expr))) \ + log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) + +#define assert_se(expr) assert_message_se(expr, #expr) + +/* We override the glibc assert() here. */ +#undef assert +#ifdef NDEBUG +#define assert(expr) do {} while (false) +#else +#define assert(expr) assert_message_se(expr, #expr) +#endif + +#define assert_not_reached(t) \ + do { \ + log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) + +#if defined(static_assert) +/* static_assert() is sometimes defined in a way that trips up + * -Wdeclaration-after-statement, hence let's temporarily turn off + * this warning around it. */ +#define assert_cc(expr) \ + DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ + static_assert(expr, #expr); \ + REENABLE_WARNING +#else +#define assert_cc(expr) \ + DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ + struct CONCATENATE(_assert_struct_, __COUNTER__) { \ + char x[(expr) ? 0 : -1]; \ + }; \ + REENABLE_WARNING +#endif + +#define assert_log(expr, message) ((_likely_(expr)) \ + ? (true) \ + : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) + +#define assert_return(expr, r) \ + do { \ + if (!assert_log(expr, #expr)) \ + return (r); \ + } while (false) + +#define assert_return_errno(expr, r, err) \ + do { \ + if (!assert_log(expr, #expr)) { \ + errno = err; \ + return (r); \ + } \ + } while (false) + +#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) +#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) +#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) +#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) +#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) +#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) +#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) +#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) +#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) +#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) +#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) +#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) + +#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) + +#define char_array_0(x) x[sizeof(x)-1] = 0; + +/* 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 + * types). Includes space for the trailing NUL. */ +#define DECIMAL_STR_MAX(type) \ + (2+(sizeof(type) <= 1 ? 3 : \ + sizeof(type) <= 2 ? 5 : \ + 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 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; \ + }) + +#define SWAP_TWO(x, y) do { \ + typeof(x) _t = (x); \ + (x) = (y); \ + (y) = (_t); \ + } while (false) + +/* Define C11 thread_local attribute even on older gcc compiler + * version */ +#ifndef thread_local +/* + * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ + * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + */ +#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) +#define thread_local _Thread_local +#else +#define thread_local __thread +#endif +#endif + +/* Define C11 noreturn without and even on older gcc + * compiler versions */ +#ifndef noreturn +#if __STDC_VERSION__ >= 201112L +#define noreturn _Noreturn +#else +#define noreturn __attribute__((noreturn)) +#endif +#endif + +#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ + static inline void func##p(type *p) { \ + if (*p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#include "log.h" diff --git a/src/libbasic/include/basic/memfd-util.h b/src/libbasic/include/basic/memfd-util.h new file mode 100644 index 0000000000..46d4989e4c --- /dev/null +++ b/src/libbasic/include/basic/memfd-util.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +int memfd_new(const char *name); +int memfd_new_and_map(const char *name, size_t sz, void **p); + +int memfd_map(int fd, uint64_t offset, size_t size, void **p); + +int memfd_set_sealed(int fd); +int memfd_get_sealed(int fd); + +int memfd_get_size(int fd, uint64_t *sz); +int memfd_set_size(int fd, uint64_t sz); diff --git a/src/libbasic/include/basic/mempool.h b/src/libbasic/include/basic/mempool.h new file mode 100644 index 0000000000..0618b8dd22 --- /dev/null +++ b/src/libbasic/include/basic/mempool.h @@ -0,0 +1,47 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011-2014 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 . +***/ + +#include + +struct pool; + +struct mempool { + struct pool *first_pool; + void *freelist; + size_t tile_size; + unsigned at_least; +}; + +void* mempool_alloc_tile(struct mempool *mp); +void* mempool_alloc0_tile(struct mempool *mp); +void mempool_free_tile(struct mempool *mp, void *p); + +#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ +static struct mempool pool_name = { \ + .tile_size = sizeof(tile_type), \ + .at_least = alloc_at_least, \ +} + + +#ifdef VALGRIND +void mempool_drop(struct mempool *mp); +#endif diff --git a/src/libbasic/include/basic/missing.h b/src/libbasic/include/basic/missing.h new file mode 100644 index 0000000000..8721b075bb --- /dev/null +++ b/src/libbasic/include/basic/missing.h @@ -0,0 +1,1045 @@ +#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 . +***/ + +/* Missing glibc definitions to access certain kernel APIs */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_AUDIT +#include +#endif + +#ifdef ARCH_MIPS +#include +#endif + +#ifdef HAVE_LINUX_BTRFS_H +#include +#endif + +#include "macro.h" + +#ifndef RLIMIT_RTTIME +#define RLIMIT_RTTIME 15 +#endif + +/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */ +#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS) + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_SETPIPE_SZ +#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) +#endif + +#ifndef F_GETPIPE_SZ +#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef F_OFD_GETLK +#define F_OFD_GETLK 36 +#define F_OFD_SETLK 37 +#define F_OFD_SETLKW 38 +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef IP_FREEBIND +#define IP_FREEBIND 15 +#endif + +#ifndef OOM_SCORE_ADJ_MIN +#define OOM_SCORE_ADJ_MIN (-1000) +#endif + +#ifndef OOM_SCORE_ADJ_MAX +#define OOM_SCORE_ADJ_MAX 1000 +#endif + +#ifndef AUDIT_SERVICE_START +#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ +#endif + +#ifndef AUDIT_SERVICE_STOP +#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ +#endif + +#ifndef TIOCVHANGUP +#define TIOCVHANGUP 0x5437 +#endif + +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#ifndef NETLINK_LIST_MEMBERSHIPS +#define NETLINK_LIST_MEMBERSHIPS 9 +#endif + +#ifndef SOL_SCTP +#define SOL_SCTP 132 +#endif + +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif + +#ifndef GRND_RANDOM +#define GRND_RANDOM 0x0002 +#endif + +#ifndef BTRFS_IOCTL_MAGIC +#define BTRFS_IOCTL_MAGIC 0x94 +#endif + +#ifndef BTRFS_PATH_NAME_MAX +#define BTRFS_PATH_NAME_MAX 4087 +#endif + +#ifndef BTRFS_DEVICE_PATH_NAME_MAX +#define BTRFS_DEVICE_PATH_NAME_MAX 1024 +#endif + +#ifndef BTRFS_FSID_SIZE +#define BTRFS_FSID_SIZE 16 +#endif + +#ifndef BTRFS_UUID_SIZE +#define BTRFS_UUID_SIZE 16 +#endif + +#ifndef BTRFS_SUBVOL_RDONLY +#define BTRFS_SUBVOL_RDONLY (1ULL << 1) +#endif + +#ifndef BTRFS_SUBVOL_NAME_MAX +#define BTRFS_SUBVOL_NAME_MAX 4039 +#endif + +#ifndef BTRFS_INO_LOOKUP_PATH_MAX +#define BTRFS_INO_LOOKUP_PATH_MAX 4080 +#endif + +#ifndef BTRFS_SEARCH_ARGS_BUFSIZE +#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) +#endif + +#ifndef BTRFS_QGROUP_LEVEL_SHIFT +#define BTRFS_QGROUP_LEVEL_SHIFT 48 +#endif + +#ifndef HAVE_LINUX_BTRFS_H +struct btrfs_ioctl_vol_args { + int64_t fd; + char name[BTRFS_PATH_NAME_MAX + 1]; +}; + +struct btrfs_qgroup_limit { + __u64 flags; + __u64 max_rfer; + __u64 max_excl; + __u64 rsv_rfer; + __u64 rsv_excl; +}; + +struct btrfs_qgroup_inherit { + __u64 flags; + __u64 num_qgroups; + __u64 num_ref_copies; + __u64 num_excl_copies; + struct btrfs_qgroup_limit lim; + __u64 qgroups[0]; +}; + +struct btrfs_ioctl_qgroup_limit_args { + __u64 qgroupid; + struct btrfs_qgroup_limit lim; +}; + +struct btrfs_ioctl_vol_args_v2 { + __s64 fd; + __u64 transid; + __u64 flags; + union { + struct { + __u64 size; + struct btrfs_qgroup_inherit *qgroup_inherit; + }; + __u64 unused[4]; + }; + char name[BTRFS_SUBVOL_NAME_MAX + 1]; +}; + +struct btrfs_ioctl_dev_info_args { + uint64_t devid; /* in/out */ + uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */ + uint64_t bytes_used; /* out */ + uint64_t total_bytes; /* out */ + uint64_t unused[379]; /* pad to 4k */ + char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ +}; + +struct btrfs_ioctl_fs_info_args { + uint64_t max_id; /* out */ + uint64_t num_devices; /* out */ + uint8_t fsid[BTRFS_FSID_SIZE]; /* out */ + uint64_t reserved[124]; /* pad to 1k */ +}; + +struct btrfs_ioctl_ino_lookup_args { + __u64 treeid; + __u64 objectid; + char name[BTRFS_INO_LOOKUP_PATH_MAX]; +}; + +struct btrfs_ioctl_search_key { + /* which root are we searching. 0 is the tree of tree roots */ + __u64 tree_id; + + /* keys returned will be >= min and <= max */ + __u64 min_objectid; + __u64 max_objectid; + + /* keys returned will be >= min and <= max */ + __u64 min_offset; + __u64 max_offset; + + /* max and min transids to search for */ + __u64 min_transid; + __u64 max_transid; + + /* keys returned will be >= min and <= max */ + __u32 min_type; + __u32 max_type; + + /* + * how many items did userland ask for, and how many are we + * returning + */ + __u32 nr_items; + + /* align to 64 bits */ + __u32 unused; + + /* some extra for later */ + __u64 unused1; + __u64 unused2; + __u64 unused3; + __u64 unused4; +}; + +struct btrfs_ioctl_search_header { + __u64 transid; + __u64 objectid; + __u64 offset; + __u32 type; + __u32 len; +}; + + +struct btrfs_ioctl_search_args { + struct btrfs_ioctl_search_key key; + char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; +}; + +struct btrfs_ioctl_clone_range_args { + __s64 src_fd; + __u64 src_offset, src_length; + __u64 dest_offset; +}; + +#define BTRFS_QUOTA_CTL_ENABLE 1 +#define BTRFS_QUOTA_CTL_DISABLE 2 +#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 +struct btrfs_ioctl_quota_ctl_args { + __u64 cmd; + __u64 status; +}; +#endif + +#ifndef BTRFS_IOC_DEFRAG +#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ + struct btrfs_ioctl_vol_args) +#endif + +#ifndef BTRFS_IOC_RESIZE +#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \ + struct btrfs_ioctl_vol_args) +#endif + +#ifndef BTRFS_IOC_CLONE +#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) +#endif + +#ifndef BTRFS_IOC_CLONE_RANGE +#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \ + struct btrfs_ioctl_clone_range_args) +#endif + +#ifndef BTRFS_IOC_SUBVOL_CREATE +#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ + struct btrfs_ioctl_vol_args) +#endif + +#ifndef BTRFS_IOC_SNAP_DESTROY +#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ + struct btrfs_ioctl_vol_args) +#endif + +#ifndef BTRFS_IOC_TREE_SEARCH +#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ + struct btrfs_ioctl_search_args) +#endif + +#ifndef BTRFS_IOC_INO_LOOKUP +#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ + struct btrfs_ioctl_ino_lookup_args) +#endif + +#ifndef BTRFS_IOC_SNAP_CREATE_V2 +#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ + struct btrfs_ioctl_vol_args_v2) +#endif + +#ifndef BTRFS_IOC_SUBVOL_GETFLAGS +#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64) +#endif + +#ifndef BTRFS_IOC_SUBVOL_SETFLAGS +#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64) +#endif + +#ifndef BTRFS_IOC_DEV_INFO +#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \ + struct btrfs_ioctl_dev_info_args) +#endif + +#ifndef BTRFS_IOC_FS_INFO +#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ + struct btrfs_ioctl_fs_info_args) +#endif + +#ifndef BTRFS_IOC_DEVICES_READY +#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \ + struct btrfs_ioctl_vol_args) +#endif + +#ifndef BTRFS_IOC_QUOTA_CTL +#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \ + struct btrfs_ioctl_quota_ctl_args) +#endif + +#ifndef BTRFS_IOC_QGROUP_LIMIT +#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \ + struct btrfs_ioctl_qgroup_limit_args) +#endif + +#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT +#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46) +#endif + +#ifndef BTRFS_FIRST_FREE_OBJECTID +#define BTRFS_FIRST_FREE_OBJECTID 256 +#endif + +#ifndef BTRFS_LAST_FREE_OBJECTID +#define BTRFS_LAST_FREE_OBJECTID -256ULL +#endif + +#ifndef BTRFS_ROOT_TREE_OBJECTID +#define BTRFS_ROOT_TREE_OBJECTID 1 +#endif + +#ifndef BTRFS_QUOTA_TREE_OBJECTID +#define BTRFS_QUOTA_TREE_OBJECTID 8ULL +#endif + +#ifndef BTRFS_ROOT_ITEM_KEY +#define BTRFS_ROOT_ITEM_KEY 132 +#endif + +#ifndef BTRFS_QGROUP_STATUS_KEY +#define BTRFS_QGROUP_STATUS_KEY 240 +#endif + +#ifndef BTRFS_QGROUP_INFO_KEY +#define BTRFS_QGROUP_INFO_KEY 242 +#endif + +#ifndef BTRFS_QGROUP_LIMIT_KEY +#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 + +#ifndef BTRFS_SUPER_MAGIC +#define BTRFS_SUPER_MAGIC 0x9123683E +#endif + +#ifndef CGROUP_SUPER_MAGIC +#define CGROUP_SUPER_MAGIC 0x27e0eb +#endif + +#ifndef CGROUP2_SUPER_MAGIC +#define CGROUP2_SUPER_MAGIC 0x63677270 +#endif + +#ifndef TMPFS_MAGIC +#define TMPFS_MAGIC 0x01021994 +#endif + +#ifndef MQUEUE_MAGIC +#define MQUEUE_MAGIC 0x19800202 +#endif + +#ifndef SECURITYFS_MAGIC +#define SECURITYFS_MAGIC 0x73636673 +#endif + +#ifndef TRACEFS_MAGIC +#define TRACEFS_MAGIC 0x74726163 +#endif + +#ifndef BPF_FS_MAGIC +#define BPF_FS_MAGIC 0xcafe4a11 +#endif + +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif + +#ifndef MS_PRIVATE +#define MS_PRIVATE (1 << 18) +#endif + +#ifndef SCM_SECURITY +#define SCM_SECURITY 0x03 +#endif + +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1<<24) +#endif + +#ifndef MS_REC +#define MS_REC 16384 +#endif + +#ifndef MS_SHARED +#define MS_SHARED (1<<20) +#endif + +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + +#ifndef PR_SET_CHILD_SUBREAPER +#define PR_SET_CHILD_SUBREAPER 36 +#endif + +#ifndef MAX_HANDLE_SZ +#define MAX_HANDLE_SZ 128 +#endif + +#ifndef HAVE_SECURE_GETENV +# ifdef HAVE___SECURE_GETENV +# define secure_getenv __secure_getenv +# else +# error "neither secure_getenv nor __secure_getenv are available" +# endif +#endif + +#ifndef CIFS_MAGIC_NUMBER +# define CIFS_MAGIC_NUMBER 0xFF534D42 +#endif + +#ifndef TFD_TIMER_CANCEL_ON_SET +# define TFD_TIMER_CANCEL_ON_SET (1 << 1) +#endif + +#ifndef SO_REUSEPORT +# define SO_REUSEPORT 15 +#endif + +#ifndef EVIOCREVOKE +# define EVIOCREVOKE _IOW('E', 0x91, int) +#endif + +#ifndef DRM_IOCTL_SET_MASTER +# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +#endif + +#ifndef DRM_IOCTL_DROP_MASTER +# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +#endif + +#if defined(__i386__) || defined(__x86_64__) + +/* The precise definition of __O_TMPFILE is arch specific, so let's + * just define this on x86 where we know the value. */ + +#ifndef __O_TMPFILE +#define __O_TMPFILE 020000000 +#endif + +/* a horrid kludge trying to make sure that this will fail on old kernels */ +#ifndef O_TMPFILE +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#endif + +#endif + +#if !HAVE_DECL_LO_FLAGS_PARTSCAN +#define LO_FLAGS_PARTSCAN 8 +#endif + +#ifndef LOOP_CTL_REMOVE +#define LOOP_CTL_REMOVE 0x4C81 +#endif + +#ifndef LOOP_CTL_GET_FREE +#define LOOP_CTL_GET_FREE 0x4C82 +#endif + +#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE +#define IFLA_INET6_UNSPEC 0 +#define IFLA_INET6_FLAGS 1 +#define IFLA_INET6_CONF 2 +#define IFLA_INET6_STATS 3 +#define IFLA_INET6_MCAST 4 +#define IFLA_INET6_CACHEINFO 5 +#define IFLA_INET6_ICMP6STATS 6 +#define IFLA_INET6_TOKEN 7 +#define IFLA_INET6_ADDR_GEN_MODE 8 +#define __IFLA_INET6_MAX 9 + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +#define IN6_ADDR_GEN_MODE_EUI64 0 +#define IN6_ADDR_GEN_MODE_NONE 1 +#endif + +#if !HAVE_DECL_IN6_ADDR_GEN_MODE_STABLE_PRIVACY +#define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 +#endif + +#if !HAVE_DECL_IFLA_MACVLAN_FLAGS +#define IFLA_MACVLAN_UNSPEC 0 +#define IFLA_MACVLAN_MODE 1 +#define IFLA_MACVLAN_FLAGS 2 +#define __IFLA_MACVLAN_MAX 3 + +#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_IPVLAN_MODE +#define IFLA_IPVLAN_UNSPEC 0 +#define IFLA_IPVLAN_MODE 1 +#define __IFLA_IPVLAN_MAX 2 + +#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) + +#define IPVLAN_MODE_L2 0 +#define IPVLAN_MODE_L3 1 +#define IPVLAN_MAX 2 +#endif + +#if !HAVE_DECL_IFLA_VTI_REMOTE +#define IFLA_VTI_UNSPEC 0 +#define IFLA_VTI_LINK 1 +#define IFLA_VTI_IKEY 2 +#define IFLA_VTI_OKEY 3 +#define IFLA_VTI_LOCAL 4 +#define IFLA_VTI_REMOTE 5 +#define __IFLA_VTI_MAX 6 + +#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_PHYS_PORT_ID +#define IFLA_EXT_MASK 29 +#undef IFLA_PROMISCUITY +#define IFLA_PROMISCUITY 30 +#define IFLA_NUM_TX_QUEUES 31 +#define IFLA_NUM_RX_QUEUES 32 +#define IFLA_CARRIER 33 +#define IFLA_PHYS_PORT_ID 34 +#define __IFLA_MAX 35 + +#define IFLA_MAX (__IFLA_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_BOND_AD_INFO +#define IFLA_BOND_UNSPEC 0 +#define IFLA_BOND_MODE 1 +#define IFLA_BOND_ACTIVE_SLAVE 2 +#define IFLA_BOND_MIIMON 3 +#define IFLA_BOND_UPDELAY 4 +#define IFLA_BOND_DOWNDELAY 5 +#define IFLA_BOND_USE_CARRIER 6 +#define IFLA_BOND_ARP_INTERVAL 7 +#define IFLA_BOND_ARP_IP_TARGET 8 +#define IFLA_BOND_ARP_VALIDATE 9 +#define IFLA_BOND_ARP_ALL_TARGETS 10 +#define IFLA_BOND_PRIMARY 11 +#define IFLA_BOND_PRIMARY_RESELECT 12 +#define IFLA_BOND_FAIL_OVER_MAC 13 +#define IFLA_BOND_XMIT_HASH_POLICY 14 +#define IFLA_BOND_RESEND_IGMP 15 +#define IFLA_BOND_NUM_PEER_NOTIF 16 +#define IFLA_BOND_ALL_SLAVES_ACTIVE 17 +#define IFLA_BOND_MIN_LINKS 18 +#define IFLA_BOND_LP_INTERVAL 19 +#define IFLA_BOND_PACKETS_PER_SLAVE 20 +#define IFLA_BOND_AD_LACP_RATE 21 +#define IFLA_BOND_AD_SELECT 22 +#define IFLA_BOND_AD_INFO 23 +#define __IFLA_BOND_MAX 24 + +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_VLAN_PROTOCOL +#define IFLA_VLAN_UNSPEC 0 +#define IFLA_VLAN_ID 1 +#define IFLA_VLAN_FLAGS 2 +#define IFLA_VLAN_EGRESS_QOS 3 +#define IFLA_VLAN_INGRESS_QOS 4 +#define IFLA_VLAN_PROTOCOL 5 +#define __IFLA_VLAN_MAX 6 + +#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL +#define IFLA_VXLAN_UNSPEC 0 +#define IFLA_VXLAN_ID 1 +#define IFLA_VXLAN_GROUP 2 +#define IFLA_VXLAN_LINK 3 +#define IFLA_VXLAN_LOCAL 4 +#define IFLA_VXLAN_TTL 5 +#define IFLA_VXLAN_TOS 6 +#define IFLA_VXLAN_LEARNING 7 +#define IFLA_VXLAN_AGEING 8 +#define IFLA_VXLAN_LIMIT 9 +#define IFLA_VXLAN_PORT_RANGE 10 +#define IFLA_VXLAN_PROXY 11 +#define IFLA_VXLAN_RSC 12 +#define IFLA_VXLAN_L2MISS 13 +#define IFLA_VXLAN_L3MISS 14 +#define IFLA_VXLAN_PORT 15 +#define IFLA_VXLAN_GROUP6 16 +#define IFLA_VXLAN_LOCAL6 17 +#define IFLA_VXLAN_UDP_CSUM 18 +#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19 +#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20 +#define IFLA_VXLAN_REMCSUM_TX 21 +#define IFLA_VXLAN_REMCSUM_RX 22 +#define IFLA_VXLAN_GBP 23 +#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24 +#define __IFLA_VXLAN_MAX 25 + +#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT +#define IFLA_IPTUN_UNSPEC 0 +#define IFLA_IPTUN_LINK 1 +#define IFLA_IPTUN_LOCAL 2 +#define IFLA_IPTUN_REMOTE 3 +#define IFLA_IPTUN_TTL 4 +#define IFLA_IPTUN_TOS 5 +#define IFLA_IPTUN_ENCAP_LIMIT 6 +#define IFLA_IPTUN_FLOWINFO 7 +#define IFLA_IPTUN_FLAGS 8 +#define IFLA_IPTUN_PROTO 9 +#define IFLA_IPTUN_PMTUDISC 10 +#define IFLA_IPTUN_6RD_PREFIX 11 +#define IFLA_IPTUN_6RD_RELAY_PREFIX 12 +#define IFLA_IPTUN_6RD_PREFIXLEN 13 +#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14 +#define IFLA_IPTUN_ENCAP_TYPE 15 +#define IFLA_IPTUN_ENCAP_FLAGS 16 +#define IFLA_IPTUN_ENCAP_SPORT 17 +#define IFLA_IPTUN_ENCAP_DPORT 18 + +#define __IFLA_IPTUN_MAX 19 + +#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_GRE_ENCAP_DPORT +#define IFLA_GRE_UNSPEC 0 +#define IFLA_GRE_LINK 1 +#define IFLA_GRE_IFLAGS 2 +#define IFLA_GRE_OFLAGS 3 +#define IFLA_GRE_IKEY 4 +#define IFLA_GRE_OKEY 5 +#define IFLA_GRE_LOCAL 6 +#define IFLA_GRE_REMOTE 7 +#define IFLA_GRE_TTL 8 +#define IFLA_GRE_TOS 9 +#define IFLA_GRE_PMTUDISC 10 +#define IFLA_GRE_ENCAP_LIMIT 11 +#define IFLA_GRE_FLOWINFO 12 +#define IFLA_GRE_FLAGS 13 +#define IFLA_GRE_ENCAP_TYPE 14 +#define IFLA_GRE_ENCAP_FLAGS 15 +#define IFLA_GRE_ENCAP_SPORT 16 +#define IFLA_GRE_ENCAP_DPORT 17 + +#define __IFLA_GRE_MAX 18 + +#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO +#define IFLA_BRIDGE_FLAGS 0 +#define IFLA_BRIDGE_MODE 1 +#define IFLA_BRIDGE_VLAN_INFO 2 +#define __IFLA_BRIDGE_MAX 3 + +#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) +#endif + +#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN +#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ +#endif + +#ifndef BRIDGE_VLAN_INFO_RANGE_END +#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ +#endif + +#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID +#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_VLAN_FILTERING 7 +#define IFLA_BR_VLAN_PROTOCOL 8 +#define IFLA_BR_GROUP_FWD_MASK 9 +#define IFLA_BR_ROOT_ID 10 +#define IFLA_BR_BRIDGE_ID 11 +#define IFLA_BR_ROOT_PORT 12 +#define IFLA_BR_ROOT_PATH_COST 13 +#define IFLA_BR_TOPOLOGY_CHANGE 14 +#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15 +#define IFLA_BR_HELLO_TIMER 16 +#define IFLA_BR_TCN_TIMER 17 +#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18 +#define IFLA_BR_GC_TIMER 19 +#define IFLA_BR_GROUP_ADDR 20 +#define IFLA_BR_FDB_FLUSH 21 +#define IFLA_BR_MCAST_ROUTER 22 +#define IFLA_BR_MCAST_SNOOPING 23 +#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24 +#define IFLA_BR_MCAST_QUERIER 25 +#define IFLA_BR_MCAST_HASH_ELASTICITY 26 +#define IFLA_BR_MCAST_HASH_MAX 27 +#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28 +#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29 +#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30 +#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31 +#define IFLA_BR_MCAST_QUERIER_INTVL 32 +#define IFLA_BR_MCAST_QUERY_INTVL 33 +#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34 +#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35 +#define IFLA_BR_NF_CALL_IPTABLES 36 +#define IFLA_BR_NF_CALL_IP6TABLES 37 +#define IFLA_BR_NF_CALL_ARPTABLES 38 +#define IFLA_BR_VLAN_DEFAULT_PVID 39 +#define __IFLA_BR_MAX 40 + +#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 +#define IFLA_BRPORT_PRIORITY 2 +#define IFLA_BRPORT_COST 3 +#define IFLA_BRPORT_MODE 4 +#define IFLA_BRPORT_GUARD 5 +#define IFLA_BRPORT_PROTECT 6 +#define IFLA_BRPORT_FAST_LEAVE 7 +#define IFLA_BRPORT_LEARNING 8 +#define IFLA_BRPORT_UNICAST_FLOOD 9 +#define IFLA_BRPORT_LEARNING_SYNC 11 +#define __IFLA_BRPORT_MAX 12 + +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) +#endif + +#if !HAVE_DECL_IFLA_BRPORT_PROXYARP +#define IFLA_BRPORT_PROXYARP 10 +#endif + +#if !HAVE_DECL_IFLA_VRF_TABLE +#define IFLA_VRF_TABLE 1 +#endif + +#if !HAVE_DECL_NDA_IFINDEX +#define NDA_UNSPEC 0 +#define NDA_DST 1 +#define NDA_LLADDR 2 +#define NDA_CACHEINFO 3 +#define NDA_PROBES 4 +#define NDA_VLAN 5 +#define NDA_PORT 6 +#define NDA_VNI 7 +#define NDA_IFINDEX 8 +#define __NDA_MAX 9 + +#define NDA_MAX (__NDA_MAX - 1) +#endif + +#ifndef RTA_PREF +#define RTA_PREF 20 +#endif + +#ifndef IPV6_UNICAST_IF +#define IPV6_UNICAST_IF 76 +#endif + +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif + +#ifndef IFF_MULTI_QUEUE +#define IFF_MULTI_QUEUE 0x100 +#endif + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 +#endif + +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 +#endif + +#ifndef BOND_XMIT_POLICY_ENCAP23 +#define BOND_XMIT_POLICY_ENCAP23 3 +#endif + +#ifndef BOND_XMIT_POLICY_ENCAP34 +#define BOND_XMIT_POLICY_ENCAP34 4 +#endif + +#ifndef NET_ADDR_RANDOM +# define NET_ADDR_RANDOM 1 +#endif + +#ifndef NET_NAME_UNKNOWN +# define NET_NAME_UNKNOWN 0 +#endif + +#ifndef NET_NAME_ENUM +# define NET_NAME_ENUM 1 +#endif + +#ifndef NET_NAME_PREDICTABLE +# define NET_NAME_PREDICTABLE 2 +#endif + +#ifndef NET_NAME_USER +# define NET_NAME_USER 3 +#endif + +#ifndef NET_NAME_RENAMED +# define NET_NAME_RENAMED 4 +#endif + +#ifndef BPF_XOR +# define BPF_XOR 0xa0 +#endif + +/* Note that LOOPBACK_IFINDEX is currently not exported by the + * kernel/glibc, but hardcoded internally by the kernel. However, as + * it is exported to userspace indirectly via rtnetlink and the + * ioctls, and made use of widely we define it here too, in a way that + * is compatible with the kernel's internal definition. */ +#ifndef LOOPBACK_IFINDEX +#define LOOPBACK_IFINDEX 1 +#endif + +#if !HAVE_DECL_IFA_FLAGS +#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 + +#ifndef MAX_AUDIT_MESSAGE_LENGTH +#define MAX_AUDIT_MESSAGE_LENGTH 8970 +#endif + +#ifndef AUDIT_NLGRP_MAX +#define AUDIT_NLGRP_READLOG 1 +#endif + +#ifndef CAP_MAC_OVERRIDE +#define CAP_MAC_OVERRIDE 32 +#endif + +#ifndef CAP_MAC_ADMIN +#define CAP_MAC_ADMIN 33 +#endif + +#ifndef CAP_SYSLOG +#define CAP_SYSLOG 34 +#endif + +#ifndef CAP_WAKE_ALARM +#define CAP_WAKE_ALARM 35 +#endif + +#ifndef CAP_BLOCK_SUSPEND +#define CAP_BLOCK_SUSPEND 36 +#endif + +#ifndef CAP_AUDIT_READ +#define CAP_AUDIT_READ 37 +#endif + +#ifndef RENAME_NOREPLACE +#define RENAME_NOREPLACE (1 << 0) +#endif + +#ifndef KCMP_FILE +#define KCMP_FILE 0 +#endif + +#ifndef INPUT_PROP_POINTING_STICK +#define INPUT_PROP_POINTING_STICK 0x05 +#endif + +#ifndef INPUT_PROP_ACCELEROMETER +#define INPUT_PROP_ACCELEROMETER 0x06 +#endif + +#ifndef HAVE_KEY_SERIAL_T +typedef int32_t key_serial_t; +#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 + +/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway, + * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with + * linux/if.h. */ +#ifndef IF_OPER_UNKNOWN +#define IF_OPER_UNKNOWN 0 +#endif + +#ifndef IF_OPER_UP +#define IF_OPER_UP 6 + +#ifndef HAVE_CHAR32_T +#define char32_t uint32_t +#endif + +#ifndef HAVE_CHAR16_T +#define char16_t uint16_t +#endif + +#ifndef ETHERTYPE_LLDP +#define ETHERTYPE_LLDP 0x88cc +#endif + +#endif + +#include "missing_syscall.h" diff --git a/src/libbasic/include/basic/missing_syscall.h b/src/libbasic/include/basic/missing_syscall.h new file mode 100644 index 0000000000..e6fd67cb9d --- /dev/null +++ b/src/libbasic/include/basic/missing_syscall.h @@ -0,0 +1,300 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2016 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 . +***/ + +/* Missing glibc definitions to access certain kernel APIs */ + +#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); +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_MEMFD_CREATE +# ifndef __NR_memfd_create +# if defined __x86_64__ +# define __NR_memfd_create 319 +# elif defined __arm__ +# define __NR_memfd_create 385 +# elif defined __aarch64__ +# define __NR_memfd_create 279 +# elif defined __s390__ +# define __NR_memfd_create 350 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_memfd_create 5314 +# endif +# elif defined __i386__ +# define __NR_memfd_create 356 +# else +# warning "__NR_memfd_create unknown for your architecture" +# endif +# endif + +static inline int memfd_create(const char *name, unsigned int flags) { +# ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_GETRANDOM +# ifndef __NR_getrandom +# if defined __x86_64__ +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__arm__) +# define __NR_getrandom 384 +# elif defined(__aarch64__) +# define __NR_getrandom 278 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__s390x__) +# define __NR_getrandom 349 +# elif defined(__powerpc__) +# define __NR_getrandom 359 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_getrandom 4353 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_getrandom 6317 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_getrandom 5313 +# endif +# else +# warning "__NR_getrandom unknown for your architecture" +# endif +# endif + +static inline int getrandom(void *buffer, size_t count, unsigned flags) { +# ifdef __NR_getrandom + return syscall(__NR_getrandom, buffer, count, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_GETTID +static inline pid_t gettid(void) { + return (pid_t) syscall(SYS_gettid); +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_NAME_TO_HANDLE_AT +# ifndef __NR_name_to_handle_at +# if defined(__x86_64__) +# define __NR_name_to_handle_at 303 +# elif defined(__i386__) +# define __NR_name_to_handle_at 341 +# elif defined(__arm__) +# define __NR_name_to_handle_at 370 +# elif defined(__powerpc__) +# define __NR_name_to_handle_at 345 +# else +# error "__NR_name_to_handle_at is not defined" +# endif +# endif + +struct file_handle { + unsigned int handle_bytes; + int handle_type; + unsigned char f_handle[0]; +}; + +static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { +# ifdef __NR_name_to_handle_at + return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_SETNS +# ifndef __NR_setns +# if defined(__x86_64__) +# define __NR_setns 308 +# elif defined(__i386__) +# define __NR_setns 346 +# else +# error "__NR_setns is not defined" +# endif +# endif + +static inline int setns(int fd, int nstype) { +# ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +static inline pid_t raw_getpid(void) { +#if defined(__alpha__) + return (pid_t) syscall(__NR_getxpid); +#else + return (pid_t) syscall(__NR_getpid); +#endif +} + +/* ======================================================================= */ + +#if !HAVE_DECL_RENAMEAT2 +# ifndef __NR_renameat2 +# if defined __x86_64__ +# define __NR_renameat2 316 +# elif defined __arm__ +# define __NR_renameat2 382 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_renameat2 4351 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_renameat2 6315 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_renameat2 5311 +# endif +# elif defined __i386__ +# define __NR_renameat2 353 +# else +# warning "__NR_renameat2 unknown for your architecture" +# endif +# endif + +static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { +# ifdef __NR_renameat2 + return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_KCMP +static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { +# ifdef __NR_kcmp + return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_KEYCTL +static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { +# ifdef __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) { +# ifdef __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) { +# ifdef __NR_request_key + return syscall(__NR_request_key, type, description, callout_info, destringid); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_COPY_FILE_RANGE +# ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined __s390__ +# define __NR_copy_file_range 375 +# elif defined __arm__ +# define __NR_copy_file_range 391 +# elif defined __aarch64__ +# define __NR_copy_file_range 285 +# elif defined __powerpc__ +# define __NR_copy_file_range 379 +# else +# warning "__NR_copy_file_range not defined for your architecture" +# endif +# endif + +static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { +# ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif diff --git a/src/libbasic/include/basic/mkdir.h b/src/libbasic/include/basic/mkdir.h new file mode 100644 index 0000000000..d564a3547f --- /dev/null +++ b/src/libbasic/include/basic/mkdir.h @@ -0,0 +1,38 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid); +int mkdir_parents(const char *path, mode_t mode); +int mkdir_p(const char *path, mode_t mode); + +/* mandatory access control(MAC) versions */ +int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid); +int mkdir_parents_label(const char *path, mode_t mode); +int mkdir_p_label(const char *path, mode_t mode); + +/* internally used */ +typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); +int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir); +int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); +int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); diff --git a/src/libbasic/include/basic/mount-util.h b/src/libbasic/include/basic/mount-util.h new file mode 100644 index 0000000000..f46989ebb3 --- /dev/null +++ b/src/libbasic/include/basic/mount-util.h @@ -0,0 +1,54 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include + +#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]; +}; + +const char* mode_to_inaccessible_node(mode_t mode); + +#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ } diff --git a/src/libbasic/include/basic/nss-util.h b/src/libbasic/include/basic/nss-util.h new file mode 100644 index 0000000000..e7844fff96 --- /dev/null +++ b/src/libbasic/include/basic/nss-util.h @@ -0,0 +1,199 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH + +#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ +enum nss_status _nss_##module##_gethostbyname4_r( \ + const char *name, \ + struct gaih_addrtuple **pat, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop, \ + int32_t *ttlp) _public_; \ +enum nss_status _nss_##module##_gethostbyname3_r( \ + const char *name, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop, \ + int32_t *ttlp, \ + char **canonp) _public_; \ +enum nss_status _nss_##module##_gethostbyname2_r( \ + const char *name, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) _public_; \ +enum nss_status _nss_##module##_gethostbyname_r( \ + const char *name, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) _public_ + +#define NSS_GETHOSTBYADDR_PROTOTYPES(module) \ +enum nss_status _nss_##module##_gethostbyaddr2_r( \ + const void* addr, socklen_t len, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop, \ + int32_t *ttlp) _public_; \ +enum nss_status _nss_##module##_gethostbyaddr_r( \ + const void* addr, socklen_t len, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) _public_ + +#define NSS_GETHOSTBYNAME_FALLBACKS(module) \ +enum nss_status _nss_##module##_gethostbyname2_r( \ + const char *name, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) { \ + return _nss_##module##_gethostbyname3_r( \ + name, \ + af, \ + host, \ + buffer, buflen, \ + errnop, h_errnop, \ + NULL, \ + NULL); \ +} \ +enum nss_status _nss_##module##_gethostbyname_r( \ + const char *name, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) { \ + enum nss_status ret = NSS_STATUS_NOTFOUND; \ + \ + if (_res.options & RES_USE_INET6) \ + ret = _nss_##module##_gethostbyname3_r( \ + name, \ + AF_INET6, \ + host, \ + buffer, buflen, \ + errnop, h_errnop, \ + NULL, \ + NULL); \ + if (ret == NSS_STATUS_NOTFOUND) \ + ret = _nss_##module##_gethostbyname3_r( \ + name, \ + AF_INET, \ + host, \ + buffer, buflen, \ + errnop, h_errnop, \ + NULL, \ + NULL); \ + return ret; \ +} \ +struct __useless_struct_to_allow_trailing_semicolon__ + +#define NSS_GETHOSTBYADDR_FALLBACKS(module) \ +enum nss_status _nss_##module##_gethostbyaddr_r( \ + const void* addr, socklen_t len, \ + int af, \ + struct hostent *host, \ + char *buffer, size_t buflen, \ + int *errnop, int *h_errnop) { \ + return _nss_##module##_gethostbyaddr2_r( \ + addr, len, \ + af, \ + host, \ + buffer, buflen, \ + errnop, h_errnop, \ + NULL); \ +} \ +struct __useless_struct_to_allow_trailing_semicolon__ + +#define NSS_GETPW_PROTOTYPES(module) \ +enum nss_status _nss_##module##_getpwnam_r( \ + const char *name, \ + struct passwd *pwd, \ + char *buffer, size_t buflen, \ + int *errnop) _public_; \ +enum nss_status _nss_##module##_getpwuid_r( \ + uid_t uid, \ + struct passwd *pwd, \ + char *buffer, size_t buflen, \ + int *errnop) _public_ + +#define NSS_GETGR_PROTOTYPES(module) \ +enum nss_status _nss_##module##_getgrnam_r( \ + const char *name, \ + struct group *gr, \ + char *buffer, size_t buflen, \ + int *errnop) _public_; \ +enum nss_status _nss_##module##_getgrgid_r( \ + gid_t gid, \ + struct group *gr, \ + char *buffer, size_t buflen, \ + int *errnop) _public_ + +typedef enum nss_status (*_nss_gethostbyname4_r_t)( + const char *name, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + +typedef enum nss_status (*_nss_gethostbyname3_r_t)( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp, + char **canonp); + +typedef enum nss_status (*_nss_gethostbyname2_r_t)( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); + +typedef enum nss_status (*_nss_gethostbyname_r_t)( + const char *name, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); + +typedef enum nss_status (*_nss_gethostbyaddr2_r_t)( + const void* addr, socklen_t len, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); +typedef enum nss_status (*_nss_gethostbyaddr_r_t)( + const void* addr, socklen_t len, + int af, + struct hostent *host, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); diff --git a/src/libbasic/include/basic/ordered-set.h b/src/libbasic/include/basic/ordered-set.h new file mode 100644 index 0000000000..e1dfc86380 --- /dev/null +++ b/src/libbasic/include/basic/ordered-set.h @@ -0,0 +1,74 @@ +#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 . +***/ + +#include "hashmap.h" + +typedef struct OrderedSet OrderedSet; + +static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) { + return (OrderedSet*) ordered_hashmap_new(ops); +} + +static inline 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; +} + +static inline OrderedSet* ordered_set_free_free(OrderedSet *s) { + ordered_hashmap_free_free((OrderedHashmap*) s); + return NULL; +} + +static inline int ordered_set_put(OrderedSet *s, void *p) { + return ordered_hashmap_put((OrderedHashmap*) s, p, p); +} + +static inline bool ordered_set_isempty(OrderedSet *s) { + return ordered_hashmap_isempty((OrderedHashmap*) s); +} + +static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value) { + return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL); +} + +int ordered_set_consume(OrderedSet *s, void *p); +int ordered_set_put_strdup(OrderedSet *s, const char *p); +int ordered_set_put_strdupv(OrderedSet *s, char **l); + +#define ORDERED_SET_FOREACH(e, s, i) \ + for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); ) + +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); + +#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep) +#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep) diff --git a/src/libbasic/include/basic/parse-util.h b/src/libbasic/include/basic/parse-util.h new file mode 100644 index 0000000000..73441bb6fd --- /dev/null +++ b/src/libbasic/include/basic/parse-util.h @@ -0,0 +1,109 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#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 + +#if SIZE_MAX == UINT_MAX +static inline int safe_atozu(const char *s, size_t *ret_u) { + assert_cc(sizeof(size_t) == sizeof(unsigned)); + return safe_atou(s, (unsigned *) ret_u); +} +#else +static inline int safe_atozu(const char *s, size_t *ret_u) { + assert_cc(sizeof(size_t) == sizeof(long unsigned)); + return safe_atolu(s, 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); + +int parse_percent(const char *p); diff --git a/src/libbasic/include/basic/path-util.h b/src/libbasic/include/basic/path-util.h new file mode 100644 index 0000000000..a27c13fcc3 --- /dev/null +++ b/src/libbasic/include/basic/path-util.h @@ -0,0 +1,127 @@ +#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 . +***/ + +#include +#include +#include + +#include "macro.h" +#include "time-util.h" + +#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" +#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" + +#ifdef HAVE_SPLIT_USR +# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR +#else +# define DEFAULT_PATH DEFAULT_PATH_NORMAL +#endif + +bool is_path(const char *p) _pure_; +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); +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_; +int path_compare(const char *a, const char *b) _pure_; +bool path_equal(const char *a, const char *b) _pure_; +bool path_equal_or_files_same(const char *a, const char *b); +char* path_join(const char *root, const char *path, const char *rest); + +static inline bool path_equal_ptr(const char *a, const char *b) { + return !!a == !!b && (!a || path_equal(a, b)); +} + +/* Note: the search terminates on the first NULL item. */ +#define PATH_IN_SET(p, ...) \ + ({ \ + char **s; \ + bool _found = false; \ + STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ + if (path_equal(p, *s)) { \ + _found = true; \ + break; \ + } \ + _found; \ + }) + +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 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 + * directory. Excludes the specified directory itself */ +#define PATH_FOREACH_PREFIX(prefix, path) \ + for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + +/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ +#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ + for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + +char *prefix_root(const char *root, const char *path); + +/* Similar to prefix_root(), but returns an alloca() buffer, or + * possibly a const pointer into the path parameter */ +#define prefix_roota(root, path) \ + ({ \ + const char* _path = (path), *_root = (root), *_ret; \ + char *_p, *_n; \ + size_t _l; \ + while (_path[0] == '/' && _path[1] == '/') \ + _path ++; \ + if (isempty(_root) || path_equal(_root, "/")) \ + _ret = _path; \ + else { \ + _l = strlen(_root) + 1 + strlen(_path) + 1; \ + _n = alloca(_l); \ + _p = stpcpy(_n, _root); \ + while (_p > _n && _p[-1] == '/') \ + _p--; \ + if (_path[0] != '/') \ + *(_p++) = '/'; \ + strcpy(_p, _path); \ + _ret = _n; \ + } \ + _ret; \ + }) + +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_or_backup_file(const char *filename) _pure_; + +bool is_device_path(const char *path); diff --git a/src/libbasic/include/basic/prioq.h b/src/libbasic/include/basic/prioq.h new file mode 100644 index 0000000000..113c73d040 --- /dev/null +++ b/src/libbasic/include/basic/prioq.h @@ -0,0 +1,43 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "hashmap.h" +#include "macro.h" + +typedef struct Prioq Prioq; + +#define PRIOQ_IDX_NULL ((unsigned) -1) + +Prioq *prioq_new(compare_func_t compare); +Prioq *prioq_free(Prioq *q); +int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); + +int prioq_put(Prioq *q, void *data, unsigned *idx); +int prioq_remove(Prioq *q, void *data, unsigned *idx); +int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); + +void *prioq_peek(Prioq *q) _pure_; +void *prioq_pop(Prioq *q); + +unsigned prioq_size(Prioq *q) _pure_; +bool prioq_isempty(Prioq *q) _pure_; diff --git a/src/libbasic/include/basic/proc-cmdline.h b/src/libbasic/include/basic/proc-cmdline.h new file mode 100644 index 0000000000..452642a2f5 --- /dev/null +++ b/src/libbasic/include/basic/proc-cmdline.h @@ -0,0 +1,27 @@ +#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 . +***/ + +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/libbasic/include/basic/process-util.h b/src/libbasic/include/basic/process-util.h new file mode 100644 index 0000000000..9f75088796 --- /dev/null +++ b/src/libbasic/include/basic/process-util.h @@ -0,0 +1,105 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "formats-util.h" +#include "macro.h" + +#define procfs_file_alloca(pid, field) \ + ({ \ + pid_t _pid_ = (pid); \ + const char *_r_; \ + if (_pid_ == 0) { \ + _r_ = ("/proc/self/" field); \ + } else { \ + _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ + sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ + } \ + _r_; \ + }) + +int get_process_state(pid_t pid); +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); +int get_process_uid(pid_t pid, uid_t *uid); +int get_process_gid(pid_t pid, gid_t *gid); +int get_process_capeff(pid_t pid, char **capeff); +int get_process_cwd(pid_t pid, char **cwd); +int get_process_root(pid_t pid, char **root); +int get_process_environ(pid_t pid, char **environ); +int 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); +void sigkill_waitp(pid_t *pid); + +int kill_and_sigcont(pid_t pid, int sig); + +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); +int pid_from_same_root_fs(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); + +int pid_compare_func(const void *a, const void *b); diff --git a/src/libbasic/include/basic/random-util.h b/src/libbasic/include/basic/random-util.h new file mode 100644 index 0000000000..3cee4c5014 --- /dev/null +++ b/src/libbasic/include/basic/random-util.h @@ -0,0 +1,39 @@ +#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 . +***/ + +#include +#include + +int dev_urandom(void *p, size_t n); +void random_bytes(void *p, size_t n); +void initialize_srand(void); + +static inline uint64_t random_u64(void) { + uint64_t u; + random_bytes(&u, sizeof(u)); + return u; +} + +static inline uint32_t random_u32(void) { + uint32_t u; + random_bytes(&u, sizeof(u)); + return u; +} diff --git a/src/libbasic/include/basic/ratelimit.h b/src/libbasic/include/basic/ratelimit.h new file mode 100644 index 0000000000..9c8dddf5ad --- /dev/null +++ b/src/libbasic/include/basic/ratelimit.h @@ -0,0 +1,58 @@ +#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 . +***/ + +#include + +#include "time-util.h" +#include "util.h" + +typedef struct RateLimit { + usec_t interval; + usec_t begin; + unsigned burst; + unsigned num; +} RateLimit; + +#define RATELIMIT_DEFINE(_name, _interval, _burst) \ + RateLimit _name = { \ + .interval = (_interval), \ + .burst = (_burst), \ + .num = 0, \ + .begin = 0 \ + } + +#define RATELIMIT_INIT(v, _interval, _burst) \ + do { \ + RateLimit *_r = &(v); \ + _r->interval = (_interval); \ + _r->burst = (_burst); \ + _r->num = 0; \ + _r->begin = 0; \ + } while (false) + +#define RATELIMIT_RESET(v) \ + do { \ + RateLimit *_r = &(v); \ + _r->num = 0; \ + _r->begin = 0; \ + } while (false) + +bool ratelimit_test(RateLimit *r); diff --git a/src/libbasic/include/basic/raw-clone.h b/src/libbasic/include/basic/raw-clone.h new file mode 100644 index 0000000000..d473828999 --- /dev/null +++ b/src/libbasic/include/basic/raw-clone.h @@ -0,0 +1,81 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2016 Michael Karcher + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "log.h" +#include "macro.h" + +/** + * raw_clone() - uses clone to create a new process with clone flags + * @flags: Flags to pass to the clone system call + * + * Uses the clone system call to create a new process with the cloning + * flags and termination signal passed in the flags parameter. Opposed + * to glibc's clone funtion, using this function does not set up a + * separate stack for the child, but relies on copy-on-write semantics + * on the one stack at a common virtual address, just as fork does. + * + * To obtain copy-on-write semantics, flags must not contain CLONE_VM, + * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are + * not usabale. + * Additionally, as this function does not pass the ptid, newtls and ctid + * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID, + * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. + * + * Returns: 0 in the child process and the child process id in the parent. + */ +static inline int raw_clone(unsigned long flags) { + assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| + CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); +#if defined(__s390__) || defined(__CRIS__) + /* On s390 and cris the order of the first and second arguments + * of the raw clone() system call is reversed. */ + return (int) syscall(__NR_clone, NULL, flags); +#elif defined(__sparc__) && defined(__arch64__) + { + /** + * sparc64 always returns the other process id in %o0, and + * a boolean flag whether this is the child or the parent in + * %o1. Inline assembly is needed to get the flag returned + * in %o1. + */ + int in_child; + int child_pid; + asm volatile("mov %2, %%g1\n\t" + "mov %3, %%o0\n\t" + "mov 0 , %%o1\n\t" + "t 0x6d\n\t" + "mov %%o1, %0\n\t" + "mov %%o0, %1" : + "=r"(in_child), "=r"(child_pid) : + "i"(__NR_clone), "r"(flags) : + "%o1", "%o0", "%g1" ); + if (in_child) + return 0; + else + return child_pid; + } +#else + return (int) syscall(__NR_clone, flags, NULL); +#endif +} diff --git a/src/libbasic/include/basic/refcnt.h b/src/libbasic/include/basic/refcnt.h new file mode 100644 index 0000000000..1d77a6445a --- /dev/null +++ b/src/libbasic/include/basic/refcnt.h @@ -0,0 +1,34 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* A type-safe atomic refcounter. + * + * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ + +typedef struct { + volatile unsigned _value; +} RefCount; + +#define REFCNT_GET(r) ((r)._value) +#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) +#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) + +#define REFCNT_INIT ((RefCount) { ._value = 1 }) diff --git a/src/libbasic/include/basic/replace-var.h b/src/libbasic/include/basic/replace-var.h new file mode 100644 index 0000000000..78412910b2 --- /dev/null +++ b/src/libbasic/include/basic/replace-var.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata); diff --git a/src/libbasic/include/basic/rlimit-util.h b/src/libbasic/include/basic/rlimit-util.h new file mode 100644 index 0000000000..d4594eccd6 --- /dev/null +++ b/src/libbasic/include/basic/rlimit-util.h @@ -0,0 +1,36 @@ +#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 . +***/ + +#include + +#include "macro.h" + +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); + +int rlimit_parse_one(int resource, const char *val, rlim_t *ret); +int rlimit_parse(int resource, const char *val, struct rlimit *ret); + +int rlimit_format(const struct rlimit *rl, char **ret); + +#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) diff --git a/src/libbasic/include/basic/rm-rf.h b/src/libbasic/include/basic/rm-rf.h new file mode 100644 index 0000000000..f693a5bb7c --- /dev/null +++ b/src/libbasic/include/basic/rm-rf.h @@ -0,0 +1,41 @@ +#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 . +***/ + +#include + +typedef enum RemoveFlags { + REMOVE_ONLY_DIRECTORIES = 1, + REMOVE_ROOT = 2, + REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */ + REMOVE_SUBVOLUME = 8, +} RemoveFlags; + +int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); +int rm_rf(const char *path, RemoveFlags flags); + +/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ +static inline void rm_rf_physical_and_free(char *p) { + if (!p) + return; + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free); diff --git a/src/libbasic/include/basic/securebits.h b/src/libbasic/include/basic/securebits.h new file mode 100644 index 0000000000..98fbe0d433 --- /dev/null +++ b/src/libbasic/include/basic/securebits.h @@ -0,0 +1,45 @@ +#ifndef _LINUX_SECUREBITS_H +#define _LINUX_SECUREBITS_H 1 + +/* This is minimal version of Linux' linux/securebits.h header file, + * which is licensed GPL2 */ + +#define SECUREBITS_DEFAULT 0x00000000 + +/* When set UID 0 has no special privileges. When unset, we support + inheritance of root-permissions and suid-root executable under + compatibility mode. We raise the effective and inheritable bitmasks + *of the executable file* if the effective uid of the new process is + 0. If the real uid is 0, we raise the effective (legacy) bit of the + executable file. */ +#define SECURE_NOROOT 0 +#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ + +/* When set, setuid to/from uid 0 does not trigger capability-"fixup". + When unset, to provide compatibility with old programs relying on + set*uid to gain/lose privilege, transitions to/from uid 0 cause + capabilities to be gained/lost. */ +#define SECURE_NO_SETUID_FIXUP 2 +#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ + +/* When set, a process can retain its capabilities even after + transitioning to a non-root user (the set-uid fixup suppressed by + bit 2). Bit-4 is cleared when a process calls exec(); setting both + bit 4 and 5 will create a barrier through exec that no exec()'d + child can use this feature again. */ +#define SECURE_KEEP_CAPS 4 +#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ + +/* Each securesetting is implemented using two bits. One bit specifies + whether the setting is on or off. The other bit specify whether the + setting is locked or not. A setting which is locked cannot be + changed from user-level. */ +#define issecure_mask(X) (1 << (X)) +#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits)) + +#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ + issecure_mask(SECURE_NO_SETUID_FIXUP) | \ + issecure_mask(SECURE_KEEP_CAPS)) +#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) + +#endif /* !_LINUX_SECUREBITS_H */ diff --git a/src/libbasic/include/basic/selinux-util.h b/src/libbasic/include/basic/selinux-util.h new file mode 100644 index 0000000000..ce6bc8e44c --- /dev/null +++ b/src/libbasic/include/basic/selinux-util.h @@ -0,0 +1,51 @@ +#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 . +***/ + +#include +#include +#include + +#include "macro.h" + +bool mac_selinux_use(void); +bool mac_selinux_have(void); +void mac_selinux_retest(void); + +int mac_selinux_init(void); +void mac_selinux_finish(void); + +int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs); +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); +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); + +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/libbasic/include/basic/set.h b/src/libbasic/include/basic/set.h new file mode 100644 index 0000000000..12f64a8c57 --- /dev/null +++ b/src/libbasic/include/basic/set.h @@ -0,0 +1,138 @@ +#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 . +***/ + +#include "extract-word.h" +#include "hashmap.h" +#include "macro.h" + +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; +} + +static inline Set *set_free_free(Set *s) { + internal_hashmap_free_free(HASHMAP_BASE(s)); + return NULL; +} + +/* no set_free_free_free */ + +static inline Set *set_copy(Set *s) { + return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); +} + +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) + +int set_put(Set *s, const void *key); +/* no set_update */ +/* no set_replace */ +static inline void *set_get(Set *s, void *key) { + return internal_hashmap_get(HASHMAP_BASE(s), key); +} +/* no set_get2 */ + +static inline bool set_contains(Set *s, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(s), key); +} + +static inline void *set_remove(Set *s, const void *key) { + return internal_hashmap_remove(HASHMAP_BASE(s), key); +} + +/* no set_remove2 */ +/* no set_remove_value */ +int set_remove_and_put(Set *s, const void *old_key, const void *new_key); +/* no set_remove_and_replace */ +int set_merge(Set *s, Set *other); + +static inline int set_reserve(Set *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} + +static inline int set_move(Set *s, Set *other) { + return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); +} + +static inline int set_move_one(Set *s, Set *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); +} + +static inline unsigned set_size(Set *s) { + return internal_hashmap_size(HASHMAP_BASE(s)); +} + +static inline bool set_isempty(Set *s) { + return set_size(s) == 0; +} + +static inline unsigned set_buckets(Set *s) { + return internal_hashmap_buckets(HASHMAP_BASE(s)); +} + +bool set_iterate(Set *s, Iterator *i, void **value); + +static inline void set_clear(Set *s) { + internal_hashmap_clear(HASHMAP_BASE(s)); +} + +static inline void set_clear_free(Set *s) { + internal_hashmap_clear_free(HASHMAP_BASE(s)); +} + +/* no set_clear_free_free */ + +static inline void *set_steal_first(Set *s) { + return internal_hashmap_steal_first(HASHMAP_BASE(s)); +} + +/* no set_steal_first_key */ +/* no set_first_key */ + +static inline void *set_first(Set *s) { + return internal_hashmap_first(HASHMAP_BASE(s)); +} + +/* no set_next */ + +static inline char **set_get_strv(Set *s) { + return internal_hashmap_get_strv(HASHMAP_BASE(s)); +} + +int set_consume(Set *s, void *value); +int set_put_strdup(Set *s, const char *p); +int set_put_strdupv(Set *s, char **l); +int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); + +#define SET_FOREACH(e, s, i) \ + for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) + +#define SET_FOREACH_MOVE(e, d, s) \ + for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) + +DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); + +#define _cleanup_set_free_ _cleanup_(set_freep) +#define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/libbasic/include/basic/sigbus.h b/src/libbasic/include/basic/sigbus.h new file mode 100644 index 0000000000..980243d9ce --- /dev/null +++ b/src/libbasic/include/basic/sigbus.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + 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 . +***/ + +void sigbus_install(void); +void sigbus_reset(void); + +int sigbus_pop(void **ret); diff --git a/src/libbasic/include/basic/signal-util.h b/src/libbasic/include/basic/signal-util.h new file mode 100644 index 0000000000..dfd6eb564d --- /dev/null +++ b/src/libbasic/include/basic/signal-util.h @@ -0,0 +1,56 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" + +int reset_all_signal_handlers(void); +int reset_signal_mask(void); + +int ignore_signals(int sig, ...); +int default_signals(int sig, ...); +int sigaction_many(const struct sigaction *sa, ...); + +int sigset_add_many(sigset_t *ss, ...); +int sigprocmask_many(int how, sigset_t *old, ...); + +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); + +static inline void block_signals_reset(sigset_t *ss) { + assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); +} + +#define BLOCK_SIGNALS(...) \ + _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ + sigset_t t; \ + assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ + t; \ + }) + +static inline bool SIGNAL_VALID(int signo) { + return signo > 0 && signo < _NSIG; +} diff --git a/src/libbasic/include/basic/siphash24.h b/src/libbasic/include/basic/siphash24.h new file mode 100644 index 0000000000..54e2420cc6 --- /dev/null +++ b/src/libbasic/include/basic/siphash24.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +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/libbasic/include/basic/smack-util.h b/src/libbasic/include/basic/smack-util.h new file mode 100644 index 0000000000..f90ba0a027 --- /dev/null +++ b/src/libbasic/include/basic/smack-util.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Intel Corporation + + Author: Auke Kok + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "macro.h" + +#define SMACK_FLOOR_LABEL "_" +#define SMACK_STAR_LABEL "*" + +typedef enum SmackAttr { + SMACK_ATTR_ACCESS = 0, + SMACK_ATTR_EXEC = 1, + SMACK_ATTR_MMAP = 2, + SMACK_ATTR_TRANSMUTE = 3, + SMACK_ATTR_IPIN = 4, + SMACK_ATTR_IPOUT = 5, + _SMACK_ATTR_MAX, + _SMACK_ATTR_INVALID = -1, +} SmackAttr; + +bool mac_smack_use(void); + +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); + +const char* smack_attr_to_string(SmackAttr i) _const_; +SmackAttr smack_attr_from_string(const char *s) _pure_; +int mac_smack_read(const char *path, SmackAttr attr, char **label); +int mac_smack_read_fd(int fd, SmackAttr attr, char **label); +int mac_smack_apply(const char *path, SmackAttr attr, const char *label); +int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label); +int mac_smack_apply_pid(pid_t pid, const char *label); +int mac_smack_copy(const char *dest, const char *src); diff --git a/src/libbasic/include/basic/socket-util.h b/src/libbasic/include/basic/socket-util.h new file mode 100644 index 0000000000..bad1f32e09 --- /dev/null +++ b/src/libbasic/include/basic/socket-util.h @@ -0,0 +1,155 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "macro.h" +#include "util.h" + +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_nl nl; + struct sockaddr_storage storage; + struct sockaddr_ll ll; +}; + +typedef struct SocketAddress { + union sockaddr_union sockaddr; + + /* We store the size here explicitly due to the weird + * sockaddr_un semantics for abstract sockets */ + socklen_t size; + + /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ + int type; + + /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ + int protocol; +} SocketAddress; + +typedef enum SocketAddressBindIPv6Only { + SOCKET_ADDRESS_DEFAULT, + SOCKET_ADDRESS_BOTH, + SOCKET_ADDRESS_IPV6_ONLY, + _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, + _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 +} SocketAddressBindIPv6Only; + +#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) + +int socket_address_parse(SocketAddress *a, const char *s); +int socket_address_parse_and_warn(SocketAddress *a, const char *s); +int socket_address_parse_netlink(SocketAddress *a, const char *s); +int socket_address_print(const SocketAddress *a, char **p); +int socket_address_verify(const SocketAddress *a) _pure_; +int socket_address_unlink(SocketAddress *a); + +bool socket_address_can_accept(const SocketAddress *a) _pure_; + +int socket_address_listen( + const SocketAddress *a, + int flags, + int backlog, + SocketAddressBindIPv6Only only, + const char *bind_to_device, + bool reuse_port, + bool free_bind, + bool transparent, + mode_t directory_mode, + mode_t socket_mode, + const char *label); +int make_socket_fd(int log_level, const char* address, int type, int flags); + +bool socket_address_is(const SocketAddress *a, const char *s, int type); +bool socket_address_is_netlink(const SocketAddress *a, const char *s); + +bool socket_address_matches_fd(const SocketAddress *a, int fd); + +bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; + +const char* socket_address_get_path(const SocketAddress *a); + +bool socket_ipv6_is_supported(void); + +int sockaddr_port(const struct sockaddr *_sa) _pure_; + +int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); +int getpeername_pretty(int fd, bool include_port, char **ret); +int getsockname_pretty(int fd, char **ret); + +int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); +int getnameinfo_pretty(int fd, char **ret); + +const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; +SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; + +int netlink_family_to_string_alloc(int b, char **s); +int netlink_family_from_string(const char *s) _pure_; + +bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); + +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); + +bool ifname_valid(const char *p); + +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); + +ssize_t next_datagram_size_fd(int fd); + +int flush_accept(int fd); + +#define CMSG_FOREACH(cmsg, mh) \ + for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) + +/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ +#define SOCKADDR_UN_LEN(sa) \ + ({ \ + const struct sockaddr_un *_sa = &(sa); \ + assert(_sa->sun_family == AF_UNIX); \ + offsetof(struct sockaddr_un, sun_path) + \ + (_sa->sun_path[0] == 0 ? \ + 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ + strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ + }) diff --git a/src/libbasic/include/basic/sparse-endian.h b/src/libbasic/include/basic/sparse-endian.h new file mode 100644 index 0000000000..c913fda8c5 --- /dev/null +++ b/src/libbasic/include/basic/sparse-endian.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2012 Josh Triplett + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef SPARSE_ENDIAN_H +#define SPARSE_ENDIAN_H + +#include +#include +#include + +#ifdef __CHECKER__ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) +#else +#define __bitwise +#define __force +#endif + +typedef uint16_t __bitwise le16_t; +typedef uint16_t __bitwise be16_t; +typedef uint32_t __bitwise le32_t; +typedef uint32_t __bitwise be32_t; +typedef uint64_t __bitwise le64_t; +typedef uint64_t __bitwise be64_t; + +#undef htobe16 +#undef htole16 +#undef be16toh +#undef le16toh +#undef htobe32 +#undef htole32 +#undef be32toh +#undef le32toh +#undef htobe64 +#undef htole64 +#undef be64toh +#undef le64toh + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define bswap_16_on_le(x) __bswap_16(x) +#define bswap_32_on_le(x) __bswap_32(x) +#define bswap_64_on_le(x) __bswap_64(x) +#define bswap_16_on_be(x) (x) +#define bswap_32_on_be(x) (x) +#define bswap_64_on_be(x) (x) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define bswap_16_on_le(x) (x) +#define bswap_32_on_le(x) (x) +#define bswap_64_on_le(x) (x) +#define bswap_16_on_be(x) __bswap_16(x) +#define bswap_32_on_be(x) __bswap_32(x) +#define bswap_64_on_be(x) __bswap_64(x) +#endif + +static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); } +static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); } +static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); } + +static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); } +static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); } +static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); } + +static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); } +static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); } +static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); } + +static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); } +static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); } +static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); } + +#endif /* SPARSE_ENDIAN_H */ diff --git a/src/libbasic/include/basic/special.h b/src/libbasic/include/basic/special.h new file mode 100644 index 0000000000..084d3dfa23 --- /dev/null +++ b/src/libbasic/include/basic/special.h @@ -0,0 +1,119 @@ +#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 . +***/ + +#define SPECIAL_DEFAULT_TARGET "default.target" + +/* Shutdown targets */ +#define SPECIAL_UMOUNT_TARGET "umount.target" +/* This is not really intended to be started by directly. This is + * mostly so that other targets (reboot/halt/poweroff) can depend on + * it to bring all services down that want to be brought down on + * system shutdown. */ +#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" +#define SPECIAL_HALT_TARGET "halt.target" +#define SPECIAL_POWEROFF_TARGET "poweroff.target" +#define SPECIAL_REBOOT_TARGET "reboot.target" +#define SPECIAL_KEXEC_TARGET "kexec.target" +#define SPECIAL_EXIT_TARGET "exit.target" +#define SPECIAL_SUSPEND_TARGET "suspend.target" +#define SPECIAL_HIBERNATE_TARGET "hibernate.target" +#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target" + +/* Special boot targets */ +#define SPECIAL_RESCUE_TARGET "rescue.target" +#define SPECIAL_EMERGENCY_TARGET "emergency.target" +#define SPECIAL_MULTI_USER_TARGET "multi-user.target" +#define SPECIAL_GRAPHICAL_TARGET "graphical.target" + +/* Early boot targets */ +#define SPECIAL_SYSINIT_TARGET "sysinit.target" +#define SPECIAL_SOCKETS_TARGET "sockets.target" +#define SPECIAL_BUSNAMES_TARGET "busnames.target" +#define SPECIAL_TIMERS_TARGET "timers.target" +#define SPECIAL_PATHS_TARGET "paths.target" +#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" +#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" +#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" +#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target" +#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" +#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ +#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" +#define SPECIAL_SWAP_TARGET "swap.target" +#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target" +#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ +#define SPECIAL_BASIC_TARGET "basic.target" + +/* LSB compatibility */ +#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ +#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ +#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ + +/* + * Rules regarding adding further high level targets like the above: + * + * - Be conservative, only add more of these when we really need + * them. We need strong usecases for further additions. + * + * - When there can be multiple implementations running side-by-side, + * it needs to be a .target unit which can pull in all + * implementations. + * + * - If something can be implemented with socket activation, and + * without, it needs to be a .target unit, so that it can pull in + * the appropriate unit. + * + * - Otherwise, it should be a .service unit. + * + * - In some cases it is OK to have both a .service and a .target + * unit, i.e. if there can be multiple parallel implementations, but + * only one is the "system" one. Example: syslog. + * + * Or to put this in other words: .service symlinks can be used to + * arbitrate between multiple implementations if there can be only one + * of a kind. .target units can be used to support multiple + * implementations that can run side-by-side. + */ + +/* Magic early boot services */ +#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" +#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" +#define SPECIAL_QUOTAON_SERVICE "quotaon.service" +#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" + +/* Services systemd relies on */ +#define SPECIAL_DBUS_SERVICE "dbus.service" +#define SPECIAL_DBUS_SOCKET "dbus.socket" +#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket" +#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service" + +/* Magic init signals */ +#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" +#define SPECIAL_SIGPWR_TARGET "sigpwr.target" +#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" + +/* Where we add all our system units, users and machines by default */ +#define SPECIAL_SYSTEM_SLICE "system.slice" +#define SPECIAL_USER_SLICE "user.slice" +#define SPECIAL_MACHINE_SLICE "machine.slice" +#define SPECIAL_ROOT_SLICE "-.slice" + +/* The scope unit systemd itself lives in. */ +#define SPECIAL_INIT_SCOPE "init.scope" diff --git a/src/libbasic/include/basic/stat-util.h b/src/libbasic/include/basic/stat-util.h new file mode 100644 index 0000000000..56d28f791e --- /dev/null +++ b/src/libbasic/include/basic/stat-util.h @@ -0,0 +1,69 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include + +#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/libbasic/include/basic/stdio-util.h b/src/libbasic/include/basic/stdio-util.h new file mode 100644 index 0000000000..bd1144b4c9 --- /dev/null +++ b/src/libbasic/include/basic/stdio-util.h @@ -0,0 +1,76 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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/libbasic/include/basic/strbuf.h b/src/libbasic/include/basic/strbuf.h new file mode 100644 index 0000000000..a1632da0e8 --- /dev/null +++ b/src/libbasic/include/basic/strbuf.h @@ -0,0 +1,54 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +struct strbuf { + char *buf; + size_t len; + struct strbuf_node *root; + + size_t nodes_count; + size_t in_count; + size_t in_len; + size_t dedup_len; + size_t dedup_count; +}; + +struct strbuf_node { + size_t value_off; + size_t value_len; + + struct strbuf_child_entry *children; + uint8_t children_count; +}; + +struct strbuf_child_entry { + uint8_t c; + struct strbuf_node *child; +}; + +struct strbuf *strbuf_new(void); +ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len); +void strbuf_complete(struct strbuf *str); +void strbuf_cleanup(struct strbuf *str); diff --git a/src/libbasic/include/basic/string-table.h b/src/libbasic/include/basic/string-table.h new file mode 100644 index 0000000000..369610efc8 --- /dev/null +++ b/src/libbasic/include/basic/string-table.h @@ -0,0 +1,119 @@ + +#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 . +***/ + +#include +#include +#include +#include +#include + +#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; \ + if (!s) \ + return -1; \ + 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_TO_STRING_FALLBACK(name,type,max,scope) \ + scope 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; \ + } + +#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ + 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; \ + } \ + + +#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) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) diff --git a/src/libbasic/include/basic/string-util.h b/src/libbasic/include/basic/string-util.h new file mode 100644 index 0000000000..1209e1e2e1 --- /dev/null +++ b/src/libbasic/include/basic/string-util.h @@ -0,0 +1,194 @@ +#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 . +***/ + +#include +#include +#include +#include + +#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 HEXDIGITS DIGITS "abcdefABCDEF" + +#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 const char *empty_to_null(const char *p) { + return isempty(p) ? NULL : p; +} + +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/libbasic/include/basic/strv.h b/src/libbasic/include/basic/strv.h new file mode 100644 index 0000000000..683ce83a2a --- /dev/null +++ b/src/libbasic/include/basic/strv.h @@ -0,0 +1,174 @@ +#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 . +***/ + +#include +#include +#include +#include + +#include "alloc-util.h" +#include "extract-word.h" +#include "macro.h" +#include "util.h" + +char *strv_find(char **l, const char *name) _pure_; +char *strv_find_prefix(char **l, const char *name) _pure_; +char *strv_find_startswith(char **l, const char *name) _pure_; + +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, 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); +int strv_extend_front(char ***l, const char *value); +int strv_push(char ***l, char *value); +int strv_push_pair(char ***l, char *a, char *b); +int strv_push_prepend(char ***l, char *value); +int strv_consume(char ***l, char *value); +int strv_consume_pair(char ***l, char *a, char *b); +int strv_consume_prepend(char ***l, char *value); + +char **strv_remove(char **l, const char *s); +char **strv_uniq(char **l); +bool strv_is_uniq(char **l); + +bool strv_equal(char **a, char **b); + +#define strv_contains(l, s) (!!strv_find((l), (s))) + +char **strv_new(const char *x, ...) _sentinel_; +char **strv_new_ap(const char *x, va_list ap); + +#define STRV_IGNORE ((const char *) -1) + +static inline const char* STRV_IFNOTNULL(const char *x) { + return x ? x : STRV_IGNORE; +} + +static inline bool strv_isempty(char * const *l) { + return !l || !*l; +} + +char **strv_split(const char *s, const char *separator); +char **strv_split_newlines(const char *s); + +int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); + +char *strv_join(char **l, const char *separator); +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_; + +#define STRV_FOREACH(s, l) \ + for ((s) = (l); (s) && *(s); (s)++) + +#define STRV_FOREACH_BACKWARDS(s, l) \ + STRV_FOREACH(s, l) \ + ; \ + for ((s)--; (l) && ((s) >= (l)); (s)--) + +#define STRV_FOREACH_PAIR(x, y, l) \ + for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) + +char **strv_sort(char **l); +void strv_print(char **l); + +#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) + +#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) + +#define strv_from_stdarg_alloca(first) \ + ({ \ + char **_l; \ + \ + if (!first) \ + _l = (char**) &first; \ + else { \ + unsigned _n; \ + va_list _ap; \ + \ + _n = 1; \ + va_start(_ap, first); \ + while (va_arg(_ap, char*)) \ + _n++; \ + va_end(_ap); \ + \ + _l = newa(char*, _n+1); \ + _l[_n = 0] = (char*) first; \ + va_start(_ap, first); \ + for (;;) { \ + _l[++_n] = va_arg(_ap, char*); \ + if (!_l[_n]) \ + break; \ + } \ + va_end(_ap); \ + } \ + _l; \ + }) + +#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) + +#define FOREACH_STRING(x, ...) \ + for (char **_l = ({ \ + char **_ll = STRV_MAKE(__VA_ARGS__); \ + x = _ll ? _ll[0] : NULL; \ + _ll; \ + }); \ + _l && *_l; \ + x = ({ \ + _l ++; \ + _l[0]; \ + })) + +char **strv_reverse(char **l); +char **strv_shell_escape(char **l, const char *bad); + +bool strv_fnmatch(char* const* patterns, const char *s, int flags); + +static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { + assert(s); + 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); + +int fputstrv(FILE *f, char **l, const char *separator, bool *space); diff --git a/src/libbasic/include/basic/strxcpyx.h b/src/libbasic/include/basic/strxcpyx.h new file mode 100644 index 0000000000..80ff58726b --- /dev/null +++ b/src/libbasic/include/basic/strxcpyx.h @@ -0,0 +1,31 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include + +#include "macro.h" + +size_t strpcpy(char **dest, size_t size, const char *src); +size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); +size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; +size_t strscpy(char *dest, size_t size, const char *src); +size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; diff --git a/src/libbasic/include/basic/syslog-util.h b/src/libbasic/include/basic/syslog-util.h new file mode 100644 index 0000000000..5cb606a1bf --- /dev/null +++ b/src/libbasic/include/basic/syslog-util.h @@ -0,0 +1,32 @@ +#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 . +***/ + +#include + +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/libbasic/include/basic/terminal-util.h b/src/libbasic/include/basic/terminal-util.h new file mode 100644 index 0000000000..169ab772ff --- /dev/null +++ b/src/libbasic/include/basic/terminal-util.h @@ -0,0 +1,128 @@ +#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 . +***/ + +#include +#include +#include +#include + +#include "macro.h" +#include "time-util.h" + +#define ANSI_RED "\x1B[0;31m" +#define ANSI_GREEN "\x1B[0;32m" +#define ANSI_UNDERLINE "\x1B[0;4m" +#define ANSI_HIGHLIGHT "\x1B[0;1;39m" +#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m" +#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m" +#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" +#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m" +#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" +#define ANSI_NORMAL "\x1B[0m" + +#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" + +/* Set cursor to top left corner and clear screen */ +#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J" + +int reset_terminal_fd(int fd, bool switch_to_text); +int reset_terminal(const char *name); + +int open_terminal(const char *name, int mode); +int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout); +int release_terminal(void); + +int terminal_vhangup_fd(int fd); +int terminal_vhangup(const char *name); + +int chvt(int vt); + +int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); +int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4); +int ask_string(char **ret, const char *text, ...) _printf_(2, 3); + +int vt_disallocate(const char *name); + +char *resolve_dev_console(char **active); +int get_kernel_consoles(char ***consoles); +bool tty_is_vc(const char *tty); +bool tty_is_vc_resolve(const char *tty); +bool tty_is_console(const char *tty) _pure_; +int vtnr_from_tty(const char *tty); +const char *default_term_for_tty(const char *tty); + +int make_stdio(int fd); +int make_null_stdio(void); +int make_console_stdio(void); + +int fd_columns(int fd); +unsigned columns(void); +int fd_lines(int fd); +unsigned lines(void); +void columns_lines_cache_reset(int _unused_ signum); + +bool on_tty(void); +bool terminal_is_dumb(void); +bool colors_enabled(void); + +static inline const char *ansi_underline(void) { + return colors_enabled() ? ANSI_UNDERLINE : ""; +} + +static inline const char *ansi_highlight(void) { + return colors_enabled() ? ANSI_HIGHLIGHT : ""; +} + +static inline const char *ansi_highlight_underline(void) { + return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : ""; +} + +static inline const char *ansi_highlight_red(void) { + return colors_enabled() ? ANSI_HIGHLIGHT_RED : ""; +} + +static inline const char *ansi_highlight_green(void) { + return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : ""; +} + +static inline const char *ansi_highlight_yellow(void) { + return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : ""; +} + +static inline const char *ansi_highlight_blue(void) { + return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : ""; +} + +static inline const char *ansi_normal(void) { + return colors_enabled() ? ANSI_NORMAL : ""; +} + +int get_ctty_devnr(pid_t pid, dev_t *d); +int get_ctty(pid_t, dev_t *_devnr, char **r); + +int getttyname_malloc(int fd, char **r); +int getttyname_harder(int fd, char **r); + +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/libbasic/include/basic/time-util.h b/src/libbasic/include/basic/time-util.h new file mode 100644 index 0000000000..1b058f0e49 --- /dev/null +++ b/src/libbasic/include/basic/time-util.h @@ -0,0 +1,178 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include + +typedef uint64_t usec_t; +typedef uint64_t nsec_t; + +#define NSEC_FMT "%" PRIu64 +#define USEC_FMT "%" PRIu64 + +#include "macro.h" + +typedef struct dual_timestamp { + usec_t realtime; + usec_t monotonic; +} dual_timestamp; + +typedef struct triple_timestamp { + usec_t realtime; + usec_t monotonic; + usec_t boottime; +} triple_timestamp; + +#define USEC_INFINITY ((usec_t) -1) +#define NSEC_INFINITY ((nsec_t) -1) + +#define MSEC_PER_SEC 1000ULL +#define USEC_PER_SEC ((usec_t) 1000000ULL) +#define USEC_PER_MSEC ((usec_t) 1000ULL) +#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) +#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) +#define NSEC_PER_USEC ((nsec_t) 1000ULL) + +#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) +#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) +#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) +#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) +#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) +#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) +#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) +#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) +#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) +#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) +#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) +#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) + +#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */ +#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ +#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 +#define FORMAT_TIMESPAN_MAX 64 + +#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) + +#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) +#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) + +usec_t now(clockid_t clock); +nsec_t now_nsec(clockid_t clock); + +dual_timestamp* dual_timestamp_get(dual_timestamp *ts); +dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); + +triple_timestamp* triple_timestamp_get(triple_timestamp *ts); +triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); + +#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ + IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) + +#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ + IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) + +static inline bool dual_timestamp_is_set(dual_timestamp *ts) { + return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || + (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); +} + +static inline bool triple_timestamp_is_set(triple_timestamp *ts) { + return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || + (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || + (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); +} + +usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); + +usec_t timespec_load(const struct timespec *ts) _pure_; +struct timespec *timespec_store(struct timespec *ts, usec_t u); + +usec_t timeval_load(const struct timeval *tv) _pure_; +struct timeval *timeval_store(struct timeval *tv, usec_t u); + +char *format_timestamp(char *buf, size_t l, usec_t t); +char *format_timestamp_utc(char *buf, size_t l, usec_t t); +char *format_timestamp_us(char *buf, size_t l, usec_t t); +char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); +char *format_timestamp_relative(char *buf, size_t l, usec_t t); +char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); + +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); +int dual_timestamp_deserialize(const char *value, dual_timestamp *t); +int timestamp_deserialize(const char *value, usec_t *timestamp); + +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); + +int get_timezones(char ***l); +bool timezone_is_valid(const char *name); + +bool clock_boottime_supported(void); +bool clock_supported(clockid_t clock); +clockid_t clock_boottime_or_monotonic(void); + +#define xstrftime(buf, fmt, tm) \ + assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ + "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); + +static inline usec_t usec_add(usec_t a, usec_t b) { + usec_t c; + + /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't + * overflow. */ + + c = a + b; + if (c < a || c < b) /* overflow check */ + return USEC_INFINITY; + + return c; +} + +static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { + if (delta < 0) + return usec_add(timestamp, (usec_t) (-delta)); + + if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ + return USEC_INFINITY; + + if (timestamp < (usec_t) delta) + return 0; + + return timestamp - delta; +} diff --git a/src/libbasic/include/basic/umask-util.h b/src/libbasic/include/basic/umask-util.h new file mode 100644 index 0000000000..359d87d27c --- /dev/null +++ b/src/libbasic/include/basic/umask-util.h @@ -0,0 +1,46 @@ +#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 . +***/ + +#include +#include +#include + +#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/libbasic/include/basic/unaligned.h b/src/libbasic/include/basic/unaligned.h new file mode 100644 index 0000000000..7c847a3ccb --- /dev/null +++ b/src/libbasic/include/basic/unaligned.h @@ -0,0 +1,129 @@ +#pragma once + +/*** + 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 . +***/ + +#include +#include + +/* BE */ + +static inline uint16_t unaligned_read_be16(const void *_u) { + const uint8_t *u = _u; + + return (((uint16_t) u[0]) << 8) | + ((uint16_t) u[1]); +} + +static inline uint32_t unaligned_read_be32(const void *_u) { + const uint8_t *u = _u; + + return (((uint32_t) unaligned_read_be16(u)) << 16) | + ((uint32_t) unaligned_read_be16(u + 2)); +} + +static inline uint64_t unaligned_read_be64(const void *_u) { + const uint8_t *u = _u; + + return (((uint64_t) unaligned_read_be32(u)) << 32) | + ((uint64_t) unaligned_read_be32(u + 4)); +} + +static inline void unaligned_write_be16(void *_u, uint16_t a) { + uint8_t *u = _u; + + u[0] = (uint8_t) (a >> 8); + u[1] = (uint8_t) a; +} + +static inline void unaligned_write_be32(void *_u, uint32_t a) { + uint8_t *u = _u; + + unaligned_write_be16(u, (uint16_t) (a >> 16)); + unaligned_write_be16(u + 2, (uint16_t) a); +} + +static inline void unaligned_write_be64(void *_u, uint64_t a) { + uint8_t *u = _u; + + 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)); +} + +#if __BYTE_ORDER == __BIG_ENDIAN +#define unaligned_read_ne16 unaligned_read_be16 +#define unaligned_read_ne32 unaligned_read_be32 +#define unaligned_read_ne64 unaligned_read_be64 + +#define unaligned_write_ne16 unaligned_write_be16 +#define unaligned_write_ne32 unaligned_write_be32 +#define unaligned_write_ne64 unaligned_write_be64 +#else +#define unaligned_read_ne16 unaligned_read_le16 +#define unaligned_read_ne32 unaligned_read_le32 +#define unaligned_read_ne64 unaligned_read_le64 + +#define unaligned_write_ne16 unaligned_write_le16 +#define unaligned_write_ne32 unaligned_write_le32 +#define unaligned_write_ne64 unaligned_write_le64 +#endif diff --git a/src/libbasic/include/basic/unit-name.h b/src/libbasic/include/basic/unit-name.h new file mode 100644 index 0000000000..44eadf0347 --- /dev/null +++ b/src/libbasic/include/basic/unit-name.h @@ -0,0 +1,367 @@ +#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 . +***/ + +#include + +#include "macro.h" + +#define UNIT_NAME_MAX 256 + +typedef enum UnitType { + UNIT_SERVICE = 0, + UNIT_SOCKET, + UNIT_BUSNAME, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_MOUNT, + UNIT_AUTOMOUNT, + UNIT_SWAP, + UNIT_TIMER, + UNIT_PATH, + UNIT_SLICE, + UNIT_SCOPE, + _UNIT_TYPE_MAX, + _UNIT_TYPE_INVALID = -1 +} UnitType; + +typedef enum UnitLoadState { + UNIT_STUB = 0, + UNIT_LOADED, + UNIT_NOT_FOUND, + UNIT_ERROR, + UNIT_MERGED, + UNIT_MASKED, + _UNIT_LOAD_STATE_MAX, + _UNIT_LOAD_STATE_INVALID = -1 +} 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_REQUISITE, + UNIT_WANTS, + UNIT_BINDS_TO, + UNIT_PART_OF, + + /* Inverse of the above */ + UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */ + UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */ + UNIT_WANTED_BY, /* inverse of 'wants' */ + UNIT_BOUND_BY, /* inverse of 'binds_to' */ + UNIT_CONSISTS_OF, /* inverse of 'part_of' */ + + /* Negative dependencies */ + UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */ + UNIT_CONFLICTED_BY, + + /* Order */ + UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ + UNIT_AFTER, + + /* On Failure */ + UNIT_ON_FAILURE, + + /* Triggers (i.e. a socket triggers a service) */ + UNIT_TRIGGERS, + UNIT_TRIGGERED_BY, + + /* Propagate reloads */ + UNIT_PROPAGATES_RELOAD_TO, + UNIT_RELOAD_PROPAGATED_FROM, + + /* Joins namespace of */ + UNIT_JOINS_NAMESPACE_OF, + + /* Reference information for GC logic */ + UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ + UNIT_REFERENCED_BY, + + _UNIT_DEPENDENCY_MAX, + _UNIT_DEPENDENCY_INVALID = -1 +} UnitDependency; + +typedef enum UnitNameFlags { + UNIT_NAME_PLAIN = 1, /* Allow foo.service */ + UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */ + UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */ + UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE, +} UnitNameFlags; + +bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_; +bool unit_prefix_is_valid(const char *p) _pure_; +bool unit_instance_is_valid(const char *i) _pure_; +bool unit_suffix_is_valid(const char *s) _pure_; + +static inline int unit_prefix_and_instance_is_valid(const char *p) { + /* For prefix+instance and instance the same rules apply */ + return unit_instance_is_valid(p); +} + +int unit_name_to_prefix(const char *n, char **prefix); +int unit_name_to_instance(const char *n, char **instance); +int unit_name_to_prefix_and_instance(const char *n, char **ret); + +UnitType unit_name_to_type(const char *n) _pure_; + +int unit_name_change_suffix(const char *n, const char *suffix, char **ret); + +int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); + +char *unit_name_escape(const char *f); +int unit_name_unescape(const char *f, char **ret); +int unit_name_path_escape(const char *f, char **ret); +int unit_name_path_unescape(const char *f, char **ret); + +int unit_name_replace_instance(const char *f, const char *i, char **ret); + +int unit_name_template(const char *f, char **ret); + +int unit_name_from_path(const char *path, const char *suffix, char **ret); +int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret); +int unit_name_to_path(const char *name, char **ret); + +char *unit_dbus_path_from_name(const char *name); +int unit_name_from_dbus_path(const char *path, char **name); + +const char* unit_dbus_interface_from_type(UnitType t); +const char *unit_dbus_interface_from_name(const char *name); + +typedef enum UnitNameMangle { + UNIT_NAME_NOGLOB, + UNIT_NAME_GLOB, +} UnitNameMangle; + +int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret); + +static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) { + return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret); +} + +int slice_build_parent_slice(const char *slice, char **ret); +int slice_build_subslice(const char *slice, const char*name, char **subslice); +bool slice_name_is_valid(const char *name); + +const char *unit_type_to_string(UnitType i) _const_; +UnitType unit_type_from_string(const char *s) _pure_; + +const char *unit_load_state_to_string(UnitLoadState i) _const_; +UnitLoadState unit_load_state_from_string(const char *s) _pure_; + +const char *unit_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/libbasic/include/basic/user-util.h b/src/libbasic/include/basic/user-util.h new file mode 100644 index 0000000000..8026eca3f4 --- /dev/null +++ b/src/libbasic/include/basic/user-util.h @@ -0,0 +1,70 @@ +#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 . +***/ + +#include +#include +#include + +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)) + +static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; +} diff --git a/src/libbasic/include/basic/utf8.h b/src/libbasic/include/basic/utf8.h new file mode 100644 index 0000000000..f9b9c9468b --- /dev/null +++ b/src/libbasic/include/basic/utf8.h @@ -0,0 +1,60 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "macro.h" +#include "missing.h" + +#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" +#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" + +bool unichar_is_valid(char32_t c); + +const char *utf8_is_valid(const char *s) _pure_; +char *ascii_is_valid(const char *s) _pure_; + +bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; +#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) + +char *utf8_escape_invalid(const char *s); +char *utf8_escape_non_printable(const char *str); + +size_t utf8_encode_unichar(char *out_utf8, char32_t g); +char *utf16_to_utf8(const void *s, size_t length); + +int utf8_encoded_valid_unichar(const char *str); +int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); + +static inline bool utf16_is_surrogate(char16_t c) { + return (0xd800 <= c && c <= 0xdfff); +} + +static inline bool utf16_is_trailing_surrogate(char16_t c) { + return (0xdc00 <= c && c <= 0xdfff); +} + +static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { + return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; +} diff --git a/src/libbasic/include/basic/util.h b/src/libbasic/include/basic/util.h new file mode 100644 index 0000000000..44497dcd78 --- /dev/null +++ b/src/libbasic/include/basic/util.h @@ -0,0 +1,192 @@ +#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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "formats-util.h" +#include "macro.h" +#include "missing.h" +#include "time-util.h" + +size_t page_size(void) _pure_; +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +static inline const char* yes_no(bool b) { + return b ? "yes" : "no"; +} + +static inline const char* true_false(bool b) { + return b ? "true" : "false"; +} + +static inline const char* one_zero(bool b) { + return b ? "1" : "0"; +} + +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); + +bool plymouth_running(void); + +bool display_is_local(const char *display) _pure_; +int socket_from_display(const char *display, char **path); + +int block_get_whole_disk(dev_t d, dev_t *ret); + +#define NULSTR_FOREACH(i, l) \ + for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) + +#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)) + +extern int saved_argc; +extern char **saved_argv; + +bool kexec_loaded(void); + +int prot_from_flags(int flags) _const_; + +int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); + +bool in_initrd(void); +void in_initrd_force(bool value); + +void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, + int (*compar) (const void *, const void *, void *), + void *arg); + +/** + * 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 memcpy requires src to be nonnull. We do nothing if n is 0. + */ +static inline void memcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return; + assert(src); + memcpy(dst, src, n); +} + +int on_ac_power(void); + +#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; +} + +static inline void _reset_errno_(int *saved_errno) { + errno = *saved_errno; +} + +#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno + +static inline int negative_errno(void) { + /* This helper should be used to shut up gcc if you know 'errno' is + * negative. Instead of "return -errno;", use "return negative_errno();" + * It will suppress bogus gcc warnings in case it assumes 'errno' might + * be 0 and thus the caller's error-handling might not be triggered. */ + assert_return(errno > 0, -EINVAL); + return -errno; +} + +static inline unsigned u64log2(uint64_t n) { +#if __SIZEOF_LONG_LONG__ == 8 + return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; +#else +#error "Wut?" +#endif +} + +static inline unsigned u32ctz(uint32_t n) { +#if __SIZEOF_INT__ == 4 + return __builtin_ctz(n); +#else +#error "Wut?" +#endif +} + +static inline unsigned log2i(int x) { + assert(x > 0); + + return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; +} + +static inline unsigned log2u(unsigned x) { + assert(x > 0); + + return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; +} + +static inline unsigned log2u_round_up(unsigned x) { + assert(x > 0); + + if (x == 1) + return 0; + + return log2u(x - 1) + 1; +} + +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); + +uint64_t physical_memory(void); +uint64_t physical_memory_scale(uint64_t v, uint64_t max); + +uint64_t system_tasks_max(void); +uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); + +int update_reboot_parameter_and_warn(const char *param); + +int version(void); diff --git a/src/libbasic/include/basic/verbs.h b/src/libbasic/include/basic/verbs.h new file mode 100644 index 0000000000..7b5e18510f --- /dev/null +++ b/src/libbasic/include/basic/verbs.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + 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 . +***/ + +#define VERB_ANY ((unsigned) -1) +#define VERB_DEFAULT 1U +#define VERB_NOCHROOT 2U + +typedef struct { + const char *verb; + unsigned min_args, max_args; + unsigned flags; + int (* const dispatch)(int argc, char *argv[], void *userdata); +} Verb; + +int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/libbasic/include/basic/virt.h b/src/libbasic/include/basic/virt.h new file mode 100644 index 0000000000..a538f07f6b --- /dev/null +++ b/src/libbasic/include/basic/virt.h @@ -0,0 +1,72 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" + +enum { + VIRTUALIZATION_NONE = 0, + + VIRTUALIZATION_VM_FIRST, + VIRTUALIZATION_KVM = VIRTUALIZATION_VM_FIRST, + VIRTUALIZATION_QEMU, + VIRTUALIZATION_BOCHS, + VIRTUALIZATION_XEN, + VIRTUALIZATION_UML, + VIRTUALIZATION_VMWARE, + VIRTUALIZATION_ORACLE, + VIRTUALIZATION_MICROSOFT, + VIRTUALIZATION_ZVM, + VIRTUALIZATION_PARALLELS, + VIRTUALIZATION_VM_OTHER, + VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, + + VIRTUALIZATION_CONTAINER_FIRST, + VIRTUALIZATION_SYSTEMD_NSPAWN = VIRTUALIZATION_CONTAINER_FIRST, + VIRTUALIZATION_LXC_LIBVIRT, + VIRTUALIZATION_LXC, + VIRTUALIZATION_OPENVZ, + VIRTUALIZATION_DOCKER, + VIRTUALIZATION_RKT, + VIRTUALIZATION_CONTAINER_OTHER, + VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, + + _VIRTUALIZATION_MAX, + _VIRTUALIZATION_INVALID = -1 +}; + +static inline bool VIRTUALIZATION_IS_VM(int x) { + return x >= VIRTUALIZATION_VM_FIRST && x <= VIRTUALIZATION_VM_LAST; +} + +static inline bool VIRTUALIZATION_IS_CONTAINER(int x) { + return x >= VIRTUALIZATION_CONTAINER_FIRST && x <= VIRTUALIZATION_CONTAINER_LAST; +} + +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/libbasic/include/basic/web-util.h b/src/libbasic/include/basic/web-util.h new file mode 100644 index 0000000000..e6bb6b53f5 --- /dev/null +++ b/src/libbasic/include/basic/web-util.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#include + +#include "macro.h" + +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/libbasic/include/basic/xattr-util.h b/src/libbasic/include/basic/xattr-util.h new file mode 100644 index 0000000000..6fa097bf7e --- /dev/null +++ b/src/libbasic/include/basic/xattr-util.h @@ -0,0 +1,37 @@ +#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 . +***/ + +#include +#include +#include + +#include "time-util.h" + +int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink); +int fgetxattr_malloc(int fd, const char *name, char **value); + +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); diff --git a/src/libbasic/include/basic/xml.h b/src/libbasic/include/basic/xml.h new file mode 100644 index 0000000000..41cb69f0dc --- /dev/null +++ b/src/libbasic/include/basic/xml.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +enum { + XML_END, + XML_TEXT, + XML_TAG_OPEN, + XML_TAG_CLOSE, + XML_TAG_CLOSE_EMPTY, + XML_ATTRIBUTE_NAME, + XML_ATTRIBUTE_VALUE, +}; + +int xml_tokenize(const char **p, char **name, void **state, unsigned *line); diff --git a/src/libbasic/src/Makefile b/src/libbasic/src/Makefile new file mode 100644 index 0000000000..f91014e14e --- /dev/null +++ b/src/libbasic/src/Makefile @@ -0,0 +1,241 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libbasic.la + +libbasic_la_SOURCES = \ + src/basic/missing.h \ + src/basic/missing_syscall.h \ + src/basic/raw-clone.h \ + src/basic/capability-util.c \ + src/basic/capability-util.h \ + src/basic/conf-files.c \ + src/basic/conf-files.h \ + src/basic/stdio-util.h \ + src/basic/hostname-util.h \ + src/basic/hostname-util.c \ + src/basic/unit-name.c \ + src/basic/unit-name.h \ + src/basic/ioprio.h \ + src/basic/securebits.h \ + src/basic/special.h \ + src/basic/list.h \ + src/basic/unaligned.h \ + src/basic/macro.h \ + src/basic/def.h \ + src/basic/sparse-endian.h \ + src/basic/refcnt.h \ + src/basic/util.c \ + src/basic/util.h \ + src/basic/io-util.c \ + src/basic/io-util.h \ + src/basic/string-util.c \ + src/basic/string-util.h \ + src/basic/fd-util.c \ + src/basic/fd-util.h \ + src/basic/parse-util.c \ + src/basic/parse-util.h \ + src/basic/user-util.c \ + src/basic/user-util.h \ + src/basic/rlimit-util.c \ + src/basic/rlimit-util.h \ + src/basic/dirent-util.c \ + src/basic/dirent-util.h \ + src/basic/xattr-util.c \ + src/basic/xattr-util.h \ + src/basic/chattr-util.c \ + src/basic/chattr-util.h \ + src/basic/proc-cmdline.c \ + src/basic/proc-cmdline.h \ + src/basic/fs-util.c \ + src/basic/fs-util.h \ + src/basic/syslog-util.c \ + src/basic/syslog-util.h \ + src/basic/stat-util.c \ + src/basic/stat-util.h \ + src/basic/mount-util.c \ + src/basic/mount-util.h \ + src/basic/hexdecoct.c \ + src/basic/hexdecoct.h \ + src/basic/glob-util.h \ + src/basic/glob-util.c \ + src/basic/extract-word.c \ + src/basic/extract-word.h \ + src/basic/escape.c \ + src/basic/escape.h \ + src/basic/cpu-set-util.c \ + src/basic/cpu-set-util.h \ + src/basic/lockfile-util.c \ + src/basic/lockfile-util.h \ + src/basic/path-util.c \ + src/basic/path-util.h \ + src/basic/time-util.c \ + src/basic/time-util.h \ + src/basic/locale-util.c \ + src/basic/locale-util.h \ + src/basic/umask-util.h \ + src/basic/signal-util.c \ + src/basic/signal-util.h \ + src/basic/string-table.c \ + src/basic/string-table.h \ + src/basic/mempool.c \ + src/basic/mempool.h \ + src/basic/hashmap.c \ + src/basic/hashmap.h \ + src/basic/hash-funcs.c \ + src/basic/hash-funcs.h \ + src/basic/siphash24.c \ + src/basic/siphash24.h \ + src/basic/set.h \ + src/basic/ordered-set.h \ + src/basic/ordered-set.c \ + src/basic/bitmap.c \ + src/basic/bitmap.h \ + src/basic/prioq.c \ + src/basic/prioq.h \ + src/basic/web-util.c \ + src/basic/web-util.h \ + src/basic/strv.c \ + src/basic/strv.h \ + src/basic/env-util.c \ + src/basic/env-util.h \ + src/basic/strbuf.c \ + src/basic/strbuf.h \ + src/basic/strxcpyx.c \ + src/basic/strxcpyx.h \ + src/basic/log.c \ + src/basic/log.h \ + src/basic/bus-label.c \ + src/basic/bus-label.h \ + src/basic/ratelimit.h \ + src/basic/ratelimit.c \ + src/basic/exit-status.c \ + src/basic/exit-status.h \ + src/basic/virt.c \ + src/basic/virt.h \ + src/basic/architecture.c \ + src/basic/architecture.h \ + src/basic/smack-util.c \ + src/basic/smack-util.h \ + src/basic/device-nodes.c \ + src/basic/device-nodes.h \ + src/basic/utf8.c \ + src/basic/utf8.h \ + src/basic/gunicode.c \ + src/basic/gunicode.h \ + src/basic/socket-util.c \ + src/basic/socket-util.h \ + src/basic/in-addr-util.c \ + src/basic/in-addr-util.h \ + src/basic/ether-addr-util.h \ + src/basic/ether-addr-util.c \ + src/basic/replace-var.c \ + src/basic/replace-var.h \ + src/basic/clock-util.c \ + src/basic/clock-util.h \ + src/basic/calendarspec.c \ + src/basic/calendarspec.h \ + src/basic/fileio.c \ + src/basic/fileio.h \ + src/basic/MurmurHash2.c \ + src/basic/MurmurHash2.h \ + src/basic/mkdir.c \ + src/basic/mkdir.h \ + src/basic/cgroup-util.c \ + src/basic/cgroup-util.h \ + src/basic/errno-list.c \ + src/basic/errno-list.h \ + src/basic/af-list.c \ + src/basic/af-list.h \ + src/basic/arphrd-list.c \ + src/basic/arphrd-list.h \ + src/basic/terminal-util.c \ + src/basic/terminal-util.h \ + src/basic/login-util.h \ + src/basic/login-util.c \ + src/basic/cap-list.c \ + src/basic/cap-list.h \ + src/basic/audit-util.c \ + src/basic/audit-util.h \ + src/basic/xml.c \ + src/basic/xml.h \ + src/basic/barrier.c \ + src/basic/barrier.h \ + src/basic/async.c \ + src/basic/async.h \ + src/basic/memfd-util.c \ + src/basic/memfd-util.h \ + src/basic/process-util.c \ + src/basic/process-util.h \ + src/basic/random-util.c \ + src/basic/random-util.h \ + src/basic/verbs.c \ + src/basic/verbs.h \ + src/basic/sigbus.c \ + src/basic/sigbus.h \ + src/basic/build.h \ + src/basic/socket-label.c \ + src/basic/label.c \ + src/basic/label.h \ + src/basic/btrfs-util.c \ + src/basic/btrfs-util.h \ + src/basic/btrfs-ctree.h \ + src/basic/selinux-util.c \ + src/basic/selinux-util.h \ + src/basic/mkdir-label.c \ + src/basic/fileio-label.c \ + src/basic/fileio-label.h \ + src/basic/rm-rf.c \ + src/basic/rm-rf.h \ + src/basic/copy.c \ + src/basic/copy.h \ + src/basic/alloc-util.h \ + src/basic/alloc-util.c \ + src/basic/formats-util.h \ + src/basic/nss-util.h + +nodist_libbasic_la_SOURCES = \ + src/basic/errno-from-name.h \ + src/basic/errno-to-name.h \ + src/basic/af-from-name.h \ + src/basic/af-to-name.h \ + src/basic/arphrd-from-name.h \ + src/basic/arphrd-to-name.h \ + src/basic/cap-from-name.h \ + src/basic/cap-to-name.h + +libbasic_la_CFLAGS = \ + $(SELINUX_CFLAGS) \ + $(CAP_CFLAGS) \ + -pthread + +libbasic_la_LIBADD = \ + $(SELINUX_LIBS) \ + $(CAP_LIBS) \ + -lrt \ + -lm + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libbasic/src/MurmurHash2.c b/src/libbasic/src/MurmurHash2.c new file mode 100644 index 0000000000..7d43f18f61 --- /dev/null +++ b/src/libbasic/src/MurmurHash2.c @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// MurmurHash2 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - This code makes a few assumptions about how your machine behaves - + +// 1. We can read a 4-byte value from any address without crashing +// 2. sizeof(int) == 4 + +// And it has a few limitations - + +// 1. It will not work incrementally. +// 2. It will not produce the same results on little-endian and big-endian +// machines. + +#include "basic/MurmurHash2.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const uint32_t m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + uint32_t h = seed ^ len; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data = (const unsigned char *)key; + + while (len >= 4) + { + uint32_t k = *(uint32_t*)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} diff --git a/src/libbasic/src/af-list.c b/src/libbasic/src/af-list.c new file mode 100644 index 0000000000..1c75c926bb --- /dev/null +++ b/src/libbasic/src/af-list.c @@ -0,0 +1,56 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/af-list.h" +#include "basic/macro.h" + +static const struct af_name* lookup_af(register const char *str, register unsigned int len); + +#include "basic/af-from-name.h" +#include "basic/af-to-name.h" + +const char *af_to_name(int id) { + + if (id <= 0) + return NULL; + + if (id >= (int) ELEMENTSOF(af_names)) + return NULL; + + return af_names[id]; +} + +int af_from_name(const char *name) { + const struct af_name *sc; + + assert(name); + + sc = lookup_af(name, strlen(name)); + if (!sc) + return AF_UNSPEC; + + return sc->id; +} + +int af_max(void) { + return ELEMENTSOF(af_names); +} diff --git a/src/libbasic/src/alloc-util.c b/src/libbasic/src/alloc-util.c new file mode 100644 index 0000000000..4e88a3a3bc --- /dev/null +++ b/src/libbasic/src/alloc-util.c @@ -0,0 +1,83 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/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/libbasic/src/architecture.c b/src/libbasic/src/architecture.c new file mode 100644 index 0000000000..22815f5353 --- /dev/null +++ b/src/libbasic/src/architecture.c @@ -0,0 +1,179 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/architecture.h" +#include "basic/macro.h" +#include "basic/string-table.h" +#include "basic/string-util.h" + +int uname_architecture(void) { + + /* Return a sanitized enum identifying the architecture we are + * running on. This is based on uname(), and the user may + * hence control what this returns by using + * personality(). This puts the user in control on systems + * that can run binaries of multiple architectures. + * + * We do not translate the string returned by uname() + * 1:1. Instead we try to clean it up and break down the + * confusion on x86 and arm in particular. + * + * We do not try to distinguish CPUs not CPU features, but + * actual architectures, i.e. that have genuinely different + * code. */ + + static const struct { + const char *machine; + int arch; + } arch_map[] = { +#if defined(__x86_64__) || defined(__i386__) + { "x86_64", ARCHITECTURE_X86_64 }, + { "i686", ARCHITECTURE_X86 }, + { "i586", ARCHITECTURE_X86 }, + { "i486", ARCHITECTURE_X86 }, + { "i386", ARCHITECTURE_X86 }, +#elif defined(__powerpc__) || defined(__powerpc64__) + { "ppc64", ARCHITECTURE_PPC64 }, + { "ppc64le", ARCHITECTURE_PPC64_LE }, + { "ppc", ARCHITECTURE_PPC }, + { "ppcle", ARCHITECTURE_PPC_LE }, +#elif defined(__ia64__) + { "ia64", ARCHITECTURE_IA64 }, +#elif defined(__hppa__) || defined(__hppa64__) + { "parisc64", ARCHITECTURE_PARISC64 }, + { "parisc", ARCHITECTURE_PARISC }, +#elif defined(__s390__) || defined(__s390x__) + { "s390x", ARCHITECTURE_S390X }, + { "s390", ARCHITECTURE_S390 }, +#elif defined(__sparc__) + { "sparc64", ARCHITECTURE_SPARC64 }, + { "sparc", ARCHITECTURE_SPARC }, +#elif defined(__mips__) || defined(__mips64__) + { "mips64", ARCHITECTURE_MIPS64 }, + { "mips", ARCHITECTURE_MIPS }, +#elif defined(__alpha__) + { "alpha" , ARCHITECTURE_ALPHA }, +#elif defined(__arm__) || defined(__aarch64__) + { "aarch64", ARCHITECTURE_ARM64 }, + { "aarch64_be", ARCHITECTURE_ARM64_BE }, + { "armv4l", ARCHITECTURE_ARM }, + { "armv4b", ARCHITECTURE_ARM_BE }, + { "armv4tl", ARCHITECTURE_ARM }, + { "armv4tb", ARCHITECTURE_ARM_BE }, + { "armv5tl", ARCHITECTURE_ARM }, + { "armv5tb", ARCHITECTURE_ARM_BE }, + { "armv5tel", ARCHITECTURE_ARM }, + { "armv5teb" , ARCHITECTURE_ARM_BE }, + { "armv5tejl", ARCHITECTURE_ARM }, + { "armv5tejb", ARCHITECTURE_ARM_BE }, + { "armv6l", ARCHITECTURE_ARM }, + { "armv6b", ARCHITECTURE_ARM_BE }, + { "armv7l", ARCHITECTURE_ARM }, + { "armv7b", ARCHITECTURE_ARM_BE }, + { "armv7ml", ARCHITECTURE_ARM }, + { "armv7mb", ARCHITECTURE_ARM_BE }, + { "armv4l", ARCHITECTURE_ARM }, + { "armv4b", ARCHITECTURE_ARM_BE }, + { "armv4tl", ARCHITECTURE_ARM }, + { "armv4tb", ARCHITECTURE_ARM_BE }, + { "armv5tl", ARCHITECTURE_ARM }, + { "armv5tb", ARCHITECTURE_ARM_BE }, + { "armv5tel", ARCHITECTURE_ARM }, + { "armv5teb", ARCHITECTURE_ARM_BE }, + { "armv5tejl", ARCHITECTURE_ARM }, + { "armv5tejb", ARCHITECTURE_ARM_BE }, + { "armv6l", ARCHITECTURE_ARM }, + { "armv6b", ARCHITECTURE_ARM_BE }, + { "armv7l", ARCHITECTURE_ARM }, + { "armv7b", ARCHITECTURE_ARM_BE }, + { "armv7ml", ARCHITECTURE_ARM }, + { "armv7mb", ARCHITECTURE_ARM_BE }, + { "armv8l", ARCHITECTURE_ARM }, + { "armv8b", ARCHITECTURE_ARM_BE }, +#elif defined(__sh__) || defined(__sh64__) + { "sh5", ARCHITECTURE_SH64 }, + { "sh2", ARCHITECTURE_SH }, + { "sh2a", ARCHITECTURE_SH }, + { "sh3", ARCHITECTURE_SH }, + { "sh4", ARCHITECTURE_SH }, + { "sh4a", ARCHITECTURE_SH }, +#elif defined(__m68k__) + { "m68k", ARCHITECTURE_M68K }, +#elif defined(__tilegx__) + { "tilegx", ARCHITECTURE_TILEGX }, +#elif defined(__cris__) + { "crisv32", ARCHITECTURE_CRIS }, +#elif defined(__nios2__) + { "nios2", ARCHITECTURE_NIOS2 }, +#else +#error "Please register your architecture here!" +#endif + }; + + static int cached = _ARCHITECTURE_INVALID; + struct utsname u; + unsigned i; + + if (cached != _ARCHITECTURE_INVALID) + return cached; + + assert_se(uname(&u) >= 0); + + for (i = 0; i < ELEMENTSOF(arch_map); i++) + if (streq(arch_map[i].machine, u.machine)) + return cached = arch_map[i].arch; + + assert_not_reached("Couldn't identify architecture. You need to patch systemd."); + return _ARCHITECTURE_INVALID; +} + +static const char *const architecture_table[_ARCHITECTURE_MAX] = { + [ARCHITECTURE_X86] = "x86", + [ARCHITECTURE_X86_64] = "x86-64", + [ARCHITECTURE_PPC] = "ppc", + [ARCHITECTURE_PPC_LE] = "ppc-le", + [ARCHITECTURE_PPC64] = "ppc64", + [ARCHITECTURE_PPC64_LE] = "ppc64-le", + [ARCHITECTURE_IA64] = "ia64", + [ARCHITECTURE_PARISC] = "parisc", + [ARCHITECTURE_PARISC64] = "parisc64", + [ARCHITECTURE_S390] = "s390", + [ARCHITECTURE_S390X] = "s390x", + [ARCHITECTURE_SPARC] = "sparc", + [ARCHITECTURE_SPARC64] = "sparc64", + [ARCHITECTURE_MIPS] = "mips", + [ARCHITECTURE_MIPS_LE] = "mips-le", + [ARCHITECTURE_MIPS64] = "mips64", + [ARCHITECTURE_MIPS64_LE] = "mips64-le", + [ARCHITECTURE_ALPHA] = "alpha", + [ARCHITECTURE_ARM] = "arm", + [ARCHITECTURE_ARM_BE] = "arm-be", + [ARCHITECTURE_ARM64] = "arm64", + [ARCHITECTURE_ARM64_BE] = "arm64-be", + [ARCHITECTURE_SH] = "sh", + [ARCHITECTURE_SH64] = "sh64", + [ARCHITECTURE_M68K] = "m68k", + [ARCHITECTURE_TILEGX] = "tilegx", + [ARCHITECTURE_CRIS] = "cris", + [ARCHITECTURE_NIOS2] = "nios2", +}; + +DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/libbasic/src/arphrd-list.c b/src/libbasic/src/arphrd-list.c new file mode 100644 index 0000000000..649bd37456 --- /dev/null +++ b/src/libbasic/src/arphrd-list.c @@ -0,0 +1,56 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/arphrd-list.h" +#include "basic/macro.h" + +static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len); + +#include "basic/arphrd-from-name.h" +#include "basic/arphrd-to-name.h" + +const char *arphrd_to_name(int id) { + + if (id <= 0) + return NULL; + + if (id >= (int) ELEMENTSOF(arphrd_names)) + return NULL; + + return arphrd_names[id]; +} + +int arphrd_from_name(const char *name) { + const struct arphrd_name *sc; + + assert(name); + + sc = lookup_arphrd(name, strlen(name)); + if (!sc) + return 0; + + return sc->id; +} + +int arphrd_max(void) { + return ELEMENTSOF(arphrd_names); +} diff --git a/src/libbasic/src/async.c b/src/libbasic/src/async.c new file mode 100644 index 0000000000..0e2fce6850 --- /dev/null +++ b/src/libbasic/src/async.c @@ -0,0 +1,94 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/async.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/util.h" + +int asynchronous_job(void* (*func)(void *p), void *arg) { + pthread_attr_t a; + pthread_t t; + int r; + + /* It kinda sucks that we have to resort to threads to + * implement an asynchronous sync(), but well, such is + * life. + * + * Note that issuing this command right before exiting a + * process will cause the process to wait for the sync() to + * complete. This function hence is nicely asynchronous really + * only in long running processes. */ + + r = pthread_attr_init(&a); + if (r > 0) + return -r; + + r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); + if (r > 0) + goto finish; + + r = pthread_create(&t, &a, func, arg); + +finish: + pthread_attr_destroy(&a); + return -r; +} + +static void *sync_thread(void *p) { + sync(); + return NULL; +} + +int asynchronous_sync(void) { + log_debug("Spawning new thread for sync"); + + return asynchronous_job(sync_thread, NULL); +} + +static void *close_thread(void *p) { + assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); + return NULL; +} + +int asynchronous_close(int fd) { + int r; + + /* This is supposed to behave similar to safe_close(), but + * actually invoke close() asynchronously, so that it will + * never block. Ideally the kernel would have an API for this, + * but it doesn't, so we work around it, and hide this as a + * far away as we can. */ + + if (fd >= 0) { + PROTECT_ERRNO; + + r = asynchronous_job(close_thread, FD_TO_PTR(fd)); + if (r < 0) + assert_se(close_nointr(fd) != -EBADF); + } + + return -1; +} diff --git a/src/libbasic/src/audit-util.c b/src/libbasic/src/audit-util.c new file mode 100644 index 0000000000..0cd8543374 --- /dev/null +++ b/src/libbasic/src/audit-util.c @@ -0,0 +1,105 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/user-util.h" + +int audit_session_from_pid(pid_t pid, uint32_t *id) { + _cleanup_free_ char *s = NULL; + const char *p; + uint32_t u; + int r; + + assert(id); + + /* We don't convert ENOENT to ESRCH here, since we can't + * really distuingish between "audit is not available in the + * kernel" and "the process does not exist", both which will + * result in ENOENT. */ + + p = procfs_file_alloca(pid, "sessionid"); + + r = read_one_line_file(p, &s); + if (r < 0) + return r; + + r = safe_atou32(s, &u); + if (r < 0) + return r; + + if (u == AUDIT_SESSION_INVALID || u <= 0) + return -ENODATA; + + *id = u; + return 0; +} + +int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { + _cleanup_free_ char *s = NULL; + const char *p; + uid_t u; + int r; + + assert(uid); + + p = procfs_file_alloca(pid, "loginuid"); + + r = read_one_line_file(p, &s); + if (r < 0) + return r; + + r = parse_uid(s, &u); + if (r == -ENXIO) /* the UID was -1 */ + return -ENODATA; + if (r < 0) + return r; + + *uid = (uid_t) u; + return 0; +} + +bool use_audit(void) { + static int cached_use = -1; + + if (cached_use < 0) { + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); + if (fd < 0) + cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT; + else { + cached_use = true; + safe_close(fd); + } + } + + return cached_use; +} diff --git a/src/libbasic/src/barrier.c b/src/libbasic/src/barrier.c new file mode 100644 index 0000000000..ad685bb3c1 --- /dev/null +++ b/src/libbasic/src/barrier.c @@ -0,0 +1,415 @@ +/*** + This file is part of systemd. + + Copyright 2014 David Herrmann + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/barrier.h" +#include "basic/fd-util.h" +#include "basic/macro.h" + +/** + * Barriers + * This barrier implementation provides a simple synchronization method based + * on file-descriptors that can safely be used between threads and processes. A + * barrier object contains 2 shared counters based on eventfd. Both processes + * can now place barriers and wait for the other end to reach a random or + * specific barrier. + * Barriers are numbered, so you can either wait for the other end to reach any + * barrier or the last barrier that you placed. This way, you can use barriers + * for one-way *and* full synchronization. Note that even-though barriers are + * numbered, these numbers are internal and recycled once both sides reached the + * same barrier (implemented as a simple signed counter). It is thus not + * possible to address barriers by their ID. + * + * Barrier-API: Both ends can place as many barriers via barrier_place() as + * they want and each pair of barriers on both sides will be implicitly linked. + * Each side can use the barrier_wait/sync_*() family of calls to wait for the + * other side to place a specific barrier. barrier_wait_next() waits until the + * other side calls barrier_place(). No links between the barriers are + * considered and this simply serves as most basic asynchronous barrier. + * barrier_sync_next() is like barrier_wait_next() and waits for the other side + * to place their next barrier via barrier_place(). However, it only waits for + * barriers that are linked to a barrier we already placed. If the other side + * already placed more barriers than we did, barrier_sync_next() returns + * immediately. + * barrier_sync() extends barrier_sync_next() and waits until the other end + * placed as many barriers via barrier_place() as we did. If they already placed + * as many as we did (or more), it returns immediately. + * + * Additionally to basic barriers, an abortion event is available. + * barrier_abort() places an abortion event that cannot be undone. An abortion + * immediately cancels all placed barriers and replaces them. Any running and + * following wait/sync call besides barrier_wait_abortion() will immediately + * return false on both sides (otherwise, they always return true). + * barrier_abort() can be called multiple times on both ends and will be a + * no-op if already called on this side. + * barrier_wait_abortion() can be used to wait for the other side to call + * barrier_abort() and is the only wait/sync call that does not return + * immediately if we aborted outself. It only returns once the other side + * called barrier_abort(). + * + * Barriers can be used for in-process and inter-process synchronization. + * However, for in-process synchronization you could just use mutexes. + * Therefore, main target is IPC and we require both sides to *not* share the FD + * table. If that's given, barriers provide target tracking: If the remote side + * exit()s, an abortion event is implicitly queued on the other side. This way, + * a sync/wait call will be woken up if the remote side crashed or exited + * unexpectedly. However, note that these abortion events are only queued if the + * barrier-queue has been drained. Therefore, it is safe to place a barrier and + * exit. The other side can safely wait on the barrier even though the exit + * queued an abortion event. Usually, the abortion event would overwrite the + * barrier, however, that's not true for exit-abortion events. Those are only + * queued if the barrier-queue is drained (thus, the receiving side has placed + * more barriers than the remote side). + */ + +/** + * barrier_create() - Initialize a barrier object + * @obj: barrier to initialize + * + * This initializes a barrier object. The caller is responsible of allocating + * the memory and keeping it valid. The memory does not have to be zeroed + * beforehand. + * Two eventfd objects are allocated for each barrier. If allocation fails, an + * error is returned. + * + * If this function fails, the barrier is reset to an invalid state so it is + * safe to call barrier_destroy() on the object regardless whether the + * initialization succeeded or not. + * + * The caller is responsible to destroy the object via barrier_destroy() before + * releasing the underlying memory. + * + * Returns: 0 on success, negative error code on failure. + */ +int barrier_create(Barrier *b) { + _cleanup_(barrier_destroyp) Barrier *staging = b; + int r; + + assert(b); + + b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (b->me < 0) + return -errno; + + b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (b->them < 0) + return -errno; + + r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK); + if (r < 0) + return -errno; + + staging = NULL; + return 0; +} + +/** + * barrier_destroy() - Destroy a barrier object + * @b: barrier to destroy or NULL + * + * This destroys a barrier object that has previously been passed to + * barrier_create(). The object is released and reset to invalid + * state. Therefore, it is safe to call barrier_destroy() multiple + * times or even if barrier_create() failed. However, barrier must be + * always initialized with BARRIER_NULL. + * + * If @b is NULL, this is a no-op. + */ +void barrier_destroy(Barrier *b) { + if (!b) + return; + + b->me = safe_close(b->me); + b->them = safe_close(b->them); + safe_close_pair(b->pipe); + b->barriers = 0; +} + +/** + * barrier_set_role() - Set the local role of the barrier + * @b: barrier to operate on + * @role: role to set on the barrier + * + * This sets the roles on a barrier object. This is needed to know + * which side of the barrier you're on. Usually, the parent creates + * the barrier via barrier_create() and then calls fork() or clone(). + * Therefore, the FDs are duplicated and the child retains the same + * barrier object. + * + * Both sides need to call barrier_set_role() after fork() or clone() + * are done. If this is not done, barriers will not work correctly. + * + * Note that barriers could be supported without fork() or clone(). However, + * this is currently not needed so it hasn't been implemented. + */ +void barrier_set_role(Barrier *b, unsigned int role) { + int fd; + + assert(b); + assert(role == BARRIER_PARENT || role == BARRIER_CHILD); + /* make sure this is only called once */ + assert(b->pipe[0] >= 0 && b->pipe[1] >= 0); + + if (role == BARRIER_PARENT) + b->pipe[1] = safe_close(b->pipe[1]); + else { + b->pipe[0] = safe_close(b->pipe[0]); + + /* swap me/them for children */ + fd = b->me; + b->me = b->them; + b->them = fd; + } +} + +/* places barrier; returns false if we aborted, otherwise true */ +static bool barrier_write(Barrier *b, uint64_t buf) { + ssize_t len; + + /* prevent new sync-points if we already aborted */ + 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)); + + if (len != sizeof(buf)) + goto error; + + /* lock if we aborted */ + if (buf >= (uint64_t)BARRIER_ABORTION) { + if (barrier_they_aborted(b)) + b->barriers = BARRIER_WE_ABORTED; + else + b->barriers = BARRIER_I_ABORTED; + } else if (!barrier_is_aborted(b)) + b->barriers += buf; + + return !barrier_i_aborted(b); + +error: + /* If there is an unexpected error, we have to make this fatal. There + * is no way we can recover from sync-errors. Therefore, we close the + * pipe-ends and treat this as abortion. The other end will notice the + * pipe-close and treat it as abortion, too. */ + + safe_close_pair(b->pipe); + b->barriers = BARRIER_WE_ABORTED; + return false; +} + +/* waits for barriers; returns false if they aborted, otherwise true */ +static bool barrier_read(Barrier *b, int64_t comp) { + if (barrier_they_aborted(b)) + return false; + + while (b->barriers > comp) { + struct pollfd pfd[2] = { + { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1], + .events = POLLHUP }, + { .fd = b->them, + .events = POLLIN }}; + uint64_t buf; + int r; + + r = poll(pfd, 2, -1); + if (r < 0 && IN_SET(errno, EAGAIN, EINTR)) + continue; + else if (r < 0) + goto error; + + if (pfd[1].revents) { + ssize_t len; + + /* events on @them signal new data for us */ + len = read(b->them, &buf, sizeof(buf)); + if (len < 0 && IN_SET(errno, EAGAIN, EINTR)) + continue; + + if (len != sizeof(buf)) + goto error; + } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL)) + /* POLLHUP on the pipe tells us the other side exited. + * We treat this as implicit abortion. But we only + * handle it if there's no event on the eventfd. This + * guarantees that exit-abortions do not overwrite real + * barriers. */ + buf = BARRIER_ABORTION; + else + continue; + + /* lock if they aborted */ + if (buf >= (uint64_t)BARRIER_ABORTION) { + if (barrier_i_aborted(b)) + b->barriers = BARRIER_WE_ABORTED; + else + b->barriers = BARRIER_THEY_ABORTED; + } else if (!barrier_is_aborted(b)) + b->barriers -= buf; + } + + return !barrier_they_aborted(b); + +error: + /* If there is an unexpected error, we have to make this fatal. There + * is no way we can recover from sync-errors. Therefore, we close the + * pipe-ends and treat this as abortion. The other end will notice the + * pipe-close and treat it as abortion, too. */ + + safe_close_pair(b->pipe); + b->barriers = BARRIER_WE_ABORTED; + return false; +} + +/** + * barrier_place() - Place a new barrier + * @b: barrier object + * + * This places a new barrier on the barrier object. If either side already + * aborted, this is a no-op and returns "false". Otherwise, the barrier is + * placed and this returns "true". + * + * Returns: true if barrier was placed, false if either side aborted. + */ +bool barrier_place(Barrier *b) { + assert(b); + + if (barrier_is_aborted(b)) + return false; + + barrier_write(b, BARRIER_SINGLE); + return true; +} + +/** + * barrier_abort() - Abort the synchronization + * @b: barrier object to abort + * + * This aborts the barrier-synchronization. If barrier_abort() was already + * called on this side, this is a no-op. Otherwise, the barrier is put into the + * ABORT-state and will stay there. The other side is notified about the + * abortion. Any following attempt to place normal barriers or to wait on normal + * barriers will return immediately as "false". + * + * You can wait for the other side to call barrier_abort(), too. Use + * barrier_wait_abortion() for that. + * + * Returns: false if the other side already aborted, true otherwise. + */ +bool barrier_abort(Barrier *b) { + assert(b); + + barrier_write(b, BARRIER_ABORTION); + return !barrier_they_aborted(b); +} + +/** + * barrier_wait_next() - Wait for the next barrier of the other side + * @b: barrier to operate on + * + * This waits until the other side places its next barrier. This is independent + * of any barrier-links and just waits for any next barrier of the other side. + * + * If either side aborted, this returns false. + * + * Returns: false if either side aborted, true otherwise. + */ +bool barrier_wait_next(Barrier *b) { + assert(b); + + if (barrier_is_aborted(b)) + return false; + + barrier_read(b, b->barriers - 1); + return !barrier_is_aborted(b); +} + +/** + * barrier_wait_abortion() - Wait for the other side to abort + * @b: barrier to operate on + * + * This waits until the other side called barrier_abort(). This can be called + * regardless whether the local side already called barrier_abort() or not. + * + * If the other side has already aborted, this returns immediately. + * + * Returns: false if the local side aborted, true otherwise. + */ +bool barrier_wait_abortion(Barrier *b) { + assert(b); + + barrier_read(b, BARRIER_THEY_ABORTED); + return !barrier_i_aborted(b); +} + +/** + * barrier_sync_next() - Wait for the other side to place a next linked barrier + * @b: barrier to operate on + * + * This is like barrier_wait_next() and waits for the other side to call + * barrier_place(). However, this only waits for linked barriers. That means, if + * the other side already placed more barriers than (or as much as) we did, this + * returns immediately instead of waiting. + * + * If either side aborted, this returns false. + * + * Returns: false if either side aborted, true otherwise. + */ +bool barrier_sync_next(Barrier *b) { + assert(b); + + if (barrier_is_aborted(b)) + return false; + + barrier_read(b, MAX((int64_t)0, b->barriers - 1)); + return !barrier_is_aborted(b); +} + +/** + * barrier_sync() - Wait for the other side to place as many barriers as we did + * @b: barrier to operate on + * + * This is like barrier_sync_next() but waits for the other side to call + * barrier_place() as often as we did (in total). If they already placed as much + * as we did (or more), this returns immediately instead of waiting. + * + * If either side aborted, this returns false. + * + * Returns: false if either side aborted, true otherwise. + */ +bool barrier_sync(Barrier *b) { + assert(b); + + if (barrier_is_aborted(b)) + return false; + + barrier_read(b, 0); + return !barrier_is_aborted(b); +} diff --git a/src/libbasic/src/bitmap.c b/src/libbasic/src/bitmap.c new file mode 100644 index 0000000000..0b6799ef5a --- /dev/null +++ b/src/libbasic/src/bitmap.c @@ -0,0 +1,237 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bitmap.h" +#include "basic/hashmap.h" +#include "basic/macro.h" + +struct Bitmap { + uint64_t *bitmaps; + size_t n_bitmaps; + size_t bitmaps_allocated; +}; + +/* Bitmaps are only meant to store relatively small numbers + * (corresponding to, say, an enum), so it is ok to limit + * the max entry. 64k should be plenty. */ +#define BITMAPS_MAX_ENTRY 0xffff + +/* This indicates that we reached the end of the bitmap */ +#define BITMAP_END ((unsigned) -1) + +#define BITMAP_NUM_TO_OFFSET(n) ((n) / (sizeof(uint64_t) * 8)) +#define BITMAP_NUM_TO_REM(n) ((n) % (sizeof(uint64_t) * 8)) +#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(uint64_t) * 8 + (rem)) + +Bitmap *bitmap_new(void) { + return new0(Bitmap, 1); +} + +Bitmap *bitmap_copy(Bitmap *b) { + Bitmap *ret; + + ret = bitmap_new(); + if (!ret) + return NULL; + + ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps); + if (!ret->bitmaps) { + free(ret); + return NULL; + } + + ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps; + return ret; +} + +void bitmap_free(Bitmap *b) { + if (!b) + return; + + free(b->bitmaps); + free(b); +} + +int bitmap_ensure_allocated(Bitmap **b) { + Bitmap *a; + + assert(b); + + if (*b) + return 0; + + a = bitmap_new(); + if (!a) + return -ENOMEM; + + *b = a; + + return 0; +} + +int bitmap_set(Bitmap *b, unsigned n) { + uint64_t bitmask; + unsigned offset; + + assert(b); + + /* we refuse to allocate huge bitmaps */ + if (n > BITMAPS_MAX_ENTRY) + return -ERANGE; + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) { + if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1)) + return -ENOMEM; + + b->n_bitmaps = offset + 1; + } + + bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); + + b->bitmaps[offset] |= bitmask; + + return 0; +} + +void bitmap_unset(Bitmap *b, unsigned n) { + uint64_t bitmask; + unsigned offset; + + if (!b) + return; + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) + return; + + bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); + + b->bitmaps[offset] &= ~bitmask; +} + +bool bitmap_isset(Bitmap *b, unsigned n) { + uint64_t bitmask; + unsigned offset; + + if (!b) + return false; + + offset = BITMAP_NUM_TO_OFFSET(n); + + if (offset >= b->n_bitmaps) + return false; + + bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); + + return !!(b->bitmaps[offset] & bitmask); +} + +bool bitmap_isclear(Bitmap *b) { + unsigned i; + + if (!b) + return true; + + for (i = 0; i < b->n_bitmaps; i++) + if (b->bitmaps[i] != 0) + return false; + + return true; +} + +void bitmap_clear(Bitmap *b) { + + if (!b) + return; + + b->bitmaps = mfree(b->bitmaps); + b->n_bitmaps = 0; + b->bitmaps_allocated = 0; +} + +bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) { + uint64_t bitmask; + unsigned offset, rem; + + assert(i); + assert(n); + + if (!b || i->idx == BITMAP_END) + return false; + + offset = BITMAP_NUM_TO_OFFSET(i->idx); + rem = BITMAP_NUM_TO_REM(i->idx); + bitmask = UINT64_C(1) << rem; + + for (; offset < b->n_bitmaps; offset ++) { + if (b->bitmaps[offset]) { + for (; bitmask; bitmask <<= 1, rem ++) { + if (b->bitmaps[offset] & bitmask) { + *n = BITMAP_OFFSET_TO_NUM(offset, rem); + i->idx = *n + 1; + + return true; + } + } + } + + rem = 0; + bitmask = 1; + } + + i->idx = BITMAP_END; + + return false; +} + +bool bitmap_equal(Bitmap *a, Bitmap *b) { + size_t common_n_bitmaps; + Bitmap *c; + unsigned i; + + if (a == b) + return true; + + if (!a != !b) + return false; + + if (!a) + return true; + + common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps); + if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0) + return false; + + c = a->n_bitmaps > b->n_bitmaps ? a : b; + for (i = common_n_bitmaps; i < c->n_bitmaps; i++) + if (c->bitmaps[i] != 0) + return false; + + return true; +} diff --git a/src/libbasic/src/btrfs-util.c b/src/libbasic/src/btrfs-util.c new file mode 100644 index 0000000000..acbcb5f6b9 --- /dev/null +++ b/src/libbasic/src/btrfs-util.c @@ -0,0 +1,2076 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_LINUX_BTRFS_H +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/btrfs-ctree.h" +#include "basic/btrfs-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/sparse-endian.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "basic/util.h" + +/* WARNING: Be careful with file system ioctls! When we get an fd, we + * need to make sure it either refers to only a regular file or + * directory, or that it is located on btrfs, before invoking any + * btrfs ioctls. The ioctl numbers are reused by some device drivers + * (such as DRM), and hence might have bad effects when invoked on + * device nodes (that reference drivers) rather than fds to normal + * files or directories. */ + +static int validate_subvolume_name(const char *name) { + + if (!filename_is_valid(name)) + return -EINVAL; + + if (strlen(name) > BTRFS_SUBVOL_NAME_MAX) + return -E2BIG; + + return 0; +} + +static int open_parent(const char *path, int flags) { + _cleanup_free_ char *parent = NULL; + int fd; + + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + fd = open(parent, flags); + if (fd < 0) + return -errno; + + return fd; +} + +static int extract_subvolume_name(const char *path, const char **subvolume) { + const char *fn; + int r; + + assert(path); + assert(subvolume); + + fn = basename(path); + + r = validate_subvolume_name(fn); + if (r < 0) + return r; + + *subvolume = fn; + return 0; +} + +int btrfs_is_filesystem(int fd) { + struct statfs sfs; + + assert(fd >= 0); + + if (fstatfs(fd, &sfs) < 0) + return -errno; + + return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); +} + +int btrfs_is_subvol_fd(int fd) { + struct stat st; + + assert(fd >= 0); + + /* On btrfs subvolumes always have the inode 256 */ + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode) || st.st_ino != 256) + return 0; + + 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; + const char *subvolume; + int r; + + assert(path); + + r = extract_subvolume_name(path, &subvolume); + if (r < 0) + return r; + + fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return fd; + + strncpy(args.name, subvolume, sizeof(args.name)-1); + + if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) + return -errno; + + return 0; +} + +int btrfs_subvol_make_label(const char *path) { + int r; + + assert(path); + + r = mac_selinux_create_file_prepare(path, S_IFDIR); + if (r < 0) + return r; + + r = btrfs_subvol_make(path); + mac_selinux_create_file_clear(); + + if (r < 0) + return r; + + return mac_smack_fix(path, false, false); +} + +int btrfs_subvol_set_read_only_fd(int fd, bool b) { + uint64_t flags, nflags; + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode) || st.st_ino != 256) + return -EINVAL; + + if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) + return -errno; + + if (b) + nflags = flags | BTRFS_SUBVOL_RDONLY; + else + nflags = flags & ~BTRFS_SUBVOL_RDONLY; + + if (flags == nflags) + return 0; + + if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0) + return -errno; + + return 0; +} + +int btrfs_subvol_set_read_only(const char *path, bool b) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + return btrfs_subvol_set_read_only_fd(fd, b); +} + +int btrfs_subvol_get_read_only_fd(int fd) { + uint64_t flags; + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode) || st.st_ino != 256) + return -EINVAL; + + if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) + return -errno; + + return !!(flags & BTRFS_SUBVOL_RDONLY); +} + +int btrfs_reflink(int infd, int outfd) { + struct stat st; + int r; + + assert(infd >= 0); + assert(outfd >= 0); + + /* Make sure we invoke the ioctl on a regular file, so that no + * device driver accidentally gets it. */ + + if (fstat(outfd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EINVAL; + + r = ioctl(outfd, BTRFS_IOC_CLONE, infd); + if (r < 0) + return -errno; + + return 0; +} + +int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) { + struct btrfs_ioctl_clone_range_args args = { + .src_fd = infd, + .src_offset = in_offset, + .src_length = sz, + .dest_offset = out_offset, + }; + struct stat st; + int r; + + assert(infd >= 0); + assert(outfd >= 0); + assert(sz > 0); + + if (fstat(outfd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EINVAL; + + r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args); + if (r < 0) + return -errno; + + return 0; +} + +int btrfs_get_block_device_fd(int fd, dev_t *dev) { + struct btrfs_ioctl_fs_info_args fsi = {}; + uint64_t id; + int r; + + assert(fd >= 0); + assert(dev); + + r = btrfs_is_filesystem(fd); + if (r < 0) + return r; + if (!r) + return -ENOTTY; + + if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0) + return -errno; + + /* We won't do this for btrfs RAID */ + if (fsi.num_devices != 1) + return 0; + + for (id = 1; id <= fsi.max_id; id++) { + struct btrfs_ioctl_dev_info_args di = { + .devid = id, + }; + struct stat st; + + if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) { + if (errno == ENODEV) + continue; + + return -errno; + } + + if (stat((char*) di.path, &st) < 0) + return -errno; + + if (!S_ISBLK(st.st_mode)) + return -ENODEV; + + if (major(st.st_rdev) == 0) + return -ENODEV; + + *dev = st.st_rdev; + return 1; + } + + return -ENODEV; +} + +int btrfs_get_block_device(const char *path, dev_t *dev) { + _cleanup_close_ int fd = -1; + + assert(path); + assert(dev); + + fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return btrfs_get_block_device_fd(fd, dev); +} + +int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { + struct btrfs_ioctl_ino_lookup_args args = { + .objectid = BTRFS_FIRST_FREE_OBJECTID + }; + int r; + + assert(fd >= 0); + assert(ret); + + r = btrfs_is_filesystem(fd); + if (r < 0) + return r; + if (!r) + return -ENOTTY; + + if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + return -errno; + + *ret = args.treeid; + return 0; +} + +int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) { + _cleanup_close_ int subvol_fd = -1; + + assert(fd >= 0); + assert(ret); + + subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (subvol_fd < 0) + return -errno; + + return btrfs_subvol_get_id_fd(subvol_fd, ret); +} + +static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) { + assert(args); + + /* the objectid, type, offset together make up the btrfs key, + * which is considered a single 136byte integer when + * comparing. This call increases the counter by one, dealing + * with the overflow between the overflows */ + + if (args->key.min_offset < (uint64_t) -1) { + args->key.min_offset++; + return true; + } + + if (args->key.min_type < (uint8_t) -1) { + args->key.min_type++; + args->key.min_offset = 0; + return true; + } + + if (args->key.min_objectid < (uint64_t) -1) { + args->key.min_objectid++; + args->key.min_offset = 0; + args->key.min_type = 0; + return true; + } + + return 0; +} + +static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) { + assert(args); + assert(h); + + args->key.min_objectid = h->objectid; + args->key.min_type = h->type; + args->key.min_offset = h->offset; +} + +static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) { + assert(args); + + /* Compare min and max */ + + if (args->key.min_objectid < args->key.max_objectid) + return -1; + if (args->key.min_objectid > args->key.max_objectid) + return 1; + + if (args->key.min_type < args->key.max_type) + return -1; + if (args->key.min_type > args->key.max_type) + return 1; + + if (args->key.min_offset < args->key.max_offset) + return -1; + if (args->key.min_offset > args->key.max_offset) + return 1; + + return 0; +} + +#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \ + for ((i) = 0, \ + (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \ + (i) < (args).key.nr_items; \ + (i)++, \ + (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len)) + +#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, uint64_t subvol_id, BtrfsSubvolInfo *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_ITEM_KEY, + .key.max_type = BTRFS_ROOT_ITEM_KEY, + + .key.min_offset = 0, + .key.max_offset = (uint64_t) -1, + + /* No restrictions on the other components */ + .key.min_transid = 0, + .key.max_transid = (uint64_t) -1, + }; + + bool found = false; + 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 -errno; + + if (args.key.nr_items <= 0) + break; + + FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { + + const struct btrfs_root_item *ri; + + /* Make sure we start the next search at least from this entry */ + btrfs_ioctl_search_args_set(&args, sh); + + if (sh->objectid != subvol_id) + continue; + if (sh->type != BTRFS_ROOT_ITEM_KEY) + continue; + + /* Older versions of the struct lacked the otime setting */ + if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) + continue; + + ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + + (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; + + ret->subvol_id = subvol_id; + ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); + + assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); + memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); + memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); + + found = true; + goto finish; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + +finish: + if (!found) + return -ENODATA; + + return 0; +} + +int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { + + 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_STATUS_KEY, + .key.max_type = BTRFS_QGROUP_LIMIT_KEY, + + /* No restrictions on the other components */ + .key.min_transid = 0, + .key.max_transid = (uint64_t) -1, + }; + + bool found_info = false, found_limit = false; + 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_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 (errno == ENOENT) /* quota tree is missing: quota 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->objectid != 0) + continue; + if (sh->offset != qgroupid) + continue; + + if (sh->type == BTRFS_QGROUP_INFO_KEY) { + const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + ret->referenced = le64toh(qii->rfer); + ret->exclusive = le64toh(qii->excl); + + found_info = true; + + } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) { + const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER) + ret->referenced_max = le64toh(qli->max_rfer); + else + ret->referenced_max = (uint64_t) -1; + + 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; + } + + if (found_info && found_limit) + goto finish; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + +finish: + if (!found_limit && !found_info) + return -ENODATA; + + if (!found_info) { + ret->referenced = (uint64_t) -1; + ret->exclusive = (uint64_t) -1; + } + + if (!found_limit) { + ret->referenced_max = (uint64_t) -1; + ret->exclusive_max = (uint64_t) -1; + } + + 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; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EINVAL; + + if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) + return -errno; + + return 0; +} + +int btrfs_defrag(const char *p) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return btrfs_defrag_fd(fd); +} + +int btrfs_quota_enable_fd(int fd, bool b) { + struct btrfs_ioctl_quota_ctl_args args = { + .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE, + }; + int r; + + assert(fd >= 0); + + r = btrfs_is_filesystem(fd); + if (r < 0) + return r; + if (!r) + return -ENOTTY; + + if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0) + return -errno; + + return 0; +} + +int btrfs_quota_enable(const char *path, bool b) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return btrfs_quota_enable_fd(fd, b); +} + +int btrfs_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, + .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER, + }; + unsigned c; + int r; + + assert(fd >= 0); + + 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.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_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_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) { + struct btrfs_ioctl_vol_args args = {}; + _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL; + _cleanup_close_ int loop_fd = -1, backing_fd = -1; + struct stat st; + dev_t dev = 0; + int r; + + /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */ + if (!FILE_SIZE_VALID(new_size)) + return -EINVAL; + + /* btrfs cannot handle file systems < 16M, hence use this as minimum */ + if (new_size < 16*1024*1024) + new_size = 16*1024*1024; + + r = btrfs_get_block_device_fd(fd, &dev); + if (r < 0) + return r; + if (r == 0) + return -ENODEV; + + if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0) + return -ENOMEM; + r = read_one_line_file(p, &backing); + if (r == -ENOENT) + return -ENODEV; + if (r < 0) + return r; + if (isempty(backing) || !path_is_absolute(backing)) + return -ENODEV; + + backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY); + if (backing_fd < 0) + return -errno; + + if (fstat(backing_fd, &st) < 0) + return -errno; + if (!S_ISREG(st.st_mode)) + return -ENODEV; + + if (new_size == (uint64_t) st.st_size) + return 0; + + if (grow_only && new_size < (uint64_t) st.st_size) + return -EINVAL; + + if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0) + return -ENOMEM; + loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY); + if (loop_fd < 0) + return -errno; + + if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name)) + return -EINVAL; + + if (new_size < (uint64_t) st.st_size) { + /* Decrease size: first decrease btrfs size, then shorten loopback */ + if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) + return -errno; + } + + if (ftruncate(backing_fd, new_size) < 0) + return -errno; + + if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0) + return -errno; + + if (new_size > (uint64_t) st.st_size) { + /* Increase size: first enlarge loopback, then increase btrfs size */ + if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) + return -errno; + } + + /* Make sure the free disk space is correctly updated for both file systems */ + (void) fsync(fd); + (void) fsync(backing_fd); + + return 1; +} + +int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return btrfs_resize_loopback_fd(fd, new_size, grow_only); +} + +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, + + .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, + .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, + + .key.min_type = BTRFS_ROOT_BACKREF_KEY, + .key.max_type = BTRFS_ROOT_BACKREF_KEY, + + .key.min_transid = 0, + .key.max_transid = (uint64_t) -1, + }; + + struct btrfs_ioctl_vol_args vol_args = {}; + _cleanup_close_ int subvol_fd = -1; + struct stat st; + bool made_writable = false; + int r; + + assert(fd >= 0); + assert(subvolume); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EINVAL; + + subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (subvol_fd < 0) + return -errno; + + if (subvol_id == 0) { + r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id); + if (r < 0) + return r; + } + + /* 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) { + const struct btrfs_ioctl_search_header *sh; + unsigned i; + + args.key.nr_items = 256; + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + return -errno; + + if (args.key.nr_items <= 0) + break; + + FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { + _cleanup_free_ char *p = NULL; + const struct btrfs_root_ref *ref; + struct btrfs_ioctl_ino_lookup_args ino_args; + + btrfs_ioctl_search_args_set(&args, sh); + + if (sh->type != BTRFS_ROOT_BACKREF_KEY) + continue; + if (sh->offset != subvol_id) + continue; + + ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + + p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); + if (!p) + return -ENOMEM; + + zero(ino_args); + ino_args.treeid = subvol_id; + ino_args.objectid = htole64(ref->dirid); + + if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) + return -errno; + + if (!made_writable) { + r = btrfs_subvol_set_read_only_fd(subvol_fd, false); + if (r < 0) + return r; + + made_writable = true; + } + + if (isempty(ino_args.name)) + /* Subvolume is in the top-level + * directory of the subvolume. */ + r = subvol_remove_children(subvol_fd, p, sh->objectid, flags); + else { + _cleanup_close_ int child_fd = -1; + + /* Subvolume is somewhere further down, + * hence we need to open the + * containing directory first */ + + child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (child_fd < 0) + return -errno; + + r = subvol_remove_children(child_fd, p, sh->objectid, flags); + } + if (r < 0) + return r; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + + /* OK, the child subvolumes should all be gone now, let's try + * again to remove the subvolume */ + if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0) + return -errno; + + (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); + return 0; +} + +int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { + _cleanup_close_ int fd = -1; + const char *subvolume; + int r; + + assert(path); + + r = extract_subvolume_name(path, &subvolume); + if (r < 0) + return r; + + fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return fd; + + return subvol_remove_children(fd, subvolume, 0, flags); +} + +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) { + + struct btrfs_ioctl_search_args args = { + .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, + + .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, + .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, + + .key.min_type = BTRFS_ROOT_BACKREF_KEY, + .key.max_type = BTRFS_ROOT_BACKREF_KEY, + + .key.min_transid = 0, + .key.max_transid = (uint64_t) -1, + }; + + struct btrfs_ioctl_vol_args_v2 vol_args = { + .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0, + .fd = old_fd, + }; + _cleanup_close_ int subvolume_fd = -1; + uint64_t new_subvol_id; + int r; + + assert(old_fd >= 0); + assert(new_fd >= 0); + assert(subvolume); + + strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); + + if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0) + return -errno; + + if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) && + !(flags & BTRFS_SNAPSHOT_QUOTA)) + return 0; + + if (old_subvol_id == 0) { + r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id); + if (r < 0) + return r; + } + + r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id); + 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) { + const struct btrfs_ioctl_search_header *sh; + unsigned i; + + args.key.nr_items = 256; + if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + return -errno; + + if (args.key.nr_items <= 0) + break; + + FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { + _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL; + struct btrfs_ioctl_ino_lookup_args ino_args; + const struct btrfs_root_ref *ref; + _cleanup_close_ int old_child_fd = -1, new_child_fd = -1; + + btrfs_ioctl_search_args_set(&args, sh); + + if (sh->type != BTRFS_ROOT_BACKREF_KEY) + continue; + + /* Avoid finding the source subvolume a second + * time */ + if (sh->offset != old_subvol_id) + continue; + + /* Avoid running into loops if the new + * subvolume is below the old one. */ + if (sh->objectid == new_subvol_id) + continue; + + ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); + p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); + if (!p) + return -ENOMEM; + + zero(ino_args); + ino_args.treeid = old_subvol_id; + ino_args.objectid = htole64(ref->dirid); + + if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) + return -errno; + + /* The kernel returns an empty name if the + * subvolume is in the top-level directory, + * and otherwise appends a slash, so that we + * can just concatenate easily here, without + * adding a slash. */ + c = strappend(ino_args.name, p); + if (!c) + return -ENOMEM; + + old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (old_child_fd < 0) + return -errno; + + np = strjoin(subvolume, "/", ino_args.name, NULL); + if (!np) + return -ENOMEM; + + new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (new_child_fd < 0) + return -errno; + + if (flags & BTRFS_SNAPSHOT_READ_ONLY) { + /* If the snapshot is read-only we + * need to mark it writable + * temporarily, to put the subsnapshot + * into place. */ + + if (subvolume_fd < 0) { + subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (subvolume_fd < 0) + return -errno; + } + + r = btrfs_subvol_set_read_only_fd(subvolume_fd, false); + if (r < 0) + return r; + } + + /* When btrfs clones the subvolumes, child + * subvolumes appear as empty directories. Remove + * them, so that we can create a new snapshot + * in their place */ + if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) { + int k = -errno; + + if (flags & BTRFS_SNAPSHOT_READ_ONLY) + (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true); + + return k; + } + + r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY); + + /* Restore the readonly flag */ + if (flags & BTRFS_SNAPSHOT_READ_ONLY) { + int k; + + k = btrfs_subvol_set_read_only_fd(subvolume_fd, true); + if (r >= 0 && k < 0) + return k; + } + + if (r < 0) + return r; + } + + /* Increase search key by one, to read the next item, if we can. */ + if (!btrfs_ioctl_search_args_inc(&args)) + break; + } + + if (flags & BTRFS_SNAPSHOT_QUOTA) + (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id); + + return 0; +} + +int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) { + _cleanup_close_ int new_fd = -1; + const char *subvolume; + int r; + + assert(old_fd >= 0); + assert(new_path); + + r = btrfs_is_subvol_fd(old_fd); + if (r < 0) + return r; + if (r == 0) { + if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) + return -EISDIR; + + r = btrfs_subvol_make(new_path); + if (r < 0) + return r; + + r = copy_directory_fd(old_fd, new_path, true); + if (r < 0) { + (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) { + (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); + return r; + } + } + + return 0; + } + + r = extract_subvolume_name(new_path, &subvolume); + if (r < 0) + return r; + + new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (new_fd < 0) + return new_fd; + + return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags); +} + +int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) { + _cleanup_close_ int old_fd = -1; + + assert(old_path); + assert(new_path); + + old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (old_fd < 0) + return -errno; + + return btrfs_subvol_snapshot_fd(old_fd, new_path, flags); +} + +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/libbasic/src/bus-label.c b/src/libbasic/src/bus-label.c new file mode 100644 index 0000000000..a7fd5a227e --- /dev/null +++ b/src/libbasic/src/bus-label.c @@ -0,0 +1,98 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" + +char *bus_label_escape(const char *s) { + char *r, *t; + const char *f; + + assert_return(s, NULL); + + /* Escapes all chars that D-Bus' object path cannot deal + * with. Can be reversed with bus_path_unescape(). We special + * case the empty string. */ + + if (*s == 0) + return strdup("_"); + + r = new(char, strlen(s)*3 + 1); + if (!r) + return NULL; + + for (f = s, t = r; *f; f++) { + + /* Escape everything that is not a-zA-Z0-9. We also + * escape 0-9 if it's the first character */ + + if (!(*f >= 'A' && *f <= 'Z') && + !(*f >= 'a' && *f <= 'z') && + !(f > s && *f >= '0' && *f <= '9')) { + *(t++) = '_'; + *(t++) = hexchar(*f >> 4); + *(t++) = hexchar(*f); + } else + *(t++) = *f; + } + + *t = 0; + + return r; +} + +char *bus_label_unescape_n(const char *f, size_t l) { + char *r, *t; + size_t i; + + assert_return(f, NULL); + + /* Special case for the empty string */ + if (l == 1 && *f == '_') + return strdup(""); + + r = new(char, l + 1); + if (!r) + return NULL; + + for (i = 0, t = r; i < l; ++i) { + if (f[i] == '_') { + int a, b; + + if (l - i < 3 || + (a = unhexchar(f[i + 1])) < 0 || + (b = unhexchar(f[i + 2])) < 0) { + /* Invalid escape code, let's take it literal then */ + *(t++) = '_'; + } else { + *(t++) = (char) ((a << 4) | b); + i += 2; + } + } else + *(t++) = f[i]; + } + + *t = 0; + + return r; +} diff --git a/src/libbasic/src/calendarspec.c b/src/libbasic/src/calendarspec.c new file mode 100644 index 0000000000..de5ef92f80 --- /dev/null +++ b/src/libbasic/src/calendarspec.c @@ -0,0 +1,1127 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/calendarspec.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" + +/* Longest valid date/time range is 1970..2199 */ +#define MAX_RANGE_LEN 230 +#define BITS_WEEKDAYS 127 + +static void free_chain(CalendarComponent *c) { + CalendarComponent *n; + + while (c) { + n = c->next; + free(c); + c = n; + } +} + +void calendar_spec_free(CalendarSpec *c) { + + if (!c) + return; + + free_chain(c->year); + free_chain(c->month); + free_chain(c->day); + free_chain(c->hour); + free_chain(c->minute); + free_chain(c->microsecond); + + free(c); +} + +static int component_compare(const void *_a, const void *_b) { + CalendarComponent * const *a = _a, * const *b = _b; + + if ((*a)->value < (*b)->value) + return -1; + if ((*a)->value > (*b)->value) + return 1; + + if ((*a)->repeat < (*b)->repeat) + return -1; + if ((*a)->repeat > (*b)->repeat) + return 1; + + return 0; +} + +static void sort_chain(CalendarComponent **c) { + unsigned n = 0, k; + CalendarComponent **b, *i, **j, *next; + + assert(c); + + for (i = *c; i; i = i->next) + n++; + + if (n <= 1) + return; + + j = b = alloca(sizeof(CalendarComponent*) * n); + for (i = *c; i; i = i->next) + *(j++) = i; + + qsort(b, n, sizeof(CalendarComponent*), component_compare); + + b[n-1]->next = NULL; + next = b[n-1]; + + /* Drop non-unique entries */ + for (k = n-1; k > 0; k--) { + if (b[k-1]->value == next->value && + b[k-1]->repeat == next->repeat) { + free(b[k-1]); + continue; + } + + b[k-1]->next = next; + next = b[k-1]; + } + + *c = next; +} + +static void fix_year(CalendarComponent *c) { + /* Turns 12 → 2012, 89 → 1989 */ + + while (c) { + CalendarComponent *n = c->next; + + if (c->value >= 0 && c->value < 70) + c->value += 2000; + + if (c->value >= 70 && c->value < 100) + c->value += 1900; + + c = n; + } +} + +int calendar_spec_normalize(CalendarSpec *c) { + assert(c); + + if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) + c->weekdays_bits = -1; + + fix_year(c->year); + + sort_chain(&c->year); + sort_chain(&c->month); + sort_chain(&c->day); + sort_chain(&c->hour); + sort_chain(&c->minute); + sort_chain(&c->microsecond); + + return 0; +} + +_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) { + if (!c) + return true; + + if (c->value < from || c->value > to) + return false; + + if (c->value + c->repeat > to) + return false; + + if (c->next) + return chain_valid(c->next, from, to); + + return true; +} + +_pure_ bool calendar_spec_valid(CalendarSpec *c) { + assert(c); + + if (c->weekdays_bits > BITS_WEEKDAYS) + return false; + + if (!chain_valid(c->year, 1970, 2199)) + return false; + + if (!chain_valid(c->month, 1, 12)) + return false; + + if (!chain_valid(c->day, 1, 31)) + return false; + + if (!chain_valid(c->hour, 0, 23)) + return false; + + if (!chain_valid(c->minute, 0, 59)) + return false; + + if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1)) + return false; + + return true; +} + +static void format_weekdays(FILE *f, const CalendarSpec *c) { + static const char *const days[] = { + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" + }; + + int l, x; + bool need_comma = false; + + assert(f); + assert(c); + assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS); + + for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) { + + if (c->weekdays_bits & (1 << x)) { + + if (l < 0) { + if (need_comma) + fputc(',', f); + else + need_comma = true; + + fputs(days[x], f); + l = x; + } + + } else if (l >= 0) { + + if (x > l + 1) { + fputs(x > l + 2 ? ".." : ",", f); + fputs(days[x-1], f); + } + + l = -1; + } + } + + if (l >= 0 && x > l + 1) { + fputs(x > l + 2 ? ".." : ",", f); + fputs(days[x-1], f); + } +} + +static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) { + assert(f); + + if (!c) { + fputc('*', f); + return; + } + + assert(c->value >= 0); + 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, usec); + } +} + +int calendar_spec_to_string(const CalendarSpec *c, char **p) { + char *buf = NULL; + size_t sz = 0; + FILE *f; + int r; + + assert(c); + assert(p); + + f = open_memstream(&buf, &sz); + if (!f) + return -ENOMEM; + + if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { + format_weekdays(f, c); + fputc(' ', f); + } + + format_chain(f, 4, c->year, false); + fputc('-', f); + format_chain(f, 2, c->month, false); + fputc('-', f); + format_chain(f, 2, c->day, false); + fputc(' ', f); + format_chain(f, 2, c->hour, false); + fputc(':', f); + format_chain(f, 2, c->minute, false); + fputc(':', f); + format_chain(f, 2, c->microsecond, true); + + if (c->utc) + fputs(" UTC", f); + + r = fflush_and_check(f); + if (r < 0) { + free(buf); + fclose(f); + return r; + } + + fclose(f); + + *p = buf; + return 0; +} + +static int parse_weekdays(const char **p, CalendarSpec *c) { + static const struct { + const char *name; + const int nr; + } day_nr[] = { + { "Monday", 0 }, + { "Mon", 0 }, + { "Tuesday", 1 }, + { "Tue", 1 }, + { "Wednesday", 2 }, + { "Wed", 2 }, + { "Thursday", 3 }, + { "Thu", 3 }, + { "Friday", 4 }, + { "Fri", 4 }, + { "Saturday", 5 }, + { "Sat", 5 }, + { "Sunday", 6 }, + { "Sun", 6 } + }; + + int l = -1; + bool first = true; + + assert(p); + assert(*p); + assert(c); + + for (;;) { + unsigned i; + + if (!first && **p == ' ') + return 0; + + for (i = 0; i < ELEMENTSOF(day_nr); i++) { + size_t skip; + + if (!startswith_no_case(*p, day_nr[i].name)) + continue; + + skip = strlen(day_nr[i].name); + + if ((*p)[skip] != '-' && + (*p)[skip] != '.' && + (*p)[skip] != ',' && + (*p)[skip] != ' ' && + (*p)[skip] != 0) + return -EINVAL; + + c->weekdays_bits |= 1 << day_nr[i].nr; + + if (l >= 0) { + int j; + + if (l > day_nr[i].nr) + return -EINVAL; + + for (j = l + 1; j < day_nr[i].nr; j++) + c->weekdays_bits |= 1 << j; + } + + *p += skip; + break; + } + + /* Couldn't find this prefix, so let's assume the + weekday was not specified and let's continue with + the date */ + if (i >= ELEMENTSOF(day_nr)) + return first ? 0 : -EINVAL; + + /* We reached the end of the string */ + if (**p == 0) + return 0; + + /* We reached the end of the weekday spec part */ + if (**p == ' ') { + *p += strspn(*p, " "); + return 0; + } + + if (**p == '.') { + if (l >= 0) + return -EINVAL; + + if ((*p)[1] != '.') + return -EINVAL; + + l = day_nr[i].nr; + *p += 1; + + /* Support ranges with "-" for backwards compatibility */ + } else if (**p == '-') { + if (l >= 0) + return -EINVAL; + + l = day_nr[i].nr; + } else + l = -1; + + *p += 1; + first = false; + } +} + +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, &ee, 10); + if (errno > 0) + return -errno; + if (ee == *p) + return -EINVAL; + if ((unsigned long) (int) value != value) + return -ERANGE; + e = ee; + + if (usec) { + if (value * USEC_PER_SEC / USEC_PER_SEC != value) + return -ERANGE; + + 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 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 prepend_component(const char **p, bool usec, CalendarComponent **c) { + unsigned long i, value, range_end, range_inc, 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; + } else if (e[0] == '.' && e[1] == '.') { + e += 2; + r = parse_component_decimal(&e, usec, &range_end); + if (r < 0) + return r; + + if (value >= range_end) + return -EINVAL; + + range_inc = usec ? USEC_PER_SEC : 1; + + /* Don't allow impossibly large ranges... */ + if (range_end - value >= MAX_RANGE_LEN * range_inc) + return -EINVAL; + + /* ...or ranges with only a single element */ + if (range_end - value < range_inc) + return -EINVAL; + + for (i = value; i <= range_end; i += range_inc) { + r = const_chain(i, c); + if (r < 0) + return r; + } + } + + if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':') + return -EINVAL; + + cc = new0(CalendarComponent, 1); + if (!cc) + return -ENOMEM; + + cc->value = value; + cc->repeat = repeat; + cc->next = *c; + + *p = e; + *c = cc; + + if (*e ==',') { + *p += 1; + return prepend_component(p, usec, c); + } + + return 0; +} + +static int parse_chain(const char **p, bool usec, CalendarComponent **c) { + const char *t; + CalendarComponent *cc = NULL; + int r; + + assert(p); + assert(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; + return 0; + } + + r = prepend_component(&t, usec, &cc); + if (r < 0) { + free_chain(cc); + return r; + } + + *p = t; + *c = cc; + return 0; +} + +static int parse_date(const char **p, CalendarSpec *c) { + const char *t; + int r; + CalendarComponent *first, *second, *third; + + assert(p); + assert(*p); + assert(c); + + t = *p; + + if (*t == 0) + return 0; + + r = parse_chain(&t, false, &first); + if (r < 0) + return r; + + /* Already the end? A ':' as separator? In that case this was a time, not a date */ + if (*t == 0 || *t == ':') { + free_chain(first); + return 0; + } + + if (*t != '-') { + free_chain(first); + return -EINVAL; + } + + t++; + r = parse_chain(&t, false, &second); + if (r < 0) { + free_chain(first); + return r; + } + + /* Got two parts, hence it's month and day */ + if (*t == ' ' || *t == 0) { + *p = t + strspn(t, " "); + c->month = first; + c->day = second; + return 0; + } + + if (*t != '-') { + free_chain(first); + free_chain(second); + return -EINVAL; + } + + t++; + r = parse_chain(&t, false, &third); + if (r < 0) { + free_chain(first); + free_chain(second); + return r; + } + + /* Got tree parts, hence it is year, month and day */ + if (*t == ' ' || *t == 0) { + *p = t + strspn(t, " "); + c->year = first; + c->month = second; + c->day = third; + return 0; + } + + free_chain(first); + free_chain(second); + free_chain(third); + return -EINVAL; +} + +static int parse_calendar_time(const char **p, CalendarSpec *c) { + CalendarComponent *h = NULL, *m = NULL, *s = NULL; + const char *t; + int r; + + assert(p); + assert(*p); + assert(c); + + t = *p; + + if (*t == 0) { + /* If no time is specified at all, but a date of some + * kind, then this means 00:00:00 */ + if (c->day || c->weekdays_bits > 0) + goto null_hour; + + goto finish; + } + + r = parse_chain(&t, false, &h); + if (r < 0) + goto fail; + + if (*t != ':') { + r = -EINVAL; + goto fail; + } + + t++; + r = parse_chain(&t, false, &m); + if (r < 0) + goto fail; + + /* Already at the end? Then it's hours and minutes, and seconds are 0 */ + if (*t == 0) { + if (m != NULL) + goto null_second; + + goto finish; + } + + if (*t != ':') { + r = -EINVAL; + goto fail; + } + + t++; + r = parse_chain(&t, true, &s); + if (r < 0) + goto fail; + + /* At the end? Then it's hours, minutes and seconds */ + if (*t == 0) + goto finish; + + r = -EINVAL; + goto fail; + +null_hour: + r = const_chain(0, &h); + if (r < 0) + goto fail; + + r = const_chain(0, &m); + if (r < 0) + goto fail; + +null_second: + r = const_chain(0, &s); + if (r < 0) + goto fail; + +finish: + *p = t; + c->hour = h; + c->minute = m; + c->microsecond = s; + + return 0; + +fail: + free_chain(h); + free_chain(m); + free_chain(s); + return r; +} + +int calendar_spec_from_string(const char *p, CalendarSpec **spec) { + CalendarSpec *c; + int r; + const char *utc; + + assert(p); + assert(spec); + + if (isempty(p)) + return -EINVAL; + + c = new0(CalendarSpec, 1); + 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->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "hourly")) { + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "daily")) { + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "monthly")) { + r = const_chain(1, &c->day); + if (r < 0) + goto fail; + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "annually") || + strcaseeq(p, "yearly") || + strcaseeq(p, "anually") /* backwards compatibility */ ) { + + r = const_chain(1, &c->month); + if (r < 0) + goto fail; + r = const_chain(1, &c->day); + if (r < 0) + goto fail; + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "weekly")) { + + c->weekdays_bits = 1; + + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "quarterly")) { + + r = const_chain(1, &c->month); + if (r < 0) + goto fail; + r = const_chain(4, &c->month); + if (r < 0) + goto fail; + r = const_chain(7, &c->month); + if (r < 0) + goto fail; + r = const_chain(10, &c->month); + if (r < 0) + goto fail; + r = const_chain(1, &c->day); + if (r < 0) + goto fail; + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else if (strcaseeq(p, "biannually") || + strcaseeq(p, "bi-annually") || + strcaseeq(p, "semiannually") || + strcaseeq(p, "semi-annually")) { + + r = const_chain(1, &c->month); + if (r < 0) + goto fail; + r = const_chain(7, &c->month); + if (r < 0) + goto fail; + r = const_chain(1, &c->day); + if (r < 0) + goto fail; + r = const_chain(0, &c->hour); + if (r < 0) + goto fail; + r = const_chain(0, &c->minute); + if (r < 0) + goto fail; + r = const_chain(0, &c->microsecond); + if (r < 0) + goto fail; + + } else { + r = parse_weekdays(&p, c); + if (r < 0) + goto fail; + + r = parse_date(&p, c); + if (r < 0) + goto fail; + + r = parse_calendar_time(&p, c); + if (r < 0) + goto fail; + + if (*p != 0) { + r = -EINVAL; + goto fail; + } + } + + r = calendar_spec_normalize(c); + if (r < 0) + goto fail; + + if (!calendar_spec_valid(c)) { + r = -EINVAL; + goto fail; + } + + *spec = c; + return 0; + +fail: + calendar_spec_free(c); + return r; +} + +static int find_matching_component(const CalendarComponent *c, int *val) { + const CalendarComponent *n; + int d = -1; + bool d_set = false; + int r; + + assert(val); + + if (!c) + return 0; + + while (c) { + n = c->next; + + if (c->value >= *val) { + + if (!d_set || c->value < d) { + d = c->value; + d_set = true; + } + + } else if (c->repeat > 0) { + int k; + + k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat); + + if (!d_set || k < d) { + d = k; + d_set = true; + } + } + + c = n; + } + + if (!d_set) + return -ENOENT; + + r = *val != d; + *val = d; + return r; +} + +static bool tm_out_of_bounds(const struct tm *tm, bool utc) { + struct tm t; + assert(tm); + + t = *tm; + + if (mktime_or_timegm(&t, utc) == (time_t) -1) + return true; + + /* Did any normalization take place? If so, it was out of bounds before */ + return + t.tm_year != tm->tm_year || + t.tm_mon != tm->tm_mon || + t.tm_mday != tm->tm_mday || + t.tm_hour != tm->tm_hour || + t.tm_min != tm->tm_min || + t.tm_sec != tm->tm_sec; +} + +static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { + struct tm t; + int k; + + if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS) + return true; + + t = *tm; + 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, 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 */ + (void) mktime_or_timegm(&c, spec->utc); + c.tm_isdst = -1; + + c.tm_year += 1900; + r = find_matching_component(spec->year, &c.tm_year); + c.tm_year -= 1900; + + if (r > 0) { + c.tm_mon = 0; + c.tm_mday = 1; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; + } + if (r < 0) + return r; + if (tm_out_of_bounds(&c, spec->utc)) + return -ENOENT; + + c.tm_mon += 1; + r = find_matching_component(spec->month, &c.tm_mon); + c.tm_mon -= 1; + + if (r > 0) { + c.tm_mday = 1; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; + } + 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 = 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 = 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 = tm_usec = 0; + continue; + } + + if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { + c.tm_mday++; + 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 = tm_usec = 0; + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { + c.tm_mday++; + 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 = tm_usec = 0; + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { + c.tm_hour++; + c.tm_min = c.tm_sec = tm_usec = 0; + continue; + } + + 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 = tm_usec = 0; + continue; + } + + *tm = c; + *usec = tm_usec; + return 0; + } +} + +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); + + 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, &tm_usec); + if (r < 0) + return r; + + t = mktime_or_timegm(&tm, spec->utc); + if (t == (time_t) -1) + return -EINVAL; + + *next = (usec_t) t * USEC_PER_SEC + tm_usec; + return 0; +} diff --git a/src/libbasic/src/cap-list.c b/src/libbasic/src/cap-list.c new file mode 100644 index 0000000000..22fc8b9b31 --- /dev/null +++ b/src/libbasic/src/cap-list.c @@ -0,0 +1,66 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/cap-list.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/util.h" + +static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); + +#include "basic/cap-from-name.h" +#include "basic/cap-to-name.h" + +const char *capability_to_name(int id) { + + if (id < 0) + return NULL; + + if (id >= (int) ELEMENTSOF(capability_names)) + return NULL; + + return capability_names[id]; +} + +int capability_from_name(const char *name) { + const struct capability_name *sc; + int r, i; + + assert(name); + + /* Try to parse numeric capability */ + r = safe_atoi(name, &i); + if (r >= 0 && i >= 0) + return i; + + /* Try to parse string capability */ + sc = lookup_capability(name, strlen(name)); + if (!sc) + return -EINVAL; + + return sc->id; +} + +int capability_list_length(void) { + return (int) ELEMENTSOF(capability_names); +} diff --git a/src/libbasic/src/capability-util.c b/src/libbasic/src/capability-util.c new file mode 100644 index 0000000000..6c35ad09e5 --- /dev/null +++ b/src/libbasic/src/capability-util.c @@ -0,0 +1,361 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/util.h" + +int have_effective_cap(int value) { + _cleanup_cap_free_ cap_t cap; + cap_flag_value_t fv; + + cap = cap_get_proc(); + if (!cap) + return -errno; + + if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) + return -errno; + else + return fv == CAP_SET; +} + +unsigned long cap_last_cap(void) { + static thread_local unsigned long saved; + static thread_local bool valid = false; + _cleanup_free_ char *content = NULL; + unsigned long p = 0; + int r; + + if (valid) + return saved; + + /* available since linux-3.2 */ + r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); + if (r >= 0) { + r = safe_atolu(content, &p); + if (r >= 0) { + saved = p; + valid = true; + return p; + } + } + + /* fall back to syscall-probing for pre linux-3.2 */ + p = (unsigned long) CAP_LAST_CAP; + + if (prctl(PR_CAPBSET_READ, p) < 0) { + + /* Hmm, look downwards, until we find one that + * works */ + for (p--; p > 0; p --) + if (prctl(PR_CAPBSET_READ, p) >= 0) + break; + + } else { + + /* Hmm, look upwards, until we find one that doesn't + * work */ + for (;; p++) + if (prctl(PR_CAPBSET_READ, p+1) < 0) + break; + } + + saved = p; + valid = true; + + return p; +} + +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; + int r; + + /* If we are run as PID 1 we will lack CAP_SETPCAP by default + * in the effective set (yes, the kernel drops that when + * executing init!), so get it back temporarily so that we can + * call PR_CAPBSET_DROP. */ + + after_cap = cap_get_proc(); + if (!after_cap) + return -errno; + + if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) + return -errno; + + if (fv != CAP_SET) { + _cleanup_cap_free_ cap_t temp_cap = NULL; + static const cap_value_t v = CAP_SETPCAP; + + temp_cap = cap_dup(after_cap); + if (!temp_cap) { + r = -errno; + goto finish; + } + + if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { + r = -errno; + goto finish; + } + + if (cap_set_proc(temp_cap) < 0) { + r = -errno; + goto finish; + } + } + + for (i = 0; i <= cap_last_cap(); i++) { + + if (!(keep & (UINT64_C(1) << i))) { + cap_value_t v; + + /* Drop it from the bounding set */ + if (prctl(PR_CAPBSET_DROP, i) < 0) { + r = -errno; + goto finish; + } + v = (cap_value_t) i; + + /* Also drop it from the inheritable set, so + * that anything we exec() loses the + * capability for good. */ + if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { + r = -errno; + goto finish; + } + + /* If we shall apply this right now drop it + * also from our own capability sets. */ + if (right_now) { + if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || + cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { + r = -errno; + goto finish; + } + } + } + } + + r = 0; + +finish: + if (cap_set_proc(after_cap) < 0) + return -errno; + + return r; +} + +static int drop_from_file(const char *fn, uint64_t keep) { + int r, k; + uint32_t hi, lo; + uint64_t current, after; + char *p; + + r = read_one_line_file(fn, &p); + if (r < 0) + return r; + + assert_cc(sizeof(hi) == sizeof(unsigned)); + assert_cc(sizeof(lo) == sizeof(unsigned)); + + k = sscanf(p, "%u %u", &lo, &hi); + free(p); + + if (k != 2) + return -EIO; + + current = (uint64_t) lo | ((uint64_t) hi << 32ULL); + after = current & keep; + + if (current == after) + return 0; + + lo = (unsigned) (after & 0xFFFFFFFFULL); + hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); + + if (asprintf(&p, "%u %u", lo, hi) < 0) + return -ENOMEM; + + r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); + free(p); + + return r; +} + +int capability_bounding_set_drop_usermode(uint64_t keep) { + int r; + + r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); + if (r < 0) + return r; + + r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); + if (r < 0) + return r; + + return r; +} + +int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { + _cleanup_cap_free_ cap_t d = NULL; + unsigned i, j = 0; + int r; + + /* Unfortunately we cannot leave privilege dropping to PID 1 + * here, since we want to run as user but want to keep some + * capabilities. Since file capabilities have been introduced + * this cannot be done across exec() anymore, unless our + * binary has the capability configured in the file system, + * which we want to avoid. */ + + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change group ID: %m"); + + if (setgroups(0, NULL) < 0) + return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); + + /* Ensure we keep the permitted caps across the setresuid() */ + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); + + r = setresuid(uid, uid, uid); + if (r < 0) + return log_error_errno(errno, "Failed to change user ID: %m"); + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) + 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); + if (r < 0) + return log_error_errno(r, "Failed to drop capabilities: %m"); + + /* Now upgrade the permitted caps we still kept to effective caps */ + d = cap_init(); + if (!d) + return log_oom(); + + if (keep_capabilities) { + cap_value_t bits[u64log2(keep_capabilities) + 1]; + + for (i = 0; i < ELEMENTSOF(bits); i++) + if (keep_capabilities & (1ULL << i)) + bits[j++] = i; + + /* use enough bits */ + assert(i == 64 || (keep_capabilities >> i) == 0); + /* don't use too many bits */ + 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) + 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"); + } + + return 0; +} + +int drop_capability(cap_value_t cv) { + _cleanup_cap_free_ cap_t tmp_cap = NULL; + + tmp_cap = cap_get_proc(); + if (!tmp_cap) + return -errno; + + if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || + (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || + (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) + return -errno; + + if (cap_set_proc(tmp_cap) < 0) + return -errno; + + return 0; +} diff --git a/src/libbasic/src/cgroup-util.c b/src/libbasic/src/cgroup-util.c new file mode 100644 index 0000000000..c2abcd079d --- /dev/null +++ b/src/libbasic/src/cgroup-util.c @@ -0,0 +1,2365 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/def.h" +#include "basic/dirent-util.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/login-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" + +int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { + _cleanup_free_ char *fs = NULL; + FILE *f; + int r; + + assert(_f); + + r = cg_get_path(controller, path, "cgroup.procs", &fs); + if (r < 0) + return r; + + f = fopen(fs, "re"); + if (!f) + return -errno; + + *_f = f; + return 0; +} + +int cg_read_pid(FILE *f, pid_t *_pid) { + unsigned long ul; + + /* Note that the cgroup.procs might contain duplicates! See + * cgroups.txt for details. */ + + assert(f); + assert(_pid); + + errno = 0; + if (fscanf(f, "%lu", &ul) != 1) { + + if (feof(f)) + return 0; + + return errno > 0 ? -errno : -EIO; + } + + if (ul <= 0) + return -EIO; + + *_pid = (pid_t) ul; + return 1; +} + +int cg_read_event(const char *controller, const char *path, const char *event, + char **val) +{ + _cleanup_free_ char *events = NULL, *content = NULL; + char *p, *line; + int r; + + r = cg_get_path(controller, path, "cgroup.events", &events); + if (r < 0) + return r; + + r = read_full_file(events, &content, NULL); + if (r < 0) + return r; + + p = content; + while ((line = strsep(&p, "\n"))) { + char *key; + + key = strsep(&line, " "); + if (!key || !line) + return -EINVAL; + + if (strcmp(key, event)) + continue; + + *val = strdup(line); + return 0; + } + + return -ENOENT; +} + +int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { + _cleanup_free_ char *fs = NULL; + int r; + DIR *d; + + assert(_d); + + /* This is not recursive! */ + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) + return r; + + d = opendir(fs); + if (!d) + return -errno; + + *_d = d; + return 0; +} + +int cg_read_subgroup(DIR *d, char **fn) { + struct dirent *de; + + assert(d); + assert(fn); + + FOREACH_DIRENT_ALL(de, d, return -errno) { + char *b; + + if (de->d_type != DT_DIR) + continue; + + if (streq(de->d_name, ".") || + streq(de->d_name, "..")) + continue; + + b = strdup(de->d_name); + if (!b) + return -ENOMEM; + + *fn = b; + return 1; + } + + return 0; +} + +int cg_rmdir(const char *controller, const char *path) { + _cleanup_free_ char *p = NULL; + int r; + + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) + return r; + + r = rmdir(p); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; +} + +int cg_kill( + const char *controller, + const char *path, + int sig, + CGroupFlags flags, + Set *s, + cg_kill_log_func_t log_kill, + void *userdata) { + + _cleanup_set_free_ Set *allocated_set = NULL; + bool done = false; + int r, ret = 0; + pid_t my_pid; + + assert(sig >= 0); + + /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send + * SIGCONT on SIGKILL. */ + if (IN_SET(sig, SIGCONT, SIGKILL)) + flags &= ~CGROUP_SIGCONT; + + /* This goes through the tasks list and kills them all. This + * is repeated until no further processes are added to the + * tasks list, to properly handle forking processes */ + + if (!s) { + s = allocated_set = set_new(NULL); + if (!s) + return -ENOMEM; + } + + my_pid = getpid(); + + do { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid = 0; + done = true; + + r = cg_enumerate_processes(controller, path, &f); + if (r < 0) { + if (ret >= 0 && r != -ENOENT) + return r; + + return ret; + } + + while ((r = cg_read_pid(f, &pid)) > 0) { + + if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) + continue; + + if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) + continue; + + if (log_kill) + log_kill(pid, sig, userdata); + + /* If we haven't killed this process yet, kill + * it */ + if (kill(pid, sig) < 0) { + if (ret >= 0 && errno != ESRCH) + ret = -errno; + } else { + if (flags & CGROUP_SIGCONT) + (void) kill(pid, SIGCONT); + + if (ret == 0) + ret = 1; + } + + done = false; + + r = set_put(s, PID_TO_PTR(pid)); + if (r < 0) { + if (ret >= 0) + return r; + + return ret; + } + } + + if (r < 0) { + if (ret >= 0) + return r; + + return ret; + } + + /* To avoid racing against processes which fork + * quicker than we can kill them we repeat this until + * no new pids need to be killed. */ + + } while (!done); + + return ret; +} + +int cg_kill_recursive( + const char *controller, + const char *path, + int sig, + CGroupFlags flags, + Set *s, + cg_kill_log_func_t log_kill, + void *userdata) { + + _cleanup_set_free_ Set *allocated_set = NULL; + _cleanup_closedir_ DIR *d = NULL; + int r, ret; + char *fn; + + assert(path); + assert(sig >= 0); + + if (!s) { + s = allocated_set = set_new(NULL); + if (!s) + return -ENOMEM; + } + + ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata); + + r = cg_enumerate_subgroups(controller, path, &d); + if (r < 0) { + if (ret >= 0 && r != -ENOENT) + return r; + + return ret; + } + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + if (!p) + return -ENOMEM; + + r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata); + if (r != 0 && ret >= 0) + ret = r; + } + if (ret >= 0 && r < 0) + ret = r; + + if (flags & CGROUP_REMOVE) { + r = cg_rmdir(controller, path); + if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + return r; + } + + return ret; +} + +int cg_migrate( + const char *cfrom, + const char *pfrom, + const char *cto, + const char *pto, + CGroupFlags flags) { + + bool done = false; + _cleanup_set_free_ Set *s = NULL; + int r, ret = 0; + pid_t my_pid; + + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); + + s = set_new(NULL); + if (!s) + return -ENOMEM; + + my_pid = getpid(); + + do { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid = 0; + done = true; + + r = cg_enumerate_processes(cfrom, pfrom, &f); + if (r < 0) { + if (ret >= 0 && r != -ENOENT) + return r; + + return ret; + } + + while ((r = cg_read_pid(f, &pid)) > 0) { + + /* This might do weird stuff if we aren't a + * single-threaded program. However, we + * luckily know we are not */ + if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) + continue; + + if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) + continue; + + /* Ignore kernel threads. Since they can only + * exist in the root cgroup, we only check for + * them there. */ + if (cfrom && + (isempty(pfrom) || path_equal(pfrom, "/")) && + is_kernel_thread(pid) > 0) + continue; + + r = cg_attach(cto, pto, pid); + if (r < 0) { + if (ret >= 0 && r != -ESRCH) + ret = r; + } else if (ret == 0) + ret = 1; + + done = false; + + r = set_put(s, PID_TO_PTR(pid)); + if (r < 0) { + if (ret >= 0) + return r; + + return ret; + } + } + + if (r < 0) { + if (ret >= 0) + return r; + + return ret; + } + } while (!done); + + return ret; +} + +int cg_migrate_recursive( + const char *cfrom, + const char *pfrom, + const char *cto, + const char *pto, + CGroupFlags flags) { + + _cleanup_closedir_ DIR *d = NULL; + int r, ret = 0; + char *fn; + + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); + + ret = cg_migrate(cfrom, pfrom, cto, pto, flags); + + r = cg_enumerate_subgroups(cfrom, pfrom, &d); + if (r < 0) { + if (ret >= 0 && r != -ENOENT) + return r; + + return ret; + } + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(pfrom, "/", fn, NULL); + free(fn); + if (!p) + return -ENOMEM; + + r = cg_migrate_recursive(cfrom, p, cto, pto, flags); + if (r != 0 && ret >= 0) + ret = r; + } + + if (r < 0 && ret >= 0) + ret = r; + + if (flags & CGROUP_REMOVE) { + r = cg_rmdir(cfrom, pfrom); + if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) + return r; + } + + return ret; +} + +int cg_migrate_recursive_fallback( + const char *cfrom, + const char *pfrom, + const char *cto, + const char *pto, + CGroupFlags flags) { + + int r; + + assert(cfrom); + assert(pfrom); + assert(cto); + assert(pto); + + r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags); + if (r < 0) { + char prefix[strlen(pto) + 1]; + + /* This didn't work? Then let's try all prefixes of the destination */ + + PATH_FOREACH_PREFIX(prefix, pto) { + int q; + + q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags); + if (q >= 0) + return q; + } + } + + return r; +} + +static const char *controller_to_dirname(const char *controller) { + const char *e; + + assert(controller); + + /* Converts a controller name to the directory name below + * /sys/fs/cgroup/ we want to mount it to. Effectively, this + * just cuts off the name= prefixed used for named + * hierarchies, if it is specified. */ + + e = startswith(controller, "name="); + if (e) + return e; + + return controller; +} + +static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) { + const char *dn; + char *t = NULL; + + assert(fs); + assert(controller); + + dn = controller_to_dirname(controller); + + if (isempty(path) && isempty(suffix)) + t = strappend("/sys/fs/cgroup/", dn); + else if (isempty(path)) + t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL); + else if (isempty(suffix)) + t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL); + else + t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL); + if (!t) + return -ENOMEM; + + *fs = t; + return 0; +} + +static int join_path_unified(const char *path, const char *suffix, char **fs) { + char *t; + + assert(fs); + + if (isempty(path) && isempty(suffix)) + t = strdup("/sys/fs/cgroup"); + else if (isempty(path)) + t = strappend("/sys/fs/cgroup/", suffix); + else if (isempty(suffix)) + t = strappend("/sys/fs/cgroup/", path); + else + t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL); + if (!t) + return -ENOMEM; + + *fs = t; + return 0; +} + +int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { + int unified, r; + + assert(fs); + + if (!controller) { + char *t; + + /* If no controller is specified, we return the path + * *below* the controllers, without any prefix. */ + + if (!path && !suffix) + return -EINVAL; + + if (!suffix) + t = strdup(path); + else if (!path) + t = strdup(suffix); + else + t = strjoin(path, "/", suffix, NULL); + if (!t) + return -ENOMEM; + + *fs = path_kill_slashes(t); + return 0; + } + + if (!cg_controller_is_valid(controller)) + return -EINVAL; + + unified = cg_unified(); + if (unified < 0) + return unified; + + if (unified > 0) + r = join_path_unified(path, suffix, fs); + else + r = join_path_legacy(controller, path, suffix, fs); + if (r < 0) + return r; + + path_kill_slashes(*fs); + return 0; +} + +static int controller_is_accessible(const char *controller) { + int unified; + + assert(controller); + + /* Checks whether a specific controller is accessible, + * i.e. its hierarchy mounted. In the unified hierarchy all + * controllers are considered accessible, except for the named + * hierarchies */ + + if (!cg_controller_is_valid(controller)) + return -EINVAL; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) { + /* We don't support named hierarchies if we are using + * the unified hierarchy. */ + + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return 0; + + if (startswith(controller, "name=")) + return -EOPNOTSUPP; + + } else { + const char *cc, *dn; + + dn = controller_to_dirname(controller); + cc = strjoina("/sys/fs/cgroup/", dn); + + if (laccess(cc, F_OK) < 0) + return -errno; + } + + return 0; +} + +int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) { + int r; + + assert(controller); + assert(fs); + + /* Check if the specified controller is actually accessible */ + r = controller_is_accessible(controller); + if (r < 0) + return r; + + return cg_get_path(controller, path, suffix, fs); +} + +static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { + assert(path); + assert(sb); + assert(ftwbuf); + + if (typeflag != FTW_DP) + return 0; + + if (ftwbuf->level < 1) + return 0; + + (void) rmdir(path); + return 0; +} + +int cg_trim(const char *controller, const char *path, bool delete_root) { + _cleanup_free_ char *fs = NULL; + int r = 0; + + assert(path); + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) + return r; + + errno = 0; + if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { + if (errno == ENOENT) + r = 0; + else if (errno > 0) + r = -errno; + else + r = -EIO; + } + + if (delete_root) { + if (rmdir(fs) < 0 && errno != ENOENT) + return -errno; + } + + return r; +} + +int cg_create(const char *controller, const char *path) { + _cleanup_free_ char *fs = NULL; + int r; + + r = cg_get_path_and_check(controller, path, NULL, &fs); + if (r < 0) + return r; + + r = mkdir_parents(fs, 0755); + if (r < 0) + return r; + + if (mkdir(fs, 0755) < 0) { + + if (errno == EEXIST) + return 0; + + return -errno; + } + + return 1; +} + +int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { + int r, q; + + assert(pid >= 0); + + r = cg_create(controller, path); + if (r < 0) + return r; + + q = cg_attach(controller, path, pid); + if (q < 0) + return q; + + /* This does not remove the cgroup on failure */ + return r; +} + +int cg_attach(const char *controller, const char *path, pid_t pid) { + _cleanup_free_ char *fs = NULL; + char c[DECIMAL_STR_MAX(pid_t) + 2]; + int r; + + assert(path); + assert(pid >= 0); + + r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs); + if (r < 0) + return r; + + if (pid == 0) + pid = getpid(); + + xsprintf(c, PID_FMT "\n", pid); + + return write_string_file(fs, c, 0); +} + +int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { + int r; + + assert(controller); + assert(path); + assert(pid >= 0); + + r = cg_attach(controller, path, pid); + if (r < 0) { + char prefix[strlen(path) + 1]; + + /* This didn't work? Then let's try all prefixes of + * the destination */ + + PATH_FOREACH_PREFIX(prefix, path) { + int q; + + q = cg_attach(controller, prefix, pid); + if (q >= 0) + return q; + } + } + + return r; +} + +int cg_set_group_access( + const char *controller, + const char *path, + mode_t mode, + uid_t uid, + gid_t gid) { + + _cleanup_free_ char *fs = NULL; + int r; + + if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) + return 0; + + if (mode != MODE_INVALID) + mode &= 0777; + + r = cg_get_path(controller, path, NULL, &fs); + if (r < 0) + return r; + + return chmod_and_chown(fs, mode, uid, gid); +} + +int cg_set_task_access( + const char *controller, + const char *path, + mode_t mode, + uid_t uid, + gid_t gid) { + + _cleanup_free_ char *fs = NULL, *procs = NULL; + int r, unified; + + assert(path); + + if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) + return 0; + + if (mode != MODE_INVALID) + mode &= 0666; + + r = cg_get_path(controller, path, "cgroup.procs", &fs); + if (r < 0) + return r; + + r = chmod_and_chown(fs, mode, uid, gid); + if (r < 0) + return r; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) + return 0; + + /* Compatibility, Always keep values for "tasks" in sync with + * "cgroup.procs" */ + if (cg_get_path(controller, path, "tasks", &procs) >= 0) + (void) chmod_and_chown(procs, mode, uid, gid); + + return 0; +} + +int cg_pid_get_path(const char *controller, pid_t pid, char **path) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + const char *fs; + size_t cs = 0; + int unified; + + assert(path); + assert(pid >= 0); + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified == 0) { + if (controller) { + if (!cg_controller_is_valid(controller)) + return -EINVAL; + } else + controller = SYSTEMD_CGROUP_CONTROLLER; + + cs = strlen(controller); + } + + fs = procfs_file_alloca(pid, "cgroup"); + f = fopen(fs, "re"); + if (!f) + return errno == ENOENT ? -ESRCH : -errno; + + FOREACH_LINE(line, f, return -errno) { + char *e, *p; + + truncate_nl(line); + + if (unified) { + e = startswith(line, "0:"); + if (!e) + continue; + + e = strchr(e, ':'); + if (!e) + continue; + } else { + char *l; + size_t k; + const char *word, *state; + bool found = false; + + l = strchr(line, ':'); + if (!l) + continue; + + l++; + e = strchr(l, ':'); + if (!e) + continue; + + *e = 0; + FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { + if (k == cs && memcmp(word, controller, cs) == 0) { + found = true; + break; + } + } + + if (!found) + continue; + } + + p = strdup(e + 1); + if (!p) + return -ENOMEM; + + *path = p; + return 0; + } + + return -ENODATA; +} + +int cg_install_release_agent(const char *controller, const char *agent) { + _cleanup_free_ char *fs = NULL, *contents = NULL; + const char *sc; + int r, unified; + + assert(agent); + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) /* doesn't apply to unified hierarchy */ + return -EOPNOTSUPP; + + r = cg_get_path(controller, NULL, "release_agent", &fs); + if (r < 0) + return r; + + r = read_one_line_file(fs, &contents); + if (r < 0) + return r; + + sc = strstrip(contents); + if (isempty(sc)) { + r = write_string_file(fs, agent, 0); + if (r < 0) + return r; + } else if (!path_equal(sc, agent)) + return -EEXIST; + + fs = mfree(fs); + r = cg_get_path(controller, NULL, "notify_on_release", &fs); + if (r < 0) + return r; + + contents = mfree(contents); + r = read_one_line_file(fs, &contents); + if (r < 0) + return r; + + sc = strstrip(contents); + if (streq(sc, "0")) { + r = write_string_file(fs, "1", 0); + if (r < 0) + return r; + + return 1; + } + + if (!streq(sc, "1")) + return -EIO; + + return 0; +} + +int cg_uninstall_release_agent(const char *controller) { + _cleanup_free_ char *fs = NULL; + int r, unified; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) /* Doesn't apply to unified hierarchy */ + return -EOPNOTSUPP; + + r = cg_get_path(controller, NULL, "notify_on_release", &fs); + if (r < 0) + return r; + + r = write_string_file(fs, "0", 0); + if (r < 0) + return r; + + fs = mfree(fs); + + r = cg_get_path(controller, NULL, "release_agent", &fs); + if (r < 0) + return r; + + r = write_string_file(fs, "", 0); + if (r < 0) + return r; + + return 0; +} + +int cg_is_empty(const char *controller, const char *path) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; + int r; + + assert(path); + + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 1; + if (r < 0) + return r; + + r = cg_read_pid(f, &pid); + if (r < 0) + return r; + + return r == 0; +} + +int cg_is_empty_recursive(const char *controller, const char *path) { + int unified, r; + + assert(path); + + /* The root cgroup is always populated */ + if (controller && (isempty(path) || path_equal(path, "/"))) + return false; + + unified = cg_unified(); + if (unified < 0) + return unified; + + if (unified > 0) { + _cleanup_free_ char *t = NULL; + + /* On the unified hierarchy we can check empty state + * via the "populated" attribute of "cgroup.events". */ + + r = cg_read_event(controller, path, "populated", &t); + if (r < 0) + return r; + + return streq(t, "0"); + } else { + _cleanup_closedir_ DIR *d = NULL; + char *fn; + + r = cg_is_empty(controller, path); + if (r <= 0) + return r; + + r = cg_enumerate_subgroups(controller, path, &d); + if (r == -ENOENT) + return 1; + if (r < 0) + return r; + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + if (!p) + return -ENOMEM; + + r = cg_is_empty_recursive(controller, p); + if (r <= 0) + return r; + } + if (r < 0) + return r; + + return true; + } +} + +int cg_split_spec(const char *spec, char **controller, char **path) { + char *t = NULL, *u = NULL; + const char *e; + + assert(spec); + + if (*spec == '/') { + if (!path_is_safe(spec)) + return -EINVAL; + + if (path) { + t = strdup(spec); + if (!t) + return -ENOMEM; + + *path = path_kill_slashes(t); + } + + if (controller) + *controller = NULL; + + return 0; + } + + e = strchr(spec, ':'); + if (!e) { + if (!cg_controller_is_valid(spec)) + return -EINVAL; + + if (controller) { + t = strdup(spec); + if (!t) + return -ENOMEM; + + *controller = t; + } + + if (path) + *path = NULL; + + return 0; + } + + t = strndup(spec, e-spec); + if (!t) + return -ENOMEM; + if (!cg_controller_is_valid(t)) { + free(t); + return -EINVAL; + } + + if (isempty(e+1)) + u = NULL; + else { + u = strdup(e+1); + if (!u) { + free(t); + return -ENOMEM; + } + + if (!path_is_safe(u) || + !path_is_absolute(u)) { + free(t); + free(u); + return -EINVAL; + } + + path_kill_slashes(u); + } + + if (controller) + *controller = t; + else + free(t); + + if (path) + *path = u; + else + free(u); + + return 0; +} + +int cg_mangle_path(const char *path, char **result) { + _cleanup_free_ char *c = NULL, *p = NULL; + char *t; + int r; + + assert(path); + assert(result); + + /* First, check if it already is a filesystem path */ + if (path_startswith(path, "/sys/fs/cgroup")) { + + t = strdup(path); + if (!t) + return -ENOMEM; + + *result = path_kill_slashes(t); + return 0; + } + + /* Otherwise, treat it as cg spec */ + r = cg_split_spec(path, &c, &p); + if (r < 0) + return r; + + return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result); +} + +int cg_get_root_path(char **path) { + char *p, *e; + int r; + + assert(path); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p); + if (r < 0) + return r; + + e = endswith(p, "/" SPECIAL_INIT_SCOPE); + if (!e) + e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */ + if (!e) + e = endswith(p, "/system"); /* even more legacy */ + if (e) + *e = 0; + + *path = p; + return 0; +} + +int cg_shift_path(const char *cgroup, const char *root, const char **shifted) { + _cleanup_free_ char *rt = NULL; + char *p; + int r; + + assert(cgroup); + assert(shifted); + + if (!root) { + /* If the root was specified let's use that, otherwise + * let's determine it from PID 1 */ + + r = cg_get_root_path(&rt); + if (r < 0) + return r; + + root = rt; + } + + p = path_startswith(cgroup, root); + if (p && p > cgroup) + *shifted = p - 1; + else + *shifted = cgroup; + + return 0; +} + +int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) { + _cleanup_free_ char *raw = NULL; + const char *c; + int r; + + assert(pid >= 0); + assert(cgroup); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw); + if (r < 0) + return r; + + r = cg_shift_path(raw, root, &c); + if (r < 0) + return r; + + if (c == raw) { + *cgroup = raw; + raw = NULL; + } else { + char *n; + + n = strdup(c); + if (!n) + return -ENOMEM; + + *cgroup = n; + } + + return 0; +} + +int cg_path_decode_unit(const char *cgroup, char **unit) { + char *c, *s; + size_t n; + + assert(cgroup); + assert(unit); + + n = strcspn(cgroup, "/"); + if (n < 3) + return -ENXIO; + + c = strndupa(cgroup, n); + c = cg_unescape(c); + + if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return -ENXIO; + + s = strdup(c); + if (!s) + return -ENOMEM; + + *unit = s; + return 0; +} + +static bool valid_slice_name(const char *p, size_t n) { + + if (!p) + return false; + + if (n < strlen("x.slice")) + return false; + + if (memcmp(p + n - 6, ".slice", 6) == 0) { + char buf[n+1], *c; + + memcpy(buf, p, n); + buf[n] = 0; + + c = cg_unescape(buf); + + return unit_name_is_valid(c, UNIT_NAME_PLAIN); + } + + return false; +} + +static const char *skip_slices(const char *p) { + assert(p); + + /* Skips over all slice assignments */ + + for (;;) { + size_t n; + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (!valid_slice_name(p, n)) + return p; + + p += n; + } +} + +int cg_path_get_unit(const char *path, char **ret) { + const char *e; + char *unit; + int r; + + assert(path); + assert(ret); + + e = skip_slices(path); + + r = cg_path_decode_unit(e, &unit); + if (r < 0) + return r; + + /* We skipped over the slices, don't accept any now */ + if (endswith(unit, ".slice")) { + free(unit); + return -ENXIO; + } + + *ret = unit; + return 0; +} + +int cg_pid_get_unit(pid_t pid, char **unit) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(unit); + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_unit(cgroup, unit); +} + +/** + * Skip session-*.scope, but require it to be there. + */ +static const char *skip_session(const char *p) { + size_t n; + + if (isempty(p)) + return NULL; + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n < strlen("session-x.scope")) + return NULL; + + if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) { + char buf[n - 8 - 6 + 1]; + + memcpy(buf, p + 8, n - 8 - 6); + buf[n - 8 - 6] = 0; + + /* Note that session scopes never need unescaping, + * since they cannot conflict with the kernel's own + * names, hence we don't need to call cg_unescape() + * here. */ + + if (!session_id_valid(buf)) + return false; + + p += n; + p += strspn(p, "/"); + return p; + } + + return NULL; +} + +/** + * Skip user@*.service, but require it to be there. + */ +static const char *skip_user_manager(const char *p) { + size_t n; + + if (isempty(p)) + return NULL; + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n < strlen("user@x.service")) + return NULL; + + if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) { + char buf[n - 5 - 8 + 1]; + + memcpy(buf, p + 5, n - 5 - 8); + buf[n - 5 - 8] = 0; + + /* Note that user manager services never need unescaping, + * since they cannot conflict with the kernel's own + * names, hence we don't need to call cg_unescape() + * here. */ + + if (parse_uid(buf, NULL) < 0) + return NULL; + + p += n; + p += strspn(p, "/"); + + return p; + } + + return NULL; +} + +static const char *skip_user_prefix(const char *path) { + const char *e, *t; + + assert(path); + + /* Skip slices, if there are any */ + e = skip_slices(path); + + /* Skip the user manager, if it's in the path now... */ + t = skip_user_manager(e); + if (t) + return t; + + /* Alternatively skip the user session if it is in the path... */ + return skip_session(e); +} + +int cg_path_get_user_unit(const char *path, char **ret) { + const char *t; + + assert(path); + assert(ret); + + t = skip_user_prefix(path); + if (!t) + return -ENXIO; + + /* And from here on it looks pretty much the same as for a + * system unit, hence let's use the same parser from here + * on. */ + return cg_path_get_unit(t, ret); +} + +int cg_pid_get_user_unit(pid_t pid, char **unit) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(unit); + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_user_unit(cgroup, unit); +} + +int cg_path_get_machine_name(const char *path, char **machine) { + _cleanup_free_ char *u = NULL; + const char *sl; + int r; + + r = cg_path_get_unit(path, &u); + if (r < 0) + return r; + + sl = strjoina("/run/systemd/machines/unit:", u); + return readlink_malloc(sl, machine); +} + +int cg_pid_get_machine_name(pid_t pid, char **machine) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(machine); + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_machine_name(cgroup, machine); +} + +int cg_path_get_session(const char *path, char **session) { + _cleanup_free_ char *unit = NULL; + char *start, *end; + int r; + + assert(path); + + r = cg_path_get_unit(path, &unit); + if (r < 0) + return r; + + start = startswith(unit, "session-"); + if (!start) + return -ENXIO; + end = endswith(start, ".scope"); + if (!end) + return -ENXIO; + + *end = 0; + if (!session_id_valid(start)) + return -ENXIO; + + if (session) { + char *rr; + + rr = strdup(start); + if (!rr) + return -ENOMEM; + + *session = rr; + } + + return 0; +} + +int cg_pid_get_session(pid_t pid, char **session) { + _cleanup_free_ char *cgroup = NULL; + int r; + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_session(cgroup, session); +} + +int cg_path_get_owner_uid(const char *path, uid_t *uid) { + _cleanup_free_ char *slice = NULL; + char *start, *end; + int r; + + assert(path); + + r = cg_path_get_slice(path, &slice); + if (r < 0) + return r; + + start = startswith(slice, "user-"); + if (!start) + return -ENXIO; + end = endswith(start, ".slice"); + if (!end) + return -ENXIO; + + *end = 0; + if (parse_uid(start, uid) < 0) + return -ENXIO; + + return 0; +} + +int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) { + _cleanup_free_ char *cgroup = NULL; + int r; + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_owner_uid(cgroup, uid); +} + +int cg_path_get_slice(const char *p, char **slice) { + const char *e = NULL; + + assert(p); + assert(slice); + + /* Finds the right-most slice unit from the beginning, but + * stops before we come to the first non-slice unit. */ + + for (;;) { + size_t n; + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (!valid_slice_name(p, n)) { + + if (!e) { + char *s; + + s = strdup("-.slice"); + if (!s) + return -ENOMEM; + + *slice = s; + return 0; + } + + return cg_path_decode_unit(e, slice); + } + + e = p; + p += n; + } +} + +int cg_pid_get_slice(pid_t pid, char **slice) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(slice); + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_slice(cgroup, slice); +} + +int cg_path_get_user_slice(const char *p, char **slice) { + const char *t; + assert(p); + assert(slice); + + t = skip_user_prefix(p); + if (!t) + return -ENXIO; + + /* And now it looks pretty much the same as for a system + * slice, so let's just use the same parser from here on. */ + return cg_path_get_slice(t, slice); +} + +int cg_pid_get_user_slice(pid_t pid, char **slice) { + _cleanup_free_ char *cgroup = NULL; + int r; + + assert(slice); + + r = cg_pid_get_path_shifted(pid, NULL, &cgroup); + if (r < 0) + return r; + + return cg_path_get_user_slice(cgroup, slice); +} + +char *cg_escape(const char *p) { + bool need_prefix = false; + + /* This implements very minimal escaping for names to be used + * as file names in the cgroup tree: any name which might + * conflict with a kernel name or is prefixed with '_' is + * prefixed with a '_'. That way, when reading cgroup names it + * is sufficient to remove a single prefixing underscore if + * there is one. */ + + /* The return value of this function (unlike cg_unescape()) + * needs free()! */ + + if (p[0] == 0 || + p[0] == '_' || + p[0] == '.' || + streq(p, "notify_on_release") || + streq(p, "release_agent") || + streq(p, "tasks") || + startswith(p, "cgroup.")) + need_prefix = true; + else { + const char *dot; + + dot = strrchr(p, '.'); + if (dot) { + CGroupController c; + size_t l = dot - p; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *n; + + n = cgroup_controller_to_string(c); + + if (l != strlen(n)) + continue; + + if (memcmp(p, n, l) != 0) + continue; + + need_prefix = true; + break; + } + } + } + + if (need_prefix) + return strappend("_", p); + + return strdup(p); +} + +char *cg_unescape(const char *p) { + assert(p); + + /* The return value of this function (unlike cg_escape()) + * doesn't need free()! */ + + if (p[0] == '_') + return (char*) p+1; + + return (char*) p; +} + +#define CONTROLLER_VALID \ + DIGITS LETTERS \ + "_" + +bool cg_controller_is_valid(const char *p) { + const char *t, *s; + + if (!p) + return false; + + s = startswith(p, "name="); + if (s) + p = s; + + if (*p == 0 || *p == '_') + return false; + + for (t = p; *t; t++) + if (!strchr(CONTROLLER_VALID, *t)) + return false; + + if (t - p > FILENAME_MAX) + return false; + + return true; +} + +int cg_slice_to_path(const char *unit, char **ret) { + _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL; + const char *dash; + int r; + + assert(unit); + assert(ret); + + if (streq(unit, "-.slice")) { + char *x; + + x = strdup(""); + if (!x) + return -ENOMEM; + *ret = x; + return 0; + } + + if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN)) + return -EINVAL; + + if (!endswith(unit, ".slice")) + return -EINVAL; + + r = unit_name_to_prefix(unit, &p); + if (r < 0) + return r; + + dash = strchr(p, '-'); + + /* Don't allow initial dashes */ + if (dash == p) + return -EINVAL; + + while (dash) { + _cleanup_free_ char *escaped = NULL; + char n[dash - p + sizeof(".slice")]; + + /* Don't allow trailing or double dashes */ + if (dash[1] == 0 || dash[1] == '-') + return -EINVAL; + + strcpy(stpncpy(n, p, dash - p), ".slice"); + if (!unit_name_is_valid(n, UNIT_NAME_PLAIN)) + return -EINVAL; + + escaped = cg_escape(n); + if (!escaped) + return -ENOMEM; + + if (!strextend(&s, escaped, "/", NULL)) + return -ENOMEM; + + dash = strchr(dash+1, '-'); + } + + e = cg_escape(unit); + if (!e) + return -ENOMEM; + + if (!strextend(&s, e, NULL)) + return -ENOMEM; + + *ret = s; + s = NULL; + + return 0; +} + +int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) { + _cleanup_free_ char *p = NULL; + int r; + + r = cg_get_path(controller, path, attribute, &p); + if (r < 0) + return r; + + return write_string_file(p, value, 0); +} + +int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) { + _cleanup_free_ char *p = NULL; + int r; + + r = cg_get_path(controller, path, attribute, &p); + if (r < 0) + return r; + + return read_one_line_file(p, ret); +} + +int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) { + CGroupController c; + int r, unified; + + /* This one will create a cgroup in our private tree, but also + * duplicate it in the trees specified in mask, and remove it + * in all others */ + + /* First create the cgroup in our own hierarchy. */ + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path); + if (r < 0) + return r; + + /* If we are in the unified hierarchy, we are done now */ + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return 0; + + /* Otherwise, do the same in the other hierarchies */ + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *n; + + n = cgroup_controller_to_string(c); + + if (mask & bit) + (void) cg_create(n, path); + else if (supported & bit) + (void) cg_trim(n, path, true); + } + + return 0; +} + +int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { + CGroupController c; + int r, unified; + + r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid); + if (r < 0) + return r; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return 0; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *p = NULL; + + if (!(supported & bit)) + continue; + + if (path_callback) + p = path_callback(bit, userdata); + + if (!p) + p = path; + + (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid); + } + + return 0; +} + +int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { + Iterator i; + void *pidp; + int r = 0; + + SET_FOREACH(pidp, pids, i) { + pid_t pid = PTR_TO_PID(pidp); + int q; + + q = cg_attach_everywhere(supported, path, pid, path_callback, userdata); + if (q < 0 && r >= 0) + r = q; + } + + return r; +} + +int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { + CGroupController c; + int r = 0, unified; + + if (!path_equal(from, to)) { + r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE); + if (r < 0) + return r; + } + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return r; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *p = NULL; + + if (!(supported & bit)) + continue; + + if (to_callback) + p = to_callback(bit, userdata); + + if (!p) + p = to; + + (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0); + } + + return 0; +} + +int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) { + CGroupController c; + int r, unified; + + r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root); + if (r < 0) + return r; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return r; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + + if (!(supported & bit)) + continue; + + (void) cg_trim(cgroup_controller_to_string(c), path, delete_root); + } + + return 0; +} + +int cg_mask_supported(CGroupMask *ret) { + CGroupMask mask = 0; + int r, unified; + + /* Determines the mask of supported cgroup controllers. Only + * includes controllers we can make sense of and that are + * actually accessible. */ + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) { + _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; + const char *c; + + /* In the unified hierarchy we can read the supported + * and accessible controllers from a the top-level + * cgroup attribute */ + + r = cg_get_root_path(&root); + if (r < 0) + return r; + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path); + if (r < 0) + return r; + + r = read_one_line_file(path, &controllers); + if (r < 0) + return r; + + c = controllers; + for (;;) { + _cleanup_free_ char *n = NULL; + CGroupController v; + + r = extract_first_word(&c, &n, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + v = cgroup_controller_from_string(n); + if (v < 0) + continue; + + mask |= CGROUP_CONTROLLER_TO_MASK(v); + } + + /* Currently, we only support the memory, io and pids + * controller in the unified hierarchy, mask + * everything else off. */ + mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS; + + } else { + CGroupController c; + + /* In the legacy hierarchy, we check whether which + * hierarchies are mounted. */ + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *n; + + n = cgroup_controller_to_string(c); + if (controller_is_accessible(n) >= 0) + mask |= CGROUP_CONTROLLER_TO_MASK(c); + } + } + + *ret = mask; + return 0; +} + +int cg_kernel_controllers(Set *controllers) { + _cleanup_fclose_ FILE *f = NULL; + char buf[LINE_MAX]; + int r; + + assert(controllers); + + /* Determines the full list of kernel-known controllers. Might + * include controllers we don't actually support, arbitrary + * named hierarchies and controllers that aren't currently + * accessible (because not mounted). */ + + f = fopen("/proc/cgroups", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + return -errno; + } + + /* Ignore the header line */ + (void) fgets(buf, sizeof(buf), f); + + for (;;) { + char *controller; + int enabled = 0; + + errno = 0; + if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { + + if (feof(f)) + break; + + if (ferror(f) && errno > 0) + return -errno; + + return -EBADMSG; + } + + if (!enabled) { + free(controller); + continue; + } + + if (!cg_controller_is_valid(controller)) { + free(controller); + return -EBADMSG; + } + + r = set_consume(controllers, controller); + if (r < 0) + return r; + } + + return 0; +} + +static thread_local int unified_cache = -1; + +int cg_unified(void) { + struct statfs fs; + + /* Checks if we support the unified hierarchy. Returns an + * error when the cgroup hierarchies aren't mounted yet or we + * have any other trouble determining if the unified hierarchy + * is supported. */ + + if (unified_cache >= 0) + return unified_cache; + + if (statfs("/sys/fs/cgroup/", &fs) < 0) + return -errno; + + if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) + unified_cache = true; + else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) + unified_cache = false; + else + return -ENOMEDIUM; + + return unified_cache; +} + +void cg_unified_flush(void) { + unified_cache = -1; +} + +int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { + _cleanup_free_ char *fs = NULL; + CGroupController c; + int r, unified; + + assert(p); + + if (supported == 0) + return 0; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */ + return 0; + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs); + if (r < 0) + return r; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *n; + + if (!(supported & bit)) + continue; + + n = cgroup_controller_to_string(c); + { + char s[1 + strlen(n) + 1]; + + s[0] = mask & bit ? '+' : '-'; + strcpy(s + 1, n); + + r = write_string_file(fs, s, 0); + if (r < 0) + log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); + } + } + + return 0; +} + +bool cg_is_unified_wanted(void) { + static thread_local int wanted = -1; + int r, unified; + + /* If the hierarchy is already mounted, then follow whatever + * was chosen for it. */ + unified = cg_unified(); + if (unified >= 0) + return unified; + + /* Otherwise, let's see what the kernel command line has to + * say. Since checking that is expensive, let's cache the + * result. */ + if (wanted >= 0) + return wanted; + + r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL); + if (r > 0) + return (wanted = true); + else { + _cleanup_free_ char *value = NULL; + + r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value); + if (r < 0) + return false; + if (r == 0) + return (wanted = false); + + return (wanted = parse_boolean(value) > 0); + } +} + +bool cg_is_legacy_wanted(void) { + return !cg_is_unified_wanted(); +} + +int cg_weight_parse(const char *s, uint64_t *ret) { + uint64_t u; + int r; + + if (isempty(s)) { + *ret = CGROUP_WEIGHT_INVALID; + return 0; + } + + r = safe_atou64(s, &u); + if (r < 0) + return r; + + if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX) + return -ERANGE; + + *ret = u; + return 0; +} + +const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = { + [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, +}; + +static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = { + [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", + [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", + [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", + [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", +}; + +DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); + +int cg_cpu_shares_parse(const char *s, uint64_t *ret) { + uint64_t u; + int r; + + if (isempty(s)) { + *ret = CGROUP_CPU_SHARES_INVALID; + return 0; + } + + r = safe_atou64(s, &u); + if (r < 0) + return r; + + if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX) + return -ERANGE; + + *ret = u; + return 0; +} + +int cg_blkio_weight_parse(const char *s, uint64_t *ret) { + uint64_t u; + int r; + + if (isempty(s)) { + *ret = CGROUP_BLKIO_WEIGHT_INVALID; + return 0; + } + + r = safe_atou64(s, &u); + if (r < 0) + return r; + + if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX) + return -ERANGE; + + *ret = u; + return 0; +} + +static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { + [CGROUP_CONTROLLER_CPU] = "cpu", + [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", + [CGROUP_CONTROLLER_IO] = "io", + [CGROUP_CONTROLLER_BLKIO] = "blkio", + [CGROUP_CONTROLLER_MEMORY] = "memory", + [CGROUP_CONTROLLER_DEVICES] = "devices", + [CGROUP_CONTROLLER_PIDS] = "pids", +}; + +DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/libbasic/src/chattr-util.c b/src/libbasic/src/chattr-util.c new file mode 100644 index 0000000000..4d349431c8 --- /dev/null +++ b/src/libbasic/src/chattr-util.c @@ -0,0 +1,108 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/chattr-util.h" +#include "basic/fd-util.h" +#include "basic/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/libbasic/src/clock-util.c b/src/libbasic/src/clock-util.c new file mode 100644 index 0000000000..b1d0d2e432 --- /dev/null +++ b/src/libbasic/src/clock-util.c @@ -0,0 +1,166 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/clock-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" + +int clock_get_hwclock(struct tm *tm) { + _cleanup_close_ int fd = -1; + + assert(tm); + + fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + /* This leaves the timezone fields of struct tm + * uninitialized! */ + if (ioctl(fd, RTC_RD_TIME, tm) < 0) + return -errno; + + /* We don't know daylight saving, so we reset this in order not + * to confuse mktime(). */ + tm->tm_isdst = -1; + + return 0; +} + +int clock_set_hwclock(const struct tm *tm) { + _cleanup_close_ int fd = -1; + + assert(tm); + + fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (ioctl(fd, RTC_SET_TIME, tm) < 0) + return -errno; + + return 0; +} + +int clock_is_localtime(const char* adjtime_path) { + _cleanup_fclose_ FILE *f; + + if (adjtime_path == NULL) + adjtime_path = "/etc/adjtime"; + + /* + * The third line of adjtime is "UTC" or "LOCAL" or nothing. + * # /etc/adjtime + * 0.0 0 0 + * 0 + * UTC + */ + f = fopen(adjtime_path, "re"); + if (f) { + char line[LINE_MAX]; + bool b; + + b = fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f); + if (!b) + /* less than three lines -> default to UTC */ + return 0; + + truncate_nl(line); + return streq(line, "LOCAL"); + + } else if (errno != ENOENT) + return -errno; + + /* adjtime not present -> default to UTC */ + return 0; +} + +int clock_set_timezone(int *min) { + const struct timeval *tv_null = NULL; + struct timespec ts; + struct tm *tm; + int minutesdelta; + struct timezone tz; + + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + assert_se(tm = localtime(&ts.tv_sec)); + minutesdelta = tm->tm_gmtoff / 60; + + tz.tz_minuteswest = -minutesdelta; + tz.tz_dsttime = 0; /* DST_NONE */ + + /* + * If the RTC does not run in UTC but in local time, the very first + * call to settimeofday() will set the kernel's timezone and will warp the + * system clock, so that it runs in UTC instead of the local time we + * have read from the RTC. + */ + if (settimeofday(tv_null, &tz) < 0) + return negative_errno(); + + if (min) + *min = minutesdelta; + return 0; +} + +int clock_reset_timewarp(void) { + const struct timeval *tv_null = NULL; + struct timezone tz; + + tz.tz_minuteswest = 0; + tz.tz_dsttime = 0; /* DST_NONE */ + + /* + * The very first call to settimeofday() does time warp magic. Do a + * dummy call here, so the time warping is sealed and all later calls + * behave as expected. + */ + if (settimeofday(tv_null, &tz) < 0) + return -errno; + + return 0; +} + +#define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC) + +int clock_apply_epoch(void) { + struct timespec ts; + + if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC) + return 0; + + if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0) + return -errno; + + return 1; +} diff --git a/src/libbasic/src/conf-files.c b/src/libbasic/src/conf-files.c new file mode 100644 index 0000000000..2bb794e256 --- /dev/null +++ b/src/libbasic/src/conf-files.c @@ -0,0 +1,166 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/conf-files.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/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); + assert(suffix); + + dirpath = prefix_roota(root, path); + + dir = opendir(dirpath); + if (!dir) { + if (errno == ENOENT) + return 0; + return -errno; + } + + FOREACH_DIRENT(de, dir, return -errno) { + char *p; + + if (!dirent_is_file_with_suffix(de, suffix)) + continue; + + p = strjoin(dirpath, "/", de->d_name, NULL); + if (!p) + return -ENOMEM; + + r = hashmap_put(h, basename(p), p); + if (r == -EEXIST) { + log_debug("Skipping overridden file: %s.", p); + free(p); + } else if (r < 0) { + free(p); + return r; + } else if (r == 0) { + log_debug("Duplicate file %s", p); + free(p); + } + } + + return 0; +} + +static int base_cmp(const void *a, const void *b) { + const char *s1, *s2; + + s1 = *(char * const *)a; + s2 = *(char * const *)b; + return strcmp(basename(s1), basename(s2)); +} + +static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { + _cleanup_hashmap_free_ Hashmap *fh = NULL; + char **files, **p; + int r; + + assert(strv); + assert(suffix); + + /* This alters the dirs string array */ + if (!path_strv_resolve_uniq(dirs, root)) + return -ENOMEM; + + fh = hashmap_new(&string_hash_ops); + if (!fh) + return -ENOMEM; + + STRV_FOREACH(p, dirs) { + r = files_add(fh, root, *p, suffix); + if (r == -ENOMEM) + return r; + if (r < 0) + log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); + } + + files = hashmap_get_strv(fh); + if (!files) + return -ENOMEM; + + qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); + *strv = files; + + return 0; +} + +int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { + _cleanup_strv_free_ char **copy = NULL; + + assert(strv); + assert(suffix); + + copy = strv_copy((char**) dirs); + if (!copy) + return -ENOMEM; + + return conf_files_list_strv_internal(strv, suffix, root, copy); +} + +int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { + _cleanup_strv_free_ char **dirs = NULL; + va_list ap; + + assert(strv); + assert(suffix); + + va_start(ap, dir); + dirs = strv_new_ap(dir, ap); + va_end(ap); + + if (!dirs) + return -ENOMEM; + + return conf_files_list_strv_internal(strv, suffix, root, dirs); +} + +int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { + _cleanup_strv_free_ char **dirs = NULL; + + assert(strv); + assert(suffix); + + dirs = strv_split_nulstr(d); + if (!dirs) + return -ENOMEM; + + return conf_files_list_strv_internal(strv, suffix, root, dirs); +} diff --git a/src/libbasic/src/copy.c b/src/libbasic/src/copy.c new file mode 100644 index 0000000000..ed251f739a --- /dev/null +++ b/src/libbasic/src/copy.c @@ -0,0 +1,603 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/chattr-util.h" +#include "basic/copy.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/xattr-util.h" + +#define COPY_BUFFER_SIZE (16*1024u) + +static ssize_t try_copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { + static int have = -1; + ssize_t r; + + if (have == false) + return -ENOSYS; + + r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); + if (_unlikely_(have < 0)) + have = r >= 0 || errno != ENOSYS; + if (r >= 0) + return r; + else + return -errno; +} + +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { + bool try_cfr = true, try_sendfile = true, try_splice = true; + int r; + size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ + + assert(fdf >= 0); + assert(fdt >= 0); + + /* Try btrfs reflinks first. */ + if (try_reflink && + max_bytes == (uint64_t) -1 && + lseek(fdf, 0, SEEK_CUR) == 0 && + lseek(fdt, 0, SEEK_CUR) == 0) { + + r = btrfs_reflink(fdf, fdt); + if (r >= 0) + return 0; /* we copied the whole thing, hence hit EOF, return 0 */ + } + + for (;;) { + ssize_t n; + + if (max_bytes != (uint64_t) -1) { + if (max_bytes <= 0) + return 1; /* return > 0 if we hit the max_bytes limit */ + + if (m > max_bytes) + m = max_bytes; + } + + /* First try copy_file_range(), unless we already tried */ + if (try_cfr) { + n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); + if (n < 0) { + if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) + return n; + + try_cfr = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else + /* Success! */ + goto next; + } + + /* First try sendfile(), unless we already tried */ + if (try_sendfile) { + n = sendfile(fdt, fdf, NULL, m); + if (n < 0) { + if (!IN_SET(errno, EINVAL, ENOSYS)) + return -errno; + + try_sendfile = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else + /* Success! */ + goto next; + } + + /* Then try splice, unless we already tried */ + if (try_splice) { + n = splice(fdf, NULL, fdt, NULL, m, 0); + if (n < 0) { + if (!IN_SET(errno, EINVAL, ENOSYS)) + return -errno; + + try_splice = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else + /* Success! */ + goto next; + } + + /* As a fallback just copy bits by hand */ + { + uint8_t buf[MIN(m, COPY_BUFFER_SIZE)]; + + n = read(fdf, buf, sizeof buf); + if (n < 0) + return -errno; + if (n == 0) /* EOF */ + break; + + r = loop_write(fdt, buf, (size_t) n, false); + if (r < 0) + return r; + } + + next: + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); + max_bytes -= n; + } + /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, + * so reduce our maximum by the amount we already copied, + * but don't go below our copy buffer size, unless we are + * close the limit of bytes we are allowed to copy. */ + m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); + } + + return 0; /* return 0 if we hit EOF earlier than the size limit */ +} + +static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { + _cleanup_free_ char *target = NULL; + int r; + + assert(from); + assert(st); + assert(to); + + r = readlinkat_malloc(df, from, &target); + if (r < 0) + return r; + + if (symlinkat(target, dt, to) < 0) + return -errno; + + if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + return -errno; + + return 0; +} + +static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) { + _cleanup_close_ int fdf = -1, fdt = -1; + struct timespec ts[2]; + int r, q; + + assert(from); + assert(st); + assert(to); + + fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fdf < 0) + return -errno; + + fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); + if (fdt < 0) + return -errno; + + r = copy_bytes(fdf, fdt, (uint64_t) -1, true); + if (r < 0) { + unlinkat(dt, to, 0); + return r; + } + + if (fchown(fdt, st->st_uid, st->st_gid) < 0) + r = -errno; + + if (fchmod(fdt, st->st_mode & 07777) < 0) + r = -errno; + + ts[0] = st->st_atim; + ts[1] = st->st_mtim; + (void) futimens(fdt, ts); + + (void) copy_xattr(fdf, fdt); + + q = close(fdt); + fdt = -1; + + if (q < 0) { + r = -errno; + unlinkat(dt, to, 0); + } + + return r; +} + +static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) { + int r; + + assert(from); + assert(st); + assert(to); + + r = mkfifoat(dt, to, st->st_mode & 07777); + if (r < 0) + return -errno; + + if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + r = -errno; + + if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) + r = -errno; + + return r; +} + +static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { + int r; + + assert(from); + assert(st); + assert(to); + + r = mknodat(dt, to, st->st_mode, st->st_rdev); + if (r < 0) + return -errno; + + if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + r = -errno; + + if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) + r = -errno; + + return r; +} + +static int fd_copy_directory( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + dev_t original_device, + bool merge) { + + _cleanup_close_ int fdf = -1, fdt = -1; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + bool created; + int r; + + assert(st); + assert(to); + + if (from) + fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + else + fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); + if (fdf < 0) + return -errno; + + d = fdopendir(fdf); + if (!d) + return -errno; + fdf = -1; + + r = mkdirat(dt, to, st->st_mode & 07777); + if (r >= 0) + created = true; + else if (errno == EEXIST && merge) + created = false; + else + return -errno; + + fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fdt < 0) + return -errno; + + r = 0; + + FOREACH_DIRENT_ALL(de, d, return -errno) { + struct stat buf; + int q; + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { + r = -errno; + continue; + } + + if (buf.st_dev != original_device) + continue; + + if (S_ISREG(buf.st_mode)) + q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); + else if (S_ISDIR(buf.st_mode)) + q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); + else if (S_ISLNK(buf.st_mode)) + q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); + else if (S_ISFIFO(buf.st_mode)) + q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); + else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) + q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); + else + q = -EOPNOTSUPP; + + if (q == -EEXIST && merge) + q = 0; + + if (q < 0) + r = q; + } + + if (created) { + struct timespec ut[2] = { + st->st_atim, + st->st_mtim + }; + + if (fchown(fdt, st->st_uid, st->st_gid) < 0) + r = -errno; + + if (fchmod(fdt, st->st_mode & 07777) < 0) + r = -errno; + + (void) copy_xattr(dirfd(d), fdt); + (void) futimens(fdt, ut); + } + + return r; +} + +int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) { + struct stat st; + + assert(from); + assert(to); + + if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0) + return -errno; + + if (S_ISREG(st.st_mode)) + return fd_copy_regular(fdf, from, &st, fdt, to); + else if (S_ISDIR(st.st_mode)) + return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge); + else if (S_ISLNK(st.st_mode)) + return fd_copy_symlink(fdf, from, &st, fdt, to); + else if (S_ISFIFO(st.st_mode)) + return fd_copy_fifo(fdf, from, &st, fdt, to); + else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) + return fd_copy_node(fdf, from, &st, fdt, to); + else + return -EOPNOTSUPP; +} + +int copy_tree(const char *from, const char *to, bool merge) { + return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge); +} + +int copy_directory_fd(int dirfd, const char *to, bool merge) { + struct stat st; + + assert(dirfd >= 0); + assert(to); + + if (fstat(dirfd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); +} + +int copy_directory(const char *from, const char *to, bool merge) { + struct stat st; + + assert(from); + assert(to); + + if (lstat(from, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); +} + +int copy_file_fd(const char *from, int fdt, bool try_reflink) { + _cleanup_close_ int fdf = -1; + int r; + + assert(from); + assert(fdt >= 0); + + fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fdf < 0) + return -errno; + + r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); + + (void) copy_times(fdf, fdt); + (void) copy_xattr(fdf, fdt); + + return r; +} + +int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) { + int fdt = -1, r; + + assert(from); + assert(to); + + RUN_WITH_UMASK(0000) { + fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); + if (fdt < 0) + return -errno; + } + + if (chattr_flags != 0) + (void) chattr_fd(fdt, chattr_flags, (unsigned) -1); + + r = copy_file_fd(from, fdt, true); + if (r < 0) { + close(fdt); + unlink(to); + return r; + } + + if (close(fdt) < 0) { + unlink_noerrno(to); + return -errno; + } + + return 0; +} + +int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) { + _cleanup_free_ char *t = NULL; + int r; + + assert(from); + assert(to); + + r = tempfn_random(to, NULL, &t); + if (r < 0) + return r; + + r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags); + if (r < 0) + return r; + + if (replace) { + r = renameat(AT_FDCWD, t, AT_FDCWD, to); + if (r < 0) + r = -errno; + } else + r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to); + if (r < 0) { + (void) unlink_noerrno(t); + return r; + } + + return 0; +} + +int copy_times(int fdf, int fdt) { + struct timespec ut[2]; + struct stat st; + usec_t crtime = 0; + + assert(fdf >= 0); + assert(fdt >= 0); + + if (fstat(fdf, &st) < 0) + return -errno; + + ut[0] = st.st_atim; + ut[1] = st.st_mtim; + + if (futimens(fdt, ut) < 0) + return -errno; + + if (fd_getcrtime(fdf, &crtime) >= 0) + (void) fd_setcrtime(fdt, crtime); + + return 0; +} + +int copy_xattr(int fdf, int fdt) { + _cleanup_free_ char *bufa = NULL, *bufb = NULL; + size_t sza = 100, szb = 100; + ssize_t n; + int ret = 0; + const char *p; + + for (;;) { + bufa = malloc(sza); + if (!bufa) + return -ENOMEM; + + n = flistxattr(fdf, bufa, sza); + if (n == 0) + return 0; + if (n > 0) + break; + if (errno != ERANGE) + return -errno; + + sza *= 2; + + bufa = mfree(bufa); + } + + p = bufa; + while (n > 0) { + size_t l; + + l = strlen(p); + assert(l < (size_t) n); + + if (startswith(p, "user.")) { + ssize_t m; + + if (!bufb) { + bufb = malloc(szb); + if (!bufb) + return -ENOMEM; + } + + m = fgetxattr(fdf, p, bufb, szb); + if (m < 0) { + if (errno == ERANGE) { + szb *= 2; + bufb = mfree(bufb); + continue; + } + + return -errno; + } + + if (fsetxattr(fdt, p, bufb, m, 0) < 0) + ret = -errno; + } + + p += l + 1; + n -= l + 1; + } + + return ret; +} diff --git a/src/libbasic/src/cpu-set-util.c b/src/libbasic/src/cpu-set-util.c new file mode 100644 index 0000000000..89e012d6e2 --- /dev/null +++ b/src/libbasic/src/cpu-set-util.c @@ -0,0 +1,114 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cpu-set-util.h" +#include "basic/extract-word.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/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/libbasic/src/device-nodes.c b/src/libbasic/src/device-nodes.c new file mode 100644 index 0000000000..e5b2eb637c --- /dev/null +++ b/src/libbasic/src/device-nodes.c @@ -0,0 +1,80 @@ +/*** + This file is part of systemd. + + Copyright 2008-2011 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/device-nodes.h" +#include "basic/utf8.h" + +int whitelisted_char_for_devnode(char c, const char *white) { + + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + strchr("#+-.:=@_", c) != NULL || + (white != NULL && strchr(white, c) != NULL)) + return 1; + + return 0; +} + +int encode_devnode_name(const char *str, char *str_enc, size_t len) { + size_t i, j; + + if (str == NULL || str_enc == NULL) + return -EINVAL; + + for (i = 0, j = 0; str[i] != '\0'; i++) { + int seqlen; + + seqlen = utf8_encoded_valid_unichar(&str[i]); + if (seqlen > 1) { + + if (len-j < (size_t)seqlen) + return -EINVAL; + + memcpy(&str_enc[j], &str[i], seqlen); + j += seqlen; + i += (seqlen-1); + + } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) { + + if (len-j < 4) + return -EINVAL; + + sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); + j += 4; + + } else { + if (len-j < 1) + return -EINVAL; + + str_enc[j] = str[i]; + j++; + } + } + + if (len-j < 1) + return -EINVAL; + + str_enc[j] = '\0'; + return 0; +} diff --git a/src/libbasic/src/dirent-util.c b/src/libbasic/src/dirent-util.c new file mode 100644 index 0000000000..5bc740b189 --- /dev/null +++ b/src/libbasic/src/dirent-util.c @@ -0,0 +1,74 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/dirent-util.h" +#include "basic/path-util.h" +#include "basic/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 (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) + return false; + + if (hidden_or_backup_file(de->d_name)) + return false; + + return true; +} + +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { + assert(de); + + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) + return false; + + if (de->d_name[0] == '.') + return false; + + return endswith(de->d_name, suffix); +} diff --git a/src/libbasic/src/env-util.c b/src/libbasic/src/env-util.c new file mode 100644 index 0000000000..67d8261c87 --- /dev/null +++ b/src/libbasic/src/env-util.c @@ -0,0 +1,624 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/env-util.h" +#include "basic/extract-word.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" + +#define VALID_CHARS_ENV_NAME \ + DIGITS LETTERS \ + "_" + +#ifndef ARG_MAX +#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) +#endif + +static bool env_name_is_valid_n(const char *e, size_t n) { + const char *p; + + if (!e) + return false; + + if (n <= 0) + return false; + + if (e[0] >= '0' && e[0] <= '9') + return false; + + /* POSIX says the overall size of the environment block cannot + * be > ARG_MAX, an individual assignment hence cannot be + * either. Discounting the equal sign and trailing NUL this + * hence leaves ARG_MAX-2 as longest possible variable + * name. */ + if (n > ARG_MAX - 2) + return false; + + for (p = e; p < e + n; p++) + if (!strchr(VALID_CHARS_ENV_NAME, *p)) + return false; + + return true; +} + +bool env_name_is_valid(const char *e) { + if (!e) + return false; + + return env_name_is_valid_n(e, strlen(e)); +} + +bool env_value_is_valid(const char *e) { + if (!e) + return false; + + if (!utf8_is_valid(e)) + return false; + + /* bash allows tabs in environment variables, and so should + * we */ + if (string_has_cc(e, "\t")) + return false; + + /* POSIX says the overall size of the environment block cannot + * be > ARG_MAX, an individual assignment hence cannot be + * either. Discounting the shortest possible variable name of + * length 1, the equal sign and trailing NUL this hence leaves + * ARG_MAX-3 as longest possible variable value. */ + if (strlen(e) > ARG_MAX - 3) + return false; + + return true; +} + +bool env_assignment_is_valid(const char *e) { + const char *eq; + + eq = strchr(e, '='); + if (!eq) + return false; + + if (!env_name_is_valid_n(e, eq - e)) + return false; + + if (!env_value_is_valid(eq + 1)) + return false; + + /* POSIX says the overall size of the environment block cannot + * be > ARG_MAX, hence the individual variable assignments + * cannot be either, but let's leave room for one trailing NUL + * byte. */ + if (strlen(e) > ARG_MAX - 1) + return false; + + return true; +} + +bool strv_env_is_valid(char **e) { + char **p, **q; + + STRV_FOREACH(p, e) { + size_t k; + + if (!env_assignment_is_valid(*p)) + return false; + + /* Check if there are duplicate assginments */ + k = strcspn(*p, "="); + STRV_FOREACH(q, p + 1) + if (strneq(*p, *q, k) && (*q)[k] == '=') + return false; + } + + return true; +} + +bool strv_env_name_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; + + STRV_FOREACH(p, l) { + if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) + return false; + + STRV_FOREACH(q, p + 1) + if (streq(*p, *q)) + return false; + } + + return true; +} + +static int env_append(char **r, char ***k, char **a) { + assert(r); + assert(k); + + if (!a) + return 0; + + /* Add the entries of a to *k unless they already exist in *r + * in which case they are overridden instead. This assumes + * there is enough space in the r array. */ + + for (; *a; a++) { + char **j; + size_t n; + + n = strcspn(*a, "="); + + if ((*a)[n] == '=') + n++; + + for (j = r; j < *k; j++) + if (strneq(*j, *a, n)) + break; + + if (j >= *k) + (*k)++; + else + free(*j); + + *j = strdup(*a); + if (!*j) + return -ENOMEM; + } + + return 0; +} + +char **strv_env_merge(unsigned n_lists, ...) { + size_t n = 0; + char **l, **k, **r; + va_list ap; + unsigned i; + + /* Merges an arbitrary number of environment sets */ + + va_start(ap, n_lists); + for (i = 0; i < n_lists; i++) { + l = va_arg(ap, char**); + n += strv_length(l); + } + va_end(ap); + + r = new(char*, n+1); + if (!r) + return NULL; + + k = r; + + va_start(ap, n_lists); + for (i = 0; i < n_lists; i++) { + l = va_arg(ap, char**); + if (env_append(r, &k, l) < 0) + goto fail; + } + va_end(ap); + + *k = NULL; + + return r; + +fail: + va_end(ap); + strv_free(r); + + return NULL; +} + +_pure_ static bool env_match(const char *t, const char *pattern) { + assert(t); + assert(pattern); + + /* pattern a matches string a + * a matches a= + * a matches a=b + * a= matches a= + * a=b matches a=b + * a= does not match a + * a=b does not match a= + * a=b does not match a + * a=b does not match a=c */ + + if (streq(t, pattern)) + return true; + + if (!strchr(pattern, '=')) { + size_t l = strlen(pattern); + + return strneq(t, pattern, l) && t[l] == '='; + } + + return false; +} + +char **strv_env_delete(char **x, unsigned n_lists, ...) { + size_t n, i = 0; + char **k, **r; + va_list ap; + + /* Deletes every entry from x that is mentioned in the other + * string lists */ + + n = strv_length(x); + + r = new(char*, n+1); + if (!r) + return NULL; + + STRV_FOREACH(k, x) { + unsigned v; + + va_start(ap, n_lists); + for (v = 0; v < n_lists; v++) { + char **l, **j; + + l = va_arg(ap, char**); + STRV_FOREACH(j, l) + if (env_match(*k, *j)) + goto skip; + } + va_end(ap); + + r[i] = strdup(*k); + if (!r[i]) { + strv_free(r); + return NULL; + } + + i++; + continue; + + skip: + va_end(ap); + } + + r[i] = NULL; + + assert(i <= n); + + return r; +} + +char **strv_env_unset(char **l, const char *p) { + + char **f, **t; + + if (!l) + return NULL; + + assert(p); + + /* Drops every occurrence of the env var setting p in the + * string list. Edits in-place. */ + + for (f = t = l; *f; f++) { + + if (env_match(*f, p)) { + free(*f); + continue; + } + + *(t++) = *f; + } + + *t = NULL; + return l; +} + +char **strv_env_unset_many(char **l, ...) { + + char **f, **t; + + if (!l) + return NULL; + + /* Like strv_env_unset() but applies many at once. Edits in-place. */ + + for (f = t = l; *f; f++) { + bool found = false; + const char *p; + va_list ap; + + va_start(ap, l); + + while ((p = va_arg(ap, const char*))) { + if (env_match(*f, p)) { + found = true; + break; + } + } + + va_end(ap); + + if (found) { + free(*f); + continue; + } + + *(t++) = *f; + } + + *t = NULL; + return l; +} + +char **strv_env_set(char **x, const char *p) { + + char **k, **r; + char* m[2] = { (char*) p, NULL }; + + /* Overrides the env var setting of p, returns a new copy */ + + r = new(char*, strv_length(x)+2); + if (!r) + return NULL; + + k = r; + if (env_append(r, &k, x) < 0) + goto fail; + + if (env_append(r, &k, m) < 0) + goto fail; + + *k = NULL; + + return r; + +fail: + strv_free(r); + return NULL; +} + +char *strv_env_get_n(char **l, const char *name, size_t k) { + char **i; + + assert(name); + + if (k <= 0) + return NULL; + + STRV_FOREACH(i, l) + if (strneq(*i, name, k) && + (*i)[k] == '=') + return *i + k + 1; + + return NULL; +} + +char *strv_env_get(char **l, const char *name) { + assert(name); + + return strv_env_get_n(l, name, strlen(name)); +} + +char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { + char **p, **q; + int k = 0; + + STRV_FOREACH(p, e) { + size_t n; + bool duplicate = false; + + if (!env_assignment_is_valid(*p)) { + if (invalid_callback) + invalid_callback(*p, userdata); + free(*p); + continue; + } + + n = strcspn(*p, "="); + STRV_FOREACH(q, p + 1) + if (strneq(*p, *q, n) && (*q)[n] == '=') { + duplicate = true; + break; + } + + if (duplicate) { + free(*p); + continue; + } + + e[k++] = *p; + } + + if (e) + e[k] = NULL; + + return e; +} + +char *replace_env(const char *format, char **env) { + enum { + WORD, + CURLY, + VARIABLE + } state = WORD; + + const char *e, *word = format; + char *r = NULL, *k; + + assert(format); + + for (e = format; *e; e ++) { + + switch (state) { + + case WORD: + if (*e == '$') + state = CURLY; + break; + + case CURLY: + if (*e == '{') { + k = strnappend(r, word, e-word-1); + if (!k) + goto fail; + + free(r); + r = k; + + word = e-1; + state = VARIABLE; + + } else if (*e == '$') { + k = strnappend(r, word, e-word); + if (!k) + goto fail; + + free(r); + r = k; + + word = e+1; + state = WORD; + } else + state = WORD; + break; + + case VARIABLE: + if (*e == '}') { + const char *t; + + t = strempty(strv_env_get_n(env, word+2, e-word-2)); + + k = strappend(r, t); + if (!k) + goto fail; + + free(r); + r = k; + + word = e+1; + state = WORD; + } + break; + } + } + + k = strnappend(r, word, e-word); + if (!k) + goto fail; + + free(r); + return k; + +fail: + free(r); + return NULL; +} + +char **replace_env_argv(char **argv, char **env) { + char **ret, **i; + unsigned k = 0, l = 0; + + l = strv_length(argv); + + ret = new(char*, l+1); + if (!ret) + return NULL; + + STRV_FOREACH(i, argv) { + + /* If $FOO appears as single word, replace it by the split up variable */ + if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') { + char *e; + char **w, **m = NULL; + unsigned q; + + e = strv_env_get(env, *i+1); + if (e) { + int r; + + r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES); + if (r < 0) { + ret[k] = NULL; + strv_free(ret); + return NULL; + } + } else + m = NULL; + + q = strv_length(m); + l = l + q - 1; + + w = realloc(ret, sizeof(char*) * (l+1)); + if (!w) { + ret[k] = NULL; + strv_free(ret); + strv_free(m); + return NULL; + } + + ret = w; + if (m) { + memcpy(ret + k, m, q * sizeof(char*)); + free(m); + } + + k += q; + continue; + } + + /* If ${FOO} appears as part of a word, replace it by the variable as-is */ + ret[k] = replace_env(*i, env); + if (!ret[k]) { + strv_free(ret); + return NULL; + } + k++; + } + + ret[k] = NULL; + return ret; +} + +int getenv_bool(const char *p) { + const char *e; + + e = getenv(p); + if (!e) + return -ENXIO; + + return parse_boolean(e); +} diff --git a/src/libbasic/src/errno-list.c b/src/libbasic/src/errno-list.c new file mode 100644 index 0000000000..a40c4efc2e --- /dev/null +++ b/src/libbasic/src/errno-list.c @@ -0,0 +1,57 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/errno-list.h" +#include "basic/macro.h" + +static const struct errno_name* lookup_errno(register const char *str, + register unsigned int len); + +#include "basic/errno-from-name.h" +#include "basic/errno-to-name.h" + +const char *errno_to_name(int id) { + + if (id < 0) + id = -id; + + if (id >= (int) ELEMENTSOF(errno_names)) + return NULL; + + return errno_names[id]; +} + +int errno_from_name(const char *name) { + const struct errno_name *sc; + + assert(name); + + sc = lookup_errno(name, strlen(name)); + if (!sc) + return -EINVAL; + + assert(sc->id > 0); + return sc->id; +} + +int errno_max(void) { + return ELEMENTSOF(errno_names); +} diff --git a/src/libbasic/src/escape.c b/src/libbasic/src/escape.c new file mode 100644 index 0000000000..609b68ae53 --- /dev/null +++ b/src/libbasic/src/escape.c @@ -0,0 +1,502 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/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, char32_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; + char32_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; + char32_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; + bool eight_bit = false; + char32_t u; + 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; +} + +char *octescape(const char *s, size_t len) { + char *r, *t; + const char *f; + + /* Escapes all chars in bad, in addition to \ and " chars, + * in \nnn style escaping. */ + + r = new(char, len * 4 + 1); + if (!r) + return NULL; + + for (f = s, t = r; f < s + len; f++) { + + if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { + *(t++) = '\\'; + *(t++) = '0' + (*f >> 6); + *(t++) = '0' + ((*f >> 3) & 8); + *(t++) = '0' + (*f & 8); + } 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/libbasic/src/ether-addr-util.c b/src/libbasic/src/ether-addr-util.c new file mode 100644 index 0000000000..933b7c9dc7 --- /dev/null +++ b/src/libbasic/src/ether-addr-util.c @@ -0,0 +1,125 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/ether-addr-util.h" +#include "basic/macro.h" +#include "basic/string-util.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; +} + +bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { + assert(a); + assert(b); + + return a->ether_addr_octet[0] == b->ether_addr_octet[0] && + a->ether_addr_octet[1] == b->ether_addr_octet[1] && + a->ether_addr_octet[2] == b->ether_addr_octet[2] && + a->ether_addr_octet[3] == b->ether_addr_octet[3] && + a->ether_addr_octet[4] == b->ether_addr_octet[4] && + a->ether_addr_octet[5] == b->ether_addr_octet[5]; +} + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { + size_t pos = 0, n, field; + char sep = '\0'; + const char *hex = HEXDIGITS, *hexoff; + size_t x; + bool touched; + +#define parse_fields(v) \ + for (field = 0; field < ELEMENTSOF(v); field++) { \ + touched = false; \ + for (n = 0; n < (2 * sizeof(v[0])); n++) { \ + if (s[pos] == '\0') \ + break; \ + hexoff = strchr(hex, s[pos]); \ + if (hexoff == NULL) \ + break; \ + assert(hexoff >= hex); \ + x = hexoff - hex; \ + if (x >= 16) \ + x -= 6; /* A-F */ \ + assert(x < 16); \ + touched = true; \ + v[field] <<= 4; \ + v[field] += x; \ + pos++; \ + } \ + if (!touched) \ + return -EINVAL; \ + if (field < (ELEMENTSOF(v)-1)) { \ + if (s[pos] != sep) \ + return -EINVAL; \ + else \ + pos++; \ + } \ + } + + assert(s); + assert(ret); + + sep = s[strspn(s, hex)]; + if (sep == '\n') + return -EINVAL; + if (strchr(":.-", sep) == NULL) + return -EINVAL; + + if (sep == '.') { + uint16_t shorts[3] = { 0 }; + + parse_fields(shorts); + + for (n = 0; n < ELEMENTSOF(shorts); n++) { + ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); + ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); + } + } else { + struct ether_addr out = { .ether_addr_octet = { 0 } }; + + parse_fields(out.ether_addr_octet); + + for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) + ret->ether_addr_octet[n] = out.ether_addr_octet[n]; + } + + if (offset) + *offset = pos; + return 0; +} diff --git a/src/libbasic/src/exit-status.c b/src/libbasic/src/exit-status.c new file mode 100644 index 0000000000..7bf5752b7b --- /dev/null +++ b/src/libbasic/src/exit-status.c @@ -0,0 +1,234 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/exit-status.h" +#include "basic/macro.h" +#include "basic/set.h" + +const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { + + /* We cast to int here, so that -Wenum doesn't complain that + * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */ + + switch ((int) status) { + + case EXIT_SUCCESS: + return "SUCCESS"; + + case EXIT_FAILURE: + return "FAILURE"; + } + + if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) { + switch ((int) status) { + + case EXIT_CHDIR: + return "CHDIR"; + + case EXIT_NICE: + return "NICE"; + + case EXIT_FDS: + return "FDS"; + + case EXIT_EXEC: + return "EXEC"; + + case EXIT_MEMORY: + return "MEMORY"; + + case EXIT_LIMITS: + return "LIMITS"; + + case EXIT_OOM_ADJUST: + return "OOM_ADJUST"; + + case EXIT_SIGNAL_MASK: + return "SIGNAL_MASK"; + + case EXIT_STDIN: + return "STDIN"; + + case EXIT_STDOUT: + return "STDOUT"; + + case EXIT_CHROOT: + return "CHROOT"; + + case EXIT_IOPRIO: + return "IOPRIO"; + + case EXIT_TIMERSLACK: + return "TIMERSLACK"; + + case EXIT_SECUREBITS: + return "SECUREBITS"; + + case EXIT_SETSCHEDULER: + return "SETSCHEDULER"; + + case EXIT_CPUAFFINITY: + return "CPUAFFINITY"; + + case EXIT_GROUP: + return "GROUP"; + + case EXIT_USER: + return "USER"; + + case EXIT_CAPABILITIES: + return "CAPABILITIES"; + + case EXIT_CGROUP: + return "CGROUP"; + + case EXIT_SETSID: + return "SETSID"; + + case EXIT_CONFIRM: + return "CONFIRM"; + + case EXIT_STDERR: + return "STDERR"; + + case EXIT_PAM: + return "PAM"; + + case EXIT_NETWORK: + return "NETWORK"; + + case EXIT_NAMESPACE: + return "NAMESPACE"; + + case EXIT_NO_NEW_PRIVILEGES: + return "NO_NEW_PRIVILEGES"; + + case EXIT_SECCOMP: + return "SECCOMP"; + + case EXIT_SELINUX_CONTEXT: + return "SELINUX_CONTEXT"; + + case EXIT_PERSONALITY: + return "PERSONALITY"; + + case EXIT_APPARMOR_PROFILE: + return "APPARMOR"; + + case EXIT_ADDRESS_FAMILIES: + return "ADDRESS_FAMILIES"; + + case EXIT_RUNTIME_DIRECTORY: + return "RUNTIME_DIRECTORY"; + + case EXIT_CHOWN: + return "CHOWN"; + + case EXIT_MAKE_STARTER: + return "MAKE_STARTER"; + + case EXIT_SMACK_PROCESS_LABEL: + return "SMACK_PROCESS_LABEL"; + } + } + + if (level == EXIT_STATUS_LSB) { + switch ((int) status) { + + case EXIT_INVALIDARGUMENT: + return "INVALIDARGUMENT"; + + case EXIT_NOTIMPLEMENTED: + return "NOTIMPLEMENTED"; + + case EXIT_NOPERMISSION: + return "NOPERMISSION"; + + case EXIT_NOTINSTALLED: + return "NOTINSTALLED"; + + case EXIT_NOTCONFIGURED: + return "NOTCONFIGURED"; + + case EXIT_NOTRUNNING: + return "NOTRUNNING"; + } + } + + return NULL; +} + + +bool is_clean_exit(int code, int status, ExitStatusSet *success_status) { + + if (code == CLD_EXITED) + return status == 0 || + (success_status && + set_contains(success_status->status, INT_TO_PTR(status))); + + /* If a daemon does not implement handlers for some of the + * signals that's not considered an unclean shutdown */ + if (code == CLD_KILLED) + return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) || + (success_status && + set_contains(success_status->signal, INT_TO_PTR(status))); + + return false; +} + +bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) { + + if (is_clean_exit(code, status, success_status)) + return true; + + return + code == CLD_EXITED && + IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED); +} + +void exit_status_set_free(ExitStatusSet *x) { + assert(x); + + x->status = set_free(x->status); + x->signal = set_free(x->signal); +} + +bool exit_status_set_is_empty(ExitStatusSet *x) { + if (!x) + return true; + + return set_isempty(x->status) && set_isempty(x->signal); +} + +bool exit_status_set_test(ExitStatusSet *x, int code, int status) { + + if (exit_status_set_is_empty(x)) + return false; + + if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status))) + return true; + + if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status))) + return true; + + return false; +} diff --git a/src/libbasic/src/extract-word.c b/src/libbasic/src/extract-word.c new file mode 100644 index 0000000000..c38a815592 --- /dev/null +++ b/src/libbasic/src/extract-word.c @@ -0,0 +1,298 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/extract-word.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/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) { + bool eight_bit = false; + char32_t u; + + 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/libbasic/src/fd-util.c b/src/libbasic/src/fd-util.c new file mode 100644 index 0000000000..1ac711e66d --- /dev/null +++ b/src/libbasic/src/fd-util.c @@ -0,0 +1,380 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/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; +} + +void stdio_unset_cloexec(void) { + fd_cloexec(STDIN_FILENO, false); + fd_cloexec(STDOUT_FILENO, false); + fd_cloexec(STDERR_FILENO, false); +} + +_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_or_backup_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; +} + +int fd_get_path(int fd, char **ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + int r; + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + r = readlink_malloc(procfs_path, ret); + + if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ + return -EBADF; + + return r; +} diff --git a/src/libbasic/src/fileio-label.c b/src/libbasic/src/fileio-label.c new file mode 100644 index 0000000000..3e0cab1b38 --- /dev/null +++ b/src/libbasic/src/fileio-label.c @@ -0,0 +1,68 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2010 Harald Hoyer + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/fileio-label.h" +#include "basic/fileio.h" +#include "basic/selinux-util.h" + +int write_string_file_atomic_label(const char *fn, const char *line) { + int r; + + r = mac_selinux_create_file_prepare(fn, S_IFREG); + if (r < 0) + return r; + + r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + + mac_selinux_create_file_clear(); + + return r; +} + +int write_env_file_label(const char *fname, char **l) { + int r; + + r = mac_selinux_create_file_prepare(fname, S_IFREG); + if (r < 0) + return r; + + r = write_env_file(fname, l); + + mac_selinux_create_file_clear(); + + return r; +} + +int fopen_temporary_label(const char *target, + const char *path, FILE **f, char **temp_path) { + int r; + + r = mac_selinux_create_file_prepare(target, S_IFREG); + if (r < 0) + return r; + + r = fopen_temporary(path, f, temp_path); + + mac_selinux_create_file_clear(); + + return r; +} diff --git a/src/libbasic/src/fileio.c b/src/libbasic/src/fileio.c new file mode 100644 index 0000000000..02a9a02be8 --- /dev/null +++ b/src/libbasic/src/fileio.c @@ -0,0 +1,1398 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hexdecoct.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/random-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/utf8.h" + +int write_string_stream(FILE *f, const char *line, bool enforce_newline) { + + assert(f); + assert(line); + + fputs(line, f); + if (enforce_newline && !endswith(line, "\n")) + fputc('\n', f); + + return fflush_and_check(f); +} + +static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + int r; + + assert(fn); + assert(line); + + r = fopen_temporary(fn, &f, &p); + if (r < 0) + return r; + + (void) fchmod_umask(fileno(f), 0644); + + r = write_string_stream(f, line, enforce_newline); + if (r >= 0) { + if (rename(p, fn) < 0) + r = -errno; + } + + if (r < 0) + (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); + + if (flags & WRITE_STRING_FILE_ATOMIC) { + assert(flags & WRITE_STRING_FILE_CREATE); + + 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) { + 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) { + r = -errno; + goto fail; + } + + f = fdopen(fd, "we"); + if (!f) { + r = -errno; + safe_close(fd); + goto fail; + } + } + + 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) { + _cleanup_fclose_ FILE *f = NULL; + char t[LINE_MAX], *c; + + assert(fn); + assert(line); + + f = fopen(fn, "re"); + if (!f) + return -errno; + + if (!fgets(t, sizeof(t), f)) { + + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + t[0] = 0; + } + + c = strdup(t); + if (!c) + return -ENOMEM; + truncate_nl(c); + + *line = c; + return 0; +} + +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; + + assert(fn); + assert(blob); + + l = strlen(blob); + + 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) { + size_t n, l; + _cleanup_free_ char *buf = NULL; + struct stat st; + + assert(f); + assert(contents); + + if (fstat(fileno(f), &st) < 0) + return -errno; + + n = LINE_MAX; + + if (S_ISREG(st.st_mode)) { + + /* Safety check */ + if (st.st_size > 4*1024*1024) + return -E2BIG; + + /* Start with the right file size, but be prepared for + * files from /proc which generally report a file size + * of 0 */ + if (st.st_size > 0) + n = st.st_size; + } + + l = 0; + for (;;) { + char *t; + size_t k; + + t = realloc(buf, n+1); + if (!t) + return -ENOMEM; + + buf = t; + k = fread(buf + l, 1, n - l, f); + + if (k <= 0) { + if (ferror(f)) + return -errno; + + break; + } + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) + return -E2BIG; + } + + buf[l] = 0; + *contents = buf; + buf = NULL; /* do not free */ + + if (size) + *size = l; + + return 0; +} + +int read_full_file(const char *fn, char **contents, size_t *size) { + _cleanup_fclose_ FILE *f = NULL; + + assert(fn); + assert(contents); + + f = fopen(fn, "re"); + if (!f) + return -errno; + + return read_full_stream(f, contents, size); +} + +static int parse_env_file_internal( + FILE *f, + const char *fname, + const char *newline, + int (*push) (const char *filename, unsigned line, + const char *key, char *value, void *userdata, int *n_pushed), + void *userdata, + int *n_pushed) { + + _cleanup_free_ char *contents = NULL, *key = NULL; + size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; + char *p, *value = NULL; + int r; + unsigned line = 1; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE, + COMMENT, + COMMENT_ESCAPE + } state = PRE_KEY; + + assert(newline); + + if (f) + r = read_full_stream(f, &contents, NULL); + else + r = read_full_file(fname, &contents, NULL); + if (r < 0) + return r; + + for (p = contents; *p; p++) { + char c = *p; + + switch (state) { + + case PRE_KEY: + if (strchr(COMMENTS, c)) + state = COMMENT; + else if (!strchr(WHITESPACE, c)) { + state = KEY; + last_key_whitespace = (size_t) -1; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { + r = -ENOMEM; + goto fail; + } + + key[n_key++] = c; + } + break; + + case KEY: + if (strchr(newline, c)) { + state = PRE_KEY; + line++; + n_key = 0; + } else if (c == '=') { + state = PRE_VALUE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_key_whitespace = (size_t) -1; + else if (last_key_whitespace == (size_t) -1) + last_key_whitespace = n_key; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { + r = -ENOMEM; + goto fail; + } + + key[n_key++] = c; + } + + break; + + case PRE_VALUE: + if (strchr(newline, c)) { + state = PRE_KEY; + line++; + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + goto fail; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\'') + state = SINGLE_QUOTE_VALUE; + else if (c == '\"') + state = DOUBLE_QUOTE_VALUE; + else if (c == '\\') + state = VALUE_ESCAPE; + else if (!strchr(WHITESPACE, c)) { + state = VALUE; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + + break; + + case VALUE: + if (strchr(newline, c)) { + state = PRE_KEY; + line++; + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* Chomp off trailing whitespace from value */ + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + goto fail; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\\') { + state = VALUE_ESCAPE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_value_whitespace = (size_t) -1; + else if (last_value_whitespace == (size_t) -1) + last_value_whitespace = n_value; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + + break; + + case VALUE_ESCAPE: + state = VALUE; + + if (!strchr(newline, c)) { + /* Escaped newlines we eat up entirely */ + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + break; + + case SINGLE_QUOTE_VALUE: + if (c == '\'') + state = PRE_VALUE; + else if (c == '\\') + state = SINGLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + + break; + + case SINGLE_QUOTE_VALUE_ESCAPE: + state = SINGLE_QUOTE_VALUE; + + if (!strchr(newline, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + break; + + case DOUBLE_QUOTE_VALUE: + if (c == '\"') + state = PRE_VALUE; + else if (c == '\\') + state = DOUBLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + + break; + + case DOUBLE_QUOTE_VALUE_ESCAPE: + state = DOUBLE_QUOTE_VALUE; + + if (!strchr(newline, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { + r = -ENOMEM; + goto fail; + } + + value[n_value++] = c; + } + break; + + case COMMENT: + if (c == '\\') + state = COMMENT_ESCAPE; + else if (strchr(newline, c)) { + state = PRE_KEY; + line++; + } + break; + + case COMMENT_ESCAPE: + state = COMMENT; + break; + } + } + + if (state == PRE_VALUE || + state == VALUE || + state == VALUE_ESCAPE || + state == SINGLE_QUOTE_VALUE || + state == SINGLE_QUOTE_VALUE_ESCAPE || + state == DOUBLE_QUOTE_VALUE || + state == DOUBLE_QUOTE_VALUE_ESCAPE) { + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + if (state == VALUE) + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + goto fail; + } + + return 0; + +fail: + free(value); + return r; +} + +static int parse_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + const char *k; + va_list aq, *ap = userdata; + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(key); + log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); + return -EINVAL; + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(value); + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); + return -EINVAL; + } + + va_copy(aq, *ap); + + while ((k = va_arg(aq, const char *))) { + char **v; + + v = va_arg(aq, char **); + + if (streq(key, k)) { + va_end(aq); + free(*v); + *v = value; + + if (n_pushed) + (*n_pushed)++; + + return 1; + } + } + + va_end(aq); + free(value); + + return 0; +} + +int parse_env_file( + const char *fname, + const char *newline, ...) { + + va_list ap; + int r, n_pushed = 0; + + if (!newline) + newline = NEWLINE; + + va_start(ap, newline); + r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed); + va_end(ap); + + return r < 0 ? r : n_pushed; +} + +static int load_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + char *p; + int r; + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *t = utf8_escape_invalid(key); + + log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); + return -EINVAL; + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *t = utf8_escape_invalid(value); + + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); + return -EINVAL; + } + + p = strjoin(key, "=", strempty(value), NULL); + if (!p) + return -ENOMEM; + + r = strv_consume(m, p); + if (r < 0) + return r; + + if (n_pushed) + (*n_pushed)++; + + free(value); + return 0; +} + +int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { + char **m = NULL; + int r; + + if (!newline) + newline = NEWLINE; + + r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int load_env_file_push_pairs( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + int r; + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *t = utf8_escape_invalid(key); + + log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); + return -EINVAL; + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *t = utf8_escape_invalid(value); + + log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); + return -EINVAL; + } + + r = strv_extend(m, key); + if (r < 0) + return -ENOMEM; + + if (!value) { + r = strv_extend(m, ""); + if (r < 0) + return -ENOMEM; + } else { + r = strv_push(m, value); + if (r < 0) + return r; + } + + if (n_pushed) + (*n_pushed)++; + + return 0; +} + +int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { + char **m = NULL; + int r; + + if (!newline) + newline = NEWLINE; + + r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static void write_env_var(FILE *f, const char *v) { + const char *p; + + p = strchr(v, '='); + if (!p) { + /* Fallback */ + fputs(v, f); + fputc('\n', f); + return; + } + + p++; + fwrite(v, 1, p-v, f); + + if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { + fputc('\"', f); + + for (; *p; p++) { + if (strchr(SHELL_NEED_ESCAPE, *p)) + fputc('\\', f); + + fputc(*p, f); + } + + fputc('\"', f); + } else + fputs(p, f); + + fputc('\n', f); +} + +int write_env_file(const char *fname, char **l) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + char **i; + int r; + + assert(fname); + + r = fopen_temporary(fname, &f, &p); + if (r < 0) + return r; + + fchmod_umask(fileno(f), 0644); + + STRV_FOREACH(i, l) + write_env_var(f, *i); + + r = fflush_and_check(f); + if (r >= 0) { + if (rename(p, fname) >= 0) + return 0; + + r = -errno; + } + + unlink(p); + return r; +} + +int executable_is_script(const char *path, char **interpreter) { + int r; + _cleanup_free_ char *line = NULL; + int len; + char *ans; + + assert(path); + + r = read_one_line_file(path, &line); + if (r < 0) + return r; + + if (!startswith(line, "#!")) + return 0; + + ans = strstrip(line + 2); + len = strcspn(ans, " \t"); + + if (len == 0) + return 0; + + ans = strndup(ans, len); + if (!ans) + return -ENOMEM; + + *interpreter = ans; + return 1; +} + +/** + * Retrieve one field from a file like /proc/self/status. pattern + * 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_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); + + r = read_full_file(filename, &status, NULL); + if (r < 0) + return r; + + 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++; + + if (*t) { + t += strspn(t, " \t"); + + /* Also skip zeros, because when this is used for + * capabilities, we don't want the zeros. This way the + * same capability set always maps to the same string, + * irrespective of the total capability set size. For + * other numbers it shouldn't matter. */ + t += strspn(t, "0"); + /* Back off one char if there's nothing but whitespace + and zeros */ + if (!*t || isspace(*t)) + t--; + } + + len = strcspn(t, terminator); + + f = strndup(t, len); + if (!f) + return -ENOMEM; + + *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 mkostemp() but is subject to umask(). */ +int mkostemp_safe(char *pattern, int flags) { + _cleanup_umask_ mode_t u = 0; + int fd; + + assert(pattern); + + u = umask(077); + + fd = mkostemp(pattern, flags); + if (fd < 0) + return -errno; + + 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/.#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/.#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/.#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; +} + +int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { + int r; + + assert(s); + + /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter + * when specified shall initially point to a boolean variable initialized to false. It is set to true after the + * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each + * element, but not before the first one. */ + + if (!f) + f = stdout; + + if (space) { + if (!separator) + separator = " "; + + if (*space) { + r = fputs(separator, f); + if (r < 0) + return r; + } + + *space = true; + } + + return fputs(s, f); +} + +int open_tmpfile_unlinkable(const char *directory, int flags) { + char *p; + int fd; + + if (!directory) + directory = "/tmp"; + + /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ + +#ifdef O_TMPFILE + /* Try O_TMPFILE first, if it is supported */ + fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); + if (fd >= 0) + return fd; +#endif + + /* Fall back to unguessable name + unlinking */ + p = strjoina(directory, "/systemd-tmp-XXXXXX"); + + fd = mkostemp_safe(p, flags); + if (fd < 0) + return fd; + + (void) unlink(p); + + return fd; +} + +int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { + _cleanup_free_ char *tmp = NULL; + int r, fd; + + assert(target); + assert(ret_path); + + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ + assert((flags & O_EXCL) == 0); + + /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in + * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in + * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ + +#ifdef O_TMPFILE + { + _cleanup_free_ char *dn = NULL; + + dn = dirname_malloc(target); + if (!dn) + return -ENOMEM; + + fd = open(dn, O_TMPFILE|flags, 0640); + if (fd >= 0) { + *ret_path = NULL; + return fd; + } + + log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); + } +#endif + + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return r; + + fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); + if (fd < 0) + return -errno; + + *ret_path = tmp; + tmp = NULL; + + return fd; +} + +int link_tmpfile(int fd, const char *path, const char *target) { + + assert(fd >= 0); + assert(target); + + /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd + * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported + * on the directory, and renameat2() is used instead. + * + * Note that in both cases we will not replace existing files. This is because linkat() does not support this + * operation currently (renameat2() does), and there is no nice way to emulate this. */ + + if (path) { + if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) + return -errno; + } else { + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + + xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); + + if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + return -errno; + } + + return 0; +} + +int read_nul_string(FILE *f, char **ret) { + _cleanup_free_ char *x = NULL; + size_t allocated = 0, n = 0; + + assert(f); + assert(ret); + + /* Reads a NUL-terminated string from the specified file. */ + + for (;;) { + int c; + + if (!GREEDY_REALLOC(x, allocated, n+2)) + return -ENOMEM; + + c = fgetc(f); + if (c == 0) /* Terminate at NUL byte */ + break; + if (c == EOF) { + if (ferror(f)) + return -errno; + break; /* Terminate at EOF */ + } + + x[n++] = (char) c; + } + + if (x) + x[n] = 0; + else { + x = new0(char, 1); + if (!x) + return -ENOMEM; + } + + *ret = x; + x = NULL; + + return 0; +} diff --git a/src/libbasic/src/fs-util.c b/src/libbasic/src/fs-util.c new file mode 100644 index 0000000000..cd56843f09 --- /dev/null +++ b/src/libbasic/src/fs-util.c @@ -0,0 +1,539 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/user-util.h" +#include "basic/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_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 == MODE_INVALID) ? 0644 : mode); + 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; +} + +int var_tmp(char **ret) { + const char *tmp_dir = NULL; + const char *env_tmp_dir = NULL; + char *c = NULL; + int r; + + assert(ret); + + env_tmp_dir = getenv("TMPDIR"); + if (env_tmp_dir != NULL) { + r = is_dir(env_tmp_dir, true); + if (r < 0 && r != -ENOENT) + return r; + if (r > 0) + tmp_dir = env_tmp_dir; + } + + if (!tmp_dir) + tmp_dir = "/var/tmp"; + + c = strdup(tmp_dir); + if (!c) + return -ENOMEM; + *ret = c; + + return 0; +} + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + xsprintf(path, "/proc/self/fd/%i", what); + + r = inotify_add_watch(fd, path, mask); + if (r < 0) + return -errno; + + return r; +} diff --git a/src/libbasic/src/glob-util.c b/src/libbasic/src/glob-util.c new file mode 100644 index 0000000000..8762a59bd9 --- /dev/null +++ b/src/libbasic/src/glob-util.c @@ -0,0 +1,70 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/glob-util.h" +#include "basic/macro.h" +#include "basic/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/libbasic/src/gunicode.c b/src/libbasic/src/gunicode.c new file mode 100644 index 0000000000..4862021fbb --- /dev/null +++ b/src/libbasic/src/gunicode.c @@ -0,0 +1,112 @@ +/* gunicode.c - Unicode manipulation functions + * + * Copyright (C) 1999, 2000 Tom Tromey + * Copyright 2000, 2005 Red Hat, Inc. + */ + +#include + +#include "basic/gunicode.h" + +#define unichar uint32_t + +/** + * g_utf8_prev_char: + * @p: a pointer to a position within a UTF-8 encoded string + * + * Finds the previous UTF-8 character in the string before @p. + * + * @p does not have to be at the beginning of a UTF-8 character. No check + * is made to see if the character found is actually valid other than + * it starts with an appropriate byte. If @p might be the first + * character of the string, you must use g_utf8_find_prev_char() instead. + * + * Return value: a pointer to the found character. + **/ +char * +utf8_prev_char (const char *p) +{ + while (1) + { + p--; + if ((*p & 0xc0) != 0x80) + return (char *)p; + } +} + +struct Interval +{ + unichar start, end; +}; + +static int +interval_compare (const void *key, const void *elt) +{ + unichar c = (unichar) (long) (key); + struct Interval *interval = (struct Interval *)elt; + + if (c < interval->start) + return -1; + if (c > interval->end) + return +1; + + return 0; +} + +/* + * NOTE: + * + * The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are + * generated from the Unicode Character Database's file + * extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py + * in this way: + * + * ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt + * + * Last update for Unicode 6.0. + */ + +/** + * g_unichar_iswide: + * @c: a Unicode character + * + * Determines if a character is typically rendered in a double-width + * cell. + * + * Return value: %TRUE if the character is wide + **/ +bool +unichar_iswide (unichar c) +{ + /* See NOTE earlier for how to update this table. */ + static const struct Interval wide[] = { + {0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, + {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096}, + {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, + {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE}, + {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, + {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, + {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, + {0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A}, + {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, + {0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */ + {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, + }; + + if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0], + interval_compare)) + return true; + + return false; +} + +const char utf8_skip_data[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; diff --git a/src/libbasic/src/hash-funcs.c b/src/libbasic/src/hash-funcs.c new file mode 100644 index 0000000000..e82a342063 --- /dev/null +++ b/src/libbasic/src/hash-funcs.c @@ -0,0 +1,81 @@ +/*** + 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 . +***/ + +#include "basic/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/libbasic/src/hashmap.c b/src/libbasic/src/hashmap.c new file mode 100644 index 0000000000..cfd1c14878 --- /dev/null +++ b/src/libbasic/src/hashmap.c @@ -0,0 +1,1829 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/mempool.h" +#include "basic/process-util.h" +#include "basic/random-util.h" +#include "basic/set.h" +#include "basic/siphash24.h" +#include "basic/strv.h" +#include "basic/util.h" + +#ifdef ENABLE_DEBUG_HASHMAP +#include + +#include "basic/list.h" +#endif + +/* + * Implementation of hashmaps. + * Addressing: open + * - uses less RAM compared to closed addressing (chaining), because + * our entries are small (especially in Sets, which tend to contain + * the majority of entries in systemd). + * Collision resolution: Robin Hood + * - tends to equalize displacement of entries from their optimal buckets. + * Probe sequence: linear + * - though theoretically worse than random probing/uniform hashing/double + * hashing, it is good for cache locality. + * + * References: + * Celis, P. 1986. Robin Hood Hashing. + * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. + * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf + * - The results are derived for random probing. Suggests deletion with + * tombstones and two mean-centered search methods. None of that works + * well for linear probing. + * + * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. + * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. + * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 + * http://www.math.uu.se/~svante/papers/sj157.pdf + * - Applies to Robin Hood with linear probing. Contains remarks on + * the unsuitability of mean-centered search with linear probing. + * + * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. + * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. + * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 + * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes + * in a successful search), and Janson writes about displacement. C = d + 1. + * + * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. + * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ + * - Explanation of backward shift deletion with pictures. + * + * Khuong, P. 2013. The Other Robin Hood Hashing. + * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ + * - Short summary of random vs. linear probing, and tombstones vs. backward shift. + */ + +/* + * XXX Ideas for improvement: + * For unordered hashmaps, randomize iteration order, similarly to Perl: + * http://blog.booking.com/hardening-perls-hash-function.html + */ + +/* INV_KEEP_FREE = 1 / (1 - max_load_factor) + * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ +#define INV_KEEP_FREE 5U + +/* Fields common to entries of all hashmap/set types */ +struct hashmap_base_entry { + const void *key; +}; + +/* Entry types for specific hashmap/set types + * hashmap_base_entry must be at the beginning of each entry struct. */ + +struct plain_hashmap_entry { + struct hashmap_base_entry b; + void *value; +}; + +struct ordered_hashmap_entry { + struct plain_hashmap_entry p; + unsigned iterate_next, iterate_previous; +}; + +struct set_entry { + struct hashmap_base_entry b; +}; + +/* In several functions it is advantageous to have the hash table extended + * virtually by a couple of additional buckets. We reserve special index values + * for these "swap" buckets. */ +#define _IDX_SWAP_BEGIN (UINT_MAX - 3) +#define IDX_PUT (_IDX_SWAP_BEGIN + 0) +#define IDX_TMP (_IDX_SWAP_BEGIN + 1) +#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) + +#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ +#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ + +assert_cc(IDX_FIRST == _IDX_SWAP_END); +assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); + +/* Storage space for the "swap" buckets. + * All entry types can fit into a ordered_hashmap_entry. */ +struct swap_entries { + struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; +}; + +/* Distance from Initial Bucket */ +typedef uint8_t dib_raw_t; +#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ +#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ +#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ +#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ + +#define DIB_FREE UINT_MAX + +#ifdef ENABLE_DEBUG_HASHMAP +struct hashmap_debug_info { + LIST_FIELDS(struct hashmap_debug_info, debug_list); + unsigned max_entries; /* high watermark of n_entries */ + + /* who allocated this hashmap */ + int line; + const char *file; + const char *func; + + /* fields to detect modification while iterating */ + unsigned put_count; /* counts puts into the hashmap */ + unsigned rem_count; /* counts removals from hashmap */ + unsigned last_rem_idx; /* remembers last removal index */ +}; + +/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ +static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); +static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; + +#else /* !ENABLE_DEBUG_HASHMAP */ +#define HASHMAP_DEBUG_FIELDS +#endif /* ENABLE_DEBUG_HASHMAP */ + +enum HashmapType { + HASHMAP_TYPE_PLAIN, + HASHMAP_TYPE_ORDERED, + HASHMAP_TYPE_SET, + _HASHMAP_TYPE_MAX +}; + +struct _packed_ indirect_storage { + void *storage; /* where buckets and DIBs are stored */ + uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ + + unsigned n_entries; /* number of stored entries */ + unsigned n_buckets; /* number of buckets */ + + unsigned idx_lowest_entry; /* Index below which all buckets are free. + Makes "while(hashmap_steal_first())" loops + O(n) instead of O(n^2) for unordered hashmaps. */ + uint8_t _pad[3]; /* padding for the whole HashmapBase */ + /* The bitfields in HashmapBase complete the alignment of the whole thing. */ +}; + +struct direct_storage { + /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. + * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, + * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ + uint8_t storage[sizeof(struct indirect_storage)]; +}; + +#define DIRECT_BUCKETS(entry_t) \ + (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) + +/* We should be able to store at least one entry directly. */ +assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); + +/* We have 3 bits for n_direct_entries. */ +assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); + +/* Hashmaps with directly stored entries all use this shared hash key. + * It's no big deal if the key is guessed, because there can be only + * a handful of directly stored entries in a hashmap. When a hashmap + * outgrows direct storage, it gets its own key for indirect storage. */ +static uint8_t shared_hash_key[HASH_KEY_SIZE]; +static bool shared_hash_key_initialized; + +/* Fields that all hashmap/set types must have */ +struct HashmapBase { + const struct hash_ops *hash_ops; /* hash and compare ops to use */ + + union _packed_ { + struct indirect_storage indirect; /* if has_indirect */ + struct direct_storage direct; /* if !has_indirect */ + }; + + enum HashmapType type:2; /* HASHMAP_TYPE_* */ + bool has_indirect:1; /* whether indirect storage is used */ + unsigned n_direct_entries:3; /* Number of entries in direct storage. + * Only valid if !has_indirect. */ + bool from_pool:1; /* whether was allocated from mempool */ + HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ +}; + +/* Specific hash types + * HashmapBase must be at the beginning of each hashmap struct. */ + +struct Hashmap { + struct HashmapBase b; +}; + +struct OrderedHashmap { + struct HashmapBase b; + unsigned iterate_list_head, iterate_list_tail; +}; + +struct Set { + struct HashmapBase b; +}; + +DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); +DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); +/* No need for a separate Set pool */ +assert_cc(sizeof(Hashmap) == sizeof(Set)); + +struct hashmap_type_info { + size_t head_size; + size_t entry_size; + struct mempool *mempool; + unsigned n_direct_buckets; +}; + +static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { + [HASHMAP_TYPE_PLAIN] = { + .head_size = sizeof(Hashmap), + .entry_size = sizeof(struct plain_hashmap_entry), + .mempool = &hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), + }, + [HASHMAP_TYPE_ORDERED] = { + .head_size = sizeof(OrderedHashmap), + .entry_size = sizeof(struct ordered_hashmap_entry), + .mempool = &ordered_hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), + }, + [HASHMAP_TYPE_SET] = { + .head_size = sizeof(Set), + .entry_size = sizeof(struct set_entry), + .mempool = &hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), + }, +}; + +static unsigned n_buckets(HashmapBase *h) { + return h->has_indirect ? h->indirect.n_buckets + : hashmap_type_info[h->type].n_direct_buckets; +} + +static unsigned n_entries(HashmapBase *h) { + return h->has_indirect ? h->indirect.n_entries + : h->n_direct_entries; +} + +static void n_entries_inc(HashmapBase *h) { + if (h->has_indirect) + h->indirect.n_entries++; + else + h->n_direct_entries++; +} + +static void n_entries_dec(HashmapBase *h) { + if (h->has_indirect) + h->indirect.n_entries--; + else + h->n_direct_entries--; +} + +static void *storage_ptr(HashmapBase *h) { + return h->has_indirect ? h->indirect.storage + : h->direct.storage; +} + +static uint8_t *hash_key(HashmapBase *h) { + return h->has_indirect ? h->indirect.hash_key + : shared_hash_key; +} + +static unsigned base_bucket_hash(HashmapBase *h, const void *p) { + 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) + +static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { + static uint8_t current[HASH_KEY_SIZE]; + static bool current_initialized = false; + + /* Returns a hash function key to use. In order to keep things + * fast we will not generate a new key each time we allocate a + * new hash table. Instead, we'll just reuse the most recently + * generated one, except if we never generated one or when we + * are rehashing an entire hash table because we reached a + * fill level */ + + if (!current_initialized || !reuse_is_ok) { + random_bytes(current, sizeof(current)); + current_initialized = true; + } + + memcpy(hash_key, current, sizeof(current)); +} + +static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { + return (struct hashmap_base_entry*) + ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); +} + +static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { + return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); +} + +static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { + return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); +} + +static struct set_entry *set_bucket_at(Set *h, unsigned idx) { + return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); +} + +static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { + return &swap->e[idx - _IDX_SWAP_BEGIN]; +} + +/* Returns a pointer to the bucket at index idx. + * Understands real indexes and swap indexes, hence "_virtual". */ +static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, + unsigned idx) { + if (idx < _IDX_SWAP_BEGIN) + return bucket_at(h, idx); + + if (idx < _IDX_SWAP_END) + return &bucket_at_swap(swap, idx)->p.b; + + assert_not_reached("Invalid index"); +} + +static dib_raw_t *dib_raw_ptr(HashmapBase *h) { + return (dib_raw_t*) + ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); +} + +static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { + return idx >= from ? idx - from + : n_buckets(h) + idx - from; +} + +static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { + unsigned initial_bucket; + + if (raw_dib == DIB_RAW_FREE) + return DIB_FREE; + + if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) + return raw_dib; + + /* + * Having an overflow DIB value is very unlikely. The hash function + * would have to be bad. For example, in a table of size 2^24 filled + * to load factor 0.9 the maximum observed DIB is only about 60. + * In theory (assuming I used Maxima correctly), for an infinite size + * hash table with load factor 0.8 the probability of a given entry + * having DIB > 40 is 1.9e-8. + * This returns the correct DIB value by recomputing the hash value in + * the unlikely case. XXX Hitting this case could be a hint to rehash. + */ + initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); + return bucket_distance(h, idx, initial_bucket); +} + +static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { + dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; +} + +static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { + dib_raw_t *dibs; + + dibs = dib_raw_ptr(h); + + for ( ; idx < n_buckets(h); idx++) + if (dibs[idx] != DIB_RAW_FREE) + return idx; + + return IDX_NIL; +} + +static void bucket_mark_free(HashmapBase *h, unsigned idx) { + memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); + bucket_set_dib(h, idx, DIB_FREE); +} + +static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, + unsigned from, unsigned to) { + struct hashmap_base_entry *e_from, *e_to; + + assert(from != to); + + e_from = bucket_at_virtual(h, swap, from); + e_to = bucket_at_virtual(h, swap, to); + + memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + struct ordered_hashmap_entry *le, *le_to; + + le_to = (struct ordered_hashmap_entry*) e_to; + + if (le_to->iterate_next != IDX_NIL) { + le = (struct ordered_hashmap_entry*) + bucket_at_virtual(h, swap, le_to->iterate_next); + le->iterate_previous = to; + } + + if (le_to->iterate_previous != IDX_NIL) { + le = (struct ordered_hashmap_entry*) + bucket_at_virtual(h, swap, le_to->iterate_previous); + le->iterate_next = to; + } + + if (lh->iterate_list_head == from) + lh->iterate_list_head = to; + if (lh->iterate_list_tail == from) + lh->iterate_list_tail = to; + } +} + +static unsigned next_idx(HashmapBase *h, unsigned idx) { + return (idx + 1U) % n_buckets(h); +} + +static unsigned prev_idx(HashmapBase *h, unsigned idx) { + return (n_buckets(h) + idx - 1U) % n_buckets(h); +} + +static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { + switch (h->type) { + + case HASHMAP_TYPE_PLAIN: + case HASHMAP_TYPE_ORDERED: + return ((struct plain_hashmap_entry*)e)->value; + + case HASHMAP_TYPE_SET: + return (void*) e->key; + + default: + assert_not_reached("Unknown hashmap type"); + } +} + +static void base_remove_entry(HashmapBase *h, unsigned idx) { + unsigned left, right, prev, dib; + dib_raw_t raw_dib, *dibs; + + dibs = dib_raw_ptr(h); + assert(dibs[idx] != DIB_RAW_FREE); + +#ifdef ENABLE_DEBUG_HASHMAP + h->debug.rem_count++; + h->debug.last_rem_idx = idx; +#endif + + left = idx; + /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ + for (right = next_idx(h, left); ; right = next_idx(h, right)) { + raw_dib = dibs[right]; + if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) + break; + + /* The buckets are not supposed to be all occupied and with DIB > 0. + * That would mean we could make everyone better off by shifting them + * backward. This scenario is impossible. */ + assert(left != right); + } + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); + + if (le->iterate_next != IDX_NIL) + ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; + else + lh->iterate_list_tail = le->iterate_previous; + + if (le->iterate_previous != IDX_NIL) + ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; + else + lh->iterate_list_head = le->iterate_next; + } + + /* Now shift all buckets in the interval (left, right) one step backwards */ + for (prev = left, left = next_idx(h, left); left != right; + prev = left, left = next_idx(h, left)) { + dib = bucket_calculate_dib(h, left, dibs[left]); + assert(dib != 0); + bucket_move_entry(h, NULL, left, prev); + bucket_set_dib(h, prev, dib - 1); + } + + bucket_mark_free(h, prev); + n_entries_dec(h); +} +#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) + +static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { + struct ordered_hashmap_entry *e; + unsigned idx; + + assert(h); + assert(i); + + if (i->idx == IDX_NIL) + goto at_end; + + if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) + goto at_end; + + if (i->idx == IDX_FIRST) { + idx = h->iterate_list_head; + e = ordered_bucket_at(h, idx); + } else { + idx = i->idx; + e = ordered_bucket_at(h, idx); + /* + * We allow removing the current entry while iterating, but removal may cause + * a backward shift. The next entry may thus move one bucket to the left. + * To detect when it happens, we remember the key pointer of the entry we were + * going to iterate next. If it does not match, there was a backward shift. + */ + if (e->p.b.key != i->next_key) { + idx = prev_idx(HASHMAP_BASE(h), idx); + e = ordered_bucket_at(h, idx); + } + assert(e->p.b.key == i->next_key); + } + +#ifdef ENABLE_DEBUG_HASHMAP + i->prev_idx = idx; +#endif + + if (e->iterate_next != IDX_NIL) { + struct ordered_hashmap_entry *n; + i->idx = e->iterate_next; + n = ordered_bucket_at(h, i->idx); + i->next_key = n->p.b.key; + } else + i->idx = IDX_NIL; + + return idx; + +at_end: + i->idx = IDX_NIL; + return IDX_NIL; +} + +static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { + unsigned idx; + + assert(h); + assert(i); + + if (i->idx == IDX_NIL) + goto at_end; + + if (i->idx == IDX_FIRST) { + /* fast forward to the first occupied bucket */ + if (h->has_indirect) { + i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); + h->indirect.idx_lowest_entry = i->idx; + } else + i->idx = skip_free_buckets(h, 0); + + if (i->idx == IDX_NIL) + goto at_end; + } else { + struct hashmap_base_entry *e; + + assert(i->idx > 0); + + e = bucket_at(h, i->idx); + /* + * We allow removing the current entry while iterating, but removal may cause + * a backward shift. The next entry may thus move one bucket to the left. + * To detect when it happens, we remember the key pointer of the entry we were + * going to iterate next. If it does not match, there was a backward shift. + */ + if (e->key != i->next_key) + e = bucket_at(h, --i->idx); + + assert(e->key == i->next_key); + } + + idx = i->idx; +#ifdef ENABLE_DEBUG_HASHMAP + i->prev_idx = idx; +#endif + + i->idx = skip_free_buckets(h, i->idx + 1); + if (i->idx != IDX_NIL) + i->next_key = bucket_at(h, i->idx)->key; + else + i->idx = IDX_NIL; + + return idx; + +at_end: + i->idx = IDX_NIL; + return IDX_NIL; +} + +static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { + if (!h) { + i->idx = IDX_NIL; + return IDX_NIL; + } + +#ifdef ENABLE_DEBUG_HASHMAP + if (i->idx == IDX_FIRST) { + i->put_count = h->debug.put_count; + i->rem_count = h->debug.rem_count; + } else { + /* While iterating, must not add any new entries */ + assert(i->put_count == h->debug.put_count); + /* ... or remove entries other than the current one */ + assert(i->rem_count == h->debug.rem_count || + (i->rem_count == h->debug.rem_count - 1 && + i->prev_idx == h->debug.last_rem_idx)); + /* Reset our removals counter */ + i->rem_count = h->debug.rem_count; + } +#endif + + return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) + : hashmap_iterate_in_internal_order(h, i); +} + +bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { + struct hashmap_base_entry *e; + void *data; + unsigned idx; + + idx = hashmap_iterate_entry(h, i); + if (idx == IDX_NIL) { + if (value) + *value = NULL; + if (key) + *key = NULL; + + return false; + } + + e = bucket_at(h, idx); + data = entry_value(h, e); + if (value) + *value = data; + if (key) + *key = e->key; + + return true; +} + +bool set_iterate(Set *s, Iterator *i, void **value) { + return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); +} + +#define HASHMAP_FOREACH_IDX(idx, h, i) \ + for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ + (idx != IDX_NIL); \ + (idx) = hashmap_iterate_entry((h), &(i))) + +static void reset_direct_storage(HashmapBase *h) { + const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; + void *p; + + assert(!h->has_indirect); + + p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); + memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); +} + +static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { + HashmapBase *h; + const struct hashmap_type_info *hi = &hashmap_type_info[type]; + bool use_pool; + + use_pool = is_main_thread(); + + h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); + + if (!h) + return NULL; + + h->type = type; + h->from_pool = use_pool; + h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; + + if (type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*)h; + lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; + } + + reset_direct_storage(h); + + if (!shared_hash_key_initialized) { + random_bytes(shared_hash_key, sizeof(shared_hash_key)); + shared_hash_key_initialized= true; + } + +#ifdef ENABLE_DEBUG_HASHMAP + h->debug.func = func; + h->debug.file = file; + h->debug.line = line; + assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); + LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); + assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); +#endif + + return h; +} + +Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +} + +OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +} + +Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +} + +static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, + enum HashmapType type HASHMAP_DEBUG_PARAMS) { + HashmapBase *q; + + assert(h); + + if (*h) + return 0; + + q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); + if (!q) + return -ENOMEM; + + *h = q; + return 0; +} + +int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +} + +int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +} + +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +} + +static void hashmap_free_no_clear(HashmapBase *h) { + assert(!h->has_indirect); + assert(!h->n_direct_entries); + +#ifdef ENABLE_DEBUG_HASHMAP + assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); + LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); + assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); +#endif + + if (h->from_pool) + mempool_free_tile(hashmap_type_info[h->type].mempool, h); + else + free(h); +} + +HashmapBase *internal_hashmap_free(HashmapBase *h) { + + /* Free the hashmap, but nothing in it */ + + if (h) { + internal_hashmap_clear(h); + hashmap_free_no_clear(h); + } + + return NULL; +} + +HashmapBase *internal_hashmap_free_free(HashmapBase *h) { + + /* Free the hashmap and all data objects in it, but not the + * keys */ + + if (h) { + internal_hashmap_clear_free(h); + hashmap_free_no_clear(h); + } + + return NULL; +} + +Hashmap *hashmap_free_free_free(Hashmap *h) { + + /* Free the hashmap and all data and key objects in it */ + + if (h) { + hashmap_clear_free_free(h); + hashmap_free_no_clear(HASHMAP_BASE(h)); + } + + return NULL; +} + +void internal_hashmap_clear(HashmapBase *h) { + if (!h) + return; + + if (h->has_indirect) { + free(h->indirect.storage); + h->has_indirect = false; + } + + h->n_direct_entries = 0; + reset_direct_storage(h); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; + } +} + +void internal_hashmap_clear_free(HashmapBase *h) { + unsigned idx; + + if (!h) + return; + + for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; + idx = skip_free_buckets(h, idx + 1)) + free(entry_value(h, bucket_at(h, idx))); + + internal_hashmap_clear(h); +} + +void hashmap_clear_free_free(Hashmap *h) { + unsigned idx; + + if (!h) + return; + + for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; + idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { + struct plain_hashmap_entry *e = plain_bucket_at(h, idx); + free((void*)e->b.key); + free(e->value); + } + + internal_hashmap_clear(HASHMAP_BASE(h)); +} + +static int resize_buckets(HashmapBase *h, unsigned entries_add); + +/* + * Finds an empty bucket to put an entry into, starting the scan at 'idx'. + * Performs Robin Hood swaps as it goes. The entry to put must be placed + * by the caller into swap slot IDX_PUT. + * If used for in-place resizing, may leave a displaced entry in swap slot + * IDX_PUT. Caller must rehash it next. + * Returns: true if it left a displaced entry to rehash next in IDX_PUT, + * false otherwise. + */ +static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, + struct swap_entries *swap) { + dib_raw_t raw_dib, *dibs; + unsigned dib, distance; + +#ifdef ENABLE_DEBUG_HASHMAP + h->debug.put_count++; +#endif + + dibs = dib_raw_ptr(h); + + for (distance = 0; ; distance++) { + raw_dib = dibs[idx]; + if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { + if (raw_dib == DIB_RAW_REHASH) + bucket_move_entry(h, swap, idx, IDX_TMP); + + if (h->has_indirect && h->indirect.idx_lowest_entry > idx) + h->indirect.idx_lowest_entry = idx; + + bucket_set_dib(h, idx, distance); + bucket_move_entry(h, swap, IDX_PUT, idx); + if (raw_dib == DIB_RAW_REHASH) { + bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); + return true; + } + + return false; + } + + dib = bucket_calculate_dib(h, idx, raw_dib); + + if (dib < distance) { + /* Found a wealthier entry. Go Robin Hood! */ + bucket_set_dib(h, idx, distance); + + /* swap the entries */ + bucket_move_entry(h, swap, idx, IDX_TMP); + bucket_move_entry(h, swap, IDX_PUT, idx); + bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); + + distance = dib; + } + + idx = next_idx(h, idx); + } +} + +/* + * Puts an entry into a hashmap, boldly - no check whether key already exists. + * The caller must place the entry (only its key and value, not link indexes) + * in swap slot IDX_PUT. + * Caller must ensure: the key does not exist yet in the hashmap. + * that resize is not needed if !may_resize. + * Returns: 1 if entry was put successfully. + * -ENOMEM if may_resize==true and resize failed with -ENOMEM. + * Cannot return -ENOMEM if !may_resize. + */ +static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, + struct swap_entries *swap, bool may_resize) { + struct ordered_hashmap_entry *new_entry; + int r; + + assert(idx < n_buckets(h)); + + new_entry = bucket_at_swap(swap, IDX_PUT); + + if (may_resize) { + r = resize_buckets(h, 1); + if (r < 0) + return r; + if (r > 0) + idx = bucket_hash(h, new_entry->p.b.key); + } + assert(n_entries(h) < n_buckets(h)); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + + new_entry->iterate_next = IDX_NIL; + new_entry->iterate_previous = lh->iterate_list_tail; + + if (lh->iterate_list_tail != IDX_NIL) { + struct ordered_hashmap_entry *old_tail; + + old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); + assert(old_tail->iterate_next == IDX_NIL); + old_tail->iterate_next = IDX_PUT; + } + + lh->iterate_list_tail = IDX_PUT; + if (lh->iterate_list_head == IDX_NIL) + lh->iterate_list_head = IDX_PUT; + } + + assert_se(hashmap_put_robin_hood(h, idx, swap) == false); + + n_entries_inc(h); +#ifdef ENABLE_DEBUG_HASHMAP + h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); +#endif + + return 1; +} +#define hashmap_put_boldly(h, idx, swap, may_resize) \ + hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) + +/* + * Returns 0 if resize is not needed. + * 1 if successfully resized. + * -ENOMEM on allocation failure. + */ +static int resize_buckets(HashmapBase *h, unsigned entries_add) { + struct swap_entries swap; + void *new_storage; + dib_raw_t *old_dibs, *new_dibs; + const struct hashmap_type_info *hi; + unsigned idx, optimal_idx; + unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; + uint8_t new_shift; + bool rehash_next; + + assert(h); + + hi = &hashmap_type_info[h->type]; + new_n_entries = n_entries(h) + entries_add; + + /* overflow? */ + if (_unlikely_(new_n_entries < entries_add)) + return -ENOMEM; + + /* For direct storage we allow 100% load, because it's tiny. */ + if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) + return 0; + + /* + * Load factor = n/m = 1 - (1/INV_KEEP_FREE). + * From it follows: m = n + n/(INV_KEEP_FREE - 1) + */ + new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); + /* overflow? */ + if (_unlikely_(new_n_buckets < new_n_entries)) + return -ENOMEM; + + if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) + return -ENOMEM; + + old_n_buckets = n_buckets(h); + + if (_likely_(new_n_buckets <= old_n_buckets)) + return 0; + + new_shift = log2u_round_up(MAX( + new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), + 2 * sizeof(struct direct_storage))); + + /* Realloc storage (buckets and DIB array). */ + new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, + 1U << new_shift); + if (!new_storage) + return -ENOMEM; + + /* Must upgrade direct to indirect storage. */ + if (!h->has_indirect) { + memcpy(new_storage, h->direct.storage, + old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); + h->indirect.n_entries = h->n_direct_entries; + h->indirect.idx_lowest_entry = 0; + h->n_direct_entries = 0; + } + + /* Get a new hash key. If we've just upgraded to indirect storage, + * allow reusing a previously generated key. It's still a different key + * from the shared one that we used for direct storage. */ + get_hash_key(h->indirect.hash_key, !h->has_indirect); + + h->has_indirect = true; + h->indirect.storage = new_storage; + h->indirect.n_buckets = (1U << new_shift) / + (hi->entry_size + sizeof(dib_raw_t)); + + old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); + new_dibs = dib_raw_ptr(h); + + /* + * Move the DIB array to the new place, replacing valid DIB values with + * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. + * Note: Overlap is not possible, because we have at least doubled the + * number of buckets and dib_raw_t is smaller than any entry type. + */ + for (idx = 0; idx < old_n_buckets; idx++) { + assert(old_dibs[idx] != DIB_RAW_REHASH); + new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE + : DIB_RAW_REHASH; + } + + /* Zero the area of newly added entries (including the old DIB area) */ + memzero(bucket_at(h, old_n_buckets), + (n_buckets(h) - old_n_buckets) * hi->entry_size); + + /* The upper half of the new DIB array needs initialization */ + memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, + (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); + + /* Rehash entries that need it */ + n_rehashed = 0; + for (idx = 0; idx < old_n_buckets; idx++) { + if (new_dibs[idx] != DIB_RAW_REHASH) + continue; + + optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); + + /* + * Not much to do if by luck the entry hashes to its current + * location. Just set its DIB. + */ + if (optimal_idx == idx) { + new_dibs[idx] = 0; + n_rehashed++; + continue; + } + + new_dibs[idx] = DIB_RAW_FREE; + bucket_move_entry(h, &swap, idx, IDX_PUT); + /* bucket_move_entry does not clear the source */ + memzero(bucket_at(h, idx), hi->entry_size); + + do { + /* + * Find the new bucket for the current entry. This may make + * another entry homeless and load it into IDX_PUT. + */ + rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); + n_rehashed++; + + /* Did the current entry displace another one? */ + if (rehash_next) + optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); + } while (rehash_next); + } + + assert(n_rehashed == n_entries(h)); + + return 1; +} + +/* + * Finds an entry with a matching key + * Returns: index of the found entry, or IDX_NIL if not found. + */ +static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { + struct hashmap_base_entry *e; + unsigned dib, distance; + dib_raw_t *dibs = dib_raw_ptr(h); + + assert(idx < n_buckets(h)); + + for (distance = 0; ; distance++) { + if (dibs[idx] == DIB_RAW_FREE) + return IDX_NIL; + + dib = bucket_calculate_dib(h, idx, dibs[idx]); + + if (dib < distance) + return IDX_NIL; + if (dib == distance) { + e = bucket_at(h, idx); + if (h->hash_ops->compare(e->key, key) == 0) + return idx; + } + + idx = next_idx(h, idx); + } +} +#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) + +int hashmap_put(Hashmap *h, const void *key, void *value) { + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned hash, idx; + + assert(h); + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx != IDX_NIL) { + e = plain_bucket_at(h, idx); + if (e->value == value) + return 0; + return -EEXIST; + } + + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = key; + e->value = value; + return hashmap_put_boldly(h, hash, &swap, true); +} + +int set_put(Set *s, const void *key) { + struct swap_entries swap; + struct hashmap_base_entry *e; + unsigned hash, idx; + + assert(s); + + hash = bucket_hash(s, key); + idx = bucket_scan(s, hash, key); + if (idx != IDX_NIL) + return 0; + + e = &bucket_at_swap(&swap, IDX_PUT)->p.b; + e->key = key; + return hashmap_put_boldly(s, hash, &swap, true); +} + +int hashmap_replace(Hashmap *h, const void *key, void *value) { + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned hash, idx; + + assert(h); + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx != IDX_NIL) { + e = plain_bucket_at(h, idx); +#ifdef ENABLE_DEBUG_HASHMAP + /* Although the key is equal, the key pointer may have changed, + * and this would break our assumption for iterating. So count + * this operation as incompatible with iteration. */ + if (e->b.key != key) { + h->b.debug.put_count++; + h->b.debug.rem_count++; + h->b.debug.last_rem_idx = idx; + } +#endif + e->b.key = key; + e->value = value; + return 0; + } + + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = key; + e->value = value; + return hashmap_put_boldly(h, hash, &swap, true); +} + +int hashmap_update(Hashmap *h, const void *key, void *value) { + struct plain_hashmap_entry *e; + unsigned hash, idx; + + assert(h); + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return -ENOENT; + + e = plain_bucket_at(h, idx); + e->value = value; + return 0; +} + +void *internal_hashmap_get(HashmapBase *h, const void *key) { + struct hashmap_base_entry *e; + unsigned hash, idx; + + if (!h) + return NULL; + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return NULL; + + e = bucket_at(h, idx); + return entry_value(h, e); +} + +void *hashmap_get2(Hashmap *h, const void *key, void **key2) { + struct plain_hashmap_entry *e; + unsigned hash, idx; + + if (!h) + return NULL; + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return NULL; + + e = plain_bucket_at(h, idx); + if (key2) + *key2 = (void*) e->b.key; + + return e->value; +} + +bool internal_hashmap_contains(HashmapBase *h, const void *key) { + unsigned hash; + + if (!h) + return false; + + hash = bucket_hash(h, key); + return bucket_scan(h, hash, key) != IDX_NIL; +} + +void *internal_hashmap_remove(HashmapBase *h, const void *key) { + struct hashmap_base_entry *e; + unsigned hash, idx; + void *data; + + if (!h) + return NULL; + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return NULL; + + e = bucket_at(h, idx); + data = entry_value(h, e); + remove_entry(h, idx); + + return data; +} + +void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { + struct plain_hashmap_entry *e; + unsigned hash, idx; + void *data; + + if (!h) { + if (rkey) + *rkey = NULL; + return NULL; + } + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) { + if (rkey) + *rkey = NULL; + return NULL; + } + + e = plain_bucket_at(h, idx); + data = e->value; + if (rkey) + *rkey = (void*) e->b.key; + + remove_entry(h, idx); + + return data; +} + +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned old_hash, new_hash, idx; + + if (!h) + return -ENOENT; + + old_hash = bucket_hash(h, old_key); + idx = bucket_scan(h, old_hash, old_key); + if (idx == IDX_NIL) + return -ENOENT; + + new_hash = bucket_hash(h, new_key); + if (bucket_scan(h, new_hash, new_key) != IDX_NIL) + return -EEXIST; + + remove_entry(h, idx); + + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = new_key; + e->value = value; + assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); + + return 0; +} + +int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { + struct swap_entries swap; + struct hashmap_base_entry *e; + unsigned old_hash, new_hash, idx; + + if (!s) + return -ENOENT; + + old_hash = bucket_hash(s, old_key); + idx = bucket_scan(s, old_hash, old_key); + if (idx == IDX_NIL) + return -ENOENT; + + new_hash = bucket_hash(s, new_key); + if (bucket_scan(s, new_hash, new_key) != IDX_NIL) + return -EEXIST; + + remove_entry(s, idx); + + e = &bucket_at_swap(&swap, IDX_PUT)->p.b; + e->key = new_key; + assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); + + return 0; +} + +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned old_hash, new_hash, idx_old, idx_new; + + if (!h) + return -ENOENT; + + old_hash = bucket_hash(h, old_key); + idx_old = bucket_scan(h, old_hash, old_key); + if (idx_old == IDX_NIL) + return -ENOENT; + + old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; + + new_hash = bucket_hash(h, new_key); + idx_new = bucket_scan(h, new_hash, new_key); + if (idx_new != IDX_NIL) + if (idx_old != idx_new) { + remove_entry(h, idx_new); + /* Compensate for a possible backward shift. */ + if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) + idx_old = prev_idx(HASHMAP_BASE(h), idx_old); + assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); + } + + remove_entry(h, idx_old); + + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = new_key; + e->value = value; + assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); + + return 0; +} + +void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { + struct plain_hashmap_entry *e; + unsigned hash, idx; + + if (!h) + return NULL; + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return NULL; + + e = plain_bucket_at(h, idx); + if (e->value != value) + return NULL; + + remove_entry(h, idx); + + return value; +} + +static unsigned find_first_entry(HashmapBase *h) { + Iterator i = ITERATOR_FIRST; + + if (!h || !n_entries(h)) + return IDX_NIL; + + return hashmap_iterate_entry(h, &i); +} + +void *internal_hashmap_first(HashmapBase *h) { + unsigned idx; + + idx = find_first_entry(h); + if (idx == IDX_NIL) + return NULL; + + return entry_value(h, bucket_at(h, idx)); +} + +void *internal_hashmap_first_key(HashmapBase *h) { + struct hashmap_base_entry *e; + unsigned idx; + + idx = find_first_entry(h); + if (idx == IDX_NIL) + return NULL; + + e = bucket_at(h, idx); + return (void*) e->key; +} + +void *internal_hashmap_steal_first(HashmapBase *h) { + struct hashmap_base_entry *e; + void *data; + unsigned idx; + + idx = find_first_entry(h); + if (idx == IDX_NIL) + return NULL; + + e = bucket_at(h, idx); + data = entry_value(h, e); + remove_entry(h, idx); + + return data; +} + +void *internal_hashmap_steal_first_key(HashmapBase *h) { + struct hashmap_base_entry *e; + void *key; + unsigned idx; + + idx = find_first_entry(h); + if (idx == IDX_NIL) + return NULL; + + e = bucket_at(h, idx); + key = (void*) e->key; + remove_entry(h, idx); + + return key; +} + +unsigned internal_hashmap_size(HashmapBase *h) { + + if (!h) + return 0; + + return n_entries(h); +} + +unsigned internal_hashmap_buckets(HashmapBase *h) { + + if (!h) + return 0; + + return n_buckets(h); +} + +int internal_hashmap_merge(Hashmap *h, Hashmap *other) { + Iterator i; + unsigned idx; + + assert(h); + + HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { + struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); + int r; + + r = hashmap_put(h, pe->b.key, pe->value); + if (r < 0 && r != -EEXIST) + return r; + } + + return 0; +} + +int set_merge(Set *s, Set *other) { + Iterator i; + unsigned idx; + + assert(s); + + HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { + struct set_entry *se = set_bucket_at(other, idx); + int r; + + r = set_put(s, se->b.key); + if (r < 0) + return r; + } + + return 0; +} + +int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { + int r; + + assert(h); + + r = resize_buckets(h, entries_add); + if (r < 0) + return r; + + return 0; +} + +/* + * The same as hashmap_merge(), but every new item from other is moved to h. + * Keys already in h are skipped and stay in other. + * Returns: 0 on success. + * -ENOMEM on alloc failure, in which case no move has been done. + */ +int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { + struct swap_entries swap; + struct hashmap_base_entry *e, *n; + Iterator i; + unsigned idx; + int r; + + assert(h); + + if (!other) + return 0; + + assert(other->type == h->type); + + /* + * This reserves buckets for the worst case, where none of other's + * entries are yet present in h. This is preferable to risking + * an allocation failure in the middle of the moving and having to + * rollback or return a partial result. + */ + r = resize_buckets(h, n_entries(other)); + if (r < 0) + return r; + + HASHMAP_FOREACH_IDX(idx, other, i) { + unsigned h_hash; + + e = bucket_at(other, idx); + h_hash = bucket_hash(h, e->key); + if (bucket_scan(h, h_hash, e->key) != IDX_NIL) + continue; + + n = &bucket_at_swap(&swap, IDX_PUT)->p.b; + n->key = e->key; + if (h->type != HASHMAP_TYPE_SET) + ((struct plain_hashmap_entry*) n)->value = + ((struct plain_hashmap_entry*) e)->value; + assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); + + remove_entry(other, idx); + } + + return 0; +} + +int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { + struct swap_entries swap; + unsigned h_hash, other_hash, idx; + struct hashmap_base_entry *e, *n; + int r; + + assert(h); + + h_hash = bucket_hash(h, key); + if (bucket_scan(h, h_hash, key) != IDX_NIL) + return -EEXIST; + + if (!other) + return -ENOENT; + + assert(other->type == h->type); + + other_hash = bucket_hash(other, key); + idx = bucket_scan(other, other_hash, key); + if (idx == IDX_NIL) + return -ENOENT; + + e = bucket_at(other, idx); + + n = &bucket_at_swap(&swap, IDX_PUT)->p.b; + n->key = e->key; + if (h->type != HASHMAP_TYPE_SET) + ((struct plain_hashmap_entry*) n)->value = + ((struct plain_hashmap_entry*) e)->value; + r = hashmap_put_boldly(h, h_hash, &swap, true); + if (r < 0) + return r; + + remove_entry(other, idx); + return 0; +} + +HashmapBase *internal_hashmap_copy(HashmapBase *h) { + HashmapBase *copy; + int r; + + assert(h); + + copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); + if (!copy) + return NULL; + + switch (h->type) { + case HASHMAP_TYPE_PLAIN: + case HASHMAP_TYPE_ORDERED: + r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); + break; + case HASHMAP_TYPE_SET: + r = set_merge((Set*)copy, (Set*)h); + break; + default: + assert_not_reached("Unknown hashmap type"); + } + + if (r < 0) { + internal_hashmap_free(copy); + return NULL; + } + + return copy; +} + +char **internal_hashmap_get_strv(HashmapBase *h) { + char **sv; + Iterator i; + unsigned idx, n; + + sv = new(char*, n_entries(h)+1); + if (!sv) + return NULL; + + n = 0; + HASHMAP_FOREACH_IDX(idx, h, i) + sv[n++] = entry_value(h, bucket_at(h, idx)); + sv[n] = NULL; + + return sv; +} + +void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { + struct ordered_hashmap_entry *e; + unsigned hash, idx; + + if (!h) + return NULL; + + hash = bucket_hash(h, key); + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) + return NULL; + + e = ordered_bucket_at(h, idx); + if (e->iterate_next == IDX_NIL) + return NULL; + return ordered_bucket_at(h, e->iterate_next)->p.value; +} + +int set_consume(Set *s, void *value) { + int r; + + assert(s); + assert(value); + + r = set_put(s, value); + if (r <= 0) + free(value); + + return r; +} + +int set_put_strdup(Set *s, const char *p) { + char *c; + + assert(s); + assert(p); + + if (set_contains(s, (char*) p)) + return 0; + + c = strdup(p); + if (!c) + return -ENOMEM; + + return set_consume(s, c); +} + +int set_put_strdupv(Set *s, char **l) { + int n = 0, r; + char **i; + + assert(s); + + STRV_FOREACH(i, l) { + r = set_put_strdup(s, *i); + if (r < 0) + return r; + + n += r; + } + + return n; +} + +int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { + const char *p = v; + int r; + + assert(s); + assert(v); + + for (;;) { + char *word; + + r = extract_first_word(&p, &word, separators, flags); + if (r <= 0) + return r; + + r = set_consume(s, word); + if (r < 0) + return r; + } +} diff --git a/src/libbasic/src/hexdecoct.c b/src/libbasic/src/hexdecoct.c new file mode 100644 index 0000000000..060a95b616 --- /dev/null +++ b/src/libbasic/src/hexdecoct.c @@ -0,0 +1,754 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/util.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; +} + +ssize_t base64mem(const void *p, size_t l, char **out) { + 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 -ENOMEM; + + 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; + *out = r; + return z - r; +} + +static int base64_append_width(char **prefix, int plen, + const char *sep, int indent, + const void *p, size_t l, + int width) { + + _cleanup_free_ char *x = NULL; + char *t, *s; + ssize_t slen, len, avail; + int line, lines; + + len = base64mem(p, l, &x); + if (len <= 0) + return len; + + lines = (len + width - 1) / width; + + slen = sep ? strlen(sep) : 0; + t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); + if (!t) + return -ENOMEM; + + memcpy_safe(t + plen, sep, slen); + + for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { + int act = MIN(width, avail); + + if (line > 0 || sep) { + memset(s, ' ', indent); + s += indent; + } + + memcpy(s, x + width * line, act); + s += act; + *(s++) = line < lines - 1 ? '\n' : '\0'; + avail -= act; + } + assert(avail == 0); + + *prefix = t; + return 0; +} + +int base64_append(char **prefix, int plen, + const void *p, size_t l, + int indent, int width) { + if (plen > width / 2 || plen + indent > width) + /* leave indent on the left, keep last column free */ + return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); + else + /* leave plen on the left, keep last column free */ + return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); +}; + + +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/libbasic/src/hostname-util.c b/src/libbasic/src/hostname-util.c new file mode 100644 index 0000000000..e66f9a2250 --- /dev/null +++ b/src/libbasic/src/hostname-util.c @@ -0,0 +1,252 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" + +bool hostname_is_set(void) { + struct utsname u; + + assert_se(uname(&u) >= 0); + + if (isempty(u.nodename)) + return false; + + /* This is the built-in kernel default host name */ + if (streq(u.nodename, "(none)")) + return false; + + return true; +} + +char* gethostname_malloc(void) { + struct utsname u; + + /* This call tries to return something useful, either the actual hostname + * or it makes something up. The only reason it might fail is OOM. + * It might even return "localhost" if that's set. */ + + assert_se(uname(&u) >= 0); + + if (isempty(u.nodename) || streq(u.nodename, "(none)")) + return strdup(u.sysname); + + return strdup(u.nodename); +} + +int gethostname_strict(char **ret) { + struct utsname u; + char *k; + + /* This call will rather fail than make up a name. It will not return "localhost" either. */ + + assert_se(uname(&u) >= 0); + + if (isempty(u.nodename)) + return -ENXIO; + + if (streq(u.nodename, "(none)")) + return -ENXIO; + + if (is_localhost(u.nodename)) + return -ENXIO; + + k = strdup(u.nodename); + if (!k) + return -ENOMEM; + + *ret = k; + return 0; +} + +static bool hostname_valid_char(char c) { + return + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || + c == '_' || + c == '.'; +} + +/** + * Check if s looks like a valid host name or FQDN. This does not do + * full DNS validation, but only checks if the name is composed of + * allowed characters and the length is not above the maximum allowed + * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if + * 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_name_is_valid(). + */ +bool hostname_is_valid(const char *s, bool allow_trailing_dot) { + unsigned n_dots = 0; + const char *p; + bool dot; + + if (isempty(s)) + return false; + + /* Doesn't accept empty hostnames, hostnames with + * leading dots, and hostnames with multiple dots in a + * sequence. Also ensures that the length stays below + * HOST_NAME_MAX. */ + + for (p = s, dot = true; *p; p++) { + if (*p == '.') { + if (dot) + return false; + + dot = true; + n_dots++; + } else { + if (!hostname_valid_char(*p)) + return false; + + dot = false; + } + } + + if (dot && (n_dots < 2 || !allow_trailing_dot)) + return false; + + if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on + * Linux, but DNS allows domain names + * up to 255 characters */ + return false; + + return true; +} + +char* hostname_cleanup(char *s) { + char *p, *d; + bool dot; + + assert(s); + + strshorten(s, HOST_NAME_MAX); + + for (p = s, d = s, dot = true; *p; p++) { + if (*p == '.') { + if (dot) + continue; + + *(d++) = '.'; + dot = true; + } else if (hostname_valid_char(*p)) { + *(d++) = *p; + dot = false; + } + + } + + if (dot && d > s) + d[-1] = 0; + else + *d = 0; + + return s; +} + +bool is_localhost(const char *hostname) { + assert(hostname); + + /* This tries to identify local host and domain names + * described in RFC6761 plus the redhatism of localdomain */ + + return strcaseeq(hostname, "localhost") || + strcaseeq(hostname, "localhost.") || + strcaseeq(hostname, "localhost.localdomain") || + strcaseeq(hostname, "localhost.localdomain.") || + endswith_no_case(hostname, ".localhost") || + endswith_no_case(hostname, ".localhost.") || + endswith_no_case(hostname, ".localhost.localdomain") || + endswith_no_case(hostname, ".localhost.localdomain."); +} + +bool is_gateway_hostname(const char *hostname) { + assert(hostname); + + /* This tries to identify the valid syntaxes for the our + * synthetic "gateway" host. */ + + return + strcaseeq(hostname, "gateway") || + strcaseeq(hostname, "gateway."); +} + +int sethostname_idempotent(const char *s) { + char buf[HOST_NAME_MAX + 1] = {}; + + assert(s); + + if (gethostname(buf, sizeof(buf)) < 0) + return -errno; + + if (streq(buf, s)) + return 0; + + if (sethostname(s, strlen(s)) < 0) + return -errno; + + return 1; +} + +int read_hostname_config(const char *path, char **hostname) { + _cleanup_fclose_ FILE *f = NULL; + char l[LINE_MAX]; + char *name = NULL; + + assert(path); + assert(hostname); + + f = fopen(path, "re"); + if (!f) + return -errno; + + /* may have comments, ignore them */ + FOREACH_LINE(l, f, return -errno) { + truncate_nl(l); + if (l[0] != '\0' && l[0] != '#') { + /* found line with value */ + name = hostname_cleanup(l); + name = strdup(name); + if (!name) + return -ENOMEM; + break; + } + } + + if (!name) + /* no non-empty line found */ + return -ENOENT; + + *hostname = name; + return 0; +} diff --git a/src/libbasic/src/in-addr-util.c b/src/libbasic/src/in-addr-util.c new file mode 100644 index 0000000000..01fc83d6d9 --- /dev/null +++ b/src/libbasic/src/in-addr-util.c @@ -0,0 +1,449 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/util.h" + +bool in4_addr_is_null(const struct in_addr *a) { + return a->s_addr == 0; +} + +bool in6_addr_is_null(const struct in6_addr *a) { + return + a->s6_addr32[0] == 0 && + a->s6_addr32[1] == 0 && + a->s6_addr32[2] == 0 && + a->s6_addr32[3] == 0; +} + +int in_addr_is_null(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + return in4_addr_is_null(&u->in); + + if (family == AF_INET6) + return in6_addr_is_null(&u->in6); + + return -EAFNOSUPPORT; +} + +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) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); + + if (family == AF_INET6) + return IN6_IS_ADDR_LINKLOCAL(&u->in6); + + 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); + + if (family == AF_INET) + return a->in.s_addr == b->in.s_addr; + + if (family == AF_INET6) + return + a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && + a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && + a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && + a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; + + return -EAFNOSUPPORT; +} + +int in_addr_prefix_intersect( + int family, + const union in_addr_union *a, + unsigned aprefixlen, + const union in_addr_union *b, + unsigned bprefixlen) { + + unsigned m; + + assert(a); + assert(b); + + /* Checks whether there are any addresses that are in both + * networks */ + + m = MIN(aprefixlen, bprefixlen); + + if (family == AF_INET) { + uint32_t x, nm; + + x = be32toh(a->in.s_addr ^ b->in.s_addr); + nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); + + return (x & nm) == 0; + } + + if (family == AF_INET6) { + unsigned i; + + if (m > 128) + m = 128; + + for (i = 0; i < 16; i++) { + uint8_t x, nm; + + x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; + + if (m < 8) + nm = 0xFF << (8 - m); + else + nm = 0xFF; + + if ((x & nm) != 0) + return 0; + + if (m > 8) + m -= 8; + else + m = 0; + } + + return 1; + } + + return -EAFNOSUPPORT; +} + +int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { + assert(u); + + /* Increases the network part of an address by one. Returns + * positive it that succeeds, or 0 if this overflows. */ + + if (prefixlen <= 0) + return 0; + + if (family == AF_INET) { + uint32_t c, n; + + if (prefixlen > 32) + prefixlen = 32; + + c = be32toh(u->in.s_addr); + n = c + (1UL << (32 - prefixlen)); + if (n < c) + return 0; + n &= 0xFFFFFFFFUL << (32 - prefixlen); + + u->in.s_addr = htobe32(n); + return 1; + } + + if (family == AF_INET6) { + struct in6_addr add = {}, result; + uint8_t overflow = 0; + unsigned i; + + if (prefixlen > 128) + prefixlen = 128; + + /* First calculate what we have to add */ + add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); + + for (i = 16; i > 0; i--) { + unsigned j = i - 1; + + result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; + overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); + } + + if (overflow) + return 0; + + u->in6 = result; + return 1; + } + + return -EAFNOSUPPORT; +} + +int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { + char *x; + size_t l; + + assert(u); + assert(ret); + + if (family == AF_INET) + l = INET_ADDRSTRLEN; + else if (family == AF_INET6) + l = INET6_ADDRSTRLEN; + else + return -EAFNOSUPPORT; + + x = new(char, l); + if (!x) + return -ENOMEM; + + errno = 0; + if (!inet_ntop(family, u, x, l)) { + free(x); + return errno > 0 ? -errno : -EINVAL; + } + + *ret = x; + return 0; +} + +int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { + size_t l; + char *x; + int r; + + assert(u); + assert(ret); + + /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly + * handle IPv6 link-local addresses. */ + + if (family != AF_INET6) + goto fallback; + if (ifindex <= 0) + goto fallback; + + r = in_addr_is_link_local(family, u); + if (r < 0) + return r; + if (r == 0) + goto fallback; + + l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; + x = new(char, l); + if (!x) + return -ENOMEM; + + errno = 0; + if (!inet_ntop(family, u, x, l)) { + free(x); + return errno > 0 ? -errno : -EINVAL; + } + + sprintf(strchr(x, 0), "%%%i", ifindex); + *ret = x; + + return 0; + +fallback: + return in_addr_to_string(family, u, ret); +} + +int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { + + assert(s); + assert(ret); + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + errno = 0; + if (inet_pton(family, s, ret) <= 0) + return errno > 0 ? -errno : -EINVAL; + + return 0; +} + +int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { + int r; + + assert(s); + assert(family); + assert(ret); + + r = in_addr_from_string(AF_INET, s, ret); + if (r >= 0) { + *family = AF_INET; + return 0; + } + + r = in_addr_from_string(AF_INET6, s, ret); + if (r >= 0) { + *family = AF_INET6; + return 0; + } + + return -EINVAL; +} + +int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { + const char *suffix; + int r, ifi = 0; + + assert(s); + assert(family); + assert(ret); + + /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") + * if one is found. */ + + suffix = strchr(s, '%'); + if (suffix) { + + if (ifindex) { + /* If we shall return the interface index, try to parse it */ + r = parse_ifindex(suffix + 1, &ifi); + if (r < 0) { + unsigned u; + + u = if_nametoindex(suffix + 1); + if (u <= 0) + return -errno; + + ifi = (int) u; + } + } + + s = strndupa(s, suffix - s); + } + + r = in_addr_from_string_auto(s, family, ret); + if (r < 0) + return r; + + if (ifindex) + *ifindex = ifi; + + return r; +} + +unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { + assert(addr); + + return 32 - u32ctz(be32toh(addr->s_addr)); +} + +struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { + assert(addr); + assert(prefixlen <= 32); + + /* Shifting beyond 32 is not defined, handle this specially. */ + if (prefixlen == 0) + addr->s_addr = 0; + else + addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); + + return addr; +} + +int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { + uint8_t msb_octet = *(uint8_t*) addr; + + /* addr may not be aligned, so make sure we only access it byte-wise */ + + assert(addr); + assert(prefixlen); + + if (msb_octet < 128) + /* class A, leading bits: 0 */ + *prefixlen = 8; + else if (msb_octet < 192) + /* class B, leading bits 10 */ + *prefixlen = 16; + else if (msb_octet < 224) + /* class C, leading bits 110 */ + *prefixlen = 24; + else + /* class D or E, no default prefixlen */ + return -ERANGE; + + return 0; +} + +int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { + unsigned char prefixlen; + int r; + + assert(addr); + assert(mask); + + r = in_addr_default_prefixlen(addr, &prefixlen); + if (r < 0) + return r; + + in_addr_prefixlen_to_netmask(mask, prefixlen); + return 0; +} + +int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { + assert(addr); + + if (family == AF_INET) { + struct in_addr mask; + + if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) + return -EINVAL; + + addr->in.s_addr &= mask.s_addr; + return 0; + } + + if (family == AF_INET6) { + unsigned i; + + for (i = 0; i < 16; i++) { + uint8_t mask; + + if (prefixlen >= 8) { + mask = 0xFF; + prefixlen -= 8; + } else { + mask = 0xFF << (8 - prefixlen); + prefixlen = 0; + } + + addr->in6.s6_addr[i] &= mask; + } + + return 0; + } + + return -EAFNOSUPPORT; +} diff --git a/src/libbasic/src/io-util.c b/src/libbasic/src/io-util.c new file mode 100644 index 0000000000..800a7044f6 --- /dev/null +++ b/src/libbasic/src/io-util.c @@ -0,0 +1,269 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/io-util.h" +#include "basic/time-util.h" + +int flush_fd(int fd) { + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + + /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything + * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read + * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used + * was set to non-blocking too. */ + + 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/libbasic/src/label.c b/src/libbasic/src/label.c new file mode 100644 index 0000000000..cea374198a --- /dev/null +++ b/src/libbasic/src/label.c @@ -0,0 +1,82 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/label.h" +#include "basic/macro.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" + +int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { + int r, q; + + r = mac_selinux_fix(path, ignore_enoent, ignore_erofs); + q = mac_smack_fix(path, ignore_enoent, ignore_erofs); + + if (r < 0) + return r; + if (q < 0) + return q; + + return 0; +} + +int mkdir_label(const char *path, mode_t mode) { + int r; + + assert(path); + + r = mac_selinux_create_file_prepare(path, S_IFDIR); + if (r < 0) + return r; + + if (mkdir(path, mode) < 0) + r = -errno; + + mac_selinux_create_file_clear(); + + if (r < 0) + return r; + + return mac_smack_fix(path, false, false); +} + +int symlink_label(const char *old_path, const char *new_path) { + int r; + + assert(old_path); + assert(new_path); + + r = mac_selinux_create_file_prepare(new_path, S_IFLNK); + if (r < 0) + return r; + + if (symlink(old_path, new_path) < 0) + r = -errno; + + mac_selinux_create_file_clear(); + + if (r < 0) + return r; + + return mac_smack_fix(new_path, false, false); +} diff --git a/src/libbasic/src/locale-util.c b/src/libbasic/src/locale-util.c new file mode 100644 index 0000000000..7cedaae319 --- /dev/null +++ b/src/libbasic/src/locale-util.c @@ -0,0 +1,322 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/locale-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" + +static int add_locales_from_archive(Set *locales) { + /* Stolen from glibc... */ + + struct locarhead { + uint32_t magic; + /* Serial number. */ + uint32_t serial; + /* Name hash table. */ + uint32_t namehash_offset; + uint32_t namehash_used; + uint32_t namehash_size; + /* String table. */ + uint32_t string_offset; + uint32_t string_used; + uint32_t string_size; + /* Table with locale records. */ + uint32_t locrectab_offset; + uint32_t locrectab_used; + uint32_t locrectab_size; + /* MD5 sum hash table. */ + uint32_t sumhash_offset; + uint32_t sumhash_used; + uint32_t sumhash_size; + }; + + struct namehashent { + /* Hash value of the name. */ + uint32_t hashval; + /* Offset of the name in the string table. */ + uint32_t name_offset; + /* Offset of the locale record. */ + uint32_t locrec_offset; + }; + + const struct locarhead *h; + const struct namehashent *e; + const void *p = MAP_FAILED; + _cleanup_close_ int fd = -1; + size_t sz = 0; + struct stat st; + unsigned i; + int r; + + fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return errno == ENOENT ? 0 : -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADMSG; + + if (st.st_size < (off_t) sizeof(struct locarhead)) + return -EBADMSG; + + p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + return -errno; + + h = (const struct locarhead *) p; + if (h->magic != 0xde020109 || + h->namehash_offset + h->namehash_size > st.st_size || + h->string_offset + h->string_size > st.st_size || + h->locrectab_offset + h->locrectab_size > st.st_size || + h->sumhash_offset + h->sumhash_size > st.st_size) { + r = -EBADMSG; + goto finish; + } + + e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); + for (i = 0; i < h->namehash_size; i++) { + char *z; + + if (e[i].locrec_offset == 0) + continue; + + if (!utf8_is_valid((char*) p + e[i].name_offset)) + continue; + + z = strdup((char*) p + e[i].name_offset); + if (!z) { + r = -ENOMEM; + goto finish; + } + + r = set_consume(locales, z); + if (r < 0) + goto finish; + } + + r = 0; + + finish: + if (p != MAP_FAILED) + munmap((void*) p, sz); + + return r; +} + +static int add_locales_from_libdir (Set *locales) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *entry; + int r; + + dir = opendir("/usr/lib/locale"); + if (!dir) + return errno == ENOENT ? 0 : -errno; + + FOREACH_DIRENT(entry, dir, return -errno) { + char *z; + + dirent_ensure_type(dir, entry); + + if (entry->d_type != DT_DIR) + continue; + + z = strdup(entry->d_name); + if (!z) + return -ENOMEM; + + r = set_consume(locales, z); + if (r < 0 && r != -EEXIST) + return r; + } + + return 0; +} + +int get_locales(char ***ret) { + _cleanup_set_free_ Set *locales = NULL; + _cleanup_strv_free_ char **l = NULL; + int r; + + locales = set_new(&string_hash_ops); + if (!locales) + return -ENOMEM; + + r = add_locales_from_archive(locales); + if (r < 0 && r != -ENOENT) + return r; + + r = add_locales_from_libdir(locales); + if (r < 0) + return r; + + l = set_get_strv(locales); + if (!l) + return -ENOMEM; + + strv_sort(l); + + *ret = l; + l = NULL; + + return 0; +} + +bool locale_is_valid(const char *name) { + + if (isempty(name)) + return false; + + if (strlen(name) >= 128) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (!filename_is_valid(name)) + return false; + + if (!string_is_safe(name)) + return false; + + 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 *special_glyph(SpecialGlyph code) { + + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { + [TREE_VERTICAL] = "| ", + [TREE_BRANCH] = "|-", + [TREE_RIGHT] = "`-", + [TREE_SPACE] = " ", + [TRIANGULAR_BULLET] = ">", + [BLACK_CIRCLE] = "*", + [ARROW] = "->", + [MDASH] = "-", + }, + + /* UTF-8 */ + [ true ] = { + [TREE_VERTICAL] = "\342\224\202 ", /* │ */ + [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ + [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ + [TREE_SPACE] = " ", /* */ + [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ + [BLACK_CIRCLE] = "\342\227\217", /* ● */ + [ARROW] = "\342\206\222", /* → */ + [MDASH] = "\342\200\223", /* – */ + }, + }; + + return draw_table[is_locale_utf8()][code]; +} + +static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { + [VARIABLE_LANG] = "LANG", + [VARIABLE_LANGUAGE] = "LANGUAGE", + [VARIABLE_LC_CTYPE] = "LC_CTYPE", + [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", + [VARIABLE_LC_TIME] = "LC_TIME", + [VARIABLE_LC_COLLATE] = "LC_COLLATE", + [VARIABLE_LC_MONETARY] = "LC_MONETARY", + [VARIABLE_LC_MESSAGES] = "LC_MESSAGES", + [VARIABLE_LC_PAPER] = "LC_PAPER", + [VARIABLE_LC_NAME] = "LC_NAME", + [VARIABLE_LC_ADDRESS] = "LC_ADDRESS", + [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE", + [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT", + [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION" +}; + +DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable); diff --git a/src/libbasic/src/lockfile-util.c b/src/libbasic/src/lockfile-util.c new file mode 100644 index 0000000000..233627c1a4 --- /dev/null +++ b/src/libbasic/src/lockfile-util.c @@ -0,0 +1,153 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/lockfile-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" + +int make_lock_file(const char *p, int operation, LockFile *ret) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *t = NULL; + int r; + + /* + * We use UNPOSIX locks if they are available. They have nice + * semantics, and are mostly compatible with NFS. However, + * they are only available on new kernels. When we detect we + * are running on an older kernel, then we fall back to good + * old BSD locks. They also have nice semantics, but are + * slightly problematic on NFS, where they are upgraded to + * POSIX locks, even though locally they are orthogonal to + * POSIX locks. + */ + + t = strdup(p); + if (!t) + return -ENOMEM; + + for (;;) { + struct flock fl = { + .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, + .l_whence = SEEK_SET, + }; + struct stat st; + + fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) + return -errno; + + r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); + if (r < 0) { + + /* If the kernel is too old, use good old BSD locks */ + if (errno == EINVAL) + r = flock(fd, operation); + + if (r < 0) + return errno == EAGAIN ? -EBUSY : -errno; + } + + /* If we acquired the lock, let's check if the file + * still exists in the file system. If not, then the + * previous exclusive owner removed it and then closed + * it. In such a case our acquired lock is worthless, + * hence try again. */ + + r = fstat(fd, &st); + if (r < 0) + return -errno; + if (st.st_nlink > 0) + break; + + fd = safe_close(fd); + } + + ret->path = t; + ret->fd = fd; + ret->operation = operation; + + fd = -1; + t = NULL; + + return r; +} + +int make_lock_file_for(const char *p, int operation, LockFile *ret) { + const char *fn; + char *t; + + assert(p); + assert(ret); + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + t = newa(char, strlen(p) + 2 + 4 + 1); + stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); + + return make_lock_file(t, operation, ret); +} + +void release_lock_file(LockFile *f) { + int r; + + if (!f) + return; + + if (f->path) { + + /* If we are the exclusive owner we can safely delete + * the lock file itself. If we are not the exclusive + * owner, we can try becoming it. */ + + if (f->fd >= 0 && + (f->operation & ~LOCK_NB) == LOCK_SH) { + static const struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + r = fcntl(f->fd, F_OFD_SETLK, &fl); + if (r < 0 && errno == EINVAL) + r = flock(f->fd, LOCK_EX|LOCK_NB); + + if (r >= 0) + f->operation = LOCK_EX|LOCK_NB; + } + + if ((f->operation & ~LOCK_NB) == LOCK_EX) + unlink_noerrno(f->path); + + f->path = mfree(f->path); + } + + f->fd = safe_close(f->fd); + f->operation = 0; +} diff --git a/src/libbasic/src/log.c b/src/libbasic/src/log.c new file mode 100644 index 0000000000..84a65b7d54 --- /dev/null +++ b/src/libbasic/src/log.c @@ -0,0 +1,1170 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/syslog-util.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/util.h" + +#define SNDBUF_SIZE (8*1024*1024) + +static LogTarget log_target = LOG_TARGET_CONSOLE; +static int log_max_level = LOG_INFO; +static int log_facility = LOG_DAEMON; + +static int console_fd = STDERR_FILENO; +static int syslog_fd = -1; +static int kmsg_fd = -1; +static int journal_fd = -1; + +static bool syslog_is_stream = false; + +static bool show_color = false; +static bool show_location = false; + +static bool upgrade_syslog_to_journal = false; + +/* Akin to glibc's __abort_msg; which is private and we hence cannot + * use here. */ +static char *log_abort_msg = NULL; + +void log_close_console(void) { + + if (console_fd < 0) + return; + + if (getpid() == 1) { + if (console_fd >= 3) + safe_close(console_fd); + + console_fd = -1; + } +} + +static int log_open_console(void) { + + if (console_fd >= 0) + return 0; + + if (getpid() == 1) { + console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (console_fd < 0) + return console_fd; + } else + console_fd = STDERR_FILENO; + + return 0; +} + +void log_close_kmsg(void) { + kmsg_fd = safe_close(kmsg_fd); +} + +static int log_open_kmsg(void) { + + if (kmsg_fd >= 0) + return 0; + + kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (kmsg_fd < 0) + return -errno; + + return 0; +} + +void log_close_syslog(void) { + syslog_fd = safe_close(syslog_fd); +} + +static int create_log_socket(int type) { + struct timeval tv; + int fd; + + fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + /* We need a blocking fd here since we'd otherwise lose + messages way too early. However, let's not hang forever in the + unlikely case of a deadlock. */ + if (getpid() == 1) + timeval_store(&tv, 10 * USEC_PER_MSEC); + else + timeval_store(&tv, 10 * USEC_PER_SEC); + (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + return fd; +} + +static int log_open_syslog(void) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/dev/log", + }; + + int r; + + if (syslog_fd >= 0) + return 0; + + syslog_fd = create_log_socket(SOCK_DGRAM); + if (syslog_fd < 0) { + r = syslog_fd; + goto fail; + } + + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + safe_close(syslog_fd); + + /* Some legacy syslog systems still use stream + * sockets. They really shouldn't. But what can we + * do... */ + syslog_fd = create_log_socket(SOCK_STREAM); + if (syslog_fd < 0) { + r = syslog_fd; + goto fail; + } + + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + r = -errno; + goto fail; + } + + syslog_is_stream = true; + } else + syslog_is_stream = false; + + return 0; + +fail: + log_close_syslog(); + return r; +} + +void log_close_journal(void) { + journal_fd = safe_close(journal_fd); +} + +static int log_open_journal(void) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; + + int r; + + if (journal_fd >= 0) + return 0; + + journal_fd = create_log_socket(SOCK_DGRAM); + if (journal_fd < 0) { + r = journal_fd; + goto fail; + } + + if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + log_close_journal(); + return r; +} + +int log_open(void) { + int r; + + /* If we don't use the console we close it here, to not get + * killed by SAK. If we don't use syslog we close it here so + * that we are not confused by somebody deleting the socket in + * the fs. If we don't use /dev/kmsg we still keep it open, + * because there is no reason to close it. */ + + if (log_target == LOG_TARGET_NULL) { + log_close_journal(); + log_close_syslog(); + log_close_console(); + return 0; + } + + if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) || + getpid() == 1 || + isatty(STDERR_FILENO) <= 0) { + + if (log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) { + r = log_open_journal(); + if (r >= 0) { + log_close_syslog(); + log_close_console(); + return r; + } + } + + if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_SYSLOG) { + r = log_open_syslog(); + if (r >= 0) { + log_close_journal(); + log_close_console(); + return r; + } + } + + if (log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_SAFE || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_KMSG) { + r = log_open_kmsg(); + if (r >= 0) { + log_close_journal(); + log_close_syslog(); + log_close_console(); + return r; + } + } + } + + log_close_journal(); + log_close_syslog(); + + return log_open_console(); +} + +void log_set_target(LogTarget target) { + assert(target >= 0); + assert(target < _LOG_TARGET_MAX); + + if (upgrade_syslog_to_journal) { + if (target == LOG_TARGET_SYSLOG) + target = LOG_TARGET_JOURNAL; + else if (target == LOG_TARGET_SYSLOG_OR_KMSG) + target = LOG_TARGET_JOURNAL_OR_KMSG; + } + + log_target = target; +} + +void log_close(void) { + log_close_journal(); + log_close_syslog(); + log_close_kmsg(); + log_close_console(); +} + +void log_forget_fds(void) { + console_fd = kmsg_fd = syslog_fd = journal_fd = -1; +} + +void log_set_max_level(int level) { + assert((level & LOG_PRIMASK) == level); + + log_max_level = level; +} + +void log_set_facility(int facility) { + log_facility = facility; +} + +static int write_to_console( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { + + char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; + struct iovec iovec[6] = {}; + unsigned n = 0; + bool highlight; + + if (console_fd < 0) + return 0; + + if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { + sprintf(prefix, "<%i>", level); + IOVEC_SET_STRING(iovec[n++], prefix); + } + + highlight = LOG_PRI(level) <= LOG_ERR && show_color; + + if (show_location) { + snprintf(location, sizeof(location), "(%s:%i) ", file, line); + IOVEC_SET_STRING(iovec[n++], location); + } + + if (highlight) + IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); + IOVEC_SET_STRING(iovec[n++], buffer); + if (highlight) + IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); + IOVEC_SET_STRING(iovec[n++], "\n"); + + if (writev(console_fd, iovec, n) < 0) { + + if (errno == EIO && getpid() == 1) { + + /* If somebody tried to kick us from our + * console tty (via vhangup() or suchlike), + * try to reconnect */ + + log_close_console(); + log_open_console(); + + if (console_fd < 0) + return 0; + + if (writev(console_fd, iovec, n) < 0) + return -errno; + } else + return -errno; + } + + return 1; +} + +static int write_to_syslog( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { + + char header_priority[2 + DECIMAL_STR_MAX(int) + 1], + header_time[64], + header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; + struct iovec iovec[5] = {}; + struct msghdr msghdr = { + .msg_iov = iovec, + .msg_iovlen = ELEMENTSOF(iovec), + }; + time_t t; + struct tm *tm; + + if (syslog_fd < 0) + return 0; + + xsprintf(header_priority, "<%i>", level); + + t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); + tm = localtime(&t); + if (!tm) + return -EINVAL; + + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return -EINVAL; + + xsprintf(header_pid, "["PID_FMT"]: ", getpid()); + + IOVEC_SET_STRING(iovec[0], header_priority); + IOVEC_SET_STRING(iovec[1], header_time); + IOVEC_SET_STRING(iovec[2], program_invocation_short_name); + IOVEC_SET_STRING(iovec[3], header_pid); + IOVEC_SET_STRING(iovec[4], buffer); + + /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ + if (syslog_is_stream) + iovec[4].iov_len++; + + for (;;) { + ssize_t n; + + n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); + if (n < 0) + return -errno; + + if (!syslog_is_stream || + (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) + break; + + IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); + } + + return 1; +} + +static int write_to_kmsg( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { + + char header_priority[2 + DECIMAL_STR_MAX(int) + 1], + header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; + struct iovec iovec[5] = {}; + + if (kmsg_fd < 0) + return 0; + + xsprintf(header_priority, "<%i>", level); + xsprintf(header_pid, "["PID_FMT"]: ", getpid()); + + IOVEC_SET_STRING(iovec[0], header_priority); + IOVEC_SET_STRING(iovec[1], program_invocation_short_name); + IOVEC_SET_STRING(iovec[2], header_pid); + IOVEC_SET_STRING(iovec[3], buffer); + IOVEC_SET_STRING(iovec[4], "\n"); + + if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) + return -errno; + + return 1; +} + +static int log_do_header( + char *header, + size_t size, + int level, + int error, + const char *file, int line, const char *func, + const char *object_field, const char *object) { + + snprintf(header, size, + "PRIORITY=%i\n" + "SYSLOG_FACILITY=%i\n" + "%s%s%s" + "%s%.*i%s" + "%s%s%s" + "%s%.*i%s" + "%s%s%s" + "SYSLOG_IDENTIFIER=%s\n", + LOG_PRI(level), + LOG_FAC(level), + isempty(file) ? "" : "CODE_FILE=", + isempty(file) ? "" : file, + isempty(file) ? "" : "\n", + line ? "CODE_LINE=" : "", + line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */ + line ? "\n" : "", + isempty(func) ? "" : "CODE_FUNCTION=", + isempty(func) ? "" : func, + isempty(func) ? "" : "\n", + error ? "ERRNO=" : "", + error ? 1 : 0, error, + error ? "\n" : "", + isempty(object) ? "" : object_field, + isempty(object) ? "" : object, + isempty(object) ? "" : "\n", + program_invocation_short_name); + + return 0; +} + +static int write_to_journal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { + + char header[LINE_MAX]; + struct iovec iovec[4] = {}; + struct msghdr mh = {}; + + if (journal_fd < 0) + return 0; + + log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object); + + IOVEC_SET_STRING(iovec[0], header); + IOVEC_SET_STRING(iovec[1], "MESSAGE="); + IOVEC_SET_STRING(iovec[2], buffer); + IOVEC_SET_STRING(iovec[3], "\n"); + + mh.msg_iov = iovec; + mh.msg_iovlen = ELEMENTSOF(iovec); + + if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 1; +} + +static int log_dispatch( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + char *buffer) { + + assert(buffer); + + if (log_target == LOG_TARGET_NULL) + return -error; + + /* Patch in LOG_DAEMON facility if necessary */ + if ((level & LOG_FACMASK) == 0) + level = log_facility | LOG_PRI(level); + + if (error < 0) + error = -error; + + do { + char *e; + int k = 0; + + buffer += strspn(buffer, NEWLINE); + + if (buffer[0] == 0) + break; + + if ((e = strpbrk(buffer, NEWLINE))) + *(e++) = 0; + + if (log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) { + + k = write_to_journal(level, error, file, line, func, object_field, object, buffer); + if (k < 0) { + if (k != -EAGAIN) + log_close_journal(); + log_open_kmsg(); + } + } + + if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_SYSLOG) { + + k = write_to_syslog(level, error, file, line, func, object_field, object, buffer); + if (k < 0) { + if (k != -EAGAIN) + log_close_syslog(); + log_open_kmsg(); + } + } + + if (k <= 0 && + (log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_SAFE || + log_target == LOG_TARGET_SYSLOG_OR_KMSG || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_KMSG)) { + + k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer); + if (k < 0) { + log_close_kmsg(); + log_open_console(); + } + } + + if (k <= 0) + (void) write_to_console(level, error, file, line, func, object_field, object, buffer); + + buffer = e; + } while (buffer); + + return -error; +} + +int log_dump_internal( + int level, + int error, + const char *file, + int line, + const char *func, + char *buffer) { + + PROTECT_ERRNO; + + /* This modifies the buffer... */ + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return -error; + + return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); +} + +int log_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, + va_list ap) { + + PROTECT_ERRNO; + char buffer[LINE_MAX]; + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return -error; + + /* Make sure that %m maps to the specified error */ + if (error != 0) + errno = error; + + vsnprintf(buffer, sizeof(buffer), format, ap); + + return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); +} + +int log_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) { + + va_list ap; + int r; + + va_start(ap, format); + r = log_internalv(level, error, file, line, func, format, ap); + va_end(ap); + + return r; +} + +int log_object_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, + va_list ap) { + + PROTECT_ERRNO; + char *buffer, *b; + size_t l; + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return -error; + + /* Make sure that %m maps to the specified error */ + if (error != 0) + errno = error; + + /* Prepend the object name before the message */ + if (object) { + size_t n; + + n = strlen(object); + l = n + 2 + LINE_MAX; + + buffer = newa(char, l); + b = stpcpy(stpcpy(buffer, object), ": "); + } else { + l = LINE_MAX; + b = buffer = newa(char, l); + } + + vsnprintf(b, l, format, ap); + + return log_dispatch(level, error, file, line, func, object_field, object, buffer); +} + +int log_object_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, ...) { + + va_list ap; + int r; + + va_start(ap, format); + r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap); + va_end(ap); + + return r; +} + +static void log_assert( + int level, + const char *text, + const char *file, + int line, + const char *func, + const char *format) { + + static char buffer[LINE_MAX]; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return; + + DISABLE_WARNING_FORMAT_NONLITERAL; + xsprintf(buffer, format, text, file, line, func); + REENABLE_WARNING; + + log_abort_msg = buffer; + + log_dispatch(level, 0, file, line, func, NULL, NULL, buffer); +} + +noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) { + log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); + abort(); +} + +noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { + log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); + abort(); +} + +void log_assert_failed_return(const char *text, const char *file, int line, const char *func) { + PROTECT_ERRNO; + log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); +} + +int log_oom_internal(const char *file, int line, const char *func) { + log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); + return -ENOMEM; +} + +int log_format_iovec( + struct iovec *iovec, + unsigned iovec_len, + unsigned *n, + bool newline_separator, + int error, + const char *format, + va_list ap) { + + static const char nl = '\n'; + + while (format && *n + 1 < iovec_len) { + va_list aq; + char *m; + int r; + + /* We need to copy the va_list structure, + * since vasprintf() leaves it afterwards at + * an undefined location */ + + if (error != 0) + errno = error; + + va_copy(aq, ap); + r = vasprintf(&m, format, aq); + va_end(aq); + if (r < 0) + return -EINVAL; + + /* Now, jump enough ahead, so that we point to + * the next format string */ + VA_FORMAT_ADVANCE(format, ap); + + IOVEC_SET_STRING(iovec[(*n)++], m); + + if (newline_separator) { + iovec[*n].iov_base = (char*) &nl; + iovec[*n].iov_len = 1; + (*n)++; + } + + format = va_arg(ap, char *); + } + return 0; +} + +int log_struct_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) { + + char buf[LINE_MAX]; + bool found = false; + PROTECT_ERRNO; + va_list ap; + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return -error; + + if (log_target == LOG_TARGET_NULL) + return -error; + + if ((level & LOG_FACMASK) == 0) + level = log_facility | LOG_PRI(level); + + if ((log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) && + journal_fd >= 0) { + char header[LINE_MAX]; + struct iovec iovec[17] = {}; + unsigned n = 0, i; + int r; + struct msghdr mh = { + .msg_iov = iovec, + }; + bool fallback = false; + + /* If the journal is available do structured logging */ + log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL); + IOVEC_SET_STRING(iovec[n++], header); + + va_start(ap, format); + r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); + if (r < 0) + fallback = true; + else { + mh.msg_iovlen = n; + (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); + } + + va_end(ap); + for (i = 1; i < n; i += 2) + free(iovec[i].iov_base); + + if (!fallback) + return -error; + } + + /* Fallback if journal logging is not available or didn't work. */ + + va_start(ap, format); + while (format) { + va_list aq; + + if (error != 0) + errno = error; + + va_copy(aq, ap); + vsnprintf(buf, sizeof(buf), format, aq); + va_end(aq); + + if (startswith(buf, "MESSAGE=")) { + found = true; + break; + } + + VA_FORMAT_ADVANCE(format, ap); + + format = va_arg(ap, char *); + } + va_end(ap); + + if (!found) + return -error; + + return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8); +} + +int log_set_target_from_string(const char *e) { + LogTarget t; + + t = log_target_from_string(e); + if (t < 0) + return -EINVAL; + + log_set_target(t); + return 0; +} + +int log_set_max_level_from_string(const char *e) { + int t; + + t = log_level_from_string(e); + if (t < 0) + return -EINVAL; + + log_set_max_level(t); + return 0; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + + /* + * The systemd.log_xyz= settings are parsed by all tools, and + * so is "debug". + * + * However, "quiet" is only parsed by PID 1, and only turns of + * status output to /dev/console, but does not alter the log + * level. + */ + + if (streq(key, "debug") && !value) + log_set_max_level(LOG_DEBUG); + + else if (streq(key, "systemd.log_target") && value) { + + if (log_set_target_from_string(value) < 0) + log_warning("Failed to parse log target '%s'. Ignoring.", value); + + } else if (streq(key, "systemd.log_level") && value) { + + if (log_set_max_level_from_string(value) < 0) + log_warning("Failed to parse log level '%s'. Ignoring.", value); + + } else if (streq(key, "systemd.log_color") && value) { + + if (log_show_color_from_string(value) < 0) + log_warning("Failed to parse log color setting '%s'. Ignoring.", value); + + } else if (streq(key, "systemd.log_location") && value) { + + if (log_show_location_from_string(value) < 0) + log_warning("Failed to parse log location setting '%s'. Ignoring.", value); + } + + return 0; +} + +void log_parse_environment(void) { + const char *e; + + if (get_ctty_devnr(0, NULL) < 0) + /* Only try to read the command line in daemons. + We assume that anything that has a controlling + tty is user stuff. */ + (void) parse_proc_cmdline(parse_proc_cmdline_item); + + e = secure_getenv("SYSTEMD_LOG_TARGET"); + if (e && log_set_target_from_string(e) < 0) + log_warning("Failed to parse log target '%s'. Ignoring.", e); + + e = secure_getenv("SYSTEMD_LOG_LEVEL"); + if (e && log_set_max_level_from_string(e) < 0) + log_warning("Failed to parse log level '%s'. Ignoring.", e); + + e = secure_getenv("SYSTEMD_LOG_COLOR"); + if (e && log_show_color_from_string(e) < 0) + log_warning("Failed to parse bool '%s'. Ignoring.", e); + + e = secure_getenv("SYSTEMD_LOG_LOCATION"); + if (e && log_show_location_from_string(e) < 0) + log_warning("Failed to parse bool '%s'. Ignoring.", e); +} + +LogTarget log_get_target(void) { + return log_target; +} + +int log_get_max_level(void) { + return log_max_level; +} + +void log_show_color(bool b) { + show_color = b; +} + +bool log_get_show_color(void) { + return show_color; +} + +void log_show_location(bool b) { + show_location = b; +} + +bool log_get_show_location(void) { + return show_location; +} + +int log_show_color_from_string(const char *e) { + int t; + + t = parse_boolean(e); + if (t < 0) + return t; + + log_show_color(t); + return 0; +} + +int log_show_location_from_string(const char *e) { + int t; + + t = parse_boolean(e); + if (t < 0) + return t; + + log_show_location(t); + return 0; +} + +bool log_on_console(void) { + if (log_target == LOG_TARGET_CONSOLE || + log_target == LOG_TARGET_CONSOLE_PREFIXED) + return true; + + return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0; +} + +static const char *const log_target_table[_LOG_TARGET_MAX] = { + [LOG_TARGET_CONSOLE] = "console", + [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed", + [LOG_TARGET_KMSG] = "kmsg", + [LOG_TARGET_JOURNAL] = "journal", + [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg", + [LOG_TARGET_SYSLOG] = "syslog", + [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", + [LOG_TARGET_AUTO] = "auto", + [LOG_TARGET_SAFE] = "safe", + [LOG_TARGET_NULL] = "null" +}; + +DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); + +void log_received_signal(int level, const struct signalfd_siginfo *si) { + if (si->ssi_pid > 0) { + _cleanup_free_ char *p = NULL; + + get_process_comm(si->ssi_pid, &p); + + log_full(level, + "Received SIG%s from PID %"PRIu32" (%s).", + signal_to_string(si->ssi_signo), + si->ssi_pid, strna(p)); + } else + log_full(level, + "Received SIG%s.", + signal_to_string(si->ssi_signo)); + +} + +void log_set_upgrade_syslog_to_journal(bool b) { + upgrade_syslog_to_journal = b; +} + +int log_syntax_internal( + const char *unit, + int level, + const char *config_file, + unsigned config_line, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) { + + PROTECT_ERRNO; + char buffer[LINE_MAX]; + int r; + va_list ap; + + if (error < 0) + error = -error; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return -error; + + if (log_target == LOG_TARGET_NULL) + return -error; + + if (error != 0) + errno = error; + + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + if (unit) + r = log_struct_internal( + level, error, + file, line, func, + getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, + LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), + "CONFIG_FILE=%s", config_file, + "CONFIG_LINE=%u", config_line, + LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), + NULL); + else + r = log_struct_internal( + level, error, + file, line, func, + LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), + "CONFIG_FILE=%s", config_file, + "CONFIG_LINE=%u", config_line, + LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), + NULL); + + return r; +} diff --git a/src/libbasic/src/login-util.c b/src/libbasic/src/login-util.c new file mode 100644 index 0000000000..fe97f66392 --- /dev/null +++ b/src/libbasic/src/login-util.c @@ -0,0 +1,31 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/login-util.h" +#include "basic/string-util.h" + +bool session_id_valid(const char *id) { + + if (isempty(id)) + return false; + + return id[strspn(id, LETTERS DIGITS)] == '\0'; +} diff --git a/src/libbasic/src/memfd-util.c b/src/libbasic/src/memfd-util.c new file mode 100644 index 0000000000..810dd37ee7 --- /dev/null +++ b/src/libbasic/src/memfd-util.c @@ -0,0 +1,174 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#ifdef HAVE_LINUX_MEMFD_H +#include +#endif +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/memfd-util.h" +#include "basic/missing.h" +#include "basic/string-util.h" +#include "basic/utf8.h" + +int memfd_new(const char *name) { + _cleanup_free_ char *g = NULL; + int fd; + + if (!name) { + char pr[17] = {}; + + /* If no name is specified we generate one. We include + * a hint indicating our library implementation, and + * add the thread name to it */ + + assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); + + if (isempty(pr)) + name = "sd"; + else { + _cleanup_free_ char *e = NULL; + + e = utf8_escape_invalid(pr); + if (!e) + return -ENOMEM; + + g = strappend("sd-", e); + if (!g) + return -ENOMEM; + + name = g; + } + } + + fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); + if (fd < 0) + return -errno; + + return fd; +} + +int memfd_map(int fd, uint64_t offset, size_t size, void **p) { + void *q; + int sealed; + + assert(fd >= 0); + assert(size > 0); + assert(p); + + sealed = memfd_get_sealed(fd); + if (sealed < 0) + return sealed; + + if (sealed) + q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); + else + q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); + + if (q == MAP_FAILED) + return -errno; + + *p = q; + return 0; +} + +int memfd_set_sealed(int fd) { + int r; + + assert(fd >= 0); + + r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); + if (r < 0) + return -errno; + + return 0; +} + +int memfd_get_sealed(int fd) { + int r; + + assert(fd >= 0); + + r = fcntl(fd, F_GET_SEALS); + if (r < 0) + return -errno; + + return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); +} + +int memfd_get_size(int fd, uint64_t *sz) { + struct stat stat; + int r; + + assert(fd >= 0); + assert(sz); + + r = fstat(fd, &stat); + if (r < 0) + return -errno; + + *sz = stat.st_size; + return 0; +} + +int memfd_set_size(int fd, uint64_t sz) { + int r; + + assert(fd >= 0); + + r = ftruncate(fd, sz); + if (r < 0) + return -errno; + + return 0; +} + +int memfd_new_and_map(const char *name, size_t sz, void **p) { + _cleanup_close_ int fd = -1; + int r; + + assert(sz > 0); + assert(p); + + fd = memfd_new(name); + if (fd < 0) + return fd; + + r = memfd_set_size(fd, sz); + if (r < 0) + return r; + + r = memfd_map(fd, 0, sz, p); + if (r < 0) + return r; + + r = fd; + fd = -1; + + return r; +} diff --git a/src/libbasic/src/mempool.c b/src/libbasic/src/mempool.c new file mode 100644 index 0000000000..0a64c1b29c --- /dev/null +++ b/src/libbasic/src/mempool.c @@ -0,0 +1,104 @@ +/*** + This file is part of systemd. + + Copyright 2010-2014 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 . +***/ + +#include +#include + +#include "basic/macro.h" +#include "basic/mempool.h" +#include "basic/util.h" + +struct pool { + struct pool *next; + unsigned n_tiles; + unsigned n_used; +}; + +void* mempool_alloc_tile(struct mempool *mp) { + unsigned i; + + /* When a tile is released we add it to the list and simply + * place the next pointer at its offset 0. */ + + assert(mp->tile_size >= sizeof(void*)); + assert(mp->at_least > 0); + + if (mp->freelist) { + void *r; + + r = mp->freelist; + mp->freelist = * (void**) mp->freelist; + return r; + } + + if (_unlikely_(!mp->first_pool) || + _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { + unsigned n; + size_t size; + struct pool *p; + + n = mp->first_pool ? mp->first_pool->n_tiles : 0; + n = MAX(mp->at_least, n * 2); + size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); + n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; + + p = malloc(size); + if (!p) + return NULL; + + p->next = mp->first_pool; + p->n_tiles = n; + p->n_used = 0; + + mp->first_pool = p; + } + + i = mp->first_pool->n_used++; + + return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; +} + +void* mempool_alloc0_tile(struct mempool *mp) { + void *p; + + p = mempool_alloc_tile(mp); + if (p) + memzero(p, mp->tile_size); + return p; +} + +void mempool_free_tile(struct mempool *mp, void *p) { + * (void**) p = mp->freelist; + mp->freelist = p; +} + +#ifdef VALGRIND + +void mempool_drop(struct mempool *mp) { + struct pool *p = mp->first_pool; + while (p) { + struct pool *n; + n = p->next; + free(p); + p = n; + } +} + +#endif diff --git a/src/libbasic/src/mkdir-label.c b/src/libbasic/src/mkdir-label.c new file mode 100644 index 0000000000..83a1e925c6 --- /dev/null +++ b/src/libbasic/src/mkdir-label.c @@ -0,0 +1,38 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/label.h" +#include "basic/mkdir.h" + +int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) { + return mkdir_safe_internal(path, mode, uid, gid, mkdir_label); +} + +int mkdir_parents_label(const char *path, mode_t mode) { + return mkdir_parents_internal(NULL, path, mode, mkdir_label); +} + +int mkdir_p_label(const char *path, mode_t mode) { + return mkdir_p_internal(NULL, path, mode, mkdir_label); +} diff --git a/src/libbasic/src/mkdir.c b/src/libbasic/src/mkdir.c new file mode 100644 index 0000000000..0a7f432f3e --- /dev/null +++ b/src/libbasic/src/mkdir.c @@ -0,0 +1,128 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/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; + + if (_mkdir(path, mode) >= 0) + if (chmod_and_chown(path, mode, uid, gid) < 0) + return -errno; + + if (lstat(path, &st) < 0) + return -errno; + + if ((st.st_mode & 0007) > (mode & 0007) || + (st.st_mode & 0070) > (mode & 0070) || + (st.st_mode & 0700) > (mode & 0700) || + (uid != UID_INVALID && st.st_uid != uid) || + (gid != GID_INVALID && st.st_gid != gid) || + !S_ISDIR(st.st_mode)) + return -EEXIST; + + return 0; +} + +int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) { + return mkdir_safe_internal(path, mode, uid, gid, mkdir); +} + +int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { + const char *p, *e; + int r; + + assert(path); + + if (prefix && !path_startswith(path, prefix)) + return -ENOTDIR; + + /* return immediately if directory exists */ + e = strrchr(path, '/'); + if (!e) + return -EINVAL; + + if (e == path) + return 0; + + p = strndupa(path, e - path); + r = is_dir(p, true); + if (r > 0) + return 0; + if (r == 0) + return -ENOTDIR; + + /* create every parent directory in the path, except the last component */ + p = path + strspn(path, "/"); + for (;;) { + char t[strlen(path) + 1]; + + e = p + strcspn(p, "/"); + p = e + strspn(e, "/"); + + /* Is this the last component? If so, then we're + * done */ + if (*p == 0) + return 0; + + memcpy(t, path, e - path); + t[e-path] = 0; + + if (prefix && path_startswith(prefix, t)) + continue; + + r = _mkdir(t, mode); + if (r < 0 && errno != EEXIST) + return -errno; + } +} + +int mkdir_parents(const char *path, mode_t mode) { + return mkdir_parents_internal(NULL, path, mode, mkdir); +} + +int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { + int r; + + /* Like mkdir -p */ + + r = mkdir_parents_internal(prefix, path, mode, _mkdir); + if (r < 0) + return r; + + r = _mkdir(path, mode); + if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) + return -errno; + + return 0; +} + +int mkdir_p(const char *path, mode_t mode) { + return mkdir_p_internal(NULL, path, mode, mkdir); +} diff --git a/src/libbasic/src/mount-util.c b/src/libbasic/src/mount-util.c new file mode 100644 index 0000000000..feb75a2c04 --- /dev/null +++ b/src/libbasic/src/mount-util.c @@ -0,0 +1,559 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/stdio-util.h" +#include "basic/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_CLOEXEC|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/. 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 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_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; + + /* Deal with mount points that are obstructed by a + * later mount */ + r = path_is_mount_point(x, 0); + if (r == -ENOENT || r == 0) + continue; + if (r < 0) + return r; + + /* Try to reuse the original flag set */ + 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) + 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" + "pvfs2\0" /* OrangeFS */ + "ocfs2\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; + } +} + +const char* mode_to_inaccessible_node(mode_t mode) { + /* This function maps a node type to the correspondent inaccessible node type. + * Character and block inaccessible devices may not be created (because major=0 and minor=0), + * in such case we map character and block devices to the inaccessible node type socket. */ + switch(mode & S_IFMT) { + case S_IFREG: + return "/run/systemd/inaccessible/reg"; + case S_IFDIR: + return "/run/systemd/inaccessible/dir"; + case S_IFCHR: + if (access("/run/systemd/inaccessible/chr", F_OK) == 0) + return "/run/systemd/inaccessible/chr"; + return "/run/systemd/inaccessible/sock"; + case S_IFBLK: + if (access("/run/systemd/inaccessible/blk", F_OK) == 0) + return "/run/systemd/inaccessible/blk"; + return "/run/systemd/inaccessible/sock"; + case S_IFIFO: + return "/run/systemd/inaccessible/fifo"; + case S_IFSOCK: + return "/run/systemd/inaccessible/sock"; + } + return NULL; +} diff --git a/src/libbasic/src/ordered-set.c b/src/libbasic/src/ordered-set.c new file mode 100644 index 0000000000..fc6c3c018c --- /dev/null +++ b/src/libbasic/src/ordered-set.c @@ -0,0 +1,64 @@ +/*** + 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 . +***/ + +#include "basic/ordered-set.h" +#include "basic/strv.h" + +int ordered_set_consume(OrderedSet *s, void *p) { + int r; + + r = ordered_set_put(s, p); + if (r <= 0) + free(p); + + return r; +} + +int ordered_set_put_strdup(OrderedSet *s, const char *p) { + char *c; + int r; + + assert(s); + assert(p); + + c = strdup(p); + if (!c) + return -ENOMEM; + + r = ordered_set_consume(s, c); + if (r == -EEXIST) + return 0; + + return r; +} + +int ordered_set_put_strdupv(OrderedSet *s, char **l) { + int n = 0, r; + char **i; + + STRV_FOREACH(i, l) { + r = ordered_set_put_strdup(s, *i); + if (r < 0) + return r; + + n += r; + } + + return n; +} diff --git a/src/libbasic/src/parse-util.c b/src/libbasic/src/parse-util.c new file mode 100644 index 0000000000..08e555d870 --- /dev/null +++ b/src/libbasic/src/parse-util.c @@ -0,0 +1,553 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/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; +} + +int parse_percent(const char *p) { + const char *pc, *n; + unsigned v; + int r; + + pc = endswith(p, "%"); + if (!pc) + return -EINVAL; + + n = strndupa(p, pc - p); + r = safe_atou(n, &v); + if (r < 0) + return r; + if (v > 100) + return -ERANGE; + + return (int) v; +} diff --git a/src/libbasic/src/path-util.c b/src/libbasic/src/path-util.c new file mode 100644 index 0000000000..143380dec2 --- /dev/null +++ b/src/libbasic/src/path-util.c @@ -0,0 +1,816 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#undef basename + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/path-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" + +bool path_is_absolute(const char *p) { + return p[0] == '/'; +} + +bool is_path(const char *p) { + return !!strchr(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 -ENOMEM; + + r = path_strv_make_absolute_cwd(l); + if (r < 0) { + strv_free(l); + return r; + } + + *ret = l; + return r; +} + +char *path_make_absolute(const char *p, const char *prefix) { + assert(p); + + /* Makes every item in the list an absolute path by prepending + * the prefix, if specified and necessary */ + + if (path_is_absolute(p) || !prefix) + return strdup(p); + + return strjoin(prefix, "/", p, 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)) + c = strdup(p); + else { + _cleanup_free_ char *cwd = NULL; + + cwd = get_current_dir_name(); + if (!cwd) + return negative_errno(); + + c = strjoin(cwd, "/", p, NULL); + } + if (!c) + return -ENOMEM; + + *ret = c; + return 0; +} + +int path_make_relative(const char *from_dir, const char *to_path, char **_r) { + char *r, *p; + unsigned n_parents; + + assert(from_dir); + assert(to_path); + assert(_r); + + /* Strips the common part, and adds ".." elements as necessary. */ + + if (!path_is_absolute(from_dir)) + return -EINVAL; + + if (!path_is_absolute(to_path)) + return -EINVAL; + + /* Skip the common part. */ + for (;;) { + size_t a; + size_t b; + + from_dir += strspn(from_dir, "/"); + to_path += strspn(to_path, "/"); + + if (!*from_dir) { + if (!*to_path) + /* from_dir equals to_path. */ + r = strdup("."); + else + /* from_dir is a parent directory of to_path. */ + r = strdup(to_path); + + if (!r) + return -ENOMEM; + + path_kill_slashes(r); + + *_r = r; + return 0; + } + + if (!*to_path) + break; + + a = strcspn(from_dir, "/"); + b = strcspn(to_path, "/"); + + if (a != b) + break; + + if (memcmp(from_dir, to_path, a) != 0) + break; + + from_dir += a; + to_path += b; + } + + /* If we're here, then "from_dir" has one or more elements that need to + * be replaced with "..". */ + + /* Count the number of necessary ".." elements. */ + for (n_parents = 0;;) { + from_dir += strspn(from_dir, "/"); + + if (!*from_dir) + break; + + from_dir += strcspn(from_dir, "/"); + n_parents++; + } + + r = malloc(n_parents * 3 + strlen(to_path) + 1); + if (!r) + return -ENOMEM; + + for (p = r; n_parents > 0; n_parents--, p += 3) + memcpy(p, "../", 3); + + strcpy(p, to_path); + path_kill_slashes(r); + + *_r = r; + return 0; +} + +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 + * changes on failure. */ + + STRV_FOREACH(s, l) { + char *t; + + r = path_make_absolute_cwd(*s, &t); + if (r < 0) + return r; + + free(*s); + *s = t; + } + + return 0; +} + +char **path_strv_resolve(char **l, const char *prefix) { + char **s; + unsigned k = 0; + bool enomem = false; + + if (strv_isempty(l)) + return l; + + /* Goes through every item in the string list and canonicalize + * the path. This works in place and won't rollback any + * changes on failure. */ + + STRV_FOREACH(s, l) { + char *t, *u; + _cleanup_free_ char *orig = NULL; + + if (!path_is_absolute(*s)) { + free(*s); + continue; + } + + if (prefix) { + orig = *s; + t = strappend(prefix, orig); + if (!t) { + enomem = true; + continue; + } + } else + t = *s; + + errno = 0; + u = canonicalize_file_name(t); + if (!u) { + if (errno == ENOENT) { + if (prefix) { + u = orig; + orig = NULL; + free(t); + } else + u = t; + } else { + free(t); + if (errno == ENOMEM || errno == 0) + enomem = true; + + continue; + } + } else if (prefix) { + char *x; + + free(t); + x = path_startswith(u, prefix); + if (x) { + /* restore the slash if it was lost */ + if (!startswith(x, "/")) + *(--x) = '/'; + + t = strdup(x); + free(u); + if (!t) { + enomem = true; + continue; + } + u = t; + } else { + /* canonicalized path goes outside of + * prefix, keep the original path instead */ + free(u); + u = orig; + orig = NULL; + } + } else + free(t); + + l[k++] = u; + } + + l[k] = NULL; + + if (enomem) + return NULL; + + return l; +} + +char **path_strv_resolve_uniq(char **l, const char *prefix) { + + if (strv_isempty(l)) + return l; + + if (!path_strv_resolve(l, prefix)) + return NULL; + + return strv_uniq(l); +} + +char *path_kill_slashes(char *path) { + char *f, *t; + bool slash = false; + + /* Removes redundant inner and trailing slashes. Modifies the + * passed string in-place. + * + * ///foo///bar/ becomes /foo/bar + */ + + for (f = path, t = path; *f; f++) { + + if (*f == '/') { + slash = true; + continue; + } + + if (slash) { + slash = false; + *(t++) = '/'; + } + + *(t++) = *f; + } + + /* Special rule, if we are talking of the root directory, a + trailing slash is good */ + + if (t == path && slash) + *(t++) = '/'; + + *t = 0; + return path; +} + +char* path_startswith(const char *path, const char *prefix) { + assert(path); + assert(prefix); + + if ((path[0] == '/') != (prefix[0] == '/')) + return NULL; + + for (;;) { + size_t a, b; + + path += strspn(path, "/"); + prefix += strspn(prefix, "/"); + + if (*prefix == 0) + return (char*) path; + + if (*path == 0) + return NULL; + + a = strcspn(path, "/"); + b = strcspn(prefix, "/"); + + if (a != b) + return NULL; + + if (memcmp(path, prefix, a) != 0) + return NULL; + + path += a; + prefix += b; + } +} + +int path_compare(const char *a, const char *b) { + int d; + + assert(a); + assert(b); + + /* A relative path and an abolute path must not compare as equal. + * Which one is sorted before the other does not really matter. + * Here a relative path is ordered before an absolute path. */ + d = (a[0] == '/') - (b[0] == '/'); + if (d != 0) + return d; + + for (;;) { + size_t j, k; + + a += strspn(a, "/"); + b += strspn(b, "/"); + + if (*a == 0 && *b == 0) + return 0; + + /* Order prefixes first: "/foo" before "/foo/bar" */ + if (*a == 0) + return -1; + if (*b == 0) + return 1; + + j = strcspn(a, "/"); + k = strcspn(b, "/"); + + /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ + d = memcmp(a, b, MIN(j, k)); + 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 != 0) + return d; + + a += j; + b += k; + } +} + +bool path_equal(const char *a, const char *b) { + return path_compare(a, b) == 0; +} + +bool path_equal_or_files_same(const char *a, const char *b) { + return path_equal(a, b) || files_same(a, b) > 0; +} + +char* path_join(const char *root, const char *path, const char *rest) { + assert(path); + + if (!isempty(root)) + return strjoin(root, endswith(root, "/") ? "" : "/", + path[0] == '/' ? path+1 : path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, + rest && rest[0] == '/' ? rest+1 : rest, + NULL); + else + return strjoin(path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, + rest && rest[0] == '/' ? rest+1 : rest, + NULL); +} + +int find_binary(const char *name, char **ret) { + int last_error, r; + const char *p; + + assert(name); + + if (is_path(name)) { + if (access(name, X_OK) < 0) + return -errno; + + if (ret) { + r = path_make_absolute_cwd(name, ret); + if (r < 0) + return r; + } + + return 0; + } + + /** + * 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; + + last_error = -ENOENT; + + for (;;) { + _cleanup_free_ char *j = NULL, *element = NULL; + + r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; + + if (!path_is_absolute(element)) + continue; + + j = strjoin(element, "/", name, NULL); + if (!j) + return -ENOMEM; + + if (access(j, X_OK) >= 0) { + /* Found it! */ + + if (ret) { + *ret = path_kill_slashes(j); + j = NULL; + } + + return 0; + } + + last_error = -errno; + } + + return last_error; +} + +bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { + bool changed = false; + const char* const* i; + + assert(timestamp); + + if (paths == NULL) + return false; + + STRV_FOREACH(i, paths) { + struct stat stats; + usec_t u; + + if (stat(*i, &stats) < 0) + continue; + + u = timespec_load(&stats.st_mtim); + + /* first check */ + if (*timestamp >= u) + continue; + + log_debug("timestamp of '%s' changed", *i); + + /* update timestamp */ + if (update) { + *timestamp = u; + changed = true; + } else + return true; + } + + return changed; +} + +static int binary_is_good(const char *binary) { + _cleanup_free_ char *p = NULL, *d = NULL; + int r; + + r = find_binary(binary, &p); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + /* An fsck that is linked to /bin/true is a non-existent + * fsck */ + + r = readlink_malloc(p, &d); + if (r == -EINVAL) /* not a symlink */ + return 1; + if (r < 0) + return r; + + return !PATH_IN_SET(d, "true" + "/bin/true", + "/usr/bin/true", + "/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) { + char *n, *p; + size_t l; + + /* If root is passed, prefixes path with it. Otherwise returns + * it as is. */ + + assert(path); + + /* First, drop duplicate prefixing slashes from the path */ + while (path[0] == '/' && path[1] == '/') + path++; + + if (isempty(root) || path_equal(root, "/")) + return strdup(path); + + l = strlen(root) + 1 + strlen(path) + 1; + + n = new(char, l); + if (!n) + return NULL; + + p = stpcpy(n, root); + + while (p > n && p[-1] == '/') + p--; + + if (path[0] != '/') + *(p++) = '/'; + + strcpy(p, path); + return n; +} + +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_or_backup_file(const char *filename) { + const char *p; + + assert(filename); + + if (filename[0] == '.' || + streq(filename, "lost+found") || + streq(filename, "aquota.user") || + streq(filename, "aquota.group") || + endswith(filename, "~")) + return true; + + p = strrchr(filename, '.'); + if (!p) + return false; + + /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up + * with always new suffixes and that everybody else should just adjust to that, then it really should be on + * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt + * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional + * string. Specifically: there's now: + * + * The generic suffixes "~" and ".bak" for backup files + * The generic prefix "." for hidden files + * + * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" + * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. + */ + + return STR_IN_SET(p + 1, + "rpmnew", + "rpmsave", + "rpmorig", + "dpkg-old", + "dpkg-new", + "dpkg-tmp", + "dpkg-dist", + "dpkg-bak", + "dpkg-backup", + "dpkg-remove", + "ucf-new", + "ucf-old", + "ucf-dist", + "swp", + "bak", + "old", + "new"); +} + +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/libbasic/src/prioq.c b/src/libbasic/src/prioq.c new file mode 100644 index 0000000000..f29c80b5fc --- /dev/null +++ b/src/libbasic/src/prioq.c @@ -0,0 +1,320 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* + * 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 +#include + +#include "basic/alloc-util.h" +#include "basic/hashmap.h" +#include "basic/prioq.h" + +struct prioq_item { + void *data; + unsigned *idx; +}; + +struct Prioq { + compare_func_t compare_func; + unsigned n_items, n_allocated; + + struct prioq_item *items; +}; + +Prioq *prioq_new(compare_func_t compare_func) { + Prioq *q; + + q = new0(Prioq, 1); + if (!q) + return q; + + q->compare_func = compare_func; + return q; +} + +Prioq* prioq_free(Prioq *q) { + if (!q) + return NULL; + + free(q->items); + free(q); + + return NULL; +} + +int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { + assert(q); + + if (*q) + return 0; + + *q = prioq_new(compare_func); + if (!*q) + return -ENOMEM; + + return 0; +} + +static void swap(Prioq *q, unsigned j, unsigned k) { + void *saved_data; + unsigned *saved_idx; + + assert(q); + assert(j < q->n_items); + assert(k < q->n_items); + + assert(!q->items[j].idx || *(q->items[j].idx) == j); + assert(!q->items[k].idx || *(q->items[k].idx) == k); + + saved_data = q->items[j].data; + saved_idx = q->items[j].idx; + q->items[j].data = q->items[k].data; + q->items[j].idx = q->items[k].idx; + q->items[k].data = saved_data; + q->items[k].idx = saved_idx; + + if (q->items[j].idx) + *q->items[j].idx = j; + + if (q->items[k].idx) + *q->items[k].idx = k; +} + +static unsigned shuffle_up(Prioq *q, unsigned idx) { + assert(q); + + while (idx > 0) { + unsigned k; + + k = (idx-1)/2; + + if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) + break; + + swap(q, idx, k); + idx = k; + } + + return idx; +} + +static unsigned shuffle_down(Prioq *q, unsigned idx) { + assert(q); + + for (;;) { + unsigned j, k, s; + + k = (idx+1)*2; /* right child */ + j = k-1; /* left child */ + + if (j >= q->n_items) + break; + + if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) + + /* So our left child is smaller than we are, let's + * remember this fact */ + s = j; + else + s = idx; + + if (k < q->n_items && + q->compare_func(q->items[k].data, q->items[s].data) < 0) + + /* So our right child is smaller than we are, let's + * remember this fact */ + s = k; + + /* s now points to the smallest of the three items */ + + if (s == idx) + /* No swap necessary, we're done */ + break; + + swap(q, idx, s); + idx = s; + } + + return idx; +} + +int prioq_put(Prioq *q, void *data, unsigned *idx) { + struct prioq_item *i; + unsigned k; + + assert(q); + + if (q->n_items >= q->n_allocated) { + unsigned n; + struct prioq_item *j; + + n = MAX((q->n_items+1) * 2, 16u); + j = realloc(q->items, sizeof(struct prioq_item) * n); + if (!j) + return -ENOMEM; + + q->items = j; + q->n_allocated = n; + } + + k = q->n_items++; + i = q->items + k; + i->data = data; + i->idx = idx; + + if (idx) + *idx = k; + + shuffle_up(q, k); + + return 0; +} + +static void remove_item(Prioq *q, struct prioq_item *i) { + struct prioq_item *l; + + assert(q); + assert(i); + + l = q->items + q->n_items - 1; + + if (i == l) + /* Last entry, let's just remove it */ + q->n_items--; + else { + unsigned k; + + /* Not last entry, let's replace the last entry with + * this one, and reshuffle */ + + k = i - q->items; + + i->data = l->data; + i->idx = l->idx; + if (i->idx) + *i->idx = k; + q->n_items--; + + k = shuffle_down(q, k); + shuffle_up(q, k); + } +} + +_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { + struct prioq_item *i; + + assert(q); + + if (idx) { + if (*idx == PRIOQ_IDX_NULL || + *idx > q->n_items) + return NULL; + + i = q->items + *idx; + if (i->data != data) + return NULL; + + return i; + } else { + for (i = q->items; i < q->items + q->n_items; i++) + if (i->data == data) + return i; + return NULL; + } +} + +int prioq_remove(Prioq *q, void *data, unsigned *idx) { + struct prioq_item *i; + + if (!q) + return 0; + + i = find_item(q, data, idx); + if (!i) + return 0; + + remove_item(q, i); + return 1; +} + +int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { + struct prioq_item *i; + unsigned k; + + assert(q); + + i = find_item(q, data, idx); + if (!i) + return 0; + + k = i - q->items; + k = shuffle_down(q, k); + shuffle_up(q, k); + return 1; +} + +void *prioq_peek(Prioq *q) { + + if (!q) + return NULL; + + if (q->n_items <= 0) + return NULL; + + return q->items[0].data; +} + +void *prioq_pop(Prioq *q) { + void *data; + + if (!q) + return NULL; + + if (q->n_items <= 0) + return NULL; + + data = q->items[0].data; + remove_item(q, q->items); + return data; +} + +unsigned prioq_size(Prioq *q) { + + if (!q) + return 0; + + return q->n_items; +} + +bool prioq_isempty(Prioq *q) { + + if (!q) + return true; + + return q->n_items <= 0; +} diff --git a/src/libbasic/src/proc-cmdline.c b/src/libbasic/src/proc-cmdline.c new file mode 100644 index 0000000000..db2acbee75 --- /dev/null +++ b/src/libbasic/src/proc-cmdline.c @@ -0,0 +1,188 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/process-util.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/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, + NULL +}; + +static const char * const rlmap_initrd[] = { + "emergency", SPECIAL_EMERGENCY_TARGET, + "rescue", SPECIAL_RESCUE_TARGET, + NULL +}; + +const char* runlevel_to_target(const char *word) { + size_t i; + const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd + : rlmap; + + if (!word) + return NULL; + + if (in_initrd() && (word = startswith(word, "rd.")) == NULL) + return NULL; + + for (i = 0; rlmap_ptr[i] != NULL; i += 2) + if (streq(word, rlmap_ptr[i])) + return rlmap_ptr[i+1]; + + return NULL; +} diff --git a/src/libbasic/src/process-util.c b/src/libbasic/src/process-util.c new file mode 100644 index 0000000000..5dd5279374 --- /dev/null +++ b/src/libbasic/src/process-util.c @@ -0,0 +1,861 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_VALGRIND_VALGRIND_H +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/ioprio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/process-util.h" +#include "basic/raw-clone.h" +#include "basic/signal-util.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +int get_process_state(pid_t pid) { + const char *p; + char state; + int r; + _cleanup_free_ char *line = NULL; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "stat"); + + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + if (sscanf(p, " %c", &state) != 1) + return -EIO; + + return (unsigned char) state; +} + +int get_process_comm(pid_t pid, char **name) { + const char *p; + int r; + + assert(name); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "comm"); + + r = read_one_line_file(p, name); + if (r == -ENOENT) + return -ESRCH; + + return r; +} + +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + _cleanup_fclose_ FILE *f = NULL; + bool space = false; + char *r = NULL, *k; + const char *p; + int c; + + assert(line); + assert(pid >= 0); + + /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing + * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most + * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If + * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a + * command line that resolves to the empty string will return the "comm" name of the process instead. + * + * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and + * comm_fallback is false). */ + + p = procfs_file_alloca(pid, "cmdline"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + if (max_length == 1) { + + /* If there's only room for one byte, return the empty string */ + r = new0(char, 1); + if (!r) + return -ENOMEM; + + *line = r; + return 0; + + } else if (max_length == 0) { + size_t len = 0, allocated = 0; + + while ((c = getc(f)) != EOF) { + + if (!GREEDY_REALLOC(r, allocated, len+3)) { + free(r); + return -ENOMEM; + } + + if (isprint(c)) { + if (space) { + r[len++] = ' '; + space = false; + } + + r[len++] = c; + } else if (len > 0) + space = true; + } + + if (len > 0) + r[len] = 0; + else + r = mfree(r); + + } else { + bool dotdotdot = false; + size_t left; + + r = new(char, max_length); + if (!r) + return -ENOMEM; + + k = r; + left = max_length; + while ((c = getc(f)) != EOF) { + + if (isprint(c)) { + + if (space) { + if (left <= 2) { + dotdotdot = true; + break; + } + + *(k++) = ' '; + left--; + space = false; + } + + if (left <= 1) { + dotdotdot = true; + break; + } + + *(k++) = (char) c; + left--; + } else if (k > r) + space = true; + } + + if (dotdotdot) { + if (max_length <= 4) { + k = r; + left = max_length; + } else { + k = r + max_length - 4; + left = 4; + + /* Eat up final spaces */ + while (k > r && isspace(k[-1])) { + k--; + left++; + } + } + + strncpy(k, "...", left-1); + k[left-1] = 0; + } else + *k = 0; + } + + /* Kernel threads have no argv[] */ + if (isempty(r)) { + _cleanup_free_ char *t = NULL; + int h; + + free(r); + + if (!comm_fallback) + return -ENOENT; + + h = get_process_comm(pid, &t); + if (h < 0) + return h; + + if (max_length == 0) + r = strjoin("[", t, "]", NULL); + else { + size_t l; + + l = strlen(t); + + if (l + 3 <= max_length) + r = strjoin("[", t, "]", NULL); + else if (max_length <= 6) { + + r = new(char, max_length); + if (!r) + return -ENOMEM; + + memcpy(r, "[...]", max_length-1); + r[max_length-1] = 0; + } else { + char *e; + + t[max_length - 6] = 0; + + /* Chop off final spaces */ + e = strchr(t, 0); + while (e > t && isspace(e[-1])) + e--; + *e = 0; + + r = strjoin("[", t, "...]", NULL); + } + } + if (!r) + return -ENOMEM; + } + + *line = r; + 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 */ + + (void) 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; + char c; + bool eof; + FILE *f; + + if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ + return 0; + + assert(pid > 1); + + p = procfs_file_alloca(pid, "cmdline"); + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + count = fread(&c, 1, 1, f); + eof = feof(f); + fclose(f); + + /* Kernel threads have an empty cmdline */ + + if (count <= 0) + return eof ? 1 : -errno; + + return 0; +} + +int get_process_capeff(pid_t pid, char **capeff) { + const char *p; + int r; + + assert(capeff); + assert(pid >= 0); + + p = procfs_file_alloca(pid, "status"); + + r = get_proc_field(p, "CapEff", WHITESPACE, capeff); + if (r == -ENOENT) + return -ESRCH; + + return r; +} + +static int get_process_link_contents(const char *proc_file, char **name) { + int r; + + assert(proc_file); + assert(name); + + r = readlink_malloc(proc_file, name); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + return 0; +} + +int get_process_exe(pid_t pid, char **name) { + const char *p; + char *d; + int r; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "exe"); + r = get_process_link_contents(p, name); + if (r < 0) + return r; + + d = endswith(*name, " (deleted)"); + if (d) + *d = '\0'; + + return 0; +} + +static int get_process_id(pid_t pid, const char *field, uid_t *uid) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + const char *p; + + assert(field); + assert(uid); + + p = procfs_file_alloca(pid, "status"); + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + FOREACH_LINE(line, f, return -errno) { + char *l; + + l = strstrip(line); + + if (startswith(l, field)) { + l += strlen(field); + l += strspn(l, WHITESPACE); + + l[strcspn(l, WHITESPACE)] = 0; + + return parse_uid(l, uid); + } + } + + return -EIO; +} + +int get_process_uid(pid_t pid, uid_t *uid) { + return get_process_id(pid, "Uid:", uid); +} + +int get_process_gid(pid_t pid, gid_t *gid) { + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + return get_process_id(pid, "Gid:", gid); +} + +int get_process_cwd(pid_t pid, char **cwd) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "cwd"); + + return get_process_link_contents(p, cwd); +} + +int get_process_root(pid_t pid, char **root) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "root"); + + return get_process_link_contents(p, root); +} + +int get_process_environ(pid_t pid, char **env) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *outcome = NULL; + int c; + const char *p; + size_t allocated = 0, sz = 0; + + assert(pid >= 0); + assert(env); + + p = procfs_file_alloca(pid, "environ"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + while ((c = fgetc(f)) != EOF) { + if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) + return -ENOMEM; + + if (c == '\0') + outcome[sz++] = '\n'; + else + sz += cescape_char(c, outcome + sz); + } + + if (!outcome) { + outcome = strdup(""); + if (!outcome) + return -ENOMEM; + } else + outcome[sz] = '\0'; + + *env = outcome; + outcome = NULL; + + return 0; +} + +int get_process_ppid(pid_t pid, pid_t *_ppid) { + int r; + _cleanup_free_ char *line = NULL; + long unsigned ppid; + const char *p; + + assert(pid >= 0); + assert(_ppid); + + if (pid == 0) { + *_ppid = getppid(); + return 0; + } + + p = procfs_file_alloca(pid, "stat"); + r = read_one_line_file(p, &line); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) + return r; + + /* Let's skip the pid and comm fields. The latter is enclosed + * in () but does not escape any () in its value, so let's + * skip over it manually */ + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%lu ", /* ppid */ + &ppid) != 1) + return -EIO; + + if ((long unsigned) (pid_t) ppid != ppid) + return -ERANGE; + + *_ppid = (pid_t) ppid; + + return 0; +} + +int wait_for_terminate(pid_t pid, siginfo_t *status) { + siginfo_t dummy; + + assert(pid >= 1); + + if (!status) + status = &dummy; + + for (;;) { + zero(*status); + + if (waitid(P_PID, pid, status, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + return negative_errno(); + } + + return 0; + } +} + +/* + * Return values: + * < 0 : wait_for_terminate() failed to get the state of the + * process, the process was terminated by a signal, or + * failed for an unknown reason. + * >=0 : The process terminated normally, and its exit code is + * returned. + * + * That is, success is indicated by a return value of zero, and an + * error is indicated by a non-zero value. + * + * A warning is emitted if the process terminates abnormally, + * and also if it returns non-zero unless check_exit_code is true. + */ +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { + int r; + siginfo_t status; + + assert(name); + assert(pid > 1); + + r = wait_for_terminate(pid, &status); + if (r < 0) + return log_warning_errno(r, "Failed to wait for %s: %m", name); + + if (status.si_code == CLD_EXITED) { + if (status.si_status != 0) + log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, + "%s failed with error code %i.", name, status.si_status); + else + log_debug("%s succeeded.", name); + + return status.si_status; + } else if (status.si_code == CLD_KILLED || + status.si_code == CLD_DUMPED) { + + log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + return -EPROTO; + } + + log_warning("%s failed due to unknown reason.", name); + return -EPROTO; +} + +void sigkill_wait(pid_t pid) { + assert(pid > 1); + + if (kill(pid, SIGKILL) > 0) + (void) wait_for_terminate(pid, NULL); +} + +void sigkill_waitp(pid_t *pid) { + if (!pid) + return; + if (*pid <= 1) + return; + + sigkill_wait(*pid); +} + +int kill_and_sigcont(pid_t pid, int sig) { + int r; + + r = kill(pid, sig) < 0 ? -errno : 0; + + /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't + * affected by a process being suspended anyway. */ + if (r >= 0 && !IN_SET(SIGCONT, SIGKILL)) + (void) kill(pid, SIGCONT); + + return r; +} + +int getenv_for_pid(pid_t pid, const char *field, char **_value) { + _cleanup_fclose_ FILE *f = NULL; + char *value = NULL; + int r; + bool done = false; + size_t l; + const char *path; + + assert(pid >= 0); + assert(field); + assert(_value); + + path = procfs_file_alloca(pid, "environ"); + + f = fopen(path, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + return -errno; + } + + l = strlen(field); + r = 0; + + do { + char line[LINE_MAX]; + unsigned i; + + for (i = 0; i < sizeof(line)-1; i++) { + int c; + + c = getc(f); + if (_unlikely_(c == EOF)) { + done = true; + break; + } else if (c == 0) + break; + + line[i] = c; + } + line[i] = 0; + + if (memcmp(line, field, l) == 0 && line[l] == '=') { + value = strdup(line + l + 1); + if (!value) + return -ENOMEM; + + r = 1; + break; + } + + } while (!done); + + *_value = value; + return r; +} + +bool pid_is_unwaited(pid_t pid) { + /* Checks whether a PID is still valid at all, including a zombie */ + + if (pid < 0) + return false; + + if (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; + + return errno != ESRCH; +} + +bool pid_is_alive(pid_t pid) { + int r; + + /* Checks whether a PID is still valid and not a zombie */ + + if (pid < 0) + return false; + + 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; +} + +int pid_from_same_root_fs(pid_t pid) { + const char *root; + + if (pid < 0) + return 0; + + root = procfs_file_alloca(pid, "root"); + + return files_same(root, "/proc/1/root"); +} + +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) { + + log_close(); + + /* 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) { + int architecture; + + if (!p) + return PERSONALITY_INVALID; + + /* Parse a personality specifier. We use 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. */ + + architecture = architecture_from_string(p); + if (architecture < 0) + return PERSONALITY_INVALID; + + if (architecture == native_architecture()) + return PER_LINUX; +#ifdef SECONDARY_ARCHITECTURE + if (architecture == SECONDARY_ARCHITECTURE) + return PER_LINUX32; +#endif + + return PERSONALITY_INVALID; +} + +const char* personality_to_string(unsigned long p) { + int architecture = _ARCHITECTURE_INVALID; + + if (p == PER_LINUX) + architecture = native_architecture(); +#ifdef SECONDARY_ARCHITECTURE + else if (p == PER_LINUX32) + architecture = SECONDARY_ARCHITECTURE; +#endif + + if (architecture < 0) + return NULL; + + return architecture_to_string(architecture); +} + +void valgrind_summary_hack(void) { +#ifdef HAVE_VALGRIND_VALGRIND_H + if (getpid() == 1 && RUNNING_ON_VALGRIND) { + pid_t pid; + pid = raw_clone(SIGCHLD); + 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 +} + +int pid_compare_func(const void *a, const void *b) { + const pid_t *p = a, *q = b; + + /* Suitable for usage in qsort() */ + + if (*p < *q) + return -1; + if (*p > *q) + return 1; + return 0; +} + +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/libbasic/src/random-util.c b/src/libbasic/src/random-util.c new file mode 100644 index 0000000000..585e7976ac --- /dev/null +++ b/src/libbasic/src/random-util.c @@ -0,0 +1,134 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_SYS_AUXV_H +#include +#endif + +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/missing.h" +#include "basic/random-util.h" +#include "basic/time-util.h" + +int dev_urandom(void *p, size_t n) { + static int have_syscall = -1; + + _cleanup_close_ int fd = -1; + int r; + + /* Gathers some randomness from the kernel. This call will + * never block, and will always return some data from the + * kernel, regardless if the random pool is fully initialized + * or not. It thus makes no guarantee for the quality of the + * returned entropy, but is good enough for our usual usecases + * of seeding the hash functions for hashtable */ + + /* Use the getrandom() syscall unless we know we don't have + * it, or when the requested size is too large for it. */ + if (have_syscall != 0 || (size_t) (int) n != n) { + r = getrandom(p, n, GRND_NONBLOCK); + if (r == (int) n) { + have_syscall = true; + return 0; + } + + if (r < 0) { + if (errno == ENOSYS) + /* we lack the syscall, continue with + * reading from /dev/urandom */ + have_syscall = false; + else if (errno == EAGAIN) + /* not enough entropy for now. Let's + * remember to use the syscall the + * next time, again, but also read + * from /dev/urandom for now, which + * doesn't care about the current + * amount of entropy. */ + have_syscall = true; + else + return -errno; + } else + /* too short read? */ + return -ENODATA; + } + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return errno == ENOENT ? -ENOSYS : -errno; + + return loop_read_exact(fd, p, n, true); +} + +void initialize_srand(void) { + static bool srand_called = false; + unsigned x; +#ifdef HAVE_SYS_AUXV_H + void *auxv; +#endif + + if (srand_called) + return; + +#ifdef HAVE_SYS_AUXV_H + /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the + * pseudo-random generator. It's better than nothing... */ + + auxv = (void*) getauxval(AT_RANDOM); + if (auxv) { + assert_cc(sizeof(x) < 16); + memcpy(&x, auxv, sizeof(x)); + } else +#endif + x = 0; + + + x ^= (unsigned) now(CLOCK_REALTIME); + x ^= (unsigned) gettid(); + + srand(x); + srand_called = true; +} + +void random_bytes(void *p, size_t n) { + uint8_t *q; + int r; + + r = dev_urandom(p, n); + if (r >= 0) + return; + + /* If some idiot made /dev/urandom unavailable to us, he'll + * get a PRNG instead. */ + + initialize_srand(); + + for (q = p; q < (uint8_t*) p + n; q ++) + *q = rand(); +} diff --git a/src/libbasic/src/ratelimit.c b/src/libbasic/src/ratelimit.c new file mode 100644 index 0000000000..366aeb6d9a --- /dev/null +++ b/src/libbasic/src/ratelimit.c @@ -0,0 +1,56 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include + +#include "basic/macro.h" +#include "basic/ratelimit.h" + +/* Modelled after Linux' lib/ratelimit.c by Dave Young + * , which is licensed GPLv2. */ + +bool ratelimit_test(RateLimit *r) { + usec_t ts; + + assert(r); + + if (r->interval <= 0 || r->burst <= 0) + return true; + + ts = now(CLOCK_MONOTONIC); + + if (r->begin <= 0 || + r->begin + r->interval < ts) { + r->begin = ts; + + /* Reset counter */ + r->num = 0; + goto good; + } + + if (r->num < r->burst) + goto good; + + return false; + +good: + r->num++; + return true; +} diff --git a/src/libbasic/src/replace-var.c b/src/libbasic/src/replace-var.c new file mode 100644 index 0000000000..50e60221ad --- /dev/null +++ b/src/libbasic/src/replace-var.c @@ -0,0 +1,112 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/replace-var.h" +#include "basic/string-util.h" + +/* + * Generic infrastructure for replacing @FOO@ style variables in + * strings. Will call a callback for each replacement. + */ + +static int get_variable(const char *b, char **r) { + size_t k; + char *t; + + assert(b); + assert(r); + + if (*b != '@') + return 0; + + k = strspn(b + 1, UPPERCASE_LETTERS "_"); + if (k <= 0 || b[k+1] != '@') + return 0; + + t = strndup(b + 1, k); + if (!t) + return -ENOMEM; + + *r = t; + return 1; +} + +char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) { + char *r, *t; + const char *f; + size_t l; + + assert(text); + assert(lookup); + + l = strlen(text); + r = new(char, l+1); + if (!r) + return NULL; + + f = text; + t = r; + while (*f) { + _cleanup_free_ char *v = NULL, *n = NULL; + char *a; + int k; + size_t skip, d, nl; + + k = get_variable(f, &v); + if (k < 0) + goto oom; + if (k == 0) { + *(t++) = *(f++); + continue; + } + + n = lookup(v, userdata); + if (!n) + goto oom; + + skip = strlen(v) + 2; + + d = t - r; + nl = l - skip + strlen(n); + a = realloc(r, nl + 1); + if (!a) + goto oom; + + l = nl; + r = a; + t = r + d; + + t = stpcpy(t, n); + f += skip; + } + + *t = 0; + return r; + +oom: + free(r); + return NULL; +} diff --git a/src/libbasic/src/rlimit-util.c b/src/libbasic/src/rlimit-util.c new file mode 100644 index 0000000000..3a8196d9c1 --- /dev/null +++ b/src/libbasic/src/rlimit-util.c @@ -0,0 +1,321 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/extract-word.h" +#include "basic/formats-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/rlimit-util.h" +#include "basic/string-table.h" +#include "basic/time-util.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 int rlimit_parse_u64(const char *val, rlim_t *ret) { + uint64_t u; + int r; + + assert(val); + assert(ret); + + if (streq(val, "infinity")) { + *ret = RLIM_INFINITY; + return 0; + } + + /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */ + assert_cc(sizeof(rlim_t) == sizeof(uint64_t)); + + r = safe_atou64(val, &u); + if (r < 0) + return r; + if (u >= (uint64_t) RLIM_INFINITY) + return -ERANGE; + + *ret = (rlim_t) u; + return 0; +} + +static int rlimit_parse_size(const char *val, rlim_t *ret) { + uint64_t u; + int r; + + assert(val); + assert(ret); + + if (streq(val, "infinity")) { + *ret = RLIM_INFINITY; + return 0; + } + + r = parse_size(val, 1024, &u); + if (r < 0) + return r; + if (u >= (uint64_t) RLIM_INFINITY) + return -ERANGE; + + *ret = (rlim_t) u; + return 0; +} + +static int rlimit_parse_sec(const char *val, rlim_t *ret) { + uint64_t u; + usec_t t; + int r; + + assert(val); + assert(ret); + + if (streq(val, "infinity")) { + *ret = RLIM_INFINITY; + return 0; + } + + r = parse_sec(val, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) { + *ret = RLIM_INFINITY; + return 0; + } + + u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC); + if (u >= (uint64_t) RLIM_INFINITY) + return -ERANGE; + + *ret = (rlim_t) u; + return 0; +} + +static int rlimit_parse_usec(const char *val, rlim_t *ret) { + usec_t t; + int r; + + assert(val); + assert(ret); + + if (streq(val, "infinity")) { + *ret = RLIM_INFINITY; + return 0; + } + + r = parse_time(val, &t, 1); + if (r < 0) + return r; + if (t == USEC_INFINITY) { + *ret = RLIM_INFINITY; + return 0; + } + + *ret = (rlim_t) t; + return 0; +} + +static int rlimit_parse_nice(const char *val, rlim_t *ret) { + uint64_t rl; + int r; + + /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the + * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is + * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight + * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we + * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0. + * + * Yeah, Linux is quality engineering sometimes... */ + + if (val[0] == '+') { + + /* Prefixed with "+": Parse as positive user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl >= PRIO_MAX) + return -ERANGE; + + rl = 20 - rl; + + } else if (val[0] == '-') { + + /* Prefixed with "-": Parse as negative user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (-PRIO_MIN)) + return -ERANGE; + + rl = 20 + rl; + } else { + + /* Not prefixed: parse as raw resource limit value */ + r = safe_atou64(val, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (20 - PRIO_MIN)) + return -ERANGE; + } + + *ret = (rlim_t) rl; + return 0; +} + +static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = { + [RLIMIT_CPU] = rlimit_parse_sec, + [RLIMIT_FSIZE] = rlimit_parse_size, + [RLIMIT_DATA] = rlimit_parse_size, + [RLIMIT_STACK] = rlimit_parse_size, + [RLIMIT_CORE] = rlimit_parse_size, + [RLIMIT_RSS] = rlimit_parse_size, + [RLIMIT_NOFILE] = rlimit_parse_u64, + [RLIMIT_AS] = rlimit_parse_size, + [RLIMIT_NPROC] = rlimit_parse_u64, + [RLIMIT_MEMLOCK] = rlimit_parse_size, + [RLIMIT_LOCKS] = rlimit_parse_u64, + [RLIMIT_SIGPENDING] = rlimit_parse_u64, + [RLIMIT_MSGQUEUE] = rlimit_parse_size, + [RLIMIT_NICE] = rlimit_parse_nice, + [RLIMIT_RTPRIO] = rlimit_parse_u64, + [RLIMIT_RTTIME] = rlimit_parse_usec, +}; + +int rlimit_parse_one(int resource, const char *val, rlim_t *ret) { + assert(val); + assert(ret); + + if (resource < 0) + return -EINVAL; + if (resource >= _RLIMIT_MAX) + return -EINVAL; + + return rlimit_parse_table[resource](val, ret); +} + +int rlimit_parse(int resource, const char *val, struct rlimit *ret) { + _cleanup_free_ char *hard = NULL, *soft = NULL; + rlim_t hl, sl; + int r; + + assert(val); + assert(ret); + + r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + r = rlimit_parse_one(resource, soft, &sl); + if (r < 0) + return r; + + r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (!isempty(val)) + return -EINVAL; + if (r == 0) + hl = sl; + else { + r = rlimit_parse_one(resource, hard, &hl); + if (r < 0) + return r; + if (sl > hl) + return -EILSEQ; + } + + *ret = (struct rlimit) { + .rlim_cur = sl, + .rlim_max = hl, + }; + + return 0; +} + +int rlimit_format(const struct rlimit *rl, char **ret) { + char *s = NULL; + + assert(rl); + assert(ret); + + if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY) + s = strdup("infinity"); + else if (rl->rlim_cur >= RLIM_INFINITY) + (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max); + else if (rl->rlim_max >= RLIM_INFINITY) + (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur); + else if (rl->rlim_cur == rl->rlim_max) + (void) asprintf(&s, RLIM_FMT, rl->rlim_cur); + else + (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max); + + if (!s) + return -ENOMEM; + + *ret = s; + 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/libbasic/src/rm-rf.c b/src/libbasic/src/rm-rf.c new file mode 100644 index 0000000000..b05b512aee --- /dev/null +++ b/src/libbasic/src/rm-rf.c @@ -0,0 +1,236 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/btrfs-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" + +int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { + _cleanup_closedir_ DIR *d = NULL; + int ret = 0, r; + + assert(fd >= 0); + + /* This returns the first error we run into, but nevertheless + * tries to go on. This closes the passed fd. */ + + if (!(flags & REMOVE_PHYSICAL)) { + + r = fd_is_temporary_fs(fd); + if (r < 0) { + safe_close(fd); + return r; + } + + if (!r) { + /* We refuse to clean physical file systems + * with this call, unless explicitly + * requested. This is extra paranoia just to + * be sure we never ever remove non-state + * data */ + + log_error("Attempted to remove disk file system, and we can't allow that."); + safe_close(fd); + return -EPERM; + } + } + + d = fdopendir(fd); + if (!d) { + safe_close(fd); + return errno == ENOENT ? 0 : -errno; + } + + for (;;) { + struct dirent *de; + bool is_dir; + struct stat st; + + errno = 0; + de = readdir(d); + if (!de) { + if (errno > 0 && ret == 0) + ret = -errno; + return ret; + } + + if (streq(de->d_name, ".") || streq(de->d_name, "..")) + continue; + + if (de->d_type == DT_UNKNOWN || + (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { + if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + continue; + } + + is_dir = S_ISDIR(st.st_mode); + } else + is_dir = de->d_type == DT_DIR; + + if (is_dir) { + int subdir_fd; + + /* if root_dev is set, remove subdirectories only if device is same */ + if (root_dev && st.st_dev != root_dev->st_dev) + continue; + + subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (subdir_fd < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + continue; + } + + /* Stop at mount points */ + r = fd_is_mount_point(fd, de->d_name, 0); + if (r < 0) { + if (ret == 0 && r != -ENOENT) + ret = r; + + safe_close(subdir_fd); + continue; + } + if (r) { + safe_close(subdir_fd); + continue; + } + + if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { + + /* This could be a subvolume, try to remove it */ + + r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) { + if (r != -ENOTTY && r != -EINVAL) { + if (ret == 0) + ret = r; + + safe_close(subdir_fd); + continue; + } + + /* ENOTTY, then it wasn't a + * btrfs subvolume, continue + * below. */ + } else { + /* It was a subvolume, continue. */ + safe_close(subdir_fd); + continue; + } + } + + /* We pass REMOVE_PHYSICAL here, to avoid + * doing the fstatfs() to check the file + * system type again for each directory */ + r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); + if (r < 0 && ret == 0) + ret = r; + + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } + + } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { + + if (unlinkat(fd, de->d_name, 0) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } + } + } +} + +int rm_rf(const char *path, RemoveFlags flags) { + int fd, r; + struct statfs s; + + assert(path); + + /* We refuse to clean the root file system with this + * call. This is extra paranoia to never cause a really + * seriously broken system. */ + if (path_equal(path, "/")) { + log_error("Attempted to remove entire root file system, and we can't allow that."); + return -EPERM; + } + + if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) { + /* Try to remove as subvolume first */ + r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r >= 0) + return r; + + if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR) + return r; + + /* Not btrfs or not a subvolume */ + } + + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) { + + if (errno != ENOTDIR && errno != ELOOP) + return -errno; + + if (!(flags & REMOVE_PHYSICAL)) { + if (statfs(path, &s) < 0) + return -errno; + + if (!is_temporary_fs(&s)) { + log_error("Attempted to remove disk file system, and we can't allow that."); + return -EPERM; + } + } + + if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES)) + if (unlink(path) < 0 && errno != ENOENT) + return -errno; + + return 0; + } + + r = rm_rf_children(fd, flags, NULL); + + if (flags & REMOVE_ROOT) { + if (rmdir(path) < 0) { + if (r == 0 && errno != ENOENT) + r = -errno; + } + } + + return r; +} diff --git a/src/libbasic/src/selinux-util.c b/src/libbasic/src/selinux-util.c new file mode 100644 index 0000000000..d9d230c189 --- /dev/null +++ b/src/libbasic/src/selinux-util.c @@ -0,0 +1,485 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SELINUX +#include +#include +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/selinux-util.h" +#include "basic/time-util.h" +#include "basic/util.h" + +#ifdef HAVE_SELINUX +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon); +DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); + +#define _cleanup_freecon_ _cleanup_(freeconp) +#define _cleanup_context_free_ _cleanup_(context_freep) + +static int cached_use = -1; +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_have(void) { +#ifdef HAVE_SELINUX + if (cached_use < 0) + cached_use = is_selinux_enabled() > 0; + + return cached_use; +#else + return false; +#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; +#endif +} + +int mac_selinux_init(void) { + int r = 0; + +#ifdef HAVE_SELINUX + usec_t before_timestamp, after_timestamp; + struct mallinfo before_mallinfo, after_mallinfo; + + if (label_hnd) + return 0; + + if (!mac_selinux_use()) + return 0; + + before_mallinfo = mallinfo(); + before_timestamp = now(CLOCK_MONOTONIC); + + label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!label_hnd) { + log_enforcing("Failed to initialize SELinux context: %m"); + r = security_getenforce() == 1 ? -errno : 0; + } else { + char timespan[FORMAT_TIMESPAN_MAX]; + int l; + + after_timestamp = now(CLOCK_MONOTONIC); + after_mallinfo = mallinfo(); + + l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; + + log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.", + format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0), + (l+1023)/1024); + } +#endif + + return r; +} + +void mac_selinux_finish(void) { + +#ifdef HAVE_SELINUX + if (!label_hnd) + return; + + selabel_close(label_hnd); + label_hnd = NULL; +#endif +} + +int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { + +#ifdef HAVE_SELINUX + struct stat st; + int r; + + assert(path); + + /* if mac_selinux_init() wasn't called before we are a NOOP */ + if (!label_hnd) + return 0; + + r = lstat(path, &st); + if (r >= 0) { + _cleanup_freecon_ char* fcon = NULL; + + r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode); + + /* If there's no label to set, then exit without warning */ + if (r < 0 && errno == ENOENT) + return 0; + + if (r >= 0) { + r = lsetfilecon_raw(path, fcon); + + /* If the FS doesn't support labels, then exit without warning */ + if (r < 0 && errno == EOPNOTSUPP) + return 0; + } + } + + if (r < 0) { + /* Ignore ENOENT in some cases */ + if (ignore_enoent && errno == ENOENT) + return 0; + + if (ignore_erofs && errno == EROFS) + return 0; + + log_enforcing("Unable to fix SELinux security context of %s: %m", path); + if (security_getenforce() == 1) + return -errno; + } +#endif + + return 0; +} + +int mac_selinux_apply(const char *path, const char *label) { + +#ifdef HAVE_SELINUX + if (!mac_selinux_use()) + return 0; + + assert(path); + assert(label); + + if (setfilecon(path, label) < 0) { + log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path); + if (security_getenforce() > 0) + return -errno; + } +#endif + return 0; +} + +int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { + int r = -EOPNOTSUPP; + +#ifdef HAVE_SELINUX + _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; + security_class_t sclass; + + assert(exe); + assert(label); + + if (!mac_selinux_have()) + return -EOPNOTSUPP; + + r = getcon_raw(&mycon); + if (r < 0) + return -errno; + + r = getfilecon_raw(exe, &fcon); + if (r < 0) + return -errno; + + sclass = string_to_security_class("process"); + r = security_compute_create_raw(mycon, fcon, sclass, label); + if (r < 0) + return -errno; +#endif + + return r; +} + +int mac_selinux_get_our_label(char **label) { + int r = -EOPNOTSUPP; + + assert(label); + +#ifdef HAVE_SELINUX + if (!mac_selinux_have()) + return -EOPNOTSUPP; + + r = getcon_raw(label); + if (r < 0) + return -errno; +#endif + + return r; +} + +int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { + int r = -EOPNOTSUPP; + +#ifdef HAVE_SELINUX + _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; + _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; + security_class_t sclass; + const char *range = NULL; + + assert(socket_fd >= 0); + assert(exe); + assert(label); + + if (!mac_selinux_have()) + return -EOPNOTSUPP; + + r = getcon_raw(&mycon); + if (r < 0) + return -errno; + + r = getpeercon_raw(socket_fd, &peercon); + if (r < 0) + return -errno; + + if (!exec_label) { + /* If there is no context set for next exec let's use context + of target executable */ + r = getfilecon_raw(exe, &fcon); + if (r < 0) + return -errno; + } + + bcon = context_new(mycon); + if (!bcon) + return -ENOMEM; + + pcon = context_new(peercon); + if (!pcon) + return -ENOMEM; + + range = context_range_get(pcon); + if (!range) + return -errno; + + r = context_range_set(bcon, range); + if (r) + return -errno; + + freecon(mycon); + mycon = strdup(context_str(bcon)); + if (!mycon) + return -ENOMEM; + + sclass = string_to_security_class("process"); + r = security_compute_create_raw(mycon, fcon, sclass, label); + if (r < 0) + return -errno; +#endif + + return r; +} + +char* mac_selinux_free(char *label) { + +#ifdef HAVE_SELINUX + if (!label) + return NULL; + + if (!mac_selinux_have()) + return NULL; + + + freecon(label); +#endif + + return NULL; +} + +int mac_selinux_create_file_prepare(const char *path, mode_t mode) { + +#ifdef HAVE_SELINUX + _cleanup_freecon_ char *filecon = NULL; + int r; + + assert(path); + + if (!label_hnd) + return 0; + + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &filecon, path, mode); + else { + _cleanup_free_ char *newpath = NULL; + + r = path_make_absolute_cwd(path, &newpath); + if (r < 0) + return r; + + r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); + } + + if (r < 0) { + /* No context specified by the policy? Proceed without setting it. */ + if (errno == ENOENT) + return 0; + + log_enforcing("Failed to determine SELinux security context for %s: %m", path); + } else { + if (setfscreatecon_raw(filecon) >= 0) + return 0; /* Success! */ + + log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); + } + + if (security_getenforce() > 0) + return -errno; + +#endif + return 0; +} + +void mac_selinux_create_file_clear(void) { + +#ifdef HAVE_SELINUX + PROTECT_ERRNO; + + if (!mac_selinux_use()) + return; + + setfscreatecon_raw(NULL); +#endif +} + +int mac_selinux_create_socket_prepare(const char *label) { + +#ifdef HAVE_SELINUX + if (!mac_selinux_use()) + return 0; + + assert(label); + + if (setsockcreatecon(label) < 0) { + log_enforcing("Failed to set SELinux security context %s for sockets: %m", label); + + if (security_getenforce() == 1) + return -errno; + } +#endif + + return 0; +} + +void mac_selinux_create_socket_clear(void) { + +#ifdef HAVE_SELINUX + PROTECT_ERRNO; + + if (!mac_selinux_use()) + return; + + setsockcreatecon_raw(NULL); +#endif +} + +int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + + /* Binds a socket and label its file system object according to the SELinux policy */ + +#ifdef HAVE_SELINUX + _cleanup_freecon_ char *fcon = NULL; + const struct sockaddr_un *un; + bool context_changed = false; + char *path; + int r; + + assert(fd >= 0); + assert(addr); + assert(addrlen >= sizeof(sa_family_t)); + + if (!label_hnd) + goto skipped; + + /* Filter out non-local sockets */ + if (addr->sa_family != AF_UNIX) + goto skipped; + + /* Filter out anonymous sockets */ + if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1) + goto skipped; + + /* Filter out abstract namespace sockets */ + un = (const struct sockaddr_un*) addr; + if (un->sun_path[0] == 0) + goto skipped; + + path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); + + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); + else { + _cleanup_free_ char *newpath = NULL; + + 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) { + /* No context specified by the policy? Proceed without setting it */ + if (errno == ENOENT) + goto skipped; + + log_enforcing("Failed to determine SELinux security context for %s: %m", path); + if (security_getenforce() > 0) + return -errno; + + } else { + if (setfscreatecon_raw(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) < 0 ? -errno : 0; + + if (context_changed) + setfscreatecon_raw(NULL); + + return r; + +skipped: +#endif + if (bind(fd, addr, addrlen) < 0) + return -errno; + + return 0; +} diff --git a/src/libbasic/src/sigbus.c b/src/libbasic/src/sigbus.c new file mode 100644 index 0000000000..7249f812d7 --- /dev/null +++ b/src/libbasic/src/sigbus.c @@ -0,0 +1,152 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/macro.h" +#include "basic/sigbus.h" +#include "basic/util.h" + +#define SIGBUS_QUEUE_MAX 64 + +static struct sigaction old_sigaction; +static unsigned n_installed = 0; + +/* We maintain a fixed size list of page addresses that triggered a + SIGBUS. We access with list with atomic operations, so that we + don't have to deal with locks between signal handler and main + programs in possibly multiple threads. */ + +static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX]; +static volatile sig_atomic_t n_sigbus_queue = 0; + +static void sigbus_push(void *addr) { + unsigned u; + + assert(addr); + + /* Find a free place, increase the number of entries and leave, if we can */ + for (u = 0; u < SIGBUS_QUEUE_MAX; u++) + if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) { + __sync_fetch_and_add(&n_sigbus_queue, 1); + return; + } + + /* If we can't, make sure the queue size is out of bounds, to + * mark it as overflow */ + for (;;) { + unsigned c; + + __sync_synchronize(); + c = n_sigbus_queue; + + if (c > SIGBUS_QUEUE_MAX) /* already overflow */ + return; + + if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX)) + return; + } +} + +int sigbus_pop(void **ret) { + assert(ret); + + for (;;) { + unsigned u, c; + + __sync_synchronize(); + c = n_sigbus_queue; + + if (_likely_(c == 0)) + return 0; + + if (_unlikely_(c >= SIGBUS_QUEUE_MAX)) + return -EOVERFLOW; + + for (u = 0; u < SIGBUS_QUEUE_MAX; u++) { + void *addr; + + addr = sigbus_queue[u]; + if (!addr) + continue; + + if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) { + __sync_fetch_and_sub(&n_sigbus_queue, 1); + *ret = addr; + return 1; + } + } + } +} + +static void sigbus_handler(int sn, siginfo_t *si, void *data) { + unsigned long ul; + void *aligned; + + assert(sn == SIGBUS); + assert(si); + + if (si->si_code != BUS_ADRERR || !si->si_addr) { + assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); + raise(SIGBUS); + return; + } + + ul = (unsigned long) si->si_addr; + ul = ul / page_size(); + ul = ul * page_size(); + aligned = (void*) ul; + + /* Let's remember which address failed */ + sigbus_push(aligned); + + /* Replace mapping with an anonymous page, so that the + * execution can continue, however with a zeroed out page */ + assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned); +} + +void sigbus_install(void) { + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO, + }; + + n_installed++; + + if (n_installed == 1) + assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0); + + return; +} + +void sigbus_reset(void) { + + if (n_installed <= 0) + return; + + n_installed--; + + if (n_installed == 0) + assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); + + return; +} diff --git a/src/libbasic/src/signal-util.c b/src/libbasic/src/signal-util.c new file mode 100644 index 0000000000..ade7bff364 --- /dev/null +++ b/src/libbasic/src/signal-util.c @@ -0,0 +1,278 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/signal-util.h" +#include "basic/stdio-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" + +int reset_all_signal_handlers(void) { + static const struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; + int sig, r = 0; + + for (sig = 1; sig < _NSIG; sig++) { + + /* These two cannot be caught... */ + if (sig == SIGKILL || sig == SIGSTOP) + continue; + + /* On Linux the first two RT signals are reserved by + * glibc, and sigaction() will return EINVAL for them. */ + if ((sigaction(sig, &sa, NULL) < 0)) + if (errno != EINVAL && r >= 0) + r = -errno; + } + + return r; +} + +int reset_signal_mask(void) { + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -errno; + + if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) + return -errno; + + return 0; +} + +static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { + int r = 0; + + /* negative signal ends the list. 0 signal is skipped. */ + + if (sig < 0) + return 0; + + if (sig > 0) { + if (sigaction(sig, sa, NULL) < 0) + r = -errno; + } + + while ((sig = va_arg(ap, int)) >= 0) { + + if (sig == 0) + continue; + + if (sigaction(sig, sa, NULL) < 0) { + if (r >= 0) + r = -errno; + } + } + + return r; +} + +int sigaction_many(const struct sigaction *sa, ...) { + va_list ap; + int r; + + va_start(ap, sa); + r = sigaction_many_ap(sa, 0, ap); + va_end(ap); + + return r; +} + +int ignore_signals(int sig, ...) { + + static const struct sigaction sa = { + .sa_handler = SIG_IGN, + .sa_flags = SA_RESTART, + }; + + va_list ap; + int r; + + va_start(ap, sig); + r = sigaction_many_ap(&sa, sig, ap); + va_end(ap); + + return r; +} + +int default_signals(int sig, ...) { + + static const struct sigaction sa = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; + + va_list ap; + int r; + + va_start(ap, sig); + r = sigaction_many_ap(&sa, sig, ap); + va_end(ap); + + return r; +} + +static int sigset_add_many_ap(sigset_t *ss, va_list ap) { + int sig, r = 0; + + assert(ss); + + while ((sig = va_arg(ap, int)) >= 0) { + + if (sig == 0) + continue; + + if (sigaddset(ss, sig) < 0) { + if (r >= 0) + r = -errno; + } + } + + return r; +} + +int sigset_add_many(sigset_t *ss, ...) { + va_list ap; + int r; + + va_start(ap, ss); + r = sigset_add_many_ap(ss, ap); + va_end(ap); + + return r; +} + +int sigprocmask_many(int how, sigset_t *old, ...) { + va_list ap; + sigset_t ss; + int r; + + if (sigemptyset(&ss) < 0) + return -errno; + + va_start(ap, old); + r = sigset_add_many_ap(&ss, ap); + va_end(ap); + + if (r < 0) + return r; + + if (sigprocmask(how, &ss, old) < 0) + return -errno; + + return 0; +} + +static const char *const __signal_table[] = { + [SIGHUP] = "HUP", + [SIGINT] = "INT", + [SIGQUIT] = "QUIT", + [SIGILL] = "ILL", + [SIGTRAP] = "TRAP", + [SIGABRT] = "ABRT", + [SIGBUS] = "BUS", + [SIGFPE] = "FPE", + [SIGKILL] = "KILL", + [SIGUSR1] = "USR1", + [SIGSEGV] = "SEGV", + [SIGUSR2] = "USR2", + [SIGPIPE] = "PIPE", + [SIGALRM] = "ALRM", + [SIGTERM] = "TERM", +#ifdef SIGSTKFLT + [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ +#endif + [SIGCHLD] = "CHLD", + [SIGCONT] = "CONT", + [SIGSTOP] = "STOP", + [SIGTSTP] = "TSTP", + [SIGTTIN] = "TTIN", + [SIGTTOU] = "TTOU", + [SIGURG] = "URG", + [SIGXCPU] = "XCPU", + [SIGXFSZ] = "XFSZ", + [SIGVTALRM] = "VTALRM", + [SIGPROF] = "PROF", + [SIGWINCH] = "WINCH", + [SIGIO] = "IO", + [SIGPWR] = "PWR", + [SIGSYS] = "SYS" +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); + +const char *signal_to_string(int signo) { + static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1]; + const char *name; + + name = __signal_to_string(signo); + if (name) + return name; + + if (signo >= SIGRTMIN && signo <= SIGRTMAX) + xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); + else + xsprintf(buf, "%d", signo); + + return buf; +} + +int signal_from_string(const char *s) { + int signo; + int offset = 0; + unsigned u; + + signo = __signal_from_string(s); + if (signo > 0) + return signo; + + if (startswith(s, "RTMIN+")) { + s += 6; + offset = SIGRTMIN; + } + if (safe_atou(s, &u) >= 0) { + signo = (int) u + offset; + if (SIGNAL_VALID(signo)) + return signo; + } + return -EINVAL; +} + +int signal_from_string_try_harder(const char *s) { + int signo; + assert(s); + + signo = signal_from_string(s); + if (signo <= 0) + if (startswith(s, "SIG")) + return signal_from_string(s+3); + + return signo; +} + +void nop_signal_handler(int sig) { + /* nothing here */ +} diff --git a/src/libbasic/src/siphash24.c b/src/libbasic/src/siphash24.c new file mode 100644 index 0000000000..f67eff4761 --- /dev/null +++ b/src/libbasic/src/siphash24.c @@ -0,0 +1,193 @@ +/* + SipHash reference C implementation + + Written in 2012 by + Jean-Philippe Aumasson + Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see . + + (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 + +#include "basic/macro.h" +#include "basic/siphash24.h" +#include "basic/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; + +#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 padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); +#endif + + state->v3 ^= state->padding; + sipround(state); + sipround(state); + state->v0 ^= state->padding; + + state->padding = 0; + } + + end -= (state->inlen % sizeof(uint64_t)); + + 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; + } +} + +uint64_t siphash24_finalize(struct siphash *state) { + uint64_t b; + + assert(state); + + b = state->padding | (((uint64_t) state->inlen) << 56); + +#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) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); +#endif + + state->v3 ^= b; + sipround(state); + sipround(state); + state->v0 ^= b; + +#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); +#endif + 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/libbasic/src/smack-util.c b/src/libbasic/src/smack-util.c new file mode 100644 index 0000000000..c3ce6bd4ac --- /dev/null +++ b/src/libbasic/src/smack-util.c @@ -0,0 +1,241 @@ +/*** + This file is part of systemd. + + Copyright 2013 Intel Corporation + + Author: Auke Kok + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/smack-util.h" +#include "basic/string-table.h" +#include "basic/xattr-util.h" + +#ifdef HAVE_SMACK +bool mac_smack_use(void) { + static int cached_use = -1; + + if (cached_use < 0) + cached_use = access("/sys/fs/smackfs/", F_OK) >= 0; + + return cached_use; +} + +static const char* const smack_attr_table[_SMACK_ATTR_MAX] = { + [SMACK_ATTR_ACCESS] = "security.SMACK64", + [SMACK_ATTR_EXEC] = "security.SMACK64EXEC", + [SMACK_ATTR_MMAP] = "security.SMACK64MMAP", + [SMACK_ATTR_TRANSMUTE] = "security.SMACK64TRANSMUTE", + [SMACK_ATTR_IPIN] = "security.SMACK64IPIN", + [SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT", +}; + +DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr); + +int mac_smack_read(const char *path, SmackAttr attr, char **label) { + assert(path); + assert(attr >= 0 && attr < _SMACK_ATTR_MAX); + assert(label); + + if (!mac_smack_use()) + return 0; + + return getxattr_malloc(path, smack_attr_to_string(attr), label, true); +} + +int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { + assert(fd >= 0); + assert(attr >= 0 && attr < _SMACK_ATTR_MAX); + assert(label); + + if (!mac_smack_use()) + return 0; + + return fgetxattr_malloc(fd, smack_attr_to_string(attr), label); +} + +int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { + int r; + + assert(path); + assert(attr >= 0 && attr < _SMACK_ATTR_MAX); + + if (!mac_smack_use()) + return 0; + + if (label) + r = lsetxattr(path, smack_attr_to_string(attr), label, strlen(label), 0); + else + r = lremovexattr(path, smack_attr_to_string(attr)); + if (r < 0) + return -errno; + + return 0; +} + +int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { + int r; + + assert(fd >= 0); + assert(attr >= 0 && attr < _SMACK_ATTR_MAX); + + if (!mac_smack_use()) + return 0; + + if (label) + r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0); + else + r = fremovexattr(fd, smack_attr_to_string(attr)); + if (r < 0) + return -errno; + + return 0; +} + +int mac_smack_apply_pid(pid_t pid, const char *label) { + const char *p; + int r = 0; + + assert(label); + + if (!mac_smack_use()) + return 0; + + p = procfs_file_alloca(pid, "attr/current"); + r = write_string_file(p, label, 0); + if (r < 0) + return r; + + return r; +} + +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { + struct stat st; + int r = 0; + + assert(path); + + if (!mac_smack_use()) + return 0; + + /* + * Path must be in /dev and must exist + */ + if (!path_startswith(path, "/dev")) + return 0; + + r = lstat(path, &st); + if (r >= 0) { + const char *label; + + /* + * Label directories and character devices "*". + * Label symlinks "_". + * Don't change anything else. + */ + + if (S_ISDIR(st.st_mode)) + label = SMACK_STAR_LABEL; + else if (S_ISLNK(st.st_mode)) + label = SMACK_FLOOR_LABEL; + else if (S_ISCHR(st.st_mode)) + label = SMACK_STAR_LABEL; + else + return 0; + + r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0); + + /* If the FS doesn't support labels, then exit without warning */ + if (r < 0 && errno == EOPNOTSUPP) + return 0; + } + + if (r < 0) { + /* Ignore ENOENT in some cases */ + if (ignore_enoent && errno == ENOENT) + return 0; + + if (ignore_erofs && errno == EROFS) + return 0; + + r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); + } + + return r; +} + +int mac_smack_copy(const char *dest, const char *src) { + int r = 0; + _cleanup_free_ char *label = NULL; + + assert(dest); + assert(src); + + r = mac_smack_read(src, SMACK_ATTR_ACCESS, &label); + if (r < 0) + return r; + + r = mac_smack_apply(dest, SMACK_ATTR_ACCESS, label); + if (r < 0) + return r; + + return r; +} + +#else +bool mac_smack_use(void) { + return false; +} + +int mac_smack_read(const char *path, SmackAttr attr, char **label) { + return -EOPNOTSUPP; +} + +int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { + return -EOPNOTSUPP; +} + +int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { + return 0; +} + +int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { + return 0; +} + +int mac_smack_apply_pid(pid_t pid, const char *label) { + return 0; +} + +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { + return 0; +} + +int mac_smack_copy(const char *dest, const char *src) { + return 0; +} +#endif diff --git a/src/libbasic/src/socket-label.c b/src/libbasic/src/socket-label.c new file mode 100644 index 0000000000..75036f451f --- /dev/null +++ b/src/libbasic/src/socket-label.c @@ -0,0 +1,170 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/selinux-util.h" +#include "basic/socket-util.h" +#include "basic/umask-util.h" + +int socket_address_listen( + const SocketAddress *a, + int flags, + int backlog, + SocketAddressBindIPv6Only only, + const char *bind_to_device, + bool reuse_port, + bool free_bind, + bool transparent, + mode_t directory_mode, + mode_t socket_mode, + const char *label) { + + _cleanup_close_ int fd = -1; + int r, one; + + assert(a); + + r = socket_address_verify(a); + if (r < 0) + return r; + + if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported()) + return -EAFNOSUPPORT; + + if (label) { + r = mac_selinux_create_socket_prepare(label); + if (r < 0) + return r; + } + + fd = socket(socket_address_family(a), a->type | flags, a->protocol); + r = fd < 0 ? -errno : 0; + + if (label) + mac_selinux_create_socket_clear(); + + if (r < 0) + return r; + + if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { + int flag = only == SOCKET_ADDRESS_IPV6_ONLY; + + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) + return -errno; + } + + if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) { + if (bind_to_device) + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) + return -errno; + + if (reuse_port) { + one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0) + log_warning_errno(errno, "SO_REUSEPORT failed: %m"); + } + + if (free_bind) { + one = 1; + if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) + log_warning_errno(errno, "IP_FREEBIND failed: %m"); + } + + if (transparent) { + one = 1; + if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) + log_warning_errno(errno, "IP_TRANSPARENT failed: %m"); + } + } + + one = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) + return -errno; + + if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { + /* Create parents */ + (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); + + /* Enforce the right access mode for the socket */ + RUN_WITH_UMASK(~socket_mode) { + r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); + if (r == -EADDRINUSE) { + /* Unlink and try again */ + unlink(a->sockaddr.un.sun_path); + if (bind(fd, &a->sockaddr.sa, a->size) < 0) + return -errno; + } else if (r < 0) + return r; + } + } else { + if (bind(fd, &a->sockaddr.sa, a->size) < 0) + return -errno; + } + + if (socket_address_can_accept(a)) + if (listen(fd, backlog) < 0) + return -errno; + + r = fd; + fd = -1; + + return r; +} + +int make_socket_fd(int log_level, const char* address, int type, int flags) { + SocketAddress a; + int fd, r; + + r = socket_address_parse(&a, address); + if (r < 0) + return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); + + a.type = type; + + fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, + NULL, false, false, false, 0755, 0644, NULL); + if (fd < 0 || log_get_max_level() >= log_level) { + _cleanup_free_ char *p = NULL; + + r = socket_address_print(&a, &p); + if (r < 0) + return log_error_errno(r, "socket_address_print(): %m"); + + if (fd < 0) + log_error_errno(fd, "Failed to listen on %s: %m", p); + else + log_full(log_level, "Listening on %s", p); + } + + return fd; +} diff --git a/src/libbasic/src/socket-util.c b/src/libbasic/src/socket-util.c new file mode 100644 index 0000000000..250d58dfc6 --- /dev/null +++ b/src/libbasic/src/socket-util.c @@ -0,0 +1,1048 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/socket-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/utf8.h" +#include "basic/util.h" + +int socket_address_parse(SocketAddress *a, const char *s) { + char *e, *n; + unsigned u; + int r; + + assert(a); + assert(s); + + zero(*a); + a->type = SOCK_STREAM; + + if (*s == '[') { + /* IPv6 in [x:.....:z]:p notation */ + + e = strchr(s+1, ']'); + if (!e) + return -EINVAL; + + n = strndupa(s+1, e-s-1); + + errno = 0; + if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) + return errno > 0 ? -errno : -EINVAL; + + e++; + if (*e != ':') + return -EINVAL; + + e++; + r = safe_atou(e, &u); + if (r < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; + + a->sockaddr.in6.sin6_family = AF_INET6; + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->size = sizeof(struct sockaddr_in6); + + } else if (*s == '/') { + /* AF_UNIX socket */ + + size_t l; + + l = strlen(s); + if (l >= sizeof(a->sockaddr.un.sun_path)) + return -EINVAL; + + a->sockaddr.un.sun_family = AF_UNIX; + memcpy(a->sockaddr.un.sun_path, s, l); + a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; + + } else if (*s == '@') { + /* Abstract AF_UNIX socket */ + size_t l; + + l = strlen(s+1); + if (l >= sizeof(a->sockaddr.un.sun_path) - 1) + return -EINVAL; + + a->sockaddr.un.sun_family = AF_UNIX; + memcpy(a->sockaddr.un.sun_path+1, s+1, l); + a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; + + } else { + e = strchr(s, ':'); + if (e) { + r = safe_atou(e+1, &u); + if (r < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; + + n = strndupa(s, e-s); + + /* IPv4 in w.x.y.z:p notation? */ + r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); + if (r < 0) + return -errno; + + if (r > 0) { + /* Gotcha, it's a traditional IPv4 address */ + a->sockaddr.in.sin_family = AF_INET; + a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->size = sizeof(struct sockaddr_in); + } else { + unsigned idx; + + if (strlen(n) > IF_NAMESIZE-1) + return -EINVAL; + + /* Uh, our last resort, an interface name */ + idx = if_nametoindex(n); + if (idx == 0) + return -EINVAL; + + a->sockaddr.in6.sin6_family = AF_INET6; + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_scope_id = idx; + a->sockaddr.in6.sin6_addr = in6addr_any; + a->size = sizeof(struct sockaddr_in6); + } + } else { + + /* Just a port */ + r = safe_atou(s, &u); + if (r < 0) + return r; + + if (u <= 0 || u > 0xFFFF) + return -EINVAL; + + if (socket_ipv6_is_supported()) { + a->sockaddr.in6.sin6_family = AF_INET6; + a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_addr = in6addr_any; + a->size = sizeof(struct sockaddr_in6); + } else { + a->sockaddr.in.sin_family = AF_INET; + a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; + a->size = sizeof(struct sockaddr_in); + } + } + } + + return 0; +} + +int socket_address_parse_and_warn(SocketAddress *a, const char *s) { + SocketAddress b; + int r; + + /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ + + r = socket_address_parse(&b, s); + if (r < 0) + return r; + + if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { + log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); + return -EAFNOSUPPORT; + } + + *a = b; + return 0; +} + +int socket_address_parse_netlink(SocketAddress *a, const char *s) { + int family; + unsigned group = 0; + _cleanup_free_ char *sfamily = NULL; + assert(a); + assert(s); + + zero(*a); + a->type = SOCK_RAW; + + errno = 0; + if (sscanf(s, "%ms %u", &sfamily, &group) < 1) + return errno > 0 ? -errno : -EINVAL; + + family = netlink_family_from_string(sfamily); + if (family < 0) + return -EINVAL; + + a->sockaddr.nl.nl_family = AF_NETLINK; + a->sockaddr.nl.nl_groups = group; + + a->type = SOCK_RAW; + a->size = sizeof(struct sockaddr_nl); + a->protocol = family; + + return 0; +} + +int socket_address_verify(const SocketAddress *a) { + assert(a); + + switch (socket_address_family(a)) { + + case AF_INET: + if (a->size != sizeof(struct sockaddr_in)) + return -EINVAL; + + if (a->sockaddr.in.sin_port == 0) + return -EINVAL; + + if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + return -EINVAL; + + return 0; + + case AF_INET6: + if (a->size != sizeof(struct sockaddr_in6)) + return -EINVAL; + + if (a->sockaddr.in6.sin6_port == 0) + return -EINVAL; + + if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) + return -EINVAL; + + return 0; + + case AF_UNIX: + if (a->size < offsetof(struct sockaddr_un, sun_path)) + return -EINVAL; + + if (a->size > offsetof(struct sockaddr_un, sun_path)) { + + if (a->sockaddr.un.sun_path[0] != 0) { + char *e; + + /* path */ + e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); + if (!e) + return -EINVAL; + + if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) + return -EINVAL; + } + } + + if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET) + return -EINVAL; + + return 0; + + case AF_NETLINK: + + if (a->size != sizeof(struct sockaddr_nl)) + return -EINVAL; + + if (a->type != SOCK_RAW && a->type != SOCK_DGRAM) + return -EINVAL; + + return 0; + + default: + return -EAFNOSUPPORT; + } +} + +int socket_address_print(const SocketAddress *a, char **ret) { + int r; + + assert(a); + assert(ret); + + r = socket_address_verify(a); + if (r < 0) + return r; + + if (socket_address_family(a) == AF_NETLINK) { + _cleanup_free_ char *sfamily = NULL; + + r = netlink_family_to_string_alloc(a->protocol, &sfamily); + if (r < 0) + return r; + + r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); + if (r < 0) + return -ENOMEM; + + return 0; + } + + return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); +} + +bool socket_address_can_accept(const SocketAddress *a) { + assert(a); + + return + a->type == SOCK_STREAM || + a->type == SOCK_SEQPACKET; +} + +bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { + assert(a); + assert(b); + + /* Invalid addresses are unequal to all */ + if (socket_address_verify(a) < 0 || + socket_address_verify(b) < 0) + return false; + + if (a->type != b->type) + return false; + + if (socket_address_family(a) != socket_address_family(b)) + return false; + + switch (socket_address_family(a)) { + + case AF_INET: + if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) + return false; + + if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) + return false; + + break; + + case AF_INET6: + if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) + return false; + + if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) + return false; + + break; + + case AF_UNIX: + if (a->size <= offsetof(struct sockaddr_un, sun_path) || + b->size <= offsetof(struct sockaddr_un, sun_path)) + return false; + + if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) + return false; + + if (a->sockaddr.un.sun_path[0]) { + if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path)) + return false; + } else { + if (a->size != b->size) + return false; + + if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) + return false; + } + + break; + + case AF_NETLINK: + if (a->protocol != b->protocol) + return false; + + if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) + return false; + + break; + + default: + /* Cannot compare, so we assume the addresses are different */ + return false; + } + + return true; +} + +bool socket_address_is(const SocketAddress *a, const char *s, int type) { + struct SocketAddress b; + + assert(a); + assert(s); + + if (socket_address_parse(&b, s) < 0) + return false; + + b.type = type; + + return socket_address_equal(a, &b); +} + +bool socket_address_is_netlink(const SocketAddress *a, const char *s) { + struct SocketAddress b; + + assert(a); + assert(s); + + if (socket_address_parse_netlink(&b, s) < 0) + return false; + + return socket_address_equal(a, &b); +} + +const char* socket_address_get_path(const SocketAddress *a) { + assert(a); + + if (socket_address_family(a) != AF_UNIX) + return NULL; + + if (a->sockaddr.un.sun_path[0] == 0) + return NULL; + + return a->sockaddr.un.sun_path; +} + +bool socket_ipv6_is_supported(void) { + if (access("/proc/net/sockstat6", F_OK) != 0) + return false; + + return true; +} + +bool socket_address_matches_fd(const SocketAddress *a, int fd) { + SocketAddress b; + socklen_t solen; + + assert(a); + assert(fd >= 0); + + b.size = sizeof(b.sockaddr); + if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) + return false; + + if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) + return false; + + solen = sizeof(b.type); + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) + return false; + + if (b.type != a->type) + return false; + + if (a->protocol != 0) { + solen = sizeof(b.protocol); + if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) + return false; + + if (b.protocol != a->protocol) + return false; + } + + return socket_address_equal(a, &b); +} + +int sockaddr_port(const struct sockaddr *_sa) { + union sockaddr_union *sa = (union sockaddr_union*) _sa; + + assert(sa); + + if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port); +} + +int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { + union sockaddr_union *sa = (union sockaddr_union*) _sa; + char *p; + int r; + + assert(sa); + assert(salen >= sizeof(sa->sa.sa_family)); + + switch (sa->sa.sa_family) { + + case AF_INET: { + uint32_t a; + + a = be32toh(sa->in.sin_addr.s_addr); + + if (include_port) + r = asprintf(&p, + "%u.%u.%u.%u:%u", + a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, + be16toh(sa->in.sin_port)); + else + r = asprintf(&p, + "%u.%u.%u.%u", + a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); + if (r < 0) + return -ENOMEM; + break; + } + + case AF_INET6: { + static const unsigned char ipv4_prefix[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF + }; + + if (translate_ipv6 && + memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { + const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; + if (include_port) + r = asprintf(&p, + "%u.%u.%u.%u:%u", + a[0], a[1], a[2], a[3], + be16toh(sa->in6.sin6_port)); + else + r = asprintf(&p, + "%u.%u.%u.%u", + a[0], a[1], a[2], a[3]); + if (r < 0) + return -ENOMEM; + } else { + char a[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); + + if (include_port) { + r = asprintf(&p, + "[%s]:%u", + a, + be16toh(sa->in6.sin6_port)); + if (r < 0) + return -ENOMEM; + } else { + p = strdup(a); + if (!p) + return -ENOMEM; + } + } + + break; + } + + case AF_UNIX: + if (salen <= offsetof(struct sockaddr_un, sun_path)) { + p = strdup(""); + if (!p) + return -ENOMEM; + + } else if (sa->un.sun_path[0] == 0) { + /* abstract */ + + /* FIXME: We assume we can print the + * socket path here and that it hasn't + * more than one NUL byte. That is + * actually an invalid assumption */ + + p = new(char, sizeof(sa->un.sun_path)+1); + if (!p) + return -ENOMEM; + + p[0] = '@'; + memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); + p[sizeof(sa->un.sun_path)] = 0; + + } else { + p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); + if (!p) + return -ENOMEM; + } + + break; + + default: + return -EOPNOTSUPP; + } + + + *ret = p; + return 0; +} + +int getpeername_pretty(int fd, bool include_port, char **ret) { + union sockaddr_union sa; + socklen_t salen = sizeof(sa); + int r; + + assert(fd >= 0); + assert(ret); + + if (getpeername(fd, &sa.sa, &salen) < 0) + return -errno; + + if (sa.sa.sa_family == AF_UNIX) { + struct ucred ucred = {}; + + /* UNIX connection sockets are anonymous, so let's use + * PID/UID as pretty credentials instead */ + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) + return -ENOMEM; + + return 0; + } + + /* For remote sockets we translate IPv6 addresses back to IPv4 + * if applicable, since that's nicer. */ + + return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); +} + +int getsockname_pretty(int fd, char **ret) { + union sockaddr_union sa; + socklen_t salen = sizeof(sa); + + assert(fd >= 0); + assert(ret); + + if (getsockname(fd, &sa.sa, &salen) < 0) + return -errno; + + /* For local sockets we do not translate IPv6 addresses back + * to IPv6 if applicable, since this is usually used for + * listening sockets where the difference between IPv4 and + * IPv6 matters. */ + + return sockaddr_pretty(&sa.sa, salen, false, true, ret); +} + +int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { + int r; + char host[NI_MAXHOST], *ret; + + assert(_ret); + + r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, + NI_IDN|NI_IDN_USE_STD3_ASCII_RULES); + if (r != 0) { + int saved_errno = errno; + + r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); + if (r < 0) + return r; + + log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); + } else { + ret = strdup(host); + if (!ret) + return -ENOMEM; + } + + *_ret = ret; + return 0; +} + +int getnameinfo_pretty(int fd, char **ret) { + union sockaddr_union sa; + socklen_t salen = sizeof(sa); + + assert(fd >= 0); + assert(ret); + + if (getsockname(fd, &sa.sa, &salen) < 0) + return -errno; + + return socknameinfo_pretty(&sa, salen, ret); +} + +int socket_address_unlink(SocketAddress *a) { + assert(a); + + if (socket_address_family(a) != AF_UNIX) + return 0; + + if (a->sockaddr.un.sun_path[0] == 0) + return 0; + + if (unlink(a->sockaddr.un.sun_path) < 0) + return -errno; + + return 1; +} + +static const char* const netlink_family_table[] = { + [NETLINK_ROUTE] = "route", + [NETLINK_FIREWALL] = "firewall", + [NETLINK_INET_DIAG] = "inet-diag", + [NETLINK_NFLOG] = "nflog", + [NETLINK_XFRM] = "xfrm", + [NETLINK_SELINUX] = "selinux", + [NETLINK_ISCSI] = "iscsi", + [NETLINK_AUDIT] = "audit", + [NETLINK_FIB_LOOKUP] = "fib-lookup", + [NETLINK_CONNECTOR] = "connector", + [NETLINK_NETFILTER] = "netfilter", + [NETLINK_IP6_FW] = "ip6-fw", + [NETLINK_DNRTMSG] = "dnrtmsg", + [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", + [NETLINK_GENERIC] = "generic", + [NETLINK_SCSITRANSPORT] = "scsitransport", + [NETLINK_ECRYPTFS] = "ecryptfs" +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); + +static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { + [SOCKET_ADDRESS_DEFAULT] = "default", + [SOCKET_ADDRESS_BOTH] = "both", + [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); + +bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { + assert(a); + assert(b); + + if (a->sa.sa_family != b->sa.sa_family) + return false; + + if (a->sa.sa_family == AF_INET) + return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; + + if (a->sa.sa_family == AF_INET6) + return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; + + return false; +} + +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); + +bool ifname_valid(const char *p) { + bool numeric = true; + + /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources + * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We + * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ + + if (isempty(p)) + return false; + + if (strlen(p) >= IFNAMSIZ) + return false; + + if (STR_IN_SET(p, ".", "..")) + return false; + + while (*p) { + if ((unsigned char) *p >= 127U) + return false; + + if ((unsigned char) *p <= 32U) + return false; + + if (*p == ':' || *p == '/') + return false; + + numeric = numeric && (*p >= '0' && *p <= '9'); + p++; + } + + if (numeric) + return false; + + return true; +} + +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; +} + +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 msghdr mh = { + .msg_name = (struct sockaddr*) sa, + .msg_namelen = len, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + + 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 *(int*) CMSG_DATA(found); +} + +ssize_t next_datagram_size_fd(int fd) { + ssize_t l; + int k; + + /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will + * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't + * do. This difference is actually of major importance as we need to be sure that the size returned here + * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of + * the wrong size. */ + + l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); + if (l < 0) { + if (errno == EOPNOTSUPP || errno == EFAULT) + goto fallback; + + return -errno; + } + if (l == 0) + goto fallback; + + return l; + +fallback: + k = 0; + + /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD + * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ + + if (ioctl(fd, FIONREAD, &k) < 0) + return -errno; + + return (ssize_t) k; +} + +int flush_accept(int fd) { + + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + int r; + + + /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ + + for (;;) { + int cfd; + + r = poll(&pollfd, 1, 0); + if (r < 0) { + if (errno == EINTR) + continue; + + return -errno; + + } else if (r == 0) + return 0; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 0; + + return -errno; + } + + close(cfd); + } +} diff --git a/src/libbasic/src/stat-util.c b/src/libbasic/src/stat-util.c new file mode 100644 index 0000000000..9ddede98c9 --- /dev/null +++ b/src/libbasic/src/stat-util.c @@ -0,0 +1,219 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/stat-util.h" +#include "basic/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/libbasic/src/strbuf.c b/src/libbasic/src/strbuf.c new file mode 100644 index 0000000000..3b6fafeb30 --- /dev/null +++ b/src/libbasic/src/strbuf.c @@ -0,0 +1,205 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/strbuf.h" + +/* + * Strbuf stores given strings in a single continuous allocated memory + * area. Identical strings are de-duplicated and return the same offset + * as the first string stored. If the tail of a string already exists + * in the buffer, the tail is returned. + * + * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the + * information about the stored strings. + * + * Example of udev rules: + * $ ./udevadm test . + * ... + * read rules file: /usr/lib/udev/rules.d/99-systemd.rules + * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings + * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used + * ... + */ + +struct strbuf *strbuf_new(void) { + struct strbuf *str; + + str = new0(struct strbuf, 1); + if (!str) + return NULL; + + str->buf = new0(char, 1); + if (!str->buf) + goto err; + str->len = 1; + + str->root = new0(struct strbuf_node, 1); + if (!str->root) + goto err; + str->nodes_count = 1; + return str; +err: + free(str->buf); + free(str->root); + free(str); + return NULL; +} + +static void strbuf_node_cleanup(struct strbuf_node *node) { + size_t i; + + for (i = 0; i < node->children_count; i++) + strbuf_node_cleanup(node->children[i].child); + free(node->children); + free(node); +} + +/* clean up trie data, leave only the string buffer */ +void strbuf_complete(struct strbuf *str) { + if (!str) + return; + if (str->root) + strbuf_node_cleanup(str->root); + str->root = NULL; +} + +/* clean up everything */ +void strbuf_cleanup(struct strbuf *str) { + if (!str) + return; + if (str->root) + strbuf_node_cleanup(str->root); + free(str->buf); + free(str); +} + +static int strbuf_children_cmp(const struct strbuf_child_entry *n1, + const struct strbuf_child_entry *n2) { + return n1->c - n2->c; +} + +static void bubbleinsert(struct strbuf_node *node, + uint8_t c, + struct strbuf_node *node_child) { + + struct strbuf_child_entry new = { + .c = c, + .child = node_child, + }; + int left = 0, right = node->children_count; + + while (right > left) { + int middle = (right + left) / 2 ; + if (strbuf_children_cmp(&node->children[middle], &new) <= 0) + left = middle + 1; + else + right = middle; + } + + memmove(node->children + left + 1, node->children + left, + sizeof(struct strbuf_child_entry) * (node->children_count - left)); + node->children[left] = new; + + node->children_count++; +} + +/* add string, return the index/offset into the buffer */ +ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) { + uint8_t c; + struct strbuf_node *node; + size_t depth; + char *buf_new; + struct strbuf_child_entry *child; + struct strbuf_node *node_child; + ssize_t off; + + if (!str->root) + return -EINVAL; + + /* search string; start from last character to find possibly matching tails */ + if (len == 0) + return 0; + str->in_count++; + str->in_len += len; + + node = str->root; + c = s[len-1]; + for (depth = 0; depth <= len; depth++) { + struct strbuf_child_entry search; + + /* match against current node */ + off = node->value_off + node->value_len - len; + if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) { + str->dedup_len += len; + str->dedup_count++; + return off; + } + + c = s[len - 1 - depth]; + + /* bsearch is not allowed on a NULL sequence */ + if (node->children_count == 0) + break; + + /* lookup child node */ + search.c = c; + child = bsearch(&search, node->children, node->children_count, + sizeof(struct strbuf_child_entry), + (__compar_fn_t) strbuf_children_cmp); + if (!child) + break; + node = child->child; + } + + /* add new string */ + buf_new = realloc(str->buf, str->len + len+1); + if (!buf_new) + return -ENOMEM; + str->buf = buf_new; + off = str->len; + memcpy(str->buf + off, s, len); + str->len += len; + str->buf[str->len++] = '\0'; + + /* new node */ + node_child = new0(struct strbuf_node, 1); + if (!node_child) + return -ENOMEM; + node_child->value_off = off; + node_child->value_len = len; + + /* extend array, add new entry, sort for bisection */ + child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry)); + if (!child) { + free(node_child); + return -ENOMEM; + } + + str->nodes_count++; + + node->children = child; + bubbleinsert(node, c, node_child); + + return off; +} diff --git a/src/libbasic/src/string-table.c b/src/libbasic/src/string-table.c new file mode 100644 index 0000000000..fae5e944b5 --- /dev/null +++ b/src/libbasic/src/string-table.c @@ -0,0 +1,34 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-table.h" +#include "basic/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; + + for (i = 0; i < len; ++i) + if (streq_ptr(table[i], key)) + return (ssize_t) i; + + return -1; +} diff --git a/src/libbasic/src/string-util.c b/src/libbasic/src/string-util.c new file mode 100644 index 0000000000..003a20a41d --- /dev/null +++ b/src/libbasic/src/string-util.c @@ -0,0 +1,855 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/gunicode.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/utf8.h" +#include "basic/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; + int r; + + 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)) { + char32_t c; + + r = utf8_encoded_to_unichar(i, &c); + if (r < 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; ) { + char32_t c; + + j = utf8_prev_char(j); + r = utf8_encoded_to_unichar(j, &c); + if (r < 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/libbasic/src/strv.c b/src/libbasic/src/strv.c new file mode 100644 index 0000000000..bb4f49277d --- /dev/null +++ b/src/libbasic/src/strv.c @@ -0,0 +1,943 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/extract-word.h" +#include "basic/fileio.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +char *strv_find(char **l, const char *name) { + char **i; + + assert(name); + + STRV_FOREACH(i, l) + if (streq(*i, name)) + return *i; + + return NULL; +} + +char *strv_find_prefix(char **l, const char *name) { + char **i; + + assert(name); + + STRV_FOREACH(i, l) + if (startswith(*i, name)) + return *i; + + return NULL; +} + +char *strv_find_startswith(char **l, const char *name) { + char **i, *e; + + assert(name); + + /* Like strv_find_prefix, but actually returns only the + * suffix, not the whole item */ + + STRV_FOREACH(i, l) { + e = startswith(*i, name); + if (e) + return e; + } + + return NULL; +} + +void strv_clear(char **l) { + char **k; + + if (!l) + return; + + for (k = l; *k; k++) + free(*k); + + *l = NULL; +} + +char **strv_free(char **l) { + strv_clear(l); + free(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; + + k = r = new(char*, strv_length(l) + 1); + if (!r) + return NULL; + + if (l) + for (; *l; k++, l++) { + *k = strdup(*l); + if (!*k) { + strv_free(r); + return NULL; + } + } + + *k = NULL; + return r; +} + +unsigned strv_length(char * const *l) { + unsigned n = 0; + + if (!l) + return 0; + + for (; *l; l++) + n++; + + return n; +} + +char **strv_new_ap(const char *x, va_list ap) { + const char *s; + char **a; + unsigned n = 0, i = 0; + va_list aq; + + /* As a special trick we ignore all listed strings that equal + * STRV_IGNORE. This is supposed to be used with the + * STRV_IFNOTNULL() macro to include possibly NULL strings in + * the string list. */ + + if (x) { + n = x == STRV_IGNORE ? 0 : 1; + + va_copy(aq, ap); + while ((s = va_arg(aq, const char*))) { + if (s == STRV_IGNORE) + continue; + + n++; + } + + va_end(aq); + } + + a = new(char*, n+1); + if (!a) + return NULL; + + if (x) { + if (x != STRV_IGNORE) { + a[i] = strdup(x); + if (!a[i]) + goto fail; + i++; + } + + while ((s = va_arg(ap, const char*))) { + + if (s == STRV_IGNORE) + continue; + + a[i] = strdup(s); + if (!a[i]) + goto fail; + + i++; + } + } + + a[i] = NULL; + + return a; + +fail: + strv_free(a); + return NULL; +} + +char **strv_new(const char *x, ...) { + char **r; + va_list ap; + + va_start(ap, x); + r = strv_new_ap(x, ap); + va_end(ap); + + return r; +} + +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) { + + if (filter_duplicates && strv_contains(t, *s)) + continue; + + t[p+i] = strdup(*s); + if (!t[p+i]) + goto rollback; + + i++; + t[p+i] = NULL; + } + + 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) { + int r; + char **s; + + STRV_FOREACH(s, b) { + char *v; + + v = strappend(*s, suffix); + if (!v) + return -ENOMEM; + + r = strv_push(a, v); + if (r < 0) { + free(v); + return r; + } + } + + return 0; +} + +char **strv_split(const char *s, const char *separator) { + const char *word, *state; + size_t l; + unsigned n, i; + char **r; + + assert(s); + + n = 0; + FOREACH_WORD_SEPARATOR(word, l, s, separator, state) + n++; + + r = new(char*, n+1); + if (!r) + return NULL; + + i = 0; + FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { + r[i] = strndup(word, l); + if (!r[i]) { + strv_free(r); + return NULL; + } + + i++; + } + + r[i] = NULL; + return r; +} + +char **strv_split_newlines(const char *s) { + char **l; + unsigned n; + + assert(s); + + /* Special version of strv_split() that splits on newlines and + * suppresses an empty string at the end */ + + l = strv_split(s, NEWLINE); + if (!l) + return NULL; + + n = strv_length(l); + if (n <= 0) + return l; + + if (isempty(l[n - 1])) + l[n - 1] = mfree(l[n - 1]); + + return l; +} + +int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { + _cleanup_strv_free_ char **l = NULL; + size_t n = 0, allocated = 0; + int r; + + assert(t); + assert(s); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&s, &word, separators, flags); + if (r < 0) + return r; + if (r == 0) + break; + + if (!GREEDY_REALLOC(l, allocated, n + 2)) + return -ENOMEM; + + l[n++] = word; + word = NULL; + + l[n] = NULL; + } + + if (!l) { + l = new0(char*, 1); + if (!l) + return -ENOMEM; + } + + *t = l; + l = NULL; + + return (int) n; +} + +char *strv_join(char **l, const char *separator) { + char *r, *e; + char **s; + size_t n, k; + + if (!separator) + separator = " "; + + k = strlen(separator); + + n = 0; + STRV_FOREACH(s, l) { + if (s != l) + n += k; + n += strlen(*s); + } + + r = new(char, n+1); + if (!r) + return NULL; + + e = r; + STRV_FOREACH(s, l) { + if (s != l) + e = stpcpy(e, separator); + + e = stpcpy(e, *s); + } + + *e = 0; + + return r; +} + +char *strv_join_quoted(char **l) { + char *buf = NULL; + char **s; + size_t allocated = 0, len = 0; + + STRV_FOREACH(s, l) { + /* assuming here that escaped string cannot be more + * than twice as long, and reserving space for the + * separator and quotes. + */ + _cleanup_free_ char *esc = NULL; + size_t needed; + + if (!GREEDY_REALLOC(buf, allocated, + len + strlen(*s) * 2 + 3)) + goto oom; + + esc = cescape(*s); + if (!esc) + goto oom; + + needed = snprintf(buf + len, allocated - len, "%s\"%s\"", + len > 0 ? " " : "", esc); + assert(needed < allocated - len); + len += needed; + } + + if (!buf) + buf = malloc0(1); + + return buf; + + oom: + free(buf); + return NULL; +} + +int strv_push(char ***l, char *value) { + char **c; + unsigned n, m; + + if (!value) + return 0; + + n = strv_length(*l); + + /* Increase and check for overflow */ + m = n + 2; + if (m < n) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) + return -ENOMEM; + + c[n] = value; + c[n+1] = NULL; + + *l = c; + return 0; +} + +int strv_push_pair(char ***l, char *a, char *b) { + char **c; + unsigned n, m; + + if (!a && !b) + return 0; + + n = strv_length(*l); + + /* increase and check for overflow */ + m = n + !!a + !!b + 1; + if (m < n) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) + return -ENOMEM; + + if (a) + c[n++] = a; + if (b) + c[n++] = b; + c[n] = NULL; + + *l = c; + return 0; +} + +int strv_push_prepend(char ***l, char *value) { + char **c; + unsigned n, m, i; + + if (!value) + return 0; + + n = strv_length(*l); + + /* increase and check for overflow */ + m = n + 2; + if (m < n) + return -ENOMEM; + + c = new(char*, m); + if (!c) + return -ENOMEM; + + for (i = 0; i < n; i++) + c[i+1] = (*l)[i]; + + c[0] = value; + c[n+1] = NULL; + + free(*l); + *l = c; + + return 0; +} + +int strv_consume(char ***l, char *value) { + int r; + + r = strv_push(l, value); + if (r < 0) + free(value); + + return r; +} + +int strv_consume_pair(char ***l, char *a, char *b) { + int r; + + r = strv_push_pair(l, a, b); + if (r < 0) { + free(a); + free(b); + } + + return r; +} + +int strv_consume_prepend(char ***l, char *value) { + int r; + + r = strv_push_prepend(l, value); + if (r < 0) + free(value); + + return r; +} + +int strv_extend(char ***l, const char *value) { + char *v; + + if (!value) + return 0; + + v = strdup(value); + if (!v) + return -ENOMEM; + + return strv_consume(l, v); +} + +int strv_extend_front(char ***l, const char *value) { + size_t n, m; + char *v, **c; + + assert(l); + + /* Like strv_extend(), but prepends rather than appends the new entry */ + + if (!value) + return 0; + + n = strv_length(*l); + + /* Increase and overflow check. */ + m = n + 2; + if (m < n) + return -ENOMEM; + + v = strdup(value); + if (!v) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) { + free(v); + return -ENOMEM; + } + + memmove(c+1, c, n * sizeof(char*)); + c[0] = v; + c[n+1] = NULL; + + *l = c; + return 0; +} + +char **strv_uniq(char **l) { + char **i; + + /* Drops duplicate entries. The first identical string will be + * kept, the others dropped */ + + STRV_FOREACH(i, l) + strv_remove(i+1, *i); + + return l; +} + +bool strv_is_uniq(char **l) { + char **i; + + STRV_FOREACH(i, l) + if (strv_find(i+1, *i)) + return false; + + return true; +} + +char **strv_remove(char **l, const char *s) { + char **f, **t; + + if (!l) + return NULL; + + assert(s); + + /* Drops every occurrence of s in the string list, edits + * in-place. */ + + for (f = t = l; *f; f++) + if (streq(*f, s)) + free(*f); + else + *(t++) = *f; + + *t = NULL; + return l; +} + +char **strv_parse_nulstr(const char *s, size_t l) { + /* l is the length of the input data, which will be split at NULs into + * elements of the resulting strv. Hence, the number of items in the resulting strv + * will be equal to one plus the number of NUL bytes in the l bytes starting at s, + * unless s[l-1] is NUL, in which case the final empty string is not stored in + * the resulting strv, and length is equal to the number of NUL bytes. + * + * Note that contrary to a normal nulstr which cannot contain empty strings, because + * the input data is terminated by any two consequent NUL bytes, this parser accepts + * empty strings in s. + */ + + const char *p; + unsigned c = 0, i = 0; + char **v; + + assert(s || l <= 0); + + if (l <= 0) + return new0(char*, 1); + + for (p = s; p < s + l; p++) + if (*p == 0) + c++; + + if (s[l-1] != 0) + c++; + + v = new0(char*, c+1); + if (!v) + return NULL; + + p = s; + while (p < s + l) { + const char *e; + + e = memchr(p, 0, s + l - p); + + v[i] = strndup(p, e ? e - p : s + l - p); + if (!v[i]) { + strv_free(v); + return NULL; + } + + i++; + + if (!e) + break; + + p = e + 1; + } + + assert(i == c); + + return v; +} + +char **strv_split_nulstr(const char *s) { + const char *i; + char **r = NULL; + + NULSTR_FOREACH(i, s) + if (strv_extend(&r, i) < 0) { + strv_free(r); + return NULL; + } + + if (!r) + return strv_new(NULL, NULL); + + return r; +} + +int strv_make_nulstr(char **l, char **p, size_t *q) { + /* A valid nulstr with two NULs at the end will be created, but + * q will be the length without the two trailing NULs. Thus the output + * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, + * and can also be parsed by strv_parse_nulstr as long as the length + * is provided separately. + */ + + 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 + 2)) + return -ENOMEM; + + memcpy(m + n, *i, z + 1); + n += z + 1; + } + + if (!m) { + m = new0(char, 1); + if (!m) + return -ENOMEM; + n = 1; + } else + /* make sure there is a second extra NUL at the end of resulting nulstr */ + m[n] = '\0'; + + assert(n > 0); + *p = m; + *q = n - 1; + + m = NULL; + + return 0; +} + +bool strv_overlap(char **a, char **b) { + char **i; + + STRV_FOREACH(i, a) + if (strv_contains(b, *i)) + return true; + + return false; +} + +static int str_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + + return strcmp(*a, *b); +} + +char **strv_sort(char **l) { + + if (strv_isempty(l)) + return l; + + qsort(l, strv_length(l), sizeof(char*), str_compare); + return l; +} + +bool strv_equal(char **a, char **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)) + return false; + + return true; +} + +void strv_print(char **l) { + char **s; + + STRV_FOREACH(s, l) + puts(*s); +} + +int strv_extendf(char ***l, const char *format, ...) { + va_list ap; + char *x; + int r; + + va_start(ap, format); + r = vasprintf(&x, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return strv_consume(l, x); +} + +char **strv_reverse(char **l) { + unsigned n, i; + + n = strv_length(l); + if (n <= 1) + return l; + + for (i = 0; i < n / 2; i++) + SWAP_TWO(l[i], l[n-1-i]); + + return l; +} + +char **strv_shell_escape(char **l, const char *bad) { + char **s; + + /* Escapes every character in every string in l that is in bad, + * edits in-place, does not roll-back on error. */ + + STRV_FOREACH(s, l) { + char *v; + + v = shell_escape(*s, bad); + if (!v) + return NULL; + + free(*s); + *s = v; + } + + return l; +} + +bool strv_fnmatch(char* const* patterns, const char *s, int flags) { + char* const* p; + + STRV_FOREACH(p, patterns) + if (fnmatch(*p, s, flags) == 0) + return true; + + 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 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; +} + +int fputstrv(FILE *f, char **l, const char *separator, bool *space) { + bool b = false; + char **s; + int r; + + /* Like fputs(), but for strv, and with a less stupid argument order */ + + if (!space) + space = &b; + + STRV_FOREACH(s, l) { + r = fputs_with_space(f, *s, separator, space); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/libbasic/src/strxcpyx.c b/src/libbasic/src/strxcpyx.c new file mode 100644 index 0000000000..b6225f0e12 --- /dev/null +++ b/src/libbasic/src/strxcpyx.c @@ -0,0 +1,100 @@ +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* + * Concatenates/copies strings. In any case, terminates in all cases + * with '\0' * and moves the @dest pointer forward to the added '\0'. + * Returns the * remaining size, and 0 if the string was truncated. + */ + +#include +#include +#include + +#include "basic/strxcpyx.h" + +size_t strpcpy(char **dest, size_t size, const char *src) { + size_t len; + + len = strlen(src); + if (len >= size) { + if (size > 1) + *dest = mempcpy(*dest, src, size-1); + size = 0; + } else { + if (len > 0) { + *dest = mempcpy(*dest, src, len); + size -= len; + } + } + *dest[0] = '\0'; + return size; +} + +size_t strpcpyf(char **dest, size_t size, const char *src, ...) { + va_list va; + int i; + + va_start(va, src); + i = vsnprintf(*dest, size, src, va); + if (i < (int)size) { + *dest += i; + size -= i; + } else { + *dest += size; + size = 0; + } + va_end(va); + *dest[0] = '\0'; + return size; +} + +size_t strpcpyl(char **dest, size_t size, const char *src, ...) { + va_list va; + + va_start(va, src); + do { + size = strpcpy(dest, size, src); + src = va_arg(va, char *); + } while (src != NULL); + va_end(va); + return size; +} + +size_t strscpy(char *dest, size_t size, const char *src) { + char *s; + + s = dest; + return strpcpy(&s, size, src); +} + +size_t strscpyl(char *dest, size_t size, const char *src, ...) { + va_list va; + char *s; + + va_start(va, src); + s = dest; + do { + size = strpcpy(&s, size, src); + src = va_arg(va, char *); + } while (src != NULL); + va_end(va); + + return size; +} diff --git a/src/libbasic/src/syslog-util.c b/src/libbasic/src/syslog-util.c new file mode 100644 index 0000000000..8647f2a2d6 --- /dev/null +++ b/src/libbasic/src/syslog-util.c @@ -0,0 +1,114 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/string-table.h" +#include "basic/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/libbasic/src/terminal-util.c b/src/libbasic/src/terminal-util.c new file mode 100644 index 0000000000..225ffa588a --- /dev/null +++ b/src/libbasic/src/terminal-util.c @@ -0,0 +1,1222 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/socket-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/util.h" + +static volatile unsigned cached_columns = 0; +static volatile unsigned cached_lines = 0; + +int chvt(int vt) { + _cleanup_close_ int fd; + + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return -errno; + + if (vt <= 0) { + int tiocl[2] = { + TIOCL_GETKMSGREDIRECT, + 0 + }; + + if (ioctl(fd, TIOCLINUX, tiocl) < 0) + return -errno; + + vt = tiocl[0] <= 0 ? 1 : tiocl[0]; + } + + if (ioctl(fd, VT_ACTIVATE, vt) < 0) + return -errno; + + return 0; +} + +int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { + struct termios old_termios, new_termios; + char c, line[LINE_MAX]; + + assert(f); + assert(ret); + + if (tcgetattr(fileno(f), &old_termios) >= 0) { + new_termios = old_termios; + + new_termios.c_lflag &= ~ICANON; + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + + if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { + size_t k; + + if (t != USEC_INFINITY) { + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { + tcsetattr(fileno(f), TCSADRAIN, &old_termios); + return -ETIMEDOUT; + } + } + + k = fread(&c, 1, 1, f); + + tcsetattr(fileno(f), TCSADRAIN, &old_termios); + + if (k <= 0) + return -EIO; + + if (need_nl) + *need_nl = c != '\n'; + + *ret = c; + return 0; + } + } + + if (t != USEC_INFINITY) { + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) + return -ETIMEDOUT; + } + + errno = 0; + if (!fgets(line, sizeof(line), f)) + return errno > 0 ? -errno : -EIO; + + truncate_nl(line); + + if (strlen(line) != 1) + return -EBADMSG; + + if (need_nl) + *need_nl = false; + + *ret = line[0]; + return 0; +} + +int ask_char(char *ret, const char *replies, const char *text, ...) { + int r; + + assert(ret); + assert(replies); + assert(text); + + for (;;) { + va_list ap; + char c; + bool need_nl = true; + + if (colors_enabled()) + fputs(ANSI_HIGHLIGHT, stdout); + + va_start(ap, text); + vprintf(text, ap); + va_end(ap); + + if (colors_enabled()) + fputs(ANSI_NORMAL, stdout); + + fflush(stdout); + + r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl); + if (r < 0) { + + if (r == -EBADMSG) { + puts("Bad input, please try again."); + continue; + } + + putchar('\n'); + return r; + } + + if (need_nl) + putchar('\n'); + + if (strchr(replies, c)) { + *ret = c; + return 0; + } + + puts("Read unexpected character, please try again."); + } +} + +int ask_string(char **ret, const char *text, ...) { + assert(ret); + assert(text); + + for (;;) { + char line[LINE_MAX]; + va_list ap; + + if (colors_enabled()) + fputs(ANSI_HIGHLIGHT, stdout); + + va_start(ap, text); + vprintf(text, ap); + va_end(ap); + + if (colors_enabled()) + fputs(ANSI_NORMAL, stdout); + + fflush(stdout); + + errno = 0; + if (!fgets(line, sizeof(line), stdin)) + return errno > 0 ? -errno : -EIO; + + if (!endswith(line, "\n")) + putchar('\n'); + else { + char *s; + + if (isempty(line)) + continue; + + truncate_nl(line); + s = strdup(line); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; + } + } +} + +int reset_terminal_fd(int fd, bool switch_to_text) { + struct termios termios; + int r = 0; + + /* Set terminal to some sane defaults */ + + assert(fd >= 0); + + /* We leave locked terminal attributes untouched, so that + * Plymouth may set whatever it wants to set, and we don't + * interfere with that. */ + + /* Disable exclusive mode, just in case */ + (void) ioctl(fd, TIOCNXCL); + + /* Switch to text mode */ + if (switch_to_text) + (void) ioctl(fd, KDSETMODE, KD_TEXT); + + /* Enable console unicode mode */ + (void) ioctl(fd, KDSKBMODE, K_UNICODE); + + if (tcgetattr(fd, &termios) < 0) { + r = -errno; + goto finish; + } + + /* We only reset the stuff that matters to the software. How + * hardware is set up we don't touch assuming that somebody + * else will do that for us */ + + termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC); + termios.c_iflag |= ICRNL | IMAXBEL | IUTF8; + termios.c_oflag |= ONLCR; + termios.c_cflag |= CREAD; + termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE; + + termios.c_cc[VINTR] = 03; /* ^C */ + termios.c_cc[VQUIT] = 034; /* ^\ */ + termios.c_cc[VERASE] = 0177; + termios.c_cc[VKILL] = 025; /* ^X */ + termios.c_cc[VEOF] = 04; /* ^D */ + termios.c_cc[VSTART] = 021; /* ^Q */ + termios.c_cc[VSTOP] = 023; /* ^S */ + termios.c_cc[VSUSP] = 032; /* ^Z */ + termios.c_cc[VLNEXT] = 026; /* ^V */ + termios.c_cc[VWERASE] = 027; /* ^W */ + termios.c_cc[VREPRINT] = 022; /* ^R */ + termios.c_cc[VEOL] = 0; + termios.c_cc[VEOL2] = 0; + + termios.c_cc[VTIME] = 0; + termios.c_cc[VMIN] = 1; + + if (tcsetattr(fd, TCSANOW, &termios) < 0) + r = -errno; + +finish: + /* Just in case, flush all crap out */ + (void) tcflush(fd, TCIOFLUSH); + + return r; +} + +int reset_terminal(const char *name) { + _cleanup_close_ int fd = -1; + + /* We open the terminal with O_NONBLOCK here, to ensure we + * don't block on carrier if this is a terminal with carrier + * configured. */ + + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return fd; + + return reset_terminal_fd(fd, true); +} + +int open_terminal(const char *name, int mode) { + int fd, r; + unsigned c = 0; + + /* + * If a TTY is in the process of being closed opening it might + * cause EIO. This is horribly awful, but unlikely to be + * changed in the kernel. Hence we work around this problem by + * retrying a couple of times. + * + * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 + */ + + if (mode & O_CREAT) + return -EINVAL; + + for (;;) { + fd = open(name, mode, 0); + if (fd >= 0) + break; + + if (errno != EIO) + return -errno; + + /* Max 1s in total */ + if (c >= 20) + return -errno; + + usleep(50 * USEC_PER_MSEC); + c++; + } + + r = isatty(fd); + if (r < 0) { + safe_close(fd); + return -errno; + } + + if (!r) { + safe_close(fd); + return -ENOTTY; + } + + return fd; +} + +int acquire_terminal( + const char *name, + bool fail, + bool force, + bool ignore_tiocstty_eperm, + usec_t timeout) { + + int fd = -1, notify = -1, r = 0, wd = -1; + usec_t ts = 0; + + assert(name); + + /* We use inotify to be notified when the tty is closed. We + * create the watch before checking if we can actually acquire + * it, so that we don't lose any event. + * + * Note: strictly speaking this actually watches for the + * device being closed, it does *not* really watch whether a + * tty loses its controlling process. However, unless some + * rogue process uses TIOCNOTTY on /dev/tty *after* closing + * its tty otherwise this will not become a problem. As long + * as the administrator makes sure not configure any service + * on the same tty as an untrusted user this should not be a + * problem. (Which he probably should not do anyway.) */ + + if (timeout != USEC_INFINITY) + ts = now(CLOCK_MONOTONIC); + + if (!fail && !force) { + notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0)); + if (notify < 0) { + r = -errno; + goto fail; + } + + wd = inotify_add_watch(notify, name, IN_CLOSE); + if (wd < 0) { + r = -errno; + goto fail; + } + } + + for (;;) { + struct sigaction sa_old, sa_new = { + .sa_handler = SIG_IGN, + .sa_flags = SA_RESTART, + }; + + if (notify >= 0) { + r = flush_fd(notify); + if (r < 0) + goto fail; + } + + /* We pass here O_NOCTTY only so that we can check the return + * value TIOCSCTTY and have a reliable way to figure out if we + * successfully became the controlling process of the tty */ + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed + * if we already own the tty. */ + assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); + + /* First, try to get the tty */ + if (ioctl(fd, TIOCSCTTY, force) < 0) + r = -errno; + + assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); + + /* Sometimes, it makes sense to ignore TIOCSCTTY + * returning EPERM, i.e. when very likely we already + * are have this controlling terminal. */ + if (r < 0 && r == -EPERM && ignore_tiocstty_eperm) + r = 0; + + if (r < 0 && (force || fail || r != -EPERM)) + goto fail; + + if (r >= 0) + break; + + assert(!fail); + assert(!force); + assert(notify >= 0); + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + if (timeout != USEC_INFINITY) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (ts + timeout < n) { + r = -ETIMEDOUT; + goto fail; + } + + r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); + if (r < 0) + goto fail; + + if (r == 0) { + r = -ETIMEDOUT; + goto fail; + } + } + + l = read(notify, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + + r = -errno; + goto fail; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + if (e->wd != wd || !(e->mask & IN_CLOSE)) { + r = -EIO; + goto fail; + } + } + + break; + } + + /* We close the tty fd here since if the old session + * ended our handle will be dead. It's important that + * we do this after sleeping, so that we don't enter + * an endless loop. */ + fd = safe_close(fd); + } + + safe_close(notify); + + return fd; + +fail: + safe_close(fd); + safe_close(notify); + + return r; +} + +int release_terminal(void) { + static const struct sigaction sa_new = { + .sa_handler = SIG_IGN, + .sa_flags = SA_RESTART, + }; + + _cleanup_close_ int fd = -1; + struct sigaction sa_old; + int r = 0; + + fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return -errno; + + /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed + * by our own TIOCNOTTY */ + assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); + + if (ioctl(fd, TIOCNOTTY) < 0) + r = -errno; + + assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); + + return r; +} + +int terminal_vhangup_fd(int fd) { + assert(fd >= 0); + + if (ioctl(fd, TIOCVHANGUP) < 0) + return -errno; + + return 0; +} + +int terminal_vhangup(const char *name) { + _cleanup_close_ int fd; + + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return fd; + + return terminal_vhangup_fd(fd); +} + +int vt_disallocate(const char *name) { + _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 + * entirely (including the scrollback buffer) */ + + if (!startswith(name, "/dev/")) + return -EINVAL; + + if (!tty_is_vc(name)) { + /* So this is not a VT. I guess we cannot deallocate + * it then. But let's at least clear the screen */ + + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + loop_write(fd, + "\033[r" /* clear scrolling region */ + "\033[H" /* move home */ + "\033[2J", /* clear screen */ + 10, false); + return 0; + } + + if (!startswith(name, "/dev/tty")) + return -EINVAL; + + r = safe_atou(name+8, &u); + if (r < 0) + return r; + + if (u <= 0) + return -EINVAL; + + /* Try to deallocate */ + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return fd; + + r = ioctl(fd, VT_DISALLOCATE, u); + fd = safe_close(fd); + + if (r >= 0) + return 0; + + if (errno != EBUSY) + return -errno; + + /* Couldn't deallocate, so let's clear it fully with + * scrollback */ + fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return fd; + + loop_write(fd, + "\033[r" /* clear scrolling region */ + "\033[H" /* move home */ + "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */ + 10, false); + return 0; +} + +int make_console_stdio(void) { + int fd, r; + + /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ + + fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY); + if (fd < 0) + return log_error_errno(fd, "Failed to acquire terminal: %m"); + + r = 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"); + + return 0; +} + +bool tty_is_vc(const char *tty) { + assert(tty); + + return vtnr_from_tty(tty) >= 0; +} + +bool tty_is_console(const char *tty) { + assert(tty); + + if (startswith(tty, "/dev/")) + tty += 5; + + return streq(tty, "console"); +} + +int vtnr_from_tty(const char *tty) { + int i, r; + + assert(tty); + + if (startswith(tty, "/dev/")) + tty += 5; + + if (!startswith(tty, "tty") ) + return -EINVAL; + + if (tty[3] < '0' || tty[3] > '9') + return -EINVAL; + + r = safe_atoi(tty+3, &i); + if (r < 0) + return r; + + if (i < 0 || i > 63) + return -EINVAL; + + return i; +} + +char *resolve_dev_console(char **active) { + char *tty; + + /* Resolve where /dev/console is pointing to, if /sys is actually ours + * (i.e. not read-only-mounted which is a sign for container setups) */ + + if (path_is_read_only_fs("/sys") > 0) + return NULL; + + if (read_one_line_file("/sys/class/tty/console/active", active) < 0) + return NULL; + + /* If multiple log outputs are configured the last one is what + * /dev/console points to */ + tty = strrchr(*active, ' '); + if (tty) + tty++; + else + tty = *active; + + if (streq(tty, "tty0")) { + char *tmp; + + /* Get the active VC (e.g. tty1) */ + if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) { + free(*active); + tty = *active = tmp; + } + } + + return tty; +} + +int get_kernel_consoles(char ***consoles) { + _cleanup_strv_free_ char **con = NULL; + _cleanup_free_ char *line = NULL; + const char *active; + int r; + + assert(consoles); + + r = read_one_line_file("/sys/class/tty/console/active", &line); + if (r < 0) + return r; + + active = line; + for (;;) { + _cleanup_free_ char *tty = NULL; + char *path; + + r = extract_first_word(&active, &tty, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + if (streq(tty, "tty0")) { + tty = mfree(tty); + r = read_one_line_file("/sys/class/tty/tty0/active", &tty); + if (r < 0) + return r; + } + + path = strappend("/dev/", tty); + if (!path) + return -ENOMEM; + + if (access(path, F_OK) < 0) { + log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path); + free(path); + continue; + } + + r = strv_consume(&con, path); + if (r < 0) + return r; + } + + if (strv_isempty(con)) { + log_debug("No devices found for system console"); + + r = strv_extend(&con, "/dev/console"); + if (r < 0) + return r; + } + + *consoles = con; + con = NULL; + return 0; +} + +bool tty_is_vc_resolve(const char *tty) { + _cleanup_free_ char *active = NULL; + + assert(tty); + + if (startswith(tty, "/dev/")) + tty += 5; + + if (streq(tty, "console")) { + tty = resolve_dev_console(&active); + if (!tty) + return false; + } + + return tty_is_vc(tty); +} + +const char *default_term_for_tty(const char *tty) { + return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; +} + +int fd_columns(int fd) { + struct winsize ws = {}; + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_col <= 0) + return -EIO; + + return ws.ws_col; +} + +unsigned columns(void) { + const char *e; + int c; + + if (_likely_(cached_columns > 0)) + return cached_columns; + + c = 0; + e = getenv("COLUMNS"); + if (e) + (void) safe_atoi(e, &c); + + if (c <= 0) + c = fd_columns(STDOUT_FILENO); + + if (c <= 0) + c = 80; + + cached_columns = c; + return cached_columns; +} + +int fd_lines(int fd) { + struct winsize ws = {}; + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +unsigned lines(void) { + const char *e; + int l; + + if (_likely_(cached_lines > 0)) + return cached_lines; + + l = 0; + e = getenv("LINES"); + if (e) + (void) safe_atoi(e, &l); + + if (l <= 0) + l = fd_lines(STDOUT_FILENO); + + if (l <= 0) + l = 24; + + cached_lines = l; + return cached_lines; +} + +/* intended to be used as a SIGWINCH sighandler */ +void columns_lines_cache_reset(int signum) { + cached_columns = 0; + cached_lines = 0; +} + +bool on_tty(void) { + static int cached_on_tty = -1; + + if (_unlikely_(cached_on_tty < 0)) + cached_on_tty = isatty(STDOUT_FILENO) > 0; + + return cached_on_tty; +} + +int make_stdio(int fd) { + int r, s, t; + + assert(fd >= 0); + + r = dup2(fd, STDIN_FILENO); + s = dup2(fd, STDOUT_FILENO); + t = dup2(fd, STDERR_FILENO); + + if (fd >= 3) + safe_close(fd); + + if (r < 0 || s < 0 || t < 0) + return -errno; + + /* Explicitly unset O_CLOEXEC, since if fd was < 3, then + * dup2() was a NOP and the bit hence possibly set. */ + stdio_unset_cloexec(); + + return 0; +} + +int make_null_stdio(void) { + int null_fd; + + null_fd = open("/dev/null", O_RDWR|O_NOCTTY); + if (null_fd < 0) + return -errno; + + return make_stdio(null_fd); +} + +int getttyname_malloc(int fd, char **ret) { + size_t l = 100; + int r; + + assert(fd >= 0); + assert(ret); + + for (;;) { + char path[l]; + + r = ttyname_r(fd, path, sizeof(path)); + if (r == 0) { + const char *p; + char *c; + + p = startswith(path, "/dev/"); + c = strdup(p ?: path); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; + } + + if (r != ERANGE) + return -r; + + l *= 2; + } + + return 0; +} + +int getttyname_harder(int fd, char **r) { + int k; + char *s = NULL; + + k = getttyname_malloc(fd, &s); + if (k < 0) + return k; + + if (streq(s, "tty")) { + free(s); + return get_ctty(0, NULL, r); + } + + *r = s; + return 0; +} + +int get_ctty_devnr(pid_t pid, dev_t *d) { + int r; + _cleanup_free_ char *line = NULL; + const char *p; + unsigned long ttynr; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "stat"); + r = read_one_line_file(p, &line); + if (r < 0) + return r; + + p = strrchr(line, ')'); + if (!p) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%*d " /* ppid */ + "%*d " /* pgrp */ + "%*d " /* session */ + "%lu ", /* ttynr */ + &ttynr) != 1) + return -EIO; + + if (major(ttynr) == 0 && minor(ttynr) == 0) + return -ENXIO; + + if (d) + *d = (dev_t) ttynr; + + return 0; +} + +int get_ctty(pid_t pid, dev_t *_devnr, char **r) { + char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL; + _cleanup_free_ char *s = NULL; + const char *p; + dev_t devnr; + int k; + + assert(r); + + k = get_ctty_devnr(pid, &devnr); + if (k < 0) + return k; + + sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr)); + + k = readlink_malloc(fn, &s); + if (k < 0) { + + if (k != -ENOENT) + return k; + + /* This is an ugly hack */ + if (major(devnr) == 136) { + if (asprintf(&b, "pts/%u", minor(devnr)) < 0) + return -ENOMEM; + } else { + /* Probably something like the ptys which have no + * symlink in /dev/char. Let's return something + * vaguely useful. */ + + b = strdup(fn + 5); + if (!b) + return -ENOMEM; + } + } else { + if (startswith(s, "/dev/")) + p = s + 5; + else if (startswith(s, "../")) + p = s + 3; + else + p = s; + + b = strdup(p); + if (!b) + return -ENOMEM; + } + + *r = b; + if (_devnr) + *_devnr = devnr; + + return 0; +} + +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; + + /* Like ptsname(), but doesn't assume that the path is + * accessible in the local namespace. */ + + r = ioctl(pty, TIOCGPTN, &no); + if (r < 0) + return -errno; + + if (no < 0) + return -EIO; + + if (asprintf(ret, "/dev/pts/%i", no) < 0) + return -ENOMEM; + + 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 terminal_is_dumb(void) { + const char *e; + + if (!on_tty()) + return true; + + e = getenv("TERM"); + if (!e) + return true; + + return streq(e, "dumb"); +} + +bool colors_enabled(void) { + static int enabled = -1; + + if (_unlikely_(enabled < 0)) { + const char *colors; + + colors = getenv("SYSTEMD_COLORS"); + if (colors) + enabled = parse_boolean(colors) != 0; + else + enabled = !terminal_is_dumb(); + } + + return enabled; +} diff --git a/src/libbasic/src/time-util.c b/src/libbasic/src/time-util.c new file mode 100644 index 0000000000..1352c59295 --- /dev/null +++ b/src/libbasic/src/time-util.c @@ -0,0 +1,1229 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" + +static nsec_t timespec_load_nsec(const struct timespec *ts); + +static clockid_t map_clock_id(clockid_t c) { + + /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will + * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is + * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on + * those archs. */ + + switch (c) { + + case CLOCK_BOOTTIME_ALARM: + return CLOCK_BOOTTIME; + + case CLOCK_REALTIME_ALARM: + return CLOCK_REALTIME; + + default: + return c; + } +} + +usec_t now(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); + + return timespec_load(&ts); +} + +nsec_t now_nsec(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); + + return timespec_load_nsec(&ts); +} + +dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { + assert(ts); + + ts->realtime = now(CLOCK_REALTIME); + ts->monotonic = now(CLOCK_MONOTONIC); + + return ts; +} + +triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { + assert(ts); + + ts->realtime = now(CLOCK_REALTIME); + ts->monotonic = now(CLOCK_MONOTONIC); + ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; + + return ts; +} + +dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { + int64_t delta; + assert(ts); + + if (u == USEC_INFINITY || u <= 0) { + ts->realtime = ts->monotonic = u; + return ts; + } + + ts->realtime = u; + + delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; + ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); + + return ts; +} + +triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { + int64_t delta; + + assert(ts); + + if (u == USEC_INFINITY || u <= 0) { + ts->realtime = ts->monotonic = ts->boottime = u; + return ts; + } + + ts->realtime = u; + delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; + ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); + ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; + + return ts; +} + +dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + assert(ts); + + if (u == USEC_INFINITY) { + ts->realtime = ts->monotonic = USEC_INFINITY; + return ts; + } + + ts->monotonic = u; + delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; + ts->realtime = usec_sub(now(CLOCK_REALTIME), delta); + + return ts; +} + +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + + if (u == USEC_INFINITY) { + ts->realtime = ts->monotonic = USEC_INFINITY; + return ts; + } + + dual_timestamp_get(ts); + delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; + ts->realtime = usec_sub(ts->realtime, delta); + ts->monotonic = usec_sub(ts->monotonic, delta); + + return ts; +} + +usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { + + switch (clock) { + + case CLOCK_REALTIME: + case CLOCK_REALTIME_ALARM: + return ts->realtime; + + case CLOCK_MONOTONIC: + return ts->monotonic; + + case CLOCK_BOOTTIME: + case CLOCK_BOOTTIME_ALARM: + return ts->boottime; + + default: + return USEC_INFINITY; + } +} + +usec_t timespec_load(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) + return USEC_INFINITY; + + if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) + return USEC_INFINITY; + + return + (usec_t) ts->tv_sec * USEC_PER_SEC + + (usec_t) ts->tv_nsec / NSEC_PER_USEC; +} + +static nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) + return NSEC_INFINITY; + + if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) + return NSEC_INFINITY; + + return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; +} + +struct timespec *timespec_store(struct timespec *ts, usec_t u) { + assert(ts); + + if (u == USEC_INFINITY) { + ts->tv_sec = (time_t) -1; + ts->tv_nsec = (long) -1; + return ts; + } + + ts->tv_sec = (time_t) (u / USEC_PER_SEC); + ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); + + return ts; +} + +usec_t timeval_load(const struct timeval *tv) { + assert(tv); + + if (tv->tv_sec == (time_t) -1 && + tv->tv_usec == (suseconds_t) -1) + return USEC_INFINITY; + + if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) + return USEC_INFINITY; + + return + (usec_t) tv->tv_sec * USEC_PER_SEC + + (usec_t) tv->tv_usec; +} + +struct timeval *timeval_store(struct timeval *tv, usec_t u) { + assert(tv); + + if (u == USEC_INFINITY) { + tv->tv_sec = (time_t) -1; + tv->tv_usec = (suseconds_t) -1; + } else { + tv->tv_sec = (time_t) (u / USEC_PER_SEC); + tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); + } + + return tv; +} + +static char *format_timestamp_internal(char *buf, size_t l, usec_t t, + bool utc, bool us) { + struct tm tm; + time_t sec; + int k; + + assert(buf); + assert(l > 0); + + if (t <= 0 || t == USEC_INFINITY) + return NULL; + + sec = (time_t) (t / USEC_PER_SEC); + localtime_or_gmtime_r(&sec, &tm, utc); + + if (us) + k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm); + else + k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm); + + if (k <= 0) + return NULL; + if (us) { + snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); + if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0) + return NULL; + } + + return buf; +} + +char *format_timestamp(char *buf, size_t l, usec_t t) { + return format_timestamp_internal(buf, l, t, false, false); +} + +char *format_timestamp_utc(char *buf, size_t l, usec_t t) { + return format_timestamp_internal(buf, l, t, true, false); +} + +char *format_timestamp_us(char *buf, size_t l, usec_t t) { + return format_timestamp_internal(buf, l, t, false, true); +} + +char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { + return format_timestamp_internal(buf, l, t, true, true); +} + +char *format_timestamp_relative(char *buf, size_t l, usec_t t) { + const char *s; + usec_t n, d; + + if (t <= 0 || t == USEC_INFINITY) + return NULL; + + n = now(CLOCK_REALTIME); + if (n > t) { + d = n - t; + s = "ago"; + } else { + d = t - n; + s = "left"; + } + + if (d >= USEC_PER_YEAR) + snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", + d / USEC_PER_YEAR, + (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); + else if (d >= USEC_PER_MONTH) + snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", + d / USEC_PER_MONTH, + (d % USEC_PER_MONTH) / USEC_PER_DAY, s); + else if (d >= USEC_PER_WEEK) + snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", + d / USEC_PER_WEEK, + (d % USEC_PER_WEEK) / USEC_PER_DAY, s); + else if (d >= 2*USEC_PER_DAY) + snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); + else if (d >= 25*USEC_PER_HOUR) + snprintf(buf, l, "1 day " USEC_FMT "h %s", + (d - USEC_PER_DAY) / USEC_PER_HOUR, s); + else if (d >= 6*USEC_PER_HOUR) + snprintf(buf, l, USEC_FMT "h %s", + d / USEC_PER_HOUR, s); + else if (d >= USEC_PER_HOUR) + snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", + d / USEC_PER_HOUR, + (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); + else if (d >= 5*USEC_PER_MINUTE) + snprintf(buf, l, USEC_FMT "min %s", + d / USEC_PER_MINUTE, s); + else if (d >= USEC_PER_MINUTE) + snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", + d / USEC_PER_MINUTE, + (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); + else if (d >= USEC_PER_SEC) + snprintf(buf, l, USEC_FMT "s %s", + d / USEC_PER_SEC, s); + else if (d >= USEC_PER_MSEC) + snprintf(buf, l, USEC_FMT "ms %s", + d / USEC_PER_MSEC, s); + else if (d > 0) + snprintf(buf, l, USEC_FMT"us %s", + d, s); + else + snprintf(buf, l, "now"); + + buf[l-1] = 0; + return buf; +} + +char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { + static const struct { + const char *suffix; + usec_t usec; + } table[] = { + { "y", USEC_PER_YEAR }, + { "month", USEC_PER_MONTH }, + { "w", USEC_PER_WEEK }, + { "d", USEC_PER_DAY }, + { "h", USEC_PER_HOUR }, + { "min", USEC_PER_MINUTE }, + { "s", USEC_PER_SEC }, + { "ms", USEC_PER_MSEC }, + { "us", 1 }, + }; + + unsigned i; + char *p = buf; + bool something = false; + + assert(buf); + assert(l > 0); + + if (t == USEC_INFINITY) { + strncpy(p, "infinity", l-1); + p[l-1] = 0; + return p; + } + + if (t <= 0) { + strncpy(p, "0", l-1); + p[l-1] = 0; + return p; + } + + /* The result of this function can be parsed with parse_sec */ + + for (i = 0; i < ELEMENTSOF(table); i++) { + int k = 0; + size_t n; + bool done = false; + usec_t a, b; + + if (t <= 0) + break; + + if (t < accuracy && something) + break; + + if (t < table[i].usec) + continue; + + if (l <= 1) + break; + + a = t / table[i].usec; + b = t % table[i].usec; + + /* Let's see if we should shows this in dot notation */ + if (t < USEC_PER_MINUTE && b > 0) { + usec_t cc; + int j; + + j = 0; + for (cc = table[i].usec; cc > 1; cc /= 10) + j++; + + for (cc = accuracy; cc > 1; cc /= 10) { + b /= 10; + j--; + } + + if (j > 0) { + k = snprintf(p, l, + "%s"USEC_FMT".%0*llu%s", + p > buf ? " " : "", + a, + j, + (unsigned long long) b, + table[i].suffix); + + t = 0; + done = true; + } + } + + /* No? Then let's show it normally */ + if (!done) { + k = snprintf(p, l, + "%s"USEC_FMT"%s", + p > buf ? " " : "", + a, + table[i].suffix); + + t = b; + } + + n = MIN((size_t) k, l); + + l -= n; + p += n; + + something = true; + } + + *p = 0; + + return buf; +} + +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { + + assert(f); + assert(name); + assert(t); + + if (!dual_timestamp_is_set(t)) + return; + + fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", + name, + t->realtime, + t->monotonic); +} + +int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { + unsigned long long a, b; + + assert(value); + assert(t); + + if (sscanf(value, "%llu %llu", &a, &b) != 2) { + log_debug("Failed to parse dual timestamp value \"%s\": %m", value); + return -EINVAL; + } + + t->realtime = a; + t->monotonic = b; + + return 0; +} + +int timestamp_deserialize(const char *value, usec_t *timestamp) { + int r; + + assert(value); + + r = safe_atou64(value, timestamp); + if (r < 0) + return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); + + return r; +} + +int parse_timestamp(const char *t, usec_t *usec) { + static const struct { + const char *name; + const int nr; + } day_nr[] = { + { "Sunday", 0 }, + { "Sun", 0 }, + { "Monday", 1 }, + { "Mon", 1 }, + { "Tuesday", 2 }, + { "Tue", 2 }, + { "Wednesday", 3 }, + { "Wed", 3 }, + { "Thursday", 4 }, + { "Thu", 4 }, + { "Friday", 5 }, + { "Fri", 5 }, + { "Saturday", 6 }, + { "Sat", 6 }, + }; + + const char *k; + const char *utc; + struct tm tm, copy; + time_t x; + usec_t x_usec, plus = 0, minus = 0, ret; + int r, weekday = -1; + unsigned i; + + /* + * Allowed syntaxes: + * + * 2012-09-22 16:34:22 + * 2012-09-22 16:34 (seconds will be set to 0) + * 2012-09-22 (time will be set to 00:00:00) + * 16:34:22 (date will be set to today) + * 16:34 (date will be set to today, seconds to 0) + * now + * yesterday (time is set to 00:00:00) + * today (time is set to 00:00:00) + * tomorrow (time is set to 00:00:00) + * +5min + * -5days + * @2147483647 (seconds since epoch) + * + */ + + assert(t); + assert(usec); + + if (t[0] == '@') + return parse_sec(t + 1, usec); + + ret = now(CLOCK_REALTIME); + + if (streq(t, "now")) + goto finish; + + else if (t[0] == '+') { + r = parse_sec(t+1, &plus); + if (r < 0) + return r; + + goto finish; + + } else if (t[0] == '-') { + r = parse_sec(t+1, &minus); + if (r < 0) + return r; + + goto finish; + + } else if ((k = endswith(t, " ago"))) { + t = strndupa(t, k - t); + + r = parse_sec(t, &minus); + if (r < 0) + return r; + + goto finish; + + } else if ((k = endswith(t, " left"))) { + t = strndupa(t, k - t); + + 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; + + if (!startswith_no_case(t, day_nr[i].name)) + continue; + + skip = strlen(day_nr[i].name); + if (t[skip] != ' ') + continue; + + weekday = day_nr[i].nr; + t += skip + 1; + break; + } + + copy = tm; + k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); + 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) { + 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 from_tm; + } + + tm = copy; + k = strptime(t, "%Y-%m-%d %H:%M", &tm); + if (k && *k == 0) { + tm.tm_sec = 0; + 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 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 from_tm; + } + + tm = copy; + k = strptime(t, "%H:%M:%S", &tm); + 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 from_tm; + } + + return -EINVAL; + +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 + x_usec; + +finish: + ret += plus; + if (ret > minus) + ret -= minus; + else + ret = 0; + + *usec = ret; + + return 0; +} + +static char* extract_multiplier(char *p, usec_t *multiplier) { + 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 }, + { "minutes", USEC_PER_MINUTE }, + { "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 }, + }; + unsigned i; + + for (i = 0; i < ELEMENTSOF(table); i++) { + char *e; + + e = startswith(p, table[i].suffix); + if (e) { + *multiplier = table[i].usec; + return e; + } + } + + return p; +} + +int parse_time(const char *t, usec_t *usec, usec_t default_unit) { + const char *p, *s; + usec_t r = 0; + bool something = false; + + assert(t); + assert(usec); + assert(default_unit > 0); + + p = t; + + p += strspn(p, WHITESPACE); + s = startswith(p, "infinity"); + if (s) { + s += strspn(s, WHITESPACE); + if (*s != 0) + return -EINVAL; + + *usec = USEC_INFINITY; + return 0; + } + + for (;;) { + long long l, z = 0; + char *e; + unsigned n = 0; + usec_t multiplier = default_unit, k; + + p += strspn(p, WHITESPACE); + + if (*p == 0) { + if (!something) + return -EINVAL; + + break; + } + + errno = 0; + l = strtoll(p, &e, 10); + if (errno > 0) + return -errno; + if (l < 0) + return -ERANGE; + + if (*e == '.') { + char *b = e + 1; + + errno = 0; + z = strtoll(b, &e, 10); + if (errno > 0) + return -errno; + + if (z < 0) + return -ERANGE; + + if (e == b) + return -EINVAL; + + n = e - b; + + } else if (e == p) + return -EINVAL; + + e += strspn(e, WHITESPACE); + p = extract_multiplier(e, &multiplier); + + something = true; + + k = (usec_t) z * multiplier; + + for (; n > 0; n--) + k /= 10; + + r += (usec_t) l * multiplier + k; + } + + *usec = r; + + 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; + nsec_t nsec; + } table[] = { + { "seconds", NSEC_PER_SEC }, + { "second", NSEC_PER_SEC }, + { "sec", NSEC_PER_SEC }, + { "s", NSEC_PER_SEC }, + { "minutes", NSEC_PER_MINUTE }, + { "minute", NSEC_PER_MINUTE }, + { "min", NSEC_PER_MINUTE }, + { "months", NSEC_PER_MONTH }, + { "month", NSEC_PER_MONTH }, + { "msec", NSEC_PER_MSEC }, + { "ms", NSEC_PER_MSEC }, + { "m", NSEC_PER_MINUTE }, + { "hours", NSEC_PER_HOUR }, + { "hour", NSEC_PER_HOUR }, + { "hr", NSEC_PER_HOUR }, + { "h", NSEC_PER_HOUR }, + { "days", NSEC_PER_DAY }, + { "day", NSEC_PER_DAY }, + { "d", NSEC_PER_DAY }, + { "weeks", NSEC_PER_WEEK }, + { "week", NSEC_PER_WEEK }, + { "w", NSEC_PER_WEEK }, + { "years", NSEC_PER_YEAR }, + { "year", NSEC_PER_YEAR }, + { "y", NSEC_PER_YEAR }, + { "usec", NSEC_PER_USEC }, + { "us", NSEC_PER_USEC }, + { "nsec", 1ULL }, + { "ns", 1ULL }, + { "", 1ULL }, /* default is nsec */ + }; + + const char *p, *s; + nsec_t r = 0; + bool something = false; + + assert(t); + assert(nsec); + + p = t; + + p += strspn(p, WHITESPACE); + s = startswith(p, "infinity"); + if (s) { + s += strspn(s, WHITESPACE); + if (*s != 0) + return -EINVAL; + + *nsec = NSEC_INFINITY; + return 0; + } + + for (;;) { + long long l, z = 0; + char *e; + unsigned i, n = 0; + + p += strspn(p, WHITESPACE); + + if (*p == 0) { + if (!something) + return -EINVAL; + + break; + } + + errno = 0; + l = strtoll(p, &e, 10); + + if (errno > 0) + return -errno; + + if (l < 0) + return -ERANGE; + + if (*e == '.') { + char *b = e + 1; + + errno = 0; + z = strtoll(b, &e, 10); + if (errno > 0) + return -errno; + + if (z < 0) + return -ERANGE; + + if (e == b) + return -EINVAL; + + n = e - b; + + } else if (e == p) + return -EINVAL; + + e += strspn(e, WHITESPACE); + + for (i = 0; i < ELEMENTSOF(table); i++) + if (startswith(e, table[i].suffix)) { + nsec_t k = (nsec_t) z * table[i].nsec; + + for (; n > 0; n--) + k /= 10; + + r += (nsec_t) l * table[i].nsec + k; + p = e + strlen(table[i].suffix); + + something = true; + break; + } + + if (i >= ELEMENTSOF(table)) + return -EINVAL; + + } + + *nsec = r; + + return 0; +} + +bool ntp_synced(void) { + struct timex txc = {}; + + if (adjtimex(&txc) < 0) + return false; + + if (txc.status & STA_UNSYNC) + return false; + + return true; +} + +int get_timezones(char ***ret) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **zones = NULL; + size_t n_zones = 0, n_allocated = 0; + + assert(ret); + + zones = strv_new("UTC", NULL); + if (!zones) + return -ENOMEM; + + n_allocated = 2; + n_zones = 1; + + f = fopen("/usr/share/zoneinfo/zone.tab", "re"); + if (f) { + char l[LINE_MAX]; + + FOREACH_LINE(l, f, return -errno) { + char *p, *w; + size_t k; + + p = strstrip(l); + + if (isempty(p) || *p == '#') + continue; + + /* Skip over country code */ + p += strcspn(p, WHITESPACE); + p += strspn(p, WHITESPACE); + + /* Skip over coordinates */ + p += strcspn(p, WHITESPACE); + p += strspn(p, WHITESPACE); + + /* Found timezone name */ + k = strcspn(p, WHITESPACE); + if (k <= 0) + continue; + + w = strndup(p, k); + if (!w) + return -ENOMEM; + + if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { + free(w); + return -ENOMEM; + } + + zones[n_zones++] = w; + zones[n_zones] = NULL; + } + + strv_sort(zones); + + } else if (errno != ENOENT) + return -errno; + + *ret = zones; + zones = NULL; + + return 0; +} + +bool timezone_is_valid(const char *name) { + bool slash = false; + const char *p, *t; + struct stat st; + + if (isempty(name)) + return false; + + if (name[0] == '/') + return false; + + for (p = name; *p; p++) { + if (!(*p >= '0' && *p <= '9') && + !(*p >= 'a' && *p <= 'z') && + !(*p >= 'A' && *p <= 'Z') && + !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) + return false; + + if (*p == '/') { + + if (slash) + return false; + + slash = true; + } else + slash = false; + } + + if (slash) + return false; + + t = strjoina("/usr/share/zoneinfo/", name); + if (stat(t, &st) < 0) + return false; + + if (!S_ISREG(st.st_mode)) + return false; + + return true; +} + +bool clock_boottime_supported(void) { + static int supported = -1; + + /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ + + if (supported < 0) { + int fd; + + fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + supported = false; + else { + safe_close(fd); + supported = true; + } + } + + return supported; +} + +clockid_t clock_boottime_or_monotonic(void) { + if (clock_boottime_supported()) + return CLOCK_BOOTTIME; + else + return CLOCK_MONOTONIC; +} + +bool clock_supported(clockid_t clock) { + struct timespec ts; + + switch (clock) { + + case CLOCK_MONOTONIC: + case CLOCK_REALTIME: + return true; + + case CLOCK_BOOTTIME: + return clock_boottime_supported(); + + case CLOCK_BOOTTIME_ALARM: + if (!clock_boottime_supported()) + return false; + + /* fall through, after checking the cached value for CLOCK_BOOTTIME. */ + + default: + /* For everything else, check properly */ + return clock_gettime(clock, &ts) >= 0; + } +} + +int get_timezone(char **tz) { + _cleanup_free_ char *t = NULL; + const char *e; + char *z; + int r; + + r = readlink_malloc("/etc/localtime", &t); + if (r < 0) + return r; /* returns EINVAL if not a symlink */ + + e = path_startswith(t, "/usr/share/zoneinfo/"); + if (!e) + e = path_startswith(t, "../usr/share/zoneinfo/"); + if (!e) + return -EINVAL; + + if (!timezone_is_valid(e)) + return -EINVAL; + + z = strdup(e); + if (!z) + return -ENOMEM; + + *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/libbasic/src/unit-name.c b/src/libbasic/src/unit-name.c new file mode 100644 index 0000000000..42c79da536 --- /dev/null +++ b/src/libbasic/src/unit-name.c @@ -0,0 +1,1049 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/glob-util.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" + +/* Characters valid in a unit name. */ +#define VALID_CHARS \ + DIGITS \ + LETTERS \ + ":-_.\\" + +/* The same, but also permits the single @ character that may appear */ +#define VALID_CHARS_WITH_AT \ + "@" \ + VALID_CHARS + +/* All chars valid in a unit name glob */ +#define VALID_CHARS_GLOB \ + VALID_CHARS_WITH_AT \ + "[]!-*?" + +bool unit_name_is_valid(const char *n, UnitNameFlags flags) { + const char *e, *i, *at; + + assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0); + + if (_unlikely_(flags == 0)) + return false; + + if (isempty(n)) + return false; + + if (strlen(n) >= UNIT_NAME_MAX) + return false; + + e = strrchr(n, '.'); + if (!e || e == n) + return false; + + if (unit_type_from_string(e + 1) < 0) + return false; + + for (i = n, at = NULL; i < e; i++) { + + if (*i == '@' && !at) + at = i; + + if (!strchr("@" VALID_CHARS, *i)) + return false; + } + + if (at == n) + return false; + + if (flags & UNIT_NAME_PLAIN) + if (!at) + return true; + + if (flags & UNIT_NAME_INSTANCE) + if (at && e > at + 1) + return true; + + if (flags & UNIT_NAME_TEMPLATE) + if (at && e == at + 1) + return true; + + return false; +} + +bool unit_prefix_is_valid(const char *p) { + + /* We don't allow additional @ in the prefix string */ + + if (isempty(p)) + return false; + + return in_charset(p, VALID_CHARS); +} + +bool unit_instance_is_valid(const char *i) { + + /* The max length depends on the length of the string, so we + * don't really check this here. */ + + if (isempty(i)) + return false; + + /* We allow additional @ in the instance string, we do not + * allow them in the prefix! */ + + return in_charset(i, "@" VALID_CHARS); +} + +bool unit_suffix_is_valid(const char *s) { + if (isempty(s)) + return false; + + if (s[0] != '.') + return false; + + if (unit_type_from_string(s + 1) < 0) + return false; + + return true; +} + +int unit_name_to_prefix(const char *n, char **ret) { + const char *p; + char *s; + + assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + p = strchr(n, '@'); + if (!p) + p = strrchr(n, '.'); + + assert_se(p); + + s = strndup(n, p - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int unit_name_to_instance(const char *n, char **instance) { + const char *p, *d; + char *i; + + assert(n); + assert(instance); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + /* Everything past the first @ and before the last . is the instance */ + p = strchr(n, '@'); + if (!p) { + *instance = NULL; + return 0; + } + + p++; + + d = strrchr(p, '.'); + if (!d) + return -EINVAL; + + i = strndup(p, d-p); + if (!i) + return -ENOMEM; + + *instance = i; + return 1; +} + +int unit_name_to_prefix_and_instance(const char *n, char **ret) { + const char *d; + char *s; + + assert(n); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + d = strrchr(n, '.'); + if (!d) + return -EINVAL; + + s = strndup(n, d - n); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +UnitType unit_name_to_type(const char *n) { + const char *e; + + assert(n); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return _UNIT_TYPE_INVALID; + + assert_se(e = strrchr(n, '.')); + + return unit_type_from_string(e + 1); +} + +int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { + char *e, *s; + size_t a, b; + + assert(n); + assert(suffix); + assert(ret); + + if (!unit_name_is_valid(n, UNIT_NAME_ANY)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + assert_se(e = strrchr(n, '.')); + + a = e - n; + b = strlen(suffix); + + s = new(char, a + b + 1); + if (!s) + return -ENOMEM; + + strcpy(mempcpy(s, n, a), suffix); + *ret = s; + + return 0; +} + +int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) { + char *s; + + assert(prefix); + assert(suffix); + assert(ret); + + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; + + if (instance && !unit_instance_is_valid(instance)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + if (!instance) + s = strappend(prefix, suffix); + else + s = strjoin(prefix, "@", instance, suffix, NULL); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +static char *do_escape_char(char c, char *t) { + assert(t); + + *(t++) = '\\'; + *(t++) = 'x'; + *(t++) = hexchar(c >> 4); + *(t++) = hexchar(c); + + return t; +} + +static char *do_escape(const char *f, char *t) { + assert(f); + assert(t); + + /* do not create units with a leading '.', like for "/.dotdir" mount points */ + if (*f == '.') { + t = do_escape_char(*f, t); + f++; + } + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + return t; +} + +char *unit_name_escape(const char *f) { + char *r, *t; + + assert(f); + + r = new(char, strlen(f)*4+1); + if (!r) + return NULL; + + t = do_escape(f, r); + *t = 0; + + return r; +} + +int unit_name_unescape(const char *f, char **ret) { + _cleanup_free_ char *r = NULL; + char *t; + + assert(f); + + r = strdup(f); + if (!r) + return -ENOMEM; + + for (t = r; *f; f++) { + if (*f == '-') + *(t++) = '/'; + else if (*f == '\\') { + int a, b; + + if (f[1] != 'x') + return -EINVAL; + + a = unhexchar(f[2]); + if (a < 0) + return -EINVAL; + + b = unhexchar(f[3]); + if (b < 0) + return -EINVAL; + + *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b); + f += 3; + } else + *(t++) = *f; + } + + *t = 0; + + *ret = r; + r = NULL; + + return 0; +} + +int unit_name_path_escape(const char *f, char **ret) { + char *p, *s; + + assert(f); + assert(ret); + + p = strdupa(f); + if (!p) + return -ENOMEM; + + path_kill_slashes(p); + + if (STR_IN_SET(p, "/", "")) + s = strdup("-"); + else { + char *e; + + if (!path_is_safe(p)) + return -EINVAL; + + /* Truncate trailing slashes */ + e = endswith(p, "/"); + if (e) + *e = 0; + + /* Truncate leading slashes */ + if (p[0] == '/') + p++; + + s = unit_name_escape(p); + } + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int unit_name_path_unescape(const char *f, char **ret) { + char *s; + int r; + + assert(f); + + if (isempty(f)) + return -EINVAL; + + if (streq(f, "-")) { + s = strdup("/"); + if (!s) + return -ENOMEM; + } else { + char *w; + + r = unit_name_unescape(f, &w); + if (r < 0) + return r; + + /* Don't accept trailing or leading slashes */ + if (startswith(w, "/") || endswith(w, "/")) { + free(w); + return -EINVAL; + } + + /* Prefix a slash again */ + s = strappend("/", w); + free(w); + if (!s) + return -ENOMEM; + + if (!path_is_safe(s)) { + free(s); + return -EINVAL; + } + } + + if (ret) + *ret = s; + else + free(s); + + return 0; +} + +int unit_name_replace_instance(const char *f, const char *i, char **ret) { + const char *p, *e; + char *s; + size_t a, b; + + assert(f); + assert(i); + assert(ret); + + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + if (!unit_instance_is_valid(i)) + return -EINVAL; + + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); + + a = p - f; + b = strlen(i); + + s = new(char, a + 1 + b + strlen(e) + 1); + if (!s) + return -ENOMEM; + + strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); + + *ret = s; + return 0; +} + +int unit_name_template(const char *f, char **ret) { + const char *p, *e; + char *s; + size_t a; + + assert(f); + assert(ret); + + if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + + assert_se(p = strchr(f, '@')); + assert_se(e = strrchr(f, '.')); + + a = p - f; + + s = new(char, a + 1 + strlen(e) + 1); + if (!s) + return -ENOMEM; + + strcpy(mempcpy(s, f, a + 1), e); + + *ret = s; + return 0; +} + +int unit_name_from_path(const char *path, const char *suffix, char **ret) { + _cleanup_free_ char *p = NULL; + char *s = NULL; + int r; + + assert(path); + assert(suffix); + assert(ret); + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strappend(p, suffix); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { + _cleanup_free_ char *p = NULL; + char *s; + int r; + + assert(prefix); + assert(path); + assert(suffix); + assert(ret); + + if (!unit_prefix_is_valid(prefix)) + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + r = unit_name_path_escape(path, &p); + if (r < 0) + return r; + + s = strjoin(prefix, "@", p, suffix, NULL); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int unit_name_to_path(const char *name, char **ret) { + _cleanup_free_ char *prefix = NULL; + int r; + + assert(name); + + r = unit_name_to_prefix(name, &prefix); + if (r < 0) + return r; + + return unit_name_path_unescape(prefix, ret); +} + +char *unit_dbus_path_from_name(const char *name) { + _cleanup_free_ char *e = NULL; + + assert(name); + + e = bus_label_escape(name); + if (!e) + return NULL; + + return strappend("/org/freedesktop/systemd1/unit/", e); +} + +int unit_name_from_dbus_path(const char *path, char **name) { + const char *e; + char *n; + + e = startswith(path, "/org/freedesktop/systemd1/unit/"); + if (!e) + return -EINVAL; + + n = bus_label_unescape(e); + if (!n) + return -ENOMEM; + + *name = n; + return 0; +} + +const char* unit_dbus_interface_from_type(UnitType t) { + + static const char *const table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", + [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", + [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", + [UNIT_TARGET] = "org.freedesktop.systemd1.Target", + [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", + [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", + [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", + [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", + [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", + [UNIT_PATH] = "org.freedesktop.systemd1.Path", + [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", + [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", + }; + + if (t < 0) + return NULL; + if (t >= _UNIT_TYPE_MAX) + return NULL; + + return table[t]; +} + +const char *unit_dbus_interface_from_name(const char *name) { + UnitType t; + + t = unit_name_to_type(name); + if (t < 0) + return NULL; + + return unit_dbus_interface_from_type(t); +} + +static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { + const char *valid_chars; + + assert(f); + assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB)); + assert(t); + + /* We'll only escape the obvious characters here, to play + * safe. */ + + valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT; + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (!strchr(valid_chars, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + return t; +} + +/** + * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, + * /blah/blah is converted to blah-blah.mount, anything else is left alone, + * except that @suffix is appended if a valid unit suffix is not present. + * + * 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; + int r; + + assert(name); + assert(suffix); + assert(ret); + + if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ + return -EINVAL; + + if (!unit_suffix_is_valid(suffix)) + return -EINVAL; + + /* Already a fully valid unit name? If so, no mangling is necessary... */ + if (unit_name_is_valid(name, UNIT_NAME_ANY)) + goto good; + + /* Already a fully valid globbing expression? If so, no mangling is necessary either... */ + if (allow_globs == UNIT_NAME_GLOB && + string_is_glob(name) && + in_charset(name, VALID_CHARS_GLOB)) + goto good; + + if (is_device_path(name)) { + r = unit_name_from_path(name, ".device", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } + + if (path_is_absolute(name)) { + r = unit_name_from_path(name, ".mount", ret); + if (r >= 0) + return 1; + if (r != -EINVAL) + return r; + } + + s = new(char, strlen(name) * 4 + strlen(suffix) + 1); + if (!s) + return -ENOMEM; + + t = do_escape_mangle(name, allow_globs, s); + *t = 0; + + /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a + * valid glob. */ + if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0) + strcpy(t, suffix); + + *ret = s; + return 1; + +good: + s = strdup(name); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int slice_build_parent_slice(const char *slice, char **ret) { + char *s, *dash; + int r; + + assert(slice); + assert(ret); + + if (!slice_name_is_valid(slice)) + return -EINVAL; + + if (streq(slice, "-.slice")) { + *ret = NULL; + return 0; + } + + s = strdup(slice); + if (!s) + return -ENOMEM; + + dash = strrchr(s, '-'); + if (dash) + strcpy(dash, ".slice"); + else { + r = free_and_strdup(&s, "-.slice"); + if (r < 0) { + free(s); + return r; + } + } + + *ret = s; + return 1; +} + +int slice_build_subslice(const char *slice, const char*name, char **ret) { + char *subslice; + + assert(slice); + assert(name); + assert(ret); + + if (!slice_name_is_valid(slice)) + return -EINVAL; + + if (!unit_prefix_is_valid(name)) + return -EINVAL; + + if (streq(slice, "-.slice")) + subslice = strappend(name, ".slice"); + else { + char *e; + + assert_se(e = endswith(slice, ".slice")); + + subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); + if (!subslice) + return -ENOMEM; + + stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice"); + } + + *ret = subslice; + return 0; +} + +bool slice_name_is_valid(const char *name) { + const char *p, *e; + bool dash = false; + + if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) + return false; + + if (streq(name, "-.slice")) + return true; + + e = endswith(name, ".slice"); + if (!e) + return false; + + for (p = name; p < e; p++) { + + if (*p == '-') { + + /* Don't allow initial dash */ + if (p == name) + return false; + + /* Don't allow multiple dashes */ + if (dash) + return false; + + dash = true; + } else + dash = false; + } + + /* Don't allow trailing hash */ + if (dash) + return false; + + return true; +} + +static const char* const unit_type_table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "service", + [UNIT_SOCKET] = "socket", + [UNIT_BUSNAME] = "busname", + [UNIT_TARGET] = "target", + [UNIT_DEVICE] = "device", + [UNIT_MOUNT] = "mount", + [UNIT_AUTOMOUNT] = "automount", + [UNIT_SWAP] = "swap", + [UNIT_TIMER] = "timer", + [UNIT_PATH] = "path", + [UNIT_SLICE] = "slice", + [UNIT_SCOPE] = "scope", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); + +static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { + [UNIT_STUB] = "stub", + [UNIT_LOADED] = "loaded", + [UNIT_NOT_FOUND] = "not-found", + [UNIT_ERROR] = "error", + [UNIT_MERGED] = "merged", + [UNIT_MASKED] = "masked" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); + +static const char* const unit_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_REQUISITE] = "Requisite", + [UNIT_WANTS] = "Wants", + [UNIT_BINDS_TO] = "BindsTo", + [UNIT_PART_OF] = "PartOf", + [UNIT_REQUIRED_BY] = "RequiredBy", + [UNIT_REQUISITE_OF] = "RequisiteOf", + [UNIT_WANTED_BY] = "WantedBy", + [UNIT_BOUND_BY] = "BoundBy", + [UNIT_CONSISTS_OF] = "ConsistsOf", + [UNIT_CONFLICTS] = "Conflicts", + [UNIT_CONFLICTED_BY] = "ConflictedBy", + [UNIT_BEFORE] = "Before", + [UNIT_AFTER] = "After", + [UNIT_ON_FAILURE] = "OnFailure", + [UNIT_TRIGGERS] = "Triggers", + [UNIT_TRIGGERED_BY] = "TriggeredBy", + [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", + [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", + [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", + [UNIT_REFERENCES] = "References", + [UNIT_REFERENCED_BY] = "ReferencedBy", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/src/libbasic/src/user-util.c b/src/libbasic/src/user-util.c new file mode 100644 index 0000000000..692f2a3db3 --- /dev/null +++ b/src/libbasic/src/user-util.c @@ -0,0 +1,481 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/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 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/libbasic/src/utf8.c b/src/libbasic/src/utf8.c new file mode 100644 index 0000000000..b5144b7312 --- /dev/null +++ b/src/libbasic/src/utf8.c @@ -0,0 +1,409 @@ +/*** + This file is part of systemd. + + Copyright 2008-2011 Kay Sievers + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* Parts of this file are based on the GLIB utf8 validation functions. The + * original license text follows. */ + +/* gutf8.c - Operations on UTF-8 strings. + * + * Copyright (C) 1999 Tom Tromey + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/utf8.h" + +bool unichar_is_valid(char32_t ch) { + + if (ch >= 0x110000) /* End of unicode space */ + return false; + if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ + return false; + if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ + return false; + if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ + return false; + + return true; +} + +static bool unichar_is_control(char32_t ch) { + + /* + 0 to ' '-1 is the C0 range. + DEL=0x7F, and DEL+1 to 0x9F is C1 range. + '\t' is in C0 range, but more or less harmless and commonly used. + */ + + return (ch < ' ' && ch != '\t' && ch != '\n') || + (0x7F <= ch && ch <= 0x9F); +} + +/* count of characters used to encode one unicode char */ +static int utf8_encoded_expected_len(const char *str) { + unsigned char c; + + assert(str); + + c = (unsigned char) str[0]; + if (c < 0x80) + return 1; + if ((c & 0xe0) == 0xc0) + return 2; + if ((c & 0xf0) == 0xe0) + return 3; + if ((c & 0xf8) == 0xf0) + return 4; + if ((c & 0xfc) == 0xf8) + return 5; + if ((c & 0xfe) == 0xfc) + return 6; + + return 0; +} + +/* decode one unicode char */ +int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { + char32_t unichar; + int len, i; + + assert(str); + + len = utf8_encoded_expected_len(str); + + switch (len) { + case 1: + *ret_unichar = (char32_t)str[0]; + return 0; + case 2: + unichar = str[0] & 0x1f; + break; + case 3: + unichar = (char32_t)str[0] & 0x0f; + break; + case 4: + unichar = (char32_t)str[0] & 0x07; + break; + case 5: + unichar = (char32_t)str[0] & 0x03; + break; + case 6: + unichar = (char32_t)str[0] & 0x01; + break; + default: + return -EINVAL; + } + + for (i = 1; i < len; i++) { + if (((char32_t)str[i] & 0xc0) != 0x80) + return -EINVAL; + unichar <<= 6; + unichar |= (char32_t)str[i] & 0x3f; + } + + *ret_unichar = unichar; + + return 0; +} + +bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { + const char *p; + + assert(str); + + for (p = str; length;) { + int encoded_len, r; + char32_t val; + + encoded_len = utf8_encoded_valid_unichar(p); + if (encoded_len < 0 || + (size_t) encoded_len > length) + return false; + + r = utf8_encoded_to_unichar(p, &val); + if (r < 0 || + unichar_is_control(val) || + (!newline && val == '\n')) + return false; + + length -= encoded_len; + p += encoded_len; + } + + return true; +} + +const char *utf8_is_valid(const char *str) { + const uint8_t *p; + + assert(str); + + for (p = (const uint8_t*) str; *p; ) { + int len; + + len = utf8_encoded_valid_unichar((const char *)p); + if (len < 0) + return NULL; + + p += len; + } + + return str; +} + +char *utf8_escape_invalid(const char *str) { + char *p, *s; + + assert(str); + + p = s = malloc(strlen(str) * 4 + 1); + if (!p) + return NULL; + + while (*str) { + int len; + + len = utf8_encoded_valid_unichar(str); + if (len > 0) { + s = mempcpy(s, str, len); + str += len; + } else { + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); + str += 1; + } + } + + *s = '\0'; + + return p; +} + +char *utf8_escape_non_printable(const char *str) { + char *p, *s; + + assert(str); + + p = s = malloc(strlen(str) * 4 + 1); + if (!p) + return NULL; + + while (*str) { + int len; + + len = utf8_encoded_valid_unichar(str); + if (len > 0) { + if (utf8_is_printable(str, len)) { + s = mempcpy(s, str, len); + str += len; + } else { + while (len > 0) { + *(s++) = '\\'; + *(s++) = 'x'; + *(s++) = hexchar((int) *str >> 4); + *(s++) = hexchar((int) *str); + + str += 1; + len--; + } + } + } else { + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); + str += 1; + } + } + + *s = '\0'; + + return p; +} + +char *ascii_is_valid(const char *str) { + const char *p; + + assert(str); + + for (p = str; *p; p++) + if ((unsigned char) *p >= 128) + return NULL; + + return (char*) str; +} + +/** + * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 + * @out_utf8: output buffer of at least 4 bytes or NULL + * @g: UCS-4 character to encode + * + * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. + * The length of the character is returned. It is not zero-terminated! If the + * output buffer is NULL, only the length is returned. + * + * Returns: The length in bytes that the UTF-8 representation does or would + * occupy. + */ +size_t utf8_encode_unichar(char *out_utf8, char32_t g) { + + if (g < (1 << 7)) { + if (out_utf8) + out_utf8[0] = g & 0x7f; + return 1; + } else if (g < (1 << 11)) { + if (out_utf8) { + out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); + out_utf8[1] = 0x80 | (g & 0x3f); + } + return 2; + } else if (g < (1 << 16)) { + if (out_utf8) { + out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); + out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); + out_utf8[2] = 0x80 | (g & 0x3f); + } + return 3; + } else if (g < (1 << 21)) { + if (out_utf8) { + out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); + out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); + out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); + out_utf8[3] = 0x80 | (g & 0x3f); + } + return 4; + } + + return 0; +} + +char *utf16_to_utf8(const void *s, size_t length) { + const uint8_t *f; + char *r, *t; + + r = new(char, (length * 4 + 1) / 2 + 1); + if (!r) + return NULL; + + f = s; + t = r; + + while (f < (const uint8_t*) s + length) { + char16_t w1, w2; + + /* see RFC 2781 section 2.2 */ + + w1 = f[1] << 8 | f[0]; + f += 2; + + if (!utf16_is_surrogate(w1)) { + t += utf8_encode_unichar(t, w1); + + continue; + } + + if (utf16_is_trailing_surrogate(w1)) + continue; + else if (f >= (const uint8_t*) s + length) + break; + + w2 = f[1] << 8 | f[0]; + f += 2; + + if (!utf16_is_trailing_surrogate(w2)) { + f -= 2; + continue; + } + + t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); + } + + *t = 0; + return r; +} + +/* expected size used to encode one unicode char */ +static int utf8_unichar_to_encoded_len(char32_t unichar) { + + if (unichar < 0x80) + return 1; + if (unichar < 0x800) + return 2; + if (unichar < 0x10000) + return 3; + if (unichar < 0x200000) + return 4; + if (unichar < 0x4000000) + return 5; + + return 6; +} + +/* validate one encoded unicode char and return its length */ +int utf8_encoded_valid_unichar(const char *str) { + int len, i, r; + char32_t unichar; + + assert(str); + + len = utf8_encoded_expected_len(str); + if (len == 0) + return -EINVAL; + + /* ascii is valid */ + if (len == 1) + return 1; + + /* check if expected encoded chars are available */ + for (i = 0; i < len; i++) + if ((str[i] & 0x80) != 0x80) + return -EINVAL; + + r = utf8_encoded_to_unichar(str, &unichar); + if (r < 0) + return r; + + /* check if encoded length matches encoded value */ + if (utf8_unichar_to_encoded_len(unichar) != len) + return -EINVAL; + + /* check if value has valid range */ + if (!unichar_is_valid(unichar)) + return -EINVAL; + + return len; +} diff --git a/src/libbasic/src/util.c b/src/libbasic/src/util.c new file mode 100644 index 0000000000..a7831e118a --- /dev/null +++ b/src/libbasic/src/util.c @@ -0,0 +1,876 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/build.h" +#include "basic/cgroup-util.h" +#include "basic/def.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/hashmap.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/signal-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +/* Put this test here for a lack of better place */ +assert_cc(EAGAIN == EWOULDBLOCK); + +int saved_argc = 0; +char **saved_argv = NULL; +static int saved_in_initrd = -1; + +size_t page_size(void) { + static thread_local size_t pgsz = 0; + long r; + + if (_likely_(pgsz > 0)) + return pgsz; + + r = sysconf(_SC_PAGESIZE); + assert(r > 0); + + pgsz = (size_t) r; + return pgsz; +} + +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; + char **directory; + + /* We fork this all off from a child process so that we can + * somewhat cleanly make use of SIGALRM to set a time limit */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + pids = hashmap_new(NULL); + if (!pids) + return log_oom(); + + seen = set_new(&string_hash_ops); + if (!seen) + return log_oom(); + + STRV_FOREACH(directory, directories) { + _cleanup_closedir_ DIR *d; + struct dirent *de; + + d = opendir(*directory); + if (!d) { + if (errno == ENOENT) + continue; + + return log_error_errno(errno, "Failed to open directory %s: %m", *directory); + } + + FOREACH_DIRENT(de, d, break) { + _cleanup_free_ char *path = NULL; + pid_t pid; + int r; + + if (!dirent_is_file(de)) + continue; + + if (set_contains(seen, de->d_name)) { + log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); + continue; + } + + r = set_put_strdup(seen, de->d_name); + if (r < 0) + return log_oom(); + + path = strjoin(*directory, "/", de->d_name, NULL); + if (!path) + return log_oom(); + + if (null_or_empty_path(path)) { + log_debug("%s is empty (a mask).", path); + continue; + } + + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + continue; + } else if (pid == 0) { + char *_argv[2]; + + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + if (!argv) { + _argv[0] = path; + _argv[1] = NULL; + argv = _argv; + } else + argv[0] = path; + + execv(path, argv); + return log_error_errno(errno, "Failed to execute %s: %m", path); + } + + log_debug("Spawned %s as " PID_FMT ".", path, pid); + + r = hashmap_put(pids, PID_TO_PTR(pid), path); + if (r < 0) + return log_oom(); + path = NULL; + } + } + + /* Abort execution of this process after the timout. We simply + * rely on SIGALRM as default action terminating the process, + * and turn on alarm(). */ + + if (timeout != USEC_INFINITY) + alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + + while (!hashmap_isempty(pids)) { + _cleanup_free_ char *path = NULL; + pid_t pid; + + pid = PTR_TO_PID(hashmap_first_key(pids)); + assert(pid > 0); + + path = hashmap_remove(pids, PID_TO_PTR(pid)); + assert(path); + + wait_for_terminate_and_warn(path, pid, true); + } + + return 0; +} + +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { + pid_t executor_pid; + int r; + char *name; + char **dirs = (char**) directories; + + assert(!strv_isempty(dirs)); + + name = basename(dirs[0]); + assert(!isempty(name)); + + /* Executes all binaries in the directories in parallel and waits + * for them to finish. Optionally a timeout is applied. If a file + * with the same name exists in more than one directory, the + * earliest one wins. */ + + executor_pid = fork(); + if (executor_pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + return; + + } else if (executor_pid == 0) { + r = do_execute(dirs, timeout, argv); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + } + + wait_for_terminate_and_warn(name, executor_pid, true); +} + +bool plymouth_running(void) { + return access("/run/plymouth/pid", F_OK) >= 0; +} + +bool display_is_local(const char *display) { + assert(display); + + return + display[0] == ':' && + display[1] >= '0' && + display[1] <= '9'; +} + +int socket_from_display(const char *display, char **path) { + size_t k; + char *f, *c; + + assert(display); + assert(path); + + if (!display_is_local(display)) + return -EINVAL; + + k = strspn(display+1, "0123456789"); + + f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); + if (!f) + return -ENOMEM; + + c = stpcpy(f, "/tmp/.X11-unix/X"); + memcpy(c, display+1, k); + c[k] = 0; + + *path = f; + + return 0; +} + +int block_get_whole_disk(dev_t d, dev_t *ret) { + char *p, *s; + int r; + unsigned n, m; + + assert(ret); + + /* If it has a queue this is good enough for us */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = d; + return 0; + } + + /* If it is a partition find the originating device */ + if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r < 0) + return -ENOENT; + + /* Get parent dev_t */ + if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + + if (r < 0) + return r; + + r = sscanf(s, "%u:%u", &m, &n); + free(s); + + if (r != 2) + return -EINVAL; + + /* Only return this if it is really good enough for us. */ + if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + if (r >= 0) { + *ret = makedev(m, n); + return 0; + } + + return -ENOENT; +} + +bool kexec_loaded(void) { + bool loaded = false; + char *s; + + if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { + if (s[0] == '1') + loaded = true; + free(s); + } + return loaded; +} + +int prot_from_flags(int flags) { + + switch (flags & O_ACCMODE) { + + case O_RDONLY: + return PROT_READ; + + case O_WRONLY: + return PROT_WRITE; + + case O_RDWR: + return PROT_READ|PROT_WRITE; + + default: + return -EINVAL; + } +} + +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; + sigset_t ss, saved_ss; + unsigned n, i; + va_list ap; + char **l; + + assert(pid); + assert(path); + + /* Spawns a temporary TTY agent, making sure it goes away when + * we go away */ + + parent_pid = getpid(); + + /* First we temporarily block all signals, so that the new + * child has them blocked initially. This way, we can be sure + * that SIGTERMs are not lost we might send to the agent. */ + assert_se(sigfillset(&ss) >= 0); + assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); + + agent_pid = fork(); + if (agent_pid < 0) { + assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); + return -errno; + } + + if (agent_pid != 0) { + assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); + *pid = agent_pid; + return 0; + } + + /* In the child: + * + * Make sure the agent goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Make sure we actually can kill the agent, if we need to, in + * case somebody invoked us from a shell script that trapped + * SIGTERM or so... */ + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + /* Check whether our parent died before we were able + * to set the death signal and unblock the signals */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + /* Don't leak fds to the agent */ + close_all_fds(except, n_except); + + stdout_is_tty = isatty(STDOUT_FILENO); + stderr_is_tty = isatty(STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + int fd; + + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + log_error_errno(errno, "Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (fd > STDERR_FILENO) + close(fd); + } + + /* Count arguments */ + va_start(ap, path); + for (n = 0; va_arg(ap, char*); n++) + ; + va_end(ap); + + /* Allocate strv */ + l = alloca(sizeof(char *) * (n + 1)); + + /* Fill in arguments */ + va_start(ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg(ap, char*); + va_end(ap); + + execv(path, l); + _exit(EXIT_FAILURE); +} + +bool in_initrd(void) { + struct statfs s; + + if (saved_in_initrd >= 0) + return saved_in_initrd; + + /* We make two checks here: + * + * 1. the flag file /etc/initrd-release must exist + * 2. the root file system must be a memory file system + * + * The second check is extra paranoia, since misdetecting an + * initrd can have bad bad consequences due the initrd + * emptying when transititioning to the main systemd. + */ + + saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && + statfs("/", &s) >= 0 && + is_temporary_fs(&s); + + return saved_in_initrd; +} + +void in_initrd_force(bool value) { + saved_in_initrd = value; +} + +/* 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) { + size_t l, u, idx; + const void *p; + int comparison; + + l = 0; + u = nmemb; + while (l < u) { + idx = (l + u) / 2; + p = (void *)(((const char *) base) + (idx * size)); + comparison = compar(key, p, arg); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (void *)p; + } + return NULL; +} + +int on_ac_power(void) { + bool found_offline = false, found_online = false; + _cleanup_closedir_ DIR *d = NULL; + + d = opendir("/sys/class/power_supply"); + if (!d) + return errno == ENOENT ? true : -errno; + + for (;;) { + struct dirent *de; + _cleanup_close_ int fd = -1, device = -1; + char contents[6]; + ssize_t n; + + errno = 0; + de = readdir(d); + if (!de && errno > 0) + return -errno; + + if (!de) + break; + + if (hidden_or_backup_file(de->d_name)) + continue; + + device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (device < 0) { + if (errno == ENOENT || errno == ENOTDIR) + continue; + + return -errno; + } + + fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (errno == ENOENT) + continue; + + return -errno; + } + + n = read(fd, contents, sizeof(contents)); + if (n < 0) + return -errno; + + if (n != 6 || memcmp(contents, "Mains\n", 6)) + continue; + + safe_close(fd); + fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (errno == ENOENT) + continue; + + return -errno; + } + + n = read(fd, contents, sizeof(contents)); + if (n < 0) + return -errno; + + if (n != 2 || contents[1] != '\n') + return -EIO; + + if (contents[0] == '1') { + found_online = true; + break; + } else if (contents[0] == '0') + found_offline = true; + else + return -EIO; + } + + return found_online || !found_offline; +} + +int container_get_leader(const char *machine, pid_t *pid) { + _cleanup_free_ char *s = NULL, *class = NULL; + const char *p; + pid_t leader; + int r; + + assert(machine); + assert(pid); + + if (!machine_name_is_valid(machine)) + return -EINVAL; + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!s) + return -EIO; + + if (!streq_ptr(class, "container")) + return -EIO; + + r = parse_pid(s, &leader); + if (r < 0) + return r; + if (leader <= 1) + return -EIO; + + *pid = leader; + return 0; +} + +int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; + int rfd = -1; + + assert(pid >= 0); + + if (mntns_fd) { + const char *mntns; + + mntns = procfs_file_alloca(pid, "ns/mnt"); + mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (mntnsfd < 0) + return -errno; + } + + if (pidns_fd) { + const char *pidns; + + pidns = procfs_file_alloca(pid, "ns/pid"); + pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (pidnsfd < 0) + return -errno; + } + + if (netns_fd) { + const char *netns; + + netns = procfs_file_alloca(pid, "ns/net"); + netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (netnsfd < 0) + return -errno; + } + + if (userns_fd) { + const char *userns; + + userns = procfs_file_alloca(pid, "ns/user"); + usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (usernsfd < 0 && errno != ENOENT) + return -errno; + } + + if (root_fd) { + const char *root; + + root = procfs_file_alloca(pid, "root"); + rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (rfd < 0) + return -errno; + } + + if (pidns_fd) + *pidns_fd = pidnsfd; + + if (mntns_fd) + *mntns_fd = mntnsfd; + + if (netns_fd) + *netns_fd = netnsfd; + + if (userns_fd) + *userns_fd = usernsfd; + + if (root_fd) + *root_fd = rfd; + + pidnsfd = mntnsfd = netnsfd = usernsfd = -1; + + return 0; +} + +int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { + if (userns_fd >= 0) { + /* Can't setns to your own userns, since then you could + * escalate from non-root to root in your own namespace, so + * check if namespaces equal before attempting to enter. */ + _cleanup_free_ char *userns_fd_path = NULL; + int r; + if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) + return -ENOMEM; + + r = files_same(userns_fd_path, "/proc/self/ns/user"); + if (r < 0) + return r; + if (r) + userns_fd = -1; + } + + if (pidns_fd >= 0) + if (setns(pidns_fd, CLONE_NEWPID) < 0) + return -errno; + + if (mntns_fd >= 0) + if (setns(mntns_fd, CLONE_NEWNS) < 0) + return -errno; + + if (netns_fd >= 0) + if (setns(netns_fd, CLONE_NEWNET) < 0) + return -errno; + + if (userns_fd >= 0) + if (setns(userns_fd, CLONE_NEWUSER) < 0) + return -errno; + + if (root_fd >= 0) { + if (fchdir(root_fd) < 0) + return -errno; + + if (chroot(".") < 0) + return -errno; + } + + return reset_uid_gid(); +} + +uint64_t physical_memory(void) { + _cleanup_free_ char *root = NULL, *value = NULL; + uint64_t mem, lim; + size_t ps; + long sc; + + /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of + * memory. + * + * In order to support containers nicely that have a configured memory limit we'll take the minimum of the + * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ + + sc = sysconf(_SC_PHYS_PAGES); + assert(sc > 0); + + ps = page_size(); + mem = (uint64_t) sc * (uint64_t) ps; + + if (cg_get_root_path(&root) < 0) + return mem; + + if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value)) + return mem; + + if (safe_atou64(value, &lim) < 0) + return mem; + + /* Make sure the limit is a multiple of our own page size */ + lim /= ps; + lim *= ps; + + return MIN(mem, lim); +} + +uint64_t physical_memory_scale(uint64_t v, uint64_t max) { + uint64_t p, m, ps, r; + + assert(max > 0); + + /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success + * the result is a multiple of the page size (rounds down). */ + + ps = page_size(); + assert(ps > 0); + + p = physical_memory() / ps; + assert(p > 0); + + m = p * v; + if (m / p != v) + return UINT64_MAX; + + m /= max; + + r = m * ps; + if (r / ps != m) + return UINT64_MAX; + + return r; +} + +uint64_t system_tasks_max(void) { + +#if SIZEOF_PID_T == 4 +#define TASKS_MAX ((uint64_t) (INT32_MAX-1)) +#elif SIZEOF_PID_T == 2 +#define TASKS_MAX ((uint64_t) (INT16_MAX-1)) +#else +#error "Unknown pid_t size" +#endif + + _cleanup_free_ char *value = NULL, *root = NULL; + uint64_t a = TASKS_MAX, b = TASKS_MAX; + + /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this + * limit: + * + * a) the maximum value for the pid_t type + * b) the cgroups pids_max attribute for the system + * c) the kernel's configure maximum PID value + * + * And then pick the smallest of the three */ + + if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0) + (void) safe_atou64(value, &a); + + if (cg_get_root_path(&root) >= 0) { + value = mfree(value); + + if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) + (void) safe_atou64(value, &b); + } + + return MIN3(TASKS_MAX, + a <= 0 ? TASKS_MAX : a, + b <= 0 ? TASKS_MAX : b); +} + +uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { + uint64_t t, m; + + assert(max > 0); + + /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages + * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ + + t = system_tasks_max(); + assert(t > 0); + + m = t * v; + if (m / t != v) /* overflow? */ + return UINT64_MAX; + + return m / max; +} + +int update_reboot_parameter_and_warn(const char *param) { + int r; + + if (isempty(param)) { + if (unlink("/run/systemd/reboot-param") < 0) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); + } + + return 0; + } + + RUN_WITH_UMASK(0022) { + r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_warning_errno(r, "Failed to write reboot parameter file: %m"); + } + + return 0; +} + +int version(void) { + puts(PACKAGE_STRING "\n" + SYSTEMD_FEATURES); + return 0; +} diff --git a/src/libbasic/src/verbs.c b/src/libbasic/src/verbs.c new file mode 100644 index 0000000000..526e0fc338 --- /dev/null +++ b/src/libbasic/src/verbs.c @@ -0,0 +1,101 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/verbs.h" +#include "basic/virt.h" + +int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { + const Verb *verb; + const char *name; + unsigned i; + int left; + + assert(verbs); + assert(verbs[0].dispatch); + assert(argc >= 0); + assert(argv); + assert(argc >= optind); + + left = argc - optind; + name = argv[optind]; + + for (i = 0;; i++) { + bool found; + + /* At the end of the list? */ + if (!verbs[i].dispatch) { + if (name) + log_error("Unknown operation %s.", name); + else + log_error("Requires operation parameter."); + return -EINVAL; + } + + if (name) + found = streq(name, verbs[i].verb); + else + found = !!(verbs[i].flags & VERB_DEFAULT); + + if (found) { + verb = &verbs[i]; + break; + } + } + + assert(verb); + + if (!name) + left = 1; + + if (verb->min_args != VERB_ANY && + (unsigned) left < verb->min_args) { + log_error("Too few arguments."); + return -EINVAL; + } + + if (verb->max_args != VERB_ANY && + (unsigned) left > verb->max_args) { + log_error("Too many arguments."); + return -EINVAL; + } + + if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) { + log_info("Running in chroot, ignoring request."); + return 0; + } + + if (name) + return verb->dispatch(left, argv + optind, userdata); + else { + char* fake[2] = { + (char*) verb->verb, + NULL + }; + + return verb->dispatch(1, fake, userdata); + } +} diff --git a/src/libbasic/src/virt.c b/src/libbasic/src/virt.c new file mode 100644 index 0000000000..33a74f16cf --- /dev/null +++ b/src/libbasic/src/virt.c @@ -0,0 +1,516 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/process-util.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/virt.h" + +static int detect_vm_cpuid(void) { + + /* CPUID is an x86 specific interface. */ +#if defined(__i386__) || defined(__x86_64__) + + static const struct { + const char *cpuid; + int id; + } cpuid_vendor_table[] = { + { "XenVMMXenVMM", VIRTUALIZATION_XEN }, + { "KVMKVMKVM", VIRTUALIZATION_KVM }, + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + { "VMwareVMware", VIRTUALIZATION_VMWARE }, + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, + }; + + uint32_t eax, ecx; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#define REG_b "rbx" +#endif + + /* First detect whether there is a hypervisor */ + eax = 1; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(ecx & 0x80000000U); + + if (hypervisor) { + union { + uint32_t sig32[3]; + char text[13]; + } sig = {}; + unsigned j; + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + log_debug("Virtualization found, CPUID=%s", sig.text); + + for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++) + if (streq(sig.text, cpuid_vendor_table[j].cpuid)) + return cpuid_vendor_table[j].id; + + return VIRTUALIZATION_VM_OTHER; + } +#endif + log_debug("No virtualization found in CPUID"); + + return VIRTUALIZATION_NONE; +} + +static int detect_vm_device_tree(void) { +#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) + _cleanup_free_ char *hvtype = NULL; + int r; + + r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype); + if (r == -ENOENT) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + + dir = opendir("/proc/device-tree"); + if (!dir) { + if (errno == ENOENT) { + log_debug_errno(errno, "/proc/device-tree: %m"); + return VIRTUALIZATION_NONE; + } + return -errno; + } + + FOREACH_DIRENT(dent, dir, return -errno) + if (strstr(dent->d_name, "fw-cfg")) { + log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name); + return VIRTUALIZATION_QEMU; + } + + log_debug("No virtualization found in /proc/device-tree/*"); + return VIRTUALIZATION_NONE; + } else if (r < 0) + return r; + + log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); + if (streq(hvtype, "linux,kvm")) + return VIRTUALIZATION_KVM; + else if (strstr(hvtype, "xen")) + return VIRTUALIZATION_XEN; + else + return VIRTUALIZATION_VM_OTHER; +#else + log_debug("This platform does not support /proc/device-tree"); + return VIRTUALIZATION_NONE; +#endif +} + +static int detect_vm_dmi(void) { +#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" + }; + + static const struct { + 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 }, + { "VMW", VIRTUALIZATION_VMWARE }, + { "innotek GmbH", VIRTUALIZATION_ORACLE }, + { "Xen", VIRTUALIZATION_XEN }, + { "Bochs", VIRTUALIZATION_BOCHS }, + { "Parallels", VIRTUALIZATION_PARALLELS }, + }; + unsigned i; + int r; + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + _cleanup_free_ char *s = NULL; + unsigned j; + + r = read_one_line_file(dmi_vendors[i], &s); + if (r < 0) { + if (r == -ENOENT) + continue; + + return r; + } + + + + for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++) + if (startswith(s, dmi_vendor_table[j].vendor)) { + log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]); + return dmi_vendor_table[j].id; + } + } +#endif + + log_debug("No virtualization found in DMI"); + + return VIRTUALIZATION_NONE; +} + +static int detect_vm_xen(void) { + /* Check for Dom0 will be executed later in detect_vm_xen_dom0 + Thats why we dont check the content of /proc/xen/capabilities here. */ + if (access("/proc/xen/capabilities", F_OK) < 0) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return VIRTUALIZATION_NONE; + } + + log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); + return VIRTUALIZATION_XEN; + +} + +static bool detect_vm_xen_dom0(void) { + _cleanup_free_ char *domcap = NULL; + char *cap, *i; + int r; + + r = read_one_line_file("/proc/xen/capabilities", &domcap); + if (r == -ENOENT) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return false; + } + if (r < 0) + return r; + + i = domcap; + while ((cap = strsep(&i, ","))) + if (streq(cap, "control_d")) + break; + if (!cap) { + log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); + return false; + } + + log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); + return true; +} + +static int detect_vm_hypervisor(void) { + _cleanup_free_ char *hvtype = NULL; + int r; + + r = read_one_line_file("/sys/hypervisor/type", &hvtype); + if (r == -ENOENT) + return VIRTUALIZATION_NONE; + if (r < 0) + return r; + + log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); + + if (streq(hvtype, "xen")) + return VIRTUALIZATION_XEN; + else + return VIRTUALIZATION_VM_OTHER; +} + +static int detect_vm_uml(void) { + _cleanup_free_ char *cpuinfo_contents = NULL; + int r; + + /* Detect User-Mode Linux by reading /proc/cpuinfo */ + r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); + if (r < 0) + return r; + + if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { + log_debug("UML virtualization found in /proc/cpuinfo"); + return VIRTUALIZATION_UML; + } + + log_debug("No virtualization found in /proc/cpuinfo."); + return VIRTUALIZATION_NONE; +} + +static int detect_vm_zvm(void) { + +#if defined(__s390__) + _cleanup_free_ char *t = NULL; + int r; + + r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t); + if (r == -ENOENT) + return VIRTUALIZATION_NONE; + if (r < 0) + return r; + + log_debug("Virtualization %s found in /proc/sysinfo", t); + if (streq(t, "z/VM")) + return VIRTUALIZATION_ZVM; + else + return VIRTUALIZATION_KVM; +#else + log_debug("This platform does not support /proc/sysinfo"); + return VIRTUALIZATION_NONE; +#endif +} + +/* Returns a short identifier for the various VM implementations */ +int detect_vm(void) { + static thread_local int cached_found = _VIRTUALIZATION_INVALID; + int r; + + if (cached_found >= 0) + return cached_found; + + /* 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. + * + * 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_cpuid(); + if (r < 0) + return r; + if (r != VIRTUALIZATION_NONE) + goto finish; + + /* 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) + goto finish; + + r = detect_vm_hypervisor(); + if (r < 0) + return r; + if (r != VIRTUALIZATION_NONE) + goto finish; + + r = detect_vm_device_tree(); + if (r < 0) + return r; + if (r != VIRTUALIZATION_NONE) + goto finish; + + r = detect_vm_uml(); + if (r < 0) + return r; + if (r != VIRTUALIZATION_NONE) + goto finish; + + r = detect_vm_zvm(); + if (r < 0) + return r; + +finish: + /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others. + * In order to detect the Dom0 as not virtualization we need to + * double-check it */ + if (r == VIRTUALIZATION_XEN && detect_vm_xen_dom0()) + r = VIRTUALIZATION_NONE; + + cached_found = r; + log_debug("Found VM virtualization %s", virtualization_to_string(r)); + return r; +} + +int detect_container(void) { + + static const struct { + const char *value; + int id; + } value_table[] = { + { "lxc", VIRTUALIZATION_LXC }, + { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, + { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, + { "docker", VIRTUALIZATION_DOCKER }, + { "rkt", VIRTUALIZATION_RKT }, + }; + + static thread_local int cached_found = _VIRTUALIZATION_INVALID; + _cleanup_free_ char *m = NULL; + const char *e = NULL; + unsigned j; + int r; + + if (cached_found >= 0) + return cached_found; + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + r = VIRTUALIZATION_OPENVZ; + goto finish; + } + + if (getpid() == 1) { + /* If we are PID 1 we can just check our own + * environment variable */ + + e = getenv("container"); + if (isempty(e)) { + r = VIRTUALIZATION_NONE; + goto finish; + } + } else { + + /* Otherwise, PID 1 dropped this information into a + * file in /run. This is better than accessing + * /proc/1/environ, since we don't need CAP_SYS_PTRACE + * for that. */ + + r = read_one_line_file("/run/systemd/container", &m); + if (r == -ENOENT) { + + /* Fallback for cases where PID 1 was not + * systemd (for example, cases where + * init=/bin/sh is used. */ + + r = getenv_for_pid(1, "container", &m); + if (r <= 0) { + + /* If that didn't work, give up, + * assume no container manager. + * + * Note: This means we still cannot + * detect containers if init=/bin/sh + * is passed but privileges dropped, + * as /proc/1/environ is only readable + * with privileges. */ + + r = VIRTUALIZATION_NONE; + goto finish; + } + } + if (r < 0) + return r; + + e = m; + } + + for (j = 0; j < ELEMENTSOF(value_table); j++) + if (streq(e, value_table[j].value)) { + r = value_table[j].id; + goto finish; + } + + r = VIRTUALIZATION_CONTAINER_OTHER; + +finish: + log_debug("Found container virtualization %s", virtualization_to_string(r)); + cached_found = r; + return r; +} + +int detect_virtualization(void) { + int r; + + r = detect_container(); + if (r == 0) + r = detect_vm(); + + return r; +} + +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", + [VIRTUALIZATION_QEMU] = "qemu", + [VIRTUALIZATION_BOCHS] = "bochs", + [VIRTUALIZATION_XEN] = "xen", + [VIRTUALIZATION_UML] = "uml", + [VIRTUALIZATION_VMWARE] = "vmware", + [VIRTUALIZATION_ORACLE] = "oracle", + [VIRTUALIZATION_MICROSOFT] = "microsoft", + [VIRTUALIZATION_ZVM] = "zvm", + [VIRTUALIZATION_PARALLELS] = "parallels", + [VIRTUALIZATION_VM_OTHER] = "vm-other", + + [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", + [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", + [VIRTUALIZATION_LXC] = "lxc", + [VIRTUALIZATION_OPENVZ] = "openvz", + [VIRTUALIZATION_DOCKER] = "docker", + [VIRTUALIZATION_RKT] = "rkt", + [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", +}; + +DEFINE_STRING_TABLE_LOOKUP(virtualization, int); diff --git a/src/libbasic/src/web-util.c b/src/libbasic/src/web-util.c new file mode 100644 index 0000000000..a68813ffb8 --- /dev/null +++ b/src/libbasic/src/web-util.c @@ -0,0 +1,76 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/string-util.h" +#include "basic/utf8.h" +#include "basic/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/libbasic/src/xattr-util.c b/src/libbasic/src/xattr-util.c new file mode 100644 index 0000000000..07f063e10b --- /dev/null +++ b/src/libbasic/src/xattr-util.c @@ -0,0 +1,200 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/sparse-endian.h" +#include "basic/stdio-util.h" +#include "basic/time-util.h" +#include "basic/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_CLOEXEC|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/libbasic/src/xml.c b/src/libbasic/src/xml.c new file mode 100644 index 0000000000..273fe51fa9 --- /dev/null +++ b/src/libbasic/src/xml.c @@ -0,0 +1,255 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/xml.h" + +enum { + STATE_NULL, + STATE_TEXT, + STATE_TAG, + STATE_ATTRIBUTE, +}; + +static void inc_lines(unsigned *line, const char *s, size_t n) { + const char *p = s; + + if (!line) + return; + + for (;;) { + const char *f; + + f = memchr(p, '\n', n); + if (!f) + return; + + n -= (f - p) + 1; + p = f + 1; + (*line)++; + } +} + +/* We don't actually do real XML here. We only read a simplistic + * subset, that is a bit less strict that XML and lacks all the more + * complex features, like entities, or namespaces. However, we do + * support some HTML5-like simplifications */ + +int xml_tokenize(const char **p, char **name, void **state, unsigned *line) { + const char *c, *e, *b; + char *ret; + int t; + + assert(p); + assert(*p); + assert(name); + assert(state); + + t = PTR_TO_INT(*state); + c = *p; + + if (t == STATE_NULL) { + if (line) + *line = 1; + t = STATE_TEXT; + } + + for (;;) { + if (*c == 0) + return XML_END; + + switch (t) { + + case STATE_TEXT: { + int x; + + e = strchrnul(c, '<'); + if (e > c) { + /* More text... */ + ret = strndup(c, e - c); + if (!ret) + return -ENOMEM; + + inc_lines(line, c, e - c); + + *name = ret; + *p = e; + *state = INT_TO_PTR(STATE_TEXT); + + return XML_TEXT; + } + + assert(*e == '<'); + b = c + 1; + + if (startswith(b, "!--")) { + /* A comment */ + e = strstr(b + 3, "-->"); + if (!e) + return -EINVAL; + + inc_lines(line, b, e + 3 - b); + + c = e + 3; + continue; + } + + if (*b == '?') { + /* Processing instruction */ + + e = strstr(b + 1, "?>"); + if (!e) + return -EINVAL; + + inc_lines(line, b, e + 2 - b); + + c = e + 2; + continue; + } + + if (*b == '!') { + /* DTD */ + + e = strchr(b + 1, '>'); + if (!e) + return -EINVAL; + + inc_lines(line, b, e + 1 - b); + + c = e + 1; + continue; + } + + if (*b == '/') { + /* A closing tag */ + x = XML_TAG_CLOSE; + b++; + } else + x = XML_TAG_OPEN; + + e = strpbrk(b, WHITESPACE "/>"); + if (!e) + return -EINVAL; + + ret = strndup(b, e - b); + if (!ret) + return -ENOMEM; + + *name = ret; + *p = e; + *state = INT_TO_PTR(STATE_TAG); + + return x; + } + + case STATE_TAG: + + b = c + strspn(c, WHITESPACE); + if (*b == 0) + return -EINVAL; + + inc_lines(line, c, b - c); + + e = b + strcspn(b, WHITESPACE "=/>"); + if (e > b) { + /* An attribute */ + + ret = strndup(b, e - b); + if (!ret) + return -ENOMEM; + + *name = ret; + *p = e; + *state = INT_TO_PTR(STATE_ATTRIBUTE); + + return XML_ATTRIBUTE_NAME; + } + + if (startswith(b, "/>")) { + /* An empty tag */ + + *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */ + *p = b + 2; + *state = INT_TO_PTR(STATE_TEXT); + + return XML_TAG_CLOSE_EMPTY; + } + + if (*b != '>') + return -EINVAL; + + c = b + 1; + t = STATE_TEXT; + continue; + + case STATE_ATTRIBUTE: + + if (*c == '=') { + c++; + + if (*c == '\'' || *c == '\"') { + /* Tag with a quoted value */ + + e = strchr(c+1, *c); + if (!e) + return -EINVAL; + + inc_lines(line, c, e - c); + + ret = strndup(c+1, e - c - 1); + if (!ret) + return -ENOMEM; + + *name = ret; + *p = e + 1; + *state = INT_TO_PTR(STATE_TAG); + + return XML_ATTRIBUTE_VALUE; + + } + + /* Tag with a value without quotes */ + + b = strpbrk(c, WHITESPACE ">"); + if (!b) + b = c; + + ret = strndup(c, b - c); + if (!ret) + return -ENOMEM; + + *name = ret; + *p = b; + *state = INT_TO_PTR(STATE_TAG); + return XML_ATTRIBUTE_VALUE; + } + + t = STATE_TAG; + continue; + } + + } + + assert_not_reached("Bad state"); +} diff --git a/src/libfirewall/Makefile b/src/libfirewall/Makefile new file mode 100644 index 0000000000..6016feec17 --- /dev/null +++ b/src/libfirewall/Makefile @@ -0,0 +1,41 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_LIBIPTC),) +noinst_LTLIBRARIES += \ + libfirewall.la + +libfirewall_la_SOURCES = \ + src/shared/firewall-util.h \ + src/shared/firewall-util.c + +libfirewall_la_CFLAGS = \ + $(LIBIPTC_CFLAGS) + +libfirewall_la_LIBADD = \ + $(LIBIPTC_LIBS) +endif # HAVE_LIBIPTC + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libfirewall/firewall-util.c b/src/libfirewall/firewall-util.c new file mode 100644 index 0000000000..009cef3e88 --- /dev/null +++ b/src/libfirewall/firewall-util.c @@ -0,0 +1,359 @@ +/*** + 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 . +***/ + +#warning "Temporary work-around for broken glibc vs. linux kernel header definitions" +#warning "This really should be removed sooner rather than later, when this is fixed upstream" +#define _NET_IF_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#include + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/socket-util.h" + +#include "firewall-util.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); + +static int entry_fill_basics( + struct ipt_entry *entry, + int protocol, + const char *in_interface, + const union in_addr_union *source, + unsigned source_prefixlen, + const char *out_interface, + const union in_addr_union *destination, + unsigned destination_prefixlen) { + + assert(entry); + + if (out_interface && !ifname_valid(out_interface)) + return -EINVAL; + if (in_interface && !ifname_valid(in_interface)) + return -EINVAL; + + entry->ip.proto = protocol; + + if (in_interface) { + strcpy(entry->ip.iniface, in_interface); + memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1); + } + if (source) { + entry->ip.src = source->in; + in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen); + } + + if (out_interface) { + strcpy(entry->ip.outiface, out_interface); + memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1); + } + if (destination) { + entry->ip.dst = destination->in; + in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen); + } + + return 0; +} + +int fw_add_masquerade( + bool add, + int af, + int protocol, + const union in_addr_union *source, + unsigned source_prefixlen, + const char *out_interface, + const union in_addr_union *destination, + unsigned destination_prefixlen) { + + _cleanup_(iptc_freep) struct xtc_handle *h = NULL; + struct ipt_entry *entry, *mask; + struct ipt_entry_target *t; + size_t sz; + struct nf_nat_ipv4_multi_range_compat *mr; + int r; + + if (af != AF_INET) + return -EOPNOTSUPP; + + if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) + return -EOPNOTSUPP; + + h = iptc_init("nat"); + if (!h) + return -errno; + + sz = XT_ALIGN(sizeof(struct ipt_entry)) + + XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); + + /* Put together the entry we want to add or remove */ + entry = alloca0(sz); + entry->next_offset = sz; + entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry)); + r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen); + if (r < 0) + return r; + + /* Fill in target part */ + t = ipt_get_target(entry); + t->u.target_size = + XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); + strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name)); + mr = (struct nf_nat_ipv4_multi_range_compat*) t->data; + mr->rangesize = 1; + + /* Create a search mask entry */ + mask = alloca(sz); + memset(mask, 0xFF, sz); + + if (add) { + if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h)) + return 0; + if (errno != ENOENT) /* if other error than not existing yet, fail */ + return -errno; + + if (!iptc_insert_entry("POSTROUTING", entry, 0, h)) + return -errno; + } else { + if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) { + if (errno == ENOENT) /* if it's already gone, all is good! */ + return 0; + + return -errno; + } + } + + if (!iptc_commit(h)) + return -errno; + + return 0; +} + +int fw_add_local_dnat( + bool add, + int af, + int protocol, + const char *in_interface, + const union in_addr_union *source, + unsigned source_prefixlen, + const union in_addr_union *destination, + unsigned destination_prefixlen, + uint16_t local_port, + const union in_addr_union *remote, + uint16_t remote_port, + const union in_addr_union *previous_remote) { + + + _cleanup_(iptc_freep) struct xtc_handle *h = NULL; + struct ipt_entry *entry, *mask; + struct ipt_entry_target *t; + struct ipt_entry_match *m; + struct xt_addrtype_info_v1 *at; + struct nf_nat_ipv4_multi_range_compat *mr; + size_t sz, msz; + int r; + + assert(add || !previous_remote); + + if (af != AF_INET) + return -EOPNOTSUPP; + + if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) + return -EOPNOTSUPP; + + if (local_port <= 0) + return -EINVAL; + + if (remote_port <= 0) + return -EINVAL; + + h = iptc_init("nat"); + if (!h) + return -errno; + + sz = XT_ALIGN(sizeof(struct ipt_entry)) + + XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) + + XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); + + if (protocol == IPPROTO_TCP) + msz = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_tcp)); + else + msz = XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_udp)); + + sz += msz; + + /* Fill in basic part */ + entry = alloca0(sz); + entry->next_offset = sz; + entry->target_offset = + XT_ALIGN(sizeof(struct ipt_entry)) + + XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) + + msz; + r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen); + if (r < 0) + return r; + + /* Fill in first match */ + m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry))); + m->u.match_size = msz; + if (protocol == IPPROTO_TCP) { + struct xt_tcp *tcp; + + strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name)); + tcp = (struct xt_tcp*) m->data; + tcp->dpts[0] = tcp->dpts[1] = local_port; + tcp->spts[0] = 0; + tcp->spts[1] = 0xFFFF; + + } else { + struct xt_udp *udp; + + strncpy(m->u.user.name, "udp", sizeof(m->u.user.name)); + udp = (struct xt_udp*) m->data; + udp->dpts[0] = udp->dpts[1] = local_port; + udp->spts[0] = 0; + udp->spts[1] = 0xFFFF; + } + + /* Fill in second match */ + m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz); + m->u.match_size = + XT_ALIGN(sizeof(struct ipt_entry_match)) + + XT_ALIGN(sizeof(struct xt_addrtype_info_v1)); + strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name)); + m->u.user.revision = 1; + at = (struct xt_addrtype_info_v1*) m->data; + at->dest = XT_ADDRTYPE_LOCAL; + + /* Fill in target part */ + t = ipt_get_target(entry); + t->u.target_size = + XT_ALIGN(sizeof(struct ipt_entry_target)) + + XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); + strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name)); + mr = (struct nf_nat_ipv4_multi_range_compat*) t->data; + mr->rangesize = 1; + mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS; + mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr; + if (protocol == IPPROTO_TCP) + mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port); + else + mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port); + + mask = alloca0(sz); + memset(mask, 0xFF, sz); + + if (add) { + /* Add the PREROUTING rule, if it is missing so far */ + if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -EINVAL; + + if (!iptc_insert_entry("PREROUTING", entry, 0, h)) + return -errno; + } + + /* If a previous remote is set, remove its entry */ + if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { + mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; + + if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -errno; + } + + mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr; + } + + /* Add the OUTPUT rule, if it is missing so far */ + if (!in_interface) { + + /* Don't apply onto loopback addresses */ + if (!destination) { + entry->ip.dst.s_addr = htobe32(0x7F000000); + entry->ip.dmsk.s_addr = htobe32(0xFF000000); + entry->ip.invflags = IPT_INV_DSTIP; + } + + if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -errno; + + if (!iptc_insert_entry("OUTPUT", entry, 0, h)) + return -errno; + } + + /* If a previous remote is set, remove its entry */ + if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { + mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; + + if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -errno; + } + } + } + } else { + if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -errno; + } + + if (!in_interface) { + if (!destination) { + entry->ip.dst.s_addr = htobe32(0x7F000000); + entry->ip.dmsk.s_addr = htobe32(0xFF000000); + entry->ip.invflags = IPT_INV_DSTIP; + } + + if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) { + if (errno != ENOENT) + return -errno; + } + } + } + + if (!iptc_commit(h)) + return -errno; + + return 0; +} diff --git a/src/libfirewall/firewall-util.h b/src/libfirewall/firewall-util.h new file mode 100644 index 0000000000..1e54dc0ceb --- /dev/null +++ b/src/libfirewall/firewall-util.h @@ -0,0 +1,83 @@ +#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 . +***/ + +#include +#include + +#include "basic/in-addr-util.h" + +#ifdef HAVE_LIBIPTC + +int fw_add_masquerade( + bool add, + int af, + int protocol, + const union in_addr_union *source, + unsigned source_prefixlen, + const char *out_interface, + const union in_addr_union *destination, + unsigned destination_prefixlen); + +int fw_add_local_dnat( + bool add, + int af, + int protocol, + const char *in_interface, + const union in_addr_union *source, + unsigned source_prefixlen, + const union in_addr_union *destination, + unsigned destination_prefixlen, + uint16_t local_port, + const union in_addr_union *remote, + uint16_t remote_port, + const union in_addr_union *previous_remote); + +#else + +static inline int fw_add_masquerade( + bool add, + int af, + int protocol, + const union in_addr_union *source, + unsigned source_prefixlen, + const char *out_interface, + const union in_addr_union *destination, + unsigned destination_prefixlen) { + return -EOPNOTSUPP; +} + +static inline int fw_add_local_dnat( + bool add, + int af, + int protocol, + const char *in_interface, + const union in_addr_union *source, + unsigned source_prefixlen, + const union in_addr_union *destination, + unsigned destination_prefixlen, + uint16_t local_port, + const union in_addr_union *remote, + uint16_t remote_port, + const union in_addr_union *previous_remote) { + return -EOPNOTSUPP; +} + +#endif diff --git a/src/libshared/Makefile b/src/libshared/Makefile new file mode 100644 index 0000000000..369b265ff7 --- /dev/null +++ b/src/libshared/Makefile @@ -0,0 +1,28 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += src + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libshared/include/shared/acl-util.h b/src/libshared/include/shared/acl-util.h new file mode 100644 index 0000000000..af5bcb5b4f --- /dev/null +++ b/src/libshared/include/shared/acl-util.h @@ -0,0 +1,48 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_ACL + +#include +#include +#include + +#include "basic/macro.h" + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry); +int calc_acl_mask_if_needed(acl_t *acl_p); +int add_base_acls_if_needed(acl_t *acl_p, const char *path); +int 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. */ +DEFINE_TRIVIAL_CLEANUP_FUNC(acl_t, acl_free); +#define acl_free_charp acl_free +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, acl_free_charp); +#define acl_free_uid_tp acl_free +DEFINE_TRIVIAL_CLEANUP_FUNC(uid_t*, acl_free_uid_tp); +#define acl_free_gid_tp acl_free +DEFINE_TRIVIAL_CLEANUP_FUNC(gid_t*, acl_free_gid_tp); + +#endif diff --git a/src/libshared/include/shared/acpi-fpdt.h b/src/libshared/include/shared/acpi-fpdt.h new file mode 100644 index 0000000000..3da32c08ad --- /dev/null +++ b/src/libshared/include/shared/acpi-fpdt.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/time-util.h" + +int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit); diff --git a/src/libshared/include/shared/apparmor-util.h b/src/libshared/include/shared/apparmor-util.h new file mode 100644 index 0000000000..524f740152 --- /dev/null +++ b/src/libshared/include/shared/apparmor-util.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +bool mac_apparmor_use(void); diff --git a/src/libshared/include/shared/ask-password-api.h b/src/libshared/include/shared/ask-password-api.h new file mode 100644 index 0000000000..770ab1f5c0 --- /dev/null +++ b/src/libshared/include/shared/ask-password-api.h @@ -0,0 +1,38 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/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/libshared/include/shared/base-filesystem.h b/src/libshared/include/shared/base-filesystem.h new file mode 100644 index 0000000000..49599f0a60 --- /dev/null +++ b/src/libshared/include/shared/base-filesystem.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int base_filesystem_create(const char *root, uid_t uid, gid_t gid); diff --git a/src/libshared/include/shared/boot-timestamps.h b/src/libshared/include/shared/boot-timestamps.h new file mode 100644 index 0000000000..00ea7e3009 --- /dev/null +++ b/src/libshared/include/shared/boot-timestamps.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/time-util.h" + +int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader); diff --git a/src/libshared/include/shared/bus-unit-util.h b/src/libshared/include/shared/bus-unit-util.h new file mode 100644 index 0000000000..b4b68cc5d2 --- /dev/null +++ b/src/libshared/include/shared/bus-unit-util.h @@ -0,0 +1,57 @@ +#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 . +***/ + +#include + +#include "install.h" +#include "output-mode.h" + +typedef struct UnitInfo { + const char *machine; + const char *id; + const char *description; + const char *load_state; + const char *active_state; + const char *sub_state; + const char *following; + const char *unit_path; + uint32_t job_id; + const char *job_type; + const char *job_path; +} UnitInfo; + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); + +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, const char* const* 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); + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); + +int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/libshared/include/shared/bus-util.h b/src/libshared/include/shared/bus-util.h new file mode 100644 index 0000000000..71237e9fc1 --- /dev/null +++ b/src/libshared/include/shared/bus-util.h @@ -0,0 +1,160 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/string-util.h" + +typedef enum BusTransport { + BUS_TRANSPORT_LOCAL, + BUS_TRANSPORT_REMOTE, + BUS_TRANSPORT_MACHINE, + _BUS_TRANSPORT_MAX, + _BUS_TRANSPORT_INVALID = -1 +} BusTransport; + +typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); + +struct bus_properties_map { + const char *member; + const char *signature; + bus_property_set_t set; + size_t offset; +}; + +int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); + +int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); +int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); +int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata); + +int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name); + +typedef bool (*check_idle_t)(void *userdata); + +int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata); + +int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error); + +int bus_check_peercred(sd_bus *c); + +int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e); + +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_connect_system_systemd(sd_bus **_bus); +int bus_connect_user_systemd(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 value, bool all); +int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all); + +int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); + +#define bus_property_get_usec ((sd_bus_property_get_t) NULL) +#define bus_property_set_usec ((sd_bus_property_set_t) NULL) + +assert_cc(sizeof(int) == sizeof(int32_t)); +#define bus_property_get_int ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(unsigned) == sizeof(unsigned)); +#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL) + +/* On 64bit machines we can use the default serializer for size_t and + * friends, otherwise we need to cast this manually */ +#if __SIZEOF_SIZE_T__ == 8 +#define bus_property_get_size ((sd_bus_property_get_t) NULL) +#else +int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +#endif + +#if __SIZEOF_LONG__ == 8 +#define bus_property_get_long ((sd_bus_property_get_t) NULL) +#define bus_property_get_ulong ((sd_bus_property_get_t) NULL) +#else +int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); +#endif + +/* uid_t and friends on Linux 32 bit. This means we can just use the + * default serializer for 32bit unsigned, for serializing it, and map + * it to NULL here */ +assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +#define bus_property_get_uid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(gid_t) == sizeof(uint32_t)); +#define bus_property_get_gid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(pid_t) == sizeof(uint32_t)); +#define bus_property_get_pid ((sd_bus_property_get_t) NULL) + +assert_cc(sizeof(mode_t) == sizeof(uint32_t)); +#define bus_property_get_mode ((sd_bus_property_get_t) NULL) + +int bus_log_parse_error(int r); +int bus_log_create_error(int r); + +#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ + int function(sd_bus *bus, \ + const char *path, \ + const char *interface, \ + const char *property, \ + sd_bus_message *reply, \ + void *userdata, \ + sd_bus_error *error) { \ + \ + const char *value; \ + type *field = userdata; \ + int r; \ + \ + assert(bus); \ + assert(reply); \ + assert(field); \ + \ + value = strempty(name##_to_string(*field)); \ + \ + r = sd_bus_message_append_basic(reply, 's', value); \ + if (r < 0) \ + return r; \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \ + SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ + SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) + +int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); +int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); + +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/libshared/include/shared/cgroup-show.h b/src/libshared/include/shared/cgroup-show.h new file mode 100644 index 0000000000..5c1d6e6d98 --- /dev/null +++ b/src/libshared/include/shared/cgroup-show.h @@ -0,0 +1,32 @@ +#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 . +***/ + +#include +#include + +#include "logs-show.h" +#include "output-mode.h" + +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags); + +int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); diff --git a/src/libshared/include/shared/clean-ipc.h b/src/libshared/include/shared/clean-ipc.h new file mode 100644 index 0000000000..44a83afcf7 --- /dev/null +++ b/src/libshared/include/shared/clean-ipc.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int clean_ipc(uid_t uid); diff --git a/src/libshared/include/shared/condition.h b/src/libshared/include/shared/condition.h new file mode 100644 index 0000000000..fd798460d1 --- /dev/null +++ b/src/libshared/include/shared/condition.h @@ -0,0 +1,94 @@ +#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 . +***/ + +#include +#include + +#include "basic/list.h" +#include "basic/macro.h" + +typedef enum ConditionType { + CONDITION_ARCHITECTURE, + CONDITION_VIRTUALIZATION, + CONDITION_HOST, + CONDITION_KERNEL_COMMAND_LINE, + CONDITION_SECURITY, + CONDITION_CAPABILITY, + CONDITION_AC_POWER, + + CONDITION_NEEDS_UPDATE, + CONDITION_FIRST_BOOT, + + CONDITION_PATH_EXISTS, + CONDITION_PATH_EXISTS_GLOB, + CONDITION_PATH_IS_DIRECTORY, + CONDITION_PATH_IS_SYMBOLIC_LINK, + CONDITION_PATH_IS_MOUNT_POINT, + CONDITION_PATH_IS_READ_WRITE, + CONDITION_DIRECTORY_NOT_EMPTY, + CONDITION_FILE_NOT_EMPTY, + CONDITION_FILE_IS_EXECUTABLE, + + CONDITION_NULL, + + _CONDITION_TYPE_MAX, + _CONDITION_TYPE_INVALID = -1 +} ConditionType; + +typedef enum ConditionResult { + CONDITION_UNTESTED, + CONDITION_SUCCEEDED, + CONDITION_FAILED, + CONDITION_ERROR, + _CONDITION_RESULT_MAX, + _CONDITION_RESULT_INVALID = -1 +} ConditionResult; + +typedef struct Condition { + ConditionType type:8; + + bool trigger:1; + bool negate:1; + + ConditionResult result:6; + + char *parameter; + + LIST_FIELDS(struct Condition, conditions); +} Condition; + +Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate); +void condition_free(Condition *c); +Condition* condition_free_list(Condition *c); + +int condition_test(Condition *c); + +void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); +void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); + +const char* condition_type_to_string(ConditionType t) _const_; +ConditionType condition_type_from_string(const char *s) _pure_; + +const char* assert_type_to_string(ConditionType t) _const_; +ConditionType assert_type_from_string(const char *s) _pure_; + +const char* condition_result_to_string(ConditionResult r) _const_; +ConditionResult condition_result_from_string(const char *s) _pure_; diff --git a/src/libshared/include/shared/conf-parser.h b/src/libshared/include/shared/conf-parser.h new file mode 100644 index 0000000000..f7423d45e7 --- /dev/null +++ b/src/libshared/include/shared/conf-parser.h @@ -0,0 +1,229 @@ +#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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" + +/* An abstract parser for simple, line based, shallow configuration + * files consisting of variable assignments only. */ + +/* Prototype for a parser for a specific configuration setting */ +typedef int (*ConfigParserCallback)(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + +/* Wraps information for parsing a specific configuration variable, to + * be stored in a simple array */ +typedef struct ConfigTableItem { + const char *section; /* Section */ + const char *lvalue; /* Name of the variable */ + ConfigParserCallback parse; /* Function that is called to parse the variable's value */ + int ltype; /* Distinguish different variables passed to the same callback */ + void *data; /* Where to store the variable's data */ +} ConfigTableItem; + +/* Wraps information for parsing a specific configuration variable, to + * be stored in a gperf perfect hashtable */ +typedef struct ConfigPerfItem { + const char *section_and_lvalue; /* Section + "." + name of the variable */ + ConfigParserCallback parse; /* Function that is called to parse the variable's value */ + int ltype; /* Distinguish different variables passed to the same callback */ + size_t offset; /* Offset where to store data, from the beginning of userdata */ +} ConfigPerfItem; + +/* Prototype for a low-level gperf lookup function */ +typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length); + +/* Prototype for a generic high-level lookup function */ +typedef int (*ConfigItemLookup)( + const void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata); + +/* Linear table search implementation of ConfigItemLookup, based on + * ConfigTableItem arrays */ +int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); + +/* gperf implementation of ConfigItemLookup, based on gperf + * ConfigPerfItem tables */ +int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); + +int config_parse(const char *unit, + const char *filename, + FILE *f, + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + bool allow_include, + bool warn, + void *userdata); + +int config_parse_many(const char *conf_file, /* possibly NULL */ + const char *conf_file_dirs, /* nulstr */ + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata); + +/* Generic parsers */ +int config_parse_int(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_unsigned(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_long(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_uint32(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_uint64(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_double(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_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_iec_uint64(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_bool(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_tristate(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_string(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_path(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_strv(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(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_nsec(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_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_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int 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); +int config_parse_ifname(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 DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ + int function(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) { \ + \ + type *i = data, x; \ + \ + assert(filename); \ + assert(lvalue); \ + assert(rvalue); \ + assert(data); \ + \ + if ((x = name##_from_string(rvalue)) < 0) { \ + log_syntax(unit, LOG_ERR, filename, line, -x, \ + msg ", ignoring: %s", rvalue); \ + return 0; \ + } \ + \ + *i = x; \ + return 0; \ + } + +#define DEFINE_CONFIG_PARSE_ENUMV(function,name,type,invalid,msg) \ + int function(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) { \ + \ + type **enums = data, x, *ys; \ + _cleanup_free_ type *xs = NULL; \ + const char *word, *state; \ + size_t l, i = 0; \ + \ + assert(filename); \ + assert(lvalue); \ + assert(rvalue); \ + assert(data); \ + \ + xs = new0(type, 1); \ + if (!xs) \ + return -ENOMEM; \ + \ + *xs = invalid; \ + \ + FOREACH_WORD(word, l, rvalue, state) { \ + _cleanup_free_ char *en = NULL; \ + type *new_xs; \ + \ + en = strndup(word, l); \ + if (!en) \ + return -ENOMEM; \ + \ + if ((x = name##_from_string(en)) < 0) { \ + log_syntax(unit, LOG_ERR, filename, line, \ + -x, msg ", ignoring: %s", en); \ + continue; \ + } \ + \ + for (ys = xs; x != invalid && *ys != invalid; ys++) { \ + if (*ys == x) { \ + log_syntax(unit, LOG_ERR, filename, \ + line, -x, \ + "Duplicate entry, ignoring: %s", \ + en); \ + x = invalid; \ + } \ + } \ + \ + if (x == invalid) \ + continue; \ + \ + *(xs + i) = x; \ + new_xs = realloc(xs, (++i + 1) * sizeof(type)); \ + if (new_xs) \ + xs = new_xs; \ + else \ + return -ENOMEM; \ + \ + *(xs + i) = invalid; \ + } \ + \ + free(*enums); \ + *enums = xs; \ + xs = NULL; \ + \ + return 0; \ + } diff --git a/src/libshared/include/shared/dev-setup.h b/src/libshared/include/shared/dev-setup.h new file mode 100644 index 0000000000..5766a62060 --- /dev/null +++ b/src/libshared/include/shared/dev-setup.h @@ -0,0 +1,24 @@ +#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 . +***/ + +#include + +int dev_setup(const char *prefix, uid_t uid, gid_t gid); diff --git a/src/libshared/include/shared/dns-domain.h b/src/libshared/include/shared/dns-domain.h new file mode 100644 index 0000000000..72e4cb0104 --- /dev/null +++ b/src/libshared/include/shared/dns-domain.h @@ -0,0 +1,109 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include +#include +#include +#include + +#include "basic/hashmap.h" +#include "basic/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 + +/* 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 *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); + +int dns_name_concat(const char *a, const char *b, char **ret); + +static inline int dns_name_normalize(const char *s, char **ret) { + /* dns_name_concat() normalizes as a side-effect */ + return dns_name_concat(s, NULL, ret); +} + +static inline int dns_name_is_valid(const char *s) { + int r; + + /* dns_name_normalize() verifies as a side effect */ + r = dns_name_normalize(s, NULL); + if (r == -EINVAL) + return 0; + if (r < 0) + return r; + return 1; +} + +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); + +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/libshared/include/shared/dropin.h b/src/libshared/include/shared/dropin.h new file mode 100644 index 0000000000..44235405f6 --- /dev/null +++ b/src/libshared/include/shared/dropin.h @@ -0,0 +1,61 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/set.h" +#include "basic/unit-name.h" + +int drop_in_file(const char *dir, const char *unit, unsigned level, + const char *name, char **_p, char **_q); + +int write_drop_in(const char *dir, const char *unit, unsigned level, + const char *name, const char *data); + +int write_drop_in_format(const char *dir, const char *unit, unsigned level, + const char *name, const char *format, ...) _printf_(5, 6); + +/** + * This callback will be called for each directory entry @entry, + * with @filepath being the full path to the entry. + * + * If return value is negative, loop will be aborted. + */ +typedef int (*dependency_consumer_t)(UnitDependency dependency, + const char *entry, + const char* filepath, + void *arg); + +int unit_file_process_dir( + Set * unit_path_cache, + const char *unit_path, + const char *name, + const char *suffix, + UnitDependency dependency, + dependency_consumer_t consumer, + void *arg, + char ***strv); + +int unit_file_find_dropin_paths( + char **lookup_path, + Set *unit_path_cache, + Set *names, + char ***paths); diff --git a/src/libshared/include/shared/efivars.h b/src/libshared/include/shared/efivars.h new file mode 100644 index 0000000000..547b161367 --- /dev/null +++ b/src/libshared/include/shared/efivars.h @@ -0,0 +1,131 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/time-util.h" + +#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f) +#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c) +#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + +#ifdef ENABLE_EFI + +bool is_efi_boot(void); +bool is_efi_secure_boot(void); +bool is_efi_secure_boot_setup_mode(void); +int efi_reboot_to_firmware_supported(void); +int efi_get_reboot_to_firmware(void); +int efi_set_reboot_to_firmware(bool value); + +int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size); +int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size); +int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p); + +int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active); +int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path); +int efi_remove_boot_option(uint16_t id); +int efi_get_boot_order(uint16_t **order); +int efi_set_boot_order(uint16_t *order, size_t n); +int efi_get_boot_options(uint16_t **options); + +int efi_loader_get_device_part_uuid(sd_id128_t *u); +int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader); + +#else + +static inline bool is_efi_boot(void) { + return false; +} + +static inline bool is_efi_secure_boot(void) { + return false; +} + +static inline bool is_efi_secure_boot_setup_mode(void) { + return false; +} + +static inline int efi_reboot_to_firmware_supported(void) { + return -EOPNOTSUPP; +} + +static inline int efi_get_reboot_to_firmware(void) { + return -EOPNOTSUPP; +} + +static inline int efi_set_reboot_to_firmware(bool value) { + return -EOPNOTSUPP; +} + +static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) { + return -EOPNOTSUPP; +} + +static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) { + return -EOPNOTSUPP; +} + +static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) { + return -EOPNOTSUPP; +} + +static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) { + return -EOPNOTSUPP; +} + +static inline int efi_remove_boot_option(uint16_t id) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_order(uint16_t **order) { + return -EOPNOTSUPP; +} + +static inline int efi_set_boot_order(uint16_t *order, size_t n) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_options(uint16_t **options) { + return -EOPNOTSUPP; +} + +static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) { + return -EOPNOTSUPP; +} + +static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { + return -EOPNOTSUPP; +} + +#endif + +char *efi_tilt_backslashes(char *s); diff --git a/src/libshared/include/shared/fdset.h b/src/libshared/include/shared/fdset.h new file mode 100644 index 0000000000..3ff5d519af --- /dev/null +++ b/src/libshared/include/shared/fdset.h @@ -0,0 +1,58 @@ +#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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/set.h" + +typedef struct FDSet FDSet; + +FDSet* fdset_new(void); +FDSet* fdset_free(FDSet *s); + +int fdset_put(FDSet *s, int fd); +int fdset_put_dup(FDSet *s, int fd); + +bool fdset_contains(FDSet *s, int fd); +int fdset_remove(FDSet *s, int fd); + +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); + +int fdset_cloexec(FDSet *fds, bool b); + +int fdset_close_others(FDSet *fds); + +unsigned fdset_size(FDSet *fds); +bool fdset_isempty(FDSet *fds); + +int fdset_iterate(FDSet *s, Iterator *i); + +int fdset_steal_first(FDSet *fds); + +#define FDSET_FOREACH(fd, fds, i) \ + for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i))) + +DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free); +#define _cleanup_fdset_free_ _cleanup_(fdset_freep) diff --git a/src/libshared/include/shared/fstab-util.h b/src/libshared/include/shared/fstab-util.h new file mode 100644 index 0000000000..3d9e7ab907 --- /dev/null +++ b/src/libshared/include/shared/fstab-util.h @@ -0,0 +1,52 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/macro.h" + +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); + +static inline bool fstab_test_option(const char *opts, const char *names) { + return !!fstab_filter_options(opts, names, NULL, NULL, NULL); +} + +int fstab_find_pri(const char *options, int *ret); + +static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no) { + int r; + const char *opt; + + /* If first name given is last, return 1. + * If second name given is last or neither is found, return 0. */ + + r = fstab_filter_options(opts, yes_no, &opt, NULL, NULL); + assert(r >= 0); + + return opt == yes_no; +} + +char *fstab_node_to_udev_node(const char *p); diff --git a/src/libshared/include/shared/gcrypt-util.h b/src/libshared/include/shared/gcrypt-util.h new file mode 100644 index 0000000000..cf33b3c59c --- /dev/null +++ b/src/libshared/include/shared/gcrypt-util.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#ifdef HAVE_GCRYPT +#include + +void initialize_libgcrypt(bool secmem); +int string_hashsum(const char *s, size_t len, int md_algorithm, char **out); +#endif + +static inline int string_hashsum_sha224(const char *s, size_t len, char **out) { +#ifdef HAVE_GCRYPT + return string_hashsum(s, len, GCRY_MD_SHA224, out); +#else + return -EOPNOTSUPP; +#endif +} diff --git a/src/libshared/include/shared/generator.h b/src/libshared/include/shared/generator.h new file mode 100644 index 0000000000..a6017c1b76 --- /dev/null +++ b/src/libshared/include/shared/generator.h @@ -0,0 +1,40 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int generator_write_fsck_deps( + FILE *f, + const char *dir, + const char *what, + const char *where, + const char *type); + +int generator_write_timeouts( + const char *dir, + const char *what, + const char *where, + const char *opts, + char **filtered); + +int generator_write_initrd_root_device_deps( + const char *dir, + const char *what); diff --git a/src/libshared/include/shared/gpt.h b/src/libshared/include/shared/gpt.h new file mode 100644 index 0000000000..07153b51f4 --- /dev/null +++ b/src/libshared/include/shared/gpt.h @@ -0,0 +1,66 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +/* 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. */ + +#define GPT_ROOT_X86 SD_ID128_MAKE(44,47,95,40,f2,97,41,b2,9a,f7,d1,31,d5,f0,45,8a) +#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) +#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) +#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) + +#if defined(__x86_64__) +# define GPT_ROOT_NATIVE GPT_ROOT_X86_64 +# define GPT_ROOT_SECONDARY GPT_ROOT_X86 +#elif defined(__i386__) +# 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 +#elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN) +# define GPT_ROOT_NATIVE GPT_ROOT_ARM +#endif + +/* Flags we recognize on the root, swap, home and srv partitions when + * doing auto-discovery. These happen to be identical to what + * Microsoft defines for its own Basic Data Partitions, but that's + * just because we saw no point in defining any other values here. */ +#define GPT_FLAG_READ_ONLY (1ULL << 60) +#define GPT_FLAG_NO_AUTO (1ULL << 63) + +#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) diff --git a/src/libshared/include/shared/ima-util.h b/src/libshared/include/shared/ima-util.h new file mode 100644 index 0000000000..5be94761fd --- /dev/null +++ b/src/libshared/include/shared/ima-util.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +bool use_ima(void); diff --git a/src/libshared/include/shared/import-util.h b/src/libshared/include/shared/import-util.h new file mode 100644 index 0000000000..353fa9045c --- /dev/null +++ b/src/libshared/include/shared/import-util.h @@ -0,0 +1,43 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" + +typedef enum ImportVerify { + IMPORT_VERIFY_NO, + IMPORT_VERIFY_CHECKSUM, + IMPORT_VERIFY_SIGNATURE, + _IMPORT_VERIFY_MAX, + _IMPORT_VERIFY_INVALID = -1, +} ImportVerify; + +int import_url_last_component(const char *url, char **ret); +int import_url_change_last_component(const char *url, const char *suffix, char **ret); + +const char* import_verify_to_string(ImportVerify v) _const_; +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); + +int import_assign_pool_quota_and_warn(const char *path); diff --git a/src/libshared/include/shared/initreq.h b/src/libshared/include/shared/initreq.h new file mode 100644 index 0000000000..710037d84b --- /dev/null +++ b/src/libshared/include/shared/initreq.h @@ -0,0 +1,77 @@ +/* + * initreq.h Interface to talk to init through /dev/initctl. + * + * Copyright (C) 1995-2004 Miquel van Smoorenburg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS + * + */ +#ifndef _INITREQ_H +#define _INITREQ_H + +#include + +#if defined(__FreeBSD_kernel__) +# define INIT_FIFO "/etc/.initctl" +#else +# define INIT_FIFO "/dev/initctl" +#endif + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START 0 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_POWERFAIL 2 +#define INIT_CMD_POWERFAILNOW 3 +#define INIT_CMD_POWEROK 4 +#define INIT_CMD_BSD 5 +#define INIT_CMD_SETENV 6 +#define INIT_CMD_UNSETENV 7 + +#define INIT_CMD_CHANGECONS 12345 + +#ifdef MAXHOSTNAMELEN +# define INITRQ_HLEN MAXHOSTNAMELEN +#else +# define INITRQ_HLEN 64 +#endif + +/* + * This is what BSD 4.4 uses when talking to init. + * Linux doesn't use this right now. + */ +struct init_request_bsd { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[INITRQ_HLEN]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a separate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct init_request { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct init_request_bsd bsd; + char data[368]; + } i; +}; + +#endif diff --git a/src/libshared/include/shared/install-printf.h b/src/libshared/include/shared/install-printf.h new file mode 100644 index 0000000000..8a570fc265 --- /dev/null +++ b/src/libshared/include/shared/install-printf.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "install.h" + +int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret); diff --git a/src/libshared/include/shared/install.h b/src/libshared/include/shared/install.h new file mode 100644 index 0000000000..e056d374b6 --- /dev/null +++ b/src/libshared/include/shared/install.h @@ -0,0 +1,257 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/strv.h" +#include "basic/unit-name.h" + +typedef enum UnitFileChangeType UnitFileChangeType; +typedef enum UnitFilePresetMode UnitFilePresetMode; +typedef enum UnitFileScope UnitFileScope; +typedef enum UnitFileState UnitFileState; +typedef enum UnitFileType UnitFileType; +typedef struct UnitFileChange UnitFileChange; +typedef struct UnitFileInstallInfo UnitFileInstallInfo; +typedef struct UnitFileList UnitFileList; + +#include "path-lookup.h" + +enum UnitFileScope { + UNIT_FILE_SYSTEM, + UNIT_FILE_GLOBAL, + UNIT_FILE_USER, + _UNIT_FILE_SCOPE_MAX, + _UNIT_FILE_SCOPE_INVALID = -1 +}; + +enum UnitFileState { + UNIT_FILE_ENABLED, + UNIT_FILE_ENABLED_RUNTIME, + UNIT_FILE_LINKED, + UNIT_FILE_LINKED_RUNTIME, + UNIT_FILE_MASKED, + UNIT_FILE_MASKED_RUNTIME, + UNIT_FILE_STATIC, + UNIT_FILE_DISABLED, + UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT, + UNIT_FILE_BAD, + _UNIT_FILE_STATE_MAX, + _UNIT_FILE_STATE_INVALID = -1 +}; + +enum UnitFilePresetMode { + UNIT_FILE_PRESET_FULL, + UNIT_FILE_PRESET_ENABLE_ONLY, + UNIT_FILE_PRESET_DISABLE_ONLY, + _UNIT_FILE_PRESET_MAX, + _UNIT_FILE_PRESET_INVALID = -1 +}; + +enum UnitFileChangeType { + UNIT_FILE_SYMLINK, + UNIT_FILE_UNLINK, + UNIT_FILE_IS_MASKED, + UNIT_FILE_IS_DANGLING, + _UNIT_FILE_CHANGE_TYPE_MAX, + _UNIT_FILE_CHANGE_INVALID = INT_MIN +}; + +/* type can either one of the UnitFileChangeTypes listed above, or a negative error. + * If source is specified, it should be the contents of the path symlink. + * In case of an error, source should be the existing symlink contents or NULL + */ +struct UnitFileChange { + int type; /* UnitFileChangeType or bust */ + char *path; + char *source; +}; + +static inline bool unit_file_changes_have_modification(const UnitFileChange* changes, unsigned n_changes) { + unsigned i; + for (i = 0; i < n_changes; i++) + if (IN_SET(changes[i].type, UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK)) + return true; + return false; +} + +struct UnitFileList { + char *path; + 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 **aliases; + char **wanted_by; + char **required_by; + 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); +} + +bool unit_type_may_alias(UnitType type) _const_; +bool unit_type_may_template(UnitType type) _const_; + +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); +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_preset_all( + UnitFileScope scope, + bool runtime, + const char *root_dir, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_link( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_revert( + UnitFileScope scope, + 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, + const char *target, + UnitDependency dep, + bool force, + UnitFileChange **changes, + unsigned *n_changes); + +int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); + +int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); +Hashmap* 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); +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet); + +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); + +const char *unit_file_state_to_string(UnitFileState s) _const_; +UnitFileState unit_file_state_from_string(const char *s) _pure_; +/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */ + +const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_; +UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_; + +const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_; +UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_; diff --git a/src/libshared/include/shared/logs-show.h b/src/libshared/include/shared/logs-show.h new file mode 100644 index 0000000000..f16cb64838 --- /dev/null +++ b/src/libshared/include/shared/logs-show.h @@ -0,0 +1,71 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/macro.h" +#include "basic/time-util.h" +#include "basic/util.h" + +#include "output-mode.h" + +int output_journal( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags, + bool *ellipsized); + +int add_match_this_boot(sd_journal *j, const char *machine); + +int add_matches_for_unit( + sd_journal *j, + const char *unit); + +int add_matches_for_user_unit( + sd_journal *j, + const char *unit, + uid_t uid); + +int show_journal_by_unit( + FILE *f, + const char *unit, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + uid_t uid, + OutputFlags flags, + int journal_open_flags, + bool system_unit, + bool *ellipsized); + +void json_escape( + FILE *f, + const char* p, + size_t l, + OutputFlags flags); diff --git a/src/libshared/include/shared/machine-image.h b/src/libshared/include/shared/machine-image.h new file mode 100644 index 0000000000..30a005e114 --- /dev/null +++ b/src/libshared/include/shared/machine-image.h @@ -0,0 +1,103 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/hashmap.h" +#include "basic/lockfile-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/time-util.h" + +typedef enum ImageType { + IMAGE_DIRECTORY, + IMAGE_SUBVOLUME, + IMAGE_RAW, + _IMAGE_TYPE_MAX, + _IMAGE_TYPE_INVALID = -1 +} ImageType; + +typedef struct Image { + ImageType type; + char *name; + char *path; + bool read_only; + + usec_t crtime; + usec_t mtime; + + uint64_t usage; + uint64_t usage_exclusive; + uint64_t limit; + uint64_t limit_exclusive; + + void *userdata; +} Image; + +Image *image_unref(Image *i); +void image_hashmap_free(Hashmap *map); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free); + +int image_find(const char *name, Image **ret); +int image_discover(Hashmap *map); + +int image_remove(Image *i); +int image_rename(Image *i, const char *new_name); +int image_clone(Image *i, const char *new_name, bool read_only); +int image_read_only(Image *i, bool b); + +const char* image_type_to_string(ImageType t) _const_; +ImageType image_type_from_string(const char *s) _pure_; + +bool image_name_is_valid(const char *s) _pure_; + +int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local); +int image_name_lock(const char *name, int operation, LockFile *ret); + +int image_set_limit(Image *i, uint64_t referenced_max); + +static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { + assert(i); + + return i->name && i->name[0] == '.'; +} + +static inline bool IMAGE_IS_VENDOR(const struct Image *i) { + assert(i); + + return i->path && path_startswith(i->path, "/usr"); +} + +static inline bool IMAGE_IS_HOST(const struct Image *i) { + assert(i); + + if (i->name && streq(i->name, ".host")) + return true; + + if (i->path && path_equal(i->path, "/")) + return true; + + return false; +} diff --git a/src/libshared/include/shared/machine-pool.h b/src/libshared/include/shared/machine-pool.h new file mode 100644 index 0000000000..fe99b7e0ae --- /dev/null +++ b/src/libshared/include/shared/machine-pool.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#include + +#include + +/* Grow the /var/lib/machines directory after each 10MiB written */ +#define GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024)) + +int setup_machine_directory(uint64_t size, sd_bus_error *error); +int grow_machine_directory(void); diff --git a/src/libshared/include/shared/output-mode.h b/src/libshared/include/shared/output-mode.h new file mode 100644 index 0000000000..9a8d8127d3 --- /dev/null +++ b/src/libshared/include/shared/output-mode.h @@ -0,0 +1,57 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" + +typedef enum OutputMode { + OUTPUT_SHORT, + OUTPUT_SHORT_ISO, + OUTPUT_SHORT_PRECISE, + OUTPUT_SHORT_MONOTONIC, + OUTPUT_SHORT_UNIX, + OUTPUT_VERBOSE, + OUTPUT_EXPORT, + OUTPUT_JSON, + OUTPUT_JSON_PRETTY, + OUTPUT_JSON_SSE, + OUTPUT_CAT, + _OUTPUT_MODE_MAX, + _OUTPUT_MODE_INVALID = -1 +} OutputMode; + +/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the + * logs output, others only to the process tree output. */ + +typedef enum OutputFlags { + OUTPUT_SHOW_ALL = 1 << 0, + OUTPUT_FOLLOW = 1 << 1, + OUTPUT_WARN_CUTOFF = 1 << 2, + OUTPUT_FULL_WIDTH = 1 << 3, + OUTPUT_COLOR = 1 << 4, + OUTPUT_CATALOG = 1 << 5, + OUTPUT_BEGIN_NEWLINE = 1 << 6, + OUTPUT_UTC = 1 << 7, + OUTPUT_KERNEL_THREADS = 1 << 8, + OUTPUT_NO_HOSTNAME = 1 << 9, +} OutputFlags; + +const char* output_mode_to_string(OutputMode m) _const_; +OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/libshared/include/shared/pager.h b/src/libshared/include/shared/pager.h new file mode 100644 index 0000000000..2a3cbe1721 --- /dev/null +++ b/src/libshared/include/shared/pager.h @@ -0,0 +1,30 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" + +int pager_open(bool no_pager, bool jump_to_end); +void pager_close(void); +bool pager_have(void) _pure_; + +int show_man_page(const char *page, bool null_stdio); diff --git a/src/libshared/include/shared/path-lookup.h b/src/libshared/include/shared/path-lookup.h new file mode 100644 index 0000000000..ab14bdc267 --- /dev/null +++ b/src/libshared/include/shared/path-lookup.h @@ -0,0 +1,77 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" + +typedef struct LookupPaths LookupPaths; + +#include "install.h" + +typedef enum LookupPathsFlags { + LOOKUP_PATHS_EXCLUDE_GENERATED = 1, +} LookupPathsFlags; + +struct LookupPaths { + /* Where we look for unit files. This includes the individual special paths below, but also any vendor + * supplied, static unit file paths. */ + char **search_path; + + /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin + * shall place his own unit files. */ + char *persistent_config; + char *runtime_config; + + /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of + * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should + * not alter these directories directly. */ + char *generator; + char *generator_early; + char *generator_late; + + /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special + * semantics of this directory: all units created transiently have their unit files removed as the transient + * unit is unloaded. The user should not alter this directory directly. */ + char *transient; + + /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the + * snippets are placed in the transient directory though (see above). The user should not alter this directory + * directly. */ + char *persistent_control; + char *runtime_control; + + /* The root directory prepended to all items above, or NULL */ + char *root_dir; +}; + +int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); + +int lookup_paths_reduce(LookupPaths *p); + +int lookup_paths_mkdir_generator(LookupPaths *p); +void lookup_paths_trim_generator(LookupPaths *p); +void lookup_paths_flush_generator(LookupPaths *p); + +void lookup_paths_free(LookupPaths *p); +#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free) + +char **generator_binary_paths(UnitFileScope scope); diff --git a/src/libshared/include/shared/ptyfwd.h b/src/libshared/include/shared/ptyfwd.h new file mode 100644 index 0000000000..8f32184433 --- /dev/null +++ b/src/libshared/include/shared/ptyfwd.h @@ -0,0 +1,48 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/macro.h" + +typedef struct PTYForward PTYForward; + +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); + +int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup); +int pty_forward_get_ignore_vhangup(PTYForward *f); + +DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/libshared/include/shared/resolve-util.h b/src/libshared/include/shared/resolve-util.h new file mode 100644 index 0000000000..8df46599ad --- /dev/null +++ b/src/libshared/include/shared/resolve-util.h @@ -0,0 +1,60 @@ +#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 . +***/ + +#include "basic/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/libshared/include/shared/seccomp-util.h b/src/libshared/include/shared/seccomp-util.h new file mode 100644 index 0000000000..be33eecb85 --- /dev/null +++ b/src/libshared/include/shared/seccomp-util.h @@ -0,0 +1,35 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +const char* seccomp_arch_to_string(uint32_t c); +int seccomp_arch_from_string(const char *n, uint32_t *ret); + +int seccomp_add_secondary_archs(scmp_filter_ctx *c); + +typedef struct SystemCallFilterSet { + const char *set_name; + const char *value; +} SystemCallFilterSet; + +extern const SystemCallFilterSet syscall_filter_sets[]; diff --git a/src/libshared/include/shared/sleep-config.h b/src/libshared/include/shared/sleep-config.h new file mode 100644 index 0000000000..ad10039ff4 --- /dev/null +++ b/src/libshared/include/shared/sleep-config.h @@ -0,0 +1,26 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int parse_sleep_config(const char *verb, char ***modes, char ***states); + +int can_sleep(const char *verb); +int can_sleep_disk(char **types); +int can_sleep_state(char **types); diff --git a/src/libshared/include/shared/spawn-ask-password-agent.h b/src/libshared/include/shared/spawn-ask-password-agent.h new file mode 100644 index 0000000000..fb0749b13f --- /dev/null +++ b/src/libshared/include/shared/spawn-ask-password-agent.h @@ -0,0 +1,23 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int ask_password_agent_open(void); +void ask_password_agent_close(void); diff --git a/src/libshared/include/shared/spawn-polkit-agent.h b/src/libshared/include/shared/spawn-polkit-agent.h new file mode 100644 index 0000000000..42b2989ded --- /dev/null +++ b/src/libshared/include/shared/spawn-polkit-agent.h @@ -0,0 +1,23 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int polkit_agent_open(void); +void polkit_agent_close(void); diff --git a/src/libshared/include/shared/specifier.h b/src/libshared/include/shared/specifier.h new file mode 100644 index 0000000000..6b1623ee61 --- /dev/null +++ b/src/libshared/include/shared/specifier.h @@ -0,0 +1,37 @@ +#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 . +***/ + +typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret); + +typedef struct Specifier { + const char specifier; + const SpecifierCallback lookup; + void *data; +} Specifier; + +int specifier_printf(const char *text, const Specifier table[], void *userdata, char **ret); + +int specifier_string(char specifier, void *data, void *userdata, char **ret); + +int specifier_machine_id(char specifier, void *data, void *userdata, char **ret); +int specifier_boot_id(char specifier, void *data, void *userdata, char **ret); +int specifier_host_name(char specifier, void *data, void *userdata, char **ret); +int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret); diff --git a/src/libshared/include/shared/switch-root.h b/src/libshared/include/shared/switch-root.h new file mode 100644 index 0000000000..a7a080b3e8 --- /dev/null +++ b/src/libshared/include/shared/switch-root.h @@ -0,0 +1,23 @@ +#pragma once + +#include +/*** + This file is part of systemd. + + Copyright 2012 Harald Hoyer, 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 . +***/ + +int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags); diff --git a/src/libshared/include/shared/sysctl-util.h b/src/libshared/include/shared/sysctl-util.h new file mode 100644 index 0000000000..2decb39f58 --- /dev/null +++ b/src/libshared/include/shared/sysctl-util.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +char *sysctl_normalize(char *s); +int sysctl_read(const char *property, char **value); +int sysctl_write(const char *property, const char *value); + diff --git a/src/libshared/include/shared/test-tables.h b/src/libshared/include/shared/test-tables.h new file mode 100644 index 0000000000..228e510104 --- /dev/null +++ b/src/libshared/include/shared/test-tables.h @@ -0,0 +1,60 @@ +/*** + This file is part of systemd + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +typedef const char* (*lookup_t)(int); +typedef int (*reverse_t)(const char*); + +static inline void _test_table(const char *name, + lookup_t lookup, + reverse_t reverse, + int size, + bool sparse) { + int i, boring = 0; + + for (i = -1; i < size + 1; i++) { + const char* val = lookup(i); + int rev; + + if (val) { + rev = reverse(val); + boring = 0; + } else { + rev = reverse("--no-such--value----"); + boring += i >= 0; + } + + if (boring < 1 || i == size) + printf("%s: %d → %s → %d\n", name, i, val, rev); + else if (boring == 1) + printf("%*s ...\n", (int) strlen(name), ""); + + assert_se(!(i >= 0 && i < size ? + sparse ? rev != i && rev != -1 : val == NULL || rev != i : + val != NULL || rev != -1)); + } +} + +#define test_table(lower, upper) \ + _test_table(STRINGIFY(lower), lower##_to_string, lower##_from_string, _##upper##_MAX, false) + +#define test_table_sparse(lower, upper) \ + _test_table(STRINGIFY(lower), lower##_to_string, lower##_from_string, _##upper##_MAX, true) diff --git a/src/libshared/include/shared/tests.h b/src/libshared/include/shared/tests.h new file mode 100644 index 0000000000..93f09013a1 --- /dev/null +++ b/src/libshared/include/shared/tests.h @@ -0,0 +1,22 @@ +#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 . +***/ + +char* setup_fake_runtime_dir(void); diff --git a/src/libshared/include/shared/udev-util.h b/src/libshared/include/shared/udev-util.h new file mode 100644 index 0000000000..c3db8ab30e --- /dev/null +++ b/src/libshared/include/shared/udev-util.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/util.h" +#include "udev.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref); + +#define _cleanup_udev_unref_ _cleanup_(udev_unrefp) +#define _cleanup_udev_device_unref_ _cleanup_(udev_device_unrefp) +#define _cleanup_udev_enumerate_unref_ _cleanup_(udev_enumerate_unrefp) +#define _cleanup_udev_event_unref_ _cleanup_(udev_event_unrefp) +#define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp) +#define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp) +#define _cleanup_udev_ctrl_connection_unref_ _cleanup_(udev_ctrl_connection_unrefp) +#define _cleanup_udev_ctrl_msg_unref_ _cleanup_(udev_ctrl_msg_unrefp) +#define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp) +#define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup) diff --git a/src/libshared/include/shared/uid-range.h b/src/libshared/include/shared/uid-range.h new file mode 100644 index 0000000000..4044eb4c9c --- /dev/null +++ b/src/libshared/include/shared/uid-range.h @@ -0,0 +1,33 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +typedef struct UidRange { + uid_t start, nr; +} UidRange; + +int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr); +int uid_range_add_str(UidRange **p, unsigned *n, const char *s); + +int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid); +bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid); diff --git a/src/libshared/include/shared/utmp-wtmp.h b/src/libshared/include/shared/utmp-wtmp.h new file mode 100644 index 0000000000..50dde37b16 --- /dev/null +++ b/src/libshared/include/shared/utmp-wtmp.h @@ -0,0 +1,74 @@ +#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 . +***/ + +#include +#include + +#include "basic/time-util.h" +#include "basic/util.h" + +#ifdef HAVE_UTMP +int utmp_get_runlevel(int *runlevel, int *previous); + +int utmp_put_shutdown(void); +int utmp_put_reboot(usec_t timestamp); +int utmp_put_runlevel(int runlevel, int previous); + +int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); + +int utmp_wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, void *userdata), + void *userdata); + +#else /* HAVE_UTMP */ + +static inline int utmp_get_runlevel(int *runlevel, int *previous) { + return -ESRCH; +} +static inline int utmp_put_shutdown(void) { + return 0; +} +static inline int utmp_put_reboot(usec_t timestamp) { + return 0; +} +static inline int utmp_put_runlevel(int runlevel, int previous) { + return 0; +} +static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { + return 0; +} +static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { + return 0; +} +static inline int utmp_wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, void *userdata), + void *userdata) { + return 0; +} + +#endif /* HAVE_UTMP */ diff --git a/src/libshared/include/shared/vlan-util.h b/src/libshared/include/shared/vlan-util.h new file mode 100644 index 0000000000..b7c2f03383 --- /dev/null +++ b/src/libshared/include/shared/vlan-util.h @@ -0,0 +1,35 @@ +#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 . +***/ + +#include +#include + +#define VLANID_MAX 4094 +#define VLANID_INVALID UINT16_MAX + +/* Note that we permit VLAN Id 0 here, as that is apparently OK by the Linux kernel */ +static inline bool vlanid_is_valid(uint16_t id) { + return id <= VLANID_MAX; +} + +int parse_vlanid(const char *p, uint16_t *ret); + +int config_parse_vlanid(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/libshared/include/shared/watchdog.h b/src/libshared/include/shared/watchdog.h new file mode 100644 index 0000000000..78e1406f5d --- /dev/null +++ b/src/libshared/include/shared/watchdog.h @@ -0,0 +1,29 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/time-util.h" +#include "basic/util.h" + +int watchdog_set_timeout(usec_t *usec); +int watchdog_ping(void); +void watchdog_close(bool disarm); diff --git a/src/libshared/src/Makefile b/src/libshared/src/Makefile new file mode 100644 index 0000000000..053e56feaa --- /dev/null +++ b/src/libshared/src/Makefile @@ -0,0 +1,182 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libshared.la + +libshared_la_SOURCES = \ + src/shared/output-mode.h \ + src/shared/output-mode.c \ + src/shared/gpt.h \ + src/shared/udev-util.h \ + src/shared/linux/auto_dev-ioctl.h \ + src/shared/initreq.h \ + src/shared/dns-domain.c \ + src/shared/dns-domain.h \ + src/shared/efivars.c \ + src/shared/efivars.h \ + src/shared/fstab-util.c \ + src/shared/fstab-util.h \ + src/shared/sleep-config.c \ + src/shared/sleep-config.h \ + src/shared/conf-parser.c \ + src/shared/conf-parser.h \ + src/shared/pager.c \ + src/shared/pager.h \ + src/shared/spawn-polkit-agent.c \ + src/shared/spawn-polkit-agent.h \ + src/shared/apparmor-util.c \ + src/shared/apparmor-util.h \ + src/shared/ima-util.c \ + src/shared/ima-util.h \ + src/shared/ptyfwd.c \ + src/shared/ptyfwd.h \ + src/shared/base-filesystem.c \ + src/shared/base-filesystem.h \ + src/shared/uid-range.c \ + src/shared/uid-range.h \ + src/shared/install.c \ + src/shared/install.h \ + src/shared/install-printf.c \ + src/shared/install-printf.h \ + src/shared/path-lookup.c \ + src/shared/path-lookup.h \ + src/shared/specifier.c \ + src/shared/specifier.h \ + src/shared/dev-setup.c \ + src/shared/dev-setup.h \ + src/shared/dropin.c \ + src/shared/dropin.h \ + src/shared/condition.c \ + src/shared/condition.h \ + src/shared/clean-ipc.c \ + src/shared/clean-ipc.h \ + src/shared/generator.h \ + src/shared/generator.c \ + src/shared/acpi-fpdt.h \ + src/shared/acpi-fpdt.c \ + src/shared/boot-timestamps.h \ + src/shared/boot-timestamps.c \ + src/shared/cgroup-show.c \ + src/shared/cgroup-show.h \ + src/shared/utmp-wtmp.h \ + src/shared/watchdog.c \ + src/shared/watchdog.h \ + src/shared/spawn-ask-password-agent.c \ + src/shared/spawn-ask-password-agent.h \ + src/shared/ask-password-api.c \ + src/shared/ask-password-api.h \ + src/shared/switch-root.h \ + src/shared/switch-root.c \ + src/shared/import-util.c \ + src/shared/import-util.h \ + src/shared/sysctl-util.c \ + src/shared/sysctl-util.h \ + src/shared/bus-util.c \ + src/shared/bus-util.h \ + src/shared/logs-show.c \ + src/shared/logs-show.h \ + src/shared/machine-image.c \ + src/shared/machine-image.h \ + src/shared/machine-pool.c \ + src/shared/machine-pool.h \ + src/shared/resolve-util.c \ + src/shared/resolve-util.h \ + src/shared/bus-unit-util.c \ + src/shared/bus-unit-util.h \ + src/shared/vlan-util.h \ + src/shared/vlan-util.c \ + src/shared/tests.h \ + src/shared/tests.c \ + src/shared/fdset.c \ + src/shared/fdset.h + +ifneq ($(HAVE_UTMP),) +libshared_la_SOURCES += \ + src/shared/utmp-wtmp.c +endif # HAVE_UTMP + +ifneq ($(HAVE_SECCOMP),) +libshared_la_SOURCES += \ + src/shared/seccomp-util.h \ + src/shared/seccomp-util.c +endif # HAVE_SECCOMP + +ifneq ($(HAVE_ACL),) +libshared_la_SOURCES += \ + src/shared/acl-util.c \ + src/shared/acl-util.h +endif # HAVE_ACL + +libshared_la_CFLAGS = \ + $(ACL_CFLAGS) \ + $(LIBIDN_CFLAGS) \ + $(SECCOMP_CFLAGS) + +libshared_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la \ + libsystemd-journal-internal.la \ + libudev-internal.la \ + $(ACL_LIBS) \ + $(LIBIDN_LIBS) \ + $(SECCOMP_LIBS) + +rootlibexec_LTLIBRARIES += \ + libsystemd-shared.la + +libsystemd_shared_la_SOURCES = \ + $(libbasic_la_SOURCES) \ + $(libshared_la_SOURCES) \ + $(libsystemd_internal_la_SOURCES) \ + $(libsystemd_journal_internal_la_SOURCES) \ + $(libudev_internal_la_SOURCES) + +libsystemd_shared_la_CFLAGS = \ + $(libbasic_la_CFLAGS) \ + $(libshared_la_CFLAGS) \ + $(libsystemd_internal_la_CFLAGS) \ + $(libsystemd_journal_internal_la_CFLAGS) \ + $(libudev_internal_la_CFLAGS) \ + $(ACL_CFLAGS) \ + $(LIBIDN_CFLAGS) \ + $(SECCOMP_CFLAGS) \ + -fvisibility=default + +# We can't use libshared_la_LIBADD here because it would +# pull in libsystemd*-internal.la +libsystemd_shared_la_LIBADD = \ + $(libbasic_la_LIBADD) \ + $(libsystemd_internal_la_LIBADD) \ + $(libsystemd_journal_internal_la_LIBADD) \ + $(libudev_internal_la_LIBADD) \ + $(ACL_LIBS) \ + $(LIBIDN_LIBS) \ + $(SECCOMP_LIBS) + +libsystemd_shared_la_LDFLAGS = \ + -release $(PACKAGE_VERSION) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libshared/src/acl-util.c b/src/libshared/src/acl-util.c new file mode 100644 index 0000000000..b9af6aca5c --- /dev/null +++ b/src/libshared/src/acl-util.c @@ -0,0 +1,429 @@ +/*** + This file is part of systemd. + + Copyright 2011,2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/acl-util.h" + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) { + acl_entry_t i; + int r; + + assert(acl); + assert(entry); + + for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + uid_t *u; + bool b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + u = acl_get_qualifier(i); + if (!u) + return -errno; + + b = *u == uid; + acl_free(u); + + if (b) { + *entry = i; + return 1; + } + } + if (r < 0) + return -errno; + + return 0; +} + +int calc_acl_mask_if_needed(acl_t *acl_p) { + acl_entry_t i; + int r; + bool need = false; + + assert(acl_p); + + for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag == ACL_MASK) + return 0; + + if (IN_SET(tag, ACL_USER, ACL_GROUP)) + need = true; + } + if (r < 0) + return -errno; + + 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) { + acl_entry_t i; + int r; + bool have_user_obj = false, have_group_obj = false, have_other = false; + struct stat st; + _cleanup_(acl_freep) acl_t basic = NULL; + + assert(acl_p); + + for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag == ACL_USER_OBJ) + have_user_obj = true; + else if (tag == ACL_GROUP_OBJ) + have_group_obj = true; + else if (tag == ACL_OTHER) + have_other = true; + if (have_user_obj && have_group_obj && have_other) + return 0; + } + if (r < 0) + return -errno; + + r = stat(path, &st); + if (r < 0) + return -errno; + + basic = acl_from_mode(st.st_mode); + if (!basic) + return -errno; + + for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) { + acl_tag_t tag; + acl_entry_t dst; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if ((tag == ACL_USER_OBJ && have_user_obj) || + (tag == ACL_GROUP_OBJ && have_group_obj) || + (tag == ACL_OTHER && have_other)) + continue; + + r = acl_create_entry(acl_p, &dst); + if (r < 0) + return -errno; + + r = acl_copy_entry(dst, i); + if (r < 0) + return -errno; + } + if (r < 0) + return -errno; + return 0; +} + +int acl_search_groups(const char *path, char ***ret_groups) { + _cleanup_strv_free_ char **g = NULL; + _cleanup_(acl_free) acl_t acl = NULL; + bool ret = false; + acl_entry_t entry; + int r; + + assert(path); + + acl = acl_get_file(path, ACL_TYPE_DEFAULT); + if (!acl) + return -errno; + + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + for (;;) { + _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL; + acl_tag_t tag; + + if (r < 0) + return -errno; + if (r == 0) + break; + + if (acl_get_tag_type(entry, &tag) < 0) + return -errno; + + if (tag != ACL_GROUP) + goto next; + + gid = acl_get_qualifier(entry); + if (!gid) + return -errno; + + if (in_gid(*gid) > 0) { + if (!ret_groups) + return true; + + ret = true; + } + + if (ret_groups) { + char *name; + + name = gid_to_name(*gid); + if (!name) + return -ENOMEM; + + r = strv_consume(&g, name); + if (r < 0) + return r; + } + + next: + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); + } + + if (ret_groups) { + *ret_groups = g; + g = NULL; + } + + return ret; +} + +int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) { + _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */ + _cleanup_strv_free_ char **split; + char **entry; + int r = -EINVAL; + _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL; + + split = strv_split(text, ","); + if (!split) + return -ENOMEM; + + STRV_FOREACH(entry, split) { + char *p; + + p = startswith(*entry, "default:"); + if (!p) + p = startswith(*entry, "d:"); + + if (p) + r = strv_push(&d, p); + else + r = strv_push(&a, *entry); + if (r < 0) + return r; + } + + if (!strv_isempty(a)) { + _cleanup_free_ char *join; + + join = strv_join(a, ","); + if (!join) + return -ENOMEM; + + a_acl = acl_from_text(join); + if (!a_acl) + return -errno; + + if (want_mask) { + r = calc_acl_mask_if_needed(&a_acl); + if (r < 0) + return r; + } + } + + if (!strv_isempty(d)) { + _cleanup_free_ char *join; + + join = strv_join(d, ","); + if (!join) + return -ENOMEM; + + d_acl = acl_from_text(join); + if (!d_acl) + return -errno; + + if (want_mask) { + r = calc_acl_mask_if_needed(&d_acl); + if (r < 0) + return r; + } + } + + *acl_access = a_acl; + *acl_default = d_acl; + a_acl = d_acl = NULL; + + return 0; +} + +static int acl_entry_equal(acl_entry_t a, acl_entry_t b) { + acl_tag_t tag_a, tag_b; + + if (acl_get_tag_type(a, &tag_a) < 0) + return -errno; + + if (acl_get_tag_type(b, &tag_b) < 0) + return -errno; + + if (tag_a != tag_b) + return false; + + switch (tag_a) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + /* can have only one of those */ + return true; + case ACL_USER: { + _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL; + + uid_a = acl_get_qualifier(a); + if (!uid_a) + return -errno; + + uid_b = acl_get_qualifier(b); + if (!uid_b) + return -errno; + + return *uid_a == *uid_b; + } + case ACL_GROUP: { + _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL; + + gid_a = acl_get_qualifier(a); + if (!gid_a) + return -errno; + + gid_b = acl_get_qualifier(b); + if (!gid_b) + return -errno; + + return *gid_a == *gid_b; + } + default: + assert_not_reached("Unknown acl tag type"); + } +} + +static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) { + acl_entry_t i; + int r; + + for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + r = acl_entry_equal(i, entry); + if (r < 0) + return r; + if (r > 0) { + *out = i; + return 1; + } + } + if (r < 0) + return -errno; + return 0; +} + +int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) { + _cleanup_(acl_freep) acl_t old; + acl_entry_t i; + int r; + + old = acl_get_file(path, type); + if (!old) + return -errno; + + for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) { + + acl_entry_t j; + + r = find_acl_entry(old, i, &j); + if (r < 0) + return r; + if (r == 0) + if (acl_create_entry(&old, &j) < 0) + return -errno; + + if (acl_copy_entry(j, i) < 0) + return -errno; + } + if (r < 0) + return -errno; + + *acl = old; + 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/libshared/src/acpi-fpdt.c b/src/libshared/src/acpi-fpdt.c new file mode 100644 index 0000000000..d3a744acbd --- /dev/null +++ b/src/libshared/src/acpi-fpdt.c @@ -0,0 +1,164 @@ +/*** + This file is part of systemd. + + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/time-util.h" +#include "shared/acpi-fpdt.h" + +struct acpi_table_header { + char signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_revision; + char asl_compiler_id[4]; + uint32_t asl_compiler_revision; +}; + +enum { + ACPI_FPDT_TYPE_BOOT = 0, + ACPI_FPDT_TYPE_S3PERF = 1, +}; + +struct acpi_fpdt_header { + uint16_t type; + uint8_t length; + uint8_t revision; + uint8_t reserved[4]; + uint64_t ptr; +}; + +struct acpi_fpdt_boot_header { + char signature[4]; + uint32_t length; +}; + +enum { + ACPI_FPDT_S3PERF_RESUME_REC = 0, + ACPI_FPDT_S3PERF_SUSPEND_REC = 1, + ACPI_FPDT_BOOT_REC = 2, +}; + +struct acpi_fpdt_boot { + uint16_t type; + uint8_t length; + uint8_t revision; + uint8_t reserved[4]; + uint64_t reset_end; + uint64_t load_start; + uint64_t startup_start; + uint64_t exit_services_entry; + uint64_t exit_services_exit; +}; + +int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { + _cleanup_free_ char *buf = NULL; + struct acpi_table_header *tbl; + size_t l = 0; + struct acpi_fpdt_header *rec; + int r; + uint64_t ptr = 0; + _cleanup_close_ int fd = -1; + struct acpi_fpdt_boot_header hbrec; + struct acpi_fpdt_boot brec; + + r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l); + if (r < 0) + return r; + + if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header)) + return -EINVAL; + + tbl = (struct acpi_table_header *)buf; + if (l != tbl->length) + return -EINVAL; + + if (memcmp(tbl->signature, "FPDT", 4) != 0) + return -EINVAL; + + /* find Firmware Basic Boot Performance Pointer Record */ + for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header)); + (char *)rec < buf + l; + rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) { + if (rec->length <= 0) + break; + if (rec->type != ACPI_FPDT_TYPE_BOOT) + continue; + if (rec->length != sizeof(struct acpi_fpdt_header)) + continue; + + ptr = rec->ptr; + break; + } + + if (ptr == 0) + return -ENODATA; + + /* read Firmware Basic Boot Performance Data Record */ + fd = open("/dev/mem", O_CLOEXEC|O_RDONLY); + if (fd < 0) + return -errno; + + l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr); + if (l != sizeof(struct acpi_fpdt_boot_header)) + return -EINVAL; + + if (memcmp(hbrec.signature, "FBPT", 4) != 0) + return -EINVAL; + + if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot)) + return -EINVAL; + + l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header)); + if (l != sizeof(struct acpi_fpdt_boot)) + return -EINVAL; + + if (brec.length != sizeof(struct acpi_fpdt_boot)) + return -EINVAL; + + if (brec.type != ACPI_FPDT_BOOT_REC) + return -EINVAL; + + if (brec.exit_services_exit == 0) + /* Non-UEFI compatible boot. */ + return -ENODATA; + + if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start) + return -EINVAL; + if (brec.exit_services_exit > NSEC_PER_HOUR) + return -EINVAL; + + if (loader_start) + *loader_start = brec.startup_start / 1000; + if (loader_exit) + *loader_exit = brec.exit_services_exit / 1000; + + return 0; +} diff --git a/src/libshared/src/apparmor-util.c b/src/libshared/src/apparmor-util.c new file mode 100644 index 0000000000..af5498e05e --- /dev/null +++ b/src/libshared/src/apparmor-util.c @@ -0,0 +1,39 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/parse-util.h" +#include "shared/apparmor-util.h" + +bool mac_apparmor_use(void) { + static int cached_use = -1; + + if (cached_use < 0) { + _cleanup_free_ char *p = NULL; + + cached_use = + read_one_line_file("/sys/module/apparmor/parameters/enabled", &p) >= 0 && + parse_boolean(p) > 0; + } + + return cached_use; +} diff --git a/src/libshared/src/ask-password-api.c b/src/libshared/src/ask-password-api.c new file mode 100644 index 0000000000..5848ff5582 --- /dev/null +++ b/src/libshared/src/ask-password-api.c @@ -0,0 +1,734 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/random-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/umask-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/ask-password-api.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; + + serial = add_key("user", keyname, p, n, 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) { + + if (ttyfd < 0) + return; + + while (p > 0) { + p--; + + loop_write(ttyfd, "\b \b", 3, false); + } +} + +int ask_password_tty( + const char *message, + const char *keyname, + usec_t until, + AskPasswordFlags flags, + const char *flag_file, + char **ret) { + + struct termios old_termios, new_termios; + char passphrase[LINE_MAX + 1] = {}, *x; + size_t p = 0, codepoint = 0; + int r; + _cleanup_close_ int ttyfd = -1, notify = -1; + struct pollfd pollfd[2]; + bool reset_tty = false; + bool dirty = false; + enum { + POLL_TTY, + POLL_INOTIFY + }; + + assert(ret); + + if (flags & ASK_PASSWORD_NO_TTY) + return -EUNATCH; + + if (!message) + message = "Password:"; + + if (flag_file) { + notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); + if (notify < 0) { + r = -errno; + goto finish; + } + + if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) { + r = -errno; + goto finish; + } + } + + ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (ttyfd >= 0) { + + if (tcgetattr(ttyfd, &old_termios) < 0) { + r = -errno; + goto finish; + } + + if (colors_enabled()) + loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false); + loop_write(ttyfd, message, strlen(message), false); + loop_write(ttyfd, " ", 1, false); + if (colors_enabled()) + loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false); + + new_termios = old_termios; + new_termios.c_lflag &= ~(ICANON|ECHO); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + + if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) { + r = -errno; + goto finish; + } + + reset_tty = true; + } + + zero(pollfd); + pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO; + pollfd[POLL_TTY].events = POLLIN; + pollfd[POLL_INOTIFY].fd = notify; + pollfd[POLL_INOTIFY].events = POLLIN; + + for (;;) { + char c; + int sleep_for = -1, k; + ssize_t n; + + if (until > 0) { + usec_t y; + + y = now(CLOCK_MONOTONIC); + + if (y > until) { + r = -ETIME; + goto finish; + } + + sleep_for = (int) ((until - y) / USEC_PER_MSEC); + } + + if (flag_file) + if (access(flag_file, F_OK) < 0) { + r = -errno; + goto finish; + } + + k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); + if (k < 0) { + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } else if (k == 0) { + r = -ETIME; + goto finish; + } + + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) + flush_fd(notify); + + if (pollfd[POLL_TTY].revents == 0) + continue; + + n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + + r = -errno; + goto finish; + + } else if (n == 0) + break; + + if (c == '\n') + break; + else if (c == 21) { /* C-u */ + + if (!(flags & ASK_PASSWORD_SILENT)) + backspace_chars(ttyfd, p); + p = 0; + + } else if (c == '\b' || c == 127) { + + if (p > 0) { + + if (!(flags & ASK_PASSWORD_SILENT)) + backspace_chars(ttyfd, 1); + + p--; + } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) { + + 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 ... */ + if (ttyfd >= 0) + loop_write(ttyfd, "(no echo) ", 10, false); + + } else if (ttyfd >= 0) + loop_write(ttyfd, "\a", 1, false); + + } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) { + + backspace_chars(ttyfd, p); + flags |= ASK_PASSWORD_SILENT; + + /* ... or by pressing TAB at any time. */ + + if (ttyfd >= 0) + loop_write(ttyfd, "(no echo) ", 10, false); + } else { + if (p >= sizeof(passphrase)-1) { + loop_write(ttyfd, "\a", 1, false); + continue; + } + + passphrase[p++] = c; + + if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) { + n = utf8_encoded_valid_unichar(passphrase + codepoint); + if (n >= 0) { + codepoint = p; + 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; + } + + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x)); + + *ret = x; + r = 0; + +finish: + if (ttyfd >= 0 && reset_tty) { + loop_write(ttyfd, "\n", 1, false); + tcsetattr(ttyfd, TCSADRAIN, &old_termios); + } + + return r; +} + +static int create_socket(char **name) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + _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 -errno; + + snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); + + RUN_WITH_UMASK(0177) { + if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return -errno; + } + + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + return -errno; + + c = strdup(sa.un.sun_path); + if (!c) + return -ENOMEM; + + *name = c; + + r = fd; + fd = -1; + + return r; +} + +int ask_password_agent( + const char *message, + const char *icon, + const char *id, + const char *keyname, + usec_t until, + AskPasswordFlags flags, + char ***ret) { + + enum { + FD_SOCKET, + FD_SIGNAL, + _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_free_ char *socket_name = NULL; + _cleanup_strv_free_ char **l = NULL; + _cleanup_fclose_ FILE *f = NULL; + struct pollfd pollfd[_FD_MAX]; + sigset_t mask, oldmask; + int r; + + 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); + + (void) mkdir_p_label("/run/systemd/ask-password", 0755); + + fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + r = fd; + goto finish; + } + + (void) fchmod(fd, 0644); + + f = fdopen(fd, "w"); + if (!f) { + r = -errno; + goto finish; + } + + fd = -1; + + signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (signal_fd < 0) { + r = -errno; + goto finish; + } + + socket_fd = create_socket(&socket_name); + if (socket_fd < 0) { + r = socket_fd; + goto finish; + } + + fprintf(f, + "[Ask]\n" + "PID="PID_FMT"\n" + "Socket=%s\n" + "AcceptCached=%i\n" + "Echo=%i\n" + "NotAfter="USEC_FMT"\n", + getpid(), + socket_name, + (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0, + (flags & ASK_PASSWORD_ECHO) ? 1 : 0, + until); + + if (message) + fprintf(f, "Message=%s\n", message); + + if (icon) + fprintf(f, "Icon=%s\n", icon); + + if (id) + fprintf(f, "Id=%s\n", id); + + r = fflush_and_check(f); + if (r < 0) + goto finish; + + memcpy(final, temp, sizeof(temp)); + + final[sizeof(final)-11] = 'a'; + final[sizeof(final)-10] = 's'; + final[sizeof(final)-9] = 'k'; + + if (rename(temp, final) < 0) { + r = -errno; + goto finish; + } + + zero(pollfd); + pollfd[FD_SOCKET].fd = socket_fd; + pollfd[FD_SOCKET].events = POLLIN; + pollfd[FD_SIGNAL].fd = signal_fd; + pollfd[FD_SIGNAL].events = POLLIN; + + for (;;) { + char passphrase[LINE_MAX+1]; + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + ssize_t n; + int k; + usec_t t; + + t = now(CLOCK_MONOTONIC); + + if (until > 0 && until <= t) { + r = -ETIME; + goto finish; + } + + k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1); + if (k < 0) { + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } + + if (k <= 0) { + r = -ETIME; + goto finish; + } + + if (pollfd[FD_SIGNAL].revents & POLLIN) { + r = -EINTR; + goto finish; + } + + if (pollfd[FD_SOCKET].revents != POLLIN) { + r = -EIO; + goto finish; + } + + zero(iovec); + iovec.iov_base = passphrase; + iovec.iov_len = sizeof(passphrase); + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + n = recvmsg(socket_fd, &msghdr, 0); + if (n < 0) { + if (errno == EAGAIN || + errno == EINTR) + continue; + + r = -errno; + goto finish; + } + + cmsg_close_all(&msghdr); + + if (n <= 0) { + log_debug("Message too short"); + continue; + } + + if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || + control.cmsghdr.cmsg_level != SOL_SOCKET || + control.cmsghdr.cmsg_type != SCM_CREDENTIALS || + control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + log_debug("Received message without credentials. Ignoring."); + continue; + } + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + if (ucred->uid != 0) { + log_debug("Got request from unprivileged user. Ignoring."); + continue; + } + + if (passphrase[0] == '+') { + /* An empty message refers to the empty password */ + if (n == 1) + l = strv_new("", NULL); + else + l = strv_parse_nulstr(passphrase+1, n-1); + memory_erase(passphrase, n); + if (!l) { + r = -ENOMEM; + goto finish; + } + + if (strv_length(l) <= 0) { + l = strv_free(l); + log_debug("Invalid packet"); + continue; + } + + break; + } + + if (passphrase[0] == '-') { + r = -ECANCELED; + goto finish; + } + + 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) + (void) unlink(socket_name); + + (void) unlink(temp); + + if (final[0]) + (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, + const char *keyname, + usec_t until, + AskPasswordFlags flags, + char ***ret) { + + int r; + + assert(ret); + + if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) { + r = ask_password_keyring(keyname, flags, ret); + if (r != -ENOKEY) + return r; + } + + 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; + + 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/libshared/src/base-filesystem.c b/src/libshared/src/base-filesystem.c new file mode 100644 index 0000000000..881bfb061c --- /dev/null +++ b/src/libshared/src/base-filesystem.c @@ -0,0 +1,129 @@ +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/base-filesystem.h" + +typedef struct BaseFilesystem { + const char *dir; + 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, true }, + { "sbin", 0, "usr/sbin\0", NULL }, + { "usr", 0755, NULL, NULL }, + { "var", 0755, NULL, NULL }, + { "etc", 0755, NULL, NULL }, +#if defined(__i386__) || defined(__x86_64__) + { "lib64", 0, "usr/lib/x86_64-linux-gnu\0" + "usr/lib64\0", "ld-linux-x86-64.so.2" }, +#endif +}; + +int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { + _cleanup_close_ int fd = -1; + unsigned i; + int r = 0; + + fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return log_error_errno(errno, "Failed to open root file system: %m"); + + for (i = 0; i < ELEMENTSOF(table); i ++) { + if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + continue; + + if (table[i].target) { + const char *target = NULL, *s; + + /* check if one of the targets exists */ + NULSTR_FOREACH(s, table[i].target) { + if (faccessat(fd, s, F_OK, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + /* check if a specific file exists at the target path */ + if (table[i].exists) { + _cleanup_free_ char *p = NULL; + + p = strjoin(s, "/", table[i].exists, NULL); + if (!p) + return log_oom(); + + if (faccessat(fd, p, F_OK, AT_SYMLINK_NOFOLLOW) < 0) + continue; + } + + target = s; + break; + } + + if (!target) + continue; + + r = symlinkat(target, fd, table[i].dir); + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir); + + if (uid != UID_INVALID || gid != UID_INVALID) { + if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) + return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir); + } + + continue; + } + + RUN_WITH_UMASK(0000) + r = mkdirat(fd, table[i].dir, table[i].mode); + 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) + return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir); + } + } + + return 0; +} diff --git a/src/libshared/src/boot-timestamps.c b/src/libshared/src/boot-timestamps.c new file mode 100644 index 0000000000..3e3034f032 --- /dev/null +++ b/src/libshared/src/boot-timestamps.c @@ -0,0 +1,64 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" +#include "basic/time-util.h" +#include "shared/acpi-fpdt.h" +#include "shared/boot-timestamps.h" +#include "shared/efivars.h" + +int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) { + usec_t x = 0, y = 0, a; + int r; + dual_timestamp _n; + + assert(firmware); + assert(loader); + + if (!n) { + dual_timestamp_get(&_n); + n = &_n; + } + + r = acpi_get_boot_usec(&x, &y); + if (r < 0) { + r = efi_loader_get_boot_usec(&x, &y); + if (r < 0) + return r; + } + + /* Let's convert this to timestamps where the firmware + * began/loader began working. To make this more confusing: + * since usec_t is unsigned and the kernel's monotonic clock + * begins at kernel initialization we'll actually initialize + * the monotonic timestamps here as negative of the actual + * value. */ + + firmware->monotonic = y; + loader->monotonic = y - x; + + a = n->monotonic + firmware->monotonic; + firmware->realtime = n->realtime > a ? n->realtime - a : 0; + + a = n->monotonic + loader->monotonic; + loader->realtime = n->realtime > a ? n->realtime - a : 0; + + return 0; +} diff --git a/src/libshared/src/bus-unit-util.c b/src/libshared/src/bus-unit-util.c new file mode 100644 index 0000000000..eadf2fa14f --- /dev/null +++ b/src/libshared/src/bus-unit-util.c @@ -0,0 +1,1324 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/env-util.h" +#include "basic/escape.h" +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/locale-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/rlimit-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/syslog-util.h" +#include "basic/terminal-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "sd-bus/bus-internal.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { + assert(message); + assert(u); + + u->machine = NULL; + + return sd_bus_message_read( + message, + "(ssssssouso)", + &u->id, + &u->description, + &u->load_state, + &u->active_state, + &u->sub_state, + &u->following, + &u->unit_path, + &u->job_id, + &u->job_type, + &u->job_path); +} + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { + const char *eq, *field; + int r, rl; + + assert(m); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + field = strndupa(assignment, eq - assignment); + eq++; + + if (streq(field, "CPUQuota")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); + else { + r = parse_percent(eq); + if (r <= 0) { + log_error_errno(r, "CPU quota '%s' invalid.", eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); + } + + goto finish; + + } else if (streq(field, "EnvironmentFile")) { + + r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); + goto finish; + + } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { + char *n; + usec_t t; + size_t l; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + + l = strlen(field); + n = newa(char, l + 2); + if (!n) + return log_oom(); + + /* Change suffix Sec → USec */ + strcpy(mempcpy(n, field, l - 3), "USec"); + r = sd_bus_message_append(m, "sv", n, "t", t); + goto finish; + + } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) { + uint64_t bytes; + + if (isempty(eq) || streq(eq, "infinity")) + bytes = CGROUP_LIMIT_MAX; + else { + r = parse_percent(eq); + if (r >= 0) { + char *n; + + /* When this is a percentage we'll convert this into a relative value in the range + * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related + * ones). This way the physical memory size can be determined server-side */ + + n = strjoina(field, "Scale"); + r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); + goto finish; + + } else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) + return log_error_errno(r, "Failed to parse bytes specification %s", assignment); + } + } + + r = sd_bus_message_append(m, "sv", field, "t", bytes); + goto finish; + } else if (streq(field, "TasksMax")) { + uint64_t t; + + if (isempty(eq) || streq(eq, "infinity")) + t = (uint64_t) -1; + else { + r = parse_percent(eq); + if (r >= 0) { + r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); + goto finish; + } else { + r = safe_atou64(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment); + } + + } + + r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); + goto finish; + } + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + rl = rlimit_from_string(field); + if (rl >= 0) { + const char *sn; + struct rlimit l; + + r = rlimit_parse(rl, eq, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq); + + r = sd_bus_message_append(m, "v", "t", l.rlim_max); + 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_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); + + } else if (STR_IN_SET(field, + "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute")) { + + r = parse_boolean(eq); + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); + + r = sd_bus_message_append(m, "v", "b", r); + + } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) { + uint64_t u; + + r = cg_weight_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { + uint64_t u; + + r = cg_blkio_weight_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, + "User", "Group", "DevicePolicy", "KillMode", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "StandardInput", "StandardOutput", "StandardError", + "Description", "Slice", "Type", "WorkingDirectory", + "RootDirectory", "SyslogIdentifier", "ProtectSystem", + "ProtectHome", "SELinuxContext")) + r = sd_bus_message_append(m, "v", "s", eq); + + 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); + else { + const char *path, *rwm, *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + rwm = e+1; + } else { + path = eq; + rwm = ""; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); + } + + } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *bandwidth, *e; + uint64_t bytes; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + bandwidth = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + if (streq(bandwidth, "infinity")) { + bytes = CGROUP_LIMIT_MAX; + } else { + r = parse_size(bandwidth, 1000, &bytes); + if (r < 0) { + log_error("Failed to parse byte value %s.", bandwidth); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); + } + + } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *weight, *e; + uint64_t u; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + weight = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = safe_atou64(weight, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, weight); + return -EINVAL; + } + r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); + } + + } else if (streq(field, "Nice")) { + int32_t i; + + r = safe_atoi32(eq, &i); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", i); + + } 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_close_container(m); + + } else if (streq(field, "KillSignal")) { + int sig; + + sig = signal_from_string_try_harder(eq); + if (sig < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", sig); + + } 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", + "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { + 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); + return -EINVAL; + } + +finish: + 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); + + return 0; +} + +typedef struct BusWaitForJobs { + sd_bus *bus; + Set *jobs; + + char *name; + char *result; + + sd_bus_slot *slot_job_removed; + sd_bus_slot *slot_disconnected; +} BusWaitForJobs; + +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { + assert(m); + + log_error("Warning! D-Bus connection terminated."); + sd_bus_close(sd_bus_message_get_bus(m)); + + return 0; +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char *path, *unit, *result; + BusWaitForJobs *d = userdata; + uint32_t id; + char *found; + int r; + + assert(m); + assert(d); + + r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + found = set_remove(d->jobs, (char*) path); + if (!found) + return 0; + + free(found); + + if (!isempty(result)) + d->result = strdup(result); + + if (!isempty(unit)) + d->name = strdup(unit); + + return 0; +} + +void bus_wait_for_jobs_free(BusWaitForJobs *d) { + if (!d) + return; + + set_free_free(d->jobs); + + sd_bus_slot_unref(d->slot_disconnected); + sd_bus_slot_unref(d->slot_job_removed); + + sd_bus_unref(d->bus); + + free(d->name); + free(d->result); + + free(d); +} + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; + int r; + + assert(bus); + assert(ret); + + d = new0(BusWaitForJobs, 1); + if (!d) + return -ENOMEM; + + d->bus = sd_bus_ref(bus); + + /* When we are a bus client we match by sender. Direct + * connections OTOH have no initialized sender field, and + * hence we ignore the sender then */ + r = sd_bus_add_match( + bus, + &d->slot_job_removed, + bus->bus_client ? + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'" : + "type='signal'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + match_job_removed, d); + if (r < 0) + return r; + + r = sd_bus_add_match( + bus, + &d->slot_disconnected, + "type='signal'," + "sender='org.freedesktop.DBus.Local'," + "interface='org.freedesktop.DBus.Local'," + "member='Disconnected'", + match_disconnected, d); + if (r < 0) + return r; + + *ret = d; + d = NULL; + + return 0; +} + +static int bus_process_wait(sd_bus *bus) { + int r; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static const struct { + const char *result, *explanation; +} explanations [] = { + { "resources", "of unavailable resources or another system error" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { + _cleanup_free_ char *service_shell_quoted = NULL; + const char *systemctl = "systemctl", *journalctl = "journalctl"; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service); + + if (extra_args && extra_args[1]) { + _cleanup_free_ char *t; + + t = strv_join((char**) extra_args, " "); + systemctl = strjoina("systemctl ", t ? : ""); + journalctl = strjoina("journalctl ", t ? : ""); + } + + if (!isempty(result)) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + explanations[i].explanation, + systemctl, + service_shell_quoted ?: "", + journalctl); + goto finish; + } + } + + log_error("Job for %s failed.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + systemctl, + service_shell_quoted ?: "", + journalctl); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"%1$s reset-failed %2$s\"\n" + "followed by \"%1$s start %2$s\" again.", + systemctl, + service_shell_quoted ?: ""); +} + +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d->result); + + if (!quiet) { + if (streq(d->result, "canceled")) + log_error("Job for %s canceled.", strna(d->name)); + else if (streq(d->result, "timeout")) + log_error("Job for %s timed out.", strna(d->name)); + else if (streq(d->result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); + else if (streq(d->result, "invalid")) + log_error("%s is not active, cannot reload.", strna(d->name)); + else if (streq(d->result, "assert")) + log_error("Assertion failed on job for %s.", strna(d->name)); + else if (streq(d->result, "unsupported")) + log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { + if (d->name) { + int q; + _cleanup_free_ char *result = NULL; + + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); + + log_job_error_with_service_result(d->name, result, extra_args); + } else + log_error("Job failed. See \"journalctl -xe\" for details."); + } + } + + if (streq(d->result, "canceled")) + r = -ECANCELED; + else if (streq(d->result, "timeout")) + r = -ETIME; + else if (streq(d->result, "dependency")) + r = -EIO; + else if (streq(d->result, "invalid")) + r = -ENOEXEC; + else if (streq(d->result, "assert")) + r = -EPROTO; + else if (streq(d->result, "unsupported")) + r = -EOPNOTSUPP; + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) + r = -EIO; + + return r; +} + +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d); + + while (!set_isempty(d->jobs)) { + int q; + + q = bus_process_wait(d->bus); + if (q < 0) + return log_error_errno(q, "Failed to wait for response: %m"); + + if (d->result) { + 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) + r = q; + + log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); + } + + d->name = mfree(d->name); + d->result = mfree(d->result); + } + + return r; +} + +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { + int r; + + assert(d); + + r = set_ensure_allocated(&d->jobs, &string_hash_ops); + if (r < 0) + return r; + + return set_put_strdup(d->jobs, path); +} + +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { + int r; + + r = bus_wait_for_jobs_add(d, path); + if (r < 0) + return log_oom(); + + return bus_wait_for_jobs(d, quiet, NULL); +} + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { + const char *type, *path, *source; + int r; + + /* changes is dereferenced when calling unit_file_dump_changes() later, + * so we have to make sure this is not NULL. */ + assert(changes); + assert(n_changes); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { + /* We expect only "success" changes to be sent over the bus. + Hence, reject anything negative. */ + UnitFileChangeType ch = unit_file_change_type_from_string(type); + + if (ch < 0) { + log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); + continue; + } + + r = unit_file_changes_add(changes, n_changes, ch, path, source); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + unit_file_dump_changes(0, NULL, *changes, *n_changes, false); + return 0; +} + +struct CGroupInfo { + char *cgroup_path; + bool is_const; /* If false, cgroup_path should be free()'d */ + + Hashmap *pids; /* PID → process name */ + bool done; + + struct CGroupInfo *parent; + LIST_FIELDS(struct CGroupInfo, siblings); + LIST_HEAD(struct CGroupInfo, children); + size_t n_children; +}; + +static bool IS_ROOT(const char *p) { + return isempty(p) || streq(p, "/"); +} + +static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { + struct CGroupInfo *parent = NULL, *cg; + int r; + + assert(cgroups); + assert(ret); + + if (IS_ROOT(path)) + path = "/"; + + cg = hashmap_get(cgroups, path); + if (cg) { + *ret = cg; + return 0; + } + + if (!IS_ROOT(path)) { + const char *e, *pp; + + e = strrchr(path, '/'); + if (!e) + return -EINVAL; + + pp = strndupa(path, e - path); + if (!pp) + return -ENOMEM; + + r = add_cgroup(cgroups, pp, false, &parent); + if (r < 0) + return r; + } + + cg = new0(struct CGroupInfo, 1); + if (!cg) + return -ENOMEM; + + if (is_const) + cg->cgroup_path = (char*) path; + else { + cg->cgroup_path = strdup(path); + if (!cg->cgroup_path) { + free(cg); + return -ENOMEM; + } + } + + cg->is_const = is_const; + cg->parent = parent; + + r = hashmap_put(cgroups, cg->cgroup_path, cg); + if (r < 0) { + if (!is_const) + free(cg->cgroup_path); + free(cg); + return r; + } + + if (parent) { + LIST_PREPEND(siblings, parent->children, cg); + parent->n_children++; + } + + *ret = cg; + return 1; +} + +static int add_process( + Hashmap *cgroups, + const char *path, + pid_t pid, + const char *name) { + + struct CGroupInfo *cg; + int r; + + assert(cgroups); + assert(name); + assert(pid > 0); + + r = add_cgroup(cgroups, path, true, &cg); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); + if (r < 0) + return r; + + return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); +} + +static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { + assert(cgroups); + assert(cg); + + while (cg->children) + remove_cgroup(cgroups, cg->children); + + hashmap_remove(cgroups, cg->cgroup_path); + + if (!cg->is_const) + free(cg->cgroup_path); + + hashmap_free(cg->pids); + + if (cg->parent) + LIST_REMOVE(siblings, cg->parent->children, cg); + + free(cg); +} + +static int cgroup_info_compare_func(const void *a, const void *b) { + const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b; + + assert(x); + assert(y); + + return strcmp(x->cgroup_path, y->cgroup_path); +} + +static int dump_processes( + Hashmap *cgroups, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + struct CGroupInfo *cg; + int r; + + assert(prefix); + + if (IS_ROOT(cgroup_path)) + cgroup_path = "/"; + + cg = hashmap_get(cgroups, cgroup_path); + if (!cg) + return 0; + + if (!hashmap_isempty(cg->pids)) { + const char *name; + size_t n = 0, i; + pid_t *pids; + void *pidp; + Iterator j; + int width; + + /* Order processes by their PID */ + pids = newa(pid_t, hashmap_size(cg->pids)); + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) + pids[n++] = PTR_TO_PID(pidp); + + assert(n == hashmap_size(cg->pids)); + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *e = NULL; + const char *special; + bool more; + + name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); + assert(name); + + if (n_columns != 0) { + unsigned k; + + k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, k, 100); + if (e) + name = e; + } + + more = i+1 < n || cg->children; + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); + + fprintf(stdout, "%s%s%*"PID_PRI" %s\n", + prefix, + special, + width, pids[i], + name); + } + } + + if (cg->children) { + struct CGroupInfo **children, *child; + size_t n = 0, i; + + /* Order subcgroups by their name */ + children = newa(struct CGroupInfo*, cg->n_children); + LIST_FOREACH(siblings, child, cg->children) + children[n++] = child; + assert(n == cg->n_children); + qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func); + + if (n_columns != 0) + n_columns = MAX(LESS_BY(n_columns, 2U), 20U); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *pp = NULL; + const char *name, *special; + bool more; + + child = children[i]; + + name = strrchr(child->cgroup_path, '/'); + if (!name) + return -EINVAL; + name++; + + more = i+1 < n; + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); + + fputs(prefix, stdout); + fputs(special, stdout); + fputs(name, stdout); + fputc('\n', stdout); + + special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE); + + pp = strappend(prefix, special); + if (!pp) + return -ENOMEM; + + r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); + if (r < 0) + return r; + } + } + + cg->done = true; + return 0; +} + +static int dump_extra_processes( + Hashmap *cgroups, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + _cleanup_free_ pid_t *pids = NULL; + _cleanup_hashmap_free_ Hashmap *names = NULL; + struct CGroupInfo *cg; + size_t n_allocated = 0, n = 0, k; + Iterator i; + int width, r; + + /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as + * combined, sorted, linear list. */ + + HASHMAP_FOREACH(cg, cgroups, i) { + const char *name; + void *pidp; + Iterator j; + + if (cg->done) + continue; + + if (hashmap_isempty(cg->pids)) + continue; + + r = hashmap_ensure_allocated(&names, &trivial_hash_ops); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) + return -ENOMEM; + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { + pids[n++] = PTR_TO_PID(pidp); + + r = hashmap_put(names, pidp, (void*) name); + if (r < 0) + return r; + } + } + + if (n == 0) + return 0; + + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (k = 0; k < n; k++) { + _cleanup_free_ char *e = NULL; + const char *name; + + name = hashmap_get(names, PID_TO_PTR(pids[k])); + assert(name); + + if (n_columns != 0) { + unsigned z; + + z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, z, 100); + if (e) + name = e; + } + + fprintf(stdout, "%s%s %*" PID_PRI " %s\n", + prefix, + special_glyph(TRIANGULAR_BULLET), + width, pids[k], + name); + } + + return 0; +} + +int unit_show_processes( + sd_bus *bus, + const char *unit, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Hashmap *cgroups = NULL; + struct CGroupInfo *cg; + int r; + + assert(bus); + assert(unit); + + if (flags & OUTPUT_FULL_WIDTH) + n_columns = 0; + else if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitProcesses", + error, + &reply, + "s", + unit); + if (r < 0) + return r; + + cgroups = hashmap_new(&string_hash_ops); + if (!cgroups) + return -ENOMEM; + + r = sd_bus_message_enter_container(reply, 'a', "(sus)"); + if (r < 0) + goto finish; + + for (;;) { + const char *path = NULL, *name = NULL; + uint32_t pid; + + r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); + if (r < 0) + goto finish; + if (r == 0) + break; + + r = add_process(cgroups, path, pid, name); + if (r < 0) + goto finish; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto finish; + + r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); + if (r < 0) + goto finish; + + r = dump_extra_processes(cgroups, prefix, n_columns, flags); + +finish: + while ((cg = hashmap_first(cgroups))) + remove_cgroup(cgroups, cg); + + hashmap_free(cgroups); + + return r; +} diff --git a/src/libshared/src/bus-util.c b/src/libshared/src/bus-util.c new file mode 100644 index 0000000000..d5eb857b85 --- /dev/null +++ b/src/libshared/src/bus-util.c @@ -0,0 +1,1550 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/def.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/rlimit-util.h" +#include "basic/stdio-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "sd-bus/bus-internal.h" +#include "sd-bus/bus-message.h" +#include "shared/bus-util.h" + +static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + sd_event *e = userdata; + + assert(m); + assert(e); + + sd_bus_close(sd_bus_message_get_bus(m)); + sd_event_exit(e, 0); + + return 1; +} + +int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { + _cleanup_free_ char *match = NULL; + const char *unique; + int r; + + assert(e); + assert(bus); + assert(name); + + /* We unregister the name here and then wait for the + * NameOwnerChanged signal for this event to arrive before we + * quit. We do this in order to make sure that any queued + * requests are still processed before we really exit. */ + + r = sd_bus_get_unique_name(bus, &unique); + if (r < 0) + return r; + + r = asprintf(&match, + "sender='org.freedesktop.DBus'," + "type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "path='/org/freedesktop/DBus'," + "arg0='%s'," + "arg1='%s'," + "arg2=''", name, unique); + if (r < 0) + return -ENOMEM; + + r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e); + if (r < 0) + return r; + + r = sd_bus_release_name(bus, name); + if (r < 0) + return r; + + return 0; +} + +int bus_event_loop_with_idle( + sd_event *e, + sd_bus *bus, + const char *name, + usec_t timeout, + check_idle_t check_idle, + void *userdata) { + bool exiting = false; + int r, code; + + assert(e); + assert(bus); + assert(name); + + for (;;) { + bool idle; + + r = sd_event_get_state(e); + if (r < 0) + return r; + if (r == SD_EVENT_FINISHED) + break; + + if (check_idle) + idle = check_idle(userdata); + else + idle = true; + + r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout); + if (r < 0) + return r; + + if (r == 0 && !exiting && idle) { + + r = sd_bus_try_close(bus); + if (r == -EBUSY) + continue; + + /* Fallback for dbus1 connections: we + * unregister the name and wait for the + * response to come through for it */ + if (r == -EOPNOTSUPP) { + + /* Inform the service manager that we + * are going down, so that it will + * queue all further start requests, + * instead of assuming we are already + * running. */ + sd_notify(false, "STOPPING=1"); + + r = bus_async_unregister_and_exit(e, bus, name); + if (r < 0) + return r; + + exiting = true; + continue; + } + + if (r < 0) + return r; + + sd_event_exit(e, 0); + break; + } + } + + r = sd_event_get_exit_code(e, &code); + if (r < 0) + return r; + + return code; +} + +int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL; + int r, has_owner = 0; + + assert(c); + assert(name); + + r = sd_bus_call_method(c, + "org.freedesktop.DBus", + "/org/freedesktop/dbus", + "org.freedesktop.DBus", + "NameHasOwner", + error, + &rep, + "s", + name); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(rep, 'b', &has_owner); + if (r < 0) + return sd_bus_error_set_errno(error, r); + + return has_owner; +} + +static int check_good_user(sd_bus_message *m, uid_t good_user) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t sender_uid; + int r; + + assert(m); + + if (good_user == UID_INVALID) + return 0; + + r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + /* Don't trust augmented credentials for authorization */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); + + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + return r; + + return sender_uid == good_user; +} + +int bus_test_polkit( + sd_bus_message *call, + int capability, + const char *action, + const char **details, + uid_t good_user, + bool *_challenge, + sd_bus_error *e) { + + int r; + + assert(call); + assert(action); + + /* Tests non-interactively! */ + + r = check_good_user(call, good_user); + if (r != 0) + return r; + + r = sd_bus_query_sender_privilege(call, capability); + if (r < 0) + return r; + else if (r > 0) + return 1; +#ifdef ENABLE_POLKIT + else { + _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; + + sender = sd_bus_message_get_sender(call); + if (!sender) + return -EBADMSG; + + r = sd_bus_message_new_method_call( + call->bus, + &request, + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization"); + if (r < 0) + return r; + + r = sd_bus_message_append( + request, + "(sa{sv})s", + "system-bus-name", 1, "name", "s", sender, + action); + if (r < 0) + return r; + + r = sd_bus_message_open_container(request, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, details) { + r = sd_bus_message_append(request, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(request); + if (r < 0) + return r; + + r = sd_bus_message_append(request, "us", 0, NULL); + if (r < 0) + return r; + + r = sd_bus_call(call->bus, request, 0, e, &reply); + if (r < 0) { + /* Treat no PK available as access denied */ + if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { + sd_bus_error_free(e); + return -EACCES; + } + + return r; + } + + r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "bb", &authorized, &challenge); + if (r < 0) + return r; + + if (authorized) + return 1; + + if (_challenge) { + *_challenge = challenge; + return 0; + } + } +#endif + + return -EACCES; +} + +#ifdef ENABLE_POLKIT + +typedef struct AsyncPolkitQuery { + sd_bus_message *request, *reply; + sd_bus_message_handler_t callback; + void *userdata; + sd_bus_slot *slot; + Hashmap *registry; +} AsyncPolkitQuery; + +static void async_polkit_query_free(AsyncPolkitQuery *q) { + + if (!q) + return; + + sd_bus_slot_unref(q->slot); + + if (q->registry && q->request) + hashmap_remove(q->registry, q->request); + + sd_bus_message_unref(q->request); + sd_bus_message_unref(q->reply); + + free(q); +} + +static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + AsyncPolkitQuery *q = userdata; + int r; + + assert(reply); + assert(q); + + q->slot = sd_bus_slot_unref(q->slot); + q->reply = sd_bus_message_ref(reply); + + r = sd_bus_message_rewind(q->request, true); + if (r < 0) { + r = sd_bus_reply_method_errno(q->request, r, NULL); + goto finish; + } + + r = q->callback(q->request, q->userdata, &error_buffer); + r = bus_maybe_reply_error(q->request, r, &error_buffer); + +finish: + async_polkit_query_free(q); + + return r; +} + +#endif + +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) { + +#ifdef ENABLE_POLKIT + _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL; + AsyncPolkitQuery *q; + const char *sender, **k, **v; + sd_bus_message_handler_t callback; + void *userdata; + int c; +#endif + int r; + + assert(call); + assert(action); + assert(registry); + + r = check_good_user(call, good_user); + if (r != 0) + return r; + +#ifdef ENABLE_POLKIT + q = hashmap_get(*registry, call); + if (q) { + int authorized, challenge; + + /* This is the second invocation of this function, and + * there's already a response from polkit, let's + * process it */ + assert(q->reply); + + if (sd_bus_message_is_method_error(q->reply, NULL)) { + const sd_bus_error *e; + + /* Copy error from polkit reply */ + e = sd_bus_message_get_error(q->reply); + sd_bus_error_copy(error, e); + + /* Treat no PK available as access denied */ + if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) + return -EACCES; + + return -sd_bus_error_get_errno(e); + } + + r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}"); + if (r >= 0) + r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge); + + if (r < 0) + return r; + + if (authorized) + return 1; + + if (challenge) + return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required."); + + return -EACCES; + } +#endif + + r = sd_bus_query_sender_privilege(call, capability); + if (r < 0) + return r; + else if (r > 0) + return 1; + +#ifdef ENABLE_POLKIT + if (sd_bus_get_current_message(call->bus) != call) + return -EINVAL; + + callback = sd_bus_get_current_handler(call->bus); + if (!callback) + return -EINVAL; + + userdata = sd_bus_get_current_userdata(call->bus); + + sender = sd_bus_message_get_sender(call); + if (!sender) + return -EBADMSG; + + c = sd_bus_message_get_allow_interactive_authorization(call); + if (c < 0) + return c; + if (c > 0) + interactive = true; + + r = hashmap_ensure_allocated(registry, NULL); + if (r < 0) + return r; + + r = sd_bus_message_new_method_call( + call->bus, + &pk, + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization"); + if (r < 0) + return r; + + r = sd_bus_message_append( + pk, + "(sa{sv})s", + "system-bus-name", 1, "name", "s", sender, + action); + if (r < 0) + return r; + + r = sd_bus_message_open_container(pk, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, details) { + r = sd_bus_message_append(pk, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(pk); + if (r < 0) + return r; + + r = sd_bus_message_append(pk, "us", !!interactive, NULL); + if (r < 0) + return r; + + q = new0(AsyncPolkitQuery, 1); + if (!q) + return -ENOMEM; + + q->request = sd_bus_message_ref(call); + q->callback = callback; + q->userdata = userdata; + + r = hashmap_put(*registry, call, q); + if (r < 0) { + async_polkit_query_free(q); + return r; + } + + q->registry = *registry; + + r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0); + if (r < 0) { + async_polkit_query_free(q); + return r; + } + + return 0; +#endif + + return -EACCES; +} + +void bus_verify_polkit_async_registry_free(Hashmap *registry) { +#ifdef ENABLE_POLKIT + AsyncPolkitQuery *q; + + while ((q = hashmap_steal_first(registry))) + async_polkit_query_free(q); + + hashmap_free(registry); +#endif +} + +int bus_check_peercred(sd_bus *c) { + struct ucred ucred; + socklen_t l; + int fd; + + assert(c); + + fd = sd_bus_get_fd(c); + if (fd < 0) + return fd; + + l = sizeof(struct ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) + return -errno; + + if (l != sizeof(struct ucred)) + return -E2BIG; + + if (ucred.uid != 0 && ucred.uid != geteuid()) + return -EPERM; + + return 1; +} + +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_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 + * bus */ + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS); + if (r < 0) + return r; + + bus->bus_client = true; + + r = sd_bus_start(bus); + if (r >= 0) { + *_bus = bus; + bus = NULL; + return 0; + } + + bus = sd_bus_unref(bus); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = sd_bus_set_address(bus, "unix:path=/run/systemd/private"); + if (r < 0) + return r; + + r = sd_bus_start(bus); + if (r < 0) + return sd_bus_default_system(_bus); + + r = bus_check_peercred(bus); + if (r < 0) + return r; + + *_bus = bus; + bus = NULL; + + return 0; +} + +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; + + /* Try via kdbus first, and then directly */ + + assert(_bus); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0) + return -ENOMEM; + + bus->bus_client = true; + + r = sd_bus_start(bus); + if (r >= 0) { + *_bus = bus; + bus = NULL; + return 0; + } + + bus = sd_bus_unref(bus); + + e = secure_getenv("XDG_RUNTIME_DIR"); + if (!e) + return sd_bus_default_user(_bus); + + ee = bus_address_escape(e); + if (!ee) + return -ENOMEM; + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL); + if (!bus->address) + return -ENOMEM; + + r = sd_bus_start(bus); + if (r < 0) + return sd_bus_default_user(_bus); + + r = bus_check_peercred(bus); + if (r < 0) + return r; + + *_bus = bus; + bus = NULL; + + return 0; +} + +#define print_property(name, fmt, ...) \ + do { \ + if (value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + +int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) { + char type; + const char *contents; + int r; + + assert(name); + assert(property); + + r = sd_bus_message_peek_type(property, &type, &contents); + if (r < 0) + return r; + + switch (type) { + + case SD_BUS_TYPE_STRING: { + const char *s; + + r = sd_bus_message_read_basic(property, type, &s); + if (r < 0) + return r; + + if (all || !isempty(s)) { + _cleanup_free_ char *escaped = NULL; + + escaped = xescape(s, "\n"); + if (!escaped) + return -ENOMEM; + + print_property(name, "%s", escaped); + } + + return 1; + } + + case SD_BUS_TYPE_BOOLEAN: { + int b; + + r = sd_bus_message_read_basic(property, type, &b); + if (r < 0) + return r; + + print_property(name, "%s", yes_no(b)); + + return 1; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t u; + + r = sd_bus_message_read_basic(property, type, &u); + if (r < 0) + return r; + + /* Yes, heuristics! But we can change this check + * should it turn out to not be sufficient */ + + if (endswith(name, "Timestamp")) { + char timestamp[FORMAT_TIMESTAMP_MAX], *t; + + t = format_timestamp(timestamp, sizeof(timestamp), u); + if (t || all) + print_property(name, "%s", strempty(t)); + + } else if (strstr(name, "USec")) { + char timespan[FORMAT_TIMESPAN_MAX]; + + print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0)); + } else + print_property(name, "%"PRIu64, u); + + return 1; + } + + case SD_BUS_TYPE_INT64: { + int64_t i; + + r = sd_bus_message_read_basic(property, type, &i); + if (r < 0) + return r; + + print_property(name, "%"PRIi64, i); + + return 1; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t u; + + r = sd_bus_message_read_basic(property, type, &u); + if (r < 0) + return r; + + if (strstr(name, "UMask") || strstr(name, "Mode")) + print_property(name, "%04o", u); + else + print_property(name, "%"PRIu32, u); + + return 1; + } + + case SD_BUS_TYPE_INT32: { + int32_t i; + + r = sd_bus_message_read_basic(property, type, &i); + if (r < 0) + return r; + + print_property(name, "%"PRIi32, i); + return 1; + } + + case SD_BUS_TYPE_DOUBLE: { + double d; + + r = sd_bus_message_read_basic(property, type, &d); + if (r < 0) + return r; + + print_property(name, "%g", d); + return 1; + } + + case SD_BUS_TYPE_ARRAY: + if (streq(contents, "s")) { + bool first = true; + const char *str; + + r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { + _cleanup_free_ char *escaped = NULL; + + if (first && !value) + printf("%s=", name); + + escaped = xescape(str, "\n "); + if (!escaped) + return -ENOMEM; + + printf("%s%s", first ? "" : " ", escaped); + + first = false; + } + if (r < 0) + return r; + + if (first && all && !value) + printf("%s=", name); + if (!first || all) + puts(""); + + r = sd_bus_message_exit_container(property); + if (r < 0) + return r; + + return 1; + + } else if (streq(contents, "y")) { + const uint8_t *u; + size_t n; + + r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n); + if (r < 0) + return r; + + if (all || n > 0) { + unsigned int i; + + if (!value) + printf("%s=", name); + + for (i = 0; i < n; i++) + printf("%02x", u[i]); + + puts(""); + } + + return 1; + + } else if (streq(contents, "u")) { + uint32_t *u; + size_t n; + + r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n); + if (r < 0) + return r; + + if (all || n > 0) { + unsigned int i; + + if (!value) + printf("%s=", name); + + for (i = 0; i < n; i++) + printf("%08x", u[i]); + + puts(""); + } + + return 1; + } + + break; + } + + return 0; +} + +int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) { + _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); + assert(path); + + r = sd_bus_call_method(bus, + dest, + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *name; + const char *contents; + + r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name); + if (r < 0) + return r; + + if (!filter || strv_find(filter, name)) { + r = sd_bus_message_peek_type(reply, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return r; + + r = bus_print_property(name, reply, value, all); + if (r < 0) + return r; + if (r == 0) { + if (all) + printf("%s=[unprintable]\n", name); + /* skip what we didn't read */ + r = sd_bus_message_skip(reply, contents); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + } else { + r = sd_bus_message_skip(reply, "v"); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + + return 0; +} + +int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + sd_id128_t *p = userdata; + const void *v; + size_t n; + int r; + + r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n); + if (r < 0) + return r; + + if (n == 0) + *p = SD_ID128_NULL; + else if (n == 16) + memcpy((*p).bytes, v, n); + else + return -EINVAL; + + return 0; +} + +static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + char type; + int r; + + r = sd_bus_message_peek_type(m, &type, NULL); + if (r < 0) + return r; + + switch (type) { + case SD_BUS_TYPE_STRING: { + const char *s; + char **p = userdata; + + r = sd_bus_message_read_basic(m, type, &s); + if (r < 0) + break; + + if (isempty(s)) + break; + + r = free_and_strdup(p, s); + break; + } + + case SD_BUS_TYPE_ARRAY: { + _cleanup_strv_free_ char **l = NULL; + char ***p = userdata; + + r = bus_message_read_strv_extend(m, &l); + if (r < 0) + break; + + strv_free(*p); + *p = l; + l = NULL; + + break; + } + + case SD_BUS_TYPE_BOOLEAN: { + unsigned b; + int *p = userdata; + + r = sd_bus_message_read_basic(m, type, &b); + if (r < 0) + break; + + *p = b; + + break; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t u; + uint32_t *p = userdata; + + r = sd_bus_message_read_basic(m, type, &u); + if (r < 0) + break; + + *p = u; + + break; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t t; + uint64_t *p = userdata; + + r = sd_bus_message_read_basic(m, type, &t); + if (r < 0) + break; + + *p = t; + + break; + } + + case SD_BUS_TYPE_DOUBLE: { + double d; + double *p = userdata; + + r = sd_bus_message_read_basic(m, type, &d); + if (r < 0) + break; + + *p = d; + + break; + } + + default: + break; + } + + return r; +} + +int bus_message_map_all_properties( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(m); + assert(map); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const struct bus_properties_map *prop; + const char *member; + const char *contents; + void *v; + unsigned i; + + r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member); + if (r < 0) + return r; + + for (i = 0, prop = NULL; map[i].member; i++) + if (streq(map[i].member, member)) { + prop = &map[i]; + break; + } + + if (prop) { + r = sd_bus_message_peek_type(m, NULL, &contents); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return r; + + v = (uint8_t *)userdata + prop->offset; + if (map[i].set) + r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v); + else + r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } else { + r = sd_bus_message_skip(m, "v"); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + if (r < 0) + return r; + + return sd_bus_message_exit_container(m); +} + +int bus_message_map_properties_changed( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + + const char *member; + int r, invalidated, i; + + assert(m); + assert(map); + + r = bus_message_map_all_properties(m, map, userdata); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); + if (r < 0) + return r; + + invalidated = 0; + while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0) + for (i = 0; map[i].member; i++) + if (streq(map[i].member, member)) { + ++invalidated; + break; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return invalidated; +} + +int bus_map_all_properties( + sd_bus *bus, + const char *destination, + const char *path, + const struct bus_properties_map *map, + 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; + int r; + + assert(bus); + assert(destination); + assert(path); + assert(map); + + r = sd_bus_call_method( + bus, + destination, + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &m, + "s", ""); + if (r < 0) + return r; + + return bus_message_map_all_properties(m, map, userdata); +} + +int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { + int r; + + assert(transport >= 0); + assert(transport < _BUS_TRANSPORT_MAX); + assert(bus); + + assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); + assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); + + switch (transport) { + + case BUS_TRANSPORT_LOCAL: + if (user) + r = sd_bus_default_user(bus); + else + r = sd_bus_default_system(bus); + + break; + + case BUS_TRANSPORT_REMOTE: + r = sd_bus_open_system_remote(bus, host); + break; + + case BUS_TRANSPORT_MACHINE: + r = sd_bus_open_system_machine(bus, host); + break; + + default: + assert_not_reached("Hmm, unknown transport type."); + } + + return r; +} + +int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) { + int r; + + assert(transport >= 0); + assert(transport < _BUS_TRANSPORT_MAX); + assert(bus); + + assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); + assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); + + switch (transport) { + + case BUS_TRANSPORT_LOCAL: + if (user) + r = bus_connect_user_systemd(bus); + else + r = bus_connect_system_systemd(bus); + + break; + + case BUS_TRANSPORT_REMOTE: + r = sd_bus_open_system_remote(bus, host); + break; + + case BUS_TRANSPORT_MACHINE: + r = sd_bus_open_system_machine(bus, host); + break; + + default: + assert_not_reached("Hmm, unknown transport type."); + } + + return r; +} + +int bus_property_get_bool( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int b = *(bool*) userdata; + + return sd_bus_message_append_basic(reply, 'b', &b); +} + +#if __SIZEOF_SIZE_T__ != 8 +int bus_property_get_size( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t sz = *(size_t*) userdata; + + return sd_bus_message_append_basic(reply, 't', &sz); +} +#endif + +#if __SIZEOF_LONG__ != 8 +int bus_property_get_long( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int64_t l = *(long*) userdata; + + return sd_bus_message_append_basic(reply, 'x', &l); +} + +int bus_property_get_ulong( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t ul = *(unsigned long*) userdata; + + return sd_bus_message_append_basic(reply, 't', &ul); +} +#endif + +int bus_log_parse_error(int r) { + return log_error_errno(r, "Failed to parse bus message: %m"); +} + +int bus_log_create_error(int r) { + return log_error_errno(r, "Failed to create bus message: %m"); +} + +/** + * bus_path_encode_unique() - encode unique object path + * @b: bus connection or NULL + * @prefix: object path prefix + * @sender_id: unique-name of client, or NULL + * @external_id: external ID to be chosen by client, or NULL + * @ret_path: storage for encoded object path pointer + * + * Whenever we provide a bus API that allows clients to create and manage + * server-side objects, we need to provide a unique name for these objects. If + * we let the server choose the name, we suffer from a race condition: If a + * client creates an object asynchronously, it cannot destroy that object until + * it received the method reply. It cannot know the name of the new object, + * thus, it cannot destroy it. Furthermore, it enforces a round-trip. + * + * Therefore, many APIs allow the client to choose the unique name for newly + * created objects. There're two problems to solve, though: + * 1) Object names are usually defined via dbus object paths, which are + * usually globally namespaced. Therefore, multiple clients must be able + * to choose unique object names without interference. + * 2) If multiple libraries share the same bus connection, they must be + * able to choose unique object names without interference. + * The first problem is solved easily by prefixing a name with the + * unique-bus-name of a connection. The server side must enforce this and + * reject any other name. The second problem is solved by providing unique + * suffixes from within sd-bus. + * + * This helper allows clients to create unique object-paths. It uses the + * template '/prefix/sender_id/external_id' and returns the new path in + * @ret_path (must be freed by the caller). + * If @sender_id is NULL, the unique-name of @b is used. If @external_id is + * NULL, this function allocates a unique suffix via @b (by requesting a new + * cookie). If both @sender_id and @external_id are given, @b can be passed as + * NULL. + * + * Returns: 0 on success, negative error code on failure. + */ +int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) { + _cleanup_free_ char *sender_label = NULL, *external_label = NULL; + char external_buf[DECIMAL_STR_MAX(uint64_t)], *p; + int r; + + assert_return(b || (sender_id && external_id), -EINVAL); + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(ret_path, -EINVAL); + + if (!sender_id) { + r = sd_bus_get_unique_name(b, &sender_id); + if (r < 0) + return r; + } + + if (!external_id) { + xsprintf(external_buf, "%"PRIu64, ++b->cookie); + external_id = external_buf; + } + + sender_label = bus_label_escape(sender_id); + if (!sender_label) + return -ENOMEM; + + external_label = bus_label_escape(external_id); + if (!external_label) + return -ENOMEM; + + p = strjoin(prefix, "/", sender_label, "/", external_label, NULL); + if (!p) + return -ENOMEM; + + *ret_path = p; + return 0; +} + +/** + * bus_path_decode_unique() - decode unique object path + * @path: object path to decode + * @prefix: object path prefix + * @ret_sender: output parameter for sender-id label + * @ret_external: output parameter for external-id label + * + * This does the reverse of bus_path_encode_unique() (see its description for + * details). Both trailing labels, sender-id and external-id, are unescaped and + * returned in the given output parameters (the caller must free them). + * + * Note that this function returns 0 if the path does not match the template + * (see bus_path_encode_unique()), 1 if it matched. + * + * Returns: Negative error code on failure, 0 if the given object path does not + * match the template (return parameters are set to NULL), 1 if it was + * parsed successfully (return parameters contain allocated labels). + */ +int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) { + const char *p, *q; + char *sender, *external; + + assert(object_path_is_valid(path)); + assert(object_path_is_valid(prefix)); + assert(ret_sender); + assert(ret_external); + + p = object_path_startswith(path, prefix); + if (!p) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + q = strchr(p, '/'); + if (!q) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + sender = bus_label_unescape_n(p, q - p); + external = bus_label_unescape(q + 1); + if (!sender || !external) { + free(sender); + free(external); + return -ENOMEM; + } + + *ret_sender = sender; + *ret_external = external; + return 1; +} + +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/libshared/src/cgroup-show.c b/src/libshared/src/cgroup-show.c new file mode 100644 index 0000000000..e8dae923f5 --- /dev/null +++ b/src/libshared/src/cgroup-show.c @@ -0,0 +1,312 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/locale-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "shared/cgroup-show.h" +#include "shared/output-mode.h" + +static void show_pid_array( + pid_t pids[], + unsigned n_pids, + const char *prefix, + unsigned n_columns, + bool extra, + bool more, + OutputFlags flags) { + + unsigned i, j, pid_width; + + if (n_pids == 0) + return; + + qsort(pids, n_pids, sizeof(pid_t), pid_compare_func); + + /* Filter duplicates */ + for (j = 0, i = 1; i < n_pids; i++) { + if (pids[i] == pids[j]) + continue; + pids[++j] = pids[i]; + } + n_pids = j + 1; + pid_width = DECIMAL_STR_WIDTH(pids[j]); + + if (flags & OUTPUT_FULL_WIDTH) + n_columns = 0; + else { + if (n_columns > pid_width+2) + n_columns -= pid_width+2; + else + n_columns = 20; + } + for (i = 0; i < n_pids; i++) { + _cleanup_free_ char *t = NULL; + + get_process_cmdline(pids[i], n_columns, true, &t); + + if (extra) + printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET)); + else + printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT))); + + printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t)); + } +} + +static int show_cgroup_one_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + bool more, + OutputFlags flags) { + + char *fn; + _cleanup_fclose_ FILE *f = NULL; + size_t n = 0, n_allocated = 0; + _cleanup_free_ pid_t *pids = NULL; + _cleanup_free_ char *p = NULL; + pid_t pid; + int r; + + r = cg_mangle_path(path, &p); + if (r < 0) + return r; + + fn = strjoina(p, "/cgroup.procs"); + f = fopen(fn, "re"); + if (!f) + return -errno; + + while ((r = cg_read_pid(f, &pid)) > 0) { + + if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0) + continue; + + if (!GREEDY_REALLOC(pids, n_allocated, n + 1)) + return -ENOMEM; + + assert(n < n_allocated); + pids[n++] = pid; + } + + if (r < 0) + return r; + + show_pid_array(pids, n, prefix, n_columns, false, more, flags); + + return 0; +} + +int show_cgroup_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL; + _cleanup_closedir_ DIR *d = NULL; + char *gn = NULL; + bool shown_pids = false; + int r; + + assert(path); + + if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + r = cg_mangle_path(path, &fn); + if (r < 0) + return r; + + d = opendir(fn); + if (!d) + return -errno; + + while ((r = cg_read_subgroup(d, &gn)) > 0) { + _cleanup_free_ char *k = NULL; + + k = strjoin(fn, "/", gn, NULL); + free(gn); + if (!k) + return -ENOMEM; + + if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0) + continue; + + if (!shown_pids) { + show_cgroup_one_by_path(path, prefix, n_columns, true, flags); + shown_pids = true; + } + + if (last) { + printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last))); + + if (!p1) { + p1 = strappend(prefix, special_glyph(TREE_VERTICAL)); + if (!p1) + return -ENOMEM; + } + + show_cgroup_by_path(last, p1, n_columns-2, flags); + free(last); + } + + last = k; + k = NULL; + } + + if (r < 0) + return r; + + if (!shown_pids) + show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags); + + if (last) { + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last))); + + if (!p2) { + p2 = strappend(prefix, " "); + if (!p2) + return -ENOMEM; + } + + show_cgroup_by_path(last, p2, n_columns-2, flags); + } + + return 0; +} + +int show_cgroup(const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + _cleanup_free_ char *p = NULL; + int r; + + assert(path); + + r = cg_get_path(controller, path, NULL, &p); + if (r < 0) + return r; + + return show_cgroup_by_path(p, prefix, n_columns, flags); +} + +static int show_extra_pids( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + const pid_t pids[], + unsigned n_pids, + OutputFlags flags) { + + _cleanup_free_ pid_t *copy = NULL; + unsigned i, j; + int r; + + assert(path); + + if (n_pids <= 0) + return 0; + + if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + copy = new(pid_t, n_pids); + if (!copy) + return -ENOMEM; + + for (i = 0, j = 0; i < n_pids; i++) { + _cleanup_free_ char *k = NULL; + + r = cg_pid_get_path(controller, pids[i], &k); + if (r < 0) + return r; + + if (path_startswith(k, path)) + continue; + + copy[j++] = pids[i]; + } + + show_pid_array(copy, j, prefix, n_columns, true, false, flags); + + return 0; +} + +int show_cgroup_and_extra( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + + int r; + + assert(path); + + r = show_cgroup(controller, path, prefix, n_columns, flags); + if (r < 0) + return r; + + return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); +} + +int show_cgroup_and_extra_by_spec( + const char *spec, + const char *prefix, + unsigned n_columns, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + + _cleanup_free_ char *controller = NULL, *path = NULL; + int r; + + assert(spec); + + r = cg_split_spec(spec, &controller, &path); + if (r < 0) + return r; + + return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); +} diff --git a/src/libshared/src/clean-ipc.c b/src/libshared/src/clean-ipc.c new file mode 100644 index 0000000000..b9c60c48ae --- /dev/null +++ b/src/libshared/src/clean-ipc.c @@ -0,0 +1,365 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/clean-ipc.h" + +static int clean_sysvipc_shm(uid_t delete_uid) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + bool first = true; + int ret = 0; + + f = fopen("/proc/sysvipc/shm", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); + } + + FOREACH_LINE(line, f, goto fail) { + unsigned n_attached; + pid_t cpid, lpid; + uid_t uid, cuid; + gid_t gid, cgid; + int shmid; + + if (first) { + first = false; + continue; + } + + truncate_nl(line); + + if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, + &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8) + continue; + + if (n_attached > 0) + continue; + + if (uid != delete_uid) + continue; + + if (shmctl(shmid, IPC_RMID, NULL) < 0) { + + /* Ignore entries that are already deleted */ + if (errno == EIDRM || errno == EINVAL) + continue; + + ret = log_warning_errno(errno, + "Failed to remove SysV shared memory segment %i: %m", + shmid); + } + } + + return ret; + +fail: + return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); +} + +static int clean_sysvipc_sem(uid_t delete_uid) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + bool first = true; + int ret = 0; + + f = fopen("/proc/sysvipc/sem", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); + } + + FOREACH_LINE(line, f, goto fail) { + uid_t uid, cuid; + gid_t gid, cgid; + int semid; + + if (first) { + first = false; + continue; + } + + truncate_nl(line); + + if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, + &semid, &uid, &gid, &cuid, &cgid) != 5) + continue; + + if (uid != delete_uid) + continue; + + if (semctl(semid, 0, IPC_RMID) < 0) { + + /* Ignore entries that are already deleted */ + if (errno == EIDRM || errno == EINVAL) + continue; + + ret = log_warning_errno(errno, + "Failed to remove SysV semaphores object %i: %m", + semid); + } + } + + return ret; + +fail: + return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); +} + +static int clean_sysvipc_msg(uid_t delete_uid) { + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + bool first = true; + int ret = 0; + + f = fopen("/proc/sysvipc/msg", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); + } + + FOREACH_LINE(line, f, goto fail) { + uid_t uid, cuid; + gid_t gid, cgid; + pid_t cpid, lpid; + int msgid; + + if (first) { + first = false; + continue; + } + + truncate_nl(line); + + if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, + &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7) + continue; + + if (uid != delete_uid) + continue; + + if (msgctl(msgid, IPC_RMID, NULL) < 0) { + + /* Ignore entries that are already deleted */ + if (errno == EIDRM || errno == EINVAL) + continue; + + ret = log_warning_errno(errno, + "Failed to remove SysV message queue %i: %m", + msgid); + } + } + + return ret; + +fail: + return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); +} + +static int clean_posix_shm_internal(DIR *dir, uid_t uid) { + struct dirent *de; + int ret = 0, r; + + assert(dir); + + FOREACH_DIRENT(de, dir, goto fail) { + struct stat st; + + if (STR_IN_SET(de->d_name, "..", ".")) + continue; + + if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (errno == ENOENT) + continue; + + log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); + ret = -errno; + continue; + } + + if (st.st_uid != uid) + continue; + + if (S_ISDIR(st.st_mode)) { + _cleanup_closedir_ DIR *kid; + + kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); + if (!kid) { + if (errno != ENOENT) { + log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); + ret = -errno; + } + } else { + r = clean_posix_shm_internal(kid, uid); + if (r < 0) + ret = r; + } + + if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) { + + if (errno == ENOENT) + continue; + + log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); + ret = -errno; + } + } else { + + if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { + + if (errno == ENOENT) + continue; + + log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); + ret = -errno; + } + } + } + + return ret; + +fail: + log_warning_errno(errno, "Failed to read /dev/shm: %m"); + return -errno; +} + +static int clean_posix_shm(uid_t uid) { + _cleanup_closedir_ DIR *dir = NULL; + + dir = opendir("/dev/shm"); + if (!dir) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to open /dev/shm: %m"); + } + + return clean_posix_shm_internal(dir, uid); +} + +static int clean_posix_mq(uid_t uid) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *de; + int ret = 0; + + dir = opendir("/dev/mqueue"); + if (!dir) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); + } + + FOREACH_DIRENT(de, dir, goto fail) { + struct stat st; + char fn[1+strlen(de->d_name)+1]; + + if (STR_IN_SET(de->d_name, "..", ".")) + continue; + + if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (errno == ENOENT) + continue; + + ret = log_warning_errno(errno, + "Failed to stat() MQ segment %s: %m", + de->d_name); + continue; + } + + if (st.st_uid != uid) + continue; + + fn[0] = '/'; + strcpy(fn+1, de->d_name); + + if (mq_unlink(fn) < 0) { + if (errno == ENOENT) + continue; + + ret = log_warning_errno(errno, + "Failed to unlink POSIX message queue %s: %m", + fn); + } + } + + return ret; + +fail: + return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); +} + +int clean_ipc(uid_t uid) { + int ret = 0, r; + + /* Refuse to clean IPC of the root and system users */ + if (uid <= SYSTEM_UID_MAX) + return 0; + + r = clean_sysvipc_shm(uid); + if (r < 0) + ret = r; + + r = clean_sysvipc_sem(uid); + if (r < 0) + ret = r; + + r = clean_sysvipc_msg(uid); + if (r < 0) + ret = r; + + r = clean_posix_shm(uid); + if (r < 0) + ret = r; + + r = clean_posix_mq(uid); + if (r < 0) + ret = r; + + return ret; +} diff --git a/src/libshared/src/condition.c b/src/libshared/src/condition.c new file mode 100644 index 0000000000..12d5faf493 --- /dev/null +++ b/src/libshared/src/condition.c @@ -0,0 +1,542 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/audit-util.h" +#include "basic/cap-list.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/glob-util.h" +#include "basic/hostname-util.h" +#include "basic/list.h" +#include "basic/macro.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/apparmor-util.h" +#include "shared/condition.h" +#include "shared/ima-util.h" + +Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { + Condition *c; + int r; + + assert(type >= 0); + assert(type < _CONDITION_TYPE_MAX); + assert((!parameter) == (type == CONDITION_NULL)); + + c = new0(Condition, 1); + if (!c) + return NULL; + + c->type = type; + c->trigger = trigger; + c->negate = negate; + + r = free_and_strdup(&c->parameter, parameter); + if (r < 0) { + free(c); + return NULL; + } + + return c; +} + +void condition_free(Condition *c) { + assert(c); + + free(c->parameter); + free(c); +} + +Condition* condition_free_list(Condition *first) { + Condition *c, *n; + + LIST_FOREACH_SAFE(conditions, c, n, first) + condition_free(c); + + return NULL; +} + +static int condition_test_kernel_command_line(Condition *c) { + _cleanup_free_ char *line = NULL; + const char *p; + bool equal; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_KERNEL_COMMAND_LINE); + + r = proc_cmdline(&line); + if (r < 0) + return r; + + equal = !!strchr(c->parameter, '='); + p = line; + + for (;;) { + _cleanup_free_ char *word = NULL; + bool found; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); + if (r < 0) + return r; + if (r == 0) + break; + + if (equal) + found = streq(word, c->parameter); + else { + const char *f; + + f = startswith(word, c->parameter); + found = f && (*f == '=' || *f == 0); + } + + if (found) + return true; + } + + return false; +} + +static int condition_test_virtualization(Condition *c) { + int b, v; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_VIRTUALIZATION); + + v = detect_virtualization(); + if (v < 0) + return v; + + /* First, compare with yes/no */ + b = parse_boolean(c->parameter); + + if (v > 0 && b > 0) + return true; + + if (v == 0 && b == 0) + return true; + + /* Then, compare categorization */ + if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm")) + return true; + + if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container")) + return true; + + /* Finally compare id */ + return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v)); +} + +static int condition_test_architecture(Condition *c) { + int a, b; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_ARCHITECTURE); + + a = uname_architecture(); + if (a < 0) + return a; + + if (streq(c->parameter, "native")) + b = native_architecture(); + else { + b = architecture_from_string(c->parameter); + if (b < 0) /* unknown architecture? Then it's definitely not ours */ + return false; + } + + return a == b; +} + +static int condition_test_host(Condition *c) { + _cleanup_free_ char *h = NULL; + sd_id128_t x, y; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_HOST); + + if (sd_id128_from_string(c->parameter, &x) >= 0) { + + r = sd_id128_get_machine(&y); + if (r < 0) + return r; + + return sd_id128_equal(x, y); + } + + h = gethostname_malloc(); + if (!h) + return -ENOMEM; + + return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0; +} + +static int condition_test_ac_power(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_AC_POWER); + + r = parse_boolean(c->parameter); + if (r < 0) + return r; + + return (on_ac_power() != 0) == !!r; +} + +static int condition_test_security(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_SECURITY); + + if (streq(c->parameter, "selinux")) + return mac_selinux_have(); + if (streq(c->parameter, "smack")) + return mac_smack_use(); + if (streq(c->parameter, "apparmor")) + return mac_apparmor_use(); + if (streq(c->parameter, "audit")) + return use_audit(); + if (streq(c->parameter, "ima")) + return use_ima(); + + return false; +} + +static int condition_test_capability(Condition *c) { + _cleanup_fclose_ FILE *f = NULL; + int value; + char line[LINE_MAX]; + unsigned long long capabilities = -1; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_CAPABILITY); + + /* If it's an invalid capability, we don't have it */ + value = capability_from_name(c->parameter); + if (value < 0) + return -EINVAL; + + /* If it's a valid capability we default to assume + * that we have it */ + + f = fopen("/proc/self/status", "re"); + if (!f) + return -errno; + + while (fgets(line, sizeof(line), f)) { + truncate_nl(line); + + if (startswith(line, "CapBnd:")) { + (void) sscanf(line+7, "%llx", &capabilities); + break; + } + } + + return !!(capabilities & (1ULL << value)); +} + +static int condition_test_needs_update(Condition *c) { + const char *p; + struct stat usr, other; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_NEEDS_UPDATE); + + /* If the file system is read-only we shouldn't suggest an update */ + if (path_is_read_only_fs(c->parameter) > 0) + return false; + + /* Any other failure means we should allow the condition to be true, + * so that we rather invoke too many update tools than too + * few. */ + + if (!path_is_absolute(c->parameter)) + return true; + + p = strjoina(c->parameter, "/.updated"); + if (lstat(p, &other) < 0) + return true; + + if (lstat("/usr/", &usr) < 0) + return true; + + return usr.st_mtim.tv_sec > other.st_mtim.tv_sec || + (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec); +} + +static int condition_test_first_boot(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FIRST_BOOT); + + r = parse_boolean(c->parameter); + if (r < 0) + return r; + + return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r; +} + +static int condition_test_path_exists(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_EXISTS); + + return access(c->parameter, F_OK) >= 0; +} + +static int condition_test_path_exists_glob(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_EXISTS_GLOB); + + return glob_exists(c->parameter) > 0; +} + +static int condition_test_path_is_directory(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_DIRECTORY); + + return is_dir(c->parameter, true) > 0; +} + +static int condition_test_path_is_symbolic_link(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK); + + return is_symlink(c->parameter) > 0; +} + +static int condition_test_path_is_mount_point(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); + + return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0; +} + +static int condition_test_path_is_read_write(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_READ_WRITE); + + return path_is_read_only_fs(c->parameter) <= 0; +} + +static int condition_test_directory_not_empty(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY); + + r = dir_is_empty(c->parameter); + return r <= 0 && r != -ENOENT; +} + +static int condition_test_file_not_empty(Condition *c) { + struct stat st; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FILE_NOT_EMPTY); + + return (stat(c->parameter, &st) >= 0 && + S_ISREG(st.st_mode) && + st.st_size > 0); +} + +static int condition_test_file_is_executable(Condition *c) { + struct stat st; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FILE_IS_EXECUTABLE); + + return (stat(c->parameter, &st) >= 0 && + S_ISREG(st.st_mode) && + (st.st_mode & 0111)); +} + +static int condition_test_null(Condition *c) { + assert(c); + assert(c->type == CONDITION_NULL); + + /* Note that during parsing we already evaluate the string and + * store it in c->negate */ + return true; +} + +int condition_test(Condition *c) { + + static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = { + [CONDITION_PATH_EXISTS] = condition_test_path_exists, + [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob, + [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory, + [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link, + [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point, + [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write, + [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty, + [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, + [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, + [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, + [CONDITION_VIRTUALIZATION] = condition_test_virtualization, + [CONDITION_SECURITY] = condition_test_security, + [CONDITION_CAPABILITY] = condition_test_capability, + [CONDITION_HOST] = condition_test_host, + [CONDITION_AC_POWER] = condition_test_ac_power, + [CONDITION_ARCHITECTURE] = condition_test_architecture, + [CONDITION_NEEDS_UPDATE] = condition_test_needs_update, + [CONDITION_FIRST_BOOT] = condition_test_first_boot, + [CONDITION_NULL] = condition_test_null, + }; + + int r, b; + + assert(c); + assert(c->type >= 0); + assert(c->type < _CONDITION_TYPE_MAX); + + r = condition_tests[c->type](c); + if (r < 0) { + c->result = CONDITION_ERROR; + return r; + } + + b = (r > 0) == !c->negate; + c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED; + return b; +} + +void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { + assert(c); + assert(f); + + if (!prefix) + prefix = ""; + + fprintf(f, + "%s\t%s: %s%s%s %s\n", + prefix, + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + condition_result_to_string(c->result)); +} + +void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { + Condition *c; + + LIST_FOREACH(conditions, c, first) + condition_dump(c, f, prefix, to_string); +} + +static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { + [CONDITION_ARCHITECTURE] = "ConditionArchitecture", + [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", + [CONDITION_HOST] = "ConditionHost", + [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", + [CONDITION_SECURITY] = "ConditionSecurity", + [CONDITION_CAPABILITY] = "ConditionCapability", + [CONDITION_AC_POWER] = "ConditionACPower", + [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate", + [CONDITION_FIRST_BOOT] = "ConditionFirstBoot", + [CONDITION_PATH_EXISTS] = "ConditionPathExists", + [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", + [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", + [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", + [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", + [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", + [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", + [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", + [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", + [CONDITION_NULL] = "ConditionNull" +}; + +DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); + +static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { + [CONDITION_ARCHITECTURE] = "AssertArchitecture", + [CONDITION_VIRTUALIZATION] = "AssertVirtualization", + [CONDITION_HOST] = "AssertHost", + [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", + [CONDITION_SECURITY] = "AssertSecurity", + [CONDITION_CAPABILITY] = "AssertCapability", + [CONDITION_AC_POWER] = "AssertACPower", + [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate", + [CONDITION_FIRST_BOOT] = "AssertFirstBoot", + [CONDITION_PATH_EXISTS] = "AssertPathExists", + [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob", + [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory", + [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink", + [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint", + [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite", + [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", + [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", + [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", + [CONDITION_NULL] = "AssertNull" +}; + +DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); + +static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { + [CONDITION_UNTESTED] = "untested", + [CONDITION_SUCCEEDED] = "succeeded", + [CONDITION_FAILED] = "failed", + [CONDITION_ERROR] = "error", +}; + +DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult); diff --git a/src/libshared/src/conf-parser.c b/src/libshared/src/conf-parser.c new file mode 100644 index 0000000000..18a9b6332b --- /dev/null +++ b/src/libshared/src/conf-parser.c @@ -0,0 +1,914 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/syslog-util.h" +#include "basic/time-util.h" +#include "basic/utf8.h" +#include "shared/conf-parser.h" + +int config_item_table_lookup( + const void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + const ConfigTableItem *t; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + for (t = table; t->lvalue; t++) { + + if (!streq(lvalue, t->lvalue)) + continue; + + if (!streq_ptr(section, t->section)) + continue; + + *func = t->parse; + *ltype = t->ltype; + *data = t->data; + return 1; + } + + return 0; +} + +int config_item_perf_lookup( + const void *table, + const char *section, + const char *lvalue, + ConfigParserCallback *func, + int *ltype, + void **data, + void *userdata) { + + ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; + const ConfigPerfItem *p; + + assert(table); + assert(lvalue); + assert(func); + assert(ltype); + assert(data); + + if (!section) + p = lookup(lvalue, strlen(lvalue)); + else { + char *key; + + key = strjoin(section, ".", lvalue, NULL); + if (!key) + return -ENOMEM; + + p = lookup(key, strlen(key)); + free(key); + } + + if (!p) + return 0; + + *func = p->parse; + *ltype = p->ltype; + *data = (uint8_t*) userdata + p->offset; + return 1; +} + +/* Run the user supplied parser for an assignment */ +static int next_assignment(const char *unit, + const char *filename, + unsigned line, + ConfigItemLookup lookup, + const void *table, + const char *section, + unsigned section_line, + const char *lvalue, + const char *rvalue, + bool relaxed, + void *userdata) { + + ConfigParserCallback func = NULL; + int ltype = 0; + void *data = NULL; + int r; + + assert(filename); + assert(line > 0); + assert(lookup); + assert(lvalue); + assert(rvalue); + + r = lookup(table, section, lvalue, &func, <ype, &data, userdata); + if (r < 0) + return r; + + if (r > 0) { + if (func) + return func(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); + + return 0; + } + + /* Warn about unknown non-extension fields. */ + if (!relaxed && !startswith(lvalue, "X-")) + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section); + + return 0; +} + +/* Parse a variable assignment line */ +static int parse_line(const char* unit, + const char *filename, + unsigned line, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + bool allow_include, + char **section, + unsigned *section_line, + bool *section_ignored, + char *l, + void *userdata) { + + char *e; + + assert(filename); + assert(line > 0); + assert(lookup); + assert(l); + + l = strstrip(l); + + if (!*l) + return 0; + + if (strchr(COMMENTS "\n", *l)) + return 0; + + if (startswith(l, ".include ")) { + _cleanup_free_ char *fn = NULL; + + /* .includes are a bad idea, we only support them here + * for historical reasons. They create cyclic include + * problems and make it difficult to detect + * configuration file changes with an easy + * stat(). Better approaches, such as .d/ drop-in + * snippets exist. + * + * Support for them should be eventually removed. */ + + if (!allow_include) { + log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); + return 0; + } + + fn = file_in_same_dir(filename, strstrip(l+9)); + if (!fn) + return -ENOMEM; + + return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata); + } + + if (*l == '[') { + size_t k; + char *n; + + k = strlen(l); + assert(k > 0); + + if (l[k-1] != ']') { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); + return -EBADMSG; + } + + n = strndup(l+1, k-2); + if (!n) + return -ENOMEM; + + if (sections && !nulstr_contains(sections, n)) { + + if (!relaxed && !startswith(n, "X-")) + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); + + free(n); + *section = mfree(*section); + *section_line = 0; + *section_ignored = true; + } else { + free(*section); + *section = n; + *section_line = line; + *section_ignored = false; + } + + return 0; + } + + if (sections && !*section) { + + if (!relaxed && !*section_ignored) + 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, 0, "Missing '='."); + return -EINVAL; + } + + *e = 0; + e++; + + return next_assignment(unit, + filename, + line, + lookup, + table, + *section, + *section_line, + strstrip(l), + strstrip(e), + relaxed, + userdata); +} + +/* Go through the file and parse each line */ +int config_parse(const char *unit, + const char *filename, + FILE *f, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + bool allow_include, + bool warn, + void *userdata) { + + _cleanup_free_ char *section = NULL, *continuation = NULL; + _cleanup_fclose_ FILE *ours = NULL; + unsigned line = 0, section_line = 0; + bool section_ignored = false, allow_bom = true; + int r; + + assert(filename); + assert(lookup); + + if (!f) { + f = ours = fopen(filename, "re"); + if (!f) { + /* Only log on request, except for ENOENT, + * since we return 0 to the caller. */ + if (warn || errno == ENOENT) + log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR, + "Failed to open configuration file '%s': %m", filename); + return errno == ENOENT ? 0 : -errno; + } + } + + fd_warn_permissions(filename, fileno(f)); + + for (;;) { + char buf[LINE_MAX], *l, *p, *c = NULL, *e; + bool escaped = false; + + if (!fgets(buf, sizeof buf, f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read configuration file '%s': %m", filename); + } + + l = buf; + if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK)) + l += strlen(UTF8_BYTE_ORDER_MARK); + allow_bom = false; + + truncate_nl(l); + + if (continuation) { + c = strappend(continuation, l); + if (!c) { + if (warn) + log_oom(); + return -ENOMEM; + } + + continuation = mfree(continuation); + p = c; + } else + p = l; + + for (e = p; *e; e++) { + if (escaped) + escaped = false; + else if (*e == '\\') + escaped = true; + } + + if (escaped) { + *(e-1) = ' '; + + if (c) + continuation = c; + else { + continuation = strdup(l); + if (!continuation) { + if (warn) + log_oom(); + return -ENOMEM; + } + } + + continue; + } + + r = parse_line(unit, + filename, + ++line, + sections, + lookup, + table, + relaxed, + allow_include, + §ion, + §ion_line, + §ion_ignored, + p, + userdata); + free(c); + + if (r < 0) { + if (warn) + log_warning_errno(r, "Failed to parse file '%s': %m", + filename); + return r; + } + } + + return 0; +} + +/* Parse each config file in the specified directories. */ +int config_parse_many(const char *conf_file, + const char *conf_file_dirs, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata) { + _cleanup_strv_free_ char **files = NULL; + char **fn; + int r; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) + return r; + + if (conf_file) { + r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); + if (r < 0) + return r; + } + + STRV_FOREACH(fn, files) { + r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata); + if (r < 0) + return r; + } + + return 0; +} + +#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) { \ + \ + vartype *i = data; \ + int r; \ + \ + assert(filename); \ + assert(lvalue); \ + assert(rvalue); \ + assert(data); \ + \ + r = conv_func(rvalue, i); \ + if (r < 0) \ + log_syntax(unit, LOG_ERR, filename, line, r, \ + "Failed to parse %s value, ignoring: %s", \ + #type, rvalue); \ + \ + return 0; \ + } \ + 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, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = data; + uint64_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_size(rvalue, 1024, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + return 0; + } + + *sz = (size_t) v; + return 0; +} + +int config_parse_si_size(const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + size_t *sz = data; + uint64_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_size(rvalue, 1000, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + return 0; + } + + *sz = (size_t) v; + return 0; +} + +int config_parse_iec_uint64(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 *bytes = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_size(rvalue, 1024, bytes); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); + + return 0; +} + +int config_parse_bool(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 k; + bool *b = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); + return 0; + } + + *b = !!k; + return 0; +} + +int config_parse_tristate( + 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 k, *t = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* A tristate is pretty much a boolean, except that it can + * also take the special value -1, indicating "uninitialized", + * much like NULL is for a pointer type. */ + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); + return 0; + } + + *t = !!k; + return 0; +} + +int config_parse_string( + 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) { + + char **s = data, *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (!utf8_is_valid(rvalue)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + return 0; + } + + if (isempty(rvalue)) + n = NULL; + else { + n = strdup(rvalue); + if (!n) + return log_oom(); + } + + free(*s); + *s = n; + + return 0; +} + +int config_parse_path( + 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) { + + char **s = data, *n; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (!utf8_is_valid(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, 0, "Not an absolute path, ignoring: %s", rvalue); + return 0; + } + + n = strdup(rvalue); + if (!n) + return log_oom(); + + path_kill_slashes(n); + + free(*s); + *s = n; + + return 0; +} + +int config_parse_strv(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) { + + char ***sv = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + char **empty; + + /* Empty assignment resets the list. As a special rule + * we actually fill in a real empty array here rather + * than NULL, since some code wants to know if + * something was set at all... */ + empty = new0(char*, 1); + if (!empty) + return log_oom(); + + strv_free(*sv); + *sv = empty; + + return 0; + } + + for (;;) { + char *word = NULL; + + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES|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; + } + + if (!utf8_is_valid(word)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + free(word); + continue; + } + r = strv_consume(sv, word); + if (r < 0) + return log_oom(); + } + + return 0; +} + +int config_parse_log_facility( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + + int *o = data, x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + x = log_facility_unshifted_from_string(rvalue); + if (x < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue); + return 0; + } + + *o = (x << 3) | LOG_PRI(*o); + + return 0; +} + +int config_parse_log_level( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + + int *o = data, x; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + x = log_level_from_string(rvalue); + if (x < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue); + return 0; + } + + *o = (*o & LOG_FACMASK) | x; + return 0; +} + +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 *sig = data, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(sig); + + r = signal_from_string_try_harder(rvalue); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue); + return 0; + } + + *sig = r; + return 0; +} + +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) { + + unsigned long *personality = data, p; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(personality); + + p = personality_from_string(rvalue); + if (p == PERSONALITY_INVALID) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); + return 0; + } + + *personality = p; + return 0; +} + +int config_parse_ifname( + 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) { + + char **s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *s = mfree(*s); + return 0; + } + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(s, rvalue); + if (r < 0) + return log_oom(); + + return 0; +} diff --git a/src/libshared/src/dev-setup.c b/src/libshared/src/dev-setup.c new file mode 100644 index 0000000000..495099dc28 --- /dev/null +++ b/src/libshared/src/dev-setup.c @@ -0,0 +1,73 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/label.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/dev-setup.h" + +int dev_setup(const char *prefix, uid_t uid, gid_t gid) { + static const char symlinks[] = + "-/proc/kcore\0" "/dev/core\0" + "/proc/self/fd\0" "/dev/fd\0" + "/proc/self/fd/0\0" "/dev/stdin\0" + "/proc/self/fd/1\0" "/dev/stdout\0" + "/proc/self/fd/2\0" "/dev/stderr\0"; + + const char *j, *k; + int r; + + NULSTR_FOREACH_PAIR(j, k, symlinks) { + _cleanup_free_ char *link_name = NULL; + const char *n; + + if (j[0] == '-') { + j++; + + if (access(j, F_OK) < 0) + continue; + } + + if (prefix) { + link_name = prefix_root(prefix, k); + if (!link_name) + return -ENOMEM; + + n = link_name; + } else + n = k; + + r = symlink_label(j, n); + if (r < 0) + log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n); + + if (uid != UID_INVALID || gid != GID_INVALID) + if (lchown(n, uid, gid) < 0) + log_debug_errno(errno, "Failed to chown %s: %m", n); + } + + return 0; +} diff --git a/src/libshared/src/dns-domain.c b/src/libshared/src/dns-domain.c new file mode 100644 index 0000000000..4d9088e78e --- /dev/null +++ b/src/libshared/src/dns-domain.c @@ -0,0 +1,1322 @@ +/*** + 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 . + ***/ + +#ifdef HAVE_LIBIDN +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hashmap.h" +#include "basic/hexdecoct.h" +#include "basic/in-addr-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" +#include "shared/dns-domain.h" + +int dns_label_unescape(const char **name, char *dest, size_t sz) { + const char *n; + char *d; + int r = 0; + + assert(name); + assert(*name); + + n = *name; + d = dest; + + for (;;) { + if (*n == '.') { + n++; + break; + } + + if (*n == 0) + break; + + if (r >= DNS_LABEL_MAX) + return -EINVAL; + + if (sz <= 0) + return -ENOBUFS; + + if (*n == '\\') { + /* Escaped character */ + + n++; + + if (*n == 0) + /* Ending NUL */ + return -EINVAL; + + else if (*n == '\\' || *n == '.') { + /* Escaped backslash or dot */ + + if (d) + *(d++) = *n; + sz--; + r++; + n++; + + } else if (n[0] >= '0' && n[0] <= '9') { + unsigned k; + + /* Escaped literal ASCII character */ + + if (!(n[1] >= '0' && n[1] <= '9') || + !(n[2] >= '0' && n[2] <= '9')) + return -EINVAL; + + k = ((unsigned) (n[0] - '0') * 100) + + ((unsigned) (n[1] - '0') * 10) + + ((unsigned) (n[2] - '0')); + + /* 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; + + if (d) + *(d++) = (char) k; + sz--; + r++; + + n += 3; + } else + return -EINVAL; + + } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { + + /* Normal character */ + + if (d) + *(d++) = *n; + sz--; + r++; + n++; + } else + return -EINVAL; + } + + /* Empty label that is not at the end? */ + if (r == 0 && *n) + return -EINVAL; + + if (sz >= 1 && d) + *d = 0; + + *name = n; + return r; +} + +/* @label_terminal: terminal character of a label, updated to point to the terminal character of + * the previous label (always skipping one dot) or to NULL if there are no more + * labels. */ +int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { + const char *terminal; + int r; + + assert(name); + assert(label_terminal); + assert(dest); + + /* no more labels */ + if (!*label_terminal) { + if (sz >= 1) + *dest = 0; + + return 0; + } + + terminal = *label_terminal; + assert(*terminal == '.' || *terminal == 0); + + /* 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) */ + for (;;) { + if (terminal < name) { + /* Reached the first label, so indicate that there are no more */ + terminal = NULL; + break; + } + + /* Find the start of the last label */ + if (*terminal == '.') { + const char *y; + unsigned slashes = 0; + + for (y = terminal - 1; y >= name && *y == '\\'; y--) + slashes++; + + if (slashes % 2 == 0) { + /* The '.' was not escaped */ + name = terminal + 1; + break; + } else { + terminal = y; + continue; + } + } + + terminal--; + } + + r = dns_label_unescape(&name, dest, sz); + if (r < 0) + return r; + + *label_terminal = terminal; + + return r; +} + +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { + char *q; + + /* DNS labels must be between 1 and 63 characters long. A + * zero-length label does not exist. See RFC 2182, Section + * 11. */ + + if (l <= 0 || l > DNS_LABEL_MAX) + return -EINVAL; + if (sz < 1) + return -ENOBUFS; + + assert(p); + assert(dest); + + 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') || + (*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z')) { + + /* Proper character */ + + if (sz < 2) + return -ENOBUFS; + + *(q++) = *p; + 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); + + 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; + s = NULL; + + return r; +} + +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, l; + const char *p; + bool contains_8bit = false; + char buffer[DNS_LABEL_MAX+1]; + + assert(encoded); + assert(decoded); + + /* Converts an U-label into an A-label */ + + if (encoded_size <= 0) + return -EINVAL; + + for (p = encoded; p < encoded + encoded_size; p++) + if ((uint8_t) *p > 127) + contains_8bit = true; + + 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, buffer, 0) != 0) + return -EINVAL; + + l = strlen(buffer); + + /* Verify that 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 (int) l; +#else + return 0; +#endif +} + +int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { +#ifdef HAVE_LIBIDN + size_t input_size, output_size; + _cleanup_free_ uint32_t *input = NULL; + _cleanup_free_ char *result = NULL; + uint32_t *output = NULL; + size_t w; + + /* 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; + + if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0) + return 0; + + input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); + if (!input) + return -ENOMEM; + + output_size = input_size; + output = newa(uint32_t, output_size); + + idna_to_unicode_44i(input, input_size, output, &output_size, 0); + + result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); + if (!result) + return -ENOMEM; + if (w <= 0) + 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; + + return w; +#else + return 0; +#endif +} + +int dns_name_concat(const char *a, const char *b, char **_ret) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + const char *p; + bool first = true; + int r; + + if (a) + p = a; + else if (b) { + p = b; + b = NULL; + } else + goto finish; + + for (;;) { + char label[DNS_LABEL_MAX]; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) { + if (*p != 0) + return -EINVAL; + + if (b) { + /* Now continue with the second string, if there is one */ + p = b; + b = NULL; + continue; + } + + break; + } + + if (_ret) { + 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 { + char escaped[DNS_LABEL_ESCAPED_MAX]; + + r = dns_label_escape(label, r, escaped, sizeof(escaped)); + if (r < 0) + return r; + } + + if (!first) + n++; + else + first = false; + + n += r; + } + +finish: + if (n > DNS_HOSTNAME_MAX) + return -EINVAL; + + if (_ret) { + if (n == 0) { + /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ + if (!GREEDY_REALLOC(ret, allocated, 2)) + return -ENOMEM; + + ret[n++] = '.'; + } else { + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + } + + ret[n] = 0; + *_ret = ret; + ret = NULL; + } + + return 0; +} + +void dns_name_hash_func(const void *s, struct siphash *state) { + const char *p = s; + int r; + + assert(p); + + for (;;) { + char label[DNS_LABEL_MAX+1]; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + break; + if (r == 0) + break; + + 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 */ + } + + /* 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; + + assert(a); + assert(b); + + x = (const char *) a + strlen(a); + y = (const char *) b + strlen(b); + + for (;;) { + char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + + if (x == NULL && y == NULL) + return 0; + + r = dns_label_unescape_suffix(a, &x, la, sizeof(la)); + q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb)); + if (r < 0 || q < 0) + return r - q; + + r = ascii_strcasecmp_nn(la, r, lb, q); + if (r != 0) + return r; + } +} + +const struct hash_ops dns_name_hash_ops = { + .hash = dns_name_hash_func, + .compare = dns_name_compare_func +}; + +int dns_name_equal(const char *x, const char *y) { + int r, q; + + assert(x); + assert(y); + + for (;;) { + char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; + + r = dns_label_unescape(&x, la, sizeof(la)); + if (r < 0) + return r; + + q = dns_label_unescape(&y, lb, sizeof(lb)); + if (q < 0) + return q; + + 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; + + assert(name); + assert(suffix); + + n = name; + s = suffix; + + for (;;) { + char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; + + r = dns_label_unescape(&n, ln, sizeof(ln)); + if (r < 0) + return r; + + if (!saved_n) + saved_n = n; + + q = dns_label_unescape(&s, ls, sizeof(ls)); + if (q < 0) + return q; + + if (r == 0 && q == 0) + return true; + if (r == 0 && saved_n == n) + return false; + + 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; + n = saved_n; + saved_n = NULL; + } + } +} + +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; + + /* Determine if b is strictly greater than a and strictly smaller than c. + We consider the order of names to be circular, so that if a is + strictly greater than c, we consider b to be between them if it is + either greater than a or smaller than c. This is how the canonical + DNS name order used in NSEC records work. */ + + n = dns_name_compare_func(a, c); + if (n == 0) + return -EINVAL; + else if (n < 0) + /* a<---b--->c */ + return dns_name_compare_func(a, b) < 0 && + dns_name_compare_func(b, c) < 0; + else + /* <--b--c a--b--> */ + return dns_name_compare_func(b, c) < 0 || + dns_name_compare_func(a, b) < 0; +} + +int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { + const uint8_t *p; + int r; + + assert(a); + assert(ret); + + p = (const uint8_t*) a; + + if (family == AF_INET) + r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); + else if (family == AF_INET6) + r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", + hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), + hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), + hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), + hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), + hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), + hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), + hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), + hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); + else + return -EAFNOSUPPORT; + if (r < 0) + return -ENOMEM; + + return 0; +} + +int dns_name_address(const char *p, int *family, union in_addr_union *address) { + int r; + + assert(p); + assert(family); + assert(address); + + r = dns_name_endswith(p, "in-addr.arpa"); + if (r < 0) + return r; + if (r > 0) { + uint8_t a[4]; + unsigned i; + + for (i = 0; i < ELEMENTSOF(a); i++) { + char label[DNS_LABEL_MAX+1]; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + if (r > 3) + return -EINVAL; + + r = safe_atou8(label, &a[i]); + if (r < 0) + return r; + } + + r = dns_name_equal(p, "in-addr.arpa"); + if (r <= 0) + return r; + + *family = AF_INET; + address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | + ((uint32_t) a[2] << 16) | + ((uint32_t) a[1] << 8) | + (uint32_t) a[0]); + + return 1; + } + + r = dns_name_endswith(p, "ip6.arpa"); + if (r < 0) + return r; + if (r > 0) { + struct in6_addr a; + unsigned i; + + for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { + char label[DNS_LABEL_MAX+1]; + int x, y; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r <= 0) + return r; + if (r != 1) + return -EINVAL; + x = unhexchar(label[0]); + if (x < 0) + return -EINVAL; + + r = dns_label_unescape(&p, label, sizeof(label)); + if (r <= 0) + return r; + if (r != 1) + return -EINVAL; + y = unhexchar(label[0]); + if (y < 0) + return -EINVAL; + + a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; + } + + r = dns_name_equal(p, "ip6.arpa"); + if (r <= 0) + return r; + + *family = AF_INET6; + address->in6 = a; + return 1; + } + + return 0; +} + +bool dns_name_is_root(const char *name) { + + assert(name); + + /* There are exactly two ways to encode the root domain name: + * as empty string, or with a single dot. */ + + return STR_IN_SET(name, "", "."); +} + +bool dns_name_is_single_label(const char *name) { + int r; + + assert(name); + + 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; + + 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 = 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 . . */ + + 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; + + 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/libshared/src/dropin.c b/src/libshared/src/dropin.c new file mode 100644 index 0000000000..ed044e0ba4 --- /dev/null +++ b/src/libshared/src/dropin.c @@ -0,0 +1,251 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio-label.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "shared/dropin.h" + +int drop_in_file(const char *dir, const char *unit, unsigned level, + const char *name, char **_p, char **_q) { + + _cleanup_free_ char *b = NULL; + char *p, *q; + + char prefix[DECIMAL_STR_MAX(unsigned)]; + + assert(unit); + assert(name); + assert(_p); + assert(_q); + + sprintf(prefix, "%u", level); + + b = xescape(name, "/."); + if (!b) + return -ENOMEM; + + if (!filename_is_valid(b)) + return -EINVAL; + + p = strjoin(dir, "/", unit, ".d", NULL); + if (!p) + return -ENOMEM; + + q = strjoin(p, "/", prefix, "-", b, ".conf", NULL); + if (!q) { + free(p); + return -ENOMEM; + } + + *_p = p; + *_q = q; + return 0; +} + +int write_drop_in(const char *dir, const char *unit, unsigned level, + const char *name, const char *data) { + + _cleanup_free_ char *p = NULL, *q = NULL; + int r; + + assert(dir); + assert(unit); + assert(name); + assert(data); + + r = drop_in_file(dir, unit, level, name, &p, &q); + if (r < 0) + return r; + + (void) mkdir_p(p, 0755); + return write_string_file_atomic_label(q, data); +} + +int write_drop_in_format(const char *dir, const char *unit, unsigned level, + const char *name, const char *format, ...) { + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + assert(dir); + assert(unit); + assert(name); + assert(format); + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return write_drop_in(dir, unit, level, name, p); +} + +static int iterate_dir( + const char *path, + UnitDependency dependency, + dependency_consumer_t consumer, + void *arg, + char ***strv) { + + _cleanup_closedir_ DIR *d = NULL; + int r; + + assert(path); + + /* The config directories are special, since the order of the + * drop-ins matters */ + if (dependency < 0) { + r = strv_extend(strv, path); + if (r < 0) + return log_oom(); + + return 0; + } + + assert(consumer); + + d = opendir(path); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open directory %s: %m", path); + } + + for (;;) { + struct dirent *de; + _cleanup_free_ char *f = NULL; + + errno = 0; + de = readdir(d); + if (!de && errno > 0) + return log_error_errno(errno, "Failed to read directory %s: %m", path); + + if (!de) + break; + + if (hidden_or_backup_file(de->d_name)) + continue; + + f = strjoin(path, "/", de->d_name, NULL); + if (!f) + return log_oom(); + + r = consumer(dependency, de->d_name, f, arg); + if (r < 0) + return r; + } + + return 0; +} + +int unit_file_process_dir( + Set *unit_path_cache, + const char *unit_path, + const char *name, + const char *suffix, + UnitDependency dependency, + dependency_consumer_t consumer, + void *arg, + char ***strv) { + + _cleanup_free_ char *path = NULL; + int r; + + assert(unit_path); + assert(name); + assert(suffix); + + path = strjoin(unit_path, "/", name, suffix, NULL); + if (!path) + return log_oom(); + + if (!unit_path_cache || set_get(unit_path_cache, path)) + (void) iterate_dir(path, dependency, consumer, arg, strv); + + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { + _cleanup_free_ char *template = NULL, *p = NULL; + /* Also try the template dir */ + + r = unit_name_template(name, &template); + if (r < 0) + return log_error_errno(r, "Failed to generate template from unit name: %m"); + + p = strjoin(unit_path, "/", template, suffix, NULL); + if (!p) + return log_oom(); + + if (!unit_path_cache || set_get(unit_path_cache, p)) + (void) iterate_dir(p, dependency, consumer, arg, strv); + } + + return 0; +} + +int unit_file_find_dropin_paths( + char **lookup_path, + Set *unit_path_cache, + Set *names, + char ***paths) { + + _cleanup_strv_free_ char **strv = NULL, **ans = NULL; + Iterator i; + char *t; + int r; + + assert(paths); + + SET_FOREACH(t, names, i) { + char **p; + + STRV_FOREACH(p, lookup_path) + unit_file_process_dir(unit_path_cache, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, NULL, NULL, &strv); + } + + if (strv_isempty(strv)) + return 0; + + r = conf_files_list_strv(&ans, ".conf", NULL, (const char**) strv); + if (r < 0) + return log_warning_errno(r, "Failed to get list of configuration files: %m"); + + *paths = ans; + ans = NULL; + return 1; +} diff --git a/src/libshared/src/efivars.c b/src/libshared/src/efivars.c new file mode 100644 index 0000000000..41247eac03 --- /dev/null +++ b/src/libshared/src/efivars.c @@ -0,0 +1,715 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/time-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/efivars.h" + +#ifdef ENABLE_EFI + +#define LOAD_OPTION_ACTIVE 0x00000001 +#define MEDIA_DEVICE_PATH 0x04 +#define MEDIA_HARDDRIVE_DP 0x01 +#define MEDIA_FILEPATH_DP 0x04 +#define SIGNATURE_TYPE_GUID 0x02 +#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 + +struct boot_option { + uint32_t attr; + uint16_t path_len; + uint16_t title[]; +} _packed_; + +struct drive_path { + uint32_t part_nr; + uint64_t part_start; + uint64_t part_size; + char signature[16]; + uint8_t mbr_type; + uint8_t signature_type; +} _packed_; + +struct device_path { + uint8_t type; + uint8_t sub_type; + uint16_t length; + union { + uint16_t path[0]; + struct drive_path drive; + }; +} _packed_; + +bool is_efi_boot(void) { + return access("/sys/firmware/efi", F_OK) >= 0; +} + +static int read_flag(const char *varname) { + int r; + _cleanup_free_ void *v = NULL; + size_t s; + uint8_t b; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s); + if (r < 0) + return r; + + if (s != 1) + return -EINVAL; + + b = *(uint8_t *)v; + r = b > 0; + return r; +} + +bool is_efi_secure_boot(void) { + return read_flag("SecureBoot") > 0; +} + +bool is_efi_secure_boot_setup_mode(void) { + return read_flag("SetupMode") > 0; +} + +int efi_reboot_to_firmware_supported(void) { + int r; + size_t s; + uint64_t b; + _cleanup_free_ void *v = NULL; + + if (!is_efi_boot() || detect_container() > 0) + return -EOPNOTSUPP; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s); + if (r < 0) + return r; + else if (s != sizeof(uint64_t)) + return -EINVAL; + + b = *(uint64_t *)v; + b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + return b > 0 ? 0 : -EOPNOTSUPP; +} + +static int get_os_indications(uint64_t *os_indication) { + int r; + size_t s; + _cleanup_free_ void *v = NULL; + + r = efi_reboot_to_firmware_supported(); + if (r < 0) + return r; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s); + if (r == -ENOENT) { + /* Some firmware implementations that do support + * OsIndications and report that with + * OsIndicationsSupported will remove the + * OsIndications variable when it is unset. Let's + * pretend it's 0 then, to hide this implementation + * detail. Note that this call will return -ENOENT + * then only if the support for OsIndications is + * missing entirely, as determined by + * efi_reboot_to_firmware_supported() above. */ + *os_indication = 0; + return 0; + } else if (r < 0) + return r; + else if (s != sizeof(uint64_t)) + return -EINVAL; + + *os_indication = *(uint64_t *)v; + return 0; +} + +int efi_get_reboot_to_firmware(void) { + int r; + uint64_t b; + + r = get_os_indications(&b); + if (r < 0) + return r; + + return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI); +} + +int efi_set_reboot_to_firmware(bool value) { + int r; + uint64_t b, b_new; + + r = get_os_indications(&b); + if (r < 0) + return r; + + if (value) + b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + else + b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + + /* Avoid writing to efi vars store if we can due to firmware bugs. */ + if (b != b_new) + return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t)); + + return 0; +} + +int efi_get_variable( + sd_id128_t vendor, + const char *name, + uint32_t *attribute, + void **value, + size_t *size) { + + _cleanup_close_ int fd = -1; + _cleanup_free_ char *p = NULL; + uint32_t a; + ssize_t n; + struct stat st; + _cleanup_free_ void *buf = NULL; + + assert(name); + assert(value); + assert(size); + + if (asprintf(&p, + "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + name, SD_ID128_FORMAT_VAL(vendor)) < 0) + return -ENOMEM; + + fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + if (st.st_size < 4) + return -EIO; + if (st.st_size > 4*1024*1024 + 4) + return -E2BIG; + + n = read(fd, &a, sizeof(a)); + if (n < 0) + return -errno; + if (n != sizeof(a)) + return -EIO; + + buf = malloc(st.st_size - 4 + 2); + if (!buf) + return -ENOMEM; + + n = read(fd, buf, (size_t) st.st_size - 4); + if (n < 0) + return -errno; + if (n != (ssize_t) st.st_size - 4) + return -EIO; + + /* Always NUL terminate (2 bytes, to protect UTF-16) */ + ((char*) buf)[st.st_size - 4] = 0; + ((char*) buf)[st.st_size - 4 + 1] = 0; + + *value = buf; + buf = NULL; + *size = (size_t) st.st_size - 4; + + if (attribute) + *attribute = a; + + return 0; +} + +int efi_set_variable( + sd_id128_t vendor, + const char *name, + const void *value, + size_t size) { + + struct var { + uint32_t attr; + char buf[]; + } _packed_ * _cleanup_free_ buf = NULL; + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + + assert(name); + + if (asprintf(&p, + "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + name, SD_ID128_FORMAT_VAL(vendor)) < 0) + return -ENOMEM; + + if (size == 0) { + if (unlink(p) < 0) + return -errno; + return 0; + } + + fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644); + if (fd < 0) + return -errno; + + buf = malloc(sizeof(uint32_t) + size); + if (!buf) + return -ENOMEM; + + buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + memcpy(buf->buf, value, size); + + return loop_write(fd, buf, sizeof(uint32_t) + size, false); +} + +int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) { + _cleanup_free_ void *s = NULL; + size_t ss = 0; + int r; + char *x; + + r = efi_get_variable(vendor, name, NULL, &s, &ss); + if (r < 0) + return r; + + x = utf16_to_utf8(s, ss); + if (!x) + return -ENOMEM; + + *p = x; + return 0; +} + +static size_t utf16_size(const uint16_t *s) { + size_t l = 0; + + while (s[l] > 0) + l++; + + return (l+1) * sizeof(uint16_t); +} + +static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) { + struct uuid { + uint32_t u1; + uint16_t u2; + uint16_t u3; + uint8_t u4[8]; + } _packed_; + const struct uuid *uuid = guid; + + id128->bytes[0] = (uuid->u1 >> 24) & 0xff; + id128->bytes[1] = (uuid->u1 >> 16) & 0xff; + id128->bytes[2] = (uuid->u1 >> 8) & 0xff; + id128->bytes[3] = (uuid->u1) & 0xff; + id128->bytes[4] = (uuid->u2 >> 8) & 0xff; + id128->bytes[5] = (uuid->u2) & 0xff; + id128->bytes[6] = (uuid->u3 >> 8) & 0xff; + id128->bytes[7] = (uuid->u3) & 0xff; + memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4)); +} + +int efi_get_boot_option( + uint16_t id, + char **title, + sd_id128_t *part_uuid, + char **path, + bool *active) { + + char boot_id[9]; + _cleanup_free_ uint8_t *buf = NULL; + size_t l; + struct boot_option *header; + size_t title_size; + _cleanup_free_ char *s = NULL, *p = NULL; + sd_id128_t p_uuid = SD_ID128_NULL; + int r; + + xsprintf(boot_id, "Boot%04X", id); + r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l); + if (r < 0) + return r; + if (l < sizeof(struct boot_option)) + return -ENOENT; + + header = (struct boot_option *)buf; + title_size = utf16_size(header->title); + if (title_size > l - offsetof(struct boot_option, title)) + return -EINVAL; + + if (title) { + s = utf16_to_utf8(header->title, title_size); + if (!s) + return -ENOMEM; + } + + if (header->path_len > 0) { + uint8_t *dbuf; + size_t dnext; + + dbuf = buf + offsetof(struct boot_option, title) + title_size; + dnext = 0; + while (dnext < header->path_len) { + struct device_path *dpath; + + dpath = (struct device_path *)(dbuf + dnext); + if (dpath->length < 4) + break; + + /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ + if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) + break; + + dnext += dpath->length; + + /* Type 0x04 – Media Device Path */ + if (dpath->type != MEDIA_DEVICE_PATH) + continue; + + /* Sub-Type 1 – Hard Drive */ + if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { + /* 0x02 – GUID Partition Table */ + if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) + continue; + + /* 0x02 – GUID signature */ + if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) + continue; + + if (part_uuid) + efi_guid_to_id128(dpath->drive.signature, &p_uuid); + continue; + } + + /* Sub-Type 4 – File Path */ + if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) { + p = utf16_to_utf8(dpath->path, dpath->length-4); + efi_tilt_backslashes(p); + continue; + } + } + } + + if (title) { + *title = s; + s = NULL; + } + if (part_uuid) + *part_uuid = p_uuid; + if (path) { + *path = p; + p = NULL; + } + if (active) + *active = !!(header->attr & LOAD_OPTION_ACTIVE); + + return 0; +} + +static void to_utf16(uint16_t *dest, const char *src) { + int i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + dest[i] = '\0'; +} + +struct guid { + uint32_t u1; + uint16_t u2; + uint16_t u3; + uint8_t u4[8]; +} _packed_; + +static void id128_to_efi_guid(sd_id128_t id, void *guid) { + struct guid *uuid = guid; + + uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3]; + uuid->u2 = id.bytes[4] << 8 | id.bytes[5]; + uuid->u3 = id.bytes[6] << 8 | id.bytes[7]; + memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4)); +} + +static uint16_t *tilt_slashes(uint16_t *s) { + uint16_t *p; + + for (p = s; *p; p++) + if (*p == '/') + *p = '\\'; + + return s; +} + +int efi_add_boot_option(uint16_t id, const char *title, + uint32_t part, uint64_t pstart, uint64_t psize, + sd_id128_t part_uuid, const char *path) { + char boot_id[9]; + size_t size; + size_t title_len; + size_t path_len; + struct boot_option *option; + struct device_path *devicep; + _cleanup_free_ char *buf = NULL; + + title_len = (strlen(title)+1) * 2; + path_len = (strlen(path)+1) * 2; + + buf = calloc(sizeof(struct boot_option) + title_len + + sizeof(struct drive_path) + + sizeof(struct device_path) + path_len, 1); + if (!buf) + return -ENOMEM; + + /* header */ + option = (struct boot_option *)buf; + option->attr = LOAD_OPTION_ACTIVE; + option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + + offsetof(struct device_path, path) + path_len + + offsetof(struct device_path, path); + to_utf16(option->title, title); + size = offsetof(struct boot_option, title) + title_len; + + /* partition info */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_HARDDRIVE_DP; + devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); + devicep->drive.part_nr = part; + devicep->drive.part_start = pstart; + devicep->drive.part_size = psize; + devicep->drive.signature_type = SIGNATURE_TYPE_GUID; + devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + id128_to_efi_guid(part_uuid, devicep->drive.signature); + size += devicep->length; + + /* path to loader */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_FILEPATH_DP; + devicep->length = offsetof(struct device_path, path) + path_len; + to_utf16(devicep->path, path); + tilt_slashes(devicep->path); + size += devicep->length; + + /* end of path */ + devicep = (struct device_path *)(buf + size); + devicep->type = END_DEVICE_PATH_TYPE; + devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; + devicep->length = offsetof(struct device_path, path); + size += devicep->length; + + xsprintf(boot_id, "Boot%04X", id); + return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size); +} + +int efi_remove_boot_option(uint16_t id) { + char boot_id[9]; + + xsprintf(boot_id, "Boot%04X", id); + return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0); +} + +int efi_get_boot_order(uint16_t **order) { + _cleanup_free_ void *buf = NULL; + size_t l; + int r; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l); + if (r < 0) + return r; + + if (l <= 0) + return -ENOENT; + + if (l % sizeof(uint16_t) > 0 || + l / sizeof(uint16_t) > INT_MAX) + return -EINVAL; + + *order = buf; + buf = NULL; + return (int) (l / sizeof(uint16_t)); +} + +int efi_set_boot_order(uint16_t *order, size_t n) { + return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t)); +} + +static int boot_id_hex(const char s[4]) { + int i; + int id = 0; + + for (i = 0; i < 4; i++) + if (s[i] >= '0' && s[i] <= '9') + id |= (s[i] - '0') << (3 - i) * 4; + else if (s[i] >= 'A' && s[i] <= 'F') + id |= (s[i] - 'A' + 10) << (3 - i) * 4; + else + return -EINVAL; + + return id; +} + +static int cmp_uint16(const void *_a, const void *_b) { + const uint16_t *a = _a, *b = _b; + + return (int)*a - (int)*b; +} + +int efi_get_boot_options(uint16_t **options) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *de; + _cleanup_free_ uint16_t *list = NULL; + size_t alloc = 0; + int count = 0; + + assert(options); + + dir = opendir("/sys/firmware/efi/efivars/"); + if (!dir) + return -errno; + + FOREACH_DIRENT(de, dir, return -errno) { + int id; + + if (strncmp(de->d_name, "Boot", 4) != 0) + continue; + + if (strlen(de->d_name) != 45) + continue; + + if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0) + continue; + + id = boot_id_hex(de->d_name + 4); + if (id < 0) + continue; + + if (!GREEDY_REALLOC(list, alloc, count + 1)) + return -ENOMEM; + + list[count++] = id; + } + + qsort_safe(list, count, sizeof(uint16_t), cmp_uint16); + + *options = list; + list = NULL; + return count; +} + +static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) { + _cleanup_free_ char *j = NULL; + int r; + uint64_t x = 0; + + assert(name); + assert(u); + + r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j); + if (r < 0) + return r; + + r = safe_atou64(j, &x); + if (r < 0) + return r; + + *u = x; + return 0; +} + +int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { + uint64_t x, y; + int r; + + assert(firmware); + assert(loader); + + r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x); + if (r < 0) + return r; + + r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y); + if (r < 0) + return r; + + if (y == 0 || y < x) + return -EIO; + + if (y > USEC_PER_HOUR) + return -EIO; + + *firmware = x; + *loader = y; + + return 0; +} + +int efi_loader_get_device_part_uuid(sd_id128_t *u) { + _cleanup_free_ char *p = NULL; + int r, parsed[16]; + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p); + if (r < 0) + return r; + + if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + &parsed[0], &parsed[1], &parsed[2], &parsed[3], + &parsed[4], &parsed[5], &parsed[6], &parsed[7], + &parsed[8], &parsed[9], &parsed[10], &parsed[11], + &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) + return -EIO; + + if (u) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(parsed); i++) + u->bytes[i] = parsed[i]; + } + + return 0; +} + +#endif + +char *efi_tilt_backslashes(char *s) { + char *p; + + for (p = s; *p; p++) + if (*p == '\\') + *p = '/'; + + return s; +} diff --git a/src/libshared/src/fdset.c b/src/libshared/src/fdset.c new file mode 100644 index 0000000000..2988e4a9dc --- /dev/null +++ b/src/libshared/src/fdset.c @@ -0,0 +1,273 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "shared/fdset.h" + +#define MAKE_SET(s) ((Set*) s) +#define MAKE_FDSET(s) ((FDSet*) s) + +FDSet *fdset_new(void) { + return MAKE_FDSET(set_new(NULL)); +} + +int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) { + unsigned i; + FDSet *s; + int r; + + assert(ret); + + s = fdset_new(); + if (!s) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + + r = fdset_put(s, fds[i]); + if (r < 0) { + set_free(MAKE_SET(s)); + return r; + } + } + + *ret = s; + return 0; +} + +FDSet* fdset_free(FDSet *s) { + void *p; + + while ((p = set_steal_first(MAKE_SET(s)))) { + /* Valgrind's fd might have ended up in this set here, + * due to fdset_new_fill(). We'll ignore all failures + * here, so that the EBADFD that valgrind will return + * us on close() doesn't influence us */ + + /* When reloading duplicates of the private bus + * connection fds and suchlike are closed here, which + * has no effect at all, since they are only + * duplicates. So don't be surprised about these log + * messages. */ + + log_debug("Closing left-over fd %i", PTR_TO_FD(p)); + close_nointr(PTR_TO_FD(p)); + } + + set_free(MAKE_SET(s)); + return NULL; +} + +int fdset_put(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return set_put(MAKE_SET(s), FD_TO_PTR(fd)); +} + +int fdset_put_dup(FDSet *s, int fd) { + int copy, r; + + assert(s); + assert(fd >= 0); + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; + + r = fdset_put(s, copy); + if (r < 0) { + safe_close(copy); + return r; + } + + return copy; +} + +bool fdset_contains(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return !!set_get(MAKE_SET(s), FD_TO_PTR(fd)); +} + +int fdset_remove(FDSet *s, int fd) { + assert(s); + assert(fd >= 0); + + return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT; +} + +int fdset_new_fill(FDSet **_s) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + FDSet *s; + + assert(_s); + + /* Creates an fdset and fills in all currently open file + * descriptors. */ + + d = opendir("/proc/self/fd"); + if (!d) + return -errno; + + s = fdset_new(); + if (!s) { + r = -ENOMEM; + goto finish; + } + + while ((de = readdir(d))) { + int fd = -1; + + if (hidden_or_backup_file(de->d_name)) + continue; + + r = safe_atoi(de->d_name, &fd); + if (r < 0) + goto finish; + + if (fd < 3) + continue; + + if (fd == dirfd(d)) + continue; + + r = fdset_put(s, fd); + if (r < 0) + goto finish; + } + + r = 0; + *_s = s; + s = NULL; + +finish: + /* We won't close the fds here! */ + if (s) + set_free(MAKE_SET(s)); + + return r; +} + +int fdset_cloexec(FDSet *fds, bool b) { + Iterator i; + void *p; + int r; + + assert(fds); + + SET_FOREACH(p, MAKE_SET(fds), i) { + r = fd_cloexec(PTR_TO_FD(p), b); + if (r < 0) + return r; + } + + return 0; +} + +int fdset_new_listen_fds(FDSet **_s, bool unset) { + int n, fd, r; + FDSet *s; + + assert(_s); + + /* Creates an fdset and fills in all passed file descriptors */ + + s = fdset_new(); + if (!s) { + r = -ENOMEM; + goto fail; + } + + n = sd_listen_fds(unset); + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { + r = fdset_put(s, fd); + if (r < 0) + goto fail; + } + + *_s = s; + return 0; + + +fail: + if (s) + set_free(MAKE_SET(s)); + + return r; +} + +int fdset_close_others(FDSet *fds) { + void *e; + Iterator i; + int *a; + unsigned j, m; + + j = 0, m = fdset_size(fds); + a = alloca(sizeof(int) * m); + SET_FOREACH(e, MAKE_SET(fds), i) + a[j++] = PTR_TO_FD(e); + + assert(j == m); + + return close_all_fds(a, j); +} + +unsigned fdset_size(FDSet *fds) { + return set_size(MAKE_SET(fds)); +} + +bool fdset_isempty(FDSet *fds) { + return set_isempty(MAKE_SET(fds)); +} + +int fdset_iterate(FDSet *s, Iterator *i) { + void *p; + + if (!set_iterate(MAKE_SET(s), i, &p)) + return -ENOENT; + + return PTR_TO_FD(p); +} + +int fdset_steal_first(FDSet *fds) { + void *p; + + p = set_steal_first(MAKE_SET(fds)); + if (!p) + return -ENOENT; + + return PTR_TO_FD(p); +} diff --git a/src/libshared/src/fstab-util.c b/src/libshared/src/fstab-util.c new file mode 100644 index 0000000000..3f301c6fe3 --- /dev/null +++ b/src/libshared/src/fstab-util.c @@ -0,0 +1,263 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/device-nodes.h" +#include "basic/macro.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/fstab-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; + _cleanup_strv_free_ char **stor = NULL; + _cleanup_free_ char *v = NULL, **strv = NULL; + + assert(names && *names); + + if (!opts) + goto answer; + + /* If !value and !filtered, this function is not allowed to fail. */ + + if (!filtered) { + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, opts, ",", state) + NULSTR_FOREACH(name, names) { + if (l < strlen(name)) + continue; + if (!strneq(word, name, strlen(name))) + continue; + + /* we know that the string is NUL + * terminated, so *x is valid */ + x = word + strlen(name); + if (IN_SET(*x, '\0', '=', ',')) { + n = name; + if (value) { + free(v); + if (IN_SET(*x, '\0', ',')) + v = NULL; + else { + assert(*x == '='); + x++; + v = strndup(x, l - strlen(name) - 1); + if (!v) + return -ENOMEM; + } + } + } + } + } else { + char **t, **s; + + stor = strv_split(opts, ","); + if (!stor) + return -ENOMEM; + strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1)); + if (!strv) + return -ENOMEM; + + for (s = t = strv; *s; s++) { + NULSTR_FOREACH(name, names) { + x = startswith(*s, name); + if (x && IN_SET(*x, '\0', '=')) + goto found; + } + + *t = *s; + t++; + continue; + found: + /* Keep the last occurence found */ + n = name; + if (value) { + free(v); + if (*x == '\0') + v = NULL; + else { + assert(*x == '='); + x++; + v = strdup(x); + if (!v) + return -ENOMEM; + } + } + } + *t = NULL; + } + +answer: + if (namefound) + *namefound = n; + if (filtered) { + char *f; + + f = strv_join(strv, ","); + if (!f) + return -ENOMEM; + + *filtered = f; + } + if (value) { + *value = v; + v = NULL; + } + + return !!n; +} + +int fstab_extract_values(const char *opts, const char *name, char ***values) { + _cleanup_strv_free_ char **optsv = NULL, **res = NULL; + char **s; + + assert(opts); + assert(name); + assert(values); + + optsv = strv_split(opts, ","); + if (!optsv) + return -ENOMEM; + + STRV_FOREACH(s, optsv) { + char *arg; + int r; + + arg = startswith(*s, name); + if (!arg || *arg != '=') + continue; + r = strv_extend(&res, arg + 1); + if (r < 0) + return r; + } + + *values = res; + res = NULL; + + return !!*values; +} + +int fstab_find_pri(const char *options, int *ret) { + _cleanup_free_ char *opt = NULL; + int r; + unsigned pri; + + assert(ret); + + r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL); + if (r < 0) + return r; + if (r == 0 || !opt) + return 0; + + r = safe_atou(opt, &pri); + if (r < 0) + return r; + + if ((int) pri < 0) + return -ERANGE; + + *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/libshared/src/gcrypt-util.c b/src/libshared/src/gcrypt-util.c new file mode 100644 index 0000000000..069134a4be --- /dev/null +++ b/src/libshared/src/gcrypt-util.c @@ -0,0 +1,71 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_GCRYPT +#include + +#include "basic/hexdecoct.h" +#include "shared/gcrypt-util.h" + +void initialize_libgcrypt(bool secmem) { + const char *p; + if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) + return; + + p = gcry_check_version("1.4.5"); + assert(p); + + /* Turn off "secmem". Clients which wish to make use of this + * feature should initialize the library manually */ + if (!secmem) + gcry_control(GCRYCTL_DISABLE_SECMEM); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +} + +int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { + gcry_md_hd_t md = NULL; + size_t hash_size; + void *hash; + char *enc; + + initialize_libgcrypt(false); + + hash_size = gcry_md_get_algo_dlen(md_algorithm); + assert(hash_size > 0); + + gcry_md_open(&md, md_algorithm, 0); + if (!md) + return -EIO; + + gcry_md_write(md, s, len); + + hash = gcry_md_read(md, 0); + if (!hash) + return -EIO; + + enc = hexmem(hash, hash_size); + if (!enc) + return -ENOMEM; + + *out = enc; + return 0; +} +#endif diff --git a/src/libshared/src/generator.c b/src/libshared/src/generator.c new file mode 100644 index 0000000000..7e7c3fd78b --- /dev/null +++ b/src/libshared/src/generator.c @@ -0,0 +1,207 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/dropin.h" +#include "shared/fstab-util.h" +#include "shared/generator.h" + +static int write_fsck_sysroot_service(const char *dir, const char *what) { + _cleanup_free_ char *device = NULL, *escaped = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit; + int r; + + escaped = cescape(what); + if (!escaped) + return log_oom(); + + unit = strjoina(dir, "/systemd-fsck-root.service"); + log_debug("Creating %s", unit); + + r = unit_name_from_path(what, ".device", &device); + if (r < 0) + return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by %1$s\n\n" + "[Unit]\n" + "Documentation=man:systemd-fsck-root.service(8)\n" + "Description=File System Check on %2$s\n" + "DefaultDependencies=no\n" + "BindsTo=%3$s\n" + "After=initrd-root-device.target local-fs-pre.target\n" + "Before=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + what, + device, + escaped); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + return 0; +} + +int generator_write_fsck_deps( + FILE *f, + const char *dir, + const char *what, + const char *where, + const char *fstype) { + + int r; + + assert(f); + assert(dir); + assert(what); + assert(where); + + if (!is_device_path(what)) { + log_warning("Checking was requested for \"%s\", but it is not a device.", what); + return 0; + } + + if (!isempty(fstype) && !streq(fstype, "auto")) { + r = fsck_exists(fstype); + 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("Checking was requested for %s, but fsck.%s does not exist.", what, fstype); + return 0; + } + } + + if (path_equal(where, "/")) { + const char *lnk; + + lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); + + mkdir_parents(lnk, 0755); + if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + } else { + _cleanup_free_ char *_fsck = NULL; + const char *fsck; + + if (in_initrd() && path_equal(where, "/sysroot")) { + r = write_fsck_sysroot_service(dir, what); + if (r < 0) + return r; + + fsck = "systemd-fsck-root.service"; + } else { + r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck); + if (r < 0) + return log_error_errno(r, "Failed to create fsck service name: %m"); + + fsck = _fsck; + } + + fprintf(f, + "Requires=%1$s\n" + "After=%1$s\n", + fsck); + } + + return 0; +} + +int generator_write_timeouts( + const char *dir, + const char *what, + const char *where, + const char *opts, + char **filtered) { + + /* Allow configuration how long we wait for a device that + * backs a mount point to show up. This is useful to support + * endless device timeouts for devices that show up only after + * user input, like crypto devices. */ + + _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL; + usec_t u; + int r; + + r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" "x-systemd.device-timeout\0", + NULL, &timeout, filtered); + if (r <= 0) + return r; + + r = parse_sec(timeout, &u); + if (r < 0) { + log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); + return 0; + } + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + r = unit_name_from_path(node, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); + + return write_drop_in_format(dir, unit, 50, "device-timeout", + "# Automatically generated by %s\n\n" + "[Unit]\nJobTimeoutSec=%s", + program_invocation_short_name, timeout); +} + +int generator_write_initrd_root_device_deps(const char *dir, const char *what) { + _cleanup_free_ char *unit = NULL; + int r; + + r = unit_name_from_path(what, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); + + return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", + "# Automatically generated by %s\n\n" + "[Unit]\nRequires=%s\nAfter=%s", + program_invocation_short_name, unit, unit); +} diff --git a/src/libshared/src/ima-util.c b/src/libshared/src/ima-util.c new file mode 100644 index 0000000000..74fa5e280d --- /dev/null +++ b/src/libshared/src/ima-util.c @@ -0,0 +1,32 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "shared/ima-util.h" + +static int use_ima_cached = -1; + +bool use_ima(void) { + + if (use_ima_cached < 0) + use_ima_cached = access("/sys/kernel/security/ima/", F_OK) >= 0; + + return use_ima_cached; +} diff --git a/src/libshared/src/import-util.c b/src/libshared/src/import-util.c new file mode 100644 index 0000000000..286cc1398a --- /dev/null +++ b/src/libshared/src/import-util.c @@ -0,0 +1,185 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/import-util.h" + +int import_url_last_component(const char *url, char **ret) { + const char *e, *p; + char *s; + + e = strchrnul(url, '?'); + + while (e > url && e[-1] == '/') + e--; + + p = e; + while (p > url && p[-1] != '/') + p--; + + if (e <= p) + return -EINVAL; + + s = strndup(p, e - p); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + + +int import_url_change_last_component(const char *url, const char *suffix, char **ret) { + const char *e; + char *s; + + assert(url); + assert(ret); + + e = strchrnul(url, '?'); + + while (e > url && e[-1] == '/') + e--; + + while (e > url && e[-1] != '/') + e--; + + if (e <= url) + return -EINVAL; + + s = new(char, (e - url) + strlen(suffix) + 1); + if (!s) + return -ENOMEM; + + strcpy(mempcpy(s, url, e - url), suffix); + *ret = s; + return 0; +} + +static const char* const import_verify_table[_IMPORT_VERIFY_MAX] = { + [IMPORT_VERIFY_NO] = "no", + [IMPORT_VERIFY_CHECKSUM] = "checksum", + [IMPORT_VERIFY_SIGNATURE] = "signature", +}; + +DEFINE_STRING_TABLE_LOOKUP(import_verify, ImportVerify); + +int tar_strip_suffixes(const char *name, char **ret) { + const char *e; + char *s; + + e = endswith(name, ".tar"); + if (!e) + e = endswith(name, ".tar.xz"); + if (!e) + e = endswith(name, ".tar.gz"); + if (!e) + e = endswith(name, ".tar.bz2"); + if (!e) + e = endswith(name, ".tgz"); + if (!e) + e = strchr(name, 0); + + if (e <= name) + return -EINVAL; + + s = strndup(name, e - name); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int raw_strip_suffixes(const char *p, char **ret) { + + static const char suffixes[] = + ".xz\0" + ".gz\0" + ".bz2\0" + ".raw\0" + ".qcow2\0" + ".img\0" + ".bin\0"; + + _cleanup_free_ char *q = NULL; + + q = strdup(p); + if (!q) + return -ENOMEM; + + for (;;) { + const char *sfx; + bool changed = false; + + NULSTR_FOREACH(sfx, suffixes) { + char *e; + + e = endswith(q, sfx); + if (e) { + *e = 0; + changed = true; + } + } + + if (!changed) + break; + } + + *ret = q; + q = NULL; + + return 0; +} + +int import_assign_pool_quota_and_warn(const char *path) { + int r; + + 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 0; +} diff --git a/src/libshared/src/install-printf.c b/src/libshared/src/install-printf.c new file mode 100644 index 0000000000..2d62e3dfd2 --- /dev/null +++ b/src/libshared/src/install-printf.c @@ -0,0 +1,133 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/formats-util.h" +#include "basic/macro.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "shared/install-printf.h" +#include "shared/install.h" +#include "shared/specifier.h" + +static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { + UnitFileInstallInfo *i = userdata; + + assert(i); + + return unit_name_to_prefix_and_instance(i->name, ret); +} + +static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { + UnitFileInstallInfo *i = userdata; + + assert(i); + + return unit_name_to_prefix(i->name, ret); +} + +static int specifier_instance(char specifier, void *data, void *userdata, char **ret) { + UnitFileInstallInfo *i = userdata; + char *instance; + int r; + + assert(i); + + r = unit_name_to_instance(i->name, &instance); + if (r < 0) + return r; + + if (!instance) { + instance = strdup(""); + if (!instance) + return -ENOMEM; + } + + *ret = instance; + return 0; +} + +static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { + char *t; + + /* 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 = 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) { + + /* This is similar to unit_full_printf() but does not support + * anything path-related. + * + * %n: the full id of the unit (foo@bar.waldo) + * %N: the id of the unit without the suffix (foo@bar) + * %p: the prefix (foo) + * %i: the instance (bar) + + * %U the UID of the 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 + * %v `uname -r` of the running system + */ + + const Specifier table[] = { + { 'n', specifier_string, i->name }, + { 'N', specifier_prefix_and_instance, NULL }, + { 'p', specifier_prefix, NULL }, + { 'i', specifier_instance, NULL }, + + { 'U', specifier_user_id, NULL }, + { 'u', specifier_user_name, NULL }, + + { 'm', specifier_machine_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'v', specifier_kernel_release, NULL }, + {} + }; + + assert(i); + assert(format); + assert(ret); + + return specifier_printf(format, table, i, ret); +} diff --git a/src/libshared/src/install.c b/src/libshared/src/install.c new file mode 100644 index 0000000000..977fc2e486 --- /dev/null +++ b/src/libshared/src/install.c @@ -0,0 +1,2957 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/dirent-util.h" +#include "basic/extract-word.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/set.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "shared/conf-parser.h" +#include "shared/install-printf.h" +#include "shared/install.h" +#include "shared/path-lookup.h" + +#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 + +typedef enum SearchFlags { + SEARCH_LOAD = 1, + SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, +} SearchFlags; + +typedef struct { + OrderedHashmap *will_process; + OrderedHashmap *have_processed; +} InstallContext; + +typedef enum { + PRESET_UNKNOWN, + PRESET_ENABLE, + PRESET_DISABLE, +} PresetAction; + +typedef struct { + char *pattern; + PresetAction action; +} PresetRule; + +typedef struct { + PresetRule *rules; + size_t n_rules; +} Presets; + +static inline void presets_freep(Presets *p) { + size_t i; + + if (!p) + return; + + for (i = 0; i < p->n_rules; i++) + free(p->rules[i].pattern); + + free(p->rules); + p->n_rules = 0; +} + +static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); + +bool unit_type_may_alias(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_TIMER, + UNIT_PATH); +} + +bool unit_type_may_template(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_TIMER, + UNIT_PATH); +} + +static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { + [UNIT_FILE_TYPE_REGULAR] = "regular", + [UNIT_FILE_TYPE_SYMLINK] = "symlink", + [UNIT_FILE_TYPE_MASKED] = "masked", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); + +static int in_search_path(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + char **i; + + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + STRV_FOREACH(i, p->search_path) + if (path_equal(parent, *i)) + return true; + + return false; +} + +static const char* skip_root(const LookupPaths *p, const char *path) { + char *e; + + assert(p); + assert(path); + + if (!p->root_dir) + return path; + + e = path_startswith(path, p->root_dir); + if (!e) + return NULL; + + /* Make sure the returned path starts with a slash */ + if (e[0] != '/') { + if (e == path || e[-1] != '/') + return NULL; + + e--; + } + + return e; +} + +static int path_is_generator(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late); +} + +static int path_is_transient(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->transient); +} + +static int path_is_control(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->persistent_control) || + path_equal_ptr(parent, p->runtime_control); +} + +static int path_is_config(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + + assert(p); + assert(path); + + /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern + * configuration from transient or generated units */ + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->persistent_config) || + path_equal_ptr(parent, p->runtime_config); +} + +static int path_is_runtime(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + const char *rpath; + + assert(p); + assert(path); + + /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime + * directories, as safety net. */ + + rpath = skip_root(p, path); + if (rpath && path_startswith(rpath, "/run")) + return true; + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->runtime_config) || + path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late) || + path_equal_ptr(parent, p->transient) || + path_equal_ptr(parent, p->runtime_control); +} + +static int path_is_vendor(const LookupPaths *p, const char *path) { + const char *rpath; + + assert(p); + assert(path); + + rpath = skip_root(p, path); + if (!rpath) + return 0; + + if (path_startswith(rpath, "/usr")) + return true; + +#ifdef HAVE_SPLIT_USR + if (path_startswith(rpath, "/lib")) + return true; +#endif + + return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); +} + +int unit_file_changes_add( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + _cleanup_free_ char *p = NULL, *s = NULL; + UnitFileChange *c; + + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + *changes = c; + + p = strdup(path); + if (source) + s = strdup(source); + + if (!p || (source && !s)) + return -ENOMEM; + + path_kill_slashes(p); + if (s) + path_kill_slashes(s); + + c[*n_changes] = (UnitFileChange) { type, p, s }; + p = s = NULL; + (*n_changes) ++; + return 0; +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) { + unsigned i; + bool logged = false; + + assert(changes || n_changes == 0); + /* If verb is not specified, errors are not allowed! */ + assert(verb || r >= 0); + + for (i = 0; i < n_changes; i++) { + assert(verb || changes[i].type >= 0); + + switch(changes[i].type) { + case UNIT_FILE_SYMLINK: + if (!quiet) + log_info("Created symlink %s %s %s.", + changes[i].path, + special_glyph(ARROW), + changes[i].source); + break; + case UNIT_FILE_UNLINK: + if (!quiet) + log_info("Removed %s.", changes[i].path); + break; + case UNIT_FILE_IS_MASKED: + if (!quiet) + log_info("Unit %s is masked, ignoring.", changes[i].path); + break; + case UNIT_FILE_IS_DANGLING: + if (!quiet) + log_info("Unit %s is an alias to a unit that is not present, ignoring.", + changes[i].path); + break; + case -EEXIST: + if (changes[i].source) + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists and is a symlink to %s.", + verb, changes[i].path, changes[i].source); + else + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists.", + verb, changes[i].path); + logged = true; + break; + case -ERFKILL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.", + verb, changes[i].path); + logged = true; + break; + case -EADDRNOTAVAIL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.", + verb, changes[i].path); + logged = true; + break; + case -ELOOP: + log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s", + verb, changes[i].path); + logged = true; + break; + default: + assert(changes[i].type < 0); + log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", + verb, changes[i].path); + logged = true; + } + } + + if (r < 0 && !logged) + log_error_errno(r, "Failed to %s: %m.", verb); +} + +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. + * + * Returns 1 if a symlink was created or already exists and points to + * the right place, or negative on error. + */ + + 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 1; + } + + if (errno != EEXIST) { + unit_file_changes_add(changes, n_changes, -errno, new_path, NULL); + return -errno; + } + + r = readlink_malloc(new_path, &dest); + if (r < 0) { + /* translate EINVAL (non-symlink exists) to EEXIST */ + if (r == -EINVAL) + r = -EEXIST; + + unit_file_changes_add(changes, n_changes, r, new_path, NULL); + return r; + } + + if (path_equal(dest, old_path)) + return 1; + + if (!force) { + unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); + return -EEXIST; + } + + r = symlink_atomic(old_path, new_path); + if (r < 0) { + unit_file_changes_add(changes, n_changes, r, new_path, NULL); + 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 1; +} + +static int mark_symlink_for_removal( + Set **remove_symlinks_to, + const char *p) { + + char *n; + int r; + + assert(p); + + r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops); + if (r < 0) + return r; + + n = strdup(p); + if (!n) + return -ENOMEM; + + path_kill_slashes(n); + + r = set_consume(*remove_symlinks_to, n); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; + + return 1; +} + +static int remove_marked_symlinks_fd( + Set *remove_symlinks_to, + int fd, + const char *path, + const char *config_path, + const LookupPaths *lp, + bool *restart, + UnitFileChange **changes, + 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(lp); + assert(restart); + + d = fdopendir(fd); + if (!d) { + safe_close(fd); + return -errno; + } + + rewinddir(d); + + FOREACH_DIRENT(de, d, return -errno) { + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + _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) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + safe_close(nfd); + return -ENOMEM; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, 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; + const char *rp; + bool found; + int q; + + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) + continue; + + p = path_make_absolute(de->d_name, path); + if (!p) + return -ENOMEM; + path_kill_slashes(p); + + q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; + if (q < 0) { + if (r == 0) + r = q; + 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_contains(remove_symlinks_to, dest) || + set_contains(remove_symlinks_to, basename(dest)) || + set_contains(remove_symlinks_to, de->d_name); + + if (!found) + continue; + + if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { + if (r == 0) + r = -errno; + unit_file_changes_add(changes, n_changes, -errno, p, NULL); + continue; + } + + (void) rmdir_parents(p, config_path); + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + + /* Now, remember the full path (but with the root prefix removed) of + * the symlink we just removed, and remove any symlinks to it, too. */ + + rp = skip_root(lp, p); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); + if (q < 0) + return q; + if (q > 0) + *restart = true; + } + } + + return r; +} + +static int remove_marked_symlinks( + Set *remove_symlinks_to, + const char *config_path, + const LookupPaths *lp, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_close_ int fd = -1; + bool restart; + int r = 0; + + assert(config_path); + assert(lp); + + if (set_size(remove_symlinks_to) <= 0) + return 0; + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + do { + int q, cfd; + restart = false; + + cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + 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, lp, &restart, changes, n_changes); + if (r == 0) + r = q; + } 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, + const LookupPaths *lp, + bool *same_name_link) { + + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(name); + assert(fd >= 0); + assert(path); + assert(config_path); + assert(lp); + assert(same_name_link); + + d = fdopendir(fd); + if (!d) { + safe_close(fd); + return -errno; + } + + FOREACH_DIRENT(de, d, return -errno) { + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + _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) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + safe_close(nfd); + return -ENOMEM; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); + if (q > 0) + return 1; + if (r == 0) + r = q; + + } else if (de->d_type == DT_LNK) { + _cleanup_free_ char *p = NULL, *dest = NULL; + bool found_path, found_dest, b = false; + int q; + + /* Acquire symlink name */ + p = path_make_absolute(de->d_name, path); + if (!p) + return -ENOMEM; + + /* Acquire symlink destination */ + q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; + if (q < 0) { + 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)) + found_path = path_equal(p, name); + else + found_path = streq(de->d_name, name); + + /* Check if what the symlink points to + * matches what we are looking for */ + if (path_is_absolute(name)) + found_dest = path_equal(dest, name); + else + found_dest = streq(basename(dest), name); + + if (found_path && found_dest) { + _cleanup_free_ char *t = NULL; + + /* Filter out same name links in the main + * config path */ + t = path_make_absolute(name, config_path); + if (!t) + return -ENOMEM; + + b = path_equal(t, p); + } + + if (b) + *same_name_link = true; + else if (found_path || found_dest) + return 1; + } + } + + return r; +} + +static int find_symlinks( + const char *root_dir, + const char *name, + const char *config_path, + const LookupPaths *lp, + bool *same_name_link) { + + int fd; + + assert(name); + assert(config_path); + assert(same_name_link); + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) { + if (IN_SET(errno, ENOENT, ENOTDIR, EACCES)) + return 0; + return -errno; + } + + /* This takes possession of fd and closes it */ + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); +} + +static int find_symlinks_in_scope( + UnitFileScope scope, + const LookupPaths *paths, + const char *name, + UnitFileState *state) { + + bool same_name_link_runtime = false, same_name_link = false; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(paths); + assert(name); + + /* First look in the persistent config path */ + r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); + if (r < 0) + return r; + if (r > 0) { + *state = UNIT_FILE_ENABLED; + return r; + } + + /* Then look in runtime config path */ + r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); + if (r < 0) + return r; + 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) { + *state = UNIT_FILE_LINKED; + return 1; + } + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; + } + + return 0; +} + +static void install_info_free(UnitFileInstallInfo *i) { + + if (!i) + return; + + free(i->name); + free(i->path); + strv_free(i->aliases); + strv_free(i->wanted_by); + strv_free(i->required_by); + strv_free(i->also); + free(i->default_instance); + free(i->symlink_target); + free(i); +} + +static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { + UnitFileInstallInfo *i; + + if (!m) + return NULL; + + while ((i = ordered_hashmap_steal_first(m))) + install_info_free(i); + + return ordered_hashmap_free(m); +} + +static void install_context_done(InstallContext *c) { + assert(c); + + c->will_process = install_info_hashmap_free(c->will_process); + c->have_processed = install_info_hashmap_free(c->have_processed); +} + +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_may_process( + UnitFileInstallInfo *i, + const LookupPaths *paths, + UnitFileChange **changes, + unsigned *n_changes) { + assert(i); + assert(paths); + + /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus + * not subject to enable/disable operations. */ + + if (i->type == UNIT_FILE_TYPE_MASKED) { + unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); + return -ERFKILL; + } + if (path_is_generator(paths, i->path) || + path_is_transient(paths, i->path)) { + unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static int install_info_add( + InstallContext *c, + const char *name, + const char *path, + UnitFileInstallInfo **ret) { + + UnitFileInstallInfo *i = NULL; + int r; + + assert(c); + assert(name || path); + + if (!name) + name = basename(path); + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + i = install_info_find(c, name); + if (i) { + if (ret) + *ret = i; + return 0; + } + + 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) { + r = -ENOMEM; + goto fail; + } + + if (path) { + i->path = strdup(path); + if (!i->path) { + r = -ENOMEM; + goto fail; + } + } + + r = ordered_hashmap_put(c->will_process, i->name, i); + if (r < 0) + goto fail; + + if (ret) + *ret = i; + + return 0; + +fail: + install_info_free(i); + return r; +} + +static int config_parse_alias( + 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 *name; + UnitType type; + + assert(filename); + assert(lvalue); + assert(rvalue); + + name = basename(filename); + type = unit_name_to_type(name); + if (!unit_type_may_alias(type)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "Aliases are not allowed for %s units, ignoring.", + unit_type_to_string(type)); + + return config_parse_strv(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); +} + +static int config_parse_also( + 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 = userdata; + InstallContext *c = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + r = install_info_add(c, word, NULL, NULL); + if (r < 0) + return r; + + r = strv_push(&i->also, word); + if (r < 0) + return r; + + word = NULL; + } + + return 0; +} + +static int config_parse_default_instance( + 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; + const char *name; + char *printed; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + name = basename(filename); + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) + /* When enabling an instance, we might be using a template unit file, + * but we should ignore DefaultInstance silently. */ + return 0; + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "DefaultInstance only makes sense for template units, ignoring."); + + r = install_full_printf(i, rvalue, &printed); + if (r < 0) + return r; + + if (!unit_instance_is_valid(printed)) { + free(printed); + return -EINVAL; + } + + free(i->default_instance); + i->default_instance = printed; + + return 0; +} + +static int unit_file_load( + InstallContext *c, + UnitFileInstallInfo *info, + const char *path, + SearchFlags flags) { + + const ConfigTableItem items[] = { + { "Install", "Alias", config_parse_alias, 0, &info->aliases }, + { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, + { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, + { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, + { "Install", "Also", config_parse_also, 0, c }, + {} + }; + + const char *name; + UnitType type; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + struct stat st; + int r; + + assert(c); + assert(info); + assert(path); + + name = basename(path); + type = unit_name_to_type(name); + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && + !unit_type_may_template(type)) + return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); + + 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|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) + return -errno; + fd = -1; + + r = config_parse(NULL, path, f, + NULL, + config_item_table_lookup, items, + true, true, false, info); + if (r < 0) + return r; + + info->type = UNIT_FILE_TYPE_REGULAR; + + return + (int) strv_length(info->aliases) + + (int) strv_length(info->wanted_by) + + (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 *target = NULL; + int r; + + r = unit_file_load(c, info, path, flags); + if (r != -ELOOP) + return r; + + /* This is a symlink, let's read it. */ + + r = readlink_malloc(path, &target); + if (r < 0) + return r; + + if (path_equal(target, "/dev/null")) + info->type = UNIT_FILE_TYPE_MASKED; + else { + const char *bn; + UnitType a, b; + + bn = basename(target); + + 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; + + if (path_is_absolute(target)) + /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ + info->symlink_target = prefix_root(root_dir, target); + else + /* This is a relative path, take it relative to the dir the symlink is located in. */ + info->symlink_target = file_in_same_dir(path, target); + if (!info->symlink_target) + return -ENOMEM; + + info->type = UNIT_FILE_TYPE_SYMLINK; + } + + return 0; +} + +static int unit_file_search( + InstallContext *c, + UnitFileInstallInfo *info, + const LookupPaths *paths, + SearchFlags flags) { + + _cleanup_free_ char *template = NULL; + char **p; + int r; + + assert(c); + 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_or_readlink(c, info, info->path, paths->root_dir, flags); + + assert(info->name); + + STRV_FOREACH(p, paths->search_path) { + _cleanup_free_ char *path = NULL; + + path = strjoin(*p, "/", info->name, NULL); + if (!path) + return -ENOMEM; + + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + if (r >= 0) { + info->path = path; + path = NULL; + return r; + } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) + return r; + } + + if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + /* Unit file doesn't exist, however instance + * enablement was requested. We will check if it is + * possible to load template unit file. */ + + r = unit_name_template(info->name, &template); + if (r < 0) + return r; + + STRV_FOREACH(p, paths->search_path) { + _cleanup_free_ char *path = NULL; + + path = strjoin(*p, "/", template, NULL); + if (!path) + return -ENOMEM; + + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + if (r >= 0) { + info->path = path; + path = NULL; + return r; + } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) + return r; + } + } + + log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); + return -ENOENT; +} + +static int install_info_follow( + InstallContext *c, + UnitFileInstallInfo *i, + const char *root_dir, + 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); +} + +/** + * Search for the unit file. If the unit name is a symlink, + * follow the symlink to the target, maybe more than once. + * Propagate the instance name if present. + */ +static int install_info_traverse( + UnitFileScope scope, + InstallContext *c, + const LookupPaths *paths, + UnitFileInstallInfo *start, + SearchFlags flags, + UnitFileInstallInfo **ret) { + + UnitFileInstallInfo *i; + unsigned k = 0; + int r; + + assert(paths); + assert(start); + assert(c); + + r = unit_file_search(c, start, paths, flags); + if (r < 0) + return r; + + i = start; + while (i->type == UNIT_FILE_TYPE_SYMLINK) { + /* Follow the symlink */ + + if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) + return -ELOOP; + + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { + r = path_is_config(paths, i->path); + if (r < 0) + return r; + if (r > 0) + return -ELOOP; + } + + r = install_info_follow(c, i, paths->root_dir, flags); + if (r == -EXDEV) { + _cleanup_free_ char *buffer = NULL; + const char *bn; + + /* Target has a different name, create a new + * install info object for that, and continue + * with that. */ + + bn = basename(i->symlink_target); + + if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && + unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { + + _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; + + /* Try again, with the new target we found. */ + r = unit_file_search(c, i, paths, flags); + if (r == -ENOENT) + /* Translate error code to highlight this specific case */ + return -ENOLINK; + } + + if (r < 0) + return r; + } + + if (ret) + *ret = i; + + return 0; +} + +static int install_info_add_auto( + InstallContext *c, + const LookupPaths *paths, + const char *name_or_path, + UnitFileInstallInfo **ret) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) { + const char *pp; + + pp = prefix_roota(paths->root_dir, name_or_path); + + return install_info_add(c, NULL, pp, ret); + } else + return install_info_add(c, name_or_path, NULL, ret); +} + +static int install_info_discover( + UnitFileScope scope, + InstallContext *c, + const LookupPaths *paths, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret) { + + UnitFileInstallInfo *i; + int r; + + assert(c); + assert(paths); + assert(name); + + r = install_info_add_auto(c, paths, name, &i); + if (r < 0) + return r; + + return install_info_traverse(scope, c, paths, i, flags, ret); +} + +static int install_info_symlink_alias( + UnitFileInstallInfo *i, + const LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **s; + int r = 0, q; + + assert(i); + assert(paths); + assert(config_path); + + STRV_FOREACH(s, i->aliases) { + _cleanup_free_ char *alias_path = NULL, *dst = NULL; + const char *rp; + + q = install_full_printf(i, *s, &dst); + if (q < 0) + return q; + + alias_path = path_make_absolute(dst, config_path); + if (!alias_path) + return -ENOMEM; + + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_wants( + UnitFileInstallInfo *i, + const LookupPaths *paths, + const char *config_path, + char **list, + const char *suffix, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *buf = NULL; + const char *n; + char **s; + int r = 0, q; + + assert(i); + assert(paths); + assert(config_path); + + if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { + + /* Don't install any symlink if there's no default + * instance configured */ + + if (!i->default_instance) + return 0; + + r = unit_name_replace_instance(i->name, i->default_instance, &buf); + if (r < 0) + return r; + + n = buf; + } else + n = i->name; + + STRV_FOREACH(s, list) { + _cleanup_free_ char *path = NULL, *dst = NULL; + const char *rp; + + q = install_full_printf(i, *s, &dst); + if (q < 0) + return q; + + if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) { + r = -EINVAL; + continue; + } + + path = strjoin(config_path, "/", dst, suffix, n, NULL); + if (!path) + return -ENOMEM; + + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, path, true, changes, n_changes); + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_link( + UnitFileInstallInfo *i, + const LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *path = NULL; + const char *rp; + int r; + + assert(i); + assert(paths); + assert(config_path); + assert(i->path); + + r = in_search_path(paths, i->path); + if (r < 0) + return r; + if (r > 0) + return 0; + + path = strjoin(config_path, "/", i->name, NULL); + if (!path) + return -ENOMEM; + + rp = skip_root(paths, i->path); + + return create_symlink(rp ?: i->path, path, force, changes, n_changes); +} + +static int install_info_apply( + UnitFileInstallInfo *i, + const LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r, q; + + assert(i); + assert(paths); + assert(config_path); + + if (i->type != UNIT_FILE_TYPE_REGULAR) + return 0; + + r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); + + q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + /* Do not count links to the unit file towards the "carries_install_info" count */ + if (r == 0 && q < 0) + r = q; + + return r; +} + +static int install_context_apply( + UnitFileScope scope, + InstallContext *c, + const LookupPaths *paths, + const char *config_path, + bool force, + SearchFlags flags, + UnitFileChange **changes, + unsigned *n_changes) { + + UnitFileInstallInfo *i; + int r; + + assert(c); + assert(paths); + assert(config_path); + + 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; + + r = 0; + while ((i = ordered_hashmap_first(c->will_process))) { + int 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, paths, i, flags, NULL); + if (r < 0) + return r; + + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; + + q = install_info_apply(i, paths, config_path, force, changes, n_changes); + 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, + const char *config_path) { + + UnitFileInstallInfo *i; + int r; + + assert(c); + assert(paths); + assert(config_path); + + /* Marks all items for removal */ + + 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 = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + if (r == -ENOLINK) + return 0; + else if (r < 0) + return r; + + if (i->type != UNIT_FILE_TYPE_REGULAR) { + log_debug("Unit %s has type %s, ignoring.", + i->name, + unit_file_type_to_string(i->type) ?: "invalid"); + continue; + } + + r = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r < 0) + return r; + } + + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + const char *config_path; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + 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, config_path); + 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_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + const char *config_path; + char **i; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + 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; + const char *rp; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + if (unlink(path) < 0) { + if (errno != ENOENT) { + if (r >= 0) + r = -errno; + unit_file_changes_add(changes, n_changes, -errno, path, NULL); + } + + continue; + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + rp = skip_root(&paths, path); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); + if (r >= 0) + r = q; + + 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 = {}; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + const char *config_path; + char **i; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *full = NULL; + struct stat st; + char *fn; + + if (!path_is_absolute(*i)) + return -EINVAL; + + fn = basename(*i); + if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) + return -EINVAL; + + full = prefix_root(paths.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(&paths, *i); + if (q < 0) + return q; + if (q > 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 *new_path = NULL; + const char *old_path; + + old_path = skip_root(&paths, *i); + new_path = path_make_absolute(basename(*i), config_path); + if (!new_path) + return -ENOMEM; + + q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); + if (q < 0 && r >= 0) + r = q; + } + + return r; +} + +static int path_shall_revert(const LookupPaths *paths, const char *path) { + int r; + + assert(paths); + assert(path); + + /* Checks whether the path is one where the drop-in directories shall be removed. */ + + r = path_is_config(paths, path); + if (r != 0) + return r; + + r = path_is_control(paths, path); + if (r != 0) + return r; + + return path_is_transient(paths, path); +} + +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + /* _cleanup_(install_context_done) InstallContext c = {}; */ + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + /* Puts a unit file back into vendor state. This means: + * + * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and + * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). + * + * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in + * "config", but not in "transient" or "control" or even "generated"). + * + * We remove all that in both the runtime and the persistent directories, if that applies. + */ + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + bool has_vendor = false; + char **p; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL, *dropin = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode)) { + /* Check if there's a vendor version */ + r = path_is_vendor(&paths, path); + if (r < 0) + return r; + if (r > 0) + has_vendor = true; + } + + dropin = strappend(path, ".d"); + if (!dropin) + return -ENOMEM; + + r = lstat(dropin, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISDIR(st.st_mode)) { + /* Remove the drop-ins */ + r = path_shall_revert(&paths, dropin); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = dropin; + dropin = NULL; + } + } + } + + if (!has_vendor) + continue; + + /* OK, there's a vendor version, hence drop all configuration versions */ + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + r = path_is_config(&paths, path); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = path; + path = NULL; + } + } + } + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_strv_free_ char **fs = NULL; + const char *rp; + char **j; + + (void) get_files_in_directory(*i, &fs); + + q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); + if (q < 0 && q != -ENOENT && r >= 0) { + r = q; + continue; + } + + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + + t = strjoin(*i, "/", *j, NULL); + if (!t) + return -ENOMEM; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); + + rp = skip_root(&paths, *i); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + 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 = {}; + UnitFileInstallInfo *i, *target_info; + const char *config_path; + 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 = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + if (r < 0) + return r; + r = install_info_may_process(target_info, &paths, changes, n_changes); + if (r < 0) + return r; + + assert(target_info->type == UNIT_FILE_TYPE_REGULAR); + + STRV_FOREACH(f, files) { + char ***l; + + r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); + if (r < 0) + return r; + + 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) + l = &i->wanted_by; + else + l = &i->required_by; + + strv_free(*l); + *l = strv_new(target_info->name, NULL); + if (!*l) + return -ENOMEM; + } + + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); +} + +int unit_file_enable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + const char *config_path; + UnitFileInstallInfo *i; + char **f; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + STRV_FOREACH(f, files) { + r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); + if (r < 0) + return r; + + 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. */ + + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes); +} + +int unit_file_disable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + const char *config_path; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + STRV_FOREACH(i, files) { + 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(scope, &c, &paths, &remove_symlinks_to, config_path); + if (r < 0) + return r; + + return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); +} + +int unit_file_reenable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + 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, n, changes, n_changes); + if (r < 0) + return r; + + /* 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 *name, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + const char *new_path, *old_path; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ + return -EINVAL; + if (streq(name, SPECIAL_DEFAULT_TARGET)) + return -EINVAL; + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + r = install_info_discover(scope, &c, &paths, name, 0, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); + if (r < 0) + return r; + + old_path = skip_root(&paths, i->path); + new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); + + return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); +} + +int unit_file_get_default( + UnitFileScope scope, + const char *root_dir, + char **name) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _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(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, NULL, 0); + if (r < 0) + return r; + + n = strdup(i->name); + if (!n) + return -ENOMEM; + + *name = n; + return 0; +} + +static int unit_file_lookup_state( + UnitFileScope scope, + const LookupPaths *paths, + const char *name, + UnitFileState *ret) { + + _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; + + r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + + /* Shortcut things, if the caller just wants to know if this unit exists. */ + if (!ret) + return 0; + + switch (i->type) { + + case UNIT_FILE_TYPE_MASKED: + r = path_is_runtime(paths, i->path); + if (r < 0) + return r; + + state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + break; + + case UNIT_FILE_TYPE_REGULAR: + r = path_is_generator(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_GENERATED; + break; + } + + r = path_is_transient(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_TRANSIENT; + break; + } + + r = find_symlinks_in_scope(scope, paths, i->name, &state); + if (r < 0) + return r; + 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."); + } + + *ret = state; + return 0; +} + +int unit_file_get_state( + UnitFileScope scope, + const char *root_dir, + const char *name, + UnitFileState *ret) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + return unit_file_lookup_state(scope, &paths, name, ret); +} + +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { + _cleanup_(install_context_done) InstallContext c = {}; + int r; + + assert(paths); + assert(name); + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_discover(scope, &c, paths, name, 0, NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + return 1; +} + +static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { + _cleanup_(presets_freep) Presets ps = {}; + size_t n_allocated = 0; + _cleanup_strv_free_ char **files = NULL; + char **p; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(presets); + + if (scope == UNIT_FILE_SYSTEM) + r = conf_files_list(&files, ".preset", root_dir, + "/etc/systemd/system-preset", + "/usr/local/lib/systemd/system-preset", + "/usr/lib/systemd/system-preset", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/system-preset", +#endif + NULL); + else if (scope == UNIT_FILE_GLOBAL) + r = conf_files_list(&files, ".preset", root_dir, + "/etc/systemd/user-preset", + "/usr/local/lib/systemd/user-preset", + "/usr/lib/systemd/user-preset", + NULL); + else { + *presets = (Presets){}; + + return 0; + } + + if (r < 0) + return r; + + STRV_FOREACH(p, files) { + _cleanup_fclose_ FILE *f; + char line[LINE_MAX]; + int n = 0; + + f = fopen(*p, "re"); + if (!f) { + if (errno == ENOENT) + continue; + + return -errno; + } + + FOREACH_LINE(line, f, return -errno) { + PresetRule rule = {}; + const char *parameter; + char *l; + + l = strstrip(line); + n++; + + if (isempty(l)) + continue; + if (strchr(COMMENTS, *l)) + continue; + + parameter = first_word(l, "enable"); + if (parameter) { + char *pattern; + + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_ENABLE, + }; + } + + parameter = first_word(l, "disable"); + if (parameter) { + char *pattern; + + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_DISABLE, + }; + } + + if (rule.action) { + if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1)) + return -ENOMEM; + + ps.rules[ps.n_rules++] = rule; + continue; + } + + log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); + } + } + + *presets = ps; + ps = (Presets){}; + + return 0; +} + +static int query_presets(const char *name, const Presets presets) { + PresetAction action = PRESET_UNKNOWN; + size_t i; + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + for (i = 0; i < presets.n_rules; i++) + if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets.rules[i].action; + break; + } + + switch (action) { + case PRESET_UNKNOWN: + log_debug("Preset files don't specify rule for %s. Enabling.", name); + return 1; + case PRESET_ENABLE: + log_debug("Preset files say enable %s.", name); + return 1; + case PRESET_DISABLE: + log_debug("Preset files say disable %s.", name); + return 0; + default: + assert_not_reached("invalid preset action"); + } +} + +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { + _cleanup_(presets_freep) Presets presets = {}; + int r; + + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + + return query_presets(name, presets); +} + +static int execute_preset( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + const LookupPaths *paths, + const char *config_path, + 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); + if (r < 0) + return r; + + r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, 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, 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, + UnitFilePresetMode mode, + const char *name, + Presets presets, + UnitFileChange **changes, + unsigned *n_changes) { + + UnitFileInstallInfo *i; + int r; + + if (install_info_find(plus, name) || + install_info_find(minus, name)) + return 0; + + r = query_presets(name, presets); + if (r < 0) + return r; + + if (r > 0) { + r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + + r = install_info_may_process(i, paths, changes, n_changes); + if (r < 0) + return r; + } else + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + + return r; +} + +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(presets_freep) Presets presets = {}; + const char *config_path; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(mode < _UNIT_FILE_PRESET_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); + if (r < 0) + return r; + } + + return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes); +} + +int unit_file_preset_all( + UnitFileScope scope, + bool runtime, + const char *root_dir, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(presets_freep) Presets presets = {}; + const char *config_path = NULL; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(mode < _UNIT_FILE_PRESET_MAX); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + config_path = runtime ? paths.runtime_config : paths.persistent_config; + + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.search_path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(*i); + if (!d) { + if (errno == ENOENT) + continue; + + return -errno; + } + + FOREACH_DIRENT(de, d, return -errno) { + + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) + continue; + + dirent_ensure_type(d, de); + + if (!IN_SET(de->d_type, DT_LNK, DT_REG)) + continue; + + /* we don't pass changes[] in, because we want to handle errors on our own */ + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); + if (r == -ERFKILL) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_MASKED, de->d_name, NULL); + else if (r == -ENOLINK) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_DANGLING, de->d_name, NULL); + if (r < 0) + return r; + } + } + + return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes); +} + +static void unit_file_list_free_one(UnitFileList *f) { + if (!f) + return; + + free(f->path); + 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( + UnitFileScope scope, + const char *root_dir, + Hashmap *h, + char **states, + char **patterns) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(h); + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.search_path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(*i); + if (!d) { + if (errno == ENOENT) + continue; + if (IN_SET(errno, ENOTDIR, EACCES)) { + log_debug("Failed to open \"%s\": %m", *i); + continue; + } + + return -errno; + } + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; + + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) + continue; + + if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE)) + continue; + + if (hashmap_get(h, de->d_name)) + continue; + + dirent_ensure_type(d, de); + + if (!IN_SET(de->d_type, DT_LNK, DT_REG)) + continue; + + f = new0(UnitFileList, 1); + if (!f) + return -ENOMEM; + + f->path = path_make_absolute(de->d_name, *i); + if (!f->path) + return -ENOMEM; + + r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); + if (r < 0) + f->state = UNIT_FILE_BAD; + + if (!strv_isempty(states) && + !strv_contains(states, unit_file_state_to_string(f->state))) + continue; + + r = hashmap_put(h, basename(f->path), f); + if (r < 0) + return r; + + f = NULL; /* prevent cleanup */ + } + } + + return 0; +} + +static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { + [UNIT_FILE_ENABLED] = "enabled", + [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime", + [UNIT_FILE_LINKED] = "linked", + [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", + [UNIT_FILE_MASKED] = "masked", + [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", + [UNIT_FILE_STATIC] = "static", + [UNIT_FILE_DISABLED] = "disabled", + [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", + [UNIT_FILE_BAD] = "bad", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); + +static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { + [UNIT_FILE_SYMLINK] = "symlink", + [UNIT_FILE_UNLINK] = "unlink", + [UNIT_FILE_IS_MASKED] = "masked", + [UNIT_FILE_IS_DANGLING] = "dangling", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); + +static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { + [UNIT_FILE_PRESET_FULL] = "full", + [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", + [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only", +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode); diff --git a/src/libshared/src/logs-show.c b/src/libshared/src/logs-show.c new file mode 100644 index 0000000000..ca145f074d --- /dev/null +++ b/src/libshared/src/logs-show.c @@ -0,0 +1,1310 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/hashmap.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/sparse-endian.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "sd-journal/journal-internal.h" +#include "shared/logs-show.h" +#include "shared/output-mode.h" + +/* 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 + +#define JSON_THRESHOLD 4096 + +static int print_catalog(FILE *f, sd_journal *j) { + int r; + _cleanup_free_ char *t = NULL, *z = NULL; + + + r = sd_journal_get_catalog(j, &t); + if (r < 0) + return r; + + z = strreplace(strstrip(t), "\n", "\n-- "); + if (!z) + return log_oom(); + + fputs("-- ", f); + fputs(z, f); + fputc('\n', f); + + return 0; +} + +static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { + size_t fl, nl; + char *buf; + + assert(data); + assert(field); + assert(target); + + fl = strlen(field); + if (length < fl) + return 0; + + if (memcmp(data, field, fl)) + return 0; + + nl = length - fl; + buf = new(char, nl+1); + if (!buf) + return log_oom(); + + memcpy(buf, (const char*) data + fl, nl); + buf[nl] = 0; + + free(*target); + *target = buf; + + if (target_size) + *target_size = nl; + + return 1; +} + +static bool shall_print(const char *p, size_t l, OutputFlags flags) { + assert(p); + + if (flags & OUTPUT_SHOW_ALL) + return true; + + if (l >= PRINT_CHAR_THRESHOLD) + return false; + + if (!utf8_is_printable(p, l)) + return false; + + return true; +} + +static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) { + const char *color_on = "", *color_off = ""; + const char *pos, *end; + bool ellipsized = false; + int line = 0; + + if (flags & OUTPUT_COLOR) { + if (priority <= LOG_ERR) { + color_on = ANSI_HIGHLIGHT_RED; + color_off = ANSI_NORMAL; + } else if (priority <= LOG_NOTICE) { + color_on = ANSI_HIGHLIGHT; + color_off = ANSI_NORMAL; + } + } + + /* A special case: make sure that we print a newline when + the message is empty. */ + if (message_len == 0) + fputs("\n", f); + + for (pos = message; + pos < message + message_len; + pos = end + 1, line++) { + bool continuation = line > 0; + bool tail_line; + int len; + for (end = pos; end < message + message_len && *end != '\n'; end++) + ; + len = end - pos; + assert(len >= 0); + + /* We need to figure out when we are showing not-last line, *and* + * will skip subsequent lines. In that case, we will put the dots + * at the end of the line, instead of putting dots in the middle + * or not at all. + */ + tail_line = + line + 1 == PRINT_LINE_THRESHOLD || + end + 1 >= message + PRINT_CHAR_THRESHOLD; + + if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || + (prefix + len + 1 < n_columns && !tail_line)) { + fprintf(f, "%*s%s%.*s%s\n", + continuation * prefix, "", + color_on, len, pos, color_off); + continue; + } + + /* Beyond this point, ellipsization will happen. */ + ellipsized = true; + + if (prefix < n_columns && n_columns - prefix >= 3) { + if (n_columns - prefix > (unsigned) len + 3) + fprintf(f, "%*s%s%.*s...%s\n", + continuation * prefix, "", + color_on, len, pos, color_off); + else { + _cleanup_free_ char *e; + + e = ellipsize_mem(pos, len, n_columns - prefix, + tail_line ? 100 : 90); + if (!e) + fprintf(f, "%*s%s%.*s%s\n", + continuation * prefix, "", + color_on, len, pos, color_off); + else + fprintf(f, "%*s%s%s%s\n", + continuation * prefix, "", + color_on, e, color_off); + } + } else + fputs("...\n", f); + + if (tail_line) + break; + } + + return ellipsized; +} + +static int output_short( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) { + + int r; + const void *data; + size_t length; + size_t n = 0; + _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL; + size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0; + int p = LOG_INFO; + bool ellipsized = false; + + assert(f); + assert(j); + + /* Set the threshold to one bigger than the actual print + * threshold, so that if the line is actually longer than what + * we're willing to print, ellipsization will occur. This way + * we won't output a misleading line without any indication of + * truncation. + */ + sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1); + + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + + r = parse_field(data, length, "PRIORITY=", &priority, &priority_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "_COMM=", &comm, &comm_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "_PID=", &pid, &pid_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_field(data, length, "MESSAGE=", &message, &message_len); + if (r < 0) + return r; + } + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to get journal fields: %m"); + + if (!message) { + log_debug("Skipping message without MESSAGE= field."); + return 0; + } + + if (!(flags & OUTPUT_SHOW_ALL)) + strip_tab_ansi(&message, &message_len); + + if (priority_len == 1 && *priority >= '0' && *priority <= '7') + p = *priority - '0'; + + if (mode == OUTPUT_SHORT_MONOTONIC) { + uint64_t t; + sd_id128_t boot_id; + + r = -ENOENT; + + if (monotonic) + r = safe_atou64(monotonic, &t); + + if (r < 0) + r = sd_journal_get_monotonic_usec(j, &t, &boot_id); + + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + fprintf(f, "[%5llu.%06llu]", + (unsigned long long) (t / USEC_PER_SEC), + (unsigned long long) (t % USEC_PER_SEC)); + + n += 1 + 5 + 1 + 6 + 1; + + } else { + char buf[64]; + uint64_t x; + time_t t; + struct tm tm; + struct tm *(*gettime_r)(const time_t *, struct tm *); + + r = -ENOENT; + gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; + + if (realtime) + r = safe_atou64(realtime, &x); + + if (r < 0) + r = sd_journal_get_realtime_usec(j, &x); + + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + t = (time_t) (x / USEC_PER_SEC); + + switch (mode) { + + case OUTPUT_SHORT_UNIX: + r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); + break; + + case OUTPUT_SHORT_ISO: + r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); + break; + + case OUTPUT_SHORT_PRECISE: + r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); + if (r > 0) + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); + break; + + default: + r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); + } + + if (r <= 0) { + log_error("Failed to format time."); + return -EINVAL; + } + + fputs(buf, f); + n += strlen(buf); + } + + if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { + /* Suppress display of the hostname if this is requested. */ + hostname = NULL; + hostname_len = 0; + } + + if (hostname && shall_print(hostname, hostname_len, flags)) { + fprintf(f, " %.*s", (int) hostname_len, hostname); + n += hostname_len + 1; + } + + if (identifier && shall_print(identifier, identifier_len, flags)) { + fprintf(f, " %.*s", (int) identifier_len, identifier); + n += identifier_len + 1; + } else if (comm && shall_print(comm, comm_len, flags)) { + fprintf(f, " %.*s", (int) comm_len, comm); + n += comm_len + 1; + } else + fputs(" unknown", f); + + if (pid && shall_print(pid, pid_len, flags)) { + fprintf(f, "[%.*s]", (int) pid_len, pid); + n += pid_len + 2; + } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) { + fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid); + n += fake_pid_len + 2; + } + + if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) { + char bytes[FORMAT_BYTES_MAX]; + fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); + } else { + fputs(": ", f); + ellipsized |= + print_multiline(f, n + 2, n_columns, flags, p, message, message_len); + } + + if (flags & OUTPUT_CATALOG) + print_catalog(f, j); + + return ellipsized; +} + +static int output_verbose( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) { + + const void *data; + size_t length; + _cleanup_free_ char *cursor = NULL; + uint64_t realtime = 0; + char ts[FORMAT_TIMESTAMP_MAX + 7]; + int r; + + assert(f); + assert(j); + + sd_journal_set_data_threshold(j, 0); + + r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length); + if (r == -ENOENT) + log_debug("Source realtime timestamp not found"); + else if (r < 0) + return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m"); + else { + _cleanup_free_ char *value = NULL; + + r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, NULL); + if (r < 0) + return r; + assert(r > 0); + + r = safe_atou64(value, &realtime); + if (r < 0) + log_debug_errno(r, "Failed to parse realtime timestamp: %m"); + } + + if (r < 0) { + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) + return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m"); + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); + + fprintf(f, "%s [%s]\n", + flags & OUTPUT_UTC ? + format_timestamp_us_utc(ts, sizeof(ts), realtime) : + format_timestamp_us(ts, sizeof(ts), realtime), + cursor); + + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + const char *c; + int fieldlen; + const char *on = "", *off = ""; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + fieldlen = c - (const char*) data; + + if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) { + on = ANSI_HIGHLIGHT; + off = ANSI_NORMAL; + } + + if ((flags & OUTPUT_SHOW_ALL) || + (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH) + && utf8_is_printable(data, length))) { + fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data); + print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1); + fputs(off, f); + } else { + char bytes[FORMAT_BYTES_MAX]; + + fprintf(f, " %s%.*s=[%s blob data]%s\n", + on, + (int) (c - (const char*) data), + (const char*) data, + format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1), + off); + } + } + + if (r < 0) + return r; + + if (flags & OUTPUT_CATALOG) + print_catalog(f, j); + + return 0; +} + +static int output_export( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) { + + sd_id128_t boot_id; + char sid[33]; + int r; + usec_t realtime, monotonic; + _cleanup_free_ char *cursor = NULL; + const void *data; + size_t length; + + assert(j); + + sd_journal_set_data_threshold(j, 0); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); + + fprintf(f, + "__CURSOR=%s\n" + "__REALTIME_TIMESTAMP="USEC_FMT"\n" + "__MONOTONIC_TIMESTAMP="USEC_FMT"\n" + "_BOOT_ID=%s\n", + cursor, + realtime, + monotonic, + sd_id128_to_string(boot_id, sid)); + + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + + /* We already printed the boot id, from the data in + * the header, hence let's suppress it here */ + if (length >= 9 && + startswith(data, "_BOOT_ID=")) + continue; + + if (utf8_is_printable_newline(data, length, false)) + fwrite(data, length, 1, f); + else { + const char *c; + uint64_t le64; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fwrite(data, c - (const char*) data, 1, f); + fputc('\n', f); + le64 = htole64(length - (c - (const char*) data) - 1); + fwrite(&le64, sizeof(le64), 1, f); + fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f); + } + + fputc('\n', f); + } + + if (r < 0) + return r; + + fputc('\n', f); + + return 0; +} + +void json_escape( + FILE *f, + const char* p, + size_t l, + OutputFlags flags) { + + assert(f); + assert(p); + + if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD) + fputs("null", f); + + else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) { + bool not_first = false; + + fputs("[ ", f); + + while (l > 0) { + if (not_first) + fprintf(f, ", %u", (uint8_t) *p); + else { + not_first = true; + fprintf(f, "%u", (uint8_t) *p); + } + + p++; + l--; + } + + fputs(" ]", f); + } else { + fputc('\"', f); + + while (l > 0) { + if (*p == '"' || *p == '\\') { + fputc('\\', f); + fputc(*p, f); + } else if (*p == '\n') + fputs("\\n", f); + else if ((uint8_t) *p < ' ') + fprintf(f, "\\u%04x", (uint8_t) *p); + else + fputc(*p, f); + + p++; + l--; + } + + fputc('\"', f); + } +} + +static int output_json( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) { + + uint64_t realtime, monotonic; + _cleanup_free_ char *cursor = NULL; + const void *data; + size_t length; + sd_id128_t boot_id; + char sid[33], *k; + int r; + Hashmap *h = NULL; + bool done, separator; + + assert(j); + + sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); + + if (mode == OUTPUT_JSON_PRETTY) + fprintf(f, + "{\n" + "\t\"__CURSOR\" : \"%s\",\n" + "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n" + "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n" + "\t\"_BOOT_ID\" : \"%s\"", + cursor, + realtime, + monotonic, + sd_id128_to_string(boot_id, sid)); + else { + if (mode == OUTPUT_JSON_SSE) + fputs("data: ", f); + + fprintf(f, + "{ \"__CURSOR\" : \"%s\", " + "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", " + "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", " + "\"_BOOT_ID\" : \"%s\"", + cursor, + realtime, + monotonic, + sd_id128_to_string(boot_id, sid)); + } + + h = hashmap_new(&string_hash_ops); + if (!h) + return log_oom(); + + /* First round, iterate through the entry and count how often each field appears */ + JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + const char *eq; + char *n; + unsigned u; + + if (length >= 9 && + memcmp(data, "_BOOT_ID=", 9) == 0) + continue; + + eq = memchr(data, '=', length); + if (!eq) + continue; + + n = strndup(data, eq - (const char*) data); + if (!n) { + r = log_oom(); + goto finish; + } + + u = PTR_TO_UINT(hashmap_get(h, n)); + if (u == 0) { + r = hashmap_put(h, n, UINT_TO_PTR(1)); + if (r < 0) { + free(n); + log_oom(); + goto finish; + } + } else { + r = hashmap_update(h, n, UINT_TO_PTR(u + 1)); + free(n); + if (r < 0) { + log_oom(); + goto finish; + } + } + } + + if (r < 0) + return r; + + separator = true; + do { + done = true; + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + const char *eq; + char *kk, *n; + size_t m; + unsigned u; + + /* We already printed the boot id, from the data in + * the header, hence let's suppress it here */ + if (length >= 9 && + memcmp(data, "_BOOT_ID=", 9) == 0) + continue; + + eq = memchr(data, '=', length); + if (!eq) + continue; + + if (separator) { + if (mode == OUTPUT_JSON_PRETTY) + fputs(",\n\t", f); + else + fputs(", ", f); + } + + m = eq - (const char*) data; + + n = strndup(data, m); + if (!n) { + r = log_oom(); + goto finish; + } + + u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk)); + if (u == 0) { + /* We already printed this, let's jump to the next */ + free(n); + separator = false; + + continue; + } else if (u == 1) { + /* Field only appears once, output it directly */ + + json_escape(f, data, m, flags); + fputs(" : ", f); + + json_escape(f, eq + 1, length - m - 1, flags); + + hashmap_remove(h, n); + free(kk); + free(n); + + separator = true; + + continue; + + } else { + /* Field appears multiple times, output it as array */ + json_escape(f, data, m, flags); + fputs(" : [ ", f); + json_escape(f, eq + 1, length - m - 1, flags); + + /* Iterate through the end of the list */ + + while (sd_journal_enumerate_data(j, &data, &length) > 0) { + if (length < m + 1) + continue; + + if (memcmp(data, n, m) != 0) + continue; + + if (((const char*) data)[m] != '=') + continue; + + fputs(", ", f); + json_escape(f, (const char*) data + m + 1, length - m - 1, flags); + } + + fputs(" ]", f); + + hashmap_remove(h, n); + free(kk); + free(n); + + /* Iterate data fields form the beginning */ + done = false; + separator = true; + + break; + } + } + + } while (!done); + + if (mode == OUTPUT_JSON_PRETTY) + fputs("\n}\n", f); + else if (mode == OUTPUT_JSON_SSE) + fputs("}\n\n", f); + else + fputs(" }\n", f); + + r = 0; + +finish: + while ((k = hashmap_steal_first_key(h))) + free(k); + + hashmap_free(h); + + return r; +} + +static int output_cat( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) { + + const void *data; + size_t l; + int r; + + assert(j); + assert(f); + + sd_journal_set_data_threshold(j, 0); + + r = sd_journal_get_data(j, "MESSAGE", &data, &l); + if (r < 0) { + /* An entry without MESSAGE=? */ + if (r == -ENOENT) + return 0; + + return log_error_errno(r, "Failed to get data: %m"); + } + + assert(l >= 8); + + fwrite((const char*) data + 8, 1, l - 8, f); + fputc('\n', f); + + return 0; +} + +static int (*output_funcs[_OUTPUT_MODE_MAX])( + FILE *f, + sd_journal*j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags) = { + + [OUTPUT_SHORT] = output_short, + [OUTPUT_SHORT_ISO] = output_short, + [OUTPUT_SHORT_PRECISE] = output_short, + [OUTPUT_SHORT_MONOTONIC] = output_short, + [OUTPUT_SHORT_UNIX] = output_short, + [OUTPUT_VERBOSE] = output_verbose, + [OUTPUT_EXPORT] = output_export, + [OUTPUT_JSON] = output_json, + [OUTPUT_JSON_PRETTY] = output_json, + [OUTPUT_JSON_SSE] = output_json, + [OUTPUT_CAT] = output_cat +}; + +int output_journal( + FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + OutputFlags flags, + bool *ellipsized) { + + int ret; + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + + if (n_columns <= 0) + n_columns = columns(); + + ret = output_funcs[mode](f, j, mode, n_columns, flags); + fflush(stdout); + + if (ellipsized && ret > 0) + *ellipsized = true; + + return ret; +} + +static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) { + assert(f); + assert(flags); + + if (!(*flags & OUTPUT_BEGIN_NEWLINE)) + return 0; + + /* Print a beginning new line if that's request, but only once + * on the first line we print. */ + + fputc('\n', f); + *flags &= ~OUTPUT_BEGIN_NEWLINE; + return 0; +} + +static int show_journal(FILE *f, + sd_journal *j, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + OutputFlags flags, + bool *ellipsized) { + + int r; + unsigned line = 0; + bool need_seek = false; + int warn_cutoff = flags & OUTPUT_WARN_CUTOFF; + + assert(j); + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + + /* Seek to end */ + r = sd_journal_seek_tail(j); + if (r < 0) + return log_error_errno(r, "Failed to seek to tail: %m"); + + r = sd_journal_previous_skip(j, how_many); + if (r < 0) + return log_error_errno(r, "Failed to skip previous: %m"); + + for (;;) { + for (;;) { + usec_t usec; + + if (need_seek) { + r = sd_journal_next(j); + if (r < 0) + return log_error_errno(r, "Failed to iterate through journal: %m"); + } + + if (r == 0) + break; + + need_seek = true; + + if (not_before > 0) { + r = sd_journal_get_monotonic_usec(j, &usec, NULL); + + /* -ESTALE is returned if the + timestamp is not from this boot */ + if (r == -ESTALE) + continue; + else if (r < 0) + return log_error_errno(r, "Failed to get journal time: %m"); + + if (usec < not_before) + continue; + } + + line++; + maybe_print_begin_newline(f, &flags); + + r = output_journal(f, j, mode, n_columns, flags, ellipsized); + if (r < 0) + return r; + } + + if (warn_cutoff && line < how_many && not_before > 0) { + sd_id128_t boot_id; + usec_t cutoff = 0; + + /* Check whether the cutoff line is too early */ + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get boot id: %m"); + + r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL); + if (r < 0) + return log_error_errno(r, "Failed to get journal cutoff time: %m"); + + if (r > 0 && not_before < cutoff) { + maybe_print_begin_newline(f, &flags); + fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n"); + } + + warn_cutoff = false; + } + + if (!(flags & OUTPUT_FOLLOW)) + break; + + r = sd_journal_wait(j, USEC_INFINITY); + if (r < 0) + return log_error_errno(r, "Failed to wait for journal: %m"); + + } + + return 0; +} + +int add_matches_for_unit(sd_journal *j, const char *unit) { + const char *m1, *m2, *m3, *m4; + int r; + + assert(j); + assert(unit); + + m1 = strjoina("_SYSTEMD_UNIT=", unit); + m2 = strjoina("COREDUMP_UNIT=", unit); + m3 = strjoina("UNIT=", unit); + m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit); + + (void)( + /* Look for messages from the service itself */ + (r = sd_journal_add_match(j, m1, 0)) || + + /* Look for coredumps of the service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) || + (r = sd_journal_add_match(j, "_UID=0", 0)) || + (r = sd_journal_add_match(j, m2, 0)) || + + /* Look for messages from PID 1 about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, "_PID=1", 0)) || + (r = sd_journal_add_match(j, m3, 0)) || + + /* Look for messages from authorized daemons about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, "_UID=0", 0)) || + (r = sd_journal_add_match(j, m4, 0)) + ); + + if (r == 0 && endswith(unit, ".slice")) { + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); + + /* Show all messages belonging to a slice */ + (void)( + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m5, 0)) + ); + } + + return r; +} + +int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { + int r; + char *m1, *m2, *m3, *m4; + char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)]; + + assert(j); + assert(unit); + + m1 = strjoina("_SYSTEMD_USER_UNIT=", unit); + m2 = strjoina("USER_UNIT=", unit); + m3 = strjoina("COREDUMP_USER_UNIT=", unit); + m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit); + sprintf(muid, "_UID="UID_FMT, uid); + + (void) ( + /* Look for messages from the user service itself */ + (r = sd_journal_add_match(j, m1, 0)) || + (r = sd_journal_add_match(j, muid, 0)) || + + /* Look for messages from systemd about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m2, 0)) || + (r = sd_journal_add_match(j, muid, 0)) || + + /* Look for coredumps of the service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m3, 0)) || + (r = sd_journal_add_match(j, muid, 0)) || + (r = sd_journal_add_match(j, "_UID=0", 0)) || + + /* Look for messages from authorized daemons about this service */ + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m4, 0)) || + (r = sd_journal_add_match(j, muid, 0)) || + (r = sd_journal_add_match(j, "_UID=0", 0)) + ); + + if (r == 0 && endswith(unit, ".slice")) { + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); + + /* Show all messages belonging to a slice */ + (void)( + (r = sd_journal_add_disjunction(j)) || + (r = sd_journal_add_match(j, m5, 0)) || + (r = sd_journal_add_match(j, muid, 0)) + ); + } + + return r; +} + +static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; + pid_t pid, child; + siginfo_t si; + char buf[37]; + ssize_t k; + int r; + + assert(machine); + assert(boot_id); + + if (!machine_name_is_valid(machine)) + return -EINVAL; + + r = container_get_leader(machine, &pid); + if (r < 0) + return r; + + r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &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 fd; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + _exit(EXIT_FAILURE); + + r = loop_read_exact(fd, buf, 36, false); + safe_close(fd); + if (r < 0) + _exit(EXIT_FAILURE); + + k = send(pair[1], buf, 36, MSG_NOSIGNAL); + if (k != 36) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return r < 0 ? r : -EIO; + + k = recv(pair[0], buf, 36, 0); + if (k != 36) + return -EIO; + + buf[36] = 0; + r = sd_id128_from_string(buf, boot_id); + if (r < 0) + return r; + + return 0; +} + +int add_match_this_boot(sd_journal *j, const char *machine) { + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; + + assert(j); + + if (machine) { + r = get_boot_id_for_machine(machine, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get boot id of container %s: %m", machine); + } else { + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get boot id: %m"); + } + + sd_id128_to_string(boot_id, match + 9); + r = sd_journal_add_match(j, match, strlen(match)); + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); + + r = sd_journal_add_conjunction(j); + if (r < 0) + return log_error_errno(r, "Failed to add conjunction: %m"); + + return 0; +} + +int show_journal_by_unit( + FILE *f, + const char *unit, + OutputMode mode, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + uid_t uid, + OutputFlags flags, + int journal_open_flags, + bool system_unit, + bool *ellipsized) { + + _cleanup_(sd_journal_closep) sd_journal *j = NULL; + int r; + + assert(mode >= 0); + assert(mode < _OUTPUT_MODE_MAX); + assert(unit); + + if (how_many <= 0) + return 0; + + r = sd_journal_open(&j, journal_open_flags); + if (r < 0) + return log_error_errno(r, "Failed to open journal: %m"); + + r = add_match_this_boot(j, NULL); + if (r < 0) + return r; + + if (system_unit) + r = add_matches_for_unit(j, unit); + else + r = add_matches_for_user_unit(j, unit, uid); + if (r < 0) + return log_error_errno(r, "Failed to add unit matches: %m"); + + if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + _cleanup_free_ char *filter; + + filter = journal_make_match_string(j); + if (!filter) + return log_oom(); + + log_debug("Journal filter: %s", filter); + } + + return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized); +} diff --git a/src/libshared/src/machine-image.c b/src/libshared/src/machine-image.c new file mode 100644 index 0000000000..28b096f538 --- /dev/null +++ b/src/libshared/src/machine-image.c @@ -0,0 +1,820 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/chattr-util.h" +#include "basic/copy.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/lockfile-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "basic/xattr-util.h" +#include "shared/machine-image.h" + +static const char image_search_path[] = + "/var/lib/machines\0" + "/var/lib/container\0" /* legacy */ + "/usr/local/lib/machines\0" + "/usr/lib/machines\0"; + +Image *image_unref(Image *i) { + if (!i) + return NULL; + + free(i->name); + free(i->path); + free(i); + return NULL; +} + +static char **image_settings_path(Image *image) { + _cleanup_strv_free_ char **l = NULL; + char **ret; + const char *fn, *s; + unsigned i = 0; + + assert(image); + + l = new0(char*, 4); + if (!l) + return NULL; + + fn = strjoina(image->name, ".nspawn"); + + FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") { + l[i] = strappend(s, fn); + if (!l[i]) + return NULL; + + i++; + } + + l[i] = file_in_same_dir(image->path, fn); + if (!l[i]) + return NULL; + + ret = l; + l = NULL; + + return ret; +} + +static int image_new( + ImageType t, + const char *pretty, + const char *path, + const char *filename, + bool read_only, + usec_t crtime, + usec_t mtime, + Image **ret) { + + _cleanup_(image_unrefp) Image *i = NULL; + + assert(t >= 0); + assert(t < _IMAGE_TYPE_MAX); + assert(pretty); + assert(filename); + assert(ret); + + i = new0(Image, 1); + if (!i) + return -ENOMEM; + + i->type = t; + i->read_only = read_only; + i->crtime = crtime; + i->mtime = mtime; + i->usage = i->usage_exclusive = (uint64_t) -1; + i->limit = i->limit_exclusive = (uint64_t) -1; + + i->name = strdup(pretty); + if (!i->name) + return -ENOMEM; + + if (path) + i->path = strjoin(path, "/", filename, NULL); + else + i->path = strdup(filename); + + if (!i->path) + return -ENOMEM; + + path_kill_slashes(i->path); + + *ret = i; + i = NULL; + + return 0; +} + +static int image_make( + const char *pretty, + int dfd, + const char *path, + const char *filename, + Image **ret) { + + struct stat st; + bool read_only; + int r; + + assert(filename); + + /* We explicitly *do* follow symlinks here, since we want to + * allow symlinking trees into /var/lib/machines/, and treat + * them normally. */ + + if (fstatat(dfd, filename, &st, 0) < 0) + return -errno; + + read_only = + (path && path_startswith(path, "/usr")) || + (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS); + + if (S_ISDIR(st.st_mode)) { + _cleanup_close_ int fd = -1; + unsigned file_attr = 0; + + if (!ret) + return 1; + + if (!pretty) + pretty = filename; + + fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); + if (fd < 0) + return -errno; + + /* btrfs subvolumes have inode 256 */ + if (st.st_ino == 256) { + + r = btrfs_is_filesystem(fd); + if (r < 0) + return r; + if (r) { + BtrfsSubvolInfo info; + + /* It's a btrfs subvolume */ + + r = btrfs_subvol_get_info_fd(fd, 0, &info); + if (r < 0) + return r; + + r = image_new(IMAGE_SUBVOLUME, + pretty, + path, + filename, + info.read_only || read_only, + info.otime, + 0, + ret); + if (r < 0) + return r; + + if (btrfs_quota_scan_ongoing(fd) == 0) { + BtrfsQuotaInfo quota; + + r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); + if (r >= 0) { + (*ret)->usage = quota.referenced; + (*ret)->usage_exclusive = quota.exclusive; + + (*ret)->limit = quota.referenced_max; + (*ret)->limit_exclusive = quota.exclusive_max; + } + } + + return 1; + } + } + + /* If the IMMUTABLE bit is set, we consider the + * directory read-only. Since the ioctl is not + * supported everywhere we ignore failures. */ + (void) read_attr_fd(fd, &file_attr); + + /* It's just a normal directory. */ + r = image_new(IMAGE_DIRECTORY, + pretty, + path, + filename, + read_only || (file_attr & FS_IMMUTABLE_FL), + 0, + 0, + ret); + if (r < 0) + return r; + + return 1; + + } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) { + usec_t crtime = 0; + + /* It's a RAW disk image */ + + if (!ret) + return 1; + + fd_getcrtime_at(dfd, filename, &crtime, 0); + + if (!pretty) + pretty = strndupa(filename, strlen(filename) - 4); + + r = image_new(IMAGE_RAW, + pretty, + path, + filename, + !(st.st_mode & 0222) || read_only, + crtime, + timespec_load(&st.st_mtim), + ret); + if (r < 0) + return r; + + (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512; + (*ret)->limit = (*ret)->limit_exclusive = st.st_size; + + return 1; + } + + return 0; +} + +int image_find(const char *name, Image **ret) { + const char *path; + int r; + + assert(name); + + /* There are no images with invalid names */ + if (!image_name_is_valid(name)) + return 0; + + NULSTR_FOREACH(path, image_search_path) { + _cleanup_closedir_ DIR *d = NULL; + + d = opendir(path); + if (!d) { + if (errno == ENOENT) + continue; + + return -errno; + } + + r = image_make(NULL, dirfd(d), path, name, ret); + if (r == 0 || r == -ENOENT) { + _cleanup_free_ char *raw = NULL; + + raw = strappend(name, ".raw"); + if (!raw) + return -ENOMEM; + + r = image_make(NULL, dirfd(d), path, raw, ret); + if (r == 0 || r == -ENOENT) + continue; + } + if (r < 0) + return r; + + return 1; + } + + if (streq(name, ".host")) + return image_make(".host", AT_FDCWD, NULL, "/", ret); + + return 0; +}; + +int image_discover(Hashmap *h) { + const char *path; + int r; + + assert(h); + + NULSTR_FOREACH(path, image_search_path) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + d = opendir(path); + if (!d) { + if (errno == ENOENT) + continue; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + _cleanup_(image_unrefp) Image *image = NULL; + + if (!image_name_is_valid(de->d_name)) + continue; + + if (hashmap_contains(h, de->d_name)) + continue; + + r = image_make(NULL, dirfd(d), path, de->d_name, &image); + if (r == 0 || r == -ENOENT) + continue; + if (r < 0) + return r; + + r = hashmap_put(h, image->name, image); + if (r < 0) + return r; + + image = NULL; + } + } + + if (!hashmap_contains(h, ".host")) { + _cleanup_(image_unrefp) Image *image = NULL; + + r = image_make(".host", AT_FDCWD, NULL, "/", &image); + if (r < 0) + return r; + + r = hashmap_put(h, image->name, image); + if (r < 0) + return r; + + image = NULL; + + } + + return 0; +} + +void image_hashmap_free(Hashmap *map) { + Image *i; + + while ((i = hashmap_steal_first(map))) + image_unref(i); + + hashmap_free(map); +} + +int image_remove(Image *i) { + _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; + char **j; + int r; + + assert(i); + + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) + return -EROFS; + + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + + /* Make sure we don't interfere with a running nspawn */ + r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); + if (r < 0) + return r; + + switch (i->type) { + + case IMAGE_SUBVOLUME: + r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) + return r; + break; + + case IMAGE_DIRECTORY: + /* Allow deletion of read-only directories */ + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); + r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + if (r < 0) + return r; + + break; + + case IMAGE_RAW: + if (unlink(i->path) < 0) + return -errno; + break; + + default: + return -EOPNOTSUPP; + } + + STRV_FOREACH(j, settings) { + if (unlink(*j) < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); + } + + return 0; +} + +static int rename_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs); +} + +int image_rename(Image *i, const char *new_name) { + _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; + _cleanup_free_ char *new_path = NULL, *nn = NULL; + _cleanup_strv_free_ char **settings = NULL; + unsigned file_attr = 0; + char **j; + int r; + + assert(i); + + if (!image_name_is_valid(new_name)) + return -EINVAL; + + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) + return -EROFS; + + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + + /* Make sure we don't interfere with a running nspawn */ + r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); + if (r < 0) + return r; + + /* Make sure nobody takes the new name, between the time we + * checked it is currently unused in all search paths, and the + * time we take possession of it */ + r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); + if (r < 0) + return r; + + r = image_find(new_name, NULL); + if (r < 0) + return r; + if (r > 0) + return -EEXIST; + + switch (i->type) { + + case IMAGE_DIRECTORY: + /* Turn of the immutable bit while we rename the image, so that we can rename it */ + (void) read_attr_path(i->path, &file_attr); + + if (file_attr & FS_IMMUTABLE_FL) + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); + + /* fall through */ + + case IMAGE_SUBVOLUME: + new_path = file_in_same_dir(i->path, new_name); + break; + + case IMAGE_RAW: { + const char *fn; + + fn = strjoina(new_name, ".raw"); + new_path = file_in_same_dir(i->path, fn); + break; + } + + default: + return -EOPNOTSUPP; + } + + if (!new_path) + return -ENOMEM; + + nn = strdup(new_name); + if (!nn) + return -ENOMEM; + + r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path); + if (r < 0) + return r; + + /* Restore the immutable bit, if it was set before */ + if (file_attr & FS_IMMUTABLE_FL) + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); + + free(i->path); + i->path = new_path; + new_path = NULL; + + free(i->name); + i->name = nn; + nn = NULL; + + STRV_FOREACH(j, settings) { + r = rename_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); + } + + return 0; +} + +static int clone_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return copy_file_atomic(path, rs, 0664, false, 0); +} + +int image_clone(Image *i, const char *new_name, bool read_only) { + _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; + const char *new_path; + char **j; + int r; + + assert(i); + + if (!image_name_is_valid(new_name)) + return -EINVAL; + + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + + /* Make sure nobody takes the new name, between the time we + * checked it is currently unused in all search paths, and the + * time we take possession of it */ + r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); + if (r < 0) + return r; + + r = image_find(new_name, NULL); + if (r < 0) + return r; + if (r > 0) + return -EEXIST; + + switch (i->type) { + + case IMAGE_SUBVOLUME: + case IMAGE_DIRECTORY: + /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain + * 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 | BTRFS_SNAPSHOT_QUOTA); + if (r == -EOPNOTSUPP) { + /* No btrfs snapshots supported, create a normal directory then. */ + + r = copy_directory(i->path, new_path, false); + if (r >= 0) + (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); + } else if (r >= 0) + /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(new_path, 0, true); + + break; + + case IMAGE_RAW: + new_path = strjoina("/var/lib/machines/", new_name, ".raw"); + + r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL); + break; + + default: + return -EOPNOTSUPP; + } + + if (r < 0) + return r; + + STRV_FOREACH(j, settings) { + r = clone_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); + } + + return 0; +} + +int image_read_only(Image *i, bool b) { + _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + int r; + assert(i); + + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) + return -EROFS; + + /* Make sure we don't interfere with a running nspawn */ + r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); + if (r < 0) + return r; + + 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; + + break; + + case IMAGE_DIRECTORY: + /* For simple directory trees we cannot use the access + mode of the top-level directory, since it has an + effect on the container itself. However, we can + use the "immutable" flag, to at least make the + top-level directory read-only. It's not as good as + a read-only subvolume, but at least something, and + we can read the value back.*/ + + r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); + if (r < 0) + return r; + + break; + + case IMAGE_RAW: { + struct stat st; + + if (stat(i->path, &st) < 0) + return -errno; + + if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0) + return -errno; + + /* If the images is now read-only, it's a good time to + * defrag it, given that no write patterns will + * fragment it again. */ + if (b) + (void) btrfs_defrag(i->path); + break; + } + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) { + _cleanup_free_ char *p = NULL; + LockFile t = LOCK_FILE_INIT; + struct stat st; + int r; + + assert(path); + assert(global); + assert(local); + + /* Locks an image path. This actually creates two locks: one + * "local" one, next to the image path itself, which might be + * shared via NFS. And another "global" one, in /run, that + * uses the device/inode number. This has the benefit that we + * can even lock a tree that is a mount point, correctly. */ + + if (path_equal(path, "/")) + return -EBUSY; + + if (!path_is_absolute(path)) + return -EINVAL; + + if (stat(path, &st) >= 0) { + if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) + return -ENOMEM; + } + + r = make_lock_file_for(path, operation, &t); + if (r < 0) + return r; + + if (p) { + mkdir_p("/run/systemd/nspawn/locks", 0700); + + r = make_lock_file(p, operation, global); + if (r < 0) { + release_lock_file(&t); + return r; + } + } + + *local = t; + return 0; +} + +int image_set_limit(Image *i, uint64_t referenced_max) { + assert(i); + + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) + return -EROFS; + + if (i->type != IMAGE_SUBVOLUME) + return -EOPNOTSUPP; + + /* 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) { + const char *p; + + assert(name); + assert(ret); + + /* Locks an image name, regardless of the precise path used. */ + + if (!image_name_is_valid(name)) + return -EINVAL; + + if (streq(name, ".host")) + return -EBUSY; + + mkdir_p("/run/systemd/nspawn/locks", 0700); + p = strjoina("/run/systemd/nspawn/locks/name-", name); + + return make_lock_file(p, operation, ret); +} + +bool image_name_is_valid(const char *s) { + if (!filename_is_valid(s)) + return false; + + if (string_has_cc(s, NULL)) + return false; + + if (!utf8_is_valid(s)) + return false; + + /* Temporary files for atomically creating new files */ + if (startswith(s, ".#")) + return false; + + return true; +} + +static const char* const image_type_table[_IMAGE_TYPE_MAX] = { + [IMAGE_DIRECTORY] = "directory", + [IMAGE_SUBVOLUME] = "subvolume", + [IMAGE_RAW] = "raw", +}; + +DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); diff --git a/src/libshared/src/machine-pool.c b/src/libshared/src/machine-pool.c new file mode 100644 index 0000000000..176bc24890 --- /dev/null +++ b/src/libshared/src/machine-pool.c @@ -0,0 +1,427 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/lockfile-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "shared/machine-pool.h" + +#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL) +#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL) + +static int check_btrfs(void) { + struct statfs sfs; + + if (statfs("/var/lib/machines", &sfs) < 0) { + if (errno != ENOENT) + return -errno; + + if (statfs("/var/lib", &sfs) < 0) + return -errno; + } + + return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); +} + +static int setup_machine_raw(uint64_t size, sd_bus_error *error) { + _cleanup_free_ char *tmp = NULL; + _cleanup_close_ int fd = -1; + struct statvfs ss; + pid_t pid = 0; + siginfo_t si; + int r; + + /* We want to be able to make use of btrfs-specific file + * system features, in particular subvolumes, reflinks and + * quota. Hence, if we detect that /var/lib/machines.raw is + * not located on btrfs, let's create a loopback file, place a + * btrfs file system into it, and mount it to + * /var/lib/machines. */ + + fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd >= 0) { + r = fd; + fd = -1; + return r; + } + + if (errno != ENOENT) + return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m"); + + r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp); + if (r < 0) + return r; + + (void) mkdir_p_label("/var/lib", 0755); + fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600); + if (fd < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m"); + + if (fstatvfs(fd, &ss) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m"); + goto fail; + } + + if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines."); + goto fail; + } + + if (ftruncate(fd, size) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m"); + goto fail; + } + + pid = fork(); + if (pid < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m"); + goto fail; + } + + if (pid == 0) { + + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + fd = safe_close(fd); + + execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); + if (errno == ENOENT) + _exit(99); + + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate(pid, &si); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); + goto fail; + } + + pid = 0; + + if (si.si_code != CLD_EXITED) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); + goto fail; + } + if (si.si_status == 99) { + r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); + goto fail; + } + if (si.si_status != 0) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); + goto fail; + } + + r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw"); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m"); + goto fail; + } + + r = fd; + fd = -1; + + return r; + +fail: + unlink_noerrno(tmp); + + if (pid > 1) + kill_and_sigcont(pid, SIGKILL); + + return r; +} + +int setup_machine_directory(uint64_t size, sd_bus_error *error) { + _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT; + struct loop_info64 info = { + .lo_flags = LO_FLAGS_AUTOCLEAR, + }; + _cleanup_close_ int fd = -1, control = -1, loop = -1; + _cleanup_free_ char* loopdev = NULL; + char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL; + bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false; + char buf[FORMAT_BYTES_MAX]; + int r, nr = -1; + + /* btrfs cannot handle file systems < 16M, hence use this as minimum */ + if (size == (uint64_t) -1) + size = VAR_LIB_MACHINES_SIZE_START; + else if (size < 16*1024*1024) + size = 16*1024*1024; + + /* Make sure we only set the directory up once at a time */ + r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file); + if (r < 0) + return r; + + r = check_btrfs(); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m"); + if (r > 0) { + (void) btrfs_subvol_make_label("/var/lib/machines"); + + r = btrfs_quota_enable("/var/lib/machines", true); + if (r < 0) + log_warning_errno(r, "Failed to enable quota 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; + } + + r = mkfs_exists("btrfs"); + if (r == 0) + return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); + if (r < 0) + return r; + + fd = setup_machine_raw(size, error); + if (fd < 0) + return fd; + + control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (control < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m"); + + nr = ioctl(control, LOOP_CTL_GET_FREE); + if (nr < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m"); + + if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) { + r = -ENOMEM; + goto fail; + } + + loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK); + if (loop < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m"); + goto fail; + } + + if (ioctl(loop, LOOP_SET_FD, fd) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m"); + goto fail; + } + + if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m"); + goto fail; + } + + /* We need to make sure the new /var/lib/machines directory + * has an access mode of 0700 at the time it is first made + * available. mkfs will create it with 0755 however. Hence, + * let's mount the directory into an inaccessible directory + * below /tmp first, fix the access mode, and move it to the + * public place then. */ + + if (!mkdtemp(tmpdir)) { + r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m"); + goto fail; + } + tmpdir_made = true; + + mntdir = strjoina(tmpdir, "/mnt"); + if (mkdir(mntdir, 0700) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m"); + goto fail; + } + mntdir_made = true; + + if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m"); + goto fail; + } + mntdir_mounted = true; + + r = btrfs_quota_enable(mntdir, true); + if (r < 0) + log_warning_errno(r, "Failed to enable quota, ignoring: %m"); + + 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; + } + + (void) mkdir_p_label("/var/lib/machines", 0700); + + if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) { + r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m"); + goto fail; + } + + (void) syncfs(fd); + + log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size)); + + (void) umount2(mntdir, MNT_DETACH); + (void) rmdir(mntdir); + (void) rmdir(tmpdir); + + return 1; + +fail: + if (mntdir_mounted) + (void) umount2(mntdir, MNT_DETACH); + + if (mntdir_made) + (void) rmdir(mntdir); + if (tmpdir_made) + (void) rmdir(tmpdir); + + if (loop >= 0) { + (void) ioctl(loop, LOOP_CLR_FD); + loop = safe_close(loop); + } + + if (control >= 0 && nr >= 0) + (void) ioctl(control, LOOP_CTL_REMOVE, nr); + + return r; +} + +static int sync_path(const char *p) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} + +int grow_machine_directory(void) { + char buf[FORMAT_BYTES_MAX]; + struct statvfs a, b; + uint64_t old_size, new_size, max_add; + int r; + + /* Ensure the disk space data is accurate */ + sync_path("/var/lib/machines"); + sync_path("/var/lib/machines.raw"); + + if (statvfs("/var/lib/machines.raw", &a) < 0) + return -errno; + + if (statvfs("/var/lib/machines", &b) < 0) + return -errno; + + /* Don't grow if not enough disk space is available on the host */ + if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN) + return 0; + + /* Don't grow if at least 1/3th of the fs is still free */ + if (b.f_bavail > b.f_blocks / 3) + return 0; + + /* Calculate how much we are willing to add at most */ + max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN; + + /* Calculate the old size */ + old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize; + + /* Calculate the new size as three times the size of what is used right now */ + new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3; + + /* Always, grow at least to the start size */ + if (new_size < VAR_LIB_MACHINES_SIZE_START) + new_size = VAR_LIB_MACHINES_SIZE_START; + + /* If the new size is smaller than the old size, don't grow */ + if (new_size < old_size) + return 0; + + /* Ensure we never add more than the maximum */ + if (new_size > old_size + max_add) + new_size = old_size + max_add; + + r = btrfs_resize_loopback("/var/lib/machines", new_size, true); + if (r <= 0) + return r; + + /* 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/libshared/src/output-mode.c b/src/libshared/src/output-mode.c new file mode 100644 index 0000000000..f482b0fa06 --- /dev/null +++ b/src/libshared/src/output-mode.c @@ -0,0 +1,37 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-table.h" +#include "shared/output-mode.h" + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_ISO] = "short-iso", + [OUTPUT_SHORT_PRECISE] = "short-precise", + [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_SHORT_UNIX] = "short-unix", + [OUTPUT_VERBOSE] = "verbose", + [OUTPUT_EXPORT] = "export", + [OUTPUT_JSON] = "json", + [OUTPUT_JSON_PRETTY] = "json-pretty", + [OUTPUT_JSON_SSE] = "json-sse", + [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/libshared/src/pager.c b/src/libshared/src/pager.c new file mode 100644 index 0000000000..a019cf4ec7 --- /dev/null +++ b/src/libshared/src/pager.c @@ -0,0 +1,226 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "shared/pager.h" + +static pid_t pager_pid = 0; + +noreturn static void pager_fallback(void) { + int r; + + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false); + if (r < 0) { + log_error_errno(r, "Internal pager failed: %m"); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); +} + +int pager_open(bool no_pager, bool jump_to_end) { + _cleanup_close_pair_ int fd[2] = { -1, -1 }; + const char *pager; + pid_t parent_pid; + + if (no_pager) + return 0; + + if (pager_pid > 0) + return 1; + + if (terminal_is_dumb()) + 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 */ + (void) columns(); + + if (pipe(fd) < 0) + return log_error_errno(errno, "Failed to create pager pipe: %m"); + + parent_pid = getpid(); + + pager_pid = fork(); + if (pager_pid < 0) + return log_error_errno(errno, "Failed to fork pager: %m"); + + /* In the child start the pager */ + if (pager_pid == 0) { + const char* less_opts, *less_charset; + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + (void) dup2(fd[0], STDIN_FILENO); + safe_close_pair(fd); + + /* Initialize a good set of less options */ + less_opts = getenv("SYSTEMD_LESS"); + if (!less_opts) + less_opts = "FRSXMK"; + if (jump_to_end) + less_opts = strjoina(less_opts, " +G"); + setenv("LESS", less_opts, 1); + + /* Initialize a good charset for less. This is + * particularly important if we output UTF-8 + * characters. */ + less_charset = getenv("SYSTEMD_LESSCHARSET"); + if (!less_charset && is_locale_utf8()) + less_charset = "utf-8"; + if (less_charset) + setenv("LESSCHARSET", less_charset, 1); + + /* Make sure the pager goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + if (pager) { + execlp(pager, pager, NULL); + execl("/bin/sh", "sh", "-c", pager, NULL); + } + + /* Debian's alternatives command for pagers is + * called 'pager'. Note that we do not call + * sensible-pagers here, since that is just a + * shell script that implements a logic that + * is similar to this one anyway, but is + * Debian-specific. */ + execlp("pager", "pager", NULL); + + execlp("less", "less", NULL); + execlp("more", "more", NULL); + + pager_fallback(); + /* not reached */ + } + + /* Return in the parent */ + if (dup2(fd[1], STDOUT_FILENO) < 0) + return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); + if (dup2(fd[1], STDERR_FILENO) < 0) + return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); + + return 1; +} + +void pager_close(void) { + + if (pager_pid <= 0) + return; + + /* Inform pager that we are done */ + stdout = safe_fclose(stdout); + stderr = safe_fclose(stderr); + + (void) kill(pager_pid, SIGCONT); + (void) wait_for_terminate(pager_pid, NULL); + pager_pid = 0; +} + +bool pager_have(void) { + return pager_pid > 0; +} + +int show_man_page(const char *desc, bool null_stdio) { + const char *args[4] = { "man", NULL, NULL, NULL }; + char *e = NULL; + pid_t pid; + size_t k; + int r; + siginfo_t status; + + k = strlen(desc); + + if (desc[k-1] == ')') + e = strrchr(desc, '('); + + if (e) { + char *page = NULL, *section = NULL; + + page = strndupa(desc, e - desc); + section = strndupa(e + 1, desc + k - e - 2); + + args[1] = section; + args[2] = page; + } else + args[1] = desc; + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + + if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + if (null_stdio) { + r = make_null_stdio(); + if (r < 0) { + log_error_errno(r, "Failed to kill stdio: %m"); + _exit(EXIT_FAILURE); + } + } + + execvp(args[0], (char**) args); + log_error_errno(errno, "Failed to execute man: %m"); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate(pid, &status); + if (r < 0) + return r; + + log_debug("Exit code %i status %i", status.si_code, status.si_status); + return status.si_status; +} diff --git a/src/libshared/src/path-lookup.c b/src/libshared/src/path-lookup.c new file mode 100644 index 0000000000..7c692156f5 --- /dev/null +++ b/src/libshared/src/path-lookup.c @@ -0,0 +1,822 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/install.h" +#include "shared/path-lookup.h" + +static int user_runtime_dir(char **ret, const char *suffix) { + const char *e; + char *j; + + assert(ret); + assert(suffix); + + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; + + j = strappend(e, suffix); + if (!j) + return -ENOMEM; + + *ret = j; + return 0; +} + +static int user_config_dir(char **ret, const char *suffix) { + const char *e; + char *j; + + assert(ret); + + e = getenv("XDG_CONFIG_HOME"); + if (e) + j = strappend(e, suffix); + else { + const char *home; + + home = getenv("HOME"); + if (!home) + return -ENXIO; + + j = strjoin(home, "/.config", suffix, NULL); + } + + if (!j) + return -ENOMEM; + + *ret = j; + return 0; +} + +static int user_data_dir(char **ret, const char *suffix) { + const char *e; + char *j; + + assert(ret); + assert(suffix); + + /* We don't treat /etc/xdg/systemd here as the spec + * suggests because we assume that is a link to + * /etc/systemd/ anyway. */ + + e = getenv("XDG_DATA_HOME"); + if (e) + j = strappend(e, suffix); + else { + const char *home; + + home = getenv("HOME"); + if (!home) + return -ENXIO; + + + j = strjoin(home, "/.local/share", suffix, NULL); + } + if (!j) + return -ENOMEM; + + *ret = j; + return 1; +} + +static char** user_dirs( + const char *persistent_config, + const char *runtime_config, + const char *generator, + const char *generator_early, + const char *generator_late, + const char *transient, + const char *persistent_control, + const char *runtime_control) { + + const char * const config_unit_paths[] = { + USER_CONFIG_UNIT_PATH, + "/etc/systemd/user", + NULL + }; + + const char * const data_unit_paths[] = { + "/usr/local/lib/systemd/user", + "/usr/local/share/systemd/user", + USER_DATA_UNIT_PATH, + "/usr/lib/systemd/user", + "/usr/share/systemd/user", + NULL + }; + + const char *e; + _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; + _cleanup_free_ char *data_home = NULL; + _cleanup_free_ char **res = NULL; + char **tmp; + int r; + + /* Implement the mechanisms defined in + * + * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html + * + * We look in both the config and the data dirs because we + * want to encourage that distributors ship their unit files + * as data, and allow overriding as configuration. + */ + + e = getenv("XDG_CONFIG_DIRS"); + if (e) { + config_dirs = strv_split(e, ":"); + if (!config_dirs) + return NULL; + } + + r = user_data_dir(&data_home, "/systemd/user"); + if (r < 0 && r != -ENXIO) + return NULL; + + e = getenv("XDG_DATA_DIRS"); + if (e) + data_dirs = strv_split(e, ":"); + else + data_dirs = strv_new("/usr/local/share", + "/usr/share", + NULL); + if (!data_dirs) + return NULL; + + /* Now merge everything we found. */ + if (strv_extend(&res, persistent_control) < 0) + return NULL; + + if (strv_extend(&res, runtime_control) < 0) + return NULL; + + if (strv_extend(&res, transient) < 0) + return NULL; + + if (strv_extend(&res, generator_early) < 0) + return NULL; + + if (!strv_isempty(config_dirs)) + if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) + return NULL; + + if (strv_extend(&res, persistent_config) < 0) + return NULL; + + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + return NULL; + + if (strv_extend(&res, runtime_config) < 0) + return NULL; + + if (strv_extend(&res, generator) < 0) + return NULL; + + if (strv_extend(&res, data_home) < 0) + return NULL; + + if (!strv_isempty(data_dirs)) + if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) + return NULL; + + if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) + return NULL; + + if (strv_extend(&res, generator_late) < 0) + return NULL; + + if (path_strv_make_absolute_cwd(res) < 0) + return NULL; + + tmp = res; + res = NULL; + return tmp; +} + +static int acquire_generator_dirs( + UnitFileScope scope, + char **generator, + char **generator_early, + char **generator_late) { + + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; + const char *prefix; + + assert(generator); + assert(generator_early); + assert(generator_late); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + prefix = "/run/systemd/"; + break; + + case UNIT_FILE_USER: { + const char *e; + + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; + + prefix = strjoina(e, "/systemd/"); + break; + } + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + x = strappend(prefix, "generator"); + if (!x) + return -ENOMEM; + + y = strappend(prefix, "generator.early"); + if (!y) + return -ENOMEM; + + z = strappend(prefix, "generator.late"); + if (!z) + return -ENOMEM; + + *generator = x; + *generator_early = y; + *generator_late = z; + + x = y = z = NULL; + return 0; +} + +static int acquire_transient_dir(UnitFileScope scope, char **ret) { + assert(ret); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + char *transient; + + transient = strdup("/run/systemd/transient"); + if (!transient) + return -ENOMEM; + + *ret = transient; + return 0; + } + + case UNIT_FILE_USER: + return user_runtime_dir(ret, "/systemd/transient"); + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } +} + +static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + a = strdup(SYSTEM_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/system"); + break; + + case UNIT_FILE_GLOBAL: + a = strdup(USER_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/user"); + break; + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/user"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/user"); + if (r < 0) + return r; + + *persistent = a; + a = NULL; + + return 0; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + if (!a || !b) + return -ENOMEM; + + *persistent = a; + *runtime = b; + a = b = NULL; + + return 0; +} + +static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + _cleanup_free_ char *b = NULL; + + a = strdup("/etc/systemd/system.control"); + if (!a) + return -ENOMEM; + + b = strdup("/run/systemd/system.control"); + if (!b) + return -ENOMEM; + + *runtime = b; + b = NULL; + + break; + } + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/system.control"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/system.control"); + if (r < 0) + return r; + + break; + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + *persistent = a; + a = NULL; + + return 0; +} + +static int patch_root_prefix(char **p, const char *root_dir) { + char *c; + + assert(p); + + if (!*p) + return 0; + + c = prefix_root(root_dir, *p); + if (!c) + return -ENOMEM; + + free(*p); + *p = c; + + return 0; +} + +static int patch_root_prefix_strv(char **l, const char *root_dir) { + char **i; + int r; + + if (!root_dir) + return 0; + + STRV_FOREACH(i, l) { + r = patch_root_prefix(i, root_dir); + if (r < 0) + return r; + } + + return 0; +} + +int lookup_paths_init( + LookupPaths *p, + UnitFileScope scope, + LookupPathsFlags flags, + const char *root_dir) { + + _cleanup_free_ char + *root = NULL, + *persistent_config = NULL, *runtime_config = NULL, + *generator = NULL, *generator_early = NULL, *generator_late = NULL, + *transient = NULL, + *persistent_control = NULL, *runtime_control = NULL; + bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ + _cleanup_strv_free_ char **paths = NULL; + const char *e; + int r; + + assert(p); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + if (!isempty(root_dir) && !path_equal(root_dir, "/")) { + if (scope == UNIT_FILE_USER) + return -EINVAL; + + r = is_dir(root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; + + root = strdup(root_dir); + if (!root) + return -ENOMEM; + } + + r = acquire_config_dirs(scope, &persistent_config, &runtime_config); + if (r < 0 && r != -ENXIO) + return r; + + if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { + r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + } + + r = acquire_transient_dir(scope, &transient); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + + r = acquire_control_dirs(scope, &persistent_control, &runtime_control); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + + /* First priority is whatever has been passed to us via env vars */ + e = getenv("SYSTEMD_UNIT_PATH"); + if (e) { + const char *k; + + k = endswith(e, ":"); + if (k) { + e = strndupa(e, k - e); + append = true; + } + + /* FIXME: empty components in other places should be + * rejected. */ + + r = path_split_and_make_absolute(e, &paths); + if (r < 0) + return r; + } + + if (!paths || append) { + /* Let's figure something out. */ + + _cleanup_strv_free_ char **add = NULL; + + /* For the user units we include share/ in the search + * path in order to comply with the XDG basedir spec. + * For the system stuff we avoid such nonsense. OTOH + * we include /lib in the search path for the system + * stuff but avoid it for user stuff. */ + + switch (scope) { + + case UNIT_FILE_SYSTEM: + add = strv_new( + /* If you modify this you also want to modify + * systemdsystemunitpath= in systemd.pc.in! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), + STRV_IFNOTNULL(generator_early), + persistent_config, + SYSTEM_CONFIG_UNIT_PATH, + "/etc/systemd/system", + runtime_config, + "/run/systemd/system", + STRV_IFNOTNULL(generator), + "/usr/local/lib/systemd/system", + SYSTEM_DATA_UNIT_PATH, + "/usr/lib/systemd/system", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/system", +#endif + STRV_IFNOTNULL(generator_late), + NULL); + break; + + case UNIT_FILE_GLOBAL: + add = strv_new( + /* If you modify this you also want to modify + * systemduserunitpath= in systemd.pc.in, and + * the arrays in user_dirs() above! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), + STRV_IFNOTNULL(generator_early), + persistent_config, + USER_CONFIG_UNIT_PATH, + "/etc/systemd/user", + runtime_config, + "/run/systemd/user", + STRV_IFNOTNULL(generator), + "/usr/local/lib/systemd/user", + "/usr/local/share/systemd/user", + USER_DATA_UNIT_PATH, + "/usr/lib/systemd/user", + "/usr/share/systemd/user", + STRV_IFNOTNULL(generator_late), + NULL); + break; + + case UNIT_FILE_USER: + add = user_dirs(persistent_config, runtime_config, + generator, generator_early, generator_late, + transient, + persistent_config, runtime_control); + break; + + default: + assert_not_reached("Hmm, unexpected scope?"); + } + + if (!add) + return -ENOMEM; + + if (paths) { + r = strv_extend_strv(&paths, add, true); + if (r < 0) + return r; + } else { + /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, + * and don't have to copy anything */ + paths = add; + add = NULL; + } + } + + r = patch_root_prefix(&persistent_config, root); + if (r < 0) + return r; + r = patch_root_prefix(&runtime_config, root); + if (r < 0) + return r; + + r = patch_root_prefix(&generator, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_early, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_late, root); + if (r < 0) + return r; + + r = patch_root_prefix(&transient, root); + if (r < 0) + return r; + + r = patch_root_prefix(&persistent_control, root); + if (r < 0) + return r; + + r = patch_root_prefix(&runtime_control, root); + if (r < 0) + return r; + + r = patch_root_prefix_strv(paths, root); + if (r < 0) + return -ENOMEM; + + p->search_path = strv_uniq(paths); + paths = NULL; + + p->persistent_config = persistent_config; + p->runtime_config = runtime_config; + persistent_config = runtime_config = NULL; + + p->generator = generator; + p->generator_early = generator_early; + p->generator_late = generator_late; + generator = generator_early = generator_late = NULL; + + p->transient = transient; + transient = NULL; + + p->persistent_control = persistent_control; + p->runtime_control = runtime_control; + persistent_control = runtime_control = NULL; + + p->root_dir = root; + root = NULL; + + return 0; +} + +void lookup_paths_free(LookupPaths *p) { + if (!p) + return; + + p->search_path = strv_free(p->search_path); + + p->persistent_config = mfree(p->persistent_config); + p->runtime_config = mfree(p->runtime_config); + + p->generator = mfree(p->generator); + p->generator_early = mfree(p->generator_early); + p->generator_late = mfree(p->generator_late); + + p->transient = mfree(p->transient); + + p->persistent_control = mfree(p->persistent_control); + p->runtime_control = mfree(p->runtime_control); + + p->root_dir = mfree(p->root_dir); +} + +int lookup_paths_reduce(LookupPaths *p) { + _cleanup_free_ struct stat *stats = NULL; + size_t n_stats = 0, allocated = 0; + unsigned c = 0; + int r; + + assert(p); + + /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are + * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set, + * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into + * account when following symlinks. When we have no root path set this restriction does not apply however. */ + + if (!p->search_path) + return 0; + + while (p->search_path[c]) { + struct stat st; + unsigned k; + + if (p->root_dir) + r = lstat(p->search_path[c], &st); + else + r = stat(p->search_path[c], &st); + if (r < 0) { + if (errno == ENOENT) + goto remove_item; + + /* If something we don't grok happened, let's better leave it in. */ + log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]); + c++; + continue; + } + + for (k = 0; k < n_stats; k++) { + if (stats[k].st_dev == st.st_dev && + stats[k].st_ino == st.st_ino) + break; + } + + if (k < n_stats) /* Is there already an entry with the same device/inode? */ + goto remove_item; + + if (!GREEDY_REALLOC(stats, allocated, n_stats+1)) + return -ENOMEM; + + stats[n_stats++] = st; + c++; + continue; + + remove_item: + free(p->search_path[c]); + memmove(p->search_path + c, + p->search_path + c + 1, + (strv_length(p->search_path + c + 1) + 1) * sizeof(char*)); + } + + if (strv_isempty(p->search_path)) { + log_debug("Ignoring unit files."); + p->search_path = strv_free(p->search_path); + } else { + _cleanup_free_ char *t; + + t = strv_join(p->search_path, "\n\t"); + if (!t) + return -ENOMEM; + + log_debug("Looking for unit files in (higher priority first):\n\t%s", t); + } + + return 0; +} + +int lookup_paths_mkdir_generator(LookupPaths *p) { + int r, q; + + assert(p); + + if (!p->generator || !p->generator_early || !p->generator_late) + return -EINVAL; + + r = mkdir_p_label(p->generator, 0755); + + q = mkdir_p_label(p->generator_early, 0755); + if (q < 0 && r >= 0) + r = q; + + q = mkdir_p_label(p->generator_late, 0755); + if (q < 0 && r >= 0) + r = q; + + return r; +} + +void lookup_paths_trim_generator(LookupPaths *p) { + assert(p); + + /* Trim empty dirs */ + + if (p->generator) + (void) rmdir(p->generator); + if (p->generator_early) + (void) rmdir(p->generator_early); + if (p->generator_late) + (void) rmdir(p->generator_late); +} + +void lookup_paths_flush_generator(LookupPaths *p) { + assert(p); + + /* Flush the generated unit files in full */ + + if (p->generator) + (void) rm_rf(p->generator, REMOVE_ROOT); + if (p->generator_early) + (void) rm_rf(p->generator_early, REMOVE_ROOT); + if (p->generator_late) + (void) rm_rf(p->generator_late, REMOVE_ROOT); +} + +char **generator_binary_paths(UnitFileScope scope) { + + switch (scope) { + + case UNIT_FILE_SYSTEM: + return strv_new("/run/systemd/system-generators", + "/etc/systemd/system-generators", + "/usr/local/lib/systemd/system-generators", + SYSTEM_GENERATOR_PATH, + NULL); + + case UNIT_FILE_GLOBAL: + case UNIT_FILE_USER: + return strv_new("/run/systemd/user-generators", + "/etc/systemd/user-generators", + "/usr/local/lib/systemd/user-generators", + USER_GENERATOR_PATH, + NULL); + + default: + assert_not_reached("Hmm, unexpected scope."); + } +} diff --git a/src/libshared/src/ptyfwd.c b/src/libshared/src/ptyfwd.c new file mode 100644 index 0000000000..cd6a360ae4 --- /dev/null +++ b/src/libshared/src/ptyfwd.c @@ -0,0 +1,484 @@ +/*** + This file is part of systemd. + + Copyright 2010-2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/time-util.h" +#include "shared/ptyfwd.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; + + sd_event_source *sigwinch_event_source; + + struct termios saved_stdin_attr; + struct termios saved_stdout_attr; + + bool saved_stdin:1; + bool saved_stdout:1; + + bool stdin_readable:1; + bool stdin_hangup:1; + bool stdout_writable:1; + bool stdout_hangup:1; + bool master_readable:1; + bool master_writable:1; + bool master_hangup:1; + + bool read_from_master:1; + + bool last_char_set:1; + char last_char; + + char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; + size_t in_buffer_full, out_buffer_full; + + usec_t escape_timestamp; + unsigned escape_counter; +}; + +#define ESCAPE_USEC (1*USEC_PER_SEC) + +static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { + const char *p; + + assert(f); + assert(buffer); + assert(n > 0); + + for (p = buffer; p < buffer + n; p++) { + + /* Check for ^] */ + if (*p == 0x1D) { + usec_t nw = now(CLOCK_MONOTONIC); + + if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) { + f->escape_timestamp = nw; + f->escape_counter = 1; + } else { + (f->escape_counter)++; + + if (f->escape_counter >= 3) + return true; + } + } else { + f->escape_timestamp = 0; + f->escape_counter = 0; + } + } + + 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; + + assert(f); + + while ((f->stdin_readable && f->in_buffer_full <= 0) || + (f->master_writable && f->in_buffer_full > 0) || + (f->master_readable && f->out_buffer_full <= 0) || + (f->stdout_writable && f->out_buffer_full > 0)) { + + if (f->stdin_readable && f->in_buffer_full < LINE_MAX) { + + k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full); + if (k < 0) { + + if (errno == EAGAIN) + f->stdin_readable = false; + else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { + f->stdin_readable = false; + f->stdin_hangup = true; + + f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); + } else { + log_error_errno(errno, "read(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + } else if (k == 0) { + /* EOF on stdin */ + f->stdin_readable = false; + f->stdin_hangup = true; + + f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); + } else { + /* Check if ^] has been + * pressed three times within + * one second. If we get this + * we quite immediately. */ + if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) + return sd_event_exit(f->event, EXIT_FAILURE); + + f->in_buffer_full += (size_t) k; + } + } + + if (f->master_writable && f->in_buffer_full > 0) { + + k = write(f->master, f->in_buffer, f->in_buffer_full); + if (k < 0) { + + if (errno == EAGAIN || errno == EIO) + f->master_writable = false; + else if (errno == EPIPE || errno == ECONNRESET) { + f->master_writable = f->master_readable = false; + f->master_hangup = true; + + f->master_event_source = sd_event_source_unref(f->master_event_source); + } else { + log_error_errno(errno, "write(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + } else { + assert(f->in_buffer_full >= (size_t) k); + memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k); + f->in_buffer_full -= k; + } + } + + if (f->master_readable && f->out_buffer_full < LINE_MAX) { + + k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); + if (k < 0) { + + /* Note that EIO on the master device + * might be caused by vhangup() or + * temporary closing of everything on + * the other side, we treat it like + * EAGAIN here and try again, unless + * ignore_vhangup is off. */ + + 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; + f->master_hangup = true; + + f->master_event_source = sd_event_source_unref(f->master_event_source); + } else { + log_error_errno(errno, "read(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + } else { + f->read_from_master = true; + f->out_buffer_full += (size_t) k; + } + } + + if (f->stdout_writable && f->out_buffer_full > 0) { + + k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full); + if (k < 0) { + + if (errno == EAGAIN) + f->stdout_writable = false; + else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { + f->stdout_writable = false; + f->stdout_hangup = true; + f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); + } else { + log_error_errno(errno, "write(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + + } else { + + if (k > 0) { + f->last_char = f->out_buffer[k-1]; + f->last_char_set = true; + } + + assert(f->out_buffer_full >= (size_t) k); + memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); + f->out_buffer_full -= k; + } + } + } + + if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) { + /* Exit the loop if any side hung up and if there's + * nothing more to write or nothing we could write. */ + + if ((f->out_buffer_full <= 0 || f->stdout_hangup) && + (f->in_buffer_full <= 0 || f->master_hangup)) + return sd_event_exit(f->event, EXIT_SUCCESS); + } + + return 0; +} + +static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; + + assert(f); + assert(e); + assert(e == f->master_event_source); + assert(fd >= 0); + assert(fd == f->master); + + if (revents & (EPOLLIN|EPOLLHUP)) + f->master_readable = true; + + if (revents & (EPOLLOUT|EPOLLHUP)) + f->master_writable = true; + + return shovel(f); +} + +static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; + + assert(f); + assert(e); + assert(e == f->stdin_event_source); + assert(fd >= 0); + assert(fd == STDIN_FILENO); + + if (revents & (EPOLLIN|EPOLLHUP)) + f->stdin_readable = true; + + return shovel(f); +} + +static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; + + assert(f); + assert(e); + assert(e == f->stdout_event_source); + assert(fd >= 0); + assert(fd == STDOUT_FILENO); + + if (revents & (EPOLLOUT|EPOLLHUP)) + f->stdout_writable = true; + + return shovel(f); +} + +static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) { + PTYForward *f = userdata; + struct winsize ws; + + assert(f); + assert(e); + assert(e == f->sigwinch_event_source); + + /* The window size changed, let's forward that. */ + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) + (void) ioctl(f->master, TIOCSWINSZ, &ws); + + return 0; +} + +int pty_forward_new( + sd_event *event, + int master, + PTYForwardFlags flags, + PTYForward **ret) { + + _cleanup_(pty_forward_freep) PTYForward *f = NULL; + struct winsize ws; + int r; + + f = new0(PTYForward, 1); + if (!f) + return -ENOMEM; + + f->flags = flags; + + if (event) + f->event = sd_event_ref(event); + else { + r = sd_event_default(&f->event); + if (r < 0) + return r; + } + + if (!(flags & PTY_FORWARD_READ_ONLY)) { + r = fd_nonblock(STDIN_FILENO, true); + if (r < 0) + return r; + + r = fd_nonblock(STDOUT_FILENO, true); + if (r < 0) + return r; + } + + r = fd_nonblock(master, true); + if (r < 0) + return r; + + f->master = master; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) + (void) ioctl(master, TIOCSWINSZ, &ws); + + if (!(flags & PTY_FORWARD_READ_ONLY)) { + if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) { + struct termios raw_stdin_attr; + + f->saved_stdin = true; + + raw_stdin_attr = f->saved_stdin_attr; + cfmakeraw(&raw_stdin_attr); + raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; + tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr); + } + + if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) { + struct termios raw_stdout_attr; + + f->saved_stdout = true; + + raw_stdout_attr = f->saved_stdout_attr; + cfmakeraw(&raw_stdout_attr); + raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag; + raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag; + tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr); + } + + r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f); + if (r < 0 && r != -EPERM) + return r; + } + + r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f); + if (r == -EPERM) + /* stdout without epoll support. Likely redirected to regular file. */ + f->stdout_writable = true; + else if (r < 0) + return r; + + r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f); + if (r < 0) + return r; + + r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f); + if (r < 0) + return r; + + *ret = f; + f = NULL; + + return 0; +} + +PTYForward *pty_forward_free(PTYForward *f) { + + if (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) + tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr); + if (f->saved_stdin) + tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr); + + free(f); + } + + /* STDIN/STDOUT should not be nonblocking normally, so let's + * unconditionally reset it */ + fd_nonblock(STDIN_FILENO, false); + fd_nonblock(STDOUT_FILENO, false); + + return NULL; +} + +int pty_forward_get_last_char(PTYForward *f, char *ch) { + assert(f); + assert(ch); + + if (!f->last_char_set) + return -ENXIO; + + *ch = f->last_char; + return 0; +} + +int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { + int r; + + assert(f); + + if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) + return 0; + + SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b); + + if (!ignore_vhangup(f)) { + + /* We shall now react to vhangup()s? Let's check + * immediately if we might be in one */ + + f->master_readable = true; + r = shovel(f); + if (r < 0) + return r; + } + + return 0; +} + +int pty_forward_get_ignore_vhangup(PTYForward *f) { + assert(f); + + return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP); +} diff --git a/src/libshared/src/resolve-util.c b/src/libshared/src/resolve-util.c new file mode 100644 index 0000000000..1f031e566d --- /dev/null +++ b/src/libshared/src/resolve-util.c @@ -0,0 +1,39 @@ +/*** + 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 . +***/ + +#include "basic/string-table.h" +#include "shared/conf-parser.h" +#include "shared/resolve-util.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/libshared/src/seccomp-util.c b/src/libshared/src/seccomp-util.c new file mode 100644 index 0000000000..6417df1e88 --- /dev/null +++ b/src/libshared/src/seccomp-util.c @@ -0,0 +1,323 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/macro.h" +#include "basic/string-util.h" +#include "shared/seccomp-util.h" + +const char* seccomp_arch_to_string(uint32_t c) { + + if (c == SCMP_ARCH_NATIVE) + return "native"; + if (c == SCMP_ARCH_X86) + return "x86"; + if (c == SCMP_ARCH_X86_64) + return "x86-64"; + if (c == SCMP_ARCH_X32) + return "x32"; + if (c == SCMP_ARCH_ARM) + return "arm"; + + return NULL; +} + +int seccomp_arch_from_string(const char *n, uint32_t *ret) { + if (!n) + return -EINVAL; + + assert(ret); + + if (streq(n, "native")) + *ret = SCMP_ARCH_NATIVE; + else if (streq(n, "x86")) + *ret = SCMP_ARCH_X86; + else if (streq(n, "x86-64")) + *ret = SCMP_ARCH_X86_64; + else if (streq(n, "x32")) + *ret = SCMP_ARCH_X32; + else if (streq(n, "arm")) + *ret = SCMP_ARCH_ARM; + else + return -EINVAL; + + return 0; +} + +int seccomp_add_secondary_archs(scmp_filter_ctx *c) { + +#if defined(__i386__) || defined(__x86_64__) + int r; + + /* Add in all possible secondary archs we are aware of that + * this kernel might support. */ + + r = seccomp_arch_add(c, SCMP_ARCH_X86); + if (r < 0 && r != -EEXIST) + return r; + + r = seccomp_arch_add(c, SCMP_ARCH_X86_64); + if (r < 0 && r != -EEXIST) + return r; + + r = seccomp_arch_add(c, SCMP_ARCH_X32); + if (r < 0 && r != -EEXIST) + return r; + +#endif + + return 0; + +} + +const SystemCallFilterSet syscall_filter_sets[] = { + { + /* Clock */ + .set_name = "@clock", + .value = + "adjtimex\0" + "clock_adjtime\0" + "clock_settime\0" + "settimeofday\0" + "stime\0" + }, { + /* CPU emulation calls */ + .set_name = "@cpu-emulation", + .value = + "modify_ldt\0" + "subpage_prot\0" + "switch_endian\0" + "vm86\0" + "vm86old\0" + }, { + /* Debugging/Performance Monitoring/Tracing */ + .set_name = "@debug", + .value = + "lookup_dcookie\0" + "perf_event_open\0" + "process_vm_readv\0" + "process_vm_writev\0" + "ptrace\0" + "rtas\0" + "s390_runtime_instr\0" + "sys_debug_setcontext\0" + }, { + /* Default list */ + .set_name = "@default", + .value = + "execve\0" + "exit\0" + "exit_group\0" + "rt_sigreturn\0" + "sigreturn\0" + }, { + /* Event loop use */ + .set_name = "@io-event", + .value = + "_newselect\0" + "epoll_create1\0" + "epoll_create\0" + "epoll_ctl\0" + "epoll_ctl_old\0" + "epoll_pwait\0" + "epoll_wait\0" + "epoll_wait_old\0" + "eventfd2\0" + "eventfd\0" + "poll\0" + "ppoll\0" + "pselect6\0" + "select\0" + }, { + /* Message queues, SYSV IPC or other IPC: unusual */ + .set_name = "@ipc", + .value = "ipc\0" + "mq_getsetattr\0" + "mq_notify\0" + "mq_open\0" + "mq_timedreceive\0" + "mq_timedsend\0" + "mq_unlink\0" + "msgctl\0" + "msgget\0" + "msgrcv\0" + "msgsnd\0" + "process_vm_readv\0" + "process_vm_writev\0" + "semctl\0" + "semget\0" + "semop\0" + "semtimedop\0" + "shmat\0" + "shmctl\0" + "shmdt\0" + "shmget\0" + }, { + /* Keyring */ + .set_name = "@keyring", + .value = + "add_key\0" + "keyctl\0" + "request_key\0" + }, { + /* Kernel module control */ + .set_name = "@module", + .value = + "delete_module\0" + "finit_module\0" + "init_module\0" + }, { + /* Mounting */ + .set_name = "@mount", + .value = + "chroot\0" + "mount\0" + "oldumount\0" + "pivot_root\0" + "umount2\0" + "umount\0" + }, { + /* Network or Unix socket IO, should not be needed if not network facing */ + .set_name = "@network-io", + .value = + "accept4\0" + "accept\0" + "bind\0" + "connect\0" + "getpeername\0" + "getsockname\0" + "getsockopt\0" + "listen\0" + "recv\0" + "recvfrom\0" + "recvmmsg\0" + "recvmsg\0" + "send\0" + "sendmmsg\0" + "sendmsg\0" + "sendto\0" + "setsockopt\0" + "shutdown\0" + "socket\0" + "socketcall\0" + "socketpair\0" + }, { + /* Unusual, obsolete or unimplemented, some unknown even to libseccomp */ + .set_name = "@obsolete", + .value = + "_sysctl\0" + "afs_syscall\0" + "break\0" + "create_module\0" + "ftime\0" + "get_kernel_syms\0" + "getpmsg\0" + "gtty\0" + "lock\0" + "mpx\0" + "prof\0" + "profil\0" + "putpmsg\0" + "query_module\0" + "security\0" + "sgetmask\0" + "ssetmask\0" + "stty\0" + "sysfs\0" + "tuxcall\0" + "ulimit\0" + "uselib\0" + "ustat\0" + "vserver\0" + }, { + /* Nice grab-bag of all system calls which need superuser capabilities */ + .set_name = "@privileged", + .value = + "@clock\0" + "@module\0" + "@raw-io\0" + "acct\0" + "bdflush\0" + "bpf\0" + "capset\0" + "chown32\0" + "chown\0" + "chroot\0" + "fchown32\0" + "fchown\0" + "fchownat\0" + "kexec_file_load\0" + "kexec_load\0" + "lchown32\0" + "lchown\0" + "nfsservctl\0" + "pivot_root\0" + "quotactl\0" + "reboot\0" + "setdomainname\0" + "setfsuid32\0" + "setfsuid\0" + "setgroups32\0" + "setgroups\0" + "sethostname\0" + "setresuid32\0" + "setresuid\0" + "setreuid32\0" + "setreuid\0" + "setuid32\0" + "setuid\0" + "swapoff\0" + "swapon\0" + "sysctl\0" + "vhangup\0" + }, { + /* Process control, execution, namespaces */ + .set_name = "@process", + .value = + "arch_prctl\0" + "clone\0" + "execve\0" + "execveat\0" + "fork\0" + "kill\0" + "prctl\0" + "setns\0" + "tgkill\0" + "tkill\0" + "unshare\0" + "vfork\0" + }, { + /* Raw I/O ports */ + .set_name = "@raw-io", + .value = + "ioperm\0" + "iopl\0" + "pciconfig_iobase\0" + "pciconfig_read\0" + "pciconfig_write\0" + "s390_pci_mmio_read\0" + "s390_pci_mmio_write\0" + }, { + .set_name = NULL, + .value = NULL + } +}; diff --git a/src/libshared/src/sleep-config.c b/src/libshared/src/sleep-config.c new file mode 100644 index 0000000000..fd33763f68 --- /dev/null +++ b/src/libshared/src/sleep-config.c @@ -0,0 +1,278 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/conf-parser.h" +#include "shared/sleep-config.h" + +#define USE(x, y) do { (x) = (y); (y) = NULL; } while (0) + +int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { + + _cleanup_strv_free_ char + **suspend_mode = NULL, **suspend_state = NULL, + **hibernate_mode = NULL, **hibernate_state = NULL, + **hybrid_mode = NULL, **hybrid_state = NULL; + char **modes, **states; + + const ConfigTableItem items[] = { + { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, + { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state }, + { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode }, + { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state }, + { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode }, + { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state }, + {} + }; + + config_parse_many(PKGSYSCONFDIR "/sleep.conf", + CONF_PATHS_NULSTR("systemd/sleep.conf.d"), + "Sleep\0", config_item_table_lookup, items, + false, NULL); + + if (streq(verb, "suspend")) { + /* empty by default */ + USE(modes, suspend_mode); + + if (suspend_state) + USE(states, suspend_state); + else + states = strv_new("mem", "standby", "freeze", NULL); + + } else if (streq(verb, "hibernate")) { + if (hibernate_mode) + USE(modes, hibernate_mode); + else + modes = strv_new("platform", "shutdown", NULL); + + if (hibernate_state) + USE(states, hibernate_state); + else + states = strv_new("disk", NULL); + + } else if (streq(verb, "hybrid-sleep")) { + if (hybrid_mode) + USE(modes, hybrid_mode); + else + modes = strv_new("suspend", "platform", "shutdown", NULL); + + if (hybrid_state) + USE(states, hybrid_state); + else + states = strv_new("disk", NULL); + + } else + assert_not_reached("what verb"); + + if ((!modes && !streq(verb, "suspend")) || !states) { + strv_free(modes); + strv_free(states); + return log_oom(); + } + + *_modes = modes; + *_states = states; + return 0; +} + +int can_sleep_state(char **types) { + char **type; + int r; + _cleanup_free_ char *p = NULL; + + if (strv_isempty(types)) + return true; + + /* If /sys is read-only we cannot sleep */ + if (access("/sys/power/state", W_OK) < 0) + return false; + + r = read_one_line_file("/sys/power/state", &p); + if (r < 0) + return false; + + STRV_FOREACH(type, types) { + const char *word, *state; + size_t l, k; + + k = strlen(*type); + FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) + if (l == k && memcmp(word, *type, l) == 0) + return true; + } + + return false; +} + +int can_sleep_disk(char **types) { + char **type; + int r; + _cleanup_free_ char *p = NULL; + + if (strv_isempty(types)) + return true; + + /* If /sys is read-only we cannot sleep */ + if (access("/sys/power/disk", W_OK) < 0) + return false; + + r = read_one_line_file("/sys/power/disk", &p); + if (r < 0) + return false; + + STRV_FOREACH(type, types) { + const char *word, *state; + size_t l, k; + + k = strlen(*type); + FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) { + if (l == k && memcmp(word, *type, l) == 0) + return true; + + if (l == k + 2 && + word[0] == '[' && + memcmp(word + 1, *type, l - 2) == 0 && + word[l-1] == ']') + return true; + } + } + + return false; +} + +#define HIBERNATION_SWAP_THRESHOLD 0.98 + +static int hibernation_partition_size(size_t *size, size_t *used) { + _cleanup_fclose_ FILE *f; + unsigned i; + + assert(size); + assert(used); + + f = fopen("/proc/swaps", "re"); + if (!f) { + log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + "Failed to retrieve open /proc/swaps: %m"); + assert(errno > 0); + return -errno; + } + + (void) fscanf(f, "%*s %*s %*s %*s %*s\n"); + + for (i = 1;; i++) { + _cleanup_free_ char *dev = NULL, *type = NULL; + size_t size_field, used_field; + int k; + + k = fscanf(f, + "%ms " /* device/file */ + "%ms " /* type of swap */ + "%zu " /* swap size */ + "%zu " /* used */ + "%*i\n", /* priority */ + &dev, &type, &size_field, &used_field); + if (k != 4) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u", i); + continue; + } + + if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) { + log_warning("Ignoring deleted swapfile '%s'.", dev); + continue; + } + + *size = size_field; + *used = used_field; + return 0; + } + + log_debug("No swap partitions were found."); + return -ENOSYS; +} + +static bool enough_memory_for_hibernation(void) { + _cleanup_free_ char *active = NULL; + unsigned long long act = 0; + size_t size = 0, used = 0; + int r; + + if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) + return true; + + r = hibernation_partition_size(&size, &used); + if (r < 0) + return false; + + 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; + } + + r = safe_atollu(active, &act); + if (r < 0) { + log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", + active); + return false; + } + + r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; + log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", + r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); + + return r; +} + +int can_sleep(const char *verb) { + _cleanup_strv_free_ char **modes = NULL, **states = NULL; + int r; + + assert(streq(verb, "suspend") || + streq(verb, "hibernate") || + streq(verb, "hybrid-sleep")); + + r = parse_sleep_config(verb, &modes, &states); + if (r < 0) + return false; + + if (!can_sleep_state(states) || !can_sleep_disk(modes)) + return false; + + return streq(verb, "suspend") || enough_memory_for_hibernation(); +} diff --git a/src/libshared/src/spawn-ask-password-agent.c b/src/libshared/src/spawn-ask-password-agent.c new file mode 100644 index 0000000000..6c8aafcfb1 --- /dev/null +++ b/src/libshared/src/spawn-ask-password-agent.c @@ -0,0 +1,62 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/log.h" +#include "basic/process-util.h" +#include "basic/util.h" +#include "shared/spawn-ask-password-agent.h" + +static pid_t agent_pid = 0; + +int ask_password_agent_open(void) { + int r; + + if (agent_pid > 0) + return 0; + + /* We check STDIN here, not STDOUT, since this is about input, + * not output */ + if (!isatty(STDIN_FILENO)) + return 0; + + r = fork_agent(&agent_pid, + NULL, 0, + SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, + SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); + if (r < 0) + return log_error_errno(r, "Failed to fork TTY ask password agent: %m"); + + return 1; +} + +void ask_password_agent_close(void) { + + if (agent_pid <= 0) + return; + + /* Inform agent that we are done */ + (void) kill(agent_pid, SIGTERM); + (void) kill(agent_pid, SIGCONT); + (void) wait_for_terminate(agent_pid, NULL); + agent_pid = 0; +} diff --git a/src/libshared/src/spawn-polkit-agent.c b/src/libshared/src/spawn-polkit-agent.c new file mode 100644 index 0000000000..cb191fb6c5 --- /dev/null +++ b/src/libshared/src/spawn-polkit-agent.c @@ -0,0 +1,102 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/time-util.h" +#include "basic/util.h" +#include "shared/spawn-polkit-agent.h" + +#ifdef ENABLE_POLKIT +static pid_t agent_pid = 0; + +int polkit_agent_open(void) { + int r; + int pipe_fd[2]; + char notify_fd[DECIMAL_STR_MAX(int) + 1]; + + if (agent_pid > 0) + return 0; + + /* Clients that run as root don't need to activate/query polkit */ + if (geteuid() == 0) + return 0; + + /* We check STDIN here, not STDOUT, since this is about input, + * not output */ + if (!isatty(STDIN_FILENO)) + return 0; + + if (pipe2(pipe_fd, 0) < 0) + return -errno; + + xsprintf(notify_fd, "%i", pipe_fd[1]); + + r = fork_agent(&agent_pid, + &pipe_fd[1], 1, + POLKIT_AGENT_BINARY_PATH, + POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback", NULL); + + /* Close the writing side, because that's the one for the agent */ + safe_close(pipe_fd[1]); + + if (r < 0) + log_error_errno(r, "Failed to fork TTY ask password agent: %m"); + else + /* Wait until the agent closes the fd */ + fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); + + safe_close(pipe_fd[0]); + + return r; +} + +void polkit_agent_close(void) { + + if (agent_pid <= 0) + return; + + /* Inform agent that we are done */ + (void) kill(agent_pid, SIGTERM); + (void) kill(agent_pid, SIGCONT); + + (void) wait_for_terminate(agent_pid, NULL); + agent_pid = 0; +} + +#else + +int polkit_agent_open(void) { + return 0; +} + +void polkit_agent_close(void) { +} + +#endif diff --git a/src/libshared/src/specifier.c b/src/libshared/src/specifier.c new file mode 100644 index 0000000000..42bd306e00 --- /dev/null +++ b/src/libshared/src/specifier.c @@ -0,0 +1,188 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "shared/specifier.h" + +/* + * Generic infrastructure for replacing %x style specifiers in + * strings. Will call a callback for each replacement. + * + */ + +int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) { + char *ret, *t; + const char *f; + bool percent = false; + size_t l; + int r; + + assert(text); + assert(table); + + l = strlen(text); + ret = new(char, l+1); + if (!ret) + return -ENOMEM; + + t = ret; + + for (f = text; *f; f++, l--) { + + if (percent) { + if (*f == '%') + *(t++) = '%'; + else { + const Specifier *i; + + for (i = table; i->specifier; i++) + if (i->specifier == *f) + break; + + if (i->lookup) { + _cleanup_free_ char *w = NULL; + char *n; + size_t k, j; + + r = i->lookup(i->specifier, i->data, userdata, &w); + if (r < 0) { + free(ret); + return r; + } + + j = t - ret; + k = strlen(w); + + n = new(char, j + k + l + 1); + if (!n) { + free(ret); + return -ENOMEM; + } + + memcpy(n, ret, j); + memcpy(n + j, w, k); + + free(ret); + + ret = n; + t = n + j + k; + } else { + *(t++) = '%'; + *(t++) = *f; + } + } + + percent = false; + } else if (*f == '%') + percent = true; + else + *(t++) = *f; + } + + *t = 0; + *_ret = ret; + return 0; +} + +/* Generic handler for simple string replacements */ + +int specifier_string(char specifier, void *data, void *userdata, char **ret) { + char *n; + + n = strdup(strempty(data)); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) { + sd_id128_t id; + char *n; + int r; + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + n = new(char, 33); + if (!n) + return -ENOMEM; + + *ret = sd_id128_to_string(id, n); + return 0; +} + +int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) { + sd_id128_t id; + char *n; + int r; + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + n = new(char, 33); + if (!n) + return -ENOMEM; + + *ret = sd_id128_to_string(id, n); + return 0; +} + +int specifier_host_name(char specifier, void *data, void *userdata, char **ret) { + char *n; + + n = gethostname_malloc(); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} + +int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) { + struct utsname uts; + char *n; + int r; + + r = uname(&uts); + if (r < 0) + return -errno; + + n = strdup(uts.release); + if (!n) + return -ENOMEM; + + *ret = n; + return 0; +} diff --git a/src/libshared/src/switch-root.c b/src/libshared/src/switch-root.c new file mode 100644 index 0000000000..d41a114ae1 --- /dev/null +++ b/src/libshared/src/switch-root.c @@ -0,0 +1,156 @@ +/*** + This file is part of systemd. + + Copyright 2012 Harald Hoyer, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/base-filesystem.h" +#include "shared/switch-root.h" + +int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) { + + /* Don't try to unmount/move the old "/", there's no way to do it. */ + static const char move_mounts[] = + "/dev\0" + "/proc\0" + "/sys\0" + "/run\0"; + + _cleanup_close_ int old_root_fd = -1; + struct stat new_root_stat; + bool old_root_remove; + const char *i, *temporary_old_root; + + if (path_equal(new_root, "/")) + return 0; + + temporary_old_root = strjoina(new_root, oldroot); + mkdir_p_label(temporary_old_root, 0755); + + old_root_remove = in_initrd(); + + if (stat(new_root, &new_root_stat) < 0) + return log_error_errno(errno, "Failed to stat directory %s: %m", new_root); + + /* Work-around for kernel design: the kernel refuses switching + * root if any file systems are mounted MS_SHARED. Hence + * remount them MS_PRIVATE here as a work-around. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ + if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) + log_warning_errno(errno, "Failed to make \"/\" private mount: %m"); + + NULSTR_FOREACH(i, move_mounts) { + char new_mount[PATH_MAX]; + struct stat sb; + + xsprintf(new_mount, "%s%s", new_root, i); + + mkdir_p_label(new_mount, 0755); + + if ((stat(new_mount, &sb) < 0) || + sb.st_dev != new_root_stat.st_dev) { + + /* Mount point seems to be mounted already or + * stat failed. Unmount the old mount + * point. */ + if (umount2(i, MNT_DETACH) < 0) + log_warning_errno(errno, "Failed to unmount %s: %m", i); + continue; + } + + if (mount(i, new_mount, NULL, mountflags, NULL) < 0) { + if (mountflags & MS_MOVE) { + log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); + + if (umount2(i, MNT_FORCE) < 0) + log_warning_errno(errno, "Failed to unmount %s: %m", i); + } + if (mountflags & MS_BIND) + log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount); + + } + } + + /* Do not fail, if base_filesystem_create() fails. Not all + * switch roots are like base_filesystem_create() wants them + * to look like. They might even boot, if they are RO and + * don't have the FS layout. Just ignore the error and + * switch_root() nevertheless. */ + (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID); + + if (chdir(new_root) < 0) + return log_error_errno(errno, "Failed to change directory to %s: %m", new_root); + + if (old_root_remove) { + old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); + if (old_root_fd < 0) + log_warning_errno(errno, "Failed to open root directory: %m"); + } + + /* We first try a pivot_root() so that we can umount the old + * root dir. In many cases (i.e. where rootfs is /), that's + * not possible however, and hence we simply overmount root */ + if (pivot_root(new_root, temporary_old_root) >= 0) { + + /* Immediately get rid of the old root, if detach_oldroot is set. + * Since we are running off it we need to do this lazily. */ + if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0) + log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m", + oldroot, + errno == ENOENT ? "ignoring" : "leaving it around"); + + } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) + return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root); + + if (chroot(".") < 0) + return log_error_errno(errno, "Failed to change root: %m"); + + if (chdir("/") < 0) + return log_error_errno(errno, "Failed to change directory: %m"); + + if (old_root_fd >= 0) { + struct stat rb; + + if (fstat(old_root_fd, &rb) < 0) + log_warning_errno(errno, "Failed to stat old root directory, leaving: %m"); + else { + (void) rm_rf_children(old_root_fd, 0, &rb); + old_root_fd = -1; + } + } + + return 0; +} diff --git a/src/libshared/src/sysctl-util.c b/src/libshared/src/sysctl-util.c new file mode 100644 index 0000000000..5dfe32a16b --- /dev/null +++ b/src/libshared/src/sysctl-util.c @@ -0,0 +1,73 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "shared/sysctl-util.h" + +char *sysctl_normalize(char *s) { + char *n; + + n = strpbrk(s, "/."); + /* If the first separator is a slash, the path is + * assumed to be normalized and slashes remain slashes + * and dots remains dots. */ + if (!n || *n == '/') + return s; + + /* Otherwise, dots become slashes and slashes become + * dots. Fun. */ + while (n) { + if (*n == '.') + *n = '/'; + else + *n = '.'; + + n = strpbrk(n + 1, "/."); + } + + return s; +} + +int sysctl_write(const char *property, const char *value) { + char *p; + + assert(property); + assert(value); + + log_debug("Setting '%s' to '%s'", property, value); + + p = strjoina("/proc/sys/", property); + return write_string_file(p, value, 0); +} + +int sysctl_read(const char *property, char **content) { + char *p; + + assert(property); + assert(content); + + p = strjoina("/proc/sys/", property); + return read_full_file(p, content, NULL); +} diff --git a/src/libshared/src/tests.c b/src/libshared/src/tests.c new file mode 100644 index 0000000000..37f57ca18c --- /dev/null +++ b/src/libshared/src/tests.c @@ -0,0 +1,33 @@ +/*** + 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 . +***/ + +#include + +#include "basic/util.h" +#include "shared/tests.h" + +char* setup_fake_runtime_dir(void) { + char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; + + assert_se(mkdtemp(t)); + assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0); + assert_se(p = strdup(t)); + + return p; +} diff --git a/src/libshared/src/uid-range.c b/src/libshared/src/uid-range.c new file mode 100644 index 0000000000..22cac34c6e --- /dev/null +++ b/src/libshared/src/uid-range.c @@ -0,0 +1,208 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/macro.h" +#include "basic/user-util.h" +#include "shared/uid-range.h" + +static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) { + assert(range); + + return range->start <= start + nr && + range->start + range->nr >= start; +} + +static void uid_range_coalesce(UidRange **p, unsigned *n) { + unsigned i, j; + + assert(p); + assert(n); + + for (i = 0; i < *n; i++) { + for (j = i + 1; j < *n; j++) { + UidRange *x = (*p)+i, *y = (*p)+j; + + if (uid_range_intersect(x, y->start, y->nr)) { + uid_t begin, end; + + begin = MIN(x->start, y->start); + end = MAX(x->start + x->nr, y->start + y->nr); + + x->start = begin; + x->nr = end - begin; + + if (*n > j+1) + memmove(y, y+1, sizeof(UidRange) * (*n - j -1)); + + (*n)--; + j--; + } + } + } + +} + +static int uid_range_compare(const void *a, const void *b) { + const UidRange *x = a, *y = b; + + if (x->start < y->start) + return -1; + if (x->start > y->start) + return 1; + + if (x->nr < y->nr) + return -1; + if (x->nr > y->nr) + return 1; + + return 0; +} + +int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) { + bool found = false; + UidRange *x; + unsigned i; + + assert(p); + assert(n); + + if (nr <= 0) + return 0; + + for (i = 0; i < *n; i++) { + x = (*p) + i; + if (uid_range_intersect(x, start, nr)) { + found = true; + break; + } + } + + if (found) { + uid_t begin, end; + + begin = MIN(x->start, start); + end = MAX(x->start + x->nr, start + nr); + + x->start = begin; + x->nr = end - begin; + } else { + UidRange *t; + + t = realloc(*p, sizeof(UidRange) * (*n + 1)); + if (!t) + return -ENOMEM; + + *p = t; + x = t + ((*n) ++); + + x->start = start; + x->nr = nr; + } + + qsort(*p, *n, sizeof(UidRange), uid_range_compare); + uid_range_coalesce(p, n); + + return *n; +} + +int uid_range_add_str(UidRange **p, unsigned *n, const char *s) { + uid_t start, nr; + const char *t; + int r; + + assert(p); + assert(n); + assert(s); + + t = strchr(s, '-'); + if (t) { + char *b; + uid_t end; + + b = strndupa(s, t - s); + r = parse_uid(b, &start); + if (r < 0) + return r; + + r = parse_uid(t+1, &end); + if (r < 0) + return r; + + if (end < start) + return -EINVAL; + + nr = end - start + 1; + } else { + r = parse_uid(s, &start); + if (r < 0) + return r; + + nr = 1; + } + + return uid_range_add(p, n, start, nr); +} + +int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { + uid_t closest = UID_INVALID, candidate; + unsigned i; + + assert(p); + assert(uid); + + candidate = *uid - 1; + + for (i = 0; i < n; i++) { + uid_t begin, end; + + begin = p[i].start; + end = p[i].start + p[i].nr - 1; + + if (candidate >= begin && candidate <= end) { + *uid = candidate; + return 1; + } + + if (end < candidate) + closest = end; + } + + if (closest == UID_INVALID) + return -EBUSY; + + *uid = closest; + return 1; +} + +bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) { + unsigned i; + + assert(p); + assert(uid); + + for (i = 0; i < n; i++) + if (uid >= p[i].start && uid < p[i].start + p[i].nr) + return true; + + return false; +} diff --git a/src/libshared/src/utmp-wtmp.c b/src/libshared/src/utmp-wtmp.c new file mode 100644 index 0000000000..f10d7e9008 --- /dev/null +++ b/src/libshared/src/utmp-wtmp.c @@ -0,0 +1,445 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/time-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/utmp-wtmp.h" + +int utmp_get_runlevel(int *runlevel, int *previous) { + struct utmpx *found, lookup = { .ut_type = RUN_LVL }; + int r; + const char *e; + + assert(runlevel); + + /* If these values are set in the environment this takes + * precedence. Presumably, sysvinit does this to work around a + * race condition that would otherwise exist where we'd always + * go to disk and hence might read runlevel data that might be + * very new and does not apply to the current script being + * executed. */ + + e = getenv("RUNLEVEL"); + if (e && e[0] > 0) { + *runlevel = e[0]; + + if (previous) { + /* $PREVLEVEL seems to be an Upstart thing */ + + e = getenv("PREVLEVEL"); + if (e && e[0] > 0) + *previous = e[0]; + else + *previous = 0; + } + + return 0; + } + + if (utmpxname(_PATH_UTMPX) < 0) + return -errno; + + setutxent(); + + found = getutxid(&lookup); + if (!found) + r = -errno; + else { + int a, b; + + a = found->ut_pid & 0xFF; + b = (found->ut_pid >> 8) & 0xFF; + + *runlevel = a; + if (previous) + *previous = b; + + r = 0; + } + + endutxent(); + + return r; +} + +static void init_timestamp(struct utmpx *store, usec_t t) { + assert(store); + + if (t <= 0) + t = now(CLOCK_REALTIME); + + store->ut_tv.tv_sec = t / USEC_PER_SEC; + store->ut_tv.tv_usec = t % USEC_PER_SEC; +} + +static void init_entry(struct utmpx *store, usec_t t) { + struct utsname uts = {}; + + assert(store); + + init_timestamp(store, t); + + if (uname(&uts) >= 0) + strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); + + strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */ + strncpy(store->ut_id, "~~", sizeof(store->ut_id)); +} + +static int write_entry_utmp(const struct utmpx *store) { + int r; + + assert(store); + + /* utmp is similar to wtmp, but there is only one entry for + * each entry type resp. user; i.e. basically a key/value + * table. */ + + if (utmpxname(_PATH_UTMPX) < 0) + return -errno; + + setutxent(); + + if (!pututxline(store)) + r = -errno; + else + r = 0; + + endutxent(); + + return r; +} + +static int write_entry_wtmp(const struct utmpx *store) { + assert(store); + + /* wtmp is a simple append-only file where each entry is + simply appended to the end; i.e. basically a log. */ + + errno = 0; + updwtmpx(_PATH_WTMPX, store); + return -errno; +} + +static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { + int r, s; + + r = write_entry_utmp(store_utmp); + s = write_entry_wtmp(store_wtmp); + + if (r >= 0) + r = s; + + /* If utmp/wtmp have been disabled, that's a good thing, hence + * ignore the errors */ + if (r == -ENOENT) + r = 0; + + return r; +} + +static int write_entry_both(const struct utmpx *store) { + return write_utmp_wtmp(store, store); +} + +int utmp_put_shutdown(void) { + struct utmpx store = {}; + + init_entry(&store, 0); + + store.ut_type = RUN_LVL; + strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +int utmp_put_reboot(usec_t t) { + struct utmpx store = {}; + + init_entry(&store, t); + + store.ut_type = BOOT_TIME; + strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +_pure_ static const char *sanitize_id(const char *id) { + size_t l; + + assert(id); + l = strlen(id); + + if (l <= sizeof(((struct utmpx*) NULL)->ut_id)) + return id; + + return id + l - sizeof(((struct utmpx*) NULL)->ut_id); +} + +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { + struct utmpx store = { + .ut_type = INIT_PROCESS, + .ut_pid = pid, + .ut_session = sid, + }; + int r; + + assert(id); + + init_timestamp(&store, 0); + + /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ + strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id)); + + if (line) + strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); + + r = write_entry_both(&store); + if (r < 0) + return r; + + if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { + store.ut_type = LOGIN_PROCESS; + r = write_entry_both(&store); + if (r < 0) + return r; + } + + if (ut_type == USER_PROCESS) { + store.ut_type = USER_PROCESS; + strncpy(store.ut_user, user, sizeof(store.ut_user)-1); + r = write_entry_both(&store); + if (r < 0) + return r; + } + + return 0; +} + +int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { + struct utmpx lookup = { + .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */ + }, store, store_wtmp, *found; + + assert(id); + + setutxent(); + + /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ + strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id)); + + found = getutxid(&lookup); + if (!found) + return 0; + + if (found->ut_pid != pid) + return 0; + + memcpy(&store, found, sizeof(store)); + store.ut_type = DEAD_PROCESS; + store.ut_exit.e_termination = code; + store.ut_exit.e_exit = status; + + zero(store.ut_user); + zero(store.ut_host); + zero(store.ut_tv); + + memcpy(&store_wtmp, &store, sizeof(store_wtmp)); + /* wtmp wants the current time */ + init_timestamp(&store_wtmp, 0); + + return write_utmp_wtmp(&store, &store_wtmp); +} + + +int utmp_put_runlevel(int runlevel, int previous) { + struct utmpx store = {}; + int r; + + assert(runlevel > 0); + + if (previous <= 0) { + /* Find the old runlevel automatically */ + + r = utmp_get_runlevel(&previous, NULL); + if (r < 0) { + if (r != -ESRCH) + return r; + + previous = 0; + } + } + + if (previous == runlevel) + return 0; + + init_entry(&store, 0); + + store.ut_type = RUN_LVL; + store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); + strncpy(store.ut_user, "runlevel", sizeof(store.ut_user)); + + return write_entry_both(&store); +} + +#define TIMEOUT_MSEC 50 + +static int write_to_terminal(const char *tty, const char *message) { + _cleanup_close_ int fd = -1; + const char *p; + size_t left; + usec_t end; + + assert(tty); + assert(message); + + fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC); + if (fd < 0 || !isatty(fd)) + return -errno; + + p = message; + left = strlen(message); + + end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; + + while (left > 0) { + ssize_t n; + struct pollfd pollfd = { + .fd = fd, + .events = POLLOUT, + }; + usec_t t; + int k; + + t = now(CLOCK_MONOTONIC); + + if (t >= end) + return -ETIME; + + k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC); + if (k < 0) + return -errno; + + if (k == 0) + return -ETIME; + + n = write(fd, p, left); + if (n < 0) { + if (errno == EAGAIN) + continue; + + return -errno; + } + + assert((size_t) n <= left); + + p += n; + left -= n; + } + + return 0; +} + +int utmp_wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, void *userdata), + void *userdata) { + + _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL; + char date[FORMAT_TIMESTAMP_MAX]; + struct utmpx *u; + int r; + + hn = gethostname_malloc(); + if (!hn) + return -ENOMEM; + if (!username) { + un = getlogname_malloc(); + if (!un) + return -ENOMEM; + } + + if (!origin_tty) { + getttyname_harder(STDIN_FILENO, &stdin_tty); + origin_tty = stdin_tty; + } + + if (asprintf(&text, + "\a\r\n" + "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" + "%s\r\n\r\n", + un ?: username, hn, + origin_tty ? " on " : "", strempty(origin_tty), + format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)), + message) < 0) + return -ENOMEM; + + setutxent(); + + r = 0; + + while ((u = getutxent())) { + _cleanup_free_ char *buf = NULL; + const char *path; + int q; + + if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) + continue; + + /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */ + if (path_startswith(u->ut_line, "/dev/")) + path = u->ut_line; + else { + if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) + return -ENOMEM; + + path = buf; + } + + if (!match_tty || match_tty(path, userdata)) { + q = write_to_terminal(path, text); + if (q < 0) + r = q; + } + } + + return r; +} diff --git a/src/libshared/src/vlan-util.c b/src/libshared/src/vlan-util.c new file mode 100644 index 0000000000..ccf60a0cb0 --- /dev/null +++ b/src/libshared/src/vlan-util.c @@ -0,0 +1,69 @@ +/*** + 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 . +***/ + +#include "basic/parse-util.h" +#include "shared/conf-parser.h" +#include "shared/vlan-util.h" + +int parse_vlanid(const char *p, uint16_t *ret) { + uint16_t id; + int r; + + r = safe_atou16(p, &id); + if (r < 0) + return r; + if (!vlanid_is_valid(id)) + return -ERANGE; + + *ret = id; + return 0; +} + +int config_parse_vlanid( + 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) { + + uint16_t *id = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = parse_vlanid(rvalue, id); + if (r == -ERANGE) { + log_syntax(unit, LOG_ERR, filename, line, r, "VLAN identifier outside of valid range 0…4094, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN identifier value, ignoring: %s", rvalue); + return 0; + } + + return 0; +} diff --git a/src/libshared/src/watchdog.c b/src/libshared/src/watchdog.c new file mode 100644 index 0000000000..c4e58c9577 --- /dev/null +++ b/src/libshared/src/watchdog.c @@ -0,0 +1,165 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/time-util.h" +#include "shared/watchdog.h" + +static int watchdog_fd = -1; +static usec_t watchdog_timeout = USEC_INFINITY; + +static int update_timeout(void) { + int r; + + if (watchdog_fd < 0) + return 0; + + if (watchdog_timeout == USEC_INFINITY) + return 0; + else if (watchdog_timeout == 0) { + int flags; + + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) + return log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); + } else { + int sec, flags; + char buf[FORMAT_TIMESPAN_MAX]; + + sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); + if (r < 0) + return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec); + + watchdog_timeout = (usec_t) sec * USEC_PER_SEC; + log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0)); + + flags = WDIOS_ENABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + /* ENOTTY means the watchdog is always enabled so we're fine */ + log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING, + "Failed to enable hardware watchdog: %m"); + if (errno != ENOTTY) + return -errno; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) + return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); + } + + return 0; +} + +static int open_watchdog(void) { + struct watchdog_info ident; + + if (watchdog_fd >= 0) + return 0; + + watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); + if (watchdog_fd < 0) + return -errno; + + if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) + log_info("Hardware watchdog '%s', version %x", + ident.identity, + ident.firmware_version); + + return update_timeout(); +} + +int watchdog_set_timeout(usec_t *usec) { + int r; + + watchdog_timeout = *usec; + + /* If we didn't open the watchdog yet and didn't get any + * explicit timeout value set, don't do anything */ + if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY) + return 0; + + if (watchdog_fd < 0) + r = open_watchdog(); + else + r = update_timeout(); + + *usec = watchdog_timeout; + + return r; +} + +int watchdog_ping(void) { + int r; + + if (watchdog_fd < 0) { + r = open_watchdog(); + if (r < 0) + return r; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) + return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); + + return 0; +} + +void watchdog_close(bool disarm) { + int r; + + if (watchdog_fd < 0) + return; + + if (disarm) { + int flags; + + /* Explicitly disarm it */ + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) + log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); + + /* To be sure, use magic close logic, too */ + for (;;) { + static const char v = 'V'; + + if (write(watchdog_fd, &v, 1) > 0) + break; + + if (errno != EINTR) { + log_error_errno(errno, "Failed to disarm watchdog timer: %m"); + break; + } + } + } + + watchdog_fd = safe_close(watchdog_fd); +} diff --git a/src/libsystemd-network/Makefile b/src/libsystemd-network/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd-network/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd-network/Makefile b/src/libsystemd-network/Makefile new file mode 100644 index 0000000000..8ba25db413 --- /dev/null +++ b/src/libsystemd-network/Makefile @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +nested.subdirs += src +nested.subdirs += test + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c deleted file mode 100644 index 02028bf28a..0000000000 --- a/src/libsystemd-network/arp-util.c +++ /dev/null @@ -1,154 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 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 . -***/ - -#include -#include - -#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[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - /* Sender Hardware Address must be different from our own */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - }; - struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter - }; - union sockaddr_union link = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - _cleanup_close_ int s = -1; - int r; - - assert(ifindex > 0); - - s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = bind(s, &link.sa, sizeof(link.ll)); - if (r < 0) - return -errno; - - r = s; - s = -1; - - return r; -} - -static int arp_send_packet(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha, - bool announce) { - union sockaddr_union link = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - struct ether_arp arp = { - .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ - .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ - .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ - .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ - .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ - }; - int r; - - assert(fd >= 0); - assert(pa != 0); - assert(ha); - - memcpy(&arp.arp_sha, ha, ETH_ALEN); - memcpy(&arp.arp_tpa, &pa, sizeof(pa)); - - if (announce) - memcpy(&arp.arp_spa, &pa, sizeof(pa)); - - r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); - if (r < 0) - return -errno; - - return 0; -} - -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, false); -} - -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, true); -} diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h deleted file mode 100644 index 3ef56b002a..0000000000 --- a/src/libsystemd-network/arp-util.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. 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 . -***/ - -#include - -#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); - -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha); -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha); diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c deleted file mode 100644 index a21efc4d06..0000000000 --- a/src/libsystemd-network/dhcp-identifier.c +++ /dev/null @@ -1,129 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 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 . -***/ - -#include "libudev.h" -#include "sd-id128.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_validate_duid_len(uint16_t duid_type, size_t duid_len) { - struct duid d; - - assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); - if (duid_len > MAX_DUID_LEN) - return -EINVAL; - - switch (duid_type) { - case DUID_TYPE_LLT: - if (duid_len <= sizeof(d.llt)) - return -EINVAL; - break; - case DUID_TYPE_EN: - if (duid_len != sizeof(d.en)) - return -EINVAL; - break; - case DUID_TYPE_LL: - if (duid_len <= sizeof(d.ll)) - return -EINVAL; - break; - case DUID_TYPE_UUID: - if (duid_len != sizeof(d.uuid)) - return -EINVAL; - break; - default: - /* accept unknown type in order to be forward compatible */ - break; - } - return 0; -} - -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { - sd_id128_t machine_id; - uint64_t hash; - int r; - - assert(duid); - assert(len); - - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; - - unaligned_write_be16(&duid->type, DUID_TYPE_EN); - unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); - - *len = sizeof(duid->type) + sizeof(duid->en); - - /* a bit of snake-oil perhaps, but no need to expose the machine-id - 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 */ - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - const char *name = NULL; - uint64_t id; - - if (detect_container() <= 0) { - /* not in a container, udev will be around */ - _cleanup_udev_unref_ struct udev *udev; - char ifindex_str[2 + DECIMAL_STR_MAX(int)]; - - udev = udev_new(); - if (!udev) - return -ENOMEM; - - sprintf(ifindex_str, "n%d", ifindex); - device = udev_device_new_from_device_id(udev, ifindex_str); - if (device) { - if (udev_device_get_is_initialized(device) <= 0) - /* not yet ready */ - return -EBUSY; - - name = net_get_name(device); - } - } - - if (name) - id = siphash24(name, strlen(name), HASH_KEY.bytes); - else - /* fall back to MAC address if no predictable name available */ - id = siphash24(mac, mac_len, HASH_KEY.bytes); - - id = htole64(id); - - /* fold into 32 bits */ - unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); - - return 0; -} diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h deleted file mode 100644 index 1cc0f9fb71..0000000000 --- a/src/libsystemd-network/dhcp-identifier.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 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 . -***/ - -#include "sd-id128.h" - -#include "macro.h" -#include "sparse-endian.h" -#include "unaligned.h" - -typedef enum DUIDType { - DUID_TYPE_LLT = 1, - DUID_TYPE_EN = 2, - DUID_TYPE_LL = 3, - DUID_TYPE_UUID = 4, - _DUID_TYPE_MAX, - _DUID_TYPE_INVALID = -1, -} DUIDType; - -/* RFC 3315 section 9.1: - * A DUID can be no more than 128 octets long (not including the type code). - */ -#define MAX_DUID_LEN 128 - -/* https://tools.ietf.org/html/rfc3315#section-9.1 */ -struct duid { - be16_t type; - union { - struct { - /* DUID_TYPE_LLT */ - uint16_t htype; - uint32_t time; - uint8_t haddr[0]; - } _packed_ llt; - struct { - /* DUID_TYPE_EN */ - uint32_t pen; - uint8_t id[8]; - } _packed_ en; - struct { - /* DUID_TYPE_LL */ - int16_t htype; - uint8_t haddr[0]; - } _packed_ ll; - struct { - /* DUID_TYPE_UUID */ - sd_id128_t uuid; - } _packed_ uuid; - struct { - uint8_t data[MAX_DUID_LEN]; - } _packed_ raw; - }; -} _packed_; - -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h deleted file mode 100644 index 99f690897d..0000000000 --- a/src/libsystemd-network/dhcp-internal.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include -#include -#include - -#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, - size_t mac_addr_len, uint16_t arp_type); -int dhcp_network_bind_udp_socket(be32_t address, uint16_t port); -int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, - const void *packet, size_t len); -int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, - const void *packet, size_t len); - -int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, - uint8_t code, size_t optlen, const void *optval); - -typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, - const void *option, void *userdata); - -int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_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, - size_t *optoffset); - -uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); - -void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, - uint16_t source, be32_t destination_addr, - uint16_t destination, uint16_t len); - -int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum); - -/* 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_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) - -#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) -#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h deleted file mode 100644 index 82cae2300a..0000000000 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include - -#include "sd-dhcp-client.h" - -#include "dhcp-protocol.h" -#include "list.h" -#include "util.h" - -struct sd_dhcp_route { - struct in_addr dst_addr; - struct in_addr gw_addr; - unsigned char dst_prefixlen; -}; - -struct sd_dhcp_raw_option { - LIST_FIELDS(struct sd_dhcp_raw_option, options); - - uint8_t tag; - uint8_t length; - void *data; -}; - -struct sd_dhcp_lease { - unsigned n_ref; - - /* each 0 if unset */ - uint32_t t1; - uint32_t t2; - uint32_t lifetime; - - /* each 0 if unset */ - be32_t address; - be32_t server_address; - be32_t router; - be32_t next_server; - - bool have_subnet_mask; - be32_t subnet_mask; - - bool have_broadcast; - be32_t broadcast; - - struct in_addr *dns; - size_t dns_size; - - struct in_addr *ntp; - size_t ntp_size; - - struct sd_dhcp_route *static_route; - size_t static_route_size, static_route_allocated; - - uint16_t mtu; /* 0 if unset */ - - char *domainname; - char *hostname; - char *root_path; - - void *client_id; - size_t client_id_len; - - void *vendor_specific; - size_t vendor_specific_len; - - char *timezone; - - LIST_HEAD(struct sd_dhcp_raw_option, private_options); -}; - -int dhcp_lease_new(sd_dhcp_lease **ret); - -int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); -int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); - -int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); - -int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len); - -int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); -int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c deleted file mode 100644 index a9f5a0a5de..0000000000 --- a/src/libsystemd-network/dhcp-network.c +++ /dev/null @@ -1,235 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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, - size_t mac_addr_len, - const uint8_t *bcast_addr, - const struct ether_addr *eth_mac, - uint16_t arp_type, uint8_t dhcp_hlen) { - struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */ - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20), /* A <- A & 0x20 (More Fragments bit) */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */ - BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ - BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ - BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ - }; - struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = filter - }; - _cleanup_close_ int s = -1; - int r, on = 1; - - assert(ifindex > 0); - assert(link); - - s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - link->ll.sll_family = AF_PACKET; - link->ll.sll_protocol = htobe16(ETH_P_IP); - link->ll.sll_ifindex = ifindex; - link->ll.sll_hatype = htobe16(arp_type); - link->ll.sll_halen = mac_addr_len; - memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len); - - r = bind(s, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - r = s; - s = -1; - - return r; -} - -int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, - uint32_t xid, const uint8_t *mac_addr, - size_t mac_addr_len, uint16_t arp_type) { - static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - /* Default broadcast address for IPoIB */ - static const uint8_t ib_bcast[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff - }; - struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } }; - const uint8_t *bcast_addr = NULL; - uint8_t dhcp_hlen = 0; - - assert_return(mac_addr_len > 0, -EINVAL); - - if (arp_type == ARPHRD_ETHER) { - assert_return(mac_addr_len == ETH_ALEN, -EINVAL); - memcpy(ð_mac, mac_addr, ETH_ALEN); - bcast_addr = eth_bcast; - dhcp_hlen = ETH_ALEN; - } else if (arp_type == ARPHRD_INFINIBAND) { - assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL); - bcast_addr = ib_bcast; - } else - return -EINVAL; - - return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len, - bcast_addr, ð_mac, arp_type, dhcp_hlen); -} - -int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { - union sockaddr_union src = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(port), - .in.sin_addr.s_addr = address, - }; - _cleanup_close_ int s = -1; - int r, on = 1, tos = IPTOS_CLASS_CS6; - - s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - if (r < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (r < 0) - return -errno; - - if (address == INADDR_ANY) { - r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (r < 0) - return -errno; - } else { - r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); - if (r < 0) - return -errno; - } - - r = bind(s, &src.sa, sizeof(src.in)); - if (r < 0) - return -errno; - - r = s; - s = -1; - - return r; -} - -int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, - const void *packet, size_t len) { - int r; - - assert(link); - assert(packet); - assert(len); - - r = sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)); - if (r < 0) - return -errno; - - return 0; -} - -int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, - const void *packet, size_t len) { - union sockaddr_union dest = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(port), - .in.sin_addr.s_addr = address, - }; - int r; - - assert(s >= 0); - assert(packet); - assert(len); - - r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c deleted file mode 100644 index c105196334..0000000000 --- a/src/libsystemd-network/dhcp-option.c +++ /dev/null @@ -1,263 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "utf8.h" - -#include "dhcp-internal.h" - -static int option_append(uint8_t options[], size_t size, size_t *offset, - uint8_t code, size_t optlen, const void *optval) { - assert(options); - assert(offset); - - if (code != SD_DHCP_OPTION_END) - /* always make sure there is space for an END option */ - size--; - - switch (code) { - - case SD_DHCP_OPTION_PAD: - case SD_DHCP_OPTION_END: - if (size < *offset + 1) - return -ENOBUFS; - - options[*offset] = code; - *offset += 1; - break; - - default: - if (size < *offset + optlen + 2) - return -ENOBUFS; - - options[*offset] = code; - options[*offset + 1] = optlen; - - memcpy_safe(&options[*offset + 2], optval, optlen); - *offset += optlen + 2; - - break; - } - - return 0; -} - -int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, - uint8_t overload, - uint8_t code, size_t optlen, const void *optval) { - size_t file_offset = 0, sname_offset =0; - bool file, sname; - int r; - - assert(message); - assert(offset); - - file = overload & DHCP_OVERLOAD_FILE; - sname = overload & DHCP_OVERLOAD_SNAME; - - if (*offset < size) { - /* still space in the options array */ - r = option_append(message->options, size, offset, code, optlen, optval); - if (r >= 0) - return 0; - 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, SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - *offset = size; - } else - return r; - } - - if (overload & DHCP_OVERLOAD_FILE) { - file_offset = *offset - size; - - if (file_offset < sizeof(message->file)) { - /* still space in the 'file' array */ - r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval); - if (r >= 0) { - *offset = size + file_offset; - return 0; - } 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, SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - *offset = size + sizeof(message->file); - } else - return r; - } - } - - if (overload & DHCP_OVERLOAD_SNAME) { - sname_offset = *offset - size - (file ? sizeof(message->file) : 0); - - if (sname_offset < sizeof(message->sname)) { - /* still space in the 'sname' array */ - r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval); - if (r >= 0) { - *offset = size + (file ? sizeof(message->file) : 0) + sname_offset; - return 0; - } else { - /* no space, or other error, give up */ - return r; - } - } - } - - return -ENOBUFS; -} - -static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, - uint8_t *message_type, char **error_message, dhcp_option_callback_t cb, - void *userdata) { - uint8_t code, len; - const uint8_t *option; - size_t offset = 0; - - while (offset < buflen) { - code = options[offset ++]; - - switch (code) { - case SD_DHCP_OPTION_PAD: - continue; - - case SD_DHCP_OPTION_END: - return 0; - } - - if (buflen < offset + 1) - return -ENOBUFS; - - 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 = *option; - - break; - - case SD_DHCP_OPTION_ERROR_MESSAGE: - if (len == 0) - return -EINVAL; - - if (error_message) { - _cleanup_free_ char *string = NULL; - - /* Accept a trailing NUL byte */ - if (memchr(option, 0, len - 1)) - return -EINVAL; - - string = strndup((const char *) option, len); - if (!string) - return -ENOMEM; - - if (!ascii_is_valid(string)) - return -EINVAL; - - free(*error_message); - *error_message = string; - string = NULL; - } - - break; - case SD_DHCP_OPTION_OVERLOAD: - if (len != 1) - return -EINVAL; - - if (overload) - *overload = *option; - - break; - - default: - if (cb) - cb(code, len, option, userdata); - - break; - } - - offset += len; - } - - if (offset < buflen) - return -EINVAL; - - return 0; -} - -int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) { - _cleanup_free_ char *error_message = NULL; - uint8_t overload = 0; - uint8_t message_type = 0; - int r; - - if (!message) - return -EINVAL; - - if (len < sizeof(DHCPMessage)) - return -EINVAL; - - len -= sizeof(DHCPMessage); - - 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, &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, &error_message, cb, userdata); - if (r < 0) - return r; - } - - 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 message_type; -} diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c deleted file mode 100644 index 8be774061d..0000000000 --- a/src/libsystemd-network/dhcp-packet.c +++ /dev/null @@ -1,191 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include -#include -#include - -#include "dhcp-internal.h" -#include "dhcp-protocol.h" - -#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 - -int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, - uint8_t type, uint16_t arp_type, size_t optlen, - size_t *optoffset) { - size_t offset = 0; - int r; - - assert(op == BOOTREQUEST || op == BOOTREPLY); - assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND); - - message->op = op; - message->htype = arp_type; - message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0; - message->xid = htobe32(xid); - message->magic = htobe32(DHCP_MAGIC_COOKIE); - - r = dhcp_option_append(message, optlen, &offset, 0, - SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type); - if (r < 0) - return r; - - *optoffset = offset; - - return 0; -} - -uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) { - uint64_t *buf_64 = (uint64_t*)buf; - uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t)); - uint64_t sum = 0; - - /* See RFC1071 */ - - while (buf_64 < end_64) { - sum += *buf_64; - if (sum < *buf_64) - /* wrap around in one's complement */ - sum++; - - buf_64++; - } - - if (len % sizeof(uint64_t)) { - /* If the buffer is not aligned to 64-bit, we need - to zero-pad the last few bytes and add them in */ - uint64_t buf_tail = 0; - - memcpy(&buf_tail, buf_64, len % sizeof(uint64_t)); - - sum += buf_tail; - if (sum < buf_tail) - /* wrap around */ - sum++; - } - - while (sum >> 16) - sum = (sum & 0xffff) + (sum >> 16); - - return ~sum; -} - -void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, - uint16_t source_port, be32_t destination_addr, - uint16_t destination_port, uint16_t len) { - packet->ip.version = IPVERSION; - packet->ip.ihl = DHCP_IP_SIZE / 4; - packet->ip.tot_len = htobe16(len); - - packet->ip.tos = IPTOS_CLASS_CS6; - - packet->ip.protocol = IPPROTO_UDP; - packet->ip.saddr = source_addr; - packet->ip.daddr = destination_addr; - - packet->udp.source = htobe16(source_port); - packet->udp.dest = htobe16(destination_port); - - packet->udp.len = htobe16(len - DHCP_IP_SIZE); - - packet->ip.check = packet->udp.len; - packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8); - - packet->ip.ttl = IPDEFTTL; - packet->ip.check = 0; - packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE); -} - -int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { - size_t hdrlen; - - assert(packet); - - /* IP */ - - if (packet->ip.version != IPVERSION) { - log_debug("ignoring packet: not IPv4"); - return -EINVAL; - } - - if (packet->ip.ihl < 5) { - log_debug("ignoring packet: IPv4 IHL (%u words) invalid", - packet->ip.ihl); - return -EINVAL; - } - - hdrlen = packet->ip.ihl * 4; - if (hdrlen < 20) { - log_debug("ignoring packet: IPv4 IHL (%zu bytes) " - "smaller than minimum (20 bytes)", hdrlen); - return -EINVAL; - } - - if (len < hdrlen) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by IP header", len, - hdrlen); - return -EINVAL; - } - - /* UDP */ - - if (packet->ip.protocol != IPPROTO_UDP) { - log_debug("ignoring packet: not UDP"); - return -EINVAL; - } - - if (len < hdrlen + be16toh(packet->udp.len)) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by UDP header", len, - hdrlen + be16toh(packet->udp.len)); - return -EINVAL; - } - - if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) { - log_debug("ignoring packet: to port %u, which " - "is not the DHCP client port (%u)", - be16toh(packet->udp.dest), DHCP_PORT_CLIENT); - return -EINVAL; - } - - /* checksums - computing these is relatively expensive, so only do it - if all the other checks have passed - */ - - if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) { - log_debug("ignoring packet: invalid IP checksum"); - return -EINVAL; - } - - if (checksum && packet->udp.check) { - packet->ip.check = packet->udp.len; - packet->ip.ttl = 0; - - if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, - be16toh(packet->udp.len) + 12)) { - log_debug("ignoring packet: invalid UDP checksum"); - return -EINVAL; - } - } - - return 0; -} diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h deleted file mode 100644 index 5cf7abbff9..0000000000 --- a/src/libsystemd-network/dhcp-protocol.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include - -#include "macro.h" -#include "sparse-endian.h" - -struct DHCPMessage { - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - be32_t xid; - be16_t secs; - be16_t flags; - be32_t ciaddr; - be32_t yiaddr; - be32_t siaddr; - be32_t giaddr; - uint8_t chaddr[16]; - uint8_t sname[64]; - uint8_t file[128]; - be32_t magic; - uint8_t options[0]; -} _packed_; - -typedef struct DHCPMessage DHCPMessage; - -struct DHCPPacket { - struct iphdr ip; - struct udphdr udp; - DHCPMessage dhcp; -} _packed_; - -typedef struct DHCPPacket DHCPPacket; - -#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr)) -#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE) -#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage)) -#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */ -#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE) -#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363) - -enum { - DHCP_PORT_SERVER = 67, - DHCP_PORT_CLIENT = 68, -}; - -enum DHCPState { - DHCP_STATE_INIT = 0, - DHCP_STATE_SELECTING = 1, - DHCP_STATE_INIT_REBOOT = 2, - DHCP_STATE_REBOOTING = 3, - DHCP_STATE_REQUESTING = 4, - DHCP_STATE_BOUND = 5, - DHCP_STATE_RENEWING = 6, - DHCP_STATE_REBINDING = 7, - DHCP_STATE_STOPPED = 8, -}; - -typedef enum DHCPState DHCPState; - -enum { - BOOTREQUEST = 1, - BOOTREPLY = 2, -}; - -enum { - DHCP_DISCOVER = 1, - DHCP_OFFER = 2, - DHCP_REQUEST = 3, - DHCP_DECLINE = 4, - DHCP_ACK = 5, - DHCP_NAK = 6, - DHCP_RELEASE = 7, - DHCP_INFORM = 8, - DHCP_FORCERENEW = 9, -}; - -enum { - DHCP_OVERLOAD_FILE = 1, - DHCP_OVERLOAD_SNAME = 2, -}; - -#define DHCP_MAX_FQDN_LENGTH 255 - -enum { - 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 deleted file mode 100644 index 0c76956fad..0000000000 --- a/src/libsystemd-network/dhcp-server-internal.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include "sd-dhcp-server.h" -#include "sd-event.h" - -#include "dhcp-internal.h" -#include "hashmap.h" -#include "log.h" -#include "util.h" - -typedef struct DHCPClientId { - size_t length; - void *data; -} DHCPClientId; - -typedef struct DHCPLease { - DHCPClientId client_id; - - be32_t address; - be32_t gateway; - uint8_t chaddr[16]; - usec_t expiration; -} DHCPLease; - -struct sd_dhcp_server { - unsigned n_ref; - - sd_event *event; - int event_priority; - sd_event_source *receive_message; - int fd; - int fd_raw; - - int ifindex; - be32_t address; - be32_t netmask; - be32_t subnet; - uint32_t pool_offset; - uint32_t pool_size; - - char *timezone; - - struct in_addr *ntp, *dns; - unsigned n_ntp, n_dns; - - bool emit_router; - - Hashmap *leases_by_client_id; - DHCPLease **bound_leases; - DHCPLease invalid_lease; - - uint32_t max_lease_time, default_lease_time; -}; - -typedef struct DHCPRequest { - /* received message */ - DHCPMessage *message; - - /* options */ - DHCPClientId client_id; - size_t max_optlen; - be32_t server_id; - be32_t requested_ip; - uint32_t lifetime; -} DHCPRequest; - -#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, - size_t length); -int dhcp_server_send_packet(sd_dhcp_server *server, - DHCPRequest *req, DHCPPacket *packet, - int type, size_t optoffset); - -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 deleted file mode 100644 index 945c3b9721..0000000000 --- a/src/libsystemd-network/dhcp6-internal.h +++ /dev/null @@ -1,81 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "sd-event.h" - -#include "list.h" -#include "macro.h" -#include "sparse-endian.h" - -typedef struct DHCP6Address DHCP6Address; - -struct DHCP6Address { - LIST_FIELDS(DHCP6Address, addresses); - - struct { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; - } iaaddr _packed_; -}; - -struct DHCP6IA { - uint16_t type; - struct { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; - } _packed_; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - - LIST_HEAD(DHCP6Address, addresses); -}; - -typedef struct DHCP6IA DHCP6IA; - -#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) -#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__) - -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); -int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, - size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia); -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count, - size_t *allocated); -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, - char ***str_arr); - -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); -int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, - const void *packet, size_t len); - -const char *dhcp6_message_type_to_string(int s) _const_; -int dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(int s) _const_; -int dhcp6_message_status_from_string(const char *s) _pure_; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h deleted file mode 100644 index 14e708ef63..0000000000 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - 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 . -***/ - -#include - -#include "sd-dhcp6-lease.h" - -#include "dhcp6-internal.h" - -struct sd_dhcp6_lease { - unsigned n_ref; - - uint8_t *serverid; - size_t serverid_len; - uint8_t preference; - bool rapid_commit; - - DHCP6IA ia; - - DHCP6Address *addr_iter; - - struct in6_addr *dns; - size_t dns_count; - size_t dns_allocated; - char **domains; - size_t domains_count; - struct in6_addr *ntp; - size_t ntp_count; - size_t ntp_allocated; - char **ntp_fqdn; - size_t ntp_fqdn_count; -}; - -int dhcp6_lease_clear_timers(DHCP6IA *ia); -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); - -int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, - size_t len); -int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len); -int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); -int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); - -int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); - -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen); -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) ; - -int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c deleted file mode 100644 index fd2d60c9d5..0000000000 --- a/src/libsystemd-network/dhcp6-network.c +++ /dev/null @@ -1,91 +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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dhcp6-internal.h" -#include "dhcp6-protocol.h" -#include "fd-util.h" -#include "socket-util.h" - -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - union sockaddr_union src = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), - .in6.sin6_scope_id = index, - }; - _cleanup_close_ int s = -1; - int r, off = 0, on = 1; - - assert(index > 0); - assert(local_address); - - 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_V6ONLY, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); - if (r < 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (r < 0) - return -errno; - - r = bind(s, &src.sa, sizeof(src.in6)); - if (r < 0) - return -errno; - - r = s; - s = -1; - return r; -} - -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { - union sockaddr_union dest = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(DHCP6_PORT_SERVER), - }; - int r; - - assert(server_address); - - memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr)); - - r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)); - if (r < 0) - return -errno; - - return 0; -} diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c deleted file mode 100644 index 5462e03476..0000000000 --- a/src/libsystemd-network/dhcp6-option.c +++ /dev/null @@ -1,413 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#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 - -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; - -static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, - size_t optlen) { - DHCP6Option *option = (DHCP6Option*) *buf; - - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - - if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) - return -ENOBUFS; - - option->code = htobe16(optcode); - option->len = htobe16(optlen); - - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); - - return 0; -} - -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval) { - int r; - - assert_return(optval || optlen == 0, -EINVAL); - - r = option_append_hdr(buf, buflen, code, optlen); - if (r < 0) - return r; - - memcpy_safe(*buf, optval, optlen); - - *buf += optlen; - *buflen -= optlen; - - return 0; -} - -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { - uint16_t len; - uint8_t *ia_hdr; - size_t ia_buflen, ia_addrlen = 0; - DHCP6Address *addr; - int r; - - assert_return(buf && *buf && buflen && ia, -EINVAL); - - switch (ia->type) { - case SD_DHCP6_OPTION_IA_NA: - len = DHCP6_OPTION_IA_NA_LEN; - break; - - case SD_DHCP6_OPTION_IA_TA: - len = DHCP6_OPTION_IA_TA_LEN; - break; - - default: - return -EINVAL; - } - - if (*buflen < len) - return -ENOBUFS; - - ia_hdr = *buf; - ia_buflen = *buflen; - - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); - - memcpy(*buf, &ia->id, len); - - *buf += len; - *buflen -= len; - - LIST_FOREACH(addresses, addr, ia->addresses) { - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, - sizeof(addr->iaaddr)); - if (r < 0) - return r; - - memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr)); - - *buf += sizeof(addr->iaaddr); - *buflen -= sizeof(addr->iaaddr); - - ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); - } - - r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); - if (r < 0) - return r; - - return 0; -} - - -static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { - DHCP6Option *option = (DHCP6Option*) *buf; - uint16_t len; - - assert_return(buf, -EINVAL); - assert_return(optcode, -EINVAL); - assert_return(optlen, -EINVAL); - - if (*buflen < sizeof(DHCP6Option)) - return -ENOMSG; - - len = be16toh(option->len); - - if (len > *buflen) - return -ENOMSG; - - *optcode = be16toh(option->code); - *optlen = len; - - *buf += 4; - *buflen -= 4; - - return 0; -} - -int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, - size_t *optlen, uint8_t **optvalue) { - int r; - - assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL); - - r = option_parse_hdr(buf, buflen, optcode, optlen); - if (r < 0) - return r; - - if (*optlen > *buflen) - return -ENOBUFS; - - *optvalue = *buf; - *buflen -= *optlen; - *buf += *optlen; - - return 0; -} - -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia) { - int r; - uint16_t opt, status; - size_t optlen; - size_t iaaddr_offset; - DHCP6Address *addr; - uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; - - assert_return(ia, -EINVAL); - assert_return(!ia->addresses, -EINVAL); - - switch (iatype) { - case SD_DHCP6_OPTION_IA_NA: - - if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { - r = -ENOBUFS; - goto error; - } - - iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); - - lt_t1 = be32toh(ia->lifetime_t1); - lt_t2 = be32toh(ia->lifetime_t2); - - if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA T1 %ds > T2 %ds", - lt_t1, lt_t2); - r = -EINVAL; - goto error; - } - - break; - - case SD_DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { - r = -ENOBUFS; - goto error; - } - - iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); - - ia->lifetime_t1 = 0; - ia->lifetime_t2 = 0; - - break; - - default: - r = -ENOMSG; - goto error; - } - - ia->type = iatype; - - *buflen -= iaaddr_offset; - *buf += iaaddr_offset; - - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { - - switch (opt) { - case SD_DHCP6_OPTION_IAADDR: - - addr = new0(DHCP6Address, 1); - if (!addr) { - r = -ENOMEM; - goto error; - } - - LIST_INIT(addresses, addr); - - memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); - - lt_valid = be32toh(addr->iaaddr.lifetime_valid); - lt_pref = be32toh(addr->iaaddr.lifetime_valid); - - if (!lt_valid || lt_pref > lt_valid) { - log_dhcp6_client(client, "IA preferred %ds > valid %ds", - lt_pref, lt_valid); - free(addr); - } else { - LIST_PREPEND(addresses, ia->addresses, addr); - if (lt_valid < lt_min) - lt_min = lt_valid; - } - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < sizeof(status)) - break; - - status = (*buf)[0] << 8 | (*buf)[1]; - if (status) { - log_dhcp6_client(client, "IA status %d", - status); - r = -EINVAL; - goto error; - } - - break; - - default: - log_dhcp6_client(client, "Unknown IA option %d", opt); - break; - } - - *buflen -= optlen; - *buf += optlen; - } - - if (r == -ENOMSG) - r = 0; - - if (!ia->lifetime_t1 && !ia->lifetime_t2) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->lifetime_t1 = htobe32(lt_t1); - ia->lifetime_t2 = htobe32(lt_t2); - - log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero", - lt_t1, lt_t2); - } - - if (*buflen) - r = -ENOMSG; - -error: - *buf += *buflen; - *buflen = 0; - - return r; -} - -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count, - size_t *allocated) { - - if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) - return -EINVAL; - - if (!GREEDY_REALLOC(*addrs, *allocated, - count * sizeof(struct in6_addr) + optlen)) - return -ENOMEM; - - memcpy(*addrs + count, optval, optlen); - - count += optlen / sizeof(struct in6_addr); - - return count; -} - -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) { - size_t pos = 0, idx = 0; - _cleanup_free_ char **names = NULL; - int r; - - assert_return(optlen > 1, -ENODATA); - assert_return(optval[optlen - 1] == '\0', -EINVAL); - - while (pos < optlen) { - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - bool first = true; - - for (;;) { - uint8_t c; - - c = optval[pos++]; - - if (c == 0) - /* End of name */ - break; - else if (c <= 63) { - const char *label; - - /* Literal label */ - label = (const char *)&optval[pos]; - pos += c; - if (pos > optlen) - return -EMSGSIZE; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } - - if (first) - first = false; - else - ret[n++] = '.'; - - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - goto fail; - - n += r; - continue; - } else { - r = -EBADMSG; - goto fail; - } - } - - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; - } - - ret[n] = 0; - - r = strv_extend(&names, ret); - if (r < 0) - goto fail; - - idx++; - } - - *str_arr = names; - names = NULL; - - return idx; - -fail: - return r; -} diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h deleted file mode 100644 index 2487c470ab..0000000000 --- a/src/libsystemd-network/dhcp6-protocol.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include -#include - -#include "macro.h" -#include "sparse-endian.h" - -struct DHCP6Message { - union { - struct { - uint8_t type; - uint8_t _pad[3]; - } _packed_; - be32_t transaction_id; - }; -} _packed_; - -typedef struct DHCP6Message DHCP6Message; - -#define DHCP6_MIN_OPTIONS_SIZE \ - 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) - -#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } - -enum { - DHCP6_PORT_SERVER = 547, - DHCP6_PORT_CLIENT = 546, -}; - -#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC -#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC -#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC -#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC -#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC -#define DHCP6_REQ_TIMEOUT 1 * USEC_PER_SEC -#define DHCP6_REQ_MAX_RT 120 * USEC_PER_SEC -#define DHCP6_REQ_MAX_RC 10 -#define DHCP6_REN_TIMEOUT 10 * USEC_PER_SEC -#define DHCP6_REN_MAX_RT 600 * USEC_PER_SEC -#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC -#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC - -enum DHCP6State { - DHCP6_STATE_STOPPED = 0, - DHCP6_STATE_INFORMATION_REQUEST = 1, - DHCP6_STATE_SOLICITATION = 2, - DHCP6_STATE_REQUEST = 3, - DHCP6_STATE_BOUND = 4, - DHCP6_STATE_RENEW = 5, - DHCP6_STATE_REBIND = 6, -}; - -enum { - DHCP6_SOLICIT = 1, - DHCP6_ADVERTISE = 2, - DHCP6_REQUEST = 3, - DHCP6_CONFIRM = 4, - DHCP6_RENEW = 5, - DHCP6_REBIND = 6, - DHCP6_REPLY = 7, - DHCP6_RELEASE = 8, - DHCP6_DECLINE = 9, - DHCP6_RECONFIGURE = 10, - DHCP6_INFORMATION_REQUEST = 11, - DHCP6_RELAY_FORW = 12, - DHCP6_RELAY_REPL = 13, - _DHCP6_MESSAGE_MAX = 14, -}; - -enum { - DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, - DHCP6_NTP_SUBOPTION_MC_ADDR = 2, - DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, -}; - -enum { - DHCP6_STATUS_SUCCESS = 0, - DHCP6_STATUS_UNSPEC_FAIL = 1, - DHCP6_STATUS_NO_ADDRS_AVAIL = 2, - DHCP6_STATUS_NO_BINDING = 3, - DHCP6_STATUS_NOT_ON_LINK = 4, - DHCP6_STATUS_USE_MULTICAST = 5, - _DHCP6_STATUS_MAX = 6, -}; diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c deleted file mode 100644 index c2e4b0e9e3..0000000000 --- a/src/libsystemd-network/icmp6-util.c +++ /dev/null @@ -1,141 +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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - char ifname[IF_NAMESIZE] = ""; - static const int zero = 0, one = 1, hops = 255; - int r; - - 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 = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); - if (r < 0) - return -errno; - - if (if_indextoname(index, ifname) == 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); - 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 deleted file mode 100644 index 2b4dbc76ce..0000000000 --- a/src/libsystemd-network/icmp6-util.h +++ /dev/null @@ -1,25 +0,0 @@ -#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 . -***/ - -#include - -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/include/systemd-network/_sd-common.h b/src/libsystemd-network/include/systemd-network/_sd-common.h new file mode 120000 index 0000000000..653327c267 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/_sd-common.h @@ -0,0 +1 @@ +../../../libsystemd/include/systemd/_sd-common.h \ No newline at end of file diff --git a/src/libsystemd-network/include/systemd-network/arp-util.h b/src/libsystemd-network/include/systemd-network/arp-util.h new file mode 100644 index 0000000000..7540cce95f --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/arp-util.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. 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 . +***/ + +#include + +#include "basic/socket-util.h" +#include "basic/sparse-endian.h" + +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha); diff --git a/src/libsystemd-network/include/systemd-network/dhcp-identifier.h b/src/libsystemd-network/include/systemd-network/dhcp-identifier.h new file mode 100644 index 0000000000..5045dd9b3d --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp-identifier.h @@ -0,0 +1,74 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 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 . +***/ + +#include + +#include "basic/macro.h" +#include "basic/sparse-endian.h" +#include "basic/unaligned.h" + +typedef enum DUIDType { + DUID_TYPE_LLT = 1, + DUID_TYPE_EN = 2, + DUID_TYPE_LL = 3, + DUID_TYPE_UUID = 4, + _DUID_TYPE_MAX, + _DUID_TYPE_INVALID = -1, +} DUIDType; + +/* RFC 3315 section 9.1: + * A DUID can be no more than 128 octets long (not including the type code). + */ +#define MAX_DUID_LEN 128 + +/* https://tools.ietf.org/html/rfc3315#section-9.1 */ +struct duid { + be16_t type; + union { + struct { + /* DUID_TYPE_LLT */ + uint16_t htype; + uint32_t time; + uint8_t haddr[0]; + } _packed_ llt; + struct { + /* DUID_TYPE_EN */ + uint32_t pen; + uint8_t id[8]; + } _packed_ en; + struct { + /* DUID_TYPE_LL */ + int16_t htype; + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* DUID_TYPE_UUID */ + sd_id128_t uuid; + } _packed_ uuid; + struct { + uint8_t data[MAX_DUID_LEN]; + } _packed_ raw; + }; +} _packed_; + +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); +int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); +int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); diff --git a/src/libsystemd-network/include/systemd-network/dhcp-internal.h b/src/libsystemd-network/include/systemd-network/dhcp-internal.h new file mode 100644 index 0000000000..8ea7254ff1 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp-internal.h @@ -0,0 +1,70 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include +#include +#include + +#include + +#include "basic/socket-util.h" + +#include "dhcp-protocol.h" +#include "sd-dhcp-client.h" + +int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, + uint32_t xid, const uint8_t *mac_addr, + size_t mac_addr_len, uint16_t arp_type); +int dhcp_network_bind_udp_socket(be32_t address, uint16_t port); +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, + const void *packet, size_t len); +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, + const void *packet, size_t len); + +int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, + uint8_t code, size_t optlen, const void *optval); + +typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, + const void *option, void *userdata); + +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_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, + size_t *optoffset); + +uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); + +void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, + uint16_t source, be32_t destination_addr, + uint16_t destination, uint16_t len); + +int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum); + +/* 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_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) + +#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) +#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/include/systemd-network/dhcp-lease-internal.h b/src/libsystemd-network/include/systemd-network/dhcp-lease-internal.h new file mode 100644 index 0000000000..af66781ac7 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp-lease-internal.h @@ -0,0 +1,103 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include + +#include + +#include "basic/list.h" +#include "basic/util.h" + +#include "dhcp-protocol.h" +#include "sd-dhcp-client.h" + +struct sd_dhcp_route { + struct in_addr dst_addr; + struct in_addr gw_addr; + unsigned char dst_prefixlen; +}; + +struct sd_dhcp_raw_option { + LIST_FIELDS(struct sd_dhcp_raw_option, options); + + uint8_t tag; + uint8_t length; + void *data; +}; + +struct sd_dhcp_lease { + unsigned n_ref; + + /* each 0 if unset */ + uint32_t t1; + uint32_t t2; + uint32_t lifetime; + + /* each 0 if unset */ + be32_t address; + be32_t server_address; + be32_t router; + be32_t next_server; + + bool have_subnet_mask; + be32_t subnet_mask; + + bool have_broadcast; + be32_t broadcast; + + struct in_addr *dns; + size_t dns_size; + + struct in_addr *ntp; + size_t ntp_size; + + struct sd_dhcp_route *static_route; + size_t static_route_size, static_route_allocated; + + uint16_t mtu; /* 0 if unset */ + + char *domainname; + char *hostname; + char *root_path; + + void *client_id; + size_t client_id_len; + + void *vendor_specific; + size_t vendor_specific_len; + + char *timezone; + + LIST_HEAD(struct sd_dhcp_raw_option, private_options); +}; + +int dhcp_lease_new(sd_dhcp_lease **ret); + +int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); + +int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); + +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len); + +int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); +int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); diff --git a/src/libsystemd-network/include/systemd-network/dhcp-protocol.h b/src/libsystemd-network/include/systemd-network/dhcp-protocol.h new file mode 100644 index 0000000000..9c7197b79a --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp-protocol.h @@ -0,0 +1,113 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include + +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +struct DHCPMessage { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + be32_t xid; + be16_t secs; + be16_t flags; + be32_t ciaddr; + be32_t yiaddr; + be32_t siaddr; + be32_t giaddr; + uint8_t chaddr[16]; + uint8_t sname[64]; + uint8_t file[128]; + be32_t magic; + uint8_t options[0]; +} _packed_; + +typedef struct DHCPMessage DHCPMessage; + +struct DHCPPacket { + struct iphdr ip; + struct udphdr udp; + DHCPMessage dhcp; +} _packed_; + +typedef struct DHCPPacket DHCPPacket; + +#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr)) +#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE) +#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage)) +#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */ +#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE) +#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363) + +enum { + DHCP_PORT_SERVER = 67, + DHCP_PORT_CLIENT = 68, +}; + +enum DHCPState { + DHCP_STATE_INIT = 0, + DHCP_STATE_SELECTING = 1, + DHCP_STATE_INIT_REBOOT = 2, + DHCP_STATE_REBOOTING = 3, + DHCP_STATE_REQUESTING = 4, + DHCP_STATE_BOUND = 5, + DHCP_STATE_RENEWING = 6, + DHCP_STATE_REBINDING = 7, + DHCP_STATE_STOPPED = 8, +}; + +typedef enum DHCPState DHCPState; + +enum { + BOOTREQUEST = 1, + BOOTREPLY = 2, +}; + +enum { + DHCP_DISCOVER = 1, + DHCP_OFFER = 2, + DHCP_REQUEST = 3, + DHCP_DECLINE = 4, + DHCP_ACK = 5, + DHCP_NAK = 6, + DHCP_RELEASE = 7, + DHCP_INFORM = 8, + DHCP_FORCERENEW = 9, +}; + +enum { + DHCP_OVERLOAD_FILE = 1, + DHCP_OVERLOAD_SNAME = 2, +}; + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + 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/include/systemd-network/dhcp-server-internal.h b/src/libsystemd-network/include/systemd-network/dhcp-server-internal.h new file mode 100644 index 0000000000..45f5946e59 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp-server-internal.h @@ -0,0 +1,97 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include + +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/util.h" + +#include "dhcp-internal.h" +#include "sd-dhcp-server.h" + +typedef struct DHCPClientId { + size_t length; + void *data; +} DHCPClientId; + +typedef struct DHCPLease { + DHCPClientId client_id; + + be32_t address; + be32_t gateway; + uint8_t chaddr[16]; + usec_t expiration; +} DHCPLease; + +struct sd_dhcp_server { + unsigned n_ref; + + sd_event *event; + int event_priority; + sd_event_source *receive_message; + int fd; + int fd_raw; + + int ifindex; + be32_t address; + be32_t netmask; + be32_t subnet; + uint32_t pool_offset; + uint32_t pool_size; + + char *timezone; + + struct in_addr *ntp, *dns; + unsigned n_ntp, n_dns; + + bool emit_router; + + Hashmap *leases_by_client_id; + DHCPLease **bound_leases; + DHCPLease invalid_lease; + + uint32_t max_lease_time, default_lease_time; +}; + +typedef struct DHCPRequest { + /* received message */ + DHCPMessage *message; + + /* options */ + DHCPClientId client_id; + size_t max_optlen; + be32_t server_id; + be32_t requested_ip; + uint32_t lifetime; +} DHCPRequest; + +#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, + size_t length); +int dhcp_server_send_packet(sd_dhcp_server *server, + DHCPRequest *req, DHCPPacket *packet, + int type, size_t optoffset); + +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/include/systemd-network/dhcp6-internal.h b/src/libsystemd-network/include/systemd-network/dhcp6-internal.h new file mode 100644 index 0000000000..34cdbed57a --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp6-internal.h @@ -0,0 +1,81 @@ +#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 . +***/ + +#include +#include + +#include + +#include "basic/list.h" +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +typedef struct DHCP6Address DHCP6Address; + +struct DHCP6Address { + LIST_FIELDS(DHCP6Address, addresses); + + struct { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; + } iaaddr _packed_; +}; + +struct DHCP6IA { + uint16_t type; + struct { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; + } _packed_; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + + LIST_HEAD(DHCP6Address, addresses); +}; + +typedef struct DHCP6IA DHCP6IA; + +#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__) + +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); +int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, + size_t *optlen, uint8_t **optvalue); +int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, + DHCP6IA *ia); +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated); +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, + char ***str_arr); + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); +int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, + const void *packet, size_t len); + +const char *dhcp6_message_type_to_string(int s) _const_; +int dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(int s) _const_; +int dhcp6_message_status_from_string(const char *s) _pure_; diff --git a/src/libsystemd-network/include/systemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/include/systemd-network/dhcp6-lease-internal.h new file mode 100644 index 0000000000..9da270e9a9 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp6-lease-internal.h @@ -0,0 +1,73 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + 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 . +***/ + +#include + +#include "dhcp6-internal.h" +#include "sd-dhcp6-lease.h" + +struct sd_dhcp6_lease { + unsigned n_ref; + + uint8_t *serverid; + size_t serverid_len; + uint8_t preference; + bool rapid_commit; + + DHCP6IA ia; + + DHCP6Address *addr_iter; + + struct in6_addr *dns; + size_t dns_count; + size_t dns_allocated; + char **domains; + size_t domains_count; + struct in6_addr *ntp; + size_t ntp_count; + size_t ntp_allocated; + char **ntp_fqdn; + size_t ntp_fqdn_count; +}; + +int dhcp6_lease_clear_timers(DHCP6IA *ia); +int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); +DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); + +int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, + size_t len); +int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len); +int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); +int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); + +int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); + +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen); +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) ; + +int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/include/systemd-network/dhcp6-protocol.h b/src/libsystemd-network/include/systemd-network/dhcp6-protocol.h new file mode 100644 index 0000000000..f54f4ad618 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/dhcp6-protocol.h @@ -0,0 +1,106 @@ +#pragma once + +/*** + 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 . +***/ + +#include +#include + +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +struct DHCP6Message { + union { + struct { + uint8_t type; + uint8_t _pad[3]; + } _packed_; + be32_t transaction_id; + }; +} _packed_; + +typedef struct DHCP6Message DHCP6Message; + +#define DHCP6_MIN_OPTIONS_SIZE \ + 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) + +#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } + +enum { + DHCP6_PORT_SERVER = 547, + DHCP6_PORT_CLIENT = 546, +}; + +#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC +#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC +#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC +#define DHCP6_REQ_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_REQ_MAX_RT 120 * USEC_PER_SEC +#define DHCP6_REQ_MAX_RC 10 +#define DHCP6_REN_TIMEOUT 10 * USEC_PER_SEC +#define DHCP6_REN_MAX_RT 600 * USEC_PER_SEC +#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC +#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC + +enum DHCP6State { + DHCP6_STATE_STOPPED = 0, + DHCP6_STATE_INFORMATION_REQUEST = 1, + DHCP6_STATE_SOLICITATION = 2, + DHCP6_STATE_REQUEST = 3, + DHCP6_STATE_BOUND = 4, + DHCP6_STATE_RENEW = 5, + DHCP6_STATE_REBIND = 6, +}; + +enum { + DHCP6_SOLICIT = 1, + DHCP6_ADVERTISE = 2, + DHCP6_REQUEST = 3, + DHCP6_CONFIRM = 4, + DHCP6_RENEW = 5, + DHCP6_REBIND = 6, + DHCP6_REPLY = 7, + DHCP6_RELEASE = 8, + DHCP6_DECLINE = 9, + DHCP6_RECONFIGURE = 10, + DHCP6_INFORMATION_REQUEST = 11, + DHCP6_RELAY_FORW = 12, + DHCP6_RELAY_REPL = 13, + _DHCP6_MESSAGE_MAX = 14, +}; + +enum { + DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, + DHCP6_NTP_SUBOPTION_MC_ADDR = 2, + DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, +}; + +enum { + DHCP6_STATUS_SUCCESS = 0, + DHCP6_STATUS_UNSPEC_FAIL = 1, + DHCP6_STATUS_NO_ADDRS_AVAIL = 2, + DHCP6_STATUS_NO_BINDING = 3, + DHCP6_STATUS_NOT_ON_LINK = 4, + DHCP6_STATUS_USE_MULTICAST = 5, + _DHCP6_STATUS_MAX = 6, +}; diff --git a/src/libsystemd-network/include/systemd-network/icmp6-util.h b/src/libsystemd-network/include/systemd-network/icmp6-util.h new file mode 100644 index 0000000000..2b4dbc76ce --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/icmp6-util.h @@ -0,0 +1,25 @@ +#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 . +***/ + +#include + +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/include/systemd-network/lldp-internal.h b/src/libsystemd-network/include/systemd-network/lldp-internal.h new file mode 100644 index 0000000000..7992db6158 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/lldp-internal.h @@ -0,0 +1,56 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/prioq.h" + +#include "sd-lldp.h" + +struct sd_lldp { + unsigned n_ref; + + int ifindex; + int fd; + + sd_event *event; + int64_t event_priority; + sd_event_source *io_event_source; + sd_event_source *timer_event_source; + + Prioq *neighbor_by_expiry; + Hashmap *neighbor_by_id; + + uint64_t neighbors_max; + + sd_lldp_callback_t callback; + void *userdata; + + uint16_t capability_mask; + + struct ether_addr filter_address; +}; + +#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) +#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/include/systemd-network/lldp-neighbor.h b/src/libsystemd-network/include/systemd-network/lldp-neighbor.h new file mode 100644 index 0000000000..142872f2ad --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/lldp-neighbor.h @@ -0,0 +1,108 @@ +#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 . +***/ + +#include +#include +#include + +#include "basic/hash-funcs.h" +#include "basic/time-util.h" + +#include "lldp-internal.h" +#include "sd-lldp.h" + +typedef struct LLDPNeighborID { + /* The spec calls this an "MSAP identifier" */ + void *chassis_id; + size_t chassis_id_size; + + void *port_id; + size_t port_id_size; +} LLDPNeighborID; + +struct sd_lldp_neighbor { + /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ + sd_lldp *lldp; + unsigned n_ref; + + triple_timestamp timestamp; + + usec_t until; + unsigned prioq_idx; + + struct ether_addr source_address; + struct ether_addr destination_address; + + LLDPNeighborID id; + + /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ + size_t raw_size; + + /* The current read index for the iterative TLV interface */ + size_t rindex; + + /* And a couple of fields parsed out. */ + bool has_ttl:1; + bool has_capabilities:1; + bool has_port_vlan_id:1; + + uint16_t ttl; + + uint16_t system_capabilities; + uint16_t enabled_capabilities; + + char *port_description; + char *system_name; + char *system_description; + + uint16_t port_vlan_id; + + char *chassis_id_as_string; + char *port_id_as_string; +}; + +static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { + return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); +} + +static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) { + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; +} + +static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) { + uint8_t *p; + + p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; + return p[1] + (((size_t) (p[0] & 1)) << 8); +} + +static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; +} + +extern const struct hash_ops lldp_neighbor_id_hash_ops; +int lldp_neighbor_prioq_compare_func(const void *a, const void *b); + +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); +int lldp_neighbor_parse(sd_lldp_neighbor *n); +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); diff --git a/src/libsystemd-network/include/systemd-network/lldp-network.h b/src/libsystemd-network/include/systemd-network/lldp-network.h new file mode 100644 index 0000000000..43ed54b3b2 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/lldp-network.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int lldp_network_bind_raw_socket(int ifindex); diff --git a/src/libsystemd-network/include/systemd-network/ndisc-internal.h b/src/libsystemd-network/include/systemd-network/ndisc-internal.h new file mode 100644 index 0000000000..b58d29750e --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/ndisc-internal.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/log.h" + +#include "sd-ndisc.h" + +struct sd_ndisc { + unsigned n_ref; + + int ifindex; + int fd; + + sd_event *event; + int event_priority; + + struct ether_addr mac_addr; + uint8_t hop_limit; + uint32_t mtu; + + sd_event_source *recv_event_source; + sd_event_source *timeout_event_source; + + unsigned nd_sent; + + sd_ndisc_callback_t callback; + void *userdata; +}; + +#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__) +#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/include/systemd-network/ndisc-router.h b/src/libsystemd-network/include/systemd-network/ndisc-router.h new file mode 100644 index 0000000000..d1b071da7f --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/ndisc-router.h @@ -0,0 +1,62 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/time-util.h" + +#include "sd-ndisc.h" + +struct sd_ndisc_router { + unsigned n_ref; + + triple_timestamp timestamp; + struct in6_addr address; + + /* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */ + size_t raw_size; + + /* The current read index for the iterative option interface */ + size_t rindex; + + uint64_t flags; + unsigned preference; + uint16_t lifetime; + + uint8_t hop_limit; + uint32_t mtu; +}; + +static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) { + return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router)); +} + +static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) { + return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex; +} + +static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) { + return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0]; +} +static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) { + return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8; +} + +sd_ndisc_router *ndisc_router_new(size_t raw_size); +int ndisc_router_parse(sd_ndisc_router *rt); diff --git a/src/libsystemd-network/include/systemd-network/network-internal.h b/src/libsystemd-network/include/systemd-network/network-internal.h new file mode 100644 index 0000000000..a5cf8e4f1c --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/network-internal.h @@ -0,0 +1,81 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include + +#include "shared/condition.h" +#include "udev.h" + +#include "sd-dhcp-lease.h" + +bool net_match_config(const struct ether_addr *match_mac, + char * const *match_path, + char * const *match_driver, + char * const *match_type, + char * const *match_name, + Condition *match_host, + Condition *match_virt, + Condition *match_kernel, + Condition *match_arch, + const struct ether_addr *dev_mac, + const char *dev_path, + const char *dev_parent_driver, + const char *dev_driver, + const char *dev_type, + const char *dev_name); + +int config_parse_net_condition(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_hwaddr(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + +int config_parse_ifnames(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_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 config_parse_iaid(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, 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); +int deserialize_in_addrs(struct in_addr **addresses, const char *string); +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, + size_t size); +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, 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); +int deserialize_dhcp_option(void **data, size_t *data_len, const char *string); diff --git a/src/libsystemd-network/include/systemd-network/sd-dhcp-client.h b/src/libsystemd-network/include/systemd-network/sd-dhcp-client.h new file mode 100644 index 0000000000..ce1145a575 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-dhcp-client.h @@ -0,0 +1,157 @@ +#ifndef foosddhcpclienthfoo +#define foosddhcpclienthfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include +#include + +#include + +#include "_sd-common.h" +#include "sd-dhcp-lease.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DHCP_CLIENT_EVENT_STOP = 0, + SD_DHCP_CLIENT_EVENT_IP_ACQUIRE = 1, + SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, + SD_DHCP_CLIENT_EVENT_EXPIRED = 3, + 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_callback_t)(sd_dhcp_client *client, int event, void *userdata); +int sd_dhcp_client_set_callback( + sd_dhcp_client *client, + sd_dhcp_client_callback_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); +int sd_dhcp_client_set_request_broadcast( + sd_dhcp_client *client, + int broadcast); +int sd_dhcp_client_set_ifindex( + sd_dhcp_client *client, + int interface_index); +int sd_dhcp_client_set_mac( + sd_dhcp_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type); +int sd_dhcp_client_set_client_id( + sd_dhcp_client *client, + uint8_t type, + const uint8_t *data, + size_t data_len); +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp_client_get_client_id( + sd_dhcp_client *client, + uint8_t *type, + const uint8_t **data, + size_t *data_len); +int sd_dhcp_client_set_mtu( + sd_dhcp_client *client, + uint32_t mtu); +int sd_dhcp_client_set_hostname( + sd_dhcp_client *client, + const char *hostname); +int sd_dhcp_client_set_vendor_class_identifier( + sd_dhcp_client *client, + const char *vci); +int sd_dhcp_client_get_lease( + sd_dhcp_client *client, + sd_dhcp_lease **ret); + +int sd_dhcp_client_stop(sd_dhcp_client *client); +int sd_dhcp_client_start(sd_dhcp_client *client); + +sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); +sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); + +int sd_dhcp_client_new(sd_dhcp_client **ret); + +int sd_dhcp_client_attach_event( + sd_dhcp_client *client, + sd_event *event, + int64_t priority); +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/libsystemd-network/include/systemd-network/sd-dhcp-lease.h b/src/libsystemd-network/include/systemd-network/sd-dhcp-lease.h new file mode 100644 index 0000000000..2f565ca825 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-dhcp-lease.h @@ -0,0 +1,67 @@ +#ifndef foosddhcpleasehfoo +#define foosddhcpleasehfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp_lease sd_dhcp_lease; +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); + +int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); +int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); +int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); +int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); +int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); +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, 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/libsystemd-network/include/systemd-network/sd-dhcp-server.h b/src/libsystemd-network/include/systemd-network/sd-dhcp-server.h new file mode 100644 index 0000000000..bbb2bb203c --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-dhcp-server.h @@ -0,0 +1,65 @@ +#ifndef foosddhcpserverhfoo +#define foosddhcpserverhfoo + +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include +#include + +#include + +#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); + +sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); +sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server); + +int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t priority); +int sd_dhcp_server_detach_event(sd_dhcp_server *client); +sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); + +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); + +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); +int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); + +int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); +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/libsystemd-network/include/systemd-network/sd-dhcp6-client.h b/src/libsystemd-network/include/systemd-network/sd-dhcp6-client.h new file mode 100644 index 0000000000..cfd5f35135 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-dhcp6-client.h @@ -0,0 +1,134 @@ +#ifndef foosddhcp6clienthfoo +#define foosddhcp6clienthfoo + +/*** + 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 . +***/ + +#include +#include +#include + +#include + +#include "_sd-common.h" +#include "sd-dhcp6-lease.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DHCP6_CLIENT_EVENT_STOP = 0, + SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE = 10, + SD_DHCP6_CLIENT_EVENT_RETRANS_MAX = 11, + SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE = 12, + 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_callback_t)(sd_dhcp6_client *client, int event, void *userdata); +int sd_dhcp6_client_set_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata); + +int sd_dhcp6_client_set_ifindex( + 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 duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp6_client_set_iaid( + sd_dhcp6_client *client, + uint32_t iaid); +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); + +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, + int64_t priority); +int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); +sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); +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/libsystemd-network/include/systemd-network/sd-dhcp6-lease.h b/src/libsystemd-network/include/systemd-network/sd-dhcp6-lease.h new file mode 100644 index 0000000000..184fbb8e0d --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-dhcp6-lease.h @@ -0,0 +1,52 @@ +#ifndef foosddhcp6leasehfoo +#define foosddhcp6leasehfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + 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 . +***/ + +#include +#include + +#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); +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, + struct in6_addr *addr, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid); + +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs); +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/libsystemd-network/include/systemd-network/sd-ipv4acd.h b/src/libsystemd-network/include/systemd-network/sd-ipv4acd.h new file mode 100644 index 0000000000..e5ccb4b971 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-ipv4acd.h @@ -0,0 +1,60 @@ +#ifndef foosdipv4acdfoo +#define foosdipv4acdfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_IPV4ACD_EVENT_STOP = 0, + SD_IPV4ACD_EVENT_BIND = 1, + SD_IPV4ACD_EVENT_CONFLICT = 2, +}; + +typedef struct sd_ipv4acd sd_ipv4acd; +typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata); + +int sd_ipv4acd_detach_event(sd_ipv4acd *acd); +int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority); +int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address); +int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata); +int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr); +int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); +int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address); +int sd_ipv4acd_is_running(sd_ipv4acd *acd); +int sd_ipv4acd_start(sd_ipv4acd *acd); +int sd_ipv4acd_stop(sd_ipv4acd *acd); +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd); +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd); +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/libsystemd-network/include/systemd-network/sd-ipv4ll.h b/src/libsystemd-network/include/systemd-network/sd-ipv4ll.h new file mode 100644 index 0000000000..cff1865d05 --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-ipv4ll.h @@ -0,0 +1,60 @@ +#ifndef foosdipv4llfoo +#define foosdipv4llfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. 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 . +***/ + +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_IPV4LL_EVENT_STOP = 0, + SD_IPV4LL_EVENT_BIND = 1, + SD_IPV4LL_EVENT_CONFLICT = 2, +}; + +typedef struct sd_ipv4ll sd_ipv4ll; +typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); + +int sd_ipv4ll_detach_event(sd_ipv4ll *ll); +int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); +int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); +int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); +int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); +int sd_ipv4ll_set_ifindex(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, uint64_t seed); +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/libsystemd-network/include/systemd-network/sd-lldp.h b/src/libsystemd-network/include/systemd-network/sd-lldp.h new file mode 100644 index 0000000000..928f77f0ab --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-lldp.h @@ -0,0 +1,182 @@ +#ifndef foosdlldphfoo +#define foosdlldphfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* IEEE 802.3AB Clause 9: TLV Types */ +enum { + SD_LLDP_TYPE_END = 0, + SD_LLDP_TYPE_CHASSIS_ID = 1, + SD_LLDP_TYPE_PORT_ID = 2, + SD_LLDP_TYPE_TTL = 3, + SD_LLDP_TYPE_PORT_DESCRIPTION = 4, + SD_LLDP_TYPE_SYSTEM_NAME = 5, + SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, + SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, + SD_LLDP_TYPE_MGMT_ADDRESS = 8, + SD_LLDP_TYPE_PRIVATE = 127, +}; + +/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ +enum { + SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, + SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, + SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, + SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, + SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, + SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, +}; + +/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +enum { + SD_LLDP_PORT_SUBTYPE_RESERVED = 0, + SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, + SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, + SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, + SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, + SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, + SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, + SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, +}; + +enum { + SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, + SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, + SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, + SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, + SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, +}; + +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) + +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ + ((uint16_t) \ + (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) + +#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } +#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } + +enum { + SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, + SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, + SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, + SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, + SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, + SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, + SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, +}; + +typedef struct sd_lldp sd_lldp; +typedef struct sd_lldp_neighbor sd_lldp_neighbor; + +typedef enum sd_lldp_event { + SD_LLDP_EVENT_ADDED = 'a', + SD_LLDP_EVENT_REMOVED = 'r', + SD_LLDP_EVENT_UPDATED = 'u', + SD_LLDP_EVENT_REFRESHED = 'f', +} sd_lldp_event; + +typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); + +int sd_lldp_new(sd_lldp **ret); +sd_lldp* sd_lldp_ref(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); + +int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); +int sd_lldp_detach_event(sd_lldp *lldp); +sd_event *sd_lldp_get_event(sd_lldp *lldp); + +int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); +int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex); + +/* Controls how much and what to store in the neighbors database */ +int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); +int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); +int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); + +int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); +sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); +sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); + +/* Access to LLDP frame metadata */ +int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); +int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); +int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); +int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd-network/include/systemd-network/sd-ndisc.h b/src/libsystemd-network/include/systemd-network/sd-ndisc.h new file mode 100644 index 0000000000..de2329458a --- /dev/null +++ b/src/libsystemd-network/include/systemd-network/sd-ndisc.h @@ -0,0 +1,130 @@ +#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 . +***/ + +#include +#include +#include +#include + +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Neightbor Discovery Options, RFC 4861, Section 4.6 and + * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */ +enum { + SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, + SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2, + SD_NDISC_OPTION_PREFIX_INFORMATION = 3, + SD_NDISC_OPTION_MTU = 5, + SD_NDISC_OPTION_ROUTE_INFORMATION = 24, + SD_NDISC_OPTION_RDNSS = 25, + SD_NDISC_OPTION_FLAGS_EXTENSION = 26, + SD_NDISC_OPTION_DNSSL = 31, + SD_NDISC_OPTION_CAPTIVE_PORTAL = 37, +}; + +/* Route preference, RFC 4191, Section 2.1 */ +enum { + SD_NDISC_PREFERENCE_LOW = 3U, + SD_NDISC_PREFERENCE_MEDIUM = 0U, + SD_NDISC_PREFERENCE_HIGH = 1U, +}; + +typedef struct sd_ndisc sd_ndisc; +typedef struct sd_ndisc_router sd_ndisc_router; + +typedef enum sd_ndisc_event { + SD_NDISC_EVENT_TIMEOUT = 't', + SD_NDISC_EVENT_ROUTER = 'r', +} sd_ndisc_event; + +typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata); + +int sd_ndisc_new(sd_ndisc **ret); +sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); +sd_ndisc *sd_ndisc_unref(sd_ndisc *nd); + +int sd_ndisc_start(sd_ndisc *nd); +int sd_ndisc_stop(sd_ndisc *nd); + +int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority); +int sd_ndisc_detach_event(sd_ndisc *nd); +sd_event *sd_ndisc_get_event(sd_ndisc *nd); + +int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata); +int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index); +int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); + +int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret); +int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret); + +int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size); +sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt); +sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt); + +int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); +int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); +int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); + +int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret); +int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags); +int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret); +int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime); +int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret); + +/* Generic option access */ +int sd_ndisc_router_option_rewind(sd_ndisc_router *rt); +int sd_ndisc_router_option_next(sd_ndisc_router *rt); +int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret); +int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type); +int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); + +/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */ +int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret); +int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret); +int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret); +int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); +int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); + +/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */ +int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); +int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); +int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); +int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret); + +/* Specific option access: SD_NDISC_OPTION_RDNSS */ +int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret); +int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); + +/* Specific option access: SD_NDISC_OPTION_DNSSL */ +int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret); +int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h deleted file mode 100644 index becc162fab..0000000000 --- a/src/libsystemd-network/lldp-internal.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-event.h" -#include "sd-lldp.h" - -#include "hashmap.h" -#include "log.h" -#include "prioq.h" - -struct sd_lldp { - unsigned n_ref; - - int ifindex; - int fd; - - sd_event *event; - int64_t event_priority; - sd_event_source *io_event_source; - sd_event_source *timer_event_source; - - Prioq *neighbor_by_expiry; - Hashmap *neighbor_by_id; - - uint64_t neighbors_max; - - sd_lldp_callback_t callback; - void *userdata; - - uint16_t capability_mask; - - struct ether_addr filter_address; -}; - -#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) -#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c deleted file mode 100644 index 53e29377b3..0000000000 --- a/src/libsystemd-network/lldp-neighbor.c +++ /dev/null @@ -1,813 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "escape.h" -#include "ether-addr-util.h" -#include "hexdecoct.h" -#include "in-addr-util.h" -#include "lldp-internal.h" -#include "lldp-neighbor.h" -#include "unaligned.h" - -static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { - const LLDPNeighborID *id = p; - - siphash24_compress(id->chassis_id, id->chassis_id_size, state); - siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); - siphash24_compress(id->port_id, id->port_id_size, state); - siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); -} - -static int lldp_neighbor_id_compare_func(const void *a, const void *b) { - const LLDPNeighborID *x = a, *y = b; - int r; - - r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); - if (r != 0) - return r; - - if (x->chassis_id_size < y->chassis_id_size) - return -1; - - if (x->chassis_id_size > y->chassis_id_size) - return 1; - - r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); - if (r != 0) - return r; - - if (x->port_id_size < y->port_id_size) - return -1; - if (x->port_id_size > y->port_id_size) - return 1; - - return 0; -} - -const struct hash_ops lldp_neighbor_id_hash_ops = { - .hash = lldp_neighbor_id_hash_func, - .compare = lldp_neighbor_id_compare_func -}; - -int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { - const sd_lldp_neighbor *x = a, *y = b; - - if (x->until < y->until) - return -1; - - if (x->until > y->until) - return 1; - - return 0; -} - -_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { - if (!n) - return NULL; - - assert(n->n_ref > 0 || n->lldp); - n->n_ref++; - - return n; -} - -static void lldp_neighbor_free(sd_lldp_neighbor *n) { - assert(n); - - free(n->id.port_id); - free(n->id.chassis_id); - free(n->port_description); - free(n->system_name); - free(n->system_description); - free(n->chassis_id_as_string); - free(n->port_id_as_string); - free(n); -} - -_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { - - /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from - * the sd_lldp object. */ - - if (!n) - return NULL; - - assert(n->n_ref > 0); - n->n_ref--; - - if (n->n_ref <= 0 && !n->lldp) - lldp_neighbor_free(n); - - return NULL; -} - -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { - - /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ - - if (!n) - return NULL; - - if (!n->lldp) - return NULL; - - assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); - assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); - - n->lldp = NULL; - - if (n->n_ref <= 0) - lldp_neighbor_free(n); - - return NULL; -} - -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { - sd_lldp_neighbor *n; - - n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); - if (!n) - return NULL; - - n->raw_size = raw_size; - n->n_ref = 1; - - return n; -} - -static int parse_string(char **s, const void *q, size_t n) { - const char *p = q; - char *k; - - assert(s); - assert(p || n == 0); - - if (*s) { - log_lldp("Found duplicate string, ignoring field."); - return 0; - } - - /* Strip trailing NULs, just to be nice */ - while (n > 0 && p[n-1] == 0) - n--; - - if (n <= 0) /* Ignore empty strings */ - return 0; - - /* Look for inner NULs */ - if (memchr(p, 0, n)) { - log_lldp("Found inner NUL in string, ignoring field."); - return 0; - } - - /* Let's escape weird chars, for security reasons */ - k = cescape_length(p, n); - if (!k) - return -ENOMEM; - - free(*s); - *s = k; - - return 1; -} - -int lldp_neighbor_parse(sd_lldp_neighbor *n) { - struct ether_header h; - const uint8_t *p; - size_t left; - int r; - - assert(n); - - if (n->raw_size < sizeof(struct ether_header)) { - log_lldp("Received truncated packet, ignoring."); - return -EBADMSG; - } - - memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); - - if (h.ether_type != htobe16(ETHERTYPE_LLDP)) { - log_lldp("Received packet with wrong type, ignoring."); - return -EBADMSG; - } - - if (h.ether_dhost[0] != 0x01 || - h.ether_dhost[1] != 0x80 || - h.ether_dhost[2] != 0xc2 || - h.ether_dhost[3] != 0x00 || - h.ether_dhost[4] != 0x00 || - !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { - log_lldp("Received packet with wrong destination address, ignoring."); - return -EBADMSG; - } - - memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); - memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); - - p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); - left = n->raw_size - sizeof(struct ether_header); - - for (;;) { - uint8_t type; - uint16_t length; - - if (left < 2) { - log_lldp("TLV lacks header, ignoring."); - return -EBADMSG; - } - - type = p[0] >> 1; - length = p[1] + (((uint16_t) (p[0] & 1)) << 8); - p += 2, left -= 2; - - if (left < length) { - log_lldp("TLV truncated, ignoring datagram."); - return -EBADMSG; - } - - switch (type) { - - case SD_LLDP_TYPE_END: - if (length != 0) { - log_lldp("End marker TLV not zero-sized, ignoring datagram."); - return -EBADMSG; - } - if (left != 0) { - log_lldp("Trailing garbage in datagram, ignoring datagram."); - return -EBADMSG; - } - - goto end_marker; - - case SD_LLDP_TYPE_CHASSIS_ID: - if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */ - log_lldp("Chassis ID field size out of range, ignoring datagram."); - return -EBADMSG; - } - if (n->id.chassis_id) { - log_lldp("Duplicate chassis ID field, ignoring datagram."); - return -EBADMSG; - } - - n->id.chassis_id = memdup(p, length); - if (!n->id.chassis_id) - return -ENOMEM; - - n->id.chassis_id_size = length; - break; - - case SD_LLDP_TYPE_PORT_ID: - if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */ - log_lldp("Port ID field size out of range, ignoring datagram."); - return -EBADMSG; - } - if (n->id.port_id) { - log_lldp("Duplicate port ID field, ignoring datagram."); - return -EBADMSG; - } - - n->id.port_id = memdup(p, length); - if (!n->id.port_id) - return -ENOMEM; - - n->id.port_id_size = length; - break; - - case SD_LLDP_TYPE_TTL: - if (length != 2) { - log_lldp("TTL field has wrong size, ignoring datagram."); - return -EBADMSG; - } - - if (n->has_ttl) { - log_lldp("Duplicate TTL field, ignoring datagram."); - return -EBADMSG; - } - - n->ttl = unaligned_read_be16(p); - n->has_ttl = true; - break; - - case SD_LLDP_TYPE_PORT_DESCRIPTION: - r = parse_string(&n->port_description, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_NAME: - r = parse_string(&n->system_name, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: - r = parse_string(&n->system_description, p, length); - if (r < 0) - return r; - break; - - case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: - if (length != 4) - log_lldp("System capabilities field has wrong size, ignoring."); - else { - n->system_capabilities = unaligned_read_be16(p); - n->enabled_capabilities = unaligned_read_be16(p + 2); - n->has_capabilities = true; - } - - break; - - case SD_LLDP_TYPE_PRIVATE: - if (length < 4) - log_lldp("Found private TLV that is too short, ignoring."); - - break; - } - - - p += length, left -= length; - } - -end_marker: - if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { - log_lldp("One or more mandatory TLV missing in datagram. Ignoring."); - return -EBADMSG; - - } - - n->rindex = sizeof(struct ether_header); - - return 0; -} - -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { - assert(n); - - if (n->ttl > 0) { - usec_t base; - - /* Use the packet's timestamp if there is one known */ - base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); - if (base <= 0 || base == USEC_INFINITY) - base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ - - n->until = usec_add(base, n->ttl * USEC_PER_SEC); - } else - n->until = 0; - - if (n->lldp) - prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); -} - -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { - if (a == b) - return true; - - if (!a || !b) - return false; - - if (a->raw_size != b->raw_size) - return false; - - return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; -} - -_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { - assert_return(n, -EINVAL); - assert_return(address, -EINVAL); - - *address = n->source_address; - return 0; -} - -_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { - assert_return(n, -EINVAL); - assert_return(address, -EINVAL); - - *address = n->destination_address; - return 0; -} - -_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - *ret = LLDP_NEIGHBOR_RAW(n); - *size = n->raw_size; - - return 0; -} - -_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - assert(n->id.chassis_id_size > 0); - - *type = *(uint8_t*) n->id.chassis_id; - *ret = (uint8_t*) n->id.chassis_id + 1; - *size = n->id.chassis_id_size - 1; - - return 0; -} - -static int format_mac_address(const void *data, size_t sz, char **ret) { - struct ether_addr a; - char *k; - - assert(data || sz <= 0); - - if (sz != 7) - return 0; - - memcpy(&a, (uint8_t*) data + 1, sizeof(a)); - - k = new(char, ETHER_ADDR_TO_STRING_MAX); - if (!k) - return -ENOMEM; - - *ret = ether_addr_to_string(&a, k); - return 1; -} - -static int format_network_address(const void *data, size_t sz, char **ret) { - union in_addr_union a; - int family, r; - - if (sz == 6 && ((uint8_t*) data)[1] == 1) { - memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); - family = AF_INET; - } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { - memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); - family = AF_INET6; - } else - return 0; - - r = in_addr_to_string(family, &a, ret); - if (r < 0) - return r; - return 1; -} - -_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { - char *k; - int r; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (n->chassis_id_as_string) { - *ret = n->chassis_id_as_string; - return 0; - } - - assert(n->id.chassis_id_size > 0); - - switch (*(uint8_t*) n->id.chassis_id) { - - case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: - k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); - if (!k) - return -ENOMEM; - - goto done; - - case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: - r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - - case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: - r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - } - - /* Generic fallback */ - k = hexmem(n->id.chassis_id, n->id.chassis_id_size); - if (!k) - return -ENOMEM; - -done: - *ret = n->chassis_id_as_string = k; - return 0; -} - -_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - assert(n->id.port_id_size > 0); - - *type = *(uint8_t*) n->id.port_id; - *ret = (uint8_t*) n->id.port_id + 1; - *size = n->id.port_id_size - 1; - - return 0; -} - -_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { - char *k; - int r; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (n->port_id_as_string) { - *ret = n->port_id_as_string; - return 0; - } - - assert(n->id.port_id_size > 0); - - switch (*(uint8_t*) n->id.port_id) { - - case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: - case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: - case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: - case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: - k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); - if (!k) - return -ENOMEM; - - goto done; - - case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: - r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - - case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: - r = format_network_address(n->id.port_id, n->id.port_id_size, &k); - if (r < 0) - return r; - if (r > 0) - goto done; - - break; - } - - /* Generic fallback */ - k = hexmem(n->id.port_id, n->id.port_id_size); - if (!k) - return -ENOMEM; - -done: - *ret = n->port_id_as_string = k; - return 0; -} - -_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { - assert_return(n, -EINVAL); - assert_return(ret_sec, -EINVAL); - - *ret_sec = n->ttl; - return 0; -} - -_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->system_name) - return -ENODATA; - - *ret = n->system_name; - return 0; -} - -_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->system_description) - return -ENODATA; - - *ret = n->system_description; - return 0; -} - -_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->port_description) - return -ENODATA; - - *ret = n->port_description; - return 0; -} - -_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->has_capabilities) - return -ENODATA; - - *ret = n->system_capabilities; - return 0; -} - -_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - - if (!n->has_capabilities) - return -ENODATA; - - *ret = n->enabled_capabilities; - return 0; -} - -_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(raw || raw_size <= 0, -EINVAL); - - n = lldp_neighbor_new(raw_size); - if (!n) - return -ENOMEM; - - memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size); - r = lldp_neighbor_parse(n); - if (r < 0) - return r; - - *ret = n; - n = NULL; - - return r; -} - -_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { - assert_return(n, -EINVAL); - - assert(n->raw_size >= sizeof(struct ether_header)); - n->rindex = sizeof(struct ether_header); - - return n->rindex < n->raw_size; -} - -_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { - size_t length; - - assert_return(n, -EINVAL); - - if (n->rindex == n->raw_size) /* EOF */ - return -ESPIPE; - - if (n->rindex + 2 > n->raw_size) /* Truncated message */ - return -EBADMSG; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - n->rindex += 2 + length; - return n->rindex < n->raw_size; -} - -_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { - assert_return(n, -EINVAL); - assert_return(type, -EINVAL); - - if (n->rindex == n->raw_size) /* EOF */ - return -ESPIPE; - - if (n->rindex + 2 > n->raw_size) - return -EBADMSG; - - *type = LLDP_NEIGHBOR_TLV_TYPE(n); - return 0; -} - -_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { - uint8_t k; - int r; - - assert_return(n, -EINVAL); - - r = sd_lldp_neighbor_tlv_get_type(n, &k); - if (r < 0) - return r; - - return type == k; -} - -_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) { - const uint8_t *d; - size_t length; - int r; - - assert_return(n, -EINVAL); - assert_return(oui, -EINVAL); - assert_return(subtype, -EINVAL); - - r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); - if (r < 0) - return r; - if (r == 0) - return -ENXIO; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (length < 4) - return -EBADMSG; - - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - d = LLDP_NEIGHBOR_TLV_DATA(n); - memcpy(oui, d, 3); - *subtype = d[3]; - - return 0; -} - -_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) { - uint8_t k[3], st; - int r; - - r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); - if (r == -ENXIO) - return 0; - if (r < 0) - return r; - - return memcmp(k, oui, 3) == 0 && st == subtype; -} - -_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { - size_t length; - - assert_return(n, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - /* Note that this returns the full TLV, including the TLV header */ - - if (n->rindex + 2 > n->raw_size) - return -EBADMSG; - - length = LLDP_NEIGHBOR_TLV_LENGTH(n); - if (n->rindex + 2 + length > n->raw_size) - return -EBADMSG; - - *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; - *size = length + 2; - - return 0; -} - -_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { - assert_return(n, -EINVAL); - assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); - assert_return(clock_supported(clock), -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (!triple_timestamp_is_set(&n->timestamp)) - return -ENODATA; - - *ret = triple_timestamp_by_clock(&n->timestamp, clock); - return 0; -} diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h deleted file mode 100644 index c1a7606d06..0000000000 --- a/src/libsystemd-network/lldp-neighbor.h +++ /dev/null @@ -1,108 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -#include "sd-lldp.h" - -#include "hash-funcs.h" -#include "lldp-internal.h" -#include "time-util.h" - -typedef struct LLDPNeighborID { - /* The spec calls this an "MSAP identifier" */ - void *chassis_id; - size_t chassis_id_size; - - void *port_id; - size_t port_id_size; -} LLDPNeighborID; - -struct sd_lldp_neighbor { - /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ - sd_lldp *lldp; - unsigned n_ref; - - triple_timestamp timestamp; - - usec_t until; - unsigned prioq_idx; - - struct ether_addr source_address; - struct ether_addr destination_address; - - LLDPNeighborID id; - - /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ - size_t raw_size; - - /* The current read index for the iterative TLV interface */ - size_t rindex; - - /* And a couple of fields parsed out. */ - bool has_ttl:1; - bool has_capabilities:1; - bool has_port_vlan_id:1; - - uint16_t ttl; - - uint16_t system_capabilities; - uint16_t enabled_capabilities; - - char *port_description; - char *system_name; - char *system_description; - - uint16_t port_vlan_id; - - char *chassis_id_as_string; - char *port_id_as_string; -}; - -static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { - return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); -} - -static inline uint8_t LLDP_NEIGHBOR_TLV_TYPE(const sd_lldp_neighbor *n) { - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; -} - -static inline size_t LLDP_NEIGHBOR_TLV_LENGTH(const sd_lldp_neighbor *n) { - uint8_t *p; - - p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; - return p[1] + (((size_t) (p[0] & 1)) << 8); -} - -static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { - return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; -} - -extern const struct hash_ops lldp_neighbor_id_hash_ops; -int lldp_neighbor_prioq_compare_func(const void *a, const void *b); - -sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); -sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); -int lldp_neighbor_parse(sd_lldp_neighbor *n); -void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); -bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c deleted file mode 100644 index 59c25598e9..0000000000 --- a/src/libsystemd-network/lldp-network.c +++ /dev/null @@ -1,77 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fd-util.h" -#include "lldp-network.h" -#include "socket-util.h" - -int lldp_network_bind_raw_socket(int ifindex) { - - static const struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ - BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ - }; - - static const struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter, - }; - - union sockaddr_union saddrll = { - .ll.sll_family = AF_PACKET, - .ll.sll_ifindex = ifindex, - }; - - _cleanup_close_ int fd = -1; - int r; - - assert(ifindex > 0); - - fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, - htobe16(ETHERTYPE_LLDP)); - if (fd < 0) - return -errno; - - r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); - if (r < 0) - return -errno; - - r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); - if (r < 0) - return -errno; - - r = fd; - fd = -1; - - return r; -} diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h deleted file mode 100644 index c4cf8c79f1..0000000000 --- a/src/libsystemd-network/lldp-network.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-event.h" - -int lldp_network_bind_raw_socket(int ifindex); diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h deleted file mode 100644 index 60e183ff8c..0000000000 --- a/src/libsystemd-network/ndisc-internal.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "log.h" - -#include "sd-ndisc.h" - -struct sd_ndisc { - unsigned n_ref; - - int ifindex; - int fd; - - sd_event *event; - int event_priority; - - struct ether_addr mac_addr; - uint8_t hop_limit; - uint32_t mtu; - - sd_event_source *recv_event_source; - sd_event_source *timeout_event_source; - - unsigned nd_sent; - - sd_ndisc_callback_t callback; - void *userdata; -}; - -#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__) -#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c deleted file mode 100644 index d9950b638c..0000000000 --- a/src/libsystemd-network/ndisc-router.c +++ /dev/null @@ -1,779 +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 . -***/ - -#include - -#include "sd-ndisc.h" - -#include "alloc-util.h" -#include "dns-domain.h" -#include "hostname-util.h" -#include "missing.h" -#include "ndisc-internal.h" -#include "ndisc-router.h" -#include "strv.h" - -_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) { - if (!rt) - return NULL; - - assert(rt->n_ref > 0); - rt->n_ref++; - - return rt; -} - -_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) { - if (!rt) - return NULL; - - assert(rt->n_ref > 0); - rt->n_ref--; - - if (rt->n_ref > 0) - return NULL; - - free(rt); - return NULL; -} - -sd_ndisc_router *ndisc_router_new(size_t raw_size) { - sd_ndisc_router *rt; - - rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size); - if (!rt) - return NULL; - - rt->raw_size = raw_size; - rt->n_ref = 1; - - return rt; -} - -_public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) { - _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(raw || raw_size <= 0, -EINVAL); - - rt = ndisc_router_new(raw_size); - if (!rt) - return -ENOMEM; - - memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size); - r = ndisc_router_parse(rt); - if (r < 0) - return r; - - *ret = rt; - rt = NULL; - - return r; -} - -_public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - if (in6_addr_is_null(&rt->address)) - return -ENODATA; - - *ret_addr = rt->address; - return 0; -} - -_public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { - assert_return(rt, -EINVAL); - assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); - assert_return(clock_supported(clock), -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (!triple_timestamp_is_set(&rt->timestamp)) - return -ENODATA; - - *ret = triple_timestamp_by_clock(&rt->timestamp, clock); - return 0; -} - -_public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - *ret = NDISC_ROUTER_RAW(rt); - *size = rt->raw_size; - - return 0; -} - -int ndisc_router_parse(sd_ndisc_router *rt) { - struct nd_router_advert *a; - const uint8_t *p; - bool has_mtu = false, has_flag_extension = false; - size_t left; - - assert(rt); - - if (rt->raw_size < sizeof(struct nd_router_advert)) { - log_ndisc("Too small to be a router advertisement, ignoring."); - return -EBADMSG; - } - - /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */ - a = NDISC_ROUTER_RAW(rt); - - if (a->nd_ra_type != ND_ROUTER_ADVERT) { - log_ndisc("Received ND packet that is not a router advertisement, ignoring."); - return -EBADMSG; - } - - if (a->nd_ra_code != 0) { - log_ndisc("Received ND packet with wrong RA code, ignoring."); - return -EBADMSG; - } - - rt->hop_limit = a->nd_ra_curhoplimit; - rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */ - rt->lifetime = be16toh(a->nd_ra_router_lifetime); - - rt->preference = (rt->flags >> 3) & 3; - if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) - rt->preference = SD_NDISC_PREFERENCE_MEDIUM; - - p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert); - left = rt->raw_size - sizeof(struct nd_router_advert); - - for (;;) { - uint8_t type; - size_t length; - - if (left == 0) - break; - - if (left < 2) { - log_ndisc("Option lacks header, ignoring datagram."); - return -EBADMSG; - } - - type = p[0]; - length = p[1] * 8; - - if (length == 0) { - log_ndisc("Zero-length option, ignoring datagram."); - return -EBADMSG; - } - if (left < length) { - log_ndisc("Option truncated, ignoring datagram."); - return -EBADMSG; - } - - switch (type) { - - case SD_NDISC_OPTION_PREFIX_INFORMATION: - - if (length != 4*8) { - log_ndisc("Prefix option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - if (p[2] > 128) { - log_ndisc("Bad prefix length, ignoring datagram."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_MTU: { - uint32_t m; - - if (has_mtu) { - log_ndisc("MTU option specified twice, ignoring."); - continue; - } - - if (length != 8) { - log_ndisc("MTU option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - m = be32toh(*(uint32_t*) (p + 4)); - if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */ - rt->mtu = m; - - has_mtu = true; - break; - } - - case SD_NDISC_OPTION_ROUTE_INFORMATION: - if (length < 1*8 || length > 3*8) { - log_ndisc("Route information option of invalid size, ignoring datagram."); - return -EBADMSG; - } - - if (p[2] > 128) { - log_ndisc("Bad route prefix length, ignoring datagram."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_RDNSS: - if (length < 3*8 || (length % (2*8)) != 1*8) { - log_ndisc("RDNSS option has invalid size."); - return -EBADMSG; - } - - break; - - case SD_NDISC_OPTION_FLAGS_EXTENSION: - - if (has_flag_extension) { - log_ndisc("Flags extension option specified twice, ignoring."); - continue; - } - - if (length < 1*8) { - log_ndisc("Flags extension option has invalid size."); - return -EBADMSG; - } - - /* Add in the additional flags bits */ - rt->flags |= - ((uint64_t) p[2] << 8) | - ((uint64_t) p[3] << 16) | - ((uint64_t) p[4] << 24) | - ((uint64_t) p[5] << 32) | - ((uint64_t) p[6] << 40) | - ((uint64_t) p[7] << 48); - - has_flag_extension = true; - break; - - case SD_NDISC_OPTION_DNSSL: - if (length < 2*8) { - log_ndisc("DNSSL option has invalid size."); - return -EBADMSG; - } - - break; - } - - p += length, left -= length; - } - - rt->rindex = sizeof(struct nd_router_advert); - return 0; -} - -_public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - *ret = rt->hop_limit; - return 0; -} - -_public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) { - assert_return(rt, -EINVAL); - assert_return(ret_flags, -EINVAL); - - *ret_flags = rt->flags; - return 0; -} - -_public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) { - assert_return(rt, -EINVAL); - assert_return(ret_lifetime, -EINVAL); - - *ret_lifetime = rt->lifetime; - return 0; -} - -_public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - *ret = rt->preference; - return 0; -} - -_public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - if (rt->mtu <= 0) - return -ENODATA; - - *ret = rt->mtu; - return 0; -} - -_public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { - assert_return(rt, -EINVAL); - - assert(rt->raw_size >= sizeof(struct nd_router_advert)); - rt->rindex = sizeof(struct nd_router_advert); - - return rt->rindex < rt->raw_size; -} - -_public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) { - size_t length; - - assert_return(rt, -EINVAL); - - if (rt->rindex == rt->raw_size) /* EOF */ - return -ESPIPE; - - if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ - return -EBADMSG; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (rt->rindex + length > rt->raw_size) - return -EBADMSG; - - rt->rindex += length; - return rt->rindex < rt->raw_size; -} - -_public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - if (rt->rindex == rt->raw_size) /* EOF */ - return -ESPIPE; - - if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ - return -EBADMSG; - - *ret = NDISC_ROUTER_OPTION_TYPE(rt); - return 0; -} - -_public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { - uint8_t k; - int r; - - assert_return(rt, -EINVAL); - - r = sd_ndisc_router_option_get_type(rt, &k); - if (r < 0) - return r; - - return type == k; -} - -_public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { - size_t length; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(size, -EINVAL); - - /* Note that this returns the full option, including the option header */ - - if (rt->rindex + 2 > rt->raw_size) - return -EBADMSG; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (rt->rindex + length > rt->raw_size) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - *size = length; - - return 0; -} - -static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) { - struct nd_opt_prefix_info *ri; - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length != sizeof(struct nd_opt_prefix_info)) - return -EBADMSG; - - ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); - if (ri->nd_opt_pi_prefix_len > 128) - return -EBADMSG; - - *ret = ri; - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) { - struct nd_opt_prefix_info *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &ri); - if (r < 0) - return r; - - *ret = be32toh(ri->nd_opt_pi_valid_time); - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret = be32toh(pi->nd_opt_pi_preferred_time); - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret = pi->nd_opt_pi_flags_reserved; - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - *ret_addr = pi->nd_opt_pi_prefix; - return 0; -} - -_public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - struct nd_opt_prefix_info *pi; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_prefix_info(rt, &pi); - if (r < 0) - return r; - - if (pi->nd_opt_pi_prefix_len > 128) - return -EBADMSG; - - *ret = pi->nd_opt_pi_prefix_len; - return 0; -} - -static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) { - uint8_t *ri; - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 1*8 || length > 3*8) - return -EBADMSG; - - ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - - if (ri[2] > 128) - return -EBADMSG; - - *ret = ri; - return 0; -} - -_public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - *ret = be32toh(*(uint32_t*) (ri + 4)); - return 0; -} - -_public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_addr, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - zero(*ret_addr); - memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8); - - return 0; -} - -_public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - *ret = ri[2]; - return 0; -} - -_public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_route_info(rt, &ri); - if (r < 0) - return r; - - *ret = (ri[3] >> 3) & 3; - if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) - *ret = SD_NDISC_PREFERENCE_MEDIUM; - - return 0; -} - -static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) { - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 3*8 || (length % (2*8)) != 1*8) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; -} - -_public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_rdnss_info(rt, &ri); - if (r < 0) - return r; - - *ret = (const struct in6_addr*) (ri + 8); - return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16; -} - -_public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_rdnss_info(rt, &ri); - if (r < 0) - return r; - - *ret = be32toh(*(uint32_t*) (ri + 4)); - return 0; -} - -static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) { - size_t length; - int r; - - assert(rt); - assert(ret); - - r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL); - if (r < 0) - return r; - if (r == 0) - return -EMEDIUMTYPE; - - length = NDISC_ROUTER_OPTION_LENGTH(rt); - if (length < 2*8) - return -EBADMSG; - - *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; - return 0; -} - -_public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ char *e = NULL; - size_t allocated = 0, n = 0, left; - uint8_t *ri, *p; - bool first = true; - int r; - unsigned k = 0; - - assert_return(rt, -EINVAL); - assert_return(ret, -EINVAL); - - r = get_dnssl_info(rt, &ri); - if (r < 0) - return r; - - p = ri + 8; - left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8; - - for (;;) { - if (left == 0) { - - if (n > 0) /* Not properly NUL terminated */ - return -EBADMSG; - - break; - } - - if (*p == 0) { - /* Found NUL termination */ - - if (n > 0) { - _cleanup_free_ char *normalized = NULL; - - e[n] = 0; - r = dns_name_normalize(e, &normalized); - if (r < 0) - return r; - - /* Ignore the root domain name or "localhost" and friends */ - if (!is_localhost(normalized) && - !dns_name_is_root(normalized)) { - - if (strv_push(&l, normalized) < 0) - return -ENOMEM; - - normalized = NULL; - k++; - } - } - - n = 0; - first = true; - p++, left--; - continue; - } - - /* Check for compression (which is not allowed) */ - if (*p > 63) - return -EBADMSG; - - if (1U + *p + 1U > left) - return -EBADMSG; - - if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U)) - return -ENOMEM; - - if (first) - first = false; - else - e[n++] = '.'; - - r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - n += r; - - left -= 1 + *p; - p += 1 + *p; - } - - if (strv_isempty(l)) { - *ret = NULL; - return 0; - } - - *ret = l; - l = NULL; - - return k; -} - -_public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) { - uint8_t *ri; - int r; - - assert_return(rt, -EINVAL); - assert_return(ret_sec, -EINVAL); - - r = get_dnssl_info(rt, &ri); - if (r < 0) - return r; - - *ret_sec = be32toh(*(uint32_t*) (ri + 4)); - return 0; -} diff --git a/src/libsystemd-network/ndisc-router.h b/src/libsystemd-network/ndisc-router.h deleted file mode 100644 index 1fe703da63..0000000000 --- a/src/libsystemd-network/ndisc-router.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-ndisc.h" - -#include "time-util.h" - -struct sd_ndisc_router { - unsigned n_ref; - - triple_timestamp timestamp; - struct in6_addr address; - - /* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */ - size_t raw_size; - - /* The current read index for the iterative option interface */ - size_t rindex; - - uint64_t flags; - unsigned preference; - uint16_t lifetime; - - uint8_t hop_limit; - uint32_t mtu; -}; - -static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) { - return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router)); -} - -static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex; -} - -static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0]; -} -static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) { - return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8; -} - -sd_ndisc_router *ndisc_router_new(size_t raw_size); -int ndisc_router_parse(sd_ndisc_router *rt); diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c deleted file mode 100644 index 9d78b953fc..0000000000 --- a/src/libsystemd-network/network-internal.c +++ /dev/null @@ -1,556 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include - -#include "sd-ndisc.h" - -#include "alloc-util.h" -#include "condition.h" -#include "conf-parser.h" -#include "dhcp-lease-internal.h" -#include "ether-addr-util.h" -#include "hexdecoct.h" -#include "log.h" -#include "network-internal.h" -#include "parse-util.h" -#include "siphash24.h" -#include "socket-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" -#include "util.h" - -const char *net_get_name(struct udev_device *device) { - const char *name, *field; - - assert(device); - - /* fetch some persistent data unique (on this machine) to this device */ - FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { - name = udev_device_get_property_value(device, field); - if (name) - return name; - } - - return NULL; -} - -#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, uint64_t *result) { - size_t l, sz = 0; - const char *name = NULL; - int r; - uint8_t *v; - - assert(device); - - name = net_get_name(device); - if (!name) - return -ENOENT; - - l = strlen(name); - sz = sizeof(sd_id128_t) + l; - v = alloca(sz); - - /* fetch some persistent data unique to this machine */ - r = sd_id128_get_machine((sd_id128_t*) v); - if (r < 0) - return r; - memcpy(v + sizeof(sd_id128_t), name, l); - - /* Let's hash the machine ID plus the device name. We - * use a fixed, but originally randomly created hash - * key here. */ - *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); - - return 0; -} - -bool net_match_config(const struct ether_addr *match_mac, - char * const *match_paths, - char * const *match_drivers, - char * const *match_types, - char * const *match_names, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel, - Condition *match_arch, - const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_parent_driver, - const char *dev_driver, - const char *dev_type, - const char *dev_name) { - - if (match_host && condition_test(match_host) <= 0) - return false; - - if (match_virt && condition_test(match_virt) <= 0) - return false; - - if (match_kernel && condition_test(match_kernel) <= 0) - return false; - - if (match_arch && condition_test(match_arch) <= 0) - return false; - - if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) - return false; - - if (!strv_isempty(match_paths) && - (!dev_path || !strv_fnmatch(match_paths, dev_path, 0))) - return false; - - if (!strv_isempty(match_drivers) && - (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0))) - return false; - - if (!strv_isempty(match_types) && - (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0))) - return false; - - if (!strv_isempty(match_names) && - (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0))) - return false; - - return true; -} - -int config_parse_net_condition(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) { - - ConditionType cond = ltype; - Condition **ret = data; - bool negate; - Condition *c; - _cleanup_free_ char *s = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - negate = rvalue[0] == '!'; - if (negate) - rvalue++; - - s = strdup(rvalue); - if (!s) - return log_oom(); - - c = condition_new(cond, s, false, negate); - if (!c) - return log_oom(); - - if (*ret) - condition_free(*ret); - - *ret = c; - return 0; -} - -int config_parse_ifnames( - 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) { - - char ***sv = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); - return 0; - } - if (r == 0) - break; - - if (!ifname_valid(word)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); - return 0; - } - - r = strv_push(sv, word); - if (r < 0) - return log_oom(); - - word = NULL; - } - - return 0; -} - -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) { - - char **s = data; - _cleanup_free_ char *n = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - n = strdup(rvalue); - if (!n) - return log_oom(); - - if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - return 0; - } - - free(*s); - if (*n) { - *s = n; - n = NULL; - } else - *s = NULL; - - return 0; -} - -int config_parse_hwaddr(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - struct ether_addr **hwaddr = data; - struct ether_addr *n; - const char *start; - size_t offset; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - n = new0(struct ether_addr, 1); - if (!n) - return log_oom(); - - start = rvalue + strspn(rvalue, WHITESPACE); - r = ether_addr_from_string(start, n, &offset); - - if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); - free(n); - return 0; - } - - free(*hwaddr); - *hwaddr = n; - - return 0; -} - -int config_parse_iaid(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) { - uint32_t iaid; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &iaid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read IAID, ignoring assignment: %s", rvalue); - return 0; - } - - *((uint32_t *)data) = iaid; - - return 0; -} - -void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { - unsigned i; - - assert(f); - assert(addresses); - assert(size); - - for (i = 0; i < size; i++) - fprintf(f, "%s%s", inet_ntoa(addresses[i]), - (i < (size - 1)) ? " ": ""); -} - -int deserialize_in_addrs(struct in_addr **ret, const char *string) { - _cleanup_free_ struct in_addr *addresses = NULL; - int size = 0; - - assert(ret); - assert(string); - - for (;;) { - _cleanup_free_ char *word = NULL; - struct in_addr *new_addresses; - int r; - - r = extract_first_word(&string, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); - if (!new_addresses) - return -ENOMEM; - else - addresses = new_addresses; - - r = inet_pton(AF_INET, word, &(addresses[size])); - if (r <= 0) - continue; - - size++; - } - - *ret = addresses; - addresses = NULL; - - return size; -} - -void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) { - unsigned i; - - assert(f); - assert(addresses); - assert(size); - - for (i = 0; i < size; i++) { - char buffer[INET6_ADDRSTRLEN]; - - fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); - - if (i < size - 1) - fputc(' ', f); - } -} - -int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { - _cleanup_free_ struct in6_addr *addresses = NULL; - int size = 0; - - assert(ret); - assert(string); - - for (;;) { - _cleanup_free_ char *word = NULL; - struct in6_addr *new_addresses; - int r; - - r = extract_first_word(&string, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); - if (!new_addresses) - return -ENOMEM; - else - addresses = new_addresses; - - r = inet_pton(AF_INET6, word, &(addresses[size])); - if (r <= 0) - continue; - - size++; - } - - *ret = addresses; - addresses = NULL; - - return size; -} - -void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { - unsigned i; - - assert(f); - assert(key); - assert(routes); - assert(size); - - fprintf(f, "%s=", key); - - for (i = 0; i < size; i++) { - 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); -} - -int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { - _cleanup_free_ struct sd_dhcp_route *routes = NULL; - size_t size = 0, allocated = 0; - - assert(ret); - assert(ret_size); - assert(ret_allocated); - assert(string); - - /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ - for (;;) { - _cleanup_free_ char *word = NULL; - char *tok, *tok_end; - unsigned n; - int r; - - r = extract_first_word(&string, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(routes, allocated, size + 1)) - return -ENOMEM; - - tok = word; - - /* get the subnet */ - tok_end = strchr(tok, '/'); - if (!tok_end) - continue; - *tok_end = '\0'; - - r = inet_aton(tok, &routes[size].dst_addr); - if (r == 0) - continue; - - tok = tok_end + 1; - - /* get the prefixlen */ - tok_end = strchr(tok, ','); - if (!tok_end) - continue; - - *tok_end = '\0'; - - r = safe_atou(tok, &n); - if (r < 0 || n > 32) - continue; - - routes[size].dst_prefixlen = (uint8_t) n; - tok = tok_end + 1; - - /* get the gateway */ - r = inet_aton(tok, &routes[size].gw_addr); - if (r == 0) - continue; - - size++; - } - - *ret_size = size; - *ret_allocated = allocated; - *ret = routes; - routes = NULL; - - return 0; -} - -int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) { - _cleanup_free_ char *hex_buf = NULL; - - assert(f); - assert(key); - assert(data); - - hex_buf = hexmem(data, size); - if (hex_buf == NULL) - return -ENOMEM; - - fprintf(f, "%s=%s\n", key, hex_buf); - - return 0; -} - -int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) { - assert(data); - assert(data_len); - assert(string); - - if (strlen(string) % 2) - return -EINVAL; - - return unhexmem(string, strlen(string), (void **)data, data_len); -} diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h deleted file mode 100644 index 5bcd577167..0000000000 --- a/src/libsystemd-network/network-internal.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include - -#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, - char * const *match_driver, - char * const *match_type, - char * const *match_name, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel, - Condition *match_arch, - const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_parent_driver, - const char *dev_driver, - const char *dev_type, - const char *dev_name); - -int config_parse_net_condition(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_hwaddr(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_ifnames(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_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 config_parse_iaid(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, 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); -int deserialize_in_addrs(struct in_addr **addresses, const char *string); -void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, - size_t size); -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, 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); -int deserialize_dhcp_option(void **data, size_t *data_len, const char *string); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c deleted file mode 100644 index 179e5950bd..0000000000 --- a/src/libsystemd-network/sd-dhcp-client.c +++ /dev/null @@ -1,1906 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-dhcp-client.h" - -#include "alloc-util.h" -#include "async.h" -#include "dhcp-identifier.h" -#include "dhcp-internal.h" -#include "dhcp-lease-internal.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) - -#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) -#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) - -struct sd_dhcp_client { - unsigned n_ref; - - DHCPState state; - sd_event *event; - int event_priority; - sd_event_source *timeout_resend; - int ifindex; - int fd; - union sockaddr_union link; - sd_event_source *receive_message; - bool request_broadcast; - uint8_t *req_opts; - size_t req_opts_allocated; - size_t req_opts_size; - be32_t last_addr; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - struct { - uint8_t type; - union { - struct { - /* 0: Generic (non-LL) (RFC 2132) */ - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ gen; - struct { - /* 1: Ethernet Link-Layer (RFC 2132) */ - uint8_t haddr[ETH_ALEN]; - } _packed_ eth; - struct { - /* 2 - 254: ARP/Link-Layer (RFC 2132) */ - uint8_t haddr[0]; - } _packed_ ll; - struct { - /* 255: Node-specific (RFC 4361) */ - be32_t iaid; - struct duid duid; - } _packed_ ns; - struct { - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ raw; - }; - } _packed_ client_id; - size_t client_id_len; - char *hostname; - char *vendor_class_identifier; - uint32_t mtu; - uint32_t xid; - usec_t start_time; - unsigned int attempt; - usec_t request_sent; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - sd_event_source *timeout_expire; - sd_dhcp_client_callback_t callback; - void *userdata; - sd_dhcp_lease *lease; - usec_t start_delay; -}; - -static const uint8_t default_req_opts[] = { - 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, - uint32_t revents, - void *userdata); -static int client_receive_message_udp( - sd_event_source *s, - int fd, - uint32_t revents, - void *userdata); -static void client_stop(sd_dhcp_client *client, int error); - -int sd_dhcp_client_set_callback( - sd_dhcp_client *client, - sd_dhcp_client_callback_t cb, - void *userdata) { - - assert_return(client, -EINVAL); - - client->callback = cb; - client->userdata = userdata; - - return 0; -} - -int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) { - assert_return(client, -EINVAL); - - client->request_broadcast = !!broadcast; - - return 0; -} - -int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { - size_t i; - - assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); - - switch(option) { - - 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: - break; - } - - for (i = 0; i < client->req_opts_size; i++) - if (client->req_opts[i] == option) - return -EEXIST; - - if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, - client->req_opts_size + 1)) - return -ENOMEM; - - client->req_opts[client->req_opts_size++] = option; - - return 0; -} - -int sd_dhcp_client_set_request_address( - sd_dhcp_client *client, - const struct in_addr *last_addr) { - - assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); - - if (last_addr) - client->last_addr = last_addr->s_addr; - else - client->last_addr = INADDR_ANY; - - return 0; -} - -int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) { - - assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); - assert_return(ifindex > 0, -EINVAL); - - client->ifindex = ifindex; - return 0; -} - -int sd_dhcp_client_set_mac( - sd_dhcp_client *client, - const uint8_t *addr, - size_t addr_len, - uint16_t arp_type) { - - DHCP_CLIENT_DONT_DESTROY(client); - bool need_restart = false; - - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - assert_return(arp_type > 0, -EINVAL); - - if (arp_type == ARPHRD_ETHER) - assert_return(addr_len == ETH_ALEN, -EINVAL); - else if (arp_type == ARPHRD_INFINIBAND) - assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); - else - return -EINVAL; - - if (client->mac_addr_len == addr_len && - memcmp(&client->mac_addr, addr, addr_len) == 0) - return 0; - - if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting"); - need_restart = true; - client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); - } - - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; - client->arp_type = arp_type; - - if (need_restart && client->state != DHCP_STATE_STOPPED) - sd_dhcp_client_start(client); - - return 0; -} - -int sd_dhcp_client_get_client_id( - sd_dhcp_client *client, - uint8_t *type, - const uint8_t **data, - size_t *data_len) { - - assert_return(client, -EINVAL); - assert_return(type, -EINVAL); - assert_return(data, -EINVAL); - assert_return(data_len, -EINVAL); - - *type = 0; - *data = NULL; - *data_len = 0; - if (client->client_id_len) { - *type = client->client_id.type; - *data = client->client_id.raw.data; - *data_len = client->client_id_len - sizeof(client->client_id.type); - } - - return 0; -} - -int sd_dhcp_client_set_client_id( - sd_dhcp_client *client, - uint8_t type, - const uint8_t *data, - size_t data_len) { - - DHCP_CLIENT_DONT_DESTROY(client); - bool need_restart = false; - - assert_return(client, -EINVAL); - assert_return(data, -EINVAL); - assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL); - - switch (type) { - - case ARPHRD_ETHER: - if (data_len != ETH_ALEN) - return -EINVAL; - break; - - case ARPHRD_INFINIBAND: - if (data_len != INFINIBAND_ALEN) - return -EINVAL; - break; - - default: - break; - } - - if (client->client_id_len == data_len + sizeof(client->client_id.type) && - client->client_id.type == type && - memcmp(&client->client_id.raw.data, data, data_len) == 0) - return 0; - - if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Changing client ID on running DHCP " - "client, restarting"); - need_restart = true; - client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); - } - - client->client_id.type = type; - memcpy(&client->client_id.raw.data, data, data_len); - client->client_id_len = data_len + sizeof (client->client_id.type); - - if (need_restart && client->state != DHCP_STATE_STOPPED) - sd_dhcp_client_start(client); - - return 0; -} - -/** - * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid - * without further modification. Otherwise, if duid_type is supported, DUID - * is set based on that type. Otherwise, an error is returned. - */ -int sd_dhcp_client_set_iaid_duid( - sd_dhcp_client *client, - uint32_t iaid, - uint16_t duid_type, - const void *duid, - size_t duid_len) { - - DHCP_CLIENT_DONT_DESTROY(client); - int r; - size_t len; - - assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - - if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); - if (r < 0) - return r; - } - - zero(client->client_id); - client->client_id.type = 255; - - /* If IAID is not configured, generate it. */ - if (iaid == 0) { - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, - client->mac_addr_len, - &client->client_id.ns.iaid); - if (r < 0) - return r; - } else - client->client_id.ns.iaid = htobe32(iaid); - - if (duid != NULL) { - client->client_id.ns.duid.type = htobe16(duid_type); - memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); - len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else if (duid_type == DUID_TYPE_EN) { - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return r; - } else - return -EOPNOTSUPP; - - client->client_id_len = sizeof(client->client_id.type) + len + - sizeof(client->client_id.ns.iaid); - - if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured IAID+DUID, restarting."); - client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); - sd_dhcp_client_start(client); - } - - return 0; -} - -int sd_dhcp_client_set_hostname( - sd_dhcp_client *client, - const char *hostname) { - - char *new_hostname = NULL; - - 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; - - if (hostname) { - new_hostname = strdup(hostname); - if (!new_hostname) - return -ENOMEM; - } - - free(client->hostname); - client->hostname = new_hostname; - - return 0; -} - -int sd_dhcp_client_set_vendor_class_identifier( - sd_dhcp_client *client, - const char *vci) { - - char *new_vci = NULL; - - assert_return(client, -EINVAL); - - new_vci = strdup(vci); - if (!new_vci) - return -ENOMEM; - - free(client->vendor_class_identifier); - - client->vendor_class_identifier = new_vci; - - return 0; -} - -int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { - assert_return(client, -EINVAL); - assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE); - - client->mtu = mtu; - - return 0; -} - -int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { - assert_return(client, -EINVAL); - - if (client->state != DHCP_STATE_BOUND && - client->state != DHCP_STATE_RENEWING && - client->state != DHCP_STATE_REBINDING) - return -EADDRNOTAVAIL; - - if (ret) - *ret = client->lease; - - return 0; -} - -static void client_notify(sd_dhcp_client *client, int event) { - assert(client); - - if (client->callback) - client->callback(client, event, client->userdata); -} - -static int client_initialize(sd_dhcp_client *client) { - assert_return(client, -EINVAL); - - client->receive_message = sd_event_source_unref(client->receive_message); - - client->fd = asynchronous_close(client->fd); - - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); - - client->attempt = 1; - - client->state = DHCP_STATE_INIT; - client->xid = 0; - - client->lease = sd_dhcp_lease_unref(client->lease); - - return 0; -} - -static void client_stop(sd_dhcp_client *client, int error) { - assert(client); - - if (error < 0) - log_dhcp_client(client, "STOPPED: %s", strerror(-error)); - else if (error == SD_DHCP_CLIENT_EVENT_STOP) - log_dhcp_client(client, "STOPPED"); - else - log_dhcp_client(client, "STOPPED: Unknown event"); - - client_notify(client, error); - - client_initialize(client); -} - -static int client_message_init( - sd_dhcp_client *client, - DHCPPacket **ret, - uint8_t type, - size_t *_optlen, - size_t *_optoffset) { - - _cleanup_free_ DHCPPacket *packet = NULL; - size_t optlen, optoffset, size; - be16_t max_size; - usec_t time_now; - uint16_t secs; - int r; - - assert(client); - assert(client->start_time); - assert(ret); - assert(_optlen); - assert(_optoffset); - assert(type == DHCP_DISCOVER || type == DHCP_REQUEST); - - optlen = DHCP_MIN_OPTIONS_SIZE; - size = sizeof(DHCPPacket) + optlen; - - packet = malloc0(size); - if (!packet) - return -ENOMEM; - - r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, - client->arp_type, optlen, &optoffset); - if (r < 0) - return r; - - /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers - refuse to issue an DHCP lease if 'secs' is set to zero */ - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - assert(time_now >= client->start_time); - - /* seconds between sending first and last DISCOVER - * must always be strictly positive to deal with broken servers */ - secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; - packet->dhcp.secs = htobe16(secs); - - /* RFC2132 section 4.1 - A client that cannot receive unicast IP datagrams until its protocol - software has been configured with an IP address SHOULD set the - BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or - DHCPREQUEST messages that client sends. The BROADCAST bit will - provide a hint to the DHCP server and BOOTP relay agent to broadcast - any messages to the client on the client's subnet. - - Note: some interfaces needs this to be enabled, but some networks - needs this to be disabled as broadcasts are filteretd, so this - needs to be configurable */ - if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) - packet->dhcp.flags = htobe16(0x8000); - - /* RFC2132 section 4.1.1: - The client MUST include its hardware address in the ’chaddr’ field, if - necessary for delivery of DHCP reply messages. Non-Ethernet - interfaces will leave 'chaddr' empty and use the client identifier - instead (eg, RFC 4390 section 2.1). - */ - if (client->arp_type == ARPHRD_ETHER) - memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); - - /* If no client identifier exists, construct an RFC 4361-compliant one */ - if (client->client_id_len == 0) { - size_t duid_len; - - client->client_id.type = 255; - - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid); - if (r < 0) - return r; - - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); - if (r < 0) - return r; - - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len; - } - - /* Some DHCP servers will refuse to issue an DHCP lease if the Client - Identifier option is not set */ - if (client->client_id_len) { - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_CLIENT_IDENTIFIER, - client->client_id_len, - &client->client_id); - if (r < 0) - return r; - } - - /* RFC2131 section 3.5: - in its initial DHCPDISCOVER or DHCPREQUEST message, a - client may provide the server with a list of specific - parameters the client is interested in. If the client - includes a list of parameters in a DHCPDISCOVER message, - it MUST include that list in any subsequent DHCPREQUEST - messages. - */ - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, - client->req_opts_size, client->req_opts); - if (r < 0) - return r; - - /* RFC2131 section 3.5: - The client SHOULD include the ’maximum DHCP message size’ option to - let the server know how large the server may make its DHCP messages. - - Note (from ConnMan): Some DHCP servers will send bigger DHCP packets - than the defined default size unless the Maximum Messge Size option - is explicitly set - - RFC3442 "Requirements to Avoid Sizing Constraints": - Because a full routing table can be quite large, the standard 576 - octet maximum size for a DHCP message may be too short to contain - some legitimate Classless Static Route options. Because of this, - clients implementing the Classless Static Route option SHOULD send a - Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP - stack is capable of receiving larger IP datagrams. In this case, the - client SHOULD set the value of this option to at least the MTU of the - interface that the client is configuring. The client MAY set the - value of this option higher, up to the size of the largest UDP packet - it is prepared to accept. (Note that the value specified in the - Maximum DHCP Message Size option is the total maximum packet size, - including IP and UDP headers.) - */ - max_size = htobe16(size); - r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, - SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, - 2, &max_size); - if (r < 0) - return r; - - *_optlen = optlen; - *_optoffset = optoffset; - *ret = packet; - packet = NULL; - - 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, - INADDR_BROADCAST, DHCP_PORT_SERVER, len); - - return dhcp_network_send_raw_socket(client->fd, &client->link, - packet, len); -} - -static int client_send_discover(sd_dhcp_client *client) { - _cleanup_free_ DHCPPacket *discover = NULL; - size_t optoffset, optlen; - int r; - - assert(client); - assert(client->state == DHCP_STATE_INIT || - client->state == DHCP_STATE_SELECTING); - - r = client_message_init(client, &discover, DHCP_DISCOVER, - &optlen, &optoffset); - if (r < 0) - return r; - - /* the client may suggest values for the network address - and lease time in the DHCPDISCOVER message. The client may include - the ’requested IP address’ option to suggest that a particular IP - address be assigned, and may include the ’IP address lease time’ - option to suggest the lease time it would like. - */ - if (client->last_addr != INADDR_ANY) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, - 4, &client->last_addr); - if (r < 0) - return r; - } - - if (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, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, - strlen(client->vendor_class_identifier), - client->vendor_class_identifier); - if (r < 0) - return r; - } - - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - /* We currently ignore: - The client SHOULD wait a random time between one and ten seconds to - desynchronize the use of DHCP at startup. - */ - r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset); - if (r < 0) - return r; - - log_dhcp_client(client, "DISCOVER"); - - return 0; -} - -static int client_send_request(sd_dhcp_client *client) { - _cleanup_free_ DHCPPacket *request = NULL; - size_t optoffset, optlen; - int r; - - assert(client); - - r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset); - if (r < 0) - return r; - - switch (client->state) { - /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC, - SELECTING should be REQUESTING) - */ - - case DHCP_STATE_REQUESTING: - /* Client inserts the address of the selected server in ’server - identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be - filled in with the yiaddr value from the chosen DHCPOFFER. - */ - - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_SERVER_IDENTIFIER, - 4, &client->lease->server_address); - if (r < 0) - return r; - - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, - 4, &client->lease->address); - if (r < 0) - return r; - - break; - - case DHCP_STATE_INIT_REBOOT: - /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ - option MUST be filled in with client’s notion of its previously - assigned address. ’ciaddr’ MUST be zero. - */ - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, - 4, &client->last_addr); - if (r < 0) - return r; - break; - - case DHCP_STATE_RENEWING: - /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ - option MUST NOT be filled in, ’ciaddr’ MUST be filled in with - client’s IP address. - */ - - /* fall through */ - case DHCP_STATE_REBINDING: - /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ - option MUST NOT be filled in, ’ciaddr’ MUST be filled in with - client’s IP address. - - This message MUST be broadcast to the 0xffffffff IP broadcast address. - */ - request->dhcp.ciaddr = client->lease->address; - - break; - - case DHCP_STATE_INIT: - case DHCP_STATE_SELECTING: - case DHCP_STATE_REBOOTING: - case DHCP_STATE_BOUND: - case DHCP_STATE_STOPPED: - return -EINVAL; - } - - if (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, - SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - if (client->state == DHCP_STATE_RENEWING) { - r = dhcp_network_send_udp_socket(client->fd, - client->lease->server_address, - DHCP_PORT_SERVER, - &request->dhcp, - sizeof(DHCPMessage) + optoffset); - } else { - r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset); - } - if (r < 0) - return r; - - switch (client->state) { - - case DHCP_STATE_REQUESTING: - log_dhcp_client(client, "REQUEST (requesting)"); - break; - - case DHCP_STATE_INIT_REBOOT: - log_dhcp_client(client, "REQUEST (init-reboot)"); - break; - - case DHCP_STATE_RENEWING: - log_dhcp_client(client, "REQUEST (renewing)"); - break; - - case DHCP_STATE_REBINDING: - log_dhcp_client(client, "REQUEST (rebinding)"); - break; - - default: - log_dhcp_client(client, "REQUEST (invalid)"); - break; - } - - return 0; -} - -static int client_start(sd_dhcp_client *client); - -static int client_timeout_resend( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - sd_dhcp_client *client = userdata; - DHCP_CLIENT_DONT_DESTROY(client); - usec_t next_timeout = 0; - uint64_t time_now; - uint32_t time_left; - int r; - - assert(s); - assert(client); - assert(client->event); - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - switch (client->state) { - - case DHCP_STATE_RENEWING: - - time_left = (client->lease->t2 - client->lease->t1) / 2; - if (time_left < 60) - time_left = 60; - - next_timeout = time_now + time_left * USEC_PER_SEC; - - break; - - case DHCP_STATE_REBINDING: - - time_left = (client->lease->lifetime - client->lease->t2) / 2; - if (time_left < 60) - time_left = 60; - - next_timeout = time_now + time_left * USEC_PER_SEC; - break; - - case DHCP_STATE_REBOOTING: - /* start over as we did not receive a timely ack or nak */ - r = client_initialize(client); - if (r < 0) - goto error; - - r = client_start(client); - if (r < 0) - goto error; - else { - log_dhcp_client(client, "REBOOTED"); - return 0; - } - - case DHCP_STATE_INIT: - case DHCP_STATE_INIT_REBOOT: - case DHCP_STATE_SELECTING: - case DHCP_STATE_REQUESTING: - case DHCP_STATE_BOUND: - - if (client->attempt < 64) - client->attempt *= 2; - - next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC; - - break; - - case DHCP_STATE_STOPPED: - r = -EINVAL; - goto error; - } - - next_timeout += (random_u32() & 0x1fffff); - - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - next_timeout, 10 * USEC_PER_MSEC, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); - if (r < 0) - goto error; - - switch (client->state) { - case DHCP_STATE_INIT: - r = client_send_discover(client); - if (r >= 0) { - client->state = DHCP_STATE_SELECTING; - client->attempt = 1; - } else { - if (client->attempt >= 64) - goto error; - } - - break; - - case DHCP_STATE_SELECTING: - r = client_send_discover(client); - if (r < 0 && client->attempt >= 64) - goto error; - - break; - - case DHCP_STATE_INIT_REBOOT: - case DHCP_STATE_REQUESTING: - case DHCP_STATE_RENEWING: - case DHCP_STATE_REBINDING: - r = client_send_request(client); - if (r < 0 && client->attempt >= 64) - goto error; - - if (client->state == DHCP_STATE_INIT_REBOOT) - client->state = DHCP_STATE_REBOOTING; - - client->request_sent = time_now; - - break; - - case DHCP_STATE_REBOOTING: - case DHCP_STATE_BOUND: - - break; - - case DHCP_STATE_STOPPED: - r = -EINVAL; - goto error; - } - - return 0; - -error: - client_stop(client, r); - - /* Errors were dealt with when stopping the client, don't spill - errors into the event loop handler */ - return 0; -} - -static int client_initialize_io_events( - sd_dhcp_client *client, - sd_event_io_handler_t io_callback) { - - int r; - - assert(client); - assert(client->event); - - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, io_callback, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message"); - if (r < 0) - goto error; - -error: - if (r < 0) - client_stop(client, r); - - return 0; -} - -static int client_initialize_time_events(sd_dhcp_client *client) { - uint64_t usec = 0; - int r; - - assert(client); - assert(client->event); - - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - if (client->start_delay) { - assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); - usec += client->start_delay; - } - - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - usec, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); - if (r < 0) - goto error; - -error: - if (r < 0) - client_stop(client, r); - - return 0; - -} - -static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) { - client_initialize_io_events(client, io_callback); - client_initialize_time_events(client); - - return 0; -} - -static int client_start_delayed(sd_dhcp_client *client) { - int r; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->fd < 0, -EBUSY); - assert_return(client->xid == 0, -EINVAL); - assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY); - - client->xid = random_u32(); - - r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, - client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type); - if (r < 0) { - client_stop(client, r); - return r; - } - client->fd = r; - - if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT) - client->start_time = now(clock_boottime_or_monotonic()); - - return client_initialize_events(client, client_receive_message_raw); -} - -static int client_start(sd_dhcp_client *client) { - client->start_delay = 0; - return client_start_delayed(client); -} - -static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp_client *client = userdata; - DHCP_CLIENT_DONT_DESTROY(client); - - log_dhcp_client(client, "EXPIRED"); - - client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); - - /* lease was lost, start over if not freed or stopped in callback */ - if (client->state != DHCP_STATE_STOPPED) { - client_initialize(client); - client_start(client); - } - - return 0; -} - -static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp_client *client = userdata; - DHCP_CLIENT_DONT_DESTROY(client); - int r; - - assert(client); - - client->receive_message = sd_event_source_unref(client->receive_message); - client->fd = asynchronous_close(client->fd); - - client->state = DHCP_STATE_REBINDING; - client->attempt = 1; - - r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, - client->xid, client->mac_addr, - client->mac_addr_len, client->arp_type); - if (r < 0) { - client_stop(client, r); - return 0; - } - client->fd = r; - - return client_initialize_events(client, client_receive_message_raw); -} - -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp_client *client = userdata; - DHCP_CLIENT_DONT_DESTROY(client); - - client->state = DHCP_STATE_RENEWING; - client->attempt = 1; - - return client_initialize_time_events(client); -} - -static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) { - _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; - int r; - - r = dhcp_lease_new(&lease); - if (r < 0) - return r; - - if (client->client_id_len) { - r = dhcp_lease_set_client_id(lease, - (uint8_t *) &client->client_id, - client->client_id_len); - if (r < 0) - return r; - } - - 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; - } - - lease->next_server = offer->siaddr; - lease->address = offer->yiaddr; - - if (lease->address == 0 || - lease->server_address == 0 || - lease->lifetime == 0) { - log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring"); - return -ENOMSG; - } - - if (!lease->have_subnet_mask) { - r = dhcp_lease_set_default_subnet_mask(lease); - if (r < 0) { - log_dhcp_client(client, "received lease lacks subnet " - "mask, and a fallback one can not be " - "generated, ignoring"); - return -ENOMSG; - } - } - - sd_dhcp_lease_unref(client->lease); - client->lease = lease; - lease = NULL; - - log_dhcp_client(client, "OFFER"); - - return 0; -} - -static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) { - int r; - - r = dhcp_option_parse(force, len, NULL, NULL, NULL); - if (r != DHCP_FORCERENEW) - return -ENOMSG; - - log_dhcp_client(client, "FORCERENEW"); - - return 0; -} - -static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { - _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; - _cleanup_free_ char *error_message = NULL; - int r; - - r = dhcp_lease_new(&lease); - if (r < 0) - return r; - - if (client->client_id_len) { - r = dhcp_lease_set_client_id(lease, - (uint8_t *) &client->client_id, - client->client_id_len); - if (r < 0) - return r; - } - - r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message); - if (r == DHCP_NAK) { - log_dhcp_client(client, "NAK: %s", strna(error_message)); - return -EADDRNOTAVAIL; - } - - if (r != DHCP_ACK) { - log_dhcp_client(client, "received message was not an ACK, ignoring"); - return -ENOMSG; - } - - lease->next_server = ack->siaddr; - - lease->address = ack->yiaddr; - - if (lease->address == INADDR_ANY || - lease->server_address == INADDR_ANY || - lease->lifetime == 0) { - log_dhcp_client(client, "received lease lacks address, server " - "address or lease lifetime, ignoring"); - return -ENOMSG; - } - - if (lease->subnet_mask == INADDR_ANY) { - r = dhcp_lease_set_default_subnet_mask(lease); - if (r < 0) { - log_dhcp_client(client, "received lease lacks subnet " - "mask, and a fallback one can not be " - "generated, ignoring"); - return -ENOMSG; - } - } - - r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; - if (client->lease) { - if (client->lease->address != lease->address || - client->lease->subnet_mask != lease->subnet_mask || - client->lease->router != lease->router) { - r = SD_DHCP_CLIENT_EVENT_IP_CHANGE; - } else - r = SD_DHCP_CLIENT_EVENT_RENEW; - - client->lease = sd_dhcp_lease_unref(client->lease); - } - - client->lease = lease; - lease = NULL; - - log_dhcp_client(client, "ACK"); - - return r; -} - -static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) { - assert(client); - assert(client->request_sent); - assert(lifetime > 0); - - if (lifetime > 3) - lifetime -= 3; - else - lifetime = 0; - - return client->request_sent + (lifetime * USEC_PER_SEC * factor) + - + (random_u32() & 0x1fffff); -} - -static int client_set_lease_timeouts(sd_dhcp_client *client) { - usec_t time_now; - uint64_t lifetime_timeout; - uint64_t t2_timeout; - uint64_t t1_timeout; - char time_string[FORMAT_TIMESPAN_MAX]; - int r; - - assert(client); - assert(client->event); - assert(client->lease); - assert(client->lease->lifetime); - - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); - - /* don't set timers for infinite leases */ - if (client->lease->lifetime == 0xffffffff) - return 0; - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - assert(client->request_sent <= time_now); - - /* convert the various timeouts from relative (secs) to absolute (usecs) */ - lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1); - if (client->lease->t1 > 0 && client->lease->t2 > 0) { - /* both T1 and T2 are given */ - if (client->lease->t1 < client->lease->t2 && - client->lease->t2 < client->lease->lifetime) { - /* they are both valid */ - t2_timeout = client_compute_timeout(client, client->lease->t2, 1); - t1_timeout = client_compute_timeout(client, client->lease->t1, 1); - } else { - /* discard both */ - t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); - client->lease->t2 = (client->lease->lifetime * 7) / 8; - t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); - client->lease->t1 = client->lease->lifetime / 2; - } - } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) { - /* only T2 is given, and it is valid */ - t2_timeout = client_compute_timeout(client, client->lease->t2, 1); - t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); - client->lease->t1 = client->lease->lifetime / 2; - if (t2_timeout <= t1_timeout) { - /* the computed T1 would be invalid, so discard T2 */ - t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); - client->lease->t2 = (client->lease->lifetime * 7) / 8; - } - } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) { - /* only T1 is given, and it is valid */ - t1_timeout = client_compute_timeout(client, client->lease->t1, 1); - t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); - client->lease->t2 = (client->lease->lifetime * 7) / 8; - if (t2_timeout <= t1_timeout) { - /* the computed T2 would be invalid, so discard T1 */ - t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); - client->lease->t2 = client->lease->lifetime / 2; - } - } else { - /* fall back to the default timeouts */ - t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); - client->lease->t1 = client->lease->lifetime / 2; - t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); - client->lease->t2 = (client->lease->lifetime * 7) / 8; - } - - /* arm lifetime timeout */ - r = sd_event_add_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), - lifetime_timeout, 10 * USEC_PER_MSEC, - client_timeout_expire, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_expire, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); - if (r < 0) - return r; - - log_dhcp_client(client, "lease expires in %s", - 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) - return 0; - - /* arm T2 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t2, - clock_boottime_or_monotonic(), - t2_timeout, - 10 * USEC_PER_MSEC, - client_timeout_t2, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t2, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); - if (r < 0) - return r; - - log_dhcp_client(client, "T2 expires in %s", - 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) - return 0; - - /* arm T1 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t1, - clock_boottime_or_monotonic(), - t1_timeout, 10 * USEC_PER_MSEC, - client_timeout_t1, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t1, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); - if (r < 0) - return r; - - log_dhcp_client(client, "T1 expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC)); - - return 0; -} - -static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) { - DHCP_CLIENT_DONT_DESTROY(client); - char time_string[FORMAT_TIMESPAN_MAX]; - int r = 0, notify_event = 0; - - assert(client); - assert(client->event); - assert(message); - - switch (client->state) { - case DHCP_STATE_SELECTING: - - r = client_handle_offer(client, message, len); - if (r >= 0) { - - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - - client->state = DHCP_STATE_REQUESTING; - client->attempt = 1; - - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); - if (r < 0) - goto error; - } else if (r == -ENOMSG) - /* invalid message, let's ignore it */ - return 0; - - break; - - case DHCP_STATE_REBOOTING: - case DHCP_STATE_REQUESTING: - case DHCP_STATE_RENEWING: - case DHCP_STATE_REBINDING: - - r = client_handle_ack(client, message, len); - if (r >= 0) { - client->start_delay = 0; - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - client->receive_message = - sd_event_source_unref(client->receive_message); - client->fd = asynchronous_close(client->fd); - - if (IN_SET(client->state, DHCP_STATE_REQUESTING, - DHCP_STATE_REBOOTING)) - notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; - else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE) - notify_event = r; - - client->state = DHCP_STATE_BOUND; - client->attempt = 1; - - client->last_addr = client->lease->address; - - r = client_set_lease_timeouts(client); - if (r < 0) { - log_dhcp_client(client, "could not set lease timeouts"); - goto error; - } - - r = dhcp_network_bind_udp_socket(client->lease->address, - DHCP_PORT_CLIENT); - if (r < 0) { - log_dhcp_client(client, "could not bind UDP socket"); - goto error; - } - - client->fd = r; - - client_initialize_io_events(client, client_receive_message_udp); - - if (notify_event) { - client_notify(client, notify_event); - if (client->state == DHCP_STATE_STOPPED) - return 0; - } - - } else if (r == -EADDRNOTAVAIL) { - /* got a NAK, let's restart the client */ - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - - r = client_initialize(client); - if (r < 0) - goto error; - - r = client_start_delayed(client); - if (r < 0) - goto error; - - log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, - client->start_delay, USEC_PER_SEC)); - - client->start_delay = CLAMP(client->start_delay * 2, - RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC); - - return 0; - } else if (r == -ENOMSG) - /* invalid message, let's ignore it */ - return 0; - - break; - - case DHCP_STATE_BOUND: - r = client_handle_forcerenew(client, message, len); - if (r >= 0) { - r = client_timeout_t1(NULL, 0, client); - if (r < 0) - goto error; - } else if (r == -ENOMSG) - /* invalid message, let's ignore it */ - return 0; - - break; - - case DHCP_STATE_INIT: - case DHCP_STATE_INIT_REBOOT: - - break; - - case DHCP_STATE_STOPPED: - r = -EINVAL; - goto error; - } - -error: - if (r < 0) - client_stop(client, r); - - return r; -} - -static int client_receive_message_udp( - sd_event_source *s, - int fd, - uint32_t revents, - void *userdata) { - - sd_dhcp_client *client = userdata; - _cleanup_free_ DHCPMessage *message = NULL; - const struct ether_addr zero_mac = {}; - const struct ether_addr *expected_chaddr = NULL; - uint8_t expected_hlen = 0; - ssize_t len, buflen; - - assert(s); - assert(client); - - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return buflen; - - message = malloc0(buflen); - if (!message) - return -ENOMEM; - - len = recv(fd, message, buflen, 0); - if (len < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m"); - } - if ((size_t) len < sizeof(DHCPMessage)) { - 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"); - return 0; - } - - if (message->op != BOOTREPLY) { - 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"); - return 0; - } - - if (client->arp_type == ARPHRD_ETHER) { - expected_hlen = ETH_ALEN; - expected_chaddr = (const struct ether_addr *) &client->mac_addr; - } else { - /* 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); - return 0; - } - - if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { - log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); - return 0; - } - - if (client->state != DHCP_STATE_BOUND && - 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", - be32toh(message->xid), client->xid); - return 0; - } - - return client_handle_message(client, message, len); -} - -static int client_receive_message_raw( - sd_event_source *s, - int fd, - uint32_t revents, - void *userdata) { - - sd_dhcp_client *client = userdata; - _cleanup_free_ DHCPPacket *packet = NULL; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; - struct iovec iov = {}; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - struct cmsghdr *cmsg; - bool checksum = true; - ssize_t buflen, len; - int r; - - assert(s); - assert(client); - - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return buflen; - - packet = malloc0(buflen); - if (!packet) - return -ENOMEM; - - iov.iov_base = packet; - iov.iov_len = buflen; - - len = recvmsg(fd, &msg, 0); - if (len < 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; - - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_AUXDATA && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { - struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); - - checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); - break; - } - } - - r = dhcp_packet_verify_headers(packet, len, checksum); - if (r < 0) - return 0; - - len -= DHCP_IP_UDP_SIZE; - - return client_handle_message(client, &packet->dhcp, len); -} - -int sd_dhcp_client_start(sd_dhcp_client *client) { - int r; - - assert_return(client, -EINVAL); - - r = client_initialize(client); - if (r < 0) - return r; - - if (client->last_addr) - client->state = DHCP_STATE_INIT_REBOOT; - - r = client_start(client); - if (r >= 0) - log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex); - - return r; -} - -int sd_dhcp_client_stop(sd_dhcp_client *client) { - DHCP_CLIENT_DONT_DESTROY(client); - - assert_return(client, -EINVAL); - - client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); - client->state = DHCP_STATE_STOPPED; - - return 0; -} - -int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) { - int r; - - assert_return(client, -EINVAL); - assert_return(!client->event, -EBUSY); - - if (event) - client->event = sd_event_ref(event); - else { - r = sd_event_default(&client->event); - if (r < 0) - return 0; - } - - client->event_priority = priority; - - return 0; -} - -int sd_dhcp_client_detach_event(sd_dhcp_client *client) { - assert_return(client, -EINVAL); - - client->event = sd_event_unref(client->event); - - return 0; -} - -sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { - assert_return(client, NULL); - - return client->event; -} - -sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; - - if (client->n_ref > 0) - return NULL; - - log_dhcp_client(client, "FREE"); - - client_initialize(client); - - client->receive_message = sd_event_source_unref(client->receive_message); - - sd_dhcp_client_detach_event(client); - - sd_dhcp_lease_unref(client->lease); - - free(client->req_opts); - free(client->hostname); - free(client->vendor_class_identifier); - free(client); - - return NULL; -} - -int sd_dhcp_client_new(sd_dhcp_client **ret) { - _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; - - assert_return(ret, -EINVAL); - - client = new0(sd_dhcp_client, 1); - if (!client) - return -ENOMEM; - - client->n_ref = 1; - client->state = DHCP_STATE_INIT; - client->ifindex = -1; - client->fd = -1; - client->attempt = 1; - client->mtu = DHCP_DEFAULT_MIN_SIZE; - - client->req_opts_size = ELEMENTSOF(default_req_opts); - client->req_opts = memdup(default_req_opts, client->req_opts_size); - if (!client->req_opts) - return -ENOMEM; - - *ret = client; - client = NULL; - - return 0; -} diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c deleted file mode 100644 index ef50ed17a1..0000000000 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ /dev/null @@ -1,1178 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include -#include -#include -#include - -#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 "hexdecoct.h" -#include "hostname-util.h" -#include "in-addr-util.h" -#include "network-internal.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); - assert_return(addr, -EINVAL); - - if (lease->address == 0) - return -ENODATA; - - addr->s_addr = lease->address; - return 0; -} - -int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (!lease->have_broadcast) - return -ENODATA; - - addr->s_addr = lease->broadcast; - return 0; -} - -int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { - assert_return(lease, -EINVAL); - assert_return(lifetime, -EINVAL); - - if (lease->lifetime <= 0) - return -ENODATA; - - *lifetime = lease->lifetime; - return 0; -} - -int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) { - assert_return(lease, -EINVAL); - assert_return(t1, -EINVAL); - - if (lease->t1 <= 0) - return -ENODATA; - - *t1 = lease->t1; - return 0; -} - -int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) { - assert_return(lease, -EINVAL); - assert_return(t2, -EINVAL); - - if (lease->t2 <= 0) - return -ENODATA; - - *t2 = lease->t2; - return 0; -} - -int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { - assert_return(lease, -EINVAL); - assert_return(mtu, -EINVAL); - - if (lease->mtu <= 0) - return -ENODATA; - - *mtu = lease->mtu; - return 0; -} - -int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->dns_size <= 0) - return -ENODATA; - - *addr = lease->dns; - return (int) lease->dns_size; -} - -int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->ntp_size <= 0) - return -ENODATA; - - *addr = lease->ntp; - return (int) lease->ntp_size; -} - -int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { - assert_return(lease, -EINVAL); - assert_return(domainname, -EINVAL); - - if (!lease->domainname) - return -ENODATA; - - *domainname = lease->domainname; - return 0; -} - -int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) { - assert_return(lease, -EINVAL); - assert_return(hostname, -EINVAL); - - if (!lease->hostname) - return -ENODATA; - - *hostname = lease->hostname; - return 0; -} - -int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { - assert_return(lease, -EINVAL); - assert_return(root_path, -EINVAL); - - if (!lease->root_path) - return -ENODATA; - - *root_path = lease->root_path; - return 0; -} - -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->router == 0) - return -ENODATA; - - addr->s_addr = lease->router; - return 0; -} - -int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (!lease->have_subnet_mask) - return -ENODATA; - - addr->s_addr = lease->subnet_mask; - return 0; -} - -int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->server_address == 0) - return -ENODATA; - - addr->s_addr = lease->server_address; - return 0; -} - -int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - - if (lease->next_server == 0) - return -ENODATA; - - addr->s_addr = lease->next_server; - return 0; -} - -/* - * 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; - - 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; -} - -int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { - assert_return(lease, -EINVAL); - assert_return(data, -EINVAL); - assert_return(data_len, -EINVAL); - - if (lease->vendor_specific_len <= 0) - return -ENODATA; - - *data = lease->vendor_specific; - *data_len = lease->vendor_specific_len; - return 0; -} - -sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; - - while (lease->private_options) { - struct sd_dhcp_raw_option *option = lease->private_options; - - LIST_REMOVE(options, lease->private_options, option); - - free(option->data); - free(option); - } - - free(lease->hostname); - free(lease->domainname); - free(lease->dns); - free(lease->ntp); - free(lease->static_route); - free(lease->client_id); - free(lease->vendor_specific); - free(lease); - - return NULL; -} - -static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { - assert(option); - assert(ret); - - if (len != 4) - return -EINVAL; - - *ret = unaligned_read_be32((be32_t*) option); - if (*ret < min) - *ret = min; - - return 0; -} - -static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { - assert(option); - assert(ret); - - if (len != 2) - return -EINVAL; - - *ret = unaligned_read_be16((be16_t*) option); - if (*ret < min) - *ret = min; - - return 0; -} - -static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) { - assert(option); - assert(ret); - - if (len != 4) - return -EINVAL; - - memcpy(ret, option, 4); - return 0; -} - -static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { - assert(option); - assert(ret); - - if (len <= 0) - *ret = mfree(*ret); - else { - char *string; - - /* - * 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); - if (!string) - return -ENOMEM; - - free(*ret); - *ret = string; - } - - return 0; -} - -static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { - _cleanup_free_ char *name = NULL, *normalized = NULL; - int r; - - assert(option); - assert(ret); - - r = lease_parse_string(option, len, &name); - if (r < 0) - return r; - if (!name) { - *ret = mfree(*ret); - return 0; - } - - r = dns_name_normalize(name, &normalized); - if (r < 0) - return r; - - if (is_localhost(normalized)) - return -EINVAL; - - if (dns_name_is_root(normalized)) - return -EINVAL; - - free(*ret); - *ret = normalized; - normalized = NULL; - - return 0; -} - -static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { - assert(option); - assert(ret); - assert(n_ret); - - if (len <= 0) { - *ret = mfree(*ret); - *n_ret = 0; - } else { - size_t n_addresses; - struct in_addr *addresses; - - if (len % 4 != 0) - return -EINVAL; - - n_addresses = len / 4; - - addresses = newdup(struct in_addr, option, n_addresses); - if (!addresses) - return -ENOMEM; - - free(*ret); - *ret = addresses; - *n_ret = n_addresses; - } - - return 0; -} - -static int lease_parse_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { - - struct in_addr addr; - - assert(option || len <= 0); - assert(routes); - assert(routes_size); - assert(routes_allocated); - - if (len <= 0) - return 0; - - if (len % 8 != 0) - return -EINVAL; - - if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8))) - return -ENOMEM; - - while (len >= 8) { - struct sd_dhcp_route *route = *routes + *routes_size; - int r; - - r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); - if (r < 0) { - log_debug("Failed to determine destination prefix length from class based IP, ignoring"); - continue; - } - - assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0); - route->dst_addr = inet_makeaddr(inet_netof(addr), 0); - option += 4; - - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); - option += 4; - - len -= 8; - (*routes_size)++; - } - - return 0; -} - -/* parses RFC3442 Classless Static Route Option */ -static int lease_parse_classless_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { - - assert(option || len <= 0); - assert(routes); - assert(routes_size); - assert(routes_allocated); - - if (len <= 0) - return 0; - - /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ - - while (len > 0) { - uint8_t dst_octets; - struct sd_dhcp_route *route; - - if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1)) - return -ENOMEM; - - route = *routes + *routes_size; - - dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); - route->dst_prefixlen = *option; - option++; - len--; - - /* can't have more than 4 octets in IPv4 */ - if (dst_octets > 4 || len < dst_octets) - return -EINVAL; - - route->dst_addr.s_addr = 0; - memcpy(&route->dst_addr.s_addr, option, dst_octets); - option += dst_octets; - len -= dst_octets; - - if (len < 4) - return -EINVAL; - - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); - option += 4; - len -= 4; - - (*routes_size)++; - } - - return 0; -} - -int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) { - sd_dhcp_lease *lease = userdata; - int r; - - assert(lease); - - switch(code) { - - 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 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 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"); - else - lease->have_subnet_mask = true; - break; - - 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"); - else - lease->have_broadcast = true; - break; - - case SD_DHCP_OPTION_ROUTER: - if (len >= 4) { - r = lease_parse_be32(option, 4, &lease->router); - if (r < 0) - log_debug_errno(r, "Failed to parse router address, ignoring: %m"); - } - break; - - 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 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 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 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 SD_DHCP_OPTION_DOMAIN_NAME: - r = lease_parse_domain(option, len, &lease->domainname); - if (r < 0) { - log_debug_errno(r, "Failed to parse domain name, ignoring: %m"); - return 0; - } - - break; - - case SD_DHCP_OPTION_HOST_NAME: - r = lease_parse_domain(option, len, &lease->hostname); - if (r < 0) { - log_debug_errno(r, "Failed to parse host name, ignoring: %m"); - return 0; - } - - break; - - 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 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 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 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - r = lease_parse_classless_routes( - option, len, - &lease->static_route, - &lease->static_route_size, - &lease->static_route_allocated); - if (r < 0) - log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); - break; - - case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { - _cleanup_free_ char *tz = NULL; - - r = lease_parse_string(option, len, &tz); - if (r < 0) { - log_debug_errno(r, "Failed to parse timezone option, ignoring: %m"); - return 0; - } - - if (!timezone_is_valid(tz)) { - log_debug_errno(r, "Timezone is not valid, ignoring: %m"); - return 0; - } - - free(lease->timezone); - lease->timezone = tz; - tz = NULL; - - break; - } - - case SD_DHCP_OPTION_VENDOR_SPECIFIC: - - if (len <= 0) - lease->vendor_specific = mfree(lease->vendor_specific); - else { - void *p; - - p = memdup(option, len); - if (!p) - return -ENOMEM; - - free(lease->vendor_specific); - lease->vendor_specific = p; - } - - lease->vendor_specific_len = len; - break; - - 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; - - break; - - default: - log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code); - break; - } - - return 0; -} - -int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { - struct sd_dhcp_raw_option *cur, *option; - - assert(lease); - - LIST_FOREACH(options, cur, lease->private_options) { - if (tag < cur->tag) - break; - if (tag == cur->tag) { - log_debug("Ignoring duplicate option, tagged %i.", tag); - return 0; - } - } - - option = new(struct sd_dhcp_raw_option, 1); - if (!option) - return -ENOMEM; - - option->tag = tag; - option->length = len; - option->data = memdup(data, len); - if (!option->data) { - free(option); - return -ENOMEM; - } - - LIST_INSERT_BEFORE(options, lease->private_options, cur, option); - return 0; -} - -int dhcp_lease_new(sd_dhcp_lease **ret) { - sd_dhcp_lease *lease; - - lease = new0(sd_dhcp_lease, 1); - if (!lease) - return -ENOMEM; - - lease->router = INADDR_ANY; - lease->n_ref = 1; - - *ret = lease; - return 0; -} - -int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - struct sd_dhcp_raw_option *option; - struct in_addr address; - const struct in_addr *addresses; - const void *client_id, *data; - size_t client_id_len, data_len; - const char *string; - uint16_t mtu; - _cleanup_free_ sd_dhcp_route **routes = NULL; - uint32_t t1, t2, lifetime; - int r; - - assert(lease); - assert(lease_file); - - r = fopen_temporary(lease_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n"); - - r = sd_dhcp_lease_get_address(lease, &address); - if (r >= 0) - fprintf(f, "ADDRESS=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_netmask(lease, &address); - if (r >= 0) - fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_router(lease, &address); - if (r >= 0) - fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_server_identifier(lease, &address); - if (r >= 0) - fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_next_server(lease, &address); - if (r >= 0) - fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_broadcast(lease, &address); - if (r >= 0) - fprintf(f, "BROADCAST=%s\n", inet_ntoa(address)); - - r = sd_dhcp_lease_get_mtu(lease, &mtu); - if (r >= 0) - fprintf(f, "MTU=%" PRIu16 "\n", mtu); - - r = sd_dhcp_lease_get_t1(lease, &t1); - if (r >= 0) - fprintf(f, "T1=%" PRIu32 "\n", t1); - - r = sd_dhcp_lease_get_t2(lease, &t2); - if (r >= 0) - fprintf(f, "T2=%" PRIu32 "\n", t2); - - r = sd_dhcp_lease_get_lifetime(lease, &lifetime); - if (r >= 0) - fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime); - - r = sd_dhcp_lease_get_dns(lease, &addresses); - if (r > 0) { - fputs("DNS=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); - } - - r = sd_dhcp_lease_get_ntp(lease, &addresses); - if (r > 0) { - fputs("NTP=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); - } - - r = sd_dhcp_lease_get_domainname(lease, &string); - if (r >= 0) - fprintf(f, "DOMAINNAME=%s\n", string); - - r = sd_dhcp_lease_get_hostname(lease, &string); - if (r >= 0) - fprintf(f, "HOSTNAME=%s\n", string); - - r = sd_dhcp_lease_get_root_path(lease, &string); - if (r >= 0) - fprintf(f, "ROOT_PATH=%s\n", string); - - r = sd_dhcp_lease_get_routes(lease, &routes); - if (r > 0) - serialize_dhcp_routes(f, "ROUTES", routes, r); - - r = sd_dhcp_lease_get_timezone(lease, &string); - if (r >= 0) - fprintf(f, "TIMEZONE=%s\n", string); - - r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); - if (r >= 0) { - _cleanup_free_ char *client_id_hex = NULL; - - client_id_hex = hexmem(client_id, client_id_len); - if (!client_id_hex) { - r = -ENOMEM; - goto fail; - } - fprintf(f, "CLIENTID=%s\n", client_id_hex); - } - - r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len); - if (r >= 0) { - _cleanup_free_ char *option_hex = NULL; - - option_hex = hexmem(data, data_len); - if (!option_hex) { - r = -ENOMEM; - goto fail; - } - fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); - } - - LIST_FOREACH(options, option, lease->private_options) { - char key[strlen("OPTION_000")+1]; - - xsprintf(key, "OPTION_%" PRIu8, option->tag); - r = serialize_dhcp_option(f, key, option->data, option->length); - if (r < 0) - goto fail; - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, lease_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save lease data %s: %m", lease_file); -} - -int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { - - _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; - _cleanup_free_ char - *address = NULL, - *router = NULL, - *netmask = NULL, - *server_address = NULL, - *next_server = NULL, - *broadcast = NULL, - *dns = NULL, - *ntp = NULL, - *mtu = NULL, - *routes = NULL, - *client_id_hex = NULL, - *vendor_specific_hex = NULL, - *lifetime = NULL, - *t1 = NULL, - *t2 = NULL, - *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {}; - - int r, i; - - assert(lease_file); - assert(ret); - - r = dhcp_lease_new(&lease); - if (r < 0) - return r; - - r = parse_env_file(lease_file, NEWLINE, - "ADDRESS", &address, - "ROUTER", &router, - "NETMASK", &netmask, - "SERVER_IDENTIFIER", &server_address, - "NEXT_SERVER", &next_server, - "BROADCAST", &broadcast, - "DNS", &dns, - "NTP", &ntp, - "MTU", &mtu, - "DOMAINNAME", &lease->domainname, - "HOSTNAME", &lease->hostname, - "ROOT_PATH", &lease->root_path, - "ROUTES", &routes, - "CLIENTID", &client_id_hex, - "TIMEZONE", &lease->timezone, - "VENDOR_SPECIFIC", &vendor_specific_hex, - "LIFETIME", &lifetime, - "T1", &t1, - "T2", &t2, - "OPTION_224", &options[0], - "OPTION_225", &options[1], - "OPTION_226", &options[2], - "OPTION_227", &options[3], - "OPTION_228", &options[4], - "OPTION_229", &options[5], - "OPTION_230", &options[6], - "OPTION_231", &options[7], - "OPTION_232", &options[8], - "OPTION_233", &options[9], - "OPTION_234", &options[10], - "OPTION_235", &options[11], - "OPTION_236", &options[12], - "OPTION_237", &options[13], - "OPTION_238", &options[14], - "OPTION_239", &options[15], - "OPTION_240", &options[16], - "OPTION_241", &options[17], - "OPTION_242", &options[18], - "OPTION_243", &options[19], - "OPTION_244", &options[20], - "OPTION_245", &options[21], - "OPTION_246", &options[22], - "OPTION_247", &options[23], - "OPTION_248", &options[24], - "OPTION_249", &options[25], - "OPTION_250", &options[26], - "OPTION_251", &options[27], - "OPTION_252", &options[28], - "OPTION_253", &options[29], - "OPTION_254", &options[30], - NULL); - if (r < 0) - return r; - - if (address) { - r = inet_pton(AF_INET, address, &lease->address); - if (r <= 0) - log_debug("Failed to parse address %s, ignoring.", address); - } - - if (router) { - r = inet_pton(AF_INET, router, &lease->router); - if (r <= 0) - 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("Failed to parse netmask %s, ignoring.", netmask); - else - lease->have_subnet_mask = true; - } - - if (server_address) { - r = inet_pton(AF_INET, server_address, &lease->server_address); - if (r <= 0) - 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("Failed to parse next server %s, ignoring.", next_server); - } - - if (broadcast) { - r = inet_pton(AF_INET, broadcast, &lease->broadcast); - if (r <= 0) - log_debug("Failed to parse broadcast address %s, ignoring.", broadcast); - else - lease->have_broadcast = true; - } - - if (dns) { - r = deserialize_in_addrs(&lease->dns, dns); - if (r < 0) - log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns); - else - lease->dns_size = r; - } - - if (ntp) { - r = deserialize_in_addrs(&lease->ntp, ntp); - if (r < 0) - log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp); - else - lease->ntp_size = r; - } - - if (mtu) { - r = safe_atou16(mtu, &lease->mtu); - if (r < 0) - log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu); - } - - if (routes) { - r = deserialize_dhcp_routes( - &lease->static_route, - &lease->static_route_size, - &lease->static_route_allocated, - routes); - if (r < 0) - log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); - } - - if (lifetime) { - r = safe_atou32(lifetime, &lease->lifetime); - if (r < 0) - log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime); - } - - if (t1) { - r = safe_atou32(t1, &lease->t1); - if (r < 0) - log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1); - } - - if (t2) { - r = safe_atou32(t2, &lease->t2); - if (r < 0) - log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2); - } - - if (client_id_hex) { - r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex); - if (r < 0) - log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex); - } - - if (vendor_specific_hex) { - r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex); - if (r < 0) - log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); - } - - for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) { - _cleanup_free_ void *data = NULL; - size_t len; - - if (!options[i]) - continue; - - r = deserialize_dhcp_option(&data, &len, options[i]); - if (r < 0) { - log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]); - continue; - } - - r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len); - if (r < 0) - return r; - } - - *ret = lease; - lease = NULL; - - return 0; -} - -int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { - struct in_addr address, mask; - int r; - - assert(lease); - - if (lease->address == 0) - return -ENODATA; - - address.s_addr = lease->address; - - /* fall back to the default subnet masks based on address class */ - r = in_addr_default_subnet_mask(&address, &mask); - if (r < 0) - return r; - - lease->subnet_mask = mask.s_addr; - lease->have_subnet_mask = true; - - return 0; -} - -int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) { - assert_return(lease, -EINVAL); - assert_return(client_id, -EINVAL); - assert_return(client_id_len, -EINVAL); - - if (!lease->client_id) - return -ENODATA; - - *client_id = lease->client_id; - *client_id_len = lease->client_id_len; - - return 0; -} - -int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) { - assert_return(lease, -EINVAL); - assert_return(client_id || client_id_len <= 0, -EINVAL); - - if (client_id_len <= 0) - lease->client_id = mfree(lease->client_id); - else { - void *p; - - p = memdup(client_id, client_id_len); - if (!p) - return -ENOMEM; - - free(lease->client_id); - lease->client_id = p; - lease->client_id_len = client_id_len; - } - - return 0; -} - -int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) { - assert_return(lease, -EINVAL); - assert_return(tz, -EINVAL); - - if (!lease->timezone) - return -ENODATA; - - *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 deleted file mode 100644 index 11ee2e252e..0000000000 --- a/src/libsystemd-network/sd-dhcp-server.c +++ /dev/null @@ -1,1176 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include - -#include "sd-dhcp-server.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" -#include "unaligned.h" - -#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR -#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) - -/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet - * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address - * moreover, the server's own address may be in the pool, and is in that case reserved in order not to - * accidentally hand it out */ -int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) { - struct in_addr netmask_addr; - be32_t netmask; - uint32_t server_off, broadcast_off, size_max; - - assert_return(server, -EINVAL); - assert_return(address, -EINVAL); - assert_return(address->s_addr != INADDR_ANY, -EINVAL); - assert_return(prefixlen <= 32, -ERANGE); - assert_return(server->address == INADDR_ANY, -EBUSY); - - assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); - netmask = netmask_addr.s_addr; - - server_off = be32toh(address->s_addr & ~netmask); - broadcast_off = be32toh(~netmask); - - /* the server address cannot be the subnet address */ - assert_return(server_off != 0, -ERANGE); - - /* nor the broadcast address */ - assert_return(server_off != broadcast_off, -ERANGE); - - /* 0 offset means we should set a default, we skip the first (subnet) address - and take the next one */ - if (offset == 0) - offset = 1; - - size_max = (broadcast_off + 1) /* the number of addresses in the subnet */ - - offset /* exclude the addresses before the offset */ - - 1; /* exclude the last (broadcast) address */ - - /* The pool must contain at least one address */ - assert_return(size_max >= 1, -ERANGE); - - if (size != 0) - assert_return(size <= size_max, -ERANGE); - else - size = size_max; - - server->bound_leases = new0(DHCPLease*, size); - if (!server->bound_leases) - return -ENOMEM; - - server->pool_offset = offset; - server->pool_size = size; - - server->address = address->s_addr; - server->netmask = netmask; - server->subnet = address->s_addr & netmask; - - if (server_off >= offset && server_off - offset < size) - server->bound_leases[server_off - offset] = &server->invalid_lease; - - return 0; -} - -int sd_dhcp_server_is_running(sd_dhcp_server *server) { - assert_return(server, false); - - return !!server->receive_message; -} - -sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) { - - if (!server) - return NULL; - - assert(server->n_ref >= 1); - server->n_ref++; - - return server; -} - -void client_id_hash_func(const void *p, struct siphash *state) { - const DHCPClientId *id = p; - - assert(id); - assert(id->length); - assert(id->data); - - 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) { - const DHCPClientId *a, *b; - - a = _a; - b = _b; - - assert(!a->length || a->data); - assert(!b->length || b->data); - - if (a->length != b->length) - return a->length < b->length ? -1 : 1; - - return memcmp(a->data, b->data, a->length); -} - -static const struct hash_ops client_id_hash_ops = { - .hash = client_id_hash_func, - .compare = client_id_compare_func -}; - -static void dhcp_lease_free(DHCPLease *lease) { - if (!lease) - return; - - free(lease->client_id.data); - free(lease); -} - -sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { - DHCPLease *lease; - - if (!server) - return NULL; - - assert(server->n_ref >= 1); - server->n_ref--; - - if (server->n_ref > 0) - return NULL; - - log_dhcp_server(server, "UNREF"); - - sd_dhcp_server_stop(server); - - sd_event_unref(server->event); - - free(server->timezone); - free(server->dns); - free(server->ntp); - - while ((lease = hashmap_steal_first(server->leases_by_client_id))) - dhcp_lease_free(lease); - hashmap_free(server->leases_by_client_id); - - free(server->bound_leases); - free(server); - - return NULL; -} - -int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { - _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; - - assert_return(ret, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - - server = new0(sd_dhcp_server, 1); - if (!server) - return -ENOMEM; - - server->n_ref = 1; - server->fd_raw = -1; - server->fd = -1; - server->address = htobe32(INADDR_ANY); - server->netmask = htobe32(INADDR_ANY); - server->ifindex = ifindex; - server->leases_by_client_id = hashmap_new(&client_id_hash_ops); - server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC); - server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC); - - *ret = server; - server = NULL; - - return 0; -} - -int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) { - int r; - - assert_return(server, -EINVAL); - assert_return(!server->event, -EBUSY); - - if (event) - server->event = sd_event_ref(event); - else { - r = sd_event_default(&server->event); - if (r < 0) - return r; - } - - server->event_priority = priority; - - return 0; -} - -int sd_dhcp_server_detach_event(sd_dhcp_server *server) { - assert_return(server, -EINVAL); - - server->event = sd_event_unref(server->event); - - return 0; -} - -sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) { - assert_return(server, NULL); - - return server->event; -} - -int sd_dhcp_server_stop(sd_dhcp_server *server) { - assert_return(server, -EINVAL); - - server->receive_message = - sd_event_source_unref(server->receive_message); - - server->fd_raw = safe_close(server->fd_raw); - server->fd = safe_close(server->fd); - - log_dhcp_server(server, "STOPPED"); - - return 0; -} - -static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, - DHCPPacket *packet, size_t len) { - union sockaddr_union link = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETH_P_IP), - .ll.sll_ifindex = server->ifindex, - .ll.sll_halen = ETH_ALEN, - }; - - assert(server); - assert(server->ifindex > 0); - assert(server->address); - assert(packet); - assert(len > sizeof(DHCPPacket)); - - memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN); - - dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER, - packet->dhcp.yiaddr, - DHCP_PORT_CLIENT, len); - - return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len); -} - -static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, - uint16_t destination_port, - DHCPMessage *message, size_t len) { - union sockaddr_union dest = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(destination_port), - .in.sin_addr.s_addr = destination, - }; - struct iovec iov = { - .iov_base = message, - .iov_len = len, - }; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {}; - struct msghdr msg = { - .msg_name = &dest, - .msg_namelen = sizeof(dest.in), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - struct cmsghdr *cmsg; - struct in_pktinfo *pktinfo; - int r; - - assert(server); - assert(server->fd > 0); - assert(message); - assert(len > sizeof(DHCPMessage)); - - cmsg = CMSG_FIRSTHDR(&msg); - assert(cmsg); - - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - /* we attach source interface and address info to the message - rather than binding the socket. This will be mostly useful - when we gain support for arbitrary number of server addresses - */ - pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg); - assert(pktinfo); - - pktinfo->ipi_ifindex = server->ifindex; - pktinfo->ipi_spec_dst.s_addr = server->address; - - r = sendmsg(server->fd, &msg, 0); - if (r < 0) - return -errno; - - return 0; -} - -static bool requested_broadcast(DHCPRequest *req) { - assert(req); - - return req->message->flags & htobe16(0x8000); -} - -int dhcp_server_send_packet(sd_dhcp_server *server, - DHCPRequest *req, DHCPPacket *packet, - int type, size_t optoffset) { - be32_t destination = INADDR_ANY; - uint16_t destination_port = DHCP_PORT_CLIENT; - int r; - - assert(server); - assert(req); - assert(req->max_optlen); - assert(optoffset <= req->max_optlen); - assert(packet); - - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, - SD_DHCP_OPTION_SERVER_IDENTIFIER, - 4, &server->address); - if (r < 0) - return r; - - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, - SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - /* RFC 2131 Section 4.1 - - If the ’giaddr’ field in a DHCP message from a client is non-zero, - the server sends any return messages to the ’DHCP server’ port on the - BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’ - field is zero and the ’ciaddr’ field is nonzero, then the server - unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’. - If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is - set, then the server broadcasts DHCPOFFER and DHCPACK messages to - 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and - ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK - messages to the client’s hardware address and ’yiaddr’ address. In - all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK - messages to 0xffffffff. - - Section 4.3.2 - - If ’giaddr’ is set in the DHCPREQUEST message, the client is on a - different subnet. The server MUST set the broadcast bit in the - DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the - client, because the client may not have a correct network address - or subnet mask, and the client may not be answering ARP requests. - */ - if (req->message->giaddr) { - destination = req->message->giaddr; - destination_port = DHCP_PORT_SERVER; - if (type == DHCP_NAK) - packet->dhcp.flags = htobe16(0x8000); - } else if (req->message->ciaddr && type != DHCP_NAK) - destination = req->message->ciaddr; - - if (destination != INADDR_ANY) - return dhcp_server_send_udp(server, destination, - destination_port, &packet->dhcp, - sizeof(DHCPMessage) + optoffset); - else if (requested_broadcast(req) || type == DHCP_NAK) - return dhcp_server_send_udp(server, INADDR_BROADCAST, - destination_port, &packet->dhcp, - sizeof(DHCPMessage) + optoffset); - else - /* we cannot send UDP packet to specific MAC address when the - address is not yet configured, so must fall back to raw - packets */ - return dhcp_server_send_unicast_raw(server, packet, - sizeof(DHCPPacket) + optoffset); -} - -static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret, - uint8_t type, size_t *_optoffset, - DHCPRequest *req) { - _cleanup_free_ DHCPPacket *packet = NULL; - size_t optoffset = 0; - int r; - - assert(server); - assert(ret); - assert(_optoffset); - assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)); - - packet = malloc0(sizeof(DHCPPacket) + req->max_optlen); - if (!packet) - return -ENOMEM; - - r = dhcp_message_init(&packet->dhcp, BOOTREPLY, - be32toh(req->message->xid), type, ARPHRD_ETHER, - req->max_optlen, &optoffset); - if (r < 0) - return r; - - packet->dhcp.flags = req->message->flags; - packet->dhcp.giaddr = req->message->giaddr; - memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN); - - *_optoffset = optoffset; - *ret = packet; - packet = NULL; - - return 0; -} - -static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, - be32_t address) { - _cleanup_free_ DHCPPacket *packet = NULL; - size_t offset; - be32_t lease_time; - int r; - - r = server_message_init(server, &packet, DHCP_OFFER, &offset, req); - if (r < 0) - return r; - - packet->dhcp.yiaddr = address; - - lease_time = htobe32(req->lifetime); - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - 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, - SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); - if (r < 0) - return r; - - if (server->emit_router) { - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_ROUTER, 4, &server->address); - if (r < 0) - return r; - } - - r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset); - if (r < 0) - return r; - - return 0; -} - -static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, - be32_t address) { - _cleanup_free_ DHCPPacket *packet = NULL; - size_t offset; - be32_t lease_time; - int r; - - r = server_message_init(server, &packet, DHCP_ACK, &offset, req); - if (r < 0) - return r; - - packet->dhcp.yiaddr = address; - - lease_time = htobe32(req->lifetime); - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - 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, - SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); - if (r < 0) - return r; - - if (server->emit_router) { - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - 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, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER, - sizeof(struct in_addr) * server->n_dns, server->dns); - if (r < 0) - return r; - } - - if (server->n_ntp > 0) { - r = dhcp_option_append( - &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_NTP_SERVER, - sizeof(struct in_addr) * server->n_ntp, server->ntp); - if (r < 0) - return r; - } - - if (server->timezone) { - r = dhcp_option_append( - &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_NEW_TZDB_TIMEZONE, - strlen(server->timezone), server->timezone); - if (r < 0) - return r; - } - - r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset); - if (r < 0) - return r; - - return 0; -} - -static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) { - _cleanup_free_ DHCPPacket *packet = NULL; - size_t offset; - int r; - - r = server_message_init(server, &packet, DHCP_NAK, &offset, req); - if (r < 0) - return r; - - return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset); -} - -static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, - be32_t gateway, uint8_t chaddr[]) { - _cleanup_free_ DHCPPacket *packet = NULL; - size_t optoffset = 0; - int r; - - assert(server); - assert(address != INADDR_ANY); - assert(chaddr); - - packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE); - if (!packet) - return -ENOMEM; - - r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0, - DHCP_FORCERENEW, ARPHRD_ETHER, - DHCP_MIN_OPTIONS_SIZE, &optoffset); - if (r < 0) - return r; - - r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE, - &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); - if (r < 0) - return r; - - memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN); - - r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT, - &packet->dhcp, - sizeof(DHCPMessage) + optoffset); - if (r < 0) - return r; - - return 0; -} - -static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) { - DHCPRequest *req = userdata; - - assert(req); - - switch(code) { - case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: - if (len == 4) - req->lifetime = unaligned_read_be32(option); - - break; - case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS: - if (len == 4) - memcpy(&req->requested_ip, option, sizeof(be32_t)); - - break; - case SD_DHCP_OPTION_SERVER_IDENTIFIER: - if (len == 4) - memcpy(&req->server_id, option, sizeof(be32_t)); - - break; - case SD_DHCP_OPTION_CLIENT_IDENTIFIER: - if (len >= 2) { - uint8_t *data; - - data = memdup(option, len); - if (!data) - return -ENOMEM; - - free(req->client_id.data); - req->client_id.data = data; - req->client_id.length = len; - } - - break; - case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE: - - if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket)) - req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket); - - break; - } - - return 0; -} - -static void dhcp_request_free(DHCPRequest *req) { - if (!req) - return; - - free(req->client_id.data); - free(req); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free); -#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep) - -static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) { - assert(req); - assert(message); - - req->message = message; - - /* set client id based on MAC address if client did not send an explicit - one */ - if (!req->client_id.data) { - void *data; - - data = malloc0(ETH_ALEN + 1); - if (!data) - return -ENOMEM; - - ((uint8_t*) data)[0] = 0x01; - memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN); - - req->client_id.length = ETH_ALEN + 1; - req->client_id.data = data; - } - - if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE) - req->max_optlen = DHCP_MIN_OPTIONS_SIZE; - - if (req->lifetime <= 0) - req->lifetime = MAX(1ULL, server->default_lease_time); - - if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time) - req->lifetime = server->max_lease_time; - - return 0; -} - -static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { - assert(server); - - if (!server->pool_size) - return -EINVAL; - - if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) || - be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size))) - return -ERANGE; - - return be32toh(requested_ip & ~server->netmask) - server->pool_offset; -} - -#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) - -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; - - assert(server); - assert(message); - - if (message->op != BOOTREQUEST || - message->htype != ARPHRD_ETHER || - message->hlen != ETHER_ADDR_LEN) - return 0; - - req = new0(DHCPRequest, 1); - if (!req) - return -ENOMEM; - - type = dhcp_option_parse(message, length, parse_request, req, &error_message); - if (type < 0) - return 0; - - r = ensure_sane_request(server, req, message); - if (r < 0) - /* this only fails on critical errors */ - return r; - - existing_lease = hashmap_get(server->leases_by_client_id, - &req->client_id); - - switch(type) { - - case DHCP_DISCOVER: { - be32_t address = INADDR_ANY; - unsigned i; - - log_dhcp_server(server, "DISCOVER (0x%x)", - be32toh(req->message->xid)); - - if (!server->pool_size) - /* no pool allocated */ - return 0; - - /* for now pick a random free address from the pool */ - 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 */ - - 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]) { - address = server->subnet | htobe32(server->pool_offset + next_offer); - break; - } else - next_offer = (next_offer + 1) % server->pool_size; - } - } - - if (address == INADDR_ANY) - /* no free addresses left */ - return 0; - - r = server_send_offer(server, req, address); - if (r < 0) { - /* this only fails on critical errors */ - log_dhcp_server(server, "could not send offer: %s", - strerror(-r)); - return r; - } else { - log_dhcp_server(server, "OFFER (0x%x)", - be32toh(req->message->xid)); - return DHCP_OFFER; - } - - break; - } - case DHCP_DECLINE: - 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 */ - - return 1; - - case DHCP_REQUEST: { - be32_t address; - bool init_reboot = false; - int pool_offset; - - /* see RFC 2131, section 4.3.2 */ - - if (req->server_id) { - log_dhcp_server(server, "REQUEST (selecting) (0x%x)", - be32toh(req->message->xid)); - - /* SELECTING */ - if (req->server_id != server->address) - /* client did not pick us */ - return 0; - - if (req->message->ciaddr) - /* this MUST be zero */ - return 0; - - if (!req->requested_ip) - /* this must be filled in with the yiaddr - from the chosen OFFER */ - return 0; - - address = req->requested_ip; - } else if (req->requested_ip) { - log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)", - be32toh(req->message->xid)); - - /* INIT-REBOOT */ - if (req->message->ciaddr) - /* this MUST be zero */ - return 0; - - /* TODO: check more carefully if IP is correct */ - address = req->requested_ip; - init_reboot = true; - } else { - log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)", - be32toh(req->message->xid)); - - /* REBINDING / RENEWING */ - if (!req->message->ciaddr) - /* this MUST be filled in with clients IP address */ - return 0; - - address = req->message->ciaddr; - } - - pool_offset = get_pool_offset(server, address); - - /* verify that the requested address is from the pool, and either - owned by the current client or free */ - if (pool_offset >= 0 && - server->bound_leases[pool_offset] == existing_lease) { - DHCPLease *lease; - usec_t time_now = 0; - - if (!existing_lease) { - lease = new0(DHCPLease, 1); - lease->address = req->requested_ip; - lease->client_id.data = memdup(req->client_id.data, - req->client_id.length); - if (!lease->client_id.data) { - free(lease); - return -ENOMEM; - } - lease->client_id.length = req->client_id.length; - memcpy(&lease->chaddr, &req->message->chaddr, - ETH_ALEN); - lease->gateway = req->message->giaddr; - } else - lease = existing_lease; - - r = sd_event_now(server->event, - clock_boottime_or_monotonic(), - &time_now); - if (r < 0) { - if (!existing_lease) - dhcp_lease_free(lease); - return r; - } - - lease->expiration = req->lifetime * USEC_PER_SEC + time_now; - - r = server_send_ack(server, req, address); - if (r < 0) { - /* this only fails on critical errors */ - log_dhcp_server(server, "could not send ack: %s", - strerror(-r)); - - if (!existing_lease) - dhcp_lease_free(lease); - - return r; - } else { - log_dhcp_server(server, "ACK (0x%x)", - be32toh(req->message->xid)); - - server->bound_leases[pool_offset] = lease; - hashmap_put(server->leases_by_client_id, - &lease->client_id, lease); - - return DHCP_ACK; - } - } else if (init_reboot) { - r = server_send_nak(server, req); - if (r < 0) { - /* this only fails on critical errors */ - log_dhcp_server(server, "could not send nak: %s", - strerror(-r)); - return r; - } else { - log_dhcp_server(server, "NAK (0x%x)", - be32toh(req->message->xid)); - return DHCP_NAK; - } - } - - break; - } - - case DHCP_RELEASE: { - int pool_offset; - - log_dhcp_server(server, "RELEASE (0x%x)", - be32toh(req->message->xid)); - - if (!existing_lease) - return 0; - - if (existing_lease->address != req->message->ciaddr) - return 0; - - pool_offset = get_pool_offset(server, req->message->ciaddr); - if (pool_offset < 0) - return 0; - - if (server->bound_leases[pool_offset] == existing_lease) { - server->bound_leases[pool_offset] = NULL; - hashmap_remove(server->leases_by_client_id, existing_lease); - dhcp_lease_free(existing_lease); - - return 1; - } else - return 0; - } - } - - return 0; -} - -static int server_receive_message(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { - _cleanup_free_ DHCPMessage *message = NULL; - uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))]; - sd_dhcp_server *server = userdata; - struct iovec iov = {}; - struct msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = sizeof(cmsgbuf), - }; - struct cmsghdr *cmsg; - ssize_t buflen, len; - - assert(server); - - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return buflen; - - message = malloc(buflen); - if (!message) - return -ENOMEM; - - iov.iov_base = message; - iov.iov_len = buflen; - - len = recvmsg(fd, &msg, 0); - 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) { - if (cmsg->cmsg_level == IPPROTO_IP && - cmsg->cmsg_type == IP_PKTINFO && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) { - struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg); - - /* TODO figure out if this can be done as a filter on - * the socket, like for IPv6 */ - if (server->ifindex != info->ipi_ifindex) - return 0; - - break; - } - } - - return dhcp_server_handle_message(server, message, (size_t)len); -} - -int sd_dhcp_server_start(sd_dhcp_server *server) { - int r; - - assert_return(server, -EINVAL); - assert_return(server->event, -EINVAL); - assert_return(!server->receive_message, -EBUSY); - assert_return(server->fd_raw == -1, -EBUSY); - assert_return(server->fd == -1, -EBUSY); - assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH); - - r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); - if (r < 0) { - r = -errno; - sd_dhcp_server_stop(server); - return r; - } - server->fd_raw = r; - - r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; - } - server->fd = r; - - r = sd_event_add_io(server->event, &server->receive_message, - server->fd, EPOLLIN, - server_receive_message, server); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; - } - - r = sd_event_source_set_priority(server->receive_message, - server->event_priority); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; - } - - log_dhcp_server(server, "STARTED"); - - return 0; -} - -int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { - unsigned i; - int r = 0; - - assert_return(server, -EINVAL); - assert(server->bound_leases); - - for (i = 0; i < server->pool_size; i++) { - DHCPLease *lease = server->bound_leases[i]; - - if (!lease || lease == &server->invalid_lease) - continue; - - r = server_send_forcerenew(server, lease->address, - lease->gateway, - lease->chaddr); - if (r < 0) - return r; - else - log_dhcp_server(server, "FORCERENEW"); - } - - return r; -} - -int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) { - int r; - - assert_return(server, -EINVAL); - assert_return(timezone_is_valid(tz), -EINVAL); - - if (streq_ptr(tz, server->timezone)) - return 0; - - r = free_and_strdup(&server->timezone, tz); - if (r < 0) - return r; - - return 1; -} - -int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) { - assert_return(server, -EINVAL); - - if (t == server->max_lease_time) - return 0; - - server->max_lease_time = t; - return 1; -} - -int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) { - assert_return(server, -EINVAL); - - if (t == server->default_lease_time) - return 0; - - server->default_lease_time = t; - return 1; -} - -int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) { - assert_return(server, -EINVAL); - assert_return(dns || n <= 0, -EINVAL); - - if (server->n_dns == n && - memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0) - return 0; - - if (n <= 0) { - server->dns = mfree(server->dns); - server->n_dns = 0; - } else { - struct in_addr *c; - - c = newdup(struct in_addr, dns, n); - if (!c) - return -ENOMEM; - - free(server->dns); - server->dns = c; - server->n_dns = n; - } - - return 1; -} - -int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) { - assert_return(server, -EINVAL); - assert_return(ntp || n <= 0, -EINVAL); - - if (server->n_ntp == n && - memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0) - return 0; - - if (n <= 0) { - server->ntp = mfree(server->ntp); - server->n_ntp = 0; - } else { - struct in_addr *c; - - c = newdup(struct in_addr, ntp, n); - if (!c) - return -ENOMEM; - - free(server->ntp); - server->ntp = c; - server->n_ntp = n; - } - - return 1; -} - -int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) { - assert_return(server, -EINVAL); - - if (enabled == server->emit_router) - return 0; - - server->emit_router = enabled; - - return 1; -} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c deleted file mode 100644 index 463fde401c..0000000000 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ /dev/null @@ -1,1335 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#include "sd-dhcp6-client.h" - -#include "alloc-util.h" -#include "dhcp-identifier.h" -#include "dhcp6-internal.h" -#include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" -#include "fd-util.h" -#include "in-addr-util.h" -#include "network-internal.h" -#include "random-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "util.h" - -#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN - -struct sd_dhcp6_client { - unsigned n_ref; - - enum DHCP6State state; - sd_event *event; - int event_priority; - int ifindex; - struct in6_addr local_address; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - DHCP6IA ia_na; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - be16_t *req_opts; - size_t req_opts_allocated; - size_t req_opts_len; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; -}; - -static const uint16_t default_req_opts[] = { - 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] = { - [DHCP6_SOLICIT] = "SOLICIT", - [DHCP6_ADVERTISE] = "ADVERTISE", - [DHCP6_REQUEST] = "REQUEST", - [DHCP6_CONFIRM] = "CONFIRM", - [DHCP6_RENEW] = "RENEW", - [DHCP6_REBIND] = "REBIND", - [DHCP6_REPLY] = "REPLY", - [DHCP6_RELEASE] = "RELEASE", - [DHCP6_DECLINE] = "DECLINE", - [DHCP6_RECONFIGURE] = "RECONFIGURE", - [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST", - [DHCP6_RELAY_FORW] = "RELAY-FORW", - [DHCP6_RELAY_REPL] = "RELAY-REPL", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); - -const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); - -#define DHCP6_CLIENT_DONT_DESTROY(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); - -int sd_dhcp6_client_set_callback( - sd_dhcp6_client *client, - sd_dhcp6_client_callback_t cb, - void *userdata) { - - assert_return(client, -EINVAL); - - client->callback = cb; - client->userdata = userdata; - - return 0; -} - -int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { - - assert_return(client, -EINVAL); - assert_return(ifindex >= -1, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - - client->ifindex = ifindex; - 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, - uint16_t arp_type) { - - assert_return(client, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - assert_return(arp_type > 0, -EINVAL); - - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - - if (arp_type == ARPHRD_ETHER) - assert_return(addr_len == ETH_ALEN, -EINVAL); - else if (arp_type == ARPHRD_INFINIBAND) - assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); - else - return -EINVAL; - - if (client->mac_addr_len == addr_len && - memcmp(&client->mac_addr, addr, addr_len) == 0) - return 0; - - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; - client->arp_type = arp_type; - - return 0; -} - -static int client_ensure_duid(sd_dhcp6_client *client) { - if (client->duid_len != 0) - return 0; - - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); -} - -/** - * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid - * without further modification. Otherwise, if duid_type is supported, DUID - * is set based on that type. Otherwise, an error is returned. - */ -int sd_dhcp6_client_set_duid( - sd_dhcp6_client *client, - uint16_t duid_type, - const void *duid, - size_t duid_len) { - - int r; - assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - - if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); - if (r < 0) - return r; - } - - if (duid != NULL) { - client->duid.type = htobe16(duid_type); - memcpy(&client->duid.raw.data, duid, duid_len); - client->duid_len = sizeof(client->duid.type) + duid_len; - } else if (duid_type == DUID_TYPE_EN) { - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return r; - } else - return -EOPNOTSUPP; - - return 0; -} - -int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { - assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - - client->ia_na.id = htobe32(iaid); - - return 0; -} - -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; - - return 0; -} - -int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) { - assert_return(client, -EINVAL); - assert_return(enabled, -EINVAL); - - *enabled = client->information_request; - - return 0; -} - -int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) { - size_t t; - - assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - - switch(option) { - - 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: - return -EINVAL; - } - - for (t = 0; t < client->req_opts_len; t++) - if (client->req_opts[t] == htobe16(option)) - return -EEXIST; - - if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, - client->req_opts_len + 1)) - return -ENOMEM; - - client->req_opts[client->req_opts_len++] = htobe16(option); - - return 0; -} - -int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { - assert_return(client, -EINVAL); - - if (!client->lease) - return -ENOMSG; - - if (ret) - *ret = client->lease; - - return 0; -} - -static void client_notify(sd_dhcp6_client *client, int event) { - assert(client); - - if (client->callback) - client->callback(client, event, client->userdata); -} - -static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) { - assert(client); - - if (client->lease) { - dhcp6_lease_clear_timers(&client->lease->ia); - sd_dhcp6_lease_unref(client->lease); - } - - client->lease = lease; -} - -static int client_reset(sd_dhcp6_client *client) { - assert(client); - - client_set_lease(client, NULL); - - client->receive_message = - sd_event_source_unref(client->receive_message); - - client->fd = safe_close(client->fd); - - client->transaction_id = 0; - client->transaction_start = 0; - - client->ia_na.timeout_t1 = - sd_event_source_unref(client->ia_na.timeout_t1); - client->ia_na.timeout_t2 = - sd_event_source_unref(client->ia_na.timeout_t2); - - client->retransmit_time = 0; - client->retransmit_count = 0; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); - - client->state = DHCP6_STATE_STOPPED; - - return 0; -} - -static void client_stop(sd_dhcp6_client *client, int error) { - DHCP6_CLIENT_DONT_DESTROY(client); - - assert(client); - - client_notify(client, error); - - client_reset(client); -} - -static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { - _cleanup_free_ DHCP6Message *message = NULL; - struct in6_addr all_servers = - IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; - size_t len, optlen = 512; - uint8_t *opt; - int r; - usec_t elapsed_usec; - be16_t elapsed_time; - - assert(client); - - len = sizeof(DHCP6Message) + optlen; - - message = malloc0(len); - if (!message) - return -ENOMEM; - - opt = (uint8_t *)(message + 1); - - message->transaction_id = client->transaction_id; - - switch(client->state) { - case DHCP6_STATE_INFORMATION_REQUEST: - message->type = DHCP6_INFORMATION_REQUEST; - - break; - - case DHCP6_STATE_SOLICITATION: - message->type = DHCP6_SOLICIT; - - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); - if (r < 0) - return r; - - r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na); - if (r < 0) - return r; - - break; - - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - - if (client->state == DHCP6_STATE_REQUEST) - message->type = DHCP6_REQUEST; - else - message->type = DHCP6_RENEW; - - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, - client->lease->serverid_len, - client->lease->serverid); - if (r < 0) - return r; - - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - - break; - - case DHCP6_STATE_REBIND: - message->type = DHCP6_REBIND; - - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - - break; - - case DHCP6_STATE_STOPPED: - case DHCP6_STATE_BOUND: - return -EINVAL; - } - - 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, SD_DHCP6_OPTION_CLIENTID, - client->duid_len, &client->duid); - if (r < 0) - return r; - - elapsed_usec = time_now - client->transaction_start; - if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10) - elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10); - else - elapsed_time = 0xffff; - - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, - sizeof(elapsed_time), &elapsed_time); - if (r < 0) - return r; - - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, - len - optlen); - if (r < 0) - return r; - - log_dhcp6_client(client, "Sent %s", - dhcp6_message_type_to_string(message->type)); - - return 0; -} - -static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - client->lease->ia.timeout_t2 = - sd_event_source_unref(client->lease->ia.timeout_t2); - - log_dhcp6_client(client, "Timeout T2"); - - client_start(client, DHCP6_STATE_REBIND); - - return 0; -} - -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - client->lease->ia.timeout_t1 = - sd_event_source_unref(client->lease->ia.timeout_t1); - - log_dhcp6_client(client, "Timeout T1"); - - client_start(client, DHCP6_STATE_RENEW); - - return 0; -} - -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - DHCP6_CLIENT_DONT_DESTROY(client); - enum DHCP6State state; - - assert(s); - assert(client); - assert(client->event); - - state = client->state; - - client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - - /* RFC 3315, section 18.1.4., says that "...the client may choose to - use a Solicit message to locate a new DHCP server..." */ - if (state == DHCP6_STATE_REBIND) - client_start(client, DHCP6_STATE_SOLICITATION); - - return 0; -} - -static usec_t client_timeout_compute_random(usec_t val) { - return val - val / 10 + - (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; -} - -static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - int r = 0; - sd_dhcp6_client *client = userdata; - usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - usec_t max_retransmit_duration = 0; - uint8_t max_retransmit_count = 0; - char time_string[FORMAT_TIMESPAN_MAX]; - uint32_t expire = 0; - - assert(s); - assert(client); - assert(client->event); - - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - switch (client->state) { - case DHCP6_STATE_INFORMATION_REQUEST: - init_retransmit_time = DHCP6_INF_TIMEOUT; - max_retransmit_time = DHCP6_INF_MAX_RT; - - break; - - case DHCP6_STATE_SOLICITATION: - - if (client->retransmit_count && client->lease) { - client_start(client, DHCP6_STATE_REQUEST); - return 0; - } - - init_retransmit_time = DHCP6_SOL_TIMEOUT; - max_retransmit_time = DHCP6_SOL_MAX_RT; - - break; - - case DHCP6_STATE_REQUEST: - init_retransmit_time = DHCP6_REQ_TIMEOUT; - max_retransmit_time = DHCP6_REQ_MAX_RT; - max_retransmit_count = DHCP6_REQ_MAX_RC; - - break; - - case DHCP6_STATE_RENEW: - init_retransmit_time = DHCP6_REN_TIMEOUT; - max_retransmit_time = DHCP6_REN_MAX_RT; - - /* RFC 3315, section 18.1.3. says max retransmit duration will - be the remaining time until T2. Instead of setting MRD, - wait for T2 to trigger with the same end result */ - - break; - - case DHCP6_STATE_REBIND: - init_retransmit_time = DHCP6_REB_TIMEOUT; - max_retransmit_time = DHCP6_REB_MAX_RT; - - if (!client->timeout_resend_expire) { - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, - &expire); - if (r < 0) { - client_stop(client, r); - return 0; - } - max_retransmit_duration = expire * USEC_PER_SEC; - } - - break; - - case DHCP6_STATE_STOPPED: - case DHCP6_STATE_BOUND: - return 0; - } - - if (max_retransmit_count && - client->retransmit_count >= max_retransmit_count) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - r = client_send_message(client, time_now); - if (r >= 0) - client->retransmit_count++; - - if (!client->retransmit_time) { - client->retransmit_time = - client_timeout_compute_random(init_retransmit_time); - - if (client->state == DHCP6_STATE_SOLICITATION) - client->retransmit_time += init_retransmit_time / 10; - - } else { - if (max_retransmit_time && - client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - } - - log_dhcp6_client(client, "Next retransmission in %s", - 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(), - time_now + client->retransmit_time, - 10 * USEC_PER_MSEC, client_timeout_resend, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer"); - if (r < 0) - goto error; - - if (max_retransmit_duration && !client->timeout_resend_expire) { - - log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", - max_retransmit_duration / USEC_PER_SEC); - - r = sd_event_add_time(client->event, - &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, - USEC_PER_SEC, - client_timeout_resend_expire, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend_expire, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer"); - if (r < 0) - goto error; - } - -error: - if (r < 0) - client_stop(client, r); - - return 0; -} - -static int client_ensure_iaid(sd_dhcp6_client *client) { - int r; - - assert(client); - - if (client->ia_na.id) - return 0; - - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id); - if (r < 0) - return r; - - return 0; -} - -static int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease) { - int r; - uint8_t *optval, *option, *id = NULL; - uint16_t optcode, status; - size_t optlen, id_len; - bool clientid = false; - be32_t iaid_lease; - - assert(client); - assert(message); - assert(len >= sizeof(DHCP6Message)); - assert(lease); - - option = (uint8_t *)message + sizeof(DHCP6Message); - len -= sizeof(DHCP6Message); - - while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, - &optval)) >= 0) { - switch (optcode) { - case SD_DHCP6_OPTION_CLIENTID: - if (clientid) { - log_dhcp6_client(client, "%s contains multiple clientids", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } - - if (optlen != client->duid_len || - memcmp(&client->duid, optval, optlen) != 0) { - log_dhcp6_client(client, "%s DUID does not match", - dhcp6_message_type_to_string(message->type)); - - return -EINVAL; - } - clientid = true; - - break; - - 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", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } - - r = dhcp6_lease_set_serverid(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_PREFERENCE: - if (optlen != 1) - return -EINVAL; - - r = dhcp6_lease_set_preference(lease, *optval); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < 2) - return -EINVAL; - - status = optval[0] << 8 | optval[1]; - if (status) { - log_dhcp6_client(client, "%s Status %s", - dhcp6_message_type_to_string(message->type), - dhcp6_message_status_to_string(status)); - return -EINVAL; - } - - break; - - case SD_DHCP6_OPTION_IA_NA: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Information request ignoring IA NA option"); - - break; - } - - r = dhcp6_option_parse_ia(&optval, &optlen, optcode, - &lease->ia); - if (r < 0 && r != -ENOMSG) - return r; - - r = dhcp6_lease_get_iaid(lease, &iaid_lease); - if (r < 0) - return r; - - if (client->ia_na.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } - - break; - - case SD_DHCP6_OPTION_RAPID_COMMIT: - r = dhcp6_lease_set_rapid_commit(lease); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_set_dns(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_set_domains(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_set_ntp(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_set_sntp(lease, optval, optlen); - if (r < 0) - return r; - - break; - } - - } - - if (r == -ENOMSG) - r = 0; - - if (r < 0 || !clientid) { - log_dhcp6_client(client, "%s has incomplete options", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } - - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - r = dhcp6_lease_get_serverid(lease, &id, &id_len); - if (r < 0) - log_dhcp6_client(client, "%s has no server id", - dhcp6_message_type_to_string(message->type)); - } - - return r; -} - -static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool rapid_commit; - int r; - - assert(client); - assert(reply); - - if (reply->type != DHCP6_REPLY) - return 0; - - r = dhcp6_lease_new(&lease); - if (r < 0) - return -ENOMEM; - - r = client_parse_message(client, reply, len, lease); - if (r < 0) - return r; - - if (client->state == DHCP6_STATE_SOLICITATION) { - r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); - if (r < 0) - return r; - - if (!rapid_commit) - return 0; - } - - client_set_lease(client, lease); - lease = NULL; - - return DHCP6_STATE_BOUND; -} - -static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t pref_advertise = 0, pref_lease = 0; - int r; - - if (advertise->type != DHCP6_ADVERTISE) - return 0; - - r = dhcp6_lease_new(&lease); - if (r < 0) - return r; - - r = client_parse_message(client, advertise, len, lease); - if (r < 0) - return r; - - r = dhcp6_lease_get_preference(lease, &pref_advertise); - if (r < 0) - return r; - - r = dhcp6_lease_get_preference(client->lease, &pref_lease); - - if (r < 0 || pref_advertise > pref_lease) { - client_set_lease(client, lease); - lease = NULL; - r = 0; - } - - if (pref_advertise == 255 || client->retransmit_count > 1) - r = DHCP6_STATE_REQUEST; - - return r; -} - -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 = NULL; - ssize_t buflen, len; - int r = 0; - - assert(s); - assert(client); - assert(client->event); - - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return buflen; - - message = malloc(buflen); - if (!message) - return -ENOMEM; - - len = recv(fd, message, buflen, 0); - if (len < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); - - } - if ((size_t) len < sizeof(DHCP6Message)) { - log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring"); - return 0; - } - - switch(message->type) { - case DHCP6_SOLICIT: - case DHCP6_REQUEST: - case DHCP6_CONFIRM: - case DHCP6_RENEW: - case DHCP6_REBIND: - case DHCP6_RELEASE: - case DHCP6_DECLINE: - case DHCP6_INFORMATION_REQUEST: - case DHCP6_RELAY_FORW: - case DHCP6_RELAY_REPL: - return 0; - - case DHCP6_ADVERTISE: - case DHCP6_REPLY: - case DHCP6_RECONFIGURE: - break; - - default: - log_dhcp6_client(client, "Unknown message type %d", message->type); - return 0; - } - - if (client->transaction_id != (message->transaction_id & - htobe32(0x00ffffff))) - return 0; - - switch (client->state) { - case DHCP6_STATE_INFORMATION_REQUEST: - r = client_receive_reply(client, message, len); - if (r < 0) - return 0; - - client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - client_start(client, DHCP6_STATE_STOPPED); - - break; - - case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len); - - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - - break; - } - - /* fall through for Soliciation Rapid Commit option check */ - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - r = client_receive_reply(client, message, len); - if (r < 0) - return 0; - - if (r == DHCP6_STATE_BOUND) { - - r = client_start(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - } - - break; - - case DHCP6_STATE_BOUND: - - break; - - case DHCP6_STATE_STOPPED: - return 0; - } - - if (r >= 0) - log_dhcp6_client(client, "Recv %s", - dhcp6_message_type_to_string(message->type)); - - return 0; -} - -static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { - int r; - usec_t timeout, time_now; - char time_string[FORMAT_TIMESPAN_MAX]; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->state != state, -EINVAL); - - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->retransmit_time = 0; - client->retransmit_count = 0; - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - switch (state) { - case DHCP6_STATE_STOPPED: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - client->state = DHCP6_STATE_STOPPED; - - return 0; - } - - /* fall through */ - case DHCP6_STATE_SOLICITATION: - client->state = DHCP6_STATE_SOLICITATION; - - break; - - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - client->state = state; - - break; - - case DHCP6_STATE_BOUND: - - if (client->lease->ia.lifetime_t1 == 0xffffffff || - client->lease->ia.lifetime_t2 == 0xffffffff) { - - log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.lifetime_t1), - be32toh(client->lease->ia.lifetime_t2)); - - return 0; - } - - 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, USEC_PER_SEC)); - - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t1, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t1, - client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->lease->ia.timeout_t1, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); - if (r < 0) - return r; - - 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, USEC_PER_SEC)); - - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t2, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t2, - client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->lease->ia.timeout_t2, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); - if (r < 0) - return r; - - client->state = state; - - return 0; - } - - client->transaction_id = random_u32() & htobe32(0x00ffffff); - client->transaction_start = time_now; - - r = sd_event_add_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, - client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); - if (r < 0) - return r; - - return 0; -} - -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) { - enum DHCP6State state = DHCP6_STATE_SOLICITATION; - int r = 0; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 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 -EBUSY; - - r = client_reset(client); - if (r < 0) - return r; - - r = client_ensure_iaid(client); - if (r < 0) - return r; - - r = client_ensure_duid(client); - if (r < 0) - return r; - - r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); - if (r < 0) { - _cleanup_free_ char *p = NULL; - - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); - return log_dhcp6_client_errno(client, r, - "Failed to bind to UDP socket at address %s: %m", strna(p)); - } - - client->fd = r; - - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; - - if (client->information_request) - state = DHCP6_STATE_INFORMATION_REQUEST; - - log_dhcp6_client(client, "Started in %s mode", - client->information_request? "Information request": - "Managed"); - - return client_start(client, state); - -error: - client_reset(client); - return r; -} - -int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { - int r; - - assert_return(client, -EINVAL); - assert_return(!client->event, -EBUSY); - - if (event) - client->event = sd_event_ref(event); - else { - r = sd_event_default(&client->event); - if (r < 0) - return 0; - } - - client->event_priority = priority; - - return 0; -} - -int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { - assert_return(client, -EINVAL); - - client->event = sd_event_unref(client->event); - - return 0; -} - -sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { - assert_return(client, NULL); - - return client->event; -} - -sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; - - if (client->n_ref > 0) - return NULL; - - client_reset(client); - - sd_dhcp6_client_detach_event(client); - - free(client->req_opts); - free(client); - - return NULL; -} - -int sd_dhcp6_client_new(sd_dhcp6_client **ret) { - _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; - size_t t; - - assert_return(ret, -EINVAL); - - client = new0(sd_dhcp6_client, 1); - if (!client) - return -ENOMEM; - - client->n_ref = 1; - client->ia_na.type = SD_DHCP6_OPTION_IA_NA; - client->ifindex = -1; - client->fd = -1; - - client->req_opts_len = ELEMENTSOF(default_req_opts); - client->req_opts = new0(be16_t, client->req_opts_len); - if (!client->req_opts) - return -ENOMEM; - - for (t = 0; t < client->req_opts_len; t++) - client->req_opts[t] = htobe16(default_req_opts[t]); - - *ret = client; - client = NULL; - - return 0; -} diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c deleted file mode 100644 index 5c10a6326a..0000000000 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ /dev/null @@ -1,410 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - 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 . -***/ - -#include - -#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); - - ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1); - ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2); - - return 0; -} - -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { - DHCP6Address *addr; - uint32_t valid = 0, t; - - assert_return(ia, -EINVAL); - assert_return(expire, -EINVAL); - - LIST_FOREACH(addresses, addr, ia->addresses) { - t = be32toh(addr->iaaddr.lifetime_valid); - if (valid < t) - valid = t; - } - - t = be32toh(ia->lifetime_t2); - if (t > valid) - return -EINVAL; - - *expire = valid - t; - - return 0; -} - -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { - DHCP6Address *address; - - if (!ia) - return NULL; - - dhcp6_lease_clear_timers(ia); - - while (ia->addresses) { - address = ia->addresses; - - LIST_REMOVE(addresses, ia->addresses, address); - - free(address); - } - - return NULL; -} - -int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, - size_t len) { - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - - free(lease->serverid); - - lease->serverid = memdup(id, len); - if (!lease->serverid) - return -EINVAL; - - lease->serverid_len = len; - - return 0; -} - -int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) { - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len, -EINVAL); - - *id = lease->serverid; - *len = lease->serverid_len; - - return 0; -} - -int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { - assert_return(lease, -EINVAL); - - lease->preference = preference; - - return 0; -} - -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(preference, -EINVAL); - - if (!lease) - return -EINVAL; - - *preference = lease->preference; - - return 0; -} - -int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { - assert_return(lease, -EINVAL); - - lease->rapid_commit = true; - - return 0; -} - -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { - assert_return(lease, -EINVAL); - assert_return(rapid_commit, -EINVAL); - - *rapid_commit = lease->rapid_commit; - - return 0; -} - -int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { - assert_return(lease, -EINVAL); - assert_return(iaid, -EINVAL); - - *iaid = lease->ia.id; - - return 0; -} - -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); - - if (!lease->addr_iter) - return -ENOMSG; - - memcpy(addr, &lease->addr_iter->iaaddr.address, - sizeof(struct in6_addr)); - *lifetime_preferred = - be32toh(lease->addr_iter->iaaddr.lifetime_preferred); - *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); - - lease->addr_iter = lease->addr_iter->addresses_next; - - return 0; -} - -void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { - if (lease) - lease->addr_iter = lease->ia.addresses; -} - -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); - - if (!optlen) - return 0; - - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, - lease->dns_count, - &lease->dns_allocated); - if (r < 0) { - log_dhcp6_client(client, "Invalid DNS server option: %s", - strerror(-r)); - - return r; - } - - lease->dns_count = r; - - return 0; -} - -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { - assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); - - if (lease->dns_count) { - *addrs = lease->dns; - return lease->dns_count; - } - - return -ENOENT; -} - -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) { - int r; - char **domains; - - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); - - if (!optlen) - return 0; - - r = dhcp6_option_parse_domainname(optval, optlen, &domains); - if (r < 0) - return 0; - - free(lease->domains); - lease->domains = domains; - lease->domains_count = r; - - return r; -} - -int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { - assert_return(lease, -EINVAL); - assert_return(domains, -EINVAL); - - if (lease->domains_count) { - *domains = lease->domains; - return lease->domains_count; - } - - return -ENOENT; -} - -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - uint16_t subopt; - size_t sublen; - uint8_t *subval; - - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); - - lease->ntp = mfree(lease->ntp); - lease->ntp_count = 0; - lease->ntp_allocated = 0; - - while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, - &subval)) >= 0) { - int s; - char **servers; - - switch(subopt) { - case DHCP6_NTP_SUBOPTION_SRV_ADDR: - case DHCP6_NTP_SUBOPTION_MC_ADDR: - if (sublen != 16) - return 0; - - s = dhcp6_option_parse_ip6addrs(subval, sublen, - &lease->ntp, - lease->ntp_count, - &lease->ntp_allocated); - if (s < 0) - return s; - - lease->ntp_count = s; - - break; - - case DHCP6_NTP_SUBOPTION_SRV_FQDN: - r = dhcp6_option_parse_domainname(subval, sublen, - &servers); - if (r < 0) - return 0; - - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); - lease->ntp_fqdn = servers; - lease->ntp_fqdn_count = r; - - break; - } - } - - if (r != -ENOMSG) - return r; - - return 0; -} - -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); - - if (!optlen) - return 0; - - if (lease->ntp || lease->ntp_fqdn) { - log_dhcp6_client(client, "NTP information already provided"); - - return 0; - } - - log_dhcp6_client(client, "Using deprecated SNTP information"); - - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, - lease->ntp_count, - &lease->ntp_allocated); - if (r < 0) { - log_dhcp6_client(client, "Invalid SNTP server option: %s", - strerror(-r)); - - return r; - } - - lease->ntp_count = r; - - return 0; -} - -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs) { - assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); - - if (lease->ntp_count) { - *addrs = lease->ntp; - return lease->ntp_count; - } - - return -ENOENT; -} - -int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { - assert_return(lease, -EINVAL); - assert_return(ntp_fqdn, -EINVAL); - - if (lease->ntp_fqdn_count) { - *ntp_fqdn = lease->ntp_fqdn; - return lease->ntp_fqdn_count; - } - - return -ENOENT; -} - -sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; - - free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - - free(lease->dns); - - lease->domains = strv_free(lease->domains); - - free(lease->ntp); - - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); - free(lease); - - return NULL; -} - -int dhcp6_lease_new(sd_dhcp6_lease **ret) { - sd_dhcp6_lease *lease; - - lease = new0(sd_dhcp6_lease, 1); - if (!lease) - return -ENOMEM; - - lease->n_ref = 1; - - LIST_HEAD_INIT(lease->ia.addresses); - - *ret = lease; - return 0; -} diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c deleted file mode 100644 index 662885840f..0000000000 --- a/src/libsystemd-network/sd-ipv4acd.c +++ /dev/null @@ -1,526 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-ipv4acd.h" - -#include "alloc-util.h" -#include "arp-util.h" -#include "ether-addr-util.h" -#include "fd-util.h" -#include "in-addr-util.h" -#include "list.h" -#include "random-util.h" -#include "siphash24.h" -#include "string-util.h" -#include "util.h" - -/* Constants from the RFC */ -#define PROBE_WAIT_USEC (1U * USEC_PER_SEC) -#define PROBE_NUM 3U -#define PROBE_MIN_USEC (1U * USEC_PER_SEC) -#define PROBE_MAX_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC) -#define ANNOUNCE_NUM 2U -#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC) -#define MAX_CONFLICTS 10U -#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC) -#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC) - -typedef enum IPv4ACDState { - IPV4ACD_STATE_INIT, - IPV4ACD_STATE_STARTED, - IPV4ACD_STATE_WAITING_PROBE, - IPV4ACD_STATE_PROBING, - IPV4ACD_STATE_WAITING_ANNOUNCE, - IPV4ACD_STATE_ANNOUNCING, - IPV4ACD_STATE_RUNNING, - _IPV4ACD_STATE_MAX, - _IPV4ACD_STATE_INVALID = -1 -} IPv4ACDState; - -struct sd_ipv4acd { - unsigned n_ref; - - IPv4ACDState state; - int ifindex; - int fd; - - unsigned n_iteration; - unsigned n_conflict; - - sd_event_source *receive_message_event_source; - sd_event_source *timer_event_source; - - usec_t defend_window; - be32_t address; - - /* External */ - struct ether_addr mac_addr; - - sd_event *event; - int event_priority; - sd_ipv4acd_callback_t callback; - void* userdata; -}; - -#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__) -#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__) - -static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) { - assert(acd); - assert(st < _IPV4ACD_STATE_MAX); - - if (st == acd->state && !reset_counter) - acd->n_iteration++; - else { - acd->state = st; - acd->n_iteration = 0; - } -} - -static void ipv4acd_reset(sd_ipv4acd *acd) { - assert(acd); - - acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); - acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source); - - acd->fd = safe_close(acd->fd); - - ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true); -} - -sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref++; - - return acd; -} - -sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref--; - - if (acd->n_ref > 0) - return NULL; - - ipv4acd_reset(acd); - sd_ipv4acd_detach_event(acd); - - free(acd); - - return NULL; -} - -int sd_ipv4acd_new(sd_ipv4acd **ret) { - _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL; - - assert_return(ret, -EINVAL); - - acd = new0(sd_ipv4acd, 1); - if (!acd) - return -ENOMEM; - - acd->n_ref = 1; - acd->state = IPV4ACD_STATE_INIT; - acd->ifindex = -1; - acd->fd = -1; - - *ret = acd; - acd = NULL; - - return 0; -} - -static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) { - assert(acd); - - if (!acd->callback) - return; - - acd->callback(acd, event, acd->userdata); -} - -int sd_ipv4acd_stop(sd_ipv4acd *acd) { - assert_return(acd, -EINVAL); - - ipv4acd_reset(acd); - - log_ipv4acd(acd, "STOPPED"); - - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP); - - return 0; -} - -static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); - -static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) { - _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; - usec_t next_timeout, time_now; - int r; - - assert(acd); - - next_timeout = usec; - - if (random_usec > 0) - next_timeout += (usec_t) random_u64() % random_usec; - - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); - - r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd); - if (r < 0) - return r; - - r = sd_event_source_set_priority(timer, acd->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(timer, "ipv4acd-timer"); - - sd_event_source_unref(acd->timer_event_source); - acd->timer_event_source = timer; - timer = NULL; - - return 0; -} - -static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { - assert(acd); - assert(arp); - - /* see the BPF */ - if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0) - return true; - - /* the TPA matched instead of the SPA, this is not a conflict */ - return false; -} - -static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - sd_ipv4acd *acd = userdata; - int r = 0; - - assert(acd); - - switch (acd->state) { - - case IPV4ACD_STATE_STARTED: - ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true); - - if (acd->n_conflict >= MAX_CONFLICTS) { - char ts[FORMAT_TIMESPAN_MAX]; - log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0)); - - r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); - if (r < 0) - goto fail; - - acd->n_conflict = 0; - } else { - r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); - if (r < 0) - goto fail; - } - - break; - - case IPV4ACD_STATE_WAITING_PROBE: - case IPV4ACD_STATE_PROBING: - /* Send a probe */ - r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m"); - goto fail; - } else { - _cleanup_free_ char *address = NULL; - union in_addr_union addr = { .in.s_addr = acd->address }; - - (void) in_addr_to_string(AF_INET, &addr, &address); - log_ipv4acd(acd, "Probing %s", strna(address)); - } - - if (acd->n_iteration < PROBE_NUM - 2) { - ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false); - - r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC)); - if (r < 0) - goto fail; - } else { - ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true); - - r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0); - if (r < 0) - goto fail; - } - - break; - - case IPV4ACD_STATE_ANNOUNCING: - if (acd->n_iteration >= ANNOUNCE_NUM - 1) { - ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false); - break; - } - - /* fall through */ - - case IPV4ACD_STATE_WAITING_ANNOUNCE: - /* Send announcement packet */ - r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); - goto fail; - } else - log_ipv4acd(acd, "ANNOUNCE"); - - ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false); - - r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0); - if (r < 0) - goto fail; - - if (acd->n_iteration == 0) { - acd->n_conflict = 0; - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND); - } - - break; - - default: - assert_not_reached("Invalid state."); - } - - return 0; - -fail: - sd_ipv4acd_stop(acd); - return 0; -} - -static void ipv4acd_on_conflict(sd_ipv4acd *acd) { - _cleanup_free_ char *address = NULL; - union in_addr_union addr = { .in.s_addr = acd->address }; - - assert(acd); - - acd->n_conflict++; - - (void) in_addr_to_string(AF_INET, &addr, &address); - log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict); - - ipv4acd_reset(acd); - ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT); -} - -static int ipv4acd_on_packet( - sd_event_source *s, - int fd, - uint32_t revents, - void *userdata) { - - sd_ipv4acd *acd = userdata; - struct ether_arp packet; - ssize_t n; - int r; - - assert(s); - assert(acd); - assert(fd >= 0); - - n = recv(fd, &packet, sizeof(struct ether_arp), 0); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m"); - goto fail; - } - if ((size_t) n != sizeof(struct ether_arp)) { - log_ipv4acd(acd, "Ignoring too short ARP packet."); - return 0; - } - - switch (acd->state) { - - case IPV4ACD_STATE_ANNOUNCING: - case IPV4ACD_STATE_RUNNING: - - if (ipv4acd_arp_conflict(acd, &packet)) { - usec_t ts; - - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); - - /* Defend address */ - if (ts > acd->defend_window) { - acd->defend_window = ts + DEFEND_INTERVAL_USEC; - r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); - if (r < 0) { - log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); - goto fail; - } else - log_ipv4acd(acd, "DEFEND"); - - } else - ipv4acd_on_conflict(acd); - } - break; - - case IPV4ACD_STATE_WAITING_PROBE: - case IPV4ACD_STATE_PROBING: - case IPV4ACD_STATE_WAITING_ANNOUNCE: - /* BPF ensures this packet indicates a conflict */ - ipv4acd_on_conflict(acd); - break; - - default: - assert_not_reached("Invalid state."); - } - - return 0; - -fail: - sd_ipv4acd_stop(acd); - return 0; -} - -int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) { - assert_return(acd, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - acd->ifindex = ifindex; - - return 0; -} - -int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) { - assert_return(acd, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - acd->mac_addr = *addr; - - return 0; -} - -int sd_ipv4acd_detach_event(sd_ipv4acd *acd) { - assert_return(acd, -EINVAL); - - acd->event = sd_event_unref(acd->event); - - return 0; -} - -int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) { - int r; - - assert_return(acd, -EINVAL); - assert_return(!acd->event, -EBUSY); - - if (event) - acd->event = sd_event_ref(event); - else { - r = sd_event_default(&acd->event); - if (r < 0) - return r; - } - - acd->event_priority = priority; - - return 0; -} - -int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) { - assert_return(acd, -EINVAL); - - acd->callback = cb; - acd->userdata = userdata; - - return 0; -} - -int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { - assert_return(acd, -EINVAL); - assert_return(address, -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - acd->address = address->s_addr; - - return 0; -} - -int sd_ipv4acd_is_running(sd_ipv4acd *acd) { - assert_return(acd, false); - - return acd->state != IPV4ACD_STATE_INIT; -} - -int sd_ipv4acd_start(sd_ipv4acd *acd) { - int r; - - assert_return(acd, -EINVAL); - assert_return(acd->event, -EINVAL); - assert_return(acd->ifindex > 0, -EINVAL); - assert_return(acd->address != 0, -EINVAL); - assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL); - assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); - - r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr); - if (r < 0) - return r; - - safe_close(acd->fd); - acd->fd = r; - acd->defend_window = 0; - acd->n_conflict = 0; - - r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message"); - - r = ipv4acd_set_next_wakeup(acd, 0, 0); - if (r < 0) - goto fail; - - ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true); - return 0; - -fail: - ipv4acd_reset(acd); - return r; -} diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c deleted file mode 100644 index 5603a533a5..0000000000 --- a/src/libsystemd-network/sd-ipv4ll.c +++ /dev/null @@ -1,346 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-ipv4acd.h" -#include "sd-ipv4ll.h" - -#include "alloc-util.h" -#include "ether-addr-util.h" -#include "in-addr-util.h" -#include "list.h" -#include "random-util.h" -#include "siphash24.h" -#include "sparse-endian.h" -#include "string-util.h" -#include "util.h" - -#define IPV4LL_NETWORK UINT32_C(0xA9FE0000) -#define IPV4LL_NETMASK UINT32_C(0xFFFF0000) - -#define IPV4LL_DONT_DESTROY(ll) \ - _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) - -struct sd_ipv4ll { - unsigned n_ref; - - sd_ipv4acd *acd; - - be32_t address; /* the address pushed to ACD */ - struct ether_addr mac; - - struct { - le64_t value; - le64_t generation; - } seed; - bool seed_set; - - /* External */ - be32_t claimed_address; - - sd_ipv4ll_callback_t callback; - void* userdata; -}; - -#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__) -#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__) - -static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); - -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref++; - - return ll; -} - -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref--; - - if (ll->n_ref > 0) - return NULL; - - sd_ipv4acd_unref(ll->acd); - free(ll); - - return NULL; -} - -int sd_ipv4ll_new(sd_ipv4ll **ret) { - _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; - int r; - - assert_return(ret, -EINVAL); - - ll = new0(sd_ipv4ll, 1); - if (!ll) - return -ENOMEM; - - ll->n_ref = 1; - - r = sd_ipv4acd_new(&ll->acd); - if (r < 0) - return r; - - r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll); - if (r < 0) - return r; - - *ret = ll; - ll = NULL; - - return 0; -} - -int sd_ipv4ll_stop(sd_ipv4ll *ll) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_stop(ll->acd); -} - -int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) { - assert_return(ll, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - return sd_ipv4acd_set_ifindex(ll->acd, ifindex); -} - -int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { - int r; - - assert_return(ll, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - r = sd_ipv4acd_set_mac(ll->acd, addr); - if (r < 0) - return r; - - ll->mac = *addr; - return 0; -} - -int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_detach_event(ll->acd); -} - -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) { - assert_return(ll, -EINVAL); - - return sd_ipv4acd_attach_event(ll->acd, event, priority); -} - -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) { - assert_return(ll, -EINVAL); - - ll->callback = cb; - ll->userdata = userdata; - - return 0; -} - -int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { - assert_return(ll, -EINVAL); - assert_return(address, -EINVAL); - - if (ll->claimed_address == 0) - return -ENOENT; - - address->s_addr = ll->claimed_address; - - return 0; -} - -int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) { - assert_return(ll, -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - ll->seed.value = htole64(seed); - ll->seed_set = true; - - return 0; -} - -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) { - assert(address); - - if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) - return false; - - return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); -} - -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; -} - -#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b) - -static int ipv4ll_pick_address(sd_ipv4ll *ll) { - _cleanup_free_ char *address = NULL; - be32_t addr; - - assert(ll); - - do { - uint64_t h; - - h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes); - - /* Increase the generation counter by one */ - ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1); - - addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK); - } while (addr == ll->address || - IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U)); - - (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address); - log_ipv4ll(ll, "Picked new IP address %s.", strna(address)); - - return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); -} - -#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) - -int sd_ipv4ll_start(sd_ipv4ll *ll) { - int r; - bool picked_address = false; - - assert_return(ll, -EINVAL); - assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); - - /* If no random seed is set, generate some from the MAC address */ - if (!ll->seed_set) - ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); - - /* Restart the generation counter. */ - ll->seed.generation = 0; - - if (ll->address == 0) { - r = ipv4ll_pick_address(ll); - if (r < 0) - return r; - - picked_address = true; - } - - r = sd_ipv4acd_start(ll->acd); - if (r < 0) { - - /* We couldn't start? If so, let's forget the picked address again, the user might make a change and - * retry, and we want the new data to take effect when picking an address. */ - if (picked_address) - ll->address = 0; - - return r; - } - - return 0; -} - -static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { - assert(ll); - - if (ll->callback) - ll->callback(ll, event, ll->userdata); -} - -void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { - sd_ipv4ll *ll = userdata; - IPV4LL_DONT_DESTROY(ll); - int r; - - assert(acd); - assert(ll); - - switch (event) { - - case SD_IPV4ACD_EVENT_STOP: - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); - ll->claimed_address = 0; - break; - - case SD_IPV4ACD_EVENT_BIND: - ll->claimed_address = ll->address; - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); - break; - - case SD_IPV4ACD_EVENT_CONFLICT: - /* if an address was already bound we must call up to the - user to handle this, otherwise we just try again */ - if (ll->claimed_address != 0) { - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT); - - ll->claimed_address = 0; - } else { - r = ipv4ll_pick_address(ll); - if (r < 0) - goto error; - - r = sd_ipv4acd_start(ll->acd); - if (r < 0) - goto error; - } - - break; - - default: - assert_not_reached("Invalid IPv4ACD event."); - } - - return; - -error: - ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); -} diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c deleted file mode 100644 index 0bd1e66aa0..0000000000 --- a/src/libsystemd-network/sd-lldp.c +++ /dev/null @@ -1,539 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-lldp.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "lldp-internal.h" -#include "lldp-neighbor.h" -#include "lldp-network.h" -#include "socket-util.h" -#include "ether-addr-util.h" - -#define LLDP_DEFAULT_NEIGHBORS_MAX 128U - -static void lldp_flush_neighbors(sd_lldp *lldp) { - sd_lldp_neighbor *n; - - assert(lldp); - - while ((n = hashmap_first(lldp->neighbor_by_id))) - lldp_neighbor_unlink(n); -} - -static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { - assert(lldp); - - log_lldp("Invoking callback for '%c'.", event); - - if (!lldp->callback) - return; - - lldp->callback(lldp, event, n, lldp->userdata); -} - -static int lldp_make_space(sd_lldp *lldp, size_t extra) { - usec_t t = USEC_INFINITY; - bool changed = false; - - assert(lldp); - - /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries - * are free. */ - - for (;;) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - - n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) - break; - - sd_lldp_neighbor_ref(n); - - if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) - goto remove_one; - - if (t == USEC_INFINITY) - t = now(clock_boottime_or_monotonic()); - - if (n->until > t) - break; - - remove_one: - lldp_neighbor_unlink(n); - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); - changed = true; - } - - return changed; -} - -static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { - assert(lldp); - assert(n); - - /* Don't keep data with a zero TTL */ - if (n->ttl <= 0) - return false; - - /* Filter out data from the filter address */ - if (!ether_addr_is_null(&lldp->filter_address) && - ether_addr_equal(&lldp->filter_address, &n->source_address)) - return false; - - /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with - * no caps field set. */ - if (n->has_capabilities && - (n->enabled_capabilities & lldp->capability_mask) == 0) - return false; - - /* Keep everything else */ - return true; -} - -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); - -static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; - bool keep; - int r; - - assert(lldp); - assert(n); - assert(!n->lldp); - - keep = lldp_keep_neighbor(lldp, n); - - /* First retrieve the old entry for this MSAP */ - old = hashmap_get(lldp->neighbor_by_id, &n->id); - if (old) { - sd_lldp_neighbor_ref(old); - - if (!keep) { - lldp_neighbor_unlink(old); - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); - return 0; - } - - if (lldp_neighbor_equal(n, old)) { - /* Is this equal, then restart the TTL counter, but don't do anyting else. */ - old->timestamp = n->timestamp; - lldp_start_timer(lldp, old); - lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); - return 0; - } - - /* Data changed, remove the old entry, and add a new one */ - lldp_neighbor_unlink(old); - - } else if (!keep) - return 0; - - /* Then, make room for at least one new neighbor */ - lldp_make_space(lldp, 1); - - r = hashmap_put(lldp->neighbor_by_id, &n->id, n); - if (r < 0) - goto finish; - - r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); - if (r < 0) { - assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); - goto finish; - } - - n->lldp = lldp; - - lldp_start_timer(lldp, n); - lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); - - return 1; - -finish: - if (old) - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); - - return r; -} - -static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { - int r; - - assert(lldp); - assert(n); - - r = lldp_neighbor_parse(n); - if (r == -EBADMSG) /* Ignore bad messages */ - return 0; - if (r < 0) - return r; - - r = lldp_add_neighbor(lldp, n); - if (r < 0) { - log_lldp_errno(r, "Failed to add datagram. Ignoring."); - return 0; - } - - log_lldp("Successfully processed LLDP datagram."); - return 0; -} - -static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - ssize_t space, length; - sd_lldp *lldp = userdata; - struct timespec ts; - - assert(fd >= 0); - assert(lldp); - - space = next_datagram_size_fd(fd); - if (space < 0) - return log_lldp_errno(space, "Failed to determine datagram size to read: %m"); - - n = lldp_neighbor_new(space); - if (!n) - return -ENOMEM; - - length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); - if (length < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); - } - - if ((size_t) length != n->raw_size) { - log_lldp("Packet size mismatch."); - return -EINVAL; - } - - /* Try to get the timestamp of this packet if it is known */ - if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) - triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); - else - triple_timestamp_get(&n->timestamp); - - return lldp_handle_datagram(lldp, n); -} - -static void lldp_reset(sd_lldp *lldp) { - assert(lldp); - - lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); - lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); - lldp->fd = safe_close(lldp->fd); -} - -_public_ int sd_lldp_start(sd_lldp *lldp) { - int r; - - assert_return(lldp, -EINVAL); - assert_return(lldp->event, -EINVAL); - assert_return(lldp->ifindex > 0, -EINVAL); - - if (lldp->fd >= 0) - return 0; - - assert(!lldp->io_event_source); - - lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); - if (lldp->fd < 0) - return lldp->fd; - - r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); - - log_lldp("Started LLDP client"); - return 1; - -fail: - lldp_reset(lldp); - return r; -} - -_public_ int sd_lldp_stop(sd_lldp *lldp) { - assert_return(lldp, -EINVAL); - - if (lldp->fd < 0) - return 0; - - log_lldp("Stopping LLDP client"); - - lldp_reset(lldp); - lldp_flush_neighbors(lldp); - - return 1; -} - -_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { - int r; - - assert_return(lldp, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - assert_return(!lldp->event, -EBUSY); - - if (event) - lldp->event = sd_event_ref(event); - else { - r = sd_event_default(&lldp->event); - if (r < 0) - return r; - } - - lldp->event_priority = priority; - - return 0; -} - -_public_ int sd_lldp_detach_event(sd_lldp *lldp) { - - assert_return(lldp, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - - lldp->event = sd_event_unref(lldp->event); - return 0; -} - -_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) { - assert_return(lldp, NULL); - - return lldp->event; -} - -_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { - assert_return(lldp, -EINVAL); - - lldp->callback = cb; - lldp->userdata = userdata; - - return 0; -} - -_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { - assert_return(lldp, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - - lldp->ifindex = ifindex; - return 0; -} - -_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref++; - - return lldp; -} - -_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref --; - - if (lldp->n_ref > 0) - return NULL; - - lldp_reset(lldp); - sd_lldp_detach_event(lldp); - lldp_flush_neighbors(lldp); - - hashmap_free(lldp->neighbor_by_id); - prioq_free(lldp->neighbor_by_expiry); - free(lldp); - - return NULL; -} - -_public_ int sd_lldp_new(sd_lldp **ret) { - _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; - int r; - - assert_return(ret, -EINVAL); - - lldp = new0(sd_lldp, 1); - if (!lldp) - return -ENOMEM; - - lldp->n_ref = 1; - lldp->fd = -1; - lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; - lldp->capability_mask = (uint16_t) -1; - - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); - if (!lldp->neighbor_by_id) - return -ENOMEM; - - r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); - if (r < 0) - return r; - - *ret = lldp; - lldp = NULL; - - return 0; -} - -static int neighbor_compare_func(const void *a, const void *b) { - const sd_lldp_neighbor * const*x = a, * const *y = b; - - return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id); -} - -static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { - sd_lldp *lldp = userdata; - int r, q; - - r = lldp_make_space(lldp, 0); - if (r < 0) - return log_lldp_errno(r, "Failed to make space: %m"); - - q = lldp_start_timer(lldp, NULL); - if (q < 0) - return log_lldp_errno(q, "Failed to restart timer: %m"); - - return 0; -} - -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { - sd_lldp_neighbor *n; - int r; - - assert(lldp); - - if (neighbor) - lldp_neighbor_start_ttl(neighbor); - - n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) { - - if (lldp->timer_event_source) - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF); - - return 0; - } - - if (lldp->timer_event_source) { - r = sd_event_source_set_time(lldp->timer_event_source, n->until); - if (r < 0) - return r; - - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT); - } - - if (!lldp->event) - return 0; - - r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp); - if (r < 0) - return r; - - r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer"); - return 0; -} - -_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { - sd_lldp_neighbor **l = NULL, *n; - Iterator i; - int k = 0, r; - - assert_return(lldp, -EINVAL); - assert_return(ret, -EINVAL); - - if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ - *ret = NULL; - return 0; - } - - l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); - if (!l) - return -ENOMEM; - - r = lldp_start_timer(lldp, NULL); - if (r < 0) { - free(l); - return r; - } - - HASHMAP_FOREACH(n, lldp->neighbor_by_id, i) - l[k++] = sd_lldp_neighbor_ref(n); - - assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); - - /* Return things in a stable order */ - qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func); - *ret = l; - - return k; -} - -_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { - assert_return(lldp, -EINVAL); - assert_return(m <= 0, -EINVAL); - - lldp->neighbors_max = m; - lldp_make_space(lldp, 0); - - return 0; -} - -_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { - assert_return(lldp, -EINVAL); - assert_return(mask != 0, -EINVAL); - - lldp->capability_mask = mask; - - return 0; -} - -_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { - assert_return(lldp, -EINVAL); - - /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so - * that our own can be filtered out here. */ - - if (addr) - lldp->filter_address = *addr; - else - zero(lldp->filter_address); - - return 0; -} diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c deleted file mode 100644 index 07b0d7f704..0000000000 --- a/src/libsystemd-network/sd-ndisc.c +++ /dev/null @@ -1,422 +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 . -***/ - -#include -#include - -#include "sd-ndisc.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "icmp6-util.h" -#include "in-addr-util.h" -#include "ndisc-internal.h" -#include "ndisc-router.h" -#include "socket-util.h" -#include "string-util.h" -#include "util.h" - -#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC) -#define NDISC_MAX_ROUTER_SOLICITATIONS 3U - -static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) { - assert(ndisc); - - log_ndisc("Invoking callback for '%c'.", event); - - if (!ndisc->callback) - return; - - ndisc->callback(ndisc, event, rt, ndisc->userdata); -} - -_public_ int sd_ndisc_set_callback( - sd_ndisc *nd, - sd_ndisc_callback_t callback, - void *userdata) { - - assert_return(nd, -EINVAL); - - nd->callback = callback; - nd->userdata = userdata; - - return 0; -} - -_public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) { - assert_return(nd, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(nd->fd < 0, -EBUSY); - - nd->ifindex = ifindex; - return 0; -} - -_public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) { - assert_return(nd, -EINVAL); - - if (mac_addr) - nd->mac_addr = *mac_addr; - else - zero(nd->mac_addr); - - return 0; -} - -_public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) { - int r; - - assert_return(nd, -EINVAL); - assert_return(nd->fd < 0, -EBUSY); - 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; -} - -_public_ int sd_ndisc_detach_event(sd_ndisc *nd) { - - assert_return(nd, -EINVAL); - assert_return(nd->fd < 0, -EBUSY); - - nd->event = sd_event_unref(nd->event); - return 0; -} - -_public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) { - assert_return(nd, NULL); - - return nd->event; -} - -_public_ 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_reset(sd_ndisc *nd) { - assert(nd); - - nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); - nd->recv_event_source = sd_event_source_unref(nd->recv_event_source); - nd->fd = safe_close(nd->fd); - - return 0; -} - -_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) { - - if (!nd) - return NULL; - - assert(nd->n_ref > 0); - nd->n_ref--; - - if (nd->n_ref > 0) - return NULL; - - ndisc_reset(nd); - sd_ndisc_detach_event(nd); - free(nd); - - return NULL; -} - -_public_ int sd_ndisc_new(sd_ndisc **ret) { - _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; - - assert_return(ret, -EINVAL); - - nd = new0(sd_ndisc, 1); - if (!nd) - return -ENOMEM; - - nd->n_ref = 1; - nd->fd = -1; - - *ret = nd; - nd = NULL; - - return 0; -} - -_public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) { - assert_return(nd, -EINVAL); - assert_return(mtu, -EINVAL); - - if (nd->mtu == 0) - return -ENODATA; - - *mtu = nd->mtu; - return 0; -} - -_public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) { - assert_return(nd, -EINVAL); - assert_return(ret, -EINVAL); - - if (nd->hop_limit == 0) - return -ENODATA; - - *ret = nd->hop_limit; - return 0; -} - -static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) { - int r; - - assert(nd); - assert(rt); - - r = ndisc_router_parse(rt); - if (r == -EBADMSG) /* Bad packet */ - return 0; - if (r < 0) - return 0; - - /* Update global variables we keep */ - if (rt->mtu > 0) - nd->mtu = rt->mtu; - if (rt->hop_limit > 0) - nd->hop_limit = rt->hop_limit; - - log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec", - rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none", - rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium", - rt->lifetime); - - ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt); - return 0; -} - -static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; - sd_ndisc *nd = userdata; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */ - CMSG_SPACE(sizeof(struct timeval))]; - } 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; - ssize_t len, buflen; - - assert(s); - assert(nd); - assert(nd->event); - - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m"); - - rt = ndisc_router_new(buflen); - if (!rt) - return -ENOMEM; - - iov.iov_base = NDISC_ROUTER_RAW(rt); - iov.iov_len = rt->raw_size; - - len = recvmsg(fd, &msg, MSG_DONTWAIT); - if (len < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m"); - } - - if ((size_t) len != rt->raw_size) { - log_ndisc("Packet size mismatch."); - return -EINVAL; - } - - if (msg.msg_namelen == sizeof(struct sockaddr_in6) && - sa.in6.sin6_family == AF_INET6) { - - if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) { - _cleanup_free_ char *addr = NULL; - - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr); - log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr)); - return 0; - } - - rt->address = sa.in6.sin6_addr; - - } else if (msg.msg_namelen > 0) { - log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen); - return -EINVAL; - } - - /* namelen == 0 only happens when running the test-suite over a socketpair */ - - 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("Received RA with invalid hop limit %d. Ignoring.", hops); - return 0; - } - } - - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SO_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg))); - } - - if (!triple_timestamp_is_set(&rt->timestamp)) - triple_timestamp_get(&rt->timestamp); - - nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); - - return ndisc_handle_datagram(nd, rt); -} - -static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - sd_ndisc *nd = userdata; - usec_t time_now, next_timeout; - int r; - - assert(s); - assert(nd); - assert(nd->event); - - if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { - nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); - ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL); - return 0; - } - - r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); - if (r < 0) { - log_ndisc_errno(r, "Error sending Router Solicitation: %m"); - goto fail; - } - - log_ndisc("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_source_set_time(nd->timeout_event_source, next_timeout); - if (r < 0) { - log_ndisc_errno(r, "Error updating timer: %m"); - goto fail; - } - - r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT); - if (r < 0) { - log_ndisc_errno(r, "Error reenabling timer: %m"); - goto fail; - } - - return 0; - -fail: - sd_ndisc_stop(nd); - return 0; -} - -_public_ int sd_ndisc_stop(sd_ndisc *nd) { - assert_return(nd, -EINVAL); - - if (nd->fd < 0) - return 0; - - log_ndisc("Stopping IPv6 Router Solicitation client"); - - ndisc_reset(nd); - return 1; -} - -_public_ int sd_ndisc_start(sd_ndisc *nd) { - int r; - - assert_return(nd, -EINVAL); - assert_return(nd->event, -EINVAL); - assert_return(nd->ifindex > 0, -EINVAL); - - if (nd->fd >= 0) - return 0; - - assert(!nd->recv_event_source); - assert(!nd->timeout_event_source); - - nd->fd = icmp6_bind_router_solicitation(nd->ifindex); - if (nd->fd < 0) - return nd->fd; - - r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message"); - - r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout"); - - log_ndisc("Started IPv6 Router Solicitation client"); - return 1; - -fail: - ndisc_reset(nd); - return r; -} diff --git a/src/libsystemd-network/src/Makefile b/src/libsystemd-network/src/Makefile new file mode 100644 index 0000000000..c636e3cab1 --- /dev/null +++ b/src/libsystemd-network/src/Makefile @@ -0,0 +1,83 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +noinst_LTLIBRARIES += \ + libsystemd-network.la + +libsystemd_network_la_CFLAGS = \ + $(KMOD_CFLAGS) + +libsystemd_network_la_SOURCES = \ + src/systemd/sd-dhcp-client.h \ + src/systemd/sd-dhcp-server.h \ + src/systemd/sd-dhcp-lease.h \ + src/systemd/sd-ipv4ll.h \ + src/systemd/sd-ipv4acd.h \ + src/systemd/sd-ndisc.h \ + src/systemd/sd-dhcp6-client.h \ + src/systemd/sd-dhcp6-lease.h \ + src/systemd/sd-lldp.h \ + src/libsystemd-network/sd-dhcp-client.c \ + src/libsystemd-network/sd-dhcp-server.c \ + src/libsystemd-network/dhcp-network.c \ + src/libsystemd-network/dhcp-option.c \ + src/libsystemd-network/dhcp-packet.c \ + src/libsystemd-network/dhcp-internal.h \ + src/libsystemd-network/dhcp-server-internal.h \ + src/libsystemd-network/dhcp-protocol.h \ + src/libsystemd-network/dhcp-lease-internal.h \ + src/libsystemd-network/sd-dhcp-lease.c \ + src/libsystemd-network/sd-ipv4ll.c \ + src/libsystemd-network/sd-ipv4acd.c \ + src/libsystemd-network/arp-util.h \ + src/libsystemd-network/arp-util.c \ + src/libsystemd-network/network-internal.c \ + src/libsystemd-network/network-internal.h \ + src/libsystemd-network/sd-ndisc.c \ + src/libsystemd-network/ndisc-internal.h \ + src/libsystemd-network/ndisc-router.h \ + src/libsystemd-network/ndisc-router.c \ + src/libsystemd-network/icmp6-util.h \ + src/libsystemd-network/icmp6-util.c \ + src/libsystemd-network/sd-dhcp6-client.c \ + src/libsystemd-network/dhcp6-internal.h \ + src/libsystemd-network/dhcp6-protocol.h \ + src/libsystemd-network/dhcp6-network.c \ + src/libsystemd-network/dhcp6-option.c \ + src/libsystemd-network/dhcp6-lease-internal.h \ + src/libsystemd-network/sd-dhcp6-lease.c \ + src/libsystemd-network/dhcp-identifier.h \ + src/libsystemd-network/dhcp-identifier.c \ + src/libsystemd-network/lldp-internal.h \ + src/libsystemd-network/lldp-network.h \ + src/libsystemd-network/lldp-network.c \ + src/libsystemd-network/lldp-neighbor.h \ + src/libsystemd-network/lldp-neighbor.c \ + src/libsystemd-network/sd-lldp.c + +libsystemd_network_la_LIBADD = \ + $(KMOD_LIBS) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd-network/src/arp-util.c b/src/libsystemd-network/src/arp-util.c new file mode 100644 index 0000000000..bf66a75b16 --- /dev/null +++ b/src/libsystemd-network/src/arp-util.c @@ -0,0 +1,155 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 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 . +***/ + +#include + +#include + +#include "basic/fd-util.h" +#include "basic/util.h" +#include "systemd-network/arp-util.h" + +int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Hardware Address must be different from our own */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + }; + struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter + }; + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + _cleanup_close_ int s = -1; + int r; + + assert(ifindex > 0); + + s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + r = bind(s, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +static int arp_send_packet(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha, + bool announce) { + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETH_P_ARP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + struct ether_arp arp = { + .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ + .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ + .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ + .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */ + .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ + }; + int r; + + assert(fd >= 0); + assert(pa != 0); + assert(ha); + + memcpy(&arp.arp_sha, ha, ETH_ALEN); + memcpy(&arp.arp_tpa, &pa, sizeof(pa)); + + if (announce) + memcpy(&arp.arp_spa, &pa, sizeof(pa)); + + r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + return 0; +} + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, false); +} + +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + return arp_send_packet(fd, ifindex, pa, ha, true); +} diff --git a/src/libsystemd-network/src/dhcp-identifier.c b/src/libsystemd-network/src/dhcp-identifier.c new file mode 100644 index 0000000000..3edba38bd8 --- /dev/null +++ b/src/libsystemd-network/src/dhcp-identifier.c @@ -0,0 +1,129 @@ +/*** + This file is part of systemd. + + Copyright (C) 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 . +***/ + +#include +#include + +#include "basic/siphash24.h" +#include "basic/sparse-endian.h" +#include "basic/virt.h" +#include "shared/udev-util.h" +#include "systemd-network/dhcp-identifier.h" +#include "systemd-network/dhcp6-protocol.h" +#include "systemd-network/network-internal.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_validate_duid_len(uint16_t duid_type, size_t duid_len) { + struct duid d; + + assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); + if (duid_len > MAX_DUID_LEN) + return -EINVAL; + + switch (duid_type) { + case DUID_TYPE_LLT: + if (duid_len <= sizeof(d.llt)) + return -EINVAL; + break; + case DUID_TYPE_EN: + if (duid_len != sizeof(d.en)) + return -EINVAL; + break; + case DUID_TYPE_LL: + if (duid_len <= sizeof(d.ll)) + return -EINVAL; + break; + case DUID_TYPE_UUID: + if (duid_len != sizeof(d.uuid)) + return -EINVAL; + break; + default: + /* accept unknown type in order to be forward compatible */ + break; + } + return 0; +} + +int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { + sd_id128_t machine_id; + uint64_t hash; + int r; + + assert(duid); + assert(len); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + unaligned_write_be16(&duid->type, DUID_TYPE_EN); + unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); + + *len = sizeof(duid->type) + sizeof(duid->en); + + /* a bit of snake-oil perhaps, but no need to expose the machine-id + 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 */ + _cleanup_udev_device_unref_ struct udev_device *device = NULL; + const char *name = NULL; + uint64_t id; + + if (detect_container() <= 0) { + /* not in a container, udev will be around */ + _cleanup_udev_unref_ struct udev *udev; + char ifindex_str[2 + DECIMAL_STR_MAX(int)]; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + sprintf(ifindex_str, "n%d", ifindex); + device = udev_device_new_from_device_id(udev, ifindex_str); + if (device) { + if (udev_device_get_is_initialized(device) <= 0) + /* not yet ready */ + return -EBUSY; + + name = net_get_name(device); + } + } + + if (name) + id = siphash24(name, strlen(name), HASH_KEY.bytes); + else + /* fall back to MAC address if no predictable name available */ + id = siphash24(mac, mac_len, HASH_KEY.bytes); + + id = htole64(id); + + /* fold into 32 bits */ + unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); + + return 0; +} diff --git a/src/libsystemd-network/src/dhcp-network.c b/src/libsystemd-network/src/dhcp-network.c new file mode 100644 index 0000000000..4984d49ab7 --- /dev/null +++ b/src/libsystemd-network/src/dhcp-network.c @@ -0,0 +1,236 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "systemd-network/dhcp-internal.h" + +static int _bind_raw_socket(int ifindex, union sockaddr_union *link, + uint32_t xid, const uint8_t *mac_addr, + size_t mac_addr_len, + const uint8_t *bcast_addr, + const struct ether_addr *eth_mac, + uint16_t arp_type, uint8_t dhcp_hlen) { + struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ + BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */ + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20), /* A <- A & 0x20 (More Fragments bit) */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */ + BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_PORT_CLIENT, 1, 0), /* UDP destination port == DHCP client port ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ + BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */ + }; + struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = filter + }; + _cleanup_close_ int s = -1; + int r, on = 1; + + assert(ifindex > 0); + assert(link); + + s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + link->ll.sll_family = AF_PACKET; + link->ll.sll_protocol = htobe16(ETH_P_IP); + link->ll.sll_ifindex = ifindex; + link->ll.sll_hatype = htobe16(arp_type); + link->ll.sll_halen = mac_addr_len; + memcpy(link->ll.sll_addr, bcast_addr, mac_addr_len); + + r = bind(s, &link->sa, sizeof(link->ll)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, + uint32_t xid, const uint8_t *mac_addr, + size_t mac_addr_len, uint16_t arp_type) { + static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + /* Default broadcast address for IPoIB */ + static const uint8_t ib_bcast[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff + }; + struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } }; + const uint8_t *bcast_addr = NULL; + uint8_t dhcp_hlen = 0; + + assert_return(mac_addr_len > 0, -EINVAL); + + if (arp_type == ARPHRD_ETHER) { + assert_return(mac_addr_len == ETH_ALEN, -EINVAL); + memcpy(ð_mac, mac_addr, ETH_ALEN); + bcast_addr = eth_bcast; + dhcp_hlen = ETH_ALEN; + } else if (arp_type == ARPHRD_INFINIBAND) { + assert_return(mac_addr_len == INFINIBAND_ALEN, -EINVAL); + bcast_addr = ib_bcast; + } else + return -EINVAL; + + return _bind_raw_socket(ifindex, link, xid, mac_addr, mac_addr_len, + bcast_addr, ð_mac, arp_type, dhcp_hlen); +} + +int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { + union sockaddr_union src = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(port), + .in.sin_addr.s_addr = address, + }; + _cleanup_close_ int s = -1; + int r, on = 1, tos = IPTOS_CLASS_CS6; + + s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (r < 0) + return -errno; + + if (address == INADDR_ANY) { + r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + if (r < 0) + return -errno; + } else { + r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); + if (r < 0) + return -errno; + } + + r = bind(s, &src.sa, sizeof(src.in)); + if (r < 0) + return -errno; + + r = s; + s = -1; + + return r; +} + +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, + const void *packet, size_t len) { + int r; + + assert(link); + assert(packet); + assert(len); + + r = sendto(s, packet, len, 0, &link->sa, sizeof(link->ll)); + if (r < 0) + return -errno; + + return 0; +} + +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, + const void *packet, size_t len) { + union sockaddr_union dest = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(port), + .in.sin_addr.s_addr = address, + }; + int r; + + assert(s >= 0); + assert(packet); + assert(len); + + r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)); + if (r < 0) + return -errno; + + return 0; +} diff --git a/src/libsystemd-network/src/dhcp-option.c b/src/libsystemd-network/src/dhcp-option.c new file mode 100644 index 0000000000..f47b2ae9e7 --- /dev/null +++ b/src/libsystemd-network/src/dhcp-option.c @@ -0,0 +1,262 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/utf8.h" +#include "systemd-network/dhcp-internal.h" + +static int option_append(uint8_t options[], size_t size, size_t *offset, + uint8_t code, size_t optlen, const void *optval) { + assert(options); + assert(offset); + + if (code != SD_DHCP_OPTION_END) + /* always make sure there is space for an END option */ + size--; + + switch (code) { + + case SD_DHCP_OPTION_PAD: + case SD_DHCP_OPTION_END: + if (size < *offset + 1) + return -ENOBUFS; + + options[*offset] = code; + *offset += 1; + break; + + default: + if (size < *offset + optlen + 2) + return -ENOBUFS; + + options[*offset] = code; + options[*offset + 1] = optlen; + + memcpy_safe(&options[*offset + 2], optval, optlen); + *offset += optlen + 2; + + break; + } + + return 0; +} + +int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, + uint8_t overload, + uint8_t code, size_t optlen, const void *optval) { + size_t file_offset = 0, sname_offset =0; + bool file, sname; + int r; + + assert(message); + assert(offset); + + file = overload & DHCP_OVERLOAD_FILE; + sname = overload & DHCP_OVERLOAD_SNAME; + + if (*offset < size) { + /* still space in the options array */ + r = option_append(message->options, size, offset, code, optlen, optval); + if (r >= 0) + return 0; + 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, SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size; + } else + return r; + } + + if (overload & DHCP_OVERLOAD_FILE) { + file_offset = *offset - size; + + if (file_offset < sizeof(message->file)) { + /* still space in the 'file' array */ + r = option_append(message->file, sizeof(message->file), &file_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + file_offset; + return 0; + } 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, SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + *offset = size + sizeof(message->file); + } else + return r; + } + } + + if (overload & DHCP_OVERLOAD_SNAME) { + sname_offset = *offset - size - (file ? sizeof(message->file) : 0); + + if (sname_offset < sizeof(message->sname)) { + /* still space in the 'sname' array */ + r = option_append(message->sname, sizeof(message->sname), &sname_offset, code, optlen, optval); + if (r >= 0) { + *offset = size + (file ? sizeof(message->file) : 0) + sname_offset; + return 0; + } else { + /* no space, or other error, give up */ + return r; + } + } + } + + return -ENOBUFS; +} + +static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, + uint8_t *message_type, char **error_message, dhcp_option_callback_t cb, + void *userdata) { + uint8_t code, len; + const uint8_t *option; + size_t offset = 0; + + while (offset < buflen) { + code = options[offset ++]; + + switch (code) { + case SD_DHCP_OPTION_PAD: + continue; + + case SD_DHCP_OPTION_END: + return 0; + } + + if (buflen < offset + 1) + return -ENOBUFS; + + 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 = *option; + + break; + + case SD_DHCP_OPTION_ERROR_MESSAGE: + if (len == 0) + return -EINVAL; + + if (error_message) { + _cleanup_free_ char *string = NULL; + + /* Accept a trailing NUL byte */ + if (memchr(option, 0, len - 1)) + return -EINVAL; + + string = strndup((const char *) option, len); + if (!string) + return -ENOMEM; + + if (!ascii_is_valid(string)) + return -EINVAL; + + free(*error_message); + *error_message = string; + string = NULL; + } + + break; + case SD_DHCP_OPTION_OVERLOAD: + if (len != 1) + return -EINVAL; + + if (overload) + *overload = *option; + + break; + + default: + if (cb) + cb(code, len, option, userdata); + + break; + } + + offset += len; + } + + if (offset < buflen) + return -EINVAL; + + return 0; +} + +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) { + _cleanup_free_ char *error_message = NULL; + uint8_t overload = 0; + uint8_t message_type = 0; + int r; + + if (!message) + return -EINVAL; + + if (len < sizeof(DHCPMessage)) + return -EINVAL; + + len -= sizeof(DHCPMessage); + + 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, &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, &error_message, cb, userdata); + if (r < 0) + return r; + } + + 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 message_type; +} diff --git a/src/libsystemd-network/src/dhcp-packet.c b/src/libsystemd-network/src/dhcp-packet.c new file mode 100644 index 0000000000..cdb54caedc --- /dev/null +++ b/src/libsystemd-network/src/dhcp-packet.c @@ -0,0 +1,191 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include +#include +#include +#include + +#include "systemd-network/dhcp-internal.h" +#include "systemd-network/dhcp-protocol.h" + +#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 + +int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, + uint8_t type, uint16_t arp_type, size_t optlen, + size_t *optoffset) { + size_t offset = 0; + int r; + + assert(op == BOOTREQUEST || op == BOOTREPLY); + assert(arp_type == ARPHRD_ETHER || arp_type == ARPHRD_INFINIBAND); + + message->op = op; + message->htype = arp_type; + message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0; + message->xid = htobe32(xid); + message->magic = htobe32(DHCP_MAGIC_COOKIE); + + r = dhcp_option_append(message, optlen, &offset, 0, + SD_DHCP_OPTION_MESSAGE_TYPE, 1, &type); + if (r < 0) + return r; + + *optoffset = offset; + + return 0; +} + +uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) { + uint64_t *buf_64 = (uint64_t*)buf; + uint64_t *end_64 = buf_64 + (len / sizeof(uint64_t)); + uint64_t sum = 0; + + /* See RFC1071 */ + + while (buf_64 < end_64) { + sum += *buf_64; + if (sum < *buf_64) + /* wrap around in one's complement */ + sum++; + + buf_64++; + } + + if (len % sizeof(uint64_t)) { + /* If the buffer is not aligned to 64-bit, we need + to zero-pad the last few bytes and add them in */ + uint64_t buf_tail = 0; + + memcpy(&buf_tail, buf_64, len % sizeof(uint64_t)); + + sum += buf_tail; + if (sum < buf_tail) + /* wrap around */ + sum++; + } + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + +void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr, + uint16_t source_port, be32_t destination_addr, + uint16_t destination_port, uint16_t len) { + packet->ip.version = IPVERSION; + packet->ip.ihl = DHCP_IP_SIZE / 4; + packet->ip.tot_len = htobe16(len); + + packet->ip.tos = IPTOS_CLASS_CS6; + + packet->ip.protocol = IPPROTO_UDP; + packet->ip.saddr = source_addr; + packet->ip.daddr = destination_addr; + + packet->udp.source = htobe16(source_port); + packet->udp.dest = htobe16(destination_port); + + packet->udp.len = htobe16(len - DHCP_IP_SIZE); + + packet->ip.check = packet->udp.len; + packet->udp.check = dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, len - 8); + + packet->ip.ttl = IPDEFTTL; + packet->ip.check = 0; + packet->ip.check = dhcp_packet_checksum((uint8_t*)&packet->ip, DHCP_IP_SIZE); +} + +int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum) { + size_t hdrlen; + + assert(packet); + + /* IP */ + + if (packet->ip.version != IPVERSION) { + log_debug("ignoring packet: not IPv4"); + return -EINVAL; + } + + if (packet->ip.ihl < 5) { + log_debug("ignoring packet: IPv4 IHL (%u words) invalid", + packet->ip.ihl); + return -EINVAL; + } + + hdrlen = packet->ip.ihl * 4; + if (hdrlen < 20) { + log_debug("ignoring packet: IPv4 IHL (%zu bytes) " + "smaller than minimum (20 bytes)", hdrlen); + return -EINVAL; + } + + if (len < hdrlen) { + log_debug("ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by IP header", len, + hdrlen); + return -EINVAL; + } + + /* UDP */ + + if (packet->ip.protocol != IPPROTO_UDP) { + log_debug("ignoring packet: not UDP"); + return -EINVAL; + } + + if (len < hdrlen + be16toh(packet->udp.len)) { + log_debug("ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by UDP header", len, + hdrlen + be16toh(packet->udp.len)); + return -EINVAL; + } + + if (be16toh(packet->udp.dest) != DHCP_PORT_CLIENT) { + log_debug("ignoring packet: to port %u, which " + "is not the DHCP client port (%u)", + be16toh(packet->udp.dest), DHCP_PORT_CLIENT); + return -EINVAL; + } + + /* checksums - computing these is relatively expensive, so only do it + if all the other checks have passed + */ + + if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) { + log_debug("ignoring packet: invalid IP checksum"); + return -EINVAL; + } + + if (checksum && packet->udp.check) { + packet->ip.check = packet->udp.len; + packet->ip.ttl = 0; + + if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, + be16toh(packet->udp.len) + 12)) { + log_debug("ignoring packet: invalid UDP checksum"); + return -EINVAL; + } + } + + return 0; +} diff --git a/src/libsystemd-network/src/dhcp6-network.c b/src/libsystemd-network/src/dhcp6-network.c new file mode 100644 index 0000000000..0f7c281ad3 --- /dev/null +++ b/src/libsystemd-network/src/dhcp6-network.c @@ -0,0 +1,92 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "systemd-network/dhcp6-internal.h" +#include "systemd-network/dhcp6-protocol.h" + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { + union sockaddr_union src = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), + .in6.sin6_scope_id = index, + }; + _cleanup_close_ int s = -1; + int r, off = 0, on = 1; + + assert(index > 0); + assert(local_address); + + 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_V6ONLY, &on, sizeof(on)); + if (r < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (r < 0) + return -errno; + + r = bind(s, &src.sa, sizeof(src.in6)); + if (r < 0) + return -errno; + + r = s; + s = -1; + return r; +} + +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, + const void *packet, size_t len) { + union sockaddr_union dest = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(DHCP6_PORT_SERVER), + }; + int r; + + assert(server_address); + + memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr)); + + r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)); + if (r < 0) + return -errno; + + return 0; +} diff --git a/src/libsystemd-network/src/dhcp6-option.c b/src/libsystemd-network/src/dhcp6-option.c new file mode 100644 index 0000000000..060a822834 --- /dev/null +++ b/src/libsystemd-network/src/dhcp6-option.c @@ -0,0 +1,412 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/sparse-endian.h" +#include "basic/strv.h" +#include "basic/unaligned.h" +#include "basic/util.h" +#include "shared/dns-domain.h" +#include "systemd-network/dhcp6-internal.h" +#include "systemd-network/dhcp6-protocol.h" +#include "systemd-network/sd-dhcp6-client.h" + +#define DHCP6_OPTION_IA_NA_LEN 12 +#define DHCP6_OPTION_IA_TA_LEN 4 + +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, + size_t optlen) { + DHCP6Option *option = (DHCP6Option*) *buf; + + assert_return(buf, -EINVAL); + assert_return(*buf, -EINVAL); + assert_return(buflen, -EINVAL); + + if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) + return -ENOBUFS; + + option->code = htobe16(optcode); + option->len = htobe16(optlen); + + *buf += sizeof(DHCP6Option); + *buflen -= sizeof(DHCP6Option); + + return 0; +} + +int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, + size_t optlen, const void *optval) { + int r; + + assert_return(optval || optlen == 0, -EINVAL); + + r = option_append_hdr(buf, buflen, code, optlen); + if (r < 0) + return r; + + memcpy_safe(*buf, optval, optlen); + + *buf += optlen; + *buflen -= optlen; + + return 0; +} + +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { + uint16_t len; + uint8_t *ia_hdr; + size_t ia_buflen, ia_addrlen = 0; + DHCP6Address *addr; + int r; + + assert_return(buf && *buf && buflen && ia, -EINVAL); + + switch (ia->type) { + case SD_DHCP6_OPTION_IA_NA: + len = DHCP6_OPTION_IA_NA_LEN; + break; + + case SD_DHCP6_OPTION_IA_TA: + len = DHCP6_OPTION_IA_TA_LEN; + break; + + default: + return -EINVAL; + } + + if (*buflen < len) + return -ENOBUFS; + + ia_hdr = *buf; + ia_buflen = *buflen; + + *buf += sizeof(DHCP6Option); + *buflen -= sizeof(DHCP6Option); + + memcpy(*buf, &ia->id, len); + + *buf += len; + *buflen -= len; + + LIST_FOREACH(addresses, addr, ia->addresses) { + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, + sizeof(addr->iaaddr)); + if (r < 0) + return r; + + memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr)); + + *buf += sizeof(addr->iaaddr); + *buflen -= sizeof(addr->iaaddr); + + ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); + } + + r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); + if (r < 0) + return r; + + return 0; +} + + +static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { + DHCP6Option *option = (DHCP6Option*) *buf; + uint16_t len; + + assert_return(buf, -EINVAL); + assert_return(optcode, -EINVAL); + assert_return(optlen, -EINVAL); + + if (*buflen < sizeof(DHCP6Option)) + return -ENOMSG; + + len = be16toh(option->len); + + if (len > *buflen) + return -ENOMSG; + + *optcode = be16toh(option->code); + *optlen = len; + + *buf += 4; + *buflen -= 4; + + return 0; +} + +int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, + size_t *optlen, uint8_t **optvalue) { + int r; + + assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL); + + r = option_parse_hdr(buf, buflen, optcode, optlen); + if (r < 0) + return r; + + if (*optlen > *buflen) + return -ENOBUFS; + + *optvalue = *buf; + *buflen -= *optlen; + *buf += *optlen; + + return 0; +} + +int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, + DHCP6IA *ia) { + int r; + uint16_t opt, status; + size_t optlen; + size_t iaaddr_offset; + DHCP6Address *addr; + uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; + + assert_return(ia, -EINVAL); + assert_return(!ia->addresses, -EINVAL); + + switch (iatype) { + case SD_DHCP6_OPTION_IA_NA: + + if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + + sizeof(addr->iaaddr)) { + r = -ENOBUFS; + goto error; + } + + iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; + memcpy(&ia->id, *buf, iaaddr_offset); + + lt_t1 = be32toh(ia->lifetime_t1); + lt_t2 = be32toh(ia->lifetime_t2); + + if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { + log_dhcp6_client(client, "IA T1 %ds > T2 %ds", + lt_t1, lt_t2); + r = -EINVAL; + goto error; + } + + break; + + case SD_DHCP6_OPTION_IA_TA: + if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + + sizeof(addr->iaaddr)) { + r = -ENOBUFS; + goto error; + } + + iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; + memcpy(&ia->id, *buf, iaaddr_offset); + + ia->lifetime_t1 = 0; + ia->lifetime_t2 = 0; + + break; + + default: + r = -ENOMSG; + goto error; + } + + ia->type = iatype; + + *buflen -= iaaddr_offset; + *buf += iaaddr_offset; + + while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + + switch (opt) { + case SD_DHCP6_OPTION_IAADDR: + + addr = new0(DHCP6Address, 1); + if (!addr) { + r = -ENOMEM; + goto error; + } + + LIST_INIT(addresses, addr); + + memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); + + lt_valid = be32toh(addr->iaaddr.lifetime_valid); + lt_pref = be32toh(addr->iaaddr.lifetime_valid); + + if (!lt_valid || lt_pref > lt_valid) { + log_dhcp6_client(client, "IA preferred %ds > valid %ds", + lt_pref, lt_valid); + free(addr); + } else { + LIST_PREPEND(addresses, ia->addresses, addr); + if (lt_valid < lt_min) + lt_min = lt_valid; + } + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: + if (optlen < sizeof(status)) + break; + + status = (*buf)[0] << 8 | (*buf)[1]; + if (status) { + log_dhcp6_client(client, "IA status %d", + status); + r = -EINVAL; + goto error; + } + + break; + + default: + log_dhcp6_client(client, "Unknown IA option %d", opt); + break; + } + + *buflen -= optlen; + *buf += optlen; + } + + if (r == -ENOMSG) + r = 0; + + if (!ia->lifetime_t1 && !ia->lifetime_t2) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + ia->lifetime_t1 = htobe32(lt_t1); + ia->lifetime_t2 = htobe32(lt_t2); + + log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } + + if (*buflen) + r = -ENOMSG; + +error: + *buf += *buflen; + *buflen = 0; + + return r; +} + +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated) { + + if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) + return -EINVAL; + + if (!GREEDY_REALLOC(*addrs, *allocated, + count * sizeof(struct in6_addr) + optlen)) + return -ENOMEM; + + memcpy(*addrs + count, optval, optlen); + + count += optlen / sizeof(struct in6_addr); + + return count; +} + +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) { + size_t pos = 0, idx = 0; + _cleanup_free_ char **names = NULL; + int r; + + assert_return(optlen > 1, -ENODATA); + assert_return(optval[optlen - 1] == '\0', -EINVAL); + + while (pos < optlen) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + bool first = true; + + for (;;) { + uint8_t c; + + c = optval[pos++]; + + if (c == 0) + /* End of name */ + break; + else if (c <= 63) { + const char *label; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos > optlen) + return -EMSGSIZE; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { + r = -ENOMEM; + goto fail; + } + + if (first) + first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; + + n += r; + continue; + } else { + r = -EBADMSG; + goto fail; + } + } + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + ret[n] = 0; + + r = strv_extend(&names, ret); + if (r < 0) + goto fail; + + idx++; + } + + *str_arr = names; + names = NULL; + + return idx; + +fail: + return r; +} diff --git a/src/libsystemd-network/src/icmp6-util.c b/src/libsystemd-network/src/icmp6-util.c new file mode 100644 index 0000000000..39f9f5785f --- /dev/null +++ b/src/libsystemd-network/src/icmp6-util.c @@ -0,0 +1,142 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "systemd-network/icmp6-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; + char ifname[IF_NAMESIZE] = ""; + static const int zero = 0, one = 1, hops = 255; + int r; + + 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 = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) + return -errno; + + if (if_indextoname(index, ifname) == 0) + return -errno; + + r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + 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/src/lldp-neighbor.c b/src/libsystemd-network/src/lldp-neighbor.c new file mode 100644 index 0000000000..afe2e8d1eb --- /dev/null +++ b/src/libsystemd-network/src/lldp-neighbor.c @@ -0,0 +1,813 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/ether-addr-util.h" +#include "basic/hexdecoct.h" +#include "basic/in-addr-util.h" +#include "basic/unaligned.h" +#include "systemd-network/lldp-internal.h" +#include "systemd-network/lldp-neighbor.h" + +static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { + const LLDPNeighborID *id = p; + + siphash24_compress(id->chassis_id, id->chassis_id_size, state); + siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); + siphash24_compress(id->port_id, id->port_id_size, state); + siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); +} + +static int lldp_neighbor_id_compare_func(const void *a, const void *b) { + const LLDPNeighborID *x = a, *y = b; + int r; + + r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); + if (r != 0) + return r; + + if (x->chassis_id_size < y->chassis_id_size) + return -1; + + if (x->chassis_id_size > y->chassis_id_size) + return 1; + + r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); + if (r != 0) + return r; + + if (x->port_id_size < y->port_id_size) + return -1; + if (x->port_id_size > y->port_id_size) + return 1; + + return 0; +} + +const struct hash_ops lldp_neighbor_id_hash_ops = { + .hash = lldp_neighbor_id_hash_func, + .compare = lldp_neighbor_id_compare_func +}; + +int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { + const sd_lldp_neighbor *x = a, *y = b; + + if (x->until < y->until) + return -1; + + if (x->until > y->until) + return 1; + + return 0; +} + +_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { + if (!n) + return NULL; + + assert(n->n_ref > 0 || n->lldp); + n->n_ref++; + + return n; +} + +static void lldp_neighbor_free(sd_lldp_neighbor *n) { + assert(n); + + free(n->id.port_id); + free(n->id.chassis_id); + free(n->port_description); + free(n->system_name); + free(n->system_description); + free(n->chassis_id_as_string); + free(n->port_id_as_string); + free(n); +} + +_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { + + /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from + * the sd_lldp object. */ + + if (!n) + return NULL; + + assert(n->n_ref > 0); + n->n_ref--; + + if (n->n_ref <= 0 && !n->lldp) + lldp_neighbor_free(n); + + return NULL; +} + +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { + + /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ + + if (!n) + return NULL; + + if (!n->lldp) + return NULL; + + assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); + + n->lldp = NULL; + + if (n->n_ref <= 0) + lldp_neighbor_free(n); + + return NULL; +} + +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { + sd_lldp_neighbor *n; + + n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); + if (!n) + return NULL; + + n->raw_size = raw_size; + n->n_ref = 1; + + return n; +} + +static int parse_string(char **s, const void *q, size_t n) { + const char *p = q; + char *k; + + assert(s); + assert(p || n == 0); + + if (*s) { + log_lldp("Found duplicate string, ignoring field."); + return 0; + } + + /* Strip trailing NULs, just to be nice */ + while (n > 0 && p[n-1] == 0) + n--; + + if (n <= 0) /* Ignore empty strings */ + return 0; + + /* Look for inner NULs */ + if (memchr(p, 0, n)) { + log_lldp("Found inner NUL in string, ignoring field."); + return 0; + } + + /* Let's escape weird chars, for security reasons */ + k = cescape_length(p, n); + if (!k) + return -ENOMEM; + + free(*s); + *s = k; + + return 1; +} + +int lldp_neighbor_parse(sd_lldp_neighbor *n) { + struct ether_header h; + const uint8_t *p; + size_t left; + int r; + + assert(n); + + if (n->raw_size < sizeof(struct ether_header)) { + log_lldp("Received truncated packet, ignoring."); + return -EBADMSG; + } + + memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); + + if (h.ether_type != htobe16(ETHERTYPE_LLDP)) { + log_lldp("Received packet with wrong type, ignoring."); + return -EBADMSG; + } + + if (h.ether_dhost[0] != 0x01 || + h.ether_dhost[1] != 0x80 || + h.ether_dhost[2] != 0xc2 || + h.ether_dhost[3] != 0x00 || + h.ether_dhost[4] != 0x00 || + !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { + log_lldp("Received packet with wrong destination address, ignoring."); + return -EBADMSG; + } + + memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); + memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); + + p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); + left = n->raw_size - sizeof(struct ether_header); + + for (;;) { + uint8_t type; + uint16_t length; + + if (left < 2) { + log_lldp("TLV lacks header, ignoring."); + return -EBADMSG; + } + + type = p[0] >> 1; + length = p[1] + (((uint16_t) (p[0] & 1)) << 8); + p += 2, left -= 2; + + if (left < length) { + log_lldp("TLV truncated, ignoring datagram."); + return -EBADMSG; + } + + switch (type) { + + case SD_LLDP_TYPE_END: + if (length != 0) { + log_lldp("End marker TLV not zero-sized, ignoring datagram."); + return -EBADMSG; + } + if (left != 0) { + log_lldp("Trailing garbage in datagram, ignoring datagram."); + return -EBADMSG; + } + + goto end_marker; + + case SD_LLDP_TYPE_CHASSIS_ID: + if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */ + log_lldp("Chassis ID field size out of range, ignoring datagram."); + return -EBADMSG; + } + if (n->id.chassis_id) { + log_lldp("Duplicate chassis ID field, ignoring datagram."); + return -EBADMSG; + } + + n->id.chassis_id = memdup(p, length); + if (!n->id.chassis_id) + return -ENOMEM; + + n->id.chassis_id_size = length; + break; + + case SD_LLDP_TYPE_PORT_ID: + if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */ + log_lldp("Port ID field size out of range, ignoring datagram."); + return -EBADMSG; + } + if (n->id.port_id) { + log_lldp("Duplicate port ID field, ignoring datagram."); + return -EBADMSG; + } + + n->id.port_id = memdup(p, length); + if (!n->id.port_id) + return -ENOMEM; + + n->id.port_id_size = length; + break; + + case SD_LLDP_TYPE_TTL: + if (length != 2) { + log_lldp("TTL field has wrong size, ignoring datagram."); + return -EBADMSG; + } + + if (n->has_ttl) { + log_lldp("Duplicate TTL field, ignoring datagram."); + return -EBADMSG; + } + + n->ttl = unaligned_read_be16(p); + n->has_ttl = true; + break; + + case SD_LLDP_TYPE_PORT_DESCRIPTION: + r = parse_string(&n->port_description, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_NAME: + r = parse_string(&n->system_name, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: + r = parse_string(&n->system_description, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: + if (length != 4) + log_lldp("System capabilities field has wrong size, ignoring."); + else { + n->system_capabilities = unaligned_read_be16(p); + n->enabled_capabilities = unaligned_read_be16(p + 2); + n->has_capabilities = true; + } + + break; + + case SD_LLDP_TYPE_PRIVATE: + if (length < 4) + log_lldp("Found private TLV that is too short, ignoring."); + + break; + } + + + p += length, left -= length; + } + +end_marker: + if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { + log_lldp("One or more mandatory TLV missing in datagram. Ignoring."); + return -EBADMSG; + + } + + n->rindex = sizeof(struct ether_header); + + return 0; +} + +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { + assert(n); + + if (n->ttl > 0) { + usec_t base; + + /* Use the packet's timestamp if there is one known */ + base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); + if (base <= 0 || base == USEC_INFINITY) + base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ + + n->until = usec_add(base, n->ttl * USEC_PER_SEC); + } else + n->until = 0; + + if (n->lldp) + prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); +} + +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { + if (a == b) + return true; + + if (!a || !b) + return false; + + if (a->raw_size != b->raw_size) + return false; + + return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; +} + +_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { + assert_return(n, -EINVAL); + assert_return(address, -EINVAL); + + *address = n->source_address; + return 0; +} + +_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { + assert_return(n, -EINVAL); + assert_return(address, -EINVAL); + + *address = n->destination_address; + return 0; +} + +_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + *ret = LLDP_NEIGHBOR_RAW(n); + *size = n->raw_size; + + return 0; +} + +_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + assert(n->id.chassis_id_size > 0); + + *type = *(uint8_t*) n->id.chassis_id; + *ret = (uint8_t*) n->id.chassis_id + 1; + *size = n->id.chassis_id_size - 1; + + return 0; +} + +static int format_mac_address(const void *data, size_t sz, char **ret) { + struct ether_addr a; + char *k; + + assert(data || sz <= 0); + + if (sz != 7) + return 0; + + memcpy(&a, (uint8_t*) data + 1, sizeof(a)); + + k = new(char, ETHER_ADDR_TO_STRING_MAX); + if (!k) + return -ENOMEM; + + *ret = ether_addr_to_string(&a, k); + return 1; +} + +static int format_network_address(const void *data, size_t sz, char **ret) { + union in_addr_union a; + int family, r; + + if (sz == 6 && ((uint8_t*) data)[1] == 1) { + memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); + family = AF_INET; + } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { + memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); + family = AF_INET6; + } else + return 0; + + r = in_addr_to_string(family, &a, ret); + if (r < 0) + return r; + return 1; +} + +_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { + char *k; + int r; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (n->chassis_id_as_string) { + *ret = n->chassis_id_as_string; + return 0; + } + + assert(n->id.chassis_id_size > 0); + + switch (*(uint8_t*) n->id.chassis_id) { + + case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: + case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: + case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: + k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); + if (!k) + return -ENOMEM; + + goto done; + + case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: + r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + + case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: + r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + } + + /* Generic fallback */ + k = hexmem(n->id.chassis_id, n->id.chassis_id_size); + if (!k) + return -ENOMEM; + +done: + *ret = n->chassis_id_as_string = k; + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + assert(n->id.port_id_size > 0); + + *type = *(uint8_t*) n->id.port_id; + *ret = (uint8_t*) n->id.port_id + 1; + *size = n->id.port_id_size - 1; + + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { + char *k; + int r; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (n->port_id_as_string) { + *ret = n->port_id_as_string; + return 0; + } + + assert(n->id.port_id_size > 0); + + switch (*(uint8_t*) n->id.port_id) { + + case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: + case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: + case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: + case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: + k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); + if (!k) + return -ENOMEM; + + goto done; + + case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: + r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + + case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: + r = format_network_address(n->id.port_id, n->id.port_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + } + + /* Generic fallback */ + k = hexmem(n->id.port_id, n->id.port_id_size); + if (!k) + return -ENOMEM; + +done: + *ret = n->port_id_as_string = k; + return 0; +} + +_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { + assert_return(n, -EINVAL); + assert_return(ret_sec, -EINVAL); + + *ret_sec = n->ttl; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->system_name) + return -ENODATA; + + *ret = n->system_name; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->system_description) + return -ENODATA; + + *ret = n->system_description; + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->port_description) + return -ENODATA; + + *ret = n->port_description; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->system_capabilities; + return 0; +} + +_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->enabled_capabilities; + return 0; +} + +_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(raw || raw_size <= 0, -EINVAL); + + n = lldp_neighbor_new(raw_size); + if (!n) + return -ENOMEM; + + memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size); + r = lldp_neighbor_parse(n); + if (r < 0) + return r; + + *ret = n; + n = NULL; + + return r; +} + +_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { + assert_return(n, -EINVAL); + + assert(n->raw_size >= sizeof(struct ether_header)); + n->rindex = sizeof(struct ether_header); + + return n->rindex < n->raw_size; +} + +_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { + size_t length; + + assert_return(n, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) /* Truncated message */ + return -EBADMSG; + + length = LLDP_NEIGHBOR_TLV_LENGTH(n); + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + n->rindex += 2 + length; + return n->rindex < n->raw_size; +} + +_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + *type = LLDP_NEIGHBOR_TLV_TYPE(n); + return 0; +} + +_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { + uint8_t k; + int r; + + assert_return(n, -EINVAL); + + r = sd_lldp_neighbor_tlv_get_type(n, &k); + if (r < 0) + return r; + + return type == k; +} + +_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) { + const uint8_t *d; + size_t length; + int r; + + assert_return(n, -EINVAL); + assert_return(oui, -EINVAL); + assert_return(subtype, -EINVAL); + + r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + length = LLDP_NEIGHBOR_TLV_LENGTH(n); + if (length < 4) + return -EBADMSG; + + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + d = LLDP_NEIGHBOR_TLV_DATA(n); + memcpy(oui, d, 3); + *subtype = d[3]; + + return 0; +} + +_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) { + uint8_t k[3], st; + int r; + + r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); + if (r == -ENXIO) + return 0; + if (r < 0) + return r; + + return memcmp(k, oui, 3) == 0 && st == subtype; +} + +_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { + size_t length; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + /* Note that this returns the full TLV, including the TLV header */ + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + length = LLDP_NEIGHBOR_TLV_LENGTH(n); + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; + *size = length + 2; + + return 0; +} + +_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { + assert_return(n, -EINVAL); + assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); + assert_return(clock_supported(clock), -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + if (!triple_timestamp_is_set(&n->timestamp)) + return -ENODATA; + + *ret = triple_timestamp_by_clock(&n->timestamp, clock); + return 0; +} diff --git a/src/libsystemd-network/src/lldp-network.c b/src/libsystemd-network/src/lldp-network.c new file mode 100644 index 0000000000..9d1d592a36 --- /dev/null +++ b/src/libsystemd-network/src/lldp-network.c @@ -0,0 +1,78 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "systemd-network/lldp-network.h" + +int lldp_network_bind_raw_socket(int ifindex) { + + static const struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ + BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ + BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ + }; + + static const struct sock_fprog fprog = { + .len = ELEMENTSOF(filter), + .filter = (struct sock_filter*) filter, + }; + + union sockaddr_union saddrll = { + .ll.sll_family = AF_PACKET, + .ll.sll_ifindex = ifindex, + }; + + _cleanup_close_ int fd = -1; + int r; + + assert(ifindex > 0); + + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, + htobe16(ETHERTYPE_LLDP)); + if (fd < 0) + return -errno; + + r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + if (r < 0) + return -errno; + + r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); + if (r < 0) + return -errno; + + r = fd; + fd = -1; + + return r; +} diff --git a/src/libsystemd-network/src/ndisc-router.c b/src/libsystemd-network/src/ndisc-router.c new file mode 100644 index 0000000000..2fa77d391e --- /dev/null +++ b/src/libsystemd-network/src/ndisc-router.c @@ -0,0 +1,778 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/missing.h" +#include "basic/strv.h" +#include "shared/dns-domain.h" +#include "systemd-network/ndisc-internal.h" +#include "systemd-network/ndisc-router.h" +#include "systemd-network/sd-ndisc.h" + +_public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) { + if (!rt) + return NULL; + + assert(rt->n_ref > 0); + rt->n_ref++; + + return rt; +} + +_public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) { + if (!rt) + return NULL; + + assert(rt->n_ref > 0); + rt->n_ref--; + + if (rt->n_ref > 0) + return NULL; + + free(rt); + return NULL; +} + +sd_ndisc_router *ndisc_router_new(size_t raw_size) { + sd_ndisc_router *rt; + + rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size); + if (!rt) + return NULL; + + rt->raw_size = raw_size; + rt->n_ref = 1; + + return rt; +} + +_public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) { + _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(raw || raw_size <= 0, -EINVAL); + + rt = ndisc_router_new(raw_size); + if (!rt) + return -ENOMEM; + + memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size); + r = ndisc_router_parse(rt); + if (r < 0) + return r; + + *ret = rt; + rt = NULL; + + return r; +} + +_public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { + assert_return(rt, -EINVAL); + assert_return(ret_addr, -EINVAL); + + if (in6_addr_is_null(&rt->address)) + return -ENODATA; + + *ret_addr = rt->address; + return 0; +} + +_public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) { + assert_return(rt, -EINVAL); + assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); + assert_return(clock_supported(clock), -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + if (!triple_timestamp_is_set(&rt->timestamp)) + return -ENODATA; + + *ret = triple_timestamp_by_clock(&rt->timestamp, clock); + return 0; +} + +_public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + *ret = NDISC_ROUTER_RAW(rt); + *size = rt->raw_size; + + return 0; +} + +int ndisc_router_parse(sd_ndisc_router *rt) { + struct nd_router_advert *a; + const uint8_t *p; + bool has_mtu = false, has_flag_extension = false; + size_t left; + + assert(rt); + + if (rt->raw_size < sizeof(struct nd_router_advert)) { + log_ndisc("Too small to be a router advertisement, ignoring."); + return -EBADMSG; + } + + /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */ + a = NDISC_ROUTER_RAW(rt); + + if (a->nd_ra_type != ND_ROUTER_ADVERT) { + log_ndisc("Received ND packet that is not a router advertisement, ignoring."); + return -EBADMSG; + } + + if (a->nd_ra_code != 0) { + log_ndisc("Received ND packet with wrong RA code, ignoring."); + return -EBADMSG; + } + + rt->hop_limit = a->nd_ra_curhoplimit; + rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */ + rt->lifetime = be16toh(a->nd_ra_router_lifetime); + + rt->preference = (rt->flags >> 3) & 3; + if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) + rt->preference = SD_NDISC_PREFERENCE_MEDIUM; + + p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert); + left = rt->raw_size - sizeof(struct nd_router_advert); + + for (;;) { + uint8_t type; + size_t length; + + if (left == 0) + break; + + if (left < 2) { + log_ndisc("Option lacks header, ignoring datagram."); + return -EBADMSG; + } + + type = p[0]; + length = p[1] * 8; + + if (length == 0) { + log_ndisc("Zero-length option, ignoring datagram."); + return -EBADMSG; + } + if (left < length) { + log_ndisc("Option truncated, ignoring datagram."); + return -EBADMSG; + } + + switch (type) { + + case SD_NDISC_OPTION_PREFIX_INFORMATION: + + if (length != 4*8) { + log_ndisc("Prefix option of invalid size, ignoring datagram."); + return -EBADMSG; + } + + if (p[2] > 128) { + log_ndisc("Bad prefix length, ignoring datagram."); + return -EBADMSG; + } + + break; + + case SD_NDISC_OPTION_MTU: { + uint32_t m; + + if (has_mtu) { + log_ndisc("MTU option specified twice, ignoring."); + continue; + } + + if (length != 8) { + log_ndisc("MTU option of invalid size, ignoring datagram."); + return -EBADMSG; + } + + m = be32toh(*(uint32_t*) (p + 4)); + if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */ + rt->mtu = m; + + has_mtu = true; + break; + } + + case SD_NDISC_OPTION_ROUTE_INFORMATION: + if (length < 1*8 || length > 3*8) { + log_ndisc("Route information option of invalid size, ignoring datagram."); + return -EBADMSG; + } + + if (p[2] > 128) { + log_ndisc("Bad route prefix length, ignoring datagram."); + return -EBADMSG; + } + + break; + + case SD_NDISC_OPTION_RDNSS: + if (length < 3*8 || (length % (2*8)) != 1*8) { + log_ndisc("RDNSS option has invalid size."); + return -EBADMSG; + } + + break; + + case SD_NDISC_OPTION_FLAGS_EXTENSION: + + if (has_flag_extension) { + log_ndisc("Flags extension option specified twice, ignoring."); + continue; + } + + if (length < 1*8) { + log_ndisc("Flags extension option has invalid size."); + return -EBADMSG; + } + + /* Add in the additional flags bits */ + rt->flags |= + ((uint64_t) p[2] << 8) | + ((uint64_t) p[3] << 16) | + ((uint64_t) p[4] << 24) | + ((uint64_t) p[5] << 32) | + ((uint64_t) p[6] << 40) | + ((uint64_t) p[7] << 48); + + has_flag_extension = true; + break; + + case SD_NDISC_OPTION_DNSSL: + if (length < 2*8) { + log_ndisc("DNSSL option has invalid size."); + return -EBADMSG; + } + + break; + } + + p += length, left -= length; + } + + rt->rindex = sizeof(struct nd_router_advert); + return 0; +} + +_public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + *ret = rt->hop_limit; + return 0; +} + +_public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) { + assert_return(rt, -EINVAL); + assert_return(ret_flags, -EINVAL); + + *ret_flags = rt->flags; + return 0; +} + +_public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) { + assert_return(rt, -EINVAL); + assert_return(ret_lifetime, -EINVAL); + + *ret_lifetime = rt->lifetime; + return 0; +} + +_public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + *ret = rt->preference; + return 0; +} + +_public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + if (rt->mtu <= 0) + return -ENODATA; + + *ret = rt->mtu; + return 0; +} + +_public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) { + assert_return(rt, -EINVAL); + + assert(rt->raw_size >= sizeof(struct nd_router_advert)); + rt->rindex = sizeof(struct nd_router_advert); + + return rt->rindex < rt->raw_size; +} + +_public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) { + size_t length; + + assert_return(rt, -EINVAL); + + if (rt->rindex == rt->raw_size) /* EOF */ + return -ESPIPE; + + if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ + return -EBADMSG; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (rt->rindex + length > rt->raw_size) + return -EBADMSG; + + rt->rindex += length; + return rt->rindex < rt->raw_size; +} + +_public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) { + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + if (rt->rindex == rt->raw_size) /* EOF */ + return -ESPIPE; + + if (rt->rindex + 2 > rt->raw_size) /* Truncated message */ + return -EBADMSG; + + *ret = NDISC_ROUTER_OPTION_TYPE(rt); + return 0; +} + +_public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) { + uint8_t k; + int r; + + assert_return(rt, -EINVAL); + + r = sd_ndisc_router_option_get_type(rt, &k); + if (r < 0) + return r; + + return type == k; +} + +_public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) { + size_t length; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + /* Note that this returns the full option, including the option header */ + + if (rt->rindex + 2 > rt->raw_size) + return -EBADMSG; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (rt->rindex + length > rt->raw_size) + return -EBADMSG; + + *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; + *size = length; + + return 0; +} + +static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) { + struct nd_opt_prefix_info *ri; + size_t length; + int r; + + assert(rt); + assert(ret); + + r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION); + if (r < 0) + return r; + if (r == 0) + return -EMEDIUMTYPE; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (length != sizeof(struct nd_opt_prefix_info)) + return -EBADMSG; + + ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex); + if (ri->nd_opt_pi_prefix_len > 128) + return -EBADMSG; + + *ret = ri; + return 0; +} + +_public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) { + struct nd_opt_prefix_info *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_prefix_info(rt, &ri); + if (r < 0) + return r; + + *ret = be32toh(ri->nd_opt_pi_valid_time); + return 0; +} + +_public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) { + struct nd_opt_prefix_info *pi; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_prefix_info(rt, &pi); + if (r < 0) + return r; + + *ret = be32toh(pi->nd_opt_pi_preferred_time); + return 0; +} + +_public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) { + struct nd_opt_prefix_info *pi; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_prefix_info(rt, &pi); + if (r < 0) + return r; + + *ret = pi->nd_opt_pi_flags_reserved; + return 0; +} + +_public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { + struct nd_opt_prefix_info *pi; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret_addr, -EINVAL); + + r = get_prefix_info(rt, &pi); + if (r < 0) + return r; + + *ret_addr = pi->nd_opt_pi_prefix; + return 0; +} + +_public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { + struct nd_opt_prefix_info *pi; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_prefix_info(rt, &pi); + if (r < 0) + return r; + + if (pi->nd_opt_pi_prefix_len > 128) + return -EBADMSG; + + *ret = pi->nd_opt_pi_prefix_len; + return 0; +} + +static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) { + uint8_t *ri; + size_t length; + int r; + + assert(rt); + assert(ret); + + r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION); + if (r < 0) + return r; + if (r == 0) + return -EMEDIUMTYPE; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (length < 1*8 || length > 3*8) + return -EBADMSG; + + ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; + + if (ri[2] > 128) + return -EBADMSG; + + *ret = ri; + return 0; +} + +_public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_route_info(rt, &ri); + if (r < 0) + return r; + + *ret = be32toh(*(uint32_t*) (ri + 4)); + return 0; +} + +_public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret_addr, -EINVAL); + + r = get_route_info(rt, &ri); + if (r < 0) + return r; + + zero(*ret_addr); + memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8); + + return 0; +} + +_public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_route_info(rt, &ri); + if (r < 0) + return r; + + *ret = ri[2]; + return 0; +} + +_public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_route_info(rt, &ri); + if (r < 0) + return r; + + *ret = (ri[3] >> 3) & 3; + if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH)) + *ret = SD_NDISC_PREFERENCE_MEDIUM; + + return 0; +} + +static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) { + size_t length; + int r; + + assert(rt); + assert(ret); + + r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS); + if (r < 0) + return r; + if (r == 0) + return -EMEDIUMTYPE; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (length < 3*8 || (length % (2*8)) != 1*8) + return -EBADMSG; + + *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; + return 0; +} + +_public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_rdnss_info(rt, &ri); + if (r < 0) + return r; + + *ret = (const struct in6_addr*) (ri + 8); + return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16; +} + +_public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_rdnss_info(rt, &ri); + if (r < 0) + return r; + + *ret = be32toh(*(uint32_t*) (ri + 4)); + return 0; +} + +static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) { + size_t length; + int r; + + assert(rt); + assert(ret); + + r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL); + if (r < 0) + return r; + if (r == 0) + return -EMEDIUMTYPE; + + length = NDISC_ROUTER_OPTION_LENGTH(rt); + if (length < 2*8) + return -EBADMSG; + + *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex; + return 0; +} + +_public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) { + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *e = NULL; + size_t allocated = 0, n = 0, left; + uint8_t *ri, *p; + bool first = true; + int r; + unsigned k = 0; + + assert_return(rt, -EINVAL); + assert_return(ret, -EINVAL); + + r = get_dnssl_info(rt, &ri); + if (r < 0) + return r; + + p = ri + 8; + left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8; + + for (;;) { + if (left == 0) { + + if (n > 0) /* Not properly NUL terminated */ + return -EBADMSG; + + break; + } + + if (*p == 0) { + /* Found NUL termination */ + + if (n > 0) { + _cleanup_free_ char *normalized = NULL; + + e[n] = 0; + r = dns_name_normalize(e, &normalized); + if (r < 0) + return r; + + /* Ignore the root domain name or "localhost" and friends */ + if (!is_localhost(normalized) && + !dns_name_is_root(normalized)) { + + if (strv_push(&l, normalized) < 0) + return -ENOMEM; + + normalized = NULL; + k++; + } + } + + n = 0; + first = true; + p++, left--; + continue; + } + + /* Check for compression (which is not allowed) */ + if (*p > 63) + return -EBADMSG; + + if (1U + *p + 1U > left) + return -EBADMSG; + + if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U)) + return -ENOMEM; + + if (first) + first = false; + else + e[n++] = '.'; + + r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + + left -= 1 + *p; + p += 1 + *p; + } + + if (strv_isempty(l)) { + *ret = NULL; + return 0; + } + + *ret = l; + l = NULL; + + return k; +} + +_public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) { + uint8_t *ri; + int r; + + assert_return(rt, -EINVAL); + assert_return(ret_sec, -EINVAL); + + r = get_dnssl_info(rt, &ri); + if (r < 0) + return r; + + *ret_sec = be32toh(*(uint32_t*) (ri + 4)); + return 0; +} diff --git a/src/libsystemd-network/src/network-internal.c b/src/libsystemd-network/src/network-internal.c new file mode 100644 index 0000000000..df0d335ca0 --- /dev/null +++ b/src/libsystemd-network/src/network-internal.c @@ -0,0 +1,556 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/ether-addr-util.h" +#include "basic/hexdecoct.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/siphash24.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/condition.h" +#include "shared/conf-parser.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/network-internal.h" +#include "systemd-network/sd-ndisc.h" + +const char *net_get_name(struct udev_device *device) { + const char *name, *field; + + assert(device); + + /* fetch some persistent data unique (on this machine) to this device */ + FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { + name = udev_device_get_property_value(device, field); + if (name) + return name; + } + + return NULL; +} + +#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, uint64_t *result) { + size_t l, sz = 0; + const char *name = NULL; + int r; + uint8_t *v; + + assert(device); + + name = net_get_name(device); + if (!name) + return -ENOENT; + + l = strlen(name); + sz = sizeof(sd_id128_t) + l; + v = alloca(sz); + + /* fetch some persistent data unique to this machine */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + memcpy(v + sizeof(sd_id128_t), name, l); + + /* Let's hash the machine ID plus the device name. We + * use a fixed, but originally randomly created hash + * key here. */ + *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); + + return 0; +} + +bool net_match_config(const struct ether_addr *match_mac, + char * const *match_paths, + char * const *match_drivers, + char * const *match_types, + char * const *match_names, + Condition *match_host, + Condition *match_virt, + Condition *match_kernel, + Condition *match_arch, + const struct ether_addr *dev_mac, + const char *dev_path, + const char *dev_parent_driver, + const char *dev_driver, + const char *dev_type, + const char *dev_name) { + + if (match_host && condition_test(match_host) <= 0) + return false; + + if (match_virt && condition_test(match_virt) <= 0) + return false; + + if (match_kernel && condition_test(match_kernel) <= 0) + return false; + + if (match_arch && condition_test(match_arch) <= 0) + return false; + + if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) + return false; + + if (!strv_isempty(match_paths) && + (!dev_path || !strv_fnmatch(match_paths, dev_path, 0))) + return false; + + if (!strv_isempty(match_drivers) && + (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0))) + return false; + + if (!strv_isempty(match_types) && + (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0))) + return false; + + if (!strv_isempty(match_names) && + (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0))) + return false; + + return true; +} + +int config_parse_net_condition(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) { + + ConditionType cond = ltype; + Condition **ret = data; + bool negate; + Condition *c; + _cleanup_free_ char *s = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + negate = rvalue[0] == '!'; + if (negate) + rvalue++; + + s = strdup(rvalue); + if (!s) + return log_oom(); + + c = condition_new(cond, s, false, negate); + if (!c) + return log_oom(); + + if (*ret) + condition_free(*ret); + + *ret = c; + return 0; +} + +int config_parse_ifnames( + 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) { + + char ***sv = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); + return 0; + } + if (r == 0) + break; + + if (!ifname_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = strv_push(sv, word); + if (r < 0) + return log_oom(); + + word = NULL; + } + + return 0; +} + +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) { + + char **s = data; + _cleanup_free_ char *n = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + n = strdup(rvalue); + if (!n) + return log_oom(); + + if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); + return 0; + } + + free(*s); + if (*n) { + *s = n; + n = NULL; + } else + *s = NULL; + + return 0; +} + +int config_parse_hwaddr(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + struct ether_addr **hwaddr = data; + struct ether_addr *n; + const char *start; + size_t offset; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + n = new0(struct ether_addr, 1); + if (!n) + return log_oom(); + + start = rvalue + strspn(rvalue, WHITESPACE); + r = ether_addr_from_string(start, n, &offset); + + if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); + free(n); + return 0; + } + + free(*hwaddr); + *hwaddr = n; + + return 0; +} + +int config_parse_iaid(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) { + uint32_t iaid; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &iaid); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Unable to read IAID, ignoring assignment: %s", rvalue); + return 0; + } + + *((uint32_t *)data) = iaid; + + return 0; +} + +void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { + unsigned i; + + assert(f); + assert(addresses); + assert(size); + + for (i = 0; i < size; i++) + fprintf(f, "%s%s", inet_ntoa(addresses[i]), + (i < (size - 1)) ? " ": ""); +} + +int deserialize_in_addrs(struct in_addr **ret, const char *string) { + _cleanup_free_ struct in_addr *addresses = NULL; + int size = 0; + + assert(ret); + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + struct in_addr *new_addresses; + int r; + + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); + if (!new_addresses) + return -ENOMEM; + else + addresses = new_addresses; + + r = inet_pton(AF_INET, word, &(addresses[size])); + if (r <= 0) + continue; + + size++; + } + + *ret = addresses; + addresses = NULL; + + return size; +} + +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) { + unsigned i; + + assert(f); + assert(addresses); + assert(size); + + for (i = 0; i < size; i++) { + char buffer[INET6_ADDRSTRLEN]; + + fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f); + + if (i < size - 1) + fputc(' ', f); + } +} + +int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { + _cleanup_free_ struct in6_addr *addresses = NULL; + int size = 0; + + assert(ret); + assert(string); + + for (;;) { + _cleanup_free_ char *word = NULL; + struct in6_addr *new_addresses; + int r; + + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); + if (!new_addresses) + return -ENOMEM; + else + addresses = new_addresses; + + r = inet_pton(AF_INET6, word, &(addresses[size])); + if (r <= 0) + continue; + + size++; + } + + *ret = addresses; + addresses = NULL; + + return size; +} + +void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) { + unsigned i; + + assert(f); + assert(key); + assert(routes); + assert(size); + + fprintf(f, "%s=", key); + + for (i = 0; i < size; i++) { + 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); +} + +int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { + _cleanup_free_ struct sd_dhcp_route *routes = NULL; + size_t size = 0, allocated = 0; + + assert(ret); + assert(ret_size); + assert(ret_allocated); + assert(string); + + /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ + for (;;) { + _cleanup_free_ char *word = NULL; + char *tok, *tok_end; + unsigned n; + int r; + + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + if (!GREEDY_REALLOC(routes, allocated, size + 1)) + return -ENOMEM; + + tok = word; + + /* get the subnet */ + tok_end = strchr(tok, '/'); + if (!tok_end) + continue; + *tok_end = '\0'; + + r = inet_aton(tok, &routes[size].dst_addr); + if (r == 0) + continue; + + tok = tok_end + 1; + + /* get the prefixlen */ + tok_end = strchr(tok, ','); + if (!tok_end) + continue; + + *tok_end = '\0'; + + r = safe_atou(tok, &n); + if (r < 0 || n > 32) + continue; + + routes[size].dst_prefixlen = (uint8_t) n; + tok = tok_end + 1; + + /* get the gateway */ + r = inet_aton(tok, &routes[size].gw_addr); + if (r == 0) + continue; + + size++; + } + + *ret_size = size; + *ret_allocated = allocated; + *ret = routes; + routes = NULL; + + return 0; +} + +int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) { + _cleanup_free_ char *hex_buf = NULL; + + assert(f); + assert(key); + assert(data); + + hex_buf = hexmem(data, size); + if (hex_buf == NULL) + return -ENOMEM; + + fprintf(f, "%s=%s\n", key, hex_buf); + + return 0; +} + +int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) { + assert(data); + assert(data_len); + assert(string); + + if (strlen(string) % 2) + return -EINVAL; + + return unhexmem(string, strlen(string), (void **)data, data_len); +} diff --git a/src/libsystemd-network/src/sd-dhcp-client.c b/src/libsystemd-network/src/sd-dhcp-client.c new file mode 100644 index 0000000000..75895f2e58 --- /dev/null +++ b/src/libsystemd-network/src/sd-dhcp-client.c @@ -0,0 +1,1906 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/hostname-util.h" +#include "basic/random-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/dns-domain.h" +#include "systemd-network/dhcp-identifier.h" +#include "systemd-network/dhcp-internal.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/dhcp-protocol.h" +#include "systemd-network/sd-dhcp-client.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) + +#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) +#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) + +struct sd_dhcp_client { + unsigned n_ref; + + DHCPState state; + sd_event *event; + int event_priority; + sd_event_source *timeout_resend; + int ifindex; + int fd; + union sockaddr_union link; + sd_event_source *receive_message; + bool request_broadcast; + uint8_t *req_opts; + size_t req_opts_allocated; + size_t req_opts_size; + be32_t last_addr; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; + struct { + uint8_t type; + union { + struct { + /* 0: Generic (non-LL) (RFC 2132) */ + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ gen; + struct { + /* 1: Ethernet Link-Layer (RFC 2132) */ + uint8_t haddr[ETH_ALEN]; + } _packed_ eth; + struct { + /* 2 - 254: ARP/Link-Layer (RFC 2132) */ + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* 255: Node-specific (RFC 4361) */ + be32_t iaid; + struct duid duid; + } _packed_ ns; + struct { + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ raw; + }; + } _packed_ client_id; + size_t client_id_len; + char *hostname; + char *vendor_class_identifier; + uint32_t mtu; + uint32_t xid; + usec_t start_time; + unsigned int attempt; + usec_t request_sent; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + sd_event_source *timeout_expire; + sd_dhcp_client_callback_t callback; + void *userdata; + sd_dhcp_lease *lease; + usec_t start_delay; +}; + +static const uint8_t default_req_opts[] = { + 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, + uint32_t revents, + void *userdata); +static int client_receive_message_udp( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata); +static void client_stop(sd_dhcp_client *client, int error); + +int sd_dhcp_client_set_callback( + sd_dhcp_client *client, + sd_dhcp_client_callback_t cb, + void *userdata) { + + assert_return(client, -EINVAL); + + client->callback = cb; + client->userdata = userdata; + + return 0; +} + +int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) { + assert_return(client, -EINVAL); + + client->request_broadcast = !!broadcast; + + return 0; +} + +int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { + size_t i; + + assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); + + switch(option) { + + 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: + break; + } + + for (i = 0; i < client->req_opts_size; i++) + if (client->req_opts[i] == option) + return -EEXIST; + + if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, + client->req_opts_size + 1)) + return -ENOMEM; + + client->req_opts[client->req_opts_size++] = option; + + return 0; +} + +int sd_dhcp_client_set_request_address( + sd_dhcp_client *client, + const struct in_addr *last_addr) { + + assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); + + if (last_addr) + client->last_addr = last_addr->s_addr; + else + client->last_addr = INADDR_ANY; + + return 0; +} + +int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) { + + assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); + assert_return(ifindex > 0, -EINVAL); + + client->ifindex = ifindex; + return 0; +} + +int sd_dhcp_client_set_mac( + sd_dhcp_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type) { + + DHCP_CLIENT_DONT_DESTROY(client); + bool need_restart = false; + + assert_return(client, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) + return 0; + + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { + log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting"); + need_restart = true; + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + } + + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; + + if (need_restart && client->state != DHCP_STATE_STOPPED) + sd_dhcp_client_start(client); + + return 0; +} + +int sd_dhcp_client_get_client_id( + sd_dhcp_client *client, + uint8_t *type, + const uint8_t **data, + size_t *data_len) { + + assert_return(client, -EINVAL); + assert_return(type, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_len, -EINVAL); + + *type = 0; + *data = NULL; + *data_len = 0; + if (client->client_id_len) { + *type = client->client_id.type; + *data = client->client_id.raw.data; + *data_len = client->client_id_len - sizeof(client->client_id.type); + } + + return 0; +} + +int sd_dhcp_client_set_client_id( + sd_dhcp_client *client, + uint8_t type, + const uint8_t *data, + size_t data_len) { + + DHCP_CLIENT_DONT_DESTROY(client); + bool need_restart = false; + + assert_return(client, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL); + + switch (type) { + + case ARPHRD_ETHER: + if (data_len != ETH_ALEN) + return -EINVAL; + break; + + case ARPHRD_INFINIBAND: + if (data_len != INFINIBAND_ALEN) + return -EINVAL; + break; + + default: + break; + } + + if (client->client_id_len == data_len + sizeof(client->client_id.type) && + client->client_id.type == type && + memcmp(&client->client_id.raw.data, data, data_len) == 0) + return 0; + + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { + log_dhcp_client(client, "Changing client ID on running DHCP " + "client, restarting"); + need_restart = true; + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + } + + client->client_id.type = type; + memcpy(&client->client_id.raw.data, data, data_len); + client->client_id_len = data_len + sizeof (client->client_id.type); + + if (need_restart && client->state != DHCP_STATE_STOPPED) + sd_dhcp_client_start(client); + + return 0; +} + +/** + * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid + * without further modification. Otherwise, if duid_type is supported, DUID + * is set based on that type. Otherwise, an error is returned. + */ +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + + DHCP_CLIENT_DONT_DESTROY(client); + int r; + size_t len; + + assert_return(client, -EINVAL); + assert_return(duid_len == 0 || duid != NULL, -EINVAL); + + if (duid != NULL) { + r = dhcp_validate_duid_len(duid_type, duid_len); + if (r < 0) + return r; + } + + zero(client->client_id); + client->client_id.type = 255; + + /* If IAID is not configured, generate it. */ + if (iaid == 0) { + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, + client->mac_addr_len, + &client->client_id.ns.iaid); + if (r < 0) + return r; + } else + client->client_id.ns.iaid = htobe32(iaid); + + if (duid != NULL) { + client->client_id.ns.duid.type = htobe16(duid_type); + memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); + len = sizeof(client->client_id.ns.duid.type) + duid_len; + } else if (duid_type == DUID_TYPE_EN) { + r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); + if (r < 0) + return r; + } else + return -EOPNOTSUPP; + + client->client_id_len = sizeof(client->client_id.type) + len + + sizeof(client->client_id.ns.iaid); + + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { + log_dhcp_client(client, "Configured IAID+DUID, restarting."); + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + sd_dhcp_client_start(client); + } + + return 0; +} + +int sd_dhcp_client_set_hostname( + sd_dhcp_client *client, + const char *hostname) { + + char *new_hostname = NULL; + + 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; + + if (hostname) { + new_hostname = strdup(hostname); + if (!new_hostname) + return -ENOMEM; + } + + free(client->hostname); + client->hostname = new_hostname; + + return 0; +} + +int sd_dhcp_client_set_vendor_class_identifier( + sd_dhcp_client *client, + const char *vci) { + + char *new_vci = NULL; + + assert_return(client, -EINVAL); + + new_vci = strdup(vci); + if (!new_vci) + return -ENOMEM; + + free(client->vendor_class_identifier); + + client->vendor_class_identifier = new_vci; + + return 0; +} + +int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { + assert_return(client, -EINVAL); + assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE); + + client->mtu = mtu; + + return 0; +} + +int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { + assert_return(client, -EINVAL); + + if (client->state != DHCP_STATE_BOUND && + client->state != DHCP_STATE_RENEWING && + client->state != DHCP_STATE_REBINDING) + return -EADDRNOTAVAIL; + + if (ret) + *ret = client->lease; + + return 0; +} + +static void client_notify(sd_dhcp_client *client, int event) { + assert(client); + + if (client->callback) + client->callback(client, event, client->userdata); +} + +static int client_initialize(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + + client->receive_message = sd_event_source_unref(client->receive_message); + + client->fd = asynchronous_close(client->fd); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + client->timeout_expire = sd_event_source_unref(client->timeout_expire); + + client->attempt = 1; + + client->state = DHCP_STATE_INIT; + client->xid = 0; + + client->lease = sd_dhcp_lease_unref(client->lease); + + return 0; +} + +static void client_stop(sd_dhcp_client *client, int error) { + assert(client); + + if (error < 0) + log_dhcp_client(client, "STOPPED: %s", strerror(-error)); + else if (error == SD_DHCP_CLIENT_EVENT_STOP) + log_dhcp_client(client, "STOPPED"); + else + log_dhcp_client(client, "STOPPED: Unknown event"); + + client_notify(client, error); + + client_initialize(client); +} + +static int client_message_init( + sd_dhcp_client *client, + DHCPPacket **ret, + uint8_t type, + size_t *_optlen, + size_t *_optoffset) { + + _cleanup_free_ DHCPPacket *packet = NULL; + size_t optlen, optoffset, size; + be16_t max_size; + usec_t time_now; + uint16_t secs; + int r; + + assert(client); + assert(client->start_time); + assert(ret); + assert(_optlen); + assert(_optoffset); + assert(type == DHCP_DISCOVER || type == DHCP_REQUEST); + + optlen = DHCP_MIN_OPTIONS_SIZE; + size = sizeof(DHCPPacket) + optlen; + + packet = malloc0(size); + if (!packet) + return -ENOMEM; + + r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, + client->arp_type, optlen, &optoffset); + if (r < 0) + return r; + + /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers + refuse to issue an DHCP lease if 'secs' is set to zero */ + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + assert(time_now >= client->start_time); + + /* seconds between sending first and last DISCOVER + * must always be strictly positive to deal with broken servers */ + secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; + packet->dhcp.secs = htobe16(secs); + + /* RFC2132 section 4.1 + A client that cannot receive unicast IP datagrams until its protocol + software has been configured with an IP address SHOULD set the + BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or + DHCPREQUEST messages that client sends. The BROADCAST bit will + provide a hint to the DHCP server and BOOTP relay agent to broadcast + any messages to the client on the client's subnet. + + Note: some interfaces needs this to be enabled, but some networks + needs this to be disabled as broadcasts are filteretd, so this + needs to be configurable */ + if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) + packet->dhcp.flags = htobe16(0x8000); + + /* RFC2132 section 4.1.1: + The client MUST include its hardware address in the ’chaddr’ field, if + necessary for delivery of DHCP reply messages. Non-Ethernet + interfaces will leave 'chaddr' empty and use the client identifier + instead (eg, RFC 4390 section 2.1). + */ + if (client->arp_type == ARPHRD_ETHER) + memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); + + /* If no client identifier exists, construct an RFC 4361-compliant one */ + if (client->client_id_len == 0) { + size_t duid_len; + + client->client_id.type = 255; + + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid); + if (r < 0) + return r; + + r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + if (r < 0) + return r; + + client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len; + } + + /* Some DHCP servers will refuse to issue an DHCP lease if the Client + Identifier option is not set */ + if (client->client_id_len) { + r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_CLIENT_IDENTIFIER, + client->client_id_len, + &client->client_id); + if (r < 0) + return r; + } + + /* RFC2131 section 3.5: + in its initial DHCPDISCOVER or DHCPREQUEST message, a + client may provide the server with a list of specific + parameters the client is interested in. If the client + includes a list of parameters in a DHCPDISCOVER message, + it MUST include that list in any subsequent DHCPREQUEST + messages. + */ + r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, + client->req_opts_size, client->req_opts); + if (r < 0) + return r; + + /* RFC2131 section 3.5: + The client SHOULD include the ’maximum DHCP message size’ option to + let the server know how large the server may make its DHCP messages. + + Note (from ConnMan): Some DHCP servers will send bigger DHCP packets + than the defined default size unless the Maximum Messge Size option + is explicitly set + + RFC3442 "Requirements to Avoid Sizing Constraints": + Because a full routing table can be quite large, the standard 576 + octet maximum size for a DHCP message may be too short to contain + some legitimate Classless Static Route options. Because of this, + clients implementing the Classless Static Route option SHOULD send a + Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP + stack is capable of receiving larger IP datagrams. In this case, the + client SHOULD set the value of this option to at least the MTU of the + interface that the client is configuring. The client MAY set the + value of this option higher, up to the size of the largest UDP packet + it is prepared to accept. (Note that the value specified in the + Maximum DHCP Message Size option is the total maximum packet size, + including IP and UDP headers.) + */ + max_size = htobe16(size); + r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, + 2, &max_size); + if (r < 0) + return r; + + *_optlen = optlen; + *_optoffset = optoffset; + *ret = packet; + packet = NULL; + + 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, + INADDR_BROADCAST, DHCP_PORT_SERVER, len); + + return dhcp_network_send_raw_socket(client->fd, &client->link, + packet, len); +} + +static int client_send_discover(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *discover = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + assert(client->state == DHCP_STATE_INIT || + client->state == DHCP_STATE_SELECTING); + + r = client_message_init(client, &discover, DHCP_DISCOVER, + &optlen, &optoffset); + if (r < 0) + return r; + + /* the client may suggest values for the network address + and lease time in the DHCPDISCOVER message. The client may include + the ’requested IP address’ option to suggest that a particular IP + address be assigned, and may include the ’IP address lease time’ + option to suggest the lease time it would like. + */ + if (client->last_addr != INADDR_ANY) { + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, + 4, &client->last_addr); + if (r < 0) + return r; + } + + if (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, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, + strlen(client->vendor_class_identifier), + client->vendor_class_identifier); + if (r < 0) + return r; + } + + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + /* We currently ignore: + The client SHOULD wait a random time between one and ten seconds to + desynchronize the use of DHCP at startup. + */ + r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "DISCOVER"); + + return 0; +} + +static int client_send_request(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *request = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + + r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset); + if (r < 0) + return r; + + switch (client->state) { + /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC, + SELECTING should be REQUESTING) + */ + + case DHCP_STATE_REQUESTING: + /* Client inserts the address of the selected server in ’server + identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be + filled in with the yiaddr value from the chosen DHCPOFFER. + */ + + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_SERVER_IDENTIFIER, + 4, &client->lease->server_address); + if (r < 0) + return r; + + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, + 4, &client->lease->address); + if (r < 0) + return r; + + break; + + case DHCP_STATE_INIT_REBOOT: + /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ + option MUST be filled in with client’s notion of its previously + assigned address. ’ciaddr’ MUST be zero. + */ + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS, + 4, &client->last_addr); + if (r < 0) + return r; + break; + + case DHCP_STATE_RENEWING: + /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ + option MUST NOT be filled in, ’ciaddr’ MUST be filled in with + client’s IP address. + */ + + /* fall through */ + case DHCP_STATE_REBINDING: + /* ’server identifier’ MUST NOT be filled in, ’requested IP address’ + option MUST NOT be filled in, ’ciaddr’ MUST be filled in with + client’s IP address. + + This message MUST be broadcast to the 0xffffffff IP broadcast address. + */ + request->dhcp.ciaddr = client->lease->address; + + break; + + case DHCP_STATE_INIT: + case DHCP_STATE_SELECTING: + case DHCP_STATE_REBOOTING: + case DHCP_STATE_BOUND: + case DHCP_STATE_STOPPED: + return -EINVAL; + } + + if (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, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + if (client->state == DHCP_STATE_RENEWING) { + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &request->dhcp, + sizeof(DHCPMessage) + optoffset); + } else { + r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset); + } + if (r < 0) + return r; + + switch (client->state) { + + case DHCP_STATE_REQUESTING: + log_dhcp_client(client, "REQUEST (requesting)"); + break; + + case DHCP_STATE_INIT_REBOOT: + log_dhcp_client(client, "REQUEST (init-reboot)"); + break; + + case DHCP_STATE_RENEWING: + log_dhcp_client(client, "REQUEST (renewing)"); + break; + + case DHCP_STATE_REBINDING: + log_dhcp_client(client, "REQUEST (rebinding)"); + break; + + default: + log_dhcp_client(client, "REQUEST (invalid)"); + break; + } + + return 0; +} + +static int client_start(sd_dhcp_client *client); + +static int client_timeout_resend( + sd_event_source *s, + uint64_t usec, + void *userdata) { + + sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); + usec_t next_timeout = 0; + uint64_t time_now; + uint32_t time_left; + int r; + + assert(s); + assert(client); + assert(client->event); + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + goto error; + + switch (client->state) { + + case DHCP_STATE_RENEWING: + + time_left = (client->lease->t2 - client->lease->t1) / 2; + if (time_left < 60) + time_left = 60; + + next_timeout = time_now + time_left * USEC_PER_SEC; + + break; + + case DHCP_STATE_REBINDING: + + time_left = (client->lease->lifetime - client->lease->t2) / 2; + if (time_left < 60) + time_left = 60; + + next_timeout = time_now + time_left * USEC_PER_SEC; + break; + + case DHCP_STATE_REBOOTING: + /* start over as we did not receive a timely ack or nak */ + r = client_initialize(client); + if (r < 0) + goto error; + + r = client_start(client); + if (r < 0) + goto error; + else { + log_dhcp_client(client, "REBOOTED"); + return 0; + } + + case DHCP_STATE_INIT: + case DHCP_STATE_INIT_REBOOT: + case DHCP_STATE_SELECTING: + case DHCP_STATE_REQUESTING: + case DHCP_STATE_BOUND: + + if (client->attempt < 64) + client->attempt *= 2; + + next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC; + + break; + + case DHCP_STATE_STOPPED: + r = -EINVAL; + goto error; + } + + next_timeout += (random_u32() & 0x1fffff); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + r = sd_event_add_time(client->event, + &client->timeout_resend, + clock_boottime_or_monotonic(), + next_timeout, 10 * USEC_PER_MSEC, + client_timeout_resend, client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; + + switch (client->state) { + case DHCP_STATE_INIT: + r = client_send_discover(client); + if (r >= 0) { + client->state = DHCP_STATE_SELECTING; + client->attempt = 1; + } else { + if (client->attempt >= 64) + goto error; + } + + break; + + case DHCP_STATE_SELECTING: + r = client_send_discover(client); + if (r < 0 && client->attempt >= 64) + goto error; + + break; + + case DHCP_STATE_INIT_REBOOT: + case DHCP_STATE_REQUESTING: + case DHCP_STATE_RENEWING: + case DHCP_STATE_REBINDING: + r = client_send_request(client); + if (r < 0 && client->attempt >= 64) + goto error; + + if (client->state == DHCP_STATE_INIT_REBOOT) + client->state = DHCP_STATE_REBOOTING; + + client->request_sent = time_now; + + break; + + case DHCP_STATE_REBOOTING: + case DHCP_STATE_BOUND: + + break; + + case DHCP_STATE_STOPPED: + r = -EINVAL; + goto error; + } + + return 0; + +error: + client_stop(client, r); + + /* Errors were dealt with when stopping the client, don't spill + errors into the event loop handler */ + return 0; +} + +static int client_initialize_io_events( + sd_dhcp_client *client, + sd_event_io_handler_t io_callback) { + + int r; + + assert(client); + assert(client->event); + + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, io_callback, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message"); + if (r < 0) + goto error; + +error: + if (r < 0) + client_stop(client, r); + + return 0; +} + +static int client_initialize_time_events(sd_dhcp_client *client) { + uint64_t usec = 0; + int r; + + assert(client); + assert(client->event); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + if (client->start_delay) { + assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); + usec += client->start_delay; + } + + r = sd_event_add_time(client->event, + &client->timeout_resend, + clock_boottime_or_monotonic(), + usec, 0, + client_timeout_resend, client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; + +error: + if (r < 0) + client_stop(client, r); + + return 0; + +} + +static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) { + client_initialize_io_events(client, io_callback); + client_initialize_time_events(client); + + return 0; +} + +static int client_start_delayed(sd_dhcp_client *client) { + int r; + + assert_return(client, -EINVAL); + assert_return(client->event, -EINVAL); + assert_return(client->ifindex > 0, -EINVAL); + assert_return(client->fd < 0, -EBUSY); + assert_return(client->xid == 0, -EINVAL); + assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY); + + client->xid = random_u32(); + + r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); + if (r < 0) { + client_stop(client, r); + return r; + } + client->fd = r; + + if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT) + client->start_time = now(clock_boottime_or_monotonic()); + + return client_initialize_events(client, client_receive_message_raw); +} + +static int client_start(sd_dhcp_client *client) { + client->start_delay = 0; + return client_start_delayed(client); +} + +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); + + log_dhcp_client(client, "EXPIRED"); + + client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); + + /* lease was lost, start over if not freed or stopped in callback */ + if (client->state != DHCP_STATE_STOPPED) { + client_initialize(client); + client_start(client); + } + + return 0; +} + +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); + int r; + + assert(client); + + client->receive_message = sd_event_source_unref(client->receive_message); + client->fd = asynchronous_close(client->fd); + + client->state = DHCP_STATE_REBINDING; + client->attempt = 1; + + r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, + client->xid, client->mac_addr, + client->mac_addr_len, client->arp_type); + if (r < 0) { + client_stop(client, r); + return 0; + } + client->fd = r; + + return client_initialize_events(client, client_receive_message_raw); +} + +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp_client *client = userdata; + DHCP_CLIENT_DONT_DESTROY(client); + + client->state = DHCP_STATE_RENEWING; + client->attempt = 1; + + return client_initialize_time_events(client); +} + +static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) { + _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + int r; + + r = dhcp_lease_new(&lease); + if (r < 0) + return r; + + if (client->client_id_len) { + r = dhcp_lease_set_client_id(lease, + (uint8_t *) &client->client_id, + client->client_id_len); + if (r < 0) + return r; + } + + 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; + } + + lease->next_server = offer->siaddr; + lease->address = offer->yiaddr; + + if (lease->address == 0 || + lease->server_address == 0 || + lease->lifetime == 0) { + log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring"); + return -ENOMSG; + } + + if (!lease->have_subnet_mask) { + r = dhcp_lease_set_default_subnet_mask(lease); + if (r < 0) { + log_dhcp_client(client, "received lease lacks subnet " + "mask, and a fallback one can not be " + "generated, ignoring"); + return -ENOMSG; + } + } + + sd_dhcp_lease_unref(client->lease); + client->lease = lease; + lease = NULL; + + log_dhcp_client(client, "OFFER"); + + return 0; +} + +static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) { + int r; + + r = dhcp_option_parse(force, len, NULL, NULL, NULL); + if (r != DHCP_FORCERENEW) + return -ENOMSG; + + log_dhcp_client(client, "FORCERENEW"); + + return 0; +} + +static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { + _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + _cleanup_free_ char *error_message = NULL; + int r; + + r = dhcp_lease_new(&lease); + if (r < 0) + return r; + + if (client->client_id_len) { + r = dhcp_lease_set_client_id(lease, + (uint8_t *) &client->client_id, + client->client_id_len); + if (r < 0) + return r; + } + + r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message); + if (r == DHCP_NAK) { + log_dhcp_client(client, "NAK: %s", strna(error_message)); + return -EADDRNOTAVAIL; + } + + if (r != DHCP_ACK) { + log_dhcp_client(client, "received message was not an ACK, ignoring"); + return -ENOMSG; + } + + lease->next_server = ack->siaddr; + + lease->address = ack->yiaddr; + + if (lease->address == INADDR_ANY || + lease->server_address == INADDR_ANY || + lease->lifetime == 0) { + log_dhcp_client(client, "received lease lacks address, server " + "address or lease lifetime, ignoring"); + return -ENOMSG; + } + + if (lease->subnet_mask == INADDR_ANY) { + r = dhcp_lease_set_default_subnet_mask(lease); + if (r < 0) { + log_dhcp_client(client, "received lease lacks subnet " + "mask, and a fallback one can not be " + "generated, ignoring"); + return -ENOMSG; + } + } + + r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; + if (client->lease) { + if (client->lease->address != lease->address || + client->lease->subnet_mask != lease->subnet_mask || + client->lease->router != lease->router) { + r = SD_DHCP_CLIENT_EVENT_IP_CHANGE; + } else + r = SD_DHCP_CLIENT_EVENT_RENEW; + + client->lease = sd_dhcp_lease_unref(client->lease); + } + + client->lease = lease; + lease = NULL; + + log_dhcp_client(client, "ACK"); + + return r; +} + +static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) { + assert(client); + assert(client->request_sent); + assert(lifetime > 0); + + if (lifetime > 3) + lifetime -= 3; + else + lifetime = 0; + + return client->request_sent + (lifetime * USEC_PER_SEC * factor) + + + (random_u32() & 0x1fffff); +} + +static int client_set_lease_timeouts(sd_dhcp_client *client) { + usec_t time_now; + uint64_t lifetime_timeout; + uint64_t t2_timeout; + uint64_t t1_timeout; + char time_string[FORMAT_TIMESPAN_MAX]; + int r; + + assert(client); + assert(client->event); + assert(client->lease); + assert(client->lease->lifetime); + + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + client->timeout_expire = sd_event_source_unref(client->timeout_expire); + + /* don't set timers for infinite leases */ + if (client->lease->lifetime == 0xffffffff) + return 0; + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + assert(client->request_sent <= time_now); + + /* convert the various timeouts from relative (secs) to absolute (usecs) */ + lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1); + if (client->lease->t1 > 0 && client->lease->t2 > 0) { + /* both T1 and T2 are given */ + if (client->lease->t1 < client->lease->t2 && + client->lease->t2 < client->lease->lifetime) { + /* they are both valid */ + t2_timeout = client_compute_timeout(client, client->lease->t2, 1); + t1_timeout = client_compute_timeout(client, client->lease->t1, 1); + } else { + /* discard both */ + t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); + client->lease->t2 = (client->lease->lifetime * 7) / 8; + t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); + client->lease->t1 = client->lease->lifetime / 2; + } + } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) { + /* only T2 is given, and it is valid */ + t2_timeout = client_compute_timeout(client, client->lease->t2, 1); + t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); + client->lease->t1 = client->lease->lifetime / 2; + if (t2_timeout <= t1_timeout) { + /* the computed T1 would be invalid, so discard T2 */ + t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); + client->lease->t2 = (client->lease->lifetime * 7) / 8; + } + } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) { + /* only T1 is given, and it is valid */ + t1_timeout = client_compute_timeout(client, client->lease->t1, 1); + t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); + client->lease->t2 = (client->lease->lifetime * 7) / 8; + if (t2_timeout <= t1_timeout) { + /* the computed T2 would be invalid, so discard T1 */ + t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); + client->lease->t2 = client->lease->lifetime / 2; + } + } else { + /* fall back to the default timeouts */ + t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); + client->lease->t1 = client->lease->lifetime / 2; + t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); + client->lease->t2 = (client->lease->lifetime * 7) / 8; + } + + /* arm lifetime timeout */ + r = sd_event_add_time(client->event, &client->timeout_expire, + clock_boottime_or_monotonic(), + lifetime_timeout, 10 * USEC_PER_MSEC, + client_timeout_expire, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->timeout_expire, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); + if (r < 0) + return r; + + log_dhcp_client(client, "lease expires in %s", + 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) + return 0; + + /* arm T2 timeout */ + r = sd_event_add_time(client->event, + &client->timeout_t2, + clock_boottime_or_monotonic(), + t2_timeout, + 10 * USEC_PER_MSEC, + client_timeout_t2, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->timeout_t2, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); + if (r < 0) + return r; + + log_dhcp_client(client, "T2 expires in %s", + 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) + return 0; + + /* arm T1 timeout */ + r = sd_event_add_time(client->event, + &client->timeout_t1, + clock_boottime_or_monotonic(), + t1_timeout, 10 * USEC_PER_MSEC, + client_timeout_t1, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->timeout_t1, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); + if (r < 0) + return r; + + log_dhcp_client(client, "T1 expires in %s", + format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC)); + + return 0; +} + +static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) { + DHCP_CLIENT_DONT_DESTROY(client); + char time_string[FORMAT_TIMESPAN_MAX]; + int r = 0, notify_event = 0; + + assert(client); + assert(client->event); + assert(message); + + switch (client->state) { + case DHCP_STATE_SELECTING: + + r = client_handle_offer(client, message, len); + if (r >= 0) { + + client->timeout_resend = + sd_event_source_unref(client->timeout_resend); + + client->state = DHCP_STATE_REQUESTING; + client->attempt = 1; + + r = sd_event_add_time(client->event, + &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + if (r < 0) + goto error; + } else if (r == -ENOMSG) + /* invalid message, let's ignore it */ + return 0; + + break; + + case DHCP_STATE_REBOOTING: + case DHCP_STATE_REQUESTING: + case DHCP_STATE_RENEWING: + case DHCP_STATE_REBINDING: + + r = client_handle_ack(client, message, len); + if (r >= 0) { + client->start_delay = 0; + client->timeout_resend = + sd_event_source_unref(client->timeout_resend); + client->receive_message = + sd_event_source_unref(client->receive_message); + client->fd = asynchronous_close(client->fd); + + if (IN_SET(client->state, DHCP_STATE_REQUESTING, + DHCP_STATE_REBOOTING)) + notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; + else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE) + notify_event = r; + + client->state = DHCP_STATE_BOUND; + client->attempt = 1; + + client->last_addr = client->lease->address; + + r = client_set_lease_timeouts(client); + if (r < 0) { + log_dhcp_client(client, "could not set lease timeouts"); + goto error; + } + + r = dhcp_network_bind_udp_socket(client->lease->address, + DHCP_PORT_CLIENT); + if (r < 0) { + log_dhcp_client(client, "could not bind UDP socket"); + goto error; + } + + client->fd = r; + + client_initialize_io_events(client, client_receive_message_udp); + + if (notify_event) { + client_notify(client, notify_event); + if (client->state == DHCP_STATE_STOPPED) + return 0; + } + + } else if (r == -EADDRNOTAVAIL) { + /* got a NAK, let's restart the client */ + client->timeout_resend = + sd_event_source_unref(client->timeout_resend); + + r = client_initialize(client); + if (r < 0) + goto error; + + r = client_start_delayed(client); + if (r < 0) + goto error; + + log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, + client->start_delay, USEC_PER_SEC)); + + client->start_delay = CLAMP(client->start_delay * 2, + RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC); + + return 0; + } else if (r == -ENOMSG) + /* invalid message, let's ignore it */ + return 0; + + break; + + case DHCP_STATE_BOUND: + r = client_handle_forcerenew(client, message, len); + if (r >= 0) { + r = client_timeout_t1(NULL, 0, client); + if (r < 0) + goto error; + } else if (r == -ENOMSG) + /* invalid message, let's ignore it */ + return 0; + + break; + + case DHCP_STATE_INIT: + case DHCP_STATE_INIT_REBOOT: + + break; + + case DHCP_STATE_STOPPED: + r = -EINVAL; + goto error; + } + +error: + if (r < 0) + client_stop(client, r); + + return r; +} + +static int client_receive_message_udp( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata) { + + sd_dhcp_client *client = userdata; + _cleanup_free_ DHCPMessage *message = NULL; + const struct ether_addr zero_mac = {}; + const struct ether_addr *expected_chaddr = NULL; + uint8_t expected_hlen = 0; + ssize_t len, buflen; + + assert(s); + assert(client); + + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; + + message = malloc0(buflen); + if (!message) + return -ENOMEM; + + len = recv(fd, message, buflen, 0); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m"); + } + if ((size_t) len < sizeof(DHCPMessage)) { + 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"); + return 0; + } + + if (message->op != BOOTREPLY) { + 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"); + return 0; + } + + if (client->arp_type == ARPHRD_ETHER) { + expected_hlen = ETH_ALEN; + expected_chaddr = (const struct ether_addr *) &client->mac_addr; + } else { + /* 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); + return 0; + } + + if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { + log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); + return 0; + } + + if (client->state != DHCP_STATE_BOUND && + 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", + be32toh(message->xid), client->xid); + return 0; + } + + return client_handle_message(client, message, len); +} + +static int client_receive_message_raw( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata) { + + sd_dhcp_client *client = userdata; + _cleanup_free_ DHCPPacket *packet = NULL; + uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; + struct iovec iov = {}; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + bool checksum = true; + ssize_t buflen, len; + int r; + + assert(s); + assert(client); + + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; + + packet = malloc0(buflen); + if (!packet) + return -ENOMEM; + + iov.iov_base = packet; + iov.iov_len = buflen; + + len = recvmsg(fd, &msg, 0); + if (len < 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; + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_AUXDATA && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { + struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); + + checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); + break; + } + } + + r = dhcp_packet_verify_headers(packet, len, checksum); + if (r < 0) + return 0; + + len -= DHCP_IP_UDP_SIZE; + + return client_handle_message(client, &packet->dhcp, len); +} + +int sd_dhcp_client_start(sd_dhcp_client *client) { + int r; + + assert_return(client, -EINVAL); + + r = client_initialize(client); + if (r < 0) + return r; + + if (client->last_addr) + client->state = DHCP_STATE_INIT_REBOOT; + + r = client_start(client); + if (r >= 0) + log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex); + + return r; +} + +int sd_dhcp_client_stop(sd_dhcp_client *client) { + DHCP_CLIENT_DONT_DESTROY(client); + + assert_return(client, -EINVAL); + + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + client->state = DHCP_STATE_STOPPED; + + return 0; +} + +int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) { + int r; + + assert_return(client, -EINVAL); + assert_return(!client->event, -EBUSY); + + if (event) + client->event = sd_event_ref(event); + else { + r = sd_event_default(&client->event); + if (r < 0) + return 0; + } + + client->event_priority = priority; + + return 0; +} + +int sd_dhcp_client_detach_event(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + + client->event = sd_event_unref(client->event); + + return 0; +} + +sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { + assert_return(client, NULL); + + return client->event; +} + +sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref++; + + return client; +} + +sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref--; + + if (client->n_ref > 0) + return NULL; + + log_dhcp_client(client, "FREE"); + + client_initialize(client); + + client->receive_message = sd_event_source_unref(client->receive_message); + + sd_dhcp_client_detach_event(client); + + sd_dhcp_lease_unref(client->lease); + + free(client->req_opts); + free(client->hostname); + free(client->vendor_class_identifier); + free(client); + + return NULL; +} + +int sd_dhcp_client_new(sd_dhcp_client **ret) { + _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; + + assert_return(ret, -EINVAL); + + client = new0(sd_dhcp_client, 1); + if (!client) + return -ENOMEM; + + client->n_ref = 1; + client->state = DHCP_STATE_INIT; + client->ifindex = -1; + client->fd = -1; + client->attempt = 1; + client->mtu = DHCP_DEFAULT_MIN_SIZE; + + client->req_opts_size = ELEMENTSOF(default_req_opts); + client->req_opts = memdup(default_req_opts, client->req_opts_size); + if (!client->req_opts) + return -ENOMEM; + + *ret = client; + client = NULL; + + return 0; +} diff --git a/src/libsystemd-network/src/sd-dhcp-lease.c b/src/libsystemd-network/src/sd-dhcp-lease.c new file mode 100644 index 0000000000..0edc075b58 --- /dev/null +++ b/src/libsystemd-network/src/sd-dhcp-lease.c @@ -0,0 +1,1177 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hexdecoct.h" +#include "basic/hostname-util.h" +#include "basic/in-addr-util.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/unaligned.h" +#include "shared/dns-domain.h" +#include "systemd-network/dhcp-lease-internal.h" +#include "systemd-network/dhcp-protocol.h" +#include "systemd-network/network-internal.h" +#include "systemd-network/sd-dhcp-lease.h" + +int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->address == 0) + return -ENODATA; + + addr->s_addr = lease->address; + return 0; +} + +int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (!lease->have_broadcast) + return -ENODATA; + + addr->s_addr = lease->broadcast; + return 0; +} + +int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { + assert_return(lease, -EINVAL); + assert_return(lifetime, -EINVAL); + + if (lease->lifetime <= 0) + return -ENODATA; + + *lifetime = lease->lifetime; + return 0; +} + +int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) { + assert_return(lease, -EINVAL); + assert_return(t1, -EINVAL); + + if (lease->t1 <= 0) + return -ENODATA; + + *t1 = lease->t1; + return 0; +} + +int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) { + assert_return(lease, -EINVAL); + assert_return(t2, -EINVAL); + + if (lease->t2 <= 0) + return -ENODATA; + + *t2 = lease->t2; + return 0; +} + +int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { + assert_return(lease, -EINVAL); + assert_return(mtu, -EINVAL); + + if (lease->mtu <= 0) + return -ENODATA; + + *mtu = lease->mtu; + return 0; +} + +int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->dns_size <= 0) + return -ENODATA; + + *addr = lease->dns; + return (int) lease->dns_size; +} + +int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->ntp_size <= 0) + return -ENODATA; + + *addr = lease->ntp; + return (int) lease->ntp_size; +} + +int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { + assert_return(lease, -EINVAL); + assert_return(domainname, -EINVAL); + + if (!lease->domainname) + return -ENODATA; + + *domainname = lease->domainname; + return 0; +} + +int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) { + assert_return(lease, -EINVAL); + assert_return(hostname, -EINVAL); + + if (!lease->hostname) + return -ENODATA; + + *hostname = lease->hostname; + return 0; +} + +int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { + assert_return(lease, -EINVAL); + assert_return(root_path, -EINVAL); + + if (!lease->root_path) + return -ENODATA; + + *root_path = lease->root_path; + return 0; +} + +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->router == 0) + return -ENODATA; + + addr->s_addr = lease->router; + return 0; +} + +int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (!lease->have_subnet_mask) + return -ENODATA; + + addr->s_addr = lease->subnet_mask; + return 0; +} + +int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->server_address == 0) + return -ENODATA; + + addr->s_addr = lease->server_address; + return 0; +} + +int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (lease->next_server == 0) + return -ENODATA; + + addr->s_addr = lease->next_server; + return 0; +} + +/* + * 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; + + 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; +} + +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { + assert_return(lease, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_len, -EINVAL); + + if (lease->vendor_specific_len <= 0) + return -ENODATA; + + *data = lease->vendor_specific; + *data_len = lease->vendor_specific_len; + return 0; +} + +sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref++; + + return lease; +} + +sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref--; + + if (lease->n_ref > 0) + return NULL; + + while (lease->private_options) { + struct sd_dhcp_raw_option *option = lease->private_options; + + LIST_REMOVE(options, lease->private_options, option); + + free(option->data); + free(option); + } + + free(lease->hostname); + free(lease->domainname); + free(lease->dns); + free(lease->ntp); + free(lease->static_route); + free(lease->client_id); + free(lease->vendor_specific); + free(lease); + + return NULL; +} + +static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { + assert(option); + assert(ret); + + if (len != 4) + return -EINVAL; + + *ret = unaligned_read_be32((be32_t*) option); + if (*ret < min) + *ret = min; + + return 0; +} + +static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { + assert(option); + assert(ret); + + if (len != 2) + return -EINVAL; + + *ret = unaligned_read_be16((be16_t*) option); + if (*ret < min) + *ret = min; + + return 0; +} + +static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) { + assert(option); + assert(ret); + + if (len != 4) + return -EINVAL; + + memcpy(ret, option, 4); + return 0; +} + +static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { + assert(option); + assert(ret); + + if (len <= 0) + *ret = mfree(*ret); + else { + char *string; + + /* + * 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); + if (!string) + return -ENOMEM; + + free(*ret); + *ret = string; + } + + return 0; +} + +static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { + _cleanup_free_ char *name = NULL, *normalized = NULL; + int r; + + assert(option); + assert(ret); + + r = lease_parse_string(option, len, &name); + if (r < 0) + return r; + if (!name) { + *ret = mfree(*ret); + return 0; + } + + r = dns_name_normalize(name, &normalized); + if (r < 0) + return r; + + if (is_localhost(normalized)) + return -EINVAL; + + if (dns_name_is_root(normalized)) + return -EINVAL; + + free(*ret); + *ret = normalized; + normalized = NULL; + + return 0; +} + +static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { + assert(option); + assert(ret); + assert(n_ret); + + if (len <= 0) { + *ret = mfree(*ret); + *n_ret = 0; + } else { + size_t n_addresses; + struct in_addr *addresses; + + if (len % 4 != 0) + return -EINVAL; + + n_addresses = len / 4; + + addresses = newdup(struct in_addr, option, n_addresses); + if (!addresses) + return -ENOMEM; + + free(*ret); + *ret = addresses; + *n_ret = n_addresses; + } + + return 0; +} + +static int lease_parse_routes( + const uint8_t *option, size_t len, + struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { + + struct in_addr addr; + + assert(option || len <= 0); + assert(routes); + assert(routes_size); + assert(routes_allocated); + + if (len <= 0) + return 0; + + if (len % 8 != 0) + return -EINVAL; + + if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8))) + return -ENOMEM; + + while (len >= 8) { + struct sd_dhcp_route *route = *routes + *routes_size; + int r; + + r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); + if (r < 0) { + log_debug("Failed to determine destination prefix length from class based IP, ignoring"); + continue; + } + + assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0); + route->dst_addr = inet_makeaddr(inet_netof(addr), 0); + option += 4; + + assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + option += 4; + + len -= 8; + (*routes_size)++; + } + + return 0; +} + +/* parses RFC3442 Classless Static Route Option */ +static int lease_parse_classless_routes( + const uint8_t *option, size_t len, + struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { + + assert(option || len <= 0); + assert(routes); + assert(routes_size); + assert(routes_allocated); + + if (len <= 0) + return 0; + + /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ + + while (len > 0) { + uint8_t dst_octets; + struct sd_dhcp_route *route; + + if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1)) + return -ENOMEM; + + route = *routes + *routes_size; + + dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); + route->dst_prefixlen = *option; + option++; + len--; + + /* can't have more than 4 octets in IPv4 */ + if (dst_octets > 4 || len < dst_octets) + return -EINVAL; + + route->dst_addr.s_addr = 0; + memcpy(&route->dst_addr.s_addr, option, dst_octets); + option += dst_octets; + len -= dst_octets; + + if (len < 4) + return -EINVAL; + + assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + option += 4; + len -= 4; + + (*routes_size)++; + } + + return 0; +} + +int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) { + sd_dhcp_lease *lease = userdata; + int r; + + assert(lease); + + switch(code) { + + 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 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 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"); + else + lease->have_subnet_mask = true; + break; + + 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"); + else + lease->have_broadcast = true; + break; + + case SD_DHCP_OPTION_ROUTER: + if (len >= 4) { + r = lease_parse_be32(option, 4, &lease->router); + if (r < 0) + log_debug_errno(r, "Failed to parse router address, ignoring: %m"); + } + break; + + 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 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 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 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 SD_DHCP_OPTION_DOMAIN_NAME: + r = lease_parse_domain(option, len, &lease->domainname); + if (r < 0) { + log_debug_errno(r, "Failed to parse domain name, ignoring: %m"); + return 0; + } + + break; + + case SD_DHCP_OPTION_HOST_NAME: + r = lease_parse_domain(option, len, &lease->hostname); + if (r < 0) { + log_debug_errno(r, "Failed to parse host name, ignoring: %m"); + return 0; + } + + break; + + 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 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 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 SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + r = lease_parse_classless_routes( + option, len, + &lease->static_route, + &lease->static_route_size, + &lease->static_route_allocated); + if (r < 0) + log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); + break; + + case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { + _cleanup_free_ char *tz = NULL; + + r = lease_parse_string(option, len, &tz); + if (r < 0) { + log_debug_errno(r, "Failed to parse timezone option, ignoring: %m"); + return 0; + } + + if (!timezone_is_valid(tz)) { + log_debug_errno(r, "Timezone is not valid, ignoring: %m"); + return 0; + } + + free(lease->timezone); + lease->timezone = tz; + tz = NULL; + + break; + } + + case SD_DHCP_OPTION_VENDOR_SPECIFIC: + + if (len <= 0) + lease->vendor_specific = mfree(lease->vendor_specific); + else { + void *p; + + p = memdup(option, len); + if (!p) + return -ENOMEM; + + free(lease->vendor_specific); + lease->vendor_specific = p; + } + + lease->vendor_specific_len = len; + break; + + 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; + + break; + + default: + log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code); + break; + } + + return 0; +} + +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { + struct sd_dhcp_raw_option *cur, *option; + + assert(lease); + + LIST_FOREACH(options, cur, lease->private_options) { + if (tag < cur->tag) + break; + if (tag == cur->tag) { + log_debug("Ignoring duplicate option, tagged %i.", tag); + return 0; + } + } + + option = new(struct sd_dhcp_raw_option, 1); + if (!option) + return -ENOMEM; + + option->tag = tag; + option->length = len; + option->data = memdup(data, len); + if (!option->data) { + free(option); + return -ENOMEM; + } + + LIST_INSERT_BEFORE(options, lease->private_options, cur, option); + return 0; +} + +int dhcp_lease_new(sd_dhcp_lease **ret) { + sd_dhcp_lease *lease; + + lease = new0(sd_dhcp_lease, 1); + if (!lease) + return -ENOMEM; + + lease->router = INADDR_ANY; + lease->n_ref = 1; + + *ret = lease; + return 0; +} + +int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + struct sd_dhcp_raw_option *option; + struct in_addr address; + const struct in_addr *addresses; + const void *client_id, *data; + size_t client_id_len, data_len; + const char *string; + uint16_t mtu; + _cleanup_free_ sd_dhcp_route **routes = NULL; + uint32_t t1, t2, lifetime; + int r; + + assert(lease); + assert(lease_file); + + r = fopen_temporary(lease_file, &f, &temp_path); + if (r < 0) + goto fail; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n"); + + r = sd_dhcp_lease_get_address(lease, &address); + if (r >= 0) + fprintf(f, "ADDRESS=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_netmask(lease, &address); + if (r >= 0) + fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_router(lease, &address); + if (r >= 0) + fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_server_identifier(lease, &address); + if (r >= 0) + fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_next_server(lease, &address); + if (r >= 0) + fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_broadcast(lease, &address); + if (r >= 0) + fprintf(f, "BROADCAST=%s\n", inet_ntoa(address)); + + r = sd_dhcp_lease_get_mtu(lease, &mtu); + if (r >= 0) + fprintf(f, "MTU=%" PRIu16 "\n", mtu); + + r = sd_dhcp_lease_get_t1(lease, &t1); + if (r >= 0) + fprintf(f, "T1=%" PRIu32 "\n", t1); + + r = sd_dhcp_lease_get_t2(lease, &t2); + if (r >= 0) + fprintf(f, "T2=%" PRIu32 "\n", t2); + + r = sd_dhcp_lease_get_lifetime(lease, &lifetime); + if (r >= 0) + fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime); + + r = sd_dhcp_lease_get_dns(lease, &addresses); + if (r > 0) { + fputs("DNS=", f); + serialize_in_addrs(f, addresses, r); + fputs("\n", f); + } + + r = sd_dhcp_lease_get_ntp(lease, &addresses); + if (r > 0) { + fputs("NTP=", f); + serialize_in_addrs(f, addresses, r); + fputs("\n", f); + } + + r = sd_dhcp_lease_get_domainname(lease, &string); + if (r >= 0) + fprintf(f, "DOMAINNAME=%s\n", string); + + r = sd_dhcp_lease_get_hostname(lease, &string); + if (r >= 0) + fprintf(f, "HOSTNAME=%s\n", string); + + r = sd_dhcp_lease_get_root_path(lease, &string); + if (r >= 0) + fprintf(f, "ROOT_PATH=%s\n", string); + + r = sd_dhcp_lease_get_routes(lease, &routes); + if (r > 0) + serialize_dhcp_routes(f, "ROUTES", routes, r); + + r = sd_dhcp_lease_get_timezone(lease, &string); + if (r >= 0) + fprintf(f, "TIMEZONE=%s\n", string); + + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); + if (r >= 0) { + _cleanup_free_ char *client_id_hex = NULL; + + client_id_hex = hexmem(client_id, client_id_len); + if (!client_id_hex) { + r = -ENOMEM; + goto fail; + } + fprintf(f, "CLIENTID=%s\n", client_id_hex); + } + + r = sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len); + if (r >= 0) { + _cleanup_free_ char *option_hex = NULL; + + option_hex = hexmem(data, data_len); + if (!option_hex) { + r = -ENOMEM; + goto fail; + } + fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); + } + + LIST_FOREACH(options, option, lease->private_options) { + char key[strlen("OPTION_000")+1]; + + xsprintf(key, "OPTION_%" PRIu8, option->tag); + r = serialize_dhcp_option(f, key, option->data, option->length); + if (r < 0) + goto fail; + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + if (rename(temp_path, lease_file) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + if (temp_path) + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save lease data %s: %m", lease_file); +} + +int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { + + _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + _cleanup_free_ char + *address = NULL, + *router = NULL, + *netmask = NULL, + *server_address = NULL, + *next_server = NULL, + *broadcast = NULL, + *dns = NULL, + *ntp = NULL, + *mtu = NULL, + *routes = NULL, + *client_id_hex = NULL, + *vendor_specific_hex = NULL, + *lifetime = NULL, + *t1 = NULL, + *t2 = NULL, + *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {}; + + int r, i; + + assert(lease_file); + assert(ret); + + r = dhcp_lease_new(&lease); + if (r < 0) + return r; + + r = parse_env_file(lease_file, NEWLINE, + "ADDRESS", &address, + "ROUTER", &router, + "NETMASK", &netmask, + "SERVER_IDENTIFIER", &server_address, + "NEXT_SERVER", &next_server, + "BROADCAST", &broadcast, + "DNS", &dns, + "NTP", &ntp, + "MTU", &mtu, + "DOMAINNAME", &lease->domainname, + "HOSTNAME", &lease->hostname, + "ROOT_PATH", &lease->root_path, + "ROUTES", &routes, + "CLIENTID", &client_id_hex, + "TIMEZONE", &lease->timezone, + "VENDOR_SPECIFIC", &vendor_specific_hex, + "LIFETIME", &lifetime, + "T1", &t1, + "T2", &t2, + "OPTION_224", &options[0], + "OPTION_225", &options[1], + "OPTION_226", &options[2], + "OPTION_227", &options[3], + "OPTION_228", &options[4], + "OPTION_229", &options[5], + "OPTION_230", &options[6], + "OPTION_231", &options[7], + "OPTION_232", &options[8], + "OPTION_233", &options[9], + "OPTION_234", &options[10], + "OPTION_235", &options[11], + "OPTION_236", &options[12], + "OPTION_237", &options[13], + "OPTION_238", &options[14], + "OPTION_239", &options[15], + "OPTION_240", &options[16], + "OPTION_241", &options[17], + "OPTION_242", &options[18], + "OPTION_243", &options[19], + "OPTION_244", &options[20], + "OPTION_245", &options[21], + "OPTION_246", &options[22], + "OPTION_247", &options[23], + "OPTION_248", &options[24], + "OPTION_249", &options[25], + "OPTION_250", &options[26], + "OPTION_251", &options[27], + "OPTION_252", &options[28], + "OPTION_253", &options[29], + "OPTION_254", &options[30], + NULL); + if (r < 0) + return r; + + if (address) { + r = inet_pton(AF_INET, address, &lease->address); + if (r <= 0) + log_debug("Failed to parse address %s, ignoring.", address); + } + + if (router) { + r = inet_pton(AF_INET, router, &lease->router); + if (r <= 0) + 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("Failed to parse netmask %s, ignoring.", netmask); + else + lease->have_subnet_mask = true; + } + + if (server_address) { + r = inet_pton(AF_INET, server_address, &lease->server_address); + if (r <= 0) + 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("Failed to parse next server %s, ignoring.", next_server); + } + + if (broadcast) { + r = inet_pton(AF_INET, broadcast, &lease->broadcast); + if (r <= 0) + log_debug("Failed to parse broadcast address %s, ignoring.", broadcast); + else + lease->have_broadcast = true; + } + + if (dns) { + r = deserialize_in_addrs(&lease->dns, dns); + if (r < 0) + log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns); + else + lease->dns_size = r; + } + + if (ntp) { + r = deserialize_in_addrs(&lease->ntp, ntp); + if (r < 0) + log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp); + else + lease->ntp_size = r; + } + + if (mtu) { + r = safe_atou16(mtu, &lease->mtu); + if (r < 0) + log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu); + } + + if (routes) { + r = deserialize_dhcp_routes( + &lease->static_route, + &lease->static_route_size, + &lease->static_route_allocated, + routes); + if (r < 0) + log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); + } + + if (lifetime) { + r = safe_atou32(lifetime, &lease->lifetime); + if (r < 0) + log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime); + } + + if (t1) { + r = safe_atou32(t1, &lease->t1); + if (r < 0) + log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1); + } + + if (t2) { + r = safe_atou32(t2, &lease->t2); + if (r < 0) + log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2); + } + + if (client_id_hex) { + r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex); + if (r < 0) + log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex); + } + + if (vendor_specific_hex) { + r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex); + if (r < 0) + log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); + } + + for (i = 0; i <= SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE; i++) { + _cleanup_free_ void *data = NULL; + size_t len; + + if (!options[i]) + continue; + + r = deserialize_dhcp_option(&data, &len, options[i]); + if (r < 0) { + log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]); + continue; + } + + r = dhcp_lease_insert_private_option(lease, SD_DHCP_OPTION_PRIVATE_BASE + i, data, len); + if (r < 0) + return r; + } + + *ret = lease; + lease = NULL; + + return 0; +} + +int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { + struct in_addr address, mask; + int r; + + assert(lease); + + if (lease->address == 0) + return -ENODATA; + + address.s_addr = lease->address; + + /* fall back to the default subnet masks based on address class */ + r = in_addr_default_subnet_mask(&address, &mask); + if (r < 0) + return r; + + lease->subnet_mask = mask.s_addr; + lease->have_subnet_mask = true; + + return 0; +} + +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) { + assert_return(lease, -EINVAL); + assert_return(client_id, -EINVAL); + assert_return(client_id_len, -EINVAL); + + if (!lease->client_id) + return -ENODATA; + + *client_id = lease->client_id; + *client_id_len = lease->client_id_len; + + return 0; +} + +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) { + assert_return(lease, -EINVAL); + assert_return(client_id || client_id_len <= 0, -EINVAL); + + if (client_id_len <= 0) + lease->client_id = mfree(lease->client_id); + else { + void *p; + + p = memdup(client_id, client_id_len); + if (!p) + return -ENOMEM; + + free(lease->client_id); + lease->client_id = p; + lease->client_id_len = client_id_len; + } + + return 0; +} + +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) { + assert_return(lease, -EINVAL); + assert_return(tz, -EINVAL); + + if (!lease->timezone) + return -ENODATA; + + *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/src/sd-dhcp-server.c b/src/libsystemd-network/src/sd-dhcp-server.c new file mode 100644 index 0000000000..4a6c14e80c --- /dev/null +++ b/src/libsystemd-network/src/sd-dhcp-server.c @@ -0,0 +1,1175 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/siphash24.h" +#include "basic/string-util.h" +#include "basic/unaligned.h" +#include "systemd-network/dhcp-internal.h" +#include "systemd-network/dhcp-server-internal.h" +#include "systemd-network/sd-dhcp-server.h" + +#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR +#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) + +/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet + * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address + * moreover, the server's own address may be in the pool, and is in that case reserved in order not to + * accidentally hand it out */ +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) { + struct in_addr netmask_addr; + be32_t netmask; + uint32_t server_off, broadcast_off, size_max; + + assert_return(server, -EINVAL); + assert_return(address, -EINVAL); + assert_return(address->s_addr != INADDR_ANY, -EINVAL); + assert_return(prefixlen <= 32, -ERANGE); + assert_return(server->address == INADDR_ANY, -EBUSY); + + assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); + netmask = netmask_addr.s_addr; + + server_off = be32toh(address->s_addr & ~netmask); + broadcast_off = be32toh(~netmask); + + /* the server address cannot be the subnet address */ + assert_return(server_off != 0, -ERANGE); + + /* nor the broadcast address */ + assert_return(server_off != broadcast_off, -ERANGE); + + /* 0 offset means we should set a default, we skip the first (subnet) address + and take the next one */ + if (offset == 0) + offset = 1; + + size_max = (broadcast_off + 1) /* the number of addresses in the subnet */ + - offset /* exclude the addresses before the offset */ + - 1; /* exclude the last (broadcast) address */ + + /* The pool must contain at least one address */ + assert_return(size_max >= 1, -ERANGE); + + if (size != 0) + assert_return(size <= size_max, -ERANGE); + else + size = size_max; + + server->bound_leases = new0(DHCPLease*, size); + if (!server->bound_leases) + return -ENOMEM; + + server->pool_offset = offset; + server->pool_size = size; + + server->address = address->s_addr; + server->netmask = netmask; + server->subnet = address->s_addr & netmask; + + if (server_off >= offset && server_off - offset < size) + server->bound_leases[server_off - offset] = &server->invalid_lease; + + return 0; +} + +int sd_dhcp_server_is_running(sd_dhcp_server *server) { + assert_return(server, false); + + return !!server->receive_message; +} + +sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) { + + if (!server) + return NULL; + + assert(server->n_ref >= 1); + server->n_ref++; + + return server; +} + +void client_id_hash_func(const void *p, struct siphash *state) { + const DHCPClientId *id = p; + + assert(id); + assert(id->length); + assert(id->data); + + 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) { + const DHCPClientId *a, *b; + + a = _a; + b = _b; + + assert(!a->length || a->data); + assert(!b->length || b->data); + + if (a->length != b->length) + return a->length < b->length ? -1 : 1; + + return memcmp(a->data, b->data, a->length); +} + +static const struct hash_ops client_id_hash_ops = { + .hash = client_id_hash_func, + .compare = client_id_compare_func +}; + +static void dhcp_lease_free(DHCPLease *lease) { + if (!lease) + return; + + free(lease->client_id.data); + free(lease); +} + +sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { + DHCPLease *lease; + + if (!server) + return NULL; + + assert(server->n_ref >= 1); + server->n_ref--; + + if (server->n_ref > 0) + return NULL; + + log_dhcp_server(server, "UNREF"); + + sd_dhcp_server_stop(server); + + sd_event_unref(server->event); + + free(server->timezone); + free(server->dns); + free(server->ntp); + + while ((lease = hashmap_steal_first(server->leases_by_client_id))) + dhcp_lease_free(lease); + hashmap_free(server->leases_by_client_id); + + free(server->bound_leases); + free(server); + + return NULL; +} + +int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + + assert_return(ret, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + + server = new0(sd_dhcp_server, 1); + if (!server) + return -ENOMEM; + + server->n_ref = 1; + server->fd_raw = -1; + server->fd = -1; + server->address = htobe32(INADDR_ANY); + server->netmask = htobe32(INADDR_ANY); + server->ifindex = ifindex; + server->leases_by_client_id = hashmap_new(&client_id_hash_ops); + server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC); + server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC); + + *ret = server; + server = NULL; + + return 0; +} + +int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) { + int r; + + assert_return(server, -EINVAL); + assert_return(!server->event, -EBUSY); + + if (event) + server->event = sd_event_ref(event); + else { + r = sd_event_default(&server->event); + if (r < 0) + return r; + } + + server->event_priority = priority; + + return 0; +} + +int sd_dhcp_server_detach_event(sd_dhcp_server *server) { + assert_return(server, -EINVAL); + + server->event = sd_event_unref(server->event); + + return 0; +} + +sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) { + assert_return(server, NULL); + + return server->event; +} + +int sd_dhcp_server_stop(sd_dhcp_server *server) { + assert_return(server, -EINVAL); + + server->receive_message = + sd_event_source_unref(server->receive_message); + + server->fd_raw = safe_close(server->fd_raw); + server->fd = safe_close(server->fd); + + log_dhcp_server(server, "STOPPED"); + + return 0; +} + +static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, + DHCPPacket *packet, size_t len) { + union sockaddr_union link = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETH_P_IP), + .ll.sll_ifindex = server->ifindex, + .ll.sll_halen = ETH_ALEN, + }; + + assert(server); + assert(server->ifindex > 0); + assert(server->address); + assert(packet); + assert(len > sizeof(DHCPPacket)); + + memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN); + + dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER, + packet->dhcp.yiaddr, + DHCP_PORT_CLIENT, len); + + return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len); +} + +static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, + uint16_t destination_port, + DHCPMessage *message, size_t len) { + union sockaddr_union dest = { + .in.sin_family = AF_INET, + .in.sin_port = htobe16(destination_port), + .in.sin_addr.s_addr = destination, + }; + struct iovec iov = { + .iov_base = message, + .iov_len = len, + }; + uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))] = {}; + struct msghdr msg = { + .msg_name = &dest, + .msg_namelen = sizeof(dest.in), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + struct in_pktinfo *pktinfo; + int r; + + assert(server); + assert(server->fd > 0); + assert(message); + assert(len > sizeof(DHCPMessage)); + + cmsg = CMSG_FIRSTHDR(&msg); + assert(cmsg); + + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + /* we attach source interface and address info to the message + rather than binding the socket. This will be mostly useful + when we gain support for arbitrary number of server addresses + */ + pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg); + assert(pktinfo); + + pktinfo->ipi_ifindex = server->ifindex; + pktinfo->ipi_spec_dst.s_addr = server->address; + + r = sendmsg(server->fd, &msg, 0); + if (r < 0) + return -errno; + + return 0; +} + +static bool requested_broadcast(DHCPRequest *req) { + assert(req); + + return req->message->flags & htobe16(0x8000); +} + +int dhcp_server_send_packet(sd_dhcp_server *server, + DHCPRequest *req, DHCPPacket *packet, + int type, size_t optoffset) { + be32_t destination = INADDR_ANY; + uint16_t destination_port = DHCP_PORT_CLIENT; + int r; + + assert(server); + assert(req); + assert(req->max_optlen); + assert(optoffset <= req->max_optlen); + assert(packet); + + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, + SD_DHCP_OPTION_SERVER_IDENTIFIER, + 4, &server->address); + if (r < 0) + return r; + + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + /* RFC 2131 Section 4.1 + + If the ’giaddr’ field in a DHCP message from a client is non-zero, + the server sends any return messages to the ’DHCP server’ port on the + BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’ + field is zero and the ’ciaddr’ field is nonzero, then the server + unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’. + If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is + set, then the server broadcasts DHCPOFFER and DHCPACK messages to + 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and + ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK + messages to the client’s hardware address and ’yiaddr’ address. In + all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK + messages to 0xffffffff. + + Section 4.3.2 + + If ’giaddr’ is set in the DHCPREQUEST message, the client is on a + different subnet. The server MUST set the broadcast bit in the + DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the + client, because the client may not have a correct network address + or subnet mask, and the client may not be answering ARP requests. + */ + if (req->message->giaddr) { + destination = req->message->giaddr; + destination_port = DHCP_PORT_SERVER; + if (type == DHCP_NAK) + packet->dhcp.flags = htobe16(0x8000); + } else if (req->message->ciaddr && type != DHCP_NAK) + destination = req->message->ciaddr; + + if (destination != INADDR_ANY) + return dhcp_server_send_udp(server, destination, + destination_port, &packet->dhcp, + sizeof(DHCPMessage) + optoffset); + else if (requested_broadcast(req) || type == DHCP_NAK) + return dhcp_server_send_udp(server, INADDR_BROADCAST, + destination_port, &packet->dhcp, + sizeof(DHCPMessage) + optoffset); + else + /* we cannot send UDP packet to specific MAC address when the + address is not yet configured, so must fall back to raw + packets */ + return dhcp_server_send_unicast_raw(server, packet, + sizeof(DHCPPacket) + optoffset); +} + +static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret, + uint8_t type, size_t *_optoffset, + DHCPRequest *req) { + _cleanup_free_ DHCPPacket *packet = NULL; + size_t optoffset = 0; + int r; + + assert(server); + assert(ret); + assert(_optoffset); + assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)); + + packet = malloc0(sizeof(DHCPPacket) + req->max_optlen); + if (!packet) + return -ENOMEM; + + r = dhcp_message_init(&packet->dhcp, BOOTREPLY, + be32toh(req->message->xid), type, ARPHRD_ETHER, + req->max_optlen, &optoffset); + if (r < 0) + return r; + + packet->dhcp.flags = req->message->flags; + packet->dhcp.giaddr = req->message->giaddr; + memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN); + + *_optoffset = optoffset; + *ret = packet; + packet = NULL; + + return 0; +} + +static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, + be32_t address) { + _cleanup_free_ DHCPPacket *packet = NULL; + size_t offset; + be32_t lease_time; + int r; + + r = server_message_init(server, &packet, DHCP_OFFER, &offset, req); + if (r < 0) + return r; + + packet->dhcp.yiaddr = address; + + lease_time = htobe32(req->lifetime); + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + 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, + SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); + if (r < 0) + return r; + + if (server->emit_router) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_ROUTER, 4, &server->address); + if (r < 0) + return r; + } + + r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset); + if (r < 0) + return r; + + return 0; +} + +static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, + be32_t address) { + _cleanup_free_ DHCPPacket *packet = NULL; + size_t offset; + be32_t lease_time; + int r; + + r = server_message_init(server, &packet, DHCP_ACK, &offset, req); + if (r < 0) + return r; + + packet->dhcp.yiaddr = address; + + lease_time = htobe32(req->lifetime); + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + 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, + SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask); + if (r < 0) + return r; + + if (server->emit_router) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + 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, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, + sizeof(struct in_addr) * server->n_dns, server->dns); + if (r < 0) + return r; + } + + if (server->n_ntp > 0) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_NTP_SERVER, + sizeof(struct in_addr) * server->n_ntp, server->ntp); + if (r < 0) + return r; + } + + if (server->timezone) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_NEW_TZDB_TIMEZONE, + strlen(server->timezone), server->timezone); + if (r < 0) + return r; + } + + r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset); + if (r < 0) + return r; + + return 0; +} + +static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) { + _cleanup_free_ DHCPPacket *packet = NULL; + size_t offset; + int r; + + r = server_message_init(server, &packet, DHCP_NAK, &offset, req); + if (r < 0) + return r; + + return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset); +} + +static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, + be32_t gateway, uint8_t chaddr[]) { + _cleanup_free_ DHCPPacket *packet = NULL; + size_t optoffset = 0; + int r; + + assert(server); + assert(address != INADDR_ANY); + assert(chaddr); + + packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE); + if (!packet) + return -ENOMEM; + + r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0, + DHCP_FORCERENEW, ARPHRD_ETHER, + DHCP_MIN_OPTIONS_SIZE, &optoffset); + if (r < 0) + return r; + + r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE, + &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN); + + r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT, + &packet->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + return 0; +} + +static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) { + DHCPRequest *req = userdata; + + assert(req); + + switch(code) { + case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: + if (len == 4) + req->lifetime = unaligned_read_be32(option); + + break; + case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS: + if (len == 4) + memcpy(&req->requested_ip, option, sizeof(be32_t)); + + break; + case SD_DHCP_OPTION_SERVER_IDENTIFIER: + if (len == 4) + memcpy(&req->server_id, option, sizeof(be32_t)); + + break; + case SD_DHCP_OPTION_CLIENT_IDENTIFIER: + if (len >= 2) { + uint8_t *data; + + data = memdup(option, len); + if (!data) + return -ENOMEM; + + free(req->client_id.data); + req->client_id.data = data; + req->client_id.length = len; + } + + break; + case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE: + + if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket)) + req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket); + + break; + } + + return 0; +} + +static void dhcp_request_free(DHCPRequest *req) { + if (!req) + return; + + free(req->client_id.data); + free(req); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free); +#define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep) + +static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) { + assert(req); + assert(message); + + req->message = message; + + /* set client id based on MAC address if client did not send an explicit + one */ + if (!req->client_id.data) { + void *data; + + data = malloc0(ETH_ALEN + 1); + if (!data) + return -ENOMEM; + + ((uint8_t*) data)[0] = 0x01; + memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN); + + req->client_id.length = ETH_ALEN + 1; + req->client_id.data = data; + } + + if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE) + req->max_optlen = DHCP_MIN_OPTIONS_SIZE; + + if (req->lifetime <= 0) + req->lifetime = MAX(1ULL, server->default_lease_time); + + if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time) + req->lifetime = server->max_lease_time; + + return 0; +} + +static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { + assert(server); + + if (!server->pool_size) + return -EINVAL; + + if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) || + be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size))) + return -ERANGE; + + return be32toh(requested_ip & ~server->netmask) - server->pool_offset; +} + +#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) + +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; + + assert(server); + assert(message); + + if (message->op != BOOTREQUEST || + message->htype != ARPHRD_ETHER || + message->hlen != ETHER_ADDR_LEN) + return 0; + + req = new0(DHCPRequest, 1); + if (!req) + return -ENOMEM; + + type = dhcp_option_parse(message, length, parse_request, req, &error_message); + if (type < 0) + return 0; + + r = ensure_sane_request(server, req, message); + if (r < 0) + /* this only fails on critical errors */ + return r; + + existing_lease = hashmap_get(server->leases_by_client_id, + &req->client_id); + + switch(type) { + + case DHCP_DISCOVER: { + be32_t address = INADDR_ANY; + unsigned i; + + log_dhcp_server(server, "DISCOVER (0x%x)", + be32toh(req->message->xid)); + + if (!server->pool_size) + /* no pool allocated */ + return 0; + + /* for now pick a random free address from the pool */ + 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 */ + + 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]) { + address = server->subnet | htobe32(server->pool_offset + next_offer); + break; + } else + next_offer = (next_offer + 1) % server->pool_size; + } + } + + if (address == INADDR_ANY) + /* no free addresses left */ + return 0; + + r = server_send_offer(server, req, address); + if (r < 0) { + /* this only fails on critical errors */ + log_dhcp_server(server, "could not send offer: %s", + strerror(-r)); + return r; + } else { + log_dhcp_server(server, "OFFER (0x%x)", + be32toh(req->message->xid)); + return DHCP_OFFER; + } + + break; + } + case DHCP_DECLINE: + 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 */ + + return 1; + + case DHCP_REQUEST: { + be32_t address; + bool init_reboot = false; + int pool_offset; + + /* see RFC 2131, section 4.3.2 */ + + if (req->server_id) { + log_dhcp_server(server, "REQUEST (selecting) (0x%x)", + be32toh(req->message->xid)); + + /* SELECTING */ + if (req->server_id != server->address) + /* client did not pick us */ + return 0; + + if (req->message->ciaddr) + /* this MUST be zero */ + return 0; + + if (!req->requested_ip) + /* this must be filled in with the yiaddr + from the chosen OFFER */ + return 0; + + address = req->requested_ip; + } else if (req->requested_ip) { + log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)", + be32toh(req->message->xid)); + + /* INIT-REBOOT */ + if (req->message->ciaddr) + /* this MUST be zero */ + return 0; + + /* TODO: check more carefully if IP is correct */ + address = req->requested_ip; + init_reboot = true; + } else { + log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)", + be32toh(req->message->xid)); + + /* REBINDING / RENEWING */ + if (!req->message->ciaddr) + /* this MUST be filled in with clients IP address */ + return 0; + + address = req->message->ciaddr; + } + + pool_offset = get_pool_offset(server, address); + + /* verify that the requested address is from the pool, and either + owned by the current client or free */ + if (pool_offset >= 0 && + server->bound_leases[pool_offset] == existing_lease) { + DHCPLease *lease; + usec_t time_now = 0; + + if (!existing_lease) { + lease = new0(DHCPLease, 1); + lease->address = req->requested_ip; + lease->client_id.data = memdup(req->client_id.data, + req->client_id.length); + if (!lease->client_id.data) { + free(lease); + return -ENOMEM; + } + lease->client_id.length = req->client_id.length; + memcpy(&lease->chaddr, &req->message->chaddr, + ETH_ALEN); + lease->gateway = req->message->giaddr; + } else + lease = existing_lease; + + r = sd_event_now(server->event, + clock_boottime_or_monotonic(), + &time_now); + if (r < 0) { + if (!existing_lease) + dhcp_lease_free(lease); + return r; + } + + lease->expiration = req->lifetime * USEC_PER_SEC + time_now; + + r = server_send_ack(server, req, address); + if (r < 0) { + /* this only fails on critical errors */ + log_dhcp_server(server, "could not send ack: %s", + strerror(-r)); + + if (!existing_lease) + dhcp_lease_free(lease); + + return r; + } else { + log_dhcp_server(server, "ACK (0x%x)", + be32toh(req->message->xid)); + + server->bound_leases[pool_offset] = lease; + hashmap_put(server->leases_by_client_id, + &lease->client_id, lease); + + return DHCP_ACK; + } + } else if (init_reboot) { + r = server_send_nak(server, req); + if (r < 0) { + /* this only fails on critical errors */ + log_dhcp_server(server, "could not send nak: %s", + strerror(-r)); + return r; + } else { + log_dhcp_server(server, "NAK (0x%x)", + be32toh(req->message->xid)); + return DHCP_NAK; + } + } + + break; + } + + case DHCP_RELEASE: { + int pool_offset; + + log_dhcp_server(server, "RELEASE (0x%x)", + be32toh(req->message->xid)); + + if (!existing_lease) + return 0; + + if (existing_lease->address != req->message->ciaddr) + return 0; + + pool_offset = get_pool_offset(server, req->message->ciaddr); + if (pool_offset < 0) + return 0; + + if (server->bound_leases[pool_offset] == existing_lease) { + server->bound_leases[pool_offset] = NULL; + hashmap_remove(server->leases_by_client_id, existing_lease); + dhcp_lease_free(existing_lease); + + return 1; + } else + return 0; + } + } + + return 0; +} + +static int server_receive_message(sd_event_source *s, int fd, + uint32_t revents, void *userdata) { + _cleanup_free_ DHCPMessage *message = NULL; + uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))]; + sd_dhcp_server *server = userdata; + struct iovec iov = {}; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + ssize_t buflen, len; + + assert(server); + + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; + + message = malloc(buflen); + if (!message) + return -ENOMEM; + + iov.iov_base = message; + iov.iov_len = buflen; + + len = recvmsg(fd, &msg, 0); + 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) { + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_PKTINFO && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) { + struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg); + + /* TODO figure out if this can be done as a filter on + * the socket, like for IPv6 */ + if (server->ifindex != info->ipi_ifindex) + return 0; + + break; + } + } + + return dhcp_server_handle_message(server, message, (size_t)len); +} + +int sd_dhcp_server_start(sd_dhcp_server *server) { + int r; + + assert_return(server, -EINVAL); + assert_return(server->event, -EINVAL); + assert_return(!server->receive_message, -EBUSY); + assert_return(server->fd_raw == -1, -EBUSY); + assert_return(server->fd == -1, -EBUSY); + assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH); + + r = socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (r < 0) { + r = -errno; + sd_dhcp_server_stop(server); + return r; + } + server->fd_raw = r; + + r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER); + if (r < 0) { + sd_dhcp_server_stop(server); + return r; + } + server->fd = r; + + r = sd_event_add_io(server->event, &server->receive_message, + server->fd, EPOLLIN, + server_receive_message, server); + if (r < 0) { + sd_dhcp_server_stop(server); + return r; + } + + r = sd_event_source_set_priority(server->receive_message, + server->event_priority); + if (r < 0) { + sd_dhcp_server_stop(server); + return r; + } + + log_dhcp_server(server, "STARTED"); + + return 0; +} + +int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { + unsigned i; + int r = 0; + + assert_return(server, -EINVAL); + assert(server->bound_leases); + + for (i = 0; i < server->pool_size; i++) { + DHCPLease *lease = server->bound_leases[i]; + + if (!lease || lease == &server->invalid_lease) + continue; + + r = server_send_forcerenew(server, lease->address, + lease->gateway, + lease->chaddr); + if (r < 0) + return r; + else + log_dhcp_server(server, "FORCERENEW"); + } + + return r; +} + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) { + int r; + + assert_return(server, -EINVAL); + assert_return(timezone_is_valid(tz), -EINVAL); + + if (streq_ptr(tz, server->timezone)) + return 0; + + r = free_and_strdup(&server->timezone, tz); + if (r < 0) + return r; + + return 1; +} + +int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) { + assert_return(server, -EINVAL); + + if (t == server->max_lease_time) + return 0; + + server->max_lease_time = t; + return 1; +} + +int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) { + assert_return(server, -EINVAL); + + if (t == server->default_lease_time) + return 0; + + server->default_lease_time = t; + return 1; +} + +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) { + assert_return(server, -EINVAL); + assert_return(dns || n <= 0, -EINVAL); + + if (server->n_dns == n && + memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0) + return 0; + + if (n <= 0) { + server->dns = mfree(server->dns); + server->n_dns = 0; + } else { + struct in_addr *c; + + c = newdup(struct in_addr, dns, n); + if (!c) + return -ENOMEM; + + free(server->dns); + server->dns = c; + server->n_dns = n; + } + + return 1; +} + +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) { + assert_return(server, -EINVAL); + assert_return(ntp || n <= 0, -EINVAL); + + if (server->n_ntp == n && + memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0) + return 0; + + if (n <= 0) { + server->ntp = mfree(server->ntp); + server->n_ntp = 0; + } else { + struct in_addr *c; + + c = newdup(struct in_addr, ntp, n); + if (!c) + return -ENOMEM; + + free(server->ntp); + server->ntp = c; + server->n_ntp = n; + } + + return 1; +} + +int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) { + assert_return(server, -EINVAL); + + if (enabled == server->emit_router) + return 0; + + server->emit_router = enabled; + + return 1; +} diff --git a/src/libsystemd-network/src/sd-dhcp6-client.c b/src/libsystemd-network/src/sd-dhcp6-client.c new file mode 100644 index 0000000000..af5d91c946 --- /dev/null +++ b/src/libsystemd-network/src/sd-dhcp6-client.c @@ -0,0 +1,1335 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/random-util.h" +#include "basic/socket-util.h" +#include "basic/string-table.h" +#include "basic/util.h" +#include "systemd-network/dhcp-identifier.h" +#include "systemd-network/dhcp6-internal.h" +#include "systemd-network/dhcp6-lease-internal.h" +#include "systemd-network/dhcp6-protocol.h" +#include "systemd-network/network-internal.h" +#include "systemd-network/sd-dhcp6-client.h" + +#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN + +struct sd_dhcp6_client { + unsigned n_ref; + + enum DHCP6State state; + sd_event *event; + int event_priority; + int ifindex; + struct in6_addr local_address; + uint8_t mac_addr[MAX_MAC_ADDR_LEN]; + size_t mac_addr_len; + uint16_t arp_type; + DHCP6IA ia_na; + be32_t transaction_id; + usec_t transaction_start; + struct sd_dhcp6_lease *lease; + int fd; + bool information_request; + be16_t *req_opts; + size_t req_opts_allocated; + size_t req_opts_len; + sd_event_source *receive_message; + usec_t retransmit_time; + uint8_t retransmit_count; + sd_event_source *timeout_resend; + sd_event_source *timeout_resend_expire; + sd_dhcp6_client_callback_t callback; + void *userdata; + struct duid duid; + size_t duid_len; +}; + +static const uint16_t default_req_opts[] = { + 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] = { + [DHCP6_SOLICIT] = "SOLICIT", + [DHCP6_ADVERTISE] = "ADVERTISE", + [DHCP6_REQUEST] = "REQUEST", + [DHCP6_CONFIRM] = "CONFIRM", + [DHCP6_RENEW] = "RENEW", + [DHCP6_REBIND] = "REBIND", + [DHCP6_REPLY] = "REPLY", + [DHCP6_RELEASE] = "RELEASE", + [DHCP6_DECLINE] = "DECLINE", + [DHCP6_RECONFIGURE] = "RECONFIGURE", + [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST", + [DHCP6_RELAY_FORW] = "RELAY-FORW", + [DHCP6_RELAY_REPL] = "RELAY-REPL", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); + +const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); + +#define DHCP6_CLIENT_DONT_DESTROY(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); + +int sd_dhcp6_client_set_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata) { + + assert_return(client, -EINVAL); + + client->callback = cb; + client->userdata = userdata; + + return 0; +} + +int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { + + assert_return(client, -EINVAL); + assert_return(ifindex >= -1, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->ifindex = ifindex; + 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, + uint16_t arp_type) { + + assert_return(client, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); + assert_return(arp_type > 0, -EINVAL); + + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (client->mac_addr_len == addr_len && + memcmp(&client->mac_addr, addr, addr_len) == 0) + return 0; + + memcpy(&client->mac_addr, addr, addr_len); + client->mac_addr_len = addr_len; + client->arp_type = arp_type; + + return 0; +} + +static int client_ensure_duid(sd_dhcp6_client *client) { + if (client->duid_len != 0) + return 0; + + return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); +} + +/** + * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid + * without further modification. Otherwise, if duid_type is supported, DUID + * is set based on that type. Otherwise, an error is returned. + */ +int sd_dhcp6_client_set_duid( + sd_dhcp6_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + + int r; + assert_return(client, -EINVAL); + assert_return(duid_len == 0 || duid != NULL, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + if (duid != NULL) { + r = dhcp_validate_duid_len(duid_type, duid_len); + if (r < 0) + return r; + } + + if (duid != NULL) { + client->duid.type = htobe16(duid_type); + memcpy(&client->duid.raw.data, duid, duid_len); + client->duid_len = sizeof(client->duid.type) + duid_len; + } else if (duid_type == DUID_TYPE_EN) { + r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + if (r < 0) + return r; + } else + return -EOPNOTSUPP; + + return 0; +} + +int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { + assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->ia_na.id = htobe32(iaid); + + return 0; +} + +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; + + return 0; +} + +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) { + assert_return(client, -EINVAL); + assert_return(enabled, -EINVAL); + + *enabled = client->information_request; + + return 0; +} + +int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) { + size_t t; + + assert_return(client, -EINVAL); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + + switch(option) { + + 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: + return -EINVAL; + } + + for (t = 0; t < client->req_opts_len; t++) + if (client->req_opts[t] == htobe16(option)) + return -EEXIST; + + if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, + client->req_opts_len + 1)) + return -ENOMEM; + + client->req_opts[client->req_opts_len++] = htobe16(option); + + return 0; +} + +int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { + assert_return(client, -EINVAL); + + if (!client->lease) + return -ENOMSG; + + if (ret) + *ret = client->lease; + + return 0; +} + +static void client_notify(sd_dhcp6_client *client, int event) { + assert(client); + + if (client->callback) + client->callback(client, event, client->userdata); +} + +static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) { + assert(client); + + if (client->lease) { + dhcp6_lease_clear_timers(&client->lease->ia); + sd_dhcp6_lease_unref(client->lease); + } + + client->lease = lease; +} + +static int client_reset(sd_dhcp6_client *client) { + assert(client); + + client_set_lease(client, NULL); + + client->receive_message = + sd_event_source_unref(client->receive_message); + + client->fd = safe_close(client->fd); + + client->transaction_id = 0; + client->transaction_start = 0; + + client->ia_na.timeout_t1 = + sd_event_source_unref(client->ia_na.timeout_t1); + client->ia_na.timeout_t2 = + sd_event_source_unref(client->ia_na.timeout_t2); + + client->retransmit_time = 0; + client->retransmit_count = 0; + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_resend_expire = + sd_event_source_unref(client->timeout_resend_expire); + + client->state = DHCP6_STATE_STOPPED; + + return 0; +} + +static void client_stop(sd_dhcp6_client *client, int error) { + DHCP6_CLIENT_DONT_DESTROY(client); + + assert(client); + + client_notify(client, error); + + client_reset(client); +} + +static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { + _cleanup_free_ DHCP6Message *message = NULL; + struct in6_addr all_servers = + IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; + size_t len, optlen = 512; + uint8_t *opt; + int r; + usec_t elapsed_usec; + be16_t elapsed_time; + + assert(client); + + len = sizeof(DHCP6Message) + optlen; + + message = malloc0(len); + if (!message) + return -ENOMEM; + + opt = (uint8_t *)(message + 1); + + message->transaction_id = client->transaction_id; + + switch(client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + message->type = DHCP6_INFORMATION_REQUEST; + + break; + + case DHCP6_STATE_SOLICITATION: + message->type = DHCP6_SOLICIT; + + r = dhcp6_option_append(&opt, &optlen, + SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + if (r < 0) + return r; + + r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na); + if (r < 0) + return r; + + break; + + case DHCP6_STATE_REQUEST: + case DHCP6_STATE_RENEW: + + if (client->state == DHCP6_STATE_REQUEST) + message->type = DHCP6_REQUEST; + else + message->type = DHCP6_RENEW; + + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, + client->lease->serverid_len, + client->lease->serverid); + if (r < 0) + return r; + + r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); + if (r < 0) + return r; + + break; + + case DHCP6_STATE_REBIND: + message->type = DHCP6_REBIND; + + r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); + if (r < 0) + return r; + + break; + + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + return -EINVAL; + } + + 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, SD_DHCP6_OPTION_CLIENTID, + client->duid_len, &client->duid); + if (r < 0) + return r; + + elapsed_usec = time_now - client->transaction_start; + if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10) + elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10); + else + elapsed_time = 0xffff; + + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, + sizeof(elapsed_time), &elapsed_time); + if (r < 0) + return r; + + r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, + len - optlen); + if (r < 0) + return r; + + log_dhcp6_client(client, "Sent %s", + dhcp6_message_type_to_string(message->type)); + + return 0; +} + +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = userdata; + + assert(s); + assert(client); + assert(client->lease); + + client->lease->ia.timeout_t2 = + sd_event_source_unref(client->lease->ia.timeout_t2); + + log_dhcp6_client(client, "Timeout T2"); + + client_start(client, DHCP6_STATE_REBIND); + + return 0; +} + +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = userdata; + + assert(s); + assert(client); + assert(client->lease); + + client->lease->ia.timeout_t1 = + sd_event_source_unref(client->lease->ia.timeout_t1); + + log_dhcp6_client(client, "Timeout T1"); + + client_start(client, DHCP6_STATE_RENEW); + + return 0; +} + +static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = userdata; + DHCP6_CLIENT_DONT_DESTROY(client); + enum DHCP6State state; + + assert(s); + assert(client); + assert(client->event); + + state = client->state; + + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); + + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + client_start(client, DHCP6_STATE_SOLICITATION); + + return 0; +} + +static usec_t client_timeout_compute_random(usec_t val) { + return val - val / 10 + + (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; +} + +static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { + int r = 0; + sd_dhcp6_client *client = userdata; + usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; + usec_t max_retransmit_duration = 0; + uint8_t max_retransmit_count = 0; + char time_string[FORMAT_TIMESPAN_MAX]; + uint32_t expire = 0; + + assert(s); + assert(client); + assert(client->event); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + init_retransmit_time = DHCP6_INF_TIMEOUT; + max_retransmit_time = DHCP6_INF_MAX_RT; + + break; + + case DHCP6_STATE_SOLICITATION: + + if (client->retransmit_count && client->lease) { + client_start(client, DHCP6_STATE_REQUEST); + return 0; + } + + init_retransmit_time = DHCP6_SOL_TIMEOUT; + max_retransmit_time = DHCP6_SOL_MAX_RT; + + break; + + case DHCP6_STATE_REQUEST: + init_retransmit_time = DHCP6_REQ_TIMEOUT; + max_retransmit_time = DHCP6_REQ_MAX_RT; + max_retransmit_count = DHCP6_REQ_MAX_RC; + + break; + + case DHCP6_STATE_RENEW: + init_retransmit_time = DHCP6_REN_TIMEOUT; + max_retransmit_time = DHCP6_REN_MAX_RT; + + /* RFC 3315, section 18.1.3. says max retransmit duration will + be the remaining time until T2. Instead of setting MRD, + wait for T2 to trigger with the same end result */ + + break; + + case DHCP6_STATE_REBIND: + init_retransmit_time = DHCP6_REB_TIMEOUT; + max_retransmit_time = DHCP6_REB_MAX_RT; + + if (!client->timeout_resend_expire) { + r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, + &expire); + if (r < 0) { + client_stop(client, r); + return 0; + } + max_retransmit_duration = expire * USEC_PER_SEC; + } + + break; + + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + return 0; + } + + if (max_retransmit_count && + client->retransmit_count >= max_retransmit_count) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + goto error; + + r = client_send_message(client, time_now); + if (r >= 0) + client->retransmit_count++; + + if (!client->retransmit_time) { + client->retransmit_time = + client_timeout_compute_random(init_retransmit_time); + + if (client->state == DHCP6_STATE_SOLICITATION) + client->retransmit_time += init_retransmit_time / 10; + + } else { + if (max_retransmit_time && + client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); + } + + log_dhcp6_client(client, "Next retransmission in %s", + 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(), + time_now + client->retransmit_time, + 10 * USEC_PER_MSEC, client_timeout_resend, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer"); + if (r < 0) + goto error; + + if (max_retransmit_duration && !client->timeout_resend_expire) { + + log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", + max_retransmit_duration / USEC_PER_SEC); + + r = sd_event_add_time(client->event, + &client->timeout_resend_expire, + clock_boottime_or_monotonic(), + time_now + max_retransmit_duration, + USEC_PER_SEC, + client_timeout_resend_expire, client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend_expire, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer"); + if (r < 0) + goto error; + } + +error: + if (r < 0) + client_stop(client, r); + + return 0; +} + +static int client_ensure_iaid(sd_dhcp6_client *client) { + int r; + + assert(client); + + if (client->ia_na.id) + return 0; + + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id); + if (r < 0) + return r; + + return 0; +} + +static int client_parse_message( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + sd_dhcp6_lease *lease) { + int r; + uint8_t *optval, *option, *id = NULL; + uint16_t optcode, status; + size_t optlen, id_len; + bool clientid = false; + be32_t iaid_lease; + + assert(client); + assert(message); + assert(len >= sizeof(DHCP6Message)); + assert(lease); + + option = (uint8_t *)message + sizeof(DHCP6Message); + len -= sizeof(DHCP6Message); + + while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, + &optval)) >= 0) { + switch (optcode) { + case SD_DHCP6_OPTION_CLIENTID: + if (clientid) { + log_dhcp6_client(client, "%s contains multiple clientids", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + if (optlen != client->duid_len || + memcmp(&client->duid, optval, optlen) != 0) { + log_dhcp6_client(client, "%s DUID does not match", + dhcp6_message_type_to_string(message->type)); + + return -EINVAL; + } + clientid = true; + + break; + + 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", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + r = dhcp6_lease_set_serverid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_PREFERENCE: + if (optlen != 1) + return -EINVAL; + + r = dhcp6_lease_set_preference(lease, *optval); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: + if (optlen < 2) + return -EINVAL; + + status = optval[0] << 8 | optval[1]; + if (status) { + log_dhcp6_client(client, "%s Status %s", + dhcp6_message_type_to_string(message->type), + dhcp6_message_status_to_string(status)); + return -EINVAL; + } + + break; + + case SD_DHCP6_OPTION_IA_NA: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA NA option"); + + break; + } + + r = dhcp6_option_parse_ia(&optval, &optlen, optcode, + &lease->ia); + if (r < 0 && r != -ENOMSG) + return r; + + r = dhcp6_lease_get_iaid(lease, &iaid_lease); + if (r < 0) + return r; + + if (client->ia_na.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + break; + + case SD_DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_set_dns(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_set_domains(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_set_ntp(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_set_sntp(lease, optval, optlen); + if (r < 0) + return r; + + break; + } + + } + + if (r == -ENOMSG) + r = 0; + + if (r < 0 || !clientid) { + log_dhcp6_client(client, "%s has incomplete options", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + r = dhcp6_lease_get_serverid(lease, &id, &id_len); + if (r < 0) + log_dhcp6_client(client, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + } + + return r; +} + +static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + bool rapid_commit; + int r; + + assert(client); + assert(reply); + + if (reply->type != DHCP6_REPLY) + return 0; + + r = dhcp6_lease_new(&lease); + if (r < 0) + return -ENOMEM; + + r = client_parse_message(client, reply, len, lease); + if (r < 0) + return r; + + if (client->state == DHCP6_STATE_SOLICITATION) { + r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); + if (r < 0) + return r; + + if (!rapid_commit) + return 0; + } + + client_set_lease(client, lease); + lease = NULL; + + return DHCP6_STATE_BOUND; +} + +static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) { + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + uint8_t pref_advertise = 0, pref_lease = 0; + int r; + + if (advertise->type != DHCP6_ADVERTISE) + return 0; + + r = dhcp6_lease_new(&lease); + if (r < 0) + return r; + + r = client_parse_message(client, advertise, len, lease); + if (r < 0) + return r; + + r = dhcp6_lease_get_preference(lease, &pref_advertise); + if (r < 0) + return r; + + r = dhcp6_lease_get_preference(client->lease, &pref_lease); + + if (r < 0 || pref_advertise > pref_lease) { + client_set_lease(client, lease); + lease = NULL; + r = 0; + } + + if (pref_advertise == 255 || client->retransmit_count > 1) + r = DHCP6_STATE_REQUEST; + + return r; +} + +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 = NULL; + ssize_t buflen, len; + int r = 0; + + assert(s); + assert(client); + assert(client->event); + + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; + + message = malloc(buflen); + if (!message) + return -ENOMEM; + + len = recv(fd, message, buflen, 0); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); + + } + if ((size_t) len < sizeof(DHCP6Message)) { + log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring"); + return 0; + } + + switch(message->type) { + case DHCP6_SOLICIT: + case DHCP6_REQUEST: + case DHCP6_CONFIRM: + case DHCP6_RENEW: + case DHCP6_REBIND: + case DHCP6_RELEASE: + case DHCP6_DECLINE: + case DHCP6_INFORMATION_REQUEST: + case DHCP6_RELAY_FORW: + case DHCP6_RELAY_REPL: + return 0; + + case DHCP6_ADVERTISE: + case DHCP6_REPLY: + case DHCP6_RECONFIGURE: + break; + + default: + log_dhcp6_client(client, "Unknown message type %d", message->type); + return 0; + } + + if (client->transaction_id != (message->transaction_id & + htobe32(0x00ffffff))) + return 0; + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + r = client_receive_reply(client, message, len); + if (r < 0) + return 0; + + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); + + client_start(client, DHCP6_STATE_STOPPED); + + break; + + case DHCP6_STATE_SOLICITATION: + r = client_receive_advertise(client, message, len); + + if (r == DHCP6_STATE_REQUEST) { + client_start(client, r); + + break; + } + + /* fall through for Soliciation Rapid Commit option check */ + case DHCP6_STATE_REQUEST: + case DHCP6_STATE_RENEW: + case DHCP6_STATE_REBIND: + + r = client_receive_reply(client, message, len); + if (r < 0) + return 0; + + if (r == DHCP6_STATE_BOUND) { + + r = client_start(client, DHCP6_STATE_BOUND); + if (r < 0) { + client_stop(client, r); + return 0; + } + + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + } + + break; + + case DHCP6_STATE_BOUND: + + break; + + case DHCP6_STATE_STOPPED: + return 0; + } + + if (r >= 0) + log_dhcp6_client(client, "Recv %s", + dhcp6_message_type_to_string(message->type)); + + return 0; +} + +static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { + int r; + usec_t timeout, time_now; + char time_string[FORMAT_TIMESPAN_MAX]; + + assert_return(client, -EINVAL); + assert_return(client->event, -EINVAL); + assert_return(client->ifindex > 0, -EINVAL); + assert_return(client->state != state, -EINVAL); + + client->timeout_resend_expire = + sd_event_source_unref(client->timeout_resend_expire); + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->retransmit_time = 0; + client->retransmit_count = 0; + + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + + switch (state) { + case DHCP6_STATE_STOPPED: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->state = DHCP6_STATE_STOPPED; + + return 0; + } + + /* fall through */ + case DHCP6_STATE_SOLICITATION: + client->state = DHCP6_STATE_SOLICITATION; + + break; + + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_REQUEST: + case DHCP6_STATE_RENEW: + case DHCP6_STATE_REBIND: + + client->state = state; + + break; + + case DHCP6_STATE_BOUND: + + if (client->lease->ia.lifetime_t1 == 0xffffffff || + client->lease->ia.lifetime_t2 == 0xffffffff) { + + log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", + be32toh(client->lease->ia.lifetime_t1), + be32toh(client->lease->ia.lifetime_t2)); + + return 0; + } + + 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, USEC_PER_SEC)); + + r = sd_event_add_time(client->event, + &client->lease->ia.timeout_t1, + clock_boottime_or_monotonic(), time_now + timeout, + 10 * USEC_PER_SEC, client_timeout_t1, + client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->lease->ia.timeout_t1, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); + if (r < 0) + return r; + + 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, USEC_PER_SEC)); + + r = sd_event_add_time(client->event, + &client->lease->ia.timeout_t2, + clock_boottime_or_monotonic(), time_now + timeout, + 10 * USEC_PER_SEC, client_timeout_t2, + client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->lease->ia.timeout_t2, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); + if (r < 0) + return r; + + client->state = state; + + return 0; + } + + client->transaction_id = random_u32() & htobe32(0x00ffffff); + client->transaction_start = time_now; + + r = sd_event_add_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, + client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); + if (r < 0) + return r; + + return 0; +} + +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) { + enum DHCP6State state = DHCP6_STATE_SOLICITATION; + int r = 0; + + assert_return(client, -EINVAL); + assert_return(client->event, -EINVAL); + assert_return(client->ifindex > 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 -EBUSY; + + r = client_reset(client); + if (r < 0) + return r; + + r = client_ensure_iaid(client); + if (r < 0) + return r; + + r = client_ensure_duid(client); + if (r < 0) + return r; + + r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); + if (r < 0) { + _cleanup_free_ char *p = NULL; + + (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + return log_dhcp6_client_errno(client, r, + "Failed to bind to UDP socket at address %s: %m", strna(p)); + } + + client->fd = r; + + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, client_receive_message, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, + "dhcp6-receive-message"); + if (r < 0) + goto error; + + if (client->information_request) + state = DHCP6_STATE_INFORMATION_REQUEST; + + log_dhcp6_client(client, "Started in %s mode", + client->information_request? "Information request": + "Managed"); + + return client_start(client, state); + +error: + client_reset(client); + return r; +} + +int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { + int r; + + assert_return(client, -EINVAL); + assert_return(!client->event, -EBUSY); + + if (event) + client->event = sd_event_ref(event); + else { + r = sd_event_default(&client->event); + if (r < 0) + return 0; + } + + client->event_priority = priority; + + return 0; +} + +int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + client->event = sd_event_unref(client->event); + + return 0; +} + +sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { + assert_return(client, NULL); + + return client->event; +} + +sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref++; + + return client; +} + +sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref--; + + if (client->n_ref > 0) + return NULL; + + client_reset(client); + + sd_dhcp6_client_detach_event(client); + + free(client->req_opts); + free(client); + + return NULL; +} + +int sd_dhcp6_client_new(sd_dhcp6_client **ret) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + size_t t; + + assert_return(ret, -EINVAL); + + client = new0(sd_dhcp6_client, 1); + if (!client) + return -ENOMEM; + + client->n_ref = 1; + client->ia_na.type = SD_DHCP6_OPTION_IA_NA; + client->ifindex = -1; + client->fd = -1; + + client->req_opts_len = ELEMENTSOF(default_req_opts); + client->req_opts = new0(be16_t, client->req_opts_len); + if (!client->req_opts) + return -ENOMEM; + + for (t = 0; t < client->req_opts_len; t++) + client->req_opts[t] = htobe16(default_req_opts[t]); + + *ret = client; + client = NULL; + + return 0; +} diff --git a/src/libsystemd-network/src/sd-dhcp6-lease.c b/src/libsystemd-network/src/sd-dhcp6-lease.c new file mode 100644 index 0000000000..cb53533492 --- /dev/null +++ b/src/libsystemd-network/src/sd-dhcp6-lease.c @@ -0,0 +1,410 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "systemd-network/dhcp6-lease-internal.h" +#include "systemd-network/dhcp6-protocol.h" + +int dhcp6_lease_clear_timers(DHCP6IA *ia) { + assert_return(ia, -EINVAL); + + ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1); + ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2); + + return 0; +} + +int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { + DHCP6Address *addr; + uint32_t valid = 0, t; + + assert_return(ia, -EINVAL); + assert_return(expire, -EINVAL); + + LIST_FOREACH(addresses, addr, ia->addresses) { + t = be32toh(addr->iaaddr.lifetime_valid); + if (valid < t) + valid = t; + } + + t = be32toh(ia->lifetime_t2); + if (t > valid) + return -EINVAL; + + *expire = valid - t; + + return 0; +} + +DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { + DHCP6Address *address; + + if (!ia) + return NULL; + + dhcp6_lease_clear_timers(ia); + + while (ia->addresses) { + address = ia->addresses; + + LIST_REMOVE(addresses, ia->addresses, address); + + free(address); + } + + return NULL; +} + +int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, + size_t len) { + assert_return(lease, -EINVAL); + assert_return(id, -EINVAL); + + free(lease->serverid); + + lease->serverid = memdup(id, len); + if (!lease->serverid) + return -EINVAL; + + lease->serverid_len = len; + + return 0; +} + +int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) { + assert_return(lease, -EINVAL); + assert_return(id, -EINVAL); + assert_return(len, -EINVAL); + + *id = lease->serverid; + *len = lease->serverid_len; + + return 0; +} + +int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { + assert_return(lease, -EINVAL); + + lease->preference = preference; + + return 0; +} + +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { + assert_return(preference, -EINVAL); + + if (!lease) + return -EINVAL; + + *preference = lease->preference; + + return 0; +} + +int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { + assert_return(lease, -EINVAL); + + lease->rapid_commit = true; + + return 0; +} + +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { + assert_return(lease, -EINVAL); + assert_return(rapid_commit, -EINVAL); + + *rapid_commit = lease->rapid_commit; + + return 0; +} + +int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { + assert_return(lease, -EINVAL); + assert_return(iaid, -EINVAL); + + *iaid = lease->ia.id; + + return 0; +} + +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(lifetime_preferred, -EINVAL); + assert_return(lifetime_valid, -EINVAL); + + if (!lease->addr_iter) + return -ENOMSG; + + memcpy(addr, &lease->addr_iter->iaaddr.address, + sizeof(struct in6_addr)); + *lifetime_preferred = + be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + + lease->addr_iter = lease->addr_iter->addresses_next; + + return 0; +} + +void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { + if (lease) + lease->addr_iter = lease->ia.addresses; +} + +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, + lease->dns_count, + &lease->dns_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid DNS server option: %s", + strerror(-r)); + + return r; + } + + lease->dns_count = r; + + return 0; +} + +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->dns_count) { + *addrs = lease->dns; + return lease->dns_count; + } + + return -ENOENT; +} + +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) { + int r; + char **domains; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_domainname(optval, optlen, &domains); + if (r < 0) + return 0; + + free(lease->domains); + lease->domains = domains; + lease->domains_count = r; + + return r; +} + +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { + assert_return(lease, -EINVAL); + assert_return(domains, -EINVAL); + + if (lease->domains_count) { + *domains = lease->domains; + return lease->domains_count; + } + + return -ENOENT; +} + +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + uint16_t subopt; + size_t sublen; + uint8_t *subval; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + lease->ntp = mfree(lease->ntp); + lease->ntp_count = 0; + lease->ntp_allocated = 0; + + while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, + &subval)) >= 0) { + int s; + char **servers; + + switch(subopt) { + case DHCP6_NTP_SUBOPTION_SRV_ADDR: + case DHCP6_NTP_SUBOPTION_MC_ADDR: + if (sublen != 16) + return 0; + + s = dhcp6_option_parse_ip6addrs(subval, sublen, + &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (s < 0) + return s; + + lease->ntp_count = s; + + break; + + case DHCP6_NTP_SUBOPTION_SRV_FQDN: + r = dhcp6_option_parse_domainname(subval, sublen, + &servers); + if (r < 0) + return 0; + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); + lease->ntp_fqdn = servers; + lease->ntp_fqdn_count = r; + + break; + } + } + + if (r != -ENOMSG) + return r; + + return 0; +} + +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + if (lease->ntp || lease->ntp_fqdn) { + log_dhcp6_client(client, "NTP information already provided"); + + return 0; + } + + log_dhcp6_client(client, "Using deprecated SNTP information"); + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid SNTP server option: %s", + strerror(-r)); + + return r; + } + + lease->ntp_count = r; + + return 0; +} + +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->ntp_count) { + *addrs = lease->ntp; + return lease->ntp_count; + } + + return -ENOENT; +} + +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { + assert_return(lease, -EINVAL); + assert_return(ntp_fqdn, -EINVAL); + + if (lease->ntp_fqdn_count) { + *ntp_fqdn = lease->ntp_fqdn; + return lease->ntp_fqdn_count; + } + + return -ENOENT; +} + +sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref++; + + return lease; +} + +sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref--; + + if (lease->n_ref > 0) + return NULL; + + free(lease->serverid); + dhcp6_lease_free_ia(&lease->ia); + + free(lease->dns); + + lease->domains = strv_free(lease->domains); + + free(lease->ntp); + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); + free(lease); + + return NULL; +} + +int dhcp6_lease_new(sd_dhcp6_lease **ret) { + sd_dhcp6_lease *lease; + + lease = new0(sd_dhcp6_lease, 1); + if (!lease) + return -ENOMEM; + + lease->n_ref = 1; + + LIST_HEAD_INIT(lease->ia.addresses); + + *ret = lease; + return 0; +} diff --git a/src/libsystemd-network/src/sd-ipv4acd.c b/src/libsystemd-network/src/sd-ipv4acd.c new file mode 100644 index 0000000000..418a686150 --- /dev/null +++ b/src/libsystemd-network/src/sd-ipv4acd.c @@ -0,0 +1,525 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/ether-addr-util.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/list.h" +#include "basic/random-util.h" +#include "basic/siphash24.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "systemd-network/arp-util.h" +#include "systemd-network/sd-ipv4acd.h" + +/* Constants from the RFC */ +#define PROBE_WAIT_USEC (1U * USEC_PER_SEC) +#define PROBE_NUM 3U +#define PROBE_MIN_USEC (1U * USEC_PER_SEC) +#define PROBE_MAX_USEC (2U * USEC_PER_SEC) +#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC) +#define ANNOUNCE_NUM 2U +#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC) +#define MAX_CONFLICTS 10U +#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC) +#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC) + +typedef enum IPv4ACDState { + IPV4ACD_STATE_INIT, + IPV4ACD_STATE_STARTED, + IPV4ACD_STATE_WAITING_PROBE, + IPV4ACD_STATE_PROBING, + IPV4ACD_STATE_WAITING_ANNOUNCE, + IPV4ACD_STATE_ANNOUNCING, + IPV4ACD_STATE_RUNNING, + _IPV4ACD_STATE_MAX, + _IPV4ACD_STATE_INVALID = -1 +} IPv4ACDState; + +struct sd_ipv4acd { + unsigned n_ref; + + IPv4ACDState state; + int ifindex; + int fd; + + unsigned n_iteration; + unsigned n_conflict; + + sd_event_source *receive_message_event_source; + sd_event_source *timer_event_source; + + usec_t defend_window; + be32_t address; + + /* External */ + struct ether_addr mac_addr; + + sd_event *event; + int event_priority; + sd_ipv4acd_callback_t callback; + void* userdata; +}; + +#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__) +#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__) + +static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) { + assert(acd); + assert(st < _IPV4ACD_STATE_MAX); + + if (st == acd->state && !reset_counter) + acd->n_iteration++; + else { + acd->state = st; + acd->n_iteration = 0; + } +} + +static void ipv4acd_reset(sd_ipv4acd *acd) { + assert(acd); + + acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); + acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source); + + acd->fd = safe_close(acd->fd); + + ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true); +} + +sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) { + if (!acd) + return NULL; + + assert_se(acd->n_ref >= 1); + acd->n_ref++; + + return acd; +} + +sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { + if (!acd) + return NULL; + + assert_se(acd->n_ref >= 1); + acd->n_ref--; + + if (acd->n_ref > 0) + return NULL; + + ipv4acd_reset(acd); + sd_ipv4acd_detach_event(acd); + + free(acd); + + return NULL; +} + +int sd_ipv4acd_new(sd_ipv4acd **ret) { + _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL; + + assert_return(ret, -EINVAL); + + acd = new0(sd_ipv4acd, 1); + if (!acd) + return -ENOMEM; + + acd->n_ref = 1; + acd->state = IPV4ACD_STATE_INIT; + acd->ifindex = -1; + acd->fd = -1; + + *ret = acd; + acd = NULL; + + return 0; +} + +static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) { + assert(acd); + + if (!acd->callback) + return; + + acd->callback(acd, event, acd->userdata); +} + +int sd_ipv4acd_stop(sd_ipv4acd *acd) { + assert_return(acd, -EINVAL); + + ipv4acd_reset(acd); + + log_ipv4acd(acd, "STOPPED"); + + ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP); + + return 0; +} + +static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); + +static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) { + _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; + usec_t next_timeout, time_now; + int r; + + assert(acd); + + next_timeout = usec; + + if (random_usec > 0) + next_timeout += (usec_t) random_u64() % random_usec; + + assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd); + if (r < 0) + return r; + + r = sd_event_source_set_priority(timer, acd->event_priority); + if (r < 0) + return r; + + (void) sd_event_source_set_description(timer, "ipv4acd-timer"); + + sd_event_source_unref(acd->timer_event_source); + acd->timer_event_source = timer; + timer = NULL; + + return 0; +} + +static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { + assert(acd); + assert(arp); + + /* see the BPF */ + if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0) + return true; + + /* the TPA matched instead of the SPA, this is not a conflict */ + return false; +} + +static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_ipv4acd *acd = userdata; + int r = 0; + + assert(acd); + + switch (acd->state) { + + case IPV4ACD_STATE_STARTED: + ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true); + + if (acd->n_conflict >= MAX_CONFLICTS) { + char ts[FORMAT_TIMESPAN_MAX]; + log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0)); + + r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); + if (r < 0) + goto fail; + + acd->n_conflict = 0; + } else { + r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); + if (r < 0) + goto fail; + } + + break; + + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + /* Send a probe */ + r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + if (r < 0) { + log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m"); + goto fail; + } else { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = acd->address }; + + (void) in_addr_to_string(AF_INET, &addr, &address); + log_ipv4acd(acd, "Probing %s", strna(address)); + } + + if (acd->n_iteration < PROBE_NUM - 2) { + ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false); + + r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC)); + if (r < 0) + goto fail; + } else { + ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true); + + r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0); + if (r < 0) + goto fail; + } + + break; + + case IPV4ACD_STATE_ANNOUNCING: + if (acd->n_iteration >= ANNOUNCE_NUM - 1) { + ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false); + break; + } + + /* fall through */ + + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* Send announcement packet */ + r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + if (r < 0) { + log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); + goto fail; + } else + log_ipv4acd(acd, "ANNOUNCE"); + + ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false); + + r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0); + if (r < 0) + goto fail; + + if (acd->n_iteration == 0) { + acd->n_conflict = 0; + ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND); + } + + break; + + default: + assert_not_reached("Invalid state."); + } + + return 0; + +fail: + sd_ipv4acd_stop(acd); + return 0; +} + +static void ipv4acd_on_conflict(sd_ipv4acd *acd) { + _cleanup_free_ char *address = NULL; + union in_addr_union addr = { .in.s_addr = acd->address }; + + assert(acd); + + acd->n_conflict++; + + (void) in_addr_to_string(AF_INET, &addr, &address); + log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict); + + ipv4acd_reset(acd); + ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT); +} + +static int ipv4acd_on_packet( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata) { + + sd_ipv4acd *acd = userdata; + struct ether_arp packet; + ssize_t n; + int r; + + assert(s); + assert(acd); + assert(fd >= 0); + + n = recv(fd, &packet, sizeof(struct ether_arp), 0); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m"); + goto fail; + } + if ((size_t) n != sizeof(struct ether_arp)) { + log_ipv4acd(acd, "Ignoring too short ARP packet."); + return 0; + } + + switch (acd->state) { + + case IPV4ACD_STATE_ANNOUNCING: + case IPV4ACD_STATE_RUNNING: + + if (ipv4acd_arp_conflict(acd, &packet)) { + usec_t ts; + + assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); + + /* Defend address */ + if (ts > acd->defend_window) { + acd->defend_window = ts + DEFEND_INTERVAL_USEC; + r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr); + if (r < 0) { + log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m"); + goto fail; + } else + log_ipv4acd(acd, "DEFEND"); + + } else + ipv4acd_on_conflict(acd); + } + break; + + case IPV4ACD_STATE_WAITING_PROBE: + case IPV4ACD_STATE_PROBING: + case IPV4ACD_STATE_WAITING_ANNOUNCE: + /* BPF ensures this packet indicates a conflict */ + ipv4acd_on_conflict(acd); + break; + + default: + assert_not_reached("Invalid state."); + } + + return 0; + +fail: + sd_ipv4acd_stop(acd); + return 0; +} + +int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) { + assert_return(acd, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + + acd->ifindex = ifindex; + + return 0; +} + +int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) { + assert_return(acd, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + + acd->mac_addr = *addr; + + return 0; +} + +int sd_ipv4acd_detach_event(sd_ipv4acd *acd) { + assert_return(acd, -EINVAL); + + acd->event = sd_event_unref(acd->event); + + return 0; +} + +int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) { + int r; + + assert_return(acd, -EINVAL); + assert_return(!acd->event, -EBUSY); + + if (event) + acd->event = sd_event_ref(event); + else { + r = sd_event_default(&acd->event); + if (r < 0) + return r; + } + + acd->event_priority = priority; + + return 0; +} + +int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) { + assert_return(acd, -EINVAL); + + acd->callback = cb; + acd->userdata = userdata; + + return 0; +} + +int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) { + assert_return(acd, -EINVAL); + assert_return(address, -EINVAL); + assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + + acd->address = address->s_addr; + + return 0; +} + +int sd_ipv4acd_is_running(sd_ipv4acd *acd) { + assert_return(acd, false); + + return acd->state != IPV4ACD_STATE_INIT; +} + +int sd_ipv4acd_start(sd_ipv4acd *acd) { + int r; + + assert_return(acd, -EINVAL); + assert_return(acd->event, -EINVAL); + assert_return(acd->ifindex > 0, -EINVAL); + assert_return(acd->address != 0, -EINVAL); + assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL); + assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY); + + r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr); + if (r < 0) + return r; + + safe_close(acd->fd); + acd->fd = r; + acd->defend_window = 0; + acd->n_conflict = 0; + + r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message"); + + r = ipv4acd_set_next_wakeup(acd, 0, 0); + if (r < 0) + goto fail; + + ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true); + return 0; + +fail: + ipv4acd_reset(acd); + return r; +} diff --git a/src/libsystemd-network/src/sd-ipv4ll.c b/src/libsystemd-network/src/sd-ipv4ll.c new file mode 100644 index 0000000000..85c5b20a81 --- /dev/null +++ b/src/libsystemd-network/src/sd-ipv4ll.c @@ -0,0 +1,345 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright (C) 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 . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/ether-addr-util.h" +#include "basic/in-addr-util.h" +#include "basic/list.h" +#include "basic/random-util.h" +#include "basic/siphash24.h" +#include "basic/sparse-endian.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "systemd-network/sd-ipv4acd.h" +#include "systemd-network/sd-ipv4ll.h" + +#define IPV4LL_NETWORK UINT32_C(0xA9FE0000) +#define IPV4LL_NETMASK UINT32_C(0xFFFF0000) + +#define IPV4LL_DONT_DESTROY(ll) \ + _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll) + +struct sd_ipv4ll { + unsigned n_ref; + + sd_ipv4acd *acd; + + be32_t address; /* the address pushed to ACD */ + struct ether_addr mac; + + struct { + le64_t value; + le64_t generation; + } seed; + bool seed_set; + + /* External */ + be32_t claimed_address; + + sd_ipv4ll_callback_t callback; + void* userdata; +}; + +#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__) +#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__) + +static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); + +sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { + if (!ll) + return NULL; + + assert(ll->n_ref >= 1); + ll->n_ref++; + + return ll; +} + +sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { + if (!ll) + return NULL; + + assert(ll->n_ref >= 1); + ll->n_ref--; + + if (ll->n_ref > 0) + return NULL; + + sd_ipv4acd_unref(ll->acd); + free(ll); + + return NULL; +} + +int sd_ipv4ll_new(sd_ipv4ll **ret) { + _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; + int r; + + assert_return(ret, -EINVAL); + + ll = new0(sd_ipv4ll, 1); + if (!ll) + return -ENOMEM; + + ll->n_ref = 1; + + r = sd_ipv4acd_new(&ll->acd); + if (r < 0) + return r; + + r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll); + if (r < 0) + return r; + + *ret = ll; + ll = NULL; + + return 0; +} + +int sd_ipv4ll_stop(sd_ipv4ll *ll) { + assert_return(ll, -EINVAL); + + return sd_ipv4acd_stop(ll->acd); +} + +int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) { + assert_return(ll, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + return sd_ipv4acd_set_ifindex(ll->acd, ifindex); +} + +int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { + int r; + + assert_return(ll, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + r = sd_ipv4acd_set_mac(ll->acd, addr); + if (r < 0) + return r; + + ll->mac = *addr; + return 0; +} + +int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { + assert_return(ll, -EINVAL); + + return sd_ipv4acd_detach_event(ll->acd); +} + +int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) { + assert_return(ll, -EINVAL); + + return sd_ipv4acd_attach_event(ll->acd, event, priority); +} + +int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) { + assert_return(ll, -EINVAL); + + ll->callback = cb; + ll->userdata = userdata; + + return 0; +} + +int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + + if (ll->claimed_address == 0) + return -ENOENT; + + address->s_addr = ll->claimed_address; + + return 0; +} + +int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) { + assert_return(ll, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + ll->seed.value = htole64(seed); + ll->seed_set = true; + + return 0; +} + +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) { + assert(address); + + if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) + return false; + + return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); +} + +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; +} + +#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b) + +static int ipv4ll_pick_address(sd_ipv4ll *ll) { + _cleanup_free_ char *address = NULL; + be32_t addr; + + assert(ll); + + do { + uint64_t h; + + h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes); + + /* Increase the generation counter by one */ + ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1); + + addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK); + } while (addr == ll->address || + IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U)); + + (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address); + log_ipv4ll(ll, "Picked new IP address %s.", strna(address)); + + return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); +} + +#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) + +int sd_ipv4ll_start(sd_ipv4ll *ll) { + int r; + bool picked_address = false; + + assert_return(ll, -EINVAL); + assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + /* If no random seed is set, generate some from the MAC address */ + if (!ll->seed_set) + ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); + + /* Restart the generation counter. */ + ll->seed.generation = 0; + + if (ll->address == 0) { + r = ipv4ll_pick_address(ll); + if (r < 0) + return r; + + picked_address = true; + } + + r = sd_ipv4acd_start(ll->acd); + if (r < 0) { + + /* We couldn't start? If so, let's forget the picked address again, the user might make a change and + * retry, and we want the new data to take effect when picking an address. */ + if (picked_address) + ll->address = 0; + + return r; + } + + return 0; +} + +static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { + assert(ll); + + if (ll->callback) + ll->callback(ll, event, ll->userdata); +} + +void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { + sd_ipv4ll *ll = userdata; + IPV4LL_DONT_DESTROY(ll); + int r; + + assert(acd); + assert(ll); + + switch (event) { + + case SD_IPV4ACD_EVENT_STOP: + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); + ll->claimed_address = 0; + break; + + case SD_IPV4ACD_EVENT_BIND: + ll->claimed_address = ll->address; + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND); + break; + + case SD_IPV4ACD_EVENT_CONFLICT: + /* if an address was already bound we must call up to the + user to handle this, otherwise we just try again */ + if (ll->claimed_address != 0) { + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT); + + ll->claimed_address = 0; + } else { + r = ipv4ll_pick_address(ll); + if (r < 0) + goto error; + + r = sd_ipv4acd_start(ll->acd); + if (r < 0) + goto error; + } + + break; + + default: + assert_not_reached("Invalid IPv4ACD event."); + } + + return; + +error: + ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP); +} diff --git a/src/libsystemd-network/src/sd-lldp.c b/src/libsystemd-network/src/sd-lldp.c new file mode 100644 index 0000000000..7a3fcb9472 --- /dev/null +++ b/src/libsystemd-network/src/sd-lldp.c @@ -0,0 +1,538 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/ether-addr-util.h" +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "systemd-network/lldp-internal.h" +#include "systemd-network/lldp-neighbor.h" +#include "systemd-network/lldp-network.h" +#include "systemd-network/sd-lldp.h" + +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U + +static void lldp_flush_neighbors(sd_lldp *lldp) { + sd_lldp_neighbor *n; + + assert(lldp); + + while ((n = hashmap_first(lldp->neighbor_by_id))) + lldp_neighbor_unlink(n); +} + +static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { + assert(lldp); + + log_lldp("Invoking callback for '%c'.", event); + + if (!lldp->callback) + return; + + lldp->callback(lldp, event, n, lldp->userdata); +} + +static int lldp_make_space(sd_lldp *lldp, size_t extra) { + usec_t t = USEC_INFINITY; + bool changed = false; + + assert(lldp); + + /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries + * are free. */ + + for (;;) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + + n = prioq_peek(lldp->neighbor_by_expiry); + if (!n) + break; + + sd_lldp_neighbor_ref(n); + + if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) + goto remove_one; + + if (t == USEC_INFINITY) + t = now(clock_boottime_or_monotonic()); + + if (n->until > t) + break; + + remove_one: + lldp_neighbor_unlink(n); + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); + changed = true; + } + + return changed; +} + +static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { + assert(lldp); + assert(n); + + /* Don't keep data with a zero TTL */ + if (n->ttl <= 0) + return false; + + /* Filter out data from the filter address */ + if (!ether_addr_is_null(&lldp->filter_address) && + ether_addr_equal(&lldp->filter_address, &n->source_address)) + return false; + + /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with + * no caps field set. */ + if (n->has_capabilities && + (n->enabled_capabilities & lldp->capability_mask) == 0) + return false; + + /* Keep everything else */ + return true; +} + +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); + +static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; + bool keep; + int r; + + assert(lldp); + assert(n); + assert(!n->lldp); + + keep = lldp_keep_neighbor(lldp, n); + + /* First retrieve the old entry for this MSAP */ + old = hashmap_get(lldp->neighbor_by_id, &n->id); + if (old) { + sd_lldp_neighbor_ref(old); + + if (!keep) { + lldp_neighbor_unlink(old); + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); + return 0; + } + + if (lldp_neighbor_equal(n, old)) { + /* Is this equal, then restart the TTL counter, but don't do anyting else. */ + old->timestamp = n->timestamp; + lldp_start_timer(lldp, old); + lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); + return 0; + } + + /* Data changed, remove the old entry, and add a new one */ + lldp_neighbor_unlink(old); + + } else if (!keep) + return 0; + + /* Then, make room for at least one new neighbor */ + lldp_make_space(lldp, 1); + + r = hashmap_put(lldp->neighbor_by_id, &n->id, n); + if (r < 0) + goto finish; + + r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); + if (r < 0) { + assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); + goto finish; + } + + n->lldp = lldp; + + lldp_start_timer(lldp, n); + lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); + + return 1; + +finish: + if (old) + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); + + return r; +} + +static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { + int r; + + assert(lldp); + assert(n); + + r = lldp_neighbor_parse(n); + if (r == -EBADMSG) /* Ignore bad messages */ + return 0; + if (r < 0) + return r; + + r = lldp_add_neighbor(lldp, n); + if (r < 0) { + log_lldp_errno(r, "Failed to add datagram. Ignoring."); + return 0; + } + + log_lldp("Successfully processed LLDP datagram."); + return 0; +} + +static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + ssize_t space, length; + sd_lldp *lldp = userdata; + struct timespec ts; + + assert(fd >= 0); + assert(lldp); + + space = next_datagram_size_fd(fd); + if (space < 0) + return log_lldp_errno(space, "Failed to determine datagram size to read: %m"); + + n = lldp_neighbor_new(space); + if (!n) + return -ENOMEM; + + length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); + if (length < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); + } + + if ((size_t) length != n->raw_size) { + log_lldp("Packet size mismatch."); + return -EINVAL; + } + + /* Try to get the timestamp of this packet if it is known */ + if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) + triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); + else + triple_timestamp_get(&n->timestamp); + + return lldp_handle_datagram(lldp, n); +} + +static void lldp_reset(sd_lldp *lldp) { + assert(lldp); + + lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); + lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); + lldp->fd = safe_close(lldp->fd); +} + +_public_ int sd_lldp_start(sd_lldp *lldp) { + int r; + + assert_return(lldp, -EINVAL); + assert_return(lldp->event, -EINVAL); + assert_return(lldp->ifindex > 0, -EINVAL); + + if (lldp->fd >= 0) + return 0; + + assert(!lldp->io_event_source); + + lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); + if (lldp->fd < 0) + return lldp->fd; + + r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); + + log_lldp("Started LLDP client"); + return 1; + +fail: + lldp_reset(lldp); + return r; +} + +_public_ int sd_lldp_stop(sd_lldp *lldp) { + assert_return(lldp, -EINVAL); + + if (lldp->fd < 0) + return 0; + + log_lldp("Stopping LLDP client"); + + lldp_reset(lldp); + lldp_flush_neighbors(lldp); + + return 1; +} + +_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { + int r; + + assert_return(lldp, -EINVAL); + assert_return(lldp->fd < 0, -EBUSY); + assert_return(!lldp->event, -EBUSY); + + if (event) + lldp->event = sd_event_ref(event); + else { + r = sd_event_default(&lldp->event); + if (r < 0) + return r; + } + + lldp->event_priority = priority; + + return 0; +} + +_public_ int sd_lldp_detach_event(sd_lldp *lldp) { + + assert_return(lldp, -EINVAL); + assert_return(lldp->fd < 0, -EBUSY); + + lldp->event = sd_event_unref(lldp->event); + return 0; +} + +_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) { + assert_return(lldp, NULL); + + return lldp->event; +} + +_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { + assert_return(lldp, -EINVAL); + + lldp->callback = cb; + lldp->userdata = userdata; + + return 0; +} + +_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { + assert_return(lldp, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(lldp->fd < 0, -EBUSY); + + lldp->ifindex = ifindex; + return 0; +} + +_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) { + + if (!lldp) + return NULL; + + assert(lldp->n_ref > 0); + lldp->n_ref++; + + return lldp; +} + +_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { + + if (!lldp) + return NULL; + + assert(lldp->n_ref > 0); + lldp->n_ref --; + + if (lldp->n_ref > 0) + return NULL; + + lldp_reset(lldp); + sd_lldp_detach_event(lldp); + lldp_flush_neighbors(lldp); + + hashmap_free(lldp->neighbor_by_id); + prioq_free(lldp->neighbor_by_expiry); + free(lldp); + + return NULL; +} + +_public_ int sd_lldp_new(sd_lldp **ret) { + _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; + int r; + + assert_return(ret, -EINVAL); + + lldp = new0(sd_lldp, 1); + if (!lldp) + return -ENOMEM; + + lldp->n_ref = 1; + lldp->fd = -1; + lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; + lldp->capability_mask = (uint16_t) -1; + + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); + if (!lldp->neighbor_by_id) + return -ENOMEM; + + r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); + if (r < 0) + return r; + + *ret = lldp; + lldp = NULL; + + return 0; +} + +static int neighbor_compare_func(const void *a, const void *b) { + const sd_lldp_neighbor * const*x = a, * const *y = b; + + return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id); +} + +static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { + sd_lldp *lldp = userdata; + int r, q; + + r = lldp_make_space(lldp, 0); + if (r < 0) + return log_lldp_errno(r, "Failed to make space: %m"); + + q = lldp_start_timer(lldp, NULL); + if (q < 0) + return log_lldp_errno(q, "Failed to restart timer: %m"); + + return 0; +} + +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { + sd_lldp_neighbor *n; + int r; + + assert(lldp); + + if (neighbor) + lldp_neighbor_start_ttl(neighbor); + + n = prioq_peek(lldp->neighbor_by_expiry); + if (!n) { + + if (lldp->timer_event_source) + return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF); + + return 0; + } + + if (lldp->timer_event_source) { + r = sd_event_source_set_time(lldp->timer_event_source, n->until); + if (r < 0) + return r; + + return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT); + } + + if (!lldp->event) + return 0; + + r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp); + if (r < 0) + return r; + + r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority); + if (r < 0) + return r; + + (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer"); + return 0; +} + +_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { + sd_lldp_neighbor **l = NULL, *n; + Iterator i; + int k = 0, r; + + assert_return(lldp, -EINVAL); + assert_return(ret, -EINVAL); + + if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ + *ret = NULL; + return 0; + } + + l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); + if (!l) + return -ENOMEM; + + r = lldp_start_timer(lldp, NULL); + if (r < 0) { + free(l); + return r; + } + + HASHMAP_FOREACH(n, lldp->neighbor_by_id, i) + l[k++] = sd_lldp_neighbor_ref(n); + + assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); + + /* Return things in a stable order */ + qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func); + *ret = l; + + return k; +} + +_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { + assert_return(lldp, -EINVAL); + assert_return(m <= 0, -EINVAL); + + lldp->neighbors_max = m; + lldp_make_space(lldp, 0); + + return 0; +} + +_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { + assert_return(lldp, -EINVAL); + assert_return(mask != 0, -EINVAL); + + lldp->capability_mask = mask; + + return 0; +} + +_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { + assert_return(lldp, -EINVAL); + + /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so + * that our own can be filtered out here. */ + + if (addr) + lldp->filter_address = *addr; + else + zero(lldp->filter_address); + + return 0; +} diff --git a/src/libsystemd-network/src/sd-ndisc.c b/src/libsystemd-network/src/sd-ndisc.c new file mode 100644 index 0000000000..91e4467371 --- /dev/null +++ b/src/libsystemd-network/src/sd-ndisc.c @@ -0,0 +1,421 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "systemd-network/icmp6-util.h" +#include "systemd-network/ndisc-internal.h" +#include "systemd-network/ndisc-router.h" +#include "systemd-network/sd-ndisc.h" + +#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC) +#define NDISC_MAX_ROUTER_SOLICITATIONS 3U + +static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) { + assert(ndisc); + + log_ndisc("Invoking callback for '%c'.", event); + + if (!ndisc->callback) + return; + + ndisc->callback(ndisc, event, rt, ndisc->userdata); +} + +_public_ int sd_ndisc_set_callback( + sd_ndisc *nd, + sd_ndisc_callback_t callback, + void *userdata) { + + assert_return(nd, -EINVAL); + + nd->callback = callback; + nd->userdata = userdata; + + return 0; +} + +_public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) { + assert_return(nd, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(nd->fd < 0, -EBUSY); + + nd->ifindex = ifindex; + return 0; +} + +_public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) { + assert_return(nd, -EINVAL); + + if (mac_addr) + nd->mac_addr = *mac_addr; + else + zero(nd->mac_addr); + + return 0; +} + +_public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) { + int r; + + assert_return(nd, -EINVAL); + assert_return(nd->fd < 0, -EBUSY); + 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; +} + +_public_ int sd_ndisc_detach_event(sd_ndisc *nd) { + + assert_return(nd, -EINVAL); + assert_return(nd->fd < 0, -EBUSY); + + nd->event = sd_event_unref(nd->event); + return 0; +} + +_public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) { + assert_return(nd, NULL); + + return nd->event; +} + +_public_ 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_reset(sd_ndisc *nd) { + assert(nd); + + nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); + nd->recv_event_source = sd_event_source_unref(nd->recv_event_source); + nd->fd = safe_close(nd->fd); + + return 0; +} + +_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) { + + if (!nd) + return NULL; + + assert(nd->n_ref > 0); + nd->n_ref--; + + if (nd->n_ref > 0) + return NULL; + + ndisc_reset(nd); + sd_ndisc_detach_event(nd); + free(nd); + + return NULL; +} + +_public_ int sd_ndisc_new(sd_ndisc **ret) { + _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; + + assert_return(ret, -EINVAL); + + nd = new0(sd_ndisc, 1); + if (!nd) + return -ENOMEM; + + nd->n_ref = 1; + nd->fd = -1; + + *ret = nd; + nd = NULL; + + return 0; +} + +_public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) { + assert_return(nd, -EINVAL); + assert_return(mtu, -EINVAL); + + if (nd->mtu == 0) + return -ENODATA; + + *mtu = nd->mtu; + return 0; +} + +_public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) { + assert_return(nd, -EINVAL); + assert_return(ret, -EINVAL); + + if (nd->hop_limit == 0) + return -ENODATA; + + *ret = nd->hop_limit; + return 0; +} + +static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) { + int r; + + assert(nd); + assert(rt); + + r = ndisc_router_parse(rt); + if (r == -EBADMSG) /* Bad packet */ + return 0; + if (r < 0) + return 0; + + /* Update global variables we keep */ + if (rt->mtu > 0) + nd->mtu = rt->mtu; + if (rt->hop_limit > 0) + nd->hop_limit = rt->hop_limit; + + log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec", + rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none", + rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium", + rt->lifetime); + + ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt); + return 0; +} + +static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; + sd_ndisc *nd = userdata; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */ + CMSG_SPACE(sizeof(struct timeval))]; + } 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; + ssize_t len, buflen; + + assert(s); + assert(nd); + assert(nd->event); + + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m"); + + rt = ndisc_router_new(buflen); + if (!rt) + return -ENOMEM; + + iov.iov_base = NDISC_ROUTER_RAW(rt); + iov.iov_len = rt->raw_size; + + len = recvmsg(fd, &msg, MSG_DONTWAIT); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m"); + } + + if ((size_t) len != rt->raw_size) { + log_ndisc("Packet size mismatch."); + return -EINVAL; + } + + if (msg.msg_namelen == sizeof(struct sockaddr_in6) && + sa.in6.sin6_family == AF_INET6) { + + if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) { + _cleanup_free_ char *addr = NULL; + + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr); + log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr)); + return 0; + } + + rt->address = sa.in6.sin6_addr; + + } else if (msg.msg_namelen > 0) { + log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen); + return -EINVAL; + } + + /* namelen == 0 only happens when running the test-suite over a socketpair */ + + 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("Received RA with invalid hop limit %d. Ignoring.", hops); + return 0; + } + } + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + } + + if (!triple_timestamp_is_set(&rt->timestamp)) + triple_timestamp_get(&rt->timestamp); + + nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); + + return ndisc_handle_datagram(nd, rt); +} + +static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_ndisc *nd = userdata; + usec_t time_now, next_timeout; + int r; + + assert(s); + assert(nd); + assert(nd->event); + + if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { + nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); + ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL); + return 0; + } + + r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); + if (r < 0) { + log_ndisc_errno(r, "Error sending Router Solicitation: %m"); + goto fail; + } + + log_ndisc("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_source_set_time(nd->timeout_event_source, next_timeout); + if (r < 0) { + log_ndisc_errno(r, "Error updating timer: %m"); + goto fail; + } + + r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT); + if (r < 0) { + log_ndisc_errno(r, "Error reenabling timer: %m"); + goto fail; + } + + return 0; + +fail: + sd_ndisc_stop(nd); + return 0; +} + +_public_ int sd_ndisc_stop(sd_ndisc *nd) { + assert_return(nd, -EINVAL); + + if (nd->fd < 0) + return 0; + + log_ndisc("Stopping IPv6 Router Solicitation client"); + + ndisc_reset(nd); + return 1; +} + +_public_ int sd_ndisc_start(sd_ndisc *nd) { + int r; + + assert_return(nd, -EINVAL); + assert_return(nd->event, -EINVAL); + assert_return(nd->ifindex > 0, -EINVAL); + + if (nd->fd >= 0) + return 0; + + assert(!nd->recv_event_source); + assert(!nd->timeout_event_source); + + nd->fd = icmp6_bind_router_solicitation(nd->ifindex); + if (nd->fd < 0) + return nd->fd; + + r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message"); + + r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout"); + + log_ndisc("Started IPv6 Router Solicitation client"); + return 1; + +fail: + ndisc_reset(nd); + return r; +} diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c deleted file mode 100644 index 27fcc332a3..0000000000 --- a/src/libsystemd-network/test-acd.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include -#include - -#include "sd-event.h" -#include "sd-ipv4acd.h" -#include "sd-netlink.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); - - switch (event) { - case SD_IPV4ACD_EVENT_BIND: - log_info("bound"); - break; - case SD_IPV4ACD_EVENT_CONFLICT: - log_info("conflict"); - break; - case SD_IPV4ACD_EVENT_STOP: - log_error("the client was stopped"); - break; - default: - assert_not_reached("invalid ACD event"); - } -} - -static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) { - sd_ipv4acd *acd; - - assert_se(sd_ipv4acd_new(&acd) >= 0); - assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0); - - assert_se(sd_ipv4acd_set_ifindex(acd, ifindex) >= 0); - assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0); - assert_se(sd_ipv4acd_set_address(acd, pa) >= 0); - assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0); - - log_info("starting IPv4ACD client"); - - assert_se(sd_ipv4acd_start(acd) >= 0); - - assert_se(sd_event_loop(e) >= 0); - - assert_se(!sd_ipv4acd_unref(acd)); - - return EXIT_SUCCESS; -} - -static int test_acd(const char *ifname, const char *address) { - _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; - - assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0); - - assert_se(sd_event_new(&e) >= 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_GETLINK, 0) >= 0); - assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); - assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); - - assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); - assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); - - client_run(ifindex, &pa.in, &ha, e); - - return EXIT_SUCCESS; -} - -int main(int argc, char *argv[]) { - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - if (argc == 3) - return test_acd(argv[1], argv[2]); - else { - log_error("This program takes two arguments.\n" - "\t %s ", program_invocation_short_name); - return EXIT_FAILURE; - } -} diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c deleted file mode 100644 index 2a101cb1fe..0000000000 --- a/src/libsystemd-network/test-dhcp-client.c +++ /dev/null @@ -1,513 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include - -#include "sd-dhcp-client.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "dhcp-identifier.h" -#include "dhcp-internal.h" -#include "dhcp-protocol.h" -#include "fd-util.h" -#include "util.h" - -static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; - -typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp); - -static bool verbose = true; -static int test_fd[2]; -static test_callback_recv_t callback_recv; -static be32_t xid; -static sd_event_source *test_hangcheck; - -static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { - assert_not_reached("Test case should have completed in 2 seconds"); - - return 0; -} - -static void test_request_basic(sd_event *e) { - int r; - - sd_dhcp_client *client; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - r = sd_dhcp_client_new(&client); - - assert_se(r >= 0); - assert_se(client); - - r = sd_dhcp_client_attach_event(client, e, 0); - assert_se(r >= 0); - - assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL); - assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL); - assert_se(sd_dhcp_client_set_ifindex(NULL, 0) == -EINVAL); - - assert_se(sd_dhcp_client_set_ifindex(client, 15) == 0); - assert_se(sd_dhcp_client_set_ifindex(client, -42) == -EINVAL); - assert_se(sd_dhcp_client_set_ifindex(client, -1) == -EINVAL); - assert_se(sd_dhcp_client_set_ifindex(client, 0) == -EINVAL); - assert_se(sd_dhcp_client_set_ifindex(client, 1) == 0); - - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_ROUTER) == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_HOST_NAME) == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); - - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_PAD) == -EINVAL); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_END) == -EINVAL); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_OVERLOAD) == -EINVAL); - assert_se(sd_dhcp_client_set_request_option(client, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST) - == -EINVAL); - - assert_se(sd_dhcp_client_set_request_option(client, 33) == 0); - assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, 44) == 0); - assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); - - sd_dhcp_client_unref(client); -} - -static void test_checksum(void) { - uint8_t buf[20] = { - 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff - }; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae)); -} - -static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) { - switch(code) { - case SD_DHCP_OPTION_CLIENT_IDENTIFIER: - { - uint32_t iaid; - struct duid duid; - size_t duid_len; - - assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); - assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, &iaid) >= 0); - - assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); - assert_se(len == 19); - assert_se(((uint8_t*) option)[0] == 0xff); - - assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0); - assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0); - break; - } - - default: - break; - } - - return 0; -} - -int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) { - size_t size; - _cleanup_free_ DHCPPacket *discover; - uint16_t ip_check, udp_check; - - assert_se(s >= 0); - assert_se(packet); - - size = sizeof(DHCPPacket); - assert_se(len > size); - - discover = memdup(packet, len); - - assert_se(discover->ip.ttl == IPDEFTTL); - assert_se(discover->ip.protocol == IPPROTO_UDP); - assert_se(discover->ip.saddr == INADDR_ANY); - assert_se(discover->ip.daddr == INADDR_BROADCAST); - assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT)); - assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER)); - - ip_check = discover->ip.check; - - discover->ip.ttl = 0; - discover->ip.check = discover->udp.len; - - udp_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip.ttl, len - 8); - assert_se(udp_check == 0xffff); - - discover->ip.ttl = IPDEFTTL; - discover->ip.check = ip_check; - - ip_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip, sizeof(discover->ip)); - assert_se(ip_check == 0xffff); - - assert_se(discover->dhcp.xid); - assert_se(memcmp(discover->dhcp.chaddr, &mac_addr, ETH_ALEN) == 0); - - size = len - sizeof(struct iphdr) - sizeof(struct udphdr); - - assert_se(callback_recv); - callback_recv(size, &discover->dhcp); - - return 575; -} - -int dhcp_network_bind_raw_socket( - int index, - union sockaddr_union *link, - uint32_t id, - const uint8_t *addr, size_t addr_len, - uint16_t arp_type) { - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) - return -errno; - - return test_fd[0]; -} - -int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { - int fd; - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - return fd; -} - -int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) { - return 0; -} - -static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { - int res; - - res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL); - assert_se(res == DHCP_DISCOVER); - - if (verbose) - printf(" recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid)); - - return 0; -} - -static void test_discover_message(sd_event *e) { - sd_dhcp_client *client; - int res, r; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - r = sd_dhcp_client_new(&client); - assert_se(r >= 0); - assert_se(client); - - r = sd_dhcp_client_attach_event(client, e, 0); - assert_se(r >= 0); - - assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); - - assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0); - - callback_recv = test_discover_message_verify; - - res = sd_dhcp_client_start(client); - - assert_se(res == 0 || res == -EINPROGRESS); - - sd_event_run(e, (uint64_t) -1); - - sd_dhcp_client_stop(client); - sd_dhcp_client_unref(client); - - test_fd[1] = safe_close(test_fd[1]); - - callback_recv = NULL; -} - -static uint8_t test_addr_acq_offer[] = { - 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, - 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, - 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, - 0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, - 0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36, - 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00, - 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, - 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, - 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static uint8_t test_addr_acq_ack[] = { - 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, - 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, - 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, - 0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36, - 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00, - 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, - 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, - 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static void test_addr_acq_acquired(sd_dhcp_client *client, int event, - void *userdata) { - sd_event *e = userdata; - sd_dhcp_lease *lease; - struct in_addr addr; - - assert_se(client); - assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); - - assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); - assert_se(lease); - - assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0); - assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44], - sizeof(addr.s_addr)) == 0); - - assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0); - assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285], - sizeof(addr.s_addr)) == 0); - - assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0); - assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308], - sizeof(addr.s_addr)) == 0); - - if (verbose) - printf(" DHCP address acquired\n"); - - sd_event_exit(e, 0); -} - -static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { - uint16_t udp_check = 0; - uint8_t *msg_bytes = (uint8_t *)request; - int res; - - 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] == SD_DHCP_OPTION_END); - - if (verbose) - printf(" recv DHCP Request 0x%08x\n", be32toh(xid)); - - memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check)); - memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid)); - memcpy(&test_addr_acq_ack[56], &mac_addr, ETHER_ADDR_LEN); - - callback_recv = NULL; - - res = write(test_fd[1], test_addr_acq_ack, - sizeof(test_addr_acq_ack)); - assert_se(res == sizeof(test_addr_acq_ack)); - - if (verbose) - printf(" send DHCP Ack\n"); - - return 0; -}; - -static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { - uint16_t udp_check = 0; - uint8_t *msg_bytes = (uint8_t *)discover; - int res; - - res = dhcp_option_parse(discover, size, check_options, NULL, NULL); - assert_se(res == DHCP_DISCOVER); - - assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); - - xid = discover->xid; - - if (verbose) - printf(" recv DHCP Discover 0x%08x\n", be32toh(xid)); - - memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check)); - memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid)); - memcpy(&test_addr_acq_offer[56], &mac_addr, ETHER_ADDR_LEN); - - callback_recv = test_addr_acq_recv_request; - - res = write(test_fd[1], test_addr_acq_offer, - sizeof(test_addr_acq_offer)); - assert_se(res == sizeof(test_addr_acq_offer)); - - if (verbose) - printf(" sent DHCP Offer\n"); - - return 0; -} - -static void test_addr_acq(sd_event *e) { - usec_t time_now = now(clock_boottime_or_monotonic()); - sd_dhcp_client *client; - int res, r; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - r = sd_dhcp_client_new(&client); - assert_se(r >= 0); - assert_se(client); - - r = sd_dhcp_client_attach_event(client, e, 0); - assert_se(r >= 0); - - assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); - - assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0); - - callback_recv = test_addr_acq_recv_discover; - - assert_se(sd_event_add_time(e, &test_hangcheck, - clock_boottime_or_monotonic(), - time_now + 2 * USEC_PER_SEC, 0, - test_dhcp_hangcheck, NULL) >= 0); - - res = sd_dhcp_client_start(client); - assert_se(res == 0 || res == -EINPROGRESS); - - assert_se(sd_event_loop(e) >= 0); - - test_hangcheck = sd_event_source_unref(test_hangcheck); - - assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0); - assert_se(sd_dhcp_client_stop(client) >= 0); - sd_dhcp_client_unref(client); - - test_fd[1] = safe_close(test_fd[1]); - - callback_recv = NULL; - xid = 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - assert_se(sd_event_new(&e) >= 0); - - test_request_basic(e); - test_checksum(); - - test_discover_message(e); - test_addr_acq(e); - -#ifdef VALGRIND - /* Make sure the async_close thread has finished. - * valgrind would report some of the phread_* structures - * as not cleaned up properly. */ - sleep(1); -#endif - - return 0; -} diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c deleted file mode 100644 index d84859c053..0000000000 --- a/src/libsystemd-network/test-dhcp-option.c +++ /dev/null @@ -1,367 +0,0 @@ -#include -#include -#include -#include - -#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]; - int snamelen; - uint8_t file[128]; - int filelen; - uint8_t options[128]; - int len; - bool success; - int filepos; - int snamepos; - int pos; -}; - -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, - 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, - 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0, - 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, - 40, true, }, - { {}, 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, 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, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9, - { 222, 3, 1, 2, 3 }, 5, - { SD_DHCP_OPTION_OVERLOAD, 1, - DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, }, -}; - -static const char *dhcp_type(int type) { - switch(type) { - case DHCP_DISCOVER: - return "DHCPDISCOVER"; - case DHCP_OFFER: - return "DHCPOFFER"; - case DHCP_REQUEST: - return "DHCPREQUEST"; - case DHCP_DECLINE: - return "DHCPDECLINE"; - case DHCP_ACK: - return "DHCPACK"; - case DHCP_NAK: - return "DHCPNAK"; - case DHCP_RELEASE: - return "DHCPRELEASE"; - default: - return "unknown"; - } -} - -static void test_invalid_buffer_length(void) { - DHCPMessage message; - - 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) { - _cleanup_free_ DHCPMessage *message = NULL; - size_t optlen = 4, optoffset; - size_t len = sizeof(DHCPMessage) + optlen; - uint8_t *magic; - - message = malloc0(len); - - assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, - DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0); - - assert_se(message->xid == htobe32(0x12345678)); - assert_se(message->op == BOOTREQUEST); - - magic = (uint8_t*)&message->magic; - - assert_se(magic[0] == 99); - assert_se(magic[1] == 130); - assert_se(magic[2] == 83); - assert_se(magic[3] == 99); - - assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0); -} - -static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, - uint8_t *file, uint8_t filelen, - uint8_t *sname, uint8_t snamelen) { - DHCPMessage *message; - size_t len = sizeof(DHCPMessage) + optlen; - - message = malloc0(len); - assert_se(message); - - memcpy_safe(&message->options, options, optlen); - memcpy_safe(&message->file, file, filelen); - memcpy_safe(&message->sname, sname, snamelen); - - return message; -} - -static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) { - assert(*descpos >= 0); - - while (*descpos < *desclen) { - switch(descoption[*descpos]) { - case SD_DHCP_OPTION_PAD: - *descpos += 1; - break; - - case SD_DHCP_OPTION_MESSAGE_TYPE: - case SD_DHCP_OPTION_OVERLOAD: - *descpos += 3; - break; - - default: - return; - } - } -} - -static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) { - struct option_desc *desc = userdata; - uint8_t *descoption = NULL; - int *desclen = NULL, *descpos = NULL; - uint8_t optcode = 0; - uint8_t optlen = 0; - uint8_t i; - - assert_se((!desc && !code && !len) || desc); - - if (!desc) - return -EINVAL; - - 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) { - - if (desc->pos >= 0) { - descoption = &desc->options[0]; - desclen = &desc->len; - descpos = &desc->pos; - } else if (desc->filepos >= 0) { - descoption = &desc->file[0]; - desclen = &desc->filelen; - descpos = &desc->filepos; - } else if (desc->snamepos >= 0) { - descoption = &desc->sname[0]; - desclen = &desc->snamelen; - descpos = &desc->snamepos; - } - - assert_se(descoption && desclen && descpos); - - if (*desclen) - test_ignore_opts(descoption, descpos, desclen); - - if (*descpos < *desclen) - break; - - if (*descpos == *desclen) - *descpos = -1; - } - - assert_se(descpos); - assert_se(*descpos != -1); - - optcode = descoption[*descpos]; - optlen = descoption[*descpos + 1]; - - if (verbose) - printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode, - len, optlen); - - assert_se(code == optcode); - assert_se(len == optlen); - - for (i = 0; i < len; i++) { - - if (verbose) - printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i], - descoption[*descpos + 2 + i]); - - assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]); - } - - if (verbose) - printf("\n"); - - *descpos += optlen + 2; - - test_ignore_opts(descoption, descpos, desclen); - - if (desc->pos != -1 && desc->pos == desc->len) - desc->pos = -1; - - if (desc->filepos != -1 && desc->filepos == desc->filelen) - desc->filepos = -1; - - if (desc->snamepos != -1 && desc->snamepos == desc->snamelen) - desc->snamepos = -1; - - return 0; -} - -static void test_options(struct option_desc *desc) { - uint8_t *options = NULL; - uint8_t *file = NULL; - uint8_t *sname = NULL; - int optlen = 0; - int filelen = 0; - int snamelen = 0; - int buflen = 0; - _cleanup_free_ DHCPMessage *message = NULL; - int res; - - if (desc) { - file = &desc->file[0]; - filelen = desc->filelen; - if (!filelen) - desc->filepos = -1; - - sname = &desc->sname[0]; - snamelen = desc->snamelen; - if (!snamelen) - desc->snamepos = -1; - - options = &desc->options[0]; - optlen = desc->len; - desc->pos = 0; - } - message = create_message(options, optlen, file, filelen, - sname, snamelen); - - buflen = sizeof(DHCPMessage) + optlen; - - if (!desc) { - 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, 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, NULL)) < 0); - - if (verbose) - printf("DHCP type %s\n", dhcp_type(res)); -} - -static uint8_t options[64] = { - 'A', 'B', 'C', 'D', - 160, 2, 0x11, 0x12, - 0, - 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, - 0, - 55, 3, 0x51, 0x52, 0x53, - 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 255 -}; - -static void test_option_set(void) { - _cleanup_free_ DHCPMessage *result = NULL; - size_t offset = 0, len, pos; - unsigned i; - - result = malloc0(sizeof(DHCPMessage) + 11); - assert_se(result); - - result->options[0] = 'A'; - result->options[1] = 'B'; - result->options[2] = 'C'; - result->options[3] = 'D'; - - 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, SD_DHCP_OPTION_PAD, - 0, NULL) == -ENOBUFS); - assert_se(offset == 4); - 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] != 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] == SD_DHCP_OPTION_PAD) - pos++; - else - pos += 2 + options[pos + 1]; - - if (pos < len) - assert_se(offset == pos); - } - - for (i = 0; i < 9; i++) { - if (verbose) - printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i], - options[i]); - assert_se(result->options[i] == options[i]); - } - - if (verbose) - printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9], - SD_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], - SD_DHCP_OPTION_PAD); - - assert_se(result->options[10] == SD_DHCP_OPTION_PAD); - - for (i = 0; i < pos - 8; i++) { - if (verbose) - printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i], - options[i + 9]); - assert_se(result->sname[i] == options[i + 9]); - } - - if (verbose) - printf ("\n"); -} - -int main(int argc, char *argv[]) { - unsigned i; - - test_invalid_buffer_length(); - test_message_init(); - - test_options(NULL); - - for (i = 0; i < ELEMENTSOF(option_tests); i++) - test_options(&option_tests[i]); - - test_option_set(); - - return 0; -} diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c deleted file mode 100644 index e81c508c7f..0000000000 --- a/src/libsystemd-network/test-dhcp-server.c +++ /dev/null @@ -1,261 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include - -#include "sd-dhcp-server.h" -#include "sd-event.h" - -#include "dhcp-server-internal.h" - -static void test_pool(struct in_addr *address, unsigned size, int ret) { - _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; - - assert_se(sd_dhcp_server_new(&server, 1) >= 0); - - assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); -} - -static int test_basic(sd_event *event) { - _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; - struct in_addr address_lo = { - .s_addr = htonl(INADDR_LOOPBACK), - }; - struct in_addr address_any = { - .s_addr = htonl(INADDR_ANY), - }; - int r; - - /* attach to loopback interface */ - assert_se(sd_dhcp_server_new(&server, 1) >= 0); - assert_se(server); - - assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0); - assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY); - assert_se(sd_dhcp_server_get_event(server) == event); - assert_se(sd_dhcp_server_detach_event(server) >= 0); - assert_se(!sd_dhcp_server_get_event(server)); - assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); - assert_se(sd_dhcp_server_attach_event(server, NULL, 0) == -EBUSY); - - assert_se(sd_dhcp_server_ref(server) == server); - assert_se(!sd_dhcp_server_unref(server)); - - assert_se(sd_dhcp_server_start(server) == -EUNATCH); - - assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); - assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); - assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); - assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); - - test_pool(&address_any, 1, -EINVAL); - test_pool(&address_lo, 1, 0); - - r = sd_dhcp_server_start(server); - - if (r == -EPERM) - return EXIT_TEST_SKIP; - assert_se(r >= 0); - - assert_se(sd_dhcp_server_start(server) == -EBUSY); - assert_se(sd_dhcp_server_stop(server) >= 0); - assert_se(sd_dhcp_server_stop(server) >= 0); - assert_se(sd_dhcp_server_start(server) >= 0); - - return 0; -} - -static void test_message_handler(void) { - _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; - struct { - DHCPMessage message; - struct { - uint8_t code; - uint8_t length; - uint8_t type; - } _packed_ option_type; - struct { - uint8_t code; - uint8_t length; - be32_t address; - } _packed_ option_requested_ip; - struct { - uint8_t code; - uint8_t length; - be32_t address; - } _packed_ option_server_id; - struct { - uint8_t code; - uint8_t length; - uint8_t id[7]; - } _packed_ option_client_id; - uint8_t end; - } _packed_ test = { - .message.op = BOOTREQUEST, - .message.htype = ARPHRD_ETHER, - .message.hlen = ETHER_ADDR_LEN, - .message.xid = htobe32(0x12345678), - .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, - .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE, - .option_type.length = 1, - .option_type.type = DHCP_DISCOVER, - .end = SD_DHCP_OPTION_END, - }; - struct in_addr address_lo = { - .s_addr = htonl(INADDR_LOOPBACK), - }; - - assert_se(sd_dhcp_server_new(&server, 1) >= 0); - assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); - assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); - assert_se(sd_dhcp_server_start(server) >= 0); - - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - - test.end = 0; - /* TODO, shouldn't this fail? */ - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - 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 = 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); - - test.message.op = 0; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.message.op = BOOTREQUEST; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - - test.message.htype = 0; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.message.htype = ARPHRD_ETHER; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - - test.message.hlen = 0; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.message.hlen = ETHER_ADDR_LEN; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); - - test.option_type.type = DHCP_REQUEST; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - 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 = 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); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); - - test.option_server_id.address = htobe32(0x12345678); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - test.option_server_id.address = htobe32(INADDR_LOOPBACK); - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - 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 = 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'; - test.option_client_id.id[2] = 'B'; - test.option_client_id.id[3] = 'C'; - test.option_client_id.id[4] = 'D'; - test.option_client_id.id[5] = 'E'; - test.option_client_id.id[6] = 'F'; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); - - test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); - 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, - }, b = { - .length = 4, - }; - uint8_t hash_key[HASH_KEY_SIZE] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - }; - - a.data = (uint8_t*)strdup("abcd"); - b.data = (uint8_t*)strdup("abcd"); - - assert_se(client_id_compare_func(&a, &b) == 0); - 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_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_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); - - free(b.data); - b.data = (uint8_t*)strdup("abce"); - assert_se(client_id_compare_func(&a, &b) != 0); - - free(a.data); - free(b.data); -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; - int r; - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - assert_se(sd_event_new(&e) >= 0); - - r = test_basic(e); - if (r != 0) - return r; - - test_message_handler(); - test_client_id_hash(); - - return 0; -} diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c deleted file mode 100644 index bd289fa802..0000000000 --- a/src/libsystemd-network/test-dhcp6-client.c +++ /dev/null @@ -1,763 +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 . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-dhcp6-client.h" -#include "sd-event.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'} -}; - -static bool verbose = true; - -static sd_event_source *hangcheck; -static int test_dhcp_fd[2]; -static int test_index = 42; -static int test_client_message_num; -static be32_t test_iaid = 0; -static uint8_t test_duid[14] = { }; - -static int test_client_basic(sd_event *e) { - sd_dhcp6_client *client; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(client); - - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - - assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0); - assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL); - assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); - - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); - - 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); - - assert_se(sd_dhcp6_client_detach_event(client) >= 0); - assert_se(!sd_dhcp6_client_unref(client)); - - return 0; -} - -static int test_option(sd_event *e) { - uint8_t packet[] = { - 'F', 'O', 'O', - 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, - 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, - '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'B', 'A', 'R', - }; - uint8_t result[] = { - 'F', 'O', 'O', - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 'B', 'A', 'R', - }; - uint16_t optcode; - size_t optlen; - uint8_t *optval, *buf, *out; - size_t zero = 0, pos = 3; - size_t buflen = sizeof(packet), outlen = sizeof(result); - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(buflen == outlen); - - assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen, - &optval) == -ENOMSG); - - buflen -= 3; - buf = &packet[3]; - outlen -= 3; - out = &result[3]; - - assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, - &optval) >= 0); - pos += 4 + optlen; - assert_se(buf == &packet[pos]); - assert_se(optcode == SD_DHCP6_OPTION_ORO); - assert_se(optlen == 7); - assert_se(buflen + pos == sizeof(packet)); - - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, - optval) >= 0); - assert_se(out == &result[pos]); - assert_se(*out == 0x00); - - assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, - &optval) >= 0); - pos += 4 + optlen; - assert_se(buf == &packet[pos]); - assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); - assert_se(optlen == 9); - assert_se(buflen + pos == sizeof(packet)); - - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, - optval) >= 0); - assert_se(out == &result[pos]); - assert_se(*out == 'B'); - - assert_se(memcmp(packet, result, sizeof(packet)) == 0); - - return 0; -} - -static uint8_t msg_advertise[198] = { - 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, - 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03, - 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00, - 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, - 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, - 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, - 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, - 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, - 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, - 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, - 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, - 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, - 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19, - 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, - 0x53, 0x00, 0x07, 0x00, 0x01, 0x00 -}; - -static uint8_t msg_reply[173] = { - 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, - 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01, - 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, - 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, - 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, - 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, - 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e, - 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, - 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17, - 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c, - 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, - 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d, - 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01 -}; - -static int test_advertise_option(sd_event *e) { - _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; - size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); - be32_t val; - uint8_t preference = 255; - struct in6_addr addr; - uint32_t lt_pref, lt_valid; - int r; - bool opt_clientid = false; - struct in6_addr *addrs; - char **domains; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(dhcp6_lease_new(&lease) >= 0); - - assert_se(advertise->type == DHCP6_ADVERTISE); - assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == - 0x0fb4e5); - - while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, - &optval)) >= 0) { - - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(optlen == 14); - - opt_clientid = true; - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(optlen == 94); - assert_se(!memcmp(optval, &msg_advertise[26], optlen)); - - val = htobe32(0x0ecfa37d); - assert_se(!memcmp(optval, &val, sizeof(val))); - - val = htobe32(80); - assert_se(!memcmp(optval + 4, &val, sizeof(val))); - - val = htobe32(120); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); - - assert_se(dhcp6_option_parse_ia(&optval, &optlen, - optcode, - &lease->ia) >= 0); - - break; - - case SD_DHCP6_OPTION_SERVERID: - assert_se(optlen == 14); - assert_se(!memcmp(optval, &msg_advertise[179], optlen)); - - assert_se(dhcp6_lease_set_serverid(lease, optval, - optlen) >= 0); - break; - - case SD_DHCP6_OPTION_PREFERENCE: - assert_se(optlen == 1); - assert_se(!*optval); - - assert_se(dhcp6_lease_set_preference(lease, - *optval) >= 0); - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(optlen == 2); - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_set_dns(lease, optval, - optlen) >= 0); - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - assert_se(optlen == 11); - assert_se(dhcp6_lease_set_domains(lease, optval, - optlen) >= 0); - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_set_sntp(lease, optval, - optlen) >= 0); - break; - - default: - break; - } - } - - - assert_se(r == -ENOMSG); - - assert_se(opt_clientid); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(lt_pref == 150); - assert_se(lt_valid == 180); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) == -ENOMSG); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) == -ENOMSG); - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) == -ENOMSG); - - assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); - assert_se(len == 14); - assert_se(!memcmp(opt, &msg_advertise[179], len)); - - assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); - assert_se(preference == 0); - - r = sd_dhcp6_lease_get_dns(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); - - r = sd_dhcp6_lease_get_domains(lease, &domains); - assert_se(r == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); - - return 0; -} - -static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { - assert_not_reached("Test case should have completed in 2 seconds"); - - return 0; -} - -static void test_client_solicit_cb(sd_dhcp6_client *client, int event, - void *userdata) { - sd_event *e = userdata; - sd_dhcp6_lease *lease; - struct in6_addr *addrs; - char **domains; - - assert_se(e); - assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - 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, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); - - if (verbose) - printf(" got DHCPv6 event %d\n", event); - - sd_event_exit(e, 0); -} - -static int test_client_send_reply(DHCP6Message *request) { - DHCP6Message reply; - - reply.transaction_id = request->transaction_id; - reply.type = DHCP6_REPLY; - - memcpy(msg_reply, &reply.transaction_id, 4); - - memcpy(&msg_reply[26], test_duid, sizeof(test_duid)); - - memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) - == sizeof(msg_reply)); - - return 0; -} - -static int test_client_verify_request(DHCP6Message *request, uint8_t *option, - size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; - bool found_clientid = false, found_iana = false, found_serverid = false, - found_elapsed_time = false; - int r; - struct in6_addr addr; - be32_t val; - uint32_t lt_pref, lt_valid; - - assert_se(request->type == DHCP6_REQUEST); - - assert_se(dhcp6_lease_new(&lease) >= 0); - - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(!memcmp(optval, &test_duid, - sizeof(test_duid))); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - - assert_se(optlen == 40); - assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid))); - - val = htobe32(80); - assert_se(!memcmp(optval + 4, &val, sizeof(val))); - - val = htobe32(120); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); - - assert_se(!dhcp6_option_parse_ia(&optval, &optlen, - optcode, &lease->ia)); - - break; - - case SD_DHCP6_OPTION_SERVERID: - assert_se(!found_serverid); - found_serverid = true; - - assert_se(optlen == 14); - assert_se(!memcmp(&msg_advertise[179], optval, optlen)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - } - } - - assert_se(r == -ENOMSG); - assert_se(found_clientid && found_iana && found_serverid && - found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(lt_pref == 150); - assert_se(lt_valid == 180); - - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) == -ENOMSG); - - return 0; -} - -static int test_client_send_advertise(DHCP6Message *solicit) { - DHCP6Message advertise; - - advertise.transaction_id = solicit->transaction_id; - advertise.type = DHCP6_ADVERTISE; - - memcpy(msg_advertise, &advertise.transaction_id, 4); - - memcpy(&msg_advertise[8], test_duid, sizeof(test_duid)); - - memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) - == sizeof(msg_advertise)); - - return 0; -} - -static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, - size_t len) { - uint8_t *optval; - uint16_t optcode; - size_t optlen; - bool found_clientid = false, found_iana = false, - found_elapsed_time = false; - int r; - - assert_se(solicit->type == DHCP6_SOLICIT); - - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - assert_se(optlen == 12); - - memcpy(&test_iaid, optval, sizeof(test_iaid)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - } - } - - assert_se(r == -ENOMSG); - assert_se(found_clientid && found_iana && found_elapsed_time); - - return 0; -} - -static void test_client_information_cb(sd_dhcp6_client *client, int event, - void *userdata) { - 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); - assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - - if (verbose) - printf(" got DHCPv6 event %d\n", event); - - assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); - assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); - assert_se(sd_dhcp6_client_stop(client) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); - - 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_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; - bool found_clientid = false, found_elapsed_time = false; - int r; - struct in6_addr addr; - uint32_t lt_pref, lt_valid; - - assert_se(information_request->type == DHCP6_INFORMATION_REQUEST); - - assert_se(dhcp6_lease_new(&lease) >= 0); - - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_not_reached("IA TA option must not be present"); - - break; - - case SD_DHCP6_OPTION_SERVERID: - assert_not_reached("Server ID option must not be present"); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - } - } - - assert_se(r == -ENOMSG); - assert_se(found_clientid && found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); - - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, - <_valid) == -ENOMSG); - - return 0; -} - -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { - struct in6_addr mcast = - IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; - DHCP6Message *message; - uint8_t *option; - - assert_se(s == test_dhcp_fd[0]); - assert_se(server_address); - assert_se(packet); - assert_se(len > sizeof(DHCP6Message) + 4); - - assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); - - message = (DHCP6Message *)packet; - option = (uint8_t *)(message + 1); - len -= sizeof(DHCP6Message); - - assert_se(message->transaction_id & 0x00ffffff); - - if (test_client_message_num == 0) { - test_client_verify_information_request(message, option, len); - test_client_send_reply(message); - test_client_message_num++; - } else if (test_client_message_num == 1) { - test_client_verify_solicit(message, option, len); - test_client_send_advertise(message); - test_client_message_num++; - } else if (test_client_message_num == 2) { - test_client_verify_request(message, option, len); - test_client_send_reply(message); - test_client_message_num++; - } - - return len; -} - -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - assert_se(index == test_index); - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0) - return -errno; - - return test_dhcp_fd[0]; -} - -static int test_client_solicit(sd_event *e) { - sd_dhcp6_client *client; - usec_t time_now = now(clock_boottime_or_monotonic()); - 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__); - - assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(client); - - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - - assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0); - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); - - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val == false); - assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val == true); - - assert_se(sd_dhcp6_client_set_callback(client, - test_client_information_cb, e) >= 0); - - assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(), - 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); - - hangcheck = sd_event_source_unref(hangcheck); - - assert_se(!sd_dhcp6_client_unref(client)); - - test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; - - assert_se(sd_event_new(&e) >= 0); - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - test_client_basic(e); - test_option(e); - test_advertise_option(e); - test_client_solicit(e); - - return 0; -} diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c deleted file mode 100644 index 2b1387fa91..0000000000 --- a/src/libsystemd-network/test-ipv4ll-manual.c +++ /dev/null @@ -1,128 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-event.h" -#include "sd-ipv4ll.h" -#include "sd-netlink.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; - struct in_addr addr = {}; - - assert_se(ll); - - if (sd_ipv4ll_get_address(ll, &addr) >= 0) - assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0); - - switch (event) { - case SD_IPV4LL_EVENT_BIND: - log_info("bound %s", strna(address)); - break; - case SD_IPV4LL_EVENT_CONFLICT: - log_info("conflict on %s", strna(address)); - break; - case SD_IPV4LL_EVENT_STOP: - log_error("the client was stopped with address %s", strna(address)); - break; - default: - assert_not_reached("invalid LL event"); - } -} - -static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) { - sd_ipv4ll *ll; - - assert_se(sd_ipv4ll_new(&ll) >= 0); - assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0); - - assert_se(sd_ipv4ll_set_ifindex(ll, ifindex) >= 0); - assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0); - assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0); - - if (seed_str) { - unsigned seed; - - assert_se(safe_atou(seed_str, &seed) >= 0); - - assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0); - } - - log_info("starting IPv4LL client"); - - assert_se(sd_ipv4ll_start(ll) >= 0); - - assert_se(sd_event_loop(e) >= 0); - - assert_se(!sd_ipv4ll_unref(ll)); - - return EXIT_SUCCESS; -} - -static int test_ll(const char *ifname, const char *seed) { - _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; - - assert_se(sd_event_new(&e) >= 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_GETLINK, 0) >= 0); - assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); - assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); - - assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); - assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); - - client_run(ifindex, seed, &ha, e); - - return EXIT_SUCCESS; -} - -int main(int argc, char *argv[]) { - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - if (argc == 2) - return test_ll(argv[1], NULL); - else if (argc == 3) - return test_ll(argv[1], argv[2]); - else { - log_error("This program takes one or two arguments.\n" - "\t %s []", program_invocation_short_name); - return EXIT_FAILURE; - } -} diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c deleted file mode 100644 index fe70697075..0000000000 --- a/src/libsystemd-network/test-ipv4ll.c +++ /dev/null @@ -1,221 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#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; -static int test_fd[2]; - -static int basic_request_handler_bind = 0; -static int basic_request_handler_stop = 0; -static void* basic_request_handler_userdata = (void*) 0xCABCAB; - -static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { - assert_se(userdata == basic_request_handler_userdata); - - switch(event) { - case SD_IPV4LL_EVENT_STOP: - basic_request_handler_stop = 1; - break; - case SD_IPV4LL_EVENT_BIND: - basic_request_handler_bind = 1; - break; - default: - assert_se(0); - break; - } -} - -static int arp_network_send_raw_socket(int fd, int ifindex, - const struct ether_arp *arp) { - assert_se(arp); - assert_se(ifindex > 0); - assert_se(fd >= 0); - - if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) - return -errno; - - return 0; -} - -int arp_send_probe(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - struct ether_arp ea = {}; - - assert(fd >= 0); - assert(ifindex > 0); - assert(pa != 0); - assert(ha); - - return arp_network_send_raw_socket(fd, ifindex, &ea); -} - -int arp_send_announcement(int fd, int ifindex, - be32_t pa, const struct ether_addr *ha) { - struct ether_arp ea = {}; - - assert(fd >= 0); - assert(ifindex > 0); - assert(pa != 0); - assert(ha); - - return arp_network_send_raw_socket(fd, ifindex, &ea); -} - -int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { - if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) - return -errno; - - return test_fd[0]; -} - -static void test_public_api_setters(sd_event *e) { - struct in_addr address = {}; - uint64_t seed = 0; - sd_ipv4ll *ll; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(sd_ipv4ll_new(&ll) == 0); - assert_se(ll); - - assert_se(sd_ipv4ll_attach_event(NULL, NULL, 0) == -EINVAL); - assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); - assert_se(sd_ipv4ll_attach_event(ll, e, 0) == -EBUSY); - - 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); - - assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL); - assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL); - assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); - - assert_se(sd_ipv4ll_set_ifindex(NULL, -1) == -EINVAL); - assert_se(sd_ipv4ll_set_ifindex(ll, -1) == -EINVAL); - assert_se(sd_ipv4ll_set_ifindex(ll, -99) == -EINVAL); - assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0); - assert_se(sd_ipv4ll_set_ifindex(ll, 99) == 0); - - assert_se(sd_ipv4ll_ref(ll) == ll); - assert_se(sd_ipv4ll_unref(ll) == NULL); - - /* Cleanup */ - assert_se(sd_ipv4ll_unref(ll) == NULL); -} - -static void test_basic_request(sd_event *e) { - - sd_ipv4ll *ll; - struct ether_arp arp; - struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - assert_se(sd_ipv4ll_new(&ll) == 0); - assert_se(sd_ipv4ll_start(ll) == -EINVAL); - - assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); - assert_se(sd_ipv4ll_start(ll) == -EINVAL); - - assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); - assert_se(sd_ipv4ll_start(ll) == -EINVAL); - - assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler, - basic_request_handler_userdata) == 0); - assert_se(sd_ipv4ll_start(ll) == -EINVAL); - - assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0); - assert_se(sd_ipv4ll_start(ll) == 0); - - sd_event_run(e, (uint64_t) -1); - assert_se(sd_ipv4ll_start(ll) == -EBUSY); - - assert_se(sd_ipv4ll_is_running(ll)); - - /* PROBE */ - sd_event_run(e, (uint64_t) -1); - assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); - - if (extended) { - /* PROBE */ - sd_event_run(e, (uint64_t) -1); - assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); - - /* PROBE */ - sd_event_run(e, (uint64_t) -1); - assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); - - sd_event_run(e, (uint64_t) -1); - assert_se(basic_request_handler_bind == 1); - } - - sd_ipv4ll_stop(ll); - assert_se(basic_request_handler_stop == 1); - - /* Cleanup */ - assert_se(sd_ipv4ll_unref(ll) == NULL); - safe_close(test_fd[1]); -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - assert_se(sd_event_new(&e) >= 0); - - test_public_api_setters(e); - test_basic_request(e); - - return 0; -} diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c deleted file mode 100644 index 6bcd65de0a..0000000000 --- a/src/libsystemd-network/test-lldp.c +++ /dev/null @@ -1,261 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-event.h" -#include "sd-lldp.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "lldp-network.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] = { -1, -1 }; -static int lldp_handler_calls; - -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 void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { - lldp_handler_calls++; -} - -static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { - int r; - - r = sd_lldp_new(lldp); - if (r < 0) - return r; - - r = sd_lldp_set_ifindex(*lldp, 42); - if (r < 0) - return r; - - r = sd_lldp_set_callback(*lldp, cb, cb_data); - if (r < 0) - return r; - - r = sd_lldp_attach_event(*lldp, e, 0); - if (r < 0) - return r; - - r = sd_lldp_start(*lldp); - if (r < 0) - return r; - - return 0; -} - -static int stop_lldp(sd_lldp *lldp) { - int r; - - r = sd_lldp_stop(lldp); - if (r < 0) - return r; - - r = sd_lldp_detach_event(lldp); - if (r < 0) - return r; - - sd_lldp_unref(lldp); - safe_close(test_fd[1]); - - return 0; -} - -static void test_receive_basic_packet(sd_event *e) { - - static const 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 */ - }; - - sd_lldp *lldp; - sd_lldp_neighbor **neighbors; - uint8_t type; - const void *data; - uint16_t ttl; - size_t length; - const char *str; - - 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_neighbors(lldp, &neighbors) == 1); - - assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); - assert_se(type == SD_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_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); - assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); - assert_se(length == 3); - assert_se(strneq((char *) data, "1/3", 3)); - - assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); - assert_se(streq(str, "Port")); - - assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); - assert_se(streq(str, "SYS")); - - assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); - assert_se(streq(str, "foo")); - - assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); - assert_se(ttl == 120); - - sd_lldp_neighbor_unref(neighbors[0]); - free(neighbors); - - assert_se(stop_lldp(lldp) == 0); -} - -static void test_receive_incomplete_packet(sd_event *e) { - sd_lldp *lldp; - sd_lldp_neighbor **neighbors; - 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_neighbors(lldp, &neighbors) == 0); - - assert_se(stop_lldp(lldp) == 0); -} - -static void test_receive_oui_packet(sd_event *e) { - sd_lldp *lldp; - sd_lldp_neighbor **neighbors; - 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_neighbors(lldp, &neighbors) == 1); - - assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); - assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); - assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); - - sd_lldp_neighbor_unref(neighbors[0]); - free(neighbors); - - assert_se(stop_lldp(lldp) == 0); -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - - log_set_max_level(LOG_DEBUG); - - /* 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 deleted file mode 100644 index d9669488be..0000000000 --- a/src/libsystemd-network/test-ndisc-rs.c +++ /dev/null @@ -1,317 +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 . -***/ - -#include -#include - -#include "sd-ndisc.h" - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "icmp6-util.h" -#include "socket-util.h" -#include "strv.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 void router_dump(sd_ndisc_router *rt) { - struct in6_addr addr; - char buf[FORMAT_TIMESTAMP_MAX]; - uint8_t hop_limit; - uint64_t t, flags; - uint32_t mtu; - uint16_t lifetime; - unsigned preference; - int r; - - assert_se(rt); - - log_info("--"); - assert_se(sd_ndisc_router_get_address(rt, &addr) == -ENODATA); - - assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0); - log_info("Timestamp: %s", format_timestamp(buf, sizeof(buf), t)); - - assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0); - log_info("Monotonic: %" PRIu64, t); - - if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0) - log_info("No hop limit set"); - else - log_info("Hop limit: %u", hop_limit); - - assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); - log_info("Flags: <%s|%s>", - flags & ND_RA_FLAG_OTHER ? "OTHER" : "", - flags & ND_RA_FLAG_MANAGED ? "MANAGED" : ""); - - assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0); - log_info("Preference: %s", - preference == SD_NDISC_PREFERENCE_LOW ? "low" : - preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium"); - - assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0); - log_info("Lifetime: %" PRIu16, lifetime); - - if (sd_ndisc_router_get_mtu(rt, &mtu) < 0) - log_info("No MTU set"); - else - log_info("MTU: %" PRIu32, mtu); - - r = sd_ndisc_router_option_rewind(rt); - for (;;) { - uint8_t type; - - assert_se(r >= 0); - - if (r == 0) - break; - - assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0); - - log_info(">> Option %u", type); - - switch (type) { - - case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: - case SD_NDISC_OPTION_TARGET_LL_ADDRESS: { - _cleanup_free_ char *c = NULL; - const void *p; - size_t n; - - assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0); - assert_se(n > 2); - assert_se(c = hexmem((uint8_t*) p + 2, n - 2)); - - log_info("Address: %s", c); - break; - } - - case SD_NDISC_OPTION_PREFIX_INFORMATION: { - uint32_t lifetime_valid, lifetime_preferred; - unsigned prefix_len; - uint8_t pfl; - struct in6_addr a; - char buff[INET6_ADDRSTRLEN]; - - assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid) >= 0); - log_info("Valid Lifetime: %" PRIu32, lifetime_valid); - - assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred) >= 0); - log_info("Preferred Lifetime: %" PRIu32, lifetime_preferred); - - assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0); - log_info("Flags: <%s|%s>", - pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "", - pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : ""); - - assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0); - log_info("Prefix Length: %u", prefix_len); - - assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0); - log_info("Prefix: %s", inet_ntop(AF_INET6, &a, buff, sizeof(buff))); - - break; - } - - case SD_NDISC_OPTION_RDNSS: { - const struct in6_addr *a; - uint32_t lt; - int n, i; - - n = sd_ndisc_router_rdnss_get_addresses(rt, &a); - assert_se(n > 0); - - for (i = 0; i < n; i++) { - char buff[INET6_ADDRSTRLEN]; - log_info("DNS: %s", inet_ntop(AF_INET6, a + i, buff, sizeof(buff))); - } - - assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, <) >= 0); - log_info("Lifetime: %" PRIu32, lt); - break; - } - - case SD_NDISC_OPTION_DNSSL: { - _cleanup_strv_free_ char **l = NULL; - uint32_t lt; - int n, i; - - n = sd_ndisc_router_dnssl_get_domains(rt, &l); - assert_se(n > 0); - - for (i = 0; i < n; i++) - log_info("Domain: %s", l[i]); - - assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, <) >= 0); - log_info("Lifetime: %" PRIu32, lt); - break; - }} - - r = sd_ndisc_router_option_next(rt); - } -} - -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_callback(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { - sd_event *e = userdata; - static unsigned idx = 0; - uint64_t flags_array[] = { - 0, - 0, - 0, - ND_RA_FLAG_OTHER, - ND_RA_FLAG_MANAGED - }; - uint64_t flags; - uint32_t mtu; - - assert_se(nd); - - if (event != SD_NDISC_EVENT_ROUTER) - return; - - router_dump(rt); - - assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); - assert_se(flags == flags_array[idx]); - idx++; - - if (verbose) - printf(" got event 0x%02" PRIx64 "\n", flags); - - if (idx < ELEMENTSOF(flags_array)) { - send_ra(flags_array[idx]); - return; - } - - assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENODATA); - - 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_ifindex(nd, 42) >= 0); - assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_ndisc_set_callback(nd, test_callback, 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_start(nd) >= 0); - assert_se(sd_ndisc_stop(nd) >= 0); - - assert_se(sd_ndisc_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/Makefile b/src/libsystemd-network/test/Makefile new file mode 100644 index 0000000000..8cc38bb547 --- /dev/null +++ b/src/libsystemd-network/test/Makefile @@ -0,0 +1,118 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +test_dhcp_option_SOURCES = \ + src/libsystemd-network/dhcp-protocol.h \ + src/libsystemd-network/dhcp-internal.h \ + src/libsystemd-network/test-dhcp-option.c + +test_dhcp_option_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_dhcp_client_SOURCES = \ + src/systemd/sd-dhcp-client.h \ + src/libsystemd-network/dhcp-protocol.h \ + src/libsystemd-network/dhcp-internal.h \ + src/libsystemd-network/test-dhcp-client.c + +test_dhcp_client_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_dhcp_server_SOURCES = \ + src/libsystemd-network/test-dhcp-server.c + +test_dhcp_server_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_ipv4ll_SOURCES = \ + src/systemd/sd-ipv4ll.h \ + src/libsystemd-network/arp-util.h \ + src/libsystemd-network/test-ipv4ll.c + +test_ipv4ll_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_ipv4ll_manual_SOURCES = \ + src/systemd/sd-ipv4ll.h \ + src/libsystemd-network/test-ipv4ll-manual.c + +test_ipv4ll_manual_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_acd_SOURCES = \ + src/systemd/sd-ipv4acd.h \ + src/libsystemd-network/test-acd.c + +test_acd_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +test_ndisc_rs_SOURCES = \ + src/systemd/sd-dhcp6-client.h \ + src/systemd/sd-ndisc.h \ + src/libsystemd-network/icmp6-util.h \ + src/libsystemd-network/test-ndisc-rs.c \ + src/libsystemd-network/dhcp-identifier.h \ + src/libsystemd-network/dhcp-identifier.c + +test_ndisc_rs_LDADD = \ + libsystemd-network.la \ + libudev.la \ + libsystemd-shared.la + +test_dhcp6_client_SOURCES = \ + src/systemd/sd-dhcp6-client.h \ + src/libsystemd-network/dhcp6-internal.h \ + src/libsystemd-network/test-dhcp6-client.c \ + src/libsystemd-network/dhcp-identifier.h \ + src/libsystemd-network/dhcp-identifier.c + +test_dhcp6_client_LDADD = \ + libsystemd-network.la \ + libudev.la \ + libsystemd-shared.la + +test_lldp_SOURCES = \ + src/libsystemd-network/test-lldp.c + +test_lldp_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la + +tests += \ + test-dhcp-option \ + test-dhcp-client \ + test-dhcp-server \ + test-ipv4ll \ + test-ndisc-rs \ + test-dhcp6-client \ + test-lldp + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd-network/test/test-acd.c b/src/libsystemd-network/test/test-acd.c new file mode 100644 index 0000000000..d79e71ab90 --- /dev/null +++ b/src/libsystemd-network/test/test-acd.c @@ -0,0 +1,114 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include + +#include + +#include "basic/in-addr-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "systemd-network/sd-ipv4acd.h" + +static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) { + assert_se(acd); + + switch (event) { + case SD_IPV4ACD_EVENT_BIND: + log_info("bound"); + break; + case SD_IPV4ACD_EVENT_CONFLICT: + log_info("conflict"); + break; + case SD_IPV4ACD_EVENT_STOP: + log_error("the client was stopped"); + break; + default: + assert_not_reached("invalid ACD event"); + } +} + +static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) { + sd_ipv4acd *acd; + + assert_se(sd_ipv4acd_new(&acd) >= 0); + assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0); + + assert_se(sd_ipv4acd_set_ifindex(acd, ifindex) >= 0); + assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0); + assert_se(sd_ipv4acd_set_address(acd, pa) >= 0); + assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0); + + log_info("starting IPv4ACD client"); + + assert_se(sd_ipv4acd_start(acd) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4acd_unref(acd)); + + return EXIT_SUCCESS; +} + +static int test_acd(const char *ifname, const char *address) { + _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; + + assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0); + + assert_se(sd_event_new(&e) >= 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_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, &pa.in, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 3) + return test_acd(argv[1], argv[2]); + else { + log_error("This program takes two arguments.\n" + "\t %s ", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test/test-dhcp-client.c b/src/libsystemd-network/test/test-dhcp-client.c new file mode 100644 index 0000000000..2b40ab8f48 --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-client.c @@ -0,0 +1,513 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/util.h" +#include "systemd-network/dhcp-identifier.h" +#include "systemd-network/dhcp-internal.h" +#include "systemd-network/dhcp-protocol.h" +#include "systemd-network/sd-dhcp-client.h" + +static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; + +typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp); + +static bool verbose = true; +static int test_fd[2]; +static test_callback_recv_t callback_recv; +static be32_t xid; +static sd_event_source *test_hangcheck; + +static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { + assert_not_reached("Test case should have completed in 2 seconds"); + + return 0; +} + +static void test_request_basic(sd_event *e) { + int r; + + sd_dhcp_client *client; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + r = sd_dhcp_client_new(&client); + + assert_se(r >= 0); + assert_se(client); + + r = sd_dhcp_client_attach_event(client, e, 0); + assert_se(r >= 0); + + assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL); + assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL); + assert_se(sd_dhcp_client_set_ifindex(NULL, 0) == -EINVAL); + + assert_se(sd_dhcp_client_set_ifindex(client, 15) == 0); + assert_se(sd_dhcp_client_set_ifindex(client, -42) == -EINVAL); + assert_se(sd_dhcp_client_set_ifindex(client, -1) == -EINVAL); + assert_se(sd_dhcp_client_set_ifindex(client, 0) == -EINVAL); + assert_se(sd_dhcp_client_set_ifindex(client, 1) == 0); + + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_ROUTER) == -EEXIST); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_HOST_NAME) == -EEXIST); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); + + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_PAD) == -EINVAL); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_END) == -EINVAL); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_OVERLOAD) == -EINVAL); + assert_se(sd_dhcp_client_set_request_option(client, + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST) + == -EINVAL); + + assert_se(sd_dhcp_client_set_request_option(client, 33) == 0); + assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); + assert_se(sd_dhcp_client_set_request_option(client, 44) == 0); + assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); + + sd_dhcp_client_unref(client); +} + +static void test_checksum(void) { + uint8_t buf[20] = { + 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff + }; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae)); +} + +static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) { + switch(code) { + case SD_DHCP_OPTION_CLIENT_IDENTIFIER: + { + uint32_t iaid; + struct duid duid; + size_t duid_len; + + assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); + assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, &iaid) >= 0); + + assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); + assert_se(len == 19); + assert_se(((uint8_t*) option)[0] == 0xff); + + assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0); + assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0); + break; + } + + default: + break; + } + + return 0; +} + +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) { + size_t size; + _cleanup_free_ DHCPPacket *discover; + uint16_t ip_check, udp_check; + + assert_se(s >= 0); + assert_se(packet); + + size = sizeof(DHCPPacket); + assert_se(len > size); + + discover = memdup(packet, len); + + assert_se(discover->ip.ttl == IPDEFTTL); + assert_se(discover->ip.protocol == IPPROTO_UDP); + assert_se(discover->ip.saddr == INADDR_ANY); + assert_se(discover->ip.daddr == INADDR_BROADCAST); + assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT)); + assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER)); + + ip_check = discover->ip.check; + + discover->ip.ttl = 0; + discover->ip.check = discover->udp.len; + + udp_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip.ttl, len - 8); + assert_se(udp_check == 0xffff); + + discover->ip.ttl = IPDEFTTL; + discover->ip.check = ip_check; + + ip_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip, sizeof(discover->ip)); + assert_se(ip_check == 0xffff); + + assert_se(discover->dhcp.xid); + assert_se(memcmp(discover->dhcp.chaddr, &mac_addr, ETH_ALEN) == 0); + + size = len - sizeof(struct iphdr) - sizeof(struct udphdr); + + assert_se(callback_recv); + callback_recv(size, &discover->dhcp); + + return 575; +} + +int dhcp_network_bind_raw_socket( + int index, + union sockaddr_union *link, + uint32_t id, + const uint8_t *addr, size_t addr_len, + uint16_t arp_type) { + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) + return -errno; + + return test_fd[0]; +} + +int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { + int fd; + + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + return fd; +} + +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) { + return 0; +} + +static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { + int res; + + res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL); + assert_se(res == DHCP_DISCOVER); + + if (verbose) + printf(" recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid)); + + return 0; +} + +static void test_discover_message(sd_event *e) { + sd_dhcp_client *client; + int res, r; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + r = sd_dhcp_client_new(&client); + assert_se(r >= 0); + assert_se(client); + + r = sd_dhcp_client_attach_event(client, e, 0); + assert_se(r >= 0); + + assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + + assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0); + + callback_recv = test_discover_message_verify; + + res = sd_dhcp_client_start(client); + + assert_se(res == 0 || res == -EINPROGRESS); + + sd_event_run(e, (uint64_t) -1); + + sd_dhcp_client_stop(client); + sd_dhcp_client_unref(client); + + test_fd[1] = safe_close(test_fd[1]); + + callback_recv = NULL; +} + +static uint8_t test_addr_acq_offer[] = { + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, + 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, + 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, + 0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, + 0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36, + 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00, + 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, + 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, + 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static uint8_t test_addr_acq_ack[] = { + 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, + 0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, + 0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, + 0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36, + 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x33, 0x04, 0x00, + 0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, + 0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, + 0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void test_addr_acq_acquired(sd_dhcp_client *client, int event, + void *userdata) { + sd_event *e = userdata; + sd_dhcp_lease *lease; + struct in_addr addr; + + assert_se(client); + assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); + + assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); + assert_se(lease); + + assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0); + assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44], + sizeof(addr.s_addr)) == 0); + + assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0); + assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285], + sizeof(addr.s_addr)) == 0); + + assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0); + assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308], + sizeof(addr.s_addr)) == 0); + + if (verbose) + printf(" DHCP address acquired\n"); + + sd_event_exit(e, 0); +} + +static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { + uint16_t udp_check = 0; + uint8_t *msg_bytes = (uint8_t *)request; + int res; + + 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] == SD_DHCP_OPTION_END); + + if (verbose) + printf(" recv DHCP Request 0x%08x\n", be32toh(xid)); + + memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check)); + memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid)); + memcpy(&test_addr_acq_ack[56], &mac_addr, ETHER_ADDR_LEN); + + callback_recv = NULL; + + res = write(test_fd[1], test_addr_acq_ack, + sizeof(test_addr_acq_ack)); + assert_se(res == sizeof(test_addr_acq_ack)); + + if (verbose) + printf(" send DHCP Ack\n"); + + return 0; +}; + +static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { + uint16_t udp_check = 0; + uint8_t *msg_bytes = (uint8_t *)discover; + int res; + + res = dhcp_option_parse(discover, size, check_options, NULL, NULL); + assert_se(res == DHCP_DISCOVER); + + assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); + + xid = discover->xid; + + if (verbose) + printf(" recv DHCP Discover 0x%08x\n", be32toh(xid)); + + memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check)); + memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid)); + memcpy(&test_addr_acq_offer[56], &mac_addr, ETHER_ADDR_LEN); + + callback_recv = test_addr_acq_recv_request; + + res = write(test_fd[1], test_addr_acq_offer, + sizeof(test_addr_acq_offer)); + assert_se(res == sizeof(test_addr_acq_offer)); + + if (verbose) + printf(" sent DHCP Offer\n"); + + return 0; +} + +static void test_addr_acq(sd_event *e) { + usec_t time_now = now(clock_boottime_or_monotonic()); + sd_dhcp_client *client; + int res, r; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + r = sd_dhcp_client_new(&client); + assert_se(r >= 0); + assert_se(client); + + r = sd_dhcp_client_attach_event(client, e, 0); + assert_se(r >= 0); + + assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + + assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0); + + callback_recv = test_addr_acq_recv_discover; + + assert_se(sd_event_add_time(e, &test_hangcheck, + clock_boottime_or_monotonic(), + time_now + 2 * USEC_PER_SEC, 0, + test_dhcp_hangcheck, NULL) >= 0); + + res = sd_dhcp_client_start(client); + assert_se(res == 0 || res == -EINPROGRESS); + + assert_se(sd_event_loop(e) >= 0); + + test_hangcheck = sd_event_source_unref(test_hangcheck); + + assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0); + assert_se(sd_dhcp_client_stop(client) >= 0); + sd_dhcp_client_unref(client); + + test_fd[1] = safe_close(test_fd[1]); + + callback_recv = NULL; + xid = 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_unrefp) sd_event *e; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + assert_se(sd_event_new(&e) >= 0); + + test_request_basic(e); + test_checksum(); + + test_discover_message(e); + test_addr_acq(e); + +#ifdef VALGRIND + /* Make sure the async_close thread has finished. + * valgrind would report some of the phread_* structures + * as not cleaned up properly. */ + sleep(1); +#endif + + return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp-option.c b/src/libsystemd-network/test/test-dhcp-option.c new file mode 100644 index 0000000000..eda42b0ba5 --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-option.c @@ -0,0 +1,367 @@ +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "systemd-network/dhcp-internal.h" +#include "systemd-network/dhcp-protocol.h" + +struct option_desc { + uint8_t sname[64]; + int snamelen; + uint8_t file[128]; + int filelen; + uint8_t options[128]; + int len; + bool success; + int filepos; + int snamepos; + int pos; +}; + +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, + 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, + 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0, + 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + 40, true, }, + { {}, 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, 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, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9, + { 222, 3, 1, 2, 3 }, 5, + { SD_DHCP_OPTION_OVERLOAD, 1, + DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, }, +}; + +static const char *dhcp_type(int type) { + switch(type) { + case DHCP_DISCOVER: + return "DHCPDISCOVER"; + case DHCP_OFFER: + return "DHCPOFFER"; + case DHCP_REQUEST: + return "DHCPREQUEST"; + case DHCP_DECLINE: + return "DHCPDECLINE"; + case DHCP_ACK: + return "DHCPACK"; + case DHCP_NAK: + return "DHCPNAK"; + case DHCP_RELEASE: + return "DHCPRELEASE"; + default: + return "unknown"; + } +} + +static void test_invalid_buffer_length(void) { + DHCPMessage message; + + 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) { + _cleanup_free_ DHCPMessage *message = NULL; + size_t optlen = 4, optoffset; + size_t len = sizeof(DHCPMessage) + optlen; + uint8_t *magic; + + message = malloc0(len); + + assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, + DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0); + + assert_se(message->xid == htobe32(0x12345678)); + assert_se(message->op == BOOTREQUEST); + + magic = (uint8_t*)&message->magic; + + assert_se(magic[0] == 99); + assert_se(magic[1] == 130); + assert_se(magic[2] == 83); + assert_se(magic[3] == 99); + + assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0); +} + +static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, + uint8_t *file, uint8_t filelen, + uint8_t *sname, uint8_t snamelen) { + DHCPMessage *message; + size_t len = sizeof(DHCPMessage) + optlen; + + message = malloc0(len); + assert_se(message); + + memcpy_safe(&message->options, options, optlen); + memcpy_safe(&message->file, file, filelen); + memcpy_safe(&message->sname, sname, snamelen); + + return message; +} + +static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) { + assert(*descpos >= 0); + + while (*descpos < *desclen) { + switch(descoption[*descpos]) { + case SD_DHCP_OPTION_PAD: + *descpos += 1; + break; + + case SD_DHCP_OPTION_MESSAGE_TYPE: + case SD_DHCP_OPTION_OVERLOAD: + *descpos += 3; + break; + + default: + return; + } + } +} + +static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) { + struct option_desc *desc = userdata; + uint8_t *descoption = NULL; + int *desclen = NULL, *descpos = NULL; + uint8_t optcode = 0; + uint8_t optlen = 0; + uint8_t i; + + assert_se((!desc && !code && !len) || desc); + + if (!desc) + return -EINVAL; + + 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) { + + if (desc->pos >= 0) { + descoption = &desc->options[0]; + desclen = &desc->len; + descpos = &desc->pos; + } else if (desc->filepos >= 0) { + descoption = &desc->file[0]; + desclen = &desc->filelen; + descpos = &desc->filepos; + } else if (desc->snamepos >= 0) { + descoption = &desc->sname[0]; + desclen = &desc->snamelen; + descpos = &desc->snamepos; + } + + assert_se(descoption && desclen && descpos); + + if (*desclen) + test_ignore_opts(descoption, descpos, desclen); + + if (*descpos < *desclen) + break; + + if (*descpos == *desclen) + *descpos = -1; + } + + assert_se(descpos); + assert_se(*descpos != -1); + + optcode = descoption[*descpos]; + optlen = descoption[*descpos + 1]; + + if (verbose) + printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode, + len, optlen); + + assert_se(code == optcode); + assert_se(len == optlen); + + for (i = 0; i < len; i++) { + + if (verbose) + printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i], + descoption[*descpos + 2 + i]); + + assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]); + } + + if (verbose) + printf("\n"); + + *descpos += optlen + 2; + + test_ignore_opts(descoption, descpos, desclen); + + if (desc->pos != -1 && desc->pos == desc->len) + desc->pos = -1; + + if (desc->filepos != -1 && desc->filepos == desc->filelen) + desc->filepos = -1; + + if (desc->snamepos != -1 && desc->snamepos == desc->snamelen) + desc->snamepos = -1; + + return 0; +} + +static void test_options(struct option_desc *desc) { + uint8_t *options = NULL; + uint8_t *file = NULL; + uint8_t *sname = NULL; + int optlen = 0; + int filelen = 0; + int snamelen = 0; + int buflen = 0; + _cleanup_free_ DHCPMessage *message = NULL; + int res; + + if (desc) { + file = &desc->file[0]; + filelen = desc->filelen; + if (!filelen) + desc->filepos = -1; + + sname = &desc->sname[0]; + snamelen = desc->snamelen; + if (!snamelen) + desc->snamepos = -1; + + options = &desc->options[0]; + optlen = desc->len; + desc->pos = 0; + } + message = create_message(options, optlen, file, filelen, + sname, snamelen); + + buflen = sizeof(DHCPMessage) + optlen; + + if (!desc) { + 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, 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, NULL)) < 0); + + if (verbose) + printf("DHCP type %s\n", dhcp_type(res)); +} + +static uint8_t options[64] = { + 'A', 'B', 'C', 'D', + 160, 2, 0x11, 0x12, + 0, + 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0, + 55, 3, 0x51, 0x52, 0x53, + 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 255 +}; + +static void test_option_set(void) { + _cleanup_free_ DHCPMessage *result = NULL; + size_t offset = 0, len, pos; + unsigned i; + + result = malloc0(sizeof(DHCPMessage) + 11); + assert_se(result); + + result->options[0] = 'A'; + result->options[1] = 'B'; + result->options[2] = 'C'; + result->options[3] = 'D'; + + 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, SD_DHCP_OPTION_PAD, + 0, NULL) == -ENOBUFS); + assert_se(offset == 4); + 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] != 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] == SD_DHCP_OPTION_PAD) + pos++; + else + pos += 2 + options[pos + 1]; + + if (pos < len) + assert_se(offset == pos); + } + + for (i = 0; i < 9; i++) { + if (verbose) + printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i], + options[i]); + assert_se(result->options[i] == options[i]); + } + + if (verbose) + printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9], + SD_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], + SD_DHCP_OPTION_PAD); + + assert_se(result->options[10] == SD_DHCP_OPTION_PAD); + + for (i = 0; i < pos - 8; i++) { + if (verbose) + printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i], + options[i + 9]); + assert_se(result->sname[i] == options[i + 9]); + } + + if (verbose) + printf ("\n"); +} + +int main(int argc, char *argv[]) { + unsigned i; + + test_invalid_buffer_length(); + test_message_init(); + + test_options(NULL); + + for (i = 0; i < ELEMENTSOF(option_tests); i++) + test_options(&option_tests[i]); + + test_option_set(); + + return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp-server.c b/src/libsystemd-network/test/test-dhcp-server.c new file mode 100644 index 0000000000..0796a98707 --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-server.c @@ -0,0 +1,261 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 Intel Corporation. All rights reserved. + 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 . +***/ + +#include + +#include + +#include "systemd-network/dhcp-server-internal.h" +#include "systemd-network/sd-dhcp-server.h" + +static void test_pool(struct in_addr *address, unsigned size, int ret) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + + assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); +} + +static int test_basic(sd_event *event) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + struct in_addr address_lo = { + .s_addr = htonl(INADDR_LOOPBACK), + }; + struct in_addr address_any = { + .s_addr = htonl(INADDR_ANY), + }; + int r; + + /* attach to loopback interface */ + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + assert_se(server); + + assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0); + assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY); + assert_se(sd_dhcp_server_get_event(server) == event); + assert_se(sd_dhcp_server_detach_event(server) >= 0); + assert_se(!sd_dhcp_server_get_event(server)); + assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); + assert_se(sd_dhcp_server_attach_event(server, NULL, 0) == -EBUSY); + + assert_se(sd_dhcp_server_ref(server) == server); + assert_se(!sd_dhcp_server_unref(server)); + + assert_se(sd_dhcp_server_start(server) == -EUNATCH); + + assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); + + test_pool(&address_any, 1, -EINVAL); + test_pool(&address_lo, 1, 0); + + r = sd_dhcp_server_start(server); + + if (r == -EPERM) + return EXIT_TEST_SKIP; + assert_se(r >= 0); + + assert_se(sd_dhcp_server_start(server) == -EBUSY); + assert_se(sd_dhcp_server_stop(server) >= 0); + assert_se(sd_dhcp_server_stop(server) >= 0); + assert_se(sd_dhcp_server_start(server) >= 0); + + return 0; +} + +static void test_message_handler(void) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + struct { + DHCPMessage message; + struct { + uint8_t code; + uint8_t length; + uint8_t type; + } _packed_ option_type; + struct { + uint8_t code; + uint8_t length; + be32_t address; + } _packed_ option_requested_ip; + struct { + uint8_t code; + uint8_t length; + be32_t address; + } _packed_ option_server_id; + struct { + uint8_t code; + uint8_t length; + uint8_t id[7]; + } _packed_ option_client_id; + uint8_t end; + } _packed_ test = { + .message.op = BOOTREQUEST, + .message.htype = ARPHRD_ETHER, + .message.hlen = ETHER_ADDR_LEN, + .message.xid = htobe32(0x12345678), + .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, + .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE, + .option_type.length = 1, + .option_type.type = DHCP_DISCOVER, + .end = SD_DHCP_OPTION_END, + }; + struct in_addr address_lo = { + .s_addr = htonl(INADDR_LOOPBACK), + }; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); + assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); + assert_se(sd_dhcp_server_start(server) >= 0); + + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + + test.end = 0; + /* TODO, shouldn't this fail? */ + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + 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 = 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); + + test.message.op = 0; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + test.message.op = BOOTREQUEST; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + + test.message.htype = 0; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + test.message.htype = ARPHRD_ETHER; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + + test.message.hlen = 0; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + test.message.hlen = ETHER_ADDR_LEN; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + + test.option_type.type = DHCP_REQUEST; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + 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 = 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); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + + test.option_server_id.address = htobe32(0x12345678); + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + test.option_server_id.address = htobe32(INADDR_LOOPBACK); + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + 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 = 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'; + test.option_client_id.id[2] = 'B'; + test.option_client_id.id[3] = 'C'; + test.option_client_id.id[4] = 'D'; + test.option_client_id.id[5] = 'E'; + test.option_client_id.id[6] = 'F'; + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); + 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, + }, b = { + .length = 4, + }; + uint8_t hash_key[HASH_KEY_SIZE] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + }; + + a.data = (uint8_t*)strdup("abcd"); + b.data = (uint8_t*)strdup("abcd"); + + assert_se(client_id_compare_func(&a, &b) == 0); + 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_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_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); + + free(b.data); + b.data = (uint8_t*)strdup("abce"); + assert_se(client_id_compare_func(&a, &b) != 0); + + free(a.data); + free(b.data); +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_unrefp) sd_event *e; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + assert_se(sd_event_new(&e) >= 0); + + r = test_basic(e); + if (r != 0) + return r; + + test_message_handler(); + test_client_id_hash(); + + return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp6-client.c b/src/libsystemd-network/test/test-dhcp6-client.c new file mode 100644 index 0000000000..20019f4bba --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp6-client.c @@ -0,0 +1,763 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/socket-util.h" +#include "basic/virt.h" +#include "systemd-network/dhcp6-internal.h" +#include "systemd-network/dhcp6-lease-internal.h" +#include "systemd-network/dhcp6-protocol.h" +#include "systemd-network/sd-dhcp6-client.h" + +static struct ether_addr mac_addr = { + .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +}; + +static bool verbose = true; + +static sd_event_source *hangcheck; +static int test_dhcp_fd[2]; +static int test_index = 42; +static int test_client_message_num; +static be32_t test_iaid = 0; +static uint8_t test_duid[14] = { }; + +static int test_client_basic(sd_event *e) { + sd_dhcp6_client *client; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(sd_dhcp6_client_new(&client) >= 0); + assert_se(client); + + assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + + assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0); + assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL); + assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0); + assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); + + assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); + + 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); + + assert_se(sd_dhcp6_client_detach_event(client) >= 0); + assert_se(!sd_dhcp6_client_unref(client)); + + return 0; +} + +static int test_option(sd_event *e) { + uint8_t packet[] = { + 'F', 'O', 'O', + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, + 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, + '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'B', 'A', 'R', + }; + uint8_t result[] = { + 'F', 'O', 'O', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 'B', 'A', 'R', + }; + uint16_t optcode; + size_t optlen; + uint8_t *optval, *buf, *out; + size_t zero = 0, pos = 3; + size_t buflen = sizeof(packet), outlen = sizeof(result); + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(buflen == outlen); + + assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen, + &optval) == -ENOMSG); + + buflen -= 3; + buf = &packet[3]; + outlen -= 3; + out = &result[3]; + + assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, + &optval) >= 0); + pos += 4 + optlen; + assert_se(buf == &packet[pos]); + assert_se(optcode == SD_DHCP6_OPTION_ORO); + assert_se(optlen == 7); + assert_se(buflen + pos == sizeof(packet)); + + assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, + optval) >= 0); + assert_se(out == &result[pos]); + assert_se(*out == 0x00); + + assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, + &optval) >= 0); + pos += 4 + optlen; + assert_se(buf == &packet[pos]); + assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); + assert_se(optlen == 9); + assert_se(buflen + pos == sizeof(packet)); + + assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, + optval) >= 0); + assert_se(out == &result[pos]); + assert_se(*out == 'B'); + + assert_se(memcmp(packet, result, sizeof(packet)) == 0); + + return 0; +} + +static uint8_t msg_advertise[198] = { + 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, + 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, + 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03, + 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00, + 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, + 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, + 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, + 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, + 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, + 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, + 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, + 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, + 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, + 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, + 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, + 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19, + 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, + 0x53, 0x00, 0x07, 0x00, 0x01, 0x00 +}; + +static uint8_t msg_reply[173] = { + 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e, + 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, + 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01, + 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, + 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, + 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, + 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, + 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, + 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, + 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e, + 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, + 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17, + 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, + 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c, + 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, + 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d, + 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01 +}; + +static int test_advertise_option(sd_event *e) { + _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; + size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); + be32_t val; + uint8_t preference = 255; + struct in6_addr addr; + uint32_t lt_pref, lt_valid; + int r; + bool opt_clientid = false; + struct in6_addr *addrs; + char **domains; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(dhcp6_lease_new(&lease) >= 0); + + assert_se(advertise->type == DHCP6_ADVERTISE); + assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == + 0x0fb4e5); + + while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, + &optval)) >= 0) { + + switch(optcode) { + case SD_DHCP6_OPTION_CLIENTID: + assert_se(optlen == 14); + + opt_clientid = true; + break; + + case SD_DHCP6_OPTION_IA_NA: + assert_se(optlen == 94); + assert_se(!memcmp(optval, &msg_advertise[26], optlen)); + + val = htobe32(0x0ecfa37d); + assert_se(!memcmp(optval, &val, sizeof(val))); + + val = htobe32(80); + assert_se(!memcmp(optval + 4, &val, sizeof(val))); + + val = htobe32(120); + assert_se(!memcmp(optval + 8, &val, sizeof(val))); + + assert_se(dhcp6_option_parse_ia(&optval, &optlen, + optcode, + &lease->ia) >= 0); + + break; + + case SD_DHCP6_OPTION_SERVERID: + assert_se(optlen == 14); + assert_se(!memcmp(optval, &msg_advertise[179], optlen)); + + assert_se(dhcp6_lease_set_serverid(lease, optval, + optlen) >= 0); + break; + + case SD_DHCP6_OPTION_PREFERENCE: + assert_se(optlen == 1); + assert_se(!*optval); + + assert_se(dhcp6_lease_set_preference(lease, + *optval) >= 0); + break; + + case SD_DHCP6_OPTION_ELAPSED_TIME: + assert_se(optlen == 2); + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_dns(lease, optval, + optlen) >= 0); + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + assert_se(optlen == 11); + assert_se(dhcp6_lease_set_domains(lease, optval, + optlen) >= 0); + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_sntp(lease, optval, + optlen) >= 0); + break; + + default: + break; + } + } + + + assert_se(r == -ENOMSG); + + assert_se(opt_clientid); + + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) >= 0); + assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) >= 0); + assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) >= 0); + assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); + assert_se(len == 14); + assert_se(!memcmp(opt, &msg_advertise[179], len)); + + assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); + assert_se(preference == 0); + + r = sd_dhcp6_lease_get_dns(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); + + r = sd_dhcp6_lease_get_domains(lease, &domains); + assert_se(r == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); + + return 0; +} + +static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { + assert_not_reached("Test case should have completed in 2 seconds"); + + return 0; +} + +static void test_client_solicit_cb(sd_dhcp6_client *client, int event, + void *userdata) { + sd_event *e = userdata; + sd_dhcp6_lease *lease; + struct in6_addr *addrs; + char **domains; + + assert_se(e); + assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + 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, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); + + if (verbose) + printf(" got DHCPv6 event %d\n", event); + + sd_event_exit(e, 0); +} + +static int test_client_send_reply(DHCP6Message *request) { + DHCP6Message reply; + + reply.transaction_id = request->transaction_id; + reply.type = DHCP6_REPLY; + + memcpy(msg_reply, &reply.transaction_id, 4); + + memcpy(&msg_reply[26], test_duid, sizeof(test_duid)); + + memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid)); + + assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) + == sizeof(msg_reply)); + + return 0; +} + +static int test_client_verify_request(DHCP6Message *request, uint8_t *option, + size_t len) { + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + uint8_t *optval; + uint16_t optcode; + size_t optlen; + bool found_clientid = false, found_iana = false, found_serverid = false, + found_elapsed_time = false; + int r; + struct in6_addr addr; + be32_t val; + uint32_t lt_pref, lt_valid; + + assert_se(request->type == DHCP6_REQUEST); + + assert_se(dhcp6_lease_new(&lease) >= 0); + + while ((r = dhcp6_option_parse(&option, &len, + &optcode, &optlen, &optval)) >= 0) { + switch(optcode) { + case SD_DHCP6_OPTION_CLIENTID: + assert_se(!found_clientid); + found_clientid = true; + + assert_se(!memcmp(optval, &test_duid, + sizeof(test_duid))); + + break; + + case SD_DHCP6_OPTION_IA_NA: + assert_se(!found_iana); + found_iana = true; + + + assert_se(optlen == 40); + assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid))); + + val = htobe32(80); + assert_se(!memcmp(optval + 4, &val, sizeof(val))); + + val = htobe32(120); + assert_se(!memcmp(optval + 8, &val, sizeof(val))); + + assert_se(!dhcp6_option_parse_ia(&optval, &optlen, + optcode, &lease->ia)); + + break; + + case SD_DHCP6_OPTION_SERVERID: + assert_se(!found_serverid); + found_serverid = true; + + assert_se(optlen == 14); + assert_se(!memcmp(&msg_advertise[179], optval, optlen)); + + break; + + case SD_DHCP6_OPTION_ELAPSED_TIME: + assert_se(!found_elapsed_time); + found_elapsed_time = true; + + assert_se(optlen == 2); + + break; + } + } + + assert_se(r == -ENOMSG); + assert_se(found_clientid && found_iana && found_serverid && + found_elapsed_time); + + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) >= 0); + assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + return 0; +} + +static int test_client_send_advertise(DHCP6Message *solicit) { + DHCP6Message advertise; + + advertise.transaction_id = solicit->transaction_id; + advertise.type = DHCP6_ADVERTISE; + + memcpy(msg_advertise, &advertise.transaction_id, 4); + + memcpy(&msg_advertise[8], test_duid, sizeof(test_duid)); + + memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid)); + + assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) + == sizeof(msg_advertise)); + + return 0; +} + +static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, + size_t len) { + uint8_t *optval; + uint16_t optcode; + size_t optlen; + bool found_clientid = false, found_iana = false, + found_elapsed_time = false; + int r; + + assert_se(solicit->type == DHCP6_SOLICIT); + + while ((r = dhcp6_option_parse(&option, &len, + &optcode, &optlen, &optval)) >= 0) { + switch(optcode) { + case SD_DHCP6_OPTION_CLIENTID: + assert_se(!found_clientid); + found_clientid = true; + + assert_se(optlen == sizeof(test_duid)); + memcpy(&test_duid, optval, sizeof(test_duid)); + + break; + + case SD_DHCP6_OPTION_IA_NA: + assert_se(!found_iana); + found_iana = true; + + assert_se(optlen == 12); + + memcpy(&test_iaid, optval, sizeof(test_iaid)); + + break; + + case SD_DHCP6_OPTION_ELAPSED_TIME: + assert_se(!found_elapsed_time); + found_elapsed_time = true; + + assert_se(optlen == 2); + + break; + } + } + + assert_se(r == -ENOMSG); + assert_se(found_clientid && found_iana && found_elapsed_time); + + return 0; +} + +static void test_client_information_cb(sd_dhcp6_client *client, int event, + void *userdata) { + 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); + assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + + if (verbose) + printf(" got DHCPv6 event %d\n", event); + + assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); + assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); + assert_se(sd_dhcp6_client_stop(client) >= 0); + assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + + 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_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + uint8_t *optval; + uint16_t optcode; + size_t optlen; + bool found_clientid = false, found_elapsed_time = false; + int r; + struct in6_addr addr; + uint32_t lt_pref, lt_valid; + + assert_se(information_request->type == DHCP6_INFORMATION_REQUEST); + + assert_se(dhcp6_lease_new(&lease) >= 0); + + while ((r = dhcp6_option_parse(&option, &len, + &optcode, &optlen, &optval)) >= 0) { + switch(optcode) { + case SD_DHCP6_OPTION_CLIENTID: + assert_se(!found_clientid); + found_clientid = true; + + assert_se(optlen == sizeof(test_duid)); + memcpy(&test_duid, optval, sizeof(test_duid)); + + break; + + case SD_DHCP6_OPTION_IA_NA: + assert_not_reached("IA TA option must not be present"); + + break; + + case SD_DHCP6_OPTION_SERVERID: + assert_not_reached("Server ID option must not be present"); + + break; + + case SD_DHCP6_OPTION_ELAPSED_TIME: + assert_se(!found_elapsed_time); + found_elapsed_time = true; + + assert_se(optlen == 2); + + break; + } + } + + assert_se(r == -ENOMSG); + assert_se(found_clientid && found_elapsed_time); + + sd_dhcp6_lease_reset_address_iter(lease); + + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + return 0; +} + +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, + const void *packet, size_t len) { + struct in6_addr mcast = + IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; + DHCP6Message *message; + uint8_t *option; + + assert_se(s == test_dhcp_fd[0]); + assert_se(server_address); + assert_se(packet); + assert_se(len > sizeof(DHCP6Message) + 4); + + assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); + + message = (DHCP6Message *)packet; + option = (uint8_t *)(message + 1); + len -= sizeof(DHCP6Message); + + assert_se(message->transaction_id & 0x00ffffff); + + if (test_client_message_num == 0) { + test_client_verify_information_request(message, option, len); + test_client_send_reply(message); + test_client_message_num++; + } else if (test_client_message_num == 1) { + test_client_verify_solicit(message, option, len); + test_client_send_advertise(message); + test_client_message_num++; + } else if (test_client_message_num == 2) { + test_client_verify_request(message, option, len); + test_client_send_reply(message); + test_client_message_num++; + } + + return len; +} + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { + assert_se(index == test_index); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0) + return -errno; + + return test_dhcp_fd[0]; +} + +static int test_client_solicit(sd_event *e) { + sd_dhcp6_client *client; + usec_t time_now = now(clock_boottime_or_monotonic()); + 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__); + + assert_se(sd_dhcp6_client_new(&client) >= 0); + assert_se(client); + + assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + + assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0); + assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, + sizeof (mac_addr), + ARPHRD_ETHER) >= 0); + + assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); + assert_se(val == false); + assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); + assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); + assert_se(val == true); + + assert_se(sd_dhcp6_client_set_callback(client, + test_client_information_cb, e) >= 0); + + assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(), + 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); + + hangcheck = sd_event_source_unref(hangcheck); + + assert_se(!sd_dhcp6_client_unref(client)); + + test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); + + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_unrefp) sd_event *e; + + assert_se(sd_event_new(&e) >= 0); + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_client_basic(e); + test_option(e); + test_advertise_option(e); + test_client_solicit(e); + + return 0; +} diff --git a/src/libsystemd-network/test/test-ipv4ll-manual.c b/src/libsystemd-network/test/test-ipv4ll-manual.c new file mode 100644 index 0000000000..8dac0b1ca8 --- /dev/null +++ b/src/libsystemd-network/test/test-ipv4ll-manual.c @@ -0,0 +1,129 @@ +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/in-addr-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "systemd-network/sd-ipv4ll.h" + +static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) { + _cleanup_free_ char *address = NULL; + struct in_addr addr = {}; + + assert_se(ll); + + if (sd_ipv4ll_get_address(ll, &addr) >= 0) + assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0); + + switch (event) { + case SD_IPV4LL_EVENT_BIND: + log_info("bound %s", strna(address)); + break; + case SD_IPV4LL_EVENT_CONFLICT: + log_info("conflict on %s", strna(address)); + break; + case SD_IPV4LL_EVENT_STOP: + log_error("the client was stopped with address %s", strna(address)); + break; + default: + assert_not_reached("invalid LL event"); + } +} + +static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) { + sd_ipv4ll *ll; + + assert_se(sd_ipv4ll_new(&ll) >= 0); + assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0); + + assert_se(sd_ipv4ll_set_ifindex(ll, ifindex) >= 0); + assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0); + assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0); + + if (seed_str) { + unsigned seed; + + assert_se(safe_atou(seed_str, &seed) >= 0); + + assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0); + } + + log_info("starting IPv4LL client"); + + assert_se(sd_ipv4ll_start(ll) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_ipv4ll_unref(ll)); + + return EXIT_SUCCESS; +} + +static int test_ll(const char *ifname, const char *seed) { + _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; + + assert_se(sd_event_new(&e) >= 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_GETLINK, 0) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); + assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + + assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); + assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + + client_run(ifindex, seed, &ha, e); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc == 2) + return test_ll(argv[1], NULL); + else if (argc == 3) + return test_ll(argv[1], argv[2]); + else { + log_error("This program takes one or two arguments.\n" + "\t %s []", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-network/test/test-ipv4ll.c b/src/libsystemd-network/test/test-ipv4ll.c new file mode 100644 index 0000000000..6d3e7f35d5 --- /dev/null +++ b/src/libsystemd-network/test/test-ipv4ll.c @@ -0,0 +1,220 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Axis Communications AB. 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/socket-util.h" +#include "basic/util.h" +#include "systemd-network/arp-util.h" +#include "systemd-network/sd-ipv4ll.h" + +static bool verbose = false; +static bool extended = false; +static int test_fd[2]; + +static int basic_request_handler_bind = 0; +static int basic_request_handler_stop = 0; +static void* basic_request_handler_userdata = (void*) 0xCABCAB; + +static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { + assert_se(userdata == basic_request_handler_userdata); + + switch(event) { + case SD_IPV4LL_EVENT_STOP: + basic_request_handler_stop = 1; + break; + case SD_IPV4LL_EVENT_BIND: + basic_request_handler_bind = 1; + break; + default: + assert_se(0); + break; + } +} + +static int arp_network_send_raw_socket(int fd, int ifindex, + const struct ether_arp *arp) { + assert_se(arp); + assert_se(ifindex > 0); + assert_se(fd >= 0); + + if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) + return -errno; + + return 0; +} + +int arp_send_probe(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; + + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); + + return arp_network_send_raw_socket(fd, ifindex, &ea); +} + +int arp_send_announcement(int fd, int ifindex, + be32_t pa, const struct ether_addr *ha) { + struct ether_arp ea = {}; + + assert(fd >= 0); + assert(ifindex > 0); + assert(pa != 0); + assert(ha); + + return arp_network_send_raw_socket(fd, ifindex, &ea); +} + +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { + if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) + return -errno; + + return test_fd[0]; +} + +static void test_public_api_setters(sd_event *e) { + struct in_addr address = {}; + uint64_t seed = 0; + sd_ipv4ll *ll; + struct ether_addr mac_addr = { + .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(sd_ipv4ll_new(&ll) == 0); + assert_se(ll); + + assert_se(sd_ipv4ll_attach_event(NULL, NULL, 0) == -EINVAL); + assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); + assert_se(sd_ipv4ll_attach_event(ll, e, 0) == -EBUSY); + + 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); + + assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL); + assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL); + assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); + + assert_se(sd_ipv4ll_set_ifindex(NULL, -1) == -EINVAL); + assert_se(sd_ipv4ll_set_ifindex(ll, -1) == -EINVAL); + assert_se(sd_ipv4ll_set_ifindex(ll, -99) == -EINVAL); + assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0); + assert_se(sd_ipv4ll_set_ifindex(ll, 99) == 0); + + assert_se(sd_ipv4ll_ref(ll) == ll); + assert_se(sd_ipv4ll_unref(ll) == NULL); + + /* Cleanup */ + assert_se(sd_ipv4ll_unref(ll) == NULL); +} + +static void test_basic_request(sd_event *e) { + + sd_ipv4ll *ll; + struct ether_arp arp; + struct ether_addr mac_addr = { + .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + assert_se(sd_ipv4ll_new(&ll) == 0); + assert_se(sd_ipv4ll_start(ll) == -EINVAL); + + assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); + assert_se(sd_ipv4ll_start(ll) == -EINVAL); + + assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); + assert_se(sd_ipv4ll_start(ll) == -EINVAL); + + assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler, + basic_request_handler_userdata) == 0); + assert_se(sd_ipv4ll_start(ll) == -EINVAL); + + assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0); + assert_se(sd_ipv4ll_start(ll) == 0); + + sd_event_run(e, (uint64_t) -1); + assert_se(sd_ipv4ll_start(ll) == -EBUSY); + + assert_se(sd_ipv4ll_is_running(ll)); + + /* PROBE */ + sd_event_run(e, (uint64_t) -1); + assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); + + if (extended) { + /* PROBE */ + sd_event_run(e, (uint64_t) -1); + assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); + + /* PROBE */ + sd_event_run(e, (uint64_t) -1); + assert_se(recv(test_fd[1], &arp, sizeof(struct ether_arp), 0) == sizeof(struct ether_arp)); + + sd_event_run(e, (uint64_t) -1); + assert_se(basic_request_handler_bind == 1); + } + + sd_ipv4ll_stop(ll); + assert_se(basic_request_handler_stop == 1); + + /* Cleanup */ + assert_se(sd_ipv4ll_unref(ll) == NULL); + safe_close(test_fd[1]); +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + assert_se(sd_event_new(&e) >= 0); + + test_public_api_setters(e); + test_basic_request(e); + + return 0; +} diff --git a/src/libsystemd-network/test/test-lldp.c b/src/libsystemd-network/test/test-lldp.c new file mode 100644 index 0000000000..ad321621e4 --- /dev/null +++ b/src/libsystemd-network/test/test-lldp.c @@ -0,0 +1,261 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "systemd-network/lldp-network.h" +#include "systemd-network/sd-lldp.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] = { -1, -1 }; +static int lldp_handler_calls; + +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 void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { + lldp_handler_calls++; +} + +static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { + int r; + + r = sd_lldp_new(lldp); + if (r < 0) + return r; + + r = sd_lldp_set_ifindex(*lldp, 42); + if (r < 0) + return r; + + r = sd_lldp_set_callback(*lldp, cb, cb_data); + if (r < 0) + return r; + + r = sd_lldp_attach_event(*lldp, e, 0); + if (r < 0) + return r; + + r = sd_lldp_start(*lldp); + if (r < 0) + return r; + + return 0; +} + +static int stop_lldp(sd_lldp *lldp) { + int r; + + r = sd_lldp_stop(lldp); + if (r < 0) + return r; + + r = sd_lldp_detach_event(lldp); + if (r < 0) + return r; + + sd_lldp_unref(lldp); + safe_close(test_fd[1]); + + return 0; +} + +static void test_receive_basic_packet(sd_event *e) { + + static const 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 */ + }; + + sd_lldp *lldp; + sd_lldp_neighbor **neighbors; + uint8_t type; + const void *data; + uint16_t ttl; + size_t length; + const char *str; + + 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_neighbors(lldp, &neighbors) == 1); + + assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); + assert_se(type == SD_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_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); + assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); + assert_se(length == 3); + assert_se(strneq((char *) data, "1/3", 3)); + + assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); + assert_se(streq(str, "Port")); + + assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); + assert_se(streq(str, "SYS")); + + assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); + assert_se(streq(str, "foo")); + + assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); + assert_se(ttl == 120); + + sd_lldp_neighbor_unref(neighbors[0]); + free(neighbors); + + assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_incomplete_packet(sd_event *e) { + sd_lldp *lldp; + sd_lldp_neighbor **neighbors; + 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_neighbors(lldp, &neighbors) == 0); + + assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_oui_packet(sd_event *e) { + sd_lldp *lldp; + sd_lldp_neighbor **neighbors; + 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_neighbors(lldp, &neighbors) == 1); + + assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); + + sd_lldp_neighbor_unref(neighbors[0]); + free(neighbors); + + assert_se(stop_lldp(lldp) == 0); +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + + log_set_max_level(LOG_DEBUG); + + /* 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/test-ndisc-rs.c b/src/libsystemd-network/test/test-ndisc-rs.c new file mode 100644 index 0000000000..ff4b5c72ec --- /dev/null +++ b/src/libsystemd-network/test/test-ndisc-rs.c @@ -0,0 +1,316 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/socket-util.h" +#include "basic/strv.h" +#include "systemd-network/icmp6-util.h" +#include "systemd-network/sd-ndisc.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 void router_dump(sd_ndisc_router *rt) { + struct in6_addr addr; + char buf[FORMAT_TIMESTAMP_MAX]; + uint8_t hop_limit; + uint64_t t, flags; + uint32_t mtu; + uint16_t lifetime; + unsigned preference; + int r; + + assert_se(rt); + + log_info("--"); + assert_se(sd_ndisc_router_get_address(rt, &addr) == -ENODATA); + + assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0); + log_info("Timestamp: %s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0); + log_info("Monotonic: %" PRIu64, t); + + if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0) + log_info("No hop limit set"); + else + log_info("Hop limit: %u", hop_limit); + + assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); + log_info("Flags: <%s|%s>", + flags & ND_RA_FLAG_OTHER ? "OTHER" : "", + flags & ND_RA_FLAG_MANAGED ? "MANAGED" : ""); + + assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0); + log_info("Preference: %s", + preference == SD_NDISC_PREFERENCE_LOW ? "low" : + preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium"); + + assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0); + log_info("Lifetime: %" PRIu16, lifetime); + + if (sd_ndisc_router_get_mtu(rt, &mtu) < 0) + log_info("No MTU set"); + else + log_info("MTU: %" PRIu32, mtu); + + r = sd_ndisc_router_option_rewind(rt); + for (;;) { + uint8_t type; + + assert_se(r >= 0); + + if (r == 0) + break; + + assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0); + + log_info(">> Option %u", type); + + switch (type) { + + case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: + case SD_NDISC_OPTION_TARGET_LL_ADDRESS: { + _cleanup_free_ char *c = NULL; + const void *p; + size_t n; + + assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0); + assert_se(n > 2); + assert_se(c = hexmem((uint8_t*) p + 2, n - 2)); + + log_info("Address: %s", c); + break; + } + + case SD_NDISC_OPTION_PREFIX_INFORMATION: { + uint32_t lifetime_valid, lifetime_preferred; + unsigned prefix_len; + uint8_t pfl; + struct in6_addr a; + char buff[INET6_ADDRSTRLEN]; + + assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid) >= 0); + log_info("Valid Lifetime: %" PRIu32, lifetime_valid); + + assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred) >= 0); + log_info("Preferred Lifetime: %" PRIu32, lifetime_preferred); + + assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0); + log_info("Flags: <%s|%s>", + pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "", + pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : ""); + + assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0); + log_info("Prefix Length: %u", prefix_len); + + assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0); + log_info("Prefix: %s", inet_ntop(AF_INET6, &a, buff, sizeof(buff))); + + break; + } + + case SD_NDISC_OPTION_RDNSS: { + const struct in6_addr *a; + uint32_t lt; + int n, i; + + n = sd_ndisc_router_rdnss_get_addresses(rt, &a); + assert_se(n > 0); + + for (i = 0; i < n; i++) { + char buff[INET6_ADDRSTRLEN]; + log_info("DNS: %s", inet_ntop(AF_INET6, a + i, buff, sizeof(buff))); + } + + assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, <) >= 0); + log_info("Lifetime: %" PRIu32, lt); + break; + } + + case SD_NDISC_OPTION_DNSSL: { + _cleanup_strv_free_ char **l = NULL; + uint32_t lt; + int n, i; + + n = sd_ndisc_router_dnssl_get_domains(rt, &l); + assert_se(n > 0); + + for (i = 0; i < n; i++) + log_info("Domain: %s", l[i]); + + assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, <) >= 0); + log_info("Lifetime: %" PRIu32, lt); + break; + }} + + r = sd_ndisc_router_option_next(rt); + } +} + +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_callback(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { + sd_event *e = userdata; + static unsigned idx = 0; + uint64_t flags_array[] = { + 0, + 0, + 0, + ND_RA_FLAG_OTHER, + ND_RA_FLAG_MANAGED + }; + uint64_t flags; + uint32_t mtu; + + assert_se(nd); + + if (event != SD_NDISC_EVENT_ROUTER) + return; + + router_dump(rt); + + assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); + assert_se(flags == flags_array[idx]); + idx++; + + if (verbose) + printf(" got event 0x%02" PRIx64 "\n", flags); + + if (idx < ELEMENTSOF(flags_array)) { + send_ra(flags_array[idx]); + return; + } + + assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENODATA); + + 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_ifindex(nd, 42) >= 0); + assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); + assert_se(sd_ndisc_set_callback(nd, test_callback, 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_start(nd) >= 0); + assert_se(sd_ndisc_stop(nd) >= 0); + + assert_se(sd_ndisc_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/Makefile b/src/libsystemd/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/Makefile b/src/libsystemd/Makefile new file mode 100644 index 0000000000..a6c9314ca0 --- /dev/null +++ b/src/libsystemd/Makefile @@ -0,0 +1,103 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +LIBSYSTEMD_CURRENT=16 +LIBSYSTEMD_REVISION=0 +LIBSYSTEMD_AGE=16 + +EXTRA_DIST += \ + src/libsystemd/libsystemd.pc.in \ + src/libsystemd/sd-bus/DIFFERENCES \ + src/libsystemd/sd-bus/GVARIANT-SERIALIZATION + +libsystemd_la_SOURCES = + +libsystemd_la_LDFLAGS = \ + -version-info $(LIBSYSTEMD_CURRENT):$(LIBSYSTEMD_REVISION):$(LIBSYSTEMD_AGE) \ + -Wl,--version-script=$(srcdir)/libsystemd.sym + +libsystemd_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la \ + libsystemd-journal-internal.la + +pkgconfiglib_DATA += \ + src/libsystemd/libsystemd.pc + +pkginclude_HEADERS += \ + src/systemd/sd-bus.h \ + src/systemd/sd-bus-protocol.h \ + src/systemd/sd-bus-vtable.h \ + src/systemd/sd-event.h \ + src/systemd/sd-login.h \ + src/systemd/sd-id128.h \ + src/systemd/sd-daemon.h + +lib_LTLIBRARIES += \ + libsystemd.la + +# ------------------------------------------------------------------------------ + +tests += \ + test-bus-marshal \ + test-bus-signature \ + test-bus-benchmark \ + test-bus-chat \ + test-bus-cleanup \ + test-bus-server \ + test-bus-match \ + test-bus-kernel \ + test-bus-kernel-bloom \ + test-bus-zero-copy \ + test-bus-introspect \ + test-bus-objects \ + test-bus-error \ + test-bus-creds \ + test-bus-gvariant \ + test-event \ + test-netlink \ + test-local-addresses \ + test-resolve + +test-libsystemd-sym.c: \ + $(top_builddir)/src/libsystemd/libsystemd.sym \ + src/systemd/sd-journal.h \ + src/systemd/sd-daemon.h \ + src/systemd/sd-login.h \ + src/systemd/sd-bus.h \ + src/systemd/sd-utf8.h \ + src/systemd/sd-resolve.h \ + src/systemd/sd-path.h \ + src/systemd/sd-event.h + $(generate-sym-test) + +nodist_test_libsystemd_sym_SOURCES = \ + test-libsystemd-sym.c +test_libsystemd_sym_LDADD = \ + libsystemd.la + +nested.subdirs += src + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/include/systemd/_sd-common.h b/src/libsystemd/include/systemd/_sd-common.h new file mode 100644 index 0000000000..3bb886be75 --- /dev/null +++ b/src/libsystemd/include/systemd/_sd-common.h @@ -0,0 +1,83 @@ +#ifndef foosdcommonhfoo +#define foosdcommonhfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +/* This is a private header; never even think of including this directly! */ + +#if __INCLUDE_LEVEL__ <= 1 +#error "Do not include _sd-common.h directly; it is a private header." +#endif + +#ifndef _sd_printf_ +# if __GNUC__ >= 4 +# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b))) +# else +# define _sd_printf_(a,b) +# endif +#endif + +#ifndef _sd_sentinel_ +# define _sd_sentinel_ __attribute__((sentinel)) +#endif + +#ifndef _sd_packed_ +# define _sd_packed_ __attribute__((packed)) +#endif + +#ifndef _sd_pure_ +# define _sd_pure_ __attribute__((pure)) +#endif + +#ifndef _SD_STRINGIFY +# define _SD_XSTRINGIFY(x) #x +# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x) +#endif + +#ifndef _SD_BEGIN_DECLARATIONS +# ifdef __cplusplus +# define _SD_BEGIN_DECLARATIONS \ + extern "C" { \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# else +# define _SD_BEGIN_DECLARATIONS \ + struct _sd_useless_struct_to_allow_trailing_semicolon_ +# endif +#endif + +#ifndef _SD_END_DECLARATIONS +# ifdef __cplusplus +# define _SD_END_DECLARATIONS \ + } \ + struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_ +# else +# define _SD_END_DECLARATIONS \ + 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/libsystemd/include/systemd/sd-bus-protocol.h b/src/libsystemd/include/systemd/sd-bus-protocol.h new file mode 100644 index 0000000000..623cee0c50 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus-protocol.h @@ -0,0 +1,102 @@ +#ifndef foosdbusprotocolhfoo +#define foosdbusprotocolhfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* Types of message */ + +enum { + _SD_BUS_MESSAGE_TYPE_INVALID = 0, + SD_BUS_MESSAGE_METHOD_CALL, + SD_BUS_MESSAGE_METHOD_RETURN, + SD_BUS_MESSAGE_METHOD_ERROR, + SD_BUS_MESSAGE_SIGNAL, + _SD_BUS_MESSAGE_TYPE_MAX +}; + +/* Primitive types */ + +enum { + _SD_BUS_TYPE_INVALID = 0, + SD_BUS_TYPE_BYTE = 'y', + SD_BUS_TYPE_BOOLEAN = 'b', + SD_BUS_TYPE_INT16 = 'n', + SD_BUS_TYPE_UINT16 = 'q', + SD_BUS_TYPE_INT32 = 'i', + SD_BUS_TYPE_UINT32 = 'u', + SD_BUS_TYPE_INT64 = 'x', + SD_BUS_TYPE_UINT64 = 't', + SD_BUS_TYPE_DOUBLE = 'd', + SD_BUS_TYPE_STRING = 's', + SD_BUS_TYPE_OBJECT_PATH = 'o', + SD_BUS_TYPE_SIGNATURE = 'g', + SD_BUS_TYPE_UNIX_FD = 'h', + SD_BUS_TYPE_ARRAY = 'a', + SD_BUS_TYPE_VARIANT = 'v', + SD_BUS_TYPE_STRUCT = 'r', /* not actually used in signatures */ + SD_BUS_TYPE_STRUCT_BEGIN = '(', + SD_BUS_TYPE_STRUCT_END = ')', + SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */ + SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{', + SD_BUS_TYPE_DICT_ENTRY_END = '}' +}; + +/* Well-known errors. Note that this is only a sanitized subset of the + * errors that the reference implementation generates. */ + +#define SD_BUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" +#define SD_BUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" +#define SD_BUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" +#define SD_BUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" +#define SD_BUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" +#define SD_BUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" +#define SD_BUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" +#define SD_BUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" +#define SD_BUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" +#define SD_BUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" +#define SD_BUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" +#define SD_BUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" +#define SD_BUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" +#define SD_BUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" +#define SD_BUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" +#define SD_BUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" +#define SD_BUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +#define SD_BUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" +#define SD_BUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" +#define SD_BUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" +#define SD_BUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" +#define SD_BUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" +#define SD_BUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#define SD_BUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" +#define SD_BUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" +#define SD_BUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" +#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" +#define SD_BUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" +#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED \ + "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-bus-vtable.h b/src/libsystemd/include/systemd/sd-bus-vtable.h new file mode 100644 index 0000000000..e8f84eb545 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus-vtable.h @@ -0,0 +1,141 @@ +#ifndef foosdbusvtablehfoo +#define foosdbusvtablehfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_bus_vtable sd_bus_vtable; + +#include "sd-bus.h" + +enum { + _SD_BUS_VTABLE_START = '<', + _SD_BUS_VTABLE_END = '>', + _SD_BUS_VTABLE_METHOD = 'M', + _SD_BUS_VTABLE_SIGNAL = 'S', + _SD_BUS_VTABLE_PROPERTY = 'P', + _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W' +}; + +enum { + SD_BUS_VTABLE_DEPRECATED = 1ULL << 0, + SD_BUS_VTABLE_HIDDEN = 1ULL << 1, + SD_BUS_VTABLE_UNPRIVILEGED = 1ULL << 2, + SD_BUS_VTABLE_METHOD_NO_REPLY = 1ULL << 3, + SD_BUS_VTABLE_PROPERTY_CONST = 1ULL << 4, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE = 1ULL << 5, + SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION = 1ULL << 6, + SD_BUS_VTABLE_PROPERTY_EXPLICIT = 1ULL << 7, + _SD_BUS_VTABLE_CAPABILITY_MASK = 0xFFFFULL << 40 +}; + +#define SD_BUS_VTABLE_CAPABILITY(x) ((uint64_t) (((x)+1) & 0xFFFF) << 40) + +struct sd_bus_vtable { + /* Please do not initialize this structure directly, use the + * macros below instead */ + + uint8_t type:8; + uint64_t flags:56; + union { + struct { + size_t element_size; + } start; + struct { + const char *member; + const char *signature; + const char *result; + sd_bus_message_handler_t handler; + size_t offset; + } method; + struct { + const char *member; + const char *signature; + } signal; + struct { + const char *member; + const char *signature; + sd_bus_property_get_t get; + sd_bus_property_set_t set; + size_t offset; + } property; + } x; +}; + +#define SD_BUS_VTABLE_START(_flags) \ + { \ + .type = _SD_BUS_VTABLE_START, \ + .flags = _flags, \ + .x.start.element_size = sizeof(sd_bus_vtable), \ + } + +#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_METHOD, \ + .flags = _flags, \ + .x.method.member = _member, \ + .x.method.signature = _signature, \ + .x.method.result = _result, \ + .x.method.handler = _handler, \ + .x.method.offset = _offset, \ + } +#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ + SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) + +#define SD_BUS_SIGNAL(_member, _signature, _flags) \ + { \ + .type = _SD_BUS_VTABLE_SIGNAL, \ + .flags = _flags, \ + .x.signal.member = _member, \ + .x.signal.signature = _signature, \ + } + +#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_PROPERTY, \ + .flags = _flags, \ + .x.property.member = _member, \ + .x.property.signature = _signature, \ + .x.property.get = _get, \ + .x.property.offset = _offset, \ + } + +#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \ + { \ + .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \ + .flags = _flags, \ + .x.property.member = _member, \ + .x.property.signature = _signature, \ + .x.property.get = _get, \ + .x.property.set = _set, \ + .x.property.offset = _offset, \ + } + +#define SD_BUS_VTABLE_END \ + { \ + .type = _SD_BUS_VTABLE_END, \ + } + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-bus.h b/src/libsystemd/include/systemd/sd-bus.h new file mode 100644 index 0000000000..4e338a3270 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-bus.h @@ -0,0 +1,455 @@ +#ifndef foosdbushfoo +#define foosdbushfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "_sd-common.h" +#include "sd-event.h" +#include "sd-id128.h" + +_SD_BEGIN_DECLARATIONS; + +/* Types */ + +typedef struct sd_bus sd_bus; +typedef struct sd_bus_message sd_bus_message; +typedef struct sd_bus_slot sd_bus_slot; +typedef struct sd_bus_creds sd_bus_creds; +typedef struct sd_bus_track sd_bus_track; + +typedef struct { + const char *name; + const char *message; + int _need_free; +} sd_bus_error; + +typedef struct { + const char* name; + int code; +} sd_bus_error_map; + +/* Flags */ + +enum { + SD_BUS_CREDS_PID = 1ULL << 0, + SD_BUS_CREDS_TID = 1ULL << 1, + SD_BUS_CREDS_PPID = 1ULL << 2, + SD_BUS_CREDS_UID = 1ULL << 3, + SD_BUS_CREDS_EUID = 1ULL << 4, + SD_BUS_CREDS_SUID = 1ULL << 5, + SD_BUS_CREDS_FSUID = 1ULL << 6, + SD_BUS_CREDS_GID = 1ULL << 7, + SD_BUS_CREDS_EGID = 1ULL << 8, + SD_BUS_CREDS_SGID = 1ULL << 9, + SD_BUS_CREDS_FSGID = 1ULL << 10, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 11, + SD_BUS_CREDS_COMM = 1ULL << 12, + SD_BUS_CREDS_TID_COMM = 1ULL << 13, + SD_BUS_CREDS_EXE = 1ULL << 14, + SD_BUS_CREDS_CMDLINE = 1ULL << 15, + SD_BUS_CREDS_CGROUP = 1ULL << 16, + SD_BUS_CREDS_UNIT = 1ULL << 17, + SD_BUS_CREDS_SLICE = 1ULL << 18, + SD_BUS_CREDS_USER_UNIT = 1ULL << 19, + SD_BUS_CREDS_USER_SLICE = 1ULL << 20, + SD_BUS_CREDS_SESSION = 1ULL << 21, + SD_BUS_CREDS_OWNER_UID = 1ULL << 22, + SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 23, + SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 24, + SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 25, + SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 26, + SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 27, + SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 28, + SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 29, + SD_BUS_CREDS_TTY = 1ULL << 30, + SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 31, + SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32, + SD_BUS_CREDS_DESCRIPTION = 1ULL << 33, + SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ + _SD_BUS_CREDS_ALL = (1ULL << 34) -1 +}; + +enum { + SD_BUS_NAME_REPLACE_EXISTING = 1ULL << 0, + SD_BUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, + SD_BUS_NAME_QUEUE = 1ULL << 2 +}; + +/* Callbacks */ + +typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error); +typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error); +typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error); +typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata); + +#include "sd-bus-protocol.h" +#include "sd-bus-vtable.h" + +/* Connections */ + +int sd_bus_default(sd_bus **ret); +int sd_bus_default_user(sd_bus **ret); +int sd_bus_default_system(sd_bus **ret); + +int sd_bus_open(sd_bus **ret); +int sd_bus_open_user(sd_bus **ret); +int sd_bus_open_system(sd_bus **ret); +int sd_bus_open_system_remote(sd_bus **ret, const char *host); +int sd_bus_open_system_machine(sd_bus **ret, const char *machine); + +int sd_bus_new(sd_bus **ret); + +int sd_bus_set_address(sd_bus *bus, const char *address); +int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); +int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]); +int sd_bus_get_address(sd_bus *bus, const char **address); +int sd_bus_set_bus_client(sd_bus *bus, int b); +int sd_bus_is_bus_client(sd_bus *bus); +int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t bus_id); +int sd_bus_is_server(sd_bus *bus); +int sd_bus_set_anonymous(sd_bus *bus, int b); +int sd_bus_is_anonymous(sd_bus *bus); +int sd_bus_set_trusted(sd_bus *bus, int b); +int sd_bus_is_trusted(sd_bus *bus); +int sd_bus_set_monitor(sd_bus *bus, int b); +int sd_bus_is_monitor(sd_bus *bus); +int sd_bus_set_description(sd_bus *bus, const char *description); +int sd_bus_get_description(sd_bus *bus, const char **description); +int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask); +int sd_bus_negotiate_timestamp(sd_bus *bus, int b); +int sd_bus_negotiate_fds(sd_bus *bus, int b); +int sd_bus_can_send(sd_bus *bus, char type); +int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask); +int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); +int sd_bus_get_allow_interactive_authorization(sd_bus *bus); + +int sd_bus_start(sd_bus *ret); + +int sd_bus_try_close(sd_bus *bus); +void sd_bus_close(sd_bus *bus); + +sd_bus *sd_bus_ref(sd_bus *bus); +sd_bus *sd_bus_unref(sd_bus *bus); +sd_bus *sd_bus_flush_close_unref(sd_bus *bus); + +void sd_bus_default_flush_close(void); + +int sd_bus_is_open(sd_bus *bus); + +int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); +int sd_bus_get_scope(sd_bus *bus, const char **scope); +int sd_bus_get_tid(sd_bus *bus, pid_t *tid); +int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); + +int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie); +int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie); +int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply); +int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec); + +int sd_bus_get_fd(sd_bus *bus); +int sd_bus_get_events(sd_bus *bus); +int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec); +int sd_bus_process(sd_bus *bus, sd_bus_message **r); +int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); +int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); +int sd_bus_flush(sd_bus *bus); + +sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); +sd_bus_message* sd_bus_get_current_message(sd_bus *bus); +sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus); +void* sd_bus_get_current_userdata(sd_bus *bus); + +int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); +int sd_bus_detach_event(sd_bus *bus); +sd_event *sd_bus_get_event(sd_bus *bus); + +int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); +int sd_bus_add_fallback_vtable(sd_bus *bus, sd_bus_slot **slot, const char *prefix, const char *interface, const sd_bus_vtable *vtable, sd_bus_object_find_t find, void *userdata); +int sd_bus_add_node_enumerator(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_node_enumerator_t callback, void *userdata); +int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path); + +/* Slot object */ + +sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); +sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); + +sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); +void *sd_bus_slot_get_userdata(sd_bus_slot *slot); +void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); +int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); +int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description); + +sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); +sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus); +void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot); + +/* Message object */ + +int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); +int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m); +int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e); +int sd_bus_message_new_method_errorf(sd_bus_message *call, sd_bus_message **m, const char *name, const char *format, ...) _sd_printf_(4, 5); +int sd_bus_message_new_method_errno(sd_bus_message *call, sd_bus_message **m, int error, const sd_bus_error *e); +int sd_bus_message_new_method_errnof(sd_bus_message *call, sd_bus_message **m, int error, const char *format, ...) _sd_printf_(4, 5); + +sd_bus_message* sd_bus_message_ref(sd_bus_message *m); +sd_bus_message* sd_bus_message_unref(sd_bus_message *m); + +int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); +int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie); +int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority); + +int sd_bus_message_get_expect_reply(sd_bus_message *m); +int sd_bus_message_get_auto_start(sd_bus_message *m); +int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m); + +const char *sd_bus_message_get_signature(sd_bus_message *m, int complete); +const char *sd_bus_message_get_path(sd_bus_message *m); +const char *sd_bus_message_get_interface(sd_bus_message *m); +const char *sd_bus_message_get_member(sd_bus_message *m); +const char *sd_bus_message_get_destination(sd_bus_message *m); +const char *sd_bus_message_get_sender(sd_bus_message *m); +const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m); +int sd_bus_message_get_errno(sd_bus_message *m); + +int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec); +int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t* seqnum); + +sd_bus* sd_bus_message_get_bus(sd_bus_message *m); +sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ + +int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); +int sd_bus_message_is_method_error(sd_bus_message *m, const char *name); +int sd_bus_message_is_empty(sd_bus_message *m); +int sd_bus_message_has_signature(sd_bus_message *m, const char *signature); + +int sd_bus_message_set_expect_reply(sd_bus_message *m, int b); +int sd_bus_message_set_auto_start(sd_bus_message *m, int b); +int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); + +int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); +int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); + +int sd_bus_message_append(sd_bus_message *m, const char *types, ...); +int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); +int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); +int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); +int sd_bus_message_append_array_iovec(sd_bus_message *m, char type, const struct iovec *iov, unsigned n); +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); +int sd_bus_message_append_string_iovec(sd_bus_message *m, const struct iovec *iov, unsigned n); +int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd, uint64_t offset, uint64_t size); +int sd_bus_message_append_strv(sd_bus_message *m, char **l); +int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_close_container(sd_bus_message *m); +int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all); + +int sd_bus_message_read(sd_bus_message *m, const char *types, ...); +int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p); +int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, size_t *size); +int sd_bus_message_read_strv(sd_bus_message *m, char ***l); /* free the result! */ +int sd_bus_message_skip(sd_bus_message *m, const char *types); +int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_exit_container(sd_bus_message *m); +int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents); +int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents); +int sd_bus_message_at_end(sd_bus_message *m, int complete); +int sd_bus_message_rewind(sd_bus_message *m, int complete); + +/* Bus management */ + +int sd_bus_get_unique_name(sd_bus *bus, const char **unique); +int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ +int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ +int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); + +/* Convenience calls */ + +int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...); +int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); +int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type); +int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr); +int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */ +int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */ +int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...); + +int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...); +int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e); +int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e); +int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); + +int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); + +int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); +int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) _sd_sentinel_; + +int sd_bus_emit_object_added(sd_bus *bus, const char *path); +int sd_bus_emit_object_removed(sd_bus *bus, const char *path); +int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; +int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; + +int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); +int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); + +/* Credential handling */ + +int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); +sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c); +sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c); +uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); +uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c); + +int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid); +int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid); +int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid); +int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid); +int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid); +int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid); +int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid); +int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid); +int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid); +int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid); +int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids); +int sd_bus_creds_get_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **comm); +int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe); +int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline); +int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **cgroup); +int sd_bus_creds_get_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit); +int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **slice); +int sd_bus_creds_get_session(sd_bus_creds *c, const char **session); +int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability); +int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context); +int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid); +int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid); +int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty); +int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name); +int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names); +int sd_bus_creds_get_description(sd_bus_creds *c, const char **name); + +/* Error structures */ + +#define SD_BUS_ERROR_MAKE_CONST(name, message) ((const sd_bus_error) {(name), (message), 0}) +#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE_CONST(NULL, NULL) + +void sd_bus_error_free(sd_bus_error *e); +int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); +int sd_bus_error_set_errno(sd_bus_error *e, int error); +int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) _sd_printf_(3, 4); +int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0); +int sd_bus_error_get_errno(const sd_bus_error *e); +int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); +int sd_bus_error_is_set(const sd_bus_error *e); +int sd_bus_error_has_name(const sd_bus_error *e, const char *name); + +#define SD_BUS_ERROR_MAP(_name, _code) \ + { \ + .name = _name, \ + .code = _code, \ + } +#define SD_BUS_ERROR_MAP_END \ + { \ + .name = NULL, \ + .code = - 'x', \ + } + +int sd_bus_error_add_map(const sd_bus_error_map *map); + +/* Auxiliary macros */ + +#define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ + (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ + (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ + (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ + (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +#define SD_BUS_MESSAGE_READ_ID128(x) 16, \ + &(x).bytes[0], &(x).bytes[1], &(x).bytes[2], &(x).bytes[3], \ + &(x).bytes[4], &(x).bytes[5], &(x).bytes[6], &(x).bytes[7], \ + &(x).bytes[8], &(x).bytes[9], &(x).bytes[10], &(x).bytes[11], \ + &(x).bytes[12], &(x).bytes[13], &(x).bytes[14], &(x).bytes[15] + +/* 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 */ + +int sd_bus_track_new(sd_bus *bus, sd_bus_track **track, sd_bus_track_handler_t handler, void *userdata); +sd_bus_track* sd_bus_track_ref(sd_bus_track *track); +sd_bus_track* sd_bus_track_unref(sd_bus_track *track); + +sd_bus* sd_bus_track_get_bus(sd_bus_track *track); +void *sd_bus_track_get_userdata(sd_bus_track *track); +void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); + +int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m); +int sd_bus_track_add_name(sd_bus_track *track, const char *name); +int sd_bus_track_remove_name(sd_bus_track *track, const char *name); + +unsigned sd_bus_track_count(sd_bus_track *track); +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/libsystemd/include/systemd/sd-daemon.h b/src/libsystemd/include/systemd/sd-daemon.h new file mode 100644 index 0000000000..740b176903 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-daemon.h @@ -0,0 +1,294 @@ +#ifndef foosddaemonhfoo +#define foosddaemonhfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* + The following functionality is provided: + + - Support for logging with log levels on stderr + - File descriptor passing for socket-based activation + - Daemon startup and status notification + - Detection of systemd boots + + See sd-daemon(3) for more information. +*/ + +/* + Log levels for usage on stderr: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + This is similar to printk() usage in the kernel. +*/ +#define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + +/* The first passed file descriptor is fd 3 */ +#define SD_LISTEN_FDS_START 3 + +/* + Returns how many file descriptors have been passed, or a negative + errno code on failure. Optionally, removes the $LISTEN_FDS and + $LISTEN_PID file descriptors from the environment (recommended, but + problematic in threaded environments). If r is the return value of + this function you'll find the file descriptors passed as fds + SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative + errno style error code on failure. This function call ensures that + the FD_CLOEXEC flag is set for the passed file descriptors, to make + sure they are not passed on to child processes. If FD_CLOEXEC shall + not be set, the caller needs to unset it after this call for all file + descriptors that are used. + + See sd_listen_fds(3) for more information. +*/ +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 + specified path, 0 otherwise. If path is NULL a path name check will + not be done and the call only verifies if the file descriptor + refers to a FIFO. Returns a negative errno style error code on + failure. + + See sd_is_fifo(3) for more information. +*/ +int sd_is_fifo(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a special character device on the file + system stored under the specified path, 0 otherwise. + If path is NULL a path name check will not be done and the call + only verifies if the file descriptor refers to a special character. + Returns a negative errno style error code on failure. + + See sd_is_special(3) for more information. +*/ +int sd_is_special(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a socket of the specified family (AF_INET, + ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If + family is 0 a socket family check will not be done. If type is 0 a + socket type check will not be done and the call only verifies if + the file descriptor refers to a socket. If listening is > 0 it is + verified that the socket is in listening mode. (i.e. listen() has + been called) If listening is == 0 it is verified that the socket is + not in listening mode. If listening is < 0 no listening mode check + is done. Returns a negative errno style error code on failure. + + See sd_is_socket(3) for more information. +*/ +int sd_is_socket(int fd, int family, int type, int listening); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an Internet socket, of the specified family + (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, + SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version + check is not done. If type is 0 a socket type check will not be + done. If port is 0 a socket port check will not be done. The + listening flag is used the same way as in sd_is_socket(). Returns a + negative errno style error code on failure. + + See sd_is_socket_inet(3) for more information. +*/ +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an AF_UNIX socket of the specified type + (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 + a socket type check will not be done. If path is NULL a socket path + check will not be done. For normal AF_UNIX sockets set length to + 0. For abstract namespace sockets set length to the length of the + socket name (including the initial 0 byte), and pass the full + socket path in path (including the initial 0 byte). The listening + flag is used the same way as in sd_is_socket(). Returns a negative + errno style error code on failure. + + See sd_is_socket_unix(3) for more information. +*/ +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a POSIX Message Queue of the specified name, + 0 otherwise. If path is NULL a message queue name check is not + done. Returns a negative errno style error code on failure. + + See sd_is_mq(3) for more information. +*/ +int sd_is_mq(int fd, const char *path); + +/* + Informs systemd about changed daemon state. This takes a number of + newline separated environment-style variable assignments in a + string. The following variables are known: + + READY=1 Tells systemd that daemon startup is finished (only + relevant for services of Type=notify). The passed + argument is a boolean "1" or "0". Since there is + little value in signaling non-readiness the only + value daemons should send is "READY=1". + + STATUS=... Passes a single-line status string back to systemd + that describes the daemon state. This is free-form + and can be used for various purposes: general state + feedback, fsck-like programs could pass completion + percentages and failing programs could pass a human + readable error message. Example: "STATUS=Completed + 66% of file system check..." + + ERRNO=... If a daemon fails, the errno-style error code, + formatted as string. Example: "ERRNO=2" for ENOENT. + + BUSERROR=... If a daemon fails, the D-Bus error-style error + code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + + MAINPID=... The main pid of a daemon, in case systemd did not + fork off the process itself. Example: "MAINPID=4711" + + WATCHDOG=1 Tells systemd to update the watchdog timestamp. + Services using this feature should do this in + regular intervals. A watchdog framework can use the + timestamps to detect failed services. Also see + sd_watchdog_enabled() below. + + FDSTORE=1 Store the file descriptors passed along with the + message in the per-service file descriptor store, + and pass them to the main process again on next + invocation. This variable is only supported with + sd_pid_notify_with_fds(). + + WATCHDOG_USEC=... + Reset watchdog_usec value during runtime. + To reset watchdog_usec value, start the service again. + Example: "WATCHDOG_USEC=20000000" + + Daemons can choose to send additional variables. However, it is + recommended to prefix variable names not listed above with X_. + + Returns a negative errno-style error code on failure. Returns > 0 + if systemd could be notified, 0 if it couldn't possibly because + systemd is not running. + + Example: When a daemon finished starting up, it could issue this + call to notify systemd about it: + + sd_notify(0, "READY=1"); + + See sd_notifyf() for more complete examples. + + See sd_notify(3) for more information. +*/ +int sd_notify(int unset_environment, const char *state); + +/* + Similar to sd_notify() but takes a format string. + + Example 1: A daemon could send the following after initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + Example 2: A daemon could send the following shortly before + exiting, on failure: + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror(errno), + errno); + + See sd_notifyf(3) for more information. +*/ +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_(2,3); + +/* + Similar to sd_notify(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notify(pid_t pid, int unset_environment, const char *state); + +/* + Similar to sd_notifyf(), but send the message on behalf of another + process, if the appropriate permissions are available. +*/ +int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _sd_printf_(3,4); + +/* + Similar to sd_pid_notify(), but also passes the specified fd array + to the service manager for storage. This is particularly useful for + FDSTORE=1 messages. +*/ +int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); + +/* + Returns > 0 if the system was booted with systemd. Returns < 0 on + error. Returns 0 if the system was not booted with systemd. Note + that all of the functions above handle non-systemd boots just + fine. You should NOT protect them with a call to this function. Also + note that this function checks whether the system, not the user + session is controlled by systemd. However the functions above work + for both user and system services. + + See sd_booted(3) for more information. +*/ +int sd_booted(void); + +/* + Returns > 0 if the service manager expects watchdog keep-alive + events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns + 0 if it does not expect this. If the usec argument is non-NULL + returns the watchdog timeout in µs after which the service manager + will act on a process that has not sent a watchdog keep alive + message. This function is useful to implement services that + recognize automatically if they are being run under supervision of + systemd with WatchdogSec= set. It is recommended for clients to + generate keep-alive pings via sd_notify(0, "WATCHDOG=1") every half + of the returned time. + + See sd_watchdog_enabled(3) for more information. +*/ +int sd_watchdog_enabled(int unset_environment, uint64_t *usec); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-event.h b/src/libsystemd/include/systemd/sd-event.h new file mode 100644 index 0000000000..cc26b7df55 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-event.h @@ -0,0 +1,143 @@ +#ifndef foosdeventhfoo +#define foosdeventhfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "_sd-common.h" + +/* + Why is this better than pure epoll? + + - Supports event source prioritization + - Scales better with a large number of time events because it does not require one timerfd each + - Automatically tries to coalesce timer events system-wide + - Handles signals and child PIDs +*/ + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_event sd_event; +typedef struct sd_event_source sd_event_source; + +enum { + SD_EVENT_OFF = 0, + SD_EVENT_ON = 1, + SD_EVENT_ONESHOT = -1 +}; + +enum { + SD_EVENT_INITIAL, + SD_EVENT_ARMED, + SD_EVENT_PENDING, + SD_EVENT_RUNNING, + SD_EVENT_EXITING, + SD_EVENT_FINISHED, + SD_EVENT_PREPARING +}; + +enum { + /* And everything in-between and outside is good too */ + SD_EVENT_PRIORITY_IMPORTANT = -100, + SD_EVENT_PRIORITY_NORMAL = 0, + SD_EVENT_PRIORITY_IDLE = 100 +}; + +typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); +typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); +typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); +typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); +#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED +typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); +#else +typedef void* sd_event_child_handler_t; +#endif + +int sd_event_default(sd_event **e); + +int sd_event_new(sd_event **e); +sd_event* sd_event_ref(sd_event *e); +sd_event* sd_event_unref(sd_event *e); + +int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata); +int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); +int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata); +int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); +int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); +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 usec); +int sd_event_dispatch(sd_event *e); +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); + +int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec); + +int sd_event_get_fd(sd_event *e); +int sd_event_get_state(sd_event *e); +int sd_event_get_tid(sd_event *e, pid_t *tid); +int sd_event_get_exit_code(sd_event *e, int *code); +int sd_event_set_watchdog(sd_event *e, int b); +int sd_event_get_watchdog(sd_event *e); +int sd_event_get_iteration(sd_event *e, uint64_t *ret); + +sd_event_source* sd_event_source_ref(sd_event_source *s); +sd_event_source* sd_event_source_unref(sd_event_source *s); + +sd_event *sd_event_source_get_event(sd_event_source *s); +void* sd_event_source_get_userdata(sd_event_source *s); +void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); + +int sd_event_source_set_description(sd_event_source *s, const char *description); +int sd_event_source_get_description(sd_event_source *s, const char **description); +int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback); +int sd_event_source_get_pending(sd_event_source *s); +int sd_event_source_get_priority(sd_event_source *s, int64_t *priority); +int sd_event_source_set_priority(sd_event_source *s, int64_t priority); +int sd_event_source_get_enabled(sd_event_source *s, int *enabled); +int sd_event_source_set_enabled(sd_event_source *s, int enabled); +int sd_event_source_get_io_fd(sd_event_source *s); +int sd_event_source_set_io_fd(sd_event_source *s, int fd); +int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); +int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); +int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); +int sd_event_source_get_time(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time(sd_event_source *s, uint64_t usec); +int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec); +int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec); +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/libsystemd/include/systemd/sd-id128.h b/src/libsystemd/include/systemd/sd-id128.h new file mode 100644 index 0000000000..4dff0b9b81 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-id128.h @@ -0,0 +1,115 @@ +#ifndef foosdid128hfoo +#define foosdid128hfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* 128-bit ID APIs. See sd-id128(3) for more information. */ + +typedef union sd_id128 sd_id128_t; + +union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +}; + +#define SD_ID128_STRING_MAX 33 + +char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]); + +int sd_id128_from_string(const char *s, sd_id128_t *ret); + +int sd_id128_randomize(sd_id128_t *ret); + +int sd_id128_get_machine(sd_id128_t *ret); + +int sd_id128_get_boot(sd_id128_t *ret); + +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) + +#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} + +/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 + * times. It is hence not a good idea to call this macro with an + * expensive function as parameter or an expression with side + * effects */ + +#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" +#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + +#define SD_ID128_CONST_STR(x) \ + ((const char[SD_ID128_STRING_MAX]) { \ + ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \ + ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \ + ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \ + ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \ + ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \ + ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \ + ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \ + ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \ + ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \ + ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \ + ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \ + ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \ + ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \ + ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \ + ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \ + ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \ + ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \ + ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \ + ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \ + ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \ + ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \ + ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \ + ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \ + ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \ + ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \ + ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \ + ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \ + ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \ + ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \ + ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \ + ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \ + ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ + 0 }) + +_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return memcmp(&a, &b, 16) == 0; +} + +_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { + return a.qwords[0] == 0 && a.qwords[1] == 0; +} + +#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-journal.h b/src/libsystemd/include/systemd/sd-journal.h new file mode 100644 index 0000000000..b684cf073c --- /dev/null +++ b/src/libsystemd/include/systemd/sd-journal.h @@ -0,0 +1,174 @@ +#ifndef foosdjournalhfoo +#define foosdjournalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "_sd-common.h" +#include "sd-id128.h" + +/* Journal APIs. See sd-journal(3) for more information. */ + +_SD_BEGIN_DECLARATIONS; + +/* Write to daemon */ +int sd_journal_print(int priority, const char *format, ...) _sd_printf_(2, 3); +int sd_journal_printv(int priority, const char *format, va_list ap) _sd_printf_(2, 0); +int sd_journal_send(const char *format, ...) _sd_printf_(1, 0) _sd_sentinel_; +int sd_journal_sendv(const struct iovec *iov, int n); +int sd_journal_perror(const char *message); + +/* Used by the macros below. You probably don't want to call this directly. */ +int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(5, 6); +int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) _sd_printf_(5, 0); +int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(4, 0) _sd_sentinel_; +int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n); +int sd_journal_perror_with_location(const char *file, const char *line, const char *func, const char *message); + +/* implicitly add code location to messages sent, if this is enabled */ +#ifndef SD_JOURNAL_SUPPRESS_LOCATION + +#define sd_journal_print(priority, ...) sd_journal_print_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_printv(priority, format, ap) sd_journal_printv_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, format, ap) +#define sd_journal_send(...) sd_journal_send_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_sendv(iovec, n) sd_journal_sendv_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, iovec, n) +#define sd_journal_perror(message) sd_journal_perror_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, message) + +#endif + +int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); + +/* Browse journal stream */ + +typedef struct sd_journal sd_journal; + +/* Open flags */ +enum { + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, + + SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ +}; + +/* Wakeup event types */ +enum { + SD_JOURNAL_NOP, + SD_JOURNAL_APPEND, + SD_JOURNAL_INVALIDATE +}; + +int sd_journal_open(sd_journal **ret, int flags); +int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); +int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ +void sd_journal_close(sd_journal *j); + +int sd_journal_previous(sd_journal *j); +int sd_journal_next(sd_journal *j); + +int sd_journal_previous_skip(sd_journal *j, uint64_t skip); +int sd_journal_next_skip(sd_journal *j, uint64_t skip); + +int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); + +int sd_journal_set_data_threshold(sd_journal *j, size_t sz); +int sd_journal_get_data_threshold(sd_journal *j, size_t *sz); + +int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); +int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_data(sd_journal *j); + +int sd_journal_add_match(sd_journal *j, const void *data, size_t size); +int sd_journal_add_disjunction(sd_journal *j); +int sd_journal_add_conjunction(sd_journal *j); +void sd_journal_flush_matches(sd_journal *j); + +int sd_journal_seek_head(sd_journal *j); +int sd_journal_seek_tail(sd_journal *j); +int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cursor(sd_journal *j, char **cursor); +int sd_journal_test_cursor(sd_journal *j, const char *cursor); + +int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to); +int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, const sd_id128_t boot_id, uint64_t *from, uint64_t *to); + +int sd_journal_get_usage(sd_journal *j, uint64_t *bytes); + +int sd_journal_query_unique(sd_journal *j, const char *field); +int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); +void sd_journal_restart_unique(sd_journal *j); + +int sd_journal_enumerate_fields(sd_journal *j, const char **field); +void sd_journal_restart_fields(sd_journal *j); + +int sd_journal_get_fd(sd_journal *j); +int sd_journal_get_events(sd_journal *j); +int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec); +int sd_journal_process(sd_journal *j); +int sd_journal_wait(sd_journal *j, uint64_t timeout_usec); +int sd_journal_reliable_fd(sd_journal *j); + +int sd_journal_get_catalog(sd_journal *j, char **text); +int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); + +int sd_journal_has_runtime_files(sd_journal *j); +int sd_journal_has_persistent_files(sd_journal *j); + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH(j) \ + if (sd_journal_seek_head(j) < 0) { } \ + else while (sd_journal_next(j) > 0) + +/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ +#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ + if (sd_journal_seek_tail(j) < 0) { } \ + else while (sd_journal_previous(j) > 0) + +/* Iterate through the data fields of the current journal entry */ +#define SD_JOURNAL_FOREACH_DATA(j, data, l) \ + for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) + +/* Iterate through the all known values of a specific field */ +#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ + for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; ) + +/* Iterate through all known field names */ +#define SD_JOURNAL_FOREACH_FIELD(j, field) \ + for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; ) + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-login.h b/src/libsystemd/include/systemd/sd-login.h new file mode 100644 index 0000000000..e3ecbd8378 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-login.h @@ -0,0 +1,245 @@ +#ifndef foosdloginhfoo +#define foosdloginhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "_sd-common.h" + +/* + * A few points: + * + * Instead of returning an empty string array or empty uid array, we + * may return NULL. + * + * Free the data the library returns with libc free(). String arrays + * are NULL terminated, and you need to free the array itself, in + * addition to the strings contained. + * + * We return error codes as negative errno, kernel-style. On success, we + * return 0 or positive. + * + * These functions access data in /proc, /sys/fs/cgroup, and /run. All + * of these are virtual file systems; therefore, accesses are + * relatively cheap. + * + * See sd-login(3) for more information. + */ + +_SD_BEGIN_DECLARATIONS; + +/* Get session from PID. Note that 'shared' processes of a user are + * not attached to a session, but only attached to a user. This will + * return an error for system processes and 'shared' processes of a + * user. */ +int sd_pid_get_session(pid_t pid, char **session); + +/* Get UID of the owner of the session of the PID (or in case the + * process is a 'shared' user process, the UID of that user is + * returned). This will not return the UID of the process, but rather + * the UID of the owner of the cgroup that the process is in. This will + * return an error for system processes. */ +int sd_pid_get_owner_uid(pid_t pid, uid_t *uid); + +/* Get systemd non-slice unit (i.e. service) name from PID, for system + * services. This will return an error for non-service processes. */ +int sd_pid_get_unit(pid_t pid, char **unit); + +/* Get systemd non-slice unit (i.e. service) name from PID, for user + * services. This will return an error for non-user-service + * processes. */ +int sd_pid_get_user_unit(pid_t pid, char **unit); + +/* Get slice name from PID. */ +int sd_pid_get_slice(pid_t pid, char **slice); + +/* Get user slice name from PID. */ +int sd_pid_get_user_slice(pid_t pid, char **slice); + +/* Get machine name from PID, for processes assigned to a VM or + * container. This will return an error for non-machine processes. */ +int sd_pid_get_machine_name(pid_t pid, char **machine); + +/* Get the control group from a PID, relative to the root of the + * hierarchy. */ +int sd_pid_get_cgroup(pid_t pid, char **cgroup); + +/* Similar to sd_pid_get_session(), but retrieves data about the peer + * of a connected AF_UNIX socket */ +int sd_peer_get_session(int fd, char **session); + +/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_owner_uid(int fd, uid_t *uid); + +/* Similar to sd_pid_get_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_unit(int fd, char **unit); + +/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_unit(int fd, char **unit); + +/* Similar to sd_pid_get_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_slice(int fd, char **slice); + +/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ +int sd_peer_get_user_slice(int fd, char **slice); + +/* Similar to sd_pid_get_machine_name(), but retrieves data about the + * peer of a connected AF_UNIX socket */ +int sd_peer_get_machine_name(int fd, char **machine); + +/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer + * of a connected AF_UNIX socket. */ +int sd_peer_get_cgroup(pid_t pid, char **cgroup); + +/* Get state from UID. Possible states: offline, lingering, online, active, closing */ +int sd_uid_get_state(uid_t uid, char **state); + +/* Return primary session of user, if there is any */ +int sd_uid_get_display(uid_t uid, char **session); + +/* Return 1 if UID has session on seat. If require_active is true, this will + * look for active sessions only. */ +int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat); + +/* Return sessions of user. If require_active is true, this will look for + * active sessions only. Returns the number of sessions. + * If sessions is NULL, this will just return the number of sessions. */ +int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions); + +/* Return seats of user is on. If require_active is true, this will look for + * active seats only. Returns the number of seats. + * If seats is NULL, this will just return the number of seats. */ +int sd_uid_get_seats(uid_t uid, int require_active, char ***seats); + +/* Return 1 if the session is active. */ +int sd_session_is_active(const char *session); + +/* Return 1 if the session is remote. */ +int sd_session_is_remote(const char *session); + +/* Get state from session. Possible states: online, active, closing. + * This function is a more generic version of sd_session_is_active(). */ +int sd_session_get_state(const char *session, char **state); + +/* Determine user ID of session */ +int sd_session_get_uid(const char *session, uid_t *uid); + +/* Determine seat of session */ +int sd_session_get_seat(const char *session, char **seat); + +/* Determine the (PAM) service name this session was registered by. */ +int sd_session_get_service(const char *session, char **service); + +/* Determine the type of this session, i.e. one of "tty", "x11", "wayland", "mir" or "unspecified". */ +int sd_session_get_type(const char *session, char **type); + +/* Determine the class of this session, i.e. one of "user", "greeter" or "lock-screen". */ +int sd_session_get_class(const char *session, char **clazz); + +/* Determine the desktop brand of this session, i.e. something like "GNOME", "KDE" or "systemd-console". */ +int sd_session_get_desktop(const char *session, char **desktop); + +/* Determine the X11 display of this session. */ +int sd_session_get_display(const char *session, char **display); + +/* Determine the remote host of this session. */ +int sd_session_get_remote_host(const char *session, char **remote_host); + +/* Determine the remote user of this session (if provided by PAM). */ +int sd_session_get_remote_user(const char *session, char **remote_user); + +/* Determine the TTY of this session. */ +int sd_session_get_tty(const char *session, char **display); + +/* Determine the VT number of this session. */ +int sd_session_get_vt(const char *session, unsigned *vtnr); + +/* Return active session and user of seat */ +int sd_seat_get_active(const char *seat, char **session, uid_t *uid); + +/* Return sessions and users on seat. Returns number of sessions. + * If sessions is NULL, this returns only the number of sessions. */ +int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); + +/* Return whether the seat is multi-session capable */ +int sd_seat_can_multi_session(const char *seat); + +/* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */ +int sd_seat_can_tty(const char *seat); + +/* Return whether the seat is graphics capable, i.e. suitable for showing graphical UIs */ +int sd_seat_can_graphical(const char *seat); + +/* Return the class of machine */ +int sd_machine_get_class(const char *machine, char **clazz); + +/* Return the list if host-side network interface indices of a machine */ +int sd_machine_get_ifindices(const char *machine, int **ifindices); + +/* Get all seats, store in *seats. Returns the number of seats. If + * seats is NULL, this only returns the number of seats. */ +int sd_get_seats(char ***seats); + +/* Get all sessions, store in *sessions. Returns the number of + * sessions. If sessions is NULL, this only returns the number of sessions. */ +int sd_get_sessions(char ***sessions); + +/* Get all logged in users, store in *users. Returns the number of + * users. If users is NULL, this only returns the number of users. */ +int sd_get_uids(uid_t **users); + +/* Get all running virtual machines/containers */ +int sd_get_machine_names(char ***machines); + +/* Monitor object */ +typedef struct sd_login_monitor sd_login_monitor; + +/* Create a new monitor. Category must be NULL, "seat", "session", + * "uid", or "machine" to get monitor events for the specific category + * (or all). */ +int sd_login_monitor_new(const char *category, sd_login_monitor** ret); + +/* Destroys the passed monitor. Returns NULL. */ +sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m); + +/* Flushes the monitor */ +int sd_login_monitor_flush(sd_login_monitor *m); + +/* Get FD from monitor */ +int sd_login_monitor_get_fd(sd_login_monitor *m); + +/* Get poll() mask to monitor */ +int sd_login_monitor_get_events(sd_login_monitor *m); + +/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ +int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_login_monitor, sd_login_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-messages.h b/src/libsystemd/include/systemd/sd-messages.h new file mode 100644 index 0000000000..c8599c596d --- /dev/null +++ b/src/libsystemd/include/systemd/sd-messages.h @@ -0,0 +1,90 @@ +#ifndef foosdmessageshfoo +#define foosdmessageshfoo + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" +#include "sd-id128.h" + +_SD_BEGIN_DECLARATIONS; + +/* Hey! If you add a new message here, you *must* also update the + * message catalog with an appropriate explanation */ + +/* And if you add a new ID here, make sure to generate a random one + * with journalctl --new-id128. Do not use any other IDs, and do not + * count them up manually. */ + +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) + +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + +#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) + +#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) + +#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) + +#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) + +#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) + +#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) + +#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) + +#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) + +#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) + +#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) + +#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) +#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/include/systemd/sd-utf8.h b/src/libsystemd/include/systemd/sd-utf8.h new file mode 100644 index 0000000000..6781983878 --- /dev/null +++ b/src/libsystemd/include/systemd/sd-utf8.h @@ -0,0 +1,32 @@ +#ifndef foosdutf8hfoo +#define foosdutf8hfoo + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +_sd_pure_ const char *sd_utf8_is_valid(const char *s); +_sd_pure_ const char *sd_ascii_is_valid(const char *s); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libsystemd/libsystemd-pkgconfig.xml b/src/libsystemd/libsystemd-pkgconfig.xml new file mode 100644 index 0000000000..272da64cd7 --- /dev/null +++ b/src/libsystemd/libsystemd-pkgconfig.xml @@ -0,0 +1,12 @@ + + + + + Notes + + These APIs are implemented as a shared + library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + diff --git a/src/libsystemd/sd-bus-errors.xml b/src/libsystemd/sd-bus-errors.xml new file mode 100644 index 0000000000..d2b81f4e4a --- /dev/null +++ b/src/libsystemd/sd-bus-errors.xml @@ -0,0 +1,309 @@ + + + + + + + + + sd-bus-errors + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-bus-errors + 3 + + + + sd-bus-errors + SD_BUS_ERROR_FAILED + SD_BUS_ERROR_NO_MEMORY + SD_BUS_ERROR_SERVICE_UNKNOWN + SD_BUS_ERROR_NAME_HAS_NO_OWNER + SD_BUS_ERROR_NO_REPLY + SD_BUS_ERROR_IO_ERROR + SD_BUS_ERROR_BAD_ADDRESS + SD_BUS_ERROR_NOT_SUPPORTED + SD_BUS_ERROR_LIMITS_EXCEEDED + SD_BUS_ERROR_ACCESS_DENIED + SD_BUS_ERROR_AUTH_FAILED + SD_BUS_ERROR_NO_SERVER + SD_BUS_ERROR_TIMEOUT + SD_BUS_ERROR_NO_NETWORK + SD_BUS_ERROR_ADDRESS_IN_USE + SD_BUS_ERROR_DISCONNECTED + SD_BUS_ERROR_INVALID_ARGS + SD_BUS_ERROR_FILE_NOT_FOUND + SD_BUS_ERROR_FILE_EXISTS + SD_BUS_ERROR_UNKNOWN_METHOD + SD_BUS_ERROR_UNKNOWN_OBJECT + SD_BUS_ERROR_UNKNOWN_INTERFACE + SD_BUS_ERROR_UNKNOWN_PROPERTY + SD_BUS_ERROR_PROPERTY_READ_ONLY + SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN + SD_BUS_ERROR_INVALID_SIGNATURE + SD_BUS_ERROR_INCONSISTENT_MESSAGE + SD_BUS_ERROR_MATCH_RULE_NOT_FOUND + SD_BUS_ERROR_MATCH_RULE_INVALID + SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED + + Standard D-Bus error names + + + + + #include <systemd/sd-bus.h> + +#define SD_BUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" +#define SD_BUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" +#define SD_BUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" +#define SD_BUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" +#define SD_BUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" +#define SD_BUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" +#define SD_BUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" +#define SD_BUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" +#define SD_BUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" +#define SD_BUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" +#define SD_BUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" +#define SD_BUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" +#define SD_BUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" +#define SD_BUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" +#define SD_BUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" +#define SD_BUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" +#define SD_BUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" +#define SD_BUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" +#define SD_BUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" +#define SD_BUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" +#define SD_BUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" +#define SD_BUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" +#define SD_BUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" +#define SD_BUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" +#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" +#define SD_BUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" +#define SD_BUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" +#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" +#define SD_BUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" +#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED \ + "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" + + + + + + Description + + In addition to the error names user programs define, D-Bus + knows a number of generic, standardized error names that are + listed below. + + In addition to this list, in sd-bus, the special error + namespace System.Error. is used to map + arbitrary GNU/Linux system errors (as defined by errno3) + to D-Bus errors and back. For example, the error + EUCLEAN is mapped to + System.Error.EUCLEAN and back. + + + + + SD_BUS_ERROR_FAILED + A generic error indication. See the error + message for further details. This error name should be + avoided, in favor of a more expressive error + name. + + + + SD_BUS_ERROR_NO_MEMORY + A memory allocation failed, and the requested + operation could not be completed. + + + + SD_BUS_ERROR_SERVICE_UNKNOWN + The contacted bus service is unknown and + cannot be activated. + + + + SD_BUS_ERROR_NAME_HAS_NO_OWNER + The specified bus service name currently has + no owner. + + + SD_BUS_ERROR_NO_REPLY + A message did not receive a reply. This error + is usually generated after a timeout. + + + SD_BUS_ERROR_IO_ERROR + Generic input/output error, for example when + accessing a socket or other I/O context. + + + SD_BUS_ERROR_BAD_ADDRESS + The specified D-Bus bus address string is + malformed. + + + SD_BUS_ERROR_NOT_SUPPORTED + The requested operation is not supported on + the local system. + + + SD_BUS_ERROR_LIMITS_EXCEEDED + Some limited resource has been + exhausted. + + + SD_BUS_ERROR_ACCESS_DENIED + Access to a resource has been denied due to security restrictions. + + + SD_BUS_ERROR_AUTH_FAILED + Authentication did not complete successfully. + + + SD_BUS_ERROR_NO_SERVER + Unable to connect to the specified server. + + + SD_BUS_ERROR_TIMEOUT + An operation timed out. Note that method calls + which timeout generate a + SD_BUS_ERROR_NO_REPLY. + + + SD_BUS_ERROR_NO_NETWORK + No network available to execute requested network operation on. + + + SD_BUS_ERROR_ADDRESS_IN_USE + The specified network address is already being listened on. + + + SD_BUS_ERROR_DISCONNECTED + The connection has been terminated. + + + SD_BUS_ERROR_INVALID_ARGS + One or more invalid arguments have been passed. + + + SD_BUS_ERROR_FILE_NOT_FOUND + The requested file could not be found. + + + SD_BUS_ERROR_FILE_EXISTS + The requested file already exists. + + + SD_BUS_ERROR_UNKNOWN_METHOD + The requested method does not exist in the selected interface. + + + SD_BUS_ERROR_UNKNOWN_OBJECT + The requested object does not exist in the selected service. + + + SD_BUS_ERROR_UNKNOWN_INTERFACE + The requested interface does not exist on the selected object. + + + SD_BUS_ERROR_UNKNOWN_PROPERTY + The requested property does not exist in the selected interface. + + + SD_BUS_ERROR_PROPERTY_READ_ONLY + A write operation was requested on a read-only property. + + + SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN + The requested PID is not known. + + + SD_BUS_ERROR_INVALID_SIGNATURE + The specified message signature is not + valid. + + + + SD_BUS_ERROR_INCONSISTENT_MESSAGE + The passed message does not validate + correctly. + + + SD_BUS_ERROR_MATCH_RULE_NOT_FOUND + The specified match rule does not exist. + + + SD_BUS_ERROR_MATCH_RULE_INVALID + The specified match rule is invalid. + + + SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED + Access to the requested operation is not + permitted. However, it might be available after interactive + authentication. This is usually returned by method calls + supporting a framework for additional interactive + authorization, when interactive authorization was not enabled + with the + sd_bus_message_set_allow_interactive_authorization3 + for the method call message. + + + + + + Notes + + The various error definitions described here are available + as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_error3, + sd_bus_message_set_allow_interactive_authorization3, + errno3, + strerror3 + + + + diff --git a/src/libsystemd/sd-bus.xml b/src/libsystemd/sd-bus.xml new file mode 100644 index 0000000000..336dd33ea0 --- /dev/null +++ b/src/libsystemd/sd-bus.xml @@ -0,0 +1,123 @@ + + + + + + + + + sd-bus + systemd + + + + Documentation + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd-bus + 3 + + + + sd-bus + A lightweight D-Bus and kdbus client library + + + + + #include <systemd/sd-bus.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-bus.h provides an implementation + of a D-Bus client. It can interoperate both with the traditional + dbus-daemon1, + and with kdbus. See + + for more information about the big picture. + + + + Interfaces described here have not been declared stable yet, + and are not accessible from libsystemd.so. + This documentation is provided in hope it might be useful for + developers, without any guarantees of availability or stability. + + + + See + sd_bus_default3, + sd_bus_new3, + sd_bus_request_name3, + sd_bus_start3, + sd_bus_message_append3, + sd_bus_message_append_basic3, + sd_bus_message_append_array3, + sd_bus_message_append_string_memfd3, + sd_bus_message_append_strv3, + sd_bus_message_can_send3, + sd_bus_message_get_cookie3, + sd_bus_message_get_monotonic_usec3, + sd_bus_send3, + sd_bus_set_address3, + sd_bus_set_description3, + sd_bus_set_prepare3, + sd_bus_creds_get_pid3, + sd_bus_creds_new_from_pid3, + sd_bus_get_name_creds3, + sd_bus_get_owner_creds3, + sd_bus_negotiate_fds3, + sd_bus_path_encode3, + sd-bus-errors3, + sd_bus_error3, + sd_bus_error_add_map3, + sd_bus_set_allow_interactive_authorization3 + for more information about the functions available. + + + + + + See Also + + systemd1, + sd-event3, + dbus-daemon1, + dbus-send1, + gdbus + + + + diff --git a/src/libsystemd/sd-bus/DIFFERENCES b/src/libsystemd/sd-bus/DIFFERENCES deleted file mode 100644 index db269675a7..0000000000 --- a/src/libsystemd/sd-bus/DIFFERENCES +++ /dev/null @@ -1,25 +0,0 @@ -Known differences between dbus1 and kdbus: - -- NameAcquired/NameLost is gone entirely on kdbus backends if - libsystemd is used. It is still added in by systemd-bus-proxyd - for old dbus1 clients, and it is available if libsystemd is used - against the classic dbus1 daemon. If you want to write compatible - code with libsystem-bus you need to explicitly subscribe to - NameOwnerChanged signals and just ignore NameAcquired/NameLost - -- Applications have to deal with spurious signals they didn't expect, - due to the probabilistic bloom filters. They need to handle this - anyway, given that any client can send anything to arbitrary clients - anyway, even in dbus1, so not much changes. - -- clients of the system bus when kdbus is used must roll their own - security. Only legacy dbus1 clients get the old XML policy enforced, - which is implemented by systemd-bus-proxyd. - -- Serial numbers of synthesized messages are always (uint32_t) -1. - -- NameOwnerChanged is a synthetic message, generated locally and not - by the driver. On dbus1 only the Disconnected message was - synthesized like this. - -- There's no standard per-session bus anymore. Only a per-user bus. diff --git a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION deleted file mode 100644 index 6aeb11364a..0000000000 --- a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION +++ /dev/null @@ -1,110 +0,0 @@ -How we use GVariant for serializing D-Bus messages --------------------------------------------------- - -We stay close to the original dbus1 framing as possible, but make -certain changes to adapt for GVariant. dbus1 has the following -framing: - - 1. A fixed header of "yyyyuu" - 2. Additional header fields of "a(yv)" - 3. Padding with NUL bytes to pad up to next 8byte boundary - 4. The body - -Note that the body is not padded at the end, the complete message -hence might have a non-aligned size. Reading multiple messages at once -will hence result in possibly unaligned messages in memory. - -The header consists of the following: - - y Endianness, 'l' or 'B' - y Message Type - y Flags - y Protocol version, '1' - u Length of the body, i.e. the length of part 4 above - u 32bit Serial number - - = 12 bytes - -This header is then followed by the fields array, whose first value is -a 32bit array size. - -When using GVariant we keep the basic structure in place, only -slightly alter the header, and define protocol version '2'. The new -header: - - y Endianness, 'l' or 'B' - y Message Type - y Flags - y Protocol version, '2' - u Reserved, must be 0 - t 64bit Cookie - - = 16 bytes - -This is then followed by the GVariant fields array ("a{tv}"), and -finally the actual body as variant (v). Putting this altogether a -packet on dbus2 hence qualifies as a fully compliant GVariant -structure of (yyyyuta{tv}v). - -For details on gvariant, see: - -https://people.gnome.org/~desrt/gvariant-serialisation.pdf - -Regarding the framing of dbus2, also see: - -https://wiki.gnome.org/Projects/GLib/GDBus/Version2 - -The first four bytes of the header are defined the same way for dbus1 -and dbus2. The first bytes contain the endianess field and the -protocol version, so that the remainder of the message can be safely -made sense of just by looking at the first 32bit. - -Note that the length of the body is no longer included in the header -on dbus2! In fact, the message size must be known in advance, from the -underlying transport in order to parse dbus2 messages, while it is -directly included in dbus1 message headers. This change of semantics -is an effect of GVariant's basic design. - -The serial number has been renamed cookie and has been extended from -32bit to 64bit. It is recommended to avoid the higher 32bit of the -cookie field though, to simplify compatibility with dbus1 peers. Note -that not only the cookie/serial field in the fixed header, but also -the reply_cookie/reply_serial additional header field has been -increased from 32bit to 64bit, too! - -The header field identifiers have been extended from 8bit to -64bit. This has been done to simplify things (as kdbus otherwise uses -exclusively 64bit types, unless there is a strong reason not to), and -has no effect on the serialization size, as due to alignment for each -8bit header field identifier 56 bits of padding had to be added. - -Note that the header size changed, due to these changes. However, -consider that on dbus1 the beginning of the fields array contains the -32bit array size (since that is how arrays are encoded on dbus1), -thus, if one considers that size part of the header, instead of the -array, the size of the header on dbus1 and dbus2 stays identical, at -16 bytes. - - 0 4 8 12 16 - Common: | E | T | F | V | ... - - dbus1: | (as above) | Body Length | Serial | Fields Length | Fields array ... - - gvariant: | (as above) | Reserved | Cookie | Fields array ... - -And that's already it. - -Note: to simplify parsing, valid kdbus/dbus2 messages must include the -entire fixed header and additional header fields in a single non-memfd -message part. Also, the signature string of the body variant all the -way to the end of the message must be in a single non-memfd part -too. The parts for this extended header and footer can be the same -one, and can also continue any amount of additional body bytes. - -Note: on kdbus only native endian messages marshalled in gvariant may - be sent. If a client receives a message in non-native endianness - or in dbus1 marshalling it shall ignore the message. - -Note: The GVariant "MAYBE" type is not supported, so that messages can - be fully converted forth and back between dbus1 and gvariant - representations. diff --git a/src/libsystemd/sd-bus/Makefile b/src/libsystemd/sd-bus/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-bus/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-bus/PORTING-DBUS1 b/src/libsystemd/sd-bus/PORTING-DBUS1 deleted file mode 100644 index 2dedb28bcf..0000000000 --- a/src/libsystemd/sd-bus/PORTING-DBUS1 +++ /dev/null @@ -1,535 +0,0 @@ -A few hints on supporting kdbus as backend in your favorite D-Bus library. - -~~~ - -Before you read this, have a look at the DIFFERENCES and -GVARIANT_SERIALIZATION texts you find in the same directory where you -found this. - -We invite you to port your favorite D-Bus protocol implementation -over to kdbus. However, there are a couple of complexities -involved. On kdbus we only speak GVariant marshaling, kdbus clients -ignore traffic in dbus1 marshaling. Thus, you need to add a second, -GVariant compatible marshaler to your library first. - -After you have done that: here's the basic principle how kdbus works: - -You connect to a bus by opening its bus node in /sys/fs/kdbus/. All -buses have a device node there, it starts with a numeric UID of the -owner of the bus, followed by a dash and a string identifying the -bus. The system bus is thus called /sys/fs/kdbus/0-system, and for user -buses the device node is /sys/fs/kdbus/1000-user (if 1000 is your user -id). - -(Before we proceed, please always keep a copy of libsystemd next -to you, ultimately that's where the details are, this document simply -is a rough overview to help you grok things.) - -CONNECTING - -To connect to a bus, simply open() its device node and issue the -KDBUS_CMD_HELLO call. That's it. Now you are connected. Do not send -Hello messages or so (as you would on dbus1), that does not exist for -kdbus. - -The structure you pass to the ioctl will contain a couple of -parameters that you need to know, to operate on the bus. - -There are two flags fields, one indicating features of the kdbus -kernel side ("conn_flags"), the other one ("bus_flags") indicating -features of the bus owner (i.e. systemd). Both flags fields are 64bit -in width. - -When calling into the ioctl, you need to place your own supported -feature bits into these fields. This tells the kernel about the -features you support. When the ioctl returns, it will contain the -features the kernel supports. - -If any of the higher 32bit are set on the two flags fields and your -client does not know what they mean, it must disconnect. The upper -32bit are used to indicate "incompatible" feature additions on the bus -system, the lower 32bit indicate "compatible" feature additions. A -client that does not support a "compatible" feature addition can go on -communicating with the bus, however a client that does not support an -"incompatible" feature must not proceed with the connection. When a -client encountes such an "incompatible" feature it should immediately -try the next bus address configured in the bus address string. - -The hello structure also contains another flags field "attach_flags" -which indicates metadata that is optionally attached to all incoming -messages. You probably want to set KDBUS_ATTACH_NAMES unconditionally -in it. This has the effect that all well-known names of a sender are -attached to all incoming messages. You need this information to -implement matches that match on a message sender name correctly. Of -course, you should only request the attachment of as little metadata -fields as you need. - -The kernel will return in the "id" field your unique id. This is a -simple numeric value. For compatibility with classic dbus1 simply -format this as string and prefix ":1.". - -The kernel will also return the bloom filter size and bloom filter -hash function number used for the signal broadcast bloom filter (see -below). - -The kernel will also return the bus ID of the bus in a 128bit field. - -The pool size field specifies the size of the memory mapped buffer. -After the calling the hello ioctl, you should memory map the kdbus -fd. In this memory mapped region, the kernel will place all your incoming -messages. - -SENDING MESSAGES - -Use the MSG_SEND ioctl to send a message to another peer. The ioctl -takes a structure that contains a variety of fields: - -The flags field corresponds closely to the old dbus1 message header -flags field, though the DONT_EXPECT_REPLY field got inverted into -EXPECT_REPLY. - -The dst_id/src_id field contains the unique id of the destination and -the sender. The sender field is overridden by the kernel usually, hence -you shouldn't fill it in. The destination field can also take the -special value KDBUS_DST_ID_BROADCAST for broadcast messages. For -messages intended to a well-known name set the field to -KDBUS_DST_ID_NAME, and attach the name in a special "items" entry to -the message (see below). - -The payload field indicates the payload. For all dbus traffic it -should carry the value 0x4442757344427573ULL. (Which encodes -'DBusDBus'). - -The cookie field corresponds with the "serial" field of classic -dbus1. We simply renamed it here (and extended it to 64bit) since we -didn't want to imply the monotonicity of the assignment the way the -word "serial" indicates it. - -When sending a message that expects a reply, you need to set the -EXPECT_REPLY flag in the message flag field. In this case you should -also fill out the "timeout_ns" value which indicates the timeout in -nsec for this call. If the peer does not respond in this time you will -get a notification of a timeout. Note that this is also used for -security purposes: a single reply messages is only allowed through the -bus as long as the timeout has not ended. With this timeout value you -hence "open a time window" in which the peer might respond to your -request and the policy allows the response to go through. - -When sending a message that is a reply, you need to fill in the -cookie_reply field, which is similar to the reply_serial field of -dbus1. Note that a message cannot have EXPECT_REPLY and a reply_serial -at the same time! - -This pretty much explains the ioctl header. The actual payload of the -data is now referenced in additional items that are attached to this -ioctl header structure at the end. When sending a message, you attach -items of the type PAYLOAD_VEC, PAYLOAD_MEMFD, FDS, BLOOM_FILTER, -DST_NAME to it: - - KDBUS_ITEM_PAYLOAD_VEC: contains a pointer + length pair for - referencing arbitrary user memory. This is how you reference most - of your data. It's a lot like the good old iovec structure of glibc. - - KDBUS_ITEM_PAYLOAD_MEMFD: for large data blocks it is preferable - to send prepared "memfds" (see below) over. This item contains an - fd for a memfd plus a size. - - KDBUS_ITEM_FDS: for sending over fds attach an item of this type with - an array of fds. - - KDBUS_ITEM_BLOOM_FILTER: the calculated bloom filter of this message, - only for undirected (broadcast) message. - - KDBUS_ITEM_DST_NAME: for messages that are directed to a well-known - name (instead of a unique name), this item contains the well-known - name field. - -A single message may consists of no, one or more payload items of type -PAYLOAD_VEC or PAYLOAD_MEMFD. D-Bus protocol implementations should -treat them as a single block that just happens to be split up into -multiple items. Some restrictions apply however: - - The message header in its entirety must be contained in a single - PAYLOAD_VEC item. - - You may only split your message up right in front of each GVariant - contained in the payload, as well is immediately before framing of a - Gvariant, as well after as any padding bytes if there are any. The - padding bytes must be wholly contained in the preceding - PAYLOAD_VEC/PAYLOAD_MEMFD item. You may not split up basic types - nor arrays of fixed types. The latter is necessary to allow APIs - to return direct pointers to linear arrays of numeric - values. Examples: The basic types "u", "s", "t" have to be in the - same payload item. The array of fixed types "ay", "ai" have to be - fully in contained in the same payload item. For an array "as" or - "a(si)" the only restriction however is to keep each string - individually in an uninterrupted item, to keep the framing of each - element and the array in a single uninterrupted item, however the - various strings might end up in different items. - -Note again, that splitting up messages into separate items is up to the -implementation. Also note that the kdbus kernel side might merge -separate items if it deems this to be useful. However, the order in -which items are contained in the message is left untouched. - -PAYLOAD_MEMFD items allow zero-copy data transfer (see below regarding -the memfd concept). Note however that the overhead of mapping these -makes them relatively expensive, and only worth the trouble for memory -blocks > 512K (this value appears to be quite universal across -architectures, as we tested). Thus we recommend sending PAYLOAD_VEC -items over for small messages and restore to PAYLOAD_MEMFD items for -messages > 512K. Since while building up the message you might not -know yet whether it will grow beyond this boundary a good approach is -to simply build the message unconditionally in a memfd -object. However, when the message is sealed to be sent away check for -the size limit. If the size of the message is < 512K, then simply send -the data as PAYLOAD_VEC and reuse the memfd. If it is >= 512K, seal -the memfd and send it as PAYLOAD_MEMFD, and allocate a new memfd for -the next message. - -RECEIVING MESSAGES - -Use the MSG_RECV ioctl to read a message from kdbus. This will return -an offset into the pool memory map, relative to its beginning. - -The received message structure more or less follows the structure of -the message originally sent. However, certain changes have been -made. In the header the src_id field will be filled in. - -The payload items might have gotten merged and PAYLOAD_VEC items are -not used. Instead, you will only find PAYLOAD_OFF and PAYLOAD_MEMFD -items. The former contain an offset and size into your memory mapped -pool where you find the payload. - -If during the HELLO ioctl you asked for getting metadata attached to -your message, you will find additional KDBUS_ITEM_CREDS, -KDBUS_ITEM_PID_COMM, KDBUS_ITEM_TID_COMM, KDBUS_ITEM_TIMESTAMP, -KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP, -KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT items that -contain this metadata. This metadata will be gathered from the sender -at the point in time it sends the message. This information is -uncached, and since it is appended by the kernel, trustable. The -KDBUS_ITEM_SECLABEL item usually contains the SELinux security label, -if it is used. - -After processing the message you need to call the KDBUS_CMD_FREE -ioctl, which releases the message from the pool, and allows the kernel -to store another message there. Note that the memory used by the pool -is ordinary anonymous, swappable memory that is backed by tmpfs. Hence -there is no need to copy the message out of it quickly, instead you -can just leave it there as long as you need it and release it via the -FREE ioctl only after that's done. - -BLOOM FILTERS - -The kernel does not understand dbus marshaling, it will not look into -the message payload. To allow clients to subscribe to specific subsets -of the broadcast matches we employ bloom filters. - -When broadcasting messages, a bloom filter needs to be attached to the -message in a KDBUS_ITEM_BLOOM item (and only for broadcasting -messages!). If you don't know what bloom filters are, read up now on -Wikipedia. In short: they are a very efficient way how to -probabilistically check whether a certain word is contained in a -vocabulary. It knows no false negatives, but it does know false -positives. - -The parameters for the bloom filters that need to be included in -broadcast message is communicated to userspace as part of the hello -response structure (see above). By default it has the parameters m=512 -(bits in the filter), k=8 (nr of hash functions). Note however, that -this is subject to change in later versions, and userspace -implementations must be capable of handling m values between at least -m=8 and m=2^32, and k values between at least k=1 and k=32. The -underlying hash function is SipHash-2-4. It is used with a number of -constant (yet originally randomly generated) 128bit hash keys, more -specifically: - - b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15, - aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b, - 63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8, - 23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5, - 56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10, - 31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29, - 7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d, - f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35 - -When calculating the first bit index into the bloom filter, the -SipHash-2-4 hash value is calculated for the input data and the first -16 bytes of the array above as hash key. Of the resulting 8 bytes of -output, as many full bytes are taken for the bit index as necessary, -starting from the output's first byte. For the second bit index the -same hash value is used, continuing with the next unused output byte, -and so on. Each time the bytes returned by the hash function are -depleted it is recalculated with the next 16 byte hash key from the -array above and the same input data. - -For each message to send across the bus we populate the bloom filter -with all possible matchable strings. If a client then wants to -subscribe to messages of this type, it simply tells the kernel to test -its own calculated bit mask against the bloom filter of each message. - -More specifically, the following strings are added to the bloom filter -of each message that is broadcasted: - - The string "interface:" suffixed by the interface name - - The string "member:" suffixed by the member name - - The string "path:" suffixed by the path name - - The string "path-slash-prefix:" suffixed with the path name, and - also all prefixes of the path name (cut off at "/"), also prefixed - with "path-slash-prefix". - - The string "message-type:" suffixed with the strings "signal", - "method_call", "error" or "method_return" for the respective message - type of the message. - - If the first argument of the message is a string, "arg0:" suffixed - with the first argument. - - If the first argument of the message is a string, "arg0-dot-prefix" - suffixed with the first argument, and also all prefixes of the - argument (cut off at "."), also prefixed with "arg0-dot-prefix". - - If the first argument of the message is a string, - "arg0-slash-prefix" suffixed with the first argument, and also all - prefixes of the argument (cut off at "/"), also prefixed with - "arg0-slash-prefix". - - Similar for all further arguments that are strings up to 63, for the - arguments and their "dot" and "slash" prefixes. On the first - argument that is not a string, addition to the bloom filter should be - stopped however. - -(Note that the bloom filter does not contain sender nor receiver -names!) - -When a client wants to subscribe to messages matching a certain -expression, it should calculate the bloom mask following the same -algorithm. The kernel will then simply test the mask against the -attached bloom filters. - -Note that bloom filters are probabilistic, which means that clients -might get messages they did not expect. Your bus protocol -implementation must be capable of dealing with these unexpected -messages (which it needs to anyway, given that transfers are -relatively unrestricted on kdbus and people can send you all kinds of -non-sense). - -If a client connects to a bus whose bloom filter metrics (i.e. filter -size and number of hash functions) are outside of the range the client -supports it must immediately disconnect and continue connection with -the next bus address of the bus connection string. - -INSTALLING MATCHES - -To install matches for broadcast messages, use the KDBUS_CMD_ADD_MATCH -ioctl. It takes a structure that contains an encoded match expression, -and that is followed by one or more items, which are combined in an -AND way. (Meaning: a message is matched exactly when all items -attached to the original ioctl struct match). - -To match against other user messages add a KDBUS_ITEM_BLOOM item in -the match (see above). Note that the bloom filter does not include -matches to the sender names. To additionally check against sender -names, use the KDBUS_ITEM_ID (for unique id matches) and -KDBUS_ITEM_NAME (for well-known name matches) item types. - -To match against kernel generated messages (see below) you should add -items of the same type as the kernel messages include, -i.e. KDBUS_ITEM_NAME_ADD, KDBUS_ITEM_NAME_REMOVE, -KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, KDBUS_ITEM_ID_REMOVE and -fill them out. Note however, that you have some wildcards in this -case, for example the .id field of KDBUS_ITEM_ID_ADD/KDBUS_ITEM_ID_REMOVE -structures may be set to 0 to match against any id addition/removal. - -Note that dbus match strings do no map 1:1 to these ioctl() calls. In -many cases (where the match string is "underspecified") you might need -to issue up to six different ioctl() calls for the same match. For -example, the empty match (which matches against all messages), would -translate into one KDBUS_ITEM_BLOOM ioctl, one KDBUS_ITEM_NAME_ADD, -one KDBUS_ITEM_NAME_CHANGE, one KDBUS_ITEM_NAME_REMOVE, one -KDBUS_ITEM_ID_ADD and one KDBUS_ITEM_ID_REMOVE. - -When creating a match, you may attach a "cookie" value to them, which -is used for deleting this match again. The cookie can be selected freely -by the client. When issuing KDBUS_CMD_REMOVE_MATCH, simply pass the -same cookie as before and all matches matching the same "cookie" value -will be removed. This is particularly handy for the case where multiple -ioctl()s are added for a single match strings. - -MEMFDS - -memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items -attached to messages. If this is done, the data included in the memfd -is considered part of the payload stream of a message, and are treated -the same way as KDBUS_ITEM_PAYLOAD_VEC by the receiving side. It is -possible to interleave KDBUS_ITEM_PAYLOAD_MEMFD and -KDBUS_ITEM_PAYLOAD_VEC items freely, by the reader they will be -considered a single stream of bytes in the order these items appear in -the message, that just happens to be split up at various places -(regarding rules how they may be split up, see above). The kernel will -refuse taking KDBUS_ITEM_PAYLOAD_MEMFD items that refer to memfds that -are not sealed. - -Note that sealed memfds may be unsealed again if they are not mapped -you have the only fd reference to them. - -Alternatively to sending memfds as KDBUS_ITEM_PAYLOAD_MEMFD items -(where they are just a part of the payload stream of a message) you can -also simply attach any memfd to a message using -KDBUS_ITEM_PAYLOAD_FDS. In this case, the memfd contents is not -considered part of the payload stream of the message, but simply fds -like any other, that happen to be attached to the message. - -MESSAGES FROM THE KERNEL - -A couple of messages previously generated by the dbus1 bus driver are -now generated by the kernel. Since the kernel does not understand the -payload marshaling, they are generated by the kernel in a different -format. This is indicated with the "payload type" field of the -messages set to 0. Library implementations should take these messages -and synthesize traditional driver messages for them on reception. - -More specifically: - - Instead of the NameOwnerChanged, NameLost, NameAcquired signals - there are kernel messages containing KDBUS_ITEM_NAME_ADD, - KDBUS_ITEM_NAME_REMOVE, KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, - KDBUS_ITEM_ID_REMOVE items are generated (each message will contain - exactly one of these items). Note that in libsystemd we have - obsoleted NameLost/NameAcquired messages, since they are entirely - redundant to NameOwnerChanged. This library will hence only - synthesize NameOwnerChanged messages from these kernel messages, - and never generate NameLost/NameAcquired. If your library needs to - stay compatible to the old dbus1 userspace, you possibly might need - to synthesize both a NameOwnerChanged and NameLost/NameAcquired - message from the same kernel message. - - When a method call times out, a KDBUS_ITEM_REPLY_TIMEOUT message is - generated. This should be synthesized into a method error reply - message to the original call. - - When a method call fails because the peer terminated the connection - before responding, a KDBUS_ITEM_REPLY_DEAD message is - generated. Similarly, it should be synthesized into a method error - reply message. - -For synthesized messages we recommend setting the cookie field to -(uint32_t) -1 (and not (uint64_t) -1!), so that the cookie is not 0 -(which the dbus1 spec does not allow), but clearly recognizable as -synthetic. - -Note that the KDBUS_ITEM_NAME_XYZ messages will actually inform you -about all kinds of names, including activatable ones. Classic dbus1 -NameOwnerChanged messages OTOH are only generated when a name is -really acquired on the bus and not just simply activatable. This means -you must explicitly check for the case where an activatable name -becomes acquired or an acquired name is lost and returns to be -activatable. - -NAME REGISTRY - -To acquire names on the bus, use the KDBUS_CMD_NAME_ACQUIRE ioctl(). It -takes a flags field similar to dbus1's RequestName() bus driver call, -however the NO_QUEUE flag got inverted into a QUEUE flag instead. - -To release a previously acquired name use the KDBUS_CMD_NAME_RELEASE -ioctl(). - -To list acquired names use the KDBUS_CMD_CONN_INFO ioctl. It may be -used to list unique names, well known names as well as activatable -names and clients currently queuing for ownership of a well-known -name. The ioctl will return an offset into the memory pool. After -reading all the data you need, you need to release this via the -KDBUS_CMD_FREE ioctl(), similar how you release a received message. - -CREDENTIALS - -kdbus can optionally attach various kinds of metadata about the sender at -the point of time of sending ("credentials") to messages, on request -of the receiver. This is both supported on directed and undirected -(broadcast) messages. The metadata to attach is selected at time of -the HELLO ioctl of the receiver via a flags field (see above). Note -that clients must be able to handle that messages contain more -metadata than they asked for themselves, to simplify implementation of -broadcasting in the kernel. The receiver should not rely on this data -to be around though, even though it will be correct if it happens to -be attached. In order to avoid programming errors in applications, we -recommend though not passing this data on to clients that did not -explicitly ask for it. - -Credentials may also be queried for a well-known or unique name. Use -the KDBUS_CMD_CONN_INFO for this. It will return an offset to the pool -area again, which will contain the same credential items as messages -have attached. Note that when issuing the ioctl, you can select a -different set of credentials to gather, than what was originally requested -for being attached to incoming messages. - -Credentials are always specific to the sender's domain that was -current at the time of sending, and of the process that opened the -bus connection at the time of opening it. Note that this latter data -is cached! - -POLICY - -The kernel enforces only very limited policy on names. It will not do -access filtering by userspace payload, and thus not by interface or -method name. - -This ultimately means that most fine-grained policy enforcement needs -to be done by the receiving process. We recommend using PolicyKit for -any more complex checks. However, libraries should make simple static -policy decisions regarding privileged/unprivileged method calls -easy. We recommend doing this by enabling KDBUS_ATTACH_CAPS and -KDBUS_ATTACH_CREDS for incoming messages, and then discerning client -access by some capability, or if sender and receiver UIDs match. - -BUS ADDRESSES - -When connecting to kdbus use the "kernel:" protocol prefix in DBus -address strings. The device node path is encoded in its "path=" -parameter. - -Client libraries should use the following connection string when -connecting to the system bus: - - kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket - -This will ensure that kdbus is preferred over the legacy AF_UNIX -socket, but compatibility is kept. For the user bus use: - - kernel:path=/sys/fs/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus - -With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR -following the XDG basedir spec. - -Of course the $DBUS_SYSTEM_BUS_ADDRESS and $DBUS_SESSION_BUS_ADDRESS -variables should still take precedence. - -DBUS SERVICE FILES - -Activatable services for kdbus may not use classic dbus1 service -activation files. Instead, programs should drop in native systemd -.service and .busname unit files, so that they are treated uniformly -with other types of units and activation of the system. - -Note that this results in a major difference to classic dbus1: -activatable bus names can be established at any time in the boot process. -This is unlike dbus1 where activatable names are unconditionally available -as long as dbus-daemon is running. Being able to control when -activatable names are established is essential to allow usage of kdbus -during early boot and in initrds, without the risk of triggering -services too early. - -DISCLAIMER - -This all is so far just the status quo. We are putting this together, because -we are quite confident that further API changes will be smaller, but -to make this very clear: this is all subject to change, still! - -We invite you to port over your favorite dbus library to this new -scheme, but please be prepared to make minor changes when we still -change these interfaces! diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c deleted file mode 100644 index 112769fcb6..0000000000 --- a/src/libsystemd/sd-bus/bus-bloom.c +++ /dev/null @@ -1,156 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); -} - -static const sd_id128_t hash_keys[] = { - SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15), - SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b), - SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8), - SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5), - SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10), - SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29), - SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d), - SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35), -}; - -static void bloom_add_data( - uint64_t filter[], /* The filter bits */ - size_t size, /* Size of the filter in bytes */ - unsigned k, /* Number of hash functions */ - const void *data, /* Data to hash */ - size_t n) { /* Size of data to hash in bytes */ - - uint64_t h; - uint64_t m; - unsigned w, i, c = 0; - unsigned hash_index; - - assert(size > 0); - assert(k > 0); - - /* Determine bits in filter */ - m = size * 8; - - /* Determine how many bytes we need to generate a bit index 0..m for this filter */ - w = (u64log2(m) + 7) / 8; - - assert(w <= sizeof(uint64_t)); - - /* Make sure we have enough hash keys to generate m * k bits - * of hash value. Note that SipHash24 generates 64 bits of - * hash value for each 128 bits of hash key. */ - assert(k * w <= ELEMENTSOF(hash_keys) * 8); - - for (i = 0, hash_index = 0; i < k; i++) { - uint64_t p = 0; - unsigned d; - - for (d = 0; d < w; d++) { - if (c <= 0) { - h = siphash24(data, n, hash_keys[hash_index++].bytes); - c += 8; - } - - p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; - c--; - } - - p &= m - 1; - set_bit(filter, p); - } - - /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */ -} - -void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b) { - size_t n; - char *c; - - assert(filter); - assert(a); - assert(b); - - n = strlen(a) + 1 + strlen(b); - c = alloca(n + 1); - strcpy(stpcpy(stpcpy(c, a), ":"), b); - - bloom_add_data(filter, size, k, c, n); -} - -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b, char sep) { - size_t n; - char *c, *p; - - assert(filter); - assert(a); - assert(b); - - n = strlen(a) + 1 + strlen(b); - c = alloca(n + 1); - - p = stpcpy(stpcpy(c, a), ":"); - strcpy(p, b); - - bloom_add_data(filter, size, k, c, n); - - for (;;) { - char *e; - - e = strrchr(p, sep); - if (!e) - break; - - *(e + 1) = 0; - bloom_add_data(filter, size, k, c, e - c + 1); - - if (e == p) - break; - - *e = 0; - bloom_add_data(filter, size, k, c, e - c); - } -} - -bool bloom_validate_parameters(size_t size, unsigned k) { - uint64_t m; - unsigned w; - - if (size <= 0) - return false; - - if (k <= 0) - return false; - - m = size * 8; - w = (u64log2(m) + 7) / 8; - if (w > sizeof(uint64_t)) - return false; - - if (k * w > ELEMENTSOF(hash_keys) * 8) - return false; - - return true; -} diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h deleted file mode 100644 index c824622b95..0000000000 --- a/src/libsystemd/sd-bus/bus-bloom.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -/* - * Our default bloom filter has the following parameters: - * - * m=512 (bits in the filter) - * k=8 (hash functions) - * - * We use SipHash24 as hash function with a number of (originally - * randomized) but fixed hash keys. - * - */ - -#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */ -#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */ - -void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b); -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep); - -bool bloom_validate_parameters(size_t size, unsigned n_hash); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c deleted file mode 100644 index 02e3bf904c..0000000000 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ /dev/null @@ -1,87 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "sd-bus.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), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST), - SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO), - SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_JOB, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), - SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), - SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), - SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_USER_FOR_PID, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SEAT, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_NOT_ON_SEAT, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_NOT_IN_CONTROL, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), - SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), - SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), - - SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), - - 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_CNAME_LOOP, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), - 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_NETWORK_DOWN, ENETDOWN), - - SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), - SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), - - SD_BUS_ERROR_MAP_END -}; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h deleted file mode 100644 index c8f369cb78..0000000000 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-error.h" - -#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" -#define BUS_ERROR_NO_UNIT_FOR_PID "org.freedesktop.systemd1.NoUnitForPID" -#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" -#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed" -#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed" -#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" -#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" -#define BUS_ERROR_ALREADY_SUBSCRIBED "org.freedesktop.systemd1.AlreadySubscribed" -#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency" -#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting" -#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" -#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" -#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" -#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" -#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" -#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" -#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" -#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" -#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" - -#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" -#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" -#define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID" -#define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists" -#define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking" -#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping" -#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping" - -#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession" -#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID" -#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser" -#define BUS_ERROR_NO_USER_FOR_PID "org.freedesktop.login1.NoUserForPID" -#define BUS_ERROR_NO_SUCH_SEAT "org.freedesktop.login1.NoSuchSeat" -#define BUS_ERROR_SESSION_NOT_ON_SEAT "org.freedesktop.login1.SessionNotOnSeat" -#define BUS_ERROR_NOT_IN_CONTROL "org.freedesktop.login1.NotInControl" -#define BUS_ERROR_DEVICE_IS_TAKEN "org.freedesktop.login1.DeviceIsTaken" -#define BUS_ERROR_DEVICE_NOT_TAKEN "org.freedesktop.login1.DeviceNotTaken" -#define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress" -#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported" -#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy" - -#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled" - -#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" - -#define BUS_ERROR_NO_NAME_SERVERS "org.freedesktop.resolve1.NoNameServers" -#define BUS_ERROR_INVALID_REPLY "org.freedesktop.resolve1.InvalidReply" -#define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR" -#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" -#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" -#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_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown" -#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." - -#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" -#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" - -BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c deleted file mode 100644 index 3191d27ded..0000000000 --- a/src/libsystemd/sd-bus/bus-container.c +++ /dev/null @@ -1,277 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "bus-container.h" -#include "bus-internal.h" -#include "bus-socket.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 }; - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - pid_t child; - siginfo_t si; - int r, error_buf = 0; - ssize_t n; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->nspid > 0 || b->machine); - - if (b->nspid <= 0) { - r = container_get_leader(b->machine, &b->nspid); - if (r < 0) - return r; - } - - r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); - if (r < 0) - return r; - - b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (b->input_fd < 0) - return -errno; - - b->output_fd = b->input_fd; - - bus_socket_setup(b); - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - pid_t grandchild; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); - if (r < 0) - _exit(EXIT_FAILURE); - - /* We just changed PID namespace, however it will only - * take effect on the children we now fork. Hence, - * let's fork another time, and connect from this - * grandchild, so that SO_PEERCRED of our connection - * comes from a process from within the container, and - * not outside of it */ - - grandchild = fork(); - if (grandchild < 0) - _exit(EXIT_FAILURE); - - if (grandchild == 0) { - - r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); - if (r < 0) { - /* Try to send error up */ - error_buf = errno; - (void) write(pair[1], &error_buf, sizeof(error_buf)); - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } - - r = wait_for_terminate(grandchild, &si); - if (r < 0) - _exit(EXIT_FAILURE); - - if (si.si_code != CLD_EXITED) - _exit(EXIT_FAILURE); - - _exit(si.si_status); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return r; - - n = read(pair[0], &error_buf, sizeof(error_buf)); - if (n < 0) - return -errno; - - if (n > 0) { - if (n != sizeof(error_buf)) - return -EIO; - - if (error_buf < 0) - return -EIO; - - if (error_buf == EINPROGRESS) - return 1; - - if (error_buf > 0) - return -error_buf; - } - - if (si.si_code != CLD_EXITED) - return -EIO; - - if (si.si_status != EXIT_SUCCESS) - return -EIO; - - return bus_socket_start_auth(b); -} - -int bus_container_connect_kernel(sd_bus *b) { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - int error_buf = 0; - struct iovec iov = { - .iov_base = &error_buf, - .iov_len = sizeof(error_buf), - }; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - struct cmsghdr *cmsg; - pid_t child; - siginfo_t si; - int r, fd = -1; - ssize_t n; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->nspid > 0 || b->machine); - - if (b->nspid <= 0) { - r = container_get_leader(b->machine, &b->nspid); - if (r < 0) - return r; - } - - r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - pid_t grandchild; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); - if (r < 0) - _exit(EXIT_FAILURE); - - /* We just changed PID namespace, however it will only - * take effect on the children we now fork. Hence, - * let's fork another time, and connect from this - * grandchild, so that kdbus only sees the credentials - * of this process which comes from within the - * container, and not outside of it */ - - grandchild = fork(); - if (grandchild < 0) - _exit(EXIT_FAILURE); - - if (grandchild == 0) { - fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - /* Try to send error up */ - error_buf = errno; - (void) write(pair[1], &error_buf, sizeof(error_buf)); - _exit(EXIT_FAILURE); - } - - r = send_one_fd(pair[1], fd, 0); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - r = wait_for_terminate(grandchild, &si); - if (r < 0) - _exit(EXIT_FAILURE); - - if (si.si_code != CLD_EXITED) - _exit(EXIT_FAILURE); - - _exit(si.si_status); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return r; - - n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); - if (n < 0) - return -errno; - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - int *fds; - unsigned n_fds; - - assert(fd < 0); - - 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; - } - - fd = fds[0]; - } - } - - /* If there's an fd passed, we are good. */ - if (fd >= 0) { - b->input_fd = b->output_fd = fd; - return bus_kernel_take_fd(b); - } - - /* If there's an error passed, use it */ - if (n == sizeof(error_buf) && error_buf > 0) - return -error_buf; - - /* Otherwise, we have no clue */ - return -EIO; -} diff --git a/src/libsystemd/sd-bus/bus-container.h b/src/libsystemd/sd-bus/bus-container.h deleted file mode 100644 index 509ef45624..0000000000 --- a/src/libsystemd/sd-bus/bus-container.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -int bus_container_connect_socket(sd_bus *b); -int bus_container_connect_kernel(sd_bus *b); diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c deleted file mode 100644 index 52128e7b5c..0000000000 --- a/src/libsystemd/sd-bus/bus-control.c +++ /dev/null @@ -1,1588 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif - -#include -#include - -#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-util.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; - - assert_return(bus, -EINVAL); - assert_return(unique, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->bus_client) - return -EINVAL; - - r = bus_ensure_running(bus); - if (r < 0) - return r; - - *unique = bus->unique_name; - return 0; -} - -static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags) { - struct kdbus_cmd *n; - size_t size, l; - int r; - - assert(bus); - assert(name); - - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); - n = alloca0_align(size, 8); - n->size = size; - n->flags = request_name_flags_to_kdbus(flags); - - n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - n->items[0].type = KDBUS_ITEM_NAME; - memcpy(n->items[0].str, name, l); - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(n, n->size); -#endif - - r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n); - if (r < 0) - return -errno; - - if (n->return_flags & KDBUS_NAME_IN_QUEUE) - return 0; - - return 1; -} - -static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - uint32_t ret, param = 0; - int r; - - assert(bus); - assert(name); - - if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) - param |= BUS_NAME_ALLOW_REPLACEMENT; - if (flags & SD_BUS_NAME_REPLACE_EXISTING) - param |= BUS_NAME_REPLACE_EXISTING; - if (!(flags & SD_BUS_NAME_QUEUE)) - param |= BUS_NAME_DO_NOT_QUEUE; - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "RequestName", - NULL, - &reply, - "su", - name, - param); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "u", &ret); - if (r < 0) - return r; - - if (ret == BUS_NAME_ALREADY_OWNER) - return -EALREADY; - else if (ret == BUS_NAME_EXISTS) - return -EEXIST; - else if (ret == BUS_NAME_IN_QUEUE) - return 0; - else if (ret == BUS_NAME_PRIMARY_OWNER) - return 1; - - return -EIO; -} - -_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { - assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); - assert_return(service_name_is_valid(name), -EINVAL); - assert_return(name[0] != ':', -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Don't allow requesting the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_request_name_kernel(bus, name, flags); - else - return bus_request_name_dbus1(bus, name, flags); -} - -static int bus_release_name_kernel(sd_bus *bus, const char *name) { - struct kdbus_cmd *n; - size_t size, l; - int r; - - assert(bus); - assert(name); - - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); - n = alloca0_align(size, 8); - n->size = size; - - n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - n->items[0].type = KDBUS_ITEM_NAME; - memcpy(n->items[0].str, name, l); - -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(n, n->size); -#endif - r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n); - if (r < 0) - return -errno; - - return 0; -} - -static int bus_release_name_dbus1(sd_bus *bus, const char *name) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - uint32_t ret; - int r; - - assert(bus); - assert(name); - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ReleaseName", - NULL, - &reply, - "s", - name); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "u", &ret); - if (r < 0) - return r; - if (ret == BUS_NAME_NON_EXISTENT) - return -ESRCH; - if (ret == BUS_NAME_NOT_OWNER) - return -EADDRINUSE; - if (ret == BUS_NAME_RELEASED) - return 0; - - return -EINVAL; -} - -_public_ int sd_bus_release_name(sd_bus *bus, const char *name) { - assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - assert_return(name[0] != ':', -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Don't allow releasing the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_release_name_kernel(bus, name); - else - return bus_release_name_dbus1(bus, name); -} - -static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { - struct kdbus_cmd_list cmd = { - .size = sizeof(cmd), - .flags = flags, - }; - struct kdbus_info *name_list, *name; - uint64_t previous_id = 0; - int r; - - /* Caller will free half-constructed list on failure... */ - - r = ioctl(bus->input_fd, KDBUS_CMD_LIST, &cmd); - if (r < 0) - return -errno; - - name_list = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); - - KDBUS_FOREACH(name, name_list, cmd.list_size) { - struct kdbus_item *item; - - if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { - char *n; - - if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { - r = -ENOMEM; - goto fail; - } - - r = strv_consume(x, n); - if (r < 0) - goto fail; - - previous_id = name->id; - } - - KDBUS_ITEM_FOREACH(item, name, items) { - if (item->type == KDBUS_ITEM_OWNED_NAME) { - if (service_name_is_valid(item->name.name)) { - r = strv_extend(x, item->name.name); - if (r < 0) { - r = -ENOMEM; - goto fail; - } - } - } - } - } - - r = 0; - -fail: - bus_kernel_cmd_free(bus, cmd.offset); - return r; -} - -static int bus_list_names_kernel(sd_bus *bus, char ***acquired, char ***activatable) { - _cleanup_strv_free_ char **x = NULL, **y = NULL; - int r; - - if (acquired) { - r = kernel_get_list(bus, KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES, &x); - if (r < 0) - return r; - } - - if (activatable) { - r = kernel_get_list(bus, KDBUS_LIST_ACTIVATORS, &y); - if (r < 0) - return r; - - *activatable = y; - y = NULL; - } - - if (acquired) { - *acquired = x; - x = NULL; - } - - return 0; -} - -static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_strv_free_ char **x = NULL, **y = NULL; - int r; - - if (acquired) { - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListNames", - NULL, - &reply, - NULL); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(reply, &x); - if (r < 0) - return r; - - reply = sd_bus_message_unref(reply); - } - - if (activatable) { - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListActivatableNames", - NULL, - &reply, - NULL); - if (r < 0) - return r; - - r = sd_bus_message_read_strv(reply, &y); - if (r < 0) - return r; - - *activatable = y; - y = NULL; - } - - if (acquired) { - *acquired = x; - x = NULL; - } - - return 0; -} - -_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { - assert_return(bus, -EINVAL); - assert_return(acquired || activatable, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_list_names_kernel(bus, acquired, activatable); - else - return bus_list_names_dbus1(bus, acquired, activatable); -} - -static int bus_populate_creds_from_items( - sd_bus *bus, - struct kdbus_info *info, - uint64_t mask, - sd_bus_creds *c) { - - struct kdbus_item *item; - uint64_t m; - int r; - - assert(bus); - assert(info); - assert(c); - - KDBUS_ITEM_FOREACH(item, info, items) { - - switch (item->type) { - - case KDBUS_ITEM_PIDS: - - if (mask & SD_BUS_CREDS_PID && item->pids.pid > 0) { - c->pid = (pid_t) item->pids.pid; - c->mask |= SD_BUS_CREDS_PID; - } - - if (mask & SD_BUS_CREDS_TID && item->pids.tid > 0) { - c->tid = (pid_t) item->pids.tid; - c->mask |= SD_BUS_CREDS_TID; - } - - if (mask & SD_BUS_CREDS_PPID) { - if (item->pids.ppid > 0) { - c->ppid = (pid_t) item->pids.ppid; - c->mask |= SD_BUS_CREDS_PPID; - } else if (item->pids.pid == 1) { - /* The structure doesn't - * really distinguish the case - * where a process has no - * parent and where we don't - * know it because it could - * not be translated due to - * namespaces. However, we - * know that PID 1 has no - * parent process, hence let's - * patch that in, manually. */ - c->ppid = 0; - c->mask |= SD_BUS_CREDS_PPID; - } - } - - break; - - case KDBUS_ITEM_CREDS: - - if (mask & SD_BUS_CREDS_UID && (uid_t) item->creds.uid != UID_INVALID) { - c->uid = (uid_t) item->creds.uid; - c->mask |= SD_BUS_CREDS_UID; - } - - if (mask & SD_BUS_CREDS_EUID && (uid_t) item->creds.euid != UID_INVALID) { - c->euid = (uid_t) item->creds.euid; - c->mask |= SD_BUS_CREDS_EUID; - } - - if (mask & SD_BUS_CREDS_SUID && (uid_t) item->creds.suid != UID_INVALID) { - c->suid = (uid_t) item->creds.suid; - c->mask |= SD_BUS_CREDS_SUID; - } - - if (mask & SD_BUS_CREDS_FSUID && (uid_t) item->creds.fsuid != UID_INVALID) { - c->fsuid = (uid_t) item->creds.fsuid; - c->mask |= SD_BUS_CREDS_FSUID; - } - - if (mask & SD_BUS_CREDS_GID && (gid_t) item->creds.gid != GID_INVALID) { - c->gid = (gid_t) item->creds.gid; - c->mask |= SD_BUS_CREDS_GID; - } - - if (mask & SD_BUS_CREDS_EGID && (gid_t) item->creds.egid != GID_INVALID) { - c->egid = (gid_t) item->creds.egid; - c->mask |= SD_BUS_CREDS_EGID; - } - - if (mask & SD_BUS_CREDS_SGID && (gid_t) item->creds.sgid != GID_INVALID) { - c->sgid = (gid_t) item->creds.sgid; - c->mask |= SD_BUS_CREDS_SGID; - } - - if (mask & SD_BUS_CREDS_FSGID && (gid_t) item->creds.fsgid != GID_INVALID) { - c->fsgid = (gid_t) item->creds.fsgid; - c->mask |= SD_BUS_CREDS_FSGID; - } - - break; - - case KDBUS_ITEM_PID_COMM: - if (mask & SD_BUS_CREDS_COMM) { - r = free_and_strdup(&c->comm, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_COMM; - } - break; - - case KDBUS_ITEM_TID_COMM: - if (mask & SD_BUS_CREDS_TID_COMM) { - r = free_and_strdup(&c->tid_comm, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_TID_COMM; - } - break; - - case KDBUS_ITEM_EXE: - if (mask & SD_BUS_CREDS_EXE) { - r = free_and_strdup(&c->exe, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_EXE; - } - break; - - case KDBUS_ITEM_CMDLINE: - if (mask & SD_BUS_CREDS_CMDLINE) { - c->cmdline_size = item->size - offsetof(struct kdbus_item, data); - c->cmdline = memdup(item->data, c->cmdline_size); - if (!c->cmdline) - return -ENOMEM; - - c->mask |= SD_BUS_CREDS_CMDLINE; - } - break; - - case KDBUS_ITEM_CGROUP: - m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT | - SD_BUS_CREDS_USER_UNIT | SD_BUS_CREDS_SLICE | - SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask; - - if (m) { - r = free_and_strdup(&c->cgroup, item->str); - if (r < 0) - return r; - - r = bus_get_root_path(bus); - if (r < 0) - return r; - - r = free_and_strdup(&c->cgroup_root, bus->cgroup_root); - if (r < 0) - return r; - - c->mask |= m; - } - break; - - case KDBUS_ITEM_CAPS: - m = (SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS | - SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_BOUNDING_CAPS) & mask; - - if (m) { - if (item->caps.last_cap != cap_last_cap() || - item->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(item->caps.last_cap, 32U) * 4 * 4) - return -EBADMSG; - - c->capability = memdup(item->caps.caps, item->size - offsetof(struct kdbus_item, caps.caps)); - if (!c->capability) - return -ENOMEM; - - c->mask |= m; - } - break; - - case KDBUS_ITEM_SECLABEL: - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - r = free_and_strdup(&c->label, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - break; - - case KDBUS_ITEM_AUDIT: - if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { - c->audit_session_id = (uint32_t) item->audit.sessionid; - c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } - - if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { - c->audit_login_uid = (uid_t) item->audit.loginuid; - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } - break; - - case KDBUS_ITEM_OWNED_NAME: - if ((mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) && service_name_is_valid(item->name.name)) { - r = strv_extend(&c->well_known_names, item->name.name); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - break; - - case KDBUS_ITEM_CONN_DESCRIPTION: - if (mask & SD_BUS_CREDS_DESCRIPTION) { - r = free_and_strdup(&c->description, item->str); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_DESCRIPTION; - } - break; - - case KDBUS_ITEM_AUXGROUPS: - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - size_t i, n; - uid_t *g; - - n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); - g = new(gid_t, n); - if (!g) - return -ENOMEM; - - for (i = 0; i < n; i++) - g[i] = item->data64[i]; - - free(c->supplementary_gids); - c->supplementary_gids = g; - c->n_supplementary_gids = n; - - c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - break; - } - } - - return 0; -} - -int bus_get_name_creds_kdbus( - sd_bus *bus, - const char *name, - uint64_t mask, - bool allow_activator, - sd_bus_creds **creds) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - struct kdbus_cmd_info *cmd; - struct kdbus_info *conn_info; - size_t size, l; - uint64_t id; - int r; - - if (streq(name, "org.freedesktop.DBus")) - return -EOPNOTSUPP; - - r = bus_kernel_parse_unique_name(name, &id); - if (r < 0) - return r; - if (r > 0) { - size = offsetof(struct kdbus_cmd_info, items); - cmd = alloca0_align(size, 8); - cmd->id = id; - } else { - l = strlen(name) + 1; - size = offsetof(struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(l); - cmd = alloca0_align(size, 8); - cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; - cmd->items[0].type = KDBUS_ITEM_NAME; - memcpy(cmd->items[0].str, name, l); - } - - /* If augmentation is on, and the bus didn't provide us - * the bits we want, then ask for the PID/TID so that we - * can read the rest from /proc. */ - if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_PPID| - SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) - mask |= SD_BUS_CREDS_PID; - - cmd->size = size; - cmd->attach_flags = attach_flags_to_kdbus(mask); - - r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); - if (r < 0) - return -errno; - - conn_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset); - - /* Non-activated names are considered not available */ - if (!allow_activator && (conn_info->flags & KDBUS_HELLO_ACTIVATOR)) { - if (name[0] == ':') - r = -ENXIO; - else - r = -ESRCH; - goto fail; - } - - c = bus_creds_new(); - if (!c) { - r = -ENOMEM; - goto fail; - } - - if (mask & SD_BUS_CREDS_UNIQUE_NAME) { - if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0) { - r = -ENOMEM; - goto fail; - } - - c->mask |= SD_BUS_CREDS_UNIQUE_NAME; - } - - /* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of - them in case the service has no names. This does not mean - however that the list of owned names could not be - acquired. Hence, let's explicitly clarify that the data is - complete. */ - c->mask |= mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; - - r = bus_populate_creds_from_items(bus, conn_info, mask, c); - if (r < 0) - goto fail; - - r = bus_creds_add_more(c, mask, 0, 0); - if (r < 0) - goto fail; - - if (creds) { - *creds = c; - c = NULL; - } - - r = 0; - -fail: - bus_kernel_cmd_free(bus, cmd->offset); - return r; -} - -static int bus_get_name_creds_dbus1( - sd_bus *bus, - const char *name, - uint64_t mask, - sd_bus_creds **creds) { - - _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; - - /* Only query the owner if the caller wants to know it or if - * the caller just wants to check whether a name exists */ - if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) { - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetNameOwner", - NULL, - &reply_unique, - "s", - name); - if (r < 0) - return r; - - r = sd_bus_message_read(reply_unique, "s", &unique); - if (r < 0) - return r; - } - - if (mask != 0) { - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) { - c->unique_name = strdup(unique); - if (!c->unique_name) - return -ENOMEM; - - c->mask |= SD_BUS_CREDS_UNIQUE_NAME; - } - - if ((mask & SD_BUS_CREDS_PID) || - ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)))) { - - uint32_t u; - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixProcessID", - NULL, - &reply, - "s", - unique ? unique : name); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "u", &u); - if (r < 0) - return r; - - pid = u; - if (mask & SD_BUS_CREDS_PID) { - c->pid = u; - c->mask |= SD_BUS_CREDS_PID; - } - - reply = sd_bus_message_unref(reply); - } - - if (mask & SD_BUS_CREDS_EUID) { - uint32_t u; - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixUser", - NULL, - &reply, - "s", - unique ? unique : name); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "u", &u); - if (r < 0) - return r; - - c->euid = u; - c->mask |= SD_BUS_CREDS_EUID; - - reply = sd_bus_message_unref(reply); - } - - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const void *p = NULL; - size_t sz = 0; - - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionSELinuxSecurityContext", - &error, - &reply, - "s", - unique ? unique : name); - if (r < 0) { - if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) - return r; - } else { - r = sd_bus_message_read_array(reply, 'y', &p, &sz); - if (r < 0) - return r; - - c->label = strndup(p, sz); - if (!c->label) - return -ENOMEM; - - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - } - - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0) - return r; - } - - if (creds) { - *creds = c; - c = NULL; - } - - return 0; -} - -_public_ int sd_bus_get_name_creds( - sd_bus *bus, - const char *name, - uint64_t mask, - sd_bus_creds **creds) { - - assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(mask == 0 || creds, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - if (streq(name, "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_get_name_creds_kdbus(bus, name, mask, false, creds); - else - return bus_get_name_creds_dbus1(bus, name, mask, creds); -} - -static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - struct kdbus_cmd_info cmd = { - .size = sizeof(struct kdbus_cmd_info), - }; - struct kdbus_info *creator_info; - pid_t pid = 0; - int r; - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - /* If augmentation is on, and the bus doesn't didn't allow us - * to get the bits we want, then ask for the PID/TID so that we - * can read the rest from /proc. */ - if ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_PPID| - SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) - mask |= SD_BUS_CREDS_PID; - - cmd.attach_flags = attach_flags_to_kdbus(mask); - - r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); - if (r < 0) - return -errno; - - creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); - - r = bus_populate_creds_from_items(bus, creator_info, mask, c); - bus_kernel_cmd_free(bus, cmd.offset); - if (r < 0) - return r; - - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0) - return r; - - *ret = c; - c = NULL; - return 0; -} - -static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - pid_t pid = 0; - bool do_label; - int r; - - 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) - return -ENODATA; - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - if (bus->ucred_valid) { - if (bus->ucred.pid > 0) { - pid = c->pid = bus->ucred.pid; - c->mask |= SD_BUS_CREDS_PID & mask; - } - - if (bus->ucred.uid != UID_INVALID) { - c->euid = bus->ucred.uid; - c->mask |= SD_BUS_CREDS_EUID & mask; - } - - if (bus->ucred.gid != GID_INVALID) { - c->egid = bus->ucred.gid; - c->mask |= SD_BUS_CREDS_EGID & mask; - } - } - - if (do_label) { - c->label = strdup(bus->label); - if (!c->label) - return -ENOMEM; - - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0) - return r; - - *ret = c; - c = NULL; - return 0; -} - -_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - assert_return(bus, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->is_kernel) - return bus_get_owner_creds_kdbus(bus, mask, ret); - else - return bus_get_owner_creds_dbus1(bus, mask, ret); -} - -static int add_name_change_match(sd_bus *bus, - uint64_t cookie, - const char *name, - const char *old_owner, - const char *new_owner) { - - uint64_t name_id = KDBUS_MATCH_ID_ANY, old_owner_id = 0, new_owner_id = 0; - int is_name_id = -1, r; - struct kdbus_item *item; - - assert(bus); - - /* If we encounter a match that could match against - * NameOwnerChanged messages, then we need to create - * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} and - * KDBUS_ITEM_ID_{ADD,REMOVE} matches for it, possibly - * multiple if the match is underspecified. - * - * The NameOwnerChanged signals take three parameters with - * unique or well-known names, but only some forms actually - * exist: - * - * WELLKNOWN, "", UNIQUE → KDBUS_ITEM_NAME_ADD - * WELLKNOWN, UNIQUE, "" → KDBUS_ITEM_NAME_REMOVE - * WELLKNOWN, UNIQUE, UNIQUE → KDBUS_ITEM_NAME_CHANGE - * UNIQUE, "", UNIQUE → KDBUS_ITEM_ID_ADD - * UNIQUE, UNIQUE, "" → KDBUS_ITEM_ID_REMOVE - * - * For the latter two the two unique names must be identical. - * - * */ - - if (name) { - is_name_id = bus_kernel_parse_unique_name(name, &name_id); - if (is_name_id < 0) - return 0; - } - - if (!isempty(old_owner)) { - r = bus_kernel_parse_unique_name(old_owner, &old_owner_id); - if (r < 0) - return 0; - if (r == 0) - return 0; - if (is_name_id > 0 && old_owner_id != name_id) - return 0; - } else - old_owner_id = KDBUS_MATCH_ID_ANY; - - if (!isempty(new_owner)) { - r = bus_kernel_parse_unique_name(new_owner, &new_owner_id); - if (r < 0) - return r; - if (r == 0) - return 0; - if (is_name_id > 0 && new_owner_id != name_id) - return 0; - } else - new_owner_id = KDBUS_MATCH_ID_ANY; - - if (is_name_id <= 0) { - struct kdbus_cmd_match *m; - size_t sz, l; - - /* If the name argument is missing or is a well-known - * name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} - * matches for it */ - - l = name ? strlen(name) + 1 : 0; - - sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) + - offsetof(struct kdbus_item, name_change) + - offsetof(struct kdbus_notify_name_change, name) + - l); - - m = alloca0_align(sz, 8); - m->size = sz; - m->cookie = cookie; - - item = m->items; - item->size = - offsetof(struct kdbus_item, name_change) + - offsetof(struct kdbus_notify_name_change, name) + - l; - - item->name_change.old_id.id = old_owner_id; - item->name_change.new_id.id = new_owner_id; - - memcpy_safe(item->name_change.name, name, l); - - /* If the old name is unset or empty, then - * this can match against added names */ - if (isempty(old_owner)) { - item->type = KDBUS_ITEM_NAME_ADD; - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - } - - /* If the new name is unset or empty, then - * this can match against removed names */ - if (isempty(new_owner)) { - item->type = KDBUS_ITEM_NAME_REMOVE; - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - } - - /* The CHANGE match we need in either case, because - * what is reported as a name change by the kernel - * might just be an owner change between starter and - * normal clients. For userspace such a change should - * be considered a removal/addition, hence let's - * subscribe to this unconditionally. */ - item->type = KDBUS_ITEM_NAME_CHANGE; - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - } - - if (is_name_id != 0) { - struct kdbus_cmd_match *m; - uint64_t sz; - - /* If the name argument is missing or is a unique - * name, then add KDBUS_ITEM_ID_{ADD,REMOVE} matches - * for it */ - - sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) + - offsetof(struct kdbus_item, id_change) + - sizeof(struct kdbus_notify_id_change)); - - m = alloca0_align(sz, 8); - m->size = sz; - m->cookie = cookie; - - item = m->items; - item->size = - offsetof(struct kdbus_item, id_change) + - sizeof(struct kdbus_notify_id_change); - item->id_change.id = name_id; - - /* If the old name is unset or empty, then this can - * match against added ids */ - if (isempty(old_owner)) { - item->type = KDBUS_ITEM_ID_ADD; - if (!isempty(new_owner)) - item->id_change.id = new_owner_id; - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - } - - /* If thew new name is unset or empty, then this can - * match against removed ids */ - if (isempty(new_owner)) { - item->type = KDBUS_ITEM_ID_REMOVE; - if (!isempty(old_owner)) - item->id_change.id = old_owner_id; - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - } - } - - return 0; -} - -int bus_add_match_internal_kernel( - sd_bus *bus, - struct bus_match_component *components, - unsigned n_components, - uint64_t cookie) { - - struct kdbus_cmd_match *m; - struct kdbus_item *item; - uint64_t *bloom; - size_t sz; - const char *sender = NULL; - size_t sender_length = 0; - uint64_t src_id = KDBUS_MATCH_ID_ANY, dst_id = KDBUS_MATCH_ID_ANY; - bool using_bloom = false; - unsigned i; - bool matches_name_change = true; - const char *name_change_arg[3] = {}; - int r; - - assert(bus); - - /* Monitor streams don't support matches, make this a NOP */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - bloom = alloca0(bus->bloom_size); - - sz = ALIGN8(offsetof(struct kdbus_cmd_match, items)); - - for (i = 0; i < n_components; i++) { - struct bus_match_component *c = &components[i]; - - switch (c->type) { - - case BUS_MATCH_SENDER: - if (!streq(c->value_str, "org.freedesktop.DBus")) - matches_name_change = false; - - r = bus_kernel_parse_unique_name(c->value_str, &src_id); - if (r < 0) - return r; - else if (r > 0) - sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); - else { - sender = c->value_str; - sender_length = strlen(sender); - sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1); - } - - break; - - case BUS_MATCH_MESSAGE_TYPE: - if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL) - matches_name_change = false; - - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "message-type", bus_message_type_to_string(c->value_u8)); - using_bloom = true; - break; - - case BUS_MATCH_INTERFACE: - if (!streq(c->value_str, "org.freedesktop.DBus")) - matches_name_change = false; - - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "interface", c->value_str); - using_bloom = true; - break; - - case BUS_MATCH_MEMBER: - if (!streq(c->value_str, "NameOwnerChanged")) - matches_name_change = false; - - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "member", c->value_str); - using_bloom = true; - break; - - case BUS_MATCH_PATH: - if (!streq(c->value_str, "/org/freedesktop/DBus")) - matches_name_change = false; - - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "path", c->value_str); - using_bloom = true; - break; - - case BUS_MATCH_PATH_NAMESPACE: - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "path-slash-prefix", c->value_str); - using_bloom = true; - break; - - case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: { - char buf[sizeof("arg")-1 + 2 + 1]; - - if (c->type - BUS_MATCH_ARG < 3) - name_change_arg[c->type - BUS_MATCH_ARG] = c->value_str; - - xsprintf(buf, "arg%i", c->type - BUS_MATCH_ARG); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_ARG_HAS...BUS_MATCH_ARG_HAS_LAST: { - char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; - - xsprintf(buf, "arg%i-has", c->type - BUS_MATCH_ARG_HAS); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: - /* - * XXX: DBus spec defines arg[0..63]path= matching to be - * a two-way glob. That is, if either string is a prefix - * of the other, it matches. - * This is really hard to realize in bloom-filters, as - * we would have to create a bloom-match for each prefix - * of @c->value_str. This is excessive, hence we just - * ignore all those matches and accept everything from - * the kernel. People should really avoid those matches. - * If they're used in real-life some day, we will have - * to properly support multiple-matches here. - */ - break; - - case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: { - char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")]; - - xsprintf(buf, "arg%i-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE); - bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); - using_bloom = true; - break; - } - - case BUS_MATCH_DESTINATION: - /* - * Kernel only supports matching on destination IDs, but - * not on destination names. So just skip the - * destination name restriction and verify it in - * user-space on retrieval. - */ - r = bus_kernel_parse_unique_name(c->value_str, &dst_id); - if (r < 0) - return r; - else if (r > 0) - sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); - - /* if not a broadcast, it cannot be a name-change */ - if (r <= 0 || dst_id != KDBUS_DST_ID_BROADCAST) - matches_name_change = false; - - break; - - case BUS_MATCH_ROOT: - case BUS_MATCH_VALUE: - case BUS_MATCH_LEAF: - case _BUS_MATCH_NODE_TYPE_MAX: - case _BUS_MATCH_NODE_TYPE_INVALID: - assert_not_reached("Invalid match type?"); - } - } - - if (using_bloom) - sz += ALIGN8(offsetof(struct kdbus_item, data64) + bus->bloom_size); - - m = alloca0_align(sz, 8); - m->size = sz; - m->cookie = cookie; - - item = m->items; - - if (src_id != KDBUS_MATCH_ID_ANY) { - item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); - item->type = KDBUS_ITEM_ID; - item->id = src_id; - item = KDBUS_ITEM_NEXT(item); - } - - if (dst_id != KDBUS_MATCH_ID_ANY) { - item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); - item->type = KDBUS_ITEM_DST_ID; - item->id = dst_id; - item = KDBUS_ITEM_NEXT(item); - } - - if (using_bloom) { - item->size = offsetof(struct kdbus_item, data64) + bus->bloom_size; - item->type = KDBUS_ITEM_BLOOM_MASK; - memcpy(item->data64, bloom, bus->bloom_size); - item = KDBUS_ITEM_NEXT(item); - } - - if (sender) { - item->size = offsetof(struct kdbus_item, str) + sender_length + 1; - item->type = KDBUS_ITEM_NAME; - memcpy(item->str, sender, sender_length + 1); - } - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); - if (r < 0) - return -errno; - - if (matches_name_change) { - - /* If this match could theoretically match - * NameOwnerChanged messages, we need to - * install a second non-bloom filter explitly - * for it */ - - r = add_name_change_match(bus, cookie, name_change_arg[0], name_change_arg[1], name_change_arg[2]); - if (r < 0) - return r; - } - - return 0; -} - -#define internal_match(bus, m) \ - ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ - ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ - : (m)) - -static int bus_add_match_internal_dbus1( - sd_bus *bus, - const char *match) { - - const char *e; - - assert(bus); - assert(match); - - e = internal_match(bus, match); - - return sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "AddMatch", - NULL, - NULL, - "s", - e); -} - -int bus_add_match_internal( - sd_bus *bus, - const char *match, - struct bus_match_component *components, - unsigned n_components, - uint64_t cookie) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - if (bus->is_kernel) - return bus_add_match_internal_kernel(bus, components, n_components, cookie); - else - return bus_add_match_internal_dbus1(bus, match); -} - -int bus_remove_match_internal_kernel( - sd_bus *bus, - uint64_t cookie) { - - struct kdbus_cmd_match m = { - .size = offsetof(struct kdbus_cmd_match, items), - .cookie = cookie, - }; - int r; - - assert(bus); - - /* Monitor streams don't support matches, make this a NOP */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m); - if (r < 0) - return -errno; - - return 0; -} - -static int bus_remove_match_internal_dbus1( - sd_bus *bus, - const char *match) { - - const char *e; - - assert(bus); - assert(match); - - e = internal_match(bus, match); - - return sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "RemoveMatch", - NULL, - NULL, - "s", - e); -} - -int bus_remove_match_internal( - sd_bus *bus, - const char *match, - uint64_t cookie) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - if (bus->is_kernel) - return bus_remove_match_internal_kernel(bus, cookie); - else - return bus_remove_match_internal_dbus1(bus, match); -} - -_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; - const char *mid; - int r; - - assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); - assert_return(machine, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (streq_ptr(name, bus->unique_name)) - return sd_id128_get_machine(machine); - - r = sd_bus_message_new_method_call( - bus, - &m, - name, - "/", - "org.freedesktop.DBus.Peer", - "GetMachineId"); - if (r < 0) - return r; - - r = sd_bus_message_set_auto_start(m, false); - if (r < 0) - return r; - - r = sd_bus_call(bus, m, 0, NULL, &reply); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "s", &mid); - if (r < 0) - return r; - - return sd_id128_from_string(mid, machine); -} diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h deleted file mode 100644 index c181aa7959..0000000000 --- a/src/libsystemd/sd-bus/bus-control.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#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); -int bus_remove_match_internal(sd_bus *bus, const char *match, uint64_t cookie); - -int bus_add_match_internal_kernel(sd_bus *bus, struct bus_match_component *components, unsigned n_components, uint64_t cookie); -int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t cookie); - -int bus_get_name_creds_kdbus(sd_bus *bus, const char *name, uint64_t mask, bool allow_activator, sd_bus_creds **creds); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c deleted file mode 100644 index 2d06bf541f..0000000000 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ /dev/null @@ -1,626 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-signature.h" -#include "bus-type.h" -#include "bus-util.h" -#include "string-util.h" - -_public_ int sd_bus_emit_signal( - sd_bus *bus, - const char *path, - const char *interface, - const char *member, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = sd_bus_message_new_signal(bus, &m, path, interface, member); - if (r < 0) - return r; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - return r; - } - - return sd_bus_send(bus, m, NULL); -} - -_public_ int sd_bus_call_method_async( - sd_bus *bus, - sd_bus_slot **slot, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_message_handler_t callback, - void *userdata, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); - if (r < 0) - return r; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - return r; - } - - return sd_bus_call_async(bus, slot, m, callback, userdata, 0); -} - -_public_ int sd_bus_call_method( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - sd_bus_message **reply, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); - if (r < 0) - goto fail; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - goto fail; - } - - return sd_bus_call(bus, m, 0, error, reply); - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_reply_method_return( - sd_bus_message *call, - const char *types, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_return(call, &m); - if (r < 0) - return r; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - return r; - } - - return sd_bus_send(call->bus, m, NULL); -} - -_public_ int sd_bus_reply_method_error( - sd_bus_message *call, - const sd_bus_error *e) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(sd_bus_error_is_set(e), -EINVAL); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_error(call, &m, e); - if (r < 0) - return r; - - return sd_bus_send(call->bus, m, NULL); -} - -_public_ int sd_bus_reply_method_errorf( - sd_bus_message *call, - const char *name, - const char *format, - ...) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - va_list ap; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - va_start(ap, format); - bus_error_setfv(&error, name, format, ap); - va_end(ap); - - return sd_bus_reply_method_error(call, &error); -} - -_public_ int sd_bus_reply_method_errno( - sd_bus_message *call, - int error, - const sd_bus_error *p) { - - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - if (sd_bus_error_is_set(p)) - return sd_bus_reply_method_error(call, p); - - sd_bus_error_set_errno(&berror, error); - - return sd_bus_reply_method_error(call, &berror); -} - -_public_ int sd_bus_reply_method_errnof( - sd_bus_message *call, - int error, - const char *format, - ...) { - - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - va_list ap; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - va_start(ap, format); - sd_bus_error_set_errnofv(&berror, error, format, ap); - va_end(ap); - - return sd_bus_reply_method_error(call, &berror); -} - -_public_ int sd_bus_get_property( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - sd_bus_message **reply, - const char *type) { - - sd_bus_message *rep = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(reply, -EINVAL, error); - bus_assert_return(signature_is_single(type, false), -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(rep, 'v', type); - if (r < 0) { - sd_bus_message_unref(rep); - goto fail; - } - - *reply = rep; - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_get_property_trivial( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - char type, void *ptr) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(bus_type_is_trivial(type), -EINVAL, error); - bus_assert_return(ptr, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type)); - if (r < 0) - goto fail; - - r = sd_bus_message_read_basic(reply, type, ptr); - if (r < 0) - goto fail; - - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_get_property_string( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - char **ret) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *s; - char *n; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(ret, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'v', "s"); - if (r < 0) - goto fail; - - r = sd_bus_message_read_basic(reply, 's', &s); - if (r < 0) - goto fail; - - n = strdup(s); - if (!n) { - r = -ENOMEM; - goto fail; - } - - *ret = n; - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_get_property_strv( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - char ***ret) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(ret, -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'v', NULL); - if (r < 0) - goto fail; - - r = sd_bus_message_read_strv(reply, ret); - if (r < 0) - goto fail; - - return 0; - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_set_property( - sd_bus *bus, - const char *destination, - const char *path, - const char *interface, - const char *member, - sd_bus_error *error, - const char *type, ...) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - va_list ap; - int r; - - bus_assert_return(bus, -EINVAL, error); - bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); - bus_assert_return(member_name_is_valid(member), -EINVAL, error); - bus_assert_return(signature_is_single(type, false), -EINVAL, error); - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set"); - if (r < 0) - goto fail; - - r = sd_bus_message_append(m, "ss", strempty(interface), member); - if (r < 0) - goto fail; - - r = sd_bus_message_open_container(m, 'v', type); - if (r < 0) - goto fail; - - va_start(ap, type); - r = bus_message_append_ap(m, type, ap); - va_end(ap); - if (r < 0) - goto fail; - - r = sd_bus_message_close_container(m); - if (r < 0) - goto fail; - - return sd_bus_call(bus, m, 0, error, NULL); - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) { - sd_bus_creds *c; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - c = sd_bus_message_get_creds(call); - - /* All data we need? */ - if (c && (mask & ~c->mask) == 0) { - *creds = sd_bus_creds_ref(c); - return 0; - } - - /* No data passed? Or not enough data passed to retrieve the missing bits? */ - if (!c || !(c->mask & SD_BUS_CREDS_PID)) { - /* We couldn't read anything from the call, let's try - * to get it from the sender or peer. */ - - if (call->sender) - /* There's a sender, but the creds are - * missing. This means we are talking via - * dbus1, or are getting a message that was - * sent to us via kdbus, but was converted - * from a dbus1 message by the bus-proxy and - * thus also lacks the creds. */ - return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); - else - /* There's no sender, hence we are on a dbus1 - * direct connection. For direct connections - * the credentials of the AF_UNIX peer matter, - * which may be queried via - * sd_bus_get_owner_creds(). */ - return sd_bus_get_owner_creds(call->bus, mask, creds); - } - - return bus_creds_extend_by_pid(c, mask, creds); -} - -_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - uid_t our_uid; - bool know_caps = false; - int r; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->bus, -EINVAL); - assert_return(!bus_pid_changed(call->bus), -ECHILD); - - if (!BUS_IS_OPEN(call->bus->state)) - return -ENOTCONN; - - if (capability >= 0) { - - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); - if (r < 0) - return r; - - /* We cannot use augmented caps for authorization, - * since then data is acquired raceful from - * /proc. This can never actually happen, but let's - * better be safe than sorry, and do an extra check - * here. */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM); - - /* Note that not even on kdbus we might have the caps - * field, due to faked identities, or namespace - * translation issues. */ - r = sd_bus_creds_has_effective_cap(creds, capability); - if (r > 0) - return 1; - if (r == 0) - know_caps = true; - } else { - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - } - - /* Now, check the UID, but only if the capability check wasn't - * sufficient */ - our_uid = getuid(); - if (our_uid != 0 || !know_caps || capability < 0) { - uid_t sender_uid; - - /* We cannot use augmented uid/euid for authorization, - * since then data is acquired raceful from - * /proc. This can never actually happen, but let's - * better be safe than sorry, and do an extra check - * here. */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM); - - /* Try to use the EUID, if we have it. */ - r = sd_bus_creds_get_euid(creds, &sender_uid); - if (r < 0) - r = sd_bus_creds_get_uid(creds, &sender_uid); - - if (r >= 0) { - /* Sender has same UID as us, then let's grant access */ - if (sender_uid == our_uid) - return 1; - - /* Sender is root, we are not root. */ - if (our_uid != 0 && sender_uid == 0) - return 1; - } - } - - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c deleted file mode 100644 index c4f693dee9..0000000000 --- a/src/libsystemd/sd-bus/bus-creds.c +++ /dev/null @@ -1,1349 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 "terminal-util.h" -#include "user-util.h" -#include "util.h" - -enum { - CAP_OFFSET_INHERITABLE = 0, - CAP_OFFSET_PERMITTED = 1, - CAP_OFFSET_EFFECTIVE = 2, - CAP_OFFSET_BOUNDING = 3 -}; - -void bus_creds_done(sd_bus_creds *c) { - assert(c); - - /* For internal bus cred structures that are allocated by - * something else */ - - free(c->session); - free(c->unit); - free(c->user_unit); - free(c->slice); - free(c->user_slice); - free(c->unescaped_description); - free(c->supplementary_gids); - free(c->tty); - - free(c->well_known_names); /* note that this is an strv, but - * we only free the array, not the - * strings the array points to. The - * full strv we only free if - * c->allocated is set, see - * below. */ - - strv_free(c->cmdline_array); -} - -_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { - - if (!c) - return NULL; - - if (c->allocated) { - assert(c->n_ref > 0); - c->n_ref++; - } else { - sd_bus_message *m; - - /* If this is an embedded creds structure, then - * forward ref counting to the message */ - m = container_of(c, sd_bus_message, creds); - sd_bus_message_ref(m); - } - - return c; -} - -_public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { - - if (!c) - return NULL; - - if (c->allocated) { - assert(c->n_ref > 0); - c->n_ref--; - - if (c->n_ref == 0) { - free(c->comm); - free(c->tid_comm); - free(c->exe); - free(c->cmdline); - free(c->cgroup); - free(c->capability); - free(c->label); - free(c->unique_name); - free(c->cgroup_root); - free(c->description); - - c->supplementary_gids = mfree(c->supplementary_gids); - - c->well_known_names = strv_free(c->well_known_names); - - bus_creds_done(c); - - free(c); - } - } else { - sd_bus_message *m; - - m = container_of(c, sd_bus_message, creds); - sd_bus_message_unref(m); - } - - - return NULL; -} - -_public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) { - assert_return(c, 0); - - return c->mask; -} - -_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { - assert_return(c, 0); - - return c->augmented; -} - -sd_bus_creds* bus_creds_new(void) { - sd_bus_creds *c; - - c = new0(sd_bus_creds, 1); - if (!c) - return NULL; - - c->allocated = true; - c->n_ref = 1; - return c; -} - -_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) { - sd_bus_creds *c; - int r; - - assert_return(pid >= 0, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - - if (pid == 0) - pid = getpid(); - - c = bus_creds_new(); - if (!c) - return -ENOMEM; - - r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0); - if (r < 0) { - sd_bus_creds_unref(c); - return r; - } - - /* Check if the process existed at all, in case we haven't - * figured that out already */ - if (!pid_is_alive(pid)) { - sd_bus_creds_unref(c); - return -ESRCH; - } - - *ret = c; - return 0; -} - -_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) { - assert_return(c, -EINVAL); - assert_return(uid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_UID)) - return -ENODATA; - - *uid = c->uid; - return 0; -} - -_public_ int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) { - assert_return(c, -EINVAL); - assert_return(euid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EUID)) - return -ENODATA; - - *euid = c->euid; - return 0; -} - -_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) { - assert_return(c, -EINVAL); - assert_return(suid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SUID)) - return -ENODATA; - - *suid = c->suid; - return 0; -} - - -_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) { - assert_return(c, -EINVAL); - assert_return(fsuid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_FSUID)) - return -ENODATA; - - *fsuid = c->fsuid; - return 0; -} - -_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) { - assert_return(c, -EINVAL); - assert_return(gid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_GID)) - return -ENODATA; - - *gid = c->gid; - return 0; -} - -_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { - assert_return(c, -EINVAL); - assert_return(egid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EGID)) - return -ENODATA; - - *egid = c->egid; - return 0; -} - -_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) { - assert_return(c, -EINVAL); - assert_return(sgid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SGID)) - return -ENODATA; - - *sgid = c->sgid; - return 0; -} - -_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) { - assert_return(c, -EINVAL); - assert_return(fsgid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_FSGID)) - return -ENODATA; - - *fsgid = c->fsgid; - return 0; -} - -_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) { - assert_return(c, -EINVAL); - assert_return(gids, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) - return -ENODATA; - - *gids = c->supplementary_gids; - return (int) c->n_supplementary_gids; -} - -_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) { - assert_return(c, -EINVAL); - assert_return(pid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_PID)) - return -ENODATA; - - assert(c->pid > 0); - *pid = c->pid; - return 0; -} - -_public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) { - assert_return(c, -EINVAL); - assert_return(ppid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_PPID)) - return -ENODATA; - - /* PID 1 has no parent process. Let's distinguish the case of - * not knowing and not having a parent process by the returned - * error code. */ - if (c->ppid == 0) - return -ENXIO; - - *ppid = c->ppid; - return 0; -} - -_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) { - assert_return(c, -EINVAL); - assert_return(tid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_TID)) - return -ENODATA; - - assert(c->tid > 0); - *tid = c->tid; - return 0; -} - -_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)) - return -ENODATA; - - assert(c->label); - *ret = c->label; - return 0; -} - -_public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_COMM)) - return -ENODATA; - - assert(c->comm); - *ret = c->comm; - return 0; -} - -_public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_TID_COMM)) - return -ENODATA; - - assert(c->tid_comm); - *ret = c->tid_comm; - return 0; -} - -_public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EXE)) - return -ENODATA; - - if (!c->exe) - return -ENXIO; - - *ret = c->exe; - return 0; -} - -_public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_CGROUP)) - return -ENODATA; - - assert(c->cgroup); - *ret = c->cgroup; - return 0; -} - -_public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_UNIT)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->unit) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_unit(shifted, (char**) &c->unit); - if (r < 0) - return r; - } - - *ret = c->unit; - return 0; -} - -_public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_USER_UNIT)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->user_unit) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_user_unit(shifted, (char**) &c->user_unit); - if (r < 0) - return r; - } - - *ret = c->user_unit; - return 0; -} - -_public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SLICE)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->slice) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_slice(shifted, (char**) &c->slice); - if (r < 0) - return r; - } - - *ret = c->slice; - return 0; -} - -_public_ int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_USER_SLICE)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->user_slice) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_user_slice(shifted, (char**) &c->user_slice); - if (r < 0) - return r; - } - - *ret = c->user_slice; - return 0; -} - -_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) { - int r; - - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_SESSION)) - return -ENODATA; - - assert(c->cgroup); - - if (!c->session) { - const char *shifted; - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - r = cg_path_get_session(shifted, (char**) &c->session); - if (r < 0) - return r; - } - - *ret = c->session; - return 0; -} - -_public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) { - const char *shifted; - int r; - - assert_return(c, -EINVAL); - assert_return(uid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_OWNER_UID)) - return -ENODATA; - - assert(c->cgroup); - - r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); - if (r < 0) - return r; - - return cg_path_get_owner_uid(shifted, uid); -} - -_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) { - assert_return(c, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_CMDLINE)) - return -ENODATA; - - if (!c->cmdline) - return -ENXIO; - - if (!c->cmdline_array) { - c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size); - if (!c->cmdline_array) - return -ENOMEM; - } - - *cmdline = c->cmdline_array; - return 0; -} - -_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) { - assert_return(c, -EINVAL); - assert_return(sessionid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID)) - return -ENODATA; - - if (c->audit_session_id == AUDIT_SESSION_INVALID) - return -ENXIO; - - *sessionid = c->audit_session_id; - return 0; -} - -_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) { - assert_return(c, -EINVAL); - assert_return(uid, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID)) - return -ENODATA; - - if (c->audit_login_uid == UID_INVALID) - return -ENXIO; - - *uid = c->audit_login_uid; - return 0; -} - -_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_TTY)) - return -ENODATA; - - if (!c->tty) - return -ENXIO; - - *ret = c->tty; - return 0; -} - -_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) { - assert_return(c, -EINVAL); - assert_return(unique_name, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_UNIQUE_NAME)) - return -ENODATA; - - *unique_name = c->unique_name; - return 0; -} - -_public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) { - assert_return(c, -EINVAL); - assert_return(well_known_names, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)) - return -ENODATA; - - /* As a special hack we return the bus driver as well-known - * names list when this is requested. */ - if (c->well_known_names_driver) { - static const char* const wkn[] = { - "org.freedesktop.DBus", - NULL - }; - - *well_known_names = (char**) wkn; - return 0; - } - - if (c->well_known_names_local) { - static const char* const wkn[] = { - "org.freedesktop.DBus.Local", - NULL - }; - - *well_known_names = (char**) wkn; - return 0; - } - - *well_known_names = c->well_known_names; - return 0; -} - -_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { - assert_return(c, -EINVAL); - assert_return(ret, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_DESCRIPTION)) - return -ENODATA; - - assert(c->description); - - if (!c->unescaped_description) { - c->unescaped_description = bus_label_unescape(c->description); - if (!c->unescaped_description) - return -ENOMEM; - } - - *ret = c->unescaped_description; - return 0; -} - -static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { - size_t sz; - - assert(c); - assert(capability >= 0); - assert(c->capability); - - if ((unsigned) capability > cap_last_cap()) - return 0; - - sz = DIV_ROUND_UP(cap_last_cap(), 32U); - - return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); -} - -_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) { - assert_return(c, -EINVAL); - assert_return(capability >= 0, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS)) - return -ENODATA; - - return has_cap(c, CAP_OFFSET_EFFECTIVE, capability); -} - -_public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) { - assert_return(c, -EINVAL); - assert_return(capability >= 0, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_PERMITTED_CAPS)) - return -ENODATA; - - return has_cap(c, CAP_OFFSET_PERMITTED, capability); -} - -_public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) { - assert_return(c, -EINVAL); - assert_return(capability >= 0, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS)) - return -ENODATA; - - return has_cap(c, CAP_OFFSET_INHERITABLE, capability); -} - -_public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) { - assert_return(c, -EINVAL); - assert_return(capability >= 0, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_BOUNDING_CAPS)) - return -ENODATA; - - return has_cap(c, CAP_OFFSET_BOUNDING, capability); -} - -static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) { - size_t sz, max; - unsigned i, j; - - assert(c); - assert(p); - - max = DIV_ROUND_UP(cap_last_cap(), 32U); - p += strspn(p, WHITESPACE); - - sz = strlen(p); - if (sz % 8 != 0) - return -EINVAL; - - sz /= 8; - if (sz > max) - return -EINVAL; - - if (!c->capability) { - c->capability = new0(uint32_t, max * 4); - if (!c->capability) - return -ENOMEM; - } - - for (i = 0; i < sz; i ++) { - uint32_t v = 0; - - for (j = 0; j < 8; ++j) { - int t; - - t = unhexchar(*p++); - if (t < 0) - return -EINVAL; - - v = (v << 4) | t; - } - - c->capability[offset * max + (sz - i - 1)] = v; - } - - return 0; -} - -int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { - uint64_t missing; - int r; - - assert(c); - assert(c->allocated); - - if (!(mask & SD_BUS_CREDS_AUGMENT)) - return 0; - - /* Try to retrieve PID from creds if it wasn't passed to us */ - if (pid > 0) { - c->pid = pid; - c->mask |= SD_BUS_CREDS_PID; - } else if (c->mask & SD_BUS_CREDS_PID) - pid = c->pid; - else - /* Without pid we cannot do much... */ - return 0; - - /* Try to retrieve TID from creds if it wasn't passed to us */ - if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) - tid = c->tid; - - /* Calculate what we shall and can add */ - missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT); - if (missing == 0) - return 0; - - if (tid > 0) { - c->tid = tid; - c->mask |= SD_BUS_CREDS_TID; - } - - if (missing & (SD_BUS_CREDS_PPID | - SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | - SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | - SD_BUS_CREDS_SUPPLEMENTARY_GIDS | - SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS | - SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) { - - _cleanup_fclose_ FILE *f = NULL; - const char *p; - - p = procfs_file_alloca(pid, "status"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - else if (errno != EPERM && errno != EACCES) - return -errno; - } else { - char line[LINE_MAX]; - - FOREACH_LINE(line, f, return -errno) { - truncate_nl(line); - - if (missing & SD_BUS_CREDS_PPID) { - p = startswith(line, "PPid:"); - if (p) { - p += strspn(p, WHITESPACE); - - /* Explicitly check for PPID 0 (which is the case for PID 1) */ - if (!streq(p, "0")) { - r = parse_pid(p, &c->ppid); - if (r < 0) - return r; - - } else - c->ppid = 0; - - c->mask |= SD_BUS_CREDS_PPID; - continue; - } - } - - if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { - p = startswith(line, "Uid:"); - if (p) { - unsigned long uid, euid, suid, fsuid; - - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) - return -EIO; - - if (missing & SD_BUS_CREDS_UID) - c->uid = (uid_t) uid; - if (missing & SD_BUS_CREDS_EUID) - c->euid = (uid_t) euid; - if (missing & SD_BUS_CREDS_SUID) - c->suid = (uid_t) suid; - if (missing & SD_BUS_CREDS_FSUID) - c->fsuid = (uid_t) fsuid; - - c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); - continue; - } - } - - if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) { - p = startswith(line, "Gid:"); - if (p) { - unsigned long gid, egid, sgid, fsgid; - - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) - return -EIO; - - if (missing & SD_BUS_CREDS_GID) - c->gid = (gid_t) gid; - if (missing & SD_BUS_CREDS_EGID) - c->egid = (gid_t) egid; - if (missing & SD_BUS_CREDS_SGID) - c->sgid = (gid_t) sgid; - if (missing & SD_BUS_CREDS_FSGID) - c->fsgid = (gid_t) fsgid; - - c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); - continue; - } - } - - if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - p = startswith(line, "Groups:"); - if (p) { - size_t allocated = 0; - - for (;;) { - unsigned long g; - int n = 0; - - p += strspn(p, WHITESPACE); - if (*p == 0) - break; - - if (sscanf(p, "%lu%n", &g, &n) != 1) - return -EIO; - - if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1)) - return -ENOMEM; - - c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g; - p += n; - } - - c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - continue; - } - } - - if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) { - p = startswith(line, "CapEff:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS; - continue; - } - } - - if (missing & SD_BUS_CREDS_PERMITTED_CAPS) { - p = startswith(line, "CapPrm:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_PERMITTED, p); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_PERMITTED_CAPS; - continue; - } - } - - if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) { - p = startswith(line, "CapInh:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_INHERITABLE, p); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS; - continue; - } - } - - if (missing & SD_BUS_CREDS_BOUNDING_CAPS) { - p = startswith(line, "CapBnd:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_BOUNDING, p); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_BOUNDING_CAPS; - continue; - } - } - } - } - } - - if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) { - const char *p; - - p = procfs_file_alloca(pid, "attr/current"); - r = read_one_line_file(p, &c->label); - if (r < 0) { - if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - if (missing & SD_BUS_CREDS_COMM) { - r = get_process_comm(pid, &c->comm); - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_COMM; - } - - if (missing & SD_BUS_CREDS_EXE) { - r = get_process_exe(pid, &c->exe); - if (r == -ESRCH) { - /* Unfortunately we cannot really distinguish - * the case here where the process does not - * exist, and /proc/$PID/exe being unreadable - * because $PID is a kernel thread. Hence, - * assume it is a kernel thread, and rely on - * that this case is caught with a later - * call. */ - c->exe = NULL; - c->mask |= SD_BUS_CREDS_EXE; - } else if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_EXE; - } - - if (missing & SD_BUS_CREDS_CMDLINE) { - const char *p; - - p = procfs_file_alloca(pid, "cmdline"); - r = read_full_file(p, &c->cmdline, &c->cmdline_size); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else { - if (c->cmdline_size == 0) - c->cmdline = mfree(c->cmdline); - - c->mask |= SD_BUS_CREDS_CMDLINE; - } - } - - if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { - _cleanup_free_ char *p = NULL; - - if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0) - return -ENOMEM; - - r = read_one_line_file(p, &c->tid_comm); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_TID_COMM; - } - - if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { - - if (!c->cgroup) { - r = cg_pid_get_path(NULL, pid, &c->cgroup); - if (r < 0) { - if (r != -EPERM && r != -EACCES) - return r; - } - } - - if (!c->cgroup_root) { - r = cg_get_root_path(&c->cgroup_root); - if (r < 0) - return r; - } - - if (c->cgroup) - c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); - } - - if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { - r = audit_session_from_pid(pid, &c->audit_session_id); - if (r == -ENODATA) { - /* ENODATA means: no audit session id assigned */ - c->audit_session_id = AUDIT_SESSION_INVALID; - c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } else if (r < 0) { - if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } - - if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { - r = audit_loginuid_from_pid(pid, &c->audit_login_uid); - if (r == -ENODATA) { - /* ENODATA means: no audit login uid assigned */ - c->audit_login_uid = UID_INVALID; - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } else if (r < 0) { - if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) - return r; - } else - c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } - - if (missing & SD_BUS_CREDS_TTY) { - r = get_ctty(pid, NULL, &c->tty); - if (r == -ENXIO) { - /* ENXIO means: process has no controlling TTY */ - c->tty = NULL; - c->mask |= SD_BUS_CREDS_TTY; - } else if (r < 0) { - if (r != -EPERM && r != -EACCES && r != -ENOENT) - return r; - } else - c->mask |= SD_BUS_CREDS_TTY; - } - - /* In case only the exe path was to be read we cannot - * distinguish the case where the exe path was unreadable - * because the process was a kernel thread, or when the - * process didn't exist at all. Hence, let's do a final check, - * to be sure. */ - if (!pid_is_alive(pid)) - return -ESRCH; - - if (tid > 0 && tid != pid && !pid_is_unwaited(tid)) - return -ESRCH; - - c->augmented = missing & c->mask; - - return 0; -} - -int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *n = NULL; - int r; - - assert(c); - assert(ret); - - if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) { - /* There's already all data we need, or augmentation - * wasn't turned on. */ - - *ret = sd_bus_creds_ref(c); - return 0; - } - - n = bus_creds_new(); - if (!n) - return -ENOMEM; - - /* Copy the original data over */ - - if (c->mask & mask & SD_BUS_CREDS_PID) { - n->pid = c->pid; - n->mask |= SD_BUS_CREDS_PID; - } - - if (c->mask & mask & SD_BUS_CREDS_TID) { - n->tid = c->tid; - n->mask |= SD_BUS_CREDS_TID; - } - - if (c->mask & mask & SD_BUS_CREDS_PPID) { - n->ppid = c->ppid; - n->mask |= SD_BUS_CREDS_PPID; - } - - if (c->mask & mask & SD_BUS_CREDS_UID) { - n->uid = c->uid; - n->mask |= SD_BUS_CREDS_UID; - } - - if (c->mask & mask & SD_BUS_CREDS_EUID) { - n->euid = c->euid; - n->mask |= SD_BUS_CREDS_EUID; - } - - if (c->mask & mask & SD_BUS_CREDS_SUID) { - n->suid = c->suid; - n->mask |= SD_BUS_CREDS_SUID; - } - - if (c->mask & mask & SD_BUS_CREDS_FSUID) { - n->fsuid = c->fsuid; - n->mask |= SD_BUS_CREDS_FSUID; - } - - if (c->mask & mask & SD_BUS_CREDS_GID) { - n->gid = c->gid; - n->mask |= SD_BUS_CREDS_GID; - } - - if (c->mask & mask & SD_BUS_CREDS_EGID) { - n->egid = c->egid; - n->mask |= SD_BUS_CREDS_EGID; - } - - if (c->mask & mask & SD_BUS_CREDS_SGID) { - n->sgid = c->sgid; - n->mask |= SD_BUS_CREDS_SGID; - } - - if (c->mask & mask & SD_BUS_CREDS_FSGID) { - n->fsgid = c->fsgid; - n->mask |= SD_BUS_CREDS_FSGID; - } - - if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - if (c->supplementary_gids) { - n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); - if (!n->supplementary_gids) - return -ENOMEM; - n->n_supplementary_gids = c->n_supplementary_gids; - } else { - n->supplementary_gids = NULL; - n->n_supplementary_gids = 0; - } - - n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - - if (c->mask & mask & SD_BUS_CREDS_COMM) { - assert(c->comm); - - n->comm = strdup(c->comm); - if (!n->comm) - return -ENOMEM; - - n->mask |= SD_BUS_CREDS_COMM; - } - - if (c->mask & mask & SD_BUS_CREDS_TID_COMM) { - assert(c->tid_comm); - - n->tid_comm = strdup(c->tid_comm); - if (!n->tid_comm) - return -ENOMEM; - - n->mask |= SD_BUS_CREDS_TID_COMM; - } - - if (c->mask & mask & SD_BUS_CREDS_EXE) { - if (c->exe) { - n->exe = strdup(c->exe); - if (!n->exe) - return -ENOMEM; - } else - n->exe = NULL; - - n->mask |= SD_BUS_CREDS_EXE; - } - - if (c->mask & mask & SD_BUS_CREDS_CMDLINE) { - if (c->cmdline) { - n->cmdline = memdup(c->cmdline, c->cmdline_size); - if (!n->cmdline) - return -ENOMEM; - - n->cmdline_size = c->cmdline_size; - } else { - n->cmdline = NULL; - n->cmdline_size = 0; - } - - n->mask |= SD_BUS_CREDS_CMDLINE; - } - - if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) { - assert(c->cgroup); - - n->cgroup = strdup(c->cgroup); - if (!n->cgroup) - return -ENOMEM; - - n->cgroup_root = strdup(c->cgroup_root); - if (!n->cgroup_root) - return -ENOMEM; - - n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID); - } - - if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) { - assert(c->capability); - - n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4); - if (!n->capability) - return -ENOMEM; - - n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS); - } - - if (c->mask & mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - assert(c->label); - - n->label = strdup(c->label); - if (!n->label) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { - n->audit_session_id = c->audit_session_id; - n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; - } - if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { - n->audit_login_uid = c->audit_login_uid; - n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; - } - - if (c->mask & mask & SD_BUS_CREDS_TTY) { - if (c->tty) { - n->tty = strdup(c->tty); - if (!n->tty) - return -ENOMEM; - } else - n->tty = NULL; - n->mask |= SD_BUS_CREDS_TTY; - } - - if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) { - assert(c->unique_name); - - n->unique_name = strdup(c->unique_name); - if (!n->unique_name) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_UNIQUE_NAME; - } - - if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - if (strv_isempty(c->well_known_names)) - n->well_known_names = NULL; - else { - n->well_known_names = strv_copy(c->well_known_names); - if (!n->well_known_names) - return -ENOMEM; - } - n->well_known_names_driver = c->well_known_names_driver; - n->well_known_names_local = c->well_known_names_local; - n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - - if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { - assert(c->description); - n->description = strdup(c->description); - if (!n->description) - return -ENOMEM; - n->mask |= SD_BUS_CREDS_DESCRIPTION; - } - - n->augmented = c->augmented & n->mask; - - /* Get more data */ - - r = bus_creds_add_more(n, mask, 0, 0); - if (r < 0) - return r; - - *ret = n; - n = NULL; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h deleted file mode 100644 index df8a1f1005..0000000000 --- a/src/libsystemd/sd-bus/bus-creds.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -struct sd_bus_creds { - bool allocated; - unsigned n_ref; - - uint64_t mask; - uint64_t augmented; - - uid_t uid; - uid_t euid; - uid_t suid; - uid_t fsuid; - gid_t gid; - gid_t egid; - gid_t sgid; - gid_t fsgid; - - gid_t *supplementary_gids; - unsigned n_supplementary_gids; - - pid_t ppid; - pid_t pid; - pid_t tid; - - char *comm; - char *tid_comm; - char *exe; - - char *cmdline; - size_t cmdline_size; - char **cmdline_array; - - char *cgroup; - char *session; - char *unit; - char *user_unit; - char *slice; - char *user_slice; - - char *tty; - - uint32_t *capability; - - uint32_t audit_session_id; - uid_t audit_login_uid; - - char *label; - - char *unique_name; - - char **well_known_names; - bool well_known_names_driver:1; - bool well_known_names_local:1; - - char *cgroup_root; - - char *description, *unescaped_description; -}; - -sd_bus_creds* bus_creds_new(void); - -void bus_creds_done(sd_bus_creds *c); - -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); diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c deleted file mode 100644 index 21a6b20a11..0000000000 --- a/src/libsystemd/sd-bus/bus-dump.c +++ /dev/null @@ -1,602 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 "util.h" - -static char *indent(unsigned level, unsigned flags) { - char *p; - unsigned n, i = 0; - - n = 0; - - if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0) - level -= 1; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) - n += 2; - - p = new(char, n + level*8 + 1); - if (!p) - return NULL; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { - p[i++] = ' '; - p[i++] = ' '; - } - - memset(p + i, ' ', level*8); - p[i + level*8] = 0; - - return p; -} - -int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { - unsigned level = 1; - int r; - - assert(m); - - if (!f) - f = stdout; - - if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { - fprintf(f, - "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, - m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : - m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : - m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), - ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), - m->header->endian, - m->header->flags, - m->header->version, - m->priority); - - /* Display synthetic message serial number in a more readable - * format than (uint32_t) -1 */ - if (BUS_MESSAGE_COOKIE(m) == 0xFFFFFFFFULL) - fprintf(f, " Cookie=-1"); - else - fprintf(f, " Cookie=%" PRIu64, BUS_MESSAGE_COOKIE(m)); - - if (m->reply_cookie != 0) - fprintf(f, " ReplyCookie=%" PRIu64, m->reply_cookie); - - fputs("\n", f); - - if (m->sender) - fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_normal()); - if (m->destination) - fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_normal()); - if (m->path) - fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_normal()); - if (m->interface) - fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_normal()); - if (m->member) - fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_normal()); - - if (m->sender || m->destination || m->path || m->interface || m->member) - fputs("\n", f); - - if (sd_bus_error_is_set(&m->error)) - fprintf(f, - " ErrorName=%s%s%s" - " ErrorMessage=%s\"%s\"%s\n", - ansi_highlight_red(), strna(m->error.name), ansi_normal(), - ansi_highlight_red(), strna(m->error.message), ansi_normal()); - - if (m->monotonic != 0) - fprintf(f, " Monotonic="USEC_FMT, m->monotonic); - if (m->realtime != 0) - fprintf(f, " Realtime="USEC_FMT, m->realtime); - if (m->seqnum != 0) - fprintf(f, " SequenceNumber=%"PRIu64, m->seqnum); - - if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0) - fputs("\n", f); - - bus_creds_dump(&m->creds, f, true); - } - - r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)); - if (r < 0) - return log_error_errno(r, "Failed to rewind: %m"); - - if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { - _cleanup_free_ char *prefix = NULL; - - prefix = indent(0, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%sMESSAGE \"%s\" {\n", prefix, strempty(m->root_container.signature)); - } - - for (;;) { - _cleanup_free_ char *prefix = NULL; - const char *contents = NULL; - char type; - union { - uint8_t u8; - uint16_t u16; - int16_t s16; - uint32_t u32; - int32_t s32; - uint64_t u64; - int64_t s64; - double d64; - const char *string; - int i; - } basic; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return log_error_errno(r, "Failed to peek type: %m"); - - if (r == 0) { - if (level <= 1) - break; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return log_error_errno(r, "Failed to exit container: %m"); - - level--; - - prefix = indent(level, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%s};\n", prefix); - continue; - } - - prefix = indent(level, flags); - if (!prefix) - return log_oom(); - - if (bus_type_is_container(type) > 0) { - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return log_error_errno(r, "Failed to enter container: %m"); - - if (type == SD_BUS_TYPE_ARRAY) - fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents); - else if (type == SD_BUS_TYPE_VARIANT) - fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents); - else if (type == SD_BUS_TYPE_STRUCT) - fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents); - else if (type == SD_BUS_TYPE_DICT_ENTRY) - fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents); - - level++; - - continue; - } - - r = sd_bus_message_read_basic(m, type, &basic); - if (r < 0) - return log_error_errno(r, "Failed to get basic: %m"); - - assert(r > 0); - - switch (type) { - - case SD_BUS_TYPE_BYTE: - fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_normal()); - break; - - case SD_BUS_TYPE_BOOLEAN: - fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_normal()); - break; - - case SD_BUS_TYPE_INT16: - fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT16: - fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_normal()); - break; - - case SD_BUS_TYPE_INT32: - fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT32: - fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_normal()); - break; - - case SD_BUS_TYPE_INT64: - fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_normal()); - break; - - case SD_BUS_TYPE_UINT64: - fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_normal()); - break; - - case SD_BUS_TYPE_DOUBLE: - fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_normal()); - break; - - case SD_BUS_TYPE_STRING: - fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_OBJECT_PATH: - fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_SIGNATURE: - fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); - break; - - case SD_BUS_TYPE_UNIX_FD: - fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_normal()); - break; - - default: - assert_not_reached("Unknown basic type."); - } - } - - if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { - _cleanup_free_ char *prefix = NULL; - - prefix = indent(0, flags); - if (!prefix) - return log_oom(); - - fprintf(f, "%s};\n\n", prefix); - } - - return 0; -} - -static void dump_capabilities( - sd_bus_creds *c, - FILE *f, - const char *name, - bool terse, - int (*has)(sd_bus_creds *c, int capability)) { - - unsigned long i, last_cap; - unsigned n = 0; - int r; - - assert(c); - assert(f); - assert(name); - assert(has); - - i = 0; - r = has(c, i); - if (r < 0) - return; - - fprintf(f, "%s%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight()); - last_cap = cap_last_cap(); - - for (;;) { - if (r > 0) { - - if (n > 0) - fputc(' ', f); - if (n % 4 == 3) - fprintf(f, terse ? "\n " : "\n "); - - fprintf(f, "%s", strna(capability_to_name(i))); - n++; - } - - i++; - - if (i > last_cap) - break; - - r = has(c, i); - } - - fputs("\n", f); - - if (!terse) - fputs(ansi_normal(), f); -} - -int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { - uid_t owner, audit_loginuid; - uint32_t audit_sessionid; - char **cmdline = NULL, **well_known = NULL; - const char *prefix, *color, *suffix, *s; - int r, q, v, w, z; - - assert(c); - - if (!f) - f = stdout; - - if (terse) { - prefix = " "; - suffix = ""; - color = ""; - } else { - const char *off; - - prefix = ""; - color = ansi_highlight(); - - off = ansi_normal(); - suffix = strjoina(off, "\n"); - } - - if (c->mask & SD_BUS_CREDS_PID) - fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); - if (c->mask & SD_BUS_CREDS_TID) - fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); - if (c->mask & SD_BUS_CREDS_PPID) { - if (c->ppid == 0) - fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix); - else - fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix); - } - if (c->mask & SD_BUS_CREDS_TTY) - fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix); - - if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY)))) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_UID) - fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix); - if (c->mask & SD_BUS_CREDS_EUID) - fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix); - if (c->mask & SD_BUS_CREDS_SUID) - fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix); - if (c->mask & SD_BUS_CREDS_FSUID) - fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix); - r = sd_bus_creds_get_owner_uid(c, &owner); - if (r >= 0) - fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix); - if (c->mask & SD_BUS_CREDS_GID) - fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix); - if (c->mask & SD_BUS_CREDS_EGID) - fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix); - if (c->mask & SD_BUS_CREDS_SGID) - fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix); - if (c->mask & SD_BUS_CREDS_FSGID) - fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix); - - if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - unsigned i; - - fprintf(f, "%sSupplementaryGIDs=%s", prefix, color); - for (i = 0; i < c->n_supplementary_gids; i++) - fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]); - fprintf(f, "%s", suffix); - } - - if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0)) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_COMM) - fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix); - if (c->mask & SD_BUS_CREDS_TID_COMM) - fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); - if (c->mask & SD_BUS_CREDS_EXE) - fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix); - - if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) - fputs("\n", f); - - r = sd_bus_creds_get_cmdline(c, &cmdline); - if (r >= 0) { - char **i; - - fprintf(f, "%sCommandLine=%s", prefix, color); - STRV_FOREACH(i, cmdline) { - if (i != cmdline) - fputc(' ', f); - - fputs(*i, f); - } - - fprintf(f, "%s", suffix); - } else if (r != -ENODATA) - fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix); - - if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) - fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); - if (c->mask & SD_BUS_CREDS_DESCRIPTION) - fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix); - - if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_CGROUP) - fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); - s = NULL; - r = sd_bus_creds_get_unit(c, &s); - if (r != -ENODATA) - fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - v = sd_bus_creds_get_slice(c, &s); - if (v != -ENODATA) - fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - q = sd_bus_creds_get_user_unit(c, &s); - if (q != -ENODATA) - fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - w = sd_bus_creds_get_user_slice(c, &s); - if (w != -ENODATA) - fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix); - s = NULL; - z = sd_bus_creds_get_session(c, &s); - if (z != -ENODATA) - fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix); - - if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA)) - fputs("\n", f); - - r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid); - if (r >= 0) - fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); - else if (r != -ENODATA) - fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix); - q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid); - if (q >= 0) - fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); - else if (q != -ENODATA) - fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix); - - if (terse && (r != -ENODATA || q != -ENODATA)) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) - fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); - - if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { - char **i; - - fprintf(f, "%sWellKnownNames=%s", prefix, color); - STRV_FOREACH(i, well_known) { - if (i != well_known) - fputc(' ', f); - - fputs(*i, f); - } - - fprintf(f, "%s", suffix); - } - - if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)) - fputc('\n', f); - - dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap); - dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap); - dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap); - dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap); - - return 0; -} - -/* - * For details about the file format, see: - * - * http://wiki.wireshark.org/Development/LibpcapFileFormat - */ - -typedef struct _packed_ pcap_hdr_s { - uint32_t magic_number; /* magic number */ - uint16_t version_major; /* major version number */ - uint16_t version_minor; /* minor version number */ - int32_t thiszone; /* GMT to local correction */ - uint32_t sigfigs; /* accuracy of timestamps */ - uint32_t snaplen; /* max length of captured packets, in octets */ - uint32_t network; /* data link type */ -} pcap_hdr_t ; - -typedef struct _packed_ pcaprec_hdr_s { - uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ - uint32_t incl_len; /* number of octets of packet saved in file */ - uint32_t orig_len; /* actual length of packet */ -} pcaprec_hdr_t; - -int bus_pcap_header(size_t snaplen, FILE *f) { - - pcap_hdr_t hdr = { - .magic_number = 0xa1b2c3d4U, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, /* UTC */ - .sigfigs = 0, - .network = 231, /* D-Bus */ - }; - - if (!f) - f = stdout; - - assert(snaplen > 0); - assert((size_t) (uint32_t) snaplen == snaplen); - - hdr.snaplen = (uint32_t) snaplen; - - fwrite(&hdr, 1, sizeof(hdr), f); - - return fflush_and_check(f); -} - -int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { - struct bus_body_part *part; - pcaprec_hdr_t hdr = {}; - struct timeval tv; - unsigned i; - size_t w; - - if (!f) - f = stdout; - - assert(m); - assert(snaplen > 0); - assert((size_t) (uint32_t) snaplen == snaplen); - - if (m->realtime != 0) - timeval_store(&tv, m->realtime); - else - assert_se(gettimeofday(&tv, NULL) >= 0); - - hdr.ts_sec = tv.tv_sec; - hdr.ts_usec = tv.tv_usec; - hdr.orig_len = BUS_MESSAGE_SIZE(m); - hdr.incl_len = MIN(hdr.orig_len, snaplen); - - /* write the pcap header */ - fwrite(&hdr, 1, sizeof(hdr), f); - - /* write the dbus header */ - w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); - fwrite(m->header, 1, w, f); - snaplen -= w; - - /* write the dbus body */ - MESSAGE_FOREACH_PART(part, i, m) { - if (snaplen <= 0) - break; - - w = MIN(part->size, snaplen); - fwrite(part->data, 1, w, f); - snaplen -= w; - } - - return fflush_and_check(f); -} diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h deleted file mode 100644 index 874e86d09c..0000000000 --- a/src/libsystemd/sd-bus/bus-dump.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-bus.h" - -enum { - BUS_MESSAGE_DUMP_WITH_HEADER = 1, - BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2, -}; - -int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags); - -int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); - -int bus_pcap_header(size_t snaplen, FILE *f); -int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c deleted file mode 100644 index 26219bdeed..0000000000 --- a/src/libsystemd/sd-bus/bus-error.c +++ /dev/null @@ -1,608 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#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), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), - SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), - SD_BUS_ERROR_MAP_END -}; - -/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section. - * Hide them; for currently unknown reasons they get exported to the shared libries - * even without being listed in the sym file. */ -extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_; -extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_; - -/* Additional maps registered with sd_bus_error_add_map() are in this - * NULL terminated array */ -static const sd_bus_error_map **additional_error_maps = NULL; - -static int bus_error_name_to_errno(const char *name) { - const sd_bus_error_map **map, *m; - const char *p; - int r; - - if (!name) - return EINVAL; - - p = startswith(name, "System.Error."); - if (p) { - r = errno_from_name(p); - if (r < 0) - return EIO; - - return r; - } - - 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) - break; - - 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 next 8 byte - * boundary, which is the selected alignment for the - * arrays. */ - if (m->code == BUS_ERROR_MAP_END_MARKER) { - m = ALIGN8_PTR(m+1); - continue; - } - - if (streq(m->name, name)) - return m->code; - - m++; - } - - return EIO; -} - -static sd_bus_error errno_to_bus_error_const(int error) { - - if (error < 0) - error = -error; - - switch (error) { - - case ENOMEM: - return BUS_ERROR_OOM; - - case EPERM: - case EACCES: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); - - case EINVAL: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); - - case ESRCH: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); - - case ENOENT: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); - - case EEXIST: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists"); - - case ETIMEDOUT: - case ETIME: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out"); - - case EIO: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error"); - - case ENETRESET: - case ECONNABORTED: - case ECONNRESET: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); - - case EOPNOTSUPP: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); - - case EADDRNOTAVAIL: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); - - case ENOBUFS: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); - - case EADDRINUSE: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); - - case EBADMSG: - return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); - } - - return SD_BUS_ERROR_NULL; -} - -static int errno_to_bus_error_name_new(int error, char **ret) { - const char *name; - char *n; - - if (error < 0) - error = -error; - - name = errno_to_name(error); - if (!name) - return 0; - - n = strappend("System.Error.", name); - if (!n) - return -ENOMEM; - - *ret = n; - return 1; -} - -bool bus_error_is_dirty(sd_bus_error *e) { - if (!e) - return false; - - return e->name || e->message || e->_need_free != 0; -} - -_public_ void sd_bus_error_free(sd_bus_error *e) { - if (!e) - return; - - if (e->_need_free > 0) { - free((void*) e->name); - free((void*) e->message); - } - - e->name = e->message = NULL; - e->_need_free = 0; -} - -_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { - - if (!name) - return 0; - if (!e) - goto finish; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - - e->name = strdup(name); - if (!e->name) { - *e = BUS_ERROR_OOM; - return -ENOMEM; - } - - if (message) - e->message = strdup(message); - - e->_need_free = 1; - -finish: - return -bus_error_name_to_errno(name); -} - -int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) { - - if (!name) - return 0; - - if (e) { - assert_return(!bus_error_is_dirty(e), -EINVAL); - - 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); - - e->_need_free = 1; - } - - return -bus_error_name_to_errno(name); -} - -_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { - - if (format) { - int r; - va_list ap; - - va_start(ap, format); - r = bus_error_setfv(e, name, format, ap); - va_end(ap); - - return r; - } - - return sd_bus_error_set(e, name, NULL); -} - -_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { - - if (!sd_bus_error_is_set(e)) - return 0; - if (!dest) - goto finish; - - assert_return(!bus_error_is_dirty(dest), -EINVAL); - - /* - * _need_free < 0 indicates that the error is temporarily const, needs deep copying - * _need_free == 0 indicates that the error is perpetually const, needs no deep copying - * _need_free > 0 indicates that the error is fully dynamic, needs deep copying - */ - - if (e->_need_free == 0) - *dest = *e; - else { - dest->name = strdup(e->name); - if (!dest->name) { - *dest = BUS_ERROR_OOM; - return -ENOMEM; - } - - if (e->message) - dest->message = strdup(e->message); - - dest->_need_free = 1; - } - -finish: - return -bus_error_name_to_errno(e->name); -} - -_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { - if (!name) - return 0; - if (!e) - goto finish; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - - *e = SD_BUS_ERROR_MAKE_CONST(name, message); - -finish: - return -bus_error_name_to_errno(name); -} - -_public_ int sd_bus_error_is_set(const sd_bus_error *e) { - if (!e) - return 0; - - return !!e->name; -} - -_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) { - if (!e) - return 0; - - return streq_ptr(e->name, name); -} - -_public_ int sd_bus_error_get_errno(const sd_bus_error* e) { - if (!e) - return 0; - - if (!e->name) - return 0; - - return bus_error_name_to_errno(e->name); -} - -static void bus_error_strerror(sd_bus_error *e, int error) { - size_t k = 64; - char *m; - - assert(e); - - for (;;) { - char *x; - - m = new(char, k); - if (!m) - return; - - errno = 0; - x = strerror_r(error, m, k); - if (errno == ERANGE || strlen(x) >= k - 1) { - free(m); - k *= 2; - continue; - } - - if (errno) { - free(m); - return; - } - - if (x == m) { - if (e->_need_free > 0) { - /* Error is already dynamic, let's just update the message */ - free((char*) e->message); - e->message = x; - - } else { - char *t; - /* Error was const so far, let's make it dynamic, if we can */ - - t = strdup(e->name); - if (!t) { - free(m); - return; - } - - e->_need_free = 1; - e->name = t; - e->message = x; - } - } else { - free(m); - - if (e->_need_free > 0) { - char *t; - - /* Error is dynamic, let's hence make the message also dynamic */ - t = strdup(x); - if (!t) - return; - - free((char*) e->message); - e->message = t; - } else { - /* Error is const, hence we can just override */ - e->message = x; - } - } - - return; - } -} - -_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { - - if (error < 0) - error = -error; - - if (!e) - return -error; - if (error == 0) - return -error; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - - /* First, try a const translation */ - *e = errno_to_bus_error_const(error); - - if (!sd_bus_error_is_set(e)) { - int k; - - /* If that didn't work, try a dynamic one. */ - - k = errno_to_bus_error_name_new(error, (char**) &e->name); - if (k > 0) - e->_need_free = 1; - else if (k < 0) { - *e = BUS_ERROR_OOM; - return -error; - } else - *e = BUS_ERROR_FAILED; - } - - /* Now, fill in the message from strerror() if we can */ - bus_error_strerror(e, error); - return -error; -} - -_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { - PROTECT_ERRNO; - int r; - - if (error < 0) - error = -error; - - if (!e) - return -error; - if (error == 0) - return 0; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - - /* First, try a const translation */ - *e = errno_to_bus_error_const(error); - - if (!sd_bus_error_is_set(e)) { - int k; - - /* If that didn't work, try a dynamic one */ - - k = errno_to_bus_error_name_new(error, (char**) &e->name); - if (k > 0) - e->_need_free = 1; - else if (k < 0) { - *e = BUS_ERROR_OOM; - return -ENOMEM; - } else - *e = BUS_ERROR_FAILED; - } - - if (format) { - char *m; - - /* Then, let's try to fill in the supplied message */ - - errno = error; /* Make sure that %m resolves to the specified error */ - r = vasprintf(&m, format, ap); - if (r >= 0) { - - if (e->_need_free <= 0) { - char *t; - - t = strdup(e->name); - if (t) { - e->_need_free = 1; - e->name = t; - e->message = m; - return -error; - } - - free(m); - } else { - free((char*) e->message); - e->message = m; - return -error; - } - } - } - - /* If that didn't work, use strerror() for the message */ - bus_error_strerror(e, error); - return -error; -} - -_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) { - int r; - - if (error < 0) - error = -error; - - if (!e) - return -error; - if (error == 0) - return 0; - - assert_return(!bus_error_is_dirty(e), -EINVAL); - - if (format) { - va_list ap; - - va_start(ap, format); - r = sd_bus_error_set_errnofv(e, error, format, ap); - va_end(ap); - - return r; - } - - return sd_bus_error_set_errno(e, error); -} - -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 - * its error messages, so let's override them here */ - if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) - return "Access denied"; - - if (e->message) - return e->message; - } - - if (error < 0) - error = -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 (; 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; - - additional_error_maps = maps; - return 1; -} diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h deleted file mode 100644 index e2c4cf4b3f..0000000000 --- a/src/libsystemd/sd-bus/bus-error.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -#include "macro.h" - -bool bus_error_is_dirty(sd_bus_error *e); - -const char *bus_error_message(const sd_bus_error *e, int error); - -int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0); -int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0); - -#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") -#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") - -/* - * There are two ways to register error maps with the error translation - * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only - * works when linked into the same ELF module, or via - * sd_bus_error_add_map() which is the official, external API, that - * works from any module. - * - * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in - * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at - * least once per compilation unit (i.e. per library), to ensure that - * the error map is really added to the final binary. - */ - -#define BUS_ERROR_MAP_ELF_REGISTER \ - __attribute__ ((__section__("BUS_ERROR_MAP"))) \ - __attribute__ ((__used__)) \ - __attribute__ ((aligned(8))) - -#define BUS_ERROR_MAP_ELF_USE(errors) \ - extern const sd_bus_error_map errors[]; \ - __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors; - -/* We use something exotic as end marker, to ensure people build the - * maps using the macsd-ros. */ -#define BUS_ERROR_MAP_END_MARKER -'x' - -BUS_ERROR_MAP_ELF_USE(bus_standard_errors); diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c deleted file mode 100644 index 58782767fa..0000000000 --- a/src/libsystemd/sd-bus/bus-gvariant.c +++ /dev/null @@ -1,311 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-gvariant.h" -#include "bus-signature.h" -#include "bus-type.h" - -int bus_gvariant_get_size(const char *signature) { - const char *p; - int sum = 0, r; - - /* For fixed size structs. Fails for variable size structs. */ - - p = signature; - while (*p != 0) { - size_t n; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - else { - char t[n+1]; - - memcpy(t, p, n); - t[n] = 0; - - r = bus_gvariant_get_alignment(t); - if (r < 0) - return r; - - sum = ALIGN_TO(sum, r); - } - - switch (*p) { - - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_BYTE: - sum += 1; - break; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - sum += 2; - break; - - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: - sum += 4; - break; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - sum += 8; - break; - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - if (n == 2) { - /* unary type () has fixed size of 1 */ - r = 1; - } else { - char t[n-1]; - - memcpy(t, p + 1, n - 2); - t[n - 2] = 0; - - r = bus_gvariant_get_size(t); - if (r < 0) - return r; - } - - sum += r; - break; - } - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_ARRAY: - case SD_BUS_TYPE_VARIANT: - return -EINVAL; - - default: - assert_not_reached("Unknown signature type"); - } - - p += n; - } - - r = bus_gvariant_get_alignment(signature); - if (r < 0) - return r; - - return ALIGN_TO(sum, r); -} - -int bus_gvariant_get_alignment(const char *signature) { - size_t alignment = 1; - const char *p; - int r; - - p = signature; - while (*p != 0 && alignment < 8) { - size_t n; - int a; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - - switch (*p) { - - case SD_BUS_TYPE_BYTE: - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - a = 1; - break; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - a = 2; - break; - - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: - a = 4; - break; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - case SD_BUS_TYPE_VARIANT: - a = 8; - break; - - case SD_BUS_TYPE_ARRAY: { - char t[n]; - - memcpy(t, p + 1, n - 1); - t[n - 1] = 0; - - a = bus_gvariant_get_alignment(t); - break; - } - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - char t[n-1]; - - memcpy(t, p + 1, n - 2); - t[n - 2] = 0; - - a = bus_gvariant_get_alignment(t); - break; - } - - default: - assert_not_reached("Unknown signature type"); - } - - if (a < 0) - return a; - - assert(a > 0 && a <= 8); - if ((size_t) a > alignment) - alignment = (size_t) a; - - p += n; - } - - return alignment; -} - -int bus_gvariant_is_fixed_size(const char *signature) { - const char *p; - int r; - - assert(signature); - - p = signature; - while (*p != 0) { - size_t n; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - - switch (*p) { - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_ARRAY: - case SD_BUS_TYPE_VARIANT: - return 0; - - case SD_BUS_TYPE_BYTE: - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - break; - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - char t[n-1]; - - memcpy(t, p + 1, n - 2); - t[n - 2] = 0; - - r = bus_gvariant_is_fixed_size(t); - if (r <= 0) - return r; - break; - } - - default: - assert_not_reached("Unknown signature type"); - } - - p += n; - } - - return true; -} - -size_t bus_gvariant_determine_word_size(size_t sz, size_t extra) { - if (sz + extra <= 0xFF) - return 1; - else if (sz + extra*2 <= 0xFFFF) - return 2; - else if (sz + extra*4 <= 0xFFFFFFFF) - return 4; - else - return 8; -} - -size_t bus_gvariant_read_word_le(void *p, size_t sz) { - union { - uint16_t u16; - uint32_t u32; - uint64_t u64; - } x; - - assert(p); - - if (sz == 1) - return *(uint8_t*) p; - - memcpy(&x, p, sz); - - if (sz == 2) - return le16toh(x.u16); - else if (sz == 4) - return le32toh(x.u32); - else if (sz == 8) - return le64toh(x.u64); - - assert_not_reached("unknown word width"); -} - -void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) { - union { - uint16_t u16; - uint32_t u32; - uint64_t u64; - } x; - - assert(p); - assert(sz == 8 || (value < (1ULL << (sz*8)))); - - if (sz == 1) { - *(uint8_t*) p = value; - return; - } else if (sz == 2) - x.u16 = htole16((uint16_t) value); - else if (sz == 4) - x.u32 = htole32((uint32_t) value); - else if (sz == 8) - x.u64 = htole64((uint64_t) value); - else - assert_not_reached("unknown word width"); - - memcpy(p, &x, sz); -} diff --git a/src/libsystemd/sd-bus/bus-gvariant.h b/src/libsystemd/sd-bus/bus-gvariant.h deleted file mode 100644 index 6da637fb05..0000000000 --- a/src/libsystemd/sd-bus/bus-gvariant.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "macro.h" - -int bus_gvariant_get_size(const char *signature) _pure_; -int bus_gvariant_get_alignment(const char *signature) _pure_; -int bus_gvariant_is_fixed_size(const char *signature) _pure_; - -size_t bus_gvariant_determine_word_size(size_t sz, size_t extra); -void bus_gvariant_write_word_le(void *p, size_t sz, size_t value); -size_t bus_gvariant_read_word_le(void *p, size_t sz); diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c deleted file mode 100644 index caca679086..0000000000 --- a/src/libsystemd/sd-bus/bus-internal.c +++ /dev/null @@ -1,374 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - bool slash; - - if (!p) - return false; - - if (p[0] != '/') - return false; - - if (p[1] == 0) - return true; - - for (slash = true, q = p+1; *q; q++) - if (*q == '/') { - if (slash) - return false; - - slash = true; - } else { - bool good; - - good = - (*q >= 'a' && *q <= 'z') || - (*q >= 'A' && *q <= 'Z') || - (*q >= '0' && *q <= '9') || - *q == '_'; - - if (!good) - return false; - - slash = false; - } - - if (slash) - return false; - - return true; -} - -char* object_path_startswith(const char *a, const char *b) { - const char *p; - - if (!object_path_is_valid(a) || - !object_path_is_valid(b)) - return NULL; - - if (streq(b, "/")) - return (char*) a + 1; - - p = startswith(a, b); - if (!p) - return NULL; - - if (*p == 0) - return (char*) p; - - if (*p == '/') - return (char*) p + 1; - - return NULL; -} - -bool interface_name_is_valid(const char *p) { - const char *q; - bool dot, found_dot = false; - - if (isempty(p)) - return false; - - for (dot = true, q = p; *q; q++) - if (*q == '.') { - if (dot) - return false; - - found_dot = dot = true; - } else { - bool good; - - good = - (*q >= 'a' && *q <= 'z') || - (*q >= 'A' && *q <= 'Z') || - (!dot && *q >= '0' && *q <= '9') || - *q == '_'; - - if (!good) - return false; - - dot = false; - } - - if (q - p > 255) - return false; - - if (dot) - return false; - - if (!found_dot) - return false; - - return true; -} - -bool service_name_is_valid(const char *p) { - const char *q; - bool dot, found_dot = false, unique; - - if (isempty(p)) - return false; - - unique = p[0] == ':'; - - for (dot = true, q = unique ? p+1 : p; *q; q++) - if (*q == '.') { - if (dot) - return false; - - found_dot = dot = true; - } else { - bool good; - - good = - (*q >= 'a' && *q <= 'z') || - (*q >= 'A' && *q <= 'Z') || - ((!dot || unique) && *q >= '0' && *q <= '9') || - *q == '_' || *q == '-'; - - if (!good) - return false; - - dot = false; - } - - if (q - p > 255) - return false; - - if (dot) - return false; - - if (!found_dot) - return false; - - return true; -} - -char* service_name_startswith(const char *a, const char *b) { - const char *p; - - if (!service_name_is_valid(a) || - !service_name_is_valid(b)) - return NULL; - - p = startswith(a, b); - if (!p) - return NULL; - - if (*p == 0) - return (char*) p; - - if (*p == '.') - return (char*) p + 1; - - return NULL; -} - -bool member_name_is_valid(const char *p) { - const char *q; - - if (isempty(p)) - return false; - - for (q = p; *q; q++) { - bool good; - - good = - (*q >= 'a' && *q <= 'z') || - (*q >= 'A' && *q <= 'Z') || - (*q >= '0' && *q <= '9') || - *q == '_'; - - if (!good) - return false; - } - - if (q - p > 255) - return false; - - return true; -} - -/* - * Complex pattern match - * This checks whether @a is a 'complex-prefix' of @b, or @b is a - * 'complex-prefix' of @a, based on strings that consist of labels with @c as - * spearator. This function returns true if: - * - both strings are equal - * - either is a prefix of the other and ends with @c - * The second rule makes sure that either string needs to be fully included in - * the other, and the string which is considered the prefix needs to end with a - * separator. - */ -static bool complex_pattern_check(char c, const char *a, const char *b) { - bool separator = false; - - if (!a && !b) - return true; - - if (!a || !b) - return false; - - for (;;) { - if (*a != *b) - return (separator && (*a == 0 || *b == 0)); - - if (*a == 0) - return true; - - separator = *a == c; - - a++, b++; - } -} - -bool namespace_complex_pattern(const char *pattern, const char *value) { - return complex_pattern_check('.', pattern, value); -} - -bool path_complex_pattern(const char *pattern, const char *value) { - return complex_pattern_check('/', pattern, value); -} - -/* - * Simple pattern match - * This checks whether @a is a 'simple-prefix' of @b, based on strings that - * consist of labels with @c as separator. This function returns true, if: - * - if @a and @b are equal - * - if @a is a prefix of @b, and the first following character in @b (or the - * last character in @a) is @c - * The second rule basically makes sure that if @a is a prefix of @b, then @b - * must follow with a new label separated by @c. It cannot extend the label. - */ -static bool simple_pattern_check(char c, const char *a, const char *b) { - bool separator = false; - - if (!a && !b) - return true; - - if (!a || !b) - return false; - - for (;;) { - if (*a != *b) - return *a == 0 && (*b == c || separator); - - if (*a == 0) - return true; - - separator = *a == c; - - a++, b++; - } -} - -bool namespace_simple_pattern(const char *pattern, const char *value) { - return simple_pattern_check('.', pattern, value); -} - -bool path_simple_pattern(const char *pattern, const char *value) { - return simple_pattern_check('/', pattern, value); -} - -int bus_message_type_from_string(const char *s, uint8_t *u) { - if (streq(s, "signal")) - *u = SD_BUS_MESSAGE_SIGNAL; - else if (streq(s, "method_call")) - *u = SD_BUS_MESSAGE_METHOD_CALL; - else if (streq(s, "error")) - *u = SD_BUS_MESSAGE_METHOD_ERROR; - else if (streq(s, "method_return")) - *u = SD_BUS_MESSAGE_METHOD_RETURN; - else - return -EINVAL; - - return 0; -} - -const char *bus_message_type_to_string(uint8_t u) { - if (u == SD_BUS_MESSAGE_SIGNAL) - return "signal"; - else if (u == SD_BUS_MESSAGE_METHOD_CALL) - return "method_call"; - else if (u == SD_BUS_MESSAGE_METHOD_ERROR) - return "error"; - else if (u == SD_BUS_MESSAGE_METHOD_RETURN) - return "method_return"; - else - return NULL; -} - -char *bus_address_escape(const char *v) { - const char *a; - char *r, *b; - - r = new(char, strlen(v)*3+1); - if (!r) - return NULL; - - for (a = v, b = r; *a; a++) { - - if ((*a >= '0' && *a <= '9') || - (*a >= 'a' && *a <= 'z') || - (*a >= 'A' && *a <= 'Z') || - strchr("_-/.", *a)) - *(b++) = *a; - else { - *(b++) = '%'; - *(b++) = hexchar(*a >> 4); - *(b++) = hexchar(*a & 0xF); - } - } - - *b = 0; - return r; -} - -int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) { - assert(m); - - if (r < 0) { - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_reply_method_errno(m, r, error); - - } else if (sd_bus_error_is_set(error)) { - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_reply_method_error(m, error); - } else - return r; - - log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s", - bus_message_type_to_string(m->header->type), - strna(m->sender), - strna(m->path), - strna(m->interface), - strna(m->member), - strna(m->root_container.signature), - bus_error_message(error, r)); - - return 1; -} diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h deleted file mode 100644 index 216d9f62bc..0000000000 --- a/src/libsystemd/sd-bus/bus-internal.h +++ /dev/null @@ -1,399 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-bus.h" - -#include "bus-error.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; - usec_t timeout; - uint64_t cookie; - unsigned prioq_idx; -}; - -struct filter_callback { - sd_bus_message_handler_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct filter_callback, callbacks); -}; - -struct match_callback { - sd_bus_message_handler_t callback; - - uint64_t cookie; - unsigned last_iteration; - - char *match_string; - - struct bus_match_node *match_node; -}; - -struct node { - char *path; - struct node *parent; - LIST_HEAD(struct node, child); - LIST_FIELDS(struct node, siblings); - - LIST_HEAD(struct node_callback, callbacks); - LIST_HEAD(struct node_vtable, vtables); - LIST_HEAD(struct node_enumerator, enumerators); - LIST_HEAD(struct node_object_manager, object_managers); -}; - -struct node_callback { - struct node *node; - - bool is_fallback; - sd_bus_message_handler_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct node_callback, callbacks); -}; - -struct node_enumerator { - struct node *node; - - sd_bus_node_enumerator_t callback; - - unsigned last_iteration; - - LIST_FIELDS(struct node_enumerator, enumerators); -}; - -struct node_object_manager { - struct node *node; - - LIST_FIELDS(struct node_object_manager, object_managers); -}; - -struct node_vtable { - struct node *node; - - char *interface; - bool is_fallback; - const sd_bus_vtable *vtable; - sd_bus_object_find_t find; - - unsigned last_iteration; - - LIST_FIELDS(struct node_vtable, vtables); -}; - -struct vtable_member { - const char *path; - const char *interface; - const char *member; - struct node_vtable *parent; - unsigned last_iteration; - const sd_bus_vtable *vtable; -}; - -typedef enum BusSlotType { - BUS_REPLY_CALLBACK, - BUS_FILTER_CALLBACK, - BUS_MATCH_CALLBACK, - BUS_NODE_CALLBACK, - BUS_NODE_ENUMERATOR, - BUS_NODE_VTABLE, - BUS_NODE_OBJECT_MANAGER, - _BUS_SLOT_INVALID = -1, -} BusSlotType; - -struct sd_bus_slot { - unsigned n_ref; - sd_bus *bus; - void *userdata; - BusSlotType type:5; - bool floating:1; - bool match_added:1; - char *description; - - LIST_FIELDS(sd_bus_slot, slots); - - union { - struct reply_callback reply_callback; - struct filter_callback filter_callback; - struct match_callback match_callback; - struct node_callback node_callback; - struct node_enumerator node_enumerator; - struct node_object_manager node_object_manager; - struct node_vtable node_vtable; - }; -}; - -enum bus_state { - BUS_UNSET, - BUS_OPENING, - BUS_AUTHENTICATING, - BUS_HELLO, - BUS_RUNNING, - BUS_CLOSING, - BUS_CLOSED -}; - -static inline bool BUS_IS_OPEN(enum bus_state state) { - return state > BUS_UNSET && state < BUS_CLOSING; -} - -enum bus_auth { - _BUS_AUTH_INVALID, - BUS_AUTH_EXTERNAL, - BUS_AUTH_ANONYMOUS -}; - -struct sd_bus { - /* We use atomic ref counting here since sd_bus_message - objects retain references to their originating sd_bus but - we want to allow them to be processed in a different - thread. We won't provide full thread safety, but only the - bare minimum that makes it possible to use sd_bus and - sd_bus_message objects independently and on different - threads as long as each object is used only once at the - same time. */ - RefCount n_ref; - - enum bus_state state; - int input_fd, output_fd; - int message_version; - int message_endian; - - bool is_kernel:1; - bool can_fds:1; - bool bus_client:1; - bool ucred_valid:1; - bool is_server:1; - bool anonymous_auth:1; - bool prefer_readv:1; - bool prefer_writev:1; - bool match_callbacks_modified:1; - bool filter_callbacks_modified:1; - bool nodes_modified:1; - bool trusted:1; - bool fake_creds_valid:1; - bool fake_pids_valid:1; - bool manual_peer_interface:1; - bool is_system:1; - bool is_user:1; - bool allow_interactive_authorization:1; - - int use_memfd; - - void *rbuffer; - size_t rbuffer_size; - - sd_bus_message **rqueue; - unsigned rqueue_size; - size_t rqueue_allocated; - - sd_bus_message **wqueue; - unsigned wqueue_size; - size_t windex; - size_t wqueue_allocated; - - uint64_t cookie; - - char *unique_name; - uint64_t unique_id; - - struct bus_match_node match_callbacks; - Prioq *reply_callbacks_prioq; - OrderedHashmap *reply_callbacks; - LIST_HEAD(struct filter_callback, filter_callbacks); - - Hashmap *nodes; - Hashmap *vtable_methods; - Hashmap *vtable_properties; - - union sockaddr_union sockaddr; - socklen_t sockaddr_size; - - char *kernel; - char *machine; - pid_t nspid; - - sd_id128_t server_id; - - char *address; - unsigned address_index; - - int last_connect_error; - - enum bus_auth auth; - size_t auth_rbegin; - struct iovec auth_iovec[3]; - unsigned auth_index; - char *auth_buffer; - usec_t auth_timeout; - - struct ucred ucred; - char *label; - - uint64_t creds_mask; - - int *fds; - unsigned n_fds; - - char *exec_path; - char **exec_argv; - - unsigned iteration_counter; - - void *kdbus_buffer; - - /* We do locking around the memfd cache, since we want to - * allow people to process a sd_bus_message in a different - * thread then it was generated on and free it there. Since - * adding something to the memfd cache might happen when a - * message is released, we hence need to protect this bit with - * a mutex. */ - pthread_mutex_t memfd_cache_mutex; - struct memfd_cache memfd_cache[MEMFD_CACHE_MAX]; - unsigned n_memfd_cache; - - pid_t original_pid; - - uint64_t hello_flags; - uint64_t attach_flags; - - uint64_t match_cookie; - - sd_event_source *input_io_event_source; - sd_event_source *output_io_event_source; - sd_event_source *time_event_source; - sd_event_source *quit_event_source; - sd_event *event; - int event_priority; - - sd_bus_message *current_message; - sd_bus_slot *current_slot; - sd_bus_message_handler_t current_handler; - void *current_userdata; - - sd_bus **default_bus_ptr; - pid_t tid; - - struct kdbus_creds fake_creds; - struct kdbus_pids fake_pids; - char *fake_label; - - char *cgroup_root; - - char *description; - - size_t bloom_size; - unsigned bloom_n_hash; - - sd_bus_track *track_queue; - - LIST_HEAD(sd_bus_slot, slots); -}; - -#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) - -#define BUS_WQUEUE_MAX 1024 -#define BUS_RQUEUE_MAX 64*1024 - -#define BUS_MESSAGE_SIZE_MAX (64*1024*1024) -#define BUS_AUTH_SIZE_MAX (64*1024) - -#define BUS_CONTAINER_DEPTH 128 - -/* Defined by the specification as maximum size of an array in - * bytes */ -#define BUS_ARRAY_MAX_SIZE 67108864 - -#define BUS_FDS_MAX 1024 - -#define BUS_EXEC_ARGV_MAX 256 - -bool interface_name_is_valid(const char *p) _pure_; -bool service_name_is_valid(const char *p) _pure_; -char* service_name_startswith(const char *a, const char *b); -bool member_name_is_valid(const char *p) _pure_; -bool object_path_is_valid(const char *p) _pure_; -char *object_path_startswith(const char *a, const char *b) _pure_; - -bool namespace_complex_pattern(const char *pattern, const char *value) _pure_; -bool path_complex_pattern(const char *pattern, const char *value) _pure_; - -bool namespace_simple_pattern(const char *pattern, const char *value) _pure_; -bool path_simple_pattern(const char *pattern, const char *value) _pure_; - -int bus_message_type_from_string(const char *s, uint8_t *u) _pure_; -const char *bus_message_type_to_string(uint8_t u) _pure_; - -#define error_name_is_valid interface_name_is_valid - -int bus_ensure_running(sd_bus *bus); -int bus_start_running(sd_bus *bus); -int bus_next_address(sd_bus *bus); - -int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m); - -int bus_rqueue_make_room(sd_bus *bus); - -bool bus_pid_changed(sd_bus *bus); - -char *bus_address_escape(const char *v); - -#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \ - _slash && !(_slash[(_slash) == (prefix)] = 0); \ - _slash = streq((prefix), "/") ? NULL : strrchr((prefix), '/')) - -/* If we are invoking callbacks of a bus object, ensure unreffing the - * bus from the callback doesn't destroy the object we are working - * on */ -#define BUS_DONT_DESTROY(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); -int bus_set_address_system_remote(sd_bus *b, const char *host); -int bus_set_address_system_machine(sd_bus *b, const char *machine); - -int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); - -int bus_get_root_path(sd_bus *bus); - -int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); - -#define bus_assert_return(expr, r, error) \ - do { \ - if (!assert_log(expr, #expr)) \ - return sd_bus_error_set_errno(error, r); \ - } while (false) diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c deleted file mode 100644 index 8f93edb8da..0000000000 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ /dev/null @@ -1,212 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - - zero(*i); - i->trusted = trusted; - - i->f = open_memstream(&i->introspection, &i->size); - if (!i->f) - return -ENOMEM; - - fputs(BUS_INTROSPECT_DOCTYPE - "\n", i->f); - - return 0; -} - -int introspect_write_default_interfaces(struct introspect *i, bool object_manager) { - assert(i); - - fputs(BUS_INTROSPECT_INTERFACE_PEER - BUS_INTROSPECT_INTERFACE_INTROSPECTABLE - BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f); - - if (object_manager) - fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f); - - return 0; -} - -int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { - char *node; - - assert(i); - assert(prefix); - - while ((node = set_steal_first(s))) { - const char *e; - - e = object_path_startswith(node, prefix); - if (e && e[0]) - fprintf(i->f, " \n", e); - - free(node); - } - - return 0; -} - -static void introspect_write_flags(struct introspect *i, int type, int flags) { - if (flags & SD_BUS_VTABLE_DEPRECATED) - fputs(" \n", i->f); - - if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) - fputs(" \n", i->f); - - if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) { - if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) - fputs(" \n", i->f); - - if (flags & SD_BUS_VTABLE_PROPERTY_CONST) - fputs(" \n", i->f); - else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) - fputs(" \n", i->f); - else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) - fputs(" \n", i->f); - } - - if (!i->trusted && - (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) && - !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) - fputs(" \n", i->f); -} - -static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) { - int r; - - for (;;) { - size_t l; - - if (!*signature) - return 0; - - r = signature_element_length(signature, &l); - if (r < 0) - return r; - - fprintf(i->f, " f, " direction=\"%s\"/>\n", direction); - else - fputs("/>\n", i->f); - - signature += l; - } -} - -int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { - assert(i); - assert(v); - - for (; v->type != _SD_BUS_VTABLE_END; v++) { - - /* Ignore methods, signals and properties that are - * marked "hidden", but do show the interface - * itself */ - - if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) - continue; - - switch (v->type) { - - case _SD_BUS_VTABLE_START: - if (v->flags & SD_BUS_VTABLE_DEPRECATED) - fputs(" \n", i->f); - break; - - case _SD_BUS_VTABLE_METHOD: - fprintf(i->f, " \n", v->x.method.member); - introspect_write_arguments(i, strempty(v->x.method.signature), "in"); - introspect_write_arguments(i, strempty(v->x.method.result), "out"); - introspect_write_flags(i, v->type, v->flags); - fputs(" \n", i->f); - break; - - case _SD_BUS_VTABLE_PROPERTY: - case _SD_BUS_VTABLE_WRITABLE_PROPERTY: - fprintf(i->f, " \n", - v->x.property.member, - v->x.property.signature, - v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); - introspect_write_flags(i, v->type, v->flags); - fputs(" \n", i->f); - break; - - case _SD_BUS_VTABLE_SIGNAL: - fprintf(i->f, " \n", v->x.signal.member); - introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); - introspect_write_flags(i, v->type, v->flags); - fputs(" \n", i->f); - break; - } - - } - - return 0; -} - -int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) { - sd_bus_message *q; - int r; - - assert(i); - assert(m); - assert(reply); - - fputs("\n", i->f); - - r = fflush_and_check(i->f); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(m, &q); - if (r < 0) - return r; - - r = sd_bus_message_append(q, "s", i->introspection); - if (r < 0) { - sd_bus_message_unref(q); - return r; - } - - *reply = q; - return 0; -} - -void introspect_free(struct introspect *i) { - assert(i); - - safe_fclose(i->f); - - free(i->introspection); - zero(*i); -} diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h deleted file mode 100644 index 8e2f3800ca..0000000000 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -#include "set.h" - -struct introspect { - FILE *f; - char *introspection; - size_t size; - bool trusted; -}; - -int introspect_begin(struct introspect *i, bool trusted); -int introspect_write_default_interfaces(struct introspect *i, bool object_manager); -int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix); -int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v); -int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply); -void introspect_free(struct introspect *i); diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c deleted file mode 100644 index 59398b841d..0000000000 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ /dev/null @@ -1,1782 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif - -#include -#include -#include -#include - -/* 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 -#undef basename - -#include "alloc-util.h" -#include "bus-bloom.h" -#include "bus-internal.h" -#include "bus-kernel.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)) - -int bus_kernel_parse_unique_name(const char *s, uint64_t *id) { - int r; - - assert(s); - assert(id); - - if (!startswith(s, ":1.")) - return 0; - - r = safe_atou64(s + 3, id); - if (r < 0) - return r; - - return 1; -} - -static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) { - assert(d); - assert(sz > 0); - - *d = ALIGN8_PTR(*d); - - /* Note that p can be NULL, which encodes a region full of - * zeroes, which is useful to optimize certain padding - * conditions */ - - (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec); - (*d)->type = KDBUS_ITEM_PAYLOAD_VEC; - (*d)->vec.address = PTR_TO_UINT64(p); - (*d)->vec.size = sz; - - *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); -} - -static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t start, size_t sz) { - assert(d); - assert(memfd >= 0); - assert(sz > 0); - - *d = ALIGN8_PTR(*d); - (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd); - (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD; - (*d)->memfd.fd = memfd; - (*d)->memfd.start = start; - (*d)->memfd.size = sz; - - *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); -} - -static void append_destination(struct kdbus_item **d, const char *s, size_t length) { - assert(d); - assert(s); - - *d = ALIGN8_PTR(*d); - - (*d)->size = offsetof(struct kdbus_item, str) + length + 1; - (*d)->type = KDBUS_ITEM_DST_NAME; - memcpy((*d)->str, s, length + 1); - - *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); -} - -static struct kdbus_bloom_filter *append_bloom(struct kdbus_item **d, size_t length) { - struct kdbus_item *i; - - assert(d); - - i = ALIGN8_PTR(*d); - - i->size = offsetof(struct kdbus_item, bloom_filter) + - offsetof(struct kdbus_bloom_filter, data) + - length; - i->type = KDBUS_ITEM_BLOOM_FILTER; - - *d = (struct kdbus_item *) ((uint8_t*) i + i->size); - - return &i->bloom_filter; -} - -static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) { - assert(d); - assert(fds); - assert(n_fds > 0); - - *d = ALIGN8_PTR(*d); - (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds; - (*d)->type = KDBUS_ITEM_FDS; - memcpy((*d)->fds, fds, sizeof(int) * n_fds); - - *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); -} - -static void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { - char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; - char *e; - - assert(data); - assert(size > 0); - assert(i < 64); - assert(t); - - e = stpcpy(buf, "arg"); - if (i < 10) - *(e++) = '0' + (char) i; - else { - *(e++) = '0' + (char) (i / 10); - *(e++) = '0' + (char) (i % 10); - } - - *e = 0; - bloom_add_pair(data, size, n_hash, buf, t); - - strcpy(e, "-dot-prefix"); - bloom_add_prefixes(data, size, n_hash, buf, t, '.'); - strcpy(e, "-slash-prefix"); - bloom_add_prefixes(data, size, n_hash, buf, t, '/'); -} - -static void add_bloom_arg_has(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { - char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; - char *e; - - assert(data); - assert(size > 0); - assert(i < 64); - assert(t); - - e = stpcpy(buf, "arg"); - if (i < 10) - *(e++) = '0' + (char) i; - else { - *(e++) = '0' + (char) (i / 10); - *(e++) = '0' + (char) (i % 10); - } - - strcpy(e, "-has"); - bloom_add_pair(data, size, n_hash, buf, t); -} - -static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { - void *data; - unsigned i; - int r; - - assert(m); - assert(bloom); - - data = bloom->data; - memzero(data, m->bus->bloom_size); - bloom->generation = 0; - - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type)); - - if (m->interface) - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "interface", m->interface); - if (m->member) - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "member", m->member); - if (m->path) { - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path", m->path); - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path); - bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/'); - } - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - for (i = 0; i < 64; i++) { - const char *t, *contents; - char type; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - - if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { - - /* The bloom filter includes simple strings of any kind */ - r = sd_bus_message_read_basic(m, type, &t); - if (r < 0) - return r; - - add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); - } - - if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) { - - /* As well as array of simple strings of any kinds */ - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) - add_bloom_arg_has(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - } else - /* Stop adding to bloom filter as soon as we - * run into the first argument we cannot add - * to it. */ - break; - } - - return 0; -} - -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 = false; - uint64_t dst_id; - size_t sz, dl; - unsigned i; - int r; - - assert(b); - assert(m); - assert(m->sealed); - - /* We put this together only once, if this message is reused - * we reuse the earlier-built version */ - if (m->kdbus) - return 0; - - destination = m->destination ?: m->destination_ptr; - - if (destination) { - r = bus_kernel_parse_unique_name(destination, &dst_id); - if (r < 0) - return r; - 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 - dst_id = KDBUS_DST_ID_BROADCAST; - - sz = offsetof(struct kdbus_msg, items); - - /* Add in fixed header, fields header and payload */ - sz += (1 + m->n_body_parts) * ALIGN8(offsetof(struct kdbus_item, vec) + - MAX(sizeof(struct kdbus_vec), - sizeof(struct kdbus_memfd))); - - /* Add space for bloom filter */ - sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) + - offsetof(struct kdbus_bloom_filter, data) + - m->bus->bloom_size); - - /* Add in well-known destination header */ - if (well_known) { - dl = strlen(destination); - sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1); - } - - /* Add space for unix fds */ - if (m->n_fds > 0) - sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds); - - m->kdbus = memalign(8, sz); - if (!m->kdbus) { - r = -ENOMEM; - goto fail; - } - - m->free_kdbus = true; - memzero(m->kdbus, sz); - - m->kdbus->flags = - ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) | - ((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); - - 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; - - if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - m->kdbus->cookie_reply = m->reply_cookie; - else { - struct timespec now; - - assert_se(clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == 0); - m->kdbus->timeout_ns = now.tv_sec * NSEC_PER_SEC + now.tv_nsec + - m->timeout * NSEC_PER_USEC; - } - - d = m->kdbus->items; - - if (well_known) - append_destination(&d, destination, dl); - - append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m)); - - MESSAGE_FOREACH_PART(part, i, m) { - if (part->is_zero) { - /* If this is padding then simply send a - * vector with a NULL data pointer which the - * kernel will just pass through. This is the - * most efficient way to encode zeroes */ - - append_payload_vec(&d, NULL, part->size); - continue; - } - - if (part->memfd >= 0 && part->sealed && destination) { - /* Try to send a memfd, if the part is - * sealed and this is not a broadcast. Since we can only */ - - append_payload_memfd(&d, part->memfd, part->memfd_offset, part->size); - continue; - } - - /* Otherwise, let's send a vector to the actual data. - * For that, we need to map it first. */ - r = bus_body_part_map(part); - if (r < 0) - goto fail; - - append_payload_vec(&d, part->data, part->size); - } - - if (m->header->type == SD_BUS_MESSAGE_SIGNAL) { - struct kdbus_bloom_filter *bloom; - - bloom = append_bloom(&d, m->bus->bloom_size); - r = bus_message_setup_bloom(m, bloom); - if (r < 0) - goto fail; - } - - if (m->n_fds > 0) - append_fds(&d, m->fds, m->n_fds); - - m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus; - assert(m->kdbus->size <= sz); - - return 0; - -fail: - m->poisoned = true; - return r; -} - -static void unset_memfds(struct sd_bus_message *m) { - struct bus_body_part *part; - unsigned i; - - assert(m); - - /* Make sure the memfds are not freed twice */ - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0) - part->memfd = -1; -} - -static void message_set_timestamp(sd_bus *bus, sd_bus_message *m, const struct kdbus_timestamp *ts) { - assert(bus); - assert(m); - - if (!ts) - return; - - if (!(bus->attach_flags & KDBUS_ATTACH_TIMESTAMP)) - return; - - m->realtime = ts->realtime_ns / NSEC_PER_USEC; - m->monotonic = ts->monotonic_ns / NSEC_PER_USEC; - m->seqnum = ts->seqnum; -} - -static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { - sd_bus_message *m = NULL; - struct kdbus_item *d; - unsigned n_fds = 0; - _cleanup_free_ int *fds = NULL; - struct bus_header *header = NULL; - void *footer = NULL; - size_t header_size = 0, footer_size = 0; - size_t n_bytes = 0, idx = 0; - const char *destination = NULL, *seclabel = NULL; - bool last_was_memfd = false; - int r; - - assert(bus); - assert(k); - assert(k->payload_type == KDBUS_PAYLOAD_DBUS); - - KDBUS_ITEM_FOREACH(d, k, items) { - size_t l; - - l = d->size - offsetof(struct kdbus_item, data); - - switch (d->type) { - - case KDBUS_ITEM_PAYLOAD_OFF: - if (!header) { - header = (struct bus_header*)((uint8_t*) k + d->vec.offset); - header_size = d->vec.size; - } - - footer = (uint8_t*) k + d->vec.offset; - footer_size = d->vec.size; - - n_bytes += d->vec.size; - last_was_memfd = false; - break; - - case KDBUS_ITEM_PAYLOAD_MEMFD: - if (!header) /* memfd cannot be first part */ - return -EBADMSG; - - n_bytes += d->memfd.size; - last_was_memfd = true; - break; - - case KDBUS_ITEM_FDS: { - int *f; - unsigned j; - - j = l / sizeof(int); - f = realloc(fds, sizeof(int) * (n_fds + j)); - if (!f) - return -ENOMEM; - - fds = f; - memcpy(fds + n_fds, d->fds, sizeof(int) * j); - n_fds += j; - break; - } - - case KDBUS_ITEM_SECLABEL: - seclabel = d->str; - break; - } - } - - if (last_was_memfd) /* memfd cannot be last part */ - return -EBADMSG; - - if (!header) - return -EBADMSG; - - if (header_size < sizeof(struct bus_header)) - return -EBADMSG; - - /* on kdbus we only speak native endian gvariant, never dbus1 - * marshalling or reverse endian */ - if (header->version != 2 || - header->endian != BUS_NATIVE_ENDIAN) - return -EPROTOTYPE; - - r = bus_message_from_header( - bus, - header, header_size, - footer, footer_size, - n_bytes, - fds, n_fds, - seclabel, 0, &m); - if (r < 0) - return r; - - /* The well-known names list is different from the other - credentials. If we asked for it, but nothing is there, this - means that the list of well-known names is simply empty, not - that we lack any data */ - - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; - - KDBUS_ITEM_FOREACH(d, k, items) { - size_t l; - - l = d->size - offsetof(struct kdbus_item, data); - - switch (d->type) { - - case KDBUS_ITEM_PAYLOAD_OFF: { - size_t begin_body; - - begin_body = BUS_MESSAGE_BODY_BEGIN(m); - - if (idx + d->vec.size > begin_body) { - struct bus_body_part *part; - - /* Contains body material */ - - part = message_append_part(m); - if (!part) { - r = -ENOMEM; - goto fail; - } - - /* A -1 offset is NUL padding. */ - part->is_zero = d->vec.offset == ~0ULL; - - if (idx >= begin_body) { - if (!part->is_zero) - part->data = (uint8_t* )k + d->vec.offset; - part->size = d->vec.size; - } else { - if (!part->is_zero) - part->data = (uint8_t*) k + d->vec.offset + (begin_body - idx); - part->size = d->vec.size - (begin_body - idx); - } - - part->sealed = true; - } - - idx += d->vec.size; - break; - } - - case KDBUS_ITEM_PAYLOAD_MEMFD: { - struct bus_body_part *part; - - if (idx < BUS_MESSAGE_BODY_BEGIN(m)) { - r = -EBADMSG; - goto fail; - } - - part = message_append_part(m); - if (!part) { - r = -ENOMEM; - goto fail; - } - - part->memfd = d->memfd.fd; - part->memfd_offset = d->memfd.start; - part->size = d->memfd.size; - part->sealed = true; - - idx += d->memfd.size; - break; - } - - case KDBUS_ITEM_PIDS: - - /* The PID/TID might be missing, when the data - * is faked by a bus proxy and it lacks that - * information about the real client (since - * SO_PEERCRED is used for that). Also kernel - * namespacing might make some of this data - * unavailable when untranslatable. */ - - if (d->pids.pid > 0) { - m->creds.pid = (pid_t) d->pids.pid; - m->creds.mask |= SD_BUS_CREDS_PID & bus->creds_mask; - } - - if (d->pids.tid > 0) { - m->creds.tid = (pid_t) d->pids.tid; - m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; - } - - if (d->pids.ppid > 0) { - m->creds.ppid = (pid_t) d->pids.ppid; - m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; - } else if (d->pids.pid == 1) { - m->creds.ppid = 0; - m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; - } - - break; - - case KDBUS_ITEM_CREDS: - - /* EUID/SUID/FSUID/EGID/SGID/FSGID might be - * missing too (see above). */ - - if ((uid_t) d->creds.uid != UID_INVALID) { - m->creds.uid = (uid_t) d->creds.uid; - m->creds.mask |= SD_BUS_CREDS_UID & bus->creds_mask; - } - - if ((uid_t) d->creds.euid != UID_INVALID) { - m->creds.euid = (uid_t) d->creds.euid; - m->creds.mask |= SD_BUS_CREDS_EUID & bus->creds_mask; - } - - if ((uid_t) d->creds.suid != UID_INVALID) { - m->creds.suid = (uid_t) d->creds.suid; - m->creds.mask |= SD_BUS_CREDS_SUID & bus->creds_mask; - } - - if ((uid_t) d->creds.fsuid != UID_INVALID) { - m->creds.fsuid = (uid_t) d->creds.fsuid; - m->creds.mask |= SD_BUS_CREDS_FSUID & bus->creds_mask; - } - - if ((gid_t) d->creds.gid != GID_INVALID) { - m->creds.gid = (gid_t) d->creds.gid; - m->creds.mask |= SD_BUS_CREDS_GID & bus->creds_mask; - } - - if ((gid_t) d->creds.egid != GID_INVALID) { - m->creds.egid = (gid_t) d->creds.egid; - m->creds.mask |= SD_BUS_CREDS_EGID & bus->creds_mask; - } - - if ((gid_t) d->creds.sgid != GID_INVALID) { - m->creds.sgid = (gid_t) d->creds.sgid; - m->creds.mask |= SD_BUS_CREDS_SGID & bus->creds_mask; - } - - if ((gid_t) d->creds.fsgid != GID_INVALID) { - m->creds.fsgid = (gid_t) d->creds.fsgid; - m->creds.mask |= SD_BUS_CREDS_FSGID & bus->creds_mask; - } - - break; - - case KDBUS_ITEM_TIMESTAMP: - message_set_timestamp(bus, m, &d->timestamp); - break; - - case KDBUS_ITEM_PID_COMM: - m->creds.comm = d->str; - m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask; - break; - - case KDBUS_ITEM_TID_COMM: - m->creds.tid_comm = d->str; - m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask; - break; - - case KDBUS_ITEM_EXE: - m->creds.exe = d->str; - m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask; - break; - - case KDBUS_ITEM_CMDLINE: - m->creds.cmdline = d->str; - m->creds.cmdline_size = l; - m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask; - break; - - case KDBUS_ITEM_CGROUP: - m->creds.cgroup = d->str; - m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask; - - r = bus_get_root_path(bus); - if (r < 0) - goto fail; - - m->creds.cgroup_root = bus->cgroup_root; - break; - - case KDBUS_ITEM_AUDIT: - m->creds.audit_session_id = (uint32_t) d->audit.sessionid; - m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; - - m->creds.audit_login_uid = (uid_t) d->audit.loginuid; - m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; - break; - - case KDBUS_ITEM_CAPS: - if (d->caps.last_cap != cap_last_cap() || - d->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(d->caps.last_cap, 32U) * 4 * 4) { - r = -EBADMSG; - goto fail; - } - - m->creds.capability = d->caps.caps; - m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask; - break; - - case KDBUS_ITEM_DST_NAME: - if (!service_name_is_valid(d->str)) { - r = -EBADMSG; - goto fail; - } - - destination = d->str; - break; - - case KDBUS_ITEM_OWNED_NAME: - if (!service_name_is_valid(d->name.name)) { - r = -EBADMSG; - goto fail; - } - - if (bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - char **wkn; - size_t n; - - /* We just extend the array here, but - * do not allocate the strings inside - * of it, instead we just point to our - * buffer directly. */ - n = strv_length(m->creds.well_known_names); - wkn = realloc(m->creds.well_known_names, (n + 2) * sizeof(char*)); - if (!wkn) { - r = -ENOMEM; - goto fail; - } - - wkn[n] = d->name.name; - wkn[n+1] = NULL; - m->creds.well_known_names = wkn; - - m->creds.mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; - } - break; - - case KDBUS_ITEM_CONN_DESCRIPTION: - m->creds.description = d->str; - m->creds.mask |= SD_BUS_CREDS_DESCRIPTION & bus->creds_mask; - break; - - case KDBUS_ITEM_AUXGROUPS: - - if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { - size_t i, n; - gid_t *g; - - n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); - g = new(gid_t, n); - if (!g) { - r = -ENOMEM; - goto fail; - } - - for (i = 0; i < n; i++) - g[i] = d->data64[i]; - - m->creds.supplementary_gids = g; - m->creds.n_supplementary_gids = n; - m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; - } - - break; - - case KDBUS_ITEM_FDS: - case KDBUS_ITEM_SECLABEL: - case KDBUS_ITEM_BLOOM_FILTER: - break; - - default: - log_debug("Got unknown field from kernel %llu", d->type); - } - } - - /* If we requested the list of well-known names to be appended - * and the sender had none no item for it will be - * attached. However, this does *not* mean that the kernel - * didn't want to provide this information to us. Hence, let's - * explicitly mark this information as available if it was - * requested. */ - m->creds.mask |= bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; - - r = bus_message_parse_fields(m); - if (r < 0) - goto fail; - - /* Refuse messages if kdbus and dbus1 cookie doesn't match up */ - if ((uint64_t) m->header->dbus2.cookie != k->cookie) { - r = -EBADMSG; - goto fail; - } - - /* Refuse messages where the reply flag doesn't match up */ - if (!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) != !!(k->flags & KDBUS_MSG_EXPECT_REPLY)) { - r = -EBADMSG; - goto fail; - } - - /* Refuse reply messages where the reply cookie doesn't match up */ - if ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) && m->reply_cookie != k->cookie_reply) { - r = -EBADMSG; - goto fail; - } - - /* Refuse messages where the autostart flag doesn't match up */ - if (!(m->header->flags & BUS_MESSAGE_NO_AUTO_START) != !(k->flags & KDBUS_MSG_NO_AUTO_START)) { - r = -EBADMSG; - goto fail; - } - - /* Override information from the user header with data from the kernel */ - if (k->src_id == KDBUS_SRC_ID_KERNEL) - bus_message_set_sender_driver(bus, m); - else { - xsprintf(m->sender_buffer, ":1.%llu", - (unsigned long long)k->src_id); - m->sender = m->creds.unique_name = m->sender_buffer; - } - - if (destination) - m->destination = destination; - else if (k->dst_id == KDBUS_DST_ID_BROADCAST) - m->destination = NULL; - 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 { - xsprintf(m->destination_buffer, ":1.%llu", - (unsigned long long)k->dst_id); - m->destination = m->destination_buffer; - } - - /* We take possession of the kmsg struct now */ - m->kdbus = k; - m->release_kdbus = true; - m->free_fds = true; - fds = NULL; - - bus->rqueue[bus->rqueue_size++] = m; - - return 1; - -fail: - unset_memfds(m); - sd_bus_message_unref(m); - - return r; -} - -int bus_kernel_take_fd(sd_bus *b) { - struct kdbus_bloom_parameter *bloom = NULL; - struct kdbus_item *items, *item; - struct kdbus_cmd_hello *hello; - _cleanup_free_ char *g = NULL; - const char *name; - size_t l = 0, m = 0, sz; - int r; - - assert(b); - - if (b->is_server) - return -EINVAL; - - b->use_memfd = 1; - - if (b->description) { - g = bus_label_escape(b->description); - if (!g) - return -ENOMEM; - - name = g; - } else { - char pr[17] = {}; - - /* If no name is explicitly set, we'll include a hint - * indicating the library implementation, a hint which - * kind of bus this is and the thread name */ - - assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); - - if (isempty(pr)) { - name = b->is_system ? "sd-system" : - b->is_user ? "sd-user" : "sd"; - } else { - _cleanup_free_ char *e = NULL; - - e = bus_label_escape(pr); - if (!e) - return -ENOMEM; - - g = strappend(b->is_system ? "sd-system-" : - b->is_user ? "sd-user-" : "sd-", - e); - if (!g) - return -ENOMEM; - - name = g; - } - - b->description = bus_label_unescape(name); - if (!b->description) - return -ENOMEM; - } - - m = strlen(name); - - sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + - ALIGN8(offsetof(struct kdbus_item, str) + m + 1); - - if (b->fake_creds_valid) - sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds)); - - if (b->fake_pids_valid) - sz += ALIGN8(offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids)); - - if (b->fake_label) { - l = strlen(b->fake_label); - sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); - } - - hello = alloca0_align(sz, 8); - hello->size = sz; - hello->flags = b->hello_flags; - hello->attach_flags_send = _KDBUS_ATTACH_ANY; - hello->attach_flags_recv = b->attach_flags; - hello->pool_size = KDBUS_POOL_SIZE; - - item = hello->items; - - item->size = offsetof(struct kdbus_item, str) + m + 1; - item->type = KDBUS_ITEM_CONN_DESCRIPTION; - memcpy(item->str, name, m + 1); - item = KDBUS_ITEM_NEXT(item); - - if (b->fake_creds_valid) { - item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds); - item->type = KDBUS_ITEM_CREDS; - item->creds = b->fake_creds; - - item = KDBUS_ITEM_NEXT(item); - } - - if (b->fake_pids_valid) { - item->size = offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids); - item->type = KDBUS_ITEM_PIDS; - item->pids = b->fake_pids; - - item = KDBUS_ITEM_NEXT(item); - } - - if (b->fake_label) { - item->size = offsetof(struct kdbus_item, str) + l + 1; - item->type = KDBUS_ITEM_SECLABEL; - memcpy(item->str, b->fake_label, l+1); - } - - r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); - if (r < 0) { - if (errno == ENOTTY) - /* If the ioctl is not supported we assume that the - * API version changed in a major incompatible way, - * let's indicate an API incompatibility in this - * case. */ - return -ESOCKTNOSUPPORT; - - return -errno; - } - - if (!b->kdbus_buffer) { - b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); - if (b->kdbus_buffer == MAP_FAILED) { - b->kdbus_buffer = NULL; - r = -errno; - goto fail; - } - } - - /* The higher 32bit of the bus_flags fields are considered - * 'incompatible flags'. Refuse them all for now. */ - if (hello->bus_flags > 0xFFFFFFFFULL) { - r = -ESOCKTNOSUPPORT; - goto fail; - } - - /* extract bloom parameters from items */ - items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset); - KDBUS_FOREACH(item, items, hello->items_size) { - switch (item->type) { - case KDBUS_ITEM_BLOOM_PARAMETER: - bloom = &item->bloom_parameter; - break; - } - } - - if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) { - r = -EOPNOTSUPP; - goto fail; - } - - b->bloom_size = (size_t) bloom->size; - b->bloom_n_hash = (unsigned) bloom->n_hash; - - if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) { - r = -ENOMEM; - goto fail; - } - - b->unique_id = hello->id; - - b->is_kernel = true; - b->bus_client = true; - b->can_fds = !!(hello->flags & KDBUS_HELLO_ACCEPT_FD); - b->message_version = 2; - b->message_endian = BUS_NATIVE_ENDIAN; - - /* the kernel told us the UUID of the underlying bus */ - memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes)); - - /* free returned items */ - (void) bus_kernel_cmd_free(b, hello->offset); - return bus_start_running(b); - -fail: - (void) bus_kernel_cmd_free(b, hello->offset); - return r; -} - -int bus_kernel_connect(sd_bus *b) { - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->kernel); - - if (b->is_server) - return -EINVAL; - - b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (b->input_fd < 0) - return -errno; - - b->output_fd = b->input_fd; - - return bus_kernel_take_fd(b); -} - -int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset) { - struct kdbus_cmd_free cmd = { - .size = sizeof(cmd), - .offset = offset, - }; - int r; - - assert(bus); - assert(bus->is_kernel); - - r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); - if (r < 0) - return -errno; - - return 0; -} - -static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { - struct kdbus_item *d; - - assert(bus); - assert(k); - - KDBUS_ITEM_FOREACH(d, k, items) { - if (d->type == KDBUS_ITEM_FDS) - close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); - else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) - safe_close(d->memfd.fd); - } - - bus_kernel_cmd_free(bus, (uint8_t*) k - (uint8_t*) bus->kdbus_buffer); -} - -int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) { - struct kdbus_cmd_send cmd = { }; - int r; - - assert(bus); - assert(m); - assert(bus->state == BUS_RUNNING); - - /* If we can't deliver, we want room for the error message */ - r = bus_rqueue_make_room(bus); - if (r < 0) - return r; - - r = bus_message_setup_kmsg(bus, m); - if (r < 0) - return r; - - cmd.size = sizeof(cmd); - cmd.msg_address = (uintptr_t)m->kdbus; - - /* If this is a synchronous method call, then let's tell the - * kernel, so that it can pass CPU time/scheduling to the - * destination for the time, if it wants to. If we - * synchronously wait for the result anyway, we won't need CPU - * anyway. */ - if (hint_sync_call) { - m->kdbus->flags |= KDBUS_MSG_EXPECT_REPLY; - cmd.flags |= KDBUS_SEND_SYNC_REPLY; - } - - r = ioctl(bus->output_fd, KDBUS_CMD_SEND, &cmd); - if (r < 0) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus_message *reply; - - if (errno == EAGAIN || errno == EINTR) - return 0; - else if (errno == ENXIO || errno == ESRCH) { - - /* ENXIO: unique name not known - * ESRCH: well-known name not known */ - - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination); - else { - log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination); - return 0; - } - - } else if (errno == EADDRNOTAVAIL) { - - /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */ - - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination); - else { - log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination); - return 0; - } - } else - return -errno; - - r = bus_message_new_synthetic_error( - bus, - BUS_MESSAGE_COOKIE(m), - &error, - &reply); - - if (r < 0) - return r; - - r = bus_seal_synthetic_message(bus, reply); - if (r < 0) - return r; - - bus->rqueue[bus->rqueue_size++] = reply; - - } else if (hint_sync_call) { - struct kdbus_msg *k; - - k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + cmd.reply.offset); - assert(k); - - if (k->payload_type == KDBUS_PAYLOAD_DBUS) { - - r = bus_kernel_make_message(bus, k); - if (r < 0) { - close_kdbus_msg(bus, k); - - /* Anybody can send us invalid messages, let's just drop them. */ - if (r == -EBADMSG || r == -EPROTOTYPE) - log_debug_errno(r, "Ignoring invalid synchronous reply: %m"); - else - return r; - } - } else { - log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); - close_kdbus_msg(bus, k); - } - } - - return 1; -} - -static int push_name_owner_changed( - sd_bus *bus, - const char *name, - const char *old_owner, - const char *new_owner, - const struct kdbus_timestamp *ts) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameOwnerChanged"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); - if (r < 0) - return r; - - bus_message_set_sender_driver(bus, m); - message_set_timestamp(bus, m, ts); - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - bus->rqueue[bus->rqueue_size++] = m; - m = NULL; - - return 1; -} - -static int translate_name_change( - sd_bus *bus, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX]; - - assert(bus); - assert(k); - assert(d); - - if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) - old_owner[0] = 0; - else - sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old_id.id); - - if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) { - - if (isempty(old_owner)) - return 0; - - new_owner[0] = 0; - } else - sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new_id.id); - - return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner, ts); -} - -static int translate_id_change( - sd_bus *bus, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - char owner[UNIQUE_NAME_MAX]; - - assert(bus); - assert(k); - assert(d); - - sprintf(owner, ":1.%llu", d->id_change.id); - - return push_name_owner_changed( - bus, owner, - d->type == KDBUS_ITEM_ID_ADD ? NULL : owner, - d->type == KDBUS_ITEM_ID_ADD ? owner : NULL, - ts); -} - -static int translate_reply( - sd_bus *bus, - const struct kdbus_msg *k, - const struct kdbus_item *d, - const struct kdbus_timestamp *ts) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - assert(k); - assert(d); - - r = bus_message_new_synthetic_error( - bus, - k->cookie_reply, - d->type == KDBUS_ITEM_REPLY_TIMEOUT ? - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") : - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"), - &m); - if (r < 0) - return r; - - message_set_timestamp(bus, m, ts); - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - bus->rqueue[bus->rqueue_size++] = m; - m = NULL; - - return 1; -} - -static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) { - static int (* const translate[])(sd_bus *bus, const struct kdbus_msg *k, const struct kdbus_item *d, const struct kdbus_timestamp *ts) = { - [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, - - [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, - [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, - - [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, - [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, - }; - - struct kdbus_item *d, *found = NULL; - struct kdbus_timestamp *ts = NULL; - - assert(bus); - assert(k); - assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); - - KDBUS_ITEM_FOREACH(d, k, items) { - if (d->type == KDBUS_ITEM_TIMESTAMP) - ts = &d->timestamp; - else if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) { - if (found) - return -EBADMSG; - found = d; - } else - log_debug("Got unknown field from kernel %llu", d->type); - } - - if (!found) { - log_debug("Didn't find a kernel message to translate."); - return 0; - } - - return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found, ts); -} - -int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { - struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; - struct kdbus_msg *k; - int r; - - assert(bus); - - r = bus_rqueue_make_room(bus); - if (r < 0) - return r; - - if (hint_priority) { - recv.flags |= KDBUS_RECV_USE_PRIORITY; - recv.priority = priority; - } - - r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv); - if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS) - log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); - if (r < 0) { - if (errno == EAGAIN) - return 0; - - return -errno; - } - - k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.msg.offset); - if (k->payload_type == KDBUS_PAYLOAD_DBUS) { - r = bus_kernel_make_message(bus, k); - - /* Anybody can send us invalid messages, let's just drop them. */ - if (r == -EBADMSG || r == -EPROTOTYPE) { - log_debug_errno(r, "Ignoring invalid message: %m"); - r = 0; - } - - if (r <= 0) - close_kdbus_msg(bus, k); - } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL) { - r = bus_kernel_translate_message(bus, k); - close_kdbus_msg(bus, k); - } else { - log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); - r = 0; - close_kdbus_msg(bus, k); - } - - return r < 0 ? r : 1; -} - -int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated) { - struct memfd_cache *c; - int fd; - - assert(address); - assert(mapped); - assert(allocated); - - if (!bus || !bus->is_kernel) - return -EOPNOTSUPP; - - 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); - - r = memfd_new(bus->description); - if (r < 0) - return r; - - *address = NULL; - *mapped = 0; - *allocated = 0; - return r; - } - - c = &bus->memfd_cache[--bus->n_memfd_cache]; - - assert(c->fd >= 0); - assert(c->mapped == 0 || c->address); - - *address = c->address; - *mapped = c->mapped; - *allocated = c->allocated; - fd = c->fd; - - assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); - - return fd; -} - -static void close_and_munmap(int fd, void *address, size_t size) { - if (size > 0) - assert_se(munmap(address, PAGE_ALIGN(size)) >= 0); - - safe_close(fd); -} - -void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated) { - struct memfd_cache *c; - uint64_t max_mapped = PAGE_ALIGN(MEMFD_CACHE_ITEM_SIZE_MAX); - - assert(fd >= 0); - assert(mapped == 0 || address); - - if (!bus || !bus->is_kernel) { - close_and_munmap(fd, address, mapped); - return; - } - - 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); - - close_and_munmap(fd, address, mapped); - return; - } - - c = &bus->memfd_cache[bus->n_memfd_cache++]; - c->fd = fd; - c->address = address; - - /* If overly long, let's return a bit to the OS */ - if (mapped > max_mapped) { - assert_se(memfd_set_size(fd, max_mapped) >= 0); - assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0); - c->mapped = c->allocated = max_mapped; - } else { - c->mapped = mapped; - c->allocated = allocated; - } - - assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); -} - -void bus_kernel_flush_memfd(sd_bus *b) { - unsigned i; - - assert(b); - - for (i = 0; i < b->n_memfd_cache; i++) - close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped); -} - -uint64_t request_name_flags_to_kdbus(uint64_t flags) { - uint64_t f = 0; - - if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) - f |= KDBUS_NAME_ALLOW_REPLACEMENT; - - if (flags & SD_BUS_NAME_REPLACE_EXISTING) - f |= KDBUS_NAME_REPLACE_EXISTING; - - if (flags & SD_BUS_NAME_QUEUE) - f |= KDBUS_NAME_QUEUE; - - return f; -} - -uint64_t attach_flags_to_kdbus(uint64_t mask) { - uint64_t m = 0; - - if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) - m |= KDBUS_ATTACH_CREDS; - - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) - m |= KDBUS_ATTACH_PIDS; - - if (mask & SD_BUS_CREDS_COMM) - m |= KDBUS_ATTACH_PID_COMM; - - if (mask & SD_BUS_CREDS_TID_COMM) - m |= KDBUS_ATTACH_TID_COMM; - - if (mask & SD_BUS_CREDS_EXE) - m |= KDBUS_ATTACH_EXE; - - if (mask & SD_BUS_CREDS_CMDLINE) - m |= KDBUS_ATTACH_CMDLINE; - - if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) - m |= KDBUS_ATTACH_CGROUP; - - if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) - m |= KDBUS_ATTACH_CAPS; - - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) - m |= KDBUS_ATTACH_SECLABEL; - - if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) - m |= KDBUS_ATTACH_AUDIT; - - if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) - m |= KDBUS_ATTACH_NAMES; - - if (mask & SD_BUS_CREDS_DESCRIPTION) - m |= KDBUS_ATTACH_CONN_DESCRIPTION; - - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) - m |= KDBUS_ATTACH_AUXGROUPS; - - return m; -} - -int bus_kernel_create_bus(const char *name, bool world, char **s) { - struct kdbus_cmd *make; - struct kdbus_item *n; - size_t l; - int fd; - - assert(name); - assert(s); - - fd = open("/sys/fs/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - l = strlen(name); - make = alloca0_align(offsetof(struct kdbus_cmd, items) + - ALIGN8(offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter)) + - ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)) + - ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1), - 8); - - make->size = offsetof(struct kdbus_cmd, items); - - /* Set the bloom parameters */ - n = make->items; - n->size = offsetof(struct kdbus_item, bloom_parameter) + - sizeof(struct kdbus_bloom_parameter); - n->type = KDBUS_ITEM_BLOOM_PARAMETER; - n->bloom_parameter.size = DEFAULT_BLOOM_SIZE; - n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH; - - assert_cc(DEFAULT_BLOOM_SIZE > 0); - assert_cc(DEFAULT_BLOOM_N_HASH > 0); - - make->size += ALIGN8(n->size); - - /* Provide all metadata via bus-owner queries */ - n = KDBUS_ITEM_NEXT(n); - n->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; - n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); - n->data64[0] = _KDBUS_ATTACH_ANY; - make->size += ALIGN8(n->size); - - /* Set the a good name */ - n = KDBUS_ITEM_NEXT(n); - sprintf(n->str, UID_FMT "-%s", getuid(), name); - n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; - n->type = KDBUS_ITEM_MAKE_NAME; - make->size += ALIGN8(n->size); - - make->flags = world ? KDBUS_MAKE_ACCESS_WORLD : 0; - - if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { - safe_close(fd); - - /* Major API change? then the ioctls got shuffled around. */ - if (errno == ENOTTY) - return -ESOCKTNOSUPPORT; - - return -errno; - } - - if (s) { - char *p; - - p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); - if (!p) { - safe_close(fd); - return -ENOMEM; - } - - *s = p; - } - - return fd; -} - -int bus_kernel_open_bus_fd(const char *bus, char **path) { - char *p; - int fd; - size_t len; - - assert(bus); - - len = strlen("/sys/fs/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; - - if (path) { - p = new(char, len); - if (!p) - return -ENOMEM; - } else - p = newa(char, len); - - sprintf(p, "/sys/fs/kdbus/" UID_FMT "-%s/bus", getuid(), bus); - - fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) { - if (path) - free(p); - - return -errno; - } - - if (path) - *path = p; - - return fd; -} - -int bus_kernel_try_close(sd_bus *bus) { - struct kdbus_cmd byebye = { .size = sizeof(byebye) }; - - assert(bus); - assert(bus->is_kernel); - - if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE, &byebye) < 0) - return -errno; - - return 0; -} - -int bus_kernel_drop_one(int fd) { - struct kdbus_cmd_recv recv = { - .size = sizeof(recv), - .flags = KDBUS_RECV_DROP, - }; - - assert(fd >= 0); - - if (ioctl(fd, KDBUS_CMD_RECV, &recv) < 0) - return -errno; - - return 0; -} - -int bus_kernel_realize_attach_flags(sd_bus *bus) { - struct kdbus_cmd *update; - struct kdbus_item *n; - - assert(bus); - assert(bus->is_kernel); - - update = alloca0_align(offsetof(struct kdbus_cmd, items) + - ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)), - 8); - - n = update->items; - n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; - n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); - n->data64[0] = bus->attach_flags; - - update->size = - offsetof(struct kdbus_cmd, items) + - ALIGN8(n->size); - - if (ioctl(bus->input_fd, KDBUS_CMD_UPDATE, update) < 0) - return -errno; - - return 0; -} - -int bus_kernel_get_bus_name(sd_bus *bus, char **name) { - struct kdbus_cmd_info cmd = { - .size = sizeof(struct kdbus_cmd_info), - }; - struct kdbus_info *info; - struct kdbus_item *item; - char *n = NULL; - int r; - - assert(bus); - assert(name); - assert(bus->is_kernel); - - r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); - if (r < 0) - return -errno; - - info = (struct kdbus_info*) ((uint8_t*) bus->kdbus_buffer + cmd.offset); - - KDBUS_ITEM_FOREACH(item, info, items) - if (item->type == KDBUS_ITEM_MAKE_NAME) { - r = free_and_strdup(&n, item->str); - break; - } - - bus_kernel_cmd_free(bus, cmd.offset); - - if (r < 0) - return r; - if (!n) - return -EIO; - - *name = n; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h deleted file mode 100644 index 53ba3bdcf3..0000000000 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -#define KDBUS_ITEM_NEXT(item) \ - (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size)) - -#define KDBUS_ITEM_FOREACH(part, head, first) \ - for (part = (head)->first; \ - ((uint8_t *)(part) < (uint8_t *)(head) + (head)->size) && \ - ((uint8_t *) part >= (uint8_t *) head); \ - part = KDBUS_ITEM_NEXT(part)) -#define KDBUS_FOREACH(iter, first, _size) \ - for (iter = (first); \ - ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ - ((uint8_t *)(iter) >= (uint8_t *)(first)); \ - iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size))) - -#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) -#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) - -#define MEMFD_CACHE_MAX 32 - -/* When we cache a memfd block for reuse, we will truncate blocks - * longer than this in order not to keep too much data around. */ -#define MEMFD_CACHE_ITEM_SIZE_MAX (128*1024) - -/* This determines at which minimum size we prefer sending memfds over - * sending vectors */ -#define MEMFD_MIN_SIZE (512*1024) - -/* The size of the per-connection memory pool that we set up and where - * the kernel places our incoming messages */ -#define KDBUS_POOL_SIZE (16*1024*1024) - -struct memfd_cache { - int fd; - void *address; - size_t mapped; - size_t allocated; -}; - -int bus_kernel_connect(sd_bus *b); -int bus_kernel_take_fd(sd_bus *b); - -int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call); -int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority); - -int bus_kernel_open_bus_fd(const char *bus, char **path); - -int bus_kernel_create_bus(const char *name, bool world, char **s); -int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **path); - -int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated); -void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated); - -void bus_kernel_flush_memfd(sd_bus *bus); - -int bus_kernel_parse_unique_name(const char *s, uint64_t *id); - -uint64_t request_name_flags_to_kdbus(uint64_t sd_bus_flags); -uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); - -int bus_kernel_try_close(sd_bus *bus); - -int bus_kernel_drop_one(int fd); - -int bus_kernel_realize_attach_flags(sd_bus *bus); - -int bus_kernel_get_bus_name(sd_bus *bus, char **name); - -int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c deleted file mode 100644 index db01f21135..0000000000 --- a/src/libsystemd/sd-bus/bus-match.c +++ /dev/null @@ -1,1221 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-internal.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: - * - * A: type=signal,sender=foo,interface=bar - * B: type=signal,sender=quux,interface=fips - * C: type=signal,sender=quux,interface=waldo - * D: type=signal,member=test - * E: sender=miau - * F: type=signal - * G: type=signal - * - * results in this tree: - * - * BUS_MATCH_ROOT - * + BUS_MATCH_MESSAGE_TYPE - * | ` BUS_MATCH_VALUE: value == signal - * | + DBUS_MATCH_SENDER - * | | + BUS_MATCH_VALUE: value == foo - * | | | ` DBUS_MATCH_INTERFACE - * | | | ` BUS_MATCH_VALUE: value == bar - * | | | ` BUS_MATCH_LEAF: A - * | | ` BUS_MATCH_VALUE: value == quux - * | | ` DBUS_MATCH_INTERFACE - * | | | BUS_MATCH_VALUE: value == fips - * | | | ` BUS_MATCH_LEAF: B - * | | ` BUS_MATCH_VALUE: value == waldo - * | | ` BUS_MATCH_LEAF: C - * | + DBUS_MATCH_MEMBER - * | | ` BUS_MATCH_VALUE: value == test - * | | ` BUS_MATCH_LEAF: D - * | + BUS_MATCH_LEAF: F - * | ` BUS_MATCH_LEAF: G - * ` BUS_MATCH_SENDER - * ` BUS_MATCH_VALUE: value == miau - * ` BUS_MATCH_LEAF: E - */ - -static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) { - return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST; -} - -static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) { - return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) || - (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) || - (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST); -} - -static void bus_match_node_free(struct bus_match_node *node) { - assert(node); - assert(node->parent); - assert(!node->child); - assert(node->type != BUS_MATCH_ROOT); - assert(node->type < _BUS_MATCH_NODE_TYPE_MAX); - - if (node->parent->child) { - /* We are apparently linked into the parent's child - * list. Let's remove us from there. */ - if (node->prev) { - assert(node->prev->next == node); - node->prev->next = node->next; - } else { - assert(node->parent->child == node); - node->parent->child = node->next; - } - - if (node->next) - node->next->prev = node->prev; - } - - if (node->type == BUS_MATCH_VALUE) { - /* We might be in the parent's hash table, so clean - * this up */ - - if (node->parent->type == BUS_MATCH_MESSAGE_TYPE) - hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8)); - else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str) - hashmap_remove(node->parent->compare.children, node->value.str); - - free(node->value.str); - } - - if (BUS_MATCH_IS_COMPARE(node->type)) { - assert(hashmap_isempty(node->compare.children)); - hashmap_free(node->compare.children); - } - - free(node); -} - -static bool bus_match_node_maybe_free(struct bus_match_node *node) { - assert(node); - - if (node->type == BUS_MATCH_ROOT) - return false; - - if (node->child) - return false; - - if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children)) - return true; - - bus_match_node_free(node); - return true; -} - -static bool value_node_test( - struct bus_match_node *node, - enum bus_match_node_type parent_type, - uint8_t value_u8, - const char *value_str, - char **value_strv, - sd_bus_message *m) { - - assert(node); - assert(node->type == BUS_MATCH_VALUE); - - /* Tests parameters against this value node, doing prefix - * magic and stuff. */ - - switch (parent_type) { - - case BUS_MATCH_MESSAGE_TYPE: - return node->value.u8 == value_u8; - - case BUS_MATCH_SENDER: - if (streq_ptr(node->value.str, value_str)) - return true; - - if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - char **i; - - /* on kdbus we have the well known names list - * in the credentials, let's make use of that - * for an accurate match */ - - STRV_FOREACH(i, m->creds.well_known_names) - if (streq_ptr(node->value.str, *i)) - return true; - - } else { - - /* If we don't have kdbus, we don't know the - * well-known names of the senders. In that, - * let's just hope that dbus-daemon doesn't - * send us stuff we didn't want. */ - - if (node->value.str[0] != ':' && value_str && value_str[0] == ':') - return true; - } - - return false; - - case BUS_MATCH_DESTINATION: - case BUS_MATCH_INTERFACE: - case BUS_MATCH_MEMBER: - case BUS_MATCH_PATH: - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - - if (value_str) - return streq_ptr(node->value.str, value_str); - - return false; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { - char **i; - - STRV_FOREACH(i, value_strv) - if (streq_ptr(node->value.str, *i)) - return true; - - return false; - } - - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - if (value_str) - return namespace_simple_pattern(node->value.str, value_str); - - return false; - - case BUS_MATCH_PATH_NAMESPACE: - return path_simple_pattern(node->value.str, value_str); - - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - if (value_str) - return path_complex_pattern(node->value.str, value_str); - - return false; - - default: - assert_not_reached("Invalid node type"); - } -} - -static bool value_node_same( - struct bus_match_node *node, - enum bus_match_node_type parent_type, - uint8_t value_u8, - const char *value_str) { - - /* Tests parameters against this value node, not doing prefix - * magic and stuff, i.e. this one actually compares the match - * itself. */ - - assert(node); - assert(node->type == BUS_MATCH_VALUE); - - switch (parent_type) { - - case BUS_MATCH_MESSAGE_TYPE: - return node->value.u8 == value_u8; - - case BUS_MATCH_SENDER: - case BUS_MATCH_DESTINATION: - case BUS_MATCH_INTERFACE: - case BUS_MATCH_MEMBER: - case BUS_MATCH_PATH: - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - case BUS_MATCH_PATH_NAMESPACE: - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - return streq(node->value.str, value_str); - - default: - assert_not_reached("Invalid node type"); - } -} - -int bus_match_run( - sd_bus *bus, - struct bus_match_node *node, - sd_bus_message *m) { - - _cleanup_strv_free_ char **test_strv = NULL; - const char *test_str = NULL; - uint8_t test_u8 = 0; - int r; - - assert(m); - - if (!node) - return 0; - - if (bus && bus->match_callbacks_modified) - return 0; - - /* Not these special semantics: when traversing the tree we - * usually let bus_match_run() when called for a node - * recursively invoke bus_match_run(). There's are two - * exceptions here though, which are BUS_NODE_ROOT (which - * cannot have a sibling), and BUS_NODE_VALUE (whose siblings - * are invoked anyway by its parent. */ - - switch (node->type) { - - case BUS_MATCH_ROOT: - - /* Run all children. Since we cannot have any siblings - * we won't call any. The children of the root node - * are compares or leaves, they will automatically - * call their siblings. */ - return bus_match_run(bus, node->child, m); - - case BUS_MATCH_VALUE: - - /* Run all children. We don't execute any siblings, we - * assume our caller does that. The children of value - * nodes are compares or leaves, they will - * automatically call their siblings */ - - assert(node->child); - return bus_match_run(bus, node->child, m); - - case BUS_MATCH_LEAF: - - if (bus) { - if (node->leaf.callback->last_iteration == bus->iteration_counter) - return 0; - - node->leaf.callback->last_iteration = bus->iteration_counter; - } - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - /* Run the callback. And then invoke siblings. */ - if (node->leaf.callback->callback) { - _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); - if (bus) { - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = node->leaf.callback->callback; - bus->current_userdata = slot->userdata; - } - r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); - if (bus) { - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - } - - r = bus_maybe_reply_error(m, r, &error_buffer); - if (r != 0) - return r; - - if (bus && bus->match_callbacks_modified) - return 0; - } - - return bus_match_run(bus, node->next, m); - - case BUS_MATCH_MESSAGE_TYPE: - test_u8 = m->header->type; - break; - - case BUS_MATCH_SENDER: - test_str = m->sender; - /* FIXME: resolve test_str from a well-known to a unique name first */ - break; - - case BUS_MATCH_DESTINATION: - test_str = m->destination; - break; - - case BUS_MATCH_INTERFACE: - test_str = m->interface; - break; - - case BUS_MATCH_MEMBER: - test_str = m->member; - break; - - case BUS_MATCH_PATH: - case BUS_MATCH_PATH_NAMESPACE: - test_str = m->path; - break; - - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); - break; - - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); - break; - - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); - break; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: - (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); - break; - - default: - assert_not_reached("Unknown match type."); - } - - if (BUS_MATCH_CAN_HASH(node->type)) { - struct bus_match_node *found; - - /* Lookup via hash table, nice! So let's jump directly. */ - - if (test_str) - found = hashmap_get(node->compare.children, test_str); - else if (test_strv) { - char **i; - - STRV_FOREACH(i, test_strv) { - found = hashmap_get(node->compare.children, *i); - if (found) { - r = bus_match_run(bus, found, m); - if (r != 0) - return r; - } - } - - found = NULL; - } else if (node->type == BUS_MATCH_MESSAGE_TYPE) - found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8)); - else - found = NULL; - - if (found) { - r = bus_match_run(bus, found, m); - if (r != 0) - return r; - } - } else { - struct bus_match_node *c; - - /* No hash table, so let's iterate manually... */ - - for (c = node->child; c; c = c->next) { - if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m)) - continue; - - r = bus_match_run(bus, c, m); - if (r != 0) - return r; - - if (bus && bus->match_callbacks_modified) - return 0; - } - } - - if (bus && bus->match_callbacks_modified) - return 0; - - /* And now, let's invoke our siblings */ - return bus_match_run(bus, node->next, m); -} - -static int bus_match_add_compare_value( - struct bus_match_node *where, - enum bus_match_node_type t, - uint8_t value_u8, - const char *value_str, - struct bus_match_node **ret) { - - struct bus_match_node *c = NULL, *n = NULL; - int r; - - assert(where); - assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); - assert(BUS_MATCH_IS_COMPARE(t)); - assert(ret); - - for (c = where->child; c && c->type != t; c = c->next) - ; - - if (c) { - /* Comparison node already exists? Then let's see if - * the value node exists too. */ - - if (t == BUS_MATCH_MESSAGE_TYPE) - n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8)); - else if (BUS_MATCH_CAN_HASH(t)) - n = hashmap_get(c->compare.children, value_str); - else { - for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) - ; - } - - if (n) { - *ret = n; - return 0; - } - } else { - /* Comparison node, doesn't exist yet? Then let's - * create it. */ - - c = new0(struct bus_match_node, 1); - if (!c) { - r = -ENOMEM; - goto fail; - } - - c->type = t; - c->parent = where; - c->next = where->child; - if (c->next) - c->next->prev = c; - where->child = c; - - if (t == BUS_MATCH_MESSAGE_TYPE) { - c->compare.children = hashmap_new(NULL); - if (!c->compare.children) { - r = -ENOMEM; - goto fail; - } - } else if (BUS_MATCH_CAN_HASH(t)) { - c->compare.children = hashmap_new(&string_hash_ops); - if (!c->compare.children) { - r = -ENOMEM; - goto fail; - } - } - } - - n = new0(struct bus_match_node, 1); - if (!n) { - r = -ENOMEM; - goto fail; - } - - n->type = BUS_MATCH_VALUE; - n->value.u8 = value_u8; - if (value_str) { - n->value.str = strdup(value_str); - if (!n->value.str) { - r = -ENOMEM; - goto fail; - } - } - - n->parent = c; - if (c->compare.children) { - - if (t == BUS_MATCH_MESSAGE_TYPE) - r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n); - else - r = hashmap_put(c->compare.children, n->value.str, n); - - if (r < 0) - goto fail; - } else { - n->next = c->child; - if (n->next) - n->next->prev = n; - c->child = n; - } - - *ret = n; - return 1; - -fail: - if (c) - bus_match_node_maybe_free(c); - - if (n) { - free(n->value.str); - free(n); - } - - return r; -} - -static int bus_match_find_compare_value( - struct bus_match_node *where, - enum bus_match_node_type t, - uint8_t value_u8, - const char *value_str, - struct bus_match_node **ret) { - - struct bus_match_node *c, *n; - - assert(where); - assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); - assert(BUS_MATCH_IS_COMPARE(t)); - assert(ret); - - for (c = where->child; c && c->type != t; c = c->next) - ; - - if (!c) - return 0; - - if (t == BUS_MATCH_MESSAGE_TYPE) - n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8)); - else if (BUS_MATCH_CAN_HASH(t)) - n = hashmap_get(c->compare.children, value_str); - else { - for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) - ; - } - - if (n) { - *ret = n; - return 1; - } - - return 0; -} - -static int bus_match_add_leaf( - struct bus_match_node *where, - struct match_callback *callback) { - - struct bus_match_node *n; - - assert(where); - assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); - assert(callback); - - n = new0(struct bus_match_node, 1); - if (!n) - return -ENOMEM; - - n->type = BUS_MATCH_LEAF; - n->parent = where; - n->next = where->child; - if (n->next) - n->next->prev = n; - - n->leaf.callback = callback; - callback->match_node = n; - - where->child = n; - - return 1; -} - -static int bus_match_find_leaf( - struct bus_match_node *where, - sd_bus_message_handler_t callback, - void *userdata, - struct bus_match_node **ret) { - - struct bus_match_node *c; - - assert(where); - assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); - assert(ret); - - for (c = where->child; c; c = c->next) { - sd_bus_slot *s; - - s = container_of(c->leaf.callback, sd_bus_slot, match_callback); - - if (c->type == BUS_MATCH_LEAF && - c->leaf.callback->callback == callback && - s->userdata == userdata) { - *ret = c; - return 1; - } - } - - return 0; -} - -enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) { - assert(k); - - if (n == 4 && startswith(k, "type")) - return BUS_MATCH_MESSAGE_TYPE; - if (n == 6 && startswith(k, "sender")) - return BUS_MATCH_SENDER; - if (n == 11 && startswith(k, "destination")) - return BUS_MATCH_DESTINATION; - if (n == 9 && startswith(k, "interface")) - return BUS_MATCH_INTERFACE; - if (n == 6 && startswith(k, "member")) - return BUS_MATCH_MEMBER; - if (n == 4 && startswith(k, "path")) - return BUS_MATCH_PATH; - if (n == 14 && startswith(k, "path_namespace")) - return BUS_MATCH_PATH_NAMESPACE; - - if (n == 4 && startswith(k, "arg")) { - int j; - - j = undecchar(k[3]); - if (j < 0) - return -EINVAL; - - return BUS_MATCH_ARG + j; - } - - if (n == 5 && startswith(k, "arg")) { - int a, b; - enum bus_match_node_type t; - - a = undecchar(k[3]); - b = undecchar(k[4]); - if (a <= 0 || b < 0) - return -EINVAL; - - t = BUS_MATCH_ARG + a * 10 + b; - if (t > BUS_MATCH_ARG_LAST) - return -EINVAL; - - return t; - } - - if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) { - int j; - - j = undecchar(k[3]); - if (j < 0) - return -EINVAL; - - return BUS_MATCH_ARG_PATH + j; - } - - if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) { - enum bus_match_node_type t; - int a, b; - - a = undecchar(k[3]); - b = undecchar(k[4]); - if (a <= 0 || b < 0) - return -EINVAL; - - t = BUS_MATCH_ARG_PATH + a * 10 + b; - if (t > BUS_MATCH_ARG_PATH_LAST) - return -EINVAL; - - return t; - } - - if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) { - int j; - - j = undecchar(k[3]); - if (j < 0) - return -EINVAL; - - return BUS_MATCH_ARG_NAMESPACE + j; - } - - if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) { - enum bus_match_node_type t; - int a, b; - - a = undecchar(k[3]); - b = undecchar(k[4]); - if (a <= 0 || b < 0) - return -EINVAL; - - t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b; - if (t > BUS_MATCH_ARG_NAMESPACE_LAST) - return -EINVAL; - - return t; - } - - if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { - int j; - - j = undecchar(k[3]); - if (j < 0) - return -EINVAL; - - return BUS_MATCH_ARG_HAS + j; - } - - if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { - enum bus_match_node_type t; - int a, b; - - a = undecchar(k[3]); - b = undecchar(k[4]); - if (a <= 0 || b < 0) - return -EINVAL; - - t = BUS_MATCH_ARG_HAS + a * 10 + b; - if (t > BUS_MATCH_ARG_HAS_LAST) - return -EINVAL; - - return t; - } - - return -EINVAL; -} - -static int match_component_compare(const void *a, const void *b) { - const struct bus_match_component *x = a, *y = b; - - if (x->type < y->type) - return -1; - if (x->type > y->type) - return 1; - - return 0; -} - -void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) { - unsigned i; - - for (i = 0; i < n_components; i++) - free(components[i].value_str); - - free(components); -} - -int bus_match_parse( - const char *match, - struct bus_match_component **_components, - unsigned *_n_components) { - - const char *p = match; - struct bus_match_component *components = NULL; - size_t components_allocated = 0; - unsigned n_components = 0, i; - _cleanup_free_ char *value = NULL; - int r; - - assert(match); - assert(_components); - assert(_n_components); - - while (*p != 0) { - const char *eq, *q; - enum bus_match_node_type t; - unsigned j = 0; - size_t value_allocated = 0; - bool escaped = false, quoted; - uint8_t u; - - /* Avahi's match rules appear to include whitespace, skip over it */ - p += strspn(p, " "); - - eq = strchr(p, '='); - if (!eq) - return -EINVAL; - - t = bus_match_node_type_from_string(p, eq - p); - if (t < 0) - return -EINVAL; - - quoted = eq[1] == '\''; - - for (q = eq + 1 + quoted;; q++) { - - if (*q == 0) { - - if (quoted) { - r = -EINVAL; - goto fail; - } else { - if (value) - value[j] = 0; - break; - } - } - - if (!escaped) { - if (*q == '\\') { - escaped = true; - continue; - } - - if (quoted) { - if (*q == '\'') { - if (value) - value[j] = 0; - break; - } - } else { - if (*q == ',') { - if (value) - value[j] = 0; - - break; - } - } - } - - if (!GREEDY_REALLOC(value, value_allocated, j + 2)) { - r = -ENOMEM; - goto fail; - } - - value[j++] = *q; - escaped = false; - } - - if (!value) { - value = strdup(""); - if (!value) { - r = -ENOMEM; - goto fail; - } - } - - if (t == BUS_MATCH_MESSAGE_TYPE) { - r = bus_message_type_from_string(value, &u); - if (r < 0) - goto fail; - - value = mfree(value); - } else - u = 0; - - if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) { - r = -ENOMEM; - goto fail; - } - - components[n_components].type = t; - components[n_components].value_str = value; - components[n_components].value_u8 = u; - n_components++; - - value = NULL; - - if (q[quoted] == 0) - break; - - if (q[quoted] != ',') { - r = -EINVAL; - goto fail; - } - - p = q + 1 + quoted; - } - - /* Order the whole thing, so that we always generate the same tree */ - qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare); - - /* Check for duplicates */ - for (i = 0; i+1 < n_components; i++) - if (components[i].type == components[i+1].type) { - r = -EINVAL; - goto fail; - } - - *_components = components; - *_n_components = n_components; - - return 0; - -fail: - bus_match_parse_free(components, n_components); - return r; -} - -char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) { - _cleanup_fclose_ FILE *f = NULL; - char *buffer = NULL; - size_t size = 0; - unsigned i; - int r; - - if (n_components <= 0) - return strdup(""); - - assert(components); - - f = open_memstream(&buffer, &size); - if (!f) - return NULL; - - for (i = 0; i < n_components; i++) { - char buf[32]; - - if (i != 0) - fputc(',', f); - - fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f); - fputc('=', f); - fputc('\'', f); - - if (components[i].type == BUS_MATCH_MESSAGE_TYPE) - fputs(bus_message_type_to_string(components[i].value_u8), f); - else - fputs(components[i].value_str, f); - - fputc('\'', f); - } - - r = fflush_and_check(f); - if (r < 0) - return NULL; - - return buffer; -} - -int bus_match_add( - struct bus_match_node *root, - struct bus_match_component *components, - unsigned n_components, - struct match_callback *callback) { - - unsigned i; - struct bus_match_node *n; - int r; - - assert(root); - assert(callback); - - n = root; - for (i = 0; i < n_components; i++) { - r = bus_match_add_compare_value( - n, components[i].type, - components[i].value_u8, components[i].value_str, &n); - if (r < 0) - return r; - } - - return bus_match_add_leaf(n, callback); -} - -int bus_match_remove( - struct bus_match_node *root, - struct match_callback *callback) { - - struct bus_match_node *node, *pp; - - assert(root); - assert(callback); - - node = callback->match_node; - if (!node) - return 0; - - assert(node->type == BUS_MATCH_LEAF); - - callback->match_node = NULL; - - /* Free the leaf */ - pp = node->parent; - bus_match_node_free(node); - - /* Prune the tree above */ - while (pp) { - node = pp; - pp = node->parent; - - if (!bus_match_node_maybe_free(node)) - break; - } - - return 1; -} - -int bus_match_find( - struct bus_match_node *root, - struct bus_match_component *components, - unsigned n_components, - sd_bus_message_handler_t callback, - void *userdata, - struct match_callback **ret) { - - struct bus_match_node *n, **gc; - unsigned i; - int r; - - assert(root); - assert(ret); - - gc = newa(struct bus_match_node*, n_components); - - n = root; - for (i = 0; i < n_components; i++) { - r = bus_match_find_compare_value( - n, components[i].type, - components[i].value_u8, components[i].value_str, - &n); - if (r <= 0) - return r; - - gc[i] = n; - } - - r = bus_match_find_leaf(n, callback, userdata, &n); - if (r <= 0) - return r; - - *ret = n->leaf.callback; - return 1; -} - -void bus_match_free(struct bus_match_node *node) { - struct bus_match_node *c; - - if (!node) - return; - - if (BUS_MATCH_CAN_HASH(node->type)) { - Iterator i; - - HASHMAP_FOREACH(c, node->compare.children, i) - bus_match_free(c); - - assert(hashmap_isempty(node->compare.children)); - } - - while ((c = node->child)) - bus_match_free(c); - - if (node->type != BUS_MATCH_ROOT) - bus_match_node_free(node); -} - -const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) { - switch (t) { - - case BUS_MATCH_ROOT: - return "root"; - - case BUS_MATCH_VALUE: - return "value"; - - case BUS_MATCH_LEAF: - return "leaf"; - - case BUS_MATCH_MESSAGE_TYPE: - return "type"; - - case BUS_MATCH_SENDER: - return "sender"; - - case BUS_MATCH_DESTINATION: - return "destination"; - - case BUS_MATCH_INTERFACE: - return "interface"; - - case BUS_MATCH_MEMBER: - return "member"; - - case BUS_MATCH_PATH: - return "path"; - - case BUS_MATCH_PATH_NAMESPACE: - return "path_namespace"; - - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG); - return buf; - - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH); - return buf; - - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE); - return buf; - - case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: - snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); - return buf; - - default: - return NULL; - } -} - -void bus_match_dump(struct bus_match_node *node, unsigned level) { - struct bus_match_node *c; - _cleanup_free_ char *pfx = NULL; - char buf[32]; - - if (!node) - return; - - pfx = strrep(" ", level); - printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf))); - - if (node->type == BUS_MATCH_VALUE) { - if (node->parent->type == BUS_MATCH_MESSAGE_TYPE) - printf(" <%u>\n", node->value.u8); - else - printf(" <%s>\n", node->value.str); - } else if (node->type == BUS_MATCH_ROOT) - puts(" root"); - else if (node->type == BUS_MATCH_LEAF) - printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata); - else - putchar('\n'); - - if (BUS_MATCH_CAN_HASH(node->type)) { - Iterator i; - - HASHMAP_FOREACH(c, node->compare.children, i) - bus_match_dump(c, level + 1); - } - - for (c = node->child; c; c = c->next) - bus_match_dump(c, level + 1); -} - -enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) { - bool found_driver = false; - unsigned i; - - if (n_components <= 0) - return BUS_MATCH_GENERIC; - - assert(components); - - /* Checks whether the specified match can only match the - * pseudo-service for local messages, which we detect by - * sender, interface or path. If a match is not restricted to - * local messages, then we check if it only matches on the - * driver. */ - - for (i = 0; i < n_components; i++) { - const struct bus_match_component *c = components + i; - - if (c->type == BUS_MATCH_SENDER) { - if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) - return BUS_MATCH_LOCAL; - - if (streq_ptr(c->value_str, "org.freedesktop.DBus")) - found_driver = true; - } - - if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) - return BUS_MATCH_LOCAL; - - if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local")) - return BUS_MATCH_LOCAL; - } - - return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC; - -} diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h deleted file mode 100644 index 8cbbb63b11..0000000000 --- a/src/libsystemd/sd-bus/bus-match.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "hashmap.h" - -enum bus_match_node_type { - BUS_MATCH_ROOT, - BUS_MATCH_VALUE, - BUS_MATCH_LEAF, - - /* The following are all different kinds of compare nodes */ - BUS_MATCH_SENDER, - BUS_MATCH_MESSAGE_TYPE, - BUS_MATCH_DESTINATION, - BUS_MATCH_INTERFACE, - BUS_MATCH_MEMBER, - BUS_MATCH_PATH, - BUS_MATCH_PATH_NAMESPACE, - BUS_MATCH_ARG, - BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63, - BUS_MATCH_ARG_PATH, - BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63, - BUS_MATCH_ARG_NAMESPACE, - BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63, - BUS_MATCH_ARG_HAS, - BUS_MATCH_ARG_HAS_LAST = BUS_MATCH_ARG_HAS + 63, - _BUS_MATCH_NODE_TYPE_MAX, - _BUS_MATCH_NODE_TYPE_INVALID = -1 -}; - -struct bus_match_node { - enum bus_match_node_type type; - struct bus_match_node *parent, *next, *prev, *child; - - union { - struct { - char *str; - uint8_t u8; - } value; - struct { - struct match_callback *callback; - } leaf; - struct { - /* If this is set, then the child is NULL */ - Hashmap *children; - } compare; - }; -}; - -struct bus_match_component { - enum bus_match_node_type type; - uint8_t value_u8; - char *value_str; -}; - -enum bus_match_scope { - BUS_MATCH_GENERIC, - BUS_MATCH_LOCAL, - BUS_MATCH_DRIVER, -}; - -int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m); - -int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, struct match_callback *callback); -int bus_match_remove(struct bus_match_node *root, struct match_callback *callback); - -int bus_match_find(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, struct match_callback **ret); - -void bus_match_free(struct bus_match_node *node); - -void bus_match_dump(struct bus_match_node *node, unsigned level); - -const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l); -enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n); - -int bus_match_parse(const char *match, struct bus_match_component **_components, unsigned *_n_components); -void bus_match_parse_free(struct bus_match_component *components, unsigned n_components); -char *bus_match_to_string(struct bus_match_component *components, unsigned n_components); - -enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components); diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c deleted file mode 100644 index 5cec804e32..0000000000 --- a/src/libsystemd/sd-bus/bus-message.c +++ /dev/null @@ -1,5939 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-gvariant.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-signature.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); - -static void *adjust_pointer(const void *p, void *old_base, size_t sz, void *new_base) { - - if (p == NULL) - return NULL; - - if (old_base == new_base) - return (void*) p; - - if ((uint8_t*) p < (uint8_t*) old_base) - return (void*) p; - - if ((uint8_t*) p >= (uint8_t*) old_base + sz) - return (void*) p; - - return (uint8_t*) new_base + ((uint8_t*) p - (uint8_t*) old_base); -} - -static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { - assert(m); - assert(part); - - if (part->memfd >= 0) { - /* If we can reuse the memfd, try that. For that it - * can't be sealed yet. */ - - if (!part->sealed) { - assert(part->memfd_offset == 0); - assert(part->data == part->mmap_begin); - bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); - } else { - if (part->mapped > 0) - assert_se(munmap(part->mmap_begin, part->mapped) == 0); - - safe_close(part->memfd); - } - - } else if (part->munmap_this) - munmap(part->mmap_begin, part->mapped); - else if (part->free_this) - free(part->data); - - if (part != &m->body) - free(part); -} - -static void message_reset_parts(sd_bus_message *m) { - struct bus_body_part *part; - - assert(m); - - part = &m->body; - while (m->n_body_parts > 0) { - struct bus_body_part *next = part->next; - message_free_part(m, part); - part = next; - m->n_body_parts--; - } - - m->body_end = NULL; - - m->cached_rindex_part = NULL; - m->cached_rindex_part_begin = 0; -} - -static void message_reset_containers(sd_bus_message *m) { - unsigned i; - - assert(m); - - for (i = 0; i < m->n_containers; i++) { - free(m->containers[i].signature); - free(m->containers[i].offsets); - } - - m->containers = mfree(m->containers); - - m->n_containers = m->containers_allocated = 0; - m->root_container.index = 0; -} - -static void message_free(sd_bus_message *m) { - assert(m); - - if (m->free_header) - free(m->header); - - message_reset_parts(m); - - if (m->release_kdbus) - bus_kernel_cmd_free(m->bus, (uint8_t *) m->kdbus - (uint8_t *) m->bus->kdbus_buffer); - - if (m->free_kdbus) - free(m->kdbus); - - sd_bus_unref(m->bus); - - if (m->free_fds) { - close_many(m->fds, m->n_fds); - free(m->fds); - } - - if (m->iovec != m->iovec_fixed) - free(m->iovec); - - m->destination_ptr = mfree(m->destination_ptr); - message_reset_containers(m); - free(m->root_container.signature); - free(m->root_container.offsets); - - free(m->root_container.peeked_signature); - - bus_creds_done(&m->creds); - free(m); -} - -static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) { - void *op, *np; - size_t old_size, new_size, start; - - assert(m); - - if (m->poisoned) - return NULL; - - old_size = sizeof(struct bus_header) + m->fields_size; - start = ALIGN_TO(old_size, align); - new_size = start + sz; - - if (new_size < start || - new_size > (size_t) ((uint32_t) -1)) - goto poison; - - if (old_size == new_size) - return (uint8_t*) m->header + old_size; - - if (m->free_header) { - np = realloc(m->header, ALIGN8(new_size)); - if (!np) - goto poison; - } else { - /* Initially, the header is allocated as part of - * the sd_bus_message itself, let's replace it by - * dynamic data */ - - np = malloc(ALIGN8(new_size)); - if (!np) - goto poison; - - memcpy(np, m->header, sizeof(struct bus_header)); - } - - /* Zero out padding */ - if (start > old_size) - memzero((uint8_t*) np + old_size, start - old_size); - - op = m->header; - m->header = np; - m->fields_size = new_size - sizeof(struct bus_header); - - /* Adjust quick access pointers */ - m->path = adjust_pointer(m->path, op, old_size, m->header); - m->interface = adjust_pointer(m->interface, op, old_size, m->header); - m->member = adjust_pointer(m->member, op, old_size, m->header); - m->destination = adjust_pointer(m->destination, op, old_size, m->header); - m->sender = adjust_pointer(m->sender, op, old_size, m->header); - m->error.name = adjust_pointer(m->error.name, op, old_size, m->header); - - m->free_header = true; - - if (add_offset) { - if (m->n_header_offsets >= ELEMENTSOF(m->header_offsets)) - goto poison; - - m->header_offsets[m->n_header_offsets++] = new_size - sizeof(struct bus_header); - } - - return (uint8_t*) np + start; - -poison: - m->poisoned = true; - return NULL; -} - -static int message_append_field_string( - sd_bus_message *m, - uint64_t h, - char type, - const char *s, - const char **ret) { - - size_t l; - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - /* dbus1 doesn't allow strings over 32bit, let's enforce this - * globally, to not risk convertability */ - l = strlen(s); - if (l > (size_t) (uint32_t) -1) - return -EINVAL; - - /* Signature "(yv)" where the variant contains "s" */ - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - - /* (field id 64bit, ((string + NUL) + NUL + signature string 's') */ - p = message_extend_fields(m, 8, 8 + l + 1 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - memcpy(p+8, s, l); - p[8+l] = 0; - p[8+l+1] = 0; - p[8+l+2] = type; - - if (ret) - *ret = (char*) p + 8; - - } else { - /* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */ - p = message_extend_fields(m, 8, 4 + 4 + l + 1, false); - if (!p) - return -ENOMEM; - - p[0] = (uint8_t) h; - p[1] = 1; - p[2] = type; - p[3] = 0; - - ((uint32_t*) p)[1] = l; - memcpy(p + 8, s, l + 1); - - if (ret) - *ret = (char*) p + 8; - } - - return 0; -} - -static int message_append_field_signature( - sd_bus_message *m, - uint64_t h, - const char *s, - const char **ret) { - - size_t l; - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - /* dbus1 doesn't allow signatures over 8bit, let's enforce - * this globally, to not risk convertability */ - l = strlen(s); - if (l > 255) - return -EINVAL; - - /* Signature "(yv)" where the variant contains "g" */ - - if (BUS_MESSAGE_IS_GVARIANT(m)) - /* For gvariant the serialization is the same as for normal strings */ - return message_append_field_string(m, h, 'g', s, ret); - else { - /* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */ - p = message_extend_fields(m, 8, 4 + 1 + l + 1, false); - if (!p) - return -ENOMEM; - - p[0] = (uint8_t) h; - p[1] = 1; - p[2] = SD_BUS_TYPE_SIGNATURE; - p[3] = 0; - p[4] = l; - memcpy(p + 5, s, l + 1); - - if (ret) - *ret = (const char*) p + 5; - } - - return 0; -} - -static int message_append_field_uint32(sd_bus_message *m, uint64_t h, uint32_t x) { - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* (field id 64bit + ((value + NUL + signature string 'u') */ - - p = message_extend_fields(m, 8, 8 + 4 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - *((uint32_t*) (p + 8)) = x; - p[12] = 0; - p[13] = 'u'; - } else { - /* (field id byte + (signature length + signature 'u' + NUL) + value) */ - p = message_extend_fields(m, 8, 4 + 4, false); - if (!p) - return -ENOMEM; - - p[0] = (uint8_t) h; - p[1] = 1; - p[2] = 'u'; - p[3] = 0; - - ((uint32_t*) p)[1] = x; - } - - return 0; -} - -static int message_append_field_uint64(sd_bus_message *m, uint64_t h, uint64_t x) { - uint8_t *p; - - assert(m); - - /* dbus1 only allows 8bit header field ids */ - if (h > 0xFF) - return -EINVAL; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* (field id 64bit + ((value + NUL + signature string 't') */ - - p = message_extend_fields(m, 8, 8 + 8 + 1 + 1, true); - if (!p) - return -ENOMEM; - - *((uint64_t*) p) = h; - *((uint64_t*) (p + 8)) = x; - p[16] = 0; - p[17] = 't'; - } else { - /* (field id byte + (signature length + signature 't' + NUL) + 4 byte padding + value) */ - p = message_extend_fields(m, 8, 4 + 4 + 8, false); - if (!p) - return -ENOMEM; - - p[0] = (uint8_t) h; - p[1] = 1; - p[2] = 't'; - p[3] = 0; - p[4] = 0; - p[5] = 0; - p[6] = 0; - p[7] = 0; - - ((uint64_t*) p)[1] = x; - } - - return 0; -} - -static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) { - assert(m); - - if (BUS_MESSAGE_IS_GVARIANT(m)) - return message_append_field_uint64(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, cookie); - else { - /* 64bit cookies are not supported on dbus1 */ - if (cookie > 0xffffffffUL) - return -EOPNOTSUPP; - - return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie); - } -} - -int bus_message_from_header( - sd_bus *bus, - void *header, - size_t header_accessible, - void *footer, - size_t footer_accessible, - size_t message_size, - int *fds, - unsigned n_fds, - const char *label, - size_t extra, - sd_bus_message **ret) { - - _cleanup_free_ sd_bus_message *m = NULL; - struct bus_header *h; - size_t a, label_sz; - - assert(bus); - assert(header || header_accessible <= 0); - assert(footer || footer_accessible <= 0); - assert(fds || n_fds <= 0); - assert(ret); - - if (header_accessible < sizeof(struct bus_header)) - return -EBADMSG; - - if (header_accessible > message_size) - return -EBADMSG; - if (footer_accessible > message_size) - return -EBADMSG; - - h = header; - if (!IN_SET(h->version, 1, 2)) - return -EBADMSG; - - if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) - return -EBADMSG; - - if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) - return -EBADMSG; - - /* Note that we are happy with unknown flags in the flags header! */ - - a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra); - - if (label) { - label_sz = strlen(label); - a += label_sz + 1; - } - - m = malloc0(a); - if (!m) - return -ENOMEM; - - m->n_ref = 1; - m->sealed = true; - m->header = header; - m->header_accessible = header_accessible; - m->footer = footer; - m->footer_accessible = footer_accessible; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t ws; - - if (h->dbus2.cookie == 0) - return -EBADMSG; - - /* dbus2 derives the sizes from the message size and - the offset table at the end, since it is formatted as - gvariant "yyyyuta{tv}v". Since the message itself is a - structure with precisely to variable sized entries, - there's only one offset in the table, which marks the - end of the fields array. */ - - ws = bus_gvariant_determine_word_size(message_size, 0); - if (footer_accessible < ws) - return -EBADMSG; - - m->fields_size = bus_gvariant_read_word_le((uint8_t*) footer + footer_accessible - ws, ws); - if (ALIGN8(m->fields_size) > message_size - ws) - return -EBADMSG; - if (m->fields_size < sizeof(struct bus_header)) - return -EBADMSG; - - m->fields_size -= sizeof(struct bus_header); - m->body_size = message_size - (sizeof(struct bus_header) + ALIGN8(m->fields_size)); - } else { - if (h->dbus1.serial == 0) - return -EBADMSG; - - /* dbus1 has the sizes in the header */ - m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size); - m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size); - - if (sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size != message_size) - return -EBADMSG; - } - - m->fds = fds; - m->n_fds = n_fds; - - if (label) { - m->creds.label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra); - memcpy(m->creds.label, label, label_sz + 1); - - m->creds.mask |= SD_BUS_CREDS_SELINUX_CONTEXT; - } - - m->bus = sd_bus_ref(bus); - *ret = m; - m = NULL; - - return 0; -} - -int bus_message_from_malloc( - sd_bus *bus, - void *buffer, - size_t length, - int *fds, - unsigned n_fds, - const char *label, - sd_bus_message **ret) { - - sd_bus_message *m; - size_t sz; - int r; - - r = bus_message_from_header( - bus, - buffer, length, /* in this case the initial bytes and the final bytes are the same */ - buffer, length, - length, - fds, n_fds, - label, - 0, &m); - if (r < 0) - return r; - - sz = length - sizeof(struct bus_header) - ALIGN8(m->fields_size); - if (sz > 0) { - m->n_body_parts = 1; - m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(m->fields_size); - m->body.size = sz; - m->body.sealed = true; - m->body.memfd = -1; - } - - m->n_iovec = 1; - m->iovec = m->iovec_fixed; - m->iovec[0].iov_base = buffer; - m->iovec[0].iov_len = length; - - r = bus_message_parse_fields(m); - if (r < 0) - goto fail; - - /* We take possession of the memory and fds now */ - m->free_header = true; - m->free_fds = true; - - *ret = m; - return 0; - -fail: - message_free(m); - return r; -} - -static sd_bus_message *message_new(sd_bus *bus, uint8_t type) { - sd_bus_message *m; - - assert(bus); - - m = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header)); - if (!m) - return NULL; - - m->n_ref = 1; - m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message))); - m->header->endian = BUS_NATIVE_ENDIAN; - m->header->type = type; - m->header->version = bus->message_version; - m->allow_fds = bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING); - m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m); - m->bus = sd_bus_ref(bus); - - if (bus->allow_interactive_authorization) - m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; - - return m; -} - -_public_ int sd_bus_message_new_signal( - sd_bus *bus, - sd_bus_message **m, - const char *path, - const char *interface, - const char *member) { - - sd_bus_message *t; - int r; - - assert_return(bus, -ENOTCONN); - assert_return(bus->state != BUS_UNSET, -ENOTCONN); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(interface_name_is_valid(interface), -EINVAL); - assert_return(member_name_is_valid(member), -EINVAL); - assert_return(m, -EINVAL); - - t = message_new(bus, SD_BUS_MESSAGE_SIGNAL); - if (!t) - return -ENOMEM; - - t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; - - r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path); - if (r < 0) - goto fail; - r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface); - if (r < 0) - goto fail; - r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member); - if (r < 0) - goto fail; - - *m = t; - return 0; - -fail: - sd_bus_message_unref(t); - return r; -} - -_public_ int sd_bus_message_new_method_call( - sd_bus *bus, - sd_bus_message **m, - const char *destination, - const char *path, - const char *interface, - const char *member) { - - sd_bus_message *t; - int r; - - assert_return(bus, -ENOTCONN); - assert_return(bus->state != BUS_UNSET, -ENOTCONN); - assert_return(!destination || service_name_is_valid(destination), -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!interface || interface_name_is_valid(interface), -EINVAL); - assert_return(member_name_is_valid(member), -EINVAL); - assert_return(m, -EINVAL); - - t = message_new(bus, SD_BUS_MESSAGE_METHOD_CALL); - if (!t) - return -ENOMEM; - - r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path); - if (r < 0) - goto fail; - r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member); - if (r < 0) - goto fail; - - if (interface) { - r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface); - if (r < 0) - goto fail; - } - - if (destination) { - r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination); - if (r < 0) - goto fail; - } - - *m = t; - return 0; - -fail: - message_free(t); - return r; -} - -static int message_new_reply( - sd_bus_message *call, - uint8_t type, - sd_bus_message **m) { - - sd_bus_message *t; - int r; - - assert_return(call, -EINVAL); - assert_return(call->sealed, -EPERM); - assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(call->bus->state != BUS_UNSET, -ENOTCONN); - assert_return(m, -EINVAL); - - t = message_new(call->bus, type); - if (!t) - return -ENOMEM; - - t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; - t->reply_cookie = BUS_MESSAGE_COOKIE(call); - if (t->reply_cookie == 0) - return -EOPNOTSUPP; - - r = message_append_reply_cookie(t, t->reply_cookie); - if (r < 0) - goto fail; - - if (call->sender) { - r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination); - if (r < 0) - goto fail; - } - - t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED); - t->enforced_reply_signature = call->enforced_reply_signature; - - *m = t; - return 0; - -fail: - message_free(t); - return r; -} - -_public_ int sd_bus_message_new_method_return( - sd_bus_message *call, - sd_bus_message **m) { - - return message_new_reply(call, SD_BUS_MESSAGE_METHOD_RETURN, m); -} - -_public_ int sd_bus_message_new_method_error( - sd_bus_message *call, - sd_bus_message **m, - const sd_bus_error *e) { - - sd_bus_message *t; - int r; - - assert_return(sd_bus_error_is_set(e), -EINVAL); - assert_return(m, -EINVAL); - - r = message_new_reply(call, SD_BUS_MESSAGE_METHOD_ERROR, &t); - if (r < 0) - return r; - - r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name); - if (r < 0) - goto fail; - - if (e->message) { - r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message); - if (r < 0) - goto fail; - } - - t->error._need_free = -1; - - *m = t; - return 0; - -fail: - message_free(t); - return r; -} - -_public_ int sd_bus_message_new_method_errorf( - sd_bus_message *call, - sd_bus_message **m, - const char *name, - const char *format, - ...) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - va_list ap; - - assert_return(name, -EINVAL); - assert_return(m, -EINVAL); - - va_start(ap, format); - bus_error_setfv(&error, name, format, ap); - va_end(ap); - - return sd_bus_message_new_method_error(call, m, &error); -} - -_public_ int sd_bus_message_new_method_errno( - sd_bus_message *call, - sd_bus_message **m, - int error, - const sd_bus_error *p) { - - _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); - - sd_bus_error_set_errno(&berror, error); - - return sd_bus_message_new_method_error(call, m, &berror); -} - -_public_ int sd_bus_message_new_method_errnof( - sd_bus_message *call, - sd_bus_message **m, - int error, - const char *format, - ...) { - - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - va_list ap; - - va_start(ap, format); - sd_bus_error_set_errnofv(&berror, error, format, ap); - va_end(ap); - - return sd_bus_message_new_method_error(call, m, &berror); -} - -void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus.Local"; - m->creds.well_known_names_local = true; - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; -} - -void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; - m->creds.well_known_names_driver = true; - m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; -} - -int bus_message_new_synthetic_error( - sd_bus *bus, - uint64_t cookie, - const sd_bus_error *e, - sd_bus_message **m) { - - sd_bus_message *t; - int r; - - assert(bus); - assert(sd_bus_error_is_set(e)); - assert(m); - - t = message_new(bus, SD_BUS_MESSAGE_METHOD_ERROR); - if (!t) - return -ENOMEM; - - t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; - t->reply_cookie = cookie; - - r = message_append_reply_cookie(t, t->reply_cookie); - if (r < 0) - goto fail; - - if (bus && bus->unique_name) { - r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination); - if (r < 0) - goto fail; - } - - r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name); - if (r < 0) - goto fail; - - if (e->message) { - r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message); - if (r < 0) - goto fail; - } - - t->error._need_free = -1; - - bus_message_set_sender_driver(bus, t); - - *m = t; - return 0; - -fail: - message_free(t); - return r; -} - -_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { - - if (!m) - return NULL; - - assert(m->n_ref > 0); - m->n_ref++; - - return m; -} - -_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) { - - if (!m) - return NULL; - - assert(m->n_ref > 0); - m->n_ref--; - - if (m->n_ref > 0) - return NULL; - - message_free(m); - return NULL; -} - -_public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) { - assert_return(m, -EINVAL); - assert_return(type, -EINVAL); - - *type = m->header->type; - return 0; -} - -_public_ int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie) { - uint64_t c; - - assert_return(m, -EINVAL); - assert_return(cookie, -EINVAL); - - c = BUS_MESSAGE_COOKIE(m); - if (c == 0) - return -ENODATA; - - *cookie = BUS_MESSAGE_COOKIE(m); - return 0; -} - -_public_ int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie) { - assert_return(m, -EINVAL); - assert_return(cookie, -EINVAL); - - if (m->reply_cookie == 0) - return -ENODATA; - - *cookie = m->reply_cookie; - return 0; -} - -_public_ int sd_bus_message_get_expect_reply(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && - !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED); -} - -_public_ int sd_bus_message_get_auto_start(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return !(m->header->flags & BUS_MESSAGE_NO_AUTO_START); -} - -_public_ int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && - (m->header->flags & BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION); -} - -_public_ const char *sd_bus_message_get_path(sd_bus_message *m) { - assert_return(m, NULL); - - return m->path; -} - -_public_ const char *sd_bus_message_get_interface(sd_bus_message *m) { - assert_return(m, NULL); - - return m->interface; -} - -_public_ const char *sd_bus_message_get_member(sd_bus_message *m) { - assert_return(m, NULL); - - return m->member; -} - -_public_ const char *sd_bus_message_get_destination(sd_bus_message *m) { - assert_return(m, NULL); - - return m->destination; -} - -_public_ const char *sd_bus_message_get_sender(sd_bus_message *m) { - assert_return(m, NULL); - - return m->sender; -} - -_public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) { - assert_return(m, NULL); - - if (!sd_bus_error_is_set(&m->error)) - return NULL; - - return &m->error; -} - -_public_ int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec) { - assert_return(m, -EINVAL); - assert_return(usec, -EINVAL); - - if (m->monotonic <= 0) - return -ENODATA; - - *usec = m->monotonic; - return 0; -} - -_public_ int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec) { - assert_return(m, -EINVAL); - assert_return(usec, -EINVAL); - - if (m->realtime <= 0) - return -ENODATA; - - *usec = m->realtime; - return 0; -} - -_public_ int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum) { - assert_return(m, -EINVAL); - assert_return(seqnum, -EINVAL); - - if (m->seqnum <= 0) - return -ENODATA; - - *seqnum = m->seqnum; - return 0; -} - -_public_ sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m) { - assert_return(m, NULL); - - if (m->creds.mask == 0) - return NULL; - - return &m->creds; -} - -_public_ int sd_bus_message_is_signal( - sd_bus_message *m, - const char *interface, - const char *member) { - - assert_return(m, -EINVAL); - - if (m->header->type != SD_BUS_MESSAGE_SIGNAL) - return 0; - - if (interface && (!m->interface || !streq(m->interface, interface))) - return 0; - - if (member && (!m->member || !streq(m->member, member))) - return 0; - - return 1; -} - -_public_ int sd_bus_message_is_method_call( - sd_bus_message *m, - const char *interface, - const char *member) { - - assert_return(m, -EINVAL); - - if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) - return 0; - - if (interface && (!m->interface || !streq(m->interface, interface))) - return 0; - - if (member && (!m->member || !streq(m->member, member))) - return 0; - - return 1; -} - -_public_ int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) { - assert_return(m, -EINVAL); - - if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) - return 0; - - if (name && (!m->error.name || !streq(m->error.name, name))) - return 0; - - return 1; -} - -_public_ int sd_bus_message_set_expect_reply(sd_bus_message *m, int b) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EPERM); - - SET_FLAG(m->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED, !b); - - return 0; -} - -_public_ int sd_bus_message_set_auto_start(sd_bus_message *m, int b) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - SET_FLAG(m->header->flags, BUS_MESSAGE_NO_AUTO_START, !b); - - return 0; -} - -_public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - SET_FLAG(m->header->flags, BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION, b); - - return 0; -} - -static struct bus_container *message_get_container(sd_bus_message *m) { - assert(m); - - if (m->n_containers == 0) - return &m->root_container; - - assert(m->containers); - return m->containers + m->n_containers - 1; -} - -struct bus_body_part *message_append_part(sd_bus_message *m) { - struct bus_body_part *part; - - assert(m); - - if (m->poisoned) - return NULL; - - if (m->n_body_parts <= 0) { - part = &m->body; - zero(*part); - } else { - assert(m->body_end); - - part = new0(struct bus_body_part, 1); - if (!part) { - m->poisoned = true; - return NULL; - } - - m->body_end->next = part; - } - - part->memfd = -1; - m->body_end = part; - m->n_body_parts++; - - return part; -} - -static void part_zero(struct bus_body_part *part, size_t sz) { - assert(part); - assert(sz > 0); - assert(sz < 8); - - /* All other fields can be left in their defaults */ - assert(!part->data); - assert(part->memfd < 0); - - part->size = sz; - part->is_zero = true; - part->sealed = true; -} - -static int part_make_space( - struct sd_bus_message *m, - struct bus_body_part *part, - size_t sz, - void **q) { - - void *n; - int r; - - assert(m); - assert(part); - assert(!part->sealed); - - if (m->poisoned) - return -ENOMEM; - - if (!part->data && part->memfd < 0) { - part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated); - part->mmap_begin = part->data; - } - - if (part->memfd >= 0) { - - if (part->allocated == 0 || sz > part->allocated) { - uint64_t new_allocated; - - new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1); - r = memfd_set_size(part->memfd, new_allocated); - if (r < 0) { - m->poisoned = true; - return r; - } - - part->allocated = new_allocated; - } - - if (!part->data || sz > part->mapped) { - size_t psz; - - psz = PAGE_ALIGN(sz > 0 ? sz : 1); - if (part->mapped <= 0) - n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); - else - n = mremap(part->mmap_begin, part->mapped, psz, MREMAP_MAYMOVE); - - if (n == MAP_FAILED) { - m->poisoned = true; - return -errno; - } - - part->mmap_begin = part->data = n; - part->mapped = psz; - part->memfd_offset = 0; - } - - part->munmap_this = true; - } else { - if (part->allocated == 0 || sz > part->allocated) { - size_t new_allocated; - - new_allocated = sz > 0 ? 2 * sz : 64; - n = realloc(part->data, new_allocated); - if (!n) { - m->poisoned = true; - return -ENOMEM; - } - - part->data = n; - part->allocated = new_allocated; - part->free_this = true; - } - } - - if (q) - *q = part->data ? (uint8_t*) part->data + part->size : NULL; - - part->size = sz; - return 0; -} - -static int message_add_offset(sd_bus_message *m, size_t offset) { - struct bus_container *c; - - assert(m); - assert(BUS_MESSAGE_IS_GVARIANT(m)); - - /* Add offset to current container, unless this is the first - * item in it, which will have the 0 offset, which we can - * ignore. */ - c = message_get_container(m); - - if (!c->need_offsets) - return 0; - - if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1)) - return -ENOMEM; - - c->offsets[c->n_offsets++] = offset; - return 0; -} - -static void message_extend_containers(sd_bus_message *m, size_t expand) { - struct bus_container *c; - - assert(m); - - if (expand <= 0) - return; - - /* Update counters */ - for (c = m->containers; c < m->containers + m->n_containers; c++) { - - if (c->array_size) - *c->array_size += expand; - } -} - -static void *message_extend_body( - sd_bus_message *m, - size_t align, - size_t sz, - bool add_offset, - bool force_inline) { - - size_t start_body, end_body, padding, added; - void *p; - int r; - - assert(m); - assert(align > 0); - assert(!m->sealed); - - if (m->poisoned) - return NULL; - - start_body = ALIGN_TO((size_t) m->body_size, align); - end_body = start_body + sz; - - padding = start_body - m->body_size; - added = padding + sz; - - /* Check for 32bit overflows */ - if (end_body > (size_t) ((uint32_t) -1) || - end_body < start_body) { - m->poisoned = true; - return NULL; - } - - if (added > 0) { - struct bus_body_part *part = NULL; - bool add_new_part; - - add_new_part = - m->n_body_parts <= 0 || - m->body_end->sealed || - (padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size) || - (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */ - - if (add_new_part) { - if (padding > 0) { - part = message_append_part(m); - if (!part) - return NULL; - - part_zero(part, padding); - } - - part = message_append_part(m); - if (!part) - return NULL; - - r = part_make_space(m, part, sz, &p); - if (r < 0) - return NULL; - } else { - struct bus_container *c; - void *op; - size_t os, start_part, end_part; - - part = m->body_end; - op = part->data; - os = part->size; - - start_part = ALIGN_TO(part->size, align); - end_part = start_part + sz; - - r = part_make_space(m, part, end_part, &p); - if (r < 0) - return NULL; - - if (padding > 0) { - memzero(p, padding); - p = (uint8_t*) p + padding; - } - - /* Readjust pointers */ - for (c = m->containers; c < m->containers + m->n_containers; c++) - c->array_size = adjust_pointer(c->array_size, op, os, part->data); - - m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); - } - } else - /* Return something that is not NULL and is aligned */ - p = (uint8_t *) NULL + align; - - m->body_size = end_body; - message_extend_containers(m, added); - - if (add_offset) { - r = message_add_offset(m, end_body); - if (r < 0) { - m->poisoned = true; - return NULL; - } - } - - return p; -} - -static int message_push_fd(sd_bus_message *m, int fd) { - int *f, copy; - - assert(m); - - if (fd < 0) - return -EINVAL; - - if (!m->allow_fds) - return -EOPNOTSUPP; - - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (copy < 0) - return -errno; - - f = realloc(m->fds, sizeof(int) * (m->n_fds + 1)); - if (!f) { - m->poisoned = true; - safe_close(copy); - return -ENOMEM; - } - - m->fds = f; - m->fds[m->n_fds] = copy; - m->free_fds = true; - - return copy; -} - -int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) { - _cleanup_close_ int fd = -1; - struct bus_container *c; - ssize_t align, sz; - void *a; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(bus_type_is_basic(type), -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - c = message_get_container(m); - - if (c->signature && c->signature[c->index]) { - /* Container signature is already set */ - - if (c->signature[c->index] != type) - return -ENXIO; - } else { - char *e; - - /* Maybe we can append to the signature? But only if this is the top-level container */ - if (c->enclosing != 0) - return -ENXIO; - - e = strextend(&c->signature, CHAR_TO_STR(type), NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - uint8_t u8; - uint32_t u32; - - switch (type) { - - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_STRING: - p = strempty(p); - - /* Fall through... */ - case SD_BUS_TYPE_OBJECT_PATH: - if (!p) - return -EINVAL; - - align = 1; - sz = strlen(p) + 1; - break; - - case SD_BUS_TYPE_BOOLEAN: - - u8 = p && *(int*) p; - p = &u8; - - align = sz = 1; - break; - - case SD_BUS_TYPE_UNIX_FD: - - if (!p) - return -EINVAL; - - fd = message_push_fd(m, *(int*) p); - if (fd < 0) - return fd; - - u32 = m->n_fds; - p = &u32; - - align = sz = 4; - break; - - default: - align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); - sz = bus_gvariant_get_size(CHAR_TO_STR(type)); - break; - } - - assert(align > 0); - assert(sz > 0); - - a = message_extend_body(m, align, sz, true, false); - if (!a) - return -ENOMEM; - - memcpy(a, p, sz); - - if (stored) - *stored = (const uint8_t*) a; - - } else { - uint32_t u32; - - switch (type) { - - case SD_BUS_TYPE_STRING: - /* To make things easy we'll serialize a NULL string - * into the empty string */ - p = strempty(p); - - /* Fall through... */ - case SD_BUS_TYPE_OBJECT_PATH: - - if (!p) - return -EINVAL; - - align = 4; - sz = 4 + strlen(p) + 1; - break; - - case SD_BUS_TYPE_SIGNATURE: - - p = strempty(p); - - align = 1; - sz = 1 + strlen(p) + 1; - break; - - case SD_BUS_TYPE_BOOLEAN: - - u32 = p && *(int*) p; - p = &u32; - - align = sz = 4; - break; - - case SD_BUS_TYPE_UNIX_FD: - - if (!p) - return -EINVAL; - - fd = message_push_fd(m, *(int*) p); - if (fd < 0) - return fd; - - u32 = m->n_fds; - p = &u32; - - align = sz = 4; - break; - - default: - align = bus_type_get_alignment(type); - sz = bus_type_get_size(type); - break; - } - - assert(align > 0); - assert(sz > 0); - - a = message_extend_body(m, align, sz, false, false); - if (!a) - return -ENOMEM; - - if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) { - *(uint32_t*) a = sz - 5; - memcpy((uint8_t*) a + 4, p, sz - 4); - - if (stored) - *stored = (const uint8_t*) a + 4; - - } else if (type == SD_BUS_TYPE_SIGNATURE) { - *(uint8_t*) a = sz - 2; - memcpy((uint8_t*) a + 1, p, sz - 1); - - if (stored) - *stored = (const uint8_t*) a + 1; - } else { - memcpy(a, p, sz); - - if (stored) - *stored = a; - } - } - - if (type == SD_BUS_TYPE_UNIX_FD) - m->n_fds++; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - fd = -1; - return 0; -} - -_public_ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) { - return message_append_basic(m, type, p, NULL); -} - -_public_ int sd_bus_message_append_string_space( - sd_bus_message *m, - size_t size, - char **s) { - - struct bus_container *c; - void *a; - - assert_return(m, -EINVAL); - assert_return(s, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - c = message_get_container(m); - - if (c->signature && c->signature[c->index]) { - /* Container signature is already set */ - - if (c->signature[c->index] != SD_BUS_TYPE_STRING) - return -ENXIO; - } else { - char *e; - - /* Maybe we can append to the signature? But only if this is the top-level container */ - if (c->enclosing != 0) - return -ENXIO; - - e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - a = message_extend_body(m, 1, size + 1, true, false); - if (!a) - return -ENOMEM; - - *s = a; - } else { - a = message_extend_body(m, 4, 4 + size + 1, false, false); - if (!a) - return -ENOMEM; - - *(uint32_t*) a = size; - *s = (char*) a + 4; - } - - (*s)[size] = 0; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - return 0; -} - -_public_ int sd_bus_message_append_string_iovec( - sd_bus_message *m, - const struct iovec *iov, - unsigned n) { - - size_t size; - unsigned i; - char *p; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(iov || n == 0, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - size = IOVEC_TOTAL_SIZE(iov, n); - - r = sd_bus_message_append_string_space(m, size, &p); - if (r < 0) - return r; - - for (i = 0; i < n; i++) { - - if (iov[i].iov_base) - memcpy(p, iov[i].iov_base, iov[i].iov_len); - else - memset(p, ' ', iov[i].iov_len); - - p += iov[i].iov_len; - } - - return 0; -} - -static int bus_message_open_array( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - uint32_t **array_size, - size_t *begin, - bool *need_offsets) { - - unsigned nindex; - int alignment, r; - - assert(m); - assert(c); - assert(contents); - assert(array_size); - assert(begin); - assert(need_offsets); - - if (!signature_is_single(contents, true)) - return -EINVAL; - - if (c->signature && c->signature[c->index]) { - - /* Verify the existing signature */ - - if (c->signature[c->index] != SD_BUS_TYPE_ARRAY) - return -ENXIO; - - if (!startswith(c->signature + c->index + 1, contents)) - return -ENXIO; - - nindex = c->index + 1 + strlen(contents); - } else { - char *e; - - if (c->enclosing != 0) - return -ENXIO; - - /* Extend the existing signature */ - - e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - - nindex = e - c->signature; - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - alignment = bus_gvariant_get_alignment(contents); - if (alignment < 0) - return alignment; - - /* Add alignment padding and add to offset list */ - if (!message_extend_body(m, alignment, 0, false, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->body_size; - *need_offsets = r == 0; - } else { - void *a, *op; - size_t os; - struct bus_body_part *o; - - alignment = bus_type_get_alignment(contents[0]); - if (alignment < 0) - return alignment; - - a = message_extend_body(m, 4, 4, false, false); - if (!a) - return -ENOMEM; - - o = m->body_end; - op = m->body_end->data; - os = m->body_end->size; - - /* Add alignment between size and first element */ - if (!message_extend_body(m, alignment, 0, false, false)) - return -ENOMEM; - - /* location of array size might have changed so let's readjust a */ - if (o == m->body_end) - a = adjust_pointer(a, op, os, m->body_end->data); - - *(uint32_t*) a = 0; - *array_size = a; - } - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index = nindex; - - return 0; -} - -static int bus_message_open_variant( - sd_bus_message *m, - struct bus_container *c, - const char *contents) { - - assert(m); - assert(c); - assert(contents); - - if (!signature_is_single(contents, false)) - return -EINVAL; - - if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) - return -EINVAL; - - if (c->signature && c->signature[c->index]) { - - if (c->signature[c->index] != SD_BUS_TYPE_VARIANT) - return -ENXIO; - - } else { - char *e; - - if (c->enclosing != 0) - return -ENXIO; - - e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* Variants are always aligned to 8 */ - - if (!message_extend_body(m, 8, 0, false, false)) - return -ENOMEM; - - } else { - size_t l; - void *a; - - l = strlen(contents); - a = message_extend_body(m, 1, 1 + l + 1, false, false); - if (!a) - return -ENOMEM; - - *(uint8_t*) a = l; - memcpy((uint8_t*) a + 1, contents, l + 1); - } - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - return 0; -} - -static int bus_message_open_struct( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *begin, - bool *need_offsets) { - - size_t nindex; - int r; - - assert(m); - assert(c); - assert(contents); - assert(begin); - assert(need_offsets); - - if (!signature_is_valid(contents, false)) - return -EINVAL; - - if (c->signature && c->signature[c->index]) { - size_t l; - - l = strlen(contents); - - if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN || - !startswith(c->signature + c->index + 1, contents) || - c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END) - return -ENXIO; - - nindex = c->index + 1 + l + 1; - } else { - char *e; - - if (c->enclosing != 0) - return -ENXIO; - - e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - - nindex = e - c->signature; - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - int alignment; - - alignment = bus_gvariant_get_alignment(contents); - if (alignment < 0) - return alignment; - - if (!message_extend_body(m, alignment, 0, false, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->body_size; - *need_offsets = r == 0; - } else { - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0, false, false)) - return -ENOMEM; - } - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index = nindex; - - return 0; -} - -static int bus_message_open_dict_entry( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *begin, - bool *need_offsets) { - - int r; - - assert(m); - assert(c); - assert(contents); - assert(begin); - assert(need_offsets); - - if (!signature_is_pair(contents)) - return -EINVAL; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - return -ENXIO; - - if (c->signature && c->signature[c->index]) { - size_t l; - - l = strlen(contents); - - if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN || - !startswith(c->signature + c->index + 1, contents) || - c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END) - return -ENXIO; - } else - return -ENXIO; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - int alignment; - - alignment = bus_gvariant_get_alignment(contents); - if (alignment < 0) - return alignment; - - if (!message_extend_body(m, alignment, 0, false, false)) - return -ENOMEM; - - r = bus_gvariant_is_fixed_size(contents); - if (r < 0) - return r; - - *begin = m->body_size; - *need_offsets = r == 0; - } else { - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0, false, false)) - return -ENOMEM; - } - - return 0; -} - -_public_ int sd_bus_message_open_container( - sd_bus_message *m, - char type, - const char *contents) { - - struct bus_container *c, *w; - uint32_t *array_size = NULL; - char *signature; - size_t before, begin = 0; - bool need_offsets = false; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(contents, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - /* Make sure we have space for one more container */ - if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) { - m->poisoned = true; - return -ENOMEM; - } - - c = message_get_container(m); - - signature = strdup(contents); - if (!signature) { - m->poisoned = true; - return -ENOMEM; - } - - /* Save old index in the parent container, in case we have to - * abort this container */ - c->saved_index = c->index; - before = m->body_size; - - if (type == SD_BUS_TYPE_ARRAY) - r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets); - else if (type == SD_BUS_TYPE_VARIANT) - r = bus_message_open_variant(m, c, contents); - else if (type == SD_BUS_TYPE_STRUCT) - r = bus_message_open_struct(m, c, contents, &begin, &need_offsets); - else if (type == SD_BUS_TYPE_DICT_ENTRY) - r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets); - else - r = -EINVAL; - - if (r < 0) { - free(signature); - return r; - } - - /* OK, let's fill it in */ - w = m->containers + m->n_containers++; - w->enclosing = type; - w->signature = signature; - w->index = 0; - w->array_size = array_size; - w->before = before; - w->begin = begin; - w->n_offsets = w->offsets_allocated = 0; - w->offsets = NULL; - w->need_offsets = need_offsets; - - return 0; -} - -static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) { - - assert(m); - assert(c); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) - return 0; - - if (c->need_offsets) { - size_t payload, sz, i; - uint8_t *a; - - /* Variable-width arrays */ - - payload = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0; - sz = bus_gvariant_determine_word_size(payload, c->n_offsets); - - a = message_extend_body(m, 1, sz * c->n_offsets, true, false); - if (!a) - return -ENOMEM; - - for (i = 0; i < c->n_offsets; i++) - bus_gvariant_write_word_le(a + sz*i, sz, c->offsets[i] - c->begin); - } else { - void *a; - - /* Fixed-width or empty arrays */ - - a = message_extend_body(m, 1, 0, true, false); /* let's add offset to parent */ - if (!a) - return -ENOMEM; - } - - return 0; -} - -static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) { - uint8_t *a; - size_t l; - - assert(m); - assert(c); - assert(c->signature); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) - return 0; - - l = strlen(c->signature); - - a = message_extend_body(m, 1, 1 + l, true, false); - if (!a) - return -ENOMEM; - - a[0] = 0; - memcpy(a+1, c->signature, l); - - return 0; -} - -static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) { - bool fixed_size = true; - size_t n_variable = 0; - unsigned i = 0; - const char *p; - uint8_t *a; - int r; - - assert(m); - assert(c); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) - return 0; - - p = strempty(c->signature); - while (*p != 0) { - size_t n; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - else { - char t[n+1]; - - memcpy(t, p, n); - t[n] = 0; - - r = bus_gvariant_is_fixed_size(t); - if (r < 0) - return r; - } - - assert(!c->need_offsets || i <= c->n_offsets); - - /* We need to add an offset for each item that has a - * variable size and that is not the last one in the - * list */ - if (r == 0) - fixed_size = false; - if (r == 0 && p[n] != 0) - n_variable++; - - i++; - p += n; - } - - assert(!c->need_offsets || i == c->n_offsets); - assert(c->need_offsets || n_variable == 0); - - if (isempty(c->signature)) { - /* The unary type is encoded as fixed 1 byte padding */ - a = message_extend_body(m, 1, 1, add_offset, false); - if (!a) - return -ENOMEM; - - *a = 0; - } else if (n_variable <= 0) { - int alignment = 1; - - /* Structures with fixed-size members only have to be - * fixed-size themselves. But gvariant requires all fixed-size - * elements to be sized a multiple of their alignment. Hence, - * we must *always* add final padding after the last member so - * the overall size of the structure is properly aligned. */ - if (fixed_size) - alignment = bus_gvariant_get_alignment(strempty(c->signature)); - - assert(alignment > 0); - - a = message_extend_body(m, alignment, 0, add_offset, false); - if (!a) - return -ENOMEM; - } else { - size_t sz; - unsigned j; - - assert(c->offsets[c->n_offsets-1] == m->body_size); - - sz = bus_gvariant_determine_word_size(m->body_size - c->begin, n_variable); - - a = message_extend_body(m, 1, sz * n_variable, add_offset, false); - if (!a) - return -ENOMEM; - - p = strempty(c->signature); - for (i = 0, j = 0; i < c->n_offsets; i++) { - unsigned k; - size_t n; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - else { - char t[n+1]; - - memcpy(t, p, n); - t[n] = 0; - - p += n; - - r = bus_gvariant_is_fixed_size(t); - if (r < 0) - return r; - if (r > 0 || p[0] == 0) - continue; - } - - k = n_variable - 1 - j; - - bus_gvariant_write_word_le(a + k * sz, sz, c->offsets[i] - c->begin); - - j++; - } - } - - return 0; -} - -_public_ int sd_bus_message_close_container(sd_bus_message *m) { - struct bus_container *c; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers > 0, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - c = message_get_container(m); - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - if (c->signature && c->signature[c->index] != 0) - return -EINVAL; - - m->n_containers--; - - if (c->enclosing == SD_BUS_TYPE_ARRAY) - r = bus_message_close_array(m, c); - else if (c->enclosing == SD_BUS_TYPE_VARIANT) - r = bus_message_close_variant(m, c); - else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY) - r = bus_message_close_struct(m, c, true); - else - assert_not_reached("Unknown container type"); - - free(c->signature); - free(c->offsets); - - return r; -} - -typedef struct { - const char *types; - unsigned n_struct; - unsigned n_array; -} TypeStack; - -static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) { - assert(stack); - assert(max > 0); - - if (*i >= max) - return -EINVAL; - - stack[*i].types = types; - stack[*i].n_struct = n_struct; - stack[*i].n_array = n_array; - (*i)++; - - return 0; -} - -static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) { - assert(stack); - assert(max > 0); - assert(types); - assert(n_struct); - assert(n_array); - - if (*i <= 0) - return 0; - - (*i)--; - *types = stack[*i].types; - *n_struct = stack[*i].n_struct; - *n_array = stack[*i].n_array; - - return 1; -} - -int bus_message_append_ap( - sd_bus_message *m, - const char *types, - va_list ap) { - - unsigned n_array, n_struct; - TypeStack stack[BUS_CONTAINER_DEPTH]; - unsigned stack_ptr = 0; - int r; - - assert(m); - - if (!types) - return 0; - - n_array = (unsigned) -1; - n_struct = strlen(types); - - for (;;) { - const char *t; - - if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) { - r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - continue; - } - - t = types; - if (n_array != (unsigned) -1) - n_array--; - else { - types++; - n_struct--; - } - - switch (*t) { - - case SD_BUS_TYPE_BYTE: { - uint8_t x; - - x = (uint8_t) va_arg(ap, int); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: { - uint32_t x; - - /* We assume a boolean is the same as int32_t */ - assert_cc(sizeof(int32_t) == sizeof(int)); - - x = va_arg(ap, uint32_t); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: { - uint16_t x; - - x = (uint16_t) va_arg(ap, int); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: { - uint64_t x; - - x = va_arg(ap, uint64_t); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_DOUBLE: { - double x; - - x = va_arg(ap, double); - r = sd_bus_message_append_basic(m, *t, &x); - break; - } - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: { - const char *x; - - x = va_arg(ap, const char*); - r = sd_bus_message_append_basic(m, *t, x); - break; - } - - case SD_BUS_TYPE_ARRAY: { - size_t k; - - r = signature_element_length(t + 1, &k); - if (r < 0) - return r; - - { - char s[k + 1]; - memcpy(s, t + 1, k); - s[k] = 0; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); - if (r < 0) - return r; - } - - if (n_array == (unsigned) -1) { - types += k; - n_struct -= k; - } - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = t + 1; - n_struct = k; - n_array = va_arg(ap, unsigned); - - break; - } - - case SD_BUS_TYPE_VARIANT: { - const char *s; - - s = va_arg(ap, const char*); - if (!s) - return -EINVAL; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s); - if (r < 0) - return r; - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = s; - n_struct = strlen(s); - n_array = (unsigned) -1; - - break; - } - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - size_t k; - - r = signature_element_length(t, &k); - if (r < 0) - return r; - - { - char s[k - 1]; - - memcpy(s, t + 1, k - 2); - s[k - 2] = 0; - - r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); - if (r < 0) - return r; - } - - if (n_array == (unsigned) -1) { - types += k - 1; - n_struct -= k - 1; - } - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = t + 1; - n_struct = k - 2; - n_array = (unsigned) -1; - - break; - } - - default: - r = -EINVAL; - } - - if (r < 0) - return r; - } - - return 1; -} - -_public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) { - va_list ap; - int r; - - assert_return(m, -EINVAL); - assert_return(types, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - - return r; -} - -_public_ int sd_bus_message_append_array_space( - sd_bus_message *m, - char type, - size_t size, - void **ptr) { - - ssize_t align, sz; - void *a; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL); - assert_return(ptr || size == 0, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - /* alignment and size of the trivial types (except bool) is - * identical for gvariant and dbus1 marshalling */ - align = bus_type_get_alignment(type); - sz = bus_type_get_size(type); - - assert_se(align > 0); - assert_se(sz > 0); - - if (size % sz != 0) - return -EINVAL; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); - if (r < 0) - return r; - - a = message_extend_body(m, align, size, false, false); - if (!a) - return -ENOMEM; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - *ptr = a; - return 0; -} - -_public_ int sd_bus_message_append_array( - sd_bus_message *m, - char type, - const void *ptr, - size_t size) { - int r; - void *p; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(bus_type_is_trivial(type), -EINVAL); - assert_return(ptr || size == 0, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - r = sd_bus_message_append_array_space(m, type, size, &p); - if (r < 0) - return r; - - memcpy_safe(p, ptr, size); - - return 0; -} - -_public_ int sd_bus_message_append_array_iovec( - sd_bus_message *m, - char type, - const struct iovec *iov, - unsigned n) { - - size_t size; - unsigned i; - void *p; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(bus_type_is_trivial(type), -EINVAL); - assert_return(iov || n == 0, -EINVAL); - assert_return(!m->poisoned, -ESTALE); - - size = IOVEC_TOTAL_SIZE(iov, n); - - r = sd_bus_message_append_array_space(m, type, size, &p); - if (r < 0) - return r; - - for (i = 0; i < n; i++) { - - if (iov[i].iov_base) - memcpy(p, iov[i].iov_base, iov[i].iov_len); - else - memzero(p, iov[i].iov_len); - - p = (uint8_t*) p + iov[i].iov_len; - } - - return 0; -} - -_public_ int sd_bus_message_append_array_memfd( - sd_bus_message *m, - char type, - int memfd, - uint64_t offset, - uint64_t size) { - - _cleanup_close_ int copy_fd = -1; - struct bus_body_part *part; - ssize_t align, sz; - uint64_t real_size; - void *a; - int r; - - assert_return(m, -EINVAL); - assert_return(memfd >= 0, -EBADF); - assert_return(bus_type_is_trivial(type), -EINVAL); - assert_return(size > 0, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - r = memfd_set_sealed(memfd); - if (r < 0) - return r; - - copy_fd = dup(memfd); - if (copy_fd < 0) - return copy_fd; - - r = memfd_get_size(memfd, &real_size); - if (r < 0) - return r; - - if (offset == 0 && size == (uint64_t) -1) - size = real_size; - else if (offset + size > real_size) - return -EMSGSIZE; - - align = bus_type_get_alignment(type); - sz = bus_type_get_size(type); - - assert_se(align > 0); - assert_se(sz > 0); - - if (offset % align != 0) - return -EINVAL; - - if (size % sz != 0) - return -EINVAL; - - if (size > (uint64_t) (uint32_t) -1) - return -EINVAL; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); - if (r < 0) - return r; - - a = message_extend_body(m, align, 0, false, false); - if (!a) - return -ENOMEM; - - part = message_append_part(m); - if (!part) - return -ENOMEM; - - part->memfd = copy_fd; - part->memfd_offset = offset; - part->sealed = true; - part->size = size; - copy_fd = -1; - - m->body_size += size; - message_extend_containers(m, size); - - return sd_bus_message_close_container(m); -} - -_public_ int sd_bus_message_append_string_memfd( - sd_bus_message *m, - int memfd, - uint64_t offset, - uint64_t size) { - - _cleanup_close_ int copy_fd = -1; - struct bus_body_part *part; - struct bus_container *c; - uint64_t real_size; - void *a; - int r; - - assert_return(m, -EINVAL); - assert_return(memfd >= 0, -EBADF); - assert_return(size > 0, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - r = memfd_set_sealed(memfd); - if (r < 0) - return r; - - copy_fd = dup(memfd); - if (copy_fd < 0) - return copy_fd; - - r = memfd_get_size(memfd, &real_size); - if (r < 0) - return r; - - if (offset == 0 && size == (uint64_t) -1) - size = real_size; - else if (offset + size > real_size) - return -EMSGSIZE; - - /* We require this to be NUL terminated */ - if (size == 0) - return -EINVAL; - - if (size > (uint64_t) (uint32_t) -1) - return -EINVAL; - - c = message_get_container(m); - if (c->signature && c->signature[c->index]) { - /* Container signature is already set */ - - if (c->signature[c->index] != SD_BUS_TYPE_STRING) - return -ENXIO; - } else { - char *e; - - /* Maybe we can append to the signature? But only if this is the top-level container */ - if (c->enclosing != 0) - return -ENXIO; - - e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL); - if (!e) { - m->poisoned = true; - return -ENOMEM; - } - } - - if (!BUS_MESSAGE_IS_GVARIANT(m)) { - a = message_extend_body(m, 4, 4, false, false); - if (!a) - return -ENOMEM; - - *(uint32_t*) a = size - 1; - } - - part = message_append_part(m); - if (!part) - return -ENOMEM; - - part->memfd = copy_fd; - part->memfd_offset = offset; - part->sealed = true; - part->size = size; - copy_fd = -1; - - m->body_size += size; - message_extend_containers(m, size); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - r = message_add_offset(m, m->body_size); - if (r < 0) { - m->poisoned = true; - return -ENOMEM; - } - } - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - return 0; -} - -_public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) { - char **i; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->poisoned, -ESTALE); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return r; - - STRV_FOREACH(i, l) { - r = sd_bus_message_append_basic(m, 's', *i); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(m); -} - -static int bus_message_close_header(sd_bus_message *m) { - - assert(m); - - /* The actual user data is finished now, we just complete the - variant and struct now (at least on gvariant). Remember - this position, so that during parsing we know where to - put the outer container end. */ - m->user_body_size = m->body_size; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - const char *signature; - size_t sz, l; - void *d; - - /* Add offset table to end of fields array */ - if (m->n_header_offsets >= 1) { - uint8_t *a; - unsigned i; - - assert(m->fields_size == m->header_offsets[m->n_header_offsets-1]); - - sz = bus_gvariant_determine_word_size(m->fields_size, m->n_header_offsets); - a = message_extend_fields(m, 1, sz * m->n_header_offsets, false); - if (!a) - return -ENOMEM; - - for (i = 0; i < m->n_header_offsets; i++) - bus_gvariant_write_word_le(a + sz*i, sz, m->header_offsets[i]); - } - - /* Add gvariant NUL byte plus signature to the end of - * the body, followed by the final offset pointing to - * the end of the fields array */ - - signature = strempty(m->root_container.signature); - l = strlen(signature); - - sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l + 2, 1); - d = message_extend_body(m, 1, 1 + l + 2 + sz, false, true); - if (!d) - return -ENOMEM; - - *(uint8_t*) d = 0; - *((uint8_t*) d + 1) = SD_BUS_TYPE_STRUCT_BEGIN; - memcpy((uint8_t*) d + 2, signature, l); - *((uint8_t*) d + 1 + l + 1) = SD_BUS_TYPE_STRUCT_END; - - bus_gvariant_write_word_le((uint8_t*) d + 1 + l + 2, sz, sizeof(struct bus_header) + m->fields_size); - - m->footer = d; - m->footer_accessible = 1 + l + 2 + sz; - } else { - m->header->dbus1.fields_size = m->fields_size; - m->header->dbus1.body_size = m->body_size; - } - - return 0; -} - -int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { - struct bus_body_part *part; - size_t a; - unsigned i; - int r; - - assert(m); - - if (m->sealed) - return -EPERM; - - if (m->n_containers > 0) - return -EBADMSG; - - if (m->poisoned) - return -ESTALE; - - if (cookie > 0xffffffffULL && - !BUS_MESSAGE_IS_GVARIANT(m)) - return -EOPNOTSUPP; - - /* In vtables the return signature of method calls is listed, - * let's check if they match if this is a response */ - if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN && - m->enforced_reply_signature && - !streq(strempty(m->root_container.signature), m->enforced_reply_signature)) - return -ENOMSG; - - /* If gvariant marshalling is used we need to close the body structure */ - r = bus_message_close_struct(m, &m->root_container, false); - if (r < 0) - return r; - - /* If there's a non-trivial signature set, then add it in - * here, but only on dbus1 */ - if (!isempty(m->root_container.signature) && !BUS_MESSAGE_IS_GVARIANT(m)) { - r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL); - if (r < 0) - return r; - } - - if (m->n_fds > 0) { - r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds); - if (r < 0) - return r; - } - - r = bus_message_close_header(m); - if (r < 0) - return r; - - if (BUS_MESSAGE_IS_GVARIANT(m)) - m->header->dbus2.cookie = cookie; - else - m->header->dbus1.serial = (uint32_t) cookie; - - m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout; - - /* Add padding at the end of the fields part, since we know - * the body needs to start at an 8 byte alignment. We made - * sure we allocated enough space for this, so all we need to - * do here is to zero it out. */ - a = ALIGN8(m->fields_size) - m->fields_size; - if (a > 0) - memzero((uint8_t*) BUS_MESSAGE_FIELDS(m) + m->fields_size, a); - - /* If this is something we can send as memfd, then let's seal - the memfd now. Note that we can send memfds as payload only - for directed messages, and not for broadcasts. */ - if (m->destination && m->bus->use_memfd) { - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0 && - !part->sealed && - (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0) && - part != m->body_end) { /* The last part may never be sent as memfd */ - uint64_t sz; - - /* Try to seal it if that makes - * sense. First, unmap our own map to - * make sure we don't keep it busy. */ - bus_body_part_unmap(part); - - /* Then, sync up real memfd size */ - sz = part->size; - r = memfd_set_size(part->memfd, sz); - if (r < 0) - return r; - - /* Finally, try to seal */ - if (memfd_set_sealed(part->memfd) >= 0) - part->sealed = true; - } - } - - m->root_container.end = m->user_body_size; - m->root_container.index = 0; - m->root_container.offset_index = 0; - m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0; - - m->sealed = true; - - return 0; -} - -int bus_body_part_map(struct bus_body_part *part) { - void *p; - size_t psz, shift; - - assert_se(part); - - if (part->data) - return 0; - - if (part->size <= 0) - return 0; - - /* For smaller zero parts (as used for padding) we don't need to map anything... */ - if (part->memfd < 0 && part->is_zero && part->size < 8) { - static const uint8_t zeroes[7] = { }; - part->data = (void*) zeroes; - return 0; - } - - shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); - psz = PAGE_ALIGN(part->size + shift); - - if (part->memfd >= 0) - p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift); - else if (part->is_zero) - p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - else - return -EINVAL; - - if (p == MAP_FAILED) - return -errno; - - part->mapped = psz; - part->mmap_begin = p; - part->data = (uint8_t*) p + shift; - part->munmap_this = true; - - return 0; -} - -void bus_body_part_unmap(struct bus_body_part *part) { - - assert_se(part); - - if (part->memfd < 0) - return; - - if (!part->mmap_begin) - return; - - if (!part->munmap_this) - return; - - assert_se(munmap(part->mmap_begin, part->mapped) == 0); - - part->mmap_begin = NULL; - part->data = NULL; - part->mapped = 0; - part->munmap_this = false; - - return; -} - -static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) { - size_t k, start, end; - - assert(rindex); - assert(align > 0); - - start = ALIGN_TO((size_t) *rindex, align); - end = start + nbytes; - - if (end > sz) - return -EBADMSG; - - /* Verify that padding is 0 */ - for (k = *rindex; k < start; k++) - if (((const uint8_t*) p)[k] != 0) - return -EBADMSG; - - if (r) - *r = (uint8_t*) p + start; - - *rindex = end; - - return 1; -} - -static bool message_end_of_signature(sd_bus_message *m) { - struct bus_container *c; - - assert(m); - - c = message_get_container(m); - return !c->signature || c->signature[c->index] == 0; -} - -static bool message_end_of_array(sd_bus_message *m, size_t index) { - struct bus_container *c; - - assert(m); - - c = message_get_container(m); - if (c->enclosing != SD_BUS_TYPE_ARRAY) - return false; - - if (BUS_MESSAGE_IS_GVARIANT(m)) - return index >= c->end; - else { - assert(c->array_size); - return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size); - } -} - -_public_ int sd_bus_message_at_end(sd_bus_message *m, int complete) { - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - - if (complete && m->n_containers > 0) - return false; - - if (message_end_of_signature(m)) - return true; - - if (message_end_of_array(m, m->rindex)) - return true; - - return false; -} - -static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) { - struct bus_body_part *part; - size_t begin; - int r; - - assert(m); - - if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) { - part = m->cached_rindex_part; - begin = m->cached_rindex_part_begin; - } else { - part = &m->body; - begin = 0; - } - - while (part) { - if (index < begin) - return NULL; - - if (index + sz <= begin + part->size) { - - r = bus_body_part_map(part); - if (r < 0) - return NULL; - - if (p) - *p = (uint8_t*) part->data + index - begin; - - m->cached_rindex_part = part; - m->cached_rindex_part_begin = begin; - - return part; - } - - begin += part->size; - part = part->next; - } - - return NULL; -} - -static int container_next_item(sd_bus_message *m, struct bus_container *c, size_t *rindex) { - int r; - - assert(m); - assert(c); - assert(rindex); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) - return 0; - - if (c->enclosing == SD_BUS_TYPE_ARRAY) { - int sz; - - sz = bus_gvariant_get_size(c->signature); - if (sz < 0) { - int alignment; - - if (c->offset_index+1 >= c->n_offsets) - goto end; - - /* Variable-size array */ - - alignment = bus_gvariant_get_alignment(c->signature); - assert(alignment > 0); - - *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); - c->item_size = c->offsets[c->offset_index+1] - *rindex; - } else { - - if (c->offset_index+1 >= (c->end-c->begin)/sz) - goto end; - - /* Fixed-size array */ - *rindex = c->begin + (c->offset_index+1) * sz; - c->item_size = sz; - } - - c->offset_index++; - - } else if (c->enclosing == 0 || - c->enclosing == SD_BUS_TYPE_STRUCT || - c->enclosing == SD_BUS_TYPE_DICT_ENTRY) { - - int alignment; - size_t n, j; - - if (c->offset_index+1 >= c->n_offsets) - goto end; - - r = signature_element_length(c->signature + c->index, &n); - if (r < 0) - return r; - - r = signature_element_length(c->signature + c->index + n, &j); - if (r < 0) - return r; - else { - char t[j+1]; - memcpy(t, c->signature + c->index + n, j); - t[j] = 0; - - alignment = bus_gvariant_get_alignment(t); - } - - assert(alignment > 0); - - *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); - c->item_size = c->offsets[c->offset_index+1] - *rindex; - - c->offset_index++; - - } else if (c->enclosing == SD_BUS_TYPE_VARIANT) - goto end; - else - assert_not_reached("Unknown container type"); - - return 0; - -end: - /* Reached the end */ - *rindex = c->end; - c->item_size = 0; - return 0; -} - - -static int message_peek_body( - sd_bus_message *m, - size_t *rindex, - size_t align, - size_t nbytes, - void **ret) { - - size_t k, start, end, padding; - struct bus_body_part *part; - uint8_t *q; - - assert(m); - assert(rindex); - assert(align > 0); - - start = ALIGN_TO((size_t) *rindex, align); - padding = start - *rindex; - end = start + nbytes; - - if (end > m->user_body_size) - return -EBADMSG; - - part = find_part(m, *rindex, padding, (void**) &q); - if (!part) - return -EBADMSG; - - if (q) { - /* Verify padding */ - for (k = 0; k < padding; k++) - if (q[k] != 0) - return -EBADMSG; - } - - part = find_part(m, start, nbytes, (void**) &q); - if (!part || (nbytes > 0 && !q)) - return -EBADMSG; - - *rindex = end; - - if (ret) - *ret = q; - - return 0; -} - -static bool validate_nul(const char *s, size_t l) { - - /* Check for NUL chars in the string */ - if (memchr(s, 0, l)) - return false; - - /* Check for NUL termination */ - if (s[l] != 0) - return false; - - return true; -} - -static bool validate_string(const char *s, size_t l) { - - if (!validate_nul(s, l)) - return false; - - /* Check if valid UTF8 */ - if (!utf8_is_valid(s)) - return false; - - return true; -} - -static bool validate_signature(const char *s, size_t l) { - - if (!validate_nul(s, l)) - return false; - - /* Check if valid signature */ - if (!signature_is_valid(s, true)) - return false; - - return true; -} - -static bool validate_object_path(const char *s, size_t l) { - - if (!validate_nul(s, l)) - return false; - - if (!object_path_is_valid(s)) - return false; - - return true; -} - -_public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { - struct bus_container *c; - size_t rindex; - void *q; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(bus_type_is_basic(type), -EINVAL); - - if (message_end_of_signature(m)) - return -ENXIO; - - if (message_end_of_array(m, m->rindex)) - return 0; - - c = message_get_container(m); - if (c->signature[c->index] != type) - return -ENXIO; - - rindex = m->rindex; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - - if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { - bool ok; - - r = message_peek_body(m, &rindex, 1, c->item_size, &q); - if (r < 0) - return r; - - if (type == SD_BUS_TYPE_STRING) - ok = validate_string(q, c->item_size-1); - else if (type == SD_BUS_TYPE_OBJECT_PATH) - ok = validate_object_path(q, c->item_size-1); - else - ok = validate_signature(q, c->item_size-1); - - if (!ok) - return -EBADMSG; - - if (p) - *(const char**) p = q; - } else { - int sz, align; - - sz = bus_gvariant_get_size(CHAR_TO_STR(type)); - assert(sz > 0); - if ((size_t) sz != c->item_size) - return -EBADMSG; - - align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); - assert(align > 0); - - r = message_peek_body(m, &rindex, align, c->item_size, &q); - if (r < 0) - return r; - - switch (type) { - - case SD_BUS_TYPE_BYTE: - if (p) - *(uint8_t*) p = *(uint8_t*) q; - break; - - case SD_BUS_TYPE_BOOLEAN: - if (p) - *(int*) p = !!*(uint8_t*) q; - break; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - if (p) - *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q); - break; - - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - if (p) - *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - break; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - if (p) - *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); - break; - - case SD_BUS_TYPE_UNIX_FD: { - uint32_t j; - - j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - if (j >= m->n_fds) - return -EBADMSG; - - if (p) - *(int*) p = m->fds[j]; - - break; - } - - default: - assert_not_reached("unexpected type"); - } - } - - r = container_next_item(m, c, &rindex); - if (r < 0) - return r; - } else { - - if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) { - uint32_t l; - bool ok; - - r = message_peek_body(m, &rindex, 4, 4, &q); - if (r < 0) - return r; - - l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - r = message_peek_body(m, &rindex, 1, l+1, &q); - if (r < 0) - return r; - - if (type == SD_BUS_TYPE_OBJECT_PATH) - ok = validate_object_path(q, l); - else - ok = validate_string(q, l); - if (!ok) - return -EBADMSG; - - if (p) - *(const char**) p = q; - - } else if (type == SD_BUS_TYPE_SIGNATURE) { - uint8_t l; - - r = message_peek_body(m, &rindex, 1, 1, &q); - if (r < 0) - return r; - - l = *(uint8_t*) q; - r = message_peek_body(m, &rindex, 1, l+1, &q); - if (r < 0) - return r; - - if (!validate_signature(q, l)) - return -EBADMSG; - - if (p) - *(const char**) p = q; - - } else { - ssize_t sz, align; - - align = bus_type_get_alignment(type); - assert(align > 0); - - sz = bus_type_get_size(type); - assert(sz > 0); - - r = message_peek_body(m, &rindex, align, sz, &q); - if (r < 0) - return r; - - switch (type) { - - case SD_BUS_TYPE_BYTE: - if (p) - *(uint8_t*) p = *(uint8_t*) q; - break; - - case SD_BUS_TYPE_BOOLEAN: - if (p) - *(int*) p = !!*(uint32_t*) q; - break; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - if (p) - *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q); - break; - - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - if (p) - *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - break; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - if (p) - *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); - break; - - case SD_BUS_TYPE_UNIX_FD: { - uint32_t j; - - j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - if (j >= m->n_fds) - return -EBADMSG; - - if (p) - *(int*) p = m->fds[j]; - break; - } - - default: - assert_not_reached("Unknown basic type..."); - } - } - } - - m->rindex = rindex; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - return 1; -} - -static int bus_message_enter_array( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - uint32_t **array_size, - size_t *item_size, - size_t **offsets, - size_t *n_offsets) { - - size_t rindex; - void *q; - int r, alignment; - - assert(m); - assert(c); - assert(contents); - assert(array_size); - assert(item_size); - assert(offsets); - assert(n_offsets); - - if (!signature_is_single(contents, true)) - return -EINVAL; - - if (!c->signature || c->signature[c->index] == 0) - return -ENXIO; - - if (c->signature[c->index] != SD_BUS_TYPE_ARRAY) - return -ENXIO; - - if (!startswith(c->signature + c->index + 1, contents)) - return -ENXIO; - - rindex = m->rindex; - - if (!BUS_MESSAGE_IS_GVARIANT(m)) { - /* dbus1 */ - - r = message_peek_body(m, &rindex, 4, 4, &q); - if (r < 0) - return r; - - if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE) - return -EBADMSG; - - alignment = bus_type_get_alignment(contents[0]); - if (alignment < 0) - return alignment; - - r = message_peek_body(m, &rindex, alignment, 0, NULL); - if (r < 0) - return r; - - *array_size = (uint32_t*) q; - - } else if (c->item_size <= 0) { - - /* gvariant: empty array */ - *item_size = 0; - *offsets = NULL; - *n_offsets = 0; - - } else if (bus_gvariant_is_fixed_size(contents)) { - - /* gvariant: fixed length array */ - *item_size = bus_gvariant_get_size(contents); - *offsets = NULL; - *n_offsets = 0; - - } else { - size_t where, p = 0, framing, sz; - unsigned i; - - /* gvariant: variable length array */ - sz = bus_gvariant_determine_word_size(c->item_size, 0); - - where = rindex + c->item_size - sz; - r = message_peek_body(m, &where, 1, sz, &q); - if (r < 0) - return r; - - framing = bus_gvariant_read_word_le(q, sz); - if (framing > c->item_size - sz) - return -EBADMSG; - if ((c->item_size - framing) % sz != 0) - return -EBADMSG; - - *n_offsets = (c->item_size - framing) / sz; - - where = rindex + framing; - r = message_peek_body(m, &where, 1, *n_offsets * sz, &q); - if (r < 0) - return r; - - *offsets = new(size_t, *n_offsets); - if (!*offsets) - return -ENOMEM; - - for (i = 0; i < *n_offsets; i++) { - size_t x; - - x = bus_gvariant_read_word_le((uint8_t*) q + i * sz, sz); - if (x > c->item_size - sz) - return -EBADMSG; - if (x < p) - return -EBADMSG; - - (*offsets)[i] = rindex + x; - p = x; - } - - *item_size = (*offsets)[0] - rindex; - } - - m->rindex = rindex; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index += 1 + strlen(contents); - - return 1; -} - -static int bus_message_enter_variant( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *item_size) { - - size_t rindex; - uint8_t l; - void *q; - int r; - - assert(m); - assert(c); - assert(contents); - assert(item_size); - - if (!signature_is_single(contents, false)) - return -EINVAL; - - if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) - return -EINVAL; - - if (!c->signature || c->signature[c->index] == 0) - return -ENXIO; - - if (c->signature[c->index] != SD_BUS_TYPE_VARIANT) - return -ENXIO; - - rindex = m->rindex; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t k, where; - - k = strlen(contents); - if (1+k > c->item_size) - return -EBADMSG; - - where = rindex + c->item_size - (1+k); - r = message_peek_body(m, &where, 1, 1+k, &q); - if (r < 0) - return r; - - if (*(char*) q != 0) - return -EBADMSG; - - if (memcmp((uint8_t*) q+1, contents, k)) - return -ENXIO; - - *item_size = c->item_size - (1+k); - - } else { - r = message_peek_body(m, &rindex, 1, 1, &q); - if (r < 0) - return r; - - l = *(uint8_t*) q; - r = message_peek_body(m, &rindex, 1, l+1, &q); - if (r < 0) - return r; - - if (!validate_signature(q, l)) - return -EBADMSG; - - if (!streq(q, contents)) - return -ENXIO; - } - - m->rindex = rindex; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index++; - - return 1; -} - -static int build_struct_offsets( - sd_bus_message *m, - const char *signature, - size_t size, - size_t *item_size, - size_t **offsets, - size_t *n_offsets) { - - unsigned n_variable = 0, n_total = 0, v; - size_t previous = 0, where; - const char *p; - size_t sz; - void *q; - int r; - - assert(m); - assert(item_size); - assert(offsets); - assert(n_offsets); - - if (isempty(signature)) { - /* Unary type is encoded as *fixed* 1 byte padding */ - r = message_peek_body(m, &m->rindex, 1, 1, &q); - if (r < 0) - return r; - - if (*(uint8_t *) q != 0) - return -EBADMSG; - - *item_size = 0; - *offsets = NULL; - *n_offsets = 0; - return 0; - } - - sz = bus_gvariant_determine_word_size(size, 0); - if (sz <= 0) - return -EBADMSG; - - /* First, loop over signature and count variable elements and - * elements in general. We use this to know how large the - * offset array is at the end of the structure. Note that - * GVariant only stores offsets for all variable size elements - * that are not the last item. */ - - p = signature; - while (*p != 0) { - size_t n; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - else { - char t[n+1]; - - memcpy(t, p, n); - t[n] = 0; - - r = bus_gvariant_is_fixed_size(t); - } - - if (r < 0) - return r; - if (r == 0 && p[n] != 0) /* except the last item */ - n_variable++; - n_total++; - - p += n; - } - - if (size < n_variable * sz) - return -EBADMSG; - - where = m->rindex + size - (n_variable * sz); - r = message_peek_body(m, &where, 1, n_variable * sz, &q); - if (r < 0) - return r; - - v = n_variable; - - *offsets = new(size_t, n_total); - if (!*offsets) - return -ENOMEM; - - *n_offsets = 0; - - /* Second, loop again and build an offset table */ - p = signature; - while (*p != 0) { - size_t n, offset; - int k; - - r = signature_element_length(p, &n); - if (r < 0) - return r; - else { - char t[n+1]; - - memcpy(t, p, n); - t[n] = 0; - - k = bus_gvariant_get_size(t); - if (k < 0) { - size_t x; - - /* variable size */ - if (v > 0) { - v--; - - x = bus_gvariant_read_word_le((uint8_t*) q + v*sz, sz); - if (x >= size) - return -EBADMSG; - if (m->rindex + x < previous) - return -EBADMSG; - } else - /* The last item's end - * is determined from - * the start of the - * offset array */ - x = size - (n_variable * sz); - - offset = m->rindex + x; - - } else { - size_t align; - - /* fixed size */ - align = bus_gvariant_get_alignment(t); - assert(align > 0); - - offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k; - } - } - - previous = (*offsets)[(*n_offsets)++] = offset; - p += n; - } - - assert(v == 0); - assert(*n_offsets == n_total); - - *item_size = (*offsets)[0] - m->rindex; - return 0; -} - -static int enter_struct_or_dict_entry( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *item_size, - size_t **offsets, - size_t *n_offsets) { - - int r; - - assert(m); - assert(c); - assert(contents); - assert(item_size); - assert(offsets); - assert(n_offsets); - - if (!BUS_MESSAGE_IS_GVARIANT(m)) { - - /* dbus1 */ - r = message_peek_body(m, &m->rindex, 8, 0, NULL); - if (r < 0) - return r; - - } else - /* gvariant with contents */ - return build_struct_offsets(m, contents, c->item_size, item_size, offsets, n_offsets); - - return 0; -} - -static int bus_message_enter_struct( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *item_size, - size_t **offsets, - size_t *n_offsets) { - - size_t l; - int r; - - assert(m); - assert(c); - assert(contents); - assert(item_size); - assert(offsets); - assert(n_offsets); - - if (!signature_is_valid(contents, false)) - return -EINVAL; - - if (!c->signature || c->signature[c->index] == 0) - return -ENXIO; - - l = strlen(contents); - - if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN || - !startswith(c->signature + c->index + 1, contents) || - c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END) - return -ENXIO; - - r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets); - if (r < 0) - return r; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index += 1 + l + 1; - - return 1; -} - -static int bus_message_enter_dict_entry( - sd_bus_message *m, - struct bus_container *c, - const char *contents, - size_t *item_size, - size_t **offsets, - size_t *n_offsets) { - - size_t l; - int r; - - assert(m); - assert(c); - assert(contents); - - if (!signature_is_pair(contents)) - return -EINVAL; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - return -ENXIO; - - if (!c->signature || c->signature[c->index] == 0) - return 0; - - l = strlen(contents); - - if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN || - !startswith(c->signature + c->index + 1, contents) || - c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END) - return -ENXIO; - - r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets); - if (r < 0) - return r; - - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index += 1 + l + 1; - - return 1; -} - -_public_ int sd_bus_message_enter_container(sd_bus_message *m, - char type, - const char *contents) { - struct bus_container *c, *w; - uint32_t *array_size = NULL; - char *signature; - size_t before; - size_t *offsets = NULL; - size_t n_offsets = 0, item_size = 0; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(type != 0 || !contents, -EINVAL); - - if (type == 0 || !contents) { - const char *cc; - char tt; - - /* Allow entering into anonymous containers */ - r = sd_bus_message_peek_type(m, &tt, &cc); - if (r < 0) - return r; - - if (type != 0 && type != tt) - return -ENXIO; - - if (contents && !streq(contents, cc)) - return -ENXIO; - - type = tt; - contents = cc; - } - - /* - * We enforce a global limit on container depth, that is much - * higher than the 32 structs and 32 arrays the specification - * mandates. This is simpler to implement for us, and we need - * this only to ensure our container array doesn't grow - * without bounds. We are happy to return any data from a - * message as long as the data itself is valid, even if the - * overall message might be not. - * - * Note that the message signature is validated when - * parsing the headers, and that validation does check the - * 32/32 limit. - * - * Note that the specification defines no limits on the depth - * of stacked variants, but we do. - */ - if (m->n_containers >= BUS_CONTAINER_DEPTH) - return -EBADMSG; - - if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) - return -ENOMEM; - - if (message_end_of_signature(m)) - return -ENXIO; - - if (message_end_of_array(m, m->rindex)) - return 0; - - c = message_get_container(m); - - signature = strdup(contents); - if (!signature) - return -ENOMEM; - - c->saved_index = c->index; - before = m->rindex; - - if (type == SD_BUS_TYPE_ARRAY) - r = bus_message_enter_array(m, c, contents, &array_size, &item_size, &offsets, &n_offsets); - else if (type == SD_BUS_TYPE_VARIANT) - r = bus_message_enter_variant(m, c, contents, &item_size); - else if (type == SD_BUS_TYPE_STRUCT) - r = bus_message_enter_struct(m, c, contents, &item_size, &offsets, &n_offsets); - else if (type == SD_BUS_TYPE_DICT_ENTRY) - r = bus_message_enter_dict_entry(m, c, contents, &item_size, &offsets, &n_offsets); - else - r = -EINVAL; - - if (r <= 0) { - free(signature); - free(offsets); - return r; - } - - /* OK, let's fill it in */ - w = m->containers + m->n_containers++; - w->enclosing = type; - w->signature = signature; - w->peeked_signature = NULL; - w->index = 0; - - w->before = before; - w->begin = m->rindex; - - /* Unary type has fixed size of 1, but virtual size of 0 */ - if (BUS_MESSAGE_IS_GVARIANT(m) && - type == SD_BUS_TYPE_STRUCT && - isempty(signature)) - w->end = m->rindex + 0; - else - w->end = m->rindex + c->item_size; - - w->array_size = array_size; - w->item_size = item_size; - w->offsets = offsets; - w->n_offsets = n_offsets; - w->offset_index = 0; - - return 1; -} - -_public_ int sd_bus_message_exit_container(sd_bus_message *m) { - struct bus_container *c; - unsigned saved; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(m->n_containers > 0, -ENXIO); - - c = message_get_container(m); - - if (c->enclosing != SD_BUS_TYPE_ARRAY) { - if (c->signature && c->signature[c->index] != 0) - return -EBUSY; - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - if (m->rindex < c->end) - return -EBUSY; - - } else if (c->enclosing == SD_BUS_TYPE_ARRAY) { - uint32_t l; - - l = BUS_MESSAGE_BSWAP32(m, *c->array_size); - if (c->begin + l != m->rindex) - return -EBUSY; - } - - free(c->signature); - free(c->peeked_signature); - free(c->offsets); - m->n_containers--; - - c = message_get_container(m); - - saved = c->index; - c->index = c->saved_index; - r = container_next_item(m, c, &m->rindex); - c->index = saved; - if (r < 0) - return r; - - return 1; -} - -static void message_quit_container(sd_bus_message *m) { - struct bus_container *c; - - assert(m); - assert(m->sealed); - assert(m->n_containers > 0); - - c = message_get_container(m); - - /* Undo seeks */ - assert(m->rindex >= c->before); - m->rindex = c->before; - - /* Free container */ - free(c->signature); - free(c->offsets); - m->n_containers--; - - /* Correct index of new top-level container */ - c = message_get_container(m); - c->index = c->saved_index; -} - -_public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) { - struct bus_container *c; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - - if (message_end_of_signature(m)) - goto eof; - - if (message_end_of_array(m, m->rindex)) - goto eof; - - c = message_get_container(m); - - if (bus_type_is_basic(c->signature[c->index])) { - if (contents) - *contents = NULL; - if (type) - *type = c->signature[c->index]; - return 1; - } - - if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) { - - if (contents) { - size_t l; - char *sig; - - r = signature_element_length(c->signature+c->index+1, &l); - if (r < 0) - return r; - - assert(l >= 1); - - sig = strndup(c->signature + c->index + 1, l); - if (!sig) - return -ENOMEM; - - free(c->peeked_signature); - *contents = c->peeked_signature = sig; - } - - if (type) - *type = SD_BUS_TYPE_ARRAY; - - return 1; - } - - if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN || - c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) { - - if (contents) { - size_t l; - char *sig; - - r = signature_element_length(c->signature+c->index, &l); - if (r < 0) - return r; - - assert(l >= 2); - sig = strndup(c->signature + c->index + 1, l - 2); - if (!sig) - return -ENOMEM; - - free(c->peeked_signature); - *contents = c->peeked_signature = sig; - } - - if (type) - *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY; - - return 1; - } - - if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) { - if (contents) { - void *q; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t k; - - if (c->item_size < 2) - return -EBADMSG; - - /* Look for the NUL delimiter that - separates the payload from the - signature. Since the body might be - in a different part that then the - signature we map byte by byte. */ - - for (k = 2; k <= c->item_size; k++) { - size_t where; - - where = m->rindex + c->item_size - k; - r = message_peek_body(m, &where, 1, k, &q); - if (r < 0) - return r; - - if (*(char*) q == 0) - break; - } - - if (k > c->item_size) - return -EBADMSG; - - free(c->peeked_signature); - c->peeked_signature = strndup((char*) q + 1, k - 1); - if (!c->peeked_signature) - return -ENOMEM; - - if (!signature_is_valid(c->peeked_signature, true)) - return -EBADMSG; - - *contents = c->peeked_signature; - } else { - size_t rindex, l; - - rindex = m->rindex; - r = message_peek_body(m, &rindex, 1, 1, &q); - if (r < 0) - return r; - - l = *(uint8_t*) q; - r = message_peek_body(m, &rindex, 1, l+1, &q); - if (r < 0) - return r; - - if (!validate_signature(q, l)) - return -EBADMSG; - - *contents = q; - } - } - - if (type) - *type = SD_BUS_TYPE_VARIANT; - - return 1; - } - - return -EINVAL; - -eof: - if (type) - *type = 0; - if (contents) - *contents = NULL; - return 0; -} - -_public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) { - struct bus_container *c; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - - if (complete) { - message_reset_containers(m); - m->rindex = 0; - - c = message_get_container(m); - } else { - c = message_get_container(m); - - c->offset_index = 0; - c->index = 0; - m->rindex = c->begin; - } - - c->offset_index = 0; - c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin; - - return !isempty(c->signature); -} - -static int message_read_ap( - sd_bus_message *m, - const char *types, - va_list ap) { - - unsigned n_array, n_struct; - TypeStack stack[BUS_CONTAINER_DEPTH]; - unsigned stack_ptr = 0; - unsigned n_loop = 0; - int r; - - assert(m); - - if (isempty(types)) - return 0; - - /* Ideally, we'd just call ourselves recursively on every - * complex type. However, the state of a va_list that is - * passed to a function is undefined after that function - * returns. This means we need to docode the va_list linearly - * in a single stackframe. We hence implement our own - * home-grown stack in an array. */ - - n_array = (unsigned) -1; /* length of current array entries */ - n_struct = strlen(types); /* length of current struct contents signature */ - - for (;;) { - const char *t; - - n_loop++; - - if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) { - r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - continue; - } - - t = types; - if (n_array != (unsigned) -1) - n_array--; - else { - types++; - n_struct--; - } - - switch (*t) { - - case SD_BUS_TYPE_BYTE: - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_UNIX_FD: { - void *p; - - p = va_arg(ap, void*); - r = sd_bus_message_read_basic(m, *t, p); - if (r < 0) - return r; - if (r == 0) { - if (n_loop <= 1) - return 0; - - return -ENXIO; - } - - break; - } - - case SD_BUS_TYPE_ARRAY: { - size_t k; - - r = signature_element_length(t + 1, &k); - if (r < 0) - return r; - - { - char s[k + 1]; - memcpy(s, t + 1, k); - s[k] = 0; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s); - if (r < 0) - return r; - if (r == 0) { - if (n_loop <= 1) - return 0; - - return -ENXIO; - } - } - - if (n_array == (unsigned) -1) { - types += k; - n_struct -= k; - } - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = t + 1; - n_struct = k; - n_array = va_arg(ap, unsigned); - - break; - } - - case SD_BUS_TYPE_VARIANT: { - const char *s; - - s = va_arg(ap, const char *); - if (!s) - return -EINVAL; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s); - if (r < 0) - return r; - if (r == 0) { - if (n_loop <= 1) - return 0; - - return -ENXIO; - } - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = s; - n_struct = strlen(s); - n_array = (unsigned) -1; - - break; - } - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - size_t k; - - r = signature_element_length(t, &k); - if (r < 0) - return r; - - { - char s[k - 1]; - memcpy(s, t + 1, k - 2); - s[k - 2] = 0; - - r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); - if (r < 0) - return r; - if (r == 0) { - if (n_loop <= 1) - return 0; - return -ENXIO; - } - } - - if (n_array == (unsigned) -1) { - types += k - 1; - n_struct -= k - 1; - } - - r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); - if (r < 0) - return r; - - types = t + 1; - n_struct = k - 2; - n_array = (unsigned) -1; - - break; - } - - default: - return -EINVAL; - } - } - - return 1; -} - -_public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) { - va_list ap; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(types, -EINVAL); - - va_start(ap, types); - r = message_read_ap(m, types, ap); - va_end(ap); - - return r; -} - -_public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) { - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - - /* If types is NULL, read exactly one element */ - if (!types) { - struct bus_container *c; - size_t l; - - if (message_end_of_signature(m)) - return -ENXIO; - - if (message_end_of_array(m, m->rindex)) - return 0; - - c = message_get_container(m); - - r = signature_element_length(c->signature + c->index, &l); - if (r < 0) - return r; - - types = strndupa(c->signature + c->index, l); - } - - switch (*types) { - - case 0: /* Nothing to drop */ - return 0; - - case SD_BUS_TYPE_BYTE: - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_UNIX_FD: - - r = sd_bus_message_read_basic(m, *types, NULL); - if (r <= 0) - return r; - - r = sd_bus_message_skip(m, types + 1); - if (r < 0) - return r; - - return 1; - - case SD_BUS_TYPE_ARRAY: { - size_t k; - - r = signature_element_length(types + 1, &k); - if (r < 0) - return r; - - { - char s[k+1]; - memcpy(s, types+1, k); - s[k] = 0; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s); - if (r <= 0) - return r; - - for (;;) { - r = sd_bus_message_skip(m, s); - if (r < 0) - return r; - if (r == 0) - break; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - - r = sd_bus_message_skip(m, types + 1 + k); - if (r < 0) - return r; - - return 1; - } - - case SD_BUS_TYPE_VARIANT: { - const char *contents; - char x; - - r = sd_bus_message_peek_type(m, &x, &contents); - if (r <= 0) - return r; - - if (x != SD_BUS_TYPE_VARIANT) - return -ENXIO; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); - if (r <= 0) - return r; - - r = sd_bus_message_skip(m, contents); - if (r < 0) - return r; - assert(r != 0); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_skip(m, types + 1); - if (r < 0) - return r; - - return 1; - } - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - size_t k; - - r = signature_element_length(types, &k); - if (r < 0) - return r; - - { - char s[k-1]; - memcpy(s, types+1, k-2); - s[k-2] = 0; - - r = sd_bus_message_enter_container(m, *types == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); - if (r <= 0) - return r; - - r = sd_bus_message_skip(m, s); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - - r = sd_bus_message_skip(m, types + k); - if (r < 0) - return r; - - return 1; - } - - default: - return -EINVAL; - } -} - -_public_ int sd_bus_message_read_array( - sd_bus_message *m, - char type, - const void **ptr, - size_t *size) { - - struct bus_container *c; - void *p; - size_t sz; - ssize_t align; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(bus_type_is_trivial(type), -EINVAL); - assert_return(ptr, -EINVAL); - assert_return(size, -EINVAL); - assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); - if (r <= 0) - return r; - - c = message_get_container(m); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); - if (align < 0) - return align; - - sz = c->end - c->begin; - } else { - align = bus_type_get_alignment(type); - if (align < 0) - return align; - - sz = BUS_MESSAGE_BSWAP32(m, *c->array_size); - } - - if (sz == 0) - /* Zero length array, let's return some aligned - * pointer that is not NULL */ - p = (uint8_t*) NULL + align; - else { - r = message_peek_body(m, &m->rindex, align, sz, &p); - if (r < 0) - goto fail; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - goto fail; - - *ptr = (const void*) p; - *size = sz; - - return 1; - -fail: - message_quit_container(m); - return r; -} - -static int message_peek_fields( - sd_bus_message *m, - size_t *rindex, - size_t align, - size_t nbytes, - void **ret) { - - assert(m); - assert(rindex); - assert(align > 0); - - return buffer_peek(BUS_MESSAGE_FIELDS(m), m->fields_size, rindex, align, nbytes, ret); -} - -static int message_peek_field_uint32( - sd_bus_message *m, - size_t *ri, - size_t item_size, - uint32_t *ret) { - - int r; - void *q; - - assert(m); - assert(ri); - - if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 4) - return -EBADMSG; - - /* identical for gvariant and dbus1 */ - - r = message_peek_fields(m, ri, 4, 4, &q); - if (r < 0) - return r; - - if (ret) - *ret = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); - - return 0; -} - -static int message_peek_field_uint64( - sd_bus_message *m, - size_t *ri, - size_t item_size, - uint64_t *ret) { - - int r; - void *q; - - assert(m); - assert(ri); - - if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 8) - return -EBADMSG; - - /* identical for gvariant and dbus1 */ - - r = message_peek_fields(m, ri, 8, 8, &q); - if (r < 0) - return r; - - if (ret) - *ret = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); - - return 0; -} - -static int message_peek_field_string( - sd_bus_message *m, - bool (*validate)(const char *p), - size_t *ri, - size_t item_size, - const char **ret) { - - uint32_t l; - int r; - void *q; - - assert(m); - assert(ri); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - - if (item_size <= 0) - return -EBADMSG; - - r = message_peek_fields(m, ri, 1, item_size, &q); - if (r < 0) - return r; - - l = item_size - 1; - } else { - r = message_peek_field_uint32(m, ri, 4, &l); - if (r < 0) - return r; - - r = message_peek_fields(m, ri, 1, l+1, &q); - if (r < 0) - return r; - } - - if (validate) { - if (!validate_nul(q, l)) - return -EBADMSG; - - if (!validate(q)) - return -EBADMSG; - } else { - if (!validate_string(q, l)) - return -EBADMSG; - } - - if (ret) - *ret = q; - - return 0; -} - -static int message_peek_field_signature( - sd_bus_message *m, - size_t *ri, - size_t item_size, - const char **ret) { - - size_t l; - int r; - void *q; - - assert(m); - assert(ri); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - - if (item_size <= 0) - return -EBADMSG; - - r = message_peek_fields(m, ri, 1, item_size, &q); - if (r < 0) - return r; - - l = item_size - 1; - } else { - r = message_peek_fields(m, ri, 1, 1, &q); - if (r < 0) - return r; - - l = *(uint8_t*) q; - r = message_peek_fields(m, ri, 1, l+1, &q); - if (r < 0) - return r; - } - - if (!validate_signature(q, l)) - return -EBADMSG; - - if (ret) - *ret = q; - - return 0; -} - -static int message_skip_fields( - sd_bus_message *m, - size_t *ri, - uint32_t array_size, - const char **signature) { - - size_t original_index; - int r; - - assert(m); - assert(ri); - assert(signature); - assert(!BUS_MESSAGE_IS_GVARIANT(m)); - - original_index = *ri; - - for (;;) { - char t; - size_t l; - - if (array_size != (uint32_t) -1 && - array_size <= *ri - original_index) - return 0; - - t = **signature; - if (!t) - return 0; - - if (t == SD_BUS_TYPE_STRING) { - - r = message_peek_field_string(m, NULL, ri, 0, NULL); - if (r < 0) - return r; - - (*signature)++; - - } else if (t == SD_BUS_TYPE_OBJECT_PATH) { - - r = message_peek_field_string(m, object_path_is_valid, ri, 0, NULL); - if (r < 0) - return r; - - (*signature)++; - - } else if (t == SD_BUS_TYPE_SIGNATURE) { - - r = message_peek_field_signature(m, ri, 0, NULL); - if (r < 0) - return r; - - (*signature)++; - - } else if (bus_type_is_basic(t)) { - ssize_t align, k; - - align = bus_type_get_alignment(t); - k = bus_type_get_size(t); - assert(align > 0 && k > 0); - - r = message_peek_fields(m, ri, align, k, NULL); - if (r < 0) - return r; - - (*signature)++; - - } else if (t == SD_BUS_TYPE_ARRAY) { - - r = signature_element_length(*signature+1, &l); - if (r < 0) - return r; - - assert(l >= 1); - { - char sig[l-1], *s; - uint32_t nas; - int alignment; - - strncpy(sig, *signature + 1, l-1); - s = sig; - - alignment = bus_type_get_alignment(sig[0]); - if (alignment < 0) - return alignment; - - r = message_peek_field_uint32(m, ri, 0, &nas); - if (r < 0) - return r; - if (nas > BUS_ARRAY_MAX_SIZE) - return -EBADMSG; - - r = message_peek_fields(m, ri, alignment, 0, NULL); - if (r < 0) - return r; - - r = message_skip_fields(m, ri, nas, (const char**) &s); - if (r < 0) - return r; - } - - (*signature) += 1 + l; - - } else if (t == SD_BUS_TYPE_VARIANT) { - const char *s; - - r = message_peek_field_signature(m, ri, 0, &s); - if (r < 0) - return r; - - r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s); - if (r < 0) - return r; - - (*signature)++; - - } else if (t == SD_BUS_TYPE_STRUCT || - t == SD_BUS_TYPE_DICT_ENTRY) { - - r = signature_element_length(*signature, &l); - if (r < 0) - return r; - - assert(l >= 2); - { - char sig[l-1], *s; - strncpy(sig, *signature + 1, l-1); - s = sig; - - r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s); - if (r < 0) - return r; - } - - *signature += l; - } else - return -EINVAL; - } -} - -int bus_message_parse_fields(sd_bus_message *m) { - size_t ri; - int r; - uint32_t unix_fds = 0; - bool unix_fds_set = false; - void *offsets = NULL; - unsigned n_offsets = 0; - size_t sz = 0; - unsigned i = 0; - - assert(m); - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - char *p; - - /* Read the signature from the end of the body variant first */ - sz = bus_gvariant_determine_word_size(BUS_MESSAGE_SIZE(m), 0); - if (m->footer_accessible < 1 + sz) - return -EBADMSG; - - p = (char*) m->footer + m->footer_accessible - (1 + sz); - for (;;) { - if (p < (char*) m->footer) - return -EBADMSG; - - if (*p == 0) { - size_t l; - char *c; - - /* We found the beginning of the signature - * string, yay! We require the body to be a - * structure, so verify it and then strip the - * opening/closing brackets. */ - - l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz); - if (l < 2 || - p[1] != SD_BUS_TYPE_STRUCT_BEGIN || - p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) - return -EBADMSG; - - c = strndup(p + 1 + 1, l - 2); - if (!c) - return -ENOMEM; - - free(m->root_container.signature); - m->root_container.signature = c; - break; - } - - p--; - } - - /* Calculate the actual user body size, by removing - * the trailing variant signature and struct offset - * table */ - m->user_body_size = m->body_size - ((char*) m->footer + m->footer_accessible - p); - - /* Pull out the offset table for the fields array */ - sz = bus_gvariant_determine_word_size(m->fields_size, 0); - if (sz > 0) { - size_t framing; - void *q; - - ri = m->fields_size - sz; - r = message_peek_fields(m, &ri, 1, sz, &q); - if (r < 0) - return r; - - framing = bus_gvariant_read_word_le(q, sz); - if (framing >= m->fields_size - sz) - return -EBADMSG; - if ((m->fields_size - framing) % sz != 0) - return -EBADMSG; - - ri = framing; - r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets); - if (r < 0) - return r; - - n_offsets = (m->fields_size - framing) / sz; - } - } else - m->user_body_size = m->body_size; - - ri = 0; - while (ri < m->fields_size) { - _cleanup_free_ char *sig = NULL; - const char *signature; - uint64_t field_type; - size_t item_size = (size_t) -1; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - uint64_t *u64; - - if (i >= n_offsets) - break; - - if (i == 0) - ri = 0; - else - ri = ALIGN_TO(bus_gvariant_read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8); - - r = message_peek_fields(m, &ri, 8, 8, (void**) &u64); - if (r < 0) - return r; - - field_type = BUS_MESSAGE_BSWAP64(m, *u64); - } else { - uint8_t *u8; - - r = message_peek_fields(m, &ri, 8, 1, (void**) &u8); - if (r < 0) - return r; - - field_type = *u8; - } - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - size_t where, end; - char *b; - void *q; - - end = bus_gvariant_read_word_le((uint8_t*) offsets + i*sz, sz); - - if (end < ri) - return -EBADMSG; - - where = ri = ALIGN_TO(ri, 8); - item_size = end - ri; - r = message_peek_fields(m, &where, 1, item_size, &q); - if (r < 0) - return r; - - b = memrchr(q, 0, item_size); - if (!b) - return -EBADMSG; - - sig = strndup(b+1, item_size - (b+1-(char*) q)); - if (!sig) - return -ENOMEM; - - signature = sig; - item_size = b - (char*) q; - } else { - r = message_peek_field_signature(m, &ri, 0, &signature); - if (r < 0) - return r; - } - - switch (field_type) { - - case _BUS_MESSAGE_HEADER_INVALID: - return -EBADMSG; - - case BUS_MESSAGE_HEADER_PATH: - - if (m->path) - return -EBADMSG; - - if (!streq(signature, "o")) - return -EBADMSG; - - r = message_peek_field_string(m, object_path_is_valid, &ri, item_size, &m->path); - break; - - case BUS_MESSAGE_HEADER_INTERFACE: - - if (m->interface) - return -EBADMSG; - - if (!streq(signature, "s")) - return -EBADMSG; - - r = message_peek_field_string(m, interface_name_is_valid, &ri, item_size, &m->interface); - break; - - case BUS_MESSAGE_HEADER_MEMBER: - - if (m->member) - return -EBADMSG; - - if (!streq(signature, "s")) - return -EBADMSG; - - r = message_peek_field_string(m, member_name_is_valid, &ri, item_size, &m->member); - break; - - case BUS_MESSAGE_HEADER_ERROR_NAME: - - if (m->error.name) - return -EBADMSG; - - if (!streq(signature, "s")) - return -EBADMSG; - - r = message_peek_field_string(m, error_name_is_valid, &ri, item_size, &m->error.name); - if (r >= 0) - m->error._need_free = -1; - - break; - - case BUS_MESSAGE_HEADER_DESTINATION: - - if (m->destination) - return -EBADMSG; - - if (!streq(signature, "s")) - return -EBADMSG; - - r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->destination); - break; - - case BUS_MESSAGE_HEADER_SENDER: - - if (m->sender) - return -EBADMSG; - - if (!streq(signature, "s")) - return -EBADMSG; - - r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->sender); - - if (r >= 0 && m->sender[0] == ':' && m->bus->bus_client && !m->bus->is_kernel) { - m->creds.unique_name = (char*) m->sender; - m->creds.mask |= SD_BUS_CREDS_UNIQUE_NAME & m->bus->creds_mask; - } - - break; - - - case BUS_MESSAGE_HEADER_SIGNATURE: { - const char *s; - char *c; - - if (BUS_MESSAGE_IS_GVARIANT(m)) /* only applies to dbus1 */ - return -EBADMSG; - - if (m->root_container.signature) - return -EBADMSG; - - if (!streq(signature, "g")) - return -EBADMSG; - - r = message_peek_field_signature(m, &ri, item_size, &s); - if (r < 0) - return r; - - c = strdup(s); - if (!c) - return -ENOMEM; - - free(m->root_container.signature); - m->root_container.signature = c; - break; - } - - case BUS_MESSAGE_HEADER_REPLY_SERIAL: - - if (m->reply_cookie != 0) - return -EBADMSG; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - /* 64bit on dbus2 */ - - if (!streq(signature, "t")) - return -EBADMSG; - - r = message_peek_field_uint64(m, &ri, item_size, &m->reply_cookie); - if (r < 0) - return r; - } else { - /* 32bit on dbus1 */ - uint32_t serial; - - if (!streq(signature, "u")) - return -EBADMSG; - - r = message_peek_field_uint32(m, &ri, item_size, &serial); - if (r < 0) - return r; - - m->reply_cookie = serial; - } - - if (m->reply_cookie == 0) - return -EBADMSG; - - break; - - case BUS_MESSAGE_HEADER_UNIX_FDS: - if (unix_fds_set) - return -EBADMSG; - - if (!streq(signature, "u")) - return -EBADMSG; - - r = message_peek_field_uint32(m, &ri, item_size, &unix_fds); - if (r < 0) - return -EBADMSG; - - unix_fds_set = true; - break; - - default: - if (!BUS_MESSAGE_IS_GVARIANT(m)) - r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature); - } - - if (r < 0) - return r; - - i++; - } - - if (m->n_fds != unix_fds) - return -EBADMSG; - - switch (m->header->type) { - - case SD_BUS_MESSAGE_SIGNAL: - if (!m->path || !m->interface || !m->member) - return -EBADMSG; - - if (m->reply_cookie != 0) - return -EBADMSG; - - break; - - case SD_BUS_MESSAGE_METHOD_CALL: - - if (!m->path || !m->member) - return -EBADMSG; - - if (m->reply_cookie != 0) - return -EBADMSG; - - break; - - case SD_BUS_MESSAGE_METHOD_RETURN: - - if (m->reply_cookie == 0) - return -EBADMSG; - break; - - case SD_BUS_MESSAGE_METHOD_ERROR: - - if (m->reply_cookie == 0 || !m->error.name) - return -EBADMSG; - break; - } - - /* Refuse non-local messages that claim they are local */ - if (streq_ptr(m->path, "/org/freedesktop/DBus/Local")) - return -EBADMSG; - if (streq_ptr(m->interface, "org.freedesktop.DBus.Local")) - return -EBADMSG; - if (streq_ptr(m->sender, "org.freedesktop.DBus.Local")) - return -EBADMSG; - - m->root_container.end = m->user_body_size; - - if (BUS_MESSAGE_IS_GVARIANT(m)) { - r = build_struct_offsets( - m, - m->root_container.signature, - m->user_body_size, - &m->root_container.item_size, - &m->root_container.offsets, - &m->root_container.n_offsets); - if (r < 0) - return r; - } - - /* Try to read the error message, but if we can't it's a non-issue */ - if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR) - (void) sd_bus_message_read(m, "s", &m->error.message); - - return 0; -} - -_public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) { - assert_return(m, -EINVAL); - assert_return(destination, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(!m->destination, -EEXIST); - - return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination); -} - -int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) { - size_t total; - void *p, *e; - unsigned i; - struct bus_body_part *part; - - assert(m); - assert(buffer); - assert(sz); - - total = BUS_MESSAGE_SIZE(m); - - p = malloc(total); - if (!p) - return -ENOMEM; - - e = mempcpy(p, m->header, BUS_MESSAGE_BODY_BEGIN(m)); - MESSAGE_FOREACH_PART(part, i, m) - e = mempcpy(e, part->data, part->size); - - assert(total == (size_t) ((uint8_t*) e - (uint8_t*) p)); - - *buffer = p; - *sz = total; - - return 0; -} - -int bus_message_read_strv_extend(sd_bus_message *m, char ***l) { - const char *s; - int r; - - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "s"); - if (r <= 0) - return r; - - while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) { - r = strv_extend(l, s); - if (r < 0) - return r; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) { - char **strv = NULL; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(l, -EINVAL); - - r = bus_message_read_strv_extend(m, &strv); - if (r <= 0) { - strv_free(strv); - return r; - } - - *l = strv; - return 1; -} - -static int bus_message_get_arg_skip( - sd_bus_message *m, - unsigned i, - char *_type, - const char **_contents) { - - unsigned j; - int r; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - for (j = 0;; j++) { - const char *contents; - char type; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - if (r == 0) - return -ENXIO; - - /* Don't match against arguments after the first one we don't understand */ - if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE) && - !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) - return -ENXIO; - - if (j >= i) { - if (_contents) - *_contents = contents; - if (_type) - *_type = type; - return 0; - } - - r = sd_bus_message_skip(m, NULL); - if (r < 0) - return r; - } - -} - -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str) { - char type; - int r; - - assert(m); - assert(str); - - r = bus_message_get_arg_skip(m, i, &type, NULL); - if (r < 0) - return r; - - if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) - return -ENXIO; - - return sd_bus_message_read_basic(m, type, str); -} - -int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv) { - const char *contents; - char type; - int r; - - assert(m); - assert(strv); - - r = bus_message_get_arg_skip(m, i, &type, &contents); - if (r < 0) - return r; - - if (type != SD_BUS_TYPE_ARRAY) - return -ENXIO; - if (!STR_IN_SET(contents, "s", "o", "g")) - return -ENXIO; - - return sd_bus_message_read_strv(m, strv); -} - -_public_ int sd_bus_message_get_errno(sd_bus_message *m) { - assert_return(m, EINVAL); - - if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) - return 0; - - return sd_bus_error_get_errno(&m->error); -} - -_public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complete) { - struct bus_container *c; - - assert_return(m, NULL); - - c = complete ? &m->root_container : message_get_container(m); - return strempty(c->signature); -} - -_public_ int sd_bus_message_is_empty(sd_bus_message *m) { - assert_return(m, -EINVAL); - - return isempty(m->root_container.signature); -} - -_public_ int sd_bus_message_has_signature(sd_bus_message *m, const char *signature) { - assert_return(m, -EINVAL); - - return streq(strempty(m->root_container.signature), strempty(signature)); -} - -_public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) { - bool done_something = false; - int r; - - assert_return(m, -EINVAL); - assert_return(source, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(source->sealed, -EPERM); - - do { - const char *contents; - char type; - union { - uint8_t u8; - uint16_t u16; - int16_t s16; - uint32_t u32; - int32_t s32; - uint64_t u64; - int64_t s64; - double d64; - const char *string; - int i; - } basic; - - r = sd_bus_message_peek_type(source, &type, &contents); - if (r < 0) - return r; - if (r == 0) - break; - - done_something = true; - - if (bus_type_is_container(type) > 0) { - - r = sd_bus_message_enter_container(source, type, contents); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, type, contents); - if (r < 0) - return r; - - r = sd_bus_message_copy(m, source, true); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(source); - if (r < 0) - return r; - - continue; - } - - r = sd_bus_message_read_basic(source, type, &basic); - if (r < 0) - return r; - - assert(r > 0); - - if (type == SD_BUS_TYPE_OBJECT_PATH || - type == SD_BUS_TYPE_SIGNATURE || - type == SD_BUS_TYPE_STRING) - r = sd_bus_message_append_basic(m, type, basic.string); - else - r = sd_bus_message_append_basic(m, type, &basic); - - if (r < 0) - return r; - - } while (all); - - return done_something; -} - -_public_ int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents) { - const char *c; - char t; - int r; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(!type || bus_type_is_valid(type), -EINVAL); - assert_return(!contents || signature_is_valid(contents, true), -EINVAL); - assert_return(type || contents, -EINVAL); - assert_return(!contents || !type || bus_type_is_container(type), -EINVAL); - - r = sd_bus_message_peek_type(m, &t, &c); - if (r <= 0) - return r; - - if (type != 0 && type != t) - return 0; - - if (contents && !streq_ptr(contents, c)) - return 0; - - return 1; -} - -_public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) { - assert_return(m, NULL); - - return m->bus; -} - -int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL; - usec_t timeout; - int r; - - assert(bus); - assert(m); - assert(*m); - - switch ((*m)->header->type) { - - case SD_BUS_MESSAGE_SIGNAL: - r = sd_bus_message_new_signal(bus, &n, (*m)->path, (*m)->interface, (*m)->member); - if (r < 0) - return r; - - break; - - case SD_BUS_MESSAGE_METHOD_CALL: - r = sd_bus_message_new_method_call(bus, &n, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member); - if (r < 0) - return r; - - break; - - case SD_BUS_MESSAGE_METHOD_RETURN: - case SD_BUS_MESSAGE_METHOD_ERROR: - - n = message_new(bus, (*m)->header->type); - if (!n) - return -ENOMEM; - - n->reply_cookie = (*m)->reply_cookie; - - r = message_append_reply_cookie(n, n->reply_cookie); - if (r < 0) - return r; - - if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) { - r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message); - if (r < 0) - return r; - - n->error._need_free = -1; - } - - break; - - default: - return -EINVAL; - } - - if ((*m)->destination && !n->destination) { - r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination); - if (r < 0) - return r; - } - - if ((*m)->sender && !n->sender) { - r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender); - if (r < 0) - return r; - } - - n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START); - - r = sd_bus_message_copy(n, *m, true); - if (r < 0) - return r; - - timeout = (*m)->timeout; - if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)) - timeout = BUS_DEFAULT_TIMEOUT; - - r = bus_message_seal(n, BUS_MESSAGE_COOKIE(*m), timeout); - if (r < 0) - return r; - - sd_bus_message_unref(*m); - *m = n; - n = NULL; - - return 0; -} - -int bus_message_append_sender(sd_bus_message *m, const char *sender) { - assert(m); - assert(sender); - - assert_return(!m->sealed, -EPERM); - assert_return(!m->sender, -EPERM); - - return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender); -} - -_public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) { - assert_return(m, -EINVAL); - assert_return(priority, -EINVAL); - - *priority = m->priority; - return 0; -} - -_public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - m->priority = priority; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h deleted file mode 100644 index 4710c106b9..0000000000 --- a/src/libsystemd/sd-bus/bus-message.h +++ /dev/null @@ -1,244 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-bus.h" - -#include "bus-creds.h" -#include "bus-protocol.h" -#include "macro.h" -#include "time-util.h" - -struct bus_container { - char enclosing; - bool need_offsets:1; - - /* Indexes into the signature string */ - unsigned index, saved_index; - char *signature; - - size_t before, begin, end; - - /* dbus1: pointer to the array size value, if this is a value */ - uint32_t *array_size; - - /* gvariant: list of offsets to end of children if this is struct/dict entry/array */ - size_t *offsets, n_offsets, offsets_allocated, offset_index; - size_t item_size; - - char *peeked_signature; -}; - -struct bus_body_part { - struct bus_body_part *next; - void *data; - void *mmap_begin; - size_t size; - size_t mapped; - size_t allocated; - uint64_t memfd_offset; - int memfd; - bool free_this:1; - bool munmap_this:1; - bool sealed:1; - bool is_zero:1; -}; - -struct sd_bus_message { - unsigned n_ref; - - sd_bus *bus; - - uint64_t reply_cookie; - - const char *path; - const char *interface; - const char *member; - const char *destination; - const char *sender; - - sd_bus_error error; - - sd_bus_creds creds; - - usec_t monotonic; - usec_t realtime; - uint64_t seqnum; - int64_t priority; - uint64_t verify_destination_id; - - bool sealed:1; - bool dont_send:1; - bool allow_fds:1; - bool free_header:1; - bool free_kdbus:1; - bool free_fds:1; - bool release_kdbus:1; - bool poisoned:1; - - /* The first and last bytes of the message */ - struct bus_header *header; - void *footer; - - /* How many bytes are accessible in the above pointers */ - size_t header_accessible; - size_t footer_accessible; - - size_t fields_size; - size_t body_size; - size_t user_body_size; - - struct bus_body_part body; - struct bus_body_part *body_end; - unsigned n_body_parts; - - size_t rindex; - struct bus_body_part *cached_rindex_part; - size_t cached_rindex_part_begin; - - uint32_t n_fds; - int *fds; - - struct bus_container root_container, *containers; - size_t n_containers; - size_t containers_allocated; - - struct iovec *iovec; - struct iovec iovec_fixed[2]; - unsigned n_iovec; - - struct kdbus_msg *kdbus; - - char *peeked_signature; - - /* If set replies to this message must carry the signature - * specified here to successfully seal. This is initialized - * from the vtable data */ - const char *enforced_reply_signature; - - usec_t timeout; - - char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char *destination_ptr; - - size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; - unsigned n_header_offsets; -}; - -static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) { - return m->header->endian != BUS_NATIVE_ENDIAN; -} - -static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) { - return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u; -} - -static inline uint32_t BUS_MESSAGE_BSWAP32(sd_bus_message *m, uint32_t u) { - return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_32(u) : u; -} - -static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) { - return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_64(u) : u; -} - -static inline uint64_t BUS_MESSAGE_COOKIE(sd_bus_message *m) { - if (m->header->version == 2) - return BUS_MESSAGE_BSWAP64(m, m->header->dbus2.cookie); - - return BUS_MESSAGE_BSWAP32(m, m->header->dbus1.serial); -} - -static inline size_t BUS_MESSAGE_SIZE(sd_bus_message *m) { - return - sizeof(struct bus_header) + - ALIGN8(m->fields_size) + - m->body_size; -} - -static inline size_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) { - return - sizeof(struct bus_header) + - ALIGN8(m->fields_size); -} - -static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) { - return (uint8_t*) m->header + sizeof(struct bus_header); -} - -static inline bool BUS_MESSAGE_IS_GVARIANT(sd_bus_message *m) { - return m->header->version == 2; -} - -int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout); -int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz); -int bus_message_read_strv_extend(sd_bus_message *m, char ***l); - -int bus_message_from_header( - sd_bus *bus, - void *header, - size_t header_accessible, - void *footer, - size_t footer_accessible, - size_t message_size, - int *fds, - unsigned n_fds, - const char *label, - size_t extra, - sd_bus_message **ret); - -int bus_message_from_malloc( - sd_bus *bus, - void *buffer, - size_t length, - int *fds, - unsigned n_fds, - const char *label, - sd_bus_message **ret); - -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); -int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); - -int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); - -int bus_message_parse_fields(sd_bus_message *m); - -struct bus_body_part *message_append_part(sd_bus_message *m); - -#define MESSAGE_FOREACH_PART(part, i, m) \ - for ((i) = 0, (part) = &(m)->body; (i) < (m)->n_body_parts; (i)++, (part) = (part)->next) - -int bus_body_part_map(struct bus_body_part *part); -void bus_body_part_unmap(struct bus_body_part *part); - -int bus_message_to_errno(sd_bus_message *m); - -int bus_message_new_synthetic_error(sd_bus *bus, uint64_t serial, const sd_bus_error *e, sd_bus_message **m); - -int bus_message_remarshal(sd_bus *bus, sd_bus_message **m); - -int bus_message_append_sender(sd_bus_message *m, const char *sender); - -void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m); -void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c deleted file mode 100644 index 9bd07ffcab..0000000000 --- a/src/libsystemd/sd-bus/bus-objects.c +++ /dev/null @@ -1,2806 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-introspect.h" -#include "bus-message.h" -#include "bus-objects.h" -#include "bus-signature.h" -#include "bus-slot.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, - const char *path, - struct node_vtable *c, - void **userdata, - sd_bus_error *error) { - - sd_bus_slot *s; - void *u; - int r; - - assert(bus); - assert(path); - assert(c); - - s = container_of(c, sd_bus_slot, node_vtable); - u = s->userdata; - if (c->find) { - bus->current_slot = sd_bus_slot_ref(s); - bus->current_userdata = u; - r = c->find(bus, path, c->interface, u, &u, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(s); - - if (r < 0) - return r; - if (sd_bus_error_is_set(error)) - return -sd_bus_error_get_errno(error); - if (r == 0) - return r; - } - - if (userdata) - *userdata = u; - - return 1; -} - -static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) { - assert(p); - - return (uint8_t*) u + p->x.method.offset; -} - -static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) { - assert(p); - - return (uint8_t*) u + p->x.property.offset; -} - -static int vtable_property_get_userdata( - sd_bus *bus, - const char *path, - struct vtable_member *p, - void **userdata, - sd_bus_error *error) { - - void *u; - int r; - - assert(bus); - assert(path); - assert(p); - assert(userdata); - - r = node_vtable_get_userdata(bus, path, p->parent, &u, error); - if (r <= 0) - return r; - if (bus->nodes_modified) - return 0; - - *userdata = vtable_property_convert_userdata(p->vtable, u); - return 1; -} - -static int add_enumerated_to_set( - sd_bus *bus, - const char *prefix, - struct node_enumerator *first, - Set *s, - sd_bus_error *error) { - - struct node_enumerator *c; - int r; - - assert(bus); - assert(prefix); - assert(s); - - LIST_FOREACH(enumerators, c, first) { - char **children = NULL, **k; - sd_bus_slot *slot; - - if (bus->nodes_modified) - return 0; - - slot = container_of(c, sd_bus_slot, node_enumerator); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = slot->userdata; - r = c->callback(bus, prefix, slot->userdata, &children, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - if (r < 0) - return r; - if (sd_bus_error_is_set(error)) - return -sd_bus_error_get_errno(error); - - STRV_FOREACH(k, children) { - if (r < 0) { - free(*k); - continue; - } - - if (!object_path_is_valid(*k)) { - free(*k); - r = -EINVAL; - continue; - } - - if (!object_path_startswith(*k, prefix)) { - free(*k); - continue; - } - - r = set_consume(s, *k); - if (r == -EEXIST) - r = 0; - } - - free(children); - if (r < 0) - return r; - } - - return 0; -} - -enum { - /* if set, add_subtree() works recursively */ - CHILDREN_RECURSIVE = (1U << 1), - /* if set, add_subtree() scans object-manager hierarchies recursively */ - CHILDREN_SUBHIERARCHIES = (1U << 0), -}; - -static int add_subtree_to_set( - sd_bus *bus, - const char *prefix, - struct node *n, - unsigned int flags, - Set *s, - sd_bus_error *error) { - - struct node *i; - int r; - - assert(bus); - assert(prefix); - assert(n); - assert(s); - - r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - LIST_FOREACH(siblings, i, n->child) { - char *t; - - if (!object_path_startswith(i->path, prefix)) - continue; - - t = strdup(i->path); - if (!t) - return -ENOMEM; - - r = set_consume(s, t); - if (r < 0 && r != -EEXIST) - return r; - - if ((flags & CHILDREN_RECURSIVE) && - ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) { - r = add_subtree_to_set(bus, prefix, i, flags, s, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - } - - return 0; -} - -static int get_child_nodes( - sd_bus *bus, - const char *prefix, - struct node *n, - unsigned int flags, - Set **_s, - sd_bus_error *error) { - - Set *s = NULL; - int r; - - assert(bus); - assert(prefix); - assert(n); - assert(_s); - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = add_subtree_to_set(bus, prefix, n, flags, s, error); - if (r < 0) { - set_free_free(s); - return r; - } - - *_s = s; - return 0; -} - -static int node_callbacks_run( - sd_bus *bus, - sd_bus_message *m, - struct node_callback *first, - bool require_fallback, - bool *found_object) { - - struct node_callback *c; - int r; - - assert(bus); - assert(m); - assert(found_object); - - LIST_FOREACH(callbacks, c, first) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - sd_bus_slot *slot; - - if (bus->nodes_modified) - return 0; - - if (require_fallback && !c->is_fallback) - continue; - - *found_object = true; - - if (c->last_iteration == bus->iteration_counter) - continue; - - c->last_iteration = bus->iteration_counter; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - slot = container_of(c, sd_bus_slot, node_callback); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - r = bus_maybe_reply_error(m, r, &error_buffer); - if (r != 0) - return r; - } - - return 0; -} - -#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF) - -static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) { - uint64_t cap; - int r; - - assert(bus); - assert(m); - assert(c); - - /* If the entire bus is trusted let's grant access */ - if (bus->trusted) - return 0; - - /* If the member is marked UNPRIVILEGED let's grant access */ - if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED) - return 0; - - /* Check have the caller has the requested capability - * set. Note that the flags value contains the capability - * number plus one, which we need to subtract here. We do this - * so that we have 0 as special value for "default - * capability". */ - cap = CAPABILITY_SHIFT(c->vtable->flags); - if (cap == 0) - cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags); - if (cap == 0) - cap = CAP_SYS_ADMIN; - else - cap--; - - r = sd_bus_query_sender_privilege(m, cap); - if (r < 0) - return r; - if (r > 0) - return 0; - - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member); -} - -static int method_callbacks_run( - sd_bus *bus, - sd_bus_message *m, - struct vtable_member *c, - bool require_fallback, - bool *found_object) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *signature; - void *u; - int r; - - assert(bus); - assert(m); - assert(c); - assert(found_object); - - if (require_fallback && !c->parent->is_fallback) - return 0; - - r = check_access(bus, m, c, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - - r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error); - if (r <= 0) - return bus_maybe_reply_error(m, r, &error); - if (bus->nodes_modified) - return 0; - - u = vtable_method_convert_userdata(c->vtable, u); - - *found_object = true; - - if (c->last_iteration == bus->iteration_counter) - return 0; - - c->last_iteration = bus->iteration_counter; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - signature = sd_bus_message_get_signature(m, true); - if (!signature) - return -EINVAL; - - if (!streq(strempty(c->vtable->x.method.signature), signature)) - return sd_bus_reply_method_errorf( - m, - SD_BUS_ERROR_INVALID_ARGS, - "Invalid arguments '%s' to call %s.%s(), expecting '%s'.", - signature, c->interface, c->member, strempty(c->vtable->x.method.signature)); - - /* Keep track what the signature of the reply to this message - * should be, so that this can be enforced when sealing the - * reply. */ - m->enforced_reply_signature = strempty(c->vtable->x.method.result); - - if (c->vtable->x.method.handler) { - sd_bus_slot *slot; - - slot = container_of(c->parent, sd_bus_slot, node_vtable); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->vtable->x.method.handler; - bus->current_userdata = u; - r = c->vtable->x.method.handler(m, u, &error); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error); - } - - /* If the method callback is NULL, make this a successful NOP */ - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) - return r; - - return 1; -} - -static int invoke_property_get( - sd_bus *bus, - sd_bus_slot *slot, - const sd_bus_vtable *v, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - const void *p; - int r; - - assert(bus); - assert(slot); - assert(v); - assert(path); - assert(interface); - assert(property); - assert(reply); - - if (v->x.property.get) { - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = userdata; - r = v->x.property.get(bus, path, interface, property, reply, userdata, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - if (r < 0) - return r; - if (sd_bus_error_is_set(error)) - return -sd_bus_error_get_errno(error); - return r; - } - - /* Automatic handling if no callback is defined. */ - - if (streq(v->x.property.signature, "as")) - return sd_bus_message_append_strv(reply, *(char***) userdata); - - assert(signature_is_single(v->x.property.signature, false)); - assert(bus_type_is_basic(v->x.property.signature[0])); - - switch (v->x.property.signature[0]) { - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_SIGNATURE: - p = strempty(*(char**) userdata); - break; - - case SD_BUS_TYPE_OBJECT_PATH: - p = *(char**) userdata; - assert(p); - break; - - default: - p = userdata; - break; - } - - return sd_bus_message_append_basic(reply, v->x.property.signature[0], p); -} - -static int invoke_property_set( - sd_bus *bus, - sd_bus_slot *slot, - const sd_bus_vtable *v, - const char *path, - const char *interface, - const char *property, - sd_bus_message *value, - void *userdata, - sd_bus_error *error) { - - int r; - - assert(bus); - assert(slot); - assert(v); - assert(path); - assert(interface); - assert(property); - assert(value); - - if (v->x.property.set) { - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_userdata = userdata; - r = v->x.property.set(bus, path, interface, property, value, userdata, error); - bus->current_userdata = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - if (r < 0) - return r; - if (sd_bus_error_is_set(error)) - return -sd_bus_error_get_errno(error); - return r; - } - - /* Automatic handling if no callback is defined. */ - - assert(signature_is_single(v->x.property.signature, false)); - assert(bus_type_is_basic(v->x.property.signature[0])); - - switch (v->x.property.signature[0]) { - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: { - const char *p; - char *n; - - r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p); - if (r < 0) - return r; - - n = strdup(p); - if (!n) - return -ENOMEM; - - free(*(char**) userdata); - *(char**) userdata = n; - - break; - } - - default: - r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata); - if (r < 0) - return r; - - break; - } - - return 1; -} - -static int property_get_set_callbacks_run( - sd_bus *bus, - sd_bus_message *m, - struct vtable_member *c, - bool require_fallback, - bool is_get, - bool *found_object) { - - _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; - - assert(bus); - assert(m); - assert(c); - assert(found_object); - - if (require_fallback && !c->parent->is_fallback) - return 0; - - r = vtable_property_get_userdata(bus, m->path, c, &u, &error); - if (r <= 0) - return bus_maybe_reply_error(m, r, &error); - if (bus->nodes_modified) - return 0; - - slot = container_of(c->parent, sd_bus_slot, node_vtable); - - *found_object = true; - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - - if (is_get) { - /* Note that we do not protect against reexecution - * here (using the last_iteration check, see below), - * should the node tree have changed and we got called - * again. We assume that property Get() calls are - * ultimately without side-effects or if they aren't - * then at least idempotent. */ - - r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature); - if (r < 0) - return r; - - /* Note that we do not do an access check here. Read - * access to properties is always unrestricted, since - * PropertiesChanged signals broadcast contents - * anyway. */ - - r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - - if (bus->nodes_modified) - return 0; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - } else { - const char *signature = NULL; - char type = 0; - - if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member); - - /* Avoid that we call the set routine more than once - * if the processing of this message got restarted - * because the node tree changed. */ - if (c->last_iteration == bus->iteration_counter) - return 0; - - c->last_iteration = bus->iteration_counter; - - r = sd_bus_message_peek_type(m, &type, &signature); - if (r < 0) - return r; - - if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature))) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature)); - - r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature); - if (r < 0) - return r; - - r = check_access(bus, m, c, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - - r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - - if (bus->nodes_modified) - return 0; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - - r = sd_bus_send(bus, reply, NULL); - if (r < 0) - return r; - - return 1; -} - -static int vtable_append_one_property( - sd_bus *bus, - sd_bus_message *reply, - const char *path, - struct node_vtable *c, - const sd_bus_vtable *v, - void *userdata, - sd_bus_error *error) { - - sd_bus_slot *slot; - int r; - - assert(bus); - assert(reply); - assert(path); - assert(c); - assert(v); - - r = sd_bus_message_open_container(reply, 'e', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "s", v->x.property.member); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'v', v->x.property.signature); - if (r < 0) - return r; - - slot = container_of(c, sd_bus_slot, node_vtable); - - r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return 0; -} - -static int vtable_append_all_properties( - sd_bus *bus, - sd_bus_message *reply, - const char *path, - struct node_vtable *c, - void *userdata, - sd_bus_error *error) { - - const sd_bus_vtable *v; - int r; - - assert(bus); - assert(reply); - assert(path); - assert(c); - - if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) - return 1; - - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { - if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) - continue; - - if (v->flags & SD_BUS_VTABLE_HIDDEN) - continue; - - if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) - continue; - - r = vtable_append_one_property(bus, reply, path, c, v, userdata, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 1; -} - -static int property_get_all_callbacks_run( - sd_bus *bus, - sd_bus_message *m, - struct node_vtable *first, - bool require_fallback, - const char *iface, - bool *found_object) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - struct node_vtable *c; - bool found_interface; - int r; - - assert(bus); - assert(m); - assert(found_object); - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (r < 0) - return r; - - found_interface = !iface || - streq(iface, "org.freedesktop.DBus.Properties") || - streq(iface, "org.freedesktop.DBus.Peer") || - streq(iface, "org.freedesktop.DBus.Introspectable"); - - LIST_FOREACH(vtables, c, first) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - void *u; - - if (require_fallback && !c->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, m->path, c, &u, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - *found_object = true; - - if (iface && !streq(c->interface, iface)) - continue; - found_interface = true; - - r = vtable_append_all_properties(bus, reply, m->path, c, u, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - if (bus->nodes_modified) - return 0; - } - - if (!found_interface) { - r = sd_bus_reply_method_errorf( - m, - SD_BUS_ERROR_UNKNOWN_INTERFACE, - "Unknown interface '%s'.", iface); - if (r < 0) - return r; - - return 1; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_send(bus, reply, NULL); - if (r < 0) - return r; - - return 1; -} - -static int bus_node_exists( - sd_bus *bus, - struct node *n, - const char *path, - bool require_fallback) { - - struct node_vtable *c; - struct node_callback *k; - int r; - - assert(bus); - assert(n); - assert(path); - - /* Tests if there's anything attached directly to this node - * for the specified path */ - - if (!require_fallback && (n->enumerators || n->object_managers)) - return true; - - LIST_FOREACH(callbacks, k, n->callbacks) { - if (require_fallback && !k->is_fallback) - continue; - - return 1; - } - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (require_fallback && !c->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, path, c, NULL, &error); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -static int process_introspect( - sd_bus *bus, - sd_bus_message *m, - struct node *n, - bool require_fallback, - bool *found_object) { - - _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; - struct node_vtable *c; - bool empty; - int r; - - assert(bus); - assert(m); - assert(n); - assert(found_object); - - r = get_child_nodes(bus, m->path, n, 0, &s, &error); - if (r < 0) - return bus_maybe_reply_error(m, r, &error); - if (bus->nodes_modified) - return 0; - - r = introspect_begin(&intro, bus->trusted); - if (r < 0) - return r; - - r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers); - if (r < 0) - return r; - - empty = set_isempty(s); - - LIST_FOREACH(vtables, c, n->vtables) { - if (require_fallback && !c->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, m->path, c, NULL, &error); - if (r < 0) { - r = bus_maybe_reply_error(m, r, &error); - goto finish; - } - if (bus->nodes_modified) { - r = 0; - goto finish; - } - if (r == 0) - continue; - - empty = false; - - if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) - continue; - - if (!streq_ptr(previous_interface, c->interface)) { - - if (previous_interface) - fputs("
\n", intro.f); - - fprintf(intro.f, " \n", c->interface); - } - - r = introspect_write_interface(&intro, c->vtable); - if (r < 0) - goto finish; - - previous_interface = c->interface; - } - - if (previous_interface) - fputs(" \n", intro.f); - - if (empty) { - /* Nothing?, let's see if we exist at all, and if not - * refuse to do anything */ - r = bus_node_exists(bus, n, m->path, require_fallback); - if (r <= 0) - goto finish; - if (bus->nodes_modified) { - r = 0; - goto finish; - } - } - - *found_object = true; - - r = introspect_write_child_nodes(&intro, s, m->path); - if (r < 0) - goto finish; - - r = introspect_finish(&intro, bus, m, &reply); - if (r < 0) - goto finish; - - r = sd_bus_send(bus, reply, NULL); - if (r < 0) - goto finish; - - r = 1; - -finish: - introspect_free(&intro); - return r; -} - -static int object_manager_serialize_path( - sd_bus *bus, - sd_bus_message *reply, - const char *prefix, - const char *path, - bool require_fallback, - sd_bus_error *error) { - - const char *previous_interface = NULL; - bool found_something = false; - struct node_vtable *i; - struct node *n; - int r; - - assert(bus); - assert(reply); - assert(prefix); - assert(path); - assert(error); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, i, n->vtables) { - void *u; - - if (require_fallback && !i->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, path, i, &u, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - if (!found_something) { - - /* Open the object part */ - - r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "o", path); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}"); - if (r < 0) - return r; - - found_something = true; - } - - if (!streq_ptr(previous_interface, i->interface)) { - - /* Maybe close the previous interface part */ - - if (previous_interface) { - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - } - - /* Open the new interface part */ - - r = sd_bus_message_open_container(reply, 'e', "sa{sv}"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "s", i->interface); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (r < 0) - return r; - } - - r = vtable_append_all_properties(bus, reply, path, i, u, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - previous_interface = i->interface; - } - - if (previous_interface) { - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - } - - if (found_something) { - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - } - - return 1; -} - -static int object_manager_serialize_path_and_fallbacks( - sd_bus *bus, - sd_bus_message *reply, - const char *path, - sd_bus_error *error) { - - char *prefix; - int r; - - assert(bus); - assert(reply); - assert(path); - assert(error); - - /* First, add all vtables registered for this path */ - r = object_manager_serialize_path(bus, reply, path, path, false, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - /* Second, add fallback vtables registered for any of the prefixes */ - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_manager_serialize_path(bus, reply, prefix, path, true, error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -static int process_get_managed_objects( - sd_bus *bus, - sd_bus_message *m, - struct node *n, - bool require_fallback, - bool *found_object) { - - _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; - int r; - - assert(bus); - assert(m); - assert(n); - assert(found_object); - - /* Spec says, GetManagedObjects() is only implemented on the root of a - * sub-tree. Therefore, we require a registered object-manager on - * exactly the queried path, otherwise, we refuse to respond. */ - - if (require_fallback || !n->object_managers) - return 0; - - r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}"); - if (r < 0) - return r; - - SET_FOREACH(path, s, i) { - r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error); - if (r < 0) - return r; - - if (bus->nodes_modified) - return 0; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_send(bus, reply, NULL); - if (r < 0) - return r; - - return 1; -} - -static int object_find_and_run( - sd_bus *bus, - sd_bus_message *m, - const char *p, - bool require_fallback, - bool *found_object) { - - struct node *n; - struct vtable_member vtable_key, *v; - int r; - - assert(bus); - assert(m); - assert(p); - assert(found_object); - - n = hashmap_get(bus->nodes, p); - if (!n) - return 0; - - /* First, try object callbacks */ - r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - - if (!m->interface || !m->member) - return 0; - - /* Then, look for a known method */ - vtable_key.path = (char*) p; - vtable_key.interface = m->interface; - vtable_key.member = m->member; - - v = hashmap_get(bus->vtable_methods, &vtable_key); - if (v) { - r = method_callbacks_run(bus, m, v, require_fallback, found_object); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - } - - /* Then, look for a known property */ - if (streq(m->interface, "org.freedesktop.DBus.Properties")) { - bool get = false; - - get = streq(m->member, "Get"); - - if (get || streq(m->member, "Set")) { - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - vtable_key.path = (char*) p; - - r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member); - if (r < 0) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters"); - - v = hashmap_get(bus->vtable_properties, &vtable_key); - if (v) { - r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object); - if (r != 0) - return r; - } - - } else if (streq(m->member, "GetAll")) { - const char *iface; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - r = sd_bus_message_read(m, "s", &iface); - if (r < 0) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter"); - - if (iface[0] == 0) - iface = NULL; - - r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object); - if (r != 0) - return r; - } - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { - - if (!isempty(sd_bus_message_get_signature(m, true))) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters"); - - r = process_introspect(bus, m, n, require_fallback, found_object); - if (r != 0) - return r; - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) { - - if (!isempty(sd_bus_message_get_signature(m, true))) - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters"); - - r = process_get_managed_objects(bus, m, n, require_fallback, found_object); - if (r != 0) - return r; - } - - if (bus->nodes_modified) - return 0; - - if (!*found_object) { - r = bus_node_exists(bus, n, m->path, require_fallback); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r > 0) - *found_object = true; - } - - return 0; -} - -int bus_process_object(sd_bus *bus, sd_bus_message *m) { - int r; - size_t pl; - bool found_object = false; - - assert(bus); - assert(m); - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) - return 0; - - if (hashmap_isempty(bus->nodes)) - return 0; - - /* Never respond to broadcast messages */ - if (bus->bus_client && !m->destination) - return 0; - - assert(m->path); - assert(m->member); - - pl = strlen(m->path); - do { - char prefix[pl+1]; - - bus->nodes_modified = false; - - r = object_find_and_run(bus, m, m->path, false, &found_object); - if (r != 0) - return r; - - /* Look for fallback prefixes */ - OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) { - - if (bus->nodes_modified) - break; - - r = object_find_and_run(bus, m, prefix, true, &found_object); - if (r != 0) - return r; - } - - } while (bus->nodes_modified); - - if (!found_object) - return 0; - - if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") || - sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) - r = sd_bus_reply_method_errorf( - m, - SD_BUS_ERROR_UNKNOWN_PROPERTY, - "Unknown property or interface."); - else - r = sd_bus_reply_method_errorf( - m, - SD_BUS_ERROR_UNKNOWN_METHOD, - "Unknown method '%s' or interface '%s'.", m->member, m->interface); - - if (r < 0) - return r; - - return 1; -} - -static struct node *bus_node_allocate(sd_bus *bus, const char *path) { - struct node *n, *parent; - const char *e; - _cleanup_free_ char *s = NULL; - char *p; - int r; - - assert(bus); - assert(path); - assert(path[0] == '/'); - - n = hashmap_get(bus->nodes, path); - if (n) - return n; - - r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops); - if (r < 0) - return NULL; - - s = strdup(path); - if (!s) - return NULL; - - if (streq(path, "/")) - parent = NULL; - else { - e = strrchr(path, '/'); - assert(e); - - p = strndupa(path, MAX(1, e - path)); - - parent = bus_node_allocate(bus, p); - if (!parent) - return NULL; - } - - n = new0(struct node, 1); - if (!n) - return NULL; - - n->parent = parent; - n->path = s; - s = NULL; /* do not free */ - - r = hashmap_put(bus->nodes, n->path, n); - if (r < 0) { - free(n->path); - free(n); - return NULL; - } - - if (parent) - LIST_PREPEND(siblings, parent->child, n); - - return n; -} - -void bus_node_gc(sd_bus *b, struct node *n) { - assert(b); - - if (!n) - return; - - if (n->child || - n->callbacks || - n->vtables || - n->enumerators || - n->object_managers) - return; - - assert(hashmap_remove(b->nodes, n->path) == n); - - if (n->parent) - LIST_REMOVE(siblings, n->parent->child, n); - - free(n->path); - bus_node_gc(b, n->parent); - free(n); -} - -static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { - struct node *n; - - assert(bus); - assert(path); - - n = hashmap_get(bus->nodes, path); - if (!n) { - char *prefix; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - n = hashmap_get(bus->nodes, prefix); - if (n) - break; - } - } - - while (n && !n->object_managers) - n = n->parent; - - if (out) - *out = n; - return !!n; -} - -static int bus_add_object( - sd_bus *bus, - sd_bus_slot **slot, - bool fallback, - const char *path, - sd_bus_message_handler_t callback, - void *userdata) { - - sd_bus_slot *s; - struct node *n; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - n = bus_node_allocate(bus, path); - if (!n) - return -ENOMEM; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_callback.callback = callback; - s->node_callback.is_fallback = fallback; - - s->node_callback.node = n; - LIST_PREPEND(callbacks, n->callbacks, &s->node_callback); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -_public_ int sd_bus_add_object( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - sd_bus_message_handler_t callback, - void *userdata) { - - return bus_add_object(bus, slot, false, path, callback, userdata); -} - -_public_ int sd_bus_add_fallback( - sd_bus *bus, - sd_bus_slot **slot, - const char *prefix, - sd_bus_message_handler_t callback, - void *userdata) { - - return bus_add_object(bus, slot, true, prefix, callback, userdata); -} - -static void vtable_member_hash_func(const void *a, struct siphash *state) { - const struct vtable_member *m = a; - - assert(m); - - 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) { - const struct vtable_member *x = a, *y = b; - int r; - - assert(x); - assert(y); - - r = strcmp(x->path, y->path); - if (r != 0) - return r; - - r = strcmp(x->interface, y->interface); - if (r != 0) - return r; - - return strcmp(x->member, y->member); -} - -static const struct hash_ops vtable_member_hash_ops = { - .hash = vtable_member_hash_func, - .compare = vtable_member_compare_func -}; - -static int add_object_vtable_internal( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - const char *interface, - const sd_bus_vtable *vtable, - bool fallback, - sd_bus_object_find_t find, - void *userdata) { - - sd_bus_slot *s = NULL; - struct node_vtable *i, *existing = NULL; - const sd_bus_vtable *v; - struct node *n; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(interface_name_is_valid(interface), -EINVAL); - assert_return(vtable, -EINVAL); - assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL); - assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!streq(interface, "org.freedesktop.DBus.Properties") && - !streq(interface, "org.freedesktop.DBus.Introspectable") && - !streq(interface, "org.freedesktop.DBus.Peer") && - !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL); - - r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops); - if (r < 0) - return r; - - n = bus_node_allocate(bus, path); - if (!n) - return -ENOMEM; - - LIST_FOREACH(vtables, i, n->vtables) { - if (i->is_fallback != fallback) { - r = -EPROTOTYPE; - goto fail; - } - - if (streq(i->interface, interface)) { - - if (i->vtable == vtable) { - r = -EEXIST; - goto fail; - } - - existing = i; - } - } - - s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_vtable.is_fallback = fallback; - s->node_vtable.vtable = vtable; - s->node_vtable.find = find; - - s->node_vtable.interface = strdup(interface); - if (!s->node_vtable.interface) { - r = -ENOMEM; - goto fail; - } - - for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { - - switch (v->type) { - - case _SD_BUS_VTABLE_METHOD: { - struct vtable_member *m; - - if (!member_name_is_valid(v->x.method.member) || - !signature_is_valid(strempty(v->x.method.signature), false) || - !signature_is_valid(strempty(v->x.method.result), false) || - !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) || - v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) { - r = -EINVAL; - goto fail; - } - - m = new0(struct vtable_member, 1); - if (!m) { - r = -ENOMEM; - goto fail; - } - - m->parent = &s->node_vtable; - m->path = n->path; - m->interface = s->node_vtable.interface; - m->member = v->x.method.member; - m->vtable = v; - - r = hashmap_put(bus->vtable_methods, m, m); - if (r < 0) { - free(m); - goto fail; - } - - break; - } - - case _SD_BUS_VTABLE_WRITABLE_PROPERTY: - - if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) { - r = -EINVAL; - goto fail; - } - - if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) { - r = -EINVAL; - goto fail; - } - - /* Fall through */ - - case _SD_BUS_VTABLE_PROPERTY: { - struct vtable_member *m; - - if (!member_name_is_valid(v->x.property.member) || - !signature_is_single(v->x.property.signature, false) || - !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) || - (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) || - (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 || - ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) || - (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) { - r = -EINVAL; - goto fail; - } - - m = new0(struct vtable_member, 1); - if (!m) { - r = -ENOMEM; - goto fail; - } - - m->parent = &s->node_vtable; - m->path = n->path; - m->interface = s->node_vtable.interface; - m->member = v->x.property.member; - m->vtable = v; - - r = hashmap_put(bus->vtable_properties, m, m); - if (r < 0) { - free(m); - goto fail; - } - - break; - } - - case _SD_BUS_VTABLE_SIGNAL: - - if (!member_name_is_valid(v->x.signal.member) || - !signature_is_valid(strempty(v->x.signal.signature), false) || - v->flags & SD_BUS_VTABLE_UNPRIVILEGED) { - r = -EINVAL; - goto fail; - } - - break; - - default: - r = -EINVAL; - goto fail; - } - } - - s->node_vtable.node = n; - LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -_public_ int sd_bus_add_object_vtable( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - const char *interface, - const sd_bus_vtable *vtable, - void *userdata) { - - return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata); -} - -_public_ int sd_bus_add_fallback_vtable( - sd_bus *bus, - sd_bus_slot **slot, - const char *prefix, - const char *interface, - const sd_bus_vtable *vtable, - sd_bus_object_find_t find, - void *userdata) { - - return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata); -} - -_public_ int sd_bus_add_node_enumerator( - sd_bus *bus, - sd_bus_slot **slot, - const char *path, - sd_bus_node_enumerator_t callback, - void *userdata) { - - sd_bus_slot *s; - struct node *n; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - n = bus_node_allocate(bus, path); - if (!n) - return -ENOMEM; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_enumerator.callback = callback; - - s->node_enumerator.node = n; - LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} - -static int emit_properties_changed_on_interface( - sd_bus *bus, - const char *prefix, - const char *path, - const char *interface, - bool require_fallback, - bool *found_interface, - char **names) { - - _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; - struct node *n; - char **property; - void *u = NULL; - int r; - - assert(bus); - assert(prefix); - assert(path); - assert(interface); - assert(found_interface); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", interface); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "{sv}"); - if (r < 0) - return r; - - key.path = prefix; - key.interface = interface; - - LIST_FOREACH(vtables, c, n->vtables) { - if (require_fallback && !c->is_fallback) - continue; - - if (!streq(c->interface, interface)) - continue; - - r = node_vtable_get_userdata(bus, path, c, &u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - *found_interface = true; - - if (names) { - /* If the caller specified a list of - * properties we include exactly those in the - * PropertiesChanged message */ - - STRV_FOREACH(property, names) { - struct vtable_member *v; - - assert_return(member_name_is_valid(*property), -EINVAL); - - key.member = *property; - v = hashmap_get(bus->vtable_properties, &key); - if (!v) - return -ENOENT; - - /* If there are two vtables for the same - * interface, let's handle this property when - * we come to that vtable. */ - if (c != v->parent) - continue; - - assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE || - v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM); - - assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM); - - if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { - has_invalidating = true; - continue; - } - - has_changing = true; - - r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - } else { - const sd_bus_vtable *v; - - /* If the caller specified no properties list - * we include all properties that are marked - * as changing in the message. */ - - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { - if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) - continue; - - if (v->flags & SD_BUS_VTABLE_HIDDEN) - continue; - - if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { - has_invalidating = true; - continue; - } - - if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) - continue; - - has_changing = true; - - r = vtable_append_one_property(bus, m, m->path, c, v, u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - } - } - - if (!has_invalidating && !has_changing) - return 0; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return r; - - if (has_invalidating) { - LIST_FOREACH(vtables, c, n->vtables) { - if (require_fallback && !c->is_fallback) - continue; - - if (!streq(c->interface, interface)) - continue; - - r = node_vtable_get_userdata(bus, path, c, &u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - if (names) { - STRV_FOREACH(property, names) { - struct vtable_member *v; - - key.member = *property; - assert_se(v = hashmap_get(bus->vtable_properties, &key)); - assert(c == v->parent); - - if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) - continue; - - r = sd_bus_message_append(m, "s", *property); - if (r < 0) - return r; - } - } else { - const sd_bus_vtable *v; - - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { - if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) - continue; - - if (v->flags & SD_BUS_VTABLE_HIDDEN) - continue; - - if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) - continue; - - r = sd_bus_message_append(m, "s", v->x.property.member); - if (r < 0) - return r; - } - } - } - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_send(bus, m, NULL); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_bus_emit_properties_changed_strv( - sd_bus *bus, - const char *path, - const char *interface, - char **names) { - - BUS_DONT_DESTROY(bus); - bool found_interface = false; - char *prefix; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(interface_name_is_valid(interface), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - /* A non-NULL but empty names list means nothing needs to be - generated. A NULL list OTOH indicates that all properties - that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be - included in the PropertiesChanged message. */ - if (names && names[0] == NULL) - return 0; - - do { - bus->nodes_modified = false; - - r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names); - if (r != 0) - return r; - if (bus->nodes_modified) - continue; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names); - if (r != 0) - return r; - if (bus->nodes_modified) - break; - } - - } while (bus->nodes_modified); - - return found_interface ? 0 : -ENOENT; -} - -_public_ int sd_bus_emit_properties_changed( - sd_bus *bus, - const char *path, - const char *interface, - const char *name, ...) { - - char **names; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(interface_name_is_valid(interface), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (!name) - return 0; - - names = strv_from_stdarg_alloca(name); - - return sd_bus_emit_properties_changed_strv(bus, path, interface, names); -} - -static int object_added_append_all_prefix( - sd_bus *bus, - sd_bus_message *m, - Set *s, - const char *prefix, - const char *path, - bool require_fallback) { - - const char *previous_interface = NULL; - struct node_vtable *c; - struct node *n; - int r; - - assert(bus); - assert(m); - assert(s); - assert(prefix); - assert(path); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - void *u = NULL; - - if (require_fallback && !c->is_fallback) - continue; - - r = node_vtable_get_userdata(bus, path, c, &u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - if (!streq_ptr(c->interface, previous_interface)) { - /* If a child-node already handled this interface, we - * skip it on any of its parents. The child vtables - * always fully override any conflicting vtables of - * any parent node. */ - if (set_get(s, c->interface)) - continue; - - r = set_put(s, c->interface); - if (r < 0) - return r; - - if (previous_interface) { - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - r = sd_bus_message_open_container(m, 'e', "sa{sv}"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", c->interface); - if (r < 0) - return r; - r = sd_bus_message_open_container(m, 'a', "{sv}"); - if (r < 0) - return r; - - previous_interface = c->interface; - } - - r = vtable_append_all_properties(bus, m, path, c, u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - if (previous_interface) { - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - return 0; -} - -static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; - char *prefix; - int r; - - assert(bus); - assert(m); - assert(path); - - /* - * This appends all interfaces registered on path @path. We first add - * the builtin interfaces, which are always available and handled by - * sd-bus. Then, we add all interfaces registered on the exact node, - * followed by all fallback interfaces registered on any parent prefix. - * - * If an interface is registered multiple times on the same node with - * different vtables, we merge all the properties across all vtables. - * However, if a child node has the same interface registered as one of - * its parent nodes has as fallback, we make the child overwrite the - * parent instead of extending it. Therefore, we keep a "Set" of all - * handled interfaces during parent traversal, so we skip interfaces on - * a parent that were overwritten by a child. - */ - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); - if (r < 0) - return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); - if (r < 0) - return r; - - r = object_added_append_all_prefix(bus, m, s, path, path, false); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_added_append_all_prefix(bus, m, s, prefix, path, true); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { - BUS_DONT_DESTROY(bus); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - /* - * This emits an InterfacesAdded signal on the given path, by iterating - * all registered vtables and fallback vtables on the path. All - * properties are queried and included in the signal. - * This call is equivalent to sd_bus_emit_interfaces_added() with an - * explicit list of registered interfaces. However, unlike - * interfaces_added(), this call can figure out the list of supported - * interfaces itself. Furthermore, it properly adds the builtin - * org.freedesktop.DBus.* interfaces. - */ - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); - if (r < 0) - return r; - - r = sd_bus_message_append_basic(m, 'o', path); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); - if (r < 0) - return r; - - r = object_added_append_all(bus, m, path); - if (r < 0) - return r; - - if (bus->nodes_modified) - continue; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - } while (bus->nodes_modified); - - return sd_bus_send(bus, m, NULL); -} - -static int object_removed_append_all_prefix( - sd_bus *bus, - sd_bus_message *m, - Set *s, - const char *prefix, - const char *path, - bool require_fallback) { - - const char *previous_interface = NULL; - struct node_vtable *c; - struct node *n; - int r; - - assert(bus); - assert(m); - assert(s); - assert(prefix); - assert(path); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, c, n->vtables) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - void *u = NULL; - - if (require_fallback && !c->is_fallback) - continue; - if (streq_ptr(c->interface, previous_interface)) - continue; - - /* If a child-node already handled this interface, we - * skip it on any of its parents. The child vtables - * always fully override any conflicting vtables of - * any parent node. */ - if (set_get(s, c->interface)) - continue; - - r = node_vtable_get_userdata(bus, path, c, &u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - r = set_put(s, c->interface); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", c->interface); - if (r < 0) - return r; - - previous_interface = c->interface; - } - - return 0; -} - -static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; - char *prefix; - int r; - - assert(bus); - assert(m); - assert(path); - - /* see sd_bus_emit_object_added() for details */ - - s = set_new(&string_hash_ops); - if (!s) - return -ENOMEM; - - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties"); - if (r < 0) - return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); - if (r < 0) - return r; - - r = object_removed_append_all_prefix(bus, m, s, path, path, false); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_removed_append_all_prefix(bus, m, s, prefix, path, true); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return 0; -} - -_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { - BUS_DONT_DESTROY(bus); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - /* - * This is like sd_bus_emit_object_added(), but emits an - * InterfacesRemoved signal on the given path. This only includes any - * registered interfaces but skips the properties. Note that this will - * call into the find() callbacks of any registered vtable. Therefore, - * you must call this function before destroying/unlinking your object. - * Otherwise, the list of interfaces will be incomplete. However, note - * that this will *NOT* call into any property callback. Therefore, the - * object might be in an "destructed" state, as long as we can find it. - */ - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); - if (r < 0) - return r; - - r = sd_bus_message_append_basic(m, 'o', path); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return r; - - r = object_removed_append_all(bus, m, path); - if (r < 0) - return r; - - if (bus->nodes_modified) - continue; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - } while (bus->nodes_modified); - - return sd_bus_send(bus, m, NULL); -} - -static int interfaces_added_append_one_prefix( - sd_bus *bus, - sd_bus_message *m, - const char *prefix, - const char *path, - const char *interface, - bool require_fallback) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - bool found_interface = false; - struct node_vtable *c; - struct node *n; - void *u = NULL; - int r; - - assert(bus); - assert(m); - assert(prefix); - assert(path); - assert(interface); - - n = hashmap_get(bus->nodes, prefix); - if (!n) - return 0; - - LIST_FOREACH(vtables, c, n->vtables) { - if (require_fallback && !c->is_fallback) - continue; - - if (!streq(c->interface, interface)) - continue; - - r = node_vtable_get_userdata(bus, path, c, &u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - if (r == 0) - continue; - - if (!found_interface) { - r = sd_bus_message_append_basic(m, 's', interface); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "{sv}"); - if (r < 0) - return r; - - found_interface = true; - } - - r = vtable_append_all_properties(bus, m, path, c, u, &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; - } - - if (found_interface) { - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - return found_interface; -} - -static int interfaces_added_append_one( - sd_bus *bus, - sd_bus_message *m, - const char *path, - const char *interface) { - - char *prefix; - int r; - - assert(bus); - assert(m); - assert(path); - assert(interface); - - r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - - prefix = alloca(strlen(path) + 1); - OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true); - if (r != 0) - return r; - if (bus->nodes_modified) - return 0; - } - - return -ENOENT; -} - -_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) { - BUS_DONT_DESTROY(bus); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - char **i; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (strv_isempty(interfaces)) - return 0; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - do { - bus->nodes_modified = false; - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); - if (r < 0) - return r; - - r = sd_bus_message_append_basic(m, 'o', path); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); - if (r < 0) - return r; - - STRV_FOREACH(i, interfaces) { - assert_return(interface_name_is_valid(*i), -EINVAL); - - r = sd_bus_message_open_container(m, 'e', "sa{sv}"); - if (r < 0) - return r; - - r = interfaces_added_append_one(bus, m, path, *i); - if (r < 0) - return r; - - if (bus->nodes_modified) - break; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - if (bus->nodes_modified) - continue; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - } while (bus->nodes_modified); - - return sd_bus_send(bus, m, NULL); -} - -_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) { - char **interfaces; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - interfaces = strv_from_stdarg_alloca(interface); - - return sd_bus_emit_interfaces_added_strv(bus, path, interfaces); -} - -_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct node *object_manager; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (strv_isempty(interfaces)) - return 0; - - r = bus_find_parent_object_manager(bus, &object_manager, path); - if (r < 0) - return r; - if (r == 0) - return -ESRCH; - - r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); - if (r < 0) - return r; - - r = sd_bus_message_append_basic(m, 'o', path); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, interfaces); - if (r < 0) - return r; - - return sd_bus_send(bus, m, NULL); -} - -_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) { - char **interfaces; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - interfaces = strv_from_stdarg_alloca(interface); - - return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces); -} - -_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) { - sd_bus_slot *s; - struct node *n; - int r; - - assert_return(bus, -EINVAL); - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - n = bus_node_allocate(bus, path); - if (!n) - return -ENOMEM; - - s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL); - if (!s) { - r = -ENOMEM; - goto fail; - } - - s->node_object_manager.node = n; - LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager); - bus->nodes_modified = true; - - if (slot) - *slot = s; - - return 0; - -fail: - sd_bus_slot_unref(s); - bus_node_gc(bus, n); - - return r; -} diff --git a/src/libsystemd/sd-bus/bus-objects.h b/src/libsystemd/sd-bus/bus-objects.h deleted file mode 100644 index e0b8c534ed..0000000000 --- a/src/libsystemd/sd-bus/bus-objects.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-internal.h" - -int bus_process_object(sd_bus *bus, sd_bus_message *m); -void bus_node_gc(sd_bus *b, struct node *n); diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h deleted file mode 100644 index 9d180cb284..0000000000 --- a/src/libsystemd/sd-bus/bus-protocol.h +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "macro.h" - -/* Packet header */ - -struct _packed_ bus_header { - /* The first four fields are identical for dbus1, and dbus2 */ - uint8_t endian; - uint8_t type; - uint8_t flags; - uint8_t version; - - union _packed_ { - /* dbus1: Used for SOCK_STREAM connections */ - struct _packed_ { - uint32_t body_size; - - /* Note that what the bus spec calls "serial" we'll call - "cookie" instead, because we don't want to imply that the - cookie was in any way monotonically increasing. */ - uint32_t serial; - uint32_t fields_size; - } dbus1; - - /* dbus2: Used for kdbus connections */ - struct _packed_ { - uint32_t _reserved; - uint64_t cookie; - } dbus2; - - /* Note that both header versions have the same size! */ - }; -}; - -/* Endianness */ - -enum { - _BUS_INVALID_ENDIAN = 0, - BUS_LITTLE_ENDIAN = 'l', - BUS_BIG_ENDIAN = 'B', -#if __BYTE_ORDER == __BIG_ENDIAN - BUS_NATIVE_ENDIAN = BUS_BIG_ENDIAN, - BUS_REVERSE_ENDIAN = BUS_LITTLE_ENDIAN -#else - BUS_NATIVE_ENDIAN = BUS_LITTLE_ENDIAN, - BUS_REVERSE_ENDIAN = BUS_BIG_ENDIAN -#endif -}; - -/* Flags */ - -enum { - BUS_MESSAGE_NO_REPLY_EXPECTED = 1, - BUS_MESSAGE_NO_AUTO_START = 2, - BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION = 4, -}; - -/* Header fields */ - -enum { - _BUS_MESSAGE_HEADER_INVALID = 0, - BUS_MESSAGE_HEADER_PATH, - BUS_MESSAGE_HEADER_INTERFACE, - BUS_MESSAGE_HEADER_MEMBER, - BUS_MESSAGE_HEADER_ERROR_NAME, - BUS_MESSAGE_HEADER_REPLY_SERIAL, - BUS_MESSAGE_HEADER_DESTINATION, - BUS_MESSAGE_HEADER_SENDER, - BUS_MESSAGE_HEADER_SIGNATURE, - BUS_MESSAGE_HEADER_UNIX_FDS, - _BUS_MESSAGE_HEADER_MAX -}; - -/* RequestName parameters */ - -enum { - BUS_NAME_ALLOW_REPLACEMENT = 1, - BUS_NAME_REPLACE_EXISTING = 2, - BUS_NAME_DO_NOT_QUEUE = 4 -}; - -/* RequestName returns */ -enum { - BUS_NAME_PRIMARY_OWNER = 1, - BUS_NAME_IN_QUEUE = 2, - BUS_NAME_EXISTS = 3, - BUS_NAME_ALREADY_OWNER = 4 -}; - -/* ReleaseName returns */ -enum { - BUS_NAME_RELEASED = 1, - BUS_NAME_NON_EXISTENT = 2, - BUS_NAME_NOT_OWNER = 3, -}; - -/* StartServiceByName returns */ -enum { - BUS_START_REPLY_SUCCESS = 1, - BUS_START_REPLY_ALREADY_RUNNING = 2, -}; - -#define BUS_INTROSPECT_DOCTYPE \ - "\n" - -#define BUS_INTROSPECT_INTERFACE_PEER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c deleted file mode 100644 index 7bc243494a..0000000000 --- a/src/libsystemd/sd-bus/bus-signature.c +++ /dev/null @@ -1,158 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "bus-signature.h" -#include "bus-type.h" - -static int signature_element_length_internal( - const char *s, - bool allow_dict_entry, - unsigned array_depth, - unsigned struct_depth, - size_t *l) { - - int r; - - if (!s) - return -EINVAL; - - assert(l); - - if (bus_type_is_basic(*s) || *s == SD_BUS_TYPE_VARIANT) { - *l = 1; - return 0; - } - - if (*s == SD_BUS_TYPE_ARRAY) { - size_t t; - - if (array_depth >= 32) - return -EINVAL; - - r = signature_element_length_internal(s + 1, true, array_depth+1, struct_depth, &t); - if (r < 0) - return r; - - *l = t + 1; - return 0; - } - - if (*s == SD_BUS_TYPE_STRUCT_BEGIN) { - const char *p = s + 1; - - if (struct_depth >= 32) - return -EINVAL; - - while (*p != SD_BUS_TYPE_STRUCT_END) { - size_t t; - - r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t); - if (r < 0) - return r; - - p += t; - } - - *l = p - s + 1; - return 0; - } - - if (*s == SD_BUS_TYPE_DICT_ENTRY_BEGIN && allow_dict_entry) { - const char *p = s + 1; - unsigned n = 0; - - if (struct_depth >= 32) - return -EINVAL; - - while (*p != SD_BUS_TYPE_DICT_ENTRY_END) { - size_t t; - - if (n == 0 && !bus_type_is_basic(*p)) - return -EINVAL; - - r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t); - if (r < 0) - return r; - - p += t; - n++; - } - - if (n != 2) - return -EINVAL; - - *l = p - s + 1; - return 0; - } - - return -EINVAL; -} - - -int signature_element_length(const char *s, size_t *l) { - return signature_element_length_internal(s, true, 0, 0, l); -} - -bool signature_is_single(const char *s, bool allow_dict_entry) { - int r; - size_t t; - - if (!s) - return false; - - r = signature_element_length_internal(s, allow_dict_entry, 0, 0, &t); - if (r < 0) - return false; - - return s[t] == 0; -} - -bool signature_is_pair(const char *s) { - - if (!s) - return false; - - if (!bus_type_is_basic(*s)) - return false; - - return signature_is_single(s + 1, false); -} - -bool signature_is_valid(const char *s, bool allow_dict_entry) { - const char *p; - int r; - - if (!s) - return false; - - p = s; - while (*p) { - size_t t; - - r = signature_element_length_internal(p, allow_dict_entry, 0, 0, &t); - if (r < 0) - return false; - - p += t; - } - - return p - s <= 255; -} diff --git a/src/libsystemd/sd-bus/bus-signature.h b/src/libsystemd/sd-bus/bus-signature.h deleted file mode 100644 index 1e0cd7f587..0000000000 --- a/src/libsystemd/sd-bus/bus-signature.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -bool signature_is_single(const char *s, bool allow_dict_entry); -bool signature_is_pair(const char *s); -bool signature_is_valid(const char *s, bool allow_dict_entry); - -int signature_element_length(const char *s, size_t *l); diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c deleted file mode 100644 index 8e9074c7df..0000000000 --- a/src/libsystemd/sd-bus/bus-slot.c +++ /dev/null @@ -1,286 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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, - bool floating, - BusSlotType type, - size_t extra, - void *userdata) { - - sd_bus_slot *slot; - - assert(bus); - - slot = malloc0(offsetof(sd_bus_slot, reply_callback) + extra); - if (!slot) - return NULL; - - slot->n_ref = 1; - slot->type = type; - slot->bus = bus; - slot->floating = floating; - slot->userdata = userdata; - - if (!floating) - sd_bus_ref(bus); - - LIST_PREPEND(slots, bus->slots, slot); - - return slot; -} - -_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) { - - if (!slot) - return NULL; - - assert(slot->n_ref > 0); - - slot->n_ref++; - return slot; -} - -void bus_slot_disconnect(sd_bus_slot *slot) { - sd_bus *bus; - - assert(slot); - - if (!slot->bus) - return; - - switch (slot->type) { - - case BUS_REPLY_CALLBACK: - - if (slot->reply_callback.cookie != 0) - ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); - - if (slot->reply_callback.timeout != 0) - prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); - - break; - - case BUS_FILTER_CALLBACK: - slot->bus->filter_callbacks_modified = true; - LIST_REMOVE(callbacks, slot->bus->filter_callbacks, &slot->filter_callback); - break; - - case BUS_MATCH_CALLBACK: - - if (slot->match_added) - bus_remove_match_internal(slot->bus, slot->match_callback.match_string, slot->match_callback.cookie); - - slot->bus->match_callbacks_modified = true; - bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); - - free(slot->match_callback.match_string); - - break; - - case BUS_NODE_CALLBACK: - - if (slot->node_callback.node) { - LIST_REMOVE(callbacks, slot->node_callback.node->callbacks, &slot->node_callback); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_callback.node); - } - - break; - - case BUS_NODE_ENUMERATOR: - - if (slot->node_enumerator.node) { - LIST_REMOVE(enumerators, slot->node_enumerator.node->enumerators, &slot->node_enumerator); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_enumerator.node); - } - - break; - - case BUS_NODE_OBJECT_MANAGER: - - if (slot->node_object_manager.node) { - LIST_REMOVE(object_managers, slot->node_object_manager.node->object_managers, &slot->node_object_manager); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_object_manager.node); - } - - break; - - case BUS_NODE_VTABLE: - - if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) { - const sd_bus_vtable *v; - - for (v = slot->node_vtable.vtable; v->type != _SD_BUS_VTABLE_END; v++) { - struct vtable_member *x = NULL; - - switch (v->type) { - - case _SD_BUS_VTABLE_METHOD: { - struct vtable_member key; - - key.path = slot->node_vtable.node->path; - key.interface = slot->node_vtable.interface; - key.member = v->x.method.member; - - x = hashmap_remove(slot->bus->vtable_methods, &key); - break; - } - - case _SD_BUS_VTABLE_PROPERTY: - case _SD_BUS_VTABLE_WRITABLE_PROPERTY: { - struct vtable_member key; - - key.path = slot->node_vtable.node->path; - key.interface = slot->node_vtable.interface; - key.member = v->x.method.member; - - - x = hashmap_remove(slot->bus->vtable_properties, &key); - break; - }} - - free(x); - } - } - - free(slot->node_vtable.interface); - - if (slot->node_vtable.node) { - LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); - slot->bus->nodes_modified = true; - - bus_node_gc(slot->bus, slot->node_vtable.node); - } - - break; - - default: - assert_not_reached("Wut? Unknown slot type?"); - } - - bus = slot->bus; - - slot->type = _BUS_SLOT_INVALID; - slot->bus = NULL; - LIST_REMOVE(slots, bus->slots, slot); - - if (!slot->floating) - sd_bus_unref(bus); -} - -_public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { - - if (!slot) - return NULL; - - assert(slot->n_ref > 0); - - if (slot->n_ref > 1) { - slot->n_ref--; - return NULL; - } - - bus_slot_disconnect(slot); - free(slot->description); - free(slot); - - return NULL; -} - -_public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) { - assert_return(slot, NULL); - - return slot->bus; -} - -_public_ void *sd_bus_slot_get_userdata(sd_bus_slot *slot) { - assert_return(slot, NULL); - - return slot->userdata; -} - -_public_ void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata) { - void *ret; - - assert_return(slot, NULL); - - ret = slot->userdata; - slot->userdata = userdata; - - return ret; -} - -_public_ sd_bus_message *sd_bus_slot_get_current_message(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_message; -} - -_public_ sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_handler; -} - -_public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) { - assert_return(slot, NULL); - assert_return(slot->type >= 0, NULL); - - if (slot->bus->current_slot != slot) - return NULL; - - return slot->bus->current_userdata; -} - -_public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) { - assert_return(slot, -EINVAL); - - return free_and_strdup(&slot->description, description); -} - -_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) { - assert_return(slot, -EINVAL); - assert_return(description, -EINVAL); - assert_return(slot->description, -ENXIO); - - *description = slot->description; - return 0; -} diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h deleted file mode 100644 index 3b8b94dc6b..0000000000 --- a/src/libsystemd/sd-bus/bus-slot.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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); - -void bus_slot_disconnect(sd_bus_slot *slot); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c deleted file mode 100644 index cfd7753139..0000000000 --- a/src/libsystemd/sd-bus/bus-socket.c +++ /dev/null @@ -1,1064 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.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) - -static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) { - - while (size > 0) { - struct iovec *i = iov + *idx; - - if (i->iov_len > size) { - i->iov_base = (uint8_t*) i->iov_base + size; - i->iov_len -= size; - return; - } - - size -= i->iov_len; - - i->iov_base = NULL; - i->iov_len = 0; - - (*idx)++; - } -} - -static int append_iovec(sd_bus_message *m, const void *p, size_t sz) { - assert(m); - assert(p); - assert(sz > 0); - - m->iovec[m->n_iovec].iov_base = (void*) p; - m->iovec[m->n_iovec].iov_len = sz; - m->n_iovec++; - - return 0; -} - -static int bus_message_setup_iovec(sd_bus_message *m) { - struct bus_body_part *part; - unsigned n, i; - int r; - - assert(m); - assert(m->sealed); - - if (m->n_iovec > 0) - return 0; - - assert(!m->iovec); - - n = 1 + m->n_body_parts; - if (n < ELEMENTSOF(m->iovec_fixed)) - m->iovec = m->iovec_fixed; - else { - m->iovec = new(struct iovec, n); - if (!m->iovec) { - r = -ENOMEM; - goto fail; - } - } - - r = append_iovec(m, m->header, BUS_MESSAGE_BODY_BEGIN(m)); - if (r < 0) - goto fail; - - MESSAGE_FOREACH_PART(part, i, m) { - r = bus_body_part_map(part); - if (r < 0) - goto fail; - - r = append_iovec(m, part->data, part->size); - if (r < 0) - goto fail; - } - - assert(n == m->n_iovec); - - return 0; - -fail: - m->poisoned = true; - return r; -} - -bool bus_socket_auth_needs_write(sd_bus *b) { - - unsigned i; - - if (b->auth_index >= ELEMENTSOF(b->auth_iovec)) - return false; - - for (i = b->auth_index; i < ELEMENTSOF(b->auth_iovec); i++) { - struct iovec *j = b->auth_iovec + i; - - if (j->iov_len > 0) - return true; - } - - return false; -} - -static int bus_socket_write_auth(sd_bus *b) { - ssize_t k; - - assert(b); - assert(b->state == BUS_AUTHENTICATING); - - if (!bus_socket_auth_needs_write(b)) - return 0; - - if (b->prefer_writev) - k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); - else { - struct msghdr mh; - zero(mh); - - mh.msg_iov = b->auth_iovec + b->auth_index; - mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index; - - k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); - if (k < 0 && errno == ENOTSOCK) { - b->prefer_writev = true; - k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); - } - } - - if (k < 0) - return errno == EAGAIN ? 0 : -errno; - - iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k); - return 1; -} - -static int bus_socket_auth_verify_client(sd_bus *b) { - char *e, *f, *start; - sd_id128_t peer; - unsigned i; - int r; - - assert(b); - - /* We expect two response lines: "OK" and possibly - * "AGREE_UNIX_FD" */ - - e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2); - if (!e) - return 0; - - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) { - f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2); - if (!f) - return 0; - - start = f + 2; - } else { - f = NULL; - start = e + 2; - } - - /* Nice! We got all the lines we need. First check the OK - * line */ - - if (e - (char*) b->rbuffer != 3 + 32) - return -EPERM; - - if (memcmp(b->rbuffer, "OK ", 3)) - return -EPERM; - - b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL; - - for (i = 0; i < 32; i += 2) { - int x, y; - - x = unhexchar(((char*) b->rbuffer)[3 + i]); - y = unhexchar(((char*) b->rbuffer)[3 + i + 1]); - - if (x < 0 || y < 0) - return -EINVAL; - - peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y); - } - - if (!sd_id128_is_null(b->server_id) && - !sd_id128_equal(b->server_id, peer)) - return -EPERM; - - b->server_id = peer; - - /* And possibly check the second line, too */ - - if (f) - b->can_fds = - (f - e == strlen("\r\nAGREE_UNIX_FD")) && - memcmp(e + 2, "AGREE_UNIX_FD", strlen("AGREE_UNIX_FD")) == 0; - - b->rbuffer_size -= (start - (char*) b->rbuffer); - memmove(b->rbuffer, start, b->rbuffer_size); - - r = bus_start_running(b); - if (r < 0) - return r; - - return 1; -} - -static bool line_equals(const char *s, size_t m, const char *line) { - size_t l; - - l = strlen(line); - if (l != m) - return false; - - return memcmp(s, line, l) == 0; -} - -static bool line_begins(const char *s, size_t m, const char *word) { - size_t l; - - l = strlen(word); - if (m < l) - return false; - - if (memcmp(s, word, l) != 0) - return false; - - return m == l || (m > l && s[l] == ' '); -} - -static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { - _cleanup_free_ char *token = NULL; - size_t len; - int r; - - if (!b->anonymous_auth) - return 0; - - if (l <= 0) - return 1; - - assert(p[0] == ' '); - p++; l--; - - if (l % 2 != 0) - return 0; - - r = unhexmem(p, l, (void **) &token, &len); - if (r < 0) - return 0; - - if (memchr(token, 0, len)) - return 0; - - return !!utf8_is_valid(token); -} - -static int verify_external_token(sd_bus *b, const char *p, size_t l) { - _cleanup_free_ char *token = NULL; - size_t len; - uid_t u; - int r; - - /* We don't do any real authentication here. Instead, we if - * the owner of this bus wanted authentication he should have - * checked SO_PEERCRED before even creating the bus object. */ - - if (!b->anonymous_auth && !b->ucred_valid) - return 0; - - if (l <= 0) - return 1; - - assert(p[0] == ' '); - p++; l--; - - if (l % 2 != 0) - return 0; - - r = unhexmem(p, l, (void**) &token, &len); - if (r < 0) - return 0; - - if (memchr(token, 0, len)) - return 0; - - r = parse_uid(token, &u); - if (r < 0) - return 0; - - /* We ignore the passed value if anonymous authentication is - * on anyway. */ - if (!b->anonymous_auth && u != b->ucred.uid) - return 0; - - return 1; -} - -static int bus_socket_auth_write(sd_bus *b, const char *t) { - char *p; - size_t l; - - assert(b); - assert(t); - - /* We only make use of the first iovec */ - assert(b->auth_index == 0 || b->auth_index == 1); - - l = strlen(t); - p = malloc(b->auth_iovec[0].iov_len + l); - if (!p) - return -ENOMEM; - - memcpy_safe(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len); - memcpy(p + b->auth_iovec[0].iov_len, t, l); - - b->auth_iovec[0].iov_base = p; - b->auth_iovec[0].iov_len += l; - - free(b->auth_buffer); - b->auth_buffer = p; - b->auth_index = 0; - return 0; -} - -static int bus_socket_auth_write_ok(sd_bus *b) { - char t[3 + 32 + 2 + 1]; - - assert(b); - - xsprintf(t, "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id)); - - return bus_socket_auth_write(b, t); -} - -static int bus_socket_auth_verify_server(sd_bus *b) { - char *e; - const char *line; - size_t l; - bool processed = false; - int r; - - assert(b); - - if (b->rbuffer_size < 1) - return 0; - - /* First char must be a NUL byte */ - if (*(char*) b->rbuffer != 0) - return -EIO; - - if (b->rbuffer_size < 3) - return 0; - - /* Begin with the first line */ - if (b->auth_rbegin <= 0) - b->auth_rbegin = 1; - - for (;;) { - /* Check if line is complete */ - line = (char*) b->rbuffer + b->auth_rbegin; - e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2); - if (!e) - return processed; - - l = e - line; - - if (line_begins(line, l, "AUTH ANONYMOUS")) { - - r = verify_anonymous_token(b, line + 14, l - 14); - if (r < 0) - return r; - if (r == 0) - r = bus_socket_auth_write(b, "REJECTED\r\n"); - else { - b->auth = BUS_AUTH_ANONYMOUS; - r = bus_socket_auth_write_ok(b); - } - - } else if (line_begins(line, l, "AUTH EXTERNAL")) { - - r = verify_external_token(b, line + 13, l - 13); - if (r < 0) - return r; - if (r == 0) - r = bus_socket_auth_write(b, "REJECTED\r\n"); - else { - b->auth = BUS_AUTH_EXTERNAL; - r = bus_socket_auth_write_ok(b); - } - - } else if (line_begins(line, l, "AUTH")) - r = bus_socket_auth_write(b, "REJECTED EXTERNAL ANONYMOUS\r\n"); - else if (line_equals(line, l, "CANCEL") || - line_begins(line, l, "ERROR")) { - - b->auth = _BUS_AUTH_INVALID; - r = bus_socket_auth_write(b, "REJECTED\r\n"); - - } else if (line_equals(line, l, "BEGIN")) { - - if (b->auth == _BUS_AUTH_INVALID) - r = bus_socket_auth_write(b, "ERROR\r\n"); - else { - /* We can't leave from the auth phase - * before we haven't written - * everything queued, so let's check - * that */ - - if (bus_socket_auth_needs_write(b)) - return 1; - - b->rbuffer_size -= (e + 2 - (char*) b->rbuffer); - memmove(b->rbuffer, e + 2, b->rbuffer_size); - return bus_start_running(b); - } - - } else if (line_begins(line, l, "DATA")) { - - if (b->auth == _BUS_AUTH_INVALID) - r = bus_socket_auth_write(b, "ERROR\r\n"); - else { - if (b->auth == BUS_AUTH_ANONYMOUS) - r = verify_anonymous_token(b, line + 4, l - 4); - else - r = verify_external_token(b, line + 4, l - 4); - - if (r < 0) - return r; - if (r == 0) { - b->auth = _BUS_AUTH_INVALID; - r = bus_socket_auth_write(b, "REJECTED\r\n"); - } else - r = bus_socket_auth_write_ok(b); - } - } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) { - if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD)) - r = bus_socket_auth_write(b, "ERROR\r\n"); - else { - b->can_fds = true; - r = bus_socket_auth_write(b, "AGREE_UNIX_FD\r\n"); - } - } else - r = bus_socket_auth_write(b, "ERROR\r\n"); - - if (r < 0) - return r; - - b->auth_rbegin = e + 2 - (char*) b->rbuffer; - - processed = true; - } -} - -static int bus_socket_auth_verify(sd_bus *b) { - assert(b); - - if (b->is_server) - return bus_socket_auth_verify_server(b); - else - return bus_socket_auth_verify_client(b); -} - -static int bus_socket_read_auth(sd_bus *b) { - struct msghdr mh; - struct iovec iov = {}; - size_t n; - ssize_t k; - int r; - void *p; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; - } control; - bool handle_cmsg = false; - - assert(b); - assert(b->state == BUS_AUTHENTICATING); - - r = bus_socket_auth_verify(b); - if (r != 0) - return r; - - n = MAX(256u, b->rbuffer_size * 2); - - if (n > BUS_AUTH_SIZE_MAX) - n = BUS_AUTH_SIZE_MAX; - - if (b->rbuffer_size >= n) - return -ENOBUFS; - - p = realloc(b->rbuffer, n); - if (!p) - return -ENOMEM; - - b->rbuffer = p; - - iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size; - iov.iov_len = n - b->rbuffer_size; - - if (b->prefer_readv) - k = readv(b->input_fd, &iov, 1); - else { - zero(mh); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); - if (k < 0 && errno == ENOTSOCK) { - b->prefer_readv = true; - k = readv(b->input_fd, &iov, 1); - } else - handle_cmsg = true; - } - if (k < 0) - return errno == EAGAIN ? 0 : -errno; - if (k == 0) - return -ECONNRESET; - - b->rbuffer_size += k; - - if (handle_cmsg) { - struct cmsghdr *cmsg; - - CMSG_FOREACH(cmsg, &mh) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - int j; - - /* Whut? We received fds during the auth - * protocol? Somebody is playing games with - * us. Close them all, and fail */ - j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - close_many((int*) CMSG_DATA(cmsg), j); - return -EIO; - } else - log_debug("Got unexpected auxiliary data with level=%d and type=%d", - cmsg->cmsg_level, cmsg->cmsg_type); - } - - r = bus_socket_auth_verify(b); - if (r != 0) - return r; - - return 1; -} - -void bus_socket_setup(sd_bus *b) { - assert(b); - - /* Increase the buffers to 8 MB */ - fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); - fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); - - b->is_kernel = false; - b->message_version = 1; - b->message_endian = 0; -} - -static void bus_get_peercred(sd_bus *b) { - int r; - - assert(b); - - /* Get the peer for socketpair() sockets */ - b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; - - /* Get the SELinux context of the peer */ - 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) { - size_t l; - const char *auth_suffix, *auth_prefix; - - assert(b); - - if (b->anonymous_auth) { - auth_prefix = "\0AUTH ANONYMOUS "; - - /* For ANONYMOUS auth we send some arbitrary "trace" string */ - l = 9; - b->auth_buffer = hexmem("anonymous", l); - } else { - char text[DECIMAL_STR_MAX(uid_t) + 1]; - - auth_prefix = "\0AUTH EXTERNAL "; - - xsprintf(text, UID_FMT, geteuid()); - - l = strlen(text); - b->auth_buffer = hexmem(text, l); - } - - if (!b->auth_buffer) - return -ENOMEM; - - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) - auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n"; - else - auth_suffix = "\r\nBEGIN\r\n"; - - b->auth_iovec[0].iov_base = (void*) auth_prefix; - b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1); - b->auth_iovec[1].iov_base = (void*) b->auth_buffer; - b->auth_iovec[1].iov_len = l * 2; - b->auth_iovec[2].iov_base = (void*) auth_suffix; - b->auth_iovec[2].iov_len = strlen(auth_suffix); - - return bus_socket_write_auth(b); -} - -int bus_socket_start_auth(sd_bus *b) { - assert(b); - - bus_get_peercred(b); - - b->state = BUS_AUTHENTICATING; - b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT; - - if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; - - if (b->output_fd != b->input_fd) - if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; - - if (b->is_server) - return bus_socket_read_auth(b); - else - return bus_socket_start_auth_client(b); -} - -int bus_socket_connect(sd_bus *b) { - int r; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->sockaddr.sa.sa_family != AF_UNSPEC); - - b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (b->input_fd < 0) - return -errno; - - b->output_fd = b->input_fd; - - bus_socket_setup(b); - - r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); - if (r < 0) { - if (errno == EINPROGRESS) - return 1; - - return -errno; - } - - return bus_socket_start_auth(b); -} - -int bus_socket_exec(sd_bus *b) { - int s[2], r; - pid_t pid; - - assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->exec_path); - - r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s); - if (r < 0) - return -errno; - - pid = fork(); - if (pid < 0) { - safe_close_pair(s); - return -errno; - } - if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - close_all_fds(s+1, 1); - - assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO); - assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO); - - if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO) - safe_close(s[1]); - - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_nonblock(STDIN_FILENO, false); - fd_nonblock(STDOUT_FILENO, false); - - if (b->exec_argv) - execvp(b->exec_path, b->exec_argv); - else { - const char *argv[] = { b->exec_path, NULL }; - execvp(b->exec_path, (char**) argv); - } - - _exit(EXIT_FAILURE); - } - - safe_close(s[1]); - b->output_fd = b->input_fd = s[0]; - - bus_socket_setup(b); - - return bus_socket_start_auth(b); -} - -int bus_socket_take_fd(sd_bus *b) { - assert(b); - - bus_socket_setup(b); - - return bus_socket_start_auth(b); -} - -int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { - struct iovec *iov; - ssize_t k; - size_t n; - unsigned j; - int r; - - assert(bus); - assert(m); - assert(idx); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - if (*idx >= BUS_MESSAGE_SIZE(m)) - return 0; - - r = bus_message_setup_iovec(m); - if (r < 0) - return r; - - n = m->n_iovec * sizeof(struct iovec); - iov = alloca(n); - memcpy_safe(iov, m->iovec, n); - - j = 0; - iovec_advance(iov, &j, *idx); - - if (bus->prefer_writev) - k = writev(bus->output_fd, iov, m->n_iovec); - else { - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = m->n_iovec, - }; - - if (m->n_fds > 0) { - struct cmsghdr *control; - - mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); - mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); - control->cmsg_level = SOL_SOCKET; - control->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds); - } - - k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); - if (k < 0 && errno == ENOTSOCK) { - bus->prefer_writev = true; - k = writev(bus->output_fd, iov, m->n_iovec); - } - } - - if (k < 0) - return errno == EAGAIN ? 0 : -errno; - - *idx += (size_t) k; - return 1; -} - -static int bus_socket_read_message_need(sd_bus *bus, size_t *need) { - uint32_t a, b; - uint8_t e; - uint64_t sum; - - assert(bus); - assert(need); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - if (bus->rbuffer_size < sizeof(struct bus_header)) { - *need = sizeof(struct bus_header) + 8; - - /* Minimum message size: - * - * Header + - * - * Method Call: +2 string headers - * Signal: +3 string headers - * Method Error: +1 string headers - * +1 uint32 headers - * Method Reply: +1 uint32 headers - * - * A string header is at least 9 bytes - * A uint32 header is at least 8 bytes - * - * Hence the minimum message size of a valid message - * is header + 8 bytes */ - - return 0; - } - - a = ((const uint32_t*) bus->rbuffer)[1]; - b = ((const uint32_t*) bus->rbuffer)[3]; - - e = ((const uint8_t*) bus->rbuffer)[0]; - if (e == BUS_LITTLE_ENDIAN) { - a = le32toh(a); - b = le32toh(b); - } else if (e == BUS_BIG_ENDIAN) { - a = be32toh(a); - b = be32toh(b); - } else - return -EBADMSG; - - sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a; - if (sum >= BUS_MESSAGE_SIZE_MAX) - return -ENOBUFS; - - *need = (size_t) sum; - return 0; -} - -static int bus_socket_make_message(sd_bus *bus, size_t size) { - sd_bus_message *t; - void *b; - int r; - - assert(bus); - assert(bus->rbuffer_size >= size); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - r = bus_rqueue_make_room(bus); - if (r < 0) - return r; - - if (bus->rbuffer_size > size) { - b = memdup((const uint8_t*) bus->rbuffer + size, - bus->rbuffer_size - size); - if (!b) - return -ENOMEM; - } else - b = NULL; - - r = bus_message_from_malloc(bus, - bus->rbuffer, size, - bus->fds, bus->n_fds, - NULL, - &t); - if (r < 0) { - free(b); - return r; - } - - bus->rbuffer = b; - bus->rbuffer_size -= size; - - bus->fds = NULL; - bus->n_fds = 0; - - bus->rqueue[bus->rqueue_size++] = t; - - return 1; -} - -int bus_socket_read_message(sd_bus *bus) { - struct msghdr mh; - struct iovec iov = {}; - ssize_t k; - size_t need; - int r; - void *b; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; - } control; - bool handle_cmsg = false; - - assert(bus); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - r = bus_socket_read_message_need(bus, &need); - if (r < 0) - return r; - - if (bus->rbuffer_size >= need) - return bus_socket_make_message(bus, need); - - b = realloc(bus->rbuffer, need); - if (!b) - return -ENOMEM; - - bus->rbuffer = b; - - iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size; - iov.iov_len = need - bus->rbuffer_size; - - if (bus->prefer_readv) - k = readv(bus->input_fd, &iov, 1); - else { - zero(mh); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); - if (k < 0 && errno == ENOTSOCK) { - bus->prefer_readv = true; - k = readv(bus->input_fd, &iov, 1); - } else - handle_cmsg = true; - } - if (k < 0) - return errno == EAGAIN ? 0 : -errno; - if (k == 0) - return -ECONNRESET; - - bus->rbuffer_size += k; - - if (handle_cmsg) { - struct cmsghdr *cmsg; - - CMSG_FOREACH(cmsg, &mh) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) { - int n, *f; - - n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - - if (!bus->can_fds) { - /* Whut? We received fds but this - * isn't actually enabled? Close them, - * and fail */ - - close_many((int*) CMSG_DATA(cmsg), n); - return -EIO; - } - - f = realloc(bus->fds, sizeof(int) * (bus->n_fds + n)); - if (!f) { - close_many((int*) CMSG_DATA(cmsg), n); - return -ENOMEM; - } - - memcpy_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); - bus->fds = f; - bus->n_fds += n; - } else - log_debug("Got unexpected auxiliary data with level=%d and type=%d", - cmsg->cmsg_level, cmsg->cmsg_type); - } - - r = bus_socket_read_message_need(bus, &need); - if (r < 0) - return r; - - if (bus->rbuffer_size >= need) - return bus_socket_make_message(bus, need); - - return 1; -} - -int bus_socket_process_opening(sd_bus *b) { - int error = 0; - socklen_t slen = sizeof(error); - struct pollfd p = { - .fd = b->output_fd, - .events = POLLOUT, - }; - int r; - - assert(b->state == BUS_OPENING); - - r = poll(&p, 1, 0); - if (r < 0) - return -errno; - - if (!(p.revents & (POLLOUT|POLLERR|POLLHUP))) - return 0; - - r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen); - if (r < 0) - b->last_connect_error = errno; - else if (error != 0) - b->last_connect_error = error; - else if (p.revents & (POLLERR|POLLHUP)) - b->last_connect_error = ECONNREFUSED; - else - return bus_socket_start_auth(b); - - return bus_next_address(b); -} - -int bus_socket_process_authenticating(sd_bus *b) { - int r; - - assert(b); - assert(b->state == BUS_AUTHENTICATING); - - if (now(CLOCK_MONOTONIC) >= b->auth_timeout) - return -ETIMEDOUT; - - r = bus_socket_write_auth(b); - if (r != 0) - return r; - - return bus_socket_read_auth(b); -} diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h deleted file mode 100644 index 684feead74..0000000000 --- a/src/libsystemd/sd-bus/bus-socket.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -void bus_socket_setup(sd_bus *b); - -int bus_socket_connect(sd_bus *b); -int bus_socket_exec(sd_bus *b); -int bus_socket_take_fd(sd_bus *b); -int bus_socket_start_auth(sd_bus *b); - -int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx); -int bus_socket_read_message(sd_bus *bus); - -int bus_socket_process_opening(sd_bus *b); -int bus_socket_process_authenticating(sd_bus *b); - -bool bus_socket_auth_needs_write(sd_bus *b); diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c deleted file mode 100644 index 1f436fe560..0000000000 --- a/src/libsystemd/sd-bus/bus-track.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-track.h" -#include "bus-util.h" - -struct sd_bus_track { - unsigned n_ref; - sd_bus *bus; - sd_bus_track_handler_t handler; - void *userdata; - Hashmap *names; - LIST_FIELDS(sd_bus_track, queue); - Iterator iterator; - bool in_queue; - bool modified; -}; - -#define MATCH_PREFIX \ - "type='signal'," \ - "sender='org.freedesktop.DBus'," \ - "path='/org/freedesktop/DBus'," \ - "interface='org.freedesktop.DBus'," \ - "member='NameOwnerChanged'," \ - "arg0='" - -#define MATCH_SUFFIX \ - "'" - -#define MATCH_FOR_NAME(name) \ - ({ \ - char *_x; \ - size_t _l = strlen(name); \ - _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \ - strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ - _x; \ - }) - -static void bus_track_add_to_queue(sd_bus_track *track) { - assert(track); - - if (track->in_queue) - return; - - if (!track->handler) - return; - - LIST_PREPEND(queue, track->bus->track_queue, track); - track->in_queue = true; -} - -static void bus_track_remove_from_queue(sd_bus_track *track) { - assert(track); - - if (!track->in_queue) - return; - - LIST_REMOVE(queue, track->bus->track_queue, track); - track->in_queue = false; -} - -_public_ int sd_bus_track_new( - sd_bus *bus, - sd_bus_track **track, - sd_bus_track_handler_t handler, - void *userdata) { - - sd_bus_track *t; - - assert_return(bus, -EINVAL); - assert_return(track, -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - t = new0(sd_bus_track, 1); - if (!t) - return -ENOMEM; - - t->n_ref = 1; - t->handler = handler; - t->userdata = userdata; - t->bus = sd_bus_ref(bus); - - bus_track_add_to_queue(t); - - *track = t; - return 0; -} - -_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) { - - if (!track) - return NULL; - - assert(track->n_ref > 0); - - track->n_ref++; - - return track; -} - -_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { - const char *n; - - if (!track) - return NULL; - - assert(track->n_ref > 0); - - if (track->n_ref > 1) { - track->n_ref--; - return NULL; - } - - while ((n = hashmap_first_key(track->names))) - sd_bus_track_remove_name(track, n); - - bus_track_remove_from_queue(track); - hashmap_free(track->names); - sd_bus_unref(track->bus); - free(track); - - return NULL; -} - -static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - sd_bus_track *track = userdata; - const char *name, *old, *new; - int r; - - assert(message); - assert(track); - - r = sd_bus_message_read(message, "sss", &name, &old, &new); - if (r < 0) - return 0; - - sd_bus_track_remove_name(track, name); - return 0; -} - -_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { - _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; - _cleanup_free_ char *n = NULL; - const char *match; - int r; - - assert_return(track, -EINVAL); - assert_return(service_name_is_valid(name), -EINVAL); - - r = hashmap_ensure_allocated(&track->names, &string_hash_ops); - if (r < 0) - return r; - - n = strdup(name); - if (!n) - return -ENOMEM; - - /* First, subscribe to this name */ - match = MATCH_FOR_NAME(n); - r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track); - if (r < 0) - return r; - - r = hashmap_put(track->names, n, slot); - if (r == -EEXIST) - return 0; - if (r < 0) - return r; - - /* Second, check if it is currently existing, or maybe - * doesn't, or maybe disappeared already. */ - r = sd_bus_get_name_creds(track->bus, n, 0, NULL); - if (r < 0) { - hashmap_remove(track->names, n); - return r; - } - - n = NULL; - slot = NULL; - - bus_track_remove_from_queue(track); - track->modified = true; - - return 1; -} - -_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) { - _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; - _cleanup_free_ char *n = NULL; - - assert_return(name, -EINVAL); - - if (!track) - return 0; - - slot = hashmap_remove2(track->names, (char*) name, (void**) &n); - if (!slot) - return 0; - - if (hashmap_isempty(track->names)) - bus_track_add_to_queue(track); - - track->modified = true; - - return 1; -} - -_public_ unsigned sd_bus_track_count(sd_bus_track *track) { - if (!track) - return 0; - - return hashmap_size(track->names); -} - -_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) { - assert_return(track, NULL); - assert_return(name, NULL); - - return hashmap_get(track->names, (void*) name) ? name : NULL; -} - -_public_ const char* sd_bus_track_first(sd_bus_track *track) { - const char *n = NULL; - - if (!track) - return NULL; - - track->modified = false; - track->iterator = ITERATOR_FIRST; - - hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); - return n; -} - -_public_ const char* sd_bus_track_next(sd_bus_track *track) { - const char *n = NULL; - - if (!track) - return NULL; - - if (track->modified) - return NULL; - - hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); - return n; -} - -_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) { - const char *sender; - - assert_return(track, -EINVAL); - assert_return(m, -EINVAL); - - sender = sd_bus_message_get_sender(m); - if (!sender) - return -EINVAL; - - return sd_bus_track_add_name(track, sender); -} - -_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) { - const char *sender; - - assert_return(track, -EINVAL); - assert_return(m, -EINVAL); - - sender = sd_bus_message_get_sender(m); - if (!sender) - return -EINVAL; - - return sd_bus_track_remove_name(track, sender); -} - -_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) { - assert_return(track, NULL); - - return track->bus; -} - -void bus_track_dispatch(sd_bus_track *track) { - int r; - - assert(track); - assert(track->in_queue); - assert(track->handler); - - bus_track_remove_from_queue(track); - - sd_bus_track_ref(track); - - r = track->handler(track, track->userdata); - if (r < 0) - log_debug_errno(r, "Failed to process track handler: %m"); - else if (r == 0) - bus_track_add_to_queue(track); - - sd_bus_track_unref(track); -} - -_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) { - assert_return(track, NULL); - - return track->userdata; -} - -_public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) { - void *ret; - - assert_return(track, NULL); - - ret = track->userdata; - track->userdata = userdata; - - return ret; -} diff --git a/src/libsystemd/sd-bus/bus-track.h b/src/libsystemd/sd-bus/bus-track.h deleted file mode 100644 index 7d93a727d6..0000000000 --- a/src/libsystemd/sd-bus/bus-track.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -void bus_track_dispatch(sd_bus_track *track); diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c deleted file mode 100644 index c692afc580..0000000000 --- a/src/libsystemd/sd-bus/bus-type.c +++ /dev/null @@ -1,176 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-type.h" - -bool bus_type_is_valid(char c) { - static const char valid[] = { - SD_BUS_TYPE_BYTE, - SD_BUS_TYPE_BOOLEAN, - SD_BUS_TYPE_INT16, - SD_BUS_TYPE_UINT16, - SD_BUS_TYPE_INT32, - SD_BUS_TYPE_UINT32, - SD_BUS_TYPE_INT64, - SD_BUS_TYPE_UINT64, - SD_BUS_TYPE_DOUBLE, - SD_BUS_TYPE_STRING, - SD_BUS_TYPE_OBJECT_PATH, - SD_BUS_TYPE_SIGNATURE, - SD_BUS_TYPE_ARRAY, - SD_BUS_TYPE_VARIANT, - SD_BUS_TYPE_STRUCT, - SD_BUS_TYPE_DICT_ENTRY, - SD_BUS_TYPE_UNIX_FD - }; - - return !!memchr(valid, c, sizeof(valid)); -} - -bool bus_type_is_valid_in_signature(char c) { - static const char valid[] = { - SD_BUS_TYPE_BYTE, - SD_BUS_TYPE_BOOLEAN, - SD_BUS_TYPE_INT16, - SD_BUS_TYPE_UINT16, - SD_BUS_TYPE_INT32, - SD_BUS_TYPE_UINT32, - SD_BUS_TYPE_INT64, - SD_BUS_TYPE_UINT64, - SD_BUS_TYPE_DOUBLE, - SD_BUS_TYPE_STRING, - SD_BUS_TYPE_OBJECT_PATH, - SD_BUS_TYPE_SIGNATURE, - SD_BUS_TYPE_ARRAY, - SD_BUS_TYPE_VARIANT, - SD_BUS_TYPE_STRUCT_BEGIN, - SD_BUS_TYPE_STRUCT_END, - SD_BUS_TYPE_DICT_ENTRY_BEGIN, - SD_BUS_TYPE_DICT_ENTRY_END, - SD_BUS_TYPE_UNIX_FD - }; - - return !!memchr(valid, c, sizeof(valid)); -} - -bool bus_type_is_basic(char c) { - static const char valid[] = { - SD_BUS_TYPE_BYTE, - SD_BUS_TYPE_BOOLEAN, - SD_BUS_TYPE_INT16, - SD_BUS_TYPE_UINT16, - SD_BUS_TYPE_INT32, - SD_BUS_TYPE_UINT32, - SD_BUS_TYPE_INT64, - SD_BUS_TYPE_UINT64, - SD_BUS_TYPE_DOUBLE, - SD_BUS_TYPE_STRING, - SD_BUS_TYPE_OBJECT_PATH, - SD_BUS_TYPE_SIGNATURE, - SD_BUS_TYPE_UNIX_FD - }; - - return !!memchr(valid, c, sizeof(valid)); -} - -bool bus_type_is_trivial(char c) { - static const char valid[] = { - SD_BUS_TYPE_BYTE, - SD_BUS_TYPE_BOOLEAN, - SD_BUS_TYPE_INT16, - SD_BUS_TYPE_UINT16, - SD_BUS_TYPE_INT32, - SD_BUS_TYPE_UINT32, - SD_BUS_TYPE_INT64, - SD_BUS_TYPE_UINT64, - SD_BUS_TYPE_DOUBLE - }; - - return !!memchr(valid, c, sizeof(valid)); -} - -bool bus_type_is_container(char c) { - static const char valid[] = { - SD_BUS_TYPE_ARRAY, - SD_BUS_TYPE_VARIANT, - SD_BUS_TYPE_STRUCT, - SD_BUS_TYPE_DICT_ENTRY - }; - - return !!memchr(valid, c, sizeof(valid)); -} - -int bus_type_get_alignment(char c) { - - switch (c) { - case SD_BUS_TYPE_BYTE: - case SD_BUS_TYPE_SIGNATURE: - case SD_BUS_TYPE_VARIANT: - return 1; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - return 2; - - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_ARRAY: - case SD_BUS_TYPE_UNIX_FD: - return 4; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - case SD_BUS_TYPE_STRUCT: - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: - return 8; - } - - return -EINVAL; -} - -int bus_type_get_size(char c) { - - switch (c) { - case SD_BUS_TYPE_BYTE: - return 1; - - case SD_BUS_TYPE_INT16: - case SD_BUS_TYPE_UINT16: - return 2; - - case SD_BUS_TYPE_BOOLEAN: - case SD_BUS_TYPE_INT32: - case SD_BUS_TYPE_UINT32: - case SD_BUS_TYPE_UNIX_FD: - return 4; - - case SD_BUS_TYPE_INT64: - case SD_BUS_TYPE_UINT64: - case SD_BUS_TYPE_DOUBLE: - return 8; - } - - return -EINVAL; -} diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h deleted file mode 100644 index 5c87eb5f08..0000000000 --- a/src/libsystemd/sd-bus/bus-type.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#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_; -/* "trivial" is systemd's term for what the D-Bus Specification calls - * a "fixed type": that is, a basic type of fixed length */ -bool bus_type_is_trivial(char c) _const_; -bool bus_type_is_container(char c) _const_; - -int bus_type_get_alignment(char c) _const_; -int bus_type_get_size(char c) _const_; diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c deleted file mode 100644 index b09509f8e1..0000000000 --- a/src/libsystemd/sd-bus/busctl-introspect.c +++ /dev/null @@ -1,790 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 - -typedef struct Context { - const XMLIntrospectOps *ops; - void *userdata; - - char *interface_name; - uint64_t interface_flags; - - char *member_name; - char *member_signature; - char *member_result; - uint64_t member_flags; - bool member_writable; - - const char *current; - void *xml_state; -} Context; - -static void context_reset_member(Context *c) { - free(c->member_name); - free(c->member_signature); - free(c->member_result); - - c->member_name = c->member_signature = c->member_result = NULL; - c->member_flags = 0; - c->member_writable = false; -} - -static void context_reset_interface(Context *c) { - c->interface_name = mfree(c->interface_name); - c->interface_flags = 0; - - context_reset_member(c); -} - -static int parse_xml_annotation(Context *context, uint64_t *flags) { - - enum { - STATE_ANNOTATION, - STATE_NAME, - STATE_VALUE - } state = STATE_ANNOTATION; - - _cleanup_free_ char *field = NULL, *value = NULL; - - assert(context); - - for (;;) { - _cleanup_free_ char *name = NULL; - - int t; - - t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); - if (t < 0) { - log_error("XML parse error."); - return t; - } - - if (t == XML_END) { - log_error("Premature end of XML data."); - return -EBADMSG; - } - - switch (state) { - - case STATE_ANNOTATION: - - if (t == XML_ATTRIBUTE_NAME) { - - if (streq_ptr(name, "name")) - state = STATE_NAME; - - else if (streq_ptr(name, "value")) - state = STATE_VALUE; - - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) { - - if (flags) { - if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) { - - if (streq_ptr(value, "true")) - *flags |= SD_BUS_VTABLE_DEPRECATED; - - } else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) { - - if (streq_ptr(value, "true")) - *flags |= SD_BUS_VTABLE_METHOD_NO_REPLY; - - } else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) { - - if (streq_ptr(value, "const")) - *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST; - else if (streq_ptr(value, "invalidates")) - *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION; - else if (streq_ptr(value, "false")) - *flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION); - } - } - - return 0; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - free(field); - field = name; - name = NULL; - - state = STATE_ANNOTATION; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_VALUE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(value); - value = name; - name = NULL; - - state = STATE_ANNOTATION; - } else { - log_error("Unexpected token in . (3)"); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Bad state"); - } - } -} - -static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) { - - enum { - STATE_NODE, - STATE_NODE_NAME, - STATE_INTERFACE, - STATE_INTERFACE_NAME, - STATE_METHOD, - STATE_METHOD_NAME, - STATE_METHOD_ARG, - STATE_METHOD_ARG_NAME, - STATE_METHOD_ARG_TYPE, - STATE_METHOD_ARG_DIRECTION, - STATE_SIGNAL, - STATE_SIGNAL_NAME, - STATE_SIGNAL_ARG, - STATE_SIGNAL_ARG_NAME, - STATE_SIGNAL_ARG_TYPE, - STATE_PROPERTY, - STATE_PROPERTY_NAME, - STATE_PROPERTY_TYPE, - STATE_PROPERTY_ACCESS, - } state = STATE_NODE; - - _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL; - const char *np = prefix; - int r; - - assert(context); - assert(prefix); - - if (n_depth > NODE_DEPTH_MAX) { - log_error(" depth too high."); - return -EINVAL; - } - - for (;;) { - _cleanup_free_ char *name = NULL; - int t; - - t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); - if (t < 0) { - log_error("XML parse error."); - return t; - } - - if (t == XML_END) { - log_error("Premature end of XML data."); - return -EBADMSG; - } - - switch (state) { - - case STATE_NODE: - if (t == XML_ATTRIBUTE_NAME) { - - if (streq_ptr(name, "name")) - state = STATE_NODE_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_OPEN) { - - if (streq_ptr(name, "interface")) - state = STATE_INTERFACE; - else if (streq_ptr(name, "node")) { - - r = parse_xml_node(context, np, n_depth+1); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) { - - if (context->ops->on_path) { - r = context->ops->on_path(node_path ? node_path : np, context->userdata); - if (r < 0) - return r; - } - - return 0; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_NODE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - free(node_path); - - if (name[0] == '/') { - node_path = name; - name = NULL; - } else { - - if (endswith(prefix, "/")) - node_path = strappend(prefix, name); - else - node_path = strjoin(prefix, "/", name, NULL); - if (!node_path) - return log_oom(); - } - - np = node_path; - state = STATE_NODE; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_INTERFACE: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_INTERFACE_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "method")) - state = STATE_METHOD; - else if (streq_ptr(name, "signal")) - state = STATE_SIGNAL; - else if (streq_ptr(name, "property")) { - context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE; - state = STATE_PROPERTY; - } else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->interface_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) { - - if (n_depth == 0) { - if (context->ops->on_interface) { - r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_interface(context); - } - - state = STATE_NODE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_INTERFACE_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - if (n_depth == 0) { - free(context->interface_name); - context->interface_name = name; - name = NULL; - } - - state = STATE_INTERFACE; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_METHOD: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_METHOD_NAME; - else { - log_error("Unexpected attribute %s", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "arg")) - state = STATE_METHOD_ARG; - else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) { - - if (n_depth == 0) { - if (context->ops->on_method) { - r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in (1)."); - return -EINVAL; - } - - break; - - case STATE_METHOD_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - - state = STATE_METHOD; - } else { - log_error("Unexpected token in (2)."); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_METHOD_ARG_NAME; - else if (streq_ptr(name, "type")) - state = STATE_METHOD_ARG_TYPE; - else if (streq_ptr(name, "direction")) - state = STATE_METHOD_ARG_DIRECTION; - else { - log_error("Unexpected method attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, NULL); - if (r < 0) - return r; - } else { - log_error("Unexpected method tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { - - if (n_depth == 0) { - - if (argument_type) { - if (!argument_direction || streq(argument_direction, "in")) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); - } else if (streq(argument_direction, "out")) { - if (!strextend(&context->member_result, argument_type, NULL)) - return log_oom(); - } - } - - argument_type = mfree(argument_type); - argument_direction = mfree(argument_direction); - } - - state = STATE_METHOD; - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in method . (1)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_NAME: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_METHOD_ARG; - else { - log_error("Unexpected token in method . (2)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; - - state = STATE_METHOD_ARG; - } else { - log_error("Unexpected token in method . (3)"); - return -EINVAL; - } - - break; - - case STATE_METHOD_ARG_DIRECTION: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_direction); - argument_direction = name; - name = NULL; - - state = STATE_METHOD_ARG; - } else { - log_error("Unexpected token in method . (4)"); - return -EINVAL; - } - - break; - - case STATE_SIGNAL: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_SIGNAL_NAME; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "arg")) - state = STATE_SIGNAL_ARG; - else if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) { - - if (n_depth == 0) { - if (context->ops->on_signal) { - r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - - state = STATE_SIGNAL; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - - case STATE_SIGNAL_ARG: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_SIGNAL_ARG_NAME; - else if (streq_ptr(name, "type")) - state = STATE_SIGNAL_ARG_TYPE; - else { - log_error("Unexpected signal attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, NULL); - if (r < 0) - return r; - } else { - log_error("Unexpected signal tag %s.", name); - return -EINVAL; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { - - if (argument_type) { - if (!strextend(&context->member_signature, argument_type, NULL)) - return log_oom(); - - argument_type = mfree(argument_type); - } - - state = STATE_SIGNAL; - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in signal (1)."); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_ARG_NAME: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_SIGNAL_ARG; - else { - log_error("Unexpected token in signal (2)."); - return -EINVAL; - } - - break; - - case STATE_SIGNAL_ARG_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - free(argument_type); - argument_type = name; - name = NULL; - - state = STATE_SIGNAL_ARG; - } else { - log_error("Unexpected token in signal (3)."); - return -EINVAL; - } - - break; - - case STATE_PROPERTY: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq_ptr(name, "name")) - state = STATE_PROPERTY_NAME; - else if (streq_ptr(name, "type")) - state = STATE_PROPERTY_TYPE; - else if (streq_ptr(name, "access")) - state = STATE_PROPERTY_ACCESS; - else { - log_error("Unexpected attribute %s.", name); - return -EBADMSG; - } - } else if (t == XML_TAG_OPEN) { - - if (streq_ptr(name, "annotation")) { - r = parse_xml_annotation(context, &context->member_flags); - if (r < 0) - return r; - } else { - log_error("Unexpected tag %s.", name); - return -EINVAL; - } - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) { - - if (n_depth == 0) { - if (context->ops->on_property) { - r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata); - if (r < 0) - return r; - } - - context_reset_member(context); - } - - state = STATE_INTERFACE; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token in . (1)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_name); - context->member_name = name; - name = NULL; - } - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (2)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (n_depth == 0) { - free(context->member_signature); - context->member_signature = name; - name = NULL; - } - - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (3)"); - return -EINVAL; - } - - break; - - case STATE_PROPERTY_ACCESS: - - if (t == XML_ATTRIBUTE_VALUE) { - - if (streq(name, "readwrite") || streq(name, "write")) - context->member_writable = true; - - state = STATE_PROPERTY; - } else { - log_error("Unexpected token in . (4)"); - return -EINVAL; - } - - break; - } - } -} - -int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) { - Context context = { - .ops = ops, - .userdata = userdata, - .current = xml, - }; - - int r; - - assert(prefix); - assert(xml); - assert(ops); - - for (;;) { - _cleanup_free_ char *name = NULL; - - r = xml_tokenize(&context.current, &name, &context.xml_state, NULL); - if (r < 0) { - log_error("XML parse error"); - goto finish; - } - - if (r == XML_END) { - r = 0; - break; - } - - if (r == XML_TAG_OPEN) { - - if (streq(name, "node")) { - r = parse_xml_node(&context, prefix, 0); - if (r < 0) - goto finish; - } else { - log_error("Unexpected tag '%s' in introspection data.", name); - r = -EBADMSG; - goto finish; - } - } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token."); - r = -EBADMSG; - goto finish; - } - } - -finish: - context_reset_interface(&context); - - return r; -} diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/libsystemd/sd-bus/busctl-introspect.h deleted file mode 100644 index d922e352db..0000000000 --- a/src/libsystemd/sd-bus/busctl-introspect.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -typedef struct XMLIntrospectOps { - int (*on_path)(const char *path, void *userdata); - int (*on_interface)(const char *name, uint64_t flags, void *userdata); - int (*on_method)(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata); - int (*on_signal)(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata); - int (*on_property)(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata); -} XMLIntrospectOps; - -int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata); diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c deleted file mode 100644 index eb042e9c81..0000000000 --- a/src/libsystemd/sd-bus/busctl.c +++ /dev/null @@ -1,2087 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.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; -static char *arg_address = NULL; -static bool arg_unique = false; -static bool arg_acquired = false; -static bool arg_activatable = false; -static bool arg_show_machine = false; -static char **arg_matches = NULL; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_user = false; -static size_t arg_snaplen = 4096; -static bool arg_list = false; -static bool arg_quiet = false; -static bool arg_verbose = false; -static bool arg_expect_reply = true; -static bool arg_auto_start = true; -static bool arg_allow_interactive_authorization = true; -static bool arg_augment_creds = true; -static usec_t arg_timeout = 0; - -#define NAME_IS_ACQUIRED INT_TO_PTR(1) -#define NAME_IS_ACTIVATABLE INT_TO_PTR(2) - -static int list_bus_names(sd_bus *bus, char **argv) { - _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL; - _cleanup_free_ char **merged = NULL; - _cleanup_hashmap_free_ Hashmap *names = NULL; - char **i; - int r; - size_t max_i = 0; - unsigned n = 0; - void *v; - char *k; - Iterator iterator; - - assert(bus); - - if (!arg_unique && !arg_acquired && !arg_activatable) - arg_unique = arg_acquired = arg_activatable = true; - - r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); - if (r < 0) - return log_error_errno(r, "Failed to list names: %m"); - - pager_open(arg_no_pager, false); - - names = hashmap_new(&string_hash_ops); - if (!names) - return log_oom(); - - STRV_FOREACH(i, acquired) { - max_i = MAX(max_i, strlen(*i)); - - r = hashmap_put(names, *i, NAME_IS_ACQUIRED); - if (r < 0) - return log_error_errno(r, "Failed to add to hashmap: %m"); - } - - STRV_FOREACH(i, activatable) { - max_i = MAX(max_i, strlen(*i)); - - r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to add to hashmap: %m"); - } - - merged = new(char*, hashmap_size(names) + 1); - HASHMAP_FOREACH_KEY(v, k, names, iterator) - merged[n++] = k; - - merged[n] = NULL; - strv_sort(merged); - - if (arg_legend) { - printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s", - (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION"); - - if (arg_show_machine) - puts(" MACHINE"); - else - putchar('\n'); - } - - STRV_FOREACH(i, merged) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_id128_t mid; - - if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) { - /* Activatable */ - - printf("%-*s", (int) max_i, *i); - printf(" - - - (activatable) - - "); - if (arg_show_machine) - puts(" -"); - else - putchar('\n'); - continue; - - } - - if (!arg_unique && (*i)[0] == ':') - continue; - - if (!arg_acquired && (*i)[0] != ':') - continue; - - printf("%-*s", (int) max_i, *i); - - r = sd_bus_get_name_creds( - bus, *i, - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | - SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| - SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| - SD_BUS_CREDS_DESCRIPTION, &creds); - if (r >= 0) { - const char *unique, *session, *unit, *cn; - pid_t pid; - uid_t uid; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r >= 0) { - const char *comm = NULL; - - sd_bus_creds_get_comm(creds, &comm); - - printf(" %10lu %-15s", (unsigned long) pid, strna(comm)); - } else - fputs(" - - ", stdout); - - r = sd_bus_creds_get_euid(creds, &uid); - if (r >= 0) { - _cleanup_free_ char *u = NULL; - - u = uid_to_name(uid); - if (!u) - return log_oom(); - - if (strlen(u) > 16) - u[16] = 0; - - printf(" %-16s", u); - } else - fputs(" - ", stdout); - - r = sd_bus_creds_get_unique_name(creds, &unique); - if (r >= 0) - printf(" %-13s", unique); - else - fputs(" - ", stdout); - - r = sd_bus_creds_get_unit(creds, &unit); - if (r >= 0) { - _cleanup_free_ char *e; - - e = ellipsize(unit, 25, 100); - if (!e) - return log_oom(); - - printf(" %-25s", e); - } else - fputs(" - ", stdout); - - r = sd_bus_creds_get_session(creds, &session); - if (r >= 0) - printf(" %-10s", session); - else - fputs(" - ", stdout); - - r = sd_bus_creds_get_description(creds, &cn); - if (r >= 0) - printf(" %-19s", cn); - else - fputs(" - ", stdout); - - } else - printf(" - - - - - - - "); - - if (arg_show_machine) { - r = sd_bus_get_name_machine_id(bus, *i, &mid); - if (r >= 0) { - char m[SD_ID128_STRING_MAX]; - printf(" %s\n", sd_id128_to_string(mid, m)); - } else - puts(" -"); - } else - putchar('\n'); - } - - return 0; -} - -static void print_subtree(const char *prefix, const char *path, char **l) { - const char *vertical, *space; - char **n; - - /* We assume the list is sorted. Let's first skip over the - * entry we are looking at. */ - for (;;) { - if (!*l) - return; - - if (!streq(*l, path)) - break; - - l++; - } - - vertical = strjoina(prefix, special_glyph(TREE_VERTICAL)); - space = strjoina(prefix, special_glyph(TREE_SPACE)); - - for (;;) { - bool has_more = false; - - if (!*l || !path_startswith(*l, path)) - break; - - n = l + 1; - for (;;) { - if (!*n || !path_startswith(*n, path)) - break; - - if (!path_startswith(*n, *l)) { - has_more = true; - break; - } - - n++; - } - - printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l); - - print_subtree(has_more ? vertical : space, *l, l); - l = n; - } -} - -static void print_tree(const char *prefix, char **l) { - - pager_open(arg_no_pager, false); - - prefix = strempty(prefix); - - if (arg_list) { - char **i; - - STRV_FOREACH(i, l) - printf("%s%s\n", prefix, *i); - return; - } - - if (strv_isempty(l)) { - printf("No objects discovered.\n"); - return; - } - - if (streq(l[0], "/") && !l[1]) { - printf("Only root object discovered.\n"); - return; - } - - print_subtree(prefix, "/", l); -} - -static int on_path(const char *path, void *userdata) { - Set *paths = userdata; - int r; - - assert(paths); - - r = set_put_strdup(paths, path); - if (r < 0) - return log_oom(); - - return 0; -} - -static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) { - static const XMLIntrospectOps ops = { - .on_path = on_path, - }; - - _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; - - r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - if (r < 0) { - if (many) - printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r)); - else - log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &xml); - if (r < 0) - return bus_log_parse_error(r); - - return parse_xml_introspect(path, xml, &ops, paths); -} - -static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) { - _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL; - _cleanup_free_ char **l = NULL; - char *m; - int r; - - paths = set_new(&string_hash_ops); - if (!paths) - return log_oom(); - - done = set_new(&string_hash_ops); - if (!done) - return log_oom(); - - failed = set_new(&string_hash_ops); - if (!failed) - return log_oom(); - - m = strdup("/"); - if (!m) - return log_oom(); - - r = set_put(paths, m); - if (r < 0) { - free(m); - return log_oom(); - } - - for (;;) { - _cleanup_free_ char *p = NULL; - int q; - - p = set_steal_first(paths); - if (!p) - break; - - if (set_contains(done, p) || - set_contains(failed, p)) - continue; - - q = find_nodes(bus, service, p, paths, many); - if (q < 0) { - if (r >= 0) - r = q; - - q = set_put(failed, p); - } else - q = set_put(done, p); - - if (q < 0) - return log_oom(); - - assert(q != 0); - p = NULL; - } - - pager_open(arg_no_pager, false); - - l = set_get_strv(done); - if (!l) - return log_oom(); - - strv_sort(l); - print_tree(prefix, l); - - fflush(stdout); - - return r; -} - -static int tree(sd_bus *bus, char **argv) { - char **i; - int r = 0; - - if (!arg_unique && !arg_acquired) - arg_acquired = true; - - if (strv_length(argv) <= 1) { - _cleanup_strv_free_ char **names = NULL; - bool not_first = false; - - r = sd_bus_list_names(bus, &names, NULL); - if (r < 0) - return log_error_errno(r, "Failed to get name list: %m"); - - pager_open(arg_no_pager, false); - - STRV_FOREACH(i, names) { - int q; - - if (!arg_unique && (*i)[0] == ':') - continue; - - if (!arg_acquired && (*i)[0] == ':') - continue; - - if (not_first) - printf("\n"); - - printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); - - q = tree_one(bus, *i, NULL, true); - if (q < 0 && r >= 0) - r = q; - - not_first = true; - } - } else { - STRV_FOREACH(i, argv+1) { - int q; - - if (i > argv+1) - printf("\n"); - - if (argv[2]) { - pager_open(arg_no_pager, false); - printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); - } - - q = tree_one(bus, *i, NULL, !!argv[2]); - if (q < 0 && r >= 0) - r = q; - } - } - - return r; -} - -static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { - int r; - - for (;;) { - const char *contents = NULL; - char type; - union { - uint8_t u8; - uint16_t u16; - int16_t s16; - uint32_t u32; - int32_t s32; - uint64_t u64; - int64_t s64; - double d64; - const char *string; - int i; - } basic; - - r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) - return r; - if (r == 0) - return needs_space; - - if (bus_type_is_container(type) > 0) { - - r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) - return r; - - if (type == SD_BUS_TYPE_ARRAY) { - unsigned n = 0; - - /* count array entries */ - for (;;) { - - r = sd_bus_message_skip(m, contents); - if (r < 0) - return r; - if (r == 0) - break; - - n++; - } - - r = sd_bus_message_rewind(m, false); - if (r < 0) - return r; - - if (needs_space) - fputc(' ', f); - - fprintf(f, "%u", n); - needs_space = true; - - } else if (type == SD_BUS_TYPE_VARIANT) { - - if (needs_space) - fputc(' ', f); - - fprintf(f, "%s", contents); - needs_space = true; - } - - r = format_cmdline(m, f, needs_space); - if (r < 0) - return r; - - needs_space = r > 0; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - continue; - } - - r = sd_bus_message_read_basic(m, type, &basic); - if (r < 0) - return r; - - if (needs_space) - fputc(' ', f); - - switch (type) { - case SD_BUS_TYPE_BYTE: - fprintf(f, "%u", basic.u8); - break; - - case SD_BUS_TYPE_BOOLEAN: - fputs(true_false(basic.i), f); - break; - - case SD_BUS_TYPE_INT16: - fprintf(f, "%i", basic.s16); - break; - - case SD_BUS_TYPE_UINT16: - fprintf(f, "%u", basic.u16); - break; - - case SD_BUS_TYPE_INT32: - fprintf(f, "%i", basic.s32); - break; - - case SD_BUS_TYPE_UINT32: - fprintf(f, "%u", basic.u32); - break; - - case SD_BUS_TYPE_INT64: - fprintf(f, "%" PRIi64, basic.s64); - break; - - case SD_BUS_TYPE_UINT64: - fprintf(f, "%" PRIu64, basic.u64); - break; - - case SD_BUS_TYPE_DOUBLE: - fprintf(f, "%g", basic.d64); - break; - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: { - _cleanup_free_ char *b = NULL; - - b = cescape(basic.string); - if (!b) - return -ENOMEM; - - fprintf(f, "\"%s\"", b); - break; - } - - case SD_BUS_TYPE_UNIX_FD: - fprintf(f, "%i", basic.i); - break; - - default: - assert_not_reached("Unknown basic type."); - } - - needs_space = true; - } -} - -typedef struct Member { - const char *type; - char *interface; - char *name; - char *signature; - char *result; - char *value; - bool writable; - uint64_t flags; -} Member; - -static void member_hash_func(const void *p, struct siphash *state) { - const Member *m = p; - uint64_t arity = 1; - - assert(m); - assert(m->type); - - string_hash_func(m->type, state); - - arity += !!m->name + !!m->interface; - - uint64_hash_func(&arity, state); - - if (m->name) - string_hash_func(m->name, state); - - if (m->interface) - string_hash_func(m->interface, state); -} - -static int member_compare_func(const void *a, const void *b) { - const Member *x = a, *y = b; - int d; - - assert(x); - assert(y); - assert(x->type); - assert(y->type); - - d = strcmp_ptr(x->interface, y->interface); - if (d != 0) - return d; - - d = strcmp(x->type, y->type); - if (d != 0) - return d; - - return strcmp_ptr(x->name, y->name); -} - -static int member_compare_funcp(const void *a, const void *b) { - const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b; - - return member_compare_func(*x, *y); -} - -static void member_free(Member *m) { - if (!m) - return; - - free(m->interface); - free(m->name); - free(m->signature); - free(m->result); - free(m->value); - free(m); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free); - -static void member_set_free(Set *s) { - Member *m; - - while ((m = set_steal_first(s))) - member_free(m); - - set_free(s); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free); - -static int on_interface(const char *interface, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(members); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "interface"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate interface"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "method"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->result, result); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate method"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "signal"; - m->flags = flags; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate signal"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) { - _cleanup_(member_freep) Member *m; - Set *members = userdata; - int r; - - assert(interface); - assert(name); - - m = new0(Member, 1); - if (!m) - return log_oom(); - - m->type = "property"; - m->flags = flags; - m->writable = writable; - - r = free_and_strdup(&m->interface, interface); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->name, name); - if (r < 0) - return log_oom(); - - r = free_and_strdup(&m->signature, signature); - if (r < 0) - return log_oom(); - - r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate property"); - return -EINVAL; - } - - m = NULL; - return 0; -} - -static const char *strdash(const char *x) { - return isempty(x) ? "-" : x; -} - -static int introspect(sd_bus *bus, char **argv) { - static const struct hash_ops member_hash_ops = { - .hash = member_hash_func, - .compare = member_compare_func, - }; - - static const XMLIntrospectOps ops = { - .on_interface = on_interface, - .on_method = on_method, - .on_signal = on_signal, - .on_property = on_property, - }; - - _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; - const char *xml; - int r; - unsigned name_width, type_width, signature_width, result_width; - Member **sorted = NULL; - unsigned k = 0, j, n_args; - - n_args = strv_length(argv); - if (n_args < 3) { - log_error("Requires service and object path argument."); - return -EINVAL; - } - - if (n_args > 4) { - log_error("Too many arguments."); - return -EINVAL; - } - - members = set_new(&member_hash_ops); - if (!members) - return log_oom(); - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - if (r < 0) { - log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_read(reply, "s", &xml); - if (r < 0) - return bus_log_parse_error(r); - - /* First, get list of all properties */ - r = parse_xml_introspect(argv[2], xml, &ops, members); - if (r < 0) - return r; - - /* Second, find the current values for them */ - SET_FOREACH(m, members, i) { - - if (!streq(m->type, "property")) - continue; - - if (m->value) - continue; - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "{sv}"); - if (r < 0) - return bus_log_parse_error(r); - - for (;;) { - Member *z; - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *mf = NULL; - size_t sz = 0; - const char *name; - - r = sd_bus_message_enter_container(reply, 'e', "sv"); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0) - break; - - r = sd_bus_message_read(reply, "s", &name); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'v', NULL); - if (r < 0) - return bus_log_parse_error(r); - - mf = open_memstream(&buf, &sz); - if (!mf) - return log_oom(); - - r = format_cmdline(reply, mf, false); - if (r < 0) - return bus_log_parse_error(r); - - fclose(mf); - mf = NULL; - - z = set_get(members, &((Member) { - .type = "property", - .interface = m->interface, - .name = (char*) name })); - if (z) { - free(z->value); - z->value = buf; - buf = NULL; - } - - r = sd_bus_message_exit_container(reply); - 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_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - } - - pager_open(arg_no_pager, false); - - name_width = strlen("NAME"); - type_width = strlen("TYPE"); - signature_width = strlen("SIGNATURE"); - result_width = strlen("RESULT/VALUE"); - - sorted = newa(Member*, set_size(members)); - - SET_FOREACH(m, members, i) { - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - if (m->interface) - name_width = MAX(name_width, strlen(m->interface)); - if (m->name) - name_width = MAX(name_width, strlen(m->name) + 1); - if (m->type) - type_width = MAX(type_width, strlen(m->type)); - if (m->signature) - signature_width = MAX(signature_width, strlen(m->signature)); - if (m->result) - result_width = MAX(result_width, strlen(m->result)); - if (m->value) - result_width = MAX(result_width, strlen(m->value)); - - sorted[k++] = m; - } - - if (result_width > 40) - result_width = 40; - - qsort(sorted, k, sizeof(Member*), member_compare_funcp); - - if (arg_legend) { - printf("%-*s %-*s %-*s %-*s %s\n", - (int) name_width, "NAME", - (int) type_width, "TYPE", - (int) signature_width, "SIGNATURE", - (int) result_width, "RESULT/VALUE", - "FLAGS"); - } - - for (j = 0; j < k; j++) { - _cleanup_free_ char *ellipsized = NULL; - const char *rv; - bool is_interface; - - m = sorted[j]; - - if (argv[3] && !streq(argv[3], m->interface)) - continue; - - is_interface = streq(m->type, "interface"); - - if (argv[3] && is_interface) - continue; - - if (m->value) { - ellipsized = ellipsize(m->value, result_width, 100); - if (!ellipsized) - return log_oom(); - - rv = ellipsized; - } else - rv = strdash(m->result); - - printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", - is_interface ? ansi_highlight() : "", - is_interface ? "" : ".", - - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name), - is_interface ? ansi_normal() : "", - (int) type_width, strdash(m->type), - (int) signature_width, strdash(m->signature), - (int) result_width, rv, - (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"), - (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "", - (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "", - m->writable ? " writable" : ""); - } - - return 0; -} - -static int message_dump(sd_bus_message *m, FILE *f) { - return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER); -} - -static int message_pcap(sd_bus_message *m, FILE *f) { - return bus_message_pcap_frame(m, arg_snaplen, f); -} - -static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char **i; - uint32_t flags = 0; - int r; - - /* upgrade connection; it's not used for anything else after this call */ - r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(message, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - STRV_FOREACH(i, argv+1) { - _cleanup_free_ char *m = NULL; - - if (!service_name_is_valid(*i)) { - log_error("Invalid service name '%s'", *i); - return -EINVAL; - } - - m = strjoin("sender='", *i, "'", NULL); - if (!m) - return log_oom(); - - r = sd_bus_message_append_basic(message, 's', m); - if (r < 0) - return bus_log_create_error(r); - - free(m); - m = strjoin("destination='", *i, "'", NULL); - if (!m) - return log_oom(); - - r = sd_bus_message_append_basic(message, 's', m); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH(i, arg_matches) { - r = sd_bus_message_append_basic(message, 's', *i); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(message); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(message, 'u', &flags); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, message, arg_timeout, &error, NULL); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - log_info("Monitoring bus message stream."); - - for (;;) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) - return log_error_errno(r, "Failed to process bus: %m"); - - if (m) { - dump(m, stdout); - fflush(stdout); - - if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) { - log_info("Connection terminated, exiting."); - return 0; - } - - continue; - } - - if (r > 0) - continue; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return log_error_errno(r, "Failed to wait for bus: %m"); - } -} - -static int capture(sd_bus *bus, char *argv[]) { - int r; - - if (isatty(fileno(stdout)) > 0) { - log_error("Refusing to write message data to console, please redirect output to a file."); - return -EINVAL; - } - - bus_pcap_header(arg_snaplen, stdout); - - r = monitor(bus, argv, message_pcap); - if (r < 0) - return r; - - if (ferror(stdout)) { - log_error("Couldn't write capture file."); - return -EIO; - } - - return r; -} - -static int status(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - pid_t pid; - int r; - - assert(bus); - - if (strv_length(argv) > 2) { - log_error("Expects no or one argument."); - return -EINVAL; - } - - if (argv[1]) { - r = parse_pid(argv[1], &pid); - if (r < 0) - r = sd_bus_get_name_creds( - bus, - argv[1], - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, - &creds); - else - r = sd_bus_creds_new_from_pid( - &creds, - pid, - _SD_BUS_CREDS_ALL); - } else { - const char *scope, *address; - sd_id128_t bus_id; - - r = sd_bus_get_address(bus, &address); - if (r >= 0) - printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal()); - - r = sd_bus_get_scope(bus, &scope); - if (r >= 0) - printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal()); - - r = sd_bus_get_bus_id(bus, &bus_id); - if (r >= 0) - printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal()); - - r = sd_bus_get_owner_creds( - bus, - (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, - &creds); - } - - if (r < 0) - return log_error_errno(r, "Failed to get credentials: %m"); - - bus_creds_dump(creds, NULL, false); - return 0; -} - -static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) { - char **p; - int r; - - assert(m); - assert(signature); - assert(x); - - p = *x; - - for (;;) { - const char *v; - char t; - - t = *signature; - v = *p; - - if (t == 0) - break; - if (!v) { - log_error("Too few parameters for signature."); - return -EINVAL; - } - - signature++; - p++; - - switch (t) { - - case SD_BUS_TYPE_BOOLEAN: - - r = parse_boolean(v); - if (r < 0) { - log_error("Failed to parse as boolean: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &r); - break; - - case SD_BUS_TYPE_BYTE: { - uint8_t z; - - r = safe_atou8(v, &z); - if (r < 0) { - log_error("Failed to parse as byte (unsigned 8bit integer): %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT16: { - int16_t z; - - r = safe_atoi16(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 16bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT16: { - uint16_t z; - - r = safe_atou16(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 16bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT32: { - int32_t z; - - r = safe_atoi32(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 32bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t z; - - r = safe_atou32(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 32bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_INT64: { - int64_t z; - - r = safe_atoi64(v, &z); - if (r < 0) { - log_error("Failed to parse as signed 64bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t z; - - r = safe_atou64(v, &z); - if (r < 0) { - log_error("Failed to parse as unsigned 64bit integer: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - - case SD_BUS_TYPE_DOUBLE: { - double z; - - r = safe_atod(v, &z); - if (r < 0) { - log_error("Failed to parse as double precision floating point: %s", v); - return r; - } - - r = sd_bus_message_append_basic(m, t, &z); - break; - } - - case SD_BUS_TYPE_STRING: - case SD_BUS_TYPE_OBJECT_PATH: - case SD_BUS_TYPE_SIGNATURE: - - r = sd_bus_message_append_basic(m, t, v); - break; - - case SD_BUS_TYPE_ARRAY: { - uint32_t n; - size_t k; - - r = safe_atou32(v, &n); - if (r < 0) { - log_error("Failed to parse number of array entries: %s", v); - return r; - } - - r = signature_element_length(signature, &k); - if (r < 0) { - log_error("Invalid array signature."); - return r; - } - - { - unsigned i; - char s[k + 1]; - memcpy(s, signature, k); - s[k] = 0; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); - if (r < 0) - return bus_log_create_error(r); - - for (i = 0; i < n; i++) { - r = message_append_cmdline(m, s, &p); - if (r < 0) - return r; - } - } - - signature += k; - - r = sd_bus_message_close_container(m); - break; - } - - case SD_BUS_TYPE_VARIANT: - r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v); - if (r < 0) - return bus_log_create_error(r); - - r = message_append_cmdline(m, v, &p); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - break; - - case SD_BUS_TYPE_STRUCT_BEGIN: - case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { - size_t k; - - signature--; - p--; - - r = signature_element_length(signature, &k); - if (r < 0) { - log_error("Invalid struct/dict entry signature."); - return r; - } - - { - char s[k-1]; - memcpy(s, signature + 1, k - 2); - s[k - 2] = 0; - - r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); - if (r < 0) - return bus_log_create_error(r); - - r = message_append_cmdline(m, s, &p); - if (r < 0) - return r; - } - - signature += k; - - r = sd_bus_message_close_container(m); - break; - } - - case SD_BUS_TYPE_UNIX_FD: - log_error("UNIX file descriptor not supported as type."); - return -EINVAL; - - default: - log_error("Unknown signature type %c.", t); - return -EINVAL; - } - - if (r < 0) - return bus_log_create_error(r); - } - - *x = p; - return 0; -} - -static int call(sd_bus *bus, char *argv[]) { - _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); - - if (strv_length(argv) < 5) { - log_error("Expects at least four arguments."); - return -EINVAL; - } - - r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_expect_reply(m, arg_expect_reply); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_auto_start(m, arg_auto_start); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization); - if (r < 0) - return bus_log_create_error(r); - - if (!isempty(argv[5])) { - char **p; - - p = argv+6; - - r = message_append_cmdline(m, argv[5], &p); - if (r < 0) - return r; - - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } - } - - if (!arg_expect_reply) { - r = sd_bus_send(bus, m, NULL); - if (r < 0) { - log_error("Failed to send message."); - return r; - } - - return 0; - } - - r = sd_bus_call(bus, m, arg_timeout, &error, &reply); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_is_empty(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0 && !arg_quiet) { - - if (arg_verbose) { - pager_open(arg_no_pager, false); - - r = bus_message_dump(reply, stdout, 0); - if (r < 0) - return r; - } else { - - fputs(sd_bus_message_get_signature(reply, true), stdout); - fputc(' ', stdout); - - r = format_cmdline(reply, stdout, false); - if (r < 0) - return bus_log_parse_error(r); - - fputc('\n', stdout); - } - } - - return 0; -} - -static int get_property(sd_bus *bus, char *argv[]) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - unsigned n; - char **i; - int r; - - assert(bus); - - n = strv_length(argv); - if (n < 5) { - log_error("Expects at least four arguments."); - return -EINVAL; - } - - STRV_FOREACH(i, argv + 4) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *contents = NULL; - char type; - - r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_peek_type(reply, &type, &contents); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, 'v', contents); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_verbose) { - pager_open(arg_no_pager, false); - - r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); - if (r < 0) - return r; - } else { - fputs(contents, stdout); - fputc(' ', stdout); - - r = format_cmdline(reply, stdout, false); - if (r < 0) - return bus_log_parse_error(r); - - fputc('\n', stdout); - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - } - - return 0; -} - -static int set_property(sd_bus *bus, char *argv[]) { - _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; - - assert(bus); - - n = strv_length(argv); - if (n < 6) { - log_error("Expects at least five arguments."); - return -EINVAL; - } - - r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "ss", argv[3], argv[4]); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', argv[5]); - if (r < 0) - return bus_log_create_error(r); - - p = argv+6; - r = message_append_cmdline(m, argv[5], &p); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } - - r = sd_bus_call(bus, m, arg_timeout, &error, NULL); - if (r < 0) { - log_error("%s", bus_error_message(&error, r)); - return r; - } - - return 0; -} - -static int help(void) { - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Introspect the bus.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --system Connect to system bus\n" - " --user Connect to user bus\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --address=ADDRESS Connect to bus specified by address\n" - " --show-machine Show machine ID column in list\n" - " --unique Only show unique names\n" - " --acquired Only show acquired names\n" - " --activatable Only show activatable names\n" - " --match=MATCH Only show matching messages\n" - " --size=SIZE Maximum length of captured packet\n" - " --list Don't show tree, but simple object path list\n" - " --quiet Don't show method call reply\n" - " --verbose Show result values in long format\n" - " --expect-reply=BOOL Expect a method call reply\n" - " --auto-start=BOOL Auto-start destination service\n" - " --allow-interactive-authorization=BOOL\n" - " Allow interactive authorization for operation\n" - " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" - "Commands:\n" - " list List bus names\n" - " status [SERVICE] Show bus service, process or bus owner credentials\n" - " monitor [SERVICE...] Show bus traffic\n" - " capture [SERVICE...] Capture bus traffic as pcap\n" - " tree [SERVICE...] Show object tree of service\n" - " introspect SERVICE OBJECT [INTERFACE]\n" - " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" - " Call a method\n" - " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" - " Get property value\n" - " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" - " Set property value\n" - " help Show this help\n" - , program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_SYSTEM, - ARG_USER, - ARG_ADDRESS, - ARG_MATCH, - ARG_SHOW_MACHINE, - ARG_UNIQUE, - ARG_ACQUIRED, - ARG_ACTIVATABLE, - ARG_SIZE, - ARG_LIST, - ARG_VERBOSE, - ARG_EXPECT_REPLY, - ARG_AUTO_START, - ARG_ALLOW_INTERACTIVE_AUTHORIZATION, - ARG_TIMEOUT, - ARG_AUGMENT_CREDS, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "address", required_argument, NULL, ARG_ADDRESS }, - { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE }, - { "unique", no_argument, NULL, ARG_UNIQUE }, - { "acquired", no_argument, NULL, ARG_ACQUIRED }, - { "activatable", no_argument, NULL, ARG_ACTIVATABLE }, - { "match", required_argument, NULL, ARG_MATCH }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "size", required_argument, NULL, ARG_SIZE }, - { "list", no_argument, NULL, ARG_LIST }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, ARG_VERBOSE }, - { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY }, - { "auto-start", required_argument, NULL, ARG_AUTO_START }, - { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, - {}, - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(); - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_legend = false; - break; - - case ARG_USER: - arg_user = true; - break; - - case ARG_SYSTEM: - arg_user = false; - break; - - case ARG_ADDRESS: - arg_address = optarg; - break; - - case ARG_SHOW_MACHINE: - arg_show_machine = true; - break; - - case ARG_UNIQUE: - arg_unique = true; - break; - - case ARG_ACQUIRED: - arg_acquired = true; - break; - - case ARG_ACTIVATABLE: - arg_activatable = true; - break; - - case ARG_MATCH: - if (strv_extend(&arg_matches, optarg) < 0) - return log_oom(); - break; - - case ARG_SIZE: { - uint64_t sz; - - r = parse_size(optarg, 1024, &sz); - if (r < 0) { - log_error("Failed to parse size: %s", optarg); - return r; - } - - if ((uint64_t) (size_t) sz != sz) { - log_error("Size out of range."); - return -E2BIG; - } - - arg_snaplen = (size_t) sz; - break; - } - - case ARG_LIST: - arg_list = true; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case 'q': - arg_quiet = true; - break; - - case ARG_VERBOSE: - arg_verbose = true; - break; - - case ARG_EXPECT_REPLY: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --expect-reply= parameter."); - return r; - } - - arg_expect_reply = !!r; - break; - - - case ARG_AUTO_START: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --auto-start= parameter."); - return r; - } - - arg_auto_start = !!r; - break; - - - case ARG_ALLOW_INTERACTIVE_AUTHORIZATION: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --allow-interactive-authorization= parameter."); - return r; - } - - arg_allow_interactive_authorization = !!r; - break; - - case ARG_TIMEOUT: - r = parse_sec(optarg, &arg_timeout); - if (r < 0) { - log_error("Failed to parse --timeout= parameter."); - return r; - } - - break; - - case ARG_AUGMENT_CREDS: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --augment-creds= parameter."); - return r; - } - - arg_augment_creds = !!r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int busctl_main(sd_bus *bus, int argc, char *argv[]) { - assert(bus); - - if (optind >= argc || - streq(argv[optind], "list")) - return list_bus_names(bus, argv + optind); - - if (streq(argv[optind], "monitor")) - return monitor(bus, argv + optind, message_dump); - - if (streq(argv[optind], "capture")) - return capture(bus, argv + optind); - - if (streq(argv[optind], "status")) - return status(bus, argv + optind); - - if (streq(argv[optind], "tree")) - return tree(bus, argv + optind); - - if (streq(argv[optind], "introspect")) - return introspect(bus, argv + optind); - - if (streq(argv[optind], "call")) - return call(bus, argv + optind); - - if (streq(argv[optind], "get-property")) - return get_property(bus, argv + optind); - - if (streq(argv[optind], "set-property")) - return set_property(bus, argv + optind); - - if (streq(argv[optind], "help")) - return help(); - - log_error("Unknown command '%s'", argv[optind]); - return -EINVAL; -} - -int main(int argc, char *argv[]) { - sd_bus *bus = NULL; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_bus_new(&bus); - if (r < 0) { - log_error_errno(r, "Failed to allocate bus: %m"); - goto finish; - } - - if (streq_ptr(argv[optind], "monitor") || - streq_ptr(argv[optind], "capture")) { - - r = sd_bus_set_monitor(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to set monitor mode: %m"); - goto finish; - } - - r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL); - if (r < 0) { - log_error_errno(r, "Failed to enable credentials: %m"); - goto finish; - } - - r = sd_bus_negotiate_timestamp(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to enable timestamps: %m"); - goto finish; - } - - r = sd_bus_negotiate_fds(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to enable fds: %m"); - goto finish; - } - } - - r = sd_bus_set_bus_client(bus, true); - if (r < 0) { - log_error_errno(r, "Failed to set bus client: %m"); - goto finish; - } - - if (arg_address) - r = sd_bus_set_address(bus, arg_address); - else { - switch (arg_transport) { - - case BUS_TRANSPORT_LOCAL: - if (arg_user) { - bus->is_user = true; - r = bus_set_address_user(bus); - } else { - bus->is_system = true; - r = bus_set_address_system(bus); - } - break; - - case BUS_TRANSPORT_REMOTE: - r = bus_set_address_system_remote(bus, arg_host); - break; - - case BUS_TRANSPORT_MACHINE: - r = bus_set_address_system_machine(bus, arg_host); - break; - - default: - assert_not_reached("Hmm, unknown transport type."); - } - } - if (r < 0) { - log_error_errno(r, "Failed to set address: %m"); - goto finish; - } - - r = sd_bus_start(bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to bus: %m"); - goto finish; - } - - r = busctl_main(bus, argc, argv); - -finish: - sd_bus_flush_close_unref(bus); - pager_close(); - - strv_free(arg_matches); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/kdbus.h b/src/libsystemd/sd-bus/kdbus.h deleted file mode 100644 index ecffc6b13c..0000000000 --- a/src/libsystemd/sd-bus/kdbus.h +++ /dev/null @@ -1,980 +0,0 @@ -/* - * kdbus is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - */ - -#ifndef _UAPI_KDBUS_H_ -#define _UAPI_KDBUS_H_ - -#include -#include - -#define KDBUS_IOCTL_MAGIC 0x95 -#define KDBUS_SRC_ID_KERNEL (0) -#define KDBUS_DST_ID_NAME (0) -#define KDBUS_MATCH_ID_ANY (~0ULL) -#define KDBUS_DST_ID_BROADCAST (~0ULL) -#define KDBUS_FLAG_NEGOTIATE (1ULL << 63) - -/** - * struct kdbus_notify_id_change - name registry change message - * @id: New or former owner of the name - * @flags: flags field from KDBUS_HELLO_* - * - * Sent from kernel to userspace when the owner or activator of - * a well-known name changes. - * - * Attached to: - * KDBUS_ITEM_ID_ADD - * KDBUS_ITEM_ID_REMOVE - */ -struct kdbus_notify_id_change { - __u64 id; - __u64 flags; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_notify_name_change - name registry change message - * @old_id: ID and flags of former owner of a name - * @new_id: ID and flags of new owner of a name - * @name: Well-known name - * - * Sent from kernel to userspace when the owner or activator of - * a well-known name changes. - * - * Attached to: - * KDBUS_ITEM_NAME_ADD - * KDBUS_ITEM_NAME_REMOVE - * KDBUS_ITEM_NAME_CHANGE - */ -struct kdbus_notify_name_change { - struct kdbus_notify_id_change old_id; - struct kdbus_notify_id_change new_id; - char name[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_creds - process credentials - * @uid: User ID - * @euid: Effective UID - * @suid: Saved UID - * @fsuid: Filesystem UID - * @gid: Group ID - * @egid: Effective GID - * @sgid: Saved GID - * @fsgid: Filesystem GID - * - * Attached to: - * KDBUS_ITEM_CREDS - */ -struct kdbus_creds { - __u64 uid; - __u64 euid; - __u64 suid; - __u64 fsuid; - __u64 gid; - __u64 egid; - __u64 sgid; - __u64 fsgid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_pids - process identifiers - * @pid: Process ID - * @tid: Thread ID - * @ppid: Parent process ID - * - * The PID and TID of a process. - * - * Attached to: - * KDBUS_ITEM_PIDS - */ -struct kdbus_pids { - __u64 pid; - __u64 tid; - __u64 ppid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_caps - process capabilities - * @last_cap: Highest currently known capability bit - * @caps: Variable number of 32-bit capabilities flags - * - * Contains a variable number of 32-bit capabilities flags. - * - * Attached to: - * KDBUS_ITEM_CAPS - */ -struct kdbus_caps { - __u32 last_cap; - __u32 caps[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_audit - audit information - * @sessionid: The audit session ID - * @loginuid: The audit login uid - * - * Attached to: - * KDBUS_ITEM_AUDIT - */ -struct kdbus_audit { - __u32 sessionid; - __u32 loginuid; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_timestamp - * @seqnum: Global per-domain message sequence number - * @monotonic_ns: Monotonic timestamp, in nanoseconds - * @realtime_ns: Realtime timestamp, in nanoseconds - * - * Attached to: - * KDBUS_ITEM_TIMESTAMP - */ -struct kdbus_timestamp { - __u64 seqnum; - __u64 monotonic_ns; - __u64 realtime_ns; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_vec - I/O vector for kdbus payload items - * @size: The size of the vector - * @address: Memory address of data buffer - * @offset: Offset in the in-message payload memory, - * relative to the message head - * - * Attached to: - * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF - */ -struct kdbus_vec { - __u64 size; - union { - __u64 address; - __u64 offset; - }; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_bloom_parameter - bus-wide bloom parameters - * @size: Size of the bit field in bytes (m / 8) - * @n_hash: Number of hash functions used (k) - */ -struct kdbus_bloom_parameter { - __u64 size; - __u64 n_hash; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_bloom_filter - bloom filter containing n elements - * @generation: Generation of the element set in the filter - * @data: Bit field, multiple of 8 bytes - */ -struct kdbus_bloom_filter { - __u64 generation; - __u64 data[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_memfd - a kdbus memfd - * @start: The offset into the memfd where the segment starts - * @size: The size of the memfd segment - * @fd: The file descriptor number - * @__pad: Padding to ensure proper alignment and size - * - * Attached to: - * KDBUS_ITEM_PAYLOAD_MEMFD - */ -struct kdbus_memfd { - __u64 start; - __u64 size; - int fd; - __u32 __pad; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_name - a registered well-known name with its flags - * @flags: Flags from KDBUS_NAME_* - * @name: Well-known name - * - * Attached to: - * KDBUS_ITEM_OWNED_NAME - */ -struct kdbus_name { - __u64 flags; - char name[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_policy_access_type - permissions of a policy record - * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid - * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid - * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid - * @KDBUS_POLICY_ACCESS_WORLD: World-accessible - */ -enum kdbus_policy_access_type { - _KDBUS_POLICY_ACCESS_NULL, - KDBUS_POLICY_ACCESS_USER, - KDBUS_POLICY_ACCESS_GROUP, - KDBUS_POLICY_ACCESS_WORLD, -}; - -/** - * enum kdbus_policy_access_flags - mode flags - * @KDBUS_POLICY_OWN: Allow to own a well-known name - * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE - * @KDBUS_POLICY_TALK: Allow communication to a well-known name - * Implies KDBUS_POLICY_SEE - * @KDBUS_POLICY_SEE: Allow to see a well-known name - */ -enum kdbus_policy_type { - KDBUS_POLICY_SEE = 0, - KDBUS_POLICY_TALK, - KDBUS_POLICY_OWN, -}; - -/** - * struct kdbus_policy_access - policy access item - * @type: One of KDBUS_POLICY_ACCESS_* types - * @access: Access to grant - * @id: For KDBUS_POLICY_ACCESS_USER, the uid - * For KDBUS_POLICY_ACCESS_GROUP, the gid - */ -struct kdbus_policy_access { - __u64 type; /* USER, GROUP, WORLD */ - __u64 access; /* OWN, TALK, SEE */ - __u64 id; /* uid, gid, 0 */ -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_attach_flags - flags for metadata attachments - * @KDBUS_ATTACH_TIMESTAMP: Timestamp - * @KDBUS_ATTACH_CREDS: Credentials - * @KDBUS_ATTACH_PIDS: PIDs - * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups - * @KDBUS_ATTACH_NAMES: Well-known names - * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID - * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID - * @KDBUS_ATTACH_EXE: The path of the executable - * @KDBUS_ATTACH_CMDLINE: The process command line - * @KDBUS_ATTACH_CGROUP: The croup membership - * @KDBUS_ATTACH_CAPS: The process capabilities - * @KDBUS_ATTACH_SECLABEL: The security label - * @KDBUS_ATTACH_AUDIT: The audit IDs - * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name - * @_KDBUS_ATTACH_ALL: All of the above - * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of - * metatdata. - */ -enum kdbus_attach_flags { - KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, - KDBUS_ATTACH_CREDS = 1ULL << 1, - KDBUS_ATTACH_PIDS = 1ULL << 2, - KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, - KDBUS_ATTACH_NAMES = 1ULL << 4, - KDBUS_ATTACH_TID_COMM = 1ULL << 5, - KDBUS_ATTACH_PID_COMM = 1ULL << 6, - KDBUS_ATTACH_EXE = 1ULL << 7, - KDBUS_ATTACH_CMDLINE = 1ULL << 8, - KDBUS_ATTACH_CGROUP = 1ULL << 9, - KDBUS_ATTACH_CAPS = 1ULL << 10, - KDBUS_ATTACH_SECLABEL = 1ULL << 11, - KDBUS_ATTACH_AUDIT = 1ULL << 12, - KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, - _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, - _KDBUS_ATTACH_ANY = ~0ULL -}; - -/** - * enum kdbus_item_type - item types to chain data in a list - * @_KDBUS_ITEM_NULL: Uninitialized/invalid - * @_KDBUS_ITEM_USER_BASE: Start of user items - * @KDBUS_ITEM_NEGOTIATE: Negotiate supported items - * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data - * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head - * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd - * @KDBUS_ITEM_FDS: Attached file descriptors - * @KDBUS_ITEM_CANCEL_FD: FD used to cancel a synchronous - * operation by writing to it from - * userspace - * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with - * KDBUS_CMD_BUS_MAKE, carries a - * struct kdbus_bloom_parameter - * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, - * used to match against a bloom mask of a - * connection, carries a struct - * kdbus_bloom_filter - * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a - * message'sbloom filter - * @KDBUS_ITEM_DST_NAME: Destination's well-known name - * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint - * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which - * metadata a connection opts in to send - * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which - * metadata a connection requests to - * receive for each reeceived message - * @KDBUS_ITEM_ID: Connection ID - * @KDBUS_ITEM_NAME: Well-know name with flags - * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items - * @KDBUS_ITEM_TIMESTAMP: Timestamp - * @KDBUS_ITEM_CREDS: Process credentials - * @KDBUS_ITEM_PIDS: Process identifiers - * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups - * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated - * connection - * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier - * (Don't trust this, see below.) - * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier - * (Don't trust this, see below.) - * @KDBUS_ITEM_EXE: The path of the executable - * (Don't trust this, see below.) - * @KDBUS_ITEM_CMDLINE: The process command line - * (Don't trust this, see below.) - * @KDBUS_ITEM_CGROUP: The croup membership - * @KDBUS_ITEM_CAPS: The process capabilities - * @KDBUS_ITEM_SECLABEL: The security label - * @KDBUS_ITEM_AUDIT: The audit IDs - * @KDBUS_ITEM_CONN_DESCRIPTION: The connection's human-readable name - * (debugging) - * @_KDBUS_ITEM_POLICY_BASE: Start of policy items - * @KDBUS_ITEM_POLICY_ACCESS: Policy access block - * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items - * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change - * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change - * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change - * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached - * @KDBUS_ITEM_REPLY_DEAD: Destination died - * - * N.B: The process and thread COMM fields, as well as the CMDLINE and - * EXE fields may be altered by unprivileged processes und should - * hence *not* used for security decisions. Peers should make use of - * these items only for informational purposes, such as generating log - * records. - */ -enum kdbus_item_type { - _KDBUS_ITEM_NULL, - _KDBUS_ITEM_USER_BASE, - KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE, - KDBUS_ITEM_PAYLOAD_VEC, - KDBUS_ITEM_PAYLOAD_OFF, - KDBUS_ITEM_PAYLOAD_MEMFD, - KDBUS_ITEM_FDS, - KDBUS_ITEM_CANCEL_FD, - KDBUS_ITEM_BLOOM_PARAMETER, - KDBUS_ITEM_BLOOM_FILTER, - KDBUS_ITEM_BLOOM_MASK, - KDBUS_ITEM_DST_NAME, - KDBUS_ITEM_MAKE_NAME, - KDBUS_ITEM_ATTACH_FLAGS_SEND, - KDBUS_ITEM_ATTACH_FLAGS_RECV, - KDBUS_ITEM_ID, - KDBUS_ITEM_NAME, - KDBUS_ITEM_DST_ID, - - /* keep these item types in sync with KDBUS_ATTACH_* flags */ - _KDBUS_ITEM_ATTACH_BASE = 0x1000, - KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, - KDBUS_ITEM_CREDS, - KDBUS_ITEM_PIDS, - KDBUS_ITEM_AUXGROUPS, - KDBUS_ITEM_OWNED_NAME, - KDBUS_ITEM_TID_COMM, - KDBUS_ITEM_PID_COMM, - KDBUS_ITEM_EXE, - KDBUS_ITEM_CMDLINE, - KDBUS_ITEM_CGROUP, - KDBUS_ITEM_CAPS, - KDBUS_ITEM_SECLABEL, - KDBUS_ITEM_AUDIT, - KDBUS_ITEM_CONN_DESCRIPTION, - - _KDBUS_ITEM_POLICY_BASE = 0x2000, - KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, - - _KDBUS_ITEM_KERNEL_BASE = 0x8000, - KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE, - KDBUS_ITEM_NAME_REMOVE, - KDBUS_ITEM_NAME_CHANGE, - KDBUS_ITEM_ID_ADD, - KDBUS_ITEM_ID_REMOVE, - KDBUS_ITEM_REPLY_TIMEOUT, - KDBUS_ITEM_REPLY_DEAD, -}; - -/** - * struct kdbus_item - chain of data blocks - * @size: Overall data record size - * @type: Kdbus_item type of data - * @data: Generic bytes - * @data32: Generic 32 bit array - * @data64: Generic 64 bit array - * @str: Generic string - * @id: Connection ID - * @vec: KDBUS_ITEM_PAYLOAD_VEC - * @creds: KDBUS_ITEM_CREDS - * @audit: KDBUS_ITEM_AUDIT - * @timestamp: KDBUS_ITEM_TIMESTAMP - * @name: KDBUS_ITEM_NAME - * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER - * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER - * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD - * @name_change: KDBUS_ITEM_NAME_ADD - * KDBUS_ITEM_NAME_REMOVE - * KDBUS_ITEM_NAME_CHANGE - * @id_change: KDBUS_ITEM_ID_ADD - * KDBUS_ITEM_ID_REMOVE - * @policy: KDBUS_ITEM_POLICY_ACCESS - */ -struct kdbus_item { - __u64 size; - __u64 type; - union { - __u8 data[0]; - __u32 data32[0]; - __u64 data64[0]; - char str[0]; - - __u64 id; - struct kdbus_vec vec; - struct kdbus_creds creds; - struct kdbus_pids pids; - struct kdbus_audit audit; - struct kdbus_caps caps; - struct kdbus_timestamp timestamp; - struct kdbus_name name; - struct kdbus_bloom_parameter bloom_parameter; - struct kdbus_bloom_filter bloom_filter; - struct kdbus_memfd memfd; - int fds[0]; - struct kdbus_notify_name_change name_change; - struct kdbus_notify_id_change id_change; - struct kdbus_policy_access policy_access; - }; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_msg_flags - type of message - * @KDBUS_MSG_EXPECT_REPLY: Expect a reply message, used for - * method calls. The userspace-supplied - * cookie identifies the message and the - * respective reply carries the cookie - * in cookie_reply - * @KDBUS_MSG_NO_AUTO_START: Do not start a service if the addressed - * name is not currently active. This flag is - * not looked at by the kernel but only - * serves as hint for userspace implementations. - * @KDBUS_MSG_SIGNAL: Treat this message as signal - */ -enum kdbus_msg_flags { - KDBUS_MSG_EXPECT_REPLY = 1ULL << 0, - KDBUS_MSG_NO_AUTO_START = 1ULL << 1, - KDBUS_MSG_SIGNAL = 1ULL << 2, -}; - -/** - * enum kdbus_payload_type - type of payload carried by message - * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message - * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus" - * - * Any payload-type is accepted. Common types will get added here once - * established. - */ -enum kdbus_payload_type { - KDBUS_PAYLOAD_KERNEL, - KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL, -}; - -/** - * struct kdbus_msg - the representation of a kdbus message - * @size: Total size of the message - * @flags: Message flags (KDBUS_MSG_*), userspace → kernel - * @priority: Message queue priority value - * @dst_id: 64-bit ID of the destination connection - * @src_id: 64-bit ID of the source connection - * @payload_type: Payload type (KDBUS_PAYLOAD_*) - * @cookie: Userspace-supplied cookie, for the connection - * to identify its messages - * @timeout_ns: The time to wait for a message reply from the peer. - * If there is no reply, and the send command is - * executed asynchronously, a kernel-generated message - * with an attached KDBUS_ITEM_REPLY_TIMEOUT item - * is sent to @src_id. For synchronously executed send - * command, the value denotes the maximum time the call - * blocks to wait for a reply. The timeout is expected in - * nanoseconds and as absolute CLOCK_MONOTONIC value. - * @cookie_reply: A reply to the requesting message with the same - * cookie. The requesting connection can match its - * request and the reply with this value - * @items: A list of kdbus_items containing the message payload - */ -struct kdbus_msg { - __u64 size; - __u64 flags; - __s64 priority; - __u64 dst_id; - __u64 src_id; - __u64 payload_type; - __u64 cookie; - union { - __u64 timeout_ns; - __u64 cookie_reply; - }; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_msg_info - returned message container - * @offset: Offset of kdbus_msg slice in pool - * @msg_size: Copy of the kdbus_msg.size field - * @return_flags: Command return flags, kernel → userspace - */ -struct kdbus_msg_info { - __u64 offset; - __u64 msg_size; - __u64 return_flags; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_send_flags - flags for sending messages - * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to - * reply to this message. The - * KDBUS_CMD_SEND ioctl() will block - * until the reply is received, and - * reply in struct kdbus_cmd_send will - * yield the offset in the sender's pool - * where the reply can be found. - * This flag is only valid if - * @KDBUS_MSG_EXPECT_REPLY is set as well. - */ -enum kdbus_send_flags { - KDBUS_SEND_SYNC_REPLY = 1ULL << 0, -}; - -/** - * struct kdbus_cmd_send - send message - * @size: Overall size of this structure - * @flags: Flags to change send behavior (KDBUS_SEND_*) - * @return_flags: Command return flags, kernel → userspace - * @msg_address: Storage address of the kdbus_msg to send - * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY - * was given - * @items: Additional items for this command - */ -struct kdbus_cmd_send { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 msg_address; - struct kdbus_msg_info reply; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_recv_flags - flags for de-queuing messages - * @KDBUS_RECV_PEEK: Return the next queued message without - * actually de-queuing it, and without installing - * any file descriptors or other resources. It is - * usually used to determine the activating - * connection of a bus name. - * @KDBUS_RECV_DROP: Drop and free the next queued message and all - * its resources without actually receiving it. - * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or - * higher priority (lowest values); if not set, - * the priority value is ignored. - */ -enum kdbus_recv_flags { - KDBUS_RECV_PEEK = 1ULL << 0, - KDBUS_RECV_DROP = 1ULL << 1, - KDBUS_RECV_USE_PRIORITY = 1ULL << 2, -}; - -/** - * enum kdbus_recv_return_flags - return flags for message receive commands - * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not - * be installed. These descriptors in - * KDBUS_ITEM_FDS will carry the value -1. - * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since - * the last time a message was received. - * The 'dropped_msgs' counter contains the - * number of messages dropped pool - * overflows or other missed broadcasts. - */ -enum kdbus_recv_return_flags { - KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, - KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, -}; - -/** - * struct kdbus_cmd_recv - struct to de-queue a buffered message - * @size: Overall size of this object - * @flags: KDBUS_RECV_* flags, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @priority: Minimum priority of the messages to de-queue. Lowest - * values have the highest priority. - * @dropped_msgs: In case there were any dropped messages since the last - * time a message was received, this will be set to the - * number of lost messages and - * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in - * 'return_flags'. This can only happen if the ioctl - * returns 0 or EAGAIN. - * @msg: Return storage for received message. - * @items: Additional items for this command. - * - * This struct is used with the KDBUS_CMD_RECV ioctl. - */ -struct kdbus_cmd_recv { - __u64 size; - __u64 flags; - __u64 return_flags; - __s64 priority; - __u64 dropped_msgs; - struct kdbus_msg_info msg; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_cmd_free - struct to free a slice of memory in the pool - * @size: Overall size of this structure - * @flags: Flags for the free command, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @offset: The offset of the memory slice, as returned by other - * ioctls - * @items: Additional items to modify the behavior - * - * This struct is used with the KDBUS_CMD_FREE ioctl. - */ -struct kdbus_cmd_free { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 offset; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello - * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of - * any passed file descriptors - * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers - * a well-know name for a process to be started - * when traffic arrives - * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers - * policy entries for a name. The provided name - * is not activated and not registered with the - * name database, it only allows unprivileged - * connections to acquire a name, talk or discover - * a service - * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor - * bus traffic - */ -enum kdbus_hello_flags { - KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, - KDBUS_HELLO_ACTIVATOR = 1ULL << 1, - KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, - KDBUS_HELLO_MONITOR = 1ULL << 3, -}; - -/** - * struct kdbus_cmd_hello - struct to say hello to kdbus - * @size: The total size of the structure - * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @attach_flags_send: Mask of metadata to attach to each message sent - * off by this connection (KDBUS_ATTACH_*) - * @attach_flags_recv: Mask of metadata to attach to each message receieved - * by the new connection (KDBUS_ATTACH_*) - * @bus_flags: The flags field copied verbatim from the original - * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful - * to do negotiation of features of the payload that is - * transferred (kernel → userspace) - * @id: The ID of this connection (kernel → userspace) - * @pool_size: Size of the connection's buffer where the received - * messages are placed - * @offset: Pool offset where items are returned to report - * additional information about the bus and the newly - * created connection. - * @items_size: Size of buffer returned in the pool slice at @offset. - * @id128: Unique 128-bit ID of the bus (kernel → userspace) - * @items: A list of items - * - * This struct is used with the KDBUS_CMD_HELLO ioctl. - */ -struct kdbus_cmd_hello { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 attach_flags_send; - __u64 attach_flags_recv; - __u64 bus_flags; - __u64 id; - __u64 pool_size; - __u64 offset; - __u64 items_size; - __u8 id128[16]; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_info - connection information - * @size: total size of the struct - * @id: 64bit object ID - * @flags: object creation flags - * @items: list of items - * - * Note that the user is responsible for freeing the allocated memory with - * the KDBUS_CMD_FREE ioctl. - */ -struct kdbus_info { - __u64 size; - __u64 id; - __u64 flags; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_list_flags - what to include into the returned list - * @KDBUS_LIST_UNIQUE: active connections - * @KDBUS_LIST_ACTIVATORS: activator connections - * @KDBUS_LIST_NAMES: known well-known names - * @KDBUS_LIST_QUEUED: queued-up names - */ -enum kdbus_list_flags { - KDBUS_LIST_UNIQUE = 1ULL << 0, - KDBUS_LIST_NAMES = 1ULL << 1, - KDBUS_LIST_ACTIVATORS = 1ULL << 2, - KDBUS_LIST_QUEUED = 1ULL << 3, -}; - -/** - * struct kdbus_cmd_list - list connections - * @size: overall size of this object - * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel - * @return_flags: command return flags, kernel → userspace - * @offset: Offset in the caller's pool buffer where an array of - * kdbus_info objects is stored. - * The user must use KDBUS_CMD_FREE to free the - * allocated memory. - * @list_size: size of returned list in bytes - * @items: Items for the command. Reserved for future use. - * - * This structure is used with the KDBUS_CMD_LIST ioctl. - */ -struct kdbus_cmd_list { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 offset; - __u64 list_size; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl - * @size: The total size of the struct - * @flags: Flags for this ioctl, userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @id: The 64-bit ID of the connection. If set to zero, passing - * @name is required. kdbus will look up the name to - * determine the ID in this case. - * @attach_flags: Set of attach flags to specify the set of information - * to receive, userspace → kernel - * @offset: Returned offset in the caller's pool buffer where the - * kdbus_info struct result is stored. The user must - * use KDBUS_CMD_FREE to free the allocated memory. - * @info_size: Output buffer to report size of data at @offset. - * @items: The optional item list, containing the - * well-known name to look up as a KDBUS_ITEM_NAME. - * Only needed in case @id is zero. - * - * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will - * tell the user the offset in the connection pool buffer at which to find the - * result in a struct kdbus_info. - */ -struct kdbus_cmd_info { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 id; - __u64 attach_flags; - __u64 offset; - __u64 info_size; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl - * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already - * exists, remove them before installing the new - * matches. - */ -enum kdbus_cmd_match_flags { - KDBUS_MATCH_REPLACE = 1ULL << 0, -}; - -/** - * struct kdbus_cmd_match - struct to add or remove matches - * @size: The total size of the struct - * @flags: Flags for match command (KDBUS_MATCH_*), - * userspace → kernel - * @return_flags: Command return flags, kernel → userspace - * @cookie: Userspace supplied cookie. When removing, the cookie - * identifies the match to remove - * @items: A list of items for additional information - * - * This structure is used with the KDBUS_CMD_MATCH_ADD and - * KDBUS_CMD_MATCH_REMOVE ioctl. - */ -struct kdbus_cmd_match { - __u64 size; - __u64 flags; - __u64 return_flags; - __u64 cookie; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE - * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible - * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible - */ -enum kdbus_make_flags { - KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, - KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, -}; - -/** - * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE - * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections - * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name - * @KDBUS_NAME_QUEUE: Name should be queued if busy - * @KDBUS_NAME_IN_QUEUE: Name is queued - * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection - */ -enum kdbus_name_flags { - KDBUS_NAME_REPLACE_EXISTING = 1ULL << 0, - KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, - KDBUS_NAME_QUEUE = 1ULL << 2, - KDBUS_NAME_IN_QUEUE = 1ULL << 3, - KDBUS_NAME_ACTIVATOR = 1ULL << 4, -}; - -/** - * struct kdbus_cmd - generic ioctl payload - * @size: Overall size of this structure - * @flags: Flags for this ioctl, userspace → kernel - * @return_flags: Ioctl return flags, kernel → userspace - * @items: Additional items to modify the behavior - * - * This is a generic ioctl payload object. It's used by all ioctls that only - * take flags and items as input. - */ -struct kdbus_cmd { - __u64 size; - __u64 flags; - __u64 return_flags; - struct kdbus_item items[0]; -} __attribute__((__aligned__(8))); - -/** - * Ioctl API - * - * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command - * creates a new bus with the specified - * name. The bus is immediately shut down and - * cleaned up when the opened file descriptor is - * closed. - * - * KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to - * the bus. Such endpoints usually carry a more - * restrictive policy and grant restricted access - * to specific applications. - * KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used - * to update the policy. - * - * KDBUS_CMD_HELLO: By opening the bus node, a connection is - * created. After a HELLO the opened connection - * becomes an active peer on the bus. - * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to - * update the metadata subscription mask and - * policy. - * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no - * messages queued up in the connection's pool, - * the call succeeds, and the handle is rendered - * unusable. Otherwise, -EBUSY is returned without - * any further side-effects. - * KDBUS_CMD_FREE: Release the allocated memory in the receiver's - * pool. - * KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the - * initial creator of the connection. The data was - * stored at registration time and does not - * necessarily represent the connected process or - * the actual state of the process. - * KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus - * a connection is attached to. - * - * KDBUS_CMD_SEND: Send a message and pass data from userspace to - * the kernel. - * KDBUS_CMD_RECV: Receive a message from the kernel which is - * placed in the receiver's pool. - * - * KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with - * the connection. Well-known names are used to - * address a peer on the bus. - * KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection - * currently owns. - * KDBUS_CMD_LIST: Retrieve the list of all currently registered - * well-known and unique names. - * - * KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should - * be delivered to the connection. - * KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. - */ -enum kdbus_ioctl_type { - /* bus owner (00-0f) */ - KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, - struct kdbus_cmd), - - /* endpoint owner (10-1f) */ - KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, - struct kdbus_cmd), - KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11, - struct kdbus_cmd), - - /* connection owner (80-ff) */ - KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80, - struct kdbus_cmd_hello), - KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, - struct kdbus_cmd), - KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82, - struct kdbus_cmd), - KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83, - struct kdbus_cmd_free), - KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84, - struct kdbus_cmd_info), - KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85, - struct kdbus_cmd_info), - KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86, - struct kdbus_cmd_list), - - KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90, - struct kdbus_cmd_send), - KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91, - struct kdbus_cmd_recv), - - KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0, - struct kdbus_cmd), - KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1, - struct kdbus_cmd), - - KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0, - struct kdbus_cmd_match), - KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1, - struct kdbus_cmd_match), -}; - -#endif /* _UAPI_KDBUS_H_ */ diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c deleted file mode 100644 index ed5f94e136..0000000000 --- a/src/libsystemd/sd-bus/sd-bus.c +++ /dev/null @@ -1,3791 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-container.h" -#include "bus-control.h" -#include "bus-internal.h" -#include "bus-kernel.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-objects.h" -#include "bus-protocol.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 { \ - sd_bus_message *_mm = (m); \ - log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", \ - bus_message_type_to_string(_mm->header->type), \ - strna(sd_bus_message_get_sender(_mm)), \ - strna(sd_bus_message_get_destination(_mm)), \ - strna(sd_bus_message_get_path(_mm)), \ - strna(sd_bus_message_get_interface(_mm)), \ - strna(sd_bus_message_get_member(_mm)), \ - BUS_MESSAGE_COOKIE(_mm), \ - _mm->reply_cookie, \ - strna(_mm->error.message)); \ - } while (false) - -static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); -static int attach_io_events(sd_bus *b); -static void detach_io_events(sd_bus *b); - -static thread_local sd_bus *default_system_bus = NULL; -static thread_local sd_bus *default_user_bus = NULL; -static thread_local sd_bus *default_starter_bus = NULL; - -static void bus_close_fds(sd_bus *b) { - assert(b); - - detach_io_events(b); - - if (b->input_fd != b->output_fd) - safe_close(b->output_fd); - b->output_fd = b->input_fd = safe_close(b->input_fd); -} - -static void bus_reset_queues(sd_bus *b) { - assert(b); - - while (b->rqueue_size > 0) - sd_bus_message_unref(b->rqueue[--b->rqueue_size]); - - b->rqueue = mfree(b->rqueue); - b->rqueue_allocated = 0; - - while (b->wqueue_size > 0) - sd_bus_message_unref(b->wqueue[--b->wqueue_size]); - - b->wqueue = mfree(b->wqueue); - b->wqueue_allocated = 0; -} - -static void bus_free(sd_bus *b) { - sd_bus_slot *s; - - assert(b); - assert(!b->track_queue); - - b->state = BUS_CLOSED; - - sd_bus_detach_event(b); - - while ((s = b->slots)) { - /* At this point only floating slots can still be - * around, because the non-floating ones keep a - * reference to the bus, and we thus couldn't be - * destructing right now... We forcibly disconnect the - * slots here, so that they still can be referenced by - * apps, but are dead. */ - - assert(s->floating); - bus_slot_disconnect(s); - sd_bus_slot_unref(s); - } - - if (b->default_bus_ptr) - *b->default_bus_ptr = NULL; - - bus_close_fds(b); - - if (b->kdbus_buffer) - munmap(b->kdbus_buffer, KDBUS_POOL_SIZE); - - free(b->label); - free(b->rbuffer); - free(b->unique_name); - free(b->auth_buffer); - free(b->address); - free(b->kernel); - free(b->machine); - free(b->fake_label); - free(b->cgroup_root); - free(b->description); - - free(b->exec_path); - strv_free(b->exec_argv); - - close_many(b->fds, b->n_fds); - free(b->fds); - - bus_reset_queues(b); - - ordered_hashmap_free_free(b->reply_callbacks); - prioq_free(b->reply_callbacks_prioq); - - assert(b->match_callbacks.type == BUS_MATCH_ROOT); - bus_match_free(&b->match_callbacks); - - hashmap_free_free(b->vtable_methods); - hashmap_free_free(b->vtable_properties); - - assert(hashmap_isempty(b->nodes)); - hashmap_free(b->nodes); - - bus_kernel_flush_memfd(b); - - assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0); - - free(b); -} - -_public_ int sd_bus_new(sd_bus **ret) { - sd_bus *r; - - assert_return(ret, -EINVAL); - - r = new0(sd_bus, 1); - if (!r) - return -ENOMEM; - - r->n_ref = REFCNT_INIT; - r->input_fd = r->output_fd = -1; - r->message_version = 1; - r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - r->hello_flags |= KDBUS_HELLO_ACCEPT_FD; - r->attach_flags |= KDBUS_ATTACH_NAMES; - r->original_pid = getpid(); - - assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0); - - /* We guarantee that wqueue always has space for at least one - * entry */ - if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) { - free(r); - return -ENOMEM; - } - - *ret = r; - return 0; -} - -_public_ int sd_bus_set_address(sd_bus *bus, const char *address) { - char *a; - - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(address, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - a = strdup(address); - if (!a) - return -ENOMEM; - - free(bus->address); - bus->address = a; - - return 0; -} - -_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(input_fd >= 0, -EBADF); - assert_return(output_fd >= 0, -EBADF); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->input_fd = input_fd; - bus->output_fd = output_fd; - return 0; -} - -_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) { - char *p, **a; - - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(path, -EINVAL); - assert_return(!strv_isempty(argv), -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - p = strdup(path); - if (!p) - return -ENOMEM; - - a = strv_copy(argv); - if (!a) { - free(p); - return -ENOMEM; - } - - free(bus->exec_path); - strv_free(bus->exec_argv); - - bus->exec_path = p; - bus->exec_argv = a; - - return 0; -} - -_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->bus_client = !!b; - return 0; -} - -_public_ int sd_bus_set_monitor(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b); - return 0; -} - -_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b); - return 0; -} - -_public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { - uint64_t new_flags; - assert_return(bus, -EINVAL); - assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - new_flags = bus->attach_flags; - SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); - - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - if (bus->state != BUS_UNSET && bus->is_kernel) - bus_kernel_realize_attach_flags(bus); - - return 0; -} - -_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { - uint64_t new_flags; - - assert_return(bus, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); - assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - SET_FLAG(bus->creds_mask, mask, b); - - /* The well knowns we need unconditionally, so that matches can work */ - bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - - /* Make sure we don't lose the timestamp flag */ - new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - if (bus->state != BUS_UNSET && bus->is_kernel) - bus_kernel_realize_attach_flags(bus); - - return 0; -} - -_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { - assert_return(bus, -EINVAL); - assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->is_server = !!b; - bus->server_id = server_id; - return 0; -} - -_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->anonymous_auth = !!b; - return 0; -} - -_public_ int sd_bus_set_trusted(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->trusted = !!b; - return 0; -} - -_public_ int sd_bus_set_description(sd_bus *bus, const char *description) { - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return free_and_strdup(&bus->description, description); -} - -_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->allow_interactive_authorization = !!b; - return 0; -} - -_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->allow_interactive_authorization; -} - -static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { - const char *s; - sd_bus *bus; - int r; - - assert(reply); - bus = reply->bus; - assert(bus); - assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING); - - r = sd_bus_message_get_errno(reply); - if (r > 0) - return -r; - - r = sd_bus_message_read(reply, "s", &s); - if (r < 0) - return r; - - if (!service_name_is_valid(s) || s[0] != ':') - return -EBADMSG; - - bus->unique_name = strdup(s); - if (!bus->unique_name) - return -ENOMEM; - - if (bus->state == BUS_HELLO) - bus->state = BUS_RUNNING; - - return 1; -} - -static int bus_send_hello(sd_bus *bus) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - - if (!bus->bus_client || bus->is_kernel) - return 0; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "Hello"); - if (r < 0) - return r; - - return sd_bus_call_async(bus, NULL, m, hello_callback, NULL, 0); -} - -int bus_start_running(sd_bus *bus) { - assert(bus); - - if (bus->bus_client && !bus->is_kernel) { - bus->state = BUS_HELLO; - return 1; - } - - bus->state = BUS_RUNNING; - return 1; -} - -static int parse_address_key(const char **p, const char *key, char **value) { - size_t l, n = 0, allocated = 0; - const char *a; - char *r = NULL; - - assert(p); - assert(*p); - assert(value); - - if (key) { - l = strlen(key); - if (strncmp(*p, key, l) != 0) - return 0; - - if ((*p)[l] != '=') - return 0; - - if (*value) - return -EINVAL; - - a = *p + l + 1; - } else - a = *p; - - while (*a != ';' && *a != ',' && *a != 0) { - char c; - - if (*a == '%') { - int x, y; - - x = unhexchar(a[1]); - if (x < 0) { - free(r); - return x; - } - - y = unhexchar(a[2]); - if (y < 0) { - free(r); - return y; - } - - c = (char) ((x << 4) | y); - a += 3; - } else { - c = *a; - a++; - } - - if (!GREEDY_REALLOC(r, allocated, n + 2)) - return -ENOMEM; - - r[n++] = c; - } - - if (!r) { - r = strdup(""); - if (!r) - return -ENOMEM; - } else - r[n] = 0; - - if (*a == ',') - a++; - - *p = a; - - free(*value); - *value = r; - - return 1; -} - -static void skip_address_key(const char **p) { - assert(p); - assert(*p); - - *p += strcspn(*p, ","); - - if (**p == ',') - (*p)++; -} - -static int parse_unix_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *path = NULL, *abstract = NULL; - size_t l; - int r; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "path", &path); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "abstract", &abstract); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!path && !abstract) - return -EINVAL; - - if (path && abstract) - return -EINVAL; - - if (path) { - l = strlen(path); - if (l > sizeof(b->sockaddr.un.sun_path)) - return -E2BIG; - - b->sockaddr.un.sun_family = AF_UNIX; - strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path)); - b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l; - } else if (abstract) { - l = strlen(abstract); - if (l > sizeof(b->sockaddr.un.sun_path) - 1) - return -E2BIG; - - b->sockaddr.un.sun_family = AF_UNIX; - b->sockaddr.un.sun_path[0] = 0; - strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1); - b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - } - - return 0; -} - -static int parse_tcp_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL; - int r; - struct addrinfo *result, hints = { - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_ADDRCONFIG, - }; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "host", &host); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "port", &port); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "family", &family); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!host || !port) - return -EINVAL; - - if (family) { - if (streq(family, "ipv4")) - hints.ai_family = AF_INET; - else if (streq(family, "ipv6")) - hints.ai_family = AF_INET6; - else - return -EINVAL; - } - - r = getaddrinfo(host, port, &hints, &result); - if (r == EAI_SYSTEM) - return -errno; - else if (r != 0) - return -EADDRNOTAVAIL; - - memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen); - b->sockaddr_size = result->ai_addrlen; - - freeaddrinfo(result); - - return 0; -} - -static int parse_exec_address(sd_bus *b, const char **p, char **guid) { - char *path = NULL; - unsigned n_argv = 0, j; - char **argv = NULL; - size_t allocated = 0; - int r; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - goto fail; - else if (r > 0) - continue; - - r = parse_address_key(p, "path", &path); - if (r < 0) - goto fail; - else if (r > 0) - continue; - - if (startswith(*p, "argv")) { - unsigned ul; - - errno = 0; - ul = strtoul(*p + 4, (char**) p, 10); - if (errno > 0 || **p != '=' || ul > 256) { - r = -EINVAL; - goto fail; - } - - (*p)++; - - if (ul >= n_argv) { - if (!GREEDY_REALLOC0(argv, allocated, ul + 2)) { - r = -ENOMEM; - goto fail; - } - - n_argv = ul + 1; - } - - r = parse_address_key(p, NULL, argv + ul); - if (r < 0) - goto fail; - - continue; - } - - skip_address_key(p); - } - - if (!path) { - r = -EINVAL; - goto fail; - } - - /* Make sure there are no holes in the array, with the - * exception of argv[0] */ - for (j = 1; j < n_argv; j++) - if (!argv[j]) { - r = -EINVAL; - goto fail; - } - - if (argv && argv[0] == NULL) { - argv[0] = strdup(path); - if (!argv[0]) { - r = -ENOMEM; - goto fail; - } - } - - b->exec_path = path; - b->exec_argv = argv; - return 0; - -fail: - for (j = 0; j < n_argv; j++) - free(argv[j]); - - free(argv); - free(path); - return r; -} - -static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *path = NULL; - int r; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "path", &path); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!path) - return -EINVAL; - - free(b->kernel); - b->kernel = path; - path = NULL; - - return 0; -} - -static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *machine = NULL, *pid = NULL; - int r; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "machine", &machine); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "pid", &pid); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!machine == !pid) - return -EINVAL; - - if (machine) { - if (!machine_name_is_valid(machine)) - return -EINVAL; - - free(b->machine); - b->machine = machine; - machine = NULL; - } else { - b->machine = mfree(b->machine); - } - - if (pid) { - r = parse_pid(pid, &b->nspid); - if (r < 0) - return r; - } else - b->nspid = 0; - - b->sockaddr.un.sun_family = AF_UNIX; - strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); - b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); - - return 0; -} - -static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { - _cleanup_free_ char *machine = NULL, *pid = NULL; - int r; - - assert(b); - assert(p); - assert(*p); - assert(guid); - - while (**p != 0 && **p != ';') { - r = parse_address_key(p, "guid", guid); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "machine", &machine); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_address_key(p, "pid", &pid); - if (r < 0) - return r; - else if (r > 0) - continue; - - skip_address_key(p); - } - - if (!machine == !pid) - return -EINVAL; - - if (machine) { - if (!machine_name_is_valid(machine)) - return -EINVAL; - - free(b->machine); - b->machine = machine; - machine = NULL; - } else { - b->machine = mfree(b->machine); - } - - if (pid) { - r = parse_pid(pid, &b->nspid); - if (r < 0) - return r; - } else - b->nspid = 0; - - r = free_and_strdup(&b->kernel, "/sys/fs/kdbus/0-system/bus"); - if (r < 0) - return r; - - return 0; -} - -static void bus_reset_parsed_address(sd_bus *b) { - assert(b); - - zero(b->sockaddr); - b->sockaddr_size = 0; - b->exec_argv = strv_free(b->exec_argv); - b->exec_path = mfree(b->exec_path); - b->server_id = SD_ID128_NULL; - b->kernel = mfree(b->kernel); - b->machine = mfree(b->machine); - b->nspid = 0; -} - -static int bus_parse_next_address(sd_bus *b) { - _cleanup_free_ char *guid = NULL; - const char *a; - int r; - - assert(b); - - if (!b->address) - return 0; - if (b->address[b->address_index] == 0) - return 0; - - bus_reset_parsed_address(b); - - a = b->address + b->address_index; - - while (*a != 0) { - - if (*a == ';') { - a++; - continue; - } - - if (startswith(a, "unix:")) { - a += 5; - - r = parse_unix_address(b, &a, &guid); - if (r < 0) - return r; - break; - - } else if (startswith(a, "tcp:")) { - - a += 4; - r = parse_tcp_address(b, &a, &guid); - if (r < 0) - return r; - - break; - - } else if (startswith(a, "unixexec:")) { - - a += 9; - r = parse_exec_address(b, &a, &guid); - if (r < 0) - return r; - - break; - - } else if (startswith(a, "kernel:")) { - - a += 7; - r = parse_kernel_address(b, &a, &guid); - if (r < 0) - return r; - - break; - } else if (startswith(a, "x-machine-unix:")) { - - a += 15; - r = parse_container_unix_address(b, &a, &guid); - if (r < 0) - return r; - - break; - } else if (startswith(a, "x-machine-kernel:")) { - - a += 17; - r = parse_container_kernel_address(b, &a, &guid); - if (r < 0) - return r; - - break; - } - - a = strchr(a, ';'); - if (!a) - return 0; - } - - if (guid) { - r = sd_id128_from_string(guid, &b->server_id); - if (r < 0) - return r; - } - - b->address_index = a - b->address; - return 1; -} - -static int bus_start_address(sd_bus *b) { - bool container_kdbus_available = false; - bool kdbus_available = false; - int r; - - assert(b); - - for (;;) { - bool skipped = false; - - bus_close_fds(b); - - /* - * Usually, if you provide multiple different bus-addresses, we - * try all of them in order. We use the first one that - * succeeds. However, if you mix kernel and unix addresses, we - * never try unix-addresses if a previous kernel address was - * tried and kdbus was available. This is required to prevent - * clients to fallback to the bus-proxy if kdbus is available - * but failed (eg., too many connections). - */ - - if (b->exec_path) - r = bus_socket_exec(b); - else if ((b->nspid > 0 || b->machine) && b->kernel) { - r = bus_container_connect_kernel(b); - if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) - container_kdbus_available = true; - - } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) { - if (!container_kdbus_available) - r = bus_container_connect_socket(b); - else - skipped = true; - - } else if (b->kernel) { - r = bus_kernel_connect(b); - if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) - kdbus_available = true; - - } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { - if (!kdbus_available) - r = bus_socket_connect(b); - else - skipped = true; - } else - skipped = true; - - if (!skipped) { - if (r >= 0) { - r = attach_io_events(b); - if (r >= 0) - return r; - } - - b->last_connect_error = -r; - } - - r = bus_parse_next_address(b); - if (r < 0) - return r; - if (r == 0) - return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED; - } -} - -int bus_next_address(sd_bus *b) { - assert(b); - - bus_reset_parsed_address(b); - return bus_start_address(b); -} - -static int bus_start_fd(sd_bus *b) { - struct stat st; - int r; - - assert(b); - assert(b->input_fd >= 0); - assert(b->output_fd >= 0); - - r = fd_nonblock(b->input_fd, true); - if (r < 0) - return r; - - r = fd_cloexec(b->input_fd, true); - if (r < 0) - return r; - - if (b->input_fd != b->output_fd) { - r = fd_nonblock(b->output_fd, true); - if (r < 0) - return r; - - r = fd_cloexec(b->output_fd, true); - if (r < 0) - return r; - } - - if (fstat(b->input_fd, &st) < 0) - return -errno; - - if (S_ISCHR(b->input_fd)) - return bus_kernel_take_fd(b); - else - return bus_socket_take_fd(b); -} - -_public_ int sd_bus_start(sd_bus *bus) { - int r; - - assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - bus->state = BUS_OPENING; - - if (bus->is_server && bus->bus_client) - return -EINVAL; - - if (bus->input_fd >= 0) - r = bus_start_fd(bus); - else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine) - r = bus_start_address(bus); - else - return -EINVAL; - - if (r < 0) { - sd_bus_close(bus); - return r; - } - - return bus_send_hello(bus); -} - -_public_ int sd_bus_open(sd_bus **ret) { - const char *e; - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - /* Let's connect to the starter bus if it is set, and - * otherwise to the bus that is appropropriate for the scope - * we are running in */ - - e = secure_getenv("DBUS_STARTER_BUS_TYPE"); - if (e) { - if (streq(e, "system")) - return sd_bus_open_system(ret); - else if (STR_IN_SET(e, "session", "user")) - return sd_bus_open_user(ret); - } - - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (!e) { - if (cg_pid_get_owner_uid(0, NULL) >= 0) - return sd_bus_open_user(ret); - else - return sd_bus_open_system(ret); - } - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = sd_bus_set_address(b, e); - if (r < 0) - goto fail; - - b->bus_client = true; - - /* We don't know whether the bus is trusted or not, so better - * be safe, and authenticate everything */ - b->trusted = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; - b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; - - r = sd_bus_start(b); - if (r < 0) - goto fail; - - *ret = b; - return 0; - -fail: - bus_free(b); - return r; -} - -int bus_set_address_system(sd_bus *b) { - const char *e; - assert(b); - - e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); - - return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); -} - -_public_ int sd_bus_open_system(sd_bus **ret) { - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = bus_set_address_system(b); - if (r < 0) - goto fail; - - b->bus_client = true; - b->is_system = true; - - /* Let's do per-method access control on the system bus. We - * need the caller's UID and capability set for that. */ - b->trusted = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; - b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; - - r = sd_bus_start(b); - if (r < 0) - goto fail; - - *ret = b; - return 0; - -fail: - bus_free(b); - return r; -} - -int bus_set_address_user(sd_bus *b) { - const char *e; - uid_t uid; - int r; - - assert(b); - - e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - if (e) - return sd_bus_set_address(b, e); - - r = cg_pid_get_owner_uid(0, &uid); - if (r < 0) - uid = getuid(); - - e = secure_getenv("XDG_RUNTIME_DIR"); - if (e) { - _cleanup_free_ char *ee = NULL; - - ee = bus_address_escape(e); - if (!ee) - return -ENOMEM; - - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee); - } else - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid); - - if (!b->address) - return -ENOMEM; - - return 0; -} - -_public_ int sd_bus_open_user(sd_bus **ret) { - sd_bus *b; - int r; - - assert_return(ret, -EINVAL); - - r = sd_bus_new(&b); - if (r < 0) - return r; - - r = bus_set_address_user(b); - if (r < 0) - return r; - - b->bus_client = true; - b->is_user = true; - - /* We don't do any per-method access control on the user - * bus. */ - b->trusted = true; - - r = sd_bus_start(b); - if (r < 0) - goto fail; - - *ret = b; - return 0; - -fail: - bus_free(b); - return r; -} - -int bus_set_address_system_remote(sd_bus *b, const char *host) { - _cleanup_free_ char *e = NULL; - char *m = NULL, *c = NULL; - - assert(b); - assert(host); - - /* Let's see if we shall enter some container */ - m = strchr(host, ':'); - if (m) { - m++; - - /* Let's make sure this is not a port of some kind, - * and is a valid machine name. */ - if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) { - char *t; - - /* Cut out the host part */ - t = strndupa(host, m - host - 1); - e = bus_address_escape(t); - if (!e) - return -ENOMEM; - - c = strjoina(",argv4=--machine=", m); - } - } - - if (!e) { - e = bus_address_escape(host); - if (!e) - return -ENOMEM; - } - - b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c, NULL); - if (!b->address) - return -ENOMEM; - - return 0; - } - -_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { - sd_bus *bus; - int r; - - assert_return(host, -EINVAL); - assert_return(ret, -EINVAL); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = bus_set_address_system_remote(bus, host); - if (r < 0) - goto fail; - - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; - - r = sd_bus_start(bus); - if (r < 0) - goto fail; - - *ret = bus; - return 0; - -fail: - bus_free(bus); - return r; -} - -int bus_set_address_system_machine(sd_bus *b, const char *machine) { - _cleanup_free_ char *e = NULL; - - assert(b); - assert(machine); - - e = bus_address_escape(machine); - if (!e) - return -ENOMEM; - - b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); - if (!b->address) - return -ENOMEM; - - return 0; -} - -_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { - sd_bus *bus; - int r; - - assert_return(machine, -EINVAL); - assert_return(ret, -EINVAL); - assert_return(machine_name_is_valid(machine), -EINVAL); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = bus_set_address_system_machine(bus, machine); - if (r < 0) - goto fail; - - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; - - r = sd_bus_start(bus); - if (r < 0) - goto fail; - - *ret = bus; - return 0; - -fail: - bus_free(bus); - return r; -} - -_public_ void sd_bus_close(sd_bus *bus) { - - if (!bus) - return; - if (bus->state == BUS_CLOSED) - return; - if (bus_pid_changed(bus)) - return; - - bus->state = BUS_CLOSED; - - sd_bus_detach_event(bus); - - /* Drop all queued messages so that they drop references to - * the bus object and the bus may be freed */ - bus_reset_queues(bus); - - if (!bus->is_kernel) - bus_close_fds(bus); - - /* We'll leave the fd open in case this is a kernel bus, since - * there might still be memblocks around that reference this - * bus, and they might need to invoke the KDBUS_CMD_FREE - * ioctl on the fd when they are freed. */ -} - -_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { - - if (!bus) - return NULL; - - sd_bus_flush(bus); - sd_bus_close(bus); - - return sd_bus_unref(bus); -} - -static void bus_enter_closing(sd_bus *bus) { - assert(bus); - - if (bus->state != BUS_OPENING && - bus->state != BUS_AUTHENTICATING && - bus->state != BUS_HELLO && - bus->state != BUS_RUNNING) - return; - - bus->state = BUS_CLOSING; -} - -_public_ sd_bus *sd_bus_ref(sd_bus *bus) { - - if (!bus) - return NULL; - - assert_se(REFCNT_INC(bus->n_ref) >= 2); - - return bus; -} - -_public_ sd_bus *sd_bus_unref(sd_bus *bus) { - unsigned i; - - if (!bus) - return NULL; - - i = REFCNT_DEC(bus->n_ref); - if (i > 0) - return NULL; - - bus_free(bus); - return NULL; -} - -_public_ int sd_bus_is_open(sd_bus *bus) { - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return BUS_IS_OPEN(bus->state); -} - -_public_ int sd_bus_can_send(sd_bus *bus, char type) { - int r; - - assert_return(bus, -EINVAL); - assert_return(bus->state != BUS_UNSET, -ENOTCONN); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - if (type == SD_BUS_TYPE_UNIX_FD) { - if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) - return 0; - - r = bus_ensure_running(bus); - if (r < 0) - return r; - - return bus->can_fds; - } - - return bus_type_is_valid(type); -} - -_public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { - int r; - - assert_return(bus, -EINVAL); - assert_return(id, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_ensure_running(bus); - if (r < 0) - return r; - - *id = bus->server_id; - return 0; -} - -static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { - assert(b); - assert(m); - - if (m->sealed) { - /* If we copy the same message to multiple - * destinations, avoid using the same cookie - * numbers. */ - b->cookie = MAX(b->cookie, BUS_MESSAGE_COOKIE(m)); - return 0; - } - - if (timeout == 0) - timeout = BUS_DEFAULT_TIMEOUT; - - return bus_message_seal(m, ++b->cookie, timeout); -} - -static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) { - bool remarshal = false; - - assert(b); - - /* wrong packet version */ - if (b->message_version != 0 && b->message_version != (*m)->header->version) - remarshal = true; - - /* wrong packet endianness */ - if (b->message_endian != 0 && b->message_endian != (*m)->header->endian) - remarshal = true; - - /* TODO: kdbus-messages received from the kernel contain data which is - * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to - * force remarshaling of the message. Technically, we could just - * recreate the kdbus message, but that is non-trivial as other parts of - * the message refer to m->kdbus already. This should be fixed! */ - if ((*m)->kdbus && (*m)->release_kdbus) - remarshal = true; - - return remarshal ? bus_message_remarshal(b, m) : 0; -} - -int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { - assert(b); - assert(m); - - /* Fake some timestamps, if they were requested, and not - * already initialized */ - if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { - if (m->realtime <= 0) - m->realtime = now(CLOCK_REALTIME); - - if (m->monotonic <= 0) - m->monotonic = now(CLOCK_MONOTONIC); - } - - /* The bus specification says the serial number cannot be 0, - * hence let's fill something in for synthetic messages. Since - * synthetic messages might have a fake sender and we don't - * want to interfere with the real sender's serial numbers we - * pick a fixed, artificial one. We use (uint32_t) -1 rather - * than (uint64_t) -1 since dbus1 only had 32bit identifiers, - * even though kdbus can do 64bit. */ - return bus_message_seal(m, 0xFFFFFFFFULL, 0); -} - -static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call, size_t *idx) { - int r; - - assert(bus); - assert(m); - - if (bus->is_kernel) - r = bus_kernel_write_message(bus, m, hint_sync_call); - else - r = bus_socket_write_message(bus, m, idx); - - if (r <= 0) - return r; - - if (bus->is_kernel || *idx >= BUS_MESSAGE_SIZE(m)) - log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", - bus_message_type_to_string(m->header->type), - strna(sd_bus_message_get_sender(m)), - strna(sd_bus_message_get_destination(m)), - strna(sd_bus_message_get_path(m)), - strna(sd_bus_message_get_interface(m)), - strna(sd_bus_message_get_member(m)), - BUS_MESSAGE_COOKIE(m), - m->reply_cookie, - strna(m->error.message)); - - return r; -} - -static int dispatch_wqueue(sd_bus *bus) { - int r, ret = 0; - - assert(bus); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - while (bus->wqueue_size > 0) { - - r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex); - if (r < 0) - return r; - else if (r == 0) - /* Didn't do anything this time */ - return ret; - else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) { - /* Fully written. Let's drop the entry from - * the queue. - * - * This isn't particularly optimized, but - * well, this is supposed to be our worst-case - * buffer only, and the socket buffer is - * supposed to be our primary buffer, and if - * it got full, then all bets are off - * anyway. */ - - bus->wqueue_size--; - sd_bus_message_unref(bus->wqueue[0]); - memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size); - bus->windex = 0; - - ret = 1; - } - } - - return ret; -} - -static int bus_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { - assert(bus); - - if (bus->is_kernel) - return bus_kernel_read_message(bus, hint_priority, priority); - else - return bus_socket_read_message(bus); -} - -int bus_rqueue_make_room(sd_bus *bus) { - assert(bus); - - if (bus->rqueue_size >= BUS_RQUEUE_MAX) - return -ENOBUFS; - - if (!GREEDY_REALLOC(bus->rqueue, bus->rqueue_allocated, bus->rqueue_size + 1)) - return -ENOMEM; - - return 0; -} - -static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **m) { - int r, ret = 0; - - assert(bus); - assert(m); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - /* Note that the priority logic is only available on kdbus, - * where the rqueue is unused. We check the rqueue here - * anyway, because it's simple... */ - - for (;;) { - if (bus->rqueue_size > 0) { - /* Dispatch a queued message */ - - *m = bus->rqueue[0]; - bus->rqueue_size--; - memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size); - return 1; - } - - /* Try to read a new message */ - r = bus_read_message(bus, hint_priority, priority); - if (r < 0) - return r; - if (r == 0) - return ret; - - ret = 1; - } -} - -static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); - int r; - - assert_return(m, -EINVAL); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (m->n_fds > 0) { - r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD); - if (r < 0) - return r; - if (r == 0) - return -EOPNOTSUPP; - } - - /* If the cookie number isn't kept, then we know that no reply - * is expected */ - if (!cookie && !m->sealed) - m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; - - r = bus_seal_message(bus, m, 0); - if (r < 0) - return r; - - /* Remarshall if we have to. This will possibly unref the - * message and place a replacement in m */ - r = bus_remarshal_message(bus, &m); - if (r < 0) - return r; - - /* If this is a reply and no reply was requested, then let's - * suppress this, if we can */ - if (m->dont_send) - goto finish; - - if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { - size_t idx = 0; - - r = bus_write_message(bus, m, hint_sync_call, &idx); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - return -ECONNRESET; - } - - return r; - } - - if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) { - /* Wasn't fully written. So let's remember how - * much was written. Note that the first entry - * of the wqueue array is always allocated so - * that we always can remember how much was - * written. */ - bus->wqueue[0] = sd_bus_message_ref(m); - bus->wqueue_size = 1; - bus->windex = idx; - } - - } else { - /* Just append it to the queue. */ - - if (bus->wqueue_size >= BUS_WQUEUE_MAX) - return -ENOBUFS; - - if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1)) - return -ENOMEM; - - bus->wqueue[bus->wqueue_size++] = sd_bus_message_ref(m); - } - -finish: - if (cookie) - *cookie = BUS_MESSAGE_COOKIE(m); - - return 1; -} - -_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { - return bus_send_internal(bus, m, cookie, false); -} - -_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { - int r; - - assert_return(m, -EINVAL); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (!streq_ptr(m->destination, destination)) { - - if (!destination) - return -EEXIST; - - r = sd_bus_message_set_destination(m, destination); - if (r < 0) - return r; - } - - return sd_bus_send(bus, m, cookie); -} - -static usec_t calc_elapse(uint64_t usec) { - if (usec == (uint64_t) -1) - return 0; - - return now(CLOCK_MONOTONIC) + usec; -} - -static int timeout_compare(const void *a, const void *b) { - const struct reply_callback *x = a, *y = b; - - if (x->timeout != 0 && y->timeout == 0) - return -1; - - if (x->timeout == 0 && y->timeout != 0) - return 1; - - if (x->timeout < y->timeout) - return -1; - - if (x->timeout > y->timeout) - return 1; - - return 0; -} - -_public_ int sd_bus_call_async( - sd_bus *bus, - sd_bus_slot **slot, - sd_bus_message *_m, - sd_bus_message_handler_t callback, - void *userdata, - uint64_t usec) { - - _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); - assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); - assert_return(callback, -EINVAL); - - if (!bus) - bus = m->bus; - - assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); - if (r < 0) - return r; - - r = prioq_ensure_allocated(&bus->reply_callbacks_prioq, timeout_compare); - if (r < 0) - return r; - - r = bus_seal_message(bus, m, usec); - if (r < 0) - return r; - - r = bus_remarshal_message(bus, &m); - if (r < 0) - return r; - - s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); - if (!s) - return -ENOMEM; - - s->reply_callback.callback = callback; - - s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); - r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); - if (r < 0) { - s->reply_callback.cookie = 0; - return r; - } - - s->reply_callback.timeout = calc_elapse(m->timeout); - if (s->reply_callback.timeout != 0) { - r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); - if (r < 0) { - s->reply_callback.timeout = 0; - return r; - } - } - - r = sd_bus_send(bus, m, &s->reply_callback.cookie); - if (r < 0) - return r; - - if (slot) - *slot = s; - s = NULL; - - return r; -} - -int bus_ensure_running(sd_bus *bus) { - int r; - - assert(bus); - - if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED || bus->state == BUS_CLOSING) - return -ENOTCONN; - if (bus->state == BUS_RUNNING) - return 1; - - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (bus->state == BUS_RUNNING) - return 1; - if (r > 0) - continue; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } -} - -_public_ int sd_bus_call( - sd_bus *bus, - sd_bus_message *_m, - uint64_t usec, - sd_bus_error *error, - sd_bus_message **reply) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); - usec_t timeout; - uint64_t cookie; - unsigned i; - int r; - - bus_assert_return(m, -EINVAL, error); - bus_assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL, error); - bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error); - bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error); - - if (!bus) - bus = m->bus; - - bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); - bus_assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS, error); - - if (!BUS_IS_OPEN(bus->state)) { - r = -ENOTCONN; - goto fail; - } - - r = bus_ensure_running(bus); - if (r < 0) - goto fail; - - i = bus->rqueue_size; - - r = bus_seal_message(bus, m, usec); - if (r < 0) - goto fail; - - r = bus_remarshal_message(bus, &m); - if (r < 0) - goto fail; - - r = bus_send_internal(bus, m, &cookie, true); - if (r < 0) - goto fail; - - timeout = calc_elapse(m->timeout); - - for (;;) { - usec_t left; - - while (i < bus->rqueue_size) { - sd_bus_message *incoming = NULL; - - incoming = bus->rqueue[i]; - - if (incoming->reply_cookie == cookie) { - /* Found a match! */ - - memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); - bus->rqueue_size--; - log_debug_bus_message(incoming); - - if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { - - if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { - if (reply) - *reply = incoming; - else - sd_bus_message_unref(incoming); - - return 1; - } - - r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); - sd_bus_message_unref(incoming); - return r; - - } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { - r = sd_bus_error_copy(error, &incoming->error); - sd_bus_message_unref(incoming); - return r; - } else { - r = -EIO; - goto fail; - } - - } else if (BUS_MESSAGE_COOKIE(incoming) == cookie && - bus->unique_name && - incoming->sender && - streq(bus->unique_name, incoming->sender)) { - - memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); - bus->rqueue_size--; - - /* Our own message? Somebody is trying - * to send its own client a message, - * let's not dead-lock, let's fail - * immediately. */ - - sd_bus_message_unref(incoming); - r = -ELOOP; - goto fail; - } - - /* Try to read more, right-away */ - i++; - } - - r = bus_read_message(bus, false, 0); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = -ECONNRESET; - } - - goto fail; - } - if (r > 0) - continue; - - if (timeout > 0) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - if (n >= timeout) { - r = -ETIMEDOUT; - goto fail; - } - - left = timeout - n; - } else - left = (uint64_t) -1; - - r = bus_poll(bus, true, left); - if (r < 0) - goto fail; - if (r == 0) { - r = -ETIMEDOUT; - goto fail; - } - - r = dispatch_wqueue(bus); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = -ECONNRESET; - } - - goto fail; - } - } - -fail: - return sd_bus_error_set_errno(error, r); -} - -_public_ int sd_bus_get_fd(sd_bus *bus) { - - assert_return(bus, -EINVAL); - assert_return(bus->input_fd == bus->output_fd, -EPERM); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->input_fd; -} - -_public_ int sd_bus_get_events(sd_bus *bus) { - int flags = 0; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) - return -ENOTCONN; - - if (bus->state == BUS_OPENING) - flags |= POLLOUT; - else if (bus->state == BUS_AUTHENTICATING) { - - if (bus_socket_auth_needs_write(bus)) - flags |= POLLOUT; - - flags |= POLLIN; - - } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) { - if (bus->rqueue_size <= 0) - flags |= POLLIN; - if (bus->wqueue_size > 0) - flags |= POLLOUT; - } - - return flags; -} - -_public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { - struct reply_callback *c; - - assert_return(bus, -EINVAL); - assert_return(timeout_usec, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) - return -ENOTCONN; - - if (bus->track_queue) { - *timeout_usec = 0; - return 1; - } - - if (bus->state == BUS_CLOSING) { - *timeout_usec = 0; - return 1; - } - - if (bus->state == BUS_AUTHENTICATING) { - *timeout_usec = bus->auth_timeout; - return 1; - } - - if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - if (bus->rqueue_size > 0) { - *timeout_usec = 0; - return 1; - } - - c = prioq_peek(bus->reply_callbacks_prioq); - if (!c) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - if (c->timeout == 0) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - *timeout_usec = c->timeout; - return 1; -} - -static int process_timeout(sd_bus *bus) { - _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; - int r; - - assert(bus); - - c = prioq_peek(bus->reply_callbacks_prioq); - if (!c) - return 0; - - n = now(CLOCK_MONOTONIC); - if (c->timeout > n) - return 0; - - r = bus_message_new_synthetic_error( - bus, - c->cookie, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out"), - &m); - if (r < 0) - return r; - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); - c->timeout = 0; - - ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - bus->iteration_counter++; - - bus->current_message = m; - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - bus->current_message = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); -} - -static int process_hello(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - if (bus->state != BUS_HELLO) - return 0; - - /* Let's make sure the first message on the bus is the HELLO - * reply. But note that we don't actually parse the message - * here (we leave that to the usual handling), we just verify - * we don't let any earlier msg through. */ - - if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && - m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) - return -EIO; - - if (m->reply_cookie != 1) - return -EIO; - - return 0; -} - -static int process_reply(sd_bus *bus, sd_bus_message *m) { - _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; - - assert(bus); - assert(m); - - if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && - m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) - return 0; - - if (bus->is_kernel && (bus->hello_flags & KDBUS_HELLO_MONITOR)) - return 0; - - if (m->destination && bus->unique_name && !streq_ptr(m->destination, bus->unique_name)) - return 0; - - c = ordered_hashmap_remove(bus->reply_callbacks, &m->reply_cookie); - if (!c) - return 0; - - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { - - /* If the reply contained a file descriptor which we - * didn't want we pass an error instead. */ - - r = bus_message_new_synthetic_error( - bus, - m->reply_cookie, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptor"), - &synthetic_reply); - if (r < 0) - return r; - - /* Copy over original timestamp */ - synthetic_reply->realtime = m->realtime; - synthetic_reply->monotonic = m->monotonic; - synthetic_reply->seqnum = m->seqnum; - - r = bus_seal_synthetic_message(bus, synthetic_reply); - if (r < 0) - return r; - - m = synthetic_reply; - } else { - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - } - - if (c->timeout != 0) { - prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; - } - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); -} - -static int process_filter(sd_bus *bus, sd_bus_message *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - struct filter_callback *l; - int r; - - assert(bus); - assert(m); - - do { - bus->filter_callbacks_modified = false; - - LIST_FOREACH(callbacks, l, bus->filter_callbacks) { - sd_bus_slot *slot; - - if (bus->filter_callbacks_modified) - break; - - /* Don't run this more than once per iteration */ - if (l->last_iteration == bus->iteration_counter) - continue; - - l->last_iteration = bus->iteration_counter; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - slot = container_of(l, sd_bus_slot, filter_callback); - - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = l->callback; - bus->current_userdata = slot->userdata; - r = l->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = sd_bus_slot_unref(slot); - - r = bus_maybe_reply_error(m, r, &error_buffer); - if (r != 0) - return r; - - } - - } while (bus->filter_callbacks_modified); - - return 0; -} - -static int process_match(sd_bus *bus, sd_bus_message *m) { - int r; - - assert(bus); - assert(m); - - do { - bus->match_callbacks_modified = false; - - r = bus_match_run(bus, &bus->match_callbacks, m); - if (r != 0) - return r; - - } while (bus->match_callbacks_modified); - - return 0; -} - -static int process_builtin(sd_bus *bus, sd_bus_message *m) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(bus); - assert(m); - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - if (bus->manual_peer_interface) - return 0; - - if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) - return 0; - - if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer")) - return 0; - - if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 1; - - if (streq_ptr(m->member, "Ping")) - r = sd_bus_message_new_method_return(m, &reply); - else if (streq_ptr(m->member, "GetMachineId")) { - sd_id128_t id; - char sid[33]; - - r = sd_id128_get_machine(&id); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid)); - } else { - r = sd_bus_message_new_method_errorf( - m, &reply, - SD_BUS_ERROR_UNKNOWN_METHOD, - "Unknown method '%s' on interface '%s'.", m->member, m->interface); - } - - if (r < 0) - return r; - - r = sd_bus_send(bus, reply, NULL); - if (r < 0) - return r; - - return 1; -} - -static int process_fd_check(sd_bus *bus, sd_bus_message *m) { - assert(bus); - assert(m); - - /* If we got a message with a file descriptor which we didn't - * want to accept, then let's drop it. How can this even - * happen? For example, when the kernel queues a message into - * an activatable names's queue which allows fds, and then is - * delivered to us later even though we ourselves did not - * negotiate it. */ - - if (bus->hello_flags & KDBUS_HELLO_MONITOR) - return 0; - - if (m->n_fds <= 0) - return 0; - - if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) - return 0; - - if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) - return 1; /* just eat it up */ - - return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Message contains file descriptors, which I cannot accept. Sorry."); -} - -static int process_message(sd_bus *bus, sd_bus_message *m) { - int r; - - assert(bus); - assert(m); - - bus->current_message = m; - bus->iteration_counter++; - - log_debug_bus_message(m); - - r = process_hello(bus, m); - if (r != 0) - goto finish; - - r = process_reply(bus, m); - if (r != 0) - goto finish; - - r = process_fd_check(bus, m); - if (r != 0) - goto finish; - - r = process_filter(bus, m); - if (r != 0) - goto finish; - - r = process_match(bus, m); - if (r != 0) - goto finish; - - r = process_builtin(bus, m); - if (r != 0) - goto finish; - - r = bus_process_object(bus, m); - -finish: - bus->current_message = NULL; - return r; -} - -static int dispatch_track(sd_bus *bus) { - assert(bus); - - if (!bus->track_queue) - return 0; - - bus_track_dispatch(bus->track_queue); - return 1; -} - -static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(bus); - assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); - - r = process_timeout(bus); - if (r != 0) - goto null_message; - - r = dispatch_wqueue(bus); - if (r != 0) - goto null_message; - - r = dispatch_track(bus); - if (r != 0) - goto null_message; - - r = dispatch_rqueue(bus, hint_priority, priority, &m); - if (r < 0) - return r; - if (!m) - goto null_message; - - r = process_message(bus, m); - if (r != 0) - goto null_message; - - if (ret) { - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - *ret = m; - m = NULL; - return 1; - } - - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) { - - log_debug("Unprocessed message call sender=%s object=%s interface=%s member=%s", - strna(sd_bus_message_get_sender(m)), - strna(sd_bus_message_get_path(m)), - strna(sd_bus_message_get_interface(m)), - strna(sd_bus_message_get_member(m))); - - r = sd_bus_reply_method_errorf( - m, - SD_BUS_ERROR_UNKNOWN_OBJECT, - "Unknown object '%s'.", m->path); - if (r < 0) - return r; - } - - return 1; - -null_message: - if (r >= 0 && ret) - *ret = NULL; - - return r; -} - -static int process_closing(sd_bus *bus, sd_bus_message **ret) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - struct reply_callback *c; - int r; - - assert(bus); - assert(bus->state == BUS_CLOSING); - - c = ordered_hashmap_first(bus->reply_callbacks); - if (c) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - sd_bus_slot *slot; - - /* First, fail all outstanding method calls */ - r = bus_message_new_synthetic_error( - bus, - c->cookie, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"), - &m); - if (r < 0) - return r; - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - if (c->timeout != 0) { - prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; - } - - ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); - c->cookie = 0; - - slot = container_of(c, sd_bus_slot, reply_callback); - - bus->iteration_counter++; - - bus->current_message = m; - bus->current_slot = sd_bus_slot_ref(slot); - bus->current_handler = c->callback; - bus->current_userdata = slot->userdata; - r = c->callback(m, slot->userdata, &error_buffer); - bus->current_userdata = NULL; - bus->current_handler = NULL; - bus->current_slot = NULL; - bus->current_message = NULL; - - if (slot->floating) { - bus_slot_disconnect(slot); - sd_bus_slot_unref(slot); - } - - sd_bus_slot_unref(slot); - - return bus_maybe_reply_error(m, r, &error_buffer); - } - - /* Then, synthesize a Disconnected message */ - r = sd_bus_message_new_signal( - bus, - &m, - "/org/freedesktop/DBus/Local", - "org.freedesktop.DBus.Local", - "Disconnected"); - if (r < 0) - return r; - - bus_message_set_sender_local(bus, m); - - r = bus_seal_synthetic_message(bus, m); - if (r < 0) - return r; - - sd_bus_close(bus); - - bus->current_message = m; - bus->iteration_counter++; - - r = process_filter(bus, m); - if (r != 0) - goto finish; - - r = process_match(bus, m); - if (r != 0) - goto finish; - - if (ret) { - *ret = m; - m = NULL; - } - - r = 1; - -finish: - bus->current_message = NULL; - - return r; -} - -static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { - BUS_DONT_DESTROY(bus); - int r; - - /* Returns 0 when we didn't do anything. This should cause the - * caller to invoke sd_bus_wait() before returning the next - * time. Returns > 0 when we did something, which possibly - * means *ret is filled in with an unprocessed message. */ - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - /* We don't allow recursively invoking sd_bus_process(). */ - assert_return(!bus->current_message, -EBUSY); - assert(!bus->current_slot); - - switch (bus->state) { - - case BUS_UNSET: - return -ENOTCONN; - - case BUS_CLOSED: - return -ECONNRESET; - - case BUS_OPENING: - r = bus_socket_process_opening(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - if (ret) - *ret = NULL; - return r; - - case BUS_AUTHENTICATING: - r = bus_socket_process_authenticating(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - - if (ret) - *ret = NULL; - - return r; - - case BUS_RUNNING: - case BUS_HELLO: - r = process_running(bus, hint_priority, priority, ret); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - - if (ret) - *ret = NULL; - } - - return r; - - case BUS_CLOSING: - return process_closing(bus, ret); - } - - assert_not_reached("Unknown state"); -} - -_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { - return bus_process_internal(bus, false, 0, ret); -} - -_public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) { - return bus_process_internal(bus, true, priority, ret); -} - -static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { - struct pollfd p[2] = {}; - int r, e, n; - struct timespec ts; - usec_t m = USEC_INFINITY; - - assert(bus); - - if (bus->state == BUS_CLOSING) - return 1; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - e = sd_bus_get_events(bus); - if (e < 0) - return e; - - if (need_more) - /* The caller really needs some more data, he doesn't - * care about what's already read, or any timeouts - * except its own. */ - e |= POLLIN; - else { - usec_t until; - /* The caller wants to process if there's something to - * process, but doesn't care otherwise */ - - r = sd_bus_get_timeout(bus, &until); - if (r < 0) - return r; - if (r > 0) { - usec_t nw; - nw = now(CLOCK_MONOTONIC); - m = until > nw ? until - nw : 0; - } - } - - if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) - m = timeout_usec; - - p[0].fd = bus->input_fd; - if (bus->output_fd == bus->input_fd) { - p[0].events = e; - n = 1; - } else { - p[0].events = e & POLLIN; - p[1].fd = bus->output_fd; - p[1].events = e & POLLOUT; - n = 2; - } - - r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); - if (r < 0) - return -errno; - - return r > 0 ? 1 : 0; -} - -_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->state == BUS_CLOSING) - return 0; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->rqueue_size > 0) - return 0; - - return bus_poll(bus, false, timeout_usec); -} - -_public_ int sd_bus_flush(sd_bus *bus) { - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->state == BUS_CLOSING) - return 0; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - r = bus_ensure_running(bus); - if (r < 0) - return r; - - if (bus->wqueue_size <= 0) - return 0; - - for (;;) { - r = dispatch_wqueue(bus); - if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - return -ECONNRESET; - } - - return r; - } - - if (bus->wqueue_size <= 0) - return 0; - - r = bus_poll(bus, false, (uint64_t) -1); - if (r < 0) - return r; - } -} - -_public_ int sd_bus_add_filter( - sd_bus *bus, - sd_bus_slot **slot, - sd_bus_message_handler_t callback, - void *userdata) { - - sd_bus_slot *s; - - assert_return(bus, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - s = bus_slot_allocate(bus, !slot, BUS_FILTER_CALLBACK, sizeof(struct filter_callback), userdata); - if (!s) - return -ENOMEM; - - s->filter_callback.callback = callback; - - bus->filter_callbacks_modified = true; - LIST_PREPEND(callbacks, bus->filter_callbacks, &s->filter_callback); - - if (slot) - *slot = s; - - return 0; -} - -_public_ int sd_bus_add_match( - sd_bus *bus, - sd_bus_slot **slot, - const char *match, - sd_bus_message_handler_t callback, - void *userdata) { - - struct bus_match_component *components = NULL; - unsigned n_components = 0; - sd_bus_slot *s = NULL; - int r = 0; - - assert_return(bus, -EINVAL); - assert_return(match, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - goto finish; - - s = bus_slot_allocate(bus, !slot, BUS_MATCH_CALLBACK, sizeof(struct match_callback), userdata); - if (!s) { - r = -ENOMEM; - goto finish; - } - - s->match_callback.callback = callback; - s->match_callback.cookie = ++bus->match_cookie; - - if (bus->bus_client) { - enum bus_match_scope scope; - - scope = bus_match_get_scope(components, n_components); - - /* Do not install server-side matches for matches - * against the local service, interface or bus - * path. */ - if (scope != BUS_MATCH_LOCAL) { - - if (!bus->is_kernel) { - /* When this is not a kernel transport, we - * store the original match string, so that we - * can use it to remove the match again */ - - s->match_callback.match_string = strdup(match); - if (!s->match_callback.match_string) { - r = -ENOMEM; - goto finish; - } - } - - r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components, s->match_callback.cookie); - if (r < 0) - goto finish; - - s->match_added = true; - } - } - - bus->match_callbacks_modified = true; - r = bus_match_add(&bus->match_callbacks, components, n_components, &s->match_callback); - if (r < 0) - goto finish; - - if (slot) - *slot = s; - s = NULL; - -finish: - bus_match_parse_free(components, n_components); - sd_bus_slot_unref(s); - - return r; -} - -int bus_remove_match_by_string( - sd_bus *bus, - const char *match, - sd_bus_message_handler_t callback, - void *userdata) { - - struct bus_match_component *components = NULL; - unsigned n_components = 0; - struct match_callback *c; - int r = 0; - - assert_return(bus, -EINVAL); - assert_return(match, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - goto finish; - - r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); - if (r <= 0) - goto finish; - - sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); - -finish: - bus_match_parse_free(components, n_components); - - return r; -} - -bool bus_pid_changed(sd_bus *bus) { - assert(bus); - - /* We don't support people creating a bus connection and - * keeping it around over a fork(). Let's complain. */ - - return bus->original_pid != getpid(); -} - -static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_bus *bus = userdata; - int r; - - assert(bus); - - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - - return 1; -} - -static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { - sd_bus *bus = userdata; - int r; - - assert(bus); - - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - - return 1; -} - -static int prepare_callback(sd_event_source *s, void *userdata) { - sd_bus *bus = userdata; - int r, e; - usec_t until; - - assert(s); - assert(bus); - - e = sd_bus_get_events(bus); - if (e < 0) - return e; - - if (bus->output_fd != bus->input_fd) { - - r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN); - if (r < 0) - return r; - - r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT); - if (r < 0) - return r; - } else { - r = sd_event_source_set_io_events(bus->input_io_event_source, e); - if (r < 0) - return r; - } - - r = sd_bus_get_timeout(bus, &until); - if (r < 0) - return r; - if (r > 0) { - int j; - - j = sd_event_source_set_time(bus->time_event_source, until); - if (j < 0) - return j; - } - - r = sd_event_source_set_enabled(bus->time_event_source, r > 0); - if (r < 0) - return r; - - return 1; -} - -static int quit_callback(sd_event_source *event, void *userdata) { - sd_bus *bus = userdata; - - assert(event); - - sd_bus_flush(bus); - sd_bus_close(bus); - - return 1; -} - -static int attach_io_events(sd_bus *bus) { - int r; - - assert(bus); - - if (bus->input_fd < 0) - return 0; - - if (!bus->event) - return 0; - - if (!bus->input_io_event_source) { - r = sd_event_add_io(bus->event, &bus->input_io_event_source, bus->input_fd, 0, io_callback, bus); - if (r < 0) - return r; - - r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback); - if (r < 0) - return r; - - r = sd_event_source_set_priority(bus->input_io_event_source, bus->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(bus->input_io_event_source, "bus-input"); - } else - r = sd_event_source_set_io_fd(bus->input_io_event_source, bus->input_fd); - - if (r < 0) - return r; - - if (bus->output_fd != bus->input_fd) { - assert(bus->output_fd >= 0); - - if (!bus->output_io_event_source) { - r = sd_event_add_io(bus->event, &bus->output_io_event_source, bus->output_fd, 0, io_callback, bus); - if (r < 0) - return r; - - r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(bus->input_io_event_source, "bus-output"); - } else - r = sd_event_source_set_io_fd(bus->output_io_event_source, bus->output_fd); - - if (r < 0) - return r; - } - - return 0; -} - -static void detach_io_events(sd_bus *bus) { - assert(bus); - - if (bus->input_io_event_source) { - sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF); - bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source); - } - - if (bus->output_io_event_source) { - sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF); - bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source); - } -} - -_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { - int r; - - assert_return(bus, -EINVAL); - assert_return(!bus->event, -EBUSY); - - assert(!bus->input_io_event_source); - assert(!bus->output_io_event_source); - assert(!bus->time_event_source); - - if (event) - bus->event = sd_event_ref(event); - else { - r = sd_event_default(&bus->event); - if (r < 0) - return r; - } - - bus->event_priority = priority; - - r = sd_event_add_time(bus->event, &bus->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, bus); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(bus->time_event_source, priority); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(bus->time_event_source, "bus-time"); - if (r < 0) - goto fail; - - r = sd_event_add_exit(bus->event, &bus->quit_event_source, quit_callback, bus); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(bus->quit_event_source, "bus-exit"); - if (r < 0) - goto fail; - - r = attach_io_events(bus); - if (r < 0) - goto fail; - - return 0; - -fail: - sd_bus_detach_event(bus); - return r; -} - -_public_ int sd_bus_detach_event(sd_bus *bus) { - assert_return(bus, -EINVAL); - - if (!bus->event) - return 0; - - detach_io_events(bus); - - if (bus->time_event_source) { - sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF); - bus->time_event_source = sd_event_source_unref(bus->time_event_source); - } - - if (bus->quit_event_source) { - sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF); - bus->quit_event_source = sd_event_source_unref(bus->quit_event_source); - } - - bus->event = sd_event_unref(bus->event); - return 1; -} - -_public_ sd_event* sd_bus_get_event(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->event; -} - -_public_ sd_bus_message* sd_bus_get_current_message(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_message; -} - -_public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_slot; -} - -_public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_handler; -} - -_public_ void* sd_bus_get_current_userdata(sd_bus *bus) { - assert_return(bus, NULL); - - return bus->current_userdata; -} - -static int bus_default(int (*bus_open)(sd_bus **), sd_bus **default_bus, sd_bus **ret) { - sd_bus *b = NULL; - int r; - - assert(bus_open); - assert(default_bus); - - if (!ret) - return !!*default_bus; - - if (*default_bus) { - *ret = sd_bus_ref(*default_bus); - return 0; - } - - r = bus_open(&b); - if (r < 0) - return r; - - b->default_bus_ptr = default_bus; - b->tid = gettid(); - *default_bus = b; - - *ret = b; - return 1; -} - -_public_ int sd_bus_default_system(sd_bus **ret) { - return bus_default(sd_bus_open_system, &default_system_bus, ret); -} - - -_public_ int sd_bus_default_user(sd_bus **ret) { - return bus_default(sd_bus_open_user, &default_user_bus, ret); -} - -_public_ int sd_bus_default(sd_bus **ret) { - - const char *e; - - /* Let's try our best to reuse another cached connection. If - * the starter bus type is set, connect via our normal - * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that - * we can share the connection with the user/system default - * bus. */ - - e = secure_getenv("DBUS_STARTER_BUS_TYPE"); - if (e) { - if (streq(e, "system")) - return sd_bus_default_system(ret); - else if (STR_IN_SET(e, "user", "session")) - return sd_bus_default_user(ret); - } - - /* No type is specified, so we have not other option than to - * use the starter address if it is set. */ - - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (e) { - - return bus_default(sd_bus_open, &default_starter_bus, ret); - } - - /* Finally, if nothing is set use the cached connection for - * the right scope */ - - if (cg_pid_get_owner_uid(0, NULL) >= 0) - return sd_bus_default_user(ret); - else - return sd_bus_default_system(ret); -} - -_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) { - assert_return(b, -EINVAL); - assert_return(tid, -EINVAL); - assert_return(!bus_pid_changed(b), -ECHILD); - - if (b->tid != 0) { - *tid = b->tid; - return 0; - } - - if (b->event) - return sd_event_get_tid(b->event, tid); - - return -ENXIO; -} - -_public_ int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path) { - _cleanup_free_ char *e = NULL; - char *ret; - - assert_return(object_path_is_valid(prefix), -EINVAL); - assert_return(external_id, -EINVAL); - assert_return(ret_path, -EINVAL); - - e = bus_label_escape(external_id); - if (!e) - return -ENOMEM; - - ret = strjoin(prefix, "/", e, NULL); - if (!ret) - return -ENOMEM; - - *ret_path = ret; - return 0; -} - -_public_ int sd_bus_path_decode(const char *path, const char *prefix, char **external_id) { - const char *e; - char *ret; - - assert_return(object_path_is_valid(path), -EINVAL); - assert_return(object_path_is_valid(prefix), -EINVAL); - assert_return(external_id, -EINVAL); - - e = object_path_startswith(path, prefix); - if (!e) { - *external_id = NULL; - return 0; - } - - ret = bus_label_unescape(e); - if (!ret) - return -ENOMEM; - - *external_id = ret; - 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; - - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->is_kernel) - return -EOPNOTSUPP; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (bus->rqueue_size > 0) - return -EBUSY; - - if (bus->wqueue_size > 0) - return -EBUSY; - - r = bus_kernel_try_close(bus); - if (r < 0) - return r; - - sd_bus_close(bus); - return 0; -} - -_public_ int sd_bus_get_description(sd_bus *bus, const char **description) { - assert_return(bus, -EINVAL); - assert_return(description, -EINVAL); - assert_return(bus->description, -ENXIO); - assert_return(!bus_pid_changed(bus), -ECHILD); - - *description = bus->description; - return 0; -} - -int bus_get_root_path(sd_bus *bus) { - int r; - - if (bus->cgroup_root) - return 0; - - r = cg_get_root_path(&bus->cgroup_root); - if (r == -ENOENT) { - bus->cgroup_root = strdup("/"); - if (!bus->cgroup_root) - return -ENOMEM; - - r = 0; - } - - return r; -} - -_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { - int r; - - assert_return(bus, -EINVAL); - assert_return(scope, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->is_kernel) { - _cleanup_free_ char *n = NULL; - const char *dash; - - r = bus_kernel_get_bus_name(bus, &n); - if (r < 0) - return r; - - if (streq(n, "0-system")) { - *scope = "system"; - return 0; - } - - dash = strchr(n, '-'); - if (streq_ptr(dash, "-user")) { - *scope = "user"; - return 0; - } - } - - if (bus->is_user) { - *scope = "user"; - return 0; - } - - if (bus->is_system) { - *scope = "system"; - return 0; - } - - return -ENODATA; -} - -_public_ int sd_bus_get_address(sd_bus *bus, const char **address) { - - assert_return(bus, -EINVAL); - assert_return(address, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (bus->address) { - *address = bus->address; - return 0; - } - - return -ENODATA; -} - -_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { - assert_return(bus, -EINVAL); - assert_return(mask, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - *mask = bus->creds_mask; - return 0; -} - -_public_ int sd_bus_is_bus_client(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->bus_client; -} - -_public_ int sd_bus_is_server(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->is_server; -} - -_public_ int sd_bus_is_anonymous(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->anonymous_auth; -} - -_public_ int sd_bus_is_trusted(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return bus->trusted; -} - -_public_ int sd_bus_is_monitor(sd_bus *bus) { - assert_return(bus, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); -} - -static void flush_close(sd_bus *bus) { - if (!bus) - return; - - /* Flushes and closes the specified bus. We take a ref before, - * to ensure the flushing does not cause the bus to be - * unreferenced. */ - - sd_bus_flush_close_unref(sd_bus_ref(bus)); -} - -_public_ void sd_bus_default_flush_close(void) { - flush_close(default_starter_bus); - flush_close(default_user_bus); - flush_close(default_system_bus); -} diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c deleted file mode 100644 index 56ac2ab3dd..0000000000 --- a/src/libsystemd/sd-bus/test-bus-benchmark.c +++ /dev/null @@ -1,371 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.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) - -static usec_t arg_loop_usec = 100 * USEC_PER_MSEC; - -typedef enum Type { - TYPE_KDBUS, - TYPE_LEGACY, - TYPE_DIRECT, -} Type; - -static void server(sd_bus *b, size_t *result) { - int r; - - for (;;) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - r = sd_bus_process(b, &m); - assert_se(r >= 0); - - if (r == 0) - assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0); - if (!m) - continue; - - if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping")) - assert_se(sd_bus_reply_method_return(m, NULL) >= 0); - else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) { - const void *p; - size_t sz; - - /* Make sure the mmap is mapped */ - assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) { - uint64_t res; - assert_se(sd_bus_message_read(m, "t", &res) > 0); - - *result = res; - return; - - } else if (!sd_bus_message_is_signal(m, NULL, NULL)) - assert_not_reached("Unknown method"); - } -} - -static void transaction(sd_bus *b, size_t sz, const char *server_name) { - _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); - assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0); - - memset(p, 0x80, sz); - - assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0); -} - -static void client_bisect(const char *address, const char *server_name) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; - size_t lsize, rsize, csize; - sd_bus *b; - int r; - - r = sd_bus_new(&b); - assert_se(r >= 0); - - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_start(b); - assert_se(r >= 0); - - r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); - assert_se(r >= 0); - - lsize = 1; - rsize = MAX_SIZE; - - printf("SIZE\tCOPY\tMEMFD\n"); - - for (;;) { - usec_t t; - unsigned n_copying, n_memfd; - - csize = (lsize + rsize) / 2; - - if (csize <= lsize) - break; - - if (csize <= 0) - break; - - printf("%zu\t", csize); - - b->use_memfd = 0; - - t = now(CLOCK_MONOTONIC); - for (n_copying = 0;; n_copying++) { - transaction(b, csize, server_name); - if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) - break; - } - printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); - - b->use_memfd = -1; - - t = now(CLOCK_MONOTONIC); - for (n_memfd = 0;; n_memfd++) { - transaction(b, csize, server_name); - if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) - break; - } - printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec)); - - if (n_copying == n_memfd) - break; - - if (n_copying > n_memfd) - lsize = csize; - else - rsize = csize; - } - - b->use_memfd = 1; - assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); - assert_se(sd_bus_message_append(x, "t", csize) >= 0); - assert_se(sd_bus_send(b, x, NULL) >= 0); - - sd_bus_unref(b); -} - -static void client_chart(Type type, const char *address, const char *server_name, int fd) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; - size_t csize; - sd_bus *b; - int r; - - r = sd_bus_new(&b); - assert_se(r >= 0); - - if (type == TYPE_DIRECT) { - r = sd_bus_set_fd(b, fd, fd); - assert_se(r >= 0); - } else { - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_set_bus_client(b, true); - assert_se(r >= 0); - } - - r = sd_bus_start(b); - assert_se(r >= 0); - - r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); - assert_se(r >= 0); - - switch (type) { - case TYPE_KDBUS: - printf("SIZE\tCOPY\tMEMFD\n"); - break; - case TYPE_LEGACY: - printf("SIZE\tLEGACY\n"); - break; - case TYPE_DIRECT: - printf("SIZE\tDIRECT\n"); - break; - } - - for (csize = 1; csize <= MAX_SIZE; csize *= 2) { - usec_t t; - unsigned n_copying, n_memfd; - - printf("%zu\t", csize); - - if (type == TYPE_KDBUS) { - b->use_memfd = 0; - - t = now(CLOCK_MONOTONIC); - for (n_copying = 0;; n_copying++) { - transaction(b, csize, server_name); - if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) - break; - } - - printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); - - b->use_memfd = -1; - } - - t = now(CLOCK_MONOTONIC); - for (n_memfd = 0;; n_memfd++) { - transaction(b, csize, server_name); - if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) - break; - } - - printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec)); - } - - b->use_memfd = 1; - assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); - assert_se(sd_bus_message_append(x, "t", csize) >= 0); - assert_se(sd_bus_send(b, x, NULL) >= 0); - - sd_bus_unref(b); -} - -int main(int argc, char *argv[]) { - enum { - MODE_BISECT, - MODE_CHART, - } mode = MODE_BISECT; - Type type = TYPE_KDBUS; - int i, pair[2] = { -1, -1 }; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL; - _cleanup_close_ int bus_ref = -1; - const char *unique; - cpu_set_t cpuset; - size_t result; - sd_bus *b; - pid_t pid; - int r; - - for (i = 1; i < argc; i++) { - if (streq(argv[i], "chart")) { - mode = MODE_CHART; - continue; - } else if (streq(argv[i], "legacy")) { - type = TYPE_LEGACY; - continue; - } else if (streq(argv[i], "direct")) { - type = TYPE_DIRECT; - continue; - } - - assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0); - } - - assert_se(!MODE_BISECT || TYPE_KDBUS); - - assert_se(arg_loop_usec > 0); - - if (type == TYPE_KDBUS) { - assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); - - bus_ref = bus_kernel_create_bus(name, false, &bus_name); - if (bus_ref == -ENOENT) - exit(EXIT_TEST_SKIP); - - assert_se(bus_ref >= 0); - - address = strappend("kernel:path=", bus_name); - assert_se(address); - } else if (type == TYPE_LEGACY) { - const char *e; - - e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); - assert_se(e); - - address = strdup(e); - assert_se(address); - } - - r = sd_bus_new(&b); - assert_se(r >= 0); - - if (type == TYPE_DIRECT) { - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); - - r = sd_bus_set_fd(b, pair[0], pair[0]); - assert_se(r >= 0); - - r = sd_bus_set_server(b, true, SD_ID128_NULL); - assert_se(r >= 0); - } else { - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_set_bus_client(b, true); - assert_se(r >= 0); - } - - r = sd_bus_start(b); - assert_se(r >= 0); - - if (type != TYPE_DIRECT) { - r = sd_bus_get_unique_name(b, &unique); - assert_se(r >= 0); - - server_name = strdup(unique); - assert_se(server_name); - } - - sync(); - setpriority(PRIO_PROCESS, 0, -19); - - pid = fork(); - assert_se(pid >= 0); - - if (pid == 0) { - CPU_ZERO(&cpuset); - CPU_SET(0, &cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - - safe_close(bus_ref); - sd_bus_unref(b); - - switch (mode) { - case MODE_BISECT: - client_bisect(address, server_name); - break; - - case MODE_CHART: - client_chart(type, address, server_name, pair[1]); - break; - } - - _exit(0); - } - - CPU_ZERO(&cpuset); - CPU_SET(1, &cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - - server(b, &result); - - if (mode == MODE_BISECT) - printf("Copying/memfd are equally fast at %zu bytes\n", result); - - assert_se(waitpid(pid, NULL, 0) == pid); - - safe_close(pair[1]); - sd_bus_unref(b); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c deleted file mode 100644 index 048c0d19e2..0000000000 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ /dev/null @@ -1,560 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.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))); - return 0; -} - -static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - int r; - - if (sd_bus_message_is_method_error(m, NULL)) - return 0; - - if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) { - log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m)); - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) - return log_error_errno(r, "Failed to send reply: %m"); - - return 1; - } - - return 0; -} - -static int server_init(sd_bus **_bus) { - sd_bus *bus = NULL; - sd_id128_t id; - int r; - const char *unique; - - assert_se(_bus); - - r = sd_bus_open_user(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - goto fail; - } - - r = sd_bus_get_bus_id(bus, &id); - if (r < 0) { - log_error_errno(r, "Failed to get server ID: %m"); - goto fail; - } - - r = sd_bus_get_unique_name(bus, &unique); - if (r < 0) { - log_error_errno(r, "Failed to get unique name: %m"); - goto fail; - } - - log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id)); - log_info("Unique ID: %s", unique); - log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h')); - - r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0); - if (r < 0) { - log_error_errno(r, "Failed to acquire name: %m"); - goto fail; - } - - r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add object: %m"); - goto fail; - } - - r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); - goto fail; - } - - r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); - if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); - goto fail; - } - - bus_match_dump(&bus->match_callbacks, 0); - - *_bus = bus; - return 0; - -fail: - sd_bus_unref(bus); - return r; -} - -static int server(sd_bus *bus) { - int r; - bool client1_gone = false, client2_gone = false; - - while (!client1_gone || !client2_gone) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - pid_t pid = 0; - const char *label = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto fail; - } - - continue; - } - - if (!m) - continue; - - sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid); - sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label); - log_info("Got message! member=%s pid="PID_FMT" label=%s", - strna(sd_bus_message_get_member(m)), - pid, - strna(label)); - /* bus_message_dump(m); */ - /* sd_bus_message_rewind(m, true); */ - - if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) { - const char *hello; - _cleanup_free_ char *lowercase = NULL; - - r = sd_bus_message_read(m, "s", &hello); - if (r < 0) { - log_error_errno(r, "Failed to get parameter: %m"); - goto fail; - } - - lowercase = strdup(hello); - if (!lowercase) { - r = log_oom(); - goto fail; - } - - ascii_strlower(lowercase); - - r = sd_bus_reply_method_return(m, "s", lowercase); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) { - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - - client1_gone = true; - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) { - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - - client2_gone = true; - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) { - - sleep(1); - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) { - int fd; - static const char x = 'X'; - - r = sd_bus_message_read(m, "h", &fd); - if (r < 0) { - log_error_errno(r, "Failed to get parameter: %m"); - goto fail; - } - - log_info("Received fd=%d", fd); - - if (write(fd, &x, 1) < 0) { - log_error_errno(errno, "Failed to write to fd: %m"); - safe_close(fd); - goto fail; - } - - r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - - } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { - - r = sd_bus_reply_method_error( - m, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - } - } - - r = 0; - -fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - - return r; -} - -static void* client1(void*p) { - _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 }; - char x; - - r = sd_bus_open_user(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - goto finish; - } - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "LowerCase", - &error, - &reply, - "s", - "HELLO"); - if (r < 0) { - log_error_errno(r, "Failed to issue method call: %m"); - goto finish; - } - - r = sd_bus_message_read(reply, "s", &hello); - if (r < 0) { - log_error_errno(r, "Failed to get string: %m"); - goto finish; - } - - assert_se(streq(hello, "hello")); - - if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) { - log_error_errno(errno, "Failed to allocate pipe: %m"); - r = -errno; - goto finish; - } - - log_info("Sending fd=%d", pp[1]); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "FileDescriptor", - &error, - NULL, - "h", - pp[1]); - if (r < 0) { - log_error_errno(r, "Failed to issue method call: %m"); - goto finish; - } - - errno = 0; - if (read(pp[0], &x, 1) <= 0) { - log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read"); - goto finish; - } - - r = 0; - -finish: - if (bus) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; - - r = sd_bus_message_new_method_call( - bus, - &q, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "ExitClient1"); - if (r < 0) - log_error_errno(r, "Failed to allocate method call: %m"); - else - sd_bus_send(bus, q, NULL); - - } - - return INT_TO_PTR(r); -} - -static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - bool *x = userdata; - - log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m))); - - *x = 1; - return 1; -} - -static void* client2(void*p) { - _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; - - r = sd_bus_open_user(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to user bus: %m"); - goto finish; - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/foo/bar/waldo/piep", - "org.object.test", - "Foobar"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - r = sd_bus_send(bus, m, NULL); - if (r < 0) { - log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); - goto finish; - } - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_signal( - bus, - &m, - "/foobar", - "foo.bar", - "Notify"); - if (r < 0) { - log_error_errno(r, "Failed to allocate signal: %m"); - goto finish; - } - - r = sd_bus_send(bus, m, NULL); - if (r < 0) { - log_error("Failed to issue signal: %s", bus_error_message(&error, -r)); - goto finish; - } - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.DBus.Peer", - "GetMachineId"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); - goto finish; - } - - r = sd_bus_message_read(reply, "s", &mid); - if (r < 0) { - log_error_errno(r, "Failed to parse machine ID: %m"); - goto finish; - } - - log_info("Machine ID is %s.", mid); - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Slow"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - reply = sd_bus_message_unref(reply); - - r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply); - if (r < 0) - log_info("Failed to issue method call: %s", bus_error_message(&error, -r)); - else - log_info("Slow call succeed."); - - m = sd_bus_message_unref(m); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Slow"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC); - if (r < 0) { - log_info("Failed to issue method call: %s", bus_error_message(&error, -r)); - goto finish; - } - - while (!quit) { - r = sd_bus_process(bus, NULL); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto finish; - } - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto finish; - } - } - } - - r = 0; - -finish: - if (bus) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; - - r = sd_bus_message_new_method_call( - bus, - &q, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "ExitClient2"); - if (r < 0) { - log_error_errno(r, "Failed to allocate method call: %m"); - goto finish; - } - - (void) sd_bus_send(bus, q, NULL); - } - - return INT_TO_PTR(r); -} - -int main(int argc, char *argv[]) { - pthread_t c1, c2; - sd_bus *bus; - void *p; - int q, r; - - r = server_init(&bus); - if (r < 0) { - log_info("Failed to connect to bus, skipping tests."); - return EXIT_TEST_SKIP; - } - - log_info("Initialized..."); - - r = pthread_create(&c1, NULL, client1, bus); - if (r != 0) - return EXIT_FAILURE; - - r = pthread_create(&c2, NULL, client2, bus); - if (r != 0) - return EXIT_FAILURE; - - r = server(bus); - - q = pthread_join(c1, &p); - if (q != 0) - return EXIT_FAILURE; - if (PTR_TO_INT(p) < 0) - return EXIT_FAILURE; - - q = pthread_join(c2, &p); - if (q != 0) - return EXIT_FAILURE; - if (PTR_TO_INT(p) < 0) - return EXIT_FAILURE; - - if (r < 0) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c deleted file mode 100644 index 250a5b2908..0000000000 --- a/src/libsystemd/sd-bus/test-bus-cleanup.c +++ /dev/null @@ -1,95 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "refcnt.h" - -static void test_bus_new(void) { - _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - r = sd_bus_open_system(&bus); - if (r == -ECONNREFUSED || r == -ENOENT) - return r; - - assert_se(r >= 0); - printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref)); - - return 0; -} - -static void test_bus_new_method_call(void) { - sd_bus *bus = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - assert_se(sd_bus_open_system(&bus) >= 0); - - assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); - - printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - assert_se(sd_bus_open_system(&bus) >= 0); - - assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); - - printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->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) { - int r; - - log_parse_environment(); - log_open(); - - test_bus_new(); - r = test_bus_open(); - if (r < 0) { - log_info("Failed to connect to bus, skipping tests."); - return EXIT_TEST_SKIP; - } - - test_bus_new_method_call(); - test_bus_new_signal(); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c deleted file mode 100644 index e9ef483bdd..0000000000 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ /dev/null @@ -1,50 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "bus-dump.h" -#include "bus-util.h" -#include "cgroup-util.h" - -int main(int argc, char *argv[]) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - int r; - - if (cg_unified() == -ENOMEDIUM) { - puts("Skipping test: /sys/fs/cgroup/ not available"); - return EXIT_TEST_SKIP; - } - - r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); - assert_se(r >= 0); - - bus_creds_dump(creds, NULL, true); - - creds = sd_bus_creds_unref(creds); - - r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL); - if (r != -EACCES) { - assert_se(r >= 0); - putchar('\n'); - bus_creds_dump(creds, NULL, true); - } - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c deleted file mode 100644 index 66a3874f10..0000000000 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ /dev/null @@ -1,232 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "errno-list.h" - -static void test_error(void) { - _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, - .message = "oh! no", - ._need_free = -1 - }; - - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -EOPNOTSUPP); - assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED)); - assert_se(streq(error.message, "xxx")); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED)); - assert_se(sd_bus_error_get_errno(&error) == EOPNOTSUPP); - 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")); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); - assert_se(sd_bus_error_get_errno(&error) == ENOENT); - assert_se(sd_bus_error_is_set(&error)); - - assert_se(!sd_bus_error_is_set(&second)); - assert_se(second._need_free == 0); - assert_se(error._need_free > 0); - assert_se(sd_bus_error_copy(&second, &error) == -ENOENT); - assert_se(second._need_free > 0); - assert_se(streq(error.name, second.name)); - assert_se(streq(error.message, second.message)); - assert_se(sd_bus_error_get_errno(&second) == ENOENT); - assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND)); - assert_se(sd_bus_error_is_set(&second)); - - sd_bus_error_free(&error); - sd_bus_error_free(&second); - - assert_se(!sd_bus_error_is_set(&second)); - assert_se(const_error._need_free == 0); - assert_se(sd_bus_error_copy(&second, &const_error) == -EEXIST); - assert_se(second._need_free == 0); - assert_se(streq(const_error.name, second.name)); - assert_se(streq(const_error.message, second.message)); - assert_se(sd_bus_error_get_errno(&second) == EEXIST); - assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_EXISTS)); - assert_se(sd_bus_error_is_set(&second)); - sd_bus_error_free(&second); - - assert_se(!sd_bus_error_is_set(&second)); - assert_se(temporarily_const_error._need_free < 0); - assert_se(sd_bus_error_copy(&second, &temporarily_const_error) == -EACCES); - assert_se(second._need_free > 0); - assert_se(streq(temporarily_const_error.name, second.name)); - assert_se(streq(temporarily_const_error.message, second.message)); - assert_se(sd_bus_error_get_errno(&second) == EACCES); - assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_ACCESS_DENIED)); - assert_se(sd_bus_error_is_set(&second)); - - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set_const(&error, "System.Error.EUCLEAN", "Hallo") == -EUCLEAN); - assert_se(streq(error.name, "System.Error.EUCLEAN")); - assert_se(streq(error.message, "Hallo")); - assert_se(sd_bus_error_has_name(&error, "System.Error.EUCLEAN")); - assert_se(sd_bus_error_get_errno(&error) == EUCLEAN); - assert_se(sd_bus_error_is_set(&error)); - sd_bus_error_free(&error); - - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY); - assert_se(streq(error.name, "System.Error.EBUSY")); - assert_se(streq(error.message, strerror(EBUSY))); - assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY")); - assert_se(sd_bus_error_get_errno(&error) == EBUSY); - assert_se(sd_bus_error_is_set(&error)); - sd_bus_error_free(&error); - - assert_se(!sd_bus_error_is_set(&error)); - assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO); - assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR)); - assert_se(streq(error.message, "Waldi X")); - 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[]; -extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; - -static void dump_mapping_table(void) { - const sd_bus_error_map *m; - - printf("----- errno mappings ------\n"); - m = __start_BUS_ERROR_MAP; - while (m < __stop_BUS_ERROR_MAP) { - - if (m->code == BUS_ERROR_MAP_END_MARKER) { - m = ALIGN8_PTR(m+1); - continue; - } - - printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); - m++; - } - printf("---------------------------\n"); -} - -static void test_errno_mapping_standard(void) { - assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); - assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); - assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); - assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); -} - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), - SD_BUS_ERROR_MAP_END -}; - -BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors3[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), - SD_BUS_ERROR_MAP_END -}; - -static const sd_bus_error_map test_errors4[] = { - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), - SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), - 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); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); - - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); - - assert_se(sd_bus_error_add_map(test_errors3) > 0); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); - assert_se(sd_bus_error_add_map(test_errors4) > 0); - assert_se(sd_bus_error_add_map(test_errors4) == 0); - assert_se(sd_bus_error_add_map(test_errors3) == 0); - - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); - assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); - 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[]) { - dump_mapping_table(); - - test_error(); - test_errno_mapping_standard(); - test_errno_mapping_custom(); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c deleted file mode 100644 index 83f114a0fe..0000000000 --- a/src/libsystemd/sd-bus/test-bus-gvariant.c +++ /dev/null @@ -1,224 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_GLIB -#include -#endif - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-gvariant.h" -#include "bus-internal.h" -#include "bus-message.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); - assert_se(bus_gvariant_is_fixed_size("()") > 0); - assert_se(bus_gvariant_is_fixed_size("y") > 0); - assert_se(bus_gvariant_is_fixed_size("u") > 0); - assert_se(bus_gvariant_is_fixed_size("b") > 0); - assert_se(bus_gvariant_is_fixed_size("n") > 0); - assert_se(bus_gvariant_is_fixed_size("q") > 0); - assert_se(bus_gvariant_is_fixed_size("i") > 0); - assert_se(bus_gvariant_is_fixed_size("t") > 0); - assert_se(bus_gvariant_is_fixed_size("d") > 0); - assert_se(bus_gvariant_is_fixed_size("s") == 0); - assert_se(bus_gvariant_is_fixed_size("o") == 0); - assert_se(bus_gvariant_is_fixed_size("g") == 0); - assert_se(bus_gvariant_is_fixed_size("h") > 0); - assert_se(bus_gvariant_is_fixed_size("ay") == 0); - assert_se(bus_gvariant_is_fixed_size("v") == 0); - assert_se(bus_gvariant_is_fixed_size("(u)") > 0); - assert_se(bus_gvariant_is_fixed_size("(uuuuy)") > 0); - assert_se(bus_gvariant_is_fixed_size("(uusuuy)") == 0); - assert_se(bus_gvariant_is_fixed_size("a{ss}") == 0); - assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0); - assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0); -} - -static void test_bus_gvariant_get_size(void) { - assert_se(bus_gvariant_get_size("") == 0); - assert_se(bus_gvariant_get_size("()") == 1); - assert_se(bus_gvariant_get_size("y") == 1); - assert_se(bus_gvariant_get_size("u") == 4); - assert_se(bus_gvariant_get_size("b") == 1); - assert_se(bus_gvariant_get_size("n") == 2); - assert_se(bus_gvariant_get_size("q") == 2); - assert_se(bus_gvariant_get_size("i") == 4); - assert_se(bus_gvariant_get_size("t") == 8); - assert_se(bus_gvariant_get_size("d") == 8); - assert_se(bus_gvariant_get_size("s") < 0); - assert_se(bus_gvariant_get_size("o") < 0); - assert_se(bus_gvariant_get_size("g") < 0); - assert_se(bus_gvariant_get_size("h") == 4); - assert_se(bus_gvariant_get_size("ay") < 0); - assert_se(bus_gvariant_get_size("v") < 0); - assert_se(bus_gvariant_get_size("(u)") == 4); - assert_se(bus_gvariant_get_size("(uuuuy)") == 20); - assert_se(bus_gvariant_get_size("(uusuuy)") < 0); - assert_se(bus_gvariant_get_size("a{ss}") < 0); - assert_se(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28); - assert_se(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0); - assert_se(bus_gvariant_get_size("((b)(t))") == 16); - assert_se(bus_gvariant_get_size("((b)(b)(t))") == 16); - assert_se(bus_gvariant_get_size("(bt)") == 16); - assert_se(bus_gvariant_get_size("((t)(b))") == 16); - assert_se(bus_gvariant_get_size("(tb)") == 16); - assert_se(bus_gvariant_get_size("((b)(b))") == 2); - assert_se(bus_gvariant_get_size("((t)(t))") == 16); -} - -static void test_bus_gvariant_get_alignment(void) { - assert_se(bus_gvariant_get_alignment("") == 1); - assert_se(bus_gvariant_get_alignment("()") == 1); - assert_se(bus_gvariant_get_alignment("y") == 1); - assert_se(bus_gvariant_get_alignment("b") == 1); - assert_se(bus_gvariant_get_alignment("u") == 4); - assert_se(bus_gvariant_get_alignment("s") == 1); - assert_se(bus_gvariant_get_alignment("o") == 1); - assert_se(bus_gvariant_get_alignment("g") == 1); - assert_se(bus_gvariant_get_alignment("v") == 8); - assert_se(bus_gvariant_get_alignment("h") == 4); - assert_se(bus_gvariant_get_alignment("i") == 4); - assert_se(bus_gvariant_get_alignment("t") == 8); - assert_se(bus_gvariant_get_alignment("x") == 8); - assert_se(bus_gvariant_get_alignment("q") == 2); - assert_se(bus_gvariant_get_alignment("n") == 2); - assert_se(bus_gvariant_get_alignment("d") == 8); - assert_se(bus_gvariant_get_alignment("ay") == 1); - assert_se(bus_gvariant_get_alignment("as") == 1); - assert_se(bus_gvariant_get_alignment("au") == 4); - assert_se(bus_gvariant_get_alignment("an") == 2); - assert_se(bus_gvariant_get_alignment("ans") == 2); - assert_se(bus_gvariant_get_alignment("ant") == 8); - assert_se(bus_gvariant_get_alignment("(ss)") == 1); - assert_se(bus_gvariant_get_alignment("(ssu)") == 4); - assert_se(bus_gvariant_get_alignment("a(ssu)") == 4); - assert_se(bus_gvariant_get_alignment("(u)") == 4); - assert_se(bus_gvariant_get_alignment("(uuuuy)") == 4); - assert_se(bus_gvariant_get_alignment("(uusuuy)") == 4); - assert_se(bus_gvariant_get_alignment("a{ss}") == 1); - assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4); - assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8); - assert_se(bus_gvariant_get_alignment("((b)(t))") == 8); - assert_se(bus_gvariant_get_alignment("((b)(b)(t))") == 8); - assert_se(bus_gvariant_get_alignment("(bt)") == 8); - assert_se(bus_gvariant_get_alignment("((t)(b))") == 8); - assert_se(bus_gvariant_get_alignment("(tb)") == 8); - assert_se(bus_gvariant_get_alignment("((b)(b))") == 1); - assert_se(bus_gvariant_get_alignment("((t)(t))") == 8); -} - -static void test_marshal(void) { - _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; - - r = sd_bus_open_system(&bus); - if (r < 0) - exit(EXIT_TEST_SKIP); - - bus->message_version = 2; /* dirty hack to enable gvariant */ - - assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName") >= 0); - - assert_cc(sizeof(struct bus_header) == 16); - - assert_se(sd_bus_message_append(m, - "a(usv)", 3, - 4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111, - 4712, "second-string-parameter", "(a(si))", 2, "Y", 5, "Z", 6, - 4713, "third-string-parameter", "(uu)", 1, 2) >= 0); - - assert_se(bus_message_seal(m, 4711, 0) >= 0); - -#ifdef HAVE_GLIB - { - GVariant *v; - char *t; - -#if !defined(GLIB_VERSION_2_36) - g_type_init(); -#endif - - v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv})"), m->header, sizeof(struct bus_header) + m->fields_size, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - t = g_variant_print(v, TRUE); - printf("%s\n", t); - g_free(t); - g_variant_unref(v); - - v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, m->user_body_size, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - t = g_variant_print(v, TRUE); - printf("%s\n", t); - g_free(t); - g_variant_unref(v); - } -#endif - - assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); - - assert_se(bus_message_get_blob(m, &blob, &sz) >= 0); - -#ifdef HAVE_GLIB - { - GVariant *v; - char *t; - - v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv}v)"), blob, sz, false, NULL, NULL); - assert_se(g_variant_is_normal_form(v)); - t = g_variant_print(v, TRUE); - printf("%s\n", t); - g_free(t); - g_variant_unref(v); - } -#endif - - assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0); - blob = NULL; - - assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); - - m = sd_bus_message_unref(m); - - assert_se(sd_bus_message_new_method_call(bus, &m, "a.x", "/a/x", "a.x", "Ax") >= 0); - - assert_se(sd_bus_message_append(m, "as", 0) >= 0); - - assert_se(bus_message_seal(m, 4712, 0) >= 0); - assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); -} - -int main(int argc, char *argv[]) { - - test_bus_gvariant_is_fixed_size(); - test_bus_gvariant_get_size(); - test_bus_gvariant_get_alignment(); - test_marshal(); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c deleted file mode 100644 index 4425cfae26..0000000000 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ /dev/null @@ -1,63 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; -} - -static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { - return -EINVAL; -} - -static const sd_bus_vtable vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Hello", "ssas", "a(uu)", NULL, 0), - SD_BUS_METHOD("DeprecatedHello", "", "", NULL, SD_BUS_VTABLE_DEPRECATED), - SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", NULL, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY), - SD_BUS_SIGNAL("Wowza", "sss", 0), - SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED), - SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0), - SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED), - SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_PROPERTY("Constant", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EXPLICIT), - SD_BUS_VTABLE_END -}; - -int main(int argc, char *argv[]) { - struct introspect intro; - - log_set_max_level(LOG_DEBUG); - - assert_se(introspect_begin(&intro, false) >= 0); - - fprintf(intro.f, " \n"); - assert_se(introspect_write_interface(&intro, vtable) >= 0); - fputs(" \n", intro.f); - - fflush(intro.f); - fputs(intro.introspection, stdout); - - introspect_free(&intro); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c deleted file mode 100644 index eb6179d7d2..0000000000 --- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c +++ /dev/null @@ -1,141 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - - *found = 1; - - return 0; -} - -static void test_one( - const char *path, - const char *interface, - const char *member, - bool as_list, - const char *arg0, - const char *match, - bool good) { - - _cleanup_close_ int bus_ref = -1; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - sd_bus *a, *b; - int r, found = 0; - - assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); - - bus_ref = bus_kernel_create_bus(name, false, &bus_name); - if (bus_ref == -ENOENT) - exit(EXIT_TEST_SKIP); - - assert_se(bus_ref >= 0); - - address = strappend("kernel:path=", bus_name); - assert_se(address); - - r = sd_bus_new(&a); - assert_se(r >= 0); - - r = sd_bus_new(&b); - assert_se(r >= 0); - - r = sd_bus_set_address(a, address); - assert_se(r >= 0); - - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_start(a); - assert_se(r >= 0); - - r = sd_bus_start(b); - assert_se(r >= 0); - - log_debug("match"); - r = sd_bus_add_match(b, NULL, match, test_match, &found); - assert_se(r >= 0); - - log_debug("signal"); - - if (as_list) - r = sd_bus_emit_signal(a, path, interface, member, "as", 1, arg0); - else - r = sd_bus_emit_signal(a, path, interface, member, "s", arg0); - assert_se(r >= 0); - - r = sd_bus_process(b, &m); - assert_se(r >= 0 && good == !!found); - - sd_bus_unref(a); - sd_bus_unref(b); -} - -int main(int argc, char *argv[]) { - log_set_max_level(LOG_DEBUG); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/tuut'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "interface='waldo.com'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Piep'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/quux'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/quux'", false); - test_one("/", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/'", true); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/bar/waldo", "arg0path='/foo/'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo/bar/waldo'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/", "arg0path='/foo/bar/waldo'", true); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c deleted file mode 100644 index 2214817312..0000000000 --- a/src/libsystemd/sd-bus/test-bus-kernel.c +++ /dev/null @@ -1,190 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-kernel.h" -#include "bus-util.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_(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]; - const char *nn; - - log_set_max_level(LOG_DEBUG); - - assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); - - bus_ref = bus_kernel_create_bus(name, false, &bus_name); - if (bus_ref == -ENOENT) - return EXIT_TEST_SKIP; - - assert_se(bus_ref >= 0); - - address = strappend("kernel:path=", bus_name); - assert_se(address); - - r = sd_bus_new(&a); - assert_se(r >= 0); - - r = sd_bus_new(&b); - assert_se(r >= 0); - - r = sd_bus_set_description(a, "a"); - assert_se(r >= 0); - - r = sd_bus_set_address(a, address); - assert_se(r >= 0); - - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - assert_se(sd_bus_negotiate_timestamp(a, 1) >= 0); - assert_se(sd_bus_negotiate_creds(a, true, _SD_BUS_CREDS_ALL) >= 0); - - assert_se(sd_bus_negotiate_timestamp(b, 0) >= 0); - assert_se(sd_bus_negotiate_creds(b, true, 0) >= 0); - - r = sd_bus_start(a); - assert_se(r >= 0); - - r = sd_bus_start(b); - assert_se(r >= 0); - - assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); - assert_se(sd_bus_negotiate_creds(b, true, _SD_BUS_CREDS_ALL) >= 0); - - r = sd_bus_get_unique_name(a, &ua); - assert_se(r >= 0); - printf("unique a: %s\n", ua); - - r = sd_bus_get_description(a, &nn); - assert_se(r >= 0); - printf("name of a: %s\n", nn); - - r = sd_bus_get_unique_name(b, &ub); - assert_se(r >= 0); - printf("unique b: %s\n", ub); - - r = sd_bus_get_description(b, &nn); - assert_se(r >= 0); - printf("name of b: %s\n", nn); - - assert_se(bus_kernel_get_bus_name(b, &bname) >= 0); - assert_se(endswith(bname, name)); - - r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay"); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN)); - assert_se(r == -EHOSTUNREACH); - - r = sd_bus_add_match(b, NULL, "interface='waldo.com',member='Piep'", NULL, NULL); - assert_se(r >= 0); - - r = sd_bus_emit_signal(a, "/foo/bar/waldo", "waldo.com", "Piep", "sss", "I am a string", "/this/is/a/path", "and.this.a.domain.name"); - assert_se(r >= 0); - - r = sd_bus_try_close(b); - assert_se(r == -EBUSY); - - r = sd_bus_process_priority(b, -10, &m); - assert_se(r == 0); - - r = sd_bus_process(b, &m); - assert_se(r > 0); - assert_se(m); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - assert_se(sd_bus_message_rewind(m, true) >= 0); - - r = sd_bus_message_read(m, "s", &the_string); - assert_se(r >= 0); - assert_se(streq(the_string, "I am a string")); - - sd_bus_message_unref(m); - m = NULL; - - r = sd_bus_request_name(a, "net.x0pointer.foobar", 0); - assert_se(r >= 0); - - r = sd_bus_message_new_method_call(b, &m, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod"); - assert_se(r >= 0); - - assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0); - - assert_se(write(pipe_fds[1], "x", 1) == 1); - - pipe_fds[1] = safe_close(pipe_fds[1]); - - r = sd_bus_message_append(m, "h", pipe_fds[0]); - assert_se(r >= 0); - - pipe_fds[0] = safe_close(pipe_fds[0]); - - r = sd_bus_send(b, m, NULL); - assert_se(r >= 0); - - for (;;) { - sd_bus_message_unref(m); - m = NULL; - r = sd_bus_process(a, &m); - assert_se(r > 0); - assert_se(m); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - assert_se(sd_bus_message_rewind(m, true) >= 0); - - if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) { - int fd; - char x; - - r = sd_bus_message_read(m, "h", &fd); - assert_se(r >= 0); - - assert_se(read(fd, &x, 1) == 1); - assert_se(x == 'x'); - break; - } - } - - r = sd_bus_release_name(a, "net.x0pointer.foobar"); - assert_se(r >= 0); - - r = sd_bus_release_name(a, "net.x0pointer.foobar"); - assert_se(r == -ESRCH); - - r = sd_bus_try_close(a); - assert_se(r >= 0); - - sd_bus_unref(a); - sd_bus_unref(b); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c deleted file mode 100644 index a28cc5b79e..0000000000 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ /dev/null @@ -1,432 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#ifdef HAVE_GLIB -#include -#endif - -#ifdef HAVE_DBUS -#include -#endif - -#include "sd-bus.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; - - assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix")); - assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix")); - assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d); - assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d); - assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, "")); -} - -static void test_bus_path_encode(void) { - _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; - - assert_se(sd_bus_path_encode("/foo/bar", "waldo", &a) >= 0 && streq(a, "/foo/bar/waldo")); - assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL); - assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo")); - - assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0); - assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0); - - assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_")); - assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, "")); - - assert_se(sd_bus_path_encode("/foo/bar", "foo.bar", &e) >= 0 && streq(e, "/foo/bar/foo_2ebar")); - 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; - - assert_se(t = bus_label_escape(a)); - assert_se(streq(t, b)); - - assert_se(x = bus_label_unescape(t)); - assert_se(streq(a, x)); - - assert_se(y = bus_label_unescape(b)); - assert_se(streq(a, y)); -} - -static void test_bus_label_escape(void) { - test_bus_label_escape_one("foo123bar", "foo123bar"); - test_bus_label_escape_one("foo.bar", "foo_2ebar"); - test_bus_label_escape_one("foo_2ebar", "foo_5f2ebar"); - test_bus_label_escape_one("", "_"); - test_bus_label_escape_one("_", "_5f"); - test_bus_label_escape_one("1", "_31"); - test_bus_label_escape_one(":1", "_3a1"); -} - -int main(int argc, char *argv[]) { - _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; - void *buffer = NULL; - size_t sz; - char *h; - const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array; - char *s; - _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_(sd_bus_unrefp) sd_bus *bus = NULL; - double dbl; - uint64_t u64; - - r = sd_bus_default_system(&bus); - if (r < 0) - return EXIT_TEST_SKIP; - - r = sd_bus_message_new_method_call(bus, &m, "foobar.waldo", "/", "foobar.waldo", "Piep"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, ""); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "s", "a string"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "s", NULL); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "asg", 2, "string #1", "string #2", "sba(tt)ss"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "sass", "foobar", 5, "foo", "bar", "waldo", "piep", "pap", "after"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "y(ty)y(yt)y", 8, 777ULL, 7, 9, 77, 7777ULL, 10); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "()"); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3"); - assert_se(r >= 0); - - r = sd_bus_message_open_container(m, 'a', "s"); - assert_se(r >= 0); - - r = sd_bus_message_append_basic(m, 's', "foobar"); - assert_se(r >= 0); - - r = sd_bus_message_append_basic(m, 's', "waldo"); - assert_se(r >= 0); - - r = sd_bus_message_close_container(m); - assert_se(r >= 0); - - r = sd_bus_message_append_string_space(m, 5, &s); - assert_se(r >= 0); - strcpy(s, "hallo"); - - r = sd_bus_message_append_array(m, 'i', integer_array, sizeof(integer_array)); - assert_se(r >= 0); - - r = sd_bus_message_append_array(m, 'u', NULL, 0); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "a(stdo)", 1, "foo", 815ULL, 47.0, "/"); - assert_se(r >= 0); - - r = bus_message_seal(m, 4711, 0); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - ms = open_memstream(&first, &first_size); - bus_message_dump(m, ms, 0); - fflush(ms); - assert_se(!ferror(ms)); - - r = bus_message_get_blob(m, &buffer, &sz); - assert_se(r >= 0); - - h = hexmem(buffer, sz); - assert_se(h); - - log_info("message size = %zu, contents =\n%s", sz, h); - free(h); - -#ifdef HAVE_GLIB - { - GDBusMessage *g; - char *p; - -#if !defined(GLIB_VERSION_2_36) - g_type_init(); -#endif - - g = g_dbus_message_new_from_blob(buffer, sz, 0, NULL); - p = g_dbus_message_print(g, 0); - log_info("%s", p); - g_free(p); - g_object_unref(g); - } -#endif - -#ifdef HAVE_DBUS - { - DBusMessage *w; - DBusError error; - - dbus_error_init(&error); - - w = dbus_message_demarshal(buffer, sz, &error); - if (!w) - log_error("%s", error.message); - else - dbus_message_unref(w); - - dbus_error_free(&error); - } -#endif - - m = sd_bus_message_unref(m); - - r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - fclose(ms); - ms = open_memstream(&second, &second_size); - bus_message_dump(m, ms, 0); - fflush(ms); - assert_se(!ferror(ms)); - assert_se(first_size == second_size); - assert_se(memcmp(first, second, first_size) == 0); - - assert_se(sd_bus_message_rewind(m, true) >= 0); - - r = sd_bus_message_read(m, "ssasg", &x, &x2, 2, &y, &z, &a_signature); - assert_se(r > 0); - assert_se(streq(x, "a string")); - assert_se(streq(x2, "")); - assert_se(streq(y, "string #1")); - assert_se(streq(z, "string #2")); - assert_se(streq(a_signature, "sba(tt)ss")); - - r = sd_bus_message_read(m, "sass", &x, 5, &y, &z, &a, &b, &c, &d); - assert_se(r > 0); - assert_se(streq(x, "foobar")); - assert_se(streq(y, "foo")); - assert_se(streq(z, "bar")); - assert_se(streq(a, "waldo")); - assert_se(streq(b, "piep")); - assert_se(streq(c, "pap")); - assert_se(streq(d, "after")); - - r = sd_bus_message_read(m, "a{yv}", 2, &u, "s", &x, &v, "s", &y); - assert_se(r > 0); - assert_se(u == 3); - assert_se(streq(x, "foo")); - assert_se(v == 5); - assert_se(streq(y, "waldo")); - - r = sd_bus_message_read(m, "y(ty)", &v, &u64, &u); - assert_se(r > 0); - assert_se(v == 8); - assert_se(u64 == 777); - assert_se(u == 7); - - r = sd_bus_message_read(m, "y(yt)", &v, &u, &u64); - assert_se(r > 0); - assert_se(v == 9); - assert_se(u == 77); - assert_se(u64 == 7777); - - r = sd_bus_message_read(m, "y", &v); - assert_se(r > 0); - assert_se(v == 10); - - r = sd_bus_message_read(m, "()"); - assert_se(r > 0); - - r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d); - assert_se(r > 0); - assert_se(boolean); - assert_se(streq(x, "aaa")); - assert_se(streq(y, "1")); - assert_se(streq(a, "bbb")); - assert_se(streq(b, "2")); - assert_se(streq(c, "ccc")); - assert_se(streq(d, "3")); - - assert_se(sd_bus_message_verify_type(m, 'a', "s") > 0); - - r = sd_bus_message_read(m, "as", 2, &x, &y); - assert_se(r > 0); - assert_se(streq(x, "foobar")); - assert_se(streq(y, "waldo")); - - r = sd_bus_message_read_basic(m, 's', &s); - assert_se(r > 0); - assert_se(streq(s, "hallo")); - - r = sd_bus_message_read_array(m, 'i', (const void**) &return_array, &sz); - assert_se(r > 0); - assert_se(sz == sizeof(integer_array)); - assert_se(memcmp(integer_array, return_array, sz) == 0); - - r = sd_bus_message_read_array(m, 'u', (const void**) &return_array, &sz); - assert_se(r > 0); - assert_se(sz == 0); - - r = sd_bus_message_read(m, "a(stdo)", 1, &x, &u64, &dbl, &y); - assert_se(r > 0); - assert_se(streq(x, "foo")); - assert_se(u64 == 815ULL); - assert_se(fabs(dbl - 47.0) < 0.1); - assert_se(streq(y, "/")); - - r = sd_bus_message_peek_type(m, NULL, NULL); - assert_se(r == 0); - - r = sd_bus_message_new_method_call(bus, ©, "foobar.waldo", "/", "foobar.waldo", "Piep"); - assert_se(r >= 0); - - r = sd_bus_message_rewind(m, true); - assert_se(r >= 0); - - r = sd_bus_message_copy(copy, m, true); - assert_se(r >= 0); - - r = bus_message_seal(copy, 4712, 0); - assert_se(r >= 0); - - fclose(ms); - ms = open_memstream(&third, &third_size); - bus_message_dump(copy, ms, 0); - fflush(ms); - assert_se(!ferror(ms)); - - printf("<%.*s>\n", (int) first_size, first); - printf("<%.*s>\n", (int) third_size, third); - - assert_se(first_size == third_size); - assert_se(memcmp(first, third, third_size) == 0); - - r = sd_bus_message_rewind(m, true); - assert_se(r >= 0); - - assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0); - - r = sd_bus_message_skip(m, "ssasg"); - assert_se(r > 0); - - assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0); - - r = sd_bus_message_skip(m, "sass"); - assert_se(r >= 0); - - assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0); - - r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y()"); - assert_se(r >= 0); - - assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0); - - r = sd_bus_message_read(m, "b", &boolean); - assert_se(r > 0); - assert_se(boolean); - - r = sd_bus_message_enter_container(m, 0, NULL); - assert_se(r > 0); - - r = sd_bus_message_read(m, "(ss)", &x, &y); - assert_se(r > 0); - - r = sd_bus_message_read(m, "(ss)", &a, &b); - assert_se(r > 0); - - r = sd_bus_message_read(m, "(ss)", &c, &d); - assert_se(r > 0); - - r = sd_bus_message_read(m, "(ss)", &x, &y); - assert_se(r == 0); - - r = sd_bus_message_exit_container(m); - assert_se(r >= 0); - - assert_se(streq(x, "aaa")); - assert_se(streq(y, "1")); - assert_se(streq(a, "bbb")); - assert_se(streq(b, "2")); - assert_se(streq(c, "ccc")); - assert_se(streq(d, "3")); - - 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 deleted file mode 100644 index 29c4529f95..0000000000 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ /dev/null @@ -1,159 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-match.h" -#include "bus-message.h" -#include "bus-slot.h" -#include "bus-util.h" -#include "log.h" -#include "macro.h" - -static bool mask[32]; - -static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - log_info("Ran %u", PTR_TO_UINT(userdata)); - assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); - mask[PTR_TO_UINT(userdata)] = true; - return 0; -} - -static bool mask_contains(unsigned a[], unsigned n) { - unsigned i, j; - - for (i = 0; i < ELEMENTSOF(mask); i++) { - bool found = false; - - for (j = 0; j < n; j++) - if (a[j] == i) { - found = true; - break; - } - - if (found != mask[i]) - return false; - } - - return true; -} - -static int match_add(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - sd_bus_slot *s; - int r; - - s = slots + value; - zero(*s); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - return r; - - s->userdata = INT_TO_PTR(value); - s->match_callback.callback = filter; - - r = bus_match_add(root, components, n_components, &s->match_callback); - bus_match_parse_free(components, n_components); - - return r; -} - -static void test_match_scope(const char *match, enum bus_match_scope scope) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - - assert_se(bus_match_parse(match, &components, &n_components) >= 0); - assert_se(bus_match_get_scope(components, n_components) == scope); - bus_match_parse_free(components, n_components); -} - -int main(int argc, char *argv[]) { - struct bus_match_node root = { - .type = BUS_MATCH_ROOT, - }; - - _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; - - r = sd_bus_open_system(&bus); - if (r < 0) - return EXIT_TEST_SKIP; - - assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0); - assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0); - assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0); - assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0); - assert_se(match_add(slots, &root, "", 5) >= 0); - assert_se(match_add(slots, &root, "interface='quux.x'", 6) >= 0); - assert_se(match_add(slots, &root, "interface='bar.x'", 7) >= 0); - assert_se(match_add(slots, &root, "member='waldo',path='/foo/bar'", 8) >= 0); - assert_se(match_add(slots, &root, "path='/foo/bar'", 9) >= 0); - assert_se(match_add(slots, &root, "path_namespace='/foo'", 10) >= 0); - assert_se(match_add(slots, &root, "path_namespace='/foo/quux'", 11) >= 0); - assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); - assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); - assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); - assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0); - assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0); - assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0); - assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0); - - bus_match_dump(&root, 0); - - assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0); - assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 0); - assert_se(bus_message_seal(m, 1, 0) >= 0); - - zero(mask); - assert_se(bus_match_run(NULL, &root, m) == 0); - assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14, 15, 16, 17 }, 11)); - - assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0); - assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 0); - - bus_match_dump(&root, 0); - - zero(mask); - assert_se(bus_match_run(NULL, &root, m) == 0); - assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7, 15, 16, 17 }, 9)); - - for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) { - char buf[32]; - const char *x; - - assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf))); - - if (i >= BUS_MATCH_MESSAGE_TYPE) - assert_se(bus_match_node_type_from_string(x, strlen(x)) == i); - } - - bus_match_free(&root); - - test_match_scope("interface='foobar'", BUS_MATCH_GENERIC); - test_match_scope("", BUS_MATCH_GENERIC); - test_match_scope("interface='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); - test_match_scope("sender='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); - test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL); - test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c deleted file mode 100644 index f11cafd888..0000000000 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ /dev/null @@ -1,555 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "log.h" -#include "macro.h" -#include "strv.h" -#include "util.h" - -struct context { - int fds[2]; - bool quit; - char *something; - char *automatic_string_property; - uint32_t automatic_integer_property; -}; - -static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { - struct context *c = userdata; - const char *s; - char *n = NULL; - int r; - - r = sd_bus_message_read(m, "s", &s); - assert_se(r > 0); - - n = strjoin("<<<", s, ">>>", NULL); - assert_se(n); - - free(c->something); - c->something = n; - - log_info("AlterSomething() called, got %s, returning %s", s, n); - - /* This should fail, since the return type doesn't match */ - assert_se(sd_bus_reply_method_return(m, "u", 4711) == -ENOMSG); - - r = sd_bus_reply_method_return(m, "s", n); - assert_se(r >= 0); - - return 1; -} - -static int exit_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { - struct context *c = userdata; - int r; - - c->quit = true; - - log_info("Exit called"); - - r = sd_bus_reply_method_return(m, ""); - assert_se(r >= 0); - - return 1; -} - -static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { - struct context *c = userdata; - int r; - - log_info("property get for %s called, returning \"%s\".", property, c->something); - - r = sd_bus_message_append(reply, "s", c->something); - assert_se(r >= 0); - - return 1; -} - -static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) { - struct context *c = userdata; - const char *s; - char *n; - int r; - - log_info("property set for %s called", property); - - r = sd_bus_message_read(value, "s", &s); - assert_se(r >= 0); - - n = strdup(s); - assert_se(n); - - free(c->something); - c->something = n; - - return 1; -} - -static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *s = NULL; - const char *x; - int r; - - assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0); - r = sd_bus_message_append(reply, "s", s); - assert_se(r >= 0); - - assert_se(x = startswith(path, "/value/")); - - assert_se(PTR_TO_UINT(userdata) == 30); - - return 1; -} - -static int notify_test(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int r; - - assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 0); - - r = sd_bus_reply_method_return(m, NULL); - assert_se(r >= 0); - - return 1; -} - -static const sd_bus_vtable vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0), - SD_BUS_METHOD("Exit", "", "", exit_handler, 0), - SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0), - SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0), - SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0), - SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0), - SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0), - SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0), - SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0), - SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0), - SD_BUS_VTABLE_END -}; - -static const sd_bus_vtable vtable2[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0), - SD_BUS_METHOD("NotifyTest2", "", "", notify_test2, 0), - SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0), - SD_BUS_PROPERTY("AnExplicitProperty", "s", NULL, offsetof(struct context, something), SD_BUS_VTABLE_PROPERTY_EXPLICIT|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), - SD_BUS_VTABLE_END -}; - -static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - - if (object_path_startswith("/value", path)) - assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL)); - - return 1; -} - -static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - - if (object_path_startswith("/value/a", path)) - assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL)); - - return 1; -} - -static void *server(void *p) { - struct context *c = p; - sd_bus *bus = NULL; - sd_id128_t id; - int r; - - c->quit = false; - - assert_se(sd_id128_randomize(&id) >= 0); - - assert_se(sd_bus_new(&bus) >= 0); - assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0); - assert_se(sd_bus_set_server(bus, 1, id) >= 0); - - assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0); - assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0); - assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0); - assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0); - assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0); - assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0); - assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0); - - assert_se(sd_bus_start(bus) >= 0); - - log_error("Entering event loop on server"); - - while (!c->quit) { - log_error("Loop!"); - - r = sd_bus_process(bus, NULL); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto fail; - } - - continue; - } - } - - r = 0; - -fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - - return INT_TO_PTR(r); -} - -static int client(struct context *c) { - _cleanup_(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; - - assert_se(sd_bus_new(&bus) >= 0); - assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0); - assert_se(sd_bus_start(bus) >= 0); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL); - assert_se(r >= 0); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo"); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - assert_se(streq(s, "<<>>")); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, ""); - assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); - - sd_bus_error_free(&error); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo"); - assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)); - - sd_bus_error_free(&error); - - r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s"); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - assert_se(streq(s, "<<>>")); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test"); - assert_se(r >= 0); - - r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s"); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - assert_se(streq(s, "test")); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815); - assert_se(r >= 0); - - assert_se(c->automatic_integer_property == 815); - - r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!"); - assert_se(r >= 0); - - assert_se(streq(c->automatic_string_property, "Du Dödel, Du!")); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - fputs(s, stdout); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s"); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - log_info("read %s", s); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - fputs(s, stdout); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - fputs(s, stdout); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); - assert_se(r >= 0); - - r = sd_bus_message_read(reply, "s", &s); - assert_se(r >= 0); - fputs(s, stdout); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", ""); - assert_se(r >= 0); - - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2"); - assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE)); - sd_bus_error_free(&error); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); - assert_se(r < 0); - assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); - sd_bus_error_free(&error); - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); - assert_se(r >= 0); - - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, ""); - assert_se(r >= 0); - - r = sd_bus_process(bus, &reply); - assert_se(r > 0); - - assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); - bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - sd_bus_message_unref(reply); - reply = NULL; - - r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, ""); - assert_se(r >= 0); - - sd_bus_flush(bus); - - return 0; -} - -int main(int argc, char *argv[]) { - struct context c = {}; - pthread_t s; - void *p; - int r, q; - - zero(c); - - c.automatic_integer_property = 4711; - assert_se(c.automatic_string_property = strdup("dudeldu")); - - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0); - - r = pthread_create(&s, NULL, server, &c); - if (r != 0) - return -r; - - r = client(&c); - - q = pthread_join(s, &p); - if (q != 0) - return -q; - - if (r < 0) - return r; - - if (PTR_TO_INT(p) < 0) - return PTR_TO_INT(p); - - free(c.something); - free(c.automatic_string_property); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c deleted file mode 100644 index b6272efc30..0000000000 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ /dev/null @@ -1,216 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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]; - - bool client_negotiate_unix_fds; - bool server_negotiate_unix_fds; - - bool client_anonymous_auth; - bool server_anonymous_auth; -}; - -static void *server(void *p) { - struct context *c = p; - sd_bus *bus = NULL; - sd_id128_t id; - bool quit = false; - int r; - - assert_se(sd_id128_randomize(&id) >= 0); - - assert_se(sd_bus_new(&bus) >= 0); - assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0); - assert_se(sd_bus_set_server(bus, 1, id) >= 0); - assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0); - assert_se(sd_bus_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0); - assert_se(sd_bus_start(bus) >= 0); - - while (!quit) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - - r = sd_bus_process(bus, &m); - if (r < 0) { - log_error_errno(r, "Failed to process requests: %m"); - goto fail; - } - - if (r == 0) { - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Failed to wait: %m"); - goto fail; - } - - continue; - } - - if (!m) - continue; - - log_info("Got message! member=%s", strna(sd_bus_message_get_member(m))); - - if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) { - - assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds)); - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) { - log_error_errno(r, "Failed to allocate return: %m"); - goto fail; - } - - quit = true; - - } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { - r = sd_bus_message_new_method_error( - m, - &reply, - &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); - if (r < 0) { - log_error_errno(r, "Failed to allocate return: %m"); - goto fail; - } - } - - if (reply) { - r = sd_bus_send(bus, reply, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send reply: %m"); - goto fail; - } - } - } - - r = 0; - -fail: - if (bus) { - sd_bus_flush(bus); - sd_bus_unref(bus); - } - - return INT_TO_PTR(r); -} - -static int client(struct context *c) { - _cleanup_(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; - - assert_se(sd_bus_new(&bus) >= 0); - assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0); - assert_se(sd_bus_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0); - assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0); - assert_se(sd_bus_start(bus) >= 0); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd.test", - "/", - "org.freedesktop.systemd.test", - "Exit"); - if (r < 0) - return log_error_errno(r, "Failed to allocate method call: %m"); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds, - bool client_anonymous_auth, bool server_anonymous_auth) { - - struct context c; - pthread_t s; - void *p; - int r, q; - - zero(c); - - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0); - - c.client_negotiate_unix_fds = client_negotiate_unix_fds; - c.server_negotiate_unix_fds = server_negotiate_unix_fds; - c.client_anonymous_auth = client_anonymous_auth; - c.server_anonymous_auth = server_anonymous_auth; - - r = pthread_create(&s, NULL, server, &c); - if (r != 0) - return -r; - - r = client(&c); - - q = pthread_join(s, &p); - if (q != 0) - return -q; - - if (r < 0) - return r; - - if (PTR_TO_INT(p) < 0) - return PTR_TO_INT(p); - - return 0; -} - -int main(int argc, char *argv[]) { - int r; - - r = test_one(true, true, false, false); - assert_se(r >= 0); - - r = test_one(true, false, false, false); - assert_se(r >= 0); - - r = test_one(false, true, false, false); - assert_se(r >= 0); - - r = test_one(false, false, false, false); - assert_se(r >= 0); - - r = test_one(true, true, true, true); - assert_se(r >= 0); - - r = test_one(true, true, false, true); - assert_se(r >= 0); - - r = test_one(true, true, true, false); - assert_se(r == -EPERM); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c deleted file mode 100644 index 4f4fd093bf..0000000000 --- a/src/libsystemd/sd-bus/test-bus-signature.c +++ /dev/null @@ -1,164 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "bus-internal.h" -#include "bus-signature.h" -#include "log.h" -#include "string-util.h" - -int main(int argc, char *argv[]) { - char prefix[256]; - int r; - - assert_se(signature_is_single("y", false)); - assert_se(signature_is_single("u", false)); - assert_se(signature_is_single("v", false)); - assert_se(signature_is_single("as", false)); - assert_se(signature_is_single("(ss)", false)); - assert_se(signature_is_single("()", false)); - assert_se(signature_is_single("(()()()()())", false)); - assert_se(signature_is_single("(((())))", false)); - assert_se(signature_is_single("((((s))))", false)); - assert_se(signature_is_single("{ss}", true)); - assert_se(signature_is_single("a{ss}", false)); - assert_se(!signature_is_single("uu", false)); - assert_se(!signature_is_single("", false)); - assert_se(!signature_is_single("(", false)); - assert_se(!signature_is_single(")", false)); - assert_se(!signature_is_single("())", false)); - assert_se(!signature_is_single("((())", false)); - assert_se(!signature_is_single("{)", false)); - assert_se(!signature_is_single("{}", true)); - assert_se(!signature_is_single("{sss}", true)); - assert_se(!signature_is_single("{s}", true)); - assert_se(!signature_is_single("{ss}", false)); - assert_se(!signature_is_single("{ass}", true)); - assert_se(!signature_is_single("a}", true)); - - assert_se(signature_is_pair("yy")); - assert_se(signature_is_pair("ss")); - assert_se(signature_is_pair("sas")); - assert_se(signature_is_pair("sv")); - assert_se(signature_is_pair("sa(vs)")); - assert_se(!signature_is_pair("")); - assert_se(!signature_is_pair("va")); - assert_se(!signature_is_pair("sss")); - assert_se(!signature_is_pair("{s}ss")); - - assert_se(signature_is_valid("ssa{ss}sssub", true)); - assert_se(signature_is_valid("ssa{ss}sssub", false)); - assert_se(signature_is_valid("{ss}", true)); - assert_se(!signature_is_valid("{ss}", false)); - assert_se(signature_is_valid("", true)); - assert_se(signature_is_valid("", false)); - - assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false)); - - assert_se(!signature_is_valid("a", false)); - assert_se(signature_is_valid("as", false)); - assert_se(signature_is_valid("aas", false)); - assert_se(signature_is_valid("aaas", false)); - assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false)); - assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false)); - assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false)); - - assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false)); - assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false)); - - assert_se(namespace_complex_pattern("", "")); - assert_se(namespace_complex_pattern("foobar", "foobar")); - assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo")); - assert_se(namespace_complex_pattern("foobar.", "foobar.waldo")); - assert_se(namespace_complex_pattern("foobar.waldo", "foobar.")); - assert_se(!namespace_complex_pattern("foobar.waldo", "foobar")); - assert_se(!namespace_complex_pattern("foobar", "foobar.waldo")); - assert_se(!namespace_complex_pattern("", "foo")); - assert_se(!namespace_complex_pattern("foo", "")); - assert_se(!namespace_complex_pattern("foo.", "")); - - assert_se(path_complex_pattern("", "")); - assert_se(!path_complex_pattern("", "/")); - assert_se(!path_complex_pattern("/", "")); - assert_se(path_complex_pattern("/", "/")); - assert_se(path_complex_pattern("/foobar/", "/")); - assert_se(!path_complex_pattern("/foobar/", "/foobar")); - assert_se(path_complex_pattern("/foobar", "/foobar")); - assert_se(!path_complex_pattern("/foobar", "/foobar/")); - assert_se(!path_complex_pattern("/foobar", "/foobar/waldo")); - assert_se(path_complex_pattern("/foobar/", "/foobar/waldo")); - assert_se(path_complex_pattern("/foobar/waldo", "/foobar/")); - - assert_se(path_simple_pattern("/foo/", "/foo/bar/waldo")); - - assert_se(namespace_simple_pattern("", "")); - assert_se(namespace_simple_pattern("", ".foobar")); - assert_se(namespace_simple_pattern("foobar", "foobar")); - assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo")); - assert_se(namespace_simple_pattern("foobar", "foobar.waldo")); - assert_se(!namespace_simple_pattern("foobar.waldo", "foobar")); - assert_se(!namespace_simple_pattern("", "foo")); - assert_se(!namespace_simple_pattern("foo", "")); - assert_se(namespace_simple_pattern("foo.", "foo.bar.waldo")); - - assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar")); - assert_se(streq(object_path_startswith("/foo", "/foo"), "")); - assert_se(streq(object_path_startswith("/foo", "/"), "foo")); - assert_se(streq(object_path_startswith("/", "/"), "")); - assert_se(!object_path_startswith("/foo", "/bar")); - assert_se(!object_path_startswith("/", "/bar")); - assert_se(!object_path_startswith("/foo", "")); - - assert_se(object_path_is_valid("/foo/bar")); - assert_se(object_path_is_valid("/foo")); - assert_se(object_path_is_valid("/")); - assert_se(object_path_is_valid("/foo5")); - assert_se(object_path_is_valid("/foo_5")); - assert_se(!object_path_is_valid("")); - assert_se(!object_path_is_valid("/foo/")); - assert_se(!object_path_is_valid("//")); - assert_se(!object_path_is_valid("//foo")); - assert_se(!object_path_is_valid("/foo//bar")); - assert_se(!object_path_is_valid("/foo/aaaäöä")); - - OBJECT_PATH_FOREACH_PREFIX(prefix, "/") { - log_info("<%s>", prefix); - assert_not_reached("???"); - } - - r = 0; - OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx") { - log_info("<%s>", prefix); - assert_se(streq(prefix, "/")); - assert_se(r == 0); - r++; - } - assert_se(r == 1); - - r = 0; - OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx/yyy/zzz") { - log_info("<%s>", prefix); - assert_se(r != 0 || streq(prefix, "/xxx/yyy")); - assert_se(r != 1 || streq(prefix, "/xxx")); - assert_se(r != 2 || streq(prefix, "/")); - r++; - } - assert_se(r == 3); - - return 0; -} diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c deleted file mode 100644 index 3380e8500a..0000000000 --- a/src/libsystemd/sd-bus/test-bus-zero-copy.c +++ /dev/null @@ -1,210 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.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 - -#define STRING_SIZE 123 - -int main(int argc, char *argv[]) { - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; - const char *unique; - uint8_t *p; - sd_bus *a, *b; - int r, bus_ref; - sd_bus_message *m; - int f; - uint64_t sz; - uint32_t u32; - size_t i, l; - char *s; - _cleanup_close_ int sfd = -1; - - log_set_max_level(LOG_DEBUG); - - assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); - - bus_ref = bus_kernel_create_bus(name, false, &bus_name); - if (bus_ref == -ENOENT) - return EXIT_TEST_SKIP; - - assert_se(bus_ref >= 0); - - address = strappend("kernel:path=", bus_name); - assert_se(address); - - r = sd_bus_new(&a); - assert_se(r >= 0); - - r = sd_bus_new(&b); - assert_se(r >= 0); - - r = sd_bus_set_address(a, address); - assert_se(r >= 0); - - r = sd_bus_set_address(b, address); - assert_se(r >= 0); - - r = sd_bus_start(a); - assert_se(r >= 0); - - r = sd_bus_start(b); - assert_se(r >= 0); - - r = sd_bus_get_unique_name(a, &unique); - assert_se(r >= 0); - - r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod"); - assert_se(r >= 0); - - r = sd_bus_message_open_container(m, 'r', "aysay"); - assert_se(r >= 0); - - r = sd_bus_message_append_array_space(m, 'y', FIRST_ARRAY, (void**) &p); - assert_se(r >= 0); - - p[0] = '<'; - memset(p+1, 'L', FIRST_ARRAY-2); - p[FIRST_ARRAY-1] = '>'; - - f = memfd_new_and_map(NULL, STRING_SIZE, (void**) &s); - assert_se(f >= 0); - - s[0] = '<'; - for (i = 1; i < STRING_SIZE-2; i++) - s[i] = '0' + (i % 10); - s[STRING_SIZE-2] = '>'; - s[STRING_SIZE-1] = 0; - munmap(s, STRING_SIZE); - - r = memfd_get_size(f, &sz); - assert_se(r >= 0); - assert_se(sz == STRING_SIZE); - - r = sd_bus_message_append_string_memfd(m, f, 0, (uint64_t) -1); - assert_se(r >= 0); - - close(f); - - f = memfd_new_and_map(NULL, SECOND_ARRAY, (void**) &p); - assert_se(f >= 0); - - p[0] = '<'; - memset(p+1, 'P', SECOND_ARRAY-2); - p[SECOND_ARRAY-1] = '>'; - munmap(p, SECOND_ARRAY); - - r = memfd_get_size(f, &sz); - assert_se(r >= 0); - assert_se(sz == SECOND_ARRAY); - - r = sd_bus_message_append_array_memfd(m, 'y', f, 0, (uint64_t) -1); - assert_se(r >= 0); - - close(f); - - r = sd_bus_message_close_container(m); - assert_se(r >= 0); - - r = sd_bus_message_append(m, "u", 4711); - assert_se(r >= 0); - - assert_se((sfd = memfd_new_and_map(NULL, 6, (void**) &p)) >= 0); - memcpy(p, "abcd\0", 6); - munmap(p, 6); - assert_se(sd_bus_message_append_string_memfd(m, sfd, 1, 4) >= 0); - - r = bus_message_seal(m, 55, 99*USEC_PER_SEC); - assert_se(r >= 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - - r = sd_bus_send(b, m, NULL); - assert_se(r >= 0); - - sd_bus_message_unref(m); - - r = sd_bus_process(a, &m); - assert_se(r > 0); - - bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); - sd_bus_message_rewind(m, true); - - r = sd_bus_message_enter_container(m, 'r', "aysay"); - assert_se(r > 0); - - r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l); - assert_se(r > 0); - assert_se(l == FIRST_ARRAY); - - assert_se(p[0] == '<'); - for (i = 1; i < l-1; i++) - assert_se(p[i] == 'L'); - assert_se(p[l-1] == '>'); - - r = sd_bus_message_read(m, "s", &s); - assert_se(r > 0); - - assert_se(s[0] == '<'); - for (i = 1; i < STRING_SIZE-2; i++) - assert_se(s[i] == (char) ('0' + (i % 10))); - assert_se(s[STRING_SIZE-2] == '>'); - assert_se(s[STRING_SIZE-1] == 0); - - r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l); - assert_se(r > 0); - assert_se(l == SECOND_ARRAY); - - assert_se(p[0] == '<'); - for (i = 1; i < l-1; i++) - assert_se(p[i] == 'P'); - assert_se(p[l-1] == '>'); - - r = sd_bus_message_exit_container(m); - assert_se(r > 0); - - r = sd_bus_message_read(m, "u", &u32); - assert_se(r > 0); - assert_se(u32 == 4711); - - r = sd_bus_message_read(m, "s", &s); - assert_se(r > 0); - assert_se(streq_ptr(s, "bcd")); - - sd_bus_message_unref(m); - - sd_bus_unref(a); - sd_bus_unref(b); - - return 0; -} diff --git a/src/libsystemd/sd-daemon.xml b/src/libsystemd/sd-daemon.xml new file mode 100644 index 0000000000..b06d99f346 --- /dev/null +++ b/src/libsystemd/sd-daemon.xml @@ -0,0 +1,144 @@ + + + + + + + + + sd-daemon + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-daemon + 3 + + + + sd-daemon + SD_EMERG + SD_ALERT + SD_CRIT + SD_ERR + SD_WARNING + SD_NOTICE + SD_INFO + SD_DEBUG + APIs for + new-style daemons + + + + + #include <systemd/sd-daemon.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-daemon.h provides APIs for new-style + daemons, as implemented by the + systemd1 + service manager. + + See + sd_listen_fds3, + sd_notify3, + sd_booted3, + sd_is_fifo3, + sd_watchdog_enabled3 + for more information about the functions implemented. In addition + to these functions, a couple of logging prefixes are defined as + macros: + + #define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + + These prefixes are intended to be used in conjunction with + stderr-based logging as implemented by systemd. If a systemd + service definition file is configured with + StandardError=journal, + StandardError=syslog or + StandardError=kmsg, these prefixes can be used + to encode a log level in lines printed. This is similar to the + kernel printk()-style logging. See + klogctl2 + for more information. + + The log levels are identical to + syslog3's + log level system. To use these prefixes simply prefix every line + with one of these strings. A line that is not prefixed will be + logged at the default log level SD_INFO. + + + Hello World + + A daemon may log with the log level NOTICE by issuing this + call: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + + + + + + See Also + + systemd1, + sd_listen_fds3, + sd_notify3, + sd_booted3, + sd_is_fifo3, + sd_watchdog_enabled3, + daemon7, + systemd.service5, + systemd.socket5, + fprintf3, + pkg-config1 + + + + diff --git a/src/libsystemd/sd-daemon/Makefile b/src/libsystemd/sd-daemon/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-daemon/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c deleted file mode 100644 index b20a7ebb4c..0000000000 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ /dev/null @@ -1,622 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "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; - int n, r, fd; - pid_t pid; - - e = getenv("LISTEN_PID"); - if (!e) { - r = 0; - goto finish; - } - - r = parse_pid(e, &pid); - if (r < 0) - goto finish; - - /* Is this for us? */ - if (getpid() != pid) { - r = 0; - goto finish; - } - - e = getenv("LISTEN_FDS"); - if (!e) { - r = 0; - goto finish; - } - - r = safe_atoi(e, &n); - if (r < 0) - goto finish; - - 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 = n; - -finish: - 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; - } - - *names = l; - l = NULL; - - return n_fds; -} - -_public_ int sd_is_fifo(int fd, const char *path) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISFIFO(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - } - - return 1; -} - -_public_ int sd_is_special(int fd, const char *path) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) - return st_path.st_rdev == st_fd.st_rdev; - else - return 0; - } - - return 1; -} - -static int sd_is_socket_internal(int fd, int type, int listening) { - struct stat st_fd; - - assert_return(fd >= 0, -EBADF); - assert_return(type >= 0, -EINVAL); - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISSOCK(st_fd.st_mode)) - return 0; - - if (type != 0) { - int other_type = 0; - socklen_t l = sizeof(other_type); - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) - return -errno; - - if (l != sizeof(other_type)) - return -EINVAL; - - if (other_type != type) - return 0; - } - - if (listening >= 0) { - int accepting = 0; - socklen_t l = sizeof(accepting); - - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) - return -errno; - - if (l != sizeof(accepting)) - return -EINVAL; - - if (!accepting != !listening) - return 0; - } - - return 1; -} - -_public_ int sd_is_socket(int fd, int family, int type, int listening) { - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(family >= 0, -EINVAL); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (family > 0) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - return sockaddr.sa.sa_family == family; - } - - return 1; -} - -_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_INET && - sockaddr.sa.sa_family != AF_INET6) - return 0; - - if (family != 0) - if (sockaddr.sa.sa_family != family) - return 0; - - if (port > 0) { - if (sockaddr.sa.sa_family == AF_INET) { - if (l < sizeof(struct sockaddr_in)) - return -EINVAL; - - return htobe16(port) == sockaddr.in.sin_port; - } else { - if (l < sizeof(struct sockaddr_in6)) - return -EINVAL; - - return htobe16(port) == sockaddr.in6.sin6_port; - } - } - - return 1; -} - -_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { - union sockaddr_union sockaddr = {}; - socklen_t l = sizeof(sockaddr); - int r; - - assert_return(fd >= 0, -EBADF); - - r = sd_is_socket_internal(fd, type, listening); - if (r <= 0) - return r; - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_UNIX) - return 0; - - if (path) { - if (length == 0) - length = strlen(path); - - if (length == 0) - /* Unnamed socket */ - return l == offsetof(struct sockaddr_un, sun_path); - - if (path[0]) - /* Normal path socket */ - return - (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && - memcmp(path, sockaddr.un.sun_path, length+1) == 0; - else - /* Abstract namespace socket */ - return - (l == offsetof(struct sockaddr_un, sun_path) + length) && - memcmp(path, sockaddr.un.sun_path, length) == 0; - } - - return 1; -} - -_public_ int sd_is_mq(int fd, const char *path) { - struct mq_attr attr; - - /* Check that the fd is valid */ - assert_return(fcntl(fd, F_GETFD) >= 0, -errno); - - 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]; - struct stat a, b; - - assert_return(path_is_absolute(path), -EINVAL); - - if (fstat(fd, &a) < 0) - return -errno; - - strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); - fpath[sizeof(fpath)-1] = 0; - - if (stat(fpath, &b) < 0) - return -errno; - - if (a.st_dev != b.st_dev || - a.st_ino != b.st_ino) - return 0; - } - - return 1; -} - -_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { - union sockaddr_union sockaddr = { - .sa.sa_family = AF_UNIX, - }; - struct iovec iovec = { - .iov_base = (char*) state, - }; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_name = &sockaddr, - }; - _cleanup_close_ int fd = -1; - struct cmsghdr *cmsg = NULL; - const char *e; - bool have_pid; - int r; - - if (!state) { - r = -EINVAL; - goto finish; - } - - if (n_fds > 0 && !fds) { - r = -EINVAL; - goto finish; - } - - e = getenv("NOTIFY_SOCKET"); - if (!e) - return 0; - - /* Must be an abstract socket, or an absolute path */ - if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { - r = -EINVAL; - 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)); - if (sockaddr.un.sun_path[0] == '@') - sockaddr.un.sun_path[0] = 0; - - msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); - - have_pid = pid != 0 && pid != getpid(); - - if (n_fds > 0 || have_pid) { - /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated 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) { - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); - - memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); - - if (have_pid) - assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); - } - - if (have_pid) { - struct ucred *ucred; - - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - - ucred = (struct ucred*) CMSG_DATA(cmsg); - ucred->pid = pid; - ucred->uid = getuid(); - ucred->gid = getgid(); - } - } - - /* First try with fake ucred data, as requested */ - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { - r = 1; - goto finish; - } - - /* If that failed, try with our own ucred instead */ - if (have_pid) { - msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); - if (msghdr.msg_controllen == 0) - msghdr.msg_control = NULL; - - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { - r = 1; - goto finish; - } - } - - r = -errno; - -finish: - if (unset_environment) - unsetenv("NOTIFY_SOCKET"); - - return r; -} - -_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { - return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); -} - -_public_ int sd_notify(int unset_environment, const char *state) { - return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0); -} - -_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) { - _cleanup_free_ char *p = NULL; - int r; - - if (format) { - va_list ap; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - } - - return sd_pid_notify(pid, unset_environment, p); -} - -_public_ int sd_notifyf(int unset_environment, const char *format, ...) { - _cleanup_free_ char *p = NULL; - int r; - - if (format) { - va_list ap; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - } - - return sd_pid_notify(0, unset_environment, p); -} - -_public_ int sd_booted(void) { - /* 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. */ - - return laccess("/run/systemd/system/", F_OK) >= 0; -} - -_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) { - const char *s, *p = ""; /* p is set to dummy value to do unsetting */ - uint64_t u; - int r = 0; - - s = getenv("WATCHDOG_USEC"); - if (!s) - goto finish; - - r = safe_atou64(s, &u); - if (r < 0) - goto finish; - if (u <= 0 || u >= USEC_INFINITY) { - r = -EINVAL; - goto finish; - } - - p = getenv("WATCHDOG_PID"); - if (p) { - pid_t pid; - - r = parse_pid(p, &pid); - if (r < 0) - goto finish; - - /* Is this for us? */ - if (getpid() != pid) { - r = 0; - goto finish; - } - } - - if (usec) - *usec = u; - - r = 1; - -finish: - if (unset_environment && s) - unsetenv("WATCHDOG_USEC"); - if (unset_environment && p) - unsetenv("WATCHDOG_PID"); - - return r; -} diff --git a/src/libsystemd/sd-device/Makefile b/src/libsystemd/sd-device/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-device/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h deleted file mode 100644 index eb06f9542d..0000000000 --- a/src/libsystemd/sd-device/device-enumerator-private.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-device.h" - -int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); -int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); -int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); -sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); -sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); - -#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \ - for (device = device_enumerator_get_first(enumerator); \ - device; \ - device = device_enumerator_get_next(enumerator)) diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c deleted file mode 100644 index 62d03ae00d..0000000000 --- a/src/libsystemd/sd-device/device-enumerator.c +++ /dev/null @@ -1,988 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014-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 . -***/ - -#include "sd-device.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 - -typedef enum DeviceEnumerationType { - DEVICE_ENUMERATION_TYPE_DEVICES, - DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, - _DEVICE_ENUMERATION_TYPE_MAX, - _DEVICE_ENUMERATION_TYPE_INVALID = -1, -} DeviceEnumerationType; - -struct sd_device_enumerator { - unsigned n_ref; - - DeviceEnumerationType type; - Prioq *devices; - bool scan_uptodate; - - Set *match_subsystem; - Set *nomatch_subsystem; - Hashmap *match_sysattr; - Hashmap *nomatch_sysattr; - Hashmap *match_property; - Set *match_sysname; - Set *match_tag; - sd_device *match_parent; - bool match_allow_uninitialized; -}; - -_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL; - - assert(ret); - - enumerator = new0(sd_device_enumerator, 1); - if (!enumerator) - return -ENOMEM; - - enumerator->n_ref = 1; - enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID; - - *ret = enumerator; - enumerator = NULL; - - return 0; -} - -_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - assert_se((++ enumerator->n_ref) >= 2); - - return enumerator; -} - -_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { - if (enumerator && (-- enumerator->n_ref) == 0) { - sd_device *device; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - prioq_free(enumerator->devices); - - set_free_free(enumerator->match_subsystem); - set_free_free(enumerator->nomatch_subsystem); - hashmap_free_free_free(enumerator->match_sysattr); - hashmap_free_free_free(enumerator->nomatch_sysattr); - hashmap_free_free_free(enumerator->match_property); - set_free_free(enumerator->match_sysname); - set_free_free(enumerator->match_tag); - sd_device_unref(enumerator->match_parent); - - free(enumerator); - } - - return NULL; -} - -_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { - Set **set; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(subsystem, -EINVAL); - - if (match) - set = &enumerator->match_subsystem; - else - set = &enumerator->nomatch_subsystem; - - r = set_ensure_allocated(set, NULL); - if (r < 0) - return r; - - r = set_put_strdup(*set, subsystem); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { - _cleanup_free_ char *sysattr = NULL, *value = NULL; - Hashmap **hashmap; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(_sysattr, -EINVAL); - - if (match) - hashmap = &enumerator->match_sysattr; - else - hashmap = &enumerator->nomatch_sysattr; - - r = hashmap_ensure_allocated(hashmap, NULL); - if (r < 0) - return r; - - sysattr = strdup(_sysattr); - if (!sysattr) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(*hashmap, sysattr, value); - if (r < 0) - return r; - - sysattr = NULL; - value = NULL; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { - _cleanup_free_ char *property = NULL, *value = NULL; - int r; - - assert_return(enumerator, -EINVAL); - assert_return(_property, -EINVAL); - - r = hashmap_ensure_allocated(&enumerator->match_property, NULL); - if (r < 0) - return r; - - property = strdup(_property); - if (!property) - return -ENOMEM; - - if (_value) { - value = strdup(_value); - if (!value) - return -ENOMEM; - } - - r = hashmap_put(enumerator->match_property, property, value); - if (r < 0) - return r; - - property = NULL; - value = NULL; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(sysname, -EINVAL); - - r = set_ensure_allocated(&enumerator->match_sysname, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_sysname, sysname); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(tag, -EINVAL); - - r = set_ensure_allocated(&enumerator->match_tag, NULL); - if (r < 0) - return r; - - r = set_put_strdup(enumerator->match_tag, tag); - if (r < 0) - return r; - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { - assert_return(enumerator, -EINVAL); - assert_return(parent, -EINVAL); - - sd_device_unref(enumerator->match_parent); - enumerator->match_parent = sd_device_ref(parent); - - enumerator->scan_uptodate = false; - - return 0; -} - -_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { - assert_return(enumerator, -EINVAL); - - enumerator->match_allow_uninitialized = true; - - enumerator->scan_uptodate = false; - - return 0; -} - -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { - assert_return(enumerator, -EINVAL); - - enumerator->match_allow_uninitialized = false; - - enumerator->scan_uptodate = false; - - return 0; -} - -static int device_compare(const void *_a, const void *_b) { - sd_device *a = (sd_device *)_a, *b = (sd_device *)_b; - const char *devpath_a, *devpath_b, *sound_a; - bool delay_a, delay_b; - - assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); - - sound_a = strstr(devpath_a, "/sound/card"); - if (sound_a) { - /* For sound cards the control device must be enumerated last to - * make sure it's the final device node that gets ACLs applied. - * Applications rely on this fact and use ACL changes on the - * control node as an indicator that the ACL change of the - * entire sound card completed. The kernel makes this guarantee - * when creating those devices, and hence we should too when - * enumerating them. */ - sound_a += strlen("/sound/card"); - sound_a = strchr(sound_a, '/'); - - if (sound_a) { - unsigned prefix_len; - - prefix_len = sound_a - devpath_a; - - if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { - const char *sound_b; - - sound_b = devpath_b + prefix_len; - - if (startswith(sound_a, "/controlC") && - !startswith(sound_b, "/contolC")) - return 1; - - if (!startswith(sound_a, "/controlC") && - startswith(sound_b, "/controlC")) - return -1; - } - } - } - - /* md and dm devices are enumerated after all other devices */ - delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); - delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); - if (delay_a != delay_b) - return delay_a - delay_b; - - return strcmp(devpath_a, devpath_b); -} - -int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { - int r; - - assert_return(enumerator, -EINVAL); - assert_return(device, -EINVAL); - - r = prioq_ensure_allocated(&enumerator->devices, device_compare); - if (r < 0) - return r; - - r = prioq_put(enumerator->devices, device, NULL); - if (r < 0) - return r; - - sd_device_ref(device); - - return 0; -} - -static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { - const char *value; - int r; - - assert(device); - assert(sysattr); - - r = sd_device_get_sysattr_value(device, sysattr, &value); - if (r < 0) - return false; - - if (!match_value) - return true; - - if (fnmatch(match_value, value, 0) == 0) - return true; - - return false; -} - -static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) { - const char *sysattr; - const char *value; - Iterator i; - - assert(enumerator); - assert(device); - - HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i) - if (match_sysattr_value(device, sysattr, value)) - return false; - - HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i) - if (!match_sysattr_value(device, sysattr, value)) - return false; - - return true; -} - -static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { - const char *property; - const char *value; - Iterator i; - - assert(enumerator); - assert(device); - - if (hashmap_isempty(enumerator->match_property)) - return true; - - HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) { - const char *property_dev, *value_dev; - - FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { - if (fnmatch(property, property_dev, 0) != 0) - continue; - - if (!value && !value_dev) - return true; - - if (!value || !value_dev) - continue; - - if (fnmatch(value, value_dev, 0) == 0) - return true; - } - } - - return false; -} - -static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { - const char *tag; - Iterator i; - - assert(enumerator); - assert(device); - - SET_FOREACH(tag, enumerator->match_tag, i) - if (!sd_device_has_tag(device, tag)) - return false; - - return true; -} - -static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { - const char *devpath, *devpath_dev; - int r; - - assert(enumerator); - assert(device); - - if (!enumerator->match_parent) - return true; - - r = sd_device_get_devpath(enumerator->match_parent, &devpath); - assert(r >= 0); - - r = sd_device_get_devpath(device, &devpath_dev); - assert(r >= 0); - - return startswith(devpath_dev, devpath); -} - -static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { - const char *sysname_match; - Iterator i; - - assert(enumerator); - assert(sysname); - - if (set_isempty(enumerator->match_sysname)) - return true; - - SET_FOREACH(sysname_match, enumerator->match_sysname, i) - if (fnmatch(sysname_match, sysname, 0) == 0) - return true; - - return false; -} - -static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - assert(enumerator); - assert(basedir); - - path = strjoina("/sys/", basedir, "/"); - - if (subdir1) - path = strjoina(path, subdir1, "/"); - - if (subdir2) - path = strjoina(path, subdir2, "/"); - - dir = opendir(path); - if (!dir) - return -errno; - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1]; - dev_t devnum; - int ifindex, initialized, k; - - if (dent->d_name[0] == '.') - continue; - - if (!match_sysname(enumerator, dent->d_name)) - continue; - - (void)sprintf(syspath, "%s%s", path, dent->d_name); - - k = sd_device_new_from_syspath(&device, syspath); - if (k < 0) { - if (k != -ENODEV) - /* this is necessarily racey, so ignore missing devices */ - r = k; - - continue; - } - - k = sd_device_get_devnum(device, &devnum); - if (k < 0) { - r = k; - continue; - } - - k = sd_device_get_ifindex(device, &ifindex); - if (k < 0) { - r = k; - continue; - } - - k = sd_device_get_is_initialized(device, &initialized); - if (k < 0) { - r = k; - continue; - } - - /* - * All devices with a device node or network interfaces - * possibly need udev to adjust the device node permission - * or context, or rename the interface before it can be - * reliably used from other processes. - * - * For now, we can only check these types of devices, we - * might not store a database, and have no way to find out - * for all other types of devices. - */ - if (!enumerator->match_allow_uninitialized && - !initialized && - (major(devnum) > 0 || ifindex > 0)) - continue; - - if (!match_parent(enumerator, device)) - continue; - - if (!match_tag(enumerator, device)) - continue; - - if (!match_property(enumerator, device)) - continue; - - if (!match_sysattr(enumerator, device)) - continue; - - k = device_enumerator_add_device(enumerator, device); - if (k < 0) - r = k; - } - - return r; -} - -static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { - const char *subsystem_match; - Iterator i; - - assert(enumerator); - - if (!subsystem) - return false; - - SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i) - if (fnmatch(subsystem_match, subsystem, 0) == 0) - return false; - - if (set_isempty(enumerator->match_subsystem)) - return true; - - SET_FOREACH(subsystem_match, enumerator->match_subsystem, i) - if (fnmatch(subsystem_match, subsystem, 0) == 0) - return true; - - return false; -} - -static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - path = strjoina("/sys/", basedir); - - dir = opendir(path); - if (!dir) - return -errno; - - log_debug(" device-enumerator: scanning %s", path); - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - int k; - - if (dent->d_name[0] == '.') - continue; - - if (!match_subsystem(enumerator, subsystem ? : dent->d_name)) - continue; - - k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); - if (k < 0) - r = k; - } - - return r; -} - -static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) { - _cleanup_closedir_ DIR *dir = NULL; - char *path; - struct dirent *dent; - int r = 0; - - assert(enumerator); - assert(tag); - - path = strjoina("/run/udev/tags/", tag); - - dir = opendir(path); - if (!dir) { - if (errno == ENOENT) - return 0; - else { - log_error("sd-device-enumerator: could not open tags directory %s: %m", path); - return -errno; - } - } - - /* TODO: filter away subsystems? */ - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *subsystem, *sysname; - int k; - - if (dent->d_name[0] == '.') - continue; - - k = sd_device_new_from_device_id(&device, dent->d_name); - if (k < 0) { - if (k != -ENODEV) - /* this is necessarily racy, so ignore missing devices */ - r = k; - - continue; - } - - k = sd_device_get_subsystem(device, &subsystem); - if (k < 0) { - r = k; - continue; - } - - if (!match_subsystem(enumerator, subsystem)) - continue; - - k = sd_device_get_sysname(device, &sysname); - if (k < 0) { - r = k; - continue; - } - - if (!match_sysname(enumerator, sysname)) - continue; - - if (!match_parent(enumerator, device)) - continue; - - if (!match_property(enumerator, device)) - continue; - - if (!match_sysattr(enumerator, device)) - continue; - - k = device_enumerator_add_device(enumerator, device); - if (k < 0) { - r = k; - continue; - } - } - - return r; -} - -static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) { - const char *tag; - Iterator i; - int r = 0; - - assert(enumerator); - - SET_FOREACH(tag, enumerator->match_tag, i) { - int k; - - k = enumerator_scan_devices_tag(enumerator, tag); - if (k < 0) - r = k; - } - - return r; -} - -static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *subsystem, *sysname; - int r; - - r = sd_device_new_from_syspath(&device, path); - if (r == -ENODEV) - /* this is necessarily racy, so ignore missing devices */ - return 0; - else if (r < 0) - return r; - - r = sd_device_get_subsystem(device, &subsystem); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - if (!match_subsystem(enumerator, subsystem)) - return 0; - - r = sd_device_get_sysname(device, &sysname); - if (r < 0) - return r; - - if (!match_sysname(enumerator, sysname)) - return 0; - - if (!match_property(enumerator, device)) - return 0; - - if (!match_sysattr(enumerator, device)) - return 0; - - r = device_enumerator_add_device(enumerator, device); - if (r < 0) - return r; - - return 1; -} - -static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - int r = 0; - - dir = opendir(path); - if (!dir) { - log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); - return -errno; - } - - FOREACH_DIRENT_ALL(dent, dir, return -errno) { - _cleanup_free_ char *child = NULL; - int k; - - if (dent->d_name[0] == '.') - continue; - - if (dent->d_type != DT_DIR) - continue; - - child = strjoin(path, "/", dent->d_name, NULL); - if (!child) - return -ENOMEM; - - k = parent_add_child(enumerator, child); - if (k < 0) - r = k; - - if (maxdepth > 0) - parent_crawl_children(enumerator, child, maxdepth - 1); - else - log_debug("device-enumerate: max depth reached, %s: ignoring devices", child); - } - - return r; -} - -static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { - const char *path; - int r = 0, k; - - r = sd_device_get_syspath(enumerator->match_parent, &path); - if (r < 0) - return r; - - k = parent_add_child(enumerator, path); - if (k < 0) - r = k; - - k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); - if (k < 0) - r = k; - - return r; -} - -static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { - int r = 0; - - log_debug("device-enumerator: scan all dirs"); - - if (access("/sys/subsystem", F_OK) >= 0) { - /* we have /subsystem/, forget all the old stuff */ - r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); - if (r < 0) - return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m"); - } else { - int k; - - k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m"); - r = k; - } - - k = enumerator_scan_dir(enumerator, "class", NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m"); - r = k; - } - } - - return r; -} - -int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { - sd_device *device; - int r = 0, k; - - assert(enumerator); - - if (enumerator->scan_uptodate && - enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) - return 0; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - if (!set_isempty(enumerator->match_tag)) { - k = enumerator_scan_devices_tags(enumerator); - if (k < 0) - r = k; - } else if (enumerator->match_parent) { - k = enumerator_scan_devices_children(enumerator); - if (k < 0) - r = k; - } else { - k = enumerator_scan_devices_all(enumerator); - if (k < 0) - r = k; - } - - enumerator->scan_uptodate = true; - - return r; -} - -_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { - int r; - - assert_return(enumerator, NULL); - - r = device_enumerator_scan_devices(enumerator); - if (r < 0) - return NULL; - - enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; - - return prioq_peek(enumerator->devices); -} - -_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - if (!enumerator->scan_uptodate || - enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES) - return NULL; - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} - -int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { - sd_device *device; - const char *subsysdir; - int r = 0, k; - - assert(enumerator); - - if (enumerator->scan_uptodate && - enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) - return 0; - - while ((device = prioq_pop(enumerator->devices))) - sd_device_unref(device); - - /* modules */ - if (match_subsystem(enumerator, "module")) { - k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan modules: %m"); - r = k; - } - } - - if (access("/sys/subsystem", F_OK) >= 0) - subsysdir = "subsystem"; - else - subsysdir = "bus"; - - /* subsystems (only buses support coldplug) */ - if (match_subsystem(enumerator, "subsystem")) { - k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m"); - r = k; - } - } - - /* subsystem drivers */ - if (match_subsystem(enumerator, "drivers")) { - k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); - if (k < 0) { - log_debug_errno(k, "device-enumerator: failed to scan drivers: %m"); - r = k; - } - } - - enumerator->scan_uptodate = true; - - return r; -} - -_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { - int r; - - assert_return(enumerator, NULL); - - r = device_enumerator_scan_subsystems(enumerator); - if (r < 0) - return NULL; - - enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; - - return prioq_peek(enumerator->devices); -} - -_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - if (enumerator->scan_uptodate || - enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) - return NULL; - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} - -sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - return prioq_peek(enumerator->devices); -} - -sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { - assert_return(enumerator, NULL); - - sd_device_unref(prioq_pop(enumerator->devices)); - - return prioq_peek(enumerator->devices); -} diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h deleted file mode 100644 index 9fad388953..0000000000 --- a/src/libsystemd/sd-device/device-internal.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include "hashmap.h" -#include "set.h" - -struct sd_device { - uint64_t n_ref; - - sd_device *parent; - bool parent_set; /* no need to try to reload parent */ - - OrderedHashmap *properties; - Iterator properties_iterator; - uint64_t properties_generation; /* changes whenever the properties are changed */ - uint64_t properties_iterator_generation; /* generation when iteration was started */ - - /* the subset of the properties that should be written to the db*/ - OrderedHashmap *properties_db; - - Hashmap *sysattr_values; /* cached sysattr values */ - - Set *sysattrs; /* names of sysattrs */ - Iterator sysattrs_iterator; - bool sysattrs_read; /* don't try to re-read sysattrs once read */ - - Set *tags; - Iterator tags_iterator; - uint64_t tags_generation; /* changes whenever the tags are changed */ - uint64_t tags_iterator_generation; /* generation when iteration was started */ - bool property_tags_outdated; /* need to update TAGS= property */ - - Set *devlinks; - Iterator devlinks_iterator; - uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ - uint64_t devlinks_iterator_generation; /* generation when iteration was started */ - bool property_devlinks_outdated; /* need to update DEVLINKS= property */ - int devlink_priority; - - char **properties_strv; /* the properties hashmap as a strv */ - uint8_t *properties_nulstr; /* the same as a nulstr */ - size_t properties_nulstr_len; - bool properties_buf_outdated; /* need to reread hashmap */ - - int watch_handle; - - char *syspath; - const char *devpath; - const char *sysnum; - char *sysname; - bool sysname_set; /* don't reread sysname */ - - char *devtype; - int ifindex; - char *devname; - dev_t devnum; - - char *subsystem; - bool subsystem_set; /* don't reread subsystem */ - char *driver_subsystem; /* only set for the 'drivers' subsystem */ - bool driver_subsystem_set; /* don't reread subsystem */ - char *driver; - bool driver_set; /* don't reread driver */ - - char *id_filename; - - bool is_initialized; - uint64_t usec_initialized; - - mode_t devmode; - uid_t devuid; - gid_t devgid; - - bool uevent_loaded; /* don't reread uevent */ - bool db_loaded; /* don't reread db */ - - bool sealed; /* don't read more information from uevent/db */ - bool db_persist; /* don't clean up the db when switching from initrd to real root */ -}; - -typedef enum DeviceAction { - DEVICE_ACTION_ADD, - DEVICE_ACTION_REMOVE, - DEVICE_ACTION_CHANGE, - DEVICE_ACTION_MOVE, - DEVICE_ACTION_ONLINE, - DEVICE_ACTION_OFFLINE, - _DEVICE_ACTION_MAX, - _DEVICE_ACTION_INVALID = -1, -} DeviceAction; - -int device_new_aux(sd_device **ret); -int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); -int device_add_property_internal(sd_device *device, const char *key, const char *value); -int device_read_uevent_file(sd_device *device); -int device_read_db_aux(sd_device *device, bool force); - -int device_set_syspath(sd_device *device, const char *_syspath, bool verify); -int device_set_ifindex(sd_device *device, const char *ifindex); -int device_set_devmode(sd_device *device, const char *devmode); -int device_set_devname(sd_device *device, const char *_devname); -int device_set_devtype(sd_device *device, const char *_devtype); -int device_set_devnum(sd_device *device, const char *major, const char *minor); -int device_set_subsystem(sd_device *device, const char *_subsystem); -int device_set_driver(sd_device *device, const char *_driver); -int device_set_usec_initialized(sd_device *device, const char *initialized); - -DeviceAction device_action_from_string(const char *s) _pure_; -const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c deleted file mode 100644 index 9082d377f4..0000000000 --- a/src/libsystemd/sd-device/device-private.c +++ /dev/null @@ -1,1119 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include -#include -#include - -#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 "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; - - assert(device); - assert(key); - - r = device_add_property_aux(device, key, value, false); - if (r < 0) - return r; - - if (key[0] != '.') { - r = device_add_property_aux(device, key, value, true); - if (r < 0) - return r; - } - - return 0; -} - -static int device_add_property_internal_from_string(sd_device *device, const char *str) { - _cleanup_free_ char *key = NULL; - char *value; - - assert(device); - assert(str); - - key = strdup(str); - if (!key) - return -ENOMEM; - - value = strchr(key, '='); - if (!value) - return -EINVAL; - - *value = '\0'; - - if (isempty(++value)) - value = NULL; - - return device_add_property_internal(device, key, value); -} - -static int handle_db_line(sd_device *device, char key, const char *value) { - char *path; - int r; - - assert(device); - assert(value); - - switch (key) { - case 'S': - path = strjoina("/dev/", value); - r = device_add_devlink(device, path); - if (r < 0) - return r; - - break; - case 'L': - r = safe_atoi(value, &device->devlink_priority); - if (r < 0) - return r; - - break; - case 'E': - r = device_add_property_internal_from_string(device, value); - if (r < 0) - return r; - - break; - case 'G': - r = device_add_tag(device, value); - if (r < 0) - return r; - - break; - case 'W': - r = safe_atoi(value, &device->watch_handle); - if (r < 0) - return r; - - break; - case 'I': - r = device_set_usec_initialized(device, value); - if (r < 0) - return r; - - break; - default: - log_debug("device db: unknown key '%c'", key); - } - - return 0; -} - -void device_set_devlink_priority(sd_device *device, int priority) { - assert(device); - - device->devlink_priority = priority; -} - -void device_set_is_initialized(sd_device *device) { - assert(device); - - device->is_initialized = true; -} - -int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { - char num[DECIMAL_STR_MAX(usec_t)]; - usec_t usec_initialized; - int r; - - assert(device); - - if (device_old && device_old->usec_initialized > 0) - usec_initialized = device_old->usec_initialized; - else - usec_initialized = now(CLOCK_MONOTONIC); - - r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); - if (r < 0) - return -errno; - - r = device_set_usec_initialized(device, num); - if (r < 0) - return r; - - return 0; -} - -static int device_read_db(sd_device *device) { - _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; - char key; - size_t db_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - assert(device); - - if (device->db_loaded || device->sealed) - return 0; - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = read_full_file(path, &db, &db_len); - if (r < 0) { - if (r == -ENOENT) - return 0; - else - return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); - } - - /* devices with a database entry are initialized */ - device_set_is_initialized(device); - - for (i = 0; i < db_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, db[i])) { - key = db[i]; - - state = KEY; - } - - break; - case KEY: - if (db[i] != ':') { - log_debug("sd-device: ignoring invalid db entry with key '%c'", key); - - state = INVALID_LINE; - } else { - db[i] = '\0'; - - state = PRE_VALUE; - } - - break; - case PRE_VALUE: - value = &db[i]; - - state = VALUE; - - break; - case INVALID_LINE: - if (strchr(NEWLINE, db[i])) - state = PRE_KEY; - - break; - case VALUE: - if (strchr(NEWLINE, db[i])) { - db[i] = '\0'; - r = handle_db_line(device, key, value); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing db"); - } - } - - device->db_loaded = true; - - return 0; -} - -uint64_t device_get_properties_generation(sd_device *device) { - assert(device); - - return device->properties_generation; -} - -uint64_t device_get_tags_generation(sd_device *device) { - assert(device); - - return device->tags_generation; -} - -uint64_t device_get_devlinks_generation(sd_device *device) { - assert(device); - - return device->devlinks_generation; -} - -int device_get_devnode_mode(sd_device *device, mode_t *mode) { - int r; - - assert(device); - assert(mode); - - r = device_read_db(device); - if (r < 0) - return r; - - *mode = device->devmode; - - return 0; -} - -int device_get_devnode_uid(sd_device *device, uid_t *uid) { - int r; - - assert(device); - assert(uid); - - r = device_read_db(device); - if (r < 0) - return r; - - *uid = device->devuid; - - return 0; -} - -static int device_set_devuid(sd_device *device, const char *uid) { - unsigned u; - int r; - - assert(device); - assert(uid); - - r = safe_atou(uid, &u); - if (r < 0) - return r; - - r = device_add_property_internal(device, "DEVUID", uid); - if (r < 0) - return r; - - device->devuid = u; - - return 0; -} - -int device_get_devnode_gid(sd_device *device, gid_t *gid) { - int r; - - assert(device); - assert(gid); - - r = device_read_db(device); - if (r < 0) - return r; - - *gid = device->devgid; - - return 0; -} - -static int device_set_devgid(sd_device *device, const char *gid) { - unsigned g; - int r; - - assert(device); - assert(gid); - - r = safe_atou(gid, &g); - if (r < 0) - return r; - - r = device_add_property_internal(device, "DEVGID", gid); - if (r < 0) - return r; - - device->devgid = g; - - return 0; -} - -static int device_amend(sd_device *device, const char *key, const char *value) { - int r; - - assert(device); - assert(key); - assert(value); - - if (streq(key, "DEVPATH")) { - char *path; - - path = strjoina("/sys", value); - - /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ - r = device_set_syspath(device, path, false); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path); - } else if (streq(key, "SUBSYSTEM")) { - r = device_set_subsystem(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value); - } else if (streq(key, "DEVTYPE")) { - r = device_set_devtype(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value); - } else if (streq(key, "DEVNAME")) { - r = device_set_devname(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value); - } else if (streq(key, "USEC_INITIALIZED")) { - r = device_set_usec_initialized(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value); - } else if (streq(key, "DRIVER")) { - r = device_set_driver(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value); - } else if (streq(key, "IFINDEX")) { - r = device_set_ifindex(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value); - } else if (streq(key, "DEVMODE")) { - r = device_set_devmode(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value); - } else if (streq(key, "DEVUID")) { - r = device_set_devuid(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value); - } else if (streq(key, "DEVGID")) { - r = device_set_devgid(device, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value); - } else if (streq(key, "DEVLINKS")) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, value, state) { - char devlink[l + 1]; - - strncpy(devlink, word, l); - devlink[l] = '\0'; - - r = device_add_devlink(device, devlink); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink); - } - } else if (streq(key, "TAGS")) { - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { - char tag[l + 1]; - - (void)strncpy(tag, word, l); - tag[l] = '\0'; - - r = device_add_tag(device, tag); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag); - } - } else { - r = device_add_property_internal(device, key, value); - if (r < 0) - return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value); - } - - return 0; -} - -static const char* const device_action_table[_DEVICE_ACTION_MAX] = { - [DEVICE_ACTION_ADD] = "add", - [DEVICE_ACTION_REMOVE] = "remove", - [DEVICE_ACTION_CHANGE] = "change", - [DEVICE_ACTION_MOVE] = "move", - [DEVICE_ACTION_ONLINE] = "online", - [DEVICE_ACTION_OFFLINE] = "offline", -}; - -DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); - -static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, - DeviceAction *_action) { - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum = 0; - const char *major = NULL, *minor = NULL; - char *value; - int r; - - assert(device); - assert(key); - assert(_major); - assert(_minor); - assert(_seqnum); - assert(_action); - - value = strchr(key, '='); - if (!value) { - log_debug("sd-device: not a key-value pair: '%s'", key); - return -EINVAL; - } - - *value = '\0'; - - value++; - - if (streq(key, "MAJOR")) - major = value; - else if (streq(key, "MINOR")) - minor = value; - else { - if (streq(key, "ACTION")) { - action = device_action_from_string(value); - if (action == _DEVICE_ACTION_INVALID) - return -EINVAL; - } else if (streq(key, "SEQNUM")) { - r = safe_atou64(value, &seqnum); - if (r < 0) - return r; - else if (seqnum == 0) - /* kernel only sends seqnum > 0 */ - return -EINVAL; - } - - r = device_amend(device, key, value); - if (r < 0) - return r; - } - - if (major != 0) - *_major = major; - - if (minor != 0) - *_minor = minor; - - if (action != _DEVICE_ACTION_INVALID) - *_action = action; - - if (seqnum > 0) - *_seqnum = seqnum; - - return 0; -} - -void device_seal(sd_device *device) { - assert(device); - - device->sealed = true; -} - -static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { - assert(device); - - if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { - log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum"); - return -EINVAL; - } - - device->sealed = true; - - return 0; -} - -int device_new_from_strv(sd_device **ret, char **strv) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - char **key; - const char *major = NULL, *minor = NULL; - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum; - int r; - - assert(ret); - assert(strv); - - r = device_new_aux(&device); - if (r < 0) - return r; - - STRV_FOREACH(key, strv) { - r = device_append(device, *key, &major, &minor, &seqnum, &action); - if (r < 0) - return r; - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); - } - - r = device_verify(device, action, seqnum); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - const char *major = NULL, *minor = NULL; - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum; - unsigned i = 0; - int r; - - assert(ret); - assert(nulstr); - assert(len); - - r = device_new_aux(&device); - if (r < 0) - return r; - - while (i < len) { - char *key; - const char *end; - - key = (char*)&nulstr[i]; - end = memchr(key, '\0', len - i); - if (!end) { - log_debug("sd-device: failed to parse nulstr"); - return -EINVAL; - } - i += end - key + 1; - - r = device_append(device, key, &major, &minor, &seqnum, &action); - if (r < 0) - return r; - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); - } - - r = device_verify(device, action, seqnum); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -static int device_update_properties_bufs(sd_device *device) { - const char *val, *prop; - _cleanup_free_ char **buf_strv = NULL; - _cleanup_free_ uint8_t *buf_nulstr = NULL; - size_t allocated_nulstr = 0; - size_t nulstr_len = 0, num = 0, i = 0; - - assert(device); - - if (!device->properties_buf_outdated) - return 0; - - FOREACH_DEVICE_PROPERTY(device, prop, val) { - size_t len = 0; - - len = strlen(prop) + 1 + strlen(val); - - buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); - if (!buf_nulstr) - return -ENOMEM; - - strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); - nulstr_len += len + 1; - ++num; - } - - /* build buf_strv from buf_nulstr */ - buf_strv = new0(char *, num + 1); - if (!buf_strv) - return -ENOMEM; - - NULSTR_FOREACH(val, (char*) buf_nulstr) { - buf_strv[i] = (char *) val; - assert(i < num); - i++; - } - - free(device->properties_nulstr); - device->properties_nulstr = buf_nulstr; - buf_nulstr = NULL; - device->properties_nulstr_len = nulstr_len; - free(device->properties_strv); - device->properties_strv = buf_strv; - buf_strv = NULL; - - device->properties_buf_outdated = false; - - return 0; -} - -int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { - int r; - - assert(device); - assert(nulstr); - assert(len); - - r = device_update_properties_bufs(device); - if (r < 0) - return r; - - *nulstr = device->properties_nulstr; - *len = device->properties_nulstr_len; - - return 0; -} - -int device_get_properties_strv(sd_device *device, char ***strv) { - int r; - - assert(device); - assert(strv); - - r = device_update_properties_bufs(device); - if (r < 0) - return r; - - *strv = device->properties_strv; - - return 0; -} - -int device_get_devlink_priority(sd_device *device, int *priority) { - int r; - - assert(device); - assert(priority); - - r = device_read_db(device); - if (r < 0) - return r; - - *priority = device->devlink_priority; - - return 0; -} - -int device_get_watch_handle(sd_device *device, int *handle) { - int r; - - assert(device); - assert(handle); - - r = device_read_db(device); - if (r < 0) - return r; - - *handle = device->watch_handle; - - return 0; -} - -void device_set_watch_handle(sd_device *device, int handle) { - assert(device); - - device->watch_handle = handle; -} - -int device_rename(sd_device *device, const char *name) { - _cleanup_free_ char *dirname = NULL; - char *new_syspath; - const char *interface; - int r; - - assert(device); - assert(name); - - dirname = dirname_malloc(device->syspath); - if (!dirname) - return -ENOMEM; - - new_syspath = strjoina(dirname, "/", name); - - /* the user must trust that the new name is correct */ - r = device_set_syspath(device, new_syspath, false); - if (r < 0) - return r; - - r = sd_device_get_property_value(device, "INTERFACE", &interface); - if (r >= 0) { - r = device_add_property_internal(device, "INTERFACE", name); - if (r < 0) - return r; - - /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ - r = device_add_property_internal(device, "INTERFACE_OLD", interface); - if (r < 0) - return r; - } else if (r != -ENOENT) - return r; - - return 0; -} - -int device_shallow_clone(sd_device *old_device, sd_device **new_device) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(old_device); - assert(new_device); - - r = device_new_aux(&ret); - if (r < 0) - return r; - - r = device_set_syspath(ret, old_device->syspath, false); - if (r < 0) - return r; - - r = device_set_subsystem(ret, old_device->subsystem); - if (r < 0) - return r; - - ret->devnum = old_device->devnum; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_clone_with_db(sd_device *old_device, sd_device **new_device) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(old_device); - assert(new_device); - - r = device_shallow_clone(old_device, &ret); - if (r < 0) - return r; - - r = device_read_db(ret); - if (r < 0) - return r; - - ret->sealed = true; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(new_device); - assert(syspath); - assert(action); - - r = sd_device_new_from_syspath(&ret, syspath); - if (r < 0) - return r; - - r = device_read_uevent_file(ret); - if (r < 0) - return r; - - r = device_add_property_internal(ret, "ACTION", action); - if (r < 0) - return r; - - *new_device = ret; - ret = NULL; - - return 0; -} - -int device_copy_properties(sd_device *device_dst, sd_device *device_src) { - const char *property, *value; - int r; - - assert(device_dst); - assert(device_src); - - FOREACH_DEVICE_PROPERTY(device_src, property, value) { - r = device_add_property(device_dst, property, value); - if (r < 0) - return r; - } - - return 0; -} - -void device_cleanup_tags(sd_device *device) { - assert(device); - - set_free_free(device->tags); - device->tags = NULL; - device->property_tags_outdated = true; - device->tags_generation++; -} - -void device_cleanup_devlinks(sd_device *device) { - assert(device); - - set_free_free(device->devlinks); - device->devlinks = NULL; - device->property_devlinks_outdated = true; - device->devlinks_generation++; -} - -void device_remove_tag(sd_device *device, const char *tag) { - assert(device); - assert(tag); - - free(set_remove(device->tags, tag)); - device->property_tags_outdated = true; - device->tags_generation++; -} - -static int device_tag(sd_device *device, const char *tag, bool add) { - const char *id; - char *path; - int r; - - assert(device); - assert(tag); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/tags/", tag, "/", id); - - if (add) { - r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); - if (r < 0) - return r; - } else { - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - } - - return 0; -} - -int device_tag_index(sd_device *device, sd_device *device_old, bool add) { - const char *tag; - int r = 0, k; - - if (add && device_old) { - /* delete possible left-over tags */ - FOREACH_DEVICE_TAG(device_old, tag) { - if (!sd_device_has_tag(device, tag)) { - k = device_tag(device_old, tag, false); - if (r >= 0 && k < 0) - r = k; - } - } - } - - FOREACH_DEVICE_TAG(device, tag) { - k = device_tag(device, tag, add); - if (r >= 0 && k < 0) - r = k; - } - - return r; -} - -static bool device_has_info(sd_device *device) { - assert(device); - - if (!set_isempty(device->devlinks)) - return true; - - if (device->devlink_priority != 0) - return true; - - if (!ordered_hashmap_isempty(device->properties_db)) - return true; - - if (!set_isempty(device->tags)) - return true; - - if (device->watch_handle >= 0) - return true; - - return false; -} - -void device_set_db_persist(sd_device *device) { - assert(device); - - device->db_persist = true; -} - -int device_update_db(sd_device *device) { - const char *id; - char *path; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *path_tmp = NULL; - bool has_info; - int r; - - assert(device); - - has_info = device_has_info(device); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - /* do not store anything for otherwise empty devices */ - if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; - } - - /* write a database file */ - r = mkdir_parents(path, 0755); - if (r < 0) - return r; - - r = fopen_temporary(path, &f, &path_tmp); - if (r < 0) - return r; - - /* - * set 'sticky' bit to indicate that we should not clean the - * database when we transition from initramfs to the real root - */ - if (device->db_persist) { - r = fchmod(fileno(f), 01644); - if (r < 0) { - r = -errno; - goto fail; - } - } else { - r = fchmod(fileno(f), 0644); - if (r < 0) { - r = -errno; - goto fail; - } - } - - if (has_info) { - const char *property, *value, *tag; - Iterator i; - - if (major(device->devnum) > 0) { - const char *devlink; - - FOREACH_DEVICE_DEVLINK(device, devlink) - fprintf(f, "S:%s\n", devlink + strlen("/dev/")); - - if (device->devlink_priority != 0) - fprintf(f, "L:%i\n", device->devlink_priority); - - if (device->watch_handle >= 0) - fprintf(f, "W:%i\n", device->watch_handle); - } - - if (device->usec_initialized > 0) - fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); - - ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) - fprintf(f, "E:%s=%s\n", property, value); - - FOREACH_DEVICE_TAG(device, tag) - fprintf(f, "G:%s\n", tag); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - r = rename(path_tmp, path); - if (r < 0) { - r = -errno; - goto fail; - } - - log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", - path, device->devpath); - - return 0; - -fail: - (void) unlink(path); - (void) unlink(path_tmp); - - return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath); -} - -int device_delete_db(sd_device *device) { - const char *id; - char *path; - int r; - - assert(device); - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; -} - -int device_read_db_force(sd_device *device) { - assert(device); - - return device_read_db_aux(device, true); -} diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h deleted file mode 100644 index 29b3e155fb..0000000000 --- a/src/libsystemd/sd-device/device-private.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include -#include -#include - -#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); - -int device_get_id_filename(sd_device *device, const char **ret); - -int device_get_devlink_priority(sd_device *device, int *priority); -int device_get_watch_handle(sd_device *device, int *handle); -int device_get_devnode_mode(sd_device *device, mode_t *mode); -int device_get_devnode_uid(sd_device *device, uid_t *uid); -int device_get_devnode_gid(sd_device *device, gid_t *gid); - -void device_seal(sd_device *device); -void device_set_is_initialized(sd_device *device); -void device_set_watch_handle(sd_device *device, int fd); -void device_set_db_persist(sd_device *device); -void device_set_devlink_priority(sd_device *device, int priority); -int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); -int device_add_devlink(sd_device *device, const char *devlink); -int device_add_property(sd_device *device, const char *property, const char *value); -int device_add_tag(sd_device *device, const char *tag); -void device_remove_tag(sd_device *device, const char *tag); -void device_cleanup_tags(sd_device *device); -void device_cleanup_devlinks(sd_device *device); - -uint64_t device_get_properties_generation(sd_device *device); -uint64_t device_get_tags_generation(sd_device *device); -uint64_t device_get_devlinks_generation(sd_device *device); - -int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len); -int device_get_properties_strv(sd_device *device, char ***strv); - -int device_rename(sd_device *device, const char *name); -int device_shallow_clone(sd_device *old_device, sd_device **new_device); -int device_clone_with_db(sd_device *old_device, sd_device **new_device); -int device_copy_properties(sd_device *device_dst, sd_device *device_src); -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); - -int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); -int device_update_db(sd_device *device); -int device_delete_db(sd_device *device); -int device_read_db_force(sd_device *device); diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h deleted file mode 100644 index 5b42e11de6..0000000000 --- a/src/libsystemd/sd-device/device-util.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014-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 . -***/ - -#include "util.h" - -#define FOREACH_DEVICE_PROPERTY(device, key, value) \ - for (key = sd_device_get_property_first(device, &(value)); \ - key; \ - key = sd_device_get_property_next(device, &(value))) - -#define FOREACH_DEVICE_TAG(device, tag) \ - for (tag = sd_device_get_tag_first(device); \ - tag; \ - tag = sd_device_get_tag_next(device)) - -#define FOREACH_DEVICE_SYSATTR(device, attr) \ - for (attr = sd_device_get_sysattr_first(device); \ - attr; \ - attr = sd_device_get_sysattr_next(device)) - -#define FOREACH_DEVICE_DEVLINK(device, devlink) \ - for (devlink = sd_device_get_devlink_first(device); \ - devlink; \ - devlink = sd_device_get_devlink_next(device)) - -#define FOREACH_DEVICE(enumerator, device) \ - for (device = sd_device_enumerator_get_device_first(enumerator); \ - device; \ - device = sd_device_enumerator_get_device_next(enumerator)) - -#define FOREACH_SUBSYSTEM(enumerator, device) \ - for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ - device; \ - device = sd_device_enumerator_get_subsystem_next(enumerator)) diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c deleted file mode 100644 index 0c4ad966bd..0000000000 --- a/src/libsystemd/sd-device/sd-device.c +++ /dev/null @@ -1,1935 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include -#include -#include - -#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 "strxcpyx.h" -#include "util.h" - -int device_new_aux(sd_device **ret) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - - assert(ret); - - device = new0(sd_device, 1); - if (!device) - return -ENOMEM; - - device->n_ref = 1; - device->watch_handle = -1; - - *ret = device; - device = NULL; - - return 0; -} - -_public_ sd_device *sd_device_ref(sd_device *device) { - if (device) - assert_se(++ device->n_ref >= 2); - - return device; -} - -_public_ sd_device *sd_device_unref(sd_device *device) { - if (device && -- device->n_ref == 0) { - sd_device_unref(device->parent); - free(device->syspath); - free(device->sysname); - free(device->devtype); - free(device->devname); - free(device->subsystem); - free(device->driver_subsystem); - free(device->driver); - free(device->id_filename); - free(device->properties_strv); - free(device->properties_nulstr); - - ordered_hashmap_free_free_free(device->properties); - ordered_hashmap_free_free_free(device->properties_db); - hashmap_free_free_free(device->sysattr_values); - set_free_free(device->sysattrs); - set_free_free(device->tags); - set_free_free(device->devlinks); - - free(device); - } - - return NULL; -} - -int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) { - OrderedHashmap **properties; - - assert(device); - assert(_key); - - if (db) - properties = &device->properties_db; - else - properties = &device->properties; - - if (_value) { - _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL; - int r; - - r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops); - if (r < 0) - return r; - - key = strdup(_key); - if (!key) - return -ENOMEM; - - value = strdup(_value); - if (!value) - return -ENOMEM; - - old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); - - r = ordered_hashmap_replace(*properties, key, value); - if (r < 0) - return r; - - key = NULL; - value = NULL; - } else { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value = NULL; - - value = ordered_hashmap_remove2(*properties, _key, (void**) &key); - } - - if (!db) { - device->properties_generation++; - device->properties_buf_outdated = true; - } - - return 0; -} - -int device_add_property_internal(sd_device *device, const char *key, const char *value) { - return device_add_property_aux(device, key, value, false); -} - -int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { - _cleanup_free_ char *syspath = NULL; - const char *devpath; - int r; - - assert(device); - assert(_syspath); - - /* must be a subdirectory of /sys */ - if (!path_startswith(_syspath, "/sys/")) { - log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath); - return -EINVAL; - } - - if (verify) { - r = readlink_and_canonicalize(_syspath, &syspath); - if (r == -ENOENT) - /* the device does not exist (any more?) */ - return -ENODEV; - else if (r == -EINVAL) { - /* not a symlink */ - syspath = canonicalize_file_name(_syspath); - if (!syspath) { - if (errno == ENOENT) - /* the device does not exist (any more?) */ - return -ENODEV; - - return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath); - } - } else if (r < 0) { - log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath); - return r; - } - - if (path_startswith(syspath, "/sys/devices/")) { - char *path; - - /* all 'devices' require an 'uevent' file */ - path = strjoina(syspath, "/uevent"); - r = access(path, F_OK); - if (r < 0) { - if (errno == ENOENT) - /* this is not a valid device */ - return -ENODEV; - - log_debug("sd-device: %s does not have an uevent file: %m", syspath); - return -errno; - } - } else { - /* everything else just needs to be a directory */ - if (!is_dir(syspath, false)) - return -ENODEV; - } - } else { - syspath = strdup(_syspath); - if (!syspath) - return -ENOMEM; - } - - devpath = syspath + strlen("/sys"); - - r = device_add_property_internal(device, "DEVPATH", devpath); - if (r < 0) - return r; - - free(device->syspath); - device->syspath = syspath; - syspath = NULL; - - device->devpath = devpath; - - return 0; -} - -_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(syspath, -EINVAL); - - r = device_new_aux(&device); - if (r < 0) - return r; - - r = device_set_syspath(device, syspath, true); - if (r < 0) - return r; - - *ret = device; - device = NULL; - - return 0; -} - -_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { - char *syspath; - char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; - - assert_return(ret, -EINVAL); - assert_return(type == 'b' || type == 'c', -EINVAL); - - /* use /sys/dev/{block,char}/: link */ - snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); - - syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); - - return sd_device_new_from_syspath(ret, syspath); -} - -_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { - char *name, *syspath; - size_t len = 0; - - assert_return(ret, -EINVAL); - assert_return(subsystem, -EINVAL); - assert_return(sysname, -EINVAL); - - if (streq(subsystem, "subsystem")) { - syspath = strjoina("/sys/subsystem/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/bus/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/class/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } else if (streq(subsystem, "module")) { - syspath = strjoina("/sys/module/", sysname); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } else if (streq(subsystem, "drivers")) { - char subsys[PATH_MAX]; - char *driver; - - strscpy(subsys, sizeof(subsys), sysname); - driver = strchr(subsys, ':'); - if (driver) { - driver[0] = '\0'; - driver++; - - syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - } - } - - /* 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/", name); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - syspath = strjoina("/sys/class/", subsystem, "/", name); - if (access(syspath, F_OK) >= 0) - return sd_device_new_from_syspath(ret, syspath); - - return -ENODEV; -} - -int device_set_devtype(sd_device *device, const char *_devtype) { - _cleanup_free_ char *devtype = NULL; - int r; - - assert(device); - assert(_devtype); - - devtype = strdup(_devtype); - if (!devtype) - return -ENOMEM; - - r = device_add_property_internal(device, "DEVTYPE", devtype); - if (r < 0) - return r; - - free(device->devtype); - device->devtype = devtype; - devtype = NULL; - - return 0; -} - -int device_set_ifindex(sd_device *device, const char *_ifindex) { - int ifindex, r; - - assert(device); - assert(_ifindex); - - r = parse_ifindex(_ifindex, &ifindex); - if (r < 0) - return r; - - r = device_add_property_internal(device, "IFINDEX", _ifindex); - if (r < 0) - return r; - - device->ifindex = ifindex; - - return 0; -} - -int device_set_devname(sd_device *device, const char *_devname) { - _cleanup_free_ char *devname = NULL; - int r; - - assert(device); - assert(_devname); - - if (_devname[0] != '/') { - r = asprintf(&devname, "/dev/%s", _devname); - if (r < 0) - return -ENOMEM; - } else { - devname = strdup(_devname); - if (!devname) - return -ENOMEM; - } - - r = device_add_property_internal(device, "DEVNAME", devname); - if (r < 0) - return r; - - free(device->devname); - device->devname = devname; - devname = NULL; - - return 0; -} - -int device_set_devmode(sd_device *device, const char *_devmode) { - unsigned devmode; - int r; - - assert(device); - assert(_devmode); - - r = safe_atou(_devmode, &devmode); - if (r < 0) - return r; - - if (devmode > 07777) - return -EINVAL; - - r = device_add_property_internal(device, "DEVMODE", _devmode); - if (r < 0) - return r; - - device->devmode = devmode; - - return 0; -} - -int device_set_devnum(sd_device *device, const char *major, const char *minor) { - unsigned maj = 0, min = 0; - int r; - - assert(device); - assert(major); - - r = safe_atou(major, &maj); - if (r < 0) - return r; - if (!maj) - return 0; - - if (minor) { - r = safe_atou(minor, &min); - if (r < 0) - return r; - } - - r = device_add_property_internal(device, "MAJOR", major); - if (r < 0) - return r; - - if (minor) { - r = device_add_property_internal(device, "MINOR", minor); - if (r < 0) - return r; - } - - device->devnum = makedev(maj, min); - - return 0; -} - -static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) { - int r; - - assert(device); - assert(key); - assert(value); - assert(major); - assert(minor); - - if (streq(key, "DEVTYPE")) { - r = device_set_devtype(device, value); - if (r < 0) - return r; - } else if (streq(key, "IFINDEX")) { - r = device_set_ifindex(device, value); - if (r < 0) - return r; - } else if (streq(key, "DEVNAME")) { - r = device_set_devname(device, value); - if (r < 0) - return r; - } else if (streq(key, "DEVMODE")) { - r = device_set_devmode(device, value); - if (r < 0) - return r; - } else if (streq(key, "MAJOR")) - *major = value; - else if (streq(key, "MINOR")) - *minor = value; - else { - r = device_add_property_internal(device, key, value); - if (r < 0) - return r; - } - - return 0; -} - -int device_read_uevent_file(sd_device *device) { - _cleanup_free_ char *uevent = NULL; - const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; - char *path; - size_t uevent_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - assert(device); - - if (device->uevent_loaded || device->sealed) - return 0; - - device->uevent_loaded = true; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/uevent"); - - r = read_full_file(path, &uevent, &uevent_len); - if (r == -EACCES) - /* empty uevent files may be write-only */ - return 0; - else if (r == -ENOENT) - /* some devices may not have uevent files, see set_syspath() */ - return 0; - else if (r < 0) { - log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path); - return r; - } - - for (i = 0; i < uevent_len; i++) - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, uevent[i])) { - key = &uevent[i]; - - state = KEY; - } - - break; - case KEY: - if (uevent[i] == '=') { - uevent[i] = '\0'; - - state = PRE_VALUE; - } else if (strchr(NEWLINE, uevent[i])) { - uevent[i] = '\0'; - log_debug("sd-device: ignoring invalid uevent line '%s'", key); - - state = PRE_KEY; - } - - break; - case PRE_VALUE: - value = &uevent[i]; - state = VALUE; - - /* fall through to handle empty property */ - case VALUE: - if (strchr(NEWLINE, uevent[i])) { - uevent[i] = '\0'; - - r = handle_uevent_line(device, key, value, &major, &minor); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing uevent file"); - } - - if (major) { - r = device_set_devnum(device, major, minor); - if (r < 0) - log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path); - } - - return 0; -} - -_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { - int r; - - assert_return(device, -EINVAL); - assert_return(ifindex, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *ifindex = device->ifindex; - - return 0; -} - -_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { - int r; - - assert_return(ret, -EINVAL); - assert_return(id, -EINVAL); - - switch (id[0]) { - case 'b': - case 'c': - { - char type; - int maj, min; - - r = sscanf(id, "%c%i:%i", &type, &maj, &min); - if (r != 3) - return -EINVAL; - - return sd_device_new_from_devnum(ret, type, makedev(maj, min)); - } - case 'n': - { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - _cleanup_close_ int sk = -1; - struct ifreq ifr = {}; - int ifindex; - - r = parse_ifindex(&id[1], &ifr.ifr_ifindex); - if (r < 0) - return r; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -errno; - - r = ioctl(sk, SIOCGIFNAME, &ifr); - if (r < 0) - return -errno; - - r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name); - if (r < 0) - return r; - - r = sd_device_get_ifindex(device, &ifindex); - if (r < 0) - return r; - - /* this is racey, so we might end up with the wrong device */ - if (ifr.ifr_ifindex != ifindex) - return -ENODEV; - - *ret = device; - device = NULL; - - return 0; - } - case '+': - { - char subsys[PATH_MAX]; - char *sysname; - - (void)strscpy(subsys, sizeof(subsys), id + 1); - sysname = strchr(subsys, ':'); - if (!sysname) - return -EINVAL; - - sysname[0] = '\0'; - sysname++; - - return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); - } - default: - return -EINVAL; - } -} - -_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - assert(path_startswith(device->syspath, "/sys/")); - - *ret = device->syspath; - - return 0; -} - -static int device_new_from_child(sd_device **ret, sd_device *child) { - _cleanup_free_ char *path = NULL; - const char *subdir, *syspath; - int r; - - assert(ret); - assert(child); - - r = sd_device_get_syspath(child, &syspath); - if (r < 0) - return r; - - path = strdup(syspath); - if (!path) - return -ENOMEM; - subdir = path + strlen("/sys"); - - for (;;) { - char *pos; - - pos = strrchr(subdir, '/'); - if (!pos || pos < subdir + 2) - break; - - *pos = '\0'; - - r = sd_device_new_from_syspath(ret, path); - if (r < 0) - continue; - - return 0; - } - - return -ENODEV; -} - -_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { - - assert_return(ret, -EINVAL); - assert_return(child, -EINVAL); - - if (!child->parent_set) { - child->parent_set = true; - - (void)device_new_from_child(&child->parent, child); - } - - if (!child->parent) - return -ENOENT; - - *ret = child->parent; - - return 0; -} - -int device_set_subsystem(sd_device *device, const char *_subsystem) { - _cleanup_free_ char *subsystem = NULL; - int r; - - assert(device); - assert(_subsystem); - - subsystem = strdup(_subsystem); - if (!subsystem) - return -ENOMEM; - - r = device_add_property_internal(device, "SUBSYSTEM", subsystem); - if (r < 0) - return r; - - free(device->subsystem); - device->subsystem = subsystem; - subsystem = NULL; - - device->subsystem_set = true; - - return 0; -} - -static int device_set_drivers_subsystem(sd_device *device, const char *_subsystem) { - _cleanup_free_ char *subsystem = NULL; - int r; - - assert(device); - assert(_subsystem); - assert(*_subsystem); - - subsystem = strdup(_subsystem); - if (!subsystem) - return -ENOMEM; - - r = device_set_subsystem(device, "drivers"); - if (r < 0) - return r; - - free(device->driver_subsystem); - device->driver_subsystem = subsystem; - subsystem = NULL; - - return 0; -} - -_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { - const char *syspath, *drivers = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(device, -EINVAL); - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - if (!device->subsystem_set) { - _cleanup_free_ char *subsystem = NULL; - char *path; - - /* read 'subsystem' link */ - path = strjoina(syspath, "/subsystem"); - r = readlink_value(path, &subsystem); - if (r >= 0) - r = device_set_subsystem(device, subsystem); - /* use implicit names */ - else if (path_startswith(device->devpath, "/module/")) - r = device_set_subsystem(device, "module"); - else if (!(drivers = strstr(syspath, "/drivers/")) && - (path_startswith(device->devpath, "/subsystem/") || - path_startswith(device->devpath, "/class/") || - path_startswith(device->devpath, "/bus/"))) - r = device_set_subsystem(device, "subsystem"); - if (r < 0 && r != -ENOENT) - return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath); - - device->subsystem_set = true; - } else if (!device->driver_subsystem_set) - drivers = strstr(syspath, "/drivers/"); - - if (!device->driver_subsystem_set) { - if (drivers) { - _cleanup_free_ char *subpath = NULL; - - subpath = strndup(syspath, drivers - syspath); - if (!subpath) - r = -ENOMEM; - else { - const char *subsys; - - subsys = strrchr(subpath, '/'); - if (!subsys) - r = -EINVAL; - else - r = device_set_drivers_subsystem(device, subsys + 1); - } - if (r < 0 && r != -ENOENT) - return log_debug_errno(r, "sd-device: could not set subsystem for driver %s: %m", device->devpath); - } - - device->driver_subsystem_set = true; - } - - if (!device->subsystem) - return -ENOENT; - - *ret = device->subsystem; - - return 0; -} - -_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { - int r; - - assert(devtype); - assert(device); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *devtype = device->devtype; - - return 0; -} - -_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) { - sd_device *parent = NULL; - int r; - - assert_return(child, -EINVAL); - assert_return(subsystem, -EINVAL); - - r = sd_device_get_parent(child, &parent); - while (r >= 0) { - const char *parent_subsystem = NULL; - const char *parent_devtype = NULL; - - (void)sd_device_get_subsystem(parent, &parent_subsystem); - if (streq_ptr(parent_subsystem, subsystem)) { - if (!devtype) - break; - - (void)sd_device_get_devtype(parent, &parent_devtype); - if (streq_ptr(parent_devtype, devtype)) - break; - } - r = sd_device_get_parent(parent, &parent); - } - - if (r < 0) - return r; - - *ret = parent; - - return 0; -} - -_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { - int r; - - assert_return(device, -EINVAL); - assert_return(devnum, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - *devnum = device->devnum; - - return 0; -} - -int device_set_driver(sd_device *device, const char *_driver) { - _cleanup_free_ char *driver = NULL; - int r; - - assert(device); - assert(_driver); - - driver = strdup(_driver); - if (!driver) - return -ENOMEM; - - r = device_add_property_internal(device, "DRIVER", driver); - if (r < 0) - return r; - - free(device->driver); - device->driver = driver; - driver = NULL; - - device->driver_set = true; - - return 0; -} - -_public_ int sd_device_get_driver(sd_device *device, const char **ret) { - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->driver_set) { - _cleanup_free_ char *driver = NULL; - const char *syspath; - char *path; - int r; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/driver"); - r = readlink_value(path, &driver); - if (r >= 0) { - r = device_set_driver(device, driver); - if (r < 0) - return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); - } else if (r == -ENOENT) - device->driver_set = true; - else - return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); - } - - if (!device->driver) - return -ENOENT; - - *ret = device->driver; - - return 0; -} - -_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { - assert_return(device, -EINVAL); - assert_return(devpath, -EINVAL); - - assert(device->devpath); - assert(device->devpath[0] == '/'); - - *devpath = device->devpath; - - return 0; -} - -_public_ int sd_device_get_devname(sd_device *device, const char **devname) { - int r; - - assert_return(device, -EINVAL); - assert_return(devname, -EINVAL); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - if (!device->devname) - return -ENOENT; - - assert(path_startswith(device->devname, "/dev/")); - - *devname = device->devname; - - return 0; -} - -static int device_set_sysname(sd_device *device) { - _cleanup_free_ char *sysname = NULL; - const char *sysnum = NULL; - const char *pos; - size_t len = 0; - - pos = strrchr(device->devpath, '/'); - if (!pos) - return -EINVAL; - pos++; - - /* devpath is not a root directory */ - if (*pos == '\0' || pos <= device->devpath) - return -EINVAL; - - sysname = strdup(pos); - if (!sysname) - return -ENOMEM; - - /* some devices have '!' in their name, change that to '/' */ - while (sysname[len] != '\0') { - if (sysname[len] == '!') - sysname[len] = '/'; - - len++; - } - - /* trailing number */ - while (len > 0 && isdigit(sysname[--len])) - sysnum = &sysname[len]; - - if (len == 0) - sysnum = NULL; - - free(device->sysname); - device->sysname = sysname; - sysname = NULL; - - device->sysnum = sysnum; - - device->sysname_set = true; - - return 0; -} - -_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { - int r; - - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->sysname_set) { - r = device_set_sysname(device); - if (r < 0) - return r; - } - - assert_return(device->sysname, -ENOENT); - - *ret = device->sysname; - - return 0; -} - -_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { - int r; - - assert_return(device, -EINVAL); - assert_return(ret, -EINVAL); - - if (!device->sysname_set) { - r = device_set_sysname(device); - if (r < 0) - return r; - } - - *ret = device->sysnum; - - return 0; -} - -static bool is_valid_tag(const char *tag) { - assert(tag); - - return !strchr(tag, ':') && !strchr(tag, ' '); -} - -int device_add_tag(sd_device *device, const char *tag) { - int r; - - assert(device); - assert(tag); - - if (!is_valid_tag(tag)) - return -EINVAL; - - r = set_ensure_allocated(&device->tags, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->tags, tag); - if (r < 0) - return r; - - device->tags_generation++; - device->property_tags_outdated = true; - - return 0; -} - -int device_add_devlink(sd_device *device, const char *devlink) { - int r; - - assert(device); - assert(devlink); - - r = set_ensure_allocated(&device->devlinks, &string_hash_ops); - if (r < 0) - return r; - - r = set_put_strdup(device->devlinks, devlink); - if (r < 0) - return r; - - device->devlinks_generation++; - device->property_devlinks_outdated = true; - - return 0; -} - -static int device_add_property_internal_from_string(sd_device *device, const char *str) { - _cleanup_free_ char *key = NULL; - char *value; - - assert(device); - assert(str); - - key = strdup(str); - if (!key) - return -ENOMEM; - - value = strchr(key, '='); - if (!value) - return -EINVAL; - - *value = '\0'; - - if (isempty(++value)) - value = NULL; - - return device_add_property_internal(device, key, value); -} - -int device_set_usec_initialized(sd_device *device, const char *initialized) { - uint64_t usec_initialized; - int r; - - assert(device); - assert(initialized); - - r = safe_atou64(initialized, &usec_initialized); - if (r < 0) - return r; - - r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); - if (r < 0) - return r; - - device->usec_initialized = usec_initialized; - - return 0; -} - -static int handle_db_line(sd_device *device, char key, const char *value) { - char *path; - int r; - - assert(device); - assert(value); - - switch (key) { - case 'G': - r = device_add_tag(device, value); - if (r < 0) - return r; - - break; - case 'S': - path = strjoina("/dev/", value); - r = device_add_devlink(device, path); - if (r < 0) - return r; - - break; - case 'E': - r = device_add_property_internal_from_string(device, value); - if (r < 0) - return r; - - break; - case 'I': - r = device_set_usec_initialized(device, value); - if (r < 0) - return r; - - break; - case 'L': - r = safe_atoi(value, &device->devlink_priority); - if (r < 0) - return r; - - break; - case 'W': - r = safe_atoi(value, &device->watch_handle); - if (r < 0) - return r; - - break; - default: - log_debug("device db: unknown key '%c'", key); - } - - return 0; -} - -int device_get_id_filename(sd_device *device, const char **ret) { - assert(device); - assert(ret); - - if (!device->id_filename) { - _cleanup_free_ char *id = NULL; - const char *subsystem; - dev_t devnum; - int ifindex, r; - - r = sd_device_get_subsystem(device, &subsystem); - if (r < 0) - return r; - - r = sd_device_get_devnum(device, &devnum); - if (r < 0) - return r; - - r = sd_device_get_ifindex(device, &ifindex); - if (r < 0) - return r; - - if (major(devnum) > 0) { - assert(subsystem); - - /* use dev_t — b259:131072, c254:0 */ - r = asprintf(&id, "%c%u:%u", - streq(subsystem, "block") ? 'b' : 'c', - major(devnum), minor(devnum)); - if (r < 0) - return -ENOMEM; - } else if (ifindex > 0) { - /* use netdev ifindex — n3 */ - r = asprintf(&id, "n%u", ifindex); - if (r < 0) - return -ENOMEM; - } else { - /* use $subsys:$sysname — pci:0000:00:1f.2 - * sysname() has '!' translated, get it from devpath - */ - const char *sysname; - - sysname = basename(device->devpath); - if (!sysname) - return -EINVAL; - - if (!subsystem) - return -EINVAL; - - if (streq(subsystem, "drivers")) { - /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem - * encoded as well */ - r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname); - if (r < 0) - return -ENOMEM; - } else { - r = asprintf(&id, "+%s:%s", subsystem, sysname); - if (r < 0) - return -ENOMEM; - } - } - - device->id_filename = id; - id = NULL; - } - - *ret = device->id_filename; - - return 0; -} - -int device_read_db_aux(sd_device *device, bool force) { - _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; - char key; - size_t db_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - if (device->db_loaded || (!force && device->sealed)) - return 0; - - device->db_loaded = true; - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = read_full_file(path, &db, &db_len); - if (r < 0) { - if (r == -ENOENT) - return 0; - else - return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); - } - - /* devices with a database entry are initialized */ - device->is_initialized = true; - - for (i = 0; i < db_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, db[i])) { - key = db[i]; - - state = KEY; - } - - break; - case KEY: - if (db[i] != ':') { - log_debug("sd-device: ignoring invalid db entry with key '%c'", key); - - state = INVALID_LINE; - } else { - db[i] = '\0'; - - state = PRE_VALUE; - } - - break; - case PRE_VALUE: - value = &db[i]; - - state = VALUE; - - break; - case INVALID_LINE: - if (strchr(NEWLINE, db[i])) - state = PRE_KEY; - - break; - case VALUE: - if (strchr(NEWLINE, db[i])) { - db[i] = '\0'; - r = handle_db_line(device, key, value); - if (r < 0) - log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("invalid state when parsing db"); - } - } - - return 0; -} - -static int device_read_db(sd_device *device) { - return device_read_db_aux(device, false); -} - -_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) { - int r; - - assert_return(device, -EINVAL); - assert_return(initialized, -EINVAL); - - r = device_read_db(device); - if (r < 0) - return r; - - *initialized = device->is_initialized; - - return 0; -} - -_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) { - usec_t now_ts; - int r; - - assert_return(device, -EINVAL); - assert_return(usec, -EINVAL); - - r = device_read_db(device); - if (r < 0) - return r; - - if (!device->is_initialized) - return -EBUSY; - - if (!device->usec_initialized) - return -ENODATA; - - now_ts = now(clock_boottime_or_monotonic()); - - if (now_ts < device->usec_initialized) - return -EIO; - - *usec = now_ts - device->usec_initialized; - - return 0; -} - -_public_ const char *sd_device_get_tag_first(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - device->tags_iterator_generation = device->tags_generation; - device->tags_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->tags, &device->tags_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_tag_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - if (device->tags_iterator_generation != device->tags_generation) - return NULL; - - (void) set_iterate(device->tags, &device->tags_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_devlink_first(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - device->devlinks_iterator_generation = device->devlinks_generation; - device->devlinks_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_devlink_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - (void) device_read_db(device); - - if (device->devlinks_iterator_generation != device->devlinks_generation) - return NULL; - - (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); - return v; -} - -static int device_properties_prepare(sd_device *device) { - int r; - - assert(device); - - r = device_read_uevent_file(device); - if (r < 0) - return r; - - r = device_read_db(device); - if (r < 0) - return r; - - if (device->property_devlinks_outdated) { - _cleanup_free_ char *devlinks = NULL; - size_t devlinks_allocated = 0, devlinks_len = 0; - const char *devlink; - - for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) { - char *e; - - if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2)) - return -ENOMEM; - if (devlinks_len > 0) - stpcpy(devlinks + devlinks_len++, " "); - e = stpcpy(devlinks + devlinks_len, devlink); - devlinks_len = e - devlinks; - } - - r = device_add_property_internal(device, "DEVLINKS", devlinks); - if (r < 0) - return r; - - device->property_devlinks_outdated = false; - } - - if (device->property_tags_outdated) { - _cleanup_free_ char *tags = NULL; - size_t tags_allocated = 0, tags_len = 0; - const char *tag; - - if (!GREEDY_REALLOC(tags, tags_allocated, 2)) - return -ENOMEM; - stpcpy(tags, ":"); - tags_len++; - - for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { - char *e; - - if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) - return -ENOMEM; - e = stpcpy(stpcpy(tags + tags_len, tag), ":"); - tags_len = e - tags; - } - - r = device_add_property_internal(device, "TAGS", tags); - if (r < 0) - return r; - - device->property_tags_outdated = false; - } - - return 0; -} - -_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { - const char *key; - const char *value; - int r; - - assert_return(device, NULL); - - r = device_properties_prepare(device); - if (r < 0) - return NULL; - - device->properties_iterator_generation = device->properties_generation; - device->properties_iterator = ITERATOR_FIRST; - - ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); - - if (_value) - *_value = value; - - return key; -} - -_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { - const char *key; - const char *value; - int r; - - assert_return(device, NULL); - - r = device_properties_prepare(device); - if (r < 0) - return NULL; - - if (device->properties_iterator_generation != device->properties_generation) - return NULL; - - ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); - - if (_value) - *_value = value; - - return key; -} - -static int device_sysattrs_read_all(sd_device *device) { - _cleanup_closedir_ DIR *dir = NULL; - const char *syspath; - struct dirent *dent; - int r; - - assert(device); - - if (device->sysattrs_read) - return 0; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - dir = opendir(syspath); - if (!dir) - return -errno; - - r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); - if (r < 0) - return r; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *path; - struct stat statbuf; - - /* only handle symlinks and regular files */ - if (dent->d_type != DT_LNK && dent->d_type != DT_REG) - continue; - - path = strjoina(syspath, "/", dent->d_name); - - if (lstat(path, &statbuf) != 0) - continue; - - if (!(statbuf.st_mode & S_IRUSR)) - continue; - - r = set_put_strdup(device->sysattrs, dent->d_name); - if (r < 0) - return r; - } - - device->sysattrs_read = true; - - return 0; -} - -_public_ const char *sd_device_get_sysattr_first(sd_device *device) { - void *v; - int r; - - assert_return(device, NULL); - - if (!device->sysattrs_read) { - r = device_sysattrs_read_all(device); - if (r < 0) { - errno = -r; - return NULL; - } - } - - device->sysattrs_iterator = ITERATOR_FIRST; - - (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); - return v; -} - -_public_ const char *sd_device_get_sysattr_next(sd_device *device) { - void *v; - - assert_return(device, NULL); - - if (!device->sysattrs_read) - return NULL; - - (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); - return v; -} - -_public_ int sd_device_has_tag(sd_device *device, const char *tag) { - assert_return(device, -EINVAL); - assert_return(tag, -EINVAL); - - (void) device_read_db(device); - - return !!set_contains(device->tags, tag); -} - -_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { - char *value; - int r; - - assert_return(device, -EINVAL); - assert_return(key, -EINVAL); - assert_return(_value, -EINVAL); - - r = device_properties_prepare(device); - if (r < 0) - return r; - - value = ordered_hashmap_get(device->properties, key); - if (!value) - return -ENOENT; - - *_value = value; - - return 0; -} - -/* replaces the value if it already exists */ -static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value_old = NULL; - int r; - - assert(device); - assert(_key); - - r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops); - if (r < 0) - return r; - - value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key); - if (!key) { - key = strdup(_key); - if (!key) - return -ENOMEM; - } - - r = hashmap_put(device->sysattr_values, key, value); - if (r < 0) - return r; - - key = NULL; - - return 0; -} - -static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) { - const char *key = NULL, *value; - - assert(device); - assert(_key); - - value = hashmap_get2(device->sysattr_values, _key, (void **) &key); - if (!key) - return -ENOENT; - - if (_value) - *_value = value; - - return 0; -} - -/* We cache all sysattr lookups. If an attribute does not exist, it is stored - * with a NULL value in the cache, otherwise the returned string is stored */ -_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) { - _cleanup_free_ char *value = NULL; - const char *syspath, *cached_value = NULL; - char *path; - struct stat statbuf; - int r; - - assert_return(device, -EINVAL); - assert_return(sysattr, -EINVAL); - - /* look for possibly already cached result */ - r = device_get_sysattr_value(device, sysattr, &cached_value); - if (r != -ENOENT) { - if (r < 0) - return r; - - if (!cached_value) - /* we looked up the sysattr before and it did not exist */ - return -ENOENT; - - if (_value) - *_value = cached_value; - - return 0; - } - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/", sysattr); - r = lstat(path, &statbuf); - if (r < 0) { - /* remember that we could not access the sysattr */ - r = device_add_sysattr_value(device, sysattr, NULL); - if (r < 0) - return r; - - return -ENOENT; - } else if (S_ISLNK(statbuf.st_mode)) { - /* Some core links return only the last element of the target path, - * these are just values, the paths should not be exposed. */ - if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { - r = readlink_value(path, &value); - if (r < 0) - return r; - } else - return -EINVAL; - } else if (S_ISDIR(statbuf.st_mode)) { - /* skip directories */ - return -EINVAL; - } else if (!(statbuf.st_mode & S_IRUSR)) { - /* skip non-readable files */ - return -EPERM; - } else { - size_t size; - - /* read attribute value */ - r = read_full_file(path, &value, &size); - if (r < 0) - return r; - - /* drop trailing newlines */ - while (size > 0 && value[--size] == '\n') - value[size] = '\0'; - } - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - *_value = value; - value = NULL; - - return 0; -} - -static void device_remove_sysattr_value(sd_device *device, const char *_key) { - _cleanup_free_ char *key = NULL; - _cleanup_free_ char *value = NULL; - - assert(device); - assert(_key); - - value = hashmap_remove2(device->sysattr_values, _key, (void **) &key); - - return; -} - -/* set the attribute and save it in the cache. If a NULL value is passed the - * attribute is cleared from the cache */ -_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *value = NULL; - const char *syspath; - char *path; - struct stat statbuf; - size_t value_len = 0; - ssize_t size; - int r; - - assert_return(device, -EINVAL); - assert_return(sysattr, -EINVAL); - - if (!_value) { - device_remove_sysattr_value(device, sysattr); - - return 0; - } - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) - return r; - - path = strjoina(syspath, "/", sysattr); - r = lstat(path, &statbuf); - if (r < 0) { - value = strdup(""); - if (!value) - return -ENOMEM; - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - return -ENXIO; - } - - if (S_ISLNK(statbuf.st_mode)) - return -EINVAL; - - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - return -EISDIR; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - return -EACCES; - - value_len = strlen(_value); - - /* drop trailing newlines */ - while (value_len > 0 && _value[value_len - 1] == '\n') - _value[--value_len] = '\0'; - - /* value length is limited to 4k */ - if (value_len > 4096) - return -EINVAL; - - fd = open(path, O_WRONLY | O_CLOEXEC); - if (fd < 0) - return -errno; - - value = strdup(_value); - if (!value) - return -ENOMEM; - - size = write(fd, value, value_len); - if (size < 0) - return -errno; - - if ((size_t)size != value_len) - return -EIO; - - r = device_add_sysattr_value(device, sysattr, value); - if (r < 0) - return r; - - value = NULL; - - return 0; -} diff --git a/src/libsystemd/sd-event.xml b/src/libsystemd/sd-event.xml new file mode 100644 index 0000000000..fc615f0906 --- /dev/null +++ b/src/libsystemd/sd-event.xml @@ -0,0 +1,187 @@ + + + + + + + + + sd-event + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-event + 3 + + + + sd-event + A generic event loop implementation + + + + + #include <systemd/sd-event.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-event.h provides a generic event + loop implementation, based on Linux epoll7. + + + See + sd_event_new3, + sd_event_run3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_source_unref3, + sd_event_source_set_priority3, + sd_event_source_set_enabled3, + sd_event_source_set_userdata3, + sd_event_source_get_event3, + sd_event_source_get_pending3, + sd_event_source_set_description3, + sd_event_source_set_prepare3, + sd_event_wait3, + sd_event_get_fd3, + sd_event_set_watchdog3, + sd_event_exit3, + sd_event_now3 + for more information about the functions available. + + The event loop design is targeted on running a separate + instance of the event loop in each thread; it has no concept of + distributing events from a single event loop instance onto + multiple worker threads. Dispatching events is strictly ordered + and subject to configurable priorities. In each event loop + iteration a single event source is dispatched. Each time an event + source is dispatched the kernel is polled for new events, before + the next event source is dispatched. The event loop is designed to + honour priorities and provide fairness within each priority. It is + not designed to provide optimal throughput, as this contradicts + these goals due the limitations of the underlying epoll7 + primitives. + + The event loop implementation provides the following features: + + + I/O event sources, based on epoll7's + file descriptor watching, including edge triggered events (EPOLLET). See sd_event_add_io3. + + Timer event sources, based on timerfd_create2, + supporting the CLOCK_MONOTONIC, + CLOCK_REALTIME, + CLOCK_BOOTIME clocks, as well as the + CLOCK_REALTIME_ALARM and + CLOCK_BOOTTIME_ALARM clocks that can resume + the system from suspend. When creating timer events a required + accuracy parameter may be specified which allows coalescing of + timer events to minimize power consumption. See sd_event_add_time3. + + UNIX process signal events, based on + signalfd2, + including full support for real-time signals, and queued parameters. See sd_event_add_signal3. + + Child process state change events, based on + waitid2. See sd_event_add_child3. + + Static event sources, of three types: defer, + post and exit, for invoking calls in each event loop, after + other event sources or at event loop termination. See + sd_event_add_defer3. + + Event sources may be assigned a 64bit priority + value, that controls the order in which event sources are + dispatched if multiple are pending simultaneously. See + sd_event_source_set_priority3. + + The event loop may automatically send watchdog + notification messages to the service manager. See + sd_event_set_watchdog3. + + The event loop may be integrated into foreign + event loops, such as the GLib one. See + sd_event_get_fd3 + for an example. + + + + + + + + See Also + + systemd1, + sd_event_new3, + sd_event_run3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_source_unref3, + sd_event_source_set_priority3, + sd_event_source_set_enabled3, + sd_event_source_set_userdata3, + sd_event_source_get_event3, + sd_event_source_get_pending3, + sd_event_source_set_description3, + sd_event_source_set_prepare3, + sd_event_wait3, + sd_event_get_fd3, + sd_event_set_watchdog3, + sd_event_exit3, + sd_event_now3, + epoll7, + timerfd_create2, + signalfd2, + waitid2 + + + + diff --git a/src/libsystemd/sd-event/Makefile b/src/libsystemd/sd-event/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-event/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c deleted file mode 100644 index 9857f8b1fc..0000000000 --- a/src/libsystemd/sd-event/sd-event.c +++ /dev/null @@ -1,2884 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-daemon.h" -#include "sd-event.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "list.h" -#include "macro.h" -#include "missing.h" -#include "prioq.h" -#include "process-util.h" -#include "set.h" -#include "signal-util.h" -#include "string-table.h" -#include "string-util.h" -#include "time-util.h" -#include "util.h" - -#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) - -typedef enum EventSourceType { - SOURCE_IO, - SOURCE_TIME_REALTIME, - SOURCE_TIME_BOOTTIME, - SOURCE_TIME_MONOTONIC, - SOURCE_TIME_REALTIME_ALARM, - SOURCE_TIME_BOOTTIME_ALARM, - SOURCE_SIGNAL, - SOURCE_CHILD, - SOURCE_DEFER, - SOURCE_POST, - SOURCE_EXIT, - SOURCE_WATCHDOG, - _SOURCE_EVENT_SOURCE_TYPE_MAX, - _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 { - WAKEUP_NONE, - WAKEUP_EVENT_SOURCE, - WAKEUP_CLOCK_DATA, - WAKEUP_SIGNAL_DATA, - _WAKEUP_TYPE_MAX, - _WAKEUP_TYPE_INVALID = -1, -} WakeupType; - -#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) - -struct sd_event_source { - WakeupType wakeup; - - unsigned n_ref; - - sd_event *event; - void *userdata; - sd_event_handler_t prepare; - - char *description; - - EventSourceType type:5; - int enabled:3; - bool pending:1; - bool dispatching:1; - bool floating:1; - - int64_t priority; - unsigned pending_index; - unsigned prepare_index; - uint64_t pending_iteration; - uint64_t prepare_iteration; - - LIST_FIELDS(sd_event_source, sources); - - union { - struct { - sd_event_io_handler_t callback; - int fd; - uint32_t events; - uint32_t revents; - bool registered:1; - } io; - struct { - sd_event_time_handler_t callback; - usec_t next, accuracy; - unsigned earliest_index; - unsigned latest_index; - } time; - struct { - sd_event_signal_handler_t callback; - struct signalfd_siginfo siginfo; - int sig; - } signal; - struct { - sd_event_child_handler_t callback; - siginfo_t siginfo; - pid_t pid; - int options; - } child; - struct { - sd_event_handler_t callback; - } defer; - struct { - sd_event_handler_t callback; - } post; - struct { - sd_event_handler_t callback; - unsigned prioq_index; - } exit; - }; -}; - -struct clock_data { - WakeupType wakeup; - int fd; - - /* For all clocks we maintain two priority queues each, one - * ordered for the earliest times the events may be - * dispatched, and one ordered by the latest times they must - * have been dispatched. The range between the top entries in - * the two prioqs is the time window we can freely schedule - * wakeups in */ - - Prioq *earliest; - Prioq *latest; - usec_t next; - - bool needs_rearm:1; -}; - -struct signal_data { - WakeupType wakeup; - - /* For each priority we maintain one signal fd, so that we - * only have to dequeue a single event per priority at a - * time. */ - - int fd; - int64_t priority; - sigset_t sigset; - sd_event_source *current; -}; - -struct sd_event { - unsigned n_ref; - - int epoll_fd; - int watchdog_fd; - - Prioq *pending; - Prioq *prepare; - - /* timerfd_create() only supports these five clocks so far. We - * can add support for more clocks when the kernel learns to - * deal with them, too. */ - struct clock_data realtime; - struct clock_data boottime; - struct clock_data monotonic; - struct clock_data realtime_alarm; - struct clock_data boottime_alarm; - - usec_t perturb; - - sd_event_source **signal_sources; /* indexed by signal number */ - Hashmap *signal_data; /* indexed by priority */ - - Hashmap *child_sources; - unsigned n_enabled_child_sources; - - Set *post_sources; - - Prioq *exit; - - pid_t original_pid; - - uint64_t iteration; - triple_timestamp timestamp; - int state; - - bool exit_requested:1; - bool need_process_child:1; - bool watchdog:1; - bool profile_delays:1; - - int exit_code; - - pid_t tid; - sd_event **default_event_ptr; - - usec_t watchdog_last, watchdog_period; - - 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); - -static int pending_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(x->pending); - assert(y->pending); - - /* Enabled ones first */ - if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) - return -1; - if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) - return 1; - - /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - /* Older entries first */ - if (x->pending_iteration < y->pending_iteration) - return -1; - if (x->pending_iteration > y->pending_iteration) - return 1; - - return 0; -} - -static int prepare_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(x->prepare); - assert(y->prepare); - - /* Enabled ones first */ - if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) - return -1; - if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) - return 1; - - /* Move most recently prepared ones last, so that we can stop - * preparing as soon as we hit one that has already been - * prepared in the current iteration */ - if (x->prepare_iteration < y->prepare_iteration) - return -1; - if (x->prepare_iteration > y->prepare_iteration) - return 1; - - /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; -} - -static int earliest_time_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(EVENT_SOURCE_IS_TIME(x->type)); - assert(x->type == y->type); - - /* Enabled ones first */ - if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) - return -1; - if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) - return 1; - - /* Move the pending ones to the end */ - if (!x->pending && y->pending) - return -1; - if (x->pending && !y->pending) - return 1; - - /* Order by time */ - if (x->time.next < y->time.next) - return -1; - if (x->time.next > y->time.next) - return 1; - - return 0; -} - -static usec_t time_event_source_latest(const sd_event_source *s) { - return usec_add(s->time.next, s->time.accuracy); -} - -static int latest_time_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(EVENT_SOURCE_IS_TIME(x->type)); - assert(x->type == y->type); - - /* Enabled ones first */ - if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) - return -1; - if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) - return 1; - - /* Move the pending ones to the end */ - if (!x->pending && y->pending) - return -1; - if (x->pending && !y->pending) - return 1; - - /* Order by time */ - if (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(y)) - return 1; - - return 0; -} - -static int exit_prioq_compare(const void *a, const void *b) { - const sd_event_source *x = a, *y = b; - - assert(x->type == SOURCE_EXIT); - assert(y->type == SOURCE_EXIT); - - /* Enabled ones first */ - if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) - return -1; - if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) - return 1; - - /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; -} - -static void free_clock_data(struct clock_data *d) { - assert(d); - assert(d->wakeup == WAKEUP_CLOCK_DATA); - - safe_close(d->fd); - prioq_free(d->earliest); - prioq_free(d->latest); -} - -static void event_free(sd_event *e) { - sd_event_source *s; - - assert(e); - - while ((s = e->sources)) { - assert(s->floating); - source_disconnect(s); - sd_event_source_unref(s); - } - - assert(e->n_sources == 0); - - if (e->default_event_ptr) - *(e->default_event_ptr) = NULL; - - safe_close(e->epoll_fd); - safe_close(e->watchdog_fd); - - free_clock_data(&e->realtime); - free_clock_data(&e->boottime); - free_clock_data(&e->monotonic); - free_clock_data(&e->realtime_alarm); - free_clock_data(&e->boottime_alarm); - - prioq_free(e->pending); - prioq_free(e->prepare); - prioq_free(e->exit); - - free(e->signal_sources); - hashmap_free(e->signal_data); - - hashmap_free(e->child_sources); - set_free(e->post_sources); - free(e); -} - -_public_ int sd_event_new(sd_event** ret) { - sd_event *e; - int r; - - assert_return(ret, -EINVAL); - - e = new0(sd_event, 1); - if (!e) - return -ENOMEM; - - e->n_ref = 1; - e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; - e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; - e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; - e->original_pid = getpid(); - e->perturb = USEC_INFINITY; - - 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) { - r = -errno; - 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; - -fail: - event_free(e); - return r; -} - -_public_ sd_event* sd_event_ref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref++; - - return e; -} - -_public_ sd_event* sd_event_unref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref--; - - if (e->n_ref <= 0) - event_free(e); - - return NULL; -} - -static bool event_pid_changed(sd_event *e) { - assert(e); - - /* We don't support people creating an event loop and keeping - * it around over a fork(). Let's complain. */ - - return e->original_pid != getpid(); -} - -static void source_io_unregister(sd_event_source *s) { - int r; - - assert(s); - assert(s->type == SOURCE_IO); - - if (event_pid_changed(s->event)) - return; - - if (!s->io.registered) - return; - - 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 (type %s) from epoll: %m", - strna(s->description), event_source_type_to_string(s->type)); - - s->io.registered = false; -} - -static int source_io_register( - sd_event_source *s, - int enabled, - uint32_t events) { - - struct epoll_event ev = {}; - int r; - - assert(s); - assert(s->type == SOURCE_IO); - assert(enabled != SD_EVENT_OFF); - - ev.events = events; - ev.data.ptr = s; - - if (enabled == SD_EVENT_ONESHOT) - ev.events |= EPOLLONESHOT; - - if (s->io.registered) - r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev); - else - r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev); - if (r < 0) - return -errno; - - s->io.registered = true; - - return 0; -} - -static clockid_t event_source_type_to_clock(EventSourceType t) { - - switch (t) { - - case SOURCE_TIME_REALTIME: - return CLOCK_REALTIME; - - case SOURCE_TIME_BOOTTIME: - return CLOCK_BOOTTIME; - - case SOURCE_TIME_MONOTONIC: - return CLOCK_MONOTONIC; - - case SOURCE_TIME_REALTIME_ALARM: - return CLOCK_REALTIME_ALARM; - - case SOURCE_TIME_BOOTTIME_ALARM: - return CLOCK_BOOTTIME_ALARM; - - default: - return (clockid_t) -1; - } -} - -static EventSourceType clock_to_event_source_type(clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - return SOURCE_TIME_REALTIME; - - case CLOCK_BOOTTIME: - return SOURCE_TIME_BOOTTIME; - - case CLOCK_MONOTONIC: - return SOURCE_TIME_MONOTONIC; - - case CLOCK_REALTIME_ALARM: - return SOURCE_TIME_REALTIME_ALARM; - - case CLOCK_BOOTTIME_ALARM: - return SOURCE_TIME_BOOTTIME_ALARM; - - default: - return _SOURCE_EVENT_SOURCE_TYPE_INVALID; - } -} - -static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { - assert(e); - - switch (t) { - - case SOURCE_TIME_REALTIME: - return &e->realtime; - - case SOURCE_TIME_BOOTTIME: - return &e->boottime; - - case SOURCE_TIME_MONOTONIC: - return &e->monotonic; - - case SOURCE_TIME_REALTIME_ALARM: - return &e->realtime_alarm; - - case SOURCE_TIME_BOOTTIME_ALARM: - return &e->boottime_alarm; - - default: - return NULL; - } -} - -static int event_make_signal_data( - sd_event *e, - int sig, - struct signal_data **ret) { - - struct epoll_event ev = {}; - struct signal_data *d; - bool added = false; - sigset_t ss_copy; - int64_t priority; - int r; - - assert(e); - - if (event_pid_changed(e)) - return -ECHILD; - - if (e->signal_sources && e->signal_sources[sig]) - priority = e->signal_sources[sig]->priority; - else - priority = 0; - - d = hashmap_get(e->signal_data, &priority); - if (d) { - if (sigismember(&d->sigset, sig) > 0) { - if (ret) - *ret = d; - return 0; - } - } else { - r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); - if (r < 0) - return r; - - d = new0(struct signal_data, 1); - if (!d) - return -ENOMEM; - - d->wakeup = WAKEUP_SIGNAL_DATA; - d->fd = -1; - d->priority = priority; - - r = hashmap_put(e->signal_data, &d->priority, d); - if (r < 0) { - free(d); - return r; - } - - added = true; - } - - ss_copy = d->sigset; - assert_se(sigaddset(&ss_copy, sig) >= 0); - - r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); - if (r < 0) { - r = -errno; - goto fail; - } - - d->sigset = ss_copy; - - if (d->fd >= 0) { - if (ret) - *ret = d; - return 0; - } - - d->fd = r; - - ev.events = EPOLLIN; - ev.data.ptr = d; - - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); - if (r < 0) { - r = -errno; - goto fail; - } - - if (ret) - *ret = d; - - return 0; - -fail: - if (added) { - d->fd = safe_close(d->fd); - hashmap_remove(e->signal_data, &d->priority); - free(d); - } - - return r; -} - -static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) { - assert(e); - assert(d); - - /* Turns off the specified signal in the signal data - * object. If the signal mask of the object becomes empty that - * way removes it. */ - - if (sigismember(&d->sigset, sig) == 0) - return; - - assert_se(sigdelset(&d->sigset, sig) >= 0); - - if (sigisemptyset(&d->sigset)) { - - /* If all the mask is all-zero we can get rid of the structure */ - hashmap_remove(e->signal_data, &d->priority); - assert(!d->current); - safe_close(d->fd); - free(d); - return; - } - - assert(d->fd >= 0); - - if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0) - log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m"); -} - -static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) { - struct signal_data *d; - static const int64_t zero_priority = 0; - - assert(e); - - /* Rechecks if the specified signal is still something we are - * interested in. If not, we'll unmask it, and possibly drop - * the signalfd for it. */ - - if (sig == SIGCHLD && - e->n_enabled_child_sources > 0) - return; - - if (e->signal_sources && - e->signal_sources[sig] && - e->signal_sources[sig]->enabled != SD_EVENT_OFF) - return; - - /* - * The specified signal might be enabled in three different queues: - * - * 1) the one that belongs to the priority passed (if it is non-NULL) - * 2) the one that belongs to the priority of the event source of the signal (if there is one) - * 3) the 0 priority (to cover the SIGCHLD case) - * - * Hence, let's remove it from all three here. - */ - - if (priority) { - d = hashmap_get(e->signal_data, priority); - if (d) - event_unmask_signal_data(e, d, sig); - } - - if (e->signal_sources && e->signal_sources[sig]) { - d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority); - if (d) - event_unmask_signal_data(e, d, sig); - } - - d = hashmap_get(e->signal_data, &zero_priority); - if (d) - event_unmask_signal_data(e, d, sig); -} - -static void source_disconnect(sd_event_source *s) { - sd_event *event; - - assert(s); - - if (!s->event) - return; - - assert(s->event->n_sources > 0); - - switch (s->type) { - - case SOURCE_IO: - if (s->io.fd >= 0) - source_io_unregister(s); - - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_remove(d->earliest, s, &s->time.earliest_index); - prioq_remove(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - if (s->signal.sig > 0) { - - if (s->event->signal_sources) - s->event->signal_sources[s->signal.sig] = NULL; - - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - } - - break; - - case SOURCE_CHILD: - if (s->child.pid > 0) { - if (s->enabled != SD_EVENT_OFF) { - assert(s->event->n_enabled_child_sources > 0); - s->event->n_enabled_child_sources--; - } - - (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - } - - break; - - case SOURCE_DEFER: - /* nothing */ - break; - - case SOURCE_POST: - set_remove(s->event->post_sources, s); - break; - - case SOURCE_EXIT: - prioq_remove(s->event->exit, s, &s->exit.prioq_index); - break; - - default: - assert_not_reached("Wut? I shouldn't exist."); - } - - if (s->pending) - prioq_remove(s->event->pending, s, &s->pending_index); - - if (s->prepare) - prioq_remove(s->event->prepare, s, &s->prepare_index); - - event = s->event; - - s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID; - s->event = NULL; - LIST_REMOVE(sources, event->sources, s); - event->n_sources--; - - if (!s->floating) - sd_event_unref(event); -} - -static void source_free(sd_event_source *s) { - assert(s); - - source_disconnect(s); - free(s->description); - free(s); -} - -static int source_set_pending(sd_event_source *s, bool b) { - int r; - - assert(s); - assert(s->type != SOURCE_EXIT); - - if (s->pending == b) - return 0; - - s->pending = b; - - if (b) { - s->pending_iteration = s->event->iteration; - - r = prioq_put(s->event->pending, s, &s->pending_index); - if (r < 0) { - s->pending = false; - return r; - } - } else - assert_se(prioq_remove(s->event->pending, s, &s->pending_index)); - - if (EVENT_SOURCE_IS_TIME(s->type)) { - struct clock_data *d; - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - } - - if (s->type == SOURCE_SIGNAL && !b) { - struct signal_data *d; - - d = hashmap_get(s->event->signal_data, &s->priority); - if (d && d->current == s) - d->current = NULL; - } - - return 0; -} - -static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { - sd_event_source *s; - - assert(e); - - s = new0(sd_event_source, 1); - if (!s) - return NULL; - - s->n_ref = 1; - s->event = e; - s->floating = floating; - s->type = type; - s->pending_index = s->prepare_index = PRIOQ_IDX_NULL; - - if (!floating) - sd_event_ref(e); - - LIST_PREPEND(sources, e->sources, s); - e->n_sources++; - - return s; -} - -_public_ int sd_event_add_io( - sd_event *e, - sd_event_source **ret, - int fd, - uint32_t events, - sd_event_io_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(fd >= 0, -EBADF); - assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - s = source_new(e, !ret, SOURCE_IO); - if (!s) - return -ENOMEM; - - s->wakeup = WAKEUP_EVENT_SOURCE; - s->io.fd = fd; - s->io.events = events; - s->io.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ON; - - r = source_io_register(s, s->enabled, events); - if (r < 0) { - source_free(s); - return r; - } - - if (ret) - *ret = s; - - return 0; -} - -static void initialize_perturb(sd_event *e) { - sd_id128_t bootid = {}; - - /* When we sleep for longer, we try to realign the wakeup to - the same time wihtin each minute/second/250ms, so that - events all across the system can be coalesced into a single - CPU wakeup. However, let's take some system-specific - randomness for this value, so that in a network of systems - with synced clocks timer events are distributed a - bit. Here, we calculate a perturbation usec offset from the - boot ID. */ - - if (_likely_(e->perturb != USEC_INFINITY)) - return; - - if (sd_id128_get_boot(&bootid) >= 0) - e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; -} - -static int event_setup_timer_fd( - sd_event *e, - struct clock_data *d, - clockid_t clock) { - - struct epoll_event ev = {}; - int r, fd; - - assert(e); - assert(d); - - if (_likely_(d->fd >= 0)) - return 0; - - fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - - ev.events = EPOLLIN; - ev.data.ptr = d; - - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); - if (r < 0) { - safe_close(fd); - return -errno; - } - - d->fd = fd; - return 0; -} - -static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) { - assert(s); - - return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); -} - -_public_ int sd_event_add_time( - sd_event *e, - sd_event_source **ret, - clockid_t clock, - uint64_t usec, - uint64_t accuracy, - sd_event_time_handler_t callback, - void *userdata) { - - EventSourceType type; - sd_event_source *s; - struct clock_data *d; - int r; - - assert_return(e, -EINVAL); - assert_return(accuracy != (uint64_t) -1, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */ - return -EOPNOTSUPP; - - type = clock_to_event_source_type(clock); /* checks whether sd-event supports this clock */ - if (type < 0) - return -EOPNOTSUPP; - - if (!callback) - callback = time_exit_callback; - - d = event_get_clock_data(e, type); - assert(d); - - r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); - if (r < 0) - return r; - - 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); - if (r < 0) - return r; - } - - s = source_new(e, !ret, type); - if (!s) - return -ENOMEM; - - s->time.next = usec; - s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy; - s->time.callback = callback; - s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; - s->userdata = userdata; - s->enabled = SD_EVENT_ONESHOT; - - d->needs_rearm = true; - - r = prioq_put(d->earliest, s, &s->time.earliest_index); - if (r < 0) - goto fail; - - r = prioq_put(d->latest, s, &s->time.latest_index); - if (r < 0) - goto fail; - - if (ret) - *ret = s; - - return 0; - -fail: - source_free(s); - return r; -} - -static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - assert(s); - - return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); -} - -_public_ int sd_event_add_signal( - sd_event *e, - sd_event_source **ret, - int sig, - sd_event_signal_handler_t callback, - void *userdata) { - - sd_event_source *s; - struct signal_data *d; - sigset_t ss; - int r; - - assert_return(e, -EINVAL); - assert_return(SIGNAL_VALID(sig), -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - if (!callback) - callback = signal_exit_callback; - - r = pthread_sigmask(SIG_SETMASK, NULL, &ss); - if (r != 0) - return -r; - - if (!sigismember(&ss, sig)) - return -EBUSY; - - if (!e->signal_sources) { - e->signal_sources = new0(sd_event_source*, _NSIG); - if (!e->signal_sources) - return -ENOMEM; - } else if (e->signal_sources[sig]) - return -EBUSY; - - s = source_new(e, !ret, SOURCE_SIGNAL); - if (!s) - return -ENOMEM; - - s->signal.sig = sig; - s->signal.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ON; - - e->signal_sources[sig] = s; - - r = event_make_signal_data(e, sig, &d); - if (r < 0) { - source_free(s); - return r; - } - - /* Use the signal name as description for the event source by default */ - (void) sd_event_source_set_description(s, signal_to_string(sig)); - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_child( - sd_event *e, - sd_event_source **ret, - pid_t pid, - int options, - sd_event_child_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(pid > 1, -EINVAL); - assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); - assert_return(options != 0, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = hashmap_ensure_allocated(&e->child_sources, NULL); - if (r < 0) - return r; - - if (hashmap_contains(e->child_sources, PID_TO_PTR(pid))) - return -EBUSY; - - s = source_new(e, !ret, SOURCE_CHILD); - if (!s) - return -ENOMEM; - - s->child.pid = pid; - s->child.options = options; - s->child.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ONESHOT; - - r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); - if (r < 0) { - source_free(s); - return r; - } - - e->n_enabled_child_sources++; - - r = event_make_signal_data(e, SIGCHLD, NULL); - if (r < 0) { - e->n_enabled_child_sources--; - source_free(s); - return r; - } - - e->need_process_child = true; - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_defer( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - s = source_new(e, !ret, SOURCE_DEFER); - if (!s) - return -ENOMEM; - - s->defer.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ONESHOT; - - r = source_set_pending(s, true); - if (r < 0) { - source_free(s); - return r; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_post( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = set_ensure_allocated(&e->post_sources, NULL); - if (r < 0) - return r; - - s = source_new(e, !ret, SOURCE_POST); - if (!s) - return -ENOMEM; - - s->post.callback = callback; - s->userdata = userdata; - s->enabled = SD_EVENT_ON; - - r = set_put(e->post_sources, s); - if (r < 0) { - source_free(s); - return r; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ int sd_event_add_exit( - sd_event *e, - sd_event_source **ret, - sd_event_handler_t callback, - void *userdata) { - - sd_event_source *s; - int r; - - assert_return(e, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); - if (r < 0) - return r; - - s = source_new(e, !ret, SOURCE_EXIT); - if (!s) - return -ENOMEM; - - s->exit.callback = callback; - s->userdata = userdata; - s->exit.prioq_index = PRIOQ_IDX_NULL; - s->enabled = SD_EVENT_ONESHOT; - - r = prioq_put(s->event->exit, s, &s->exit.prioq_index); - if (r < 0) { - source_free(s); - return r; - } - - if (ret) - *ret = s; - - return 0; -} - -_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { - - if (!s) - return NULL; - - assert(s->n_ref >= 1); - s->n_ref++; - - return s; -} - -_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { - - if (!s) - return NULL; - - assert(s->n_ref >= 1); - s->n_ref--; - - if (s->n_ref <= 0) { - /* Here's a special hack: when we are called from a - * dispatch handler we won't free the event source - * immediately, but we will detach the fd from the - * epoll. This way it is safe for the caller to unref - * the event source and immediately close the fd, but - * we still retain a valid event source object after - * the callback. */ - - if (s->dispatching) { - if (s->type == SOURCE_IO) - source_io_unregister(s); - - source_disconnect(s); - } else - source_free(s); - } - - return NULL; -} - -_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { - assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return free_and_strdup(&s->description, description); -} - -_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { - assert_return(s, -EINVAL); - assert_return(description, -EINVAL); - assert_return(s->description, -ENXIO); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *description = s->description; - return 0; -} - -_public_ sd_event *sd_event_source_get_event(sd_event_source *s) { - assert_return(s, NULL); - - return s->event; -} - -_public_ int sd_event_source_get_pending(sd_event_source *s) { - assert_return(s, -EINVAL); - assert_return(s->type != SOURCE_EXIT, -EDOM); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return s->pending; -} - -_public_ int sd_event_source_get_io_fd(sd_event_source *s) { - assert_return(s, -EINVAL); - assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return s->io.fd; -} - -_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { - int r; - - assert_return(s, -EINVAL); - assert_return(fd >= 0, -EBADF); - assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - if (s->io.fd == fd) - return 0; - - if (s->enabled == SD_EVENT_OFF) { - s->io.fd = fd; - s->io.registered = false; - } else { - int saved_fd; - - saved_fd = s->io.fd; - assert(s->io.registered); - - s->io.fd = fd; - s->io.registered = false; - - r = source_io_register(s, s->enabled, s->io.events); - if (r < 0) { - s->io.fd = saved_fd; - s->io.registered = true; - return r; - } - - epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL); - } - - return 0; -} - -_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { - assert_return(s, -EINVAL); - assert_return(events, -EINVAL); - assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *events = s->io.events; - return 0; -} - -_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) { - int r; - - assert_return(s, -EINVAL); - assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - /* edge-triggered updates are never skipped, so we can reset edges */ - if (s->io.events == events && !(events & EPOLLET)) - return 0; - - if (s->enabled != SD_EVENT_OFF) { - r = source_io_register(s, s->enabled, events); - if (r < 0) - return r; - } - - s->io.events = events; - source_set_pending(s, false); - - return 0; -} - -_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) { - assert_return(s, -EINVAL); - assert_return(revents, -EINVAL); - assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(s->pending, -ENODATA); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *revents = s->io.revents; - return 0; -} - -_public_ int sd_event_source_get_signal(sd_event_source *s) { - assert_return(s, -EINVAL); - assert_return(s->type == SOURCE_SIGNAL, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return s->signal.sig; -} - -_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) { - assert_return(s, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); - - return s->priority; -} - -_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) { - int r; - - assert_return(s, -EINVAL); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - if (s->priority == priority) - return 0; - - if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { - struct signal_data *old, *d; - - /* Move us from the signalfd belonging to the old - * priority to the signalfd of the new priority */ - - assert_se(old = hashmap_get(s->event->signal_data, &s->priority)); - - s->priority = priority; - - r = event_make_signal_data(s->event, s->signal.sig, &d); - if (r < 0) { - s->priority = old->priority; - return r; - } - - event_unmask_signal_data(s->event, old, s->signal.sig); - } else - s->priority = priority; - - if (s->pending) - prioq_reshuffle(s->event->pending, s, &s->pending_index); - - if (s->prepare) - prioq_reshuffle(s->event->prepare, s, &s->prepare_index); - - if (s->type == SOURCE_EXIT) - prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); - - return 0; -} - -_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { - assert_return(s, -EINVAL); - assert_return(m, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *m = s->enabled; - return 0; -} - -_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { - int r; - - assert_return(s, -EINVAL); - assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL); - assert_return(!event_pid_changed(s->event), -ECHILD); - - /* If we are dead anyway, we are fine with turning off - * sources, but everything else needs to fail. */ - if (s->event->state == SD_EVENT_FINISHED) - return m == SD_EVENT_OFF ? 0 : -ESTALE; - - if (s->enabled == m) - return 0; - - if (m == SD_EVENT_OFF) { - - switch (s->type) { - - case SOURCE_IO: - source_io_unregister(s); - s->enabled = m; - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - s->enabled = m; - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - s->enabled = m; - - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - break; - - case SOURCE_CHILD: - s->enabled = m; - - assert(s->event->n_enabled_child_sources > 0); - s->event->n_enabled_child_sources--; - - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - break; - - case SOURCE_EXIT: - s->enabled = m; - prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); - break; - - case SOURCE_DEFER: - case SOURCE_POST: - s->enabled = m; - break; - - default: - assert_not_reached("Wut? I shouldn't exist."); - } - - } else { - switch (s->type) { - - case SOURCE_IO: - r = source_io_register(s, m, s->io.events); - if (r < 0) - return r; - - s->enabled = m; - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: { - struct clock_data *d; - - s->enabled = m; - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - break; - } - - case SOURCE_SIGNAL: - - s->enabled = m; - - r = event_make_signal_data(s->event, s->signal.sig, NULL); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - event_gc_signal_data(s->event, &s->priority, s->signal.sig); - return r; - } - - break; - - case SOURCE_CHILD: - - if (s->enabled == SD_EVENT_OFF) - s->event->n_enabled_child_sources++; - - s->enabled = m; - - r = event_make_signal_data(s->event, SIGCHLD, NULL); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - s->event->n_enabled_child_sources--; - event_gc_signal_data(s->event, &s->priority, SIGCHLD); - return r; - } - - break; - - case SOURCE_EXIT: - s->enabled = m; - prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); - break; - - case SOURCE_DEFER: - case SOURCE_POST: - s->enabled = m; - break; - - default: - assert_not_reached("Wut? I shouldn't exist."); - } - } - - if (s->pending) - prioq_reshuffle(s->event->pending, s, &s->pending_index); - - if (s->prepare) - prioq_reshuffle(s->event->prepare, s, &s->prepare_index); - - return 0; -} - -_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { - assert_return(s, -EINVAL); - assert_return(usec, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *usec = s->time.next; - return 0; -} - -_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { - struct clock_data *d; - - assert_return(s, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - s->time.next = usec; - - source_set_pending(s, false); - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - - return 0; -} - -_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { - assert_return(s, -EINVAL); - assert_return(usec, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *usec = s->time.accuracy; - return 0; -} - -_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { - struct clock_data *d; - - assert_return(s, -EINVAL); - assert_return(usec != (uint64_t) -1, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - if (usec == 0) - usec = DEFAULT_ACCURACY_USEC; - - s->time.accuracy = usec; - - source_set_pending(s, false); - - d = event_get_clock_data(s->event, s->type); - assert(d); - - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - - return 0; -} - -_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) { - assert_return(s, -EINVAL); - assert_return(clock, -EINVAL); - assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *clock = event_source_type_to_clock(s->type); - return 0; -} - -_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { - assert_return(s, -EINVAL); - assert_return(pid, -EINVAL); - assert_return(s->type == SOURCE_CHILD, -EDOM); - assert_return(!event_pid_changed(s->event), -ECHILD); - - *pid = s->child.pid; - return 0; -} - -_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) { - int r; - - assert_return(s, -EINVAL); - assert_return(s->type != SOURCE_EXIT, -EDOM); - assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(s->event), -ECHILD); - - if (s->prepare == callback) - return 0; - - if (callback && s->prepare) { - s->prepare = callback; - return 0; - } - - r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare); - if (r < 0) - return r; - - s->prepare = callback; - - if (callback) { - r = prioq_put(s->event->prepare, s, &s->prepare_index); - if (r < 0) - return r; - } else - prioq_remove(s->event->prepare, s, &s->prepare_index); - - return 0; -} - -_public_ void* sd_event_source_get_userdata(sd_event_source *s) { - assert_return(s, NULL); - - return s->userdata; -} - -_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) { - void *ret; - - assert_return(s, NULL); - - ret = s->userdata; - s->userdata = userdata; - - return ret; -} - -static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) { - usec_t c; - assert(e); - assert(a <= b); - - if (a <= 0) - return 0; - if (a >= USEC_INFINITY) - return USEC_INFINITY; - - if (b <= a + 1) - return a; - - initialize_perturb(e); - - /* - Find a good time to wake up again between times a and b. We - have two goals here: - - a) We want to wake up as seldom as possible, hence prefer - later times over earlier times. - - b) But if we have to wake up, then let's make sure to - dispatch as much as possible on the entire system. - - We implement this by waking up everywhere at the same time - within any given minute if we can, synchronised via the - perturbation value determined from the boot ID. If we can't, - then we try to find the same spot in every 10s, then 1s and - then 250ms step. Otherwise, we pick the last possible time - to wake up. - */ - - c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb; - if (c >= b) { - if (_unlikely_(c < USEC_PER_MINUTE)) - return b; - - c -= USEC_PER_MINUTE; - } - - if (c >= a) - return c; - - c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10)); - if (c >= b) { - if (_unlikely_(c < USEC_PER_SEC*10)) - return b; - - c -= USEC_PER_SEC*10; - } - - if (c >= a) - return c; - - c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC); - if (c >= b) { - if (_unlikely_(c < USEC_PER_SEC)) - return b; - - c -= USEC_PER_SEC; - } - - if (c >= a) - return c; - - c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250)); - if (c >= b) { - if (_unlikely_(c < USEC_PER_MSEC*250)) - return b; - - c -= USEC_PER_MSEC*250; - } - - if (c >= a) - return c; - - return b; -} - -static int event_arm_timer( - sd_event *e, - struct clock_data *d) { - - struct itimerspec its = {}; - sd_event_source *a, *b; - usec_t t; - int r; - - assert(e); - assert(d); - - if (!d->needs_rearm) - return 0; - else - d->needs_rearm = false; - - a = prioq_peek(d->earliest); - if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { - - if (d->fd < 0) - return 0; - - if (d->next == USEC_INFINITY) - return 0; - - /* disarm */ - r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); - if (r < 0) - return r; - - d->next = USEC_INFINITY; - return 0; - } - - b = prioq_peek(d->latest); - assert_se(b && b->enabled != SD_EVENT_OFF); - - t = sleep_between(e, a->time.next, time_event_source_latest(b)); - if (d->next == t) - return 0; - - assert_se(d->fd >= 0); - - if (t == 0) { - /* We don' want to disarm here, just mean some time looooong ago. */ - its.it_value.tv_sec = 0; - its.it_value.tv_nsec = 1; - } else - timespec_store(&its.it_value, t); - - r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); - if (r < 0) - return -errno; - - d->next = t; - return 0; -} - -static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) { - assert(e); - assert(s); - assert(s->type == SOURCE_IO); - - /* If the event source was already pending, we just OR in the - * new revents, otherwise we reset the value. The ORing is - * necessary to handle EPOLLONESHOT events properly where - * readability might happen independently of writability, and - * we need to keep track of both */ - - if (s->pending) - s->io.revents |= revents; - else - s->io.revents = revents; - - return source_set_pending(s, true); -} - -static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) { - uint64_t x; - ssize_t ss; - - assert(e); - assert(fd >= 0); - - assert_return(events == EPOLLIN, -EIO); - - ss = read(fd, &x, sizeof(x)); - if (ss < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - if (_unlikely_(ss != sizeof(x))) - return -EIO; - - if (next) - *next = USEC_INFINITY; - - return 0; -} - -static int process_timer( - sd_event *e, - usec_t n, - struct clock_data *d) { - - sd_event_source *s; - int r; - - assert(e); - assert(d); - - for (;;) { - s = prioq_peek(d->earliest); - if (!s || - s->time.next > n || - s->enabled == SD_EVENT_OFF || - s->pending) - break; - - r = source_set_pending(s, true); - if (r < 0) - return r; - - prioq_reshuffle(d->earliest, s, &s->time.earliest_index); - prioq_reshuffle(d->latest, s, &s->time.latest_index); - d->needs_rearm = true; - } - - return 0; -} - -static int process_child(sd_event *e) { - sd_event_source *s; - Iterator i; - int r; - - assert(e); - - e->need_process_child = false; - - /* - So, this is ugly. We iteratively invoke waitid() with P_PID - + WNOHANG for each PID we wait for, instead of using - P_ALL. This is because we only want to get child - information of very specific child processes, and not all - of them. We might not have processed the SIGCHLD even of a - previous invocation and we don't want to maintain a - unbounded *per-child* event queue, hence we really don't - want anything flushed out of the kernel's queue that we - don't care about. Since this is O(n) this means that if you - have a lot of processes you probably want to handle SIGCHLD - yourself. - - We do not reap the children here (by using WNOWAIT), this - is only done after the event source is dispatched so that - the callback still sees the process as a zombie. - */ - - HASHMAP_FOREACH(s, e->child_sources, i) { - assert(s->type == SOURCE_CHILD); - - if (s->pending) - continue; - - if (s->enabled == SD_EVENT_OFF) - continue; - - zero(s->child.siginfo); - r = waitid(P_PID, s->child.pid, &s->child.siginfo, - WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options); - if (r < 0) - return -errno; - - if (s->child.siginfo.si_pid != 0) { - bool zombie = - s->child.siginfo.si_code == CLD_EXITED || - s->child.siginfo.si_code == CLD_KILLED || - s->child.siginfo.si_code == CLD_DUMPED; - - if (!zombie && (s->child.options & WEXITED)) { - /* If the child isn't dead then let's - * immediately remove the state change - * from the queue, since there's no - * benefit in leaving it queued */ - - assert(s->child.options & (WSTOPPED|WCONTINUED)); - waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED))); - } - - r = source_set_pending(s, true); - if (r < 0) - return r; - } - } - - return 0; -} - -static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { - bool read_one = false; - int r; - - assert(e); - assert_return(events == EPOLLIN, -EIO); - - /* If there's a signal queued on this priority and SIGCHLD is - on this priority too, then make sure to recheck the - children we watch. This is because we only ever dequeue - the first signal per priority, and if we dequeue one, and - SIGCHLD might be enqueued later we wouldn't know, but we - might have higher priority children we care about hence we - need to check that explicitly. */ - - if (sigismember(&d->sigset, SIGCHLD)) - e->need_process_child = true; - - /* If there's already an event source pending for this - * priority we don't read another */ - if (d->current) - return 0; - - for (;;) { - struct signalfd_siginfo si; - ssize_t n; - sd_event_source *s = NULL; - - n = read(d->fd, &si, sizeof(si)); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return read_one; - - return -errno; - } - - if (_unlikely_(n != sizeof(si))) - return -EIO; - - assert(SIGNAL_VALID(si.ssi_signo)); - - read_one = true; - - if (e->signal_sources) - s = e->signal_sources[si.ssi_signo]; - if (!s) - continue; - if (s->pending) - continue; - - s->signal.siginfo = si; - d->current = s; - - r = source_set_pending(s, true); - if (r < 0) - return r; - - return 1; - } -} - -static int source_dispatch(sd_event_source *s) { - int r = 0; - - assert(s); - assert(s->pending || s->type == SOURCE_EXIT); - - if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) { - r = source_set_pending(s, false); - if (r < 0) - return r; - } - - if (s->type != SOURCE_POST) { - sd_event_source *z; - Iterator i; - - /* If we execute a non-post source, let's mark all - * post sources as pending */ - - SET_FOREACH(z, s->event->post_sources, i) { - if (z->enabled == SD_EVENT_OFF) - continue; - - r = source_set_pending(z, true); - if (r < 0) - return r; - } - } - - if (s->enabled == SD_EVENT_ONESHOT) { - r = sd_event_source_set_enabled(s, SD_EVENT_OFF); - if (r < 0) - return r; - } - - s->dispatching = true; - - switch (s->type) { - - case SOURCE_IO: - r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata); - break; - - case SOURCE_TIME_REALTIME: - case SOURCE_TIME_BOOTTIME: - case SOURCE_TIME_MONOTONIC: - case SOURCE_TIME_REALTIME_ALARM: - case SOURCE_TIME_BOOTTIME_ALARM: - r = s->time.callback(s, s->time.next, s->userdata); - break; - - case SOURCE_SIGNAL: - r = s->signal.callback(s, &s->signal.siginfo, s->userdata); - break; - - case SOURCE_CHILD: { - bool zombie; - - zombie = s->child.siginfo.si_code == CLD_EXITED || - s->child.siginfo.si_code == CLD_KILLED || - s->child.siginfo.si_code == CLD_DUMPED; - - r = s->child.callback(s, &s->child.siginfo, s->userdata); - - /* Now, reap the PID for good. */ - if (zombie) - waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); - - break; - } - - case SOURCE_DEFER: - r = s->defer.callback(s, s->userdata); - break; - - case SOURCE_POST: - r = s->post.callback(s, s->userdata); - break; - - case SOURCE_EXIT: - r = s->exit.callback(s, s->userdata); - break; - - case SOURCE_WATCHDOG: - case _SOURCE_EVENT_SOURCE_TYPE_MAX: - case _SOURCE_EVENT_SOURCE_TYPE_INVALID: - assert_not_reached("Wut? I shouldn't exist."); - } - - s->dispatching = false; - - 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); - else if (r < 0) - sd_event_source_set_enabled(s, SD_EVENT_OFF); - - return 1; -} - -static int event_prepare(sd_event *e) { - int r; - - assert(e); - - for (;;) { - sd_event_source *s; - - s = prioq_peek(e->prepare); - if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF) - break; - - s->prepare_iteration = e->iteration; - r = prioq_reshuffle(e->prepare, s, &s->prepare_index); - if (r < 0) - return r; - - assert(s->prepare); - - s->dispatching = true; - r = s->prepare(s, s->userdata); - s->dispatching = false; - - 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); - else if (r < 0) - sd_event_source_set_enabled(s, SD_EVENT_OFF); - } - - return 0; -} - -static int dispatch_exit(sd_event *e) { - sd_event_source *p; - int r; - - assert(e); - - p = prioq_peek(e->exit); - if (!p || p->enabled == SD_EVENT_OFF) { - e->state = SD_EVENT_FINISHED; - return 0; - } - - sd_event_ref(e); - e->iteration++; - e->state = SD_EVENT_EXITING; - - r = source_dispatch(p); - - e->state = SD_EVENT_INITIAL; - sd_event_unref(e); - - return r; -} - -static sd_event_source* event_next_pending(sd_event *e) { - sd_event_source *p; - - assert(e); - - p = prioq_peek(e->pending); - if (!p) - return NULL; - - if (p->enabled == SD_EVENT_OFF) - return NULL; - - return p; -} - -static int arm_watchdog(sd_event *e) { - struct itimerspec its = {}; - usec_t t; - int r; - - assert(e); - assert(e->watchdog_fd >= 0); - - t = sleep_between(e, - e->watchdog_last + (e->watchdog_period / 2), - e->watchdog_last + (e->watchdog_period * 3 / 4)); - - timespec_store(&its.it_value, t); - - /* Make sure we never set the watchdog to 0, which tells the - * kernel to disable it. */ - if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) - its.it_value.tv_nsec = 1; - - r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL); - if (r < 0) - return -errno; - - return 0; -} - -static int process_watchdog(sd_event *e) { - assert(e); - - if (!e->watchdog) - return 0; - - /* Don't notify watchdog too often */ - if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic) - return 0; - - sd_notify(false, "WATCHDOG=1"); - e->watchdog_last = e->timestamp.monotonic; - - return arm_watchdog(e); -} - -_public_ int sd_event_prepare(sd_event *e) { - int r; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); - - if (e->exit_requested) - goto pending; - - e->iteration++; - - e->state = SD_EVENT_PREPARING; - r = event_prepare(e); - e->state = SD_EVENT_INITIAL; - if (r < 0) - return r; - - r = event_arm_timer(e, &e->realtime); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->boottime); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->monotonic); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->realtime_alarm); - if (r < 0) - return r; - - r = event_arm_timer(e, &e->boottime_alarm); - if (r < 0) - return r; - - if (event_next_pending(e) || e->need_process_child) - goto pending; - - e->state = SD_EVENT_ARMED; - - return 0; - -pending: - e->state = SD_EVENT_ARMED; - r = sd_event_wait(e, 0); - if (r == 0) - e->state = SD_EVENT_ARMED; - - return r; -} - -_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { - struct epoll_event *ev_queue; - unsigned ev_queue_max; - int r, m, i; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_ARMED, -EBUSY); - - if (e->exit_requested) { - e->state = SD_EVENT_PENDING; - return 1; - } - - ev_queue_max = MAX(e->n_sources, 1u); - ev_queue = newa(struct epoll_event, ev_queue_max); - - m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, - timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); - if (m < 0) { - if (errno == EINTR) { - e->state = SD_EVENT_PENDING; - return 1; - } - - r = -errno; - goto finish; - } - - triple_timestamp_get(&e->timestamp); - - for (i = 0; i < m; i++) { - - if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) - r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); - else { - WakeupType *t = ev_queue[i].data.ptr; - - switch (*t) { - - case WAKEUP_EVENT_SOURCE: - r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); - break; - - case WAKEUP_CLOCK_DATA: { - struct clock_data *d = ev_queue[i].data.ptr; - r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); - break; - } - - case WAKEUP_SIGNAL_DATA: - r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); - break; - - default: - assert_not_reached("Invalid wake-up pointer"); - } - } - if (r < 0) - goto finish; - } - - r = process_watchdog(e); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.realtime, &e->realtime); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.boottime, &e->boottime); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.monotonic, &e->monotonic); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm); - if (r < 0) - goto finish; - - r = process_timer(e, e->timestamp.boottime, &e->boottime_alarm); - if (r < 0) - goto finish; - - if (e->need_process_child) { - r = process_child(e); - if (r < 0) - goto finish; - } - - if (event_next_pending(e)) { - e->state = SD_EVENT_PENDING; - - return 1; - } - - r = 0; - -finish: - e->state = SD_EVENT_INITIAL; - - return r; -} - -_public_ int sd_event_dispatch(sd_event *e) { - sd_event_source *p; - int r; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_PENDING, -EBUSY); - - if (e->exit_requested) - return dispatch_exit(e); - - p = event_next_pending(e); - if (p) { - sd_event_ref(e); - - e->state = SD_EVENT_RUNNING; - r = source_dispatch(p); - e->state = SD_EVENT_INITIAL; - - sd_event_unref(e); - - return r; - } - - e->state = SD_EVENT_INITIAL; - - 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; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_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); - if (r < 0) - return r; - - return 1; - } - - return r; -} - -_public_ int sd_event_loop(sd_event *e) { - int r; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); - - sd_event_ref(e); - - while (e->state != SD_EVENT_FINISHED) { - r = sd_event_run(e, (uint64_t) -1); - if (r < 0) - goto finish; - } - - r = e->exit_code; - -finish: - sd_event_unref(e); - return r; -} - -_public_ int sd_event_get_fd(sd_event *e) { - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - return e->epoll_fd; -} - -_public_ int sd_event_get_state(sd_event *e) { - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - return e->state; -} - -_public_ int sd_event_get_exit_code(sd_event *e, int *code) { - assert_return(e, -EINVAL); - assert_return(code, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - if (!e->exit_requested) - return -ENODATA; - - *code = e->exit_code; - return 0; -} - -_public_ int sd_event_exit(sd_event *e, int code) { - assert_return(e, -EINVAL); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(!event_pid_changed(e), -ECHILD); - - e->exit_requested = true; - e->exit_code = code; - - return 0; -} - -_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); - - if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) - return -EOPNOTSUPP; - - /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here, - * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for - * the purpose of getting the time this doesn't matter. */ - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) - return -EOPNOTSUPP; - - if (!triple_timestamp_is_set(&e->timestamp)) { - /* Implicitly fall back to now() if we never ran - * before and thus have no cached time. */ - *usec = now(clock); - return 1; - } - - *usec = triple_timestamp_by_clock(&e->timestamp, clock); - return 0; -} - -_public_ int sd_event_default(sd_event **ret) { - - static thread_local sd_event *default_event = NULL; - sd_event *e = NULL; - int r; - - if (!ret) - return !!default_event; - - if (default_event) { - *ret = sd_event_ref(default_event); - return 0; - } - - r = sd_event_new(&e); - if (r < 0) - return r; - - e->default_event_ptr = &default_event; - e->tid = gettid(); - default_event = e; - - *ret = e; - return 1; -} - -_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { - assert_return(e, -EINVAL); - assert_return(tid, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - if (e->tid != 0) { - *tid = e->tid; - return 0; - } - - return -ENXIO; -} - -_public_ int sd_event_set_watchdog(sd_event *e, int b) { - int r; - - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - if (e->watchdog == !!b) - return e->watchdog; - - if (b) { - struct epoll_event ev = {}; - - r = sd_watchdog_enabled(false, &e->watchdog_period); - if (r <= 0) - return r; - - /* Issue first ping immediately */ - sd_notify(false, "WATCHDOG=1"); - e->watchdog_last = now(CLOCK_MONOTONIC); - - e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); - if (e->watchdog_fd < 0) - return -errno; - - r = arm_watchdog(e); - if (r < 0) - goto fail; - - ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG); - - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev); - if (r < 0) { - r = -errno; - goto fail; - } - - } else { - if (e->watchdog_fd >= 0) { - epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL); - e->watchdog_fd = safe_close(e->watchdog_fd); - } - } - - e->watchdog = !!b; - return e->watchdog; - -fail: - e->watchdog_fd = safe_close(e->watchdog_fd); - return r; -} - -_public_ int sd_event_get_watchdog(sd_event *e) { - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - return e->watchdog; -} - -_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { - assert_return(e, -EINVAL); - assert_return(!event_pid_changed(e), -ECHILD); - - *ret = e->iteration; - return 0; -} diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c deleted file mode 100644 index 289114490c..0000000000 --- a/src/libsystemd/sd-event/test-event.c +++ /dev/null @@ -1,361 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-event.h" - -#include "fd-util.h" -#include "log.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)); - return 1; -} - -static bool got_a, got_b, got_c, got_unref; -static unsigned got_d; - -static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_event_source_unref(s); - got_unref = true; - return 0; -} - -static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - - log_info("got IO on %c", PTR_TO_INT(userdata)); - - if (userdata == INT_TO_PTR('a')) { - assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); - assert_se(!got_a); - got_a = true; - } else if (userdata == INT_TO_PTR('b')) { - assert_se(!got_b); - got_b = true; - } else if (userdata == INT_TO_PTR('d')) { - got_d++; - if (got_d < 2) - assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0); - else - assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); - } else - assert_not_reached("Yuck!"); - - return 1; -} - -static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) { - - assert_se(s); - assert_se(si); - - log_info("got child on %c", PTR_TO_INT(userdata)); - - assert_se(userdata == INT_TO_PTR('f')); - - assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0); - sd_event_source_unref(s); - - return 1; -} - -static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - sd_event_source *p = NULL; - pid_t pid; - - assert_se(s); - assert_se(si); - - log_info("got signal on %c", PTR_TO_INT(userdata)); - - assert_se(userdata == INT_TO_PTR('e')); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - pid = fork(); - assert_se(pid >= 0); - - if (pid == 0) - _exit(0); - - assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0); - assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); - - sd_event_source_unref(s); - - return 1; -} - -static int defer_handler(sd_event_source *s, void *userdata) { - sd_event_source *p = NULL; - - assert_se(s); - - log_info("got defer on %c", PTR_TO_INT(userdata)); - - assert_se(userdata == INT_TO_PTR('d')); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0); - - assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 0); - assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); - raise(SIGUSR1); - - sd_event_source_unref(s); - - return 1; -} - -static bool do_quit = false; - -static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) { - log_info("got timer on %c", PTR_TO_INT(userdata)); - - if (userdata == INT_TO_PTR('c')) { - - if (do_quit) { - sd_event_source *p; - - assert_se(sd_event_add_defer(sd_event_source_get_event(s), &p, defer_handler, INT_TO_PTR('d')) >= 0); - assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); - } else { - assert_se(!got_c); - got_c = true; - } - } else - assert_not_reached("Huh?"); - - return 2; -} - -static bool got_exit = false; - -static int exit_handler(sd_event_source *s, void *userdata) { - log_info("got quit handler on %c", PTR_TO_INT(userdata)); - - got_exit = true; - - 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); - assert_se(pipe(d) >= 0); - 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); - - /* Test whether we cleanly can destroy an io event source from its own handler */ - got_unref = false; - assert_se(sd_event_add_io(e, &t, k[0], EPOLLIN, unref_handler, NULL) >= 0); - assert_se(write(k[1], &ch, 1) == 1); - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(got_unref); - - got_a = false, got_b = false, got_c = false, got_d = 0; - - /* Add a oneshot handler, trigger it, re-enable it, and trigger - * it again. */ - assert_se(sd_event_add_io(e, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 0); - assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0); - assert_se(write(d[1], &ch, 1) >= 0); - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(got_d == 1); - assert_se(write(d[1], &ch, 1) >= 0); - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(got_d == 2); - - assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0); - assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0); - assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0); - assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0); - - assert_se(sd_event_source_set_priority(x, 99) >= 0); - assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0); - assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0); - assert_se(sd_event_source_set_priority(z, 50) >= 0); - assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0); - assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0); - - /* Test for floating event sources */ - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0); - assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 0); - - assert_se(write(a[1], &ch, 1) >= 0); - assert_se(write(b[1], &ch, 1) >= 0); - - assert_se(!got_a && !got_b && !got_c); - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - - assert_se(!got_a && got_b && !got_c); - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - - assert_se(!got_a && got_b && got_c); - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - - assert_se(got_a && got_b && got_c); - - sd_event_source_unref(x); - sd_event_source_unref(y); - - do_quit = true; - 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); - - sd_event_source_unref(w); - - sd_event_unref(e); - - safe_close_pair(a); - safe_close_pair(b); - safe_close_pair(d); - 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); - if (clock_boottime_supported()) { - 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); - if (clock_boottime_supported()) { - 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; - -static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - last_rtqueue_sigval = si->ssi_int; - n_rtqueue++; - return 0; -} - -static void test_rtqueue(void) { - sd_event_source *u = NULL, *v = NULL, *s = NULL; - sd_event *e = NULL; - - assert_se(sd_event_default(&e) >= 0); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); - assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0); - assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0); - assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0); - - assert_se(sd_event_source_set_priority(v, -10) >= 0); - - assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0); - assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0); - assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0); - assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0); - assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0); - - assert_se(n_rtqueue == 0); - assert_se(last_rtqueue_sigval == 0); - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 1); - assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 2); - assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 3); - assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */ - - assert_se(sd_event_run(e, (uint64_t) -1) >= 1); - assert_se(n_rtqueue == 4); - assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */ - - assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */ - assert_se(n_rtqueue == 4); - assert_se(last_rtqueue_sigval == 1); - - sd_event_source_unref(u); - sd_event_source_unref(v); - sd_event_source_unref(s); - - sd_event_unref(e); -} - -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/Makefile b/src/libsystemd/sd-hwdb/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-hwdb/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h deleted file mode 100644 index 8ffb5e5c74..0000000000 --- a/src/libsystemd/sd-hwdb/hwdb-internal.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sparse-endian.h" -#include "util.h" - -#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' } - -/* on-disk trie objects */ -struct trie_header_f { - uint8_t signature[8]; - - /* version of tool which created the file */ - le64_t tool_version; - le64_t file_size; - - /* size of structures to allow them to grow */ - le64_t header_size; - le64_t node_size; - le64_t child_entry_size; - le64_t value_entry_size; - - /* offset of the root trie node */ - le64_t nodes_root_off; - - /* size of the nodes and string section */ - le64_t nodes_len; - le64_t strings_len; -} _packed_; - -struct trie_node_f { - /* prefix of lookup string, shared by all children */ - le64_t prefix_off; - /* size of children entry array appended to the node */ - uint8_t children_count; - uint8_t padding[7]; - /* size of value entry array appended to the node */ - le64_t values_count; -} _packed_; - -/* array of child entries, follows directly the node record */ -struct trie_child_entry_f { - /* index of the child node */ - uint8_t c; - uint8_t padding[7]; - /* offset of the child node */ - le64_t child_off; -} _packed_; - -/* array of value entries, follows directly the node record/child array */ -struct trie_value_entry_f { - le64_t key_off; - le64_t value_off; -} _packed_; diff --git a/src/libsystemd/sd-hwdb/hwdb-util.h b/src/libsystemd/sd-hwdb/hwdb-util.h deleted file mode 100644 index 5e21e5008b..0000000000 --- a/src/libsystemd/sd-hwdb/hwdb-util.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-hwdb.h" - -#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 deleted file mode 100644 index 062fa97b17..0000000000 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ /dev/null @@ -1,470 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - Copyright 2008 Alan Jenkins - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-hwdb.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "hwdb-internal.h" -#include "hwdb-util.h" -#include "refcnt.h" -#include "string-util.h" - -struct sd_hwdb { - RefCount n_ref; - int refcount; - - FILE *f; - struct stat st; - union { - struct trie_header_f *head; - const char *map; - }; - - char *modalias; - - OrderedHashmap *properties; - Iterator properties_iterator; - bool properties_modified; -}; - -struct linebuf { - char bytes[LINE_MAX]; - size_t size; - size_t len; -}; - -static void linebuf_init(struct linebuf *buf) { - buf->size = 0; - buf->len = 0; -} - -static const char *linebuf_get(struct linebuf *buf) { - if (buf->len + 1 >= sizeof(buf->bytes)) - return NULL; - buf->bytes[buf->len] = '\0'; - return buf->bytes; -} - -static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) { - if (buf->len + len >= sizeof(buf->bytes)) - return false; - memcpy(buf->bytes + buf->len, s, len); - buf->len += len; - return true; -} - -static bool linebuf_add_char(struct linebuf *buf, char c) { - if (buf->len + 1 >= sizeof(buf->bytes)) - return false; - buf->bytes[buf->len++] = c; - return true; -} - -static void linebuf_rem(struct linebuf *buf, size_t count) { - assert(buf->len >= count); - buf->len -= count; -} - -static void linebuf_rem_char(struct linebuf *buf) { - linebuf_rem(buf, 1); -} - -static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) { - return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size)); -} - -static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) { - const char *base = (const char *)node; - - base += le64toh(hwdb->head->node_size); - base += node->children_count * le64toh(hwdb->head->child_entry_size); - return (const struct trie_value_entry_f *)base; -} - -static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) { - return (const struct trie_node_f *)(hwdb->map + le64toh(off)); -} - -static const char *trie_string(sd_hwdb *hwdb, le64_t off) { - return hwdb->map + le64toh(off); -} - -static int trie_children_cmp_f(const void *v1, const void *v2) { - const struct trie_child_entry_f *n1 = v1; - const struct trie_child_entry_f *n2 = v2; - - return n1->c - n2->c; -} - -static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) { - struct trie_child_entry_f *child; - struct trie_child_entry_f search; - - search.c = c; - child = bsearch(&search, trie_node_children(hwdb, node), node->children_count, - le64toh(hwdb->head->child_entry_size), trie_children_cmp_f); - if (child) - return trie_node_from_off(hwdb, child->child_off); - return NULL; -} - -static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) { - int r; - - assert(hwdb); - assert(key); - assert(value); - - /* - * Silently ignore all properties which do not start with a - * space; future extensions might use additional prefixes. - */ - if (key[0] != ' ') - return 0; - - key++; - - r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); - if (r < 0) - return r; - - r = ordered_hashmap_replace(hwdb->properties, key, (char*)value); - if (r < 0) - return r; - - hwdb->properties_modified = true; - - return 0; -} - -static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p, - struct linebuf *buf, const char *search) { - size_t len; - size_t i; - const char *prefix; - int err; - - prefix = trie_string(hwdb, node->prefix_off); - len = strlen(prefix + p); - linebuf_add(buf, prefix + p, len); - - for (i = 0; i < node->children_count; i++) { - const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i]; - - linebuf_add_char(buf, child->c); - err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search); - if (err < 0) - return err; - linebuf_rem_char(buf); - } - - if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0) - for (i = 0; i < le64toh(node->values_count); i++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off)); - if (err < 0) - return err; - } - - linebuf_rem(buf, len); - return 0; -} - -static int trie_search_f(sd_hwdb *hwdb, const char *search) { - struct linebuf buf; - const struct trie_node_f *node; - size_t i = 0; - int err; - - linebuf_init(&buf); - - node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off); - while (node) { - const struct trie_node_f *child; - size_t p = 0; - - if (node->prefix_off) { - uint8_t c; - - for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) { - if (c == '*' || c == '?' || c == '[') - return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p); - if (c != search[i + p]) - return 0; - } - i += p; - } - - child = node_lookup_f(hwdb, node, '*'); - if (child) { - linebuf_add_char(&buf, '*'); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - child = node_lookup_f(hwdb, node, '?'); - if (child) { - linebuf_add_char(&buf, '?'); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - child = node_lookup_f(hwdb, node, '['); - if (child) { - linebuf_add_char(&buf, '['); - err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); - if (err < 0) - return err; - linebuf_rem_char(&buf); - } - - if (search[i] == '\0') { - size_t n; - - for (n = 0; n < le64toh(node->values_count); n++) { - err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off), - trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off)); - if (err < 0) - return err; - } - return 0; - } - - child = node_lookup_f(hwdb, node, search[i]); - node = child; - i++; - } - return 0; -} - -static const char hwdb_bin_paths[] = - "/etc/systemd/hwdb/hwdb.bin\0" - "/etc/udev/hwdb.bin\0" - "/usr/lib/systemd/hwdb/hwdb.bin\0" -#ifdef HAVE_SPLIT_USR - "/lib/systemd/hwdb/hwdb.bin\0" -#endif - UDEVLIBEXECDIR "/hwdb.bin\0"; - -_public_ int sd_hwdb_new(sd_hwdb **ret) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - const char *hwdb_bin_path; - const char sig[] = HWDB_SIG; - - assert_return(ret, -EINVAL); - - hwdb = new0(sd_hwdb, 1); - if (!hwdb) - return -ENOMEM; - - hwdb->n_ref = REFCNT_INIT; - - /* find hwdb.bin in hwdb_bin_paths */ - NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) { - hwdb->f = fopen(hwdb_bin_path, "re"); - if (hwdb->f) - break; - else if (errno == ENOENT) - continue; - else - return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); - } - - if (!hwdb->f) { - log_debug("hwdb.bin does not exist, please run udevadm hwdb --update"); - return -ENOENT; - } - - if (fstat(fileno(hwdb->f), &hwdb->st) < 0 || - (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) - return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); - - hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0); - if (hwdb->map == MAP_FAILED) - return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path); - - if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || - (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { - log_debug("error recognizing the format of %s", hwdb_bin_path); - return -EINVAL; - } - - log_debug("=== trie on-disk ==="); - log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); - log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size); - log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size)); - log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len)); - log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len)); - - *ret = hwdb; - hwdb = NULL; - - return 0; -} - -_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) { - assert_return(hwdb, NULL); - - assert_se(REFCNT_INC(hwdb->n_ref) >= 2); - - return hwdb; -} - -_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) { - if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) { - if (hwdb->map) - munmap((void *)hwdb->map, hwdb->st.st_size); - safe_fclose(hwdb->f); - free(hwdb->modalias); - ordered_hashmap_free(hwdb->properties); - free(hwdb); - } - - return NULL; -} - -bool hwdb_validate(sd_hwdb *hwdb) { - bool found = false; - const char* p; - struct stat st; - - if (!hwdb) - return false; - if (!hwdb->f) - return false; - - /* if hwdb.bin doesn't exist anywhere, we need to update */ - NULSTR_FOREACH(p, hwdb_bin_paths) { - if (stat(p, &st) >= 0) { - found = true; - break; - } - } - if (!found) - return true; - - if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim)) - return true; - return false; -} - -static int properties_prepare(sd_hwdb *hwdb, const char *modalias) { - _cleanup_free_ char *mod = NULL; - int r; - - assert(hwdb); - assert(modalias); - - if (streq_ptr(modalias, hwdb->modalias)) - return 0; - - mod = strdup(modalias); - if (!mod) - return -ENOMEM; - - ordered_hashmap_clear(hwdb->properties); - - hwdb->properties_modified = true; - - r = trie_search_f(hwdb, modalias); - if (r < 0) - return r; - - free(hwdb->modalias); - hwdb->modalias = mod; - mod = NULL; - - return 0; -} - -_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) { - const char *value; - int r; - - assert_return(hwdb, -EINVAL); - assert_return(hwdb->f, -EINVAL); - assert_return(modalias, -EINVAL); - assert_return(_value, -EINVAL); - - r = properties_prepare(hwdb, modalias); - if (r < 0) - return r; - - value = ordered_hashmap_get(hwdb->properties, key); - if (!value) - return -ENOENT; - - *_value = value; - - return 0; -} - -_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) { - int r; - - assert_return(hwdb, -EINVAL); - assert_return(hwdb->f, -EINVAL); - assert_return(modalias, -EINVAL); - - r = properties_prepare(hwdb, modalias); - if (r < 0) - return r; - - hwdb->properties_modified = false; - hwdb->properties_iterator = ITERATOR_FIRST; - - return 0; -} - -_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) { - const void *k; - void *v; - - assert_return(hwdb, -EINVAL); - assert_return(key, -EINVAL); - assert_return(value, -EINVAL); - - if (hwdb->properties_modified) - return -EAGAIN; - - ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k); - if (!k) - return 0; - - *key = k; - *value = v; - - return 1; -} diff --git a/src/libsystemd/sd-id128.xml b/src/libsystemd/sd-id128.xml new file mode 100644 index 0000000000..ea7972055d --- /dev/null +++ b/src/libsystemd/sd-id128.xml @@ -0,0 +1,166 @@ + + + + + + + + + sd-id128 + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-id128 + 3 + + + + sd-id128 + sd_id128_t + SD_ID128_MAKE + SD_ID128_CONST_STR + SD_ID128_FORMAT_STR + SD_ID128_FORMAT_VAL + sd_id128_equal + APIs for processing 128-bit IDs + + + + + #include <systemd/sd-id128.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-id128.h provides APIs to process and + generate 128-bit ID values. The 128-bit ID values processed and + generated by these APIs are a generalization of OSF UUIDs as + defined by RFC + 4122 but use a simpler string format. These functions + impose no structure on the used IDs, much unlike OSF UUIDs or + Microsoft GUIDs, but are fully compatible with those types of IDs. + + + See + sd_id128_to_string3, + sd_id128_randomize3 + and + sd_id128_get_machine3 + for more information about the implemented functions. + + A 128-bit ID is implemented as the following + union type: + + typedef union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +} sd_id128_t; + + This union type allows accessing the 128-bit ID as 16 + separate bytes or two 64-bit words. It is generally safer to + access the ID components by their 8-bit array to avoid endianness + issues. This union is intended to be passed call-by-value (as + opposed to call-by-reference) and may be directly manipulated by + clients. + + A couple of macros are defined to denote and decode 128-bit + IDs: + + SD_ID128_MAKE() may be used to denote a + constant 128-bit ID in source code. A commonly used idiom is to + assign a name to a 128-bit ID using this macro: + + #define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + + SD_ID128_CONST_STR() may be used to + convert constant 128-bit IDs into constant strings for output. The + following example code will output the string + "fc2e22bc6ee647b6b90729ab34a250b1": + int main(int argc, char *argv[]) { + puts(SD_ID128_CONST_STR(SD_MESSAGE_COREDUMP)); +} + + SD_ID128_FORMAT_STR and + SD_ID128_FORMAT_VAL() may be used to format a + 128-bit ID in a + printf3 + format string, as shown in the following example: + + int main(int argc, char *argv[]) { + sd_id128_t id; + id = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07); + printf("The ID encoded in this C file is " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(id)); + return 0; +} + + Use sd_id128_equal() to compare two 128-bit IDs: + + int main(int argc, char *argv[]) { + sd_id128_t a, b, c; + a = SD_ID128_MAKE(ee,89,be,71,bd,6e,43,d6,91,e6,c5,5d,eb,03,02,07); + b = SD_ID128_MAKE(f2,28,88,9c,5f,09,44,15,9d,d7,04,77,58,cb,e7,3e); + c = a; + assert(sd_id128_equal(a, c)); + assert(!sd_id128_equal(a, b)); + return 0; +} + + Note that new, randomized IDs may be generated with + journalctl1's + option. + + + + + + See Also + + systemd1, + sd_id128_to_string3, + sd_id128_randomize3, + sd_id128_get_machine3, + printf3, + journalctl1, + sd-journal7, + pkg-config1, + machine-id5 + + + + diff --git a/src/libsystemd/sd-id128/Makefile b/src/libsystemd/sd-id128/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-id128/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c deleted file mode 100644 index c3f527d657..0000000000 --- a/src/libsystemd/sd-id128/id128-util.c +++ /dev/null @@ -1,194 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "fd-util.h" -#include "hexdecoct.h" -#include "id128-util.h" -#include "io-util.h" -#include "stdio-util.h" - -char *id128_to_uuid_string(sd_id128_t id, char s[37]) { - unsigned n, k = 0; - - assert(s); - - /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - - for (n = 0; n < 16; n++) { - - if (IN_SET(n, 4, 6, 8, 10)) - s[k++] = '-'; - - s[k++] = hexchar(id.bytes[n] >> 4); - s[k++] = hexchar(id.bytes[n] & 0xF); - } - - assert(k == 36); - - s[k] = 0; - - return s; -} - -bool id128_is_valid(const char *s) { - size_t i, l; - - assert(s); - - l = strlen(s); - if (l == 32) { - - /* Plain formatted 128bit hex string */ - - for (i = 0; i < l; i++) { - char c = s[i]; - - if (!(c >= '0' && c <= '9') && - !(c >= 'a' && c <= 'z') && - !(c >= 'A' && c <= 'Z')) - return false; - } - - } else if (l == 36) { - - /* Formatted UUID */ - - for (i = 0; i < l; i++) { - char c = s[i]; - - if ((i == 8 || i == 13 || i == 18 || i == 23)) { - if (c != '-') - return false; - } else { - if (!(c >= '0' && c <= '9') && - !(c >= 'a' && c <= 'z') && - !(c >= 'A' && c <= 'Z')) - return false; - } - } - - } else - return false; - - return true; -} - -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { - char buffer[36 + 2]; - ssize_t l; - - assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); - - /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both - * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they - * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you - * accept". */ - - l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ - if (l < 0) - return (int) l; - if (l == 0) /* empty? */ - return -ENOMEDIUM; - - switch (l) { - - case 33: /* plain UUID with trailing newline */ - if (buffer[32] != '\n') - return -EINVAL; - - /* fall through */ - case 32: /* plain UUID without trailing newline */ - if (f == ID128_UUID) - return -EINVAL; - - buffer[32] = 0; - break; - - case 37: /* RFC UUID with trailing newline */ - if (buffer[36] != '\n') - return -EINVAL; - - /* fall through */ - case 36: /* RFC UUID without trailing newline */ - if (f == ID128_PLAIN) - return -EINVAL; - - buffer[36] = 0; - break; - - default: - return -EINVAL; - } - - return sd_id128_from_string(buffer, ret); -} - -int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - return id128_read_fd(fd, f, ret); -} - -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { - char buffer[36 + 2]; - size_t sz; - int r; - - assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); - - if (f != ID128_UUID) { - sd_id128_to_string(id, buffer); - buffer[32] = '\n'; - sz = 33; - } else { - id128_to_uuid_string(id, buffer); - buffer[36] = '\n'; - sz = 37; - } - - r = loop_write(fd, buffer, sz, false); - if (r < 0) - return r; - - if (do_sync) { - if (fsync(fd) < 0) - return -errno; - } - - return r; -} - -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd < 0) - return -errno; - - return id128_write_fd(fd, f, id, do_sync); -} diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h deleted file mode 100644 index 3ba59acbca..0000000000 --- a/src/libsystemd/sd-id128/id128-util.h +++ /dev/null @@ -1,45 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-id128.h" -#include "macro.h" - -char *id128_to_uuid_string(sd_id128_t id, char s[37]); - -/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */ -#define ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" - -bool id128_is_valid(const char *s) _pure_; - -typedef enum Id128Format { - ID128_ANY, - ID128_PLAIN, /* formatted as 32 hex chars as-is */ - ID128_UUID, /* formatted as 36 character uuid string */ - _ID128_FORMAT_MAX, -} Id128Format; - -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret); -int id128_read(const char *p, Id128Format f, sd_id128_t *ret); - -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c deleted file mode 100644 index 9f47d04e61..0000000000 --- a/src/libsystemd/sd-id128/sd-id128.c +++ /dev/null @@ -1,161 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-id128.h" - -#include "fd-util.h" -#include "hexdecoct.h" -#include "id128-util.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; - - assert_return(s, NULL); - - for (n = 0; n < 16; n++) { - s[n*2] = hexchar(id.bytes[n] >> 4); - s[n*2+1] = hexchar(id.bytes[n] & 0xF); - } - - s[32] = 0; - - return s; -} - -_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { - unsigned n, i; - sd_id128_t t; - bool is_guid = false; - - assert_return(s, -EINVAL); - - for (n = 0, i = 0; n < 16;) { - int a, b; - - if (s[i] == '-') { - /* Is this a GUID? Then be nice, and skip over - * the dashes */ - - if (i == 8) - is_guid = true; - else if (i == 13 || i == 18 || i == 23) { - if (!is_guid) - return -EINVAL; - } else - return -EINVAL; - - i++; - continue; - } - - a = unhexchar(s[i++]); - if (a < 0) - return -EINVAL; - - b = unhexchar(s[i++]); - if (b < 0) - return -EINVAL; - - t.bytes[n++] = (a << 4) | b; - } - - if (i != (is_guid ? 36 : 32)) - return -EINVAL; - - if (s[i] != 0) - return -EINVAL; - - if (ret) - *ret = t; - return 0; -} - -_public_ int sd_id128_get_machine(sd_id128_t *ret) { - static thread_local sd_id128_t saved_machine_id = {}; - int r; - - assert_return(ret, -EINVAL); - - if (sd_id128_is_null(saved_machine_id)) { - r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); - if (r < 0) - return r; - - if (sd_id128_is_null(saved_machine_id)) - return -EINVAL; - } - - *ret = saved_machine_id; - return 0; -} - -_public_ int sd_id128_get_boot(sd_id128_t *ret) { - static thread_local sd_id128_t saved_boot_id = {}; - int r; - - assert_return(ret, -EINVAL); - - if (sd_id128_is_null(saved_boot_id)) { - r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id); - if (r < 0) - return r; - } - - *ret = saved_boot_id; - return 0; -} - -static sd_id128_t make_v4_uuid(sd_id128_t id) { - /* Stolen from generate_random_uuid() of drivers/char/random.c - * in the kernel sources */ - - /* Set UUID version to 4 --- truly random generation */ - id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; - - /* Set the UUID variant to DCE */ - id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; - - return id; -} - -_public_ int sd_id128_randomize(sd_id128_t *ret) { - sd_id128_t t; - int r; - - assert_return(ret, -EINVAL); - - r = dev_urandom(&t, sizeof(t)); - if (r < 0) - return r; - - /* Turn this into a valid v4 UUID, to be nice. Note that we - * only guarantee this for newly generated UUIDs, not for - * pre-existing ones. */ - - *ret = make_v4_uuid(t); - return 0; -} diff --git a/src/libsystemd/sd-journal.xml b/src/libsystemd/sd-journal.xml new file mode 100644 index 0000000000..09747a480c --- /dev/null +++ b/src/libsystemd/sd-journal.xml @@ -0,0 +1,133 @@ + + + + + + + + + sd-journal + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-journal + 3 + + + + sd-journal + APIs for submitting and querying log entries to and + from the journal + + + + + #include <systemd/sd-journal.h> + + + + pkg-config --cflags --libs libsystemd + + + + + + Description + + sd-journal.h provides APIs to submit + and query log entries. The APIs exposed act both as client for the + systemd-journald.service8 + journal service and as parser for the journal files on disk. + + + See + sd_journal_print3, + sd_journal_stream_fd3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_realtime_usec3, + sd_journal_add_match3, + sd_journal_seek_head3, + sd_journal_query_enumerate3, + sd_journal_enumerate_fields3, + sd_journal_get_cursor3, + sd_journal_get_cutoff_realtime_usec3, + sd_journal_get_cutoff_monotonic_usec3, + sd_journal_get_usage3, + sd_journal_get_catalog3, + sd_journal_get_fd3, + sd_journal_has_runtime_files3 + and + sd_journal_has_persistent_files3 + for more information about the functions implemented. + + Command line access for submitting entries to the journal is + available with the + systemd-cat1 + tool. Command line access for querying entries from the journal is + available with the + journalctl1 + tool. + + + + + + See Also + + systemd1, + sd_journal_print3, + sd_journal_stream_fd3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_data3, + sd_journal_get_realtime_usec3, + sd_journal_add_match3, + sd_journal_seek_head3, + sd_journal_query_enumerate3, + sd_journal_enumerate_fields3, + sd_journal_get_cursor3, + sd_journal_get_cutoff_realtime_usec3, + sd_journal_get_cutoff_monotonic_usec3, + sd_journal_get_usage3, + sd_journal_get_fd3, + sd_journal_query_unique3, + sd_journal_get_catalog3, + sd_journal_has_runtime_files3, + sd_journal_has_persistent_files3, + journalctl1, + sd-id1283, + pkg-config1 + + + + diff --git a/src/libsystemd/sd-login.xml b/src/libsystemd/sd-login.xml new file mode 100644 index 0000000000..328f71164d --- /dev/null +++ b/src/libsystemd/sd-login.xml @@ -0,0 +1,135 @@ + + + + + + + + + sd-login + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-login + 3 + + + + sd-login + APIs for + tracking logins + + + + + #include <systemd/sd-login.h> + + + + pkg-config --cflags --libs libsystemd + + + + + Description + + sd-login.h provides APIs to introspect + and monitor seat, login session and user status information on the + local system. + + See Multi-Seat + on Linux for an introduction into multi-seat support on + Linux, the background for this set of APIs. + + Note that these APIs only allow purely passive access and + monitoring of seats, sessions and users. To actively make changes + to the seat configuration, terminate login sessions, or switch + session on a seat you need to utilize the D-Bus API of + systemd-logind, instead. + + These functions synchronously access data in + /proc, /sys/fs/cgroup + and /run. All of these are virtual file + systems, hence the runtime cost of the accesses is relatively + cheap. + + It is possible (and often a very good choice) to mix calls + to the synchronous interface of sd-login.h + with the asynchronous D-Bus interface of systemd-logind. However, + if this is done you need to think a bit about possible races since + the stream of events from D-Bus and from + sd-login.h interfaces such as the login + monitor are asynchronous and not ordered against each + other. + + If the functions return string arrays, these are generally + NULL terminated and need to be freed by the + caller with the libc + free3 + call after use, including the strings referenced therein. + Similarly, individual strings returned need to be freed, as + well. + + As a special exception, instead of an empty string array + NULL may be returned, which should be treated + equivalent to an empty string array. + + See + sd_pid_get_session3, + sd_uid_get_state3, + sd_session_is_active3, + sd_seat_get_active3, + sd_get_seats3, + sd_login_monitor_new3 + for more information about the functions + implemented. + + + + + + See Also + + systemd1, + sd_pid_get_session3, + sd_uid_get_state3, + sd_session_is_active3, + sd_seat_get_active3, + sd_get_seats3, + sd_login_monitor_new3, + sd-daemon3, + pkg-config1 + + + + diff --git a/src/libsystemd/sd-login/Makefile b/src/libsystemd/sd-login/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-login/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c deleted file mode 100644 index 3fcefada3f..0000000000 --- a/src/libsystemd/sd-login/sd-login.c +++ /dev/null @@ -1,1062 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-login.h" - -#include "alloc-util.h" -#include "cgroup-util.h" -#include "dirent-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "fs-util.h" -#include "hostname-util.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: - * - * invalid input parameters → -EINVAL - * invalid fd → -EBADF - * process does not exist → -ESRCH - * cgroup does not exist → -ENOENT - * machine, session does not exist → -ENXIO - * requested metadata on object is missing → -ENODATA - */ - -_public_ int sd_pid_get_session(pid_t pid, char **session) { - - assert_return(pid >= 0, -EINVAL); - assert_return(session, -EINVAL); - - return cg_pid_get_session(pid, session); -} - -_public_ int sd_pid_get_unit(pid_t pid, char **unit) { - - assert_return(pid >= 0, -EINVAL); - assert_return(unit, -EINVAL); - - return cg_pid_get_unit(pid, unit); -} - -_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) { - - assert_return(pid >= 0, -EINVAL); - assert_return(unit, -EINVAL); - - return cg_pid_get_user_unit(pid, unit); -} - -_public_ int sd_pid_get_machine_name(pid_t pid, char **name) { - - assert_return(pid >= 0, -EINVAL); - assert_return(name, -EINVAL); - - return cg_pid_get_machine_name(pid, name); -} - -_public_ int sd_pid_get_slice(pid_t pid, char **slice) { - - assert_return(pid >= 0, -EINVAL); - assert_return(slice, -EINVAL); - - return cg_pid_get_slice(pid, slice); -} - -_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { - - assert_return(pid >= 0, -EINVAL); - assert_return(slice, -EINVAL); - - return cg_pid_get_user_slice(pid, slice); -} - -_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { - - assert_return(pid >= 0, -EINVAL); - assert_return(uid, -EINVAL); - - return cg_pid_get_owner_uid(pid, uid); -} - -_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { - char *c; - int r; - - assert_return(pid >= 0, -EINVAL); - assert_return(cgroup, -EINVAL); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c); - if (r < 0) - return r; - - /* The internal APIs return the empty string for the root - * cgroup, let's return the "/" in the public APIs instead, as - * that's easier and less ambiguous for people to grok. */ - if (isempty(c)) { - free(c); - c = strdup("/"); - if (!c) - return -ENOMEM; - - } - - *cgroup = c; - return 0; -} - -_public_ int sd_peer_get_session(int fd, char **session) { - struct ucred ucred = {}; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(session, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_session(ucred.pid, session); -} - -_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(uid, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_owner_uid(ucred.pid, uid); -} - -_public_ int sd_peer_get_unit(int fd, char **unit) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(unit, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_unit(ucred.pid, unit); -} - -_public_ int sd_peer_get_user_unit(int fd, char **unit) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(unit, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_user_unit(ucred.pid, unit); -} - -_public_ int sd_peer_get_machine_name(int fd, char **machine) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(machine, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_machine_name(ucred.pid, machine); -} - -_public_ int sd_peer_get_slice(int fd, char **slice) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(slice, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_slice(ucred.pid, slice); -} - -_public_ int sd_peer_get_user_slice(int fd, char **slice) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(slice, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return cg_pid_get_user_slice(ucred.pid, slice); -} - -_public_ int sd_peer_get_cgroup(int fd, char **cgroup) { - struct ucred ucred; - int r; - - assert_return(fd >= 0, -EBADF); - assert_return(cgroup, -EINVAL); - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - return sd_pid_get_cgroup(ucred.pid, cgroup); -} - -static int file_of_uid(uid_t uid, char **p) { - - assert_return(uid_is_valid(uid), -EINVAL); - assert(p); - - if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0) - return -ENOMEM; - - return 0; -} - -_public_ int sd_uid_get_state(uid_t uid, char**state) { - _cleanup_free_ char *p = NULL; - char *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); - if (r == -ENOENT) { - free(s); - s = strdup("offline"); - if (!s) - return -ENOMEM; - - } - if (r < 0) { - free(s); - return r; - } - if (isempty(s)) { - free(s); - return -EIO; - } - - *state = s; - return 0; -} - -_public_ int sd_uid_get_display(uid_t uid, char **session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(session, -EINVAL); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *session = s; - s = NULL; - - return 0; -} - -static int file_of_seat(const char *seat, char **_p) { - char *p; - int r; - - assert(_p); - - if (seat) { - if (!filename_is_valid(seat)) - return -EINVAL; - - p = strappend("/run/systemd/seats/", seat); - } else { - _cleanup_free_ char *buf = NULL; - - r = sd_session_get_seat(NULL, &buf); - if (r < 0) - return r; - - p = strappend("/run/systemd/seats/", buf); - } - - if (!p) - return -ENOMEM; - - *_p = p; - p = NULL; - return 0; -} - -_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { - _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; - size_t l; - int r; - const char *word, *variable, *state; - - assert_return(uid_is_valid(uid), -EINVAL); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - variable = require_active ? "ACTIVE_UID" : "UIDS"; - - r = parse_env_file(p, NEWLINE, variable, &s, NULL); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - if (isempty(s)) - return 0; - - if (asprintf(&t, UID_FMT, uid) < 0) - return -ENOMEM; - - FOREACH_WORD(word, l, s, state) - if (strneq(t, word, l)) - return 1; - - return 0; -} - -static int uid_get_array(uid_t uid, const char *variable, char ***array) { - _cleanup_free_ char *p = NULL, *s = NULL; - char **a; - int r; - - assert(variable); - - r = file_of_uid(uid, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, variable, &s, NULL); - if (r == -ENOENT || (r >= 0 && isempty(s))) { - if (array) - *array = NULL; - return 0; - } - if (r < 0) - return r; - - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - - strv_uniq(a); - r = strv_length(a); - - if (array) - *array = a; - else - strv_free(a); - - return r; -} - -_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) { - return uid_get_array( - uid, - require_active == 0 ? "ONLINE_SESSIONS" : - require_active > 0 ? "ACTIVE_SESSIONS" : - "SESSIONS", - sessions); -} - -_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) { - return uid_get_array( - uid, - require_active == 0 ? "ONLINE_SEATS" : - require_active > 0 ? "ACTIVE_SEATS" : - "SEATS", - seats); -} - -static int file_of_session(const char *session, char **_p) { - char *p; - int r; - - assert(_p); - - if (session) { - if (!session_id_valid(session)) - return -EINVAL; - - p = strappend("/run/systemd/sessions/", session); - } else { - _cleanup_free_ char *buf = NULL; - - r = sd_pid_get_session(0, &buf); - if (r < 0) - return r; - - p = strappend("/run/systemd/sessions/", buf); - } - - if (!p) - return -ENOMEM; - - *_p = p; - return 0; -} - -_public_ int sd_session_is_active(const char *session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - return parse_boolean(s); -} - -_public_ int sd_session_is_remote(const char *session) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - return parse_boolean(s); -} - -_public_ int sd_session_get_state(const char *session, char **state) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - *state = s; - s = NULL; - - return 0; -} - -_public_ int sd_session_get_uid(const char *session, uid_t *uid) { - int r; - _cleanup_free_ char *p = NULL, *s = NULL; - - assert_return(uid, -EINVAL); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, "UID", &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -EIO; - - return parse_uid(s, uid); -} - -static int session_get_string(const char *session, const char *field, char **value) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert_return(value, -EINVAL); - assert(field); - - r = file_of_session(session, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, field, &s, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *value = s; - s = NULL; - return 0; -} - -_public_ int sd_session_get_seat(const char *session, char **seat) { - return session_get_string(session, "SEAT", seat); -} - -_public_ int sd_session_get_tty(const char *session, char **tty) { - return session_get_string(session, "TTY", tty); -} - -_public_ int sd_session_get_vt(const char *session, unsigned *vtnr) { - _cleanup_free_ char *vtnr_string = NULL; - unsigned u; - int r; - - assert_return(vtnr, -EINVAL); - - r = session_get_string(session, "VTNR", &vtnr_string); - if (r < 0) - return r; - - r = safe_atou(vtnr_string, &u); - if (r < 0) - return r; - - *vtnr = u; - return 0; -} - -_public_ int sd_session_get_service(const char *session, char **service) { - return session_get_string(session, "SERVICE", service); -} - -_public_ int sd_session_get_type(const char *session, char **type) { - return session_get_string(session, "TYPE", type); -} - -_public_ int sd_session_get_class(const char *session, char **class) { - return session_get_string(session, "CLASS", class); -} - -_public_ int sd_session_get_desktop(const char *session, char **desktop) { - _cleanup_free_ char *escaped = NULL; - char *t; - int r; - - assert_return(desktop, -EINVAL); - - r = session_get_string(session, "DESKTOP", &escaped); - if (r < 0) - return r; - - r = cunescape(escaped, 0, &t); - if (r < 0) - return r; - - *desktop = t; - return 0; -} - -_public_ int sd_session_get_display(const char *session, char **display) { - return session_get_string(session, "DISPLAY", display); -} - -_public_ int sd_session_get_remote_user(const char *session, char **remote_user) { - return session_get_string(session, "REMOTE_USER", remote_user); -} - -_public_ int sd_session_get_remote_host(const char *session, char **remote_host) { - return session_get_string(session, "REMOTE_HOST", remote_host); -} - -_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { - _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; - int r; - - assert_return(session || uid, -EINVAL); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - "ACTIVE", &s, - "ACTIVE_UID", &t, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - - if (session && !s) - return -ENODATA; - - if (uid && !t) - return -ENODATA; - - if (uid && t) { - r = parse_uid(t, uid); - if (r < 0) - return r; - } - - if (session && s) { - *session = s; - s = NULL; - } - - return 0; -} - -_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) { - _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ uid_t *b = NULL; - unsigned n = 0; - int r; - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - "SESSIONS", &s, - "ACTIVE_SESSIONS", &t, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - - if (s) { - a = strv_split(s, " "); - if (!a) - return -ENOMEM; - } - - if (uids && t) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, t, state) - n++; - - if (n > 0) { - unsigned i = 0; - - b = new(uid_t, n); - if (!b) - return -ENOMEM; - - FOREACH_WORD(word, l, t, state) { - _cleanup_free_ char *k = NULL; - - k = strndup(word, l); - if (!k) - return -ENOMEM; - - r = parse_uid(k, b + i); - if (r < 0) - continue; - - i++; - } - } - } - - r = strv_length(a); - - if (sessions) { - *sessions = a; - a = NULL; - } - - if (uids) { - *uids = b; - b = NULL; - } - - if (n_uids) - *n_uids = n; - - return r; -} - -static int seat_get_can(const char *seat, const char *variable) { - _cleanup_free_ char *p = NULL, *s = NULL; - int r; - - assert(variable); - - r = file_of_seat(seat, &p); - if (r < 0) - return r; - - r = parse_env_file(p, NEWLINE, - variable, &s, - NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - return parse_boolean(s); -} - -_public_ int sd_seat_can_multi_session(const char *seat) { - return seat_get_can(seat, "CAN_MULTI_SESSION"); -} - -_public_ int sd_seat_can_tty(const char *seat) { - return seat_get_can(seat, "CAN_TTY"); -} - -_public_ int sd_seat_can_graphical(const char *seat) { - return seat_get_can(seat, "CAN_GRAPHICAL"); -} - -_public_ int sd_get_seats(char ***seats) { - return get_files_in_directory("/run/systemd/seats/", seats); -} - -_public_ int sd_get_sessions(char ***sessions) { - return get_files_in_directory("/run/systemd/sessions/", sessions); -} - -_public_ int sd_get_uids(uid_t **users) { - _cleanup_closedir_ DIR *d; - int r = 0; - unsigned n = 0; - _cleanup_free_ uid_t *l = NULL; - - d = opendir("/run/systemd/users/"); - if (!d) - return -errno; - - for (;;) { - struct dirent *de; - int k; - uid_t uid; - - 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; - - k = parse_uid(de->d_name, &uid); - if (k < 0) - continue; - - if (users) { - if ((unsigned) r >= n) { - uid_t *t; - - n = MAX(16, 2*r); - t = realloc(l, sizeof(uid_t) * n); - if (!t) - return -ENOMEM; - - l = t; - } - - assert((unsigned) r < n); - l[r++] = uid; - } else - r++; - } - - if (users) { - *users = l; - l = NULL; - } - - return r; -} - -_public_ int sd_get_machine_names(char ***machines) { - char **l = NULL, **a, **b; - int r; - - assert_return(machines, -EINVAL); - - r = get_files_in_directory("/run/systemd/machines/", &l); - if (r < 0) - return r; - - if (l) { - r = 0; - - /* Filter out the unit: symlinks */ - for (a = l, b = l; *a; a++) { - if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) - free(*a); - else { - *b = *a; - b++; - r++; - } - } - - *b = NULL; - } - - *machines = l; - return r; -} - -_public_ int sd_machine_get_class(const char *machine, char **class) { - _cleanup_free_ char *c = NULL; - const char *p; - int r; - - assert_return(machine_name_is_valid(machine), -EINVAL); - assert_return(class, -EINVAL); - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (!c) - return -EIO; - - *class = c; - c = NULL; - - return 0; -} - -_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { - _cleanup_free_ char *netif = NULL; - size_t l, allocated = 0, nr = 0; - int *ni = NULL; - const char *p, *word, *state; - int r; - - assert_return(machine_name_is_valid(machine), -EINVAL); - assert_return(ifindices, -EINVAL); - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL); - if (r == -ENOENT) - return -ENXIO; - if (r < 0) - return r; - if (!netif) { - *ifindices = NULL; - return 0; - } - - FOREACH_WORD(word, l, netif, state) { - char buf[l+1]; - int ifi; - - *(char*) (mempcpy(buf, word, l)) = 0; - - if (parse_ifindex(buf, &ifi) < 0) - continue; - - if (!GREEDY_REALLOC(ni, allocated, nr+1)) { - free(ni); - return -ENOMEM; - } - - ni[nr++] = ifi; - } - - *ifindices = ni; - return nr; -} - -static inline int MONITOR_TO_FD(sd_login_monitor *m) { - return (int) (unsigned long) m - 1; -} - -static inline sd_login_monitor* FD_TO_MONITOR(int fd) { - return (sd_login_monitor*) (unsigned long) (fd + 1); -} - -_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) { - int fd, k; - bool good = false; - - assert_return(m, -EINVAL); - - fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (fd < 0) - return -errno; - - if (!category || streq(category, "seat")) { - k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "session")) { - k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "uid")) { - k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!category || streq(category, "machine")) { - k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE); - if (k < 0) { - safe_close(fd); - return -errno; - } - - good = true; - } - - if (!good) { - close_nointr(fd); - return -EINVAL; - } - - *m = FD_TO_MONITOR(fd); - return 0; -} - -_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { - int fd; - - if (!m) - return NULL; - - fd = MONITOR_TO_FD(m); - close_nointr(fd); - - return NULL; -} - -_public_ int sd_login_monitor_flush(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - return flush_fd(MONITOR_TO_FD(m)); -} - -_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - return MONITOR_TO_FD(m); -} - -_public_ int sd_login_monitor_get_events(sd_login_monitor *m) { - - assert_return(m, -EINVAL); - - /* For now we will only return POLLIN here, since we don't - * need anything else ever for inotify. However, let's have - * this API to keep our options open should we later on need - * it. */ - return POLLIN; -} - -_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) { - - assert_return(m, -EINVAL); - assert_return(timeout_usec, -EINVAL); - - /* For now we will only return (uint64_t) -1, since we don't - * need any timeout. However, let's have this API to keep our - * options open should we later on need it. */ - *timeout_usec = (uint64_t) -1; - return 0; -} diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c deleted file mode 100644 index c1fd7dd33e..0000000000 --- a/src/libsystemd/sd-login/test-login.c +++ /dev/null @@ -1,264 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-login.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 }; - _cleanup_free_ char *pp = NULL, *qq = NULL; - int r, k; - uid_t u, u2; - char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; - char *session; - char *state; - char *session2; - char *t; - char **seats, **sessions, **machines; - uid_t *uids; - unsigned n; - struct pollfd pollfd; - sd_login_monitor *m = NULL; - - assert_se(sd_pid_get_session(0, &session) == 0); - printf("session = %s\n", session); - - assert_se(sd_pid_get_owner_uid(0, &u2) == 0); - printf("user = "UID_FMT"\n", u2); - - assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); - printf("cgroup = %s\n", cgroup); - free(cgroup); - - display_session = NULL; - r = sd_uid_get_display(u2, &display_session); - assert_se(r >= 0 || r == -ENODATA); - printf("user's display session = %s\n", strna(display_session)); - free(display_session); - - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); - sd_peer_get_session(pair[0], &pp); - sd_peer_get_session(pair[1], &qq); - assert_se(streq_ptr(pp, qq)); - - r = sd_uid_get_sessions(u2, false, &sessions); - assert_se(r >= 0); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); - - assert_se(r == sd_uid_get_sessions(u2, false, NULL)); - - r = sd_uid_get_seats(u2, false, &seats); - assert_se(r >= 0); - assert_se(r == (int) strv_length(seats)); - assert_se(t = strv_join(seats, ", ")); - strv_free(seats); - printf("seats = %s\n", t); - free(t); - - assert_se(r == sd_uid_get_seats(u2, false, NULL)); - - r = sd_session_is_active(session); - assert_se(r >= 0); - printf("active = %s\n", yes_no(r)); - - r = sd_session_is_remote(session); - assert_se(r >= 0); - printf("remote = %s\n", yes_no(r)); - - r = sd_session_get_state(session, &state); - assert_se(r >= 0); - printf("state = %s\n", state); - free(state); - - assert_se(sd_session_get_uid(session, &u) >= 0); - printf("uid = "UID_FMT"\n", u); - assert_se(u == u2); - - assert_se(sd_session_get_type(session, &type) >= 0); - printf("type = %s\n", type); - free(type); - - assert_se(sd_session_get_class(session, &class) >= 0); - printf("class = %s\n", class); - free(class); - - display = NULL; - r = sd_session_get_display(session, &display); - assert_se(r >= 0 || r == -ENODATA); - printf("display = %s\n", strna(display)); - free(display); - - remote_user = NULL; - r = sd_session_get_remote_user(session, &remote_user); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_user = %s\n", strna(remote_user)); - free(remote_user); - - remote_host = NULL; - r = sd_session_get_remote_host(session, &remote_host); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_host = %s\n", strna(remote_host)); - free(remote_host); - - assert_se(sd_session_get_seat(session, &seat) >= 0); - printf("seat = %s\n", seat); - - r = sd_seat_can_multi_session(seat); - assert_se(r >= 0); - printf("can do multi session = %s\n", yes_no(r)); - - r = sd_seat_can_tty(seat); - assert_se(r >= 0); - printf("can do tty = %s\n", yes_no(r)); - - r = sd_seat_can_graphical(seat); - assert_se(r >= 0); - printf("can do graphical = %s\n", yes_no(r)); - - assert_se(sd_uid_get_state(u, &state) >= 0); - printf("state = %s\n", state); - - assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); - - k = sd_uid_is_on_seat(u, 1, seat); - assert_se(k >= 0); - assert_se(!!r == !!r); - - assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); - printf("session2 = %s\n", session2); - printf("uid2 = "UID_FMT"\n", u2); - - r = sd_seat_get_sessions(seat, &sessions, &uids, &n); - assert_se(r >= 0); - printf("n_sessions = %i\n", r); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); - printf("uids ="); - for (k = 0; k < (int) n; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); - - assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); - - free(session); - free(state); - free(session2); - free(seat); - - r = sd_get_seats(&seats); - assert_se(r >= 0); - assert_se(r == (int) strv_length(seats)); - assert_se(t = strv_join(seats, ", ")); - strv_free(seats); - printf("n_seats = %i\n", r); - printf("seats = %s\n", t); - free(t); - - assert_se(sd_get_seats(NULL) == r); - - r = sd_seat_get_active(NULL, &t, NULL); - assert_se(r >= 0); - printf("active session on current seat = %s\n", t); - free(t); - - r = sd_get_sessions(&sessions); - assert_se(r >= 0); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("n_sessions = %i\n", r); - printf("sessions = %s\n", t); - free(t); - - assert_se(sd_get_sessions(NULL) == r); - - r = sd_get_uids(&uids); - assert_se(r >= 0); - - printf("uids ="); - for (k = 0; k < r; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); - - printf("n_uids = %i\n", r); - assert_se(sd_get_uids(NULL) == r); - - r = sd_get_machine_names(&machines); - assert_se(r >= 0); - assert_se(r == (int) strv_length(machines)); - assert_se(t = strv_join(machines, ", ")); - strv_free(machines); - printf("n_machines = %i\n", r); - printf("machines = %s\n", t); - free(t); - - r = sd_login_monitor_new("session", &m); - assert_se(r >= 0); - - for (n = 0; n < 5; n++) { - usec_t timeout, nw; - - zero(pollfd); - assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0); - assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0); - - assert_se(sd_login_monitor_get_timeout(m, &timeout) >= 0); - - nw = now(CLOCK_MONOTONIC); - - r = poll(&pollfd, 1, - timeout == (uint64_t) -1 ? -1 : - timeout > nw ? (int) ((timeout - nw) / 1000) : - 0); - - assert_se(r >= 0); - - sd_login_monitor_flush(m); - printf("Wake!\n"); - } - - sd_login_monitor_unref(m); -} - -int main(int argc, char* argv[]) { - log_parse_environment(); - log_open(); - - test_login(); - - return 0; -} diff --git a/src/libsystemd/sd-netlink/Makefile b/src/libsystemd/sd-netlink/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-netlink/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c deleted file mode 100644 index ed9ee041ab..0000000000 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ /dev/null @@ -1,275 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Lennart Poettering - 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 . -***/ - -#include "sd-netlink.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; - - /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */ - - if (a->family == AF_INET && b->family == AF_INET6) - return -1; - if (a->family == AF_INET6 && b->family == AF_INET) - return 1; - - if (a->scope < b->scope) - return -1; - if (a->scope > b->scope) - return 1; - - if (a->metric < b->metric) - return -1; - if (a->metric > b->metric) - return 1; - - if (a->ifindex < b->ifindex) - return -1; - if (a->ifindex > b->ifindex) - return 1; - - return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); -} - -int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) { - _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; - int r; - - assert(ret); - - if (context) - rtnl = sd_netlink_ref(context); - else { - r = sd_netlink_open(&rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (m = reply; m; m = sd_netlink_message_next(m)) { - struct local_address *a; - unsigned char flags; - uint16_t type; - int ifi, family; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - if (type != RTM_NEWADDR) - continue; - - r = sd_rtnl_message_addr_get_ifindex(m, &ifi); - if (r < 0) - return r; - if (ifindex > 0 && ifi != ifindex) - continue; - - r = sd_rtnl_message_addr_get_family(m, &family); - if (r < 0) - return r; - if (af != AF_UNSPEC && af != family) - continue; - - r = sd_rtnl_message_addr_get_flags(m, &flags); - if (r < 0) - return r; - if (flags & IFA_F_DEPRECATED) - continue; - - if (!GREEDY_REALLOC0(list, n_allocated, n_list+1)) - return -ENOMEM; - - a = list + n_list; - - r = sd_rtnl_message_addr_get_scope(m, &a->scope); - if (r < 0) - return r; - - if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE)) - continue; - - switch (family) { - - case AF_INET: - r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in); - if (r < 0) { - r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in); - if (r < 0) - continue; - } - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6); - if (r < 0) { - r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6); - if (r < 0) - continue; - } - break; - - default: - continue; - } - - a->ifindex = ifi; - a->family = family; - - n_list++; - }; - - qsort_safe(list, n_list, sizeof(struct local_address), address_compare); - - *ret = list; - list = NULL; - - return (int) n_list; -} - -int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) { - _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; - int r; - - assert(ret); - - if (context) - rtnl = sd_netlink_ref(context); - else { - r = sd_netlink_open(&rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (m = reply; m; m = sd_netlink_message_next(m)) { - struct local_address *a; - uint16_t type; - unsigned char dst_len, src_len; - uint32_t ifi; - int family; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - if (type != RTM_NEWROUTE) - continue; - - /* We only care for default routes */ - r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); - if (r < 0) - return r; - if (dst_len != 0) - continue; - - r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); - if (r < 0) - return r; - if (src_len != 0) - continue; - - r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); - if (r < 0) - return r; - if (ifindex > 0 && (int) ifi != ifindex) - continue; - - r = sd_rtnl_message_route_get_family(m, &family); - if (r < 0) - return r; - if (af != AF_UNSPEC && af != family) - continue; - - if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) - return -ENOMEM; - - a = list + n_list; - - switch (family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); - if (r < 0) - continue; - - break; - case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); - if (r < 0) - continue; - - break; - default: - continue; - } - - sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric); - - a->ifindex = ifi; - a->family = family; - - n_list++; - } - - if (n_list > 0) - qsort(list, n_list, sizeof(struct local_address), address_compare); - - *ret = list; - list = NULL; - - return (int) n_list; -} diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h deleted file mode 100644 index 18d71e797e..0000000000 --- a/src/libsystemd/sd-netlink/local-addresses.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2008-2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include "sd-netlink.h" - -#include "in-addr-util.h" - -struct local_address { - int family, ifindex; - unsigned char scope; - uint32_t metric; - union in_addr_union address; -}; - -int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); - -int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h deleted file mode 100644 index dcfb080ad3..0000000000 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#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)) - -#define RTNL_WQUEUE_MAX 1024 -#define RTNL_RQUEUE_MAX 64*1024 - -#define RTNL_CONTAINER_DEPTH 32 - -struct reply_callback { - sd_netlink_message_handler_t callback; - void *userdata; - usec_t timeout; - uint64_t serial; - unsigned prioq_idx; -}; - -struct match_callback { - sd_netlink_message_handler_t callback; - uint16_t type; - void *userdata; - - LIST_FIELDS(struct match_callback, match_callbacks); -}; - -struct sd_netlink { - RefCount n_ref; - - int fd; - - union { - struct sockaddr sa; - 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; - - sd_netlink_message **rqueue_partial; - unsigned rqueue_partial_size; - size_t rqueue_partial_allocated; - - struct nlmsghdr *rbuffer; - size_t rbuffer_allocated; - - bool processing:1; - - uint32_t serial; - - struct Prioq *reply_callbacks_prioq; - Hashmap *reply_callbacks; - - LIST_HEAD(struct match_callback, match_callbacks); - - pid_t original_pid; - - sd_event_source *io_event_source; - sd_event_source *time_event_source; - sd_event_source *exit_event_source; - sd_event *event; -}; - -struct netlink_attribute { - size_t offset; /* offset from hdr to attribute */ - bool nested:1; - bool net_byteorder:1; -}; - -struct netlink_container { - const struct NLTypeSystem *type_system; /* the type system of the container */ - size_t offset; /* offset from hdr to the start of the container */ - struct netlink_attribute *attributes; - unsigned short n_attributes; /* number of attributes in container */ -}; - -struct sd_netlink_message { - RefCount n_ref; - - sd_netlink *rtnl; - - struct nlmsghdr *hdr; - struct netlink_container containers[RTNL_CONTAINER_DEPTH]; - unsigned n_containers; /* number of containers */ - bool sealed:1; - bool broadcast:1; - - sd_netlink_message *next; /* next in a chain of multi-part messages */ -}; - -int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type); -int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); - -int socket_open(int family); -int socket_bind(sd_netlink *nl); -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); - -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 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 deleted file mode 100644 index df3b3c922e..0000000000 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ /dev/null @@ -1,963 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "formats-util.h" -#include "missing.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; - -#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) -#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) - -int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { - sd_netlink_message *m; - - assert_return(ret, -EINVAL); - - /* Note that 'rtnl' is currently unused, if we start using it internally - we must take care to avoid problems due to mutual references between - buses and their queued messages. See sd-bus. - */ - - m = new0(sd_netlink_message, 1); - if (!m) - return -ENOMEM; - - m->n_ref = REFCNT_INIT; - - m->sealed = false; - - *ret = m; - - return 0; -} - -int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - const NLType *nl_type; - size_t size; - int r; - - r = type_system_get_type(&type_system_root, &nl_type, type); - if (r < 0) - return r; - - if (type_get_type(nl_type) != NETLINK_TYPE_NESTED) - return -EINVAL; - - r = message_new_empty(rtnl, &m); - if (r < 0) - return r; - - size = NLMSG_SPACE(type_get_size(nl_type)); - - assert(size >= sizeof(struct nlmsghdr)); - m->hdr = malloc0(size); - if (!m->hdr) - return -ENOMEM; - - m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - - type_get_type_system(nl_type, &m->containers[0].type_system); - m->hdr->nlmsg_len = size; - m->hdr->nlmsg_type = type; - - *ret = m; - m = NULL; - - return 0; -} - -int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(m->hdr->nlmsg_type == RTM_GETLINK || - m->hdr->nlmsg_type == RTM_GETADDR || - m->hdr->nlmsg_type == RTM_GETROUTE || - m->hdr->nlmsg_type == RTM_GETNEIGH, - -EINVAL); - - SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); - - return 0; -} - -sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) { - if (m) - assert_se(REFCNT_INC(m->n_ref) >= 2); - - return m; -} - -sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { - sd_netlink_message *t; - - while (m && REFCNT_DEC(m->n_ref) == 0) { - unsigned i; - - free(m->hdr); - - for (i = 0; i <= m->n_containers; i++) - free(m->containers[i].attributes); - - t = m; - m = m->next; - free(t); - } - - return NULL; -} - -int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { - assert_return(m, -EINVAL); - assert_return(type, -EINVAL); - - *type = m->hdr->nlmsg_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); - - return m->broadcast; -} - -/* If successful the updated message will be correctly aligned, if - unsuccessful the old message is untouched. */ -static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { - uint32_t rta_length; - size_t message_length, padding_length; - struct nlmsghdr *new_hdr; - struct rtattr *rta; - char *padding; - unsigned i; - int offset; - - assert(m); - assert(m->hdr); - assert(!m->sealed); - assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); - assert(!data || data_length); - - /* get offset of the new attribute */ - offset = m->hdr->nlmsg_len; - - /* get the size of the new rta attribute (with padding at the end) */ - rta_length = RTA_LENGTH(data_length); - - /* get the new message size (with padding at the end) */ - message_length = offset + RTA_ALIGN(rta_length); - - /* realloc to fit the new attribute */ - new_hdr = realloc(m->hdr, message_length); - if (!new_hdr) - return -ENOMEM; - m->hdr = new_hdr; - - /* get pointer to the attribute we are about to add */ - rta = (struct rtattr *) ((uint8_t *) m->hdr + offset); - - /* if we are inside containers, extend them */ - for (i = 0; i < m->n_containers; i++) - GET_CONTAINER(m, i)->rta_len += message_length - offset; - - /* fill in the attribute */ - rta->rta_type = type; - rta->rta_len = rta_length; - if (data) - /* we don't deal with the case where the user lies about the type - * and gives us too little data (so don't do that) - */ - padding = mempcpy(RTA_DATA(rta), data, data_length); - - else - /* if no data was passed, make sure we still initialize the padding - note that we can have data_length > 0 (used by some containers) */ - padding = RTA_DATA(rta); - - /* make sure also the padding at the end of the message is initialized */ - padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; - memzero(padding, padding_length); - - /* update message size */ - m->hdr->nlmsg_len = message_length; - - return offset; -} - -static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) { - const NLType *type; - int r; - - assert(m); - - r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type); - if (r < 0) - return r; - - if (type_get_type(type) != data_type) - return -EINVAL; - - if (out_size) - *out_size = type_get_size(type); - return 0; -} - -int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { - size_t length, size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); - if (r < 0) - return r; - - if (size) { - length = strnlen(data, size+1); - if (length > size) - return -EINVAL; - } else - length = strlen(data); - - r = add_rtattr(m, type, data, length + 1); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); - if (r < 0) - return r; - - r = add_rtattr(m, type, NULL, 0); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint8_t)); - if (r < 0) - return r; - - return 0; -} - - -int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint16_t)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); - if (r < 0) - return r; - - r = add_rtattr(m, type, &data, sizeof(uint32_t)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = add_rtattr(m, type, data, len); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct in_addr)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct in6_addr)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, ETH_ALEN); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(info, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); - if (r < 0) - return r; - - r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); - if (r < 0) - return r; - - return 0; -} - -int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); - if (r < 0) { - const NLTypeSystemUnion *type_system_union; - int family; - - r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); - if (r < 0) - return r; - - r = sd_rtnl_message_get_family(m, &family); - if (r < 0) - return r; - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); - if (r < 0) - return r; - - r = type_system_union_protocol_get_type_system(type_system_union, - &m->containers[m->n_containers + 1].type_system, - family); - if (r < 0) - return r; - } else { - r = type_system_get_type_system(m->containers[m->n_containers].type_system, - &m->containers[m->n_containers + 1].type_system, - type); - if (r < 0) - return r; - } - - r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); - if (r < 0) - return r; - - m->containers[m->n_containers++].offset = r; - - return 0; -} - -int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { - const NLTypeSystemUnion *type_system_union; - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); - if (r < 0) - return r; - - r = type_system_union_get_type_system(type_system_union, - &m->containers[m->n_containers + 1].type_system, - key); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, type_system_union->match, key); - if (r < 0) - return r; - - /* do we evere need non-null size */ - r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); - if (r < 0) - return r; - - m->containers[m->n_containers++].offset = r; - - return 0; -} - - -int sd_netlink_message_close_container(sd_netlink_message *m) { - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers > 0, -EINVAL); - - m->containers[m->n_containers].type_system = NULL; - m->n_containers--; - - return 0; -} - -static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) { - struct netlink_attribute *attribute; - struct rtattr *rta; - - assert_return(m, -EINVAL); - assert_return(m->sealed, -EPERM); - assert_return(data, -EINVAL); - assert(m->n_containers < RTNL_CONTAINER_DEPTH); - assert(m->containers[m->n_containers].attributes); - assert(type < m->containers[m->n_containers].n_attributes); - - attribute = &m->containers[m->n_containers].attributes[type]; - - if (!attribute->offset) - return -ENODATA; - - rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); - - *data = RTA_DATA(rta); - - if (net_byteorder) - *net_byteorder = attribute->net_byteorder; - - return RTA_PAYLOAD(rta); -} - -int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if (strnlen(attr_data, r) >= (size_t) r) - return -EIO; - - if (data) - *data = (const char *) attr_data; - - return 0; -} - -int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t) r < sizeof(uint8_t)) - return -EIO; - - if (data) - *data = *(uint8_t *) attr_data; - - return 0; -} - -int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { - void *attr_data; - bool net_byteorder; - int r; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); - if (r < 0) - return r; - else if ((size_t) r < sizeof(uint16_t)) - return -EIO; - - if (data) { - if (net_byteorder) - *data = be16toh(*(uint16_t *) attr_data); - else - *data = *(uint16_t *) attr_data; - } - - return 0; -} - -int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { - void *attr_data; - bool net_byteorder; - int r; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); - if (r < 0) - return r; - else if ((size_t)r < sizeof(uint32_t)) - return -EIO; - - if (data) { - if (net_byteorder) - *data = be32toh(*(uint32_t *) attr_data); - else - *data = *(uint32_t *) attr_data; - } - - return 0; -} - -int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct ether_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct ether_addr)); - - return 0; -} - -int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct ifa_cacheinfo)) - return -EIO; - - if (info) - memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); - - return 0; -} - -int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct in_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct in_addr)); - - return 0; -} - -int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { - int r; - void *attr_data; - - assert_return(m, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); - if (r < 0) - return r; - - r = netlink_message_read_internal(m, type, &attr_data, NULL); - if (r < 0) - return r; - else if ((size_t)r < sizeof(struct in6_addr)) - return -EIO; - - if (data) - memcpy(data, attr_data, sizeof(struct in6_addr)); - - return 0; -} - -static int netlink_container_parse(sd_netlink_message *m, - struct netlink_container *container, - int count, - struct rtattr *rta, - unsigned int rt_len) { - _cleanup_free_ struct netlink_attribute *attributes = NULL; - - attributes = new0(struct netlink_attribute, count); - if (!attributes) - return -ENOMEM; - - for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { - unsigned short type; - - type = RTA_TYPE(rta); - - /* if the kernel is newer than the headers we used - when building, we ignore out-of-range attributes */ - if (type >= count) - continue; - - if (attributes[type].offset) - log_debug("rtnl: message parse - overwriting repeated attribute"); - - attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; - attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED; - attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; - } - - container->attributes = attributes; - attributes = NULL; - container->n_attributes = count; - - return 0; -} - -int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) { - const NLType *nl_type; - const NLTypeSystem *type_system; - void *container; - uint16_t type; - size_t size; - int r; - - assert_return(m, -EINVAL); - assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL); - - r = type_system_get_type(m->containers[m->n_containers].type_system, - &nl_type, - type_id); - if (r < 0) - return r; - - type = type_get_type(nl_type); - - if (type == NETLINK_TYPE_NESTED) { - r = type_system_get_type_system(m->containers[m->n_containers].type_system, - &type_system, - type_id); - if (r < 0) - return r; - } else if (type == NETLINK_TYPE_UNION) { - const NLTypeSystemUnion *type_system_union; - - r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, - &type_system_union, - type_id); - if (r < 0) - return r; - - switch (type_system_union->match_type) { - case NL_MATCH_SIBLING: - { - const char *key; - - r = sd_netlink_message_read_string(m, type_system_union->match, &key); - if (r < 0) - return r; - - r = type_system_union_get_type_system(type_system_union, - &type_system, - key); - if (r < 0) - return r; - - break; - } - case NL_MATCH_PROTOCOL: - { - int family; - - r = sd_rtnl_message_get_family(m, &family); - if (r < 0) - return r; - - r = type_system_union_protocol_get_type_system(type_system_union, - &type_system, - family); - if (r < 0) - return r; - - break; - } - default: - assert_not_reached("sd-netlink: invalid type system union type"); - } - } else - return -EINVAL; - - r = netlink_message_read_internal(m, type_id, &container, NULL); - if (r < 0) - return r; - else - size = (size_t)r; - - m->n_containers++; - - r = netlink_container_parse(m, - &m->containers[m->n_containers], - type_system_get_count(type_system), - container, - size); - if (r < 0) { - m->n_containers--; - return r; - } - - m->containers[m->n_containers].type_system = type_system; - - return 0; -} - -int sd_netlink_message_exit_container(sd_netlink_message *m) { - assert_return(m, -EINVAL); - assert_return(m->sealed, -EINVAL); - assert_return(m->n_containers > 0, -EINVAL); - - m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); - m->containers[m->n_containers].type_system = NULL; - - m->n_containers--; - - return 0; -} - -uint32_t rtnl_message_get_serial(sd_netlink_message *m) { - assert(m); - assert(m->hdr); - - return m->hdr->nlmsg_seq; -} - -int sd_netlink_message_is_error(sd_netlink_message *m) { - assert_return(m, 0); - assert_return(m->hdr, 0); - - return m->hdr->nlmsg_type == NLMSG_ERROR; -} - -int sd_netlink_message_get_errno(sd_netlink_message *m) { - struct nlmsgerr *err; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - - if (!sd_netlink_message_is_error(m)) - return 0; - - err = NLMSG_DATA(m->hdr); - - return err->error; -} - -int sd_netlink_message_rewind(sd_netlink_message *m) { - const NLType *nl_type; - uint16_t type; - size_t size; - unsigned i; - int r; - - assert_return(m, -EINVAL); - - /* don't allow appending to message once parsed */ - if (!m->sealed) - rtnl_message_seal(m); - - for (i = 1; i <= m->n_containers; i++) - m->containers[i].attributes = mfree(m->containers[i].attributes); - - m->n_containers = 0; - - if (m->containers[0].attributes) - /* top-level attributes have already been parsed */ - return 0; - - assert(m->hdr); - - r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type); - if (r < 0) - return r; - - type = type_get_type(nl_type); - size = type_get_size(nl_type); - - if (type == NETLINK_TYPE_NESTED) { - const NLTypeSystem *type_system; - - type_get_type_system(nl_type, &type_system); - - m->containers[0].type_system = type_system; - - r = netlink_container_parse(m, - &m->containers[m->n_containers], - type_system_get_count(type_system), - (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), - NLMSG_PAYLOAD(m->hdr, size)); - if (r < 0) - return r; - } - - return 0; -} - -void rtnl_message_seal(sd_netlink_message *m) { - assert(m); - assert(!m->sealed); - - m->sealed = true; -} - -sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) { - assert_return(m, NULL); - - return m->next; -} diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c deleted file mode 100644 index c165fa3359..0000000000 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ /dev/null @@ -1,474 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "formats-util.h" -#include "missing.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; - - fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family); - if (fd < 0) - return -errno; - - 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; - - r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one)); - if (r < 0) - return -errno; - - addrlen = sizeof(nl->sockaddr); - - r = bind(nl->fd, &nl->sockaddr.sa, addrlen); - /* ignore EINVAL to allow opening an already bound socket */ - if (r < 0 && errno != EINVAL) - return -errno; - - r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); - 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; -} - -static int broadcast_group_join(sd_netlink *nl, unsigned group) { - int r; - - assert(nl); - assert(nl->fd >= 0); - assert(group > 0); - - r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (r < 0) - return -errno; - - 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 { - struct sockaddr sa; - struct sockaddr_nl nl; - } addr = { - .nl.nl_family = AF_NETLINK, - }; - ssize_t k; - - assert(nl); - assert(m); - assert(m->hdr); - - k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, - 0, &addr.sa, sizeof(addr)); - if (k < 0) - return -errno; - - return k; -} - -static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { - union sockaddr_union sender; - uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))]; - struct msghdr msg = { - .msg_iov = iov, - .msg_iovlen = 1, - .msg_name = &sender, - .msg_namelen = sizeof(sender), - .msg_control = cmsg_buffer, - .msg_controllen = sizeof(cmsg_buffer), - }; - struct cmsghdr *cmsg; - uint32_t group = 0; - int r; - - assert(fd >= 0); - assert(iov); - - r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); - if (r < 0) { - /* no data */ - if (errno == ENOBUFS) - log_debug("rtnl: kernel receive buffer overrun"); - else if (errno == EAGAIN) - log_debug("rtnl: no data in socket"); - - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; - } - - if (sender.nl.nl_pid != 0) { - /* not from the kernel, ignore */ - log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid); - - if (peek) { - /* drop the message */ - r = recvmsg(fd, &msg, 0); - if (r < 0) - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; - } - - return 0; - } - - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_NETLINK && - cmsg->cmsg_type == NETLINK_PKTINFO && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { - struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); - - /* multi-cast group */ - group = pktinfo->group; - } - } - - if (_group) - *_group = group; - - return r; -} - -/* On success, the number of bytes received is returned and *ret points to the received message - * which has a valid header and the correct size. - * If nothing useful was received 0 is returned. - * On failure, a negative error code is returned. - */ -int socket_read_message(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL; - struct iovec iov = {}; - uint32_t group = 0; - bool multi_part = false, done = false; - struct nlmsghdr *new_msg; - size_t len; - int r; - unsigned i = 0; - - assert(rtnl); - assert(rtnl->rbuffer); - assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); - - /* read nothing, just get the pending message size */ - r = socket_recv_message(rtnl->fd, &iov, NULL, true); - if (r <= 0) - return r; - else - len = (size_t)r; - - /* make room for the pending message */ - if (!greedy_realloc((void **)&rtnl->rbuffer, - &rtnl->rbuffer_allocated, - len, sizeof(uint8_t))) - return -ENOMEM; - - iov.iov_base = rtnl->rbuffer; - iov.iov_len = rtnl->rbuffer_allocated; - - /* read the pending message */ - r = socket_recv_message(rtnl->fd, &iov, &group, false); - if (r <= 0) - return r; - else - len = (size_t)r; - - if (len > rtnl->rbuffer_allocated) - /* message did not fit in read buffer */ - return -EIO; - - if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { - multi_part = true; - - for (i = 0; i < rtnl->rqueue_partial_size; i++) { - if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) == - rtnl->rbuffer->nlmsg_seq) { - first = rtnl->rqueue_partial[i]; - break; - } - } - } - - for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) { - _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) - /* not broadcast and not for us */ - continue; - - if (new_msg->nlmsg_type == NLMSG_NOOP) - /* silently drop noop messages */ - continue; - - if (new_msg->nlmsg_type == NLMSG_DONE) { - /* finished reading multi-part message */ - done = true; - - /* if first is not defined, put NLMSG_DONE into the receive queue. */ - if (first) - continue; - } - - /* check that we support this message type */ - r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type); - if (r < 0) { - if (r == -EOPNOTSUPP) - log_debug("sd-netlink: ignored message with unknown type: %i", - new_msg->nlmsg_type); - - continue; - } - - /* check that the size matches the message type */ - if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) { - log_debug("sd-netlink: message larger than expected, dropping"); - continue; - } - - r = message_new_empty(rtnl, &m); - if (r < 0) - return r; - - m->broadcast = !!group; - - m->hdr = memdup(new_msg, new_msg->nlmsg_len); - if (!m->hdr) - return -ENOMEM; - - /* seal and parse the top-level message */ - r = sd_netlink_message_rewind(m); - if (r < 0) - return r; - - /* push the message onto the multi-part message stack */ - if (first) - m->next = first; - first = m; - m = NULL; - } - - if (len) - log_debug("sd-netlink: discarding %zu bytes of incoming message", len); - - if (!first) - return 0; - - if (!multi_part || done) { - /* we got a complete message, push it on the read queue */ - r = rtnl_rqueue_make_room(rtnl); - if (r < 0) - return r; - - rtnl->rqueue[rtnl->rqueue_size++] = first; - first = NULL; - - if (multi_part && (i < rtnl->rqueue_partial_size)) { - /* remove the message form the partial read queue */ - memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, - sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1)); - rtnl->rqueue_partial_size--; - } - - return 1; - } else { - /* we only got a partial multi-part message, push it on the - partial read queue */ - if (i < rtnl->rqueue_partial_size) { - rtnl->rqueue_partial[i] = first; - } else { - r = rtnl_rqueue_partial_make_room(rtnl); - if (r < 0) - return r; - - rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first; - } - first = NULL; - - return 0; - } -} diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c deleted file mode 100644 index 566a050432..0000000000 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ /dev/null @@ -1,692 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "macro.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 - -typedef enum { - BOND_ARP_TARGETS_0, - BOND_ARP_TARGETS_1, - BOND_ARP_TARGETS_2, - BOND_ARP_TARGETS_3, - BOND_ARP_TARGETS_4, - BOND_ARP_TARGETS_5, - BOND_ARP_TARGETS_6, - BOND_ARP_TARGETS_7, - BOND_ARP_TARGETS_8, - BOND_ARP_TARGETS_9, - BOND_ARP_TARGETS_10, - BOND_ARP_TARGETS_11, - BOND_ARP_TARGETS_12, - BOND_ARP_TARGETS_13, - BOND_ARP_TARGETS_14, - BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS, -} BondArpTargets; - -struct NLType { - uint16_t type; - size_t size; - const NLTypeSystem *type_system; - const NLTypeSystemUnion *type_system_union; -}; - -struct NLTypeSystem { - uint16_t count; - const NLType *types; -}; - -static const NLTypeSystem rtnl_link_type_system; - -static const NLType empty_types[1] = { - /* fake array to avoid .types==NULL, which denotes invalid type-systems */ -}; - -static const NLTypeSystem empty_type_system = { - .count = 0, - .types = empty_types, -}; - -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_MODE] = { .type = NETLINK_TYPE_U16 }, -}; - -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_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 }, - [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, - [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, -}; - -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) }, - [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, - [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, -*/ - [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, -}; - -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 }, - [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32}, - [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32}, - [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 }, - [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG }, - [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, -}; - -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 }, - [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 }, - [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_bond_arp_type_system = { - .count = ELEMENTSOF(rtnl_bond_arp_target_types), - .types = rtnl_bond_arp_target_types, -}; - -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 }, - [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system }, - [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, -}; - -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 }, - [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, -}; - -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 }, - [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, - [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, - [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, -}; - -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 }, - [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, -}; - -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 }, - [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLType rtnl_link_info_data_vrf_types[] = { - [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 }, -}; - -/* 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_BOND] = "bond", - [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", - [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", - [NL_UNION_LINK_INFO_DATA_VETH] = "veth", - [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy", - [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", - [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", - [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", - [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", - [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", - [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre", - [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap", - [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre", - [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap", - [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit", - [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti", - [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", - [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", - [NL_UNION_LINK_INFO_DATA_VRF] = "vrf", -}; - -DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); - -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), - .types = rtnl_link_info_data_bridge_types }, - [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), - .types = rtnl_link_info_data_vlan_types }, - [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), - .types = rtnl_link_info_data_veth_types }, - [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), - .types = rtnl_link_info_data_macvlan_types }, - [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), - .types = rtnl_link_info_data_ipvlan_types }, - [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), - .types = rtnl_link_info_data_vxlan_types }, - [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), - .types = rtnl_link_info_data_ipgre_types }, - [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), - .types = rtnl_link_info_data_iptun_types }, - [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), - .types = rtnl_link_info_data_ipvti_types }, - [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), - .types = rtnl_link_info_data_ip6tnl_types }, - - [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types), - .types = rtnl_link_info_data_vrf_types }, - -}; - -static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { - .num = _NL_UNION_LINK_INFO_DATA_MAX, - .lookup = nl_union_link_info_data_from_string, - .type_systems = rtnl_link_info_data_type_systems, - .match_type = NL_MATCH_SIBLING, - .match = IFLA_INFO_KIND, -}; - -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}, -/* - [IFLA_INFO_XSTATS], - [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING }, - [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED }, -*/ -}; - -static const NLTypeSystem rtnl_link_info_type_system = { - .count = ELEMENTSOF(rtnl_link_info_types), - .types = rtnl_link_info_types, -}; - -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 }, - [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 }, - [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, -}; - -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 }, -}; - -static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { - .num = AF_MAX, - .type_systems = rtnl_prot_info_type_systems, - .match_type = NL_MATCH_PROTOCOL, -}; - -static const struct NLType rtnl_af_spec_inet6_types[] = { - [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, -/* - IFLA_INET6_CONF, - IFLA_INET6_STATS, - IFLA_INET6_MCAST, - IFLA_INET6_CACHEINFO, - IFLA_INET6_ICMP6STATS, -*/ - [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 }, -}; - -static const NLTypeSystem rtnl_af_spec_inet6_type_system = { - .count = ELEMENTSOF(rtnl_af_spec_inet6_types), - .types = rtnl_af_spec_inet6_types, -}; - -static const NLType rtnl_af_spec_types[] = { - [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, -}; - -static const NLTypeSystem rtnl_af_spec_type_system = { - .count = ELEMENTSOF(rtnl_af_spec_types), - .types = rtnl_af_spec_types, -}; - -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 }, - [IFLA_MTU] = { .type = NETLINK_TYPE_U32 }, - [IFLA_LINK] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_QDISC], - [IFLA_STATS], - [IFLA_COST], - [IFLA_PRIORITY], -*/ - [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_WIRELESS], -*/ - [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union }, - [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 }, -/* - [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, -*/ - [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 }, - [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 }, - [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system }, - [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 }, - [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 }, -/* - [IFLA_NUM_VF], - [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, }, - [IFLA_STATS64], - [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED }, - [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED }, -*/ - [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_type_system }, -/* - [IFLA_VF_PORTS], - [IFLA_PORT_SELF], - [IFLA_AF_SPEC], -*/ - [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 }, - [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 }, - [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 }, - [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 }, -/* - [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, -*/ -}; - -static const NLTypeSystem rtnl_link_type_system = { - .count = ELEMENTSOF(rtnl_link_types), - .types = rtnl_link_types, -}; - -/* 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[] = { - [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, - [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, - [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ - [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) }, -/* - [IFA_ANYCAST], - [IFA_MULTICAST], -*/ - [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_address_type_system = { - .count = ELEMENTSOF(rtnl_address_types), - .types = rtnl_address_types, -}; - -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 }, - [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, - [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, - [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, - [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ -/* - [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED }, - [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, -*/ - [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */ -/* - RTA_CACHEINFO, - RTA_TABLE, - RTA_MARK, - RTA_MFC_STATS, - RTA_VIA, - RTA_NEWDST, -*/ - [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, - -}; - -static const NLTypeSystem rtnl_route_type_system = { - .count = ELEMENTSOF(rtnl_route_types), - .types = rtnl_route_types, -}; - -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) }, - [NDA_PROBES] = { .type = NETLINK_TYPE_U32 }, - [NDA_VLAN] = { .type = NETLINK_TYPE_U16 }, - [NDA_PORT] = { .type = NETLINK_TYPE_U16 }, - [NDA_VNI] = { .type = NETLINK_TYPE_U32 }, - [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 }, -}; - -static const NLTypeSystem rtnl_neigh_type_system = { - .count = ELEMENTSOF(rtnl_neigh_types), - .types = rtnl_neigh_types, -}; - -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) }, - [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, -}; - -const NLTypeSystem type_system_root = { - .count = ELEMENTSOF(rtnl_types), - .types = rtnl_types, -}; - -uint16_t type_get_type(const NLType *type) { - assert(type); - return type->type; -} - -size_t type_get_size(const NLType *type) { - assert(type); - return type->size; -} - -void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) { - assert(nl_type); - assert(ret); - assert(nl_type->type == NETLINK_TYPE_NESTED); - assert(nl_type->type_system); - - *ret = nl_type->type_system; -} - -void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) { - assert(nl_type); - assert(ret); - assert(nl_type->type == NETLINK_TYPE_UNION); - assert(nl_type->type_system_union); - - *ret = nl_type->type_system_union; -} - -uint16_t type_system_get_count(const NLTypeSystem *type_system) { - assert(type_system); - return type_system->count; -} - -int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { - const NLType *nl_type; - - assert(ret); - assert(type_system); - assert(type_system->types); - - if (type >= type_system->count) - return -EOPNOTSUPP; - - nl_type = &type_system->types[type]; - - if (nl_type->type == NETLINK_TYPE_UNSPEC) - return -EOPNOTSUPP; - - *ret = nl_type; - - return 0; -} - -int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) { - const NLType *nl_type; - int r; - - assert(ret); - - r = type_system_get_type(type_system, &nl_type, type); - if (r < 0) - return r; - - type_get_type_system(nl_type, ret); - return 0; -} - -int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) { - const NLType *nl_type; - int r; - - assert(ret); - - r = type_system_get_type(type_system, &nl_type, type); - if (r < 0) - return r; - - type_get_type_system_union(nl_type, ret); - return 0; -} - -int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) { - int type; - - assert(type_system_union); - assert(type_system_union->match_type == NL_MATCH_SIBLING); - assert(type_system_union->lookup); - assert(type_system_union->type_systems); - assert(ret); - assert(key); - - type = type_system_union->lookup(key); - if (type < 0) - return -EOPNOTSUPP; - - assert(type < type_system_union->num); - - *ret = &type_system_union->type_systems[type]; - - return 0; -} - -int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) { - const NLTypeSystem *type_system; - - assert(type_system_union); - assert(type_system_union->type_systems); - assert(type_system_union->match_type == NL_MATCH_PROTOCOL); - assert(ret); - - if (protocol >= type_system_union->num) - return -EOPNOTSUPP; - - type_system = &type_system_union->type_systems[protocol]; - if (!type_system->types) - return -EOPNOTSUPP; - - *ret = type_system; - - return 0; -} diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h deleted file mode 100644 index 7c0e598b26..0000000000 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "macro.h" - -enum { - NETLINK_TYPE_UNSPEC, - NETLINK_TYPE_U8, /* NLA_U8 */ - NETLINK_TYPE_U16, /* NLA_U16 */ - NETLINK_TYPE_U32, /* NLA_U32 */ - NETLINK_TYPE_U64, /* NLA_U64 */ - NETLINK_TYPE_STRING, /* NLA_STRING */ - NETLINK_TYPE_FLAG, /* NLA_FLAG */ - NETLINK_TYPE_IN_ADDR, - NETLINK_TYPE_ETHER_ADDR, - NETLINK_TYPE_CACHE_INFO, - NETLINK_TYPE_NESTED, /* NLA_NESTED */ - NETLINK_TYPE_UNION, -}; - -typedef enum NLMatchType { - NL_MATCH_SIBLING, - NL_MATCH_PROTOCOL, -} NLMatchType; - -typedef struct NLTypeSystemUnion NLTypeSystemUnion; -typedef struct NLTypeSystem NLTypeSystem; -typedef struct NLType NLType; - -struct NLTypeSystemUnion { - int num; - NLMatchType match_type; - uint16_t match; - int (*lookup)(const char *); - const NLTypeSystem *type_systems; -}; - -extern const NLTypeSystem type_system_root; - -uint16_t type_get_type(const NLType *type); -size_t type_get_size(const NLType *type); -void type_get_type_system(const NLType *type, const NLTypeSystem **ret); -void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret); - -uint16_t type_system_get_count(const NLTypeSystem *type_system); -int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); -int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type); -int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type); -int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key); -int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol); - -typedef enum NLUnionLinkInfoData { - NL_UNION_LINK_INFO_DATA_BOND, - NL_UNION_LINK_INFO_DATA_BRIDGE, - NL_UNION_LINK_INFO_DATA_VLAN, - NL_UNION_LINK_INFO_DATA_VETH, - NL_UNION_LINK_INFO_DATA_DUMMY, - NL_UNION_LINK_INFO_DATA_MACVLAN, - NL_UNION_LINK_INFO_DATA_MACVTAP, - NL_UNION_LINK_INFO_DATA_IPVLAN, - NL_UNION_LINK_INFO_DATA_VXLAN, - NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, - NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL, - NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL, - NL_UNION_LINK_INFO_DATA_SIT_TUNNEL, - NL_UNION_LINK_INFO_DATA_VTI_TUNNEL, - NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, - NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, - NL_UNION_LINK_INFO_DATA_VRF, - _NL_UNION_LINK_INFO_DATA_MAX, - _NL_UNION_LINK_INFO_DATA_INVALID = -1 -} NLUnionLinkInfoData; - -const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; -NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c deleted file mode 100644 index 73b9ac0258..0000000000 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include "sd-netlink.h" - -#include "netlink-internal.h" -#include "netlink-util.h" - -int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - int r; - - assert(rtnl); - assert(ifindex > 0); - assert(name); - - if (!*rtnl) { - r = sd_netlink_open(rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); - if (r < 0) - return r; - - r = sd_netlink_call(*rtnl, message, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, - const struct ether_addr *mac, unsigned mtu) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - int r; - - assert(rtnl); - assert(ifindex > 0); - - if (!alias && !mac && mtu == 0) - return 0; - - if (!*rtnl) { - r = sd_netlink_open(rtnl); - if (r < 0) - return r; - } - - r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); - if (r < 0) - return r; - - if (alias) { - r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); - if (r < 0) - return r; - } - - if (mac) { - r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac); - if (r < 0) - return r; - } - - if (mtu > 0) { - r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); - if (r < 0) - return r; - } - - r = sd_netlink_call(*rtnl, message, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) { - struct nlmsgerr *err; - int r; - - assert(error <= 0); - - r = message_new(NULL, ret, NLMSG_ERROR); - if (r < 0) - return r; - - (*ret)->hdr->nlmsg_seq = serial; - - err = NLMSG_DATA((*ret)->hdr); - - err->error = error; - - return 0; -} - -bool rtnl_message_type_is_neigh(uint16_t type) { - switch (type) { - case RTM_NEWNEIGH: - case RTM_GETNEIGH: - case RTM_DELNEIGH: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_route(uint16_t type) { - switch (type) { - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_link(uint16_t type) { - switch (type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_addr(uint16_t type) { - switch (type) { - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - return true; - default: - return false; - } -} - -int rtnl_log_parse_error(int r) { - return log_error_errno(r, "Failed to parse netlink message: %m"); -} - -int rtnl_log_create_error(int r) { - return log_error_errno(r, "Failed to create netlink message: %m"); -} diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h deleted file mode 100644 index f49bf4eaa6..0000000000 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include "sd-netlink.h" - -#include "util.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); -void rtnl_message_seal(sd_netlink_message *m); - -bool rtnl_message_type_is_link(uint16_t type); -bool rtnl_message_type_is_addr(uint16_t type); -bool rtnl_message_type_is_route(uint16_t type); -bool rtnl_message_type_is_neigh(uint16_t type); - -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); - -int rtnl_log_parse_error(int r); -int rtnl_log_create_error(int r); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c deleted file mode 100644 index 09240c7b2a..0000000000 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ /dev/null @@ -1,702 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include - -#include "sd-netlink.h" - -#include "formats-util.h" -#include "missing.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; - - 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); - - if ((rtm->rtm_family == AF_INET && prefixlen > 32) || - (rtm->rtm_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - rtm->rtm_dst_len = prefixlen; - - return 0; -} - -int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { - 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); - - if ((rtm->rtm_family == AF_INET && prefixlen > 32) || - (rtm->rtm_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - rtm->rtm_src_len = prefixlen; - - return 0; -} - -int sd_rtnl_message_route_set_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); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_scope = 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_set_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); - - rtm = NLMSG_DATA(m->hdr); - - rtm->rtm_table = table; - - return 0; -} - -int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { - 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(family, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *family = rtm->rtm_family; - - return 0; -} - -int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { - 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_family = 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; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); - assert_return(dst_len, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *dst_len = rtm->rtm_dst_len; - - return 0; -} - -int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len) { - 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(src_len, -EINVAL); - - rtm = NLMSG_DATA(m->hdr); - - *src_len = rtm->rtm_src_len; - - return 0; -} - -int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int rtm_family, - unsigned char rtm_protocol) { - struct rtmsg *rtm; - int r; - - assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL); - assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) || - rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWROUTE) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; - - rtm = NLMSG_DATA((*ret)->hdr); - - rtm->rtm_family = rtm_family; - rtm->rtm_scope = RT_SCOPE_UNIVERSE; - rtm->rtm_type = RTN_UNICAST; - rtm->rtm_table = RT_TABLE_MAIN; - rtm->rtm_protocol = rtm_protocol; - - return 0; -} - -int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - ndm->ndm_flags |= flags; - - return 0; -} - -int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - ndm->ndm_state |= state; - - return 0; -} - -int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - *flags = ndm->ndm_flags; - - return 0; -} - -int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - *state = ndm->ndm_state; - - return 0; -} - -int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - assert_return(family, -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - - *family = ndm->ndm_family; - - return 0; -} - -int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *index) { - struct ndmsg *ndm; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); - assert_return(index, -EINVAL); - - ndm = NLMSG_DATA(m->hdr); - - *index = ndm->ndm_ifindex; - - return 0; -} - -int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int ndm_family) { - struct ndmsg *ndm; - int r; - - assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); - assert_return(ndm_family == AF_INET || - ndm_family == AF_INET6 || - ndm_family == PF_BRIDGE, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWNEIGH) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; - - ndm = NLMSG_DATA((*ret)->hdr); - - ndm->ndm_family = ndm_family; - ndm->ndm_ifindex = index; - - return 0; -} - -int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(change, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_flags = flags; - ifi->ifi_change = change; - - return 0; -} - -int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_type = type; - - return 0; -} - -int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - ifi->ifi_family = family; - - return 0; -} - -int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int index) { - struct ifinfomsg *ifi; - int r; - - assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWLINK) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; - - ifi = NLMSG_DATA((*ret)->hdr); - - ifi->ifi_family = AF_UNSPEC; - ifi->ifi_index = index; - - return 0; -} - -int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - if ((ifa->ifa_family == AF_INET && prefixlen > 32) || - (ifa->ifa_family == AF_INET6 && prefixlen > 128)) - return -ERANGE; - - ifa->ifa_prefixlen = prefixlen; - - return 0; -} - -int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - ifa->ifa_flags = flags; - - return 0; -} - -int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - ifa->ifa_scope = scope; - - return 0; -} - -int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(family, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *family = ifa->ifa_family; - - return 0; -} - -int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(prefixlen, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *prefixlen = ifa->ifa_prefixlen; - - return 0; -} - -int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(scope, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *scope = ifa->ifa_scope; - - return 0; -} - -int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(flags, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *flags = ifa->ifa_flags; - - return 0; -} - -int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex) { - struct ifaddrmsg *ifa; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); - assert_return(ifindex, -EINVAL); - - ifa = NLMSG_DATA(m->hdr); - - *ifindex = ifa->ifa_index; - - return 0; -} - -int sd_rtnl_message_new_addr(sd_netlink *rtnl, sd_netlink_message **ret, - uint16_t nlmsg_type, int index, - int family) { - struct ifaddrmsg *ifa; - int r; - - assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL); - assert_return((nlmsg_type == RTM_GETADDR && index == 0) || - index > 0, -EINVAL); - assert_return((nlmsg_type == RTM_GETADDR && family == AF_UNSPEC) || - family == AF_INET || family == AF_INET6, -EINVAL); - assert_return(ret, -EINVAL); - - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_GETADDR) - (*ret)->hdr->nlmsg_flags |= NLM_F_DUMP; - - ifa = NLMSG_DATA((*ret)->hdr); - - ifa->ifa_index = index; - ifa->ifa_family = family; - if (family == AF_INET) - ifa->ifa_prefixlen = 32; - else if (family == AF_INET6) - ifa->ifa_prefixlen = 128; - - return 0; -} - -int sd_rtnl_message_new_addr_update(sd_netlink *rtnl, sd_netlink_message **ret, - int index, int family) { - int r; - - r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family); - if (r < 0) - return r; - - (*ret)->hdr->nlmsg_flags |= NLM_F_REPLACE; - - return 0; -} - -int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(ifindex, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - *ifindex = ifi->ifi_index; - - return 0; -} - -int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(flags, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - *flags = ifi->ifi_flags; - - return 0; -} - -int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type) { - struct ifinfomsg *ifi; - - assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); - assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); - assert_return(type, -EINVAL); - - ifi = NLMSG_DATA(m->hdr); - - *type = ifi->ifi_type; - - return 0; -} - -int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { - assert_return(m, -EINVAL); - assert_return(family, -EINVAL); - - assert(m->hdr); - - if (rtnl_message_type_is_link(m->hdr->nlmsg_type)) { - struct ifinfomsg *ifi; - - ifi = NLMSG_DATA(m->hdr); - - *family = ifi->ifi_family; - - return 0; - } else if (rtnl_message_type_is_route(m->hdr->nlmsg_type)) { - struct rtmsg *rtm; - - rtm = NLMSG_DATA(m->hdr); - - *family = rtm->rtm_family; - - return 0; - } else if (rtnl_message_type_is_neigh(m->hdr->nlmsg_type)) { - struct ndmsg *ndm; - - ndm = NLMSG_DATA(m->hdr); - - *family = ndm->ndm_family; - - return 0; - } else if (rtnl_message_type_is_addr(m->hdr->nlmsg_type)) { - struct ifaddrmsg *ifa; - - ifa = NLMSG_DATA(m->hdr); - - *family = ifa->ifa_family; - - return 0; - } - - return -EOPNOTSUPP; -} diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c deleted file mode 100644 index 43114eb825..0000000000 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ /dev/null @@ -1,957 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include - -#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_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - - assert_return(ret, -EINVAL); - - rtnl = new0(sd_netlink, 1); - if (!rtnl) - return -ENOMEM; - - rtnl->n_ref = REFCNT_INIT; - rtnl->fd = -1; - rtnl->sockaddr.nl.nl_family = AF_NETLINK; - rtnl->original_pid = getpid(); - - LIST_HEAD_INIT(rtnl->match_callbacks); - - /* We guarantee that the read buffer has at least space for - * a message header */ - if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated, - sizeof(struct nlmsghdr), sizeof(uint8_t))) - return -ENOMEM; - - /* Change notification responses have sequence 0, so we must - * start our request sequence numbers at 1, or we may confuse our - * responses with notifications from the kernel */ - rtnl->serial = 1; - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - socklen_t addrlen; - int r; - - assert_return(ret, -EINVAL); - - r = sd_netlink_new(&rtnl); - if (r < 0) - return r; - - addrlen = sizeof(rtnl->sockaddr); - - r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); - if (r < 0) - return -errno; - - if (rtnl->sockaddr.nl.nl_family != AF_NETLINK) - return -EINVAL; - - rtnl->fd = fd; - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -static bool rtnl_pid_changed(sd_netlink *rtnl) { - assert(rtnl); - - /* We don't support people creating an rtnl connection and - * keeping it around over a fork(). Let's complain. */ - - return rtnl->original_pid != getpid(); -} - -int sd_netlink_open_fd(sd_netlink **ret, int fd) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(fd >= 0, -EBADF); - - r = sd_netlink_new(&rtnl); - if (r < 0) - return r; - - rtnl->fd = fd; - - r = socket_bind(rtnl); - if (r < 0) { - rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ - return r; - } - - *ret = rtnl; - rtnl = NULL; - - return 0; -} - -int sd_netlink_open(sd_netlink **ret) { - _cleanup_close_ int fd = -1; - int r; - - fd = socket_open(NETLINK_ROUTE); - if (fd < 0) - return fd; - - r = sd_netlink_open_fd(ret, fd); - if (r < 0) - return r; - - fd = -1; - - return 0; -} - -int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) { - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - return fd_inc_rcvbuf(rtnl->fd, size); -} - -sd_netlink *sd_netlink_ref(sd_netlink *rtnl) { - assert_return(rtnl, NULL); - assert_return(!rtnl_pid_changed(rtnl), NULL); - - if (rtnl) - assert_se(REFCNT_INC(rtnl->n_ref) >= 2); - - return rtnl; -} - -sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { - if (!rtnl) - return NULL; - - assert_return(!rtnl_pid_changed(rtnl), NULL); - - if (REFCNT_DEC(rtnl->n_ref) == 0) { - struct match_callback *f; - unsigned i; - - for (i = 0; i < rtnl->rqueue_size; i++) - sd_netlink_message_unref(rtnl->rqueue[i]); - free(rtnl->rqueue); - - for (i = 0; i < rtnl->rqueue_partial_size; i++) - sd_netlink_message_unref(rtnl->rqueue_partial[i]); - free(rtnl->rqueue_partial); - - free(rtnl->rbuffer); - - hashmap_free_free(rtnl->reply_callbacks); - prioq_free(rtnl->reply_callbacks_prioq); - - sd_event_source_unref(rtnl->io_event_source); - sd_event_source_unref(rtnl->time_event_source); - sd_event_unref(rtnl->event); - - while ((f = rtnl->match_callbacks)) { - sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); - } - - hashmap_free(rtnl->broadcast_group_refs); - - safe_close(rtnl->fd); - free(rtnl); - } - - return NULL; -} - -static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) { - assert(rtnl); - assert(!rtnl_pid_changed(rtnl)); - assert(m); - assert(m->hdr); - - /* don't use seq == 0, as that is used for broadcasts, so we - would get confused by replies to such messages */ - m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; - - rtnl_message_seal(m); - - return; -} - -int sd_netlink_send(sd_netlink *nl, - sd_netlink_message *message, - uint32_t *serial) { - int r; - - assert_return(nl, -EINVAL); - assert_return(!rtnl_pid_changed(nl), -ECHILD); - assert_return(message, -EINVAL); - assert_return(!message->sealed, -EPERM); - - rtnl_seal_message(nl, message); - - r = socket_write_message(nl, message); - if (r < 0) - return r; - - if (serial) - *serial = rtnl_message_get_serial(message); - - return 1; -} - -int rtnl_rqueue_make_room(sd_netlink *rtnl) { - assert(rtnl); - - if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) { - log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX); - return -ENOBUFS; - } - - if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1)) - return -ENOMEM; - - return 0; -} - -int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) { - assert(rtnl); - - if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) { - log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX); - return -ENOBUFS; - } - - if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated, - rtnl->rqueue_partial_size + 1)) - return -ENOMEM; - - return 0; -} - -static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { - int r; - - assert(rtnl); - assert(message); - - if (rtnl->rqueue_size <= 0) { - /* Try to read a new message */ - r = socket_read_message(rtnl); - if (r <= 0) - return r; - } - - /* Dispatch a queued message */ - *message = rtnl->rqueue[0]; - rtnl->rqueue_size--; - memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); - - return 1; -} - -static int process_timeout(sd_netlink *rtnl) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - struct reply_callback *c; - usec_t n; - int r; - - assert(rtnl); - - c = prioq_peek(rtnl->reply_callbacks_prioq); - if (!c) - return 0; - - n = now(CLOCK_MONOTONIC); - if (c->timeout > n) - return 0; - - r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m); - if (r < 0) - return r; - - assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c); - hashmap_remove(rtnl->reply_callbacks, &c->serial); - - r = c->callback(rtnl, m, c->userdata); - if (r < 0) - log_debug_errno(r, "sd-netlink: timedout callback failed: %m"); - - free(c); - - return 1; -} - -static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) { - _cleanup_free_ struct reply_callback *c = NULL; - uint64_t serial; - uint16_t type; - int r; - - assert(rtnl); - assert(m); - - serial = rtnl_message_get_serial(m); - c = hashmap_remove(rtnl->reply_callbacks, &serial); - if (!c) - return 0; - - if (c->timeout != 0) - prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return 0; - - if (type == NLMSG_DONE) - m = NULL; - - r = c->callback(rtnl, m, c->userdata); - if (r < 0) - log_debug_errno(r, "sd-netlink: callback failed: %m"); - - return 1; -} - -static int process_match(sd_netlink *rtnl, sd_netlink_message *m) { - struct match_callback *c; - uint16_t type; - int r; - - assert(rtnl); - assert(m); - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - - LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) { - if (type == c->type) { - r = c->callback(rtnl, m, c->userdata); - if (r != 0) { - if (r < 0) - log_debug_errno(r, "sd-netlink: match callback failed: %m"); - - break; - } - } - } - - return 1; -} - -static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - assert(rtnl); - - r = process_timeout(rtnl); - if (r != 0) - goto null_message; - - r = dispatch_rqueue(rtnl, &m); - if (r < 0) - return r; - if (!m) - goto null_message; - - if (sd_netlink_message_is_broadcast(m)) { - r = process_match(rtnl, m); - if (r != 0) - goto null_message; - } else { - r = process_reply(rtnl, m); - if (r != 0) - goto null_message; - } - - if (ret) { - *ret = m; - m = NULL; - - return 1; - } - - return 1; - -null_message: - if (r >= 0 && ret) - *ret = NULL; - - return r; -} - -int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) { - NETLINK_DONT_DESTROY(rtnl); - int r; - - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - assert_return(!rtnl->processing, -EBUSY); - - rtnl->processing = true; - r = process_running(rtnl, ret); - rtnl->processing = false; - - return r; -} - -static usec_t calc_elapse(uint64_t usec) { - if (usec == (uint64_t) -1) - return 0; - - if (usec == 0) - usec = RTNL_DEFAULT_TIMEOUT; - - return now(CLOCK_MONOTONIC) + usec; -} - -static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) { - struct pollfd p[1] = {}; - struct timespec ts; - usec_t m = USEC_INFINITY; - int r, e; - - assert(rtnl); - - e = sd_netlink_get_events(rtnl); - if (e < 0) - return e; - - if (need_more) - /* Caller wants more data, and doesn't care about - * what's been read or any other timeouts. */ - e |= POLLIN; - else { - usec_t until; - /* Caller wants to process if there is something to - * process, but doesn't care otherwise */ - - r = sd_netlink_get_timeout(rtnl, &until); - if (r < 0) - return r; - if (r > 0) { - usec_t nw; - nw = now(CLOCK_MONOTONIC); - m = until > nw ? until - nw : 0; - } - } - - if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) - m = timeout_usec; - - p[0].fd = rtnl->fd; - p[0].events = e; - - r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); - if (r < 0) - return -errno; - - return r > 0 ? 1 : 0; -} - -int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) { - assert_return(nl, -EINVAL); - assert_return(!rtnl_pid_changed(nl), -ECHILD); - - if (nl->rqueue_size > 0) - return 0; - - return rtnl_poll(nl, false, timeout_usec); -} - -static int timeout_compare(const void *a, const void *b) { - const struct reply_callback *x = a, *y = b; - - if (x->timeout != 0 && y->timeout == 0) - return -1; - - if (x->timeout == 0 && y->timeout != 0) - return 1; - - if (x->timeout < y->timeout) - return -1; - - if (x->timeout > y->timeout) - return 1; - - return 0; -} - -int sd_netlink_call_async(sd_netlink *nl, - sd_netlink_message *m, - sd_netlink_message_handler_t callback, - void *userdata, - uint64_t usec, - uint32_t *serial) { - struct reply_callback *c; - uint32_t s; - int r, k; - - assert_return(nl, -EINVAL); - assert_return(m, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!rtnl_pid_changed(nl), -ECHILD); - - r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops); - if (r < 0) - return r; - - if (usec != (uint64_t) -1) { - r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare); - if (r < 0) - return r; - } - - c = new0(struct reply_callback, 1); - if (!c) - return -ENOMEM; - - c->callback = callback; - c->userdata = userdata; - c->timeout = calc_elapse(usec); - - k = sd_netlink_send(nl, m, &s); - if (k < 0) { - free(c); - return k; - } - - c->serial = s; - - r = hashmap_put(nl->reply_callbacks, &c->serial, c); - if (r < 0) { - free(c); - return r; - } - - if (c->timeout != 0) { - r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx); - if (r > 0) { - c->timeout = 0; - sd_netlink_call_async_cancel(nl, c->serial); - return r; - } - } - - if (serial) - *serial = s; - - return k; -} - -int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) { - struct reply_callback *c; - uint64_t s = serial; - - assert_return(nl, -EINVAL); - assert_return(serial != 0, -EINVAL); - assert_return(!rtnl_pid_changed(nl), -ECHILD); - - c = hashmap_remove(nl->reply_callbacks, &s); - if (!c) - return 0; - - if (c->timeout != 0) - prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx); - - free(c); - return 1; -} - -int sd_netlink_call(sd_netlink *rtnl, - sd_netlink_message *message, - uint64_t usec, - sd_netlink_message **ret) { - usec_t timeout; - uint32_t serial; - int r; - - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - assert_return(message, -EINVAL); - - r = sd_netlink_send(rtnl, message, &serial); - if (r < 0) - return r; - - timeout = calc_elapse(usec); - - for (;;) { - usec_t left; - unsigned i; - - for (i = 0; i < rtnl->rqueue_size; i++) { - uint32_t received_serial; - - received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); - - if (received_serial == serial) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL; - uint16_t type; - - incoming = rtnl->rqueue[i]; - - /* found a match, remove from rqueue and return it */ - memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, - sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1)); - rtnl->rqueue_size--; - - r = sd_netlink_message_get_errno(incoming); - if (r < 0) - return r; - - r = sd_netlink_message_get_type(incoming, &type); - if (r < 0) - return r; - - if (type == NLMSG_DONE) { - *ret = NULL; - return 0; - } - - if (ret) { - *ret = incoming; - incoming = NULL; - } - - return 1; - } - } - - r = socket_read_message(rtnl); - if (r < 0) - return r; - if (r > 0) - /* received message, so try to process straight away */ - continue; - - if (timeout > 0) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - if (n >= timeout) - return -ETIMEDOUT; - - left = timeout - n; - } else - left = (uint64_t) -1; - - r = rtnl_poll(rtnl, true, left); - if (r < 0) - return r; - else if (r == 0) - return -ETIMEDOUT; - } -} - -int sd_netlink_get_events(sd_netlink *rtnl) { - assert_return(rtnl, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - if (rtnl->rqueue_size == 0) - return POLLIN; - else - return 0; -} - -int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) { - struct reply_callback *c; - - assert_return(rtnl, -EINVAL); - assert_return(timeout_usec, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - if (rtnl->rqueue_size > 0) { - *timeout_usec = 0; - return 1; - } - - c = prioq_peek(rtnl->reply_callbacks_prioq); - if (!c) { - *timeout_usec = (uint64_t) -1; - return 0; - } - - *timeout_usec = c->timeout; - - return 1; -} - -static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_netlink *rtnl = userdata; - int r; - - assert(rtnl); - - r = sd_netlink_process(rtnl, NULL); - if (r < 0) - return r; - - return 1; -} - -static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { - sd_netlink *rtnl = userdata; - int r; - - assert(rtnl); - - r = sd_netlink_process(rtnl, NULL); - if (r < 0) - return r; - - return 1; -} - -static int prepare_callback(sd_event_source *s, void *userdata) { - sd_netlink *rtnl = userdata; - int r, e; - usec_t until; - - assert(s); - assert(rtnl); - - e = sd_netlink_get_events(rtnl); - if (e < 0) - return e; - - r = sd_event_source_set_io_events(rtnl->io_event_source, e); - if (r < 0) - return r; - - r = sd_netlink_get_timeout(rtnl, &until); - if (r < 0) - return r; - if (r > 0) { - int j; - - j = sd_event_source_set_time(rtnl->time_event_source, until); - if (j < 0) - return j; - } - - r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0); - if (r < 0) - return r; - - return 1; -} - -int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) { - int r; - - assert_return(rtnl, -EINVAL); - assert_return(!rtnl->event, -EBUSY); - - assert(!rtnl->io_event_source); - assert(!rtnl->time_event_source); - - if (event) - rtnl->event = sd_event_ref(event); - else { - r = sd_event_default(&rtnl->event); - if (r < 0) - return r; - } - - r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(rtnl->io_event_source, priority); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message"); - if (r < 0) - goto fail; - - r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback); - if (r < 0) - goto fail; - - r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(rtnl->time_event_source, priority); - if (r < 0) - goto fail; - - r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer"); - if (r < 0) - goto fail; - - return 0; - -fail: - sd_netlink_detach_event(rtnl); - return r; -} - -int sd_netlink_detach_event(sd_netlink *rtnl) { - assert_return(rtnl, -EINVAL); - assert_return(rtnl->event, -ENXIO); - - rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source); - - rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source); - - rtnl->event = sd_event_unref(rtnl->event); - - return 0; -} - -int sd_netlink_add_match(sd_netlink *rtnl, - uint16_t type, - sd_netlink_message_handler_t callback, - void *userdata) { - _cleanup_free_ struct match_callback *c = NULL; - int r; - - assert_return(rtnl, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - - c = new0(struct match_callback, 1); - if (!c) - return -ENOMEM; - - c->callback = callback; - c->type = type; - c->userdata = userdata; - - switch (type) { - case RTM_NEWLINK: - case RTM_DELLINK: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); - if (r < 0) - return r; - - break; - case RTM_NEWADDR: - case RTM_DELADDR: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); - if (r < 0) - return r; - - 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; - } - - LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c); - - c = NULL; - - return 0; -} - -int sd_netlink_remove_match(sd_netlink *rtnl, - uint16_t type, - 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); - - 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; - } - - return 0; -} diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c deleted file mode 100644 index e0e28cc0cc..0000000000 --- a/src/libsystemd/sd-netlink/test-local-addresses.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "af-list.h" -#include "alloc-util.h" -#include "in-addr-util.h" -#include "local-addresses.h" - -static void print_local_addresses(struct local_address *a, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) { - _cleanup_free_ char *b = NULL; - - assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); - printf("%s if%i scope=%i metric=%u address=%s\n", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); - } -} - -int main(int argc, char *argv[]) { - struct local_address *a; - int n; - - a = NULL; - n = local_addresses(NULL, 0, AF_UNSPEC, &a); - assert_se(n >= 0); - - printf("Local Addresses:\n"); - print_local_addresses(a, (unsigned) n); - a = mfree(a); - - n = local_gateways(NULL, 0, AF_UNSPEC, &a); - assert_se(n >= 0); - - printf("Local Gateways:\n"); - print_local_addresses(a, (unsigned) n); - free(a); - - return 0; -} diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c deleted file mode 100644 index 58c2e892f5..0000000000 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ /dev/null @@ -1,440 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include - -#include "sd-netlink.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_(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); - assert_se(sd_rtnl_message_link_set_family(message, PF_BRIDGE) >= 0); - assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0); - assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0); - assert_se(sd_netlink_message_close_container(message) >= 0); - - assert_se(sd_netlink_message_rewind(message) >= 0); - - assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0); - assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0); - assert_se(cost == 10); - assert_se(sd_netlink_message_exit_container(message) >= 0); -} - -static void test_link_configure(sd_netlink *rtnl, int ifindex) { - _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; - const char *name_out; - struct ether_addr mac_out; - - /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ - assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, name) >= 0); - assert_se(sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0); - assert_se(sd_netlink_message_append_u32(message, IFLA_MTU, mtu) >= 0); - - assert_se(sd_netlink_call(rtnl, message, 0, NULL) == 1); - assert_se(sd_netlink_message_rewind(message) >= 0); - - assert_se(sd_netlink_message_read_string(message, IFLA_IFNAME, &name_out) >= 0); - assert_se(streq(name, name_out)); - - assert_se(sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &mac_out) >= 0); - assert_se(streq(mac, ether_addr_to_string(&mac_out, buffer))); - - assert_se(sd_netlink_message_read_u32(message, IFLA_MTU, &mtu_out) >= 0); - assert_se(mtu == mtu_out); -} - -static void test_link_get(sd_netlink *rtnl, int ifindex) { - sd_netlink_message *m; - sd_netlink_message *r; - unsigned int mtu = 1500; - const char *str_data; - uint8_t u8_data; - uint32_t u32_data; - struct ether_addr eth_data; - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - assert_se(m); - - /* u8 test cases */ - assert_se(sd_netlink_message_append_u8(m, IFLA_CARRIER, 0) >= 0); - assert_se(sd_netlink_message_append_u8(m, IFLA_OPERSTATE, 0) >= 0); - assert_se(sd_netlink_message_append_u8(m, IFLA_LINKMODE, 0) >= 0); - - /* u32 test cases */ - assert_se(sd_netlink_message_append_u32(m, IFLA_MTU, mtu) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_GROUP, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_TXQLEN, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_TX_QUEUES, 0) >= 0); - assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_RX_QUEUES, 0) >= 0); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); - - assert_se(sd_netlink_message_read_string(r, IFLA_IFNAME, &str_data) == 0); - - assert_se(sd_netlink_message_read_u8(r, IFLA_CARRIER, &u8_data) == 0); - assert_se(sd_netlink_message_read_u8(r, IFLA_OPERSTATE, &u8_data) == 0); - assert_se(sd_netlink_message_read_u8(r, IFLA_LINKMODE, &u8_data) == 0); - - assert_se(sd_netlink_message_read_u32(r, IFLA_MTU, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_GROUP, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_TXQLEN, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_TX_QUEUES, &u32_data) == 0); - assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_RX_QUEUES, &u32_data) == 0); - - assert_se(sd_netlink_message_read_ether_addr(r, IFLA_ADDRESS, ð_data) == 0); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); -} - - -static void test_address_get(sd_netlink *rtnl, int ifindex) { - sd_netlink_message *m; - sd_netlink_message *r; - struct in_addr in_data; - struct ifa_cacheinfo cache; - const char *label; - - assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0); - assert_se(m); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); - - assert_se(sd_netlink_message_read_in_addr(r, IFA_LOCAL, &in_data) == 0); - assert_se(sd_netlink_message_read_in_addr(r, IFA_ADDRESS, &in_data) == 0); - assert_se(sd_netlink_message_read_string(r, IFA_LABEL, &label) == 0); - assert_se(sd_netlink_message_read_cache_info(r, IFA_CACHEINFO, &cache) == 0); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - -} - -static void test_route(void) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req; - struct in_addr addr, addr_data; - uint32_t index = 2, u32_data; - int r; - - r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); - if (r < 0) { - log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); - return; - } - - addr.s_addr = htonl(INADDR_LOOPBACK); - - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &addr); - if (r < 0) { - log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); - return; - } - - r = sd_netlink_message_append_u32(req, RTA_OIF, index); - if (r < 0) { - log_error_errno(r, "Could not append RTA_OIF attribute: %m"); - return; - } - - assert_se(sd_netlink_message_rewind(req) >= 0); - - assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0); - assert_se(addr_data.s_addr == addr.s_addr); - - assert_se(sd_netlink_message_read_u32(req, RTA_OIF, &u32_data) >= 0); - assert_se(u32_data == index); - - assert_se((req = sd_netlink_message_unref(req)) == NULL); -} - -static void test_multiple(void) { - sd_netlink *rtnl1, *rtnl2; - - assert_se(sd_netlink_open(&rtnl1) >= 0); - assert_se(sd_netlink_open(&rtnl2) >= 0); - - rtnl1 = sd_netlink_unref(rtnl1); - rtnl2 = sd_netlink_unref(rtnl2); -} - -static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - char *ifname = userdata; - const char *data; - - assert_se(rtnl); - assert_se(m); - - log_info("got link info about %s", ifname); - free(ifname); - - assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0); - assert_se(streq(data, "lo")); - - return 1; -} - -static void test_event_loop(int ifindex) { - _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"); - assert_se(ifname); - - assert_se(sd_netlink_open(&rtnl) >= 0); - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - - assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0); - - assert_se(sd_event_default(&event) >= 0); - - assert_se(sd_netlink_attach_event(rtnl, event, 0) >= 0); - - assert_se(sd_event_run(event, 0) >= 0); - - assert_se(sd_netlink_detach_event(rtnl) >= 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - int *counter = userdata; - int r; - - (*counter)--; - - r = sd_netlink_message_get_errno(m); - - log_info_errno(r, "%d left in pipe. got reply: %m", *counter); - - assert_se(r >= 0); - - return 1; -} - -static void test_async(int ifindex) { - _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; - - ifname = strdup("lo"); - assert_se(ifname); - - assert_se(sd_netlink_open(&rtnl) >= 0); - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - - assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0); - - assert_se(sd_netlink_wait(rtnl, 0) >= 0); - assert_se(sd_netlink_process(rtnl, &r) >= 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_pipe(int ifindex) { - _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); - - assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0); - - counter++; - assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0); - - counter++; - assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0); - - while (counter > 0) { - assert_se(sd_netlink_wait(rtnl, 0) >= 0); - assert_se(sd_netlink_process(rtnl, NULL) >= 0); - } - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_container(void) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - uint16_t u16_data; - uint32_t u32_data; - const char *string_data; - - assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); - - assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0); - assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); - assert_se(sd_netlink_message_append_u16(m, IFLA_VLAN_ID, 100) >= 0); - assert_se(sd_netlink_message_close_container(m) >= 0); - assert_se(sd_netlink_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); - assert_se(sd_netlink_message_close_container(m) >= 0); - assert_se(sd_netlink_message_close_container(m) == -EINVAL); - - assert_se(sd_netlink_message_rewind(m) >= 0); - - assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0); - assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); - assert_se(streq("vlan", string_data)); - - assert_se(sd_netlink_message_enter_container(m, IFLA_INFO_DATA) >= 0); - assert_se(sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &u16_data) >= 0); - assert_se(sd_netlink_message_exit_container(m) >= 0); - - assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); - assert_se(streq("vlan", string_data)); - assert_se(sd_netlink_message_exit_container(m) >= 0); - - assert_se(sd_netlink_message_read_u32(m, IFLA_LINKINFO, &u32_data) < 0); - - assert_se(sd_netlink_message_exit_container(m) == -EINVAL); -} - -static void test_match(void) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - - assert_se(sd_netlink_open(&rtnl) >= 0); - - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); - - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0); - - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); -} - -static void test_get_addresses(sd_netlink *rtnl) { - _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); - - assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0); - - for (m = reply; m; m = sd_netlink_message_next(m)) { - uint16_t type; - unsigned char scope, flags; - int family, ifindex; - - assert_se(sd_netlink_message_get_type(m, &type) >= 0); - assert_se(type == RTM_NEWADDR); - - assert_se(sd_rtnl_message_addr_get_ifindex(m, &ifindex) >= 0); - assert_se(sd_rtnl_message_addr_get_family(m, &family) >= 0); - assert_se(sd_rtnl_message_addr_get_scope(m, &scope) >= 0); - assert_se(sd_rtnl_message_addr_get_flags(m, &flags) >= 0); - - assert_se(ifindex > 0); - assert_se(family == AF_INET || family == AF_INET6); - - log_info("got IPv%u address on ifindex %i", family == AF_INET ? 4: 6, ifindex); - } -} - -static void test_message(void) { - _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); -} - -int main(void) { - sd_netlink *rtnl; - sd_netlink_message *m; - sd_netlink_message *r; - const char *string_data; - int if_loopback; - uint16_t type; - - test_message(); - - test_match(); - - test_multiple(); - - test_route(); - - test_container(); - - assert_se(sd_netlink_open(&rtnl) >= 0); - assert_se(rtnl); - - if_loopback = (int) if_nametoindex("lo"); - assert_se(if_loopback > 0); - - test_async(if_loopback); - - test_pipe(if_loopback); - - test_event_loop(if_loopback); - - test_link_configure(rtnl, if_loopback); - - test_get_addresses(rtnl); - - test_message_link_bridge(rtnl); - - assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0); - assert_se(m); - - assert_se(sd_netlink_message_get_type(m, &type) >= 0); - assert_se(type == RTM_GETLINK); - - assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &string_data) == -EPERM); - - assert_se(sd_netlink_call(rtnl, m, 0, &r) == 1); - assert_se(sd_netlink_message_get_type(r, &type) >= 0); - assert_se(type == RTM_NEWLINK); - - assert_se((r = sd_netlink_message_unref(r)) == NULL); - - assert_se(sd_netlink_call(rtnl, m, -1, &r) == -EPERM); - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - - test_link_get(rtnl, if_loopback); - test_address_get(rtnl, if_loopback); - - assert_se((m = sd_netlink_message_unref(m)) == NULL); - assert_se((r = sd_netlink_message_unref(r)) == NULL); - assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-network/Makefile b/src/libsystemd/sd-network/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-network/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c deleted file mode 100644 index a0d9b5f1a4..0000000000 --- a/src/libsystemd/sd-network/network-util.c +++ /dev/null @@ -1,37 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "fd-util.h" -#include "network-util.h" -#include "strv.h" - -bool network_is_online(void) { - _cleanup_free_ char *state = NULL; - int r; - - r = sd_network_get_operational_state(&state); - if (r < 0) /* if we don't know anything, we consider the system online */ - return true; - - if (STR_IN_SET(state, "routable", "degraded")) - return true; - - return false; -} diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h deleted file mode 100644 index 26780dce28..0000000000 --- a/src/libsystemd/sd-network/network-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Thomas Hindø Paabøl 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 . -***/ - -#include "sd-network.h" - -bool network_is_online(void); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c deleted file mode 100644 index f8e18f23fd..0000000000 --- a/src/libsystemd/sd-network/sd-network.c +++ /dev/null @@ -1,400 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - 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 . -***/ - -#include -#include -#include -#include - -#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 "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -_public_ int sd_network_get_operational_state(char **state) { - _cleanup_free_ char *s = NULL; - int r; - - assert_return(state, -EINVAL); - - r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) - return -ENODATA; - - *state = s; - s = NULL; - - return 0; -} - -static int network_get_strv(const char *key, char ***ret) { - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ret, -EINVAL); - - r = parse_env_file("/run/systemd/netif/state", 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; -} - -_public_ int sd_network_get_dns(char ***ret) { - return network_get_strv("DNS", ret); -} - -_public_ int sd_network_get_ntp(char ***ret) { - return network_get_strv("NTP", ret); -} - -_public_ int sd_network_get_search_domains(char ***ret) { - return network_get_strv("DOMAINS", ret); -} - -_public_ int sd_network_get_route_domains(char ***ret) { - return network_get_strv("ROUTE_DOMAINS", ret); -} - -static int network_link_get_string(int ifindex, const char *field, char **ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - - r = parse_env_file(path, NEWLINE, field, &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_link_get_strv(int ifindex, const char *key, char ***ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ char *s = NULL; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - r = parse_env_file(path, 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; -} - -_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { - return network_link_get_string(ifindex, "ADMIN_STATE", state); -} - -_public_ int sd_network_link_get_network_file(int ifindex, char **filename) { - return network_link_get_string(ifindex, "NETWORK_FILE", filename); -} - -_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) { - return network_link_get_string(ifindex, "LLMNR", llmnr); -} - -_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { - return network_link_get_string(ifindex, "MDNS", mdns); -} - -_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { - return network_link_get_string(ifindex, "DNSSEC", dnssec); -} - -_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_timezone(int ifindex, char **ret) { - return network_link_get_string(ifindex, "TIMEZONE", ret); -} - -_public_ int sd_network_link_get_dns(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "DNS", ret); -} - -_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "NTP", ret); -} - -_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "DOMAINS", ret); -} - -_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); -} - -static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { - char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; - _cleanup_free_ int *ifis = NULL; - _cleanup_free_ char *s = NULL; - size_t allocated = 0, c = 0; - const char *x; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(ret, -EINVAL); - - xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - r = parse_env_file(path, NEWLINE, key, &s, NULL); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (isempty(s)) { - *ret = NULL; - return 0; - } - - x = s; - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&x, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - r = parse_ifindex(word, &ifindex); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) - return -ENOMEM; - - ifis[c++] = ifindex; - } - - if (!GREEDY_REALLOC(ifis, allocated, c + 1)) - return -ENOMEM; - ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ - - *ret = ifis; - ifis = NULL; - - return c; -} - -_public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { - return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); -} - -_public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { - return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); -} - -static inline int MONITOR_TO_FD(sd_network_monitor *m) { - return (int) (unsigned long) m - 1; -} - -static inline sd_network_monitor* FD_TO_MONITOR(int fd) { - return (sd_network_monitor*) (unsigned long) (fd + 1); -} - -static int monitor_add_inotify_watch(int fd) { - int k; - - k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); - if (k >= 0) - return 0; - else if (errno != ENOENT) - return -errno; - - k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); - if (k >= 0) - return 0; - else if (errno != ENOENT) - return -errno; - - k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); - if (k < 0) - return -errno; - - return 0; -} - -_public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { - _cleanup_close_ int fd = -1; - int k; - bool good = false; - - assert_return(m, -EINVAL); - - fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (fd < 0) - return -errno; - - if (!category || streq(category, "links")) { - k = monitor_add_inotify_watch(fd); - if (k < 0) - return k; - - good = true; - } - - if (!good) - return -EINVAL; - - *m = FD_TO_MONITOR(fd); - fd = -1; - - return 0; -} - -_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { - int fd; - - if (m) { - fd = MONITOR_TO_FD(m); - close_nointr(fd); - } - - return NULL; -} - -_public_ int sd_network_monitor_flush(sd_network_monitor *m) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - int fd, k; - - assert_return(m, -EINVAL); - - fd = MONITOR_TO_FD(m); - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if (e->mask & IN_ISDIR) { - k = monitor_add_inotify_watch(fd); - if (k < 0) - return k; - - k = inotify_rm_watch(fd, e->wd); - if (k < 0) - return -errno; - } - } - - return 0; -} - -_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { - - assert_return(m, -EINVAL); - - return MONITOR_TO_FD(m); -} - -_public_ int sd_network_monitor_get_events(sd_network_monitor *m) { - - assert_return(m, -EINVAL); - - /* For now we will only return POLLIN here, since we don't - * need anything else ever for inotify. However, let's have - * this API to keep our options open should we later on need - * it. */ - return POLLIN; -} - -_public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { - - assert_return(m, -EINVAL); - assert_return(timeout_usec, -EINVAL); - - /* For now we will only return (uint64_t) -1, since we don't - * need any timeout. However, let's have this API to keep our - * options open should we later on need it. */ - *timeout_usec = (uint64_t) -1; - return 0; -} diff --git a/src/libsystemd/sd-path/Makefile b/src/libsystemd/sd-path/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libsystemd/sd-path/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c deleted file mode 100644 index b7aec1f20a..0000000000 --- a/src/libsystemd/sd-path/sd-path.c +++ /dev/null @@ -1,638 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 "user-util.h" -#include "util.h" - -static int from_environment(const char *envname, const char *fallback, const char **ret) { - assert(ret); - - if (envname) { - const char *e; - - e = secure_getenv(envname); - if (e && path_is_absolute(e)) { - *ret = e; - return 0; - } - } - - if (fallback) { - *ret = fallback; - return 0; - } - - return -ENXIO; -} - -static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { - _cleanup_free_ char *h = NULL; - char *cc = NULL; - int r; - - assert(suffix); - assert(buffer); - assert(ret); - - if (envname) { - const char *e = NULL; - - e = secure_getenv(envname); - if (e && path_is_absolute(e)) { - *ret = e; - return 0; - } - } - - r = get_home_dir(&h); - if (r < 0) - return r; - - if (endswith(h, "/")) - cc = strappend(h, suffix); - else - cc = strjoin(h, "/", suffix, NULL); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - return 0; -} - -static int from_user_dir(const char *field, char **buffer, const char **ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *b = NULL; - _cleanup_free_ const char *fn = NULL; - const char *c = NULL; - char line[LINE_MAX]; - size_t n; - int r; - - assert(field); - assert(buffer); - assert(ret); - - r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); - if (r < 0) - return r; - - fn = strappend(c, "/user-dirs.dirs"); - if (!fn) - return -ENOMEM; - - f = fopen(fn, "re"); - if (!f) { - if (errno == ENOENT) - goto fallback; - - return -errno; - } - - /* This is an awful parse, but it follows closely what - * xdg-user-dirs does upstream */ - - n = strlen(field); - FOREACH_LINE(line, f, return -errno) { - char *l, *p, *e; - - l = strstrip(line); - - if (!strneq(l, field, n)) - continue; - - p = l + n; - p += strspn(p, WHITESPACE); - - if (*p != '=') - continue; - p++; - - p += strspn(p, WHITESPACE); - - if (*p != '"') - continue; - p++; - - e = strrchr(p, '"'); - if (!e) - continue; - *e = 0; - - /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */ - if (startswith(p, "$HOME/")) { - _cleanup_free_ char *h = NULL; - char *cc; - - r = get_home_dir(&h); - if (r < 0) - return r; - - cc = strappend(h, p+5); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - return 0; - } else if (streq(p, "$HOME")) { - - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - return 0; - } else if (path_is_absolute(p)) { - char *copy; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *buffer = copy; - *ret = copy; - return 0; - } - } - -fallback: - /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */ - if (streq(field, "XDG_DESKTOP_DIR")) { - _cleanup_free_ char *h = NULL; - char *cc; - - r = get_home_dir(&h); - if (r < 0) - return r; - - cc = strappend(h, "/Desktop"); - if (!cc) - return -ENOMEM; - - *buffer = cc; - *ret = cc; - } else { - - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - } - - return 0; -} - -static int get_path(uint64_t type, char **buffer, const char **ret) { - int r; - - assert(buffer); - assert(ret); - - switch (type) { - - case SD_PATH_TEMPORARY: - return from_environment("TMPDIR", "/tmp", ret); - - case SD_PATH_TEMPORARY_LARGE: - return from_environment("TMPDIR", "/var/tmp", ret); - - case SD_PATH_SYSTEM_BINARIES: - *ret = "/usr/bin"; - return 0; - - case SD_PATH_SYSTEM_INCLUDE: - *ret = "/usr/include"; - return 0; - - case SD_PATH_SYSTEM_LIBRARY_PRIVATE: - *ret = "/usr/lib"; - return 0; - - case SD_PATH_SYSTEM_LIBRARY_ARCH: - *ret = LIBDIR; - return 0; - - case SD_PATH_SYSTEM_SHARED: - *ret = "/usr/share"; - return 0; - - case SD_PATH_SYSTEM_CONFIGURATION_FACTORY: - *ret = "/usr/share/factory/etc"; - return 0; - - case SD_PATH_SYSTEM_STATE_FACTORY: - *ret = "/usr/share/factory/var"; - return 0; - - case SD_PATH_SYSTEM_CONFIGURATION: - *ret = "/etc"; - return 0; - - case SD_PATH_SYSTEM_RUNTIME: - *ret = "/run"; - return 0; - - case SD_PATH_SYSTEM_RUNTIME_LOGS: - *ret = "/run/log"; - return 0; - - case SD_PATH_SYSTEM_STATE_PRIVATE: - *ret = "/var/lib"; - return 0; - - case SD_PATH_SYSTEM_STATE_LOGS: - *ret = "/var/log"; - return 0; - - case SD_PATH_SYSTEM_STATE_CACHE: - *ret = "/var/cache"; - return 0; - - case SD_PATH_SYSTEM_STATE_SPOOL: - *ret = "/var/spool"; - return 0; - - case SD_PATH_USER_BINARIES: - return from_home_dir(NULL, ".local/bin", buffer, ret); - - case SD_PATH_USER_LIBRARY_PRIVATE: - return from_home_dir(NULL, ".local/lib", buffer, ret); - - case SD_PATH_USER_LIBRARY_ARCH: - return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret); - - case SD_PATH_USER_SHARED: - return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret); - - case SD_PATH_USER_CONFIGURATION: - return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret); - - case SD_PATH_USER_RUNTIME: - return from_environment("XDG_RUNTIME_DIR", NULL, ret); - - case SD_PATH_USER_STATE_CACHE: - return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret); - - case SD_PATH_USER: - r = get_home_dir(buffer); - if (r < 0) - return r; - - *ret = *buffer; - return 0; - - case SD_PATH_USER_DOCUMENTS: - return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret); - - case SD_PATH_USER_MUSIC: - return from_user_dir("XDG_MUSIC_DIR", buffer, ret); - - case SD_PATH_USER_PICTURES: - return from_user_dir("XDG_PICTURES_DIR", buffer, ret); - - case SD_PATH_USER_VIDEOS: - return from_user_dir("XDG_VIDEOS_DIR", buffer, ret); - - case SD_PATH_USER_DOWNLOAD: - return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret); - - case SD_PATH_USER_PUBLIC: - return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret); - - case SD_PATH_USER_TEMPLATES: - return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret); - - case SD_PATH_USER_DESKTOP: - return from_user_dir("XDG_DESKTOP_DIR", buffer, ret); - } - - return -EOPNOTSUPP; -} - -_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { - char *buffer = NULL, *cc; - const char *ret; - int r; - - assert_return(path, -EINVAL); - - if (IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { - - _cleanup_strv_free_ char **l = NULL; - - r = sd_path_search(type, suffix, &l); - if (r < 0) - return r; - - buffer = strv_join(l, ":"); - if (!buffer) - return -ENOMEM; - - *path = buffer; - return 0; - } - - r = get_path(type, &buffer, &ret); - if (r < 0) - return r; - - if (!suffix) { - if (!buffer) { - buffer = strdup(ret); - if (!buffer) - return -ENOMEM; - } - - *path = buffer; - return 0; - } - - suffix += strspn(suffix, "/"); - - if (endswith(ret, "/")) - cc = strappend(ret, suffix); - else - cc = strjoin(ret, "/", suffix, NULL); - - free(buffer); - - if (!cc) - return -ENOMEM; - - *path = cc; - return 0; -} - -static int search_from_environment( - char ***list, - const char *env_home, - const char *home_suffix, - const char *env_search, - bool env_search_sufficient, - const char *first, ...) { - - const char *e; - char *h = NULL; - char **l = NULL; - int r; - - assert(list); - - if (env_search) { - e = secure_getenv(env_search); - if (e) { - l = strv_split(e, ":"); - if (!l) - return -ENOMEM; - - if (env_search_sufficient) { - *list = l; - return 0; - } - } - } - - if (!l && first) { - va_list ap; - - va_start(ap, first); - l = strv_new_ap(first, ap); - va_end(ap); - - if (!l) - return -ENOMEM; - } - - if (env_home) { - e = secure_getenv(env_home); - if (e && path_is_absolute(e)) { - h = strdup(e); - if (!h) { - strv_free(l); - return -ENOMEM; - } - } - } - - if (!h && home_suffix) { - e = secure_getenv("HOME"); - if (e && path_is_absolute(e)) { - if (endswith(e, "/")) - h = strappend(e, home_suffix); - else - h = strjoin(e, "/", home_suffix, NULL); - - if (!h) { - strv_free(l); - return -ENOMEM; - } - } - } - - if (h) { - r = strv_consume_prepend(&l, h); - if (r < 0) { - strv_free(l); - return -ENOMEM; - } - } - - *list = l; - return 0; -} - -static int get_search(uint64_t type, char ***list) { - - assert(list); - - switch(type) { - - case SD_PATH_SEARCH_BINARIES: - return search_from_environment(list, - NULL, - ".local/bin", - "PATH", - true, - "/usr/local/sbin", - "/usr/local/bin", - "/usr/sbin", - "/usr/bin", -#ifdef HAVE_SPLIT_USR - "/sbin", - "/bin", -#endif - NULL); - - case SD_PATH_SEARCH_LIBRARY_PRIVATE: - return search_from_environment(list, - NULL, - ".local/lib", - NULL, - false, - "/usr/local/lib", - "/usr/lib", -#ifdef HAVE_SPLIT_USR - "/lib", -#endif - NULL); - - case SD_PATH_SEARCH_LIBRARY_ARCH: - return search_from_environment(list, - NULL, - ".local/lib/" LIB_ARCH_TUPLE, - "LD_LIBRARY_PATH", - true, - LIBDIR, -#ifdef HAVE_SPLIT_USR - ROOTLIBDIR, -#endif - NULL); - - case SD_PATH_SEARCH_SHARED: - return search_from_environment(list, - "XDG_DATA_HOME", - ".local/share", - "XDG_DATA_DIRS", - false, - "/usr/local/share", - "/usr/share", - NULL); - - case SD_PATH_SEARCH_CONFIGURATION_FACTORY: - return search_from_environment(list, - NULL, - NULL, - NULL, - false, - "/usr/local/share/factory/etc", - "/usr/share/factory/etc", - NULL); - - case SD_PATH_SEARCH_STATE_FACTORY: - return search_from_environment(list, - NULL, - NULL, - NULL, - false, - "/usr/local/share/factory/var", - "/usr/share/factory/var", - NULL); - - case SD_PATH_SEARCH_CONFIGURATION: - return search_from_environment(list, - "XDG_CONFIG_HOME", - ".config", - "XDG_CONFIG_DIRS", - false, - "/etc", - NULL); - } - - return -EOPNOTSUPP; -} - -_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { - char **l, **i, **j, **n; - int r; - - assert_return(paths, -EINVAL); - - if (!IN_SET(type, - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION)) { - - char *p; - - r = sd_path_home(type, suffix, &p); - if (r < 0) - return r; - - l = new(char*, 2); - if (!l) { - free(p); - return -ENOMEM; - } - - l[0] = p; - l[1] = NULL; - - *paths = l; - return 0; - } - - r = get_search(type, &l); - if (r < 0) - return r; - - if (!suffix) { - *paths = l; - return 0; - } - - n = new(char*, strv_length(l)+1); - if (!n) { - strv_free(l); - return -ENOMEM; - } - - j = n; - STRV_FOREACH(i, l) { - - if (endswith(*i, "/")) - *j = strappend(*i, suffix); - else - *j = strjoin(*i, "/", suffix, NULL); - - if (!*j) { - strv_free(l); - strv_free(n); - return -ENOMEM; - } - - j++; - } - - *j = NULL; - *paths = n; - return 0; -} diff --git a/src/libsystemd/sd-resolve/Makefile b/src/libsystemd/sd-resolve/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c deleted file mode 100644 index 60aa55de3b..0000000000 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ /dev/null @@ -1,1243 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2005-2008 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-resolve.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "list.h" -#include "missing.h" -#include "socket-util.h" -#include "util.h" - -#define WORKERS_MIN 1U -#define WORKERS_MAX 16U -#define QUERIES_MAX 256U -#define BUFSIZE 10240U - -typedef enum { - REQUEST_ADDRINFO, - RESPONSE_ADDRINFO, - REQUEST_NAMEINFO, - RESPONSE_NAMEINFO, - REQUEST_TERMINATE, - RESPONSE_DIED -} QueryType; - -enum { - REQUEST_RECV_FD, - REQUEST_SEND_FD, - RESPONSE_RECV_FD, - RESPONSE_SEND_FD, - _FD_MAX -}; - -struct sd_resolve { - unsigned n_ref; - - bool dead:1; - pid_t original_pid; - - int fds[_FD_MAX]; - - pthread_t workers[WORKERS_MAX]; - unsigned n_valid_workers; - - unsigned current_id; - sd_resolve_query* query_array[QUERIES_MAX]; - unsigned n_queries, n_done, n_outstanding; - - sd_event_source *event_source; - sd_event *event; - - sd_resolve_query *current; - - sd_resolve **default_resolve_ptr; - pid_t tid; - - LIST_HEAD(sd_resolve_query, queries); -}; - -struct sd_resolve_query { - unsigned n_ref; - - sd_resolve *resolve; - - QueryType type:4; - bool done:1; - bool floating:1; - unsigned id; - - int ret; - int _errno; - int _h_errno; - struct addrinfo *addrinfo; - char *serv, *host; - - union { - sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; - sd_resolve_getnameinfo_handler_t getnameinfo_handler; - }; - - void *userdata; - - LIST_FIELDS(sd_resolve_query, queries); -}; - -typedef struct RHeader { - QueryType type; - unsigned id; - size_t length; -} RHeader; - -typedef struct AddrInfoRequest { - struct RHeader header; - bool hints_valid; - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t node_len, service_len; -} AddrInfoRequest; - -typedef struct AddrInfoResponse { - struct RHeader header; - int ret; - int _errno; - int _h_errno; - /* followed by addrinfo_serialization[] */ -} AddrInfoResponse; - -typedef struct AddrInfoSerialization { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - size_t canonname_len; - /* Followed by ai_addr amd ai_canonname with variable lengths */ -} AddrInfoSerialization; - -typedef struct NameInfoRequest { - struct RHeader header; - int flags; - socklen_t sockaddr_len; - bool gethost:1, getserv:1; -} NameInfoRequest; - -typedef struct NameInfoResponse { - struct RHeader header; - size_t hostlen, servlen; - int ret; - int _errno; - int _h_errno; -} NameInfoResponse; - -typedef union Packet { - RHeader rheader; - AddrInfoRequest addrinfo_request; - AddrInfoResponse addrinfo_response; - NameInfoRequest nameinfo_request; - NameInfoResponse nameinfo_response; -} Packet; - -static int getaddrinfo_done(sd_resolve_query* q); -static int getnameinfo_done(sd_resolve_query *q); - -static void resolve_query_disconnect(sd_resolve_query *q); - -#define RESOLVE_DONT_DESTROY(resolve) \ - _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) - -static int send_died(int out_fd) { - - RHeader rh = { - .type = RESPONSE_DIED, - .length = sizeof(RHeader), - }; - - assert(out_fd >= 0); - - if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { - AddrInfoSerialization s; - size_t cnl, l; - - assert(p); - assert(ai); - assert(length); - assert(*length <= maxlength); - - cnl = ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0; - l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl; - - if (*length + l > maxlength) - return NULL; - - s.ai_flags = ai->ai_flags; - s.ai_family = ai->ai_family; - s.ai_socktype = ai->ai_socktype; - s.ai_protocol = ai->ai_protocol; - s.ai_addrlen = ai->ai_addrlen; - s.canonname_len = cnl; - - memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization)); - memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); - memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, - ai->ai_canonname, cnl); - - *length += l; - return (uint8_t*) p + l; -} - -static int send_addrinfo_reply( - int out_fd, - unsigned id, - int ret, - struct addrinfo *ai, - int _errno, - int _h_errno) { - - AddrInfoResponse resp = { - .header.type = RESPONSE_ADDRINFO, - .header.id = id, - .header.length = sizeof(AddrInfoResponse), - .ret = ret, - ._errno = _errno, - ._h_errno = _h_errno, - }; - - struct msghdr mh = {}; - struct iovec iov[2]; - union { - AddrInfoSerialization ais; - uint8_t space[BUFSIZE]; - } buffer; - - assert(out_fd >= 0); - - if (ret == 0 && ai) { - void *p = &buffer; - struct addrinfo *k; - - for (k = ai; k; k = k->ai_next) { - p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); - if (!p) { - freeaddrinfo(ai); - return -ENOBUFS; - } - } - } - - if (ai) - freeaddrinfo(ai); - - iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; - iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; - - mh.msg_iov = iov; - mh.msg_iovlen = ELEMENTSOF(iov); - - if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -static int send_nameinfo_reply( - int out_fd, - unsigned id, - int ret, - const char *host, - const char *serv, - int _errno, - int _h_errno) { - - NameInfoResponse resp = { - .header.type = RESPONSE_NAMEINFO, - .header.id = id, - .ret = ret, - ._errno = _errno, - ._h_errno = _h_errno, - }; - - struct msghdr mh = {}; - struct iovec iov[3]; - size_t hl, sl; - - assert(out_fd >= 0); - - sl = serv ? strlen(serv)+1 : 0; - hl = host ? strlen(host)+1 : 0; - - resp.header.length = sizeof(NameInfoResponse) + hl + sl; - resp.hostlen = hl; - resp.servlen = sl; - - iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; - iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; - iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; - - mh.msg_iov = iov; - mh.msg_iovlen = ELEMENTSOF(iov); - - if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - -static int handle_request(int out_fd, const Packet *packet, size_t length) { - const RHeader *req; - - assert(out_fd >= 0); - assert(packet); - - req = &packet->rheader; - - assert(length >= sizeof(RHeader)); - assert(length == req->length); - - switch (req->type) { - - case REQUEST_ADDRINFO: { - const AddrInfoRequest *ai_req = &packet->addrinfo_request; - struct addrinfo hints = {}, *result = NULL; - const char *node, *service; - int ret; - - assert(length >= sizeof(AddrInfoRequest)); - assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len); - - hints.ai_flags = ai_req->ai_flags; - hints.ai_family = ai_req->ai_family; - hints.ai_socktype = ai_req->ai_socktype; - hints.ai_protocol = ai_req->ai_protocol; - - node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL; - service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL; - - ret = getaddrinfo( - node, service, - ai_req->hints_valid ? &hints : NULL, - &result); - - /* send_addrinfo_reply() frees result */ - return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); - } - - case REQUEST_NAMEINFO: { - const NameInfoRequest *ni_req = &packet->nameinfo_request; - char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; - union sockaddr_union sa; - int ret; - - assert(length >= sizeof(NameInfoRequest)); - assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len); - assert(sizeof(sa) >= ni_req->sockaddr_len); - - memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); - - ret = getnameinfo(&sa.sa, ni_req->sockaddr_len, - ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, - ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, - ni_req->flags); - - return send_nameinfo_reply(out_fd, req->id, ret, - ret == 0 && ni_req->gethost ? hostbuf : NULL, - ret == 0 && ni_req->getserv ? servbuf : NULL, - errno, h_errno); - } - - case REQUEST_TERMINATE: - /* Quit */ - return -ECONNRESET; - - default: - assert_not_reached("Unknown request"); - } - - return 0; -} - -static void* thread_worker(void *p) { - sd_resolve *resolve = p; - sigset_t fullset; - - /* No signals in this thread please */ - assert_se(sigfillset(&fullset) == 0); - assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); - - /* Assign a pretty name to this thread */ - (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); - - while (!resolve->dead) { - union { - Packet packet; - uint8_t space[BUFSIZE]; - } buf; - ssize_t length; - - length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof(buf), 0); - if (length < 0) { - if (errno == EINTR) - continue; - - break; - } - if (length == 0) - break; - - if (resolve->dead) - break; - - if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) - break; - } - - send_died(resolve->fds[RESPONSE_SEND_FD]); - - return NULL; -} - -static int start_threads(sd_resolve *resolve, unsigned extra) { - unsigned n; - int r; - - n = resolve->n_outstanding + extra; - n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); - - while (resolve->n_valid_workers < n) { - - r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); - if (r != 0) - return -r; - - resolve->n_valid_workers++; - } - - return 0; -} - -static bool resolve_pid_changed(sd_resolve *r) { - assert(r); - - /* We don't support people creating a resolver and keeping it - * around after fork(). Let's complain. */ - - return r->original_pid != getpid(); -} - -_public_ int sd_resolve_new(sd_resolve **ret) { - sd_resolve *resolve = NULL; - int i, r; - - assert_return(ret, -EINVAL); - - resolve = new0(sd_resolve, 1); - if (!resolve) - return -ENOMEM; - - resolve->n_ref = 1; - resolve->original_pid = getpid(); - - for (i = 0; i < _FD_MAX; i++) - resolve->fds[i] = -1; - - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); - if (r < 0) { - r = -errno; - goto fail; - } - - r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD); - if (r < 0) { - r = -errno; - goto fail; - } - - fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); - fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); - fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); - - fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); - - *ret = resolve; - return 0; - -fail: - sd_resolve_unref(resolve); - return r; -} - -_public_ int sd_resolve_default(sd_resolve **ret) { - - static thread_local sd_resolve *default_resolve = NULL; - sd_resolve *e = NULL; - int r; - - if (!ret) - return !!default_resolve; - - if (default_resolve) { - *ret = sd_resolve_ref(default_resolve); - return 0; - } - - r = sd_resolve_new(&e); - if (r < 0) - return r; - - e->default_resolve_ptr = &default_resolve; - e->tid = gettid(); - default_resolve = e; - - *ret = e; - return 1; -} - -_public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { - assert_return(resolve, -EINVAL); - assert_return(tid, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - if (resolve->tid != 0) { - *tid = resolve->tid; - return 0; - } - - if (resolve->event) - return sd_event_get_tid(resolve->event, tid); - - return -ENXIO; -} - -static void resolve_free(sd_resolve *resolve) { - PROTECT_ERRNO; - sd_resolve_query *q; - unsigned i; - - assert(resolve); - - while ((q = resolve->queries)) { - assert(q->floating); - resolve_query_disconnect(q); - sd_resolve_query_unref(q); - } - - if (resolve->default_resolve_ptr) - *(resolve->default_resolve_ptr) = NULL; - - resolve->dead = true; - - sd_resolve_detach_event(resolve); - - if (resolve->fds[REQUEST_SEND_FD] >= 0) { - - RHeader req = { - .type = REQUEST_TERMINATE, - .length = sizeof(req) - }; - - /* Send one termination packet for each worker */ - for (i = 0; i < resolve->n_valid_workers; i++) - (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); - } - - /* Now terminate them and wait until they are gone. - If we get an error than most likely the thread already exited. */ - for (i = 0; i < resolve->n_valid_workers; i++) - (void) pthread_join(resolve->workers[i], NULL); - - /* Close all communication channels */ - close_many(resolve->fds, _FD_MAX); - free(resolve); -} - -_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { - assert_return(resolve, NULL); - - assert(resolve->n_ref >= 1); - resolve->n_ref++; - - return resolve; -} - -_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { - - if (!resolve) - return NULL; - - assert(resolve->n_ref >= 1); - resolve->n_ref--; - - if (resolve->n_ref <= 0) - resolve_free(resolve); - - return NULL; -} - -_public_ int sd_resolve_get_fd(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - return resolve->fds[RESPONSE_RECV_FD]; -} - -_public_ int sd_resolve_get_events(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - return resolve->n_queries > resolve->n_done ? POLLIN : 0; -} - -_public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { - assert_return(resolve, -EINVAL); - assert_return(usec, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - *usec = (uint64_t) -1; - return 0; -} - -static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { - sd_resolve_query *q; - - assert(resolve); - - q = resolve->query_array[id % QUERIES_MAX]; - if (q) - if (q->id == id) - return q; - - return NULL; -} - -static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { - int r; - - assert(q); - assert(!q->done); - assert(q->resolve == resolve); - - q->done = true; - resolve->n_done++; - - resolve->current = sd_resolve_query_ref(q); - - switch (q->type) { - - case REQUEST_ADDRINFO: - r = getaddrinfo_done(q); - break; - - case REQUEST_NAMEINFO: - r = getnameinfo_done(q); - break; - - default: - assert_not_reached("Cannot complete unknown query type"); - } - - resolve->current = NULL; - - if (q->floating) { - resolve_query_disconnect(q); - sd_resolve_query_unref(q); - } - - sd_resolve_query_unref(q); - - return r; -} - -static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { - AddrInfoSerialization s; - size_t l; - struct addrinfo *ai; - - assert(p); - assert(*p); - assert(ret_ai); - assert(length); - - if (*length < sizeof(AddrInfoSerialization)) - return -EBADMSG; - - memcpy(&s, *p, sizeof(s)); - - l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; - if (*length < l) - return -EBADMSG; - - ai = new0(struct addrinfo, 1); - if (!ai) - return -ENOMEM; - - ai->ai_flags = s.ai_flags; - ai->ai_family = s.ai_family; - ai->ai_socktype = s.ai_socktype; - ai->ai_protocol = s.ai_protocol; - ai->ai_addrlen = s.ai_addrlen; - - if (s.ai_addrlen > 0) { - ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); - if (!ai->ai_addr) { - free(ai); - return -ENOMEM; - } - } - - if (s.canonname_len > 0) { - ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); - if (!ai->ai_canonname) { - free(ai->ai_addr); - free(ai); - return -ENOMEM; - } - } - - *length -= l; - *ret_ai = ai; - *p = ((const uint8_t*) *p) + l; - - return 0; -} - -static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { - const RHeader *resp; - sd_resolve_query *q; - int r; - - assert(resolve); - - resp = &packet->rheader; - assert(resp); - assert(length >= sizeof(RHeader)); - assert(length == resp->length); - - if (resp->type == RESPONSE_DIED) { - resolve->dead = true; - return 0; - } - - assert(resolve->n_outstanding > 0); - resolve->n_outstanding--; - - q = lookup_query(resolve, resp->id); - if (!q) - return 0; - - switch (resp->type) { - - case RESPONSE_ADDRINFO: { - const AddrInfoResponse *ai_resp = &packet->addrinfo_response; - const void *p; - size_t l; - struct addrinfo *prev = NULL; - - assert(length >= sizeof(AddrInfoResponse)); - assert(q->type == REQUEST_ADDRINFO); - - q->ret = ai_resp->ret; - q->_errno = ai_resp->_errno; - q->_h_errno = ai_resp->_h_errno; - - l = length - sizeof(AddrInfoResponse); - p = (const uint8_t*) resp + sizeof(AddrInfoResponse); - - while (l > 0 && p) { - struct addrinfo *ai = NULL; - - r = unserialize_addrinfo(&p, &l, &ai); - if (r < 0) { - q->ret = EAI_SYSTEM; - q->_errno = -r; - q->_h_errno = 0; - freeaddrinfo(q->addrinfo); - q->addrinfo = NULL; - break; - } - - if (prev) - prev->ai_next = ai; - else - q->addrinfo = ai; - - prev = ai; - } - - return complete_query(resolve, q); - } - - case RESPONSE_NAMEINFO: { - const NameInfoResponse *ni_resp = &packet->nameinfo_response; - - assert(length >= sizeof(NameInfoResponse)); - assert(q->type == REQUEST_NAMEINFO); - - q->ret = ni_resp->ret; - q->_errno = ni_resp->_errno; - q->_h_errno = ni_resp->_h_errno; - - if (ni_resp->hostlen > 0) { - q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); - if (!q->host) { - q->ret = EAI_MEMORY; - q->_errno = ENOMEM; - q->_h_errno = 0; - } - } - - if (ni_resp->servlen > 0) { - q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); - if (!q->serv) { - q->ret = EAI_MEMORY; - q->_errno = ENOMEM; - q->_h_errno = 0; - } - } - - return complete_query(resolve, q); - } - - default: - return 0; - } -} - -_public_ int sd_resolve_process(sd_resolve *resolve) { - RESOLVE_DONT_DESTROY(resolve); - - union { - Packet packet; - uint8_t space[BUFSIZE]; - } buf; - ssize_t l; - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - /* We don't allow recursively invoking sd_resolve_process(). */ - assert_return(!resolve->current, -EBUSY); - - l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - return -errno; - } - if (l == 0) - return -ECONNREFUSED; - - r = handle_response(resolve, &buf.packet, (size_t) l); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - if (resolve->n_done >= resolve->n_queries) - return 0; - - do { - r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); - } while (r == -EINTR); - - if (r < 0) - return r; - - return sd_resolve_process(resolve); -} - -static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { - sd_resolve_query *q; - int r; - - assert(resolve); - assert(_q); - - if (resolve->n_queries >= QUERIES_MAX) - return -ENOBUFS; - - r = start_threads(resolve, 1); - if (r < 0) - return r; - - while (resolve->query_array[resolve->current_id % QUERIES_MAX]) - resolve->current_id++; - - q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); - if (!q) - return -ENOMEM; - - q->n_ref = 1; - q->resolve = resolve; - q->floating = floating; - q->id = resolve->current_id++; - - if (!floating) - sd_resolve_ref(resolve); - - LIST_PREPEND(queries, resolve->queries, q); - resolve->n_queries++; - - *_q = q; - return 0; -} - -_public_ int sd_resolve_getaddrinfo( - sd_resolve *resolve, - sd_resolve_query **_q, - const char *node, const char *service, - const struct addrinfo *hints, - sd_resolve_getaddrinfo_handler_t callback, void *userdata) { - - AddrInfoRequest req = {}; - struct msghdr mh = {}; - struct iovec iov[3]; - sd_resolve_query *q; - int r; - - assert_return(resolve, -EINVAL); - assert_return(node || service, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - r = alloc_query(resolve, !_q, &q); - if (r < 0) - return r; - - q->type = REQUEST_ADDRINFO; - q->getaddrinfo_handler = callback; - q->userdata = userdata; - - req.node_len = node ? strlen(node)+1 : 0; - req.service_len = service ? strlen(service)+1 : 0; - - req.header.id = q->id; - req.header.type = REQUEST_ADDRINFO; - req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; - - if (hints) { - req.hints_valid = true; - req.ai_flags = hints->ai_flags; - req.ai_family = hints->ai_family; - req.ai_socktype = hints->ai_socktype; - req.ai_protocol = hints->ai_protocol; - } - - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; - if (node) - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; - if (service) - iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; - mh.msg_iov = iov; - - if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { - sd_resolve_query_unref(q); - return -errno; - } - - resolve->n_outstanding++; - - if (_q) - *_q = q; - - return 0; -} - -static int getaddrinfo_done(sd_resolve_query* q) { - assert(q); - assert(q->done); - assert(q->getaddrinfo_handler); - - errno = q->_errno; - h_errno = q->_h_errno; - - return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); -} - -_public_ int sd_resolve_getnameinfo( - sd_resolve *resolve, - sd_resolve_query**_q, - const struct sockaddr *sa, socklen_t salen, - int flags, - uint64_t get, - sd_resolve_getnameinfo_handler_t callback, - void *userdata) { - - NameInfoRequest req = {}; - struct msghdr mh = {}; - struct iovec iov[2]; - sd_resolve_query *q; - int r; - - assert_return(resolve, -EINVAL); - assert_return(sa, -EINVAL); - assert_return(salen >= sizeof(struct sockaddr), -EINVAL); - assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); - assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - r = alloc_query(resolve, !_q, &q); - if (r < 0) - return r; - - q->type = REQUEST_NAMEINFO; - q->getnameinfo_handler = callback; - q->userdata = userdata; - - req.header.id = q->id; - req.header.type = REQUEST_NAMEINFO; - req.header.length = sizeof(NameInfoRequest) + salen; - - req.flags = flags; - req.sockaddr_len = salen; - req.gethost = !!(get & SD_RESOLVE_GET_HOST); - req.getserv = !!(get & SD_RESOLVE_GET_SERVICE); - - iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; - iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; - - mh.msg_iov = iov; - mh.msg_iovlen = 2; - - if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { - sd_resolve_query_unref(q); - return -errno; - } - - resolve->n_outstanding++; - - if (_q) - *_q = q; - - return 0; -} - -static int getnameinfo_done(sd_resolve_query *q) { - - assert(q); - assert(q->done); - assert(q->getnameinfo_handler); - - errno = q->_errno; - h_errno= q->_h_errno; - - return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); -} - -_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { - assert_return(q, NULL); - - assert(q->n_ref >= 1); - q->n_ref++; - - return q; -} - -static void resolve_freeaddrinfo(struct addrinfo *ai) { - while (ai) { - struct addrinfo *next = ai->ai_next; - - free(ai->ai_addr); - free(ai->ai_canonname); - free(ai); - ai = next; - } -} - -static void resolve_query_disconnect(sd_resolve_query *q) { - sd_resolve *resolve; - unsigned i; - - assert(q); - - if (!q->resolve) - return; - - resolve = q->resolve; - assert(resolve->n_queries > 0); - - if (q->done) { - assert(resolve->n_done > 0); - resolve->n_done--; - } - - i = q->id % QUERIES_MAX; - assert(resolve->query_array[i] == q); - resolve->query_array[i] = NULL; - LIST_REMOVE(queries, resolve->queries, q); - resolve->n_queries--; - - q->resolve = NULL; - if (!q->floating) - sd_resolve_unref(resolve); -} - -static void resolve_query_free(sd_resolve_query *q) { - assert(q); - - resolve_query_disconnect(q); - - resolve_freeaddrinfo(q->addrinfo); - free(q->host); - free(q->serv); - free(q); -} - -_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { - if (!q) - return NULL; - - assert(q->n_ref >= 1); - q->n_ref--; - - if (q->n_ref <= 0) - resolve_query_free(q); - - return NULL; -} - -_public_ int sd_resolve_query_is_done(sd_resolve_query *q) { - assert_return(q, -EINVAL); - assert_return(!resolve_pid_changed(q->resolve), -ECHILD); - - return q->done; -} - -_public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { - void *ret; - - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - ret = q->userdata; - q->userdata = userdata; - - return ret; -} - -_public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - return q->userdata; -} - -_public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { - assert_return(q, NULL); - assert_return(!resolve_pid_changed(q->resolve), NULL); - - return q->resolve; -} - -static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_resolve *resolve = userdata; - int r; - - assert(resolve); - - r = sd_resolve_process(resolve); - if (r < 0) - return r; - - return 1; -} - -_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { - int r; - - assert_return(resolve, -EINVAL); - assert_return(!resolve->event, -EBUSY); - - assert(!resolve->event_source); - - if (event) - resolve->event = sd_event_ref(event); - else { - r = sd_event_default(&resolve->event); - if (r < 0) - return r; - } - - r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(resolve->event_source, priority); - if (r < 0) - goto fail; - - return 0; - -fail: - sd_resolve_detach_event(resolve); - return r; -} - -_public_ int sd_resolve_detach_event(sd_resolve *resolve) { - assert_return(resolve, -EINVAL); - - if (!resolve->event) - return 0; - - if (resolve->event_source) { - sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); - resolve->event_source = sd_event_source_unref(resolve->event_source); - } - - resolve->event = sd_event_unref(resolve->event); - return 1; -} - -_public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { - assert_return(resolve, NULL); - - return resolve->event; -} diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c deleted file mode 100644 index 1be1a7f8a7..0000000000 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2005-2008 Lennart Poettering - Copyright 2014 Daniel Buch - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "sd-resolve.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; - - assert_se(q); - - if (ret != 0) { - log_error("getaddrinfo error: %s %i", gai_strerror(ret), ret); - return 0; - } - - for (i = ai; i; i = i->ai_next) { - _cleanup_free_ char *addr = NULL; - - assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0); - puts(addr); - } - - printf("canonical name: %s\n", strna(ai->ai_canonname)); - - return 0; -} - -static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata) { - assert_se(q); - - if (ret != 0) { - log_error("getnameinfo error: %s %i", gai_strerror(ret), ret); - return 0; - } - - printf("Host: %s — Serv: %s\n", strna(host), strna(serv)); - return 0; -} - -int main(int argc, char *argv[]) { - _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 = { - .ai_family = PF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME - }; - - struct sockaddr_in sa = { - .sin_family = AF_INET, - .sin_port = htons(80) - }; - - assert_se(sd_resolve_default(&resolve) >= 0); - - /* Test a floating resolver query */ - sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); - - /* Make a name -> address query */ - r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); - if (r < 0) - log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); - - /* Make an address -> name query */ - sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71"); - r = sd_resolve_getnameinfo(resolve, &q2, (struct sockaddr*) &sa, sizeof(sa), 0, SD_RESOLVE_GET_BOTH, getnameinfo_handler, NULL); - if (r < 0) - log_error_errno(r, "sd_resolve_getnameinfo(): %m"); - - /* 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"); - } - } - - return 0; -} diff --git a/src/libsystemd/sd-utf8/Makefile b/src/libsystemd/sd-utf8/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/libsystemd/sd-utf8/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/libsystemd/sd-utf8/sd-utf8.c b/src/libsystemd/sd-utf8/sd-utf8.c deleted file mode 100644 index 33a5a04ea1..0000000000 --- a/src/libsystemd/sd-utf8/sd-utf8.c +++ /dev/null @@ -1,35 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-utf8.h" - -#include "utf8.h" -#include "util.h" - -_public_ const char *sd_utf8_is_valid(const char *s) { - assert_return(s, NULL); - - return utf8_is_valid(s); -} - -_public_ const char *sd_ascii_is_valid(const char *s) { - assert_return(s, NULL); - - return ascii_is_valid(s); -} diff --git a/src/libsystemd/sd_booted.xml b/src/libsystemd/sd_booted.xml new file mode 100644 index 0000000000..4dd674b8ea --- /dev/null +++ b/src/libsystemd/sd_booted.xml @@ -0,0 +1,95 @@ + + + + + + + + + sd_booted + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_booted + 3 + + + + sd_booted + Test whether the system is running the systemd init system + + + + + #include <systemd/sd-daemon.h> + + + int sd_booted + void + + + + + + Description + sd_booted() checks whether the system + was booted up using the systemd init system. + + + + Return Value + + On failure, this call returns a negative errno-style error + code. If the system was booted up with systemd as init system, + this call returns a positive return value, zero otherwise. + + + + Notes + + + + Internally, this function checks whether the directory + /run/systemd/system/ exists. A simple check + like this can also be implemented trivially in shell or any other + language. + + + + See Also + + systemd1, + sd-daemon3 + + + + diff --git a/src/libsystemd/sd_bus_add_match.xml b/src/libsystemd/sd_bus_add_match.xml new file mode 100644 index 0000000000..8bcf7164a0 --- /dev/null +++ b/src/libsystemd/sd_bus_add_match.xml @@ -0,0 +1,119 @@ + + + + + + + + + sd_bus_add_match + systemd + + + + Julian + Orth + ju.orth@gmail.com + + + + + + sd_bus_add_match + 3 + + + + sd_bus_add_match + + Add a match rule for message dispatching + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_add_match + sd_bus *bus + sd_bus_slot **slot + const char *match + sd_bus_message_handler_t callback + void *userdata + + + + typedef int (*sd_bus_message_handler_t) + sd_bus_message *m + void *userdata + sd_bus_error *ret_error + + + + + + Description + + + sd_bus_add_match() adds a match rule used to dispatch + incoming messages. The syntax of the rule passed in + match is described in the + D-Bus Specification. + + + + The message m passed to the callback is only + borrowed, that is, the callback should not call + sd_bus_message_unref3 + on it. If the callback wants to hold on to the message beyond the lifetime + of the callback, it needs to call + sd_bus_message_ref3 + to create a new reference. + + + + If an error occurs during the callback invocation, the callback should + return a negative error number. If it wants other callbacks that match the + same rule to be called, it should return 0. Otherwise it should return a + positive integer. + + + + + Return Value + + + On success, sd_bus_add_match() returns 0 or a + positive integer. On failure, it returns a negative errno-style error + code. + + + + + See Also + + + systemd1, + sd-bus3, + + + + diff --git a/src/libsystemd/sd_bus_creds_get_pid.xml b/src/libsystemd/sd_bus_creds_get_pid.xml new file mode 100644 index 0000000000..4c05835568 --- /dev/null +++ b/src/libsystemd/sd_bus_creds_get_pid.xml @@ -0,0 +1,566 @@ + + + + + + + + + sd_bus_creds_get_pid + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_creds_get_pid + 3 + + + + sd_bus_creds_get_pid + sd_bus_creds_get_ppid + sd_bus_creds_get_tid + sd_bus_creds_get_uid + sd_bus_creds_get_euid + sd_bus_creds_get_suid + sd_bus_creds_get_fsuid + sd_bus_creds_get_gid + sd_bus_creds_get_egid + sd_bus_creds_get_sgid + sd_bus_creds_get_fsgid + sd_bus_creds_get_supplementary_gids + sd_bus_creds_get_comm + sd_bus_creds_get_tid_comm + sd_bus_creds_get_exe + sd_bus_creds_get_cmdline + sd_bus_creds_get_cgroup + sd_bus_creds_get_unit + sd_bus_creds_get_slice + sd_bus_creds_get_user_unit + sd_bus_creds_get_user_slice + sd_bus_creds_get_session + sd_bus_creds_get_owner_uid + sd_bus_creds_has_effective_cap + sd_bus_creds_has_permitted_cap + sd_bus_creds_has_inheritable_cap + sd_bus_creds_has_bounding_cap + sd_bus_creds_get_selinux_context + sd_bus_creds_get_audit_session_id + sd_bus_creds_get_audit_login_uid + sd_bus_creds_get_tty + sd_bus_creds_get_unique_name + sd_bus_creds_get_well_known_names + sd_bus_creds_get_description + + Retrieve fields from a credentials object + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_creds_get_pid + sd_bus_creds *c + pid_t *pid + + + + int sd_bus_creds_get_ppid + sd_bus_creds *c + pid_t *ppid + + + + int sd_bus_creds_get_tid + sd_bus_creds *c + pid_t *tid + + + + int sd_bus_creds_get_uid + sd_bus_creds *c + uid_t *uid + + + + int sd_bus_creds_get_euid + sd_bus_creds *c + uid_t *uid + + + + int sd_bus_creds_get_suid + sd_bus_creds *c + uid_t *uid + + + + int sd_bus_creds_get_fsuid + sd_bus_creds *c + uid_t *uid + + + + int sd_bus_creds_get_gid + sd_bus_creds *c + gid_t *gid + + + + int sd_bus_creds_get_egid + sd_bus_creds *c + gid_t *gid + + + + int sd_bus_creds_get_sgid + sd_bus_creds *c + gid_t *gid + + + + int sd_bus_creds_get_fsgid + sd_bus_creds *c + gid_t *gid + + + + int sd_bus_creds_get_supplementary_gids + sd_bus_creds *c + const gid_t **gids + + + + int sd_bus_creds_get_comm + sd_bus_creds *c + const char **comm + + + + int sd_bus_creds_get_tid_comm + sd_bus_creds *c + const char **comm + + + + int sd_bus_creds_get_exe + sd_bus_creds *c + const char **exe + + + + int sd_bus_creds_get_cmdline + sd_bus_creds *c + char ***cmdline + + + + int sd_bus_creds_get_cgroup + sd_bus_creds *c + const char **cgroup + + + + int sd_bus_creds_get_unit + sd_bus_creds *c + const char **unit + + + + int sd_bus_creds_get_slice + sd_bus_creds *c + const char **slice + + + + int sd_bus_creds_get_user_unit + sd_bus_creds *c + const char **unit + + + + int sd_bus_creds_get_user_slice + sd_bus_creds *c + const char **slice + + + + int sd_bus_creds_get_session + sd_bus_creds *c + const char **slice + + + + int sd_bus_creds_get_owner_uid + sd_bus_creds *c + uid_t *uid + + + + int sd_bus_creds_has_effective_cap + sd_bus_creds *c + int capability + + + + int sd_bus_creds_has_permitted_cap + sd_bus_creds *c + int capability + + + + int sd_bus_creds_has_inheritable_cap + sd_bus_creds *c + int capability + + + + int sd_bus_creds_has_bounding_cap + sd_bus_creds *c + int capability + + + + int sd_bus_creds_get_selinux_context + sd_bus_creds *c + const char **context + + + + int sd_bus_creds_get_audit_session_id + sd_bus_creds *c + uint32_t *sessionid + + + + int sd_bus_creds_get_audit_login_uid + sd_bus_creds *c + uid_t *loginuid + + + + int sd_bus_creds_get_tty + sd_bus_creds *c + const char **tty + + + + int sd_bus_creds_get_unique_name + sd_bus_creds *c + const char **name + + + + int sd_bus_creds_get_well_known_names + sd_bus_creds *c + char ***name + + + + int sd_bus_creds_get_description + sd_bus_creds *c + const char **name + + + + + + + Description + + These functions return credential information from an + sd_bus_creds object. Credential objects may + be created with + sd_bus_creds_new_from_pid3, + in which case they describe the credentials of the process + identified by the specified PID, with + sd_bus_get_name_creds3, + in which case they describe the credentials of a bus peer + identified by the specified bus name, with + sd_bus_get_owner_creds3, + in which case they describe the credentials of the creator of a + bus, or with + sd_bus_message_get_creds3, + in which case they describe the credentials of the sender of the + message. + + Not all credential fields are part of every + sd_bus_creds object. Use + sd_bus_creds_get_mask3 + to determine the mask of fields available. + + sd_bus_creds_get_pid() will retrieve + the PID (process identifier). Similarly, + sd_bus_creds_get_ppid() will retrieve the + parent PID. Note that PID 1 has no parent process, in which case + -ENXIO is returned. + + sd_bus_creds_get_tid() will retrieve the + TID (thread identifier). + + sd_bus_creds_get_uid() will retrieve + the numeric UID (user identifier). Similarly, + sd_bus_creds_get_euid() returns the effective + UID, sd_bus_creds_get_suid() the saved UID + and sd_bus_creds_get_fsuid() the file system + UID. + + sd_bus_creds_get_gid() will retrieve the + numeric GID (group identifier). Similarly, + sd_bus_creds_get_egid() returns the effective + GID, sd_bus_creds_get_sgid() the saved GID + and sd_bus_creds_get_fsgid() the file system + GID. + + sd_bus_creds_get_supplementary_gids() + will retrieve the supplementary GIDs list. + + sd_bus_creds_get_comm() will retrieve the + comm field (truncated name of the executable, as stored in + /proc/pid/comm). + + + sd_bus_creds_get_tid_comm() will retrieve + the comm field of the thread (as stored in + /proc/pid/task/tid/comm). + + + sd_bus_creds_get_exe() will retrieve + the path to the program executable (as stored in the + /proc/pid/exe + link, but with the (deleted) suffix removed). Note + that kernel threads do not have an executable path, in which case + -ENXIO is returned. + + sd_bus_creds_get_cmdline() will + retrieve an array of command line arguments (as stored in + /proc/pid/cmdline). Note + that kernel threads do not have a command line, in which case + -ENXIO is returned. + + sd_bus_creds_get_cgroup() will retrieve + the cgroup path. See cgroups.txt. + + + sd_bus_creds_get_unit() will retrieve + the systemd unit name (in the system instance of systemd) that the + process is a part of. See + systemd.unit5. For + processes that are not part of a unit, returns -ENXIO. + + + sd_bus_creds_get_user_unit() will + retrieve the systemd unit name (in the user instance of systemd) + that the process is a part of. See + systemd.unit5. For + processes that are not part of a user unit, returns -ENXIO. + + + sd_bus_creds_get_slice() will retrieve + the systemd slice (a unit in the system instance of systemd) that + the process is a part of. See + systemd.slice5. Similarly, + sd_bus_creds_get_user_slice() retrieves the + systemd slice of the process, in the user instance of systemd. + + + sd_bus_creds_get_session() will + retrieve the identifier of the login session that the process is + a part of. See + systemd-logind.service8. For + processes that are not part of a session, returns -ENXIO. + + + sd_bus_creds_get_owner_uid() will + retrieve the numeric UID (user identifier) of the user who owns + the login session that the process is a part of. See + systemd-logind.service8. + For processes that are not part of a session, returns -ENXIO. + + + sd_bus_creds_has_effective_cap() will check whether the capability specified by + capability was set in the effective capabilities mask. A positive return value means that it + was set, zero means that it was not set, and a negative return value indicates an error. See capabilities7 and the + AmbientCapabilities= and CapabilityBoundingSet= settings in + systemd.exec5. + + + sd_bus_creds_has_permitted_cap() is + similar to sd_bus_creds_has_effective_cap(), + but will check the permitted capabilities mask. + + sd_bus_creds_has_inheritable_cap() is + similar to sd_bus_creds_has_effective_cap(), + but will check the inheritable capabilities mask. + + sd_bus_creds_has_bounding_cap() is + similar to sd_bus_creds_has_effective_cap(), + but will check the bounding capabilities mask. + + sd_bus_creds_get_selinux_context() will + retrieve the SELinux security context (label) of the process. + + sd_bus_creds_get_audit_session_id() + will retrieve the audit session identifier of the process. Returns + -ENXIO for processes that are not part of an audit session. + + sd_bus_creds_get_audit_login_uid() will + retrieve the audit user login identifier (the identifier of the + user who is "responsible" for the session). Returns -ENXIO for + processes that are not part of an audit session. + + sd_bus_creds_get_tty() will retrieve + the controlling TTY, without the prefixing "/dev/". Returns -ENXIO + for processes that have no controlling TTY. + + sd_bus_creds_get_unique_name() will + retrieve the D-Bus unique name. See The + D-Bus specification. + + sd_bus_creds_get_well_known_names() will + retrieve the set of D-Bus well-known names. See The + D-Bus specification. + + sd_bus_creds_get_description() will + retrieve a descriptive name of the bus connection of the + peer. This name is useful to discern multiple bus connections by + the same peer, and may be altered by the peer with the + sd_bus_set_description3 + call. + + All functions that take a const + char** parameter will store the answer there as an + address of a NUL-terminated string. It will be valid as long as + c remains valid, and should not be freed or + modified by the caller. + + All functions that take a char*** + parameter will store the answer there as an address of an array + of strings. Each individual string is NUL-terminated, and the + array is NULL-terminated as a whole. It will be valid as long as + c remains valid, and should not be freed or + modified by the caller. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error code. + + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENODATA + + The given field is not available in the + credentials object c. + + + + + -ENXIO + + The given field is not specified for the described + process or peer. This will be returned by + sd_bus_get_unit(), + sd_bus_get_slice(), + sd_bus_get_user_unit(), + sd_bus_get_user_slice(), + sd_bus_get_session(), and + sd_bus_get_owner_uid() if the process is + not part of a systemd system unit, systemd user unit, systemd + slice, or logind session. It will also be returned by + sd_bus_creds_get_exe() and + sd_bus_creds_get_cmdline() for kernel + threads (since these are not started from an executable binary, + nor have a command line), and by + sd_bus_creds_get_audit_session_id() and + sd_bus_creds_get_audit_login_uid() when + the process is not part of an audit session, and + sd_bus_creds_get_tty() if the process has + no controlling TTY. + + + + + + -EINVAL + + Specified pointer parameter is NULL. + + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + sd_bus_creds_get_pid() and the other + functions described here are available as a shared library, which + can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_creds_new_from_pid2, + fork2, + execve2, + credentials7, + free3, + proc5, + systemd.journal-fields7 + + + + diff --git a/src/libsystemd/sd_bus_creds_new_from_pid.xml b/src/libsystemd/sd_bus_creds_new_from_pid.xml new file mode 100644 index 0000000000..082f7b67db --- /dev/null +++ b/src/libsystemd/sd_bus_creds_new_from_pid.xml @@ -0,0 +1,353 @@ + + + + + + + + + sd_bus_creds_new_from_pid + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_creds_new_from_pid + 3 + + + + sd_bus_creds_new_from_pid + sd_bus_creds_get_mask + sd_bus_creds_get_augmented_mask + sd_bus_creds_ref + sd_bus_creds_unref + sd_bus_creds_unrefp + + Retrieve credentials object for the specified PID + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_creds_new_from_pid + pid_t pid + uint64_t creds_mask + sd_bus_creds **ret + + + + uint64_t sd_bus_creds_get_mask + const sd_bus_creds *c + + + + uint64_t sd_bus_creds_get_augmented_mask + const sd_bus_creds *c + + + + sd_bus_creds *sd_bus_creds_ref + sd_bus_creds *c + + + + sd_bus_creds *sd_bus_creds_unref + sd_bus_creds *c + + + + void sd_bus_creds_unrefp + sd_bus_creds **c + + + + + SD_BUS_CREDS_PID, + SD_BUS_CREDS_PPID, + SD_BUS_CREDS_TID, + SD_BUS_CREDS_UID, + SD_BUS_CREDS_EUID, + SD_BUS_CREDS_SUID, + SD_BUS_CREDS_FSUID, + SD_BUS_CREDS_GID, + SD_BUS_CREDS_EGID, + SD_BUS_CREDS_SGID, + SD_BUS_CREDS_FSGID, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS, + SD_BUS_CREDS_COMM, + SD_BUS_CREDS_TID_COMM, + SD_BUS_CREDS_EXE, + SD_BUS_CREDS_CMDLINE, + SD_BUS_CREDS_CGROUP, + SD_BUS_CREDS_UNIT, + SD_BUS_CREDS_SLICE, + SD_BUS_CREDS_USER_UNIT, + SD_BUS_CREDS_USER_SLICE, + SD_BUS_CREDS_SESSION, + SD_BUS_CREDS_OWNER_UID, + SD_BUS_CREDS_EFFECTIVE_CAPS, + SD_BUS_CREDS_PERMITTED_CAPS, + SD_BUS_CREDS_INHERITABLE_CAPS, + SD_BUS_CREDS_BOUNDING_CAPS, + SD_BUS_CREDS_SELINUX_CONTEXT, + SD_BUS_CREDS_AUDIT_SESSION_ID, + SD_BUS_CREDS_AUDIT_LOGIN_UID, + SD_BUS_CREDS_TTY, + SD_BUS_CREDS_UNIQUE_NAME, + SD_BUS_CREDS_WELL_KNOWN_NAMES, + SD_BUS_CREDS_DESCRIPTION, + SD_BUS_CREDS_AUGMENT, + _SD_BUS_CREDS_ALL + + + + + Description + + sd_bus_creds_new_from_pid() creates a + new credentials object and fills it with information about the + process pid. The pointer to this object + will be stored in the ret pointer. Note that + credential objects may also be created and retrieved via + sd_bus_get_name_creds3, + sd_bus_get_owner_creds3 + and + sd_bus_message_get_creds3. + + The information that will be stored is determined by + creds_mask. It may contain a subset of ORed + constants SD_BUS_CREDS_PID, + SD_BUS_CREDS_PPID, + SD_BUS_CREDS_TID, + SD_BUS_CREDS_UID, + SD_BUS_CREDS_EUID, + SD_BUS_CREDS_SUID, + SD_BUS_CREDS_FSUID, + SD_BUS_CREDS_GID, + SD_BUS_CREDS_EGID, + SD_BUS_CREDS_SGID, + SD_BUS_CREDS_FSGID, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS, + SD_BUS_CREDS_COMM, + SD_BUS_CREDS_TID_COMM, + SD_BUS_CREDS_EXE, + SD_BUS_CREDS_CMDLINE, + SD_BUS_CREDS_CGROUP, + SD_BUS_CREDS_UNIT, + SD_BUS_CREDS_SLICE, + SD_BUS_CREDS_USER_UNIT, + SD_BUS_CREDS_USER_SLICE, + SD_BUS_CREDS_SESSION, + SD_BUS_CREDS_OWNER_UID, + SD_BUS_CREDS_EFFECTIVE_CAPS, + SD_BUS_CREDS_PERMITTED_CAPS, + SD_BUS_CREDS_INHERITABLE_CAPS, + SD_BUS_CREDS_BOUNDING_CAPS, + SD_BUS_CREDS_SELINUX_CONTEXT, + SD_BUS_CREDS_AUDIT_SESSION_ID, + SD_BUS_CREDS_AUDIT_LOGIN_UID, + SD_BUS_CREDS_TTY, + SD_BUS_CREDS_UNIQUE_NAME, + SD_BUS_CREDS_WELL_KNOWN_NAMES, and + SD_BUS_CREDS_DESCRIPTION. Use the special + value _SD_BUS_CREDS_ALL to request all + supported fields. The SD_BUS_CREDS_AUGMENT + constant may not be ORed into the mask for invocations of + sd_bus_creds_new_from_pid(). + + Fields can be retrieved from the credentials object using + sd_bus_creds_get_pid3 + and other functions which correspond directly to the constants + listed above. + + A mask of fields which were actually successfully retrieved + can be retrieved with + sd_bus_creds_get_mask(). If the credentials + object was created with + sd_bus_creds_new_from_pid(), this will be a + subset of fields requested in creds_mask. + + + Similar to sd_bus_creds_get_mask(), the + function sd_bus_creds_get_augmented_mask() + returns a bitmask of field constants. The mask indicates which + credential fields have been retrieved in a non-atomic fashion. For + credential objects created via + sd_bus_creds_new_from_pid(), this mask will be + identical to the mask returned by + sd_bus_creds_get_mask(). However, for + credential objects retrieved via + sd_bus_get_name_creds(), this mask will be set + for the credential fields that could not be determined atomically + at peer connection time, and which were later added by reading + augmenting credential data from + /proc. Similarly, for credential objects + retrieved via sd_bus_get_owner_creds(), the + mask is set for the fields that could not be determined atomically + at bus creation time, but have been augmented. Similarly, for + credential objects retrieved via + sd_bus_message_get_creds(), the mask is set + for the fields that could not be determined atomically at message + sending time, but have been augmented. The mask returned by + sd_bus_creds_get_augmented_mask() is always a + subset of (or identical to) the mask returned by + sd_bus_creds_get_mask() for the same + object. The latter call hence returns all credential fields + available in the credential object, the former then marks the + subset of those that have been augmented. Note that augmented + fields are unsuitable for authorization decisions, as they may be + retrieved at different times, thus being subject to races. Hence, + augmented fields should be used exclusively for informational + purposes. + + + sd_bus_creds_ref() creates a new + reference to the credentials object c. This + object will not be destroyed until + sd_bus_creds_unref() has been called as many + times plus once more. Once the reference count has dropped to zero, + c cannot be used anymore, so further + calls to sd_bus_creds_ref(c) or + sd_bus_creds_unref(c) are illegal. + + sd_bus_creds_unref() destroys a reference + to c. + + sd_bus_creds_unrefp() is similar to + sd_bus_creds_unref() but takes a pointer to a + pointer to an sd_bus_creds object. This call is useful in + conjunction with GCC's and LLVM's Clean-up + Variable Attribute. Note that this function is defined as + inline function. + + sd_bus_creds_ref(), + sd_bus_creds_unref() and + sd_bus_creds_unrefp() execute no operation if + the passed in bus credentials object is + NULL. + + + + + Return Value + + On success, sd_bus_creds_new_from_pid() + returns 0 or a positive integer. On failure, it returns a negative + errno-style error code. + + sd_bus_creds_get_mask() returns the + mask of successfully acquired fields. + + sd_bus_creds_get_augmented_mask() + returns the mask of fields that have been augmented from data in + /proc, and are thus not suitable for + authorization decisions. + + sd_bus_creds_ref() always returns the + argument. + + sd_bus_creds_unref() always returns + NULL. + + + + Reference ownership + + Function sd_bus_creds_new_from_pid() + creates a new object and the caller owns the sole reference. When + not needed anymore, this reference should be destroyed with + sd_bus_creds_unref3. + + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ESRCH + + Specified pid could not + be found. + + + + -EINVAL + + Specified parameter is invalid + (NULL in case of output + parameters). + + + + -ENOMEM + + Memory allocation failed. + + + + -EOPNOTSUPP + + One of the requested fields is unknown to the local system. + + + + + + Notes + + sd_bus_creds_new_from_pid() and the + other calls described here are available as a shared library, + which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_creds_get_pid3, + sd_bus_get_name_creds3, + sd_bus_get_owner_creds3, + sd_bus_message_get_creds3 + + + + diff --git a/src/libsystemd/sd_bus_default.xml b/src/libsystemd/sd_bus_default.xml new file mode 100644 index 0000000000..6d5a90de72 --- /dev/null +++ b/src/libsystemd/sd_bus_default.xml @@ -0,0 +1,312 @@ + + + + + + + + + sd_bus_default + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_default + 3 + + + + sd_bus_default + sd_bus_default_user + sd_bus_default_system + + sd_bus_open + sd_bus_open_user + sd_bus_open_system + sd_bus_open_system_remote + sd_bus_open_system_machine + + Acquire a connection to a system or user bus + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_default + sd_bus **bus + + + + int sd_bus_default_user + sd_bus **bus + + + + int sd_bus_default_system + sd_bus **bus + + + + int sd_bus_open + sd_bus **bus + + + + int sd_bus_open_user + sd_bus **bus + + + + int sd_bus_open_system + sd_bus **bus + + + + int sd_bus_open_system_remote + sd_bus **bus + const char *host + + + + int sd_bus_open_system_machine + sd_bus **bus + const char *machine + + + + + + + Description + + sd_bus_default() acquires a bus + connection object to the user bus when invoked in user context, or + to the system bus otherwise. The connection object is associated + with the calling thread. Each time the function is invoked from + the same thread, the same object is returned, but its reference + count is increased by one, as long as at least one reference is + kept. When the last reference to the connection is dropped (using + the + sd_bus_unref3 + call), the connection is terminated. Note that the connection is + not automatically terminated when the associated thread ends. It + is important to drop the last reference to the bus connection + explicitly before the thread ends, as otherwise, the connection will + leak. Also, queued but unread or unwritten messages keep the + bus referenced, see below. + + sd_bus_default_user() returns a user + bus connection object associated with the calling thread. + sd_bus_default_system() is similar, but + connects to the system bus. Note that + sd_bus_default() is identical to these two + calls, depending on the execution context. + + sd_bus_open() creates a new, + independent bus connection to the user bus when invoked in user + context, or the system bus + otherwise. sd_bus_open_user() is similar, but + connects only to the user bus. + sd_bus_open_system() does the same, but + connects to the system bus. In contrast to + sd_bus_default(), + sd_bus_default_user(), and + sd_bus_default_system(), these calls return + new, independent connection objects that are not associated with + the invoking thread and are not shared between multiple + invocations. It is recommended to share connections per thread to + efficiently make use the available resources. Thus, it is + recommended to use sd_bus_default(), + sd_bus_default_user() and + sd_bus_default_system() to connect to the + user or system buses. + + If the $DBUS_SESSION_BUS_ADDRESS environment + variable is set + (cf. environ7), + it will be used as the address of the user bus. This variable can + contain multiple addresses separated by ;. If + this variable is not set, a suitable default for the default user + D-Bus instance will be used. + + If the $DBUS_SYSTEM_BUS_ADDRESS + environment variable is set, it will be used as the address of the + system bus. This variable uses the same syntax as + $DBUS_SESSION_BUS_ADDRESS. If this variable is + not set, a suitable default for the default system D-Bus instance + will be used. + + sd_bus_open_system_remote() connects to + the system bus on the specified host using + ssh1. host + consists of an optional user name followed by the + @ symbol, and the hostname. + + + sd_bus_open_system_machine() connects + to the system bus in the specified machine, + where machine is the name of a local + container. See + machinectl1 + for more information about the "machine" concept. Note that + connections into local containers are only available to privileged + processes at this time. + + These calls allocate a bus connection object and initiate + the connection to a well-known bus of some form. An alternative to + using these high-level calls is to create an unconnected bus + object with + sd_bus_new3 + and to connect it with + sd_bus_start3. + + + + + + Reference ownership + The functions sd_bus_open(), + sd_bus_open_user(), + sd_bus_open_system(), + sd_bus_open_system_remote(), and + sd_bus_open_system_machine() return a new + connection object and the caller owns the sole reference. When not + needed anymore, this reference should be destroyed with + sd_bus_unref3. + + + The functions sd_bus_default(), + sd_bus_default_user() and + sd_bus_default_system() do not necessarily + create a new object, but increase the connection reference of an + existing connection object by one. Use + sd_bus_unref3 + to drop the reference. + + Queued but unwritten/unread messages also keep a reference + to their bus connection object. For this reason, even if an + application dropped all references to a bus connection, it might + not get destroyed right away. Until all incoming queued + messages are read, and until all outgoing unwritten messages are + written, the bus object will stay + alive. sd_bus_flush() may be used to write + all outgoing queued messages so they drop their references. To + flush the unread incoming messages, use + sd_bus_close(), which will also close the bus + connection. When using the default bus logic, it is a good idea to + first invoke sd_bus_flush() followed by + sd_bus_close() when a thread or process + terminates, and thus its bus connection object should be + freed. + + The life cycle of the default bus connection should be the + responsibility of the code that creates/owns the thread the + default bus connection object is associated with. Library code + should neither call sd_bus_flush() nor + sd_bus_close() on default bus objects unless + it does so in its own private, self-allocated thread. Library code + should not use the default bus object in other threads unless it + is clear that the program using it will life cycle the bus + connection object and flush and close it before exiting from the + thread. In libraries where it is not clear that the calling + program will life cycle the bus connection object, it is hence + recommended to use sd_bus_open_system() + instead of sd_bus_default_system() and + related calls. + + + + Return Value + + On success, these calls return 0 or a positive + integer. On failure, these calls return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + The specified parameters are invalid. + + + + -ENOMEM + + Memory allocation failed. + + + + -ESOCKTNOSUPPORT + + The protocol version required to connect to the selected bus is not supported. + + + + In addition, any further connection-related errors may be + by returned. See sd_bus_send3. + + + + Notes + + sd_bus_open_user() and the other + functions described here are available as a shared library, which + can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_new3, + sd_bus_ref3, + sd_bus_unref3, + ssh1, + systemd-machined.service8, + machinectl1 + + + + diff --git a/src/libsystemd/sd_bus_error.xml b/src/libsystemd/sd_bus_error.xml new file mode 100644 index 0000000000..c2d7ee389b --- /dev/null +++ b/src/libsystemd/sd_bus_error.xml @@ -0,0 +1,389 @@ + + + + + + + + + sd_bus_error + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_error + 3 + + + + sd_bus_error + SD_BUS_ERROR_MAKE_CONST + SD_BUS_ERROR_NULL + sd_bus_error_free + sd_bus_error_set + sd_bus_error_setf + sd_bus_error_set_const + sd_bus_error_set_errno + sd_bus_error_set_errnof + sd_bus_error_set_errnofv + sd_bus_error_get_errno + sd_bus_error_copy + sd_bus_error_is_set + sd_bus_error_has_name + + sd-bus error handling + + + + + #include <systemd/sd-bus.h> + + typedef struct { + const char *name; + const char *message; + ... +} sd_bus_error; + + + SD_BUS_ERROR_MAKE_CONST(name, message) + + + SD_BUS_ERROR_NULL + + + + void sd_bus_error_free + sd_bus_error *e + + + + int sd_bus_error_set + sd_bus_error *e + const char *name + const char *message + + + + int sd_bus_error_setf + sd_bus_error *e + const char *name + const char *format + ... + + + + int sd_bus_error_set_const + sd_bus_error *e + const char *name + const char *message + + + + int sd_bus_error_set_errno + sd_bus_error *e + int error + + + + int sd_bus_error_set_errnof + sd_bus_error *e + int error + const char *format + ... + + + + int sd_bus_error_set_errnofv + sd_bus_error *e + int error + const char *format + va_list ap + + + + int sd_bus_error_get_errno + const sd_bus_error *e + + + + int sd_bus_error_copy + sd_bus_error *dst + const sd_bus_error *e + + + + int sd_bus_error_is_set + const sd_bus_error *e + + + + int sd_bus_error_has_name + const sd_bus_error *e + const char *name + + + + + + + Description + + The sd_bus_error structure carries + information about a D-Bus error condition. The functions described + below may be used to set and query fields in this structure. The + name field contains a short identifier + of an error. It should follow the rules for error names described + in the D-Bus specification, subsection Valid + Names. A number of common, standardized error names are + described in + sd-bus-errors3, + but additional domain-specific errors may be defined by + applications. The message field usually + contains a human-readable string describing the details, but might + be NULL. An unset sd_bus_error structure + should have both fields initialized to NULL. Set an error + structure to SD_BUS_ERROR_NULL in order to + reset both fields to NULL. When no longer necessary, resources + held by the sd_bus_errorstructure should + be destroyed with sd_bus_error_free(). + + sd_bus_error_set() sets an error + structure to the specified name and message strings. The strings + will be copied into internal, newly allocated memory. It is + essential to free the error structure again when it is not + required anymore (see above). The function will return an + errno-like negative value (see errno3) + determined from the specified error name. Various well-known + D-Bus errors are converted to well-known errno + counterparts, and the other ones to -EIO. See + sd-bus-errors3 + for a list of well-known error names. Additional error mappings + may be defined with + sd_bus_error_add_map3. If + e is NULL, no error structure is initialized, + but the error is still converted into an + errno-style error. If + name is NULL, it is + assumed that no error occurred, and 0 is returned. This means that + this function may be conveniently used in a + return statement. If + message is NULL, no message is set. This + call can fail if no memory may be allocated for the name and + message strings, in which case an + SD_BUS_ERROR_NO_MEMORY error might be set + instead and -ENOMEM be returned. Do not use this call on error + structures that are already initialized. If you intend to reuse an + error structure, free the old data stored in it with + sd_bus_error_free() first. + + sd_bus_error_setf() is similar to + sd_bus_error_set(), but takes a printf3 + format string and corresponding arguments to generate the + message field. + + sd_bus_error_set_const() is similar to + sd_bus_error_set(), but the string parameters + are not copied internally, and must hence remain constant and + valid for the lifetime of e. Use this call + to avoid memory allocations when setting error structures. Since + this call does not allocate memory, it will not fail with an + out-of-memory condition as + sd_bus_error_set() can, as described + above. Alternatively, the + SD_BUS_ERROR_MAKE_CONST() macro may be used + to generate a literal, constant bus error structure + on-the-fly. + + sd_bus_error_set_errno() will set + name from an + errno-like value that is converted to a D-Bus + error. strerror_r3 + will be used to set message. Well-known + D-Bus error names will be used for name + if applicable, otherwise a name in the + System.Error. namespace will be generated. The + sign of the specified error number is ignored. The absolute value + is used implicitly. The call always returns a negative value, for + convenient usage in return statements. This + call might fail due to lack of memory, in which case an + SD_BUS_ERROR_NO_MEMORY error is set instead, + and -ENOMEM is returned. + + sd_bus_error_set_errnof() is similar to + sd_bus_error_set_errno(), but in addition to + error, takes a printf3 + format string and corresponding arguments. The + message field will be generated from + format and the arguments. + + sd_bus_error_set_errnofv() is similar to + sd_bus_error_set_errnof(), but takes the + format string parameters as va_arg3 + parameter list. + + sd_bus_error_get_errno() converts the + name field of an error structure to an + errno-like (positive) value using the same + rules as sd_bus_error_set(). If + e is NULL, 0 will be + returned. + + sd_bus_error_copy() will initialize + dst using the values in + e. If the strings in + e were set using + sd_bus_set_error_const(), they will be shared. + Otherwise, they will be copied. Returns a converted + errno-like, negative error code. + + sd_bus_error_is_set() will return a + non-zero value if e is + non-NULL and an error has been set, + false otherwise. + + sd_bus_error_has_name() will return a + non-zero value if e is + non-NULL and an error with the same + name has been set, + false otherwise. + + sd_bus_error_free() will destroy + resources held by e. The parameter itself + will not be deallocated, and must be free3d + by the caller if necessary. The function may also be called safely + on unset errors (error structures with both fields set to NULL), + in which case it performs no operation. This call will reset the + error structure after freeing the data, so that all fields are set + to NULL. The structure may be reused afterwards. + + + + Return Value + + The functions sd_bus_error_set(), + sd_bus_error_setf(), and + sd_bus_error_set_const(), when successful, + return the negative errno value corresponding to the + name parameter. The functions + sd_bus_error_set_errno(), + sd_bus_error_set_errnof() and + sd_bus_error_set_errnofv(), when successful, + return the negative value of the error + parameter. If an error occurs, one of the negative error values + listed below will be returned. + + sd_bus_error_get_errno() returns + false when e is + NULL, and a positive errno value mapped from + e->name otherwise. + + sd_bus_error_copy() returns 0 or a + positive integer on success, and a negative error value converted + from the error name otherwise. + + sd_bus_error_is_set() returns a + non-zero value when e and the + name field are + non-NULL, zero otherwise. + + sd_bus_error_has_name() returns a + non-zero value when e is + non-NULL and the + name field is equal to + name, zero otherwise. + + + + Reference ownership + sd_bus_error is not reference + counted. Users should destroy resources held by it by calling + sd_bus_error_free(). Usually, error structures + are allocated on the stack or passed in as function parameters, + but they may also be allocated dynamically, in which case it is + the duty of the caller to free3 + the memory held by the structure itself after freeing its contents + with sd_bus_error_free(). + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + Error was already set in + sd_bus_error structure when one the + error-setting functions was called. + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + sd_bus_set_error() and other functions + described here are available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd-bus-errors3, + sd_bus_error_add_map3, + errno3, + strerror_r3 + + + + diff --git a/src/libsystemd/sd_bus_error_add_map.xml b/src/libsystemd/sd_bus_error_add_map.xml new file mode 100644 index 0000000000..7dc1ef6c90 --- /dev/null +++ b/src/libsystemd/sd_bus_error_add_map.xml @@ -0,0 +1,173 @@ + + + + + + + + + sd_bus_error_add_map + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_bus_error_add_map + 3 + + + + sd_bus_error_add_map + sd_bus_error_map + SD_BUS_ERROR_MAP + SD_BUS_ERROR_END + + Additional sd-dbus error mappings + + + + + #include <systemd/sd-bus.h> + + typedef struct { + const char *name; + int code; + ... +} sd_bus_error_map; + + + + + SD_BUS_ERROR_MAP(name, code) + + + SD_BUS_ERROR_MAP_END + + + + int sd_bus_error_add_map + const sd_bus_map *map + + + + + + Description + + The sd_bus_error_add_map() call may be + used to register additional mappings for converting D-Bus errors + to GNU/Linux errno-style errors. The mappings + defined with this call are consulted by calls such as + sd_bus_error_set3 + or + sd_bus_error_get_errno3. By + default, a number of generic, standardized mappings are known, as + documented in + sd-bus-errors3. Use + this call to add further, application-specific mappings. + + The function takes a pointer to an array of + sd_bus_error_map structures. A reference + to the specified array is added to the lookup tables for error + mappings. Note that the structure is not copied, and that it is hence + essential that the array stays available and constant during the + entire remaining runtime of the process. + + The mapping array should be put together with a series of + SD_BUS_ERROR_MAP() macro invocations that + take a literal name string and a (positive) + errno-style error number. The last entry of the + array should be an invocation of the + SD_BUS_ERROR_MAP_END macro. The array should not be + put together without use of these two macros. + + Note that the call is idempotent: it is safe to invoke it + multiple times with the parameter, which will only add the passed + mapping array once. + + Note that the memory allocated by this call is not intended + to be freed during the lifetime of the process. It should not be + freed explicitly. + + + + Return Value + + sd_bus_error_add_map() returns a + positive value when the new array was added to the lookup + tables. It returns zero when the same array was already added + before. On error, a negative errno-style error + code is returned. See below for known error codes. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + The specified mapping array is invalid. + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The various error definitions described here are available + as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_error3, + sd-bus-errors3, + errno3, + strerror_r3 + + + + diff --git a/src/libsystemd/sd_bus_get_fd.xml b/src/libsystemd/sd_bus_get_fd.xml new file mode 100644 index 0000000000..9f7019069f --- /dev/null +++ b/src/libsystemd/sd_bus_get_fd.xml @@ -0,0 +1,101 @@ + + + + + + + + + sd_bus_get_fd + systemd + + + + Julian + Orth + ju.orth@gmail.com + + + + + + sd_bus_get_fd + 3 + + + + sd_bus_get_fd + + Get the file descriptor connected to the message bus + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_get_fd + sd_bus *bus + + + + + + Description + + + sd_bus_get_fd() returns the file descriptor used to + communicate with the message bus. This descriptor can be used with + select3, + poll3, + or similar functions to wait for incoming messages. + + + + If the bus was created with the + sd_bus_set_fd3 + function, then the input_fd used in that call is + returned. + + + + + Return Value + + + Returns the file descriptor used for incoming messages from the message + bus. + + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_set_fd3, + + + + diff --git a/src/libsystemd/sd_bus_message_append.xml b/src/libsystemd/sd_bus_message_append.xml new file mode 100644 index 0000000000..77fce02eae --- /dev/null +++ b/src/libsystemd/sd_bus_message_append.xml @@ -0,0 +1,264 @@ + + + + + + + + + sd_bus_message_append + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_message_append + 3 + + + + sd_bus_message_append + + Attach fields to a D-Bus message based on a type + string + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_append + sd_bus_message *m + const char *types + ... + + + + + + Description + + The sd_bus_message_append() function + appends a sequence of fields to the D-Bus message object + m. The type string + types describes the types of the field + arguments that follow. For each type specified in the type string, + one or more arguments need to be specified, in the same order as + declared in the type string. + + The type string is composed of the elements shown in the + table below. It contains zero or more single "complete types". + Each complete type may be one of the basic types or a fully + described container type. A container type may be a structure with + the contained types, a variant, an array with its element type, or + a dictionary entry with the contained types. The type string is + NUL-terminated. + + In case of a basic type, one argument of the corresponding + type is expected. + + A structure is denoted by a sequence of complete types + between ( and ). This + sequence cannot be empty — it must contain at least one type. + Arguments corresponding to this nested sequence follow the same + rules as if they were not nested. + + A variant is denoted by v. Corresponding + arguments must begin with a type string denoting a complete type, + and following that, arguments corresponding to the specified type. + + + An array is denoted by a followed by a + complete type. Corresponding arguments must begin with the number of + entries in the array, followed by the entries themselves, + matching the element type of the array. + + A dictionary is an array of dictionary entries, denoted by + a followed by a pair of complete types between + { and }. The first of those + types must be a basic type. Corresponding arguments must begin + with the number of dictionary entries, followed by a pair of + values for each entry matching the element type of + the dictionary entries. + + For further details on the D-Bus type system, please consult + the D-Bus + Specification. + + + Item type specifiers + + + + + + + + + + a + SD_BUS_TYPE_ARRAY + array + determined by array type and size + int, followed by array contents + + + + v + SD_BUS_TYPE_VARIANT + variant + determined by the type argument + signature string, followed by variant contents + + + + ( + SD_BUS_TYPE_STRUCT_BEGIN + array start + determined by the nested types + structure contents + + + ) + SD_BUS_TYPE_STRUCT_END + array end + + + + { + SD_BUS_TYPE_DICT_ENTRY_BEGIN + dictionary entry start + determined by the nested types + dictionary contents + + + } + SD_BUS_TYPE_DICT_ENTRY_END + dictionary entry end + + + +
+ +
+ + + Types String Grammar + + types ::= complete_type* +complete_type ::= basic_type | variant | structure | array | dictionary +basic_type ::= "y" | "n" | "q" | "u" | "i" | "x" | "t" | "d" | + "b" | "h" | + "s" | "o" | "g" +variant ::= "v" +structure ::= "(" complete_type+ ")" +array ::= "a" complete_type +dictionary ::= "a" "{" basic_type complete_type "}" + + + + + Examples + + Append a single basic type (the string a string): + + + sd_bus_message *m; +... +sd_bus_message_append(m, "s", "a string"); + + Append all types of integers: + + uint8_t y = 1; +int16_t n = 2; +uint16_t q = 3; +int32_t i = 4; +uint32_t u = 5; +int32_t x = 6; +uint32_t t = 7; +double d = 8.0; +sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d); + + Append a structure composed of a string and a D-Bus path: + + sd_bus_message_append(m, "(so)", "a string", "/a/path"); + + + Append an array of UNIX file descriptors: + + sd_bus_message_append(m, "ah", 3, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO); + + + Append a variant, with the real type "g" (signature), + and value "sdbusisgood": + + sd_bus_message_append(m, "v", "g", "sdbusisgood"); + + Append a dictionary containing the mapping {1=>"a", 2=>"b", 3=>""}: + + + sd_bus_message_append(m, "a{is}", 3, 1, "a", 2, "b", 3, NULL); + + + + + Return Value + + On success, this call returns 0 or a positive + integer. On failure, this call returns a negative + errno-style error code. + + + + + + Notes + + sd_bus_open_user() and other functions + described here are available as a shared library, which can be + compiled and linked to with the + libsystemd-bus pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append_basic3, + sd_bus_message_append_array3 + + + +
diff --git a/src/libsystemd/sd_bus_message_append_array.xml b/src/libsystemd/sd_bus_message_append_array.xml new file mode 100644 index 0000000000..27db2a96c3 --- /dev/null +++ b/src/libsystemd/sd_bus_message_append_array.xml @@ -0,0 +1,213 @@ + + + + + + + + + sd_bus_message_append_array + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_message_append_array + 3 + + + + sd_bus_message_append_array + sd_bus_message_append_array_memfd + sd_bus_message_append_array_iovec + sd_bus_message_append_array_space + + Append an array of fields to a D-Bus + message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_append_array + sd_bus_message *m + char type + char void *ptr + size_t size + + + + int sd_bus_message_append_array_memfd + sd_bus_message *m + char type + int memfd + uint64_t offset + uint64_t size + + + + int sd_bus_message_append_array_iovec + sd_bus_message *m + char type + const struct iovec *iov + unsigned n + + + + int sd_bus_message_append_array_space + char type + size_t size + void **ptr + + + + + + Description + + The sd_bus_message_append_array() + function appends an array to a D-Bus message + m. A container will be opened, the array + contents appended, and the container closed. The parameter + type determines how the pointer + p is interpreted. + type must be one of the "trivial" types + y, n, q, + i, u, x, + t, d (but not + b), as defined by the Basic + Types section of the D-Bus specification, and listed in + sd_bus_message_append_basic3. + Pointer p must point to an array of size + size bytes containing items of the + respective type. Size size must be a + multiple of the size of the type type. As a + special case, p may be + NULL, if size is 0. + The memory pointed to by p is copied into + the memory area containing the message and stays in possession of + the caller. The caller may hence freely change the data after this + call without affecting the message the array was appended + to. + + The sd_bus_message_append_array_memfd() + function appends an array of a trivial type to message + m, similar to + sd_bus_message_append_array(). The contents + of the memory file descriptor memfd + starting at the specified offset and of the specified size is + used as the contents of the array. The offset and size must be a + multiple of the size of the type + type. However, as a special exception, if + the offset is specified as zero and the size specified as + UINT64_MAX the full memory file descriptor contents is used. The + memory file descriptor is sealed by this call if it has not been + sealed yet, and cannot be modified after this call. See + memfd_create2 + for details about memory file descriptors. Appending arrays with + memory file descriptors enables efficient zero-copy data transfer, + as the memory file descriptor may be passed as-is to the + destination, without copying the memory in it to the destination + process. Not all protocol transports support passing memory file + descriptors between participants, in which case this call will + automatically fall back to copying. Also, as memory file + descriptor passing is inefficient for smaller amounts of data, + copying might still be enforced even where memory file descriptor + passing is supported. + + The sd_bus_message_append_array_iovec() + function appends an array of a trivial type to the message + m, similar to + sd_bus_message_append_array(). Contents of + the I/O vector array iov are used as the + contents of the array. The total size of + iov payload (the sum of + iov_len fields) must be a multiple of + the size of the type type. The + iov argument must point to + n I/O vector structures. Each structure may + have the iov_base field set, in which + case the memory pointed to will be copied into the message, or + unset (set to zero), in which case a block of zeros of length + iov_len bytes will be inserted. The + memory pointed at by iov may be changed + after this call. + + The sd_bus_message_append_array_space() + function appends space for an array of a trivial type to message + m. It behaves the same as + sd_bus_message_append_array(), but instead of + copying items to the message, it returns a pointer to the + destination area to the caller in pointer + p. The caller should subsequently write the + array contents to this memory. Modifications to the memory + pointed to should only occur until the next operation on the bus + message is invoked. Most importantly, the memory should not be + altered anymore when another field has been added to the message + or the message has been sealed. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, they return a negative errno-style error code. + + + + + + Notes + + sd_bus_append_array() and other + functions described here are available as a shared library, which + can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append3, + sd_bus_message_append_basic3, + memfd_create2, + The D-Bus specification + + + + diff --git a/src/libsystemd/sd_bus_message_append_basic.xml b/src/libsystemd/sd_bus_message_append_basic.xml new file mode 100644 index 0000000000..276953af69 --- /dev/null +++ b/src/libsystemd/sd_bus_message_append_basic.xml @@ -0,0 +1,295 @@ + + + + + + + + + sd_bus_message_append_basic + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_message_append_basic + 3 + + + + sd_bus_message_append_basic + + Attach a single field to a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_append_basic + sd_bus_message *m + char type + const void *p + + + + + + Description + + sd_bus_message_append_basic() appends a + single field to the message m. The + parameter type determines how the pointer + p is interpreted. + type must be one of the basic types as + defined by the Basic + Types section of the D-Bus specification, and listed in + the table below. + + + + Item type specifiers + + + + + + + + + + Specifier + Constant + Description + Size + Expected C Type + + + + + y + SD_BUS_TYPE_BYTE + unsigned integer + 1 byte + uint8_t + + + + b + SD_BUS_TYPE_BOOLEAN + boolean + 4 bytes + int + + + + n + SD_BUS_TYPE_INT16 + signed integer + 2 bytes + int16_t + + + + q + SD_BUS_TYPE_UINT16 + unsigned integer + 2 bytes + uint16_t + + + + i + SD_BUS_TYPE_INT32 + signed integer + 4 bytes + int32_t + + + + u + SD_BUS_TYPE_UINT32 + unsigned integer + 4 bytes + uint32_t + + + + x + SD_BUS_TYPE_INT64 + signed integer + 8 bytes + int64_t + + + + t + SD_BUS_TYPE_UINT64 + unsigned integer + 8 bytes + uint64_t + + + + d + SD_BUS_TYPE_DOUBLE + floating-point + 8 bytes + double + + + + s + SD_BUS_TYPE_STRING + Unicode string + variable + char[] + + + + o + SD_BUS_TYPE_OBJECT_PATH + object path + variable + char[] + + + + g + SD_BUS_TYPE_SIGNATURE + signature + variable + char[] + + + + h + SD_BUS_TYPE_UNIX_FD + UNIX file descriptor + 4 bytes + int + + + +
+ + The value of the parameter is copied into a memory area held + by the message object, stays in the possession of the caller and + may hence be freely changed after this call without affecting the + bus message it has been added to. If type + is h (UNIX file descriptor), the descriptor is + duplicated by this call and the passed descriptor stays in + possession of the caller. + + For types s, o, and + g, the parameter p is + interpreted as a pointer to a NUL-terminated + character sequence. As a special case, a NULL + pointer is interpreted as an empty string. The string should be + valid Unicode string encoded as UTF-8. In case of the two latter + types, the additional requirements for a D-Bus object path or + type signature should be satisfied. Those requirements should be + verified by the recipient of the message. + +
+ + + Return Value + + On success, this call returns 0 or a positive integer. On + failure, it returns a negative errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + Specified parameter is invalid. + + + + + -EPERM + + Message has been sealed. + + + + + -ESTALE + + Message is in invalid state. + + + + + -ENXIO + + Message cannot be appended to. + + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_bus_append_basic() function + described here is available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append3, + The D-Bus specification + + + +
diff --git a/src/libsystemd/sd_bus_message_append_string_memfd.xml b/src/libsystemd/sd_bus_message_append_string_memfd.xml new file mode 100644 index 0000000000..9e99999bf3 --- /dev/null +++ b/src/libsystemd/sd_bus_message_append_string_memfd.xml @@ -0,0 +1,153 @@ + + + + + + + + + sd_bus_message_append_string_memfd + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_message_append_string_memfd + 3 + + + + sd_bus_message_append_string_memfd + sd_bus_message_append_string_iovec + sd_bus_message_append_string_space + + Attach a string to a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_append_string_memfd + sd_bus_message *m + int memfd + + + + int sd_bus_message_append_string_iovec + sd_bus_message *m + const struct iovec *iov + unsigned n + + + + int sd_bus_message_append_string_space + sd_bus_message *m + size_t size + char **s + + + + + + Description + + The functions + sd_bus_message_append_string_memfd and + sd_bus_message_append_string_iovec can be + used to append a single string (item of type s) + to message m. + + In case of + sd_bus_message_append_string_memfd, the + contents of memfd are the string. They must + satisfy the same constraints as described for the + s type in + sd_bus_message_append_basic3. + + In case of + sd_bus_message_append_string_iovec, the + payload of iov is the string. It must + satisfy the same constraints as described for the + s type in + sd_bus_message_append_basic3. + + The iov argument must point to + n struct iovec + structures. Each structure may have the + iov_base field set, in which case the + memory pointed to will be copied into the message, or unset, in + which case a block of spaces (ASCII 32) of length + iov_len will be inserted. The + memory pointed at by iov may be changed + after this call. + + The + sd_bus_message_append_string_space function appends + space for a string to message m. It behaves + similar to sd_bus_message_append_basic with + type s, but instead of copying a string into + the message, it returns a pointer to the destination area to + the caller in pointer p. Space for the string + of length size plus the terminating + NUL is allocated. + + + + Return Value + + On success, those calls return 0 or a positive integer. On + failure, they returns a negative errno-style error code. + + + + + + Notes + + The functions described here are available as a shared library, + which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append_basic3, + The D-Bus specification + + + + diff --git a/src/libsystemd/sd_bus_message_append_strv.xml b/src/libsystemd/sd_bus_message_append_strv.xml new file mode 100644 index 0000000000..0f77adcc8b --- /dev/null +++ b/src/libsystemd/sd_bus_message_append_strv.xml @@ -0,0 +1,116 @@ + + + + + + + + + sd_bus_message_append_strv + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_message_append_strv + 3 + + + + sd_bus_message_append_strv + + Attach an array of strings to a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_append_strv + sd_bus_message *m + char **l + + + + + + Description + + The sd_bus_message_append function can be + used to append an array of strings to message + m. The parameter l + shall point to a NULL-terminated array of pointers + to NUL-terminated strings. Each string must + satisfy the same constraints as described for the + s type in + sd_bus_message_append_basic3. + + + The memory pointed at by p and the + contents of the strings themselves are copied into the memory area + containing the message and may be changed after this call. Note + that the signature of l parameter is to be + treated as const char *const *, and the contents + will not be modified. + + + + Return Value + + On success, this call returns 0 or a positive integer. On + failure, a negative errno-style error code is returned. + + + + + + Notes + + The sd_bus_append_append_strv() function + described here is available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_message_append3, + sd_bus_message_append_array3, + The D-Bus specification + + + + diff --git a/src/libsystemd/sd_bus_message_get_cookie.xml b/src/libsystemd/sd_bus_message_get_cookie.xml new file mode 100644 index 0000000000..3328eead3d --- /dev/null +++ b/src/libsystemd/sd_bus_message_get_cookie.xml @@ -0,0 +1,146 @@ + + + + + + + + + sd_bus_message_get_cookie + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_bus_message_get_cookie + 3 + + + + sd_bus_message_get_cookie + sd_bus_message_get_reply_cookie + Returns the transaction cookie of a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_get_cookie + sd_bus_message *message + uint64_t *cookie + + + + int sd_bus_message_get_reply_cookie + sd_bus_message *message + uint64_t *cookie + + + + + + Description + + sd_bus_message_get_cookie() returns the + transaction cookie of a message. The cookie uniquely identifies a + message within each bus peer, but is not globally unique. It is + assigned when a message is sent. + + sd_bus_message_get_reply_cookie() + returns the transaction cookie of the message the specified + message is a response to. When a reply message is generated for a + method call message, its cookie is copied over into this field. + Note that while every message that is transferred is identified by + a cookie, only response messages carry a reply cookie + field. + + Both functions take a message object as first parameter and + a place to store the 64-bit cookie in. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error + code. + + On success, the cookie/reply cookie is returned in the + specified 64-bit unsigned integer variable. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + A specified parameter + is invalid. + + + + -ENODATA + + No cookie has been assigned to this message. + This either indicates that the message has not been sent yet + and hence has no cookie assigned, or that the message is not a + method response message and hence carries a reply cookie + field. + + + + + + Notes + + The sd_bus_message_get_cookie() and + sd_bus_message_get_reply_cookie() interfaces + are available as a shared library, which can be compiled and + linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_new3 + + + + diff --git a/src/libsystemd/sd_bus_message_get_monotonic_usec.xml b/src/libsystemd/sd_bus_message_get_monotonic_usec.xml new file mode 100644 index 0000000000..2c0a8a5d54 --- /dev/null +++ b/src/libsystemd/sd_bus_message_get_monotonic_usec.xml @@ -0,0 +1,181 @@ + + + + + + + + + sd_bus_message_get_monotonic_usec + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_bus_message_get_monotonic_usec + 3 + + + + sd_bus_message_get_monotonic_usec + sd_bus_message_get_realtime_usec + sd_bus_message_get_seqnum + Retrieve the sender timestamps and sequence number of a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_get_monotonic_usec + sd_bus_message *message + uint64_t *usec + + + + int sd_bus_message_get_realtime_usec + sd_bus_message *message + uint64_t *usec + + + + int sd_bus_message_get_seqnum + sd_bus_message *message + uint64_t *seqnum + + + + + + Description + + sd_bus_message_get_monotonic_usec() + returns the monotonic timestamp of the time the message was sent. + This value is in microseconds since the + CLOCK_MONOTONIC epoch, see + clock_gettime2 + for details. + + Similarly, + sd_bus_message_get_realtime_usec() returns + the realtime (wallclock) timestamp of the time the message was + sent. This value is in microseconds since Jan 1st, 1970, i.e. in + the CLOCK_REALTIME clock. + + sd_bus_message_get_seqnum() returns the + kernel-assigned sequence number of the message. The kernel assigns + a global, monotonically increasing sequence number to all messages + transmitted on the local system, at the time the message was sent. + This sequence number is useful for determining message send order, + even across different buses of the local system. The sequence + number combined with the boot ID of the system (as returned by + sd_id128_get_boot3) + is a suitable globally unique identifier for bus messages. + + Note that the sending order and receiving order of messages + might differ, in particular for broadcast messages. This means + that the sequence number and the timestamps of messages a client + reads are not necessarily monotonically increasing. + + These timestamps and the sequence number are attached to + each message by the kernel and cannot be manipulated by the + sender. + + Note that these timestamps are only available on some bus + transports, and only after support for them has been negotiated + with the + sd_bus_negotiate_timestamp3 + call. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error + code. + + On success, the timestamp or sequence number is returned in + the specified 64-bit unsigned integer variable. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + A specified parameter is + invalid. + + + + -ENODATA + + No timestamp or sequence number information is + attached to the passed message. This error is returned if the + underlying transport does not support timestamping or + assigning of sequence numbers, or if this feature has not been + negotiated with + sd_bus_negotiate_timestamp3. + + + + + + Notes + + The + sd_bus_message_get_monotonic_usec(), + sd_bus_message_get_realtime_usec(), and + sd_bus_message_get_seqnum() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_new3, + sd_bus_negotiate_timestamp3, + clock_gettime2, + sd_id128_get_boot3 + + + + diff --git a/src/libsystemd/sd_bus_message_read_basic.xml b/src/libsystemd/sd_bus_message_read_basic.xml new file mode 100644 index 0000000000..6a46403159 --- /dev/null +++ b/src/libsystemd/sd_bus_message_read_basic.xml @@ -0,0 +1,113 @@ + + + + + + + + + sd_bus_message_read_basic + systemd + + + + Julian + Orth + ju.orth@gmail.com + + + + + + sd_bus_message_read_basic + 3 + + + + sd_bus_message_read_basic + + Read a basic type from a message + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_message_read_basic + sd_bus_message *m + char type + void *p + + + + + + Description + + + sd_bus_message_read_basic() reads a basic type from a + message and advances the read position in the message. The set of basic + types and their ascii codes passed in type are + described in the D-Bus + Specification. + + + + If p is not NULL, it should contain a pointer to an + appropriate object. For example, if type is + 'y', the object passed in p + should have type uint8_t *. If type + is 's', the object passed in p + should have type const char **. Note that, if the basic type + is a pointer (e.g., const char * in the case of a string), + the pointer is only borrowed and the contents must be copied if they are + to be used after the end of the messages lifetime. Similarly, during the + lifetime of such a pointer, the message must not be modified. + + + + If there is no object of the specified type at the current position in the + message, an error is returned. + + + + + Return Value + + + On success, sd_bus_message_read_basic() returns 0 or + a positive integer. On failure, it returns a negative errno-style error + code. + + + + + See Also + + + systemd1, + sd-bus3, + + + + diff --git a/src/libsystemd/sd_bus_negotiate_fds.xml b/src/libsystemd/sd_bus_negotiate_fds.xml new file mode 100644 index 0000000000..a538b13cf0 --- /dev/null +++ b/src/libsystemd/sd_bus_negotiate_fds.xml @@ -0,0 +1,200 @@ + + + + + + + + + sd_bus_negotiate_fds + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_bus_negotiate_fds + 3 + + + + sd_bus_negotiate_fds + sd_bus_negotiate_timestamp + sd_bus_negotiate_creds + + Control feature negotiation on bus connections + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_negotiate_fds + sd_bus *bus + int b + + + + int sd_bus_negotiate_timestamp + sd_bus *bus + int b + + + + int sd_bus_negotiate_creds + sd_bus *bus + int b + uint64_t mask + + + + + + Description + + sd_bus_negotiate_fds() controls whether + file descriptor passing shall be negotiated for the specified bus + connection. It takes a bus object and a boolean, which, when true, + enables file descriptor passing, and, when false, disables + it. Note that not all transports and servers support file + descriptor passing. In particular, networked transports generally + do not support file descriptor passing. To find out whether file + descriptor passing is available after negotiation, use + sd_bus_can_send3 + and pass SD_BUS_TYPE_UNIX_FD. Note that file + descriptor passing is always enabled for both sending and + receiving or for neither, but never only in one direction. By + default, file descriptor passing is negotiated for all + connections. + + Note that when bus activation is used, it is highly + recommended to set the + setting in the .busname unit file to the same + setting as negotiated by the program ultimately activated. By + default, file descriptor passing is enabled for both. + + sd_bus_negotiate_timestamps() controls + whether implicit sender timestamps shall be attached automatically + to all incoming messages. Takes a bus object and a boolean, which, + when true, enables timestamping, and, when false, disables it. + Use + sd_bus_message_get_monotonic_usec3, + sd_bus_message_get_realtime_usec3, + sd_bus_message_get_seqnum3 + to query the timestamps of incoming messages. If negotiation is + disabled or not supported, these calls will fail with + -ENODATA. Note that not all transports + support timestamping of messages. Specifically, timestamping is + only available on the kdbus transport, but not on dbus1. The + timestamping is applied by the kernel and cannot be manipulated by + userspace. By default, message timestamping is not negotiated for + connections. + + sd_bus_negotiate_creds() controls + whether and which implicit sender credentials shall be attached + automatically to all incoming messages. Takes a bus object and a + boolean indicating whether to enable or disable the credential + parts encoded in the bit mask value argument. Note that not all + transports support attaching sender credentials to messages, or do + not support all types of sender credential parameters, or might + suppress them under certain circumstances for individual + messages. Specifically, implicit sender credentials on messages + are only fully supported on kdbus transports, and dbus1 only + supports SD_BUS_CREDS_UNIQUE_NAME. The sender + credentials are attached by the kernel and cannot be manipulated + by userspace, and are thus suitable for authorization + decisions. By default, only + SD_BUS_CREDS_WELL_KNOWN_NAMES and + SD_BUS_CREDS_UNIQUE_NAME are enabled. In + fact, these two credential fields are always sent along and cannot + be turned off. + + The sd_bus_negotiate_fds() function may + be called only before the connection has been started with + sd_bus_start3. Both + sd_bus_negotiate_timestamp() and + sd_bus_negotiate_creds() may also be called + after a connection has been set up. Note that, when operating on a + connection that is shared between multiple components of the same + program (for example via + sd_bus_default3), + it is highly recommended to only enable additional per message + metadata fields, but never disable them again, in order not to + disable functionality needed by other components. + + + + Return Value + + On success, these functions return 0 or a + positive integer. On failure, they return a negative errno-style + error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EPERM + + The bus connection has already been started. + + + + + + Notes + + sd_bus_negotiate_fds() and the other + functions described here are available as a shared library, which + can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_start3, + sd_bus_message_can_send3, + sd_bus_message_get_monotonic_usec3, + sd_bus_message_get_realtime_usec3, + sd_bus_message_get_seqnum3, + sd_bus_message_get_creds3, + systemd.busname5 + + + + diff --git a/src/libsystemd/sd_bus_new.xml b/src/libsystemd/sd_bus_new.xml new file mode 100644 index 0000000000..d281b5dd44 --- /dev/null +++ b/src/libsystemd/sd_bus_new.xml @@ -0,0 +1,189 @@ + + + + + + + + + sd_bus_new + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_new + 3 + + + + sd_bus_new + sd_bus_ref + sd_bus_unref + sd_bus_unrefp + + Create a new bus object and create or destroy references to it + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_new + sd_bus **bus + + + + sd_bus *sd_bus_ref + sd_bus *bus + + + + sd_bus *sd_bus_unref + sd_bus *bus + + + + void sd_bus_unrefp + sd_bus **bus + + + + + + Description + + sd_bus_new() creates a new bus + object. This object is reference-counted, and will be destroyed + when all references are gone. Initially, the caller of this + function owns the sole reference and the bus object will not be + connected to any bus. To connect it to a bus, make sure + to set an address with + sd_bus_set_address3 + or a related call, and then start the connection with + sd_bus_start3. + + In most cases, it is a better idea to invoke + sd_bus_default_user3, + sd_bus_default_system3 + or related calls instead of the more low-level + sd_bus_new() and + sd_bus_start(). The higher-level calls not + only allocate a bus object but also start the connection to a + well-known bus in a single function invocation. + + sd_bus_ref() increases the reference + counter of bus by one. + + sd_bus_unref() decreases the reference + counter of bus by one. Once the reference + count has dropped to zero, bus is destroyed + and cannot be used anymore, so further calls to + sd_bus_ref() or + sd_bus_unref() are illegal. + + sd_bus_unrefp() is similar to + sd_bus_unref() but takes a pointer to a + pointer to an sd_bus object. This call is useful in + conjunction with GCC's and LLVM's Clean-up + Variable Attribute. Note that this function is defined as + inline function. Use a declaration like the following, in order to + allocate a bus object that is freed automatically as the code + block is left: + + { + __attribute__((cleanup(sd_bus_unrefp)) sd_bus *bus = NULL; + int r; + … + r = sd_bus_default(&bus); + if (r < 0) + fprintf(stderr, "Failed to allocate bus: %s\n", strerror(-r)); + … +} + + sd_bus_ref(), + sd_bus_unref() and + sd_bus_unrefp() execute no operation if the + passed in bus object is NULL. + + + + Return Value + + On success, sd_bus_new() returns 0 or a + positive integer. On failure, it returns a negative errno-style + error code. + + sd_bus_ref() always returns the argument. + + + sd_bus_unref() always returns + NULL. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + sd_bus_new() and other functions + described here are available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_default_user3, + sd_bus_default_system3, + sd_bus_open_user3, + sd_bus_open_system3 + + + + diff --git a/src/libsystemd/sd_bus_path_encode.xml b/src/libsystemd/sd_bus_path_encode.xml new file mode 100644 index 0000000000..3088243e45 --- /dev/null +++ b/src/libsystemd/sd_bus_path_encode.xml @@ -0,0 +1,188 @@ + + + + + + + + + sd_bus_path_encode + systemd + + + + A monkey with a typewriter + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_bus_path_encode + 3 + + + + sd_bus_path_encode + sd_bus_path_encode_many + sd_bus_path_decode + sd_bus_path_decode_many + + Convert an external identifier into an object path and back + + + + + #include <systemd/sd-bus.h> + + + 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 + ... + + + + + + Description + + sd_bus_path_encode() and + sd_bus_path_decode() convert external + identifier strings into object paths and back. These functions are + useful to map application-specific string identifiers of any kind + into bus object paths in a simple, reversible and safe way. + + sd_bus_path_encode() takes a bus path + prefix and an external identifier string as arguments, plus a + place to store the returned bus path string. The bus path prefix + must be a valid bus path, starting with a slash + /, and not ending in one. The external + identifier string may be in any format, may be the empty string, + and has no restrictions on the charset — however, it must + always be NUL-terminated. The returned string + will be the concatenation of the bus path prefix plus an escaped + version of the external identifier string. This operation may be + reversed with sd_bus_decode(). It is + recommended to only use external identifiers that generally + require little escaping to be turned into valid bus path + identifiers (for example, by sticking to a 7-bit ASCII character + set), in order to ensure the resulting bus path is still short and + easily processed. + + sd_bus_path_decode() reverses the + operation of sd_bus_path_encode() and thus + regenerates an external identifier string from a bus path. It + takes a bus path and a prefix string, plus a place to store the + returned external identifier string. If the bus path does not + start with the specified prefix, 0 is returned and the returned + string is set to NULL. Otherwise, the + string following the prefix is unescaped and returned in the + external identifier string. + + The escaping used will replace all characters which are + invalid in a bus object path by _, followed by a + hexadecimal value. As a special case, the empty string will be + replaced by a lone _. + + sd_bus_path_encode_many() works like + its counterpart sd_bus_path_encode(), but + takes a path template as argument and encodes multiple labels + according to its embedded directives. For each + % character found in the template, the caller + must provide a string via varargs, which will be encoded and + embedded at the position of the % character. + Any other character in the template is copied verbatim into the + encoded path. + + sd_bus_path_decode_many() does the + reverse of sd_bus_path_encode_many(). It + decodes the passed object path according to the given + path template. For each % character in the + template, the caller must provide an output storage + (char **) via varargs. The decoded label + will be stored there. Each % character will + only match the current label. It will never match across labels. + Furthermore, only a single directive is allowed per label. + If NULL is passed as output storage, the + label is verified but not returned to the caller. + + + + Return Value + + On success, sd_bus_path_encode() + returns positive or 0, and a valid bus path in the return + argument. On success, sd_bus_path_decode() + returns a positive value if the prefixed matched, or 0 if it + did not. If the prefix matched, the external identifier is returned + in the return parameter. If it did not match, NULL is returned in + the return parameter. On failure, a negative errno-style error + number is returned by either function. The returned strings must + be + free3'd + by the caller. + + + + Notes + + sd_bus_path_encode() and + sd_bus_path_decode() are available as a + shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + free3 + + + + diff --git a/src/libsystemd/sd_bus_process.xml b/src/libsystemd/sd_bus_process.xml new file mode 100644 index 0000000000..4b9f52e52f --- /dev/null +++ b/src/libsystemd/sd_bus_process.xml @@ -0,0 +1,111 @@ + + + + + + + + + sd_bus_process + systemd + + + + Julian + Orth + ju.orth@gmail.com + + + + + + sd_bus_process + 3 + + + + sd_bus_process + + Drive the connection + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_process + sd_bus *bus + sd_bus_message **r + + + + + + Description + + + sd_bus_process() drives the connection between the + message bus and the client. That is, it handles connecting, + authentication, and message processing. It should be called in a loop + until no further progress can be made or an error occurs. + + + + Once no further progress can be made, + sd_bus_wait3 + should be called. Alternatively the user can wait for incoming data on + the file descriptor returned by + sd_bus_get_fd3. + + + + sd_bus_process processes at most one incoming + message per call. If the parameter r is not NULL + and the call processed a message, *r is set to this message. + The caller owns a reference to this message and should call + sd_bus_message_unref3 + when the message is no longer needed. If r is not + NULL, progress was made, but no message was processed, *r is + set to NULL. + + + + + Return Value + + + If progress was made, a positive integer is returned. If no progress was + made, 0 is returned. If an error occurs, a negative errno-style error code + is returned. + + + + + See Also + + + systemd1, + sd-bus3, + + + + diff --git a/src/libsystemd/sd_bus_request_name.xml b/src/libsystemd/sd_bus_request_name.xml new file mode 100644 index 0000000000..f07ae09555 --- /dev/null +++ b/src/libsystemd/sd_bus_request_name.xml @@ -0,0 +1,213 @@ + + + + + + + + + sd_bus_request_name + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_bus_request_name + 3 + + + + sd_bus_request_name + sd_bus_release_name + Request or release a well-known service name on a bus + + + + + #include <systemd/sd-bus.h> + + + int sd_bus_request_name + sd_bus *bus + const char *name + uint64_t flags + + + + int sd_bus_release_name + sd_bus *bus + const char *name + + + + + + Description + + sd_bus_request_name() requests a + well-known service name on a bus. It takes a bus connection, a + valid bus name and a flags parameter. The flags parameter is a + combination of the following flags: + + + + SD_BUS_NAME_ALLOW_REPLACEMENT + + After acquiring the name successfully, permit + other peers to take over the name when they try to acquire it + with the SD_BUS_NAME_REPLACE_EXISTING flag + set. If SD_BUS_NAME_ALLOW_REPLACEMENT is + not set on the original request, such a request by other peers + will be denied. + + + + SD_BUS_NAME_REPLACE_EXISTING + + Take over the name if it is already acquired + by another peer, and that other peer has permitted takeover by + setting SD_BUS_NAME_ALLOW_REPLACEMENT while + acquiring it. + + + + SD_BUS_NAME_QUEUE + + Queue the acquisition of the name when the + name is already taken. + + + + sd_bus_release_name() releases an + acquired well-known name. It takes a bus connection and a valid + bus name as parameters. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error + code. + + If SD_BUS_NAME_QUEUE is specified, + sd_bus_request_name() will return 0 when the + name is already taken by another peer and the client has been + added to the queue for the name. In that case, the caller can + subscribe to NameOwnerChanged signals to be + notified when the name is successfully acquired. + sd_bus_request_name() returns > 0 when the + name has immediately been acquired successfully. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EALREADY + + The caller already is the owner of the + specified name. + + + + -EEXIST + + The name has already been acquired by a + different peer, and SD_BUS_NAME_REPLACE_EXISTING was not + specified or the other peer did not specify + SD_BUS_NAME_ALLOW_REPLACEMENT while acquiring the + name. + + + + -ESRCH + + It was attempted to release a name that is + currently not registered on the bus. + + + + -EADDRINUSE + + It was attempted to release a name that is + owned by a different peer on the bus. + + + + -EINVAL + + A specified parameter is invalid. This is also + generated when the requested name is a special service name + reserved by the D-Bus specification, or when the operation is + requested on a connection that does not refer to a + bus. + + + + -ENOTCONN + + The bus connection has been + disconnected. + + + + -ECHILD + + The bus connection has been created in a + different process than the current one. + + + + + + Notes + + The sd_bus_acquire_name() and + sd_bus_release_name() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-bus3, + sd_bus_new3 + + + + diff --git a/src/libsystemd/sd_event_add_child.xml b/src/libsystemd/sd_event_add_child.xml new file mode 100644 index 0000000000..bc732db7fa --- /dev/null +++ b/src/libsystemd/sd_event_add_child.xml @@ -0,0 +1,246 @@ + + + + + + + + + sd_event_add_child + systemd + + + + More text + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_event_add_child + 3 + + + + sd_event_add_child + sd_event_source_get_child_pid + sd_event_child_handler_t + + Add a child process state change event source to an event loop + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event_source sd_event_source; + + + typedef int (*sd_event_child_handler_t) + sd_event_source *s + const siginfo_t *si + void *userdata + + + + int sd_event_add_child + sd_event *event + sd_event_source **source + pid_t pid + int options + sd_event_child_handler_t handler + void *userdata + + + + int sd_event_source_get_child_pid + sd_event_source *source + pid_t *pid + + + + + + + Description + + sd_event_add_child() adds a new child + process state change event source to an event loop. The event loop + object is specified in the event parameter, + the event source object is returned in the + source parameter. The + pid parameter specifies the PID of the + process to watch. The handler must + reference a function to call when the process changes state. The + handler function will be passed the + userdata pointer, which may be chosen + freely by the caller. The handler also receives a pointer to a + siginfo_t structure containing + information about the child process event. The + options parameter determines which state + changes will be watched for. It must contain an OR-ed mask of + WEXITED (watch for the child process + terminating), WSTOPPED (watch for the child + process being stopped by a signal), and + WCONTINUED (watch for the child process being + resumed by a signal). See waitid2 + for further information. + + Only a single handler may be installed for a specific + child process. The handler is enabled for a single event + (SD_EVENT_ONESHOT), but this may be changed + with + sd_event_source_set_enabled3. + If the handler function returns a negative error code, it will be + disabled after the invocation, even if the + SD_EVENT_ON mode was requested before. + + + To destroy an event source object use + sd_event_source_unref3, + but note that the event source is only removed from the event loop + when all references to the event source are dropped. To make sure + an event source does not fire anymore, even when there's still a + reference to it kept, consider setting the event source to + SD_EVENT_OFF with + sd_event_source_set_enabled3. + + If the second parameter of + sd_event_add_child() is passed as NULL no + reference to the event source object is returned. In this case the + event source is considered "floating", and will be destroyed + implicitly when the event loop itself is destroyed. + + Note that the handler function is + invoked at a time where the child process is not reaped yet (and + thus still is exposed as a zombie process by the kernel). However, + the child will be reaped automatically after the function + returns. Child processes for which no child process state change + event sources are installed will not be reaped by the event loop + implementation. + + If both a child process state change event source and a + SIGCHLD signal event source is installed in + the same event loop, the configured event source priorities decide + which event source is dispatched first. If the signal handler is + processed first, it should leave the child processes for which + child process state change event sources are installed unreaped. + + sd_event_source_get_child_pid() + retrieves the configured PID of a child process state change event + source created previously with + sd_event_add_child(). It takes the event + source object as the source parameter and a + pointer to a pid_t variable to return the process ID + in. + + + + + Return Value + + On success, these functions return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate an object. + + + + -EINVAL + + An invalid argument has been passed. This includes + specifying an empty mask in options or a mask + which contains values different than a combination of + WEXITED, WSTOPPED, and + WCONTINUED. + + + + + + -EBUSY + + A handler is already installed for this + child process. + + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + -EDOM + + The passed event source is not a child process event source. + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_now3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_enabled3, + sd_event_source_set_priority3, + sd_event_source_set_userdata3, + sd_event_source_set_description3, + waitid2 + + + + diff --git a/src/libsystemd/sd_event_add_defer.xml b/src/libsystemd/sd_event_add_defer.xml new file mode 100644 index 0000000000..d9ebd3b179 --- /dev/null +++ b/src/libsystemd/sd_event_add_defer.xml @@ -0,0 +1,216 @@ + + + + + + + + + sd_event_add_defer + systemd + + + + More text + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_event_add_defer + 3 + + + + sd_event_add_defer + sd_event_add_post + sd_event_add_exit + sd_event_handler_t + + Add static event sources to an event loop + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event_source sd_event_source; + + + typedef int (*sd_event_handler_t) + sd_event_source *s + void *userdata + + + + int sd_event_add_defer + sd_event *event + sd_event_source **source + sd_event_handler_t handler + void *userdata + + + + int sd_event_add_post + sd_event *event + sd_event_source **source + sd_event_handler_t handler + void *userdata + + + + int sd_event_add_exit + sd_event *event + sd_event_source **source + sd_event_handler_t handler + void *userdata + + + + + + + Description + + These three functions add new static event sources to an + event loop. The event loop object is specified in the + event parameter, the event source object is + returned in the source parameter. The event + sources are enabled statically and will "fire" when the event loop + is run and the conditions described below are met. The handler + function will be passed the userdata + pointer, which may be chosen freely by the caller. + + sd_event_add_defer() adds a new event + source that will be dispatched instantly, before the event loop + goes to sleep again and waits for new events. By default, the + handler will be called once + (SD_EVENT_ONESHOT). Note that if the event + source is set to SD_EVENT_ON the event loop + will never go to sleep again, but continuously call the handler, + possibly interleaved with other event sources. + + sd_event_add_post() adds a new event + source that is run before the event loop will sleep and wait + for new events, but only after at least one other non-post event + source was dispatched. By default, the source is enabled + permanently (SD_EVENT_ON). Note that this + event source type will still allow the event loop to go to sleep + again, even if set to SD_EVENT_ON, as long as + no other event source is ever triggered. + + sd_event_add_exit() adds a new event + source that will be dispatched when the event loop is terminated + with sd_event_exit3. + + The + sd_event_source_set_enabled3 + function may be used to enable the event source permanently + (SD_EVENT_ON) or to make it fire just once + (SD_EVENT_ONESHOT). + + If the handler function returns a negative error code, it + will be disabled after the invocation, even if the + SD_EVENT_ON mode was requested before. + + To destroy an event source object use + sd_event_source_unref3, + but note that the event source is only removed from the event loop + when all references to the event source are dropped. To make sure + an event source does not fire anymore, even when there's still a + reference to it kept, consider setting the event source to + SD_EVENT_OFF with + sd_event_source_set_enabled3. + + If the second parameter of these functions is passed as + NULL no reference to the event source object is returned. In this + case the event source is considered "floating", and will be + destroyed implicitly when the event loop itself is + destroyed. + + + + Return Value + + On success, this functions return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate an object. + + + + -EINVAL + + An invalid argument has been passed. + + + + -ESTALE + + The event loop is already terminated. + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_now3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_source_set_enabled3, + sd_event_source_set_priority3, + sd_event_source_set_userdata3, + sd_event_source_set_description3, + sd_event_exit3 + + + + diff --git a/src/libsystemd/sd_event_add_io.xml b/src/libsystemd/sd_event_add_io.xml new file mode 100644 index 0000000000..c3749164cd --- /dev/null +++ b/src/libsystemd/sd_event_add_io.xml @@ -0,0 +1,300 @@ + + + + + + + + + sd_event_add_io + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_add_io + 3 + + + + sd_event_add_io + sd_event_source_get_io_events + sd_event_source_set_io_events + sd_event_source_get_io_revents + sd_event_source_get_io_fd + sd_event_source_set_io_fd + sd_event_source + sd_event_io_handler_t + + Add an I/O event source to an event loop + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event_source sd_event_source; + + + typedef int (*sd_event_io_handler_t) + sd_event_source *s + int fd + uint32_t revents + void *userdata + + + + int sd_event_add_io + sd_event *event + sd_event_source **source + int fd + uint32_t events + sd_event_io_handler_t handler + void *userdata + + + + int sd_event_source_get_io_events + sd_event_source *source + uint32_t *events + + + + int sd_event_source_set_io_events + sd_event_source *source + uint32_t events + + + + int sd_event_source_get_io_revents + sd_event_source *source + uint32_t *revents + + + + int sd_event_source_get_io_fd + sd_event_source *source + + + + int sd_event_source_set_io_fd + sd_event_source *source + int fd + + + + + + + Description + + sd_event_add_io() adds a new I/O event + source to an event loop. The event loop object is specified in the + event parameter, the event source object is + returned in the source parameter. The + fd parameter takes the UNIX file descriptor + to watch, which may refer to a socket, a FIFO, a message queue, a + serial connection, a character device, or any other file descriptor + compatible with Linux + epoll7. The + events parameter takes a bit mask of events + to watch for, a combination of the following event flags: + EPOLLIN, EPOLLOUT, + EPOLLRDHUP, EPOLLPRI, + and EPOLLET, see + epoll_ctl2 + for details. The handler shall reference a + function to call when the event source is triggered. The + userdata pointer will be passed to the + handler function, and may be chosen freely by the caller. The + handler will also be passed the file descriptor the event was seen + on, as well as the actual event flags. It's generally a subset of + the events watched, however may additionally include + EPOLLERR and EPOLLHUP. + + + By default, an event source will stay enabled + continuously (SD_EVENT_ON), but this may be + changed with + sd_event_source_set_enabled3. + If the handler function returns a negative error code, it will be + disabled after the invocation, even if the + SD_EVENT_ON mode was requested before. Note + that an event source set to SD_EVENT_ON will + fire continuously unless data is read from or written to the file + descriptor to reset the mask of events seen. + + + Setting the I/O event mask to watch for to 0 does not mean + that the event source won't be triggered anymore, as + EPOLLHUP and EPOLLERR + may be triggered even with a zero event mask. To temporarily + disable an I/O event source use + sd_event_source_set_enabled3 + with SD_EVENT_OFF instead. + + To destroy an event source object use + sd_event_source_unref3, + but note that the event source is only removed from the event loop + when all references to the event source are dropped. To make sure + an event source does not fire anymore, even if it is still referenced, + disable the event source using + sd_event_source_set_enabled3 + with SD_EVENT_OFF. + + If the second parameter of + sd_event_add_io() is + NULL no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed. + + It is recommended to use + sd_event_add_io() only in conjunction with + file descriptors that have O_NONBLOCK set, to + ensure that all I/O operations from invoked handlers are properly + asynchronous and non-blocking. Using file descriptors without + O_NONBLOCK might result in unexpected + starvation of other event sources. See + fcntl2 + for details on enabling O_NONBLOCK mode. + + sd_event_source_get_io_events() retrieves + the configured mask of watched I/O events of an event source created + previously with sd_event_add_io(). It takes + the event source object and a pointer to a variable to store the + mask in. + + sd_event_source_set_io_events() + configures the mask of watched I/O events of an event source created + previously with sd_event_add_io(). It takes the + event source object and the new event mask. + + sd_event_source_get_io_revents() + retrieves the I/O event mask of currently seen but undispatched + events from an event source created previously with + sd_event_add_io(). It takes the event source + object and a pointer to a variable to store the event mask + in. When called from a handler function on the handler's event + source object this will return the same mask as passed to the + handler's revents parameter. This call is + primarily useful to check for undispatched events of an event + source from the handler of an unrelated (possibly higher priority) + event source. Note the relation between + sd_event_source_get_pending() and + sd_event_source_get_io_revents(): both + functions will report non-zero results when there's an event + pending for the event source, but the former applies to all event + source types, the latter only to I/O event sources. + + sd_event_source_get_io_fd() retrieves + the UNIX file descriptor of an event source created previously + with sd_event_add_io(). It takes the event + source object and returns the non-negative file descriptor + or a negative error number on error (see below). + + sd_event_source_set_io_fd() + changes the UNIX file descriptor of an I/O event source created + previously with sd_event_add_io(). It takes + the event source object and the new file descriptor. + + + + Return Value + + On success, these functions return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned values may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate an object. + + + + -EINVAL + + An invalid argument has been passed. + + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + -EDOM + + The passed event source is not an I/O event source. + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_now3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_source_set_enabled3, + sd_event_source_set_priority3, + sd_event_source_set_userdata3, + sd_event_source_set_description3, + sd_event_source_get_pending3, + epoll_ctl3, + epoll7 + + + + diff --git a/src/libsystemd/sd_event_add_signal.xml b/src/libsystemd/sd_event_add_signal.xml new file mode 100644 index 0000000000..e98f1d2682 --- /dev/null +++ b/src/libsystemd/sd_event_add_signal.xml @@ -0,0 +1,221 @@ + + + + + + + + + sd_event_add_signal + systemd + + + + More text + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_event_add_signal + 3 + + + + sd_event_add_signal + sd_event_source_get_signal + sd_event_signal_handler_t + + Add a UNIX process signal event source to an event + loop + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event_source sd_event_source; + + + typedef int (*sd_event_signal_handler_t) + sd_event_source *s + const struct signalfd_siginfo *si + void *userdata + + + + int sd_event_add_signal + sd_event *event + sd_event_source **source + int signal + sd_event_signal_handler_t handler + void *userdata + + + + int sd_event_source_get_signal + sd_event_source *source + + + + + + + Description + + sd_event_add_signal() adds a new UNIX + process signal event source to an event loop. The event loop + object is specified in the event parameter, + and the event source object is returned in the + source parameter. The + signal parameter specifies the numeric + signal to be handled (see signal7). + The handler parameter must reference a + function to call when the signal is received or be + NULL. The handler function will be passed + the userdata pointer, which may be chosen + freely by the caller. The handler also receives a pointer to a + signalfd_siginfo structure containing + information about the received signal. See signalfd2 + for further information. + + Only a single handler may be installed for a specific + signal. The signal will be unblocked by this call, and must be + blocked before this function is called in all threads (using + sigprocmask2). If + the handler is not specified (handler is + NULL), a default handler which causes the + program to exit cleanly will be used. + + By default, the event source is enabled permanently + (SD_EVENT_ON), but this may be changed with + sd_event_source_set_enabled3. + If the handler function returns a negative error code, it will be + disabled after the invocation, even if the + SD_EVENT_ON mode was requested before. + + + To destroy an event source object use + sd_event_source_unref3, + but note that the event source is only removed from the event loop + when all references to the event source are dropped. To make sure + an event source does not fire anymore, even if it is still referenced, + disable the event source using + sd_event_source_set_enabled3 + with SD_EVENT_OFF. + + If the second parameter of + sd_event_add_signal() is + NULL no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed. + + sd_event_source_get_signal() returns + the configured signal number of an event source created previously + with sd_event_add_signal(). It takes the + event source object as the source + parameter. + + + + Return Value + + On success, these functions return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate an object. + + + + -EINVAL + + An invalid argument has been passed. + + + + -EBUSY + + A handler is already installed for this + signal or the signal was not blocked previously. + + + + -ESTALE + + The event loop is already terminated. + + + + -ECHILD + + The event loop has been created in a different process. + + + + -EDOM + + The passed event source is not a signal event source. + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_now3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_source_set_enabled3, + sd_event_source_set_description3, + sd_event_source_set_userdata3, + signal7, + signalfd2 + + + + diff --git a/src/libsystemd/sd_event_add_time.xml b/src/libsystemd/sd_event_add_time.xml new file mode 100644 index 0000000000..5496b71529 --- /dev/null +++ b/src/libsystemd/sd_event_add_time.xml @@ -0,0 +1,313 @@ + + + + + + + + + sd_event_add_time + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_add_time + 3 + + + + sd_event_add_time + sd_event_source_get_time + sd_event_source_set_time + sd_event_source_get_time_accuracy + sd_event_source_set_time_accuracy + sd_event_source_get_time_clock + sd_event_time_handler_t + + Add a timer event source to an event loop + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event_source sd_event_source; + + + typedef int (*sd_event_time_handler_t) + sd_event_source *s + uint64_t usec + void *userdata + + + + int sd_event_add_time + sd_event *event + sd_event_source **source + clockid_t clock + uint64_t usec + uint64_t accuracy + sd_event_time_handler_t handler + void *userdata + + + + int sd_event_source_get_time + sd_event_source *source + uint64_t *usec + + + + int sd_event_source_set_time + sd_event_source *source + uint64_t usec + + + + int sd_event_source_get_time_accuracy + sd_event_source *source + uint64_t *usec + + + + int sd_event_source_set_time_accuracy + sd_event_source *source + uint64_t usec + + + + int sd_event_source_get_time_clock + sd_event_source *source + clockid_t *clock + + + + + + + Description + + sd_event_add_time() adds a new timer event source to an event loop. The event loop + object is specified in the event parameter, the event source object is returned in the + source parameter. The clock parameter takes a clock identifier, one + of CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME, + CLOCK_REALTIME_ALARM, or CLOCK_BOOTTIME_ALARM. See + timerfd_create2 for details + regarding the various types of clocks. The usec parameter specifies the earliest time, in + microseconds (µs), relative to the clock's epoch, when the timer shall be triggered. If a time already in the past + is specified (including 0), this timer source "fires" immediately and is ready to be + dispatched. If the parameter is specified as UINT64_MAX the timer event will never elapse, + which may be used as an alternative to explicitly disabling a timer event source with + sd_event_source_set_enabled3. The + accuracy parameter specifies an additional accuracy value in µs specifying how much the + timer event may be delayed. Use 0 to select the default accuracy (250ms). Use 1µs for maximum + accuracy. Consider specifying 60000000µs (1min) or larger for long-running events that may be delayed + substantially. Picking higher accuracy values allows the system to coalesce timer events more aggressively, + improving power efficiency. The handler parameter shall reference a function to call when + the timer elapses. The handler function will be passed the userdata pointer, which may be + chosen freely by the caller. The handler is also passed the configured trigger time, even if it is actually called + slightly later, subject to the specified accuracy value, the kernel timer slack (see + prctl2), and additional + scheduling latencies. To query the actual time the handler was called use + sd_event_now3. + + By default, the timer will elapse once + (SD_EVENT_ONESHOT), but this may be changed + with + sd_event_source_set_enabled3. + If the handler function returns a negative error code, it will be + disabled after the invocation, even if the + SD_EVENT_ON mode was requested before. Note + that a timer event set to SD_EVENT_ON will + fire continuously unless its configured time is updated using + sd_event_source_set_time(). + + + To destroy an event source object use + sd_event_source_unref3, + but note that the event source is only removed from the event loop + when all references to the event source are dropped. To make sure + an event source does not fire anymore, even if it is still referenced, + disable the event source using + sd_event_source_set_enabled3 + with SD_EVENT_OFF. + + If the second parameter of + sd_event_add_time() is + NULL no reference to the event source object + is returned. In this case the event source is considered + "floating", and will be destroyed implicitly when the event loop + itself is destroyed. + + If the handler to + sd_event_add_time() is + NULL, and the event source fires, this will + be considered a request to exit the event loop. In this case, the + userdata parameter, cast to an integer, is + used for the exit code passed to + sd_event_exit3. + + Use CLOCK_BOOTTIME_ALARM and + CLOCK_REALTIME_ALARM to define event sources + that may wake up the system from suspend. + + In order to set up relative timers (that is, relative to the + current time), retrieve the current time via + sd_event_now3, + add the desired timespan to it, and use the result as + the usec parameter to + sd_event_add_time(). + + In order to set up repetitive timers (that is, timers that + are triggered in regular intervals), set up the timer normally, + for the first invocation. Each time the event handler is invoked, + update the timer's trigger time with + sd_event_source_set_time3 for the next timer + iteration, and reenable the timer using + sd_event_source_set_enabled(). To calculate + the next point in time to pass to + sd_event_source_set_time(), either use as + base the usec parameter passed to the timer + callback, or the timestamp returned by + sd_event_now(). In the former case timer + events will be regular, while in the latter case the scheduling + latency will keep accumulating on the timer. + + sd_event_source_get_time() retrieves + the configured time value of an event source created + previously with sd_event_add_time(). It takes + the event source object and a pointer to a variable to store the + time in, relative to the selected clock's epoch, in µs. + + sd_event_source_set_time() changes the + time of an event source created previously with + sd_event_add_time(). It takes the event + source object and a time relative to the selected clock's epoch, + in µs. + + sd_event_source_get_time_accuracy() + retrieves the configured accuracy value of an event source + created previously with sd_event_add_time(). It + takes the event source object and a pointer to a variable to store + the accuracy in. The accuracy is specified in µs. + + sd_event_source_set_time_accuracy() + changes the configured accuracy of a timer event source created + previously with sd_event_add_time(). It takes + the event source object and accuracy, in µs. + + sd_event_source_get_time_clock() + retrieves the configured clock of an event source created + previously with sd_event_add_time(). It takes + the event source object and a pointer to a variable to store the + clock identifier in. + + + + Return Value + + On success, these functions return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned values may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate an object. + + + + -EINVAL + + An invalid argument has been passed. + + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + -EOPNOTSUPP + + The selected clock is not supported by the event loop implementation. + + + + + -EDOM + + The passed event source is not a timer event source. + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_now3, + sd_event_add_io3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_source_set_enabled3, + sd_event_source_set_priority3, + sd_event_source_set_userdata3, + sd_event_source_set_description3, + clock_gettime2, + timerfd_create2, + prctl2 + + + + diff --git a/src/libsystemd/sd_event_exit.xml b/src/libsystemd/sd_event_exit.xml new file mode 100644 index 0000000000..9846a3eaf4 --- /dev/null +++ b/src/libsystemd/sd_event_exit.xml @@ -0,0 +1,163 @@ + + + + + + + + + sd_event_exit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_exit + 3 + + + + sd_event_exit + sd_event_get_exit_code + + Ask the event loop to exit + + + + + #include <systemd/sd-event.h> + + + int sd_event_exit + sd_event *event + int code + + + + int sd_event_get_exit_code + sd_event *event + int *code + + + + + + + Description + + sd_event_exit() requests the event loop + specified in the event event loop object to + exit. The code parameter may be any integer + value and is returned as-is by + sd_event_loop3 + after the last event loop iteration. It may also be queried + using sd_event_get_exit_code(), see + below. + + When exiting is requested the event loop will stop listening + for and dispatching regular event sources. Instead it will proceed + with executing only event sources registered with + sd_event_add_exit3 + in the order defined by their priority. After all exit event + sources have been dispatched the event loop is terminated. + + If sd_event_exit() is invoked a second + time while the event loop is still processing exit event sources, + the exit code stored in the event loop object is updated, but + otherwise no further operation is executed. + + sd_event_get_exit_code() may be used to + query the exit code passed into + sd_event_exit() earlier. + + While the full positive and negative integer ranges may be used + for the exit code, care should be taken not pick exit codes that + conflict with regular exit codes returned by + sd_event_loop(), if these exit codes shall be + distinguishable. + + + + Return Value + + On success, sd_event_exit() and + sd_event_get_exit_code() return 0 or a positive + integer. On failure, they return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + The event loop object or error code pointer are invalid. + + + + + -ECHILD + + The event loop was created in a different process. + + + + -ESTALE + + The event loop has exited already and all exit handlers are already processed. + + + + -ENODATA + + The event loop has not been requested to exit yet. + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_add_exit3 + + + + diff --git a/src/libsystemd/sd_event_get_fd-glib-example.c b/src/libsystemd/sd_event_get_fd-glib-example.c new file mode 100644 index 0000000000..8f3168d0ea --- /dev/null +++ b/src/libsystemd/sd_event_get_fd-glib-example.c @@ -0,0 +1,68 @@ +/*** + Copyright 2014 Tom Gundersen + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include + +typedef struct SDEventSource { + GSource source; + GPollFD pollfd; + sd_event *event; +} SDEventSource; + +static gboolean event_prepare(GSource *source, gint *timeout_) { + return sd_event_prepare(((SDEventSource *)source)->event) > 0; +} + +static gboolean event_check(GSource *source) { + return sd_event_wait(((SDEventSource *)source)->event, 0) > 0; +} + +static gboolean event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { + return sd_event_dispatch(((SDEventSource *)source)->event) > 0; +} + +static void event_finalize(GSource *source) { + sd_event_unref(((SDEventSource *)source)->event); +} + +static GSourceFuncs event_funcs = { + .prepare = event_prepare, + .check = event_check, + .dispatch = event_dispatch, + .finalize = event_finalize, +}; + +GSource *g_sd_event_create_source(sd_event *event) { + SDEventSource *source; + + source = (SDEventSource *)g_source_new(&event_funcs, sizeof(SDEventSource)); + + source->event = sd_event_ref(event); + source->pollfd.fd = sd_event_get_fd(event); + source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; + + g_source_add_poll((GSource *)source, &source->pollfd); + + return (GSource *)source; +} diff --git a/src/libsystemd/sd_event_get_fd.xml b/src/libsystemd/sd_event_get_fd.xml new file mode 100644 index 0000000000..f68752dd0e --- /dev/null +++ b/src/libsystemd/sd_event_get_fd.xml @@ -0,0 +1,140 @@ + + + + + + + + + sd_event_get_fd + systemd + + + + More text + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_event_get_fd + 3 + + + + sd_event_get_fd + + Obtain a file descriptor to poll for event loop events + + + + + #include <systemd/sd-event.h> + + + int sd_event_get_fd + sd_event *event + + + + + + + Description + + sd_event_get_fd() returns the file + descriptor that an event loop object returned by the + sd_event_new3 + function uses to wait for events. This file descriptor may itself + be polled for + POLLIN/EPOLLIN + events. This makes it possible to embed an + sd-event3 + event loop into another, possibly foreign, event loop. + + The returned file descriptor refers to an epoll7 + object. It is recommended not to alter it by invoking + epoll_ctl2 + on it, in order to avoid interference with the event loop's inner + logic and assumptions. + + + + Return Value + + On success, sd_event_get_fd() returns a + non-negative file descriptor. On failure, it returns a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + event is not a valid + pointer to an sd_event structure. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + + Examples + + + Integration in the GLib event loop + + + + + + + + + See Also + + + sd-event3, + sd_event_new3, + sd_event_wait3, + epoll_ctl3, + epoll7 + + + + diff --git a/src/libsystemd/sd_event_new.xml b/src/libsystemd/sd_event_new.xml new file mode 100644 index 0000000000..2c23b00a8c --- /dev/null +++ b/src/libsystemd/sd_event_new.xml @@ -0,0 +1,245 @@ + + + + + + + + + sd_event_new + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_new + 3 + + + + sd_event_new + sd_event_default + sd_event_ref + sd_event_unref + sd_event_unrefp + sd_event_get_tid + sd_event + + Acquire and release an event loop object + + + + + #include <systemd/sd-event.h> + + typedef struct sd_event sd_event; + + + int sd_event_new + sd_event **event + + + + int sd_event_default + sd_event **event + + + + sd_event *sd_event_ref + sd_event *event + + + + sd_event *sd_event_unref + sd_event *event + + + + void sd_event_unrefp + sd_event **event + + + + int sd_event_get_tid + sd_event *event + pid_t *tid + + + + + + + Description + + sd_event_new() allocates a new event + loop object. The event loop object is returned in the + event parameter. After use, drop + the returned reference with + sd_event_unref(). When the last reference is + dropped, the object is freed. + + sd_event_default() acquires a reference + to the default event loop object of the calling thread, possibly + allocating a new object if no default event loop object has been + allocated yet for the thread. After use, drop the returned + reference with sd_event_unref(). When the + last reference is dropped, the event loop is freed. If this + function is called while the object returned from a previous call + from the same thread is still referenced, the same object is + returned again, but the reference is increased by one. It is + recommended to use this call instead of + sd_event_new() in order to share event loop + objects between various components that are dispatched in the same + thread. All threads have exactly either zero or one default event loop + objects associated, but never more. + + After allocating an event loop object, add event sources to + it with + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3 + or + sd_event_add_defer3, + and then execute the event loop using + sd_event_run3. + + sd_event_ref() increases the reference + count of the specified event loop object by one. + + sd_event_unref() decreases the + reference count of the specified event loop object by one. If + the count hits zero, the object is freed. Note that it + is freed regardless of whether it is the default event loop object for a + thread or not. This means that allocating an event loop with + sd_event_default(), then releasing it, and + then acquiring a new one with + sd_event_default() will result in two + distinct objects. Note that, in order to free an event loop object, + all remaining event sources of the event loop also need to be + freed as each keeps a reference to it. + + sd_event_unrefp() is similar to + sd_event_unref() but takes a pointer to a + pointer to an sd_event object. This call is useful in + conjunction with GCC's and LLVM's Clean-up + Variable Attribute. Note that this function is defined as + inline function. Use a declaration like the following, + in order to allocate an event loop object that is freed + automatically as the code block is left: + + { + __attribute__((cleanup(sd_event_unrefp)) sd_event *event = NULL; + int r; + … + r = sd_event_default(&event); + if (r < 0) + fprintf(stderr, "Failed to allocate event loop: %s\n", strerror(-r)); + … +} + + sd_event_ref(), + sd_event_unref() and + sd_event_unrefp() execute no operation if the + passed in event loop object is NULL. + + sd_event_get_tid() retrieves the thread + identifier ("TID") of the thread the specified event loop object + is associated with. This call is only supported for event loops + allocated with sd_event_default(), and + returns the identifier for the thread the event loop is the + default event loop of. See gettid2 + for more information on thread identifiers. + + + + Return Value + + On success, sd_event_new() and + sd_event_default() return 0 or a positive + integer. On failure, they return a negative errno-style error + code. sd_event_ref() always returns a pointer + to the event loop object passed + in. sd_event_unref() always returns + NULL. + + + + Errors + + Returned errors may indicate the following problems: + + + + -ENOMEM + + Not enough memory to allocate the object. + + + + -EMFILE + + The maximum number of event loops has been allocated. + + + + + -ENXIO + + sd_event_get_tid() was + invoked on an event loop object that was not allocated with + sd_event_default(). + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_add_post3, + sd_event_add_exit3, + sd_event_run3, + gettid2 + + + + diff --git a/src/libsystemd/sd_event_now.xml b/src/libsystemd/sd_event_now.xml new file mode 100644 index 0000000000..2c83b0bcb5 --- /dev/null +++ b/src/libsystemd/sd_event_now.xml @@ -0,0 +1,146 @@ + + + + + + + + + sd_event_now + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_now + 3 + + + + sd_event_now + + Retrieve current event loop iteration timestamp + + + + + #include <systemd/sd-event.h> + + + int sd_event_now + sd_event *event + clockid_t clock + uint64_t *usec + + + + + + + Description + + sd_event_now() returns the time when + the most recent event loop iteration began. A timestamp + is taken right after returning from the event sleep, and before + dispatching any event sources. The event + parameter specifies the event loop object to retrieve the timestamp + from. The clock parameter specifies the clock to + retrieve the timestamp for, and is one of + CLOCK_REALTIME (or equivalently + CLOCK_REALTIME_ALARM), + CLOCK_MONOTONIC, or + CLOCK_BOOTTIME (or equivalently + CLOCK_BOOTTIME_ALARM), see + clock_gettime2 + for more information on the various clocks. The retrieved + timestamp is stored in the usec parameter, + in µs since the clock's epoch. If this function is invoked before + the first event loop iteration, the current time is returned, as + reported by clock_gettime(). To distinguish + this case from a regular invocation the return value will be + positive, and zero when the returned timestamp refers to an actual + event loop iteration. + + + + Return Value + + If the first event loop iteration has not run yet + sd_event_now() writes current time to + usec and returns a positive return value. + Otherwise, it will write the requested timestamp to usec + and return 0. On failure, the call returns a negative errno-style + error code. + + + + Errors + + Returned values may indicate the following problems: + + + + -EINVAL + + An invalid parameter was + passed. + + + + + -EOPNOTSUPP + + Unsupported clock type. + + + + + -ECHILD + + The event loop object was created in a + different process. + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_add_time3, + clock_gettime2 + + + + diff --git a/src/libsystemd/sd_event_run.xml b/src/libsystemd/sd_event_run.xml new file mode 100644 index 0000000000..5b68959165 --- /dev/null +++ b/src/libsystemd/sd_event_run.xml @@ -0,0 +1,190 @@ + + + + + + + + + sd_event_run + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + sd_event_run + 3 + + + + sd_event_run + sd_event_loop + + Run an event loop + + + + + #include <systemd/sd-event.h> + + + int sd_event_run + sd_event *event + uint64_t usec + + + + int sd_event_loop + sd_event *event + + + + + + Description + + sd_event_run() may be used to run a single + iteration of the event loop specified in the + event parameter. The function waits until an event to + process is available, and dispatches the registered handler for + it. The usec parameter specifies the + maximum time (in microseconds) to wait for an event. Use + (uint64_t) -1 to specify an infinite + timeout. + + sd_event_loop() invokes + sd_event_run() in a loop, thus implementing + the actual event loop. The call returns as soon as exiting was + requested using + sd_event_exit3. + + The event loop object event is + created with + sd_event_new3. + Events sources to wait for and their handlers may be registered + with + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_add_post3 + and + sd_event_add_exit3. + + + For low-level control of event loop execution, use + sd_event_prepare3, + sd_event_wait3 + and + sd_event_dispatch3 + which are wrapped by sd_event_run(). Along + with + sd_event_get_fd3, + these functions allow integration of an + sd-event3 + event loop into foreign event loop implementations. + + + + Return Value + + On failure, these functions return a negative errno-style + error code. sd_event_run() returns a + positive, non-zero integer if an event source was dispatched, and + zero when the specified timeout hit before an event source has + seen any event, and hence no event source was + dispatched. sd_event_loop() returns the exit + code specified when invoking + sd_event_exit(). + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The event parameter is + invalid or NULL. + + + + -EBUSY + + The event loop object is not in the right + state (see + sd_event_prepare3 + for an explanation of possible states). + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + Other errors are possible, too. + + + + + + See Also + + + systemd1, + sd_event_new3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_add_exit3, + sd_event_add_post3, + sd_event_exit3, + sd_event_get_fd3, + sd_event_wait3, + GLib Main Event Loop. + + + + diff --git a/src/libsystemd/sd_event_set_watchdog.xml b/src/libsystemd/sd_event_set_watchdog.xml new file mode 100644 index 0000000000..cbc5bc0836 --- /dev/null +++ b/src/libsystemd/sd_event_set_watchdog.xml @@ -0,0 +1,177 @@ + + + + + + + + + sd_event_set_watchdog + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_set_watchdog + 3 + + + + sd_event_set_watchdog + sd_event_get_watchdog + + Enable event loop watchdog support + + + + + #include <systemd/sd-event.h> + + + int sd_event_set_watchdog + sd_event *event + int b + + + + int sd_event_get_watchdog + sd_event *event + + + + + + + Description + + sd_event_set_watchdog() may be used to + enable or disable automatic watchdog notification support in the + event loop object specified in the event + parameter. Specifically, depending on the b + boolean argument this will make sure the event loop wakes up in + regular intervals and sends watchdog notification messages to the + service manager, if this was requested by the service + manager. Watchdog support is determined with + sd_watchdog_enabled3, + and watchdog messages are sent with + sd_notify3. See + the WatchdogSec= setting in + systemd.service5 + for details on how to enable watchdog support for a service and + the protocol used. The wake-up interval is chosen as half the + watchdog timeout declared by the service manager via the + $WATCHDOG_USEC environment variable. If the + service manager did not request watchdog notifications, or if the + process was not invoked by the service manager this call with a + true b parameter executes no + operation. Passing a false b parameter will + disable the automatic sending of watchdog notification messages if + it was enabled before. Newly allocated event loop objects have + this feature disabled. + + The first watchdog notification message is sent immediately + when set_event_set_watchdog() is invoked with + a true b parameter. + + The watchdog logic is designed to allow the service manager + to automatically detect services that ceased processing of + incoming events, and thus appear "hung". Watchdog notifications + are sent out only at the beginning of each event loop + iteration. If an event source dispatch function blocks for an + excessively long time and does not return execution to the event + loop quickly, this might hence cause the notification message to + be delayed, and possibly result in abnormal program termination, + as configured in the service unit file. + + sd_event_get_watchdog() may be used to + determine whether watchdog support was previously requested by a + call to sd_event_set_watchdog() with a true + b parameter and successfully + enabled. + + + + Return Value + + On success, sd_event_set_watchdog() and + sd_event_get_watchdog() return a non-zero + positive integer if the service manager requested watchdog support + and watchdog support was successfully enabled. They return zero if + the service manager did not request watchdog support, or if + watchdog support was explicitly disabled with a false + b parameter. On failure, they return a + negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ECHILD + + The event loop has been created in a different process. + + + + -EINVAL + + The passed event loop object was invalid. + + + + + + + + + See Also + + + systemd1, + sd-event3, + sd_event_new3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_child3, + sd_event_add_defer3, + sd_event_add_post3, + sd_event_add_exit3, + sd_watchdog_enabled3, + sd_notify3, + systemd.service5 + + + + diff --git a/src/libsystemd/sd_event_source_get_event.xml b/src/libsystemd/sd_event_source_get_event.xml new file mode 100644 index 0000000000..2fdbd411bd --- /dev/null +++ b/src/libsystemd/sd_event_source_get_event.xml @@ -0,0 +1,100 @@ + + + + + + + + + sd_event_source_get_event + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_get_event + 3 + + + + sd_event_source_get_event + + Retrieve the event loop of an event source + + + + + #include <systemd/sd-event.h> + + + sd_event* sd_event_source_get_event + sd_event_source *source + + + + + + + Description + + sd_event_source_get_event() may be used + to retrieve the event loop object the event source object specified + as source is associated with. The event + loop object is specified when creating an event source object with + calls such as + sd_event_add_io3 + or + sd_event_add_time3. + + + + Return Value + + On success, sd_event_source_get_event() + returns the associated event loop object. On failure, it returns + NULL. + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_userdata3 + + + + diff --git a/src/libsystemd/sd_event_source_get_pending.xml b/src/libsystemd/sd_event_source_get_pending.xml new file mode 100644 index 0000000000..7f88bd1b87 --- /dev/null +++ b/src/libsystemd/sd_event_source_get_pending.xml @@ -0,0 +1,167 @@ + + + + + + + + + sd_event_source_get_pending + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_get_pending + 3 + + + + sd_event_source_get_pending + + Determine pending state of event sources + + + + + #include <systemd/sd-event.h> + + + int sd_event_source_get_pending + sd_event_source *source + + + + + + + Description + + sd_event_source_get_pending() may be + used to determine whether the event source object specified as + source has seen events but has not been + dispatched yet (and thus is marked "pending"). + + Event source objects initially are not marked pending, when + they are created with calls such as + sd_event_add_io3, + sd_event_add_time3, + with the exception of those created with + sd_event_add_defer3 + which are immediately marked pending, and + sd_event_add_exit3 + for which the "pending" concept is not defined. For details see + the respective manual pages. + + In each event loop iteration one event source of those + marked pending is dispatched, in the order defined by the event + source priority, as set with + sd_event_source_set_priority3. + + For I/O event sources, as created with + sd_event_add_io3, + the call + sd_event_source_get_io_revents3 + may be used to query the type of event pending in more + detail. + + + + + Return Value + + On success, + sd_event_source_get_pending() returns an + integer greater than zero when the event source is marked pending, + and zero when the event source is not marked pending. On failure, + it returns a negative errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + source is not a valid + pointer to an sd_event_source + object. + + + + -EDOM + + source refers to an + event source object created with + sd_event_add_exit3. + + + + -ENOMEM + + Not enough memory. + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_unref3 + + + + diff --git a/src/libsystemd/sd_event_source_set_description.xml b/src/libsystemd/sd_event_source_set_description.xml new file mode 100644 index 0000000000..b9488a622f --- /dev/null +++ b/src/libsystemd/sd_event_source_set_description.xml @@ -0,0 +1,170 @@ + + + + + + + + + sd_event_source_set_description + systemd + + + + More text + Zbigniew + Jędrzejewski-Szmek + zbyszek@in.waw.pl + + + + + + sd_event_source_set_description + 3 + + + + sd_event_source_set_description + sd_event_source_get_description + + Set or retrieve descriptive names of event sources + + + + + #include <systemd/sd-event.h> + + + int sd_event_source_set_description + sd_event_source *source + const char *description + + + + int sd_event_source_get_description + sd_event_source *source + const char **description + + + + + + + Description + + sd_event_source_set_description() may + be used to set an arbitrary descriptive name for the event source + object specified as source. This name will + be used in debugging messages generated by + sd-event3 + for this event source, and may be queried using + sd_event_source_get_description() for + debugging purposes. The description parameter shall + point to a NUL-terminated string or be + NULL. In the latter case, the descriptive + name will be unset. The string is copied internally, hence the + description argument is not referenced + after the function returns. + + sd_event_source_get_description() may + be used to query the current descriptive name assigned to the + event source object source. It returns a + pointer to the current name in description, + stored in memory internal to the event source. The memory is + invalidated when the event source is destroyed or the descriptive + name is changed. + + Event source objects generally have no description set when + they are created, except for UNIX signal event sources created + with + sd_event_add_signal3, + whose descriptive name is initialized to the signal's C constant + name (e.g. SIGINT or + SIGTERM). + + + + Return Value + + On success, sd_event_source_set_description() and + sd_event_source_get_description() return a + non-negative integer. On failure, they return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + source is not a valid + pointer to an sd_event_source + object or the description argument for + sd_event_source_get_description() is + NULL. + + + + -ENOMEM + + Not enough memory to copy the + name. + + + + -ECHILD + + The event loop has been created in a different process. + + + + + -ENXIO + + No name was set for the event + source. + + + + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_userdata3 + + + + diff --git a/src/libsystemd/sd_event_source_set_enabled.xml b/src/libsystemd/sd_event_source_set_enabled.xml new file mode 100644 index 0000000000..6844f29a49 --- /dev/null +++ b/src/libsystemd/sd_event_source_set_enabled.xml @@ -0,0 +1,179 @@ + + + + + + + + + sd_event_source_set_enabled + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_set_enabled + 3 + + + + sd_event_source_set_enabled + sd_event_source_get_enabled + SD_EVENT_ON + SD_EVENT_OFF + SD_EVENT_ONESHOT + + Enable or disable event sources + + + + + #include <systemd/sd-event.h> + + enum { + SD_EVENT_OFF = 0, + SD_EVENT_ON = 1, + SD_EVENT_ONESHOT = -1, +}; + + + int sd_event_source_set_enabled + sd_event_source *source + int enabled + + + + int sd_event_source_get_enabled + sd_event_source *source + int *enabled + + + + + + + Description + + sd_event_source_set_enabled() may be + used to enable or disable the event source object specified as + source. The enabled + parameter takes one of SD_EVENT_ON (to + enable), SD_EVENT_OFF (to disable) or + SD_EVENT_ONESHOT. If invoked with + SD_EVENT_ONESHOT the event source will be + enabled but automatically reset to + SD_EVENT_OFF after the event source was + dispatched once. + + Event sources that are disabled will not result in event + loop wakeups and will not be dispatched, until they are enabled + again. + + sd_event_source_get_enabled() may be + used to query whether the event source object + source is currently enabled or not. It + returns the enablement state in + enabled. + + Event source objects are enabled when they are first created + with calls such as + sd_event_add_io3, + sd_event_add_time3. However, + depending on the event source type they are enabled continuously + (SD_EVENT_ON) or only for a single invocation + of the event source handler + (SD_EVENT_ONESHOT). For details see the + respective manual pages. + + As event source objects stay active and may be dispatched as + long as there is at least one reference to them, in many cases it + is a good idea to combine a call to + sd_event_source_unref3 + with a prior call to + sd_event_source_set_enabled() with + SD_EVENT_OFF, to ensure the event source is + not dispatched again until all other remaining references are dropped. + + + + Return Value + + On success, sd_event_source_set_enabled() and + sd_event_source_get_enabled() return a + non-negative integer. On failure, they return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + source is not a valid + pointer to an sd_event_source + object. + + + + -ENOMEM + + Not enough memory. + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_unref3 + + + + diff --git a/src/libsystemd/sd_event_source_set_prepare.xml b/src/libsystemd/sd_event_source_set_prepare.xml new file mode 100644 index 0000000000..24861d01d9 --- /dev/null +++ b/src/libsystemd/sd_event_source_set_prepare.xml @@ -0,0 +1,171 @@ + + + + + + + + + sd_event_source_set_prepare + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_set_prepare + 3 + + + + sd_event_source_set_prepare + + Set a preparation callback for event sources + + + + + #include <systemd/sd-event.h> + + + int sd_event_source_set_prepare + sd_event_source *source + sd_event_handler_t callback + + + + typedef int (*sd_event_handler_t) + sd_event_source *s + void *userdata + + + + + + + Description + + sd_event_source_set_prepare() may be + used to set a preparation callback for the event source object + specified as source. The callback function + specified as callback will be invoked + immediately before the event loop goes to sleep to wait for + incoming events. It is invoked with the user data pointer passed + when the event source was created. The callback function may be + used to reconfigure the precise events to wait for. If the + callback parameter is passed as NULL the + callback function is reset. + + Event source objects have no preparation callback associated + when they are first created with calls such as + sd_event_add_io3, + sd_event_add_time3. Preparation + callback functions are supported for all event source types with + the exception of those created with + sd_event_add_exit3. Preparation + callback functions are dispatched in the order indicated by the + event source's priority field, as set with + sd_event_source_set_priority3. Preparation + callbacks of disabled event sources (see + sd_event_source_set_enabled3) + are not invoked. + + + + Return Value + + On success, + sd_event_source_set_prepare() returns a + non-negative integer. On failure, it returns a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + source is not a valid + pointer to an sd_event_source + object. + + + + -ESTALE + + The event loop is already terminated. + + + + -ENOMEM + + Not enough memory. + + + + -ECHILD + + The event loop has been created in a different process. + + + + + -EDOM + + The specified event source has been created + with + sd_event_add_exit3. + + + + + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_enabled3, + sd_event_source_set_priority3, + sd_event_source_set_userdata3 + + + + diff --git a/src/libsystemd/sd_event_source_set_priority.xml b/src/libsystemd/sd_event_source_set_priority.xml new file mode 100644 index 0000000000..8c9b39fe5e --- /dev/null +++ b/src/libsystemd/sd_event_source_set_priority.xml @@ -0,0 +1,189 @@ + + + + + + + + + sd_event_source_set_priority + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_set_priority + 3 + + + + sd_event_source_set_priority + sd_event_source_get_priority + SD_EVENT_PRIORITY_IMPORTANT + SD_EVENT_PRIORITY_NORMAL + SD_EVENT_PRIORITY_IDLE + + Set or retrieve the priority of event sources + + + + + #include <systemd/sd-event.h> + + enum { + SD_EVENT_SOURCE_IMPORTANT = -100, + SD_EVENT_SOURCE_NORMAL = 0, + SD_EVENT_SOURCE_IDLE = 100, +}; + + + int sd_event_source_set_priority + sd_event_source *source + int64_t priority + + + + int sd_event_source_get_priority + sd_event_source *source + int64_t *priority + + + + + + + Description + + sd_event_source_set_priority() may be + used to set the priority for the event source object specified as + source. The priority is specified as an + arbitrary signed 64bit integer. The priority is initialized to + SD_EVENT_PRIORITY_NORMAL (0) when the event + source is allocated with a call such as + sd_event_add_io3 + or + sd_event_add_time3, + and may be changed with this call. If multiple event sources have seen events at the same time, they are dispatched in the order indicated by the + event sources' priorities. Event sources with smaller priority + values are dispatched first. As well-known points of reference, + the constants SD_EVENT_PRIORITY_IMPORTANT + (-100), SD_EVENT_PRIORITY_NORMAL (0) and + SD_EVENT_PRIORITY_IDLE (100) may be used to + indicate event sources that shall be dispatched early, normally or + late. It is recommended to specify priorities based on these + definitions, and relative to them — however, the full 64bit + signed integer range is available for ordering event + sources. + + Priorities define the order in which event sources that have + seen events are dispatched. Care should be taken to ensure that + high-priority event sources (those with negative priority values + assigned) do not cause starvation of low-priority event sources + (those with positive priority values assigned). + + The order in which event sources with the same priority are + dispatched is undefined, but the event loop generally tries to + dispatch them in the order it learnt about events on them. As the + backing kernel primitives do not provide accurate information + about the order in which events occurred this is not necessarily + reliable. However, it is guaranteed that if events are seen on + multiple same-priority event sources at the same time, each one is + not dispatched again until all others have been dispatched + once. This behaviour guarantees that within each priority + particular event sources do not starve or dominate the event + loop. + + sd_event_source_get_priority() may be + used to query the current priority assigned to the event source + object source. + + + + Return Value + + On success, + sd_event_source_set_priority() and + sd_event_source_get_priority() return a + non-negative integer. On failure, they return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + source is not a valid + pointer to an sd_event_source + object. + + + + -ENOMEM + + Not enough memory. + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3 + + + + diff --git a/src/libsystemd/sd_event_source_set_userdata.xml b/src/libsystemd/sd_event_source_set_userdata.xml new file mode 100644 index 0000000000..533d491b13 --- /dev/null +++ b/src/libsystemd/sd_event_source_set_userdata.xml @@ -0,0 +1,119 @@ + + + + + + + + + sd_event_source_set_userdata + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_set_userdata + 3 + + + + sd_event_source_set_userdata + sd_event_source_get_userdata + + Set or retrieve user data pointer of event sources + + + + + #include <systemd/sd-event.h> + + + void* sd_event_source_set_userdata + sd_event_source *source + void *userdata + + + + void* sd_event_source_get_userdata + sd_event_source *source + + + + + + + Description + + sd_event_source_set_userdata() may be + used to set an arbitrary user data pointer for the event source + object specified as source. The user data + pointer is usually specified when creating an event source object + with calls such as + sd_event_add_io3 + or + sd_event_add_time3, + and may be updated with this call. The user data pointer is also + passed to all handler callback functions associated with the event + source. The userdata parameter specifies + the new user data pointer to set, the function returns the + previous user data pointer. Note that NULL is + a valid user data pointer. + + sd_event_source_get_userdata() may be + used to query the current user data pointer assigned to the event + source object source. + + + + Return Value + + On success, + sd_event_source_set_userdata() and + sd_event_source_get_userdata() return the + previously set user data pointer. On failure, they return + NULL. + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_description3 + + + + diff --git a/src/libsystemd/sd_event_source_unref.xml b/src/libsystemd/sd_event_source_unref.xml new file mode 100644 index 0000000000..2c4d450763 --- /dev/null +++ b/src/libsystemd/sd_event_source_unref.xml @@ -0,0 +1,142 @@ + + + + + + + + + sd_event_source_unref + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_event_source_unref + 3 + + + + sd_event_source_unref + sd_event_source_unrefp + sd_event_source_ref + + Increase or decrease event source reference counters + + + + + #include <systemd/sd-event.h> + + + sd_event_source* sd_event_source_unref + sd_event_source *source + + + + void sd_event_source_unrefp + sd_event_source **source + + + + sd_event_source* sd_event_source_ref + sd_event_source *source + + + + + + + Description + + sd_event_source_unref() may be used to + decrement by one the reference counter of the event source object + specified as source. The reference counter + is initially set to one, when the event source is created with calls + such as + sd_event_add_io3 + or + sd_event_add_time3. When + the reference counter reaches zero it is removed from its event loop + object and destroyed. + + sd_event_source_unrefp() is similar to + sd_event_source_unref() but takes a pointer to a + pointer to an sd_event_source object. This call is useful in + conjunction with GCC's and LLVM's Clean-up + Variable Attribute. Note that this function is defined as + inline function. + + sd_event_source_ref() may be used + to increase by one the reference counter of the event source object + specified as source. + + sd_event_source_unref(), + sd_bus_creds_unrefp() and + sd_bus_creds_ref() execute no operation if + the passed event source object is + NULL. + + Note that event source objects stay alive and may be + dispatched as long as they have a reference counter greater than + zero. In order to drop a reference of an event source and make + sure the associated event source handler function is not called + anymore it is recommended to combine a call of + sd_event_source_unref() with a prior call to + sd_event_source_set_enabled() with + SD_EVENT_OFF. + + + + Return Value + + sd_event_source_unref() always returns + NULL. + sd_event_source_ref() always returns the + event source object passed in. + + + + + + See Also + + + sd-event3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_child3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_source_set_enabled3 + + + + diff --git a/src/libsystemd/sd_event_wait.xml b/src/libsystemd/sd_event_wait.xml new file mode 100644 index 0000000000..26327dc688 --- /dev/null +++ b/src/libsystemd/sd_event_wait.xml @@ -0,0 +1,356 @@ + + + + + + + + + sd_event_wait + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + sd_event_wait + 3 + + + + sd_event_wait + sd_event_prepare + sd_event_dispatch + sd_event_get_state + sd_event_get_iteration + SD_EVENT_INITIAL + SD_EVENT_PREPARING + SD_EVENT_ARMED + SD_EVENT_PENDING + SD_EVENT_RUNNING + SD_EVENT_EXITING + SD_EVENT_FINISHED + + Low-level event loop operations + + + + + #include <systemd/sd-event.h> + + enum { + SD_EVENT_INITIAL, + SD_EVENT_PREPARING, + SD_EVENT_ARMED, + SD_EVENT_PENDING, + SD_EVENT_RUNNING, + SD_EVENT_EXITING, + SD_EVENT_FINISHED, +}; + + + int sd_event_prepare + sd_event *event + + + + int sd_event_wait + sd_event *event + uint64_t usec + + + + int sd_event_dispatch + sd_event *event + + + + int sd_event_get_state + sd_event *event + + + + int sd_event_get_iteration + sd_event *event + uint64_t *ret + + + + + + + Description + + The low-level sd_event_prepare(), + sd_event_wait() and + sd_event_dispatch() functions may be used to + execute specific phases of an event loop. See + sd_event_run3 + and + sd_event_loop3 + for higher-level functions that execute individual but complete + iterations of an event loop or run it continuously. + + sd_event_prepare() checks for pending + events and arms necessary timers. If any events are ready to be + processed ("pending"), it returns a positive, non-zero value, and the caller + should process these events with + sd_event_dispatch(). + + sd_event_dispatch() dispatches the + highest priority event source that has a pending event. On + success, sd_event_dispatch() returns either + zero, which indicates that no further event sources may be + dispatched and exiting of the event loop was requested via + sd_event_exit3; + or a positive non-zero value, which means that an event source was + dispatched and the loop returned to its initial state, and the + caller should initiate the next event loop iteration by invoking + sd_event_prepare() again. + + In case sd_event_prepare() returned + zero, sd_event_wait() should be called to + wait for further events or a timeout. If any events are ready to + be processed, it returns a positive, non-zero value, and the + events should be dispatched with + sd_event_dispatch(). Otherwise, the event + loop returned to its initial state and the next event loop + iteration should be initiated by invoking + sd_event_prepare() again. + + sd_event_get_state() may be used to + determine the state the event loop is currently in. It returns one + of the states described below. + + sd_event_get_iteration() may be used to determine the current iteration of the event + loop. It returns an unsigned 64bit integer containing a counter that increases monotonically with each iteration of + the event loop, starting with 0. The counter is increased at the time of the + sd_event_prepare() invocation. + + All five functions take, as the first argument, the event loop object event that has + been created with sd_event_new(). The timeout for sd_event_wait() is + specified in usec in microseconds. (uint64_t) -1 may be used to + specify an infinite timeout. + + + + State Machine + + The event loop knows the following states, that may be + queried with sd_event_get_state(). + + + + SD_EVENT_INITIAL + + The initial state the event loop is in, + before each event loop iteration. Use + sd_event_prepare() to transition the + event loop into the SD_EVENT_ARMED or + SD_EVENT_PENDING states. + + + + SD_EVENT_PREPARING + + An event source is currently being prepared, + i.e. the preparation handler is currently being executed, as + set with + sd_event_set_prepare3. This + state is only seen in the event source preparation handler + that is invoked from the + sd_event_prepare() call and is + immediately followed by SD_EVENT_ARMED or + SD_EVENT_PENDING. + + + + SD_EVENT_ARMED + + sd_event_prepare() has + been called and no event sources were ready to be + dispatched. Use sd_event_wait() to wait + for new events, and transition into + SD_EVENT_PENDING or back into + SD_EVENT_INITIAL. + + + + SD_EVENT_PENDING + + sd_event_prepare() or + sd_event_wait() have been called and + there were event sources with events pending. Use + sd_event_dispatch() to dispatch the + highest priority event source and transition back to + SD_EVENT_INITIAL, or + SD_EVENT_FINISHED. + + + + SD_EVENT_RUNNING + + A regular event source is currently being + dispatched. This state is only seen in the event source + handler that is invoked from the + sd_event_dispatch() call, and is + immediately followed by SD_EVENT_INITIAL + or SD_EVENT_FINISHED as soon the event + source handler returns. Note that during dispatching of exit + event sources the SD_EVENT_EXITING state + is seen instead. + + + + SD_EVENT_EXITING + + Similar to + SD_EVENT_RUNNING but is the state in + effect while dispatching exit event sources. It is followed by + SD_EVENT_INITIAL or + SD_EVENT_FINISHED as soon as the event + handler returns. + + + + SD_EVENT_FINISHED + + The event loop has exited. All exit event + sources have run. If the event loop is in this state it serves + no purpose anymore, and should be freed. + + + + + A simplified flow chart of the states and the calls to + transition between them is shown below. Note that + SD_EVENT_PREPARING, + SD_EVENT_RUNNING and + SD_EVENT_EXITING are not shown here. + + + INITIAL -<---<---<---<---<---<---<---<---<---<---<---<---\ + | | + | ^ + | | + v ret == 0 | + sd_event_prepare() >--->--->--->--->- ARMED | + | | ^ + | ret > 0 | | + | | | + v v ret == 0 | + PENDING <---<---<---<---<---< sd_event_wait() >--->--->--+ + | ret > 0 ^ + | | + | | + v | + sd_event_dispatch() >--->--->--->--->--->--->--->--->--->--->/ + | ret > 0 + | ret == 0 + | + v + FINISHED + + + + + Return Value + + On success, these functions return 0 or a positive integer. + On failure, they return a negative errno-style error code. In case + of sd_event_prepare() and + sd_event_wait(), a positive, non-zero return + code indicates that events are ready to be processed and zero + indicates that no events are ready. In case of + sd_event_dispatch(), a positive, non-zero + return code indicates that the event loop returned to its initial + state and zero indicates the event loop has + exited. sd_event_get_state() returns a + positive or zero state on success. + + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The event parameter is + invalid or NULL. + + + + -EBUSY + + The event loop object is not in the right + state. + + + + -ESTALE + + The event loop is already terminated. + + + + + -ECHILD + + The event loop has been created in a different process. + + + + + + Other errors are possible, too. + + + + + + See Also + + + systemd1, + sd_event_new3, + sd_event_add_io3, + sd_event_add_time3, + sd_event_add_signal3, + sd_event_add_defer3, + sd_event_add_exit3, + sd_event_add_post3, + sd_event_run3, + sd_event_get_fd3, + sd_event_source_set_prepare3 + + + + diff --git a/src/libsystemd/sd_get_seats.xml b/src/libsystemd/sd_get_seats.xml new file mode 100644 index 0000000000..37eb3fc894 --- /dev/null +++ b/src/libsystemd/sd_get_seats.xml @@ -0,0 +1,164 @@ + + + + + + + + + sd_get_seats + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_get_seats + 3 + + + + sd_get_seats + sd_get_sessions + sd_get_uids + sd_get_machine_names + Determine available seats, sessions, logged in users and virtual machines/containers + + + + + #include <systemd/sd-login.h> + + + int sd_get_seats + char ***seats + + + + int sd_get_sessions + char ***sessions + + + + int sd_get_uids + uid_t **users + + + + int sd_get_machine_names + char ***machines + + + + + + + Description + + sd_get_seats() may be used to determine + all currently available local seats. Returns a + NULL terminated array of seat identifiers. + The returned array and all strings it references need to be freed + with the libc + free3 + call after use. Note that instead of an empty array + NULL may be returned and should be considered + equivalent to an empty array. + + Similarly, sd_get_sessions() may be + used to determine all current login sessions. + + Similarly, sd_get_uids() may be used to + determine all Unix users who currently have login sessions. + + Similarly, sd_get_machine_names() may + be used to determine all current virtual machines and containers + on the system. + + Note that the returned lists are not sorted and in an + undefined order. + + + + Return Value + + On success, sd_get_seats(), + sd_get_sessions(), + sd_get_uids() and + sd_get_machine_names() return the number of + entries in the arrays. On failure, these calls return a negative + errno-style error code. + + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_get_seats(), + sd_get_sessions(), + sd_get_uids() and + sd_get_machine_names() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-login3, + sd_session_get_seat3 + + + + diff --git a/src/libsystemd/sd_id128_get_machine.xml b/src/libsystemd/sd_id128_get_machine.xml new file mode 100644 index 0000000000..2ad1f8f728 --- /dev/null +++ b/src/libsystemd/sd_id128_get_machine.xml @@ -0,0 +1,129 @@ + + + + + + + + + sd_id128_get_machine + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_id128_get_machine + 3 + + + + sd_id128_get_machine + sd_id128_get_boot + Retrieve 128-bit IDs + + + + + #include <systemd/sd-id128.h> + + + int sd_id128_get_machine + sd_id128_t *ret + + + + int sd_id128_get_boot + sd_id128_t *ret + + + + + + + Description + + sd_id128_get_machine() returns the + machine ID of the executing host. This reads and parses the + machine-id5 + file. This function caches the machine ID internally to make + retrieving the machine ID a cheap operation. + + sd_id128_get_boot() returns the boot ID + of the executing kernel. This reads and parses the + /proc/sys/kernel/random/boot_id file exposed + by the kernel. It is randomly generated early at boot and is + unique for every running kernel instance. See + random4 + for more information. This function also internally caches the + returned ID to make this call a cheap operation. + + Note that sd_id128_get_boot() always + returns a UUID v4 compatible ID. + sd_id128_get_machine() will also return a + UUID v4-compatible ID on new installations but might not on older. + It is possible to convert the machine ID into a UUID v4-compatible + one. For more information, see + machine-id5. + + For more information about the sd_id128_t + type see + sd-id1283. + + + + Return Value + + The two calls return 0 on success (in which case + ret is filled in), or a negative + errno-style error code. + + + + Notes + + The sd_id128_get_machine() and + sd_id128_get_boot() interfaces are available + as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-id1283, + machine-id5, + random4, + sd_id128_randomize3 + + + + diff --git a/src/libsystemd/sd_id128_randomize.xml b/src/libsystemd/sd_id128_randomize.xml new file mode 100644 index 0000000000..ab449d2937 --- /dev/null +++ b/src/libsystemd/sd_id128_randomize.xml @@ -0,0 +1,114 @@ + + + + + + + + + sd_id128_randomize + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_id128_randomize + 3 + + + + sd_id128_randomize + Generate 128-bit IDs + + + + + #include <systemd/sd-id128.h> + + + int sd_id128_randomize + sd_id128_t *ret + + + + + + + Description + + sd_id128_randomize() generates a new + randomized 128-bit ID and returns it in + ret. Every invocation returns a new + randomly generated ID. This uses the + /dev/urandom kernel random number + generator. + + Note that sd_id128_randomize() always + returns a UUID v4-compatible ID. + + For more information about the sd_id128_t + type, see + sd-id1283. + + journalctl1's + option may be used as a command line + front-end for sd_id128_randomize(). + + + + Return Value + + The call returns 0 on success (in which case + ret is filled in), or a negative + errno-style error code. + + + + Notes + + The sd_id128_randomize() interface is + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-id1283, + machine-id5, + random4, + sd_id128_get_machine3 + + + + diff --git a/src/libsystemd/sd_id128_to_string.xml b/src/libsystemd/sd_id128_to_string.xml new file mode 100644 index 0000000000..927d1ad5f2 --- /dev/null +++ b/src/libsystemd/sd_id128_to_string.xml @@ -0,0 +1,130 @@ + + + + + + + + + sd_id128_to_string + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_id128_to_string + 3 + + + + sd_id128_to_string + sd_id128_from_string + Format or parse 128-bit IDs as strings + + + + + #include <systemd/sd-id128.h> + + + char *sd_id128_to_string + sd_id128_t id, char s[33] + + + + int sd_id128_from_string + const char *s, sd_id128_t *ret + + + + + + + Description + + sd_id128_to_string() formats a 128-bit + ID as a character string. It expects the ID and a string array + capable of storing 33 characters. The ID will be formatted as 32 + lowercase hexadecimal digits and be terminated by a + NUL byte. + + sd_id128_from_string() implements the reverse operation: it takes a 33 character string + with 32 hexadecimal digits (either lowercase or uppercase, terminated by NUL) and parses them + back into a 128-bit ID returned in ret. Alternatively, this call can also parse a + 37-character string with a 128-bit ID formatted as RFC UUID. If ret is passed as NULL the + function will validate the passed ID string, but not actually return it in parsed form. + + For more information about the sd_id128_t + type see + sd-id1283. + Note that these calls operate the same way on all architectures, + i.e. the results do not depend on endianness. + + When formatting a 128-bit ID into a string, it is often + easier to use a format string for + printf3. + This is easily done using the + SD_ID128_FORMAT_STR and + SD_ID128_FORMAT_VAL() macros. For more + information see + sd-id1283. + + + + Return Value + + sd_id128_to_string() always succeeds + and returns a pointer to the string array passed in. + sd_id128_from_string returns 0 on success, in + which case ret is filled in, or a negative + errno-style error code. + + + + Notes + + The sd_id128_to_string() and + sd_id128_from_string() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-id1283, + printf3 + + + + diff --git a/src/libsystemd/sd_is_fifo.xml b/src/libsystemd/sd_is_fifo.xml new file mode 100644 index 0000000000..627cb87aaf --- /dev/null +++ b/src/libsystemd/sd_is_fifo.xml @@ -0,0 +1,200 @@ + + + + + + + + + sd_is_fifo + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_is_fifo + 3 + + + + sd_is_fifo + sd_is_socket + sd_is_socket_inet + sd_is_socket_unix + sd_is_mq + sd_is_special + Check the type of a file descriptor + + + + + #include <systemd/sd-daemon.h> + + + int sd_is_fifo + int fd + const char *path + + + + int sd_is_socket + int fd + int family + int type + int listening + + + + int sd_is_socket_inet + int fd + int family + int type + int listening + uint16_t port + + + + int sd_is_socket_unix + int fd + int type + int listening + const char *path + size_t length + + + + int sd_is_mq + int fd + const char *path + + + + int sd_is_special + int fd + const char *path + + + + + + + Description + + sd_is_fifo() may be called to check + whether the specified file descriptor refers to a FIFO or pipe. If + the path parameter is not + NULL, it is checked whether the FIFO is bound + to the specified file system path. + + sd_is_socket() may be called to check + whether the specified file descriptor refers to a socket. If the + family parameter is not + AF_UNSPEC, it is checked whether the socket + is of the specified family (AF_UNIX, AF_INET, + ...). If the type parameter is not 0, it is + checked whether the socket is of the specified type + (SOCK_STREAM, + SOCK_DGRAM, ...). If the + listening parameter is positive, it is + checked whether the socket is in accepting mode, i.e. + listen() has been called for it. If + listening is 0, it is checked whether the + socket is not in this mode. If the parameter is negative, no such + check is made. The listening parameter + should only be used for stream sockets and should be set to a + negative value otherwise. + + sd_is_socket_inet() is similar to + sd_is_socket(), but optionally checks the + IPv4 or IPv6 port number the socket is bound to, unless + port is zero. For this call + family must be passed as either + AF_UNSPEC, AF_INET, or + AF_INET6. + + sd_is_socket_unix() is similar to + sd_is_socket() but optionally checks the + AF_UNIX path the socket is bound to, unless + the path parameter is + NULL. For normal file system + AF_UNIX sockets, set the + length parameter to 0. For Linux abstract + namespace sockets, set the length to the + size of the address, including the initial 0 byte, and set the + path to the initial 0 byte of the socket + address. + + sd_is_mq() may be called to check + whether the specified file descriptor refers to a POSIX message + queue. If the path parameter is not + NULL, it is checked whether the message queue + is bound to the specified name. + + sd_is_special() may be called to check + whether the specified file descriptor refers to a special file. If + the path parameter is not + NULL, it is checked whether the file + descriptor is bound to the specified file name. Special files in + this context are character device nodes and files in + /proc or /sys. + + + + Return Value + + On failure, these calls return a negative errno-style error + code. If the file descriptor is of the specified type and bound to + the specified address, a positive return value is returned, + otherwise zero. + + + + Notes + + + + Internally, these function use a combination of + fstat() and + getsockname() to check the file descriptor + type and where it is bound to. + + + + See Also + + systemd1, + sd-daemon3, + sd_listen_fds3, + systemd.service5, + systemd.socket5 + + + + diff --git a/src/libsystemd/sd_journal_add_match.xml b/src/libsystemd/sd_journal_add_match.xml new file mode 100644 index 0000000000..98415d53fd --- /dev/null +++ b/src/libsystemd/sd_journal_add_match.xml @@ -0,0 +1,216 @@ + + + + + + + + + sd_journal_add_match + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_add_match + 3 + + + + sd_journal_add_match + sd_journal_add_disjunction + sd_journal_add_conjunction + sd_journal_flush_matches + Add or remove entry matches + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_add_match + sd_journal *j + const void *data + size_t size + + + + int sd_journal_add_disjunction + sd_journal *j + + + + int sd_journal_add_conjunction + sd_journal *j + + + + void sd_journal_flush_matches + sd_journal *j + + + + + + Description + + sd_journal_add_match() adds a match by + which to filter the entries of the journal file. Matches applied + with this call will filter what can be iterated through and read + from the journal file via calls like + sd_journal_next3 + and + sd_journal_get_data3. + Parameter data must be of the form + FIELD=value, + where the FIELD part is a short uppercase string consisting only + of 0–9, A–Z and the underscore; it may not begin with two underscores or be the empty + string. The value part may be anything, including binary. Parameter + size specifies the number of bytes in data + (i.e. the length of FIELD, plus one, plus the length of + value). Parameter size may also be + specified as 0, in which case data + must be a NUL-terminated string, and the bytes before the terminating + zero are used as the match. + + If a match is applied, only entries with this field set + will be iterated. Multiple matches may be active at the same time: + If they apply to different fields, only entries with both fields + set like this will be iterated. If they apply to the same fields, + only entries where the field takes one of the specified values + will be iterated. Well known fields are documented in + systemd.journal-fields7. + Whenever a new match is added the current entry position is reset, + and + sd_journal_next3 + (or a similar call) needs to be called before entries can be read + again. + + sd_journal_add_disjunction() may be + used to insert a disjunction (i.e. logical OR) in the match list. + If this call is invoked, all previously added matches since the + last invocation of + sd_journal_add_disjunction() or + sd_journal_add_conjunction() are combined in + an OR with all matches added afterwards, until + sd_journal_add_disjunction() or + sd_journal_add_conjunction() is invoked again + to begin the next OR or AND term. + + sd_journal_add_conjunction() may be + used to insert a conjunction (i.e. logical AND) in the match list. + If this call is invoked, all previously added matches since the + last invocation of + sd_journal_add_conjunction() are combined in + an AND with all matches added afterwards, until + sd_journal_add_conjunction() is invoked again + to begin the next AND term. The combination of + sd_journal_add_match(), + sd_journal_add_disjunction() and + sd_journal_add_conjunction() may be used to + build complex search terms, even though full logical expressions + are not available. Note that + sd_journal_add_conjunction() operates one + level 'higher' than + sd_journal_add_disjunction(). It is hence + possible to build an expression of AND terms, consisting of OR + terms, consisting of AND terms, consisting of OR terms of matches + (the latter OR expression is implicitly created for matches with + the same field name, see above). + + sd_journal_flush_matches() may be used + to flush all matches, disjunction and conjunction terms again. + After this call all filtering is removed and all entries in the + journal will be iterated again. + + Note that filtering via matches only applies to the way the + journal is read, it has no effect on storage on disk. + + + + Return Value + + sd_journal_add_match(), + sd_journal_add_disjunction() and + sd_journal_add_conjunction() + return 0 on success or a negative errno-style error + code. sd_journal_flush_matches() + returns nothing. + + + + Notes + + The sd_journal_add_match(), + sd_journal_add_disjunction(), + sd_journal_add_conjunction() and + sd_journal_flush_matches() + interfaces are available as a shared library, which can + be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + Examples + + The following example adds matches to a journal context + object to iterate only through messages generated by the Avahi + service at the four error log levels, plus all messages of the + message ID 03bb1dab98ab4ecfbf6fff2738bdd964 coming from any + service (this example lacks the necessary error checking): + + ... +int add_matches(sd_journal *j) { + sd_journal_add_match(j, "_SYSTEMD_UNIT=avahi-daemon.service", 0); + sd_journal_add_match(j, "PRIORITY=0", 0); + sd_journal_add_match(j, "PRIORITY=1", 0); + sd_journal_add_match(j, "PRIORITY=2", 0); + sd_journal_add_match(j, "PRIORITY=3", 0); + sd_journal_add_disjunction(j); + sd_journal_add_match(j, "MESSAGE_ID=03bb1dab98ab4ecfbf6fff2738bdd964", 0); +} + + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_data3, + systemd.journal-fields7 + + + + diff --git a/src/libsystemd/sd_journal_enumerate_fields.xml b/src/libsystemd/sd_journal_enumerate_fields.xml new file mode 100644 index 0000000000..fa5884106b --- /dev/null +++ b/src/libsystemd/sd_journal_enumerate_fields.xml @@ -0,0 +1,161 @@ + + + + + + + + + sd_journal_enumerate_fields + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_enumerate_fields + 3 + + + + sd_journal_enumerate_fields + sd_journal_restart_fields + SD_JOURNAL_FOREACH_FIELD + Read used field names from the journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_enumerate_fields + sd_journal *j + const char **field + + + + void sd_journal_restart_fields + sd_journal *j + + + + SD_JOURNAL_FOREACH_FIELD + sd_journal *j + const char *field + + + + + + + Description + + sd_journal_enumerate_fields() may be used to iterate through all field names used in the + opened journal files. On each invocation the next field name is returned. The order of the returned field names is + not defined. It takes two arguments: the journal context object, plus a pointer to a constant string pointer where + the field name is stored in. The returned data is in a read-only memory map and is only valid until the next + invocation of sd_journal_enumerate_fields(). Note that this call is subject to the data field + size threshold as controlled by sd_journal_set_data_threshold(). + + sd_journal_restart_fields() resets the field name enumeration index to the beginning of + the list. The next invocation of sd_journal_enumerate_fields() will return the first field + name again. + + The SD_JOURNAL_FOREACH_FIELD() macro may be used as a handy wrapper around + sd_journal_restart_fields() and sd_journal_enumerate_fields(). + + These functions currently are not influenced by matches set with sd_journal_add_match() + but this might change in a later version of this software. + + To retrieve the possible values a specific field can take use + sd_journal_query_unique3. + + + + Return Value + + sd_journal_enumerate_fields() returns a + positive integer if the next field name has been read, 0 when no + more field names are known, or a negative errno-style error code. + sd_journal_restart_fields() returns + nothing. + + + + Notes + + The sd_journal_enumerate_fields() and sd_journal_restart_fields() + interfaces are available as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 file. + + + + Examples + + Use the SD_JOURNAL_FOREACH_FIELD macro to iterate through all field names in use in the + current journal. + + #include <stdio.h> +#include <string.h> +#include <systemd/sd-journal.h> + +int main(int argc, char *argv[]) { + sd_journal *j; + const char *field; + int r; + + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + fprintf(stderr, "Failed to open journal: %s\n", strerror(-r)); + return 1; + } + SD_JOURNAL_FOREACH_FIELD(j, field) + printf("%s\n", field); + sd_journal_close(j); + return 0; +} + + + + + See Also + + + systemd1, + systemd.journal-fields7, + sd-journal3, + sd_journal_open3, + sd_journal_query_unique3, + sd_journal_get_data3, + sd_journal_add_match3 + + + + diff --git a/src/libsystemd/sd_journal_get_catalog.xml b/src/libsystemd/sd_journal_get_catalog.xml new file mode 100644 index 0000000000..c19eb11b20 --- /dev/null +++ b/src/libsystemd/sd_journal_get_catalog.xml @@ -0,0 +1,137 @@ + + + + + + + + + sd_journal_get_catalog + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_catalog + 3 + + + + sd_journal_get_catalog + sd_journal_get_catalog_for_message_id + Retrieve message catalog entry + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_catalog + sd_journal *j + char **ret + + + + int sd_journal_get_catalog_for_message_id + sd_id128_t id + char **ret + + + + + + + + Description + + sd_journal_get_catalog() retrieves a + message catalog entry for the current journal entry. This will + look up an entry in the message catalog by using the + MESSAGE_ID= field of the current journal entry. + Before returning the entry all journal field names in the catalog + entry text enclosed in "@" will be replaced by the respective + field values of the current entry. If a field name referenced in + the message catalog entry does not exist, in the current journal + entry, the "@" will be removed, but the field name otherwise left + untouched. + + sd_journal_get_catalog_for_message_id() + works similar to sd_journal_get_catalog() but + the entry is looked up by the specified message ID (no open + journal context is necessary for this), and no field substitution + is performed. + + For more information about the journal message catalog + please refer to the Journal + Message Catalogs documentation page. + + + + Return Value + + sd_journal_get_catalog() and + sd_journal_get_catalog_for_message_id() + return 0 on success or a negative errno-style error code. If no + matching message catalog entry is found, -ENOENT is + returned. + + On successful return, ret points to a + new string, which must be freed with + free3. + + + + + Notes + + The sd_journal_get_catalog() and + sd_journal_get_catalog_for_message_id() + interfaces are available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + systemd.journal-fields7, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_data3, + malloc3 + + + + diff --git a/src/libsystemd/sd_journal_get_cursor.xml b/src/libsystemd/sd_journal_get_cursor.xml new file mode 100644 index 0000000000..a400d8b1b5 --- /dev/null +++ b/src/libsystemd/sd_journal_get_cursor.xml @@ -0,0 +1,144 @@ + + + + + + + + + sd_journal_get_cursor + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_cursor + 3 + + + + sd_journal_get_cursor + sd_journal_test_cursor + Get cursor string for or test cursor string against the current journal entry + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_cursor + sd_journal *j + char **cursor + + + + int sd_journal_test_cursor + sd_journal *j + const char *cursor + + + + + + + Description + + sd_journal_get_cursor() returns a + cursor string for the current journal entry. A cursor is a + serialization of the current journal position formatted as text. + The string only contains printable characters and can be passed + around in text form. The cursor identifies a journal entry + globally and in a stable way and may be used to later seek to it + via + sd_journal_seek_cursor3. + The cursor string should be considered opaque and not be parsed by + clients. Seeking to a cursor position without the specific entry + being available locally will seek to the next closest (in terms of + time) available entry. The call takes two arguments: a journal + context object and a pointer to a string pointer where the cursor + string will be placed. The string is allocated via libc + malloc3 + and should be freed after use with + free3. + + Note that sd_journal_get_cursor() will + not work before + sd_journal_next3 + (or related call) has been called at least once, in order to + position the read pointer at a valid entry. + + sd_journal_test_cursor() + may be used to check whether the current position in + the journal matches the specified cursor. This is + useful since cursor strings do not uniquely identify + an entry: the same entry might be referred to by + multiple different cursor strings, and hence string + comparing cursors is not possible. Use this call to + verify after an invocation of + sd_journal_seek_cursor3 + whether the entry being sought to was actually found + in the journal or the next closest entry was used + instead. + + + + Return Value + + sd_journal_get_cursor() returns 0 on + success or a negative errno-style error code. + sd_journal_test_cursor() returns positive if + the current entry matches the specified cursor, 0 if it does not + match the specified cursor or a negative errno-style error code on + failure. + + + + Notes + + The sd_journal_get_cursor() and + sd_journal_test_cursor() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_seek_cursor3 + + + + diff --git a/src/libsystemd/sd_journal_get_cutoff_realtime_usec.xml b/src/libsystemd/sd_journal_get_cutoff_realtime_usec.xml new file mode 100644 index 0000000000..23e7cc65e8 --- /dev/null +++ b/src/libsystemd/sd_journal_get_cutoff_realtime_usec.xml @@ -0,0 +1,145 @@ + + + + + + + + + sd_journal_get_cutoff_realtime_usec + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_cutoff_realtime_usec + 3 + + + + sd_journal_get_cutoff_realtime_usec + sd_journal_get_cutoff_monotonic_usec + Read cut-off timestamps from the current journal entry + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_cutoff_realtime_usec + sd_journal *j + uint64_t *from + uint64_t *to + + + + int sd_journal_get_cutoff_monotonic_usec + sd_journal *j + sd_id128_t boot_id + uint64_t *from + uint64_t *to + + + + + + + Description + + sd_journal_get_cutoff_realtime_usec() + retrieves the realtime (wallclock) timestamps of the first and + last entries accessible in the journal. It takes three arguments: + the journal context object j and two + pointers from and to + pointing at 64-bit unsigned integers to store the timestamps in. + The timestamps are in microseconds since the epoch, i.e. + CLOCK_REALTIME. Either one of the two + timestamp arguments may be passed as NULL in + case the timestamp is not needed, but not both. + + sd_journal_get_cutoff_monotonic_usec() + retrieves the monotonic timestamps of the first and last entries + accessible in the journal. It takes three arguments: the journal + context object j, a 128-bit identifier for + the boot boot_id, and two pointers to + 64-bit unsigned integers to store the timestamps, + from and to. The + timestamps are in microseconds since boot-up of the specific boot, + i.e. CLOCK_MONOTONIC. Since the monotonic + clock begins new with every reboot it only defines a well-defined + point in time when used together with an identifier identifying + the boot, see + sd_id128_get_boot3 + for more information. The function will return the timestamps for + the boot identified by the passed boot ID. Either one of the two + timestamp arguments may be passed as NULL in + case the timestamp is not needed, but not both. + + + + Return Value + + sd_journal_get_cutoff_realtime_usec() + and sd_journal_get_cutoff_monotonic_usec() + return 1 on success, 0 if not suitable entries are in the journal + or a negative errno-style error code. + + Locations pointed to by parameters + from and to will be + set only if the return value is positive, and obviously, the + parameters are non-null. + + + + Notes + + The + sd_journal_get_cutoff_realtime_usec() and + sd_journal_get_cutoff_monotonic_usec() + interfaces are available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_get_realtime_usec3, + sd_id128_get_boot3, + clock_gettime2 + + + + diff --git a/src/libsystemd/sd_journal_get_data.xml b/src/libsystemd/sd_journal_get_data.xml new file mode 100644 index 0000000000..1321114de0 --- /dev/null +++ b/src/libsystemd/sd_journal_get_data.xml @@ -0,0 +1,235 @@ + + + + + + + + + sd_journal_get_data + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_data + 3 + + + + sd_journal_get_data + sd_journal_enumerate_data + sd_journal_restart_data + SD_JOURNAL_FOREACH_DATA + sd_journal_set_data_threshold + sd_journal_get_data_threshold + Read data fields from the current journal entry + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_data + sd_journal *j + const char *field + const void **data + size_t *length + + + + int sd_journal_enumerate_data + sd_journal *j + const void **data + size_t *length + + + + void sd_journal_restart_data + sd_journal *j + + + + SD_JOURNAL_FOREACH_DATA + sd_journal *j + const void *data + size_t length + + + + int sd_journal_set_data_threshold + sd_journal *j + size_t sz + + + + int sd_journal_get_data_threshold + sd_journal *j + size_t *sz + + + + + + Description + + sd_journal_get_data() gets the data + object associated with a specific field from the current journal + entry. It takes four arguments: the journal context object, a + string with the field name to request, plus a pair of pointers to + pointer/size variables where the data object and its size shall be + stored in. The field name should be an entry field name. + Well-known field names are listed in + systemd.journal-fields7. + The returned data is in a read-only memory map and is only valid + until the next invocation of + sd_journal_get_data() or + sd_journal_enumerate_data(), or the read + pointer is altered. Note that the data returned will be prefixed + with the field name and '='. Also note that, by default, data fields + larger than 64K might get truncated to 64K. This threshold may be + changed and turned off with + sd_journal_set_data_threshold() (see + below). + + sd_journal_enumerate_data() may be used + to iterate through all fields of the current entry. On each + invocation the data for the next field is returned. The order of + these fields is not defined. The data returned is in the same + format as with sd_journal_get_data() and also + follows the same life-time semantics. + + sd_journal_restart_data() resets the + data enumeration index to the beginning of the entry. The next + invocation of sd_journal_enumerate_data() + will return the first field of the entry again. + + Note that the SD_JOURNAL_FOREACH_DATA() + macro may be used as a handy wrapper around + sd_journal_restart_data() and + sd_journal_enumerate_data(). + + Note that these functions will not work before + sd_journal_next3 + (or related call) has been called at least once, in order to + position the read pointer at a valid entry. + + sd_journal_set_data_threshold() may be + used to change the data field size threshold for data returned by + sd_journal_get_data(), + sd_journal_enumerate_data() and + sd_journal_enumerate_unique(). This threshold + is a hint only: it indicates that the client program is interested + only in the initial parts of the data fields, up to the threshold + in size — but the library might still return larger data objects. + That means applications should not rely exclusively on this + setting to limit the size of the data fields returned, but need to + apply an explicit size limit on the returned data as well. This + threshold defaults to 64K by default. To retrieve the complete + data fields this threshold should be turned off by setting it to + 0, so that the library always returns the complete data objects. + It is recommended to set this threshold as low as possible since + this relieves the library from having to decompress large + compressed data objects in full. + + sd_journal_get_data_threshold() returns + the currently configured data field size threshold. + + + + Return Value + + sd_journal_get_data() returns 0 on + success or a negative errno-style error code. If the current entry + does not include the specified field, -ENOENT is returned. If + sd_journal_next3 + has not been called at least once, -EADDRNOTAVAIL is returned. + sd_journal_enumerate_data() returns a + positive integer if the next field has been read, 0 when no more + fields are known, or a negative errno-style error code. + sd_journal_restart_data() returns nothing. + sd_journal_set_data_threshold() and + sd_journal_get_threshold() return 0 on + success or a negative errno-style error code. + + + + Notes + + The sd_journal_get_data(), + sd_journal_enumerate_data(), + sd_journal_restart_data(), + sd_journal_set_data_threshold() and + sd_journal_get_data_threshold() interfaces + are available as a shared library, which can be compiled and + linked to with the + libsystemd pkg-config1 + file. + + + + Examples + + See + sd_journal_next3 + for a complete example how to use + sd_journal_get_data(). + + Use the + SD_JOURNAL_FOREACH_DATA macro to + iterate through all fields of the current journal + entry: + + ... +int print_fields(sd_journal *j) { + const void *data; + size_t length; + SD_JOURNAL_FOREACH_DATA(j, data, length) + printf("%.*s\n", (int) length, data); +} +... + + + + + See Also + + + systemd1, + systemd.journal-fields7, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_realtime_usec3, + sd_journal_query_unique3 + + + + diff --git a/src/libsystemd/sd_journal_get_fd.xml b/src/libsystemd/sd_journal_get_fd.xml new file mode 100644 index 0000000000..61293f7f99 --- /dev/null +++ b/src/libsystemd/sd_journal_get_fd.xml @@ -0,0 +1,332 @@ + + + + + + + + + sd_journal_get_fd + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_fd + 3 + + + + sd_journal_get_fd + sd_journal_get_events + sd_journal_get_timeout + sd_journal_process + sd_journal_wait + sd_journal_reliable_fd + SD_JOURNAL_NOP + SD_JOURNAL_APPEND + SD_JOURNAL_INVALIDATE + Journal change notification + interface + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_fd + sd_journal *j + + + + int sd_journal_get_events + sd_journal *j + + + + int sd_journal_get_timeout + sd_journal *j + uint64_t *timeout_usec + + + + int sd_journal_process + sd_journal *j + + + + int sd_journal_wait + sd_journal *j + uint64_t timeout_usec + + + + int sd_journal_reliable_fd + sd_journal *j + + + + + + + Description + + sd_journal_get_fd() returns a file + descriptor that may be asynchronously polled in an external event + loop and is signaled as soon as the journal changes, because new + entries or files were added, rotation took place, or files have + been deleted, and similar. The file descriptor is suitable for + usage in + poll2. + Use sd_journal_get_events() for an events + mask to watch for. The call takes one argument: the journal + context object. Note that not all file systems are capable of + generating the necessary events for wakeups from this file + descriptor for changes to be noticed immediately. In particular + network files systems do not generate suitable file change events + in all cases. Cases like this can be detected with + sd_journal_reliable_fd(), below. + sd_journal_get_timeout() will ensure in these + cases that wake-ups happen frequently enough for changes to be + noticed, although with a certain latency. + + sd_journal_get_events() will return the + poll() mask to wait for. This function will + return a combination of POLLIN and + POLLOUT and similar to fill into the + .events field of struct + pollfd. + + sd_journal_get_timeout() will return a + timeout value for usage in poll(). This + returns a value in microseconds since the epoch of + CLOCK_MONOTONIC for timing out + poll() in timeout_usec. + See + clock_gettime2 + for details about CLOCK_MONOTONIC. If there + is no timeout to wait for, this will fill in (uint64_t) + -1 instead. Note that poll() takes + a relative timeout in milliseconds rather than an absolute timeout + in microseconds. To convert the absolute 'us' timeout into + relative 'ms', use code like the following: + + uint64_t t; +int msec; +sd_journal_get_timeout(m, &t); +if (t == (uint64_t) -1) + msec = -1; +else { + struct timespec ts; + uint64_t n; + clock_getttime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; +} + + The code above does not do any error checking for brevity's + sake. The calculated msec integer can be passed + directly as poll()'s timeout + parameter. + + After each poll() wake-up + sd_journal_process() needs to be called to + process events. This call will also indicate what kind of change + has been detected (see below; note that spurious wake-ups are + possible). + + A synchronous alternative for using + sd_journal_get_fd(), + sd_journal_get_events(), + sd_journal_get_timeout() and + sd_journal_process() is + sd_journal_wait(). It will synchronously wait + until the journal gets changed. The maximum time this call sleeps + may be controlled with the timeout_usec + parameter. Pass (uint64_t) -1 to wait + indefinitely. Internally this call simply combines + sd_journal_get_fd(), + sd_journal_get_events(), + sd_journal_get_timeout(), + poll() and + sd_journal_process() into one. + + sd_journal_reliable_fd() may be used to + check whether the wakeup events from the file descriptor returned + by sd_journal_get_fd() are known to be + immediately triggered. On certain file systems where file change + events from the OS are not available (such as NFS) changes need to + be polled for repeatedly, and hence are detected only with a + certain latency. This call will return a positive value if the + journal changes are detected immediately and zero when they need + to be polled for and hence might be noticed only with a certain + latency. Note that there is usually no need to invoke this function + directly as sd_journal_get_timeout() on these + file systems will ask for timeouts explicitly anyway. + + + + Return Value + + sd_journal_get_fd() returns a valid + file descriptor on success or a negative errno-style error + code. + + sd_journal_get_events() returns a + combination of POLLIN, + POLLOUT and suchlike on success or a negative + errno-style error code. + + sd_journal_reliable_fd() returns a + positive integer if the file descriptor returned by + sd_journal_get_fd() will generate wake-ups + immediately for all journal changes. Returns 0 if there might be a + latency involved. + + sd_journal_process() and + sd_journal_wait() return one of + SD_JOURNAL_NOP, + SD_JOURNAL_APPEND or + SD_JOURNAL_INVALIDATE on success or a + negative errno-style error code. If + SD_JOURNAL_NOP is returned, the journal did + not change since the last invocation. If + SD_JOURNAL_APPEND is returned, new entries + have been appended to the end of the journal. If + SD_JOURNAL_INVALIDATE, journal files were + added or removed (possibly due to rotation). In the latter event, + live-view UIs should probably refresh their entire display, while + in the case of SD_JOURNAL_APPEND, it is + sufficient to simply continue reading at the previous end of the + journal. + + + + Notes + + The sd_journal_get_fd(), + sd_journal_get_events(), + sd_journal_reliable_fd(), + sd_journal_process() and + sd_journal_wait() interfaces are available as + a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + Examples + + Iterating through the journal, in a live view tracking all + changes: + + #include <stdio.h> +#include <string.h> +#include <systemd/sd-journal.h> + +int main(int argc, char *argv[]) { + int r; + sd_journal *j; + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + fprintf(stderr, "Failed to open journal: %s\n", strerror(-r)); + return 1; + } + for (;;) { + const void *d; + size_t l; + r = sd_journal_next(j); + if (r < 0) { + fprintf(stderr, "Failed to iterate to next entry: %s\n", strerror(-r)); + break; + } + if (r == 0) { + /* Reached the end, let's wait for changes, and try again */ + r = sd_journal_wait(j, (uint64_t) -1); + if (r < 0) { + fprintf(stderr, "Failed to wait for changes: %s\n", strerror(-r)); + break; + } + continue; + } + r = sd_journal_get_data(j, "MESSAGE", &d, &l); + if (r < 0) { + fprintf(stderr, "Failed to read message field: %s\n", strerror(-r)); + continue; + } + printf("%.*s\n", (int) l, (const char*) d); + } + sd_journal_close(j); + return 0; +} + + Waiting with poll() (this + example lacks all error checking for the sake of + simplicity): + + #include <poll.h> +#include <systemd/sd-journal.h> + +int wait_for_changes(sd_journal *j) { + struct pollfd pollfd; + int msec; + + sd_journal_get_timeout(m, &t); + if (t == (uint64_t) -1) + msec = -1; + else { + struct timespec ts; + uint64_t n; + clock_getttime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; + } + + pollfd.fd = sd_journal_get_fd(j); + pollfd.events = sd_journal_get_events(j); + poll(&pollfd, 1, msec); + return sd_journal_process(j); +} + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + poll2, + clock_gettime2 + + + + diff --git a/src/libsystemd/sd_journal_get_realtime_usec.xml b/src/libsystemd/sd_journal_get_realtime_usec.xml new file mode 100644 index 0000000000..607d74666b --- /dev/null +++ b/src/libsystemd/sd_journal_get_realtime_usec.xml @@ -0,0 +1,141 @@ + + + + + + + + + sd_journal_get_realtime_usec + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_realtime_usec + 3 + + + + sd_journal_get_realtime_usec + sd_journal_get_monotonic_usec + Read timestamps from the current journal entry + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_realtime_usec + sd_journal *j + uint64_t *usec + + + + int sd_journal_get_monotonic_usec + sd_journal *j + uint64_t *usec + sd_id128_t *boot_id + + + + + + + Description + + sd_journal_get_realtime_usec() gets the + realtime (wallclock) timestamp of the current journal entry. It + takes two arguments: the journal context object and a pointer to a + 64-bit unsigned integer to store the timestamp in. The timestamp + is in microseconds since the epoch, i.e. + CLOCK_REALTIME. + + sd_journal_get_monotonic_usec() gets + the monotonic timestamp of the current journal entry. It takes + three arguments: the journal context object, a pointer to a 64-bit + unsigned integer to store the timestamp in, as well as a 128-bit + ID buffer to store the boot ID of the monotonic timestamp. The + timestamp is in microseconds since boot-up of the specific boot, + i.e. CLOCK_MONOTONIC. Since the monotonic + clock begins new with every reboot, it only defines a well-defined + point in time when used together with an identifier identifying + the boot. See + sd_id128_get_boot3 + for more information. If the boot ID parameter is passed + NULL, the function will fail if the monotonic + timestamp of the current entry is not of the current system + boot. + + Note that these functions will not work before + sd_journal_next3 + (or related call) has been called at least + once, in order to position the read pointer at a valid entry. + + + + Return Value + + sd_journal_get_realtime_usec() and + sd_journal_get_monotonic_usec() returns 0 on + success or a negative errno-style error code. If the boot ID + parameter was passed NULL and the monotonic + timestamp of the current journal entry is not of the current + system boot, -ESTALE is returned by + sd_journal_get_monotonic_usec(). + + + + Notes + + The sd_journal_get_realtime_usec() and + sd_journal_get_monotonic_usec() interfaces + are available as a shared library, which can be compiled and + linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_data3, + sd_id128_get_boot3, + clock_gettime2, + sd_journal_get_cutoff_realtime_usec3 + + + + diff --git a/src/libsystemd/sd_journal_get_usage.xml b/src/libsystemd/sd_journal_get_usage.xml new file mode 100644 index 0000000000..72c804d834 --- /dev/null +++ b/src/libsystemd/sd_journal_get_usage.xml @@ -0,0 +1,100 @@ + + + + + + + + + sd_journal_get_usage + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_get_usage + 3 + + + + sd_journal_get_usage + Journal disk usage + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_get_usage + sd_journal *j + uint64_t *bytes + + + + + + + Description + + sd_journal_get_usage() determines the + total disk space currently used by journal files (in bytes). If + SD_JOURNAL_LOCAL_ONLY was passed when opening + the journal, this value will only reflect the size of journal + files of the local host, otherwise of all hosts. + + + + Return Value + + sd_journal_get_usage() returns 0 on + success or a negative errno-style error code. + + + + Notes + + The sd_journal_get_usage() interface is + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + + + + diff --git a/src/libsystemd/sd_journal_has_runtime_files.xml b/src/libsystemd/sd_journal_has_runtime_files.xml new file mode 100644 index 0000000000..237e649206 --- /dev/null +++ b/src/libsystemd/sd_journal_has_runtime_files.xml @@ -0,0 +1,95 @@ + + + + + + + + + sd_journal_has_runtime_files + systemd + + + + Developer + Jan + Synáček + jan.synacek@gmail.com + + + + + + sd_journal_has_runtime_files + 3 + + + + sd_journal_has_runtime_files + sd_journal_has_persistent_files + Query availability of runtime or persistent journal files. + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_has_runtime_files + sd_journal *j + + + + int sd_journal_has_persistent_files + sd_journal *j + + + + + + + Description + + sd_journal_has_runtime_files() returns a positive value + if runtime journal files (present in /run/systemd/journal/) have been found. + Otherwise returns 0. + + sd_journal_has_persistent_files() returns a positive value + if persistent journal files (present in /var/log/journal/) have been found. + Otherwise returns 0. + + + + Return value + Both sd_journal_has_runtime_files() + and sd_journal_has_persistent_files() return -EINVAL + if their argument is NULL. + + + + + See Also + + sd-journal3 + + + + diff --git a/src/libsystemd/sd_journal_next.xml b/src/libsystemd/sd_journal_next.xml new file mode 100644 index 0000000000..115fe26661 --- /dev/null +++ b/src/libsystemd/sd_journal_next.xml @@ -0,0 +1,207 @@ + + + + + + + + + sd_journal_next + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_next + 3 + + + + sd_journal_next + sd_journal_previous + sd_journal_next_skip + sd_journal_previous_skip + SD_JOURNAL_FOREACH + SD_JOURNAL_FOREACH_BACKWARDS + Advance or set back the read pointer in the journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_next + sd_journal *j + + + + int sd_journal_previous + sd_journal *j + + + + int sd_journal_next_skip + sd_journal *j + uint64_t skip + + + + int sd_journal_previous_skip + sd_journal *j + uint64_t skip + + + + SD_JOURNAL_FOREACH + sd_journal *j + + + + SD_JOURNAL_FOREACH_BACKWARDS + sd_journal *j + + + + + + Description + + sd_journal_next() advances the read + pointer into the journal by one entry. The only argument taken is + a journal context object as allocated via + sd_journal_open3. + After successful invocation the entry may be read with functions + such as + sd_journal_get_data3. + + Similarly, sd_journal_previous() sets + the read pointer back one entry. + + sd_journal_next_skip() and + sd_journal_previous_skip() advance/set back + the read pointer by multiple entries at once, as specified in the + skip parameter. + + The journal is strictly ordered by reception time, and hence + advancing to the next entry guarantees that the entry then + pointing to is later in time than then previous one, or has the + same timestamp. + + Note that + sd_journal_get_data3 + and related calls will fail unless + sd_journal_next() has been invoked at least + once in order to position the read pointer on a journal + entry. + + Note that the SD_JOURNAL_FOREACH() + macro may be used as a wrapper around + sd_journal_seek_head3 + and sd_journal_next() in order to make + iterating through the journal easier. See below for an example. + Similarly, SD_JOURNAL_FOREACH_BACKWARDS() may + be used for iterating the journal in reverse order. + + + + Return Value + + The four calls return the number of entries advanced/set + back on success or a negative errno-style error code. When the end + or beginning of the journal is reached, a number smaller than + requested is returned. More specifically, if + sd_journal_next() or + sd_journal_previous() reach the end/beginning + of the journal they will return 0, instead of 1 when they are + successful. This should be considered an EOF marker. + + + + Notes + + The sd_journal_next(), + sd_journal_previous(), + sd_journal_next_skip() and + sd_journal_previous_skip() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + Examples + + Iterating through the journal: + + #include <stdio.h> +#include <string.h> +#include <systemd/sd-journal.h> + +int main(int argc, char *argv[]) { + int r; + sd_journal *j; + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + fprintf(stderr, "Failed to open journal: %s\n", strerror(-r)); + return 1; + } + SD_JOURNAL_FOREACH(j) { + const char *d; + size_t l; + + r = sd_journal_get_data(j, "MESSAGE", (const void **)&d, &l); + if (r < 0) { + fprintf(stderr, "Failed to read message field: %s\n", strerror(-r)); + continue; + } + + printf("%.*s\n", (int) l, d); + } + sd_journal_close(j); + return 0; +} + + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_get_data3, + sd_journal_get_realtime_usec3, + sd_journal_get_cursor3 + + + + diff --git a/src/libsystemd/sd_journal_open.xml b/src/libsystemd/sd_journal_open.xml new file mode 100644 index 0000000000..153af2387f --- /dev/null +++ b/src/libsystemd/sd_journal_open.xml @@ -0,0 +1,228 @@ + + + + + + + + + sd_journal_open + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_open + 3 + + + + sd_journal_open + sd_journal_open_directory + sd_journal_open_directory_fd + sd_journal_open_files + sd_journal_open_files_fd + sd_journal_close + sd_journal + SD_JOURNAL_LOCAL_ONLY + SD_JOURNAL_RUNTIME_ONLY + SD_JOURNAL_SYSTEM + SD_JOURNAL_CURRENT_USER + SD_JOURNAL_OS_ROOT + Open the system journal for reading + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_open + sd_journal **ret + int flags + + + + int sd_journal_open_directory + sd_journal **ret + const char *path + int flags + + + + int sd_journal_open_directory_fd + sd_journal **ret + int fd + int flags + + + + int sd_journal_open_files + sd_journal **ret + const char **paths + int flags + + + + int sd_journal_open_files_fd + sd_journal **ret + int fds[] + unsigned n_fds + int flags + + + + void sd_journal_close + sd_journal *j + + + + + + Description + + sd_journal_open() opens the log journal + for reading. It will find all journal files automatically and + interleave them automatically when reading. As first argument it + takes a pointer to a sd_journal pointer, which, + on success, will contain a journal context object. The second + argument is a flags field, which may consist of the following + flags ORed together: SD_JOURNAL_LOCAL_ONLY + makes sure only journal files generated on the local machine will + be opened. SD_JOURNAL_RUNTIME_ONLY makes sure + only volatile journal files will be opened, excluding those which + are stored on persistent storage. + SD_JOURNAL_SYSTEM will cause journal files of + system services and the kernel (in opposition to user session + processes) to be opened. + SD_JOURNAL_CURRENT_USER will cause journal + files of the current user to be opened. If neither + SD_JOURNAL_SYSTEM nor + SD_JOURNAL_CURRENT_USER are specified, all + journal file types will be opened. + + sd_journal_open_directory() is similar to sd_journal_open() but + takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved + automatically. This call also takes a flags argument. The only flags parameter accepted by this call is + SD_JOURNAL_OS_ROOT. If specified, the journal files are searched below the usual + /var/log/journal and /run/log/journal relative to the specified path, + instead of directly beneath it. + + sd_journal_open_directory_fd() is similar to + sd_journal_open_directory(), but takes a file descriptor referencing a directory in the file + system instead of an absolute file system path. + + sd_journal_open_files() is similar to sd_journal_open() but takes a + NULL-terminated list of file paths to open. All files will be opened and interleaved + automatically. This call also takes a flags argument, but it must be passed as 0 as no flags are currently + understood for this call. Please note that in the case of a live journal, this function is only useful for + debugging, because individual journal files can be rotated at any moment, and the opening of specific files is + inherently racy. + + sd_journal_open_files_fd() is similar to sd_journal_open_files() + but takes an array of open file descriptors that must reference journal files, instead of an array of file system + paths. Pass the array of file descriptors as second argument, and the number of array entries in the third. The + flags parameter must be passed as 0. + + sd_journal objects cannot be used in the + child after a fork. Functions which take a journal object as an + argument (sd_journal_next() and others) will + return -ECHILD after a fork. + + + sd_journal_close() will close the + journal context allocated with + sd_journal_open() or + sd_journal_open_directory() and free its + resources. + + When opening the journal only journal files accessible to + the calling user will be opened. If journal files are not + accessible to the caller, this will be silently ignored. + + See + sd_journal_next3 + for an example of how to iterate through the journal after opening + it with sd_journal_open(). + + A journal context object returned by + sd_journal_open() references a specific + journal entry as current entry, similar to a + file seek index in a classic file system file, but without + absolute positions. It may be altered with + sd_journal_next3 + and + sd_journal_seek_head3 + and related calls. The current entry position may be exported in + cursor strings, as accessible via + sd_journal_get_cursor3. + Cursor strings may be used to globally identify a specific journal + entry in a stable way and then later to seek to it (or if the + specific entry is not available locally, to its closest entry in + time) + sd_journal_seek_cursor3. + + Notification of journal changes is available via + sd_journal_get_fd() and related calls. + + + + Return Value + + The sd_journal_open(), + sd_journal_open_directory(), and + sd_journal_open_files() calls return 0 on + success or a negative errno-style error code. + sd_journal_close() returns nothing. + + + + Notes + + The sd_journal_open(), + sd_journal_open_directory() and + sd_journal_close() interfaces are available + as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_next3, + sd_journal_get_data3, + systemd-machined8 + + + + diff --git a/src/libsystemd/sd_journal_print.xml b/src/libsystemd/sd_journal_print.xml new file mode 100644 index 0000000000..76542527fc --- /dev/null +++ b/src/libsystemd/sd_journal_print.xml @@ -0,0 +1,245 @@ + + + + + + + + + sd_journal_print + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_print + 3 + + + + sd_journal_print + sd_journal_printv + sd_journal_send + sd_journal_sendv + sd_journal_perror + SD_JOURNAL_SUPPRESS_LOCATION + Submit log entries to the journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_print + int priority + const char *format + ... + + + + int sd_journal_printv + int priority + const char *format + va_list ap + + + + int sd_journal_send + const char *format + ... + + + + int sd_journal_sendv + const struct iovec *iov + int n + + + + int sd_journal_perror + const char *message + + + + + + + Description + + sd_journal_print() may be used to submit simple, plain text log entries to the system + journal. The first argument is a priority value. This is followed by a format string and its parameters, similar to + printf3 or + syslog3. + The priority value is one of LOG_EMERG, LOG_ALERT, + LOG_CRIT, LOG_ERR, LOG_WARNING, + LOG_NOTICE, LOG_INFO, LOG_DEBUG, as defined in + syslog.h, see syslog3 for details. It is + recommended to use this call to submit log messages in the application locale or system locale and in UTF-8 format, + but no such restrictions are enforced. Note that log messages written using this function are generally not + expected to end in a new-line character. However, as all trailing whitespace (including spaces, new-lines, + tabulators and carriage returns) are automatically stripped from the logged string, it is acceptable to specify one + (or more). Empty lines (after trailing whitespace removal) are suppressed. On non-empty lines, leading whitespace + (as well as inner whitespace) is left unmodified. + + sd_journal_printv() is similar to + sd_journal_print() but takes a variable + argument list encapsulated in an object of type + va_list (see + stdarg3 + for more information) instead of the format string. It is + otherwise equivalent in behavior. + + sd_journal_send() may be used to submit structured log entries to the system journal. It + takes a series of format strings, each immediately followed by their associated parameters, terminated by + NULL. The strings passed should be of the format VARIABLE=value. The + variable name must be in uppercase and consist only of characters, numbers and underscores, and may not begin with + an underscore. (All assignments that do not follow this syntax will be ignored.) The value can be of any size and + format. It is highly recommended to submit text strings formatted in the UTF-8 character encoding only, and submit + binary fields only when formatting in UTF-8 strings is not sensible. A number of well-known fields are defined, see + systemd.journal-fields7 for + details, but additional application defined fields may be used. A variable may be assigned more than one value per + entry. If this function is used, trailing whitespace is automatically removed from each formatted field. + + sd_journal_sendv() is similar to sd_journal_send() but takes an + array of struct iovec (as defined in uio.h, see readv3 for details) + instead of the format string. Each structure should reference one field of the entry to submit. The second argument + specifies the number of structures in the array. sd_journal_sendv() is particularly useful to + submit binary objects to the journal where that is necessary. Note that this function wil not strip trailing + whitespace of the passed fields, but passes the specified data along unmodified. This is different from both + sd_journal_print() and sd_journal_send() described above, which are based + on format strings, and do strip trailing whitespace. + + sd_journal_perror() is a similar to + perror3 + and writes a message to the journal that consists of the passed + string, suffixed with ": " and a human-readable representation of + the current error code stored in + errno3. + If the message string is passed as NULL or + empty string, only the error string representation will be + written, prefixed with nothing. An additional journal field ERRNO= + is included in the entry containing the numeric error code + formatted as decimal string. The log priority used is + LOG_ERR (3). + + Note that sd_journal_send() is a + wrapper around sd_journal_sendv() to make it + easier to use when only text strings shall be submitted. Also, the + following two calls are mostly equivalent: + + sd_journal_print(LOG_INFO, "Hello World, this is PID %lu!", (unsigned long) getpid()); + +sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(), + "PRIORITY=%i", LOG_INFO, + NULL); + + Note that these calls implicitly add fields for the source + file, function name and code line where invoked. This is + implemented with macros. If this is not desired, it can be turned + off by defining SD_JOURNAL_SUPPRESS_LOCATION before including + sd-journal.h. + + syslog3 + and sd_journal_print() may + largely be used interchangeably + functionality-wise. However, note that log messages + logged via the former take a different path to the + journal server than the later, and hence global + chronological ordering between the two streams cannot + be guaranteed. Using + sd_journal_print() has the + benefit of logging source code line, filenames, and + functions as metadata along all entries, and + guaranteeing chronological ordering with structured + log entries that are generated via + sd_journal_send(). Using + syslog() has the benefit of being + more portable. + + + + Return Value + + The four calls return 0 on success or a negative errno-style + error code. The + errno3 + variable itself is not altered. + + If + systemd-journald8 + is not running (the socket is not present), those functions do + nothing, and also return 0. + + + + Async signal safety + sd_journal_sendv() is "async signal + safe" in the meaning of + signal7. + + + sd_journal_print, + sd_journal_printv, + sd_journal_send, and + sd_journal_perror are + not async signal safe. + + + + Notes + + The sd_journal_print(), + sd_journal_printv(), + sd_journal_send() and + sd_journal_sendv() interfaces are available + as a shared library, which can be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_stream_fd3, + syslog3, + perror3, + errno3, + systemd.journal-fields7, + signal7, + socket7 + + + + diff --git a/src/libsystemd/sd_journal_query_unique.xml b/src/libsystemd/sd_journal_query_unique.xml new file mode 100644 index 0000000000..dbff55c105 --- /dev/null +++ b/src/libsystemd/sd_journal_query_unique.xml @@ -0,0 +1,212 @@ + + + + + + + + + sd_journal_query_unique + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_query_unique + 3 + + + + sd_journal_query_unique + sd_journal_enumerate_unique + sd_journal_restart_unique + SD_JOURNAL_FOREACH_UNIQUE + Read unique data fields from the journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_query_unique + sd_journal *j + const char *field + + + + int sd_journal_enumerate_unique + sd_journal *j + const void **data + size_t *length + + + + void sd_journal_restart_unique + sd_journal *j + + + + SD_JOURNAL_FOREACH_UNIQUE + sd_journal *j + const void *data + size_t length + + + + + + + Description + + sd_journal_query_unique() queries the + journal for all unique values the specified field can take. It + takes two arguments: the journal to query and the field name to + look for. Well-known field names are listed on + systemd.journal-fields7. + Field names must be specified without a trailing '='. After this + function has been executed successfully the field values may be + queried using sd_journal_enumerate_unique(). + Invoking this call a second time will change the field name being + queried and reset the enumeration index to the first field value + that matches. + + sd_journal_enumerate_unique() may be + used to iterate through all data fields which match the previously + selected field name as set with + sd_journal_query_unique(). On each invocation + the next field data matching the field name is returned. The order + of the returned data fields is not defined. It takes three + arguments: the journal context object, plus a pair of pointers to + pointer/size variables where the data object and its size shall be + stored in. The returned data is in a read-only memory map and is + only valid until the next invocation of + sd_journal_enumerate_unique(). Note that the + data returned will be prefixed with the field name and '='. Note + that this call is subject to the data field size threshold as + controlled by + sd_journal_set_data_threshold(). + + sd_journal_restart_unique() resets the + data enumeration index to the beginning of the list. The next + invocation of sd_journal_enumerate_unique() + will return the first field data matching the field name + again. + + Note that the + SD_JOURNAL_FOREACH_UNIQUE() macro may be used + as a handy wrapper around + sd_journal_restart_unique() and + sd_journal_enumerate_unique(). + + Note that these functions currently are not influenced by + matches set with sd_journal_add_match() but + this might change in a later version of this software. + + To enumerate all field names currently in use (and thus all suitable field parameters for + sd_journal_query_unique()), use the + sd_journal_enumerate_fields3 + call. + + + + Return Value + + sd_journal_query_unique() returns 0 on + success or a negative errno-style error code. + sd_journal_enumerate_unique() returns a + positive integer if the next field data has been read, 0 when no + more fields are known, or a negative errno-style error code. + sd_journal_restart_unique() returns + nothing. + + + + Notes + + The sd_journal_query_unique(), + sd_journal_enumerate_unique() and + sd_journal_restart_unique() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + Examples + + Use the SD_JOURNAL_FOREACH_UNIQUE macro + to iterate through all values a field of the journal can take. The + following example lists all unit names referenced in the + journal: + + #include <stdio.h> +#include <string.h> +#include <systemd/sd-journal.h> + +int main(int argc, char *argv[]) { + sd_journal *j; + const void *d; + size_t l; + int r; + + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + fprintf(stderr, "Failed to open journal: %s\n", strerror(-r)); + return 1; + } + r = sd_journal_query_unique(j, "_SYSTEMD_UNIT"); + if (r < 0) { + fprintf(stderr, "Failed to query journal: %s\n", strerror(-r)); + return 1; + } + SD_JOURNAL_FOREACH_UNIQUE(j, d, l) + printf("%.*s\n", (int) l, (const char*) d); + sd_journal_close(j); + return 0; +} + + + + + See Also + + + systemd1, + systemd.journal-fields7, + sd-journal3, + sd_journal_open3, + sd_journal_enumerate_fields3, + sd_journal_get_data3, + sd_journal_add_match3 + + + + diff --git a/src/libsystemd/sd_journal_seek_head.xml b/src/libsystemd/sd_journal_seek_head.xml new file mode 100644 index 0000000000..d74c2d5bbc --- /dev/null +++ b/src/libsystemd/sd_journal_seek_head.xml @@ -0,0 +1,172 @@ + + + + + + + + + sd_journal_seek_head + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_seek_head + 3 + + + + sd_journal_seek_head + sd_journal_seek_tail + sd_journal_seek_monotonic_usec + sd_journal_seek_realtime_usec + sd_journal_seek_cursor + Seek to a position in the + journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_seek_head + sd_journal *j + + + + int sd_journal_seek_tail + sd_journal *j + + + + int sd_journal_seek_monotonic_usec + sd_journal *j + sd_id128_t boot_id + uint64_t usec + + + + int sd_journal_seek_realtime_usec + sd_journal *j + uint64_t usec + + + + int sd_journal_seek_cursor + sd_journal *j + const char *cursor + + + + + + Description + + sd_journal_seek_head() seeks to the + beginning of the journal, i.e. the oldest available entry. + + Similarly, sd_journal_seek_tail() may + be used to seek to the end of the journal, i.e. the most recent + available entry. + + sd_journal_seek_monotonic_usec() seeks + to the entry with the specified monotonic timestamp, i.e. + CLOCK_MONOTONIC. Since monotonic time + restarts on every reboot a boot ID needs to be specified as + well. + + sd_journal_seek_realtime_usec() seeks + to the entry with the specified realtime (wallclock) timestamp, + i.e. CLOCK_REALTIME. Note that the realtime + clock is not necessarily monotonic. If a realtime timestamp is + ambiguous, it is not defined which position is sought to. + + sd_journal_seek_cursor() seeks to the + entry located at the specified cursor string. For details on + cursors, see + sd_journal_get_cursor3. + If no entry matching the specified cursor is found the call will + seek to the next closest entry (in terms of time) instead. To + verify whether the newly selected entry actually matches the + cursor, use + sd_journal_test_cursor3. + + Note that these calls do not actually make any entry the new + current entry, this needs to be done in a separate step with a + subsequent + sd_journal_next3 + invocation (or a similar call). Only then, entry data may be + retrieved via + sd_journal_get_data3. + If no entry exists that matches exactly the specified seek + address, the next closest is sought to. If + sd_journal_next3 + is used, the closest following entry will be sought to, if + sd_journal_previous3 + is used the closest preceding entry is sought to. + + + + Return Value + + The functions return 0 on success or a negative errno-style + error code. + + + + Notes + + The sd_journal_seek_head(), + sd_journal_seek_tail(), + sd_journal_seek_monotonic_usec(), + sd_journal_seek_realtime_usec(), + and sd_journal_seek_cursor() + interfaces are available as a shared library, which can + be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-journal3, + sd_journal_open3, + sd_journal_next3, + sd_journal_get_data3, + sd_journal_get_cursor3, + sd_journal_get_realtime_usec3 + + + + diff --git a/src/libsystemd/sd_journal_stream_fd.xml b/src/libsystemd/sd_journal_stream_fd.xml new file mode 100644 index 0000000000..2ea7731b48 --- /dev/null +++ b/src/libsystemd/sd_journal_stream_fd.xml @@ -0,0 +1,163 @@ + + + + + + + + + sd_journal_stream_fd + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_journal_stream_fd + 3 + + + + sd_journal_stream_fd + Create log stream file descriptor to the journal + + + + + #include <systemd/sd-journal.h> + + + int sd_journal_stream_fd + const char *identifier + int priority + int level_prefix + + + + + + + Description + + sd_journal_stream_fd() may be used to + create a log stream file descriptor. Log messages written to this + file descriptor as simple newline-separated text strings are + written to the journal. This file descriptor can be used + internally by applications or be made standard output or standard + error of other processes executed. + + sd_journal_stream_fd() takes a short + program identifier string as first argument, which will be written + to the journal as _SYSLOG_IDENTIFIER= field for each log entry + (see + systemd.journal-fields7 + for more information). The second argument shall be the default + priority level for all messages. The priority level is one of + LOG_EMERG, LOG_ALERT, + LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, + LOG_INFO, LOG_DEBUG, as + defined in syslog.h, see + syslog3 + for details. The third argument is a boolean: if true kernel-style + log level prefixes (such as SD_WARNING) are + interpreted, see + sd-daemon3 + for more information. + + It is recommended that applications log UTF-8 messages only + with this API, but this is not enforced. + + + + Return Value + + The call returns a valid write-only file descriptor on + success or a negative errno-style error code. + + + + Notes + + The sd_journal_stream_fd() interface is + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + Examples + + Creating a log stream suitable for + fprintf3: + + #include <syslog.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <systemd/sd-journal.h> +#include <systemd/sd-daemon.h> + +int main(int argc, char *argv[]) { + int fd; + FILE *log; + fd = sd_journal_stream_fd("test", LOG_INFO, 1); + if (fd < 0) { + fprintf(stderr, "Failed to create stream fd: %s\n", strerror(-fd)); + return 1; + } + log = fdopen(fd, "w"); + if (!log) { + fprintf(stderr, "Failed to create file object: %m\n"); + close(fd); + return 1; + } + fprintf(log, "Hello World!\n"); + fprintf(log, SD_WARNING "This is a warning!\n"); + fclose(log); + return 0; +} + + + + + See Also + + + systemd1, + sd-journal3, + sd-daemon3, + sd_journal_print3, + syslog3, + fprintf3, + systemd.journal-fields7 + + + + diff --git a/src/libsystemd/sd_listen_fds.xml b/src/libsystemd/sd_listen_fds.xml new file mode 100644 index 0000000000..93bf8d853f --- /dev/null +++ b/src/libsystemd/sd_listen_fds.xml @@ -0,0 +1,257 @@ + + + + + + + + + sd_listen_fds + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_listen_fds + 3 + + + + sd_listen_fds + sd_listen_fds_with_names + SD_LISTEN_FDS_START + Check for file descriptors passed by the system manager + + + + + #include <systemd/sd-daemon.h> + + #define SD_LISTEN_FDS_START 3 + + + int sd_listen_fds + int unset_environment + + + + int sd_listen_fds_with_names + int unset_environment + char*** names + + + + + + Description + + sd_listen_fds() may be invoked by a + daemon to check for file descriptors passed by the service manager as + part of the socket-based activation logic. It returns the number + of received file descriptors. If no file descriptors have been + received, zero is returned. The first file descriptor may be found + at file descriptor number 3 + (i.e. SD_LISTEN_FDS_START), the remaining + descriptors follow at 4, 5, 6, ..., if any. + + If a daemon receives more than one file descriptor, they + will be passed in the same order as configured in the systemd + socket unit file (see + systemd.socket5 + for details). Nonetheless, it is recommended to verify the correct + socket types before using them. To simplify this checking, the + functions + sd_is_fifo3, + sd_is_socket3, + sd_is_socket_inet3, + sd_is_socket_unix3 + are provided. In order to maximize flexibility, it is recommended + to make these checks as loose as possible without allowing + incorrect setups. i.e. often, the actual port number a socket is + bound to matters little for the service to work, hence it should + not be verified. On the other hand, whether a socket is a datagram + or stream socket matters a lot for the most common program logics + and should be checked. + + This function call will set the FD_CLOEXEC flag for all + passed file descriptors to avoid further inheritance to children + of the calling process. + + If multiple socket units activate the same service, the order + of the file descriptors passed to its main process is undefined. + If additional file descriptors have been passed to the service + manager using + sd_pid_notify_with_fds3's + FDSTORE=1 messages, these file descriptors are + passed last, in arbitrary order, and with duplicates + removed. + + If the unset_environment parameter is + non-zero, sd_listen_fds() will unset the + $LISTEN_FDS, $LISTEN_PID and + $LISTEN_FDNAMES environment variables before + returning (regardless of whether the function call itself + succeeded or not). Further calls to + sd_listen_fds() will then return zero, but the + variables are no longer inherited by child processes. + + sd_listen_fds_with_names() is like + sd_listen_fds(), but optionally also returns + an array of strings with identification names for the passed file + descriptors, if that is available and the + names parameter is non-NULL. This + information is read from the $LISTEN_FDNAMES + variable, which may contain a colon-separated list of names. For + socket-activated services, these names may be configured with the + FileDescriptorName= setting in socket unit + files, see + systemd.socket5 + for details. For file descriptors pushed into the file descriptor + store (see above), the name is set via the + FDNAME= field transmitted via + sd_pid_notify_with_fds(). The primary usecase + for these names are services which accept a variety of file + descriptors which are not recognizable with functions like + sd_is_socket() alone, and thus require + identification via a name. It is recommended to rely on named file + descriptors only if identification via + sd_is_socket() and related calls is not + sufficient. Note that the names used are not unique in any + way. The returned array of strings has as many entries as file + descriptors have been received, plus a final NULL pointer + terminating the array. The caller needs to free the array itself + and each of its elements with libc's free() + call after use. If the names parameter is + NULL, the call is entirely equivalent to + sd_listen_fds(). + + Under specific conditions, the following automatic file + descriptor names are returned: + + + + <command>Special names</command> + + + + + + Name + Description + + + + + unknown + The process received no name for the specific file descriptor from the service manager. + + + + stored + The file descriptor originates in the service manager's per-service file descriptor store, and the FDNAME= field was absent when the file descriptor was submitted to the service manager. + + + + connection + The service was activated in per-connection style using Accept=yes in the socket unit file, and the file descriptor is the connection socket. + + + +
+
+
+ + + Return Value + + On failure, these calls returns a negative errno-style error + code. If + $LISTEN_FDS/$LISTEN_PID was + not set or was not correctly set for this daemon and hence no file + descriptors were received, 0 is returned. Otherwise, the number of + file descriptors passed is returned. The application may find them + starting with file descriptor SD_LISTEN_FDS_START, i.e. file + descriptor 3. + + + + Notes + + + + Internally, sd_listen_fds() checks + whether the $LISTEN_PID environment variable + equals the daemon PID. If not, it returns immediately. Otherwise, + it parses the number passed in the $LISTEN_FDS + environment variable, then sets the FD_CLOEXEC flag for the parsed + number of file descriptors starting from SD_LISTEN_FDS_START. + Finally, it returns the parsed + number. sd_listen_fds_with_names() does the + same but also parses $LISTEN_FDNAMES if + set. + + + + Environment + + + + $LISTEN_PID + $LISTEN_FDS + $LISTEN_FDNAMES + + Set by the service manager for supervised + processes that use socket-based activation. This environment + variable specifies the data + sd_listen_fds() and + sd_listen_fds_with_names() parses. See + above for details. + + + + + + See Also + + + systemd1, + sd-daemon3, + sd_is_fifo3, + sd_is_socket3, + sd_is_socket_inet3, + sd_is_socket_unix3, + sd_pid_notify_with_fds3, + daemon7, + systemd.service5, + systemd.socket5 + + + +
diff --git a/src/libsystemd/sd_login_monitor_new.xml b/src/libsystemd/sd_login_monitor_new.xml new file mode 100644 index 0000000000..5625ab9207 --- /dev/null +++ b/src/libsystemd/sd_login_monitor_new.xml @@ -0,0 +1,287 @@ + + + + + + + + + sd_login_monitor_new + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_login_monitor_new + 3 + + + + sd_login_monitor_new + sd_login_monitor_unref + sd_login_monitor_unrefp + sd_login_monitor_flush + sd_login_monitor_get_fd + sd_login_monitor_get_events + sd_login_monitor_get_timeout + sd_login_monitor + Monitor login sessions, seats, users and virtual machines/containers + + + + + #include <systemd/sd-login.h> + + + int sd_login_monitor_new + const char *category + sd_login_monitor **ret + + + + sd_login_monitor *sd_login_monitor_unref + sd_login_monitor *m + + + + void sd_login_monitor_unrefp + sd_login_monitor **m + + + + int sd_login_monitor_flush + sd_login_monitor *m + + + + int sd_login_monitor_get_fd + sd_login_monitor *m + + + + int sd_login_monitor_get_events + sd_login_monitor *m + + + + int sd_login_monitor_get_timeout + sd_login_monitor *m + uint64_t *timeout_usec + + + + + + + Description + + sd_login_monitor_new() may be used to + monitor login sessions, users, seats, and virtual + machines/containers. Via a monitor object a file descriptor can be + integrated into an application defined event loop which is woken + up each time a user logs in, logs out or a seat is added or + removed, or a session, user, seat or virtual machine/container + changes state otherwise. The first parameter takes a string which + can be seat (to get only notifications about + seats being added, removed or changed), session + (to get only notifications about sessions being created or removed + or changed), uid (to get only notifications + when a user changes state in respect to logins) or + machine (to get only notifications when a + virtual machine or container is started or stopped). If + notifications shall be generated in all these conditions, + NULL may be passed. Note that in the future + additional categories may be defined. The second parameter returns + a monitor object and needs to be freed with the + sd_login_monitor_unref() call after + use. + + sd_login_monitor_unref() may be used to + destroy a monitor object. Note that this will invalidate any file + descriptor returned by + sd_login_monitor_get_fd(). + + sd_login_monitor_unrefp() is similar to + sd_login_monitor_unref() but takes a pointer + to a pointer to an sd_login_monitor object. This call + is useful in conjunction with GCC's and LLVM's Clean-up + Variable Attribute. Note that this function is defined as + inline function. Use a declaration like the following, in order to + allocate a login monitor object that is freed automatically as the + code block is left: + + { + __attribute__((cleanup(sd_login_monitor_unrefp)) sd_login_monitor *m = NULL; + int r; + … + r = sd_login_monitor_default(&m); + if (r < 0) + fprintf(stderr, "Failed to allocate login monitor object: %s\n", strerror(-r)); + … +} + + sd_login_monitor_flush() may be used to + reset the wakeup state of the monitor object. Whenever an event + causes the monitor to wake up the event loop via the file + descriptor this function needs to be called to reset the wake-up + state. If this call is not invoked, the file descriptor will + immediately wake up the event loop again. + + sd_login_monitor_unref() and + sd_login_monitor_unrefp() execute no + operation if the passed in monitor object is + NULL. + + sd_login_monitor_get_fd() may be used + to retrieve the file descriptor of the monitor object that may be + integrated in an application defined event loop, based around + poll2 + or a similar interface. The application should include the + returned file descriptor as wake-up source for the events mask + returned by sd_login_monitor_get_events(). It + should pass a timeout value as returned by + sd_login_monitor_get_timeout(). Whenever a + wake-up is triggered the file descriptor needs to be reset via + sd_login_monitor_flush(). An application + needs to reread the login state with a function like + sd_get_seats3 + or similar to determine what changed. + + sd_login_monitor_get_events() will + return the poll() mask to wait for. This + function will return a combination of POLLIN, + POLLOUT and similar to fill into the + .events field of struct + pollfd. + + sd_login_monitor_get_timeout() will + return a timeout value for usage in poll(). + This returns a value in microseconds since the epoch of + CLOCK_MONOTONIC for timing out + poll() in timeout_usec. + See + clock_gettime2 + for details about CLOCK_MONOTONIC. If there + is no timeout to wait for this will fill in (uint64_t) + -1 instead. Note that poll() takes + a relative timeout in milliseconds rather than an absolute timeout + in microseconds. To convert the absolute 'µs' timeout into + relative 'ms', use code like the following: + + uint64_t t; +int msec; +sd_login_monitor_get_timeout(m, &t); +if (t == (uint64_t) -1) + msec = -1; +else { + struct timespec ts; + uint64_t n; + clock_getttime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; +} + + The code above does not do any error checking for brevity's + sake. The calculated msec integer can be passed + directly as poll()'s timeout + parameter. + + + + Return Value + + On success, + sd_login_monitor_new(), + sd_login_monitor_flush() and + sd_login_monitor_get_timeout() + return 0 or a positive integer. On success, + sd_login_monitor_get_fd() returns + a Unix file descriptor. On success, + sd_login_monitor_get_events() + returns a combination of POLLIN, + POLLOUT and suchlike. On failure, + these calls return a negative errno-style error + code. + + sd_login_monitor_unref() + always returns NULL. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). The specified category to + watch is not known. + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_login_monitor_new(), + sd_login_monitor_unref(), + sd_login_monitor_flush(), + sd_login_monitor_get_fd(), + sd_login_monitor_get_events() and + sd_login_monitor_get_timeout() + interfaces are available as a shared library, which can be + compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-login3, + sd_get_seats3, + poll2, + clock_gettime2 + + + + diff --git a/src/libsystemd/sd_machine_get_class.xml b/src/libsystemd/sd_machine_get_class.xml new file mode 100644 index 0000000000..ef604139da --- /dev/null +++ b/src/libsystemd/sd_machine_get_class.xml @@ -0,0 +1,152 @@ + + + + + + + + + sd_machine_get_class + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_machine_get_class + 3 + + + + sd_machine_get_class + sd_machine_get_ifindices + Determine the class and network interface indices of a + locally running virtual machine or container. + + + + + #include <systemd/sd-login.h> + + + int sd_machine_get_class + const char* machine + char **class + + + + int sd_machine_get_ifindices + const char* machine + int **ifindices + + + + + + Description + + sd_machine_get_class() may be used to + determine the class of a locally running virtual machine or + container that is registered with + systemd-machined.service8. + The string returned is either vm or + container. The returned string needs to be + freed with the libc free3 + call after use. + + sd_machine_get_ifindices() may be used + to determine the numeric indices of the network interfaces on the + host that are pointing towards the specified locally running + virtual machine or container that is registered with + systemd-machined.service8. + The returned array needs to be freed with the libc free3 + call after use. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ENXIO + + The specified machine does not exist or is currently not running. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_machine_get_class() and + sd_machine_get_ifindices() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-login3, + systemd-machined.service8, + sd_pid_get_machine_name3 + + + + diff --git a/src/libsystemd/sd_notify.xml b/src/libsystemd/sd_notify.xml new file mode 100644 index 0000000000..025fbec6c1 --- /dev/null +++ b/src/libsystemd/sd_notify.xml @@ -0,0 +1,408 @@ + + + + + + + + + sd_notify + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_notify + 3 + + + + sd_notify + sd_notifyf + sd_pid_notify + sd_pid_notifyf + sd_pid_notify_with_fds + Notify service manager about start-up completion and other service status changes + + + + + #include <systemd/sd-daemon.h> + + + int sd_notify + int unset_environment + const char *state + + + + int sd_notifyf + int unset_environment + const char *format + ... + + + + int sd_pid_notify + pid_t pid + int unset_environment + const char *state + + + + int sd_pid_notifyf + pid_t pid + int unset_environment + const char *format + ... + + + + int sd_pid_notify_with_fds + pid_t pid + int unset_environment + const char *state + const int *fds + unsigned n_fds + + + + + + Description + sd_notify() may be called by a service + to notify the service manager about state changes. It can be used + to send arbitrary information, encoded in an + environment-block-like string. Most importantly, it can be used for + start-up completion notification. + + If the unset_environment parameter is + non-zero, sd_notify() will unset the + $NOTIFY_SOCKET environment variable before + returning (regardless of whether the function call itself + succeeded or not). Further calls to + sd_notify() will then fail, but the variable + is no longer inherited by child processes. + + The state parameter should contain a + newline-separated list of variable assignments, similar in style + to an environment block. A trailing newline is implied if none is + specified. The string may contain any kind of variable + assignments, but the following shall be considered + well-known: + + + + READY=1 + + Tells the service manager that service startup + is finished. This is only used by systemd if the service + definition file has Type=notify set. Since there is little + value in signaling non-readiness, the only value services + should send is READY=1 (i.e. + READY=0 is not defined). + + + + RELOADING=1 + + Tells the service manager that the service is + reloading its configuration. This is useful to allow the + service manager to track the service's internal state, and + present it to the user. Note that a service that sends this + notification must also send a READY=1 + notification when it completed reloading its + configuration. + + + + STOPPING=1 + + Tells the service manager that the service is + beginning its shutdown. This is useful to allow the service + manager to track the service's internal state, and present it + to the user. + + + + STATUS=... + + Passes a single-line UTF-8 status string back + to the service manager that describes the service state. This + is free-form and can be used for various purposes: general + state feedback, fsck-like programs could pass completion + percentages and failing programs could pass a human-readable + error message. Example: STATUS=Completed 66% of file + system check... + + + + ERRNO=... + + If a service fails, the errno-style error + code, formatted as string. Example: ERRNO=2 + for ENOENT. + + + + BUSERROR=... + + If a service fails, the D-Bus error-style + error code. Example: + BUSERROR=org.freedesktop.DBus.Error.TimedOut + + + + MAINPID=... + + The main process ID (PID) of the service, in + case the service manager did not fork off the process itself. + Example: MAINPID=4711 + + + + WATCHDOG=1 + + Tells the service manager to update the + watchdog timestamp. This is the keep-alive ping that services + need to issue in regular intervals if + WatchdogSec= is enabled for it. See + systemd.service5 + for information how to enable this functionality and + sd_watchdog_enabled3 + for the details of how the service can check whether the + watchdog is enabled. + + + + + FDSTORE=1 + + Stores additional file descriptors in the + service manager. File descriptors sent this way will be + maintained per-service by the service manager and be passed + again using the usual file descriptor passing logic on the + next invocation of the service (see + sd_listen_fds3). + This is useful for implementing service restart schemes where + services serialize their state to /run, + push their file descriptors to the system manager, and are + then restarted, retrieving their state again via socket + passing and /run. Note that the service + manager will accept messages for a service only if + FileDescriptorStoreMax= is set to non-zero + for it (defaults to zero). See + systemd.service5 + for details. Multiple arrays of file descriptors may be sent + in separate messages, in which case the arrays are combined. + Note that the service manager removes duplicate file + descriptors before passing them to the service. Use + sd_pid_notify_with_fds() to send messages + with FDSTORE=1, see + below. + + + + FDNAME=... + + When used in combination with + FDSTORE=1, specifies a name for the + submitted file descriptors. This name is passed to the service + during activation, and may be queried using + sd_listen_fds_with_names3. File + descriptors submitted without this field set, will implicitly + get the name stored assigned. Note that, if + multiple file descriptors are submitted at once, the specified + name will be assigned to all of them. In order to assign + different names to submitted file descriptors, submit them in + separate invocations of + sd_pid_notify_with_fds(). The name may + consist of any ASCII character, but must not contain control + characters or :. It may not be longer than + 255 characters. If a submitted name does not follow these + restrictions, it is ignored. + + + + WATCHDOG_USEC=... + + Reset watchdog_usec value during runtime. + Notice that this is not available when using sd_event_set_watchdog() + or sd_watchdog_enabled(). + Example : WATCHDOG_USEC=20000000 + + + + + It is recommended to prefix variable names that are not + listed above with X_ to avoid namespace + clashes. + + Note that systemd will accept status data sent from a + service only if the NotifyAccess= option is + correctly set in the service definition file. See + systemd.service5 + for details. + + sd_notifyf() is similar to + sd_notify() but takes a + printf()-like format string plus + arguments. + + sd_pid_notify() and + sd_pid_notifyf() are similar to + sd_notify() and + sd_notifyf() but take a process ID (PID) to + use as originating PID for the message as first argument. This is + useful to send notification messages on behalf of other processes, + provided the appropriate privileges are available. If the PID + argument is specified as 0, the process ID of the calling process + is used, in which case the calls are fully equivalent to + sd_notify() and + sd_notifyf(). + + sd_pid_notify_with_fds() is similar to + sd_pid_notify() but takes an additional array + of file descriptors. These file descriptors are sent along the + notification message to the service manager. This is particularly + useful for sending FDSTORE=1 messages, as + described above. The additional arguments are a pointer to the + file descriptor array plus the number of file descriptors in the + array. If the number of file descriptors is passed as 0, the call + is fully equivalent to sd_pid_notify(), i.e. + no file descriptors are passed. Note that sending file descriptors + to the service manager on messages that do not expect them (i.e. + without FDSTORE=1) they are immediately closed + on reception. + + + + Return Value + + On failure, these calls return a negative errno-style error + code. If $NOTIFY_SOCKET was not set and hence + no status data could be sent, 0 is returned. If the status was + sent, these functions return with a positive return value. In + order to support both, init systems that implement this scheme and + those which do not, it is generally recommended to ignore the + return value of this call. + + + + Notes + + + + These functions send a single datagram with the + state string as payload to the AF_UNIX socket + referenced in the $NOTIFY_SOCKET environment + variable. If the first character of + $NOTIFY_SOCKET is @, the + string is understood as Linux abstract namespace socket. The + datagram is accompanied by the process credentials of the sending + service, using SCM_CREDENTIALS. + + + + Environment + + + + $NOTIFY_SOCKET + + Set by the service manager for supervised + processes for status and start-up completion notification. + This environment variable specifies the socket + sd_notify() talks to. See above for + details. + + + + + + Examples + + + Start-up Notification + + When a service finished starting up, it might issue the + following call to notify the service manager: + + sd_notify(0, "READY=1"); + + + + Extended Start-up Notification + + A service could send the following after completing + initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + + + Error Cause Notification + + A service could send the following shortly before exiting, on failure: + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror(errno), + errno); + + + + Store a File Descriptor in the Service Manager + + To store an open file descriptor in the service manager, + in order to continue operation after a service restart without + losing state, use FDSTORE=1: + + sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &fd, 1); + + + + + See Also + + systemd1, + sd-daemon3, + sd_listen_fds3, + sd_listen_fds_with_names3, + sd_watchdog_enabled3, + daemon7, + systemd.service5 + + + + diff --git a/src/libsystemd/sd_pid_get_session.xml b/src/libsystemd/sd_pid_get_session.xml new file mode 100644 index 0000000000..806cff34e4 --- /dev/null +++ b/src/libsystemd/sd_pid_get_session.xml @@ -0,0 +1,359 @@ + + + + + + + + + sd_pid_get_session + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_pid_get_session + 3 + + + + sd_pid_get_session + sd_pid_get_unit + sd_pid_get_user_unit + sd_pid_get_owner_uid + sd_pid_get_machine_name + sd_pid_get_slice + sd_pid_get_user_slice + sd_pid_get_cgroup + sd_peer_get_session + sd_peer_get_unit + sd_peer_get_user_unit + sd_peer_get_owner_uid + sd_peer_get_machine_name + sd_peer_get_slice + sd_peer_get_user_slice + sd_peer_get_cgroup + Determine session, unit, owner of a session, + container/VM or slice of a specific PID or socket + peer + + + + + #include <systemd/sd-login.h> + + + int sd_pid_get_session + pid_t pid + char **session + + + + int sd_pid_get_unit + pid_t pid + char **unit + + + + int sd_pid_get_user_unit + pid_t pid + char **unit + + + + int sd_pid_get_owner_uid + pid_t pid + uid_t *uid + + + + int sd_pid_get_machine_name + pid_t pid + char **name + + + + int sd_pid_get_slice + pid_t pid + char **slice + + + + int sd_pid_get_user_slice + pid_t pid + char **slice + + + + int sd_pid_get_cgroup + pid_t pid + char **cgroup + + + + int sd_peer_get_session + int fd + char **session + + + + int sd_peer_get_unit + int fd + char **unit + + + + int sd_peer_get_user_unit + int fd + char **unit + + + + int sd_peer_get_owner_uid + int fd + uid_t *uid + + + + int sd_peer_get_machine_name + int fd + char **name + + + + int sd_peer_get_slice + int fd + char **slice + + + + int sd_peer_get_user_slice + int fd + char **slice + + + + int sd_peer_get_cgroup + int fd + char **cgroup + + + + + + Description + + sd_pid_get_session() may be used to + determine the login session identifier of a process identified by + the specified process identifier. The session identifier is a + short string, suitable for usage in file system paths. Note that + not all processes are part of a login session (e.g. system service + processes, user processes that are shared between multiple + sessions of the same user, or kernel threads). For processes not + being part of a login session, this function will fail with + -ENODATA. The returned string needs to be freed with the libc + free3 + call after use. + + sd_pid_get_unit() may be used to + determine the systemd system unit (i.e. system service or scope + unit) identifier of a process identified by the specified PID. The + unit name is a short string, suitable for usage in file system + paths. Note that not all processes are part of a system + unit/service (e.g. user processes, or kernel threads). For + processes not being part of a systemd system unit, this function + will fail with -ENODATA. (More specifically, this call will not + work for kernel threads.) The returned string needs to be freed + with the libc free3 + call after use. + + sd_pid_get_user_unit() may be used to + determine the systemd user unit (i.e. user service or scope unit) + identifier of a process identified by the specified PID. This is + similar to sd_pid_get_unit(), but applies to + user units instead of system units. + + sd_pid_get_owner_uid() may be used to + determine the Unix UID (user identifier) of the owner of the + session of a process identified the specified PID. Note that this + function will succeed for user processes which are shared between + multiple login sessions of the same user, whereas + sd_pid_get_session() will fail. For processes + not being part of a login session and not being a shared process + of a user, this function will fail with -ENODATA. + + sd_pid_get_machine_name() may be used + to determine the name of the VM or container is a member of. The + machine name is a short string, suitable for usage in file system + paths. The returned string needs to be freed with the libc + free3 + call after use. For processes not part of a VM or containers, this + function fails with -ENODATA. + + sd_pid_get_slice() may be used to + determine the slice unit the process is a member of. See + systemd.slice5 + for details about slices. The returned string needs to be freed + with the libc + free3 + call after use. + + Similarly, sd_pid_get_user_slice() + returns the user slice (as managed by the user's systemd instance) + of a process. + + sd_pid_get_cgroup() returns the control + group path of the specified process, relative to the root of the + hierarchy. Returns the path without trailing slash, except for + processes located in the root control group, where "/" is + returned. To find the actual control group path in the file system, + the returned path needs to be prefixed with + /sys/fs/cgroup/ (if the unified control group + setup is used), or + /sys/fs/cgroup/HIERARCHY/ + (if the legacy multi-hierarchy control group setup is used). + + If the pid parameter of any of these + functions is passed as 0, the operation is executed for the + calling process. + + The sd_peer_get_session(), + sd_peer_get_unit(), + sd_peer_get_user_unit(), + sd_peer_get_owner_uid(), + sd_peer_get_machine_name(), + sd_peer_get_slice(), + sd_peer_get_user_slice() and + sd_peer_get_cgroup() calls operate similar to + their PID counterparts, but operate on a connected AF_UNIX socket + and retrieve information about the connected peer process. Note + that these fields are retrieved via /proc, + and hence are not suitable for authorization purposes, as they are + subject to races. + + + + Return Value + + On success, these calls return 0 or a positive integer. On + failure, these calls return a negative errno-style error + code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ESRCH + + The specified PID does not refer to a running + process. + + + + + -BADF + + The specified socket file descriptor was + invalid. + + + + -ENODATA + + The given field is not specified for the described + process or peer. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_pid_get_session(), + sd_pid_get_unit(), + sd_pid_get_user_unit(), + sd_pid_get_owner_uid(), + sd_pid_get_machine_name(), + sd_pid_get_slice(), + sd_pid_get_user_slice(), + sd_peer_get_session(), + sd_peer_get_unit(), + sd_peer_get_user_unit(), + sd_peer_get_owner_uid(), + sd_peer_get_machine_name(), + sd_peer_get_slice() and + sd_peer_get_user_slice() interfaces are + available as a shared library, which can be compiled and linked to + with the libsystemd pkg-config1 + file. + + Note that the login session identifier as + returned by sd_pid_get_session() + is completely unrelated to the process session + identifier as returned by + getsid2. + + + + See Also + + + systemd1, + sd-login3, + sd_session_is_active3, + getsid2, + systemd.slice5, + systemd-machined.service8 + + + + diff --git a/src/libsystemd/sd_seat_get_active.xml b/src/libsystemd/sd_seat_get_active.xml new file mode 100644 index 0000000000..c5e6ddab02 --- /dev/null +++ b/src/libsystemd/sd_seat_get_active.xml @@ -0,0 +1,212 @@ + + + + + + + + + sd_seat_get_active + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_seat_get_active + 3 + + + + sd_seat_get_active + sd_seat_get_sessions + sd_seat_can_multi_session + sd_seat_can_tty + sd_seat_can_graphical + Determine state of a specific seat + + + + + #include <systemd/sd-login.h> + + + int sd_seat_get_active + const char *seat + char **session + uid_t *uid + + + + int sd_seat_get_sessions + const char *seat + char ***sessions + uid_t **uid + unsigned int *n_uids + + + + int sd_seat_can_multi_session + const char *seat + + + + int sd_seat_can_tty + const char *seat + + + + int sd_seat_can_graphical + const char *seat + + + + + + Description + + sd_seat_get_active() may be used to + determine which session is currently active on a seat, if there is + any. Returns the session identifier and the user identifier of the + Unix user the session is belonging to. Either the session or the + user identifier parameter can be passed NULL, + in case only one of the parameters shall be queried. The returned + string needs to be freed with the libc + free3 + call after use. + + sd_seat_get_sessions() may be used to + determine all sessions on the specified seat. Returns two arrays, + one (NULL terminated) with the session + identifiers of the sessions and one with the user identifiers of + the Unix users the sessions belong to. An additional parameter may + be used to return the number of entries in the latter array. The + two arrays and the latter parameter may be passed as + NULL in case these values need not to be + determined. The arrays and the strings referenced by them need to + be freed with the libc + free3 + call after use. Note that instead of an empty array + NULL may be returned and should be considered + equivalent to an empty array. + + sd_seat_can_multi_session() may be used + to determine whether a specific seat is capable of multi-session, + i.e. allows multiple login sessions in parallel (with only one + being active at a time). + + sd_seat_can_tty() may be used to + determine whether a specific seat provides TTY functionality, i.e. + is useful as a text console. + + sd_seat_can_graphical() may be used to + determine whether a specific seat provides graphics functionality, + i.e. is useful as a graphics display. + + If the seat parameter of any of these + functions is passed as NULL, the operation is + executed for the seat of the session of the calling process, if + there is any. + + + + Return Value + + On success, sd_seat_get_active() + returns 0 or a positive integer. On success, + sd_seat_get_sessions() returns the number of + entries in the session identifier array. If the test succeeds, + sd_seat_can_multi_session, + sd_seat_can_tty and + sd_seat_can_graphical return a positive + integer, if it fails 0. On failure, these calls return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ENODATA + + The given field is not specified for the described + seat. + + + + + -ENXIO + + The specified seat is unknown. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_seat_get_active(), + sd_seat_get_sessions(), + sd_seat_can_multi_session(), + sd_seat_can_tty() and + sd_seat_can_graphical() interfaces are + available as a shared library, which can be compiled and linked to + with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-login3, + sd_session_get_seat3 + + + + diff --git a/src/libsystemd/sd_session_is_active.xml b/src/libsystemd/sd_session_is_active.xml new file mode 100644 index 0000000000..a6076b177a --- /dev/null +++ b/src/libsystemd/sd_session_is_active.xml @@ -0,0 +1,359 @@ + + + + + + + + + sd_session_is_active + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_session_is_active + 3 + + + + sd_session_is_active + sd_session_is_remote + sd_session_get_state + sd_session_get_uid + sd_session_get_seat + sd_session_get_service + sd_session_get_type + sd_session_get_class + sd_session_get_desktop + sd_session_get_display + sd_session_get_tty + sd_session_get_vt + sd_session_get_remote_host + sd_session_get_remote_user + Determine state of a specific session + + + + + #include <systemd/sd-login.h> + + + int sd_session_is_active + const char *session + + + + int sd_session_is_remote + const char *session + + + + int sd_session_get_state + const char *session + char **state + + + + int sd_session_get_uid + const char *session + uid_t *uid + + + + int sd_session_get_seat + const char *session + char **seat + + + + int sd_session_get_service + const char *session + char **service + + + + int sd_session_get_type + const char *session + char **type + + + + int sd_session_get_class + const char *session + char **class + + + + int sd_session_get_desktop + const char *session + char **desktop + + + + int sd_session_get_display + const char *session + char **display + + + + int sd_session_get_remote_host + const char *session + char **remote_host + + + + int sd_session_get_remote_user + const char *session + char **remote_user + + + + int sd_session_get_tty + const char *session + char **tty + + + + int sd_session_get_vt + const char *session + unsigned int *vt + + + + + + Description + + sd_session_is_active() may be used to + determine whether the session identified by the specified session + identifier is currently active (i.e. currently in the foreground + and available for user input) or not. + + sd_session_is_remote() may be used to + determine whether the session identified by the specified session + identifier is a remote session (i.e. its remote host is known) or + not. + + sd_session_get_state() may be used to + determine the state of the session identified by the specified + session identifier. The following states are currently known: + online (session logged in, but session not + active, i.e. not in the foreground), active + (session logged in and active, i.e. in the foreground), + closing (session nominally logged out, but some + processes belonging to it are still around). In the future + additional states might be defined, client code should be written + to be robust in regards to additional state strings being + returned. This function is a more generic version of + sd_session_is_active(). The returned string + needs to be freed with the libc + free3 + call after use. + + sd_session_get_uid() may be used to + determine the user identifier of the Unix user the session + identified by the specified session identifier belongs to. + + sd_session_get_seat() may be used to + determine the seat identifier of the seat the session identified + by the specified session identifier belongs to. Note that not all + sessions are attached to a seat, this call will fail for them. The + returned string needs to be freed with the libc + free3 + call after use. + + sd_session_get_service() may be used to + determine the name of the service (as passed during PAM session + setup) that registered the session identified by the specified + session identifier. The returned string needs to be freed with the + libc + free3 + call after use. + + sd_session_get_type() may be used to + determine the type of the session identified by the specified + session identifier. The returned string is one of + x11, wayland, + tty, mir or + unspecified and needs to be freed with the libc + free3 + call after use. + + sd_session_get_class() may be used to + determine the class of the session identified by the specified + session identifier. The returned string is one of + user, greeter, + lock-screen, or background + and needs to be freed with the libc + free3 + call after use. + + sd_session_get_desktop() may be used to + determine the brand of the desktop running on the session + identified by the specified session identifier. This field can be + set freely by desktop environments and does not follow any special + formatting. However, desktops are strongly recommended to use the + same identifiers and capitalization as for + $XDG_CURRENT_DESKTOP, as defined by the Desktop + Entry Specification. The returned string needs to be freed + with the libc + free3 + call after use. + + sd_session_get_display() may be used to + determine the X11 display of the session identified by the + specified session identifier. The returned string needs to be + freed with the libc + free3 + call after use. + + sd_session_get_remote_host() may be + used to determine the remote hostname of the session identified by + the specified session identifier. The returned string needs to be + freed with the libc + free3 + call after use. + + sd_session_get_remote_user() may be + used to determine the remote username of the session identified by + the specified session identifier. The returned string needs to be + freed with the libc + free3 + call after use. Note that this value is rarely known to the + system, and even then should not be relied on. + + sd_session_get_tty() may be used to + determine the TTY device of the session identified by the + specified session identifier. The returned string needs to be + freed with the libc + free3 + call after use. + + sd_session_get_vt() may be used to + determine the VT number of the session identified by the specified + session identifier. This function will return an error if the seat + does not support VTs. + + If the session parameter of any of these + functions is passed as NULL, the operation is + executed for the session the calling process is a member of, if + there is any. + + + + Return Value + + If the test succeeds, + sd_session_is_active() and + sd_session_is_remote() return a + positive integer; if it fails, 0. On success, + sd_session_get_state(), + sd_session_get_uid(), + sd_session_get_seat(), + sd_session_get_service(), + sd_session_get_type(), + sd_session_get_class(), + sd_session_get_display(), + sd_session_get_remote_user(), + sd_session_get_remote_host() and + sd_session_get_tty() return 0 or + a positive integer. On failure, these calls return a + negative errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ENXIO + + The specified session does not exist. + + + + + -ENODATA + + The given field is not specified for the described + session. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + The sd_session_is_active(), + sd_session_get_state(), + sd_session_get_uid(), + sd_session_get_seat(), + sd_session_get_service(), + sd_session_get_type(), + sd_session_get_class(), + sd_session_get_display(), + sd_session_get_remote_host(), + sd_session_get_remote_user() and + sd_session_get_tty() + interfaces are available as a shared library, which can + be compiled and linked to with the + libsystemd pkg-config1 + file. + + + + See Also + + + systemd1, + sd-login3, + sd_pid_get_session3 + + + + diff --git a/src/libsystemd/sd_uid_get_state.xml b/src/libsystemd/sd_uid_get_state.xml new file mode 100644 index 0000000000..130af761da --- /dev/null +++ b/src/libsystemd/sd_uid_get_state.xml @@ -0,0 +1,230 @@ + + + + + + + + + sd_uid_get_state + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_uid_get_state + 3 + + + + sd_uid_get_state + sd_uid_is_on_seat + sd_uid_get_sessions + sd_uid_get_seats + sd_uid_get_display + Determine login state of a specific Unix user ID + + + + + #include <systemd/sd-login.h> + + + int sd_uid_get_state + uid_t uid + char **state + + + + int sd_uid_is_on_seat + uid_t uid + int require_active + const char *seat + + + + int sd_uid_get_sessions + uid_t uid + int require_active + char ***sessions + + + + int sd_uid_get_seats + uid_t uid + int require_active + char ***seats + + + + int sd_uid_get_display + uid_t uid + char **session + + + + + + Description + + sd_uid_get_state() may be used to + determine the login state of a specific Unix user identifier. The + following states are currently known: offline + (user not logged in at all), lingering (user + not logged in, but some user services running), + online (user logged in, but not active, i.e. + has no session in the foreground), active (user + logged in, and has at least one active session, i.e. one session + in the foreground), closing (user not logged + in, and not lingering, but some processes are still around). In + the future additional states might be defined, client code should + be written to be robust in regards to additional state strings + being returned. The returned string needs to be freed with the + libc + free3 + call after use. + + sd_uid_is_on_seat() may be used to + determine whether a specific user is logged in or active on a + specific seat. Accepts a Unix user identifier and a seat + identifier string as parameters. The + require_active parameter is a boolean + value. If non-zero (true), this function will test if the user is + active (i.e. has a session that is in the foreground and accepting + user input) on the specified seat, otherwise (false) only if the + user is logged in (and possibly inactive) on the specified + seat. + + sd_uid_get_sessions() may be used to + determine the current sessions of the specified user. Accepts a + Unix user identifier as parameter. The + require_active parameter controls whether + the returned list shall consist of only those sessions where the + user is currently active (> 0), where the user is currently + online but possibly inactive (= 0), or logged in at all but + possibly closing the session (< 0). The call returns a + NULL terminated string array of session + identifiers in sessions which needs to be + freed by the caller with the libc + free3 + call after use, including all the strings referenced. If the + string array parameter is passed as NULL, the + array will not be filled in, but the return code still indicates + the number of current sessions. Note that instead of an empty + array NULL may be returned and should be + considered equivalent to an empty array. + + Similarly, sd_uid_get_seats() may be + used to determine the list of seats on which the user currently + has sessions. Similar semantics apply, however note that the user + may have multiple sessions on the same seat as well as sessions + with no attached seat and hence the number of entries in the + returned array may differ from the one returned by + sd_uid_get_sessions(). + + sd_uid_get_display() returns the name + of the "primary" session of a user. If the user has graphical + sessions, it will be the oldest graphical session. Otherwise, it + will be the oldest open session. + + + + Return Value + + On success, sd_uid_get_state() returns + 0 or a positive integer. If the test succeeds, + sd_uid_is_on_seat() returns a positive + integer; if it fails, 0. + sd_uid_get_sessions() and + sd_uid_get_seats() return the number of + entries in the returned arrays. + sd_uid_get_display() returns a non-negative + code on success. On failure, these calls return a negative + errno-style error code. + + + + Errors + + Returned errors may indicate the following problems: + + + + + -ENODATA + + The given field is not specified for the described + user. + + + + + -ENXIO + + The specified seat is unknown. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that is not accepted). This is also returned if + the passed user ID is 0xFFFF or 0xFFFFFFFF, which are + undefined on Linux. + + + + -ENOMEM + + Memory allocation failed. + + + + + + Notes + + Functions described here are available as a shared library, + and can be compiled and linked to using the + libsystemd pkg-config1 + entry. + + + + See Also + + + systemd1, + sd-login3, + sd_pid_get_owner_uid3 + + + + diff --git a/src/libsystemd/sd_watchdog_enabled.xml b/src/libsystemd/sd_watchdog_enabled.xml new file mode 100644 index 0000000000..3de9899453 --- /dev/null +++ b/src/libsystemd/sd_watchdog_enabled.xml @@ -0,0 +1,169 @@ + + + + + + + + + sd_watchdog_enabled + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_watchdog_enabled + 3 + + + + sd_watchdog_enabled + Check whether the service manager expects watchdog keep-alive notifications from a service + + + + + #include <systemd/sd-daemon.h> + + + int sd_watchdog_enabled + int unset_environment + uint64_t *usec + + + + + + Description + sd_watchdog_enabled() may be called by + a service to detect whether the service manager expects regular + keep-alive watchdog notification events from it, and the timeout + after which the manager will act on the service if it did not get + such a notification. + + If the $WATCHDOG_USEC environment + variable is set, and the $WATCHDOG_PID variable + is unset or set to the PID of the current process, the service + manager expects notifications from this process. The manager will + usually terminate a service when it does not get a notification + message within the specified time after startup and after each + previous message. It is recommended that a daemon sends a + keep-alive notification message to the service manager every half + of the time returned here. Notification messages may be sent with + sd_notify3 + with a message string of WATCHDOG=1. + + If the unset_environment parameter is + non-zero, sd_watchdog_enabled() will unset + the $WATCHDOG_USEC and + $WATCHDOG_PID environment variables before + returning (regardless of whether the function call itself + succeeded or not). Those variables are no longer inherited by + child processes. Further calls to + sd_watchdog_enabled() will also return with + zero. + + If the usec parameter is non-NULL, + sd_watchdog_enabled() will write the timeout + in µs for the watchdog logic to it. + + To enable service supervision with the watchdog logic, use + WatchdogSec= in service files. See + systemd.service5 + for details. + + Use + sd_event_set_watchdog3 + to enable automatic watchdog support in + sd-event3-based event loops. + + + + Return Value + + On failure, this call returns a negative errno-style error + code. If the service manager expects watchdog keep-alive + notification messages to be sent, > 0 is returned, otherwise 0 + is returned. Only if the return value is > 0, the + usec parameter is valid after the + call. + + + + Notes + + + + Internally, this functions parses the + $WATCHDOG_PID and + $WATCHDOG_USEC environment variable. The call + will ignore these variables if $WATCHDOG_PID + does not contain the PID of the current process, under the + assumption that in that case, the variables were set for a + different process further up the process tree. + + + + Environment + + + + $WATCHDOG_PID + + Set by the system manager for supervised + process for which watchdog support is enabled, and contains + the PID of that process. See above for + details. + + + + $WATCHDOG_USEC + + Set by the system manager for supervised + process for which watchdog support is enabled, and contains + the watchdog timeout in µs See above for + details. + + + + + + See Also + + systemd1, + sd-daemon3, + daemon7, + systemd.service5, + sd_notify3, + sd_event_set_watchdog3 + + + + diff --git a/src/libsystemd/src/Makefile b/src/libsystemd/src/Makefile new file mode 100644 index 0000000000..afcbf8d4e6 --- /dev/null +++ b/src/libsystemd/src/Makefile @@ -0,0 +1,208 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libsystemd_internal_la_SOURCES = \ + src/systemd/sd-bus.h \ + src/systemd/sd-bus-protocol.h \ + src/systemd/sd-bus-vtable.h \ + src/systemd/sd-utf8.h \ + src/systemd/sd-event.h \ + src/systemd/sd-netlink.h \ + src/systemd/sd-resolve.h \ + src/systemd/sd-login.h \ + src/systemd/sd-id128.h \ + src/systemd/sd-daemon.h \ + src/systemd/sd-path.h \ + src/systemd/sd-network.h \ + src/systemd/sd-hwdb.h \ + src/systemd/sd-device.h \ + src/libsystemd/libsystemd.sym \ + src/libsystemd/sd-bus/sd-bus.c \ + src/libsystemd/sd-bus/bus-control.c \ + src/libsystemd/sd-bus/bus-control.h \ + src/libsystemd/sd-bus/bus-error.c \ + src/libsystemd/sd-bus/bus-error.h \ + src/libsystemd/sd-bus/bus-common-errors.h \ + src/libsystemd/sd-bus/bus-common-errors.c \ + src/libsystemd/sd-bus/bus-internal.c \ + src/libsystemd/sd-bus/bus-internal.h \ + src/libsystemd/sd-bus/bus-socket.c \ + src/libsystemd/sd-bus/bus-socket.h \ + src/libsystemd/sd-bus/bus-kernel.c \ + src/libsystemd/sd-bus/bus-kernel.h \ + src/libsystemd/sd-bus/bus-container.c \ + src/libsystemd/sd-bus/bus-container.h \ + src/libsystemd/sd-bus/bus-message.c \ + src/libsystemd/sd-bus/bus-message.h \ + src/libsystemd/sd-bus/bus-creds.c \ + src/libsystemd/sd-bus/bus-creds.h \ + src/libsystemd/sd-bus/bus-signature.c \ + src/libsystemd/sd-bus/bus-signature.h \ + src/libsystemd/sd-bus/bus-type.c \ + src/libsystemd/sd-bus/bus-type.h \ + src/libsystemd/sd-bus/bus-match.c \ + src/libsystemd/sd-bus/bus-match.h \ + src/libsystemd/sd-bus/bus-bloom.c \ + src/libsystemd/sd-bus/bus-bloom.h \ + src/libsystemd/sd-bus/bus-introspect.c \ + src/libsystemd/sd-bus/bus-introspect.h \ + src/libsystemd/sd-bus/bus-objects.c \ + src/libsystemd/sd-bus/bus-objects.h \ + src/libsystemd/sd-bus/bus-gvariant.c \ + src/libsystemd/sd-bus/bus-gvariant.h \ + src/libsystemd/sd-bus/bus-convenience.c \ + src/libsystemd/sd-bus/bus-track.c \ + src/libsystemd/sd-bus/bus-track.h \ + src/libsystemd/sd-bus/bus-slot.c \ + src/libsystemd/sd-bus/bus-slot.h \ + src/libsystemd/sd-bus/bus-protocol.h \ + src/libsystemd/sd-bus/kdbus.h \ + src/libsystemd/sd-bus/bus-dump.c \ + src/libsystemd/sd-bus/bus-dump.h \ + src/libsystemd/sd-utf8/sd-utf8.c \ + src/libsystemd/sd-event/sd-event.c \ + src/libsystemd/sd-netlink/sd-netlink.c \ + src/libsystemd/sd-netlink/netlink-internal.h \ + src/libsystemd/sd-netlink/netlink-message.c \ + src/libsystemd/sd-netlink/netlink-socket.c \ + src/libsystemd/sd-netlink/rtnl-message.c \ + src/libsystemd/sd-netlink/netlink-types.h \ + src/libsystemd/sd-netlink/netlink-types.c \ + src/libsystemd/sd-netlink/netlink-util.h \ + src/libsystemd/sd-netlink/netlink-util.c \ + src/libsystemd/sd-netlink/local-addresses.h \ + src/libsystemd/sd-netlink/local-addresses.c \ + src/libsystemd/sd-id128/sd-id128.c \ + src/libsystemd/sd-id128/id128-util.h \ + src/libsystemd/sd-id128/id128-util.c \ + src/libsystemd/sd-daemon/sd-daemon.c \ + src/libsystemd/sd-login/sd-login.c \ + src/libsystemd/sd-path/sd-path.c \ + src/libsystemd/sd-network/sd-network.c \ + src/libsystemd/sd-network/network-util.h \ + src/libsystemd/sd-network/network-util.c \ + src/libsystemd/sd-hwdb/sd-hwdb.c \ + src/libsystemd/sd-hwdb/hwdb-util.h \ + src/libsystemd/sd-hwdb/hwdb-internal.h \ + src/libsystemd/sd-device/device-internal.h \ + src/libsystemd/sd-device/device-util.h \ + src/libsystemd/sd-device/device-enumerator.c \ + src/libsystemd/sd-device/device-enumerator-private.h \ + src/libsystemd/sd-device/sd-device.c \ + src/libsystemd/sd-device/device-private.c \ + src/libsystemd/sd-device/device-private.h \ + src/libsystemd/sd-resolve/sd-resolve.c + +libsystemd_internal_la_LIBADD = \ + -lresolv + +noinst_LTLIBRARIES += \ + libsystemd-internal.la + +pkginclude_HEADERS += \ + src/systemd/sd-journal.h \ + src/systemd/sd-messages.h \ + src/systemd/_sd-common.h + +libsystemd_journal_internal_la_SOURCES = \ + src/journal/sd-journal.c \ + src/systemd/sd-journal.h \ + src/systemd/_sd-common.h \ + src/journal/journal-file.c \ + src/journal/journal-file.h \ + src/journal/journal-vacuum.c \ + src/journal/journal-vacuum.h \ + src/journal/journal-verify.c \ + src/journal/journal-verify.h \ + src/journal/lookup3.c \ + src/journal/lookup3.h \ + src/journal/journal-send.c \ + src/journal/journal-def.h \ + src/journal/compress.h \ + src/journal/catalog.c \ + src/journal/catalog.h \ + src/journal/mmap-cache.c \ + src/journal/mmap-cache.h \ + src/journal/compress.c \ + src/journal/audit-type.h \ + src/journal/audit-type.c \ + src/shared/gcrypt-util.h \ + src/shared/gcrypt-util.c + +nodist_libsystemd_journal_internal_la_SOURCES = \ + src/journal/audit_type-to-name.h + +gperf_txt_sources += \ + src/journal/audit_type-list.txt + +# using _CFLAGS = in the conditional below would suppress AM_CFLAGS +libsystemd_journal_internal_la_CFLAGS = \ + +libsystemd_journal_internal_la_LIBADD = + +ifneq ($(HAVE_XZ),) +libsystemd_journal_internal_la_CFLAGS += \ + $(XZ_CFLAGS) + +libsystemd_journal_internal_la_LIBADD += \ + $(XZ_LIBS) +endif # HAVE_XZ + +ifneq ($(HAVE_LZ4),) +libsystemd_journal_internal_la_CFLAGS += \ + $(LZ4_CFLAGS) + +libsystemd_journal_internal_la_LIBADD += \ + $(LZ4_LIBS) +endif # HAVE_LZ4 + +ifneq ($(HAVE_GCRYPT),) +libsystemd_journal_internal_la_SOURCES += \ + src/journal/journal-authenticate.c \ + src/journal/journal-authenticate.h \ + src/journal/fsprg.c \ + src/journal/fsprg.h + +libsystemd_journal_internal_la_LIBADD += \ + $(GCRYPT_LIBS) + +endif # HAVE_GCRYPT + +noinst_LTLIBRARIES += \ + libsystemd-journal-internal.la + +nested.subdirs += sd-bus +nested.subdirs += sd-daemon +nested.subdirs += sd-device +nested.subdirs += sd-event +nested.subdirs += sd-hwdb +nested.subdirs += sd-id128 +nested.subdirs += sd-journal +nested.subdirs += sd-login +nested.subdirs += sd-netlink +nested.subdirs += sd-network +nested.subdirs += sd-resolve + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/src/sd-bus/DIFFERENCES b/src/libsystemd/src/sd-bus/DIFFERENCES new file mode 100644 index 0000000000..db269675a7 --- /dev/null +++ b/src/libsystemd/src/sd-bus/DIFFERENCES @@ -0,0 +1,25 @@ +Known differences between dbus1 and kdbus: + +- NameAcquired/NameLost is gone entirely on kdbus backends if + libsystemd is used. It is still added in by systemd-bus-proxyd + for old dbus1 clients, and it is available if libsystemd is used + against the classic dbus1 daemon. If you want to write compatible + code with libsystem-bus you need to explicitly subscribe to + NameOwnerChanged signals and just ignore NameAcquired/NameLost + +- Applications have to deal with spurious signals they didn't expect, + due to the probabilistic bloom filters. They need to handle this + anyway, given that any client can send anything to arbitrary clients + anyway, even in dbus1, so not much changes. + +- clients of the system bus when kdbus is used must roll their own + security. Only legacy dbus1 clients get the old XML policy enforced, + which is implemented by systemd-bus-proxyd. + +- Serial numbers of synthesized messages are always (uint32_t) -1. + +- NameOwnerChanged is a synthetic message, generated locally and not + by the driver. On dbus1 only the Disconnected message was + synthesized like this. + +- There's no standard per-session bus anymore. Only a per-user bus. diff --git a/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION new file mode 100644 index 0000000000..6aeb11364a --- /dev/null +++ b/src/libsystemd/src/sd-bus/GVARIANT-SERIALIZATION @@ -0,0 +1,110 @@ +How we use GVariant for serializing D-Bus messages +-------------------------------------------------- + +We stay close to the original dbus1 framing as possible, but make +certain changes to adapt for GVariant. dbus1 has the following +framing: + + 1. A fixed header of "yyyyuu" + 2. Additional header fields of "a(yv)" + 3. Padding with NUL bytes to pad up to next 8byte boundary + 4. The body + +Note that the body is not padded at the end, the complete message +hence might have a non-aligned size. Reading multiple messages at once +will hence result in possibly unaligned messages in memory. + +The header consists of the following: + + y Endianness, 'l' or 'B' + y Message Type + y Flags + y Protocol version, '1' + u Length of the body, i.e. the length of part 4 above + u 32bit Serial number + + = 12 bytes + +This header is then followed by the fields array, whose first value is +a 32bit array size. + +When using GVariant we keep the basic structure in place, only +slightly alter the header, and define protocol version '2'. The new +header: + + y Endianness, 'l' or 'B' + y Message Type + y Flags + y Protocol version, '2' + u Reserved, must be 0 + t 64bit Cookie + + = 16 bytes + +This is then followed by the GVariant fields array ("a{tv}"), and +finally the actual body as variant (v). Putting this altogether a +packet on dbus2 hence qualifies as a fully compliant GVariant +structure of (yyyyuta{tv}v). + +For details on gvariant, see: + +https://people.gnome.org/~desrt/gvariant-serialisation.pdf + +Regarding the framing of dbus2, also see: + +https://wiki.gnome.org/Projects/GLib/GDBus/Version2 + +The first four bytes of the header are defined the same way for dbus1 +and dbus2. The first bytes contain the endianess field and the +protocol version, so that the remainder of the message can be safely +made sense of just by looking at the first 32bit. + +Note that the length of the body is no longer included in the header +on dbus2! In fact, the message size must be known in advance, from the +underlying transport in order to parse dbus2 messages, while it is +directly included in dbus1 message headers. This change of semantics +is an effect of GVariant's basic design. + +The serial number has been renamed cookie and has been extended from +32bit to 64bit. It is recommended to avoid the higher 32bit of the +cookie field though, to simplify compatibility with dbus1 peers. Note +that not only the cookie/serial field in the fixed header, but also +the reply_cookie/reply_serial additional header field has been +increased from 32bit to 64bit, too! + +The header field identifiers have been extended from 8bit to +64bit. This has been done to simplify things (as kdbus otherwise uses +exclusively 64bit types, unless there is a strong reason not to), and +has no effect on the serialization size, as due to alignment for each +8bit header field identifier 56 bits of padding had to be added. + +Note that the header size changed, due to these changes. However, +consider that on dbus1 the beginning of the fields array contains the +32bit array size (since that is how arrays are encoded on dbus1), +thus, if one considers that size part of the header, instead of the +array, the size of the header on dbus1 and dbus2 stays identical, at +16 bytes. + + 0 4 8 12 16 + Common: | E | T | F | V | ... + + dbus1: | (as above) | Body Length | Serial | Fields Length | Fields array ... + + gvariant: | (as above) | Reserved | Cookie | Fields array ... + +And that's already it. + +Note: to simplify parsing, valid kdbus/dbus2 messages must include the +entire fixed header and additional header fields in a single non-memfd +message part. Also, the signature string of the body variant all the +way to the end of the message must be in a single non-memfd part +too. The parts for this extended header and footer can be the same +one, and can also continue any amount of additional body bytes. + +Note: on kdbus only native endian messages marshalled in gvariant may + be sent. If a client receives a message in non-native endianness + or in dbus1 marshalling it shall ignore the message. + +Note: The GVariant "MAYBE" type is not supported, so that messages can + be fully converted forth and back between dbus1 and gvariant + representations. diff --git a/src/libsystemd/src/sd-bus/Makefile b/src/libsystemd/src/sd-bus/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-bus/PORTING-DBUS1 b/src/libsystemd/src/sd-bus/PORTING-DBUS1 new file mode 100644 index 0000000000..2dedb28bcf --- /dev/null +++ b/src/libsystemd/src/sd-bus/PORTING-DBUS1 @@ -0,0 +1,535 @@ +A few hints on supporting kdbus as backend in your favorite D-Bus library. + +~~~ + +Before you read this, have a look at the DIFFERENCES and +GVARIANT_SERIALIZATION texts you find in the same directory where you +found this. + +We invite you to port your favorite D-Bus protocol implementation +over to kdbus. However, there are a couple of complexities +involved. On kdbus we only speak GVariant marshaling, kdbus clients +ignore traffic in dbus1 marshaling. Thus, you need to add a second, +GVariant compatible marshaler to your library first. + +After you have done that: here's the basic principle how kdbus works: + +You connect to a bus by opening its bus node in /sys/fs/kdbus/. All +buses have a device node there, it starts with a numeric UID of the +owner of the bus, followed by a dash and a string identifying the +bus. The system bus is thus called /sys/fs/kdbus/0-system, and for user +buses the device node is /sys/fs/kdbus/1000-user (if 1000 is your user +id). + +(Before we proceed, please always keep a copy of libsystemd next +to you, ultimately that's where the details are, this document simply +is a rough overview to help you grok things.) + +CONNECTING + +To connect to a bus, simply open() its device node and issue the +KDBUS_CMD_HELLO call. That's it. Now you are connected. Do not send +Hello messages or so (as you would on dbus1), that does not exist for +kdbus. + +The structure you pass to the ioctl will contain a couple of +parameters that you need to know, to operate on the bus. + +There are two flags fields, one indicating features of the kdbus +kernel side ("conn_flags"), the other one ("bus_flags") indicating +features of the bus owner (i.e. systemd). Both flags fields are 64bit +in width. + +When calling into the ioctl, you need to place your own supported +feature bits into these fields. This tells the kernel about the +features you support. When the ioctl returns, it will contain the +features the kernel supports. + +If any of the higher 32bit are set on the two flags fields and your +client does not know what they mean, it must disconnect. The upper +32bit are used to indicate "incompatible" feature additions on the bus +system, the lower 32bit indicate "compatible" feature additions. A +client that does not support a "compatible" feature addition can go on +communicating with the bus, however a client that does not support an +"incompatible" feature must not proceed with the connection. When a +client encountes such an "incompatible" feature it should immediately +try the next bus address configured in the bus address string. + +The hello structure also contains another flags field "attach_flags" +which indicates metadata that is optionally attached to all incoming +messages. You probably want to set KDBUS_ATTACH_NAMES unconditionally +in it. This has the effect that all well-known names of a sender are +attached to all incoming messages. You need this information to +implement matches that match on a message sender name correctly. Of +course, you should only request the attachment of as little metadata +fields as you need. + +The kernel will return in the "id" field your unique id. This is a +simple numeric value. For compatibility with classic dbus1 simply +format this as string and prefix ":1.". + +The kernel will also return the bloom filter size and bloom filter +hash function number used for the signal broadcast bloom filter (see +below). + +The kernel will also return the bus ID of the bus in a 128bit field. + +The pool size field specifies the size of the memory mapped buffer. +After the calling the hello ioctl, you should memory map the kdbus +fd. In this memory mapped region, the kernel will place all your incoming +messages. + +SENDING MESSAGES + +Use the MSG_SEND ioctl to send a message to another peer. The ioctl +takes a structure that contains a variety of fields: + +The flags field corresponds closely to the old dbus1 message header +flags field, though the DONT_EXPECT_REPLY field got inverted into +EXPECT_REPLY. + +The dst_id/src_id field contains the unique id of the destination and +the sender. The sender field is overridden by the kernel usually, hence +you shouldn't fill it in. The destination field can also take the +special value KDBUS_DST_ID_BROADCAST for broadcast messages. For +messages intended to a well-known name set the field to +KDBUS_DST_ID_NAME, and attach the name in a special "items" entry to +the message (see below). + +The payload field indicates the payload. For all dbus traffic it +should carry the value 0x4442757344427573ULL. (Which encodes +'DBusDBus'). + +The cookie field corresponds with the "serial" field of classic +dbus1. We simply renamed it here (and extended it to 64bit) since we +didn't want to imply the monotonicity of the assignment the way the +word "serial" indicates it. + +When sending a message that expects a reply, you need to set the +EXPECT_REPLY flag in the message flag field. In this case you should +also fill out the "timeout_ns" value which indicates the timeout in +nsec for this call. If the peer does not respond in this time you will +get a notification of a timeout. Note that this is also used for +security purposes: a single reply messages is only allowed through the +bus as long as the timeout has not ended. With this timeout value you +hence "open a time window" in which the peer might respond to your +request and the policy allows the response to go through. + +When sending a message that is a reply, you need to fill in the +cookie_reply field, which is similar to the reply_serial field of +dbus1. Note that a message cannot have EXPECT_REPLY and a reply_serial +at the same time! + +This pretty much explains the ioctl header. The actual payload of the +data is now referenced in additional items that are attached to this +ioctl header structure at the end. When sending a message, you attach +items of the type PAYLOAD_VEC, PAYLOAD_MEMFD, FDS, BLOOM_FILTER, +DST_NAME to it: + + KDBUS_ITEM_PAYLOAD_VEC: contains a pointer + length pair for + referencing arbitrary user memory. This is how you reference most + of your data. It's a lot like the good old iovec structure of glibc. + + KDBUS_ITEM_PAYLOAD_MEMFD: for large data blocks it is preferable + to send prepared "memfds" (see below) over. This item contains an + fd for a memfd plus a size. + + KDBUS_ITEM_FDS: for sending over fds attach an item of this type with + an array of fds. + + KDBUS_ITEM_BLOOM_FILTER: the calculated bloom filter of this message, + only for undirected (broadcast) message. + + KDBUS_ITEM_DST_NAME: for messages that are directed to a well-known + name (instead of a unique name), this item contains the well-known + name field. + +A single message may consists of no, one or more payload items of type +PAYLOAD_VEC or PAYLOAD_MEMFD. D-Bus protocol implementations should +treat them as a single block that just happens to be split up into +multiple items. Some restrictions apply however: + + The message header in its entirety must be contained in a single + PAYLOAD_VEC item. + + You may only split your message up right in front of each GVariant + contained in the payload, as well is immediately before framing of a + Gvariant, as well after as any padding bytes if there are any. The + padding bytes must be wholly contained in the preceding + PAYLOAD_VEC/PAYLOAD_MEMFD item. You may not split up basic types + nor arrays of fixed types. The latter is necessary to allow APIs + to return direct pointers to linear arrays of numeric + values. Examples: The basic types "u", "s", "t" have to be in the + same payload item. The array of fixed types "ay", "ai" have to be + fully in contained in the same payload item. For an array "as" or + "a(si)" the only restriction however is to keep each string + individually in an uninterrupted item, to keep the framing of each + element and the array in a single uninterrupted item, however the + various strings might end up in different items. + +Note again, that splitting up messages into separate items is up to the +implementation. Also note that the kdbus kernel side might merge +separate items if it deems this to be useful. However, the order in +which items are contained in the message is left untouched. + +PAYLOAD_MEMFD items allow zero-copy data transfer (see below regarding +the memfd concept). Note however that the overhead of mapping these +makes them relatively expensive, and only worth the trouble for memory +blocks > 512K (this value appears to be quite universal across +architectures, as we tested). Thus we recommend sending PAYLOAD_VEC +items over for small messages and restore to PAYLOAD_MEMFD items for +messages > 512K. Since while building up the message you might not +know yet whether it will grow beyond this boundary a good approach is +to simply build the message unconditionally in a memfd +object. However, when the message is sealed to be sent away check for +the size limit. If the size of the message is < 512K, then simply send +the data as PAYLOAD_VEC and reuse the memfd. If it is >= 512K, seal +the memfd and send it as PAYLOAD_MEMFD, and allocate a new memfd for +the next message. + +RECEIVING MESSAGES + +Use the MSG_RECV ioctl to read a message from kdbus. This will return +an offset into the pool memory map, relative to its beginning. + +The received message structure more or less follows the structure of +the message originally sent. However, certain changes have been +made. In the header the src_id field will be filled in. + +The payload items might have gotten merged and PAYLOAD_VEC items are +not used. Instead, you will only find PAYLOAD_OFF and PAYLOAD_MEMFD +items. The former contain an offset and size into your memory mapped +pool where you find the payload. + +If during the HELLO ioctl you asked for getting metadata attached to +your message, you will find additional KDBUS_ITEM_CREDS, +KDBUS_ITEM_PID_COMM, KDBUS_ITEM_TID_COMM, KDBUS_ITEM_TIMESTAMP, +KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP, +KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT items that +contain this metadata. This metadata will be gathered from the sender +at the point in time it sends the message. This information is +uncached, and since it is appended by the kernel, trustable. The +KDBUS_ITEM_SECLABEL item usually contains the SELinux security label, +if it is used. + +After processing the message you need to call the KDBUS_CMD_FREE +ioctl, which releases the message from the pool, and allows the kernel +to store another message there. Note that the memory used by the pool +is ordinary anonymous, swappable memory that is backed by tmpfs. Hence +there is no need to copy the message out of it quickly, instead you +can just leave it there as long as you need it and release it via the +FREE ioctl only after that's done. + +BLOOM FILTERS + +The kernel does not understand dbus marshaling, it will not look into +the message payload. To allow clients to subscribe to specific subsets +of the broadcast matches we employ bloom filters. + +When broadcasting messages, a bloom filter needs to be attached to the +message in a KDBUS_ITEM_BLOOM item (and only for broadcasting +messages!). If you don't know what bloom filters are, read up now on +Wikipedia. In short: they are a very efficient way how to +probabilistically check whether a certain word is contained in a +vocabulary. It knows no false negatives, but it does know false +positives. + +The parameters for the bloom filters that need to be included in +broadcast message is communicated to userspace as part of the hello +response structure (see above). By default it has the parameters m=512 +(bits in the filter), k=8 (nr of hash functions). Note however, that +this is subject to change in later versions, and userspace +implementations must be capable of handling m values between at least +m=8 and m=2^32, and k values between at least k=1 and k=32. The +underlying hash function is SipHash-2-4. It is used with a number of +constant (yet originally randomly generated) 128bit hash keys, more +specifically: + + b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15, + aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b, + 63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8, + 23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5, + 56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10, + 31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29, + 7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d, + f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35 + +When calculating the first bit index into the bloom filter, the +SipHash-2-4 hash value is calculated for the input data and the first +16 bytes of the array above as hash key. Of the resulting 8 bytes of +output, as many full bytes are taken for the bit index as necessary, +starting from the output's first byte. For the second bit index the +same hash value is used, continuing with the next unused output byte, +and so on. Each time the bytes returned by the hash function are +depleted it is recalculated with the next 16 byte hash key from the +array above and the same input data. + +For each message to send across the bus we populate the bloom filter +with all possible matchable strings. If a client then wants to +subscribe to messages of this type, it simply tells the kernel to test +its own calculated bit mask against the bloom filter of each message. + +More specifically, the following strings are added to the bloom filter +of each message that is broadcasted: + + The string "interface:" suffixed by the interface name + + The string "member:" suffixed by the member name + + The string "path:" suffixed by the path name + + The string "path-slash-prefix:" suffixed with the path name, and + also all prefixes of the path name (cut off at "/"), also prefixed + with "path-slash-prefix". + + The string "message-type:" suffixed with the strings "signal", + "method_call", "error" or "method_return" for the respective message + type of the message. + + If the first argument of the message is a string, "arg0:" suffixed + with the first argument. + + If the first argument of the message is a string, "arg0-dot-prefix" + suffixed with the first argument, and also all prefixes of the + argument (cut off at "."), also prefixed with "arg0-dot-prefix". + + If the first argument of the message is a string, + "arg0-slash-prefix" suffixed with the first argument, and also all + prefixes of the argument (cut off at "/"), also prefixed with + "arg0-slash-prefix". + + Similar for all further arguments that are strings up to 63, for the + arguments and their "dot" and "slash" prefixes. On the first + argument that is not a string, addition to the bloom filter should be + stopped however. + +(Note that the bloom filter does not contain sender nor receiver +names!) + +When a client wants to subscribe to messages matching a certain +expression, it should calculate the bloom mask following the same +algorithm. The kernel will then simply test the mask against the +attached bloom filters. + +Note that bloom filters are probabilistic, which means that clients +might get messages they did not expect. Your bus protocol +implementation must be capable of dealing with these unexpected +messages (which it needs to anyway, given that transfers are +relatively unrestricted on kdbus and people can send you all kinds of +non-sense). + +If a client connects to a bus whose bloom filter metrics (i.e. filter +size and number of hash functions) are outside of the range the client +supports it must immediately disconnect and continue connection with +the next bus address of the bus connection string. + +INSTALLING MATCHES + +To install matches for broadcast messages, use the KDBUS_CMD_ADD_MATCH +ioctl. It takes a structure that contains an encoded match expression, +and that is followed by one or more items, which are combined in an +AND way. (Meaning: a message is matched exactly when all items +attached to the original ioctl struct match). + +To match against other user messages add a KDBUS_ITEM_BLOOM item in +the match (see above). Note that the bloom filter does not include +matches to the sender names. To additionally check against sender +names, use the KDBUS_ITEM_ID (for unique id matches) and +KDBUS_ITEM_NAME (for well-known name matches) item types. + +To match against kernel generated messages (see below) you should add +items of the same type as the kernel messages include, +i.e. KDBUS_ITEM_NAME_ADD, KDBUS_ITEM_NAME_REMOVE, +KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, KDBUS_ITEM_ID_REMOVE and +fill them out. Note however, that you have some wildcards in this +case, for example the .id field of KDBUS_ITEM_ID_ADD/KDBUS_ITEM_ID_REMOVE +structures may be set to 0 to match against any id addition/removal. + +Note that dbus match strings do no map 1:1 to these ioctl() calls. In +many cases (where the match string is "underspecified") you might need +to issue up to six different ioctl() calls for the same match. For +example, the empty match (which matches against all messages), would +translate into one KDBUS_ITEM_BLOOM ioctl, one KDBUS_ITEM_NAME_ADD, +one KDBUS_ITEM_NAME_CHANGE, one KDBUS_ITEM_NAME_REMOVE, one +KDBUS_ITEM_ID_ADD and one KDBUS_ITEM_ID_REMOVE. + +When creating a match, you may attach a "cookie" value to them, which +is used for deleting this match again. The cookie can be selected freely +by the client. When issuing KDBUS_CMD_REMOVE_MATCH, simply pass the +same cookie as before and all matches matching the same "cookie" value +will be removed. This is particularly handy for the case where multiple +ioctl()s are added for a single match strings. + +MEMFDS + +memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items +attached to messages. If this is done, the data included in the memfd +is considered part of the payload stream of a message, and are treated +the same way as KDBUS_ITEM_PAYLOAD_VEC by the receiving side. It is +possible to interleave KDBUS_ITEM_PAYLOAD_MEMFD and +KDBUS_ITEM_PAYLOAD_VEC items freely, by the reader they will be +considered a single stream of bytes in the order these items appear in +the message, that just happens to be split up at various places +(regarding rules how they may be split up, see above). The kernel will +refuse taking KDBUS_ITEM_PAYLOAD_MEMFD items that refer to memfds that +are not sealed. + +Note that sealed memfds may be unsealed again if they are not mapped +you have the only fd reference to them. + +Alternatively to sending memfds as KDBUS_ITEM_PAYLOAD_MEMFD items +(where they are just a part of the payload stream of a message) you can +also simply attach any memfd to a message using +KDBUS_ITEM_PAYLOAD_FDS. In this case, the memfd contents is not +considered part of the payload stream of the message, but simply fds +like any other, that happen to be attached to the message. + +MESSAGES FROM THE KERNEL + +A couple of messages previously generated by the dbus1 bus driver are +now generated by the kernel. Since the kernel does not understand the +payload marshaling, they are generated by the kernel in a different +format. This is indicated with the "payload type" field of the +messages set to 0. Library implementations should take these messages +and synthesize traditional driver messages for them on reception. + +More specifically: + + Instead of the NameOwnerChanged, NameLost, NameAcquired signals + there are kernel messages containing KDBUS_ITEM_NAME_ADD, + KDBUS_ITEM_NAME_REMOVE, KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, + KDBUS_ITEM_ID_REMOVE items are generated (each message will contain + exactly one of these items). Note that in libsystemd we have + obsoleted NameLost/NameAcquired messages, since they are entirely + redundant to NameOwnerChanged. This library will hence only + synthesize NameOwnerChanged messages from these kernel messages, + and never generate NameLost/NameAcquired. If your library needs to + stay compatible to the old dbus1 userspace, you possibly might need + to synthesize both a NameOwnerChanged and NameLost/NameAcquired + message from the same kernel message. + + When a method call times out, a KDBUS_ITEM_REPLY_TIMEOUT message is + generated. This should be synthesized into a method error reply + message to the original call. + + When a method call fails because the peer terminated the connection + before responding, a KDBUS_ITEM_REPLY_DEAD message is + generated. Similarly, it should be synthesized into a method error + reply message. + +For synthesized messages we recommend setting the cookie field to +(uint32_t) -1 (and not (uint64_t) -1!), so that the cookie is not 0 +(which the dbus1 spec does not allow), but clearly recognizable as +synthetic. + +Note that the KDBUS_ITEM_NAME_XYZ messages will actually inform you +about all kinds of names, including activatable ones. Classic dbus1 +NameOwnerChanged messages OTOH are only generated when a name is +really acquired on the bus and not just simply activatable. This means +you must explicitly check for the case where an activatable name +becomes acquired or an acquired name is lost and returns to be +activatable. + +NAME REGISTRY + +To acquire names on the bus, use the KDBUS_CMD_NAME_ACQUIRE ioctl(). It +takes a flags field similar to dbus1's RequestName() bus driver call, +however the NO_QUEUE flag got inverted into a QUEUE flag instead. + +To release a previously acquired name use the KDBUS_CMD_NAME_RELEASE +ioctl(). + +To list acquired names use the KDBUS_CMD_CONN_INFO ioctl. It may be +used to list unique names, well known names as well as activatable +names and clients currently queuing for ownership of a well-known +name. The ioctl will return an offset into the memory pool. After +reading all the data you need, you need to release this via the +KDBUS_CMD_FREE ioctl(), similar how you release a received message. + +CREDENTIALS + +kdbus can optionally attach various kinds of metadata about the sender at +the point of time of sending ("credentials") to messages, on request +of the receiver. This is both supported on directed and undirected +(broadcast) messages. The metadata to attach is selected at time of +the HELLO ioctl of the receiver via a flags field (see above). Note +that clients must be able to handle that messages contain more +metadata than they asked for themselves, to simplify implementation of +broadcasting in the kernel. The receiver should not rely on this data +to be around though, even though it will be correct if it happens to +be attached. In order to avoid programming errors in applications, we +recommend though not passing this data on to clients that did not +explicitly ask for it. + +Credentials may also be queried for a well-known or unique name. Use +the KDBUS_CMD_CONN_INFO for this. It will return an offset to the pool +area again, which will contain the same credential items as messages +have attached. Note that when issuing the ioctl, you can select a +different set of credentials to gather, than what was originally requested +for being attached to incoming messages. + +Credentials are always specific to the sender's domain that was +current at the time of sending, and of the process that opened the +bus connection at the time of opening it. Note that this latter data +is cached! + +POLICY + +The kernel enforces only very limited policy on names. It will not do +access filtering by userspace payload, and thus not by interface or +method name. + +This ultimately means that most fine-grained policy enforcement needs +to be done by the receiving process. We recommend using PolicyKit for +any more complex checks. However, libraries should make simple static +policy decisions regarding privileged/unprivileged method calls +easy. We recommend doing this by enabling KDBUS_ATTACH_CAPS and +KDBUS_ATTACH_CREDS for incoming messages, and then discerning client +access by some capability, or if sender and receiver UIDs match. + +BUS ADDRESSES + +When connecting to kdbus use the "kernel:" protocol prefix in DBus +address strings. The device node path is encoded in its "path=" +parameter. + +Client libraries should use the following connection string when +connecting to the system bus: + + kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket + +This will ensure that kdbus is preferred over the legacy AF_UNIX +socket, but compatibility is kept. For the user bus use: + + kernel:path=/sys/fs/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus + +With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR +following the XDG basedir spec. + +Of course the $DBUS_SYSTEM_BUS_ADDRESS and $DBUS_SESSION_BUS_ADDRESS +variables should still take precedence. + +DBUS SERVICE FILES + +Activatable services for kdbus may not use classic dbus1 service +activation files. Instead, programs should drop in native systemd +.service and .busname unit files, so that they are treated uniformly +with other types of units and activation of the system. + +Note that this results in a major difference to classic dbus1: +activatable bus names can be established at any time in the boot process. +This is unlike dbus1 where activatable names are unconditionally available +as long as dbus-daemon is running. Being able to control when +activatable names are established is essential to allow usage of kdbus +during early boot and in initrds, without the risk of triggering +services too early. + +DISCLAIMER + +This all is so far just the status quo. We are putting this together, because +we are quite confident that further API changes will be smaller, but +to make this very clear: this is all subject to change, still! + +We invite you to port over your favorite dbus library to this new +scheme, but please be prepared to make minor changes when we still +change these interfaces! diff --git a/src/libsystemd/src/sd-bus/bus-bloom.c b/src/libsystemd/src/sd-bus/bus-bloom.c new file mode 100644 index 0000000000..9327fb40c7 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-bloom.c @@ -0,0 +1,157 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/siphash24.h" +#include "basic/util.h" + +#include "bus-bloom.h" + +static inline void set_bit(uint64_t filter[], unsigned long b) { + filter[b >> 6] |= 1ULL << (b & 63); +} + +static const sd_id128_t hash_keys[] = { + SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15), + SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b), + SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8), + SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5), + SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10), + SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29), + SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d), + SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35), +}; + +static void bloom_add_data( + uint64_t filter[], /* The filter bits */ + size_t size, /* Size of the filter in bytes */ + unsigned k, /* Number of hash functions */ + const void *data, /* Data to hash */ + size_t n) { /* Size of data to hash in bytes */ + + uint64_t h; + uint64_t m; + unsigned w, i, c = 0; + unsigned hash_index; + + assert(size > 0); + assert(k > 0); + + /* Determine bits in filter */ + m = size * 8; + + /* Determine how many bytes we need to generate a bit index 0..m for this filter */ + w = (u64log2(m) + 7) / 8; + + assert(w <= sizeof(uint64_t)); + + /* Make sure we have enough hash keys to generate m * k bits + * of hash value. Note that SipHash24 generates 64 bits of + * hash value for each 128 bits of hash key. */ + assert(k * w <= ELEMENTSOF(hash_keys) * 8); + + for (i = 0, hash_index = 0; i < k; i++) { + uint64_t p = 0; + unsigned d; + + for (d = 0; d < w; d++) { + if (c <= 0) { + h = siphash24(data, n, hash_keys[hash_index++].bytes); + c += 8; + } + + p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; + c--; + } + + p &= m - 1; + set_bit(filter, p); + } + + /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */ +} + +void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b) { + size_t n; + char *c; + + assert(filter); + assert(a); + assert(b); + + n = strlen(a) + 1 + strlen(b); + c = alloca(n + 1); + strcpy(stpcpy(stpcpy(c, a), ":"), b); + + bloom_add_data(filter, size, k, c, n); +} + +void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b, char sep) { + size_t n; + char *c, *p; + + assert(filter); + assert(a); + assert(b); + + n = strlen(a) + 1 + strlen(b); + c = alloca(n + 1); + + p = stpcpy(stpcpy(c, a), ":"); + strcpy(p, b); + + bloom_add_data(filter, size, k, c, n); + + for (;;) { + char *e; + + e = strrchr(p, sep); + if (!e) + break; + + *(e + 1) = 0; + bloom_add_data(filter, size, k, c, e - c + 1); + + if (e == p) + break; + + *e = 0; + bloom_add_data(filter, size, k, c, e - c); + } +} + +bool bloom_validate_parameters(size_t size, unsigned k) { + uint64_t m; + unsigned w; + + if (size <= 0) + return false; + + if (k <= 0) + return false; + + m = size * 8; + w = (u64log2(m) + 7) / 8; + if (w > sizeof(uint64_t)) + return false; + + if (k * w > ELEMENTSOF(hash_keys) * 8) + return false; + + return true; +} diff --git a/src/libsystemd/src/sd-bus/bus-bloom.h b/src/libsystemd/src/sd-bus/bus-bloom.h new file mode 100644 index 0000000000..c824622b95 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-bloom.h @@ -0,0 +1,43 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +/* + * Our default bloom filter has the following parameters: + * + * m=512 (bits in the filter) + * k=8 (hash functions) + * + * We use SipHash24 as hash function with a number of (originally + * randomized) but fixed hash keys. + * + */ + +#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */ +#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */ + +void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b); +void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep); + +bool bloom_validate_parameters(size_t size, unsigned n_hash); diff --git a/src/libsystemd/src/sd-bus/bus-common-errors.c b/src/libsystemd/src/sd-bus/bus-common-errors.c new file mode 100644 index 0000000000..a19e98e94b --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-common-errors.c @@ -0,0 +1,87 @@ +/*** + 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 . +***/ + +#include + +#include + +#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), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_JOB, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), + SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), + SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_USER_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SEAT, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_NOT_ON_SEAT, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_IN_CONTROL, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), + SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP), + + SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), + + 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_CNAME_LOOP, EDEADLK), + SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), + 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_NETWORK_DOWN, ENETDOWN), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), + + SD_BUS_ERROR_MAP_END +}; diff --git a/src/libsystemd/src/sd-bus/bus-common-errors.h b/src/libsystemd/src/sd-bus/bus-common-errors.h new file mode 100644 index 0000000000..c8f369cb78 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-common-errors.h @@ -0,0 +1,86 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "bus-error.h" + +#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" +#define BUS_ERROR_NO_UNIT_FOR_PID "org.freedesktop.systemd1.NoUnitForPID" +#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" +#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed" +#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed" +#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob" +#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed" +#define BUS_ERROR_ALREADY_SUBSCRIBED "org.freedesktop.systemd1.AlreadySubscribed" +#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency" +#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting" +#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" +#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" +#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" +#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" +#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" +#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" +#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" +#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" +#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" + +#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" +#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" +#define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID" +#define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists" +#define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking" +#define BUS_ERROR_NO_SUCH_USER_MAPPING "org.freedesktop.machine1.NoSuchUserMapping" +#define BUS_ERROR_NO_SUCH_GROUP_MAPPING "org.freedesktop.machine1.NoSuchGroupMapping" + +#define BUS_ERROR_NO_SUCH_SESSION "org.freedesktop.login1.NoSuchSession" +#define BUS_ERROR_NO_SESSION_FOR_PID "org.freedesktop.login1.NoSessionForPID" +#define BUS_ERROR_NO_SUCH_USER "org.freedesktop.login1.NoSuchUser" +#define BUS_ERROR_NO_USER_FOR_PID "org.freedesktop.login1.NoUserForPID" +#define BUS_ERROR_NO_SUCH_SEAT "org.freedesktop.login1.NoSuchSeat" +#define BUS_ERROR_SESSION_NOT_ON_SEAT "org.freedesktop.login1.SessionNotOnSeat" +#define BUS_ERROR_NOT_IN_CONTROL "org.freedesktop.login1.NotInControl" +#define BUS_ERROR_DEVICE_IS_TAKEN "org.freedesktop.login1.DeviceIsTaken" +#define BUS_ERROR_DEVICE_NOT_TAKEN "org.freedesktop.login1.DeviceNotTaken" +#define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress" +#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported" +#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy" + +#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled" + +#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess" + +#define BUS_ERROR_NO_NAME_SERVERS "org.freedesktop.resolve1.NoNameServers" +#define BUS_ERROR_INVALID_REPLY "org.freedesktop.resolve1.InvalidReply" +#define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR" +#define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" +#define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" +#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_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown" +#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." + +#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" +#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" + +BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/src/sd-bus/bus-container.c b/src/libsystemd/src/sd-bus/bus-container.c new file mode 100644 index 0000000000..43b9d03aa9 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-container.c @@ -0,0 +1,278 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fd-util.h" +#include "basic/process-util.h" +#include "basic/util.h" + +#include "bus-container.h" +#include "bus-internal.h" +#include "bus-socket.h" + +int bus_container_connect_socket(sd_bus *b) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; + pid_t child; + siginfo_t si; + int r, error_buf = 0; + ssize_t n; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->nspid > 0 || b->machine); + + if (b->nspid <= 0) { + r = container_get_leader(b->machine, &b->nspid); + if (r < 0) + return r; + } + + r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + if (r < 0) + return r; + + b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (b->input_fd < 0) + return -errno; + + b->output_fd = b->input_fd; + + bus_socket_setup(b); + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + pid_t grandchild; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + /* We just changed PID namespace, however it will only + * take effect on the children we now fork. Hence, + * let's fork another time, and connect from this + * grandchild, so that SO_PEERCRED of our connection + * comes from a process from within the container, and + * not outside of it */ + + grandchild = fork(); + if (grandchild < 0) + _exit(EXIT_FAILURE); + + if (grandchild == 0) { + + r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); + if (r < 0) { + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(grandchild, &si); + if (r < 0) + _exit(EXIT_FAILURE); + + if (si.si_code != CLD_EXITED) + _exit(EXIT_FAILURE); + + _exit(si.si_status); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + n = read(pair[0], &error_buf, sizeof(error_buf)); + if (n < 0) + return -errno; + + if (n > 0) { + if (n != sizeof(error_buf)) + return -EIO; + + if (error_buf < 0) + return -EIO; + + if (error_buf == EINPROGRESS) + return 1; + + if (error_buf > 0) + return -error_buf; + } + + if (si.si_code != CLD_EXITED) + return -EIO; + + if (si.si_status != EXIT_SUCCESS) + return -EIO; + + return bus_socket_start_auth(b); +} + +int bus_container_connect_kernel(sd_bus *b) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + int error_buf = 0; + struct iovec iov = { + .iov_base = &error_buf, + .iov_len = sizeof(error_buf), + }; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + struct cmsghdr *cmsg; + pid_t child; + siginfo_t si; + int r, fd = -1; + ssize_t n; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->nspid > 0 || b->machine); + + if (b->nspid <= 0) { + r = container_get_leader(b->machine, &b->nspid); + if (r < 0) + return r; + } + + r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + pid_t grandchild; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + /* We just changed PID namespace, however it will only + * take effect on the children we now fork. Hence, + * let's fork another time, and connect from this + * grandchild, so that kdbus only sees the credentials + * of this process which comes from within the + * container, and not outside of it */ + + grandchild = fork(); + if (grandchild < 0) + _exit(EXIT_FAILURE); + + if (grandchild == 0) { + fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); + _exit(EXIT_FAILURE); + } + + r = send_one_fd(pair[1], fd, 0); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + r = wait_for_terminate(grandchild, &si); + if (r < 0) + _exit(EXIT_FAILURE); + + if (si.si_code != CLD_EXITED) + _exit(EXIT_FAILURE); + + _exit(si.si_status); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (n < 0) + return -errno; + + CMSG_FOREACH(cmsg, &mh) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + assert(fd < 0); + + 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; + } + + fd = fds[0]; + } + } + + /* If there's an fd passed, we are good. */ + if (fd >= 0) { + b->input_fd = b->output_fd = fd; + return bus_kernel_take_fd(b); + } + + /* If there's an error passed, use it */ + if (n == sizeof(error_buf) && error_buf > 0) + return -error_buf; + + /* Otherwise, we have no clue */ + return -EIO; +} diff --git a/src/libsystemd/src/sd-bus/bus-container.h b/src/libsystemd/src/sd-bus/bus-container.h new file mode 100644 index 0000000000..5cd6d15ede --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-container.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int bus_container_connect_socket(sd_bus *b); +int bus_container_connect_kernel(sd_bus *b); diff --git a/src/libsystemd/src/sd-bus/bus-control.c b/src/libsystemd/src/sd-bus/bus-control.c new file mode 100644 index 0000000000..689b70bc60 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-control.c @@ -0,0 +1,1589 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "shared/bus-util.h" + +#include "bus-bloom.h" +#include "bus-control.h" +#include "bus-internal.h" +#include "bus-message.h" + +_public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { + int r; + + assert_return(bus, -EINVAL); + assert_return(unique, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->bus_client) + return -EINVAL; + + r = bus_ensure_running(bus); + if (r < 0) + return r; + + *unique = bus->unique_name; + return 0; +} + +static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags) { + struct kdbus_cmd *n; + size_t size, l; + int r; + + assert(bus); + assert(name); + + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); + n = alloca0_align(size, 8); + n->size = size; + n->flags = request_name_flags_to_kdbus(flags); + + n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + n->items[0].type = KDBUS_ITEM_NAME; + memcpy(n->items[0].str, name, l); + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(n, n->size); +#endif + + r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n); + if (r < 0) + return -errno; + + if (n->return_flags & KDBUS_NAME_IN_QUEUE) + return 0; + + return 1; +} + +static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret, param = 0; + int r; + + assert(bus); + assert(name); + + if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) + param |= BUS_NAME_ALLOW_REPLACEMENT; + if (flags & SD_BUS_NAME_REPLACE_EXISTING) + param |= BUS_NAME_REPLACE_EXISTING; + if (!(flags & SD_BUS_NAME_QUEUE)) + param |= BUS_NAME_DO_NOT_QUEUE; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RequestName", + NULL, + &reply, + "su", + name, + param); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "u", &ret); + if (r < 0) + return r; + + if (ret == BUS_NAME_ALREADY_OWNER) + return -EALREADY; + else if (ret == BUS_NAME_EXISTS) + return -EEXIST; + else if (ret == BUS_NAME_IN_QUEUE) + return 0; + else if (ret == BUS_NAME_PRIMARY_OWNER) + return 1; + + return -EIO; +} + +_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); + assert_return(service_name_is_valid(name), -EINVAL); + assert_return(name[0] != ':', -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Don't allow requesting the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_request_name_kernel(bus, name, flags); + else + return bus_request_name_dbus1(bus, name, flags); +} + +static int bus_release_name_kernel(sd_bus *bus, const char *name) { + struct kdbus_cmd *n; + size_t size, l; + int r; + + assert(bus); + assert(name); + + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(l); + n = alloca0_align(size, 8); + n->size = size; + + n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + n->items[0].type = KDBUS_ITEM_NAME; + memcpy(n->items[0].str, name, l); + +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(n, n->size); +#endif + r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n); + if (r < 0) + return -errno; + + return 0; +} + +static int bus_release_name_dbus1(sd_bus *bus, const char *name) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret; + int r; + + assert(bus); + assert(name); + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ReleaseName", + NULL, + &reply, + "s", + name); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "u", &ret); + if (r < 0) + return r; + if (ret == BUS_NAME_NON_EXISTENT) + return -ESRCH; + if (ret == BUS_NAME_NOT_OWNER) + return -EADDRINUSE; + if (ret == BUS_NAME_RELEASED) + return 0; + + return -EINVAL; +} + +_public_ int sd_bus_release_name(sd_bus *bus, const char *name) { + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + assert_return(name[0] != ':', -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Don't allow releasing the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_release_name_kernel(bus, name); + else + return bus_release_name_dbus1(bus, name); +} + +static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { + struct kdbus_cmd_list cmd = { + .size = sizeof(cmd), + .flags = flags, + }; + struct kdbus_info *name_list, *name; + uint64_t previous_id = 0; + int r; + + /* Caller will free half-constructed list on failure... */ + + r = ioctl(bus->input_fd, KDBUS_CMD_LIST, &cmd); + if (r < 0) + return -errno; + + name_list = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); + + KDBUS_FOREACH(name, name_list, cmd.list_size) { + struct kdbus_item *item; + + if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { + char *n; + + if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { + r = -ENOMEM; + goto fail; + } + + r = strv_consume(x, n); + if (r < 0) + goto fail; + + previous_id = name->id; + } + + KDBUS_ITEM_FOREACH(item, name, items) { + if (item->type == KDBUS_ITEM_OWNED_NAME) { + if (service_name_is_valid(item->name.name)) { + r = strv_extend(x, item->name.name); + if (r < 0) { + r = -ENOMEM; + goto fail; + } + } + } + } + } + + r = 0; + +fail: + bus_kernel_cmd_free(bus, cmd.offset); + return r; +} + +static int bus_list_names_kernel(sd_bus *bus, char ***acquired, char ***activatable) { + _cleanup_strv_free_ char **x = NULL, **y = NULL; + int r; + + if (acquired) { + r = kernel_get_list(bus, KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES, &x); + if (r < 0) + return r; + } + + if (activatable) { + r = kernel_get_list(bus, KDBUS_LIST_ACTIVATORS, &y); + if (r < 0) + return r; + + *activatable = y; + y = NULL; + } + + if (acquired) { + *acquired = x; + x = NULL; + } + + return 0; +} + +static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_strv_free_ char **x = NULL, **y = NULL; + int r; + + if (acquired) { + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListNames", + NULL, + &reply, + NULL); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(reply, &x); + if (r < 0) + return r; + + reply = sd_bus_message_unref(reply); + } + + if (activatable) { + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ListActivatableNames", + NULL, + &reply, + NULL); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(reply, &y); + if (r < 0) + return r; + + *activatable = y; + y = NULL; + } + + if (acquired) { + *acquired = x; + x = NULL; + } + + return 0; +} + +_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { + assert_return(bus, -EINVAL); + assert_return(acquired || activatable, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_list_names_kernel(bus, acquired, activatable); + else + return bus_list_names_dbus1(bus, acquired, activatable); +} + +static int bus_populate_creds_from_items( + sd_bus *bus, + struct kdbus_info *info, + uint64_t mask, + sd_bus_creds *c) { + + struct kdbus_item *item; + uint64_t m; + int r; + + assert(bus); + assert(info); + assert(c); + + KDBUS_ITEM_FOREACH(item, info, items) { + + switch (item->type) { + + case KDBUS_ITEM_PIDS: + + if (mask & SD_BUS_CREDS_PID && item->pids.pid > 0) { + c->pid = (pid_t) item->pids.pid; + c->mask |= SD_BUS_CREDS_PID; + } + + if (mask & SD_BUS_CREDS_TID && item->pids.tid > 0) { + c->tid = (pid_t) item->pids.tid; + c->mask |= SD_BUS_CREDS_TID; + } + + if (mask & SD_BUS_CREDS_PPID) { + if (item->pids.ppid > 0) { + c->ppid = (pid_t) item->pids.ppid; + c->mask |= SD_BUS_CREDS_PPID; + } else if (item->pids.pid == 1) { + /* The structure doesn't + * really distinguish the case + * where a process has no + * parent and where we don't + * know it because it could + * not be translated due to + * namespaces. However, we + * know that PID 1 has no + * parent process, hence let's + * patch that in, manually. */ + c->ppid = 0; + c->mask |= SD_BUS_CREDS_PPID; + } + } + + break; + + case KDBUS_ITEM_CREDS: + + if (mask & SD_BUS_CREDS_UID && (uid_t) item->creds.uid != UID_INVALID) { + c->uid = (uid_t) item->creds.uid; + c->mask |= SD_BUS_CREDS_UID; + } + + if (mask & SD_BUS_CREDS_EUID && (uid_t) item->creds.euid != UID_INVALID) { + c->euid = (uid_t) item->creds.euid; + c->mask |= SD_BUS_CREDS_EUID; + } + + if (mask & SD_BUS_CREDS_SUID && (uid_t) item->creds.suid != UID_INVALID) { + c->suid = (uid_t) item->creds.suid; + c->mask |= SD_BUS_CREDS_SUID; + } + + if (mask & SD_BUS_CREDS_FSUID && (uid_t) item->creds.fsuid != UID_INVALID) { + c->fsuid = (uid_t) item->creds.fsuid; + c->mask |= SD_BUS_CREDS_FSUID; + } + + if (mask & SD_BUS_CREDS_GID && (gid_t) item->creds.gid != GID_INVALID) { + c->gid = (gid_t) item->creds.gid; + c->mask |= SD_BUS_CREDS_GID; + } + + if (mask & SD_BUS_CREDS_EGID && (gid_t) item->creds.egid != GID_INVALID) { + c->egid = (gid_t) item->creds.egid; + c->mask |= SD_BUS_CREDS_EGID; + } + + if (mask & SD_BUS_CREDS_SGID && (gid_t) item->creds.sgid != GID_INVALID) { + c->sgid = (gid_t) item->creds.sgid; + c->mask |= SD_BUS_CREDS_SGID; + } + + if (mask & SD_BUS_CREDS_FSGID && (gid_t) item->creds.fsgid != GID_INVALID) { + c->fsgid = (gid_t) item->creds.fsgid; + c->mask |= SD_BUS_CREDS_FSGID; + } + + break; + + case KDBUS_ITEM_PID_COMM: + if (mask & SD_BUS_CREDS_COMM) { + r = free_and_strdup(&c->comm, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_COMM; + } + break; + + case KDBUS_ITEM_TID_COMM: + if (mask & SD_BUS_CREDS_TID_COMM) { + r = free_and_strdup(&c->tid_comm, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_TID_COMM; + } + break; + + case KDBUS_ITEM_EXE: + if (mask & SD_BUS_CREDS_EXE) { + r = free_and_strdup(&c->exe, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_EXE; + } + break; + + case KDBUS_ITEM_CMDLINE: + if (mask & SD_BUS_CREDS_CMDLINE) { + c->cmdline_size = item->size - offsetof(struct kdbus_item, data); + c->cmdline = memdup(item->data, c->cmdline_size); + if (!c->cmdline) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_CMDLINE; + } + break; + + case KDBUS_ITEM_CGROUP: + m = (SD_BUS_CREDS_CGROUP | SD_BUS_CREDS_UNIT | + SD_BUS_CREDS_USER_UNIT | SD_BUS_CREDS_SLICE | + SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask; + + if (m) { + r = free_and_strdup(&c->cgroup, item->str); + if (r < 0) + return r; + + r = bus_get_root_path(bus); + if (r < 0) + return r; + + r = free_and_strdup(&c->cgroup_root, bus->cgroup_root); + if (r < 0) + return r; + + c->mask |= m; + } + break; + + case KDBUS_ITEM_CAPS: + m = (SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS | + SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_BOUNDING_CAPS) & mask; + + if (m) { + if (item->caps.last_cap != cap_last_cap() || + item->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(item->caps.last_cap, 32U) * 4 * 4) + return -EBADMSG; + + c->capability = memdup(item->caps.caps, item->size - offsetof(struct kdbus_item, caps.caps)); + if (!c->capability) + return -ENOMEM; + + c->mask |= m; + } + break; + + case KDBUS_ITEM_SECLABEL: + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + r = free_and_strdup(&c->label, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + break; + + case KDBUS_ITEM_AUDIT: + if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { + c->audit_session_id = (uint32_t) item->audit.sessionid; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } + + if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { + c->audit_login_uid = (uid_t) item->audit.loginuid; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } + break; + + case KDBUS_ITEM_OWNED_NAME: + if ((mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) && service_name_is_valid(item->name.name)) { + r = strv_extend(&c->well_known_names, item->name.name); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + break; + + case KDBUS_ITEM_CONN_DESCRIPTION: + if (mask & SD_BUS_CREDS_DESCRIPTION) { + r = free_and_strdup(&c->description, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_DESCRIPTION; + } + break; + + case KDBUS_ITEM_AUXGROUPS: + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + size_t i, n; + uid_t *g; + + n = (item->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); + if (!g) + return -ENOMEM; + + for (i = 0; i < n; i++) + g[i] = item->data64[i]; + + free(c->supplementary_gids); + c->supplementary_gids = g; + c->n_supplementary_gids = n; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + break; + } + } + + return 0; +} + +int bus_get_name_creds_kdbus( + sd_bus *bus, + const char *name, + uint64_t mask, + bool allow_activator, + sd_bus_creds **creds) { + + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + struct kdbus_cmd_info *cmd; + struct kdbus_info *conn_info; + size_t size, l; + uint64_t id; + int r; + + if (streq(name, "org.freedesktop.DBus")) + return -EOPNOTSUPP; + + r = bus_kernel_parse_unique_name(name, &id); + if (r < 0) + return r; + if (r > 0) { + size = offsetof(struct kdbus_cmd_info, items); + cmd = alloca0_align(size, 8); + cmd->id = id; + } else { + l = strlen(name) + 1; + size = offsetof(struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(l); + cmd = alloca0_align(size, 8); + cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; + cmd->items[0].type = KDBUS_ITEM_NAME; + memcpy(cmd->items[0].str, name, l); + } + + /* If augmentation is on, and the bus didn't provide us + * the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + mask |= SD_BUS_CREDS_PID; + + cmd->size = size; + cmd->attach_flags = attach_flags_to_kdbus(mask); + + r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); + if (r < 0) + return -errno; + + conn_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset); + + /* Non-activated names are considered not available */ + if (!allow_activator && (conn_info->flags & KDBUS_HELLO_ACTIVATOR)) { + if (name[0] == ':') + r = -ENXIO; + else + r = -ESRCH; + goto fail; + } + + c = bus_creds_new(); + if (!c) { + r = -ENOMEM; + goto fail; + } + + if (mask & SD_BUS_CREDS_UNIQUE_NAME) { + if (asprintf(&c->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0) { + r = -ENOMEM; + goto fail; + } + + c->mask |= SD_BUS_CREDS_UNIQUE_NAME; + } + + /* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of + them in case the service has no names. This does not mean + however that the list of owned names could not be + acquired. Hence, let's explicitly clarify that the data is + complete. */ + c->mask |= mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + + r = bus_populate_creds_from_items(bus, conn_info, mask, c); + if (r < 0) + goto fail; + + r = bus_creds_add_more(c, mask, 0, 0); + if (r < 0) + goto fail; + + if (creds) { + *creds = c; + c = NULL; + } + + r = 0; + +fail: + bus_kernel_cmd_free(bus, cmd->offset); + return r; +} + +static int bus_get_name_creds_dbus1( + sd_bus *bus, + const char *name, + uint64_t mask, + sd_bus_creds **creds) { + + _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; + + /* Only query the owner if the caller wants to know it or if + * the caller just wants to check whether a name exists */ + if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) { + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetNameOwner", + NULL, + &reply_unique, + "s", + name); + if (r < 0) + return r; + + r = sd_bus_message_read(reply_unique, "s", &unique); + if (r < 0) + return r; + } + + if (mask != 0) { + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + if ((mask & SD_BUS_CREDS_UNIQUE_NAME) && unique) { + c->unique_name = strdup(unique); + if (!c->unique_name) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_UNIQUE_NAME; + } + + if ((mask & SD_BUS_CREDS_PID) || + ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)))) { + + uint32_t u; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID", + NULL, + &reply, + "s", + unique ? unique : name); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "u", &u); + if (r < 0) + return r; + + pid = u; + if (mask & SD_BUS_CREDS_PID) { + c->pid = u; + c->mask |= SD_BUS_CREDS_PID; + } + + reply = sd_bus_message_unref(reply); + } + + if (mask & SD_BUS_CREDS_EUID) { + uint32_t u; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixUser", + NULL, + &reply, + "s", + unique ? unique : name); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "u", &u); + if (r < 0) + return r; + + c->euid = u; + c->mask |= SD_BUS_CREDS_EUID; + + reply = sd_bus_message_unref(reply); + } + + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const void *p = NULL; + size_t sz = 0; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionSELinuxSecurityContext", + &error, + &reply, + "s", + unique ? unique : name); + if (r < 0) { + if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return r; + } else { + r = sd_bus_message_read_array(reply, 'y', &p, &sz); + if (r < 0) + return r; + + c->label = strndup(p, sz); + if (!c->label) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + } + + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; + } + + if (creds) { + *creds = c; + c = NULL; + } + + return 0; +} + +_public_ int sd_bus_get_name_creds( + sd_bus *bus, + const char *name, + uint64_t mask, + sd_bus_creds **creds) { + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(mask == 0 || creds, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + if (streq(name, "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_get_name_creds_kdbus(bus, name, mask, false, creds); + else + return bus_get_name_creds_dbus1(bus, name, mask, creds); +} + +static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info), + }; + struct kdbus_info *creator_info; + pid_t pid = 0; + int r; + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + /* If augmentation is on, and the bus doesn't didn't allow us + * to get the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_PPID| + SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + mask |= SD_BUS_CREDS_PID; + + cmd.attach_flags = attach_flags_to_kdbus(mask); + + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) + return -errno; + + creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); + + r = bus_populate_creds_from_items(bus, creator_info, mask, c); + bus_kernel_cmd_free(bus, cmd.offset); + if (r < 0) + return r; + + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; + + *ret = c; + c = NULL; + return 0; +} + +static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + pid_t pid = 0; + bool do_label; + int r; + + 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) + return -ENODATA; + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + if (bus->ucred_valid) { + if (bus->ucred.pid > 0) { + pid = c->pid = bus->ucred.pid; + c->mask |= SD_BUS_CREDS_PID & mask; + } + + if (bus->ucred.uid != UID_INVALID) { + c->euid = bus->ucred.uid; + c->mask |= SD_BUS_CREDS_EUID & mask; + } + + if (bus->ucred.gid != GID_INVALID) { + c->egid = bus->ucred.gid; + c->mask |= SD_BUS_CREDS_EGID & mask; + } + } + + if (do_label) { + c->label = strdup(bus->label); + if (!c->label) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; + + *ret = c; + c = NULL; + return 0; +} + +_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + assert_return(bus, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_get_owner_creds_kdbus(bus, mask, ret); + else + return bus_get_owner_creds_dbus1(bus, mask, ret); +} + +static int add_name_change_match(sd_bus *bus, + uint64_t cookie, + const char *name, + const char *old_owner, + const char *new_owner) { + + uint64_t name_id = KDBUS_MATCH_ID_ANY, old_owner_id = 0, new_owner_id = 0; + int is_name_id = -1, r; + struct kdbus_item *item; + + assert(bus); + + /* If we encounter a match that could match against + * NameOwnerChanged messages, then we need to create + * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} and + * KDBUS_ITEM_ID_{ADD,REMOVE} matches for it, possibly + * multiple if the match is underspecified. + * + * The NameOwnerChanged signals take three parameters with + * unique or well-known names, but only some forms actually + * exist: + * + * WELLKNOWN, "", UNIQUE → KDBUS_ITEM_NAME_ADD + * WELLKNOWN, UNIQUE, "" → KDBUS_ITEM_NAME_REMOVE + * WELLKNOWN, UNIQUE, UNIQUE → KDBUS_ITEM_NAME_CHANGE + * UNIQUE, "", UNIQUE → KDBUS_ITEM_ID_ADD + * UNIQUE, UNIQUE, "" → KDBUS_ITEM_ID_REMOVE + * + * For the latter two the two unique names must be identical. + * + * */ + + if (name) { + is_name_id = bus_kernel_parse_unique_name(name, &name_id); + if (is_name_id < 0) + return 0; + } + + if (!isempty(old_owner)) { + r = bus_kernel_parse_unique_name(old_owner, &old_owner_id); + if (r < 0) + return 0; + if (r == 0) + return 0; + if (is_name_id > 0 && old_owner_id != name_id) + return 0; + } else + old_owner_id = KDBUS_MATCH_ID_ANY; + + if (!isempty(new_owner)) { + r = bus_kernel_parse_unique_name(new_owner, &new_owner_id); + if (r < 0) + return r; + if (r == 0) + return 0; + if (is_name_id > 0 && new_owner_id != name_id) + return 0; + } else + new_owner_id = KDBUS_MATCH_ID_ANY; + + if (is_name_id <= 0) { + struct kdbus_cmd_match *m; + size_t sz, l; + + /* If the name argument is missing or is a well-known + * name, then add KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE} + * matches for it */ + + l = name ? strlen(name) + 1 : 0; + + sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) + + offsetof(struct kdbus_item, name_change) + + offsetof(struct kdbus_notify_name_change, name) + + l); + + m = alloca0_align(sz, 8); + m->size = sz; + m->cookie = cookie; + + item = m->items; + item->size = + offsetof(struct kdbus_item, name_change) + + offsetof(struct kdbus_notify_name_change, name) + + l; + + item->name_change.old_id.id = old_owner_id; + item->name_change.new_id.id = new_owner_id; + + memcpy_safe(item->name_change.name, name, l); + + /* If the old name is unset or empty, then + * this can match against added names */ + if (isempty(old_owner)) { + item->type = KDBUS_ITEM_NAME_ADD; + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + } + + /* If the new name is unset or empty, then + * this can match against removed names */ + if (isempty(new_owner)) { + item->type = KDBUS_ITEM_NAME_REMOVE; + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + } + + /* The CHANGE match we need in either case, because + * what is reported as a name change by the kernel + * might just be an owner change between starter and + * normal clients. For userspace such a change should + * be considered a removal/addition, hence let's + * subscribe to this unconditionally. */ + item->type = KDBUS_ITEM_NAME_CHANGE; + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + } + + if (is_name_id != 0) { + struct kdbus_cmd_match *m; + uint64_t sz; + + /* If the name argument is missing or is a unique + * name, then add KDBUS_ITEM_ID_{ADD,REMOVE} matches + * for it */ + + sz = ALIGN8(offsetof(struct kdbus_cmd_match, items) + + offsetof(struct kdbus_item, id_change) + + sizeof(struct kdbus_notify_id_change)); + + m = alloca0_align(sz, 8); + m->size = sz; + m->cookie = cookie; + + item = m->items; + item->size = + offsetof(struct kdbus_item, id_change) + + sizeof(struct kdbus_notify_id_change); + item->id_change.id = name_id; + + /* If the old name is unset or empty, then this can + * match against added ids */ + if (isempty(old_owner)) { + item->type = KDBUS_ITEM_ID_ADD; + if (!isempty(new_owner)) + item->id_change.id = new_owner_id; + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + } + + /* If thew new name is unset or empty, then this can + * match against removed ids */ + if (isempty(new_owner)) { + item->type = KDBUS_ITEM_ID_REMOVE; + if (!isempty(old_owner)) + item->id_change.id = old_owner_id; + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + } + } + + return 0; +} + +int bus_add_match_internal_kernel( + sd_bus *bus, + struct bus_match_component *components, + unsigned n_components, + uint64_t cookie) { + + struct kdbus_cmd_match *m; + struct kdbus_item *item; + uint64_t *bloom; + size_t sz; + const char *sender = NULL; + size_t sender_length = 0; + uint64_t src_id = KDBUS_MATCH_ID_ANY, dst_id = KDBUS_MATCH_ID_ANY; + bool using_bloom = false; + unsigned i; + bool matches_name_change = true; + const char *name_change_arg[3] = {}; + int r; + + assert(bus); + + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + bloom = alloca0(bus->bloom_size); + + sz = ALIGN8(offsetof(struct kdbus_cmd_match, items)); + + for (i = 0; i < n_components; i++) { + struct bus_match_component *c = &components[i]; + + switch (c->type) { + + case BUS_MATCH_SENDER: + if (!streq(c->value_str, "org.freedesktop.DBus")) + matches_name_change = false; + + r = bus_kernel_parse_unique_name(c->value_str, &src_id); + if (r < 0) + return r; + else if (r > 0) + sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); + else { + sender = c->value_str; + sender_length = strlen(sender); + sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1); + } + + break; + + case BUS_MATCH_MESSAGE_TYPE: + if (c->value_u8 != SD_BUS_MESSAGE_SIGNAL) + matches_name_change = false; + + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "message-type", bus_message_type_to_string(c->value_u8)); + using_bloom = true; + break; + + case BUS_MATCH_INTERFACE: + if (!streq(c->value_str, "org.freedesktop.DBus")) + matches_name_change = false; + + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "interface", c->value_str); + using_bloom = true; + break; + + case BUS_MATCH_MEMBER: + if (!streq(c->value_str, "NameOwnerChanged")) + matches_name_change = false; + + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "member", c->value_str); + using_bloom = true; + break; + + case BUS_MATCH_PATH: + if (!streq(c->value_str, "/org/freedesktop/DBus")) + matches_name_change = false; + + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "path", c->value_str); + using_bloom = true; + break; + + case BUS_MATCH_PATH_NAMESPACE: + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, "path-slash-prefix", c->value_str); + using_bloom = true; + break; + + case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: { + char buf[sizeof("arg")-1 + 2 + 1]; + + if (c->type - BUS_MATCH_ARG < 3) + name_change_arg[c->type - BUS_MATCH_ARG] = c->value_str; + + xsprintf(buf, "arg%i", c->type - BUS_MATCH_ARG); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_ARG_HAS...BUS_MATCH_ARG_HAS_LAST: { + char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; + + xsprintf(buf, "arg%i-has", c->type - BUS_MATCH_ARG_HAS); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: + /* + * XXX: DBus spec defines arg[0..63]path= matching to be + * a two-way glob. That is, if either string is a prefix + * of the other, it matches. + * This is really hard to realize in bloom-filters, as + * we would have to create a bloom-match for each prefix + * of @c->value_str. This is excessive, hence we just + * ignore all those matches and accept everything from + * the kernel. People should really avoid those matches. + * If they're used in real-life some day, we will have + * to properly support multiple-matches here. + */ + break; + + case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: { + char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")]; + + xsprintf(buf, "arg%i-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_DESTINATION: + /* + * Kernel only supports matching on destination IDs, but + * not on destination names. So just skip the + * destination name restriction and verify it in + * user-space on retrieval. + */ + r = bus_kernel_parse_unique_name(c->value_str, &dst_id); + if (r < 0) + return r; + else if (r > 0) + sz += ALIGN8(offsetof(struct kdbus_item, id) + sizeof(uint64_t)); + + /* if not a broadcast, it cannot be a name-change */ + if (r <= 0 || dst_id != KDBUS_DST_ID_BROADCAST) + matches_name_change = false; + + break; + + case BUS_MATCH_ROOT: + case BUS_MATCH_VALUE: + case BUS_MATCH_LEAF: + case _BUS_MATCH_NODE_TYPE_MAX: + case _BUS_MATCH_NODE_TYPE_INVALID: + assert_not_reached("Invalid match type?"); + } + } + + if (using_bloom) + sz += ALIGN8(offsetof(struct kdbus_item, data64) + bus->bloom_size); + + m = alloca0_align(sz, 8); + m->size = sz; + m->cookie = cookie; + + item = m->items; + + if (src_id != KDBUS_MATCH_ID_ANY) { + item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); + item->type = KDBUS_ITEM_ID; + item->id = src_id; + item = KDBUS_ITEM_NEXT(item); + } + + if (dst_id != KDBUS_MATCH_ID_ANY) { + item->size = offsetof(struct kdbus_item, id) + sizeof(uint64_t); + item->type = KDBUS_ITEM_DST_ID; + item->id = dst_id; + item = KDBUS_ITEM_NEXT(item); + } + + if (using_bloom) { + item->size = offsetof(struct kdbus_item, data64) + bus->bloom_size; + item->type = KDBUS_ITEM_BLOOM_MASK; + memcpy(item->data64, bloom, bus->bloom_size); + item = KDBUS_ITEM_NEXT(item); + } + + if (sender) { + item->size = offsetof(struct kdbus_item, str) + sender_length + 1; + item->type = KDBUS_ITEM_NAME; + memcpy(item->str, sender, sender_length + 1); + } + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m); + if (r < 0) + return -errno; + + if (matches_name_change) { + + /* If this match could theoretically match + * NameOwnerChanged messages, we need to + * install a second non-bloom filter explitly + * for it */ + + r = add_name_change_match(bus, cookie, name_change_arg[0], name_change_arg[1], name_change_arg[2]); + if (r < 0) + return r; + } + + return 0; +} + +#define internal_match(bus, m) \ + ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ + ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ + : (m)) + +static int bus_add_match_internal_dbus1( + sd_bus *bus, + const char *match) { + + const char *e; + + assert(bus); + assert(match); + + e = internal_match(bus, match); + + return sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "AddMatch", + NULL, + NULL, + "s", + e); +} + +int bus_add_match_internal( + sd_bus *bus, + const char *match, + struct bus_match_component *components, + unsigned n_components, + uint64_t cookie) { + + assert(bus); + + if (!bus->bus_client) + return -EINVAL; + + if (bus->is_kernel) + return bus_add_match_internal_kernel(bus, components, n_components, cookie); + else + return bus_add_match_internal_dbus1(bus, match); +} + +int bus_remove_match_internal_kernel( + sd_bus *bus, + uint64_t cookie) { + + struct kdbus_cmd_match m = { + .size = offsetof(struct kdbus_cmd_match, items), + .cookie = cookie, + }; + int r; + + assert(bus); + + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m); + if (r < 0) + return -errno; + + return 0; +} + +static int bus_remove_match_internal_dbus1( + sd_bus *bus, + const char *match) { + + const char *e; + + assert(bus); + assert(match); + + e = internal_match(bus, match); + + return sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RemoveMatch", + NULL, + NULL, + "s", + e); +} + +int bus_remove_match_internal( + sd_bus *bus, + const char *match, + uint64_t cookie) { + + assert(bus); + + if (!bus->bus_client) + return -EINVAL; + + if (bus->is_kernel) + return bus_remove_match_internal_kernel(bus, cookie); + else + return bus_remove_match_internal_dbus1(bus, match); +} + +_public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; + const char *mid; + int r; + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(machine, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (streq_ptr(name, bus->unique_name)) + return sd_id128_get_machine(machine); + + r = sd_bus_message_new_method_call( + bus, + &m, + name, + "/", + "org.freedesktop.DBus.Peer", + "GetMachineId"); + if (r < 0) + return r; + + r = sd_bus_message_set_auto_start(m, false); + if (r < 0) + return r; + + r = sd_bus_call(bus, m, 0, NULL, &reply); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "s", &mid); + if (r < 0) + return r; + + return sd_id128_from_string(mid, machine); +} diff --git a/src/libsystemd/src/sd-bus/bus-control.h b/src/libsystemd/src/sd-bus/bus-control.h new file mode 100644 index 0000000000..229c95efb0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-control.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#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); +int bus_remove_match_internal(sd_bus *bus, const char *match, uint64_t cookie); + +int bus_add_match_internal_kernel(sd_bus *bus, struct bus_match_component *components, unsigned n_components, uint64_t cookie); +int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t cookie); + +int bus_get_name_creds_kdbus(sd_bus *bus, const char *name, uint64_t mask, bool allow_activator, sd_bus_creds **creds); diff --git a/src/libsystemd/src/sd-bus/bus-convenience.c b/src/libsystemd/src/sd-bus/bus-convenience.c new file mode 100644 index 0000000000..f76cbaf3d3 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-convenience.c @@ -0,0 +1,627 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/string-util.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-signature.h" +#include "bus-type.h" + +_public_ int sd_bus_emit_signal( + sd_bus *bus, + const char *path, + const char *interface, + const char *member, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = sd_bus_message_new_signal(bus, &m, path, interface, member); + if (r < 0) + return r; + + if (!isempty(types)) { + va_list ap; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + return r; + } + + return sd_bus_send(bus, m, NULL); +} + +_public_ int sd_bus_call_method_async( + sd_bus *bus, + sd_bus_slot **slot, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + void *userdata, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); + if (r < 0) + return r; + + if (!isempty(types)) { + va_list ap; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + return r; + } + + return sd_bus_call_async(bus, slot, m, callback, userdata, 0); +} + +_public_ int sd_bus_call_method( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member); + if (r < 0) + goto fail; + + if (!isempty(types)) { + va_list ap; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + goto fail; + } + + return sd_bus_call(bus, m, 0, error, reply); + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_reply_method_return( + sd_bus_message *call, + const char *types, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + r = sd_bus_message_new_method_return(call, &m); + if (r < 0) + return r; + + if (!isempty(types)) { + va_list ap; + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + if (r < 0) + return r; + } + + return sd_bus_send(call->bus, m, NULL); +} + +_public_ int sd_bus_reply_method_error( + sd_bus_message *call, + const sd_bus_error *e) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(sd_bus_error_is_set(e), -EINVAL); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + r = sd_bus_message_new_method_error(call, &m, e); + if (r < 0) + return r; + + return sd_bus_send(call->bus, m, NULL); +} + +_public_ int sd_bus_reply_method_errorf( + sd_bus_message *call, + const char *name, + const char *format, + ...) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + va_list ap; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + va_start(ap, format); + bus_error_setfv(&error, name, format, ap); + va_end(ap); + + return sd_bus_reply_method_error(call, &error); +} + +_public_ int sd_bus_reply_method_errno( + sd_bus_message *call, + int error, + const sd_bus_error *p) { + + _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + if (sd_bus_error_is_set(p)) + return sd_bus_reply_method_error(call, p); + + sd_bus_error_set_errno(&berror, error); + + return sd_bus_reply_method_error(call, &berror); +} + +_public_ int sd_bus_reply_method_errnof( + sd_bus_message *call, + int error, + const char *format, + ...) { + + _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; + va_list ap; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 0; + + va_start(ap, format); + sd_bus_error_set_errnofv(&berror, error, format, ap); + va_end(ap); + + return sd_bus_reply_method_error(call, &berror); +} + +_public_ int sd_bus_get_property( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + sd_bus_message **reply, + const char *type) { + + sd_bus_message *rep = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(reply, -EINVAL, error); + bus_assert_return(signature_is_single(type, false), -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(rep, 'v', type); + if (r < 0) { + sd_bus_message_unref(rep); + goto fail; + } + + *reply = rep; + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_get_property_trivial( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + char type, void *ptr) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(bus_type_is_trivial(type), -EINVAL, error); + bus_assert_return(ptr, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type)); + if (r < 0) + goto fail; + + r = sd_bus_message_read_basic(reply, type, ptr); + if (r < 0) + goto fail; + + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_get_property_string( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + char **ret) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *s; + char *n; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(ret, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'v', "s"); + if (r < 0) + goto fail; + + r = sd_bus_message_read_basic(reply, 's', &s); + if (r < 0) + goto fail; + + n = strdup(s); + if (!n) { + r = -ENOMEM; + goto fail; + } + + *ret = n; + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_get_property_strv( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + char ***ret) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(ret, -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member); + if (r < 0) + return r; + + r = sd_bus_message_enter_container(reply, 'v', NULL); + if (r < 0) + goto fail; + + r = sd_bus_message_read_strv(reply, ret); + if (r < 0) + goto fail; + + return 0; + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_set_property( + sd_bus *bus, + const char *destination, + const char *path, + const char *interface, + const char *member, + sd_bus_error *error, + const char *type, ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + va_list ap; + int r; + + bus_assert_return(bus, -EINVAL, error); + bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error); + bus_assert_return(member_name_is_valid(member), -EINVAL, error); + bus_assert_return(signature_is_single(type, false), -EINVAL, error); + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set"); + if (r < 0) + goto fail; + + r = sd_bus_message_append(m, "ss", strempty(interface), member); + if (r < 0) + goto fail; + + r = sd_bus_message_open_container(m, 'v', type); + if (r < 0) + goto fail; + + va_start(ap, type); + r = bus_message_append_ap(m, type, ap); + va_end(ap); + if (r < 0) + goto fail; + + r = sd_bus_message_close_container(m); + if (r < 0) + goto fail; + + return sd_bus_call(bus, m, 0, error, NULL); + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) { + sd_bus_creds *c; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + c = sd_bus_message_get_creds(call); + + /* All data we need? */ + if (c && (mask & ~c->mask) == 0) { + *creds = sd_bus_creds_ref(c); + return 0; + } + + /* No data passed? Or not enough data passed to retrieve the missing bits? */ + if (!c || !(c->mask & SD_BUS_CREDS_PID)) { + /* We couldn't read anything from the call, let's try + * to get it from the sender or peer. */ + + if (call->sender) + /* There's a sender, but the creds are + * missing. This means we are talking via + * dbus1, or are getting a message that was + * sent to us via kdbus, but was converted + * from a dbus1 message by the bus-proxy and + * thus also lacks the creds. */ + return sd_bus_get_name_creds(call->bus, call->sender, mask, creds); + else + /* There's no sender, hence we are on a dbus1 + * direct connection. For direct connections + * the credentials of the AF_UNIX peer matter, + * which may be queried via + * sd_bus_get_owner_creds(). */ + return sd_bus_get_owner_creds(call->bus, mask, creds); + } + + return bus_creds_extend_by_pid(c, mask, creds); +} + +_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + uid_t our_uid; + bool know_caps = false; + int r; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->bus, -EINVAL); + assert_return(!bus_pid_changed(call->bus), -ECHILD); + + if (!BUS_IS_OPEN(call->bus->state)) + return -ENOTCONN; + + if (capability >= 0) { + + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); + if (r < 0) + return r; + + /* We cannot use augmented caps for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM); + + /* Note that not even on kdbus we might have the caps + * field, due to faked identities, or namespace + * translation issues. */ + r = sd_bus_creds_has_effective_cap(creds, capability); + if (r > 0) + return 1; + if (r == 0) + know_caps = true; + } else { + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + } + + /* Now, check the UID, but only if the capability check wasn't + * sufficient */ + our_uid = getuid(); + if (our_uid != 0 || !know_caps || capability < 0) { + uid_t sender_uid; + + /* We cannot use augmented uid/euid for authorization, + * since then data is acquired raceful from + * /proc. This can never actually happen, but let's + * better be safe than sorry, and do an extra check + * here. */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM); + + /* Try to use the EUID, if we have it. */ + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + r = sd_bus_creds_get_uid(creds, &sender_uid); + + if (r >= 0) { + /* Sender has same UID as us, then let's grant access */ + if (sender_uid == our_uid) + return 1; + + /* Sender is root, we are not root. */ + if (our_uid != 0 && sender_uid == 0) + return 1; + } + } + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-creds.c b/src/libsystemd/src/sd-bus/bus-creds.c new file mode 100644 index 0000000000..bc5a82fac4 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-creds.c @@ -0,0 +1,1351 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/audit-util.h" +#include "basic/bus-label.h" +#include "basic/capability-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/hexdecoct.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-creds.h" +#include "bus-message.h" + +enum { + CAP_OFFSET_INHERITABLE = 0, + CAP_OFFSET_PERMITTED = 1, + CAP_OFFSET_EFFECTIVE = 2, + CAP_OFFSET_BOUNDING = 3 +}; + +void bus_creds_done(sd_bus_creds *c) { + assert(c); + + /* For internal bus cred structures that are allocated by + * something else */ + + free(c->session); + free(c->unit); + free(c->user_unit); + free(c->slice); + free(c->user_slice); + free(c->unescaped_description); + free(c->supplementary_gids); + free(c->tty); + + free(c->well_known_names); /* note that this is an strv, but + * we only free the array, not the + * strings the array points to. The + * full strv we only free if + * c->allocated is set, see + * below. */ + + strv_free(c->cmdline_array); +} + +_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { + + if (!c) + return NULL; + + if (c->allocated) { + assert(c->n_ref > 0); + c->n_ref++; + } else { + sd_bus_message *m; + + /* If this is an embedded creds structure, then + * forward ref counting to the message */ + m = container_of(c, sd_bus_message, creds); + sd_bus_message_ref(m); + } + + return c; +} + +_public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { + + if (!c) + return NULL; + + if (c->allocated) { + assert(c->n_ref > 0); + c->n_ref--; + + if (c->n_ref == 0) { + free(c->comm); + free(c->tid_comm); + free(c->exe); + free(c->cmdline); + free(c->cgroup); + free(c->capability); + free(c->label); + free(c->unique_name); + free(c->cgroup_root); + free(c->description); + + c->supplementary_gids = mfree(c->supplementary_gids); + + c->well_known_names = strv_free(c->well_known_names); + + bus_creds_done(c); + + free(c); + } + } else { + sd_bus_message *m; + + m = container_of(c, sd_bus_message, creds); + sd_bus_message_unref(m); + } + + + return NULL; +} + +_public_ uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c) { + assert_return(c, 0); + + return c->mask; +} + +_public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) { + assert_return(c, 0); + + return c->augmented; +} + +sd_bus_creds* bus_creds_new(void) { + sd_bus_creds *c; + + c = new0(sd_bus_creds, 1); + if (!c) + return NULL; + + c->allocated = true; + c->n_ref = 1; + return c; +} + +_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) { + sd_bus_creds *c; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + if (pid == 0) + pid = getpid(); + + c = bus_creds_new(); + if (!c) + return -ENOMEM; + + r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0); + if (r < 0) { + sd_bus_creds_unref(c); + return r; + } + + /* Check if the process existed at all, in case we haven't + * figured that out already */ + if (!pid_is_alive(pid)) { + sd_bus_creds_unref(c); + return -ESRCH; + } + + *ret = c; + return 0; +} + +_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) { + assert_return(c, -EINVAL); + assert_return(uid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_UID)) + return -ENODATA; + + *uid = c->uid; + return 0; +} + +_public_ int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) { + assert_return(c, -EINVAL); + assert_return(euid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EUID)) + return -ENODATA; + + *euid = c->euid; + return 0; +} + +_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) { + assert_return(c, -EINVAL); + assert_return(suid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUID)) + return -ENODATA; + + *suid = c->suid; + return 0; +} + + +_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) { + assert_return(c, -EINVAL); + assert_return(fsuid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSUID)) + return -ENODATA; + + *fsuid = c->fsuid; + return 0; +} + +_public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) { + assert_return(c, -EINVAL); + assert_return(gid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_GID)) + return -ENODATA; + + *gid = c->gid; + return 0; +} + +_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { + assert_return(c, -EINVAL); + assert_return(egid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EGID)) + return -ENODATA; + + *egid = c->egid; + return 0; +} + +_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) { + assert_return(c, -EINVAL); + assert_return(sgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SGID)) + return -ENODATA; + + *sgid = c->sgid; + return 0; +} + +_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) { + assert_return(c, -EINVAL); + assert_return(fsgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSGID)) + return -ENODATA; + + *fsgid = c->fsgid; + return 0; +} + +_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) { + assert_return(c, -EINVAL); + assert_return(gids, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) + return -ENODATA; + + *gids = c->supplementary_gids; + return (int) c->n_supplementary_gids; +} + +_public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) { + assert_return(c, -EINVAL); + assert_return(pid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PID)) + return -ENODATA; + + assert(c->pid > 0); + *pid = c->pid; + return 0; +} + +_public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) { + assert_return(c, -EINVAL); + assert_return(ppid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PPID)) + return -ENODATA; + + /* PID 1 has no parent process. Let's distinguish the case of + * not knowing and not having a parent process by the returned + * error code. */ + if (c->ppid == 0) + return -ENXIO; + + *ppid = c->ppid; + return 0; +} + +_public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) { + assert_return(c, -EINVAL); + assert_return(tid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_TID)) + return -ENODATA; + + assert(c->tid > 0); + *tid = c->tid; + return 0; +} + +_public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)) + return -ENODATA; + + assert(c->label); + *ret = c->label; + return 0; +} + +_public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_COMM)) + return -ENODATA; + + assert(c->comm); + *ret = c->comm; + return 0; +} + +_public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_TID_COMM)) + return -ENODATA; + + assert(c->tid_comm); + *ret = c->tid_comm; + return 0; +} + +_public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EXE)) + return -ENODATA; + + if (!c->exe) + return -ENXIO; + + *ret = c->exe; + return 0; +} + +_public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_CGROUP)) + return -ENODATA; + + assert(c->cgroup); + *ret = c->cgroup; + return 0; +} + +_public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_UNIT)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->unit) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_unit(shifted, (char**) &c->unit); + if (r < 0) + return r; + } + + *ret = c->unit; + return 0; +} + +_public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_USER_UNIT)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->user_unit) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_user_unit(shifted, (char**) &c->user_unit); + if (r < 0) + return r; + } + + *ret = c->user_unit; + return 0; +} + +_public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SLICE)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->slice) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_slice(shifted, (char**) &c->slice); + if (r < 0) + return r; + } + + *ret = c->slice; + return 0; +} + +_public_ int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_USER_SLICE)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->user_slice) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_user_slice(shifted, (char**) &c->user_slice); + if (r < 0) + return r; + } + + *ret = c->user_slice; + return 0; +} + +_public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) { + int r; + + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SESSION)) + return -ENODATA; + + assert(c->cgroup); + + if (!c->session) { + const char *shifted; + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + r = cg_path_get_session(shifted, (char**) &c->session); + if (r < 0) + return r; + } + + *ret = c->session; + return 0; +} + +_public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) { + const char *shifted; + int r; + + assert_return(c, -EINVAL); + assert_return(uid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_OWNER_UID)) + return -ENODATA; + + assert(c->cgroup); + + r = cg_shift_path(c->cgroup, c->cgroup_root, &shifted); + if (r < 0) + return r; + + return cg_path_get_owner_uid(shifted, uid); +} + +_public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) { + assert_return(c, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_CMDLINE)) + return -ENODATA; + + if (!c->cmdline) + return -ENXIO; + + if (!c->cmdline_array) { + c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size); + if (!c->cmdline_array) + return -ENOMEM; + } + + *cmdline = c->cmdline_array; + return 0; +} + +_public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) { + assert_return(c, -EINVAL); + assert_return(sessionid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID)) + return -ENODATA; + + if (c->audit_session_id == AUDIT_SESSION_INVALID) + return -ENXIO; + + *sessionid = c->audit_session_id; + return 0; +} + +_public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) { + assert_return(c, -EINVAL); + assert_return(uid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID)) + return -ENODATA; + + if (c->audit_login_uid == UID_INVALID) + return -ENXIO; + + *uid = c->audit_login_uid; + return 0; +} + +_public_ int sd_bus_creds_get_tty(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_TTY)) + return -ENODATA; + + if (!c->tty) + return -ENXIO; + + *ret = c->tty; + return 0; +} + +_public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) { + assert_return(c, -EINVAL); + assert_return(unique_name, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_UNIQUE_NAME)) + return -ENODATA; + + *unique_name = c->unique_name; + return 0; +} + +_public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) { + assert_return(c, -EINVAL); + assert_return(well_known_names, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)) + return -ENODATA; + + /* As a special hack we return the bus driver as well-known + * names list when this is requested. */ + if (c->well_known_names_driver) { + static const char* const wkn[] = { + "org.freedesktop.DBus", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + + if (c->well_known_names_local) { + static const char* const wkn[] = { + "org.freedesktop.DBus.Local", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + + *well_known_names = c->well_known_names; + return 0; +} + +_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { + assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_DESCRIPTION)) + return -ENODATA; + + assert(c->description); + + if (!c->unescaped_description) { + c->unescaped_description = bus_label_unescape(c->description); + if (!c->unescaped_description) + return -ENOMEM; + } + + *ret = c->unescaped_description; + return 0; +} + +static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { + size_t sz; + + assert(c); + assert(capability >= 0); + assert(c->capability); + + if ((unsigned) capability > cap_last_cap()) + return 0; + + sz = DIV_ROUND_UP(cap_last_cap(), 32U); + + return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); +} + +_public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) { + assert_return(c, -EINVAL); + assert_return(capability >= 0, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS)) + return -ENODATA; + + return has_cap(c, CAP_OFFSET_EFFECTIVE, capability); +} + +_public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) { + assert_return(c, -EINVAL); + assert_return(capability >= 0, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_PERMITTED_CAPS)) + return -ENODATA; + + return has_cap(c, CAP_OFFSET_PERMITTED, capability); +} + +_public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) { + assert_return(c, -EINVAL); + assert_return(capability >= 0, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS)) + return -ENODATA; + + return has_cap(c, CAP_OFFSET_INHERITABLE, capability); +} + +_public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) { + assert_return(c, -EINVAL); + assert_return(capability >= 0, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_BOUNDING_CAPS)) + return -ENODATA; + + return has_cap(c, CAP_OFFSET_BOUNDING, capability); +} + +static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) { + size_t sz, max; + unsigned i, j; + + assert(c); + assert(p); + + max = DIV_ROUND_UP(cap_last_cap(), 32U); + p += strspn(p, WHITESPACE); + + sz = strlen(p); + if (sz % 8 != 0) + return -EINVAL; + + sz /= 8; + if (sz > max) + return -EINVAL; + + if (!c->capability) { + c->capability = new0(uint32_t, max * 4); + if (!c->capability) + return -ENOMEM; + } + + for (i = 0; i < sz; i ++) { + uint32_t v = 0; + + for (j = 0; j < 8; ++j) { + int t; + + t = unhexchar(*p++); + if (t < 0) + return -EINVAL; + + v = (v << 4) | t; + } + + c->capability[offset * max + (sz - i - 1)] = v; + } + + return 0; +} + +int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { + uint64_t missing; + int r; + + assert(c); + assert(c->allocated); + + if (!(mask & SD_BUS_CREDS_AUGMENT)) + return 0; + + /* Try to retrieve PID from creds if it wasn't passed to us */ + if (pid > 0) { + c->pid = pid; + c->mask |= SD_BUS_CREDS_PID; + } else if (c->mask & SD_BUS_CREDS_PID) + pid = c->pid; + else + /* Without pid we cannot do much... */ + return 0; + + /* Try to retrieve TID from creds if it wasn't passed to us */ + if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID)) + tid = c->tid; + + /* Calculate what we shall and can add */ + missing = mask & ~(c->mask|SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_AUGMENT); + if (missing == 0) + return 0; + + if (tid > 0) { + c->tid = tid; + c->mask |= SD_BUS_CREDS_TID; + } + + if (missing & (SD_BUS_CREDS_PPID | + SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | + SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | + SD_BUS_CREDS_SUPPLEMENTARY_GIDS | + SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS | + SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) { + + _cleanup_fclose_ FILE *f = NULL; + const char *p; + + p = procfs_file_alloca(pid, "status"); + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return -ESRCH; + else if (errno != EPERM && errno != EACCES) + return -errno; + } else { + char line[LINE_MAX]; + + FOREACH_LINE(line, f, return -errno) { + truncate_nl(line); + + if (missing & SD_BUS_CREDS_PPID) { + p = startswith(line, "PPid:"); + if (p) { + p += strspn(p, WHITESPACE); + + /* Explicitly check for PPID 0 (which is the case for PID 1) */ + if (!streq(p, "0")) { + r = parse_pid(p, &c->ppid); + if (r < 0) + return r; + + } else + c->ppid = 0; + + c->mask |= SD_BUS_CREDS_PPID; + continue; + } + } + + if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { + p = startswith(line, "Uid:"); + if (p) { + unsigned long uid, euid, suid, fsuid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) + return -EIO; + + if (missing & SD_BUS_CREDS_UID) + c->uid = (uid_t) uid; + if (missing & SD_BUS_CREDS_EUID) + c->euid = (uid_t) euid; + if (missing & SD_BUS_CREDS_SUID) + c->suid = (uid_t) suid; + if (missing & SD_BUS_CREDS_FSUID) + c->fsuid = (uid_t) fsuid; + + c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); + continue; + } + } + + if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) { + p = startswith(line, "Gid:"); + if (p) { + unsigned long gid, egid, sgid, fsgid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) + return -EIO; + + if (missing & SD_BUS_CREDS_GID) + c->gid = (gid_t) gid; + if (missing & SD_BUS_CREDS_EGID) + c->egid = (gid_t) egid; + if (missing & SD_BUS_CREDS_SGID) + c->sgid = (gid_t) sgid; + if (missing & SD_BUS_CREDS_FSGID) + c->fsgid = (gid_t) fsgid; + + c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); + continue; + } + } + + if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + p = startswith(line, "Groups:"); + if (p) { + size_t allocated = 0; + + for (;;) { + unsigned long g; + int n = 0; + + p += strspn(p, WHITESPACE); + if (*p == 0) + break; + + if (sscanf(p, "%lu%n", &g, &n) != 1) + return -EIO; + + if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1)) + return -ENOMEM; + + c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g; + p += n; + } + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + continue; + } + } + + if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) { + p = startswith(line, "CapEff:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS; + continue; + } + } + + if (missing & SD_BUS_CREDS_PERMITTED_CAPS) { + p = startswith(line, "CapPrm:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_PERMITTED, p); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_PERMITTED_CAPS; + continue; + } + } + + if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) { + p = startswith(line, "CapInh:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_INHERITABLE, p); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS; + continue; + } + } + + if (missing & SD_BUS_CREDS_BOUNDING_CAPS) { + p = startswith(line, "CapBnd:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_BOUNDING, p); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_BOUNDING_CAPS; + continue; + } + } + } + } + } + + if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) { + const char *p; + + p = procfs_file_alloca(pid, "attr/current"); + r = read_one_line_file(p, &c->label); + if (r < 0) { + if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + if (missing & SD_BUS_CREDS_COMM) { + r = get_process_comm(pid, &c->comm); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_COMM; + } + + if (missing & SD_BUS_CREDS_EXE) { + r = get_process_exe(pid, &c->exe); + if (r == -ESRCH) { + /* Unfortunately we cannot really distinguish + * the case here where the process does not + * exist, and /proc/$PID/exe being unreadable + * because $PID is a kernel thread. Hence, + * assume it is a kernel thread, and rely on + * that this case is caught with a later + * call. */ + c->exe = NULL; + c->mask |= SD_BUS_CREDS_EXE; + } else if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_EXE; + } + + if (missing & SD_BUS_CREDS_CMDLINE) { + const char *p; + + p = procfs_file_alloca(pid, "cmdline"); + r = read_full_file(p, &c->cmdline, &c->cmdline_size); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else { + if (c->cmdline_size == 0) + c->cmdline = mfree(c->cmdline); + + c->mask |= SD_BUS_CREDS_CMDLINE; + } + } + + if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { + _cleanup_free_ char *p = NULL; + + if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &c->tid_comm); + if (r == -ENOENT) + return -ESRCH; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_TID_COMM; + } + + if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { + + if (!c->cgroup) { + r = cg_pid_get_path(NULL, pid, &c->cgroup); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } + } + + if (!c->cgroup_root) { + r = cg_get_root_path(&c->cgroup_root); + if (r < 0) + return r; + } + + if (c->cgroup) + c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); + } + + if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { + r = audit_session_from_pid(pid, &c->audit_session_id); + if (r == -ENODATA) { + /* ENODATA means: no audit session id assigned */ + c->audit_session_id = AUDIT_SESSION_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } + + if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { + r = audit_loginuid_from_pid(pid, &c->audit_login_uid); + if (r == -ENODATA) { + /* ENODATA means: no audit login uid assigned */ + c->audit_login_uid = UID_INVALID; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } else if (r < 0) { + if (r != -EOPNOTSUPP && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } + + if (missing & SD_BUS_CREDS_TTY) { + r = get_ctty(pid, NULL, &c->tty); + if (r == -ENXIO) { + /* ENXIO means: process has no controlling TTY */ + c->tty = NULL; + c->mask |= SD_BUS_CREDS_TTY; + } else if (r < 0) { + if (r != -EPERM && r != -EACCES && r != -ENOENT) + return r; + } else + c->mask |= SD_BUS_CREDS_TTY; + } + + /* In case only the exe path was to be read we cannot + * distinguish the case where the exe path was unreadable + * because the process was a kernel thread, or when the + * process didn't exist at all. Hence, let's do a final check, + * to be sure. */ + if (!pid_is_alive(pid)) + return -ESRCH; + + if (tid > 0 && tid != pid && !pid_is_unwaited(tid)) + return -ESRCH; + + c->augmented = missing & c->mask; + + return 0; +} + +int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *n = NULL; + int r; + + assert(c); + assert(ret); + + if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) { + /* There's already all data we need, or augmentation + * wasn't turned on. */ + + *ret = sd_bus_creds_ref(c); + return 0; + } + + n = bus_creds_new(); + if (!n) + return -ENOMEM; + + /* Copy the original data over */ + + if (c->mask & mask & SD_BUS_CREDS_PID) { + n->pid = c->pid; + n->mask |= SD_BUS_CREDS_PID; + } + + if (c->mask & mask & SD_BUS_CREDS_TID) { + n->tid = c->tid; + n->mask |= SD_BUS_CREDS_TID; + } + + if (c->mask & mask & SD_BUS_CREDS_PPID) { + n->ppid = c->ppid; + n->mask |= SD_BUS_CREDS_PPID; + } + + if (c->mask & mask & SD_BUS_CREDS_UID) { + n->uid = c->uid; + n->mask |= SD_BUS_CREDS_UID; + } + + if (c->mask & mask & SD_BUS_CREDS_EUID) { + n->euid = c->euid; + n->mask |= SD_BUS_CREDS_EUID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUID) { + n->suid = c->suid; + n->mask |= SD_BUS_CREDS_SUID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSUID) { + n->fsuid = c->fsuid; + n->mask |= SD_BUS_CREDS_FSUID; + } + + if (c->mask & mask & SD_BUS_CREDS_GID) { + n->gid = c->gid; + n->mask |= SD_BUS_CREDS_GID; + } + + if (c->mask & mask & SD_BUS_CREDS_EGID) { + n->egid = c->egid; + n->mask |= SD_BUS_CREDS_EGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SGID) { + n->sgid = c->sgid; + n->mask |= SD_BUS_CREDS_SGID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSGID) { + n->fsgid = c->fsgid; + n->mask |= SD_BUS_CREDS_FSGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + if (c->supplementary_gids) { + n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); + if (!n->supplementary_gids) + return -ENOMEM; + n->n_supplementary_gids = c->n_supplementary_gids; + } else { + n->supplementary_gids = NULL; + n->n_supplementary_gids = 0; + } + + n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + + if (c->mask & mask & SD_BUS_CREDS_COMM) { + assert(c->comm); + + n->comm = strdup(c->comm); + if (!n->comm) + return -ENOMEM; + + n->mask |= SD_BUS_CREDS_COMM; + } + + if (c->mask & mask & SD_BUS_CREDS_TID_COMM) { + assert(c->tid_comm); + + n->tid_comm = strdup(c->tid_comm); + if (!n->tid_comm) + return -ENOMEM; + + n->mask |= SD_BUS_CREDS_TID_COMM; + } + + if (c->mask & mask & SD_BUS_CREDS_EXE) { + if (c->exe) { + n->exe = strdup(c->exe); + if (!n->exe) + return -ENOMEM; + } else + n->exe = NULL; + + n->mask |= SD_BUS_CREDS_EXE; + } + + if (c->mask & mask & SD_BUS_CREDS_CMDLINE) { + if (c->cmdline) { + n->cmdline = memdup(c->cmdline, c->cmdline_size); + if (!n->cmdline) + return -ENOMEM; + + n->cmdline_size = c->cmdline_size; + } else { + n->cmdline = NULL; + n->cmdline_size = 0; + } + + n->mask |= SD_BUS_CREDS_CMDLINE; + } + + if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID)) { + assert(c->cgroup); + + n->cgroup = strdup(c->cgroup); + if (!n->cgroup) + return -ENOMEM; + + n->cgroup_root = strdup(c->cgroup_root); + if (!n->cgroup_root) + return -ENOMEM; + + n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_OWNER_UID); + } + + if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) { + assert(c->capability); + + n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4); + if (!n->capability) + return -ENOMEM; + + n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS); + } + + if (c->mask & mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + assert(c->label); + + n->label = strdup(c->label); + if (!n->label) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { + n->audit_session_id = c->audit_session_id; + n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } + if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { + n->audit_login_uid = c->audit_login_uid; + n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; + } + + if (c->mask & mask & SD_BUS_CREDS_TTY) { + if (c->tty) { + n->tty = strdup(c->tty); + if (!n->tty) + return -ENOMEM; + } else + n->tty = NULL; + n->mask |= SD_BUS_CREDS_TTY; + } + + if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) { + assert(c->unique_name); + + n->unique_name = strdup(c->unique_name); + if (!n->unique_name) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_UNIQUE_NAME; + } + + if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + if (strv_isempty(c->well_known_names)) + n->well_known_names = NULL; + else { + n->well_known_names = strv_copy(c->well_known_names); + if (!n->well_known_names) + return -ENOMEM; + } + n->well_known_names_driver = c->well_known_names_driver; + n->well_known_names_local = c->well_known_names_local; + n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + + if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { + assert(c->description); + n->description = strdup(c->description); + if (!n->description) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_DESCRIPTION; + } + + n->augmented = c->augmented & n->mask; + + /* Get more data */ + + r = bus_creds_add_more(n, mask, 0, 0); + if (r < 0) + return r; + + *ret = n; + n = NULL; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-creds.h b/src/libsystemd/src/sd-bus/bus-creds.h new file mode 100644 index 0000000000..3e2311f91d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-creds.h @@ -0,0 +1,90 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +struct sd_bus_creds { + bool allocated; + unsigned n_ref; + + uint64_t mask; + uint64_t augmented; + + uid_t uid; + uid_t euid; + uid_t suid; + uid_t fsuid; + gid_t gid; + gid_t egid; + gid_t sgid; + gid_t fsgid; + + gid_t *supplementary_gids; + unsigned n_supplementary_gids; + + pid_t ppid; + pid_t pid; + pid_t tid; + + char *comm; + char *tid_comm; + char *exe; + + char *cmdline; + size_t cmdline_size; + char **cmdline_array; + + char *cgroup; + char *session; + char *unit; + char *user_unit; + char *slice; + char *user_slice; + + char *tty; + + uint32_t *capability; + + uint32_t audit_session_id; + uid_t audit_login_uid; + + char *label; + + char *unique_name; + + char **well_known_names; + bool well_known_names_driver:1; + bool well_known_names_local:1; + + char *cgroup_root; + + char *description, *unescaped_description; +}; + +sd_bus_creds* bus_creds_new(void); + +void bus_creds_done(sd_bus_creds *c); + +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); diff --git a/src/libsystemd/src/sd-bus/bus-dump.c b/src/libsystemd/src/sd-bus/bus-dump.c new file mode 100644 index 0000000000..cec53d75e5 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-dump.c @@ -0,0 +1,603 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/cap-list.h" +#include "basic/capability-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/locale-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "bus-dump.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-type.h" + +static char *indent(unsigned level, unsigned flags) { + char *p; + unsigned n, i = 0; + + n = 0; + + if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0) + level -= 1; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) + n += 2; + + p = new(char, n + level*8 + 1); + if (!p) + return NULL; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { + p[i++] = ' '; + p[i++] = ' '; + } + + memset(p + i, ' ', level*8); + p[i + level*8] = 0; + + return p; +} + +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { + unsigned level = 1; + int r; + + assert(m); + + if (!f) + f = stdout; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { + fprintf(f, + "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, + m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : + m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : + m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), + ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), + m->header->endian, + m->header->flags, + m->header->version, + m->priority); + + /* Display synthetic message serial number in a more readable + * format than (uint32_t) -1 */ + if (BUS_MESSAGE_COOKIE(m) == 0xFFFFFFFFULL) + fprintf(f, " Cookie=-1"); + else + fprintf(f, " Cookie=%" PRIu64, BUS_MESSAGE_COOKIE(m)); + + if (m->reply_cookie != 0) + fprintf(f, " ReplyCookie=%" PRIu64, m->reply_cookie); + + fputs("\n", f); + + if (m->sender) + fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_normal()); + if (m->destination) + fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_normal()); + if (m->path) + fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_normal()); + if (m->interface) + fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_normal()); + if (m->member) + fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_normal()); + + if (m->sender || m->destination || m->path || m->interface || m->member) + fputs("\n", f); + + if (sd_bus_error_is_set(&m->error)) + fprintf(f, + " ErrorName=%s%s%s" + " ErrorMessage=%s\"%s\"%s\n", + ansi_highlight_red(), strna(m->error.name), ansi_normal(), + ansi_highlight_red(), strna(m->error.message), ansi_normal()); + + if (m->monotonic != 0) + fprintf(f, " Monotonic="USEC_FMT, m->monotonic); + if (m->realtime != 0) + fprintf(f, " Realtime="USEC_FMT, m->realtime); + if (m->seqnum != 0) + fprintf(f, " SequenceNumber=%"PRIu64, m->seqnum); + + if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0) + fputs("\n", f); + + bus_creds_dump(&m->creds, f, true); + } + + r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)); + if (r < 0) + return log_error_errno(r, "Failed to rewind: %m"); + + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { + _cleanup_free_ char *prefix = NULL; + + prefix = indent(0, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%sMESSAGE \"%s\" {\n", prefix, strempty(m->root_container.signature)); + } + + for (;;) { + _cleanup_free_ char *prefix = NULL; + const char *contents = NULL; + char type; + union { + uint8_t u8; + uint16_t u16; + int16_t s16; + uint32_t u32; + int32_t s32; + uint64_t u64; + int64_t s64; + double d64; + const char *string; + int i; + } basic; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return log_error_errno(r, "Failed to peek type: %m"); + + if (r == 0) { + if (level <= 1) + break; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return log_error_errno(r, "Failed to exit container: %m"); + + level--; + + prefix = indent(level, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%s};\n", prefix); + continue; + } + + prefix = indent(level, flags); + if (!prefix) + return log_oom(); + + if (bus_type_is_container(type) > 0) { + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return log_error_errno(r, "Failed to enter container: %m"); + + if (type == SD_BUS_TYPE_ARRAY) + fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents); + else if (type == SD_BUS_TYPE_VARIANT) + fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents); + else if (type == SD_BUS_TYPE_STRUCT) + fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents); + else if (type == SD_BUS_TYPE_DICT_ENTRY) + fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents); + + level++; + + continue; + } + + r = sd_bus_message_read_basic(m, type, &basic); + if (r < 0) + return log_error_errno(r, "Failed to get basic: %m"); + + assert(r > 0); + + switch (type) { + + case SD_BUS_TYPE_BYTE: + fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_normal()); + break; + + case SD_BUS_TYPE_BOOLEAN: + fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_normal()); + break; + + case SD_BUS_TYPE_INT16: + fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT16: + fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_normal()); + break; + + case SD_BUS_TYPE_INT32: + fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT32: + fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_normal()); + break; + + case SD_BUS_TYPE_INT64: + fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_normal()); + break; + + case SD_BUS_TYPE_UINT64: + fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_normal()); + break; + + case SD_BUS_TYPE_DOUBLE: + fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_normal()); + break; + + case SD_BUS_TYPE_STRING: + fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_OBJECT_PATH: + fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_SIGNATURE: + fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal()); + break; + + case SD_BUS_TYPE_UNIX_FD: + fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_normal()); + break; + + default: + assert_not_reached("Unknown basic type."); + } + } + + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) { + _cleanup_free_ char *prefix = NULL; + + prefix = indent(0, flags); + if (!prefix) + return log_oom(); + + fprintf(f, "%s};\n\n", prefix); + } + + return 0; +} + +static void dump_capabilities( + sd_bus_creds *c, + FILE *f, + const char *name, + bool terse, + int (*has)(sd_bus_creds *c, int capability)) { + + unsigned long i, last_cap; + unsigned n = 0; + int r; + + assert(c); + assert(f); + assert(name); + assert(has); + + i = 0; + r = has(c, i); + if (r < 0) + return; + + fprintf(f, "%s%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight()); + last_cap = cap_last_cap(); + + for (;;) { + if (r > 0) { + + if (n > 0) + fputc(' ', f); + if (n % 4 == 3) + fprintf(f, terse ? "\n " : "\n "); + + fprintf(f, "%s", strna(capability_to_name(i))); + n++; + } + + i++; + + if (i > last_cap) + break; + + r = has(c, i); + } + + fputs("\n", f); + + if (!terse) + fputs(ansi_normal(), f); +} + +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { + uid_t owner, audit_loginuid; + uint32_t audit_sessionid; + char **cmdline = NULL, **well_known = NULL; + const char *prefix, *color, *suffix, *s; + int r, q, v, w, z; + + assert(c); + + if (!f) + f = stdout; + + if (terse) { + prefix = " "; + suffix = ""; + color = ""; + } else { + const char *off; + + prefix = ""; + color = ansi_highlight(); + + off = ansi_normal(); + suffix = strjoina(off, "\n"); + } + + if (c->mask & SD_BUS_CREDS_PID) + fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); + if (c->mask & SD_BUS_CREDS_TID) + fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); + if (c->mask & SD_BUS_CREDS_PPID) { + if (c->ppid == 0) + fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix); + else + fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix); + } + if (c->mask & SD_BUS_CREDS_TTY) + fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix); + + if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY)))) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_UID) + fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix); + if (c->mask & SD_BUS_CREDS_EUID) + fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix); + if (c->mask & SD_BUS_CREDS_SUID) + fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix); + if (c->mask & SD_BUS_CREDS_FSUID) + fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix); + r = sd_bus_creds_get_owner_uid(c, &owner); + if (r >= 0) + fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix); + if (c->mask & SD_BUS_CREDS_GID) + fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix); + if (c->mask & SD_BUS_CREDS_EGID) + fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix); + if (c->mask & SD_BUS_CREDS_SGID) + fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix); + if (c->mask & SD_BUS_CREDS_FSGID) + fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix); + + if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + unsigned i; + + fprintf(f, "%sSupplementaryGIDs=%s", prefix, color); + for (i = 0; i < c->n_supplementary_gids; i++) + fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]); + fprintf(f, "%s", suffix); + } + + if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0)) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_COMM) + fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix); + if (c->mask & SD_BUS_CREDS_TID_COMM) + fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); + if (c->mask & SD_BUS_CREDS_EXE) + fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix); + + if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) + fputs("\n", f); + + r = sd_bus_creds_get_cmdline(c, &cmdline); + if (r >= 0) { + char **i; + + fprintf(f, "%sCommandLine=%s", prefix, color); + STRV_FOREACH(i, cmdline) { + if (i != cmdline) + fputc(' ', f); + + fputs(*i, f); + } + + fprintf(f, "%s", suffix); + } else if (r != -ENODATA) + fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix); + + if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) + fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); + if (c->mask & SD_BUS_CREDS_DESCRIPTION) + fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix); + + if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_CGROUP) + fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); + s = NULL; + r = sd_bus_creds_get_unit(c, &s); + if (r != -ENODATA) + fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + v = sd_bus_creds_get_slice(c, &s); + if (v != -ENODATA) + fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + q = sd_bus_creds_get_user_unit(c, &s); + if (q != -ENODATA) + fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + w = sd_bus_creds_get_user_slice(c, &s); + if (w != -ENODATA) + fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix); + s = NULL; + z = sd_bus_creds_get_session(c, &s); + if (z != -ENODATA) + fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix); + + if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA)) + fputs("\n", f); + + r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid); + if (r >= 0) + fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); + else if (r != -ENODATA) + fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix); + q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid); + if (q >= 0) + fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); + else if (q != -ENODATA) + fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix); + + if (terse && (r != -ENODATA || q != -ENODATA)) + fputs("\n", f); + + if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) + fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); + + if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { + char **i; + + fprintf(f, "%sWellKnownNames=%s", prefix, color); + STRV_FOREACH(i, well_known) { + if (i != well_known) + fputc(' ', f); + + fputs(*i, f); + } + + fprintf(f, "%s", suffix); + } + + if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)) + fputc('\n', f); + + dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap); + dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap); + dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap); + dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap); + + return 0; +} + +/* + * For details about the file format, see: + * + * http://wiki.wireshark.org/Development/LibpcapFileFormat + */ + +typedef struct _packed_ pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_hdr_t ; + +typedef struct _packed_ pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +int bus_pcap_header(size_t snaplen, FILE *f) { + + pcap_hdr_t hdr = { + .magic_number = 0xa1b2c3d4U, + .version_major = 2, + .version_minor = 4, + .thiszone = 0, /* UTC */ + .sigfigs = 0, + .network = 231, /* D-Bus */ + }; + + if (!f) + f = stdout; + + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + hdr.snaplen = (uint32_t) snaplen; + + fwrite(&hdr, 1, sizeof(hdr), f); + + return fflush_and_check(f); +} + +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { + struct bus_body_part *part; + pcaprec_hdr_t hdr = {}; + struct timeval tv; + unsigned i; + size_t w; + + if (!f) + f = stdout; + + assert(m); + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + if (m->realtime != 0) + timeval_store(&tv, m->realtime); + else + assert_se(gettimeofday(&tv, NULL) >= 0); + + hdr.ts_sec = tv.tv_sec; + hdr.ts_usec = tv.tv_usec; + hdr.orig_len = BUS_MESSAGE_SIZE(m); + hdr.incl_len = MIN(hdr.orig_len, snaplen); + + /* write the pcap header */ + fwrite(&hdr, 1, sizeof(hdr), f); + + /* write the dbus header */ + w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); + fwrite(m->header, 1, w, f); + snaplen -= w; + + /* write the dbus body */ + MESSAGE_FOREACH_PART(part, i, m) { + if (snaplen <= 0) + break; + + w = MIN(part->size, snaplen); + fwrite(part->data, 1, w, f); + snaplen -= w; + } + + return fflush_and_check(f); +} diff --git a/src/libsystemd/src/sd-bus/bus-dump.h b/src/libsystemd/src/sd-bus/bus-dump.h new file mode 100644 index 0000000000..68fa043786 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-dump.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +enum { + BUS_MESSAGE_DUMP_WITH_HEADER = 1, + BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2, +}; + +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags); + +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); + +int bus_pcap_header(size_t snaplen, FILE *f); +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/src/sd-bus/bus-error.c b/src/libsystemd/src/sd-bus/bus-error.c new file mode 100644 index 0000000000..616d784590 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-error.c @@ -0,0 +1,609 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/errno-list.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "bus-error.h" + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), + SD_BUS_ERROR_MAP_END +}; + +/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section. + * Hide them; for currently unknown reasons they get exported to the shared libries + * even without being listed in the sym file. */ +extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_; + +/* Additional maps registered with sd_bus_error_add_map() are in this + * NULL terminated array */ +static const sd_bus_error_map **additional_error_maps = NULL; + +static int bus_error_name_to_errno(const char *name) { + const sd_bus_error_map **map, *m; + const char *p; + int r; + + if (!name) + return EINVAL; + + p = startswith(name, "System.Error."); + if (p) { + r = errno_from_name(p); + if (r < 0) + return EIO; + + return r; + } + + 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) + break; + + 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 next 8 byte + * boundary, which is the selected alignment for the + * arrays. */ + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + if (streq(m->name, name)) + return m->code; + + m++; + } + + return EIO; +} + +static sd_bus_error errno_to_bus_error_const(int error) { + + if (error < 0) + error = -error; + + switch (error) { + + case ENOMEM: + return BUS_ERROR_OOM; + + case EPERM: + case EACCES: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); + + case EINVAL: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); + + case ESRCH: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); + + case ENOENT: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); + + case EEXIST: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists"); + + case ETIMEDOUT: + case ETIME: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out"); + + case EIO: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error"); + + case ENETRESET: + case ECONNABORTED: + case ECONNRESET: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); + + case EOPNOTSUPP: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); + + case EADDRNOTAVAIL: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); + + case ENOBUFS: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); + + case EADDRINUSE: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); + + case EBADMSG: + return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); + } + + return SD_BUS_ERROR_NULL; +} + +static int errno_to_bus_error_name_new(int error, char **ret) { + const char *name; + char *n; + + if (error < 0) + error = -error; + + name = errno_to_name(error); + if (!name) + return 0; + + n = strappend("System.Error.", name); + if (!n) + return -ENOMEM; + + *ret = n; + return 1; +} + +bool bus_error_is_dirty(sd_bus_error *e) { + if (!e) + return false; + + return e->name || e->message || e->_need_free != 0; +} + +_public_ void sd_bus_error_free(sd_bus_error *e) { + if (!e) + return; + + if (e->_need_free > 0) { + free((void*) e->name); + free((void*) e->message); + } + + e->name = e->message = NULL; + e->_need_free = 0; +} + +_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { + + if (!name) + return 0; + if (!e) + goto finish; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + e->name = strdup(name); + if (!e->name) { + *e = BUS_ERROR_OOM; + return -ENOMEM; + } + + if (message) + e->message = strdup(message); + + e->_need_free = 1; + +finish: + return -bus_error_name_to_errno(name); +} + +int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) { + + if (!name) + return 0; + + if (e) { + assert_return(!bus_error_is_dirty(e), -EINVAL); + + 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); + + e->_need_free = 1; + } + + return -bus_error_name_to_errno(name); +} + +_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { + + if (format) { + int r; + va_list ap; + + va_start(ap, format); + r = bus_error_setfv(e, name, format, ap); + va_end(ap); + + return r; + } + + return sd_bus_error_set(e, name, NULL); +} + +_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { + + if (!sd_bus_error_is_set(e)) + return 0; + if (!dest) + goto finish; + + assert_return(!bus_error_is_dirty(dest), -EINVAL); + + /* + * _need_free < 0 indicates that the error is temporarily const, needs deep copying + * _need_free == 0 indicates that the error is perpetually const, needs no deep copying + * _need_free > 0 indicates that the error is fully dynamic, needs deep copying + */ + + if (e->_need_free == 0) + *dest = *e; + else { + dest->name = strdup(e->name); + if (!dest->name) { + *dest = BUS_ERROR_OOM; + return -ENOMEM; + } + + if (e->message) + dest->message = strdup(e->message); + + dest->_need_free = 1; + } + +finish: + return -bus_error_name_to_errno(e->name); +} + +_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { + if (!name) + return 0; + if (!e) + goto finish; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + *e = SD_BUS_ERROR_MAKE_CONST(name, message); + +finish: + return -bus_error_name_to_errno(name); +} + +_public_ int sd_bus_error_is_set(const sd_bus_error *e) { + if (!e) + return 0; + + return !!e->name; +} + +_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) { + if (!e) + return 0; + + return streq_ptr(e->name, name); +} + +_public_ int sd_bus_error_get_errno(const sd_bus_error* e) { + if (!e) + return 0; + + if (!e->name) + return 0; + + return bus_error_name_to_errno(e->name); +} + +static void bus_error_strerror(sd_bus_error *e, int error) { + size_t k = 64; + char *m; + + assert(e); + + for (;;) { + char *x; + + m = new(char, k); + if (!m) + return; + + errno = 0; + x = strerror_r(error, m, k); + if (errno == ERANGE || strlen(x) >= k - 1) { + free(m); + k *= 2; + continue; + } + + if (errno) { + free(m); + return; + } + + if (x == m) { + if (e->_need_free > 0) { + /* Error is already dynamic, let's just update the message */ + free((char*) e->message); + e->message = x; + + } else { + char *t; + /* Error was const so far, let's make it dynamic, if we can */ + + t = strdup(e->name); + if (!t) { + free(m); + return; + } + + e->_need_free = 1; + e->name = t; + e->message = x; + } + } else { + free(m); + + if (e->_need_free > 0) { + char *t; + + /* Error is dynamic, let's hence make the message also dynamic */ + t = strdup(x); + if (!t) + return; + + free((char*) e->message); + e->message = t; + } else { + /* Error is const, hence we can just override */ + e->message = x; + } + } + + return; + } +} + +_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { + + if (error < 0) + error = -error; + + if (!e) + return -error; + if (error == 0) + return -error; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + /* First, try a const translation */ + *e = errno_to_bus_error_const(error); + + if (!sd_bus_error_is_set(e)) { + int k; + + /* If that didn't work, try a dynamic one. */ + + k = errno_to_bus_error_name_new(error, (char**) &e->name); + if (k > 0) + e->_need_free = 1; + else if (k < 0) { + *e = BUS_ERROR_OOM; + return -error; + } else + *e = BUS_ERROR_FAILED; + } + + /* Now, fill in the message from strerror() if we can */ + bus_error_strerror(e, error); + return -error; +} + +_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { + PROTECT_ERRNO; + int r; + + if (error < 0) + error = -error; + + if (!e) + return -error; + if (error == 0) + return 0; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + /* First, try a const translation */ + *e = errno_to_bus_error_const(error); + + if (!sd_bus_error_is_set(e)) { + int k; + + /* If that didn't work, try a dynamic one */ + + k = errno_to_bus_error_name_new(error, (char**) &e->name); + if (k > 0) + e->_need_free = 1; + else if (k < 0) { + *e = BUS_ERROR_OOM; + return -ENOMEM; + } else + *e = BUS_ERROR_FAILED; + } + + if (format) { + char *m; + + /* Then, let's try to fill in the supplied message */ + + errno = error; /* Make sure that %m resolves to the specified error */ + r = vasprintf(&m, format, ap); + if (r >= 0) { + + if (e->_need_free <= 0) { + char *t; + + t = strdup(e->name); + if (t) { + e->_need_free = 1; + e->name = t; + e->message = m; + return -error; + } + + free(m); + } else { + free((char*) e->message); + e->message = m; + return -error; + } + } + } + + /* If that didn't work, use strerror() for the message */ + bus_error_strerror(e, error); + return -error; +} + +_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) { + int r; + + if (error < 0) + error = -error; + + if (!e) + return -error; + if (error == 0) + return 0; + + assert_return(!bus_error_is_dirty(e), -EINVAL); + + if (format) { + va_list ap; + + va_start(ap, format); + r = sd_bus_error_set_errnofv(e, error, format, ap); + va_end(ap); + + return r; + } + + return sd_bus_error_set_errno(e, error); +} + +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 + * its error messages, so let's override them here */ + if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) + return "Access denied"; + + if (e->message) + return e->message; + } + + if (error < 0) + error = -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 (; 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; + + additional_error_maps = maps; + return 1; +} diff --git a/src/libsystemd/src/sd-bus/bus-error.h b/src/libsystemd/src/sd-bus/bus-error.h new file mode 100644 index 0000000000..0693d9c68c --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-error.h @@ -0,0 +1,64 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/macro.h" + +bool bus_error_is_dirty(sd_bus_error *e); + +const char *bus_error_message(const sd_bus_error *e, int error); + +int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0); +int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0); + +#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") +#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") + +/* + * There are two ways to register error maps with the error translation + * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only + * works when linked into the same ELF module, or via + * sd_bus_error_add_map() which is the official, external API, that + * works from any module. + * + * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in + * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at + * least once per compilation unit (i.e. per library), to ensure that + * the error map is really added to the final binary. + */ + +#define BUS_ERROR_MAP_ELF_REGISTER \ + __attribute__ ((__section__("BUS_ERROR_MAP"))) \ + __attribute__ ((__used__)) \ + __attribute__ ((aligned(8))) + +#define BUS_ERROR_MAP_ELF_USE(errors) \ + extern const sd_bus_error_map errors[]; \ + __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors; + +/* We use something exotic as end marker, to ensure people build the + * maps using the macsd-ros. */ +#define BUS_ERROR_MAP_END_MARKER -'x' + +BUS_ERROR_MAP_ELF_USE(bus_standard_errors); diff --git a/src/libsystemd/src/sd-bus/bus-gvariant.c b/src/libsystemd/src/sd-bus/bus-gvariant.c new file mode 100644 index 0000000000..58782767fa --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-gvariant.c @@ -0,0 +1,311 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "bus-gvariant.h" +#include "bus-signature.h" +#include "bus-type.h" + +int bus_gvariant_get_size(const char *signature) { + const char *p; + int sum = 0, r; + + /* For fixed size structs. Fails for variable size structs. */ + + p = signature; + while (*p != 0) { + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + r = bus_gvariant_get_alignment(t); + if (r < 0) + return r; + + sum = ALIGN_TO(sum, r); + } + + switch (*p) { + + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_BYTE: + sum += 1; + break; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + sum += 2; + break; + + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_UNIX_FD: + sum += 4; + break; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + sum += 8; + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + if (n == 2) { + /* unary type () has fixed size of 1 */ + r = 1; + } else { + char t[n-1]; + + memcpy(t, p + 1, n - 2); + t[n - 2] = 0; + + r = bus_gvariant_get_size(t); + if (r < 0) + return r; + } + + sum += r; + break; + } + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_ARRAY: + case SD_BUS_TYPE_VARIANT: + return -EINVAL; + + default: + assert_not_reached("Unknown signature type"); + } + + p += n; + } + + r = bus_gvariant_get_alignment(signature); + if (r < 0) + return r; + + return ALIGN_TO(sum, r); +} + +int bus_gvariant_get_alignment(const char *signature) { + size_t alignment = 1; + const char *p; + int r; + + p = signature; + while (*p != 0 && alignment < 8) { + size_t n; + int a; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + + switch (*p) { + + case SD_BUS_TYPE_BYTE: + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + a = 1; + break; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + a = 2; + break; + + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_UNIX_FD: + a = 4; + break; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + case SD_BUS_TYPE_VARIANT: + a = 8; + break; + + case SD_BUS_TYPE_ARRAY: { + char t[n]; + + memcpy(t, p + 1, n - 1); + t[n - 1] = 0; + + a = bus_gvariant_get_alignment(t); + break; + } + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + char t[n-1]; + + memcpy(t, p + 1, n - 2); + t[n - 2] = 0; + + a = bus_gvariant_get_alignment(t); + break; + } + + default: + assert_not_reached("Unknown signature type"); + } + + if (a < 0) + return a; + + assert(a > 0 && a <= 8); + if ((size_t) a > alignment) + alignment = (size_t) a; + + p += n; + } + + return alignment; +} + +int bus_gvariant_is_fixed_size(const char *signature) { + const char *p; + int r; + + assert(signature); + + p = signature; + while (*p != 0) { + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + + switch (*p) { + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_ARRAY: + case SD_BUS_TYPE_VARIANT: + return 0; + + case SD_BUS_TYPE_BYTE: + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_UNIX_FD: + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + char t[n-1]; + + memcpy(t, p + 1, n - 2); + t[n - 2] = 0; + + r = bus_gvariant_is_fixed_size(t); + if (r <= 0) + return r; + break; + } + + default: + assert_not_reached("Unknown signature type"); + } + + p += n; + } + + return true; +} + +size_t bus_gvariant_determine_word_size(size_t sz, size_t extra) { + if (sz + extra <= 0xFF) + return 1; + else if (sz + extra*2 <= 0xFFFF) + return 2; + else if (sz + extra*4 <= 0xFFFFFFFF) + return 4; + else + return 8; +} + +size_t bus_gvariant_read_word_le(void *p, size_t sz) { + union { + uint16_t u16; + uint32_t u32; + uint64_t u64; + } x; + + assert(p); + + if (sz == 1) + return *(uint8_t*) p; + + memcpy(&x, p, sz); + + if (sz == 2) + return le16toh(x.u16); + else if (sz == 4) + return le32toh(x.u32); + else if (sz == 8) + return le64toh(x.u64); + + assert_not_reached("unknown word width"); +} + +void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) { + union { + uint16_t u16; + uint32_t u32; + uint64_t u64; + } x; + + assert(p); + assert(sz == 8 || (value < (1ULL << (sz*8)))); + + if (sz == 1) { + *(uint8_t*) p = value; + return; + } else if (sz == 2) + x.u16 = htole16((uint16_t) value); + else if (sz == 4) + x.u32 = htole32((uint32_t) value); + else if (sz == 8) + x.u64 = htole64((uint64_t) value); + else + assert_not_reached("unknown word width"); + + memcpy(p, &x, sz); +} diff --git a/src/libsystemd/src/sd-bus/bus-gvariant.h b/src/libsystemd/src/sd-bus/bus-gvariant.h new file mode 100644 index 0000000000..d9468ee9a8 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-gvariant.h @@ -0,0 +1,30 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" + +int bus_gvariant_get_size(const char *signature) _pure_; +int bus_gvariant_get_alignment(const char *signature) _pure_; +int bus_gvariant_is_fixed_size(const char *signature) _pure_; + +size_t bus_gvariant_determine_word_size(size_t sz, size_t extra); +void bus_gvariant_write_word_le(void *p, size_t sz, size_t value); +size_t bus_gvariant_read_word_le(void *p, size_t sz); diff --git a/src/libsystemd/src/sd-bus/bus-internal.c b/src/libsystemd/src/sd-bus/bus-internal.c new file mode 100644 index 0000000000..8dc441227a --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-internal.c @@ -0,0 +1,375 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/string-util.h" + +#include "bus-internal.h" +#include "bus-message.h" + +bool object_path_is_valid(const char *p) { + const char *q; + bool slash; + + if (!p) + return false; + + if (p[0] != '/') + return false; + + if (p[1] == 0) + return true; + + for (slash = true, q = p+1; *q; q++) + if (*q == '/') { + if (slash) + return false; + + slash = true; + } else { + bool good; + + good = + (*q >= 'a' && *q <= 'z') || + (*q >= 'A' && *q <= 'Z') || + (*q >= '0' && *q <= '9') || + *q == '_'; + + if (!good) + return false; + + slash = false; + } + + if (slash) + return false; + + return true; +} + +char* object_path_startswith(const char *a, const char *b) { + const char *p; + + if (!object_path_is_valid(a) || + !object_path_is_valid(b)) + return NULL; + + if (streq(b, "/")) + return (char*) a + 1; + + p = startswith(a, b); + if (!p) + return NULL; + + if (*p == 0) + return (char*) p; + + if (*p == '/') + return (char*) p + 1; + + return NULL; +} + +bool interface_name_is_valid(const char *p) { + const char *q; + bool dot, found_dot = false; + + if (isempty(p)) + return false; + + for (dot = true, q = p; *q; q++) + if (*q == '.') { + if (dot) + return false; + + found_dot = dot = true; + } else { + bool good; + + good = + (*q >= 'a' && *q <= 'z') || + (*q >= 'A' && *q <= 'Z') || + (!dot && *q >= '0' && *q <= '9') || + *q == '_'; + + if (!good) + return false; + + dot = false; + } + + if (q - p > 255) + return false; + + if (dot) + return false; + + if (!found_dot) + return false; + + return true; +} + +bool service_name_is_valid(const char *p) { + const char *q; + bool dot, found_dot = false, unique; + + if (isempty(p)) + return false; + + unique = p[0] == ':'; + + for (dot = true, q = unique ? p+1 : p; *q; q++) + if (*q == '.') { + if (dot) + return false; + + found_dot = dot = true; + } else { + bool good; + + good = + (*q >= 'a' && *q <= 'z') || + (*q >= 'A' && *q <= 'Z') || + ((!dot || unique) && *q >= '0' && *q <= '9') || + *q == '_' || *q == '-'; + + if (!good) + return false; + + dot = false; + } + + if (q - p > 255) + return false; + + if (dot) + return false; + + if (!found_dot) + return false; + + return true; +} + +char* service_name_startswith(const char *a, const char *b) { + const char *p; + + if (!service_name_is_valid(a) || + !service_name_is_valid(b)) + return NULL; + + p = startswith(a, b); + if (!p) + return NULL; + + if (*p == 0) + return (char*) p; + + if (*p == '.') + return (char*) p + 1; + + return NULL; +} + +bool member_name_is_valid(const char *p) { + const char *q; + + if (isempty(p)) + return false; + + for (q = p; *q; q++) { + bool good; + + good = + (*q >= 'a' && *q <= 'z') || + (*q >= 'A' && *q <= 'Z') || + (*q >= '0' && *q <= '9') || + *q == '_'; + + if (!good) + return false; + } + + if (q - p > 255) + return false; + + return true; +} + +/* + * Complex pattern match + * This checks whether @a is a 'complex-prefix' of @b, or @b is a + * 'complex-prefix' of @a, based on strings that consist of labels with @c as + * spearator. This function returns true if: + * - both strings are equal + * - either is a prefix of the other and ends with @c + * The second rule makes sure that either string needs to be fully included in + * the other, and the string which is considered the prefix needs to end with a + * separator. + */ +static bool complex_pattern_check(char c, const char *a, const char *b) { + bool separator = false; + + if (!a && !b) + return true; + + if (!a || !b) + return false; + + for (;;) { + if (*a != *b) + return (separator && (*a == 0 || *b == 0)); + + if (*a == 0) + return true; + + separator = *a == c; + + a++, b++; + } +} + +bool namespace_complex_pattern(const char *pattern, const char *value) { + return complex_pattern_check('.', pattern, value); +} + +bool path_complex_pattern(const char *pattern, const char *value) { + return complex_pattern_check('/', pattern, value); +} + +/* + * Simple pattern match + * This checks whether @a is a 'simple-prefix' of @b, based on strings that + * consist of labels with @c as separator. This function returns true, if: + * - if @a and @b are equal + * - if @a is a prefix of @b, and the first following character in @b (or the + * last character in @a) is @c + * The second rule basically makes sure that if @a is a prefix of @b, then @b + * must follow with a new label separated by @c. It cannot extend the label. + */ +static bool simple_pattern_check(char c, const char *a, const char *b) { + bool separator = false; + + if (!a && !b) + return true; + + if (!a || !b) + return false; + + for (;;) { + if (*a != *b) + return *a == 0 && (*b == c || separator); + + if (*a == 0) + return true; + + separator = *a == c; + + a++, b++; + } +} + +bool namespace_simple_pattern(const char *pattern, const char *value) { + return simple_pattern_check('.', pattern, value); +} + +bool path_simple_pattern(const char *pattern, const char *value) { + return simple_pattern_check('/', pattern, value); +} + +int bus_message_type_from_string(const char *s, uint8_t *u) { + if (streq(s, "signal")) + *u = SD_BUS_MESSAGE_SIGNAL; + else if (streq(s, "method_call")) + *u = SD_BUS_MESSAGE_METHOD_CALL; + else if (streq(s, "error")) + *u = SD_BUS_MESSAGE_METHOD_ERROR; + else if (streq(s, "method_return")) + *u = SD_BUS_MESSAGE_METHOD_RETURN; + else + return -EINVAL; + + return 0; +} + +const char *bus_message_type_to_string(uint8_t u) { + if (u == SD_BUS_MESSAGE_SIGNAL) + return "signal"; + else if (u == SD_BUS_MESSAGE_METHOD_CALL) + return "method_call"; + else if (u == SD_BUS_MESSAGE_METHOD_ERROR) + return "error"; + else if (u == SD_BUS_MESSAGE_METHOD_RETURN) + return "method_return"; + else + return NULL; +} + +char *bus_address_escape(const char *v) { + const char *a; + char *r, *b; + + r = new(char, strlen(v)*3+1); + if (!r) + return NULL; + + for (a = v, b = r; *a; a++) { + + if ((*a >= '0' && *a <= '9') || + (*a >= 'a' && *a <= 'z') || + (*a >= 'A' && *a <= 'Z') || + strchr("_-/.", *a)) + *(b++) = *a; + else { + *(b++) = '%'; + *(b++) = hexchar(*a >> 4); + *(b++) = hexchar(*a & 0xF); + } + } + + *b = 0; + return r; +} + +int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) { + assert(m); + + if (r < 0) { + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_reply_method_errno(m, r, error); + + } else if (sd_bus_error_is_set(error)) { + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_reply_method_error(m, error); + } else + return r; + + log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s", + bus_message_type_to_string(m->header->type), + strna(m->sender), + strna(m->path), + strna(m->interface), + strna(m->member), + strna(m->root_container.signature), + bus_error_message(error, r)); + + return 1; +} diff --git a/src/libsystemd/src/sd-bus/bus-internal.h b/src/libsystemd/src/sd-bus/bus-internal.h new file mode 100644 index 0000000000..e02df7981d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-internal.h @@ -0,0 +1,400 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/prioq.h" +#include "basic/refcnt.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "bus-error.h" +#include "bus-kernel.h" +#include "bus-match.h" +#include "kdbus.h" + +struct reply_callback { + sd_bus_message_handler_t callback; + usec_t timeout; + uint64_t cookie; + unsigned prioq_idx; +}; + +struct filter_callback { + sd_bus_message_handler_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct filter_callback, callbacks); +}; + +struct match_callback { + sd_bus_message_handler_t callback; + + uint64_t cookie; + unsigned last_iteration; + + char *match_string; + + struct bus_match_node *match_node; +}; + +struct node { + char *path; + struct node *parent; + LIST_HEAD(struct node, child); + LIST_FIELDS(struct node, siblings); + + LIST_HEAD(struct node_callback, callbacks); + LIST_HEAD(struct node_vtable, vtables); + LIST_HEAD(struct node_enumerator, enumerators); + LIST_HEAD(struct node_object_manager, object_managers); +}; + +struct node_callback { + struct node *node; + + bool is_fallback; + sd_bus_message_handler_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct node_callback, callbacks); +}; + +struct node_enumerator { + struct node *node; + + sd_bus_node_enumerator_t callback; + + unsigned last_iteration; + + LIST_FIELDS(struct node_enumerator, enumerators); +}; + +struct node_object_manager { + struct node *node; + + LIST_FIELDS(struct node_object_manager, object_managers); +}; + +struct node_vtable { + struct node *node; + + char *interface; + bool is_fallback; + const sd_bus_vtable *vtable; + sd_bus_object_find_t find; + + unsigned last_iteration; + + LIST_FIELDS(struct node_vtable, vtables); +}; + +struct vtable_member { + const char *path; + const char *interface; + const char *member; + struct node_vtable *parent; + unsigned last_iteration; + const sd_bus_vtable *vtable; +}; + +typedef enum BusSlotType { + BUS_REPLY_CALLBACK, + BUS_FILTER_CALLBACK, + BUS_MATCH_CALLBACK, + BUS_NODE_CALLBACK, + BUS_NODE_ENUMERATOR, + BUS_NODE_VTABLE, + BUS_NODE_OBJECT_MANAGER, + _BUS_SLOT_INVALID = -1, +} BusSlotType; + +struct sd_bus_slot { + unsigned n_ref; + sd_bus *bus; + void *userdata; + BusSlotType type:5; + bool floating:1; + bool match_added:1; + char *description; + + LIST_FIELDS(sd_bus_slot, slots); + + union { + struct reply_callback reply_callback; + struct filter_callback filter_callback; + struct match_callback match_callback; + struct node_callback node_callback; + struct node_enumerator node_enumerator; + struct node_object_manager node_object_manager; + struct node_vtable node_vtable; + }; +}; + +enum bus_state { + BUS_UNSET, + BUS_OPENING, + BUS_AUTHENTICATING, + BUS_HELLO, + BUS_RUNNING, + BUS_CLOSING, + BUS_CLOSED +}; + +static inline bool BUS_IS_OPEN(enum bus_state state) { + return state > BUS_UNSET && state < BUS_CLOSING; +} + +enum bus_auth { + _BUS_AUTH_INVALID, + BUS_AUTH_EXTERNAL, + BUS_AUTH_ANONYMOUS +}; + +struct sd_bus { + /* We use atomic ref counting here since sd_bus_message + objects retain references to their originating sd_bus but + we want to allow them to be processed in a different + thread. We won't provide full thread safety, but only the + bare minimum that makes it possible to use sd_bus and + sd_bus_message objects independently and on different + threads as long as each object is used only once at the + same time. */ + RefCount n_ref; + + enum bus_state state; + int input_fd, output_fd; + int message_version; + int message_endian; + + bool is_kernel:1; + bool can_fds:1; + bool bus_client:1; + bool ucred_valid:1; + bool is_server:1; + bool anonymous_auth:1; + bool prefer_readv:1; + bool prefer_writev:1; + bool match_callbacks_modified:1; + bool filter_callbacks_modified:1; + bool nodes_modified:1; + bool trusted:1; + bool fake_creds_valid:1; + bool fake_pids_valid:1; + bool manual_peer_interface:1; + bool is_system:1; + bool is_user:1; + bool allow_interactive_authorization:1; + + int use_memfd; + + void *rbuffer; + size_t rbuffer_size; + + sd_bus_message **rqueue; + unsigned rqueue_size; + size_t rqueue_allocated; + + sd_bus_message **wqueue; + unsigned wqueue_size; + size_t windex; + size_t wqueue_allocated; + + uint64_t cookie; + + char *unique_name; + uint64_t unique_id; + + struct bus_match_node match_callbacks; + Prioq *reply_callbacks_prioq; + OrderedHashmap *reply_callbacks; + LIST_HEAD(struct filter_callback, filter_callbacks); + + Hashmap *nodes; + Hashmap *vtable_methods; + Hashmap *vtable_properties; + + union sockaddr_union sockaddr; + socklen_t sockaddr_size; + + char *kernel; + char *machine; + pid_t nspid; + + sd_id128_t server_id; + + char *address; + unsigned address_index; + + int last_connect_error; + + enum bus_auth auth; + size_t auth_rbegin; + struct iovec auth_iovec[3]; + unsigned auth_index; + char *auth_buffer; + usec_t auth_timeout; + + struct ucred ucred; + char *label; + + uint64_t creds_mask; + + int *fds; + unsigned n_fds; + + char *exec_path; + char **exec_argv; + + unsigned iteration_counter; + + void *kdbus_buffer; + + /* We do locking around the memfd cache, since we want to + * allow people to process a sd_bus_message in a different + * thread then it was generated on and free it there. Since + * adding something to the memfd cache might happen when a + * message is released, we hence need to protect this bit with + * a mutex. */ + pthread_mutex_t memfd_cache_mutex; + struct memfd_cache memfd_cache[MEMFD_CACHE_MAX]; + unsigned n_memfd_cache; + + pid_t original_pid; + + uint64_t hello_flags; + uint64_t attach_flags; + + uint64_t match_cookie; + + sd_event_source *input_io_event_source; + sd_event_source *output_io_event_source; + sd_event_source *time_event_source; + sd_event_source *quit_event_source; + sd_event *event; + int event_priority; + + sd_bus_message *current_message; + sd_bus_slot *current_slot; + sd_bus_message_handler_t current_handler; + void *current_userdata; + + sd_bus **default_bus_ptr; + pid_t tid; + + struct kdbus_creds fake_creds; + struct kdbus_pids fake_pids; + char *fake_label; + + char *cgroup_root; + + char *description; + + size_t bloom_size; + unsigned bloom_n_hash; + + sd_bus_track *track_queue; + + LIST_HEAD(sd_bus_slot, slots); +}; + +#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) + +#define BUS_WQUEUE_MAX 1024 +#define BUS_RQUEUE_MAX 64*1024 + +#define BUS_MESSAGE_SIZE_MAX (64*1024*1024) +#define BUS_AUTH_SIZE_MAX (64*1024) + +#define BUS_CONTAINER_DEPTH 128 + +/* Defined by the specification as maximum size of an array in + * bytes */ +#define BUS_ARRAY_MAX_SIZE 67108864 + +#define BUS_FDS_MAX 1024 + +#define BUS_EXEC_ARGV_MAX 256 + +bool interface_name_is_valid(const char *p) _pure_; +bool service_name_is_valid(const char *p) _pure_; +char* service_name_startswith(const char *a, const char *b); +bool member_name_is_valid(const char *p) _pure_; +bool object_path_is_valid(const char *p) _pure_; +char *object_path_startswith(const char *a, const char *b) _pure_; + +bool namespace_complex_pattern(const char *pattern, const char *value) _pure_; +bool path_complex_pattern(const char *pattern, const char *value) _pure_; + +bool namespace_simple_pattern(const char *pattern, const char *value) _pure_; +bool path_simple_pattern(const char *pattern, const char *value) _pure_; + +int bus_message_type_from_string(const char *s, uint8_t *u) _pure_; +const char *bus_message_type_to_string(uint8_t u) _pure_; + +#define error_name_is_valid interface_name_is_valid + +int bus_ensure_running(sd_bus *bus); +int bus_start_running(sd_bus *bus); +int bus_next_address(sd_bus *bus); + +int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m); + +int bus_rqueue_make_room(sd_bus *bus); + +bool bus_pid_changed(sd_bus *bus); + +char *bus_address_escape(const char *v); + +#define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \ + for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \ + _slash && !(_slash[(_slash) == (prefix)] = 0); \ + _slash = streq((prefix), "/") ? NULL : strrchr((prefix), '/')) + +/* If we are invoking callbacks of a bus object, ensure unreffing the + * bus from the callback doesn't destroy the object we are working + * on */ +#define BUS_DONT_DESTROY(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); +int bus_set_address_system_remote(sd_bus *b, const char *host); +int bus_set_address_system_machine(sd_bus *b, const char *machine); + +int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); + +int bus_get_root_path(sd_bus *bus); + +int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); + +#define bus_assert_return(expr, r, error) \ + do { \ + if (!assert_log(expr, #expr)) \ + return sd_bus_error_set_errno(error, r); \ + } while (false) diff --git a/src/libsystemd/src/sd-bus/bus-introspect.c b/src/libsystemd/src/sd-bus/bus-introspect.c new file mode 100644 index 0000000000..835b5e3909 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-introspect.c @@ -0,0 +1,213 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "bus-internal.h" +#include "bus-introspect.h" +#include "bus-protocol.h" +#include "bus-signature.h" + +int introspect_begin(struct introspect *i, bool trusted) { + assert(i); + + zero(*i); + i->trusted = trusted; + + i->f = open_memstream(&i->introspection, &i->size); + if (!i->f) + return -ENOMEM; + + fputs(BUS_INTROSPECT_DOCTYPE + "\n", i->f); + + return 0; +} + +int introspect_write_default_interfaces(struct introspect *i, bool object_manager) { + assert(i); + + fputs(BUS_INTROSPECT_INTERFACE_PEER + BUS_INTROSPECT_INTERFACE_INTROSPECTABLE + BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f); + + if (object_manager) + fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f); + + return 0; +} + +int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { + char *node; + + assert(i); + assert(prefix); + + while ((node = set_steal_first(s))) { + const char *e; + + e = object_path_startswith(node, prefix); + if (e && e[0]) + fprintf(i->f, " \n", e); + + free(node); + } + + return 0; +} + +static void introspect_write_flags(struct introspect *i, int type, int flags) { + if (flags & SD_BUS_VTABLE_DEPRECATED) + fputs(" \n", i->f); + + if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) + fputs(" \n", i->f); + + if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) { + if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) + fputs(" \n", i->f); + + if (flags & SD_BUS_VTABLE_PROPERTY_CONST) + fputs(" \n", i->f); + else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) + fputs(" \n", i->f); + else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) + fputs(" \n", i->f); + } + + if (!i->trusted && + (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) && + !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) + fputs(" \n", i->f); +} + +static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) { + int r; + + for (;;) { + size_t l; + + if (!*signature) + return 0; + + r = signature_element_length(signature, &l); + if (r < 0) + return r; + + fprintf(i->f, " f, " direction=\"%s\"/>\n", direction); + else + fputs("/>\n", i->f); + + signature += l; + } +} + +int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { + assert(i); + assert(v); + + for (; v->type != _SD_BUS_VTABLE_END; v++) { + + /* Ignore methods, signals and properties that are + * marked "hidden", but do show the interface + * itself */ + + if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) + continue; + + switch (v->type) { + + case _SD_BUS_VTABLE_START: + if (v->flags & SD_BUS_VTABLE_DEPRECATED) + fputs(" \n", i->f); + break; + + case _SD_BUS_VTABLE_METHOD: + fprintf(i->f, " \n", v->x.method.member); + introspect_write_arguments(i, strempty(v->x.method.signature), "in"); + introspect_write_arguments(i, strempty(v->x.method.result), "out"); + introspect_write_flags(i, v->type, v->flags); + fputs(" \n", i->f); + break; + + case _SD_BUS_VTABLE_PROPERTY: + case _SD_BUS_VTABLE_WRITABLE_PROPERTY: + fprintf(i->f, " \n", + v->x.property.member, + v->x.property.signature, + v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); + introspect_write_flags(i, v->type, v->flags); + fputs(" \n", i->f); + break; + + case _SD_BUS_VTABLE_SIGNAL: + fprintf(i->f, " \n", v->x.signal.member); + introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); + introspect_write_flags(i, v->type, v->flags); + fputs(" \n", i->f); + break; + } + + } + + return 0; +} + +int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) { + sd_bus_message *q; + int r; + + assert(i); + assert(m); + assert(reply); + + fputs("\n", i->f); + + r = fflush_and_check(i->f); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(m, &q); + if (r < 0) + return r; + + r = sd_bus_message_append(q, "s", i->introspection); + if (r < 0) { + sd_bus_message_unref(q); + return r; + } + + *reply = q; + return 0; +} + +void introspect_free(struct introspect *i) { + assert(i); + + safe_fclose(i->f); + + free(i->introspection); + zero(*i); +} diff --git a/src/libsystemd/src/sd-bus/bus-introspect.h b/src/libsystemd/src/sd-bus/bus-introspect.h new file mode 100644 index 0000000000..af4ae10682 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-introspect.h @@ -0,0 +1,40 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/set.h" + +struct introspect { + FILE *f; + char *introspection; + size_t size; + bool trusted; +}; + +int introspect_begin(struct introspect *i, bool trusted); +int introspect_write_default_interfaces(struct introspect *i, bool object_manager); +int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix); +int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v); +int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply); +void introspect_free(struct introspect *i); diff --git a/src/libsystemd/src/sd-bus/bus-kernel.c b/src/libsystemd/src/sd-bus/bus-kernel.c new file mode 100644 index 0000000000..9d767340b6 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-kernel.c @@ -0,0 +1,1783 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_VALGRIND_MEMCHECK_H +#include +#endif + +#include +#include +#include +#include + +/* 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 +#undef basename + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/capability-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/memfd-util.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-bloom.h" +#include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-message.h" + +#define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t)) + +int bus_kernel_parse_unique_name(const char *s, uint64_t *id) { + int r; + + assert(s); + assert(id); + + if (!startswith(s, ":1.")) + return 0; + + r = safe_atou64(s + 3, id); + if (r < 0) + return r; + + return 1; +} + +static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) { + assert(d); + assert(sz > 0); + + *d = ALIGN8_PTR(*d); + + /* Note that p can be NULL, which encodes a region full of + * zeroes, which is useful to optimize certain padding + * conditions */ + + (*d)->size = offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec); + (*d)->type = KDBUS_ITEM_PAYLOAD_VEC; + (*d)->vec.address = PTR_TO_UINT64(p); + (*d)->vec.size = sz; + + *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); +} + +static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t start, size_t sz) { + assert(d); + assert(memfd >= 0); + assert(sz > 0); + + *d = ALIGN8_PTR(*d); + (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd); + (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD; + (*d)->memfd.fd = memfd; + (*d)->memfd.start = start; + (*d)->memfd.size = sz; + + *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); +} + +static void append_destination(struct kdbus_item **d, const char *s, size_t length) { + assert(d); + assert(s); + + *d = ALIGN8_PTR(*d); + + (*d)->size = offsetof(struct kdbus_item, str) + length + 1; + (*d)->type = KDBUS_ITEM_DST_NAME; + memcpy((*d)->str, s, length + 1); + + *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); +} + +static struct kdbus_bloom_filter *append_bloom(struct kdbus_item **d, size_t length) { + struct kdbus_item *i; + + assert(d); + + i = ALIGN8_PTR(*d); + + i->size = offsetof(struct kdbus_item, bloom_filter) + + offsetof(struct kdbus_bloom_filter, data) + + length; + i->type = KDBUS_ITEM_BLOOM_FILTER; + + *d = (struct kdbus_item *) ((uint8_t*) i + i->size); + + return &i->bloom_filter; +} + +static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) { + assert(d); + assert(fds); + assert(n_fds > 0); + + *d = ALIGN8_PTR(*d); + (*d)->size = offsetof(struct kdbus_item, fds) + sizeof(int) * n_fds; + (*d)->type = KDBUS_ITEM_FDS; + memcpy((*d)->fds, fds, sizeof(int) * n_fds); + + *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); +} + +static void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + e = stpcpy(buf, "arg"); + if (i < 10) + *(e++) = '0' + (char) i; + else { + *(e++) = '0' + (char) (i / 10); + *(e++) = '0' + (char) (i % 10); + } + + *e = 0; + bloom_add_pair(data, size, n_hash, buf, t); + + strcpy(e, "-dot-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '.'); + strcpy(e, "-slash-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '/'); +} + +static void add_bloom_arg_has(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("-has")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + e = stpcpy(buf, "arg"); + if (i < 10) + *(e++) = '0' + (char) i; + else { + *(e++) = '0' + (char) (i / 10); + *(e++) = '0' + (char) (i % 10); + } + + strcpy(e, "-has"); + bloom_add_pair(data, size, n_hash, buf, t); +} + +static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { + void *data; + unsigned i; + int r; + + assert(m); + assert(bloom); + + data = bloom->data; + memzero(data, m->bus->bloom_size); + bloom->generation = 0; + + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "message-type", bus_message_type_to_string(m->header->type)); + + if (m->interface) + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "interface", m->interface); + if (m->member) + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "member", m->member); + if (m->path) { + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path", m->path); + bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path); + bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, "path-slash-prefix", m->path, '/'); + } + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + for (i = 0; i < 64; i++) { + const char *t, *contents; + char type; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { + + /* The bloom filter includes simple strings of any kind */ + r = sd_bus_message_read_basic(m, type, &t); + if (r < 0) + return r; + + add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + } + + if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) { + + /* As well as array of simple strings of any kinds */ + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) + add_bloom_arg_has(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + } else + /* Stop adding to bloom filter as soon as we + * run into the first argument we cannot add + * to it. */ + break; + } + + return 0; +} + +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 = false; + uint64_t dst_id; + size_t sz, dl; + unsigned i; + int r; + + assert(b); + assert(m); + assert(m->sealed); + + /* We put this together only once, if this message is reused + * we reuse the earlier-built version */ + if (m->kdbus) + return 0; + + destination = m->destination ?: m->destination_ptr; + + if (destination) { + r = bus_kernel_parse_unique_name(destination, &dst_id); + if (r < 0) + return r; + 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 + dst_id = KDBUS_DST_ID_BROADCAST; + + sz = offsetof(struct kdbus_msg, items); + + /* Add in fixed header, fields header and payload */ + sz += (1 + m->n_body_parts) * ALIGN8(offsetof(struct kdbus_item, vec) + + MAX(sizeof(struct kdbus_vec), + sizeof(struct kdbus_memfd))); + + /* Add space for bloom filter */ + sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) + + offsetof(struct kdbus_bloom_filter, data) + + m->bus->bloom_size); + + /* Add in well-known destination header */ + if (well_known) { + dl = strlen(destination); + sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1); + } + + /* Add space for unix fds */ + if (m->n_fds > 0) + sz += ALIGN8(offsetof(struct kdbus_item, fds) + sizeof(int)*m->n_fds); + + m->kdbus = memalign(8, sz); + if (!m->kdbus) { + r = -ENOMEM; + goto fail; + } + + m->free_kdbus = true; + memzero(m->kdbus, sz); + + m->kdbus->flags = + ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) | + ((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); + + 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; + + if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + m->kdbus->cookie_reply = m->reply_cookie; + else { + struct timespec now; + + assert_se(clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == 0); + m->kdbus->timeout_ns = now.tv_sec * NSEC_PER_SEC + now.tv_nsec + + m->timeout * NSEC_PER_USEC; + } + + d = m->kdbus->items; + + if (well_known) + append_destination(&d, destination, dl); + + append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m)); + + MESSAGE_FOREACH_PART(part, i, m) { + if (part->is_zero) { + /* If this is padding then simply send a + * vector with a NULL data pointer which the + * kernel will just pass through. This is the + * most efficient way to encode zeroes */ + + append_payload_vec(&d, NULL, part->size); + continue; + } + + if (part->memfd >= 0 && part->sealed && destination) { + /* Try to send a memfd, if the part is + * sealed and this is not a broadcast. Since we can only */ + + append_payload_memfd(&d, part->memfd, part->memfd_offset, part->size); + continue; + } + + /* Otherwise, let's send a vector to the actual data. + * For that, we need to map it first. */ + r = bus_body_part_map(part); + if (r < 0) + goto fail; + + append_payload_vec(&d, part->data, part->size); + } + + if (m->header->type == SD_BUS_MESSAGE_SIGNAL) { + struct kdbus_bloom_filter *bloom; + + bloom = append_bloom(&d, m->bus->bloom_size); + r = bus_message_setup_bloom(m, bloom); + if (r < 0) + goto fail; + } + + if (m->n_fds > 0) + append_fds(&d, m->fds, m->n_fds); + + m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus; + assert(m->kdbus->size <= sz); + + return 0; + +fail: + m->poisoned = true; + return r; +} + +static void unset_memfds(struct sd_bus_message *m) { + struct bus_body_part *part; + unsigned i; + + assert(m); + + /* Make sure the memfds are not freed twice */ + MESSAGE_FOREACH_PART(part, i, m) + if (part->memfd >= 0) + part->memfd = -1; +} + +static void message_set_timestamp(sd_bus *bus, sd_bus_message *m, const struct kdbus_timestamp *ts) { + assert(bus); + assert(m); + + if (!ts) + return; + + if (!(bus->attach_flags & KDBUS_ATTACH_TIMESTAMP)) + return; + + m->realtime = ts->realtime_ns / NSEC_PER_USEC; + m->monotonic = ts->monotonic_ns / NSEC_PER_USEC; + m->seqnum = ts->seqnum; +} + +static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { + sd_bus_message *m = NULL; + struct kdbus_item *d; + unsigned n_fds = 0; + _cleanup_free_ int *fds = NULL; + struct bus_header *header = NULL; + void *footer = NULL; + size_t header_size = 0, footer_size = 0; + size_t n_bytes = 0, idx = 0; + const char *destination = NULL, *seclabel = NULL; + bool last_was_memfd = false; + int r; + + assert(bus); + assert(k); + assert(k->payload_type == KDBUS_PAYLOAD_DBUS); + + KDBUS_ITEM_FOREACH(d, k, items) { + size_t l; + + l = d->size - offsetof(struct kdbus_item, data); + + switch (d->type) { + + case KDBUS_ITEM_PAYLOAD_OFF: + if (!header) { + header = (struct bus_header*)((uint8_t*) k + d->vec.offset); + header_size = d->vec.size; + } + + footer = (uint8_t*) k + d->vec.offset; + footer_size = d->vec.size; + + n_bytes += d->vec.size; + last_was_memfd = false; + break; + + case KDBUS_ITEM_PAYLOAD_MEMFD: + if (!header) /* memfd cannot be first part */ + return -EBADMSG; + + n_bytes += d->memfd.size; + last_was_memfd = true; + break; + + case KDBUS_ITEM_FDS: { + int *f; + unsigned j; + + j = l / sizeof(int); + f = realloc(fds, sizeof(int) * (n_fds + j)); + if (!f) + return -ENOMEM; + + fds = f; + memcpy(fds + n_fds, d->fds, sizeof(int) * j); + n_fds += j; + break; + } + + case KDBUS_ITEM_SECLABEL: + seclabel = d->str; + break; + } + } + + if (last_was_memfd) /* memfd cannot be last part */ + return -EBADMSG; + + if (!header) + return -EBADMSG; + + if (header_size < sizeof(struct bus_header)) + return -EBADMSG; + + /* on kdbus we only speak native endian gvariant, never dbus1 + * marshalling or reverse endian */ + if (header->version != 2 || + header->endian != BUS_NATIVE_ENDIAN) + return -EPROTOTYPE; + + r = bus_message_from_header( + bus, + header, header_size, + footer, footer_size, + n_bytes, + fds, n_fds, + seclabel, 0, &m); + if (r < 0) + return r; + + /* The well-known names list is different from the other + credentials. If we asked for it, but nothing is there, this + means that the list of well-known names is simply empty, not + that we lack any data */ + + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; + + KDBUS_ITEM_FOREACH(d, k, items) { + size_t l; + + l = d->size - offsetof(struct kdbus_item, data); + + switch (d->type) { + + case KDBUS_ITEM_PAYLOAD_OFF: { + size_t begin_body; + + begin_body = BUS_MESSAGE_BODY_BEGIN(m); + + if (idx + d->vec.size > begin_body) { + struct bus_body_part *part; + + /* Contains body material */ + + part = message_append_part(m); + if (!part) { + r = -ENOMEM; + goto fail; + } + + /* A -1 offset is NUL padding. */ + part->is_zero = d->vec.offset == ~0ULL; + + if (idx >= begin_body) { + if (!part->is_zero) + part->data = (uint8_t* )k + d->vec.offset; + part->size = d->vec.size; + } else { + if (!part->is_zero) + part->data = (uint8_t*) k + d->vec.offset + (begin_body - idx); + part->size = d->vec.size - (begin_body - idx); + } + + part->sealed = true; + } + + idx += d->vec.size; + break; + } + + case KDBUS_ITEM_PAYLOAD_MEMFD: { + struct bus_body_part *part; + + if (idx < BUS_MESSAGE_BODY_BEGIN(m)) { + r = -EBADMSG; + goto fail; + } + + part = message_append_part(m); + if (!part) { + r = -ENOMEM; + goto fail; + } + + part->memfd = d->memfd.fd; + part->memfd_offset = d->memfd.start; + part->size = d->memfd.size; + part->sealed = true; + + idx += d->memfd.size; + break; + } + + case KDBUS_ITEM_PIDS: + + /* The PID/TID might be missing, when the data + * is faked by a bus proxy and it lacks that + * information about the real client (since + * SO_PEERCRED is used for that). Also kernel + * namespacing might make some of this data + * unavailable when untranslatable. */ + + if (d->pids.pid > 0) { + m->creds.pid = (pid_t) d->pids.pid; + m->creds.mask |= SD_BUS_CREDS_PID & bus->creds_mask; + } + + if (d->pids.tid > 0) { + m->creds.tid = (pid_t) d->pids.tid; + m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; + } + + if (d->pids.ppid > 0) { + m->creds.ppid = (pid_t) d->pids.ppid; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } else if (d->pids.pid == 1) { + m->creds.ppid = 0; + m->creds.mask |= SD_BUS_CREDS_PPID & bus->creds_mask; + } + + break; + + case KDBUS_ITEM_CREDS: + + /* EUID/SUID/FSUID/EGID/SGID/FSGID might be + * missing too (see above). */ + + if ((uid_t) d->creds.uid != UID_INVALID) { + m->creds.uid = (uid_t) d->creds.uid; + m->creds.mask |= SD_BUS_CREDS_UID & bus->creds_mask; + } + + if ((uid_t) d->creds.euid != UID_INVALID) { + m->creds.euid = (uid_t) d->creds.euid; + m->creds.mask |= SD_BUS_CREDS_EUID & bus->creds_mask; + } + + if ((uid_t) d->creds.suid != UID_INVALID) { + m->creds.suid = (uid_t) d->creds.suid; + m->creds.mask |= SD_BUS_CREDS_SUID & bus->creds_mask; + } + + if ((uid_t) d->creds.fsuid != UID_INVALID) { + m->creds.fsuid = (uid_t) d->creds.fsuid; + m->creds.mask |= SD_BUS_CREDS_FSUID & bus->creds_mask; + } + + if ((gid_t) d->creds.gid != GID_INVALID) { + m->creds.gid = (gid_t) d->creds.gid; + m->creds.mask |= SD_BUS_CREDS_GID & bus->creds_mask; + } + + if ((gid_t) d->creds.egid != GID_INVALID) { + m->creds.egid = (gid_t) d->creds.egid; + m->creds.mask |= SD_BUS_CREDS_EGID & bus->creds_mask; + } + + if ((gid_t) d->creds.sgid != GID_INVALID) { + m->creds.sgid = (gid_t) d->creds.sgid; + m->creds.mask |= SD_BUS_CREDS_SGID & bus->creds_mask; + } + + if ((gid_t) d->creds.fsgid != GID_INVALID) { + m->creds.fsgid = (gid_t) d->creds.fsgid; + m->creds.mask |= SD_BUS_CREDS_FSGID & bus->creds_mask; + } + + break; + + case KDBUS_ITEM_TIMESTAMP: + message_set_timestamp(bus, m, &d->timestamp); + break; + + case KDBUS_ITEM_PID_COMM: + m->creds.comm = d->str; + m->creds.mask |= SD_BUS_CREDS_COMM & bus->creds_mask; + break; + + case KDBUS_ITEM_TID_COMM: + m->creds.tid_comm = d->str; + m->creds.mask |= SD_BUS_CREDS_TID_COMM & bus->creds_mask; + break; + + case KDBUS_ITEM_EXE: + m->creds.exe = d->str; + m->creds.mask |= SD_BUS_CREDS_EXE & bus->creds_mask; + break; + + case KDBUS_ITEM_CMDLINE: + m->creds.cmdline = d->str; + m->creds.cmdline_size = l; + m->creds.mask |= SD_BUS_CREDS_CMDLINE & bus->creds_mask; + break; + + case KDBUS_ITEM_CGROUP: + m->creds.cgroup = d->str; + m->creds.mask |= (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID) & bus->creds_mask; + + r = bus_get_root_path(bus); + if (r < 0) + goto fail; + + m->creds.cgroup_root = bus->cgroup_root; + break; + + case KDBUS_ITEM_AUDIT: + m->creds.audit_session_id = (uint32_t) d->audit.sessionid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; + + m->creds.audit_login_uid = (uid_t) d->audit.loginuid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; + break; + + case KDBUS_ITEM_CAPS: + if (d->caps.last_cap != cap_last_cap() || + d->size - offsetof(struct kdbus_item, caps.caps) < DIV_ROUND_UP(d->caps.last_cap, 32U) * 4 * 4) { + r = -EBADMSG; + goto fail; + } + + m->creds.capability = d->caps.caps; + m->creds.mask |= (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS) & bus->creds_mask; + break; + + case KDBUS_ITEM_DST_NAME: + if (!service_name_is_valid(d->str)) { + r = -EBADMSG; + goto fail; + } + + destination = d->str; + break; + + case KDBUS_ITEM_OWNED_NAME: + if (!service_name_is_valid(d->name.name)) { + r = -EBADMSG; + goto fail; + } + + if (bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + char **wkn; + size_t n; + + /* We just extend the array here, but + * do not allocate the strings inside + * of it, instead we just point to our + * buffer directly. */ + n = strv_length(m->creds.well_known_names); + wkn = realloc(m->creds.well_known_names, (n + 2) * sizeof(char*)); + if (!wkn) { + r = -ENOMEM; + goto fail; + } + + wkn[n] = d->name.name; + wkn[n+1] = NULL; + m->creds.well_known_names = wkn; + + m->creds.mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + break; + + case KDBUS_ITEM_CONN_DESCRIPTION: + m->creds.description = d->str; + m->creds.mask |= SD_BUS_CREDS_DESCRIPTION & bus->creds_mask; + break; + + case KDBUS_ITEM_AUXGROUPS: + + if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + size_t i, n; + gid_t *g; + + n = (d->size - offsetof(struct kdbus_item, data64)) / sizeof(uint64_t); + g = new(gid_t, n); + if (!g) { + r = -ENOMEM; + goto fail; + } + + for (i = 0; i < n; i++) + g[i] = d->data64[i]; + + m->creds.supplementary_gids = g; + m->creds.n_supplementary_gids = n; + m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + + break; + + case KDBUS_ITEM_FDS: + case KDBUS_ITEM_SECLABEL: + case KDBUS_ITEM_BLOOM_FILTER: + break; + + default: + log_debug("Got unknown field from kernel %llu", d->type); + } + } + + /* If we requested the list of well-known names to be appended + * and the sender had none no item for it will be + * attached. However, this does *not* mean that the kernel + * didn't want to provide this information to us. Hence, let's + * explicitly mark this information as available if it was + * requested. */ + m->creds.mask |= bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + + r = bus_message_parse_fields(m); + if (r < 0) + goto fail; + + /* Refuse messages if kdbus and dbus1 cookie doesn't match up */ + if ((uint64_t) m->header->dbus2.cookie != k->cookie) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the reply flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) != !!(k->flags & KDBUS_MSG_EXPECT_REPLY)) { + r = -EBADMSG; + goto fail; + } + + /* Refuse reply messages where the reply cookie doesn't match up */ + if ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) && m->reply_cookie != k->cookie_reply) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the autostart flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_AUTO_START) != !(k->flags & KDBUS_MSG_NO_AUTO_START)) { + r = -EBADMSG; + goto fail; + } + + /* Override information from the user header with data from the kernel */ + if (k->src_id == KDBUS_SRC_ID_KERNEL) + bus_message_set_sender_driver(bus, m); + else { + xsprintf(m->sender_buffer, ":1.%llu", + (unsigned long long)k->src_id); + m->sender = m->creds.unique_name = m->sender_buffer; + } + + if (destination) + m->destination = destination; + else if (k->dst_id == KDBUS_DST_ID_BROADCAST) + m->destination = NULL; + 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 { + xsprintf(m->destination_buffer, ":1.%llu", + (unsigned long long)k->dst_id); + m->destination = m->destination_buffer; + } + + /* We take possession of the kmsg struct now */ + m->kdbus = k; + m->release_kdbus = true; + m->free_fds = true; + fds = NULL; + + bus->rqueue[bus->rqueue_size++] = m; + + return 1; + +fail: + unset_memfds(m); + sd_bus_message_unref(m); + + return r; +} + +int bus_kernel_take_fd(sd_bus *b) { + struct kdbus_bloom_parameter *bloom = NULL; + struct kdbus_item *items, *item; + struct kdbus_cmd_hello *hello; + _cleanup_free_ char *g = NULL; + const char *name; + size_t l = 0, m = 0, sz; + int r; + + assert(b); + + if (b->is_server) + return -EINVAL; + + b->use_memfd = 1; + + if (b->description) { + g = bus_label_escape(b->description); + if (!g) + return -ENOMEM; + + name = g; + } else { + char pr[17] = {}; + + /* If no name is explicitly set, we'll include a hint + * indicating the library implementation, a hint which + * kind of bus this is and the thread name */ + + assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); + + if (isempty(pr)) { + name = b->is_system ? "sd-system" : + b->is_user ? "sd-user" : "sd"; + } else { + _cleanup_free_ char *e = NULL; + + e = bus_label_escape(pr); + if (!e) + return -ENOMEM; + + g = strappend(b->is_system ? "sd-system-" : + b->is_user ? "sd-user-" : "sd-", + e); + if (!g) + return -ENOMEM; + + name = g; + } + + b->description = bus_label_unescape(name); + if (!b->description) + return -ENOMEM; + } + + m = strlen(name); + + sz = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + + ALIGN8(offsetof(struct kdbus_item, str) + m + 1); + + if (b->fake_creds_valid) + sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds)); + + if (b->fake_pids_valid) + sz += ALIGN8(offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids)); + + if (b->fake_label) { + l = strlen(b->fake_label); + sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); + } + + hello = alloca0_align(sz, 8); + hello->size = sz; + hello->flags = b->hello_flags; + hello->attach_flags_send = _KDBUS_ATTACH_ANY; + hello->attach_flags_recv = b->attach_flags; + hello->pool_size = KDBUS_POOL_SIZE; + + item = hello->items; + + item->size = offsetof(struct kdbus_item, str) + m + 1; + item->type = KDBUS_ITEM_CONN_DESCRIPTION; + memcpy(item->str, name, m + 1); + item = KDBUS_ITEM_NEXT(item); + + if (b->fake_creds_valid) { + item->size = offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds); + item->type = KDBUS_ITEM_CREDS; + item->creds = b->fake_creds; + + item = KDBUS_ITEM_NEXT(item); + } + + if (b->fake_pids_valid) { + item->size = offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids); + item->type = KDBUS_ITEM_PIDS; + item->pids = b->fake_pids; + + item = KDBUS_ITEM_NEXT(item); + } + + if (b->fake_label) { + item->size = offsetof(struct kdbus_item, str) + l + 1; + item->type = KDBUS_ITEM_SECLABEL; + memcpy(item->str, b->fake_label, l+1); + } + + r = ioctl(b->input_fd, KDBUS_CMD_HELLO, hello); + if (r < 0) { + if (errno == ENOTTY) + /* If the ioctl is not supported we assume that the + * API version changed in a major incompatible way, + * let's indicate an API incompatibility in this + * case. */ + return -ESOCKTNOSUPPORT; + + return -errno; + } + + if (!b->kdbus_buffer) { + b->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, b->input_fd, 0); + if (b->kdbus_buffer == MAP_FAILED) { + b->kdbus_buffer = NULL; + r = -errno; + goto fail; + } + } + + /* The higher 32bit of the bus_flags fields are considered + * 'incompatible flags'. Refuse them all for now. */ + if (hello->bus_flags > 0xFFFFFFFFULL) { + r = -ESOCKTNOSUPPORT; + goto fail; + } + + /* extract bloom parameters from items */ + items = (void*)((uint8_t*)b->kdbus_buffer + hello->offset); + KDBUS_FOREACH(item, items, hello->items_size) { + switch (item->type) { + case KDBUS_ITEM_BLOOM_PARAMETER: + bloom = &item->bloom_parameter; + break; + } + } + + if (!bloom || !bloom_validate_parameters((size_t) bloom->size, (unsigned) bloom->n_hash)) { + r = -EOPNOTSUPP; + goto fail; + } + + b->bloom_size = (size_t) bloom->size; + b->bloom_n_hash = (unsigned) bloom->n_hash; + + if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello->id) < 0) { + r = -ENOMEM; + goto fail; + } + + b->unique_id = hello->id; + + b->is_kernel = true; + b->bus_client = true; + b->can_fds = !!(hello->flags & KDBUS_HELLO_ACCEPT_FD); + b->message_version = 2; + b->message_endian = BUS_NATIVE_ENDIAN; + + /* the kernel told us the UUID of the underlying bus */ + memcpy(b->server_id.bytes, hello->id128, sizeof(b->server_id.bytes)); + + /* free returned items */ + (void) bus_kernel_cmd_free(b, hello->offset); + return bus_start_running(b); + +fail: + (void) bus_kernel_cmd_free(b, hello->offset); + return r; +} + +int bus_kernel_connect(sd_bus *b) { + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->kernel); + + if (b->is_server) + return -EINVAL; + + b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (b->input_fd < 0) + return -errno; + + b->output_fd = b->input_fd; + + return bus_kernel_take_fd(b); +} + +int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset) { + struct kdbus_cmd_free cmd = { + .size = sizeof(cmd), + .offset = offset, + }; + int r; + + assert(bus); + assert(bus->is_kernel); + + r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); + if (r < 0) + return -errno; + + return 0; +} + +static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { + struct kdbus_item *d; + + assert(bus); + assert(k); + + KDBUS_ITEM_FOREACH(d, k, items) { + if (d->type == KDBUS_ITEM_FDS) + close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); + else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) + safe_close(d->memfd.fd); + } + + bus_kernel_cmd_free(bus, (uint8_t*) k - (uint8_t*) bus->kdbus_buffer); +} + +int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) { + struct kdbus_cmd_send cmd = { }; + int r; + + assert(bus); + assert(m); + assert(bus->state == BUS_RUNNING); + + /* If we can't deliver, we want room for the error message */ + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + r = bus_message_setup_kmsg(bus, m); + if (r < 0) + return r; + + cmd.size = sizeof(cmd); + cmd.msg_address = (uintptr_t)m->kdbus; + + /* If this is a synchronous method call, then let's tell the + * kernel, so that it can pass CPU time/scheduling to the + * destination for the time, if it wants to. If we + * synchronously wait for the result anyway, we won't need CPU + * anyway. */ + if (hint_sync_call) { + m->kdbus->flags |= KDBUS_MSG_EXPECT_REPLY; + cmd.flags |= KDBUS_SEND_SYNC_REPLY; + } + + r = ioctl(bus->output_fd, KDBUS_CMD_SEND, &cmd); + if (r < 0) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply; + + if (errno == EAGAIN || errno == EINTR) + return 0; + else if (errno == ENXIO || errno == ESRCH) { + + /* ENXIO: unique name not known + * ESRCH: well-known name not known */ + + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Destination %s not known", m->destination); + else { + log_debug("Could not deliver message to %s as destination is not known. Ignoring.", m->destination); + return 0; + } + + } else if (errno == EADDRNOTAVAIL) { + + /* EADDRNOTAVAIL: activation is possible, but turned off in request flags */ + + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + sd_bus_error_setf(&error, SD_BUS_ERROR_SERVICE_UNKNOWN, "Activation of %s not requested", m->destination); + else { + log_debug("Could not deliver message to %s as destination is not activated. Ignoring.", m->destination); + return 0; + } + } else + return -errno; + + r = bus_message_new_synthetic_error( + bus, + BUS_MESSAGE_COOKIE(m), + &error, + &reply); + + if (r < 0) + return r; + + r = bus_seal_synthetic_message(bus, reply); + if (r < 0) + return r; + + bus->rqueue[bus->rqueue_size++] = reply; + + } else if (hint_sync_call) { + struct kdbus_msg *k; + + k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + cmd.reply.offset); + assert(k); + + if (k->payload_type == KDBUS_PAYLOAD_DBUS) { + + r = bus_kernel_make_message(bus, k); + if (r < 0) { + close_kdbus_msg(bus, k); + + /* Anybody can send us invalid messages, let's just drop them. */ + if (r == -EBADMSG || r == -EPROTOTYPE) + log_debug_errno(r, "Ignoring invalid synchronous reply: %m"); + else + return r; + } + } else { + log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); + close_kdbus_msg(bus, k); + } + } + + return 1; +} + +static int push_name_owner_changed( + sd_bus *bus, + const char *name, + const char *old_owner, + const char *new_owner, + const struct kdbus_timestamp *ts) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "NameOwnerChanged"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "sss", name, old_owner, new_owner); + if (r < 0) + return r; + + bus_message_set_sender_driver(bus, m); + message_set_timestamp(bus, m, ts); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + bus->rqueue[bus->rqueue_size++] = m; + m = NULL; + + return 1; +} + +static int translate_name_change( + sd_bus *bus, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + char new_owner[UNIQUE_NAME_MAX], old_owner[UNIQUE_NAME_MAX]; + + assert(bus); + assert(k); + assert(d); + + if (d->type == KDBUS_ITEM_NAME_ADD || (d->name_change.old_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) + old_owner[0] = 0; + else + sprintf(old_owner, ":1.%llu", (unsigned long long) d->name_change.old_id.id); + + if (d->type == KDBUS_ITEM_NAME_REMOVE || (d->name_change.new_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))) { + + if (isempty(old_owner)) + return 0; + + new_owner[0] = 0; + } else + sprintf(new_owner, ":1.%llu", (unsigned long long) d->name_change.new_id.id); + + return push_name_owner_changed(bus, d->name_change.name, old_owner, new_owner, ts); +} + +static int translate_id_change( + sd_bus *bus, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + char owner[UNIQUE_NAME_MAX]; + + assert(bus); + assert(k); + assert(d); + + sprintf(owner, ":1.%llu", d->id_change.id); + + return push_name_owner_changed( + bus, owner, + d->type == KDBUS_ITEM_ID_ADD ? NULL : owner, + d->type == KDBUS_ITEM_ID_ADD ? owner : NULL, + ts); +} + +static int translate_reply( + sd_bus *bus, + const struct kdbus_msg *k, + const struct kdbus_item *d, + const struct kdbus_timestamp *ts) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + assert(k); + assert(d); + + r = bus_message_new_synthetic_error( + bus, + k->cookie_reply, + d->type == KDBUS_ITEM_REPLY_TIMEOUT ? + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out") : + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call peer died"), + &m); + if (r < 0) + return r; + + message_set_timestamp(bus, m, ts); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + bus->rqueue[bus->rqueue_size++] = m; + m = NULL; + + return 1; +} + +static int bus_kernel_translate_message(sd_bus *bus, struct kdbus_msg *k) { + static int (* const translate[])(sd_bus *bus, const struct kdbus_msg *k, const struct kdbus_item *d, const struct kdbus_timestamp *ts) = { + [KDBUS_ITEM_NAME_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + [KDBUS_ITEM_NAME_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + [KDBUS_ITEM_NAME_CHANGE - _KDBUS_ITEM_KERNEL_BASE] = translate_name_change, + + [KDBUS_ITEM_ID_ADD - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, + [KDBUS_ITEM_ID_REMOVE - _KDBUS_ITEM_KERNEL_BASE] = translate_id_change, + + [KDBUS_ITEM_REPLY_TIMEOUT - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, + [KDBUS_ITEM_REPLY_DEAD - _KDBUS_ITEM_KERNEL_BASE] = translate_reply, + }; + + struct kdbus_item *d, *found = NULL; + struct kdbus_timestamp *ts = NULL; + + assert(bus); + assert(k); + assert(k->payload_type == KDBUS_PAYLOAD_KERNEL); + + KDBUS_ITEM_FOREACH(d, k, items) { + if (d->type == KDBUS_ITEM_TIMESTAMP) + ts = &d->timestamp; + else if (d->type >= _KDBUS_ITEM_KERNEL_BASE && d->type < _KDBUS_ITEM_KERNEL_BASE + ELEMENTSOF(translate)) { + if (found) + return -EBADMSG; + found = d; + } else + log_debug("Got unknown field from kernel %llu", d->type); + } + + if (!found) { + log_debug("Didn't find a kernel message to translate."); + return 0; + } + + return translate[found->type - _KDBUS_ITEM_KERNEL_BASE](bus, k, found, ts); +} + +int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { + struct kdbus_cmd_recv recv = { .size = sizeof(recv) }; + struct kdbus_msg *k; + int r; + + assert(bus); + + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + if (hint_priority) { + recv.flags |= KDBUS_RECV_USE_PRIORITY; + recv.priority = priority; + } + + r = ioctl(bus->input_fd, KDBUS_CMD_RECV, &recv); + if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS) + log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); + if (r < 0) { + if (errno == EAGAIN) + return 0; + + return -errno; + } + + k = (struct kdbus_msg *)((uint8_t *)bus->kdbus_buffer + recv.msg.offset); + if (k->payload_type == KDBUS_PAYLOAD_DBUS) { + r = bus_kernel_make_message(bus, k); + + /* Anybody can send us invalid messages, let's just drop them. */ + if (r == -EBADMSG || r == -EPROTOTYPE) { + log_debug_errno(r, "Ignoring invalid message: %m"); + r = 0; + } + + if (r <= 0) + close_kdbus_msg(bus, k); + } else if (k->payload_type == KDBUS_PAYLOAD_KERNEL) { + r = bus_kernel_translate_message(bus, k); + close_kdbus_msg(bus, k); + } else { + log_debug("Ignoring message with unknown payload type %llu.", (unsigned long long) k->payload_type); + r = 0; + close_kdbus_msg(bus, k); + } + + return r < 0 ? r : 1; +} + +int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated) { + struct memfd_cache *c; + int fd; + + assert(address); + assert(mapped); + assert(allocated); + + if (!bus || !bus->is_kernel) + return -EOPNOTSUPP; + + 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); + + r = memfd_new(bus->description); + if (r < 0) + return r; + + *address = NULL; + *mapped = 0; + *allocated = 0; + return r; + } + + c = &bus->memfd_cache[--bus->n_memfd_cache]; + + assert(c->fd >= 0); + assert(c->mapped == 0 || c->address); + + *address = c->address; + *mapped = c->mapped; + *allocated = c->allocated; + fd = c->fd; + + assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); + + return fd; +} + +static void close_and_munmap(int fd, void *address, size_t size) { + if (size > 0) + assert_se(munmap(address, PAGE_ALIGN(size)) >= 0); + + safe_close(fd); +} + +void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated) { + struct memfd_cache *c; + uint64_t max_mapped = PAGE_ALIGN(MEMFD_CACHE_ITEM_SIZE_MAX); + + assert(fd >= 0); + assert(mapped == 0 || address); + + if (!bus || !bus->is_kernel) { + close_and_munmap(fd, address, mapped); + return; + } + + 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); + + close_and_munmap(fd, address, mapped); + return; + } + + c = &bus->memfd_cache[bus->n_memfd_cache++]; + c->fd = fd; + c->address = address; + + /* If overly long, let's return a bit to the OS */ + if (mapped > max_mapped) { + assert_se(memfd_set_size(fd, max_mapped) >= 0); + assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0); + c->mapped = c->allocated = max_mapped; + } else { + c->mapped = mapped; + c->allocated = allocated; + } + + assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) == 0); +} + +void bus_kernel_flush_memfd(sd_bus *b) { + unsigned i; + + assert(b); + + for (i = 0; i < b->n_memfd_cache; i++) + close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped); +} + +uint64_t request_name_flags_to_kdbus(uint64_t flags) { + uint64_t f = 0; + + if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) + f |= KDBUS_NAME_ALLOW_REPLACEMENT; + + if (flags & SD_BUS_NAME_REPLACE_EXISTING) + f |= KDBUS_NAME_REPLACE_EXISTING; + + if (flags & SD_BUS_NAME_QUEUE) + f |= KDBUS_NAME_QUEUE; + + return f; +} + +uint64_t attach_flags_to_kdbus(uint64_t mask) { + uint64_t m = 0; + + if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) + m |= KDBUS_ATTACH_CREDS; + + if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) + m |= KDBUS_ATTACH_PIDS; + + if (mask & SD_BUS_CREDS_COMM) + m |= KDBUS_ATTACH_PID_COMM; + + if (mask & SD_BUS_CREDS_TID_COMM) + m |= KDBUS_ATTACH_TID_COMM; + + if (mask & SD_BUS_CREDS_EXE) + m |= KDBUS_ATTACH_EXE; + + if (mask & SD_BUS_CREDS_CMDLINE) + m |= KDBUS_ATTACH_CMDLINE; + + if (mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) + m |= KDBUS_ATTACH_CGROUP; + + if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) + m |= KDBUS_ATTACH_CAPS; + + if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) + m |= KDBUS_ATTACH_SECLABEL; + + if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) + m |= KDBUS_ATTACH_AUDIT; + + if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) + m |= KDBUS_ATTACH_NAMES; + + if (mask & SD_BUS_CREDS_DESCRIPTION) + m |= KDBUS_ATTACH_CONN_DESCRIPTION; + + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) + m |= KDBUS_ATTACH_AUXGROUPS; + + return m; +} + +int bus_kernel_create_bus(const char *name, bool world, char **s) { + struct kdbus_cmd *make; + struct kdbus_item *n; + size_t l; + int fd; + + assert(name); + assert(s); + + fd = open("/sys/fs/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + l = strlen(name); + make = alloca0_align(offsetof(struct kdbus_cmd, items) + + ALIGN8(offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter)) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)) + + ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1), + 8); + + make->size = offsetof(struct kdbus_cmd, items); + + /* Set the bloom parameters */ + n = make->items; + n->size = offsetof(struct kdbus_item, bloom_parameter) + + sizeof(struct kdbus_bloom_parameter); + n->type = KDBUS_ITEM_BLOOM_PARAMETER; + n->bloom_parameter.size = DEFAULT_BLOOM_SIZE; + n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH; + + assert_cc(DEFAULT_BLOOM_SIZE > 0); + assert_cc(DEFAULT_BLOOM_N_HASH > 0); + + make->size += ALIGN8(n->size); + + /* Provide all metadata via bus-owner queries */ + n = KDBUS_ITEM_NEXT(n); + n->type = KDBUS_ITEM_ATTACH_FLAGS_SEND; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = _KDBUS_ATTACH_ANY; + make->size += ALIGN8(n->size); + + /* Set the a good name */ + n = KDBUS_ITEM_NEXT(n); + sprintf(n->str, UID_FMT "-%s", getuid(), name); + n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; + n->type = KDBUS_ITEM_MAKE_NAME; + make->size += ALIGN8(n->size); + + make->flags = world ? KDBUS_MAKE_ACCESS_WORLD : 0; + + if (ioctl(fd, KDBUS_CMD_BUS_MAKE, make) < 0) { + safe_close(fd); + + /* Major API change? then the ioctls got shuffled around. */ + if (errno == ENOTTY) + return -ESOCKTNOSUPPORT; + + return -errno; + } + + if (s) { + char *p; + + p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); + if (!p) { + safe_close(fd); + return -ENOMEM; + } + + *s = p; + } + + return fd; +} + +int bus_kernel_open_bus_fd(const char *bus, char **path) { + char *p; + int fd; + size_t len; + + assert(bus); + + len = strlen("/sys/fs/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; + + if (path) { + p = new(char, len); + if (!p) + return -ENOMEM; + } else + p = newa(char, len); + + sprintf(p, "/sys/fs/kdbus/" UID_FMT "-%s/bus", getuid(), bus); + + fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + if (path) + free(p); + + return -errno; + } + + if (path) + *path = p; + + return fd; +} + +int bus_kernel_try_close(sd_bus *bus) { + struct kdbus_cmd byebye = { .size = sizeof(byebye) }; + + assert(bus); + assert(bus->is_kernel); + + if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE, &byebye) < 0) + return -errno; + + return 0; +} + +int bus_kernel_drop_one(int fd) { + struct kdbus_cmd_recv recv = { + .size = sizeof(recv), + .flags = KDBUS_RECV_DROP, + }; + + assert(fd >= 0); + + if (ioctl(fd, KDBUS_CMD_RECV, &recv) < 0) + return -errno; + + return 0; +} + +int bus_kernel_realize_attach_flags(sd_bus *bus) { + struct kdbus_cmd *update; + struct kdbus_item *n; + + assert(bus); + assert(bus->is_kernel); + + update = alloca0_align(offsetof(struct kdbus_cmd, items) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)), + 8); + + n = update->items; + n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = bus->attach_flags; + + update->size = + offsetof(struct kdbus_cmd, items) + + ALIGN8(n->size); + + if (ioctl(bus->input_fd, KDBUS_CMD_UPDATE, update) < 0) + return -errno; + + return 0; +} + +int bus_kernel_get_bus_name(sd_bus *bus, char **name) { + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info), + }; + struct kdbus_info *info; + struct kdbus_item *item; + char *n = NULL; + int r; + + assert(bus); + assert(name); + assert(bus->is_kernel); + + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) + return -errno; + + info = (struct kdbus_info*) ((uint8_t*) bus->kdbus_buffer + cmd.offset); + + KDBUS_ITEM_FOREACH(item, info, items) + if (item->type == KDBUS_ITEM_MAKE_NAME) { + r = free_and_strdup(&n, item->str); + break; + } + + bus_kernel_cmd_free(bus, cmd.offset); + + if (r < 0) + return r; + if (!n) + return -EIO; + + *name = n; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-kernel.h b/src/libsystemd/src/sd-bus/bus-kernel.h new file mode 100644 index 0000000000..2927ba26a5 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-kernel.h @@ -0,0 +1,93 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#define KDBUS_ITEM_NEXT(item) \ + (typeof(item))(((uint8_t *)item) + ALIGN8((item)->size)) + +#define KDBUS_ITEM_FOREACH(part, head, first) \ + for (part = (head)->first; \ + ((uint8_t *)(part) < (uint8_t *)(head) + (head)->size) && \ + ((uint8_t *) part >= (uint8_t *) head); \ + part = KDBUS_ITEM_NEXT(part)) +#define KDBUS_FOREACH(iter, first, _size) \ + for (iter = (first); \ + ((uint8_t *)(iter) < (uint8_t *)(first) + (_size)) && \ + ((uint8_t *)(iter) >= (uint8_t *)(first)); \ + iter = (void*)(((uint8_t *)iter) + ALIGN8((iter)->size))) + +#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) +#define KDBUS_ITEM_SIZE(s) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) + +#define MEMFD_CACHE_MAX 32 + +/* When we cache a memfd block for reuse, we will truncate blocks + * longer than this in order not to keep too much data around. */ +#define MEMFD_CACHE_ITEM_SIZE_MAX (128*1024) + +/* This determines at which minimum size we prefer sending memfds over + * sending vectors */ +#define MEMFD_MIN_SIZE (512*1024) + +/* The size of the per-connection memory pool that we set up and where + * the kernel places our incoming messages */ +#define KDBUS_POOL_SIZE (16*1024*1024) + +struct memfd_cache { + int fd; + void *address; + size_t mapped; + size_t allocated; +}; + +int bus_kernel_connect(sd_bus *b); +int bus_kernel_take_fd(sd_bus *b); + +int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call); +int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority); + +int bus_kernel_open_bus_fd(const char *bus, char **path); + +int bus_kernel_create_bus(const char *name, bool world, char **s); +int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **path); + +int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated); +void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, size_t allocated); + +void bus_kernel_flush_memfd(sd_bus *bus); + +int bus_kernel_parse_unique_name(const char *s, uint64_t *id); + +uint64_t request_name_flags_to_kdbus(uint64_t sd_bus_flags); +uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); + +int bus_kernel_try_close(sd_bus *bus); + +int bus_kernel_drop_one(int fd); + +int bus_kernel_realize_attach_flags(sd_bus *bus); + +int bus_kernel_get_bus_name(sd_bus *bus, char **name); + +int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/src/sd-bus/bus-match.c b/src/libsystemd/src/sd-bus/bus-match.c new file mode 100644 index 0000000000..185867679e --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-match.c @@ -0,0 +1,1222 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hexdecoct.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-match.h" +#include "bus-message.h" + +/* Example: + * + * A: type=signal,sender=foo,interface=bar + * B: type=signal,sender=quux,interface=fips + * C: type=signal,sender=quux,interface=waldo + * D: type=signal,member=test + * E: sender=miau + * F: type=signal + * G: type=signal + * + * results in this tree: + * + * BUS_MATCH_ROOT + * + BUS_MATCH_MESSAGE_TYPE + * | ` BUS_MATCH_VALUE: value == signal + * | + DBUS_MATCH_SENDER + * | | + BUS_MATCH_VALUE: value == foo + * | | | ` DBUS_MATCH_INTERFACE + * | | | ` BUS_MATCH_VALUE: value == bar + * | | | ` BUS_MATCH_LEAF: A + * | | ` BUS_MATCH_VALUE: value == quux + * | | ` DBUS_MATCH_INTERFACE + * | | | BUS_MATCH_VALUE: value == fips + * | | | ` BUS_MATCH_LEAF: B + * | | ` BUS_MATCH_VALUE: value == waldo + * | | ` BUS_MATCH_LEAF: C + * | + DBUS_MATCH_MEMBER + * | | ` BUS_MATCH_VALUE: value == test + * | | ` BUS_MATCH_LEAF: D + * | + BUS_MATCH_LEAF: F + * | ` BUS_MATCH_LEAF: G + * ` BUS_MATCH_SENDER + * ` BUS_MATCH_VALUE: value == miau + * ` BUS_MATCH_LEAF: E + */ + +static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) { + return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST; +} + +static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) { + return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) || + (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) || + (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST); +} + +static void bus_match_node_free(struct bus_match_node *node) { + assert(node); + assert(node->parent); + assert(!node->child); + assert(node->type != BUS_MATCH_ROOT); + assert(node->type < _BUS_MATCH_NODE_TYPE_MAX); + + if (node->parent->child) { + /* We are apparently linked into the parent's child + * list. Let's remove us from there. */ + if (node->prev) { + assert(node->prev->next == node); + node->prev->next = node->next; + } else { + assert(node->parent->child == node); + node->parent->child = node->next; + } + + if (node->next) + node->next->prev = node->prev; + } + + if (node->type == BUS_MATCH_VALUE) { + /* We might be in the parent's hash table, so clean + * this up */ + + if (node->parent->type == BUS_MATCH_MESSAGE_TYPE) + hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8)); + else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str) + hashmap_remove(node->parent->compare.children, node->value.str); + + free(node->value.str); + } + + if (BUS_MATCH_IS_COMPARE(node->type)) { + assert(hashmap_isempty(node->compare.children)); + hashmap_free(node->compare.children); + } + + free(node); +} + +static bool bus_match_node_maybe_free(struct bus_match_node *node) { + assert(node); + + if (node->type == BUS_MATCH_ROOT) + return false; + + if (node->child) + return false; + + if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children)) + return true; + + bus_match_node_free(node); + return true; +} + +static bool value_node_test( + struct bus_match_node *node, + enum bus_match_node_type parent_type, + uint8_t value_u8, + const char *value_str, + char **value_strv, + sd_bus_message *m) { + + assert(node); + assert(node->type == BUS_MATCH_VALUE); + + /* Tests parameters against this value node, doing prefix + * magic and stuff. */ + + switch (parent_type) { + + case BUS_MATCH_MESSAGE_TYPE: + return node->value.u8 == value_u8; + + case BUS_MATCH_SENDER: + if (streq_ptr(node->value.str, value_str)) + return true; + + if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + char **i; + + /* on kdbus we have the well known names list + * in the credentials, let's make use of that + * for an accurate match */ + + STRV_FOREACH(i, m->creds.well_known_names) + if (streq_ptr(node->value.str, *i)) + return true; + + } else { + + /* If we don't have kdbus, we don't know the + * well-known names of the senders. In that, + * let's just hope that dbus-daemon doesn't + * send us stuff we didn't want. */ + + if (node->value.str[0] != ':' && value_str && value_str[0] == ':') + return true; + } + + return false; + + case BUS_MATCH_DESTINATION: + case BUS_MATCH_INTERFACE: + case BUS_MATCH_MEMBER: + case BUS_MATCH_PATH: + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: + + if (value_str) + return streq_ptr(node->value.str, value_str); + + return false; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { + char **i; + + STRV_FOREACH(i, value_strv) + if (streq_ptr(node->value.str, *i)) + return true; + + return false; + } + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + if (value_str) + return namespace_simple_pattern(node->value.str, value_str); + + return false; + + case BUS_MATCH_PATH_NAMESPACE: + return path_simple_pattern(node->value.str, value_str); + + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + if (value_str) + return path_complex_pattern(node->value.str, value_str); + + return false; + + default: + assert_not_reached("Invalid node type"); + } +} + +static bool value_node_same( + struct bus_match_node *node, + enum bus_match_node_type parent_type, + uint8_t value_u8, + const char *value_str) { + + /* Tests parameters against this value node, not doing prefix + * magic and stuff, i.e. this one actually compares the match + * itself. */ + + assert(node); + assert(node->type == BUS_MATCH_VALUE); + + switch (parent_type) { + + case BUS_MATCH_MESSAGE_TYPE: + return node->value.u8 == value_u8; + + case BUS_MATCH_SENDER: + case BUS_MATCH_DESTINATION: + case BUS_MATCH_INTERFACE: + case BUS_MATCH_MEMBER: + case BUS_MATCH_PATH: + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + case BUS_MATCH_PATH_NAMESPACE: + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + return streq(node->value.str, value_str); + + default: + assert_not_reached("Invalid node type"); + } +} + +int bus_match_run( + sd_bus *bus, + struct bus_match_node *node, + sd_bus_message *m) { + + _cleanup_strv_free_ char **test_strv = NULL; + const char *test_str = NULL; + uint8_t test_u8 = 0; + int r; + + assert(m); + + if (!node) + return 0; + + if (bus && bus->match_callbacks_modified) + return 0; + + /* Not these special semantics: when traversing the tree we + * usually let bus_match_run() when called for a node + * recursively invoke bus_match_run(). There's are two + * exceptions here though, which are BUS_NODE_ROOT (which + * cannot have a sibling), and BUS_NODE_VALUE (whose siblings + * are invoked anyway by its parent. */ + + switch (node->type) { + + case BUS_MATCH_ROOT: + + /* Run all children. Since we cannot have any siblings + * we won't call any. The children of the root node + * are compares or leaves, they will automatically + * call their siblings. */ + return bus_match_run(bus, node->child, m); + + case BUS_MATCH_VALUE: + + /* Run all children. We don't execute any siblings, we + * assume our caller does that. The children of value + * nodes are compares or leaves, they will + * automatically call their siblings */ + + assert(node->child); + return bus_match_run(bus, node->child, m); + + case BUS_MATCH_LEAF: + + if (bus) { + if (node->leaf.callback->last_iteration == bus->iteration_counter) + return 0; + + node->leaf.callback->last_iteration = bus->iteration_counter; + } + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + /* Run the callback. And then invoke siblings. */ + if (node->leaf.callback->callback) { + _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); + if (bus) { + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = node->leaf.callback->callback; + bus->current_userdata = slot->userdata; + } + r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); + if (bus) { + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + } + + r = bus_maybe_reply_error(m, r, &error_buffer); + if (r != 0) + return r; + + if (bus && bus->match_callbacks_modified) + return 0; + } + + return bus_match_run(bus, node->next, m); + + case BUS_MATCH_MESSAGE_TYPE: + test_u8 = m->header->type; + break; + + case BUS_MATCH_SENDER: + test_str = m->sender; + /* FIXME: resolve test_str from a well-known to a unique name first */ + break; + + case BUS_MATCH_DESTINATION: + test_str = m->destination; + break; + + case BUS_MATCH_INTERFACE: + test_str = m->interface; + break; + + case BUS_MATCH_MEMBER: + test_str = m->member; + break; + + case BUS_MATCH_PATH: + case BUS_MATCH_PATH_NAMESPACE: + test_str = m->path; + break; + + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); + break; + + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); + break; + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); + break; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); + break; + + default: + assert_not_reached("Unknown match type."); + } + + if (BUS_MATCH_CAN_HASH(node->type)) { + struct bus_match_node *found; + + /* Lookup via hash table, nice! So let's jump directly. */ + + if (test_str) + found = hashmap_get(node->compare.children, test_str); + else if (test_strv) { + char **i; + + STRV_FOREACH(i, test_strv) { + found = hashmap_get(node->compare.children, *i); + if (found) { + r = bus_match_run(bus, found, m); + if (r != 0) + return r; + } + } + + found = NULL; + } else if (node->type == BUS_MATCH_MESSAGE_TYPE) + found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8)); + else + found = NULL; + + if (found) { + r = bus_match_run(bus, found, m); + if (r != 0) + return r; + } + } else { + struct bus_match_node *c; + + /* No hash table, so let's iterate manually... */ + + for (c = node->child; c; c = c->next) { + if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m)) + continue; + + r = bus_match_run(bus, c, m); + if (r != 0) + return r; + + if (bus && bus->match_callbacks_modified) + return 0; + } + } + + if (bus && bus->match_callbacks_modified) + return 0; + + /* And now, let's invoke our siblings */ + return bus_match_run(bus, node->next, m); +} + +static int bus_match_add_compare_value( + struct bus_match_node *where, + enum bus_match_node_type t, + uint8_t value_u8, + const char *value_str, + struct bus_match_node **ret) { + + struct bus_match_node *c = NULL, *n = NULL; + int r; + + assert(where); + assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); + assert(BUS_MATCH_IS_COMPARE(t)); + assert(ret); + + for (c = where->child; c && c->type != t; c = c->next) + ; + + if (c) { + /* Comparison node already exists? Then let's see if + * the value node exists too. */ + + if (t == BUS_MATCH_MESSAGE_TYPE) + n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8)); + else if (BUS_MATCH_CAN_HASH(t)) + n = hashmap_get(c->compare.children, value_str); + else { + for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) + ; + } + + if (n) { + *ret = n; + return 0; + } + } else { + /* Comparison node, doesn't exist yet? Then let's + * create it. */ + + c = new0(struct bus_match_node, 1); + if (!c) { + r = -ENOMEM; + goto fail; + } + + c->type = t; + c->parent = where; + c->next = where->child; + if (c->next) + c->next->prev = c; + where->child = c; + + if (t == BUS_MATCH_MESSAGE_TYPE) { + c->compare.children = hashmap_new(NULL); + if (!c->compare.children) { + r = -ENOMEM; + goto fail; + } + } else if (BUS_MATCH_CAN_HASH(t)) { + c->compare.children = hashmap_new(&string_hash_ops); + if (!c->compare.children) { + r = -ENOMEM; + goto fail; + } + } + } + + n = new0(struct bus_match_node, 1); + if (!n) { + r = -ENOMEM; + goto fail; + } + + n->type = BUS_MATCH_VALUE; + n->value.u8 = value_u8; + if (value_str) { + n->value.str = strdup(value_str); + if (!n->value.str) { + r = -ENOMEM; + goto fail; + } + } + + n->parent = c; + if (c->compare.children) { + + if (t == BUS_MATCH_MESSAGE_TYPE) + r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n); + else + r = hashmap_put(c->compare.children, n->value.str, n); + + if (r < 0) + goto fail; + } else { + n->next = c->child; + if (n->next) + n->next->prev = n; + c->child = n; + } + + *ret = n; + return 1; + +fail: + if (c) + bus_match_node_maybe_free(c); + + if (n) { + free(n->value.str); + free(n); + } + + return r; +} + +static int bus_match_find_compare_value( + struct bus_match_node *where, + enum bus_match_node_type t, + uint8_t value_u8, + const char *value_str, + struct bus_match_node **ret) { + + struct bus_match_node *c, *n; + + assert(where); + assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); + assert(BUS_MATCH_IS_COMPARE(t)); + assert(ret); + + for (c = where->child; c && c->type != t; c = c->next) + ; + + if (!c) + return 0; + + if (t == BUS_MATCH_MESSAGE_TYPE) + n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8)); + else if (BUS_MATCH_CAN_HASH(t)) + n = hashmap_get(c->compare.children, value_str); + else { + for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) + ; + } + + if (n) { + *ret = n; + return 1; + } + + return 0; +} + +static int bus_match_add_leaf( + struct bus_match_node *where, + struct match_callback *callback) { + + struct bus_match_node *n; + + assert(where); + assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); + assert(callback); + + n = new0(struct bus_match_node, 1); + if (!n) + return -ENOMEM; + + n->type = BUS_MATCH_LEAF; + n->parent = where; + n->next = where->child; + if (n->next) + n->next->prev = n; + + n->leaf.callback = callback; + callback->match_node = n; + + where->child = n; + + return 1; +} + +static int bus_match_find_leaf( + struct bus_match_node *where, + sd_bus_message_handler_t callback, + void *userdata, + struct bus_match_node **ret) { + + struct bus_match_node *c; + + assert(where); + assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE); + assert(ret); + + for (c = where->child; c; c = c->next) { + sd_bus_slot *s; + + s = container_of(c->leaf.callback, sd_bus_slot, match_callback); + + if (c->type == BUS_MATCH_LEAF && + c->leaf.callback->callback == callback && + s->userdata == userdata) { + *ret = c; + return 1; + } + } + + return 0; +} + +enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) { + assert(k); + + if (n == 4 && startswith(k, "type")) + return BUS_MATCH_MESSAGE_TYPE; + if (n == 6 && startswith(k, "sender")) + return BUS_MATCH_SENDER; + if (n == 11 && startswith(k, "destination")) + return BUS_MATCH_DESTINATION; + if (n == 9 && startswith(k, "interface")) + return BUS_MATCH_INTERFACE; + if (n == 6 && startswith(k, "member")) + return BUS_MATCH_MEMBER; + if (n == 4 && startswith(k, "path")) + return BUS_MATCH_PATH; + if (n == 14 && startswith(k, "path_namespace")) + return BUS_MATCH_PATH_NAMESPACE; + + if (n == 4 && startswith(k, "arg")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG + j; + } + + if (n == 5 && startswith(k, "arg")) { + int a, b; + enum bus_match_node_type t; + + a = undecchar(k[3]); + b = undecchar(k[4]); + if (a <= 0 || b < 0) + return -EINVAL; + + t = BUS_MATCH_ARG + a * 10 + b; + if (t > BUS_MATCH_ARG_LAST) + return -EINVAL; + + return t; + } + + if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG_PATH + j; + } + + if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) { + enum bus_match_node_type t; + int a, b; + + a = undecchar(k[3]); + b = undecchar(k[4]); + if (a <= 0 || b < 0) + return -EINVAL; + + t = BUS_MATCH_ARG_PATH + a * 10 + b; + if (t > BUS_MATCH_ARG_PATH_LAST) + return -EINVAL; + + return t; + } + + if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG_NAMESPACE + j; + } + + if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) { + enum bus_match_node_type t; + int a, b; + + a = undecchar(k[3]); + b = undecchar(k[4]); + if (a <= 0 || b < 0) + return -EINVAL; + + t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b; + if (t > BUS_MATCH_ARG_NAMESPACE_LAST) + return -EINVAL; + + return t; + } + + if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG_HAS + j; + } + + if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { + enum bus_match_node_type t; + int a, b; + + a = undecchar(k[3]); + b = undecchar(k[4]); + if (a <= 0 || b < 0) + return -EINVAL; + + t = BUS_MATCH_ARG_HAS + a * 10 + b; + if (t > BUS_MATCH_ARG_HAS_LAST) + return -EINVAL; + + return t; + } + + return -EINVAL; +} + +static int match_component_compare(const void *a, const void *b) { + const struct bus_match_component *x = a, *y = b; + + if (x->type < y->type) + return -1; + if (x->type > y->type) + return 1; + + return 0; +} + +void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) { + unsigned i; + + for (i = 0; i < n_components; i++) + free(components[i].value_str); + + free(components); +} + +int bus_match_parse( + const char *match, + struct bus_match_component **_components, + unsigned *_n_components) { + + const char *p = match; + struct bus_match_component *components = NULL; + size_t components_allocated = 0; + unsigned n_components = 0, i; + _cleanup_free_ char *value = NULL; + int r; + + assert(match); + assert(_components); + assert(_n_components); + + while (*p != 0) { + const char *eq, *q; + enum bus_match_node_type t; + unsigned j = 0; + size_t value_allocated = 0; + bool escaped = false, quoted; + uint8_t u; + + /* Avahi's match rules appear to include whitespace, skip over it */ + p += strspn(p, " "); + + eq = strchr(p, '='); + if (!eq) + return -EINVAL; + + t = bus_match_node_type_from_string(p, eq - p); + if (t < 0) + return -EINVAL; + + quoted = eq[1] == '\''; + + for (q = eq + 1 + quoted;; q++) { + + if (*q == 0) { + + if (quoted) { + r = -EINVAL; + goto fail; + } else { + if (value) + value[j] = 0; + break; + } + } + + if (!escaped) { + if (*q == '\\') { + escaped = true; + continue; + } + + if (quoted) { + if (*q == '\'') { + if (value) + value[j] = 0; + break; + } + } else { + if (*q == ',') { + if (value) + value[j] = 0; + + break; + } + } + } + + if (!GREEDY_REALLOC(value, value_allocated, j + 2)) { + r = -ENOMEM; + goto fail; + } + + value[j++] = *q; + escaped = false; + } + + if (!value) { + value = strdup(""); + if (!value) { + r = -ENOMEM; + goto fail; + } + } + + if (t == BUS_MATCH_MESSAGE_TYPE) { + r = bus_message_type_from_string(value, &u); + if (r < 0) + goto fail; + + value = mfree(value); + } else + u = 0; + + if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) { + r = -ENOMEM; + goto fail; + } + + components[n_components].type = t; + components[n_components].value_str = value; + components[n_components].value_u8 = u; + n_components++; + + value = NULL; + + if (q[quoted] == 0) + break; + + if (q[quoted] != ',') { + r = -EINVAL; + goto fail; + } + + p = q + 1 + quoted; + } + + /* Order the whole thing, so that we always generate the same tree */ + qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare); + + /* Check for duplicates */ + for (i = 0; i+1 < n_components; i++) + if (components[i].type == components[i+1].type) { + r = -EINVAL; + goto fail; + } + + *_components = components; + *_n_components = n_components; + + return 0; + +fail: + bus_match_parse_free(components, n_components); + return r; +} + +char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) { + _cleanup_fclose_ FILE *f = NULL; + char *buffer = NULL; + size_t size = 0; + unsigned i; + int r; + + if (n_components <= 0) + return strdup(""); + + assert(components); + + f = open_memstream(&buffer, &size); + if (!f) + return NULL; + + for (i = 0; i < n_components; i++) { + char buf[32]; + + if (i != 0) + fputc(',', f); + + fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f); + fputc('=', f); + fputc('\'', f); + + if (components[i].type == BUS_MATCH_MESSAGE_TYPE) + fputs(bus_message_type_to_string(components[i].value_u8), f); + else + fputs(components[i].value_str, f); + + fputc('\'', f); + } + + r = fflush_and_check(f); + if (r < 0) + return NULL; + + return buffer; +} + +int bus_match_add( + struct bus_match_node *root, + struct bus_match_component *components, + unsigned n_components, + struct match_callback *callback) { + + unsigned i; + struct bus_match_node *n; + int r; + + assert(root); + assert(callback); + + n = root; + for (i = 0; i < n_components; i++) { + r = bus_match_add_compare_value( + n, components[i].type, + components[i].value_u8, components[i].value_str, &n); + if (r < 0) + return r; + } + + return bus_match_add_leaf(n, callback); +} + +int bus_match_remove( + struct bus_match_node *root, + struct match_callback *callback) { + + struct bus_match_node *node, *pp; + + assert(root); + assert(callback); + + node = callback->match_node; + if (!node) + return 0; + + assert(node->type == BUS_MATCH_LEAF); + + callback->match_node = NULL; + + /* Free the leaf */ + pp = node->parent; + bus_match_node_free(node); + + /* Prune the tree above */ + while (pp) { + node = pp; + pp = node->parent; + + if (!bus_match_node_maybe_free(node)) + break; + } + + return 1; +} + +int bus_match_find( + struct bus_match_node *root, + struct bus_match_component *components, + unsigned n_components, + sd_bus_message_handler_t callback, + void *userdata, + struct match_callback **ret) { + + struct bus_match_node *n, **gc; + unsigned i; + int r; + + assert(root); + assert(ret); + + gc = newa(struct bus_match_node*, n_components); + + n = root; + for (i = 0; i < n_components; i++) { + r = bus_match_find_compare_value( + n, components[i].type, + components[i].value_u8, components[i].value_str, + &n); + if (r <= 0) + return r; + + gc[i] = n; + } + + r = bus_match_find_leaf(n, callback, userdata, &n); + if (r <= 0) + return r; + + *ret = n->leaf.callback; + return 1; +} + +void bus_match_free(struct bus_match_node *node) { + struct bus_match_node *c; + + if (!node) + return; + + if (BUS_MATCH_CAN_HASH(node->type)) { + Iterator i; + + HASHMAP_FOREACH(c, node->compare.children, i) + bus_match_free(c); + + assert(hashmap_isempty(node->compare.children)); + } + + while ((c = node->child)) + bus_match_free(c); + + if (node->type != BUS_MATCH_ROOT) + bus_match_node_free(node); +} + +const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) { + switch (t) { + + case BUS_MATCH_ROOT: + return "root"; + + case BUS_MATCH_VALUE: + return "value"; + + case BUS_MATCH_LEAF: + return "leaf"; + + case BUS_MATCH_MESSAGE_TYPE: + return "type"; + + case BUS_MATCH_SENDER: + return "sender"; + + case BUS_MATCH_DESTINATION: + return "destination"; + + case BUS_MATCH_INTERFACE: + return "interface"; + + case BUS_MATCH_MEMBER: + return "member"; + + case BUS_MATCH_PATH: + return "path"; + + case BUS_MATCH_PATH_NAMESPACE: + return "path_namespace"; + + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: + snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG); + return buf; + + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: + snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH); + return buf; + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: + snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE); + return buf; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); + return buf; + + default: + return NULL; + } +} + +void bus_match_dump(struct bus_match_node *node, unsigned level) { + struct bus_match_node *c; + _cleanup_free_ char *pfx = NULL; + char buf[32]; + + if (!node) + return; + + pfx = strrep(" ", level); + printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf))); + + if (node->type == BUS_MATCH_VALUE) { + if (node->parent->type == BUS_MATCH_MESSAGE_TYPE) + printf(" <%u>\n", node->value.u8); + else + printf(" <%s>\n", node->value.str); + } else if (node->type == BUS_MATCH_ROOT) + puts(" root"); + else if (node->type == BUS_MATCH_LEAF) + printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata); + else + putchar('\n'); + + if (BUS_MATCH_CAN_HASH(node->type)) { + Iterator i; + + HASHMAP_FOREACH(c, node->compare.children, i) + bus_match_dump(c, level + 1); + } + + for (c = node->child; c; c = c->next) + bus_match_dump(c, level + 1); +} + +enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) { + bool found_driver = false; + unsigned i; + + if (n_components <= 0) + return BUS_MATCH_GENERIC; + + assert(components); + + /* Checks whether the specified match can only match the + * pseudo-service for local messages, which we detect by + * sender, interface or path. If a match is not restricted to + * local messages, then we check if it only matches on the + * driver. */ + + for (i = 0; i < n_components; i++) { + const struct bus_match_component *c = components + i; + + if (c->type == BUS_MATCH_SENDER) { + if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) + return BUS_MATCH_LOCAL; + + if (streq_ptr(c->value_str, "org.freedesktop.DBus")) + found_driver = true; + } + + if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) + return BUS_MATCH_LOCAL; + + if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local")) + return BUS_MATCH_LOCAL; + } + + return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC; + +} diff --git a/src/libsystemd/src/sd-bus/bus-match.h b/src/libsystemd/src/sd-bus/bus-match.h new file mode 100644 index 0000000000..a85abcb377 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-match.h @@ -0,0 +1,100 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/hashmap.h" + +enum bus_match_node_type { + BUS_MATCH_ROOT, + BUS_MATCH_VALUE, + BUS_MATCH_LEAF, + + /* The following are all different kinds of compare nodes */ + BUS_MATCH_SENDER, + BUS_MATCH_MESSAGE_TYPE, + BUS_MATCH_DESTINATION, + BUS_MATCH_INTERFACE, + BUS_MATCH_MEMBER, + BUS_MATCH_PATH, + BUS_MATCH_PATH_NAMESPACE, + BUS_MATCH_ARG, + BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63, + BUS_MATCH_ARG_PATH, + BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63, + BUS_MATCH_ARG_NAMESPACE, + BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63, + BUS_MATCH_ARG_HAS, + BUS_MATCH_ARG_HAS_LAST = BUS_MATCH_ARG_HAS + 63, + _BUS_MATCH_NODE_TYPE_MAX, + _BUS_MATCH_NODE_TYPE_INVALID = -1 +}; + +struct bus_match_node { + enum bus_match_node_type type; + struct bus_match_node *parent, *next, *prev, *child; + + union { + struct { + char *str; + uint8_t u8; + } value; + struct { + struct match_callback *callback; + } leaf; + struct { + /* If this is set, then the child is NULL */ + Hashmap *children; + } compare; + }; +}; + +struct bus_match_component { + enum bus_match_node_type type; + uint8_t value_u8; + char *value_str; +}; + +enum bus_match_scope { + BUS_MATCH_GENERIC, + BUS_MATCH_LOCAL, + BUS_MATCH_DRIVER, +}; + +int bus_match_run(sd_bus *bus, struct bus_match_node *root, sd_bus_message *m); + +int bus_match_add(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, struct match_callback *callback); +int bus_match_remove(struct bus_match_node *root, struct match_callback *callback); + +int bus_match_find(struct bus_match_node *root, struct bus_match_component *components, unsigned n_components, sd_bus_message_handler_t callback, void *userdata, struct match_callback **ret); + +void bus_match_free(struct bus_match_node *node); + +void bus_match_dump(struct bus_match_node *node, unsigned level); + +const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l); +enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n); + +int bus_match_parse(const char *match, struct bus_match_component **_components, unsigned *_n_components); +void bus_match_parse_free(struct bus_match_component *components, unsigned n_components); +char *bus_match_to_string(struct bus_match_component *components, unsigned n_components); + +enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components); diff --git a/src/libsystemd/src/sd-bus/bus-message.c b/src/libsystemd/src/sd-bus/bus-message.c new file mode 100644 index 0000000000..7fa5e822e0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-message.c @@ -0,0 +1,5940 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/memfd-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/utf8.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-gvariant.h" +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-signature.h" +#include "bus-type.h" + +static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored); + +static void *adjust_pointer(const void *p, void *old_base, size_t sz, void *new_base) { + + if (p == NULL) + return NULL; + + if (old_base == new_base) + return (void*) p; + + if ((uint8_t*) p < (uint8_t*) old_base) + return (void*) p; + + if ((uint8_t*) p >= (uint8_t*) old_base + sz) + return (void*) p; + + return (uint8_t*) new_base + ((uint8_t*) p - (uint8_t*) old_base); +} + +static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { + assert(m); + assert(part); + + if (part->memfd >= 0) { + /* If we can reuse the memfd, try that. For that it + * can't be sealed yet. */ + + if (!part->sealed) { + assert(part->memfd_offset == 0); + assert(part->data == part->mmap_begin); + bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); + } else { + if (part->mapped > 0) + assert_se(munmap(part->mmap_begin, part->mapped) == 0); + + safe_close(part->memfd); + } + + } else if (part->munmap_this) + munmap(part->mmap_begin, part->mapped); + else if (part->free_this) + free(part->data); + + if (part != &m->body) + free(part); +} + +static void message_reset_parts(sd_bus_message *m) { + struct bus_body_part *part; + + assert(m); + + part = &m->body; + while (m->n_body_parts > 0) { + struct bus_body_part *next = part->next; + message_free_part(m, part); + part = next; + m->n_body_parts--; + } + + m->body_end = NULL; + + m->cached_rindex_part = NULL; + m->cached_rindex_part_begin = 0; +} + +static void message_reset_containers(sd_bus_message *m) { + unsigned i; + + assert(m); + + for (i = 0; i < m->n_containers; i++) { + free(m->containers[i].signature); + free(m->containers[i].offsets); + } + + m->containers = mfree(m->containers); + + m->n_containers = m->containers_allocated = 0; + m->root_container.index = 0; +} + +static void message_free(sd_bus_message *m) { + assert(m); + + if (m->free_header) + free(m->header); + + message_reset_parts(m); + + if (m->release_kdbus) + bus_kernel_cmd_free(m->bus, (uint8_t *) m->kdbus - (uint8_t *) m->bus->kdbus_buffer); + + if (m->free_kdbus) + free(m->kdbus); + + sd_bus_unref(m->bus); + + if (m->free_fds) { + close_many(m->fds, m->n_fds); + free(m->fds); + } + + if (m->iovec != m->iovec_fixed) + free(m->iovec); + + m->destination_ptr = mfree(m->destination_ptr); + message_reset_containers(m); + free(m->root_container.signature); + free(m->root_container.offsets); + + free(m->root_container.peeked_signature); + + bus_creds_done(&m->creds); + free(m); +} + +static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) { + void *op, *np; + size_t old_size, new_size, start; + + assert(m); + + if (m->poisoned) + return NULL; + + old_size = sizeof(struct bus_header) + m->fields_size; + start = ALIGN_TO(old_size, align); + new_size = start + sz; + + if (new_size < start || + new_size > (size_t) ((uint32_t) -1)) + goto poison; + + if (old_size == new_size) + return (uint8_t*) m->header + old_size; + + if (m->free_header) { + np = realloc(m->header, ALIGN8(new_size)); + if (!np) + goto poison; + } else { + /* Initially, the header is allocated as part of + * the sd_bus_message itself, let's replace it by + * dynamic data */ + + np = malloc(ALIGN8(new_size)); + if (!np) + goto poison; + + memcpy(np, m->header, sizeof(struct bus_header)); + } + + /* Zero out padding */ + if (start > old_size) + memzero((uint8_t*) np + old_size, start - old_size); + + op = m->header; + m->header = np; + m->fields_size = new_size - sizeof(struct bus_header); + + /* Adjust quick access pointers */ + m->path = adjust_pointer(m->path, op, old_size, m->header); + m->interface = adjust_pointer(m->interface, op, old_size, m->header); + m->member = adjust_pointer(m->member, op, old_size, m->header); + m->destination = adjust_pointer(m->destination, op, old_size, m->header); + m->sender = adjust_pointer(m->sender, op, old_size, m->header); + m->error.name = adjust_pointer(m->error.name, op, old_size, m->header); + + m->free_header = true; + + if (add_offset) { + if (m->n_header_offsets >= ELEMENTSOF(m->header_offsets)) + goto poison; + + m->header_offsets[m->n_header_offsets++] = new_size - sizeof(struct bus_header); + } + + return (uint8_t*) np + start; + +poison: + m->poisoned = true; + return NULL; +} + +static int message_append_field_string( + sd_bus_message *m, + uint64_t h, + char type, + const char *s, + const char **ret) { + + size_t l; + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + /* dbus1 doesn't allow strings over 32bit, let's enforce this + * globally, to not risk convertability */ + l = strlen(s); + if (l > (size_t) (uint32_t) -1) + return -EINVAL; + + /* Signature "(yv)" where the variant contains "s" */ + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + + /* (field id 64bit, ((string + NUL) + NUL + signature string 's') */ + p = message_extend_fields(m, 8, 8 + l + 1 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + memcpy(p+8, s, l); + p[8+l] = 0; + p[8+l+1] = 0; + p[8+l+2] = type; + + if (ret) + *ret = (char*) p + 8; + + } else { + /* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */ + p = message_extend_fields(m, 8, 4 + 4 + l + 1, false); + if (!p) + return -ENOMEM; + + p[0] = (uint8_t) h; + p[1] = 1; + p[2] = type; + p[3] = 0; + + ((uint32_t*) p)[1] = l; + memcpy(p + 8, s, l + 1); + + if (ret) + *ret = (char*) p + 8; + } + + return 0; +} + +static int message_append_field_signature( + sd_bus_message *m, + uint64_t h, + const char *s, + const char **ret) { + + size_t l; + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + /* dbus1 doesn't allow signatures over 8bit, let's enforce + * this globally, to not risk convertability */ + l = strlen(s); + if (l > 255) + return -EINVAL; + + /* Signature "(yv)" where the variant contains "g" */ + + if (BUS_MESSAGE_IS_GVARIANT(m)) + /* For gvariant the serialization is the same as for normal strings */ + return message_append_field_string(m, h, 'g', s, ret); + else { + /* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */ + p = message_extend_fields(m, 8, 4 + 1 + l + 1, false); + if (!p) + return -ENOMEM; + + p[0] = (uint8_t) h; + p[1] = 1; + p[2] = SD_BUS_TYPE_SIGNATURE; + p[3] = 0; + p[4] = l; + memcpy(p + 5, s, l + 1); + + if (ret) + *ret = (const char*) p + 5; + } + + return 0; +} + +static int message_append_field_uint32(sd_bus_message *m, uint64_t h, uint32_t x) { + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* (field id 64bit + ((value + NUL + signature string 'u') */ + + p = message_extend_fields(m, 8, 8 + 4 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + *((uint32_t*) (p + 8)) = x; + p[12] = 0; + p[13] = 'u'; + } else { + /* (field id byte + (signature length + signature 'u' + NUL) + value) */ + p = message_extend_fields(m, 8, 4 + 4, false); + if (!p) + return -ENOMEM; + + p[0] = (uint8_t) h; + p[1] = 1; + p[2] = 'u'; + p[3] = 0; + + ((uint32_t*) p)[1] = x; + } + + return 0; +} + +static int message_append_field_uint64(sd_bus_message *m, uint64_t h, uint64_t x) { + uint8_t *p; + + assert(m); + + /* dbus1 only allows 8bit header field ids */ + if (h > 0xFF) + return -EINVAL; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* (field id 64bit + ((value + NUL + signature string 't') */ + + p = message_extend_fields(m, 8, 8 + 8 + 1 + 1, true); + if (!p) + return -ENOMEM; + + *((uint64_t*) p) = h; + *((uint64_t*) (p + 8)) = x; + p[16] = 0; + p[17] = 't'; + } else { + /* (field id byte + (signature length + signature 't' + NUL) + 4 byte padding + value) */ + p = message_extend_fields(m, 8, 4 + 4 + 8, false); + if (!p) + return -ENOMEM; + + p[0] = (uint8_t) h; + p[1] = 1; + p[2] = 't'; + p[3] = 0; + p[4] = 0; + p[5] = 0; + p[6] = 0; + p[7] = 0; + + ((uint64_t*) p)[1] = x; + } + + return 0; +} + +static int message_append_reply_cookie(sd_bus_message *m, uint64_t cookie) { + assert(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) + return message_append_field_uint64(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, cookie); + else { + /* 64bit cookies are not supported on dbus1 */ + if (cookie > 0xffffffffUL) + return -EOPNOTSUPP; + + return message_append_field_uint32(m, BUS_MESSAGE_HEADER_REPLY_SERIAL, (uint32_t) cookie); + } +} + +int bus_message_from_header( + sd_bus *bus, + void *header, + size_t header_accessible, + void *footer, + size_t footer_accessible, + size_t message_size, + int *fds, + unsigned n_fds, + const char *label, + size_t extra, + sd_bus_message **ret) { + + _cleanup_free_ sd_bus_message *m = NULL; + struct bus_header *h; + size_t a, label_sz; + + assert(bus); + assert(header || header_accessible <= 0); + assert(footer || footer_accessible <= 0); + assert(fds || n_fds <= 0); + assert(ret); + + if (header_accessible < sizeof(struct bus_header)) + return -EBADMSG; + + if (header_accessible > message_size) + return -EBADMSG; + if (footer_accessible > message_size) + return -EBADMSG; + + h = header; + if (!IN_SET(h->version, 1, 2)) + return -EBADMSG; + + if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID) + return -EBADMSG; + + if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN)) + return -EBADMSG; + + /* Note that we are happy with unknown flags in the flags header! */ + + a = ALIGN(sizeof(sd_bus_message)) + ALIGN(extra); + + if (label) { + label_sz = strlen(label); + a += label_sz + 1; + } + + m = malloc0(a); + if (!m) + return -ENOMEM; + + m->n_ref = 1; + m->sealed = true; + m->header = header; + m->header_accessible = header_accessible; + m->footer = footer; + m->footer_accessible = footer_accessible; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t ws; + + if (h->dbus2.cookie == 0) + return -EBADMSG; + + /* dbus2 derives the sizes from the message size and + the offset table at the end, since it is formatted as + gvariant "yyyyuta{tv}v". Since the message itself is a + structure with precisely to variable sized entries, + there's only one offset in the table, which marks the + end of the fields array. */ + + ws = bus_gvariant_determine_word_size(message_size, 0); + if (footer_accessible < ws) + return -EBADMSG; + + m->fields_size = bus_gvariant_read_word_le((uint8_t*) footer + footer_accessible - ws, ws); + if (ALIGN8(m->fields_size) > message_size - ws) + return -EBADMSG; + if (m->fields_size < sizeof(struct bus_header)) + return -EBADMSG; + + m->fields_size -= sizeof(struct bus_header); + m->body_size = message_size - (sizeof(struct bus_header) + ALIGN8(m->fields_size)); + } else { + if (h->dbus1.serial == 0) + return -EBADMSG; + + /* dbus1 has the sizes in the header */ + m->fields_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.fields_size); + m->body_size = BUS_MESSAGE_BSWAP32(m, h->dbus1.body_size); + + if (sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size != message_size) + return -EBADMSG; + } + + m->fds = fds; + m->n_fds = n_fds; + + if (label) { + m->creds.label = (char*) m + ALIGN(sizeof(sd_bus_message)) + ALIGN(extra); + memcpy(m->creds.label, label, label_sz + 1); + + m->creds.mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + + m->bus = sd_bus_ref(bus); + *ret = m; + m = NULL; + + return 0; +} + +int bus_message_from_malloc( + sd_bus *bus, + void *buffer, + size_t length, + int *fds, + unsigned n_fds, + const char *label, + sd_bus_message **ret) { + + sd_bus_message *m; + size_t sz; + int r; + + r = bus_message_from_header( + bus, + buffer, length, /* in this case the initial bytes and the final bytes are the same */ + buffer, length, + length, + fds, n_fds, + label, + 0, &m); + if (r < 0) + return r; + + sz = length - sizeof(struct bus_header) - ALIGN8(m->fields_size); + if (sz > 0) { + m->n_body_parts = 1; + m->body.data = (uint8_t*) buffer + sizeof(struct bus_header) + ALIGN8(m->fields_size); + m->body.size = sz; + m->body.sealed = true; + m->body.memfd = -1; + } + + m->n_iovec = 1; + m->iovec = m->iovec_fixed; + m->iovec[0].iov_base = buffer; + m->iovec[0].iov_len = length; + + r = bus_message_parse_fields(m); + if (r < 0) + goto fail; + + /* We take possession of the memory and fds now */ + m->free_header = true; + m->free_fds = true; + + *ret = m; + return 0; + +fail: + message_free(m); + return r; +} + +static sd_bus_message *message_new(sd_bus *bus, uint8_t type) { + sd_bus_message *m; + + assert(bus); + + m = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header)); + if (!m) + return NULL; + + m->n_ref = 1; + m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message))); + m->header->endian = BUS_NATIVE_ENDIAN; + m->header->type = type; + m->header->version = bus->message_version; + m->allow_fds = bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING); + m->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(m); + m->bus = sd_bus_ref(bus); + + if (bus->allow_interactive_authorization) + m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; + + return m; +} + +_public_ int sd_bus_message_new_signal( + sd_bus *bus, + sd_bus_message **m, + const char *path, + const char *interface, + const char *member) { + + sd_bus_message *t; + int r; + + assert_return(bus, -ENOTCONN); + assert_return(bus->state != BUS_UNSET, -ENOTCONN); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(interface_name_is_valid(interface), -EINVAL); + assert_return(member_name_is_valid(member), -EINVAL); + assert_return(m, -EINVAL); + + t = message_new(bus, SD_BUS_MESSAGE_SIGNAL); + if (!t) + return -ENOMEM; + + t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + + r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path); + if (r < 0) + goto fail; + r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface); + if (r < 0) + goto fail; + r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member); + if (r < 0) + goto fail; + + *m = t; + return 0; + +fail: + sd_bus_message_unref(t); + return r; +} + +_public_ int sd_bus_message_new_method_call( + sd_bus *bus, + sd_bus_message **m, + const char *destination, + const char *path, + const char *interface, + const char *member) { + + sd_bus_message *t; + int r; + + assert_return(bus, -ENOTCONN); + assert_return(bus->state != BUS_UNSET, -ENOTCONN); + assert_return(!destination || service_name_is_valid(destination), -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!interface || interface_name_is_valid(interface), -EINVAL); + assert_return(member_name_is_valid(member), -EINVAL); + assert_return(m, -EINVAL); + + t = message_new(bus, SD_BUS_MESSAGE_METHOD_CALL); + if (!t) + return -ENOMEM; + + r = message_append_field_string(t, BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path); + if (r < 0) + goto fail; + r = message_append_field_string(t, BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member); + if (r < 0) + goto fail; + + if (interface) { + r = message_append_field_string(t, BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface); + if (r < 0) + goto fail; + } + + if (destination) { + r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination); + if (r < 0) + goto fail; + } + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + +static int message_new_reply( + sd_bus_message *call, + uint8_t type, + sd_bus_message **m) { + + sd_bus_message *t; + int r; + + assert_return(call, -EINVAL); + assert_return(call->sealed, -EPERM); + assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(call->bus->state != BUS_UNSET, -ENOTCONN); + assert_return(m, -EINVAL); + + t = message_new(call->bus, type); + if (!t) + return -ENOMEM; + + t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + t->reply_cookie = BUS_MESSAGE_COOKIE(call); + if (t->reply_cookie == 0) + return -EOPNOTSUPP; + + r = message_append_reply_cookie(t, t->reply_cookie); + if (r < 0) + goto fail; + + if (call->sender) { + r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination); + if (r < 0) + goto fail; + } + + t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED); + t->enforced_reply_signature = call->enforced_reply_signature; + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + +_public_ int sd_bus_message_new_method_return( + sd_bus_message *call, + sd_bus_message **m) { + + return message_new_reply(call, SD_BUS_MESSAGE_METHOD_RETURN, m); +} + +_public_ int sd_bus_message_new_method_error( + sd_bus_message *call, + sd_bus_message **m, + const sd_bus_error *e) { + + sd_bus_message *t; + int r; + + assert_return(sd_bus_error_is_set(e), -EINVAL); + assert_return(m, -EINVAL); + + r = message_new_reply(call, SD_BUS_MESSAGE_METHOD_ERROR, &t); + if (r < 0) + return r; + + r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name); + if (r < 0) + goto fail; + + if (e->message) { + r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message); + if (r < 0) + goto fail; + } + + t->error._need_free = -1; + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + +_public_ int sd_bus_message_new_method_errorf( + sd_bus_message *call, + sd_bus_message **m, + const char *name, + const char *format, + ...) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + va_list ap; + + assert_return(name, -EINVAL); + assert_return(m, -EINVAL); + + va_start(ap, format); + bus_error_setfv(&error, name, format, ap); + va_end(ap); + + return sd_bus_message_new_method_error(call, m, &error); +} + +_public_ int sd_bus_message_new_method_errno( + sd_bus_message *call, + sd_bus_message **m, + int error, + const sd_bus_error *p) { + + _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); + + sd_bus_error_set_errno(&berror, error); + + return sd_bus_message_new_method_error(call, m, &berror); +} + +_public_ int sd_bus_message_new_method_errnof( + sd_bus_message *call, + sd_bus_message **m, + int error, + const char *format, + ...) { + + _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; + va_list ap; + + va_start(ap, format); + sd_bus_error_set_errnofv(&berror, error, format, ap); + va_end(ap); + + return sd_bus_message_new_method_error(call, m, &berror); +} + +void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus.Local"; + m->creds.well_known_names_local = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + +void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; + m->creds.well_known_names_driver = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + +int bus_message_new_synthetic_error( + sd_bus *bus, + uint64_t cookie, + const sd_bus_error *e, + sd_bus_message **m) { + + sd_bus_message *t; + int r; + + assert(bus); + assert(sd_bus_error_is_set(e)); + assert(m); + + t = message_new(bus, SD_BUS_MESSAGE_METHOD_ERROR); + if (!t) + return -ENOMEM; + + t->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + t->reply_cookie = cookie; + + r = message_append_reply_cookie(t, t->reply_cookie); + if (r < 0) + goto fail; + + if (bus && bus->unique_name) { + r = message_append_field_string(t, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination); + if (r < 0) + goto fail; + } + + r = message_append_field_string(t, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name); + if (r < 0) + goto fail; + + if (e->message) { + r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message); + if (r < 0) + goto fail; + } + + t->error._need_free = -1; + + bus_message_set_sender_driver(bus, t); + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + +_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { + + if (!m) + return NULL; + + assert(m->n_ref > 0); + m->n_ref++; + + return m; +} + +_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) { + + if (!m) + return NULL; + + assert(m->n_ref > 0); + m->n_ref--; + + if (m->n_ref > 0) + return NULL; + + message_free(m); + return NULL; +} + +_public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) { + assert_return(m, -EINVAL); + assert_return(type, -EINVAL); + + *type = m->header->type; + return 0; +} + +_public_ int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie) { + uint64_t c; + + assert_return(m, -EINVAL); + assert_return(cookie, -EINVAL); + + c = BUS_MESSAGE_COOKIE(m); + if (c == 0) + return -ENODATA; + + *cookie = BUS_MESSAGE_COOKIE(m); + return 0; +} + +_public_ int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie) { + assert_return(m, -EINVAL); + assert_return(cookie, -EINVAL); + + if (m->reply_cookie == 0) + return -ENODATA; + + *cookie = m->reply_cookie; + return 0; +} + +_public_ int sd_bus_message_get_expect_reply(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && + !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED); +} + +_public_ int sd_bus_message_get_auto_start(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return !(m->header->flags & BUS_MESSAGE_NO_AUTO_START); +} + +_public_ int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return m->header->type == SD_BUS_MESSAGE_METHOD_CALL && + (m->header->flags & BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION); +} + +_public_ const char *sd_bus_message_get_path(sd_bus_message *m) { + assert_return(m, NULL); + + return m->path; +} + +_public_ const char *sd_bus_message_get_interface(sd_bus_message *m) { + assert_return(m, NULL); + + return m->interface; +} + +_public_ const char *sd_bus_message_get_member(sd_bus_message *m) { + assert_return(m, NULL); + + return m->member; +} + +_public_ const char *sd_bus_message_get_destination(sd_bus_message *m) { + assert_return(m, NULL); + + return m->destination; +} + +_public_ const char *sd_bus_message_get_sender(sd_bus_message *m) { + assert_return(m, NULL); + + return m->sender; +} + +_public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) { + assert_return(m, NULL); + + if (!sd_bus_error_is_set(&m->error)) + return NULL; + + return &m->error; +} + +_public_ int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec) { + assert_return(m, -EINVAL); + assert_return(usec, -EINVAL); + + if (m->monotonic <= 0) + return -ENODATA; + + *usec = m->monotonic; + return 0; +} + +_public_ int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec) { + assert_return(m, -EINVAL); + assert_return(usec, -EINVAL); + + if (m->realtime <= 0) + return -ENODATA; + + *usec = m->realtime; + return 0; +} + +_public_ int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum) { + assert_return(m, -EINVAL); + assert_return(seqnum, -EINVAL); + + if (m->seqnum <= 0) + return -ENODATA; + + *seqnum = m->seqnum; + return 0; +} + +_public_ sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m) { + assert_return(m, NULL); + + if (m->creds.mask == 0) + return NULL; + + return &m->creds; +} + +_public_ int sd_bus_message_is_signal( + sd_bus_message *m, + const char *interface, + const char *member) { + + assert_return(m, -EINVAL); + + if (m->header->type != SD_BUS_MESSAGE_SIGNAL) + return 0; + + if (interface && (!m->interface || !streq(m->interface, interface))) + return 0; + + if (member && (!m->member || !streq(m->member, member))) + return 0; + + return 1; +} + +_public_ int sd_bus_message_is_method_call( + sd_bus_message *m, + const char *interface, + const char *member) { + + assert_return(m, -EINVAL); + + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) + return 0; + + if (interface && (!m->interface || !streq(m->interface, interface))) + return 0; + + if (member && (!m->member || !streq(m->member, member))) + return 0; + + return 1; +} + +_public_ int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) { + assert_return(m, -EINVAL); + + if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) + return 0; + + if (name && (!m->error.name || !streq(m->error.name, name))) + return 0; + + return 1; +} + +_public_ int sd_bus_message_set_expect_reply(sd_bus_message *m, int b) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EPERM); + + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED, !b); + + return 0; +} + +_public_ int sd_bus_message_set_auto_start(sd_bus_message *m, int b) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_AUTO_START, !b); + + return 0; +} + +_public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + SET_FLAG(m->header->flags, BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION, b); + + return 0; +} + +static struct bus_container *message_get_container(sd_bus_message *m) { + assert(m); + + if (m->n_containers == 0) + return &m->root_container; + + assert(m->containers); + return m->containers + m->n_containers - 1; +} + +struct bus_body_part *message_append_part(sd_bus_message *m) { + struct bus_body_part *part; + + assert(m); + + if (m->poisoned) + return NULL; + + if (m->n_body_parts <= 0) { + part = &m->body; + zero(*part); + } else { + assert(m->body_end); + + part = new0(struct bus_body_part, 1); + if (!part) { + m->poisoned = true; + return NULL; + } + + m->body_end->next = part; + } + + part->memfd = -1; + m->body_end = part; + m->n_body_parts++; + + return part; +} + +static void part_zero(struct bus_body_part *part, size_t sz) { + assert(part); + assert(sz > 0); + assert(sz < 8); + + /* All other fields can be left in their defaults */ + assert(!part->data); + assert(part->memfd < 0); + + part->size = sz; + part->is_zero = true; + part->sealed = true; +} + +static int part_make_space( + struct sd_bus_message *m, + struct bus_body_part *part, + size_t sz, + void **q) { + + void *n; + int r; + + assert(m); + assert(part); + assert(!part->sealed); + + if (m->poisoned) + return -ENOMEM; + + if (!part->data && part->memfd < 0) { + part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated); + part->mmap_begin = part->data; + } + + if (part->memfd >= 0) { + + if (part->allocated == 0 || sz > part->allocated) { + uint64_t new_allocated; + + new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1); + r = memfd_set_size(part->memfd, new_allocated); + if (r < 0) { + m->poisoned = true; + return r; + } + + part->allocated = new_allocated; + } + + if (!part->data || sz > part->mapped) { + size_t psz; + + psz = PAGE_ALIGN(sz > 0 ? sz : 1); + if (part->mapped <= 0) + n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); + else + n = mremap(part->mmap_begin, part->mapped, psz, MREMAP_MAYMOVE); + + if (n == MAP_FAILED) { + m->poisoned = true; + return -errno; + } + + part->mmap_begin = part->data = n; + part->mapped = psz; + part->memfd_offset = 0; + } + + part->munmap_this = true; + } else { + if (part->allocated == 0 || sz > part->allocated) { + size_t new_allocated; + + new_allocated = sz > 0 ? 2 * sz : 64; + n = realloc(part->data, new_allocated); + if (!n) { + m->poisoned = true; + return -ENOMEM; + } + + part->data = n; + part->allocated = new_allocated; + part->free_this = true; + } + } + + if (q) + *q = part->data ? (uint8_t*) part->data + part->size : NULL; + + part->size = sz; + return 0; +} + +static int message_add_offset(sd_bus_message *m, size_t offset) { + struct bus_container *c; + + assert(m); + assert(BUS_MESSAGE_IS_GVARIANT(m)); + + /* Add offset to current container, unless this is the first + * item in it, which will have the 0 offset, which we can + * ignore. */ + c = message_get_container(m); + + if (!c->need_offsets) + return 0; + + if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1)) + return -ENOMEM; + + c->offsets[c->n_offsets++] = offset; + return 0; +} + +static void message_extend_containers(sd_bus_message *m, size_t expand) { + struct bus_container *c; + + assert(m); + + if (expand <= 0) + return; + + /* Update counters */ + for (c = m->containers; c < m->containers + m->n_containers; c++) { + + if (c->array_size) + *c->array_size += expand; + } +} + +static void *message_extend_body( + sd_bus_message *m, + size_t align, + size_t sz, + bool add_offset, + bool force_inline) { + + size_t start_body, end_body, padding, added; + void *p; + int r; + + assert(m); + assert(align > 0); + assert(!m->sealed); + + if (m->poisoned) + return NULL; + + start_body = ALIGN_TO((size_t) m->body_size, align); + end_body = start_body + sz; + + padding = start_body - m->body_size; + added = padding + sz; + + /* Check for 32bit overflows */ + if (end_body > (size_t) ((uint32_t) -1) || + end_body < start_body) { + m->poisoned = true; + return NULL; + } + + if (added > 0) { + struct bus_body_part *part = NULL; + bool add_new_part; + + add_new_part = + m->n_body_parts <= 0 || + m->body_end->sealed || + (padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size) || + (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */ + + if (add_new_part) { + if (padding > 0) { + part = message_append_part(m); + if (!part) + return NULL; + + part_zero(part, padding); + } + + part = message_append_part(m); + if (!part) + return NULL; + + r = part_make_space(m, part, sz, &p); + if (r < 0) + return NULL; + } else { + struct bus_container *c; + void *op; + size_t os, start_part, end_part; + + part = m->body_end; + op = part->data; + os = part->size; + + start_part = ALIGN_TO(part->size, align); + end_part = start_part + sz; + + r = part_make_space(m, part, end_part, &p); + if (r < 0) + return NULL; + + if (padding > 0) { + memzero(p, padding); + p = (uint8_t*) p + padding; + } + + /* Readjust pointers */ + for (c = m->containers; c < m->containers + m->n_containers; c++) + c->array_size = adjust_pointer(c->array_size, op, os, part->data); + + m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); + } + } else + /* Return something that is not NULL and is aligned */ + p = (uint8_t *) NULL + align; + + m->body_size = end_body; + message_extend_containers(m, added); + + if (add_offset) { + r = message_add_offset(m, end_body); + if (r < 0) { + m->poisoned = true; + return NULL; + } + } + + return p; +} + +static int message_push_fd(sd_bus_message *m, int fd) { + int *f, copy; + + assert(m); + + if (fd < 0) + return -EINVAL; + + if (!m->allow_fds) + return -EOPNOTSUPP; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; + + f = realloc(m->fds, sizeof(int) * (m->n_fds + 1)); + if (!f) { + m->poisoned = true; + safe_close(copy); + return -ENOMEM; + } + + m->fds = f; + m->fds[m->n_fds] = copy; + m->free_fds = true; + + return copy; +} + +int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) { + _cleanup_close_ int fd = -1; + struct bus_container *c; + ssize_t align, sz; + void *a; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(bus_type_is_basic(type), -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + c = message_get_container(m); + + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ + + if (c->signature[c->index] != type) + return -ENXIO; + } else { + char *e; + + /* Maybe we can append to the signature? But only if this is the top-level container */ + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(type), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + uint8_t u8; + uint32_t u32; + + switch (type) { + + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_STRING: + p = strempty(p); + + /* Fall through... */ + case SD_BUS_TYPE_OBJECT_PATH: + if (!p) + return -EINVAL; + + align = 1; + sz = strlen(p) + 1; + break; + + case SD_BUS_TYPE_BOOLEAN: + + u8 = p && *(int*) p; + p = &u8; + + align = sz = 1; + break; + + case SD_BUS_TYPE_UNIX_FD: + + if (!p) + return -EINVAL; + + fd = message_push_fd(m, *(int*) p); + if (fd < 0) + return fd; + + u32 = m->n_fds; + p = &u32; + + align = sz = 4; + break; + + default: + align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); + sz = bus_gvariant_get_size(CHAR_TO_STR(type)); + break; + } + + assert(align > 0); + assert(sz > 0); + + a = message_extend_body(m, align, sz, true, false); + if (!a) + return -ENOMEM; + + memcpy(a, p, sz); + + if (stored) + *stored = (const uint8_t*) a; + + } else { + uint32_t u32; + + switch (type) { + + case SD_BUS_TYPE_STRING: + /* To make things easy we'll serialize a NULL string + * into the empty string */ + p = strempty(p); + + /* Fall through... */ + case SD_BUS_TYPE_OBJECT_PATH: + + if (!p) + return -EINVAL; + + align = 4; + sz = 4 + strlen(p) + 1; + break; + + case SD_BUS_TYPE_SIGNATURE: + + p = strempty(p); + + align = 1; + sz = 1 + strlen(p) + 1; + break; + + case SD_BUS_TYPE_BOOLEAN: + + u32 = p && *(int*) p; + p = &u32; + + align = sz = 4; + break; + + case SD_BUS_TYPE_UNIX_FD: + + if (!p) + return -EINVAL; + + fd = message_push_fd(m, *(int*) p); + if (fd < 0) + return fd; + + u32 = m->n_fds; + p = &u32; + + align = sz = 4; + break; + + default: + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + break; + } + + assert(align > 0); + assert(sz > 0); + + a = message_extend_body(m, align, sz, false, false); + if (!a) + return -ENOMEM; + + if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) { + *(uint32_t*) a = sz - 5; + memcpy((uint8_t*) a + 4, p, sz - 4); + + if (stored) + *stored = (const uint8_t*) a + 4; + + } else if (type == SD_BUS_TYPE_SIGNATURE) { + *(uint8_t*) a = sz - 2; + memcpy((uint8_t*) a + 1, p, sz - 1); + + if (stored) + *stored = (const uint8_t*) a + 1; + } else { + memcpy(a, p, sz); + + if (stored) + *stored = a; + } + } + + if (type == SD_BUS_TYPE_UNIX_FD) + m->n_fds++; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + fd = -1; + return 0; +} + +_public_ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) { + return message_append_basic(m, type, p, NULL); +} + +_public_ int sd_bus_message_append_string_space( + sd_bus_message *m, + size_t size, + char **s) { + + struct bus_container *c; + void *a; + + assert_return(m, -EINVAL); + assert_return(s, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + c = message_get_container(m); + + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ + + if (c->signature[c->index] != SD_BUS_TYPE_STRING) + return -ENXIO; + } else { + char *e; + + /* Maybe we can append to the signature? But only if this is the top-level container */ + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + a = message_extend_body(m, 1, size + 1, true, false); + if (!a) + return -ENOMEM; + + *s = a; + } else { + a = message_extend_body(m, 4, 4 + size + 1, false, false); + if (!a) + return -ENOMEM; + + *(uint32_t*) a = size; + *s = (char*) a + 4; + } + + (*s)[size] = 0; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 0; +} + +_public_ int sd_bus_message_append_string_iovec( + sd_bus_message *m, + const struct iovec *iov, + unsigned n) { + + size_t size; + unsigned i; + char *p; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(iov || n == 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + size = IOVEC_TOTAL_SIZE(iov, n); + + r = sd_bus_message_append_string_space(m, size, &p); + if (r < 0) + return r; + + for (i = 0; i < n; i++) { + + if (iov[i].iov_base) + memcpy(p, iov[i].iov_base, iov[i].iov_len); + else + memset(p, ' ', iov[i].iov_len); + + p += iov[i].iov_len; + } + + return 0; +} + +static int bus_message_open_array( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + uint32_t **array_size, + size_t *begin, + bool *need_offsets) { + + unsigned nindex; + int alignment, r; + + assert(m); + assert(c); + assert(contents); + assert(array_size); + assert(begin); + assert(need_offsets); + + if (!signature_is_single(contents, true)) + return -EINVAL; + + if (c->signature && c->signature[c->index]) { + + /* Verify the existing signature */ + + if (c->signature[c->index] != SD_BUS_TYPE_ARRAY) + return -ENXIO; + + if (!startswith(c->signature + c->index + 1, contents)) + return -ENXIO; + + nindex = c->index + 1 + strlen(contents); + } else { + char *e; + + if (c->enclosing != 0) + return -ENXIO; + + /* Extend the existing signature */ + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + + nindex = e - c->signature; + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; + + /* Add alignment padding and add to offset list */ + if (!message_extend_body(m, alignment, 0, false, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->body_size; + *need_offsets = r == 0; + } else { + void *a, *op; + size_t os; + struct bus_body_part *o; + + alignment = bus_type_get_alignment(contents[0]); + if (alignment < 0) + return alignment; + + a = message_extend_body(m, 4, 4, false, false); + if (!a) + return -ENOMEM; + + o = m->body_end; + op = m->body_end->data; + os = m->body_end->size; + + /* Add alignment between size and first element */ + if (!message_extend_body(m, alignment, 0, false, false)) + return -ENOMEM; + + /* location of array size might have changed so let's readjust a */ + if (o == m->body_end) + a = adjust_pointer(a, op, os, m->body_end->data); + + *(uint32_t*) a = 0; + *array_size = a; + } + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index = nindex; + + return 0; +} + +static int bus_message_open_variant( + sd_bus_message *m, + struct bus_container *c, + const char *contents) { + + assert(m); + assert(c); + assert(contents); + + if (!signature_is_single(contents, false)) + return -EINVAL; + + if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) + return -EINVAL; + + if (c->signature && c->signature[c->index]) { + + if (c->signature[c->index] != SD_BUS_TYPE_VARIANT) + return -ENXIO; + + } else { + char *e; + + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* Variants are always aligned to 8 */ + + if (!message_extend_body(m, 8, 0, false, false)) + return -ENOMEM; + + } else { + size_t l; + void *a; + + l = strlen(contents); + a = message_extend_body(m, 1, 1 + l + 1, false, false); + if (!a) + return -ENOMEM; + + *(uint8_t*) a = l; + memcpy((uint8_t*) a + 1, contents, l + 1); + } + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 0; +} + +static int bus_message_open_struct( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *begin, + bool *need_offsets) { + + size_t nindex; + int r; + + assert(m); + assert(c); + assert(contents); + assert(begin); + assert(need_offsets); + + if (!signature_is_valid(contents, false)) + return -EINVAL; + + if (c->signature && c->signature[c->index]) { + size_t l; + + l = strlen(contents); + + if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN || + !startswith(c->signature + c->index + 1, contents) || + c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END) + return -ENXIO; + + nindex = c->index + 1 + l + 1; + } else { + char *e; + + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + + nindex = e - c->signature; + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + int alignment; + + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; + + if (!message_extend_body(m, alignment, 0, false, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false, false)) + return -ENOMEM; + } + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index = nindex; + + return 0; +} + +static int bus_message_open_dict_entry( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *begin, + bool *need_offsets) { + + int r; + + assert(m); + assert(c); + assert(contents); + assert(begin); + assert(need_offsets); + + if (!signature_is_pair(contents)) + return -EINVAL; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + return -ENXIO; + + if (c->signature && c->signature[c->index]) { + size_t l; + + l = strlen(contents); + + if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN || + !startswith(c->signature + c->index + 1, contents) || + c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END) + return -ENXIO; + } else + return -ENXIO; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + int alignment; + + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; + + if (!message_extend_body(m, alignment, 0, false, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false, false)) + return -ENOMEM; + } + + return 0; +} + +_public_ int sd_bus_message_open_container( + sd_bus_message *m, + char type, + const char *contents) { + + struct bus_container *c, *w; + uint32_t *array_size = NULL; + char *signature; + size_t before, begin = 0; + bool need_offsets = false; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(contents, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + /* Make sure we have space for one more container */ + if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) { + m->poisoned = true; + return -ENOMEM; + } + + c = message_get_container(m); + + signature = strdup(contents); + if (!signature) { + m->poisoned = true; + return -ENOMEM; + } + + /* Save old index in the parent container, in case we have to + * abort this container */ + c->saved_index = c->index; + before = m->body_size; + + if (type == SD_BUS_TYPE_ARRAY) + r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets); + else if (type == SD_BUS_TYPE_VARIANT) + r = bus_message_open_variant(m, c, contents); + else if (type == SD_BUS_TYPE_STRUCT) + r = bus_message_open_struct(m, c, contents, &begin, &need_offsets); + else if (type == SD_BUS_TYPE_DICT_ENTRY) + r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets); + else + r = -EINVAL; + + if (r < 0) { + free(signature); + return r; + } + + /* OK, let's fill it in */ + w = m->containers + m->n_containers++; + w->enclosing = type; + w->signature = signature; + w->index = 0; + w->array_size = array_size; + w->before = before; + w->begin = begin; + w->n_offsets = w->offsets_allocated = 0; + w->offsets = NULL; + w->need_offsets = need_offsets; + + return 0; +} + +static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) { + + assert(m); + assert(c); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) + return 0; + + if (c->need_offsets) { + size_t payload, sz, i; + uint8_t *a; + + /* Variable-width arrays */ + + payload = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0; + sz = bus_gvariant_determine_word_size(payload, c->n_offsets); + + a = message_extend_body(m, 1, sz * c->n_offsets, true, false); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) + bus_gvariant_write_word_le(a + sz*i, sz, c->offsets[i] - c->begin); + } else { + void *a; + + /* Fixed-width or empty arrays */ + + a = message_extend_body(m, 1, 0, true, false); /* let's add offset to parent */ + if (!a) + return -ENOMEM; + } + + return 0; +} + +static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) { + uint8_t *a; + size_t l; + + assert(m); + assert(c); + assert(c->signature); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) + return 0; + + l = strlen(c->signature); + + a = message_extend_body(m, 1, 1 + l, true, false); + if (!a) + return -ENOMEM; + + a[0] = 0; + memcpy(a+1, c->signature, l); + + return 0; +} + +static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) { + bool fixed_size = true; + size_t n_variable = 0; + unsigned i = 0; + const char *p; + uint8_t *a; + int r; + + assert(m); + assert(c); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) + return 0; + + p = strempty(c->signature); + while (*p != 0) { + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + r = bus_gvariant_is_fixed_size(t); + if (r < 0) + return r; + } + + assert(!c->need_offsets || i <= c->n_offsets); + + /* We need to add an offset for each item that has a + * variable size and that is not the last one in the + * list */ + if (r == 0) + fixed_size = false; + if (r == 0 && p[n] != 0) + n_variable++; + + i++; + p += n; + } + + assert(!c->need_offsets || i == c->n_offsets); + assert(c->need_offsets || n_variable == 0); + + if (isempty(c->signature)) { + /* The unary type is encoded as fixed 1 byte padding */ + a = message_extend_body(m, 1, 1, add_offset, false); + if (!a) + return -ENOMEM; + + *a = 0; + } else if (n_variable <= 0) { + int alignment = 1; + + /* Structures with fixed-size members only have to be + * fixed-size themselves. But gvariant requires all fixed-size + * elements to be sized a multiple of their alignment. Hence, + * we must *always* add final padding after the last member so + * the overall size of the structure is properly aligned. */ + if (fixed_size) + alignment = bus_gvariant_get_alignment(strempty(c->signature)); + + assert(alignment > 0); + + a = message_extend_body(m, alignment, 0, add_offset, false); + if (!a) + return -ENOMEM; + } else { + size_t sz; + unsigned j; + + assert(c->offsets[c->n_offsets-1] == m->body_size); + + sz = bus_gvariant_determine_word_size(m->body_size - c->begin, n_variable); + + a = message_extend_body(m, 1, sz * n_variable, add_offset, false); + if (!a) + return -ENOMEM; + + p = strempty(c->signature); + for (i = 0, j = 0; i < c->n_offsets; i++) { + unsigned k; + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + p += n; + + r = bus_gvariant_is_fixed_size(t); + if (r < 0) + return r; + if (r > 0 || p[0] == 0) + continue; + } + + k = n_variable - 1 - j; + + bus_gvariant_write_word_le(a + k * sz, sz, c->offsets[i] - c->begin); + + j++; + } + } + + return 0; +} + +_public_ int sd_bus_message_close_container(sd_bus_message *m) { + struct bus_container *c; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers > 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + c = message_get_container(m); + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + if (c->signature && c->signature[c->index] != 0) + return -EINVAL; + + m->n_containers--; + + if (c->enclosing == SD_BUS_TYPE_ARRAY) + r = bus_message_close_array(m, c); + else if (c->enclosing == SD_BUS_TYPE_VARIANT) + r = bus_message_close_variant(m, c); + else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY) + r = bus_message_close_struct(m, c, true); + else + assert_not_reached("Unknown container type"); + + free(c->signature); + free(c->offsets); + + return r; +} + +typedef struct { + const char *types; + unsigned n_struct; + unsigned n_array; +} TypeStack; + +static int type_stack_push(TypeStack *stack, unsigned max, unsigned *i, const char *types, unsigned n_struct, unsigned n_array) { + assert(stack); + assert(max > 0); + + if (*i >= max) + return -EINVAL; + + stack[*i].types = types; + stack[*i].n_struct = n_struct; + stack[*i].n_array = n_array; + (*i)++; + + return 0; +} + +static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const char **types, unsigned *n_struct, unsigned *n_array) { + assert(stack); + assert(max > 0); + assert(types); + assert(n_struct); + assert(n_array); + + if (*i <= 0) + return 0; + + (*i)--; + *types = stack[*i].types; + *n_struct = stack[*i].n_struct; + *n_array = stack[*i].n_array; + + return 1; +} + +int bus_message_append_ap( + sd_bus_message *m, + const char *types, + va_list ap) { + + unsigned n_array, n_struct; + TypeStack stack[BUS_CONTAINER_DEPTH]; + unsigned stack_ptr = 0; + int r; + + assert(m); + + if (!types) + return 0; + + n_array = (unsigned) -1; + n_struct = strlen(types); + + for (;;) { + const char *t; + + if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) { + r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array); + if (r < 0) + return r; + if (r == 0) + break; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + continue; + } + + t = types; + if (n_array != (unsigned) -1) + n_array--; + else { + types++; + n_struct--; + } + + switch (*t) { + + case SD_BUS_TYPE_BYTE: { + uint8_t x; + + x = (uint8_t) va_arg(ap, int); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_UNIX_FD: { + uint32_t x; + + /* We assume a boolean is the same as int32_t */ + assert_cc(sizeof(int32_t) == sizeof(int)); + + x = va_arg(ap, uint32_t); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: { + uint16_t x; + + x = (uint16_t) va_arg(ap, int); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: { + uint64_t x; + + x = va_arg(ap, uint64_t); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_DOUBLE: { + double x; + + x = va_arg(ap, double); + r = sd_bus_message_append_basic(m, *t, &x); + break; + } + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: { + const char *x; + + x = va_arg(ap, const char*); + r = sd_bus_message_append_basic(m, *t, x); + break; + } + + case SD_BUS_TYPE_ARRAY: { + size_t k; + + r = signature_element_length(t + 1, &k); + if (r < 0) + return r; + + { + char s[k + 1]; + memcpy(s, t + 1, k); + s[k] = 0; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); + if (r < 0) + return r; + } + + if (n_array == (unsigned) -1) { + types += k; + n_struct -= k; + } + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = t + 1; + n_struct = k; + n_array = va_arg(ap, unsigned); + + break; + } + + case SD_BUS_TYPE_VARIANT: { + const char *s; + + s = va_arg(ap, const char*); + if (!s) + return -EINVAL; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s); + if (r < 0) + return r; + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = s; + n_struct = strlen(s); + n_array = (unsigned) -1; + + break; + } + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + r = signature_element_length(t, &k); + if (r < 0) + return r; + + { + char s[k - 1]; + + memcpy(s, t + 1, k - 2); + s[k - 2] = 0; + + r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r < 0) + return r; + } + + if (n_array == (unsigned) -1) { + types += k - 1; + n_struct -= k - 1; + } + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = t + 1; + n_struct = k - 2; + n_array = (unsigned) -1; + + break; + } + + default: + r = -EINVAL; + } + + if (r < 0) + return r; + } + + return 1; +} + +_public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) { + va_list ap; + int r; + + assert_return(m, -EINVAL); + assert_return(types, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + va_start(ap, types); + r = bus_message_append_ap(m, types, ap); + va_end(ap); + + return r; +} + +_public_ int sd_bus_message_append_array_space( + sd_bus_message *m, + char type, + size_t size, + void **ptr) { + + ssize_t align, sz; + void *a; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL); + assert_return(ptr || size == 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + /* alignment and size of the trivial types (except bool) is + * identical for gvariant and dbus1 marshalling */ + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + + assert_se(align > 0); + assert_se(sz > 0); + + if (size % sz != 0) + return -EINVAL; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); + if (r < 0) + return r; + + a = message_extend_body(m, align, size, false, false); + if (!a) + return -ENOMEM; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + *ptr = a; + return 0; +} + +_public_ int sd_bus_message_append_array( + sd_bus_message *m, + char type, + const void *ptr, + size_t size) { + int r; + void *p; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(ptr || size == 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + r = sd_bus_message_append_array_space(m, type, size, &p); + if (r < 0) + return r; + + memcpy_safe(p, ptr, size); + + return 0; +} + +_public_ int sd_bus_message_append_array_iovec( + sd_bus_message *m, + char type, + const struct iovec *iov, + unsigned n) { + + size_t size; + unsigned i; + void *p; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(iov || n == 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + + size = IOVEC_TOTAL_SIZE(iov, n); + + r = sd_bus_message_append_array_space(m, type, size, &p); + if (r < 0) + return r; + + for (i = 0; i < n; i++) { + + if (iov[i].iov_base) + memcpy(p, iov[i].iov_base, iov[i].iov_len); + else + memzero(p, iov[i].iov_len); + + p = (uint8_t*) p + iov[i].iov_len; + } + + return 0; +} + +_public_ int sd_bus_message_append_array_memfd( + sd_bus_message *m, + char type, + int memfd, + uint64_t offset, + uint64_t size) { + + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + ssize_t align, sz; + uint64_t real_size; + void *a; + int r; + + assert_return(m, -EINVAL); + assert_return(memfd >= 0, -EBADF); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(size > 0, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + r = memfd_set_sealed(memfd); + if (r < 0) + return r; + + copy_fd = dup(memfd); + if (copy_fd < 0) + return copy_fd; + + r = memfd_get_size(memfd, &real_size); + if (r < 0) + return r; + + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + + assert_se(align > 0); + assert_se(sz > 0); + + if (offset % align != 0) + return -EINVAL; + + if (size % sz != 0) + return -EINVAL; + + if (size > (uint64_t) (uint32_t) -1) + return -EINVAL; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); + if (r < 0) + return r; + + a = message_extend_body(m, align, 0, false, false); + if (!a) + return -ENOMEM; + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->memfd_offset = offset; + part->sealed = true; + part->size = size; + copy_fd = -1; + + m->body_size += size; + message_extend_containers(m, size); + + return sd_bus_message_close_container(m); +} + +_public_ int sd_bus_message_append_string_memfd( + sd_bus_message *m, + int memfd, + uint64_t offset, + uint64_t size) { + + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + struct bus_container *c; + uint64_t real_size; + void *a; + int r; + + assert_return(m, -EINVAL); + assert_return(memfd >= 0, -EBADF); + assert_return(size > 0, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + r = memfd_set_sealed(memfd); + if (r < 0) + return r; + + copy_fd = dup(memfd); + if (copy_fd < 0) + return copy_fd; + + r = memfd_get_size(memfd, &real_size); + if (r < 0) + return r; + + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + + /* We require this to be NUL terminated */ + if (size == 0) + return -EINVAL; + + if (size > (uint64_t) (uint32_t) -1) + return -EINVAL; + + c = message_get_container(m); + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ + + if (c->signature[c->index] != SD_BUS_TYPE_STRING) + return -ENXIO; + } else { + char *e; + + /* Maybe we can append to the signature? But only if this is the top-level container */ + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + } + + if (!BUS_MESSAGE_IS_GVARIANT(m)) { + a = message_extend_body(m, 4, 4, false, false); + if (!a) + return -ENOMEM; + + *(uint32_t*) a = size - 1; + } + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->memfd_offset = offset; + part->sealed = true; + part->size = size; + copy_fd = -1; + + m->body_size += size; + message_extend_containers(m, size); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + r = message_add_offset(m, m->body_size); + if (r < 0) { + m->poisoned = true; + return -ENOMEM; + } + } + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 0; +} + +_public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) { + char **i; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return r; + + STRV_FOREACH(i, l) { + r = sd_bus_message_append_basic(m, 's', *i); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(m); +} + +static int bus_message_close_header(sd_bus_message *m) { + + assert(m); + + /* The actual user data is finished now, we just complete the + variant and struct now (at least on gvariant). Remember + this position, so that during parsing we know where to + put the outer container end. */ + m->user_body_size = m->body_size; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + const char *signature; + size_t sz, l; + void *d; + + /* Add offset table to end of fields array */ + if (m->n_header_offsets >= 1) { + uint8_t *a; + unsigned i; + + assert(m->fields_size == m->header_offsets[m->n_header_offsets-1]); + + sz = bus_gvariant_determine_word_size(m->fields_size, m->n_header_offsets); + a = message_extend_fields(m, 1, sz * m->n_header_offsets, false); + if (!a) + return -ENOMEM; + + for (i = 0; i < m->n_header_offsets; i++) + bus_gvariant_write_word_le(a + sz*i, sz, m->header_offsets[i]); + } + + /* Add gvariant NUL byte plus signature to the end of + * the body, followed by the final offset pointing to + * the end of the fields array */ + + signature = strempty(m->root_container.signature); + l = strlen(signature); + + sz = bus_gvariant_determine_word_size(sizeof(struct bus_header) + ALIGN8(m->fields_size) + m->body_size + 1 + l + 2, 1); + d = message_extend_body(m, 1, 1 + l + 2 + sz, false, true); + if (!d) + return -ENOMEM; + + *(uint8_t*) d = 0; + *((uint8_t*) d + 1) = SD_BUS_TYPE_STRUCT_BEGIN; + memcpy((uint8_t*) d + 2, signature, l); + *((uint8_t*) d + 1 + l + 1) = SD_BUS_TYPE_STRUCT_END; + + bus_gvariant_write_word_le((uint8_t*) d + 1 + l + 2, sz, sizeof(struct bus_header) + m->fields_size); + + m->footer = d; + m->footer_accessible = 1 + l + 2 + sz; + } else { + m->header->dbus1.fields_size = m->fields_size; + m->header->dbus1.body_size = m->body_size; + } + + return 0; +} + +int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { + struct bus_body_part *part; + size_t a; + unsigned i; + int r; + + assert(m); + + if (m->sealed) + return -EPERM; + + if (m->n_containers > 0) + return -EBADMSG; + + if (m->poisoned) + return -ESTALE; + + if (cookie > 0xffffffffULL && + !BUS_MESSAGE_IS_GVARIANT(m)) + return -EOPNOTSUPP; + + /* In vtables the return signature of method calls is listed, + * let's check if they match if this is a response */ + if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN && + m->enforced_reply_signature && + !streq(strempty(m->root_container.signature), m->enforced_reply_signature)) + return -ENOMSG; + + /* If gvariant marshalling is used we need to close the body structure */ + r = bus_message_close_struct(m, &m->root_container, false); + if (r < 0) + return r; + + /* If there's a non-trivial signature set, then add it in + * here, but only on dbus1 */ + if (!isempty(m->root_container.signature) && !BUS_MESSAGE_IS_GVARIANT(m)) { + r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL); + if (r < 0) + return r; + } + + if (m->n_fds > 0) { + r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds); + if (r < 0) + return r; + } + + r = bus_message_close_header(m); + if (r < 0) + return r; + + if (BUS_MESSAGE_IS_GVARIANT(m)) + m->header->dbus2.cookie = cookie; + else + m->header->dbus1.serial = (uint32_t) cookie; + + m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout; + + /* Add padding at the end of the fields part, since we know + * the body needs to start at an 8 byte alignment. We made + * sure we allocated enough space for this, so all we need to + * do here is to zero it out. */ + a = ALIGN8(m->fields_size) - m->fields_size; + if (a > 0) + memzero((uint8_t*) BUS_MESSAGE_FIELDS(m) + m->fields_size, a); + + /* If this is something we can send as memfd, then let's seal + the memfd now. Note that we can send memfds as payload only + for directed messages, and not for broadcasts. */ + if (m->destination && m->bus->use_memfd) { + MESSAGE_FOREACH_PART(part, i, m) + if (part->memfd >= 0 && + !part->sealed && + (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0) && + part != m->body_end) { /* The last part may never be sent as memfd */ + uint64_t sz; + + /* Try to seal it if that makes + * sense. First, unmap our own map to + * make sure we don't keep it busy. */ + bus_body_part_unmap(part); + + /* Then, sync up real memfd size */ + sz = part->size; + r = memfd_set_size(part->memfd, sz); + if (r < 0) + return r; + + /* Finally, try to seal */ + if (memfd_set_sealed(part->memfd) >= 0) + part->sealed = true; + } + } + + m->root_container.end = m->user_body_size; + m->root_container.index = 0; + m->root_container.offset_index = 0; + m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0; + + m->sealed = true; + + return 0; +} + +int bus_body_part_map(struct bus_body_part *part) { + void *p; + size_t psz, shift; + + assert_se(part); + + if (part->data) + return 0; + + if (part->size <= 0) + return 0; + + /* For smaller zero parts (as used for padding) we don't need to map anything... */ + if (part->memfd < 0 && part->is_zero && part->size < 8) { + static const uint8_t zeroes[7] = { }; + part->data = (void*) zeroes; + return 0; + } + + shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); + psz = PAGE_ALIGN(part->size + shift); + + if (part->memfd >= 0) + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift); + else if (part->is_zero) + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + else + return -EINVAL; + + if (p == MAP_FAILED) + return -errno; + + part->mapped = psz; + part->mmap_begin = p; + part->data = (uint8_t*) p + shift; + part->munmap_this = true; + + return 0; +} + +void bus_body_part_unmap(struct bus_body_part *part) { + + assert_se(part); + + if (part->memfd < 0) + return; + + if (!part->mmap_begin) + return; + + if (!part->munmap_this) + return; + + assert_se(munmap(part->mmap_begin, part->mapped) == 0); + + part->mmap_begin = NULL; + part->data = NULL; + part->mapped = 0; + part->munmap_this = false; + + return; +} + +static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) { + size_t k, start, end; + + assert(rindex); + assert(align > 0); + + start = ALIGN_TO((size_t) *rindex, align); + end = start + nbytes; + + if (end > sz) + return -EBADMSG; + + /* Verify that padding is 0 */ + for (k = *rindex; k < start; k++) + if (((const uint8_t*) p)[k] != 0) + return -EBADMSG; + + if (r) + *r = (uint8_t*) p + start; + + *rindex = end; + + return 1; +} + +static bool message_end_of_signature(sd_bus_message *m) { + struct bus_container *c; + + assert(m); + + c = message_get_container(m); + return !c->signature || c->signature[c->index] == 0; +} + +static bool message_end_of_array(sd_bus_message *m, size_t index) { + struct bus_container *c; + + assert(m); + + c = message_get_container(m); + if (c->enclosing != SD_BUS_TYPE_ARRAY) + return false; + + if (BUS_MESSAGE_IS_GVARIANT(m)) + return index >= c->end; + else { + assert(c->array_size); + return index >= c->begin + BUS_MESSAGE_BSWAP32(m, *c->array_size); + } +} + +_public_ int sd_bus_message_at_end(sd_bus_message *m, int complete) { + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + + if (complete && m->n_containers > 0) + return false; + + if (message_end_of_signature(m)) + return true; + + if (message_end_of_array(m, m->rindex)) + return true; + + return false; +} + +static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) { + struct bus_body_part *part; + size_t begin; + int r; + + assert(m); + + if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) { + part = m->cached_rindex_part; + begin = m->cached_rindex_part_begin; + } else { + part = &m->body; + begin = 0; + } + + while (part) { + if (index < begin) + return NULL; + + if (index + sz <= begin + part->size) { + + r = bus_body_part_map(part); + if (r < 0) + return NULL; + + if (p) + *p = (uint8_t*) part->data + index - begin; + + m->cached_rindex_part = part; + m->cached_rindex_part_begin = begin; + + return part; + } + + begin += part->size; + part = part->next; + } + + return NULL; +} + +static int container_next_item(sd_bus_message *m, struct bus_container *c, size_t *rindex) { + int r; + + assert(m); + assert(c); + assert(rindex); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) + return 0; + + if (c->enclosing == SD_BUS_TYPE_ARRAY) { + int sz; + + sz = bus_gvariant_get_size(c->signature); + if (sz < 0) { + int alignment; + + if (c->offset_index+1 >= c->n_offsets) + goto end; + + /* Variable-size array */ + + alignment = bus_gvariant_get_alignment(c->signature); + assert(alignment > 0); + + *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); + c->item_size = c->offsets[c->offset_index+1] - *rindex; + } else { + + if (c->offset_index+1 >= (c->end-c->begin)/sz) + goto end; + + /* Fixed-size array */ + *rindex = c->begin + (c->offset_index+1) * sz; + c->item_size = sz; + } + + c->offset_index++; + + } else if (c->enclosing == 0 || + c->enclosing == SD_BUS_TYPE_STRUCT || + c->enclosing == SD_BUS_TYPE_DICT_ENTRY) { + + int alignment; + size_t n, j; + + if (c->offset_index+1 >= c->n_offsets) + goto end; + + r = signature_element_length(c->signature + c->index, &n); + if (r < 0) + return r; + + r = signature_element_length(c->signature + c->index + n, &j); + if (r < 0) + return r; + else { + char t[j+1]; + memcpy(t, c->signature + c->index + n, j); + t[j] = 0; + + alignment = bus_gvariant_get_alignment(t); + } + + assert(alignment > 0); + + *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); + c->item_size = c->offsets[c->offset_index+1] - *rindex; + + c->offset_index++; + + } else if (c->enclosing == SD_BUS_TYPE_VARIANT) + goto end; + else + assert_not_reached("Unknown container type"); + + return 0; + +end: + /* Reached the end */ + *rindex = c->end; + c->item_size = 0; + return 0; +} + + +static int message_peek_body( + sd_bus_message *m, + size_t *rindex, + size_t align, + size_t nbytes, + void **ret) { + + size_t k, start, end, padding; + struct bus_body_part *part; + uint8_t *q; + + assert(m); + assert(rindex); + assert(align > 0); + + start = ALIGN_TO((size_t) *rindex, align); + padding = start - *rindex; + end = start + nbytes; + + if (end > m->user_body_size) + return -EBADMSG; + + part = find_part(m, *rindex, padding, (void**) &q); + if (!part) + return -EBADMSG; + + if (q) { + /* Verify padding */ + for (k = 0; k < padding; k++) + if (q[k] != 0) + return -EBADMSG; + } + + part = find_part(m, start, nbytes, (void**) &q); + if (!part || (nbytes > 0 && !q)) + return -EBADMSG; + + *rindex = end; + + if (ret) + *ret = q; + + return 0; +} + +static bool validate_nul(const char *s, size_t l) { + + /* Check for NUL chars in the string */ + if (memchr(s, 0, l)) + return false; + + /* Check for NUL termination */ + if (s[l] != 0) + return false; + + return true; +} + +static bool validate_string(const char *s, size_t l) { + + if (!validate_nul(s, l)) + return false; + + /* Check if valid UTF8 */ + if (!utf8_is_valid(s)) + return false; + + return true; +} + +static bool validate_signature(const char *s, size_t l) { + + if (!validate_nul(s, l)) + return false; + + /* Check if valid signature */ + if (!signature_is_valid(s, true)) + return false; + + return true; +} + +static bool validate_object_path(const char *s, size_t l) { + + if (!validate_nul(s, l)) + return false; + + if (!object_path_is_valid(s)) + return false; + + return true; +} + +_public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { + struct bus_container *c; + size_t rindex; + void *q; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(bus_type_is_basic(type), -EINVAL); + + if (message_end_of_signature(m)) + return -ENXIO; + + if (message_end_of_array(m, m->rindex)) + return 0; + + c = message_get_container(m); + if (c->signature[c->index] != type) + return -ENXIO; + + rindex = m->rindex; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { + bool ok; + + r = message_peek_body(m, &rindex, 1, c->item_size, &q); + if (r < 0) + return r; + + if (type == SD_BUS_TYPE_STRING) + ok = validate_string(q, c->item_size-1); + else if (type == SD_BUS_TYPE_OBJECT_PATH) + ok = validate_object_path(q, c->item_size-1); + else + ok = validate_signature(q, c->item_size-1); + + if (!ok) + return -EBADMSG; + + if (p) + *(const char**) p = q; + } else { + int sz, align; + + sz = bus_gvariant_get_size(CHAR_TO_STR(type)); + assert(sz > 0); + if ((size_t) sz != c->item_size) + return -EBADMSG; + + align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); + assert(align > 0); + + r = message_peek_body(m, &rindex, align, c->item_size, &q); + if (r < 0) + return r; + + switch (type) { + + case SD_BUS_TYPE_BYTE: + if (p) + *(uint8_t*) p = *(uint8_t*) q; + break; + + case SD_BUS_TYPE_BOOLEAN: + if (p) + *(int*) p = !!*(uint8_t*) q; + break; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + if (p) + *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q); + break; + + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + if (p) + *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + break; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + if (p) + *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); + break; + + case SD_BUS_TYPE_UNIX_FD: { + uint32_t j; + + j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + if (j >= m->n_fds) + return -EBADMSG; + + if (p) + *(int*) p = m->fds[j]; + + break; + } + + default: + assert_not_reached("unexpected type"); + } + } + + r = container_next_item(m, c, &rindex); + if (r < 0) + return r; + } else { + + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH)) { + uint32_t l; + bool ok; + + r = message_peek_body(m, &rindex, 4, 4, &q); + if (r < 0) + return r; + + l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; + + if (type == SD_BUS_TYPE_OBJECT_PATH) + ok = validate_object_path(q, l); + else + ok = validate_string(q, l); + if (!ok) + return -EBADMSG; + + if (p) + *(const char**) p = q; + + } else if (type == SD_BUS_TYPE_SIGNATURE) { + uint8_t l; + + r = message_peek_body(m, &rindex, 1, 1, &q); + if (r < 0) + return r; + + l = *(uint8_t*) q; + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; + + if (!validate_signature(q, l)) + return -EBADMSG; + + if (p) + *(const char**) p = q; + + } else { + ssize_t sz, align; + + align = bus_type_get_alignment(type); + assert(align > 0); + + sz = bus_type_get_size(type); + assert(sz > 0); + + r = message_peek_body(m, &rindex, align, sz, &q); + if (r < 0) + return r; + + switch (type) { + + case SD_BUS_TYPE_BYTE: + if (p) + *(uint8_t*) p = *(uint8_t*) q; + break; + + case SD_BUS_TYPE_BOOLEAN: + if (p) + *(int*) p = !!*(uint32_t*) q; + break; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + if (p) + *(uint16_t*) p = BUS_MESSAGE_BSWAP16(m, *(uint16_t*) q); + break; + + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + if (p) + *(uint32_t*) p = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + break; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + if (p) + *(uint64_t*) p = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); + break; + + case SD_BUS_TYPE_UNIX_FD: { + uint32_t j; + + j = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + if (j >= m->n_fds) + return -EBADMSG; + + if (p) + *(int*) p = m->fds[j]; + break; + } + + default: + assert_not_reached("Unknown basic type..."); + } + } + } + + m->rindex = rindex; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 1; +} + +static int bus_message_enter_array( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + uint32_t **array_size, + size_t *item_size, + size_t **offsets, + size_t *n_offsets) { + + size_t rindex; + void *q; + int r, alignment; + + assert(m); + assert(c); + assert(contents); + assert(array_size); + assert(item_size); + assert(offsets); + assert(n_offsets); + + if (!signature_is_single(contents, true)) + return -EINVAL; + + if (!c->signature || c->signature[c->index] == 0) + return -ENXIO; + + if (c->signature[c->index] != SD_BUS_TYPE_ARRAY) + return -ENXIO; + + if (!startswith(c->signature + c->index + 1, contents)) + return -ENXIO; + + rindex = m->rindex; + + if (!BUS_MESSAGE_IS_GVARIANT(m)) { + /* dbus1 */ + + r = message_peek_body(m, &rindex, 4, 4, &q); + if (r < 0) + return r; + + if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE) + return -EBADMSG; + + alignment = bus_type_get_alignment(contents[0]); + if (alignment < 0) + return alignment; + + r = message_peek_body(m, &rindex, alignment, 0, NULL); + if (r < 0) + return r; + + *array_size = (uint32_t*) q; + + } else if (c->item_size <= 0) { + + /* gvariant: empty array */ + *item_size = 0; + *offsets = NULL; + *n_offsets = 0; + + } else if (bus_gvariant_is_fixed_size(contents)) { + + /* gvariant: fixed length array */ + *item_size = bus_gvariant_get_size(contents); + *offsets = NULL; + *n_offsets = 0; + + } else { + size_t where, p = 0, framing, sz; + unsigned i; + + /* gvariant: variable length array */ + sz = bus_gvariant_determine_word_size(c->item_size, 0); + + where = rindex + c->item_size - sz; + r = message_peek_body(m, &where, 1, sz, &q); + if (r < 0) + return r; + + framing = bus_gvariant_read_word_le(q, sz); + if (framing > c->item_size - sz) + return -EBADMSG; + if ((c->item_size - framing) % sz != 0) + return -EBADMSG; + + *n_offsets = (c->item_size - framing) / sz; + + where = rindex + framing; + r = message_peek_body(m, &where, 1, *n_offsets * sz, &q); + if (r < 0) + return r; + + *offsets = new(size_t, *n_offsets); + if (!*offsets) + return -ENOMEM; + + for (i = 0; i < *n_offsets; i++) { + size_t x; + + x = bus_gvariant_read_word_le((uint8_t*) q + i * sz, sz); + if (x > c->item_size - sz) + return -EBADMSG; + if (x < p) + return -EBADMSG; + + (*offsets)[i] = rindex + x; + p = x; + } + + *item_size = (*offsets)[0] - rindex; + } + + m->rindex = rindex; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index += 1 + strlen(contents); + + return 1; +} + +static int bus_message_enter_variant( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *item_size) { + + size_t rindex; + uint8_t l; + void *q; + int r; + + assert(m); + assert(c); + assert(contents); + assert(item_size); + + if (!signature_is_single(contents, false)) + return -EINVAL; + + if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) + return -EINVAL; + + if (!c->signature || c->signature[c->index] == 0) + return -ENXIO; + + if (c->signature[c->index] != SD_BUS_TYPE_VARIANT) + return -ENXIO; + + rindex = m->rindex; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t k, where; + + k = strlen(contents); + if (1+k > c->item_size) + return -EBADMSG; + + where = rindex + c->item_size - (1+k); + r = message_peek_body(m, &where, 1, 1+k, &q); + if (r < 0) + return r; + + if (*(char*) q != 0) + return -EBADMSG; + + if (memcmp((uint8_t*) q+1, contents, k)) + return -ENXIO; + + *item_size = c->item_size - (1+k); + + } else { + r = message_peek_body(m, &rindex, 1, 1, &q); + if (r < 0) + return r; + + l = *(uint8_t*) q; + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; + + if (!validate_signature(q, l)) + return -EBADMSG; + + if (!streq(q, contents)) + return -ENXIO; + } + + m->rindex = rindex; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 1; +} + +static int build_struct_offsets( + sd_bus_message *m, + const char *signature, + size_t size, + size_t *item_size, + size_t **offsets, + size_t *n_offsets) { + + unsigned n_variable = 0, n_total = 0, v; + size_t previous = 0, where; + const char *p; + size_t sz; + void *q; + int r; + + assert(m); + assert(item_size); + assert(offsets); + assert(n_offsets); + + if (isempty(signature)) { + /* Unary type is encoded as *fixed* 1 byte padding */ + r = message_peek_body(m, &m->rindex, 1, 1, &q); + if (r < 0) + return r; + + if (*(uint8_t *) q != 0) + return -EBADMSG; + + *item_size = 0; + *offsets = NULL; + *n_offsets = 0; + return 0; + } + + sz = bus_gvariant_determine_word_size(size, 0); + if (sz <= 0) + return -EBADMSG; + + /* First, loop over signature and count variable elements and + * elements in general. We use this to know how large the + * offset array is at the end of the structure. Note that + * GVariant only stores offsets for all variable size elements + * that are not the last item. */ + + p = signature; + while (*p != 0) { + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + r = bus_gvariant_is_fixed_size(t); + } + + if (r < 0) + return r; + if (r == 0 && p[n] != 0) /* except the last item */ + n_variable++; + n_total++; + + p += n; + } + + if (size < n_variable * sz) + return -EBADMSG; + + where = m->rindex + size - (n_variable * sz); + r = message_peek_body(m, &where, 1, n_variable * sz, &q); + if (r < 0) + return r; + + v = n_variable; + + *offsets = new(size_t, n_total); + if (!*offsets) + return -ENOMEM; + + *n_offsets = 0; + + /* Second, loop again and build an offset table */ + p = signature; + while (*p != 0) { + size_t n, offset; + int k; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + k = bus_gvariant_get_size(t); + if (k < 0) { + size_t x; + + /* variable size */ + if (v > 0) { + v--; + + x = bus_gvariant_read_word_le((uint8_t*) q + v*sz, sz); + if (x >= size) + return -EBADMSG; + if (m->rindex + x < previous) + return -EBADMSG; + } else + /* The last item's end + * is determined from + * the start of the + * offset array */ + x = size - (n_variable * sz); + + offset = m->rindex + x; + + } else { + size_t align; + + /* fixed size */ + align = bus_gvariant_get_alignment(t); + assert(align > 0); + + offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k; + } + } + + previous = (*offsets)[(*n_offsets)++] = offset; + p += n; + } + + assert(v == 0); + assert(*n_offsets == n_total); + + *item_size = (*offsets)[0] - m->rindex; + return 0; +} + +static int enter_struct_or_dict_entry( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *item_size, + size_t **offsets, + size_t *n_offsets) { + + int r; + + assert(m); + assert(c); + assert(contents); + assert(item_size); + assert(offsets); + assert(n_offsets); + + if (!BUS_MESSAGE_IS_GVARIANT(m)) { + + /* dbus1 */ + r = message_peek_body(m, &m->rindex, 8, 0, NULL); + if (r < 0) + return r; + + } else + /* gvariant with contents */ + return build_struct_offsets(m, contents, c->item_size, item_size, offsets, n_offsets); + + return 0; +} + +static int bus_message_enter_struct( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *item_size, + size_t **offsets, + size_t *n_offsets) { + + size_t l; + int r; + + assert(m); + assert(c); + assert(contents); + assert(item_size); + assert(offsets); + assert(n_offsets); + + if (!signature_is_valid(contents, false)) + return -EINVAL; + + if (!c->signature || c->signature[c->index] == 0) + return -ENXIO; + + l = strlen(contents); + + if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN || + !startswith(c->signature + c->index + 1, contents) || + c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END) + return -ENXIO; + + r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets); + if (r < 0) + return r; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index += 1 + l + 1; + + return 1; +} + +static int bus_message_enter_dict_entry( + sd_bus_message *m, + struct bus_container *c, + const char *contents, + size_t *item_size, + size_t **offsets, + size_t *n_offsets) { + + size_t l; + int r; + + assert(m); + assert(c); + assert(contents); + + if (!signature_is_pair(contents)) + return -EINVAL; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + return -ENXIO; + + if (!c->signature || c->signature[c->index] == 0) + return 0; + + l = strlen(contents); + + if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN || + !startswith(c->signature + c->index + 1, contents) || + c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END) + return -ENXIO; + + r = enter_struct_or_dict_entry(m, c, contents, item_size, offsets, n_offsets); + if (r < 0) + return r; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index += 1 + l + 1; + + return 1; +} + +_public_ int sd_bus_message_enter_container(sd_bus_message *m, + char type, + const char *contents) { + struct bus_container *c, *w; + uint32_t *array_size = NULL; + char *signature; + size_t before; + size_t *offsets = NULL; + size_t n_offsets = 0, item_size = 0; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(type != 0 || !contents, -EINVAL); + + if (type == 0 || !contents) { + const char *cc; + char tt; + + /* Allow entering into anonymous containers */ + r = sd_bus_message_peek_type(m, &tt, &cc); + if (r < 0) + return r; + + if (type != 0 && type != tt) + return -ENXIO; + + if (contents && !streq(contents, cc)) + return -ENXIO; + + type = tt; + contents = cc; + } + + /* + * We enforce a global limit on container depth, that is much + * higher than the 32 structs and 32 arrays the specification + * mandates. This is simpler to implement for us, and we need + * this only to ensure our container array doesn't grow + * without bounds. We are happy to return any data from a + * message as long as the data itself is valid, even if the + * overall message might be not. + * + * Note that the message signature is validated when + * parsing the headers, and that validation does check the + * 32/32 limit. + * + * Note that the specification defines no limits on the depth + * of stacked variants, but we do. + */ + if (m->n_containers >= BUS_CONTAINER_DEPTH) + return -EBADMSG; + + if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) + return -ENOMEM; + + if (message_end_of_signature(m)) + return -ENXIO; + + if (message_end_of_array(m, m->rindex)) + return 0; + + c = message_get_container(m); + + signature = strdup(contents); + if (!signature) + return -ENOMEM; + + c->saved_index = c->index; + before = m->rindex; + + if (type == SD_BUS_TYPE_ARRAY) + r = bus_message_enter_array(m, c, contents, &array_size, &item_size, &offsets, &n_offsets); + else if (type == SD_BUS_TYPE_VARIANT) + r = bus_message_enter_variant(m, c, contents, &item_size); + else if (type == SD_BUS_TYPE_STRUCT) + r = bus_message_enter_struct(m, c, contents, &item_size, &offsets, &n_offsets); + else if (type == SD_BUS_TYPE_DICT_ENTRY) + r = bus_message_enter_dict_entry(m, c, contents, &item_size, &offsets, &n_offsets); + else + r = -EINVAL; + + if (r <= 0) { + free(signature); + free(offsets); + return r; + } + + /* OK, let's fill it in */ + w = m->containers + m->n_containers++; + w->enclosing = type; + w->signature = signature; + w->peeked_signature = NULL; + w->index = 0; + + w->before = before; + w->begin = m->rindex; + + /* Unary type has fixed size of 1, but virtual size of 0 */ + if (BUS_MESSAGE_IS_GVARIANT(m) && + type == SD_BUS_TYPE_STRUCT && + isempty(signature)) + w->end = m->rindex + 0; + else + w->end = m->rindex + c->item_size; + + w->array_size = array_size; + w->item_size = item_size; + w->offsets = offsets; + w->n_offsets = n_offsets; + w->offset_index = 0; + + return 1; +} + +_public_ int sd_bus_message_exit_container(sd_bus_message *m) { + struct bus_container *c; + unsigned saved; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(m->n_containers > 0, -ENXIO); + + c = message_get_container(m); + + if (c->enclosing != SD_BUS_TYPE_ARRAY) { + if (c->signature && c->signature[c->index] != 0) + return -EBUSY; + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + if (m->rindex < c->end) + return -EBUSY; + + } else if (c->enclosing == SD_BUS_TYPE_ARRAY) { + uint32_t l; + + l = BUS_MESSAGE_BSWAP32(m, *c->array_size); + if (c->begin + l != m->rindex) + return -EBUSY; + } + + free(c->signature); + free(c->peeked_signature); + free(c->offsets); + m->n_containers--; + + c = message_get_container(m); + + saved = c->index; + c->index = c->saved_index; + r = container_next_item(m, c, &m->rindex); + c->index = saved; + if (r < 0) + return r; + + return 1; +} + +static void message_quit_container(sd_bus_message *m) { + struct bus_container *c; + + assert(m); + assert(m->sealed); + assert(m->n_containers > 0); + + c = message_get_container(m); + + /* Undo seeks */ + assert(m->rindex >= c->before); + m->rindex = c->before; + + /* Free container */ + free(c->signature); + free(c->offsets); + m->n_containers--; + + /* Correct index of new top-level container */ + c = message_get_container(m); + c->index = c->saved_index; +} + +_public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) { + struct bus_container *c; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + + if (message_end_of_signature(m)) + goto eof; + + if (message_end_of_array(m, m->rindex)) + goto eof; + + c = message_get_container(m); + + if (bus_type_is_basic(c->signature[c->index])) { + if (contents) + *contents = NULL; + if (type) + *type = c->signature[c->index]; + return 1; + } + + if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) { + + if (contents) { + size_t l; + char *sig; + + r = signature_element_length(c->signature+c->index+1, &l); + if (r < 0) + return r; + + assert(l >= 1); + + sig = strndup(c->signature + c->index + 1, l); + if (!sig) + return -ENOMEM; + + free(c->peeked_signature); + *contents = c->peeked_signature = sig; + } + + if (type) + *type = SD_BUS_TYPE_ARRAY; + + return 1; + } + + if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN || + c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) { + + if (contents) { + size_t l; + char *sig; + + r = signature_element_length(c->signature+c->index, &l); + if (r < 0) + return r; + + assert(l >= 2); + sig = strndup(c->signature + c->index + 1, l - 2); + if (!sig) + return -ENOMEM; + + free(c->peeked_signature); + *contents = c->peeked_signature = sig; + } + + if (type) + *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY; + + return 1; + } + + if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) { + if (contents) { + void *q; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t k; + + if (c->item_size < 2) + return -EBADMSG; + + /* Look for the NUL delimiter that + separates the payload from the + signature. Since the body might be + in a different part that then the + signature we map byte by byte. */ + + for (k = 2; k <= c->item_size; k++) { + size_t where; + + where = m->rindex + c->item_size - k; + r = message_peek_body(m, &where, 1, k, &q); + if (r < 0) + return r; + + if (*(char*) q == 0) + break; + } + + if (k > c->item_size) + return -EBADMSG; + + free(c->peeked_signature); + c->peeked_signature = strndup((char*) q + 1, k - 1); + if (!c->peeked_signature) + return -ENOMEM; + + if (!signature_is_valid(c->peeked_signature, true)) + return -EBADMSG; + + *contents = c->peeked_signature; + } else { + size_t rindex, l; + + rindex = m->rindex; + r = message_peek_body(m, &rindex, 1, 1, &q); + if (r < 0) + return r; + + l = *(uint8_t*) q; + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; + + if (!validate_signature(q, l)) + return -EBADMSG; + + *contents = q; + } + } + + if (type) + *type = SD_BUS_TYPE_VARIANT; + + return 1; + } + + return -EINVAL; + +eof: + if (type) + *type = 0; + if (contents) + *contents = NULL; + return 0; +} + +_public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) { + struct bus_container *c; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + + if (complete) { + message_reset_containers(m); + m->rindex = 0; + + c = message_get_container(m); + } else { + c = message_get_container(m); + + c->offset_index = 0; + c->index = 0; + m->rindex = c->begin; + } + + c->offset_index = 0; + c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin; + + return !isempty(c->signature); +} + +static int message_read_ap( + sd_bus_message *m, + const char *types, + va_list ap) { + + unsigned n_array, n_struct; + TypeStack stack[BUS_CONTAINER_DEPTH]; + unsigned stack_ptr = 0; + unsigned n_loop = 0; + int r; + + assert(m); + + if (isempty(types)) + return 0; + + /* Ideally, we'd just call ourselves recursively on every + * complex type. However, the state of a va_list that is + * passed to a function is undefined after that function + * returns. This means we need to docode the va_list linearly + * in a single stackframe. We hence implement our own + * home-grown stack in an array. */ + + n_array = (unsigned) -1; /* length of current array entries */ + n_struct = strlen(types); /* length of current struct contents signature */ + + for (;;) { + const char *t; + + n_loop++; + + if (n_array == 0 || (n_array == (unsigned) -1 && n_struct == 0)) { + r = type_stack_pop(stack, ELEMENTSOF(stack), &stack_ptr, &types, &n_struct, &n_array); + if (r < 0) + return r; + if (r == 0) + break; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + continue; + } + + t = types; + if (n_array != (unsigned) -1) + n_array--; + else { + types++; + n_struct--; + } + + switch (*t) { + + case SD_BUS_TYPE_BYTE: + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_UNIX_FD: { + void *p; + + p = va_arg(ap, void*); + r = sd_bus_message_read_basic(m, *t, p); + if (r < 0) + return r; + if (r == 0) { + if (n_loop <= 1) + return 0; + + return -ENXIO; + } + + break; + } + + case SD_BUS_TYPE_ARRAY: { + size_t k; + + r = signature_element_length(t + 1, &k); + if (r < 0) + return r; + + { + char s[k + 1]; + memcpy(s, t + 1, k); + s[k] = 0; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s); + if (r < 0) + return r; + if (r == 0) { + if (n_loop <= 1) + return 0; + + return -ENXIO; + } + } + + if (n_array == (unsigned) -1) { + types += k; + n_struct -= k; + } + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = t + 1; + n_struct = k; + n_array = va_arg(ap, unsigned); + + break; + } + + case SD_BUS_TYPE_VARIANT: { + const char *s; + + s = va_arg(ap, const char *); + if (!s) + return -EINVAL; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, s); + if (r < 0) + return r; + if (r == 0) { + if (n_loop <= 1) + return 0; + + return -ENXIO; + } + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = s; + n_struct = strlen(s); + n_array = (unsigned) -1; + + break; + } + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + r = signature_element_length(t, &k); + if (r < 0) + return r; + + { + char s[k - 1]; + memcpy(s, t + 1, k - 2); + s[k - 2] = 0; + + r = sd_bus_message_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r < 0) + return r; + if (r == 0) { + if (n_loop <= 1) + return 0; + return -ENXIO; + } + } + + if (n_array == (unsigned) -1) { + types += k - 1; + n_struct -= k - 1; + } + + r = type_stack_push(stack, ELEMENTSOF(stack), &stack_ptr, types, n_struct, n_array); + if (r < 0) + return r; + + types = t + 1; + n_struct = k - 2; + n_array = (unsigned) -1; + + break; + } + + default: + return -EINVAL; + } + } + + return 1; +} + +_public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) { + va_list ap; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(types, -EINVAL); + + va_start(ap, types); + r = message_read_ap(m, types, ap); + va_end(ap); + + return r; +} + +_public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) { + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + + /* If types is NULL, read exactly one element */ + if (!types) { + struct bus_container *c; + size_t l; + + if (message_end_of_signature(m)) + return -ENXIO; + + if (message_end_of_array(m, m->rindex)) + return 0; + + c = message_get_container(m); + + r = signature_element_length(c->signature + c->index, &l); + if (r < 0) + return r; + + types = strndupa(c->signature + c->index, l); + } + + switch (*types) { + + case 0: /* Nothing to drop */ + return 0; + + case SD_BUS_TYPE_BYTE: + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_UNIX_FD: + + r = sd_bus_message_read_basic(m, *types, NULL); + if (r <= 0) + return r; + + r = sd_bus_message_skip(m, types + 1); + if (r < 0) + return r; + + return 1; + + case SD_BUS_TYPE_ARRAY: { + size_t k; + + r = signature_element_length(types + 1, &k); + if (r < 0) + return r; + + { + char s[k+1]; + memcpy(s, types+1, k); + s[k] = 0; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s); + if (r <= 0) + return r; + + for (;;) { + r = sd_bus_message_skip(m, s); + if (r < 0) + return r; + if (r == 0) + break; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_skip(m, types + 1 + k); + if (r < 0) + return r; + + return 1; + } + + case SD_BUS_TYPE_VARIANT: { + const char *contents; + char x; + + r = sd_bus_message_peek_type(m, &x, &contents); + if (r <= 0) + return r; + + if (x != SD_BUS_TYPE_VARIANT) + return -ENXIO; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); + if (r <= 0) + return r; + + r = sd_bus_message_skip(m, contents); + if (r < 0) + return r; + assert(r != 0); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + r = sd_bus_message_skip(m, types + 1); + if (r < 0) + return r; + + return 1; + } + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + r = signature_element_length(types, &k); + if (r < 0) + return r; + + { + char s[k-1]; + memcpy(s, types+1, k-2); + s[k-2] = 0; + + r = sd_bus_message_enter_container(m, *types == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r <= 0) + return r; + + r = sd_bus_message_skip(m, s); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_skip(m, types + k); + if (r < 0) + return r; + + return 1; + } + + default: + return -EINVAL; + } +} + +_public_ int sd_bus_message_read_array( + sd_bus_message *m, + char type, + const void **ptr, + size_t *size) { + + struct bus_container *c; + void *p; + size_t sz; + ssize_t align; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(ptr, -EINVAL); + assert_return(size, -EINVAL); + assert_return(!BUS_MESSAGE_NEED_BSWAP(m), -EOPNOTSUPP); + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); + if (r <= 0) + return r; + + c = message_get_container(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); + if (align < 0) + return align; + + sz = c->end - c->begin; + } else { + align = bus_type_get_alignment(type); + if (align < 0) + return align; + + sz = BUS_MESSAGE_BSWAP32(m, *c->array_size); + } + + if (sz == 0) + /* Zero length array, let's return some aligned + * pointer that is not NULL */ + p = (uint8_t*) NULL + align; + else { + r = message_peek_body(m, &m->rindex, align, sz, &p); + if (r < 0) + goto fail; + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + goto fail; + + *ptr = (const void*) p; + *size = sz; + + return 1; + +fail: + message_quit_container(m); + return r; +} + +static int message_peek_fields( + sd_bus_message *m, + size_t *rindex, + size_t align, + size_t nbytes, + void **ret) { + + assert(m); + assert(rindex); + assert(align > 0); + + return buffer_peek(BUS_MESSAGE_FIELDS(m), m->fields_size, rindex, align, nbytes, ret); +} + +static int message_peek_field_uint32( + sd_bus_message *m, + size_t *ri, + size_t item_size, + uint32_t *ret) { + + int r; + void *q; + + assert(m); + assert(ri); + + if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 4) + return -EBADMSG; + + /* identical for gvariant and dbus1 */ + + r = message_peek_fields(m, ri, 4, 4, &q); + if (r < 0) + return r; + + if (ret) + *ret = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); + + return 0; +} + +static int message_peek_field_uint64( + sd_bus_message *m, + size_t *ri, + size_t item_size, + uint64_t *ret) { + + int r; + void *q; + + assert(m); + assert(ri); + + if (BUS_MESSAGE_IS_GVARIANT(m) && item_size != 8) + return -EBADMSG; + + /* identical for gvariant and dbus1 */ + + r = message_peek_fields(m, ri, 8, 8, &q); + if (r < 0) + return r; + + if (ret) + *ret = BUS_MESSAGE_BSWAP64(m, *(uint64_t*) q); + + return 0; +} + +static int message_peek_field_string( + sd_bus_message *m, + bool (*validate)(const char *p), + size_t *ri, + size_t item_size, + const char **ret) { + + uint32_t l; + int r; + void *q; + + assert(m); + assert(ri); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + + if (item_size <= 0) + return -EBADMSG; + + r = message_peek_fields(m, ri, 1, item_size, &q); + if (r < 0) + return r; + + l = item_size - 1; + } else { + r = message_peek_field_uint32(m, ri, 4, &l); + if (r < 0) + return r; + + r = message_peek_fields(m, ri, 1, l+1, &q); + if (r < 0) + return r; + } + + if (validate) { + if (!validate_nul(q, l)) + return -EBADMSG; + + if (!validate(q)) + return -EBADMSG; + } else { + if (!validate_string(q, l)) + return -EBADMSG; + } + + if (ret) + *ret = q; + + return 0; +} + +static int message_peek_field_signature( + sd_bus_message *m, + size_t *ri, + size_t item_size, + const char **ret) { + + size_t l; + int r; + void *q; + + assert(m); + assert(ri); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + + if (item_size <= 0) + return -EBADMSG; + + r = message_peek_fields(m, ri, 1, item_size, &q); + if (r < 0) + return r; + + l = item_size - 1; + } else { + r = message_peek_fields(m, ri, 1, 1, &q); + if (r < 0) + return r; + + l = *(uint8_t*) q; + r = message_peek_fields(m, ri, 1, l+1, &q); + if (r < 0) + return r; + } + + if (!validate_signature(q, l)) + return -EBADMSG; + + if (ret) + *ret = q; + + return 0; +} + +static int message_skip_fields( + sd_bus_message *m, + size_t *ri, + uint32_t array_size, + const char **signature) { + + size_t original_index; + int r; + + assert(m); + assert(ri); + assert(signature); + assert(!BUS_MESSAGE_IS_GVARIANT(m)); + + original_index = *ri; + + for (;;) { + char t; + size_t l; + + if (array_size != (uint32_t) -1 && + array_size <= *ri - original_index) + return 0; + + t = **signature; + if (!t) + return 0; + + if (t == SD_BUS_TYPE_STRING) { + + r = message_peek_field_string(m, NULL, ri, 0, NULL); + if (r < 0) + return r; + + (*signature)++; + + } else if (t == SD_BUS_TYPE_OBJECT_PATH) { + + r = message_peek_field_string(m, object_path_is_valid, ri, 0, NULL); + if (r < 0) + return r; + + (*signature)++; + + } else if (t == SD_BUS_TYPE_SIGNATURE) { + + r = message_peek_field_signature(m, ri, 0, NULL); + if (r < 0) + return r; + + (*signature)++; + + } else if (bus_type_is_basic(t)) { + ssize_t align, k; + + align = bus_type_get_alignment(t); + k = bus_type_get_size(t); + assert(align > 0 && k > 0); + + r = message_peek_fields(m, ri, align, k, NULL); + if (r < 0) + return r; + + (*signature)++; + + } else if (t == SD_BUS_TYPE_ARRAY) { + + r = signature_element_length(*signature+1, &l); + if (r < 0) + return r; + + assert(l >= 1); + { + char sig[l-1], *s; + uint32_t nas; + int alignment; + + strncpy(sig, *signature + 1, l-1); + s = sig; + + alignment = bus_type_get_alignment(sig[0]); + if (alignment < 0) + return alignment; + + r = message_peek_field_uint32(m, ri, 0, &nas); + if (r < 0) + return r; + if (nas > BUS_ARRAY_MAX_SIZE) + return -EBADMSG; + + r = message_peek_fields(m, ri, alignment, 0, NULL); + if (r < 0) + return r; + + r = message_skip_fields(m, ri, nas, (const char**) &s); + if (r < 0) + return r; + } + + (*signature) += 1 + l; + + } else if (t == SD_BUS_TYPE_VARIANT) { + const char *s; + + r = message_peek_field_signature(m, ri, 0, &s); + if (r < 0) + return r; + + r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s); + if (r < 0) + return r; + + (*signature)++; + + } else if (t == SD_BUS_TYPE_STRUCT || + t == SD_BUS_TYPE_DICT_ENTRY) { + + r = signature_element_length(*signature, &l); + if (r < 0) + return r; + + assert(l >= 2); + { + char sig[l-1], *s; + strncpy(sig, *signature + 1, l-1); + s = sig; + + r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s); + if (r < 0) + return r; + } + + *signature += l; + } else + return -EINVAL; + } +} + +int bus_message_parse_fields(sd_bus_message *m) { + size_t ri; + int r; + uint32_t unix_fds = 0; + bool unix_fds_set = false; + void *offsets = NULL; + unsigned n_offsets = 0; + size_t sz = 0; + unsigned i = 0; + + assert(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + char *p; + + /* Read the signature from the end of the body variant first */ + sz = bus_gvariant_determine_word_size(BUS_MESSAGE_SIZE(m), 0); + if (m->footer_accessible < 1 + sz) + return -EBADMSG; + + p = (char*) m->footer + m->footer_accessible - (1 + sz); + for (;;) { + if (p < (char*) m->footer) + return -EBADMSG; + + if (*p == 0) { + size_t l; + char *c; + + /* We found the beginning of the signature + * string, yay! We require the body to be a + * structure, so verify it and then strip the + * opening/closing brackets. */ + + l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz); + if (l < 2 || + p[1] != SD_BUS_TYPE_STRUCT_BEGIN || + p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) + return -EBADMSG; + + c = strndup(p + 1 + 1, l - 2); + if (!c) + return -ENOMEM; + + free(m->root_container.signature); + m->root_container.signature = c; + break; + } + + p--; + } + + /* Calculate the actual user body size, by removing + * the trailing variant signature and struct offset + * table */ + m->user_body_size = m->body_size - ((char*) m->footer + m->footer_accessible - p); + + /* Pull out the offset table for the fields array */ + sz = bus_gvariant_determine_word_size(m->fields_size, 0); + if (sz > 0) { + size_t framing; + void *q; + + ri = m->fields_size - sz; + r = message_peek_fields(m, &ri, 1, sz, &q); + if (r < 0) + return r; + + framing = bus_gvariant_read_word_le(q, sz); + if (framing >= m->fields_size - sz) + return -EBADMSG; + if ((m->fields_size - framing) % sz != 0) + return -EBADMSG; + + ri = framing; + r = message_peek_fields(m, &ri, 1, m->fields_size - framing, &offsets); + if (r < 0) + return r; + + n_offsets = (m->fields_size - framing) / sz; + } + } else + m->user_body_size = m->body_size; + + ri = 0; + while (ri < m->fields_size) { + _cleanup_free_ char *sig = NULL; + const char *signature; + uint64_t field_type; + size_t item_size = (size_t) -1; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + uint64_t *u64; + + if (i >= n_offsets) + break; + + if (i == 0) + ri = 0; + else + ri = ALIGN_TO(bus_gvariant_read_word_le((uint8_t*) offsets + (i-1)*sz, sz), 8); + + r = message_peek_fields(m, &ri, 8, 8, (void**) &u64); + if (r < 0) + return r; + + field_type = BUS_MESSAGE_BSWAP64(m, *u64); + } else { + uint8_t *u8; + + r = message_peek_fields(m, &ri, 8, 1, (void**) &u8); + if (r < 0) + return r; + + field_type = *u8; + } + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + size_t where, end; + char *b; + void *q; + + end = bus_gvariant_read_word_le((uint8_t*) offsets + i*sz, sz); + + if (end < ri) + return -EBADMSG; + + where = ri = ALIGN_TO(ri, 8); + item_size = end - ri; + r = message_peek_fields(m, &where, 1, item_size, &q); + if (r < 0) + return r; + + b = memrchr(q, 0, item_size); + if (!b) + return -EBADMSG; + + sig = strndup(b+1, item_size - (b+1-(char*) q)); + if (!sig) + return -ENOMEM; + + signature = sig; + item_size = b - (char*) q; + } else { + r = message_peek_field_signature(m, &ri, 0, &signature); + if (r < 0) + return r; + } + + switch (field_type) { + + case _BUS_MESSAGE_HEADER_INVALID: + return -EBADMSG; + + case BUS_MESSAGE_HEADER_PATH: + + if (m->path) + return -EBADMSG; + + if (!streq(signature, "o")) + return -EBADMSG; + + r = message_peek_field_string(m, object_path_is_valid, &ri, item_size, &m->path); + break; + + case BUS_MESSAGE_HEADER_INTERFACE: + + if (m->interface) + return -EBADMSG; + + if (!streq(signature, "s")) + return -EBADMSG; + + r = message_peek_field_string(m, interface_name_is_valid, &ri, item_size, &m->interface); + break; + + case BUS_MESSAGE_HEADER_MEMBER: + + if (m->member) + return -EBADMSG; + + if (!streq(signature, "s")) + return -EBADMSG; + + r = message_peek_field_string(m, member_name_is_valid, &ri, item_size, &m->member); + break; + + case BUS_MESSAGE_HEADER_ERROR_NAME: + + if (m->error.name) + return -EBADMSG; + + if (!streq(signature, "s")) + return -EBADMSG; + + r = message_peek_field_string(m, error_name_is_valid, &ri, item_size, &m->error.name); + if (r >= 0) + m->error._need_free = -1; + + break; + + case BUS_MESSAGE_HEADER_DESTINATION: + + if (m->destination) + return -EBADMSG; + + if (!streq(signature, "s")) + return -EBADMSG; + + r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->destination); + break; + + case BUS_MESSAGE_HEADER_SENDER: + + if (m->sender) + return -EBADMSG; + + if (!streq(signature, "s")) + return -EBADMSG; + + r = message_peek_field_string(m, service_name_is_valid, &ri, item_size, &m->sender); + + if (r >= 0 && m->sender[0] == ':' && m->bus->bus_client && !m->bus->is_kernel) { + m->creds.unique_name = (char*) m->sender; + m->creds.mask |= SD_BUS_CREDS_UNIQUE_NAME & m->bus->creds_mask; + } + + break; + + + case BUS_MESSAGE_HEADER_SIGNATURE: { + const char *s; + char *c; + + if (BUS_MESSAGE_IS_GVARIANT(m)) /* only applies to dbus1 */ + return -EBADMSG; + + if (m->root_container.signature) + return -EBADMSG; + + if (!streq(signature, "g")) + return -EBADMSG; + + r = message_peek_field_signature(m, &ri, item_size, &s); + if (r < 0) + return r; + + c = strdup(s); + if (!c) + return -ENOMEM; + + free(m->root_container.signature); + m->root_container.signature = c; + break; + } + + case BUS_MESSAGE_HEADER_REPLY_SERIAL: + + if (m->reply_cookie != 0) + return -EBADMSG; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + /* 64bit on dbus2 */ + + if (!streq(signature, "t")) + return -EBADMSG; + + r = message_peek_field_uint64(m, &ri, item_size, &m->reply_cookie); + if (r < 0) + return r; + } else { + /* 32bit on dbus1 */ + uint32_t serial; + + if (!streq(signature, "u")) + return -EBADMSG; + + r = message_peek_field_uint32(m, &ri, item_size, &serial); + if (r < 0) + return r; + + m->reply_cookie = serial; + } + + if (m->reply_cookie == 0) + return -EBADMSG; + + break; + + case BUS_MESSAGE_HEADER_UNIX_FDS: + if (unix_fds_set) + return -EBADMSG; + + if (!streq(signature, "u")) + return -EBADMSG; + + r = message_peek_field_uint32(m, &ri, item_size, &unix_fds); + if (r < 0) + return -EBADMSG; + + unix_fds_set = true; + break; + + default: + if (!BUS_MESSAGE_IS_GVARIANT(m)) + r = message_skip_fields(m, &ri, (uint32_t) -1, (const char **) &signature); + } + + if (r < 0) + return r; + + i++; + } + + if (m->n_fds != unix_fds) + return -EBADMSG; + + switch (m->header->type) { + + case SD_BUS_MESSAGE_SIGNAL: + if (!m->path || !m->interface || !m->member) + return -EBADMSG; + + if (m->reply_cookie != 0) + return -EBADMSG; + + break; + + case SD_BUS_MESSAGE_METHOD_CALL: + + if (!m->path || !m->member) + return -EBADMSG; + + if (m->reply_cookie != 0) + return -EBADMSG; + + break; + + case SD_BUS_MESSAGE_METHOD_RETURN: + + if (m->reply_cookie == 0) + return -EBADMSG; + break; + + case SD_BUS_MESSAGE_METHOD_ERROR: + + if (m->reply_cookie == 0 || !m->error.name) + return -EBADMSG; + break; + } + + /* Refuse non-local messages that claim they are local */ + if (streq_ptr(m->path, "/org/freedesktop/DBus/Local")) + return -EBADMSG; + if (streq_ptr(m->interface, "org.freedesktop.DBus.Local")) + return -EBADMSG; + if (streq_ptr(m->sender, "org.freedesktop.DBus.Local")) + return -EBADMSG; + + m->root_container.end = m->user_body_size; + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + r = build_struct_offsets( + m, + m->root_container.signature, + m->user_body_size, + &m->root_container.item_size, + &m->root_container.offsets, + &m->root_container.n_offsets); + if (r < 0) + return r; + } + + /* Try to read the error message, but if we can't it's a non-issue */ + if (m->header->type == SD_BUS_MESSAGE_METHOD_ERROR) + (void) sd_bus_message_read(m, "s", &m->error.message); + + return 0; +} + +_public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) { + assert_return(m, -EINVAL); + assert_return(destination, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->destination, -EEXIST); + + return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination); +} + +int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) { + size_t total; + void *p, *e; + unsigned i; + struct bus_body_part *part; + + assert(m); + assert(buffer); + assert(sz); + + total = BUS_MESSAGE_SIZE(m); + + p = malloc(total); + if (!p) + return -ENOMEM; + + e = mempcpy(p, m->header, BUS_MESSAGE_BODY_BEGIN(m)); + MESSAGE_FOREACH_PART(part, i, m) + e = mempcpy(e, part->data, part->size); + + assert(total == (size_t) ((uint8_t*) e - (uint8_t*) p)); + + *buffer = p; + *sz = total; + + return 0; +} + +int bus_message_read_strv_extend(sd_bus_message *m, char ***l) { + const char *s; + int r; + + assert(m); + assert(l); + + r = sd_bus_message_enter_container(m, 'a', "s"); + if (r <= 0) + return r; + + while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) { + r = strv_extend(l, s); + if (r < 0) + return r; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) { + char **strv = NULL; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(l, -EINVAL); + + r = bus_message_read_strv_extend(m, &strv); + if (r <= 0) { + strv_free(strv); + return r; + } + + *l = strv; + return 1; +} + +static int bus_message_get_arg_skip( + sd_bus_message *m, + unsigned i, + char *_type, + const char **_contents) { + + unsigned j; + int r; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + for (j = 0;; j++) { + const char *contents; + char type; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + /* Don't match against arguments after the first one we don't understand */ + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE) && + !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) + return -ENXIO; + + if (j >= i) { + if (_contents) + *_contents = contents; + if (_type) + *_type = type; + return 0; + } + + r = sd_bus_message_skip(m, NULL); + if (r < 0) + return r; + } + +} + +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str) { + char type; + int r; + + assert(m); + assert(str); + + r = bus_message_get_arg_skip(m, i, &type, NULL); + if (r < 0) + return r; + + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) + return -ENXIO; + + return sd_bus_message_read_basic(m, type, str); +} + +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv) { + const char *contents; + char type; + int r; + + assert(m); + assert(strv); + + r = bus_message_get_arg_skip(m, i, &type, &contents); + if (r < 0) + return r; + + if (type != SD_BUS_TYPE_ARRAY) + return -ENXIO; + if (!STR_IN_SET(contents, "s", "o", "g")) + return -ENXIO; + + return sd_bus_message_read_strv(m, strv); +} + +_public_ int sd_bus_message_get_errno(sd_bus_message *m) { + assert_return(m, EINVAL); + + if (m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) + return 0; + + return sd_bus_error_get_errno(&m->error); +} + +_public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complete) { + struct bus_container *c; + + assert_return(m, NULL); + + c = complete ? &m->root_container : message_get_container(m); + return strempty(c->signature); +} + +_public_ int sd_bus_message_is_empty(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return isempty(m->root_container.signature); +} + +_public_ int sd_bus_message_has_signature(sd_bus_message *m, const char *signature) { + assert_return(m, -EINVAL); + + return streq(strempty(m->root_container.signature), strempty(signature)); +} + +_public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) { + bool done_something = false; + int r; + + assert_return(m, -EINVAL); + assert_return(source, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(source->sealed, -EPERM); + + do { + const char *contents; + char type; + union { + uint8_t u8; + uint16_t u16; + int16_t s16; + uint32_t u32; + int32_t s32; + uint64_t u64; + int64_t s64; + double d64; + const char *string; + int i; + } basic; + + r = sd_bus_message_peek_type(source, &type, &contents); + if (r < 0) + return r; + if (r == 0) + break; + + done_something = true; + + if (bus_type_is_container(type) > 0) { + + r = sd_bus_message_enter_container(source, type, contents); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, type, contents); + if (r < 0) + return r; + + r = sd_bus_message_copy(m, source, true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(source); + if (r < 0) + return r; + + continue; + } + + r = sd_bus_message_read_basic(source, type, &basic); + if (r < 0) + return r; + + assert(r > 0); + + if (type == SD_BUS_TYPE_OBJECT_PATH || + type == SD_BUS_TYPE_SIGNATURE || + type == SD_BUS_TYPE_STRING) + r = sd_bus_message_append_basic(m, type, basic.string); + else + r = sd_bus_message_append_basic(m, type, &basic); + + if (r < 0) + return r; + + } while (all); + + return done_something; +} + +_public_ int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents) { + const char *c; + char t; + int r; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(!type || bus_type_is_valid(type), -EINVAL); + assert_return(!contents || signature_is_valid(contents, true), -EINVAL); + assert_return(type || contents, -EINVAL); + assert_return(!contents || !type || bus_type_is_container(type), -EINVAL); + + r = sd_bus_message_peek_type(m, &t, &c); + if (r <= 0) + return r; + + if (type != 0 && type != t) + return 0; + + if (contents && !streq_ptr(contents, c)) + return 0; + + return 1; +} + +_public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) { + assert_return(m, NULL); + + return m->bus; +} + +int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL; + usec_t timeout; + int r; + + assert(bus); + assert(m); + assert(*m); + + switch ((*m)->header->type) { + + case SD_BUS_MESSAGE_SIGNAL: + r = sd_bus_message_new_signal(bus, &n, (*m)->path, (*m)->interface, (*m)->member); + if (r < 0) + return r; + + break; + + case SD_BUS_MESSAGE_METHOD_CALL: + r = sd_bus_message_new_method_call(bus, &n, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member); + if (r < 0) + return r; + + break; + + case SD_BUS_MESSAGE_METHOD_RETURN: + case SD_BUS_MESSAGE_METHOD_ERROR: + + n = message_new(bus, (*m)->header->type); + if (!n) + return -ENOMEM; + + n->reply_cookie = (*m)->reply_cookie; + + r = message_append_reply_cookie(n, n->reply_cookie); + if (r < 0) + return r; + + if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message); + if (r < 0) + return r; + + n->error._need_free = -1; + } + + break; + + default: + return -EINVAL; + } + + if ((*m)->destination && !n->destination) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination); + if (r < 0) + return r; + } + + if ((*m)->sender && !n->sender) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender); + if (r < 0) + return r; + } + + n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START); + + r = sd_bus_message_copy(n, *m, true); + if (r < 0) + return r; + + timeout = (*m)->timeout; + if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)) + timeout = BUS_DEFAULT_TIMEOUT; + + r = bus_message_seal(n, BUS_MESSAGE_COOKIE(*m), timeout); + if (r < 0) + return r; + + sd_bus_message_unref(*m); + *m = n; + n = NULL; + + return 0; +} + +int bus_message_append_sender(sd_bus_message *m, const char *sender) { + assert(m); + assert(sender); + + assert_return(!m->sealed, -EPERM); + assert_return(!m->sender, -EPERM); + + return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender); +} + +_public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) { + assert_return(m, -EINVAL); + assert_return(priority, -EINVAL); + + *priority = m->priority; + return 0; +} + +_public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + m->priority = priority; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-message.h b/src/libsystemd/src/sd-bus/bus-message.h new file mode 100644 index 0000000000..35116c3803 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-message.h @@ -0,0 +1,245 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/macro.h" +#include "basic/time-util.h" + +#include "bus-creds.h" +#include "bus-protocol.h" + +struct bus_container { + char enclosing; + bool need_offsets:1; + + /* Indexes into the signature string */ + unsigned index, saved_index; + char *signature; + + size_t before, begin, end; + + /* dbus1: pointer to the array size value, if this is a value */ + uint32_t *array_size; + + /* gvariant: list of offsets to end of children if this is struct/dict entry/array */ + size_t *offsets, n_offsets, offsets_allocated, offset_index; + size_t item_size; + + char *peeked_signature; +}; + +struct bus_body_part { + struct bus_body_part *next; + void *data; + void *mmap_begin; + size_t size; + size_t mapped; + size_t allocated; + uint64_t memfd_offset; + int memfd; + bool free_this:1; + bool munmap_this:1; + bool sealed:1; + bool is_zero:1; +}; + +struct sd_bus_message { + unsigned n_ref; + + sd_bus *bus; + + uint64_t reply_cookie; + + const char *path; + const char *interface; + const char *member; + const char *destination; + const char *sender; + + sd_bus_error error; + + sd_bus_creds creds; + + usec_t monotonic; + usec_t realtime; + uint64_t seqnum; + int64_t priority; + uint64_t verify_destination_id; + + bool sealed:1; + bool dont_send:1; + bool allow_fds:1; + bool free_header:1; + bool free_kdbus:1; + bool free_fds:1; + bool release_kdbus:1; + bool poisoned:1; + + /* The first and last bytes of the message */ + struct bus_header *header; + void *footer; + + /* How many bytes are accessible in the above pointers */ + size_t header_accessible; + size_t footer_accessible; + + size_t fields_size; + size_t body_size; + size_t user_body_size; + + struct bus_body_part body; + struct bus_body_part *body_end; + unsigned n_body_parts; + + size_t rindex; + struct bus_body_part *cached_rindex_part; + size_t cached_rindex_part_begin; + + uint32_t n_fds; + int *fds; + + struct bus_container root_container, *containers; + size_t n_containers; + size_t containers_allocated; + + struct iovec *iovec; + struct iovec iovec_fixed[2]; + unsigned n_iovec; + + struct kdbus_msg *kdbus; + + char *peeked_signature; + + /* If set replies to this message must carry the signature + * specified here to successfully seal. This is initialized + * from the vtable data */ + const char *enforced_reply_signature; + + usec_t timeout; + + char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; + char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; + char *destination_ptr; + + size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; + unsigned n_header_offsets; +}; + +static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) { + return m->header->endian != BUS_NATIVE_ENDIAN; +} + +static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) { + return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u; +} + +static inline uint32_t BUS_MESSAGE_BSWAP32(sd_bus_message *m, uint32_t u) { + return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_32(u) : u; +} + +static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) { + return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_64(u) : u; +} + +static inline uint64_t BUS_MESSAGE_COOKIE(sd_bus_message *m) { + if (m->header->version == 2) + return BUS_MESSAGE_BSWAP64(m, m->header->dbus2.cookie); + + return BUS_MESSAGE_BSWAP32(m, m->header->dbus1.serial); +} + +static inline size_t BUS_MESSAGE_SIZE(sd_bus_message *m) { + return + sizeof(struct bus_header) + + ALIGN8(m->fields_size) + + m->body_size; +} + +static inline size_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) { + return + sizeof(struct bus_header) + + ALIGN8(m->fields_size); +} + +static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) { + return (uint8_t*) m->header + sizeof(struct bus_header); +} + +static inline bool BUS_MESSAGE_IS_GVARIANT(sd_bus_message *m) { + return m->header->version == 2; +} + +int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout); +int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz); +int bus_message_read_strv_extend(sd_bus_message *m, char ***l); + +int bus_message_from_header( + sd_bus *bus, + void *header, + size_t header_accessible, + void *footer, + size_t footer_accessible, + size_t message_size, + int *fds, + unsigned n_fds, + const char *label, + size_t extra, + sd_bus_message **ret); + +int bus_message_from_malloc( + sd_bus *bus, + void *buffer, + size_t length, + int *fds, + unsigned n_fds, + const char *label, + sd_bus_message **ret); + +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); + +int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); + +int bus_message_parse_fields(sd_bus_message *m); + +struct bus_body_part *message_append_part(sd_bus_message *m); + +#define MESSAGE_FOREACH_PART(part, i, m) \ + for ((i) = 0, (part) = &(m)->body; (i) < (m)->n_body_parts; (i)++, (part) = (part)->next) + +int bus_body_part_map(struct bus_body_part *part); +void bus_body_part_unmap(struct bus_body_part *part); + +int bus_message_to_errno(sd_bus_message *m); + +int bus_message_new_synthetic_error(sd_bus *bus, uint64_t serial, const sd_bus_error *e, sd_bus_message **m); + +int bus_message_remarshal(sd_bus *bus, sd_bus_message **m); + +int bus_message_append_sender(sd_bus_message *m, const char *sender); + +void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m); +void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m); diff --git a/src/libsystemd/src/sd-bus/bus-objects.c b/src/libsystemd/src/sd-bus/bus-objects.c new file mode 100644 index 0000000000..a56b7e8d7e --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-objects.c @@ -0,0 +1,2807 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-introspect.h" +#include "bus-message.h" +#include "bus-objects.h" +#include "bus-signature.h" +#include "bus-slot.h" +#include "bus-type.h" + +static int node_vtable_get_userdata( + sd_bus *bus, + const char *path, + struct node_vtable *c, + void **userdata, + sd_bus_error *error) { + + sd_bus_slot *s; + void *u; + int r; + + assert(bus); + assert(path); + assert(c); + + s = container_of(c, sd_bus_slot, node_vtable); + u = s->userdata; + if (c->find) { + bus->current_slot = sd_bus_slot_ref(s); + bus->current_userdata = u; + r = c->find(bus, path, c->interface, u, &u, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(s); + + if (r < 0) + return r; + if (sd_bus_error_is_set(error)) + return -sd_bus_error_get_errno(error); + if (r == 0) + return r; + } + + if (userdata) + *userdata = u; + + return 1; +} + +static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) { + assert(p); + + return (uint8_t*) u + p->x.method.offset; +} + +static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) { + assert(p); + + return (uint8_t*) u + p->x.property.offset; +} + +static int vtable_property_get_userdata( + sd_bus *bus, + const char *path, + struct vtable_member *p, + void **userdata, + sd_bus_error *error) { + + void *u; + int r; + + assert(bus); + assert(path); + assert(p); + assert(userdata); + + r = node_vtable_get_userdata(bus, path, p->parent, &u, error); + if (r <= 0) + return r; + if (bus->nodes_modified) + return 0; + + *userdata = vtable_property_convert_userdata(p->vtable, u); + return 1; +} + +static int add_enumerated_to_set( + sd_bus *bus, + const char *prefix, + struct node_enumerator *first, + Set *s, + sd_bus_error *error) { + + struct node_enumerator *c; + int r; + + assert(bus); + assert(prefix); + assert(s); + + LIST_FOREACH(enumerators, c, first) { + char **children = NULL, **k; + sd_bus_slot *slot; + + if (bus->nodes_modified) + return 0; + + slot = container_of(c, sd_bus_slot, node_enumerator); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = slot->userdata; + r = c->callback(bus, prefix, slot->userdata, &children, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + if (r < 0) + return r; + if (sd_bus_error_is_set(error)) + return -sd_bus_error_get_errno(error); + + STRV_FOREACH(k, children) { + if (r < 0) { + free(*k); + continue; + } + + if (!object_path_is_valid(*k)) { + free(*k); + r = -EINVAL; + continue; + } + + if (!object_path_startswith(*k, prefix)) { + free(*k); + continue; + } + + r = set_consume(s, *k); + if (r == -EEXIST) + r = 0; + } + + free(children); + if (r < 0) + return r; + } + + return 0; +} + +enum { + /* if set, add_subtree() works recursively */ + CHILDREN_RECURSIVE = (1U << 1), + /* if set, add_subtree() scans object-manager hierarchies recursively */ + CHILDREN_SUBHIERARCHIES = (1U << 0), +}; + +static int add_subtree_to_set( + sd_bus *bus, + const char *prefix, + struct node *n, + unsigned int flags, + Set *s, + sd_bus_error *error) { + + struct node *i; + int r; + + assert(bus); + assert(prefix); + assert(n); + assert(s); + + r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + LIST_FOREACH(siblings, i, n->child) { + char *t; + + if (!object_path_startswith(i->path, prefix)) + continue; + + t = strdup(i->path); + if (!t) + return -ENOMEM; + + r = set_consume(s, t); + if (r < 0 && r != -EEXIST) + return r; + + if ((flags & CHILDREN_RECURSIVE) && + ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) { + r = add_subtree_to_set(bus, prefix, i, flags, s, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + } + + return 0; +} + +static int get_child_nodes( + sd_bus *bus, + const char *prefix, + struct node *n, + unsigned int flags, + Set **_s, + sd_bus_error *error) { + + Set *s = NULL; + int r; + + assert(bus); + assert(prefix); + assert(n); + assert(_s); + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = add_subtree_to_set(bus, prefix, n, flags, s, error); + if (r < 0) { + set_free_free(s); + return r; + } + + *_s = s; + return 0; +} + +static int node_callbacks_run( + sd_bus *bus, + sd_bus_message *m, + struct node_callback *first, + bool require_fallback, + bool *found_object) { + + struct node_callback *c; + int r; + + assert(bus); + assert(m); + assert(found_object); + + LIST_FOREACH(callbacks, c, first) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + sd_bus_slot *slot; + + if (bus->nodes_modified) + return 0; + + if (require_fallback && !c->is_fallback) + continue; + + *found_object = true; + + if (c->last_iteration == bus->iteration_counter) + continue; + + c->last_iteration = bus->iteration_counter; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + slot = container_of(c, sd_bus_slot, node_callback); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + r = bus_maybe_reply_error(m, r, &error_buffer); + if (r != 0) + return r; + } + + return 0; +} + +#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF) + +static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) { + uint64_t cap; + int r; + + assert(bus); + assert(m); + assert(c); + + /* If the entire bus is trusted let's grant access */ + if (bus->trusted) + return 0; + + /* If the member is marked UNPRIVILEGED let's grant access */ + if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED) + return 0; + + /* Check have the caller has the requested capability + * set. Note that the flags value contains the capability + * number plus one, which we need to subtract here. We do this + * so that we have 0 as special value for "default + * capability". */ + cap = CAPABILITY_SHIFT(c->vtable->flags); + if (cap == 0) + cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags); + if (cap == 0) + cap = CAP_SYS_ADMIN; + else + cap--; + + r = sd_bus_query_sender_privilege(m, cap); + if (r < 0) + return r; + if (r > 0) + return 0; + + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member); +} + +static int method_callbacks_run( + sd_bus *bus, + sd_bus_message *m, + struct vtable_member *c, + bool require_fallback, + bool *found_object) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *signature; + void *u; + int r; + + assert(bus); + assert(m); + assert(c); + assert(found_object); + + if (require_fallback && !c->parent->is_fallback) + return 0; + + r = check_access(bus, m, c, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + + r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error); + if (r <= 0) + return bus_maybe_reply_error(m, r, &error); + if (bus->nodes_modified) + return 0; + + u = vtable_method_convert_userdata(c->vtable, u); + + *found_object = true; + + if (c->last_iteration == bus->iteration_counter) + return 0; + + c->last_iteration = bus->iteration_counter; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + signature = sd_bus_message_get_signature(m, true); + if (!signature) + return -EINVAL; + + if (!streq(strempty(c->vtable->x.method.signature), signature)) + return sd_bus_reply_method_errorf( + m, + SD_BUS_ERROR_INVALID_ARGS, + "Invalid arguments '%s' to call %s.%s(), expecting '%s'.", + signature, c->interface, c->member, strempty(c->vtable->x.method.signature)); + + /* Keep track what the signature of the reply to this message + * should be, so that this can be enforced when sealing the + * reply. */ + m->enforced_reply_signature = strempty(c->vtable->x.method.result); + + if (c->vtable->x.method.handler) { + sd_bus_slot *slot; + + slot = container_of(c->parent, sd_bus_slot, node_vtable); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->vtable->x.method.handler; + bus->current_userdata = u; + r = c->vtable->x.method.handler(m, u, &error); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error); + } + + /* If the method callback is NULL, make this a successful NOP */ + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) + return r; + + return 1; +} + +static int invoke_property_get( + sd_bus *bus, + sd_bus_slot *slot, + const sd_bus_vtable *v, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + const void *p; + int r; + + assert(bus); + assert(slot); + assert(v); + assert(path); + assert(interface); + assert(property); + assert(reply); + + if (v->x.property.get) { + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = userdata; + r = v->x.property.get(bus, path, interface, property, reply, userdata, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + if (r < 0) + return r; + if (sd_bus_error_is_set(error)) + return -sd_bus_error_get_errno(error); + return r; + } + + /* Automatic handling if no callback is defined. */ + + if (streq(v->x.property.signature, "as")) + return sd_bus_message_append_strv(reply, *(char***) userdata); + + assert(signature_is_single(v->x.property.signature, false)); + assert(bus_type_is_basic(v->x.property.signature[0])); + + switch (v->x.property.signature[0]) { + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_SIGNATURE: + p = strempty(*(char**) userdata); + break; + + case SD_BUS_TYPE_OBJECT_PATH: + p = *(char**) userdata; + assert(p); + break; + + default: + p = userdata; + break; + } + + return sd_bus_message_append_basic(reply, v->x.property.signature[0], p); +} + +static int invoke_property_set( + sd_bus *bus, + sd_bus_slot *slot, + const sd_bus_vtable *v, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + int r; + + assert(bus); + assert(slot); + assert(v); + assert(path); + assert(interface); + assert(property); + assert(value); + + if (v->x.property.set) { + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_userdata = userdata; + r = v->x.property.set(bus, path, interface, property, value, userdata, error); + bus->current_userdata = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + if (r < 0) + return r; + if (sd_bus_error_is_set(error)) + return -sd_bus_error_get_errno(error); + return r; + } + + /* Automatic handling if no callback is defined. */ + + assert(signature_is_single(v->x.property.signature, false)); + assert(bus_type_is_basic(v->x.property.signature[0])); + + switch (v->x.property.signature[0]) { + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: { + const char *p; + char *n; + + r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p); + if (r < 0) + return r; + + n = strdup(p); + if (!n) + return -ENOMEM; + + free(*(char**) userdata); + *(char**) userdata = n; + + break; + } + + default: + r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata); + if (r < 0) + return r; + + break; + } + + return 1; +} + +static int property_get_set_callbacks_run( + sd_bus *bus, + sd_bus_message *m, + struct vtable_member *c, + bool require_fallback, + bool is_get, + bool *found_object) { + + _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; + + assert(bus); + assert(m); + assert(c); + assert(found_object); + + if (require_fallback && !c->parent->is_fallback) + return 0; + + r = vtable_property_get_userdata(bus, m->path, c, &u, &error); + if (r <= 0) + return bus_maybe_reply_error(m, r, &error); + if (bus->nodes_modified) + return 0; + + slot = container_of(c->parent, sd_bus_slot, node_vtable); + + *found_object = true; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + if (is_get) { + /* Note that we do not protect against reexecution + * here (using the last_iteration check, see below), + * should the node tree have changed and we got called + * again. We assume that property Get() calls are + * ultimately without side-effects or if they aren't + * then at least idempotent. */ + + r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature); + if (r < 0) + return r; + + /* Note that we do not do an access check here. Read + * access to properties is always unrestricted, since + * PropertiesChanged signals broadcast contents + * anyway. */ + + r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + + if (bus->nodes_modified) + return 0; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + } else { + const char *signature = NULL; + char type = 0; + + if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member); + + /* Avoid that we call the set routine more than once + * if the processing of this message got restarted + * because the node tree changed. */ + if (c->last_iteration == bus->iteration_counter) + return 0; + + c->last_iteration = bus->iteration_counter; + + r = sd_bus_message_peek_type(m, &type, &signature); + if (r < 0) + return r; + + if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature))) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature)); + + r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature); + if (r < 0) + return r; + + r = check_access(bus, m, c, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + + r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + + if (bus->nodes_modified) + return 0; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + } + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; + + return 1; +} + +static int vtable_append_one_property( + sd_bus *bus, + sd_bus_message *reply, + const char *path, + struct node_vtable *c, + const sd_bus_vtable *v, + void *userdata, + sd_bus_error *error) { + + sd_bus_slot *slot; + int r; + + assert(bus); + assert(reply); + assert(path); + assert(c); + assert(v); + + r = sd_bus_message_open_container(reply, 'e', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", v->x.property.member); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'v', v->x.property.signature); + if (r < 0) + return r; + + slot = container_of(c, sd_bus_slot, node_vtable); + + r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 0; +} + +static int vtable_append_all_properties( + sd_bus *bus, + sd_bus_message *reply, + const char *path, + struct node_vtable *c, + void *userdata, + sd_bus_error *error) { + + const sd_bus_vtable *v; + int r; + + assert(bus); + assert(reply); + assert(path); + assert(c); + + if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) + return 1; + + for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + continue; + + if (v->flags & SD_BUS_VTABLE_HIDDEN) + continue; + + if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) + continue; + + r = vtable_append_one_property(bus, reply, path, c, v, userdata, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 1; +} + +static int property_get_all_callbacks_run( + sd_bus *bus, + sd_bus_message *m, + struct node_vtable *first, + bool require_fallback, + const char *iface, + bool *found_object) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + struct node_vtable *c; + bool found_interface; + int r; + + assert(bus); + assert(m); + assert(found_object); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{sv}"); + if (r < 0) + return r; + + found_interface = !iface || + streq(iface, "org.freedesktop.DBus.Properties") || + streq(iface, "org.freedesktop.DBus.Peer") || + streq(iface, "org.freedesktop.DBus.Introspectable"); + + LIST_FOREACH(vtables, c, first) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + void *u; + + if (require_fallback && !c->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, m->path, c, &u, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + *found_object = true; + + if (iface && !streq(c->interface, iface)) + continue; + found_interface = true; + + r = vtable_append_all_properties(bus, reply, m->path, c, u, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + if (bus->nodes_modified) + return 0; + } + + if (!found_interface) { + r = sd_bus_reply_method_errorf( + m, + SD_BUS_ERROR_UNKNOWN_INTERFACE, + "Unknown interface '%s'.", iface); + if (r < 0) + return r; + + return 1; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; + + return 1; +} + +static int bus_node_exists( + sd_bus *bus, + struct node *n, + const char *path, + bool require_fallback) { + + struct node_vtable *c; + struct node_callback *k; + int r; + + assert(bus); + assert(n); + assert(path); + + /* Tests if there's anything attached directly to this node + * for the specified path */ + + if (!require_fallback && (n->enumerators || n->object_managers)) + return true; + + LIST_FOREACH(callbacks, k, n->callbacks) { + if (require_fallback && !k->is_fallback) + continue; + + return 1; + } + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + if (require_fallback && !c->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, path, c, NULL, &error); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +static int process_introspect( + sd_bus *bus, + sd_bus_message *m, + struct node *n, + bool require_fallback, + bool *found_object) { + + _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; + struct node_vtable *c; + bool empty; + int r; + + assert(bus); + assert(m); + assert(n); + assert(found_object); + + r = get_child_nodes(bus, m->path, n, 0, &s, &error); + if (r < 0) + return bus_maybe_reply_error(m, r, &error); + if (bus->nodes_modified) + return 0; + + r = introspect_begin(&intro, bus->trusted); + if (r < 0) + return r; + + r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers); + if (r < 0) + return r; + + empty = set_isempty(s); + + LIST_FOREACH(vtables, c, n->vtables) { + if (require_fallback && !c->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, m->path, c, NULL, &error); + if (r < 0) { + r = bus_maybe_reply_error(m, r, &error); + goto finish; + } + if (bus->nodes_modified) { + r = 0; + goto finish; + } + if (r == 0) + continue; + + empty = false; + + if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) + continue; + + if (!streq_ptr(previous_interface, c->interface)) { + + if (previous_interface) + fputs("
\n", intro.f); + + fprintf(intro.f, " \n", c->interface); + } + + r = introspect_write_interface(&intro, c->vtable); + if (r < 0) + goto finish; + + previous_interface = c->interface; + } + + if (previous_interface) + fputs(" \n", intro.f); + + if (empty) { + /* Nothing?, let's see if we exist at all, and if not + * refuse to do anything */ + r = bus_node_exists(bus, n, m->path, require_fallback); + if (r <= 0) + goto finish; + if (bus->nodes_modified) { + r = 0; + goto finish; + } + } + + *found_object = true; + + r = introspect_write_child_nodes(&intro, s, m->path); + if (r < 0) + goto finish; + + r = introspect_finish(&intro, bus, m, &reply); + if (r < 0) + goto finish; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + goto finish; + + r = 1; + +finish: + introspect_free(&intro); + return r; +} + +static int object_manager_serialize_path( + sd_bus *bus, + sd_bus_message *reply, + const char *prefix, + const char *path, + bool require_fallback, + sd_bus_error *error) { + + const char *previous_interface = NULL; + bool found_something = false; + struct node_vtable *i; + struct node *n; + int r; + + assert(bus); + assert(reply); + assert(prefix); + assert(path); + assert(error); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, i, n->vtables) { + void *u; + + if (require_fallback && !i->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, path, i, &u, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + if (!found_something) { + + /* Open the object part */ + + r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "o", path); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}"); + if (r < 0) + return r; + + found_something = true; + } + + if (!streq_ptr(previous_interface, i->interface)) { + + /* Maybe close the previous interface part */ + + if (previous_interface) { + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + /* Open the new interface part */ + + r = sd_bus_message_open_container(reply, 'e', "sa{sv}"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", i->interface); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{sv}"); + if (r < 0) + return r; + } + + r = vtable_append_all_properties(bus, reply, path, i, u, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + previous_interface = i->interface; + } + + if (previous_interface) { + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + if (found_something) { + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + return 1; +} + +static int object_manager_serialize_path_and_fallbacks( + sd_bus *bus, + sd_bus_message *reply, + const char *path, + sd_bus_error *error) { + + char *prefix; + int r; + + assert(bus); + assert(reply); + assert(path); + assert(error); + + /* First, add all vtables registered for this path */ + r = object_manager_serialize_path(bus, reply, path, path, false, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + /* Second, add fallback vtables registered for any of the prefixes */ + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = object_manager_serialize_path(bus, reply, prefix, path, true, error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +static int process_get_managed_objects( + sd_bus *bus, + sd_bus_message *m, + struct node *n, + bool require_fallback, + bool *found_object) { + + _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; + int r; + + assert(bus); + assert(m); + assert(n); + assert(found_object); + + /* Spec says, GetManagedObjects() is only implemented on the root of a + * sub-tree. Therefore, we require a registered object-manager on + * exactly the queried path, otherwise, we refuse to respond. */ + + if (require_fallback || !n->object_managers) + return 0; + + r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}"); + if (r < 0) + return r; + + SET_FOREACH(path, s, i) { + r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error); + if (r < 0) + return r; + + if (bus->nodes_modified) + return 0; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; + + return 1; +} + +static int object_find_and_run( + sd_bus *bus, + sd_bus_message *m, + const char *p, + bool require_fallback, + bool *found_object) { + + struct node *n; + struct vtable_member vtable_key, *v; + int r; + + assert(bus); + assert(m); + assert(p); + assert(found_object); + + n = hashmap_get(bus->nodes, p); + if (!n) + return 0; + + /* First, try object callbacks */ + r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + + if (!m->interface || !m->member) + return 0; + + /* Then, look for a known method */ + vtable_key.path = (char*) p; + vtable_key.interface = m->interface; + vtable_key.member = m->member; + + v = hashmap_get(bus->vtable_methods, &vtable_key); + if (v) { + r = method_callbacks_run(bus, m, v, require_fallback, found_object); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + } + + /* Then, look for a known property */ + if (streq(m->interface, "org.freedesktop.DBus.Properties")) { + bool get = false; + + get = streq(m->member, "Get"); + + if (get || streq(m->member, "Set")) { + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + vtable_key.path = (char*) p; + + r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member); + if (r < 0) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters"); + + v = hashmap_get(bus->vtable_properties, &vtable_key); + if (v) { + r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object); + if (r != 0) + return r; + } + + } else if (streq(m->member, "GetAll")) { + const char *iface; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + r = sd_bus_message_read(m, "s", &iface); + if (r < 0) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter"); + + if (iface[0] == 0) + iface = NULL; + + r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object); + if (r != 0) + return r; + } + + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { + + if (!isempty(sd_bus_message_get_signature(m, true))) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters"); + + r = process_introspect(bus, m, n, require_fallback, found_object); + if (r != 0) + return r; + + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) { + + if (!isempty(sd_bus_message_get_signature(m, true))) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters"); + + r = process_get_managed_objects(bus, m, n, require_fallback, found_object); + if (r != 0) + return r; + } + + if (bus->nodes_modified) + return 0; + + if (!*found_object) { + r = bus_node_exists(bus, n, m->path, require_fallback); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r > 0) + *found_object = true; + } + + return 0; +} + +int bus_process_object(sd_bus *bus, sd_bus_message *m) { + int r; + size_t pl; + bool found_object = false; + + assert(bus); + assert(m); + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) + return 0; + + if (hashmap_isempty(bus->nodes)) + return 0; + + /* Never respond to broadcast messages */ + if (bus->bus_client && !m->destination) + return 0; + + assert(m->path); + assert(m->member); + + pl = strlen(m->path); + do { + char prefix[pl+1]; + + bus->nodes_modified = false; + + r = object_find_and_run(bus, m, m->path, false, &found_object); + if (r != 0) + return r; + + /* Look for fallback prefixes */ + OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) { + + if (bus->nodes_modified) + break; + + r = object_find_and_run(bus, m, prefix, true, &found_object); + if (r != 0) + return r; + } + + } while (bus->nodes_modified); + + if (!found_object) + return 0; + + if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") || + sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) + r = sd_bus_reply_method_errorf( + m, + SD_BUS_ERROR_UNKNOWN_PROPERTY, + "Unknown property or interface."); + else + r = sd_bus_reply_method_errorf( + m, + SD_BUS_ERROR_UNKNOWN_METHOD, + "Unknown method '%s' or interface '%s'.", m->member, m->interface); + + if (r < 0) + return r; + + return 1; +} + +static struct node *bus_node_allocate(sd_bus *bus, const char *path) { + struct node *n, *parent; + const char *e; + _cleanup_free_ char *s = NULL; + char *p; + int r; + + assert(bus); + assert(path); + assert(path[0] == '/'); + + n = hashmap_get(bus->nodes, path); + if (n) + return n; + + r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops); + if (r < 0) + return NULL; + + s = strdup(path); + if (!s) + return NULL; + + if (streq(path, "/")) + parent = NULL; + else { + e = strrchr(path, '/'); + assert(e); + + p = strndupa(path, MAX(1, e - path)); + + parent = bus_node_allocate(bus, p); + if (!parent) + return NULL; + } + + n = new0(struct node, 1); + if (!n) + return NULL; + + n->parent = parent; + n->path = s; + s = NULL; /* do not free */ + + r = hashmap_put(bus->nodes, n->path, n); + if (r < 0) { + free(n->path); + free(n); + return NULL; + } + + if (parent) + LIST_PREPEND(siblings, parent->child, n); + + return n; +} + +void bus_node_gc(sd_bus *b, struct node *n) { + assert(b); + + if (!n) + return; + + if (n->child || + n->callbacks || + n->vtables || + n->enumerators || + n->object_managers) + return; + + assert(hashmap_remove(b->nodes, n->path) == n); + + if (n->parent) + LIST_REMOVE(siblings, n->parent->child, n); + + free(n->path); + bus_node_gc(b, n->parent); + free(n); +} + +static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { + struct node *n; + + assert(bus); + assert(path); + + n = hashmap_get(bus->nodes, path); + if (!n) { + char *prefix; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + n = hashmap_get(bus->nodes, prefix); + if (n) + break; + } + } + + while (n && !n->object_managers) + n = n->parent; + + if (out) + *out = n; + return !!n; +} + +static int bus_add_object( + sd_bus *bus, + sd_bus_slot **slot, + bool fallback, + const char *path, + sd_bus_message_handler_t callback, + void *userdata) { + + sd_bus_slot *s; + struct node *n; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + n = bus_node_allocate(bus, path); + if (!n) + return -ENOMEM; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_callback.callback = callback; + s->node_callback.is_fallback = fallback; + + s->node_callback.node = n; + LIST_PREPEND(callbacks, n->callbacks, &s->node_callback); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +_public_ int sd_bus_add_object( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + sd_bus_message_handler_t callback, + void *userdata) { + + return bus_add_object(bus, slot, false, path, callback, userdata); +} + +_public_ int sd_bus_add_fallback( + sd_bus *bus, + sd_bus_slot **slot, + const char *prefix, + sd_bus_message_handler_t callback, + void *userdata) { + + return bus_add_object(bus, slot, true, prefix, callback, userdata); +} + +static void vtable_member_hash_func(const void *a, struct siphash *state) { + const struct vtable_member *m = a; + + assert(m); + + 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) { + const struct vtable_member *x = a, *y = b; + int r; + + assert(x); + assert(y); + + r = strcmp(x->path, y->path); + if (r != 0) + return r; + + r = strcmp(x->interface, y->interface); + if (r != 0) + return r; + + return strcmp(x->member, y->member); +} + +static const struct hash_ops vtable_member_hash_ops = { + .hash = vtable_member_hash_func, + .compare = vtable_member_compare_func +}; + +static int add_object_vtable_internal( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + const char *interface, + const sd_bus_vtable *vtable, + bool fallback, + sd_bus_object_find_t find, + void *userdata) { + + sd_bus_slot *s = NULL; + struct node_vtable *i, *existing = NULL; + const sd_bus_vtable *v; + struct node *n; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(interface_name_is_valid(interface), -EINVAL); + assert_return(vtable, -EINVAL); + assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL); + assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!streq(interface, "org.freedesktop.DBus.Properties") && + !streq(interface, "org.freedesktop.DBus.Introspectable") && + !streq(interface, "org.freedesktop.DBus.Peer") && + !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL); + + r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops); + if (r < 0) + return r; + + n = bus_node_allocate(bus, path); + if (!n) + return -ENOMEM; + + LIST_FOREACH(vtables, i, n->vtables) { + if (i->is_fallback != fallback) { + r = -EPROTOTYPE; + goto fail; + } + + if (streq(i->interface, interface)) { + + if (i->vtable == vtable) { + r = -EEXIST; + goto fail; + } + + existing = i; + } + } + + s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_vtable.is_fallback = fallback; + s->node_vtable.vtable = vtable; + s->node_vtable.find = find; + + s->node_vtable.interface = strdup(interface); + if (!s->node_vtable.interface) { + r = -ENOMEM; + goto fail; + } + + for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + + switch (v->type) { + + case _SD_BUS_VTABLE_METHOD: { + struct vtable_member *m; + + if (!member_name_is_valid(v->x.method.member) || + !signature_is_valid(strempty(v->x.method.signature), false) || + !signature_is_valid(strempty(v->x.method.result), false) || + !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) || + v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) { + r = -EINVAL; + goto fail; + } + + m = new0(struct vtable_member, 1); + if (!m) { + r = -ENOMEM; + goto fail; + } + + m->parent = &s->node_vtable; + m->path = n->path; + m->interface = s->node_vtable.interface; + m->member = v->x.method.member; + m->vtable = v; + + r = hashmap_put(bus->vtable_methods, m, m); + if (r < 0) { + free(m); + goto fail; + } + + break; + } + + case _SD_BUS_VTABLE_WRITABLE_PROPERTY: + + if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) { + r = -EINVAL; + goto fail; + } + + if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) { + r = -EINVAL; + goto fail; + } + + /* Fall through */ + + case _SD_BUS_VTABLE_PROPERTY: { + struct vtable_member *m; + + if (!member_name_is_valid(v->x.property.member) || + !signature_is_single(v->x.property.signature, false) || + !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) || + (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) || + (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 || + ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) || + (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) { + r = -EINVAL; + goto fail; + } + + m = new0(struct vtable_member, 1); + if (!m) { + r = -ENOMEM; + goto fail; + } + + m->parent = &s->node_vtable; + m->path = n->path; + m->interface = s->node_vtable.interface; + m->member = v->x.property.member; + m->vtable = v; + + r = hashmap_put(bus->vtable_properties, m, m); + if (r < 0) { + free(m); + goto fail; + } + + break; + } + + case _SD_BUS_VTABLE_SIGNAL: + + if (!member_name_is_valid(v->x.signal.member) || + !signature_is_valid(strempty(v->x.signal.signature), false) || + v->flags & SD_BUS_VTABLE_UNPRIVILEGED) { + r = -EINVAL; + goto fail; + } + + break; + + default: + r = -EINVAL; + goto fail; + } + } + + s->node_vtable.node = n; + LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +_public_ int sd_bus_add_object_vtable( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + const char *interface, + const sd_bus_vtable *vtable, + void *userdata) { + + return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata); +} + +_public_ int sd_bus_add_fallback_vtable( + sd_bus *bus, + sd_bus_slot **slot, + const char *prefix, + const char *interface, + const sd_bus_vtable *vtable, + sd_bus_object_find_t find, + void *userdata) { + + return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata); +} + +_public_ int sd_bus_add_node_enumerator( + sd_bus *bus, + sd_bus_slot **slot, + const char *path, + sd_bus_node_enumerator_t callback, + void *userdata) { + + sd_bus_slot *s; + struct node *n; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + n = bus_node_allocate(bus, path); + if (!n) + return -ENOMEM; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_enumerator.callback = callback; + + s->node_enumerator.node = n; + LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} + +static int emit_properties_changed_on_interface( + sd_bus *bus, + const char *prefix, + const char *path, + const char *interface, + bool require_fallback, + bool *found_interface, + char **names) { + + _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; + struct node *n; + char **property; + void *u = NULL; + int r; + + assert(bus); + assert(prefix); + assert(path); + assert(interface); + assert(found_interface); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", interface); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sv}"); + if (r < 0) + return r; + + key.path = prefix; + key.interface = interface; + + LIST_FOREACH(vtables, c, n->vtables) { + if (require_fallback && !c->is_fallback) + continue; + + if (!streq(c->interface, interface)) + continue; + + r = node_vtable_get_userdata(bus, path, c, &u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + *found_interface = true; + + if (names) { + /* If the caller specified a list of + * properties we include exactly those in the + * PropertiesChanged message */ + + STRV_FOREACH(property, names) { + struct vtable_member *v; + + assert_return(member_name_is_valid(*property), -EINVAL); + + key.member = *property; + v = hashmap_get(bus->vtable_properties, &key); + if (!v) + return -ENOENT; + + /* If there are two vtables for the same + * interface, let's handle this property when + * we come to that vtable. */ + if (c != v->parent) + continue; + + assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE || + v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM); + + assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM); + + if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { + has_invalidating = true; + continue; + } + + has_changing = true; + + r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + } else { + const sd_bus_vtable *v; + + /* If the caller specified no properties list + * we include all properties that are marked + * as changing in the message. */ + + for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + continue; + + if (v->flags & SD_BUS_VTABLE_HIDDEN) + continue; + + if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { + has_invalidating = true; + continue; + } + + if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) + continue; + + has_changing = true; + + r = vtable_append_one_property(bus, m, m->path, c, v, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + } + } + + if (!has_invalidating && !has_changing) + return 0; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return r; + + if (has_invalidating) { + LIST_FOREACH(vtables, c, n->vtables) { + if (require_fallback && !c->is_fallback) + continue; + + if (!streq(c->interface, interface)) + continue; + + r = node_vtable_get_userdata(bus, path, c, &u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + if (names) { + STRV_FOREACH(property, names) { + struct vtable_member *v; + + key.member = *property; + assert_se(v = hashmap_get(bus->vtable_properties, &key)); + assert(c == v->parent); + + if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) + continue; + + r = sd_bus_message_append(m, "s", *property); + if (r < 0) + return r; + } + } else { + const sd_bus_vtable *v; + + for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + continue; + + if (v->flags & SD_BUS_VTABLE_HIDDEN) + continue; + + if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) + continue; + + r = sd_bus_message_append(m, "s", v->x.property.member); + if (r < 0) + return r; + } + } + } + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_send(bus, m, NULL); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_bus_emit_properties_changed_strv( + sd_bus *bus, + const char *path, + const char *interface, + char **names) { + + BUS_DONT_DESTROY(bus); + bool found_interface = false; + char *prefix; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(interface_name_is_valid(interface), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + /* A non-NULL but empty names list means nothing needs to be + generated. A NULL list OTOH indicates that all properties + that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be + included in the PropertiesChanged message. */ + if (names && names[0] == NULL) + return 0; + + do { + bus->nodes_modified = false; + + r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names); + if (r != 0) + return r; + if (bus->nodes_modified) + continue; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names); + if (r != 0) + return r; + if (bus->nodes_modified) + break; + } + + } while (bus->nodes_modified); + + return found_interface ? 0 : -ENOENT; +} + +_public_ int sd_bus_emit_properties_changed( + sd_bus *bus, + const char *path, + const char *interface, + const char *name, ...) { + + char **names; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(interface_name_is_valid(interface), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (!name) + return 0; + + names = strv_from_stdarg_alloca(name); + + return sd_bus_emit_properties_changed_strv(bus, path, interface, names); +} + +static int object_added_append_all_prefix( + sd_bus *bus, + sd_bus_message *m, + Set *s, + const char *prefix, + const char *path, + bool require_fallback) { + + const char *previous_interface = NULL; + struct node_vtable *c; + struct node *n; + int r; + + assert(bus); + assert(m); + assert(s); + assert(prefix); + assert(path); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + void *u = NULL; + + if (require_fallback && !c->is_fallback) + continue; + + r = node_vtable_get_userdata(bus, path, c, &u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + if (!streq_ptr(c->interface, previous_interface)) { + /* If a child-node already handled this interface, we + * skip it on any of its parents. The child vtables + * always fully override any conflicting vtables of + * any parent node. */ + if (set_get(s, c->interface)) + continue; + + r = set_put(s, c->interface); + if (r < 0) + return r; + + if (previous_interface) { + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_open_container(m, 'e', "sa{sv}"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", c->interface); + if (r < 0) + return r; + r = sd_bus_message_open_container(m, 'a', "{sv}"); + if (r < 0) + return r; + + previous_interface = c->interface; + } + + r = vtable_append_all_properties(bus, m, path, c, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + if (previous_interface) { + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + return 0; +} + +static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { + _cleanup_set_free_ Set *s = NULL; + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + + /* + * This appends all interfaces registered on path @path. We first add + * the builtin interfaces, which are always available and handled by + * sd-bus. Then, we add all interfaces registered on the exact node, + * followed by all fallback interfaces registered on any parent prefix. + * + * If an interface is registered multiple times on the same node with + * different vtables, we merge all the properties across all vtables. + * However, if a child node has the same interface registered as one of + * its parent nodes has as fallback, we make the child overwrite the + * parent instead of extending it. Therefore, we keep a "Set" of all + * handled interfaces during parent traversal, so we skip interfaces on + * a parent that were overwritten by a child. + */ + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); + if (r < 0) + return r; + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); + if (r < 0) + return r; + + r = object_added_append_all_prefix(bus, m, s, path, path, false); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = object_added_append_all_prefix(bus, m, s, prefix, path, true); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { + BUS_DONT_DESTROY(bus); + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + /* + * This emits an InterfacesAdded signal on the given path, by iterating + * all registered vtables and fallback vtables on the path. All + * properties are queried and included in the signal. + * This call is equivalent to sd_bus_emit_interfaces_added() with an + * explicit list of registered interfaces. However, unlike + * interfaces_added(), this call can figure out the list of supported + * interfaces itself. Furthermore, it properly adds the builtin + * org.freedesktop.DBus.* interfaces. + */ + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); + if (r < 0) + return r; + + r = object_added_append_all(bus, m, path); + if (r < 0) + return r; + + if (bus->nodes_modified) + continue; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + } while (bus->nodes_modified); + + return sd_bus_send(bus, m, NULL); +} + +static int object_removed_append_all_prefix( + sd_bus *bus, + sd_bus_message *m, + Set *s, + const char *prefix, + const char *path, + bool require_fallback) { + + const char *previous_interface = NULL; + struct node_vtable *c; + struct node *n; + int r; + + assert(bus); + assert(m); + assert(s); + assert(prefix); + assert(path); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + void *u = NULL; + + if (require_fallback && !c->is_fallback) + continue; + if (streq_ptr(c->interface, previous_interface)) + continue; + + /* If a child-node already handled this interface, we + * skip it on any of its parents. The child vtables + * always fully override any conflicting vtables of + * any parent node. */ + if (set_get(s, c->interface)) + continue; + + r = node_vtable_get_userdata(bus, path, c, &u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + r = set_put(s, c->interface); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", c->interface); + if (r < 0) + return r; + + previous_interface = c->interface; + } + + return 0; +} + +static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { + _cleanup_set_free_ Set *s = NULL; + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + + /* see sd_bus_emit_object_added() for details */ + + s = set_new(&string_hash_ops); + if (!s) + return -ENOMEM; + + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties"); + if (r < 0) + return r; + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); + if (r < 0) + return r; + + r = object_removed_append_all_prefix(bus, m, s, path, path, false); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = object_removed_append_all_prefix(bus, m, s, prefix, path, true); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return 0; +} + +_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { + BUS_DONT_DESTROY(bus); + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + /* + * This is like sd_bus_emit_object_added(), but emits an + * InterfacesRemoved signal on the given path. This only includes any + * registered interfaces but skips the properties. Note that this will + * call into the find() callbacks of any registered vtable. Therefore, + * you must call this function before destroying/unlinking your object. + * Otherwise, the list of interfaces will be incomplete. However, note + * that this will *NOT* call into any property callback. Therefore, the + * object might be in an "destructed" state, as long as we can find it. + */ + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return r; + + r = object_removed_append_all(bus, m, path); + if (r < 0) + return r; + + if (bus->nodes_modified) + continue; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + } while (bus->nodes_modified); + + return sd_bus_send(bus, m, NULL); +} + +static int interfaces_added_append_one_prefix( + sd_bus *bus, + sd_bus_message *m, + const char *prefix, + const char *path, + const char *interface, + bool require_fallback) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + bool found_interface = false; + struct node_vtable *c; + struct node *n; + void *u = NULL; + int r; + + assert(bus); + assert(m); + assert(prefix); + assert(path); + assert(interface); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + if (require_fallback && !c->is_fallback) + continue; + + if (!streq(c->interface, interface)) + continue; + + r = node_vtable_get_userdata(bus, path, c, &u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + if (r == 0) + continue; + + if (!found_interface) { + r = sd_bus_message_append_basic(m, 's', interface); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sv}"); + if (r < 0) + return r; + + found_interface = true; + } + + r = vtable_append_all_properties(bus, m, path, c, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } + + if (found_interface) { + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + return found_interface; +} + +static int interfaces_added_append_one( + sd_bus *bus, + sd_bus_message *m, + const char *path, + const char *interface) { + + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + assert(interface); + + r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true); + if (r != 0) + return r; + if (bus->nodes_modified) + return 0; + } + + return -ENOENT; +} + +_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) { + BUS_DONT_DESTROY(bus); + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + char **i; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (strv_isempty(interfaces)) + return 0; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); + if (r < 0) + return r; + + STRV_FOREACH(i, interfaces) { + assert_return(interface_name_is_valid(*i), -EINVAL); + + r = sd_bus_message_open_container(m, 'e', "sa{sv}"); + if (r < 0) + return r; + + r = interfaces_added_append_one(bus, m, path, *i); + if (r < 0) + return r; + + if (bus->nodes_modified) + break; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + if (bus->nodes_modified) + continue; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + } while (bus->nodes_modified); + + return sd_bus_send(bus, m, NULL); +} + +_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) { + char **interfaces; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + interfaces = strv_from_stdarg_alloca(interface); + + return sd_bus_emit_interfaces_added_strv(bus, path, interfaces); +} + +_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (strv_isempty(interfaces)) + return 0; + + r = bus_find_parent_object_manager(bus, &object_manager, path); + if (r < 0) + return r; + if (r == 0) + return -ESRCH; + + r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, interfaces); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) { + char **interfaces; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + interfaces = strv_from_stdarg_alloca(interface); + + return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces); +} + +_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) { + sd_bus_slot *s; + struct node *n; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + n = bus_node_allocate(bus, path); + if (!n) + return -ENOMEM; + + s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL); + if (!s) { + r = -ENOMEM; + goto fail; + } + + s->node_object_manager.node = n; + LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager); + bus->nodes_modified = true; + + if (slot) + *slot = s; + + return 0; + +fail: + sd_bus_slot_unref(s); + bus_node_gc(bus, n); + + return r; +} diff --git a/src/libsystemd/src/sd-bus/bus-objects.h b/src/libsystemd/src/sd-bus/bus-objects.h new file mode 100644 index 0000000000..e0b8c534ed --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-objects.h @@ -0,0 +1,25 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "bus-internal.h" + +int bus_process_object(sd_bus *bus, sd_bus_message *m); +void bus_node_gc(sd_bus *b, struct node *n); diff --git a/src/libsystemd/src/sd-bus/bus-protocol.h b/src/libsystemd/src/sd-bus/bus-protocol.h new file mode 100644 index 0000000000..931dc21c46 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-protocol.h @@ -0,0 +1,180 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/macro.h" + +/* Packet header */ + +struct _packed_ bus_header { + /* The first four fields are identical for dbus1, and dbus2 */ + uint8_t endian; + uint8_t type; + uint8_t flags; + uint8_t version; + + union _packed_ { + /* dbus1: Used for SOCK_STREAM connections */ + struct _packed_ { + uint32_t body_size; + + /* Note that what the bus spec calls "serial" we'll call + "cookie" instead, because we don't want to imply that the + cookie was in any way monotonically increasing. */ + uint32_t serial; + uint32_t fields_size; + } dbus1; + + /* dbus2: Used for kdbus connections */ + struct _packed_ { + uint32_t _reserved; + uint64_t cookie; + } dbus2; + + /* Note that both header versions have the same size! */ + }; +}; + +/* Endianness */ + +enum { + _BUS_INVALID_ENDIAN = 0, + BUS_LITTLE_ENDIAN = 'l', + BUS_BIG_ENDIAN = 'B', +#if __BYTE_ORDER == __BIG_ENDIAN + BUS_NATIVE_ENDIAN = BUS_BIG_ENDIAN, + BUS_REVERSE_ENDIAN = BUS_LITTLE_ENDIAN +#else + BUS_NATIVE_ENDIAN = BUS_LITTLE_ENDIAN, + BUS_REVERSE_ENDIAN = BUS_BIG_ENDIAN +#endif +}; + +/* Flags */ + +enum { + BUS_MESSAGE_NO_REPLY_EXPECTED = 1, + BUS_MESSAGE_NO_AUTO_START = 2, + BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION = 4, +}; + +/* Header fields */ + +enum { + _BUS_MESSAGE_HEADER_INVALID = 0, + BUS_MESSAGE_HEADER_PATH, + BUS_MESSAGE_HEADER_INTERFACE, + BUS_MESSAGE_HEADER_MEMBER, + BUS_MESSAGE_HEADER_ERROR_NAME, + BUS_MESSAGE_HEADER_REPLY_SERIAL, + BUS_MESSAGE_HEADER_DESTINATION, + BUS_MESSAGE_HEADER_SENDER, + BUS_MESSAGE_HEADER_SIGNATURE, + BUS_MESSAGE_HEADER_UNIX_FDS, + _BUS_MESSAGE_HEADER_MAX +}; + +/* RequestName parameters */ + +enum { + BUS_NAME_ALLOW_REPLACEMENT = 1, + BUS_NAME_REPLACE_EXISTING = 2, + BUS_NAME_DO_NOT_QUEUE = 4 +}; + +/* RequestName returns */ +enum { + BUS_NAME_PRIMARY_OWNER = 1, + BUS_NAME_IN_QUEUE = 2, + BUS_NAME_EXISTS = 3, + BUS_NAME_ALREADY_OWNER = 4 +}; + +/* ReleaseName returns */ +enum { + BUS_NAME_RELEASED = 1, + BUS_NAME_NON_EXISTENT = 2, + BUS_NAME_NOT_OWNER = 3, +}; + +/* StartServiceByName returns */ +enum { + BUS_START_REPLY_SUCCESS = 1, + BUS_START_REPLY_ALREADY_RUNNING = 2, +}; + +#define BUS_INTROSPECT_DOCTYPE \ + "\n" + +#define BUS_INTROSPECT_INTERFACE_PEER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_PROPERTIES \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" diff --git a/src/libsystemd/src/sd-bus/bus-signature.c b/src/libsystemd/src/sd-bus/bus-signature.c new file mode 100644 index 0000000000..c11b113186 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-signature.c @@ -0,0 +1,158 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/util.h" + +#include "bus-signature.h" +#include "bus-type.h" + +static int signature_element_length_internal( + const char *s, + bool allow_dict_entry, + unsigned array_depth, + unsigned struct_depth, + size_t *l) { + + int r; + + if (!s) + return -EINVAL; + + assert(l); + + if (bus_type_is_basic(*s) || *s == SD_BUS_TYPE_VARIANT) { + *l = 1; + return 0; + } + + if (*s == SD_BUS_TYPE_ARRAY) { + size_t t; + + if (array_depth >= 32) + return -EINVAL; + + r = signature_element_length_internal(s + 1, true, array_depth+1, struct_depth, &t); + if (r < 0) + return r; + + *l = t + 1; + return 0; + } + + if (*s == SD_BUS_TYPE_STRUCT_BEGIN) { + const char *p = s + 1; + + if (struct_depth >= 32) + return -EINVAL; + + while (*p != SD_BUS_TYPE_STRUCT_END) { + size_t t; + + r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t); + if (r < 0) + return r; + + p += t; + } + + *l = p - s + 1; + return 0; + } + + if (*s == SD_BUS_TYPE_DICT_ENTRY_BEGIN && allow_dict_entry) { + const char *p = s + 1; + unsigned n = 0; + + if (struct_depth >= 32) + return -EINVAL; + + while (*p != SD_BUS_TYPE_DICT_ENTRY_END) { + size_t t; + + if (n == 0 && !bus_type_is_basic(*p)) + return -EINVAL; + + r = signature_element_length_internal(p, false, array_depth, struct_depth+1, &t); + if (r < 0) + return r; + + p += t; + n++; + } + + if (n != 2) + return -EINVAL; + + *l = p - s + 1; + return 0; + } + + return -EINVAL; +} + + +int signature_element_length(const char *s, size_t *l) { + return signature_element_length_internal(s, true, 0, 0, l); +} + +bool signature_is_single(const char *s, bool allow_dict_entry) { + int r; + size_t t; + + if (!s) + return false; + + r = signature_element_length_internal(s, allow_dict_entry, 0, 0, &t); + if (r < 0) + return false; + + return s[t] == 0; +} + +bool signature_is_pair(const char *s) { + + if (!s) + return false; + + if (!bus_type_is_basic(*s)) + return false; + + return signature_is_single(s + 1, false); +} + +bool signature_is_valid(const char *s, bool allow_dict_entry) { + const char *p; + int r; + + if (!s) + return false; + + p = s; + while (*p) { + size_t t; + + r = signature_element_length_internal(p, allow_dict_entry, 0, 0, &t); + if (r < 0) + return false; + + p += t; + } + + return p - s <= 255; +} diff --git a/src/libsystemd/src/sd-bus/bus-signature.h b/src/libsystemd/src/sd-bus/bus-signature.h new file mode 100644 index 0000000000..1e0cd7f587 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-signature.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +bool signature_is_single(const char *s, bool allow_dict_entry); +bool signature_is_pair(const char *s); +bool signature_is_valid(const char *s, bool allow_dict_entry); + +int signature_element_length(const char *s, size_t *l); diff --git a/src/libsystemd/src/sd-bus/bus-slot.c b/src/libsystemd/src/sd-bus/bus-slot.c new file mode 100644 index 0000000000..ebac784433 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-slot.c @@ -0,0 +1,287 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/string-util.h" + +#include "bus-control.h" +#include "bus-objects.h" +#include "bus-slot.h" + +sd_bus_slot *bus_slot_allocate( + sd_bus *bus, + bool floating, + BusSlotType type, + size_t extra, + void *userdata) { + + sd_bus_slot *slot; + + assert(bus); + + slot = malloc0(offsetof(sd_bus_slot, reply_callback) + extra); + if (!slot) + return NULL; + + slot->n_ref = 1; + slot->type = type; + slot->bus = bus; + slot->floating = floating; + slot->userdata = userdata; + + if (!floating) + sd_bus_ref(bus); + + LIST_PREPEND(slots, bus->slots, slot); + + return slot; +} + +_public_ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot) { + + if (!slot) + return NULL; + + assert(slot->n_ref > 0); + + slot->n_ref++; + return slot; +} + +void bus_slot_disconnect(sd_bus_slot *slot) { + sd_bus *bus; + + assert(slot); + + if (!slot->bus) + return; + + switch (slot->type) { + + case BUS_REPLY_CALLBACK: + + if (slot->reply_callback.cookie != 0) + ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); + + if (slot->reply_callback.timeout != 0) + prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); + + break; + + case BUS_FILTER_CALLBACK: + slot->bus->filter_callbacks_modified = true; + LIST_REMOVE(callbacks, slot->bus->filter_callbacks, &slot->filter_callback); + break; + + case BUS_MATCH_CALLBACK: + + if (slot->match_added) + bus_remove_match_internal(slot->bus, slot->match_callback.match_string, slot->match_callback.cookie); + + slot->bus->match_callbacks_modified = true; + bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); + + free(slot->match_callback.match_string); + + break; + + case BUS_NODE_CALLBACK: + + if (slot->node_callback.node) { + LIST_REMOVE(callbacks, slot->node_callback.node->callbacks, &slot->node_callback); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_callback.node); + } + + break; + + case BUS_NODE_ENUMERATOR: + + if (slot->node_enumerator.node) { + LIST_REMOVE(enumerators, slot->node_enumerator.node->enumerators, &slot->node_enumerator); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_enumerator.node); + } + + break; + + case BUS_NODE_OBJECT_MANAGER: + + if (slot->node_object_manager.node) { + LIST_REMOVE(object_managers, slot->node_object_manager.node->object_managers, &slot->node_object_manager); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_object_manager.node); + } + + break; + + case BUS_NODE_VTABLE: + + if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) { + const sd_bus_vtable *v; + + for (v = slot->node_vtable.vtable; v->type != _SD_BUS_VTABLE_END; v++) { + struct vtable_member *x = NULL; + + switch (v->type) { + + case _SD_BUS_VTABLE_METHOD: { + struct vtable_member key; + + key.path = slot->node_vtable.node->path; + key.interface = slot->node_vtable.interface; + key.member = v->x.method.member; + + x = hashmap_remove(slot->bus->vtable_methods, &key); + break; + } + + case _SD_BUS_VTABLE_PROPERTY: + case _SD_BUS_VTABLE_WRITABLE_PROPERTY: { + struct vtable_member key; + + key.path = slot->node_vtable.node->path; + key.interface = slot->node_vtable.interface; + key.member = v->x.method.member; + + + x = hashmap_remove(slot->bus->vtable_properties, &key); + break; + }} + + free(x); + } + } + + free(slot->node_vtable.interface); + + if (slot->node_vtable.node) { + LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); + slot->bus->nodes_modified = true; + + bus_node_gc(slot->bus, slot->node_vtable.node); + } + + break; + + default: + assert_not_reached("Wut? Unknown slot type?"); + } + + bus = slot->bus; + + slot->type = _BUS_SLOT_INVALID; + slot->bus = NULL; + LIST_REMOVE(slots, bus->slots, slot); + + if (!slot->floating) + sd_bus_unref(bus); +} + +_public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { + + if (!slot) + return NULL; + + assert(slot->n_ref > 0); + + if (slot->n_ref > 1) { + slot->n_ref--; + return NULL; + } + + bus_slot_disconnect(slot); + free(slot->description); + free(slot); + + return NULL; +} + +_public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) { + assert_return(slot, NULL); + + return slot->bus; +} + +_public_ void *sd_bus_slot_get_userdata(sd_bus_slot *slot) { + assert_return(slot, NULL); + + return slot->userdata; +} + +_public_ void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata) { + void *ret; + + assert_return(slot, NULL); + + ret = slot->userdata; + slot->userdata = userdata; + + return ret; +} + +_public_ sd_bus_message *sd_bus_slot_get_current_message(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_message; +} + +_public_ sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_handler; +} + +_public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) { + assert_return(slot, NULL); + assert_return(slot->type >= 0, NULL); + + if (slot->bus->current_slot != slot) + return NULL; + + return slot->bus->current_userdata; +} + +_public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) { + assert_return(slot, -EINVAL); + + return free_and_strdup(&slot->description, description); +} + +_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) { + assert_return(slot, -EINVAL); + assert_return(description, -EINVAL); + assert_return(slot->description, -ENXIO); + + *description = slot->description; + return 0; +} diff --git a/src/libsystemd/src/sd-bus/bus-slot.h b/src/libsystemd/src/sd-bus/bus-slot.h new file mode 100644 index 0000000000..b862799d1c --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-slot.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "bus-internal.h" + +sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata); + +void bus_slot_disconnect(sd_bus_slot *slot); diff --git a/src/libsystemd/src/sd-bus/bus-socket.c b/src/libsystemd/src/sd-bus/bus-socket.c new file mode 100644 index 0000000000..b5fd60cf24 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-socket.c @@ -0,0 +1,1065 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/utf8.h" +#include "basic/util.h" + +#include "bus-internal.h" +#include "bus-message.h" +#include "bus-socket.h" + +#define SNDBUF_SIZE (8*1024*1024) + +static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) { + + while (size > 0) { + struct iovec *i = iov + *idx; + + if (i->iov_len > size) { + i->iov_base = (uint8_t*) i->iov_base + size; + i->iov_len -= size; + return; + } + + size -= i->iov_len; + + i->iov_base = NULL; + i->iov_len = 0; + + (*idx)++; + } +} + +static int append_iovec(sd_bus_message *m, const void *p, size_t sz) { + assert(m); + assert(p); + assert(sz > 0); + + m->iovec[m->n_iovec].iov_base = (void*) p; + m->iovec[m->n_iovec].iov_len = sz; + m->n_iovec++; + + return 0; +} + +static int bus_message_setup_iovec(sd_bus_message *m) { + struct bus_body_part *part; + unsigned n, i; + int r; + + assert(m); + assert(m->sealed); + + if (m->n_iovec > 0) + return 0; + + assert(!m->iovec); + + n = 1 + m->n_body_parts; + if (n < ELEMENTSOF(m->iovec_fixed)) + m->iovec = m->iovec_fixed; + else { + m->iovec = new(struct iovec, n); + if (!m->iovec) { + r = -ENOMEM; + goto fail; + } + } + + r = append_iovec(m, m->header, BUS_MESSAGE_BODY_BEGIN(m)); + if (r < 0) + goto fail; + + MESSAGE_FOREACH_PART(part, i, m) { + r = bus_body_part_map(part); + if (r < 0) + goto fail; + + r = append_iovec(m, part->data, part->size); + if (r < 0) + goto fail; + } + + assert(n == m->n_iovec); + + return 0; + +fail: + m->poisoned = true; + return r; +} + +bool bus_socket_auth_needs_write(sd_bus *b) { + + unsigned i; + + if (b->auth_index >= ELEMENTSOF(b->auth_iovec)) + return false; + + for (i = b->auth_index; i < ELEMENTSOF(b->auth_iovec); i++) { + struct iovec *j = b->auth_iovec + i; + + if (j->iov_len > 0) + return true; + } + + return false; +} + +static int bus_socket_write_auth(sd_bus *b) { + ssize_t k; + + assert(b); + assert(b->state == BUS_AUTHENTICATING); + + if (!bus_socket_auth_needs_write(b)) + return 0; + + if (b->prefer_writev) + k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); + else { + struct msghdr mh; + zero(mh); + + mh.msg_iov = b->auth_iovec + b->auth_index; + mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index; + + k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); + if (k < 0 && errno == ENOTSOCK) { + b->prefer_writev = true; + k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index); + } + } + + if (k < 0) + return errno == EAGAIN ? 0 : -errno; + + iovec_advance(b->auth_iovec, &b->auth_index, (size_t) k); + return 1; +} + +static int bus_socket_auth_verify_client(sd_bus *b) { + char *e, *f, *start; + sd_id128_t peer; + unsigned i; + int r; + + assert(b); + + /* We expect two response lines: "OK" and possibly + * "AGREE_UNIX_FD" */ + + e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2); + if (!e) + return 0; + + if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) { + f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2); + if (!f) + return 0; + + start = f + 2; + } else { + f = NULL; + start = e + 2; + } + + /* Nice! We got all the lines we need. First check the OK + * line */ + + if (e - (char*) b->rbuffer != 3 + 32) + return -EPERM; + + if (memcmp(b->rbuffer, "OK ", 3)) + return -EPERM; + + b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL; + + for (i = 0; i < 32; i += 2) { + int x, y; + + x = unhexchar(((char*) b->rbuffer)[3 + i]); + y = unhexchar(((char*) b->rbuffer)[3 + i + 1]); + + if (x < 0 || y < 0) + return -EINVAL; + + peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y); + } + + if (!sd_id128_is_null(b->server_id) && + !sd_id128_equal(b->server_id, peer)) + return -EPERM; + + b->server_id = peer; + + /* And possibly check the second line, too */ + + if (f) + b->can_fds = + (f - e == strlen("\r\nAGREE_UNIX_FD")) && + memcmp(e + 2, "AGREE_UNIX_FD", strlen("AGREE_UNIX_FD")) == 0; + + b->rbuffer_size -= (start - (char*) b->rbuffer); + memmove(b->rbuffer, start, b->rbuffer_size); + + r = bus_start_running(b); + if (r < 0) + return r; + + return 1; +} + +static bool line_equals(const char *s, size_t m, const char *line) { + size_t l; + + l = strlen(line); + if (l != m) + return false; + + return memcmp(s, line, l) == 0; +} + +static bool line_begins(const char *s, size_t m, const char *word) { + size_t l; + + l = strlen(word); + if (m < l) + return false; + + if (memcmp(s, word, l) != 0) + return false; + + return m == l || (m > l && s[l] == ' '); +} + +static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { + _cleanup_free_ char *token = NULL; + size_t len; + int r; + + if (!b->anonymous_auth) + return 0; + + if (l <= 0) + return 1; + + assert(p[0] == ' '); + p++; l--; + + if (l % 2 != 0) + return 0; + + r = unhexmem(p, l, (void **) &token, &len); + if (r < 0) + return 0; + + if (memchr(token, 0, len)) + return 0; + + return !!utf8_is_valid(token); +} + +static int verify_external_token(sd_bus *b, const char *p, size_t l) { + _cleanup_free_ char *token = NULL; + size_t len; + uid_t u; + int r; + + /* We don't do any real authentication here. Instead, we if + * the owner of this bus wanted authentication he should have + * checked SO_PEERCRED before even creating the bus object. */ + + if (!b->anonymous_auth && !b->ucred_valid) + return 0; + + if (l <= 0) + return 1; + + assert(p[0] == ' '); + p++; l--; + + if (l % 2 != 0) + return 0; + + r = unhexmem(p, l, (void**) &token, &len); + if (r < 0) + return 0; + + if (memchr(token, 0, len)) + return 0; + + r = parse_uid(token, &u); + if (r < 0) + return 0; + + /* We ignore the passed value if anonymous authentication is + * on anyway. */ + if (!b->anonymous_auth && u != b->ucred.uid) + return 0; + + return 1; +} + +static int bus_socket_auth_write(sd_bus *b, const char *t) { + char *p; + size_t l; + + assert(b); + assert(t); + + /* We only make use of the first iovec */ + assert(b->auth_index == 0 || b->auth_index == 1); + + l = strlen(t); + p = malloc(b->auth_iovec[0].iov_len + l); + if (!p) + return -ENOMEM; + + memcpy_safe(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len); + memcpy(p + b->auth_iovec[0].iov_len, t, l); + + b->auth_iovec[0].iov_base = p; + b->auth_iovec[0].iov_len += l; + + free(b->auth_buffer); + b->auth_buffer = p; + b->auth_index = 0; + return 0; +} + +static int bus_socket_auth_write_ok(sd_bus *b) { + char t[3 + 32 + 2 + 1]; + + assert(b); + + xsprintf(t, "OK " SD_ID128_FORMAT_STR "\r\n", SD_ID128_FORMAT_VAL(b->server_id)); + + return bus_socket_auth_write(b, t); +} + +static int bus_socket_auth_verify_server(sd_bus *b) { + char *e; + const char *line; + size_t l; + bool processed = false; + int r; + + assert(b); + + if (b->rbuffer_size < 1) + return 0; + + /* First char must be a NUL byte */ + if (*(char*) b->rbuffer != 0) + return -EIO; + + if (b->rbuffer_size < 3) + return 0; + + /* Begin with the first line */ + if (b->auth_rbegin <= 0) + b->auth_rbegin = 1; + + for (;;) { + /* Check if line is complete */ + line = (char*) b->rbuffer + b->auth_rbegin; + e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2); + if (!e) + return processed; + + l = e - line; + + if (line_begins(line, l, "AUTH ANONYMOUS")) { + + r = verify_anonymous_token(b, line + 14, l - 14); + if (r < 0) + return r; + if (r == 0) + r = bus_socket_auth_write(b, "REJECTED\r\n"); + else { + b->auth = BUS_AUTH_ANONYMOUS; + r = bus_socket_auth_write_ok(b); + } + + } else if (line_begins(line, l, "AUTH EXTERNAL")) { + + r = verify_external_token(b, line + 13, l - 13); + if (r < 0) + return r; + if (r == 0) + r = bus_socket_auth_write(b, "REJECTED\r\n"); + else { + b->auth = BUS_AUTH_EXTERNAL; + r = bus_socket_auth_write_ok(b); + } + + } else if (line_begins(line, l, "AUTH")) + r = bus_socket_auth_write(b, "REJECTED EXTERNAL ANONYMOUS\r\n"); + else if (line_equals(line, l, "CANCEL") || + line_begins(line, l, "ERROR")) { + + b->auth = _BUS_AUTH_INVALID; + r = bus_socket_auth_write(b, "REJECTED\r\n"); + + } else if (line_equals(line, l, "BEGIN")) { + + if (b->auth == _BUS_AUTH_INVALID) + r = bus_socket_auth_write(b, "ERROR\r\n"); + else { + /* We can't leave from the auth phase + * before we haven't written + * everything queued, so let's check + * that */ + + if (bus_socket_auth_needs_write(b)) + return 1; + + b->rbuffer_size -= (e + 2 - (char*) b->rbuffer); + memmove(b->rbuffer, e + 2, b->rbuffer_size); + return bus_start_running(b); + } + + } else if (line_begins(line, l, "DATA")) { + + if (b->auth == _BUS_AUTH_INVALID) + r = bus_socket_auth_write(b, "ERROR\r\n"); + else { + if (b->auth == BUS_AUTH_ANONYMOUS) + r = verify_anonymous_token(b, line + 4, l - 4); + else + r = verify_external_token(b, line + 4, l - 4); + + if (r < 0) + return r; + if (r == 0) { + b->auth = _BUS_AUTH_INVALID; + r = bus_socket_auth_write(b, "REJECTED\r\n"); + } else + r = bus_socket_auth_write_ok(b); + } + } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) { + if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + r = bus_socket_auth_write(b, "ERROR\r\n"); + else { + b->can_fds = true; + r = bus_socket_auth_write(b, "AGREE_UNIX_FD\r\n"); + } + } else + r = bus_socket_auth_write(b, "ERROR\r\n"); + + if (r < 0) + return r; + + b->auth_rbegin = e + 2 - (char*) b->rbuffer; + + processed = true; + } +} + +static int bus_socket_auth_verify(sd_bus *b) { + assert(b); + + if (b->is_server) + return bus_socket_auth_verify_server(b); + else + return bus_socket_auth_verify_client(b); +} + +static int bus_socket_read_auth(sd_bus *b) { + struct msghdr mh; + struct iovec iov = {}; + size_t n; + ssize_t k; + int r; + void *p; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; + } control; + bool handle_cmsg = false; + + assert(b); + assert(b->state == BUS_AUTHENTICATING); + + r = bus_socket_auth_verify(b); + if (r != 0) + return r; + + n = MAX(256u, b->rbuffer_size * 2); + + if (n > BUS_AUTH_SIZE_MAX) + n = BUS_AUTH_SIZE_MAX; + + if (b->rbuffer_size >= n) + return -ENOBUFS; + + p = realloc(b->rbuffer, n); + if (!p) + return -ENOMEM; + + b->rbuffer = p; + + iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size; + iov.iov_len = n - b->rbuffer_size; + + if (b->prefer_readv) + k = readv(b->input_fd, &iov, 1); + else { + zero(mh); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = &control; + mh.msg_controllen = sizeof(control); + + k = recvmsg(b->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (k < 0 && errno == ENOTSOCK) { + b->prefer_readv = true; + k = readv(b->input_fd, &iov, 1); + } else + handle_cmsg = true; + } + if (k < 0) + return errno == EAGAIN ? 0 : -errno; + if (k == 0) + return -ECONNRESET; + + b->rbuffer_size += k; + + if (handle_cmsg) { + struct cmsghdr *cmsg; + + CMSG_FOREACH(cmsg, &mh) + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int j; + + /* Whut? We received fds during the auth + * protocol? Somebody is playing games with + * us. Close them all, and fail */ + j = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + close_many((int*) CMSG_DATA(cmsg), j); + return -EIO; + } else + log_debug("Got unexpected auxiliary data with level=%d and type=%d", + cmsg->cmsg_level, cmsg->cmsg_type); + } + + r = bus_socket_auth_verify(b); + if (r != 0) + return r; + + return 1; +} + +void bus_socket_setup(sd_bus *b) { + assert(b); + + /* Increase the buffers to 8 MB */ + fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); + fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); + + b->is_kernel = false; + b->message_version = 1; + b->message_endian = 0; +} + +static void bus_get_peercred(sd_bus *b) { + int r; + + assert(b); + + /* Get the peer for socketpair() sockets */ + b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; + + /* Get the SELinux context of the peer */ + 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) { + size_t l; + const char *auth_suffix, *auth_prefix; + + assert(b); + + if (b->anonymous_auth) { + auth_prefix = "\0AUTH ANONYMOUS "; + + /* For ANONYMOUS auth we send some arbitrary "trace" string */ + l = 9; + b->auth_buffer = hexmem("anonymous", l); + } else { + char text[DECIMAL_STR_MAX(uid_t) + 1]; + + auth_prefix = "\0AUTH EXTERNAL "; + + xsprintf(text, UID_FMT, geteuid()); + + l = strlen(text); + b->auth_buffer = hexmem(text, l); + } + + if (!b->auth_buffer) + return -ENOMEM; + + if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) + auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n"; + else + auth_suffix = "\r\nBEGIN\r\n"; + + b->auth_iovec[0].iov_base = (void*) auth_prefix; + b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1); + b->auth_iovec[1].iov_base = (void*) b->auth_buffer; + b->auth_iovec[1].iov_len = l * 2; + b->auth_iovec[2].iov_base = (void*) auth_suffix; + b->auth_iovec[2].iov_len = strlen(auth_suffix); + + return bus_socket_write_auth(b); +} + +int bus_socket_start_auth(sd_bus *b) { + assert(b); + + bus_get_peercred(b); + + b->state = BUS_AUTHENTICATING; + b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT; + + if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0) + b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + + if (b->output_fd != b->input_fd) + if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0) + b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + + if (b->is_server) + return bus_socket_read_auth(b); + else + return bus_socket_start_auth_client(b); +} + +int bus_socket_connect(sd_bus *b) { + int r; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->sockaddr.sa.sa_family != AF_UNSPEC); + + b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (b->input_fd < 0) + return -errno; + + b->output_fd = b->input_fd; + + bus_socket_setup(b); + + r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); + if (r < 0) { + if (errno == EINPROGRESS) + return 1; + + return -errno; + } + + return bus_socket_start_auth(b); +} + +int bus_socket_exec(sd_bus *b) { + int s[2], r; + pid_t pid; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->exec_path); + + r = socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, s); + if (r < 0) + return -errno; + + pid = fork(); + if (pid < 0) { + safe_close_pair(s); + return -errno; + } + if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + close_all_fds(s+1, 1); + + assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO); + assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO); + + if (s[1] != STDIN_FILENO && s[1] != STDOUT_FILENO) + safe_close(s[1]); + + fd_cloexec(STDIN_FILENO, false); + fd_cloexec(STDOUT_FILENO, false); + fd_nonblock(STDIN_FILENO, false); + fd_nonblock(STDOUT_FILENO, false); + + if (b->exec_argv) + execvp(b->exec_path, b->exec_argv); + else { + const char *argv[] = { b->exec_path, NULL }; + execvp(b->exec_path, (char**) argv); + } + + _exit(EXIT_FAILURE); + } + + safe_close(s[1]); + b->output_fd = b->input_fd = s[0]; + + bus_socket_setup(b); + + return bus_socket_start_auth(b); +} + +int bus_socket_take_fd(sd_bus *b) { + assert(b); + + bus_socket_setup(b); + + return bus_socket_start_auth(b); +} + +int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { + struct iovec *iov; + ssize_t k; + size_t n; + unsigned j; + int r; + + assert(bus); + assert(m); + assert(idx); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + if (*idx >= BUS_MESSAGE_SIZE(m)) + return 0; + + r = bus_message_setup_iovec(m); + if (r < 0) + return r; + + n = m->n_iovec * sizeof(struct iovec); + iov = alloca(n); + memcpy_safe(iov, m->iovec, n); + + j = 0; + iovec_advance(iov, &j, *idx); + + if (bus->prefer_writev) + k = writev(bus->output_fd, iov, m->n_iovec); + else { + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = m->n_iovec, + }; + + if (m->n_fds > 0) { + struct cmsghdr *control; + + mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds)); + mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds); + control->cmsg_level = SOL_SOCKET; + control->cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds); + } + + k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); + if (k < 0 && errno == ENOTSOCK) { + bus->prefer_writev = true; + k = writev(bus->output_fd, iov, m->n_iovec); + } + } + + if (k < 0) + return errno == EAGAIN ? 0 : -errno; + + *idx += (size_t) k; + return 1; +} + +static int bus_socket_read_message_need(sd_bus *bus, size_t *need) { + uint32_t a, b; + uint8_t e; + uint64_t sum; + + assert(bus); + assert(need); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + if (bus->rbuffer_size < sizeof(struct bus_header)) { + *need = sizeof(struct bus_header) + 8; + + /* Minimum message size: + * + * Header + + * + * Method Call: +2 string headers + * Signal: +3 string headers + * Method Error: +1 string headers + * +1 uint32 headers + * Method Reply: +1 uint32 headers + * + * A string header is at least 9 bytes + * A uint32 header is at least 8 bytes + * + * Hence the minimum message size of a valid message + * is header + 8 bytes */ + + return 0; + } + + a = ((const uint32_t*) bus->rbuffer)[1]; + b = ((const uint32_t*) bus->rbuffer)[3]; + + e = ((const uint8_t*) bus->rbuffer)[0]; + if (e == BUS_LITTLE_ENDIAN) { + a = le32toh(a); + b = le32toh(b); + } else if (e == BUS_BIG_ENDIAN) { + a = be32toh(a); + b = be32toh(b); + } else + return -EBADMSG; + + sum = (uint64_t) sizeof(struct bus_header) + (uint64_t) ALIGN_TO(b, 8) + (uint64_t) a; + if (sum >= BUS_MESSAGE_SIZE_MAX) + return -ENOBUFS; + + *need = (size_t) sum; + return 0; +} + +static int bus_socket_make_message(sd_bus *bus, size_t size) { + sd_bus_message *t; + void *b; + int r; + + assert(bus); + assert(bus->rbuffer_size >= size); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + if (bus->rbuffer_size > size) { + b = memdup((const uint8_t*) bus->rbuffer + size, + bus->rbuffer_size - size); + if (!b) + return -ENOMEM; + } else + b = NULL; + + r = bus_message_from_malloc(bus, + bus->rbuffer, size, + bus->fds, bus->n_fds, + NULL, + &t); + if (r < 0) { + free(b); + return r; + } + + bus->rbuffer = b; + bus->rbuffer_size -= size; + + bus->fds = NULL; + bus->n_fds = 0; + + bus->rqueue[bus->rqueue_size++] = t; + + return 1; +} + +int bus_socket_read_message(sd_bus *bus) { + struct msghdr mh; + struct iovec iov = {}; + ssize_t k; + size_t need; + int r; + void *b; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)]; + } control; + bool handle_cmsg = false; + + assert(bus); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + r = bus_socket_read_message_need(bus, &need); + if (r < 0) + return r; + + if (bus->rbuffer_size >= need) + return bus_socket_make_message(bus, need); + + b = realloc(bus->rbuffer, need); + if (!b) + return -ENOMEM; + + bus->rbuffer = b; + + iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size; + iov.iov_len = need - bus->rbuffer_size; + + if (bus->prefer_readv) + k = readv(bus->input_fd, &iov, 1); + else { + zero(mh); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = &control; + mh.msg_controllen = sizeof(control); + + k = recvmsg(bus->input_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (k < 0 && errno == ENOTSOCK) { + bus->prefer_readv = true; + k = readv(bus->input_fd, &iov, 1); + } else + handle_cmsg = true; + } + if (k < 0) + return errno == EAGAIN ? 0 : -errno; + if (k == 0) + return -ECONNRESET; + + bus->rbuffer_size += k; + + if (handle_cmsg) { + struct cmsghdr *cmsg; + + CMSG_FOREACH(cmsg, &mh) + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int n, *f; + + n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if (!bus->can_fds) { + /* Whut? We received fds but this + * isn't actually enabled? Close them, + * and fail */ + + close_many((int*) CMSG_DATA(cmsg), n); + return -EIO; + } + + f = realloc(bus->fds, sizeof(int) * (bus->n_fds + n)); + if (!f) { + close_many((int*) CMSG_DATA(cmsg), n); + return -ENOMEM; + } + + memcpy_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); + bus->fds = f; + bus->n_fds += n; + } else + log_debug("Got unexpected auxiliary data with level=%d and type=%d", + cmsg->cmsg_level, cmsg->cmsg_type); + } + + r = bus_socket_read_message_need(bus, &need); + if (r < 0) + return r; + + if (bus->rbuffer_size >= need) + return bus_socket_make_message(bus, need); + + return 1; +} + +int bus_socket_process_opening(sd_bus *b) { + int error = 0; + socklen_t slen = sizeof(error); + struct pollfd p = { + .fd = b->output_fd, + .events = POLLOUT, + }; + int r; + + assert(b->state == BUS_OPENING); + + r = poll(&p, 1, 0); + if (r < 0) + return -errno; + + if (!(p.revents & (POLLOUT|POLLERR|POLLHUP))) + return 0; + + r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen); + if (r < 0) + b->last_connect_error = errno; + else if (error != 0) + b->last_connect_error = error; + else if (p.revents & (POLLERR|POLLHUP)) + b->last_connect_error = ECONNREFUSED; + else + return bus_socket_start_auth(b); + + return bus_next_address(b); +} + +int bus_socket_process_authenticating(sd_bus *b) { + int r; + + assert(b); + assert(b->state == BUS_AUTHENTICATING); + + if (now(CLOCK_MONOTONIC) >= b->auth_timeout) + return -ETIMEDOUT; + + r = bus_socket_write_auth(b); + if (r != 0) + return r; + + return bus_socket_read_auth(b); +} diff --git a/src/libsystemd/src/sd-bus/bus-socket.h b/src/libsystemd/src/sd-bus/bus-socket.h new file mode 100644 index 0000000000..6e1d32e6a7 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-socket.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +void bus_socket_setup(sd_bus *b); + +int bus_socket_connect(sd_bus *b); +int bus_socket_exec(sd_bus *b); +int bus_socket_take_fd(sd_bus *b); +int bus_socket_start_auth(sd_bus *b); + +int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx); +int bus_socket_read_message(sd_bus *bus); + +int bus_socket_process_opening(sd_bus *b); +int bus_socket_process_authenticating(sd_bus *b); + +bool bus_socket_auth_needs_write(sd_bus *b); diff --git a/src/libsystemd/src/sd-bus/bus-track.c b/src/libsystemd/src/sd-bus/bus-track.c new file mode 100644 index 0000000000..24c3a7050d --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-track.c @@ -0,0 +1,338 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-track.h" + +struct sd_bus_track { + unsigned n_ref; + sd_bus *bus; + sd_bus_track_handler_t handler; + void *userdata; + Hashmap *names; + LIST_FIELDS(sd_bus_track, queue); + Iterator iterator; + bool in_queue; + bool modified; +}; + +#define MATCH_PREFIX \ + "type='signal'," \ + "sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus'," \ + "interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged'," \ + "arg0='" + +#define MATCH_SUFFIX \ + "'" + +#define MATCH_FOR_NAME(name) \ + ({ \ + char *_x; \ + size_t _l = strlen(name); \ + _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \ + strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ + _x; \ + }) + +static void bus_track_add_to_queue(sd_bus_track *track) { + assert(track); + + if (track->in_queue) + return; + + if (!track->handler) + return; + + LIST_PREPEND(queue, track->bus->track_queue, track); + track->in_queue = true; +} + +static void bus_track_remove_from_queue(sd_bus_track *track) { + assert(track); + + if (!track->in_queue) + return; + + LIST_REMOVE(queue, track->bus->track_queue, track); + track->in_queue = false; +} + +_public_ int sd_bus_track_new( + sd_bus *bus, + sd_bus_track **track, + sd_bus_track_handler_t handler, + void *userdata) { + + sd_bus_track *t; + + assert_return(bus, -EINVAL); + assert_return(track, -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + t = new0(sd_bus_track, 1); + if (!t) + return -ENOMEM; + + t->n_ref = 1; + t->handler = handler; + t->userdata = userdata; + t->bus = sd_bus_ref(bus); + + bus_track_add_to_queue(t); + + *track = t; + return 0; +} + +_public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) { + + if (!track) + return NULL; + + assert(track->n_ref > 0); + + track->n_ref++; + + return track; +} + +_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { + const char *n; + + if (!track) + return NULL; + + assert(track->n_ref > 0); + + if (track->n_ref > 1) { + track->n_ref--; + return NULL; + } + + while ((n = hashmap_first_key(track->names))) + sd_bus_track_remove_name(track, n); + + bus_track_remove_from_queue(track); + hashmap_free(track->names); + sd_bus_unref(track->bus); + free(track); + + return NULL; +} + +static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { + sd_bus_track *track = userdata; + const char *name, *old, *new; + int r; + + assert(message); + assert(track); + + r = sd_bus_message_read(message, "sss", &name, &old, &new); + if (r < 0) + return 0; + + sd_bus_track_remove_name(track, name); + return 0; +} + +_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; + _cleanup_free_ char *n = NULL; + const char *match; + int r; + + assert_return(track, -EINVAL); + assert_return(service_name_is_valid(name), -EINVAL); + + r = hashmap_ensure_allocated(&track->names, &string_hash_ops); + if (r < 0) + return r; + + n = strdup(name); + if (!n) + return -ENOMEM; + + /* First, subscribe to this name */ + match = MATCH_FOR_NAME(n); + r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track); + if (r < 0) + return r; + + r = hashmap_put(track->names, n, slot); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; + + /* Second, check if it is currently existing, or maybe + * doesn't, or maybe disappeared already. */ + r = sd_bus_get_name_creds(track->bus, n, 0, NULL); + if (r < 0) { + hashmap_remove(track->names, n); + return r; + } + + n = NULL; + slot = NULL; + + bus_track_remove_from_queue(track); + track->modified = true; + + return 1; +} + +_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) { + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; + _cleanup_free_ char *n = NULL; + + assert_return(name, -EINVAL); + + if (!track) + return 0; + + slot = hashmap_remove2(track->names, (char*) name, (void**) &n); + if (!slot) + return 0; + + if (hashmap_isempty(track->names)) + bus_track_add_to_queue(track); + + track->modified = true; + + return 1; +} + +_public_ unsigned sd_bus_track_count(sd_bus_track *track) { + if (!track) + return 0; + + return hashmap_size(track->names); +} + +_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) { + assert_return(track, NULL); + assert_return(name, NULL); + + return hashmap_get(track->names, (void*) name) ? name : NULL; +} + +_public_ const char* sd_bus_track_first(sd_bus_track *track) { + const char *n = NULL; + + if (!track) + return NULL; + + track->modified = false; + track->iterator = ITERATOR_FIRST; + + hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); + return n; +} + +_public_ const char* sd_bus_track_next(sd_bus_track *track) { + const char *n = NULL; + + if (!track) + return NULL; + + if (track->modified) + return NULL; + + hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n); + return n; +} + +_public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) { + const char *sender; + + assert_return(track, -EINVAL); + assert_return(m, -EINVAL); + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EINVAL; + + return sd_bus_track_add_name(track, sender); +} + +_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) { + const char *sender; + + assert_return(track, -EINVAL); + assert_return(m, -EINVAL); + + sender = sd_bus_message_get_sender(m); + if (!sender) + return -EINVAL; + + return sd_bus_track_remove_name(track, sender); +} + +_public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) { + assert_return(track, NULL); + + return track->bus; +} + +void bus_track_dispatch(sd_bus_track *track) { + int r; + + assert(track); + assert(track->in_queue); + assert(track->handler); + + bus_track_remove_from_queue(track); + + sd_bus_track_ref(track); + + r = track->handler(track, track->userdata); + if (r < 0) + log_debug_errno(r, "Failed to process track handler: %m"); + else if (r == 0) + bus_track_add_to_queue(track); + + sd_bus_track_unref(track); +} + +_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) { + assert_return(track, NULL); + + return track->userdata; +} + +_public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) { + void *ret; + + assert_return(track, NULL); + + ret = track->userdata; + track->userdata = userdata; + + return ret; +} diff --git a/src/libsystemd/src/sd-bus/bus-track.h b/src/libsystemd/src/sd-bus/bus-track.h new file mode 100644 index 0000000000..7d93a727d6 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-track.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +void bus_track_dispatch(sd_bus_track *track); diff --git a/src/libsystemd/src/sd-bus/bus-type.c b/src/libsystemd/src/sd-bus/bus-type.c new file mode 100644 index 0000000000..c692afc580 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-type.c @@ -0,0 +1,176 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "bus-type.h" + +bool bus_type_is_valid(char c) { + static const char valid[] = { + SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BOOLEAN, + SD_BUS_TYPE_INT16, + SD_BUS_TYPE_UINT16, + SD_BUS_TYPE_INT32, + SD_BUS_TYPE_UINT32, + SD_BUS_TYPE_INT64, + SD_BUS_TYPE_UINT64, + SD_BUS_TYPE_DOUBLE, + SD_BUS_TYPE_STRING, + SD_BUS_TYPE_OBJECT_PATH, + SD_BUS_TYPE_SIGNATURE, + SD_BUS_TYPE_ARRAY, + SD_BUS_TYPE_VARIANT, + SD_BUS_TYPE_STRUCT, + SD_BUS_TYPE_DICT_ENTRY, + SD_BUS_TYPE_UNIX_FD + }; + + return !!memchr(valid, c, sizeof(valid)); +} + +bool bus_type_is_valid_in_signature(char c) { + static const char valid[] = { + SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BOOLEAN, + SD_BUS_TYPE_INT16, + SD_BUS_TYPE_UINT16, + SD_BUS_TYPE_INT32, + SD_BUS_TYPE_UINT32, + SD_BUS_TYPE_INT64, + SD_BUS_TYPE_UINT64, + SD_BUS_TYPE_DOUBLE, + SD_BUS_TYPE_STRING, + SD_BUS_TYPE_OBJECT_PATH, + SD_BUS_TYPE_SIGNATURE, + SD_BUS_TYPE_ARRAY, + SD_BUS_TYPE_VARIANT, + SD_BUS_TYPE_STRUCT_BEGIN, + SD_BUS_TYPE_STRUCT_END, + SD_BUS_TYPE_DICT_ENTRY_BEGIN, + SD_BUS_TYPE_DICT_ENTRY_END, + SD_BUS_TYPE_UNIX_FD + }; + + return !!memchr(valid, c, sizeof(valid)); +} + +bool bus_type_is_basic(char c) { + static const char valid[] = { + SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BOOLEAN, + SD_BUS_TYPE_INT16, + SD_BUS_TYPE_UINT16, + SD_BUS_TYPE_INT32, + SD_BUS_TYPE_UINT32, + SD_BUS_TYPE_INT64, + SD_BUS_TYPE_UINT64, + SD_BUS_TYPE_DOUBLE, + SD_BUS_TYPE_STRING, + SD_BUS_TYPE_OBJECT_PATH, + SD_BUS_TYPE_SIGNATURE, + SD_BUS_TYPE_UNIX_FD + }; + + return !!memchr(valid, c, sizeof(valid)); +} + +bool bus_type_is_trivial(char c) { + static const char valid[] = { + SD_BUS_TYPE_BYTE, + SD_BUS_TYPE_BOOLEAN, + SD_BUS_TYPE_INT16, + SD_BUS_TYPE_UINT16, + SD_BUS_TYPE_INT32, + SD_BUS_TYPE_UINT32, + SD_BUS_TYPE_INT64, + SD_BUS_TYPE_UINT64, + SD_BUS_TYPE_DOUBLE + }; + + return !!memchr(valid, c, sizeof(valid)); +} + +bool bus_type_is_container(char c) { + static const char valid[] = { + SD_BUS_TYPE_ARRAY, + SD_BUS_TYPE_VARIANT, + SD_BUS_TYPE_STRUCT, + SD_BUS_TYPE_DICT_ENTRY + }; + + return !!memchr(valid, c, sizeof(valid)); +} + +int bus_type_get_alignment(char c) { + + switch (c) { + case SD_BUS_TYPE_BYTE: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_VARIANT: + return 1; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + return 2; + + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_ARRAY: + case SD_BUS_TYPE_UNIX_FD: + return 4; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + case SD_BUS_TYPE_STRUCT: + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: + return 8; + } + + return -EINVAL; +} + +int bus_type_get_size(char c) { + + switch (c) { + case SD_BUS_TYPE_BYTE: + return 1; + + case SD_BUS_TYPE_INT16: + case SD_BUS_TYPE_UINT16: + return 2; + + case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_INT32: + case SD_BUS_TYPE_UINT32: + case SD_BUS_TYPE_UNIX_FD: + return 4; + + case SD_BUS_TYPE_INT64: + case SD_BUS_TYPE_UINT64: + case SD_BUS_TYPE_DOUBLE: + return 8; + } + + return -EINVAL; +} diff --git a/src/libsystemd/src/sd-bus/bus-type.h b/src/libsystemd/src/sd-bus/bus-type.h new file mode 100644 index 0000000000..d81aa20df3 --- /dev/null +++ b/src/libsystemd/src/sd-bus/bus-type.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/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_; +/* "trivial" is systemd's term for what the D-Bus Specification calls + * a "fixed type": that is, a basic type of fixed length */ +bool bus_type_is_trivial(char c) _const_; +bool bus_type_is_container(char c) _const_; + +int bus_type_get_alignment(char c) _const_; +int bus_type_get_size(char c) _const_; diff --git a/src/libsystemd/src/sd-bus/kdbus.h b/src/libsystemd/src/sd-bus/kdbus.h new file mode 100644 index 0000000000..ecffc6b13c --- /dev/null +++ b/src/libsystemd/src/sd-bus/kdbus.h @@ -0,0 +1,980 @@ +/* + * kdbus is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + */ + +#ifndef _UAPI_KDBUS_H_ +#define _UAPI_KDBUS_H_ + +#include +#include + +#define KDBUS_IOCTL_MAGIC 0x95 +#define KDBUS_SRC_ID_KERNEL (0) +#define KDBUS_DST_ID_NAME (0) +#define KDBUS_MATCH_ID_ANY (~0ULL) +#define KDBUS_DST_ID_BROADCAST (~0ULL) +#define KDBUS_FLAG_NEGOTIATE (1ULL << 63) + +/** + * struct kdbus_notify_id_change - name registry change message + * @id: New or former owner of the name + * @flags: flags field from KDBUS_HELLO_* + * + * Sent from kernel to userspace when the owner or activator of + * a well-known name changes. + * + * Attached to: + * KDBUS_ITEM_ID_ADD + * KDBUS_ITEM_ID_REMOVE + */ +struct kdbus_notify_id_change { + __u64 id; + __u64 flags; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_notify_name_change - name registry change message + * @old_id: ID and flags of former owner of a name + * @new_id: ID and flags of new owner of a name + * @name: Well-known name + * + * Sent from kernel to userspace when the owner or activator of + * a well-known name changes. + * + * Attached to: + * KDBUS_ITEM_NAME_ADD + * KDBUS_ITEM_NAME_REMOVE + * KDBUS_ITEM_NAME_CHANGE + */ +struct kdbus_notify_name_change { + struct kdbus_notify_id_change old_id; + struct kdbus_notify_id_change new_id; + char name[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_creds - process credentials + * @uid: User ID + * @euid: Effective UID + * @suid: Saved UID + * @fsuid: Filesystem UID + * @gid: Group ID + * @egid: Effective GID + * @sgid: Saved GID + * @fsgid: Filesystem GID + * + * Attached to: + * KDBUS_ITEM_CREDS + */ +struct kdbus_creds { + __u64 uid; + __u64 euid; + __u64 suid; + __u64 fsuid; + __u64 gid; + __u64 egid; + __u64 sgid; + __u64 fsgid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_pids - process identifiers + * @pid: Process ID + * @tid: Thread ID + * @ppid: Parent process ID + * + * The PID and TID of a process. + * + * Attached to: + * KDBUS_ITEM_PIDS + */ +struct kdbus_pids { + __u64 pid; + __u64 tid; + __u64 ppid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_caps - process capabilities + * @last_cap: Highest currently known capability bit + * @caps: Variable number of 32-bit capabilities flags + * + * Contains a variable number of 32-bit capabilities flags. + * + * Attached to: + * KDBUS_ITEM_CAPS + */ +struct kdbus_caps { + __u32 last_cap; + __u32 caps[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_audit - audit information + * @sessionid: The audit session ID + * @loginuid: The audit login uid + * + * Attached to: + * KDBUS_ITEM_AUDIT + */ +struct kdbus_audit { + __u32 sessionid; + __u32 loginuid; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_timestamp + * @seqnum: Global per-domain message sequence number + * @monotonic_ns: Monotonic timestamp, in nanoseconds + * @realtime_ns: Realtime timestamp, in nanoseconds + * + * Attached to: + * KDBUS_ITEM_TIMESTAMP + */ +struct kdbus_timestamp { + __u64 seqnum; + __u64 monotonic_ns; + __u64 realtime_ns; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_vec - I/O vector for kdbus payload items + * @size: The size of the vector + * @address: Memory address of data buffer + * @offset: Offset in the in-message payload memory, + * relative to the message head + * + * Attached to: + * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF + */ +struct kdbus_vec { + __u64 size; + union { + __u64 address; + __u64 offset; + }; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_bloom_parameter - bus-wide bloom parameters + * @size: Size of the bit field in bytes (m / 8) + * @n_hash: Number of hash functions used (k) + */ +struct kdbus_bloom_parameter { + __u64 size; + __u64 n_hash; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_bloom_filter - bloom filter containing n elements + * @generation: Generation of the element set in the filter + * @data: Bit field, multiple of 8 bytes + */ +struct kdbus_bloom_filter { + __u64 generation; + __u64 data[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_memfd - a kdbus memfd + * @start: The offset into the memfd where the segment starts + * @size: The size of the memfd segment + * @fd: The file descriptor number + * @__pad: Padding to ensure proper alignment and size + * + * Attached to: + * KDBUS_ITEM_PAYLOAD_MEMFD + */ +struct kdbus_memfd { + __u64 start; + __u64 size; + int fd; + __u32 __pad; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_name - a registered well-known name with its flags + * @flags: Flags from KDBUS_NAME_* + * @name: Well-known name + * + * Attached to: + * KDBUS_ITEM_OWNED_NAME + */ +struct kdbus_name { + __u64 flags; + char name[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_policy_access_type - permissions of a policy record + * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid + * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid + * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid + * @KDBUS_POLICY_ACCESS_WORLD: World-accessible + */ +enum kdbus_policy_access_type { + _KDBUS_POLICY_ACCESS_NULL, + KDBUS_POLICY_ACCESS_USER, + KDBUS_POLICY_ACCESS_GROUP, + KDBUS_POLICY_ACCESS_WORLD, +}; + +/** + * enum kdbus_policy_access_flags - mode flags + * @KDBUS_POLICY_OWN: Allow to own a well-known name + * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE + * @KDBUS_POLICY_TALK: Allow communication to a well-known name + * Implies KDBUS_POLICY_SEE + * @KDBUS_POLICY_SEE: Allow to see a well-known name + */ +enum kdbus_policy_type { + KDBUS_POLICY_SEE = 0, + KDBUS_POLICY_TALK, + KDBUS_POLICY_OWN, +}; + +/** + * struct kdbus_policy_access - policy access item + * @type: One of KDBUS_POLICY_ACCESS_* types + * @access: Access to grant + * @id: For KDBUS_POLICY_ACCESS_USER, the uid + * For KDBUS_POLICY_ACCESS_GROUP, the gid + */ +struct kdbus_policy_access { + __u64 type; /* USER, GROUP, WORLD */ + __u64 access; /* OWN, TALK, SEE */ + __u64 id; /* uid, gid, 0 */ +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_attach_flags - flags for metadata attachments + * @KDBUS_ATTACH_TIMESTAMP: Timestamp + * @KDBUS_ATTACH_CREDS: Credentials + * @KDBUS_ATTACH_PIDS: PIDs + * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups + * @KDBUS_ATTACH_NAMES: Well-known names + * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID + * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID + * @KDBUS_ATTACH_EXE: The path of the executable + * @KDBUS_ATTACH_CMDLINE: The process command line + * @KDBUS_ATTACH_CGROUP: The croup membership + * @KDBUS_ATTACH_CAPS: The process capabilities + * @KDBUS_ATTACH_SECLABEL: The security label + * @KDBUS_ATTACH_AUDIT: The audit IDs + * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name + * @_KDBUS_ATTACH_ALL: All of the above + * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of + * metatdata. + */ +enum kdbus_attach_flags { + KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, + KDBUS_ATTACH_CREDS = 1ULL << 1, + KDBUS_ATTACH_PIDS = 1ULL << 2, + KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, + KDBUS_ATTACH_NAMES = 1ULL << 4, + KDBUS_ATTACH_TID_COMM = 1ULL << 5, + KDBUS_ATTACH_PID_COMM = 1ULL << 6, + KDBUS_ATTACH_EXE = 1ULL << 7, + KDBUS_ATTACH_CMDLINE = 1ULL << 8, + KDBUS_ATTACH_CGROUP = 1ULL << 9, + KDBUS_ATTACH_CAPS = 1ULL << 10, + KDBUS_ATTACH_SECLABEL = 1ULL << 11, + KDBUS_ATTACH_AUDIT = 1ULL << 12, + KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, + _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, + _KDBUS_ATTACH_ANY = ~0ULL +}; + +/** + * enum kdbus_item_type - item types to chain data in a list + * @_KDBUS_ITEM_NULL: Uninitialized/invalid + * @_KDBUS_ITEM_USER_BASE: Start of user items + * @KDBUS_ITEM_NEGOTIATE: Negotiate supported items + * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data + * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head + * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd + * @KDBUS_ITEM_FDS: Attached file descriptors + * @KDBUS_ITEM_CANCEL_FD: FD used to cancel a synchronous + * operation by writing to it from + * userspace + * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with + * KDBUS_CMD_BUS_MAKE, carries a + * struct kdbus_bloom_parameter + * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, + * used to match against a bloom mask of a + * connection, carries a struct + * kdbus_bloom_filter + * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a + * message'sbloom filter + * @KDBUS_ITEM_DST_NAME: Destination's well-known name + * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint + * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which + * metadata a connection opts in to send + * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which + * metadata a connection requests to + * receive for each reeceived message + * @KDBUS_ITEM_ID: Connection ID + * @KDBUS_ITEM_NAME: Well-know name with flags + * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items + * @KDBUS_ITEM_TIMESTAMP: Timestamp + * @KDBUS_ITEM_CREDS: Process credentials + * @KDBUS_ITEM_PIDS: Process identifiers + * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups + * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated + * connection + * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_EXE: The path of the executable + * (Don't trust this, see below.) + * @KDBUS_ITEM_CMDLINE: The process command line + * (Don't trust this, see below.) + * @KDBUS_ITEM_CGROUP: The croup membership + * @KDBUS_ITEM_CAPS: The process capabilities + * @KDBUS_ITEM_SECLABEL: The security label + * @KDBUS_ITEM_AUDIT: The audit IDs + * @KDBUS_ITEM_CONN_DESCRIPTION: The connection's human-readable name + * (debugging) + * @_KDBUS_ITEM_POLICY_BASE: Start of policy items + * @KDBUS_ITEM_POLICY_ACCESS: Policy access block + * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items + * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached + * @KDBUS_ITEM_REPLY_DEAD: Destination died + * + * N.B: The process and thread COMM fields, as well as the CMDLINE and + * EXE fields may be altered by unprivileged processes und should + * hence *not* used for security decisions. Peers should make use of + * these items only for informational purposes, such as generating log + * records. + */ +enum kdbus_item_type { + _KDBUS_ITEM_NULL, + _KDBUS_ITEM_USER_BASE, + KDBUS_ITEM_NEGOTIATE = _KDBUS_ITEM_USER_BASE, + KDBUS_ITEM_PAYLOAD_VEC, + KDBUS_ITEM_PAYLOAD_OFF, + KDBUS_ITEM_PAYLOAD_MEMFD, + KDBUS_ITEM_FDS, + KDBUS_ITEM_CANCEL_FD, + KDBUS_ITEM_BLOOM_PARAMETER, + KDBUS_ITEM_BLOOM_FILTER, + KDBUS_ITEM_BLOOM_MASK, + KDBUS_ITEM_DST_NAME, + KDBUS_ITEM_MAKE_NAME, + KDBUS_ITEM_ATTACH_FLAGS_SEND, + KDBUS_ITEM_ATTACH_FLAGS_RECV, + KDBUS_ITEM_ID, + KDBUS_ITEM_NAME, + KDBUS_ITEM_DST_ID, + + /* keep these item types in sync with KDBUS_ATTACH_* flags */ + _KDBUS_ITEM_ATTACH_BASE = 0x1000, + KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, + KDBUS_ITEM_CREDS, + KDBUS_ITEM_PIDS, + KDBUS_ITEM_AUXGROUPS, + KDBUS_ITEM_OWNED_NAME, + KDBUS_ITEM_TID_COMM, + KDBUS_ITEM_PID_COMM, + KDBUS_ITEM_EXE, + KDBUS_ITEM_CMDLINE, + KDBUS_ITEM_CGROUP, + KDBUS_ITEM_CAPS, + KDBUS_ITEM_SECLABEL, + KDBUS_ITEM_AUDIT, + KDBUS_ITEM_CONN_DESCRIPTION, + + _KDBUS_ITEM_POLICY_BASE = 0x2000, + KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, + + _KDBUS_ITEM_KERNEL_BASE = 0x8000, + KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE, + KDBUS_ITEM_NAME_REMOVE, + KDBUS_ITEM_NAME_CHANGE, + KDBUS_ITEM_ID_ADD, + KDBUS_ITEM_ID_REMOVE, + KDBUS_ITEM_REPLY_TIMEOUT, + KDBUS_ITEM_REPLY_DEAD, +}; + +/** + * struct kdbus_item - chain of data blocks + * @size: Overall data record size + * @type: Kdbus_item type of data + * @data: Generic bytes + * @data32: Generic 32 bit array + * @data64: Generic 64 bit array + * @str: Generic string + * @id: Connection ID + * @vec: KDBUS_ITEM_PAYLOAD_VEC + * @creds: KDBUS_ITEM_CREDS + * @audit: KDBUS_ITEM_AUDIT + * @timestamp: KDBUS_ITEM_TIMESTAMP + * @name: KDBUS_ITEM_NAME + * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER + * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER + * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD + * @name_change: KDBUS_ITEM_NAME_ADD + * KDBUS_ITEM_NAME_REMOVE + * KDBUS_ITEM_NAME_CHANGE + * @id_change: KDBUS_ITEM_ID_ADD + * KDBUS_ITEM_ID_REMOVE + * @policy: KDBUS_ITEM_POLICY_ACCESS + */ +struct kdbus_item { + __u64 size; + __u64 type; + union { + __u8 data[0]; + __u32 data32[0]; + __u64 data64[0]; + char str[0]; + + __u64 id; + struct kdbus_vec vec; + struct kdbus_creds creds; + struct kdbus_pids pids; + struct kdbus_audit audit; + struct kdbus_caps caps; + struct kdbus_timestamp timestamp; + struct kdbus_name name; + struct kdbus_bloom_parameter bloom_parameter; + struct kdbus_bloom_filter bloom_filter; + struct kdbus_memfd memfd; + int fds[0]; + struct kdbus_notify_name_change name_change; + struct kdbus_notify_id_change id_change; + struct kdbus_policy_access policy_access; + }; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_msg_flags - type of message + * @KDBUS_MSG_EXPECT_REPLY: Expect a reply message, used for + * method calls. The userspace-supplied + * cookie identifies the message and the + * respective reply carries the cookie + * in cookie_reply + * @KDBUS_MSG_NO_AUTO_START: Do not start a service if the addressed + * name is not currently active. This flag is + * not looked at by the kernel but only + * serves as hint for userspace implementations. + * @KDBUS_MSG_SIGNAL: Treat this message as signal + */ +enum kdbus_msg_flags { + KDBUS_MSG_EXPECT_REPLY = 1ULL << 0, + KDBUS_MSG_NO_AUTO_START = 1ULL << 1, + KDBUS_MSG_SIGNAL = 1ULL << 2, +}; + +/** + * enum kdbus_payload_type - type of payload carried by message + * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message + * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus" + * + * Any payload-type is accepted. Common types will get added here once + * established. + */ +enum kdbus_payload_type { + KDBUS_PAYLOAD_KERNEL, + KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL, +}; + +/** + * struct kdbus_msg - the representation of a kdbus message + * @size: Total size of the message + * @flags: Message flags (KDBUS_MSG_*), userspace → kernel + * @priority: Message queue priority value + * @dst_id: 64-bit ID of the destination connection + * @src_id: 64-bit ID of the source connection + * @payload_type: Payload type (KDBUS_PAYLOAD_*) + * @cookie: Userspace-supplied cookie, for the connection + * to identify its messages + * @timeout_ns: The time to wait for a message reply from the peer. + * If there is no reply, and the send command is + * executed asynchronously, a kernel-generated message + * with an attached KDBUS_ITEM_REPLY_TIMEOUT item + * is sent to @src_id. For synchronously executed send + * command, the value denotes the maximum time the call + * blocks to wait for a reply. The timeout is expected in + * nanoseconds and as absolute CLOCK_MONOTONIC value. + * @cookie_reply: A reply to the requesting message with the same + * cookie. The requesting connection can match its + * request and the reply with this value + * @items: A list of kdbus_items containing the message payload + */ +struct kdbus_msg { + __u64 size; + __u64 flags; + __s64 priority; + __u64 dst_id; + __u64 src_id; + __u64 payload_type; + __u64 cookie; + union { + __u64 timeout_ns; + __u64 cookie_reply; + }; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_msg_info - returned message container + * @offset: Offset of kdbus_msg slice in pool + * @msg_size: Copy of the kdbus_msg.size field + * @return_flags: Command return flags, kernel → userspace + */ +struct kdbus_msg_info { + __u64 offset; + __u64 msg_size; + __u64 return_flags; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_send_flags - flags for sending messages + * @KDBUS_SEND_SYNC_REPLY: Wait for destination connection to + * reply to this message. The + * KDBUS_CMD_SEND ioctl() will block + * until the reply is received, and + * reply in struct kdbus_cmd_send will + * yield the offset in the sender's pool + * where the reply can be found. + * This flag is only valid if + * @KDBUS_MSG_EXPECT_REPLY is set as well. + */ +enum kdbus_send_flags { + KDBUS_SEND_SYNC_REPLY = 1ULL << 0, +}; + +/** + * struct kdbus_cmd_send - send message + * @size: Overall size of this structure + * @flags: Flags to change send behavior (KDBUS_SEND_*) + * @return_flags: Command return flags, kernel → userspace + * @msg_address: Storage address of the kdbus_msg to send + * @reply: Storage for message reply if KDBUS_SEND_SYNC_REPLY + * was given + * @items: Additional items for this command + */ +struct kdbus_cmd_send { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 msg_address; + struct kdbus_msg_info reply; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_recv_flags - flags for de-queuing messages + * @KDBUS_RECV_PEEK: Return the next queued message without + * actually de-queuing it, and without installing + * any file descriptors or other resources. It is + * usually used to determine the activating + * connection of a bus name. + * @KDBUS_RECV_DROP: Drop and free the next queued message and all + * its resources without actually receiving it. + * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or + * higher priority (lowest values); if not set, + * the priority value is ignored. + */ +enum kdbus_recv_flags { + KDBUS_RECV_PEEK = 1ULL << 0, + KDBUS_RECV_DROP = 1ULL << 1, + KDBUS_RECV_USE_PRIORITY = 1ULL << 2, +}; + +/** + * enum kdbus_recv_return_flags - return flags for message receive commands + * @KDBUS_RECV_RETURN_INCOMPLETE_FDS: One or more file descriptors could not + * be installed. These descriptors in + * KDBUS_ITEM_FDS will carry the value -1. + * @KDBUS_RECV_RETURN_DROPPED_MSGS: There have been dropped messages since + * the last time a message was received. + * The 'dropped_msgs' counter contains the + * number of messages dropped pool + * overflows or other missed broadcasts. + */ +enum kdbus_recv_return_flags { + KDBUS_RECV_RETURN_INCOMPLETE_FDS = 1ULL << 0, + KDBUS_RECV_RETURN_DROPPED_MSGS = 1ULL << 1, +}; + +/** + * struct kdbus_cmd_recv - struct to de-queue a buffered message + * @size: Overall size of this object + * @flags: KDBUS_RECV_* flags, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @priority: Minimum priority of the messages to de-queue. Lowest + * values have the highest priority. + * @dropped_msgs: In case there were any dropped messages since the last + * time a message was received, this will be set to the + * number of lost messages and + * KDBUS_RECV_RETURN_DROPPED_MSGS will be set in + * 'return_flags'. This can only happen if the ioctl + * returns 0 or EAGAIN. + * @msg: Return storage for received message. + * @items: Additional items for this command. + * + * This struct is used with the KDBUS_CMD_RECV ioctl. + */ +struct kdbus_cmd_recv { + __u64 size; + __u64 flags; + __u64 return_flags; + __s64 priority; + __u64 dropped_msgs; + struct kdbus_msg_info msg; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_cmd_free - struct to free a slice of memory in the pool + * @size: Overall size of this structure + * @flags: Flags for the free command, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @offset: The offset of the memory slice, as returned by other + * ioctls + * @items: Additional items to modify the behavior + * + * This struct is used with the KDBUS_CMD_FREE ioctl. + */ +struct kdbus_cmd_free { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 offset; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello + * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of + * any passed file descriptors + * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers + * a well-know name for a process to be started + * when traffic arrives + * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers + * policy entries for a name. The provided name + * is not activated and not registered with the + * name database, it only allows unprivileged + * connections to acquire a name, talk or discover + * a service + * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor + * bus traffic + */ +enum kdbus_hello_flags { + KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, + KDBUS_HELLO_ACTIVATOR = 1ULL << 1, + KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, + KDBUS_HELLO_MONITOR = 1ULL << 3, +}; + +/** + * struct kdbus_cmd_hello - struct to say hello to kdbus + * @size: The total size of the structure + * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @attach_flags_send: Mask of metadata to attach to each message sent + * off by this connection (KDBUS_ATTACH_*) + * @attach_flags_recv: Mask of metadata to attach to each message receieved + * by the new connection (KDBUS_ATTACH_*) + * @bus_flags: The flags field copied verbatim from the original + * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful + * to do negotiation of features of the payload that is + * transferred (kernel → userspace) + * @id: The ID of this connection (kernel → userspace) + * @pool_size: Size of the connection's buffer where the received + * messages are placed + * @offset: Pool offset where items are returned to report + * additional information about the bus and the newly + * created connection. + * @items_size: Size of buffer returned in the pool slice at @offset. + * @id128: Unique 128-bit ID of the bus (kernel → userspace) + * @items: A list of items + * + * This struct is used with the KDBUS_CMD_HELLO ioctl. + */ +struct kdbus_cmd_hello { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 attach_flags_send; + __u64 attach_flags_recv; + __u64 bus_flags; + __u64 id; + __u64 pool_size; + __u64 offset; + __u64 items_size; + __u8 id128[16]; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_info - connection information + * @size: total size of the struct + * @id: 64bit object ID + * @flags: object creation flags + * @items: list of items + * + * Note that the user is responsible for freeing the allocated memory with + * the KDBUS_CMD_FREE ioctl. + */ +struct kdbus_info { + __u64 size; + __u64 id; + __u64 flags; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_list_flags - what to include into the returned list + * @KDBUS_LIST_UNIQUE: active connections + * @KDBUS_LIST_ACTIVATORS: activator connections + * @KDBUS_LIST_NAMES: known well-known names + * @KDBUS_LIST_QUEUED: queued-up names + */ +enum kdbus_list_flags { + KDBUS_LIST_UNIQUE = 1ULL << 0, + KDBUS_LIST_NAMES = 1ULL << 1, + KDBUS_LIST_ACTIVATORS = 1ULL << 2, + KDBUS_LIST_QUEUED = 1ULL << 3, +}; + +/** + * struct kdbus_cmd_list - list connections + * @size: overall size of this object + * @flags: flags for the query (KDBUS_LIST_*), userspace → kernel + * @return_flags: command return flags, kernel → userspace + * @offset: Offset in the caller's pool buffer where an array of + * kdbus_info objects is stored. + * The user must use KDBUS_CMD_FREE to free the + * allocated memory. + * @list_size: size of returned list in bytes + * @items: Items for the command. Reserved for future use. + * + * This structure is used with the KDBUS_CMD_LIST ioctl. + */ +struct kdbus_cmd_list { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 offset; + __u64 list_size; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl + * @size: The total size of the struct + * @flags: Flags for this ioctl, userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @id: The 64-bit ID of the connection. If set to zero, passing + * @name is required. kdbus will look up the name to + * determine the ID in this case. + * @attach_flags: Set of attach flags to specify the set of information + * to receive, userspace → kernel + * @offset: Returned offset in the caller's pool buffer where the + * kdbus_info struct result is stored. The user must + * use KDBUS_CMD_FREE to free the allocated memory. + * @info_size: Output buffer to report size of data at @offset. + * @items: The optional item list, containing the + * well-known name to look up as a KDBUS_ITEM_NAME. + * Only needed in case @id is zero. + * + * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will + * tell the user the offset in the connection pool buffer at which to find the + * result in a struct kdbus_info. + */ +struct kdbus_cmd_info { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 id; + __u64 attach_flags; + __u64 offset; + __u64 info_size; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl + * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already + * exists, remove them before installing the new + * matches. + */ +enum kdbus_cmd_match_flags { + KDBUS_MATCH_REPLACE = 1ULL << 0, +}; + +/** + * struct kdbus_cmd_match - struct to add or remove matches + * @size: The total size of the struct + * @flags: Flags for match command (KDBUS_MATCH_*), + * userspace → kernel + * @return_flags: Command return flags, kernel → userspace + * @cookie: Userspace supplied cookie. When removing, the cookie + * identifies the match to remove + * @items: A list of items for additional information + * + * This structure is used with the KDBUS_CMD_MATCH_ADD and + * KDBUS_CMD_MATCH_REMOVE ioctl. + */ +struct kdbus_cmd_match { + __u64 size; + __u64 flags; + __u64 return_flags; + __u64 cookie; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,ENDPOINT}_MAKE + * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible + * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible + */ +enum kdbus_make_flags { + KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, + KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, +}; + +/** + * enum kdbus_name_flags - flags for KDBUS_CMD_NAME_ACQUIRE + * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections + * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name + * @KDBUS_NAME_QUEUE: Name should be queued if busy + * @KDBUS_NAME_IN_QUEUE: Name is queued + * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection + */ +enum kdbus_name_flags { + KDBUS_NAME_REPLACE_EXISTING = 1ULL << 0, + KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, + KDBUS_NAME_QUEUE = 1ULL << 2, + KDBUS_NAME_IN_QUEUE = 1ULL << 3, + KDBUS_NAME_ACTIVATOR = 1ULL << 4, +}; + +/** + * struct kdbus_cmd - generic ioctl payload + * @size: Overall size of this structure + * @flags: Flags for this ioctl, userspace → kernel + * @return_flags: Ioctl return flags, kernel → userspace + * @items: Additional items to modify the behavior + * + * This is a generic ioctl payload object. It's used by all ioctls that only + * take flags and items as input. + */ +struct kdbus_cmd { + __u64 size; + __u64 flags; + __u64 return_flags; + struct kdbus_item items[0]; +} __attribute__((__aligned__(8))); + +/** + * Ioctl API + * + * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command + * creates a new bus with the specified + * name. The bus is immediately shut down and + * cleaned up when the opened file descriptor is + * closed. + * + * KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to + * the bus. Such endpoints usually carry a more + * restrictive policy and grant restricted access + * to specific applications. + * KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used + * to update the policy. + * + * KDBUS_CMD_HELLO: By opening the bus node, a connection is + * created. After a HELLO the opened connection + * becomes an active peer on the bus. + * KDBUS_CMD_UPDATE: Update the properties of a connection. Used to + * update the metadata subscription mask and + * policy. + * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no + * messages queued up in the connection's pool, + * the call succeeds, and the handle is rendered + * unusable. Otherwise, -EBUSY is returned without + * any further side-effects. + * KDBUS_CMD_FREE: Release the allocated memory in the receiver's + * pool. + * KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the + * initial creator of the connection. The data was + * stored at registration time and does not + * necessarily represent the connected process or + * the actual state of the process. + * KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus + * a connection is attached to. + * + * KDBUS_CMD_SEND: Send a message and pass data from userspace to + * the kernel. + * KDBUS_CMD_RECV: Receive a message from the kernel which is + * placed in the receiver's pool. + * + * KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with + * the connection. Well-known names are used to + * address a peer on the bus. + * KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection + * currently owns. + * KDBUS_CMD_LIST: Retrieve the list of all currently registered + * well-known and unique names. + * + * KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should + * be delivered to the connection. + * KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. + */ +enum kdbus_ioctl_type { + /* bus owner (00-0f) */ + KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, + struct kdbus_cmd), + + /* endpoint owner (10-1f) */ + KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, + struct kdbus_cmd), + KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x11, + struct kdbus_cmd), + + /* connection owner (80-ff) */ + KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x80, + struct kdbus_cmd_hello), + KDBUS_CMD_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, + struct kdbus_cmd), + KDBUS_CMD_BYEBYE = _IOW(KDBUS_IOCTL_MAGIC, 0x82, + struct kdbus_cmd), + KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x83, + struct kdbus_cmd_free), + KDBUS_CMD_CONN_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x84, + struct kdbus_cmd_info), + KDBUS_CMD_BUS_CREATOR_INFO = _IOR(KDBUS_IOCTL_MAGIC, 0x85, + struct kdbus_cmd_info), + KDBUS_CMD_LIST = _IOR(KDBUS_IOCTL_MAGIC, 0x86, + struct kdbus_cmd_list), + + KDBUS_CMD_SEND = _IOW(KDBUS_IOCTL_MAGIC, 0x90, + struct kdbus_cmd_send), + KDBUS_CMD_RECV = _IOR(KDBUS_IOCTL_MAGIC, 0x91, + struct kdbus_cmd_recv), + + KDBUS_CMD_NAME_ACQUIRE = _IOW(KDBUS_IOCTL_MAGIC, 0xa0, + struct kdbus_cmd), + KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0xa1, + struct kdbus_cmd), + + KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0xb0, + struct kdbus_cmd_match), + KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0xb1, + struct kdbus_cmd_match), +}; + +#endif /* _UAPI_KDBUS_H_ */ diff --git a/src/libsystemd/src/sd-bus/sd-bus.c b/src/libsystemd/src/sd-bus/sd-bus.c new file mode 100644 index 0000000000..83a021e2f5 --- /dev/null +++ b/src/libsystemd/src/sd-bus/sd-bus.c @@ -0,0 +1,3792 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/cgroup-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-container.h" +#include "bus-control.h" +#include "bus-internal.h" +#include "bus-kernel.h" +#include "bus-message.h" +#include "bus-objects.h" +#include "bus-protocol.h" +#include "bus-slot.h" +#include "bus-socket.h" +#include "bus-track.h" +#include "bus-type.h" + +#define log_debug_bus_message(m) \ + do { \ + sd_bus_message *_mm = (m); \ + log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", \ + bus_message_type_to_string(_mm->header->type), \ + strna(sd_bus_message_get_sender(_mm)), \ + strna(sd_bus_message_get_destination(_mm)), \ + strna(sd_bus_message_get_path(_mm)), \ + strna(sd_bus_message_get_interface(_mm)), \ + strna(sd_bus_message_get_member(_mm)), \ + BUS_MESSAGE_COOKIE(_mm), \ + _mm->reply_cookie, \ + strna(_mm->error.message)); \ + } while (false) + +static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); +static int attach_io_events(sd_bus *b); +static void detach_io_events(sd_bus *b); + +static thread_local sd_bus *default_system_bus = NULL; +static thread_local sd_bus *default_user_bus = NULL; +static thread_local sd_bus *default_starter_bus = NULL; + +static void bus_close_fds(sd_bus *b) { + assert(b); + + detach_io_events(b); + + if (b->input_fd != b->output_fd) + safe_close(b->output_fd); + b->output_fd = b->input_fd = safe_close(b->input_fd); +} + +static void bus_reset_queues(sd_bus *b) { + assert(b); + + while (b->rqueue_size > 0) + sd_bus_message_unref(b->rqueue[--b->rqueue_size]); + + b->rqueue = mfree(b->rqueue); + b->rqueue_allocated = 0; + + while (b->wqueue_size > 0) + sd_bus_message_unref(b->wqueue[--b->wqueue_size]); + + b->wqueue = mfree(b->wqueue); + b->wqueue_allocated = 0; +} + +static void bus_free(sd_bus *b) { + sd_bus_slot *s; + + assert(b); + assert(!b->track_queue); + + b->state = BUS_CLOSED; + + sd_bus_detach_event(b); + + while ((s = b->slots)) { + /* At this point only floating slots can still be + * around, because the non-floating ones keep a + * reference to the bus, and we thus couldn't be + * destructing right now... We forcibly disconnect the + * slots here, so that they still can be referenced by + * apps, but are dead. */ + + assert(s->floating); + bus_slot_disconnect(s); + sd_bus_slot_unref(s); + } + + if (b->default_bus_ptr) + *b->default_bus_ptr = NULL; + + bus_close_fds(b); + + if (b->kdbus_buffer) + munmap(b->kdbus_buffer, KDBUS_POOL_SIZE); + + free(b->label); + free(b->rbuffer); + free(b->unique_name); + free(b->auth_buffer); + free(b->address); + free(b->kernel); + free(b->machine); + free(b->fake_label); + free(b->cgroup_root); + free(b->description); + + free(b->exec_path); + strv_free(b->exec_argv); + + close_many(b->fds, b->n_fds); + free(b->fds); + + bus_reset_queues(b); + + ordered_hashmap_free_free(b->reply_callbacks); + prioq_free(b->reply_callbacks_prioq); + + assert(b->match_callbacks.type == BUS_MATCH_ROOT); + bus_match_free(&b->match_callbacks); + + hashmap_free_free(b->vtable_methods); + hashmap_free_free(b->vtable_properties); + + assert(hashmap_isempty(b->nodes)); + hashmap_free(b->nodes); + + bus_kernel_flush_memfd(b); + + assert_se(pthread_mutex_destroy(&b->memfd_cache_mutex) == 0); + + free(b); +} + +_public_ int sd_bus_new(sd_bus **ret) { + sd_bus *r; + + assert_return(ret, -EINVAL); + + r = new0(sd_bus, 1); + if (!r) + return -ENOMEM; + + r->n_ref = REFCNT_INIT; + r->input_fd = r->output_fd = -1; + r->message_version = 1; + r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; + r->hello_flags |= KDBUS_HELLO_ACCEPT_FD; + r->attach_flags |= KDBUS_ATTACH_NAMES; + r->original_pid = getpid(); + + assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0); + + /* We guarantee that wqueue always has space for at least one + * entry */ + if (!GREEDY_REALLOC(r->wqueue, r->wqueue_allocated, 1)) { + free(r); + return -ENOMEM; + } + + *ret = r; + return 0; +} + +_public_ int sd_bus_set_address(sd_bus *bus, const char *address) { + char *a; + + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(address, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + a = strdup(address); + if (!a) + return -ENOMEM; + + free(bus->address); + bus->address = a; + + return 0; +} + +_public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(input_fd >= 0, -EBADF); + assert_return(output_fd >= 0, -EBADF); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->input_fd = input_fd; + bus->output_fd = output_fd; + return 0; +} + +_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) { + char *p, **a; + + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(path, -EINVAL); + assert_return(!strv_isempty(argv), -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + p = strdup(path); + if (!p) + return -ENOMEM; + + a = strv_copy(argv); + if (!a) { + free(p); + return -ENOMEM; + } + + free(bus->exec_path); + strv_free(bus->exec_argv); + + bus->exec_path = p; + bus->exec_argv = a; + + return 0; +} + +_public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->bus_client = !!b; + return 0; +} + +_public_ int sd_bus_set_monitor(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b); + return 0; +} + +_public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b); + return 0; +} + +_public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { + uint64_t new_flags; + assert_return(bus, -EINVAL); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + new_flags = bus->attach_flags; + SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); + + if (bus->attach_flags == new_flags) + return 0; + + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + + return 0; +} + +_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { + uint64_t new_flags; + + assert_return(bus, -EINVAL); + assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + SET_FLAG(bus->creds_mask, mask, b); + + /* The well knowns we need unconditionally, so that matches can work */ + bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; + + /* Make sure we don't lose the timestamp flag */ + new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); + if (bus->attach_flags == new_flags) + return 0; + + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + + return 0; +} + +_public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { + assert_return(bus, -EINVAL); + assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->is_server = !!b; + bus->server_id = server_id; + return 0; +} + +_public_ int sd_bus_set_anonymous(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->anonymous_auth = !!b; + return 0; +} + +_public_ int sd_bus_set_trusted(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->trusted = !!b; + return 0; +} + +_public_ int sd_bus_set_description(sd_bus *bus, const char *description) { + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return free_and_strdup(&bus->description, description); +} + +_public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->allow_interactive_authorization = !!b; + return 0; +} + +_public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->allow_interactive_authorization; +} + +static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { + const char *s; + sd_bus *bus; + int r; + + assert(reply); + bus = reply->bus; + assert(bus); + assert(bus->state == BUS_HELLO || bus->state == BUS_CLOSING); + + r = sd_bus_message_get_errno(reply); + if (r > 0) + return -r; + + r = sd_bus_message_read(reply, "s", &s); + if (r < 0) + return r; + + if (!service_name_is_valid(s) || s[0] != ':') + return -EBADMSG; + + bus->unique_name = strdup(s); + if (!bus->unique_name) + return -ENOMEM; + + if (bus->state == BUS_HELLO) + bus->state = BUS_RUNNING; + + return 1; +} + +static int bus_send_hello(sd_bus *bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + + if (!bus->bus_client || bus->is_kernel) + return 0; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "Hello"); + if (r < 0) + return r; + + return sd_bus_call_async(bus, NULL, m, hello_callback, NULL, 0); +} + +int bus_start_running(sd_bus *bus) { + assert(bus); + + if (bus->bus_client && !bus->is_kernel) { + bus->state = BUS_HELLO; + return 1; + } + + bus->state = BUS_RUNNING; + return 1; +} + +static int parse_address_key(const char **p, const char *key, char **value) { + size_t l, n = 0, allocated = 0; + const char *a; + char *r = NULL; + + assert(p); + assert(*p); + assert(value); + + if (key) { + l = strlen(key); + if (strncmp(*p, key, l) != 0) + return 0; + + if ((*p)[l] != '=') + return 0; + + if (*value) + return -EINVAL; + + a = *p + l + 1; + } else + a = *p; + + while (*a != ';' && *a != ',' && *a != 0) { + char c; + + if (*a == '%') { + int x, y; + + x = unhexchar(a[1]); + if (x < 0) { + free(r); + return x; + } + + y = unhexchar(a[2]); + if (y < 0) { + free(r); + return y; + } + + c = (char) ((x << 4) | y); + a += 3; + } else { + c = *a; + a++; + } + + if (!GREEDY_REALLOC(r, allocated, n + 2)) + return -ENOMEM; + + r[n++] = c; + } + + if (!r) { + r = strdup(""); + if (!r) + return -ENOMEM; + } else + r[n] = 0; + + if (*a == ',') + a++; + + *p = a; + + free(*value); + *value = r; + + return 1; +} + +static void skip_address_key(const char **p) { + assert(p); + assert(*p); + + *p += strcspn(*p, ","); + + if (**p == ',') + (*p)++; +} + +static int parse_unix_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *path = NULL, *abstract = NULL; + size_t l; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "path", &path); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "abstract", &abstract); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!path && !abstract) + return -EINVAL; + + if (path && abstract) + return -EINVAL; + + if (path) { + l = strlen(path); + if (l > sizeof(b->sockaddr.un.sun_path)) + return -E2BIG; + + b->sockaddr.un.sun_family = AF_UNIX; + strncpy(b->sockaddr.un.sun_path, path, sizeof(b->sockaddr.un.sun_path)); + b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + l; + } else if (abstract) { + l = strlen(abstract); + if (l > sizeof(b->sockaddr.un.sun_path) - 1) + return -E2BIG; + + b->sockaddr.un.sun_family = AF_UNIX; + b->sockaddr.un.sun_path[0] = 0; + strncpy(b->sockaddr.un.sun_path+1, abstract, sizeof(b->sockaddr.un.sun_path)-1); + b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l; + } + + return 0; +} + +static int parse_tcp_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *host = NULL, *port = NULL, *family = NULL; + int r; + struct addrinfo *result, hints = { + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_ADDRCONFIG, + }; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "host", &host); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "port", &port); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "family", &family); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!host || !port) + return -EINVAL; + + if (family) { + if (streq(family, "ipv4")) + hints.ai_family = AF_INET; + else if (streq(family, "ipv6")) + hints.ai_family = AF_INET6; + else + return -EINVAL; + } + + r = getaddrinfo(host, port, &hints, &result); + if (r == EAI_SYSTEM) + return -errno; + else if (r != 0) + return -EADDRNOTAVAIL; + + memcpy(&b->sockaddr, result->ai_addr, result->ai_addrlen); + b->sockaddr_size = result->ai_addrlen; + + freeaddrinfo(result); + + return 0; +} + +static int parse_exec_address(sd_bus *b, const char **p, char **guid) { + char *path = NULL; + unsigned n_argv = 0, j; + char **argv = NULL; + size_t allocated = 0; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + goto fail; + else if (r > 0) + continue; + + r = parse_address_key(p, "path", &path); + if (r < 0) + goto fail; + else if (r > 0) + continue; + + if (startswith(*p, "argv")) { + unsigned ul; + + errno = 0; + ul = strtoul(*p + 4, (char**) p, 10); + if (errno > 0 || **p != '=' || ul > 256) { + r = -EINVAL; + goto fail; + } + + (*p)++; + + if (ul >= n_argv) { + if (!GREEDY_REALLOC0(argv, allocated, ul + 2)) { + r = -ENOMEM; + goto fail; + } + + n_argv = ul + 1; + } + + r = parse_address_key(p, NULL, argv + ul); + if (r < 0) + goto fail; + + continue; + } + + skip_address_key(p); + } + + if (!path) { + r = -EINVAL; + goto fail; + } + + /* Make sure there are no holes in the array, with the + * exception of argv[0] */ + for (j = 1; j < n_argv; j++) + if (!argv[j]) { + r = -EINVAL; + goto fail; + } + + if (argv && argv[0] == NULL) { + argv[0] = strdup(path); + if (!argv[0]) { + r = -ENOMEM; + goto fail; + } + } + + b->exec_path = path; + b->exec_argv = argv; + return 0; + +fail: + for (j = 0; j < n_argv; j++) + free(argv[j]); + + free(argv); + free(path); + return r; +} + +static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *path = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "path", &path); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!path) + return -EINVAL; + + free(b->kernel); + b->kernel = path; + path = NULL; + + return 0; +} + +static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL, *pid = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "pid", &pid); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine == !pid) + return -EINVAL; + + if (machine) { + if (!machine_name_is_valid(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + } else { + b->machine = mfree(b->machine); + } + + if (pid) { + r = parse_pid(pid, &b->nspid); + if (r < 0) + return r; + } else + b->nspid = 0; + + b->sockaddr.un.sun_family = AF_UNIX; + strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); + b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); + + return 0; +} + +static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL, *pid = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "pid", &pid); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine == !pid) + return -EINVAL; + + if (machine) { + if (!machine_name_is_valid(machine)) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + } else { + b->machine = mfree(b->machine); + } + + if (pid) { + r = parse_pid(pid, &b->nspid); + if (r < 0) + return r; + } else + b->nspid = 0; + + r = free_and_strdup(&b->kernel, "/sys/fs/kdbus/0-system/bus"); + if (r < 0) + return r; + + return 0; +} + +static void bus_reset_parsed_address(sd_bus *b) { + assert(b); + + zero(b->sockaddr); + b->sockaddr_size = 0; + b->exec_argv = strv_free(b->exec_argv); + b->exec_path = mfree(b->exec_path); + b->server_id = SD_ID128_NULL; + b->kernel = mfree(b->kernel); + b->machine = mfree(b->machine); + b->nspid = 0; +} + +static int bus_parse_next_address(sd_bus *b) { + _cleanup_free_ char *guid = NULL; + const char *a; + int r; + + assert(b); + + if (!b->address) + return 0; + if (b->address[b->address_index] == 0) + return 0; + + bus_reset_parsed_address(b); + + a = b->address + b->address_index; + + while (*a != 0) { + + if (*a == ';') { + a++; + continue; + } + + if (startswith(a, "unix:")) { + a += 5; + + r = parse_unix_address(b, &a, &guid); + if (r < 0) + return r; + break; + + } else if (startswith(a, "tcp:")) { + + a += 4; + r = parse_tcp_address(b, &a, &guid); + if (r < 0) + return r; + + break; + + } else if (startswith(a, "unixexec:")) { + + a += 9; + r = parse_exec_address(b, &a, &guid); + if (r < 0) + return r; + + break; + + } else if (startswith(a, "kernel:")) { + + a += 7; + r = parse_kernel_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } else if (startswith(a, "x-machine-unix:")) { + + a += 15; + r = parse_container_unix_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } else if (startswith(a, "x-machine-kernel:")) { + + a += 17; + r = parse_container_kernel_address(b, &a, &guid); + if (r < 0) + return r; + + break; + } + + a = strchr(a, ';'); + if (!a) + return 0; + } + + if (guid) { + r = sd_id128_from_string(guid, &b->server_id); + if (r < 0) + return r; + } + + b->address_index = a - b->address; + return 1; +} + +static int bus_start_address(sd_bus *b) { + bool container_kdbus_available = false; + bool kdbus_available = false; + int r; + + assert(b); + + for (;;) { + bool skipped = false; + + bus_close_fds(b); + + /* + * Usually, if you provide multiple different bus-addresses, we + * try all of them in order. We use the first one that + * succeeds. However, if you mix kernel and unix addresses, we + * never try unix-addresses if a previous kernel address was + * tried and kdbus was available. This is required to prevent + * clients to fallback to the bus-proxy if kdbus is available + * but failed (eg., too many connections). + */ + + if (b->exec_path) + r = bus_socket_exec(b); + else if ((b->nspid > 0 || b->machine) && b->kernel) { + r = bus_container_connect_kernel(b); + if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) + container_kdbus_available = true; + + } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!container_kdbus_available) + r = bus_container_connect_socket(b); + else + skipped = true; + + } else if (b->kernel) { + r = bus_kernel_connect(b); + if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) + kdbus_available = true; + + } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!kdbus_available) + r = bus_socket_connect(b); + else + skipped = true; + } else + skipped = true; + + if (!skipped) { + if (r >= 0) { + r = attach_io_events(b); + if (r >= 0) + return r; + } + + b->last_connect_error = -r; + } + + r = bus_parse_next_address(b); + if (r < 0) + return r; + if (r == 0) + return b->last_connect_error ? -b->last_connect_error : -ECONNREFUSED; + } +} + +int bus_next_address(sd_bus *b) { + assert(b); + + bus_reset_parsed_address(b); + return bus_start_address(b); +} + +static int bus_start_fd(sd_bus *b) { + struct stat st; + int r; + + assert(b); + assert(b->input_fd >= 0); + assert(b->output_fd >= 0); + + r = fd_nonblock(b->input_fd, true); + if (r < 0) + return r; + + r = fd_cloexec(b->input_fd, true); + if (r < 0) + return r; + + if (b->input_fd != b->output_fd) { + r = fd_nonblock(b->output_fd, true); + if (r < 0) + return r; + + r = fd_cloexec(b->output_fd, true); + if (r < 0) + return r; + } + + if (fstat(b->input_fd, &st) < 0) + return -errno; + + if (S_ISCHR(b->input_fd)) + return bus_kernel_take_fd(b); + else + return bus_socket_take_fd(b); +} + +_public_ int sd_bus_start(sd_bus *bus) { + int r; + + assert_return(bus, -EINVAL); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->state = BUS_OPENING; + + if (bus->is_server && bus->bus_client) + return -EINVAL; + + if (bus->input_fd >= 0) + r = bus_start_fd(bus); + else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine) + r = bus_start_address(bus); + else + return -EINVAL; + + if (r < 0) { + sd_bus_close(bus); + return r; + } + + return bus_send_hello(bus); +} + +_public_ int sd_bus_open(sd_bus **ret) { + const char *e; + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + /* Let's connect to the starter bus if it is set, and + * otherwise to the bus that is appropropriate for the scope + * we are running in */ + + e = secure_getenv("DBUS_STARTER_BUS_TYPE"); + if (e) { + if (streq(e, "system")) + return sd_bus_open_system(ret); + else if (STR_IN_SET(e, "session", "user")) + return sd_bus_open_user(ret); + } + + e = secure_getenv("DBUS_STARTER_ADDRESS"); + if (!e) { + if (cg_pid_get_owner_uid(0, NULL) >= 0) + return sd_bus_open_user(ret); + else + return sd_bus_open_system(ret); + } + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = sd_bus_set_address(b, e); + if (r < 0) + goto fail; + + b->bus_client = true; + + /* We don't know whether the bus is trusted or not, so better + * be safe, and authenticate everything */ + b->trusted = false; + b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; + + r = sd_bus_start(b); + if (r < 0) + goto fail; + + *ret = b; + return 0; + +fail: + bus_free(b); + return r; +} + +int bus_set_address_system(sd_bus *b) { + const char *e; + assert(b); + + e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (e) + return sd_bus_set_address(b, e); + + return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); +} + +_public_ int sd_bus_open_system(sd_bus **ret) { + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = bus_set_address_system(b); + if (r < 0) + goto fail; + + b->bus_client = true; + b->is_system = true; + + /* Let's do per-method access control on the system bus. We + * need the caller's UID and capability set for that. */ + b->trusted = false; + b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; + + r = sd_bus_start(b); + if (r < 0) + goto fail; + + *ret = b; + return 0; + +fail: + bus_free(b); + return r; +} + +int bus_set_address_user(sd_bus *b) { + const char *e; + uid_t uid; + int r; + + assert(b); + + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + if (e) + return sd_bus_set_address(b, e); + + r = cg_pid_get_owner_uid(0, &uid); + if (r < 0) + uid = getuid(); + + e = secure_getenv("XDG_RUNTIME_DIR"); + if (e) { + _cleanup_free_ char *ee = NULL; + + ee = bus_address_escape(e); + if (!ee) + return -ENOMEM; + + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee); + } else + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid); + + if (!b->address) + return -ENOMEM; + + return 0; +} + +_public_ int sd_bus_open_user(sd_bus **ret) { + sd_bus *b; + int r; + + assert_return(ret, -EINVAL); + + r = sd_bus_new(&b); + if (r < 0) + return r; + + r = bus_set_address_user(b); + if (r < 0) + return r; + + b->bus_client = true; + b->is_user = true; + + /* We don't do any per-method access control on the user + * bus. */ + b->trusted = true; + + r = sd_bus_start(b); + if (r < 0) + goto fail; + + *ret = b; + return 0; + +fail: + bus_free(b); + return r; +} + +int bus_set_address_system_remote(sd_bus *b, const char *host) { + _cleanup_free_ char *e = NULL; + char *m = NULL, *c = NULL; + + assert(b); + assert(host); + + /* Let's see if we shall enter some container */ + m = strchr(host, ':'); + if (m) { + m++; + + /* Let's make sure this is not a port of some kind, + * and is a valid machine name. */ + if (!in_charset(m, "0123456789") && machine_name_is_valid(m)) { + char *t; + + /* Cut out the host part */ + t = strndupa(host, m - host - 1); + e = bus_address_escape(t); + if (!e) + return -ENOMEM; + + c = strjoina(",argv4=--machine=", m); + } + } + + if (!e) { + e = bus_address_escape(host); + if (!e) + return -ENOMEM; + } + + b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c, NULL); + if (!b->address) + return -ENOMEM; + + return 0; + } + +_public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { + sd_bus *bus; + int r; + + assert_return(host, -EINVAL); + assert_return(ret, -EINVAL); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = bus_set_address_system_remote(bus, host); + if (r < 0) + goto fail; + + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + goto fail; + + *ret = bus; + return 0; + +fail: + bus_free(bus); + return r; +} + +int bus_set_address_system_machine(sd_bus *b, const char *machine) { + _cleanup_free_ char *e = NULL; + + assert(b); + assert(machine); + + e = bus_address_escape(machine); + if (!e) + return -ENOMEM; + + b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); + if (!b->address) + return -ENOMEM; + + return 0; +} + +_public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { + sd_bus *bus; + int r; + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(machine_name_is_valid(machine), -EINVAL); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + r = bus_set_address_system_machine(bus, machine); + if (r < 0) + goto fail; + + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + goto fail; + + *ret = bus; + return 0; + +fail: + bus_free(bus); + return r; +} + +_public_ void sd_bus_close(sd_bus *bus) { + + if (!bus) + return; + if (bus->state == BUS_CLOSED) + return; + if (bus_pid_changed(bus)) + return; + + bus->state = BUS_CLOSED; + + sd_bus_detach_event(bus); + + /* Drop all queued messages so that they drop references to + * the bus object and the bus may be freed */ + bus_reset_queues(bus); + + if (!bus->is_kernel) + bus_close_fds(bus); + + /* We'll leave the fd open in case this is a kernel bus, since + * there might still be memblocks around that reference this + * bus, and they might need to invoke the KDBUS_CMD_FREE + * ioctl on the fd when they are freed. */ +} + +_public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { + + if (!bus) + return NULL; + + sd_bus_flush(bus); + sd_bus_close(bus); + + return sd_bus_unref(bus); +} + +static void bus_enter_closing(sd_bus *bus) { + assert(bus); + + if (bus->state != BUS_OPENING && + bus->state != BUS_AUTHENTICATING && + bus->state != BUS_HELLO && + bus->state != BUS_RUNNING) + return; + + bus->state = BUS_CLOSING; +} + +_public_ sd_bus *sd_bus_ref(sd_bus *bus) { + + if (!bus) + return NULL; + + assert_se(REFCNT_INC(bus->n_ref) >= 2); + + return bus; +} + +_public_ sd_bus *sd_bus_unref(sd_bus *bus) { + unsigned i; + + if (!bus) + return NULL; + + i = REFCNT_DEC(bus->n_ref); + if (i > 0) + return NULL; + + bus_free(bus); + return NULL; +} + +_public_ int sd_bus_is_open(sd_bus *bus) { + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return BUS_IS_OPEN(bus->state); +} + +_public_ int sd_bus_can_send(sd_bus *bus, char type) { + int r; + + assert_return(bus, -EINVAL); + assert_return(bus->state != BUS_UNSET, -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + if (type == SD_BUS_TYPE_UNIX_FD) { + if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + return 0; + + r = bus_ensure_running(bus); + if (r < 0) + return r; + + return bus->can_fds; + } + + return bus_type_is_valid(type); +} + +_public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { + int r; + + assert_return(bus, -EINVAL); + assert_return(id, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = bus_ensure_running(bus); + if (r < 0) + return r; + + *id = bus->server_id; + return 0; +} + +static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { + assert(b); + assert(m); + + if (m->sealed) { + /* If we copy the same message to multiple + * destinations, avoid using the same cookie + * numbers. */ + b->cookie = MAX(b->cookie, BUS_MESSAGE_COOKIE(m)); + return 0; + } + + if (timeout == 0) + timeout = BUS_DEFAULT_TIMEOUT; + + return bus_message_seal(m, ++b->cookie, timeout); +} + +static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) { + bool remarshal = false; + + assert(b); + + /* wrong packet version */ + if (b->message_version != 0 && b->message_version != (*m)->header->version) + remarshal = true; + + /* wrong packet endianness */ + if (b->message_endian != 0 && b->message_endian != (*m)->header->endian) + remarshal = true; + + /* TODO: kdbus-messages received from the kernel contain data which is + * not allowed to be passed to KDBUS_CMD_SEND. Therefore, we have to + * force remarshaling of the message. Technically, we could just + * recreate the kdbus message, but that is non-trivial as other parts of + * the message refer to m->kdbus already. This should be fixed! */ + if ((*m)->kdbus && (*m)->release_kdbus) + remarshal = true; + + return remarshal ? bus_message_remarshal(b, m) : 0; +} + +int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { + assert(b); + assert(m); + + /* Fake some timestamps, if they were requested, and not + * already initialized */ + if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { + if (m->realtime <= 0) + m->realtime = now(CLOCK_REALTIME); + + if (m->monotonic <= 0) + m->monotonic = now(CLOCK_MONOTONIC); + } + + /* The bus specification says the serial number cannot be 0, + * hence let's fill something in for synthetic messages. Since + * synthetic messages might have a fake sender and we don't + * want to interfere with the real sender's serial numbers we + * pick a fixed, artificial one. We use (uint32_t) -1 rather + * than (uint64_t) -1 since dbus1 only had 32bit identifiers, + * even though kdbus can do 64bit. */ + return bus_message_seal(m, 0xFFFFFFFFULL, 0); +} + +static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call, size_t *idx) { + int r; + + assert(bus); + assert(m); + + if (bus->is_kernel) + r = bus_kernel_write_message(bus, m, hint_sync_call); + else + r = bus_socket_write_message(bus, m, idx); + + if (r <= 0) + return r; + + if (bus->is_kernel || *idx >= BUS_MESSAGE_SIZE(m)) + log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error=%s", + bus_message_type_to_string(m->header->type), + strna(sd_bus_message_get_sender(m)), + strna(sd_bus_message_get_destination(m)), + strna(sd_bus_message_get_path(m)), + strna(sd_bus_message_get_interface(m)), + strna(sd_bus_message_get_member(m)), + BUS_MESSAGE_COOKIE(m), + m->reply_cookie, + strna(m->error.message)); + + return r; +} + +static int dispatch_wqueue(sd_bus *bus) { + int r, ret = 0; + + assert(bus); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + while (bus->wqueue_size > 0) { + + r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex); + if (r < 0) + return r; + else if (r == 0) + /* Didn't do anything this time */ + return ret; + else if (bus->is_kernel || bus->windex >= BUS_MESSAGE_SIZE(bus->wqueue[0])) { + /* Fully written. Let's drop the entry from + * the queue. + * + * This isn't particularly optimized, but + * well, this is supposed to be our worst-case + * buffer only, and the socket buffer is + * supposed to be our primary buffer, and if + * it got full, then all bets are off + * anyway. */ + + bus->wqueue_size--; + sd_bus_message_unref(bus->wqueue[0]); + memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size); + bus->windex = 0; + + ret = 1; + } + } + + return ret; +} + +static int bus_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { + assert(bus); + + if (bus->is_kernel) + return bus_kernel_read_message(bus, hint_priority, priority); + else + return bus_socket_read_message(bus); +} + +int bus_rqueue_make_room(sd_bus *bus) { + assert(bus); + + if (bus->rqueue_size >= BUS_RQUEUE_MAX) + return -ENOBUFS; + + if (!GREEDY_REALLOC(bus->rqueue, bus->rqueue_allocated, bus->rqueue_size + 1)) + return -ENOMEM; + + return 0; +} + +static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **m) { + int r, ret = 0; + + assert(bus); + assert(m); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + /* Note that the priority logic is only available on kdbus, + * where the rqueue is unused. We check the rqueue here + * anyway, because it's simple... */ + + for (;;) { + if (bus->rqueue_size > 0) { + /* Dispatch a queued message */ + + *m = bus->rqueue[0]; + bus->rqueue_size--; + memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size); + return 1; + } + + /* Try to read a new message */ + r = bus_read_message(bus, hint_priority, priority); + if (r < 0) + return r; + if (r == 0) + return ret; + + ret = 1; + } +} + +static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); + int r; + + assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (m->n_fds > 0) { + r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD); + if (r < 0) + return r; + if (r == 0) + return -EOPNOTSUPP; + } + + /* If the cookie number isn't kept, then we know that no reply + * is expected */ + if (!cookie && !m->sealed) + m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + + r = bus_seal_message(bus, m, 0); + if (r < 0) + return r; + + /* Remarshall if we have to. This will possibly unref the + * message and place a replacement in m */ + r = bus_remarshal_message(bus, &m); + if (r < 0) + return r; + + /* If this is a reply and no reply was requested, then let's + * suppress this, if we can */ + if (m->dont_send) + goto finish; + + if ((bus->state == BUS_RUNNING || bus->state == BUS_HELLO) && bus->wqueue_size <= 0) { + size_t idx = 0; + + r = bus_write_message(bus, m, hint_sync_call, &idx); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + return -ECONNRESET; + } + + return r; + } + + if (!bus->is_kernel && idx < BUS_MESSAGE_SIZE(m)) { + /* Wasn't fully written. So let's remember how + * much was written. Note that the first entry + * of the wqueue array is always allocated so + * that we always can remember how much was + * written. */ + bus->wqueue[0] = sd_bus_message_ref(m); + bus->wqueue_size = 1; + bus->windex = idx; + } + + } else { + /* Just append it to the queue. */ + + if (bus->wqueue_size >= BUS_WQUEUE_MAX) + return -ENOBUFS; + + if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1)) + return -ENOMEM; + + bus->wqueue[bus->wqueue_size++] = sd_bus_message_ref(m); + } + +finish: + if (cookie) + *cookie = BUS_MESSAGE_COOKIE(m); + + return 1; +} + +_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { + return bus_send_internal(bus, m, cookie, false); +} + +_public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { + int r; + + assert_return(m, -EINVAL); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (!streq_ptr(m->destination, destination)) { + + if (!destination) + return -EEXIST; + + r = sd_bus_message_set_destination(m, destination); + if (r < 0) + return r; + } + + return sd_bus_send(bus, m, cookie); +} + +static usec_t calc_elapse(uint64_t usec) { + if (usec == (uint64_t) -1) + return 0; + + return now(CLOCK_MONOTONIC) + usec; +} + +static int timeout_compare(const void *a, const void *b) { + const struct reply_callback *x = a, *y = b; + + if (x->timeout != 0 && y->timeout == 0) + return -1; + + if (x->timeout == 0 && y->timeout != 0) + return 1; + + if (x->timeout < y->timeout) + return -1; + + if (x->timeout > y->timeout) + return 1; + + return 0; +} + +_public_ int sd_bus_call_async( + sd_bus *bus, + sd_bus_slot **slot, + sd_bus_message *_m, + sd_bus_message_handler_t callback, + void *userdata, + uint64_t usec) { + + _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); + assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); + assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); + assert_return(callback, -EINVAL); + + if (!bus) + bus = m->bus; + + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); + if (r < 0) + return r; + + r = prioq_ensure_allocated(&bus->reply_callbacks_prioq, timeout_compare); + if (r < 0) + return r; + + r = bus_seal_message(bus, m, usec); + if (r < 0) + return r; + + r = bus_remarshal_message(bus, &m); + if (r < 0) + return r; + + s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); + if (!s) + return -ENOMEM; + + s->reply_callback.callback = callback; + + s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); + r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); + if (r < 0) { + s->reply_callback.cookie = 0; + return r; + } + + s->reply_callback.timeout = calc_elapse(m->timeout); + if (s->reply_callback.timeout != 0) { + r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + if (r < 0) { + s->reply_callback.timeout = 0; + return r; + } + } + + r = sd_bus_send(bus, m, &s->reply_callback.cookie); + if (r < 0) + return r; + + if (slot) + *slot = s; + s = NULL; + + return r; +} + +int bus_ensure_running(sd_bus *bus) { + int r; + + assert(bus); + + if (bus->state == BUS_UNSET || bus->state == BUS_CLOSED || bus->state == BUS_CLOSING) + return -ENOTCONN; + if (bus->state == BUS_RUNNING) + return 1; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (bus->state == BUS_RUNNING) + return 1; + if (r > 0) + continue; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +_public_ int sd_bus_call( + sd_bus *bus, + sd_bus_message *_m, + uint64_t usec, + sd_bus_error *error, + sd_bus_message **reply) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); + usec_t timeout; + uint64_t cookie; + unsigned i; + int r; + + bus_assert_return(m, -EINVAL, error); + bus_assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL, error); + bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error); + bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error); + + if (!bus) + bus = m->bus; + + bus_assert_return(!bus_pid_changed(bus), -ECHILD, error); + bus_assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS, error); + + if (!BUS_IS_OPEN(bus->state)) { + r = -ENOTCONN; + goto fail; + } + + r = bus_ensure_running(bus); + if (r < 0) + goto fail; + + i = bus->rqueue_size; + + r = bus_seal_message(bus, m, usec); + if (r < 0) + goto fail; + + r = bus_remarshal_message(bus, &m); + if (r < 0) + goto fail; + + r = bus_send_internal(bus, m, &cookie, true); + if (r < 0) + goto fail; + + timeout = calc_elapse(m->timeout); + + for (;;) { + usec_t left; + + while (i < bus->rqueue_size) { + sd_bus_message *incoming = NULL; + + incoming = bus->rqueue[i]; + + if (incoming->reply_cookie == cookie) { + /* Found a match! */ + + memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); + bus->rqueue_size--; + log_debug_bus_message(incoming); + + if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { + + if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (reply) + *reply = incoming; + else + sd_bus_message_unref(incoming); + + return 1; + } + + r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); + sd_bus_message_unref(incoming); + return r; + + } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { + r = sd_bus_error_copy(error, &incoming->error); + sd_bus_message_unref(incoming); + return r; + } else { + r = -EIO; + goto fail; + } + + } else if (BUS_MESSAGE_COOKIE(incoming) == cookie && + bus->unique_name && + incoming->sender && + streq(bus->unique_name, incoming->sender)) { + + memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); + bus->rqueue_size--; + + /* Our own message? Somebody is trying + * to send its own client a message, + * let's not dead-lock, let's fail + * immediately. */ + + sd_bus_message_unref(incoming); + r = -ELOOP; + goto fail; + } + + /* Try to read more, right-away */ + i++; + } + + r = bus_read_message(bus, false, 0); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = -ECONNRESET; + } + + goto fail; + } + if (r > 0) + continue; + + if (timeout > 0) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (n >= timeout) { + r = -ETIMEDOUT; + goto fail; + } + + left = timeout - n; + } else + left = (uint64_t) -1; + + r = bus_poll(bus, true, left); + if (r < 0) + goto fail; + if (r == 0) { + r = -ETIMEDOUT; + goto fail; + } + + r = dispatch_wqueue(bus); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = -ECONNRESET; + } + + goto fail; + } + } + +fail: + return sd_bus_error_set_errno(error, r); +} + +_public_ int sd_bus_get_fd(sd_bus *bus) { + + assert_return(bus, -EINVAL); + assert_return(bus->input_fd == bus->output_fd, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->input_fd; +} + +_public_ int sd_bus_get_events(sd_bus *bus) { + int flags = 0; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + return -ENOTCONN; + + if (bus->state == BUS_OPENING) + flags |= POLLOUT; + else if (bus->state == BUS_AUTHENTICATING) { + + if (bus_socket_auth_needs_write(bus)) + flags |= POLLOUT; + + flags |= POLLIN; + + } else if (bus->state == BUS_RUNNING || bus->state == BUS_HELLO) { + if (bus->rqueue_size <= 0) + flags |= POLLIN; + if (bus->wqueue_size > 0) + flags |= POLLOUT; + } + + return flags; +} + +_public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { + struct reply_callback *c; + + assert_return(bus, -EINVAL); + assert_return(timeout_usec, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + return -ENOTCONN; + + if (bus->track_queue) { + *timeout_usec = 0; + return 1; + } + + if (bus->state == BUS_CLOSING) { + *timeout_usec = 0; + return 1; + } + + if (bus->state == BUS_AUTHENTICATING) { + *timeout_usec = bus->auth_timeout; + return 1; + } + + if (bus->state != BUS_RUNNING && bus->state != BUS_HELLO) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + if (bus->rqueue_size > 0) { + *timeout_usec = 0; + return 1; + } + + c = prioq_peek(bus->reply_callbacks_prioq); + if (!c) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + if (c->timeout == 0) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + *timeout_usec = c->timeout; + return 1; +} + +static int process_timeout(sd_bus *bus) { + _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; + int r; + + assert(bus); + + c = prioq_peek(bus->reply_callbacks_prioq); + if (!c) + return 0; + + n = now(CLOCK_MONOTONIC); + if (c->timeout > n) + return 0; + + r = bus_message_new_synthetic_error( + bus, + c->cookie, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Method call timed out"), + &m); + if (r < 0) + return r; + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); + c->timeout = 0; + + ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + bus->iteration_counter++; + + bus->current_message = m; + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + bus->current_message = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); +} + +static int process_hello(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + if (bus->state != BUS_HELLO) + return 0; + + /* Let's make sure the first message on the bus is the HELLO + * reply. But note that we don't actually parse the message + * here (we leave that to the usual handling), we just verify + * we don't let any earlier msg through. */ + + if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && + m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) + return -EIO; + + if (m->reply_cookie != 1) + return -EIO; + + return 0; +} + +static int process_reply(sd_bus *bus, sd_bus_message *m) { + _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; + + assert(bus); + assert(m); + + if (m->header->type != SD_BUS_MESSAGE_METHOD_RETURN && + m->header->type != SD_BUS_MESSAGE_METHOD_ERROR) + return 0; + + if (bus->is_kernel && (bus->hello_flags & KDBUS_HELLO_MONITOR)) + return 0; + + if (m->destination && bus->unique_name && !streq_ptr(m->destination, bus->unique_name)) + return 0; + + c = ordered_hashmap_remove(bus->reply_callbacks, &m->reply_cookie); + if (!c) + return 0; + + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + + /* If the reply contained a file descriptor which we + * didn't want we pass an error instead. */ + + r = bus_message_new_synthetic_error( + bus, + m->reply_cookie, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptor"), + &synthetic_reply); + if (r < 0) + return r; + + /* Copy over original timestamp */ + synthetic_reply->realtime = m->realtime; + synthetic_reply->monotonic = m->monotonic; + synthetic_reply->seqnum = m->seqnum; + + r = bus_seal_synthetic_message(bus, synthetic_reply); + if (r < 0) + return r; + + m = synthetic_reply; + } else { + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + } + + if (c->timeout != 0) { + prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); + c->timeout = 0; + } + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); +} + +static int process_filter(sd_bus *bus, sd_bus_message *m) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + struct filter_callback *l; + int r; + + assert(bus); + assert(m); + + do { + bus->filter_callbacks_modified = false; + + LIST_FOREACH(callbacks, l, bus->filter_callbacks) { + sd_bus_slot *slot; + + if (bus->filter_callbacks_modified) + break; + + /* Don't run this more than once per iteration */ + if (l->last_iteration == bus->iteration_counter) + continue; + + l->last_iteration = bus->iteration_counter; + + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + slot = container_of(l, sd_bus_slot, filter_callback); + + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = l->callback; + bus->current_userdata = slot->userdata; + r = l->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = sd_bus_slot_unref(slot); + + r = bus_maybe_reply_error(m, r, &error_buffer); + if (r != 0) + return r; + + } + + } while (bus->filter_callbacks_modified); + + return 0; +} + +static int process_match(sd_bus *bus, sd_bus_message *m) { + int r; + + assert(bus); + assert(m); + + do { + bus->match_callbacks_modified = false; + + r = bus_match_run(bus, &bus->match_callbacks, m); + if (r != 0) + return r; + + } while (bus->match_callbacks_modified); + + return 0; +} + +static int process_builtin(sd_bus *bus, sd_bus_message *m) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(bus); + assert(m); + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + if (bus->manual_peer_interface) + return 0; + + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) + return 0; + + if (!streq_ptr(m->interface, "org.freedesktop.DBus.Peer")) + return 0; + + if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) + return 1; + + if (streq_ptr(m->member, "Ping")) + r = sd_bus_message_new_method_return(m, &reply); + else if (streq_ptr(m->member, "GetMachineId")) { + sd_id128_t id; + char sid[33]; + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid)); + } else { + r = sd_bus_message_new_method_errorf( + m, &reply, + SD_BUS_ERROR_UNKNOWN_METHOD, + "Unknown method '%s' on interface '%s'.", m->member, m->interface); + } + + if (r < 0) + return r; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; + + return 1; +} + +static int process_fd_check(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + /* If we got a message with a file descriptor which we didn't + * want to accept, then let's drop it. How can this even + * happen? For example, when the kernel queues a message into + * an activatable names's queue which allows fds, and then is + * delivered to us later even though we ourselves did not + * negotiate it. */ + + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + + if (m->n_fds <= 0) + return 0; + + if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) + return 0; + + if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) + return 1; /* just eat it up */ + + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Message contains file descriptors, which I cannot accept. Sorry."); +} + +static int process_message(sd_bus *bus, sd_bus_message *m) { + int r; + + assert(bus); + assert(m); + + bus->current_message = m; + bus->iteration_counter++; + + log_debug_bus_message(m); + + r = process_hello(bus, m); + if (r != 0) + goto finish; + + r = process_reply(bus, m); + if (r != 0) + goto finish; + + r = process_fd_check(bus, m); + if (r != 0) + goto finish; + + r = process_filter(bus, m); + if (r != 0) + goto finish; + + r = process_match(bus, m); + if (r != 0) + goto finish; + + r = process_builtin(bus, m); + if (r != 0) + goto finish; + + r = bus_process_object(bus, m); + +finish: + bus->current_message = NULL; + return r; +} + +static int dispatch_track(sd_bus *bus) { + assert(bus); + + if (!bus->track_queue) + return 0; + + bus_track_dispatch(bus->track_queue); + return 1; +} + +static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO); + + r = process_timeout(bus); + if (r != 0) + goto null_message; + + r = dispatch_wqueue(bus); + if (r != 0) + goto null_message; + + r = dispatch_track(bus); + if (r != 0) + goto null_message; + + r = dispatch_rqueue(bus, hint_priority, priority, &m); + if (r < 0) + return r; + if (!m) + goto null_message; + + r = process_message(bus, m); + if (r != 0) + goto null_message; + + if (ret) { + r = sd_bus_message_rewind(m, true); + if (r < 0) + return r; + + *ret = m; + m = NULL; + return 1; + } + + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) { + + log_debug("Unprocessed message call sender=%s object=%s interface=%s member=%s", + strna(sd_bus_message_get_sender(m)), + strna(sd_bus_message_get_path(m)), + strna(sd_bus_message_get_interface(m)), + strna(sd_bus_message_get_member(m))); + + r = sd_bus_reply_method_errorf( + m, + SD_BUS_ERROR_UNKNOWN_OBJECT, + "Unknown object '%s'.", m->path); + if (r < 0) + return r; + } + + return 1; + +null_message: + if (r >= 0 && ret) + *ret = NULL; + + return r; +} + +static int process_closing(sd_bus *bus, sd_bus_message **ret) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct reply_callback *c; + int r; + + assert(bus); + assert(bus->state == BUS_CLOSING); + + c = ordered_hashmap_first(bus->reply_callbacks); + if (c) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + sd_bus_slot *slot; + + /* First, fail all outstanding method calls */ + r = bus_message_new_synthetic_error( + bus, + c->cookie, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"), + &m); + if (r < 0) + return r; + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + if (c->timeout != 0) { + prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); + c->timeout = 0; + } + + ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); + c->cookie = 0; + + slot = container_of(c, sd_bus_slot, reply_callback); + + bus->iteration_counter++; + + bus->current_message = m; + bus->current_slot = sd_bus_slot_ref(slot); + bus->current_handler = c->callback; + bus->current_userdata = slot->userdata; + r = c->callback(m, slot->userdata, &error_buffer); + bus->current_userdata = NULL; + bus->current_handler = NULL; + bus->current_slot = NULL; + bus->current_message = NULL; + + if (slot->floating) { + bus_slot_disconnect(slot); + sd_bus_slot_unref(slot); + } + + sd_bus_slot_unref(slot); + + return bus_maybe_reply_error(m, r, &error_buffer); + } + + /* Then, synthesize a Disconnected message */ + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Disconnected"); + if (r < 0) + return r; + + bus_message_set_sender_local(bus, m); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + sd_bus_close(bus); + + bus->current_message = m; + bus->iteration_counter++; + + r = process_filter(bus, m); + if (r != 0) + goto finish; + + r = process_match(bus, m); + if (r != 0) + goto finish; + + if (ret) { + *ret = m; + m = NULL; + } + + r = 1; + +finish: + bus->current_message = NULL; + + return r; +} + +static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { + BUS_DONT_DESTROY(bus); + int r; + + /* Returns 0 when we didn't do anything. This should cause the + * caller to invoke sd_bus_wait() before returning the next + * time. Returns > 0 when we did something, which possibly + * means *ret is filled in with an unprocessed message. */ + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + /* We don't allow recursively invoking sd_bus_process(). */ + assert_return(!bus->current_message, -EBUSY); + assert(!bus->current_slot); + + switch (bus->state) { + + case BUS_UNSET: + return -ENOTCONN; + + case BUS_CLOSED: + return -ECONNRESET; + + case BUS_OPENING: + r = bus_socket_process_opening(bus); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = 1; + } else if (r < 0) + return r; + if (ret) + *ret = NULL; + return r; + + case BUS_AUTHENTICATING: + r = bus_socket_process_authenticating(bus); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = 1; + } else if (r < 0) + return r; + + if (ret) + *ret = NULL; + + return r; + + case BUS_RUNNING: + case BUS_HELLO: + r = process_running(bus, hint_priority, priority, ret); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = 1; + + if (ret) + *ret = NULL; + } + + return r; + + case BUS_CLOSING: + return process_closing(bus, ret); + } + + assert_not_reached("Unknown state"); +} + +_public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { + return bus_process_internal(bus, false, 0, ret); +} + +_public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) { + return bus_process_internal(bus, true, priority, ret); +} + +static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { + struct pollfd p[2] = {}; + int r, e, n; + struct timespec ts; + usec_t m = USEC_INFINITY; + + assert(bus); + + if (bus->state == BUS_CLOSING) + return 1; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + e = sd_bus_get_events(bus); + if (e < 0) + return e; + + if (need_more) + /* The caller really needs some more data, he doesn't + * care about what's already read, or any timeouts + * except its own. */ + e |= POLLIN; + else { + usec_t until; + /* The caller wants to process if there's something to + * process, but doesn't care otherwise */ + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) { + usec_t nw; + nw = now(CLOCK_MONOTONIC); + m = until > nw ? until - nw : 0; + } + } + + if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) + m = timeout_usec; + + p[0].fd = bus->input_fd; + if (bus->output_fd == bus->input_fd) { + p[0].events = e; + n = 1; + } else { + p[0].events = e & POLLIN; + p[1].fd = bus->output_fd; + p[1].events = e & POLLOUT; + n = 2; + } + + r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + if (r < 0) + return -errno; + + return r > 0 ? 1 : 0; +} + +_public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->state == BUS_CLOSING) + return 0; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->rqueue_size > 0) + return 0; + + return bus_poll(bus, false, timeout_usec); +} + +_public_ int sd_bus_flush(sd_bus *bus) { + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->state == BUS_CLOSING) + return 0; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + r = bus_ensure_running(bus); + if (r < 0) + return r; + + if (bus->wqueue_size <= 0) + return 0; + + for (;;) { + r = dispatch_wqueue(bus); + if (r < 0) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + return -ECONNRESET; + } + + return r; + } + + if (bus->wqueue_size <= 0) + return 0; + + r = bus_poll(bus, false, (uint64_t) -1); + if (r < 0) + return r; + } +} + +_public_ int sd_bus_add_filter( + sd_bus *bus, + sd_bus_slot **slot, + sd_bus_message_handler_t callback, + void *userdata) { + + sd_bus_slot *s; + + assert_return(bus, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + s = bus_slot_allocate(bus, !slot, BUS_FILTER_CALLBACK, sizeof(struct filter_callback), userdata); + if (!s) + return -ENOMEM; + + s->filter_callback.callback = callback; + + bus->filter_callbacks_modified = true; + LIST_PREPEND(callbacks, bus->filter_callbacks, &s->filter_callback); + + if (slot) + *slot = s; + + return 0; +} + +_public_ int sd_bus_add_match( + sd_bus *bus, + sd_bus_slot **slot, + const char *match, + sd_bus_message_handler_t callback, + void *userdata) { + + struct bus_match_component *components = NULL; + unsigned n_components = 0; + sd_bus_slot *s = NULL; + int r = 0; + + assert_return(bus, -EINVAL); + assert_return(match, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = bus_match_parse(match, &components, &n_components); + if (r < 0) + goto finish; + + s = bus_slot_allocate(bus, !slot, BUS_MATCH_CALLBACK, sizeof(struct match_callback), userdata); + if (!s) { + r = -ENOMEM; + goto finish; + } + + s->match_callback.callback = callback; + s->match_callback.cookie = ++bus->match_cookie; + + if (bus->bus_client) { + enum bus_match_scope scope; + + scope = bus_match_get_scope(components, n_components); + + /* Do not install server-side matches for matches + * against the local service, interface or bus + * path. */ + if (scope != BUS_MATCH_LOCAL) { + + if (!bus->is_kernel) { + /* When this is not a kernel transport, we + * store the original match string, so that we + * can use it to remove the match again */ + + s->match_callback.match_string = strdup(match); + if (!s->match_callback.match_string) { + r = -ENOMEM; + goto finish; + } + } + + r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components, s->match_callback.cookie); + if (r < 0) + goto finish; + + s->match_added = true; + } + } + + bus->match_callbacks_modified = true; + r = bus_match_add(&bus->match_callbacks, components, n_components, &s->match_callback); + if (r < 0) + goto finish; + + if (slot) + *slot = s; + s = NULL; + +finish: + bus_match_parse_free(components, n_components); + sd_bus_slot_unref(s); + + return r; +} + +int bus_remove_match_by_string( + sd_bus *bus, + const char *match, + sd_bus_message_handler_t callback, + void *userdata) { + + struct bus_match_component *components = NULL; + unsigned n_components = 0; + struct match_callback *c; + int r = 0; + + assert_return(bus, -EINVAL); + assert_return(match, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = bus_match_parse(match, &components, &n_components); + if (r < 0) + goto finish; + + r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); + if (r <= 0) + goto finish; + + sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); + +finish: + bus_match_parse_free(components, n_components); + + return r; +} + +bool bus_pid_changed(sd_bus *bus) { + assert(bus); + + /* We don't support people creating a bus connection and + * keeping it around over a fork(). Let's complain. */ + + return bus->original_pid != getpid(); +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + + return 1; +} + +static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { + sd_bus *bus = userdata; + int r; + + assert(bus); + + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + + return 1; +} + +static int prepare_callback(sd_event_source *s, void *userdata) { + sd_bus *bus = userdata; + int r, e; + usec_t until; + + assert(s); + assert(bus); + + e = sd_bus_get_events(bus); + if (e < 0) + return e; + + if (bus->output_fd != bus->input_fd) { + + r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN); + if (r < 0) + return r; + + r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT); + if (r < 0) + return r; + } else { + r = sd_event_source_set_io_events(bus->input_io_event_source, e); + if (r < 0) + return r; + } + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) { + int j; + + j = sd_event_source_set_time(bus->time_event_source, until); + if (j < 0) + return j; + } + + r = sd_event_source_set_enabled(bus->time_event_source, r > 0); + if (r < 0) + return r; + + return 1; +} + +static int quit_callback(sd_event_source *event, void *userdata) { + sd_bus *bus = userdata; + + assert(event); + + sd_bus_flush(bus); + sd_bus_close(bus); + + return 1; +} + +static int attach_io_events(sd_bus *bus) { + int r; + + assert(bus); + + if (bus->input_fd < 0) + return 0; + + if (!bus->event) + return 0; + + if (!bus->input_io_event_source) { + r = sd_event_add_io(bus->event, &bus->input_io_event_source, bus->input_fd, 0, io_callback, bus); + if (r < 0) + return r; + + r = sd_event_source_set_prepare(bus->input_io_event_source, prepare_callback); + if (r < 0) + return r; + + r = sd_event_source_set_priority(bus->input_io_event_source, bus->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->input_io_event_source, "bus-input"); + } else + r = sd_event_source_set_io_fd(bus->input_io_event_source, bus->input_fd); + + if (r < 0) + return r; + + if (bus->output_fd != bus->input_fd) { + assert(bus->output_fd >= 0); + + if (!bus->output_io_event_source) { + r = sd_event_add_io(bus->event, &bus->output_io_event_source, bus->output_fd, 0, io_callback, bus); + if (r < 0) + return r; + + r = sd_event_source_set_priority(bus->output_io_event_source, bus->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->input_io_event_source, "bus-output"); + } else + r = sd_event_source_set_io_fd(bus->output_io_event_source, bus->output_fd); + + if (r < 0) + return r; + } + + return 0; +} + +static void detach_io_events(sd_bus *bus) { + assert(bus); + + if (bus->input_io_event_source) { + sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF); + bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source); + } + + if (bus->output_io_event_source) { + sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF); + bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source); + } +} + +_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { + int r; + + assert_return(bus, -EINVAL); + assert_return(!bus->event, -EBUSY); + + assert(!bus->input_io_event_source); + assert(!bus->output_io_event_source); + assert(!bus->time_event_source); + + if (event) + bus->event = sd_event_ref(event); + else { + r = sd_event_default(&bus->event); + if (r < 0) + return r; + } + + bus->event_priority = priority; + + r = sd_event_add_time(bus->event, &bus->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, bus); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(bus->time_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(bus->time_event_source, "bus-time"); + if (r < 0) + goto fail; + + r = sd_event_add_exit(bus->event, &bus->quit_event_source, quit_callback, bus); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(bus->quit_event_source, "bus-exit"); + if (r < 0) + goto fail; + + r = attach_io_events(bus); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_bus_detach_event(bus); + return r; +} + +_public_ int sd_bus_detach_event(sd_bus *bus) { + assert_return(bus, -EINVAL); + + if (!bus->event) + return 0; + + detach_io_events(bus); + + if (bus->time_event_source) { + sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF); + bus->time_event_source = sd_event_source_unref(bus->time_event_source); + } + + if (bus->quit_event_source) { + sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF); + bus->quit_event_source = sd_event_source_unref(bus->quit_event_source); + } + + bus->event = sd_event_unref(bus->event); + return 1; +} + +_public_ sd_event* sd_bus_get_event(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->event; +} + +_public_ sd_bus_message* sd_bus_get_current_message(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_message; +} + +_public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_slot; +} + +_public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_handler; +} + +_public_ void* sd_bus_get_current_userdata(sd_bus *bus) { + assert_return(bus, NULL); + + return bus->current_userdata; +} + +static int bus_default(int (*bus_open)(sd_bus **), sd_bus **default_bus, sd_bus **ret) { + sd_bus *b = NULL; + int r; + + assert(bus_open); + assert(default_bus); + + if (!ret) + return !!*default_bus; + + if (*default_bus) { + *ret = sd_bus_ref(*default_bus); + return 0; + } + + r = bus_open(&b); + if (r < 0) + return r; + + b->default_bus_ptr = default_bus; + b->tid = gettid(); + *default_bus = b; + + *ret = b; + return 1; +} + +_public_ int sd_bus_default_system(sd_bus **ret) { + return bus_default(sd_bus_open_system, &default_system_bus, ret); +} + + +_public_ int sd_bus_default_user(sd_bus **ret) { + return bus_default(sd_bus_open_user, &default_user_bus, ret); +} + +_public_ int sd_bus_default(sd_bus **ret) { + + const char *e; + + /* Let's try our best to reuse another cached connection. If + * the starter bus type is set, connect via our normal + * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that + * we can share the connection with the user/system default + * bus. */ + + e = secure_getenv("DBUS_STARTER_BUS_TYPE"); + if (e) { + if (streq(e, "system")) + return sd_bus_default_system(ret); + else if (STR_IN_SET(e, "user", "session")) + return sd_bus_default_user(ret); + } + + /* No type is specified, so we have not other option than to + * use the starter address if it is set. */ + + e = secure_getenv("DBUS_STARTER_ADDRESS"); + if (e) { + + return bus_default(sd_bus_open, &default_starter_bus, ret); + } + + /* Finally, if nothing is set use the cached connection for + * the right scope */ + + if (cg_pid_get_owner_uid(0, NULL) >= 0) + return sd_bus_default_user(ret); + else + return sd_bus_default_system(ret); +} + +_public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) { + assert_return(b, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!bus_pid_changed(b), -ECHILD); + + if (b->tid != 0) { + *tid = b->tid; + return 0; + } + + if (b->event) + return sd_event_get_tid(b->event, tid); + + return -ENXIO; +} + +_public_ int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path) { + _cleanup_free_ char *e = NULL; + char *ret; + + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(external_id, -EINVAL); + assert_return(ret_path, -EINVAL); + + e = bus_label_escape(external_id); + if (!e) + return -ENOMEM; + + ret = strjoin(prefix, "/", e, NULL); + if (!ret) + return -ENOMEM; + + *ret_path = ret; + return 0; +} + +_public_ int sd_bus_path_decode(const char *path, const char *prefix, char **external_id) { + const char *e; + char *ret; + + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(external_id, -EINVAL); + + e = object_path_startswith(path, prefix); + if (!e) { + *external_id = NULL; + return 0; + } + + ret = bus_label_unescape(e); + if (!ret) + return -ENOMEM; + + *external_id = ret; + 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; + + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->is_kernel) + return -EOPNOTSUPP; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->rqueue_size > 0) + return -EBUSY; + + if (bus->wqueue_size > 0) + return -EBUSY; + + r = bus_kernel_try_close(bus); + if (r < 0) + return r; + + sd_bus_close(bus); + return 0; +} + +_public_ int sd_bus_get_description(sd_bus *bus, const char **description) { + assert_return(bus, -EINVAL); + assert_return(description, -EINVAL); + assert_return(bus->description, -ENXIO); + assert_return(!bus_pid_changed(bus), -ECHILD); + + *description = bus->description; + return 0; +} + +int bus_get_root_path(sd_bus *bus) { + int r; + + if (bus->cgroup_root) + return 0; + + r = cg_get_root_path(&bus->cgroup_root); + if (r == -ENOENT) { + bus->cgroup_root = strdup("/"); + if (!bus->cgroup_root) + return -ENOMEM; + + r = 0; + } + + return r; +} + +_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { + int r; + + assert_return(bus, -EINVAL); + assert_return(scope, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->is_kernel) { + _cleanup_free_ char *n = NULL; + const char *dash; + + r = bus_kernel_get_bus_name(bus, &n); + if (r < 0) + return r; + + if (streq(n, "0-system")) { + *scope = "system"; + return 0; + } + + dash = strchr(n, '-'); + if (streq_ptr(dash, "-user")) { + *scope = "user"; + return 0; + } + } + + if (bus->is_user) { + *scope = "user"; + return 0; + } + + if (bus->is_system) { + *scope = "system"; + return 0; + } + + return -ENODATA; +} + +_public_ int sd_bus_get_address(sd_bus *bus, const char **address) { + + assert_return(bus, -EINVAL); + assert_return(address, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->address) { + *address = bus->address; + return 0; + } + + return -ENODATA; +} + +_public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { + assert_return(bus, -EINVAL); + assert_return(mask, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + *mask = bus->creds_mask; + return 0; +} + +_public_ int sd_bus_is_bus_client(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->bus_client; +} + +_public_ int sd_bus_is_server(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->is_server; +} + +_public_ int sd_bus_is_anonymous(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->anonymous_auth; +} + +_public_ int sd_bus_is_trusted(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->trusted; +} + +_public_ int sd_bus_is_monitor(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); +} + +static void flush_close(sd_bus *bus) { + if (!bus) + return; + + /* Flushes and closes the specified bus. We take a ref before, + * to ensure the flushing does not cause the bus to be + * unreferenced. */ + + sd_bus_flush_close_unref(sd_bus_ref(bus)); +} + +_public_ void sd_bus_default_flush_close(void) { + flush_close(default_starter_bus); + flush_close(default_user_bus); + flush_close(default_system_bus); +} diff --git a/src/libsystemd/src/sd-bus/test-bus-benchmark.c b/src/libsystemd/src/sd-bus/test-bus-benchmark.c new file mode 100644 index 0000000000..c8f0d11b84 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-benchmark.c @@ -0,0 +1,372 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/time-util.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-kernel.h" + +#define MAX_SIZE (2*1024*1024) + +static usec_t arg_loop_usec = 100 * USEC_PER_MSEC; + +typedef enum Type { + TYPE_KDBUS, + TYPE_LEGACY, + TYPE_DIRECT, +} Type; + +static void server(sd_bus *b, size_t *result) { + int r; + + for (;;) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_process(b, &m); + assert_se(r >= 0); + + if (r == 0) + assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0); + if (!m) + continue; + + if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping")) + assert_se(sd_bus_reply_method_return(m, NULL) >= 0); + else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) { + const void *p; + size_t sz; + + /* Make sure the mmap is mapped */ + assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) { + uint64_t res; + assert_se(sd_bus_message_read(m, "t", &res) > 0); + + *result = res; + return; + + } else if (!sd_bus_message_is_signal(m, NULL, NULL)) + assert_not_reached("Unknown method"); + } +} + +static void transaction(sd_bus *b, size_t sz, const char *server_name) { + _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); + assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0); + + memset(p, 0x80, sz); + + assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0); +} + +static void client_bisect(const char *address, const char *server_name) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; + size_t lsize, rsize, csize; + sd_bus *b; + int r; + + r = sd_bus_new(&b); + assert_se(r >= 0); + + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 0); + + lsize = 1; + rsize = MAX_SIZE; + + printf("SIZE\tCOPY\tMEMFD\n"); + + for (;;) { + usec_t t; + unsigned n_copying, n_memfd; + + csize = (lsize + rsize) / 2; + + if (csize <= lsize) + break; + + if (csize <= 0) + break; + + printf("%zu\t", csize); + + b->use_memfd = 0; + + t = now(CLOCK_MONOTONIC); + for (n_copying = 0;; n_copying++) { + transaction(b, csize, server_name); + if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) + break; + } + printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); + + b->use_memfd = -1; + + t = now(CLOCK_MONOTONIC); + for (n_memfd = 0;; n_memfd++) { + transaction(b, csize, server_name); + if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) + break; + } + printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec)); + + if (n_copying == n_memfd) + break; + + if (n_copying > n_memfd) + lsize = csize; + else + rsize = csize; + } + + b->use_memfd = 1; + assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); + assert_se(sd_bus_message_append(x, "t", csize) >= 0); + assert_se(sd_bus_send(b, x, NULL) >= 0); + + sd_bus_unref(b); +} + +static void client_chart(Type type, const char *address, const char *server_name, int fd) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL; + size_t csize; + sd_bus *b; + int r; + + r = sd_bus_new(&b); + assert_se(r >= 0); + + if (type == TYPE_DIRECT) { + r = sd_bus_set_fd(b, fd, fd); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } + + r = sd_bus_start(b); + assert_se(r >= 0); + + r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL); + assert_se(r >= 0); + + switch (type) { + case TYPE_KDBUS: + printf("SIZE\tCOPY\tMEMFD\n"); + break; + case TYPE_LEGACY: + printf("SIZE\tLEGACY\n"); + break; + case TYPE_DIRECT: + printf("SIZE\tDIRECT\n"); + break; + } + + for (csize = 1; csize <= MAX_SIZE; csize *= 2) { + usec_t t; + unsigned n_copying, n_memfd; + + printf("%zu\t", csize); + + if (type == TYPE_KDBUS) { + b->use_memfd = 0; + + t = now(CLOCK_MONOTONIC); + for (n_copying = 0;; n_copying++) { + transaction(b, csize, server_name); + if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) + break; + } + + printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec)); + + b->use_memfd = -1; + } + + t = now(CLOCK_MONOTONIC); + for (n_memfd = 0;; n_memfd++) { + transaction(b, csize, server_name); + if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec) + break; + } + + printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec)); + } + + b->use_memfd = 1; + assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0); + assert_se(sd_bus_message_append(x, "t", csize) >= 0); + assert_se(sd_bus_send(b, x, NULL) >= 0); + + sd_bus_unref(b); +} + +int main(int argc, char *argv[]) { + enum { + MODE_BISECT, + MODE_CHART, + } mode = MODE_BISECT; + Type type = TYPE_KDBUS; + int i, pair[2] = { -1, -1 }; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL; + _cleanup_close_ int bus_ref = -1; + const char *unique; + cpu_set_t cpuset; + size_t result; + sd_bus *b; + pid_t pid; + int r; + + for (i = 1; i < argc; i++) { + if (streq(argv[i], "chart")) { + mode = MODE_CHART; + continue; + } else if (streq(argv[i], "legacy")) { + type = TYPE_LEGACY; + continue; + } else if (streq(argv[i], "direct")) { + type = TYPE_DIRECT; + continue; + } + + assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0); + } + + assert_se(!MODE_BISECT || TYPE_KDBUS); + + assert_se(arg_loop_usec > 0); + + if (type == TYPE_KDBUS) { + assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + + bus_ref = bus_kernel_create_bus(name, false, &bus_name); + if (bus_ref == -ENOENT) + exit(EXIT_TEST_SKIP); + + assert_se(bus_ref >= 0); + + address = strappend("kernel:path=", bus_name); + assert_se(address); + } else if (type == TYPE_LEGACY) { + const char *e; + + e = secure_getenv("DBUS_SESSION_BUS_ADDRESS"); + assert_se(e); + + address = strdup(e); + assert_se(address); + } + + r = sd_bus_new(&b); + assert_se(r >= 0); + + if (type == TYPE_DIRECT) { + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); + + r = sd_bus_set_fd(b, pair[0], pair[0]); + assert_se(r >= 0); + + r = sd_bus_set_server(b, true, SD_ID128_NULL); + assert_se(r >= 0); + } else { + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_set_bus_client(b, true); + assert_se(r >= 0); + } + + r = sd_bus_start(b); + assert_se(r >= 0); + + if (type != TYPE_DIRECT) { + r = sd_bus_get_unique_name(b, &unique); + assert_se(r >= 0); + + server_name = strdup(unique); + assert_se(server_name); + } + + sync(); + setpriority(PRIO_PROCESS, 0, -19); + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); + + safe_close(bus_ref); + sd_bus_unref(b); + + switch (mode) { + case MODE_BISECT: + client_bisect(address, server_name); + break; + + case MODE_CHART: + client_chart(type, address, server_name, pair[1]); + break; + } + + _exit(0); + } + + CPU_ZERO(&cpuset); + CPU_SET(1, &cpuset); + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); + + server(b, &result); + + if (mode == MODE_BISECT) + printf("Copying/memfd are equally fast at %zu bytes\n", result); + + assert_se(waitpid(pid, NULL, 0) == pid); + + safe_close(pair[1]); + sd_bus_unref(b); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-chat.c b/src/libsystemd/src/sd-bus/test-bus-chat.c new file mode 100644 index 0000000000..8f3e88a7c0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-chat.c @@ -0,0 +1,561 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-match.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))); + return 0; +} + +static int object_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + int r; + + if (sd_bus_message_is_method_error(m, NULL)) + return 0; + + if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) { + log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m)); + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); + + return 1; + } + + return 0; +} + +static int server_init(sd_bus **_bus) { + sd_bus *bus = NULL; + sd_id128_t id; + int r; + const char *unique; + + assert_se(_bus); + + r = sd_bus_open_user(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to user bus: %m"); + goto fail; + } + + r = sd_bus_get_bus_id(bus, &id); + if (r < 0) { + log_error_errno(r, "Failed to get server ID: %m"); + goto fail; + } + + r = sd_bus_get_unique_name(bus, &unique); + if (r < 0) { + log_error_errno(r, "Failed to get unique name: %m"); + goto fail; + } + + log_info("Peer ID is " SD_ID128_FORMAT_STR ".", SD_ID128_FORMAT_VAL(id)); + log_info("Unique ID: %s", unique); + log_info("Can send file handles: %i", sd_bus_can_send(bus, 'h')); + + r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0); + if (r < 0) { + log_error_errno(r, "Failed to acquire name: %m"); + goto fail; + } + + r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add object: %m"); + goto fail; + } + + r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add match: %m"); + goto fail; + } + + r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); + if (r < 0) { + log_error_errno(r, "Failed to add match: %m"); + goto fail; + } + + bus_match_dump(&bus->match_callbacks, 0); + + *_bus = bus; + return 0; + +fail: + sd_bus_unref(bus); + return r; +} + +static int server(sd_bus *bus) { + int r; + bool client1_gone = false, client2_gone = false; + + while (!client1_gone || !client2_gone) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + pid_t pid = 0; + const char *label = NULL; + + r = sd_bus_process(bus, &m); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + goto fail; + } + + continue; + } + + if (!m) + continue; + + sd_bus_creds_get_pid(sd_bus_message_get_creds(m), &pid); + sd_bus_creds_get_selinux_context(sd_bus_message_get_creds(m), &label); + log_info("Got message! member=%s pid="PID_FMT" label=%s", + strna(sd_bus_message_get_member(m)), + pid, + strna(label)); + /* bus_message_dump(m); */ + /* sd_bus_message_rewind(m, true); */ + + if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) { + const char *hello; + _cleanup_free_ char *lowercase = NULL; + + r = sd_bus_message_read(m, "s", &hello); + if (r < 0) { + log_error_errno(r, "Failed to get parameter: %m"); + goto fail; + } + + lowercase = strdup(hello); + if (!lowercase) { + r = log_oom(); + goto fail; + } + + ascii_strlower(lowercase); + + r = sd_bus_reply_method_return(m, "s", lowercase); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) { + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + + client1_gone = true; + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient2")) { + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + + client2_gone = true; + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Slow")) { + + sleep(1); + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + + } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "FileDescriptor")) { + int fd; + static const char x = 'X'; + + r = sd_bus_message_read(m, "h", &fd); + if (r < 0) { + log_error_errno(r, "Failed to get parameter: %m"); + goto fail; + } + + log_info("Received fd=%d", fd); + + if (write(fd, &x, 1) < 0) { + log_error_errno(errno, "Failed to write to fd: %m"); + safe_close(fd); + goto fail; + } + + r = sd_bus_reply_method_return(m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + + } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { + + r = sd_bus_reply_method_error( + m, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + } + } + + r = 0; + +fail: + if (bus) { + sd_bus_flush(bus); + sd_bus_unref(bus); + } + + return r; +} + +static void* client1(void*p) { + _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 }; + char x; + + r = sd_bus_open_user(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to user bus: %m"); + goto finish; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "LowerCase", + &error, + &reply, + "s", + "HELLO"); + if (r < 0) { + log_error_errno(r, "Failed to issue method call: %m"); + goto finish; + } + + r = sd_bus_message_read(reply, "s", &hello); + if (r < 0) { + log_error_errno(r, "Failed to get string: %m"); + goto finish; + } + + assert_se(streq(hello, "hello")); + + if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) { + log_error_errno(errno, "Failed to allocate pipe: %m"); + r = -errno; + goto finish; + } + + log_info("Sending fd=%d", pp[1]); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "FileDescriptor", + &error, + NULL, + "h", + pp[1]); + if (r < 0) { + log_error_errno(r, "Failed to issue method call: %m"); + goto finish; + } + + errno = 0; + if (read(pp[0], &x, 1) <= 0) { + log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read"); + goto finish; + } + + r = 0; + +finish: + if (bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; + + r = sd_bus_message_new_method_call( + bus, + &q, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "ExitClient1"); + if (r < 0) + log_error_errno(r, "Failed to allocate method call: %m"); + else + sd_bus_send(bus, q, NULL); + + } + + return INT_TO_PTR(r); +} + +static int quit_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + bool *x = userdata; + + log_error("Quit callback: %s", strerror(sd_bus_message_get_errno(m))); + + *x = 1; + return 1; +} + +static void* client2(void*p) { + _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; + + r = sd_bus_open_user(&bus); + if (r < 0) { + log_error_errno(r, "Failed to connect to user bus: %m"); + goto finish; + } + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/foo/bar/waldo/piep", + "org.object.test", + "Foobar"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + r = sd_bus_send(bus, m, NULL); + if (r < 0) { + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + goto finish; + } + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_signal( + bus, + &m, + "/foobar", + "foo.bar", + "Notify"); + if (r < 0) { + log_error_errno(r, "Failed to allocate signal: %m"); + goto finish; + } + + r = sd_bus_send(bus, m, NULL); + if (r < 0) { + log_error("Failed to issue signal: %s", bus_error_message(&error, -r)); + goto finish; + } + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.DBus.Peer", + "GetMachineId"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + goto finish; + } + + r = sd_bus_message_read(reply, "s", &mid); + if (r < 0) { + log_error_errno(r, "Failed to parse machine ID: %m"); + goto finish; + } + + log_info("Machine ID is %s.", mid); + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Slow"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + reply = sd_bus_message_unref(reply); + + r = sd_bus_call(bus, m, 200 * USEC_PER_MSEC, &error, &reply); + if (r < 0) + log_info("Failed to issue method call: %s", bus_error_message(&error, -r)); + else + log_info("Slow call succeed."); + + m = sd_bus_message_unref(m); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Slow"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + r = sd_bus_call_async(bus, NULL, m, quit_callback, &quit, 200 * USEC_PER_MSEC); + if (r < 0) { + log_info("Failed to issue method call: %s", bus_error_message(&error, -r)); + goto finish; + } + + while (!quit) { + r = sd_bus_process(bus, NULL); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto finish; + } + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + goto finish; + } + } + } + + r = 0; + +finish: + if (bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *q; + + r = sd_bus_message_new_method_call( + bus, + &q, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "ExitClient2"); + if (r < 0) { + log_error_errno(r, "Failed to allocate method call: %m"); + goto finish; + } + + (void) sd_bus_send(bus, q, NULL); + } + + return INT_TO_PTR(r); +} + +int main(int argc, char *argv[]) { + pthread_t c1, c2; + sd_bus *bus; + void *p; + int q, r; + + r = server_init(&bus); + if (r < 0) { + log_info("Failed to connect to bus, skipping tests."); + return EXIT_TEST_SKIP; + } + + log_info("Initialized..."); + + r = pthread_create(&c1, NULL, client1, bus); + if (r != 0) + return EXIT_FAILURE; + + r = pthread_create(&c2, NULL, client2, bus); + if (r != 0) + return EXIT_FAILURE; + + r = server(bus); + + q = pthread_join(c1, &p); + if (q != 0) + return EXIT_FAILURE; + if (PTR_TO_INT(p) < 0) + return EXIT_FAILURE; + + q = pthread_join(c2, &p); + if (q != 0) + return EXIT_FAILURE; + if (PTR_TO_INT(p) < 0) + return EXIT_FAILURE; + + if (r < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-cleanup.c b/src/libsystemd/src/sd-bus/test-bus-cleanup.c new file mode 100644 index 0000000000..f4b9f96c03 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-cleanup.c @@ -0,0 +1,96 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/refcnt.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" +#include "bus-message.h" + +static void test_bus_new(void) { + _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + r = sd_bus_open_system(&bus); + if (r == -ECONNREFUSED || r == -ENOENT) + return r; + + assert_se(r >= 0); + printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref)); + + return 0; +} + +static void test_bus_new_method_call(void) { + sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + assert_se(sd_bus_open_system(&bus) >= 0); + + assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); + + printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->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_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + assert_se(sd_bus_open_system(&bus) >= 0); + + assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); + + printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->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) { + int r; + + log_parse_environment(); + log_open(); + + test_bus_new(); + r = test_bus_open(); + if (r < 0) { + log_info("Failed to connect to bus, skipping tests."); + return EXIT_TEST_SKIP; + } + + test_bus_new_method_call(); + test_bus_new_signal(); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-creds.c b/src/libsystemd/src/sd-bus/test-bus-creds.c new file mode 100644 index 0000000000..125d7db810 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-creds.c @@ -0,0 +1,51 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/cgroup-util.h" +#include "shared/bus-util.h" + +#include "bus-dump.h" + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + int r; + + if (cg_unified() == -ENOMEDIUM) { + puts("Skipping test: /sys/fs/cgroup/ not available"); + return EXIT_TEST_SKIP; + } + + r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); + assert_se(r >= 0); + + bus_creds_dump(creds, NULL, true); + + creds = sd_bus_creds_unref(creds); + + r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL); + if (r != -EACCES) { + assert_se(r >= 0); + putchar('\n'); + bus_creds_dump(creds, NULL, true); + } + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-error.c b/src/libsystemd/src/sd-bus/test-bus-error.c new file mode 100644 index 0000000000..2d0aa92c6c --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-error.c @@ -0,0 +1,233 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/errno-list.h" +#include "shared/bus-util.h" + +#include "bus-common-errors.h" +#include "bus-error.h" + +static void test_error(void) { + _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, + .message = "oh! no", + ._need_free = -1 + }; + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set(&error, SD_BUS_ERROR_NOT_SUPPORTED, "xxx") == -EOPNOTSUPP); + assert_se(streq(error.name, SD_BUS_ERROR_NOT_SUPPORTED)); + assert_se(streq(error.message, "xxx")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_NOT_SUPPORTED)); + assert_se(sd_bus_error_get_errno(&error) == EOPNOTSUPP); + 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")); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_get_errno(&error) == ENOENT); + assert_se(sd_bus_error_is_set(&error)); + + assert_se(!sd_bus_error_is_set(&second)); + assert_se(second._need_free == 0); + assert_se(error._need_free > 0); + assert_se(sd_bus_error_copy(&second, &error) == -ENOENT); + assert_se(second._need_free > 0); + assert_se(streq(error.name, second.name)); + assert_se(streq(error.message, second.message)); + assert_se(sd_bus_error_get_errno(&second) == ENOENT); + assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_NOT_FOUND)); + assert_se(sd_bus_error_is_set(&second)); + + sd_bus_error_free(&error); + sd_bus_error_free(&second); + + assert_se(!sd_bus_error_is_set(&second)); + assert_se(const_error._need_free == 0); + assert_se(sd_bus_error_copy(&second, &const_error) == -EEXIST); + assert_se(second._need_free == 0); + assert_se(streq(const_error.name, second.name)); + assert_se(streq(const_error.message, second.message)); + assert_se(sd_bus_error_get_errno(&second) == EEXIST); + assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_FILE_EXISTS)); + assert_se(sd_bus_error_is_set(&second)); + sd_bus_error_free(&second); + + assert_se(!sd_bus_error_is_set(&second)); + assert_se(temporarily_const_error._need_free < 0); + assert_se(sd_bus_error_copy(&second, &temporarily_const_error) == -EACCES); + assert_se(second._need_free > 0); + assert_se(streq(temporarily_const_error.name, second.name)); + assert_se(streq(temporarily_const_error.message, second.message)); + assert_se(sd_bus_error_get_errno(&second) == EACCES); + assert_se(sd_bus_error_has_name(&second, SD_BUS_ERROR_ACCESS_DENIED)); + assert_se(sd_bus_error_is_set(&second)); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_const(&error, "System.Error.EUCLEAN", "Hallo") == -EUCLEAN); + assert_se(streq(error.name, "System.Error.EUCLEAN")); + assert_se(streq(error.message, "Hallo")); + assert_se(sd_bus_error_has_name(&error, "System.Error.EUCLEAN")); + assert_se(sd_bus_error_get_errno(&error) == EUCLEAN); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY); + assert_se(streq(error.name, "System.Error.EBUSY")); + assert_se(streq(error.message, strerror(EBUSY))); + assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY")); + assert_se(sd_bus_error_get_errno(&error) == EBUSY); + assert_se(sd_bus_error_is_set(&error)); + sd_bus_error_free(&error); + + assert_se(!sd_bus_error_is_set(&error)); + assert_se(sd_bus_error_set_errnof(&error, EIO, "Waldi %c", 'X') == -EIO); + assert_se(streq(error.name, SD_BUS_ERROR_IO_ERROR)); + assert_se(streq(error.message, "Waldi X")); + 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[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; + +static void dump_mapping_table(void) { + const sd_bus_error_map *m; + + printf("----- errno mappings ------\n"); + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); + m++; + } + printf("---------------------------\n"); +} + +static void test_errno_mapping_standard(void) { + assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); + assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); + assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); + assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); +} + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), + SD_BUS_ERROR_MAP_END +}; + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors3[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors4[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), + 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); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); + + assert_se(sd_bus_error_add_map(test_errors3) > 0); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); + assert_se(sd_bus_error_add_map(test_errors4) > 0); + assert_se(sd_bus_error_add_map(test_errors4) == 0); + assert_se(sd_bus_error_add_map(test_errors3) == 0); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + 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[]) { + dump_mapping_table(); + + test_error(); + test_errno_mapping_standard(); + test_errno_mapping_custom(); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-gvariant.c b/src/libsystemd/src/sd-bus/test-bus-gvariant.c new file mode 100644 index 0000000000..5fdf8e661c --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-gvariant.c @@ -0,0 +1,225 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifdef HAVE_GLIB +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-dump.h" +#include "bus-gvariant.h" +#include "bus-internal.h" +#include "bus-message.h" + +static void test_bus_gvariant_is_fixed_size(void) { + assert_se(bus_gvariant_is_fixed_size("") > 0); + assert_se(bus_gvariant_is_fixed_size("()") > 0); + assert_se(bus_gvariant_is_fixed_size("y") > 0); + assert_se(bus_gvariant_is_fixed_size("u") > 0); + assert_se(bus_gvariant_is_fixed_size("b") > 0); + assert_se(bus_gvariant_is_fixed_size("n") > 0); + assert_se(bus_gvariant_is_fixed_size("q") > 0); + assert_se(bus_gvariant_is_fixed_size("i") > 0); + assert_se(bus_gvariant_is_fixed_size("t") > 0); + assert_se(bus_gvariant_is_fixed_size("d") > 0); + assert_se(bus_gvariant_is_fixed_size("s") == 0); + assert_se(bus_gvariant_is_fixed_size("o") == 0); + assert_se(bus_gvariant_is_fixed_size("g") == 0); + assert_se(bus_gvariant_is_fixed_size("h") > 0); + assert_se(bus_gvariant_is_fixed_size("ay") == 0); + assert_se(bus_gvariant_is_fixed_size("v") == 0); + assert_se(bus_gvariant_is_fixed_size("(u)") > 0); + assert_se(bus_gvariant_is_fixed_size("(uuuuy)") > 0); + assert_se(bus_gvariant_is_fixed_size("(uusuuy)") == 0); + assert_se(bus_gvariant_is_fixed_size("a{ss}") == 0); + assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0); + assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0); +} + +static void test_bus_gvariant_get_size(void) { + assert_se(bus_gvariant_get_size("") == 0); + assert_se(bus_gvariant_get_size("()") == 1); + assert_se(bus_gvariant_get_size("y") == 1); + assert_se(bus_gvariant_get_size("u") == 4); + assert_se(bus_gvariant_get_size("b") == 1); + assert_se(bus_gvariant_get_size("n") == 2); + assert_se(bus_gvariant_get_size("q") == 2); + assert_se(bus_gvariant_get_size("i") == 4); + assert_se(bus_gvariant_get_size("t") == 8); + assert_se(bus_gvariant_get_size("d") == 8); + assert_se(bus_gvariant_get_size("s") < 0); + assert_se(bus_gvariant_get_size("o") < 0); + assert_se(bus_gvariant_get_size("g") < 0); + assert_se(bus_gvariant_get_size("h") == 4); + assert_se(bus_gvariant_get_size("ay") < 0); + assert_se(bus_gvariant_get_size("v") < 0); + assert_se(bus_gvariant_get_size("(u)") == 4); + assert_se(bus_gvariant_get_size("(uuuuy)") == 20); + assert_se(bus_gvariant_get_size("(uusuuy)") < 0); + assert_se(bus_gvariant_get_size("a{ss}") < 0); + assert_se(bus_gvariant_get_size("((u)yyy(b(iiii)))") == 28); + assert_se(bus_gvariant_get_size("((u)yyy(b(iiivi)))") < 0); + assert_se(bus_gvariant_get_size("((b)(t))") == 16); + assert_se(bus_gvariant_get_size("((b)(b)(t))") == 16); + assert_se(bus_gvariant_get_size("(bt)") == 16); + assert_se(bus_gvariant_get_size("((t)(b))") == 16); + assert_se(bus_gvariant_get_size("(tb)") == 16); + assert_se(bus_gvariant_get_size("((b)(b))") == 2); + assert_se(bus_gvariant_get_size("((t)(t))") == 16); +} + +static void test_bus_gvariant_get_alignment(void) { + assert_se(bus_gvariant_get_alignment("") == 1); + assert_se(bus_gvariant_get_alignment("()") == 1); + assert_se(bus_gvariant_get_alignment("y") == 1); + assert_se(bus_gvariant_get_alignment("b") == 1); + assert_se(bus_gvariant_get_alignment("u") == 4); + assert_se(bus_gvariant_get_alignment("s") == 1); + assert_se(bus_gvariant_get_alignment("o") == 1); + assert_se(bus_gvariant_get_alignment("g") == 1); + assert_se(bus_gvariant_get_alignment("v") == 8); + assert_se(bus_gvariant_get_alignment("h") == 4); + assert_se(bus_gvariant_get_alignment("i") == 4); + assert_se(bus_gvariant_get_alignment("t") == 8); + assert_se(bus_gvariant_get_alignment("x") == 8); + assert_se(bus_gvariant_get_alignment("q") == 2); + assert_se(bus_gvariant_get_alignment("n") == 2); + assert_se(bus_gvariant_get_alignment("d") == 8); + assert_se(bus_gvariant_get_alignment("ay") == 1); + assert_se(bus_gvariant_get_alignment("as") == 1); + assert_se(bus_gvariant_get_alignment("au") == 4); + assert_se(bus_gvariant_get_alignment("an") == 2); + assert_se(bus_gvariant_get_alignment("ans") == 2); + assert_se(bus_gvariant_get_alignment("ant") == 8); + assert_se(bus_gvariant_get_alignment("(ss)") == 1); + assert_se(bus_gvariant_get_alignment("(ssu)") == 4); + assert_se(bus_gvariant_get_alignment("a(ssu)") == 4); + assert_se(bus_gvariant_get_alignment("(u)") == 4); + assert_se(bus_gvariant_get_alignment("(uuuuy)") == 4); + assert_se(bus_gvariant_get_alignment("(uusuuy)") == 4); + assert_se(bus_gvariant_get_alignment("a{ss}") == 1); + assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiii)))") == 4); + assert_se(bus_gvariant_get_alignment("((u)yyy(b(iiivi)))") == 8); + assert_se(bus_gvariant_get_alignment("((b)(t))") == 8); + assert_se(bus_gvariant_get_alignment("((b)(b)(t))") == 8); + assert_se(bus_gvariant_get_alignment("(bt)") == 8); + assert_se(bus_gvariant_get_alignment("((t)(b))") == 8); + assert_se(bus_gvariant_get_alignment("(tb)") == 8); + assert_se(bus_gvariant_get_alignment("((b)(b))") == 1); + assert_se(bus_gvariant_get_alignment("((t)(t))") == 8); +} + +static void test_marshal(void) { + _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; + + r = sd_bus_open_system(&bus); + if (r < 0) + exit(EXIT_TEST_SKIP); + + bus->message_version = 2; /* dirty hack to enable gvariant */ + + assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName") >= 0); + + assert_cc(sizeof(struct bus_header) == 16); + + assert_se(sd_bus_message_append(m, + "a(usv)", 3, + 4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111, + 4712, "second-string-parameter", "(a(si))", 2, "Y", 5, "Z", 6, + 4713, "third-string-parameter", "(uu)", 1, 2) >= 0); + + assert_se(bus_message_seal(m, 4711, 0) >= 0); + +#ifdef HAVE_GLIB + { + GVariant *v; + char *t; + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif + + v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv})"), m->header, sizeof(struct bus_header) + m->fields_size, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + t = g_variant_print(v, TRUE); + printf("%s\n", t); + g_free(t); + g_variant_unref(v); + + v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, m->user_body_size, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + t = g_variant_print(v, TRUE); + printf("%s\n", t); + g_free(t); + g_variant_unref(v); + } +#endif + + assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); + + assert_se(bus_message_get_blob(m, &blob, &sz) >= 0); + +#ifdef HAVE_GLIB + { + GVariant *v; + char *t; + + v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv}v)"), blob, sz, false, NULL, NULL); + assert_se(g_variant_is_normal_form(v)); + t = g_variant_print(v, TRUE); + printf("%s\n", t); + g_free(t); + g_variant_unref(v); + } +#endif + + assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0); + blob = NULL; + + assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); + + m = sd_bus_message_unref(m); + + assert_se(sd_bus_message_new_method_call(bus, &m, "a.x", "/a/x", "a.x", "Ax") >= 0); + + assert_se(sd_bus_message_append(m, "as", 0) >= 0); + + assert_se(bus_message_seal(m, 4712, 0) >= 0); + assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); +} + +int main(int argc, char *argv[]) { + + test_bus_gvariant_is_fixed_size(); + test_bus_gvariant_get_size(); + test_bus_gvariant_get_alignment(); + test_marshal(); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-introspect.c b/src/libsystemd/src/sd-bus/test-bus-introspect.c new file mode 100644 index 0000000000..517e3668ae --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-introspect.c @@ -0,0 +1,64 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/log.h" + +#include "bus-introspect.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; +} + +static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return -EINVAL; +} + +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Hello", "ssas", "a(uu)", NULL, 0), + SD_BUS_METHOD("DeprecatedHello", "", "", NULL, SD_BUS_VTABLE_DEPRECATED), + SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", NULL, SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY), + SD_BUS_SIGNAL("Wowza", "sss", 0), + SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED), + SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0), + SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED), + SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("Constant", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EXPLICIT), + SD_BUS_VTABLE_END +}; + +int main(int argc, char *argv[]) { + struct introspect intro; + + log_set_max_level(LOG_DEBUG); + + assert_se(introspect_begin(&intro, false) >= 0); + + fprintf(intro.f, " \n"); + assert_se(introspect_write_interface(&intro, vtable) >= 0); + fputs(" \n", intro.f); + + fflush(intro.f); + fputs(intro.introspection, stdout); + + introspect_free(&intro); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c new file mode 100644 index 0000000000..cb8c41b792 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-kernel-bloom.c @@ -0,0 +1,142 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-kernel.h" + +static int test_match(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + int *found = userdata; + + *found = 1; + + return 0; +} + +static void test_one( + const char *path, + const char *interface, + const char *member, + bool as_list, + const char *arg0, + const char *match, + bool good) { + + _cleanup_close_ int bus_ref = -1; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + sd_bus *a, *b; + int r, found = 0; + + assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + + bus_ref = bus_kernel_create_bus(name, false, &bus_name); + if (bus_ref == -ENOENT) + exit(EXIT_TEST_SKIP); + + assert_se(bus_ref >= 0); + + address = strappend("kernel:path=", bus_name); + assert_se(address); + + r = sd_bus_new(&a); + assert_se(r >= 0); + + r = sd_bus_new(&b); + assert_se(r >= 0); + + r = sd_bus_set_address(a, address); + assert_se(r >= 0); + + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_start(a); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + log_debug("match"); + r = sd_bus_add_match(b, NULL, match, test_match, &found); + assert_se(r >= 0); + + log_debug("signal"); + + if (as_list) + r = sd_bus_emit_signal(a, path, interface, member, "as", 1, arg0); + else + r = sd_bus_emit_signal(a, path, interface, member, "s", arg0); + assert_se(r >= 0); + + r = sd_bus_process(b, &m); + assert_se(r >= 0 && good == !!found); + + sd_bus_unref(a); + sd_bus_unref(b); +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/tuut'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "interface='waldo.com'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Piep'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/quux'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/quux'", false); + test_one("/", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/'", true); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/bar/waldo", "arg0path='/foo/'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo", "arg0path='/foo/bar/waldo'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "/foo/", "arg0path='/foo/bar/waldo'", true); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-kernel.c b/src/libsystemd/src/sd-bus/test-bus-kernel.c new file mode 100644 index 0000000000..871bf2e62c --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-kernel.c @@ -0,0 +1,191 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-dump.h" +#include "bus-kernel.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_(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]; + const char *nn; + + log_set_max_level(LOG_DEBUG); + + assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + + bus_ref = bus_kernel_create_bus(name, false, &bus_name); + if (bus_ref == -ENOENT) + return EXIT_TEST_SKIP; + + assert_se(bus_ref >= 0); + + address = strappend("kernel:path=", bus_name); + assert_se(address); + + r = sd_bus_new(&a); + assert_se(r >= 0); + + r = sd_bus_new(&b); + assert_se(r >= 0); + + r = sd_bus_set_description(a, "a"); + assert_se(r >= 0); + + r = sd_bus_set_address(a, address); + assert_se(r >= 0); + + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + assert_se(sd_bus_negotiate_timestamp(a, 1) >= 0); + assert_se(sd_bus_negotiate_creds(a, true, _SD_BUS_CREDS_ALL) >= 0); + + assert_se(sd_bus_negotiate_timestamp(b, 0) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, 0) >= 0); + + r = sd_bus_start(a); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, _SD_BUS_CREDS_ALL) >= 0); + + r = sd_bus_get_unique_name(a, &ua); + assert_se(r >= 0); + printf("unique a: %s\n", ua); + + r = sd_bus_get_description(a, &nn); + assert_se(r >= 0); + printf("name of a: %s\n", nn); + + r = sd_bus_get_unique_name(b, &ub); + assert_se(r >= 0); + printf("unique b: %s\n", ub); + + r = sd_bus_get_description(b, &nn); + assert_se(r >= 0); + printf("name of b: %s\n", nn); + + assert_se(bus_kernel_get_bus_name(b, &bname) >= 0); + assert_se(endswith(bname, name)); + + r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay"); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN)); + assert_se(r == -EHOSTUNREACH); + + r = sd_bus_add_match(b, NULL, "interface='waldo.com',member='Piep'", NULL, NULL); + assert_se(r >= 0); + + r = sd_bus_emit_signal(a, "/foo/bar/waldo", "waldo.com", "Piep", "sss", "I am a string", "/this/is/a/path", "and.this.a.domain.name"); + assert_se(r >= 0); + + r = sd_bus_try_close(b); + assert_se(r == -EBUSY); + + r = sd_bus_process_priority(b, -10, &m); + assert_se(r == 0); + + r = sd_bus_process(b, &m); + assert_se(r > 0); + assert_se(m); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + assert_se(sd_bus_message_rewind(m, true) >= 0); + + r = sd_bus_message_read(m, "s", &the_string); + assert_se(r >= 0); + assert_se(streq(the_string, "I am a string")); + + sd_bus_message_unref(m); + m = NULL; + + r = sd_bus_request_name(a, "net.x0pointer.foobar", 0); + assert_se(r >= 0); + + r = sd_bus_message_new_method_call(b, &m, "net.x0pointer.foobar", "/a/path", "an.inter.face", "AMethod"); + assert_se(r >= 0); + + assert_se(pipe2(pipe_fds, O_CLOEXEC) >= 0); + + assert_se(write(pipe_fds[1], "x", 1) == 1); + + pipe_fds[1] = safe_close(pipe_fds[1]); + + r = sd_bus_message_append(m, "h", pipe_fds[0]); + assert_se(r >= 0); + + pipe_fds[0] = safe_close(pipe_fds[0]); + + r = sd_bus_send(b, m, NULL); + assert_se(r >= 0); + + for (;;) { + sd_bus_message_unref(m); + m = NULL; + r = sd_bus_process(a, &m); + assert_se(r > 0); + assert_se(m); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + assert_se(sd_bus_message_rewind(m, true) >= 0); + + if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) { + int fd; + char x; + + r = sd_bus_message_read(m, "h", &fd); + assert_se(r >= 0); + + assert_se(read(fd, &x, 1) == 1); + assert_se(x == 'x'); + break; + } + } + + r = sd_bus_release_name(a, "net.x0pointer.foobar"); + assert_se(r >= 0); + + r = sd_bus_release_name(a, "net.x0pointer.foobar"); + assert_se(r == -ESRCH); + + r = sd_bus_try_close(a); + assert_se(r >= 0); + + sd_bus_unref(a); + sd_bus_unref(b); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-marshal.c b/src/libsystemd/src/sd-bus/test-bus-marshal.c new file mode 100644 index 0000000000..4d8f22b042 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-marshal.c @@ -0,0 +1,433 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#ifdef HAVE_GLIB +#include +#endif + +#ifdef HAVE_DBUS +#include +#endif + +#include + +#include "basic/alloc-util.h" +#include "basic/bus-label.h" +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/log.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-dump.h" +#include "bus-message.h" + +static void test_bus_path_encode_unique(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL; + + assert_se(bus_path_encode_unique(NULL, "/foo/bar", "some.sender", "a.suffix", &a) >= 0 && streq_ptr(a, "/foo/bar/some_2esender/a_2esuffix")); + assert_se(bus_path_decode_unique(a, "/foo/bar", &b, &c) > 0 && streq_ptr(b, "some.sender") && streq_ptr(c, "a.suffix")); + assert_se(bus_path_decode_unique(a, "/bar/foo", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/onlyOneSuffix", "/foo/bar", &d, &d) == 0 && !d); + assert_se(bus_path_decode_unique("/foo/bar/_/_", "/foo/bar", &d, &e) > 0 && streq_ptr(d, "") && streq_ptr(e, "")); +} + +static void test_bus_path_encode(void) { + _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL; + + assert_se(sd_bus_path_encode("/foo/bar", "waldo", &a) >= 0 && streq(a, "/foo/bar/waldo")); + assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL); + assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo")); + + assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0); + assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0); + + assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_")); + assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, "")); + + assert_se(sd_bus_path_encode("/foo/bar", "foo.bar", &e) >= 0 && streq(e, "/foo/bar/foo_2ebar")); + 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; + + assert_se(t = bus_label_escape(a)); + assert_se(streq(t, b)); + + assert_se(x = bus_label_unescape(t)); + assert_se(streq(a, x)); + + assert_se(y = bus_label_unescape(b)); + assert_se(streq(a, y)); +} + +static void test_bus_label_escape(void) { + test_bus_label_escape_one("foo123bar", "foo123bar"); + test_bus_label_escape_one("foo.bar", "foo_2ebar"); + test_bus_label_escape_one("foo_2ebar", "foo_5f2ebar"); + test_bus_label_escape_one("", "_"); + test_bus_label_escape_one("_", "_5f"); + test_bus_label_escape_one("1", "_31"); + test_bus_label_escape_one(":1", "_3a1"); +} + +int main(int argc, char *argv[]) { + _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; + void *buffer = NULL; + size_t sz; + char *h; + const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array; + char *s; + _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_(sd_bus_unrefp) sd_bus *bus = NULL; + double dbl; + uint64_t u64; + + r = sd_bus_default_system(&bus); + if (r < 0) + return EXIT_TEST_SKIP; + + r = sd_bus_message_new_method_call(bus, &m, "foobar.waldo", "/", "foobar.waldo", "Piep"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, ""); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "s", "a string"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "s", NULL); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "asg", 2, "string #1", "string #2", "sba(tt)ss"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "sass", "foobar", 5, "foo", "bar", "waldo", "piep", "pap", "after"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "a{yv}", 2, 3, "s", "foo", 5, "s", "waldo"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "y(ty)y(yt)y", 8, 777ULL, 7, 9, 77, 7777ULL, 10); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "()"); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3"); + assert_se(r >= 0); + + r = sd_bus_message_open_container(m, 'a', "s"); + assert_se(r >= 0); + + r = sd_bus_message_append_basic(m, 's', "foobar"); + assert_se(r >= 0); + + r = sd_bus_message_append_basic(m, 's', "waldo"); + assert_se(r >= 0); + + r = sd_bus_message_close_container(m); + assert_se(r >= 0); + + r = sd_bus_message_append_string_space(m, 5, &s); + assert_se(r >= 0); + strcpy(s, "hallo"); + + r = sd_bus_message_append_array(m, 'i', integer_array, sizeof(integer_array)); + assert_se(r >= 0); + + r = sd_bus_message_append_array(m, 'u', NULL, 0); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "a(stdo)", 1, "foo", 815ULL, 47.0, "/"); + assert_se(r >= 0); + + r = bus_message_seal(m, 4711, 0); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + ms = open_memstream(&first, &first_size); + bus_message_dump(m, ms, 0); + fflush(ms); + assert_se(!ferror(ms)); + + r = bus_message_get_blob(m, &buffer, &sz); + assert_se(r >= 0); + + h = hexmem(buffer, sz); + assert_se(h); + + log_info("message size = %zu, contents =\n%s", sz, h); + free(h); + +#ifdef HAVE_GLIB + { + GDBusMessage *g; + char *p; + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif + + g = g_dbus_message_new_from_blob(buffer, sz, 0, NULL); + p = g_dbus_message_print(g, 0); + log_info("%s", p); + g_free(p); + g_object_unref(g); + } +#endif + +#ifdef HAVE_DBUS + { + DBusMessage *w; + DBusError error; + + dbus_error_init(&error); + + w = dbus_message_demarshal(buffer, sz, &error); + if (!w) + log_error("%s", error.message); + else + dbus_message_unref(w); + + dbus_error_free(&error); + } +#endif + + m = sd_bus_message_unref(m); + + r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + fclose(ms); + ms = open_memstream(&second, &second_size); + bus_message_dump(m, ms, 0); + fflush(ms); + assert_se(!ferror(ms)); + assert_se(first_size == second_size); + assert_se(memcmp(first, second, first_size) == 0); + + assert_se(sd_bus_message_rewind(m, true) >= 0); + + r = sd_bus_message_read(m, "ssasg", &x, &x2, 2, &y, &z, &a_signature); + assert_se(r > 0); + assert_se(streq(x, "a string")); + assert_se(streq(x2, "")); + assert_se(streq(y, "string #1")); + assert_se(streq(z, "string #2")); + assert_se(streq(a_signature, "sba(tt)ss")); + + r = sd_bus_message_read(m, "sass", &x, 5, &y, &z, &a, &b, &c, &d); + assert_se(r > 0); + assert_se(streq(x, "foobar")); + assert_se(streq(y, "foo")); + assert_se(streq(z, "bar")); + assert_se(streq(a, "waldo")); + assert_se(streq(b, "piep")); + assert_se(streq(c, "pap")); + assert_se(streq(d, "after")); + + r = sd_bus_message_read(m, "a{yv}", 2, &u, "s", &x, &v, "s", &y); + assert_se(r > 0); + assert_se(u == 3); + assert_se(streq(x, "foo")); + assert_se(v == 5); + assert_se(streq(y, "waldo")); + + r = sd_bus_message_read(m, "y(ty)", &v, &u64, &u); + assert_se(r > 0); + assert_se(v == 8); + assert_se(u64 == 777); + assert_se(u == 7); + + r = sd_bus_message_read(m, "y(yt)", &v, &u, &u64); + assert_se(r > 0); + assert_se(v == 9); + assert_se(u == 77); + assert_se(u64 == 7777); + + r = sd_bus_message_read(m, "y", &v); + assert_se(r > 0); + assert_se(v == 10); + + r = sd_bus_message_read(m, "()"); + assert_se(r > 0); + + r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d); + assert_se(r > 0); + assert_se(boolean); + assert_se(streq(x, "aaa")); + assert_se(streq(y, "1")); + assert_se(streq(a, "bbb")); + assert_se(streq(b, "2")); + assert_se(streq(c, "ccc")); + assert_se(streq(d, "3")); + + assert_se(sd_bus_message_verify_type(m, 'a', "s") > 0); + + r = sd_bus_message_read(m, "as", 2, &x, &y); + assert_se(r > 0); + assert_se(streq(x, "foobar")); + assert_se(streq(y, "waldo")); + + r = sd_bus_message_read_basic(m, 's', &s); + assert_se(r > 0); + assert_se(streq(s, "hallo")); + + r = sd_bus_message_read_array(m, 'i', (const void**) &return_array, &sz); + assert_se(r > 0); + assert_se(sz == sizeof(integer_array)); + assert_se(memcmp(integer_array, return_array, sz) == 0); + + r = sd_bus_message_read_array(m, 'u', (const void**) &return_array, &sz); + assert_se(r > 0); + assert_se(sz == 0); + + r = sd_bus_message_read(m, "a(stdo)", 1, &x, &u64, &dbl, &y); + assert_se(r > 0); + assert_se(streq(x, "foo")); + assert_se(u64 == 815ULL); + assert_se(fabs(dbl - 47.0) < 0.1); + assert_se(streq(y, "/")); + + r = sd_bus_message_peek_type(m, NULL, NULL); + assert_se(r == 0); + + r = sd_bus_message_new_method_call(bus, ©, "foobar.waldo", "/", "foobar.waldo", "Piep"); + assert_se(r >= 0); + + r = sd_bus_message_rewind(m, true); + assert_se(r >= 0); + + r = sd_bus_message_copy(copy, m, true); + assert_se(r >= 0); + + r = bus_message_seal(copy, 4712, 0); + assert_se(r >= 0); + + fclose(ms); + ms = open_memstream(&third, &third_size); + bus_message_dump(copy, ms, 0); + fflush(ms); + assert_se(!ferror(ms)); + + printf("<%.*s>\n", (int) first_size, first); + printf("<%.*s>\n", (int) third_size, third); + + assert_se(first_size == third_size); + assert_se(memcmp(first, third, third_size) == 0); + + r = sd_bus_message_rewind(m, true); + assert_se(r >= 0); + + assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0); + + r = sd_bus_message_skip(m, "ssasg"); + assert_se(r > 0); + + assert_se(sd_bus_message_verify_type(m, 's', NULL) > 0); + + r = sd_bus_message_skip(m, "sass"); + assert_se(r >= 0); + + assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0); + + r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y()"); + assert_se(r >= 0); + + assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0); + + r = sd_bus_message_read(m, "b", &boolean); + assert_se(r > 0); + assert_se(boolean); + + r = sd_bus_message_enter_container(m, 0, NULL); + assert_se(r > 0); + + r = sd_bus_message_read(m, "(ss)", &x, &y); + assert_se(r > 0); + + r = sd_bus_message_read(m, "(ss)", &a, &b); + assert_se(r > 0); + + r = sd_bus_message_read(m, "(ss)", &c, &d); + assert_se(r > 0); + + r = sd_bus_message_read(m, "(ss)", &x, &y); + assert_se(r == 0); + + r = sd_bus_message_exit_container(m); + assert_se(r >= 0); + + assert_se(streq(x, "aaa")); + assert_se(streq(y, "1")); + assert_se(streq(a, "bbb")); + assert_se(streq(b, "2")); + assert_se(streq(c, "ccc")); + assert_se(streq(d, "3")); + + 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/src/sd-bus/test-bus-match.c b/src/libsystemd/src/sd-bus/test-bus-match.c new file mode 100644 index 0000000000..e50cebcdc6 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-match.c @@ -0,0 +1,160 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/log.h" +#include "basic/macro.h" +#include "shared/bus-util.h" + +#include "bus-match.h" +#include "bus-message.h" +#include "bus-slot.h" + +static bool mask[32]; + +static int filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Ran %u", PTR_TO_UINT(userdata)); + assert_se(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); + mask[PTR_TO_UINT(userdata)] = true; + return 0; +} + +static bool mask_contains(unsigned a[], unsigned n) { + unsigned i, j; + + for (i = 0; i < ELEMENTSOF(mask); i++) { + bool found = false; + + for (j = 0; j < n; j++) + if (a[j] == i) { + found = true; + break; + } + + if (found != mask[i]) + return false; + } + + return true; +} + +static int match_add(sd_bus_slot *slots, struct bus_match_node *root, const char *match, int value) { + struct bus_match_component *components = NULL; + unsigned n_components = 0; + sd_bus_slot *s; + int r; + + s = slots + value; + zero(*s); + + r = bus_match_parse(match, &components, &n_components); + if (r < 0) + return r; + + s->userdata = INT_TO_PTR(value); + s->match_callback.callback = filter; + + r = bus_match_add(root, components, n_components, &s->match_callback); + bus_match_parse_free(components, n_components); + + return r; +} + +static void test_match_scope(const char *match, enum bus_match_scope scope) { + struct bus_match_component *components = NULL; + unsigned n_components = 0; + + assert_se(bus_match_parse(match, &components, &n_components) >= 0); + assert_se(bus_match_get_scope(components, n_components) == scope); + bus_match_parse_free(components, n_components); +} + +int main(int argc, char *argv[]) { + struct bus_match_node root = { + .type = BUS_MATCH_ROOT, + }; + + _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; + + r = sd_bus_open_system(&bus); + if (r < 0) + return EXIT_TEST_SKIP; + + assert_se(match_add(slots, &root, "arg2='wal\\'do',sender='foo',type='signal',interface='bar.x',", 1) >= 0); + assert_se(match_add(slots, &root, "arg2='wal\\'do2',sender='foo',type='signal',interface='bar.x',", 2) >= 0); + assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='signal',interface='bar.x',", 3) >= 0); + assert_se(match_add(slots, &root, "arg3='test',sender='foo',type='method_call',interface='bar.x',", 4) >= 0); + assert_se(match_add(slots, &root, "", 5) >= 0); + assert_se(match_add(slots, &root, "interface='quux.x'", 6) >= 0); + assert_se(match_add(slots, &root, "interface='bar.x'", 7) >= 0); + assert_se(match_add(slots, &root, "member='waldo',path='/foo/bar'", 8) >= 0); + assert_se(match_add(slots, &root, "path='/foo/bar'", 9) >= 0); + assert_se(match_add(slots, &root, "path_namespace='/foo'", 10) >= 0); + assert_se(match_add(slots, &root, "path_namespace='/foo/quux'", 11) >= 0); + assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); + assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); + assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); + assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0); + assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0); + assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0); + assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0); + + bus_match_dump(&root, 0); + + assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0); + assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 0); + assert_se(bus_message_seal(m, 1, 0) >= 0); + + zero(mask); + assert_se(bus_match_run(NULL, &root, m) == 0); + assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14, 15, 16, 17 }, 11)); + + assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0); + assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 0); + + bus_match_dump(&root, 0); + + zero(mask); + assert_se(bus_match_run(NULL, &root, m) == 0); + assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7, 15, 16, 17 }, 9)); + + for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) { + char buf[32]; + const char *x; + + assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf))); + + if (i >= BUS_MATCH_MESSAGE_TYPE) + assert_se(bus_match_node_type_from_string(x, strlen(x)) == i); + } + + bus_match_free(&root); + + test_match_scope("interface='foobar'", BUS_MATCH_GENERIC); + test_match_scope("", BUS_MATCH_GENERIC); + test_match_scope("interface='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); + test_match_scope("sender='org.freedesktop.DBus.Local'", BUS_MATCH_LOCAL); + test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL); + test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-objects.c b/src/libsystemd/src/sd-bus/test-bus-objects.c new file mode 100644 index 0000000000..1c6e28e76e --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-objects.c @@ -0,0 +1,556 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-dump.h" +#include "bus-internal.h" +#include "bus-message.h" + +struct context { + int fds[2]; + bool quit; + char *something; + char *automatic_string_property; + uint32_t automatic_integer_property; +}; + +static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { + struct context *c = userdata; + const char *s; + char *n = NULL; + int r; + + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + + n = strjoin("<<<", s, ">>>", NULL); + assert_se(n); + + free(c->something); + c->something = n; + + log_info("AlterSomething() called, got %s, returning %s", s, n); + + /* This should fail, since the return type doesn't match */ + assert_se(sd_bus_reply_method_return(m, "u", 4711) == -ENOMSG); + + r = sd_bus_reply_method_return(m, "s", n); + assert_se(r >= 0); + + return 1; +} + +static int exit_handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { + struct context *c = userdata; + int r; + + c->quit = true; + + log_info("Exit called"); + + r = sd_bus_reply_method_return(m, ""); + assert_se(r >= 0); + + return 1; +} + +static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { + struct context *c = userdata; + int r; + + log_info("property get for %s called, returning \"%s\".", property, c->something); + + r = sd_bus_message_append(reply, "s", c->something); + assert_se(r >= 0); + + return 1; +} + +static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) { + struct context *c = userdata; + const char *s; + char *n; + int r; + + log_info("property set for %s called", property); + + r = sd_bus_message_read(value, "s", &s); + assert_se(r >= 0); + + n = strdup(s); + assert_se(n); + + free(c->something); + c->something = n; + + return 1; +} + +static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *s = NULL; + const char *x; + int r; + + assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0); + r = sd_bus_message_append(reply, "s", s); + assert_se(r >= 0); + + assert_se(x = startswith(path, "/value/")); + + assert_se(PTR_TO_UINT(userdata) == 30); + + return 1; +} + +static int notify_test(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", "Value", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int notify_test2(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_properties_changed_strv(sd_bus_message_get_bus(m), m->path, "org.freedesktop.systemd.ValueTest", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_interfaces_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_interfaces_added(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_interfaces_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_interfaces_removed(sd_bus_message_get_bus(m), "/value/a/x", "org.freedesktop.systemd.ValueTest", NULL) >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a/x") >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + int r; + + assert_se(sd_bus_emit_object_removed(sd_bus_message_get_bus(m), "/value/a/x") >= 0); + + r = sd_bus_reply_method_return(m, NULL); + assert_se(r >= 0); + + return 1; +} + +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0), + SD_BUS_METHOD("Exit", "", "", exit_handler, 0), + SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0), + SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0), + SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0), + SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0), + SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0), + SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0), + SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0), + SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0), + SD_BUS_VTABLE_END +}; + +static const sd_bus_vtable vtable2[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("NotifyTest", "", "", notify_test, 0), + SD_BUS_METHOD("NotifyTest2", "", "", notify_test2, 0), + SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0), + SD_BUS_PROPERTY("AnExplicitProperty", "s", NULL, offsetof(struct context, something), SD_BUS_VTABLE_PROPERTY_EXPLICIT|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_VTABLE_END +}; + +static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + + if (object_path_startswith("/value", path)) + assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL)); + + return 1; +} + +static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { + + if (object_path_startswith("/value/a", path)) + assert_se(*nodes = strv_new("/value/a/x", "/value/a/y", "/value/a/z", NULL)); + + return 1; +} + +static void *server(void *p) { + struct context *c = p; + sd_bus *bus = NULL; + sd_id128_t id; + int r; + + c->quit = false; + + assert_se(sd_id128_randomize(&id) >= 0); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0); + assert_se(sd_bus_set_server(bus, 1, id) >= 0); + + assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0); + assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0); + assert_se(sd_bus_add_fallback_vtable(bus, NULL, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0); + assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value", enumerator_callback, NULL) >= 0); + assert_se(sd_bus_add_node_enumerator(bus, NULL, "/value/a", enumerator2_callback, NULL) >= 0); + assert_se(sd_bus_add_object_manager(bus, NULL, "/value") >= 0); + assert_se(sd_bus_add_object_manager(bus, NULL, "/value/a") >= 0); + + assert_se(sd_bus_start(bus) >= 0); + + log_error("Entering event loop on server"); + + while (!c->quit) { + log_error("Loop!"); + + r = sd_bus_process(bus, NULL); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + goto fail; + } + + continue; + } + } + + r = 0; + +fail: + if (bus) { + sd_bus_flush(bus); + sd_bus_unref(bus); + } + + return INT_TO_PTR(r); +} + +static int client(struct context *c) { + _cleanup_(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; + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "NoOperation", &error, NULL, NULL); + assert_se(r >= 0); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo"); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + assert_se(streq(s, "<<>>")); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, ""); + assert_se(r < 0); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); + + sd_bus_error_free(&error); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo"); + assert_se(r < 0); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)); + + sd_bus_error_free(&error); + + r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s"); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + assert_se(streq(s, "<<>>")); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test"); + assert_se(r >= 0); + + r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s"); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + assert_se(streq(s, "test")); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815); + assert_se(r >= 0); + + assert_se(c->automatic_integer_property == 815); + + r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticStringProperty", &error, "s", "Du Dödel, Du!"); + assert_se(r >= 0); + + assert_se(streq(c->automatic_string_property, "Du Dödel, Du!")); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + fputs(s, stdout); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s"); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + log_info("read %s", s); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + fputs(s, stdout); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + fputs(s, stdout); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + assert_se(r >= 0); + + r = sd_bus_message_read(reply, "s", &s); + assert_se(r >= 0); + fputs(s, stdout); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", ""); + assert_se(r >= 0); + + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2"); + assert_se(r < 0); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE)); + sd_bus_error_free(&error); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); + assert_se(r < 0); + assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)); + sd_bus_error_free(&error); + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); + assert_se(r >= 0); + + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, ""); + assert_se(r >= 0); + + sd_bus_flush(bus); + + return 0; +} + +int main(int argc, char *argv[]) { + struct context c = {}; + pthread_t s; + void *p; + int r, q; + + zero(c); + + c.automatic_integer_property = 4711; + assert_se(c.automatic_string_property = strdup("dudeldu")); + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0); + + r = pthread_create(&s, NULL, server, &c); + if (r != 0) + return -r; + + r = client(&c); + + q = pthread_join(s, &p); + if (q != 0) + return -q; + + if (r < 0) + return r; + + if (PTR_TO_INT(p) < 0) + return PTR_TO_INT(p); + + free(c.something); + free(c.automatic_string_property); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-server.c b/src/libsystemd/src/sd-bus/test-bus-server.c new file mode 100644 index 0000000000..9ffefa6cd0 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-server.c @@ -0,0 +1,217 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "shared/bus-util.h" + +#include "bus-internal.h" + +struct context { + int fds[2]; + + bool client_negotiate_unix_fds; + bool server_negotiate_unix_fds; + + bool client_anonymous_auth; + bool server_anonymous_auth; +}; + +static void *server(void *p) { + struct context *c = p; + sd_bus *bus = NULL; + sd_id128_t id; + bool quit = false; + int r; + + assert_se(sd_id128_randomize(&id) >= 0); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0); + assert_se(sd_bus_set_server(bus, 1, id) >= 0); + assert_se(sd_bus_set_anonymous(bus, c->server_anonymous_auth) >= 0); + assert_se(sd_bus_negotiate_fds(bus, c->server_negotiate_unix_fds) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + while (!quit) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + + r = sd_bus_process(bus, &m); + if (r < 0) { + log_error_errno(r, "Failed to process requests: %m"); + goto fail; + } + + if (r == 0) { + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) { + log_error_errno(r, "Failed to wait: %m"); + goto fail; + } + + continue; + } + + if (!m) + continue; + + log_info("Got message! member=%s", strna(sd_bus_message_get_member(m))); + + if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "Exit")) { + + assert_se((sd_bus_can_send(bus, 'h') >= 1) == (c->server_negotiate_unix_fds && c->client_negotiate_unix_fds)); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) { + log_error_errno(r, "Failed to allocate return: %m"); + goto fail; + } + + quit = true; + + } else if (sd_bus_message_is_method_call(m, NULL, NULL)) { + r = sd_bus_message_new_method_error( + m, + &reply, + &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); + if (r < 0) { + log_error_errno(r, "Failed to allocate return: %m"); + goto fail; + } + } + + if (reply) { + r = sd_bus_send(bus, reply, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send reply: %m"); + goto fail; + } + } + } + + r = 0; + +fail: + if (bus) { + sd_bus_flush(bus); + sd_bus_unref(bus); + } + + return INT_TO_PTR(r); +} + +static int client(struct context *c) { + _cleanup_(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; + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0); + assert_se(sd_bus_negotiate_fds(bus, c->client_negotiate_unix_fds) >= 0); + assert_se(sd_bus_set_anonymous(bus, c->client_anonymous_auth) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd.test", + "/", + "org.freedesktop.systemd.test", + "Exit"); + if (r < 0) + return log_error_errno(r, "Failed to allocate method call: %m"); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + return r; + } + + return 0; +} + +static int test_one(bool client_negotiate_unix_fds, bool server_negotiate_unix_fds, + bool client_anonymous_auth, bool server_anonymous_auth) { + + struct context c; + pthread_t s; + void *p; + int r, q; + + zero(c); + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0); + + c.client_negotiate_unix_fds = client_negotiate_unix_fds; + c.server_negotiate_unix_fds = server_negotiate_unix_fds; + c.client_anonymous_auth = client_anonymous_auth; + c.server_anonymous_auth = server_anonymous_auth; + + r = pthread_create(&s, NULL, server, &c); + if (r != 0) + return -r; + + r = client(&c); + + q = pthread_join(s, &p); + if (q != 0) + return -q; + + if (r < 0) + return r; + + if (PTR_TO_INT(p) < 0) + return PTR_TO_INT(p); + + return 0; +} + +int main(int argc, char *argv[]) { + int r; + + r = test_one(true, true, false, false); + assert_se(r >= 0); + + r = test_one(true, false, false, false); + assert_se(r >= 0); + + r = test_one(false, true, false, false); + assert_se(r >= 0); + + r = test_one(false, false, false, false); + assert_se(r >= 0); + + r = test_one(true, true, true, true); + assert_se(r >= 0); + + r = test_one(true, true, false, true); + assert_se(r >= 0); + + r = test_one(true, true, true, false); + assert_se(r == -EPERM); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-signature.c b/src/libsystemd/src/sd-bus/test-bus-signature.c new file mode 100644 index 0000000000..c08999b8e1 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-signature.c @@ -0,0 +1,165 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/log.h" +#include "basic/string-util.h" + +#include "bus-internal.h" +#include "bus-signature.h" + +int main(int argc, char *argv[]) { + char prefix[256]; + int r; + + assert_se(signature_is_single("y", false)); + assert_se(signature_is_single("u", false)); + assert_se(signature_is_single("v", false)); + assert_se(signature_is_single("as", false)); + assert_se(signature_is_single("(ss)", false)); + assert_se(signature_is_single("()", false)); + assert_se(signature_is_single("(()()()()())", false)); + assert_se(signature_is_single("(((())))", false)); + assert_se(signature_is_single("((((s))))", false)); + assert_se(signature_is_single("{ss}", true)); + assert_se(signature_is_single("a{ss}", false)); + assert_se(!signature_is_single("uu", false)); + assert_se(!signature_is_single("", false)); + assert_se(!signature_is_single("(", false)); + assert_se(!signature_is_single(")", false)); + assert_se(!signature_is_single("())", false)); + assert_se(!signature_is_single("((())", false)); + assert_se(!signature_is_single("{)", false)); + assert_se(!signature_is_single("{}", true)); + assert_se(!signature_is_single("{sss}", true)); + assert_se(!signature_is_single("{s}", true)); + assert_se(!signature_is_single("{ss}", false)); + assert_se(!signature_is_single("{ass}", true)); + assert_se(!signature_is_single("a}", true)); + + assert_se(signature_is_pair("yy")); + assert_se(signature_is_pair("ss")); + assert_se(signature_is_pair("sas")); + assert_se(signature_is_pair("sv")); + assert_se(signature_is_pair("sa(vs)")); + assert_se(!signature_is_pair("")); + assert_se(!signature_is_pair("va")); + assert_se(!signature_is_pair("sss")); + assert_se(!signature_is_pair("{s}ss")); + + assert_se(signature_is_valid("ssa{ss}sssub", true)); + assert_se(signature_is_valid("ssa{ss}sssub", false)); + assert_se(signature_is_valid("{ss}", true)); + assert_se(!signature_is_valid("{ss}", false)); + assert_se(signature_is_valid("", true)); + assert_se(signature_is_valid("", false)); + + assert_se(signature_is_valid("sssusa(uuubbba(uu)uuuu)a{u(uuuvas)}", false)); + + assert_se(!signature_is_valid("a", false)); + assert_se(signature_is_valid("as", false)); + assert_se(signature_is_valid("aas", false)); + assert_se(signature_is_valid("aaas", false)); + assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad", false)); + assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false)); + assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false)); + + assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false)); + assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false)); + + assert_se(namespace_complex_pattern("", "")); + assert_se(namespace_complex_pattern("foobar", "foobar")); + assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo")); + assert_se(namespace_complex_pattern("foobar.", "foobar.waldo")); + assert_se(namespace_complex_pattern("foobar.waldo", "foobar.")); + assert_se(!namespace_complex_pattern("foobar.waldo", "foobar")); + assert_se(!namespace_complex_pattern("foobar", "foobar.waldo")); + assert_se(!namespace_complex_pattern("", "foo")); + assert_se(!namespace_complex_pattern("foo", "")); + assert_se(!namespace_complex_pattern("foo.", "")); + + assert_se(path_complex_pattern("", "")); + assert_se(!path_complex_pattern("", "/")); + assert_se(!path_complex_pattern("/", "")); + assert_se(path_complex_pattern("/", "/")); + assert_se(path_complex_pattern("/foobar/", "/")); + assert_se(!path_complex_pattern("/foobar/", "/foobar")); + assert_se(path_complex_pattern("/foobar", "/foobar")); + assert_se(!path_complex_pattern("/foobar", "/foobar/")); + assert_se(!path_complex_pattern("/foobar", "/foobar/waldo")); + assert_se(path_complex_pattern("/foobar/", "/foobar/waldo")); + assert_se(path_complex_pattern("/foobar/waldo", "/foobar/")); + + assert_se(path_simple_pattern("/foo/", "/foo/bar/waldo")); + + assert_se(namespace_simple_pattern("", "")); + assert_se(namespace_simple_pattern("", ".foobar")); + assert_se(namespace_simple_pattern("foobar", "foobar")); + assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo")); + assert_se(namespace_simple_pattern("foobar", "foobar.waldo")); + assert_se(!namespace_simple_pattern("foobar.waldo", "foobar")); + assert_se(!namespace_simple_pattern("", "foo")); + assert_se(!namespace_simple_pattern("foo", "")); + assert_se(namespace_simple_pattern("foo.", "foo.bar.waldo")); + + assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar")); + assert_se(streq(object_path_startswith("/foo", "/foo"), "")); + assert_se(streq(object_path_startswith("/foo", "/"), "foo")); + assert_se(streq(object_path_startswith("/", "/"), "")); + assert_se(!object_path_startswith("/foo", "/bar")); + assert_se(!object_path_startswith("/", "/bar")); + assert_se(!object_path_startswith("/foo", "")); + + assert_se(object_path_is_valid("/foo/bar")); + assert_se(object_path_is_valid("/foo")); + assert_se(object_path_is_valid("/")); + assert_se(object_path_is_valid("/foo5")); + assert_se(object_path_is_valid("/foo_5")); + assert_se(!object_path_is_valid("")); + assert_se(!object_path_is_valid("/foo/")); + assert_se(!object_path_is_valid("//")); + assert_se(!object_path_is_valid("//foo")); + assert_se(!object_path_is_valid("/foo//bar")); + assert_se(!object_path_is_valid("/foo/aaaäöä")); + + OBJECT_PATH_FOREACH_PREFIX(prefix, "/") { + log_info("<%s>", prefix); + assert_not_reached("???"); + } + + r = 0; + OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx") { + log_info("<%s>", prefix); + assert_se(streq(prefix, "/")); + assert_se(r == 0); + r++; + } + assert_se(r == 1); + + r = 0; + OBJECT_PATH_FOREACH_PREFIX(prefix, "/xxx/yyy/zzz") { + log_info("<%s>", prefix); + assert_se(r != 0 || streq(prefix, "/xxx/yyy")); + assert_se(r != 1 || streq(prefix, "/xxx")); + assert_se(r != 2 || streq(prefix, "/")); + r++; + } + assert_se(r == 3); + + return 0; +} diff --git a/src/libsystemd/src/sd-bus/test-bus-zero-copy.c b/src/libsystemd/src/sd-bus/test-bus-zero-copy.c new file mode 100644 index 0000000000..ac453e5887 --- /dev/null +++ b/src/libsystemd/src/sd-bus/test-bus-zero-copy.c @@ -0,0 +1,211 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/memfd-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "bus-dump.h" +#include "bus-kernel.h" +#include "bus-message.h" + +#define FIRST_ARRAY 17 +#define SECOND_ARRAY 33 + +#define STRING_SIZE 123 + +int main(int argc, char *argv[]) { + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; + const char *unique; + uint8_t *p; + sd_bus *a, *b; + int r, bus_ref; + sd_bus_message *m; + int f; + uint64_t sz; + uint32_t u32; + size_t i, l; + char *s; + _cleanup_close_ int sfd = -1; + + log_set_max_level(LOG_DEBUG); + + assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + + bus_ref = bus_kernel_create_bus(name, false, &bus_name); + if (bus_ref == -ENOENT) + return EXIT_TEST_SKIP; + + assert_se(bus_ref >= 0); + + address = strappend("kernel:path=", bus_name); + assert_se(address); + + r = sd_bus_new(&a); + assert_se(r >= 0); + + r = sd_bus_new(&b); + assert_se(r >= 0); + + r = sd_bus_set_address(a, address); + assert_se(r >= 0); + + r = sd_bus_set_address(b, address); + assert_se(r >= 0); + + r = sd_bus_start(a); + assert_se(r >= 0); + + r = sd_bus_start(b); + assert_se(r >= 0); + + r = sd_bus_get_unique_name(a, &unique); + assert_se(r >= 0); + + r = sd_bus_message_new_method_call(b, &m, unique, "/a/path", "an.inter.face", "AMethod"); + assert_se(r >= 0); + + r = sd_bus_message_open_container(m, 'r', "aysay"); + assert_se(r >= 0); + + r = sd_bus_message_append_array_space(m, 'y', FIRST_ARRAY, (void**) &p); + assert_se(r >= 0); + + p[0] = '<'; + memset(p+1, 'L', FIRST_ARRAY-2); + p[FIRST_ARRAY-1] = '>'; + + f = memfd_new_and_map(NULL, STRING_SIZE, (void**) &s); + assert_se(f >= 0); + + s[0] = '<'; + for (i = 1; i < STRING_SIZE-2; i++) + s[i] = '0' + (i % 10); + s[STRING_SIZE-2] = '>'; + s[STRING_SIZE-1] = 0; + munmap(s, STRING_SIZE); + + r = memfd_get_size(f, &sz); + assert_se(r >= 0); + assert_se(sz == STRING_SIZE); + + r = sd_bus_message_append_string_memfd(m, f, 0, (uint64_t) -1); + assert_se(r >= 0); + + close(f); + + f = memfd_new_and_map(NULL, SECOND_ARRAY, (void**) &p); + assert_se(f >= 0); + + p[0] = '<'; + memset(p+1, 'P', SECOND_ARRAY-2); + p[SECOND_ARRAY-1] = '>'; + munmap(p, SECOND_ARRAY); + + r = memfd_get_size(f, &sz); + assert_se(r >= 0); + assert_se(sz == SECOND_ARRAY); + + r = sd_bus_message_append_array_memfd(m, 'y', f, 0, (uint64_t) -1); + assert_se(r >= 0); + + close(f); + + r = sd_bus_message_close_container(m); + assert_se(r >= 0); + + r = sd_bus_message_append(m, "u", 4711); + assert_se(r >= 0); + + assert_se((sfd = memfd_new_and_map(NULL, 6, (void**) &p)) >= 0); + memcpy(p, "abcd\0", 6); + munmap(p, 6); + assert_se(sd_bus_message_append_string_memfd(m, sfd, 1, 4) >= 0); + + r = bus_message_seal(m, 55, 99*USEC_PER_SEC); + assert_se(r >= 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + + r = sd_bus_send(b, m, NULL); + assert_se(r >= 0); + + sd_bus_message_unref(m); + + r = sd_bus_process(a, &m); + assert_se(r > 0); + + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); + sd_bus_message_rewind(m, true); + + r = sd_bus_message_enter_container(m, 'r', "aysay"); + assert_se(r > 0); + + r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l); + assert_se(r > 0); + assert_se(l == FIRST_ARRAY); + + assert_se(p[0] == '<'); + for (i = 1; i < l-1; i++) + assert_se(p[i] == 'L'); + assert_se(p[l-1] == '>'); + + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + + assert_se(s[0] == '<'); + for (i = 1; i < STRING_SIZE-2; i++) + assert_se(s[i] == (char) ('0' + (i % 10))); + assert_se(s[STRING_SIZE-2] == '>'); + assert_se(s[STRING_SIZE-1] == 0); + + r = sd_bus_message_read_array(m, 'y', (const void**) &p, &l); + assert_se(r > 0); + assert_se(l == SECOND_ARRAY); + + assert_se(p[0] == '<'); + for (i = 1; i < l-1; i++) + assert_se(p[i] == 'P'); + assert_se(p[l-1] == '>'); + + r = sd_bus_message_exit_container(m); + assert_se(r > 0); + + r = sd_bus_message_read(m, "u", &u32); + assert_se(r > 0); + assert_se(u32 == 4711); + + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + assert_se(streq_ptr(s, "bcd")); + + sd_bus_message_unref(m); + + sd_bus_unref(a); + sd_bus_unref(b); + + return 0; +} diff --git a/src/libsystemd/src/sd-daemon/Makefile b/src/libsystemd/src/sd-daemon/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-daemon/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-daemon/sd-daemon.c b/src/libsystemd/src/sd-daemon/sd-daemon.c new file mode 100644 index 0000000000..e848c16212 --- /dev/null +++ b/src/libsystemd/src/sd-daemon/sd-daemon.c @@ -0,0 +1,622 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/socket-util.h" +#include "basic/strv.h" +#include "basic/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; + int n, r, fd; + pid_t pid; + + e = getenv("LISTEN_PID"); + if (!e) { + r = 0; + goto finish; + } + + r = parse_pid(e, &pid); + if (r < 0) + goto finish; + + /* Is this for us? */ + if (getpid() != pid) { + r = 0; + goto finish; + } + + e = getenv("LISTEN_FDS"); + if (!e) { + r = 0; + goto finish; + } + + r = safe_atoi(e, &n); + if (r < 0) + goto finish; + + 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 = n; + +finish: + 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; + } + + *names = l; + l = NULL; + + return n_fds; +} + +_public_ int sd_is_fifo(int fd, const char *path) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISFIFO(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + } + + return 1; +} + +_public_ int sd_is_special(int fd, const char *path) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) + return st_path.st_rdev == st_fd.st_rdev; + else + return 0; + } + + return 1; +} + +static int sd_is_socket_internal(int fd, int type, int listening) { + struct stat st_fd; + + assert_return(fd >= 0, -EBADF); + assert_return(type >= 0, -EINVAL); + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISSOCK(st_fd.st_mode)) + return 0; + + if (type != 0) { + int other_type = 0; + socklen_t l = sizeof(other_type); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) + return -errno; + + if (l != sizeof(other_type)) + return -EINVAL; + + if (other_type != type) + return 0; + } + + if (listening >= 0) { + int accepting = 0; + socklen_t l = sizeof(accepting); + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) + return -errno; + + if (l != sizeof(accepting)) + return -EINVAL; + + if (!accepting != !listening) + return 0; + } + + return 1; +} + +_public_ int sd_is_socket(int fd, int family, int type, int listening) { + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(family >= 0, -EINVAL); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (family > 0) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + return sockaddr.sa.sa_family == family; + } + + return 1; +} + +_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_INET && + sockaddr.sa.sa_family != AF_INET6) + return 0; + + if (family != 0) + if (sockaddr.sa.sa_family != family) + return 0; + + if (port > 0) { + if (sockaddr.sa.sa_family == AF_INET) { + if (l < sizeof(struct sockaddr_in)) + return -EINVAL; + + return htobe16(port) == sockaddr.in.sin_port; + } else { + if (l < sizeof(struct sockaddr_in6)) + return -EINVAL; + + return htobe16(port) == sockaddr.in6.sin6_port; + } + } + + return 1; +} + +_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { + union sockaddr_union sockaddr = {}; + socklen_t l = sizeof(sockaddr); + int r; + + assert_return(fd >= 0, -EBADF); + + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) + return r; + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_UNIX) + return 0; + + if (path) { + if (length == 0) + length = strlen(path); + + if (length == 0) + /* Unnamed socket */ + return l == offsetof(struct sockaddr_un, sun_path); + + if (path[0]) + /* Normal path socket */ + return + (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && + memcmp(path, sockaddr.un.sun_path, length+1) == 0; + else + /* Abstract namespace socket */ + return + (l == offsetof(struct sockaddr_un, sun_path) + length) && + memcmp(path, sockaddr.un.sun_path, length) == 0; + } + + return 1; +} + +_public_ int sd_is_mq(int fd, const char *path) { + struct mq_attr attr; + + /* Check that the fd is valid */ + assert_return(fcntl(fd, F_GETFD) >= 0, -errno); + + 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]; + struct stat a, b; + + assert_return(path_is_absolute(path), -EINVAL); + + if (fstat(fd, &a) < 0) + return -errno; + + strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); + fpath[sizeof(fpath)-1] = 0; + + if (stat(fpath, &b) < 0) + return -errno; + + if (a.st_dev != b.st_dev || + a.st_ino != b.st_ino) + return 0; + } + + return 1; +} + +_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { + union sockaddr_union sockaddr = { + .sa.sa_family = AF_UNIX, + }; + struct iovec iovec = { + .iov_base = (char*) state, + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &sockaddr, + }; + _cleanup_close_ int fd = -1; + struct cmsghdr *cmsg = NULL; + const char *e; + bool have_pid; + int r; + + if (!state) { + r = -EINVAL; + goto finish; + } + + if (n_fds > 0 && !fds) { + r = -EINVAL; + goto finish; + } + + e = getenv("NOTIFY_SOCKET"); + if (!e) + return 0; + + /* Must be an abstract socket, or an absolute path */ + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + r = -EINVAL; + 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)); + if (sockaddr.un.sun_path[0] == '@') + sockaddr.un.sun_path[0] = 0; + + msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); + + have_pid = pid != 0 && pid != getpid(); + + if (n_fds > 0 || have_pid) { + /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated 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) { + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds); + + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); + + if (have_pid) + assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); + } + + if (have_pid) { + struct ucred *ucred; + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(cmsg); + ucred->pid = pid; + ucred->uid = getuid(); + ucred->gid = getgid(); + } + } + + /* First try with fake ucred data, as requested */ + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { + r = 1; + goto finish; + } + + /* If that failed, try with our own ucred instead */ + if (have_pid) { + msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); + if (msghdr.msg_controllen == 0) + msghdr.msg_control = NULL; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) { + r = 1; + goto finish; + } + } + + r = -errno; + +finish: + if (unset_environment) + unsetenv("NOTIFY_SOCKET"); + + return r; +} + +_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) { + return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0); +} + +_public_ int sd_notify(int unset_environment, const char *state) { + return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0); +} + +_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) { + _cleanup_free_ char *p = NULL; + int r; + + if (format) { + va_list ap; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + } + + return sd_pid_notify(pid, unset_environment, p); +} + +_public_ int sd_notifyf(int unset_environment, const char *format, ...) { + _cleanup_free_ char *p = NULL; + int r; + + if (format) { + va_list ap; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + } + + return sd_pid_notify(0, unset_environment, p); +} + +_public_ int sd_booted(void) { + /* 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. */ + + return laccess("/run/systemd/system/", F_OK) >= 0; +} + +_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) { + const char *s, *p = ""; /* p is set to dummy value to do unsetting */ + uint64_t u; + int r = 0; + + s = getenv("WATCHDOG_USEC"); + if (!s) + goto finish; + + r = safe_atou64(s, &u); + if (r < 0) + goto finish; + if (u <= 0 || u >= USEC_INFINITY) { + r = -EINVAL; + goto finish; + } + + p = getenv("WATCHDOG_PID"); + if (p) { + pid_t pid; + + r = parse_pid(p, &pid); + if (r < 0) + goto finish; + + /* Is this for us? */ + if (getpid() != pid) { + r = 0; + goto finish; + } + } + + if (usec) + *usec = u; + + r = 1; + +finish: + if (unset_environment && s) + unsetenv("WATCHDOG_USEC"); + if (unset_environment && p) + unsetenv("WATCHDOG_PID"); + + return r; +} diff --git a/src/libsystemd/src/sd-device/Makefile b/src/libsystemd/src/sd-device/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-device/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-device/device-enumerator-private.h b/src/libsystemd/src/sd-device/device-enumerator-private.h new file mode 100644 index 0000000000..eb06f9542d --- /dev/null +++ b/src/libsystemd/src/sd-device/device-enumerator-private.h @@ -0,0 +1,34 @@ +#pragma once + +/*** + 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 . +***/ + +#include "sd-device.h" + +int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); +int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); + +#define FOREACH_DEVICE_AND_SUBSYSTEM(enumerator, device) \ + for (device = device_enumerator_get_first(enumerator); \ + device; \ + device = device_enumerator_get_next(enumerator)) diff --git a/src/libsystemd/src/sd-device/device-enumerator.c b/src/libsystemd/src/sd-device/device-enumerator.c new file mode 100644 index 0000000000..4e0edb6573 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-enumerator.c @@ -0,0 +1,988 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014-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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/prioq.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "device-enumerator-private.h" +#include "device-util.h" +#include "sd-device.h" + +#define DEVICE_ENUMERATE_MAX_DEPTH 256 + +typedef enum DeviceEnumerationType { + DEVICE_ENUMERATION_TYPE_DEVICES, + DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, + _DEVICE_ENUMERATION_TYPE_MAX, + _DEVICE_ENUMERATION_TYPE_INVALID = -1, +} DeviceEnumerationType; + +struct sd_device_enumerator { + unsigned n_ref; + + DeviceEnumerationType type; + Prioq *devices; + bool scan_uptodate; + + Set *match_subsystem; + Set *nomatch_subsystem; + Hashmap *match_sysattr; + Hashmap *nomatch_sysattr; + Hashmap *match_property; + Set *match_sysname; + Set *match_tag; + sd_device *match_parent; + bool match_allow_uninitialized; +}; + +_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL; + + assert(ret); + + enumerator = new0(sd_device_enumerator, 1); + if (!enumerator) + return -ENOMEM; + + enumerator->n_ref = 1; + enumerator->type = _DEVICE_ENUMERATION_TYPE_INVALID; + + *ret = enumerator; + enumerator = NULL; + + return 0; +} + +_public_ sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + assert_se((++ enumerator->n_ref) >= 2); + + return enumerator; +} + +_public_ sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator) { + if (enumerator && (-- enumerator->n_ref) == 0) { + sd_device *device; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + prioq_free(enumerator->devices); + + set_free_free(enumerator->match_subsystem); + set_free_free(enumerator->nomatch_subsystem); + hashmap_free_free_free(enumerator->match_sysattr); + hashmap_free_free_free(enumerator->nomatch_sysattr); + hashmap_free_free_free(enumerator->match_property); + set_free_free(enumerator->match_sysname); + set_free_free(enumerator->match_tag); + sd_device_unref(enumerator->match_parent); + + free(enumerator); + } + + return NULL; +} + +_public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { + Set **set; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(subsystem, -EINVAL); + + if (match) + set = &enumerator->match_subsystem; + else + set = &enumerator->nomatch_subsystem; + + r = set_ensure_allocated(set, NULL); + if (r < 0) + return r; + + r = set_put_strdup(*set, subsystem); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) { + _cleanup_free_ char *sysattr = NULL, *value = NULL; + Hashmap **hashmap; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_sysattr, -EINVAL); + + if (match) + hashmap = &enumerator->match_sysattr; + else + hashmap = &enumerator->nomatch_sysattr; + + r = hashmap_ensure_allocated(hashmap, NULL); + if (r < 0) + return r; + + sysattr = strdup(_sysattr); + if (!sysattr) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(*hashmap, sysattr, value); + if (r < 0) + return r; + + sysattr = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) { + _cleanup_free_ char *property = NULL, *value = NULL; + int r; + + assert_return(enumerator, -EINVAL); + assert_return(_property, -EINVAL); + + r = hashmap_ensure_allocated(&enumerator->match_property, NULL); + if (r < 0) + return r; + + property = strdup(_property); + if (!property) + return -ENOMEM; + + if (_value) { + value = strdup(_value); + if (!value) + return -ENOMEM; + } + + r = hashmap_put(enumerator->match_property, property, value); + if (r < 0) + return r; + + property = NULL; + value = NULL; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(sysname, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_sysname, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_sysname, sysname); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(tag, -EINVAL); + + r = set_ensure_allocated(&enumerator->match_tag, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_tag, tag); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { + assert_return(enumerator, -EINVAL); + assert_return(parent, -EINVAL); + + sd_device_unref(enumerator->match_parent); + enumerator->match_parent = sd_device_ref(parent); + + enumerator->scan_uptodate = false; + + return 0; +} + +_public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = true; + + enumerator->scan_uptodate = false; + + return 0; +} + +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { + assert_return(enumerator, -EINVAL); + + enumerator->match_allow_uninitialized = false; + + enumerator->scan_uptodate = false; + + return 0; +} + +static int device_compare(const void *_a, const void *_b) { + sd_device *a = (sd_device *)_a, *b = (sd_device *)_b; + const char *devpath_a, *devpath_b, *sound_a; + bool delay_a, delay_b; + + assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + + sound_a = strstr(devpath_a, "/sound/card"); + if (sound_a) { + /* For sound cards the control device must be enumerated last to + * make sure it's the final device node that gets ACLs applied. + * Applications rely on this fact and use ACL changes on the + * control node as an indicator that the ACL change of the + * entire sound card completed. The kernel makes this guarantee + * when creating those devices, and hence we should too when + * enumerating them. */ + sound_a += strlen("/sound/card"); + sound_a = strchr(sound_a, '/'); + + if (sound_a) { + unsigned prefix_len; + + prefix_len = sound_a - devpath_a; + + if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { + const char *sound_b; + + sound_b = devpath_b + prefix_len; + + if (startswith(sound_a, "/controlC") && + !startswith(sound_b, "/contolC")) + return 1; + + if (!startswith(sound_a, "/controlC") && + startswith(sound_b, "/controlC")) + return -1; + } + } + } + + /* md and dm devices are enumerated after all other devices */ + delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); + delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); + if (delay_a != delay_b) + return delay_a - delay_b; + + return strcmp(devpath_a, devpath_b); +} + +int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { + int r; + + assert_return(enumerator, -EINVAL); + assert_return(device, -EINVAL); + + r = prioq_ensure_allocated(&enumerator->devices, device_compare); + if (r < 0) + return r; + + r = prioq_put(enumerator->devices, device, NULL); + if (r < 0) + return r; + + sd_device_ref(device); + + return 0; +} + +static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { + const char *value; + int r; + + assert(device); + assert(sysattr); + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return false; + + if (!match_value) + return true; + + if (fnmatch(match_value, value, 0) == 0) + return true; + + return false; +} + +static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) { + const char *sysattr; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i) + if (match_sysattr_value(device, sysattr, value)) + return false; + + HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i) + if (!match_sysattr_value(device, sysattr, value)) + return false; + + return true; +} + +static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { + const char *property; + const char *value; + Iterator i; + + assert(enumerator); + assert(device); + + if (hashmap_isempty(enumerator->match_property)) + return true; + + HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) { + const char *property_dev, *value_dev; + + FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { + if (fnmatch(property, property_dev, 0) != 0) + continue; + + if (!value && !value_dev) + return true; + + if (!value || !value_dev) + continue; + + if (fnmatch(value, value_dev, 0) == 0) + return true; + } + } + + return false; +} + +static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { + const char *tag; + Iterator i; + + assert(enumerator); + assert(device); + + SET_FOREACH(tag, enumerator->match_tag, i) + if (!sd_device_has_tag(device, tag)) + return false; + + return true; +} + +static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { + const char *devpath, *devpath_dev; + int r; + + assert(enumerator); + assert(device); + + if (!enumerator->match_parent) + return true; + + r = sd_device_get_devpath(enumerator->match_parent, &devpath); + assert(r >= 0); + + r = sd_device_get_devpath(device, &devpath_dev); + assert(r >= 0); + + return startswith(devpath_dev, devpath); +} + +static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { + const char *sysname_match; + Iterator i; + + assert(enumerator); + assert(sysname); + + if (set_isempty(enumerator->match_sysname)) + return true; + + SET_FOREACH(sysname_match, enumerator->match_sysname, i) + if (fnmatch(sysname_match, sysname, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(basedir); + + path = strjoina("/sys/", basedir, "/"); + + if (subdir1) + path = strjoina(path, subdir1, "/"); + + if (subdir2) + path = strjoina(path, subdir2, "/"); + + dir = opendir(path); + if (!dir) + return -errno; + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + char syspath[strlen(path) + 1 + strlen(dent->d_name) + 1]; + dev_t devnum; + int ifindex, initialized, k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_sysname(enumerator, dent->d_name)) + continue; + + (void)sprintf(syspath, "%s%s", path, dent->d_name); + + k = sd_device_new_from_syspath(&device, syspath); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racey, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_devnum(device, &devnum); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_ifindex(device, &ifindex); + if (k < 0) { + r = k; + continue; + } + + k = sd_device_get_is_initialized(device, &initialized); + if (k < 0) { + r = k; + continue; + } + + /* + * All devices with a device node or network interfaces + * possibly need udev to adjust the device node permission + * or context, or rename the interface before it can be + * reliably used from other processes. + * + * For now, we can only check these types of devices, we + * might not store a database, and have no way to find out + * for all other types of devices. + */ + if (!enumerator->match_allow_uninitialized && + !initialized && + (major(devnum) > 0 || ifindex > 0)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_tag(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) + r = k; + } + + return r; +} + +static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { + const char *subsystem_match; + Iterator i; + + assert(enumerator); + + if (!subsystem) + return false; + + SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return false; + + if (set_isempty(enumerator->match_subsystem)) + return true; + + SET_FOREACH(subsystem_match, enumerator->match_subsystem, i) + if (fnmatch(subsystem_match, subsystem, 0) == 0) + return true; + + return false; +} + +static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + path = strjoina("/sys/", basedir); + + dir = opendir(path); + if (!dir) + return -errno; + + log_debug(" device-enumerator: scanning %s", path); + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + int k; + + if (dent->d_name[0] == '.') + continue; + + if (!match_subsystem(enumerator, subsystem ? : dent->d_name)) + continue; + + k = enumerator_scan_dir_and_add_devices(enumerator, basedir, dent->d_name, subdir); + if (k < 0) + r = k; + } + + return r; +} + +static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const char *tag) { + _cleanup_closedir_ DIR *dir = NULL; + char *path; + struct dirent *dent; + int r = 0; + + assert(enumerator); + assert(tag); + + path = strjoina("/run/udev/tags/", tag); + + dir = opendir(path); + if (!dir) { + if (errno == ENOENT) + return 0; + else { + log_error("sd-device-enumerator: could not open tags directory %s: %m", path); + return -errno; + } + } + + /* TODO: filter away subsystems? */ + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *subsystem, *sysname; + int k; + + if (dent->d_name[0] == '.') + continue; + + k = sd_device_new_from_device_id(&device, dent->d_name); + if (k < 0) { + if (k != -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + r = k; + + continue; + } + + k = sd_device_get_subsystem(device, &subsystem); + if (k < 0) { + r = k; + continue; + } + + if (!match_subsystem(enumerator, subsystem)) + continue; + + k = sd_device_get_sysname(device, &sysname); + if (k < 0) { + r = k; + continue; + } + + if (!match_sysname(enumerator, sysname)) + continue; + + if (!match_parent(enumerator, device)) + continue; + + if (!match_property(enumerator, device)) + continue; + + if (!match_sysattr(enumerator, device)) + continue; + + k = device_enumerator_add_device(enumerator, device); + if (k < 0) { + r = k; + continue; + } + } + + return r; +} + +static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) { + const char *tag; + Iterator i; + int r = 0; + + assert(enumerator); + + SET_FOREACH(tag, enumerator->match_tag, i) { + int k; + + k = enumerator_scan_devices_tag(enumerator, tag); + if (k < 0) + r = k; + } + + return r; +} + +static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *subsystem, *sysname; + int r; + + r = sd_device_new_from_syspath(&device, path); + if (r == -ENODEV) + /* this is necessarily racy, so ignore missing devices */ + return 0; + else if (r < 0) + return r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + if (!match_subsystem(enumerator, subsystem)) + return 0; + + r = sd_device_get_sysname(device, &sysname); + if (r < 0) + return r; + + if (!match_sysname(enumerator, sysname)) + return 0; + + if (!match_property(enumerator, device)) + return 0; + + if (!match_sysattr(enumerator, device)) + return 0; + + r = device_enumerator_add_device(enumerator, device); + if (r < 0) + return r; + + return 1; +} + +static int parent_crawl_children(sd_device_enumerator *enumerator, const char *path, unsigned maxdepth) { + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; + int r = 0; + + dir = opendir(path); + if (!dir) { + log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); + return -errno; + } + + FOREACH_DIRENT_ALL(dent, dir, return -errno) { + _cleanup_free_ char *child = NULL; + int k; + + if (dent->d_name[0] == '.') + continue; + + if (dent->d_type != DT_DIR) + continue; + + child = strjoin(path, "/", dent->d_name, NULL); + if (!child) + return -ENOMEM; + + k = parent_add_child(enumerator, child); + if (k < 0) + r = k; + + if (maxdepth > 0) + parent_crawl_children(enumerator, child, maxdepth - 1); + else + log_debug("device-enumerate: max depth reached, %s: ignoring devices", child); + } + + return r; +} + +static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { + const char *path; + int r = 0, k; + + r = sd_device_get_syspath(enumerator->match_parent, &path); + if (r < 0) + return r; + + k = parent_add_child(enumerator, path); + if (k < 0) + r = k; + + k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); + if (k < 0) + r = k; + + return r; +} + +static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { + int r = 0; + + log_debug("device-enumerator: scan all dirs"); + + if (access("/sys/subsystem", F_OK) >= 0) { + /* we have /subsystem/, forget all the old stuff */ + r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); + if (r < 0) + return log_debug_errno(r, "device-enumerator: failed to scan /sys/subsystem: %m"); + } else { + int k; + + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/bus: %m"); + r = k; + } + + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan /sys/class: %m"); + r = k; + } + } + + return r; +} + +int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { + sd_device *device; + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + if (!set_isempty(enumerator->match_tag)) { + k = enumerator_scan_devices_tags(enumerator); + if (k < 0) + r = k; + } else if (enumerator->match_parent) { + k = enumerator_scan_devices_children(enumerator); + if (k < 0) + r = k; + } else { + k = enumerator_scan_devices_all(enumerator); + if (k < 0) + r = k; + } + + enumerator->scan_uptodate = true; + + return r; +} + +_public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_devices(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (!enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { + sd_device *device; + const char *subsysdir; + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return 0; + + while ((device = prioq_pop(enumerator->devices))) + sd_device_unref(device); + + /* modules */ + if (match_subsystem(enumerator, "module")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan modules: %m"); + r = k; + } + } + + if (access("/sys/subsystem", F_OK) >= 0) + subsysdir = "subsystem"; + else + subsysdir = "bus"; + + /* subsystems (only buses support coldplug) */ + if (match_subsystem(enumerator, "subsystem")) { + k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan subsystems: %m"); + r = k; + } + } + + /* subsystem drivers */ + if (match_subsystem(enumerator, "drivers")) { + k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); + if (k < 0) { + log_debug_errno(k, "device-enumerator: failed to scan drivers: %m"); + r = k; + } + } + + enumerator->scan_uptodate = true; + + return r; +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { + int r; + + assert_return(enumerator, NULL); + + r = device_enumerator_scan_subsystems(enumerator); + if (r < 0) + return NULL; + + enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; + + return prioq_peek(enumerator->devices); +} + +_public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + if (enumerator->scan_uptodate || + enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) + return NULL; + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + return prioq_peek(enumerator->devices); +} + +sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { + assert_return(enumerator, NULL); + + sd_device_unref(prioq_pop(enumerator->devices)); + + return prioq_peek(enumerator->devices); +} diff --git a/src/libsystemd/src/sd-device/device-internal.h b/src/libsystemd/src/sd-device/device-internal.h new file mode 100644 index 0000000000..029032f253 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-internal.h @@ -0,0 +1,128 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include "basic/hashmap.h" +#include "basic/set.h" + +struct sd_device { + uint64_t n_ref; + + sd_device *parent; + bool parent_set; /* no need to try to reload parent */ + + OrderedHashmap *properties; + Iterator properties_iterator; + uint64_t properties_generation; /* changes whenever the properties are changed */ + uint64_t properties_iterator_generation; /* generation when iteration was started */ + + /* the subset of the properties that should be written to the db*/ + OrderedHashmap *properties_db; + + Hashmap *sysattr_values; /* cached sysattr values */ + + Set *sysattrs; /* names of sysattrs */ + Iterator sysattrs_iterator; + bool sysattrs_read; /* don't try to re-read sysattrs once read */ + + Set *tags; + Iterator tags_iterator; + uint64_t tags_generation; /* changes whenever the tags are changed */ + uint64_t tags_iterator_generation; /* generation when iteration was started */ + bool property_tags_outdated; /* need to update TAGS= property */ + + Set *devlinks; + Iterator devlinks_iterator; + uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ + uint64_t devlinks_iterator_generation; /* generation when iteration was started */ + bool property_devlinks_outdated; /* need to update DEVLINKS= property */ + int devlink_priority; + + char **properties_strv; /* the properties hashmap as a strv */ + uint8_t *properties_nulstr; /* the same as a nulstr */ + size_t properties_nulstr_len; + bool properties_buf_outdated; /* need to reread hashmap */ + + int watch_handle; + + char *syspath; + const char *devpath; + const char *sysnum; + char *sysname; + bool sysname_set; /* don't reread sysname */ + + char *devtype; + int ifindex; + char *devname; + dev_t devnum; + + char *subsystem; + bool subsystem_set; /* don't reread subsystem */ + char *driver_subsystem; /* only set for the 'drivers' subsystem */ + bool driver_subsystem_set; /* don't reread subsystem */ + char *driver; + bool driver_set; /* don't reread driver */ + + char *id_filename; + + bool is_initialized; + uint64_t usec_initialized; + + mode_t devmode; + uid_t devuid; + gid_t devgid; + + bool uevent_loaded; /* don't reread uevent */ + bool db_loaded; /* don't reread db */ + + bool sealed; /* don't read more information from uevent/db */ + bool db_persist; /* don't clean up the db when switching from initrd to real root */ +}; + +typedef enum DeviceAction { + DEVICE_ACTION_ADD, + DEVICE_ACTION_REMOVE, + DEVICE_ACTION_CHANGE, + DEVICE_ACTION_MOVE, + DEVICE_ACTION_ONLINE, + DEVICE_ACTION_OFFLINE, + _DEVICE_ACTION_MAX, + _DEVICE_ACTION_INVALID = -1, +} DeviceAction; + +int device_new_aux(sd_device **ret); +int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); +int device_add_property_internal(sd_device *device, const char *key, const char *value); +int device_read_uevent_file(sd_device *device); +int device_read_db_aux(sd_device *device, bool force); + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify); +int device_set_ifindex(sd_device *device, const char *ifindex); +int device_set_devmode(sd_device *device, const char *devmode); +int device_set_devname(sd_device *device, const char *_devname); +int device_set_devtype(sd_device *device, const char *_devtype); +int device_set_devnum(sd_device *device, const char *major, const char *minor); +int device_set_subsystem(sd_device *device, const char *_subsystem); +int device_set_driver(sd_device *device, const char *_driver); +int device_set_usec_initialized(sd_device *device, const char *initialized); + +DeviceAction device_action_from_string(const char *s) _pure_; +const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/src/sd-device/device-private.c b/src/libsystemd/src/sd-device/device-private.c new file mode 100644 index 0000000000..bf028fbbc6 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-private.c @@ -0,0 +1,1119 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/refcnt.h" +#include "basic/set.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/strxcpyx.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "sd-device.h" + +int device_add_property(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + if (key[0] != '.') { + r = device_add_property_aux(device, key, value, true); + if (r < 0) + return r; + } + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +void device_set_devlink_priority(sd_device *device, int priority) { + assert(device); + + device->devlink_priority = priority; +} + +void device_set_is_initialized(sd_device *device) { + assert(device); + + device->is_initialized = true; +} + +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { + char num[DECIMAL_STR_MAX(usec_t)]; + usec_t usec_initialized; + int r; + + assert(device); + + if (device_old && device_old->usec_initialized > 0) + usec_initialized = device_old->usec_initialized; + else + usec_initialized = now(CLOCK_MONOTONIC); + + r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); + if (r < 0) + return -errno; + + r = device_set_usec_initialized(device, num); + if (r < 0) + return r; + + return 0; +} + +static int device_read_db(sd_device *device) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->db_loaded || device->sealed) + return 0; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else + return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); + } + + /* devices with a database entry are initialized */ + device_set_is_initialized(device); + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + device->db_loaded = true; + + return 0; +} + +uint64_t device_get_properties_generation(sd_device *device) { + assert(device); + + return device->properties_generation; +} + +uint64_t device_get_tags_generation(sd_device *device) { + assert(device); + + return device->tags_generation; +} + +uint64_t device_get_devlinks_generation(sd_device *device) { + assert(device); + + return device->devlinks_generation; +} + +int device_get_devnode_mode(sd_device *device, mode_t *mode) { + int r; + + assert(device); + assert(mode); + + r = device_read_db(device); + if (r < 0) + return r; + + *mode = device->devmode; + + return 0; +} + +int device_get_devnode_uid(sd_device *device, uid_t *uid) { + int r; + + assert(device); + assert(uid); + + r = device_read_db(device); + if (r < 0) + return r; + + *uid = device->devuid; + + return 0; +} + +static int device_set_devuid(sd_device *device, const char *uid) { + unsigned u; + int r; + + assert(device); + assert(uid); + + r = safe_atou(uid, &u); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVUID", uid); + if (r < 0) + return r; + + device->devuid = u; + + return 0; +} + +int device_get_devnode_gid(sd_device *device, gid_t *gid) { + int r; + + assert(device); + assert(gid); + + r = device_read_db(device); + if (r < 0) + return r; + + *gid = device->devgid; + + return 0; +} + +static int device_set_devgid(sd_device *device, const char *gid) { + unsigned g; + int r; + + assert(device); + assert(gid); + + r = safe_atou(gid, &g); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVGID", gid); + if (r < 0) + return r; + + device->devgid = g; + + return 0; +} + +static int device_amend(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + assert(value); + + if (streq(key, "DEVPATH")) { + char *path; + + path = strjoina("/sys", value); + + /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ + r = device_set_syspath(device, path, false); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path); + } else if (streq(key, "SUBSYSTEM")) { + r = device_set_subsystem(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value); + } else if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value); + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value); + } else if (streq(key, "USEC_INITIALIZED")) { + r = device_set_usec_initialized(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value); + } else if (streq(key, "DRIVER")) { + r = device_set_driver(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value); + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value); + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value); + } else if (streq(key, "DEVUID")) { + r = device_set_devuid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value); + } else if (streq(key, "DEVGID")) { + r = device_set_devgid(device, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value); + } else if (streq(key, "DEVLINKS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, value, state) { + char devlink[l + 1]; + + strncpy(devlink, word, l); + devlink[l] = '\0'; + + r = device_add_devlink(device, devlink); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink); + } + } else if (streq(key, "TAGS")) { + const char *word, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { + char tag[l + 1]; + + (void)strncpy(tag, word, l); + tag[l] = '\0'; + + r = device_add_tag(device, tag); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag); + } + } else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value); + } + + return 0; +} + +static const char* const device_action_table[_DEVICE_ACTION_MAX] = { + [DEVICE_ACTION_ADD] = "add", + [DEVICE_ACTION_REMOVE] = "remove", + [DEVICE_ACTION_CHANGE] = "change", + [DEVICE_ACTION_MOVE] = "move", + [DEVICE_ACTION_ONLINE] = "online", + [DEVICE_ACTION_OFFLINE] = "offline", +}; + +DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); + +static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, + DeviceAction *_action) { + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum = 0; + const char *major = NULL, *minor = NULL; + char *value; + int r; + + assert(device); + assert(key); + assert(_major); + assert(_minor); + assert(_seqnum); + assert(_action); + + value = strchr(key, '='); + if (!value) { + log_debug("sd-device: not a key-value pair: '%s'", key); + return -EINVAL; + } + + *value = '\0'; + + value++; + + if (streq(key, "MAJOR")) + major = value; + else if (streq(key, "MINOR")) + minor = value; + else { + if (streq(key, "ACTION")) { + action = device_action_from_string(value); + if (action == _DEVICE_ACTION_INVALID) + return -EINVAL; + } else if (streq(key, "SEQNUM")) { + r = safe_atou64(value, &seqnum); + if (r < 0) + return r; + else if (seqnum == 0) + /* kernel only sends seqnum > 0 */ + return -EINVAL; + } + + r = device_amend(device, key, value); + if (r < 0) + return r; + } + + if (major != 0) + *_major = major; + + if (minor != 0) + *_minor = minor; + + if (action != _DEVICE_ACTION_INVALID) + *_action = action; + + if (seqnum > 0) + *_seqnum = seqnum; + + return 0; +} + +void device_seal(sd_device *device) { + assert(device); + + device->sealed = true; +} + +static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { + assert(device); + + if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { + log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum"); + return -EINVAL; + } + + device->sealed = true; + + return 0; +} + +int device_new_from_strv(sd_device **ret, char **strv) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + char **key; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + int r; + + assert(ret); + assert(strv); + + r = device_new_aux(&device); + if (r < 0) + return r; + + STRV_FOREACH(key, strv) { + r = device_append(device, *key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *major = NULL, *minor = NULL; + DeviceAction action = _DEVICE_ACTION_INVALID; + uint64_t seqnum; + unsigned i = 0; + int r; + + assert(ret); + assert(nulstr); + assert(len); + + r = device_new_aux(&device); + if (r < 0) + return r; + + while (i < len) { + char *key; + const char *end; + + key = (char*)&nulstr[i]; + end = memchr(key, '\0', len - i); + if (!end) { + log_debug("sd-device: failed to parse nulstr"); + return -EINVAL; + } + i += end - key + 1; + + r = device_append(device, key, &major, &minor, &seqnum, &action); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device, action, seqnum); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +static int device_update_properties_bufs(sd_device *device) { + const char *val, *prop; + _cleanup_free_ char **buf_strv = NULL; + _cleanup_free_ uint8_t *buf_nulstr = NULL; + size_t allocated_nulstr = 0; + size_t nulstr_len = 0, num = 0, i = 0; + + assert(device); + + if (!device->properties_buf_outdated) + return 0; + + FOREACH_DEVICE_PROPERTY(device, prop, val) { + size_t len = 0; + + len = strlen(prop) + 1 + strlen(val); + + buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2); + if (!buf_nulstr) + return -ENOMEM; + + strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); + nulstr_len += len + 1; + ++num; + } + + /* build buf_strv from buf_nulstr */ + buf_strv = new0(char *, num + 1); + if (!buf_strv) + return -ENOMEM; + + NULSTR_FOREACH(val, (char*) buf_nulstr) { + buf_strv[i] = (char *) val; + assert(i < num); + i++; + } + + free(device->properties_nulstr); + device->properties_nulstr = buf_nulstr; + buf_nulstr = NULL; + device->properties_nulstr_len = nulstr_len; + free(device->properties_strv); + device->properties_strv = buf_strv; + buf_strv = NULL; + + device->properties_buf_outdated = false; + + return 0; +} + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) { + int r; + + assert(device); + assert(nulstr); + assert(len); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *nulstr = device->properties_nulstr; + *len = device->properties_nulstr_len; + + return 0; +} + +int device_get_properties_strv(sd_device *device, char ***strv) { + int r; + + assert(device); + assert(strv); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + *strv = device->properties_strv; + + return 0; +} + +int device_get_devlink_priority(sd_device *device, int *priority) { + int r; + + assert(device); + assert(priority); + + r = device_read_db(device); + if (r < 0) + return r; + + *priority = device->devlink_priority; + + return 0; +} + +int device_get_watch_handle(sd_device *device, int *handle) { + int r; + + assert(device); + assert(handle); + + r = device_read_db(device); + if (r < 0) + return r; + + *handle = device->watch_handle; + + return 0; +} + +void device_set_watch_handle(sd_device *device, int handle) { + assert(device); + + device->watch_handle = handle; +} + +int device_rename(sd_device *device, const char *name) { + _cleanup_free_ char *dirname = NULL; + char *new_syspath; + const char *interface; + int r; + + assert(device); + assert(name); + + dirname = dirname_malloc(device->syspath); + if (!dirname) + return -ENOMEM; + + new_syspath = strjoina(dirname, "/", name); + + /* the user must trust that the new name is correct */ + r = device_set_syspath(device, new_syspath, false); + if (r < 0) + return r; + + r = sd_device_get_property_value(device, "INTERFACE", &interface); + if (r >= 0) { + r = device_add_property_internal(device, "INTERFACE", name); + if (r < 0) + return r; + + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ + r = device_add_property_internal(device, "INTERFACE_OLD", interface); + if (r < 0) + return r; + } else if (r != -ENOENT) + return r; + + return 0; +} + +int device_shallow_clone(sd_device *old_device, sd_device **new_device) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_new_aux(&ret); + if (r < 0) + return r; + + r = device_set_syspath(ret, old_device->syspath, false); + if (r < 0) + return r; + + r = device_set_subsystem(ret, old_device->subsystem); + if (r < 0) + return r; + + ret->devnum = old_device->devnum; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_clone_with_db(sd_device *old_device, sd_device **new_device) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(old_device); + assert(new_device); + + r = device_shallow_clone(old_device, &ret); + if (r < 0) + return r; + + r = device_read_db(ret); + if (r < 0) + return r; + + ret->sealed = true; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { + _cleanup_(sd_device_unrefp) sd_device *ret = NULL; + int r; + + assert(new_device); + assert(syspath); + assert(action); + + r = sd_device_new_from_syspath(&ret, syspath); + if (r < 0) + return r; + + r = device_read_uevent_file(ret); + if (r < 0) + return r; + + r = device_add_property_internal(ret, "ACTION", action); + if (r < 0) + return r; + + *new_device = ret; + ret = NULL; + + return 0; +} + +int device_copy_properties(sd_device *device_dst, sd_device *device_src) { + const char *property, *value; + int r; + + assert(device_dst); + assert(device_src); + + FOREACH_DEVICE_PROPERTY(device_src, property, value) { + r = device_add_property(device_dst, property, value); + if (r < 0) + return r; + } + + return 0; +} + +void device_cleanup_tags(sd_device *device) { + assert(device); + + set_free_free(device->tags); + device->tags = NULL; + device->property_tags_outdated = true; + device->tags_generation++; +} + +void device_cleanup_devlinks(sd_device *device) { + assert(device); + + set_free_free(device->devlinks); + device->devlinks = NULL; + device->property_devlinks_outdated = true; + device->devlinks_generation++; +} + +void device_remove_tag(sd_device *device, const char *tag) { + assert(device); + assert(tag); + + free(set_remove(device->tags, tag)); + device->property_tags_outdated = true; + device->tags_generation++; +} + +static int device_tag(sd_device *device, const char *tag, bool add) { + const char *id; + char *path; + int r; + + assert(device); + assert(tag); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/tags/", tag, "/", id); + + if (add) { + r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); + if (r < 0) + return r; + } else { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + } + + return 0; +} + +int device_tag_index(sd_device *device, sd_device *device_old, bool add) { + const char *tag; + int r = 0, k; + + if (add && device_old) { + /* delete possible left-over tags */ + FOREACH_DEVICE_TAG(device_old, tag) { + if (!sd_device_has_tag(device, tag)) { + k = device_tag(device_old, tag, false); + if (r >= 0 && k < 0) + r = k; + } + } + } + + FOREACH_DEVICE_TAG(device, tag) { + k = device_tag(device, tag, add); + if (r >= 0 && k < 0) + r = k; + } + + return r; +} + +static bool device_has_info(sd_device *device) { + assert(device); + + if (!set_isempty(device->devlinks)) + return true; + + if (device->devlink_priority != 0) + return true; + + if (!ordered_hashmap_isempty(device->properties_db)) + return true; + + if (!set_isempty(device->tags)) + return true; + + if (device->watch_handle >= 0) + return true; + + return false; +} + +void device_set_db_persist(sd_device *device) { + assert(device); + + device->db_persist = true; +} + +int device_update_db(sd_device *device) { + const char *id; + char *path; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *path_tmp = NULL; + bool has_info; + int r; + + assert(device); + + has_info = device_has_info(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + /* do not store anything for otherwise empty devices */ + if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) { + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; + } + + /* write a database file */ + r = mkdir_parents(path, 0755); + if (r < 0) + return r; + + r = fopen_temporary(path, &f, &path_tmp); + if (r < 0) + return r; + + /* + * set 'sticky' bit to indicate that we should not clean the + * database when we transition from initramfs to the real root + */ + if (device->db_persist) { + r = fchmod(fileno(f), 01644); + if (r < 0) { + r = -errno; + goto fail; + } + } else { + r = fchmod(fileno(f), 0644); + if (r < 0) { + r = -errno; + goto fail; + } + } + + if (has_info) { + const char *property, *value, *tag; + Iterator i; + + if (major(device->devnum) > 0) { + const char *devlink; + + FOREACH_DEVICE_DEVLINK(device, devlink) + fprintf(f, "S:%s\n", devlink + strlen("/dev/")); + + if (device->devlink_priority != 0) + fprintf(f, "L:%i\n", device->devlink_priority); + + if (device->watch_handle >= 0) + fprintf(f, "W:%i\n", device->watch_handle); + } + + if (device->usec_initialized > 0) + fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); + + ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i) + fprintf(f, "E:%s=%s\n", property, value); + + FOREACH_DEVICE_TAG(device, tag) + fprintf(f, "G:%s\n", tag); + } + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + r = rename(path_tmp, path); + if (r < 0) { + r = -errno; + goto fail; + } + + log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", + path, device->devpath); + + return 0; + +fail: + (void) unlink(path); + (void) unlink(path_tmp); + + return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath); +} + +int device_delete_db(sd_device *device) { + const char *id; + char *path; + int r; + + assert(device); + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = unlink(path); + if (r < 0 && errno != ENOENT) + return -errno; + + return 0; +} + +int device_read_db_force(sd_device *device) { + assert(device); + + return device_read_db_aux(device, true); +} diff --git a/src/libsystemd/src/sd-device/device-private.h b/src/libsystemd/src/sd-device/device-private.h new file mode 100644 index 0000000000..29b3e155fb --- /dev/null +++ b/src/libsystemd/src/sd-device/device-private.h @@ -0,0 +1,68 @@ +#pragma once + +/*** + 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 . +***/ + +#include +#include +#include + +#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); + +int device_get_id_filename(sd_device *device, const char **ret); + +int device_get_devlink_priority(sd_device *device, int *priority); +int device_get_watch_handle(sd_device *device, int *handle); +int device_get_devnode_mode(sd_device *device, mode_t *mode); +int device_get_devnode_uid(sd_device *device, uid_t *uid); +int device_get_devnode_gid(sd_device *device, gid_t *gid); + +void device_seal(sd_device *device); +void device_set_is_initialized(sd_device *device); +void device_set_watch_handle(sd_device *device, int fd); +void device_set_db_persist(sd_device *device); +void device_set_devlink_priority(sd_device *device, int priority); +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); +int device_add_devlink(sd_device *device, const char *devlink); +int device_add_property(sd_device *device, const char *property, const char *value); +int device_add_tag(sd_device *device, const char *tag); +void device_remove_tag(sd_device *device, const char *tag); +void device_cleanup_tags(sd_device *device); +void device_cleanup_devlinks(sd_device *device); + +uint64_t device_get_properties_generation(sd_device *device); +uint64_t device_get_tags_generation(sd_device *device); +uint64_t device_get_devlinks_generation(sd_device *device); + +int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len); +int device_get_properties_strv(sd_device *device, char ***strv); + +int device_rename(sd_device *device, const char *name); +int device_shallow_clone(sd_device *old_device, sd_device **new_device); +int device_clone_with_db(sd_device *old_device, sd_device **new_device); +int device_copy_properties(sd_device *device_dst, sd_device *device_src); +int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); + +int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); +int device_update_db(sd_device *device); +int device_delete_db(sd_device *device); +int device_read_db_force(sd_device *device); diff --git a/src/libsystemd/src/sd-device/device-util.h b/src/libsystemd/src/sd-device/device-util.h new file mode 100644 index 0000000000..a8aca6d8f4 --- /dev/null +++ b/src/libsystemd/src/sd-device/device-util.h @@ -0,0 +1,52 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014-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 . +***/ + +#include "basic/util.h" + +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (key = sd_device_get_property_first(device, &(value)); \ + key; \ + key = sd_device_get_property_next(device, &(value))) + +#define FOREACH_DEVICE_TAG(device, tag) \ + for (tag = sd_device_get_tag_first(device); \ + tag; \ + tag = sd_device_get_tag_next(device)) + +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (attr = sd_device_get_sysattr_first(device); \ + attr; \ + attr = sd_device_get_sysattr_next(device)) + +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (devlink = sd_device_get_devlink_first(device); \ + devlink; \ + devlink = sd_device_get_devlink_next(device)) + +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) diff --git a/src/libsystemd/src/sd-device/sd-device.c b/src/libsystemd/src/sd-device/sd-device.c new file mode 100644 index 0000000000..79c2c2caeb --- /dev/null +++ b/src/libsystemd/src/sd-device/sd-device.c @@ -0,0 +1,1935 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/strxcpyx.h" +#include "basic/util.h" + +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "sd-device.h" + +int device_new_aux(sd_device **ret) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + + assert(ret); + + device = new0(sd_device, 1); + if (!device) + return -ENOMEM; + + device->n_ref = 1; + device->watch_handle = -1; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ sd_device *sd_device_ref(sd_device *device) { + if (device) + assert_se(++ device->n_ref >= 2); + + return device; +} + +_public_ sd_device *sd_device_unref(sd_device *device) { + if (device && -- device->n_ref == 0) { + sd_device_unref(device->parent); + free(device->syspath); + free(device->sysname); + free(device->devtype); + free(device->devname); + free(device->subsystem); + free(device->driver_subsystem); + free(device->driver); + free(device->id_filename); + free(device->properties_strv); + free(device->properties_nulstr); + + ordered_hashmap_free_free_free(device->properties); + ordered_hashmap_free_free_free(device->properties_db); + hashmap_free_free_free(device->sysattr_values); + set_free_free(device->sysattrs); + set_free_free(device->tags); + set_free_free(device->devlinks); + + free(device); + } + + return NULL; +} + +int device_add_property_aux(sd_device *device, const char *_key, const char *_value, bool db) { + OrderedHashmap **properties; + + assert(device); + assert(_key); + + if (db) + properties = &device->properties_db; + else + properties = &device->properties; + + if (_value) { + _cleanup_free_ char *key = NULL, *value = NULL, *old_key = NULL, *old_value = NULL; + int r; + + r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops); + if (r < 0) + return r; + + key = strdup(_key); + if (!key) + return -ENOMEM; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); + + r = ordered_hashmap_replace(*properties, key, value); + if (r < 0) + return r; + + key = NULL; + value = NULL; + } else { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + value = ordered_hashmap_remove2(*properties, _key, (void**) &key); + } + + if (!db) { + device->properties_generation++; + device->properties_buf_outdated = true; + } + + return 0; +} + +int device_add_property_internal(sd_device *device, const char *key, const char *value) { + return device_add_property_aux(device, key, value, false); +} + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { + _cleanup_free_ char *syspath = NULL; + const char *devpath; + int r; + + assert(device); + assert(_syspath); + + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) { + log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath); + return -EINVAL; + } + + if (verify) { + r = readlink_and_canonicalize(_syspath, &syspath); + if (r == -ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + else if (r == -EINVAL) { + /* not a symlink */ + syspath = canonicalize_file_name(_syspath); + if (!syspath) { + if (errno == ENOENT) + /* the device does not exist (any more?) */ + return -ENODEV; + + return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath); + } + } else if (r < 0) { + log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath); + return r; + } + + if (path_startswith(syspath, "/sys/devices/")) { + char *path; + + /* all 'devices' require an 'uevent' file */ + path = strjoina(syspath, "/uevent"); + r = access(path, F_OK); + if (r < 0) { + if (errno == ENOENT) + /* this is not a valid device */ + return -ENODEV; + + log_debug("sd-device: %s does not have an uevent file: %m", syspath); + return -errno; + } + } else { + /* everything else just needs to be a directory */ + if (!is_dir(syspath, false)) + return -ENODEV; + } + } else { + syspath = strdup(_syspath); + if (!syspath) + return -ENOMEM; + } + + devpath = syspath + strlen("/sys"); + + r = device_add_property_internal(device, "DEVPATH", devpath); + if (r < 0) + return r; + + free(device->syspath); + device->syspath = syspath; + syspath = NULL; + + device->devpath = devpath; + + return 0; +} + +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(syspath, -EINVAL); + + r = device_new_aux(&device); + if (r < 0) + return r; + + r = device_set_syspath(device, syspath, true); + if (r < 0) + return r; + + *ret = device; + device = NULL; + + return 0; +} + +_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { + char *syspath; + char id[DECIMAL_STR_MAX(unsigned) * 2 + 1]; + + assert_return(ret, -EINVAL); + assert_return(type == 'b' || type == 'c', -EINVAL); + + /* use /sys/dev/{block,char}/: link */ + snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); + + syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); + + return sd_device_new_from_syspath(ret, syspath); +} + +_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { + char *name, *syspath; + size_t len = 0; + + assert_return(ret, -EINVAL); + assert_return(subsystem, -EINVAL); + assert_return(sysname, -EINVAL); + + if (streq(subsystem, "subsystem")) { + syspath = strjoina("/sys/subsystem/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "module")) { + syspath = strjoina("/sys/module/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } else if (streq(subsystem, "drivers")) { + char subsys[PATH_MAX]; + char *driver; + + strscpy(subsys, sizeof(subsys), sysname); + driver = strchr(subsys, ':'); + if (driver) { + driver[0] = '\0'; + driver++; + + syspath = strjoina("/sys/subsystem/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + } + } + + /* 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/", name); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + syspath = strjoina("/sys/class/", subsystem, "/", name); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + + return -ENODEV; +} + +int device_set_devtype(sd_device *device, const char *_devtype) { + _cleanup_free_ char *devtype = NULL; + int r; + + assert(device); + assert(_devtype); + + devtype = strdup(_devtype); + if (!devtype) + return -ENOMEM; + + r = device_add_property_internal(device, "DEVTYPE", devtype); + if (r < 0) + return r; + + free(device->devtype); + device->devtype = devtype; + devtype = NULL; + + return 0; +} + +int device_set_ifindex(sd_device *device, const char *_ifindex) { + int ifindex, r; + + assert(device); + assert(_ifindex); + + r = parse_ifindex(_ifindex, &ifindex); + if (r < 0) + return r; + + r = device_add_property_internal(device, "IFINDEX", _ifindex); + if (r < 0) + return r; + + device->ifindex = ifindex; + + return 0; +} + +int device_set_devname(sd_device *device, const char *_devname) { + _cleanup_free_ char *devname = NULL; + int r; + + assert(device); + assert(_devname); + + if (_devname[0] != '/') { + r = asprintf(&devname, "/dev/%s", _devname); + if (r < 0) + return -ENOMEM; + } else { + devname = strdup(_devname); + if (!devname) + return -ENOMEM; + } + + r = device_add_property_internal(device, "DEVNAME", devname); + if (r < 0) + return r; + + free(device->devname); + device->devname = devname; + devname = NULL; + + return 0; +} + +int device_set_devmode(sd_device *device, const char *_devmode) { + unsigned devmode; + int r; + + assert(device); + assert(_devmode); + + r = safe_atou(_devmode, &devmode); + if (r < 0) + return r; + + if (devmode > 07777) + return -EINVAL; + + r = device_add_property_internal(device, "DEVMODE", _devmode); + if (r < 0) + return r; + + device->devmode = devmode; + + return 0; +} + +int device_set_devnum(sd_device *device, const char *major, const char *minor) { + unsigned maj = 0, min = 0; + int r; + + assert(device); + assert(major); + + r = safe_atou(major, &maj); + if (r < 0) + return r; + if (!maj) + return 0; + + if (minor) { + r = safe_atou(minor, &min); + if (r < 0) + return r; + } + + r = device_add_property_internal(device, "MAJOR", major); + if (r < 0) + return r; + + if (minor) { + r = device_add_property_internal(device, "MINOR", minor); + if (r < 0) + return r; + } + + device->devnum = makedev(maj, min); + + return 0; +} + +static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) { + int r; + + assert(device); + assert(key); + assert(value); + assert(major); + assert(minor); + + if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return r; + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return r; + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return r; + } else if (streq(key, "MAJOR")) + *major = value; + else if (streq(key, "MINOR")) + *minor = value; + else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return r; + } + + return 0; +} + +int device_read_uevent_file(sd_device *device) { + _cleanup_free_ char *uevent = NULL; + const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; + char *path; + size_t uevent_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->uevent_loaded || device->sealed) + return 0; + + device->uevent_loaded = true; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/uevent"); + + r = read_full_file(path, &uevent, &uevent_len); + if (r == -EACCES) + /* empty uevent files may be write-only */ + return 0; + else if (r == -ENOENT) + /* some devices may not have uevent files, see set_syspath() */ + return 0; + else if (r < 0) { + log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path); + return r; + } + + for (i = 0; i < uevent_len; i++) + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, uevent[i])) { + key = &uevent[i]; + + state = KEY; + } + + break; + case KEY: + if (uevent[i] == '=') { + uevent[i] = '\0'; + + state = PRE_VALUE; + } else if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + log_debug("sd-device: ignoring invalid uevent line '%s'", key); + + state = PRE_KEY; + } + + break; + case PRE_VALUE: + value = &uevent[i]; + state = VALUE; + + /* fall through to handle empty property */ + case VALUE: + if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + + r = handle_uevent_line(device, key, value, &major, &minor); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing uevent file"); + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path); + } + + return 0; +} + +_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { + int r; + + assert_return(device, -EINVAL); + assert_return(ifindex, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *ifindex = device->ifindex; + + return 0; +} + +_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { + int r; + + assert_return(ret, -EINVAL); + assert_return(id, -EINVAL); + + switch (id[0]) { + case 'b': + case 'c': + { + char type; + int maj, min; + + r = sscanf(id, "%c%i:%i", &type, &maj, &min); + if (r != 3) + return -EINVAL; + + return sd_device_new_from_devnum(ret, type, makedev(maj, min)); + } + case 'n': + { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_close_ int sk = -1; + struct ifreq ifr = {}; + int ifindex; + + r = parse_ifindex(&id[1], &ifr.ifr_ifindex); + if (r < 0) + return r; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return -errno; + + r = ioctl(sk, SIOCGIFNAME, &ifr); + if (r < 0) + return -errno; + + r = sd_device_new_from_subsystem_sysname(&device, "net", ifr.ifr_name); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + /* this is racey, so we might end up with the wrong device */ + if (ifr.ifr_ifindex != ifindex) + return -ENODEV; + + *ret = device; + device = NULL; + + return 0; + } + case '+': + { + char subsys[PATH_MAX]; + char *sysname; + + (void)strscpy(subsys, sizeof(subsys), id + 1); + sysname = strchr(subsys, ':'); + if (!sysname) + return -EINVAL; + + sysname[0] = '\0'; + sysname++; + + return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); + } + default: + return -EINVAL; + } +} + +_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + assert(path_startswith(device->syspath, "/sys/")); + + *ret = device->syspath; + + return 0; +} + +static int device_new_from_child(sd_device **ret, sd_device *child) { + _cleanup_free_ char *path = NULL; + const char *subdir, *syspath; + int r; + + assert(ret); + assert(child); + + r = sd_device_get_syspath(child, &syspath); + if (r < 0) + return r; + + path = strdup(syspath); + if (!path) + return -ENOMEM; + subdir = path + strlen("/sys"); + + for (;;) { + char *pos; + + pos = strrchr(subdir, '/'); + if (!pos || pos < subdir + 2) + break; + + *pos = '\0'; + + r = sd_device_new_from_syspath(ret, path); + if (r < 0) + continue; + + return 0; + } + + return -ENODEV; +} + +_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { + + assert_return(ret, -EINVAL); + assert_return(child, -EINVAL); + + if (!child->parent_set) { + child->parent_set = true; + + (void)device_new_from_child(&child->parent, child); + } + + if (!child->parent) + return -ENOENT; + + *ret = child->parent; + + return 0; +} + +int device_set_subsystem(sd_device *device, const char *_subsystem) { + _cleanup_free_ char *subsystem = NULL; + int r; + + assert(device); + assert(_subsystem); + + subsystem = strdup(_subsystem); + if (!subsystem) + return -ENOMEM; + + r = device_add_property_internal(device, "SUBSYSTEM", subsystem); + if (r < 0) + return r; + + free(device->subsystem); + device->subsystem = subsystem; + subsystem = NULL; + + device->subsystem_set = true; + + return 0; +} + +static int device_set_drivers_subsystem(sd_device *device, const char *_subsystem) { + _cleanup_free_ char *subsystem = NULL; + int r; + + assert(device); + assert(_subsystem); + assert(*_subsystem); + + subsystem = strdup(_subsystem); + if (!subsystem) + return -ENOMEM; + + r = device_set_subsystem(device, "drivers"); + if (r < 0) + return r; + + free(device->driver_subsystem); + device->driver_subsystem = subsystem; + subsystem = NULL; + + return 0; +} + +_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { + const char *syspath, *drivers = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(device, -EINVAL); + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + if (!device->subsystem_set) { + _cleanup_free_ char *subsystem = NULL; + char *path; + + /* read 'subsystem' link */ + path = strjoina(syspath, "/subsystem"); + r = readlink_value(path, &subsystem); + if (r >= 0) + r = device_set_subsystem(device, subsystem); + /* use implicit names */ + else if (path_startswith(device->devpath, "/module/")) + r = device_set_subsystem(device, "module"); + else if (!(drivers = strstr(syspath, "/drivers/")) && + (path_startswith(device->devpath, "/subsystem/") || + path_startswith(device->devpath, "/class/") || + path_startswith(device->devpath, "/bus/"))) + r = device_set_subsystem(device, "subsystem"); + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath); + + device->subsystem_set = true; + } else if (!device->driver_subsystem_set) + drivers = strstr(syspath, "/drivers/"); + + if (!device->driver_subsystem_set) { + if (drivers) { + _cleanup_free_ char *subpath = NULL; + + subpath = strndup(syspath, drivers - syspath); + if (!subpath) + r = -ENOMEM; + else { + const char *subsys; + + subsys = strrchr(subpath, '/'); + if (!subsys) + r = -EINVAL; + else + r = device_set_drivers_subsystem(device, subsys + 1); + } + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "sd-device: could not set subsystem for driver %s: %m", device->devpath); + } + + device->driver_subsystem_set = true; + } + + if (!device->subsystem) + return -ENOENT; + + *ret = device->subsystem; + + return 0; +} + +_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { + int r; + + assert(devtype); + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devtype = device->devtype; + + return 0; +} + +_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) { + sd_device *parent = NULL; + int r; + + assert_return(child, -EINVAL); + assert_return(subsystem, -EINVAL); + + r = sd_device_get_parent(child, &parent); + while (r >= 0) { + const char *parent_subsystem = NULL; + const char *parent_devtype = NULL; + + (void)sd_device_get_subsystem(parent, &parent_subsystem); + if (streq_ptr(parent_subsystem, subsystem)) { + if (!devtype) + break; + + (void)sd_device_get_devtype(parent, &parent_devtype); + if (streq_ptr(parent_devtype, devtype)) + break; + } + r = sd_device_get_parent(parent, &parent); + } + + if (r < 0) + return r; + + *ret = parent; + + return 0; +} + +_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { + int r; + + assert_return(device, -EINVAL); + assert_return(devnum, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + *devnum = device->devnum; + + return 0; +} + +int device_set_driver(sd_device *device, const char *_driver) { + _cleanup_free_ char *driver = NULL; + int r; + + assert(device); + assert(_driver); + + driver = strdup(_driver); + if (!driver) + return -ENOMEM; + + r = device_add_property_internal(device, "DRIVER", driver); + if (r < 0) + return r; + + free(device->driver); + device->driver = driver; + driver = NULL; + + device->driver_set = true; + + return 0; +} + +_public_ int sd_device_get_driver(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->driver_set) { + _cleanup_free_ char *driver = NULL; + const char *syspath; + char *path; + int r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/driver"); + r = readlink_value(path, &driver); + if (r >= 0) { + r = device_set_driver(device, driver); + if (r < 0) + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } else if (r == -ENOENT) + device->driver_set = true; + else + return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath); + } + + if (!device->driver) + return -ENOENT; + + *ret = device->driver; + + return 0; +} + +_public_ int sd_device_get_devpath(sd_device *device, const char **devpath) { + assert_return(device, -EINVAL); + assert_return(devpath, -EINVAL); + + assert(device->devpath); + assert(device->devpath[0] == '/'); + + *devpath = device->devpath; + + return 0; +} + +_public_ int sd_device_get_devname(sd_device *device, const char **devname) { + int r; + + assert_return(device, -EINVAL); + assert_return(devname, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (!device->devname) + return -ENOENT; + + assert(path_startswith(device->devname, "/dev/")); + + *devname = device->devname; + + return 0; +} + +static int device_set_sysname(sd_device *device) { + _cleanup_free_ char *sysname = NULL; + const char *sysnum = NULL; + const char *pos; + size_t len = 0; + + pos = strrchr(device->devpath, '/'); + if (!pos) + return -EINVAL; + pos++; + + /* devpath is not a root directory */ + if (*pos == '\0' || pos <= device->devpath) + return -EINVAL; + + sysname = strdup(pos); + if (!sysname) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + while (sysname[len] != '\0') { + if (sysname[len] == '!') + sysname[len] = '/'; + + len++; + } + + /* trailing number */ + while (len > 0 && isdigit(sysname[--len])) + sysnum = &sysname[len]; + + if (len == 0) + sysnum = NULL; + + free(device->sysname); + device->sysname = sysname; + sysname = NULL; + + device->sysnum = sysnum; + + device->sysname_set = true; + + return 0; +} + +_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + assert_return(device->sysname, -ENOENT); + + *ret = device->sysname; + + return 0; +} + +_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + assert_return(ret, -EINVAL); + + if (!device->sysname_set) { + r = device_set_sysname(device); + if (r < 0) + return r; + } + + *ret = device->sysnum; + + return 0; +} + +static bool is_valid_tag(const char *tag) { + assert(tag); + + return !strchr(tag, ':') && !strchr(tag, ' '); +} + +int device_add_tag(sd_device *device, const char *tag) { + int r; + + assert(device); + assert(tag); + + if (!is_valid_tag(tag)) + return -EINVAL; + + r = set_ensure_allocated(&device->tags, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->tags, tag); + if (r < 0) + return r; + + device->tags_generation++; + device->property_tags_outdated = true; + + return 0; +} + +int device_add_devlink(sd_device *device, const char *devlink) { + int r; + + assert(device); + assert(devlink); + + r = set_ensure_allocated(&device->devlinks, &string_hash_ops); + if (r < 0) + return r; + + r = set_put_strdup(device->devlinks, devlink); + if (r < 0) + return r; + + device->devlinks_generation++; + device->property_devlinks_outdated = true; + + return 0; +} + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + return device_add_property_internal(device, key, value); +} + +int device_set_usec_initialized(sd_device *device, const char *initialized) { + uint64_t usec_initialized; + int r; + + assert(device); + assert(initialized); + + r = safe_atou64(initialized, &usec_initialized); + if (r < 0) + return r; + + r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); + if (r < 0) + return r; + + device->usec_initialized = usec_initialized; + + return 0; +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + char *path; + int r; + + assert(device); + assert(value); + + switch (key) { + case 'G': + r = device_add_tag(device, value); + if (r < 0) + return r; + + break; + case 'S': + path = strjoina("/dev/", value); + r = device_add_devlink(device, path); + if (r < 0) + return r; + + break; + case 'E': + r = device_add_property_internal_from_string(device, value); + if (r < 0) + return r; + + break; + case 'I': + r = device_set_usec_initialized(device, value); + if (r < 0) + return r; + + break; + case 'L': + r = safe_atoi(value, &device->devlink_priority); + if (r < 0) + return r; + + break; + case 'W': + r = safe_atoi(value, &device->watch_handle); + if (r < 0) + return r; + + break; + default: + log_debug("device db: unknown key '%c'", key); + } + + return 0; +} + +int device_get_id_filename(sd_device *device, const char **ret) { + assert(device); + assert(ret); + + if (!device->id_filename) { + _cleanup_free_ char *id = NULL; + const char *subsystem; + dev_t devnum; + int ifindex, r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0) + return r; + + r = sd_device_get_devnum(device, &devnum); + if (r < 0) + return r; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) + return r; + + if (major(devnum) > 0) { + assert(subsystem); + + /* use dev_t — b259:131072, c254:0 */ + r = asprintf(&id, "%c%u:%u", + streq(subsystem, "block") ? 'b' : 'c', + major(devnum), minor(devnum)); + if (r < 0) + return -ENOMEM; + } else if (ifindex > 0) { + /* use netdev ifindex — n3 */ + r = asprintf(&id, "n%u", ifindex); + if (r < 0) + return -ENOMEM; + } else { + /* use $subsys:$sysname — pci:0000:00:1f.2 + * sysname() has '!' translated, get it from devpath + */ + const char *sysname; + + sysname = basename(device->devpath); + if (!sysname) + return -EINVAL; + + if (!subsystem) + return -EINVAL; + + if (streq(subsystem, "drivers")) { + /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem + * encoded as well */ + r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname); + if (r < 0) + return -ENOMEM; + } else { + r = asprintf(&id, "+%s:%s", subsystem, sysname); + if (r < 0) + return -ENOMEM; + } + } + + device->id_filename = id; + id = NULL; + } + + *ret = device->id_filename; + + return 0; +} + +int device_read_db_aux(sd_device *device, bool force) { + _cleanup_free_ char *db = NULL; + char *path; + const char *id, *value; + char key; + size_t db_len; + unsigned i; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + if (device->db_loaded || (!force && device->sealed)) + return 0; + + device->db_loaded = true; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + r = read_full_file(path, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + else + return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path); + } + + /* devices with a database entry are initialized */ + device->is_initialized = true; + + for (i = 0; i < db_len; i++) { + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_debug("sd-device: ignoring invalid db entry with key '%c'", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached("invalid state when parsing db"); + } + } + + return 0; +} + +static int device_read_db(sd_device *device) { + return device_read_db_aux(device, false); +} + +_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) { + int r; + + assert_return(device, -EINVAL); + assert_return(initialized, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + *initialized = device->is_initialized; + + return 0; +} + +_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) { + usec_t now_ts; + int r; + + assert_return(device, -EINVAL); + assert_return(usec, -EINVAL); + + r = device_read_db(device); + if (r < 0) + return r; + + if (!device->is_initialized) + return -EBUSY; + + if (!device->usec_initialized) + return -ENODATA; + + now_ts = now(clock_boottime_or_monotonic()); + + if (now_ts < device->usec_initialized) + return -EIO; + + *usec = now_ts - device->usec_initialized; + + return 0; +} + +_public_ const char *sd_device_get_tag_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->tags_iterator_generation = device->tags_generation; + device->tags_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->tags, &device->tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_tag_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->tags_iterator_generation != device->tags_generation) + return NULL; + + (void) set_iterate(device->tags, &device->tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->devlinks_iterator_generation = device->devlinks_generation; + device->devlinks_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->devlinks_iterator_generation != device->devlinks_generation) + return NULL; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} + +static int device_properties_prepare(sd_device *device) { + int r; + + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->property_devlinks_outdated) { + _cleanup_free_ char *devlinks = NULL; + size_t devlinks_allocated = 0, devlinks_len = 0; + const char *devlink; + + for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) { + char *e; + + if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2)) + return -ENOMEM; + if (devlinks_len > 0) + stpcpy(devlinks + devlinks_len++, " "); + e = stpcpy(devlinks + devlinks_len, devlink); + devlinks_len = e - devlinks; + } + + r = device_add_property_internal(device, "DEVLINKS", devlinks); + if (r < 0) + return r; + + device->property_devlinks_outdated = false; + } + + if (device->property_tags_outdated) { + _cleanup_free_ char *tags = NULL; + size_t tags_allocated = 0, tags_len = 0; + const char *tag; + + if (!GREEDY_REALLOC(tags, tags_allocated, 2)) + return -ENOMEM; + stpcpy(tags, ":"); + tags_len++; + + for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { + char *e; + + if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) + return -ENOMEM; + e = stpcpy(stpcpy(tags + tags_len, tag), ":"); + tags_len = e - tags; + } + + r = device_add_property_internal(device, "TAGS", tags); + if (r < 0) + return r; + + device->property_tags_outdated = false; + } + + return 0; +} + +_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + device->properties_iterator_generation = device->properties_generation; + device->properties_iterator = ITERATOR_FIRST; + + ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { + const char *key; + const char *value; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + if (device->properties_iterator_generation != device->properties_generation) + return NULL; + + ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)&value, (const void**)&key); + + if (_value) + *_value = value; + + return key; +} + +static int device_sysattrs_read_all(sd_device *device) { + _cleanup_closedir_ DIR *dir = NULL; + const char *syspath; + struct dirent *dent; + int r; + + assert(device); + + if (device->sysattrs_read) + return 0; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + dir = opendir(syspath); + if (!dir) + return -errno; + + r = set_ensure_allocated(&device->sysattrs, &string_hash_ops); + if (r < 0) + return r; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *path; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) + continue; + + path = strjoina(syspath, "/", dent->d_name); + + if (lstat(path, &statbuf) != 0) + continue; + + if (!(statbuf.st_mode & S_IRUSR)) + continue; + + r = set_put_strdup(device->sysattrs, dent->d_name); + if (r < 0) + return r; + } + + device->sysattrs_read = true; + + return 0; +} + +_public_ const char *sd_device_get_sysattr_first(sd_device *device) { + void *v; + int r; + + assert_return(device, NULL); + + if (!device->sysattrs_read) { + r = device_sysattrs_read_all(device); + if (r < 0) { + errno = -r; + return NULL; + } + } + + device->sysattrs_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_sysattr_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + if (!device->sysattrs_read) + return NULL; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ int sd_device_has_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + (void) device_read_db(device); + + return !!set_contains(device->tags, tag); +} + +_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { + char *value; + int r; + + assert_return(device, -EINVAL); + assert_return(key, -EINVAL); + assert_return(_value, -EINVAL); + + r = device_properties_prepare(device); + if (r < 0) + return r; + + value = ordered_hashmap_get(device->properties, key); + if (!value) + return -ENOENT; + + *_value = value; + + return 0; +} + +/* replaces the value if it already exists */ +static int device_add_sysattr_value(sd_device *device, const char *_key, char *value) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value_old = NULL; + int r; + + assert(device); + assert(_key); + + r = hashmap_ensure_allocated(&device->sysattr_values, &string_hash_ops); + if (r < 0) + return r; + + value_old = hashmap_remove2(device->sysattr_values, _key, (void **)&key); + if (!key) { + key = strdup(_key); + if (!key) + return -ENOMEM; + } + + r = hashmap_put(device->sysattr_values, key, value); + if (r < 0) + return r; + + key = NULL; + + return 0; +} + +static int device_get_sysattr_value(sd_device *device, const char *_key, const char **_value) { + const char *key = NULL, *value; + + assert(device); + assert(_key); + + value = hashmap_get2(device->sysattr_values, _key, (void **) &key); + if (!key) + return -ENOENT; + + if (_value) + *_value = value; + + return 0; +} + +/* We cache all sysattr lookups. If an attribute does not exist, it is stored + * with a NULL value in the cache, otherwise the returned string is stored */ +_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) { + _cleanup_free_ char *value = NULL; + const char *syspath, *cached_value = NULL; + char *path; + struct stat statbuf; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + /* look for possibly already cached result */ + r = device_get_sysattr_value(device, sysattr, &cached_value); + if (r != -ENOENT) { + if (r < 0) + return r; + + if (!cached_value) + /* we looked up the sysattr before and it did not exist */ + return -ENOENT; + + if (_value) + *_value = cached_value; + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + /* remember that we could not access the sysattr */ + r = device_add_sysattr_value(device, sysattr, NULL); + if (r < 0) + return r; + + return -ENOENT; + } else if (S_ISLNK(statbuf.st_mode)) { + /* Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. */ + if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { + r = readlink_value(path, &value); + if (r < 0) + return r; + } else + return -EINVAL; + } else if (S_ISDIR(statbuf.st_mode)) { + /* skip directories */ + return -EINVAL; + } else if (!(statbuf.st_mode & S_IRUSR)) { + /* skip non-readable files */ + return -EPERM; + } else { + size_t size; + + /* read attribute value */ + r = read_full_file(path, &value, &size); + if (r < 0) + return r; + + /* drop trailing newlines */ + while (size > 0 && value[--size] == '\n') + value[size] = '\0'; + } + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + *_value = value; + value = NULL; + + return 0; +} + +static void device_remove_sysattr_value(sd_device *device, const char *_key) { + _cleanup_free_ char *key = NULL; + _cleanup_free_ char *value = NULL; + + assert(device); + assert(_key); + + value = hashmap_remove2(device->sysattr_values, _key, (void **) &key); + + return; +} + +/* set the attribute and save it in the cache. If a NULL value is passed the + * attribute is cleared from the cache */ +_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *_value) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *value = NULL; + const char *syspath; + char *path; + struct stat statbuf; + size_t value_len = 0; + ssize_t size; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + if (!_value) { + device_remove_sysattr_value(device, sysattr); + + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/", sysattr); + r = lstat(path, &statbuf); + if (r < 0) { + value = strdup(""); + if (!value) + return -ENOMEM; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + return -ENXIO; + } + + if (S_ISLNK(statbuf.st_mode)) + return -EINVAL; + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + return -EISDIR; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + return -EACCES; + + value_len = strlen(_value); + + /* drop trailing newlines */ + while (value_len > 0 && _value[value_len - 1] == '\n') + _value[--value_len] = '\0'; + + /* value length is limited to 4k */ + if (value_len > 4096) + return -EINVAL; + + fd = open(path, O_WRONLY | O_CLOEXEC); + if (fd < 0) + return -errno; + + value = strdup(_value); + if (!value) + return -ENOMEM; + + size = write(fd, value, value_len); + if (size < 0) + return -errno; + + if ((size_t)size != value_len) + return -EIO; + + r = device_add_sysattr_value(device, sysattr, value); + if (r < 0) + return r; + + value = NULL; + + return 0; +} diff --git a/src/libsystemd/src/sd-device/sd-device.h b/src/libsystemd/src/sd-device/sd-device.h new file mode 100644 index 0000000000..5e32fc6110 --- /dev/null +++ b/src/libsystemd/src/sd-device/sd-device.h @@ -0,0 +1,101 @@ +#ifndef foosddevicehfoo +#define foosddevicehfoo + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2014-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 . +***/ + +#include +#include +#include + +#include + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; + +/* device */ + +sd_device *sd_device_ref(sd_device *device); +sd_device *sd_device_unref(sd_device *device); + +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); +int sd_device_new_from_device_id(sd_device **ret, const char *id); + +int sd_device_get_parent(sd_device *child, sd_device **ret); +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); + +int sd_device_get_syspath(sd_device *device, const char **ret); +int sd_device_get_subsystem(sd_device *device, const char **ret); +int sd_device_get_devtype(sd_device *device, const char **ret); +int sd_device_get_devnum(sd_device *device, dev_t *devnum); +int sd_device_get_ifindex(sd_device *device, int *ifindex); +int sd_device_get_driver(sd_device *device, const char **ret); +int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devname(sd_device *device, const char **ret); +int sd_device_get_sysname(sd_device *device, const char **ret); +int sd_device_get_sysnum(sd_device *device, const char **ret); + +int sd_device_get_is_initialized(sd_device *device, int *initialized); +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec); + +const char *sd_device_get_tag_first(sd_device *device); +const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_devlink_first(sd_device *device); +const char *sd_device_get_devlink_next(sd_device *device); +const char *sd_device_get_property_first(sd_device *device, const char **value); +const char *sd_device_get_property_next(sd_device *device, const char **value); +const char *sd_device_get_sysattr_first(sd_device *device); +const char *sd_device_get_sysattr_next(sd_device *device); + +int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_get_property_value(sd_device *device, const char *key, const char **value); +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); + +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *value); + +/* device enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); + +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); + +int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); +int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); +int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); +int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); +int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); + +_SD_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/libsystemd/src/sd-event/Makefile b/src/libsystemd/src/sd-event/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-event/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-event/sd-event.c b/src/libsystemd/src/sd-event/sd-event.c new file mode 100644 index 0000000000..495e97eea2 --- /dev/null +++ b/src/libsystemd/src/sd-event/sd-event.c @@ -0,0 +1,2884 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/prioq.h" +#include "basic/process-util.h" +#include "basic/set.h" +#include "basic/signal-util.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "basic/util.h" + +#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) + +typedef enum EventSourceType { + SOURCE_IO, + SOURCE_TIME_REALTIME, + SOURCE_TIME_BOOTTIME, + SOURCE_TIME_MONOTONIC, + SOURCE_TIME_REALTIME_ALARM, + SOURCE_TIME_BOOTTIME_ALARM, + SOURCE_SIGNAL, + SOURCE_CHILD, + SOURCE_DEFER, + SOURCE_POST, + SOURCE_EXIT, + SOURCE_WATCHDOG, + _SOURCE_EVENT_SOURCE_TYPE_MAX, + _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 { + WAKEUP_NONE, + WAKEUP_EVENT_SOURCE, + WAKEUP_CLOCK_DATA, + WAKEUP_SIGNAL_DATA, + _WAKEUP_TYPE_MAX, + _WAKEUP_TYPE_INVALID = -1, +} WakeupType; + +#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) + +struct sd_event_source { + WakeupType wakeup; + + unsigned n_ref; + + sd_event *event; + void *userdata; + sd_event_handler_t prepare; + + char *description; + + EventSourceType type:5; + int enabled:3; + bool pending:1; + bool dispatching:1; + bool floating:1; + + int64_t priority; + unsigned pending_index; + unsigned prepare_index; + uint64_t pending_iteration; + uint64_t prepare_iteration; + + LIST_FIELDS(sd_event_source, sources); + + union { + struct { + sd_event_io_handler_t callback; + int fd; + uint32_t events; + uint32_t revents; + bool registered:1; + } io; + struct { + sd_event_time_handler_t callback; + usec_t next, accuracy; + unsigned earliest_index; + unsigned latest_index; + } time; + struct { + sd_event_signal_handler_t callback; + struct signalfd_siginfo siginfo; + int sig; + } signal; + struct { + sd_event_child_handler_t callback; + siginfo_t siginfo; + pid_t pid; + int options; + } child; + struct { + sd_event_handler_t callback; + } defer; + struct { + sd_event_handler_t callback; + } post; + struct { + sd_event_handler_t callback; + unsigned prioq_index; + } exit; + }; +}; + +struct clock_data { + WakeupType wakeup; + int fd; + + /* For all clocks we maintain two priority queues each, one + * ordered for the earliest times the events may be + * dispatched, and one ordered by the latest times they must + * have been dispatched. The range between the top entries in + * the two prioqs is the time window we can freely schedule + * wakeups in */ + + Prioq *earliest; + Prioq *latest; + usec_t next; + + bool needs_rearm:1; +}; + +struct signal_data { + WakeupType wakeup; + + /* For each priority we maintain one signal fd, so that we + * only have to dequeue a single event per priority at a + * time. */ + + int fd; + int64_t priority; + sigset_t sigset; + sd_event_source *current; +}; + +struct sd_event { + unsigned n_ref; + + int epoll_fd; + int watchdog_fd; + + Prioq *pending; + Prioq *prepare; + + /* timerfd_create() only supports these five clocks so far. We + * can add support for more clocks when the kernel learns to + * deal with them, too. */ + struct clock_data realtime; + struct clock_data boottime; + struct clock_data monotonic; + struct clock_data realtime_alarm; + struct clock_data boottime_alarm; + + usec_t perturb; + + sd_event_source **signal_sources; /* indexed by signal number */ + Hashmap *signal_data; /* indexed by priority */ + + Hashmap *child_sources; + unsigned n_enabled_child_sources; + + Set *post_sources; + + Prioq *exit; + + pid_t original_pid; + + uint64_t iteration; + triple_timestamp timestamp; + int state; + + bool exit_requested:1; + bool need_process_child:1; + bool watchdog:1; + bool profile_delays:1; + + int exit_code; + + pid_t tid; + sd_event **default_event_ptr; + + usec_t watchdog_last, watchdog_period; + + 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); + +static int pending_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(x->pending); + assert(y->pending); + + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + + /* Lower priority values first */ + if (x->priority < y->priority) + return -1; + if (x->priority > y->priority) + return 1; + + /* Older entries first */ + if (x->pending_iteration < y->pending_iteration) + return -1; + if (x->pending_iteration > y->pending_iteration) + return 1; + + return 0; +} + +static int prepare_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(x->prepare); + assert(y->prepare); + + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + + /* Move most recently prepared ones last, so that we can stop + * preparing as soon as we hit one that has already been + * prepared in the current iteration */ + if (x->prepare_iteration < y->prepare_iteration) + return -1; + if (x->prepare_iteration > y->prepare_iteration) + return 1; + + /* Lower priority values first */ + if (x->priority < y->priority) + return -1; + if (x->priority > y->priority) + return 1; + + return 0; +} + +static int earliest_time_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(EVENT_SOURCE_IS_TIME(x->type)); + assert(x->type == y->type); + + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + + /* Move the pending ones to the end */ + if (!x->pending && y->pending) + return -1; + if (x->pending && !y->pending) + return 1; + + /* Order by time */ + if (x->time.next < y->time.next) + return -1; + if (x->time.next > y->time.next) + return 1; + + return 0; +} + +static usec_t time_event_source_latest(const sd_event_source *s) { + return usec_add(s->time.next, s->time.accuracy); +} + +static int latest_time_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(EVENT_SOURCE_IS_TIME(x->type)); + assert(x->type == y->type); + + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + + /* Move the pending ones to the end */ + if (!x->pending && y->pending) + return -1; + if (x->pending && !y->pending) + return 1; + + /* Order by time */ + if (time_event_source_latest(x) < time_event_source_latest(y)) + return -1; + if (time_event_source_latest(x) > time_event_source_latest(y)) + return 1; + + return 0; +} + +static int exit_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + + assert(x->type == SOURCE_EXIT); + assert(y->type == SOURCE_EXIT); + + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + + /* Lower priority values first */ + if (x->priority < y->priority) + return -1; + if (x->priority > y->priority) + return 1; + + return 0; +} + +static void free_clock_data(struct clock_data *d) { + assert(d); + assert(d->wakeup == WAKEUP_CLOCK_DATA); + + safe_close(d->fd); + prioq_free(d->earliest); + prioq_free(d->latest); +} + +static void event_free(sd_event *e) { + sd_event_source *s; + + assert(e); + + while ((s = e->sources)) { + assert(s->floating); + source_disconnect(s); + sd_event_source_unref(s); + } + + assert(e->n_sources == 0); + + if (e->default_event_ptr) + *(e->default_event_ptr) = NULL; + + safe_close(e->epoll_fd); + safe_close(e->watchdog_fd); + + free_clock_data(&e->realtime); + free_clock_data(&e->boottime); + free_clock_data(&e->monotonic); + free_clock_data(&e->realtime_alarm); + free_clock_data(&e->boottime_alarm); + + prioq_free(e->pending); + prioq_free(e->prepare); + prioq_free(e->exit); + + free(e->signal_sources); + hashmap_free(e->signal_data); + + hashmap_free(e->child_sources); + set_free(e->post_sources); + free(e); +} + +_public_ int sd_event_new(sd_event** ret) { + sd_event *e; + int r; + + assert_return(ret, -EINVAL); + + e = new0(sd_event, 1); + if (!e) + return -ENOMEM; + + e->n_ref = 1; + e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; + e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; + e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; + e->original_pid = getpid(); + e->perturb = USEC_INFINITY; + + 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) { + r = -errno; + 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; + +fail: + event_free(e); + return r; +} + +_public_ sd_event* sd_event_ref(sd_event *e) { + + if (!e) + return NULL; + + assert(e->n_ref >= 1); + e->n_ref++; + + return e; +} + +_public_ sd_event* sd_event_unref(sd_event *e) { + + if (!e) + return NULL; + + assert(e->n_ref >= 1); + e->n_ref--; + + if (e->n_ref <= 0) + event_free(e); + + return NULL; +} + +static bool event_pid_changed(sd_event *e) { + assert(e); + + /* We don't support people creating an event loop and keeping + * it around over a fork(). Let's complain. */ + + return e->original_pid != getpid(); +} + +static void source_io_unregister(sd_event_source *s) { + int r; + + assert(s); + assert(s->type == SOURCE_IO); + + if (event_pid_changed(s->event)) + return; + + if (!s->io.registered) + return; + + 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 (type %s) from epoll: %m", + strna(s->description), event_source_type_to_string(s->type)); + + s->io.registered = false; +} + +static int source_io_register( + sd_event_source *s, + int enabled, + uint32_t events) { + + struct epoll_event ev = {}; + int r; + + assert(s); + assert(s->type == SOURCE_IO); + assert(enabled != SD_EVENT_OFF); + + ev.events = events; + ev.data.ptr = s; + + if (enabled == SD_EVENT_ONESHOT) + ev.events |= EPOLLONESHOT; + + if (s->io.registered) + r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev); + else + r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev); + if (r < 0) + return -errno; + + s->io.registered = true; + + return 0; +} + +static clockid_t event_source_type_to_clock(EventSourceType t) { + + switch (t) { + + case SOURCE_TIME_REALTIME: + return CLOCK_REALTIME; + + case SOURCE_TIME_BOOTTIME: + return CLOCK_BOOTTIME; + + case SOURCE_TIME_MONOTONIC: + return CLOCK_MONOTONIC; + + case SOURCE_TIME_REALTIME_ALARM: + return CLOCK_REALTIME_ALARM; + + case SOURCE_TIME_BOOTTIME_ALARM: + return CLOCK_BOOTTIME_ALARM; + + default: + return (clockid_t) -1; + } +} + +static EventSourceType clock_to_event_source_type(clockid_t clock) { + + switch (clock) { + + case CLOCK_REALTIME: + return SOURCE_TIME_REALTIME; + + case CLOCK_BOOTTIME: + return SOURCE_TIME_BOOTTIME; + + case CLOCK_MONOTONIC: + return SOURCE_TIME_MONOTONIC; + + case CLOCK_REALTIME_ALARM: + return SOURCE_TIME_REALTIME_ALARM; + + case CLOCK_BOOTTIME_ALARM: + return SOURCE_TIME_BOOTTIME_ALARM; + + default: + return _SOURCE_EVENT_SOURCE_TYPE_INVALID; + } +} + +static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { + assert(e); + + switch (t) { + + case SOURCE_TIME_REALTIME: + return &e->realtime; + + case SOURCE_TIME_BOOTTIME: + return &e->boottime; + + case SOURCE_TIME_MONOTONIC: + return &e->monotonic; + + case SOURCE_TIME_REALTIME_ALARM: + return &e->realtime_alarm; + + case SOURCE_TIME_BOOTTIME_ALARM: + return &e->boottime_alarm; + + default: + return NULL; + } +} + +static int event_make_signal_data( + sd_event *e, + int sig, + struct signal_data **ret) { + + struct epoll_event ev = {}; + struct signal_data *d; + bool added = false; + sigset_t ss_copy; + int64_t priority; + int r; + + assert(e); + + if (event_pid_changed(e)) + return -ECHILD; + + if (e->signal_sources && e->signal_sources[sig]) + priority = e->signal_sources[sig]->priority; + else + priority = 0; + + d = hashmap_get(e->signal_data, &priority); + if (d) { + if (sigismember(&d->sigset, sig) > 0) { + if (ret) + *ret = d; + return 0; + } + } else { + r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); + if (r < 0) + return r; + + d = new0(struct signal_data, 1); + if (!d) + return -ENOMEM; + + d->wakeup = WAKEUP_SIGNAL_DATA; + d->fd = -1; + d->priority = priority; + + r = hashmap_put(e->signal_data, &d->priority, d); + if (r < 0) { + free(d); + return r; + } + + added = true; + } + + ss_copy = d->sigset; + assert_se(sigaddset(&ss_copy, sig) >= 0); + + r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); + if (r < 0) { + r = -errno; + goto fail; + } + + d->sigset = ss_copy; + + if (d->fd >= 0) { + if (ret) + *ret = d; + return 0; + } + + d->fd = r; + + ev.events = EPOLLIN; + ev.data.ptr = d; + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); + if (r < 0) { + r = -errno; + goto fail; + } + + if (ret) + *ret = d; + + return 0; + +fail: + if (added) { + d->fd = safe_close(d->fd); + hashmap_remove(e->signal_data, &d->priority); + free(d); + } + + return r; +} + +static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) { + assert(e); + assert(d); + + /* Turns off the specified signal in the signal data + * object. If the signal mask of the object becomes empty that + * way removes it. */ + + if (sigismember(&d->sigset, sig) == 0) + return; + + assert_se(sigdelset(&d->sigset, sig) >= 0); + + if (sigisemptyset(&d->sigset)) { + + /* If all the mask is all-zero we can get rid of the structure */ + hashmap_remove(e->signal_data, &d->priority); + assert(!d->current); + safe_close(d->fd); + free(d); + return; + } + + assert(d->fd >= 0); + + if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0) + log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m"); +} + +static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) { + struct signal_data *d; + static const int64_t zero_priority = 0; + + assert(e); + + /* Rechecks if the specified signal is still something we are + * interested in. If not, we'll unmask it, and possibly drop + * the signalfd for it. */ + + if (sig == SIGCHLD && + e->n_enabled_child_sources > 0) + return; + + if (e->signal_sources && + e->signal_sources[sig] && + e->signal_sources[sig]->enabled != SD_EVENT_OFF) + return; + + /* + * The specified signal might be enabled in three different queues: + * + * 1) the one that belongs to the priority passed (if it is non-NULL) + * 2) the one that belongs to the priority of the event source of the signal (if there is one) + * 3) the 0 priority (to cover the SIGCHLD case) + * + * Hence, let's remove it from all three here. + */ + + if (priority) { + d = hashmap_get(e->signal_data, priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + if (e->signal_sources && e->signal_sources[sig]) { + d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + d = hashmap_get(e->signal_data, &zero_priority); + if (d) + event_unmask_signal_data(e, d, sig); +} + +static void source_disconnect(sd_event_source *s) { + sd_event *event; + + assert(s); + + if (!s->event) + return; + + assert(s->event->n_sources > 0); + + switch (s->type) { + + case SOURCE_IO: + if (s->io.fd >= 0) + source_io_unregister(s); + + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_remove(d->earliest, s, &s->time.earliest_index); + prioq_remove(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + if (s->signal.sig > 0) { + + if (s->event->signal_sources) + s->event->signal_sources[s->signal.sig] = NULL; + + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + } + + break; + + case SOURCE_CHILD: + if (s->child.pid > 0) { + if (s->enabled != SD_EVENT_OFF) { + assert(s->event->n_enabled_child_sources > 0); + s->event->n_enabled_child_sources--; + } + + (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + } + + break; + + case SOURCE_DEFER: + /* nothing */ + break; + + case SOURCE_POST: + set_remove(s->event->post_sources, s); + break; + + case SOURCE_EXIT: + prioq_remove(s->event->exit, s, &s->exit.prioq_index); + break; + + default: + assert_not_reached("Wut? I shouldn't exist."); + } + + if (s->pending) + prioq_remove(s->event->pending, s, &s->pending_index); + + if (s->prepare) + prioq_remove(s->event->prepare, s, &s->prepare_index); + + event = s->event; + + s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID; + s->event = NULL; + LIST_REMOVE(sources, event->sources, s); + event->n_sources--; + + if (!s->floating) + sd_event_unref(event); +} + +static void source_free(sd_event_source *s) { + assert(s); + + source_disconnect(s); + free(s->description); + free(s); +} + +static int source_set_pending(sd_event_source *s, bool b) { + int r; + + assert(s); + assert(s->type != SOURCE_EXIT); + + if (s->pending == b) + return 0; + + s->pending = b; + + if (b) { + s->pending_iteration = s->event->iteration; + + r = prioq_put(s->event->pending, s, &s->pending_index); + if (r < 0) { + s->pending = false; + return r; + } + } else + assert_se(prioq_remove(s->event->pending, s, &s->pending_index)); + + if (EVENT_SOURCE_IS_TIME(s->type)) { + struct clock_data *d; + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + } + + if (s->type == SOURCE_SIGNAL && !b) { + struct signal_data *d; + + d = hashmap_get(s->event->signal_data, &s->priority); + if (d && d->current == s) + d->current = NULL; + } + + return 0; +} + +static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { + sd_event_source *s; + + assert(e); + + s = new0(sd_event_source, 1); + if (!s) + return NULL; + + s->n_ref = 1; + s->event = e; + s->floating = floating; + s->type = type; + s->pending_index = s->prepare_index = PRIOQ_IDX_NULL; + + if (!floating) + sd_event_ref(e); + + LIST_PREPEND(sources, e->sources, s); + e->n_sources++; + + return s; +} + +_public_ int sd_event_add_io( + sd_event *e, + sd_event_source **ret, + int fd, + uint32_t events, + sd_event_io_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + s = source_new(e, !ret, SOURCE_IO); + if (!s) + return -ENOMEM; + + s->wakeup = WAKEUP_EVENT_SOURCE; + s->io.fd = fd; + s->io.events = events; + s->io.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ON; + + r = source_io_register(s, s->enabled, events); + if (r < 0) { + source_free(s); + return r; + } + + if (ret) + *ret = s; + + return 0; +} + +static void initialize_perturb(sd_event *e) { + sd_id128_t bootid = {}; + + /* When we sleep for longer, we try to realign the wakeup to + the same time wihtin each minute/second/250ms, so that + events all across the system can be coalesced into a single + CPU wakeup. However, let's take some system-specific + randomness for this value, so that in a network of systems + with synced clocks timer events are distributed a + bit. Here, we calculate a perturbation usec offset from the + boot ID. */ + + if (_likely_(e->perturb != USEC_INFINITY)) + return; + + if (sd_id128_get_boot(&bootid) >= 0) + e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; +} + +static int event_setup_timer_fd( + sd_event *e, + struct clock_data *d, + clockid_t clock) { + + struct epoll_event ev = {}; + int r, fd; + + assert(e); + assert(d); + + if (_likely_(d->fd >= 0)) + return 0; + + fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + return -errno; + + ev.events = EPOLLIN; + ev.data.ptr = d; + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (r < 0) { + safe_close(fd); + return -errno; + } + + d->fd = fd; + return 0; +} + +static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) { + assert(s); + + return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); +} + +_public_ int sd_event_add_time( + sd_event *e, + sd_event_source **ret, + clockid_t clock, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata) { + + EventSourceType type; + sd_event_source *s; + struct clock_data *d; + int r; + + assert_return(e, -EINVAL); + assert_return(accuracy != (uint64_t) -1, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + if (!clock_supported(clock)) /* Checks whether the kernel supports the clock */ + return -EOPNOTSUPP; + + type = clock_to_event_source_type(clock); /* checks whether sd-event supports this clock */ + if (type < 0) + return -EOPNOTSUPP; + + if (!callback) + callback = time_exit_callback; + + d = event_get_clock_data(e, type); + assert(d); + + r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); + if (r < 0) + return r; + + 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); + if (r < 0) + return r; + } + + s = source_new(e, !ret, type); + if (!s) + return -ENOMEM; + + s->time.next = usec; + s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy; + s->time.callback = callback; + s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; + s->userdata = userdata; + s->enabled = SD_EVENT_ONESHOT; + + d->needs_rearm = true; + + r = prioq_put(d->earliest, s, &s->time.earliest_index); + if (r < 0) + goto fail; + + r = prioq_put(d->latest, s, &s->time.latest_index); + if (r < 0) + goto fail; + + if (ret) + *ret = s; + + return 0; + +fail: + source_free(s); + return r; +} + +static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + assert(s); + + return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); +} + +_public_ int sd_event_add_signal( + sd_event *e, + sd_event_source **ret, + int sig, + sd_event_signal_handler_t callback, + void *userdata) { + + sd_event_source *s; + struct signal_data *d; + sigset_t ss; + int r; + + assert_return(e, -EINVAL); + assert_return(SIGNAL_VALID(sig), -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + if (!callback) + callback = signal_exit_callback; + + r = pthread_sigmask(SIG_SETMASK, NULL, &ss); + if (r != 0) + return -r; + + if (!sigismember(&ss, sig)) + return -EBUSY; + + if (!e->signal_sources) { + e->signal_sources = new0(sd_event_source*, _NSIG); + if (!e->signal_sources) + return -ENOMEM; + } else if (e->signal_sources[sig]) + return -EBUSY; + + s = source_new(e, !ret, SOURCE_SIGNAL); + if (!s) + return -ENOMEM; + + s->signal.sig = sig; + s->signal.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ON; + + e->signal_sources[sig] = s; + + r = event_make_signal_data(e, sig, &d); + if (r < 0) { + source_free(s); + return r; + } + + /* Use the signal name as description for the event source by default */ + (void) sd_event_source_set_description(s, signal_to_string(sig)); + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_child( + sd_event *e, + sd_event_source **ret, + pid_t pid, + int options, + sd_event_child_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(pid > 1, -EINVAL); + assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); + assert_return(options != 0, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = hashmap_ensure_allocated(&e->child_sources, NULL); + if (r < 0) + return r; + + if (hashmap_contains(e->child_sources, PID_TO_PTR(pid))) + return -EBUSY; + + s = source_new(e, !ret, SOURCE_CHILD); + if (!s) + return -ENOMEM; + + s->child.pid = pid; + s->child.options = options; + s->child.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ONESHOT; + + r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); + if (r < 0) { + source_free(s); + return r; + } + + e->n_enabled_child_sources++; + + r = event_make_signal_data(e, SIGCHLD, NULL); + if (r < 0) { + e->n_enabled_child_sources--; + source_free(s); + return r; + } + + e->need_process_child = true; + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_defer( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + s = source_new(e, !ret, SOURCE_DEFER); + if (!s) + return -ENOMEM; + + s->defer.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ONESHOT; + + r = source_set_pending(s, true); + if (r < 0) { + source_free(s); + return r; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_post( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = set_ensure_allocated(&e->post_sources, NULL); + if (r < 0) + return r; + + s = source_new(e, !ret, SOURCE_POST); + if (!s) + return -ENOMEM; + + s->post.callback = callback; + s->userdata = userdata; + s->enabled = SD_EVENT_ON; + + r = set_put(e->post_sources, s); + if (r < 0) { + source_free(s); + return r; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ int sd_event_add_exit( + sd_event *e, + sd_event_source **ret, + sd_event_handler_t callback, + void *userdata) { + + sd_event_source *s; + int r; + + assert_return(e, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); + if (r < 0) + return r; + + s = source_new(e, !ret, SOURCE_EXIT); + if (!s) + return -ENOMEM; + + s->exit.callback = callback; + s->userdata = userdata; + s->exit.prioq_index = PRIOQ_IDX_NULL; + s->enabled = SD_EVENT_ONESHOT; + + r = prioq_put(s->event->exit, s, &s->exit.prioq_index); + if (r < 0) { + source_free(s); + return r; + } + + if (ret) + *ret = s; + + return 0; +} + +_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { + + if (!s) + return NULL; + + assert(s->n_ref >= 1); + s->n_ref++; + + return s; +} + +_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { + + if (!s) + return NULL; + + assert(s->n_ref >= 1); + s->n_ref--; + + if (s->n_ref <= 0) { + /* Here's a special hack: when we are called from a + * dispatch handler we won't free the event source + * immediately, but we will detach the fd from the + * epoll. This way it is safe for the caller to unref + * the event source and immediately close the fd, but + * we still retain a valid event source object after + * the callback. */ + + if (s->dispatching) { + if (s->type == SOURCE_IO) + source_io_unregister(s); + + source_disconnect(s); + } else + source_free(s); + } + + return NULL; +} + +_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { + assert_return(s, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return free_and_strdup(&s->description, description); +} + +_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { + assert_return(s, -EINVAL); + assert_return(description, -EINVAL); + assert_return(s->description, -ENXIO); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *description = s->description; + return 0; +} + +_public_ sd_event *sd_event_source_get_event(sd_event_source *s) { + assert_return(s, NULL); + + return s->event; +} + +_public_ int sd_event_source_get_pending(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type != SOURCE_EXIT, -EDOM); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return s->pending; +} + +_public_ int sd_event_source_get_io_fd(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return s->io.fd; +} + +_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { + int r; + + assert_return(s, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + if (s->io.fd == fd) + return 0; + + if (s->enabled == SD_EVENT_OFF) { + s->io.fd = fd; + s->io.registered = false; + } else { + int saved_fd; + + saved_fd = s->io.fd; + assert(s->io.registered); + + s->io.fd = fd; + s->io.registered = false; + + r = source_io_register(s, s->enabled, s->io.events); + if (r < 0) { + s->io.fd = saved_fd; + s->io.registered = true; + return r; + } + + epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL); + } + + return 0; +} + +_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { + assert_return(s, -EINVAL); + assert_return(events, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *events = s->io.events; + return 0; +} + +_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) { + int r; + + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + /* edge-triggered updates are never skipped, so we can reset edges */ + if (s->io.events == events && !(events & EPOLLET)) + return 0; + + if (s->enabled != SD_EVENT_OFF) { + r = source_io_register(s, s->enabled, events); + if (r < 0) + return r; + } + + s->io.events = events; + source_set_pending(s, false); + + return 0; +} + +_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) { + assert_return(s, -EINVAL); + assert_return(revents, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + assert_return(s->pending, -ENODATA); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *revents = s->io.revents; + return 0; +} + +_public_ int sd_event_source_get_signal(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_SIGNAL, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return s->signal.sig; +} + +_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) { + assert_return(s, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + + return s->priority; +} + +_public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) { + int r; + + assert_return(s, -EINVAL); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + if (s->priority == priority) + return 0; + + if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { + struct signal_data *old, *d; + + /* Move us from the signalfd belonging to the old + * priority to the signalfd of the new priority */ + + assert_se(old = hashmap_get(s->event->signal_data, &s->priority)); + + s->priority = priority; + + r = event_make_signal_data(s->event, s->signal.sig, &d); + if (r < 0) { + s->priority = old->priority; + return r; + } + + event_unmask_signal_data(s->event, old, s->signal.sig); + } else + s->priority = priority; + + if (s->pending) + prioq_reshuffle(s->event->pending, s, &s->pending_index); + + if (s->prepare) + prioq_reshuffle(s->event->prepare, s, &s->prepare_index); + + if (s->type == SOURCE_EXIT) + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); + + return 0; +} + +_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { + assert_return(s, -EINVAL); + assert_return(m, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *m = s->enabled; + return 0; +} + +_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + int r; + + assert_return(s, -EINVAL); + assert_return(m == SD_EVENT_OFF || m == SD_EVENT_ON || m == SD_EVENT_ONESHOT, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + + /* If we are dead anyway, we are fine with turning off + * sources, but everything else needs to fail. */ + if (s->event->state == SD_EVENT_FINISHED) + return m == SD_EVENT_OFF ? 0 : -ESTALE; + + if (s->enabled == m) + return 0; + + if (m == SD_EVENT_OFF) { + + switch (s->type) { + + case SOURCE_IO: + source_io_unregister(s); + s->enabled = m; + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + s->enabled = m; + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + s->enabled = m; + + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + break; + + case SOURCE_CHILD: + s->enabled = m; + + assert(s->event->n_enabled_child_sources > 0); + s->event->n_enabled_child_sources--; + + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + break; + + case SOURCE_EXIT: + s->enabled = m; + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); + break; + + case SOURCE_DEFER: + case SOURCE_POST: + s->enabled = m; + break; + + default: + assert_not_reached("Wut? I shouldn't exist."); + } + + } else { + switch (s->type) { + + case SOURCE_IO: + r = source_io_register(s, m, s->io.events); + if (r < 0) + return r; + + s->enabled = m; + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; + + s->enabled = m; + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + break; + } + + case SOURCE_SIGNAL: + + s->enabled = m; + + r = event_make_signal_data(s->event, s->signal.sig, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + return r; + } + + break; + + case SOURCE_CHILD: + + if (s->enabled == SD_EVENT_OFF) + s->event->n_enabled_child_sources++; + + s->enabled = m; + + r = event_make_signal_data(s->event, SIGCHLD, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + s->event->n_enabled_child_sources--; + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + return r; + } + + break; + + case SOURCE_EXIT: + s->enabled = m; + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); + break; + + case SOURCE_DEFER: + case SOURCE_POST: + s->enabled = m; + break; + + default: + assert_not_reached("Wut? I shouldn't exist."); + } + } + + if (s->pending) + prioq_reshuffle(s->event->pending, s, &s->pending_index); + + if (s->prepare) + prioq_reshuffle(s->event->prepare, s, &s->prepare_index); + + return 0; +} + +_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { + assert_return(s, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *usec = s->time.next; + return 0; +} + +_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { + struct clock_data *d; + + assert_return(s, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + s->time.next = usec; + + source_set_pending(s, false); + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + + return 0; +} + +_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { + assert_return(s, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *usec = s->time.accuracy; + return 0; +} + +_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { + struct clock_data *d; + + assert_return(s, -EINVAL); + assert_return(usec != (uint64_t) -1, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + if (usec == 0) + usec = DEFAULT_ACCURACY_USEC; + + s->time.accuracy = usec; + + source_set_pending(s, false); + + d = event_get_clock_data(s->event, s->type); + assert(d); + + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + + return 0; +} + +_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) { + assert_return(s, -EINVAL); + assert_return(clock, -EINVAL); + assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *clock = event_source_type_to_clock(s->type); + return 0; +} + +_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { + assert_return(s, -EINVAL); + assert_return(pid, -EINVAL); + assert_return(s->type == SOURCE_CHILD, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *pid = s->child.pid; + return 0; +} + +_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) { + int r; + + assert_return(s, -EINVAL); + assert_return(s->type != SOURCE_EXIT, -EDOM); + assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(s->event), -ECHILD); + + if (s->prepare == callback) + return 0; + + if (callback && s->prepare) { + s->prepare = callback; + return 0; + } + + r = prioq_ensure_allocated(&s->event->prepare, prepare_prioq_compare); + if (r < 0) + return r; + + s->prepare = callback; + + if (callback) { + r = prioq_put(s->event->prepare, s, &s->prepare_index); + if (r < 0) + return r; + } else + prioq_remove(s->event->prepare, s, &s->prepare_index); + + return 0; +} + +_public_ void* sd_event_source_get_userdata(sd_event_source *s) { + assert_return(s, NULL); + + return s->userdata; +} + +_public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) { + void *ret; + + assert_return(s, NULL); + + ret = s->userdata; + s->userdata = userdata; + + return ret; +} + +static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) { + usec_t c; + assert(e); + assert(a <= b); + + if (a <= 0) + return 0; + if (a >= USEC_INFINITY) + return USEC_INFINITY; + + if (b <= a + 1) + return a; + + initialize_perturb(e); + + /* + Find a good time to wake up again between times a and b. We + have two goals here: + + a) We want to wake up as seldom as possible, hence prefer + later times over earlier times. + + b) But if we have to wake up, then let's make sure to + dispatch as much as possible on the entire system. + + We implement this by waking up everywhere at the same time + within any given minute if we can, synchronised via the + perturbation value determined from the boot ID. If we can't, + then we try to find the same spot in every 10s, then 1s and + then 250ms step. Otherwise, we pick the last possible time + to wake up. + */ + + c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb; + if (c >= b) { + if (_unlikely_(c < USEC_PER_MINUTE)) + return b; + + c -= USEC_PER_MINUTE; + } + + if (c >= a) + return c; + + c = (b / (USEC_PER_SEC*10)) * (USEC_PER_SEC*10) + (e->perturb % (USEC_PER_SEC*10)); + if (c >= b) { + if (_unlikely_(c < USEC_PER_SEC*10)) + return b; + + c -= USEC_PER_SEC*10; + } + + if (c >= a) + return c; + + c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC); + if (c >= b) { + if (_unlikely_(c < USEC_PER_SEC)) + return b; + + c -= USEC_PER_SEC; + } + + if (c >= a) + return c; + + c = (b / (USEC_PER_MSEC*250)) * (USEC_PER_MSEC*250) + (e->perturb % (USEC_PER_MSEC*250)); + if (c >= b) { + if (_unlikely_(c < USEC_PER_MSEC*250)) + return b; + + c -= USEC_PER_MSEC*250; + } + + if (c >= a) + return c; + + return b; +} + +static int event_arm_timer( + sd_event *e, + struct clock_data *d) { + + struct itimerspec its = {}; + sd_event_source *a, *b; + usec_t t; + int r; + + assert(e); + assert(d); + + if (!d->needs_rearm) + return 0; + else + d->needs_rearm = false; + + a = prioq_peek(d->earliest); + if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { + + if (d->fd < 0) + return 0; + + if (d->next == USEC_INFINITY) + return 0; + + /* disarm */ + r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return r; + + d->next = USEC_INFINITY; + return 0; + } + + b = prioq_peek(d->latest); + assert_se(b && b->enabled != SD_EVENT_OFF); + + t = sleep_between(e, a->time.next, time_event_source_latest(b)); + if (d->next == t) + return 0; + + assert_se(d->fd >= 0); + + if (t == 0) { + /* We don' want to disarm here, just mean some time looooong ago. */ + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 1; + } else + timespec_store(&its.it_value, t); + + r = timerfd_settime(d->fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return -errno; + + d->next = t; + return 0; +} + +static int process_io(sd_event *e, sd_event_source *s, uint32_t revents) { + assert(e); + assert(s); + assert(s->type == SOURCE_IO); + + /* If the event source was already pending, we just OR in the + * new revents, otherwise we reset the value. The ORing is + * necessary to handle EPOLLONESHOT events properly where + * readability might happen independently of writability, and + * we need to keep track of both */ + + if (s->pending) + s->io.revents |= revents; + else + s->io.revents = revents; + + return source_set_pending(s, true); +} + +static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) { + uint64_t x; + ssize_t ss; + + assert(e); + assert(fd >= 0); + + assert_return(events == EPOLLIN, -EIO); + + ss = read(fd, &x, sizeof(x)); + if (ss < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + if (_unlikely_(ss != sizeof(x))) + return -EIO; + + if (next) + *next = USEC_INFINITY; + + return 0; +} + +static int process_timer( + sd_event *e, + usec_t n, + struct clock_data *d) { + + sd_event_source *s; + int r; + + assert(e); + assert(d); + + for (;;) { + s = prioq_peek(d->earliest); + if (!s || + s->time.next > n || + s->enabled == SD_EVENT_OFF || + s->pending) + break; + + r = source_set_pending(s, true); + if (r < 0) + return r; + + prioq_reshuffle(d->earliest, s, &s->time.earliest_index); + prioq_reshuffle(d->latest, s, &s->time.latest_index); + d->needs_rearm = true; + } + + return 0; +} + +static int process_child(sd_event *e) { + sd_event_source *s; + Iterator i; + int r; + + assert(e); + + e->need_process_child = false; + + /* + So, this is ugly. We iteratively invoke waitid() with P_PID + + WNOHANG for each PID we wait for, instead of using + P_ALL. This is because we only want to get child + information of very specific child processes, and not all + of them. We might not have processed the SIGCHLD even of a + previous invocation and we don't want to maintain a + unbounded *per-child* event queue, hence we really don't + want anything flushed out of the kernel's queue that we + don't care about. Since this is O(n) this means that if you + have a lot of processes you probably want to handle SIGCHLD + yourself. + + We do not reap the children here (by using WNOWAIT), this + is only done after the event source is dispatched so that + the callback still sees the process as a zombie. + */ + + HASHMAP_FOREACH(s, e->child_sources, i) { + assert(s->type == SOURCE_CHILD); + + if (s->pending) + continue; + + if (s->enabled == SD_EVENT_OFF) + continue; + + zero(s->child.siginfo); + r = waitid(P_PID, s->child.pid, &s->child.siginfo, + WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options); + if (r < 0) + return -errno; + + if (s->child.siginfo.si_pid != 0) { + bool zombie = + s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + + if (!zombie && (s->child.options & WEXITED)) { + /* If the child isn't dead then let's + * immediately remove the state change + * from the queue, since there's no + * benefit in leaving it queued */ + + assert(s->child.options & (WSTOPPED|WCONTINUED)); + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED))); + } + + r = source_set_pending(s, true); + if (r < 0) + return r; + } + } + + return 0; +} + +static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { + bool read_one = false; + int r; + + assert(e); + assert_return(events == EPOLLIN, -EIO); + + /* If there's a signal queued on this priority and SIGCHLD is + on this priority too, then make sure to recheck the + children we watch. This is because we only ever dequeue + the first signal per priority, and if we dequeue one, and + SIGCHLD might be enqueued later we wouldn't know, but we + might have higher priority children we care about hence we + need to check that explicitly. */ + + if (sigismember(&d->sigset, SIGCHLD)) + e->need_process_child = true; + + /* If there's already an event source pending for this + * priority we don't read another */ + if (d->current) + return 0; + + for (;;) { + struct signalfd_siginfo si; + ssize_t n; + sd_event_source *s = NULL; + + n = read(d->fd, &si, sizeof(si)); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return read_one; + + return -errno; + } + + if (_unlikely_(n != sizeof(si))) + return -EIO; + + assert(SIGNAL_VALID(si.ssi_signo)); + + read_one = true; + + if (e->signal_sources) + s = e->signal_sources[si.ssi_signo]; + if (!s) + continue; + if (s->pending) + continue; + + s->signal.siginfo = si; + d->current = s; + + r = source_set_pending(s, true); + if (r < 0) + return r; + + return 1; + } +} + +static int source_dispatch(sd_event_source *s) { + int r = 0; + + assert(s); + assert(s->pending || s->type == SOURCE_EXIT); + + if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) { + r = source_set_pending(s, false); + if (r < 0) + return r; + } + + if (s->type != SOURCE_POST) { + sd_event_source *z; + Iterator i; + + /* If we execute a non-post source, let's mark all + * post sources as pending */ + + SET_FOREACH(z, s->event->post_sources, i) { + if (z->enabled == SD_EVENT_OFF) + continue; + + r = source_set_pending(z, true); + if (r < 0) + return r; + } + } + + if (s->enabled == SD_EVENT_ONESHOT) { + r = sd_event_source_set_enabled(s, SD_EVENT_OFF); + if (r < 0) + return r; + } + + s->dispatching = true; + + switch (s->type) { + + case SOURCE_IO: + r = s->io.callback(s, s->io.fd, s->io.revents, s->userdata); + break; + + case SOURCE_TIME_REALTIME: + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: + r = s->time.callback(s, s->time.next, s->userdata); + break; + + case SOURCE_SIGNAL: + r = s->signal.callback(s, &s->signal.siginfo, s->userdata); + break; + + case SOURCE_CHILD: { + bool zombie; + + zombie = s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + + r = s->child.callback(s, &s->child.siginfo, s->userdata); + + /* Now, reap the PID for good. */ + if (zombie) + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); + + break; + } + + case SOURCE_DEFER: + r = s->defer.callback(s, s->userdata); + break; + + case SOURCE_POST: + r = s->post.callback(s, s->userdata); + break; + + case SOURCE_EXIT: + r = s->exit.callback(s, s->userdata); + break; + + case SOURCE_WATCHDOG: + case _SOURCE_EVENT_SOURCE_TYPE_MAX: + case _SOURCE_EVENT_SOURCE_TYPE_INVALID: + assert_not_reached("Wut? I shouldn't exist."); + } + + s->dispatching = false; + + 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); + else if (r < 0) + sd_event_source_set_enabled(s, SD_EVENT_OFF); + + return 1; +} + +static int event_prepare(sd_event *e) { + int r; + + assert(e); + + for (;;) { + sd_event_source *s; + + s = prioq_peek(e->prepare); + if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF) + break; + + s->prepare_iteration = e->iteration; + r = prioq_reshuffle(e->prepare, s, &s->prepare_index); + if (r < 0) + return r; + + assert(s->prepare); + + s->dispatching = true; + r = s->prepare(s, s->userdata); + s->dispatching = false; + + 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); + else if (r < 0) + sd_event_source_set_enabled(s, SD_EVENT_OFF); + } + + return 0; +} + +static int dispatch_exit(sd_event *e) { + sd_event_source *p; + int r; + + assert(e); + + p = prioq_peek(e->exit); + if (!p || p->enabled == SD_EVENT_OFF) { + e->state = SD_EVENT_FINISHED; + return 0; + } + + sd_event_ref(e); + e->iteration++; + e->state = SD_EVENT_EXITING; + + r = source_dispatch(p); + + e->state = SD_EVENT_INITIAL; + sd_event_unref(e); + + return r; +} + +static sd_event_source* event_next_pending(sd_event *e) { + sd_event_source *p; + + assert(e); + + p = prioq_peek(e->pending); + if (!p) + return NULL; + + if (p->enabled == SD_EVENT_OFF) + return NULL; + + return p; +} + +static int arm_watchdog(sd_event *e) { + struct itimerspec its = {}; + usec_t t; + int r; + + assert(e); + assert(e->watchdog_fd >= 0); + + t = sleep_between(e, + e->watchdog_last + (e->watchdog_period / 2), + e->watchdog_last + (e->watchdog_period * 3 / 4)); + + timespec_store(&its.it_value, t); + + /* Make sure we never set the watchdog to 0, which tells the + * kernel to disable it. */ + if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) + its.it_value.tv_nsec = 1; + + r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return -errno; + + return 0; +} + +static int process_watchdog(sd_event *e) { + assert(e); + + if (!e->watchdog) + return 0; + + /* Don't notify watchdog too often */ + if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic) + return 0; + + sd_notify(false, "WATCHDOG=1"); + e->watchdog_last = e->timestamp.monotonic; + + return arm_watchdog(e); +} + +_public_ int sd_event_prepare(sd_event *e) { + int r; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + + if (e->exit_requested) + goto pending; + + e->iteration++; + + e->state = SD_EVENT_PREPARING; + r = event_prepare(e); + e->state = SD_EVENT_INITIAL; + if (r < 0) + return r; + + r = event_arm_timer(e, &e->realtime); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->boottime); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->monotonic); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->realtime_alarm); + if (r < 0) + return r; + + r = event_arm_timer(e, &e->boottime_alarm); + if (r < 0) + return r; + + if (event_next_pending(e) || e->need_process_child) + goto pending; + + e->state = SD_EVENT_ARMED; + + return 0; + +pending: + e->state = SD_EVENT_ARMED; + r = sd_event_wait(e, 0); + if (r == 0) + e->state = SD_EVENT_ARMED; + + return r; +} + +_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + struct epoll_event *ev_queue; + unsigned ev_queue_max; + int r, m, i; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_ARMED, -EBUSY); + + if (e->exit_requested) { + e->state = SD_EVENT_PENDING; + return 1; + } + + ev_queue_max = MAX(e->n_sources, 1u); + ev_queue = newa(struct epoll_event, ev_queue_max); + + m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, + timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); + if (m < 0) { + if (errno == EINTR) { + e->state = SD_EVENT_PENDING; + return 1; + } + + r = -errno; + goto finish; + } + + triple_timestamp_get(&e->timestamp); + + for (i = 0; i < m; i++) { + + if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) + r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); + else { + WakeupType *t = ev_queue[i].data.ptr; + + switch (*t) { + + case WAKEUP_EVENT_SOURCE: + r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + + case WAKEUP_CLOCK_DATA: { + struct clock_data *d = ev_queue[i].data.ptr; + r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); + break; + } + + case WAKEUP_SIGNAL_DATA: + r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + + default: + assert_not_reached("Invalid wake-up pointer"); + } + } + if (r < 0) + goto finish; + } + + r = process_watchdog(e); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.realtime, &e->realtime); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.boottime, &e->boottime); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.monotonic, &e->monotonic); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm); + if (r < 0) + goto finish; + + r = process_timer(e, e->timestamp.boottime, &e->boottime_alarm); + if (r < 0) + goto finish; + + if (e->need_process_child) { + r = process_child(e); + if (r < 0) + goto finish; + } + + if (event_next_pending(e)) { + e->state = SD_EVENT_PENDING; + + return 1; + } + + r = 0; + +finish: + e->state = SD_EVENT_INITIAL; + + return r; +} + +_public_ int sd_event_dispatch(sd_event *e) { + sd_event_source *p; + int r; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_PENDING, -EBUSY); + + if (e->exit_requested) + return dispatch_exit(e); + + p = event_next_pending(e); + if (p) { + sd_event_ref(e); + + e->state = SD_EVENT_RUNNING; + r = source_dispatch(p); + e->state = SD_EVENT_INITIAL; + + sd_event_unref(e); + + return r; + } + + e->state = SD_EVENT_INITIAL; + + 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; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_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); + if (r < 0) + return r; + + return 1; + } + + return r; +} + +_public_ int sd_event_loop(sd_event *e) { + int r; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + + sd_event_ref(e); + + while (e->state != SD_EVENT_FINISHED) { + r = sd_event_run(e, (uint64_t) -1); + if (r < 0) + goto finish; + } + + r = e->exit_code; + +finish: + sd_event_unref(e); + return r; +} + +_public_ int sd_event_get_fd(sd_event *e) { + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + return e->epoll_fd; +} + +_public_ int sd_event_get_state(sd_event *e) { + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + return e->state; +} + +_public_ int sd_event_get_exit_code(sd_event *e, int *code) { + assert_return(e, -EINVAL); + assert_return(code, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + if (!e->exit_requested) + return -ENODATA; + + *code = e->exit_code; + return 0; +} + +_public_ int sd_event_exit(sd_event *e, int code) { + assert_return(e, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + e->exit_requested = true; + e->exit_code = code; + + return 0; +} + +_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); + + if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) + return -EOPNOTSUPP; + + /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here, + * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for + * the purpose of getting the time this doesn't matter. */ + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) + return -EOPNOTSUPP; + + if (!triple_timestamp_is_set(&e->timestamp)) { + /* Implicitly fall back to now() if we never ran + * before and thus have no cached time. */ + *usec = now(clock); + return 1; + } + + *usec = triple_timestamp_by_clock(&e->timestamp, clock); + return 0; +} + +_public_ int sd_event_default(sd_event **ret) { + + static thread_local sd_event *default_event = NULL; + sd_event *e = NULL; + int r; + + if (!ret) + return !!default_event; + + if (default_event) { + *ret = sd_event_ref(default_event); + return 0; + } + + r = sd_event_new(&e); + if (r < 0) + return r; + + e->default_event_ptr = &default_event; + e->tid = gettid(); + default_event = e; + + *ret = e; + return 1; +} + +_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { + assert_return(e, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + if (e->tid != 0) { + *tid = e->tid; + return 0; + } + + return -ENXIO; +} + +_public_ int sd_event_set_watchdog(sd_event *e, int b) { + int r; + + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + if (e->watchdog == !!b) + return e->watchdog; + + if (b) { + struct epoll_event ev = {}; + + r = sd_watchdog_enabled(false, &e->watchdog_period); + if (r <= 0) + return r; + + /* Issue first ping immediately */ + sd_notify(false, "WATCHDOG=1"); + e->watchdog_last = now(CLOCK_MONOTONIC); + + e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (e->watchdog_fd < 0) + return -errno; + + r = arm_watchdog(e); + if (r < 0) + goto fail; + + ev.events = EPOLLIN; + ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG); + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev); + if (r < 0) { + r = -errno; + goto fail; + } + + } else { + if (e->watchdog_fd >= 0) { + epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL); + e->watchdog_fd = safe_close(e->watchdog_fd); + } + } + + e->watchdog = !!b; + return e->watchdog; + +fail: + e->watchdog_fd = safe_close(e->watchdog_fd); + return r; +} + +_public_ int sd_event_get_watchdog(sd_event *e) { + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + return e->watchdog; +} + +_public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { + assert_return(e, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + *ret = e->iteration; + return 0; +} diff --git a/src/libsystemd/src/sd-event/test-event.c b/src/libsystemd/src/sd-event/test-event.c new file mode 100644 index 0000000000..10395f155f --- /dev/null +++ b/src/libsystemd/src/sd-event/test-event.c @@ -0,0 +1,361 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/signal-util.h" +#include "basic/util.h" + +static int prepare_handler(sd_event_source *s, void *userdata) { + log_info("preparing %c", PTR_TO_INT(userdata)); + return 1; +} + +static bool got_a, got_b, got_c, got_unref; +static unsigned got_d; + +static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_event_source_unref(s); + got_unref = true; + return 0; +} + +static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + + log_info("got IO on %c", PTR_TO_INT(userdata)); + + if (userdata == INT_TO_PTR('a')) { + assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); + assert_se(!got_a); + got_a = true; + } else if (userdata == INT_TO_PTR('b')) { + assert_se(!got_b); + got_b = true; + } else if (userdata == INT_TO_PTR('d')) { + got_d++; + if (got_d < 2) + assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0); + else + assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0); + } else + assert_not_reached("Yuck!"); + + return 1; +} + +static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) { + + assert_se(s); + assert_se(si); + + log_info("got child on %c", PTR_TO_INT(userdata)); + + assert_se(userdata == INT_TO_PTR('f')); + + assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0); + sd_event_source_unref(s); + + return 1; +} + +static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + sd_event_source *p = NULL; + pid_t pid; + + assert_se(s); + assert_se(si); + + log_info("got signal on %c", PTR_TO_INT(userdata)); + + assert_se(userdata == INT_TO_PTR('e')); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) + _exit(0); + + assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0); + assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); + + sd_event_source_unref(s); + + return 1; +} + +static int defer_handler(sd_event_source *s, void *userdata) { + sd_event_source *p = NULL; + + assert_se(s); + + log_info("got defer on %c", PTR_TO_INT(userdata)); + + assert_se(userdata == INT_TO_PTR('d')); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0); + + assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 0); + assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); + raise(SIGUSR1); + + sd_event_source_unref(s); + + return 1; +} + +static bool do_quit = false; + +static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) { + log_info("got timer on %c", PTR_TO_INT(userdata)); + + if (userdata == INT_TO_PTR('c')) { + + if (do_quit) { + sd_event_source *p; + + assert_se(sd_event_add_defer(sd_event_source_get_event(s), &p, defer_handler, INT_TO_PTR('d')) >= 0); + assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); + } else { + assert_se(!got_c); + got_c = true; + } + } else + assert_not_reached("Huh?"); + + return 2; +} + +static bool got_exit = false; + +static int exit_handler(sd_event_source *s, void *userdata) { + log_info("got quit handler on %c", PTR_TO_INT(userdata)); + + got_exit = true; + + 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); + assert_se(pipe(d) >= 0); + 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); + + /* Test whether we cleanly can destroy an io event source from its own handler */ + got_unref = false; + assert_se(sd_event_add_io(e, &t, k[0], EPOLLIN, unref_handler, NULL) >= 0); + assert_se(write(k[1], &ch, 1) == 1); + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(got_unref); + + got_a = false, got_b = false, got_c = false, got_d = 0; + + /* Add a oneshot handler, trigger it, re-enable it, and trigger + * it again. */ + assert_se(sd_event_add_io(e, &w, d[0], EPOLLIN, io_handler, INT_TO_PTR('d')) >= 0); + assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0); + assert_se(write(d[1], &ch, 1) >= 0); + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(got_d == 1); + assert_se(write(d[1], &ch, 1) >= 0); + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(got_d == 2); + + assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0); + assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0); + assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0); + assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0); + + assert_se(sd_event_source_set_priority(x, 99) >= 0); + assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0); + assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0); + assert_se(sd_event_source_set_priority(z, 50) >= 0); + assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0); + assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0); + + /* Test for floating event sources */ + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0); + assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 0); + + assert_se(write(a[1], &ch, 1) >= 0); + assert_se(write(b[1], &ch, 1) >= 0); + + assert_se(!got_a && !got_b && !got_c); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + + assert_se(!got_a && got_b && !got_c); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + + assert_se(!got_a && got_b && got_c); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + + assert_se(got_a && got_b && got_c); + + sd_event_source_unref(x); + sd_event_source_unref(y); + + do_quit = true; + 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); + + sd_event_source_unref(w); + + sd_event_unref(e); + + safe_close_pair(a); + safe_close_pair(b); + safe_close_pair(d); + 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); + if (clock_boottime_supported()) { + 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); + if (clock_boottime_supported()) { + 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; + +static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + last_rtqueue_sigval = si->ssi_int; + n_rtqueue++; + return 0; +} + +static void test_rtqueue(void) { + sd_event_source *u = NULL, *v = NULL, *s = NULL; + sd_event *e = NULL; + + assert_se(sd_event_default(&e) >= 0); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); + assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0); + + assert_se(sd_event_source_set_priority(v, -10) >= 0); + + assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0); + + assert_se(n_rtqueue == 0); + assert_se(last_rtqueue_sigval == 0); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 1); + assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 2); + assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 3); + assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */ + + assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */ + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); + + sd_event_source_unref(u); + sd_event_source_unref(v); + sd_event_source_unref(s); + + sd_event_unref(e); +} + +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/src/sd-hwdb/Makefile b/src/libsystemd/src/sd-hwdb/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-hwdb/hwdb-internal.h b/src/libsystemd/src/sd-hwdb/hwdb-internal.h new file mode 100644 index 0000000000..7b32bbd11d --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/hwdb-internal.h @@ -0,0 +1,72 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/sparse-endian.h" +#include "basic/util.h" + +#define HWDB_SIG { 'K', 'S', 'L', 'P', 'H', 'H', 'R', 'H' } + +/* on-disk trie objects */ +struct trie_header_f { + uint8_t signature[8]; + + /* version of tool which created the file */ + le64_t tool_version; + le64_t file_size; + + /* size of structures to allow them to grow */ + le64_t header_size; + le64_t node_size; + le64_t child_entry_size; + le64_t value_entry_size; + + /* offset of the root trie node */ + le64_t nodes_root_off; + + /* size of the nodes and string section */ + le64_t nodes_len; + le64_t strings_len; +} _packed_; + +struct trie_node_f { + /* prefix of lookup string, shared by all children */ + le64_t prefix_off; + /* size of children entry array appended to the node */ + uint8_t children_count; + uint8_t padding[7]; + /* size of value entry array appended to the node */ + le64_t values_count; +} _packed_; + +/* array of child entries, follows directly the node record */ +struct trie_child_entry_f { + /* index of the child node */ + uint8_t c; + uint8_t padding[7]; + /* offset of the child node */ + le64_t child_off; +} _packed_; + +/* array of value entries, follows directly the node record/child array */ +struct trie_value_entry_f { + le64_t key_off; + le64_t value_off; +} _packed_; diff --git a/src/libsystemd/src/sd-hwdb/hwdb-util.h b/src/libsystemd/src/sd-hwdb/hwdb-util.h new file mode 100644 index 0000000000..c8c0a4938e --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/hwdb-util.h @@ -0,0 +1,26 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/util.h" + +#include "sd-hwdb.h" + +bool hwdb_validate(sd_hwdb *hwdb); diff --git a/src/libsystemd/src/sd-hwdb/sd-hwdb.c b/src/libsystemd/src/sd-hwdb/sd-hwdb.c new file mode 100644 index 0000000000..a48c7af124 --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/sd-hwdb.c @@ -0,0 +1,470 @@ +/*** + This file is part of systemd. + + Copyright 2012 Kay Sievers + Copyright 2008 Alan Jenkins + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/refcnt.h" +#include "basic/string-util.h" + +#include "hwdb-internal.h" +#include "hwdb-util.h" +#include "sd-hwdb.h" + +struct sd_hwdb { + RefCount n_ref; + int refcount; + + FILE *f; + struct stat st; + union { + struct trie_header_f *head; + const char *map; + }; + + char *modalias; + + OrderedHashmap *properties; + Iterator properties_iterator; + bool properties_modified; +}; + +struct linebuf { + char bytes[LINE_MAX]; + size_t size; + size_t len; +}; + +static void linebuf_init(struct linebuf *buf) { + buf->size = 0; + buf->len = 0; +} + +static const char *linebuf_get(struct linebuf *buf) { + if (buf->len + 1 >= sizeof(buf->bytes)) + return NULL; + buf->bytes[buf->len] = '\0'; + return buf->bytes; +} + +static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) { + if (buf->len + len >= sizeof(buf->bytes)) + return false; + memcpy(buf->bytes + buf->len, s, len); + buf->len += len; + return true; +} + +static bool linebuf_add_char(struct linebuf *buf, char c) { + if (buf->len + 1 >= sizeof(buf->bytes)) + return false; + buf->bytes[buf->len++] = c; + return true; +} + +static void linebuf_rem(struct linebuf *buf, size_t count) { + assert(buf->len >= count); + buf->len -= count; +} + +static void linebuf_rem_char(struct linebuf *buf) { + linebuf_rem(buf, 1); +} + +static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) { + return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size)); +} + +static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) { + const char *base = (const char *)node; + + base += le64toh(hwdb->head->node_size); + base += node->children_count * le64toh(hwdb->head->child_entry_size); + return (const struct trie_value_entry_f *)base; +} + +static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) { + return (const struct trie_node_f *)(hwdb->map + le64toh(off)); +} + +static const char *trie_string(sd_hwdb *hwdb, le64_t off) { + return hwdb->map + le64toh(off); +} + +static int trie_children_cmp_f(const void *v1, const void *v2) { + const struct trie_child_entry_f *n1 = v1; + const struct trie_child_entry_f *n2 = v2; + + return n1->c - n2->c; +} + +static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) { + struct trie_child_entry_f *child; + struct trie_child_entry_f search; + + search.c = c; + child = bsearch(&search, trie_node_children(hwdb, node), node->children_count, + le64toh(hwdb->head->child_entry_size), trie_children_cmp_f); + if (child) + return trie_node_from_off(hwdb, child->child_off); + return NULL; +} + +static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) { + int r; + + assert(hwdb); + assert(key); + assert(value); + + /* + * Silently ignore all properties which do not start with a + * space; future extensions might use additional prefixes. + */ + if (key[0] != ' ') + return 0; + + key++; + + r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_replace(hwdb->properties, key, (char*)value); + if (r < 0) + return r; + + hwdb->properties_modified = true; + + return 0; +} + +static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p, + struct linebuf *buf, const char *search) { + size_t len; + size_t i; + const char *prefix; + int err; + + prefix = trie_string(hwdb, node->prefix_off); + len = strlen(prefix + p); + linebuf_add(buf, prefix + p, len); + + for (i = 0; i < node->children_count; i++) { + const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i]; + + linebuf_add_char(buf, child->c); + err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search); + if (err < 0) + return err; + linebuf_rem_char(buf); + } + + if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0) + for (i = 0; i < le64toh(node->values_count); i++) { + err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off), + trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off)); + if (err < 0) + return err; + } + + linebuf_rem(buf, len); + return 0; +} + +static int trie_search_f(sd_hwdb *hwdb, const char *search) { + struct linebuf buf; + const struct trie_node_f *node; + size_t i = 0; + int err; + + linebuf_init(&buf); + + node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off); + while (node) { + const struct trie_node_f *child; + size_t p = 0; + + if (node->prefix_off) { + uint8_t c; + + for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) { + if (c == '*' || c == '?' || c == '[') + return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p); + if (c != search[i + p]) + return 0; + } + i += p; + } + + child = node_lookup_f(hwdb, node, '*'); + if (child) { + linebuf_add_char(&buf, '*'); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + child = node_lookup_f(hwdb, node, '?'); + if (child) { + linebuf_add_char(&buf, '?'); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + child = node_lookup_f(hwdb, node, '['); + if (child) { + linebuf_add_char(&buf, '['); + err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i); + if (err < 0) + return err; + linebuf_rem_char(&buf); + } + + if (search[i] == '\0') { + size_t n; + + for (n = 0; n < le64toh(node->values_count); n++) { + err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off), + trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off)); + if (err < 0) + return err; + } + return 0; + } + + child = node_lookup_f(hwdb, node, search[i]); + node = child; + i++; + } + return 0; +} + +static const char hwdb_bin_paths[] = + "/etc/systemd/hwdb/hwdb.bin\0" + "/etc/udev/hwdb.bin\0" + "/usr/lib/systemd/hwdb/hwdb.bin\0" +#ifdef HAVE_SPLIT_USR + "/lib/systemd/hwdb/hwdb.bin\0" +#endif + UDEVLIBEXECDIR "/hwdb.bin\0"; + +_public_ int sd_hwdb_new(sd_hwdb **ret) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + const char *hwdb_bin_path; + const char sig[] = HWDB_SIG; + + assert_return(ret, -EINVAL); + + hwdb = new0(sd_hwdb, 1); + if (!hwdb) + return -ENOMEM; + + hwdb->n_ref = REFCNT_INIT; + + /* find hwdb.bin in hwdb_bin_paths */ + NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) { + hwdb->f = fopen(hwdb_bin_path, "re"); + if (hwdb->f) + break; + else if (errno == ENOENT) + continue; + else + return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); + } + + if (!hwdb->f) { + log_debug("hwdb.bin does not exist, please run udevadm hwdb --update"); + return -ENOENT; + } + + if (fstat(fileno(hwdb->f), &hwdb->st) < 0 || + (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) + return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); + + hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0); + if (hwdb->map == MAP_FAILED) + return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path); + + if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || + (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { + log_debug("error recognizing the format of %s", hwdb_bin_path); + return -EINVAL; + } + + log_debug("=== trie on-disk ==="); + log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); + log_debug("file size: %8"PRIi64" bytes", hwdb->st.st_size); + log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size)); + log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len)); + log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len)); + + *ret = hwdb; + hwdb = NULL; + + return 0; +} + +_public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) { + assert_return(hwdb, NULL); + + assert_se(REFCNT_INC(hwdb->n_ref) >= 2); + + return hwdb; +} + +_public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) { + if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) { + if (hwdb->map) + munmap((void *)hwdb->map, hwdb->st.st_size); + safe_fclose(hwdb->f); + free(hwdb->modalias); + ordered_hashmap_free(hwdb->properties); + free(hwdb); + } + + return NULL; +} + +bool hwdb_validate(sd_hwdb *hwdb) { + bool found = false; + const char* p; + struct stat st; + + if (!hwdb) + return false; + if (!hwdb->f) + return false; + + /* if hwdb.bin doesn't exist anywhere, we need to update */ + NULSTR_FOREACH(p, hwdb_bin_paths) { + if (stat(p, &st) >= 0) { + found = true; + break; + } + } + if (!found) + return true; + + if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim)) + return true; + return false; +} + +static int properties_prepare(sd_hwdb *hwdb, const char *modalias) { + _cleanup_free_ char *mod = NULL; + int r; + + assert(hwdb); + assert(modalias); + + if (streq_ptr(modalias, hwdb->modalias)) + return 0; + + mod = strdup(modalias); + if (!mod) + return -ENOMEM; + + ordered_hashmap_clear(hwdb->properties); + + hwdb->properties_modified = true; + + r = trie_search_f(hwdb, modalias); + if (r < 0) + return r; + + free(hwdb->modalias); + hwdb->modalias = mod; + mod = NULL; + + return 0; +} + +_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) { + const char *value; + int r; + + assert_return(hwdb, -EINVAL); + assert_return(hwdb->f, -EINVAL); + assert_return(modalias, -EINVAL); + assert_return(_value, -EINVAL); + + r = properties_prepare(hwdb, modalias); + if (r < 0) + return r; + + value = ordered_hashmap_get(hwdb->properties, key); + if (!value) + return -ENOENT; + + *_value = value; + + return 0; +} + +_public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) { + int r; + + assert_return(hwdb, -EINVAL); + assert_return(hwdb->f, -EINVAL); + assert_return(modalias, -EINVAL); + + r = properties_prepare(hwdb, modalias); + if (r < 0) + return r; + + hwdb->properties_modified = false; + hwdb->properties_iterator = ITERATOR_FIRST; + + return 0; +} + +_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) { + const void *k; + void *v; + + assert_return(hwdb, -EINVAL); + assert_return(key, -EINVAL); + assert_return(value, -EINVAL); + + if (hwdb->properties_modified) + return -EAGAIN; + + ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k); + if (!k) + return 0; + + *key = k; + *value = v; + + return 1; +} diff --git a/src/libsystemd/src/sd-hwdb/sd-hwdb.h b/src/libsystemd/src/sd-hwdb/sd-hwdb.h new file mode 100644 index 0000000000..f46d7ad561 --- /dev/null +++ b/src/libsystemd/src/sd-hwdb/sd-hwdb.h @@ -0,0 +1,49 @@ +#ifndef foosdhwdbhfoo +#define foosdhwdbhfoo + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_hwdb sd_hwdb; + +sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb); +sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb); + +int sd_hwdb_new(sd_hwdb **ret); + +int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **value); + +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 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/libsystemd/src/sd-id128/Makefile b/src/libsystemd/src/sd-id128/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-id128/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-id128/id128-util.c b/src/libsystemd/src/sd-id128/id128-util.c new file mode 100644 index 0000000000..a9c43f9730 --- /dev/null +++ b/src/libsystemd/src/sd-id128/id128-util.c @@ -0,0 +1,195 @@ +/*** + 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 . +***/ + +#include +#include + +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/io-util.h" +#include "basic/stdio-util.h" + +#include "id128-util.h" + +char *id128_to_uuid_string(sd_id128_t id, char s[37]) { + unsigned n, k = 0; + + assert(s); + + /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ + + for (n = 0; n < 16; n++) { + + if (IN_SET(n, 4, 6, 8, 10)) + s[k++] = '-'; + + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); + } + + assert(k == 36); + + s[k] = 0; + + return s; +} + +bool id128_is_valid(const char *s) { + size_t i, l; + + assert(s); + + l = strlen(s); + if (l == 32) { + + /* Plain formatted 128bit hex string */ + + for (i = 0; i < l; i++) { + char c = s[i]; + + if (!(c >= '0' && c <= '9') && + !(c >= 'a' && c <= 'z') && + !(c >= 'A' && c <= 'Z')) + return false; + } + + } else if (l == 36) { + + /* Formatted UUID */ + + for (i = 0; i < l; i++) { + char c = s[i]; + + if ((i == 8 || i == 13 || i == 18 || i == 23)) { + if (c != '-') + return false; + } else { + if (!(c >= '0' && c <= '9') && + !(c >= 'a' && c <= 'z') && + !(c >= 'A' && c <= 'Z')) + return false; + } + } + + } else + return false; + + return true; +} + +int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { + char buffer[36 + 2]; + ssize_t l; + + assert(fd >= 0); + assert(f < _ID128_FORMAT_MAX); + + /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both + * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they + * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you + * accept". */ + + l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ + if (l < 0) + return (int) l; + if (l == 0) /* empty? */ + return -ENOMEDIUM; + + switch (l) { + + case 33: /* plain UUID with trailing newline */ + if (buffer[32] != '\n') + return -EINVAL; + + /* fall through */ + case 32: /* plain UUID without trailing newline */ + if (f == ID128_UUID) + return -EINVAL; + + buffer[32] = 0; + break; + + case 37: /* RFC UUID with trailing newline */ + if (buffer[36] != '\n') + return -EINVAL; + + /* fall through */ + case 36: /* RFC UUID without trailing newline */ + if (f == ID128_PLAIN) + return -EINVAL; + + buffer[36] = 0; + break; + + default: + return -EINVAL; + } + + return sd_id128_from_string(buffer, ret); +} + +int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + return id128_read_fd(fd, f, ret); +} + +int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { + char buffer[36 + 2]; + size_t sz; + int r; + + assert(fd >= 0); + assert(f < _ID128_FORMAT_MAX); + + if (f != ID128_UUID) { + sd_id128_to_string(id, buffer); + buffer[32] = '\n'; + sz = 33; + } else { + id128_to_uuid_string(id, buffer); + buffer[36] = '\n'; + sz = 37; + } + + r = loop_write(fd, buffer, sz, false); + if (r < 0) + return r; + + if (do_sync) { + if (fsync(fd) < 0) + return -errno; + } + + return r; +} + +int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { + _cleanup_close_ int fd = -1; + + fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); + if (fd < 0) + return -errno; + + return id128_write_fd(fd, f, id, do_sync); +} diff --git a/src/libsystemd/src/sd-id128/id128-util.h b/src/libsystemd/src/sd-id128/id128-util.h new file mode 100644 index 0000000000..d45c556746 --- /dev/null +++ b/src/libsystemd/src/sd-id128/id128-util.h @@ -0,0 +1,46 @@ +#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 . +***/ + +#include + +#include + +#include "basic/macro.h" + +char *id128_to_uuid_string(sd_id128_t id, char s[37]); + +/* Like SD_ID128_FORMAT_STR, but formats as UUID, not in plain format */ +#define ID128_UUID_FORMAT_STR "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" + +bool id128_is_valid(const char *s) _pure_; + +typedef enum Id128Format { + ID128_ANY, + ID128_PLAIN, /* formatted as 32 hex chars as-is */ + ID128_UUID, /* formatted as 36 character uuid string */ + _ID128_FORMAT_MAX, +} Id128Format; + +int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret); +int id128_read(const char *p, Id128Format f, sd_id128_t *ret); + +int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); +int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); diff --git a/src/libsystemd/src/sd-id128/sd-id128.c b/src/libsystemd/src/sd-id128/sd-id128.c new file mode 100644 index 0000000000..06fbe32f62 --- /dev/null +++ b/src/libsystemd/src/sd-id128/sd-id128.c @@ -0,0 +1,162 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/random-util.h" +#include "basic/util.h" + +#include "id128-util.h" + +_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { + unsigned n; + + assert_return(s, NULL); + + for (n = 0; n < 16; n++) { + s[n*2] = hexchar(id.bytes[n] >> 4); + s[n*2+1] = hexchar(id.bytes[n] & 0xF); + } + + s[32] = 0; + + return s; +} + +_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { + unsigned n, i; + sd_id128_t t; + bool is_guid = false; + + assert_return(s, -EINVAL); + + for (n = 0, i = 0; n < 16;) { + int a, b; + + if (s[i] == '-') { + /* Is this a GUID? Then be nice, and skip over + * the dashes */ + + if (i == 8) + is_guid = true; + else if (i == 13 || i == 18 || i == 23) { + if (!is_guid) + return -EINVAL; + } else + return -EINVAL; + + i++; + continue; + } + + a = unhexchar(s[i++]); + if (a < 0) + return -EINVAL; + + b = unhexchar(s[i++]); + if (b < 0) + return -EINVAL; + + t.bytes[n++] = (a << 4) | b; + } + + if (i != (is_guid ? 36 : 32)) + return -EINVAL; + + if (s[i] != 0) + return -EINVAL; + + if (ret) + *ret = t; + return 0; +} + +_public_ int sd_id128_get_machine(sd_id128_t *ret) { + static thread_local sd_id128_t saved_machine_id = {}; + int r; + + assert_return(ret, -EINVAL); + + if (sd_id128_is_null(saved_machine_id)) { + r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); + if (r < 0) + return r; + + if (sd_id128_is_null(saved_machine_id)) + return -EINVAL; + } + + *ret = saved_machine_id; + return 0; +} + +_public_ int sd_id128_get_boot(sd_id128_t *ret) { + static thread_local sd_id128_t saved_boot_id = {}; + int r; + + assert_return(ret, -EINVAL); + + if (sd_id128_is_null(saved_boot_id)) { + r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id); + if (r < 0) + return r; + } + + *ret = saved_boot_id; + return 0; +} + +static sd_id128_t make_v4_uuid(sd_id128_t id) { + /* Stolen from generate_random_uuid() of drivers/char/random.c + * in the kernel sources */ + + /* Set UUID version to 4 --- truly random generation */ + id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; + + /* Set the UUID variant to DCE */ + id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; + + return id; +} + +_public_ int sd_id128_randomize(sd_id128_t *ret) { + sd_id128_t t; + int r; + + assert_return(ret, -EINVAL); + + r = dev_urandom(&t, sizeof(t)); + if (r < 0) + return r; + + /* Turn this into a valid v4 UUID, to be nice. Note that we + * only guarantee this for newly generated UUIDs, not for + * pre-existing ones. */ + + *ret = make_v4_uuid(t); + return 0; +} diff --git a/src/libsystemd/src/sd-journal/Makefile b/src/libsystemd/src/sd-journal/Makefile new file mode 100644 index 0000000000..d0415fb0d3 --- /dev/null +++ b/src/libsystemd/src/sd-journal/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +audit_list_includes = -include linux/audit.h -include missing.h +ifneq ($(HAVE_AUDIT),) +audit_list_includes += -include libaudit.h +endif # HAVE_AUDIT + +$(outdir)/audit_type-list.txt: + $(AM_V_GEN)$(CPP) $(sd.ALL_CPPFLAGS) -dM $(audit_list_includes) - $@ + +$(outdir)/audit_type-to-name.h: $(outdir)/audit_type-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char *audit_type_to_string(int type) {\n\tswitch(type) {" } {printf " case AUDIT_%s: return \"%s\";\n", $$1, $$1 } END{ print " default: return NULL;\n\t}\n}\n" }' <$< >$@ + +# fsprg.c is a drop-in file using void pointer arithmetic +libsystemd_journal_internal_la_CFLAGS += \ + $(GCRYPT_CFLAGS) \ + -Wno-pointer-arith + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/src/sd-journal/audit-type.c b/src/libsystemd/src/sd-journal/audit-type.c new file mode 100644 index 0000000000..0ccbb456c3 --- /dev/null +++ b/src/libsystemd/src/sd-journal/audit-type.c @@ -0,0 +1,31 @@ +/*** + 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 . +***/ + +#include + +#include +#ifdef HAVE_AUDIT +# include +#endif + +#include "audit_type-to-name.h" +#include "basic/macro.h" +#include "basic/missing.h" + +#include "audit-type.h" diff --git a/src/libsystemd/src/sd-journal/audit-type.h b/src/libsystemd/src/sd-journal/audit-type.h new file mode 100644 index 0000000000..8b96e2e466 --- /dev/null +++ b/src/libsystemd/src/sd-journal/audit-type.h @@ -0,0 +1,37 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/macro.h" + +const char *audit_type_to_string(int type); +int audit_type_from_string(const char *s); + +/* This is inspired by DNS TYPEnnn formatting */ +#define audit_type_name_alloca(type) \ + ({ \ + const char *_s_; \ + _s_ = audit_type_to_string(type); \ + if (!_s_) { \ + _s_ = alloca(strlen("AUDIT") + DECIMAL_STR_MAX(int)); \ + sprintf((char*) _s_, "AUDIT%04i", type); \ + } \ + _s_; \ + }) diff --git a/src/libsystemd/src/sd-journal/catalog.c b/src/libsystemd/src/sd-journal/catalog.c new file mode 100644 index 0000000000..7dc02deb49 --- /dev/null +++ b/src/libsystemd/src/sd-journal/catalog.c @@ -0,0 +1,768 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/siphash24.h" +#include "basic/sparse-endian.h" +#include "basic/strbuf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "catalog.h" + +const char * const catalog_file_dirs[] = { + "/usr/local/lib/systemd/catalog/", + "/usr/lib/systemd/catalog/", + NULL +}; + +#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' } + +typedef struct CatalogHeader { + uint8_t signature[8]; /* "RHHHKSLP" */ + le32_t compatible_flags; + le32_t incompatible_flags; + le64_t header_size; + le64_t n_items; + le64_t catalog_item_size; +} CatalogHeader; + +typedef struct CatalogItem { + sd_id128_t id; + char language[32]; + le64_t offset; +} CatalogItem; + +static void catalog_hash_func(const void *p, struct siphash *state) { + const CatalogItem *i = p; + + 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) { + const CatalogItem *i = a, *j = b; + unsigned k; + + for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) { + if (i->id.bytes[k] < j->id.bytes[k]) + return -1; + if (i->id.bytes[k] > j->id.bytes[k]) + return 1; + } + + return strcmp(i->language, j->language); +} + +const struct hash_ops catalog_hash_ops = { + .hash = catalog_hash_func, + .compare = catalog_compare_func +}; + +static bool next_header(const char **s) { + const char *e; + + e = strchr(*s, '\n'); + + /* Unexpected end */ + if (!e) + return false; + + /* End of headers */ + if (e == *s) + return false; + + *s = e + 1; + return true; +} + +static const char *skip_header(const char *s) { + while (next_header(&s)) + ; + return s; +} + +static char *combine_entries(const char *one, const char *two) { + const char *b1, *b2; + size_t l1, l2, n; + char *dest, *p; + + /* Find split point of headers to body */ + b1 = skip_header(one); + b2 = skip_header(two); + + l1 = strlen(one); + l2 = strlen(two); + dest = new(char, l1 + l2 + 1); + if (!dest) { + log_oom(); + return NULL; + } + + p = dest; + + /* Headers from @one */ + n = b1 - one; + p = mempcpy(p, one, n); + + /* Headers from @two, these will only be found if not present above */ + n = b2 - two; + p = mempcpy(p, two, n); + + /* Body from @one */ + n = l1 - (b1 - one); + if (n > 0) { + memcpy(p, b1, n); + p += n; + + /* Body from @two */ + } else { + n = l2 - (b2 - two); + memcpy(p, b2, n); + p += n; + } + + assert(p - dest <= (ptrdiff_t)(l1 + l2)); + p[0] = '\0'; + return dest; +} + +static int finish_item( + Hashmap *h, + sd_id128_t id, + const char *language, + char *payload, size_t payload_size) { + + _cleanup_free_ CatalogItem *i = NULL; + _cleanup_free_ char *prev = NULL, *combined = NULL; + + assert(h); + assert(payload); + assert(payload_size > 0); + + i = new0(CatalogItem, 1); + if (!i) + return log_oom(); + + i->id = id; + if (language) { + assert(strlen(language) > 1 && strlen(language) < 32); + strcpy(i->language, language); + } + + prev = hashmap_get(h, i); + if (prev) { + /* Already have such an item, combine them */ + combined = combine_entries(payload, prev); + if (!combined) + return log_oom(); + + if (hashmap_update(h, i, combined) < 0) + return log_oom(); + combined = NULL; + } else { + /* A new item */ + combined = memdup(payload, payload_size + 1); + if (!combined) + return log_oom(); + + if (hashmap_put(h, i, combined) < 0) + return log_oom(); + i = NULL; + combined = NULL; + } + + return 0; +} + +int catalog_file_lang(const char* filename, char **lang) { + char *beg, *end, *_lang; + + end = endswith(filename, ".catalog"); + if (!end) + return 0; + + beg = end - 1; + while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32) + beg--; + + if (*beg != '.' || end <= beg + 1) + return 0; + + _lang = strndup(beg + 1, end - beg - 1); + if (!_lang) + return -ENOMEM; + + *lang = _lang; + return 1; +} + +static int catalog_entry_lang(const char* filename, int line, + const char* t, const char* deflang, char **lang) { + size_t c; + + c = strlen(t); + if (c == 0) { + log_error("[%s:%u] Language too short.", filename, line); + return -EINVAL; + } + if (c > 31) { + log_error("[%s:%u] language too long.", filename, line); + return -EINVAL; + } + + if (deflang) { + if (streq(t, deflang)) { + log_warning("[%s:%u] language specified unnecessarily", + filename, line); + return 0; + } else + log_warning("[%s:%u] language differs from default for file", + filename, line); + } + + *lang = strdup(t); + if (!*lang) + return -ENOMEM; + + return 0; +} + +int catalog_import_file(Hashmap *h, const char *path) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *payload = NULL; + size_t payload_size = 0, payload_allocated = 0; + unsigned n = 0; + sd_id128_t id; + _cleanup_free_ char *deflang = NULL, *lang = NULL; + bool got_id = false, empty_line = true; + int r; + + assert(h); + assert(path); + + f = fopen(path, "re"); + if (!f) + return log_error_errno(errno, "Failed to open file %s: %m", path); + + r = catalog_file_lang(path, &deflang); + if (r < 0) + 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); + + for (;;) { + char line[LINE_MAX]; + size_t line_len; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + return log_error_errno(errno, "Failed to read file %s: %m", path); + } + + n++; + + truncate_nl(line); + + if (line[0] == 0) { + empty_line = true; + continue; + } + + if (strchr(COMMENTS "\n", line[0])) + continue; + + if (empty_line && + strlen(line) >= 2+1+32 && + line[0] == '-' && + line[1] == '-' && + line[2] == ' ' && + (line[2+1+32] == ' ' || line[2+1+32] == '\0')) { + + bool with_language; + sd_id128_t jd; + + /* New entry */ + + with_language = line[2+1+32] != '\0'; + line[2+1+32] = '\0'; + + if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { + + if (got_id) { + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); + if (r < 0) + return r; + + lang = mfree(lang); + payload_size = 0; + } + + if (with_language) { + char *t; + + t = strstrip(line + 2 + 1 + 32 + 1); + r = catalog_entry_lang(path, n, t, deflang, &lang); + if (r < 0) + return r; + } + + got_id = true; + empty_line = false; + id = jd; + + continue; + } + } + + /* Payload */ + if (!got_id) { + log_error("[%s:%u] Got payload before ID.", path, n); + return -EINVAL; + } + + line_len = strlen(line); + if (!GREEDY_REALLOC(payload, payload_allocated, + payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1)) + return log_oom(); + + if (empty_line) + payload[payload_size++] = '\n'; + memcpy(payload + payload_size, line, line_len); + payload_size += line_len; + payload[payload_size++] = '\n'; + payload[payload_size] = '\0'; + + empty_line = false; + } + + if (got_id) { + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); + if (r < 0) + return r; + } + + return 0; +} + +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; + _cleanup_free_ char *d, *p = NULL; + size_t k; + + d = dirname_malloc(database); + if (!d) + return log_oom(); + + r = mkdir_p(d, 0775); + if (r < 0) + return log_error_errno(r, "Recursive mkdir %s: %m", d); + + r = fopen_temporary(database, &w, &p); + if (r < 0) + return log_error_errno(r, "Failed to open database for writing: %s: %m", + database); + + zero(header); + memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); + header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); + header.catalog_item_size = htole64(sizeof(CatalogItem)); + header.n_items = htole64(n); + + r = -EIO; + + k = fwrite(&header, 1, sizeof(header), w); + if (k != sizeof(header)) { + log_error("%s: failed to write header.", p); + goto error; + } + + k = fwrite(items, 1, n * sizeof(CatalogItem), w); + if (k != n * sizeof(CatalogItem)) { + log_error("%s: failed to write database.", p); + goto error; + } + + k = fwrite(sb->buf, 1, sb->len, w); + if (k != sb->len) { + log_error("%s: failed to write strings.", p); + goto error; + } + + r = fflush_and_check(w); + if (r < 0) { + log_error_errno(r, "%s: failed to write database: %m", p); + goto error; + } + + fchmod(fileno(w), 0644); + + if (rename(p, database) < 0) { + r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); + goto error; + } + + return ftello(w); + +error: + (void) unlink(p); + return r; +} + +int catalog_update(const char* database, const char* root, const char* const* dirs) { + _cleanup_strv_free_ char **files = NULL; + char **f; + struct strbuf *sb = NULL; + _cleanup_hashmap_free_free_free_ Hashmap *h = NULL; + _cleanup_free_ CatalogItem *items = NULL; + ssize_t offset; + char *payload; + CatalogItem *i; + Iterator j; + unsigned n; + int r; + int64_t sz; + + h = hashmap_new(&catalog_hash_ops); + sb = strbuf_new(); + + if (!h || !sb) { + r = log_oom(); + goto finish; + } + + r = conf_files_list_strv(&files, ".catalog", root, dirs); + if (r < 0) { + log_error_errno(r, "Failed to get catalog files: %m"); + goto finish; + } + + STRV_FOREACH(f, files) { + log_debug("Reading file '%s'", *f); + r = catalog_import_file(h, *f); + if (r < 0) { + log_error_errno(r, "Failed to import file '%s': %m", *f); + goto finish; + } + } + + if (hashmap_size(h) <= 0) { + log_info("No items in catalog."); + goto finish; + } else + log_debug("Found %u items in catalog.", hashmap_size(h)); + + items = new(CatalogItem, hashmap_size(h)); + if (!items) { + r = log_oom(); + goto finish; + } + + n = 0; + HASHMAP_FOREACH_KEY(payload, i, h, j) { + log_debug("Found " SD_ID128_FORMAT_STR ", language %s", + SD_ID128_FORMAT_VAL(i->id), + isempty(i->language) ? "C" : i->language); + + offset = strbuf_add_string(sb, payload, strlen(payload)); + if (offset < 0) { + r = log_oom(); + goto finish; + } + i->offset = htole64((uint64_t) offset); + items[n++] = *i; + } + + assert(n == hashmap_size(h)); + qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func); + + strbuf_complete(sb); + + 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: + strbuf_cleanup(sb); + + return r; +} + +static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) { + const CatalogHeader *h; + int fd; + void *p; + struct stat st; + + assert(_fd); + assert(_st); + assert(_p); + + fd = open(database, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) { + safe_close(fd); + return -errno; + } + + if (st.st_size < (off_t) sizeof(CatalogHeader)) { + safe_close(fd); + return -EINVAL; + } + + p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) { + safe_close(fd); + return -errno; + } + + h = p; + if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 || + le64toh(h->header_size) < sizeof(CatalogHeader) || + le64toh(h->catalog_item_size) < sizeof(CatalogItem) || + h->incompatible_flags != 0 || + le64toh(h->n_items) <= 0 || + st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) { + safe_close(fd); + munmap(p, st.st_size); + return -EBADMSG; + } + + *_fd = fd; + *_st = st; + *_p = p; + + return 0; +} + +static const char *find_id(void *p, sd_id128_t id) { + CatalogItem key, *f = NULL; + const CatalogHeader *h = p; + const char *loc; + + zero(key); + key.id = id; + + loc = setlocale(LC_MESSAGES, NULL); + if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) { + strncpy(key.language, loc, sizeof(key.language)); + key.language[strcspn(key.language, ".@")] = 0; + + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + if (!f) { + char *e; + + e = strchr(key.language, '_'); + if (e) { + *e = 0; + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + } + } + } + + if (!f) { + zero(key.language); + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + } + + if (!f) + return NULL; + + return (const char*) p + + le64toh(h->header_size) + + le64toh(h->n_items) * le64toh(h->catalog_item_size) + + le64toh(f->offset); +} + +int catalog_get(const char* database, sd_id128_t id, char **_text) { + _cleanup_close_ int fd = -1; + void *p = NULL; + struct stat st = {}; + char *text = NULL; + int r; + const char *s; + + assert(_text); + + r = open_mmap(database, &fd, &st, &p); + if (r < 0) + return r; + + s = find_id(p, id); + if (!s) { + r = -ENOENT; + goto finish; + } + + text = strdup(s); + if (!text) { + r = -ENOMEM; + goto finish; + } + + *_text = text; + r = 0; + +finish: + if (p) + munmap(p, st.st_size); + + return r; +} + +static char *find_header(const char *s, const char *header) { + + for (;;) { + const char *v; + + v = startswith(s, header); + if (v) { + v += strspn(v, WHITESPACE); + return strndup(v, strcspn(v, NEWLINE)); + } + + if (!next_header(&s)) + return NULL; + } +} + +static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) { + if (oneline) { + _cleanup_free_ char *subject = NULL, *defined_by = NULL; + + subject = find_header(s, "Subject:"); + defined_by = find_header(s, "Defined-By:"); + + fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", + SD_ID128_FORMAT_VAL(id), + strna(defined_by), strna(subject)); + } else + fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n", + SD_ID128_FORMAT_VAL(id), s); +} + + +int catalog_list(FILE *f, const char *database, bool oneline) { + _cleanup_close_ int fd = -1; + void *p = NULL; + struct stat st; + const CatalogHeader *h; + const CatalogItem *items; + int r; + unsigned n; + sd_id128_t last_id; + bool last_id_set = false; + + r = open_mmap(database, &fd, &st, &p); + if (r < 0) + return r; + + h = p; + items = (const CatalogItem*) ((const uint8_t*) p + le64toh(h->header_size)); + + for (n = 0; n < le64toh(h->n_items); n++) { + const char *s; + + if (last_id_set && sd_id128_equal(last_id, items[n].id)) + continue; + + assert_se(s = find_id(p, items[n].id)); + + dump_catalog_entry(f, items[n].id, s, oneline); + + last_id_set = true; + last_id = items[n].id; + } + + munmap(p, st.st_size); + + return 0; +} + +int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) { + char **item; + int r = 0; + + STRV_FOREACH(item, items) { + sd_id128_t id; + int k; + _cleanup_free_ char *msg = NULL; + + k = sd_id128_from_string(*item, &id); + if (k < 0) { + log_error_errno(k, "Failed to parse id128 '%s': %m", *item); + if (r == 0) + r = k; + continue; + } + + k = catalog_get(database, id, &msg); + if (k < 0) { + 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; + } + + dump_catalog_entry(f, id, msg, oneline); + } + + return r; +} diff --git a/src/libsystemd/src/sd-journal/catalog.h b/src/libsystemd/src/sd-journal/catalog.h new file mode 100644 index 0000000000..fa60757723 --- /dev/null +++ b/src/libsystemd/src/sd-journal/catalog.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "basic/hashmap.h" +#include "basic/strbuf.h" + +int catalog_import_file(Hashmap *h, const char *path); +int catalog_update(const char* database, const char* root, const char* const* dirs); +int catalog_get(const char* database, sd_id128_t id, char **data); +int catalog_list(FILE *f, const char* database, bool oneline); +int catalog_list_items(FILE *f, const char* database, bool oneline, char **items); +int catalog_file_lang(const char *filename, char **lang); +extern const char * const catalog_file_dirs[]; +extern const struct hash_ops catalog_hash_ops; diff --git a/src/libsystemd/src/sd-journal/compress.c b/src/libsystemd/src/sd-journal/compress.c new file mode 100644 index 0000000000..a96d93c455 --- /dev/null +++ b/src/libsystemd/src/sd-journal/compress.c @@ -0,0 +1,684 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_XZ +#include +#endif + +#ifdef HAVE_LZ4 +#include +#include +#endif + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/sparse-endian.h" +#include "basic/string-table.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "compress.h" +#include "journal-def.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)) + +static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = { + [OBJECT_COMPRESSED_XZ] = "XZ", + [OBJECT_COMPRESSED_LZ4] = "LZ4", +}; + +DEFINE_STRING_TABLE_LOOKUP(object_compressed, int); + +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[] = { + { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt }, + { LZMA_VLI_UNKNOWN, NULL } + }; + lzma_ret ret; + size_t out_pos = 0; + + 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 + * compressed result is longer than the original */ + + if (src_size < 80) + return -ENOBUFS; + + ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL, + src, src_size, dst, &out_pos, dst_alloc_size); + if (ret != LZMA_OK) + return -ENOBUFS; + + *dst_size = out_pos; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +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 + * compressed result is longer than the original */ + + if (src_size < 9) + return -ENOBUFS; + + r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); + if (r <= 0) + return -ENOBUFS; + + *(le64_t*) dst = htole64(src_size); + *dst_size = r + 8; + + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + + +int decompress_blob_xz(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + size_t space; + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return -ENOMEM; + + space = MIN(src_size * 2, dst_max ?: (size_t) -1); + if (!greedy_realloc(dst, dst_alloc_size, space, 1)) + return -ENOMEM; + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *dst; + s.avail_out = space; + + for (;;) { + size_t used; + + ret = lzma_code(&s, LZMA_FINISH); + + if (ret == LZMA_STREAM_END) + break; + else if (ret != LZMA_OK) + return -ENOMEM; + + if (dst_max > 0 && (space - s.avail_out) >= dst_max) + break; + else if (dst_max > 0 && space == dst_max) + return -ENOBUFS; + + used = space - s.avail_out; + space = MIN(2 * space, dst_max ?: (size_t) -1); + if (!greedy_realloc(dst, dst_alloc_size, space, 1)) + return -ENOMEM; + + s.avail_out = space - used; + s.next_out = *(uint8_t**)dst + used; + } + + *dst_size = space - s.avail_out; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_blob_lz4(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + +#ifdef HAVE_LZ4 + char* out; + int r, size; /* LZ4 uses int for size */ + + assert(src); + assert(src_size > 0); + assert(dst); + assert(dst_alloc_size); + assert(dst_size); + assert(*dst_alloc_size == 0 || *dst); + + if (src_size <= 8) + return -EBADMSG; + + size = le64toh( *(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); + if (!out) + return -ENOMEM; + *dst = out; + *dst_alloc_size = size; + } else + out = *dst; + + r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); + if (r < 0 || r != size) + return -EBADMSG; + + *dst_size = size; + return 0; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_blob(int compression, + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) { + if (compression == OBJECT_COMPRESSED_XZ) + return decompress_blob_xz(src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else if (compression == OBJECT_COMPRESSED_LZ4) + return decompress_blob_lz4(src, src_size, + dst, dst_alloc_size, dst_size, dst_max); + else + return -EBADMSG; +} + + +int decompress_startswith_xz(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + + /* Checks whether the decompressed blob starts with the + * mentioned prefix. The byte extra needs to follow the + * prefix */ + + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) + return -EBADMSG; + + if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) + return -ENOMEM; + + s.next_in = src; + s.avail_in = src_size; + + s.next_out = *buffer; + s.avail_out = *buffer_size; + + for (;;) { + ret = lzma_code(&s, LZMA_FINISH); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + return -EBADMSG; + + if (*buffer_size - s.avail_out >= prefix_len + 1) + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; + + if (ret == LZMA_STREAM_END) + return 0; + + s.avail_out += *buffer_size; + + if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1))) + return -ENOMEM; + + s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out; + } + +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_startswith_lz4(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { +#ifdef HAVE_LZ4 + /* Checks whether the decompressed blob starts with the + * mentioned prefix. The byte extra needs to follow the + * prefix */ + + int r; + size_t size; + + assert(src); + assert(src_size > 0); + assert(buffer); + assert(buffer_size); + assert(prefix); + assert(*buffer_size == 0 || *buffer); + + if (src_size <= 8) + return -EBADMSG; + + if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) + return -ENOMEM; + + r = LZ4_decompress_safe_partial((char*)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 (size >= prefix_len + 1) + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; + else + return 0; + +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_startswith(int compression, + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra) { + if (compression == OBJECT_COMPRESSED_XZ) + return decompress_startswith_xz(src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else if (compression == OBJECT_COMPRESSED_LZ4) + return decompress_startswith_lz4(src, src_size, + buffer, buffer_size, + prefix, prefix_len, + extra); + else + return -EBADMSG; +} + +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + uint8_t buf[BUFSIZ], out[BUFSIZ]; + lzma_action action = LZMA_RUN; + + assert(fdf >= 0); + assert(fdt >= 0); + + ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); + if (ret != LZMA_OK) { + log_error("Failed to initialize XZ encoder: code %u", ret); + return -EINVAL; + } + + for (;;) { + if (s.avail_in == 0 && action == LZMA_RUN) { + size_t m = sizeof(buf); + ssize_t n; + + if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes) + m = (size_t) max_bytes; + + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) + action = LZMA_FINISH; + else { + s.next_in = buf; + s.avail_in = n; + + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); + max_bytes -= n; + } + } + } + + if (s.avail_out == 0) { + s.next_out = out; + s.avail_out = sizeof(out); + } + + ret = lzma_code(&s, action); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) { + log_error("Compression failed: code %u", ret); + return -EBADMSG; + } + + if (s.avail_out == 0 || ret == LZMA_STREAM_END) { + ssize_t n, k; + + n = sizeof(out) - s.avail_out; + + k = loop_write(fdt, out, n, false); + if (k < 0) + return k; + + if (ret == LZMA_STREAM_END) { + log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", + s.total_in, s.total_out, + (double) s.total_out / s.total_in * 100); + + return 0; + } + } + } +#else + return -EPROTONOSUPPORT; +#endif +} + +#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, + }; + + c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; + + if (fstat(fdf, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); + + 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; + + n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences); + if (LZ4F_isError(n)) + return -EINVAL; + + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0); + if (src == MAP_FAILED) + return -errno; + + log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n); + + while (total_in < (size_t) st.st_size) { + ssize_t k; + + 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; + } + + total_in += k; + offset += n; + total_out += n; + + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes); + return -EFBIG; + } + + if (size - offset < frame_size + 4) { + k = loop_write(fdt, buf, offset, false); + if (k < 0) { + r = k; + goto cleanup; + } + offset = 0; + } + } + + n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; + } + + 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); + cleanup: + munmap(src, st.st_size); + return r; +#else + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { + +#ifdef HAVE_XZ + _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; + lzma_ret ret; + + uint8_t buf[BUFSIZ], out[BUFSIZ]; + lzma_action action = LZMA_RUN; + + assert(fdf >= 0); + assert(fdt >= 0); + + ret = lzma_stream_decoder(&s, UINT64_MAX, 0); + if (ret != LZMA_OK) { + log_debug("Failed to initialize XZ decoder: code %u", ret); + return -ENOMEM; + } + + for (;;) { + if (s.avail_in == 0 && action == LZMA_RUN) { + ssize_t n; + + n = read(fdf, buf, sizeof(buf)); + if (n < 0) + return -errno; + if (n == 0) + action = LZMA_FINISH; + else { + s.next_in = buf; + s.avail_in = n; + } + } + + if (s.avail_out == 0) { + s.next_out = out; + s.avail_out = sizeof(out); + } + + ret = lzma_code(&s, action); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) { + log_debug("Decompression failed: code %u", ret); + return -EBADMSG; + } + + if (s.avail_out == 0 || ret == LZMA_STREAM_END) { + ssize_t n, k; + + n = sizeof(out) - s.avail_out; + + if (max_bytes != (uint64_t) -1) { + if (max_bytes < (uint64_t) n) + return -EFBIG; + + max_bytes -= n; + } + + k = loop_write(fdt, out, n, false); + if (k < 0) + return k; + + if (ret == LZMA_STREAM_END) { + log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", + s.total_in, s.total_out, + (double) s.total_out / s.total_in * 100); + + return 0; + } + } + } +#else + log_debug("Cannot decompress file. Compiled without XZ support."); + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { +#ifdef HAVE_LZ4 + 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; + + if (fstat(in, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); + + buf = malloc(LZ4_BUFSIZE); + if (!buf) + return -ENOMEM; + + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0); + if (src == MAP_FAILED) + return -errno; + + while (total_in < (size_t) st.st_size) { + size_t produced = LZ4_BUFSIZE; + size_t used = st.st_size - total_in; + + c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); + if (LZ4F_isError(c)) { + r = -EBADMSG; + goto cleanup; + } + + total_in += used; + total_out += produced; + + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes); + r = -EFBIG; + goto cleanup; + } + + r = loop_write(out, buf, produced, false); + if (r < 0) + goto cleanup; + } + + log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", + total_in, total_out, + (double) total_out / total_in * 100); + cleanup: + munmap(src, st.st_size); + return r; +#else + log_debug("Cannot decompress file. Compiled without LZ4 support."); + return -EPROTONOSUPPORT; +#endif +} + +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { + + if (endswith(filename, ".lz4")) + return decompress_stream_lz4(fdf, fdt, max_bytes); + else if (endswith(filename, ".xz")) + return decompress_stream_xz(fdf, fdt, max_bytes); + else + return -EPROTONOSUPPORT; +} diff --git a/src/libsystemd/src/sd-journal/compress.h b/src/libsystemd/src/sd-journal/compress.h new file mode 100644 index 0000000000..c138099d9a --- /dev/null +++ b/src/libsystemd/src/sd-journal/compress.h @@ -0,0 +1,85 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "journal-def.h" + +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_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_alloc_size, size_t *dst_size) { + int r; +#ifdef HAVE_LZ4 + 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_alloc_size, dst_size); + if (r == 0) + return OBJECT_COMPRESSED_XZ; +#endif + return r; +} + +int decompress_blob_xz(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +int decompress_blob_lz4(const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +int decompress_blob(int compression, + const void *src, uint64_t src_size, + void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); + +int decompress_startswith_xz(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); +int decompress_startswith_lz4(const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); +int decompress_startswith(int compression, + const void *src, uint64_t src_size, + void **buffer, size_t *buffer_size, + const void *prefix, size_t prefix_len, + uint8_t extra); + +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes); +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes); + +int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); +int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); + +#ifdef HAVE_LZ4 +# define compress_stream compress_stream_lz4 +# define COMPRESSED_EXT ".lz4" +#else +# define compress_stream compress_stream_xz +# define COMPRESSED_EXT ".xz" +#endif + +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes); diff --git a/src/libsystemd/src/sd-journal/fsprg.c b/src/libsystemd/src/sd-journal/fsprg.c new file mode 100644 index 0000000000..612b10f3a9 --- /dev/null +++ b/src/libsystemd/src/sd-journal/fsprg.c @@ -0,0 +1,374 @@ +/* + * fsprg v0.1 - (seekable) forward-secure pseudorandom generator + * Copyright (C) 2012 B. Poettering + * Contact: fsprg@point-at-infinity.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/* + * See "Practical Secure Logging: Seekable Sequential Key Generators" + * by G. A. Marson, B. Poettering for details: + * + * http://eprint.iacr.org/2013/397 + */ + +#include +#include + +#include "fsprg.h" +#include "gcrypt-util.h" + +#define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384)) +#define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar)); + +#define RND_HASH GCRY_MD_SHA256 +#define RND_GEN_P 0x01 +#define RND_GEN_Q 0x02 +#define RND_GEN_X 0x03 + +/******************************************************************************/ + +static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) { + unsigned len; + size_t nwritten; + + assert(gcry_mpi_cmp_ui(x, 0) >= 0); + len = (gcry_mpi_get_nbits(x) + 7) / 8; + assert(len <= buflen); + memzero(buf, buflen); + gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x); + assert(nwritten == len); +} + +static gcry_mpi_t mpi_import(const void *buf, size_t buflen) { + gcry_mpi_t h; + unsigned len; + + assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0); + len = (gcry_mpi_get_nbits(h) + 7) / 8; + assert(len <= buflen); + assert(gcry_mpi_cmp_ui(h, 0) >= 0); + + return h; +} + +static void uint64_export(void *buf, size_t buflen, uint64_t x) { + assert(buflen == 8); + ((uint8_t*) buf)[0] = (x >> 56) & 0xff; + ((uint8_t*) buf)[1] = (x >> 48) & 0xff; + ((uint8_t*) buf)[2] = (x >> 40) & 0xff; + ((uint8_t*) buf)[3] = (x >> 32) & 0xff; + ((uint8_t*) buf)[4] = (x >> 24) & 0xff; + ((uint8_t*) buf)[5] = (x >> 16) & 0xff; + ((uint8_t*) buf)[6] = (x >> 8) & 0xff; + ((uint8_t*) buf)[7] = (x >> 0) & 0xff; +} + +_pure_ static uint64_t uint64_import(const void *buf, size_t buflen) { + assert(buflen == 8); + return + (uint64_t)(((uint8_t*) buf)[0]) << 56 | + (uint64_t)(((uint8_t*) buf)[1]) << 48 | + (uint64_t)(((uint8_t*) buf)[2]) << 40 | + (uint64_t)(((uint8_t*) buf)[3]) << 32 | + (uint64_t)(((uint8_t*) buf)[4]) << 24 | + (uint64_t)(((uint8_t*) buf)[5]) << 16 | + (uint64_t)(((uint8_t*) buf)[6]) << 8 | + (uint64_t)(((uint8_t*) buf)[7]) << 0; +} + +/* deterministically generate from seed/idx a string of buflen pseudorandom bytes */ +static void det_randomize(void *buf, size_t buflen, const void *seed, size_t seedlen, uint32_t idx) { + gcry_md_hd_t hd, hd2; + size_t olen, cpylen; + uint32_t ctr; + + olen = gcry_md_get_algo_dlen(RND_HASH); + gcry_md_open(&hd, RND_HASH, 0); + gcry_md_write(hd, seed, seedlen); + gcry_md_putc(hd, (idx >> 24) & 0xff); + gcry_md_putc(hd, (idx >> 16) & 0xff); + gcry_md_putc(hd, (idx >> 8) & 0xff); + gcry_md_putc(hd, (idx >> 0) & 0xff); + + for (ctr = 0; buflen; ctr++) { + gcry_md_copy(&hd2, hd); + gcry_md_putc(hd2, (ctr >> 24) & 0xff); + gcry_md_putc(hd2, (ctr >> 16) & 0xff); + gcry_md_putc(hd2, (ctr >> 8) & 0xff); + gcry_md_putc(hd2, (ctr >> 0) & 0xff); + gcry_md_final(hd2); + cpylen = (buflen < olen) ? buflen : olen; + memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen); + gcry_md_close(hd2); + buf += cpylen; + buflen -= cpylen; + } + gcry_md_close(hd); +} + +/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */ +static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint32_t idx) { + size_t buflen = bits / 8; + uint8_t buf[buflen]; + gcry_mpi_t p; + + assert(bits % 8 == 0); + assert(buflen > 0); + + det_randomize(buf, buflen, seed, seedlen, idx); + buf[0] |= 0xc0; /* set upper two bits, so that n=pq has maximum size */ + buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */ + + p = mpi_import(buf, buflen); + while (gcry_prime_check(p, 0)) + gcry_mpi_add_ui(p, p, 4); + + return p; +} + +/* deterministically generate from seed/idx a quadratic residue (mod n) */ +static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen, uint32_t idx, unsigned secpar) { + size_t buflen = secpar / 8; + uint8_t buf[buflen]; + gcry_mpi_t x; + + det_randomize(buf, buflen, seed, seedlen, idx); + buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */ + x = mpi_import(buf, buflen); + assert(gcry_mpi_cmp(x, n) < 0); + gcry_mpi_mulm(x, x, x, n); + return x; +} + +/* compute 2^m (mod phi(p)), for a prime p */ +static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) { + gcry_mpi_t phi, r; + int n; + + phi = gcry_mpi_new(0); + gcry_mpi_sub_ui(phi, p, 1); + + /* count number of used bits in m */ + for (n = 0; (1ULL << n) <= m; n++) + ; + + r = gcry_mpi_new(0); + gcry_mpi_set_ui(r, 1); + while (n) { /* square and multiply algorithm for fast exponentiation */ + n--; + gcry_mpi_mulm(r, r, r, phi); + if (m & ((uint64_t)1 << n)) { + gcry_mpi_add(r, r, r); + if (gcry_mpi_cmp(r, phi) >= 0) + gcry_mpi_sub(r, r, phi); + } + } + + gcry_mpi_release(phi); + return r; +} + +/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */ +static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) { + *xp = gcry_mpi_new(0); + *xq = gcry_mpi_new(0); + gcry_mpi_mod(*xp, x, p); + gcry_mpi_mod(*xq, x, q); +} + +/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */ +static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) { + gcry_mpi_t a, u; + + a = gcry_mpi_new(0); + u = gcry_mpi_new(0); + *x = gcry_mpi_new(0); + gcry_mpi_subm(a, xq, xp, q); + gcry_mpi_invm(u, p, q); + gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */ + gcry_mpi_mul(*x, p, a); + gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */ + gcry_mpi_release(a); + gcry_mpi_release(u); +} + +/******************************************************************************/ + +size_t FSPRG_mskinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */ +} + +size_t FSPRG_mpkinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + _secpar / 8; /* to store header,n */ +} + +size_t FSPRG_stateinbytes(unsigned _secpar) { + VALIDATE_SECPAR(_secpar); + return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */ +} + +static void store_secpar(void *buf, uint16_t secpar) { + secpar = secpar / 16 - 1; + ((uint8_t*) buf)[0] = (secpar >> 8) & 0xff; + ((uint8_t*) buf)[1] = (secpar >> 0) & 0xff; +} + +static uint16_t read_secpar(const void *buf) { + uint16_t secpar; + secpar = + (uint16_t)(((uint8_t*) buf)[0]) << 8 | + (uint16_t)(((uint8_t*) buf)[1]) << 0; + return 16 * (secpar + 1); +} + +void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) { + uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN]; + gcry_mpi_t n, p, q; + uint16_t secpar; + + VALIDATE_SECPAR(_secpar); + secpar = _secpar; + + initialize_libgcrypt(false); + + if (!seed) { + gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM); + seed = iseed; + seedlen = FSPRG_RECOMMENDED_SEEDLEN; + } + + p = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_P); + q = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_Q); + + if (msk) { + store_secpar(msk + 0, secpar); + mpi_export(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8, p); + mpi_export(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8, q); + } + + if (mpk) { + n = gcry_mpi_new(0); + gcry_mpi_mul(n, p, q); + assert(gcry_mpi_get_nbits(n) == secpar); + + store_secpar(mpk + 0, secpar); + mpi_export(mpk + 2, secpar / 8, n); + + gcry_mpi_release(n); + } + + gcry_mpi_release(p); + gcry_mpi_release(q); +} + +void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) { + gcry_mpi_t n, x; + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(mpk + 0); + n = mpi_import(mpk + 2, secpar / 8); + x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); + + memcpy(state, mpk, 2 + secpar / 8); + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); + memzero(state + 2 + 2 * secpar / 8, 8); + + gcry_mpi_release(n); + gcry_mpi_release(x); +} + +void FSPRG_Evolve(void *state) { + gcry_mpi_t n, x; + uint16_t secpar; + uint64_t epoch; + + initialize_libgcrypt(false); + + secpar = read_secpar(state + 0); + n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8); + x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8); + epoch = uint64_import(state + 2 + 2 * secpar / 8, 8); + + gcry_mpi_mulm(x, x, x, n); + epoch++; + + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x); + uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); + + gcry_mpi_release(n); + gcry_mpi_release(x); +} + +uint64_t FSPRG_GetEpoch(const void *state) { + uint16_t secpar; + secpar = read_secpar(state + 0); + return uint64_import(state + 2 + 2 * secpar / 8, 8); +} + +void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) { + gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm; + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(msk + 0); + p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8); + q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8); + + n = gcry_mpi_new(0); + gcry_mpi_mul(n, p, q); + + x = gensquare(n, seed, seedlen, RND_GEN_X, secpar); + CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */ + + kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */ + kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */ + + gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */ + gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */ + + CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */ + + store_secpar(state + 0, secpar); + mpi_export(state + 2 + 0 * secpar / 8, secpar / 8, n); + mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm); + uint64_export(state + 2 + 2 * secpar / 8, 8, epoch); + + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(n); + gcry_mpi_release(x); + gcry_mpi_release(xp); + gcry_mpi_release(xq); + gcry_mpi_release(kp); + gcry_mpi_release(kq); + gcry_mpi_release(xm); +} + +void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) { + uint16_t secpar; + + initialize_libgcrypt(false); + + secpar = read_secpar(state + 0); + det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx); +} diff --git a/src/libsystemd/src/sd-journal/fsprg.h b/src/libsystemd/src/sd-journal/fsprg.h new file mode 100644 index 0000000000..ef6d34fb4e --- /dev/null +++ b/src/libsystemd/src/sd-journal/fsprg.h @@ -0,0 +1,65 @@ +#ifndef __fsprgh__ +#define __fsprgh__ + +/* + * fsprg v0.1 - (seekable) forward-secure pseudorandom generator + * Copyright (C) 2012 B. Poettering + * Contact: fsprg@point-at-infinity.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "basic/macro.h" +#include "basic/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FSPRG_RECOMMENDED_SECPAR 1536 +#define FSPRG_RECOMMENDED_SEEDLEN (96/8) + +size_t FSPRG_mskinbytes(unsigned secpar) _const_; +size_t FSPRG_mpkinbytes(unsigned secpar) _const_; +size_t FSPRG_stateinbytes(unsigned secpar) _const_; + +/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */ +void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar); + +/* Initialize state deterministically in dependence on seed. */ +/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use + the same seed for both GenMK and GenState0. +*/ +void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen); + +void FSPRG_Evolve(void *state); + +uint64_t FSPRG_GetEpoch(const void *state) _pure_; + +/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */ +void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen); + +void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libsystemd/src/sd-journal/gcrypt-util.c b/src/libsystemd/src/sd-journal/gcrypt-util.c new file mode 120000 index 0000000000..56bb2382a6 --- /dev/null +++ b/src/libsystemd/src/sd-journal/gcrypt-util.c @@ -0,0 +1 @@ +../../../libshared/src/gcrypt-util.c \ No newline at end of file diff --git a/src/libsystemd/src/sd-journal/gcrypt-util.h b/src/libsystemd/src/sd-journal/gcrypt-util.h new file mode 120000 index 0000000000..c1168ad265 --- /dev/null +++ b/src/libsystemd/src/sd-journal/gcrypt-util.h @@ -0,0 +1 @@ +../../../libshared/include/shared/gcrypt-util.h \ No newline at end of file diff --git a/src/libsystemd/src/sd-journal/journal-authenticate.c b/src/libsystemd/src/sd-journal/journal-authenticate.c new file mode 100644 index 0000000000..5400d38ffd --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-authenticate.c @@ -0,0 +1,552 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fd-util.h" +#include "basic/hexdecoct.h" + +#include "fsprg.h" +#include "gcrypt-util.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" + +static uint64_t journal_file_tag_seqnum(JournalFile *f) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->n_tags) + 1; + f->header->n_tags = htole64(r); + + return r; +} + +int journal_file_append_tag(JournalFile *f) { + Object *o; + uint64_t p; + int r; + + assert(f); + + if (!f->seal) + return 0; + + if (!f->hmac_running) + return 0; + + assert(f->hmac); + + r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); + if (r < 0) + return r; + + o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); + o->tag.epoch = htole64(FSPRG_GetEpoch(f->fsprg_state)); + + log_debug("Writing tag %"PRIu64" for epoch %"PRIu64"", + le64toh(o->tag.seqnum), + FSPRG_GetEpoch(f->fsprg_state)); + + /* Add the tag object itself, so that we can protect its + * header. This will exclude the actual hash value in it */ + r = journal_file_hmac_put_object(f, OBJECT_TAG, o, p); + if (r < 0) + return r; + + /* Get the HMAC tag and store it in the object */ + memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH); + f->hmac_running = false; + + return 0; +} + +int journal_file_hmac_start(JournalFile *f) { + uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ + assert(f); + + if (!f->seal) + return 0; + + if (f->hmac_running) + return 0; + + /* Prepare HMAC for next cycle */ + gcry_md_reset(f->hmac); + FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0); + gcry_md_setkey(f->hmac, key, sizeof(key)); + + f->hmac_running = true; + + return 0; +} + +static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { + uint64_t t; + + assert(f); + assert(epoch); + assert(f->seal); + + if (f->fss_start_usec == 0 || + f->fss_interval_usec == 0) + return -EOPNOTSUPP; + + if (realtime < f->fss_start_usec) + return -ESTALE; + + t = realtime - f->fss_start_usec; + t = t / f->fss_interval_usec; + + *epoch = t; + return 0; +} + +static int journal_file_fsprg_need_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (epoch > goal) + return -ESTALE; + + return epoch != goal; +} + +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (epoch < goal) + log_debug("Evolving FSPRG key from epoch %"PRIu64" to %"PRIu64".", epoch, goal); + + for (;;) { + if (epoch > goal) + return -ESTALE; + if (epoch == goal) + return 0; + + FSPRG_Evolve(f->fsprg_state); + epoch = FSPRG_GetEpoch(f->fsprg_state); + } +} + +int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) { + void *msk; + uint64_t epoch; + + assert(f); + + if (!f->seal) + return 0; + + assert(f->fsprg_seed); + + if (f->fsprg_state) { + /* Cheaper... */ + + epoch = FSPRG_GetEpoch(f->fsprg_state); + if (goal == epoch) + return 0; + + if (goal == epoch+1) { + FSPRG_Evolve(f->fsprg_state); + return 0; + } + } else { + f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR); + f->fsprg_state = malloc(f->fsprg_state_size); + + if (!f->fsprg_state) + return -ENOMEM; + } + + log_debug("Seeking FSPRG key to %"PRIu64".", goal); + + msk = alloca(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR)); + FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR); + FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size); + return 0; +} + +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + if (realtime <= 0) + realtime = now(CLOCK_REALTIME); + + r = journal_file_fsprg_need_evolve(f, realtime); + if (r <= 0) + return 0; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + r = journal_file_fsprg_evolve(f, realtime); + if (r < 0) + return r; + + return 0; +} + +int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + if (!o) { + r = journal_file_move_to_object(f, type, p, &o); + if (r < 0) + return r; + } else { + if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + } + + gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); + + switch (o->object.type) { + + case OBJECT_DATA: + /* All but hash and payload are mutable */ + gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); + gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); + break; + + case OBJECT_FIELD: + /* Same here */ + gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash)); + gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload)); + break; + + case OBJECT_ENTRY: + /* All */ + gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); + break; + + case OBJECT_FIELD_HASH_TABLE: + case OBJECT_DATA_HASH_TABLE: + case OBJECT_ENTRY_ARRAY: + /* Nothing: everything is mutable */ + break; + + case OBJECT_TAG: + /* All but the tag itself */ + gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); + gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch)); + break; + default: + return -EINVAL; + } + + return 0; +} + +int journal_file_hmac_put_header(JournalFile *f) { + int r; + + assert(f); + + if (!f->seal) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + /* All but state+reserved, boot_id, arena_size, + * tail_object_offset, n_objects, n_entries, + * tail_entry_seqnum, head_entry_seqnum, entry_array_offset, + * head_entry_realtime, tail_entry_realtime, + * tail_entry_monotonic, n_data, n_fields, n_tags, + * n_entry_arrays. */ + + gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); + gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); + gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); + gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); + + return 0; +} + +int journal_file_fss_load(JournalFile *f) { + int r, fd = -1; + char *p = NULL; + struct stat st; + FSSHeader *m = NULL; + sd_id128_t machine; + + assert(f); + + if (!f->seal) + return 0; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", + SD_ID128_FORMAT_VAL(machine)) < 0) + return -ENOMEM; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + if (errno != ENOENT) + log_error_errno(errno, "Failed to open %s: %m", p); + + r = -errno; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if (st.st_size < (off_t) sizeof(FSSHeader)) { + r = -ENODATA; + goto finish; + } + + m = mmap(NULL, PAGE_ALIGN(sizeof(FSSHeader)), PROT_READ, MAP_SHARED, fd, 0); + if (m == MAP_FAILED) { + m = NULL; + r = -errno; + goto finish; + } + + if (memcmp(m->signature, FSS_HEADER_SIGNATURE, 8) != 0) { + r = -EBADMSG; + goto finish; + } + + if (m->incompatible_flags != 0) { + r = -EPROTONOSUPPORT; + goto finish; + } + + if (le64toh(m->header_size) < sizeof(FSSHeader)) { + r = -EBADMSG; + goto finish; + } + + if (le64toh(m->fsprg_state_size) != FSPRG_stateinbytes(le16toh(m->fsprg_secpar))) { + r = -EBADMSG; + goto finish; + } + + f->fss_file_size = le64toh(m->header_size) + le64toh(m->fsprg_state_size); + if ((uint64_t) st.st_size < f->fss_file_size) { + r = -ENODATA; + goto finish; + } + + if (!sd_id128_equal(machine, m->machine_id)) { + r = -EHOSTDOWN; + goto finish; + } + + if (le64toh(m->start_usec) <= 0 || + le64toh(m->interval_usec) <= 0) { + r = -EBADMSG; + goto finish; + } + + f->fss_file = mmap(NULL, PAGE_ALIGN(f->fss_file_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (f->fss_file == MAP_FAILED) { + f->fss_file = NULL; + r = -errno; + goto finish; + } + + f->fss_start_usec = le64toh(f->fss_file->start_usec); + f->fss_interval_usec = le64toh(f->fss_file->interval_usec); + + f->fsprg_state = (uint8_t*) f->fss_file + le64toh(f->fss_file->header_size); + f->fsprg_state_size = le64toh(f->fss_file->fsprg_state_size); + + r = 0; + +finish: + if (m) + munmap(m, PAGE_ALIGN(sizeof(FSSHeader))); + + safe_close(fd); + free(p); + + return r; +} + +int journal_file_hmac_setup(JournalFile *f) { + gcry_error_t e; + + if (!f->seal) + return 0; + + initialize_libgcrypt(true); + + e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + if (e != 0) + return -EOPNOTSUPP; + + return 0; +} + +int journal_file_append_first_tag(JournalFile *f) { + int r; + uint64_t p; + + if (!f->seal) + return 0; + + log_debug("Calculating first tag..."); + + r = journal_file_hmac_put_header(f); + if (r < 0) + return r; + + p = le64toh(f->header->field_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + p = le64toh(f->header->data_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, NULL, p); + if (r < 0) + return r; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + return 0; +} + +int journal_file_parse_verification_key(JournalFile *f, const char *key) { + uint8_t *seed; + size_t seed_size, c; + const char *k; + int r; + unsigned long long start, interval; + + seed_size = FSPRG_RECOMMENDED_SEEDLEN; + seed = malloc(seed_size); + if (!seed) + return -ENOMEM; + + k = key; + for (c = 0; c < seed_size; c++) { + int x, y; + + while (*k == '-') + k++; + + x = unhexchar(*k); + if (x < 0) { + free(seed); + return -EINVAL; + } + k++; + y = unhexchar(*k); + if (y < 0) { + free(seed); + return -EINVAL; + } + k++; + + seed[c] = (uint8_t) (x * 16 + y); + } + + if (*k != '/') { + free(seed); + return -EINVAL; + } + k++; + + r = sscanf(k, "%llx-%llx", &start, &interval); + if (r != 2) { + free(seed); + return -EINVAL; + } + + f->fsprg_seed = seed; + f->fsprg_seed_size = seed_size; + + f->fss_start_usec = start * interval; + f->fss_interval_usec = interval; + + return 0; +} + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u) { + uint64_t epoch; + + assert(f); + assert(u); + + if (!f->seal) + return false; + + epoch = FSPRG_GetEpoch(f->fsprg_state); + + *u = (usec_t) (f->fss_start_usec + f->fss_interval_usec * epoch + f->fss_interval_usec); + + return true; +} diff --git a/src/libsystemd/src/sd-journal/journal-authenticate.h b/src/libsystemd/src/sd-journal/journal-authenticate.h new file mode 100644 index 0000000000..6c87319ede --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-authenticate.h @@ -0,0 +1,41 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "journal-file.h" + +int journal_file_append_tag(JournalFile *f); +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); +int journal_file_append_first_tag(JournalFile *f); + +int journal_file_hmac_setup(JournalFile *f); +int journal_file_hmac_start(JournalFile *f); +int journal_file_hmac_put_header(JournalFile *f); +int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p); + +int journal_file_fss_load(JournalFile *f); +int journal_file_parse_verification_key(JournalFile *f, const char *key); + +int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime); +int journal_file_fsprg_seek(JournalFile *f, uint64_t epoch); + +bool journal_file_next_evolve_usec(JournalFile *f, usec_t *u); diff --git a/src/libsystemd/src/sd-journal/journal-def.h b/src/libsystemd/src/sd-journal/journal-def.h new file mode 100644 index 0000000000..7e456fcc66 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-def.h @@ -0,0 +1,233 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +/* + * If you change this file you probably should also change its documentation: + * + * http://www.freedesktop.org/wiki/Software/systemd/journal-files + * + */ + +typedef struct Header Header; +typedef struct ObjectHeader ObjectHeader; +typedef union Object Object; +typedef struct DataObject DataObject; +typedef struct FieldObject FieldObject; +typedef struct EntryObject EntryObject; +typedef struct HashTableObject HashTableObject; +typedef struct EntryArrayObject EntryArrayObject; +typedef struct TagObject TagObject; +typedef struct EntryItem EntryItem; +typedef struct HashItem HashItem; +typedef struct FSSHeader FSSHeader; + +/* Object types */ +typedef enum ObjectType { + OBJECT_UNUSED, /* also serves as "any type" or "additional context" */ + OBJECT_DATA, + OBJECT_FIELD, + OBJECT_ENTRY, + OBJECT_DATA_HASH_TABLE, + OBJECT_FIELD_HASH_TABLE, + OBJECT_ENTRY_ARRAY, + OBJECT_TAG, + _OBJECT_TYPE_MAX +} ObjectType; + +/* Object flags */ +enum { + OBJECT_COMPRESSED_XZ = 1 << 0, + OBJECT_COMPRESSED_LZ4 = 1 << 1, + _OBJECT_COMPRESSED_MAX +}; + +#define OBJECT_COMPRESSION_MASK (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4) + +struct ObjectHeader { + uint8_t type; + uint8_t flags; + uint8_t reserved[6]; + le64_t size; + uint8_t payload[]; +} _packed_; + +struct DataObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t next_field_offset; + le64_t entry_offset; /* the first array entry we store inline */ + le64_t entry_array_offset; + le64_t n_entries; + uint8_t payload[]; +} _packed_; + +struct FieldObject { + ObjectHeader object; + le64_t hash; + le64_t next_hash_offset; + le64_t head_data_offset; + uint8_t payload[]; +} _packed_; + +struct EntryItem { + le64_t object_offset; + le64_t hash; +} _packed_; + +struct EntryObject { + ObjectHeader object; + le64_t seqnum; + le64_t realtime; + le64_t monotonic; + sd_id128_t boot_id; + le64_t xor_hash; + EntryItem items[]; +} _packed_; + +struct HashItem { + le64_t head_hash_offset; + le64_t tail_hash_offset; +} _packed_; + +struct HashTableObject { + ObjectHeader object; + HashItem items[]; +} _packed_; + +struct EntryArrayObject { + ObjectHeader object; + le64_t next_entry_array_offset; + le64_t items[]; +} _packed_; + +#define TAG_LENGTH (256/8) + +struct TagObject { + ObjectHeader object; + le64_t seqnum; + le64_t epoch; + uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */ +} _packed_; + +union Object { + ObjectHeader object; + DataObject data; + FieldObject field; + EntryObject entry; + HashTableObject hash_table; + EntryArrayObject entry_array; + TagObject tag; +}; + +enum { + STATE_OFFLINE = 0, + STATE_ONLINE = 1, + STATE_ARCHIVED = 2, + _STATE_MAX +}; + +/* Header flags */ +enum { + HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0, + HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1, +}; + +#define HEADER_INCOMPATIBLE_ANY (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4) + +#if defined(HAVE_XZ) && defined(HAVE_LZ4) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_ANY +#elif defined(HAVE_XZ) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_XZ +#elif defined(HAVE_LZ4) +# define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_LZ4 +#else +# define HEADER_INCOMPATIBLE_SUPPORTED 0 +#endif + +enum { + HEADER_COMPATIBLE_SEALED = 1 +}; + +#define HEADER_COMPATIBLE_ANY HEADER_COMPATIBLE_SEALED +#ifdef HAVE_GCRYPT +# define HEADER_COMPATIBLE_SUPPORTED HEADER_COMPATIBLE_SEALED +#else +# define HEADER_COMPATIBLE_SUPPORTED 0 +#endif + +#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }) + +struct Header { + uint8_t signature[8]; /* "LPKSHHRH" */ + le32_t compatible_flags; + le32_t incompatible_flags; + uint8_t state; + uint8_t reserved[7]; + sd_id128_t file_id; + sd_id128_t machine_id; + sd_id128_t boot_id; /* last writer */ + sd_id128_t seqnum_id; + le64_t header_size; + le64_t arena_size; + le64_t data_hash_table_offset; + le64_t data_hash_table_size; + le64_t field_hash_table_offset; + le64_t field_hash_table_size; + le64_t tail_object_offset; + le64_t n_objects; + le64_t n_entries; + le64_t tail_entry_seqnum; + le64_t head_entry_seqnum; + le64_t entry_array_offset; + le64_t head_entry_realtime; + le64_t tail_entry_realtime; + le64_t tail_entry_monotonic; + /* Added in 187 */ + le64_t n_data; + le64_t n_fields; + /* Added in 189 */ + le64_t n_tags; + le64_t n_entry_arrays; + + /* Size: 240 */ +} _packed_; + +#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' }) + +struct FSSHeader { + uint8_t signature[8]; /* "KSHHRHLP" */ + le32_t compatible_flags; + le32_t incompatible_flags; + sd_id128_t machine_id; + sd_id128_t boot_id; /* last writer */ + le64_t header_size; + le64_t start_usec; + le64_t interval_usec; + le16_t fsprg_secpar; + le16_t reserved[3]; + le64_t fsprg_state_size; +} _packed_; diff --git a/src/libsystemd/src/sd-journal/journal-file.c b/src/libsystemd/src/sd-journal/journal-file.c new file mode 100644 index 0000000000..d8f4671617 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-file.c @@ -0,0 +1,3619 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/btrfs-util.h" +#include "basic/chattr-util.h" +#include "basic/fd-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/random-util.h" +#include "basic/set.h" +#include "basic/string-util.h" +#include "basic/xattr-util.h" + +#include "compress.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" +#include "lookup3.h" + +#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem)) +#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem)) + +#define COMPRESSION_SIZE_THRESHOLD (512ULL) + +/* This is the minimum journal file size */ +#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 */ + +/* This is the upper bound if we deduce the keep_free value from the + * file system size */ +#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ + +/* This is the keep_free value when we can't determine the system + * size */ +#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */ + +/* 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)) + +/* How many entries to keep in the entry array chain cache at max */ +#define CHAIN_CACHE_MAX 20 + +/* How much to increase the journal file size at once each time we allocate something new. */ +#define FILE_SIZE_INCREASE (8ULL*1024ULL*1024ULL) /* 8MB */ + +/* Reread fstat() of the file for detecting deletions at least this often */ +#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC) + +/* The mmap context to use for the header we pick as one above the last defined typed */ +#define CONTEXT_HEADER _OBJECT_TYPE_MAX + +/* This may be called from a separate thread to prevent blocking the caller for the duration of fsync(). + * As a result we use atomic operations on f->offline_state for inter-thread communications with + * journal_file_set_offline() and journal_file_set_online(). */ +static void journal_file_set_offline_internal(JournalFile *f) { + assert(f); + assert(f->fd >= 0); + assert(f->header); + + for (;;) { + switch (f->offline_state) { + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_DONE)) + continue; + return; + + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_SYNCING: + (void) fsync(f->fd); + + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING)) + continue; + + f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE; + (void) fsync(f->fd); + break; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE)) + continue; + /* fall through */ + + case OFFLINE_DONE: + return; + + case OFFLINE_JOINED: + log_debug("OFFLINE_JOINED unexpected offline state for journal_file_set_offline_internal()"); + return; + } + } +} + +static void * journal_file_set_offline_thread(void *arg) { + JournalFile *f = arg; + + journal_file_set_offline_internal(f); + + return NULL; +} + +static int journal_file_set_offline_thread_join(JournalFile *f) { + int r; + + assert(f); + + if (f->offline_state == OFFLINE_JOINED) + return 0; + + r = pthread_join(f->offline_thread, NULL); + if (r) + return -r; + + f->offline_state = OFFLINE_JOINED; + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + return 0; +} + +/* Trigger a restart if the offline thread is mid-flight in a restartable state. */ +static bool journal_file_set_offline_try_restart(JournalFile *f) { + for (;;) { + switch (f->offline_state) { + case OFFLINE_AGAIN_FROM_SYNCING: + case OFFLINE_AGAIN_FROM_OFFLINING: + return true; + + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING)) + continue; + return true; + + default: + return false; + } + } +} + +/* Sets a journal offline. + * + * If wait is false then an offline is dispatched in a separate thread for a + * subsequent journal_file_set_offline() or journal_file_set_online() of the + * same journal to synchronize with. + * + * If wait is true, then either an existing offline thread will be restarted + * and joined, or if none exists the offline is simply performed in this + * context without involving another thread. + */ +int journal_file_set_offline(JournalFile *f, bool wait) { + bool restarted; + int r; + + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + /* An offlining journal is implicitly online and may modify f->header->state, + * we must also join any potentially lingering offline thread when not online. */ + if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) + return journal_file_set_offline_thread_join(f); + + /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ + restarted = journal_file_set_offline_try_restart(f); + if ((restarted && wait) || !restarted) { + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + } + + if (restarted) + return 0; + + /* Initiate a new offline. */ + f->offline_state = OFFLINE_SYNCING; + + if (wait) /* Without using a thread if waiting. */ + journal_file_set_offline_internal(f); + else { + r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); + if (r > 0) { + f->offline_state = OFFLINE_JOINED; + return -r; + } + } + + return 0; +} + +static int journal_file_set_online(JournalFile *f) { + bool joined = false; + + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + while (!joined) { + switch (f->offline_state) { + case OFFLINE_JOINED: + /* No offline thread, no need to wait. */ + joined = true; + break; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled syncing prior to offlining, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from syncing, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from offlining, must wait for offlining to complete however. */ + + /* fall through to wait */ + default: { + int r; + + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + + joined = true; + break; + } + } + } + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + switch (f->header->state) { + case STATE_ONLINE: + return 0; + + case STATE_OFFLINE: + f->header->state = STATE_ONLINE; + (void) fsync(f->fd); + return 0; + + default: + return -EINVAL; + } +} + +bool journal_file_is_offlining(JournalFile *f) { + assert(f); + + __sync_synchronize(); + + if (f->offline_state == OFFLINE_DONE || + f->offline_state == OFFLINE_JOINED) + return false; + + return true; +} + +JournalFile* journal_file_close(JournalFile *f) { + assert(f); + +#ifdef HAVE_GCRYPT + /* Write the final tag */ + if (f->seal && f->writable) + journal_file_append_tag(f); +#endif + + if (f->post_change_timer) { + int enabled; + + if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0) + if (enabled == SD_EVENT_ONESHOT) + journal_file_post_change(f); + + (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF); + sd_event_source_unref(f->post_change_timer); + } + + journal_file_set_offline(f, true); + + if (f->mmap && f->fd >= 0) + mmap_cache_close_fd(f->mmap, f->fd); + + if (f->fd >= 0 && f->defrag_on_close) { + + /* Be friendly to btrfs: turn COW back on again now, + * and defragment the file. We won't write to the file + * ever again, hence remove all fragmentation, and + * reenable all the good bits COW usually provides + * (such as data checksumming). */ + + (void) chattr_fd(f->fd, 0, FS_NOCOW_FL); + (void) btrfs_defrag_fd(f->fd); + } + + if (f->close_fd) + safe_close(f->fd); + free(f->path); + + mmap_cache_unref(f->mmap); + + ordered_hashmap_free_free(f->chain_cache); + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + free(f->compress_buffer); +#endif + +#ifdef HAVE_GCRYPT + if (f->fss_file) + munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size)); + else + free(f->fsprg_state); + + free(f->fsprg_seed); + + if (f->hmac) + gcry_md_close(f->hmac); +#endif + + free(f); + return NULL; +} + +void journal_file_close_set(Set *s) { + JournalFile *f; + + assert(s); + + while ((f = set_steal_first(s))) + (void) journal_file_close(f); +} + +static int journal_file_init_header(JournalFile *f, JournalFile *template) { + Header h = {}; + ssize_t k; + int r; + + assert(f); + + memcpy(h.signature, HEADER_SIGNATURE, 8); + h.header_size = htole64(ALIGN64(sizeof(h))); + + h.incompatible_flags |= htole32( + f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ | + f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4); + + h.compatible_flags = htole32( + f->seal * HEADER_COMPATIBLE_SEALED); + + r = sd_id128_randomize(&h.file_id); + if (r < 0) + return r; + + if (template) { + h.seqnum_id = template->header->seqnum_id; + h.tail_entry_seqnum = template->header->tail_entry_seqnum; + } else + h.seqnum_id = h.file_id; + + k = pwrite(f->fd, &h, sizeof(h), 0); + if (k < 0) + return -errno; + + if (k != sizeof(h)) + return -EIO; + + return 0; +} + +static int fsync_directory_of_file(int fd) { + _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_close_ int dfd = -1; + struct stat st; + int r; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADFD; + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return -EINVAL; + + dn = dirname_malloc(path); + if (!dn) + return -ENOMEM; + + dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + return -errno; + + if (fsync(dfd) < 0) + return -errno; + + return 0; +} + +static int journal_file_refresh_header(JournalFile *f) { + sd_id128_t boot_id; + int r; + + assert(f); + assert(f->header); + + r = sd_id128_get_machine(&f->header->machine_id); + if (r < 0) + return r; + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + + if (sd_id128_equal(boot_id, f->header->boot_id)) + f->tail_entry_monotonic_valid = true; + + f->header->boot_id = boot_id; + + r = journal_file_set_online(f); + + /* Sync the online state to disk */ + (void) fsync(f->fd); + + /* We likely just created a new file, also sync the directory this file is located in. */ + (void) fsync_directory_of_file(f->fd); + + return r; +} + +static int journal_file_verify_header(JournalFile *f) { + uint32_t flags; + + assert(f); + assert(f->header); + + if (memcmp(f->header->signature, HEADER_SIGNATURE, 8)) + return -EBADMSG; + + /* In both read and write mode we refuse to open files with + * incompatible flags we don't know */ + flags = le32toh(f->header->incompatible_flags); + if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) { + if (flags & ~HEADER_INCOMPATIBLE_ANY) + log_debug("Journal file %s has unknown incompatible flags %"PRIx32, + f->path, flags & ~HEADER_INCOMPATIBLE_ANY); + flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED; + if (flags) + log_debug("Journal file %s uses incompatible flags %"PRIx32 + " disabled at compilation time.", f->path, flags); + return -EPROTONOSUPPORT; + } + + /* When open for writing we refuse to open files with + * compatible flags, too */ + flags = le32toh(f->header->compatible_flags); + if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) { + if (flags & ~HEADER_COMPATIBLE_ANY) + log_debug("Journal file %s has unknown compatible flags %"PRIx32, + f->path, flags & ~HEADER_COMPATIBLE_ANY); + flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED; + if (flags) + log_debug("Journal file %s uses compatible flags %"PRIx32 + " disabled at compilation time.", f->path, flags); + return -EPROTONOSUPPORT; + } + + if (f->header->state >= _STATE_MAX) + return -EBADMSG; + + /* The first addition was n_data, so check that we are at least this large */ + if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) + return -EBADMSG; + + if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + return -EBADMSG; + + if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) + return -ENODATA; + + if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (!VALID64(le64toh(f->header->data_hash_table_offset)) || + !VALID64(le64toh(f->header->field_hash_table_offset)) || + !VALID64(le64toh(f->header->tail_object_offset)) || + !VALID64(le64toh(f->header->entry_array_offset))) + return -ENODATA; + + if (f->writable) { + uint8_t state; + sd_id128_t machine_id; + int r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!sd_id128_equal(machine_id, f->header->machine_id)) + return -EHOSTDOWN; + + state = f->header->state; + + if (state == STATE_ONLINE) { + log_debug("Journal file %s is already online. Assuming unclean closing.", f->path); + return -EBUSY; + } else if (state == STATE_ARCHIVED) + return -ESHUTDOWN; + else if (state != STATE_OFFLINE) { + log_debug("Journal file %s has unknown state %i.", f->path, state); + return -EBUSY; + } + } + + f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header); + f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header); + + f->seal = JOURNAL_HEADER_SEALED(f->header); + + return 0; +} + +static int journal_file_fstat(JournalFile *f) { + assert(f); + assert(f->fd >= 0); + + if (fstat(f->fd, &f->last_stat) < 0) + return -errno; + + f->last_stat_usec = now(CLOCK_MONOTONIC); + + /* Refuse appending to files that are already deleted */ + if (f->last_stat.st_nlink <= 0) + return -EIDRM; + + return 0; +} + +static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { + uint64_t old_size, new_size; + int r; + + assert(f); + assert(f->header); + + /* We assume that this file is not sparse, and we know that + * for sure, since we always call posix_fallocate() + * ourselves */ + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + return -EIO; + + old_size = + le64toh(f->header->header_size) + + le64toh(f->header->arena_size); + + new_size = PAGE_ALIGN(offset + size); + if (new_size < le64toh(f->header->header_size)) + new_size = le64toh(f->header->header_size); + + if (new_size <= old_size) { + + /* We already pre-allocated enough space, but before + * we write to it, let's check with fstat() if the + * file got deleted, in order make sure we don't throw + * away the data immediately. Don't check fstat() for + * all writes though, but only once ever 10s. */ + + if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC)) + return 0; + + return journal_file_fstat(f); + } + + /* Allocate more space. */ + + if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) + return -E2BIG; + + if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) { + struct statvfs svfs; + + if (fstatvfs(f->fd, &svfs) >= 0) { + uint64_t available; + + available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free); + + if (new_size - old_size > available) + return -E2BIG; + } + } + + /* Increase by larger blocks at once */ + new_size = ((new_size+FILE_SIZE_INCREASE-1) / FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE; + if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) + new_size = f->metrics.max_size; + + /* Note that the glibc fallocate() fallback is very + inefficient, hence we try to minimize the allocation area + as we can. */ + r = posix_fallocate(f->fd, old_size, new_size - old_size); + if (r != 0) + return -r; + + f->header->arena_size = htole64(new_size - le64toh(f->header->header_size)); + + return journal_file_fstat(f); +} + +static unsigned type_to_context(ObjectType type) { + /* One context for each type, plus one catch-all for the rest */ + assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS); + assert_cc(CONTEXT_HEADER < MMAP_CACHE_MAX_CONTEXTS); + return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0; +} + +static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) { + int r; + + assert(f); + assert(ret); + + if (size <= 0) + return -EINVAL; + + /* Avoid SIGBUS on invalid accesses */ + if (offset + size > (uint64_t) f->last_stat.st_size) { + /* Hmm, out of range? Let's refresh the fstat() data + * first, before we trust that check. */ + + r = journal_file_fstat(f); + if (r < 0) + return r; + + if (offset + size > (uint64_t) f->last_stat.st_size) + return -EADDRNOTAVAIL; + } + + return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); +} + +static uint64_t minimum_header_size(Object *o) { + + static const uint64_t table[] = { + [OBJECT_DATA] = sizeof(DataObject), + [OBJECT_FIELD] = sizeof(FieldObject), + [OBJECT_ENTRY] = sizeof(EntryObject), + [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject), + [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject), + [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject), + [OBJECT_TAG] = sizeof(TagObject), + }; + + if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0) + return sizeof(ObjectHeader); + + return table[o->object.type]; +} + +int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) { + int r; + void *t; + Object *o; + uint64_t s; + + assert(f); + assert(ret); + + /* Objects may only be located at multiple of 64 bit */ + if (!VALID64(offset)) + return -EBADMSG; + + /* Object may not be located in the file header */ + if (offset < le64toh(f->header->header_size)) + return -EBADMSG; + + r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + + o = (Object*) t; + s = le64toh(o->object.size); + + if (s < sizeof(ObjectHeader)) + return -EBADMSG; + + if (o->object.type <= OBJECT_UNUSED) + return -EBADMSG; + + if (s < minimum_header_size(o)) + return -EBADMSG; + + if (type > OBJECT_UNUSED && o->object.type != type) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { + r = journal_file_move_to(f, type, false, offset, s, &t); + if (r < 0) + return r; + + o = (Object*) t; + } + + *ret = o; + return 0; +} + +static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { + uint64_t r; + + assert(f); + assert(f->header); + + r = le64toh(f->header->tail_entry_seqnum) + 1; + + if (seqnum) { + /* If an external seqnum counter was passed, we update + * both the local and the external one, and set it to + * the maximum of both */ + + if (*seqnum + 1 > r) + r = *seqnum + 1; + + *seqnum = r; + } + + f->header->tail_entry_seqnum = htole64(r); + + if (f->header->head_entry_seqnum == 0) + f->header->head_entry_seqnum = htole64(r); + + return r; +} + +int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); + assert(f->header); + assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); + + r = journal_file_set_online(f); + if (r < 0) + return r; + + p = le64toh(f->header->tail_object_offset); + if (p == 0) + p = le64toh(f->header->header_size); + else { + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); + if (r < 0) + return r; + + p += ALIGN64(le64toh(tail->object.size)); + } + + r = journal_file_allocate(f, p, size); + if (r < 0) + return r; + + r = journal_file_move_to(f, type, false, p, size, &t); + if (r < 0) + return r; + + o = (Object*) t; + + zero(o->object); + o->object.type = type; + o->object.size = htole64(size); + + f->header->tail_object_offset = htole64(p); + f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); + + *ret = o; + *offset = p; + + return 0; +} + +static int journal_file_setup_data_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + assert(f->header); + + /* 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) + s = DEFAULT_DATA_HASH_TABLE_SIZE; + + log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem)); + + r = journal_file_append_object(f, + OBJECT_DATA_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memzero(o->hash_table.items, s); + + f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->data_hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_setup_field_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + assert(f->header); + + /* We use a fixed size hash table for the fields as this + * number should grow very slowly only */ + + s = DEFAULT_FIELD_HASH_TABLE_SIZE; + r = journal_file_append_object(f, + OBJECT_FIELD_HASH_TABLE, + offsetof(Object, hash_table.items) + s, + &o, &p); + if (r < 0) + return r; + + memzero(o->hash_table.items, s); + + f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items)); + f->header->field_hash_table_size = htole64(s); + + return 0; +} + +int journal_file_map_data_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + assert(f->header); + + if (f->data_hash_table) + return 0; + + p = le64toh(f->header->data_hash_table_offset); + s = le64toh(f->header->data_hash_table_size); + + r = journal_file_move_to(f, + OBJECT_DATA_HASH_TABLE, + true, + p, s, + &t); + if (r < 0) + return r; + + f->data_hash_table = t; + return 0; +} + +int journal_file_map_field_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + assert(f->header); + + if (f->field_hash_table) + return 0; + + p = le64toh(f->header->field_hash_table_offset); + s = le64toh(f->header->field_hash_table_size); + + r = journal_file_move_to(f, + OBJECT_FIELD_HASH_TABLE, + true, + p, s, + &t); + if (r < 0) + return r; + + f->field_hash_table = t; + return 0; +} + +static int journal_file_link_field( + JournalFile *f, + Object *o, + uint64_t offset, + uint64_t hash) { + + uint64_t p, h, m; + int r; + + assert(f); + assert(f->header); + assert(f->field_hash_table); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_FIELD) + return -EINVAL; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + /* This might alter the window we are looking at */ + o->field.next_hash_offset = o->field.head_data_offset = 0; + + h = hash % m; + p = le64toh(f->field_hash_table[h].tail_hash_offset); + if (p == 0) + f->field_hash_table[h].head_hash_offset = htole64(offset); + else { + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + + o->field.next_hash_offset = htole64(offset); + } + + f->field_hash_table[h].tail_hash_offset = htole64(offset); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + f->header->n_fields = htole64(le64toh(f->header->n_fields) + 1); + + return 0; +} + +static int journal_file_link_data( + JournalFile *f, + Object *o, + uint64_t offset, + uint64_t hash) { + + uint64_t p, h, m; + int r; + + assert(f); + assert(f->header); + assert(f->data_hash_table); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_DATA) + return -EINVAL; + + m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + /* This might alter the window we are looking at */ + o->data.next_hash_offset = o->data.next_field_offset = 0; + o->data.entry_offset = o->data.entry_array_offset = 0; + o->data.n_entries = 0; + + h = hash % m; + p = le64toh(f->data_hash_table[h].tail_hash_offset); + if (p == 0) + /* Only entry in the hash table is easy */ + f->data_hash_table[h].head_hash_offset = htole64(offset); + else { + /* Move back to the previous data object, to patch in + * pointer */ + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + o->data.next_hash_offset = htole64(offset); + } + + f->data_hash_table[h].tail_hash_offset = htole64(offset); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + f->header->n_data = htole64(le64toh(f->header->n_data) + 1); + + return 0; +} + +int journal_file_find_field_object_with_hash( + JournalFile *f, + const void *field, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + + uint64_t p, osize, h, m; + int r; + + assert(f); + assert(f->header); + assert(field && size > 0); + + /* If the field hash table is empty, we can't find anything */ + if (le64toh(f->header->field_hash_table_size) <= 0) + return 0; + + /* Map the field hash table, if it isn't mapped yet. */ + r = journal_file_map_field_hash_table(f); + if (r < 0) + return r; + + osize = offsetof(Object, field.payload) + size; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + h = hash % m; + p = le64toh(f->field_hash_table[h].head_hash_offset); + + while (p > 0) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + + if (le64toh(o->field.hash) == hash && + le64toh(o->object.size) == osize && + memcmp(o->field.payload, field, size) == 0) { + + if (ret) + *ret = o; + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->field.next_hash_offset); + } + + return 0; +} + +int journal_file_find_field_object( + JournalFile *f, + const void *field, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(field && size > 0); + + hash = hash64(field, size); + + return journal_file_find_field_object_with_hash(f, + field, size, hash, + ret, offset); +} + +int journal_file_find_data_object_with_hash( + JournalFile *f, + const void *data, uint64_t size, uint64_t hash, + Object **ret, uint64_t *offset) { + + uint64_t p, osize, h, m; + int r; + + assert(f); + assert(f->header); + assert(data || size == 0); + + /* If there's no data hash table, then there's no entry. */ + if (le64toh(f->header->data_hash_table_size) <= 0) + return 0; + + /* Map the data hash table, if it isn't mapped yet. */ + r = journal_file_map_data_hash_table(f); + if (r < 0) + return r; + + osize = offsetof(Object, data.payload) + size; + + m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (m <= 0) + return -EBADMSG; + + h = hash % m; + p = le64toh(f->data_hash_table[h].head_hash_offset); + + while (p > 0) { + Object *o; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le64toh(o->data.hash) != hash) + goto next; + + if (o->object.flags & OBJECT_COMPRESSION_MASK) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + uint64_t l; + size_t rsize = 0; + + l = le64toh(o->object.size); + if (l <= offsetof(Object, data.payload)) + return -EBADMSG; + + l -= offsetof(Object, data.payload); + + r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, + o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0); + if (r < 0) + return r; + + if (rsize == size && + memcmp(f->compress_buffer, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } +#else + return -EPROTONOSUPPORT; +#endif + } else if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + next: + p = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +int journal_file_find_data_object( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + return journal_file_find_data_object_with_hash(f, + data, size, hash, + ret, offset); +} + +static int journal_file_append_field( + JournalFile *f, + const void *field, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash, p; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(field && size > 0); + + hash = hash64(field, size); + + r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p); + if (r < 0) + return r; + else if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, field.payload) + size; + r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p); + if (r < 0) + return r; + + o->field.hash = htole64(hash); + memcpy(o->field.payload, field, size); + + r = journal_file_link_field(f, o, p, hash); + if (r < 0) + return r; + + /* The linking might have altered the window, so let's + * refresh our pointer */ + r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); + if (r < 0) + return r; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p); + if (r < 0) + return r; +#endif + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; +} + +static int journal_file_append_data( + JournalFile *f, + const void *data, uint64_t size, + Object **ret, uint64_t *offset) { + + uint64_t hash, p; + uint64_t osize; + Object *o; + int r, compression = 0; + const void *eq; + + assert(f); + assert(data || size == 0); + + hash = hash64(data, size); + + r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); + if (r < 0) + return r; + if (r > 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + osize = offsetof(Object, data.payload) + size; + r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p); + if (r < 0) + return r; + + o->data.hash = htole64(hash); + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) { + size_t rsize = 0; + + compression = compress_blob(data, size, o->data.payload, size - 1, &rsize); + + 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 == 0) + memcpy_safe(o->data.payload, data, size); + + r = journal_file_link_data(f, o, p, hash); + if (r < 0) + return r; + + /* The linking might have altered the window, so let's + * refresh our pointer */ + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (!data) + eq = NULL; + else + eq = memchr(data, '=', size); + if (eq && eq > data) { + Object *fo = NULL; + uint64_t fp; + + /* Create field object ... */ + r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp); + if (r < 0) + return r; + + /* ... and link it in. */ + o->data.next_field_offset = fo->field.head_data_offset; + fo->field.head_data_offset = le64toh(p); + } + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); + if (r < 0) + return r; +#endif + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; +} + +uint64_t journal_file_entry_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_ENTRY) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); +} + +uint64_t journal_file_entry_array_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_ENTRY_ARRAY) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t); +} + +uint64_t journal_file_hash_table_n_items(Object *o) { + assert(o); + + if (o->object.type != OBJECT_DATA_HASH_TABLE && + o->object.type != OBJECT_FIELD_HASH_TABLE) + return 0; + + return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem); +} + +static int link_entry_into_array(JournalFile *f, + le64_t *first, + le64_t *idx, + uint64_t p) { + int r; + uint64_t n = 0, ap = 0, q, i, a, hidx; + Object *o; + + assert(f); + assert(f->header); + assert(first); + assert(idx); + assert(p > 0); + + a = le64toh(*first); + i = hidx = le64toh(*idx); + while (a > 0) { + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + n = journal_file_entry_array_n_items(o); + if (i < n) { + o->entry_array.items[i] = htole64(p); + *idx = htole64(hidx + 1); + return 0; + } + + i -= n; + ap = a; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + if (hidx > n) + n = (hidx+1) * 2; + else + n = n * 2; + + if (n < 4) + n = 4; + + r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY, + offsetof(Object, entry_array.items) + n * sizeof(uint64_t), + &o, &q); + if (r < 0) + return r; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q); + if (r < 0) + return r; +#endif + + o->entry_array.items[i] = htole64(p); + + if (ap == 0) + *first = htole64(q); + else { + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o); + if (r < 0) + return r; + + o->entry_array.next_entry_array_offset = htole64(q); + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1); + + *idx = htole64(hidx + 1); + + return 0; +} + +static int link_entry_into_array_plus_one(JournalFile *f, + le64_t *extra, + le64_t *first, + le64_t *idx, + uint64_t p) { + + int r; + + assert(f); + assert(extra); + assert(first); + assert(idx); + assert(p > 0); + + if (*idx == 0) + *extra = htole64(p); + else { + le64_t i; + + i = htole64(le64toh(*idx) - 1); + r = link_entry_into_array(f, first, &i, p); + if (r < 0) + return r; + } + + *idx = htole64(le64toh(*idx) + 1); + return 0; +} + +static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { + uint64_t p; + int r; + assert(f); + assert(o); + assert(offset > 0); + + p = le64toh(o->entry.items[i].object_offset); + if (p == 0) + return -EINVAL; + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + return link_entry_into_array_plus_one(f, + &o->data.entry_offset, + &o->data.entry_array_offset, + &o->data.n_entries, + offset); +} + +static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { + uint64_t n, i; + int r; + + assert(f); + assert(f->header); + assert(o); + assert(offset > 0); + + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + __sync_synchronize(); + + /* Link up the entry itself */ + r = link_entry_into_array(f, + &f->header->entry_array_offset, + &f->header->n_entries, + offset); + if (r < 0) + return r; + + /* log_debug("=> %s seqnr=%"PRIu64" n_entries=%"PRIu64, f->path, o->entry.seqnum, f->header->n_entries); */ + + if (f->header->head_entry_realtime == 0) + f->header->head_entry_realtime = o->entry.realtime; + + f->header->tail_entry_realtime = o->entry.realtime; + f->header->tail_entry_monotonic = o->entry.monotonic; + + f->tail_entry_monotonic_valid = true; + + /* Link up the items */ + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + r = journal_file_link_entry_item(f, o, offset, i); + if (r < 0) + return r; + } + + return 0; +} + +static int journal_file_append_entry_internal( + JournalFile *f, + const dual_timestamp *ts, + uint64_t xor_hash, + const EntryItem items[], unsigned n_items, + uint64_t *seqnum, + Object **ret, uint64_t *offset) { + uint64_t np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(f->header); + assert(items || n_items == 0); + assert(ts); + + osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); + + r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np); + if (r < 0) + return r; + + o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum)); + memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem)); + o->entry.realtime = htole64(ts->realtime); + o->entry.monotonic = htole64(ts->monotonic); + o->entry.xor_hash = htole64(xor_hash); + o->entry.boot_id = f->header->boot_id; + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); + if (r < 0) + return r; +#endif + + r = journal_file_link_entry(f, o, np); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +void journal_file_post_change(JournalFile *f) { + assert(f); + + /* inotify() does not receive IN_MODIFY events from file + * accesses done via mmap(). After each access we hence + * trigger IN_MODIFY by truncating the journal file to its + * current size which triggers IN_MODIFY. */ + + __sync_synchronize(); + + if (ftruncate(f->fd, f->last_stat.st_size) < 0) + log_debug_errno(errno, "Failed to truncate file to its own size: %m"); +} + +static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) { + assert(userdata); + + journal_file_post_change(userdata); + + return 1; +} + +static void schedule_post_change(JournalFile *f) { + sd_event_source *timer; + int enabled, r; + uint64_t now; + + assert(f); + assert(f->post_change_timer); + + timer = f->post_change_timer; + + r = sd_event_source_get_enabled(timer, &enabled); + if (r < 0) { + log_debug_errno(r, "Failed to get ftruncate timer state: %m"); + goto fail; + } + + if (enabled == SD_EVENT_ONESHOT) + return; + + r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now); + if (r < 0) { + log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m"); + goto fail; + } + + r = sd_event_source_set_time(timer, now+f->post_change_timer_period); + if (r < 0) { + log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m"); + goto fail; + } + + r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT); + if (r < 0) { + log_debug_errno(r, "Failed to enable scheduled ftruncate: %m"); + goto fail; + } + + return; + +fail: + /* On failure, let's simply post the change immediately. */ + journal_file_post_change(f); +} + +/* Enable coalesced change posting in a timer on the provided sd_event instance */ +int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) { + _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; + int r; + + assert(f); + assert_return(!f->post_change_timer, -EINVAL); + assert(e); + assert(t); + + r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(timer, SD_EVENT_OFF); + if (r < 0) + return r; + + f->post_change_timer = timer; + timer = NULL; + f->post_change_timer_period = t; + + return r; +} + +static int entry_item_cmp(const void *_a, const void *_b) { + const EntryItem *a = _a, *b = _b; + + if (le64toh(a->object_offset) < le64toh(b->object_offset)) + return -1; + if (le64toh(a->object_offset) > le64toh(b->object_offset)) + return 1; + return 0; +} + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) { + unsigned i; + EntryItem *items; + int r; + uint64_t xor_hash = 0; + struct dual_timestamp _ts; + + assert(f); + assert(f->header); + assert(iovec || n_iovec == 0); + + if (!ts) { + dual_timestamp_get(&_ts); + ts = &_ts; + } + +#ifdef HAVE_GCRYPT + r = journal_file_maybe_append_tag(f, ts->realtime); + if (r < 0) + return r; +#endif + + /* alloca() can't take 0, hence let's allocate at least one */ + items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec)); + + for (i = 0; i < n_iovec; i++) { + uint64_t p; + Object *o; + + r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); + if (r < 0) + return r; + + xor_hash ^= le64toh(o->data.hash); + items[i].object_offset = htole64(p); + items[i].hash = o->data.hash; + } + + /* Order by the position on disk, in order to improve seek + * times for rotating media. */ + qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp); + + r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset); + + /* If the memory mapping triggered a SIGBUS then we return an + * IO error and ignore the error code passed down to us, since + * it is very likely just an effect of a nullified replacement + * mapping page */ + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) + r = -EIO; + + if (f->post_change_timer) + schedule_post_change(f); + else + journal_file_post_change(f); + + return r; +} + +typedef struct ChainCacheItem { + uint64_t first; /* the array at the beginning of the chain */ + uint64_t array; /* the cached array */ + uint64_t begin; /* the first item in the cached array */ + uint64_t total; /* the total number of items in all arrays before this one in the chain */ + uint64_t last_index; /* the last index we looked at, to optimize locality when bisecting */ +} ChainCacheItem; + +static void chain_cache_put( + OrderedHashmap *h, + ChainCacheItem *ci, + uint64_t first, + uint64_t array, + uint64_t begin, + uint64_t total, + uint64_t last_index) { + + if (!ci) { + /* If the chain item to cache for this chain is the + * first one it's not worth caching anything */ + if (array == first) + return; + + if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) { + ci = ordered_hashmap_steal_first(h); + assert(ci); + } else { + ci = new(ChainCacheItem, 1); + if (!ci) + return; + } + + ci->first = first; + + if (ordered_hashmap_put(h, &ci->first, ci) < 0) { + free(ci); + return; + } + } else + assert(ci->first == first); + + ci->array = array; + ci->begin = begin; + ci->total = total; + ci->last_index = last_index; +} + +static int generic_array_get( + JournalFile *f, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + uint64_t p = 0, a, t = 0; + int r; + ChainCacheItem *ci; + + assert(f); + + a = first; + + /* Try the chain cache first */ + ci = ordered_hashmap_get(f->chain_cache, &first); + if (ci && i > ci->total) { + a = ci->array; + i -= ci->total; + t = ci->total; + } + + while (a > 0) { + uint64_t k; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(o); + if (i < k) { + p = le64toh(o->entry_array.items[i]); + goto found; + } + + i -= k; + t += k; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + return 0; + +found: + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; +} + +static int generic_array_get_plus_one( + JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t i, + Object **ret, uint64_t *offset) { + + Object *o; + + assert(f); + + if (i == 0) { + int r; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + return 1; + } + + return generic_array_get(f, first, i-1, ret, offset); +} + +enum { + TEST_FOUND, + TEST_LEFT, + TEST_RIGHT +}; + +static int generic_array_bisect( + JournalFile *f, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1; + bool subtract_one = false; + Object *o, *array = NULL; + int r; + ChainCacheItem *ci; + + assert(f); + assert(test_object); + + /* Start with the first array in the chain */ + a = first; + + ci = ordered_hashmap_get(f->chain_cache, &first); + if (ci && n > ci->total) { + /* Ah, we have iterated this bisection array chain + * previously! Let's see if we can skip ahead in the + * chain, as far as the last time. But we can't jump + * backwards in the chain, so let's check that + * first. */ + + r = test_object(f, ci->begin, needle); + if (r < 0) + return r; + + if (r == TEST_LEFT) { + /* OK, what we are looking for is right of the + * begin of this EntryArray, so let's jump + * straight to previously cached array in the + * chain */ + + a = ci->array; + n -= ci->total; + t = ci->total; + last_index = ci->last_index; + } + } + + while (a > 0) { + uint64_t left, right, k, lp; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array); + if (r < 0) + return r; + + k = journal_file_entry_array_n_items(array); + right = MIN(k, n); + if (right <= 0) + return 0; + + i = right - 1; + lp = p = le64toh(array->entry_array.items[i]); + if (p <= 0) + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); + n = i; + continue; + } + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) { + left = 0; + right -= 1; + + if (last_index != (uint64_t) -1) { + assert(last_index <= right); + + /* If we cached the last index we + * looked at, let's try to not to jump + * too wildly around and see if we can + * limit the range to look at early to + * the immediate neighbors of the last + * index we looked at. */ + + if (last_index > 0) { + uint64_t x = last_index - 1; + + p = le64toh(array->entry_array.items[x]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = x; + else + left = x + 1; + } + + if (last_index < right) { + uint64_t y = last_index + 1; + + p = le64toh(array->entry_array.items[y]); + if (p <= 0) + return -EBADMSG; + + r = test_object(f, p, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = y; + else + left = y + 1; + } + } + + for (;;) { + if (left == right) { + if (direction == DIRECTION_UP) + subtract_one = true; + + i = left; + goto found; + } + + assert(left < right); + i = (left + right) / 2; + + p = le64toh(array->entry_array.items[i]); + if (p <= 0) + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); + right = n = i; + continue; + } + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + if (r == TEST_RIGHT) + right = i; + else + left = i + 1; + } + } + + if (k >= n) { + if (direction == DIRECTION_UP) { + i = n; + subtract_one = true; + goto found; + } + + return 0; + } + + last_p = lp; + + n -= k; + t += k; + last_index = (uint64_t) -1; + a = le64toh(array->entry_array.next_entry_array_offset); + } + + return 0; + +found: + if (subtract_one && t == 0 && i == 0) + return 0; + + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, le64toh(array->entry_array.items[0]), t, subtract_one ? (i > 0 ? i-1 : (uint64_t) -1) : i); + + if (subtract_one && i == 0) + p = last_p; + else if (subtract_one) + p = le64toh(array->entry_array.items[i-1]); + else + p = le64toh(array->entry_array.items[i]); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + if (idx) + *idx = t + i + (subtract_one ? -1 : 0); + + return 1; +} + +static int generic_array_bisect_plus_one( + JournalFile *f, + uint64_t extra, + uint64_t first, + uint64_t n, + uint64_t needle, + int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle), + direction_t direction, + Object **ret, + uint64_t *offset, + uint64_t *idx) { + + int r; + bool step_back = false; + Object *o; + + assert(f); + assert(test_object); + + if (n <= 0) + return 0; + + /* This bisects the array in object 'first', but first checks + * an extra */ + r = test_object(f, extra, needle); + if (r < 0) + return r; + + if (r == TEST_FOUND) + r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; + + /* if we are looking with DIRECTION_UP then we need to first + see if in the actual array there is a matching entry, and + return the last one of that. But if there isn't any we need + to return this one. Hence remember this, and return it + below. */ + if (r == TEST_LEFT) + step_back = direction == DIRECTION_UP; + + if (r == TEST_RIGHT) { + if (direction == DIRECTION_DOWN) + goto found; + else + return 0; + } + + r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); + + if (r == 0 && step_back) + goto found; + + if (r > 0 && idx) + (*idx)++; + + return r; + +found: + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + if (idx) + *idx = 0; + + return 1; +} + +_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { + assert(f); + assert(p > 0); + + if (p == needle) + return TEST_FOUND; + else if (p < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.seqnum) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.seqnum) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_seqnum( + JournalFile *f, + uint64_t seqnum, + direction_t direction, + Object **ret, + uint64_t *offset) { + assert(f); + assert(f->header); + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.realtime) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.realtime) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_realtime( + JournalFile *f, + uint64_t realtime, + direction_t direction, + Object **ret, + uint64_t *offset) { + assert(f); + assert(f->header); + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { + Object *o; + int r; + + assert(f); + assert(p > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + if (le64toh(o->entry.monotonic) == needle) + return TEST_FOUND; + else if (le64toh(o->entry.monotonic) < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +static int find_data_object_by_boot_id( + JournalFile *f, + sd_id128_t boot_id, + Object **o, + uint64_t *b) { + + char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID="; + + sd_id128_to_string(boot_id, t + 9); + return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b); +} + +int journal_file_move_to_entry_by_monotonic( + JournalFile *f, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, + uint64_t *offset) { + + Object *o; + int r; + + assert(f); + + r = find_data_object_by_boot_id(f, boot_id, &o, NULL); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + return generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + ret, offset, NULL); +} + +void journal_file_reset_location(JournalFile *f) { + f->location_type = LOCATION_HEAD; + f->current_offset = 0; + f->current_seqnum = 0; + f->current_realtime = 0; + f->current_monotonic = 0; + zero(f->current_boot_id); + f->current_xor_hash = 0; +} + +void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) { + f->location_type = LOCATION_SEEK; + f->current_offset = offset; + f->current_seqnum = le64toh(o->entry.seqnum); + f->current_realtime = le64toh(o->entry.realtime); + f->current_monotonic = le64toh(o->entry.monotonic); + f->current_boot_id = o->entry.boot_id; + f->current_xor_hash = le64toh(o->entry.xor_hash); +} + +int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { + assert(af); + assert(af->header); + assert(bf); + assert(bf->header); + assert(af->location_type == LOCATION_SEEK); + assert(bf->location_type == LOCATION_SEEK); + + /* If contents and timestamps match, these entries are + * identical, even if the seqnum does not match */ + if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) && + af->current_monotonic == bf->current_monotonic && + af->current_realtime == bf->current_realtime && + af->current_xor_hash == bf->current_xor_hash) + return 0; + + if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { + + /* If this is from the same seqnum source, compare + * seqnums */ + if (af->current_seqnum < bf->current_seqnum) + return -1; + if (af->current_seqnum > bf->current_seqnum) + return 1; + + /* Wow! This is weird, different data but the same + * seqnums? Something is borked, but let's make the + * best of it and compare by time. */ + } + + if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) { + + /* If the boot id matches, compare monotonic time */ + if (af->current_monotonic < bf->current_monotonic) + return -1; + if (af->current_monotonic > bf->current_monotonic) + return 1; + } + + /* Otherwise, compare UTC time */ + if (af->current_realtime < bf->current_realtime) + return -1; + if (af->current_realtime > bf->current_realtime) + return 1; + + /* Finally, compare by contents */ + if (af->current_xor_hash < bf->current_xor_hash) + return -1; + if (af->current_xor_hash > bf->current_xor_hash) + return 1; + + return 0; +} + +int journal_file_next_entry( + JournalFile *f, + uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t i, n, ofs; + int r; + + assert(f); + assert(f->header); + + n = le64toh(f->header->n_entries); + if (n <= 0) + return 0; + + if (p == 0) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + r = generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + } + + /* And jump to it */ + r = generic_array_get(f, + le64toh(f->header->entry_array_offset), + i, + ret, &ofs); + if (r == -EBADMSG && direction == DIRECTION_DOWN) { + /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, + * consider this the end of the journal file. */ + log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); + return 0; + } + if (r <= 0) + return r; + + if (p > 0 && + (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { + log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); + return -EBADMSG; + } + + if (offset) + *offset = ofs; + + return 1; +} + +int journal_file_next_entry_for_data( + JournalFile *f, + Object *o, uint64_t p, + uint64_t data_offset, + direction_t direction, + Object **ret, uint64_t *offset) { + + uint64_t n, i; + int r; + Object *d; + + assert(f); + assert(p > 0 || !o); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + n = le64toh(d->data.n_entries); + if (n <= 0) + return n; + + if (!o) + i = direction == DIRECTION_DOWN ? 0 : n - 1; + else { + if (o->object.type != OBJECT_ENTRY) + return -EINVAL; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + DIRECTION_DOWN, + NULL, NULL, + &i); + + if (r <= 0) + return r; + + if (direction == DIRECTION_DOWN) { + if (i >= n - 1) + return 0; + + i++; + } else { + if (i <= 0) + return 0; + + i--; + } + + } + + return generic_array_get_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + i, + ret, offset); +} + +int journal_file_move_to_entry_by_offset_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + int r; + Object *d; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_monotonic_for_data( + JournalFile *f, + uint64_t data_offset, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *o, *d; + int r; + uint64_t b, z; + + assert(f); + + /* First, seek by time */ + r = find_data_object_by_boot_id(f, boot_id, &o, &b); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + NULL, &z, NULL); + if (r <= 0) + return r; + + /* And now, continue seeking until we find an entry that + * exists in both bisection arrays */ + + for (;;) { + Object *qo; + uint64_t p, q; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + z, + test_object_offset, + direction, + NULL, &p, NULL); + if (r <= 0) + return r; + + r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + p, + test_object_offset, + direction, + &qo, &q, NULL); + + if (r <= 0) + return r; + + if (p == q) { + if (ret) + *ret = qo; + if (offset) + *offset = q; + + return 1; + } + + z = q; + } +} + +int journal_file_move_to_entry_by_seqnum_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t seqnum, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + seqnum, + test_object_seqnum, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_realtime_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t realtime, + direction_t direction, + Object **ret, uint64_t *offset) { + + Object *d; + int r; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + realtime, + test_object_realtime, + direction, + ret, offset, NULL); +} + +void journal_file_dump(JournalFile *f) { + Object *o; + int r; + uint64_t p; + + assert(f); + assert(f->header); + + journal_file_print_header(f); + + p = le64toh(f->header->header_size); + while (p != 0) { + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + + switch (o->object.type) { + + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; + + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; + + case OBJECT_FIELD: + printf("Type: OBJECT_FIELD\n"); + break; + + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n", + le64toh(o->entry.seqnum), + le64toh(o->entry.monotonic), + le64toh(o->entry.realtime)); + break; + + case OBJECT_FIELD_HASH_TABLE: + printf("Type: OBJECT_FIELD_HASH_TABLE\n"); + break; + + case OBJECT_DATA_HASH_TABLE: + printf("Type: OBJECT_DATA_HASH_TABLE\n"); + break; + + case OBJECT_ENTRY_ARRAY: + printf("Type: OBJECT_ENTRY_ARRAY\n"); + break; + + case OBJECT_TAG: + printf("Type: OBJECT_TAG seqnum=%"PRIu64" epoch=%"PRIu64"\n", + le64toh(o->tag.seqnum), + le64toh(o->tag.epoch)); + break; + + default: + printf("Type: unknown (%i)\n", o->object.type); + break; + } + + if (o->object.flags & OBJECT_COMPRESSION_MASK) + printf("Flags: %s\n", + object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK)); + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + return; +fail: + log_error("File corrupt"); +} + +static const char* format_timestamp_safe(char *buf, size_t l, usec_t t) { + const char *x; + + x = format_timestamp(buf, l, t); + if (x) + return x; + return " --- "; +} + +void journal_file_print_header(JournalFile *f) { + char a[33], b[33], c[33], d[33]; + char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX], z[FORMAT_TIMESTAMP_MAX]; + struct stat st; + char bytes[FORMAT_BYTES_MAX]; + + assert(f); + assert(f->header); + + printf("File Path: %s\n" + "File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Sequential Number ID: %s\n" + "State: %s\n" + "Compatible Flags:%s%s\n" + "Incompatible Flags:%s%s%s\n" + "Header size: %"PRIu64"\n" + "Arena size: %"PRIu64"\n" + "Data Hash Table Size: %"PRIu64"\n" + "Field Hash Table Size: %"PRIu64"\n" + "Rotate Suggested: %s\n" + "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Head Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Monotonic Timestamp: %s (%"PRIx64")\n" + "Objects: %"PRIu64"\n" + "Entry Objects: %"PRIu64"\n", + f->path, + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + sd_id128_to_string(f->header->seqnum_id, d), + f->header->state == STATE_OFFLINE ? "OFFLINE" : + f->header->state == STATE_ONLINE ? "ONLINE" : + f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN", + JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "", + (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "", + JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "", + JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "", + (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "", + le64toh(f->header->header_size), + le64toh(f->header->arena_size), + le64toh(f->header->data_hash_table_size) / sizeof(HashItem), + le64toh(f->header->field_hash_table_size) / sizeof(HashItem), + yes_no(journal_file_rotate_suggested(f, 0)), + le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), + le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), + format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), + format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), + format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), + le64toh(f->header->n_objects), + le64toh(f->header->n_entries)); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + printf("Data Objects: %"PRIu64"\n" + "Data Hash Table Fill: %.1f%%\n", + le64toh(f->header->n_data), + 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)))); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + printf("Field Objects: %"PRIu64"\n" + "Field Hash Table Fill: %.1f%%\n", + le64toh(f->header->n_fields), + 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags)) + printf("Tag Objects: %"PRIu64"\n", + le64toh(f->header->n_tags)); + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) + printf("Entry Array Objects: %"PRIu64"\n", + le64toh(f->header->n_entry_arrays)); + + if (fstat(f->fd, &st) >= 0) + printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL)); +} + +static int journal_file_warn_btrfs(JournalFile *f) { + unsigned attrs; + int r; + + assert(f); + + /* Before we write anything, check if the COW logic is turned + * off on btrfs. Given our write pattern that is quite + * unfriendly to COW file systems this should greatly improve + * performance on COW file systems, such as btrfs, at the + * expense of data integrity features (which shouldn't be too + * bad, given that we do our own checksumming). */ + + r = btrfs_is_filesystem(f->fd); + if (r < 0) + return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m"); + if (!r) + return 0; + + r = read_attr_fd(f->fd, &attrs); + if (r < 0) + return log_warning_errno(r, "Failed to read file attributes: %m"); + + if (attrs & FS_NOCOW_FL) { + log_debug("Detected btrfs file system with copy-on-write disabled, all is good."); + return 0; + } + + log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. " + "This is likely to slow down journal access substantially, please consider turning " + "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path); + + return 1; +} + +int journal_file_open( + int fd, + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret) { + + bool newly_created = false; + JournalFile *f; + void *h; + int r; + + assert(ret); + assert(fd >= 0 || fname); + + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + if (fname) { + if (!endswith(fname, ".journal") && + !endswith(fname, ".journal~")) + return -EINVAL; + } + + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; + + f->fd = fd; + f->mode = mode; + + f->flags = flags; + f->prot = prot_from_flags(flags); + f->writable = (flags & O_ACCMODE) != O_RDONLY; +#if defined(HAVE_LZ4) + f->compress_lz4 = compress; +#elif defined(HAVE_XZ) + f->compress_xz = compress; +#endif +#ifdef HAVE_GCRYPT + f->seal = seal; +#endif + + if (mmap_cache) + f->mmap = mmap_cache_ref(mmap_cache); + else { + f->mmap = mmap_cache_new(); + if (!f->mmap) { + r = -ENOMEM; + goto fail; + } + } + + if (fname) + f->path = strdup(fname); + else /* If we don't know the path, fill in something explanatory and vaguely useful */ + asprintf(&f->path, "/proc/self/%i", fd); + if (!f->path) { + r = -ENOMEM; + goto fail; + } + + f->chain_cache = ordered_hashmap_new(&uint64_hash_ops); + if (!f->chain_cache) { + r = -ENOMEM; + goto fail; + } + + if (f->fd < 0) { + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + /* fds we opened here by us should also be closed by us. */ + f->close_fd = true; + } + + r = journal_file_fstat(f); + if (r < 0) + goto fail; + + if (f->last_stat.st_size == 0 && f->writable) { + + (void) journal_file_warn_btrfs(f); + + /* Let's attach the creation time to the journal file, + * so that the vacuuming code knows the age of this + * file even if the file might end up corrupted one + * day... Ideally we'd just use the creation time many + * file systems maintain for each file, but there is + * currently no usable API to query this, hence let's + * emulate this via extended attributes. If extended + * attributes are not supported we'll just skip this, + * and rely solely on mtime/atime/ctime of the file. */ + + fd_setcrtime(f->fd, 0); + +#ifdef HAVE_GCRYPT + /* Try to load the FSPRG state, and if we can't, then + * just don't do sealing */ + if (f->seal) { + r = journal_file_fss_load(f); + if (r < 0) + f->seal = false; + } +#endif + + r = journal_file_init_header(f, template); + if (r < 0) + goto fail; + + r = journal_file_fstat(f); + if (r < 0) + goto fail; + + newly_created = true; + } + + if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { + r = -ENODATA; + goto fail; + } + + r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h); + if (r < 0) + goto fail; + + f->header = h; + + if (!newly_created) { + if (deferred_closes) + journal_file_close_set(deferred_closes); + + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } + +#ifdef HAVE_GCRYPT + if (!newly_created && f->writable) { + r = journal_file_fss_load(f); + if (r < 0) + goto fail; + } +#endif + + if (f->writable) { + if (metrics) { + journal_default_metrics(metrics, f->fd); + f->metrics = *metrics; + } else if (template) + f->metrics = template->metrics; + + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } + +#ifdef HAVE_GCRYPT + r = journal_file_hmac_setup(f); + if (r < 0) + goto fail; +#endif + + if (newly_created) { + r = journal_file_setup_field_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_setup_data_hash_table(f); + if (r < 0) + goto fail; + +#ifdef HAVE_GCRYPT + r = journal_file_append_first_tag(f); + if (r < 0) + goto fail; +#endif + } + + if (mmap_cache_got_sigbus(f->mmap, f->fd)) { + r = -EIO; + goto fail; + } + + if (template && template->post_change_timer) { + r = journal_file_enable_post_change_timer( + f, + sd_event_source_get_event(template->post_change_timer), + template->post_change_timer_period); + + if (r < 0) + goto fail; + } + + /* The file is opened now successfully, thus we take possession of any passed in fd. */ + f->close_fd = true; + + *ret = f; + return 0; + +fail: + if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd)) + r = -EIO; + + (void) journal_file_close(f); + + return r; +} + +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes) { + _cleanup_free_ char *p = NULL; + size_t l; + JournalFile *old_file, *new_file = NULL; + int r; + + assert(f); + assert(*f); + + old_file = *f; + + if (!old_file->writable) + return -EINVAL; + + /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse + * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ + if (path_startswith(old_file->path, "/proc/self/fd")) + return -EINVAL; + + if (!endswith(old_file->path, ".journal")) + return -EINVAL; + + l = strlen(old_file->path); + r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal", + (int) l - 8, old_file->path, + SD_ID128_FORMAT_VAL(old_file->header->seqnum_id), + le64toh((*f)->header->head_entry_seqnum), + le64toh((*f)->header->head_entry_realtime)); + if (r < 0) + return -ENOMEM; + + /* Try to rename the file to the archived version. If the file + * already was deleted, we'll get ENOENT, let's ignore that + * case. */ + r = rename(old_file->path, p); + if (r < 0 && errno != ENOENT) + return -errno; + + /* Sync the rename to disk */ + (void) fsync_directory_of_file(old_file->fd); + + /* Set as archive so offlining commits w/state=STATE_ARCHIVED. + * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, + * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which + * would result in the rotated journal never getting fsync() called before closing. + * Now we simply queue the archive state by setting an archive bit, leaving the state + * as STATE_ONLINE so proper offlining occurs. */ + old_file->archive = true; + + /* Currently, btrfs is not very good with out write patterns + * and fragments heavily. Let's defrag our journal files when + * we archive them */ + old_file->defrag_on_close = true; + + r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); + + if (deferred_closes && + set_put(deferred_closes, old_file) >= 0) + (void) journal_file_set_offline(old_file, false); + else + (void) journal_file_close(old_file); + + *f = new_file; + return r; +} + +int journal_file_open_reliably( + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret) { + + int r; + size_t l; + _cleanup_free_ char *p = NULL; + + r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + if (!IN_SET(r, + -EBADMSG, /* corrupted */ + -ENODATA, /* truncated */ + -EHOSTDOWN, /* other machine */ + -EPROTONOSUPPORT, /* incompatible feature */ + -EBUSY, /* unclean shutdown */ + -ESHUTDOWN, /* already archived */ + -EIO, /* IO error, including SIGBUS on mmap */ + -EIDRM /* File has been deleted */)) + return r; + + if ((flags & O_ACCMODE) == O_RDONLY) + return r; + + if (!(flags & O_CREAT)) + return r; + + if (!endswith(fname, ".journal")) + return r; + + /* The file is corrupted. Rotate it away and try it again (but only once) */ + + l = strlen(fname); + if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~", + (int) l - 8, fname, + now(CLOCK_REALTIME), + random_u64()) < 0) + return -ENOMEM; + + if (rename(fname, p) < 0) + return -errno; + + /* btrfs doesn't cope well with our write pattern and + * fragments heavily. Let's defrag all files we rotate */ + + (void) chattr_path(p, 0, FS_NOCOW_FL); + (void) btrfs_defrag(p); + + log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); + + return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); +} + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { + uint64_t i, n; + uint64_t q, xor_hash = 0; + int r; + EntryItem *items; + dual_timestamp ts; + + assert(from); + assert(to); + assert(o); + assert(p); + + if (!to->writable) + return -EPERM; + + ts.monotonic = le64toh(o->entry.monotonic); + ts.realtime = le64toh(o->entry.realtime); + + n = journal_file_entry_n_items(o); + /* alloca() can't take 0, hence let's allocate at least one */ + items = alloca(sizeof(EntryItem) * MAX(1u, n)); + + for (i = 0; i < n; i++) { + uint64_t l, h; + le64_t le_hash; + size_t t; + void *data; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + + r = journal_file_move_to_object(from, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We hit the limit on 32bit machines */ + if ((uint64_t) t != l) + return -E2BIG; + + if (o->object.flags & OBJECT_COMPRESSION_MASK) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + size_t rsize = 0; + + r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK, + o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0); + if (r < 0) + return r; + + data = from->compress_buffer; + l = rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else + data = o->data.payload; + + r = journal_file_append_data(to, data, l, &u, &h); + if (r < 0) + return r; + + xor_hash ^= le64toh(u->data.hash); + items[i].object_offset = htole64(h); + items[i].hash = u->data.hash; + + r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + } + + r = journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset); + + if (mmap_cache_got_sigbus(to->mmap, to->fd)) + return -EIO; + + 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) { + char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX]; + struct statvfs ss; + 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) { + + if (fs_size > 0) { + m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */ + + if (m->max_use > DEFAULT_MAX_USE_UPPER) + m->max_use = DEFAULT_MAX_USE_UPPER; + + if (m->max_use < DEFAULT_MAX_USE_LOWER) + m->max_use = DEFAULT_MAX_USE_LOWER; + } else + m->max_use = DEFAULT_MAX_USE_LOWER; + } else { + m->max_use = PAGE_ALIGN(m->max_use); + + if (m->max_use != 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 */ + + if (m->max_size > DEFAULT_MAX_SIZE_UPPER) + m->max_size = DEFAULT_MAX_SIZE_UPPER; + } else + m->max_size = PAGE_ALIGN(m->max_size); + + if (m->max_size != 0) { + if (m->max_size < JOURNAL_FILE_SIZE_MIN) + m->max_size = JOURNAL_FILE_SIZE_MIN; + + 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; + else { + m->min_size = PAGE_ALIGN(m->min_size); + + if (m->min_size < JOURNAL_FILE_SIZE_MIN) + m->min_size = JOURNAL_FILE_SIZE_MIN; + + if (m->max_size != 0 && m->min_size > m->max_size) + m->max_size = m->min_size; + } + + if (m->keep_free == (uint64_t) -1) { + + if (fs_size > 0) { + m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ + + if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) + m->keep_free = DEFAULT_KEEP_FREE_UPPER; + + } else + m->keep_free = DEFAULT_KEEP_FREE; + } + + 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) { + assert(f); + assert(f->header); + assert(from || to); + + if (from) { + if (f->header->head_entry_realtime == 0) + return -ENOENT; + + *from = le64toh(f->header->head_entry_realtime); + } + + if (to) { + if (f->header->tail_entry_realtime == 0) + return -ENOENT; + + *to = le64toh(f->header->tail_entry_realtime); + } + + return 1; +} + +int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) { + Object *o; + uint64_t p; + int r; + + assert(f); + assert(from || to); + + r = find_data_object_by_boot_id(f, boot_id, &o, &p); + if (r <= 0) + return r; + + if (le64toh(o->data.n_entries) <= 0) + return 0; + + if (from) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o); + if (r < 0) + return r; + + *from = le64toh(o->entry.monotonic); + } + + if (to) { + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + r = generic_array_get_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries)-1, + &o, NULL); + if (r <= 0) + return r; + + *to = le64toh(o->entry.monotonic); + } + + return 1; +} + +bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) { + assert(f); + assert(f->header); + + /* If we gained new header fields we gained new features, + * hence suggest a rotation */ + if (le64toh(f->header->header_size) < sizeof(Header)) { + log_debug("%s uses an outdated header, suggesting rotation.", f->path); + return true; + } + + /* Let's check if the hash tables grew over a certain fill + * level (75%, borrowing this value from Java's hash table + * implementation), and if so suggest a rotation. To calculate + * the fill level we need the n_data field, which only exists + * in newer versions. */ + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) { + log_debug("Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.", + f->path, + 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))), + le64toh(f->header->n_data), + le64toh(f->header->data_hash_table_size) / sizeof(HashItem), + (unsigned long long) f->last_stat.st_size, + f->last_stat.st_size / le64toh(f->header->n_data)); + return true; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) { + log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.", + f->path, + 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))), + le64toh(f->header->n_fields), + le64toh(f->header->field_hash_table_size) / sizeof(HashItem)); + return true; + } + + /* Are the data objects properly indexed by field objects? */ + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + le64toh(f->header->n_data) > 0 && + le64toh(f->header->n_fields) == 0) + return true; + + if (max_file_usec > 0) { + usec_t t, h; + + h = le64toh(f->header->head_entry_realtime); + t = now(CLOCK_REALTIME); + + if (h > 0 && t > h + max_file_usec) + return true; + } + + return false; +} diff --git a/src/libsystemd/src/sd-journal/journal-file.h b/src/libsystemd/src/sd-journal/journal-file.h new file mode 100644 index 0000000000..daafc8ae0d --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-file.h @@ -0,0 +1,266 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#ifdef HAVE_GCRYPT +#include +#endif + +#include +#include + +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/sparse-endian.h" + +#include "journal-def.h" +#include "mmap-cache.h" + +typedef struct JournalMetrics { + /* 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 { + DIRECTION_UP, + DIRECTION_DOWN +} direction_t; + +typedef enum LocationType { + /* The first and last entries, resp. */ + LOCATION_HEAD, + LOCATION_TAIL, + + /* We already read the entry we currently point to, and the + * next one to read should probably not be this one again. */ + LOCATION_DISCRETE, + + /* We should seek to the precise location specified, and + * return it, as we haven't read it yet. */ + LOCATION_SEEK +} LocationType; + +typedef enum OfflineState { + OFFLINE_JOINED, + OFFLINE_SYNCING, + OFFLINE_OFFLINING, + OFFLINE_CANCEL, + OFFLINE_AGAIN_FROM_SYNCING, + OFFLINE_AGAIN_FROM_OFFLINING, + OFFLINE_DONE +} OfflineState; + +typedef struct JournalFile { + int fd; + + mode_t mode; + + int flags; + int prot; + bool writable:1; + bool compress_xz:1; + bool compress_lz4:1; + bool seal:1; + bool defrag_on_close:1; + bool close_fd:1; + bool archive:1; + + bool tail_entry_monotonic_valid:1; + + direction_t last_direction; + LocationType location_type; + uint64_t last_n_entries; + + char *path; + struct stat last_stat; + usec_t last_stat_usec; + + Header *header; + HashItem *data_hash_table; + HashItem *field_hash_table; + + uint64_t current_offset; + uint64_t current_seqnum; + uint64_t current_realtime; + uint64_t current_monotonic; + sd_id128_t current_boot_id; + uint64_t current_xor_hash; + + JournalMetrics metrics; + MMapCache *mmap; + + sd_event_source *post_change_timer; + usec_t post_change_timer_period; + + OrderedHashmap *chain_cache; + + pthread_t offline_thread; + volatile OfflineState offline_state; + +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + void *compress_buffer; + size_t compress_buffer_size; +#endif + +#ifdef HAVE_GCRYPT + gcry_md_hd_t hmac; + bool hmac_running; + + FSSHeader *fss_file; + size_t fss_file_size; + + uint64_t fss_start_usec; + uint64_t fss_interval_usec; + + void *fsprg_state; + size_t fsprg_state_size; + + void *fsprg_seed; + size_t fsprg_seed_size; +#endif +} JournalFile; + +int journal_file_open( + int fd, + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret); + +int journal_file_set_offline(JournalFile *f, bool wait); +bool journal_file_is_offlining(JournalFile *f); +JournalFile* journal_file_close(JournalFile *j); +void journal_file_close_set(Set *s); + +int journal_file_open_reliably( + const char *fname, + int flags, + mode_t mode, + bool compress, + bool seal, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + JournalFile *template, + JournalFile **ret); + +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) +#define VALID64(x) (((x) & 7ULL) == 0ULL) + +/* Use six characters to cover the offsets common in smallish journal + * files without adding too many zeros. */ +#define OFSfmt "%06"PRIx64 + +static inline bool VALID_REALTIME(uint64_t u) { + /* This considers timestamps until the year 3112 valid. That should be plenty room... */ + return u > 0 && u < (1ULL << 55); +} + +static inline bool VALID_MONOTONIC(uint64_t u) { + /* This considers timestamps until 1142 years of runtime valid. */ + return u < (1ULL << 55); +} + +static inline bool VALID_EPOCH(uint64_t u) { + /* This allows changing the key for 1142 years, every usec. */ + return u < (1ULL << 55); +} + +#define JOURNAL_HEADER_CONTAINS(h, field) \ + (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) + +#define JOURNAL_HEADER_SEALED(h) \ + (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED)) + +#define JOURNAL_HEADER_COMPRESSED_XZ(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) + +#define JOURNAL_HEADER_COMPRESSED_LZ4(h) \ + (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) + +int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); + +uint64_t journal_file_entry_n_items(Object *o) _pure_; +uint64_t journal_file_entry_array_n_items(Object *o) _pure_; +uint64_t journal_file_hash_table_n_items(Object *o) _pure_; + +int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); + +int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); + +void journal_file_reset_location(JournalFile *f); +void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset); +int journal_file_compare_locations(JournalFile *af, JournalFile *bf); +int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); + +int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset); + +void journal_file_dump(JournalFile *f); +void journal_file_print_header(JournalFile *f); + +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes); + +void journal_file_post_change(JournalFile *f); +int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); + +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); +int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); + +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/libsystemd/src/sd-journal/journal-internal.h b/src/libsystemd/src/sd-journal/journal-internal.h new file mode 100644 index 0000000000..e249a8b979 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-internal.h @@ -0,0 +1,144 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include +#include + +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/set.h" + +#include "journal-def.h" +#include "journal-file.h" + +typedef struct Match Match; +typedef struct Location Location; +typedef struct Directory Directory; + +typedef enum MatchType { + MATCH_DISCRETE, + MATCH_OR_TERM, + MATCH_AND_TERM +} MatchType; + +struct Match { + MatchType type; + Match *parent; + LIST_FIELDS(Match, matches); + + /* For concrete matches */ + char *data; + size_t size; + le64_t le_hash; + + /* For terms */ + LIST_HEAD(Match, matches); +}; + +struct Location { + LocationType type; + + bool seqnum_set; + bool realtime_set; + bool monotonic_set; + bool xor_hash_set; + + uint64_t seqnum; + sd_id128_t seqnum_id; + + uint64_t realtime; + + uint64_t monotonic; + sd_id128_t boot_id; + + uint64_t xor_hash; +}; + +struct Directory { + char *path; + int wd; + bool is_root; +}; + +struct sd_journal { + int toplevel_fd; + + char *path; + char *prefix; + + OrderedHashmap *files; + MMapCache *mmap; + + Location current_location; + + JournalFile *current_file; + uint64_t current_field; + + Match *level0, *level1, *level2; + + pid_t original_pid; + + int inotify_fd; + unsigned current_invalidate_counter, last_invalidate_counter; + usec_t last_process_usec; + + /* Iterating through unique fields and their data values */ + char *unique_field; + JournalFile *unique_file; + uint64_t unique_offset; + + /* Iterating through known fields */ + JournalFile *fields_file; + uint64_t fields_offset; + uint64_t fields_hash_table_index; + char *fields_buffer; + size_t fields_buffer_allocated; + + int flags; + + bool on_network:1; + bool no_new_files:1; + bool no_inotify:1; + bool unique_file_lost:1; /* File we were iterating over got + removed, and there were no more + files, so sd_j_enumerate_unique + will return a value equal to 0. */ + bool fields_file_lost:1; + bool has_runtime_files:1; + bool has_persistent_files:1; + + size_t data_threshold; + + Hashmap *directories_by_path; + Hashmap *directories_by_wd; + + Hashmap *errors; +}; + +char *journal_make_match_string(sd_journal *j); +void journal_print_header(sd_journal *j); + +#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/libsystemd/src/sd-journal/journal-send.c b/src/libsystemd/src/sd-journal/journal-send.c new file mode 100644 index 0000000000..180311009c --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-send.c @@ -0,0 +1,575 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#define SD_JOURNAL_SUPPRESS_LOCATION + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/memfd-util.h" +#include "basic/socket-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#define SNDBUF_SIZE (8*1024*1024) + +#define ALLOCA_CODE_FUNC(f, func) \ + do { \ + size_t _fl; \ + const char *_func = (func); \ + char **_f = &(f); \ + _fl = strlen(_func) + 1; \ + *_f = alloca(_fl + 10); \ + memcpy(*_f, "CODE_FUNC=", 10); \ + memcpy(*_f + 10, _func, _fl); \ + } while (false) + +/* We open a single fd, and we'll share it with the current process, + * all its threads, and all its subprocesses. This means we need to + * initialize it atomically, and need to operate on it atomically + * never assuming we are the only user */ + +static int journal_fd(void) { + int fd; + static int fd_plus_one = 0; + +retry: + if (fd_plus_one > 0) + return fd_plus_one - 1; + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) { + safe_close(fd); + goto retry; + } + + return fd; +} + +_public_ int sd_journal_print(int priority, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv(priority, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { + + /* FIXME: Instead of limiting things to LINE_MAX we could do a + C99 variable-length array on the stack here in a loop. */ + + char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; + struct iovec iov[2]; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + assert_return(format, -EINVAL); + + xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + + /* Strip trailing whitespace, keep prefix whitespace. */ + (void) strstrip(buffer); + + /* Suppress empty lines */ + if (isempty(buffer+8)) + return 0; + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + + return sd_journal_sendv(iov, 2); +} + +_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { + PROTECT_ERRNO; + int r, n = 0, i = 0, j; + struct iovec *iov = NULL; + + assert(_iov); + + if (extra > 0) { + n = MAX(extra * 2, extra + 4); + iov = malloc0(n * sizeof(struct iovec)); + if (!iov) { + r = -ENOMEM; + goto fail; + } + + i = extra; + } + + while (format) { + struct iovec *c; + char *buffer; + va_list aq; + + if (i >= n) { + n = MAX(i*2, 4); + c = realloc(iov, n * sizeof(struct iovec)); + if (!c) { + r = -ENOMEM; + goto fail; + } + + iov = c; + } + + va_copy(aq, ap); + if (vasprintf(&buffer, format, aq) < 0) { + va_end(aq); + r = -ENOMEM; + goto fail; + } + va_end(aq); + + VA_FORMAT_ADVANCE(format, ap); + + (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */ + + IOVEC_SET_STRING(iov[i++], buffer); + + format = va_arg(ap, char *); + } + + *_iov = iov; + + return i; + +fail: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_send(const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 0, &iov); + va_end(ap); + + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + + r = sd_journal_sendv(iov, i); + +finish: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv(const struct iovec *iov, int n) { + PROTECT_ERRNO; + int fd, r; + _cleanup_close_ int buffer_fd = -1; + struct iovec *w; + uint64_t *l; + int i, j = 0; + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; + struct msghdr mh = { + .msg_name = (struct sockaddr*) &sa.sa, + .msg_namelen = SOCKADDR_UN_LEN(sa.un), + }; + ssize_t k; + bool have_syslog_identifier = false; + bool seal = true; + + assert_return(iov, -EINVAL); + assert_return(n > 0, -EINVAL); + + w = newa(struct iovec, n * 5 + 3); + l = newa(uint64_t, n); + + for (i = 0; i < n; i++) { + char *c, *nl; + + if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) + return -EINVAL; + + c = memchr(iov[i].iov_base, '=', iov[i].iov_len); + if (_unlikely_(!c || c == iov[i].iov_base)) + return -EINVAL; + + have_syslog_identifier = have_syslog_identifier || + (c == (char *) iov[i].iov_base + 17 && + startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER")); + + nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); + if (nl) { + if (_unlikely_(nl < c)) + return -EINVAL; + + /* Already includes a newline? Bummer, then + * let's write the variable name, then a + * newline, then the size (64bit LE), followed + * by the data and a final newline */ + + w[j].iov_base = iov[i].iov_base; + w[j].iov_len = c - (char*) iov[i].iov_base; + j++; + + IOVEC_SET_STRING(w[j++], "\n"); + + l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); + w[j].iov_base = &l[i]; + w[j].iov_len = sizeof(uint64_t); + j++; + + w[j].iov_base = c + 1; + w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; + j++; + + } else + /* Nothing special? Then just add the line and + * append a newline */ + w[j++] = iov[i]; + + IOVEC_SET_STRING(w[j++], "\n"); + } + + if (!have_syslog_identifier && + string_is_safe(program_invocation_short_name)) { + + /* Implicitly add program_invocation_short_name, if it + * is not set explicitly. We only do this for + * program_invocation_short_name, and nothing else + * since everything else is much nicer to retrieve + * from the outside. */ + + IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER="); + IOVEC_SET_STRING(w[j++], program_invocation_short_name); + IOVEC_SET_STRING(w[j++], "\n"); + } + + fd = journal_fd(); + if (_unlikely_(fd < 0)) + return fd; + + mh.msg_iov = w; + mh.msg_iovlen = j; + + k = sendmsg(fd, &mh, MSG_NOSIGNAL); + if (k >= 0) + return 0; + + /* Fail silently if the journal is not available */ + if (errno == ENOENT) + return 0; + + if (errno != EMSGSIZE && errno != ENOBUFS) + return -errno; + + /* Message doesn't fit... Let's dump the data in a memfd or + * temporary file and just pass a file descriptor of it to the + * other side. + * + * For the temporary files we use /dev/shm instead of /tmp + * here, since we want this to be a tmpfs, and one that is + * available from early boot on and where unprivileged users + * can create files. */ + buffer_fd = memfd_new(NULL); + if (buffer_fd < 0) { + if (buffer_fd == -ENOSYS) { + buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); + if (buffer_fd < 0) + return buffer_fd; + + seal = false; + } else + return buffer_fd; + } + + n = writev(buffer_fd, w, j); + if (n < 0) + return -errno; + + if (seal) { + r = memfd_set_sealed(buffer_fd); + if (r < 0) + return r; + } + + 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[]) { + PROTECT_ERRNO; + size_t n, k; + + k = isempty(message) ? 0 : strlen(message) + 2; + n = 8 + k + 256 + 1; + + for (;;) { + char buffer[n]; + char* j; + + errno = 0; + j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); + if (errno == 0) { + char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1]; + + if (j != buffer + 8 + k) + memmove(buffer + 8 + k, j, strlen(j)+1); + + memcpy(buffer, "MESSAGE=", 8); + + if (k > 0) { + memcpy(buffer + 8, message, k - 2); + memcpy(buffer + 8 + k - 2, ": ", 2); + } + + xsprintf(error, "ERRNO=%i", _saved_errno_); + + assert_cc(3 == LOG_ERR); + IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); + IOVEC_SET_STRING(iov[skip+1], buffer); + IOVEC_SET_STRING(iov[skip+2], error); + + return sd_journal_sendv(iov, skip + 3); + } + + if (errno != ERANGE) + return -errno; + + n *= 2; + } +} + +_public_ int sd_journal_perror(const char *message) { + struct iovec iovec[3]; + + return fill_iovec_perror_and_send(message, 0, iovec); +} + +_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; + _cleanup_close_ int fd = -1; + char *header; + size_t l; + int r; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return -errno; + + if (shutdown(fd, SHUT_RD) < 0) + return -errno; + + fd_inc_sndbuf(fd, SNDBUF_SIZE); + + if (!identifier) + identifier = ""; + + l = strlen(identifier); + header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2); + + memcpy(header, identifier, l); + header[l++] = '\n'; + header[l++] = '\n'; /* unit id */ + header[l++] = '0' + priority; + header[l++] = '\n'; + header[l++] = '0' + !!level_prefix; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + header[l++] = '0'; + header[l++] = '\n'; + + r = loop_write(fd, header, l, false); + if (r < 0) + return r; + + r = fd; + fd = -1; + return r; +} + +_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv_with_location(priority, file, line, func, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { + char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; + struct iovec iov[5]; + char *f; + + assert_return(priority >= 0, -EINVAL); + assert_return(priority <= 7, -EINVAL); + assert_return(format, -EINVAL); + + xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + + /* Strip trailing whitespace, keep prefixing whitespace */ + (void) strstrip(buffer); + + /* Suppress empty lines */ + if (isempty(buffer+8)) + return 0; + + /* func is initialized from __func__ which is not a macro, but + * a static const char[], hence cannot easily be prefixed with + * CODE_FUNC=, hence let's do it manually here. */ + ALLOCA_CODE_FUNC(f, func); + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + IOVEC_SET_STRING(iov[2], file); + IOVEC_SET_STRING(iov[3], line); + IOVEC_SET_STRING(iov[4], f); + + return sd_journal_sendv(iov, ELEMENTSOF(iov)); +} + +_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + char *f; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 3, &iov); + va_end(ap); + + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(iov[0], file); + IOVEC_SET_STRING(iov[1], line); + IOVEC_SET_STRING(iov[2], f); + + r = sd_journal_sendv(iov, i); + +finish: + for (j = 3; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv_with_location( + const char *file, const char *line, + const char *func, + const struct iovec *iov, int n) { + + struct iovec *niov; + char *f; + + assert_return(iov, -EINVAL); + assert_return(n > 0, -EINVAL); + + niov = alloca(sizeof(struct iovec) * (n + 3)); + memcpy(niov, iov, sizeof(struct iovec) * n); + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(niov[n++], file); + IOVEC_SET_STRING(niov[n++], line); + IOVEC_SET_STRING(niov[n++], f); + + return sd_journal_sendv(niov, n); +} + +_public_ int sd_journal_perror_with_location( + const char *file, const char *line, + const char *func, + const char *message) { + + struct iovec iov[6]; + char *f; + + ALLOCA_CODE_FUNC(f, func); + + IOVEC_SET_STRING(iov[0], file); + IOVEC_SET_STRING(iov[1], line); + IOVEC_SET_STRING(iov[2], f); + + return fill_iovec_perror_and_send(message, 3, iov); +} diff --git a/src/libsystemd/src/sd-journal/journal-vacuum.c b/src/libsystemd/src/sd-journal/journal-vacuum.c new file mode 100644 index 0000000000..a3ebf9b029 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-vacuum.c @@ -0,0 +1,350 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/xattr-util.h" + +#include "journal-def.h" +#include "journal-file.h" +#include "journal-vacuum.h" + +struct vacuum_info { + uint64_t usage; + char *filename; + + uint64_t realtime; + + sd_id128_t seqnum_id; + uint64_t seqnum; + bool have_seqnum; +}; + +static int vacuum_compare(const void *_a, const void *_b) { + const struct vacuum_info *a, *b; + + a = _a; + b = _b; + + if (a->have_seqnum && b->have_seqnum && + sd_id128_equal(a->seqnum_id, b->seqnum_id)) { + if (a->seqnum < b->seqnum) + return -1; + else if (a->seqnum > b->seqnum) + return 1; + else + return 0; + } + + if (a->realtime < b->realtime) + return -1; + else if (a->realtime > b->realtime) + return 1; + else if (a->have_seqnum && b->have_seqnum) + return memcmp(&a->seqnum_id, &b->seqnum_id, 16); + else + return strcmp(a->filename, b->filename); +} + +static void patch_realtime( + int fd, + const char *fn, + const struct stat *st, + unsigned long long *realtime) { + + 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(fd >= 0); + assert(fn); + assert(st); + assert(realtime); + + x = timespec_load(&st->st_ctim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + x = timespec_load(&st->st_atim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + x = timespec_load(&st->st_mtim); + if (x > 0 && x != USEC_INFINITY && x < *realtime) + *realtime = x; + + /* Let's read the original creation time, if possible. Ideally + * we'd just query the creation time the FS might provide, but + * unfortunately there's currently no sane API to query + * it. Hence let's implement this manually... */ + + if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) { + if (crtime < *realtime) + *realtime = crtime; + } +} + +static int journal_file_empty(int dir_fd, const char *name) { + _cleanup_close_ int fd; + struct stat st; + le64_t n_entries; + ssize_t n; + + 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; + + /* If an offline file doesn't even have a header we consider it empty */ + if (st.st_size < (off_t) sizeof(Header)) + return 1; + + /* If the number of entries is empty, we consider it empty, too */ + n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)); + if (n < 0) + return -errno; + if (n != sizeof(n_entries)) + return -EIO; + + return le64toh(n_entries) <= 0; +} + +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; + struct vacuum_info *list = NULL; + 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 && n_max_files <= 0) + return 0; + + if (max_retention_usec > 0) { + retention_limit = now(CLOCK_REALTIME); + if (retention_limit > max_retention_usec) + retention_limit -= max_retention_usec; + else + max_retention_usec = retention_limit = 0; + } + + d = opendir(directory); + if (!d) + return -errno; + + 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; + + 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; + + q = strlen(de->d_name); + + if (endswith(de->d_name, ".journal")) { + + /* Vacuum archived files. Active files are + * left around */ + + 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] != '@') { + n_active_files++; + continue; + } + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + de->d_name[q-8-16-1-16-1] = 0; + if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { + n_active_files++; + continue; + } + + if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { + n_active_files++; + continue; + } + + have_seqnum = true; + + } else if (endswith(de->d_name, ".journal~")) { + unsigned long long tmp; + + /* Vacuum corrupted files */ + + 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] != '@') { + n_active_files++; + continue; + } + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { + n_active_files++; + continue; + } + + have_seqnum = false; + } else { + /* We do not vacuum unknown files! */ + log_debug("Not vacuuming unknown file %s.", de->d_name); + continue; + } + + 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)); + + freed += size; + } else if (errno != ENOENT) + log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); + + continue; + } + + patch_realtime(dirfd(d), p, &st, &realtime); + + if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { + r = -ENOMEM; + goto finish; + } + + list[n_list].filename = p; + 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; + 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) && + (n_max_files <= 0 || left <= n_max_files)) + break; + + if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)); + freed += list[i].usage; + + if (list[i].usage < sum) + sum -= list[i].usage; + else + sum = 0; + + } else if (errno != ENOENT) + log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename); + } + + 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); + free(list); + + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes, sizeof(sbytes), freed)); + + return r; +} diff --git a/src/libsystemd/src/sd-journal/journal-vacuum.h b/src/libsystemd/src/sd-journal/journal-vacuum.h new file mode 100644 index 0000000000..c7b38e2da9 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-vacuum.h @@ -0,0 +1,27 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/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/libsystemd/src/sd-journal/journal-verify.c b/src/libsystemd/src/sd-journal/journal-verify.c new file mode 100644 index 0000000000..bb890273b3 --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-verify.c @@ -0,0 +1,1309 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/terminal-util.h" +#include "basic/util.h" + +#include "compress.h" +#include "journal-authenticate.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-verify.h" +#include "lookup3.h" + +static void draw_progress(uint64_t p, usec_t *last_usec) { + unsigned n, i, j, k; + usec_t z, x; + + if (!on_tty()) + return; + + z = now(CLOCK_MONOTONIC); + x = *last_usec; + + if (x != 0 && x + 40 * USEC_PER_MSEC > z) + return; + + *last_usec = z; + + n = (3 * columns()) / 4; + j = (n * (unsigned) p) / 65535ULL; + k = n - j; + + fputs("\r", stdout); + if (colors_enabled()) + fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout); + + for (i = 0; i < j; i++) + fputs("\xe2\x96\x88", stdout); + + fputs(ANSI_NORMAL, stdout); + + for (i = 0; i < k; i++) + fputs("\xe2\x96\x91", stdout); + + printf(" %3"PRIu64"%%", 100U * p / 65535U); + + fputs("\r", stdout); + if (colors_enabled()) + fputs("\x1B[?25h", stdout); + + fflush(stdout); +} + +static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) { + + /* Calculates scale * p / m, but handles m == 0 safely, and saturates */ + + if (p >= m || m == 0) + return scale; + + return scale * p / m; +} + +static void flush_progress(void) { + unsigned n, i; + + if (!on_tty()) + return; + + n = (3 * columns()) / 4; + + putchar('\r'); + + for (i = 0; i < n + 5; i++) + putchar(' '); + + putchar('\r'); + fflush(stdout); +} + +#define debug(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ + } while (0) + +#define warning(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ + } while (0) + +#define error(_offset, _fmt, ...) do { \ + flush_progress(); \ + log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ + } while (0) + +static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { + uint64_t i; + + assert(f); + assert(offset); + assert(o); + + /* This does various superficial tests about the length an + * possible field values. It does not follow any references to + * other objects. */ + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && + o->object.type != OBJECT_DATA) { + error(offset, "Found compressed object that isn't of type DATA, which is not allowed."); + return -EBADMSG; + } + + switch (o->object.type) { + + case OBJECT_DATA: { + uint64_t h1, h2; + int compression, r; + + if (le64toh(o->data.entry_offset) == 0) + warning(offset, "Unused data (entry_offset==0)"); + + if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) { + error(offset, "Bad n_entries: %"PRIu64, o->data.n_entries); + return -EBADMSG; + } + + if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) { + error(offset, "Bad object size (<= %zu): %"PRIu64, + offsetof(DataObject, payload), + le64toh(o->object.size)); + return -EBADMSG; + } + + h1 = le64toh(o->data.hash); + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { + _cleanup_free_ void *b = NULL; + size_t alloc = 0, b_size; + + r = decompress_blob(compression, + o->data.payload, + le64toh(o->object.size) - offsetof(Object, data.payload), + &b, &alloc, &b_size, 0); + if (r < 0) { + error(offset, "%s decompression failed: %s", + object_compressed_to_string(compression), strerror(-r)); + return r; + } + + h2 = hash64(b, b_size); + } else + h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); + + if (h1 != h2) { + error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2); + return -EBADMSG; + } + + if (!VALID64(o->data.next_hash_offset) || + !VALID64(o->data.next_field_offset) || + !VALID64(o->data.entry_offset) || + !VALID64(o->data.entry_array_offset)) { + error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt, + o->data.next_hash_offset, + o->data.next_field_offset, + o->data.entry_offset, + o->data.entry_array_offset); + return -EBADMSG; + } + + break; + } + + case OBJECT_FIELD: + if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) { + error(offset, + "Bad field size (<= %zu): %"PRIu64, + offsetof(FieldObject, payload), + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID64(o->field.next_hash_offset) || + !VALID64(o->field.head_data_offset)) { + error(offset, + "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt, + o->field.next_hash_offset, + o->field.head_data_offset); + return -EBADMSG; + } + break; + + case OBJECT_ENTRY: + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) { + error(offset, + "Bad entry size (<= %zu): %"PRIu64, + offsetof(EntryObject, items), + le64toh(o->object.size)); + return -EBADMSG; + } + + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) { + error(offset, + "Invalid number items in entry: %"PRIu64, + (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem)); + return -EBADMSG; + } + + if (le64toh(o->entry.seqnum) <= 0) { + error(offset, + "Invalid entry seqnum: %"PRIx64, + le64toh(o->entry.seqnum)); + return -EBADMSG; + } + + if (!VALID_REALTIME(le64toh(o->entry.realtime))) { + error(offset, + "Invalid entry realtime timestamp: %"PRIu64, + le64toh(o->entry.realtime)); + return -EBADMSG; + } + + if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) { + error(offset, + "Invalid entry monotonic timestamp: %"PRIu64, + le64toh(o->entry.monotonic)); + return -EBADMSG; + } + + for (i = 0; i < journal_file_entry_n_items(o); i++) { + if (o->entry.items[i].object_offset == 0 || + !VALID64(o->entry.items[i].object_offset)) { + error(offset, + "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt, + i, journal_file_entry_n_items(o), + o->entry.items[i].object_offset); + return -EBADMSG; + } + } + + break; + + case OBJECT_DATA_HASH_TABLE: + case OBJECT_FIELD_HASH_TABLE: + if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || + (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) { + error(offset, + "Invalid %s hash table size: %"PRIu64, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + le64toh(o->object.size)); + return -EBADMSG; + } + + for (i = 0; i < journal_file_hash_table_n_items(o); i++) { + if (o->hash_table.items[i].head_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].head_hash_offset)); + return -EBADMSG; + } + if (o->hash_table.items[i].tail_hash_offset != 0 && + !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].tail_hash_offset)); + return -EBADMSG; + } + + if ((o->hash_table.items[i].head_hash_offset != 0) != + (o->hash_table.items[i].tail_hash_offset != 0)) { + error(offset, + "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + i, journal_file_hash_table_n_items(o), + le64toh(o->hash_table.items[i].head_hash_offset), + le64toh(o->hash_table.items[i].tail_hash_offset)); + return -EBADMSG; + } + } + + break; + + case OBJECT_ENTRY_ARRAY: + if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || + (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) { + error(offset, + "Invalid object entry array size: %"PRIu64, + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID64(o->entry_array.next_entry_array_offset)) { + error(offset, + "Invalid object entry array next_entry_array_offset: "OFSfmt, + o->entry_array.next_entry_array_offset); + return -EBADMSG; + } + + for (i = 0; i < journal_file_entry_array_n_items(o); i++) + if (le64toh(o->entry_array.items[i]) != 0 && + !VALID64(le64toh(o->entry_array.items[i]))) { + error(offset, + "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt, + i, journal_file_entry_array_n_items(o), + le64toh(o->entry_array.items[i])); + return -EBADMSG; + } + + break; + + case OBJECT_TAG: + if (le64toh(o->object.size) != sizeof(TagObject)) { + error(offset, + "Invalid object tag size: %"PRIu64, + le64toh(o->object.size)); + return -EBADMSG; + } + + if (!VALID_EPOCH(o->tag.epoch)) { + error(offset, + "Invalid object tag epoch: %"PRIu64, + o->tag.epoch); + return -EBADMSG; + } + + break; + } + + return 0; +} + +static int write_uint64(int fd, uint64_t p) { + ssize_t k; + + k = write(fd, &p, sizeof(p)); + if (k < 0) + return -errno; + if (k != sizeof(p)) + return -EIO; + + return 0; +} + +static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { + uint64_t a, b; + int r; + + assert(m); + assert(fd >= 0); + + /* Bisection ... */ + + a = 0; b = n; + while (a < b) { + uint64_t c, *z; + + c = (a + b) / 2; + + r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); + if (r < 0) + return r; + + if (*z == p) + return 1; + + if (a + 1 >= b) + return 0; + + if (p < *z) + b = c; + else + a = c; + } + + return 0; +} + +static int entry_points_to_data( + JournalFile *f, + int entry_fd, + uint64_t n_entries, + uint64_t entry_p, + uint64_t data_p) { + + int r; + uint64_t i, n, a; + Object *o; + bool found = false; + + assert(f); + assert(entry_fd >= 0); + + if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) { + error(data_p, "Data object references invalid entry at "OFSfmt, entry_p); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) + if (le64toh(o->entry.items[i].object_offset) == data_p) { + found = true; + break; + } + + if (!found) { + error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p); + return -EBADMSG; + } + + /* Check if this entry is also in main entry array. Since the + * main entry array has already been verified we can rely on + * its consistency. */ + + i = 0; + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + + while (i < n) { + uint64_t m, u; + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + m = journal_file_entry_array_n_items(o); + u = MIN(n - i, m); + + if (entry_p <= le64toh(o->entry_array.items[u-1])) { + uint64_t x, y, z; + + x = 0; + y = u; + + while (x < y) { + z = (x + y) / 2; + + if (le64toh(o->entry_array.items[z]) == entry_p) + return 0; + + if (x + 1 >= y) + break; + + if (entry_p < le64toh(o->entry_array.items[z])) + y = z; + else + x = z; + } + + error(entry_p, "Entry object doesn't exist in main entry array"); + return -EBADMSG; + } + + i += u; + a = le64toh(o->entry_array.next_entry_array_offset); + } + + return 0; +} + +static int verify_data( + JournalFile *f, + Object *o, uint64_t p, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays) { + + uint64_t i, n, a, last, q; + int r; + + assert(f); + assert(o); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + + n = le64toh(o->data.n_entries); + a = le64toh(o->data.entry_array_offset); + + /* Entry array means at least two objects */ + if (a && n < 2) { + error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n); + return -EBADMSG; + } + + if (n == 0) + return 0; + + /* We already checked that earlier */ + assert(o->data.entry_offset); + + last = q = le64toh(o->data.entry_offset); + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + i = 1; + while (i < n) { + uint64_t next, m, j; + + if (a == 0) { + error(p, "Array chain too short"); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + error(p, "Invalid array offset "OFSfmt, a); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + + q = le64toh(o->entry_array.items[j]); + if (q <= last) { + error(p, "Data object's entry array not sorted"); + return -EBADMSG; + } + last = q; + + r = entry_points_to_data(f, entry_fd, n_entries, q, p); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + +static int verify_hash_table( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec, + bool show_progress) { + + uint64_t i, n; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (n <= 0) + return 0; + + r = journal_file_map_data_hash_table(f); + if (r < 0) + return log_error_errno(r, "Failed to map data hash table: %m"); + + for (i = 0; i < n; i++) { + uint64_t last = 0, p; + + if (show_progress) + draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec); + + p = le64toh(f->data_hash_table[i].head_hash_offset); + while (p != 0) { + Object *o; + uint64_t next; + + if (!contains_uint64(f->mmap, data_fd, n_data, p)) { + error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + next = le64toh(o->data.next_hash_offset); + if (next != 0 && next <= p) { + error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + if (le64toh(o->data.hash) % n != i) { + error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays); + if (r < 0) + return r; + + last = p; + p = next; + } + + if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) { + error(p, "Tail hash pointer mismatch in hash table"); + return -EBADMSG; + } + } + + return 0; +} + +static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) { + uint64_t n, h, q; + int r; + assert(f); + + n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); + if (n <= 0) + return 0; + + r = journal_file_map_data_hash_table(f); + if (r < 0) + return log_error_errno(r, "Failed to map data hash table: %m"); + + h = hash % n; + + q = le64toh(f->data_hash_table[h].head_hash_offset); + while (q != 0) { + Object *o; + + if (p == q) + return 1; + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &o); + if (r < 0) + return r; + + q = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +static int verify_entry( + JournalFile *f, + Object *o, uint64_t p, + int data_fd, uint64_t n_data) { + + uint64_t i, n; + int r; + + assert(f); + assert(o); + assert(data_fd >= 0); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t q, h; + Object *u; + + q = le64toh(o->entry.items[i].object_offset); + h = le64toh(o->entry.items[i].hash); + + if (!contains_uint64(f->mmap, data_fd, n_data, q)) { + error(p, "Invalid data object of entry"); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_DATA, q, &u); + if (r < 0) + return r; + + if (le64toh(u->data.hash) != h) { + error(p, "Hash mismatch for data object of entry"); + return -EBADMSG; + } + + r = data_object_in_hash_table(f, h, q); + if (r < 0) + return r; + if (r == 0) { + error(p, "Data object missing from hash table"); + return -EBADMSG; + } + } + + return 0; +} + +static int verify_entry_array( + JournalFile *f, + int data_fd, uint64_t n_data, + int entry_fd, uint64_t n_entries, + int entry_array_fd, uint64_t n_entry_arrays, + usec_t *last_usec, + bool show_progress) { + + uint64_t i = 0, a, n, last = 0; + int r; + + assert(f); + assert(data_fd >= 0); + assert(entry_fd >= 0); + assert(entry_array_fd >= 0); + assert(last_usec); + + n = le64toh(f->header->n_entries); + a = le64toh(f->header->entry_array_offset); + while (i < n) { + uint64_t next, m, j; + Object *o; + + if (show_progress) + draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec); + + if (a == 0) { + error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + + next = le64toh(o->entry_array.next_entry_array_offset); + if (next != 0 && next <= a) { + error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next); + return -EBADMSG; + } + + m = journal_file_entry_array_n_items(o); + for (j = 0; i < n && j < m; i++, j++) { + uint64_t p; + + p = le64toh(o->entry_array.items[j]); + if (p <= last) { + error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + last = p; + + if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { + error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n); + return -EBADMSG; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); + if (r < 0) + return r; + + r = verify_entry(f, o, p, data_fd, n_data); + if (r < 0) + return r; + + /* Pointer might have moved, reposition */ + r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); + if (r < 0) + return r; + } + + a = next; + } + + return 0; +} + +int journal_file_verify( + JournalFile *f, + const char *key, + usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, + bool show_progress) { + int r; + Object *o; + uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0; + + uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + sd_id128_t entry_boot_id; + bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; + uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; + usec_t last_usec = 0; + int data_fd = -1, entry_fd = -1, entry_array_fd = -1; + unsigned i; + bool found_last = false; + _cleanup_free_ char *tmp_dir = NULL; + +#ifdef HAVE_GCRYPT + uint64_t last_tag = 0; +#endif + assert(f); + + if (key) { +#ifdef HAVE_GCRYPT + r = journal_file_parse_verification_key(f, key); + if (r < 0) { + log_error("Failed to parse seed."); + return r; + } +#else + return -EOPNOTSUPP; +#endif + } else if (f->seal) + return -ENOKEY; + + r = var_tmp(&tmp_dir); + if (r < 0) { + log_error_errno(r, "Failed to determine temporary directory: %m"); + goto fail; + } + + data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); + if (data_fd < 0) { + r = log_error_errno(data_fd, "Failed to create data file: %m"); + goto fail; + } + + entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); + if (entry_fd < 0) { + r = log_error_errno(entry_fd, "Failed to create entry file: %m"); + goto fail; + } + + entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC); + if (entry_array_fd < 0) { + r = log_error_errno(entry_array_fd, + "Failed to create entry array file: %m"); + goto fail; + } + + if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) { + log_error("Cannot verify file with unknown extensions."); + r = -EOPNOTSUPP; + goto fail; + } + + for (i = 0; i < sizeof(f->header->reserved); i++) + if (f->header->reserved[i] != 0) { + error(offsetof(Header, reserved[i]), "Reserved field is non-zero"); + r = -EBADMSG; + goto fail; + } + + /* First iteration: we go through all objects, verify the + * superficial structure, headers, hashes. */ + + p = le64toh(f->header->header_size); + for (;;) { + /* Early exit if there are no objects in the file, at all */ + if (le64toh(f->header->tail_object_offset) == 0) + break; + + if (show_progress) + draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec); + + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) { + error(p, "Invalid object"); + goto fail; + } + + if (p > le64toh(f->header->tail_object_offset)) { + error(offsetof(Header, tail_object_offset), "Invalid tail object pointer"); + r = -EBADMSG; + goto fail; + } + + n_objects++; + + r = journal_file_object_verify(f, p, o); + if (r < 0) { + error(p, "Invalid object contents: %s", strerror(-r)); + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && + (o->object.flags & OBJECT_COMPRESSED_LZ4)) { + error(p, "Objected with double compression"); + r = -EINVAL; + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) { + error(p, "XZ compressed object in file without XZ compression"); + r = -EBADMSG; + goto fail; + } + + if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) { + error(p, "LZ4 compressed object in file without LZ4 compression"); + r = -EBADMSG; + goto fail; + } + + switch (o->object.type) { + + case OBJECT_DATA: + r = write_uint64(data_fd, p); + if (r < 0) + goto fail; + + n_data++; + break; + + case OBJECT_FIELD: + n_fields++; + break; + + case OBJECT_ENTRY: + if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) { + error(p, "First entry before first tag"); + r = -EBADMSG; + goto fail; + } + + r = write_uint64(entry_fd, p); + if (r < 0) + goto fail; + + if (le64toh(o->entry.realtime) < last_tag_realtime) { + error(p, "Older entry after newer tag"); + r = -EBADMSG; + goto fail; + } + + if (!entry_seqnum_set && + le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { + error(p, "Head entry sequence number incorrect"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum >= le64toh(o->entry.seqnum)) { + error(p, "Entry sequence number out of synchronization"); + r = -EBADMSG; + goto fail; + } + + entry_seqnum = le64toh(o->entry.seqnum); + entry_seqnum_set = true; + + if (entry_monotonic_set && + sd_id128_equal(entry_boot_id, o->entry.boot_id) && + entry_monotonic > le64toh(o->entry.monotonic)) { + error(p, "Entry timestamp out of synchronization"); + r = -EBADMSG; + goto fail; + } + + entry_monotonic = le64toh(o->entry.monotonic); + entry_boot_id = o->entry.boot_id; + entry_monotonic_set = true; + + if (!entry_realtime_set && + le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { + error(p, "Head entry realtime timestamp incorrect"); + r = -EBADMSG; + goto fail; + } + + entry_realtime = le64toh(o->entry.realtime); + entry_realtime_set = true; + + n_entries++; + break; + + case OBJECT_DATA_HASH_TABLE: + if (n_data_hash_tables > 1) { + error(p, "More than one data hash table"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + error(p, "header fields for data hash table invalid"); + r = -EBADMSG; + goto fail; + } + + n_data_hash_tables++; + break; + + case OBJECT_FIELD_HASH_TABLE: + if (n_field_hash_tables > 1) { + error(p, "More than one field hash table"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + error(p, "Header fields for field hash table invalid"); + r = -EBADMSG; + goto fail; + } + + n_field_hash_tables++; + break; + + case OBJECT_ENTRY_ARRAY: + r = write_uint64(entry_array_fd, p); + if (r < 0) + goto fail; + + if (p == le64toh(f->header->entry_array_offset)) { + if (found_main_entry_array) { + error(p, "More than one main entry array"); + r = -EBADMSG; + goto fail; + } + + found_main_entry_array = true; + } + + n_entry_arrays++; + break; + + case OBJECT_TAG: + if (!JOURNAL_HEADER_SEALED(f->header)) { + error(p, "Tag object in file without sealing"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.seqnum) != n_tags + 1) { + error(p, "Tag sequence number out of synchronization"); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.epoch) < last_epoch) { + error(p, "Epoch sequence out of synchronization"); + r = -EBADMSG; + goto fail; + } + +#ifdef HAVE_GCRYPT + if (f->seal) { + uint64_t q, rt; + + debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum)); + + rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec; + if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) { + error(p, "tag/entry realtime timestamp out of synchronization"); + r = -EBADMSG; + goto fail; + } + + /* OK, now we know the epoch. So let's now set + * it, and calculate the HMAC for everything + * since the last tag. */ + r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch)); + if (r < 0) + goto fail; + + r = journal_file_hmac_start(f); + if (r < 0) + goto fail; + + if (last_tag == 0) { + r = journal_file_hmac_put_header(f); + if (r < 0) + goto fail; + + q = le64toh(f->header->header_size); + } else + q = last_tag; + + while (q <= p) { + r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o); + if (r < 0) + goto fail; + + r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q); + if (r < 0) + goto fail; + + q = q + ALIGN64(le64toh(o->object.size)); + } + + /* Position might have changed, let's reposition things */ + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o); + if (r < 0) + goto fail; + + if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) { + error(p, "Tag failed verification"); + r = -EBADMSG; + goto fail; + } + + f->hmac_running = false; + last_tag_realtime = rt; + last_sealed_realtime = entry_realtime; + } + + last_tag = p + ALIGN64(le64toh(o->object.size)); +#endif + + last_epoch = le64toh(o->tag.epoch); + + n_tags++; + break; + + default: + n_weird++; + } + + if (p == le64toh(f->header->tail_object_offset)) { + found_last = true; + break; + } + + p = p + ALIGN64(le64toh(o->object.size)); + }; + + if (!found_last && le64toh(f->header->tail_object_offset) != 0) { + error(le64toh(f->header->tail_object_offset), "Tail object pointer dead"); + r = -EBADMSG; + goto fail; + } + + if (n_objects != le64toh(f->header->n_objects)) { + error(offsetof(Header, n_objects), "Object number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_entries != le64toh(f->header->n_entries)) { + error(offsetof(Header, n_entries), "Entry number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + n_data != le64toh(f->header->n_data)) { + error(offsetof(Header, n_data), "Data number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + n_fields != le64toh(f->header->n_fields)) { + error(offsetof(Header, n_fields), "Field number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && + n_tags != le64toh(f->header->n_tags)) { + error(offsetof(Header, n_tags), "Tag number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) && + n_entry_arrays != le64toh(f->header->n_entry_arrays)) { + error(offsetof(Header, n_entry_arrays), "Entry array number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) { + error(0, "Missing entry array"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { + error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum"); + r = -EBADMSG; + goto fail; + } + + if (entry_monotonic_set && + (!sd_id128_equal(entry_boot_id, f->header->boot_id) || + entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { + error(0, "Invalid tail monotonic timestamp"); + r = -EBADMSG; + goto fail; + } + + if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { + error(0, "Invalid tail realtime timestamp"); + r = -EBADMSG; + goto fail; + } + + /* Second iteration: we follow all objects referenced from the + * two entry points: the object hash table and the entry + * array. We also check that everything referenced (directly + * or indirectly) in the data hash table also exists in the + * entry array, and vice versa. Note that we do not care for + * unreferenced objects. We only care that everything that is + * referenced is consistent. */ + + r = verify_entry_array(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec, + show_progress); + if (r < 0) + goto fail; + + r = verify_hash_table(f, + data_fd, n_data, + entry_fd, n_entries, + entry_array_fd, n_entry_arrays, + &last_usec, + show_progress); + if (r < 0) + goto fail; + + if (show_progress) + flush_progress(); + + mmap_cache_close_fd(f->mmap, data_fd); + mmap_cache_close_fd(f->mmap, entry_fd); + mmap_cache_close_fd(f->mmap, entry_array_fd); + + safe_close(data_fd); + safe_close(entry_fd); + safe_close(entry_array_fd); + + if (first_contained) + *first_contained = le64toh(f->header->head_entry_realtime); + if (last_validated) + *last_validated = last_sealed_realtime; + if (last_contained) + *last_contained = le64toh(f->header->tail_entry_realtime); + + return 0; + +fail: + if (show_progress) + flush_progress(); + + log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).", + f->path, + p, + (unsigned long long) f->last_stat.st_size, + 100 * p / f->last_stat.st_size); + + if (data_fd >= 0) { + mmap_cache_close_fd(f->mmap, data_fd); + safe_close(data_fd); + } + + if (entry_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_fd); + safe_close(entry_fd); + } + + if (entry_array_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_array_fd); + safe_close(entry_array_fd); + } + + return r; +} diff --git a/src/libsystemd/src/sd-journal/journal-verify.h b/src/libsystemd/src/sd-journal/journal-verify.h new file mode 100644 index 0000000000..8f0eaf6daa --- /dev/null +++ b/src/libsystemd/src/sd-journal/journal-verify.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journal-file.h" + +int journal_file_verify(JournalFile *f, const char *key, usec_t *first_contained, usec_t *last_validated, usec_t *last_contained, bool show_progress); diff --git a/src/libsystemd/src/sd-journal/lookup3.c b/src/libsystemd/src/sd-journal/lookup3.c new file mode 100644 index 0000000000..3d791234f4 --- /dev/null +++ b/src/libsystemd/src/sd-journal/lookup3.c @@ -0,0 +1,1009 @@ +/* Slightly modified by Lennart Poettering, to avoid name clashes, and + * unexport a few functions. */ + +#include "lookup3.h" + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +/* #define SELF_TEST 1 */ + +#include /* defines uint32_t etc */ +#include /* defines printf for tests */ +#include /* attempt to define endianness */ +#include /* defines time_t for timings in the test */ +#ifdef linux +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t jenkins_hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void jenkins_hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + { + const uint8_t *k8 = (const uint8_t *) k; + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void jenkins_hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + { + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticeably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + { + const uint8_t *k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + uint8_t buf[256]; + uint32_t i; + uint32_t h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + uint32_t x[HASHSTATE],y[HASHSTATE]; + uint32_t hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; lz) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + uint8_t buf[MAXLEN+20], *b; + uint32_t len; + uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; + uint32_t h; + uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; + uint32_t i; + uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + uint32_t j; + uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + uint32_t ref,x,y; + uint8_t *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + + /* check that hashlittle2 and hashlittle produce the same results */ + i=47; j=0; + hashlittle2(q, sizeof(q), &i, &j); + if (hashlittle(q, sizeof(q), 47) != i) + printf("hashlittle2 and hashlittle mismatch\n"); + + /* check that hashword2 and hashword produce the same results */ + len = 0xdeadbeef; + i=47, j=0; + hashword2(&len, 1, &i, &j); + if (hashword(&len, 1, 47) != i) + printf("hashword2 and hashword mismatch %x %x\n", + i, hashword(&len, 1, 47)); + + /* check hashlittle doesn't read before or after the ends of the string */ + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i +#include + +#include "basic/macro.h" + +uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval) _pure_; +void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval) _pure_; +void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_; + +static inline uint64_t hash64(const void *data, size_t length) { + uint32_t a = 0, b = 0; + + jenkins_hashlittle2(data, length, &a, &b); + + return ((uint64_t) a << 32ULL) | (uint64_t) b; +} diff --git a/src/libsystemd/src/sd-journal/mmap-cache.c b/src/libsystemd/src/sd-journal/mmap-cache.c new file mode 100644 index 0000000000..bda21aed05 --- /dev/null +++ b/src/libsystemd/src/sd-journal/mmap-cache.c @@ -0,0 +1,726 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/list.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/sigbus.h" +#include "basic/util.h" + +#include "mmap-cache.h" + +typedef struct Window Window; +typedef struct Context Context; +typedef struct FileDescriptor FileDescriptor; + +struct Window { + MMapCache *cache; + + bool invalidated:1; + bool keep_always:1; + bool in_unused:1; + + int prot; + void *ptr; + uint64_t offset; + size_t size; + + FileDescriptor *fd; + + LIST_FIELDS(Window, by_fd); + LIST_FIELDS(Window, unused); + + LIST_HEAD(Context, contexts); +}; + +struct Context { + MMapCache *cache; + unsigned id; + Window *window; + + LIST_FIELDS(Context, by_window); +}; + +struct FileDescriptor { + MMapCache *cache; + int fd; + bool sigbus; + LIST_HEAD(Window, windows); +}; + +struct MMapCache { + int n_ref; + unsigned n_windows; + + unsigned n_hit, n_missed; + + Hashmap *fds; + Context *contexts[MMAP_CACHE_MAX_CONTEXTS]; + + LIST_HEAD(Window, unused); + Window *last_unused; +}; + +#define WINDOWS_MIN 64 + +#ifdef ENABLE_DEBUG_MMAP_CACHE +/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */ +# define WINDOW_SIZE (page_size()) +#else +# define WINDOW_SIZE (8ULL*1024ULL*1024ULL) +#endif + +MMapCache* mmap_cache_new(void) { + MMapCache *m; + + m = new0(MMapCache, 1); + if (!m) + return NULL; + + m->n_ref = 1; + return m; +} + +MMapCache* mmap_cache_ref(MMapCache *m) { + assert(m); + assert(m->n_ref > 0); + + m->n_ref++; + return m; +} + +static void window_unlink(Window *w) { + Context *c; + + assert(w); + + if (w->ptr) + munmap(w->ptr, w->size); + + if (w->fd) + LIST_REMOVE(by_fd, w->fd->windows, w); + + if (w->in_unused) { + if (w->cache->last_unused == w) + w->cache->last_unused = w->unused_prev; + + LIST_REMOVE(unused, w->cache->unused, w); + } + + LIST_FOREACH(by_window, c, w->contexts) { + assert(c->window == w); + c->window = NULL; + } +} + +static void window_invalidate(Window *w) { + assert(w); + + if (w->invalidated) + return; + + /* Replace the window with anonymous pages. This is useful + * when we hit a SIGBUS and want to make sure the file cannot + * trigger any further SIGBUS, possibly overrunning the sigbus + * queue. */ + + assert_se(mmap(w->ptr, w->size, w->prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == w->ptr); + w->invalidated = true; +} + +static void window_free(Window *w) { + assert(w); + + window_unlink(w); + w->cache->n_windows--; + free(w); +} + +_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { + assert(w); + assert(fd >= 0); + assert(size > 0); + + return + w->fd && + fd == w->fd->fd && + prot == w->prot && + offset >= w->offset && + offset + size <= w->offset + w->size; +} + +static Window *window_add(MMapCache *m, FileDescriptor *fd, int prot, bool keep_always, uint64_t offset, size_t size, void *ptr) { + Window *w; + + assert(m); + assert(fd); + + if (!m->last_unused || m->n_windows <= WINDOWS_MIN) { + + /* Allocate a new window */ + w = new0(Window, 1); + if (!w) + return NULL; + m->n_windows++; + } else { + + /* Reuse an existing one */ + w = m->last_unused; + window_unlink(w); + zero(*w); + } + + w->cache = m; + w->fd = fd; + w->prot = prot; + w->keep_always = keep_always; + w->offset = offset; + w->size = size; + w->ptr = ptr; + + LIST_PREPEND(by_fd, fd->windows, w); + + return w; +} + +static void context_detach_window(Context *c) { + Window *w; + + assert(c); + + if (!c->window) + return; + + w = c->window; + c->window = NULL; + LIST_REMOVE(by_window, w->contexts, c); + + if (!w->contexts && !w->keep_always) { + /* Not used anymore? */ +#ifdef ENABLE_DEBUG_MMAP_CACHE + /* Unmap unused windows immediately to expose use-after-unmap + * by SIGSEGV. */ + window_free(w); +#else + LIST_PREPEND(unused, c->cache->unused, w); + if (!c->cache->last_unused) + c->cache->last_unused = w; + + w->in_unused = true; +#endif + } +} + +static void context_attach_window(Context *c, Window *w) { + assert(c); + assert(w); + + if (c->window == w) + return; + + context_detach_window(c); + + if (w->in_unused) { + /* Used again? */ + LIST_REMOVE(unused, c->cache->unused, w); + if (c->cache->last_unused == w) + c->cache->last_unused = w->unused_prev; + + w->in_unused = false; + } + + c->window = w; + LIST_PREPEND(by_window, w->contexts, c); +} + +static Context *context_add(MMapCache *m, unsigned id) { + Context *c; + + assert(m); + + c = m->contexts[id]; + if (c) + return c; + + c = new0(Context, 1); + if (!c) + return NULL; + + c->cache = m; + c->id = id; + + assert(!m->contexts[id]); + m->contexts[id] = c; + + return c; +} + +static void context_free(Context *c) { + assert(c); + + context_detach_window(c); + + if (c->cache) { + assert(c->cache->contexts[c->id] == c); + c->cache->contexts[c->id] = NULL; + } + + free(c); +} + +static void fd_free(FileDescriptor *f) { + assert(f); + + while (f->windows) + window_free(f->windows); + + if (f->cache) + assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); + + free(f); +} + +static FileDescriptor* fd_add(MMapCache *m, int fd) { + FileDescriptor *f; + int r; + + assert(m); + assert(fd >= 0); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (f) + return f; + + r = hashmap_ensure_allocated(&m->fds, NULL); + if (r < 0) + return NULL; + + f = new0(FileDescriptor, 1); + if (!f) + return NULL; + + f->cache = m; + f->fd = fd; + + r = hashmap_put(m->fds, FD_TO_PTR(fd), f); + if (r < 0) { + free(f); + return NULL; + } + + return f; +} + +static void mmap_cache_free(MMapCache *m) { + FileDescriptor *f; + int i; + + assert(m); + + for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++) + if (m->contexts[i]) + context_free(m->contexts[i]); + + while ((f = hashmap_first(m->fds))) + fd_free(f); + + hashmap_free(m->fds); + + while (m->unused) + window_free(m->unused); + + free(m); +} + +MMapCache* mmap_cache_unref(MMapCache *m) { + + if (!m) + return NULL; + + assert(m->n_ref > 0); + + m->n_ref--; + if (m->n_ref == 0) + mmap_cache_free(m); + + return NULL; +} + +static int make_room(MMapCache *m) { + assert(m); + + if (!m->last_unused) + return 0; + + window_free(m->last_unused); + return 1; +} + +static int try_context( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + void **ret) { + + Context *c; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + + c = m->contexts[context]; + if (!c) + return 0; + + assert(c->id == context); + + if (!c->window) + return 0; + + if (!window_matches(c->window, fd, prot, offset, size)) { + + /* Drop the reference to the window, since it's unnecessary now */ + context_detach_window(c); + return 0; + } + + if (c->window->fd->sigbus) + return -EIO; + + c->window->keep_always = c->window->keep_always || keep_always; + + *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); + return 1; +} + +static int find_mmap( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + void **ret) { + + FileDescriptor *f; + Window *w; + Context *c; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return 0; + + assert(f->fd == fd); + + if (f->sigbus) + return -EIO; + + LIST_FOREACH(by_fd, w, f->windows) + if (window_matches(w, fd, prot, offset, size)) + break; + + if (!w) + return 0; + + c = context_add(m, context); + if (!c) + return -ENOMEM; + + context_attach_window(c, w); + w->keep_always = w->keep_always || keep_always; + + *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; +} + +static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags, uint64_t offset, size_t size, void **res) { + void *ptr; + + assert(m); + assert(fd >= 0); + assert(res); + + for (;;) { + int r; + + ptr = mmap(addr, size, prot, flags, fd, offset); + if (ptr != MAP_FAILED) + break; + if (errno != ENOMEM) + return negative_errno(); + + r = make_room(m); + if (r < 0) + return r; + if (r == 0) + return -ENOMEM; + } + + *res = ptr; + return 0; +} + +static int add_mmap( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret) { + + uint64_t woffset, wsize; + Context *c; + FileDescriptor *f; + Window *w; + void *d; + int r; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); + wsize = PAGE_ALIGN(wsize); + + if (wsize < WINDOW_SIZE) { + uint64_t delta; + + delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2); + + if (delta > offset) + woffset = 0; + else + woffset -= delta; + + wsize = WINDOW_SIZE; + } + + if (st) { + /* Memory maps that are larger then the files + underneath have undefined behavior. Hence, clamp + things to the file size if we know it */ + + if (woffset >= (uint64_t) st->st_size) + return -EADDRNOTAVAIL; + + if (woffset + wsize > (uint64_t) st->st_size) + wsize = PAGE_ALIGN(st->st_size - woffset); + } + + r = mmap_try_harder(m, NULL, fd, prot, MAP_SHARED, woffset, wsize, &d); + if (r < 0) + return r; + + c = context_add(m, context); + if (!c) + goto outofmem; + + f = fd_add(m, fd); + if (!f) + goto outofmem; + + w = window_add(m, f, prot, keep_always, woffset, wsize, d); + if (!w) + goto outofmem; + + context_detach_window(c); + c->window = w; + LIST_PREPEND(by_window, w->contexts, c); + + *ret = (uint8_t*) w->ptr + (offset - w->offset); + return 1; + +outofmem: + (void) munmap(d, wsize); + return -ENOMEM; +} + +int mmap_cache_get( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret) { + + int r; + + assert(m); + assert(m->n_ref > 0); + assert(fd >= 0); + assert(size > 0); + assert(ret); + assert(context < MMAP_CACHE_MAX_CONTEXTS); + + /* Check whether the current context is the right one already */ + r = try_context(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit++; + return r; + } + + /* Search for a matching mmap */ + r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); + if (r != 0) { + m->n_hit++; + return r; + } + + m->n_missed++; + + /* Create a new mmap */ + return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret); +} + +unsigned mmap_cache_get_hit(MMapCache *m) { + assert(m); + + return m->n_hit; +} + +unsigned mmap_cache_get_missed(MMapCache *m) { + assert(m); + + return m->n_missed; +} + +static void mmap_cache_process_sigbus(MMapCache *m) { + bool found = false; + FileDescriptor *f; + Iterator i; + int r; + + assert(m); + + /* Iterate through all triggered pages and mark their files as + * invalidated */ + for (;;) { + bool ours; + void *addr; + + r = sigbus_pop(&addr); + if (_likely_(r == 0)) + break; + if (r < 0) { + log_error_errno(r, "SIGBUS handling failed: %m"); + abort(); + } + + ours = false; + HASHMAP_FOREACH(f, m->fds, i) { + Window *w; + + LIST_FOREACH(by_fd, w, f->windows) { + if ((uint8_t*) addr >= (uint8_t*) w->ptr && + (uint8_t*) addr < (uint8_t*) w->ptr + w->size) { + found = ours = f->sigbus = true; + break; + } + } + + if (ours) + break; + } + + /* Didn't find a matching window, give up */ + if (!ours) { + log_error("Unknown SIGBUS page, aborting."); + abort(); + } + } + + /* The list of triggered pages is now empty. Now, let's remap + * all windows of the triggered file to anonymous maps, so + * that no page of the file in question is triggered again, so + * that we can be sure not to hit the queue size limit. */ + if (_likely_(!found)) + return; + + HASHMAP_FOREACH(f, m->fds, i) { + Window *w; + + if (!f->sigbus) + continue; + + LIST_FOREACH(by_fd, w, f->windows) + window_invalidate(w); + } +} + +bool mmap_cache_got_sigbus(MMapCache *m, int fd) { + FileDescriptor *f; + + assert(m); + assert(fd >= 0); + + mmap_cache_process_sigbus(m); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return false; + + return f->sigbus; +} + +void mmap_cache_close_fd(MMapCache *m, int fd) { + FileDescriptor *f; + + assert(m); + assert(fd >= 0); + + /* Make sure that any queued SIGBUS are first dispatched, so + * that we don't end up with a SIGBUS entry we cannot relate + * to any existing memory map */ + + mmap_cache_process_sigbus(m); + + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (!f) + return; + + fd_free(f); +} diff --git a/src/libsystemd/src/sd-journal/mmap-cache.h b/src/libsystemd/src/sd-journal/mmap-cache.h new file mode 100644 index 0000000000..199d944647 --- /dev/null +++ b/src/libsystemd/src/sd-journal/mmap-cache.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +/* One context per object type, plus one of the header, plus one "additional" one */ +#define MMAP_CACHE_MAX_CONTEXTS 9 + +typedef struct MMapCache MMapCache; + +MMapCache* mmap_cache_new(void); +MMapCache* mmap_cache_ref(MMapCache *m); +MMapCache* mmap_cache_unref(MMapCache *m); + +int mmap_cache_get( + MMapCache *m, + int fd, + int prot, + unsigned context, + bool keep_always, + uint64_t offset, + size_t size, + struct stat *st, + void **ret); +void mmap_cache_close_fd(MMapCache *m, int fd); + +unsigned mmap_cache_get_hit(MMapCache *m); +unsigned mmap_cache_get_missed(MMapCache *m); + +bool mmap_cache_got_sigbus(MMapCache *m, int fd); diff --git a/src/libsystemd/src/sd-journal/sd-journal.c b/src/libsystemd/src/sd-journal/sd-journal.c new file mode 100644 index 0000000000..1a4b0c6418 --- /dev/null +++ b/src/libsystemd/src/sd-journal/sd-journal.c @@ -0,0 +1,2987 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hashmap.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/list.h" +#include "basic/missing.h" +#include "basic/path-util.h" +#include "basic/replace-var.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" + +#include "catalog.h" +#include "compress.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-internal.h" +#include "lookup3.h" + +#define JOURNAL_FILES_MAX 7168 + +#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC) + +#define REPLACE_VAR_MAX 256 + +#define DEFAULT_DATA_THRESHOLD (64*1024) + +static void remove_file_real(sd_journal *j, JournalFile *f); + +static bool journal_pid_changed(sd_journal *j) { + assert(j); + + /* We don't support people creating a journal object and + * keeping it around over a fork(). Let's complain. */ + + return j->original_pid != getpid(); +} + +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 = hashmap_ensure_allocated(&j->errors, NULL); + if (k < 0) + return k; + + 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) { + Iterator i; + JournalFile *f; + + assert(j); + + j->current_file = NULL; + j->current_field = 0; + + ORDERED_HASHMAP_FOREACH(f, j->files, i) + journal_file_reset_location(f); +} + +static void reset_location(sd_journal *j) { + assert(j); + + detach_location(j); + zero(j->current_location); +} + +static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) { + assert(l); + assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK); + assert(f); + assert(o->object.type == OBJECT_ENTRY); + + l->type = type; + l->seqnum = le64toh(o->entry.seqnum); + l->seqnum_id = f->header->seqnum_id; + l->realtime = le64toh(o->entry.realtime); + l->monotonic = le64toh(o->entry.monotonic); + l->boot_id = o->entry.boot_id; + l->xor_hash = le64toh(o->entry.xor_hash); + + l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true; +} + +static void set_location(sd_journal *j, JournalFile *f, Object *o) { + assert(j); + assert(f); + assert(o); + + init_location(&j->current_location, LOCATION_DISCRETE, f, o); + + j->current_file = f; + j->current_field = 0; + + /* Let f know its candidate entry was picked. */ + assert(f->location_type == LOCATION_SEEK); + f->location_type = LOCATION_DISCRETE; +} + +static int match_is_valid(const void *data, size_t size) { + const char *b, *p; + + assert(data); + + if (size < 2) + return false; + + if (startswith(data, "__")) + return false; + + b = data; + for (p = b; p < b + size; p++) { + + if (*p == '=') + return p > b; + + if (*p == '_') + continue; + + if (*p >= 'A' && *p <= 'Z') + continue; + + if (*p >= '0' && *p <= '9') + continue; + + return false; + } + + return false; +} + +static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { + const uint8_t *a = _a, *b = _b; + size_t j; + + for (j = 0; j < s && j < t; j++) { + + if (a[j] != b[j]) + return false; + + if (a[j] == '=') + return true; + } + + assert_not_reached("\"=\" not found"); +} + +static Match *match_new(Match *p, MatchType t) { + Match *m; + + m = new0(Match, 1); + if (!m) + return NULL; + + m->type = t; + + if (p) { + m->parent = p; + LIST_PREPEND(matches, p->matches, m); + } + + return m; +} + +static void match_free(Match *m) { + assert(m); + + while (m->matches) + match_free(m->matches); + + if (m->parent) + LIST_REMOVE(matches, m->parent->matches, m); + + free(m->data); + free(m); +} + +static void match_free_if_empty(Match *m) { + if (!m || m->matches) + return; + + match_free(m); +} + +_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { + Match *l3, *l4, *add_here = NULL, *m; + le64_t le_hash; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + + if (size == 0) + size = strlen(data); + + assert_return(match_is_valid(data, size), -EINVAL); + + /* level 0: AND term + * level 1: OR terms + * level 2: AND terms + * level 3: OR terms + * level 4: concrete matches */ + + if (!j->level0) { + j->level0 = match_new(NULL, MATCH_AND_TERM); + if (!j->level0) + return -ENOMEM; + } + + if (!j->level1) { + j->level1 = match_new(j->level0, MATCH_OR_TERM); + if (!j->level1) + return -ENOMEM; + } + + if (!j->level2) { + j->level2 = match_new(j->level1, MATCH_AND_TERM); + if (!j->level2) + return -ENOMEM; + } + + assert(j->level0->type == MATCH_AND_TERM); + assert(j->level1->type == MATCH_OR_TERM); + assert(j->level2->type == MATCH_AND_TERM); + + le_hash = htole64(hash64(data, size)); + + LIST_FOREACH(matches, l3, j->level2->matches) { + assert(l3->type == MATCH_OR_TERM); + + LIST_FOREACH(matches, l4, l3->matches) { + assert(l4->type == MATCH_DISCRETE); + + /* Exactly the same match already? Then ignore + * this addition */ + if (l4->le_hash == le_hash && + l4->size == size && + memcmp(l4->data, data, size) == 0) + return 0; + + /* Same field? Then let's add this to this OR term */ + if (same_field(data, size, l4->data, l4->size)) { + add_here = l3; + break; + } + } + + if (add_here) + break; + } + + if (!add_here) { + add_here = match_new(j->level2, MATCH_OR_TERM); + if (!add_here) + goto fail; + } + + m = match_new(add_here, MATCH_DISCRETE); + if (!m) + goto fail; + + m->le_hash = le_hash; + m->size = size; + m->data = memdup(data, size); + if (!m->data) + goto fail; + + detach_location(j); + + return 0; + +fail: + match_free_if_empty(add_here); + match_free_if_empty(j->level2); + match_free_if_empty(j->level1); + match_free_if_empty(j->level0); + + return -ENOMEM; +} + +_public_ int sd_journal_add_conjunction(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level1->matches) + return 0; + + j->level1 = NULL; + j->level2 = NULL; + + return 0; +} + +_public_ int sd_journal_add_disjunction(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level2) + return 0; + + if (!j->level2->matches) + return 0; + + j->level2 = NULL; + return 0; +} + +static char *match_make_string(Match *m) { + char *p, *r; + Match *i; + bool enclose = false; + + if (!m) + return strdup("none"); + + if (m->type == MATCH_DISCRETE) + return strndup(m->data, m->size); + + p = NULL; + LIST_FOREACH(matches, i, m->matches) { + char *t, *k; + + t = match_make_string(i); + if (!t) { + free(p); + return NULL; + } + + if (p) { + k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL); + free(p); + free(t); + + if (!k) + return NULL; + + p = k; + + enclose = true; + } else + p = t; + } + + if (enclose) { + r = strjoin("(", p, ")", NULL); + free(p); + return r; + } + + return p; +} + +char *journal_make_match_string(sd_journal *j) { + assert(j); + + return match_make_string(j->level0); +} + +_public_ void sd_journal_flush_matches(sd_journal *j) { + if (!j) + return; + + if (j->level0) + match_free(j->level0); + + j->level0 = j->level1 = j->level2 = NULL; + + detach_location(j); +} + +_pure_ static int compare_with_location(JournalFile *f, Location *l) { + assert(f); + assert(l); + assert(f->location_type == LOCATION_SEEK); + assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK); + + if (l->monotonic_set && + sd_id128_equal(f->current_boot_id, l->boot_id) && + l->realtime_set && + f->current_realtime == l->realtime && + l->xor_hash_set && + f->current_xor_hash == l->xor_hash) + return 0; + + if (l->seqnum_set && + sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) { + + if (f->current_seqnum < l->seqnum) + return -1; + if (f->current_seqnum > l->seqnum) + return 1; + } + + if (l->monotonic_set && + sd_id128_equal(f->current_boot_id, l->boot_id)) { + + if (f->current_monotonic < l->monotonic) + return -1; + if (f->current_monotonic > l->monotonic) + return 1; + } + + if (l->realtime_set) { + + if (f->current_realtime < l->realtime) + return -1; + if (f->current_realtime > l->realtime) + return 1; + } + + if (l->xor_hash_set) { + + if (f->current_xor_hash < l->xor_hash) + return -1; + if (f->current_xor_hash > l->xor_hash) + return 1; + } + + return 0; +} + +static int next_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + uint64_t after_offset, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + uint64_t np = 0; + Object *n; + + assert(j); + assert(m); + assert(f); + + if (m->type == MATCH_DISCRETE) { + uint64_t dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + if (r <= 0) + return r; + + return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset); + + } else if (m->type == MATCH_OR_TERM) { + Match *i; + + /* Find the earliest match beyond after_offset */ + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = next_for_match(j, i, f, after_offset, direction, NULL, &cp); + if (r < 0) + return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np)) + np = cp; + } + } + + if (np == 0) + return 0; + + } else if (m->type == MATCH_AND_TERM) { + Match *i, *last_moved; + + /* Always jump to the next matching entry and repeat + * this until we find an offset that matches for all + * matches. */ + + if (!m->matches) + return 0; + + r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np); + if (r <= 0) + return r; + + assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset); + last_moved = m->matches; + + LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) { + uint64_t cp; + + r = next_for_match(j, i, f, np, direction, NULL, &cp); + if (r <= 0) + return r; + + assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np); + if (direction == DIRECTION_DOWN ? cp > np : cp < np) { + np = cp; + last_moved = i; + } + } + } + + assert(np > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; + + if (ret) + *ret = n; + if (offset) + *offset = np; + + return 1; +} + +static int find_location_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + + assert(j); + assert(m); + assert(f); + + if (m->type == MATCH_DISCRETE) { + uint64_t dp; + + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); + if (r <= 0) + return r; + + /* FIXME: missing: find by monotonic */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; + } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset); + + return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset); + + } else if (m->type == MATCH_OR_TERM) { + uint64_t np = 0; + Object *n; + Match *i; + + /* Find the earliest match */ + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r < 0) + return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) + np = cp; + } + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; + + if (ret) + *ret = n; + if (offset) + *offset = np; + + return 1; + + } else { + Match *i; + uint64_t np = 0; + + assert(m->type == MATCH_AND_TERM); + + /* First jump to the last match, and then find the + * next one where all matches match */ + + if (!m->matches) + return 0; + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r <= 0) + return r; + + if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np)) + np = cp; + } + + return next_for_match(j, m, f, np, direction, ret, offset); + } +} + +static int find_location_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + + assert(j); + assert(f); + assert(ret); + assert(offset); + + if (!j->level0) { + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; + } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset); + + return journal_file_next_entry(f, 0, direction, ret, offset); + } else + return find_location_for_match(j, j->level0, f, direction, ret, offset); +} + +static int next_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + assert(j); + assert(f); + assert(ret); + assert(offset); + + /* No matches is easy. We simple advance the file + * pointer by one. */ + if (!j->level0) + return journal_file_next_entry(f, f->current_offset, direction, ret, offset); + + /* If we have a match then we look for the next matching entry + * with an offset at least one step larger */ + return next_for_match(j, j->level0, f, + direction == DIRECTION_DOWN ? f->current_offset + 1 + : f->current_offset - 1, + direction, ret, offset); +} + +static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) { + Object *c; + uint64_t cp, n_entries; + int r; + + assert(j); + assert(f); + + n_entries = le64toh(f->header->n_entries); + + /* If we hit EOF before, we don't need to look into this file again + * unless direction changed or new entries appeared. */ + if (f->last_direction == direction && f->location_type == LOCATION_TAIL && + n_entries == f->last_n_entries) + return 0; + + f->last_n_entries = n_entries; + + if (f->last_direction == direction && f->current_offset > 0) { + /* LOCATION_SEEK here means we did the work in a previous + * iteration and the current location already points to a + * candidate entry. */ + if (f->location_type != LOCATION_SEEK) { + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } + } else { + f->last_direction = direction; + + r = find_location_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } + + /* OK, we found the spot, now let's advance until an entry + * that is actually different from what we were previously + * looking at. This is necessary to handle entries which exist + * in two (or more) journal files, and which shall all be + * suppressed but one. */ + + for (;;) { + bool found; + + if (j->current_location.type == LOCATION_DISCRETE) { + int k; + + k = compare_with_location(f, &j->current_location); + + found = direction == DIRECTION_DOWN ? k > 0 : k < 0; + } else + found = true; + + if (found) + return 1; + + r = next_with_matches(j, f, direction, &c, &cp); + if (r <= 0) + return r; + + journal_file_save_location(f, c, cp); + } +} + +static int real_journal_next(sd_journal *j, direction_t direction) { + JournalFile *f, *new_file = NULL; + Iterator i; + Object *o; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + bool found; + + r = next_beyond_location(j, f, direction); + if (r < 0) { + log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path); + remove_file_real(j, f); + continue; + } else if (r == 0) { + f->location_type = LOCATION_TAIL; + continue; + } + + if (!new_file) + found = true; + else { + int k; + + k = journal_file_compare_locations(f, new_file); + + found = direction == DIRECTION_DOWN ? k < 0 : k > 0; + } + + if (found) + new_file = f; + } + + if (!new_file) + return 0; + + r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o); + if (r < 0) + return r; + + set_location(j, new_file, o); + + return 1; +} + +_public_ int sd_journal_next(sd_journal *j) { + return real_journal_next(j, DIRECTION_DOWN); +} + +_public_ int sd_journal_previous(sd_journal *j) { + return real_journal_next(j, DIRECTION_UP); +} + +static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) { + int c = 0, r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (skip == 0) { + /* If this is not a discrete skip, then at least + * resolve the current location */ + if (j->current_location.type != LOCATION_DISCRETE) + return real_journal_next(j, direction); + + return 0; + } + + do { + r = real_journal_next(j, direction); + if (r < 0) + return r; + + if (r == 0) + return c; + + skip--; + c++; + } while (skip > 0); + + return c; +} + +_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_DOWN, skip); +} + +_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) { + return real_journal_next_skip(j, DIRECTION_UP, skip); +} + +_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { + Object *o; + int r; + char bid[33], sid[33]; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(cursor, -EINVAL); + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + sd_id128_to_string(j->current_file->header->seqnum_id, sid); + sd_id128_to_string(o->entry.boot_id, bid); + + if (asprintf(cursor, + "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64, + sid, le64toh(o->entry.seqnum), + bid, le64toh(o->entry.monotonic), + le64toh(o->entry.realtime), + le64toh(o->entry.xor_hash)) < 0) + return -ENOMEM; + + return 0; +} + +_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { + const char *word, *state; + size_t l; + unsigned long long seqnum, monotonic, realtime, xor_hash; + bool + seqnum_id_set = false, + seqnum_set = false, + boot_id_set = false, + monotonic_set = false, + realtime_set = false, + xor_hash_set = false; + sd_id128_t seqnum_id, boot_id; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); + + FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { + char *item; + int k = 0; + + if (l < 2 || word[1] != '=') + return -EINVAL; + + item = strndup(word, l); + if (!item) + return -ENOMEM; + + switch (word[0]) { + + case 's': + seqnum_id_set = true; + k = sd_id128_from_string(item+2, &seqnum_id); + break; + + case 'i': + seqnum_set = true; + if (sscanf(item+2, "%llx", &seqnum) != 1) + k = -EINVAL; + break; + + case 'b': + boot_id_set = true; + k = sd_id128_from_string(item+2, &boot_id); + break; + + case 'm': + monotonic_set = true; + if (sscanf(item+2, "%llx", &monotonic) != 1) + k = -EINVAL; + break; + + case 't': + realtime_set = true; + if (sscanf(item+2, "%llx", &realtime) != 1) + k = -EINVAL; + break; + + case 'x': + xor_hash_set = true; + if (sscanf(item+2, "%llx", &xor_hash) != 1) + k = -EINVAL; + break; + } + + free(item); + + if (k < 0) + return k; + } + + if ((!seqnum_set || !seqnum_id_set) && + (!monotonic_set || !boot_id_set) && + !realtime_set) + return -EINVAL; + + reset_location(j); + + j->current_location.type = LOCATION_SEEK; + + if (realtime_set) { + j->current_location.realtime = (uint64_t) realtime; + j->current_location.realtime_set = true; + } + + if (seqnum_set && seqnum_id_set) { + j->current_location.seqnum = (uint64_t) seqnum; + j->current_location.seqnum_id = seqnum_id; + j->current_location.seqnum_set = true; + } + + if (monotonic_set && boot_id_set) { + j->current_location.monotonic = (uint64_t) monotonic; + j->current_location.boot_id = boot_id; + j->current_location.monotonic_set = true; + } + + if (xor_hash_set) { + j->current_location.xor_hash = (uint64_t) xor_hash; + j->current_location.xor_hash_set = true; + } + + return 0; +} + +_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(cursor), -EINVAL); + + if (!j->current_file || j->current_file->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *item = NULL; + unsigned long long ll; + sd_id128_t id; + int k = 0; + + r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + + if (r == 0) + break; + + if (strlen(item) < 2 || item[1] != '=') + return -EINVAL; + + switch (item[0]) { + + case 's': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, j->current_file->header->seqnum_id)) + return 0; + break; + + case 'i': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.seqnum)) + return 0; + break; + + case 'b': + k = sd_id128_from_string(item+2, &id); + if (k < 0) + return k; + if (!sd_id128_equal(id, o->entry.boot_id)) + return 0; + break; + + case 'm': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.monotonic)) + return 0; + break; + + case 't': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.realtime)) + return 0; + break; + + case 'x': + if (sscanf(item+2, "%llx", &ll) != 1) + return -EINVAL; + if (ll != le64toh(o->entry.xor_hash)) + return 0; + break; + } + } + + return 1; +} + + +_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_SEEK; + j->current_location.boot_id = boot_id; + j->current_location.monotonic = usec; + j->current_location.monotonic_set = true; + + return 0; +} + +_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_SEEK; + j->current_location.realtime = usec; + j->current_location.realtime_set = true; + + return 0; +} + +_public_ int sd_journal_seek_head(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_HEAD; + + return 0; +} + +_public_ int sd_journal_seek_tail(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + reset_location(j); + j->current_location.type = LOCATION_TAIL; + + return 0; +} + +static void check_network(sd_journal *j, int fd) { + struct statfs sfs; + + assert(j); + + if (j->on_network) + return; + + if (fstatfs(fd, &sfs) < 0) + return; + + j->on_network = + F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) || + F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC); +} + +static bool file_has_type_prefix(const char *prefix, const char *filename) { + const char *full, *tilded, *atted; + + full = strjoina(prefix, ".journal"); + tilded = strjoina(full, "~"); + atted = strjoina(prefix, "@"); + + return streq(filename, full) || + streq(filename, tilded) || + startswith(filename, atted); +} + +static bool file_type_wanted(int flags, const char *filename) { + assert(filename); + + if (!endswith(filename, ".journal") && !endswith(filename, ".journal~")) + return false; + + /* no flags set → every type is OK */ + if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER))) + return true; + + if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename)) + return true; + + if (flags & SD_JOURNAL_CURRENT_USER) { + char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1]; + + xsprintf(prefix, "user-"UID_FMT, getuid()); + + if (file_has_type_prefix(prefix, filename)) + return true; + } + + return false; +} + +static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { + assert(j); + assert(path); + assert(prefix); + + if (j->toplevel_fd >= 0) + return false; + + return path_startswith(path, prefix); +} + +static const char *skip_slash(const char *p) { + + if (!p) + return NULL; + + while (*p == '/') + p++; + + return p; +} + +static int add_any_file(sd_journal *j, int fd, const char *path) { + JournalFile *f = NULL; + bool close_fd = false; + int r, k; + + assert(j); + assert(fd >= 0 || path); + + if (path && ordered_hashmap_get(j->files, path)) + return 0; + + if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { + log_debug("Too many open journal files, not adding %s.", path); + r = -ETOOMANYREFS; + goto fail; + } + + if (fd < 0 && j->toplevel_fd >= 0) { + + /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, + * explicitly, since otherwise openat() ignores the first argument.) */ + + fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); + goto fail; + } + + close_fd = true; + } + + r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); + if (r < 0) { + if (close_fd) + safe_close(fd); + 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) { + f->close_fd = close_fd; + (void) journal_file_close(f); + goto fail; + } + + if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) + j->has_runtime_files = true; + else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) + j->has_persistent_files = true; + + log_debug("File %s added.", f->path); + + check_network(j, f->fd); + + 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) { + const char *path; + + assert(j); + assert(prefix); + assert(filename); + + if (j->no_new_files) + return 0; + + if (!file_type_wanted(j->flags, filename)) + return 0; + + path = strjoina(prefix, "/", filename); + return add_any_file(j, -1, 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 = strjoina(prefix, "/", filename); + f = ordered_hashmap_get(j->files, path); + if (!f) + return; + + remove_file_real(j, f); +} + +static void remove_file_real(sd_journal *j, JournalFile *f) { + assert(j); + assert(f); + + ordered_hashmap_remove(j->files, f->path); + + log_debug("File %s removed.", f->path); + + if (j->current_file == f) { + j->current_file = NULL; + j->current_field = 0; + } + + if (j->unique_file == f) { + /* Jump to the next unique_file or NULL if that one was last */ + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); + j->unique_offset = 0; + if (!j->unique_file) + j->unique_file_lost = true; + } + + if (j->fields_file == f) { + j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path); + j->fields_offset = 0; + if (!j->fields_file) + j->fields_file_lost = true; + } + + (void) journal_file_close(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; + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de = NULL; + Directory *m; + int r, k; + + assert(j); + assert(prefix); + + /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch + * and reenumerates directory contents */ + + if (dirname) + path = strjoin(prefix, "/", dirname, NULL); + else + path = strdup(prefix); + if (!path) { + r = -ENOMEM; + goto fail; + } + + log_debug("Considering directory %s.", path); + + /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) + return 0; + + + if (j->toplevel_fd < 0) + d = opendir(path); + else + /* Open the specified directory relative to the toplevel fd. Enforce that the path specified is + * relative, by dropping the initial slash */ + d = xopendirat(j->toplevel_fd, skip_slash(path), 0); + if (!d) { + 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) { + r = -ENOMEM; + goto fail; + } + + m->is_root = false; + m->path = path; + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + free(m); + r = -ENOMEM; + goto fail; + } + + path = NULL; /* avoid freeing in cleanup */ + j->current_invalidate_counter++; + + log_debug("Directory %s added.", m->path); + + } else if (m->is_root) + return 0; + + if (m->wd <= 0 && j->inotify_fd >= 0) { + /* Watch this directory, if it not being watched yet. */ + + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + IN_ONLYDIR); + + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } + + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { + + if (dirent_is_file_with_suffix(de, ".journal") || + 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 ?: prefix); + if (k < 0) + return k; + + return r; +} + +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, k; + + assert(j); + + /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we + * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially + * populate the set, as well as to update it later. */ + + if (p) { + /* If there's a path specified, use it. */ + + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_has_prefix(j, p, "/run")) + return -EINVAL; + + if (j->prefix) + p = strjoina(j->prefix, p); + + if (j->toplevel_fd < 0) + d = opendir(p); + else + d = xopendirat(j->toplevel_fd, skip_slash(p), 0); + + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } + } else { + int dfd; + + /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since + * opendir() will take possession of the fd, and close it, which we don't want. */ + + p = "."; /* store this as "." in the directories hashmap */ + + dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); + if (dfd < 0) { + r = -errno; + goto fail; + } + + d = fdopendir(dfd); + if (!d) { + r = -errno; + safe_close(dfd); + goto fail; + } + + rewinddir(d); + } + + m = hashmap_get(j->directories_by_path, p); + if (!m) { + m = new0(Directory, 1); + if (!m) { + r = -ENOMEM; + goto fail; + } + + m->is_root = true; + + m->path = strdup(p); + if (!m->path) { + free(m); + r = -ENOMEM; + goto fail; + } + + if (hashmap_put(j->directories_by_path, m->path, m) < 0) { + free(m->path); + free(m); + r = -ENOMEM; + goto fail; + } + + j->current_invalidate_counter++; + + log_debug("Root directory %s added.", m->path); + + } else if (!m->is_root) + return 0; + + if (m->wd <= 0 && j->inotify_fd >= 0) { + + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_ONLYDIR); + + if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) + inotify_rm_watch(j->inotify_fd, m->wd); + } + + if (j->no_new_files) + return 0; + + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { + sd_id128_t id; + + if (dirent_is_file_with_suffix(de, ".journal") || + 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) { + assert(j); + + if (d->wd > 0) { + hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd)); + + if (j->inotify_fd >= 0) + inotify_rm_watch(j->inotify_fd, d->wd); + } + + hashmap_remove(j->directories_by_path, d->path); + + if (d->is_root) + log_debug("Root directory %s removed.", d->path); + else + log_debug("Directory %s removed.", d->path); + + free(d->path); + free(d); +} + +static int add_search_paths(sd_journal *j) { + + static const char search_paths[] = + "/run/log/journal\0" + "/var/log/journal\0"; + const char *p; + + assert(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) + (void) add_root_directory(j, p, true); + + return 0; +} + +static int add_current_paths(sd_journal *j) { + Iterator i; + JournalFile *f; + + assert(j); + assert(j->no_new_files); + + /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we + * treat them as fatal. */ + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + _cleanup_free_ char *dir; + int r; + + dir = dirname_malloc(f->path); + if (!dir) + return -ENOMEM; + + r = add_directory(j, dir, NULL); + if (r < 0) + return r; + } + + return 0; +} + +static int allocate_inotify(sd_journal *j) { + assert(j); + + if (j->inotify_fd < 0) { + j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (j->inotify_fd < 0) + return -errno; + } + + return hashmap_ensure_allocated(&j->directories_by_wd, NULL); +} + +static sd_journal *journal_new(int flags, const char *path) { + sd_journal *j; + + j = new0(sd_journal, 1); + if (!j) + return NULL; + + j->original_pid = getpid(); + j->toplevel_fd = -1; + j->inotify_fd = -1; + j->flags = flags; + j->data_threshold = DEFAULT_DATA_THRESHOLD; + + if (path) { + j->path = strdup(path); + if (!j->path) + goto fail; + } + + j->files = ordered_hashmap_new(&string_hash_ops); + j->directories_by_path = hashmap_new(&string_hash_ops); + j->mmap = mmap_cache_new(); + if (!j->files || !j->directories_by_path || !j->mmap) + goto fail; + + return j; + +fail: + sd_journal_close(j); + return NULL; +} + +_public_ int sd_journal_open(sd_journal **ret, int flags) { + sd_journal *j; + int r; + + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + r = add_search_paths(j); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + + return r; +} + +_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) { + _cleanup_free_ char *root = NULL, *class = NULL; + sd_journal *j; + char *p; + int r; + + /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + * combination with sd_journal_open_directory_fd(). */ + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); + assert_return(machine_name_is_valid(machine), -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!root) + return -ENODATA; + + if (!streq_ptr(class, "container")) + return -EIO; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->prefix = root; + root = NULL; + + r = add_search_paths(j); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) { + sd_journal *j; + int r; + + assert_return(ret, -EINVAL); + assert_return(path, -EINVAL); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + j = journal_new(flags, path); + if (!j) + return -ENOMEM; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, path, false); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { + sd_journal *j; + const char **path; + int r; + + assert_return(ret, -EINVAL); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + STRV_FOREACH(path, paths) { + r = add_any_file(j, -1, *path); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { + sd_journal *j; + struct stat st; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EBADFD; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->toplevel_fd = fd; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, NULL, false); + if (r < 0) + goto fail; + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { + Iterator iterator; + JournalFile *f; + sd_journal *j; + unsigned i; + int r; + + assert_return(ret, -EINVAL); + assert_return(n_fds > 0, -EBADF); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + struct stat st; + + if (fds[i] < 0) { + r = -EBADF; + goto fail; + } + + if (fstat(fds[i], &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISREG(st.st_mode)) { + r = -EBADFD; + goto fail; + } + + r = add_any_file(j, fds[i], NULL); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + j->no_inotify = true; + + *ret = j; + return 0; + +fail: + /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they + * remain open */ + ORDERED_HASHMAP_FOREACH(f, j->files, iterator) + f->close_fd = false; + + sd_journal_close(j); + return r; +} + +_public_ void sd_journal_close(sd_journal *j) { + Directory *d; + JournalFile *f; + char *p; + + if (!j) + return; + + sd_journal_flush_matches(j); + + while ((f = ordered_hashmap_steal_first(j->files))) + (void) journal_file_close(f); + + ordered_hashmap_free(j->files); + + while ((d = hashmap_first(j->directories_by_path))) + remove_directory(j, d); + + while ((d = hashmap_first(j->directories_by_wd))) + remove_directory(j, d); + + hashmap_free(j->directories_by_path); + hashmap_free(j->directories_by_wd); + + safe_close(j->inotify_fd); + + if (j->mmap) { + log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap)); + 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); + free(j->fields_buffer); + free(j); +} + +_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) { + Object *o; + JournalFile *f; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + *ret = le64toh(o->entry.realtime); + return 0; +} + +_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) { + Object *o; + JournalFile *f; + int r; + sd_id128_t id; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + if (ret_boot_id) + *ret_boot_id = o->entry.boot_id; + else { + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + if (!sd_id128_equal(id, o->entry.boot_id)) + return -ESTALE; + } + + if (ret) + *ret = le64toh(o->entry.monotonic); + + return 0; +} + +static bool field_is_valid(const char *field) { + const char *p; + + assert(field); + + if (isempty(field)) + return false; + + if (startswith(field, "__")) + return false; + + for (p = field; *p; p++) { + + if (*p == '_') + continue; + + if (*p >= 'A' && *p <= 'Z') + continue; + + if (*p >= '0' && *p <= '9') + continue; + + return false; + } + + return true; +} + +_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) { + JournalFile *f; + uint64_t i, n; + size_t field_length; + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); + assert_return(field_is_valid(field), -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + field_length = strlen(field); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t p, l; + le64_t le_hash; + size_t t; + int compression; + + p = le64toh(o->entry.items[i].object_offset); + le_hash = o->entry.items[i].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + r = decompress_startswith(compression, + o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, + field, field_length, '='); + if (r < 0) + log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m", + object_compressed_to_string(compression), l, p); + else if (r > 0) { + + size_t rsize; + + r = decompress_blob(compression, + o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize, + j->data_threshold); + if (r < 0) + return r; + + *data = f->compress_buffer; + *size = (size_t) rsize; + + return 0; + } +#else + return -EPROTONOSUPPORT; +#endif + } else if (l >= field_length+1 && + memcmp(o->data.payload, field, field_length) == 0 && + o->data.payload[field_length] == '=') { + + t = (size_t) l; + + if ((uint64_t) t != l) + return -E2BIG; + + *data = o->data.payload; + *size = t; + + return 0; + } + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + } + + return -ENOENT; +} + +static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) { + size_t t; + uint64_t l; + int compression; + + l = le64toh(o->object.size) - offsetof(Object, data.payload); + t = (size_t) l; + + /* We can't read objects larger than 4G on a 32bit machine */ + if ((uint64_t) t != l) + return -E2BIG; + + compression = o->object.flags & OBJECT_COMPRESSION_MASK; + if (compression) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + size_t rsize; + int r; + + r = decompress_blob(compression, + o->data.payload, l, &f->compress_buffer, + &f->compress_buffer_size, &rsize, j->data_threshold); + if (r < 0) + return r; + + *data = f->compress_buffer; + *size = (size_t) rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else { + *data = o->data.payload; + *size = t; + } + + return 0; +} + +_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { + JournalFile *f; + uint64_t p, n; + le64_t le_hash; + int r; + Object *o; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(size, -EINVAL); + + f = j->current_file; + if (!f) + return -EADDRNOTAVAIL; + + if (f->current_offset <= 0) + return -EADDRNOTAVAIL; + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + if (j->current_field >= n) + return 0; + + p = le64toh(o->entry.items[j->current_field].object_offset); + le_hash = o->entry.items[j->current_field].hash; + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + + if (le_hash != o->data.hash) + return -EBADMSG; + + r = return_data(j, f, o, data, size); + if (r < 0) + return r; + + j->current_field++; + + return 1; +} + +_public_ void sd_journal_restart_data(sd_journal *j) { + if (!j) + return; + + j->current_field = 0; +} + +_public_ int sd_journal_get_fd(sd_journal *j) { + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (j->no_inotify) + return -EMEDIUMTYPE; + + if (j->inotify_fd >= 0) + return j->inotify_fd; + + r = allocate_inotify(j); + if (r < 0) + return r; + + log_debug("Reiterating files to get inotify watches established"); + + /* Iterate through all dirs again, to add them to the + * inotify */ + if (j->no_new_files) + r = add_current_paths(j); + else if (j->toplevel_fd >= 0) + r = add_root_directory(j, NULL, false); + else if (j->path) + r = add_root_directory(j, j->path, true); + else + r = add_search_paths(j); + if (r < 0) + return r; + + return j->inotify_fd; +} + +_public_ int sd_journal_get_events(sd_journal *j) { + int fd; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + return POLLIN; +} + +_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { + int fd; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(timeout_usec, -EINVAL); + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + if (!j->on_network) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + /* If we are on the network we need to regularly check for + * changes manually */ + + *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC; + return 1; +} + +static void process_inotify_event(sd_journal *j, struct inotify_event *e) { + Directory *d; + + assert(j); + assert(e); + + /* Is this a subdirectory we watch? */ + d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd)); + if (d) { + sd_id128_t id; + + if (!(e->mask & IN_ISDIR) && e->len > 0 && + (endswith(e->name, ".journal") || + endswith(e->name, ".journal~"))) { + + /* Event for a journal file */ + + 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) { + + /* Event for a subdirectory */ + + if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) + remove_directory(j, d); + + } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) { + + /* Event for root directory */ + + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) + (void) add_directory(j, d->path, e->name); + } + + return; + } + + if (e->mask & IN_IGNORED) + return; + + log_debug("Unknown inotify event."); +} + +static int determine_change(sd_journal *j) { + bool b; + + assert(j); + + b = j->current_invalidate_counter != j->last_invalidate_counter; + j->last_invalidate_counter = j->current_invalidate_counter; + + return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND; +} + +_public_ int sd_journal_process(sd_journal *j) { + bool got_something = false; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + j->last_process_usec = now(CLOCK_MONOTONIC); + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(j->inotify_fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return got_something ? determine_change(j) : SD_JOURNAL_NOP; + + return -errno; + } + + got_something = true; + + FOREACH_INOTIFY_EVENT(e, buffer, l) + process_inotify_event(j, e); + } +} + +_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + int r; + uint64_t t; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + if (j->inotify_fd < 0) { + + /* This is the first invocation, hence create the + * inotify watch */ + r = sd_journal_get_fd(j); + if (r < 0) + return r; + + /* The journal might have changed since the context + * object was created and we weren't watching before, + * hence don't wait for anything, and return + * immediately. */ + return determine_change(j); + } + + r = sd_journal_get_timeout(j, &t); + if (r < 0) + return r; + + if (t != (uint64_t) -1) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + t = t > n ? t - n : 0; + + if (timeout_usec == (uint64_t) -1 || timeout_usec > t) + timeout_usec = t; + } + + do { + r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_journal_process(j); +} + +_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) { + Iterator i; + JournalFile *f; + bool first = true; + uint64_t fmin = 0, tmax = 0; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_realtime_usec(f, &fr, &t); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (first) { + fmin = fr; + tmax = t; + first = false; + } else { + fmin = MIN(fr, fmin); + tmax = MAX(t, tmax); + } + } + + if (from) + *from = fmin; + if (to) + *to = tmax; + + return first ? 0 : 1; +} + +_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) { + Iterator i; + JournalFile *f; + bool found = false; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(from || to, -EINVAL); + assert_return(from != to, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + usec_t fr, t; + + r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (found) { + if (from) + *from = MIN(fr, *from); + if (to) + *to = MAX(t, *to); + } else { + if (from) + *from = fr; + if (to) + *to = t; + found = true; + } + } + + return found; +} + +void journal_print_header(sd_journal *j) { + Iterator i; + JournalFile *f; + bool newline = false; + + assert(j); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + if (newline) + putchar('\n'); + else + newline = true; + + journal_file_print_header(f); + } +} + +_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) { + Iterator i; + JournalFile *f; + uint64_t sum = 0; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(bytes, -EINVAL); + + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + struct stat st; + + if (fstat(f->fd, &st) < 0) + return -errno; + + sum += (uint64_t) st.st_blocks * 512ULL; + } + + *bytes = sum; + return 0; +} + +_public_ int sd_journal_query_unique(sd_journal *j, const char *field) { + char *f; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(!isempty(field), -EINVAL); + assert_return(field_is_valid(field), -EINVAL); + + f = strdup(field); + if (!f) + return -ENOMEM; + + free(j->unique_field); + j->unique_field = f; + j->unique_file = NULL; + j->unique_offset = 0; + j->unique_file_lost = false; + + return 0; +} + +_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { + size_t k; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(data, -EINVAL); + assert_return(l, -EINVAL); + assert_return(j->unique_field, -EINVAL); + + k = strlen(j->unique_field); + + if (!j->unique_file) { + if (j->unique_file_lost) + return 0; + + j->unique_file = ordered_hashmap_first(j->files); + if (!j->unique_file) + return 0; + + j->unique_offset = 0; + } + + for (;;) { + JournalFile *of; + Iterator i; + Object *o; + const void *odata; + size_t ol; + bool found; + int r; + + /* Proceed to next data object in the field's linked list */ + if (j->unique_offset == 0) { + r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL); + if (r < 0) + return r; + + j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0; + } else { + r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o); + if (r < 0) + return r; + + j->unique_offset = le64toh(o->data.next_field_offset); + } + + /* We reached the end of the list? Then start again, with the next file */ + if (j->unique_offset == 0) { + j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path); + if (!j->unique_file) + return 0; + + continue; + } + + /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED + * instead, so that we can look at this data object at the same + * time as one on another file */ + r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o); + if (r < 0) + return r; + + /* Let's do the type check by hand, since we used 0 context above. */ + if (o->object.type != OBJECT_DATA) { + log_debug("%s:offset " OFSfmt ": object has type %d, expected %d", + j->unique_file->path, j->unique_offset, + o->object.type, OBJECT_DATA); + return -EBADMSG; + } + + r = return_data(j, j->unique_file, o, &odata, &ol); + if (r < 0) + return r; + + /* Check if we have at least the field name and "=". */ + if (ol <= k) { + log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu", + j->unique_file->path, j->unique_offset, + ol, k + 1); + return -EBADMSG; + } + + if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') { + log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"", + j->unique_file->path, j->unique_offset, + j->unique_field); + return -EBADMSG; + } + + /* OK, now let's see if we already returned this data + * object by checking if it exists in the earlier + * traversed files. */ + found = false; + ORDERED_HASHMAP_FOREACH(of, j->files, i) { + if (of == j->unique_file) + break; + + /* Skip this file it didn't have any fields indexed */ + if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) + continue; + + r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + found = true; + break; + } + } + + if (found) + continue; + + r = return_data(j, j->unique_file, o, data, l); + if (r < 0) + return r; + + return 1; + } +} + +_public_ void sd_journal_restart_unique(sd_journal *j) { + if (!j) + return; + + j->unique_file = NULL; + j->unique_offset = 0; + j->unique_file_lost = false; +} + +_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) { + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(field, -EINVAL); + + if (!j->fields_file) { + if (j->fields_file_lost) + return 0; + + j->fields_file = ordered_hashmap_first(j->files); + if (!j->fields_file) + return 0; + + j->fields_hash_table_index = 0; + j->fields_offset = 0; + } + + for (;;) { + JournalFile *f, *of; + Iterator i; + uint64_t m; + Object *o; + size_t sz; + bool found; + + f = j->fields_file; + + if (j->fields_offset == 0) { + bool eof = false; + + /* We are not yet positioned at any field. Let's pick the first one */ + r = journal_file_map_field_hash_table(f); + if (r < 0) + return r; + + m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem); + for (;;) { + if (j->fields_hash_table_index >= m) { + /* Reached the end of the hash table, go to the next file. */ + eof = true; + break; + } + + j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset); + + if (j->fields_offset != 0) + break; + + /* Empty hash table bucket, go to next one */ + j->fields_hash_table_index++; + } + + if (eof) { + /* Proceed with next file */ + j->fields_file = ordered_hashmap_next(j->files, f->path); + if (!j->fields_file) { + *field = NULL; + return 0; + } + + j->fields_offset = 0; + j->fields_hash_table_index = 0; + continue; + } + + } else { + /* We are already positioned at a field. If so, let's figure out the next field from it */ + + r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o); + if (r < 0) + return r; + + j->fields_offset = le64toh(o->field.next_hash_offset); + if (j->fields_offset == 0) { + /* Reached the end of the hash table chain */ + j->fields_hash_table_index++; + continue; + } + } + + /* We use OBJECT_UNUSED here, so that the iterator below doesn't remove our mmap window */ + r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o); + if (r < 0) + return r; + + /* Because we used OBJECT_UNUSED above, we need to do our type check manually */ + if (o->object.type != OBJECT_FIELD) { + log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD); + return -EBADMSG; + } + + sz = le64toh(o->object.size) - offsetof(Object, field.payload); + + /* Let's see if we already returned this field name before. */ + found = false; + ORDERED_HASHMAP_FOREACH(of, j->files, i) { + if (of == f) + break; + + /* Skip this file it didn't have any fields indexed */ + if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0) + continue; + + r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + found = true; + break; + } + } + + if (found) + continue; + + /* Check if this is really a valid string containing no NUL byte */ + if (memchr(o->field.payload, 0, sz)) + return -EBADMSG; + + if (sz > j->data_threshold) + sz = j->data_threshold; + + if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1)) + return -ENOMEM; + + memcpy(j->fields_buffer, o->field.payload, sz); + j->fields_buffer[sz] = 0; + + if (!field_is_valid(j->fields_buffer)) + return -EBADMSG; + + *field = j->fields_buffer; + return 1; + } +} + +_public_ void sd_journal_restart_fields(sd_journal *j) { + if (!j) + return; + + j->fields_file = NULL; + j->fields_hash_table_index = 0; + j->fields_offset = 0; + j->fields_file_lost = false; +} + +_public_ int sd_journal_reliable_fd(sd_journal *j) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + return !j->on_network; +} + +static char *lookup_field(const char *field, void *userdata) { + sd_journal *j = userdata; + const void *data; + size_t size, d; + int r; + + assert(field); + assert(j); + + r = sd_journal_get_data(j, field, &data, &size); + if (r < 0 || + size > REPLACE_VAR_MAX) + return strdup(field); + + d = strlen(field) + 1; + + return strndup((const char*) data + d, size - d); +} + +_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { + const void *data; + size_t size; + sd_id128_t id; + _cleanup_free_ char *text = NULL, *cid = NULL; + char *t; + int r; + + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(ret, -EINVAL); + + r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size); + if (r < 0) + return r; + + cid = strndup((const char*) data + 11, size - 11); + if (!cid) + return -ENOMEM; + + r = sd_id128_from_string(cid, &id); + if (r < 0) + return r; + + r = catalog_get(CATALOG_DATABASE, id, &text); + if (r < 0) + return r; + + t = replace_var(text, lookup_field, j); + if (!t) + return -ENOMEM; + + *ret = t; + return 0; +} + +_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) { + assert_return(ret, -EINVAL); + + return catalog_get(CATALOG_DATABASE, id, ret); +} + +_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + + j->data_threshold = sz; + return 0; +} + +_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) { + assert_return(j, -EINVAL); + assert_return(!journal_pid_changed(j), -ECHILD); + assert_return(sz, -EINVAL); + + *sz = j->data_threshold; + return 0; +} + +_public_ int sd_journal_has_runtime_files(sd_journal *j) { + assert_return(j, -EINVAL); + + return j->has_runtime_files; +} + +_public_ int sd_journal_has_persistent_files(sd_journal *j) { + assert_return(j, -EINVAL); + + return j->has_persistent_files; +} diff --git a/src/libsystemd/src/sd-login/Makefile b/src/libsystemd/src/sd-login/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-login/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-login/sd-login.c b/src/libsystemd/src/sd-login/sd-login.c new file mode 100644 index 0000000000..2698dc5c2b --- /dev/null +++ b/src/libsystemd/src/sd-login/sd-login.c @@ -0,0 +1,1062 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/dirent-util.h" +#include "basic/escape.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/io-util.h" +#include "basic/login-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" + +/* Error codes: + * + * invalid input parameters → -EINVAL + * invalid fd → -EBADF + * process does not exist → -ESRCH + * cgroup does not exist → -ENOENT + * machine, session does not exist → -ENXIO + * requested metadata on object is missing → -ENODATA + */ + +_public_ int sd_pid_get_session(pid_t pid, char **session) { + + assert_return(pid >= 0, -EINVAL); + assert_return(session, -EINVAL); + + return cg_pid_get_session(pid, session); +} + +_public_ int sd_pid_get_unit(pid_t pid, char **unit) { + + assert_return(pid >= 0, -EINVAL); + assert_return(unit, -EINVAL); + + return cg_pid_get_unit(pid, unit); +} + +_public_ int sd_pid_get_user_unit(pid_t pid, char **unit) { + + assert_return(pid >= 0, -EINVAL); + assert_return(unit, -EINVAL); + + return cg_pid_get_user_unit(pid, unit); +} + +_public_ int sd_pid_get_machine_name(pid_t pid, char **name) { + + assert_return(pid >= 0, -EINVAL); + assert_return(name, -EINVAL); + + return cg_pid_get_machine_name(pid, name); +} + +_public_ int sd_pid_get_slice(pid_t pid, char **slice) { + + assert_return(pid >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + return cg_pid_get_slice(pid, slice); +} + +_public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { + + assert_return(pid >= 0, -EINVAL); + assert_return(slice, -EINVAL); + + return cg_pid_get_user_slice(pid, slice); +} + +_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { + + assert_return(pid >= 0, -EINVAL); + assert_return(uid, -EINVAL); + + return cg_pid_get_owner_uid(pid, uid); +} + +_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { + char *c; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(cgroup, -EINVAL); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c); + if (r < 0) + return r; + + /* The internal APIs return the empty string for the root + * cgroup, let's return the "/" in the public APIs instead, as + * that's easier and less ambiguous for people to grok. */ + if (isempty(c)) { + free(c); + c = strdup("/"); + if (!c) + return -ENOMEM; + + } + + *cgroup = c; + return 0; +} + +_public_ int sd_peer_get_session(int fd, char **session) { + struct ucred ucred = {}; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(session, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_session(ucred.pid, session); +} + +_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(uid, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_owner_uid(ucred.pid, uid); +} + +_public_ int sd_peer_get_unit(int fd, char **unit) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(unit, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_unit(ucred.pid, unit); +} + +_public_ int sd_peer_get_user_unit(int fd, char **unit) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(unit, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_user_unit(ucred.pid, unit); +} + +_public_ int sd_peer_get_machine_name(int fd, char **machine) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(machine, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_machine_name(ucred.pid, machine); +} + +_public_ int sd_peer_get_slice(int fd, char **slice) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(slice, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_slice(ucred.pid, slice); +} + +_public_ int sd_peer_get_user_slice(int fd, char **slice) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(slice, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return cg_pid_get_user_slice(ucred.pid, slice); +} + +_public_ int sd_peer_get_cgroup(int fd, char **cgroup) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(cgroup, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return sd_pid_get_cgroup(ucred.pid, cgroup); +} + +static int file_of_uid(uid_t uid, char **p) { + + assert_return(uid_is_valid(uid), -EINVAL); + assert(p); + + if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0) + return -ENOMEM; + + return 0; +} + +_public_ int sd_uid_get_state(uid_t uid, char**state) { + _cleanup_free_ char *p = NULL; + char *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + if (r == -ENOENT) { + free(s); + s = strdup("offline"); + if (!s) + return -ENOMEM; + + } + if (r < 0) { + free(s); + return r; + } + if (isempty(s)) { + free(s); + return -EIO; + } + + *state = s; + return 0; +} + +_public_ int sd_uid_get_display(uid_t uid, char **session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(session, -EINVAL); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *session = s; + s = NULL; + + return 0; +} + +static int file_of_seat(const char *seat, char **_p) { + char *p; + int r; + + assert(_p); + + if (seat) { + if (!filename_is_valid(seat)) + return -EINVAL; + + p = strappend("/run/systemd/seats/", seat); + } else { + _cleanup_free_ char *buf = NULL; + + r = sd_session_get_seat(NULL, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/seats/", buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; + p = NULL; + return 0; +} + +_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { + _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; + size_t l; + int r; + const char *word, *variable, *state; + + assert_return(uid_is_valid(uid), -EINVAL); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + variable = require_active ? "ACTIVE_UID" : "UIDS"; + + r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + if (isempty(s)) + return 0; + + if (asprintf(&t, UID_FMT, uid) < 0) + return -ENOMEM; + + FOREACH_WORD(word, l, s, state) + if (strneq(t, word, l)) + return 1; + + return 0; +} + +static int uid_get_array(uid_t uid, const char *variable, char ***array) { + _cleanup_free_ char *p = NULL, *s = NULL; + char **a; + int r; + + assert(variable); + + r = file_of_uid(uid, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT || (r >= 0 && isempty(s))) { + if (array) + *array = NULL; + return 0; + } + if (r < 0) + return r; + + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + + strv_uniq(a); + r = strv_length(a); + + if (array) + *array = a; + else + strv_free(a); + + return r; +} + +_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) { + return uid_get_array( + uid, + require_active == 0 ? "ONLINE_SESSIONS" : + require_active > 0 ? "ACTIVE_SESSIONS" : + "SESSIONS", + sessions); +} + +_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) { + return uid_get_array( + uid, + require_active == 0 ? "ONLINE_SEATS" : + require_active > 0 ? "ACTIVE_SEATS" : + "SEATS", + seats); +} + +static int file_of_session(const char *session, char **_p) { + char *p; + int r; + + assert(_p); + + if (session) { + if (!session_id_valid(session)) + return -EINVAL; + + p = strappend("/run/systemd/sessions/", session); + } else { + _cleanup_free_ char *buf = NULL; + + r = sd_pid_get_session(0, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/sessions/", buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; + return 0; +} + +_public_ int sd_session_is_active(const char *session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + return parse_boolean(s); +} + +_public_ int sd_session_is_remote(const char *session) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + return parse_boolean(s); +} + +_public_ int sd_session_get_state(const char *session, char **state) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + *state = s; + s = NULL; + + return 0; +} + +_public_ int sd_session_get_uid(const char *session, uid_t *uid) { + int r; + _cleanup_free_ char *p = NULL, *s = NULL; + + assert_return(uid, -EINVAL); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, "UID", &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -EIO; + + return parse_uid(s, uid); +} + +static int session_get_string(const char *session, const char *field, char **value) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert_return(value, -EINVAL); + assert(field); + + r = file_of_session(session, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, field, &s, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *value = s; + s = NULL; + return 0; +} + +_public_ int sd_session_get_seat(const char *session, char **seat) { + return session_get_string(session, "SEAT", seat); +} + +_public_ int sd_session_get_tty(const char *session, char **tty) { + return session_get_string(session, "TTY", tty); +} + +_public_ int sd_session_get_vt(const char *session, unsigned *vtnr) { + _cleanup_free_ char *vtnr_string = NULL; + unsigned u; + int r; + + assert_return(vtnr, -EINVAL); + + r = session_get_string(session, "VTNR", &vtnr_string); + if (r < 0) + return r; + + r = safe_atou(vtnr_string, &u); + if (r < 0) + return r; + + *vtnr = u; + return 0; +} + +_public_ int sd_session_get_service(const char *session, char **service) { + return session_get_string(session, "SERVICE", service); +} + +_public_ int sd_session_get_type(const char *session, char **type) { + return session_get_string(session, "TYPE", type); +} + +_public_ int sd_session_get_class(const char *session, char **class) { + return session_get_string(session, "CLASS", class); +} + +_public_ int sd_session_get_desktop(const char *session, char **desktop) { + _cleanup_free_ char *escaped = NULL; + char *t; + int r; + + assert_return(desktop, -EINVAL); + + r = session_get_string(session, "DESKTOP", &escaped); + if (r < 0) + return r; + + r = cunescape(escaped, 0, &t); + if (r < 0) + return r; + + *desktop = t; + return 0; +} + +_public_ int sd_session_get_display(const char *session, char **display) { + return session_get_string(session, "DISPLAY", display); +} + +_public_ int sd_session_get_remote_user(const char *session, char **remote_user) { + return session_get_string(session, "REMOTE_USER", remote_user); +} + +_public_ int sd_session_get_remote_host(const char *session, char **remote_host) { + return session_get_string(session, "REMOTE_HOST", remote_host); +} + +_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { + _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; + int r; + + assert_return(session || uid, -EINVAL); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + "ACTIVE", &s, + "ACTIVE_UID", &t, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + + if (session && !s) + return -ENODATA; + + if (uid && !t) + return -ENODATA; + + if (uid && t) { + r = parse_uid(t, uid); + if (r < 0) + return r; + } + + if (session && s) { + *session = s; + s = NULL; + } + + return 0; +} + +_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) { + _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ uid_t *b = NULL; + unsigned n = 0; + int r; + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + "SESSIONS", &s, + "ACTIVE_SESSIONS", &t, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + + if (s) { + a = strv_split(s, " "); + if (!a) + return -ENOMEM; + } + + if (uids && t) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, t, state) + n++; + + if (n > 0) { + unsigned i = 0; + + b = new(uid_t, n); + if (!b) + return -ENOMEM; + + FOREACH_WORD(word, l, t, state) { + _cleanup_free_ char *k = NULL; + + k = strndup(word, l); + if (!k) + return -ENOMEM; + + r = parse_uid(k, b + i); + if (r < 0) + continue; + + i++; + } + } + } + + r = strv_length(a); + + if (sessions) { + *sessions = a; + a = NULL; + } + + if (uids) { + *uids = b; + b = NULL; + } + + if (n_uids) + *n_uids = n; + + return r; +} + +static int seat_get_can(const char *seat, const char *variable) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert(variable); + + r = file_of_seat(seat, &p); + if (r < 0) + return r; + + r = parse_env_file(p, NEWLINE, + variable, &s, + NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + return parse_boolean(s); +} + +_public_ int sd_seat_can_multi_session(const char *seat) { + return seat_get_can(seat, "CAN_MULTI_SESSION"); +} + +_public_ int sd_seat_can_tty(const char *seat) { + return seat_get_can(seat, "CAN_TTY"); +} + +_public_ int sd_seat_can_graphical(const char *seat) { + return seat_get_can(seat, "CAN_GRAPHICAL"); +} + +_public_ int sd_get_seats(char ***seats) { + return get_files_in_directory("/run/systemd/seats/", seats); +} + +_public_ int sd_get_sessions(char ***sessions) { + return get_files_in_directory("/run/systemd/sessions/", sessions); +} + +_public_ int sd_get_uids(uid_t **users) { + _cleanup_closedir_ DIR *d; + int r = 0; + unsigned n = 0; + _cleanup_free_ uid_t *l = NULL; + + d = opendir("/run/systemd/users/"); + if (!d) + return -errno; + + for (;;) { + struct dirent *de; + int k; + uid_t uid; + + 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; + + k = parse_uid(de->d_name, &uid); + if (k < 0) + continue; + + if (users) { + if ((unsigned) r >= n) { + uid_t *t; + + n = MAX(16, 2*r); + t = realloc(l, sizeof(uid_t) * n); + if (!t) + return -ENOMEM; + + l = t; + } + + assert((unsigned) r < n); + l[r++] = uid; + } else + r++; + } + + if (users) { + *users = l; + l = NULL; + } + + return r; +} + +_public_ int sd_get_machine_names(char ***machines) { + char **l = NULL, **a, **b; + int r; + + assert_return(machines, -EINVAL); + + r = get_files_in_directory("/run/systemd/machines/", &l); + if (r < 0) + return r; + + if (l) { + r = 0; + + /* Filter out the unit: symlinks */ + for (a = l, b = l; *a; a++) { + if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) + free(*a); + else { + *b = *a; + b++; + r++; + } + } + + *b = NULL; + } + + *machines = l; + return r; +} + +_public_ int sd_machine_get_class(const char *machine, char **class) { + _cleanup_free_ char *c = NULL; + const char *p; + int r; + + assert_return(machine_name_is_valid(machine), -EINVAL); + assert_return(class, -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (!c) + return -EIO; + + *class = c; + c = NULL; + + return 0; +} + +_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { + _cleanup_free_ char *netif = NULL; + size_t l, allocated = 0, nr = 0; + int *ni = NULL; + const char *p, *word, *state; + int r; + + assert_return(machine_name_is_valid(machine), -EINVAL); + assert_return(ifindices, -EINVAL); + + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (!netif) { + *ifindices = NULL; + return 0; + } + + FOREACH_WORD(word, l, netif, state) { + char buf[l+1]; + int ifi; + + *(char*) (mempcpy(buf, word, l)) = 0; + + if (parse_ifindex(buf, &ifi) < 0) + continue; + + if (!GREEDY_REALLOC(ni, allocated, nr+1)) { + free(ni); + return -ENOMEM; + } + + ni[nr++] = ifi; + } + + *ifindices = ni; + return nr; +} + +static inline int MONITOR_TO_FD(sd_login_monitor *m) { + return (int) (unsigned long) m - 1; +} + +static inline sd_login_monitor* FD_TO_MONITOR(int fd) { + return (sd_login_monitor*) (unsigned long) (fd + 1); +} + +_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) { + int fd, k; + bool good = false; + + assert_return(m, -EINVAL); + + fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (fd < 0) + return -errno; + + if (!category || streq(category, "seat")) { + k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "session")) { + k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "uid")) { + k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!category || streq(category, "machine")) { + k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE); + if (k < 0) { + safe_close(fd); + return -errno; + } + + good = true; + } + + if (!good) { + close_nointr(fd); + return -EINVAL; + } + + *m = FD_TO_MONITOR(fd); + return 0; +} + +_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { + int fd; + + if (!m) + return NULL; + + fd = MONITOR_TO_FD(m); + close_nointr(fd); + + return NULL; +} + +_public_ int sd_login_monitor_flush(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + return flush_fd(MONITOR_TO_FD(m)); +} + +_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + return MONITOR_TO_FD(m); +} + +_public_ int sd_login_monitor_get_events(sd_login_monitor *m) { + + assert_return(m, -EINVAL); + + /* For now we will only return POLLIN here, since we don't + * need anything else ever for inotify. However, let's have + * this API to keep our options open should we later on need + * it. */ + return POLLIN; +} + +_public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) { + + assert_return(m, -EINVAL); + assert_return(timeout_usec, -EINVAL); + + /* For now we will only return (uint64_t) -1, since we don't + * need any timeout. However, let's have this API to keep our + * options open should we later on need it. */ + *timeout_usec = (uint64_t) -1; + return 0; +} diff --git a/src/libsystemd/src/sd-login/test-login.c b/src/libsystemd/src/sd-login/test-login.c new file mode 100644 index 0000000000..bec645242a --- /dev/null +++ b/src/libsystemd/src/sd-login/test-login.c @@ -0,0 +1,264 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +static void test_login(void) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *pp = NULL, *qq = NULL; + int r, k; + uid_t u, u2; + char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; + char *session; + char *state; + char *session2; + char *t; + char **seats, **sessions, **machines; + uid_t *uids; + unsigned n; + struct pollfd pollfd; + sd_login_monitor *m = NULL; + + assert_se(sd_pid_get_session(0, &session) == 0); + printf("session = %s\n", session); + + assert_se(sd_pid_get_owner_uid(0, &u2) == 0); + printf("user = "UID_FMT"\n", u2); + + assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); + printf("cgroup = %s\n", cgroup); + free(cgroup); + + display_session = NULL; + r = sd_uid_get_display(u2, &display_session); + assert_se(r >= 0 || r == -ENODATA); + printf("user's display session = %s\n", strna(display_session)); + free(display_session); + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); + sd_peer_get_session(pair[0], &pp); + sd_peer_get_session(pair[1], &qq); + assert_se(streq_ptr(pp, qq)); + + r = sd_uid_get_sessions(u2, false, &sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("sessions = %s\n", t); + free(t); + + assert_se(r == sd_uid_get_sessions(u2, false, NULL)); + + r = sd_uid_get_seats(u2, false, &seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); + assert_se(t = strv_join(seats, ", ")); + strv_free(seats); + printf("seats = %s\n", t); + free(t); + + assert_se(r == sd_uid_get_seats(u2, false, NULL)); + + r = sd_session_is_active(session); + assert_se(r >= 0); + printf("active = %s\n", yes_no(r)); + + r = sd_session_is_remote(session); + assert_se(r >= 0); + printf("remote = %s\n", yes_no(r)); + + r = sd_session_get_state(session, &state); + assert_se(r >= 0); + printf("state = %s\n", state); + free(state); + + assert_se(sd_session_get_uid(session, &u) >= 0); + printf("uid = "UID_FMT"\n", u); + assert_se(u == u2); + + assert_se(sd_session_get_type(session, &type) >= 0); + printf("type = %s\n", type); + free(type); + + assert_se(sd_session_get_class(session, &class) >= 0); + printf("class = %s\n", class); + free(class); + + display = NULL; + r = sd_session_get_display(session, &display); + assert_se(r >= 0 || r == -ENODATA); + printf("display = %s\n", strna(display)); + free(display); + + remote_user = NULL; + r = sd_session_get_remote_user(session, &remote_user); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_user = %s\n", strna(remote_user)); + free(remote_user); + + remote_host = NULL; + r = sd_session_get_remote_host(session, &remote_host); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_host = %s\n", strna(remote_host)); + free(remote_host); + + assert_se(sd_session_get_seat(session, &seat) >= 0); + printf("seat = %s\n", seat); + + r = sd_seat_can_multi_session(seat); + assert_se(r >= 0); + printf("can do multi session = %s\n", yes_no(r)); + + r = sd_seat_can_tty(seat); + assert_se(r >= 0); + printf("can do tty = %s\n", yes_no(r)); + + r = sd_seat_can_graphical(seat); + assert_se(r >= 0); + printf("can do graphical = %s\n", yes_no(r)); + + assert_se(sd_uid_get_state(u, &state) >= 0); + printf("state = %s\n", state); + + assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); + + k = sd_uid_is_on_seat(u, 1, seat); + assert_se(k >= 0); + assert_se(!!r == !!r); + + assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); + printf("session2 = %s\n", session2); + printf("uid2 = "UID_FMT"\n", u2); + + r = sd_seat_get_sessions(seat, &sessions, &uids, &n); + assert_se(r >= 0); + printf("n_sessions = %i\n", r); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("sessions = %s\n", t); + free(t); + printf("uids ="); + for (k = 0; k < (int) n; k++) + printf(" "UID_FMT, uids[k]); + printf("\n"); + free(uids); + + assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); + + free(session); + free(state); + free(session2); + free(seat); + + r = sd_get_seats(&seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); + assert_se(t = strv_join(seats, ", ")); + strv_free(seats); + printf("n_seats = %i\n", r); + printf("seats = %s\n", t); + free(t); + + assert_se(sd_get_seats(NULL) == r); + + r = sd_seat_get_active(NULL, &t, NULL); + assert_se(r >= 0); + printf("active session on current seat = %s\n", t); + free(t); + + r = sd_get_sessions(&sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, ", ")); + strv_free(sessions); + printf("n_sessions = %i\n", r); + printf("sessions = %s\n", t); + free(t); + + assert_se(sd_get_sessions(NULL) == r); + + r = sd_get_uids(&uids); + assert_se(r >= 0); + + printf("uids ="); + for (k = 0; k < r; k++) + printf(" "UID_FMT, uids[k]); + printf("\n"); + free(uids); + + printf("n_uids = %i\n", r); + assert_se(sd_get_uids(NULL) == r); + + r = sd_get_machine_names(&machines); + assert_se(r >= 0); + assert_se(r == (int) strv_length(machines)); + assert_se(t = strv_join(machines, ", ")); + strv_free(machines); + printf("n_machines = %i\n", r); + printf("machines = %s\n", t); + free(t); + + r = sd_login_monitor_new("session", &m); + assert_se(r >= 0); + + for (n = 0; n < 5; n++) { + usec_t timeout, nw; + + zero(pollfd); + assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0); + assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0); + + assert_se(sd_login_monitor_get_timeout(m, &timeout) >= 0); + + nw = now(CLOCK_MONOTONIC); + + r = poll(&pollfd, 1, + timeout == (uint64_t) -1 ? -1 : + timeout > nw ? (int) ((timeout - nw) / 1000) : + 0); + + assert_se(r >= 0); + + sd_login_monitor_flush(m); + printf("Wake!\n"); + } + + sd_login_monitor_unref(m); +} + +int main(int argc, char* argv[]) { + log_parse_environment(); + log_open(); + + test_login(); + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/Makefile b/src/libsystemd/src/sd-netlink/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-netlink/local-addresses.c b/src/libsystemd/src/sd-netlink/local-addresses.c new file mode 100644 index 0000000000..81e99e0e5d --- /dev/null +++ b/src/libsystemd/src/sd-netlink/local-addresses.c @@ -0,0 +1,275 @@ +/*** + This file is part of systemd. + + Copyright 2008-2011 Lennart Poettering + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/macro.h" + +#include "local-addresses.h" +#include "netlink-util.h" +#include "sd-netlink.h" + +static int address_compare(const void *_a, const void *_b) { + const struct local_address *a = _a, *b = _b; + + /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */ + + if (a->family == AF_INET && b->family == AF_INET6) + return -1; + if (a->family == AF_INET6 && b->family == AF_INET) + return 1; + + if (a->scope < b->scope) + return -1; + if (a->scope > b->scope) + return 1; + + if (a->metric < b->metric) + return -1; + if (a->metric > b->metric) + return 1; + + if (a->ifindex < b->ifindex) + return -1; + if (a->ifindex > b->ifindex) + return 1; + + return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); +} + +int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) { + _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; + int r; + + assert(ret); + + if (context) + rtnl = sd_netlink_ref(context); + else { + r = sd_netlink_open(&rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_netlink_message_next(m)) { + struct local_address *a; + unsigned char flags; + uint16_t type; + int ifi, family; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + if (type != RTM_NEWADDR) + continue; + + r = sd_rtnl_message_addr_get_ifindex(m, &ifi); + if (r < 0) + return r; + if (ifindex > 0 && ifi != ifindex) + continue; + + r = sd_rtnl_message_addr_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) + continue; + + r = sd_rtnl_message_addr_get_flags(m, &flags); + if (r < 0) + return r; + if (flags & IFA_F_DEPRECATED) + continue; + + if (!GREEDY_REALLOC0(list, n_allocated, n_list+1)) + return -ENOMEM; + + a = list + n_list; + + r = sd_rtnl_message_addr_get_scope(m, &a->scope); + if (r < 0) + return r; + + if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE)) + continue; + + switch (family) { + + case AF_INET: + r = sd_netlink_message_read_in_addr(m, IFA_LOCAL, &a->address.in); + if (r < 0) { + r = sd_netlink_message_read_in_addr(m, IFA_ADDRESS, &a->address.in); + if (r < 0) + continue; + } + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(m, IFA_LOCAL, &a->address.in6); + if (r < 0) { + r = sd_netlink_message_read_in6_addr(m, IFA_ADDRESS, &a->address.in6); + if (r < 0) + continue; + } + break; + + default: + continue; + } + + a->ifindex = ifi; + a->family = family; + + n_list++; + }; + + qsort_safe(list, n_list, sizeof(struct local_address), address_compare); + + *ret = list; + list = NULL; + + return (int) n_list; +} + +int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) { + _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; + int r; + + assert(ret); + + if (context) + rtnl = sd_netlink_ref(context); + else { + r = sd_netlink_open(&rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); + if (r < 0) + return r; + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_netlink_message_next(m)) { + struct local_address *a; + uint16_t type; + unsigned char dst_len, src_len; + uint32_t ifi; + int family; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + if (type != RTM_NEWROUTE) + continue; + + /* We only care for default routes */ + r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); + if (r < 0) + return r; + if (dst_len != 0) + continue; + + r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); + if (r < 0) + return r; + if (src_len != 0) + continue; + + r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); + if (r < 0) + return r; + if (ifindex > 0 && (int) ifi != ifindex) + continue; + + r = sd_rtnl_message_route_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) + continue; + + if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) + return -ENOMEM; + + a = list + n_list; + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); + if (r < 0) + continue; + + break; + case AF_INET6: + r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); + if (r < 0) + continue; + + break; + default: + continue; + } + + sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric); + + a->ifindex = ifi; + a->family = family; + + n_list++; + } + + if (n_list > 0) + qsort(list, n_list, sizeof(struct local_address), address_compare); + + *ret = list; + list = NULL; + + return (int) n_list; +} diff --git a/src/libsystemd/src/sd-netlink/local-addresses.h b/src/libsystemd/src/sd-netlink/local-addresses.h new file mode 100644 index 0000000000..5eb0683131 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/local-addresses.h @@ -0,0 +1,36 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2008-2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + + +#include "basic/in-addr-util.h" + +#include "sd-netlink.h" + +struct local_address { + int family, ifindex; + unsigned char scope; + uint32_t metric; + union in_addr_union address; +}; + +int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); + +int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret); diff --git a/src/libsystemd/src/sd-netlink/netlink-internal.h b/src/libsystemd/src/sd-netlink/netlink-internal.h new file mode 100644 index 0000000000..69511f87ad --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-internal.h @@ -0,0 +1,137 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include + +#include "basic/list.h" +#include "basic/prioq.h" +#include "basic/refcnt.h" + +#include "netlink-types.h" +#include "sd-netlink.h" + +#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) + +#define RTNL_WQUEUE_MAX 1024 +#define RTNL_RQUEUE_MAX 64*1024 + +#define RTNL_CONTAINER_DEPTH 32 + +struct reply_callback { + sd_netlink_message_handler_t callback; + void *userdata; + usec_t timeout; + uint64_t serial; + unsigned prioq_idx; +}; + +struct match_callback { + sd_netlink_message_handler_t callback; + uint16_t type; + void *userdata; + + LIST_FIELDS(struct match_callback, match_callbacks); +}; + +struct sd_netlink { + RefCount n_ref; + + int fd; + + union { + struct sockaddr sa; + 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; + + sd_netlink_message **rqueue_partial; + unsigned rqueue_partial_size; + size_t rqueue_partial_allocated; + + struct nlmsghdr *rbuffer; + size_t rbuffer_allocated; + + bool processing:1; + + uint32_t serial; + + struct Prioq *reply_callbacks_prioq; + Hashmap *reply_callbacks; + + LIST_HEAD(struct match_callback, match_callbacks); + + pid_t original_pid; + + sd_event_source *io_event_source; + sd_event_source *time_event_source; + sd_event_source *exit_event_source; + sd_event *event; +}; + +struct netlink_attribute { + size_t offset; /* offset from hdr to attribute */ + bool nested:1; + bool net_byteorder:1; +}; + +struct netlink_container { + const struct NLTypeSystem *type_system; /* the type system of the container */ + size_t offset; /* offset from hdr to the start of the container */ + struct netlink_attribute *attributes; + unsigned short n_attributes; /* number of attributes in container */ +}; + +struct sd_netlink_message { + RefCount n_ref; + + sd_netlink *rtnl; + + struct nlmsghdr *hdr; + struct netlink_container containers[RTNL_CONTAINER_DEPTH]; + unsigned n_containers; /* number of containers */ + bool sealed:1; + bool broadcast:1; + + sd_netlink_message *next; /* next in a chain of multi-part messages */ +}; + +int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type); +int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); + +int socket_open(int family); +int socket_bind(sd_netlink *nl); +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); + +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 NETLINK_DONT_DESTROY(rtnl) \ + _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl) diff --git a/src/libsystemd/src/sd-netlink/netlink-message.c b/src/libsystemd/src/sd-netlink/netlink-message.c new file mode 100644 index 0000000000..352df2f35a --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-message.c @@ -0,0 +1,963 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/missing.h" +#include "basic/refcnt.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "sd-netlink.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; + +#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) +#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) + +int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { + sd_netlink_message *m; + + assert_return(ret, -EINVAL); + + /* Note that 'rtnl' is currently unused, if we start using it internally + we must take care to avoid problems due to mutual references between + buses and their queued messages. See sd-bus. + */ + + m = new0(sd_netlink_message, 1); + if (!m) + return -ENOMEM; + + m->n_ref = REFCNT_INIT; + + m->sealed = false; + + *ret = m; + + return 0; +} + +int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + const NLType *nl_type; + size_t size; + int r; + + r = type_system_get_type(&type_system_root, &nl_type, type); + if (r < 0) + return r; + + if (type_get_type(nl_type) != NETLINK_TYPE_NESTED) + return -EINVAL; + + r = message_new_empty(rtnl, &m); + if (r < 0) + return r; + + size = NLMSG_SPACE(type_get_size(nl_type)); + + assert(size >= sizeof(struct nlmsghdr)); + m->hdr = malloc0(size); + if (!m->hdr) + return -ENOMEM; + + m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + type_get_type_system(nl_type, &m->containers[0].type_system); + m->hdr->nlmsg_len = size; + m->hdr->nlmsg_type = type; + + *ret = m; + m = NULL; + + return 0; +} + +int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(m->hdr->nlmsg_type == RTM_GETLINK || + m->hdr->nlmsg_type == RTM_GETADDR || + m->hdr->nlmsg_type == RTM_GETROUTE || + m->hdr->nlmsg_type == RTM_GETNEIGH, + -EINVAL); + + SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); + + return 0; +} + +sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) { + if (m) + assert_se(REFCNT_INC(m->n_ref) >= 2); + + return m; +} + +sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { + sd_netlink_message *t; + + while (m && REFCNT_DEC(m->n_ref) == 0) { + unsigned i; + + free(m->hdr); + + for (i = 0; i <= m->n_containers; i++) + free(m->containers[i].attributes); + + t = m; + m = m->next; + free(t); + } + + return NULL; +} + +int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) { + assert_return(m, -EINVAL); + assert_return(type, -EINVAL); + + *type = m->hdr->nlmsg_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); + + return m->broadcast; +} + +/* If successful the updated message will be correctly aligned, if + unsuccessful the old message is untouched. */ +static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { + uint32_t rta_length; + size_t message_length, padding_length; + struct nlmsghdr *new_hdr; + struct rtattr *rta; + char *padding; + unsigned i; + int offset; + + assert(m); + assert(m->hdr); + assert(!m->sealed); + assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); + assert(!data || data_length); + + /* get offset of the new attribute */ + offset = m->hdr->nlmsg_len; + + /* get the size of the new rta attribute (with padding at the end) */ + rta_length = RTA_LENGTH(data_length); + + /* get the new message size (with padding at the end) */ + message_length = offset + RTA_ALIGN(rta_length); + + /* realloc to fit the new attribute */ + new_hdr = realloc(m->hdr, message_length); + if (!new_hdr) + return -ENOMEM; + m->hdr = new_hdr; + + /* get pointer to the attribute we are about to add */ + rta = (struct rtattr *) ((uint8_t *) m->hdr + offset); + + /* if we are inside containers, extend them */ + for (i = 0; i < m->n_containers; i++) + GET_CONTAINER(m, i)->rta_len += message_length - offset; + + /* fill in the attribute */ + rta->rta_type = type; + rta->rta_len = rta_length; + if (data) + /* we don't deal with the case where the user lies about the type + * and gives us too little data (so don't do that) + */ + padding = mempcpy(RTA_DATA(rta), data, data_length); + + else + /* if no data was passed, make sure we still initialize the padding + note that we can have data_length > 0 (used by some containers) */ + padding = RTA_DATA(rta); + + /* make sure also the padding at the end of the message is initialized */ + padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; + memzero(padding, padding_length); + + /* update message size */ + m->hdr->nlmsg_len = message_length; + + return offset; +} + +static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) { + const NLType *type; + int r; + + assert(m); + + r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type); + if (r < 0) + return r; + + if (type_get_type(type) != data_type) + return -EINVAL; + + if (out_size) + *out_size = type_get_size(type); + return 0; +} + +int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) { + size_t length, size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING); + if (r < 0) + return r; + + if (size) { + length = strnlen(data, size+1); + if (length > size) + return -EINVAL; + } else + length = strlen(data); + + r = add_rtattr(m, type, data, length + 1); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) { + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG); + if (r < 0) + return r; + + r = add_rtattr(m, type, NULL, 0); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint8_t)); + if (r < 0) + return r; + + return 0; +} + + +int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint16_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); + if (r < 0) + return r; + + r = add_rtattr(m, type, &data, sizeof(uint32_t)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = add_rtattr(m, type, data, len); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, sizeof(struct in_addr)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, sizeof(struct in6_addr)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(data, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = add_rtattr(m, type, data, ETH_ALEN); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(info, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); + if (r < 0) + return r; + + r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo)); + if (r < 0) + return r; + + return 0; +} + +int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) { + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE); + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED); + if (r < 0) { + const NLTypeSystemUnion *type_system_union; + int family; + + r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION); + if (r < 0) + return r; + + r = sd_rtnl_message_get_family(m, &family); + if (r < 0) + return r; + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); + if (r < 0) + return r; + + r = type_system_union_protocol_get_type_system(type_system_union, + &m->containers[m->n_containers + 1].type_system, + family); + if (r < 0) + return r; + } else { + r = type_system_get_type_system(m->containers[m->n_containers].type_system, + &m->containers[m->n_containers + 1].type_system, + type); + if (r < 0) + return r; + } + + r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); + if (r < 0) + return r; + + m->containers[m->n_containers++].offset = r; + + return 0; +} + +int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) { + const NLTypeSystemUnion *type_system_union; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type); + if (r < 0) + return r; + + r = type_system_union_get_type_system(type_system_union, + &m->containers[m->n_containers + 1].type_system, + key); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, type_system_union->match, key); + if (r < 0) + return r; + + /* do we evere need non-null size */ + r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); + if (r < 0) + return r; + + m->containers[m->n_containers++].offset = r; + + return 0; +} + + +int sd_netlink_message_close_container(sd_netlink_message *m) { + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers > 0, -EINVAL); + + m->containers[m->n_containers].type_system = NULL; + m->n_containers--; + + return 0; +} + +static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) { + struct netlink_attribute *attribute; + struct rtattr *rta; + + assert_return(m, -EINVAL); + assert_return(m->sealed, -EPERM); + assert_return(data, -EINVAL); + assert(m->n_containers < RTNL_CONTAINER_DEPTH); + assert(m->containers[m->n_containers].attributes); + assert(type < m->containers[m->n_containers].n_attributes); + + attribute = &m->containers[m->n_containers].attributes[type]; + + if (!attribute->offset) + return -ENODATA; + + rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); + + *data = RTA_DATA(rta); + + if (net_byteorder) + *net_byteorder = attribute->net_byteorder; + + return RTA_PAYLOAD(rta); +} + +int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if (strnlen(attr_data, r) >= (size_t) r) + return -EIO; + + if (data) + *data = (const char *) attr_data; + + return 0; +} + +int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t) r < sizeof(uint8_t)) + return -EIO; + + if (data) + *data = *(uint8_t *) attr_data; + + return 0; +} + +int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) { + void *attr_data; + bool net_byteorder; + int r; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); + if (r < 0) + return r; + else if ((size_t) r < sizeof(uint16_t)) + return -EIO; + + if (data) { + if (net_byteorder) + *data = be16toh(*(uint16_t *) attr_data); + else + *data = *(uint16_t *) attr_data; + } + + return 0; +} + +int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) { + void *attr_data; + bool net_byteorder; + int r; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); + if (r < 0) + return r; + else if ((size_t)r < sizeof(uint32_t)) + return -EIO; + + if (data) { + if (net_byteorder) + *data = be32toh(*(uint32_t *) attr_data); + else + *data = *(uint32_t *) attr_data; + } + + return 0; +} + +int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct ether_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct ether_addr)); + + return 0; +} + +int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct ifa_cacheinfo)) + return -EIO; + + if (info) + memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); + + return 0; +} + +int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct in_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct in_addr)); + + return 0; +} + +int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) { + int r; + void *attr_data; + + assert_return(m, -EINVAL); + + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + if (r < 0) + return r; + + r = netlink_message_read_internal(m, type, &attr_data, NULL); + if (r < 0) + return r; + else if ((size_t)r < sizeof(struct in6_addr)) + return -EIO; + + if (data) + memcpy(data, attr_data, sizeof(struct in6_addr)); + + return 0; +} + +static int netlink_container_parse(sd_netlink_message *m, + struct netlink_container *container, + int count, + struct rtattr *rta, + unsigned int rt_len) { + _cleanup_free_ struct netlink_attribute *attributes = NULL; + + attributes = new0(struct netlink_attribute, count); + if (!attributes) + return -ENOMEM; + + for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { + unsigned short type; + + type = RTA_TYPE(rta); + + /* if the kernel is newer than the headers we used + when building, we ignore out-of-range attributes */ + if (type >= count) + continue; + + if (attributes[type].offset) + log_debug("rtnl: message parse - overwriting repeated attribute"); + + attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; + attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED; + attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER; + } + + container->attributes = attributes; + attributes = NULL; + container->n_attributes = count; + + return 0; +} + +int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) { + const NLType *nl_type; + const NLTypeSystem *type_system; + void *container; + uint16_t type; + size_t size; + int r; + + assert_return(m, -EINVAL); + assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL); + + r = type_system_get_type(m->containers[m->n_containers].type_system, + &nl_type, + type_id); + if (r < 0) + return r; + + type = type_get_type(nl_type); + + if (type == NETLINK_TYPE_NESTED) { + r = type_system_get_type_system(m->containers[m->n_containers].type_system, + &type_system, + type_id); + if (r < 0) + return r; + } else if (type == NETLINK_TYPE_UNION) { + const NLTypeSystemUnion *type_system_union; + + r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, + &type_system_union, + type_id); + if (r < 0) + return r; + + switch (type_system_union->match_type) { + case NL_MATCH_SIBLING: + { + const char *key; + + r = sd_netlink_message_read_string(m, type_system_union->match, &key); + if (r < 0) + return r; + + r = type_system_union_get_type_system(type_system_union, + &type_system, + key); + if (r < 0) + return r; + + break; + } + case NL_MATCH_PROTOCOL: + { + int family; + + r = sd_rtnl_message_get_family(m, &family); + if (r < 0) + return r; + + r = type_system_union_protocol_get_type_system(type_system_union, + &type_system, + family); + if (r < 0) + return r; + + break; + } + default: + assert_not_reached("sd-netlink: invalid type system union type"); + } + } else + return -EINVAL; + + r = netlink_message_read_internal(m, type_id, &container, NULL); + if (r < 0) + return r; + else + size = (size_t)r; + + m->n_containers++; + + r = netlink_container_parse(m, + &m->containers[m->n_containers], + type_system_get_count(type_system), + container, + size); + if (r < 0) { + m->n_containers--; + return r; + } + + m->containers[m->n_containers].type_system = type_system; + + return 0; +} + +int sd_netlink_message_exit_container(sd_netlink_message *m) { + assert_return(m, -EINVAL); + assert_return(m->sealed, -EINVAL); + assert_return(m->n_containers > 0, -EINVAL); + + m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); + m->containers[m->n_containers].type_system = NULL; + + m->n_containers--; + + return 0; +} + +uint32_t rtnl_message_get_serial(sd_netlink_message *m) { + assert(m); + assert(m->hdr); + + return m->hdr->nlmsg_seq; +} + +int sd_netlink_message_is_error(sd_netlink_message *m) { + assert_return(m, 0); + assert_return(m->hdr, 0); + + return m->hdr->nlmsg_type == NLMSG_ERROR; +} + +int sd_netlink_message_get_errno(sd_netlink_message *m) { + struct nlmsgerr *err; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + + if (!sd_netlink_message_is_error(m)) + return 0; + + err = NLMSG_DATA(m->hdr); + + return err->error; +} + +int sd_netlink_message_rewind(sd_netlink_message *m) { + const NLType *nl_type; + uint16_t type; + size_t size; + unsigned i; + int r; + + assert_return(m, -EINVAL); + + /* don't allow appending to message once parsed */ + if (!m->sealed) + rtnl_message_seal(m); + + for (i = 1; i <= m->n_containers; i++) + m->containers[i].attributes = mfree(m->containers[i].attributes); + + m->n_containers = 0; + + if (m->containers[0].attributes) + /* top-level attributes have already been parsed */ + return 0; + + assert(m->hdr); + + r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type); + if (r < 0) + return r; + + type = type_get_type(nl_type); + size = type_get_size(nl_type); + + if (type == NETLINK_TYPE_NESTED) { + const NLTypeSystem *type_system; + + type_get_type_system(nl_type, &type_system); + + m->containers[0].type_system = type_system; + + r = netlink_container_parse(m, + &m->containers[m->n_containers], + type_system_get_count(type_system), + (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), + NLMSG_PAYLOAD(m->hdr, size)); + if (r < 0) + return r; + } + + return 0; +} + +void rtnl_message_seal(sd_netlink_message *m) { + assert(m); + assert(!m->sealed); + + m->sealed = true; +} + +sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m) { + assert_return(m, NULL); + + return m->next; +} diff --git a/src/libsystemd/src/sd-netlink/netlink-socket.c b/src/libsystemd/src/sd-netlink/netlink-socket.c new file mode 100644 index 0000000000..ec3513bcf8 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-socket.c @@ -0,0 +1,474 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/formats-util.h" +#include "basic/missing.h" +#include "basic/refcnt.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "sd-netlink.h" + +int socket_open(int family) { + int fd; + + fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family); + if (fd < 0) + return -errno; + + 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; + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one)); + if (r < 0) + return -errno; + + addrlen = sizeof(nl->sockaddr); + + r = bind(nl->fd, &nl->sockaddr.sa, addrlen); + /* ignore EINVAL to allow opening an already bound socket */ + if (r < 0 && errno != EINVAL) + return -errno; + + r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); + 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; +} + +static int broadcast_group_join(sd_netlink *nl, unsigned group) { + int r; + + assert(nl); + assert(nl->fd >= 0); + assert(group > 0); + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); + if (r < 0) + return -errno; + + 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 { + struct sockaddr sa; + struct sockaddr_nl nl; + } addr = { + .nl.nl_family = AF_NETLINK, + }; + ssize_t k; + + assert(nl); + assert(m); + assert(m->hdr); + + k = sendto(nl->fd, m->hdr, m->hdr->nlmsg_len, + 0, &addr.sa, sizeof(addr)); + if (k < 0) + return -errno; + + return k; +} + +static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) { + union sockaddr_union sender; + uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))]; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = 1, + .msg_name = &sender, + .msg_namelen = sizeof(sender), + .msg_control = cmsg_buffer, + .msg_controllen = sizeof(cmsg_buffer), + }; + struct cmsghdr *cmsg; + uint32_t group = 0; + int r; + + assert(fd >= 0); + assert(iov); + + r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); + if (r < 0) { + /* no data */ + if (errno == ENOBUFS) + log_debug("rtnl: kernel receive buffer overrun"); + else if (errno == EAGAIN) + log_debug("rtnl: no data in socket"); + + return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + } + + if (sender.nl.nl_pid != 0) { + /* not from the kernel, ignore */ + log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid); + + if (peek) { + /* drop the message */ + r = recvmsg(fd, &msg, 0); + if (r < 0) + return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + } + + return 0; + } + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_NETLINK && + cmsg->cmsg_type == NETLINK_PKTINFO && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) { + struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg); + + /* multi-cast group */ + group = pktinfo->group; + } + } + + if (_group) + *_group = group; + + return r; +} + +/* On success, the number of bytes received is returned and *ret points to the received message + * which has a valid header and the correct size. + * If nothing useful was received 0 is returned. + * On failure, a negative error code is returned. + */ +int socket_read_message(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL; + struct iovec iov = {}; + uint32_t group = 0; + bool multi_part = false, done = false; + struct nlmsghdr *new_msg; + size_t len; + int r; + unsigned i = 0; + + assert(rtnl); + assert(rtnl->rbuffer); + assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); + + /* read nothing, just get the pending message size */ + r = socket_recv_message(rtnl->fd, &iov, NULL, true); + if (r <= 0) + return r; + else + len = (size_t)r; + + /* make room for the pending message */ + if (!greedy_realloc((void **)&rtnl->rbuffer, + &rtnl->rbuffer_allocated, + len, sizeof(uint8_t))) + return -ENOMEM; + + iov.iov_base = rtnl->rbuffer; + iov.iov_len = rtnl->rbuffer_allocated; + + /* read the pending message */ + r = socket_recv_message(rtnl->fd, &iov, &group, false); + if (r <= 0) + return r; + else + len = (size_t)r; + + if (len > rtnl->rbuffer_allocated) + /* message did not fit in read buffer */ + return -EIO; + + if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) { + multi_part = true; + + for (i = 0; i < rtnl->rqueue_partial_size; i++) { + if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) == + rtnl->rbuffer->nlmsg_seq) { + first = rtnl->rqueue_partial[i]; + break; + } + } + } + + for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) { + _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) + /* not broadcast and not for us */ + continue; + + if (new_msg->nlmsg_type == NLMSG_NOOP) + /* silently drop noop messages */ + continue; + + if (new_msg->nlmsg_type == NLMSG_DONE) { + /* finished reading multi-part message */ + done = true; + + /* if first is not defined, put NLMSG_DONE into the receive queue. */ + if (first) + continue; + } + + /* check that we support this message type */ + r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type); + if (r < 0) { + if (r == -EOPNOTSUPP) + log_debug("sd-netlink: ignored message with unknown type: %i", + new_msg->nlmsg_type); + + continue; + } + + /* check that the size matches the message type */ + if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) { + log_debug("sd-netlink: message larger than expected, dropping"); + continue; + } + + r = message_new_empty(rtnl, &m); + if (r < 0) + return r; + + m->broadcast = !!group; + + m->hdr = memdup(new_msg, new_msg->nlmsg_len); + if (!m->hdr) + return -ENOMEM; + + /* seal and parse the top-level message */ + r = sd_netlink_message_rewind(m); + if (r < 0) + return r; + + /* push the message onto the multi-part message stack */ + if (first) + m->next = first; + first = m; + m = NULL; + } + + if (len) + log_debug("sd-netlink: discarding %zu bytes of incoming message", len); + + if (!first) + return 0; + + if (!multi_part || done) { + /* we got a complete message, push it on the read queue */ + r = rtnl_rqueue_make_room(rtnl); + if (r < 0) + return r; + + rtnl->rqueue[rtnl->rqueue_size++] = first; + first = NULL; + + if (multi_part && (i < rtnl->rqueue_partial_size)) { + /* remove the message form the partial read queue */ + memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, + sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1)); + rtnl->rqueue_partial_size--; + } + + return 1; + } else { + /* we only got a partial multi-part message, push it on the + partial read queue */ + if (i < rtnl->rqueue_partial_size) { + rtnl->rqueue_partial[i] = first; + } else { + r = rtnl_rqueue_partial_make_room(rtnl); + if (r < 0) + return r; + + rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first; + } + first = NULL; + + return 0; + } +} diff --git a/src/libsystemd/src/sd-netlink/netlink-types.c b/src/libsystemd/src/sd-netlink/netlink-types.c new file mode 100644 index 0000000000..7aaecd2272 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-types.c @@ -0,0 +1,694 @@ +/*** + 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 . +***/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/string-table.h" +#include "basic/util.h" + +#include "netlink-types.h" + +/* Maximum ARP IP target defined in kernel */ +#define BOND_MAX_ARP_TARGETS 16 + +typedef enum { + BOND_ARP_TARGETS_0, + BOND_ARP_TARGETS_1, + BOND_ARP_TARGETS_2, + BOND_ARP_TARGETS_3, + BOND_ARP_TARGETS_4, + BOND_ARP_TARGETS_5, + BOND_ARP_TARGETS_6, + BOND_ARP_TARGETS_7, + BOND_ARP_TARGETS_8, + BOND_ARP_TARGETS_9, + BOND_ARP_TARGETS_10, + BOND_ARP_TARGETS_11, + BOND_ARP_TARGETS_12, + BOND_ARP_TARGETS_13, + BOND_ARP_TARGETS_14, + BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS, +} BondArpTargets; + +struct NLType { + uint16_t type; + size_t size; + const NLTypeSystem *type_system; + const NLTypeSystemUnion *type_system_union; +}; + +struct NLTypeSystem { + uint16_t count; + const NLType *types; +}; + +static const NLTypeSystem rtnl_link_type_system; + +static const NLType empty_types[1] = { + /* fake array to avoid .types==NULL, which denotes invalid type-systems */ +}; + +static const NLTypeSystem empty_type_system = { + .count = 0, + .types = empty_types, +}; + +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_MODE] = { .type = NETLINK_TYPE_U16 }, +}; + +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_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 }, + [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, +}; + +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) }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED }, +*/ + [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, +}; + +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 }, + [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32}, + [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_U32}, + [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG }, + [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, +}; + +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 }, + [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 }, + [BOND_ARP_TARGETS_MAX] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_bond_arp_type_system = { + .count = ELEMENTSOF(rtnl_bond_arp_target_types), + .types = rtnl_bond_arp_target_types, +}; + +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 }, + [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system }, + [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, +}; + +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 }, + [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, +}; + +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 }, + [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, +}; + +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 }, + [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, +}; + +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 }, + [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLType rtnl_link_info_data_vrf_types[] = { + [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 }, +}; + +/* 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_BOND] = "bond", + [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", + [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", + [NL_UNION_LINK_INFO_DATA_VETH] = "veth", + [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy", + [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", + [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap", + [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan", + [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan", + [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip", + [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre", + [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap", + [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre", + [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap", + [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit", + [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti", + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6", + [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", + [NL_UNION_LINK_INFO_DATA_VRF] = "vrf", +}; + +DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); + +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), + .types = rtnl_link_info_data_bridge_types }, + [NL_UNION_LINK_INFO_DATA_VLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types), + .types = rtnl_link_info_data_vlan_types }, + [NL_UNION_LINK_INFO_DATA_VETH] = { .count = ELEMENTSOF(rtnl_link_info_data_veth_types), + .types = rtnl_link_info_data_veth_types }, + [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_MACVTAP] = { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types), + .types = rtnl_link_info_data_macvlan_types }, + [NL_UNION_LINK_INFO_DATA_IPVLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types), + .types = rtnl_link_info_data_ipvlan_types }, + [NL_UNION_LINK_INFO_DATA_VXLAN] = { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types), + .types = rtnl_link_info_data_vxlan_types }, + [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types), + .types = rtnl_link_info_data_ipgre_types }, + [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types), + .types = rtnl_link_info_data_iptun_types }, + [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types), + .types = rtnl_link_info_data_ipvti_types }, + [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types), + .types = rtnl_link_info_data_ip6tnl_types }, + + [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types), + .types = rtnl_link_info_data_vrf_types }, + +}; + +static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { + .num = _NL_UNION_LINK_INFO_DATA_MAX, + .lookup = nl_union_link_info_data_from_string, + .type_systems = rtnl_link_info_data_type_systems, + .match_type = NL_MATCH_SIBLING, + .match = IFLA_INFO_KIND, +}; + +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}, +/* + [IFLA_INFO_XSTATS], + [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING }, + [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED }, +*/ +}; + +static const NLTypeSystem rtnl_link_info_type_system = { + .count = ELEMENTSOF(rtnl_link_info_types), + .types = rtnl_link_info_types, +}; + +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 }, + [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, +}; + +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 }, +}; + +static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { + .num = AF_MAX, + .type_systems = rtnl_prot_info_type_systems, + .match_type = NL_MATCH_PROTOCOL, +}; + +static const struct NLType rtnl_af_spec_inet6_types[] = { + [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, +/* + IFLA_INET6_CONF, + IFLA_INET6_STATS, + IFLA_INET6_MCAST, + IFLA_INET6_CACHEINFO, + IFLA_INET6_ICMP6STATS, +*/ + [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 }, +}; + +static const NLTypeSystem rtnl_af_spec_inet6_type_system = { + .count = ELEMENTSOF(rtnl_af_spec_inet6_types), + .types = rtnl_af_spec_inet6_types, +}; + +static const NLType rtnl_af_spec_types[] = { + [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, +}; + +static const NLTypeSystem rtnl_af_spec_type_system = { + .count = ELEMENTSOF(rtnl_af_spec_types), + .types = rtnl_af_spec_types, +}; + +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 }, + [IFLA_MTU] = { .type = NETLINK_TYPE_U32 }, + [IFLA_LINK] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_QDISC], + [IFLA_STATS], + [IFLA_COST], + [IFLA_PRIORITY], +*/ + [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_WIRELESS], +*/ + [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union }, + [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 }, +/* + [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, +*/ + [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system }, + [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 }, + [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 }, +/* + [IFLA_NUM_VF], + [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, }, + [IFLA_STATS64], + [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED }, + [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED }, +*/ + [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_type_system }, +/* + [IFLA_VF_PORTS], + [IFLA_PORT_SELF], + [IFLA_AF_SPEC], +*/ + [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 }, + [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 }, + [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 }, + [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 }, +/* + [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, +*/ +}; + +static const NLTypeSystem rtnl_link_type_system = { + .count = ELEMENTSOF(rtnl_link_types), + .types = rtnl_link_types, +}; + +/* 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[] = { + [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, + [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ + [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) }, +/* + [IFA_ANYCAST], + [IFA_MULTICAST], +*/ + [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_address_type_system = { + .count = ELEMENTSOF(rtnl_address_types), + .types = rtnl_address_types, +}; + +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 }, + [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, + [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, + [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, + [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ +/* + [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED }, + [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, +*/ + [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */ +/* + RTA_CACHEINFO, + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, +*/ + [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, + +}; + +static const NLTypeSystem rtnl_route_type_system = { + .count = ELEMENTSOF(rtnl_route_types), + .types = rtnl_route_types, +}; + +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) }, + [NDA_PROBES] = { .type = NETLINK_TYPE_U32 }, + [NDA_VLAN] = { .type = NETLINK_TYPE_U16 }, + [NDA_PORT] = { .type = NETLINK_TYPE_U16 }, + [NDA_VNI] = { .type = NETLINK_TYPE_U32 }, + [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_neigh_type_system = { + .count = ELEMENTSOF(rtnl_neigh_types), + .types = rtnl_neigh_types, +}; + +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) }, + [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, +}; + +const NLTypeSystem type_system_root = { + .count = ELEMENTSOF(rtnl_types), + .types = rtnl_types, +}; + +uint16_t type_get_type(const NLType *type) { + assert(type); + return type->type; +} + +size_t type_get_size(const NLType *type) { + assert(type); + return type->size; +} + +void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) { + assert(nl_type); + assert(ret); + assert(nl_type->type == NETLINK_TYPE_NESTED); + assert(nl_type->type_system); + + *ret = nl_type->type_system; +} + +void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) { + assert(nl_type); + assert(ret); + assert(nl_type->type == NETLINK_TYPE_UNION); + assert(nl_type->type_system_union); + + *ret = nl_type->type_system_union; +} + +uint16_t type_system_get_count(const NLTypeSystem *type_system) { + assert(type_system); + return type_system->count; +} + +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { + const NLType *nl_type; + + assert(ret); + assert(type_system); + assert(type_system->types); + + if (type >= type_system->count) + return -EOPNOTSUPP; + + nl_type = &type_system->types[type]; + + if (nl_type->type == NETLINK_TYPE_UNSPEC) + return -EOPNOTSUPP; + + *ret = nl_type; + + return 0; +} + +int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + type_get_type_system(nl_type, ret); + return 0; +} + +int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + type_get_type_system_union(nl_type, ret); + return 0; +} + +int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) { + int type; + + assert(type_system_union); + assert(type_system_union->match_type == NL_MATCH_SIBLING); + assert(type_system_union->lookup); + assert(type_system_union->type_systems); + assert(ret); + assert(key); + + type = type_system_union->lookup(key); + if (type < 0) + return -EOPNOTSUPP; + + assert(type < type_system_union->num); + + *ret = &type_system_union->type_systems[type]; + + return 0; +} + +int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) { + const NLTypeSystem *type_system; + + assert(type_system_union); + assert(type_system_union->type_systems); + assert(type_system_union->match_type == NL_MATCH_PROTOCOL); + assert(ret); + + if (protocol >= type_system_union->num) + return -EOPNOTSUPP; + + type_system = &type_system_union->type_systems[protocol]; + if (!type_system->types) + return -EOPNOTSUPP; + + *ret = type_system; + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/netlink-types.h b/src/libsystemd/src/sd-netlink/netlink-types.h new file mode 100644 index 0000000000..56b9cb143f --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-types.h @@ -0,0 +1,95 @@ +#pragma once + +/*** + 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 . +***/ + +#include "basic/macro.h" + +enum { + NETLINK_TYPE_UNSPEC, + NETLINK_TYPE_U8, /* NLA_U8 */ + NETLINK_TYPE_U16, /* NLA_U16 */ + NETLINK_TYPE_U32, /* NLA_U32 */ + NETLINK_TYPE_U64, /* NLA_U64 */ + NETLINK_TYPE_STRING, /* NLA_STRING */ + NETLINK_TYPE_FLAG, /* NLA_FLAG */ + NETLINK_TYPE_IN_ADDR, + NETLINK_TYPE_ETHER_ADDR, + NETLINK_TYPE_CACHE_INFO, + NETLINK_TYPE_NESTED, /* NLA_NESTED */ + NETLINK_TYPE_UNION, +}; + +typedef enum NLMatchType { + NL_MATCH_SIBLING, + NL_MATCH_PROTOCOL, +} NLMatchType; + +typedef struct NLTypeSystemUnion NLTypeSystemUnion; +typedef struct NLTypeSystem NLTypeSystem; +typedef struct NLType NLType; + +struct NLTypeSystemUnion { + int num; + NLMatchType match_type; + uint16_t match; + int (*lookup)(const char *); + const NLTypeSystem *type_systems; +}; + +extern const NLTypeSystem type_system_root; + +uint16_t type_get_type(const NLType *type); +size_t type_get_size(const NLType *type); +void type_get_type_system(const NLType *type, const NLTypeSystem **ret); +void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret); + +uint16_t type_system_get_count(const NLTypeSystem *type_system); +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); +int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type); +int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type); +int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key); +int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol); + +typedef enum NLUnionLinkInfoData { + NL_UNION_LINK_INFO_DATA_BOND, + NL_UNION_LINK_INFO_DATA_BRIDGE, + NL_UNION_LINK_INFO_DATA_VLAN, + NL_UNION_LINK_INFO_DATA_VETH, + NL_UNION_LINK_INFO_DATA_DUMMY, + NL_UNION_LINK_INFO_DATA_MACVLAN, + NL_UNION_LINK_INFO_DATA_MACVTAP, + NL_UNION_LINK_INFO_DATA_IPVLAN, + NL_UNION_LINK_INFO_DATA_VXLAN, + NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL, + NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL, + NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL, + NL_UNION_LINK_INFO_DATA_SIT_TUNNEL, + NL_UNION_LINK_INFO_DATA_VTI_TUNNEL, + NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL, + NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, + NL_UNION_LINK_INFO_DATA_VRF, + _NL_UNION_LINK_INFO_DATA_MAX, + _NL_UNION_LINK_INFO_DATA_INVALID = -1 +} NLUnionLinkInfoData; + +const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; +NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; diff --git a/src/libsystemd/src/sd-netlink/netlink-util.c b/src/libsystemd/src/sd-netlink/netlink-util.c new file mode 100644 index 0000000000..9248aa0b18 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-util.c @@ -0,0 +1,169 @@ +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include "netlink-internal.h" +#include "netlink-util.h" +#include "sd-netlink.h" + +int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + int r; + + assert(rtnl); + assert(ifindex > 0); + assert(name); + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(message, IFLA_IFNAME, name); + if (r < 0) + return r; + + r = sd_netlink_call(*rtnl, message, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, + const struct ether_addr *mac, unsigned mtu) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + int r; + + assert(rtnl); + assert(ifindex > 0); + + if (!alias && !mac && mtu == 0) + return 0; + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex); + if (r < 0) + return r; + + if (alias) { + r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias); + if (r < 0) + return r; + } + + if (mac) { + r = sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, mac); + if (r < 0) + return r; + } + + if (mtu > 0) { + r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu); + if (r < 0) + return r; + } + + r = sd_netlink_call(*rtnl, message, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) { + struct nlmsgerr *err; + int r; + + assert(error <= 0); + + r = message_new(NULL, ret, NLMSG_ERROR); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_seq = serial; + + err = NLMSG_DATA((*ret)->hdr); + + err->error = error; + + return 0; +} + +bool rtnl_message_type_is_neigh(uint16_t type) { + switch (type) { + case RTM_NEWNEIGH: + case RTM_GETNEIGH: + case RTM_DELNEIGH: + return true; + default: + return false; + } +} + +bool rtnl_message_type_is_route(uint16_t type) { + switch (type) { + case RTM_NEWROUTE: + case RTM_GETROUTE: + case RTM_DELROUTE: + return true; + default: + return false; + } +} + +bool rtnl_message_type_is_link(uint16_t type) { + switch (type) { + case RTM_NEWLINK: + case RTM_SETLINK: + case RTM_GETLINK: + case RTM_DELLINK: + return true; + default: + return false; + } +} + +bool rtnl_message_type_is_addr(uint16_t type) { + switch (type) { + case RTM_NEWADDR: + case RTM_GETADDR: + case RTM_DELADDR: + return true; + default: + return false; + } +} + +int rtnl_log_parse_error(int r) { + return log_error_errno(r, "Failed to parse netlink message: %m"); +} + +int rtnl_log_create_error(int r) { + return log_error_errno(r, "Failed to create netlink message: %m"); +} diff --git a/src/libsystemd/src/sd-netlink/netlink-util.h b/src/libsystemd/src/sd-netlink/netlink-util.h new file mode 100644 index 0000000000..c9b932f994 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/netlink-util.h @@ -0,0 +1,39 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2013 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 . +***/ + +#include "basic/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); +void rtnl_message_seal(sd_netlink_message *m); + +bool rtnl_message_type_is_link(uint16_t type); +bool rtnl_message_type_is_addr(uint16_t type); +bool rtnl_message_type_is_route(uint16_t type); +bool rtnl_message_type_is_neigh(uint16_t type); + +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); + +int rtnl_log_parse_error(int r); +int rtnl_log_create_error(int r); diff --git a/src/libsystemd/src/sd-netlink/rtnl-message.c b/src/libsystemd/src/sd-netlink/rtnl-message.c new file mode 100644 index 0000000000..42178e5706 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/rtnl-message.c @@ -0,0 +1,702 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include +#include + +#include "basic/formats-util.h" +#include "basic/missing.h" +#include "basic/refcnt.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "netlink-internal.h" +#include "netlink-types.h" +#include "netlink-util.h" +#include "sd-netlink.h" + +int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + 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); + + if ((rtm->rtm_family == AF_INET && prefixlen > 32) || + (rtm->rtm_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + rtm->rtm_dst_len = prefixlen; + + return 0; +} + +int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + 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); + + if ((rtm->rtm_family == AF_INET && prefixlen > 32) || + (rtm->rtm_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + rtm->rtm_src_len = prefixlen; + + return 0; +} + +int sd_rtnl_message_route_set_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); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_scope = 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_set_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); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_table = table; + + return 0; +} + +int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { + 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(family, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; +} + +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { + 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_family = 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; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(dst_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *dst_len = rtm->rtm_dst_len; + + return 0; +} + +int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len) { + 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(src_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *src_len = rtm->rtm_src_len; + + return 0; +} + +int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int rtm_family, + unsigned char rtm_protocol) { + struct rtmsg *rtm; + int r; + + assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL); + assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) || + rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWROUTE) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + + rtm = NLMSG_DATA((*ret)->hdr); + + rtm->rtm_family = rtm_family; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + rtm->rtm_type = RTN_UNICAST; + rtm->rtm_table = RT_TABLE_MAIN; + rtm->rtm_protocol = rtm_protocol; + + return 0; +} + +int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + ndm->ndm_flags |= flags; + + return 0; +} + +int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + ndm->ndm_state |= state; + + return 0; +} + +int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + *flags = ndm->ndm_flags; + + return 0; +} + +int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + *state = ndm->ndm_state; + + return 0; +} + +int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *family = ndm->ndm_family; + + return 0; +} + +int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *index) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(index, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *index = ndm->ndm_ifindex; + + return 0; +} + +int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int ndm_family) { + struct ndmsg *ndm; + int r; + + assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); + assert_return(ndm_family == AF_INET || + ndm_family == AF_INET6 || + ndm_family == PF_BRIDGE, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWNEIGH) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + + ndm = NLMSG_DATA((*ret)->hdr); + + ndm->ndm_family = ndm_family; + ndm->ndm_ifindex = index; + + return 0; +} + +int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(change, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_flags = flags; + ifi->ifi_change = change; + + return 0; +} + +int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_type = type; + + return 0; +} + +int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_family = family; + + return 0; +} + +int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int index) { + struct ifinfomsg *ifi; + int r; + + assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWLINK) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + ifi = NLMSG_DATA((*ret)->hdr); + + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = index; + + return 0; +} + +int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + if ((ifa->ifa_family == AF_INET && prefixlen > 32) || + (ifa->ifa_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + ifa->ifa_prefixlen = prefixlen; + + return 0; +} + +int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + ifa->ifa_flags = flags; + + return 0; +} + +int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + ifa->ifa_scope = scope; + + return 0; +} + +int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *family = ifa->ifa_family; + + return 0; +} + +int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(prefixlen, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *prefixlen = ifa->ifa_prefixlen; + + return 0; +} + +int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(scope, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *scope = ifa->ifa_scope; + + return 0; +} + +int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *flags = ifa->ifa_flags; + + return 0; +} + +int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex) { + struct ifaddrmsg *ifa; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL); + assert_return(ifindex, -EINVAL); + + ifa = NLMSG_DATA(m->hdr); + + *ifindex = ifa->ifa_index; + + return 0; +} + +int sd_rtnl_message_new_addr(sd_netlink *rtnl, sd_netlink_message **ret, + uint16_t nlmsg_type, int index, + int family) { + struct ifaddrmsg *ifa; + int r; + + assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL); + assert_return((nlmsg_type == RTM_GETADDR && index == 0) || + index > 0, -EINVAL); + assert_return((nlmsg_type == RTM_GETADDR && family == AF_UNSPEC) || + family == AF_INET || family == AF_INET6, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_GETADDR) + (*ret)->hdr->nlmsg_flags |= NLM_F_DUMP; + + ifa = NLMSG_DATA((*ret)->hdr); + + ifa->ifa_index = index; + ifa->ifa_family = family; + if (family == AF_INET) + ifa->ifa_prefixlen = 32; + else if (family == AF_INET6) + ifa->ifa_prefixlen = 128; + + return 0; +} + +int sd_rtnl_message_new_addr_update(sd_netlink *rtnl, sd_netlink_message **ret, + int index, int family) { + int r; + + r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, index, family); + if (r < 0) + return r; + + (*ret)->hdr->nlmsg_flags |= NLM_F_REPLACE; + + return 0; +} + +int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(ifindex, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *ifindex = ifi->ifi_index; + + return 0; +} + +int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *flags = ifi->ifi_flags; + + return 0; +} + +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + assert_return(type, -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + *type = ifi->ifi_type; + + return 0; +} + +int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { + assert_return(m, -EINVAL); + assert_return(family, -EINVAL); + + assert(m->hdr); + + if (rtnl_message_type_is_link(m->hdr->nlmsg_type)) { + struct ifinfomsg *ifi; + + ifi = NLMSG_DATA(m->hdr); + + *family = ifi->ifi_family; + + return 0; + } else if (rtnl_message_type_is_route(m->hdr->nlmsg_type)) { + struct rtmsg *rtm; + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; + } else if (rtnl_message_type_is_neigh(m->hdr->nlmsg_type)) { + struct ndmsg *ndm; + + ndm = NLMSG_DATA(m->hdr); + + *family = ndm->ndm_family; + + return 0; + } else if (rtnl_message_type_is_addr(m->hdr->nlmsg_type)) { + struct ifaddrmsg *ifa; + + ifa = NLMSG_DATA(m->hdr); + + *family = ifa->ifa_family; + + return 0; + } + + return -EOPNOTSUPP; +} diff --git a/src/libsystemd/src/sd-netlink/sd-netlink.c b/src/libsystemd/src/sd-netlink/sd-netlink.c new file mode 100644 index 0000000000..d78777397e --- /dev/null +++ b/src/libsystemd/src/sd-netlink/sd-netlink.c @@ -0,0 +1,957 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/hashmap.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "netlink-internal.h" +#include "netlink-util.h" +#include "sd-netlink.h" + +static int sd_netlink_new(sd_netlink **ret) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + + assert_return(ret, -EINVAL); + + rtnl = new0(sd_netlink, 1); + if (!rtnl) + return -ENOMEM; + + rtnl->n_ref = REFCNT_INIT; + rtnl->fd = -1; + rtnl->sockaddr.nl.nl_family = AF_NETLINK; + rtnl->original_pid = getpid(); + + LIST_HEAD_INIT(rtnl->match_callbacks); + + /* We guarantee that the read buffer has at least space for + * a message header */ + if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated, + sizeof(struct nlmsghdr), sizeof(uint8_t))) + return -ENOMEM; + + /* Change notification responses have sequence 0, so we must + * start our request sequence numbers at 1, or we may confuse our + * responses with notifications from the kernel */ + rtnl->serial = 1; + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + socklen_t addrlen; + int r; + + assert_return(ret, -EINVAL); + + r = sd_netlink_new(&rtnl); + if (r < 0) + return r; + + addrlen = sizeof(rtnl->sockaddr); + + r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); + if (r < 0) + return -errno; + + if (rtnl->sockaddr.nl.nl_family != AF_NETLINK) + return -EINVAL; + + rtnl->fd = fd; + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +static bool rtnl_pid_changed(sd_netlink *rtnl) { + assert(rtnl); + + /* We don't support people creating an rtnl connection and + * keeping it around over a fork(). Let's complain. */ + + return rtnl->original_pid != getpid(); +} + +int sd_netlink_open_fd(sd_netlink **ret, int fd) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + + r = sd_netlink_new(&rtnl); + if (r < 0) + return r; + + rtnl->fd = fd; + + r = socket_bind(rtnl); + if (r < 0) { + rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ + return r; + } + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +int sd_netlink_open(sd_netlink **ret) { + _cleanup_close_ int fd = -1; + int r; + + fd = socket_open(NETLINK_ROUTE); + if (fd < 0) + return fd; + + r = sd_netlink_open_fd(ret, fd); + if (r < 0) + return r; + + fd = -1; + + return 0; +} + +int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) { + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + return fd_inc_rcvbuf(rtnl->fd, size); +} + +sd_netlink *sd_netlink_ref(sd_netlink *rtnl) { + assert_return(rtnl, NULL); + assert_return(!rtnl_pid_changed(rtnl), NULL); + + if (rtnl) + assert_se(REFCNT_INC(rtnl->n_ref) >= 2); + + return rtnl; +} + +sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { + if (!rtnl) + return NULL; + + assert_return(!rtnl_pid_changed(rtnl), NULL); + + if (REFCNT_DEC(rtnl->n_ref) == 0) { + struct match_callback *f; + unsigned i; + + for (i = 0; i < rtnl->rqueue_size; i++) + sd_netlink_message_unref(rtnl->rqueue[i]); + free(rtnl->rqueue); + + for (i = 0; i < rtnl->rqueue_partial_size; i++) + sd_netlink_message_unref(rtnl->rqueue_partial[i]); + free(rtnl->rqueue_partial); + + free(rtnl->rbuffer); + + hashmap_free_free(rtnl->reply_callbacks); + prioq_free(rtnl->reply_callbacks_prioq); + + sd_event_source_unref(rtnl->io_event_source); + sd_event_source_unref(rtnl->time_event_source); + sd_event_unref(rtnl->event); + + while ((f = rtnl->match_callbacks)) { + sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); + } + + hashmap_free(rtnl->broadcast_group_refs); + + safe_close(rtnl->fd); + free(rtnl); + } + + return NULL; +} + +static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) { + assert(rtnl); + assert(!rtnl_pid_changed(rtnl)); + assert(m); + assert(m->hdr); + + /* don't use seq == 0, as that is used for broadcasts, so we + would get confused by replies to such messages */ + m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; + + rtnl_message_seal(m); + + return; +} + +int sd_netlink_send(sd_netlink *nl, + sd_netlink_message *message, + uint32_t *serial) { + int r; + + assert_return(nl, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + assert_return(message, -EINVAL); + assert_return(!message->sealed, -EPERM); + + rtnl_seal_message(nl, message); + + r = socket_write_message(nl, message); + if (r < 0) + return r; + + if (serial) + *serial = rtnl_message_get_serial(message); + + return 1; +} + +int rtnl_rqueue_make_room(sd_netlink *rtnl) { + assert(rtnl); + + if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX) { + log_debug("rtnl: exhausted the read queue size (%d)", RTNL_RQUEUE_MAX); + return -ENOBUFS; + } + + if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1)) + return -ENOMEM; + + return 0; +} + +int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) { + assert(rtnl); + + if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX) { + log_debug("rtnl: exhausted the partial read queue size (%d)", RTNL_RQUEUE_MAX); + return -ENOBUFS; + } + + if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated, + rtnl->rqueue_partial_size + 1)) + return -ENOMEM; + + return 0; +} + +static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { + int r; + + assert(rtnl); + assert(message); + + if (rtnl->rqueue_size <= 0) { + /* Try to read a new message */ + r = socket_read_message(rtnl); + if (r <= 0) + return r; + } + + /* Dispatch a queued message */ + *message = rtnl->rqueue[0]; + rtnl->rqueue_size--; + memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); + + return 1; +} + +static int process_timeout(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + struct reply_callback *c; + usec_t n; + int r; + + assert(rtnl); + + c = prioq_peek(rtnl->reply_callbacks_prioq); + if (!c) + return 0; + + n = now(CLOCK_MONOTONIC); + if (c->timeout > n) + return 0; + + r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m); + if (r < 0) + return r; + + assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c); + hashmap_remove(rtnl->reply_callbacks, &c->serial); + + r = c->callback(rtnl, m, c->userdata); + if (r < 0) + log_debug_errno(r, "sd-netlink: timedout callback failed: %m"); + + free(c); + + return 1; +} + +static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) { + _cleanup_free_ struct reply_callback *c = NULL; + uint64_t serial; + uint16_t type; + int r; + + assert(rtnl); + assert(m); + + serial = rtnl_message_get_serial(m); + c = hashmap_remove(rtnl->reply_callbacks, &serial); + if (!c) + return 0; + + if (c->timeout != 0) + prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return 0; + + if (type == NLMSG_DONE) + m = NULL; + + r = c->callback(rtnl, m, c->userdata); + if (r < 0) + log_debug_errno(r, "sd-netlink: callback failed: %m"); + + return 1; +} + +static int process_match(sd_netlink *rtnl, sd_netlink_message *m) { + struct match_callback *c; + uint16_t type; + int r; + + assert(rtnl); + assert(m); + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + + LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) { + if (type == c->type) { + r = c->callback(rtnl, m, c->userdata); + if (r != 0) { + if (r < 0) + log_debug_errno(r, "sd-netlink: match callback failed: %m"); + + break; + } + } + } + + return 1; +} + +static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(rtnl); + + r = process_timeout(rtnl); + if (r != 0) + goto null_message; + + r = dispatch_rqueue(rtnl, &m); + if (r < 0) + return r; + if (!m) + goto null_message; + + if (sd_netlink_message_is_broadcast(m)) { + r = process_match(rtnl, m); + if (r != 0) + goto null_message; + } else { + r = process_reply(rtnl, m); + if (r != 0) + goto null_message; + } + + if (ret) { + *ret = m; + m = NULL; + + return 1; + } + + return 1; + +null_message: + if (r >= 0 && ret) + *ret = NULL; + + return r; +} + +int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) { + NETLINK_DONT_DESTROY(rtnl); + int r; + + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + assert_return(!rtnl->processing, -EBUSY); + + rtnl->processing = true; + r = process_running(rtnl, ret); + rtnl->processing = false; + + return r; +} + +static usec_t calc_elapse(uint64_t usec) { + if (usec == (uint64_t) -1) + return 0; + + if (usec == 0) + usec = RTNL_DEFAULT_TIMEOUT; + + return now(CLOCK_MONOTONIC) + usec; +} + +static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) { + struct pollfd p[1] = {}; + struct timespec ts; + usec_t m = USEC_INFINITY; + int r, e; + + assert(rtnl); + + e = sd_netlink_get_events(rtnl); + if (e < 0) + return e; + + if (need_more) + /* Caller wants more data, and doesn't care about + * what's been read or any other timeouts. */ + e |= POLLIN; + else { + usec_t until; + /* Caller wants to process if there is something to + * process, but doesn't care otherwise */ + + r = sd_netlink_get_timeout(rtnl, &until); + if (r < 0) + return r; + if (r > 0) { + usec_t nw; + nw = now(CLOCK_MONOTONIC); + m = until > nw ? until - nw : 0; + } + } + + if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) + m = timeout_usec; + + p[0].fd = rtnl->fd; + p[0].events = e; + + r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + if (r < 0) + return -errno; + + return r > 0 ? 1 : 0; +} + +int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) { + assert_return(nl, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + + if (nl->rqueue_size > 0) + return 0; + + return rtnl_poll(nl, false, timeout_usec); +} + +static int timeout_compare(const void *a, const void *b) { + const struct reply_callback *x = a, *y = b; + + if (x->timeout != 0 && y->timeout == 0) + return -1; + + if (x->timeout == 0 && y->timeout != 0) + return 1; + + if (x->timeout < y->timeout) + return -1; + + if (x->timeout > y->timeout) + return 1; + + return 0; +} + +int sd_netlink_call_async(sd_netlink *nl, + sd_netlink_message *m, + sd_netlink_message_handler_t callback, + void *userdata, + uint64_t usec, + uint32_t *serial) { + struct reply_callback *c; + uint32_t s; + int r, k; + + assert_return(nl, -EINVAL); + assert_return(m, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + + r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops); + if (r < 0) + return r; + + if (usec != (uint64_t) -1) { + r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare); + if (r < 0) + return r; + } + + c = new0(struct reply_callback, 1); + if (!c) + return -ENOMEM; + + c->callback = callback; + c->userdata = userdata; + c->timeout = calc_elapse(usec); + + k = sd_netlink_send(nl, m, &s); + if (k < 0) { + free(c); + return k; + } + + c->serial = s; + + r = hashmap_put(nl->reply_callbacks, &c->serial, c); + if (r < 0) { + free(c); + return r; + } + + if (c->timeout != 0) { + r = prioq_put(nl->reply_callbacks_prioq, c, &c->prioq_idx); + if (r > 0) { + c->timeout = 0; + sd_netlink_call_async_cancel(nl, c->serial); + return r; + } + } + + if (serial) + *serial = s; + + return k; +} + +int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial) { + struct reply_callback *c; + uint64_t s = serial; + + assert_return(nl, -EINVAL); + assert_return(serial != 0, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + + c = hashmap_remove(nl->reply_callbacks, &s); + if (!c) + return 0; + + if (c->timeout != 0) + prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx); + + free(c); + return 1; +} + +int sd_netlink_call(sd_netlink *rtnl, + sd_netlink_message *message, + uint64_t usec, + sd_netlink_message **ret) { + usec_t timeout; + uint32_t serial; + int r; + + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + assert_return(message, -EINVAL); + + r = sd_netlink_send(rtnl, message, &serial); + if (r < 0) + return r; + + timeout = calc_elapse(usec); + + for (;;) { + usec_t left; + unsigned i; + + for (i = 0; i < rtnl->rqueue_size; i++) { + uint32_t received_serial; + + received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); + + if (received_serial == serial) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL; + uint16_t type; + + incoming = rtnl->rqueue[i]; + + /* found a match, remove from rqueue and return it */ + memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, + sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1)); + rtnl->rqueue_size--; + + r = sd_netlink_message_get_errno(incoming); + if (r < 0) + return r; + + r = sd_netlink_message_get_type(incoming, &type); + if (r < 0) + return r; + + if (type == NLMSG_DONE) { + *ret = NULL; + return 0; + } + + if (ret) { + *ret = incoming; + incoming = NULL; + } + + return 1; + } + } + + r = socket_read_message(rtnl); + if (r < 0) + return r; + if (r > 0) + /* received message, so try to process straight away */ + continue; + + if (timeout > 0) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + if (n >= timeout) + return -ETIMEDOUT; + + left = timeout - n; + } else + left = (uint64_t) -1; + + r = rtnl_poll(rtnl, true, left); + if (r < 0) + return r; + else if (r == 0) + return -ETIMEDOUT; + } +} + +int sd_netlink_get_events(sd_netlink *rtnl) { + assert_return(rtnl, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + if (rtnl->rqueue_size == 0) + return POLLIN; + else + return 0; +} + +int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) { + struct reply_callback *c; + + assert_return(rtnl, -EINVAL); + assert_return(timeout_usec, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + if (rtnl->rqueue_size > 0) { + *timeout_usec = 0; + return 1; + } + + c = prioq_peek(rtnl->reply_callbacks_prioq); + if (!c) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + *timeout_usec = c->timeout; + + return 1; +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_netlink *rtnl = userdata; + int r; + + assert(rtnl); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return r; + + return 1; +} + +static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { + sd_netlink *rtnl = userdata; + int r; + + assert(rtnl); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return r; + + return 1; +} + +static int prepare_callback(sd_event_source *s, void *userdata) { + sd_netlink *rtnl = userdata; + int r, e; + usec_t until; + + assert(s); + assert(rtnl); + + e = sd_netlink_get_events(rtnl); + if (e < 0) + return e; + + r = sd_event_source_set_io_events(rtnl->io_event_source, e); + if (r < 0) + return r; + + r = sd_netlink_get_timeout(rtnl, &until); + if (r < 0) + return r; + if (r > 0) { + int j; + + j = sd_event_source_set_time(rtnl->time_event_source, until); + if (j < 0) + return j; + } + + r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0); + if (r < 0) + return r; + + return 1; +} + +int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) { + int r; + + assert_return(rtnl, -EINVAL); + assert_return(!rtnl->event, -EBUSY); + + assert(!rtnl->io_event_source); + assert(!rtnl->time_event_source); + + if (event) + rtnl->event = sd_event_ref(event); + else { + r = sd_event_default(&rtnl->event); + if (r < 0) + return r; + } + + r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(rtnl->io_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message"); + if (r < 0) + goto fail; + + r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback); + if (r < 0) + goto fail; + + r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(rtnl->time_event_source, priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer"); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_netlink_detach_event(rtnl); + return r; +} + +int sd_netlink_detach_event(sd_netlink *rtnl) { + assert_return(rtnl, -EINVAL); + assert_return(rtnl->event, -ENXIO); + + rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source); + + rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source); + + rtnl->event = sd_event_unref(rtnl->event); + + return 0; +} + +int sd_netlink_add_match(sd_netlink *rtnl, + uint16_t type, + sd_netlink_message_handler_t callback, + void *userdata) { + _cleanup_free_ struct match_callback *c = NULL; + int r; + + assert_return(rtnl, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!rtnl_pid_changed(rtnl), -ECHILD); + + c = new0(struct match_callback, 1); + if (!c) + return -ENOMEM; + + c->callback = callback; + c->type = type; + c->userdata = userdata; + + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); + if (r < 0) + return r; + + break; + case RTM_NEWADDR: + case RTM_DELADDR: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + 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; + } + + LIST_PREPEND(match_callbacks, rtnl->match_callbacks, c); + + c = NULL; + + return 0; +} + +int sd_netlink_remove_match(sd_netlink *rtnl, + uint16_t type, + 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); + + 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; + } + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/sd-netlink.h b/src/libsystemd/src/sd-netlink/sd-netlink.h new file mode 100644 index 0000000000..8a8c85a004 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/sd-netlink.h @@ -0,0 +1,163 @@ +#ifndef foosdnetlinkhfoo +#define foosdnetlinkhfoo + +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include +#include + +#include +#include + +#include +#include + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_netlink sd_netlink; +typedef struct sd_netlink_message sd_netlink_message; + +/* callback */ + +typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata); + +/* bus */ +int sd_netlink_new_from_netlink(sd_netlink **nl, int fd); +int sd_netlink_open(sd_netlink **nl); +int sd_netlink_open_fd(sd_netlink **nl, int fd); +int sd_netlink_inc_rcvbuf(sd_netlink *nl, const size_t size); + +sd_netlink *sd_netlink_ref(sd_netlink *nl); +sd_netlink *sd_netlink_unref(sd_netlink *nl); + +int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial); +int sd_netlink_call_async(sd_netlink *nl, sd_netlink_message *message, + sd_netlink_message_handler_t callback, + void *userdata, uint64_t usec, uint32_t *serial); +int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial); +int sd_netlink_call(sd_netlink *nl, sd_netlink_message *message, uint64_t timeout, + sd_netlink_message **reply); + +int sd_netlink_get_events(sd_netlink *nl); +int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout); +int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret); +int sd_netlink_wait(sd_netlink *nl, uint64_t timeout); + +int sd_netlink_add_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); +int sd_netlink_remove_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); + +int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); +int sd_netlink_detach_event(sd_netlink *nl); + +int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data); +int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data); +int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data); +int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data); +int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len); +int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data); +int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data); +int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data); +int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info); + +int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key); +int sd_netlink_message_close_container(sd_netlink_message *m); + +int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data); +int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data); +int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data); +int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data); +int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data); +int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info); +int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data); +int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data); +int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type); +int sd_netlink_message_exit_container(sd_netlink_message *m); + +int sd_netlink_message_rewind(sd_netlink_message *m); + +sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m); + +sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m); +sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m); + +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 */ + +int sd_rtnl_message_new_link(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index); +int sd_rtnl_message_new_addr_update(sd_netlink *nl, sd_netlink_message **ret, int index, int family); +int sd_rtnl_message_new_addr(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int family); +int sd_rtnl_message_new_route(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol); +int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int nda_family); + +int sd_rtnl_message_get_family(sd_netlink_message *m, int *family); + +int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen); +int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope); +int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags); +int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen); +int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope); +int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags); +int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex); + +int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change); +int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type); +int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family); +int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex); +int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags); +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *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_set_table(sd_netlink_message *m, unsigned char table); +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_set_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); + +int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags); +int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state); +int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family); +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/libsystemd/src/sd-netlink/test-local-addresses.c b/src/libsystemd/src/sd-netlink/test-local-addresses.c new file mode 100644 index 0000000000..8755a27182 --- /dev/null +++ b/src/libsystemd/src/sd-netlink/test-local-addresses.c @@ -0,0 +1,57 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/in-addr-util.h" + +#include "local-addresses.h" + +static void print_local_addresses(struct local_address *a, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *b = NULL; + + assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); + printf("%s if%i scope=%i metric=%u address=%s\n", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); + } +} + +int main(int argc, char *argv[]) { + struct local_address *a; + int n; + + a = NULL; + n = local_addresses(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Addresses:\n"); + print_local_addresses(a, (unsigned) n); + a = mfree(a); + + n = local_gateways(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Gateways:\n"); + print_local_addresses(a, (unsigned) n); + free(a); + + return 0; +} diff --git a/src/libsystemd/src/sd-netlink/test-netlink.c b/src/libsystemd/src/sd-netlink/test-netlink.c new file mode 100644 index 0000000000..1fde81778e --- /dev/null +++ b/src/libsystemd/src/sd-netlink/test-netlink.c @@ -0,0 +1,440 @@ +/*** + This file is part of systemd. + + Copyright 2013 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 . +***/ + +#include +#include + +#include "basic/ether-addr-util.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#include "netlink-util.h" +#include "sd-netlink.h" + +static void test_message_link_bridge(sd_netlink *rtnl) { + _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); + assert_se(sd_rtnl_message_link_set_family(message, PF_BRIDGE) >= 0); + assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0); + assert_se(sd_netlink_message_close_container(message) >= 0); + + assert_se(sd_netlink_message_rewind(message) >= 0); + + assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0); + assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0); + assert_se(cost == 10); + assert_se(sd_netlink_message_exit_container(message) >= 0); +} + +static void test_link_configure(sd_netlink *rtnl, int ifindex) { + _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; + const char *name_out; + struct ether_addr mac_out; + + /* we'd really like to test NEWLINK, but let's not mess with the running kernel */ + assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0); + assert_se(sd_netlink_message_append_string(message, IFLA_IFNAME, name) >= 0); + assert_se(sd_netlink_message_append_ether_addr(message, IFLA_ADDRESS, ether_aton(mac)) >= 0); + assert_se(sd_netlink_message_append_u32(message, IFLA_MTU, mtu) >= 0); + + assert_se(sd_netlink_call(rtnl, message, 0, NULL) == 1); + assert_se(sd_netlink_message_rewind(message) >= 0); + + assert_se(sd_netlink_message_read_string(message, IFLA_IFNAME, &name_out) >= 0); + assert_se(streq(name, name_out)); + + assert_se(sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &mac_out) >= 0); + assert_se(streq(mac, ether_addr_to_string(&mac_out, buffer))); + + assert_se(sd_netlink_message_read_u32(message, IFLA_MTU, &mtu_out) >= 0); + assert_se(mtu == mtu_out); +} + +static void test_link_get(sd_netlink *rtnl, int ifindex) { + sd_netlink_message *m; + sd_netlink_message *r; + unsigned int mtu = 1500; + const char *str_data; + uint8_t u8_data; + uint32_t u32_data; + struct ether_addr eth_data; + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + assert_se(m); + + /* u8 test cases */ + assert_se(sd_netlink_message_append_u8(m, IFLA_CARRIER, 0) >= 0); + assert_se(sd_netlink_message_append_u8(m, IFLA_OPERSTATE, 0) >= 0); + assert_se(sd_netlink_message_append_u8(m, IFLA_LINKMODE, 0) >= 0); + + /* u32 test cases */ + assert_se(sd_netlink_message_append_u32(m, IFLA_MTU, mtu) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_GROUP, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_TXQLEN, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_TX_QUEUES, 0) >= 0); + assert_se(sd_netlink_message_append_u32(m, IFLA_NUM_RX_QUEUES, 0) >= 0); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); + + assert_se(sd_netlink_message_read_string(r, IFLA_IFNAME, &str_data) == 0); + + assert_se(sd_netlink_message_read_u8(r, IFLA_CARRIER, &u8_data) == 0); + assert_se(sd_netlink_message_read_u8(r, IFLA_OPERSTATE, &u8_data) == 0); + assert_se(sd_netlink_message_read_u8(r, IFLA_LINKMODE, &u8_data) == 0); + + assert_se(sd_netlink_message_read_u32(r, IFLA_MTU, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_GROUP, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_TXQLEN, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_TX_QUEUES, &u32_data) == 0); + assert_se(sd_netlink_message_read_u32(r, IFLA_NUM_RX_QUEUES, &u32_data) == 0); + + assert_se(sd_netlink_message_read_ether_addr(r, IFLA_ADDRESS, ð_data) == 0); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); +} + + +static void test_address_get(sd_netlink *rtnl, int ifindex) { + sd_netlink_message *m; + sd_netlink_message *r; + struct in_addr in_data; + struct ifa_cacheinfo cache; + const char *label; + + assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0); + assert_se(m); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == 1); + + assert_se(sd_netlink_message_read_in_addr(r, IFA_LOCAL, &in_data) == 0); + assert_se(sd_netlink_message_read_in_addr(r, IFA_ADDRESS, &in_data) == 0); + assert_se(sd_netlink_message_read_string(r, IFA_LABEL, &label) == 0); + assert_se(sd_netlink_message_read_cache_info(r, IFA_CACHEINFO, &cache) == 0); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + +} + +static void test_route(void) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req; + struct in_addr addr, addr_data; + uint32_t index = 2, u32_data; + int r; + + r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); + if (r < 0) { + log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); + return; + } + + addr.s_addr = htonl(INADDR_LOOPBACK); + + r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &addr); + if (r < 0) { + log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + return; + } + + r = sd_netlink_message_append_u32(req, RTA_OIF, index); + if (r < 0) { + log_error_errno(r, "Could not append RTA_OIF attribute: %m"); + return; + } + + assert_se(sd_netlink_message_rewind(req) >= 0); + + assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0); + assert_se(addr_data.s_addr == addr.s_addr); + + assert_se(sd_netlink_message_read_u32(req, RTA_OIF, &u32_data) >= 0); + assert_se(u32_data == index); + + assert_se((req = sd_netlink_message_unref(req)) == NULL); +} + +static void test_multiple(void) { + sd_netlink *rtnl1, *rtnl2; + + assert_se(sd_netlink_open(&rtnl1) >= 0); + assert_se(sd_netlink_open(&rtnl2) >= 0); + + rtnl1 = sd_netlink_unref(rtnl1); + rtnl2 = sd_netlink_unref(rtnl2); +} + +static int link_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + char *ifname = userdata; + const char *data; + + assert_se(rtnl); + assert_se(m); + + log_info("got link info about %s", ifname); + free(ifname); + + assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &data) >= 0); + assert_se(streq(data, "lo")); + + return 1; +} + +static void test_event_loop(int ifindex) { + _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"); + assert_se(ifname); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0); + + assert_se(sd_event_default(&event) >= 0); + + assert_se(sd_netlink_attach_event(rtnl, event, 0) >= 0); + + assert_se(sd_event_run(event, 0) >= 0); + + assert_se(sd_netlink_detach_event(rtnl) >= 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + int *counter = userdata; + int r; + + (*counter)--; + + r = sd_netlink_message_get_errno(m); + + log_info_errno(r, "%d left in pipe. got reply: %m", *counter); + + assert_se(r >= 0); + + return 1; +} + +static void test_async(int ifindex) { + _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; + + ifname = strdup("lo"); + assert_se(ifname); + + assert_se(sd_netlink_open(&rtnl) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); + + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0); + + assert_se(sd_netlink_wait(rtnl, 0) >= 0); + assert_se(sd_netlink_process(rtnl, &r) >= 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_pipe(int ifindex) { + _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); + + assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0); + assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0); + + counter++; + assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0); + + counter++; + assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0); + + while (counter > 0) { + assert_se(sd_netlink_wait(rtnl, 0) >= 0); + assert_se(sd_netlink_process(rtnl, NULL) >= 0); + } + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_container(void) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + uint16_t u16_data; + uint32_t u32_data; + const char *string_data; + + assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); + + assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0); + assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); + assert_se(sd_netlink_message_append_u16(m, IFLA_VLAN_ID, 100) >= 0); + assert_se(sd_netlink_message_close_container(m) >= 0); + assert_se(sd_netlink_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); + assert_se(sd_netlink_message_close_container(m) >= 0); + assert_se(sd_netlink_message_close_container(m) == -EINVAL); + + assert_se(sd_netlink_message_rewind(m) >= 0); + + assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0); + assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); + assert_se(streq("vlan", string_data)); + + assert_se(sd_netlink_message_enter_container(m, IFLA_INFO_DATA) >= 0); + assert_se(sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &u16_data) >= 0); + assert_se(sd_netlink_message_exit_container(m) >= 0); + + assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0); + assert_se(streq("vlan", string_data)); + assert_se(sd_netlink_message_exit_container(m) >= 0); + + assert_se(sd_netlink_message_read_u32(m, IFLA_LINKINFO, &u32_data) < 0); + + assert_se(sd_netlink_message_exit_container(m) == -EINVAL); +} + +static void test_match(void) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + + assert_se(sd_netlink_open(&rtnl) >= 0); + + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); + + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0); + + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); +} + +static void test_get_addresses(sd_netlink *rtnl) { + _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); + + assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0); + + for (m = reply; m; m = sd_netlink_message_next(m)) { + uint16_t type; + unsigned char scope, flags; + int family, ifindex; + + assert_se(sd_netlink_message_get_type(m, &type) >= 0); + assert_se(type == RTM_NEWADDR); + + assert_se(sd_rtnl_message_addr_get_ifindex(m, &ifindex) >= 0); + assert_se(sd_rtnl_message_addr_get_family(m, &family) >= 0); + assert_se(sd_rtnl_message_addr_get_scope(m, &scope) >= 0); + assert_se(sd_rtnl_message_addr_get_flags(m, &flags) >= 0); + + assert_se(ifindex > 0); + assert_se(family == AF_INET || family == AF_INET6); + + log_info("got IPv%u address on ifindex %i", family == AF_INET ? 4: 6, ifindex); + } +} + +static void test_message(void) { + _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); +} + +int main(void) { + sd_netlink *rtnl; + sd_netlink_message *m; + sd_netlink_message *r; + const char *string_data; + int if_loopback; + uint16_t type; + + test_message(); + + test_match(); + + test_multiple(); + + test_route(); + + test_container(); + + assert_se(sd_netlink_open(&rtnl) >= 0); + assert_se(rtnl); + + if_loopback = (int) if_nametoindex("lo"); + assert_se(if_loopback > 0); + + test_async(if_loopback); + + test_pipe(if_loopback); + + test_event_loop(if_loopback); + + test_link_configure(rtnl, if_loopback); + + test_get_addresses(rtnl); + + test_message_link_bridge(rtnl); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0); + assert_se(m); + + assert_se(sd_netlink_message_get_type(m, &type) >= 0); + assert_se(type == RTM_GETLINK); + + assert_se(sd_netlink_message_read_string(m, IFLA_IFNAME, &string_data) == -EPERM); + + assert_se(sd_netlink_call(rtnl, m, 0, &r) == 1); + assert_se(sd_netlink_message_get_type(r, &type) >= 0); + assert_se(type == RTM_NEWLINK); + + assert_se((r = sd_netlink_message_unref(r)) == NULL); + + assert_se(sd_netlink_call(rtnl, m, -1, &r) == -EPERM); + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + + test_link_get(rtnl, if_loopback); + test_address_get(rtnl, if_loopback); + + assert_se((m = sd_netlink_message_unref(m)) == NULL); + assert_se((r = sd_netlink_message_unref(r)) == NULL); + assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); + + return EXIT_SUCCESS; +} diff --git a/src/libsystemd/src/sd-network/Makefile b/src/libsystemd/src/sd-network/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-network/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-network/network-util.c b/src/libsystemd/src/sd-network/network-util.c new file mode 100644 index 0000000000..ae3e51ae77 --- /dev/null +++ b/src/libsystemd/src/sd-network/network-util.c @@ -0,0 +1,38 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/strv.h" + +#include "network-util.h" + +bool network_is_online(void) { + _cleanup_free_ char *state = NULL; + int r; + + r = sd_network_get_operational_state(&state); + if (r < 0) /* if we don't know anything, we consider the system online */ + return true; + + if (STR_IN_SET(state, "routable", "degraded")) + return true; + + return false; +} diff --git a/src/libsystemd/src/sd-network/network-util.h b/src/libsystemd/src/sd-network/network-util.h new file mode 100644 index 0000000000..26780dce28 --- /dev/null +++ b/src/libsystemd/src/sd-network/network-util.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Thomas Hindø Paabøl 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 . +***/ + +#include "sd-network.h" + +bool network_is_online(void); diff --git a/src/libsystemd/src/sd-network/sd-network.c b/src/libsystemd/src/sd-network/sd-network.c new file mode 100644 index 0000000000..c3755371ff --- /dev/null +++ b/src/libsystemd/src/sd-network/sd-network.c @@ -0,0 +1,400 @@ +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + 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 . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "sd-network.h" + +_public_ int sd_network_get_operational_state(char **state) { + _cleanup_free_ char *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = parse_env_file("/run/systemd/netif/state", NEWLINE, "OPER_STATE", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *state = s; + s = NULL; + + return 0; +} + +static int network_get_strv(const char *key, char ***ret) { + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ret, -EINVAL); + + r = parse_env_file("/run/systemd/netif/state", 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; +} + +_public_ int sd_network_get_dns(char ***ret) { + return network_get_strv("DNS", ret); +} + +_public_ int sd_network_get_ntp(char ***ret) { + return network_get_strv("NTP", ret); +} + +_public_ int sd_network_get_search_domains(char ***ret) { + return network_get_strv("DOMAINS", ret); +} + +_public_ int sd_network_get_route_domains(char ***ret) { + return network_get_strv("ROUTE_DOMAINS", ret); +} + +static int network_link_get_string(int ifindex, const char *field, char **ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + + r = parse_env_file(path, NEWLINE, field, &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_link_get_strv(int ifindex, const char *key, char ***ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *s = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, 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; +} + +_public_ int sd_network_link_get_setup_state(int ifindex, char **state) { + return network_link_get_string(ifindex, "ADMIN_STATE", state); +} + +_public_ int sd_network_link_get_network_file(int ifindex, char **filename) { + return network_link_get_string(ifindex, "NETWORK_FILE", filename); +} + +_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) { + return network_link_get_string(ifindex, "LLMNR", llmnr); +} + +_public_ int sd_network_link_get_mdns(int ifindex, char **mdns) { + return network_link_get_string(ifindex, "MDNS", mdns); +} + +_public_ int sd_network_link_get_dnssec(int ifindex, char **dnssec) { + return network_link_get_string(ifindex, "DNSSEC", dnssec); +} + +_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_timezone(int ifindex, char **ret) { + return network_link_get_string(ifindex, "TIMEZONE", ret); +} + +_public_ int sd_network_link_get_dns(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "DNS", ret); +} + +_public_ int sd_network_link_get_ntp(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "NTP", ret); +} + +_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "DOMAINS", ret); +} + +_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { + return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); +} + +static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ int *ifis = NULL; + _cleanup_free_ char *s = NULL; + size_t allocated = 0, c = 0; + const char *x; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + x = s; + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&x, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + r = parse_ifindex(word, &ifindex); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + + ifis[c++] = ifindex; + } + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ + + *ret = ifis; + ifis = NULL; + + return c; +} + +_public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); +} + +_public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); +} + +static inline int MONITOR_TO_FD(sd_network_monitor *m) { + return (int) (unsigned long) m - 1; +} + +static inline sd_network_monitor* FD_TO_MONITOR(int fd) { + return (sd_network_monitor*) (unsigned long) (fd + 1); +} + +static int monitor_add_inotify_watch(int fd) { + int k; + + k = inotify_add_watch(fd, "/run/systemd/netif/links/", IN_MOVED_TO|IN_DELETE); + if (k >= 0) + return 0; + else if (errno != ENOENT) + return -errno; + + k = inotify_add_watch(fd, "/run/systemd/netif/", IN_CREATE|IN_ISDIR); + if (k >= 0) + return 0; + else if (errno != ENOENT) + return -errno; + + k = inotify_add_watch(fd, "/run/systemd/", IN_CREATE|IN_ISDIR); + if (k < 0) + return -errno; + + return 0; +} + +_public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category) { + _cleanup_close_ int fd = -1; + int k; + bool good = false; + + assert_return(m, -EINVAL); + + fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (fd < 0) + return -errno; + + if (!category || streq(category, "links")) { + k = monitor_add_inotify_watch(fd); + if (k < 0) + return k; + + good = true; + } + + if (!good) + return -EINVAL; + + *m = FD_TO_MONITOR(fd); + fd = -1; + + return 0; +} + +_public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { + int fd; + + if (m) { + fd = MONITOR_TO_FD(m); + close_nointr(fd); + } + + return NULL; +} + +_public_ int sd_network_monitor_flush(sd_network_monitor *m) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + int fd, k; + + assert_return(m, -EINVAL); + + fd = MONITOR_TO_FD(m); + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + if (e->mask & IN_ISDIR) { + k = monitor_add_inotify_watch(fd); + if (k < 0) + return k; + + k = inotify_rm_watch(fd, e->wd); + if (k < 0) + return -errno; + } + } + + return 0; +} + +_public_ int sd_network_monitor_get_fd(sd_network_monitor *m) { + + assert_return(m, -EINVAL); + + return MONITOR_TO_FD(m); +} + +_public_ int sd_network_monitor_get_events(sd_network_monitor *m) { + + assert_return(m, -EINVAL); + + /* For now we will only return POLLIN here, since we don't + * need anything else ever for inotify. However, let's have + * this API to keep our options open should we later on need + * it. */ + return POLLIN; +} + +_public_ int sd_network_monitor_get_timeout(sd_network_monitor *m, uint64_t *timeout_usec) { + + assert_return(m, -EINVAL); + assert_return(timeout_usec, -EINVAL); + + /* For now we will only return (uint64_t) -1, since we don't + * need any timeout. However, let's have this API to keep our + * options open should we later on need it. */ + *timeout_usec = (uint64_t) -1; + return 0; +} diff --git a/src/libsystemd/src/sd-network/sd-network.h b/src/libsystemd/src/sd-network/sd-network.h new file mode 100644 index 0000000000..ac2660de45 --- /dev/null +++ b/src/libsystemd/src/sd-network/sd-network.h @@ -0,0 +1,176 @@ +#ifndef foosdnetworkhfoo +#define foosdnetworkhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + 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 . +***/ + +#include +#include + +#include + +/* + * A few points: + * + * Instead of returning an empty string array or empty integer array, we + * may return NULL. + * + * Free the data the library returns with libc free(). String arrays + * are NULL terminated, and you need to free the array itself in + * addition to the strings contained. + * + * We return error codes as negative errno, kernel-style. On success, we + * return 0 or positive. + * + * These functions access data in /run. This is a virtual file system; + * therefore, accesses are relatively cheap. + * + * See sd-network(3) for more information. + */ + +_SD_BEGIN_DECLARATIONS; + +/* Get overall operational state + * Possible states: down, up, dormant, carrier, degraded, routable + * Possible return codes: + * -ENODATA: networkd is not aware of any links + */ +int sd_network_get_operational_state(char **state); + +/* Get DNS entries for all links. These are string representations of + * IP addresses */ +int sd_network_get_dns(char ***dns); + +/* Get NTP entries for all links. These are domain names or string + * representations of IP addresses */ +int sd_network_get_ntp(char ***ntp); + +/* Get the search domains for all links. */ +int sd_network_get_search_domains(char ***domains); + +/* Get the search domains for all links. */ +int sd_network_get_route_domains(char ***domains); + +/* Get setup state from ifindex. + * Possible states: + * pending: udev is still processing the link, we don't yet know if we will manage it + * failed: networkd failed to manage the link + * configuring: in the process of retrieving configuration or configuring the link + * configured: link configured successfully + * unmanaged: networkd is not handling the link + * linger: the link is gone, but has not yet been dropped by networkd + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_setup_state(int ifindex, char **state); + +/* Get operational state from ifindex. + * Possible states: + * off: the device is powered down + * no-carrier: the device is powered up, but it does not yet have a carrier + * dormant: the device has a carrier, but is not yet ready for normal traffic + * carrier: the link has a carrier + * degraded: the link has carrier and addresses valid on the local link configured + * routable: the link has carrier and routable address configured + * Possible return codes: + * -ENODATA: networkd is not aware of the link + */ +int sd_network_link_get_operational_state(int ifindex, char **state); + +/* Get path to .network file applied to link */ +int sd_network_link_get_network_file(int ifindex, char **filename); + +/* Get DNS entries for a given link. These are string representations of + * IP addresses */ +int sd_network_link_get_dns(int ifindex, char ***ret); + +/* Get NTP entries for a given link. These are domain names or string + * representations of IP addresses */ +int sd_network_link_get_ntp(int ifindex, char ***ret); + +/* Indicates whether or not LLMNR 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_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); + +/* Get the search DNS domain names for a given link. */ +int sd_network_link_get_search_domains(int ifindex, char ***domains); + +/* Get the route DNS domain names for a given link. */ +int sd_network_link_get_route_domains(int ifindex, char ***domains); + +/* Get the carrier interface indexes to which current link is bound to. */ +int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); + +/* Get the CARRIERS that are bound to current link. */ +int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); + +/* Get the timezone that was learnt on a specific link. */ +int sd_network_link_get_timezone(int ifindex, char **timezone); + +/* Monitor object */ +typedef struct sd_network_monitor sd_network_monitor; + +/* Create a new monitor. Category must be NULL, "links" or "leases". */ +int sd_network_monitor_new(sd_network_monitor **ret, const char *category); + +/* Destroys the passed monitor. Returns NULL. */ +sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m); + +/* Flushes the monitor */ +int sd_network_monitor_flush(sd_network_monitor *m); + +/* Get FD from monitor */ +int sd_network_monitor_get_fd(sd_network_monitor *m); + +/* Get poll() mask to monitor */ +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/libsystemd/src/sd-resolve/Makefile b/src/libsystemd/src/sd-resolve/Makefile new file mode 120000 index 0000000000..71a1159ce0 --- /dev/null +++ b/src/libsystemd/src/sd-resolve/Makefile @@ -0,0 +1 @@ +../subdir.mk \ No newline at end of file diff --git a/src/libsystemd/src/sd-resolve/sd-resolve.c b/src/libsystemd/src/sd-resolve/sd-resolve.c new file mode 100644 index 0000000000..d638a374a1 --- /dev/null +++ b/src/libsystemd/src/sd-resolve/sd-resolve.c @@ -0,0 +1,1243 @@ +/*** + This file is part of systemd. + + Copyright 2005-2008 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/list.h" +#include "basic/missing.h" +#include "basic/socket-util.h" +#include "basic/util.h" + +#include "sd-resolve.h" + +#define WORKERS_MIN 1U +#define WORKERS_MAX 16U +#define QUERIES_MAX 256U +#define BUFSIZE 10240U + +typedef enum { + REQUEST_ADDRINFO, + RESPONSE_ADDRINFO, + REQUEST_NAMEINFO, + RESPONSE_NAMEINFO, + REQUEST_TERMINATE, + RESPONSE_DIED +} QueryType; + +enum { + REQUEST_RECV_FD, + REQUEST_SEND_FD, + RESPONSE_RECV_FD, + RESPONSE_SEND_FD, + _FD_MAX +}; + +struct sd_resolve { + unsigned n_ref; + + bool dead:1; + pid_t original_pid; + + int fds[_FD_MAX]; + + pthread_t workers[WORKERS_MAX]; + unsigned n_valid_workers; + + unsigned current_id; + sd_resolve_query* query_array[QUERIES_MAX]; + unsigned n_queries, n_done, n_outstanding; + + sd_event_source *event_source; + sd_event *event; + + sd_resolve_query *current; + + sd_resolve **default_resolve_ptr; + pid_t tid; + + LIST_HEAD(sd_resolve_query, queries); +}; + +struct sd_resolve_query { + unsigned n_ref; + + sd_resolve *resolve; + + QueryType type:4; + bool done:1; + bool floating:1; + unsigned id; + + int ret; + int _errno; + int _h_errno; + struct addrinfo *addrinfo; + char *serv, *host; + + union { + sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; + sd_resolve_getnameinfo_handler_t getnameinfo_handler; + }; + + void *userdata; + + LIST_FIELDS(sd_resolve_query, queries); +}; + +typedef struct RHeader { + QueryType type; + unsigned id; + size_t length; +} RHeader; + +typedef struct AddrInfoRequest { + struct RHeader header; + bool hints_valid; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t node_len, service_len; +} AddrInfoRequest; + +typedef struct AddrInfoResponse { + struct RHeader header; + int ret; + int _errno; + int _h_errno; + /* followed by addrinfo_serialization[] */ +} AddrInfoResponse; + +typedef struct AddrInfoSerialization { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + size_t canonname_len; + /* Followed by ai_addr amd ai_canonname with variable lengths */ +} AddrInfoSerialization; + +typedef struct NameInfoRequest { + struct RHeader header; + int flags; + socklen_t sockaddr_len; + bool gethost:1, getserv:1; +} NameInfoRequest; + +typedef struct NameInfoResponse { + struct RHeader header; + size_t hostlen, servlen; + int ret; + int _errno; + int _h_errno; +} NameInfoResponse; + +typedef union Packet { + RHeader rheader; + AddrInfoRequest addrinfo_request; + AddrInfoResponse addrinfo_response; + NameInfoRequest nameinfo_request; + NameInfoResponse nameinfo_response; +} Packet; + +static int getaddrinfo_done(sd_resolve_query* q); +static int getnameinfo_done(sd_resolve_query *q); + +static void resolve_query_disconnect(sd_resolve_query *q); + +#define RESOLVE_DONT_DESTROY(resolve) \ + _cleanup_(sd_resolve_unrefp) _unused_ sd_resolve *_dont_destroy_##resolve = sd_resolve_ref(resolve) + +static int send_died(int out_fd) { + + RHeader rh = { + .type = RESPONSE_DIED, + .length = sizeof(RHeader), + }; + + assert(out_fd >= 0); + + if (send(out_fd, &rh, rh.length, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *length, size_t maxlength) { + AddrInfoSerialization s; + size_t cnl, l; + + assert(p); + assert(ai); + assert(length); + assert(*length <= maxlength); + + cnl = ai->ai_canonname ? strlen(ai->ai_canonname)+1 : 0; + l = sizeof(AddrInfoSerialization) + ai->ai_addrlen + cnl; + + if (*length + l > maxlength) + return NULL; + + s.ai_flags = ai->ai_flags; + s.ai_family = ai->ai_family; + s.ai_socktype = ai->ai_socktype; + s.ai_protocol = ai->ai_protocol; + s.ai_addrlen = ai->ai_addrlen; + s.canonname_len = cnl; + + memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization)); + memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); + memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, + ai->ai_canonname, cnl); + + *length += l; + return (uint8_t*) p + l; +} + +static int send_addrinfo_reply( + int out_fd, + unsigned id, + int ret, + struct addrinfo *ai, + int _errno, + int _h_errno) { + + AddrInfoResponse resp = { + .header.type = RESPONSE_ADDRINFO, + .header.id = id, + .header.length = sizeof(AddrInfoResponse), + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[2]; + union { + AddrInfoSerialization ais; + uint8_t space[BUFSIZE]; + } buffer; + + assert(out_fd >= 0); + + if (ret == 0 && ai) { + void *p = &buffer; + struct addrinfo *k; + + for (k = ai; k; k = k->ai_next) { + p = serialize_addrinfo(p, k, &resp.header.length, (uint8_t*) &buffer + BUFSIZE - (uint8_t*) p); + if (!p) { + freeaddrinfo(ai); + return -ENOBUFS; + } + } + } + + if (ai) + freeaddrinfo(ai); + + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(AddrInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = &buffer, .iov_len = resp.header.length - sizeof(AddrInfoResponse) }; + + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); + + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static int send_nameinfo_reply( + int out_fd, + unsigned id, + int ret, + const char *host, + const char *serv, + int _errno, + int _h_errno) { + + NameInfoResponse resp = { + .header.type = RESPONSE_NAMEINFO, + .header.id = id, + .ret = ret, + ._errno = _errno, + ._h_errno = _h_errno, + }; + + struct msghdr mh = {}; + struct iovec iov[3]; + size_t hl, sl; + + assert(out_fd >= 0); + + sl = serv ? strlen(serv)+1 : 0; + hl = host ? strlen(host)+1 : 0; + + resp.header.length = sizeof(NameInfoResponse) + hl + sl; + resp.hostlen = hl; + resp.servlen = sl; + + iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(NameInfoResponse) }; + iov[1] = (struct iovec) { .iov_base = (void*) host, .iov_len = hl }; + iov[2] = (struct iovec) { .iov_base = (void*) serv, .iov_len = sl }; + + mh.msg_iov = iov; + mh.msg_iovlen = ELEMENTSOF(iov); + + if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static int handle_request(int out_fd, const Packet *packet, size_t length) { + const RHeader *req; + + assert(out_fd >= 0); + assert(packet); + + req = &packet->rheader; + + assert(length >= sizeof(RHeader)); + assert(length == req->length); + + switch (req->type) { + + case REQUEST_ADDRINFO: { + const AddrInfoRequest *ai_req = &packet->addrinfo_request; + struct addrinfo hints = {}, *result = NULL; + const char *node, *service; + int ret; + + assert(length >= sizeof(AddrInfoRequest)); + assert(length == sizeof(AddrInfoRequest) + ai_req->node_len + ai_req->service_len); + + hints.ai_flags = ai_req->ai_flags; + hints.ai_family = ai_req->ai_family; + hints.ai_socktype = ai_req->ai_socktype; + hints.ai_protocol = ai_req->ai_protocol; + + node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL; + service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL; + + ret = getaddrinfo( + node, service, + ai_req->hints_valid ? &hints : NULL, + &result); + + /* send_addrinfo_reply() frees result */ + return send_addrinfo_reply(out_fd, req->id, ret, result, errno, h_errno); + } + + case REQUEST_NAMEINFO: { + const NameInfoRequest *ni_req = &packet->nameinfo_request; + char hostbuf[NI_MAXHOST], servbuf[NI_MAXSERV]; + union sockaddr_union sa; + int ret; + + assert(length >= sizeof(NameInfoRequest)); + assert(length == sizeof(NameInfoRequest) + ni_req->sockaddr_len); + assert(sizeof(sa) >= ni_req->sockaddr_len); + + memcpy(&sa, (const uint8_t *) ni_req + sizeof(NameInfoRequest), ni_req->sockaddr_len); + + ret = getnameinfo(&sa.sa, ni_req->sockaddr_len, + ni_req->gethost ? hostbuf : NULL, ni_req->gethost ? sizeof(hostbuf) : 0, + ni_req->getserv ? servbuf : NULL, ni_req->getserv ? sizeof(servbuf) : 0, + ni_req->flags); + + return send_nameinfo_reply(out_fd, req->id, ret, + ret == 0 && ni_req->gethost ? hostbuf : NULL, + ret == 0 && ni_req->getserv ? servbuf : NULL, + errno, h_errno); + } + + case REQUEST_TERMINATE: + /* Quit */ + return -ECONNRESET; + + default: + assert_not_reached("Unknown request"); + } + + return 0; +} + +static void* thread_worker(void *p) { + sd_resolve *resolve = p; + sigset_t fullset; + + /* No signals in this thread please */ + assert_se(sigfillset(&fullset) == 0); + assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); + + /* Assign a pretty name to this thread */ + (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); + + while (!resolve->dead) { + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; + ssize_t length; + + length = recv(resolve->fds[REQUEST_RECV_FD], &buf, sizeof(buf), 0); + if (length < 0) { + if (errno == EINTR) + continue; + + break; + } + if (length == 0) + break; + + if (resolve->dead) + break; + + if (handle_request(resolve->fds[RESPONSE_SEND_FD], &buf.packet, (size_t) length) < 0) + break; + } + + send_died(resolve->fds[RESPONSE_SEND_FD]); + + return NULL; +} + +static int start_threads(sd_resolve *resolve, unsigned extra) { + unsigned n; + int r; + + n = resolve->n_outstanding + extra; + n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); + + while (resolve->n_valid_workers < n) { + + r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); + if (r != 0) + return -r; + + resolve->n_valid_workers++; + } + + return 0; +} + +static bool resolve_pid_changed(sd_resolve *r) { + assert(r); + + /* We don't support people creating a resolver and keeping it + * around after fork(). Let's complain. */ + + return r->original_pid != getpid(); +} + +_public_ int sd_resolve_new(sd_resolve **ret) { + sd_resolve *resolve = NULL; + int i, r; + + assert_return(ret, -EINVAL); + + resolve = new0(sd_resolve, 1); + if (!resolve) + return -ENOMEM; + + resolve->n_ref = 1; + resolve->original_pid = getpid(); + + for (i = 0; i < _FD_MAX; i++) + resolve->fds[i] = -1; + + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + REQUEST_RECV_FD); + if (r < 0) { + r = -errno; + goto fail; + } + + r = socketpair(PF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, resolve->fds + RESPONSE_RECV_FD); + if (r < 0) { + r = -errno; + goto fail; + } + + fd_inc_sndbuf(resolve->fds[REQUEST_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[REQUEST_RECV_FD], QUERIES_MAX * BUFSIZE); + fd_inc_sndbuf(resolve->fds[RESPONSE_SEND_FD], QUERIES_MAX * BUFSIZE); + fd_inc_rcvbuf(resolve->fds[RESPONSE_RECV_FD], QUERIES_MAX * BUFSIZE); + + fd_nonblock(resolve->fds[RESPONSE_RECV_FD], true); + + *ret = resolve; + return 0; + +fail: + sd_resolve_unref(resolve); + return r; +} + +_public_ int sd_resolve_default(sd_resolve **ret) { + + static thread_local sd_resolve *default_resolve = NULL; + sd_resolve *e = NULL; + int r; + + if (!ret) + return !!default_resolve; + + if (default_resolve) { + *ret = sd_resolve_ref(default_resolve); + return 0; + } + + r = sd_resolve_new(&e); + if (r < 0) + return r; + + e->default_resolve_ptr = &default_resolve; + e->tid = gettid(); + default_resolve = e; + + *ret = e; + return 1; +} + +_public_ int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid) { + assert_return(resolve, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + if (resolve->tid != 0) { + *tid = resolve->tid; + return 0; + } + + if (resolve->event) + return sd_event_get_tid(resolve->event, tid); + + return -ENXIO; +} + +static void resolve_free(sd_resolve *resolve) { + PROTECT_ERRNO; + sd_resolve_query *q; + unsigned i; + + assert(resolve); + + while ((q = resolve->queries)) { + assert(q->floating); + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + if (resolve->default_resolve_ptr) + *(resolve->default_resolve_ptr) = NULL; + + resolve->dead = true; + + sd_resolve_detach_event(resolve); + + if (resolve->fds[REQUEST_SEND_FD] >= 0) { + + RHeader req = { + .type = REQUEST_TERMINATE, + .length = sizeof(req) + }; + + /* Send one termination packet for each worker */ + for (i = 0; i < resolve->n_valid_workers; i++) + (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); + } + + /* Now terminate them and wait until they are gone. + If we get an error than most likely the thread already exited. */ + for (i = 0; i < resolve->n_valid_workers; i++) + (void) pthread_join(resolve->workers[i], NULL); + + /* Close all communication channels */ + close_many(resolve->fds, _FD_MAX); + free(resolve); +} + +_public_ sd_resolve* sd_resolve_ref(sd_resolve *resolve) { + assert_return(resolve, NULL); + + assert(resolve->n_ref >= 1); + resolve->n_ref++; + + return resolve; +} + +_public_ sd_resolve* sd_resolve_unref(sd_resolve *resolve) { + + if (!resolve) + return NULL; + + assert(resolve->n_ref >= 1); + resolve->n_ref--; + + if (resolve->n_ref <= 0) + resolve_free(resolve); + + return NULL; +} + +_public_ int sd_resolve_get_fd(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + return resolve->fds[RESPONSE_RECV_FD]; +} + +_public_ int sd_resolve_get_events(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + return resolve->n_queries > resolve->n_done ? POLLIN : 0; +} + +_public_ int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *usec) { + assert_return(resolve, -EINVAL); + assert_return(usec, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + *usec = (uint64_t) -1; + return 0; +} + +static sd_resolve_query *lookup_query(sd_resolve *resolve, unsigned id) { + sd_resolve_query *q; + + assert(resolve); + + q = resolve->query_array[id % QUERIES_MAX]; + if (q) + if (q->id == id) + return q; + + return NULL; +} + +static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { + int r; + + assert(q); + assert(!q->done); + assert(q->resolve == resolve); + + q->done = true; + resolve->n_done++; + + resolve->current = sd_resolve_query_ref(q); + + switch (q->type) { + + case REQUEST_ADDRINFO: + r = getaddrinfo_done(q); + break; + + case REQUEST_NAMEINFO: + r = getnameinfo_done(q); + break; + + default: + assert_not_reached("Cannot complete unknown query type"); + } + + resolve->current = NULL; + + if (q->floating) { + resolve_query_disconnect(q); + sd_resolve_query_unref(q); + } + + sd_resolve_query_unref(q); + + return r; +} + +static int unserialize_addrinfo(const void **p, size_t *length, struct addrinfo **ret_ai) { + AddrInfoSerialization s; + size_t l; + struct addrinfo *ai; + + assert(p); + assert(*p); + assert(ret_ai); + assert(length); + + if (*length < sizeof(AddrInfoSerialization)) + return -EBADMSG; + + memcpy(&s, *p, sizeof(s)); + + l = sizeof(AddrInfoSerialization) + s.ai_addrlen + s.canonname_len; + if (*length < l) + return -EBADMSG; + + ai = new0(struct addrinfo, 1); + if (!ai) + return -ENOMEM; + + ai->ai_flags = s.ai_flags; + ai->ai_family = s.ai_family; + ai->ai_socktype = s.ai_socktype; + ai->ai_protocol = s.ai_protocol; + ai->ai_addrlen = s.ai_addrlen; + + if (s.ai_addrlen > 0) { + ai->ai_addr = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization), s.ai_addrlen); + if (!ai->ai_addr) { + free(ai); + return -ENOMEM; + } + } + + if (s.canonname_len > 0) { + ai->ai_canonname = memdup((const uint8_t*) *p + sizeof(AddrInfoSerialization) + s.ai_addrlen, s.canonname_len); + if (!ai->ai_canonname) { + free(ai->ai_addr); + free(ai); + return -ENOMEM; + } + } + + *length -= l; + *ret_ai = ai; + *p = ((const uint8_t*) *p) + l; + + return 0; +} + +static int handle_response(sd_resolve *resolve, const Packet *packet, size_t length) { + const RHeader *resp; + sd_resolve_query *q; + int r; + + assert(resolve); + + resp = &packet->rheader; + assert(resp); + assert(length >= sizeof(RHeader)); + assert(length == resp->length); + + if (resp->type == RESPONSE_DIED) { + resolve->dead = true; + return 0; + } + + assert(resolve->n_outstanding > 0); + resolve->n_outstanding--; + + q = lookup_query(resolve, resp->id); + if (!q) + return 0; + + switch (resp->type) { + + case RESPONSE_ADDRINFO: { + const AddrInfoResponse *ai_resp = &packet->addrinfo_response; + const void *p; + size_t l; + struct addrinfo *prev = NULL; + + assert(length >= sizeof(AddrInfoResponse)); + assert(q->type == REQUEST_ADDRINFO); + + q->ret = ai_resp->ret; + q->_errno = ai_resp->_errno; + q->_h_errno = ai_resp->_h_errno; + + l = length - sizeof(AddrInfoResponse); + p = (const uint8_t*) resp + sizeof(AddrInfoResponse); + + while (l > 0 && p) { + struct addrinfo *ai = NULL; + + r = unserialize_addrinfo(&p, &l, &ai); + if (r < 0) { + q->ret = EAI_SYSTEM; + q->_errno = -r; + q->_h_errno = 0; + freeaddrinfo(q->addrinfo); + q->addrinfo = NULL; + break; + } + + if (prev) + prev->ai_next = ai; + else + q->addrinfo = ai; + + prev = ai; + } + + return complete_query(resolve, q); + } + + case RESPONSE_NAMEINFO: { + const NameInfoResponse *ni_resp = &packet->nameinfo_response; + + assert(length >= sizeof(NameInfoResponse)); + assert(q->type == REQUEST_NAMEINFO); + + q->ret = ni_resp->ret; + q->_errno = ni_resp->_errno; + q->_h_errno = ni_resp->_h_errno; + + if (ni_resp->hostlen > 0) { + q->host = strndup((const char*) ni_resp + sizeof(NameInfoResponse), ni_resp->hostlen-1); + if (!q->host) { + q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } + + if (ni_resp->servlen > 0) { + q->serv = strndup((const char*) ni_resp + sizeof(NameInfoResponse) + ni_resp->hostlen, ni_resp->servlen-1); + if (!q->serv) { + q->ret = EAI_MEMORY; + q->_errno = ENOMEM; + q->_h_errno = 0; + } + } + + return complete_query(resolve, q); + } + + default: + return 0; + } +} + +_public_ int sd_resolve_process(sd_resolve *resolve) { + RESOLVE_DONT_DESTROY(resolve); + + union { + Packet packet; + uint8_t space[BUFSIZE]; + } buf; + ssize_t l; + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + /* We don't allow recursively invoking sd_resolve_process(). */ + assert_return(!resolve->current, -EBUSY); + + l = recv(resolve->fds[RESPONSE_RECV_FD], &buf, sizeof(buf), 0); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + return -errno; + } + if (l == 0) + return -ECONNREFUSED; + + r = handle_response(resolve, &buf.packet, (size_t) l); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec) { + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + if (resolve->n_done >= resolve->n_queries) + return 0; + + do { + r = fd_wait_for_event(resolve->fds[RESPONSE_RECV_FD], POLLIN, timeout_usec); + } while (r == -EINTR); + + if (r < 0) + return r; + + return sd_resolve_process(resolve); +} + +static int alloc_query(sd_resolve *resolve, bool floating, sd_resolve_query **_q) { + sd_resolve_query *q; + int r; + + assert(resolve); + assert(_q); + + if (resolve->n_queries >= QUERIES_MAX) + return -ENOBUFS; + + r = start_threads(resolve, 1); + if (r < 0) + return r; + + while (resolve->query_array[resolve->current_id % QUERIES_MAX]) + resolve->current_id++; + + q = resolve->query_array[resolve->current_id % QUERIES_MAX] = new0(sd_resolve_query, 1); + if (!q) + return -ENOMEM; + + q->n_ref = 1; + q->resolve = resolve; + q->floating = floating; + q->id = resolve->current_id++; + + if (!floating) + sd_resolve_ref(resolve); + + LIST_PREPEND(queries, resolve->queries, q); + resolve->n_queries++; + + *_q = q; + return 0; +} + +_public_ int sd_resolve_getaddrinfo( + sd_resolve *resolve, + sd_resolve_query **_q, + const char *node, const char *service, + const struct addrinfo *hints, + sd_resolve_getaddrinfo_handler_t callback, void *userdata) { + + AddrInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[3]; + sd_resolve_query *q; + int r; + + assert_return(resolve, -EINVAL); + assert_return(node || service, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; + + q->type = REQUEST_ADDRINFO; + q->getaddrinfo_handler = callback; + q->userdata = userdata; + + req.node_len = node ? strlen(node)+1 : 0; + req.service_len = service ? strlen(service)+1 : 0; + + req.header.id = q->id; + req.header.type = REQUEST_ADDRINFO; + req.header.length = sizeof(AddrInfoRequest) + req.node_len + req.service_len; + + if (hints) { + req.hints_valid = true; + req.ai_flags = hints->ai_flags; + req.ai_family = hints->ai_family; + req.ai_socktype = hints->ai_socktype; + req.ai_protocol = hints->ai_protocol; + } + + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(AddrInfoRequest) }; + if (node) + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) node, .iov_len = req.node_len }; + if (service) + iov[mh.msg_iovlen++] = (struct iovec) { .iov_base = (void*) service, .iov_len = req.service_len }; + mh.msg_iov = iov; + + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } + + resolve->n_outstanding++; + + if (_q) + *_q = q; + + return 0; +} + +static int getaddrinfo_done(sd_resolve_query* q) { + assert(q); + assert(q->done); + assert(q->getaddrinfo_handler); + + errno = q->_errno; + h_errno = q->_h_errno; + + return q->getaddrinfo_handler(q, q->ret, q->addrinfo, q->userdata); +} + +_public_ int sd_resolve_getnameinfo( + sd_resolve *resolve, + sd_resolve_query**_q, + const struct sockaddr *sa, socklen_t salen, + int flags, + uint64_t get, + sd_resolve_getnameinfo_handler_t callback, + void *userdata) { + + NameInfoRequest req = {}; + struct msghdr mh = {}; + struct iovec iov[2]; + sd_resolve_query *q; + int r; + + assert_return(resolve, -EINVAL); + assert_return(sa, -EINVAL); + assert_return(salen >= sizeof(struct sockaddr), -EINVAL); + assert_return(salen <= sizeof(union sockaddr_union), -EINVAL); + assert_return((get & ~SD_RESOLVE_GET_BOTH) == 0, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(!resolve_pid_changed(resolve), -ECHILD); + + r = alloc_query(resolve, !_q, &q); + if (r < 0) + return r; + + q->type = REQUEST_NAMEINFO; + q->getnameinfo_handler = callback; + q->userdata = userdata; + + req.header.id = q->id; + req.header.type = REQUEST_NAMEINFO; + req.header.length = sizeof(NameInfoRequest) + salen; + + req.flags = flags; + req.sockaddr_len = salen; + req.gethost = !!(get & SD_RESOLVE_GET_HOST); + req.getserv = !!(get & SD_RESOLVE_GET_SERVICE); + + iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(NameInfoRequest) }; + iov[1] = (struct iovec) { .iov_base = (void*) sa, .iov_len = salen }; + + mh.msg_iov = iov; + mh.msg_iovlen = 2; + + if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { + sd_resolve_query_unref(q); + return -errno; + } + + resolve->n_outstanding++; + + if (_q) + *_q = q; + + return 0; +} + +static int getnameinfo_done(sd_resolve_query *q) { + + assert(q); + assert(q->done); + assert(q->getnameinfo_handler); + + errno = q->_errno; + h_errno= q->_h_errno; + + return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); +} + +_public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { + assert_return(q, NULL); + + assert(q->n_ref >= 1); + q->n_ref++; + + return q; +} + +static void resolve_freeaddrinfo(struct addrinfo *ai) { + while (ai) { + struct addrinfo *next = ai->ai_next; + + free(ai->ai_addr); + free(ai->ai_canonname); + free(ai); + ai = next; + } +} + +static void resolve_query_disconnect(sd_resolve_query *q) { + sd_resolve *resolve; + unsigned i; + + assert(q); + + if (!q->resolve) + return; + + resolve = q->resolve; + assert(resolve->n_queries > 0); + + if (q->done) { + assert(resolve->n_done > 0); + resolve->n_done--; + } + + i = q->id % QUERIES_MAX; + assert(resolve->query_array[i] == q); + resolve->query_array[i] = NULL; + LIST_REMOVE(queries, resolve->queries, q); + resolve->n_queries--; + + q->resolve = NULL; + if (!q->floating) + sd_resolve_unref(resolve); +} + +static void resolve_query_free(sd_resolve_query *q) { + assert(q); + + resolve_query_disconnect(q); + + resolve_freeaddrinfo(q->addrinfo); + free(q->host); + free(q->serv); + free(q); +} + +_public_ sd_resolve_query* sd_resolve_query_unref(sd_resolve_query* q) { + if (!q) + return NULL; + + assert(q->n_ref >= 1); + q->n_ref--; + + if (q->n_ref <= 0) + resolve_query_free(q); + + return NULL; +} + +_public_ int sd_resolve_query_is_done(sd_resolve_query *q) { + assert_return(q, -EINVAL); + assert_return(!resolve_pid_changed(q->resolve), -ECHILD); + + return q->done; +} + +_public_ void* sd_resolve_query_set_userdata(sd_resolve_query *q, void *userdata) { + void *ret; + + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + ret = q->userdata; + q->userdata = userdata; + + return ret; +} + +_public_ void* sd_resolve_query_get_userdata(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + return q->userdata; +} + +_public_ sd_resolve *sd_resolve_query_get_resolve(sd_resolve_query *q) { + assert_return(q, NULL); + assert_return(!resolve_pid_changed(q->resolve), NULL); + + return q->resolve; +} + +static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_resolve *resolve = userdata; + int r; + + assert(resolve); + + r = sd_resolve_process(resolve); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { + int r; + + assert_return(resolve, -EINVAL); + assert_return(!resolve->event, -EBUSY); + + assert(!resolve->event_source); + + if (event) + resolve->event = sd_event_ref(event); + else { + r = sd_event_default(&resolve->event); + if (r < 0) + return r; + } + + r = sd_event_add_io(resolve->event, &resolve->event_source, resolve->fds[RESPONSE_RECV_FD], POLLIN, io_callback, resolve); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(resolve->event_source, priority); + if (r < 0) + goto fail; + + return 0; + +fail: + sd_resolve_detach_event(resolve); + return r; +} + +_public_ int sd_resolve_detach_event(sd_resolve *resolve) { + assert_return(resolve, -EINVAL); + + if (!resolve->event) + return 0; + + if (resolve->event_source) { + sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF); + resolve->event_source = sd_event_source_unref(resolve->event_source); + } + + resolve->event = sd_event_unref(resolve->event); + return 1; +} + +_public_ sd_event *sd_resolve_get_event(sd_resolve *resolve) { + assert_return(resolve, NULL); + + return resolve->event; +} diff --git a/src/libsystemd/src/sd-resolve/sd-resolve.h b/src/libsystemd/src/sd-resolve/sd-resolve.h new file mode 100644 index 0000000000..1996ae401a --- /dev/null +++ b/src/libsystemd/src/sd-resolve/sd-resolve.h @@ -0,0 +1,116 @@ +#ifndef foosdresolvehfoo +#define foosdresolvehfoo + +/*** + This file is part of systemd. + + Copyright 2005-2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +_SD_BEGIN_DECLARATIONS; + +/* An opaque sd-resolve session structure */ +typedef struct sd_resolve sd_resolve; + +/* An opaque sd-resolve query structure */ +typedef struct sd_resolve_query sd_resolve_query; + +/* A callback on completion */ +typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); +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 = UINT64_C(1), + SD_RESOLVE_GET_SERVICE = UINT64_C(2), + SD_RESOLVE_GET_BOTH = UINT64_C(3), +}; + +int sd_resolve_default(sd_resolve **ret); + +/* Allocate a new sd-resolve session. */ +int sd_resolve_new(sd_resolve **ret); + +/* Free a sd-resolve session. This destroys all attached + * sd_resolve_query objects automatically. */ +sd_resolve* sd_resolve_unref(sd_resolve *resolve); +sd_resolve* sd_resolve_ref(sd_resolve *resolve); + +/* Return the UNIX file descriptor to poll() for events on. Use this + * function to integrate sd-resolve with your custom main loop. */ +int sd_resolve_get_fd(sd_resolve *resolve); + +/* Return the poll() events (a combination of flags like POLLIN, + * POLLOUT, ...) to check for. */ +int sd_resolve_get_events(sd_resolve *resolve); + +/* Return the poll() timeout to pass. Returns (uint64_t) -1 as + * timeout if no timeout is needed. */ +int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *timeout_usec); + +/* Process pending responses. After this function is called, you can + * get the next completed query object(s) using + * sd_resolve_get_next(). */ +int sd_resolve_process(sd_resolve *resolve); + +/* Wait for a resolve event to complete. */ +int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec); + +int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid); + +int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int64_t priority); +int sd_resolve_detach_event(sd_resolve *resolve); +sd_event *sd_resolve_get_event(sd_resolve *resolve); + +/* Issue a name-to-address query on the specified session. The + * arguments are compatible with those of libc's + * getaddrinfo(3). The function returns a new query object. When the + * query is completed, you may retrieve the results using + * sd_resolve_getaddrinfo_done(). */ +int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char *node, const char *service, const struct addrinfo *hints, sd_resolve_getaddrinfo_handler_t callback, void *userdata); + +/* Issue an address-to-name query on the specified session. The + * arguments are compatible with those of libc's + * getnameinfo(3). The function returns a new query object. When the + * query is completed, you may retrieve the results using + * sd_resolve_getnameinfo_done(). Set gethost (resp. getserv) to non-zero + * if you want to query the hostname (resp. the service name). */ +int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata); + +sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q); +sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q); + +/* Returns non-zero when the query operation specified by q has been completed. */ +int sd_resolve_query_is_done(sd_resolve_query*q); + +void *sd_resolve_query_get_userdata(sd_resolve_query *q); +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/libsystemd/src/sd-resolve/test-resolve.c b/src/libsystemd/src/sd-resolve/test-resolve.c new file mode 100644 index 0000000000..d552a48da8 --- /dev/null +++ b/src/libsystemd/src/sd-resolve/test-resolve.c @@ -0,0 +1,114 @@ +/*** + This file is part of systemd. + + Copyright 2005-2008 Lennart Poettering + Copyright 2014 Daniel Buch + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" + +#include "sd-resolve.h" + +static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { + const struct addrinfo *i; + + assert_se(q); + + if (ret != 0) { + log_error("getaddrinfo error: %s %i", gai_strerror(ret), ret); + return 0; + } + + for (i = ai; i; i = i->ai_next) { + _cleanup_free_ char *addr = NULL; + + assert_se(sockaddr_pretty(i->ai_addr, i->ai_addrlen, false, true, &addr) == 0); + puts(addr); + } + + printf("canonical name: %s\n", strna(ai->ai_canonname)); + + return 0; +} + +static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata) { + assert_se(q); + + if (ret != 0) { + log_error("getnameinfo error: %s %i", gai_strerror(ret), ret); + return 0; + } + + printf("Host: %s — Serv: %s\n", strna(host), strna(serv)); + return 0; +} + +int main(int argc, char *argv[]) { + _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 = { + .ai_family = PF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME + }; + + struct sockaddr_in sa = { + .sin_family = AF_INET, + .sin_port = htons(80) + }; + + assert_se(sd_resolve_default(&resolve) >= 0); + + /* Test a floating resolver query */ + sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); + + /* Make a name -> address query */ + r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); + if (r < 0) + log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); + + /* Make an address -> name query */ + sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71"); + r = sd_resolve_getnameinfo(resolve, &q2, (struct sockaddr*) &sa, sizeof(sa), 0, SD_RESOLVE_GET_BOTH, getnameinfo_handler, NULL); + if (r < 0) + log_error_errno(r, "sd_resolve_getnameinfo(): %m"); + + /* 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"); + } + } + + return 0; +} diff --git a/src/libsystemd/src/sd-utf8/sd-utf8.c b/src/libsystemd/src/sd-utf8/sd-utf8.c new file mode 100644 index 0000000000..58e4a88f07 --- /dev/null +++ b/src/libsystemd/src/sd-utf8/sd-utf8.c @@ -0,0 +1,35 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/utf8.h" +#include "basic/util.h" + +_public_ const char *sd_utf8_is_valid(const char *s) { + assert_return(s, NULL); + + return utf8_is_valid(s); +} + +_public_ const char *sd_ascii_is_valid(const char *s) { + assert_return(s, NULL); + + return ascii_is_valid(s); +} diff --git a/src/libsystemd/src/subdir.mk b/src/libsystemd/src/subdir.mk new file mode 100644 index 0000000000..605b592401 --- /dev/null +++ b/src/libsystemd/src/subdir.mk @@ -0,0 +1,29 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +sd.CPPFLAGS += -DLIBDIR=\"$(libdir)\" +sd.CPPFLAGS += -DUDEVLIBEXECDIR=\"$(udevlibexecdir)\" + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libsystemd/src/test.mk b/src/libsystemd/src/test.mk new file mode 100644 index 0000000000..689705fb40 --- /dev/null +++ b/src/libsystemd/src/test.mk @@ -0,0 +1,155 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +test_bus_marshal_SOURCES = \ + src/libsystemd/sd-bus/test-bus-marshal.c + +test_bus_marshal_LDADD = \ + libsystemd-shared.la \ + $(GLIB_LIBS) \ + $(DBUS_LIBS) + +test_bus_marshal_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +test_bus_signature_SOURCES = \ + src/libsystemd/sd-bus/test-bus-signature.c + +test_bus_signature_LDADD = \ + libsystemd-shared.la + +test_bus_chat_SOURCES = \ + src/libsystemd/sd-bus/test-bus-chat.c + +test_bus_chat_LDADD = \ + libsystemd-shared.la + +test_bus_cleanup_SOURCES = \ + src/libsystemd/sd-bus/test-bus-cleanup.c + +test_bus_cleanup_CFLAGS = \ + $(SECCOMP_CFLAGS) + +test_bus_cleanup_LDADD = \ + libsystemd-shared.la + +test_bus_server_SOURCES = \ + src/libsystemd/sd-bus/test-bus-server.c + +test_bus_server_LDADD = \ + libsystemd-shared.la + +test_bus_objects_SOURCES = \ + src/libsystemd/sd-bus/test-bus-objects.c + +test_bus_objects_LDADD = \ + libsystemd-shared.la + +test_bus_error_SOURCES = \ + src/libsystemd/sd-bus/test-bus-error.c + +# Link statically because this test uses BUS_ERROR_MAP_ELF_REGISTER +test_bus_error_LDADD = \ + libshared.la + +test_bus_gvariant_SOURCES = \ + src/libsystemd/sd-bus/test-bus-gvariant.c + +test_bus_gvariant_LDADD = \ + libsystemd-shared.la \ + $(GLIB_LIBS) + +test_bus_gvariant_CFLAGS = \ + $(GLIB_CFLAGS) + +test_bus_creds_SOURCES = \ + src/libsystemd/sd-bus/test-bus-creds.c + +test_bus_creds_LDADD = \ + libsystemd-shared.la + +test_bus_match_SOURCES = \ + src/libsystemd/sd-bus/test-bus-match.c + +test_bus_match_LDADD = \ + libsystemd-shared.la + +test_bus_kernel_SOURCES = \ + src/libsystemd/sd-bus/test-bus-kernel.c + +test_bus_kernel_LDADD = \ + libsystemd-shared.la + +test_bus_kernel_bloom_SOURCES = \ + src/libsystemd/sd-bus/test-bus-kernel-bloom.c + +test_bus_kernel_bloom_LDADD = \ + libsystemd-shared.la + +test_bus_benchmark_SOURCES = \ + src/libsystemd/sd-bus/test-bus-benchmark.c + +test_bus_benchmark_LDADD = \ + libsystemd-shared.la + +test_bus_zero_copy_SOURCES = \ + src/libsystemd/sd-bus/test-bus-zero-copy.c + +test_bus_zero_copy_LDADD = \ + libsystemd-shared.la + +test_bus_introspect_SOURCES = \ + src/libsystemd/sd-bus/test-bus-introspect.c + +test_bus_introspect_LDADD = \ + libsystemd-shared.la + +test_event_SOURCES = \ + src/libsystemd/sd-event/test-event.c + +test_event_LDADD = \ + libsystemd-shared.la + +test_netlink_SOURCES = \ + src/libsystemd/sd-netlink/test-netlink.c + +test_netlink_LDADD = \ + libsystemd-shared.la + +test_local_addresses_SOURCES = \ + src/libsystemd/sd-netlink/test-local-addresses.c + +test_local_addresses_LDADD = \ + libsystemd-shared.la + +test_resolve_SOURCES = \ + src/libsystemd/sd-resolve/test-resolve.c + +test_resolve_LDADD = \ + libsystemd-shared.la + + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libudev/Makefile b/src/libudev/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/libudev/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/libudev/Makefile b/src/libudev/Makefile new file mode 100644 index 0000000000..89189839b1 --- /dev/null +++ b/src/libudev/Makefile @@ -0,0 +1,64 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +LIBUDEV_CURRENT=7 +LIBUDEV_REVISION=5 +LIBUDEV_AGE=6 + +include_HEADERS += \ + src/libudev/libudev.h + +rootlib_LTLIBRARIES += \ + libudev.la + +libudev_la_LDFLAGS = \ + -version-info $(LIBUDEV_CURRENT):$(LIBUDEV_REVISION):$(LIBUDEV_AGE) \ + -Wl,--version-script=$(srcdir)/libudev.sym + +libudev_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la + +pkgconfiglib_DATA += \ + src/libudev/libudev.pc + +EXTRA_DIST += \ + src/libudev/libudev.pc.in + +test-libudev-sym.c: \ + src/libudev/libudev.sym \ + src/udev/udev.h + $(generate-sym-test) + +nodist_test_libudev_sym_SOURCES = \ + test-libudev-sym.c +test_libudev_sym_CFLAGS = \ + -Wno-deprecated-declarations +test_libudev_sym_LDADD = \ + libudev.la + +nested.subdirs += src + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libudev/include/libudev.h b/src/libudev/include/libudev.h new file mode 100644 index 0000000000..3f6d0ed16c --- /dev/null +++ b/src/libudev/include/libudev.h @@ -0,0 +1,207 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifndef _LIBUDEV_H_ +#define _LIBUDEV_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * udev - library context + * + * reads the udev config and system environment + * allows custom logging + */ +struct udev; +struct udev *udev_ref(struct udev *udev); +struct udev *udev_unref(struct udev *udev); +struct udev *udev_new(void); +void udev_set_log_fn(struct udev *udev, + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args)) __attribute__ ((deprecated)); +int udev_get_log_priority(struct udev *udev) __attribute__ ((deprecated)); +void udev_set_log_priority(struct udev *udev, int priority) __attribute__ ((deprecated)); +void *udev_get_userdata(struct udev *udev); +void udev_set_userdata(struct udev *udev, void *userdata); + +/* + * udev_list + * + * access to libudev generated lists + */ +struct udev_list_entry; +struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); +struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); +const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); +const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); +/** + * udev_list_entry_foreach: + * @list_entry: entry to store the current position + * @first_entry: first entry to start with + * + * Helper to iterate over all entries of a list. + */ +#define udev_list_entry_foreach(list_entry, first_entry) \ + for (list_entry = first_entry; \ + list_entry != NULL; \ + list_entry = udev_list_entry_get_next(list_entry)) + +/* + * udev_device + * + * access to sysfs/kernel devices + */ +struct udev_device; +struct udev_device *udev_device_ref(struct udev_device *udev_device); +struct udev_device *udev_device_unref(struct udev_device *udev_device); +struct udev *udev_device_get_udev(struct udev_device *udev_device); +struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); +struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); +struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); +struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id); +struct udev_device *udev_device_new_from_environment(struct udev *udev); +/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ +struct udev_device *udev_device_get_parent(struct udev_device *udev_device); +struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, + const char *subsystem, const char *devtype); +/* retrieve device properties */ +const char *udev_device_get_devpath(struct udev_device *udev_device); +const char *udev_device_get_subsystem(struct udev_device *udev_device); +const char *udev_device_get_devtype(struct udev_device *udev_device); +const char *udev_device_get_syspath(struct udev_device *udev_device); +const char *udev_device_get_sysname(struct udev_device *udev_device); +const char *udev_device_get_sysnum(struct udev_device *udev_device); +const char *udev_device_get_devnode(struct udev_device *udev_device); +int udev_device_get_is_initialized(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); +const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); +const char *udev_device_get_driver(struct udev_device *udev_device); +dev_t udev_device_get_devnum(struct udev_device *udev_device); +const char *udev_device_get_action(struct udev_device *udev_device); +unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); +unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device); +const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); +int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value); +int udev_device_has_tag(struct udev_device *udev_device, const char *tag); + +/* + * udev_monitor + * + * access to kernel uevents and udev events + */ +struct udev_monitor; +struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); +struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor); +struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); +/* kernel and udev generated events over netlink */ +struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); +/* bind socket */ +int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); +int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); +int udev_monitor_get_fd(struct udev_monitor *udev_monitor); +struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); +/* in-kernel socket filters to select messages that get delivered to a listener */ +int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, + const char *subsystem, const char *devtype); +int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); +int udev_monitor_filter_update(struct udev_monitor *udev_monitor); +int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); + +/* + * udev_enumerate + * + * search sysfs for specific devices and provide a sorted list + */ +struct udev_enumerate; +struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); +struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate); +struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); +struct udev_enumerate *udev_enumerate_new(struct udev *udev); +/* device properties filter */ +int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); +int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); +int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); +int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); +int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); +int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); +int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); +int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); +int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); +int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); +/* run enumeration with active filters */ +int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); +int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); +/* return device list */ +struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); + +/* + * udev_queue + * + * access to the currently running udev events + */ +struct udev_queue; +struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); +struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue); +struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); +struct udev_queue *udev_queue_new(struct udev *udev); +unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated)); +unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated)); +int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); +int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); +int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) __attribute__ ((deprecated)); +int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, + unsigned long long int start, unsigned long long int end) __attribute__ ((deprecated)); +int udev_queue_get_fd(struct udev_queue *udev_queue); +int udev_queue_flush(struct udev_queue *udev_queue); +struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) __attribute__ ((deprecated)); + +/* + * udev_hwdb + * + * access to the static hardware properties database + */ +struct udev_hwdb; +struct udev_hwdb *udev_hwdb_new(struct udev *udev); +struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb); +struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb); +struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags); + +/* + * udev_util + * + * udev specific utilities + */ +int udev_util_encode_string(const char *str, char *str_enc, size_t len); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h deleted file mode 100644 index 0e9af8ec09..0000000000 --- a/src/libudev/libudev-device-internal.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include "libudev.h" -#include "sd-device.h" - -#include "libudev-private.h" - -/** - * udev_device: - * - * Opaque object representing one kernel sys device. - */ -struct udev_device { - struct udev *udev; - - /* real device object */ - sd_device *device; - - /* legacy */ - int refcount; - - struct udev_device *parent; - bool parent_set; - - struct udev_list properties; - uint64_t properties_generation; - struct udev_list tags; - uint64_t tags_generation; - struct udev_list devlinks; - uint64_t devlinks_generation; - bool properties_read:1; - bool tags_read:1; - bool devlinks_read:1; - struct udev_list sysattrs; - bool sysattrs_read; -}; - -struct udev_device *udev_device_new(struct udev *udev); diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c deleted file mode 100644 index 2aae0726c1..0000000000 --- a/src/libudev/libudev-device-private.c +++ /dev/null @@ -1,411 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include "libudev.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; - int r; - - assert(udev_device); - - if (udev_device_old) - device_old = udev_device_old->device; - - r = device_tag_index(udev_device->device, device_old, add); - if (r < 0) - return r; - - return 0; -} - -int udev_device_update_db(struct udev_device *udev_device) { - int r; - - assert(udev_device); - - r = device_update_db(udev_device->device); - if (r < 0) - return r; - - return 0; -} - -int udev_device_delete_db(struct udev_device *udev_device) { - int r; - - assert(udev_device); - - r = device_delete_db(udev_device->device); - if (r < 0) - return r; - - return 0; -} - -int udev_device_get_ifindex(struct udev_device *udev_device) { - int r, ifindex; - - assert(udev_device); - - r = sd_device_get_ifindex(udev_device->device, &ifindex); - if (r < 0) - return r; - - return ifindex; -} - -const char *udev_device_get_devpath_old(struct udev_device *udev_device) { - const char *devpath_old = NULL; - int r; - - assert(udev_device); - - r = sd_device_get_property_value(udev_device->device, "DEVPATH_OLD", &devpath_old); - if (r < 0 && r != -ENOENT) { - errno = -r; - return NULL; - } - - return devpath_old; -} - -mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) { - mode_t mode; - int r; - - assert(udev_device); - - r = device_get_devnode_mode(udev_device->device, &mode); - if (r < 0) { - errno = -r; - return 0; - } - - return mode; -} - -uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) { - uid_t uid; - int r; - - assert(udev_device); - - r = device_get_devnode_uid(udev_device->device, &uid); - if (r < 0) { - errno = -r; - return 0; - } - - return uid; -} - -gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) { - gid_t gid; - int r; - - assert(udev_device); - - r = device_get_devnode_gid(udev_device->device, &gid); - if (r < 0) { - errno = -r; - return 0; - } - - return gid; -} - -void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) { - assert(udev_device); - - 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) { - char **envp; - int r; - - assert(udev_device); - - r = device_get_properties_strv(udev_device->device, &envp); - if (r < 0) { - errno = -r; - return NULL; - } - - return envp; -} - -ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) { - const char *nulstr; - size_t len; - int r; - - assert(udev_device); - assert(buf); - - r = device_get_properties_nulstr(udev_device->device, (const uint8_t **)&nulstr, &len); - if (r < 0) - return r; - - *buf = nulstr; - - return len; -} - -int udev_device_get_devlink_priority(struct udev_device *udev_device) { - int priority, r; - - assert(udev_device); - - r = device_get_devlink_priority(udev_device->device, &priority); - if (r < 0) - return r; - - return priority; -} - -int udev_device_get_watch_handle(struct udev_device *udev_device) { - int handle, r; - - assert(udev_device); - - r = device_get_watch_handle(udev_device->device, &handle); - if (r < 0) - return r; - - return handle; -} - -void udev_device_set_is_initialized(struct udev_device *udev_device) { - assert(udev_device); - - device_set_is_initialized(udev_device->device); -} - -int udev_device_rename(struct udev_device *udev_device, const char *name) { - int r; - - assert(udev_device); - - r = device_rename(udev_device->device, name); - if (r < 0) - return r; - - return 0; -} - -struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) { - struct udev_device *device; - int r; - - assert(old_device); - - device = udev_device_new(old_device->udev); - if (!device) - return NULL; - - r = device_shallow_clone(old_device->device, &device->device); - if (r < 0) { - udev_device_unref(device); - errno = -r; - return NULL; - } - - return device; -} - -struct udev_device *udev_device_clone_with_db(struct udev_device *udev_device_old) { - struct udev_device *udev_device; - int r; - - assert(udev_device_old); - - udev_device = udev_device_new(udev_device_old->udev); - if (!udev_device) - return NULL; - - r = device_clone_with_db(udev_device_old->device, &udev_device->device); - if (r < 0) { - udev_device_unref(udev_device); - errno = -r; - return NULL; - } - - return udev_device; -} - -struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) { - struct udev_device *device; - int r; - - device = udev_device_new(udev); - if (!device) - return NULL; - - r = device_new_from_nulstr(&device->device, (uint8_t*)nulstr, buflen); - if (r < 0) { - udev_device_unref(device); - errno = -r; - return NULL; - } - - return device; -} - -struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action) { - struct udev_device *device; - int r; - - device = udev_device_new(udev); - if (!device) - return NULL; - - r = device_new_from_synthetic_event(&device->device, syspath, action); - if (r < 0) { - udev_device_unref(device); - errno = -r; - return NULL; - } - - return device; -} - -int udev_device_copy_properties(struct udev_device *udev_device_dst, struct udev_device *udev_device_src) { - int r; - - assert(udev_device_dst); - assert(udev_device_src); - - r = device_copy_properties(udev_device_dst->device, udev_device_src->device); - if (r < 0) - return r; - - return 0; -} - -const char *udev_device_get_id_filename(struct udev_device *udev_device) { - const char *filename; - int r; - - assert(udev_device); - - r = device_get_id_filename(udev_device->device, &filename); - if (r < 0) { - errno = -r; - return NULL; - } - - return filename; -} - -int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) { - - assert(udev_device); - - device_set_watch_handle(udev_device->device, handle); - - return 0; -} - -void udev_device_set_db_persist(struct udev_device *udev_device) { - assert(udev_device); - - device_set_db_persist(udev_device->device); -} - -int udev_device_set_devlink_priority(struct udev_device *udev_device, int priority) { - assert(udev_device); - - device_set_devlink_priority(udev_device->device, priority); - - return 0; -} - -int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) { - int r; - - assert(udev_device); - - r = device_add_devlink(udev_device->device, devlink); - if (r < 0) - return r; - - return 0; -} - -int udev_device_add_property(struct udev_device *udev_device, const char *property, const char *value) { - int r; - - assert(udev_device); - - r = device_add_property(udev_device->device, property, value); - if (r < 0) - return r; - - return 0; -} - -int udev_device_add_tag(struct udev_device *udev_device, const char *tag) { - int r; - - assert(udev_device); - - r = device_add_tag(udev_device->device, tag); - if (r < 0) - return r; - - return 0; -} - -void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) { - assert(udev_device); - - device_remove_tag(udev_device->device, tag); -} - -void udev_device_cleanup_tags_list(struct udev_device *udev_device) { - assert(udev_device); - - device_cleanup_tags(udev_device->device); -} - -void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) { - assert(udev_device); - - device_cleanup_devlinks(udev_device->device); -} - -void udev_device_set_info_loaded(struct udev_device *udev_device) { - assert(udev_device); - - device_seal(udev_device->device); -} - -void udev_device_read_db(struct udev_device *udev_device) { - assert(udev_device); - - device_read_db_force(udev_device->device); -} diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c deleted file mode 100644 index 995bf56586..0000000000 --- a/src/libudev/libudev-device.c +++ /dev/null @@ -1,958 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "sd-device.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 - * @short_description: kernel sys devices - * - * Representation of kernel sys devices. Devices are uniquely identified - * by their syspath, every device has exactly one path in the kernel sys - * filesystem. Devices usually belong to a kernel subsystem, and have - * a unique name inside that subsystem. - */ - -/** - * udev_device_get_seqnum: - * @udev_device: udev device - * - * This is only valid if the device was received through a monitor. Devices read from - * sys do not have a sequence number. - * - * Returns: the kernel event sequence number, or 0 if there is no sequence number available. - **/ -_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) -{ - const char *seqnum; - unsigned long long ret; - int r; - - assert_return_errno(udev_device, 0, EINVAL); - - r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum); - if (r == -ENOENT) - return 0; - else if (r < 0) { - errno = -r; - return 0; - } - - r = safe_atollu(seqnum, &ret); - if (r < 0) { - errno = -r; - return 0; - } - - return ret; -} - -/** - * udev_device_get_devnum: - * @udev_device: udev device - * - * Get the device major/minor number. - * - * Returns: the dev_t number. - **/ -_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device) -{ - dev_t devnum; - int r; - - assert_return_errno(udev_device, makedev(0, 0), EINVAL); - - r = sd_device_get_devnum(udev_device->device, &devnum); - if (r < 0) { - errno = -r; - return makedev(0, 0); - } - - return devnum; -} - -/** - * udev_device_get_driver: - * @udev_device: udev device - * - * Get the kernel driver name. - * - * Returns: the driver name string, or #NULL if there is no driver attached. - **/ -_public_ const char *udev_device_get_driver(struct udev_device *udev_device) -{ - const char *driver; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_driver(udev_device->device, &driver); - if (r < 0) { - errno = -r; - return NULL; - } - - return driver; -} - -/** - * udev_device_get_devtype: - * @udev_device: udev device - * - * Retrieve the devtype string of the udev device. - * - * Returns: the devtype name of the udev device, or #NULL if it can not be determined - **/ -_public_ const char *udev_device_get_devtype(struct udev_device *udev_device) -{ - const char *devtype; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_devtype(udev_device->device, &devtype); - if (r < 0) { - errno = -r; - return NULL; - } - - return devtype; -} - -/** - * udev_device_get_subsystem: - * @udev_device: udev device - * - * Retrieve the subsystem string of the udev device. The string does not - * contain any "/". - * - * Returns: the subsystem name of the udev device, or #NULL if it can not be determined - **/ -_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device) -{ - const char *subsystem; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_subsystem(udev_device->device, &subsystem); - if (r < 0) { - errno = -r; - return NULL; - } else if (!subsystem) - errno = ENODATA; - - return subsystem; -} - -/** - * udev_device_get_property_value: - * @udev_device: udev device - * @key: property name - * - * Get the value of a given property. - * - * Returns: the property string, or #NULL if there is no such property. - **/ -_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) -{ - const char *value = NULL; - int r; - - assert_return_errno(udev_device && key, NULL, EINVAL); - - r = sd_device_get_property_value(udev_device->device, key, &value); - if (r < 0) { - errno = -r; - return NULL; - } - - return value; -} - -struct udev_device *udev_device_new(struct udev *udev) { - struct udev_device *udev_device; - - assert_return_errno(udev, NULL, EINVAL); - - udev_device = new0(struct udev_device, 1); - if (!udev_device) { - errno = ENOMEM; - return NULL; - } - udev_device->refcount = 1; - udev_device->udev = udev; - udev_list_init(udev, &udev_device->properties, true); - udev_list_init(udev, &udev_device->tags, true); - udev_list_init(udev, &udev_device->sysattrs, true); - udev_list_init(udev, &udev_device->devlinks, true); - - return udev_device; -} - -/** - * udev_device_new_from_syspath: - * @udev: udev library context - * @syspath: sys device path including sys directory - * - * Create new udev device, and fill in information from the sys - * device and the udev database entry. The syspath is the absolute - * path to the device, including the sys mount point. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) { - struct udev_device *udev_device; - int r; - - udev_device = udev_device_new(udev); - if (!udev_device) - return NULL; - - r = sd_device_new_from_syspath(&udev_device->device, syspath); - if (r < 0) { - errno = -r; - udev_device_unref(udev_device); - return NULL; - } - - return udev_device; -} - -/** - * udev_device_new_from_devnum: - * @udev: udev library context - * @type: char or block device - * @devnum: device major/minor number - * - * Create new udev device, and fill in information from the sys - * device and the udev database entry. The device is looked-up - * by its major/minor number and type. Character and block device - * numbers are not unique across the two types. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) -{ - struct udev_device *udev_device; - int r; - - udev_device = udev_device_new(udev); - if (!udev_device) - return NULL; - - r = sd_device_new_from_devnum(&udev_device->device, type, devnum); - if (r < 0) { - errno = -r; - udev_device_unref(udev_device); - return NULL; - } - - return udev_device; -} - -/** - * udev_device_new_from_device_id: - * @udev: udev library context - * @id: text string identifying a kernel device - * - * Create new udev device, and fill in information from the sys - * device and the udev database entry. The device is looked-up - * by a special string: - * b8:2 - block device major:minor - * c128:1 - char device major:minor - * n3 - network device ifindex - * +sound:card29 - kernel driver core subsystem:device name - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) -{ - struct udev_device *udev_device; - int r; - - udev_device = udev_device_new(udev); - if (!udev_device) - return NULL; - - r = sd_device_new_from_device_id(&udev_device->device, id); - if (r < 0) { - errno = -r; - udev_device_unref(udev_device); - return NULL; - } - - return udev_device; -} - -/** - * udev_device_new_from_subsystem_sysname: - * @udev: udev library context - * @subsystem: the subsystem of the device - * @sysname: the name of the device - * - * Create new udev device, and fill in information from the sys device - * and the udev database entry. The device is looked up by the subsystem - * and name string of the device, like "mem" / "zero", or "block" / "sda". - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) -{ - struct udev_device *udev_device; - int r; - - udev_device = udev_device_new(udev); - if (!udev_device) - return NULL; - - r = sd_device_new_from_subsystem_sysname(&udev_device->device, subsystem, sysname); - if (r < 0) { - errno = -r; - udev_device_unref(udev_device); - return NULL; - } - - return udev_device; -} - -/** - * udev_device_new_from_environment - * @udev: udev library context - * - * Create new udev device, and fill in information from the - * current process environment. This only works reliable if - * the process is called from a udev rule. It is usually used - * for tools executed from IMPORT= rules. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev) -{ - struct udev_device *udev_device; - int r; - - udev_device = udev_device_new(udev); - if (!udev_device) - return NULL; - - r = device_new_from_strv(&udev_device->device, environ); - if (r < 0) { - errno = -r; - udev_device_unref(udev_device); - return NULL; - } - - return udev_device; -} - -static struct udev_device *device_new_from_parent(struct udev_device *child) -{ - struct udev_device *parent; - int r; - - assert_return_errno(child, NULL, EINVAL); - - parent = udev_device_new(child->udev); - if (!parent) - return NULL; - - r = sd_device_get_parent(child->device, &parent->device); - if (r < 0) { - errno = -r; - udev_device_unref(parent); - return NULL; - } - - /* the parent is unref'ed with the child, so take a ref from libudev as well */ - sd_device_ref(parent->device); - - return parent; -} - -/** - * udev_device_get_parent: - * @udev_device: the device to start searching from - * - * Find the next parent device, and fill in information from the sys - * device and the udev database entry. - * - * Returned device is not referenced. It is attached to the child - * device, and will be cleaned up when the child device is cleaned up. - * - * It is not necessarily just the upper level directory, empty or not - * recognized sys directories are ignored. - * - * It can be called as many times as needed, without caring about - * references. - * - * Returns: a new udev device, or #NULL, if it no parent exist. - **/ -_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - if (!udev_device->parent_set) { - udev_device->parent_set = true; - udev_device->parent = device_new_from_parent(udev_device); - } - - /* TODO: errno will differ here in case parent == NULL */ - return udev_device->parent; -} - -/** - * udev_device_get_parent_with_subsystem_devtype: - * @udev_device: udev device to start searching from - * @subsystem: the subsystem of the device - * @devtype: the type (DEVTYPE) of the device - * - * Find the next parent device, with a matching subsystem and devtype - * value, and fill in information from the sys device and the udev - * database entry. - * - * If devtype is #NULL, only subsystem is checked, and any devtype will - * match. - * - * Returned device is not referenced. It is attached to the child - * device, and will be cleaned up when the child device is cleaned up. - * - * It can be called as many times as needed, without caring about - * references. - * - * Returns: a new udev device, or #NULL if no matching parent exists. - **/ -_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) -{ - sd_device *parent; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - /* this relies on the fact that finding the subdevice of a parent or the - parent of a subdevice commute */ - - /* first find the correct sd_device */ - r = sd_device_get_parent_with_subsystem_devtype(udev_device->device, subsystem, devtype, &parent); - if (r < 0) { - errno = -r; - return NULL; - } - - /* then walk the chain of udev_device parents until the correspanding - one is found */ - while ((udev_device = udev_device_get_parent(udev_device))) { - if (udev_device->device == parent) - return udev_device; - } - - errno = ENOENT; - return NULL; -} - -/** - * udev_device_get_udev: - * @udev_device: udev device - * - * Retrieve the udev library context the device was created with. - * - * Returns: the udev library context - **/ -_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - return udev_device->udev; -} - -/** - * udev_device_ref: - * @udev_device: udev device - * - * Take a reference of a udev device. - * - * Returns: the passed udev device - **/ -_public_ struct udev_device *udev_device_ref(struct udev_device *udev_device) -{ - if (udev_device) - udev_device->refcount++; - - return udev_device; -} - -/** - * udev_device_unref: - * @udev_device: udev device - * - * Drop a reference of a udev device. If the refcount reaches zero, - * the resources of the device will be released. - * - * Returns: #NULL - **/ -_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) -{ - if (udev_device && (-- udev_device->refcount) == 0) { - sd_device_unref(udev_device->device); - udev_device_unref(udev_device->parent); - - udev_list_cleanup(&udev_device->properties); - udev_list_cleanup(&udev_device->sysattrs); - udev_list_cleanup(&udev_device->tags); - udev_list_cleanup(&udev_device->devlinks); - - free(udev_device); - } - - return NULL; -} - -/** - * udev_device_get_devpath: - * @udev_device: udev device - * - * Retrieve the kernel devpath value of the udev device. The path - * does not contain the sys mount point, and starts with a '/'. - * - * Returns: the devpath of the udev device - **/ -_public_ const char *udev_device_get_devpath(struct udev_device *udev_device) -{ - const char *devpath; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_devpath(udev_device->device, &devpath); - if (r < 0) { - errno = -r; - return NULL; - } - - return devpath; -} - -/** - * udev_device_get_syspath: - * @udev_device: udev device - * - * Retrieve the sys path of the udev device. The path is an - * absolute path and starts with the sys mount point. - * - * Returns: the sys path of the udev device - **/ -_public_ const char *udev_device_get_syspath(struct udev_device *udev_device) -{ - const char *syspath; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_syspath(udev_device->device, &syspath); - if (r < 0) { - errno = -r; - return NULL; - } - - return syspath; -} - -/** - * udev_device_get_sysname: - * @udev_device: udev device - * - * Get the kernel device name in /sys. - * - * Returns: the name string of the device - **/ -_public_ const char *udev_device_get_sysname(struct udev_device *udev_device) -{ - const char *sysname; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_sysname(udev_device->device, &sysname); - if (r < 0) { - errno = -r; - return NULL; - } - - return sysname; -} - -/** - * udev_device_get_sysnum: - * @udev_device: udev device - * - * Get the instance number of the device. - * - * Returns: the trailing number string of the device name - **/ -_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device) -{ - const char *sysnum; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_sysnum(udev_device->device, &sysnum); - if (r < 0) { - errno = -r; - return NULL; - } - - return sysnum; -} - -/** - * udev_device_get_devnode: - * @udev_device: udev device - * - * Retrieve the device node file name belonging to the udev device. - * The path is an absolute path, and starts with the device directory. - * - * Returns: the device node file name of the udev device, or #NULL if no device node exists - **/ -_public_ const char *udev_device_get_devnode(struct udev_device *udev_device) -{ - const char *devnode; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_devname(udev_device->device, &devnode); - if (r < 0) { - errno = -r; - return NULL; - } - - return devnode; -} - -/** - * udev_device_get_devlinks_list_entry: - * @udev_device: udev device - * - * Retrieve the list of device links pointing to the device file of - * the udev device. The next list entry can be retrieved with - * udev_list_entry_get_next(), which returns #NULL if no more entries exist. - * The devlink path can be retrieved from the list entry by - * udev_list_entry_get_name(). The path is an absolute path, and starts with - * the device directory. - * - * Returns: the first entry of the device node link list - **/ -_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - if (device_get_devlinks_generation(udev_device->device) != udev_device->devlinks_generation || - !udev_device->devlinks_read) { - const char *devlink; - - udev_list_cleanup(&udev_device->devlinks); - - FOREACH_DEVICE_DEVLINK(udev_device->device, devlink) - udev_list_entry_add(&udev_device->devlinks, devlink, NULL); - - udev_device->devlinks_read = true; - udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device); - } - - return udev_list_get_entry(&udev_device->devlinks); -} - -/** - * udev_device_get_event_properties_entry: - * @udev_device: udev device - * - * Retrieve the list of key/value device properties of the udev - * device. The next list entry can be retrieved with udev_list_entry_get_next(), - * which returns #NULL if no more entries exist. The property name - * can be retrieved from the list entry by udev_list_entry_get_name(), - * the property value by udev_list_entry_get_value(). - * - * Returns: the first entry of the property list - **/ -_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - if (device_get_properties_generation(udev_device->device) != udev_device->properties_generation || - !udev_device->properties_read) { - const char *key, *value; - - udev_list_cleanup(&udev_device->properties); - - FOREACH_DEVICE_PROPERTY(udev_device->device, key, value) - udev_list_entry_add(&udev_device->properties, key, value); - - udev_device->properties_read = true; - udev_device->properties_generation = device_get_properties_generation(udev_device->device); - } - - return udev_list_get_entry(&udev_device->properties); -} - -/** - * udev_device_get_action: - * @udev_device: udev device - * - * This is only valid if the device was received through a monitor. Devices read from - * sys do not have an action string. Usual actions are: add, remove, change, online, - * offline. - * - * Returns: the kernel action value, or #NULL if there is no action value available. - **/ -_public_ const char *udev_device_get_action(struct udev_device *udev_device) { - const char *action = NULL; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_property_value(udev_device->device, "ACTION", &action); - if (r < 0 && r != -ENOENT) { - errno = -r; - return NULL; - } - - return action; -} - -/** - * udev_device_get_usec_since_initialized: - * @udev_device: udev device - * - * Return the number of microseconds passed since udev set up the - * device for the first time. - * - * This is only implemented for devices with need to store properties - * in the udev database. All other devices return 0 here. - * - * Returns: the number of microseconds since the device was first seen. - **/ -_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) -{ - usec_t ts; - int r; - - assert_return(udev_device, -EINVAL); - - r = sd_device_get_usec_since_initialized(udev_device->device, &ts); - if (r < 0) { - errno = EINVAL; - return 0; - } - - return ts; -} - -/** - * udev_device_get_sysattr_value: - * @udev_device: udev device - * @sysattr: attribute name - * - * The retrieved value is cached in the device. Repeated calls will return the same - * value and not open the attribute again. - * - * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. - **/ -_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) -{ - const char *value; - int r; - - assert_return_errno(udev_device, NULL, EINVAL); - - r = sd_device_get_sysattr_value(udev_device->device, sysattr, &value); - if (r < 0) { - errno = -r; - return NULL; - } - - return value; -} - -/** - * udev_device_set_sysattr_value: - * @udev_device: udev device - * @sysattr: attribute name - * @value: new value to be set - * - * Update the contents of the sys attribute and the cached value of the device. - * - * Returns: Negative error code on failure or 0 on success. - **/ -_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value) -{ - int r; - - assert_return(udev_device, -EINVAL); - - r = sd_device_set_sysattr_value(udev_device->device, sysattr, value); - if (r < 0) - return r; - - return 0; -} - -/** - * udev_device_get_sysattr_list_entry: - * @udev_device: udev device - * - * Retrieve the list of available sysattrs, with value being empty; - * This just return all available sysfs attributes for a particular - * device without reading their values. - * - * Returns: the first entry of the property list - **/ -_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - if (!udev_device->sysattrs_read) { - const char *sysattr; - - udev_list_cleanup(&udev_device->sysattrs); - - FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr) - udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL); - - udev_device->sysattrs_read = true; - } - - return udev_list_get_entry(&udev_device->sysattrs); -} - -/** - * udev_device_get_is_initialized: - * @udev_device: udev device - * - * Check if udev has already handled the device and has set up - * device node permissions and context, or has renamed a network - * device. - * - * This is only implemented for devices with a device node - * or network interfaces. All other devices return 1 here. - * - * Returns: 1 if the device is set up. 0 otherwise. - **/ -_public_ int udev_device_get_is_initialized(struct udev_device *udev_device) -{ - int r, initialized; - - assert_return(udev_device, -EINVAL); - - r = sd_device_get_is_initialized(udev_device->device, &initialized); - if (r < 0) { - errno = -r; - - return 0; - } - - return initialized; -} - -/** - * udev_device_get_tags_list_entry: - * @udev_device: udev device - * - * Retrieve the list of tags attached to the udev device. The next - * list entry can be retrieved with udev_list_entry_get_next(), - * which returns #NULL if no more entries exist. The tag string - * can be retrieved from the list entry by udev_list_entry_get_name(). - * - * Returns: the first entry of the tag list - **/ -_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) -{ - assert_return_errno(udev_device, NULL, EINVAL); - - if (device_get_tags_generation(udev_device->device) != udev_device->tags_generation || - !udev_device->tags_read) { - const char *tag; - - udev_list_cleanup(&udev_device->tags); - - FOREACH_DEVICE_TAG(udev_device->device, tag) - udev_list_entry_add(&udev_device->tags, tag, NULL); - - udev_device->tags_read = true; - udev_device->tags_generation = device_get_tags_generation(udev_device->device); - } - - return udev_list_get_entry(&udev_device->tags); -} - -/** - * udev_device_has_tag: - * @udev_device: udev device - * @tag: tag name - * - * Check if a given device has a certain tag associated. - * - * Returns: 1 if the tag is found. 0 otherwise. - **/ -_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag) -{ - assert_return(udev_device, 0); - - return sd_device_has_tag(udev_device->device, tag); -} diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c deleted file mode 100644 index 3b8abfb260..0000000000 --- a/src/libudev/libudev-enumerate.c +++ /dev/null @@ -1,419 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "sd-device.h" - -#include "alloc-util.h" -#include "device-enumerator-private.h" -#include "device-util.h" -#include "libudev-device-internal.h" - -/** - * SECTION:libudev-enumerate - * @short_description: lookup and sort sys devices - * - * Lookup devices in the sys filesystem, filter devices by properties, - * and return a sorted list of devices. - */ - -/** - * udev_enumerate: - * - * Opaque object representing one device lookup/sort context. - */ -struct udev_enumerate { - struct udev *udev; - int refcount; - struct udev_list devices_list; - bool devices_uptodate:1; - - sd_device_enumerator *enumerator; -}; - -/** - * udev_enumerate_new: - * @udev: udev library context - * - * Create an enumeration context to scan /sys. - * - * Returns: an enumeration context. - **/ -_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) { - _cleanup_free_ struct udev_enumerate *udev_enumerate = NULL; - struct udev_enumerate *ret; - int r; - - assert_return_errno(udev, NULL, EINVAL); - - udev_enumerate = new0(struct udev_enumerate, 1); - if (!udev_enumerate) { - errno = ENOMEM; - return NULL; - } - - r = sd_device_enumerator_new(&udev_enumerate->enumerator); - if (r < 0) { - errno = -r; - return NULL; - } - - r = sd_device_enumerator_allow_uninitialized(udev_enumerate->enumerator); - if (r < 0) { - errno = -r; - return NULL; - } - - udev_enumerate->refcount = 1; - udev_enumerate->udev = udev; - - udev_list_init(udev, &udev_enumerate->devices_list, false); - - ret = udev_enumerate; - udev_enumerate = NULL; - - return ret; -} - -/** - * udev_enumerate_ref: - * @udev_enumerate: context - * - * Take a reference of a enumeration context. - * - * Returns: the passed enumeration context - **/ -_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) { - if (udev_enumerate) - udev_enumerate->refcount++; - - return udev_enumerate; -} - -/** - * udev_enumerate_unref: - * @udev_enumerate: context - * - * Drop a reference of an enumeration context. If the refcount reaches zero, - * all resources of the enumeration context will be released. - * - * Returns: #NULL - **/ -_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) { - if (udev_enumerate && (-- udev_enumerate->refcount) == 0) { - udev_list_cleanup(&udev_enumerate->devices_list); - sd_device_enumerator_unref(udev_enumerate->enumerator); - free(udev_enumerate); - } - - return NULL; -} - -/** - * udev_enumerate_get_udev: - * @udev_enumerate: context - * - * Get the udev library context. - * - * Returns: a pointer to the context. - */ -_public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) { - assert_return_errno(udev_enumerate, NULL, EINVAL); - - return udev_enumerate->udev; -} - -/** - * udev_enumerate_get_list_entry: - * @udev_enumerate: context - * - * Get the first entry of the sorted list of device paths. - * - * Returns: a udev_list_entry. - */ -_public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) { - assert_return_errno(udev_enumerate, NULL, EINVAL); - - if (!udev_enumerate->devices_uptodate) { - sd_device *device; - - udev_list_cleanup(&udev_enumerate->devices_list); - - FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) { - const char *syspath; - int r; - - r = sd_device_get_syspath(device, &syspath); - if (r < 0) { - errno = -r; - return NULL; - } - - udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL); - } - - udev_enumerate->devices_uptodate = true; - } - - return udev_list_get_entry(&udev_enumerate->devices_list); -} - -/** - * udev_enumerate_add_match_subsystem: - * @udev_enumerate: context - * @subsystem: filter for a subsystem of the device to include in the list - * - * Match only devices belonging to a certain kernel subsystem. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { - assert_return(udev_enumerate, -EINVAL); - - if (!subsystem) - return 0; - - return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true); -} - -/** - * udev_enumerate_add_nomatch_subsystem: - * @udev_enumerate: context - * @subsystem: filter for a subsystem of the device to exclude from the list - * - * Match only devices not belonging to a certain kernel subsystem. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { - assert_return(udev_enumerate, -EINVAL); - - if (!subsystem) - return 0; - - return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false); -} - -/** - * udev_enumerate_add_match_sysattr: - * @udev_enumerate: context - * @sysattr: filter for a sys attribute at the device to include in the list - * @value: optional value of the sys attribute - * - * Match only devices with a certain /sys device attribute. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { - assert_return(udev_enumerate, -EINVAL); - - if (!sysattr) - return 0; - - return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true); -} - -/** - * udev_enumerate_add_nomatch_sysattr: - * @udev_enumerate: context - * @sysattr: filter for a sys attribute at the device to exclude from the list - * @value: optional value of the sys attribute - * - * Match only devices not having a certain /sys device attribute. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { - assert_return(udev_enumerate, -EINVAL); - - if (!sysattr) - return 0; - - return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false); -} - -/** - * udev_enumerate_add_match_property: - * @udev_enumerate: context - * @property: filter for a property of the device to include in the list - * @value: value of the property - * - * Match only devices with a certain property. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) { - assert_return(udev_enumerate, -EINVAL); - - if (!property) - return 0; - - return sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value); -} - -/** - * udev_enumerate_add_match_tag: - * @udev_enumerate: context - * @tag: filter for a tag of the device to include in the list - * - * Match only devices with a certain tag. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) { - assert_return(udev_enumerate, -EINVAL); - - if (!tag) - return 0; - - return sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag); -} - -/** - * udev_enumerate_add_match_parent: - * @udev_enumerate: context - * @parent: parent device where to start searching - * - * Return the devices on the subtree of one given device. The parent - * itself is included in the list. - * - * A reference for the device is held until the udev_enumerate context - * is cleaned up. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) { - assert_return(udev_enumerate, -EINVAL); - - if (!parent) - return 0; - - return sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device); -} - -/** - * udev_enumerate_add_match_is_initialized: - * @udev_enumerate: context - * - * Match only devices which udev has set up already. This makes - * sure, that the device node permissions and context are properly set - * and that network devices are fully renamed. - * - * Usually, devices which are found in the kernel but not already - * handled by udev, have still pending events. Services should subscribe - * to monitor events and wait for these devices to become ready, instead - * of using uninitialized devices. - * - * For now, this will not affect devices which do not have a device node - * and are not network interfaces. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) { - assert_return(udev_enumerate, -EINVAL); - - return device_enumerator_add_match_is_initialized(udev_enumerate->enumerator); -} - -/** - * udev_enumerate_add_match_sysname: - * @udev_enumerate: context - * @sysname: filter for the name of the device to include in the list - * - * Match only devices with a given /sys device name. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { - assert_return(udev_enumerate, -EINVAL); - - if (!sysname) - return 0; - - return sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname); -} - -/** - * udev_enumerate_add_syspath: - * @udev_enumerate: context - * @syspath: path of a device - * - * Add a device to the list of devices, to retrieve it back sorted in dependency order. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - int r; - - assert_return(udev_enumerate, -EINVAL); - - if (!syspath) - return 0; - - r = sd_device_new_from_syspath(&device, syspath); - if (r < 0) - return r; - - r = device_enumerator_add_device(udev_enumerate->enumerator, device); - if (r < 0) - return r; - - return 0; -} - -/** - * udev_enumerate_scan_devices: - * @udev_enumerate: udev enumeration context - * - * Scan /sys for all devices which match the given filters. No matches - * will return all currently available devices. - * - * Returns: 0 on success, otherwise a negative error value. - **/ -_public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) { - assert_return(udev_enumerate, -EINVAL); - - return device_enumerator_scan_devices(udev_enumerate->enumerator); -} - -/** - * udev_enumerate_scan_subsystems: - * @udev_enumerate: udev enumeration context - * - * Scan /sys for all kernel subsystems, including buses, classes, drivers. - * - * Returns: 0 on success, otherwise a negative error value. - **/ -_public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) { - assert_return(udev_enumerate, -EINVAL); - - return device_enumerator_scan_subsystems(udev_enumerate->enumerator); -} diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c deleted file mode 100644 index a53f000015..0000000000 --- a/src/libudev/libudev-hwdb.c +++ /dev/null @@ -1,146 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 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 . -***/ - -#include "sd-hwdb.h" - -#include "alloc-util.h" -#include "hwdb-util.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-hwdb - * @short_description: retrieve properties from the hardware database - * - * Libudev hardware database interface. - */ - -/** - * udev_hwdb: - * - * Opaque object representing the hardware database. - */ -struct udev_hwdb { - struct udev *udev; - int refcount; - - sd_hwdb *hwdb; - - struct udev_list properties_list; -}; - -/** - * udev_hwdb_new: - * @udev: udev library context - * - * Create a hardware database context to query properties for devices. - * - * Returns: a hwdb context. - **/ -_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb_internal = NULL; - struct udev_hwdb *hwdb; - int r; - - assert_return(udev, NULL); - - r = sd_hwdb_new(&hwdb_internal); - if (r < 0) - return NULL; - - hwdb = new0(struct udev_hwdb, 1); - if (!hwdb) - return NULL; - - hwdb->refcount = 1; - hwdb->hwdb = hwdb_internal; - hwdb_internal = NULL; - - udev_list_init(udev, &hwdb->properties_list, true); - - return hwdb; -} - -/** - * udev_hwdb_ref: - * @hwdb: context - * - * Take a reference of a hwdb context. - * - * Returns: the passed enumeration context - **/ -_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) { - if (!hwdb) - return NULL; - hwdb->refcount++; - return hwdb; -} - -/** - * udev_hwdb_unref: - * @hwdb: context - * - * Drop a reference of a hwdb context. If the refcount reaches zero, - * all resources of the hwdb context will be released. - * - * Returns: #NULL - **/ -_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) { - if (!hwdb) - return NULL; - hwdb->refcount--; - if (hwdb->refcount > 0) - return NULL; - sd_hwdb_unref(hwdb->hwdb); - udev_list_cleanup(&hwdb->properties_list); - free(hwdb); - return NULL; -} - -/** - * udev_hwdb_get_properties_list_entry: - * @hwdb: context - * @modalias: modalias string - * @flags: (unused) - * - * Lookup a matching device in the hardware database. The lookup key is a - * modalias string, whose formats are defined for the Linux kernel modules. - * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry - * of a list of retrieved properties is returned. - * - * Returns: a udev_list_entry. - */ -_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) { - const char *key, *value; - - if (!hwdb || !modalias) { - errno = EINVAL; - return NULL; - } - - udev_list_cleanup(&hwdb->properties_list); - - SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value) { - if (udev_list_entry_add(&hwdb->properties_list, key, value) == NULL) { - errno = ENOMEM; - return NULL; - } - } - - return udev_list_get_entry(&hwdb->properties_list); -} diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c deleted file mode 100644 index da496ed456..0000000000 --- a/src/libudev/libudev-list.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-list - * @short_description: list operation - * - * Libudev list operations. - */ - -/** - * udev_list_entry: - * - * Opaque object representing one entry in a list. An entry contains - * contains a name, and optionally a value. - */ -struct udev_list_entry { - struct udev_list_node node; - struct udev_list *list; - char *name; - char *value; - int num; -}; - -/* the list's head points to itself if empty */ -void udev_list_node_init(struct udev_list_node *list) -{ - list->next = list; - list->prev = list; -} - -int udev_list_node_is_empty(struct udev_list_node *list) -{ - return list->next == list; -} - -static void udev_list_node_insert_between(struct udev_list_node *new, - struct udev_list_node *prev, - struct udev_list_node *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) -{ - udev_list_node_insert_between(new, list->prev, list); -} - -void udev_list_node_remove(struct udev_list_node *entry) -{ - struct udev_list_node *prev = entry->prev; - struct udev_list_node *next = entry->next; - - next->prev = prev; - prev->next = next; - - entry->prev = NULL; - entry->next = NULL; -} - -/* return list entry which embeds this node */ -static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) -{ - return container_of(node, struct udev_list_entry, node); -} - -void udev_list_init(struct udev *udev, struct udev_list *list, bool unique) -{ - memzero(list, sizeof(struct udev_list)); - list->udev = udev; - list->unique = unique; - udev_list_node_init(&list->node); -} - -/* insert entry into a list as the last element */ -static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) -{ - /* inserting before the list head make the node the last node in the list */ - udev_list_node_insert_between(&new->node, list->node.prev, &list->node); - new->list = list; -} - -/* insert entry into a list, before a given existing entry */ -static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) -{ - udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); - new->list = entry->list; -} - -/* binary search in sorted array */ -static int list_search(struct udev_list *list, const char *name) -{ - unsigned int first, last; - - first = 0; - last = list->entries_cur; - while (first < last) { - unsigned int i; - int cmp; - - i = (first + last)/2; - cmp = strcmp(name, list->entries[i]->name); - if (cmp < 0) - last = i; - else if (cmp > 0) - first = i+1; - else - return i; - } - - /* not found, return negative insertion-index+1 */ - return -(first+1); -} - -struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) -{ - struct udev_list_entry *entry; - int i = 0; - - if (list->unique) { - /* lookup existing name or insertion-index */ - i = list_search(list, name); - if (i >= 0) { - entry = list->entries[i]; - - free(entry->value); - if (value == NULL) { - entry->value = NULL; - return entry; - } - entry->value = strdup(value); - if (entry->value == NULL) - return NULL; - return entry; - } - } - - /* add new name */ - entry = new0(struct udev_list_entry, 1); - if (entry == NULL) - return NULL; - entry->name = strdup(name); - if (entry->name == NULL) { - free(entry); - return NULL; - } - if (value != NULL) { - entry->value = strdup(value); - if (entry->value == NULL) { - free(entry->name); - free(entry); - return NULL; - } - } - - if (list->unique) { - /* allocate or enlarge sorted array if needed */ - if (list->entries_cur >= list->entries_max) { - struct udev_list_entry **entries; - unsigned int add; - - add = list->entries_max; - if (add < 1) - add = 64; - entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *)); - if (entries == NULL) { - free(entry->name); - free(entry->value); - free(entry); - return NULL; - } - list->entries = entries; - list->entries_max += add; - } - - /* the negative i returned the insertion index */ - i = (-i)-1; - - /* insert into sorted list */ - if ((unsigned int)i < list->entries_cur) - udev_list_entry_insert_before(entry, list->entries[i]); - else - udev_list_entry_append(entry, list); - - /* insert into sorted array */ - memmove(&list->entries[i+1], &list->entries[i], - (list->entries_cur - i) * sizeof(struct udev_list_entry *)); - list->entries[i] = entry; - list->entries_cur++; - } else { - udev_list_entry_append(entry, list); - } - - return entry; -} - -void udev_list_entry_delete(struct udev_list_entry *entry) -{ - if (entry->list->entries != NULL) { - int i; - struct udev_list *list = entry->list; - - /* remove entry from sorted array */ - i = list_search(list, entry->name); - if (i >= 0) { - memmove(&list->entries[i], &list->entries[i+1], - ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *)); - list->entries_cur--; - } - } - - udev_list_node_remove(&entry->node); - free(entry->name); - free(entry->value); - free(entry); -} - -void udev_list_cleanup(struct udev_list *list) -{ - struct udev_list_entry *entry_loop; - struct udev_list_entry *entry_tmp; - - list->entries = mfree(list->entries); - list->entries_cur = 0; - list->entries_max = 0; - udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) - udev_list_entry_delete(entry_loop); -} - -struct udev_list_entry *udev_list_get_entry(struct udev_list *list) -{ - if (udev_list_node_is_empty(&list->node)) - return NULL; - return list_node_to_entry(list->node.next); -} - -/** - * udev_list_entry_get_next: - * @list_entry: current entry - * - * Get the next entry from the list. - * - * Returns: udev_list_entry, #NULL if no more entries are available. - */ -_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) -{ - struct udev_list_node *next; - - if (list_entry == NULL) - return NULL; - next = list_entry->node.next; - /* empty list or no more entries */ - if (next == &list_entry->list->node) - return NULL; - return list_node_to_entry(next); -} - -/** - * udev_list_entry_get_by_name: - * @list_entry: current entry - * @name: name string to match - * - * Lookup an entry in the list with a certain name. - * - * Returns: udev_list_entry, #NULL if no matching entry is found. - */ -_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) -{ - int i; - - if (list_entry == NULL) - return NULL; - - if (!list_entry->list->unique) - return NULL; - - i = list_search(list_entry->list, name); - if (i < 0) - return NULL; - return list_entry->list->entries[i]; -} - -/** - * udev_list_entry_get_name: - * @list_entry: current entry - * - * Get the name of a list entry. - * - * Returns: the name string of this entry. - */ -_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return NULL; - return list_entry->name; -} - -/** - * udev_list_entry_get_value: - * @list_entry: current entry - * - * Get the value of list entry. - * - * Returns: the value string of this entry. - */ -_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return NULL; - return list_entry->value; -} - -int udev_list_entry_get_num(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return -EINVAL; - return list_entry->num; -} - -void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num) -{ - if (list_entry == NULL) - return; - list_entry->num = num; -} diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c deleted file mode 100644 index 1f9d16c450..0000000000 --- a/src/libudev/libudev-monitor.c +++ /dev/null @@ -1,845 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "libudev-private.h" -#include "missing.h" -#include "mount-util.h" -#include "socket-util.h" -#include "string-util.h" - -/** - * SECTION:libudev-monitor - * @short_description: device event source - * - * Connects to a device event source. - */ - -/** - * udev_monitor: - * - * Opaque object handling an event source. - */ -struct udev_monitor { - struct udev *udev; - int refcount; - int sock; - union sockaddr_union snl; - union sockaddr_union snl_trusted_sender; - union sockaddr_union snl_destination; - socklen_t addrlen; - struct udev_list filter_subsystem_list; - struct udev_list filter_tag_list; - bool bound; -}; - -enum udev_monitor_netlink_group { - UDEV_MONITOR_NONE, - UDEV_MONITOR_KERNEL, - UDEV_MONITOR_UDEV, -}; - -#define UDEV_MONITOR_MAGIC 0xfeedcafe -struct udev_monitor_netlink_header { - /* "libudev" prefix to distinguish libudev and kernel messages */ - char prefix[8]; - /* - * magic to protect against daemon <-> library message format mismatch - * used in the kernel from socket filter rules; needs to be stored in network order - */ - unsigned int magic; - /* total length of header structure known to the sender */ - unsigned int header_size; - /* properties string buffer */ - unsigned int properties_off; - unsigned int properties_len; - /* - * hashes of primary device properties strings, to let libudev subscribers - * use in-kernel socket filters; values need to be stored in network order - */ - unsigned int filter_subsystem_hash; - unsigned int filter_devtype_hash; - unsigned int filter_tag_bloom_hi; - unsigned int filter_tag_bloom_lo; -}; - -static struct udev_monitor *udev_monitor_new(struct udev *udev) -{ - struct udev_monitor *udev_monitor; - - udev_monitor = new0(struct udev_monitor, 1); - if (udev_monitor == NULL) - return NULL; - udev_monitor->refcount = 1; - udev_monitor->udev = udev; - udev_list_init(udev, &udev_monitor->filter_subsystem_list, false); - udev_list_init(udev, &udev_monitor->filter_tag_list, true); - return udev_monitor; -} - -/* we consider udev running when /dev is on devtmpfs */ -static bool udev_has_devtmpfs(struct udev *udev) { - - union file_handle_union h = FILE_HANDLE_INIT; - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX], *e; - int mount_id; - int r; - - r = name_to_handle_at(AT_FDCWD, "/dev", &h.handle, &mount_id, 0); - if (r < 0) { - if (errno != EOPNOTSUPP) - log_debug_errno(errno, "name_to_handle_at on /dev: %m"); - return false; - } - - f = fopen("/proc/self/mountinfo", "re"); - if (!f) - return false; - - FOREACH_LINE(line, f, return false) { - int mid; - - if (sscanf(line, "%i", &mid) != 1) - continue; - - if (mid != mount_id) - continue; - - e = strstr(line, " - "); - if (!e) - continue; - - /* accept any name that starts with the currently expected type */ - if (startswith(e + 3, "devtmpfs")) - return true; - } - - return false; -} - -static void monitor_set_nl_address(struct udev_monitor *udev_monitor) { - union sockaddr_union snl; - socklen_t addrlen; - int r; - - assert(udev_monitor); - - /* get the address the kernel has assigned us - * it is usually, but not necessarily the pid - */ - addrlen = sizeof(struct sockaddr_nl); - r = getsockname(udev_monitor->sock, &snl.sa, &addrlen); - if (r >= 0) - udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid; -} - -struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) -{ - struct udev_monitor *udev_monitor; - unsigned int group; - - if (udev == NULL) - return NULL; - - if (name == NULL) - group = UDEV_MONITOR_NONE; - else if (streq(name, "udev")) { - /* - * We do not support subscribing to uevents if no instance of - * udev is running. Uevents would otherwise broadcast the - * processing data of the host into containers, which is not - * desired. - * - * Containers will currently not get any udev uevents, until - * a supporting infrastructure is available. - * - * We do not set a netlink multicast group here, so the socket - * will not receive any messages. - */ - if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) { - log_debug("the udev service seems not to be active, disable the monitor"); - group = UDEV_MONITOR_NONE; - } else - group = UDEV_MONITOR_UDEV; - } else if (streq(name, "kernel")) - group = UDEV_MONITOR_KERNEL; - else - return NULL; - - udev_monitor = udev_monitor_new(udev); - if (udev_monitor == NULL) - return NULL; - - if (fd < 0) { - udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); - if (udev_monitor->sock < 0) { - log_debug_errno(errno, "error getting socket: %m"); - free(udev_monitor); - return NULL; - } - } else { - udev_monitor->bound = true; - udev_monitor->sock = fd; - monitor_set_nl_address(udev_monitor); - } - - udev_monitor->snl.nl.nl_family = AF_NETLINK; - udev_monitor->snl.nl.nl_groups = group; - - /* default destination for sending */ - udev_monitor->snl_destination.nl.nl_family = AF_NETLINK; - udev_monitor->snl_destination.nl.nl_groups = UDEV_MONITOR_UDEV; - - return udev_monitor; -} - -/** - * udev_monitor_new_from_netlink: - * @udev: udev library context - * @name: name of event source - * - * Create new udev monitor and connect to a specified event - * source. Valid sources identifiers are "udev" and "kernel". - * - * Applications should usually not connect directly to the - * "kernel" events, because the devices might not be useable - * at that time, before udev has configured them, and created - * device nodes. Accessing devices at the same time as udev, - * might result in unpredictable behavior. The "udev" events - * are sent out after udev has finished its event processing, - * all rules have been processed, and needed device nodes are - * created. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev monitor. - * - * Returns: a new udev monitor, or #NULL, in case of an error - **/ -_public_ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) -{ - return udev_monitor_new_from_netlink_fd(udev, name, -1); -} - -static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, - unsigned short code, unsigned int data) -{ - struct sock_filter *ins = &inss[*i]; - - ins->code = code; - ins->k = data; - (*i)++; -} - -static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, - unsigned short code, unsigned int data, - unsigned short jt, unsigned short jf) -{ - struct sock_filter *ins = &inss[*i]; - - ins->code = code; - ins->jt = jt; - ins->jf = jf; - ins->k = data; - (*i)++; -} - -/** - * udev_monitor_filter_update: - * @udev_monitor: monitor - * - * Update the installed socket filter. This is only needed, - * if the filter was removed or changed. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) -{ - struct sock_filter ins[512]; - struct sock_fprog filter; - unsigned int i; - struct udev_list_entry *list_entry; - int err; - - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL && - udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) - return 0; - - memzero(ins, sizeof(ins)); - i = 0; - - /* load magic in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); - /* jump if magic matches */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); - /* wrong magic, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) { - int tag_matches; - - /* count tag matches, to calculate end of tag match block */ - tag_matches = 0; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) - tag_matches++; - - /* add all tags matches */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { - uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); - uint32_t tag_bloom_hi = tag_bloom_bits >> 32; - uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; - - /* load device bloom bits in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)); - /* clear bits (tag bits & bloom bits) */ - bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi); - /* jump to next tag if it does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3); - - /* load device bloom bits in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)); - /* clear bits (tag bits & bloom bits) */ - bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo); - /* jump behind end of tag match block if tag matches */ - tag_matches--; - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0); - } - - /* nothing matched, drop packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); - } - - /* add all subsystem matches */ - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) { - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { - unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); - - /* load device subsystem value in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)); - if (udev_list_entry_get_value(list_entry) == NULL) { - /* jump if subsystem does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); - } else { - /* jump if subsystem does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3); - - /* load device devtype value in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)); - /* jump if value does not match */ - hash = util_string_hash32(udev_list_entry_get_value(list_entry)); - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); - } - - /* matched, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - if (i+1 >= ELEMENTSOF(ins)) - return -E2BIG; - } - - /* nothing matched, drop packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); - } - - /* matched, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - /* install filter */ - memzero(&filter, sizeof(filter)); - filter.len = i; - filter.filter = ins; - err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); - return err < 0 ? -errno : 0; -} - -int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) -{ - udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid; - return 0; -} - -/** - * udev_monitor_enable_receiving: - * @udev_monitor: the monitor which should receive events - * - * Binds the @udev_monitor socket to the event source. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) -{ - int err = 0; - const int on = 1; - - udev_monitor_filter_update(udev_monitor); - - if (!udev_monitor->bound) { - err = bind(udev_monitor->sock, - &udev_monitor->snl.sa, sizeof(struct sockaddr_nl)); - if (err == 0) - udev_monitor->bound = true; - } - - if (err >= 0) - monitor_set_nl_address(udev_monitor); - 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)); - if (err < 0) - log_debug_errno(errno, "setting SO_PASSCRED failed: %m"); - - return 0; -} - -/** - * udev_monitor_set_receive_buffer_size: - * @udev_monitor: the monitor which should receive events - * @size: the size in bytes - * - * Set the size of the kernel socket buffer. This call needs the - * appropriate privileges to succeed. - * - * Returns: 0 on success, otherwise -1 on error. - */ -_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) -{ - if (udev_monitor == NULL) - return -EINVAL; - return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); -} - -int udev_monitor_disconnect(struct udev_monitor *udev_monitor) -{ - int err; - - err = close(udev_monitor->sock); - udev_monitor->sock = -1; - return err < 0 ? -errno : 0; -} - -/** - * udev_monitor_ref: - * @udev_monitor: udev monitor - * - * Take a reference of a udev monitor. - * - * Returns: the passed udev monitor - **/ -_public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return NULL; - udev_monitor->refcount++; - return udev_monitor; -} - -/** - * udev_monitor_unref: - * @udev_monitor: udev monitor - * - * Drop a reference of a udev monitor. If the refcount reaches zero, - * the bound socket will be closed, and the resources of the monitor - * will be released. - * - * Returns: #NULL - **/ -_public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return NULL; - udev_monitor->refcount--; - if (udev_monitor->refcount > 0) - return NULL; - if (udev_monitor->sock >= 0) - close(udev_monitor->sock); - udev_list_cleanup(&udev_monitor->filter_subsystem_list); - udev_list_cleanup(&udev_monitor->filter_tag_list); - free(udev_monitor); - return NULL; -} - -/** - * udev_monitor_get_udev: - * @udev_monitor: udev monitor - * - * Retrieve the udev library context the monitor was created with. - * - * Returns: the udev library context - **/ -_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return NULL; - return udev_monitor->udev; -} - -/** - * udev_monitor_get_fd: - * @udev_monitor: udev monitor - * - * Retrieve the socket file descriptor associated with the monitor. - * - * Returns: the socket file descriptor - **/ -_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return -EINVAL; - return udev_monitor->sock; -} - -static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) -{ - struct udev_list_entry *list_entry; - - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) - goto tag; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { - const char *subsys = udev_list_entry_get_name(list_entry); - const char *dsubsys = udev_device_get_subsystem(udev_device); - const char *devtype; - const char *ddevtype; - - if (!streq(dsubsys, subsys)) - continue; - - devtype = udev_list_entry_get_value(list_entry); - if (devtype == NULL) - goto tag; - ddevtype = udev_device_get_devtype(udev_device); - if (ddevtype == NULL) - continue; - if (streq(ddevtype, devtype)) - goto tag; - } - return 0; - -tag: - if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) - return 1; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { - const char *tag = udev_list_entry_get_name(list_entry); - - if (udev_device_has_tag(udev_device, tag)) - return 1; - } - return 0; -} - -/** - * udev_monitor_receive_device: - * @udev_monitor: udev monitor - * - * Receive data from the udev monitor socket, allocate a new udev - * device, fill in the received data, and return the device. - * - * Only socket connections with uid=0 are accepted. - * - * The monitor socket is by default set to NONBLOCK. A variant of poll() on - * the file descriptor returned by udev_monitor_get_fd() should to be used to - * wake up when new devices arrive, or alternatively the file descriptor - * switched into blocking mode. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, in case of an error - **/ -_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) -{ - struct udev_device *udev_device; - struct msghdr smsg; - struct iovec iov; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - struct cmsghdr *cmsg; - union sockaddr_union snl; - struct ucred *cred; - union { - struct udev_monitor_netlink_header nlh; - char raw[8192]; - } buf; - ssize_t buflen; - ssize_t bufpos; - bool is_initialized = false; - -retry: - if (udev_monitor == NULL) - return NULL; - iov.iov_base = &buf; - iov.iov_len = sizeof(buf); - memzero(&smsg, sizeof(struct msghdr)); - smsg.msg_iov = &iov; - smsg.msg_iovlen = 1; - smsg.msg_control = cred_msg; - smsg.msg_controllen = sizeof(cred_msg); - smsg.msg_name = &snl; - smsg.msg_namelen = sizeof(snl); - - buflen = recvmsg(udev_monitor->sock, &smsg, 0); - if (buflen < 0) { - if (errno != EINTR) - log_debug("unable to receive message"); - return NULL; - } - - if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC)) { - log_debug("invalid message length"); - return NULL; - } - - if (snl.nl.nl_groups == 0) { - /* unicast message, check if we trust the sender */ - if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || - snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { - log_debug("unicast netlink message ignored"); - return NULL; - } - } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { - if (snl.nl.nl_pid > 0) { - log_debug("multicast kernel netlink message from PID %"PRIu32" ignored", - snl.nl.nl_pid); - return NULL; - } - } - - cmsg = CMSG_FIRSTHDR(&smsg); - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - log_debug("no sender credentials received, message ignored"); - return NULL; - } - - cred = (struct ucred *)CMSG_DATA(cmsg); - if (cred->uid != 0) { - log_debug("sender uid="UID_FMT", message ignored", cred->uid); - return NULL; - } - - if (memcmp(buf.raw, "libudev", 8) == 0) { - /* udev message needs proper version magic */ - if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)) { - log_debug("unrecognized message signature (%x != %x)", - buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC)); - return NULL; - } - if (buf.nlh.properties_off+32 > (size_t)buflen) { - log_debug("message smaller than expected (%u > %zd)", - buf.nlh.properties_off+32, buflen); - return NULL; - } - - bufpos = buf.nlh.properties_off; - - /* devices received from udev are always initialized */ - is_initialized = true; - } else { - /* kernel message with header */ - bufpos = strlen(buf.raw) + 1; - if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { - log_debug("invalid message length"); - return NULL; - } - - /* check message header */ - if (strstr(buf.raw, "@/") == NULL) { - log_debug("unrecognized message header"); - return NULL; - } - } - - udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos); - if (!udev_device) { - log_debug("could not create device: %m"); - return NULL; - } - - if (is_initialized) - udev_device_set_is_initialized(udev_device); - - /* skip device, if it does not pass the current filter */ - if (!passes_filter(udev_monitor, udev_device)) { - struct pollfd pfd[1]; - int rc; - - udev_device_unref(udev_device); - - /* if something is queued, get next device */ - pfd[0].fd = udev_monitor->sock; - pfd[0].events = POLLIN; - rc = poll(pfd, 1, 0); - if (rc > 0) - goto retry; - return NULL; - } - - return udev_device; -} - -int udev_monitor_send_device(struct udev_monitor *udev_monitor, - struct udev_monitor *destination, struct udev_device *udev_device) -{ - const char *buf, *val; - ssize_t blen, count; - struct udev_monitor_netlink_header nlh = { - .prefix = "libudev", - .magic = htobe32(UDEV_MONITOR_MAGIC), - .header_size = sizeof nlh, - }; - struct iovec iov[2] = { - { .iov_base = &nlh, .iov_len = sizeof nlh }, - }; - struct msghdr smsg = { - .msg_iov = iov, - .msg_iovlen = 2, - }; - struct udev_list_entry *list_entry; - uint64_t tag_bloom_bits; - - blen = udev_device_get_properties_monitor_buf(udev_device, &buf); - if (blen < 32) { - log_debug("device buffer is too small to contain a valid device"); - return -EINVAL; - } - - /* fill in versioned header */ - val = udev_device_get_subsystem(udev_device); - nlh.filter_subsystem_hash = htobe32(util_string_hash32(val)); - - val = udev_device_get_devtype(udev_device); - if (val != NULL) - nlh.filter_devtype_hash = htobe32(util_string_hash32(val)); - - /* add tag bloom filter */ - tag_bloom_bits = 0; - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) - tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); - if (tag_bloom_bits > 0) { - nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32); - nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff); - } - - /* add properties list */ - nlh.properties_off = iov[0].iov_len; - nlh.properties_len = blen; - iov[1].iov_base = (char *)buf; - iov[1].iov_len = blen; - - /* - * Use custom address for target, or the default one. - * - * If we send to a multicast group, we will get - * ECONNREFUSED, which is expected. - */ - if (destination) - smsg.msg_name = &destination->snl; - else - smsg.msg_name = &udev_monitor->snl_destination; - smsg.msg_namelen = sizeof(struct sockaddr_nl); - count = sendmsg(udev_monitor->sock, &smsg, 0); - if (count < 0) { - if (!destination && errno == ECONNREFUSED) { - log_debug("passed device to netlink monitor %p", udev_monitor); - return 0; - } else - return -errno; - } - - log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor); - return count; -} - -/** - * udev_monitor_filter_add_match_subsystem_devtype: - * @udev_monitor: the monitor - * @subsystem: the subsystem value to match the incoming devices against - * @devtype: the devtype value to match the incoming devices against - * - * This filter is efficiently executed inside the kernel, and libudev subscribers - * will usually not be woken up for devices which do not match. - * - * The filter must be installed before the monitor is switched to listening mode. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) -{ - if (udev_monitor == NULL) - return -EINVAL; - if (subsystem == NULL) - return -EINVAL; - if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_monitor_filter_add_match_tag: - * @udev_monitor: the monitor - * @tag: the name of a tag - * - * This filter is efficiently executed inside the kernel, and libudev subscribers - * will usually not be woken up for devices which do not match. - * - * The filter must be installed before the monitor is switched to listening mode. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) -{ - if (udev_monitor == NULL) - return -EINVAL; - if (tag == NULL) - return -EINVAL; - if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_monitor_filter_remove: - * @udev_monitor: monitor - * - * Remove all filters from monitor. - * - * Returns: 0 on success, otherwise a negative error value. - */ -_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) -{ - static struct sock_fprog filter = { 0, NULL }; - - udev_list_cleanup(&udev_monitor->filter_subsystem_list); - return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); -} diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h deleted file mode 100644 index 52c5075110..0000000000 --- a/src/libudev/libudev-private.h +++ /dev/null @@ -1,150 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifndef _LIBUDEV_PRIVATE_H_ -#define _LIBUDEV_PRIVATE_H_ - -#include -#include -#include - -#include "libudev.h" - -#include "macro.h" -#include "mkdir.h" -#include "strxcpyx.h" -#include "util.h" - -#define READ_END 0 -#define WRITE_END 1 - -/* libudev.c */ -int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]); - -/* libudev-device.c */ -struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen); -struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action); -struct udev_device *udev_device_shallow_clone(struct udev_device *old_device); -struct udev_device *udev_device_clone_with_db(struct udev_device *old_device); -int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src); -mode_t udev_device_get_devnode_mode(struct udev_device *udev_device); -uid_t udev_device_get_devnode_uid(struct udev_device *udev_device); -gid_t udev_device_get_devnode_gid(struct udev_device *udev_device); -int udev_device_rename(struct udev_device *udev_device, const char *new_name); -int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink); -void udev_device_cleanup_devlinks_list(struct udev_device *udev_device); -int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value); -char **udev_device_get_properties_envp(struct udev_device *udev_device); -ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf); -const char *udev_device_get_devpath_old(struct udev_device *udev_device); -const char *udev_device_get_id_filename(struct udev_device *udev_device); -void udev_device_set_is_initialized(struct udev_device *udev_device); -int udev_device_add_tag(struct udev_device *udev_device, const char *tag); -void udev_device_remove_tag(struct udev_device *udev_device, const char *tag); -void udev_device_cleanup_tags_list(struct udev_device *udev_device); -usec_t udev_device_get_usec_initialized(struct udev_device *udev_device); -void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *old_device); -int udev_device_get_devlink_priority(struct udev_device *udev_device); -int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio); -int udev_device_get_watch_handle(struct udev_device *udev_device); -int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); -int udev_device_get_ifindex(struct udev_device *udev_device); -void udev_device_set_info_loaded(struct udev_device *device); -bool udev_device_get_db_persist(struct udev_device *udev_device); -void udev_device_set_db_persist(struct udev_device *udev_device); -void udev_device_read_db(struct udev_device *udev_device); - -/* libudev-device-private.c */ -int udev_device_update_db(struct udev_device *udev_device); -int udev_device_delete_db(struct udev_device *udev_device); -int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add); - -/* libudev-monitor.c - netlink/unix socket communication */ -int udev_monitor_disconnect(struct udev_monitor *udev_monitor); -int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender); -int udev_monitor_send_device(struct udev_monitor *udev_monitor, - struct udev_monitor *destination, struct udev_device *udev_device); -struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd); - -/* libudev-list.c */ -struct udev_list_node { - struct udev_list_node *next, *prev; -}; -struct udev_list { - struct udev *udev; - struct udev_list_node node; - struct udev_list_entry **entries; - unsigned int entries_cur; - unsigned int entries_max; - bool unique; -}; -void udev_list_node_init(struct udev_list_node *list); -int udev_list_node_is_empty(struct udev_list_node *list); -void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list); -void udev_list_node_remove(struct udev_list_node *entry); -#define udev_list_node_foreach(node, list) \ - for (node = (list)->next; \ - node != list; \ - node = (node)->next) -#define udev_list_node_foreach_safe(node, tmp, list) \ - for (node = (list)->next, tmp = (node)->next; \ - node != list; \ - node = tmp, tmp = (tmp)->next) -void udev_list_init(struct udev *udev, struct udev_list *list, bool unique); -void udev_list_cleanup(struct udev_list *list); -struct udev_list_entry *udev_list_get_entry(struct udev_list *list); -struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value); -void udev_list_entry_delete(struct udev_list_entry *entry); -int udev_list_entry_get_num(struct udev_list_entry *list_entry); -void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num); -#define udev_list_entry_foreach_safe(entry, tmp, first) \ - for (entry = first, tmp = udev_list_entry_get_next(entry); \ - entry != NULL; \ - entry = tmp, tmp = udev_list_entry_get_next(tmp)) - -/* libudev-queue.c */ -unsigned long long int udev_get_kernel_seqnum(struct udev *udev); -int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); -ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); -ssize_t udev_queue_skip_devpath(FILE *queue_file); - -/* libudev-queue-private.c */ -struct udev_queue_export *udev_queue_export_new(struct udev *udev); -struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export); -void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); -int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); -int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); - -/* libudev-util.c */ -#define UTIL_PATH_SIZE 1024 -#define UTIL_NAME_SIZE 512 -#define UTIL_LINE_SIZE 16384 -#define UDEV_ALLOWED_CHARS_INPUT "/ $%?," -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); -int util_replace_whitespace(const char *str, char *to, size_t len); -int util_replace_chars(char *str, const char *white); -unsigned int util_string_hash32(const char *key); -uint64_t util_string_bloom64(const char *str); - -/* libudev-util-private.c */ -int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value); - -#endif diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c deleted file mode 100644 index e3dffa6925..0000000000 --- a/src/libudev/libudev-queue.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2009 Alan Jenkins - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-queue - * @short_description: access to currently active events - * - * This exports the current state of the udev processing queue. - */ - -/** - * udev_queue: - * - * Opaque object representing the current event queue in the udev daemon. - */ -struct udev_queue { - struct udev *udev; - int refcount; - int fd; -}; - -/** - * udev_queue_new: - * @udev: udev library context - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev queue context. - * - * Returns: the udev queue context, or #NULL on error. - **/ -_public_ struct udev_queue *udev_queue_new(struct udev *udev) -{ - struct udev_queue *udev_queue; - - if (udev == NULL) - return NULL; - - udev_queue = new0(struct udev_queue, 1); - if (udev_queue == NULL) - return NULL; - - udev_queue->refcount = 1; - udev_queue->udev = udev; - udev_queue->fd = -1; - return udev_queue; -} - -/** - * udev_queue_ref: - * @udev_queue: udev queue context - * - * Take a reference of a udev queue context. - * - * Returns: the same udev queue context. - **/ -_public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return NULL; - - udev_queue->refcount++; - return udev_queue; -} - -/** - * udev_queue_unref: - * @udev_queue: udev queue context - * - * Drop a reference of a udev queue context. If the refcount reaches zero, - * the resources of the queue context will be released. - * - * Returns: #NULL - **/ -_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return NULL; - - udev_queue->refcount--; - if (udev_queue->refcount > 0) - return NULL; - - safe_close(udev_queue->fd); - - free(udev_queue); - return NULL; -} - -/** - * udev_queue_get_udev: - * @udev_queue: udev queue context - * - * Retrieve the udev library context the queue context was created with. - * - * Returns: the udev library context. - **/ -_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return NULL; - return udev_queue->udev; -} - -/** - * udev_queue_get_kernel_seqnum: - * @udev_queue: udev queue context - * - * This function is deprecated. - * - * Returns: 0. - **/ -_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) -{ - return 0; -} - -/** - * udev_queue_get_udev_seqnum: - * @udev_queue: udev queue context - * - * This function is deprecated. - * - * Returns: 0. - **/ -_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) -{ - return 0; -} - -/** - * udev_queue_get_udev_is_active: - * @udev_queue: udev queue context - * - * Check if udev is active on the system. - * - * Returns: a flag indicating if udev is active. - **/ -_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) -{ - return access("/run/udev/control", F_OK) >= 0; -} - -/** - * udev_queue_get_queue_is_empty: - * @udev_queue: udev queue context - * - * Check if udev is currently processing any events. - * - * Returns: a flag indicating if udev is currently handling events. - **/ -_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) -{ - return access("/run/udev/queue", F_OK) < 0; -} - -/** - * udev_queue_get_seqnum_sequence_is_finished: - * @udev_queue: udev queue context - * @start: first event sequence number - * @end: last event sequence number - * - * This function is deprecated, it just returns the result of - * udev_queue_get_queue_is_empty(). - * - * Returns: a flag indicating if udev is currently handling events. - **/ -_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, - unsigned long long int start, unsigned long long int end) -{ - return udev_queue_get_queue_is_empty(udev_queue); -} - -/** - * udev_queue_get_seqnum_is_finished: - * @udev_queue: udev queue context - * @seqnum: sequence number - * - * This function is deprecated, it just returns the result of - * udev_queue_get_queue_is_empty(). - * - * Returns: a flag indicating if udev is currently handling events. - **/ -_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) -{ - return udev_queue_get_queue_is_empty(udev_queue); -} - -/** - * udev_queue_get_queued_list_entry: - * @udev_queue: udev queue context - * - * This function is deprecated. - * - * Returns: NULL. - **/ -_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) -{ - return NULL; -} - -/** - * udev_queue_get_fd: - * @udev_queue: udev queue context - * - * Returns: a file descriptor to watch for a queue to become empty. - */ -_public_ int udev_queue_get_fd(struct udev_queue *udev_queue) { - int fd; - int r; - - if (udev_queue->fd >= 0) - return udev_queue->fd; - - fd = inotify_init1(IN_CLOEXEC); - if (fd < 0) - return -errno; - - r = inotify_add_watch(fd, "/run/udev" , IN_DELETE); - if (r < 0) { - r = -errno; - close(fd); - return r; - } - - udev_queue->fd = fd; - return fd; -} - -/** - * udev_queue_flush: - * @udev_queue: udev queue context - * - * Returns: the result of clearing the watch for queue changes. - */ -_public_ int udev_queue_flush(struct udev_queue *udev_queue) { - if (udev_queue->fd < 0) - return -EINVAL; - - return flush_fd(udev_queue->fd); -} diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c deleted file mode 100644 index 574cfeac85..0000000000 --- a/src/libudev/libudev-util.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "MurmurHash2.h" -#include "device-nodes.h" -#include "libudev-private.h" -#include "syslog-util.h" -#include "utf8.h" - -/** - * SECTION:libudev-util - * @short_description: utils - * - * Utilities useful when dealing with devices and device node names. - */ - -/* handle "[/]" format */ -int util_resolve_subsys_kernel(struct udev *udev, const char *string, - char *result, size_t maxsize, int read_value) -{ - char temp[UTIL_PATH_SIZE]; - char *subsys; - char *sysname; - struct udev_device *dev; - char *attr; - - if (string[0] != '[') - return -1; - - strscpy(temp, sizeof(temp), string); - - subsys = &temp[1]; - - sysname = strchr(subsys, '/'); - if (sysname == NULL) - return -1; - sysname[0] = '\0'; - sysname = &sysname[1]; - - attr = strchr(sysname, ']'); - if (attr == NULL) - return -1; - attr[0] = '\0'; - attr = &attr[1]; - if (attr[0] == '/') - attr = &attr[1]; - if (attr[0] == '\0') - attr = NULL; - - if (read_value && attr == NULL) - return -1; - - dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); - if (dev == NULL) - return -1; - - if (read_value) { - const char *val; - - val = udev_device_get_sysattr_value(dev, attr); - if (val != NULL) - strscpy(result, maxsize, val); - else - result[0] = '\0'; - log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); - } else { - size_t l; - char *s; - - s = result; - l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); - if (attr != NULL) - strpcpyl(&s, l, "/", attr, NULL); - log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); - } - udev_device_unref(dev); - return 0; -} - -int util_log_priority(const char *priority) -{ - char *endptr; - int prio; - - prio = strtoul(priority, &endptr, 10); - if (endptr[0] == '\0' || isspace(endptr[0])) { - if (prio >= 0 && prio <= 7) - return prio; - else - return -ERANGE; - } - - return log_level_from_string(priority); -} - -size_t util_path_encode(const char *src, char *dest, size_t size) -{ - size_t i, j; - - for (i = 0, j = 0; src[i] != '\0'; i++) { - if (src[i] == '/') { - if (j+4 >= size) { - j = 0; - break; - } - memcpy(&dest[j], "\\x2f", 4); - j += 4; - } else if (src[i] == '\\') { - if (j+4 >= size) { - j = 0; - break; - } - memcpy(&dest[j], "\\x5c", 4); - j += 4; - } else { - if (j+1 >= size) { - j = 0; - break; - } - dest[j] = src[i]; - j++; - } - } - dest[j] = '\0'; - return j; -} - -void util_remove_trailing_chars(char *path, char c) -{ - size_t len; - - if (path == NULL) - return; - len = strlen(path); - while (len > 0 && path[len-1] == c) - path[--len] = '\0'; -} - -int util_replace_whitespace(const char *str, char *to, size_t len) -{ - size_t i, j; - - /* strip trailing whitespace */ - len = strnlen(str, len); - while (len && isspace(str[len-1])) - len--; - - /* strip leading whitespace */ - i = 0; - while ((i < len) && isspace(str[i])) - i++; - - j = 0; - while (i < len) { - /* substitute multiple whitespace with a single '_' */ - if (isspace(str[i])) { - while (isspace(str[i])) - i++; - to[j++] = '_'; - } - to[j++] = str[i++]; - } - to[j] = '\0'; - return 0; -} - -/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ -int util_replace_chars(char *str, const char *white) -{ - size_t i = 0; - int replaced = 0; - - while (str[i] != '\0') { - int len; - - if (whitelisted_char_for_devnode(str[i], white)) { - i++; - continue; - } - - /* accept hex encoding */ - if (str[i] == '\\' && str[i+1] == 'x') { - i += 2; - continue; - } - - /* accept valid utf8 */ - len = utf8_encoded_valid_unichar(&str[i]); - if (len > 1) { - i += len; - continue; - } - - /* if space is allowed, replace whitespace with ordinary space */ - if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) { - str[i] = ' '; - i++; - replaced++; - continue; - } - - /* everything else is replaced with '_' */ - str[i] = '_'; - i++; - replaced++; - } - return replaced; -} - -/** - * udev_util_encode_string: - * @str: input string to be encoded - * @str_enc: output string to store the encoded input string - * @len: maximum size of the output string, which may be - * four times as long as the input string - * - * Encode all potentially unsafe characters of a string to the - * corresponding 2 char hex value prefixed by '\x'. - * - * Returns: 0 if the entire string was copied, non-zero otherwise. - **/ -_public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) -{ - return encode_devnode_name(str, str_enc, len); -} - -unsigned int util_string_hash32(const char *str) -{ - return MurmurHash2(str, strlen(str), 0); -} - -/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ -uint64_t util_string_bloom64(const char *str) -{ - uint64_t bits = 0; - unsigned int hash = util_string_hash32(str); - - bits |= 1LLU << (hash & 63); - bits |= 1LLU << ((hash >> 6) & 63); - bits |= 1LLU << ((hash >> 12) & 63); - bits |= 1LLU << ((hash >> 18) & 63); - return bits; -} diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c deleted file mode 100644 index 63fb05547d..0000000000 --- a/src/libudev/libudev.c +++ /dev/null @@ -1,253 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2014 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "libudev-private.h" -#include "missing.h" -#include "string-util.h" - -/** - * SECTION:libudev - * @short_description: libudev context - * - * The context contains the default values read from the udev config file, - * and is passed to all library operations. - */ - -/** - * udev: - * - * Opaque object representing the library context. - */ -struct udev { - int refcount; - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args); - void *userdata; -}; - -/** - * udev_get_userdata: - * @udev: udev library context - * - * Retrieve stored data pointer from library context. This might be useful - * to access from callbacks. - * - * Returns: stored userdata - **/ -_public_ void *udev_get_userdata(struct udev *udev) { - if (udev == NULL) - return NULL; - return udev->userdata; -} - -/** - * udev_set_userdata: - * @udev: udev library context - * @userdata: data pointer - * - * Store custom @userdata in the library context. - **/ -_public_ void udev_set_userdata(struct udev *udev, void *userdata) { - if (udev == NULL) - return; - udev->userdata = userdata; -} - -/** - * udev_new: - * - * Create udev library context. This reads the udev configuration - * file, and fills in the default values. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev library context. - * - * Returns: a new udev library context - **/ -_public_ struct udev *udev_new(void) { - struct udev *udev; - _cleanup_fclose_ FILE *f = NULL; - - udev = new0(struct udev, 1); - if (udev == NULL) - return NULL; - udev->refcount = 1; - - f = fopen("/etc/udev/udev.conf", "re"); - if (f != NULL) { - char line[UTIL_LINE_SIZE]; - unsigned line_nr = 0; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *key; - char *val; - - line_nr++; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - continue; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) { - log_debug("/etc/udev/udev.conf:%u: missing assignment, skipping line.", line_nr); - continue; - } - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - continue; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - continue; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - continue; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr); - continue; - } - val[len-1] = '\0'; - val++; - } - - if (streq(key, "udev_log")) { - int prio; - - prio = util_log_priority(val); - if (prio < 0) - log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val); - else - log_set_max_level(prio); - continue; - } - } - } - - return udev; -} - -/** - * udev_ref: - * @udev: udev library context - * - * Take a reference of the udev library context. - * - * Returns: the passed udev library context - **/ -_public_ struct udev *udev_ref(struct udev *udev) { - if (udev == NULL) - return NULL; - udev->refcount++; - return udev; -} - -/** - * udev_unref: - * @udev: udev library context - * - * Drop a reference of the udev library context. If the refcount - * reaches zero, the resources of the context will be released. - * - * Returns: the passed udev library context if it has still an active reference, or #NULL otherwise. - **/ -_public_ struct udev *udev_unref(struct udev *udev) { - if (udev == NULL) - return NULL; - udev->refcount--; - if (udev->refcount > 0) - return udev; - free(udev); - return NULL; -} - -/** - * udev_set_log_fn: - * @udev: udev library context - * @log_fn: function to be called for log messages - * - * This function is deprecated. - * - **/ -_public_ void udev_set_log_fn(struct udev *udev, - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args)) { - return; -} - -/** - * udev_get_log_priority: - * @udev: udev library context - * - * This function is deprecated. - * - **/ -_public_ int udev_get_log_priority(struct udev *udev) { - return log_get_max_level(); -} - -/** - * udev_set_log_priority: - * @udev: udev library context - * @priority: the new log priority - * - * This function is deprecated. - * - **/ -_public_ void udev_set_log_priority(struct udev *udev, int priority) { - log_set_max_level(priority); -} diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h deleted file mode 100644 index 3f6d0ed16c..0000000000 --- a/src/libudev/libudev.h +++ /dev/null @@ -1,207 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifndef _LIBUDEV_H_ -#define _LIBUDEV_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * udev - library context - * - * reads the udev config and system environment - * allows custom logging - */ -struct udev; -struct udev *udev_ref(struct udev *udev); -struct udev *udev_unref(struct udev *udev); -struct udev *udev_new(void); -void udev_set_log_fn(struct udev *udev, - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args)) __attribute__ ((deprecated)); -int udev_get_log_priority(struct udev *udev) __attribute__ ((deprecated)); -void udev_set_log_priority(struct udev *udev, int priority) __attribute__ ((deprecated)); -void *udev_get_userdata(struct udev *udev); -void udev_set_userdata(struct udev *udev, void *userdata); - -/* - * udev_list - * - * access to libudev generated lists - */ -struct udev_list_entry; -struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); -struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); -const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); -const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); -/** - * udev_list_entry_foreach: - * @list_entry: entry to store the current position - * @first_entry: first entry to start with - * - * Helper to iterate over all entries of a list. - */ -#define udev_list_entry_foreach(list_entry, first_entry) \ - for (list_entry = first_entry; \ - list_entry != NULL; \ - list_entry = udev_list_entry_get_next(list_entry)) - -/* - * udev_device - * - * access to sysfs/kernel devices - */ -struct udev_device; -struct udev_device *udev_device_ref(struct udev_device *udev_device); -struct udev_device *udev_device_unref(struct udev_device *udev_device); -struct udev *udev_device_get_udev(struct udev_device *udev_device); -struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); -struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); -struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); -struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id); -struct udev_device *udev_device_new_from_environment(struct udev *udev); -/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ -struct udev_device *udev_device_get_parent(struct udev_device *udev_device); -struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, - const char *subsystem, const char *devtype); -/* retrieve device properties */ -const char *udev_device_get_devpath(struct udev_device *udev_device); -const char *udev_device_get_subsystem(struct udev_device *udev_device); -const char *udev_device_get_devtype(struct udev_device *udev_device); -const char *udev_device_get_syspath(struct udev_device *udev_device); -const char *udev_device_get_sysname(struct udev_device *udev_device); -const char *udev_device_get_sysnum(struct udev_device *udev_device); -const char *udev_device_get_devnode(struct udev_device *udev_device); -int udev_device_get_is_initialized(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); -const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); -const char *udev_device_get_driver(struct udev_device *udev_device); -dev_t udev_device_get_devnum(struct udev_device *udev_device); -const char *udev_device_get_action(struct udev_device *udev_device); -unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); -unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device); -const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); -int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value); -int udev_device_has_tag(struct udev_device *udev_device, const char *tag); - -/* - * udev_monitor - * - * access to kernel uevents and udev events - */ -struct udev_monitor; -struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); -struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor); -struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); -/* kernel and udev generated events over netlink */ -struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); -/* bind socket */ -int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); -int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); -int udev_monitor_get_fd(struct udev_monitor *udev_monitor); -struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); -/* in-kernel socket filters to select messages that get delivered to a listener */ -int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, - const char *subsystem, const char *devtype); -int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); -int udev_monitor_filter_update(struct udev_monitor *udev_monitor); -int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); - -/* - * udev_enumerate - * - * search sysfs for specific devices and provide a sorted list - */ -struct udev_enumerate; -struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); -struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate); -struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); -struct udev_enumerate *udev_enumerate_new(struct udev *udev); -/* device properties filter */ -int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); -int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); -int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); -int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); -int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); -int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); -int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); -int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); -int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); -int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); -/* run enumeration with active filters */ -int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); -int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); -/* return device list */ -struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); - -/* - * udev_queue - * - * access to the currently running udev events - */ -struct udev_queue; -struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); -struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue); -struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); -struct udev_queue *udev_queue_new(struct udev *udev); -unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated)); -unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated)); -int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); -int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); -int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) __attribute__ ((deprecated)); -int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, - unsigned long long int start, unsigned long long int end) __attribute__ ((deprecated)); -int udev_queue_get_fd(struct udev_queue *udev_queue); -int udev_queue_flush(struct udev_queue *udev_queue); -struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) __attribute__ ((deprecated)); - -/* - * udev_hwdb - * - * access to the static hardware properties database - */ -struct udev_hwdb; -struct udev_hwdb *udev_hwdb_new(struct udev *udev); -struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb); -struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb); -struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags); - -/* - * udev_util - * - * udev specific utilities - */ -int udev_util_encode_string(const char *str, char *str_enc, size_t len); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/src/libudev/libudev.xml b/src/libudev/libudev.xml new file mode 100644 index 0000000000..53b68dcc89 --- /dev/null +++ b/src/libudev/libudev.xml @@ -0,0 +1,125 @@ + + +%entities; +]> + + + + + + + libudev + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + libudev + 3 + + + + libudev + API for enumerating and introspecting local devices + + + + + #include <libudev.h> + + + + pkg-config --cflags --libs libudev + + + + + Description + + libudev.h provides APIs to introspect + and enumerate devices on the local system. + + All functions require a libudev context to operate. This + context can be create via + udev_new3. + It is used to track library state and link objects together. No + global state is used by libudev, everything is always linked to + a udev context. Furthermore, multiple different udev contexts can + be used in parallel by multiple threads. However, a single context + must not be accessed by multiple threads in parallel. The caller + is responsible for providing suitable locking if they intend to use + it from multiple threads. + + To introspect a local device on a system, a udev device + object can be created via + udev_device_new_from_syspath3 + and friends. The device object allows one to query current state, + read and write attributes and lookup properties of the device in + question. + + To enumerate local devices on the system, an enumeration + object can be created via + udev_enumerate_new3. + + To monitor the local system for hotplugged or unplugged + devices, a monitor can be created via + udev_monitor_new_from_netlink3. + + Whenever libudev returns a list of objects, the + udev_list_entry3 + API should be used to iterate, access and modify those lists. + + Furthermore, libudev also exports legacy APIs that should + not be used by new software (and as such are not documented as + part of this manual). This includes the hardware database known + as udev_hwdb (please use the new + sd-hwdb3 + API instead) and the udev_queue object to + query the udev daemon (which should not be used by new software + at all). + + + + See Also + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + sd-device3, + sd-hwdb3, + pkg-config1 + + + + diff --git a/src/libudev/src/Makefile b/src/libudev/src/Makefile new file mode 100644 index 0000000000..46f122d52d --- /dev/null +++ b/src/libudev/src/Makefile @@ -0,0 +1,45 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +libudev_la_SOURCES =\ + src/libudev/libudev.sym \ + src/libudev/libudev-private.h \ + src/libudev/libudev-device-internal.h \ + src/libudev/libudev.c \ + src/libudev/libudev-list.c \ + src/libudev/libudev-util.c \ + src/libudev/libudev-device.c \ + src/libudev/libudev-device-private.c \ + src/libudev/libudev-enumerate.c \ + src/libudev/libudev-monitor.c \ + src/libudev/libudev-queue.c \ + src/libudev/libudev-hwdb.c +noinst_LTLIBRARIES += \ + libudev-internal.la + +libudev_internal_la_SOURCES =\ + $(libudev_la_SOURCES) + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/libudev/src/libudev-device-internal.h b/src/libudev/src/libudev-device-internal.h new file mode 100644 index 0000000000..07a73e6b9c --- /dev/null +++ b/src/libudev/src/libudev-device-internal.h @@ -0,0 +1,59 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include + +#include "sd-device/sd-device.h" + +#include "libudev-private.h" + +/** + * udev_device: + * + * Opaque object representing one kernel sys device. + */ +struct udev_device { + struct udev *udev; + + /* real device object */ + sd_device *device; + + /* legacy */ + int refcount; + + struct udev_device *parent; + bool parent_set; + + struct udev_list properties; + uint64_t properties_generation; + struct udev_list tags; + uint64_t tags_generation; + struct udev_list devlinks; + uint64_t devlinks_generation; + bool properties_read:1; + bool tags_read:1; + bool devlinks_read:1; + struct udev_list sysattrs; + bool sysattrs_read; +}; + +struct udev_device *udev_device_new(struct udev *udev); diff --git a/src/libudev/src/libudev-device-private.c b/src/libudev/src/libudev-device-private.c new file mode 100644 index 0000000000..97b60da3f1 --- /dev/null +++ b/src/libudev/src/libudev-device-private.c @@ -0,0 +1,412 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include + +#include "sd-device/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; + int r; + + assert(udev_device); + + if (udev_device_old) + device_old = udev_device_old->device; + + r = device_tag_index(udev_device->device, device_old, add); + if (r < 0) + return r; + + return 0; +} + +int udev_device_update_db(struct udev_device *udev_device) { + int r; + + assert(udev_device); + + r = device_update_db(udev_device->device); + if (r < 0) + return r; + + return 0; +} + +int udev_device_delete_db(struct udev_device *udev_device) { + int r; + + assert(udev_device); + + r = device_delete_db(udev_device->device); + if (r < 0) + return r; + + return 0; +} + +int udev_device_get_ifindex(struct udev_device *udev_device) { + int r, ifindex; + + assert(udev_device); + + r = sd_device_get_ifindex(udev_device->device, &ifindex); + if (r < 0) + return r; + + return ifindex; +} + +const char *udev_device_get_devpath_old(struct udev_device *udev_device) { + const char *devpath_old = NULL; + int r; + + assert(udev_device); + + r = sd_device_get_property_value(udev_device->device, "DEVPATH_OLD", &devpath_old); + if (r < 0 && r != -ENOENT) { + errno = -r; + return NULL; + } + + return devpath_old; +} + +mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) { + mode_t mode; + int r; + + assert(udev_device); + + r = device_get_devnode_mode(udev_device->device, &mode); + if (r < 0) { + errno = -r; + return 0; + } + + return mode; +} + +uid_t udev_device_get_devnode_uid(struct udev_device *udev_device) { + uid_t uid; + int r; + + assert(udev_device); + + r = device_get_devnode_uid(udev_device->device, &uid); + if (r < 0) { + errno = -r; + return 0; + } + + return uid; +} + +gid_t udev_device_get_devnode_gid(struct udev_device *udev_device) { + gid_t gid; + int r; + + assert(udev_device); + + r = device_get_devnode_gid(udev_device->device, &gid); + if (r < 0) { + errno = -r; + return 0; + } + + return gid; +} + +void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *udev_device_old) { + assert(udev_device); + + 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) { + char **envp; + int r; + + assert(udev_device); + + r = device_get_properties_strv(udev_device->device, &envp); + if (r < 0) { + errno = -r; + return NULL; + } + + return envp; +} + +ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) { + const char *nulstr; + size_t len; + int r; + + assert(udev_device); + assert(buf); + + r = device_get_properties_nulstr(udev_device->device, (const uint8_t **)&nulstr, &len); + if (r < 0) + return r; + + *buf = nulstr; + + return len; +} + +int udev_device_get_devlink_priority(struct udev_device *udev_device) { + int priority, r; + + assert(udev_device); + + r = device_get_devlink_priority(udev_device->device, &priority); + if (r < 0) + return r; + + return priority; +} + +int udev_device_get_watch_handle(struct udev_device *udev_device) { + int handle, r; + + assert(udev_device); + + r = device_get_watch_handle(udev_device->device, &handle); + if (r < 0) + return r; + + return handle; +} + +void udev_device_set_is_initialized(struct udev_device *udev_device) { + assert(udev_device); + + device_set_is_initialized(udev_device->device); +} + +int udev_device_rename(struct udev_device *udev_device, const char *name) { + int r; + + assert(udev_device); + + r = device_rename(udev_device->device, name); + if (r < 0) + return r; + + return 0; +} + +struct udev_device *udev_device_shallow_clone(struct udev_device *old_device) { + struct udev_device *device; + int r; + + assert(old_device); + + device = udev_device_new(old_device->udev); + if (!device) + return NULL; + + r = device_shallow_clone(old_device->device, &device->device); + if (r < 0) { + udev_device_unref(device); + errno = -r; + return NULL; + } + + return device; +} + +struct udev_device *udev_device_clone_with_db(struct udev_device *udev_device_old) { + struct udev_device *udev_device; + int r; + + assert(udev_device_old); + + udev_device = udev_device_new(udev_device_old->udev); + if (!udev_device) + return NULL; + + r = device_clone_with_db(udev_device_old->device, &udev_device->device); + if (r < 0) { + udev_device_unref(udev_device); + errno = -r; + return NULL; + } + + return udev_device; +} + +struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) { + struct udev_device *device; + int r; + + device = udev_device_new(udev); + if (!device) + return NULL; + + r = device_new_from_nulstr(&device->device, (uint8_t*)nulstr, buflen); + if (r < 0) { + udev_device_unref(device); + errno = -r; + return NULL; + } + + return device; +} + +struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action) { + struct udev_device *device; + int r; + + device = udev_device_new(udev); + if (!device) + return NULL; + + r = device_new_from_synthetic_event(&device->device, syspath, action); + if (r < 0) { + udev_device_unref(device); + errno = -r; + return NULL; + } + + return device; +} + +int udev_device_copy_properties(struct udev_device *udev_device_dst, struct udev_device *udev_device_src) { + int r; + + assert(udev_device_dst); + assert(udev_device_src); + + r = device_copy_properties(udev_device_dst->device, udev_device_src->device); + if (r < 0) + return r; + + return 0; +} + +const char *udev_device_get_id_filename(struct udev_device *udev_device) { + const char *filename; + int r; + + assert(udev_device); + + r = device_get_id_filename(udev_device->device, &filename); + if (r < 0) { + errno = -r; + return NULL; + } + + return filename; +} + +int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) { + + assert(udev_device); + + device_set_watch_handle(udev_device->device, handle); + + return 0; +} + +void udev_device_set_db_persist(struct udev_device *udev_device) { + assert(udev_device); + + device_set_db_persist(udev_device->device); +} + +int udev_device_set_devlink_priority(struct udev_device *udev_device, int priority) { + assert(udev_device); + + device_set_devlink_priority(udev_device->device, priority); + + return 0; +} + +int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink) { + int r; + + assert(udev_device); + + r = device_add_devlink(udev_device->device, devlink); + if (r < 0) + return r; + + return 0; +} + +int udev_device_add_property(struct udev_device *udev_device, const char *property, const char *value) { + int r; + + assert(udev_device); + + r = device_add_property(udev_device->device, property, value); + if (r < 0) + return r; + + return 0; +} + +int udev_device_add_tag(struct udev_device *udev_device, const char *tag) { + int r; + + assert(udev_device); + + r = device_add_tag(udev_device->device, tag); + if (r < 0) + return r; + + return 0; +} + +void udev_device_remove_tag(struct udev_device *udev_device, const char *tag) { + assert(udev_device); + + device_remove_tag(udev_device->device, tag); +} + +void udev_device_cleanup_tags_list(struct udev_device *udev_device) { + assert(udev_device); + + device_cleanup_tags(udev_device->device); +} + +void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) { + assert(udev_device); + + device_cleanup_devlinks(udev_device->device); +} + +void udev_device_set_info_loaded(struct udev_device *udev_device) { + assert(udev_device); + + device_seal(udev_device->device); +} + +void udev_device_read_db(struct udev_device *udev_device) { + assert(udev_device); + + device_read_db_force(udev_device->device); +} diff --git a/src/libudev/src/libudev-device.c b/src/libudev/src/libudev-device.c new file mode 100644 index 0000000000..a3a0d473a6 --- /dev/null +++ b/src/libudev/src/libudev-device.c @@ -0,0 +1,960 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/parse-util.h" +#include "sd-device/device-private.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" + +#include "libudev-device-internal.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-device + * @short_description: kernel sys devices + * + * Representation of kernel sys devices. Devices are uniquely identified + * by their syspath, every device has exactly one path in the kernel sys + * filesystem. Devices usually belong to a kernel subsystem, and have + * a unique name inside that subsystem. + */ + +/** + * udev_device_get_seqnum: + * @udev_device: udev device + * + * This is only valid if the device was received through a monitor. Devices read from + * sys do not have a sequence number. + * + * Returns: the kernel event sequence number, or 0 if there is no sequence number available. + **/ +_public_ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) +{ + const char *seqnum; + unsigned long long ret; + int r; + + assert_return_errno(udev_device, 0, EINVAL); + + r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum); + if (r == -ENOENT) + return 0; + else if (r < 0) { + errno = -r; + return 0; + } + + r = safe_atollu(seqnum, &ret); + if (r < 0) { + errno = -r; + return 0; + } + + return ret; +} + +/** + * udev_device_get_devnum: + * @udev_device: udev device + * + * Get the device major/minor number. + * + * Returns: the dev_t number. + **/ +_public_ dev_t udev_device_get_devnum(struct udev_device *udev_device) +{ + dev_t devnum; + int r; + + assert_return_errno(udev_device, makedev(0, 0), EINVAL); + + r = sd_device_get_devnum(udev_device->device, &devnum); + if (r < 0) { + errno = -r; + return makedev(0, 0); + } + + return devnum; +} + +/** + * udev_device_get_driver: + * @udev_device: udev device + * + * Get the kernel driver name. + * + * Returns: the driver name string, or #NULL if there is no driver attached. + **/ +_public_ const char *udev_device_get_driver(struct udev_device *udev_device) +{ + const char *driver; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_driver(udev_device->device, &driver); + if (r < 0) { + errno = -r; + return NULL; + } + + return driver; +} + +/** + * udev_device_get_devtype: + * @udev_device: udev device + * + * Retrieve the devtype string of the udev device. + * + * Returns: the devtype name of the udev device, or #NULL if it can not be determined + **/ +_public_ const char *udev_device_get_devtype(struct udev_device *udev_device) +{ + const char *devtype; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_devtype(udev_device->device, &devtype); + if (r < 0) { + errno = -r; + return NULL; + } + + return devtype; +} + +/** + * udev_device_get_subsystem: + * @udev_device: udev device + * + * Retrieve the subsystem string of the udev device. The string does not + * contain any "/". + * + * Returns: the subsystem name of the udev device, or #NULL if it can not be determined + **/ +_public_ const char *udev_device_get_subsystem(struct udev_device *udev_device) +{ + const char *subsystem; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_subsystem(udev_device->device, &subsystem); + if (r < 0) { + errno = -r; + return NULL; + } else if (!subsystem) + errno = ENODATA; + + return subsystem; +} + +/** + * udev_device_get_property_value: + * @udev_device: udev device + * @key: property name + * + * Get the value of a given property. + * + * Returns: the property string, or #NULL if there is no such property. + **/ +_public_ const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) +{ + const char *value = NULL; + int r; + + assert_return_errno(udev_device && key, NULL, EINVAL); + + r = sd_device_get_property_value(udev_device->device, key, &value); + if (r < 0) { + errno = -r; + return NULL; + } + + return value; +} + +struct udev_device *udev_device_new(struct udev *udev) { + struct udev_device *udev_device; + + assert_return_errno(udev, NULL, EINVAL); + + udev_device = new0(struct udev_device, 1); + if (!udev_device) { + errno = ENOMEM; + return NULL; + } + udev_device->refcount = 1; + udev_device->udev = udev; + udev_list_init(udev, &udev_device->properties, true); + udev_list_init(udev, &udev_device->tags, true); + udev_list_init(udev, &udev_device->sysattrs, true); + udev_list_init(udev, &udev_device->devlinks, true); + + return udev_device; +} + +/** + * udev_device_new_from_syspath: + * @udev: udev library context + * @syspath: sys device path including sys directory + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The syspath is the absolute + * path to the device, including the sys mount point. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +_public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) { + struct udev_device *udev_device; + int r; + + udev_device = udev_device_new(udev); + if (!udev_device) + return NULL; + + r = sd_device_new_from_syspath(&udev_device->device, syspath); + if (r < 0) { + errno = -r; + udev_device_unref(udev_device); + return NULL; + } + + return udev_device; +} + +/** + * udev_device_new_from_devnum: + * @udev: udev library context + * @type: char or block device + * @devnum: device major/minor number + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The device is looked-up + * by its major/minor number and type. Character and block device + * numbers are not unique across the two types. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +_public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) +{ + struct udev_device *udev_device; + int r; + + udev_device = udev_device_new(udev); + if (!udev_device) + return NULL; + + r = sd_device_new_from_devnum(&udev_device->device, type, devnum); + if (r < 0) { + errno = -r; + udev_device_unref(udev_device); + return NULL; + } + + return udev_device; +} + +/** + * udev_device_new_from_device_id: + * @udev: udev library context + * @id: text string identifying a kernel device + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The device is looked-up + * by a special string: + * b8:2 - block device major:minor + * c128:1 - char device major:minor + * n3 - network device ifindex + * +sound:card29 - kernel driver core subsystem:device name + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) +{ + struct udev_device *udev_device; + int r; + + udev_device = udev_device_new(udev); + if (!udev_device) + return NULL; + + r = sd_device_new_from_device_id(&udev_device->device, id); + if (r < 0) { + errno = -r; + udev_device_unref(udev_device); + return NULL; + } + + return udev_device; +} + +/** + * udev_device_new_from_subsystem_sysname: + * @udev: udev library context + * @subsystem: the subsystem of the device + * @sysname: the name of the device + * + * Create new udev device, and fill in information from the sys device + * and the udev database entry. The device is looked up by the subsystem + * and name string of the device, like "mem" / "zero", or "block" / "sda". + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +_public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) +{ + struct udev_device *udev_device; + int r; + + udev_device = udev_device_new(udev); + if (!udev_device) + return NULL; + + r = sd_device_new_from_subsystem_sysname(&udev_device->device, subsystem, sysname); + if (r < 0) { + errno = -r; + udev_device_unref(udev_device); + return NULL; + } + + return udev_device; +} + +/** + * udev_device_new_from_environment + * @udev: udev library context + * + * Create new udev device, and fill in information from the + * current process environment. This only works reliable if + * the process is called from a udev rule. It is usually used + * for tools executed from IMPORT= rules. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +_public_ struct udev_device *udev_device_new_from_environment(struct udev *udev) +{ + struct udev_device *udev_device; + int r; + + udev_device = udev_device_new(udev); + if (!udev_device) + return NULL; + + r = device_new_from_strv(&udev_device->device, environ); + if (r < 0) { + errno = -r; + udev_device_unref(udev_device); + return NULL; + } + + return udev_device; +} + +static struct udev_device *device_new_from_parent(struct udev_device *child) +{ + struct udev_device *parent; + int r; + + assert_return_errno(child, NULL, EINVAL); + + parent = udev_device_new(child->udev); + if (!parent) + return NULL; + + r = sd_device_get_parent(child->device, &parent->device); + if (r < 0) { + errno = -r; + udev_device_unref(parent); + return NULL; + } + + /* the parent is unref'ed with the child, so take a ref from libudev as well */ + sd_device_ref(parent->device); + + return parent; +} + +/** + * udev_device_get_parent: + * @udev_device: the device to start searching from + * + * Find the next parent device, and fill in information from the sys + * device and the udev database entry. + * + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. + * + * It is not necessarily just the upper level directory, empty or not + * recognized sys directories are ignored. + * + * It can be called as many times as needed, without caring about + * references. + * + * Returns: a new udev device, or #NULL, if it no parent exist. + **/ +_public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + if (!udev_device->parent_set) { + udev_device->parent_set = true; + udev_device->parent = device_new_from_parent(udev_device); + } + + /* TODO: errno will differ here in case parent == NULL */ + return udev_device->parent; +} + +/** + * udev_device_get_parent_with_subsystem_devtype: + * @udev_device: udev device to start searching from + * @subsystem: the subsystem of the device + * @devtype: the type (DEVTYPE) of the device + * + * Find the next parent device, with a matching subsystem and devtype + * value, and fill in information from the sys device and the udev + * database entry. + * + * If devtype is #NULL, only subsystem is checked, and any devtype will + * match. + * + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. + * + * It can be called as many times as needed, without caring about + * references. + * + * Returns: a new udev device, or #NULL if no matching parent exists. + **/ +_public_ struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) +{ + sd_device *parent; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + /* this relies on the fact that finding the subdevice of a parent or the + parent of a subdevice commute */ + + /* first find the correct sd_device */ + r = sd_device_get_parent_with_subsystem_devtype(udev_device->device, subsystem, devtype, &parent); + if (r < 0) { + errno = -r; + return NULL; + } + + /* then walk the chain of udev_device parents until the correspanding + one is found */ + while ((udev_device = udev_device_get_parent(udev_device))) { + if (udev_device->device == parent) + return udev_device; + } + + errno = ENOENT; + return NULL; +} + +/** + * udev_device_get_udev: + * @udev_device: udev device + * + * Retrieve the udev library context the device was created with. + * + * Returns: the udev library context + **/ +_public_ struct udev *udev_device_get_udev(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + return udev_device->udev; +} + +/** + * udev_device_ref: + * @udev_device: udev device + * + * Take a reference of a udev device. + * + * Returns: the passed udev device + **/ +_public_ struct udev_device *udev_device_ref(struct udev_device *udev_device) +{ + if (udev_device) + udev_device->refcount++; + + return udev_device; +} + +/** + * udev_device_unref: + * @udev_device: udev device + * + * Drop a reference of a udev device. If the refcount reaches zero, + * the resources of the device will be released. + * + * Returns: #NULL + **/ +_public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) +{ + if (udev_device && (-- udev_device->refcount) == 0) { + sd_device_unref(udev_device->device); + udev_device_unref(udev_device->parent); + + udev_list_cleanup(&udev_device->properties); + udev_list_cleanup(&udev_device->sysattrs); + udev_list_cleanup(&udev_device->tags); + udev_list_cleanup(&udev_device->devlinks); + + free(udev_device); + } + + return NULL; +} + +/** + * udev_device_get_devpath: + * @udev_device: udev device + * + * Retrieve the kernel devpath value of the udev device. The path + * does not contain the sys mount point, and starts with a '/'. + * + * Returns: the devpath of the udev device + **/ +_public_ const char *udev_device_get_devpath(struct udev_device *udev_device) +{ + const char *devpath; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_devpath(udev_device->device, &devpath); + if (r < 0) { + errno = -r; + return NULL; + } + + return devpath; +} + +/** + * udev_device_get_syspath: + * @udev_device: udev device + * + * Retrieve the sys path of the udev device. The path is an + * absolute path and starts with the sys mount point. + * + * Returns: the sys path of the udev device + **/ +_public_ const char *udev_device_get_syspath(struct udev_device *udev_device) +{ + const char *syspath; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_syspath(udev_device->device, &syspath); + if (r < 0) { + errno = -r; + return NULL; + } + + return syspath; +} + +/** + * udev_device_get_sysname: + * @udev_device: udev device + * + * Get the kernel device name in /sys. + * + * Returns: the name string of the device + **/ +_public_ const char *udev_device_get_sysname(struct udev_device *udev_device) +{ + const char *sysname; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_sysname(udev_device->device, &sysname); + if (r < 0) { + errno = -r; + return NULL; + } + + return sysname; +} + +/** + * udev_device_get_sysnum: + * @udev_device: udev device + * + * Get the instance number of the device. + * + * Returns: the trailing number string of the device name + **/ +_public_ const char *udev_device_get_sysnum(struct udev_device *udev_device) +{ + const char *sysnum; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_sysnum(udev_device->device, &sysnum); + if (r < 0) { + errno = -r; + return NULL; + } + + return sysnum; +} + +/** + * udev_device_get_devnode: + * @udev_device: udev device + * + * Retrieve the device node file name belonging to the udev device. + * The path is an absolute path, and starts with the device directory. + * + * Returns: the device node file name of the udev device, or #NULL if no device node exists + **/ +_public_ const char *udev_device_get_devnode(struct udev_device *udev_device) +{ + const char *devnode; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_devname(udev_device->device, &devnode); + if (r < 0) { + errno = -r; + return NULL; + } + + return devnode; +} + +/** + * udev_device_get_devlinks_list_entry: + * @udev_device: udev device + * + * Retrieve the list of device links pointing to the device file of + * the udev device. The next list entry can be retrieved with + * udev_list_entry_get_next(), which returns #NULL if no more entries exist. + * The devlink path can be retrieved from the list entry by + * udev_list_entry_get_name(). The path is an absolute path, and starts with + * the device directory. + * + * Returns: the first entry of the device node link list + **/ +_public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + if (device_get_devlinks_generation(udev_device->device) != udev_device->devlinks_generation || + !udev_device->devlinks_read) { + const char *devlink; + + udev_list_cleanup(&udev_device->devlinks); + + FOREACH_DEVICE_DEVLINK(udev_device->device, devlink) + udev_list_entry_add(&udev_device->devlinks, devlink, NULL); + + udev_device->devlinks_read = true; + udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device); + } + + return udev_list_get_entry(&udev_device->devlinks); +} + +/** + * udev_device_get_event_properties_entry: + * @udev_device: udev device + * + * Retrieve the list of key/value device properties of the udev + * device. The next list entry can be retrieved with udev_list_entry_get_next(), + * which returns #NULL if no more entries exist. The property name + * can be retrieved from the list entry by udev_list_entry_get_name(), + * the property value by udev_list_entry_get_value(). + * + * Returns: the first entry of the property list + **/ +_public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + if (device_get_properties_generation(udev_device->device) != udev_device->properties_generation || + !udev_device->properties_read) { + const char *key, *value; + + udev_list_cleanup(&udev_device->properties); + + FOREACH_DEVICE_PROPERTY(udev_device->device, key, value) + udev_list_entry_add(&udev_device->properties, key, value); + + udev_device->properties_read = true; + udev_device->properties_generation = device_get_properties_generation(udev_device->device); + } + + return udev_list_get_entry(&udev_device->properties); +} + +/** + * udev_device_get_action: + * @udev_device: udev device + * + * This is only valid if the device was received through a monitor. Devices read from + * sys do not have an action string. Usual actions are: add, remove, change, online, + * offline. + * + * Returns: the kernel action value, or #NULL if there is no action value available. + **/ +_public_ const char *udev_device_get_action(struct udev_device *udev_device) { + const char *action = NULL; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_property_value(udev_device->device, "ACTION", &action); + if (r < 0 && r != -ENOENT) { + errno = -r; + return NULL; + } + + return action; +} + +/** + * udev_device_get_usec_since_initialized: + * @udev_device: udev device + * + * Return the number of microseconds passed since udev set up the + * device for the first time. + * + * This is only implemented for devices with need to store properties + * in the udev database. All other devices return 0 here. + * + * Returns: the number of microseconds since the device was first seen. + **/ +_public_ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) +{ + usec_t ts; + int r; + + assert_return(udev_device, -EINVAL); + + r = sd_device_get_usec_since_initialized(udev_device->device, &ts); + if (r < 0) { + errno = EINVAL; + return 0; + } + + return ts; +} + +/** + * udev_device_get_sysattr_value: + * @udev_device: udev device + * @sysattr: attribute name + * + * The retrieved value is cached in the device. Repeated calls will return the same + * value and not open the attribute again. + * + * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. + **/ +_public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) +{ + const char *value; + int r; + + assert_return_errno(udev_device, NULL, EINVAL); + + r = sd_device_get_sysattr_value(udev_device->device, sysattr, &value); + if (r < 0) { + errno = -r; + return NULL; + } + + return value; +} + +/** + * udev_device_set_sysattr_value: + * @udev_device: udev device + * @sysattr: attribute name + * @value: new value to be set + * + * Update the contents of the sys attribute and the cached value of the device. + * + * Returns: Negative error code on failure or 0 on success. + **/ +_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value) +{ + int r; + + assert_return(udev_device, -EINVAL); + + r = sd_device_set_sysattr_value(udev_device->device, sysattr, value); + if (r < 0) + return r; + + return 0; +} + +/** + * udev_device_get_sysattr_list_entry: + * @udev_device: udev device + * + * Retrieve the list of available sysattrs, with value being empty; + * This just return all available sysfs attributes for a particular + * device without reading their values. + * + * Returns: the first entry of the property list + **/ +_public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + if (!udev_device->sysattrs_read) { + const char *sysattr; + + udev_list_cleanup(&udev_device->sysattrs); + + FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr) + udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL); + + udev_device->sysattrs_read = true; + } + + return udev_list_get_entry(&udev_device->sysattrs); +} + +/** + * udev_device_get_is_initialized: + * @udev_device: udev device + * + * Check if udev has already handled the device and has set up + * device node permissions and context, or has renamed a network + * device. + * + * This is only implemented for devices with a device node + * or network interfaces. All other devices return 1 here. + * + * Returns: 1 if the device is set up. 0 otherwise. + **/ +_public_ int udev_device_get_is_initialized(struct udev_device *udev_device) +{ + int r, initialized; + + assert_return(udev_device, -EINVAL); + + r = sd_device_get_is_initialized(udev_device->device, &initialized); + if (r < 0) { + errno = -r; + + return 0; + } + + return initialized; +} + +/** + * udev_device_get_tags_list_entry: + * @udev_device: udev device + * + * Retrieve the list of tags attached to the udev device. The next + * list entry can be retrieved with udev_list_entry_get_next(), + * which returns #NULL if no more entries exist. The tag string + * can be retrieved from the list entry by udev_list_entry_get_name(). + * + * Returns: the first entry of the tag list + **/ +_public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) +{ + assert_return_errno(udev_device, NULL, EINVAL); + + if (device_get_tags_generation(udev_device->device) != udev_device->tags_generation || + !udev_device->tags_read) { + const char *tag; + + udev_list_cleanup(&udev_device->tags); + + FOREACH_DEVICE_TAG(udev_device->device, tag) + udev_list_entry_add(&udev_device->tags, tag, NULL); + + udev_device->tags_read = true; + udev_device->tags_generation = device_get_tags_generation(udev_device->device); + } + + return udev_list_get_entry(&udev_device->tags); +} + +/** + * udev_device_has_tag: + * @udev_device: udev device + * @tag: tag name + * + * Check if a given device has a certain tag associated. + * + * Returns: 1 if the tag is found. 0 otherwise. + **/ +_public_ int udev_device_has_tag(struct udev_device *udev_device, const char *tag) +{ + assert_return(udev_device, 0); + + return sd_device_has_tag(udev_device->device, tag); +} diff --git a/src/libudev/src/libudev-enumerate.c b/src/libudev/src/libudev-enumerate.c new file mode 100644 index 0000000000..52b2ccff0b --- /dev/null +++ b/src/libudev/src/libudev-enumerate.c @@ -0,0 +1,420 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + 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 . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "sd-device/device-enumerator-private.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" + +#include "libudev-device-internal.h" + +/** + * SECTION:libudev-enumerate + * @short_description: lookup and sort sys devices + * + * Lookup devices in the sys filesystem, filter devices by properties, + * and return a sorted list of devices. + */ + +/** + * udev_enumerate: + * + * Opaque object representing one device lookup/sort context. + */ +struct udev_enumerate { + struct udev *udev; + int refcount; + struct udev_list devices_list; + bool devices_uptodate:1; + + sd_device_enumerator *enumerator; +}; + +/** + * udev_enumerate_new: + * @udev: udev library context + * + * Create an enumeration context to scan /sys. + * + * Returns: an enumeration context. + **/ +_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) { + _cleanup_free_ struct udev_enumerate *udev_enumerate = NULL; + struct udev_enumerate *ret; + int r; + + assert_return_errno(udev, NULL, EINVAL); + + udev_enumerate = new0(struct udev_enumerate, 1); + if (!udev_enumerate) { + errno = ENOMEM; + return NULL; + } + + r = sd_device_enumerator_new(&udev_enumerate->enumerator); + if (r < 0) { + errno = -r; + return NULL; + } + + r = sd_device_enumerator_allow_uninitialized(udev_enumerate->enumerator); + if (r < 0) { + errno = -r; + return NULL; + } + + udev_enumerate->refcount = 1; + udev_enumerate->udev = udev; + + udev_list_init(udev, &udev_enumerate->devices_list, false); + + ret = udev_enumerate; + udev_enumerate = NULL; + + return ret; +} + +/** + * udev_enumerate_ref: + * @udev_enumerate: context + * + * Take a reference of a enumeration context. + * + * Returns: the passed enumeration context + **/ +_public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) { + if (udev_enumerate) + udev_enumerate->refcount++; + + return udev_enumerate; +} + +/** + * udev_enumerate_unref: + * @udev_enumerate: context + * + * Drop a reference of an enumeration context. If the refcount reaches zero, + * all resources of the enumeration context will be released. + * + * Returns: #NULL + **/ +_public_ struct udev_enumerate *udev_enumerate_unref(struct udev_enumerate *udev_enumerate) { + if (udev_enumerate && (-- udev_enumerate->refcount) == 0) { + udev_list_cleanup(&udev_enumerate->devices_list); + sd_device_enumerator_unref(udev_enumerate->enumerator); + free(udev_enumerate); + } + + return NULL; +} + +/** + * udev_enumerate_get_udev: + * @udev_enumerate: context + * + * Get the udev library context. + * + * Returns: a pointer to the context. + */ +_public_ struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) { + assert_return_errno(udev_enumerate, NULL, EINVAL); + + return udev_enumerate->udev; +} + +/** + * udev_enumerate_get_list_entry: + * @udev_enumerate: context + * + * Get the first entry of the sorted list of device paths. + * + * Returns: a udev_list_entry. + */ +_public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) { + assert_return_errno(udev_enumerate, NULL, EINVAL); + + if (!udev_enumerate->devices_uptodate) { + sd_device *device; + + udev_list_cleanup(&udev_enumerate->devices_list); + + FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) { + const char *syspath; + int r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) { + errno = -r; + return NULL; + } + + udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL); + } + + udev_enumerate->devices_uptodate = true; + } + + return udev_list_get_entry(&udev_enumerate->devices_list); +} + +/** + * udev_enumerate_add_match_subsystem: + * @udev_enumerate: context + * @subsystem: filter for a subsystem of the device to include in the list + * + * Match only devices belonging to a certain kernel subsystem. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { + assert_return(udev_enumerate, -EINVAL); + + if (!subsystem) + return 0; + + return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true); +} + +/** + * udev_enumerate_add_nomatch_subsystem: + * @udev_enumerate: context + * @subsystem: filter for a subsystem of the device to exclude from the list + * + * Match only devices not belonging to a certain kernel subsystem. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { + assert_return(udev_enumerate, -EINVAL); + + if (!subsystem) + return 0; + + return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false); +} + +/** + * udev_enumerate_add_match_sysattr: + * @udev_enumerate: context + * @sysattr: filter for a sys attribute at the device to include in the list + * @value: optional value of the sys attribute + * + * Match only devices with a certain /sys device attribute. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { + assert_return(udev_enumerate, -EINVAL); + + if (!sysattr) + return 0; + + return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true); +} + +/** + * udev_enumerate_add_nomatch_sysattr: + * @udev_enumerate: context + * @sysattr: filter for a sys attribute at the device to exclude from the list + * @value: optional value of the sys attribute + * + * Match only devices not having a certain /sys device attribute. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { + assert_return(udev_enumerate, -EINVAL); + + if (!sysattr) + return 0; + + return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false); +} + +/** + * udev_enumerate_add_match_property: + * @udev_enumerate: context + * @property: filter for a property of the device to include in the list + * @value: value of the property + * + * Match only devices with a certain property. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) { + assert_return(udev_enumerate, -EINVAL); + + if (!property) + return 0; + + return sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value); +} + +/** + * udev_enumerate_add_match_tag: + * @udev_enumerate: context + * @tag: filter for a tag of the device to include in the list + * + * Match only devices with a certain tag. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) { + assert_return(udev_enumerate, -EINVAL); + + if (!tag) + return 0; + + return sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag); +} + +/** + * udev_enumerate_add_match_parent: + * @udev_enumerate: context + * @parent: parent device where to start searching + * + * Return the devices on the subtree of one given device. The parent + * itself is included in the list. + * + * A reference for the device is held until the udev_enumerate context + * is cleaned up. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) { + assert_return(udev_enumerate, -EINVAL); + + if (!parent) + return 0; + + return sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device); +} + +/** + * udev_enumerate_add_match_is_initialized: + * @udev_enumerate: context + * + * Match only devices which udev has set up already. This makes + * sure, that the device node permissions and context are properly set + * and that network devices are fully renamed. + * + * Usually, devices which are found in the kernel but not already + * handled by udev, have still pending events. Services should subscribe + * to monitor events and wait for these devices to become ready, instead + * of using uninitialized devices. + * + * For now, this will not affect devices which do not have a device node + * and are not network interfaces. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) { + assert_return(udev_enumerate, -EINVAL); + + return device_enumerator_add_match_is_initialized(udev_enumerate->enumerator); +} + +/** + * udev_enumerate_add_match_sysname: + * @udev_enumerate: context + * @sysname: filter for the name of the device to include in the list + * + * Match only devices with a given /sys device name. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { + assert_return(udev_enumerate, -EINVAL); + + if (!sysname) + return 0; + + return sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname); +} + +/** + * udev_enumerate_add_syspath: + * @udev_enumerate: context + * @syspath: path of a device + * + * Add a device to the list of devices, to retrieve it back sorted in dependency order. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + assert_return(udev_enumerate, -EINVAL); + + if (!syspath) + return 0; + + r = sd_device_new_from_syspath(&device, syspath); + if (r < 0) + return r; + + r = device_enumerator_add_device(udev_enumerate->enumerator, device); + if (r < 0) + return r; + + return 0; +} + +/** + * udev_enumerate_scan_devices: + * @udev_enumerate: udev enumeration context + * + * Scan /sys for all devices which match the given filters. No matches + * will return all currently available devices. + * + * Returns: 0 on success, otherwise a negative error value. + **/ +_public_ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) { + assert_return(udev_enumerate, -EINVAL); + + return device_enumerator_scan_devices(udev_enumerate->enumerator); +} + +/** + * udev_enumerate_scan_subsystems: + * @udev_enumerate: udev enumeration context + * + * Scan /sys for all kernel subsystems, including buses, classes, drivers. + * + * Returns: 0 on success, otherwise a negative error value. + **/ +_public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) { + assert_return(udev_enumerate, -EINVAL); + + return device_enumerator_scan_subsystems(udev_enumerate->enumerator); +} diff --git a/src/libudev/src/libudev-hwdb.c b/src/libudev/src/libudev-hwdb.c new file mode 100644 index 0000000000..1d47cfaa4e --- /dev/null +++ b/src/libudev/src/libudev-hwdb.c @@ -0,0 +1,146 @@ +/*** + This file is part of systemd. + + Copyright 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 . +***/ + +#include "basic/alloc-util.h" +#include "sd-hwdb/hwdb-util.h" +#include "sd-hwdb/sd-hwdb.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev-hwdb + * @short_description: retrieve properties from the hardware database + * + * Libudev hardware database interface. + */ + +/** + * udev_hwdb: + * + * Opaque object representing the hardware database. + */ +struct udev_hwdb { + struct udev *udev; + int refcount; + + sd_hwdb *hwdb; + + struct udev_list properties_list; +}; + +/** + * udev_hwdb_new: + * @udev: udev library context + * + * Create a hardware database context to query properties for devices. + * + * Returns: a hwdb context. + **/ +_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb_internal = NULL; + struct udev_hwdb *hwdb; + int r; + + assert_return(udev, NULL); + + r = sd_hwdb_new(&hwdb_internal); + if (r < 0) + return NULL; + + hwdb = new0(struct udev_hwdb, 1); + if (!hwdb) + return NULL; + + hwdb->refcount = 1; + hwdb->hwdb = hwdb_internal; + hwdb_internal = NULL; + + udev_list_init(udev, &hwdb->properties_list, true); + + return hwdb; +} + +/** + * udev_hwdb_ref: + * @hwdb: context + * + * Take a reference of a hwdb context. + * + * Returns: the passed enumeration context + **/ +_public_ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb) { + if (!hwdb) + return NULL; + hwdb->refcount++; + return hwdb; +} + +/** + * udev_hwdb_unref: + * @hwdb: context + * + * Drop a reference of a hwdb context. If the refcount reaches zero, + * all resources of the hwdb context will be released. + * + * Returns: #NULL + **/ +_public_ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb) { + if (!hwdb) + return NULL; + hwdb->refcount--; + if (hwdb->refcount > 0) + return NULL; + sd_hwdb_unref(hwdb->hwdb); + udev_list_cleanup(&hwdb->properties_list); + free(hwdb); + return NULL; +} + +/** + * udev_hwdb_get_properties_list_entry: + * @hwdb: context + * @modalias: modalias string + * @flags: (unused) + * + * Lookup a matching device in the hardware database. The lookup key is a + * modalias string, whose formats are defined for the Linux kernel modules. + * Examples are: pci:v00008086d00001C2D*, usb:v04F2pB221*. The first entry + * of a list of retrieved properties is returned. + * + * Returns: a udev_list_entry. + */ +_public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags) { + const char *key, *value; + + if (!hwdb || !modalias) { + errno = EINVAL; + return NULL; + } + + udev_list_cleanup(&hwdb->properties_list); + + SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value) { + if (udev_list_entry_add(&hwdb->properties_list, key, value) == NULL) { + errno = ENOMEM; + return NULL; + } + } + + return udev_list_get_entry(&hwdb->properties_list); +} diff --git a/src/libudev/src/libudev-list.c b/src/libudev/src/libudev-list.c new file mode 100644 index 0000000000..a943dc0cd1 --- /dev/null +++ b/src/libudev/src/libudev-list.c @@ -0,0 +1,353 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev-list + * @short_description: list operation + * + * Libudev list operations. + */ + +/** + * udev_list_entry: + * + * Opaque object representing one entry in a list. An entry contains + * contains a name, and optionally a value. + */ +struct udev_list_entry { + struct udev_list_node node; + struct udev_list *list; + char *name; + char *value; + int num; +}; + +/* the list's head points to itself if empty */ +void udev_list_node_init(struct udev_list_node *list) +{ + list->next = list; + list->prev = list; +} + +int udev_list_node_is_empty(struct udev_list_node *list) +{ + return list->next == list; +} + +static void udev_list_node_insert_between(struct udev_list_node *new, + struct udev_list_node *prev, + struct udev_list_node *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) +{ + udev_list_node_insert_between(new, list->prev, list); +} + +void udev_list_node_remove(struct udev_list_node *entry) +{ + struct udev_list_node *prev = entry->prev; + struct udev_list_node *next = entry->next; + + next->prev = prev; + prev->next = next; + + entry->prev = NULL; + entry->next = NULL; +} + +/* return list entry which embeds this node */ +static inline struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) +{ + return container_of(node, struct udev_list_entry, node); +} + +void udev_list_init(struct udev *udev, struct udev_list *list, bool unique) +{ + memzero(list, sizeof(struct udev_list)); + list->udev = udev; + list->unique = unique; + udev_list_node_init(&list->node); +} + +/* insert entry into a list as the last element */ +static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) +{ + /* inserting before the list head make the node the last node in the list */ + udev_list_node_insert_between(&new->node, list->node.prev, &list->node); + new->list = list; +} + +/* insert entry into a list, before a given existing entry */ +static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) +{ + udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); + new->list = entry->list; +} + +/* binary search in sorted array */ +static int list_search(struct udev_list *list, const char *name) +{ + unsigned int first, last; + + first = 0; + last = list->entries_cur; + while (first < last) { + unsigned int i; + int cmp; + + i = (first + last)/2; + cmp = strcmp(name, list->entries[i]->name); + if (cmp < 0) + last = i; + else if (cmp > 0) + first = i+1; + else + return i; + } + + /* not found, return negative insertion-index+1 */ + return -(first+1); +} + +struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) +{ + struct udev_list_entry *entry; + int i = 0; + + if (list->unique) { + /* lookup existing name or insertion-index */ + i = list_search(list, name); + if (i >= 0) { + entry = list->entries[i]; + + free(entry->value); + if (value == NULL) { + entry->value = NULL; + return entry; + } + entry->value = strdup(value); + if (entry->value == NULL) + return NULL; + return entry; + } + } + + /* add new name */ + entry = new0(struct udev_list_entry, 1); + if (entry == NULL) + return NULL; + entry->name = strdup(name); + if (entry->name == NULL) { + free(entry); + return NULL; + } + if (value != NULL) { + entry->value = strdup(value); + if (entry->value == NULL) { + free(entry->name); + free(entry); + return NULL; + } + } + + if (list->unique) { + /* allocate or enlarge sorted array if needed */ + if (list->entries_cur >= list->entries_max) { + struct udev_list_entry **entries; + unsigned int add; + + add = list->entries_max; + if (add < 1) + add = 64; + entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *)); + if (entries == NULL) { + free(entry->name); + free(entry->value); + free(entry); + return NULL; + } + list->entries = entries; + list->entries_max += add; + } + + /* the negative i returned the insertion index */ + i = (-i)-1; + + /* insert into sorted list */ + if ((unsigned int)i < list->entries_cur) + udev_list_entry_insert_before(entry, list->entries[i]); + else + udev_list_entry_append(entry, list); + + /* insert into sorted array */ + memmove(&list->entries[i+1], &list->entries[i], + (list->entries_cur - i) * sizeof(struct udev_list_entry *)); + list->entries[i] = entry; + list->entries_cur++; + } else { + udev_list_entry_append(entry, list); + } + + return entry; +} + +void udev_list_entry_delete(struct udev_list_entry *entry) +{ + if (entry->list->entries != NULL) { + int i; + struct udev_list *list = entry->list; + + /* remove entry from sorted array */ + i = list_search(list, entry->name); + if (i >= 0) { + memmove(&list->entries[i], &list->entries[i+1], + ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *)); + list->entries_cur--; + } + } + + udev_list_node_remove(&entry->node); + free(entry->name); + free(entry->value); + free(entry); +} + +void udev_list_cleanup(struct udev_list *list) +{ + struct udev_list_entry *entry_loop; + struct udev_list_entry *entry_tmp; + + list->entries = mfree(list->entries); + list->entries_cur = 0; + list->entries_max = 0; + udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) + udev_list_entry_delete(entry_loop); +} + +struct udev_list_entry *udev_list_get_entry(struct udev_list *list) +{ + if (udev_list_node_is_empty(&list->node)) + return NULL; + return list_node_to_entry(list->node.next); +} + +/** + * udev_list_entry_get_next: + * @list_entry: current entry + * + * Get the next entry from the list. + * + * Returns: udev_list_entry, #NULL if no more entries are available. + */ +_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + struct udev_list_node *next; + + if (list_entry == NULL) + return NULL; + next = list_entry->node.next; + /* empty list or no more entries */ + if (next == &list_entry->list->node) + return NULL; + return list_node_to_entry(next); +} + +/** + * udev_list_entry_get_by_name: + * @list_entry: current entry + * @name: name string to match + * + * Lookup an entry in the list with a certain name. + * + * Returns: udev_list_entry, #NULL if no matching entry is found. + */ +_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) +{ + int i; + + if (list_entry == NULL) + return NULL; + + if (!list_entry->list->unique) + return NULL; + + i = list_search(list_entry->list, name); + if (i < 0) + return NULL; + return list_entry->list->entries[i]; +} + +/** + * udev_list_entry_get_name: + * @list_entry: current entry + * + * Get the name of a list entry. + * + * Returns: the name string of this entry. + */ +_public_ const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return NULL; + return list_entry->name; +} + +/** + * udev_list_entry_get_value: + * @list_entry: current entry + * + * Get the value of list entry. + * + * Returns: the value string of this entry. + */ +_public_ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return NULL; + return list_entry->value; +} + +int udev_list_entry_get_num(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return -EINVAL; + return list_entry->num; +} + +void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num) +{ + if (list_entry == NULL) + return; + list_entry->num = num; +} diff --git a/src/libudev/src/libudev-monitor.c b/src/libudev/src/libudev-monitor.c new file mode 100644 index 0000000000..87ee3d2e1b --- /dev/null +++ b/src/libudev/src/libudev-monitor.c @@ -0,0 +1,847 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/missing.h" +#include "basic/mount-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev-monitor + * @short_description: device event source + * + * Connects to a device event source. + */ + +/** + * udev_monitor: + * + * Opaque object handling an event source. + */ +struct udev_monitor { + struct udev *udev; + int refcount; + int sock; + union sockaddr_union snl; + union sockaddr_union snl_trusted_sender; + union sockaddr_union snl_destination; + socklen_t addrlen; + struct udev_list filter_subsystem_list; + struct udev_list filter_tag_list; + bool bound; +}; + +enum udev_monitor_netlink_group { + UDEV_MONITOR_NONE, + UDEV_MONITOR_KERNEL, + UDEV_MONITOR_UDEV, +}; + +#define UDEV_MONITOR_MAGIC 0xfeedcafe +struct udev_monitor_netlink_header { + /* "libudev" prefix to distinguish libudev and kernel messages */ + char prefix[8]; + /* + * magic to protect against daemon <-> library message format mismatch + * used in the kernel from socket filter rules; needs to be stored in network order + */ + unsigned int magic; + /* total length of header structure known to the sender */ + unsigned int header_size; + /* properties string buffer */ + unsigned int properties_off; + unsigned int properties_len; + /* + * hashes of primary device properties strings, to let libudev subscribers + * use in-kernel socket filters; values need to be stored in network order + */ + unsigned int filter_subsystem_hash; + unsigned int filter_devtype_hash; + unsigned int filter_tag_bloom_hi; + unsigned int filter_tag_bloom_lo; +}; + +static struct udev_monitor *udev_monitor_new(struct udev *udev) +{ + struct udev_monitor *udev_monitor; + + udev_monitor = new0(struct udev_monitor, 1); + if (udev_monitor == NULL) + return NULL; + udev_monitor->refcount = 1; + udev_monitor->udev = udev; + udev_list_init(udev, &udev_monitor->filter_subsystem_list, false); + udev_list_init(udev, &udev_monitor->filter_tag_list, true); + return udev_monitor; +} + +/* we consider udev running when /dev is on devtmpfs */ +static bool udev_has_devtmpfs(struct udev *udev) { + + union file_handle_union h = FILE_HANDLE_INIT; + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX], *e; + int mount_id; + int r; + + r = name_to_handle_at(AT_FDCWD, "/dev", &h.handle, &mount_id, 0); + if (r < 0) { + if (errno != EOPNOTSUPP) + log_debug_errno(errno, "name_to_handle_at on /dev: %m"); + return false; + } + + f = fopen("/proc/self/mountinfo", "re"); + if (!f) + return false; + + FOREACH_LINE(line, f, return false) { + int mid; + + if (sscanf(line, "%i", &mid) != 1) + continue; + + if (mid != mount_id) + continue; + + e = strstr(line, " - "); + if (!e) + continue; + + /* accept any name that starts with the currently expected type */ + if (startswith(e + 3, "devtmpfs")) + return true; + } + + return false; +} + +static void monitor_set_nl_address(struct udev_monitor *udev_monitor) { + union sockaddr_union snl; + socklen_t addrlen; + int r; + + assert(udev_monitor); + + /* get the address the kernel has assigned us + * it is usually, but not necessarily the pid + */ + addrlen = sizeof(struct sockaddr_nl); + r = getsockname(udev_monitor->sock, &snl.sa, &addrlen); + if (r >= 0) + udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid; +} + +struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) +{ + struct udev_monitor *udev_monitor; + unsigned int group; + + if (udev == NULL) + return NULL; + + if (name == NULL) + group = UDEV_MONITOR_NONE; + else if (streq(name, "udev")) { + /* + * We do not support subscribing to uevents if no instance of + * udev is running. Uevents would otherwise broadcast the + * processing data of the host into containers, which is not + * desired. + * + * Containers will currently not get any udev uevents, until + * a supporting infrastructure is available. + * + * We do not set a netlink multicast group here, so the socket + * will not receive any messages. + */ + if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) { + log_debug("the udev service seems not to be active, disable the monitor"); + group = UDEV_MONITOR_NONE; + } else + group = UDEV_MONITOR_UDEV; + } else if (streq(name, "kernel")) + group = UDEV_MONITOR_KERNEL; + else + return NULL; + + udev_monitor = udev_monitor_new(udev); + if (udev_monitor == NULL) + return NULL; + + if (fd < 0) { + udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); + if (udev_monitor->sock < 0) { + log_debug_errno(errno, "error getting socket: %m"); + free(udev_monitor); + return NULL; + } + } else { + udev_monitor->bound = true; + udev_monitor->sock = fd; + monitor_set_nl_address(udev_monitor); + } + + udev_monitor->snl.nl.nl_family = AF_NETLINK; + udev_monitor->snl.nl.nl_groups = group; + + /* default destination for sending */ + udev_monitor->snl_destination.nl.nl_family = AF_NETLINK; + udev_monitor->snl_destination.nl.nl_groups = UDEV_MONITOR_UDEV; + + return udev_monitor; +} + +/** + * udev_monitor_new_from_netlink: + * @udev: udev library context + * @name: name of event source + * + * Create new udev monitor and connect to a specified event + * source. Valid sources identifiers are "udev" and "kernel". + * + * Applications should usually not connect directly to the + * "kernel" events, because the devices might not be useable + * at that time, before udev has configured them, and created + * device nodes. Accessing devices at the same time as udev, + * might result in unpredictable behavior. The "udev" events + * are sent out after udev has finished its event processing, + * all rules have been processed, and needed device nodes are + * created. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev monitor. + * + * Returns: a new udev monitor, or #NULL, in case of an error + **/ +_public_ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) +{ + return udev_monitor_new_from_netlink_fd(udev, name, -1); +} + +static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, + unsigned short code, unsigned int data) +{ + struct sock_filter *ins = &inss[*i]; + + ins->code = code; + ins->k = data; + (*i)++; +} + +static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, + unsigned short code, unsigned int data, + unsigned short jt, unsigned short jf) +{ + struct sock_filter *ins = &inss[*i]; + + ins->code = code; + ins->jt = jt; + ins->jf = jf; + ins->k = data; + (*i)++; +} + +/** + * udev_monitor_filter_update: + * @udev_monitor: monitor + * + * Update the installed socket filter. This is only needed, + * if the filter was removed or changed. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) +{ + struct sock_filter ins[512]; + struct sock_fprog filter; + unsigned int i; + struct udev_list_entry *list_entry; + int err; + + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL && + udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) + return 0; + + memzero(ins, sizeof(ins)); + i = 0; + + /* load magic in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); + /* jump if magic matches */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); + /* wrong magic, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) { + int tag_matches; + + /* count tag matches, to calculate end of tag match block */ + tag_matches = 0; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) + tag_matches++; + + /* add all tags matches */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { + uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); + uint32_t tag_bloom_hi = tag_bloom_bits >> 32; + uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; + + /* load device bloom bits in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)); + /* clear bits (tag bits & bloom bits) */ + bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi); + /* jump to next tag if it does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3); + + /* load device bloom bits in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)); + /* clear bits (tag bits & bloom bits) */ + bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo); + /* jump behind end of tag match block if tag matches */ + tag_matches--; + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0); + } + + /* nothing matched, drop packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); + } + + /* add all subsystem matches */ + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { + unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); + + /* load device subsystem value in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)); + if (udev_list_entry_get_value(list_entry) == NULL) { + /* jump if subsystem does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); + } else { + /* jump if subsystem does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3); + + /* load device devtype value in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)); + /* jump if value does not match */ + hash = util_string_hash32(udev_list_entry_get_value(list_entry)); + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); + } + + /* matched, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + if (i+1 >= ELEMENTSOF(ins)) + return -E2BIG; + } + + /* nothing matched, drop packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); + } + + /* matched, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + /* install filter */ + memzero(&filter, sizeof(filter)); + filter.len = i; + filter.filter = ins; + err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); + return err < 0 ? -errno : 0; +} + +int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) +{ + udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid; + return 0; +} + +/** + * udev_monitor_enable_receiving: + * @udev_monitor: the monitor which should receive events + * + * Binds the @udev_monitor socket to the event source. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + int err = 0; + const int on = 1; + + udev_monitor_filter_update(udev_monitor); + + if (!udev_monitor->bound) { + err = bind(udev_monitor->sock, + &udev_monitor->snl.sa, sizeof(struct sockaddr_nl)); + if (err == 0) + udev_monitor->bound = true; + } + + if (err >= 0) + monitor_set_nl_address(udev_monitor); + 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)); + if (err < 0) + log_debug_errno(errno, "setting SO_PASSCRED failed: %m"); + + return 0; +} + +/** + * udev_monitor_set_receive_buffer_size: + * @udev_monitor: the monitor which should receive events + * @size: the size in bytes + * + * Set the size of the kernel socket buffer. This call needs the + * appropriate privileges to succeed. + * + * Returns: 0 on success, otherwise -1 on error. + */ +_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) +{ + if (udev_monitor == NULL) + return -EINVAL; + return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); +} + +int udev_monitor_disconnect(struct udev_monitor *udev_monitor) +{ + int err; + + err = close(udev_monitor->sock); + udev_monitor->sock = -1; + return err < 0 ? -errno : 0; +} + +/** + * udev_monitor_ref: + * @udev_monitor: udev monitor + * + * Take a reference of a udev monitor. + * + * Returns: the passed udev monitor + **/ +_public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return NULL; + udev_monitor->refcount++; + return udev_monitor; +} + +/** + * udev_monitor_unref: + * @udev_monitor: udev monitor + * + * Drop a reference of a udev monitor. If the refcount reaches zero, + * the bound socket will be closed, and the resources of the monitor + * will be released. + * + * Returns: #NULL + **/ +_public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return NULL; + udev_monitor->refcount--; + if (udev_monitor->refcount > 0) + return NULL; + if (udev_monitor->sock >= 0) + close(udev_monitor->sock); + udev_list_cleanup(&udev_monitor->filter_subsystem_list); + udev_list_cleanup(&udev_monitor->filter_tag_list); + free(udev_monitor); + return NULL; +} + +/** + * udev_monitor_get_udev: + * @udev_monitor: udev monitor + * + * Retrieve the udev library context the monitor was created with. + * + * Returns: the udev library context + **/ +_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return NULL; + return udev_monitor->udev; +} + +/** + * udev_monitor_get_fd: + * @udev_monitor: udev monitor + * + * Retrieve the socket file descriptor associated with the monitor. + * + * Returns: the socket file descriptor + **/ +_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return -EINVAL; + return udev_monitor->sock; +} + +static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) + goto tag; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { + const char *subsys = udev_list_entry_get_name(list_entry); + const char *dsubsys = udev_device_get_subsystem(udev_device); + const char *devtype; + const char *ddevtype; + + if (!streq(dsubsys, subsys)) + continue; + + devtype = udev_list_entry_get_value(list_entry); + if (devtype == NULL) + goto tag; + ddevtype = udev_device_get_devtype(udev_device); + if (ddevtype == NULL) + continue; + if (streq(ddevtype, devtype)) + goto tag; + } + return 0; + +tag: + if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) + return 1; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { + const char *tag = udev_list_entry_get_name(list_entry); + + if (udev_device_has_tag(udev_device, tag)) + return 1; + } + return 0; +} + +/** + * udev_monitor_receive_device: + * @udev_monitor: udev monitor + * + * Receive data from the udev monitor socket, allocate a new udev + * device, fill in the received data, and return the device. + * + * Only socket connections with uid=0 are accepted. + * + * The monitor socket is by default set to NONBLOCK. A variant of poll() on + * the file descriptor returned by udev_monitor_get_fd() should to be used to + * wake up when new devices arrive, or alternatively the file descriptor + * switched into blocking mode. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, in case of an error + **/ +_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_device *udev_device; + struct msghdr smsg; + struct iovec iov; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cmsg; + union sockaddr_union snl; + struct ucred *cred; + union { + struct udev_monitor_netlink_header nlh; + char raw[8192]; + } buf; + ssize_t buflen; + ssize_t bufpos; + bool is_initialized = false; + +retry: + if (udev_monitor == NULL) + return NULL; + iov.iov_base = &buf; + iov.iov_len = sizeof(buf); + memzero(&smsg, sizeof(struct msghdr)); + smsg.msg_iov = &iov; + smsg.msg_iovlen = 1; + smsg.msg_control = cred_msg; + smsg.msg_controllen = sizeof(cred_msg); + smsg.msg_name = &snl; + smsg.msg_namelen = sizeof(snl); + + buflen = recvmsg(udev_monitor->sock, &smsg, 0); + if (buflen < 0) { + if (errno != EINTR) + log_debug("unable to receive message"); + return NULL; + } + + if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC)) { + log_debug("invalid message length"); + return NULL; + } + + if (snl.nl.nl_groups == 0) { + /* unicast message, check if we trust the sender */ + if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || + snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { + log_debug("unicast netlink message ignored"); + return NULL; + } + } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { + if (snl.nl.nl_pid > 0) { + log_debug("multicast kernel netlink message from PID %"PRIu32" ignored", + snl.nl.nl_pid); + return NULL; + } + } + + cmsg = CMSG_FIRSTHDR(&smsg); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + log_debug("no sender credentials received, message ignored"); + return NULL; + } + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + log_debug("sender uid="UID_FMT", message ignored", cred->uid); + return NULL; + } + + if (memcmp(buf.raw, "libudev", 8) == 0) { + /* udev message needs proper version magic */ + if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)) { + log_debug("unrecognized message signature (%x != %x)", + buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC)); + return NULL; + } + if (buf.nlh.properties_off+32 > (size_t)buflen) { + log_debug("message smaller than expected (%u > %zd)", + buf.nlh.properties_off+32, buflen); + return NULL; + } + + bufpos = buf.nlh.properties_off; + + /* devices received from udev are always initialized */ + is_initialized = true; + } else { + /* kernel message with header */ + bufpos = strlen(buf.raw) + 1; + if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { + log_debug("invalid message length"); + return NULL; + } + + /* check message header */ + if (strstr(buf.raw, "@/") == NULL) { + log_debug("unrecognized message header"); + return NULL; + } + } + + udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos); + if (!udev_device) { + log_debug("could not create device: %m"); + return NULL; + } + + if (is_initialized) + udev_device_set_is_initialized(udev_device); + + /* skip device, if it does not pass the current filter */ + if (!passes_filter(udev_monitor, udev_device)) { + struct pollfd pfd[1]; + int rc; + + udev_device_unref(udev_device); + + /* if something is queued, get next device */ + pfd[0].fd = udev_monitor->sock; + pfd[0].events = POLLIN; + rc = poll(pfd, 1, 0); + if (rc > 0) + goto retry; + return NULL; + } + + return udev_device; +} + +int udev_monitor_send_device(struct udev_monitor *udev_monitor, + struct udev_monitor *destination, struct udev_device *udev_device) +{ + const char *buf, *val; + ssize_t blen, count; + struct udev_monitor_netlink_header nlh = { + .prefix = "libudev", + .magic = htobe32(UDEV_MONITOR_MAGIC), + .header_size = sizeof nlh, + }; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof nlh }, + }; + struct msghdr smsg = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + struct udev_list_entry *list_entry; + uint64_t tag_bloom_bits; + + blen = udev_device_get_properties_monitor_buf(udev_device, &buf); + if (blen < 32) { + log_debug("device buffer is too small to contain a valid device"); + return -EINVAL; + } + + /* fill in versioned header */ + val = udev_device_get_subsystem(udev_device); + nlh.filter_subsystem_hash = htobe32(util_string_hash32(val)); + + val = udev_device_get_devtype(udev_device); + if (val != NULL) + nlh.filter_devtype_hash = htobe32(util_string_hash32(val)); + + /* add tag bloom filter */ + tag_bloom_bits = 0; + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) + tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); + if (tag_bloom_bits > 0) { + nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32); + nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff); + } + + /* add properties list */ + nlh.properties_off = iov[0].iov_len; + nlh.properties_len = blen; + iov[1].iov_base = (char *)buf; + iov[1].iov_len = blen; + + /* + * Use custom address for target, or the default one. + * + * If we send to a multicast group, we will get + * ECONNREFUSED, which is expected. + */ + if (destination) + smsg.msg_name = &destination->snl; + else + smsg.msg_name = &udev_monitor->snl_destination; + smsg.msg_namelen = sizeof(struct sockaddr_nl); + count = sendmsg(udev_monitor->sock, &smsg, 0); + if (count < 0) { + if (!destination && errno == ECONNREFUSED) { + log_debug("passed device to netlink monitor %p", udev_monitor); + return 0; + } else + return -errno; + } + + log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor); + return count; +} + +/** + * udev_monitor_filter_add_match_subsystem_devtype: + * @udev_monitor: the monitor + * @subsystem: the subsystem value to match the incoming devices against + * @devtype: the devtype value to match the incoming devices against + * + * This filter is efficiently executed inside the kernel, and libudev subscribers + * will usually not be woken up for devices which do not match. + * + * The filter must be installed before the monitor is switched to listening mode. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) +{ + if (udev_monitor == NULL) + return -EINVAL; + if (subsystem == NULL) + return -EINVAL; + if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_monitor_filter_add_match_tag: + * @udev_monitor: the monitor + * @tag: the name of a tag + * + * This filter is efficiently executed inside the kernel, and libudev subscribers + * will usually not be woken up for devices which do not match. + * + * The filter must be installed before the monitor is switched to listening mode. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) +{ + if (udev_monitor == NULL) + return -EINVAL; + if (tag == NULL) + return -EINVAL; + if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_monitor_filter_remove: + * @udev_monitor: monitor + * + * Remove all filters from monitor. + * + * Returns: 0 on success, otherwise a negative error value. + */ +_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) +{ + static struct sock_fprog filter = { 0, NULL }; + + udev_list_cleanup(&udev_monitor->filter_subsystem_list); + return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); +} diff --git a/src/libudev/src/libudev-private.h b/src/libudev/src/libudev-private.h new file mode 100644 index 0000000000..8dfc8d6fe7 --- /dev/null +++ b/src/libudev/src/libudev-private.h @@ -0,0 +1,150 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#ifndef _LIBUDEV_PRIVATE_H_ +#define _LIBUDEV_PRIVATE_H_ + +#include +#include +#include + +#include + +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/strxcpyx.h" +#include "basic/util.h" + +#define READ_END 0 +#define WRITE_END 1 + +/* libudev.c */ +int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]); + +/* libudev-device.c */ +struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen); +struct udev_device *udev_device_new_from_synthetic_event(struct udev *udev, const char *syspath, const char *action); +struct udev_device *udev_device_shallow_clone(struct udev_device *old_device); +struct udev_device *udev_device_clone_with_db(struct udev_device *old_device); +int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src); +mode_t udev_device_get_devnode_mode(struct udev_device *udev_device); +uid_t udev_device_get_devnode_uid(struct udev_device *udev_device); +gid_t udev_device_get_devnode_gid(struct udev_device *udev_device); +int udev_device_rename(struct udev_device *udev_device, const char *new_name); +int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink); +void udev_device_cleanup_devlinks_list(struct udev_device *udev_device); +int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value); +char **udev_device_get_properties_envp(struct udev_device *udev_device); +ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf); +const char *udev_device_get_devpath_old(struct udev_device *udev_device); +const char *udev_device_get_id_filename(struct udev_device *udev_device); +void udev_device_set_is_initialized(struct udev_device *udev_device); +int udev_device_add_tag(struct udev_device *udev_device, const char *tag); +void udev_device_remove_tag(struct udev_device *udev_device, const char *tag); +void udev_device_cleanup_tags_list(struct udev_device *udev_device); +usec_t udev_device_get_usec_initialized(struct udev_device *udev_device); +void udev_device_ensure_usec_initialized(struct udev_device *udev_device, struct udev_device *old_device); +int udev_device_get_devlink_priority(struct udev_device *udev_device); +int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio); +int udev_device_get_watch_handle(struct udev_device *udev_device); +int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); +int udev_device_get_ifindex(struct udev_device *udev_device); +void udev_device_set_info_loaded(struct udev_device *device); +bool udev_device_get_db_persist(struct udev_device *udev_device); +void udev_device_set_db_persist(struct udev_device *udev_device); +void udev_device_read_db(struct udev_device *udev_device); + +/* libudev-device-private.c */ +int udev_device_update_db(struct udev_device *udev_device); +int udev_device_delete_db(struct udev_device *udev_device); +int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add); + +/* libudev-monitor.c - netlink/unix socket communication */ +int udev_monitor_disconnect(struct udev_monitor *udev_monitor); +int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender); +int udev_monitor_send_device(struct udev_monitor *udev_monitor, + struct udev_monitor *destination, struct udev_device *udev_device); +struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd); + +/* libudev-list.c */ +struct udev_list_node { + struct udev_list_node *next, *prev; +}; +struct udev_list { + struct udev *udev; + struct udev_list_node node; + struct udev_list_entry **entries; + unsigned int entries_cur; + unsigned int entries_max; + bool unique; +}; +void udev_list_node_init(struct udev_list_node *list); +int udev_list_node_is_empty(struct udev_list_node *list); +void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list); +void udev_list_node_remove(struct udev_list_node *entry); +#define udev_list_node_foreach(node, list) \ + for (node = (list)->next; \ + node != list; \ + node = (node)->next) +#define udev_list_node_foreach_safe(node, tmp, list) \ + for (node = (list)->next, tmp = (node)->next; \ + node != list; \ + node = tmp, tmp = (tmp)->next) +void udev_list_init(struct udev *udev, struct udev_list *list, bool unique); +void udev_list_cleanup(struct udev_list *list); +struct udev_list_entry *udev_list_get_entry(struct udev_list *list); +struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value); +void udev_list_entry_delete(struct udev_list_entry *entry); +int udev_list_entry_get_num(struct udev_list_entry *list_entry); +void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num); +#define udev_list_entry_foreach_safe(entry, tmp, first) \ + for (entry = first, tmp = udev_list_entry_get_next(entry); \ + entry != NULL; \ + entry = tmp, tmp = udev_list_entry_get_next(tmp)) + +/* libudev-queue.c */ +unsigned long long int udev_get_kernel_seqnum(struct udev *udev); +int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); +ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); +ssize_t udev_queue_skip_devpath(FILE *queue_file); + +/* libudev-queue-private.c */ +struct udev_queue_export *udev_queue_export_new(struct udev *udev); +struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export); +void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); +int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); +int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); + +/* libudev-util.c */ +#define UTIL_PATH_SIZE 1024 +#define UTIL_NAME_SIZE 512 +#define UTIL_LINE_SIZE 16384 +#define UDEV_ALLOWED_CHARS_INPUT "/ $%?," +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); +int util_replace_whitespace(const char *str, char *to, size_t len); +int util_replace_chars(char *str, const char *white); +unsigned int util_string_hash32(const char *key); +uint64_t util_string_bloom64(const char *str); + +/* libudev-util-private.c */ +int util_resolve_subsys_kernel(struct udev *udev, const char *string, char *result, size_t maxsize, int read_value); + +#endif diff --git a/src/libudev/src/libudev-queue.c b/src/libudev/src/libudev-queue.c new file mode 100644 index 0000000000..bc89252096 --- /dev/null +++ b/src/libudev/src/libudev-queue.c @@ -0,0 +1,269 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + Copyright 2009 Alan Jenkins + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev-queue + * @short_description: access to currently active events + * + * This exports the current state of the udev processing queue. + */ + +/** + * udev_queue: + * + * Opaque object representing the current event queue in the udev daemon. + */ +struct udev_queue { + struct udev *udev; + int refcount; + int fd; +}; + +/** + * udev_queue_new: + * @udev: udev library context + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev queue context. + * + * Returns: the udev queue context, or #NULL on error. + **/ +_public_ struct udev_queue *udev_queue_new(struct udev *udev) +{ + struct udev_queue *udev_queue; + + if (udev == NULL) + return NULL; + + udev_queue = new0(struct udev_queue, 1); + if (udev_queue == NULL) + return NULL; + + udev_queue->refcount = 1; + udev_queue->udev = udev; + udev_queue->fd = -1; + return udev_queue; +} + +/** + * udev_queue_ref: + * @udev_queue: udev queue context + * + * Take a reference of a udev queue context. + * + * Returns: the same udev queue context. + **/ +_public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + + udev_queue->refcount++; + return udev_queue; +} + +/** + * udev_queue_unref: + * @udev_queue: udev queue context + * + * Drop a reference of a udev queue context. If the refcount reaches zero, + * the resources of the queue context will be released. + * + * Returns: #NULL + **/ +_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + + udev_queue->refcount--; + if (udev_queue->refcount > 0) + return NULL; + + safe_close(udev_queue->fd); + + free(udev_queue); + return NULL; +} + +/** + * udev_queue_get_udev: + * @udev_queue: udev queue context + * + * Retrieve the udev library context the queue context was created with. + * + * Returns: the udev library context. + **/ +_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + return udev_queue->udev; +} + +/** + * udev_queue_get_kernel_seqnum: + * @udev_queue: udev queue context + * + * This function is deprecated. + * + * Returns: 0. + **/ +_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) +{ + return 0; +} + +/** + * udev_queue_get_udev_seqnum: + * @udev_queue: udev queue context + * + * This function is deprecated. + * + * Returns: 0. + **/ +_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) +{ + return 0; +} + +/** + * udev_queue_get_udev_is_active: + * @udev_queue: udev queue context + * + * Check if udev is active on the system. + * + * Returns: a flag indicating if udev is active. + **/ +_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) +{ + return access("/run/udev/control", F_OK) >= 0; +} + +/** + * udev_queue_get_queue_is_empty: + * @udev_queue: udev queue context + * + * Check if udev is currently processing any events. + * + * Returns: a flag indicating if udev is currently handling events. + **/ +_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) +{ + return access("/run/udev/queue", F_OK) < 0; +} + +/** + * udev_queue_get_seqnum_sequence_is_finished: + * @udev_queue: udev queue context + * @start: first event sequence number + * @end: last event sequence number + * + * This function is deprecated, it just returns the result of + * udev_queue_get_queue_is_empty(). + * + * Returns: a flag indicating if udev is currently handling events. + **/ +_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, + unsigned long long int start, unsigned long long int end) +{ + return udev_queue_get_queue_is_empty(udev_queue); +} + +/** + * udev_queue_get_seqnum_is_finished: + * @udev_queue: udev queue context + * @seqnum: sequence number + * + * This function is deprecated, it just returns the result of + * udev_queue_get_queue_is_empty(). + * + * Returns: a flag indicating if udev is currently handling events. + **/ +_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) +{ + return udev_queue_get_queue_is_empty(udev_queue); +} + +/** + * udev_queue_get_queued_list_entry: + * @udev_queue: udev queue context + * + * This function is deprecated. + * + * Returns: NULL. + **/ +_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) +{ + return NULL; +} + +/** + * udev_queue_get_fd: + * @udev_queue: udev queue context + * + * Returns: a file descriptor to watch for a queue to become empty. + */ +_public_ int udev_queue_get_fd(struct udev_queue *udev_queue) { + int fd; + int r; + + if (udev_queue->fd >= 0) + return udev_queue->fd; + + fd = inotify_init1(IN_CLOEXEC); + if (fd < 0) + return -errno; + + r = inotify_add_watch(fd, "/run/udev" , IN_DELETE); + if (r < 0) { + r = -errno; + close(fd); + return r; + } + + udev_queue->fd = fd; + return fd; +} + +/** + * udev_queue_flush: + * @udev_queue: udev queue context + * + * Returns: the result of clearing the watch for queue changes. + */ +_public_ int udev_queue_flush(struct udev_queue *udev_queue) { + if (udev_queue->fd < 0) + return -EINVAL; + + return flush_fd(udev_queue->fd); +} diff --git a/src/libudev/src/libudev-util.c b/src/libudev/src/libudev-util.c new file mode 100644 index 0000000000..661039ecea --- /dev/null +++ b/src/libudev/src/libudev-util.c @@ -0,0 +1,269 @@ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/MurmurHash2.h" +#include "basic/device-nodes.h" +#include "basic/syslog-util.h" +#include "basic/utf8.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev-util + * @short_description: utils + * + * Utilities useful when dealing with devices and device node names. + */ + +/* handle "[/]" format */ +int util_resolve_subsys_kernel(struct udev *udev, const char *string, + char *result, size_t maxsize, int read_value) +{ + char temp[UTIL_PATH_SIZE]; + char *subsys; + char *sysname; + struct udev_device *dev; + char *attr; + + if (string[0] != '[') + return -1; + + strscpy(temp, sizeof(temp), string); + + subsys = &temp[1]; + + sysname = strchr(subsys, '/'); + if (sysname == NULL) + return -1; + sysname[0] = '\0'; + sysname = &sysname[1]; + + attr = strchr(sysname, ']'); + if (attr == NULL) + return -1; + attr[0] = '\0'; + attr = &attr[1]; + if (attr[0] == '/') + attr = &attr[1]; + if (attr[0] == '\0') + attr = NULL; + + if (read_value && attr == NULL) + return -1; + + dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); + if (dev == NULL) + return -1; + + if (read_value) { + const char *val; + + val = udev_device_get_sysattr_value(dev, attr); + if (val != NULL) + strscpy(result, maxsize, val); + else + result[0] = '\0'; + log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); + } else { + size_t l; + char *s; + + s = result; + l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); + if (attr != NULL) + strpcpyl(&s, l, "/", attr, NULL); + log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); + } + udev_device_unref(dev); + return 0; +} + +int util_log_priority(const char *priority) +{ + char *endptr; + int prio; + + prio = strtoul(priority, &endptr, 10); + if (endptr[0] == '\0' || isspace(endptr[0])) { + if (prio >= 0 && prio <= 7) + return prio; + else + return -ERANGE; + } + + return log_level_from_string(priority); +} + +size_t util_path_encode(const char *src, char *dest, size_t size) +{ + size_t i, j; + + for (i = 0, j = 0; src[i] != '\0'; i++) { + if (src[i] == '/') { + if (j+4 >= size) { + j = 0; + break; + } + memcpy(&dest[j], "\\x2f", 4); + j += 4; + } else if (src[i] == '\\') { + if (j+4 >= size) { + j = 0; + break; + } + memcpy(&dest[j], "\\x5c", 4); + j += 4; + } else { + if (j+1 >= size) { + j = 0; + break; + } + dest[j] = src[i]; + j++; + } + } + dest[j] = '\0'; + return j; +} + +void util_remove_trailing_chars(char *path, char c) +{ + size_t len; + + if (path == NULL) + return; + len = strlen(path); + while (len > 0 && path[len-1] == c) + path[--len] = '\0'; +} + +int util_replace_whitespace(const char *str, char *to, size_t len) +{ + size_t i, j; + + /* strip trailing whitespace */ + len = strnlen(str, len); + while (len && isspace(str[len-1])) + len--; + + /* strip leading whitespace */ + i = 0; + while ((i < len) && isspace(str[i])) + i++; + + j = 0; + while (i < len) { + /* substitute multiple whitespace with a single '_' */ + if (isspace(str[i])) { + while (isspace(str[i])) + i++; + to[j++] = '_'; + } + to[j++] = str[i++]; + } + to[j] = '\0'; + return 0; +} + +/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ +int util_replace_chars(char *str, const char *white) +{ + size_t i = 0; + int replaced = 0; + + while (str[i] != '\0') { + int len; + + if (whitelisted_char_for_devnode(str[i], white)) { + i++; + continue; + } + + /* accept hex encoding */ + if (str[i] == '\\' && str[i+1] == 'x') { + i += 2; + continue; + } + + /* accept valid utf8 */ + len = utf8_encoded_valid_unichar(&str[i]); + if (len > 1) { + i += len; + continue; + } + + /* if space is allowed, replace whitespace with ordinary space */ + if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) { + str[i] = ' '; + i++; + replaced++; + continue; + } + + /* everything else is replaced with '_' */ + str[i] = '_'; + i++; + replaced++; + } + return replaced; +} + +/** + * udev_util_encode_string: + * @str: input string to be encoded + * @str_enc: output string to store the encoded input string + * @len: maximum size of the output string, which may be + * four times as long as the input string + * + * Encode all potentially unsafe characters of a string to the + * corresponding 2 char hex value prefixed by '\x'. + * + * Returns: 0 if the entire string was copied, non-zero otherwise. + **/ +_public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) +{ + return encode_devnode_name(str, str_enc, len); +} + +unsigned int util_string_hash32(const char *str) +{ + return MurmurHash2(str, strlen(str), 0); +} + +/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ +uint64_t util_string_bloom64(const char *str) +{ + uint64_t bits = 0; + unsigned int hash = util_string_hash32(str); + + bits |= 1LLU << (hash & 63); + bits |= 1LLU << ((hash >> 6) & 63); + bits |= 1LLU << ((hash >> 12) & 63); + bits |= 1LLU << ((hash >> 18) & 63); + return bits; +} diff --git a/src/libudev/src/libudev.c b/src/libudev/src/libudev.c new file mode 100644 index 0000000000..c1af43e3ca --- /dev/null +++ b/src/libudev/src/libudev.c @@ -0,0 +1,254 @@ +/*** + This file is part of systemd. + + Copyright 2008-2014 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/string-util.h" + +#include "libudev-private.h" + +/** + * SECTION:libudev + * @short_description: libudev context + * + * The context contains the default values read from the udev config file, + * and is passed to all library operations. + */ + +/** + * udev: + * + * Opaque object representing the library context. + */ +struct udev { + int refcount; + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args); + void *userdata; +}; + +/** + * udev_get_userdata: + * @udev: udev library context + * + * Retrieve stored data pointer from library context. This might be useful + * to access from callbacks. + * + * Returns: stored userdata + **/ +_public_ void *udev_get_userdata(struct udev *udev) { + if (udev == NULL) + return NULL; + return udev->userdata; +} + +/** + * udev_set_userdata: + * @udev: udev library context + * @userdata: data pointer + * + * Store custom @userdata in the library context. + **/ +_public_ void udev_set_userdata(struct udev *udev, void *userdata) { + if (udev == NULL) + return; + udev->userdata = userdata; +} + +/** + * udev_new: + * + * Create udev library context. This reads the udev configuration + * file, and fills in the default values. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev library context. + * + * Returns: a new udev library context + **/ +_public_ struct udev *udev_new(void) { + struct udev *udev; + _cleanup_fclose_ FILE *f = NULL; + + udev = new0(struct udev, 1); + if (udev == NULL) + return NULL; + udev->refcount = 1; + + f = fopen("/etc/udev/udev.conf", "re"); + if (f != NULL) { + char line[UTIL_LINE_SIZE]; + unsigned line_nr = 0; + + while (fgets(line, sizeof(line), f)) { + size_t len; + char *key; + char *val; + + line_nr++; + + /* find key */ + key = line; + while (isspace(key[0])) + key++; + + /* comment or empty line */ + if (key[0] == '#' || key[0] == '\0') + continue; + + /* split key/value */ + val = strchr(key, '='); + if (val == NULL) { + log_debug("/etc/udev/udev.conf:%u: missing assignment, skipping line.", line_nr); + continue; + } + val[0] = '\0'; + val++; + + /* find value */ + while (isspace(val[0])) + val++; + + /* terminate key */ + len = strlen(key); + if (len == 0) + continue; + while (isspace(key[len-1])) + len--; + key[len] = '\0'; + + /* terminate value */ + len = strlen(val); + if (len == 0) + continue; + while (isspace(val[len-1])) + len--; + val[len] = '\0'; + + if (len == 0) + continue; + + /* unquote */ + if (val[0] == '"' || val[0] == '\'') { + if (val[len-1] != val[0]) { + log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr); + continue; + } + val[len-1] = '\0'; + val++; + } + + if (streq(key, "udev_log")) { + int prio; + + prio = util_log_priority(val); + if (prio < 0) + log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val); + else + log_set_max_level(prio); + continue; + } + } + } + + return udev; +} + +/** + * udev_ref: + * @udev: udev library context + * + * Take a reference of the udev library context. + * + * Returns: the passed udev library context + **/ +_public_ struct udev *udev_ref(struct udev *udev) { + if (udev == NULL) + return NULL; + udev->refcount++; + return udev; +} + +/** + * udev_unref: + * @udev: udev library context + * + * Drop a reference of the udev library context. If the refcount + * reaches zero, the resources of the context will be released. + * + * Returns: the passed udev library context if it has still an active reference, or #NULL otherwise. + **/ +_public_ struct udev *udev_unref(struct udev *udev) { + if (udev == NULL) + return NULL; + udev->refcount--; + if (udev->refcount > 0) + return udev; + free(udev); + return NULL; +} + +/** + * udev_set_log_fn: + * @udev: udev library context + * @log_fn: function to be called for log messages + * + * This function is deprecated. + * + **/ +_public_ void udev_set_log_fn(struct udev *udev, + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args)) { + return; +} + +/** + * udev_get_log_priority: + * @udev: udev library context + * + * This function is deprecated. + * + **/ +_public_ int udev_get_log_priority(struct udev *udev) { + return log_get_max_level(); +} + +/** + * udev_set_log_priority: + * @udev: udev library context + * @priority: the new log priority + * + * This function is deprecated. + * + **/ +_public_ void udev_set_log_priority(struct udev *udev, int priority) { + log_set_max_level(priority); +} diff --git a/src/libudev/src/udev.h b/src/libudev/src/udev.h new file mode 100644 index 0000000000..26afe63b55 --- /dev/null +++ b/src/libudev/src/udev.h @@ -0,0 +1,217 @@ +#pragma once + +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include + +#include "basic/label.h" +#include "basic/macro.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "sd-netlink/sd-netlink.h" + +#include "libudev-private.h" + +struct udev_event { + struct udev *udev; + struct udev_device *dev; + struct udev_device *dev_parent; + struct udev_device *dev_db; + char *name; + char *program_result; + mode_t mode; + uid_t uid; + gid_t gid; + struct udev_list seclabel_list; + struct udev_list run_list; + int exec_delay; + usec_t birth_usec; + sd_netlink *rtnl; + unsigned int builtin_run; + unsigned int builtin_ret; + bool inotify_watch; + bool inotify_watch_final; + bool group_set; + bool group_final; + bool owner_set; + bool owner_final; + bool mode_set; + bool mode_final; + bool name_final; + bool devlink_final; + bool run_final; +}; + +struct udev_watch { + struct udev_list_node node; + int handle; + char *name; +}; + +/* udev-rules.c */ +struct udev_rules; +struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); +struct udev_rules *udev_rules_unref(struct udev_rules *rules); +bool udev_rules_check_timestamp(struct udev_rules *rules); +void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list); +int udev_rules_apply_static_dev_perms(struct udev_rules *rules); + +/* udev-event.c */ +struct udev_event *udev_event_new(struct udev_device *dev); +void udev_event_unref(struct udev_event *event); +size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); +int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, + char *result, size_t maxsize, int read_value); +int udev_event_spawn(struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + bool accept_failure, + const char *cmd, char *result, size_t ressize); +void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, + struct udev_rules *rules); +void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec); +int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); + +/* udev-watch.c */ +int udev_watch_init(struct udev *udev); +void udev_watch_restore(struct udev *udev); +void udev_watch_begin(struct udev *udev, struct udev_device *dev); +void udev_watch_end(struct udev *udev, struct udev_device *dev); +struct udev_device *udev_watch_lookup(struct udev *udev, int wd); + +/* udev-node.c */ +void udev_node_add(struct udev_device *dev, bool apply, + mode_t mode, uid_t uid, gid_t gid, + struct udev_list *seclabel_list); +void udev_node_remove(struct udev_device *dev); +void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); + +/* udev-ctrl.c */ +struct udev_ctrl; +struct udev_ctrl *udev_ctrl_new(struct udev *udev); +struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); +int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); +struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); +int udev_ctrl_cleanup(struct udev_ctrl *uctrl); +struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); +int udev_ctrl_get_fd(struct udev_ctrl *uctrl); +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); +struct udev_ctrl_connection; +struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); +struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); +struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); +struct udev_ctrl_msg; +struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); +struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); +const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); + +/* built-in commands */ +enum udev_builtin_cmd { +#ifdef HAVE_BLKID + UDEV_BUILTIN_BLKID, +#endif + UDEV_BUILTIN_BTRFS, + UDEV_BUILTIN_HWDB, + UDEV_BUILTIN_INPUT_ID, + UDEV_BUILTIN_KEYBOARD, +#ifdef HAVE_KMOD + UDEV_BUILTIN_KMOD, +#endif + UDEV_BUILTIN_NET_ID, + UDEV_BUILTIN_NET_LINK, + UDEV_BUILTIN_PATH_ID, + UDEV_BUILTIN_USB_ID, +#ifdef HAVE_ACL + UDEV_BUILTIN_UACCESS, +#endif + UDEV_BUILTIN_MAX +}; +struct udev_builtin { + const char *name; + int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); + const char *help; + int (*init)(struct udev *udev); + void (*exit)(struct udev *udev); + bool (*validate)(struct udev *udev); + bool run_once; +}; +#ifdef HAVE_BLKID +extern const struct udev_builtin udev_builtin_blkid; +#endif +extern const struct udev_builtin udev_builtin_btrfs; +extern const struct udev_builtin udev_builtin_hwdb; +extern const struct udev_builtin udev_builtin_input_id; +extern const struct udev_builtin udev_builtin_keyboard; +#ifdef HAVE_KMOD +extern const struct udev_builtin udev_builtin_kmod; +#endif +extern const struct udev_builtin udev_builtin_net_id; +extern const struct udev_builtin udev_builtin_net_setup_link; +extern const struct udev_builtin udev_builtin_path_id; +extern const struct udev_builtin udev_builtin_usb_id; +extern const struct udev_builtin udev_builtin_uaccess; +void udev_builtin_init(struct udev *udev); +void udev_builtin_exit(struct udev *udev); +enum udev_builtin_cmd udev_builtin_lookup(const char *command); +const char *udev_builtin_name(enum udev_builtin_cmd cmd); +bool udev_builtin_run_once(enum udev_builtin_cmd cmd); +int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); +void udev_builtin_list(struct udev *udev); +bool udev_builtin_validate(struct udev *udev); +int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); +int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias, + const char *filter, bool test); + +/* udevadm commands */ +struct udevadm_cmd { + const char *name; + int (*cmd)(struct udev *udev, int argc, char *argv[]); + const char *help; + int debug; +}; +extern const struct udevadm_cmd udevadm_info; +extern const struct udevadm_cmd udevadm_trigger; +extern const struct udevadm_cmd udevadm_settle; +extern const struct udevadm_cmd udevadm_control; +extern const struct udevadm_cmd udevadm_monitor; +extern const struct udevadm_cmd udevadm_hwdb; +extern const struct udevadm_cmd udevadm_test; +extern const struct udevadm_cmd udevadm_test_builtin; diff --git a/src/libudev/udev_device_get_syspath.xml b/src/libudev/udev_device_get_syspath.xml new file mode 100644 index 0000000000..014f43b21c --- /dev/null +++ b/src/libudev/udev_device_get_syspath.xml @@ -0,0 +1,207 @@ + + +%entities; +]> + + + + + + + udev_device_get_syspath + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_device_get_syspath + 3 + + + + udev_device_get_syspath + udev_device_get_sysname + udev_device_get_sysnum + udev_device_get_devpath + udev_device_get_devnode + udev_device_get_devnum + udev_device_get_devtype + udev_device_get_subsystem + udev_device_get_driver + udev_device_get_udev + udev_device_get_parent + udev_device_get_parent_with_subsystem_devtype + udev_device_get_is_initialized + udev_device_get_action + + Query device properties + + + + + #include <libudev.h> + + + const char *udev_device_get_syspath + struct udev_device *udev_device + + + + const char *udev_device_get_sysname + struct udev_device *udev_device + + + + const char *udev_device_get_sysnum + struct udev_device *udev_device + + + + const char *udev_device_get_devpath + struct udev_device *udev_device + + + + const char *udev_device_get_devnode + struct udev_device *udev_device + + + + dev_t udev_device_get_devnum + struct udev_device *udev_device + + + + const char *udev_device_get_devtype + struct udev_device *udev_device + + + + const char *udev_device_get_subsystem + struct udev_device *udev_device + + + + const char *udev_device_get_driver + struct udev_device *udev_device + + + + struct udev *udev_device_get_udev + struct udev_device *udev_device + + + + struct udev_device *udev_device_get_parent + struct udev_device *udev_device + + + + struct udev_device *udev_device_get_parent_with_subsystem_devtype + struct udev_device *udev_device + const char *subsystem + const char *devtype + + + + int udev_device_get_is_initialized + struct udev_device *udev_device + + + + const char *udev_device_get_action + struct udev_device *udev_device + + + + + + + + + Return Value + + On success, udev_device_get_syspath(), + udev_device_get_sysname(), + udev_device_get_sysnum(), + udev_device_get_devpath(), + udev_device_get_devnode(), + udev_device_get_devtype(), + udev_device_get_subsystem(), + udev_device_get_driver() and + udev_device_get_action() return a pointer + to a constant string that describes the requested property. The + lifetime of this string is bound to the device it was requested + on. On failure, each function may return + NULL. + + On success, udev_device_get_devnum() + returns the device type of the passed device. On failure, a + device type with minor and major number set to + 0 is returned. + + udev_device_get_udev() always returns + a valid pointer to the udev context that this device belongs + to. + + On success, udev_device_get_parent() + and + udev_device_get_parent_with_subsystem_devtype() + return a pointer to the parent device. No additional reference + to this device is acquired, but the child device owns a reference + to such a parent device. On failure, NULL + is returned. + + On success, udev_device_get_is_initialized() returns either 1 or + 0, depending on whether the passed device has already been initialized by udev or not. On + failure, a negative error code is returned. Note that devices for which no udev rules are defined are never + reported initialized. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_device_has_tag3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_device_has_tag.xml b/src/libudev/udev_device_has_tag.xml new file mode 100644 index 0000000000..480257343c --- /dev/null +++ b/src/libudev/udev_device_has_tag.xml @@ -0,0 +1,163 @@ + + +%entities; +]> + + + + + + + udev_device_has_tag + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_device_has_tag + 3 + + + + udev_device_has_tag + udev_device_get_devlinks_list_entry + udev_device_get_properties_list_entry + udev_device_get_tags_list_entry + udev_device_get_sysattr_list_entry + udev_device_get_property_value + udev_device_get_sysattr_value + udev_device_set_sysattr_value + + Retrieve or set device attributes + + + + + #include <libudev.h> + + + struct udev_list_entry *udev_device_get_devlinks_list_entry + struct udev_device *udev_device + + + + struct udev_list_entry *udev_device_get_properties_list_entry + struct udev_device *udev_device + + + + struct udev_list_entry *udev_device_get_tags_list_entry + struct udev_device *udev_device + + + + struct udev_list_entry *udev_device_get_sysattr_list_entry + struct udev_device *udev_device + + + + const char *udev_device_get_property_value + struct udev_device *udev_device + const char *key + + + + int udev_device_has_tag + struct udev_device *udev_device + const char *tag + + + + const char *udev_device_get_sysattr_value + struct udev_device *udev_device + const char *sysattr + + + + int udev_device_set_sysattr_value + struct udev_device *udev_device + const char *sysattr + const char *value + + + + + + + + + Return Value + + On success, + udev_device_get_devlinks_list_entry(), + udev_device_get_properties_list_entry(), + udev_device_get_tags_list_entry() and + udev_device_get_sysattr_list_entry() return + a pointer to the first entry of the retrieved list. If that list + is empty, or if an error occurred, NULL is + returned. + + On success, + udev_device_get_property_value() and + udev_device_get_sysattr_value() return a + pointer to a constant string of the requested value. On error, + NULL is returned. + + On success, + udev_device_set_sysattr_value() returns + an integer greater than, or equal to, 0. + On failure, a negative error code is returned. + + On success, udev_device_has_tag() + returns 1 or 0, + depending on whether the device has the given tag or not. + On failure, a negative error code is returned. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_device_get_syspath3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_device_new_from_syspath.xml b/src/libudev/udev_device_new_from_syspath.xml new file mode 100644 index 0000000000..0bb71c8e91 --- /dev/null +++ b/src/libudev/udev_device_new_from_syspath.xml @@ -0,0 +1,214 @@ + + +%entities; +]> + + + + + + + udev_device_new_from_syspath + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_device_new_from_syspath + 3 + + + + udev_device_new_from_syspath + udev_device_new_from_devnum + udev_device_new_from_subsystem_sysname + udev_device_new_from_device_id + udev_device_new_from_environment + udev_device_ref + udev_device_unref + + Create, acquire and release a udev device object + + + + + #include <libudev.h> + + + struct udev_device *udev_device_new_from_syspath + struct udev *udev + const char *syspath + + + + struct udev_device *udev_device_new_from_devnum + struct udev *udev + char type + dev_t devnum + + + + struct udev_device *udev_device_new_from_subsystem_sysname + struct udev *udev + const char *subsystem + const char *sysname + + + + struct udev_device *udev_device_new_from_device_id + struct udev *udev + const char *id + + + + struct udev_device *udev_device_new_from_environment + struct udev *udev + + + + struct udev_device *udev_device_ref + struct udev_device *udev_device + + + + struct udev_device *udev_device_unref + struct udev_device *udev_device + + + + + + + Description + + udev_device_new_from_syspath, + udev_device_new_from_devnum, + udev_device_new_from_subsystem_sysname, + udev_device_new_from_device_id, and + udev_device_new_from_environment + allocate a new udev device object and returns a pointer to it. This + object is opaque and must not be accessed by the caller via different + means than functions provided by libudev. Initially, the reference count + of the device is 1. You can acquire further references, and drop + gained references via udev_device_ref() and + udev_device_unref(). Once the reference count hits 0, + the device object is destroyed and freed. + + udev_device_new_from_syspath, + udev_device_new_from_devnum, + udev_device_new_from_subsystem_sysname, and + udev_device_new_from_device_id + create the device object based on information found in + /sys, annotated with properties from the udev-internal + device database. A syspath is any subdirectory of /sys, + with the restriction that a subdirectory of /sys/devices + (or a symlink to one) represents a real device and as such must contain + a uevent file. udev_device_new_from_devnum + takes a device type, which can be b for block devices or + c for character devices, as well as a devnum (see + makedev3). + udev_device_new_from_subsystem_sysname looks up devices based + on the provided subsystem and sysname + (see udev_device_get_subsystem3 + and + udev_device_get_sysname3) + and udev_device_new_from_device_id looks up devices based on the provided + device ID, which is a special string in one of the following four forms: + + Device ID strings + + + + + + Example + Explanation + + + b8:2 + block device major:minor + + c128:1 + char device major:minor + + n3 + network device ifindex + + +sound:card29 + kernel driver core subsystem:device name + + +
+
+ + udev_device_new_from_environment + creates a device from the current environment (see + environ7). + Each key-value pair is interpreted in the same way as if it was + received in an uevent (see + udev_monitor_receive_device3). + The keys DEVPATH, SUBSYSTEM, + ACTION, and SEQNUM are mandatory. + +
+ + + Return Value + + On success, udev_device_new_from_syspath(), + udev_device_new_from_devnum(), + udev_device_new_from_subsystem_sysname(), + udev_device_new_from_device_id() and + udev_device_new_from_environment() return a + pointer to the allocated udev device. On failure, + NULL is returned, + and errno is set appropriately. + udev_device_ref() returns the argument + that it was passed, unmodified. + udev_device_unref() always returns + NULL. + + + + See Also + + + udev_new3, + udev_device_get_syspath3, + udev_device_has_tag3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + +
diff --git a/src/libudev/udev_enumerate_add_match_subsystem.xml b/src/libudev/udev_enumerate_add_match_subsystem.xml new file mode 100644 index 0000000000..5acce00bb0 --- /dev/null +++ b/src/libudev/udev_enumerate_add_match_subsystem.xml @@ -0,0 +1,163 @@ + + +%entities; +]> + + + + + + + udev_enumerate_add_match_subsystem + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_enumerate_add_match_subsystem + 3 + + + + udev_enumerate_add_match_subsystem + udev_enumerate_add_nomatch_subsystem + udev_enumerate_add_match_sysattr + udev_enumerate_add_nomatch_sysattr + udev_enumerate_add_match_property + udev_enumerate_add_match_sysname + udev_enumerate_add_match_tag + udev_enumerate_add_match_parent + udev_enumerate_add_match_is_initialized + + Modify filters + + + + + #include <libudev.h> + + + int udev_enumerate_add_match_subsystem + struct udev_enumerate *udev_enumerate + const char *subsystem + + + + int udev_enumerate_add_nomatch_subsystem + struct udev_enumerate *udev_enumerate + const char *subsystem + + + + int udev_enumerate_add_match_sysattr + struct udev_enumerate *udev_enumerate + const char *sysattr + const char *value + + + + int udev_enumerate_add_nomatch_sysattr + struct udev_enumerate *udev_enumerate + const char *sysattr + const char *value + + + + int udev_enumerate_add_match_property + struct udev_enumerate *udev_enumerate + const char *property + const char *value + + + + int udev_enumerate_add_match_sysname + struct udev_enumerate *udev_enumerate + const char *sysname + + + + int udev_enumerate_add_match_tag + struct udev_enumerate *udev_enumerate + const char *tag + + + + int udev_enumerate_add_match_parent + struct udev_enumerate *udev_enumerate + struct udev_device *parent + + + + int udev_enumerate_add_match_is_initialized + struct udev_enumerate *udev_enumerate + + + + + + + + + Return Value + + On success, + udev_enumerate_add_match_subsystem, + udev_enumerate_add_nomatch_subsystem, + udev_enumerate_add_match_sysattr, + udev_enumerate_add_nomatch_sysattr, + udev_enumerate_add_match_property, + udev_enumerate_add_match_sysname, + udev_enumerate_add_match_tag, + udev_enumerate_add_match_parent and + udev_enumerate_add_match_is_initialized + return an integer greater than, or equal to, + 0. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_enumerate_scan_devices3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_enumerate_new.xml b/src/libudev/udev_enumerate_new.xml new file mode 100644 index 0000000000..b5856c5577 --- /dev/null +++ b/src/libudev/udev_enumerate_new.xml @@ -0,0 +1,111 @@ + + +%entities; +]> + + + + + + + udev_enumerate_new + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_enumerate_new + 3 + + + + udev_enumerate_new + udev_enumerate_ref + udev_enumerate_unref + + Create, acquire and release a udev enumerate object + + + + + #include <libudev.h> + + + struct udev_enumerate *udev_enumerate_new + struct udev *udev + + + + struct udev_enumerate *udev_enumerate_ref + struct udev_enumerate *udev_enumerate + + + + struct udev_enumerate *udev_enumerate_unref + struct udev_enumerate *udev_enumerate + + + + + + + + + Return Value + + On success, udev_enumerate_new() returns a + pointer to the allocated udev monitor. On failure, + NULL is returned. + udev_enumerate_ref() returns the argument + that it was passed, unmodified. + udev_enumerate_unref() always returns + NULL. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_add_match_subsystem3, + udev_enumerate_scan_devices3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_enumerate_scan_devices.xml b/src/libudev/udev_enumerate_scan_devices.xml new file mode 100644 index 0000000000..e0b6bfba32 --- /dev/null +++ b/src/libudev/udev_enumerate_scan_devices.xml @@ -0,0 +1,133 @@ + + +%entities; +]> + + + + + + + udev_enumerate_scan_devices + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_enumerate_scan_devices + 3 + + + + udev_enumerate_scan_devices + udev_enumerate_scan_subsystems + udev_enumerate_get_list_entry + udev_enumerate_add_syspath + udev_enumerate_get_udev + + Query or modify a udev enumerate object + + + + + #include <libudev.h> + + + int udev_enumerate_scan_devices + struct udev_enumerate *udev_enumerate + + + + int udev_enumerate_scan_subsystems + struct udev_enumerate *udev_enumerate + + + + struct udev_list_entry *udev_enumerate_get_list_entry + struct udev_enumerate *udev_enumerate + + + + int udev_enumerate_add_syspath + struct udev_enumerate *udev_enumerate + const char *syspath + + + + struct udev *udev_enumerate_get_udev + struct udev_enumerate *udev_enumerate + + + + + + + + + Return Value + + On success, + udev_enumerate_scan_devices(), + udev_enumerate_scan_subsystems() and + udev_enumerate_add_syspath() + return an integer greater than, or equal to, + 0. + + On success, + udev_enumerate_get_list_entry() + returns a pointer to the first entry in the list of found + devices. If the list is empty, or on failure, + NULL is returned. + + udev_enumerate_get_udev() always + returns a pointer to the udev context that this enumerated + object is associated with. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_enumerate_add_match_subsystem3, + udev_monitor_new_from_netlink3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_list_entry.xml b/src/libudev/udev_list_entry.xml new file mode 100644 index 0000000000..a1b531d52a --- /dev/null +++ b/src/libudev/udev_list_entry.xml @@ -0,0 +1,123 @@ + + +%entities; +]> + + + + + + + udev_list_entry + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_list_entry + 3 + + + + udev_list_entry + udev_list_entry_get_next + udev_list_entry_get_by_name + udev_list_entry_get_name + udev_list_entry_get_value + + Iterate and access udev lists + + + + + #include <libudev.h> + + + struct udev_list_entry *udev_list_entry_get_next + struct udev_list_entry *list_entry + + + + struct udev_list_entry *udev_list_entry_get_by_name + struct udev_list_entry *list_entry + const char *name + + + + const char *udev_list_entry_get_name + struct udev_list_entry *list_entry + + + + const char *udev_list_entry_get_value + struct udev_list_entry *list_entry + + + + + + + + + Return Value + + On success, + udev_list_entry_get_next() and + udev_list_entry_get_by_name() return + a pointer to the requested list entry. If no such entry can + be found, or on failure, NULL is + returned. + + On success, + udev_list_entry_get_name() and + udev_list_entry_get_value() return a + pointer to a constant string representing the requested value. + The string is bound to the lifetime of the list entry itself. + On failure, NULL is returned. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + systemd1, + + + + diff --git a/src/libudev/udev_monitor_filter_update.xml b/src/libudev/udev_monitor_filter_update.xml new file mode 100644 index 0000000000..f129595618 --- /dev/null +++ b/src/libudev/udev_monitor_filter_update.xml @@ -0,0 +1,122 @@ + + +%entities; +]> + + + + + + + udev_monitor_filter_update + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_monitor_filter_update + 3 + + + + udev_monitor_filter_update + udev_monitor_filter_remove + udev_monitor_filter_add_match_subsystem_devtype + udev_monitor_filter_add_match_tag + + Modify filters + + + + + #include <libudev.h> + + + int udev_monitor_filter_update + struct udev_monitor *udev_monitor + + + + int udev_monitor_filter_remove + struct udev_monitor *udev_monitor + + + + int udev_monitor_filter_add_match_subsystem_devtype + struct udev_monitor *udev_monitor + const char *subsystem + const char *devtype + + + + int udev_monitor_filter_add_match_tag + struct udev_monitor *udev_monitor + const char *tag + + + + + + + + + Return Value + + On success, + udev_monitor_filter_update(), + udev_monitor_filter_remove(), + udev_monitor_filter_add_match_subsystem_devtype() + and + udev_monitor_filter_add_match_tag() + return an integer greater than, or equal to, + 0. On failure, a negative error code is + returned. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_monitor_receive_device3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_monitor_new_from_netlink.xml b/src/libudev/udev_monitor_new_from_netlink.xml new file mode 100644 index 0000000000..d73a4acaec --- /dev/null +++ b/src/libudev/udev_monitor_new_from_netlink.xml @@ -0,0 +1,113 @@ + + +%entities; +]> + + + + + + + udev_monitor_new_from_netlink + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_monitor_new_from_netlink + 3 + + + + udev_monitor_new_from_netlink + udev_monitor_ref + udev_monitor_unref + + Create, acquire and release a udev monitor object + + + + + #include <libudev.h> + + + struct udev_monitor *udev_monitor_new_from_netlink + struct udev *udev + const char *name + + + + struct udev_monitor *udev_monitor_ref + struct udev_monitor *udev_monitor + + + + struct udev_monitor *udev_monitor_unref + struct udev_monitor *udev_monitor + + + + + + + + + Return Value + + On success, + udev_monitor_new_from_netlink() returns a + pointer to the allocated udev monitor. On failure, + NULL is returned. + udev_monitor_ref() returns the argument + that it was passed, unmodified. + udev_monitor_unref() always returns + NULL. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_monitor_filter_update3, + udev_monitor_receive_device3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_monitor_receive_device.xml b/src/libudev/udev_monitor_receive_device.xml new file mode 100644 index 0000000000..7e842f88df --- /dev/null +++ b/src/libudev/udev_monitor_receive_device.xml @@ -0,0 +1,137 @@ + + +%entities; +]> + + + + + + + udev_monitor_receive_device + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_monitor_receive_device + 3 + + + + udev_monitor_receive_device + udev_monitor_enable_receiving + udev_monitor_set_receive_buffer_size + udev_monitor_get_fd + udev_monitor_get_udev + + Query and modify device monitor + + + + + #include <libudev.h> + + + struct udev_device *udev_monitor_receive_device + struct udev_monitor *udev_monitor + + + + int udev_monitor_enable_receiving + struct udev_monitor *udev_monitor + + + + int udev_monitor_set_receive_buffer_size + struct udev_monitor *udev_monitor + int size + + + + int udev_monitor_get_fd + struct udev_monitor *udev_monitor + + + + struct udev *udev_monitor_get_udev + struct udev_monitor *udev_monitor + + + + + + + + + Return Value + + On success, + udev_monitor_receive_device() returns a + pointer to a newly referenced device that was received via the + monitor. The caller is responsible to drop this reference when + done. On failure, NULL is returned. + + On success, + udev_monitor_enable_receiving() and + udev_monitor_set_receive_buffer_size() + return an integer greater than, or equal to, + 0. On failure, a negative error code is + returned. + + On success, udev_monitor_get_fd() + returns the file descriptor used by this monitor. On failure, + a negative error code is returned. + + udev_monitor_get_udev() always returns + a pointer to the udev context that this monitor is associated + with. + + + + See Also + + + udev_new3, + udev_device_new_from_syspath3, + udev_enumerate_new3, + udev_monitor_new_from_netlink3, + udev_monitor_filter_update3, + udev_list_entry3, + systemd1, + + + + diff --git a/src/libudev/udev_new.xml b/src/libudev/udev_new.xml new file mode 100644 index 0000000000..587835a3ca --- /dev/null +++ b/src/libudev/udev_new.xml @@ -0,0 +1,110 @@ + + +%entities; +]> + + + + + + + udev_new + systemd + + + + Developer + David + Herrmann + dh.herrmann@gmail.com + + + + + + udev_new + 3 + + + + udev_new + udev_ref + udev_unref + + Create, acquire and release a udev context object + + + + + #include <libudev.h> + + + struct udev *udev_new + void + + + + struct udev *udev_ref + struct udev *udev + + + + struct udev *udev_unref + struct udev *udev + + + + + + + Description + + udev_new() allocates a new udev context + object and returns a pointer to it. This object is opaque and must + not be accessed by the caller via different means than functions + provided by libudev. Initially, the reference count of the context + is 1. You can acquire further references, and drop gained references + via udev_ref() and + udev_unref(). Once the reference count hits 0, + the context object is destroyed and freed. + + + + Return Value + + On success, udev_new() returns a pointer + to the allocated udev context. On failure, NULL + is returned. udev_ref() returns the argument + that it was passed, unmodified. udev_unref() + always returns NULL. + + + + See Also + + + systemd1, + + + + diff --git a/src/locale/.gitignore b/src/locale/.gitignore deleted file mode 100644 index b1e0ba755e..0000000000 --- a/src/locale/.gitignore +++ /dev/null @@ -1 +0,0 @@ -org.freedesktop.locale1.policy diff --git a/src/locale/Makefile b/src/locale/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/locale/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/locale/kbd-model-map b/src/locale/kbd-model-map deleted file mode 100644 index 8fa984f83b..0000000000 --- a/src/locale/kbd-model-map +++ /dev/null @@ -1,68 +0,0 @@ -# Generated from system-config-keyboard's model list -# consolelayout xlayout xmodel xvariant xoptions -sg ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp -nl nl pc105 - terminate:ctrl_alt_bksp -mk-utf mk,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -trq tr pc105 - terminate:ctrl_alt_bksp -uk gb pc105 - terminate:ctrl_alt_bksp -is-latin1 is pc105 - terminate:ctrl_alt_bksp -de de pc105 - terminate:ctrl_alt_bksp -la-latin1 latam pc105 - terminate:ctrl_alt_bksp -us us pc105+inet - terminate:ctrl_alt_bksp -ko kr pc105 - terminate:ctrl_alt_bksp -ro-std ro pc105 std terminate:ctrl_alt_bksp -de-latin1 de pc105 - terminate:ctrl_alt_bksp -slovene si pc105 - terminate:ctrl_alt_bksp -hu101 hu pc105 qwerty terminate:ctrl_alt_bksp -jp106 jp jp106 - terminate:ctrl_alt_bksp -croat hr pc105 - terminate:ctrl_alt_bksp -it2 it pc105 - terminate:ctrl_alt_bksp -hu hu pc105 - terminate:ctrl_alt_bksp -sr-latin rs pc105 latin terminate:ctrl_alt_bksp -fi fi pc105 - terminate:ctrl_alt_bksp -fr_CH ch pc105 fr terminate:ctrl_alt_bksp -dk-latin1 dk pc105 - terminate:ctrl_alt_bksp -fr fr pc105 - terminate:ctrl_alt_bksp -it it pc105 - terminate:ctrl_alt_bksp -ua-utf ua,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -fr-latin1 fr pc105 - terminate:ctrl_alt_bksp -sg-latin1 ch pc105 de_nodeadkeys terminate:ctrl_alt_bksp -be-latin1 be pc105 - terminate:ctrl_alt_bksp -dk dk pc105 - terminate:ctrl_alt_bksp -fr-pc fr pc105 - terminate:ctrl_alt_bksp -bg_pho-utf8 bg,us pc105 ,phonetic terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -it-ibm it pc105 - terminate:ctrl_alt_bksp -cz-us-qwertz cz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -br-abnt2 br abnt2 - terminate:ctrl_alt_bksp -ro ro pc105 - terminate:ctrl_alt_bksp -us-acentos us pc105 intl terminate:ctrl_alt_bksp -pt-latin1 pt pc105 - terminate:ctrl_alt_bksp -ro-std-cedilla ro pc105 std_cedilla terminate:ctrl_alt_bksp -tj_alt-UTF8 tj pc105 - terminate:ctrl_alt_bksp -de-latin1-nodeadkeys de pc105 nodeadkeys terminate:ctrl_alt_bksp -no no pc105 - terminate:ctrl_alt_bksp -bg_bds-utf8 bg,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -dvorak us pc105 dvorak terminate:ctrl_alt_bksp -dvorak us pc105 dvorak-alt-intl terminate:ctrl_alt_bksp -ru ru,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -cz-lat2 cz pc105 qwerty terminate:ctrl_alt_bksp -pl2 pl pc105 - terminate:ctrl_alt_bksp -es es pc105 - terminate:ctrl_alt_bksp -ro-cedilla ro pc105 cedilla terminate:ctrl_alt_bksp -ie ie pc105 - terminate:ctrl_alt_bksp -et ee pc105 - terminate:ctrl_alt_bksp -sk-qwerty sk pc105 - terminate:ctrl_alt_bksp,qwerty -sk-qwertz sk pc105 - terminate:ctrl_alt_bksp -fr-latin9 fr pc105 latin9 terminate:ctrl_alt_bksp -fr_CH-latin1 ch pc105 fr terminate:ctrl_alt_bksp -cf ca pc105 - terminate:ctrl_alt_bksp -sv-latin1 se pc105 - terminate:ctrl_alt_bksp -sr-cy rs pc105 - terminate:ctrl_alt_bksp -gr gr,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -by by,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -il il pc105 - terminate:ctrl_alt_bksp -kazakh kz,us pc105 - terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll -lt.baltic lt pc105 - terminate:ctrl_alt_bksp -lt.l4 lt pc105 - terminate:ctrl_alt_bksp -lt lt pc105 - terminate:ctrl_alt_bksp -khmer kh,us pc105 - terminate:ctrl_alt_bksp diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c deleted file mode 100644 index a6bcd1ad54..0000000000 --- a/src/locale/keymap-util.c +++ /dev/null @@ -1,724 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "def.h" -#include "env-util.h" -#include "fd-util.h" -#include "fileio-label.h" -#include "fileio.h" -#include "keymap-util.h" -#include "locale-util.h" -#include "macro.h" -#include "mkdir.h" -#include "string-util.h" -#include "strv.h" - -static bool startswith_comma(const char *s, const char *prefix) { - s = startswith(s, prefix); - if (!s) - return false; - - return *s == ',' || *s == '\0'; -} - -static const char* strnulldash(const char *s) { - return isempty(s) || streq(s, "-") ? NULL : s; -} - -static const char* systemd_kbd_model_map(void) { - const char* s; - - s = getenv("SYSTEMD_KBD_MODEL_MAP"); - if (s) - return s; - - return SYSTEMD_KBD_MODEL_MAP; -} - -static const char* systemd_language_fallback_map(void) { - const char* s; - - s = getenv("SYSTEMD_LANGUAGE_FALLBACK_MAP"); - if (s) - return s; - - return SYSTEMD_LANGUAGE_FALLBACK_MAP; -} - -static void context_free_x11(Context *c) { - c->x11_layout = mfree(c->x11_layout); - c->x11_options = mfree(c->x11_options); - c->x11_model = mfree(c->x11_model); - c->x11_variant = mfree(c->x11_variant); -} - -static void context_free_vconsole(Context *c) { - c->vc_keymap = mfree(c->vc_keymap); - c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); -} - -static void context_free_locale(Context *c) { - int p; - - for (p = 0; p < _VARIABLE_LC_MAX; p++) - c->locale[p] = mfree(c->locale[p]); -} - -void context_free(Context *c) { - context_free_locale(c); - context_free_x11(c); - context_free_vconsole(c); -}; - -void locale_simplify(Context *c) { - int p; - - for (p = VARIABLE_LANG+1; p < _VARIABLE_LC_MAX; p++) - if (isempty(c->locale[p]) || streq_ptr(c->locale[VARIABLE_LANG], c->locale[p])) - c->locale[p] = mfree(c->locale[p]); -} - -static int locale_read_data(Context *c) { - int r; - - context_free_locale(c); - - r = parse_env_file("/etc/locale.conf", NEWLINE, - "LANG", &c->locale[VARIABLE_LANG], - "LANGUAGE", &c->locale[VARIABLE_LANGUAGE], - "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], - "LC_NUMERIC", &c->locale[VARIABLE_LC_NUMERIC], - "LC_TIME", &c->locale[VARIABLE_LC_TIME], - "LC_COLLATE", &c->locale[VARIABLE_LC_COLLATE], - "LC_MONETARY", &c->locale[VARIABLE_LC_MONETARY], - "LC_MESSAGES", &c->locale[VARIABLE_LC_MESSAGES], - "LC_PAPER", &c->locale[VARIABLE_LC_PAPER], - "LC_NAME", &c->locale[VARIABLE_LC_NAME], - "LC_ADDRESS", &c->locale[VARIABLE_LC_ADDRESS], - "LC_TELEPHONE", &c->locale[VARIABLE_LC_TELEPHONE], - "LC_MEASUREMENT", &c->locale[VARIABLE_LC_MEASUREMENT], - "LC_IDENTIFICATION", &c->locale[VARIABLE_LC_IDENTIFICATION], - NULL); - - if (r == -ENOENT) { - int p; - - /* Fill in what we got passed from systemd. */ - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - r = free_and_strdup(&c->locale[p], empty_to_null(getenv(name))); - if (r < 0) - return r; - } - - r = 0; - } - - locale_simplify(c); - return r; -} - -static int vconsole_read_data(Context *c) { - int r; - - context_free_vconsole(c); - - r = parse_env_file("/etc/vconsole.conf", NEWLINE, - "KEYMAP", &c->vc_keymap, - "KEYMAP_TOGGLE", &c->vc_keymap_toggle, - NULL); - - if (r < 0 && r != -ENOENT) - return r; - - return 0; -} - -static int x11_read_data(Context *c) { - _cleanup_fclose_ FILE *f; - char line[LINE_MAX]; - bool in_section = false; - int r; - - context_free_x11(c); - - f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - while (fgets(line, sizeof(line), f)) { - char *l; - - char_array_0(line); - l = strstrip(line); - - if (l[0] == 0 || l[0] == '#') - continue; - - if (in_section && first_word(l, "Option")) { - _cleanup_strv_free_ char **a = NULL; - - r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); - if (r < 0) - return r; - - if (strv_length(a) == 3) { - char **p = NULL; - - if (streq(a[1], "XkbLayout")) - p = &c->x11_layout; - else if (streq(a[1], "XkbModel")) - p = &c->x11_model; - else if (streq(a[1], "XkbVariant")) - p = &c->x11_variant; - else if (streq(a[1], "XkbOptions")) - p = &c->x11_options; - - if (p) { - free(*p); - *p = a[2]; - a[2] = NULL; - } - } - - } else if (!in_section && first_word(l, "Section")) { - _cleanup_strv_free_ char **a = NULL; - - r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); - if (r < 0) - return -ENOMEM; - - if (strv_length(a) == 2 && streq(a[1], "InputClass")) - in_section = true; - - } else if (in_section && first_word(l, "EndSection")) - in_section = false; - } - - return 0; -} - -int context_read_data(Context *c) { - int r, q, p; - - r = locale_read_data(c); - q = vconsole_read_data(c); - p = x11_read_data(c); - - return r < 0 ? r : q < 0 ? q : p; -} - -int locale_write_data(Context *c, char ***settings) { - int r, p; - _cleanup_strv_free_ char **l = NULL; - - /* Set values will be returned as strv in *settings on success. */ - - r = load_env_file(NULL, "/etc/locale.conf", NULL, &l); - if (r < 0 && r != -ENOENT) - return r; - - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - _cleanup_free_ char *t = NULL; - char **u; - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - if (isempty(c->locale[p])) { - l = strv_env_unset(l, name); - continue; - } - - if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) - return -ENOMEM; - - u = strv_env_set(l, t); - if (!u) - return -ENOMEM; - - strv_free(l); - l = u; - } - - if (strv_isempty(l)) { - if (unlink("/etc/locale.conf") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - - r = write_env_file_label("/etc/locale.conf", l); - if (r < 0) - return r; - - *settings = l; - l = NULL; - return 0; -} - -int vconsole_write_data(Context *c) { - int r; - _cleanup_strv_free_ char **l = NULL; - - r = load_env_file(NULL, "/etc/vconsole.conf", NULL, &l); - if (r < 0 && r != -ENOENT) - return r; - - if (isempty(c->vc_keymap)) - l = strv_env_unset(l, "KEYMAP"); - else { - _cleanup_free_ char *s = NULL; - char **u; - - s = strappend("KEYMAP=", c->vc_keymap); - if (!s) - return -ENOMEM; - - u = strv_env_set(l, s); - if (!u) - return -ENOMEM; - - strv_free(l); - l = u; - } - - if (isempty(c->vc_keymap_toggle)) - l = strv_env_unset(l, "KEYMAP_TOGGLE"); - else { - _cleanup_free_ char *s = NULL; - char **u; - - s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle); - if (!s) - return -ENOMEM; - - u = strv_env_set(l, s); - if (!u) - return -ENOMEM; - - strv_free(l); - l = u; - } - - if (strv_isempty(l)) { - if (unlink("/etc/vconsole.conf") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - - return write_env_file_label("/etc/vconsole.conf", l); -} - -int x11_write_data(Context *c) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *temp_path = NULL; - int r; - - if (isempty(c->x11_layout) && - isempty(c->x11_model) && - isempty(c->x11_variant) && - isempty(c->x11_options)) { - - if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) - return errno == ENOENT ? 0 : -errno; - - return 0; - } - - mkdir_p_label("/etc/X11/xorg.conf.d", 0755); - - r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); - if (r < 0) - return r; - - fchmod(fileno(f), 0644); - - fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" - "# manually too freely.\n" - "Section \"InputClass\"\n" - " Identifier \"system-keyboard\"\n" - " MatchIsKeyboard \"on\"\n", f); - - if (!isempty(c->x11_layout)) - fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); - - if (!isempty(c->x11_model)) - fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); - - if (!isempty(c->x11_variant)) - fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); - - if (!isempty(c->x11_options)) - fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); - - fputs("EndSection\n", f); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink("/etc/X11/xorg.conf.d/00-keyboard.conf"); - - if (temp_path) - (void) unlink(temp_path); - - return r; -} - -static int read_next_mapping(const char* filename, - unsigned min_fields, unsigned max_fields, - FILE *f, unsigned *n, char ***a) { - assert(f); - assert(n); - assert(a); - - for (;;) { - char line[LINE_MAX]; - char *l, **b; - int r; - size_t length; - - errno = 0; - if (!fgets(line, sizeof(line), f)) { - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - return 0; - } - - (*n)++; - - l = strstrip(line); - if (l[0] == 0 || l[0] == '#') - continue; - - r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES); - if (r < 0) - return r; - - length = strv_length(b); - if (length < min_fields || length > max_fields) { - log_error("Invalid line %s:%u, ignoring.", filename, *n); - strv_free(b); - continue; - - } - - *a = b; - return 1; - } -} - -int vconsole_convert_to_x11(Context *c) { - const char *map; - int modified = -1; - - map = systemd_kbd_model_map(); - - if (isempty(c->vc_keymap)) { - modified = - !isempty(c->x11_layout) || - !isempty(c->x11_model) || - !isempty(c->x11_variant) || - !isempty(c->x11_options); - - context_free_x11(c); - } else { - _cleanup_fclose_ FILE *f = NULL; - unsigned n = 0; - - f = fopen(map, "re"); - if (!f) - return -errno; - - for (;;) { - _cleanup_strv_free_ char **a = NULL; - int r; - - r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); - if (r < 0) - return r; - if (r == 0) - break; - - if (!streq(c->vc_keymap, a[0])) - continue; - - if (!streq_ptr(c->x11_layout, strnulldash(a[1])) || - !streq_ptr(c->x11_model, strnulldash(a[2])) || - !streq_ptr(c->x11_variant, strnulldash(a[3])) || - !streq_ptr(c->x11_options, strnulldash(a[4]))) { - - if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 || - free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 || - free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 || - free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0) - return -ENOMEM; - - modified = true; - } - - break; - } - } - - if (modified > 0) - log_info("Changing X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", - strempty(c->x11_layout), - strempty(c->x11_model), - strempty(c->x11_variant), - strempty(c->x11_options)); - else if (modified < 0) - log_notice("X11 keyboard layout was not modified: no conversion found for \"%s\".", - c->vc_keymap); - else - log_debug("X11 keyboard layout did not need to be modified."); - - return modified > 0; -} - -int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap) { - const char *dir; - _cleanup_free_ char *n; - - if (x11_variant) - n = strjoin(x11_layout, "-", x11_variant, NULL); - else - n = strdup(x11_layout); - if (!n) - return -ENOMEM; - - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { - _cleanup_free_ char *p = NULL, *pz = NULL; - bool uncompressed; - - p = strjoin(dir, "xkb/", n, ".map", NULL); - pz = strjoin(dir, "xkb/", n, ".map.gz", NULL); - if (!p || !pz) - return -ENOMEM; - - uncompressed = access(p, F_OK) == 0; - if (uncompressed || access(pz, F_OK) == 0) { - log_debug("Found converted keymap %s at %s", - n, uncompressed ? p : pz); - - *new_keymap = n; - n = NULL; - return 1; - } - } - - return 0; -} - -int find_legacy_keymap(Context *c, char **new_keymap) { - const char *map; - _cleanup_fclose_ FILE *f = NULL; - unsigned n = 0; - unsigned best_matching = 0; - int r; - - assert(!isempty(c->x11_layout)); - - map = systemd_kbd_model_map(); - - f = fopen(map, "re"); - if (!f) - return -errno; - - for (;;) { - _cleanup_strv_free_ char **a = NULL; - unsigned matching = 0; - - r = read_next_mapping(map, 5, UINT_MAX, f, &n, &a); - if (r < 0) - return r; - if (r == 0) - break; - - /* Determine how well matching this entry is */ - if (streq(c->x11_layout, a[1])) - /* If we got an exact match, this is best */ - matching = 10; - else { - /* We have multiple X layouts, look for an - * entry that matches our key with everything - * but the first layout stripped off. */ - if (startswith_comma(c->x11_layout, a[1])) - matching = 5; - else { - char *x; - - /* If that didn't work, strip off the - * other layouts from the entry, too */ - x = strndupa(a[1], strcspn(a[1], ",")); - if (startswith_comma(c->x11_layout, x)) - matching = 1; - } - } - - if (matching > 0) { - if (isempty(c->x11_model) || streq_ptr(c->x11_model, a[2])) { - matching++; - - if (streq_ptr(c->x11_variant, a[3])) { - matching++; - - if (streq_ptr(c->x11_options, a[4])) - matching++; - } - } - } - - /* The best matching entry so far, then let's save that */ - if (matching >= MAX(best_matching, 1u)) { - log_debug("Found legacy keymap %s with score %u", - a[0], matching); - - if (matching > best_matching) { - best_matching = matching; - - r = free_and_strdup(new_keymap, a[0]); - if (r < 0) - return r; - } - } - } - - if (best_matching < 10 && c->x11_layout) { - /* The best match is only the first part of the X11 - * keymap. Check if we have a converted map which - * matches just the first layout. - */ - char *l, *v = NULL, *converted; - - l = strndupa(c->x11_layout, strcspn(c->x11_layout, ",")); - if (c->x11_variant) - v = strndupa(c->x11_variant, strcspn(c->x11_variant, ",")); - r = find_converted_keymap(l, v, &converted); - if (r < 0) - return r; - if (r > 0) { - free(*new_keymap); - *new_keymap = converted; - } - } - - return (bool) *new_keymap; -} - -int find_language_fallback(const char *lang, char **language) { - const char *map; - _cleanup_fclose_ FILE *f = NULL; - unsigned n = 0; - - assert(lang); - assert(language); - - map = systemd_language_fallback_map(); - - f = fopen(map, "re"); - if (!f) - return -errno; - - for (;;) { - _cleanup_strv_free_ char **a = NULL; - int r; - - r = read_next_mapping(map, 2, 2, f, &n, &a); - if (r <= 0) - return r; - - if (streq(lang, a[0])) { - assert(strv_length(a) == 2); - *language = a[1]; - a[1] = NULL; - return 1; - } - } - - assert_not_reached("should not be here"); -} - -int x11_convert_to_vconsole(Context *c) { - bool modified = false; - - if (isempty(c->x11_layout)) { - modified = - !isempty(c->vc_keymap) || - !isempty(c->vc_keymap_toggle); - - context_free_vconsole(c); - } else { - char *new_keymap = NULL; - int r; - - r = find_converted_keymap(c->x11_layout, c->x11_variant, &new_keymap); - if (r < 0) - return r; - else if (r == 0) { - r = find_legacy_keymap(c, &new_keymap); - if (r < 0) - return r; - } - if (r == 0) - /* We search for layout-variant match first, but then we also look - * for anything which matches just the layout. So it's accurate to say - * that we couldn't find anything which matches the layout. */ - log_notice("No conversion to virtual console map found for \"%s\".", - c->x11_layout); - - if (!streq_ptr(c->vc_keymap, new_keymap)) { - free(c->vc_keymap); - c->vc_keymap = new_keymap; - c->vc_keymap_toggle = mfree(c->vc_keymap_toggle); - modified = true; - } else - free(new_keymap); - } - - if (modified) - log_info("Changing virtual console keymap to '%s' toggle '%s'", - strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); - else - log_debug("Virtual console keymap was not modified."); - - return modified; -} diff --git a/src/locale/keymap-util.h b/src/locale/keymap-util.h deleted file mode 100644 index 20ef2a4a34..0000000000 --- a/src/locale/keymap-util.h +++ /dev/null @@ -1,46 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "locale-util.h" - -typedef struct Context { - char *locale[_VARIABLE_LC_MAX]; - - char *x11_layout; - char *x11_model; - char *x11_variant; - char *x11_options; - - char *vc_keymap; - char *vc_keymap_toggle; -} Context; - -int find_converted_keymap(const char *x11_layout, const char *x11_variant, char **new_keymap); -int find_legacy_keymap(Context *c, char **new_keymap); -int find_language_fallback(const char *lang, char **language); - -int context_read_data(Context *c); -void context_free(Context *c); -int vconsole_convert_to_x11(Context *c); -int vconsole_write_data(Context *c); -int x11_convert_to_vconsole(Context *c); -int x11_write_data(Context *c); -void locale_simplify(Context *c); -int locale_write_data(Context *c, char ***settings); diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map deleted file mode 100644 index d0b02a6b98..0000000000 --- a/src/locale/language-fallback-map +++ /dev/null @@ -1,13 +0,0 @@ -csb_PL csb:pl -en_AU en_AU:en_GB -en_IE en_IE:en_GB -en_NZ en_NZ:en_GB -en_ZA en_ZA:en_GB -fr_BE fr_BE:fr_FR -fr_CA fr_CA:fr_FR -fr_CH fr_CH:fr_FR -fr_LU fr_LU:fr_FR -it_CH it_CH:it_IT -mai_IN mai:hi -nds_DE nds:de -szl_PL szl:pl diff --git a/src/locale/localectl.c b/src/locale/localectl.c deleted file mode 100644 index 81afb4909f..0000000000 --- a/src/locale/localectl.c +++ /dev/null @@ -1,683 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "bus-error.h" -#include "bus-util.h" -#include "def.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; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_convert = true; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -typedef struct StatusInfo { - char **locale; - char *vconsole_keymap; - char *vconsole_keymap_toggle; - char *x11_layout; - char *x11_model; - char *x11_variant; - char *x11_options; -} StatusInfo; - -static void status_info_clear(StatusInfo *info) { - if (info) { - strv_free(info->locale); - free(info->vconsole_keymap); - free(info->vconsole_keymap_toggle); - free(info->x11_layout); - free(info->x11_model); - free(info->x11_variant); - free(info->x11_options); - zero(*info); - } -} - -static void print_overridden_variables(void) { - int r; - char *variables[_VARIABLE_LC_MAX] = {}; - LocaleVariable j; - bool print_warning = true; - - if (detect_container() > 0 || arg_host) - return; - - r = parse_env_file("/proc/cmdline", WHITESPACE, - "locale.LANG", &variables[VARIABLE_LANG], - "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE], - "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE], - "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], - "locale.LC_TIME", &variables[VARIABLE_LC_TIME], - "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE], - "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY], - "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], - "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER], - "locale.LC_NAME", &variables[VARIABLE_LC_NAME], - "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], - "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], - "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], - "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION], - NULL); - - if (r < 0 && r != -ENOENT) { - log_warning_errno(r, "Failed to read /proc/cmdline: %m"); - goto finish; - } - - for (j = 0; j < _VARIABLE_LC_MAX; j++) - if (variables[j]) { - if (print_warning) { - log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" - " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); - - print_warning = false; - } else - log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); - } - finish: - for (j = 0; j < _VARIABLE_LC_MAX; j++) - free(variables[j]); -} - -static void print_status_info(StatusInfo *i) { - assert(i); - - if (strv_isempty(i->locale)) - puts(" System Locale: n/a"); - else { - char **j; - - printf(" System Locale: %s\n", i->locale[0]); - STRV_FOREACH(j, i->locale + 1) - printf(" %s\n", *j); - } - - printf(" VC Keymap: %s\n", strna(i->vconsole_keymap)); - if (!isempty(i->vconsole_keymap_toggle)) - printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle); - - printf(" X11 Layout: %s\n", strna(i->x11_layout)); - if (!isempty(i->x11_model)) - printf(" X11 Model: %s\n", i->x11_model); - if (!isempty(i->x11_variant)) - printf(" X11 Variant: %s\n", i->x11_variant); - if (!isempty(i->x11_options)) - printf(" X11 Options: %s\n", i->x11_options); -} - -static int show_status(sd_bus *bus, char **args, unsigned n) { - _cleanup_(status_info_clear) StatusInfo info = {}; - static const struct bus_properties_map map[] = { - { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) }, - { "VConsoleKeymap", "s", NULL, offsetof(StatusInfo, vconsole_keymap) }, - { "VConsoleKeymapToggle", "s", NULL, offsetof(StatusInfo, vconsole_keymap_toggle) }, - { "X11Layout", "s", NULL, offsetof(StatusInfo, x11_layout) }, - { "X11Model", "s", NULL, offsetof(StatusInfo, x11_model) }, - { "X11Variant", "s", NULL, offsetof(StatusInfo, x11_variant) }, - { "X11Options", "s", NULL, offsetof(StatusInfo, x11_options) }, - { "Locale", "as", NULL, offsetof(StatusInfo, locale) }, - {} - }; - int r; - - assert(bus); - - r = bus_map_all_properties(bus, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - map, - &info); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - print_overridden_variables(); - print_status_info(&info); - - return r; -} - -static int set_locale(sd_bus *bus, char **args, unsigned n) { - _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); - assert(args); - - polkit_agent_open_if_enabled(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "SetLocale"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, args + 1); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "b", arg_ask_password); - 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 issue method call: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int list_locales(sd_bus *bus, char **args, unsigned n) { - _cleanup_strv_free_ char **l = NULL; - int r; - - assert(args); - - r = get_locales(&l); - if (r < 0) - return log_error_errno(r, "Failed to read list of locales: %m"); - - pager_open(arg_no_pager, false); - strv_print(l); - - return 0; -} - -static int set_vconsole_keymap(sd_bus *bus, char **args, unsigned n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *map, *toggle_map; - int r; - - assert(bus); - assert(args); - - if (n > 3) { - log_error("Too many arguments."); - return -EINVAL; - } - - polkit_agent_open_if_enabled(); - - map = args[1]; - toggle_map = n > 2 ? args[2] : ""; - - r = sd_bus_call_method( - bus, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "SetVConsoleKeyboard", - &error, - NULL, - "ssbb", map, toggle_map, arg_convert, arg_ask_password); - if (r < 0) - log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); - - return r; -} - -static Set *keymaps = NULL; - -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int tflag, - struct FTW *ftwbuf) { - - char *p, *e; - int r; - - if (tflag != FTW_F) - return 0; - - if (!endswith(fpath, ".map") && - !endswith(fpath, ".map.gz")) - return 0; - - p = strdup(basename(fpath)); - if (!p) - return log_oom(); - - e = endswith(p, ".map"); - if (e) - *e = 0; - - e = endswith(p, ".map.gz"); - if (e) - *e = 0; - - r = set_consume(keymaps, p); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Can't add keymap: %m"); - - return 0; -} - -static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) { - _cleanup_strv_free_ char **l = NULL; - const char *dir; - - keymaps = set_new(&string_hash_ops); - if (!keymaps) - return log_oom(); - - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) - nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS); - - l = set_get_strv(keymaps); - if (!l) { - set_free_free(keymaps); - return log_oom(); - } - - set_free(keymaps); - - if (strv_isempty(l)) { - log_error("Couldn't find any console keymaps."); - return -ENOENT; - } - - strv_sort(l); - - pager_open(arg_no_pager, false); - - strv_print(l); - - return 0; -} - -static int set_x11_keymap(sd_bus *bus, char **args, unsigned n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *layout, *model, *variant, *options; - int r; - - assert(bus); - assert(args); - - if (n > 5) { - log_error("Too many arguments."); - return -EINVAL; - } - - polkit_agent_open_if_enabled(); - - layout = args[1]; - model = n > 2 ? args[2] : ""; - variant = n > 3 ? args[3] : ""; - options = n > 4 ? args[4] : ""; - - r = sd_bus_call_method( - bus, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "SetX11Keyboard", - &error, - NULL, - "ssssbb", layout, model, variant, options, - arg_convert, arg_ask_password); - if (r < 0) - log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); - - return r; -} - -static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **list = NULL; - char line[LINE_MAX]; - enum { - NONE, - MODELS, - LAYOUTS, - VARIANTS, - OPTIONS - } state = NONE, look_for; - int r; - - if (n > 2) { - log_error("Too many arguments."); - return -EINVAL; - } - - f = fopen("/usr/share/X11/xkb/rules/base.lst", "re"); - if (!f) - return log_error_errno(errno, "Failed to open keyboard mapping list. %m"); - - if (streq(args[0], "list-x11-keymap-models")) - look_for = MODELS; - else if (streq(args[0], "list-x11-keymap-layouts")) - look_for = LAYOUTS; - else if (streq(args[0], "list-x11-keymap-variants")) - look_for = VARIANTS; - else if (streq(args[0], "list-x11-keymap-options")) - look_for = OPTIONS; - else - assert_not_reached("Wrong parameter"); - - FOREACH_LINE(line, f, break) { - char *l, *w; - - l = strstrip(line); - - if (isempty(l)) - continue; - - if (l[0] == '!') { - if (startswith(l, "! model")) - state = MODELS; - else if (startswith(l, "! layout")) - state = LAYOUTS; - else if (startswith(l, "! variant")) - state = VARIANTS; - else if (startswith(l, "! option")) - state = OPTIONS; - else - state = NONE; - - continue; - } - - if (state != look_for) - continue; - - w = l + strcspn(l, WHITESPACE); - - if (n > 1) { - char *e; - - if (*w == 0) - continue; - - *w = 0; - w++; - w += strspn(w, WHITESPACE); - - e = strchr(w, ':'); - if (!e) - continue; - - *e = 0; - - if (!streq(w, args[1])) - continue; - } else - *w = 0; - - r = strv_extend(&list, l); - if (r < 0) - return log_oom(); - } - - if (strv_isempty(list)) { - log_error("Couldn't find any entries."); - return -ENOENT; - } - - strv_sort(list); - strv_uniq(list); - - pager_open(arg_no_pager, false); - - strv_print(list); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...] COMMAND ...\n\n" - "Query or change system locale and keyboard settings.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-ask-password Do not prompt for password\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --no-convert Don't convert keyboard mappings\n\n" - "Commands:\n" - " status Show current locale settings\n" - " set-locale LOCALE... Set system locale\n" - " list-locales Show known locales\n" - " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n" - " list-keymaps Show known virtual console keyboard mappings\n" - " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n" - " Set X11 and console keyboard mappings\n" - " list-x11-keymap-models Show known X11 keyboard mapping models\n" - " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n" - " list-x11-keymap-variants [LAYOUT]\n" - " Show known X11 keyboard mapping variants\n" - " list-x11-keymap-options Show known X11 keyboard mapping options\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_CONVERT, - ARG_NO_ASK_PASSWORD - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "no-convert", no_argument, NULL, ARG_NO_CONVERT }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_NO_CONVERT: - arg_convert = false; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int localectl_main(sd_bus *bus, int argc, char *argv[]) { - - static const struct { - const char* verb; - const enum { - MORE, - LESS, - EQUAL - } argc_cmp; - const int argc; - int (* const dispatch)(sd_bus *bus, char **args, unsigned n); - } verbs[] = { - { "status", LESS, 1, show_status }, - { "set-locale", MORE, 2, set_locale }, - { "list-locales", EQUAL, 1, list_locales }, - { "set-keymap", MORE, 2, set_vconsole_keymap }, - { "list-keymaps", EQUAL, 1, list_vconsole_keymaps }, - { "set-x11-keymap", MORE, 2, set_x11_keymap }, - { "list-x11-keymap-models", EQUAL, 1, list_x11_keymaps }, - { "list-x11-keymap-layouts", EQUAL, 1, list_x11_keymaps }, - { "list-x11-keymap-variants", LESS, 2, list_x11_keymaps }, - { "list-x11-keymap-options", EQUAL, 1, list_x11_keymaps }, - }; - - int left; - unsigned i; - - assert(argc >= 0); - assert(argv); - - left = argc - optind; - - if (left <= 0) - /* Special rule: no arguments means "status" */ - i = 0; - else { - if (streq(argv[optind], "help")) { - help(); - return 0; - } - - for (i = 0; i < ELEMENTSOF(verbs); i++) - if (streq(argv[optind], verbs[i].verb)) - break; - - if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); - return -EINVAL; - } - } - - switch (verbs[i].argc_cmp) { - - case EQUAL: - if (left != verbs[i].argc) { - log_error("Invalid number of arguments."); - return -EINVAL; - } - - break; - - case MORE: - if (left < verbs[i].argc) { - log_error("Too few arguments."); - return -EINVAL; - } - - break; - - case LESS: - if (left > verbs[i].argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Unknown comparison operator."); - } - - return verbs[i].dispatch(bus, argv + optind, left); -} - -int main(int argc, char*argv[]) { - sd_bus *bus = NULL; - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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; - } - - r = localectl_main(bus, argc, argv); - -finish: - sd_bus_flush_close_unref(bus); - pager_close(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/locale/localed.c b/src/locale/localed.c deleted file mode 100644 index 298f176e40..0000000000 --- a/src/locale/localed.c +++ /dev/null @@ -1,710 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_XKBCOMMON -#include -#include -#endif - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-message.h" -#include "bus-util.h" -#include "def.h" -#include "keymap-util.h" -#include "locale-util.h" -#include "macro.h" -#include "path-util.h" -#include "selinux-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" - -static Hashmap *polkit_registry = NULL; - -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_(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; - - assert(bus); - - l_unset = new0(char*, _VARIABLE_LC_MAX); - if (!l_unset) - return -ENOMEM; - - l_set = new0(char*, _VARIABLE_LC_MAX); - if (!l_set) - return -ENOMEM; - - for (p = 0, c_set = 0, c_unset = 0; p < _VARIABLE_LC_MAX; p++) { - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - if (isempty(c->locale[p])) - l_unset[c_set++] = (char*) name; - else { - char *s; - - if (asprintf(&s, "%s=%s", name, c->locale[p]) < 0) - return -ENOMEM; - - l_set[c_unset++] = s; - } - } - - assert(c_set + c_unset == _VARIABLE_LC_MAX); - r = sd_bus_message_new_method_call(bus, &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UnsetAndSetEnvironment"); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, l_unset); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, l_set); - if (r < 0) - return r; - - r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) - log_error_errno(r, "Failed to update the manager environment: %m"); - - return 0; -} - -static int vconsole_reload(sd_bus *bus) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - - r = sd_bus_call_method(bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "RestartUnit", - &error, - NULL, - "ss", "systemd-vconsole-setup.service", "replace"); - - if (r < 0) - log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); - return r; -} - -static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus *bus) { - int r; - - assert(bus); - - r = vconsole_convert_to_x11(c); - if (r <= 0) - return r; - - /* modified */ - r = x11_write_data(c); - if (r < 0) - return log_error_errno(r, "Failed to write X11 keyboard layout: %m"); - - sd_bus_emit_properties_changed(bus, - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); - - return 1; -} - -static int x11_convert_to_vconsole_and_emit(Context *c, sd_bus *bus) { - int r; - - assert(bus); - - r = x11_convert_to_vconsole(c); - if (r <= 0) - return r; - - /* modified */ - r = vconsole_write_data(c); - if (r < 0) - log_error_errno(r, "Failed to save virtual console keymap: %m"); - - sd_bus_emit_properties_changed(bus, - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "VConsoleKeymap", "VConsoleKeymapToggle", NULL); - - return vconsole_reload(bus); -} - -static int property_get_locale( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Context *c = userdata; - _cleanup_strv_free_ char **l = NULL; - int p, q; - - l = new0(char*, _VARIABLE_LC_MAX+1); - if (!l) - return -ENOMEM; - - for (p = 0, q = 0; p < _VARIABLE_LC_MAX; p++) { - char *t; - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - if (isempty(c->locale[p])) - continue; - - if (asprintf(&t, "%s=%s", name, c->locale[p]) < 0) - return -ENOMEM; - - l[q++] = t; - } - - return sd_bus_message_append_strv(reply, l); -} - -static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - _cleanup_strv_free_ char **l = NULL; - char **i; - const char *lang = NULL; - int interactive; - bool modified = false; - bool have[_VARIABLE_LC_MAX] = {}; - int p; - int r; - - assert(m); - assert(c); - - r = bus_message_read_strv_extend(m, &l); - if (r < 0) - return r; - - r = sd_bus_message_read_basic(m, 'b', &interactive); - if (r < 0) - return r; - - /* Check whether a variable changed and if it is valid */ - STRV_FOREACH(i, l) { - bool valid = false; - - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - size_t k; - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - k = strlen(name); - if (startswith(*i, name) && - (*i)[k] == '=' && - locale_is_valid((*i) + k + 1)) { - valid = true; - have[p] = true; - - if (p == VARIABLE_LANG) - lang = (*i) + k + 1; - - if (!streq_ptr(*i + k + 1, c->locale[p])) - modified = true; - - break; - } - } - - if (!valid) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); - } - - /* If LANG was specified, but not LANGUAGE, check if we should - * set it based on the language fallback table. */ - if (have[VARIABLE_LANG] && !have[VARIABLE_LANGUAGE]) { - _cleanup_free_ char *language = NULL; - - assert(lang); - - (void) find_language_fallback(lang, &language); - if (language) { - log_debug("Converted LANG=%s to LANGUAGE=%s", lang, language); - if (!streq_ptr(language, c->locale[VARIABLE_LANGUAGE])) { - r = strv_extendf(&l, "LANGUAGE=%s", language); - if (r < 0) - return r; - - have[VARIABLE_LANGUAGE] = true; - modified = true; - } - } - } - - /* Check whether a variable is unset */ - if (!modified) - for (p = 0; p < _VARIABLE_LC_MAX; p++) - if (!isempty(c->locale[p]) && !have[p]) { - modified = true; - break; - } - - if (modified) { - _cleanup_strv_free_ char **settings = NULL; - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - "org.freedesktop.locale1.set-locale", - NULL, - interactive, - UID_INVALID, - &polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - STRV_FOREACH(i, l) - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - size_t k; - const char *name; - - name = locale_variable_to_string(p); - assert(name); - - k = strlen(name); - if (startswith(*i, name) && (*i)[k] == '=') { - r = free_and_strdup(&c->locale[p], *i + k + 1); - if (r < 0) - return r; - break; - } - } - - for (p = 0; p < _VARIABLE_LC_MAX; p++) { - if (have[p]) - continue; - - c->locale[p] = mfree(c->locale[p]); - } - - locale_simplify(c); - - r = locale_write_data(c, &settings); - if (r < 0) { - log_error_errno(r, "Failed to set locale: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r)); - } - - locale_update_system_manager(c, sd_bus_message_get_bus(m)); - - if (settings) { - _cleanup_free_ char *line; - - line = strv_join(settings, ", "); - log_info("Changed locale to %s.", strnull(line)); - } else - log_info("Changed locale to unset."); - - (void) sd_bus_emit_properties_changed( - sd_bus_message_get_bus(m), - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "Locale", NULL); - } else - log_debug("Locale settings were not modified."); - - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - const char *keymap, *keymap_toggle; - int convert, interactive; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "ssbb", &keymap, &keymap_toggle, &convert, &interactive); - if (r < 0) - return r; - - keymap = empty_to_null(keymap); - keymap_toggle = empty_to_null(keymap_toggle); - - if (!streq_ptr(keymap, c->vc_keymap) || - !streq_ptr(keymap_toggle, c->vc_keymap_toggle)) { - - if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) || - (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle)))) - return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keymap data"); - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - "org.freedesktop.locale1.set-keyboard", - NULL, - interactive, - UID_INVALID, - &polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - if (free_and_strdup(&c->vc_keymap, keymap) < 0 || - free_and_strdup(&c->vc_keymap_toggle, keymap_toggle) < 0) - return -ENOMEM; - - r = vconsole_write_data(c); - if (r < 0) { - log_error_errno(r, "Failed to set virtual console keymap: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r)); - } - - log_info("Changed virtual console keymap to '%s' toggle '%s'", - strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); - - r = vconsole_reload(sd_bus_message_get_bus(m)); - if (r < 0) - log_error_errno(r, "Failed to request keymap reload: %m"); - - (void) sd_bus_emit_properties_changed( - sd_bus_message_get_bus(m), - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "VConsoleKeymap", "VConsoleKeymapToggle", NULL); - - if (convert) { - r = vconsole_convert_to_x11_and_emit(c, sd_bus_message_get_bus(m)); - if (r < 0) - log_error_errno(r, "Failed to convert keymap data: %m"); - } - } - - return sd_bus_reply_method_return(m, NULL); -} - -#ifdef HAVE_XKBCOMMON - -_printf_(3, 0) -static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { - const char *fmt; - - fmt = strjoina("libxkbcommon: ", format); - log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); -} - -#define LOAD_SYMBOL(symbol, dl, name) \ - ({ \ - (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \ - (symbol) ? 0 : -EOPNOTSUPP; \ - }) - -static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { - - /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge - * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function - * pointers to the shared library are below: */ - - struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL; - void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL; - void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL; - struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL; - void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL; - - const struct xkb_rule_names rmlvo = { - .model = model, - .layout = layout, - .variant = variant, - .options = options, - }; - struct xkb_context *ctx = NULL; - struct xkb_keymap *km = NULL; - void *dl; - int r; - - /* Compile keymap from RMLVO information to check out its validity */ - - dl = dlopen("libxkbcommon.so.0", RTLD_LAZY); - if (!dl) - return -EOPNOTSUPP; - - r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new"); - if (r < 0) - goto finish; - - r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref"); - if (r < 0) - goto finish; - - r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn"); - if (r < 0) - goto finish; - - r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names"); - if (r < 0) - goto finish; - - r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref"); - if (r < 0) - goto finish; - - ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); - if (!ctx) { - r = -ENOMEM; - goto finish; - } - - symbol_xkb_context_set_log_fn(ctx, log_xkb); - - km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!km) { - r = -EINVAL; - goto finish; - } - - r = 0; - -finish: - if (symbol_xkb_keymap_unref && km) - symbol_xkb_keymap_unref(km); - - if (symbol_xkb_context_unref && ctx) - symbol_xkb_context_unref(ctx); - - (void) dlclose(dl); - return r; -} - -#else - -static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { - return 0; -} - -#endif - -static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - const char *layout, *model, *variant, *options; - int convert, interactive; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "ssssbb", &layout, &model, &variant, &options, &convert, &interactive); - if (r < 0) - return r; - - layout = empty_to_null(layout); - model = empty_to_null(model); - variant = empty_to_null(variant); - options = empty_to_null(options); - - if (!streq_ptr(layout, c->x11_layout) || - !streq_ptr(model, c->x11_model) || - !streq_ptr(variant, c->x11_variant) || - !streq_ptr(options, c->x11_options)) { - - if ((layout && !string_is_safe(layout)) || - (model && !string_is_safe(model)) || - (variant && !string_is_safe(variant)) || - (options && !string_is_safe(options))) - return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data"); - - r = bus_verify_polkit_async( - m, - CAP_SYS_ADMIN, - "org.freedesktop.locale1.set-keyboard", - NULL, - interactive, - UID_INVALID, - &polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = verify_xkb_rmlvo(model, layout, variant, options); - if (r < 0) { - log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", - strempty(model), strempty(layout), strempty(variant), strempty(options)); - - if (r == -EOPNOTSUPP) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system."); - - return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); - } - - if (free_and_strdup(&c->x11_layout, layout) < 0 || - free_and_strdup(&c->x11_model, model) < 0 || - free_and_strdup(&c->x11_variant, variant) < 0 || - free_and_strdup(&c->x11_options, options) < 0) - return -ENOMEM; - - r = x11_write_data(c); - if (r < 0) { - log_error_errno(r, "Failed to set X11 keyboard layout: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r)); - } - - log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", - strempty(c->x11_layout), - strempty(c->x11_model), - strempty(c->x11_variant), - strempty(c->x11_options)); - - (void) sd_bus_emit_properties_changed( - sd_bus_message_get_bus(m), - "/org/freedesktop/locale1", - "org.freedesktop.locale1", - "X11Layout", "X11Model", "X11Variant", "X11Options", NULL); - - if (convert) { - r = x11_convert_to_vconsole_and_emit(c, sd_bus_message_get_bus(m)); - if (r < 0) - log_error_errno(r, "Failed to convert keymap data: %m"); - } - } - - return sd_bus_reply_method_return(m, NULL); -} - -static const sd_bus_vtable locale_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Locale", "as", property_get_locale, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("X11Layout", "s", NULL, offsetof(Context, x11_layout), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("X11Model", "s", NULL, offsetof(Context, x11_model), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("X11Variant", "s", NULL, offsetof(Context, x11_variant), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("X11Options", "s", NULL, offsetof(Context, x11_options), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("VConsoleKeymap", "s", NULL, offsetof(Context, vc_keymap), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", NULL, offsetof(Context, vc_keymap_toggle), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(c); - assert(event); - assert(_bus); - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get system bus connection: %m"); - - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - *_bus = bus; - bus = NULL; - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(context_free) Context context = {}; - _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); - log_parse_environment(); - log_open(); - - umask(0022); - mac_selinux_init(); - - 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"); - goto finish; - } - - sd_event_set_watchdog(event, true); - - r = connect_bus(&context, event, &bus); - if (r < 0) - goto finish; - - r = context_read_data(&context); - if (r < 0) { - log_error_errno(r, "Failed to read locale data: %m"); - goto finish; - } - - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); - if (r < 0) - log_error_errno(r, "Failed to run event loop: %m"); - -finish: - bus_verify_polkit_async_registry_free(polkit_registry); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/locale/org.freedesktop.locale1.conf b/src/locale/org.freedesktop.locale1.conf deleted file mode 100644 index 79d0ecd2bb..0000000000 --- a/src/locale/org.freedesktop.locale1.conf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/locale/org.freedesktop.locale1.policy.in b/src/locale/org.freedesktop.locale1.policy.in deleted file mode 100644 index df63845e9b..0000000000 --- a/src/locale/org.freedesktop.locale1.policy.in +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Set system locale - <_message>Authentication is required to set the system locale. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.locale1.set-keyboard - - - - <_description>Set system keyboard settings - <_message>Authentication is required to set the system keyboard settings. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/locale/org.freedesktop.locale1.service b/src/locale/org.freedesktop.locale1.service deleted file mode 100644 index 025f9a0fc2..0000000000 --- a/src/locale/org.freedesktop.locale1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.locale1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.locale1.service diff --git a/src/locale/test-keymap-util.c b/src/locale/test-keymap-util.c deleted file mode 100644 index 2adda3da2b..0000000000 --- a/src/locale/test-keymap-util.c +++ /dev/null @@ -1,220 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2016 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "keymap-util.h" -#include "log.h" -#include "string-util.h" - -static void test_find_language_fallback(void) { - _cleanup_free_ char *ans = NULL, *ans2 = NULL; - - log_info("/*** %s ***/", __func__); - - assert_se(find_language_fallback("foobar", &ans) == 0); - assert_se(ans == NULL); - - assert_se(find_language_fallback("csb", &ans) == 0); - assert_se(ans == NULL); - - assert_se(find_language_fallback("csb_PL", &ans) == 1); - assert_se(streq(ans, "csb:pl")); - - assert_se(find_language_fallback("szl_PL", &ans2) == 1); - assert_se(streq(ans2, "szl:pl")); -} - -static void test_find_converted_keymap(void) { - _cleanup_free_ char *ans = NULL, *ans2 = NULL; - int r; - - log_info("/*** %s ***/", __func__); - - assert_se(find_converted_keymap("pl", "foobar", &ans) == 0); - assert_se(ans == NULL); - - r = find_converted_keymap("pl", NULL, &ans); - if (r == 0) { - log_info("Skipping rest of %s: keymaps are not installed", __func__); - return; - } - - assert_se(r == 1); - assert_se(streq(ans, "pl")); - - assert_se(find_converted_keymap("pl", "dvorak", &ans2) == 1); - assert_se(streq(ans2, "pl-dvorak")); -} - -static void test_find_legacy_keymap(void) { - Context c = {}; - _cleanup_free_ char *ans = NULL, *ans2 = NULL; - - log_info("/*** %s ***/", __func__); - - c.x11_layout = (char*) "foobar"; - assert_se(find_legacy_keymap(&c, &ans) == 0); - assert_se(ans == NULL); - - c.x11_layout = (char*) "pl"; - assert_se(find_legacy_keymap(&c, &ans) == 1); - assert_se(streq(ans, "pl2")); - - c.x11_layout = (char*) "pl,ru"; - assert_se(find_legacy_keymap(&c, &ans2) == 1); - assert_se(streq(ans, "pl2")); -} - -static void test_vconsole_convert_to_x11(void) { - _cleanup_(context_free) Context c = {}; - - log_info("/*** %s ***/", __func__); - - log_info("/* test emptying first (:) */"); - assert_se(free_and_strdup(&c.x11_layout, "foo") >= 0); - assert_se(free_and_strdup(&c.x11_variant, "bar") >= 0); - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(c.x11_layout == NULL); - assert_se(c.x11_variant == NULL); - - log_info("/* test emptying second (:) */"); - - assert_se(vconsole_convert_to_x11(&c) == 0); - assert_se(c.x11_layout == NULL); - assert_se(c.x11_variant == NULL); - - log_info("/* test without variant, new mapping (es:) */"); - assert_se(free_and_strdup(&c.vc_keymap, "es") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(c.x11_layout, "es")); - assert_se(c.x11_variant == NULL); - - log_info("/* test with known variant, new mapping (es:dvorak) */"); - assert_se(free_and_strdup(&c.vc_keymap, "es-dvorak") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 0); // FIXME - assert_se(streq(c.x11_layout, "es")); - assert_se(c.x11_variant == NULL); // FIXME: "dvorak" - - log_info("/* test with old mapping (fr:latin9) */"); - assert_se(free_and_strdup(&c.vc_keymap, "fr-latin9") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(c.x11_layout, "fr")); - assert_se(streq(c.x11_variant, "latin9")); - - log_info("/* test with a compound mapping (ru,us) */"); - assert_se(free_and_strdup(&c.vc_keymap, "ru") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(c.x11_layout, "ru,us")); - assert_se(c.x11_variant == NULL); - - log_info("/* test with a simple mapping (us) */"); - assert_se(free_and_strdup(&c.vc_keymap, "us") >= 0); - - assert_se(vconsole_convert_to_x11(&c) == 1); - assert_se(streq(c.x11_layout, "us")); - assert_se(c.x11_variant == NULL); -} - -static void test_x11_convert_to_vconsole(void) { - _cleanup_(context_free) Context c = {}; - int r; - - log_info("/*** %s ***/", __func__); - - log_info("/* test emptying first (:) */"); - assert_se(free_and_strdup(&c.vc_keymap, "foobar") >= 0); - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(c.vc_keymap == NULL); - - log_info("/* test emptying second (:) */"); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(c.vc_keymap == NULL); - - log_info("/* test without variant, new mapping (es:) */"); - assert_se(free_and_strdup(&c.x11_layout, "es") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "es")); - - log_info("/* test with unknown variant, new mapping (es:foobar) */"); - assert_se(free_and_strdup(&c.x11_variant, "foobar") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(streq(c.vc_keymap, "es")); - - log_info("/* test with known variant, new mapping (es:dvorak) */"); - assert_se(free_and_strdup(&c.x11_variant, "dvorak") >= 0); - - r = x11_convert_to_vconsole(&c); - if (r == 0) { - log_info("Skipping rest of %s: keymaps are not installed", __func__); - return; - } - - assert_se(r == 1); - assert_se(streq(c.vc_keymap, "es-dvorak")); - - log_info("/* test with old mapping (fr:latin9) */"); - assert_se(free_and_strdup(&c.x11_layout, "fr") >= 0); - assert_se(free_and_strdup(&c.x11_variant, "latin9") >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "fr-latin9")); - - log_info("/* test with a compound mapping (us,ru:) */"); - assert_se(free_and_strdup(&c.x11_layout, "us,ru") >= 0); - assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "us")); - - log_info("/* test with a compound mapping (ru,us:) */"); - assert_se(free_and_strdup(&c.x11_layout, "ru,us") >= 0); - assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 1); - assert_se(streq(c.vc_keymap, "ru")); - - /* https://bugzilla.redhat.com/show_bug.cgi?id=1333998 */ - log_info("/* test with a simple new mapping (ru:) */"); - assert_se(free_and_strdup(&c.x11_layout, "ru") >= 0); - assert_se(free_and_strdup(&c.x11_variant, NULL) >= 0); - - assert_se(x11_convert_to_vconsole(&c) == 0); - assert_se(streq(c.vc_keymap, "ru")); -} - -int main(int argc, char **argv) { - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - - test_find_language_fallback(); - test_find_converted_keymap(); - test_find_legacy_keymap(); - - test_vconsole_convert_to_x11(); - test_x11_convert_to_vconsole(); - - return 0; -} diff --git a/src/login/.gitignore b/src/login/.gitignore deleted file mode 100644 index 3a8ba497c1..0000000000 --- a/src/login/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/logind-gperf.c -/logind.conf -/org.freedesktop.login1.policy -/71-seat.rules -/73-seat-late.rules -/systemd-user diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules deleted file mode 100644 index e2855b50f7..0000000000 --- a/src/login/70-power-switch.rules +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="power_switch_end" - -SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", \ - SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="*,116|116,*|116|*,116,*", TAG+="power-switch" - -LABEL="power_switch_end" diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules deleted file mode 100644 index 50dcd2e275..0000000000 --- a/src/login/70-uaccess.rules +++ /dev/null @@ -1,81 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="uaccess_end" -ENV{MAJOR}=="", GOTO="uaccess_end" - -# PTP/MTP protocol devices, cameras, portable media players -SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess" - -# Digicams with proprietary protocol -ENV{ID_GPHOTO2}=="?*", TAG+="uaccess" - -# SCSI and USB scanners -ENV{libsane_matched}=="yes", TAG+="uaccess" - -# HPLIP devices (necessary for ink level check and HP tool maintenance) -ENV{ID_HPLIP}=="1", TAG+="uaccess" - -# optical drives -SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess" - -# Sound devices -SUBSYSTEM=="sound", TAG+="uaccess" \ - OPTIONS+="static_node=snd/timer", OPTIONS+="static_node=snd/seq" - -# ffado is an userspace driver for firewire sound cards -SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess" - -# Webcams, frame grabber, TV cards -SUBSYSTEM=="video4linux", TAG+="uaccess" -SUBSYSTEM=="dvb", TAG+="uaccess" - -# IIDC devices: industrial cameras and some webcams -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="uaccess" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" -# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" - -# DRI video devices -SUBSYSTEM=="drm", KERNEL=="card*|renderD*", TAG+="uaccess" - -# KVM -SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" - -# smart-card readers -ENV{ID_SMARTCARD_READER}=="?*", TAG+="uaccess" - -# (USB) authentication devices -ENV{ID_SECURITY_TOKEN}=="?*", TAG+="uaccess" - -# PDA devices -ENV{ID_PDA}=="?*", TAG+="uaccess" - -# Programmable remote control -ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" - -# joysticks -SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" - -# color measurement devices -ENV{COLOR_MEASUREMENT_DEVICE}=="?*", TAG+="uaccess" - -# DDC/CI device, usually high-end monitors such as the DreamColor -ENV{DDC_DEVICE}=="?*", TAG+="uaccess" - -# media player raw devices (for user-mode drivers, Android SDK, etc.) -SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" - -# software-defined radio communication devices -ENV{ID_SOFTWARE_RADIO}=="?*", TAG+="uaccess" - -# 3D printers, CNC machines, laser cutters, 3D scanners, etc. -ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess" - -LABEL="uaccess_end" diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in deleted file mode 100644 index de55c9a4ec..0000000000 --- a/src/login/71-seat.rules.in +++ /dev/null @@ -1,54 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="seat_end" - -TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat" -SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat" -SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat" -SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat", TAG+="master-of-seat" -SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" -SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" - -# 'Plugable' USB hub, sound, network, graphics adapter -SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1" - -# qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group -# devices belonging to one seat. See: -# http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt -SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", TAG+="seat", ENV{ID_AUTOSEAT}="1" - -# Mimo 720, with integrated USB hub, displaylink graphics, and e2i -# touchscreen. This device carries no proper VID/PID in the USB hub, -# but it does carry good ID data in the graphics component, hence we -# check it from the parent. There's a bit of a race here however, -# given that the child devices might not exist yet at the time this -# rule is executed. To work around this we'll trigger the parent from -# the child if we notice that the parent wasn't recognized yet. - -# Match parent -SUBSYSTEM=="usb", ATTR{idVendor}=="058f", ATTR{idProduct}=="6254", \ - ATTR{%k.2/idVendor}=="17e9", ATTR{%k.2/idProduct}=="401a", ATTR{%k.2/product}=="mimo inc", \ - ENV{ID_AUTOSEAT}="1", ENV{ID_AVOID_LOOP}="1" - -# Match child, look for parent's ID_AVOID_LOOP -SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ - ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ - IMPORT{parent}="ID_AVOID_LOOP" - -# Match child, retrigger parent -SUBSYSTEM=="usb", ATTR{idVendor}=="17e9", ATTR{idProduct}=="401a", ATTR{product}=="mimo inc", \ - ATTR{../idVendor}=="058f", ATTR{../idProduct}=="6254", \ - ENV{ID_AVOID_LOOP}=="", \ - RUN+="@rootbindir@/udevadm trigger --parent-match=%p/.." - -TAG=="seat", ENV{ID_PATH}=="", IMPORT{builtin}="path_id" -TAG=="seat", ENV{ID_FOR_SEAT}=="", ENV{ID_PATH_TAG}!="", ENV{ID_FOR_SEAT}="$env{SUBSYSTEM}-$env{ID_PATH_TAG}" - -SUBSYSTEM=="input", ATTR{name}=="Wiebetech LLC Wiebetech", RUN+="@rootbindir@/loginctl lock-sessions" - -LABEL="seat_end" diff --git a/src/login/73-seat-late.rules.in b/src/login/73-seat-late.rules.in deleted file mode 100644 index 901df750fd..0000000000 --- a/src/login/73-seat-late.rules.in +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="seat_late_end" - -ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="seat-$env{ID_FOR_SEAT}" -ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" - -ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" - -TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" - -LABEL="seat_late_end" diff --git a/src/login/Makefile b/src/login/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/login/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/login/inhibit.c b/src/login/inhibit.c deleted file mode 100644 index f2c37a8623..0000000000 --- a/src/login/inhibit.c +++ /dev/null @@ -1,292 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.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; -static const char* arg_why = "Unknown reason"; -static const char* arg_mode = NULL; - -static enum { - ACTION_INHIBIT, - ACTION_LIST -} arg_action = ACTION_INHIBIT; - -static int inhibit(sd_bus *bus, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - int fd; - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "Inhibit", - error, - &reply, - "ssss", arg_what, arg_who, arg_why, arg_mode); - if (r < 0) - return r; - - r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); - if (r < 0) - return r; - - r = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (r < 0) - return -errno; - - return r; -} - -static int print_inhibitors(sd_bus *bus, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *what, *who, *why, *mode; - unsigned int uid, pid; - unsigned n = 0; - int r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListInhibitors", - error, - &reply, - ""); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { - _cleanup_free_ char *comm = NULL, *u = NULL; - - if (arg_mode && !streq(mode, arg_mode)) - continue; - - get_process_comm(pid, &comm); - u = uid_to_name(uid); - - printf(" Who: %s (UID "UID_FMT"/%s, PID "PID_FMT"/%s)\n" - " What: %s\n" - " Why: %s\n" - " Mode: %s\n\n", - who, uid, strna(u), pid, strna(comm), - what, - why, - mode); - - n++; - } - 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); - - printf("%u inhibitors listed.\n", n); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Execute a process while inhibiting shutdown/sleep/idle.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --what=WHAT Operations to inhibit, colon separated list of:\n" - " shutdown, sleep, idle, handle-power-key,\n" - " handle-suspend-key, handle-hibernate-key,\n" - " handle-lid-switch\n" - " --who=STRING A descriptive string who is inhibiting\n" - " --why=STRING A descriptive string why is being inhibited\n" - " --mode=MODE One of block or delay\n" - " --list List active inhibitors\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_WHAT, - ARG_WHO, - ARG_WHY, - ARG_MODE, - ARG_LIST, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "what", required_argument, NULL, ARG_WHAT }, - { "who", required_argument, NULL, ARG_WHO }, - { "why", required_argument, NULL, ARG_WHY }, - { "mode", required_argument, NULL, ARG_MODE }, - { "list", no_argument, NULL, ARG_LIST }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_WHAT: - arg_what = optarg; - break; - - case ARG_WHO: - arg_who = optarg; - break; - - case ARG_WHY: - arg_why = optarg; - break; - - case ARG_MODE: - arg_mode = optarg; - break; - - case ARG_LIST: - arg_action = ACTION_LIST; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_action == ACTION_INHIBIT && optind == argc) - arg_action = ACTION_LIST; - - else if (arg_action == ACTION_INHIBIT && optind >= argc) { - log_error("Missing command line to execute."); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - _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(); - log_open(); - - r = parse_argv(argc, argv); - if (r < 0) - return EXIT_FAILURE; - if (r == 0) - return EXIT_SUCCESS; - - r = sd_bus_default_system(&bus); - if (r < 0) { - log_error_errno(r, "Failed to connect to bus: %m"); - return EXIT_FAILURE; - } - - if (arg_action == ACTION_LIST) { - - r = print_inhibitors(bus, &error); - if (r < 0) { - log_error("Failed to list inhibitors: %s", bus_error_message(&error, -r)); - return EXIT_FAILURE; - } - - } else { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *w = NULL; - pid_t pid; - - if (!arg_who) - arg_who = w = strv_join(argv + optind, " "); - - if (!arg_mode) - arg_mode = "block"; - - fd = inhibit(bus, &error); - if (fd < 0) { - log_error("Failed to inhibit: %s", bus_error_message(&error, fd)); - return EXIT_FAILURE; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return EXIT_FAILURE; - } - - if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - close_all_fds(NULL, 0); - - execvp(argv[optind], argv + optind); - log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn(argv[optind], pid, true); - return r < 0 ? EXIT_FAILURE : r; - } - - return 0; -} diff --git a/src/login/loginctl.c b/src/login/loginctl.c deleted file mode 100644 index 0fc33cf541..0000000000 --- a/src/login/loginctl.c +++ /dev/null @@ -1,1587 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-unit-util.h" -#include "bus-util.h" -#include "cgroup-show.h" -#include "cgroup-util.h" -#include "log.h" -#include "logs-show.h" -#include "macro.h" -#include "pager.h" -#include "parse-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "spawn-polkit-agent.h" -#include "strv.h" -#include "sysfs-show.h" -#include "terminal-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; -static bool arg_value = false; -static bool arg_full = false; -static bool arg_no_pager = false; -static bool arg_legend = true; -static const char *arg_kill_who = NULL; -static int arg_signal = SIGTERM; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_ask_password = true; -static unsigned arg_lines = 10; -static OutputMode arg_output = OUTPUT_SHORT; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -static OutputFlags get_output_flags(void) { - - return - arg_all * OUTPUT_SHOW_ALL | - arg_full * OUTPUT_FULL_WIDTH | - (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - colors_enabled() * OUTPUT_COLOR; -} - -static int list_sessions(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 *id, *user, *seat, *object; - sd_bus *bus = userdata; - unsigned k = 0; - uint32_t uid; - int r; - - assert(bus); - assert(argv); - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSessions", - &error, &reply, - ""); - if (r < 0) { - log_error("Failed to list sessions: %s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "(susso)"); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT"); - - while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) { - printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat); - k++; - } - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("\n%u sessions listed.\n", k); - - return 0; -} - -static int list_users(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 *user, *object; - sd_bus *bus = userdata; - unsigned k = 0; - uint32_t uid; - int r; - - assert(bus); - assert(argv); - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListUsers", - &error, &reply, - ""); - if (r < 0) { - log_error("Failed to list users: %s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "(uso)"); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("%10s %-16s\n", "UID", "USER"); - - while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) { - printf("%10u %-16s\n", (unsigned) uid, user); - k++; - } - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("\n%u users listed.\n", k); - - return 0; -} - -static int list_seats(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 *seat, *object; - sd_bus *bus = userdata; - unsigned k = 0; - int r; - - assert(bus); - assert(argv); - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListSeats", - &error, &reply, - ""); - if (r < 0) { - log_error("Failed to list seats: %s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "(so)"); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("%-16s\n", "SEAT"); - - while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) { - printf("%-16s\n", seat); - k++; - } - if (r < 0) - return bus_log_parse_error(r); - - if (arg_legend) - printf("\n%u seats listed.\n", k); - - return 0; -} - -static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) { - _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; - unsigned c; - int r; - - assert(bus); - assert(unit); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - interface, - "ControlGroup", - &error, - &reply, - "s"); - if (r < 0) - return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) - return bus_log_parse_error(r); - - if (isempty(cgroup)) - return 0; - - c = columns(); - if (c > 18) - c -= 18; - else - c = 0; - - r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); - if (r == -EBADR) { - - if (arg_transport == BUS_TRANSPORT_REMOTE) - return 0; - - /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ - - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) - return 0; - - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); - } else if (r < 0) - return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); - - return 0; -} - -typedef struct SessionStatusInfo { - char *id; - uid_t uid; - char *name; - struct dual_timestamp timestamp; - unsigned int vtnr; - char *seat; - char *tty; - char *display; - int remote; - char *remote_host; - char *remote_user; - char *service; - pid_t leader; - char *type; - char *class; - char *state; - char *scope; - char *desktop; -} SessionStatusInfo; - -typedef struct UserStatusInfo { - uid_t uid; - int linger; - char *name; - struct dual_timestamp timestamp; - char *state; - char **sessions; - char *display; - char *slice; -} UserStatusInfo; - -typedef struct SeatStatusInfo { - char *id; - char *active_session; - char **sessions; -} SeatStatusInfo; - -static void session_status_info_clear(SessionStatusInfo *info) { - if (info) { - free(info->id); - free(info->name); - free(info->seat); - free(info->tty); - free(info->display); - free(info->remote_host); - free(info->remote_user); - free(info->service); - free(info->type); - free(info->class); - free(info->state); - free(info->scope); - free(info->desktop); - zero(*info); - } -} - -static void user_status_info_clear(UserStatusInfo *info) { - if (info) { - free(info->name); - free(info->state); - strv_free(info->sessions); - free(info->display); - free(info->slice); - zero(*info); - } -} - -static void seat_status_info_clear(SeatStatusInfo *info) { - if (info) { - free(info->id); - free(info->active_session); - strv_free(info->sessions); - zero(*info); - } -} - -static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - const char *contents; - int r; - - r = sd_bus_message_peek_type(m, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents); - if (r < 0) - return r; - - if (contents[0] == 's' || contents[0] == 'o') { - const char *s; - char **p = (char **) userdata; - - r = sd_bus_message_read_basic(m, contents[0], &s); - if (r < 0) - return r; - - r = free_and_strdup(p, s); - if (r < 0) - return r; - } else { - r = sd_bus_message_read_basic(m, contents[0], userdata); - if (r < 0) - return r; - } - - r = sd_bus_message_skip(m, contents+1); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - const char *name; - int r; - - assert(bus); - assert(m); - - r = sd_bus_message_enter_container(m, 'a', "(so)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) { - r = strv_extend(userdata, name); - if (r < 0) - return r; - } - if (r < 0) - return r; - - return sd_bus_message_exit_container(m); -} - -static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) { - - static const struct bus_properties_map map[] = { - { "Id", "s", NULL, offsetof(SessionStatusInfo, id) }, - { "Name", "s", NULL, offsetof(SessionStatusInfo, name) }, - { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) }, - { "Display", "s", NULL, offsetof(SessionStatusInfo, display) }, - { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) }, - { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) }, - { "Service", "s", NULL, offsetof(SessionStatusInfo, service) }, - { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) }, - { "Type", "s", NULL, offsetof(SessionStatusInfo, type) }, - { "Class", "s", NULL, offsetof(SessionStatusInfo, class) }, - { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) }, - { "State", "s", NULL, offsetof(SessionStatusInfo, state) }, - { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) }, - { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) }, - { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) }, - { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) }, - { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) }, - { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) }, - { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) }, - {} - }; - - char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; - char since2[FORMAT_TIMESTAMP_MAX], *s2; - _cleanup_(session_status_info_clear) SessionStatusInfo i = {}; - int r; - - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - if (*new_line) - printf("\n"); - - *new_line = true; - - printf("%s - ", strna(i.id)); - - if (i.name) - printf("%s (%u)\n", i.name, (unsigned) i.uid); - else - printf("%u\n", (unsigned) i.uid); - - s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime); - s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime); - - if (s1) - printf("\t Since: %s; %s\n", s2, s1); - else if (s2) - printf("\t Since: %s\n", s2); - - if (i.leader > 0) { - _cleanup_free_ char *t = NULL; - - printf("\t Leader: %u", (unsigned) i.leader); - - get_process_comm(i.leader, &t); - if (t) - printf(" (%s)", t); - - printf("\n"); - } - - if (!isempty(i.seat)) { - printf("\t Seat: %s", i.seat); - - if (i.vtnr > 0) - printf("; vc%u", i.vtnr); - - printf("\n"); - } - - if (i.tty) - printf("\t TTY: %s\n", i.tty); - else if (i.display) - printf("\t Display: %s\n", i.display); - - if (i.remote_host && i.remote_user) - printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host); - else if (i.remote_host) - printf("\t Remote: %s\n", i.remote_host); - else if (i.remote_user) - printf("\t Remote: user %s\n", i.remote_user); - else if (i.remote) - printf("\t Remote: Yes\n"); - - if (i.service) { - printf("\t Service: %s", i.service); - - if (i.type) - printf("; type %s", i.type); - - if (i.class) - printf("; class %s", i.class); - - printf("\n"); - } else if (i.type) { - printf("\t Type: %s", i.type); - - if (i.class) - printf("; class %s", i.class); - - printf("\n"); - } else if (i.class) - printf("\t Class: %s\n", i.class); - - if (!isempty(i.desktop)) - printf("\t Desktop: %s\n", i.desktop); - - if (i.state) - printf("\t State: %s\n", i.state); - - if (i.scope) { - printf("\t Unit: %s\n", i.scope); - show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader); - - if (arg_transport == BUS_TRANSPORT_LOCAL) { - - show_journal_by_unit( - stdout, - i.scope, - arg_output, - 0, - i.timestamp.monotonic, - arg_lines, - 0, - get_output_flags() | OUTPUT_BEGIN_NEWLINE, - SD_JOURNAL_LOCAL_ONLY, - true, - NULL); - } - } - - return 0; -} - -static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) { - - static const struct bus_properties_map map[] = { - { "Name", "s", NULL, offsetof(UserStatusInfo, name) }, - { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) }, - { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) }, - { "State", "s", NULL, offsetof(UserStatusInfo, state) }, - { "UID", "u", NULL, offsetof(UserStatusInfo, uid) }, - { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) }, - { "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) }, - { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) }, - { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) }, - {} - }; - - char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; - char since2[FORMAT_TIMESTAMP_MAX], *s2; - _cleanup_(user_status_info_clear) UserStatusInfo i = {}; - int r; - - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - if (*new_line) - printf("\n"); - - *new_line = true; - - if (i.name) - printf("%s (%u)\n", i.name, (unsigned) i.uid); - else - printf("%u\n", (unsigned) i.uid); - - s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime); - s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime); - - if (s1) - printf("\t Since: %s; %s\n", s2, s1); - else if (s2) - printf("\t Since: %s\n", s2); - - if (!isempty(i.state)) - printf("\t State: %s\n", i.state); - - if (!strv_isempty(i.sessions)) { - char **l; - printf("\tSessions:"); - - STRV_FOREACH(l, i.sessions) - printf(" %s%s", - streq_ptr(*l, i.display) ? "*" : "", - *l); - - printf("\n"); - } - - printf("\t Linger: %s\n", yes_no(i.linger)); - - if (i.slice) { - printf("\t Unit: %s\n", i.slice); - show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0); - - show_journal_by_unit( - stdout, - i.slice, - arg_output, - 0, - i.timestamp.monotonic, - arg_lines, - 0, - get_output_flags() | OUTPUT_BEGIN_NEWLINE, - SD_JOURNAL_LOCAL_ONLY, - true, - NULL); - } - - return 0; -} - -static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) { - - static const struct bus_properties_map map[] = { - { "Id", "s", NULL, offsetof(SeatStatusInfo, id) }, - { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) }, - { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) }, - {} - }; - - _cleanup_(seat_status_info_clear) SeatStatusInfo i = {}; - int r; - - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - if (*new_line) - printf("\n"); - - *new_line = true; - - printf("%s\n", strna(i.id)); - - if (!strv_isempty(i.sessions)) { - char **l; - printf("\tSessions:"); - - STRV_FOREACH(l, i.sessions) { - if (streq_ptr(*l, i.active_session)) - printf(" *%s", *l); - else - printf(" %s", *l); - } - - printf("\n"); - } - - if (arg_transport == BUS_TRANSPORT_LOCAL) { - unsigned c; - - c = columns(); - if (c > 21) - c -= 21; - else - c = 0; - - printf("\t Devices:\n"); - - show_sysfs(i.id, "\t\t ", c); - } - - return 0; -} - -#define property(name, fmt, ...) \ - do { \ - if (arg_value) \ - printf(fmt "\n", __VA_ARGS__); \ - else \ - printf("%s=" fmt "\n", name, __VA_ARGS__); \ - } while(0) - -static int print_property(const char *name, sd_bus_message *m, const char *contents) { - int r; - - assert(name); - assert(m); - assert(contents); - - if (arg_property && !strv_find(arg_property, name)) - /* skip what we didn't read */ - return sd_bus_message_skip(m, contents); - - switch (contents[0]) { - - case SD_BUS_TYPE_STRUCT_BEGIN: - - if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) { - const char *s; - - r = sd_bus_message_read(m, "(so)", &s, NULL); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all || !isempty(s)) - property(name, "%s", s); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) { - uint32_t uid; - - r = sd_bus_message_read(m, "(uo)", &uid, NULL); - if (r < 0) - return bus_log_parse_error(r); - - if (!uid_is_valid(uid)) { - log_error("Invalid user ID: " UID_FMT, uid); - return -EINVAL; - } - - property(name, UID_FMT, uid); - return 0; - } - - break; - - case SD_BUS_TYPE_ARRAY: - - if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) { - const char *s; - bool space = false; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)"); - if (r < 0) - return bus_log_parse_error(r); - - if (!arg_value) - printf("%s=", name); - - while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) { - printf("%s%s", space ? " " : "", s); - space = true; - } - - if (space || !arg_value) - printf("\n"); - - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - } - - break; - } - - r = bus_print_property(name, m, arg_value, arg_all); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0) { - r = sd_bus_message_skip(m, contents); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all) - printf("%s=[unprintable]\n", name); - } - - return 0; -} - -static int show_properties(sd_bus *bus, const char *path, bool *new_line) { - _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); - assert(path); - assert(new_line); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); - if (r < 0) - return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return bus_log_parse_error(r); - - if (*new_line) - printf("\n"); - - *new_line = true; - - while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *name, *contents; - - r = sd_bus_message_read(reply, "s", &name); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_peek_type(reply, NULL, &contents); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return bus_log_parse_error(r); - - r = print_property(name, reply, contents); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - 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 (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - return 0; -} - -static int show_session(int argc, char *argv[], void *userdata) { - bool properties, new_line = false; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - properties = !strstr(argv[0], "status"); - - pager_open(arg_no_pager, false); - - if (argc <= 1) { - /* If not argument is specified inspect the manager - * itself */ - if (properties) - return show_properties(bus, "/org/freedesktop/login1", &new_line); - - /* And in the pretty case, show data of the calling session */ - return print_session_status_info(bus, "/org/freedesktop/login1/session/self", &new_line); - } - - for (i = 1; i < argc; i++) { - _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( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "GetSession", - &error, &reply, - "s", argv[i]); - if (r < 0) { - log_error("Failed to get session: %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); - - if (properties) - r = show_properties(bus, path, &new_line); - else - r = print_session_status_info(bus, path, &new_line); - - if (r < 0) - return r; - } - - return 0; -} - -static int show_user(int argc, char *argv[], void *userdata) { - bool properties, new_line = false; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - properties = !strstr(argv[0], "status"); - - pager_open(arg_no_pager, false); - - if (argc <= 1) { - /* If not argument is specified inspect the manager - * itself */ - if (properties) - return show_properties(bus, "/org/freedesktop/login1", &new_line); - - return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line); - } - - for (i = 1; i < argc; i++) { - _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; - - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "GetUser", - &error, &reply, - "u", (uint32_t) uid); - if (r < 0) { - log_error("Failed to get user: %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); - - if (properties) - r = show_properties(bus, path, &new_line); - else - r = print_user_status_info(bus, path, &new_line); - - if (r < 0) - return r; - } - - return 0; -} - -static int show_seat(int argc, char *argv[], void *userdata) { - bool properties, new_line = false; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - properties = !strstr(argv[0], "status"); - - pager_open(arg_no_pager, false); - - if (argc <= 1) { - /* If not argument is specified inspect the manager - * itself */ - if (properties) - return show_properties(bus, "/org/freedesktop/login1", &new_line); - - return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line); - } - - for (i = 1; i < argc; i++) { - _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( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "GetSeat", - &error, &reply, - "s", argv[i]); - if (r < 0) { - log_error("Failed to get seat: %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); - - if (properties) - r = show_properties(bus, path, &new_line); - else - r = print_seat_status_info(bus, path, &new_line); - - if (r < 0) - return r; - } - - return 0; -} - -static int activate(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - char *short_argv[3]; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - if (argc < 2) { - /* No argument? Let's convert this into the empty - * session name, which the calls will then resolve to - * the caller's session. */ - - short_argv[0] = argv[0]; - short_argv[1] = (char*) ""; - short_argv[2] = NULL; - - argv = short_argv; - argc = 2; - } - - for (i = 1; i < argc; i++) { - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - streq(argv[0], "lock-session") ? "LockSession" : - streq(argv[0], "unlock-session") ? "UnlockSession" : - streq(argv[0], "terminate-session") ? "TerminateSession" : - "ActivateSession", - &error, NULL, - "s", argv[i]); - if (r < 0) { - log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int kill_session(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - if (!arg_kill_who) - arg_kill_who = "all"; - - for (i = 1; i < argc; i++) { - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "KillSession", - &error, NULL, - "ssi", argv[i], arg_kill_who, arg_signal); - if (r < 0) { - log_error("Could not kill session: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int enable_linger(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - char* short_argv[3]; - bool b; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - b = streq(argv[0], "enable-linger"); - - if (argc < 2) { - short_argv[0] = argv[0]; - short_argv[1] = (char*) ""; - short_argv[2] = NULL; - argv = short_argv; - argc = 2; - } - - for (i = 1; i < argc; i++) { - uid_t uid; - - if (isempty(argv[i])) - uid = UID_INVALID; - else { - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - } - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "SetUserLinger", - &error, NULL, - "ubb", (uint32_t) uid, b, true); - if (r < 0) { - log_error("Could not enable linger: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int terminate_user(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - for (i = 1; i < argc; i++) { - uid_t uid; - - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "TerminateUser", - &error, NULL, - "u", (uint32_t) uid); - if (r < 0) { - log_error("Could not terminate user: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int kill_user(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - if (!arg_kill_who) - arg_kill_who = "all"; - - for (i = 1; i < argc; i++) { - uid_t uid; - - r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to look up user %s: %m", argv[i]); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "KillUser", - &error, NULL, - "ui", (uint32_t) uid, arg_signal); - if (r < 0) { - log_error("Could not kill user: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int attach(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - for (i = 2; i < argc; i++) { - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "AttachDevice", - &error, NULL, - "ssb", argv[1], argv[i], true); - - if (r < 0) { - log_error("Could not attach device: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int flush_devices(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "FlushDevices", - &error, NULL, - "b", true); - if (r < 0) - log_error("Could not flush devices: %s", bus_error_message(&error, -r)); - - return r; -} - -static int lock_sessions(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions", - &error, NULL, - NULL); - if (r < 0) - log_error("Could not lock sessions: %s", bus_error_message(&error, -r)); - - return r; -} - -static int terminate_seat(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - assert(argv); - - polkit_agent_open_if_enabled(); - - for (i = 1; i < argc; i++) { - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "TerminateSeat", - &error, NULL, - "s", argv[i]); - if (r < 0) { - log_error("Could not terminate seat: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Send control commands to or query the login manager.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --no-ask-password Don't prompt for password\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " -p --property=NAME Show only properties by this name\n" - " -a --all Show all properties, including empty ones\n" - " --value When showing properties, only print the value\n" - " -l --full Do not ellipsize output\n" - " --kill-who=WHO Who to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" - " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, json-pretty, json-sse, cat)\n\n" - "Session Commands:\n" - " list-sessions List sessions\n" - " session-status [ID...] Show session status\n" - " show-session [ID...] Show properties of sessions or the manager\n" - " activate [ID] Activate a session\n" - " lock-session [ID...] Screen lock one or more sessions\n" - " unlock-session [ID...] Screen unlock one or more sessions\n" - " lock-sessions Screen lock all current sessions\n" - " unlock-sessions Screen unlock all current sessions\n" - " terminate-session ID... Terminate one or more sessions\n" - " kill-session ID... Send signal to processes of a session\n\n" - "User Commands:\n" - " list-users List users\n" - " user-status [USER...] Show user status\n" - " show-user [USER...] Show properties of users or the manager\n" - " enable-linger [USER...] Enable linger state of one or more users\n" - " disable-linger [USER...] Disable linger state of one or more users\n" - " terminate-user USER... Terminate all sessions of one or more users\n" - " kill-user USER... Send signal to processes of a user\n\n" - "Seat Commands:\n" - " list-seats List seats\n" - " seat-status [NAME...] Show seat status\n" - " show-seat [NAME...] Show properties of seats or the manager\n" - " attach NAME DEVICE... Attach one or more devices to a seat\n" - " flush-devices Flush all device associations\n" - " terminate-seat NAME... Terminate all sessions on one or more seats\n" - , program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_VALUE, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_KILL_WHO, - ARG_NO_ASK_PASSWORD, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "value", no_argument, NULL, ARG_VALUE }, - { "full", no_argument, NULL, 'l' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "kill-who", required_argument, NULL, ARG_KILL_WHO }, - { "signal", required_argument, NULL, 's' }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "lines", required_argument, NULL, 'n' }, - { "output", required_argument, NULL, 'o' }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(0, NULL, NULL); - return 0; - - case ARG_VERSION: - return version(); - - case 'p': { - r = strv_extend(&arg_property, optarg); - if (r < 0) - return log_oom(); - - /* If the user asked for a particular - * property, show it to him, even if it is - * empty. */ - arg_all = true; - break; - } - - case 'a': - arg_all = true; - break; - - case ARG_VALUE: - arg_value = true; - break; - - case 'l': - arg_full = true; - break; - - case 'n': - if (safe_atou(optarg, &arg_lines) < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } - break; - - case 'o': - arg_output = output_mode_from_string(optarg); - if (arg_output < 0) { - log_error("Unknown output '%s'.", optarg); - return -EINVAL; - } - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_legend = false; - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case ARG_KILL_WHO: - arg_kill_who = optarg; - break; - - case 's': - arg_signal = signal_from_string_try_harder(optarg); - if (arg_signal < 0) { - log_error("Failed to parse signal string %s.", optarg); - return -EINVAL; - } - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int loginctl_main(int argc, char *argv[], sd_bus *bus) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions }, - { "session-status", VERB_ANY, VERB_ANY, 0, show_session }, - { "show-session", VERB_ANY, VERB_ANY, 0, show_session }, - { "activate", VERB_ANY, 2, 0, activate }, - { "lock-session", VERB_ANY, VERB_ANY, 0, activate }, - { "unlock-session", VERB_ANY, VERB_ANY, 0, activate }, - { "lock-sessions", VERB_ANY, 1, 0, lock_sessions }, - { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions }, - { "terminate-session", 2, VERB_ANY, 0, activate }, - { "kill-session", 2, VERB_ANY, 0, kill_session }, - { "list-users", VERB_ANY, 1, 0, list_users }, - { "user-status", VERB_ANY, VERB_ANY, 0, show_user }, - { "show-user", VERB_ANY, VERB_ANY, 0, show_user }, - { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, - { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger }, - { "terminate-user", 2, VERB_ANY, 0, terminate_user }, - { "kill-user", 2, VERB_ANY, 0, kill_user }, - { "list-seats", VERB_ANY, 1, 0, list_seats }, - { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat }, - { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat }, - { "attach", 3, VERB_ANY, 0, attach }, - { "flush-devices", VERB_ANY, 1, 0, flush_devices }, - { "terminate-seat", 2, VERB_ANY, 0, terminate_seat }, - {} - }; - - return dispatch_verb(argc, argv, verbs, bus); -} - -int main(int argc, char *argv[]) { - sd_bus *bus = NULL; - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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; - } - - sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); - - r = loginctl_main(argc, argv, bus); - -finish: - sd_bus_flush_close_unref(bus); - - pager_close(); - polkit_agent_close(); - - strv_free(arg_property); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/login/logind-acl.c b/src/login/logind-acl.c deleted file mode 100644 index 0cef88a82d..0000000000 --- a/src/login/logind-acl.c +++ /dev/null @@ -1,292 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "acl-util.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; - int found; - bool changed = false; - - assert(acl); - - for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - found > 0; - found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - acl_tag_t tag; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - if (acl_delete_entry(acl, i) < 0) - return -errno; - - changed = true; - } - - if (found < 0) - return -errno; - - return changed; -} - -int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - - acl_t acl; - int r = 0; - bool changed = false; - - assert(path); - - acl = acl_get_file(path, ACL_TYPE_ACCESS); - if (!acl) - return -errno; - - if (flush) { - - r = flush_acl(acl); - if (r < 0) - goto finish; - if (r > 0) - changed = true; - - } else if (del && old_uid > 0) { - acl_entry_t entry; - - r = acl_find_uid(acl, old_uid, &entry); - if (r < 0) - goto finish; - - if (r > 0) { - if (acl_delete_entry(acl, entry) < 0) { - r = -errno; - goto finish; - } - - changed = true; - } - } - - if (add && new_uid > 0) { - acl_entry_t entry; - acl_permset_t permset; - int rd, wt; - - r = acl_find_uid(acl, new_uid, &entry); - if (r < 0) - goto finish; - - if (r == 0) { - if (acl_create_entry(&acl, &entry) < 0) { - r = -errno; - goto finish; - } - - if (acl_set_tag_type(entry, ACL_USER) < 0 || - acl_set_qualifier(entry, &new_uid) < 0) { - r = -errno; - goto finish; - } - } - - if (acl_get_permset(entry, &permset) < 0) { - r = -errno; - goto finish; - } - - rd = acl_get_perm(permset, ACL_READ); - if (rd < 0) { - r = -errno; - goto finish; - } - - wt = acl_get_perm(permset, ACL_WRITE); - if (wt < 0) { - r = -errno; - goto finish; - } - - if (!rd || !wt) { - - if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) { - r = -errno; - goto finish; - } - - changed = true; - } - } - - if (!changed) - goto finish; - - if (acl_calc_mask(&acl) < 0) { - r = -errno; - goto finish; - } - - if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) { - r = -errno; - goto finish; - } - - r = 0; - -finish: - acl_free(acl); - - return r; -} - -int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_set_free_free_ Set *nodes = NULL; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - Iterator i; - char *n; - int r; - - assert(udev); - - nodes = set_new(&string_hash_ops); - if (!nodes) - return -ENOMEM; - - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - if (isempty(seat)) - seat = "seat0"; - - /* We can only match by one tag in libudev. We choose - * "uaccess" for that. If we could match for two tags here we - * could add the seat name as second match tag, but this would - * be hardly optimizable in libudev, and hence checking the - * second tag manually in our loop is a good solution. */ - r = udev_enumerate_add_match_tag(e, "uaccess"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - const char *node, *sn; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - if (!streq(seat, sn)) - continue; - - node = udev_device_get_devnode(d); - /* In case people mistag devices with nodes, we need to ignore this */ - if (!node) - continue; - - n = strdup(node); - if (!n) - return -ENOMEM; - - log_debug("Found udev node %s for seat %s", n, seat); - r = set_consume(nodes, n); - if (r < 0) - return r; - } - - /* udev exports "dead" device nodes to allow module on-demand loading, - * these devices are not known to the kernel at this moment */ - dir = opendir("/run/udev/static_node-tags/uaccess"); - if (dir) { - FOREACH_DIRENT(dent, dir, return -errno) { - _cleanup_free_ char *unescaped_devname = NULL; - - if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0) - return -ENOMEM; - - n = strappend("/dev/", unescaped_devname); - if (!n) - return -ENOMEM; - - log_debug("Found static node %s for seat %s", n, seat); - r = set_consume(nodes, n); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - r = 0; - SET_FOREACH(n, nodes, i) { - int k; - - log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)", - n, seat, old_uid, new_uid, - del ? " del" : "", add ? " add" : ""); - - k = devnode_acl(n, flush, del, old_uid, add, new_uid); - if (k == -ENOENT) - log_debug("Device %s disappeared while setting ACLs", n); - else if (k < 0 && r == 0) - r = k; - } - - return r; -} diff --git a/src/login/logind-acl.h b/src/login/logind-acl.h deleted file mode 100644 index 1286c6a3cd..0000000000 --- a/src/login/logind-acl.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "libudev.h" - -#ifdef HAVE_ACL - -int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid); - -int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid); -#else - -static inline int devnode_acl(const char *path, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - return 0; -} - -static inline int devnode_acl_all(struct udev *udev, - const char *seat, - bool flush, - bool del, uid_t old_uid, - bool add, uid_t new_uid) { - return 0; -} - -#endif diff --git a/src/login/logind-action.c b/src/login/logind-action.c deleted file mode 100644 index 8ef48dbaa1..0000000000 --- a/src/login/logind-action.c +++ /dev/null @@ -1,178 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "bus-error.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, - InhibitWhat inhibit_key, - HandleAction handle, - bool ignore_inhibited, - bool is_edge) { - - static const char * const message_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = "Powering Off...", - [HANDLE_REBOOT] = "Rebooting...", - [HANDLE_HALT] = "Halting...", - [HANDLE_KEXEC] = "Rebooting via kexec...", - [HANDLE_SUSPEND] = "Suspending...", - [HANDLE_HIBERNATE] = "Hibernating...", - [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." - }; - - static const char * const target_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, - [HANDLE_HALT] = SPECIAL_HALT_TARGET, - [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, - [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, - [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - InhibitWhat inhibit_operation; - Inhibitor *offending = NULL; - bool supported; - int r; - - assert(m); - - /* If the key handling is turned off, don't do anything */ - if (handle == HANDLE_IGNORE) { - log_debug("Refusing operation, as it is turned off."); - return 0; - } - - if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) { - /* If the last system suspend or startup is too close, - * let's not suspend for now, to give USB docking - * stations some time to settle so that we can - * properly watch its displays. */ - if (m->lid_switch_ignore_event_source) { - log_debug("Ignoring lid switch request, system startup or resume too close."); - return 0; - } - } - - /* If the key handling is inhibited, don't do anything */ - if (!ignore_inhibited && inhibit_key > 0) { - if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { - log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); - return 0; - } - } - - /* Locking is handled differently from the rest. */ - if (handle == HANDLE_LOCK) { - - if (!is_edge) - return 0; - - log_info("Locking sessions..."); - session_send_lock_all(m, true); - return 1; - } - - if (handle == HANDLE_SUSPEND) - supported = can_sleep("suspend") > 0; - else if (handle == HANDLE_HIBERNATE) - supported = can_sleep("hibernate") > 0; - else if (handle == HANDLE_HYBRID_SLEEP) - supported = can_sleep("hybrid-sleep") > 0; - else if (handle == HANDLE_KEXEC) - supported = access(KEXEC, X_OK) >= 0; - else - supported = true; - - if (!supported) { - log_warning("Requested operation not supported, ignoring."); - return -EOPNOTSUPP; - } - - if (m->action_what) { - log_debug("Action already in progress, ignoring."); - return -EALREADY; - } - - inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; - - /* If the actual operation is inhibited, warn and fail */ - if (!ignore_inhibited && - manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) { - _cleanup_free_ char *comm = NULL, *u = NULL; - - get_process_comm(offending->pid, &comm); - u = uid_to_name(offending->uid); - - /* If this is just a recheck of the lid switch then don't warn about anything */ - if (!is_edge) { - log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", - inhibit_what_to_string(inhibit_operation), - offending->uid, strna(u), - offending->pid, strna(comm)); - return 0; - } - - log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", - inhibit_what_to_string(inhibit_operation), - offending->uid, strna(u), - offending->pid, strna(comm)); - - return -EPERM; - } - - log_info("%s", message_table[handle]); - - r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); - return r; - } - - return 1; -} - -static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { - [HANDLE_IGNORE] = "ignore", - [HANDLE_POWEROFF] = "poweroff", - [HANDLE_REBOOT] = "reboot", - [HANDLE_HALT] = "halt", - [HANDLE_KEXEC] = "kexec", - [HANDLE_SUSPEND] = "suspend", - [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", - [HANDLE_LOCK] = "lock" -}; - -DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); -DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); diff --git a/src/login/logind-action.h b/src/login/logind-action.h deleted file mode 100644 index fb40ae48d2..0000000000 --- a/src/login/logind-action.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef enum HandleAction { - HANDLE_IGNORE, - HANDLE_POWEROFF, - HANDLE_REBOOT, - HANDLE_HALT, - HANDLE_KEXEC, - HANDLE_SUSPEND, - HANDLE_HIBERNATE, - HANDLE_HYBRID_SLEEP, - HANDLE_LOCK, - _HANDLE_ACTION_MAX, - _HANDLE_ACTION_INVALID = -1 -} HandleAction; - -#include "logind-inhibit.h" -#include "logind.h" - -int manager_handle_action( - Manager *m, - InhibitWhat inhibit_key, - HandleAction handle, - bool ignore_inhibited, - bool is_edge); - -const char* handle_action_to_string(HandleAction h) _const_; -HandleAction handle_action_from_string(const char *s) _pure_; - -int config_parse_handle_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/login/logind-button.c b/src/login/logind-button.c deleted file mode 100644 index baa6b7113c..0000000000 --- a/src/login/logind-button.c +++ /dev/null @@ -1,288 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-messages.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; - - assert(m); - assert(name); - - b = new0(Button, 1); - if (!b) - return NULL; - - b->name = strdup(name); - if (!b->name) { - free(b); - return NULL; - } - - if (hashmap_put(m->buttons, b->name, b) < 0) { - free(b->name); - free(b); - return NULL; - } - - b->manager = m; - b->fd = -1; - - return b; -} - -void button_free(Button *b) { - assert(b); - - hashmap_remove(b->manager->buttons, b->name); - - sd_event_source_unref(b->io_event_source); - sd_event_source_unref(b->check_event_source); - - if (b->fd >= 0) - /* If the device has been unplugged close() returns - * ENODEV, let's ignore this, hence we don't use - * safe_close() */ - (void) close(b->fd); - - free(b->name); - free(b->seat); - free(b); -} - -int button_set_seat(Button *b, const char *sn) { - char *s; - - assert(b); - assert(sn); - - s = strdup(sn); - if (!s) - return -ENOMEM; - - free(b->seat); - b->seat = s; - - return 0; -} - -static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { - HandleAction handle_action; - - assert(manager); - - /* If we are docked, handle the lid switch differently */ - if (manager_is_docked_or_external_displays(manager)) - handle_action = manager->handle_lid_switch_docked; - else - handle_action = manager->handle_lid_switch; - - manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge); -} - -static int button_recheck(sd_event_source *e, void *userdata) { - Button *b = userdata; - - assert(b); - assert(b->lid_closed); - - button_lid_switch_handle_action(b->manager, false); - return 1; -} - -static int button_install_check_event_source(Button *b) { - int r; - assert(b); - - /* Install a post handler, so that we keep rechecking as long as the lid is closed. */ - - if (b->check_event_source) - return 0; - - r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b); - if (r < 0) - return r; - - return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1); -} - -static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Button *b = userdata; - struct input_event ev; - ssize_t l; - - assert(s); - assert(fd == b->fd); - assert(b); - - l = read(b->fd, &ev, sizeof(ev)); - if (l < 0) - return errno != EAGAIN ? -errno : 0; - if ((size_t) l < sizeof(ev)) - return -EIO; - - if (ev.type == EV_KEY && ev.value > 0) { - - switch (ev.code) { - - case KEY_POWER: - case KEY_POWER2: - log_struct(LOG_INFO, - LOG_MESSAGE("Power key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); - break; - - /* The kernel is a bit confused here: - - KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend" - KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate" - */ - - case KEY_SLEEP: - log_struct(LOG_INFO, - LOG_MESSAGE("Suspend key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); - break; - - case KEY_SUSPEND: - log_struct(LOG_INFO, - LOG_MESSAGE("Hibernate key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), - NULL); - - manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); - break; - } - - } else if (ev.type == EV_SW && ev.value > 0) { - - if (ev.code == SW_LID) { - log_struct(LOG_INFO, - LOG_MESSAGE("Lid closed."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), - NULL); - - b->lid_closed = true; - button_lid_switch_handle_action(b->manager, true); - button_install_check_event_source(b); - - } else if (ev.code == SW_DOCK) { - log_struct(LOG_INFO, - LOG_MESSAGE("System docked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), - NULL); - - b->docked = true; - } - - } else if (ev.type == EV_SW && ev.value == 0) { - - if (ev.code == SW_LID) { - log_struct(LOG_INFO, - LOG_MESSAGE("Lid opened."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), - NULL); - - b->lid_closed = false; - b->check_event_source = sd_event_source_unref(b->check_event_source); - - } else if (ev.code == SW_DOCK) { - log_struct(LOG_INFO, - LOG_MESSAGE("System undocked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), - NULL); - - b->docked = false; - } - } - - return 0; -} - -int button_open(Button *b) { - char *p, name[256]; - int r; - - assert(b); - - b->fd = safe_close(b->fd); - - p = strjoina("/dev/input/", b->name); - - b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (b->fd < 0) - return log_warning_errno(errno, "Failed to open %s: %m", b->name); - - if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { - r = log_error_errno(errno, "Failed to get input name: %m"); - goto fail; - } - - r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); - if (r < 0) { - log_error_errno(r, "Failed to add button event: %m"); - goto fail; - } - - log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name); - - return 0; - -fail: - b->fd = safe_close(b->fd); - return r; -} - -int button_check_switches(Button *b) { - uint8_t switches[SW_MAX/8+1] = {}; - assert(b); - - if (b->fd < 0) - return -EINVAL; - - if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0) - return -errno; - - b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1; - b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1; - - if (b->lid_closed) - button_install_check_event_source(b); - - return 0; -} diff --git a/src/login/logind-button.h b/src/login/logind-button.h deleted file mode 100644 index f30cba2959..0000000000 --- a/src/login/logind-button.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Button Button; - -#include "logind.h" - -struct Button { - Manager *manager; - - sd_event_source *io_event_source; - sd_event_source *check_event_source; - - char *name; - char *seat; - int fd; - - bool lid_closed; - bool docked; -}; - -Button* button_new(Manager *m, const char *name); -void button_free(Button*b); -int button_open(Button *b); -int button_set_seat(Button *b, const char *sn); -int button_check_switches(Button *b); diff --git a/src/login/logind-core.c b/src/login/logind-core.c deleted file mode 100644 index eff5a4a36f..0000000000 --- a/src/login/logind-core.c +++ /dev/null @@ -1,558 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bus-error.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; - - assert(m); - assert(sysfs); - - d = hashmap_get(m->devices, sysfs); - if (d) - /* we support adding master-flags, but not removing them */ - d->master = d->master || master; - else { - d = device_new(m, sysfs, master); - if (!d) - return -ENOMEM; - } - - if (_device) - *_device = d; - - return 0; -} - -int manager_add_seat(Manager *m, const char *id, Seat **_seat) { - Seat *s; - - assert(m); - assert(id); - - s = hashmap_get(m->seats, id); - if (!s) { - s = seat_new(m, id); - if (!s) - return -ENOMEM; - } - - if (_seat) - *_seat = s; - - return 0; -} - -int manager_add_session(Manager *m, const char *id, Session **_session) { - Session *s; - - assert(m); - assert(id); - - s = hashmap_get(m->sessions, id); - if (!s) { - s = session_new(m, id); - if (!s) - return -ENOMEM; - } - - if (_session) - *_session = s; - - return 0; -} - -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { - User *u; - int r; - - assert(m); - assert(name); - - u = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!u) { - r = user_new(&u, m, uid, gid, name); - if (r < 0) - return r; - } - - if (_user) - *_user = u; - - return 0; -} - -int manager_add_user_by_name(Manager *m, const char *name, User **_user) { - uid_t uid; - gid_t gid; - int r; - - assert(m); - assert(name); - - r = get_user_creds(&name, &uid, &gid, NULL, NULL); - if (r < 0) - return r; - - return manager_add_user(m, uid, gid, name, _user); -} - -int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { - struct passwd *p; - - assert(m); - - errno = 0; - p = getpwuid(uid); - if (!p) - return errno > 0 ? -errno : -ENOENT; - - return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); -} - -int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { - Inhibitor *i; - - assert(m); - assert(id); - - i = hashmap_get(m->inhibitors, id); - if (i) { - if (_inhibitor) - *_inhibitor = i; - - return 0; - } - - i = inhibitor_new(m, id); - if (!i) - return -ENOMEM; - - if (_inhibitor) - *_inhibitor = i; - - return 0; -} - -int manager_add_button(Manager *m, const char *name, Button **_button) { - Button *b; - - assert(m); - assert(name); - - b = hashmap_get(m->buttons, name); - if (!b) { - b = button_new(m, name); - if (!b) - return -ENOMEM; - } - - if (_button) - *_button = b; - - return 0; -} - -int manager_process_seat_device(Manager *m, struct udev_device *d) { - Device *device; - int r; - - assert(m); - - if (streq_ptr(udev_device_get_action(d), "remove")) { - - device = hashmap_get(m->devices, udev_device_get_syspath(d)); - if (!device) - return 0; - - seat_add_to_gc_queue(device->seat); - device_free(device); - - } else { - const char *sn; - Seat *seat = NULL; - bool master; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - if (!seat_name_is_valid(sn)) { - log_warning("Device with invalid seat name %s found, ignoring.", sn); - return 0; - } - - seat = hashmap_get(m->seats, sn); - master = udev_device_has_tag(d, "master-of-seat"); - - /* Ignore non-master devices for unknown seats */ - if (!master && !seat) - return 0; - - r = manager_add_device(m, udev_device_get_syspath(d), master, &device); - if (r < 0) - return r; - - if (!seat) { - r = manager_add_seat(m, sn, &seat); - if (r < 0) { - if (!device->seat) - device_free(device); - - return r; - } - } - - device_attach(device, seat); - seat_start(seat); - } - - return 0; -} - -int manager_process_button_device(Manager *m, struct udev_device *d) { - Button *b; - - int r; - - assert(m); - - if (streq_ptr(udev_device_get_action(d), "remove")) { - - b = hashmap_get(m->buttons, udev_device_get_sysname(d)); - if (!b) - return 0; - - button_free(b); - - } else { - const char *sn; - - r = manager_add_button(m, udev_device_get_sysname(d), &b); - if (r < 0) - return r; - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - button_set_seat(b, sn); - button_open(b); - } - - return 0; -} - -int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) { - _cleanup_free_ char *unit = NULL; - Session *s; - int r; - - assert(m); - - if (pid < 1) - return -EINVAL; - - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - return 0; - - s = hashmap_get(m->session_units, unit); - if (!s) - return 0; - - if (session) - *session = s; - return 1; -} - -int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) { - _cleanup_free_ char *unit = NULL; - User *u; - int r; - - assert(m); - assert(user); - - if (pid < 1) - return -EINVAL; - - r = cg_pid_get_slice(pid, &unit); - if (r < 0) - return 0; - - u = hashmap_get(m->user_units, unit); - if (!u) - return 0; - - *user = u; - return 1; -} - -int manager_get_idle_hint(Manager *m, dual_timestamp *t) { - Session *s; - bool idle_hint; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - Iterator i; - - assert(m); - - idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL); - - HASHMAP_FOREACH(s, m->sessions, i) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(s, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic < ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -bool manager_shall_kill(Manager *m, const char *user) { - assert(m); - assert(user); - - if (!m->kill_exclude_users && streq(user, "root")) - return false; - - if (strv_contains(m->kill_exclude_users, user)) - return false; - - if (!strv_isempty(m->kill_only_users)) - return strv_contains(m->kill_only_users, user); - - return m->kill_user_processes; -} - -static int vt_is_busy(unsigned int vtnr) { - struct vt_stat vt_stat; - int r = 0; - _cleanup_close_ int fd; - - assert(vtnr >= 1); - - /* We explicitly open /dev/tty1 here instead of /dev/tty0. If - * we'd open the latter we'd open the foreground tty which - * hence would be unconditionally busy. By opening /dev/tty1 - * we avoid this. Since tty1 is special and needs to be an - * explicitly loaded getty or DM this is safe. */ - - fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) - r = -errno; - else - r = !!(vt_stat.v_state & (1 << vtnr)); - - return r; -} - -int manager_spawn_autovt(Manager *m, unsigned int vtnr) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)]; - int r; - - assert(m); - assert(vtnr >= 1); - - if (vtnr > m->n_autovts && - vtnr != m->reserve_vt) - return 0; - - if (vtnr != m->reserve_vt) { - /* If this is the reserved TTY, we'll start the getty - * on it in any case, but otherwise only if it is not - * busy. */ - - r = vt_is_busy(vtnr); - if (r < 0) - return r; - else if (r > 0) - return -EBUSY; - } - - snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr); - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - &error, - NULL, - "ss", name, "fail"); - if (r < 0) - log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); - - return r; -} - -static bool manager_is_docked(Manager *m) { - Iterator i; - Button *b; - - HASHMAP_FOREACH(b, m->buttons, i) - if (b->docked) - return true; - - return false; -} - -static int manager_count_external_displays(Manager *m) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - int r; - int n = 0; - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_subsystem(e, "drm"); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - struct udev_device *p; - const char *status, *enabled, *dash, *nn, *i; - bool external = false; - - d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - p = udev_device_get_parent(d); - if (!p) - continue; - - /* If the parent shares the same subsystem as the - * device we are looking at then it is a connector, - * which is what we are interested in. */ - if (!streq_ptr(udev_device_get_subsystem(p), "drm")) - continue; - - nn = udev_device_get_sysname(d); - if (!nn) - continue; - - /* Ignore internal displays: the type is encoded in - * the sysfs name, as the second dash separated item - * (the first is the card name, the last the connector - * number). We implement a whitelist of external - * displays here, rather than a whitelist, to ensure - * we don't block suspends too eagerly. */ - dash = strchr(nn, '-'); - if (!dash) - continue; - - dash++; - FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" - "Composite-", "SVIDEO-", "Component-", - "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") { - - if (startswith(dash, i)) { - external = true; - break; - } - } - if (!external) - continue; - - /* Ignore ports that are not enabled */ - enabled = udev_device_get_sysattr_value(d, "enabled"); - if (!enabled) - continue; - if (!streq_ptr(enabled, "enabled")) - continue; - - /* We count any connector which is not explicitly - * "disconnected" as connected. */ - status = udev_device_get_sysattr_value(d, "status"); - if (!streq_ptr(status, "disconnected")) - n++; - } - - return n; -} - -bool manager_is_docked_or_external_displays(Manager *m) { - int n; - - /* If we are docked don't react to lid closing */ - if (manager_is_docked(m)) { - log_debug("System is docked."); - return true; - } - - /* If we have more than one display connected, - * assume that we are docked. */ - n = manager_count_external_displays(m); - if (n < 0) - log_warning_errno(n, "Display counting failed: %m"); - else if (n >= 1) { - log_debug("External (%i) displays connected.", n); - return true; - } - - return false; -} diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c deleted file mode 100644 index 0a84d75e24..0000000000 --- a/src/login/logind-dbus.c +++ /dev/null @@ -1,3169 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "audit-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "dirent-util.h" -#include "efivars.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_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *session; - int r; - - assert(m); - assert(message); - assert(ret); - - if (isempty(name)) { - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - } - - session = hashmap_get(m->sessions, name); - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); - - *ret = session; - return 0; -} - -int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) { - User *user; - int r; - - assert(m); - assert(message); - assert(ret); - - if (uid == UID_INVALID) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - /* Note that we get the owner UID of the session, not the actual client UID here! */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r < 0) - return r; - } - - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "No user "UID_FMT" known or logged in", uid); - - *ret = user; - return 0; -} - -int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) { - Seat *seat; - int r; - - assert(m); - assert(message); - assert(ret); - - if (isempty(name)) { - Session *session; - - r = manager_get_session_from_creds(m, message, NULL, error, &session); - if (r < 0) - return r; - - seat = session->seat; - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat."); - } else { - seat = hashmap_get(m->seats, name); - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", name); - } - - *ret = seat; - return 0; -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "b", manager_get_idle_hint(m, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - - assert(bus); - assert(reply); - assert(m); - - manager_get_idle_hint(m, &t); - - return sd_bus_message_append(reply, "t", streq(property, "IdleSinceHint") ? t.realtime : t.monotonic); -} - -static int property_get_inhibited( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - InhibitWhat w; - - assert(bus); - assert(reply); - assert(m); - - w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY); - - return sd_bus_message_append(reply, "s", inhibit_what_to_string(w)); -} - -static int property_get_preparing( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - bool b; - - assert(bus); - assert(reply); - assert(m); - - if (streq(property, "PreparingForShutdown")) - b = !!(m->action_what & INHIBIT_SHUTDOWN); - else - b = !!(m->action_what & INHIBIT_SLEEP); - - return sd_bus_message_append(reply, "b", b); -} - -static int property_get_scheduled_shutdown( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - int r; - - assert(bus); - assert(reply); - assert(m); - - r = sd_bus_message_open_container(reply, 'r', "st"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); - if (r < 0) - return r; - - return sd_bus_message_close_container(reply); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); - -static int property_get_docked( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); -} - -static int property_get_current_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); -} - -static int property_get_current_inhibitors( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Manager *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); -} - -static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Session *session = NULL; - Manager *m = userdata; - pid_t pid; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - if (pid < 0) - return -EINVAL; - - if (pid == 0) { - r = manager_get_session_from_creds(m, message, NULL, error, &session); - if (r < 0) - return r; - } else { - r = manager_get_session_by_pid(m, pid, &session); - if (r < 0) - return r; - - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid); - } - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - User *user = NULL; - pid_t pid; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - if (pid < 0) - return -EINVAL; - - if (pid == 0) { - r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); - if (r < 0) - return r; - } else { - r = manager_get_user_by_pid(m, pid, &user); - if (r < 0) - return r; - if (!user) - return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "PID "PID_FMT" does not belong to any known or logged in user", pid); - } - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - const char *name; - Seat *seat; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, name, error, &seat); - if (r < 0) - return r; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Session *session; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(susso)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(session, m->sessions, i) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(susso)", - session->id, - (uint32_t) session->user->uid, - session->user->name, - session->seat ? session->seat->id : "", - p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - User *user; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(uso)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(user, m->users, i) { - _cleanup_free_ char *p = NULL; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(uso)", - (uint32_t) user->uid, - user->name, - p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_seats(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Seat *seat; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(seat, m->seats, i) { - _cleanup_free_ char *p = NULL; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", seat->id, p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Inhibitor *inhibitor; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(ssssuu)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { - - r = sd_bus_message_append(reply, "(ssssuu)", - strempty(inhibit_what_to_string(inhibitor->what)), - strempty(inhibitor->who), - strempty(inhibitor->why), - strempty(inhibit_mode_to_string(inhibitor->mode)), - (uint32_t) inhibitor->uid, - (uint32_t) inhibitor->pid); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; - uint32_t audit_id = 0; - _cleanup_free_ char *id = NULL; - Session *session = NULL; - Manager *m = userdata; - User *user = NULL; - Seat *seat = NULL; - pid_t leader; - uid_t uid; - int remote; - uint32_t vtnr = 0; - SessionType t; - SessionClass c; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); - if (r < 0) - return r; - - if (!uid_is_valid(uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); - if (leader < 0 || leader == 1) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); - - if (isempty(type)) - t = _SESSION_TYPE_INVALID; - else { - t = session_type_from_string(type); - if (t < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type); - } - - if (isempty(class)) - c = _SESSION_CLASS_INVALID; - else { - c = session_class_from_string(class); - if (c < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class); - } - - if (isempty(desktop)) - desktop = NULL; - else { - if (!string_is_safe(desktop)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop); - } - - if (isempty(cseat)) - seat = NULL; - else { - seat = hashmap_get(m->seats, cseat); - if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat); - } - - if (tty_is_vc(tty)) { - int v; - - if (!seat) - seat = m->seat0; - else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); - - v = vtnr_from_tty(tty); - if (v <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty); - - if (!vtnr) - vtnr = (uint32_t) v; - else if (vtnr != (uint32_t) v) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match"); - - } else if (tty_is_console(tty)) { - - if (!seat) - seat = m->seat0; - else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0"); - - if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0"); - } - - if (seat) { - if (seat_has_vts(seat)) { - if (!vtnr || vtnr > 63) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range"); - } else { - if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0"); - } - } - - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - return r; - - if (t == _SESSION_TYPE_INVALID) { - if (!isempty(display)) - t = SESSION_X11; - else if (!isempty(tty)) - t = SESSION_TTY; - else - t = SESSION_UNSPECIFIED; - } - - if (c == _SESSION_CLASS_INVALID) { - if (t == SESSION_UNSPECIFIED) - c = SESSION_BACKGROUND; - else - c = SESSION_USER; - } - - if (leader == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); - if (r < 0) - return r; - } - - r = manager_get_session_by_pid(m, leader, NULL); - if (r > 0) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session"); - - /* - * Old gdm and lightdm start the user-session on the same VT as - * the greeter session. But they destroy the greeter session - * after the user-session and want the user-session to take - * over the VT. We need to support this for - * backwards-compatibility, so make sure we allow new sessions - * on a VT that a greeter is running on. Furthermore, to allow - * re-logins, we have to allow a greeter to take over a used VT for - * the exact same reasons. - */ - if (c != SESSION_GREETER && - vtnr > 0 && - vtnr < m->seat0->position_count && - m->seat0->positions[vtnr] && - m->seat0->positions[vtnr]->class != SESSION_GREETER) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); - - if (hashmap_size(m->sessions) >= m->sessions_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); - - audit_session_from_pid(leader, &audit_id); - if (audit_id > 0) { - /* Keep our session IDs and the audit session IDs in sync */ - - if (asprintf(&id, "%"PRIu32, audit_id) < 0) - return -ENOMEM; - - /* Wut? There's already a session by this name and we - * didn't find it above? Weird, then let's not trust - * the audit data and let's better register a new - * ID */ - if (hashmap_get(m->sessions, id)) { - log_warning("Existing logind session ID %s used by new audit session, ignoring", id); - audit_id = 0; - - id = mfree(id); - } - } - - if (!id) { - do { - id = mfree(id); - - if (asprintf(&id, "c%lu", ++m->session_counter) < 0) - return -ENOMEM; - - } while (hashmap_get(m->sessions, id)); - } - - r = manager_add_user_by_uid(m, uid, &user); - if (r < 0) - goto fail; - - r = manager_add_session(m, id, &session); - if (r < 0) - goto fail; - - session_set_user(session, user); - - session->leader = leader; - session->audit_id = audit_id; - session->type = t; - session->class = c; - session->remote = remote; - session->vtnr = vtnr; - - if (!isempty(tty)) { - session->tty = strdup(tty); - if (!session->tty) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(display)) { - session->display = strdup(display); - if (!session->display) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(remote_user)) { - session->remote_user = strdup(remote_user); - if (!session->remote_user) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(remote_host)) { - session->remote_host = strdup(remote_host); - if (!session->remote_host) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(service)) { - session->service = strdup(service); - if (!session->service) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(desktop)) { - session->desktop = strdup(desktop); - if (!session->desktop) { - r = -ENOMEM; - goto fail; - } - } - - if (seat) { - r = seat_attach_session(seat, session); - if (r < 0) - goto fail; - } - - r = session_start(session); - if (r < 0) - goto fail; - - session->create_message = sd_bus_message_ref(message); - - /* Now, let's wait until the slice unit and stuff got - * created. We send the reply back from - * session_send_create_reply(). */ - - return 1; - -fail: - if (session) - session_add_to_gc_queue(session); - - if (user) - user_add_to_gc_queue(user); - - return r; -} - -static int method_release_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - r = session_release(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_activate(message, session, error); -} - -static int method_activate_session_on_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *session_name, *seat_name; - Manager *m = userdata; - Session *session; - Seat *seat; - int r; - - assert(message); - assert(m); - - /* Same as ActivateSession() but refuses to work if - * the seat doesn't match */ - - r = sd_bus_message_read(message, "ss", &session_name, &seat_name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, session_name, error, &session); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, seat_name, error, &seat); - if (r < 0) - return r; - - if (session->seat != seat) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name); - - r = session_activate(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_lock_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_lock(message, session, error); -} - -static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.lock-sessions", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_send_lock_all(m, streq(sd_bus_message_get_member(message), "LockSessions")); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_kill_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *name; - Manager *m = userdata; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_kill(message, session, error); -} - -static int method_kill_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - return bus_user_method_kill(message, user, error); -} - -static int method_terminate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_session_from_creds(m, message, name, error, &session); - if (r < 0) - return r; - - return bus_session_method_terminate(message, session, error); -} - -static int method_terminate_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint32_t uid; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - - r = manager_get_user_from_creds(m, message, uid, error, &user); - if (r < 0) - return r; - - return bus_user_method_terminate(message, user, error); -} - -static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Seat *seat; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = manager_get_seat_from_creds(m, message, name, error, &seat); - if (r < 0) - return r; - - return bus_seat_method_terminate(message, seat, error); -} - -static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *cc = NULL; - Manager *m = userdata; - int r, b, interactive; - struct passwd *pw; - const char *path; - uint32_t uid; - bool self = false; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ubb", &uid, &b, &interactive); - if (r < 0) - return r; - - if (uid == UID_INVALID) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - /* Note that we get the owner UID of the session, not the actual client UID here! */ - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r < 0) - return r; - - self = true; - - } else if (!uid_is_valid(uid)) - return -EINVAL; - - errno = 0; - pw = getpwuid(uid); - if (!pw) - return errno > 0 ? -errno : -ENOENT; - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - mkdir_p_label("/var/lib/systemd", 0755); - - r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0); - if (r < 0) - return r; - - cc = cescape(pw->pw_name); - if (!cc) - return -ENOMEM; - - path = strjoina("/var/lib/systemd/linger/", cc); - if (b) { - User *u; - - r = touch(path); - if (r < 0) - return r; - - if (manager_add_user_by_uid(m, uid, &u) >= 0) - user_start(u); - - } else { - User *u; - - r = unlink(path); - if (r < 0 && errno != ENOENT) - return -errno; - - u = hashmap_get(m->users, UID_TO_PTR(uid)); - if (u) - user_add_to_gc_queue(u); - } - - return sd_bus_reply_method_return(message, NULL); -} - -static int trigger_device(Manager *m, struct udev_device *d) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *first, *item; - int r; - - assert(m); - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - if (d) { - r = udev_enumerate_add_match_parent(e, d); - if (r < 0) - return r; - } - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_free_ char *t = NULL; - const char *p; - - p = udev_list_entry_get_name(item); - - t = strappend(p, "/uevent"); - if (!t) - return -ENOMEM; - - write_string_file(t, "change", WRITE_STRING_FILE_CREATE); - } - - return 0; -} - -static int attach_device(Manager *m, const char *seat, const char *sysfs) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - _cleanup_free_ char *rule = NULL, *file = NULL; - const char *id_for_seat; - int r; - - assert(m); - assert(seat); - assert(sysfs); - - d = udev_device_new_from_syspath(m->udev, sysfs); - if (!d) - return -ENODEV; - - if (!udev_device_has_tag(d, "seat")) - return -ENODEV; - - id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT"); - if (!id_for_seat) - return -ENODEV; - - if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) - return -ENOMEM; - - if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) - return -ENOMEM; - - mkdir_p_label("/etc/udev/rules.d", 0755); - r = write_string_file_atomic_label(file, rule); - if (r < 0) - return r; - - return trigger_device(m, d); -} - -static int flush_devices(Manager *m) { - _cleanup_closedir_ DIR *d; - - assert(m); - - d = opendir("/etc/udev/rules.d"); - if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/udev/rules.d: %m"); - } else { - struct dirent *de; - - while ((de = readdir(d))) { - - if (!dirent_is_file(de)) - continue; - - if (!startswith(de->d_name, "72-seat-")) - continue; - - if (!endswith(de->d_name, ".rules")) - continue; - - if (unlinkat(dirfd(d), de->d_name, 0) < 0) - log_warning_errno(errno, "Failed to unlink %s: %m", de->d_name); - } - } - - return trigger_device(m, NULL); -} - -static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *sysfs, *seat; - Manager *m = userdata; - int interactive, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ssb", &seat, &sysfs, &interactive); - if (r < 0) - return r; - - if (!path_startswith(sysfs, "/sys")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs); - - if (!seat_name_is_valid(seat)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.attach-device", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = attach_device(m, seat, sysfs); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - int interactive, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &interactive); - if (r < 0) - return r; - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.flush-devices", - NULL, - interactive, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = flush_devices(m); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int have_multiple_sessions( - Manager *m, - uid_t uid) { - - Session *session; - Iterator i; - - assert(m); - - /* Check for other users' sessions. Greeter sessions do not - * count, and non-login sessions do not count either. */ - HASHMAP_FOREACH(session, m->sessions, i) - if (session->class == SESSION_USER && - session->user->uid != uid) - return true; - - return false; -} - -static int bus_manager_log_shutdown( - Manager *m, - InhibitWhat w, - const char *unit_name) { - - const char *p, *q; - - assert(m); - assert(unit_name); - - if (w != INHIBIT_SHUTDOWN) - return 0; - - if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { - p = "MESSAGE=System is powering down"; - q = "SHUTDOWN=power-off"; - } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { - p = "MESSAGE=System is halting"; - q = "SHUTDOWN=halt"; - } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { - p = "MESSAGE=System is rebooting"; - q = "SHUTDOWN=reboot"; - } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { - p = "MESSAGE=System is rebooting with kexec"; - q = "SHUTDOWN=kexec"; - } else { - p = "MESSAGE=System is shutting down"; - q = NULL; - } - - if (isempty(m->wall_message)) - p = strjoina(p, "."); - else - p = strjoina(p, " (", m->wall_message, ")."); - - return log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), - p, - q, - NULL); -} - -static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { - Manager *m = userdata; - - assert(e); - assert(m); - - m->lid_switch_ignore_event_source = sd_event_source_unref(m->lid_switch_ignore_event_source); - return 0; -} - -int manager_set_lid_switch_ignore(Manager *m, usec_t until) { - int r; - - assert(m); - - if (until <= now(CLOCK_MONOTONIC)) - return 0; - - /* We want to ignore the lid switch for a while after each - * suspend, and after boot-up. Hence let's install a timer for - * this. As long as the event source exists we ignore the lid - * switch. */ - - if (m->lid_switch_ignore_event_source) { - usec_t u; - - r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u); - if (r < 0) - return r; - - if (until <= u) - return 0; - - r = sd_event_source_set_time(m->lid_switch_ignore_event_source, until); - } else - r = sd_event_add_time( - m->event, - &m->lid_switch_ignore_event_source, - CLOCK_MONOTONIC, - until, 0, - lid_switch_ignore_handler, m); - - return r; -} - -static void reset_scheduled_shutdown(Manager *m) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); - m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); - m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); - m->scheduled_shutdown_timeout = 0; - m->shutdown_dry_run = false; - - if (m->unlink_nologin) { - (void) unlink("/run/nologin"); - m->unlink_nologin = false; - } -} - -static int execute_shutdown_or_sleep( - Manager *m, - InhibitWhat w, - const char *unit_name, - sd_bus_error *error) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - char *c = NULL; - const char *p; - int r; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); - - bus_manager_log_shutdown(m, w, unit_name); - - if (m->shutdown_dry_run) { - log_info("Running in dry run, suppressing action."); - reset_scheduled_shutdown(m); - } else { - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, - &reply, - "ss", unit_name, "replace-irreversibly"); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "o", &p); - if (r < 0) - return r; - - c = strdup(p); - if (!c) - return -ENOMEM; - } - - m->action_unit = unit_name; - free(m->action_job); - m->action_job = c; - m->action_what = w; - - /* Make sure the lid switch is ignored for a while */ - manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + m->holdoff_timeout_usec); - - return 0; -} - -int manager_dispatch_delayed(Manager *manager, bool timeout) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Inhibitor *offending = NULL; - int r; - - assert(manager); - - if (manager->action_what == 0 || manager->action_job) - return 0; - - if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { - _cleanup_free_ char *comm = NULL, *u = NULL; - - if (!timeout) - return 0; - - (void) get_process_comm(offending->pid, &comm); - u = uid_to_name(offending->uid); - - log_notice("Delay lock is active (UID "UID_FMT"/%s, PID "PID_FMT"/%s) but inhibitor timeout is reached.", - offending->uid, strna(u), - offending->pid, strna(comm)); - } - - /* Actually do the operation */ - r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error); - if (r < 0) { - log_warning("Failed to send delayed message: %s", bus_error_message(&error, r)); - - manager->action_unit = NULL; - manager->action_what = 0; - return r; - } - - return 1; -} - -static int manager_inhibit_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *manager = userdata; - int r; - - assert(manager); - assert(manager->inhibit_timeout_source == s); - - r = manager_dispatch_delayed(manager, true); - return (r < 0) ? r : 0; -} - -static int delay_shutdown_or_sleep( - Manager *m, - InhibitWhat w, - const char *unit_name) { - - int r; - usec_t timeout_val; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); - - timeout_val = now(CLOCK_MONOTONIC) + m->inhibit_delay_max; - - if (m->inhibit_timeout_source) { - r = sd_event_source_set_time(m->inhibit_timeout_source, timeout_val); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->inhibit_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->inhibit_timeout_source, CLOCK_MONOTONIC, - timeout_val, 0, manager_inhibit_timeout_handler, m); - if (r < 0) - return r; - } - - m->action_unit = unit_name; - m->action_what = w; - - return 0; -} - -static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { - - static const char * const signal_name[_INHIBIT_WHAT_MAX] = { - [INHIBIT_SHUTDOWN] = "PrepareForShutdown", - [INHIBIT_SLEEP] = "PrepareForSleep" - }; - - int active = _active; - - assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(signal_name[w]); - - return sd_bus_emit_signal(m->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - signal_name[w], - "b", - active); -} - -int bus_manager_shutdown_or_sleep_now_or_later( - Manager *m, - const char *unit_name, - InhibitWhat w, - sd_bus_error *error) { - - bool delayed; - int r; - - assert(m); - assert(unit_name); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(!m->action_job); - - /* Tell everybody to prepare for shutdown/sleep */ - send_prepare_for(m, w, true); - - delayed = - m->inhibit_delay_max > 0 && - manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL); - - if (delayed) - /* Shutdown is delayed, keep in mind what we - * want to do, and start a timeout */ - r = delay_shutdown_or_sleep(m, w, unit_name); - else - /* Shutdown is not delayed, execute it - * immediately */ - r = execute_shutdown_or_sleep(m, w, unit_name, error); - - return r; -} - -static int verify_shutdown_creds( - Manager *m, - sd_bus_message *message, - InhibitWhat w, - bool interactive, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - sd_bus_error *error) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - bool multiple_sessions, blocked; - uid_t uid; - int r; - - assert(m); - assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = have_multiple_sessions(m, uid); - if (r < 0) - return r; - - multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - - if (multiple_sessions && action_multiple_sessions) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (blocked && action_ignore_inhibit) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - if (!multiple_sessions && !blocked && action) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - } - - return 0; -} - -static int method_do_shutdown_or_sleep( - Manager *m, - sd_bus_message *message, - const char *unit_name, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - const char *sleep_verb, - sd_bus_error *error) { - - int interactive, r; - - assert(m); - assert(message); - assert(unit_name); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - - r = sd_bus_message_read(message, "b", &interactive); - if (r < 0) - return r; - - /* Don't allow multiple jobs being executed at the same time */ - if (m->action_what) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); - - if (sleep_verb) { - r = can_sleep(sleep_verb); - if (r < 0) - return r; - - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb not supported"); - } - - r = verify_shutdown_creds(m, message, w, interactive, action, action_multiple_sessions, - action_ignore_inhibit, error); - if (r != 0) - return r; - - r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_POWEROFF_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, - error); -} - -static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_REBOOT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, - error); -} - -static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_SUSPEND_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - "suspend", - error); -} - -static int nologin_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *m = userdata; - int r; - - log_info("Creating /run/nologin, blocking further logins..."); - - r = write_string_file_atomic_label("/run/nologin", "System is going down."); - if (r < 0) - log_error_errno(r, "Failed to create /run/nologin: %m"); - else - m->unlink_nologin = true; - - return 0; -} - -static int update_schedule_file(Manager *m) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(m); - - r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); - if (r < 0) - return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); - - (void) fchmod(fileno(f), 0644); - - fprintf(f, - "USEC="USEC_FMT"\n" - "WARN_WALL=%i\n" - "MODE=%s\n", - m->scheduled_shutdown_timeout, - m->enable_wall_messages, - m->scheduled_shutdown_type); - - if (!isempty(m->wall_message)) { - _cleanup_free_ char *t; - - t = cescape(m->wall_message); - if (!t) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WALL_MESSAGE=%s\n", t); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(temp_path); - (void) unlink("/run/systemd/shutdown/scheduled"); - - return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); -} - -static int manager_scheduled_shutdown_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Manager *m = userdata; - const char *target; - int r; - - assert(m); - - if (isempty(m->scheduled_shutdown_type)) - return 0; - - if (streq(m->scheduled_shutdown_type, "halt")) - target = SPECIAL_HALT_TARGET; - else if (streq(m->scheduled_shutdown_type, "poweroff")) - target = SPECIAL_POWEROFF_TARGET; - else - target = SPECIAL_REBOOT_TARGET; - - r = execute_shutdown_or_sleep(m, 0, target, &error); - if (r < 0) - return log_error_errno(r, "Unable to execute transition to %s: %m", target); - - return 0; -} - -static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *action_multiple_sessions = NULL; - const char *action_ignore_inhibit = NULL; - const char *action = NULL; - uint64_t elapse; - char *type; - int r; - - assert(m); - assert(message); - - r = sd_bus_message_read(message, "st", &type, &elapse); - if (r < 0) - return r; - - if (startswith(type, "dry-")) { - type += 4; - m->shutdown_dry_run = true; - } - - if (streq(type, "reboot")) { - action = "org.freedesktop.login1.reboot"; - action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; - } else if (streq(type, "halt")) { - action = "org.freedesktop.login1.halt"; - action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; - } else if (streq(type, "poweroff")) { - action = "org.freedesktop.login1.power-off"; - action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; - } else - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); - - r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, false, - action, action_multiple_sessions, action_ignore_inhibit, error); - if (r != 0) - return r; - - if (m->scheduled_shutdown_timeout_source) { - r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, - CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - - r = free_and_strdup(&m->scheduled_shutdown_type, type); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } - - if (m->nologin_timeout_source) { - r = sd_event_source_set_time(m->nologin_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->nologin_timeout_source, - CLOCK_REALTIME, elapse - 5 * USEC_PER_MINUTE, 0, nologin_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - - m->scheduled_shutdown_timeout = elapse; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); - if (r >= 0) { - const char *tty = NULL; - - (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); - (void) sd_bus_creds_get_tty(creds, &tty); - - r = free_and_strdup(&m->scheduled_shutdown_tty, tty); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } - } - - r = manager_setup_wall_message_timer(m); - if (r < 0) - return r; - - if (!isempty(type)) { - r = update_schedule_file(m); - if (r < 0) - return r; - } else - (void) unlink("/run/systemd/shutdown/scheduled"); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - bool cancelled; - - assert(m); - assert(message); - - cancelled = m->scheduled_shutdown_type != NULL; - reset_scheduled_shutdown(m); - - if (cancelled) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *tty = NULL; - uid_t uid = 0; - int r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); - if (r >= 0) { - (void) sd_bus_creds_get_uid(creds, &uid); - (void) sd_bus_creds_get_tty(creds, &tty); - } - - utmp_wall("The system shutdown has been cancelled", - uid_to_name(uid), tty, logind_wall_tty_filter, m); - } - - return sd_bus_reply_method_return(message, "b", cancelled); -} - -static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hibernate", - error); -} - -static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_do_shutdown_or_sleep( - m, message, - SPECIAL_HYBRID_SLEEP_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hybrid-sleep", - error); -} - -static int method_can_shutdown_or_sleep( - Manager *m, - sd_bus_message *message, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - const char *sleep_verb, - sd_bus_error *error) { - - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - bool multiple_sessions, challenge, blocked; - const char *result = NULL; - uid_t uid; - int r; - - assert(m); - assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(action); - assert(action_multiple_sessions); - assert(action_ignore_inhibit); - - if (sleep_verb) { - r = can_sleep(sleep_verb); - if (r < 0) - return r; - if (r == 0) - return sd_bus_reply_method_return(message, "s", "na"); - } - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = have_multiple_sessions(m, uid); - if (r < 0) - return r; - - multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - - if (multiple_sessions) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - } - - if (blocked) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0 && !result) - result = "yes"; - else if (challenge && (!result || streq(result, "yes"))) - result = "challenge"; - else - result = "no"; - } - - if (!multiple_sessions && !blocked) { - /* If neither inhibit nor multiple sessions - * apply then just check the normal policy */ - - r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - } - - return sd_bus_reply_method_return(message, "s", result); -} - -static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, - error); -} - -static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, - error); -} - -static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - "suspend", - error); -} - -static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hibernate", - error); -} - -static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - "hybrid-sleep", - error); -} - -static int property_get_reboot_to_firmware_setup( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - int r; - - assert(bus); - assert(reply); - assert(userdata); - - r = efi_get_reboot_to_firmware(); - if (r < 0 && r != -EOPNOTSUPP) - return r; - - return sd_bus_message_append(reply, "b", r > 0); -} - -static int method_set_reboot_to_firmware_setup( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int b, r; - Manager *m = userdata; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = efi_set_reboot_to_firmware(b); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_can_reboot_to_firmware_setup( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int r; - bool challenge; - const char *result; - Manager *m = userdata; - - assert(message); - assert(m); - - r = efi_reboot_to_firmware_supported(); - if (r == -EOPNOTSUPP) - return sd_bus_reply_method_return(message, "s", "na"); - else if (r < 0) - return r; - - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - UID_INVALID, - &challenge, - error); - if (r < 0) - return r; - - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; - - return sd_bus_reply_method_return(message, "s", result); -} - -static int method_set_wall_message( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - int r; - Manager *m = userdata; - char *wall_message; - int enable_wall_messages; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); - if (r < 0) - return r; - - r = bus_verify_polkit_async(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-wall-message", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - if (isempty(wall_message)) - m->wall_message = mfree(m->wall_message); - else { - r = free_and_strdup(&m->wall_message, wall_message); - if (r < 0) - return log_oom(); - } - - m->enable_wall_messages = enable_wall_messages; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *who, *why, *what, *mode; - _cleanup_free_ char *id = NULL; - _cleanup_close_ int fifo_fd = -1; - Manager *m = userdata; - Inhibitor *i = NULL; - InhibitMode mm; - InhibitWhat w; - pid_t pid; - uid_t uid; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ssss", &what, &who, &why, &mode); - if (r < 0) - return r; - - w = inhibit_what_from_string(what); - if (w <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what); - - mm = inhibit_mode_from_string(mode); - if (mm < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode); - - /* Delay is only supported for shutdown/sleep */ - if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep"); - - /* Don't allow taking delay locks while we are already - * executing the operation. We shouldn't create the impression - * that the lock was successful if the machine is about to go - * down/suspend any moment. */ - if (m->action_what & w) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); - - r = bus_verify_polkit_async( - message, - CAP_SYS_BOOT, - w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") : - w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") : - w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" : - w == INHIBIT_HANDLE_POWER_KEY ? "org.freedesktop.login1.inhibit-handle-power-key" : - w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : - w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : - "org.freedesktop.login1.inhibit-handle-lid-switch", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - if (hashmap_size(m->inhibitors) >= m->inhibitors_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); - - do { - id = mfree(id); - - if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) - return -ENOMEM; - - } while (hashmap_get(m->inhibitors, id)); - - r = manager_add_inhibitor(m, id, &i); - if (r < 0) - return r; - - i->what = w; - i->mode = mm; - i->pid = pid; - i->uid = uid; - i->why = strdup(why); - i->who = strdup(who); - - if (!i->why || !i->who) { - r = -ENOMEM; - goto fail; - } - - fifo_fd = inhibitor_create_fifo(i); - if (fifo_fd < 0) { - r = fifo_fd; - goto fail; - } - - inhibitor_start(i); - - return sd_bus_reply_method_return(message, "h", fifo_fd); - -fail: - if (i) - inhibitor_free(i); - - return r; -} - -const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), - SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), - - SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleLidSwitch", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HandleLidSwitchDocked", "s", property_get_handle_action, offsetof(Manager, handle_lid_switch_docked), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("HoldoffTimeoutUSec", "t", NULL, offsetof(Manager, holdoff_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleAction", "s", property_get_handle_action, offsetof(Manager, idle_action), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IdleActionUSec", "t", NULL, offsetof(Manager, idle_action_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("PreparingForShutdown", "b", property_get_preparing, 0, 0), - SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), - SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), - SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), - SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), - SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), - SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), - - SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0), - SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("SessionNew", "so", 0), - SD_BUS_SIGNAL("SessionRemoved", "so", 0), - SD_BUS_SIGNAL("UserNew", "uo", 0), - SD_BUS_SIGNAL("UserRemoved", "uo", 0), - SD_BUS_SIGNAL("SeatNew", "so", 0), - SD_BUS_SIGNAL("SeatRemoved", "so", 0), - SD_BUS_SIGNAL("PrepareForShutdown", "b", 0), - SD_BUS_SIGNAL("PrepareForSleep", "b", 0), - - SD_BUS_VTABLE_END -}; - -static int session_jobs_reply(Session *s, const char *unit, const char *result) { - int r = 0; - - assert(s); - assert(unit); - - if (!s->started) - return r; - - if (streq(result, "done")) - r = session_send_create_reply(s, NULL); - else { - _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; - - sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); - r = session_send_create_reply(s, &e); - } - - return r; -} - -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *result, *unit; - Manager *m = userdata; - Session *session; - uint32_t id; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (m->action_job && streq(m->action_job, path)) { - log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); - - /* Tell people that they now may take a lock again */ - send_prepare_for(m, m->action_what, false); - - m->action_job = mfree(m->action_job); - m->action_unit = NULL; - m->action_what = 0; - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session && streq_ptr(path, session->scope_job)) { - session->scope_job = mfree(session->scope_job); - session_jobs_reply(session, unit, result); - - session_save(session); - user_save(session->user); - session_add_to_gc_queue(session); - } - - user = hashmap_get(m->user_units, unit); - if (user && - (streq_ptr(path, user->service_job) || - streq_ptr(path, user->slice_job))) { - - if (streq_ptr(path, user->service_job)) - user->service_job = mfree(user->service_job); - - if (streq_ptr(path, user->slice_job)) - user->slice_job = mfree(user->slice_job); - - LIST_FOREACH(sessions_by_user, session, user->sessions) - session_jobs_reply(session, unit, result); - - user_save(user); - user_add_to_gc_queue(user); - } - - return 0; -} - -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *unit; - Manager *m = userdata; - Session *session; - User *user; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "so", &unit, &path); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session) - session_add_to_gc_queue(session); - - user = hashmap_get(m->user_units, unit); - if (user) - user_add_to_gc_queue(user); - - return 0; -} - -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *unit = NULL; - Manager *m = userdata; - const char *path; - Session *session; - User *user; - int r; - - assert(message); - assert(m); - - path = sd_bus_message_get_path(message); - if (!path) - return 0; - - r = unit_name_from_dbus_path(path, &unit); - if (r == -EINVAL) /* not a unit */ - return 0; - if (r < 0) { - log_oom(); - return 0; - } - - session = hashmap_get(m->session_units, unit); - if (session) - session_add_to_gc_queue(session); - - user = hashmap_get(m->user_units, unit); - if (user) - user_add_to_gc_queue(user); - - return 0; -} - -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - Iterator i; - int b, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (b) - return 0; - - /* systemd finished reloading, let's recheck all our sessions */ - log_debug("System manager has been reloaded, rechecking sessions..."); - - HASHMAP_FOREACH(session, m->sessions, i) - session_add_to_gc_queue(session); - - return 0; -} - -int manager_send_changed(Manager *manager, const char *property, ...) { - char **l; - - assert(manager); - - l = strv_from_stdarg_alloca(property); - - return sd_bus_emit_properties_changed_strv( - manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - l); -} - -static int strdup_job(sd_bus_message *reply, char **job) { - const char *j; - char *copy; - int r; - - r = sd_bus_message_read(reply, "o", &j); - if (r < 0) - return r; - - copy = strdup(j); - if (!copy) - return -ENOMEM; - - *job = copy; - return 1; -} - -int manager_start_slice( - Manager *manager, - const char *slice, - const char *description, - const char *after, - const char *after2, - uint64_t tasks_max, - sd_bus_error *error, - char **job) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(manager); - assert(slice); - assert(job); - - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - if (!isempty(description)) { - r = sd_bus_message_append(m, "(sv)", "Description", "s", description); - if (r < 0) - return r; - } - - if (!isempty(after)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); - if (r < 0) - return r; - } - - if (!isempty(after2)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); - if (r < 0) - return r; - } - - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return r; - - r = sd_bus_call(manager->bus, m, 0, error, &reply); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_start_scope( - Manager *manager, - const char *scope, - pid_t pid, - const char *slice, - const char *description, - const char *after, - const char *after2, - uint64_t tasks_max, - sd_bus_error *error, - char **job) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(manager); - assert(scope); - assert(pid > 1); - assert(job); - - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - if (!isempty(slice)) { - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return r; - } - - if (!isempty(description)) { - r = sd_bus_message_append(m, "(sv)", "Description", "s", description); - if (r < 0) - return r; - } - - if (!isempty(after)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); - if (r < 0) - return r; - } - - if (!isempty(after2)) { - r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); - if (r < 0) - return r; - } - - /* cgroup empty notification is not available in containers - * currently. To make this less problematic, let's shorten the - * stop timeout for sessions, so that we don't wait - * forever. */ - - /* Make sure that the session shells are terminated with - * SIGHUP since bash and friends tend to ignore SIGTERM */ - r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", true); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return r; - - r = sd_bus_call(manager->bus, m, 0, error, &reply); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(unit); - assert(job); - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, - &reply, - "ss", unit, "replace"); - if (r < 0) - return r; - - return strdup_job(reply, job); -} - -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(unit); - assert(job); - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StopUnit", - error, - &reply, - "ss", unit, "fail"); - if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { - - *job = NULL; - sd_bus_error_free(error); - return 0; - } - - return r; - } - - return strdup_job(reply, job); -} - -int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - int r; - - assert(manager); - assert(scope); - - path = unit_dbus_path_from_name(scope); - if (!path) - return -ENOMEM; - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Scope", - "Abandon", - error, - NULL, - NULL); - if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) || - sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) { - sd_bus_error_free(error); - return 0; - } - - return r; - } - - return 1; -} - -int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) { - assert(manager); - assert(unit); - - return sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - error, - NULL, - "ssi", unit, who == KILL_LEADER ? "main" : "all", signo); -} - -int manager_unit_is_active(Manager *manager, const char *unit) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *path = NULL; - const char *state; - int r; - - assert(manager); - assert(unit); - - path = unit_dbus_path_from_name(unit); - if (!path) - return -ENOMEM; - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &reply, - "s"); - if (r < 0) { - /* systemd might have droppped off momentarily, let's - * not make this an error */ - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - /* If the unit is already unloaded then it's not - * active */ - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) - return false; - - return r; - } - - r = sd_bus_message_read(reply, "s", &state); - if (r < 0) - return -EINVAL; - - return !streq(state, "inactive") && !streq(state, "failed"); -} - -int manager_job_is_active(Manager *manager, const char *path) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(path); - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "State", - &error, - &reply, - "s"); - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) - return false; - - return r; - } - - /* We don't actually care about the state really. The fact - * that we could read the job state is enough for us */ - - return true; -} diff --git a/src/login/logind-device.c b/src/login/logind-device.c deleted file mode 100644 index eb5edd1cd5..0000000000 --- a/src/login/logind-device.c +++ /dev/null @@ -1,124 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "logind-device.h" -#include "util.h" - -Device* device_new(Manager *m, const char *sysfs, bool master) { - Device *d; - - assert(m); - assert(sysfs); - - d = new0(Device, 1); - if (!d) - return NULL; - - d->sysfs = strdup(sysfs); - if (!d->sysfs) { - free(d); - return NULL; - } - - if (hashmap_put(m->devices, d->sysfs, d) < 0) { - free(d->sysfs); - free(d); - return NULL; - } - - d->manager = m; - d->master = master; - dual_timestamp_get(&d->timestamp); - - return d; -} - -static void device_detach(Device *d) { - Seat *s; - SessionDevice *sd; - - assert(d); - - if (!d->seat) - return; - - while ((sd = d->session_devices)) - session_device_free(sd); - - s = d->seat; - LIST_REMOVE(devices, d->seat->devices, d); - d->seat = NULL; - - if (!seat_has_master_device(s)) { - seat_add_to_gc_queue(s); - seat_send_changed(s, "CanGraphical", NULL); - } -} - -void device_free(Device *d) { - assert(d); - - device_detach(d); - - hashmap_remove(d->manager->devices, d->sysfs); - - free(d->sysfs); - free(d); -} - -void device_attach(Device *d, Seat *s) { - Device *i; - bool had_master; - - assert(d); - assert(s); - - if (d->seat == s) - return; - - if (d->seat) - device_detach(d); - - d->seat = s; - had_master = seat_has_master_device(s); - - /* We keep the device list sorted by the "master" flag. That is, master - * devices are at the front, other devices at the tail. As there is no - * way to easily add devices at the list-tail, we need to iterate the - * list to find the first non-master device when adding non-master - * devices. We assume there is only a few (normally 1) master devices - * per seat, so we iterate only a few times. */ - - if (d->master || !s->devices) - LIST_PREPEND(devices, s->devices, d); - else { - LIST_FOREACH(devices, i, s->devices) { - if (!i->devices_next || !i->master) { - LIST_INSERT_AFTER(devices, s->devices, i, d); - break; - } - } - } - - if (!had_master && d->master) - seat_send_changed(s, "CanGraphical", NULL); -} diff --git a/src/login/logind-device.h b/src/login/logind-device.h deleted file mode 100644 index 927068e00a..0000000000 --- a/src/login/logind-device.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Device Device; - -#include "list.h" -#include "logind-seat.h" -#include "logind-session-device.h" - -struct Device { - Manager *manager; - - char *sysfs; - Seat *seat; - bool master; - - dual_timestamp timestamp; - - LIST_FIELDS(struct Device, devices); - LIST_HEAD(SessionDevice, session_devices); -}; - -Device* device_new(Manager *m, const char *sysfs, bool master); -void device_free(Device *d); -void device_attach(Device *d, Seat *s); diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf deleted file mode 100644 index 0b6a5f3cf4..0000000000 --- a/src/login/logind-gperf.gperf +++ /dev/null @@ -1,39 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "logind.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name logind_gperf_hash -%define lookup-function-name logind_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Login.NAutoVTs, config_parse_unsigned, 0, offsetof(Manager, n_autovts) -Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manager, reserve_vt) -Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes) -Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users) -Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) -Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) -Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) -Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) -Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) -Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) -Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked) -Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited) -Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited) -Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited) -Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) -Login.HoldoffTimeoutSec, config_parse_sec, 0, offsetof(Manager, holdoff_timeout_usec) -Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) -Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) -Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) -Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) -Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) -Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) -Login.UserTasksMax, config_parse_user_tasks_max,0, offsetof(Manager, user_tasks_max) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c deleted file mode 100644 index 6c78e0dddc..0000000000 --- a/src/login/logind-inhibit.c +++ /dev/null @@ -1,485 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "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; - - assert(m); - - i = new0(Inhibitor, 1); - if (!i) - return NULL; - - i->state_file = strappend("/run/systemd/inhibit/", id); - if (!i->state_file) { - free(i); - return NULL; - } - - i->id = basename(i->state_file); - - if (hashmap_put(m->inhibitors, i->id, i) < 0) { - free(i->state_file); - free(i); - return NULL; - } - - i->manager = m; - i->fifo_fd = -1; - - return i; -} - -void inhibitor_free(Inhibitor *i) { - assert(i); - - hashmap_remove(i->manager->inhibitors, i->id); - - inhibitor_remove_fifo(i); - - free(i->who); - free(i->why); - - if (i->state_file) { - unlink(i->state_file); - free(i->state_file); - } - - free(i); -} - -int inhibitor_save(Inhibitor *i) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(i); - - r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(i->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "WHAT=%s\n" - "MODE=%s\n" - "UID="UID_FMT"\n" - "PID="PID_FMT"\n", - inhibit_what_to_string(i->what), - inhibit_mode_to_string(i->mode), - i->uid, - i->pid); - - if (i->who) { - _cleanup_free_ char *cc = NULL; - - cc = cescape(i->who); - if (!cc) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WHO=%s\n", cc); - } - - if (i->why) { - _cleanup_free_ char *cc = NULL; - - cc = cescape(i->why); - if (!cc) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "WHY=%s\n", cc); - } - - if (i->fifo_path) - fprintf(f, "FIFO=%s\n", i->fifo_path); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, i->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(i->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file); -} - -int inhibitor_start(Inhibitor *i) { - assert(i); - - if (i->started) - return 0; - - dual_timestamp_get(&i->since); - - log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s started.", - strna(i->who), strna(i->why), - i->pid, i->uid, - inhibit_mode_to_string(i->mode)); - - inhibitor_save(i); - - i->started = true; - - manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); - - return 0; -} - -int inhibitor_stop(Inhibitor *i) { - assert(i); - - if (i->started) - log_debug("Inhibitor %s (%s) pid="PID_FMT" uid="UID_FMT" mode=%s stopped.", - strna(i->who), strna(i->why), - i->pid, i->uid, - inhibit_mode_to_string(i->mode)); - - if (i->state_file) - unlink(i->state_file); - - i->started = false; - - manager_send_changed(i->manager, i->mode == INHIBIT_BLOCK ? "BlockInhibited" : "DelayInhibited", NULL); - - return 0; -} - -int inhibitor_load(Inhibitor *i) { - - _cleanup_free_ char - *what = NULL, - *uid = NULL, - *pid = NULL, - *who = NULL, - *why = NULL, - *mode = NULL; - - InhibitWhat w; - InhibitMode mm; - char *cc; - int r; - - r = parse_env_file(i->state_file, NEWLINE, - "WHAT", &what, - "UID", &uid, - "PID", &pid, - "WHO", &who, - "WHY", &why, - "MODE", &mode, - "FIFO", &i->fifo_path, - NULL); - if (r < 0) - return r; - - w = what ? inhibit_what_from_string(what) : 0; - if (w >= 0) - i->what = w; - - mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK; - if (mm >= 0) - i->mode = mm; - - if (uid) { - r = parse_uid(uid, &i->uid); - if (r < 0) - return r; - } - - if (pid) { - r = parse_pid(pid, &i->pid); - if (r < 0) - return r; - } - - if (who) { - r = cunescape(who, 0, &cc); - if (r < 0) - return r; - - free(i->who); - i->who = cc; - } - - if (why) { - r = cunescape(why, 0, &cc); - if (r < 0) - return r; - - free(i->why); - i->why = cc; - } - - if (i->fifo_path) { - int fd; - - fd = inhibitor_create_fifo(i); - safe_close(fd); - } - - return 0; -} - -static int inhibitor_dispatch_fifo(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Inhibitor *i = userdata; - - assert(s); - assert(fd == i->fifo_fd); - assert(i); - - inhibitor_stop(i); - inhibitor_free(i); - - return 0; -} - -int inhibitor_create_fifo(Inhibitor *i) { - int r; - - assert(i); - - /* Create FIFO */ - if (!i->fifo_path) { - r = mkdir_safe_label("/run/systemd/inhibit", 0755, 0, 0); - if (r < 0) - return r; - - i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL); - if (!i->fifo_path) - return -ENOMEM; - - if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST) - return -errno; - } - - /* Open reading side */ - if (i->fifo_fd < 0) { - i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); - if (i->fifo_fd < 0) - return -errno; - } - - if (!i->event_source) { - r = sd_event_add_io(i->manager->event, &i->event_source, i->fifo_fd, 0, inhibitor_dispatch_fifo, i); - if (r < 0) - return r; - - r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); - if (r < 0) - return r; - } - - /* Open writing side */ - r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); - if (r < 0) - return -errno; - - return r; -} - -void inhibitor_remove_fifo(Inhibitor *i) { - assert(i); - - i->event_source = sd_event_source_unref(i->event_source); - i->fifo_fd = safe_close(i->fifo_fd); - - if (i->fifo_path) { - unlink(i->fifo_path); - i->fifo_path = mfree(i->fifo_path); - } -} - -InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { - Inhibitor *i; - Iterator j; - InhibitWhat what = 0; - - assert(m); - - HASHMAP_FOREACH(i, m->inhibitors, j) - if (i->mode == mm) - what |= i->what; - - return what; -} - -static int pid_is_active(Manager *m, pid_t pid) { - Session *s; - int r; - - r = manager_get_session_by_pid(m, pid, &s); - if (r < 0) - return r; - - /* If there's no session assigned to it, then it's globally - * active on all ttys */ - if (r == 0) - return 1; - - return session_is_active(s); -} - -bool manager_is_inhibited( - Manager *m, - InhibitWhat w, - InhibitMode mm, - dual_timestamp *since, - bool ignore_inactive, - bool ignore_uid, - uid_t uid, - Inhibitor **offending) { - - Inhibitor *i; - Iterator j; - struct dual_timestamp ts = DUAL_TIMESTAMP_NULL; - bool inhibited = false; - - assert(m); - assert(w > 0 && w < _INHIBIT_WHAT_MAX); - - HASHMAP_FOREACH(i, m->inhibitors, j) { - if (!(i->what & w)) - continue; - - if (i->mode != mm) - continue; - - if (ignore_inactive && pid_is_active(m, i->pid) <= 0) - continue; - - if (ignore_uid && i->uid == uid) - continue; - - if (!inhibited || - i->since.monotonic < ts.monotonic) - ts = i->since; - - inhibited = true; - - if (offending) - *offending = i; - } - - if (since) - *since = ts; - - return inhibited; -} - -const char *inhibit_what_to_string(InhibitWhat w) { - static thread_local char buffer[97]; - char *p; - - if (w < 0 || w >= _INHIBIT_WHAT_MAX) - return NULL; - - p = buffer; - if (w & INHIBIT_SHUTDOWN) - p = stpcpy(p, "shutdown:"); - if (w & INHIBIT_SLEEP) - p = stpcpy(p, "sleep:"); - if (w & INHIBIT_IDLE) - p = stpcpy(p, "idle:"); - if (w & INHIBIT_HANDLE_POWER_KEY) - p = stpcpy(p, "handle-power-key:"); - if (w & INHIBIT_HANDLE_SUSPEND_KEY) - p = stpcpy(p, "handle-suspend-key:"); - if (w & INHIBIT_HANDLE_HIBERNATE_KEY) - p = stpcpy(p, "handle-hibernate-key:"); - if (w & INHIBIT_HANDLE_LID_SWITCH) - p = stpcpy(p, "handle-lid-switch:"); - - if (p > buffer) - *(p-1) = 0; - else - *p = 0; - - return buffer; -} - -InhibitWhat inhibit_what_from_string(const char *s) { - InhibitWhat what = 0; - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, s, ":", state) { - if (l == 8 && strneq(word, "shutdown", l)) - what |= INHIBIT_SHUTDOWN; - else if (l == 5 && strneq(word, "sleep", l)) - what |= INHIBIT_SLEEP; - else if (l == 4 && strneq(word, "idle", l)) - what |= INHIBIT_IDLE; - else if (l == 16 && strneq(word, "handle-power-key", l)) - what |= INHIBIT_HANDLE_POWER_KEY; - else if (l == 18 && strneq(word, "handle-suspend-key", l)) - what |= INHIBIT_HANDLE_SUSPEND_KEY; - else if (l == 20 && strneq(word, "handle-hibernate-key", l)) - what |= INHIBIT_HANDLE_HIBERNATE_KEY; - else if (l == 17 && strneq(word, "handle-lid-switch", l)) - what |= INHIBIT_HANDLE_LID_SWITCH; - else - return _INHIBIT_WHAT_INVALID; - } - - return what; -} - -static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { - [INHIBIT_BLOCK] = "block", - [INHIBIT_DELAY] = "delay" -}; - -DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode); diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h deleted file mode 100644 index 70de199c60..0000000000 --- a/src/login/logind-inhibit.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Inhibitor Inhibitor; - - -typedef enum InhibitWhat { - INHIBIT_SHUTDOWN = 1, - INHIBIT_SLEEP = 2, - INHIBIT_IDLE = 4, - INHIBIT_HANDLE_POWER_KEY = 8, - INHIBIT_HANDLE_SUSPEND_KEY = 16, - INHIBIT_HANDLE_HIBERNATE_KEY = 32, - INHIBIT_HANDLE_LID_SWITCH = 64, - _INHIBIT_WHAT_MAX = 128, - _INHIBIT_WHAT_INVALID = -1 -} InhibitWhat; - -typedef enum InhibitMode { - INHIBIT_BLOCK, - INHIBIT_DELAY, - _INHIBIT_MODE_MAX, - _INHIBIT_MODE_INVALID = -1 -} InhibitMode; - -#include "logind.h" - -struct Inhibitor { - Manager *manager; - - sd_event_source *event_source; - - char *id; - char *state_file; - - bool started; - - InhibitWhat what; - char *who; - char *why; - InhibitMode mode; - - pid_t pid; - uid_t uid; - - dual_timestamp since; - - char *fifo_path; - int fifo_fd; -}; - -Inhibitor* inhibitor_new(Manager *m, const char *id); -void inhibitor_free(Inhibitor *i); - -int inhibitor_save(Inhibitor *i); -int inhibitor_load(Inhibitor *i); - -int inhibitor_start(Inhibitor *i); -int inhibitor_stop(Inhibitor *i); - -int inhibitor_create_fifo(Inhibitor *i); -void inhibitor_remove_fifo(Inhibitor *i); - -InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); -bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending); - -const char *inhibit_what_to_string(InhibitWhat k); -InhibitWhat inhibit_what_from_string(const char *s); - -const char *inhibit_mode_to_string(InhibitMode k); -InhibitMode inhibit_mode_from_string(const char *s); diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c deleted file mode 100644 index f934a5326a..0000000000 --- a/src/login/logind-seat-dbus.c +++ /dev/null @@ -1,474 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-label.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, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = s->active ? session_bus_path(s->active) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p); -} - -static int property_get_can_multi_session( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_multi_session(s)); -} - -static int property_get_can_tty( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_tty(s)); -} - -static int property_get_can_graphical( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_can_graphical(s)); -} - -static int property_get_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - Session *session; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", session->id, p); - if (r < 0) - return r; - - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return 1; -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Seat *s = userdata; - dual_timestamp t; - uint64_t u; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = seat_get_idle_hint(s, &t); - if (r < 0) - return r; - - u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", u); -} - -int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - UID_INVALID, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = seat_stop_sessions(s, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - const char *name; - Session *session; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - session = hashmap_get(s->manager->sessions, name); - if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name); - - if (session->seat != s) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); - - r = session_activate(session); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - unsigned int to; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "u", &to); - if (r < 0) - return r; - - if (to <= 0) - return -EINVAL; - - r = seat_switch_to(s, to); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = seat_switch_to_next(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Seat *s = userdata; - int r; - - assert(message); - assert(s); - - r = seat_switch_to_previous(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable seat_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Seat *seat; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/seat/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - Session *session; - const char *name; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - - session = hashmap_get(m->sessions, name); - if (!session) - return 0; - - seat = session->seat; - } else { - _cleanup_free_ char *e = NULL; - const char *p; - - p = startswith(path, "/org/freedesktop/login1/seat/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - seat = hashmap_get(m->seats, e); - } - - if (!seat) - return 0; - - *found = seat; - return 1; -} - -char *seat_bus_path(Seat *s) { - _cleanup_free_ char *t = NULL; - - assert(s); - - t = bus_label_escape(s->id); - if (!t) - return NULL; - - return strappend("/org/freedesktop/login1/seat/", t); -} - -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - Seat *seat; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(seat, m->seats, i) { - char *p; - - p = seat_bus_path(seat); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *name; - Session *session; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_session(creds, &name); - if (r >= 0) { - session = hashmap_get(m->sessions, name); - if (session && session->seat) { - r = strv_extend(&l, "/org/freedesktop/login1/seat/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int seat_send_signal(Seat *s, bool new_seat) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = seat_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_seat ? "SeatNew" : "SeatRemoved", - "so", s->id, p); -} - -int seat_send_changed(Seat *s, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(s); - - if (!s->started) - return 0; - - p = seat_bus_path(s); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l); -} diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c deleted file mode 100644 index b5192320e4..0000000000 --- a/src/login/logind-seat.c +++ /dev/null @@ -1,695 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "logind-acl.h" -#include "logind-seat.h" -#include "mkdir.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; - - assert(m); - assert(id); - - s = new0(Seat, 1); - if (!s) - return NULL; - - s->state_file = strappend("/run/systemd/seats/", id); - if (!s->state_file) { - free(s); - return NULL; - } - - s->id = basename(s->state_file); - s->manager = m; - - if (hashmap_put(m->seats, s->id, s) < 0) { - free(s->state_file); - free(s); - return NULL; - } - - return s; -} - -void seat_free(Seat *s) { - assert(s); - - if (s->in_gc_queue) - LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s); - - while (s->sessions) - session_free(s->sessions); - - assert(!s->active); - - while (s->devices) - device_free(s->devices); - - hashmap_remove(s->manager->seats, s->id); - - free(s->positions); - free(s->state_file); - free(s); -} - -int seat_save(Seat *s) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(s); - - if (!s->started) - return 0; - - r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(s->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "IS_SEAT0=%i\n" - "CAN_MULTI_SESSION=%i\n" - "CAN_TTY=%i\n" - "CAN_GRAPHICAL=%i\n", - seat_is_seat0(s), - seat_can_multi_session(s), - seat_can_tty(s), - seat_can_graphical(s)); - - if (s->active) { - assert(s->active->user); - - fprintf(f, - "ACTIVE=%s\n" - "ACTIVE_UID="UID_FMT"\n", - s->active->id, - s->active->user->uid); - } - - if (s->sessions) { - Session *i; - - fputs("SESSIONS=", f); - LIST_FOREACH(sessions_by_seat, i, s->sessions) { - fprintf(f, - "%s%c", - i->id, - i->sessions_by_seat_next ? ' ' : '\n'); - } - - fputs("UIDS=", f); - LIST_FOREACH(sessions_by_seat, i, s->sessions) - fprintf(f, - UID_FMT"%c", - i->user->uid, - i->sessions_by_seat_next ? ' ' : '\n'); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, s->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file); -} - -int seat_load(Seat *s) { - assert(s); - - /* There isn't actually anything to read here ... */ - - return 0; -} - -static int vt_allocate(unsigned int vtnr) { - char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)]; - _cleanup_close_ int fd = -1; - - assert(vtnr >= 1); - - xsprintf(p, "/dev/tty%u", vtnr); - fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return 0; -} - -int seat_preallocate_vts(Seat *s) { - int r = 0; - unsigned i; - - assert(s); - assert(s->manager); - - log_debug("Preallocating VTs..."); - - if (s->manager->n_autovts <= 0) - return 0; - - if (!seat_has_vts(s)) - return 0; - - for (i = 1; i <= s->manager->n_autovts; i++) { - int q; - - q = vt_allocate(i); - if (q < 0) { - log_error_errno(q, "Failed to preallocate VT %u: %m", i); - r = q; - } - } - - return r; -} - -int seat_apply_acls(Seat *s, Session *old_active) { - int r; - - assert(s); - - r = devnode_acl_all(s->manager->udev, - s->id, - false, - !!old_active, old_active ? old_active->user->uid : 0, - !!s->active, s->active ? s->active->user->uid : 0); - - if (r < 0) - log_error_errno(r, "Failed to apply ACLs: %m"); - - return r; -} - -int seat_set_active(Seat *s, Session *session) { - Session *old_active; - - assert(s); - assert(!session || session->seat == s); - - if (session == s->active) - return 0; - - old_active = s->active; - s->active = session; - - if (old_active) { - session_device_pause_all(old_active); - session_send_changed(old_active, "Active", NULL); - } - - seat_apply_acls(s, old_active); - - if (session && session->started) { - session_send_changed(session, "Active", NULL); - session_device_resume_all(session); - } - - if (!session || session->started) - seat_send_changed(s, "ActiveSession", NULL); - - seat_save(s); - - if (session) { - session_save(session); - user_save(session->user); - } - - if (old_active) { - session_save(old_active); - if (!session || session->user != old_active->user) - user_save(old_active->user); - } - - return 0; -} - -int seat_switch_to(Seat *s, unsigned int num) { - /* Public session positions skip 0 (there is only F1-F12). Maybe it - * will get reassigned in the future, so return error for now. */ - if (num == 0) - return -EINVAL; - - if (num >= s->position_count || !s->positions[num]) { - /* allow switching to unused VTs to trigger auto-activate */ - if (seat_has_vts(s) && num < 64) - return chvt(num); - - return -EINVAL; - } - - return session_activate(s->positions[num]); -} - -int seat_switch_to_next(Seat *s) { - unsigned int start, i; - - if (s->position_count == 0) - return -EINVAL; - - start = 1; - if (s->active && s->active->position > 0) - start = s->active->position; - - for (i = start + 1; i < s->position_count; ++i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - for (i = 1; i < start; ++i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - return -EINVAL; -} - -int seat_switch_to_previous(Seat *s) { - unsigned int start, i; - - if (s->position_count == 0) - return -EINVAL; - - start = 1; - if (s->active && s->active->position > 0) - start = s->active->position; - - for (i = start - 1; i > 0; --i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - for (i = s->position_count - 1; i > start; --i) - if (s->positions[i]) - return session_activate(s->positions[i]); - - return -EINVAL; -} - -int seat_active_vt_changed(Seat *s, unsigned int vtnr) { - Session *i, *new_active = NULL; - int r; - - assert(s); - assert(vtnr >= 1); - - if (!seat_has_vts(s)) - return -EINVAL; - - log_debug("VT changed to %u", vtnr); - - /* we might have earlier closing sessions on the same VT, so try to - * find a running one first */ - LIST_FOREACH(sessions_by_seat, i, s->sessions) - if (i->vtnr == vtnr && !i->stopping) { - new_active = i; - break; - } - - if (!new_active) { - /* no running one? then we can't decide which one is the - * active one, let the first one win */ - LIST_FOREACH(sessions_by_seat, i, s->sessions) - if (i->vtnr == vtnr) { - new_active = i; - break; - } - } - - r = seat_set_active(s, new_active); - manager_spawn_autovt(s->manager, vtnr); - - return r; -} - -int seat_read_active_vt(Seat *s) { - char t[64]; - ssize_t k; - unsigned int vtnr; - int r; - - assert(s); - - if (!seat_has_vts(s)) - return 0; - - lseek(s->manager->console_active_fd, SEEK_SET, 0); - - k = read(s->manager->console_active_fd, t, sizeof(t)-1); - if (k <= 0) { - log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); - return k < 0 ? -errno : -EIO; - } - - t[k] = 0; - truncate_nl(t); - - if (!startswith(t, "tty")) { - log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); - return -EIO; - } - - r = safe_atou(t+3, &vtnr); - if (r < 0) { - log_error("Failed to parse VT number %s", t+3); - return r; - } - - if (!vtnr) { - log_error("VT number invalid: %s", t+3); - return -EIO; - } - - return seat_active_vt_changed(s, vtnr); -} - -int seat_start(Seat *s) { - assert(s); - - if (s->started) - return 0; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), - "SEAT_ID=%s", s->id, - LOG_MESSAGE("New seat %s.", s->id), - NULL); - - /* Initialize VT magic stuff */ - seat_preallocate_vts(s); - - /* Read current VT */ - seat_read_active_vt(s); - - s->started = true; - - /* Save seat data */ - seat_save(s); - - seat_send_signal(s, true); - - return 0; -} - -int seat_stop(Seat *s, bool force) { - int r = 0; - - assert(s); - - if (s->started) - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), - "SEAT_ID=%s", s->id, - LOG_MESSAGE("Removed seat %s.", s->id), - NULL); - - seat_stop_sessions(s, force); - - unlink(s->state_file); - seat_add_to_gc_queue(s); - - if (s->started) - seat_send_signal(s, false); - - s->started = false; - - return r; -} - -int seat_stop_sessions(Seat *s, bool force) { - Session *session; - int r = 0, k; - - assert(s); - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - k = session_stop(session, force); - if (k < 0) - r = k; - } - - return r; -} - -void seat_evict_position(Seat *s, Session *session) { - Session *iter; - unsigned int pos = session->position; - - session->position = 0; - - if (pos == 0) - return; - - if (pos < s->position_count && s->positions[pos] == session) { - s->positions[pos] = NULL; - - /* There might be another session claiming the same - * position (eg., during gdm->session transition), so let's look - * for it and set it on the free slot. */ - LIST_FOREACH(sessions_by_seat, iter, s->sessions) { - if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) { - s->positions[pos] = iter; - break; - } - } - } -} - -void seat_claim_position(Seat *s, Session *session, unsigned int pos) { - /* with VTs, the position is always the same as the VTnr */ - if (seat_has_vts(s)) - pos = session->vtnr; - - if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1)) - return; - - seat_evict_position(s, session); - - session->position = pos; - if (pos > 0) - s->positions[pos] = session; -} - -static void seat_assign_position(Seat *s, Session *session) { - unsigned int pos; - - if (session->position > 0) - return; - - for (pos = 1; pos < s->position_count; ++pos) - if (!s->positions[pos]) - break; - - seat_claim_position(s, session, pos); -} - -int seat_attach_session(Seat *s, Session *session) { - assert(s); - assert(session); - assert(!session->seat); - - if (!seat_has_vts(s) != !session->vtnr) - return -EINVAL; - - session->seat = s; - LIST_PREPEND(sessions_by_seat, s->sessions, session); - seat_assign_position(s, session); - - seat_send_changed(s, "Sessions", NULL); - - /* On seats with VTs, the VT logic defines which session is active. On - * seats without VTs, we automatically activate new sessions. */ - if (!seat_has_vts(s)) - seat_set_active(s, session); - - return 0; -} - -void seat_complete_switch(Seat *s) { - Session *session; - - assert(s); - - /* if no session-switch is pending or if it got canceled, do nothing */ - if (!s->pending_switch) - return; - - session = s->pending_switch; - s->pending_switch = NULL; - - seat_set_active(s, session); -} - -bool seat_has_vts(Seat *s) { - assert(s); - - return seat_is_seat0(s) && s->manager->console_active_fd >= 0; -} - -bool seat_is_seat0(Seat *s) { - assert(s); - - return s->manager->seat0 == s; -} - -bool seat_can_multi_session(Seat *s) { - assert(s); - - return seat_has_vts(s); -} - -bool seat_can_tty(Seat *s) { - assert(s); - - return seat_has_vts(s); -} - -bool seat_has_master_device(Seat *s) { - assert(s); - - /* device list is ordered by "master" flag */ - return !!s->devices && s->devices->master; -} - -bool seat_can_graphical(Seat *s) { - assert(s); - - return seat_has_master_device(s); -} - -int seat_get_idle_hint(Seat *s, dual_timestamp *t) { - Session *session; - bool idle_hint = true; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - - assert(s); - - LIST_FOREACH(sessions_by_seat, session, s->sessions) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(session, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic > ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -bool seat_check_gc(Seat *s, bool drop_not_started) { - assert(s); - - if (drop_not_started && !s->started) - return false; - - if (seat_is_seat0(s)) - return true; - - return seat_has_master_device(s); -} - -void seat_add_to_gc_queue(Seat *s) { - assert(s); - - if (s->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s); - s->in_gc_queue = true; -} - -static bool seat_name_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '-' || - c == '_'; -} - -bool seat_name_is_valid(const char *name) { - const char *p; - - assert(name); - - if (!startswith(name, "seat")) - return false; - - if (!name[4]) - return false; - - for (p = name; *p; p++) - if (!seat_name_valid_char(*p)) - return false; - - if (strlen(name) > 255) - return false; - - return true; -} diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h deleted file mode 100644 index 9a4fbc5bc5..0000000000 --- a/src/login/logind-seat.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Seat Seat; - -#include "list.h" -#include "logind-session.h" - -struct Seat { - Manager *manager; - char *id; - - char *state_file; - - LIST_HEAD(Device, devices); - - Session *active; - Session *pending_switch; - LIST_HEAD(Session, sessions); - - Session **positions; - size_t position_count; - - bool in_gc_queue:1; - bool started:1; - - LIST_FIELDS(Seat, gc_queue); -}; - -Seat *seat_new(Manager *m, const char *id); -void seat_free(Seat *s); - -int seat_save(Seat *s); -int seat_load(Seat *s); - -int seat_apply_acls(Seat *s, Session *old_active); -int seat_set_active(Seat *s, Session *session); -int seat_switch_to(Seat *s, unsigned int num); -int seat_switch_to_next(Seat *s); -int seat_switch_to_previous(Seat *s); -int seat_active_vt_changed(Seat *s, unsigned int vtnr); -int seat_read_active_vt(Seat *s); -int seat_preallocate_vts(Seat *s); - -int seat_attach_session(Seat *s, Session *session); -void seat_complete_switch(Seat *s); -void seat_evict_position(Seat *s, Session *session); -void seat_claim_position(Seat *s, Session *session, unsigned int pos); - -bool seat_has_vts(Seat *s); -bool seat_is_seat0(Seat *s); -bool seat_can_multi_session(Seat *s); -bool seat_can_tty(Seat *s); -bool seat_has_master_device(Seat *s); -bool seat_can_graphical(Seat *s); - -int seat_get_idle_hint(Seat *s, dual_timestamp *t); - -int seat_start(Seat *s); -int seat_stop(Seat *s, bool force); -int seat_stop_sessions(Seat *s, bool force); - -bool seat_check_gc(Seat *s, bool drop_not_started); -void seat_add_to_gc_queue(Seat *s); - -bool seat_name_is_valid(const char *name); - -extern const sd_bus_vtable seat_vtable[]; - -int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *seat_bus_path(Seat *s); - -int seat_send_signal(Seat *s, bool new_seat); -int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_; - -int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c deleted file mode 100644 index 22dea5db1f..0000000000 --- a/src/login/logind-session-dbus.c +++ /dev/null @@ -1,798 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-label.h" -#include "bus-util.h" -#include "fd-util.h" -#include "logind-session-device.h" -#include "logind-session.h" -#include "logind.h" -#include "signal-util.h" -#include "strv.h" -#include "util.h" - -static int property_get_user( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = user_bus_path(s->user); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p); -} - -static int property_get_name( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "s", s->user->name); -} - -static int property_get_seat( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - p = s->seat ? seat_bus_path(s->seat) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass); - -static int property_get_active( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_is_active(s)); -} - -static int property_get_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s))); -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - uint64_t u; - int r; - - assert(bus); - assert(reply); - assert(s); - - r = session_get_idle_hint(s, &t); - if (r < 0) - return r; - - u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", u); -} - -static int property_get_locked_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Session *s = userdata; - - assert(bus); - assert(reply); - assert(s); - - return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); -} - -int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_stop(s, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = session_activate(s); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - int r; - - assert(message); - assert(s); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.lock-sessions", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock")); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - uid_t uid; - int r, b; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && uid != s->user->uid) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint"); - - session_set_idle_hint(s, b); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - uid_t uid; - int r, b; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && uid != s->user->uid) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); - - session_set_locked_hint(s, b); - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - const char *swho; - int32_t signo; - KillWho who; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "si", &swho, &signo); - if (r < 0) - return r; - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); - } - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - s->user->uid, - &s->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = session_kill(s, who, signo); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - Session *s = userdata; - int r, force; - uid_t uid; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "b", &force); - if (r < 0) - return r; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - if (uid != 0 && (force || uid != s->user->uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control"); - - r = session_set_controller(s, sd_bus_message_get_sender(message), force); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_release_control(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - - assert(message); - assert(s); - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - session_drop_controller(s); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (sd) - /* We don't allow retrieving a device multiple times. - * The related ReleaseDevice call is not ref-counted. - * The caller should use dup() if it requires more - * than one fd (it would be functionally - * equivalent). */ - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken"); - - r = session_device_new(s, dev, &sd); - if (r < 0) - return r; - - r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active); - if (r < 0) - session_device_free(sd); - - return r; -} - -static int method_release_device(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (!sd) - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); - - session_device_free(sd); - return sd_bus_reply_method_return(message, NULL); -} - -static int method_pause_device_complete(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Session *s = userdata; - uint32_t major, minor; - SessionDevice *sd; - dev_t dev; - int r; - - assert(message); - assert(s); - - r = sd_bus_message_read(message, "uu", &major, &minor); - if (r < 0) - return r; - - if (!session_is_controller(s, sd_bus_message_get_sender(message))) - return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session"); - - dev = makedev(major, minor); - sd = hashmap_get(s->devices, &dev); - if (!sd) - return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); - - session_device_complete_pause(sd); - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_SIGNAL("PauseDevice", "uus", 0), - SD_BUS_SIGNAL("ResumeDevice", "uuh", 0), - SD_BUS_SIGNAL("Lock", NULL, 0), - SD_BUS_SIGNAL("Unlock", NULL, 0), - - SD_BUS_VTABLE_END -}; - -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Session *session; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/session/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - const char *name; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_session(creds, &name); - if (r < 0) - return r; - - session = hashmap_get(m->sessions, name); - } else { - _cleanup_free_ char *e = NULL; - const char *p; - - p = startswith(path, "/org/freedesktop/login1/session/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - session = hashmap_get(m->sessions, e); - } - - if (!session) - return 0; - - *found = session; - return 1; -} - -char *session_bus_path(Session *s) { - _cleanup_free_ char *t = NULL; - - assert(s); - - t = bus_label_escape(s->id); - if (!t) - return NULL; - - return strappend("/org/freedesktop/login1/session/", t); -} - -int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - Session *session; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(session, m->sessions, i) { - char *p; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *name; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_session(creds, &name); - if (r >= 0) { - session = hashmap_get(m->sessions, name); - if (session) { - r = strv_extend(&l, "/org/freedesktop/login1/session/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int session_send_signal(Session *s, bool new_session) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_session ? "SessionNew" : "SessionRemoved", - "so", s->id, p); -} - -int session_send_changed(Session *s, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(s); - - if (!s->started) - return 0; - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l); -} - -int session_send_lock(Session *s, bool lock) { - _cleanup_free_ char *p = NULL; - - assert(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - s->manager->bus, - p, - "org.freedesktop.login1.Session", - lock ? "Lock" : "Unlock", - NULL); -} - -int session_send_lock_all(Manager *m, bool lock) { - Session *session; - Iterator i; - int r = 0; - - assert(m); - - HASHMAP_FOREACH(session, m->sessions, i) { - int k; - - k = session_send_lock(session, lock); - if (k < 0) - r = k; - } - - return r; -} - -int session_send_create_reply(Session *s, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; - _cleanup_close_ int fifo_fd = -1; - _cleanup_free_ char *p = NULL; - - assert(s); - - /* This is called after the session scope and the user service - * were successfully created, and finishes where - * bus_manager_create_session() left off. */ - - if (!s->create_message) - return 0; - - if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job)) - return 0; - - c = s->create_message; - s->create_message = NULL; - - if (error) - return sd_bus_reply_method_error(c, error); - - fifo_fd = session_create_fifo(s); - if (fifo_fd < 0) - return fifo_fd; - - /* Update the session state file before we notify the client - * about the result. */ - session_save(s); - - p = session_bus_path(s); - if (!p) - return -ENOMEM; - - log_debug("Sending reply about created session: " - "id=%s object_path=%s uid=%u runtime_path=%s " - "session_fd=%d seat=%s vtnr=%u", - s->id, - p, - (uint32_t) s->user->uid, - s->user->runtime_path, - fifo_fd, - s->seat ? s->seat->id : "", - (uint32_t) s->vtnr); - - return sd_bus_reply_method_return( - c, "soshusub", - s->id, - p, - s->user->runtime_path, - fifo_fd, - (uint32_t) s->user->uid, - s->seat ? s->seat->id : "", - (uint32_t) s->vtnr, - false); -} diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c deleted file mode 100644 index 4055a23277..0000000000 --- a/src/login/logind-session-device.c +++ /dev/null @@ -1,480 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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, - SESSION_DEVICE_TRY_PAUSE, - SESSION_DEVICE_PAUSE, - SESSION_DEVICE_RELEASE, -}; - -static int session_device_notify(SessionDevice *sd, enum SessionDeviceNotifications type) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *path = NULL; - const char *t = NULL; - uint32_t major, minor; - int r; - - assert(sd); - - major = major(sd->dev); - minor = minor(sd->dev); - - if (!sd->session->controller) - return 0; - - path = session_bus_path(sd->session); - if (!path) - return -ENOMEM; - - r = sd_bus_message_new_signal( - sd->session->manager->bus, - &m, path, - "org.freedesktop.login1.Session", - (type == SESSION_DEVICE_RESUME) ? "ResumeDevice" : "PauseDevice"); - if (!m) - return r; - - r = sd_bus_message_set_destination(m, sd->session->controller); - if (r < 0) - return r; - - switch (type) { - case SESSION_DEVICE_RESUME: - r = sd_bus_message_append(m, "uuh", major, minor, sd->fd); - if (r < 0) - return r; - break; - case SESSION_DEVICE_TRY_PAUSE: - t = "pause"; - break; - case SESSION_DEVICE_PAUSE: - t = "force"; - break; - case SESSION_DEVICE_RELEASE: - t = "gone"; - break; - default: - return -EINVAL; - } - - if (t) { - r = sd_bus_message_append(m, "uus", major, minor, t); - if (r < 0) - return r; - } - - return sd_bus_send(sd->session->manager->bus, m, NULL); -} - -static int sd_eviocrevoke(int fd) { - static bool warned; - int r; - - assert(fd >= 0); - - r = ioctl(fd, EVIOCREVOKE, NULL); - if (r < 0) { - r = -errno; - if (r == -EINVAL && !warned) { - warned = true; - log_warning("kernel does not support evdev-revocation"); - } - } - - return 0; -} - -static int sd_drmsetmaster(int fd) { - int r; - - assert(fd >= 0); - - r = ioctl(fd, DRM_IOCTL_SET_MASTER, 0); - if (r < 0) - return -errno; - - return 0; -} - -static int sd_drmdropmaster(int fd) { - int r; - - assert(fd >= 0); - - r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); - if (r < 0) - return -errno; - - return 0; -} - -static int session_device_open(SessionDevice *sd, bool active) { - int fd, r; - - assert(sd->type != DEVICE_TYPE_UNKNOWN); - - /* open device and try to get an udev_device from it */ - fd = open(sd->node, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (fd < 0) - return -errno; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - if (active) { - /* Weird legacy DRM semantics might return an error - * even though we're master. No way to detect that so - * fail at all times and let caller retry in inactive - * state. */ - r = sd_drmsetmaster(fd); - if (r < 0) { - close_nointr(fd); - return r; - } - } else { - /* DRM-Master is granted to the first user who opens a - * device automatically (ughh, racy!). Hence, we just - * drop DRM-Master in case we were the first. */ - sd_drmdropmaster(fd); - } - break; - case DEVICE_TYPE_EVDEV: - if (!active) - sd_eviocrevoke(fd); - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices wihout synchronizations */ - break; - } - - return fd; -} - -static int session_device_start(SessionDevice *sd) { - int r; - - assert(sd); - assert(session_is_active(sd->session)); - - if (sd->active) - return 0; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - /* Device is kept open. Simply call drmSetMaster() and hope - * there is no-one else. In case it fails, we keep the device - * paused. Maybe at some point we have a drmStealMaster(). */ - r = sd_drmsetmaster(sd->fd); - if (r < 0) - return r; - break; - case DEVICE_TYPE_EVDEV: - /* Evdev devices are revoked while inactive. Reopen it and we - * are fine. */ - r = session_device_open(sd, true); - if (r < 0) - return r; - close_nointr(sd->fd); - sd->fd = r; - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices wihout synchronizations */ - break; - } - - sd->active = true; - return 0; -} - -static void session_device_stop(SessionDevice *sd) { - assert(sd); - - if (!sd->active) - return; - - switch (sd->type) { - case DEVICE_TYPE_DRM: - /* On DRM devices we simply drop DRM-Master but keep it open. - * This allows the user to keep resources allocated. The - * CAP_SYS_ADMIN restriction to DRM-Master prevents users from - * circumventing this. */ - sd_drmdropmaster(sd->fd); - break; - case DEVICE_TYPE_EVDEV: - /* Revoke access on evdev file-descriptors during deactivation. - * This will basically prevent any operations on the fd and - * cannot be undone. Good side is: it needs no CAP_SYS_ADMIN - * protection this way. */ - sd_eviocrevoke(sd->fd); - break; - case DEVICE_TYPE_UNKNOWN: - default: - /* fallback for devices without synchronization */ - break; - } - - sd->active = false; -} - -static DeviceType detect_device_type(struct udev_device *dev) { - const char *sysname, *subsystem; - DeviceType type; - - sysname = udev_device_get_sysname(dev); - subsystem = udev_device_get_subsystem(dev); - type = DEVICE_TYPE_UNKNOWN; - - if (streq_ptr(subsystem, "drm")) { - if (startswith(sysname, "card")) - type = DEVICE_TYPE_DRM; - } else if (streq_ptr(subsystem, "input")) { - if (startswith(sysname, "event")) - type = DEVICE_TYPE_EVDEV; - } - - return type; -} - -static int session_device_verify(SessionDevice *sd) { - struct udev_device *dev, *p = NULL; - const char *sp, *node; - int r; - - dev = udev_device_new_from_devnum(sd->session->manager->udev, 'c', sd->dev); - if (!dev) - return -ENODEV; - - sp = udev_device_get_syspath(dev); - node = udev_device_get_devnode(dev); - if (!node) { - r = -EINVAL; - goto err_dev; - } - - /* detect device type so we can find the correct sysfs parent */ - sd->type = detect_device_type(dev); - if (sd->type == DEVICE_TYPE_UNKNOWN) { - r = -ENODEV; - goto err_dev; - } else if (sd->type == DEVICE_TYPE_EVDEV) { - /* for evdev devices we need the parent node as device */ - p = dev; - dev = udev_device_get_parent_with_subsystem_devtype(p, "input", NULL); - if (!dev) { - r = -ENODEV; - goto err_dev; - } - sp = udev_device_get_syspath(dev); - } else if (sd->type != DEVICE_TYPE_DRM) { - /* Prevent opening unsupported devices. Especially devices of - * subsystem "input" must be opened via the evdev node as - * we require EVIOCREVOKE. */ - r = -ENODEV; - goto err_dev; - } - - /* search for an existing seat device and return it if available */ - sd->device = hashmap_get(sd->session->manager->devices, sp); - if (!sd->device) { - /* The caller might have gotten the udev event before we were - * able to process it. Hence, fake the "add" event and let the - * logind-manager handle the new device. */ - r = manager_process_seat_device(sd->session->manager, dev); - if (r < 0) - goto err_dev; - - /* if it's still not available, then the device is invalid */ - sd->device = hashmap_get(sd->session->manager->devices, sp); - if (!sd->device) { - r = -ENODEV; - goto err_dev; - } - } - - if (sd->device->seat != sd->session->seat) { - r = -EPERM; - goto err_dev; - } - - sd->node = strdup(node); - if (!sd->node) { - r = -ENOMEM; - goto err_dev; - } - - r = 0; -err_dev: - udev_device_unref(p ? : dev); - return r; -} - -int session_device_new(Session *s, dev_t dev, SessionDevice **out) { - SessionDevice *sd; - int r; - - assert(s); - assert(out); - - if (!s->seat) - return -EPERM; - - sd = new0(SessionDevice, 1); - if (!sd) - return -ENOMEM; - - sd->session = s; - sd->dev = dev; - sd->fd = -1; - sd->type = DEVICE_TYPE_UNKNOWN; - - r = session_device_verify(sd); - if (r < 0) - goto error; - - r = hashmap_put(s->devices, &sd->dev, sd); - if (r < 0) { - r = -ENOMEM; - goto error; - } - - /* Open the device for the first time. We need a valid fd to pass back - * to the caller. If the session is not active, this _might_ immediately - * revoke access and thus invalidate the fd. But this is still needed - * to pass a valid fd back. */ - sd->active = session_is_active(s); - r = session_device_open(sd, sd->active); - if (r < 0) { - /* EINVAL _may_ mean a master is active; retry inactive */ - if (sd->active && r == -EINVAL) { - sd->active = false; - r = session_device_open(sd, false); - } - if (r < 0) - goto error; - } - sd->fd = r; - - LIST_PREPEND(sd_by_device, sd->device->session_devices, sd); - - *out = sd; - return 0; - -error: - hashmap_remove(s->devices, &sd->dev); - free(sd->node); - free(sd); - return r; -} - -void session_device_free(SessionDevice *sd) { - assert(sd); - - session_device_stop(sd); - session_device_notify(sd, SESSION_DEVICE_RELEASE); - close_nointr(sd->fd); - - LIST_REMOVE(sd_by_device, sd->device->session_devices, sd); - - hashmap_remove(sd->session->devices, &sd->dev); - - free(sd->node); - free(sd); -} - -void session_device_complete_pause(SessionDevice *sd) { - SessionDevice *iter; - Iterator i; - - if (!sd->active) - return; - - session_device_stop(sd); - - /* if not all devices are paused, wait for further completion events */ - HASHMAP_FOREACH(iter, sd->session->devices, i) - if (iter->active) - return; - - /* complete any pending session switch */ - seat_complete_switch(sd->session->seat); -} - -void session_device_resume_all(Session *s) { - SessionDevice *sd; - Iterator i; - int r; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (!sd->active) { - r = session_device_start(sd); - if (!r) - session_device_notify(sd, SESSION_DEVICE_RESUME); - } - } -} - -void session_device_pause_all(Session *s) { - SessionDevice *sd; - Iterator i; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (sd->active) { - session_device_stop(sd); - session_device_notify(sd, SESSION_DEVICE_PAUSE); - } - } -} - -unsigned int session_device_try_pause_all(Session *s) { - SessionDevice *sd; - Iterator i; - unsigned int num_pending = 0; - - assert(s); - - HASHMAP_FOREACH(sd, s->devices, i) { - if (sd->active) { - session_device_notify(sd, SESSION_DEVICE_TRY_PAUSE); - ++num_pending; - } - } - - return num_pending; -} diff --git a/src/login/logind-session-device.h b/src/login/logind-session-device.h deleted file mode 100644 index 7c8503583f..0000000000 --- a/src/login/logind-session-device.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef enum DeviceType DeviceType; -typedef struct SessionDevice SessionDevice; - -#include "list.h" -#include "logind.h" - -enum DeviceType { - DEVICE_TYPE_UNKNOWN, - DEVICE_TYPE_DRM, - DEVICE_TYPE_EVDEV, -}; - -struct SessionDevice { - Session *session; - Device *device; - - dev_t dev; - char *node; - int fd; - bool active; - DeviceType type; - - LIST_FIELDS(struct SessionDevice, sd_by_device); -}; - -int session_device_new(Session *s, dev_t dev, SessionDevice **out); -void session_device_free(SessionDevice *sd); -void session_device_complete_pause(SessionDevice *sd); - -void session_device_resume_all(Session *s); -void session_device_pause_all(Session *s); -unsigned int session_device_try_pause_all(Session *s); diff --git a/src/login/logind-session.c b/src/login/logind-session.c deleted file mode 100644 index b6da237397..0000000000 --- a/src/login/logind-session.c +++ /dev/null @@ -1,1273 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "audit-util.h" -#include "bus-error.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) - -static void session_remove_fifo(Session *s); - -Session* session_new(Manager *m, const char *id) { - Session *s; - - assert(m); - assert(id); - assert(session_id_valid(id)); - - s = new0(Session, 1); - if (!s) - return NULL; - - s->state_file = strappend("/run/systemd/sessions/", id); - if (!s->state_file) { - free(s); - return NULL; - } - - s->devices = hashmap_new(&devt_hash_ops); - if (!s->devices) { - free(s->state_file); - free(s); - return NULL; - } - - s->id = basename(s->state_file); - - if (hashmap_put(m->sessions, s->id, s) < 0) { - hashmap_free(s->devices); - free(s->state_file); - free(s); - return NULL; - } - - s->manager = m; - s->fifo_fd = -1; - s->vtfd = -1; - - return s; -} - -void session_free(Session *s) { - SessionDevice *sd; - - assert(s); - - if (s->in_gc_queue) - LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - session_remove_fifo(s); - - session_drop_controller(s); - - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - hashmap_free(s->devices); - - if (s->user) { - LIST_REMOVE(sessions_by_user, s->user->sessions, s); - - if (s->user->display == s) - s->user->display = NULL; - } - - if (s->seat) { - if (s->seat->active == s) - s->seat->active = NULL; - if (s->seat->pending_switch == s) - s->seat->pending_switch = NULL; - - seat_evict_position(s->seat, s); - LIST_REMOVE(sessions_by_seat, s->seat->sessions, s); - } - - if (s->scope) { - hashmap_remove(s->manager->session_units, s->scope); - free(s->scope); - } - - free(s->scope_job); - - sd_bus_message_unref(s->create_message); - - free(s->tty); - free(s->display); - free(s->remote_host); - free(s->remote_user); - free(s->service); - free(s->desktop); - - hashmap_remove(s->manager->sessions, s->id); - - free(s->state_file); - free(s); -} - -void session_set_user(Session *s, User *u) { - assert(s); - assert(!s->user); - - s->user = u; - LIST_PREPEND(sessions_by_user, u->sessions, s); -} - -int session_save(Session *s) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r = 0; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (!s->started) - return 0; - - r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(s->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - assert(s->user); - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "UID="UID_FMT"\n" - "USER=%s\n" - "ACTIVE=%i\n" - "STATE=%s\n" - "REMOTE=%i\n", - s->user->uid, - s->user->name, - session_is_active(s), - session_state_to_string(session_get_state(s)), - s->remote); - - if (s->type >= 0) - fprintf(f, "TYPE=%s\n", session_type_to_string(s->type)); - - if (s->class >= 0) - fprintf(f, "CLASS=%s\n", session_class_to_string(s->class)); - - if (s->scope) - fprintf(f, "SCOPE=%s\n", s->scope); - if (s->scope_job) - fprintf(f, "SCOPE_JOB=%s\n", s->scope_job); - - if (s->fifo_path) - fprintf(f, "FIFO=%s\n", s->fifo_path); - - if (s->seat) - fprintf(f, "SEAT=%s\n", s->seat->id); - - if (s->tty) - fprintf(f, "TTY=%s\n", s->tty); - - if (s->display) - fprintf(f, "DISPLAY=%s\n", s->display); - - if (s->remote_host) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->remote_host); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "REMOTE_HOST=%s\n", escaped); - } - - if (s->remote_user) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->remote_user); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "REMOTE_USER=%s\n", escaped); - } - - if (s->service) { - _cleanup_free_ char *escaped; - - escaped = cescape(s->service); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "SERVICE=%s\n", escaped); - } - - if (s->desktop) { - _cleanup_free_ char *escaped; - - - escaped = cescape(s->desktop); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "DESKTOP=%s\n", escaped); - } - - if (s->seat && seat_has_vts(s->seat)) - fprintf(f, "VTNR=%u\n", s->vtnr); - - if (!s->vtnr) - fprintf(f, "POSITION=%u\n", s->position); - - if (s->leader > 0) - fprintf(f, "LEADER="PID_FMT"\n", s->leader); - - if (s->audit_id > 0) - fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id); - - if (dual_timestamp_is_set(&s->timestamp)) - fprintf(f, - "REALTIME="USEC_FMT"\n" - "MONOTONIC="USEC_FMT"\n", - s->timestamp.realtime, - s->timestamp.monotonic); - - if (s->controller) - fprintf(f, "CONTROLLER=%s\n", s->controller); - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, s->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(s->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save session data %s: %m", s->state_file); -} - - -int session_load(Session *s) { - _cleanup_free_ char *remote = NULL, - *seat = NULL, - *vtnr = NULL, - *state = NULL, - *position = NULL, - *leader = NULL, - *type = NULL, - *class = NULL, - *uid = NULL, - *realtime = NULL, - *monotonic = NULL, - *controller = NULL; - - int k, r; - - assert(s); - - r = parse_env_file(s->state_file, NEWLINE, - "REMOTE", &remote, - "SCOPE", &s->scope, - "SCOPE_JOB", &s->scope_job, - "FIFO", &s->fifo_path, - "SEAT", &seat, - "TTY", &s->tty, - "DISPLAY", &s->display, - "REMOTE_HOST", &s->remote_host, - "REMOTE_USER", &s->remote_user, - "SERVICE", &s->service, - "DESKTOP", &s->desktop, - "VTNR", &vtnr, - "STATE", &state, - "POSITION", &position, - "LEADER", &leader, - "TYPE", &type, - "CLASS", &class, - "UID", &uid, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - "CONTROLLER", &controller, - NULL); - - if (r < 0) - return log_error_errno(r, "Failed to read %s: %m", s->state_file); - - if (!s->user) { - uid_t u; - User *user; - - if (!uid) { - log_error("UID not specified for session %s", s->id); - return -ENOENT; - } - - r = parse_uid(uid, &u); - if (r < 0) { - log_error("Failed to parse UID value %s for session %s.", uid, s->id); - return r; - } - - user = hashmap_get(s->manager->users, UID_TO_PTR(u)); - if (!user) { - log_error("User of session %s not known.", s->id); - return -ENOENT; - } - - session_set_user(s, user); - } - - if (remote) { - k = parse_boolean(remote); - if (k >= 0) - s->remote = k; - } - - if (vtnr) - safe_atou(vtnr, &s->vtnr); - - if (seat && !s->seat) { - Seat *o; - - o = hashmap_get(s->manager->seats, seat); - if (o) - r = seat_attach_session(o, s); - if (!o || r < 0) - log_error("Cannot attach session %s to seat %s", s->id, seat); - } - - if (!s->seat || !seat_has_vts(s->seat)) - s->vtnr = 0; - - if (position && s->seat) { - unsigned int npos; - - safe_atou(position, &npos); - seat_claim_position(s->seat, s, npos); - } - - if (leader) { - k = parse_pid(leader, &s->leader); - if (k >= 0) - audit_session_from_pid(s->leader, &s->audit_id); - } - - if (type) { - SessionType t; - - t = session_type_from_string(type); - if (t >= 0) - s->type = t; - } - - if (class) { - SessionClass c; - - c = session_class_from_string(class); - if (c >= 0) - s->class = c; - } - - if (state && streq(state, "closing")) - s->stopping = true; - - if (s->fifo_path) { - int fd; - - /* If we open an unopened pipe for reading we will not - get an EOF. to trigger an EOF we hence open it for - writing, but close it right away which then will - trigger the EOF. This will happen immediately if no - other process has the FIFO open for writing, i. e. - when the session died before logind (re)started. */ - - fd = session_create_fifo(s); - safe_close(fd); - } - - if (realtime) - timestamp_deserialize(realtime, &s->timestamp.realtime); - if (monotonic) - timestamp_deserialize(monotonic, &s->timestamp.monotonic); - - if (controller) { - if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) - session_set_controller(s, controller, false); - else - session_restore_vt(s); - } - - return r; -} - -int session_activate(Session *s) { - unsigned int num_pending; - - assert(s); - assert(s->user); - - if (!s->seat) - return -EOPNOTSUPP; - - if (s->seat->active == s) - return 0; - - /* on seats with VTs, we let VTs manage session-switching */ - if (seat_has_vts(s->seat)) { - if (!s->vtnr) - return -EOPNOTSUPP; - - return chvt(s->vtnr); - } - - /* On seats without VTs, we implement session-switching in logind. We - * try to pause all session-devices and wait until the session - * controller acknowledged them. Once all devices are asleep, we simply - * switch the active session and be done. - * We save the session we want to switch to in seat->pending_switch and - * seat_complete_switch() will perform the final switch. */ - - s->seat->pending_switch = s; - - /* if no devices are running, immediately perform the session switch */ - num_pending = session_device_try_pause_all(s); - if (!num_pending) - seat_complete_switch(s->seat); - - return 0; -} - -static int session_start_scope(Session *s) { - int r; - - assert(s); - assert(s->user); - - if (!s->scope) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *scope, *job = NULL; - const char *description; - - scope = strjoin("session-", s->id, ".scope", NULL); - if (!scope) - return log_oom(); - - description = strjoina("Session ", s->id, " of user ", s->user->name); - - r = manager_start_scope( - s->manager, - scope, - s->leader, - s->user->slice, - description, - "systemd-logind.service", - "systemd-user-sessions.service", - (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ - &error, - &job); - if (r < 0) { - log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); - free(scope); - return r; - } else { - s->scope = scope; - - free(s->scope_job); - s->scope_job = job; - } - } - - if (s->scope) - (void) hashmap_put(s->manager->session_units, s->scope, s); - - return 0; -} - -int session_start(Session *s) { - int r; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (s->started) - return 0; - - r = user_start(s->user); - if (r < 0) - return r; - - /* Create cgroup */ - r = session_start_scope(s); - if (r < 0) - return r; - - log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), - "SESSION_ID=%s", s->id, - "USER_ID=%s", s->user->name, - "LEADER="PID_FMT, s->leader, - LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), - NULL); - - if (!dual_timestamp_is_set(&s->timestamp)) - dual_timestamp_get(&s->timestamp); - - if (s->seat) - seat_read_active_vt(s->seat); - - s->started = true; - - user_elect_display(s->user); - - /* Save data */ - session_save(s); - user_save(s->user); - if (s->seat) - seat_save(s->seat); - - /* Send signals */ - session_send_signal(s, true); - user_send_changed(s->user, "Sessions", "Display", NULL); - if (s->seat) { - if (s->seat->active == s) - seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL); - else - seat_send_changed(s->seat, "Sessions", NULL); - } - - return 0; -} - -static int session_stop_scope(Session *s, bool force) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(s); - - if (!s->scope) - return 0; - - /* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything - * that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log - * when killing any processes left after this point. */ - r = manager_abandon_scope(s->manager, s->scope, &error); - if (r < 0) - log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r)); - - /* Optionally, let's kill everything that's left now. */ - if (force || manager_shall_kill(s->manager, s->user->name)) { - char *job = NULL; - - r = manager_stop_unit(s->manager, s->scope, &error, &job); - if (r < 0) - return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r)); - - free(s->scope_job); - s->scope_job = job; - } else - s->scope_job = mfree(s->scope_job); - - return 0; -} - -int session_stop(Session *s, bool force) { - int r; - - assert(s); - - if (!s->user) - return -ESTALE; - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (s->seat) - seat_evict_position(s->seat, s); - - /* We are going down, don't care about FIFOs anymore */ - session_remove_fifo(s); - - /* Kill cgroup */ - r = session_stop_scope(s, force); - - s->stopping = true; - - user_elect_display(s->user); - - session_save(s); - user_save(s->user); - - return r; -} - -int session_finalize(Session *s) { - SessionDevice *sd; - - assert(s); - - if (!s->user) - return -ESTALE; - - if (s->started) - log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), - "SESSION_ID=%s", s->id, - "USER_ID=%s", s->user->name, - "LEADER="PID_FMT, s->leader, - LOG_MESSAGE("Removed session %s.", s->id), - NULL); - - s->timer_event_source = sd_event_source_unref(s->timer_event_source); - - if (s->seat) - seat_evict_position(s->seat, s); - - /* Kill session devices */ - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - (void) unlink(s->state_file); - session_add_to_gc_queue(s); - user_add_to_gc_queue(s->user); - - if (s->started) { - session_send_signal(s, false); - s->started = false; - } - - if (s->seat) { - if (s->seat->active == s) - seat_set_active(s->seat, NULL); - - seat_save(s->seat); - seat_send_changed(s->seat, "Sessions", NULL); - } - - user_save(s->user); - user_send_changed(s->user, "Sessions", "Display", NULL); - - return 0; -} - -static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) { - Session *s = userdata; - - assert(es); - assert(s); - - session_stop(s, false); - return 0; -} - -int session_release(Session *s) { - assert(s); - - if (!s->started || s->stopping) - return 0; - - if (s->timer_event_source) - return 0; - - return sd_event_add_time(s->manager->event, - &s->timer_event_source, - CLOCK_MONOTONIC, - now(CLOCK_MONOTONIC) + RELEASE_USEC, 0, - release_timeout_callback, s); -} - -bool session_is_active(Session *s) { - assert(s); - - if (!s->seat) - return true; - - return s->seat->active == s; -} - -static int get_tty_atime(const char *tty, usec_t *atime) { - _cleanup_free_ char *p = NULL; - struct stat st; - - assert(tty); - assert(atime); - - if (!path_is_absolute(tty)) { - p = strappend("/dev/", tty); - if (!p) - return -ENOMEM; - - tty = p; - } else if (!path_startswith(tty, "/dev/")) - return -ENOENT; - - if (lstat(tty, &st) < 0) - return -errno; - - *atime = timespec_load(&st.st_atim); - return 0; -} - -static int get_process_ctty_atime(pid_t pid, usec_t *atime) { - _cleanup_free_ char *p = NULL; - int r; - - assert(pid > 0); - assert(atime); - - r = get_ctty(pid, NULL, &p); - if (r < 0) - return r; - - return get_tty_atime(p, atime); -} - -int session_get_idle_hint(Session *s, dual_timestamp *t) { - usec_t atime = 0, n; - int r; - - assert(s); - - /* Explicit idle hint is set */ - if (s->idle_hint) { - if (t) - *t = s->idle_hint_timestamp; - - return s->idle_hint; - } - - /* Graphical sessions should really implement a real - * idle hint logic */ - if (SESSION_TYPE_IS_GRAPHICAL(s->type)) - goto dont_know; - - /* For sessions with an explicitly configured tty, let's check - * its atime */ - if (s->tty) { - r = get_tty_atime(s->tty, &atime); - if (r >= 0) - goto found_atime; - } - - /* For sessions with a leader but no explicitly configured - * tty, let's check the controlling tty of the leader */ - if (s->leader > 0) { - r = get_process_ctty_atime(s->leader, &atime); - if (r >= 0) - goto found_atime; - } - -dont_know: - if (t) - *t = s->idle_hint_timestamp; - - return 0; - -found_atime: - if (t) - dual_timestamp_from_realtime(t, atime); - - n = now(CLOCK_REALTIME); - - if (s->manager->idle_action_usec <= 0) - return 0; - - return atime + s->manager->idle_action_usec <= n; -} - -void session_set_idle_hint(Session *s, bool b) { - assert(s); - - if (s->idle_hint == b) - return; - - s->idle_hint = b; - dual_timestamp_get(&s->idle_hint_timestamp); - - session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - - if (s->seat) - seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - - user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); - manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); -} - -int session_get_locked_hint(Session *s) { - assert(s); - - return s->locked_hint; -} - -void session_set_locked_hint(Session *s, bool b) { - assert(s); - - if (s->locked_hint == b) - return; - - s->locked_hint = b; - - session_send_changed(s, "LockedHint", NULL); -} - -static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - Session *s = userdata; - - assert(s); - assert(s->fifo_fd == fd); - - /* EOF on the FIFO means the session died abnormally. */ - - session_remove_fifo(s); - session_stop(s, false); - - return 1; -} - -int session_create_fifo(Session *s) { - int r; - - assert(s); - - /* Create FIFO */ - if (!s->fifo_path) { - r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0); - if (r < 0) - return r; - - if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0) - return -ENOMEM; - - if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST) - return -errno; - } - - /* Open reading side */ - if (s->fifo_fd < 0) { - s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); - if (s->fifo_fd < 0) - return -errno; - - } - - if (!s->fifo_event_source) { - r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s); - if (r < 0) - return r; - - /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new - * sessions). */ - r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); - if (r < 0) - return r; - } - - /* Open writing side */ - r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); - if (r < 0) - return -errno; - - return r; -} - -static void session_remove_fifo(Session *s) { - assert(s); - - s->fifo_event_source = sd_event_source_unref(s->fifo_event_source); - s->fifo_fd = safe_close(s->fifo_fd); - - if (s->fifo_path) { - unlink(s->fifo_path); - s->fifo_path = mfree(s->fifo_path); - } -} - -bool session_check_gc(Session *s, bool drop_not_started) { - assert(s); - - if (drop_not_started && !s->started) - return false; - - if (!s->user) - return false; - - if (s->fifo_fd >= 0) { - if (pipe_eof(s->fifo_fd) <= 0) - return true; - } - - if (s->scope_job && manager_job_is_active(s->manager, s->scope_job)) - return true; - - if (s->scope && manager_unit_is_active(s->manager, s->scope)) - return true; - - return false; -} - -void session_add_to_gc_queue(Session *s) { - assert(s); - - if (s->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s); - s->in_gc_queue = true; -} - -SessionState session_get_state(Session *s) { - assert(s); - - /* always check closing first */ - if (s->stopping || s->timer_event_source) - return SESSION_CLOSING; - - if (s->scope_job || s->fifo_fd < 0) - return SESSION_OPENING; - - if (session_is_active(s)) - return SESSION_ACTIVE; - - return SESSION_ONLINE; -} - -int session_kill(Session *s, KillWho who, int signo) { - assert(s); - - if (!s->scope) - return -ESRCH; - - return manager_kill_unit(s->manager, s->scope, who, signo, NULL); -} - -static int session_open_vt(Session *s) { - char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; - - if (s->vtnr < 1) - return -ENODEV; - - if (s->vtfd >= 0) - return s->vtfd; - - sprintf(path, "/dev/tty%u", s->vtnr); - s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); - if (s->vtfd < 0) - return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id); - - return s->vtfd; -} - -int session_prepare_vt(Session *s) { - int vt, r; - struct vt_mode mode = { 0 }; - - if (s->vtnr < 1) - return 0; - - vt = session_open_vt(s); - if (vt < 0) - return vt; - - r = fchown(vt, s->user->uid, -1); - if (r < 0) { - r = log_error_errno(errno, - "Cannot change owner of /dev/tty%u: %m", - s->vtnr); - goto error; - } - - r = ioctl(vt, KDSKBMODE, K_OFF); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set K_OFF on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - r = ioctl(vt, KDSETMODE, KD_GRAPHICS); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set KD_GRAPHICS on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. - * So we need a dummy handler here which just acknowledges *all* VT - * switch requests. */ - mode.mode = VT_PROCESS; - mode.relsig = SIGRTMIN; - mode.acqsig = SIGRTMIN + 1; - r = ioctl(vt, VT_SETMODE, &mode); - if (r < 0) { - r = log_error_errno(errno, - "Cannot set VT_PROCESS on /dev/tty%u: %m", - s->vtnr); - goto error; - } - - return 0; - -error: - session_restore_vt(s); - return r; -} - -void session_restore_vt(Session *s) { - - static const struct vt_mode mode = { - .mode = VT_AUTO, - }; - - _cleanup_free_ char *utf8 = NULL; - int vt, kb, old_fd; - - /* We need to get a fresh handle to the virtual terminal, - * since the old file-descriptor is potentially in a hung-up - * state after the controlling process exited; we do a - * little dance to avoid having the terminal be available - * for reuse before we've cleaned it up. - */ - old_fd = s->vtfd; - s->vtfd = -1; - - vt = session_open_vt(s); - safe_close(old_fd); - - if (vt < 0) - return; - - (void) ioctl(vt, KDSETMODE, KD_TEXT); - - if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1') - kb = K_UNICODE; - else - kb = K_XLATE; - - (void) ioctl(vt, KDSKBMODE, kb); - - (void) ioctl(vt, VT_SETMODE, &mode); - (void) fchown(vt, 0, (gid_t) -1); - - s->vtfd = safe_close(s->vtfd); -} - -void session_leave_vt(Session *s) { - int r; - - assert(s); - - /* This is called whenever we get a VT-switch signal from the kernel. - * We acknowledge all of them unconditionally. Note that session are - * free to overwrite those handlers and we only register them for - * sessions with controllers. Legacy sessions are not affected. - * However, if we switch from a non-legacy to a legacy session, we must - * make sure to pause all device before acknowledging the switch. We - * process the real switch only after we are notified via sysfs, so the - * legacy session might have already started using the devices. If we - * don't pause the devices before the switch, we might confuse the - * session we switch to. */ - - if (s->vtfd < 0) - return; - - session_device_pause_all(s); - r = ioctl(s->vtfd, VT_RELDISP, 1); - if (r < 0) - log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id); -} - -bool session_is_controller(Session *s, const char *sender) { - assert(s); - - return streq_ptr(s->controller, sender); -} - -static void session_release_controller(Session *s, bool notify) { - _cleanup_free_ char *name = NULL; - SessionDevice *sd; - - if (!s->controller) - return; - - name = s->controller; - - /* By resetting the controller before releasing the devices, we won't - * send notification signals. This avoids sending useless notifications - * if the controller is released on disconnects. */ - if (!notify) - s->controller = NULL; - - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); - - s->controller = NULL; - s->track = sd_bus_track_unref(s->track); -} - -static int on_bus_track(sd_bus_track *track, void *userdata) { - Session *s = userdata; - - assert(track); - assert(s); - - session_drop_controller(s); - - return 0; -} - -int session_set_controller(Session *s, const char *sender, bool force) { - _cleanup_free_ char *name = NULL; - int r; - - assert(s); - assert(sender); - - if (session_is_controller(s, sender)) - return 0; - if (s->controller && !force) - return -EBUSY; - - name = strdup(sender); - if (!name) - return -ENOMEM; - - s->track = sd_bus_track_unref(s->track); - r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s); - if (r < 0) - return r; - - r = sd_bus_track_add_name(s->track, name); - if (r < 0) - return r; - - /* When setting a session controller, we forcibly mute the VT and set - * it into graphics-mode. Applications can override that by changing - * VT state after calling TakeControl(). However, this serves as a good - * default and well-behaving controllers can now ignore VTs entirely. - * Note that we reset the VT on ReleaseControl() and if the controller - * exits. - * If logind crashes/restarts, we restore the controller during restart - * or reset the VT in case it crashed/exited, too. */ - r = session_prepare_vt(s); - if (r < 0) { - s->track = sd_bus_track_unref(s->track); - return r; - } - - session_release_controller(s, true); - s->controller = name; - name = NULL; - session_save(s); - - return 0; -} - -void session_drop_controller(Session *s) { - assert(s); - - if (!s->controller) - return; - - s->track = sd_bus_track_unref(s->track); - session_release_controller(s, false); - session_save(s); - session_restore_vt(s); -} - -static const char* const session_state_table[_SESSION_STATE_MAX] = { - [SESSION_OPENING] = "opening", - [SESSION_ONLINE] = "online", - [SESSION_ACTIVE] = "active", - [SESSION_CLOSING] = "closing" -}; - -DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState); - -static const char* const session_type_table[_SESSION_TYPE_MAX] = { - [SESSION_UNSPECIFIED] = "unspecified", - [SESSION_TTY] = "tty", - [SESSION_X11] = "x11", - [SESSION_WAYLAND] = "wayland", - [SESSION_MIR] = "mir", - [SESSION_WEB] = "web", -}; - -DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); - -static const char* const session_class_table[_SESSION_CLASS_MAX] = { - [SESSION_USER] = "user", - [SESSION_GREETER] = "greeter", - [SESSION_LOCK_SCREEN] = "lock-screen", - [SESSION_BACKGROUND] = "background" -}; - -DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass); - -static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_LEADER] = "leader", - [KILL_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/login/logind-session.h b/src/login/logind-session.h deleted file mode 100644 index ffb7cd2d41..0000000000 --- a/src/login/logind-session.h +++ /dev/null @@ -1,185 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Session Session; -typedef enum KillWho KillWho; - -#include "list.h" -#include "login-util.h" -#include "logind-user.h" - -typedef enum SessionState { - SESSION_OPENING, /* Session scope is being created */ - SESSION_ONLINE, /* Logged in */ - SESSION_ACTIVE, /* Logged in and in the fg */ - SESSION_CLOSING, /* Logged out, but scope is still there */ - _SESSION_STATE_MAX, - _SESSION_STATE_INVALID = -1 -} SessionState; - -typedef enum SessionClass { - SESSION_USER, - SESSION_GREETER, - SESSION_LOCK_SCREEN, - SESSION_BACKGROUND, - _SESSION_CLASS_MAX, - _SESSION_CLASS_INVALID = -1 -} SessionClass; - -typedef enum SessionType { - SESSION_UNSPECIFIED, - SESSION_TTY, - SESSION_X11, - SESSION_WAYLAND, - SESSION_MIR, - SESSION_WEB, - _SESSION_TYPE_MAX, - _SESSION_TYPE_INVALID = -1 -} SessionType; - -#define SESSION_TYPE_IS_GRAPHICAL(type) IN_SET(type, SESSION_X11, SESSION_WAYLAND, SESSION_MIR) - -enum KillWho { - KILL_LEADER, - KILL_ALL, - _KILL_WHO_MAX, - _KILL_WHO_INVALID = -1 -}; - -struct Session { - Manager *manager; - - const char *id; - unsigned int position; - SessionType type; - SessionClass class; - - char *state_file; - - User *user; - - dual_timestamp timestamp; - - char *tty; - char *display; - - bool remote; - char *remote_user; - char *remote_host; - char *service; - char *desktop; - - char *scope; - char *scope_job; - - Seat *seat; - unsigned int vtnr; - int vtfd; - - pid_t leader; - uint32_t audit_id; - - int fifo_fd; - char *fifo_path; - - sd_event_source *fifo_event_source; - - bool idle_hint; - dual_timestamp idle_hint_timestamp; - - bool locked_hint; - - bool in_gc_queue:1; - bool started:1; - bool stopping:1; - - sd_bus_message *create_message; - - sd_event_source *timer_event_source; - - char *controller; - Hashmap *devices; - sd_bus_track *track; - - LIST_FIELDS(Session, sessions_by_user); - LIST_FIELDS(Session, sessions_by_seat); - - LIST_FIELDS(Session, gc_queue); -}; - -Session *session_new(Manager *m, const char *id); -void session_free(Session *s); -void session_set_user(Session *s, User *u); -bool session_check_gc(Session *s, bool drop_not_started); -void session_add_to_gc_queue(Session *s); -int session_activate(Session *s); -bool session_is_active(Session *s); -int session_get_idle_hint(Session *s, dual_timestamp *t); -void session_set_idle_hint(Session *s, bool b); -int session_get_locked_hint(Session *s); -void session_set_locked_hint(Session *s, bool b); -int session_create_fifo(Session *s); -int session_start(Session *s); -int session_stop(Session *s, bool force); -int session_finalize(Session *s); -int session_release(Session *s); -int session_save(Session *s); -int session_load(Session *s); -int session_kill(Session *s, KillWho who, int signo); - -SessionState session_get_state(Session *u); - -extern const sd_bus_vtable session_vtable[]; -int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error); -int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *session_bus_path(Session *s); - -int session_send_signal(Session *s, bool new_session); -int session_send_changed(Session *s, const char *properties, ...) _sentinel_; -int session_send_lock(Session *s, bool lock); -int session_send_lock_all(Manager *m, bool lock); - -int session_send_create_reply(Session *s, sd_bus_error *error); - -const char* session_state_to_string(SessionState t) _const_; -SessionState session_state_from_string(const char *s) _pure_; - -const char* session_type_to_string(SessionType t) _const_; -SessionType session_type_from_string(const char *s) _pure_; - -const char* session_class_to_string(SessionClass t) _const_; -SessionClass session_class_from_string(const char *s) _pure_; - -const char *kill_who_to_string(KillWho k) _const_; -KillWho kill_who_from_string(const char *s) _pure_; - -int session_prepare_vt(Session *s); -void session_restore_vt(Session *s); -void session_leave_vt(Session *s); - -bool session_is_controller(Session *s, const char *sender); -int session_set_controller(Session *s, const char *sender, bool force); -void session_drop_controller(Session *s); - -int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c deleted file mode 100644 index af6392e025..0000000000 --- a/src/login/logind-user-dbus.c +++ /dev/null @@ -1,398 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "bus-util.h" -#include "formats-util.h" -#include "logind-user.h" -#include "logind.h" -#include "signal-util.h" -#include "strv.h" -#include "user-util.h" - -static int property_get_display( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_free_ char *p = NULL; - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - p = u->display ? session_bus_path(u->display) : strdup("/"); - if (!p) - return -ENOMEM; - - return sd_bus_message_append(reply, "(so)", u->display ? u->display->id : "", p); -} - -static int property_get_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "s", user_state_to_string(user_get_state(u))); -} - -static int property_get_sessions( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - Session *session; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = sd_bus_message_open_container(reply, 'a', "(so)"); - if (r < 0) - return r; - - LIST_FOREACH(sessions_by_user, session, u->sessions) { - _cleanup_free_ char *p = NULL; - - p = session_bus_path(session); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(so)", session->id, p); - if (r < 0) - return r; - - } - - return sd_bus_message_close_container(reply); -} - -static int property_get_idle_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - - assert(bus); - assert(reply); - assert(u); - - return sd_bus_message_append(reply, "b", user_get_idle_hint(u, NULL) > 0); -} - -static int property_get_idle_since_hint( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - dual_timestamp t = DUAL_TIMESTAMP_NULL; - uint64_t k; - - assert(bus); - assert(reply); - assert(u); - - user_get_idle_hint(u, &t); - k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic; - - return sd_bus_message_append(reply, "t", k); -} - -static int property_get_linger( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - User *u = userdata; - int r; - - assert(bus); - assert(reply); - assert(u); - - r = user_check_linger_file(u); - - return sd_bus_message_append(reply, "b", r > 0); -} - -int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - User *u = userdata; - int r; - - assert(message); - assert(u); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - u->uid, - &u->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = user_stop(u, true); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - User *u = userdata; - int32_t signo; - int r; - - assert(message); - assert(u); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.login1.manage", - NULL, - false, - u->uid, - &u->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(message, "i", &signo); - if (r < 0) - return r; - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - - r = user_kill(u, signo); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable user_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), - SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0), - - SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - - SD_BUS_VTABLE_END -}; - -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - uid_t uid; - User *user; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/login1/user/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_owner_uid(creds, &uid); - } else { - const char *p; - - p = startswith(path, "/org/freedesktop/login1/user/_"); - if (!p) - return 0; - - r = parse_uid(p, &uid); - } - if (r < 0) - return 0; - - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return 0; - - *found = user; - return 1; -} - -char *user_bus_path(User *u) { - char *s; - - assert(u); - - if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0) - return NULL; - - return s; -} - -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - sd_bus_message *message; - Manager *m = userdata; - User *user; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(user, m->users, i) { - char *p; - - p = user_bus_path(user); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - message = sd_bus_get_current_message(bus); - if (message) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - uid_t uid; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds); - if (r >= 0) { - r = sd_bus_creds_get_owner_uid(creds, &uid); - if (r >= 0) { - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (user) { - r = strv_extend(&l, "/org/freedesktop/login1/user/self"); - if (r < 0) - return r; - } - } - } - } - - *nodes = l; - l = NULL; - - return 1; -} - -int user_send_signal(User *u, bool new_user) { - _cleanup_free_ char *p = NULL; - - assert(u); - - p = user_bus_path(u); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - u->manager->bus, - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - new_user ? "UserNew" : "UserRemoved", - "uo", (uint32_t) u->uid, p); -} - -int user_send_changed(User *u, const char *properties, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(u); - - if (!u->started) - return 0; - - p = user_bus_path(u); - if (!p) - return -ENOMEM; - - l = strv_from_stdarg_alloca(properties); - - return sd_bus_emit_properties_changed_strv(u->manager->bus, p, "org.freedesktop.login1.User", l); -} diff --git a/src/login/logind-user.c b/src/login/logind-user.c deleted file mode 100644 index 348e396292..0000000000 --- a/src/login/logind-user.c +++ /dev/null @@ -1,916 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "clean-ipc.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" - -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { - _cleanup_(user_freep) User *u = NULL; - char lu[DECIMAL_STR_MAX(uid_t) + 1]; - int r; - - assert(out); - assert(m); - assert(name); - - u = new0(User, 1); - if (!u) - return -ENOMEM; - - u->manager = m; - u->uid = uid; - u->gid = gid; - xsprintf(lu, UID_FMT, uid); - - u->name = strdup(name); - if (!u->name) - return -ENOMEM; - - if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) - return -ENOMEM; - - if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) - return -ENOMEM; - - r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); - if (r < 0) - return r; - - r = unit_name_build("user", lu, ".service", &u->service); - if (r < 0) - return r; - - r = hashmap_put(m->users, UID_TO_PTR(uid), u); - if (r < 0) - return r; - - r = hashmap_put(m->user_units, u->slice, u); - if (r < 0) - return r; - - r = hashmap_put(m->user_units, u->service, u); - if (r < 0) - return r; - - *out = u; - u = NULL; - return 0; -} - -User *user_free(User *u) { - if (!u) - return NULL; - - if (u->in_gc_queue) - LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u); - - while (u->sessions) - session_free(u->sessions); - - if (u->service) - hashmap_remove_value(u->manager->user_units, u->service, u); - - if (u->slice) - hashmap_remove_value(u->manager->user_units, u->slice, u); - - hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); - - u->slice_job = mfree(u->slice_job); - u->service_job = mfree(u->service_job); - - u->service = mfree(u->service); - u->slice = mfree(u->slice); - u->runtime_path = mfree(u->runtime_path); - u->state_file = mfree(u->state_file); - u->name = mfree(u->name); - - return mfree(u); -} - -static int user_save_internal(User *u) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(u); - assert(u->state_file); - - r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(u->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "NAME=%s\n" - "STATE=%s\n", - u->name, - user_state_to_string(user_get_state(u))); - - /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ - if (u->runtime_path) - fprintf(f, "RUNTIME=%s\n", u->runtime_path); - - if (u->service_job) - fprintf(f, "SERVICE_JOB=%s\n", u->service_job); - - if (u->slice_job) - fprintf(f, "SLICE_JOB=%s\n", u->slice_job); - - if (u->display) - fprintf(f, "DISPLAY=%s\n", u->display->id); - - if (dual_timestamp_is_set(&u->timestamp)) - fprintf(f, - "REALTIME="USEC_FMT"\n" - "MONOTONIC="USEC_FMT"\n", - u->timestamp.realtime, - u->timestamp.monotonic); - - if (u->sessions) { - Session *i; - bool first; - - fputs("SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nSEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - - fputs("\nACTIVE_SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!session_is_active(i)) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nONLINE_SESSIONS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (session_get_state(i) == SESSION_CLOSING) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->id, f); - } - - fputs("\nACTIVE_SEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (!session_is_active(i) || !i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - - fputs("\nONLINE_SEATS=", f); - first = true; - LIST_FOREACH(sessions_by_user, i, u->sessions) { - if (session_get_state(i) == SESSION_CLOSING || !i->seat) - continue; - - if (first) - first = false; - else - fputc(' ', f); - - fputs(i->seat->id, f); - } - fputc('\n', f); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, u->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(u->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save user data %s: %m", u->state_file); -} - -int user_save(User *u) { - assert(u); - - if (!u->started) - return 0; - - return user_save_internal (u); -} - -int user_load(User *u) { - _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL; - Session *s = NULL; - int r; - - assert(u); - - r = parse_env_file(u->state_file, NEWLINE, - "SERVICE_JOB", &u->service_job, - "SLICE_JOB", &u->slice_job, - "DISPLAY", &display, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to read %s: %m", u->state_file); - } - - if (display) - s = hashmap_get(u->manager->sessions, display); - - if (s && s->display && display_is_local(s->display)) - u->display = s; - - if (realtime) - timestamp_deserialize(realtime, &u->timestamp.realtime); - if (monotonic) - timestamp_deserialize(monotonic, &u->timestamp.monotonic); - - return r; -} - -static int user_mkdir_runtime_path(User *u) { - int r; - - assert(u); - - r = mkdir_safe_label("/run/user", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /run/user: %m"); - - if (path_is_mount_point(u->runtime_path, 0) <= 0) { - _cleanup_free_ char *t = NULL; - - (void) mkdir_label(u->runtime_path, 0700); - - if (mac_smack_use()) - r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - else - r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - if (r < 0) { - r = log_oom(); - goto fail; - } - - r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); - if (r < 0) { - if (errno != EPERM) { - r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); - goto fail; - } - - /* Lacking permissions, maybe - * CAP_SYS_ADMIN-less container? In this case, - * just use a normal directory. */ - - r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); - if (r < 0) { - log_error_errno(r, "Failed to change runtime directory ownership and mode: %m"); - goto fail; - } - } - - r = label_fix(u->runtime_path, false, false); - if (r < 0) - log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path); - } - - return 0; - -fail: - /* Try to clean up, but ignore errors */ - (void) rmdir(u->runtime_path); - return r; -} - -static int user_start_slice(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *description; - char *job; - int r; - - assert(u); - - u->slice_job = mfree(u->slice_job); - description = strjoina("User Slice of ", u->name); - - r = manager_start_slice( - u->manager, - u->slice, - description, - "systemd-logind.service", - "systemd-user-sessions.service", - u->manager->user_tasks_max, - &error, - &job); - if (r >= 0) - u->slice_job = job; - else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) - /* we don't fail due to this, let's try to continue */ - log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", - u->slice, bus_error_message(&error, r), error.name); - - return 0; -} - -static int user_start_service(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - u->service_job = mfree(u->service_job); - - r = manager_start_unit( - u->manager, - u->service, - &error, - &job); - if (r < 0) { - /* we don't fail due to this, let's try to continue */ - log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); - } else { - u->service_job = job; - } - - return 0; -} - -int user_start(User *u) { - int r; - - assert(u); - - if (u->started && !u->stopping) - return 0; - - /* - * If u->stopping is set, the user is marked for removal and the slice - * and service stop-jobs are queued. We have to clear that flag before - * queing the start-jobs again. If they succeed, the user object can be - * re-used just fine (pid1 takes care of job-ordering and proper - * restart), but if they fail, we want to force another user_stop() so - * possibly pending units are stopped. - * Note that we don't clear u->started, as we have no clue what state - * the user is in on failure here. Hence, we pretend the user is - * running so it will be properly taken down by GC. However, we clearly - * return an error from user_start() in that case, so no further - * reference to the user is taken. - */ - u->stopping = false; - - if (!u->started) { - log_debug("New user %s logged in.", u->name); - - /* Make XDG_RUNTIME_DIR */ - r = user_mkdir_runtime_path(u); - if (r < 0) - return r; - } - - /* Create cgroup */ - r = user_start_slice(u); - if (r < 0) - return r; - - /* Save the user data so far, because pam_systemd will read the - * XDG_RUNTIME_DIR out of it while starting up systemd --user. - * We need to do user_save_internal() because we have not - * "officially" started yet. */ - user_save_internal(u); - - /* Spawn user systemd */ - r = user_start_service(u); - if (r < 0) - return r; - - if (!u->started) { - if (!dual_timestamp_is_set(&u->timestamp)) - dual_timestamp_get(&u->timestamp); - user_send_signal(u, true); - u->started = true; - } - - /* Save new user data */ - user_save(u); - - return 0; -} - -static int user_stop_slice(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - r = manager_stop_unit(u->manager, u->slice, &error, &job); - if (r < 0) { - log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); - return r; - } - - free(u->slice_job); - u->slice_job = job; - - return r; -} - -static int user_stop_service(User *u) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job; - int r; - - assert(u); - - r = manager_stop_unit(u->manager, u->service, &error, &job); - if (r < 0) { - log_error("Failed to stop user service: %s", bus_error_message(&error, r)); - return r; - } - - free(u->service_job); - u->service_job = job; - - return r; -} - -static int user_remove_runtime_path(User *u) { - int r; - - assert(u); - - r = rm_rf(u->runtime_path, 0); - if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); - - /* Ignore cases where the directory isn't mounted, as that's - * quite possible, if we lacked the permissions to mount - * something */ - r = umount2(u->runtime_path, MNT_DETACH); - if (r < 0 && errno != EINVAL && errno != ENOENT) - log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); - - r = rm_rf(u->runtime_path, REMOVE_ROOT); - if (r < 0) - log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); - - return r; -} - -int user_stop(User *u, bool force) { - Session *s; - int r = 0, k; - assert(u); - - /* Stop jobs have already been queued */ - if (u->stopping) { - user_save(u); - return r; - } - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - k = session_stop(s, force); - if (k < 0) - r = k; - } - - /* Kill systemd */ - k = user_stop_service(u); - if (k < 0) - r = k; - - /* Kill cgroup */ - k = user_stop_slice(u); - if (k < 0) - r = k; - - u->stopping = true; - - user_save(u); - - return r; -} - -int user_finalize(User *u) { - Session *s; - int r = 0, k; - - assert(u); - - if (u->started) - log_debug("User %s logged out.", u->name); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - k = session_finalize(s); - if (k < 0) - r = k; - } - - /* Kill XDG_RUNTIME_DIR */ - k = user_remove_runtime_path(u); - if (k < 0) - r = k; - - /* Clean SysV + POSIX IPC objects */ - if (u->manager->remove_ipc) { - k = clean_ipc(u->uid); - if (k < 0) - r = k; - } - - unlink(u->state_file); - user_add_to_gc_queue(u); - - if (u->started) { - user_send_signal(u, false); - u->started = false; - } - - return r; -} - -int user_get_idle_hint(User *u, dual_timestamp *t) { - Session *s; - bool idle_hint = true; - dual_timestamp ts = DUAL_TIMESTAMP_NULL; - - assert(u); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - dual_timestamp k; - int ih; - - ih = session_get_idle_hint(s, &k); - if (ih < 0) - return ih; - - if (!ih) { - if (!idle_hint) { - if (k.monotonic < ts.monotonic) - ts = k; - } else { - idle_hint = false; - ts = k; - } - } else if (idle_hint) { - - if (k.monotonic > ts.monotonic) - ts = k; - } - } - - if (t) - *t = ts; - - return idle_hint; -} - -int user_check_linger_file(User *u) { - _cleanup_free_ char *cc = NULL; - char *p = NULL; - - cc = cescape(u->name); - if (!cc) - return -ENOMEM; - - p = strjoina("/var/lib/systemd/linger/", cc); - - return access(p, F_OK) >= 0; -} - -bool user_check_gc(User *u, bool drop_not_started) { - assert(u); - - if (drop_not_started && !u->started) - return false; - - if (u->sessions) - return true; - - if (user_check_linger_file(u) > 0) - return true; - - if (u->slice_job && manager_job_is_active(u->manager, u->slice_job)) - return true; - - if (u->service_job && manager_job_is_active(u->manager, u->service_job)) - return true; - - return false; -} - -void user_add_to_gc_queue(User *u) { - assert(u); - - if (u->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u); - u->in_gc_queue = true; -} - -UserState user_get_state(User *u) { - Session *i; - - assert(u); - - if (u->stopping) - return USER_CLOSING; - - if (!u->started || u->slice_job || u->service_job) - return USER_OPENING; - - if (u->sessions) { - bool all_closing = true; - - LIST_FOREACH(sessions_by_user, i, u->sessions) { - SessionState state; - - state = session_get_state(i); - if (state == SESSION_ACTIVE) - return USER_ACTIVE; - if (state != SESSION_CLOSING) - all_closing = false; - } - - return all_closing ? USER_CLOSING : USER_ONLINE; - } - - if (user_check_linger_file(u) > 0) - return USER_LINGERING; - - return USER_CLOSING; -} - -int user_kill(User *u, int signo) { - assert(u); - - return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL); -} - -static bool elect_display_filter(Session *s) { - /* Return true if the session is a candidate for the user’s ‘primary - * session’ or ‘display’. */ - assert(s); - - return (s->class == SESSION_USER && !s->stopping); -} - -static int elect_display_compare(Session *s1, Session *s2) { - /* Indexed by SessionType. Lower numbers mean more preferred. */ - const int type_ranks[_SESSION_TYPE_MAX] = { - [SESSION_UNSPECIFIED] = 0, - [SESSION_TTY] = -2, - [SESSION_X11] = -3, - [SESSION_WAYLAND] = -3, - [SESSION_MIR] = -3, - [SESSION_WEB] = -1, - }; - - /* Calculate the partial order relationship between s1 and s2, - * returning < 0 if s1 is preferred as the user’s ‘primary session’, - * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2 - * is preferred. - * - * s1 or s2 may be NULL. */ - if (!s1 && !s2) - return 0; - - if ((s1 == NULL) != (s2 == NULL)) - return (s1 == NULL) - (s2 == NULL); - - if (s1->stopping != s2->stopping) - return s1->stopping - s2->stopping; - - if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER)) - return (s1->class != SESSION_USER) - (s2->class != SESSION_USER); - - if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID)) - return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID); - - if (s1->type != s2->type) - return type_ranks[s1->type] - type_ranks[s2->type]; - - return 0; -} - -void user_elect_display(User *u) { - Session *s; - - assert(u); - - /* This elects a primary session for each user, which we call - * the "display". We try to keep the assignment stable, but we - * "upgrade" to better choices. */ - log_debug("Electing new display for user %s", u->name); - - LIST_FOREACH(sessions_by_user, s, u->sessions) { - if (!elect_display_filter(s)) { - log_debug("Ignoring session %s", s->id); - continue; - } - - if (elect_display_compare(s, u->display) < 0) { - log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-"); - u->display = s; - } - } -} - -static const char* const user_state_table[_USER_STATE_MAX] = { - [USER_OFFLINE] = "offline", - [USER_OPENING] = "opening", - [USER_LINGERING] = "lingering", - [USER_ONLINE] = "online", - [USER_ACTIVE] = "active", - [USER_CLOSING] = "closing" -}; - -DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); - -int config_parse_tmpfs_size( - const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t *sz = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* First, try to parse as percentage */ - r = parse_percent(rvalue); - if (r > 0 && r < 100) - *sz = physical_memory_scale(r, 100U); - else { - uint64_t k; - - /* If the passed argument was not a percentage, or out of range, parse as byte size */ - - r = parse_size(rvalue, 1024, &k); - if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); - return 0; - } - - *sz = PAGE_ALIGN((size_t) k); - } - - return 0; -} - -int config_parse_user_tasks_max( - 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 *m = data; - uint64_t k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* First, try to parse as percentage */ - r = parse_percent(rvalue); - if (r > 0 && r < 100) - k = system_tasks_max_scale(r, 100U); - else { - - /* If the passed argument was not a percentage, or out of range, parse as byte size */ - - r = safe_atou64(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue); - return 0; - } - } - - if (k <= 0 || k >= UINT64_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue); - return 0; - } - - *m = k; - return 0; -} diff --git a/src/login/logind-user.h b/src/login/logind-user.h deleted file mode 100644 index 4f0966dc77..0000000000 --- a/src/login/logind-user.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct User User; - -#include "list.h" -#include "logind.h" - -typedef enum UserState { - USER_OFFLINE, /* Not logged in at all */ - USER_OPENING, /* Is logging in */ - USER_LINGERING, /* Lingering has been enabled by the admin for this user */ - USER_ONLINE, /* User logged in */ - USER_ACTIVE, /* User logged in and has a session in the fg */ - USER_CLOSING, /* User logged out, but processes still remain and lingering is not enabled */ - _USER_STATE_MAX, - _USER_STATE_INVALID = -1 -} UserState; - -struct User { - Manager *manager; - uid_t uid; - gid_t gid; - char *name; - char *state_file; - char *runtime_path; - char *slice; - char *service; - - char *service_job; - char *slice_job; - - Session *display; - - dual_timestamp timestamp; - - bool in_gc_queue:1; - bool started:1; - bool stopping:1; - - LIST_HEAD(Session, sessions); - LIST_FIELDS(User, gc_queue); -}; - -int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); -User *user_free(User *u); - -DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); - -bool user_check_gc(User *u, bool drop_not_started); -void user_add_to_gc_queue(User *u); -int user_start(User *u); -int user_stop(User *u, bool force); -int user_finalize(User *u); -UserState user_get_state(User *u); -int user_get_idle_hint(User *u, dual_timestamp *t); -int user_save(User *u); -int user_load(User *u); -int user_kill(User *u, int signo); -int user_check_linger_file(User *u); -void user_elect_display(User *u); - -extern const sd_bus_vtable user_vtable[]; -int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -char *user_bus_path(User *s); - -int user_send_signal(User *u, bool new_user); -int user_send_changed(User *u, const char *properties, ...) _sentinel_; - -const char* user_state_to_string(UserState s) _const_; -UserState user_state_from_string(const char *s) _pure_; - -int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c deleted file mode 100644 index 29ab00eb1f..0000000000 --- a/src/login/logind-utmp.c +++ /dev/null @@ -1,183 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "audit-util.h" -#include "bus-common-errors.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) { - - usec_t left; - unsigned int i; - static const int wall_timers[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 25, 40, 55, 70, 100, 130, 150, 180, - }; - - /* If the time is already passed, then don't announce */ - if (n >= elapse) - return 0; - - left = elapse - n; - - for (i = 1; i < ELEMENTSOF(wall_timers); i++) - if (wall_timers[i] * USEC_PER_MINUTE >= left) - return left - wall_timers[i-1] * USEC_PER_MINUTE; - - return left % USEC_PER_HOUR; -} - -bool logind_wall_tty_filter(const char *tty, void *userdata) { - - Manager *m = userdata; - - assert(m); - - if (!startswith(tty, "/dev/") || !m->scheduled_shutdown_tty) - return true; - - return !streq(tty + 5, m->scheduled_shutdown_tty); -} - -static int warn_wall(Manager *m, usec_t n) { - char date[FORMAT_TIMESTAMP_MAX] = {}; - _cleanup_free_ char *l = NULL; - usec_t left; - int r; - - assert(m); - - if (!m->enable_wall_messages) - return 0; - - left = m->scheduled_shutdown_timeout > n; - - r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", - strempty(m->wall_message), - isempty(m->wall_message) ? "" : "\n", - m->scheduled_shutdown_type, - left ? "at " : "NOW", - left ? format_timestamp(date, sizeof(date), m->scheduled_shutdown_timeout) : ""); - if (r < 0) { - log_oom(); - return 0; - } - - utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), - m->scheduled_shutdown_tty, logind_wall_tty_filter, m); - - return 1; -} - -static int wall_message_timeout_handler( - sd_event_source *s, - uint64_t usec, - void *userdata) { - - Manager *m = userdata; - usec_t n, next; - int r; - - assert(m); - assert(s == m->wall_message_timeout_source); - - n = now(CLOCK_REALTIME); - - r = warn_wall(m, n); - if (r == 0) - return 0; - - next = when_wall(n, m->scheduled_shutdown_timeout); - if (next > 0) { - r = sd_event_source_set_time(s, n + next); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); - - r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } - - return 0; -} - -int manager_setup_wall_message_timer(Manager *m) { - - usec_t n, elapse; - int r; - - assert(m); - - n = now(CLOCK_REALTIME); - elapse = m->scheduled_shutdown_timeout; - - /* wall message handling */ - - if (isempty(m->scheduled_shutdown_type)) { - warn_wall(m, n); - return 0; - } - - if (elapse < n) - return 0; - - /* Warn immediately if less than 15 minutes are left */ - if (elapse - n < 15 * USEC_PER_MINUTE) { - r = warn_wall(m, n); - if (r == 0) - return 0; - } - - elapse = when_wall(n, elapse); - if (elapse == 0) - return 0; - - if (m->wall_message_timeout_source) { - r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); - - r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } else { - r = sd_event_add_time(m->event, &m->wall_message_timeout_source, - CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed. %m"); - } - - return 0; -} diff --git a/src/login/logind.c b/src/login/logind.c deleted file mode 100644 index 5ce36d28c7..0000000000 --- a/src/login/logind.c +++ /dev/null @@ -1,1208 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "libudev.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-error.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 "logind.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "strv.h" -#include "udev-util.h" - -static void manager_free(Manager *m); - -static void manager_reset_config(Manager *m) { - m->n_autovts = 6; - m->reserve_vt = 6; - m->remove_ipc = true; - m->inhibit_delay_max = 5 * USEC_PER_SEC; - m->handle_power_key = HANDLE_POWEROFF; - m->handle_suspend_key = HANDLE_SUSPEND; - m->handle_hibernate_key = HANDLE_HIBERNATE; - m->handle_lid_switch = HANDLE_SUSPEND; - m->handle_lid_switch_docked = HANDLE_IGNORE; - m->power_key_ignore_inhibited = false; - m->suspend_key_ignore_inhibited = false; - m->hibernate_key_ignore_inhibited = false; - m->lid_switch_ignore_inhibited = true; - - m->holdoff_timeout_usec = 30 * USEC_PER_SEC; - - m->idle_action_usec = 30 * USEC_PER_MINUTE; - m->idle_action = HANDLE_IGNORE; - - m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */ - m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */ - m->sessions_max = 8192; - m->inhibitors_max = 8192; - - m->kill_user_processes = KILL_USER_PROCESSES; - - m->kill_only_users = strv_free(m->kill_only_users); - m->kill_exclude_users = strv_free(m->kill_exclude_users); -} - -static Manager *manager_new(void) { - Manager *m; - int r; - - m = new0(Manager, 1); - if (!m) - return NULL; - - m->console_active_fd = -1; - m->reserve_vt_fd = -1; - - m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); - - m->devices = hashmap_new(&string_hash_ops); - m->seats = hashmap_new(&string_hash_ops); - m->sessions = hashmap_new(&string_hash_ops); - m->users = hashmap_new(NULL); - m->inhibitors = hashmap_new(&string_hash_ops); - m->buttons = hashmap_new(&string_hash_ops); - - m->user_units = hashmap_new(&string_hash_ops); - m->session_units = hashmap_new(&string_hash_ops); - - if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) - goto fail; - - m->udev = udev_new(); - if (!m->udev) - goto fail; - - r = sd_event_default(&m->event); - if (r < 0) - goto fail; - - sd_event_set_watchdog(m->event, true); - - manager_reset_config(m); - - return m; - -fail: - manager_free(m); - return NULL; -} - -static void manager_free(Manager *m) { - Session *session; - User *u; - Device *d; - Seat *s; - Inhibitor *i; - Button *b; - - assert(m); - - while ((session = hashmap_first(m->sessions))) - session_free(session); - - while ((u = hashmap_first(m->users))) - user_free(u); - - while ((d = hashmap_first(m->devices))) - device_free(d); - - while ((s = hashmap_first(m->seats))) - seat_free(s); - - while ((i = hashmap_first(m->inhibitors))) - inhibitor_free(i); - - while ((b = hashmap_first(m->buttons))) - button_free(b); - - hashmap_free(m->devices); - hashmap_free(m->seats); - hashmap_free(m->sessions); - hashmap_free(m->users); - hashmap_free(m->inhibitors); - hashmap_free(m->buttons); - - hashmap_free(m->user_units); - hashmap_free(m->session_units); - - sd_event_source_unref(m->idle_action_event_source); - sd_event_source_unref(m->inhibit_timeout_source); - sd_event_source_unref(m->scheduled_shutdown_timeout_source); - sd_event_source_unref(m->nologin_timeout_source); - sd_event_source_unref(m->wall_message_timeout_source); - - sd_event_source_unref(m->console_active_event_source); - sd_event_source_unref(m->udev_seat_event_source); - sd_event_source_unref(m->udev_device_event_source); - sd_event_source_unref(m->udev_vcsa_event_source); - sd_event_source_unref(m->udev_button_event_source); - sd_event_source_unref(m->lid_switch_ignore_event_source); - - safe_close(m->console_active_fd); - - udev_monitor_unref(m->udev_seat_monitor); - udev_monitor_unref(m->udev_device_monitor); - udev_monitor_unref(m->udev_vcsa_monitor); - udev_monitor_unref(m->udev_button_monitor); - - udev_unref(m->udev); - - if (m->unlink_nologin) - (void) unlink("/run/nologin"); - - bus_verify_polkit_async_registry_free(m->polkit_registry); - - sd_bus_unref(m->bus); - sd_event_unref(m->event); - - safe_close(m->reserve_vt_fd); - - strv_free(m->kill_only_users); - strv_free(m->kill_exclude_users); - - free(m->scheduled_shutdown_type); - free(m->scheduled_shutdown_tty); - free(m->wall_message); - free(m->action_job); - free(m); -} - -static int manager_enumerate_devices(Manager *m) { - struct udev_list_entry *item = NULL, *first = NULL; - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - int r; - - assert(m); - - /* Loads devices from udev and creates seats for them as - * necessary */ - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_tag(e, "master-of-seat"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - int k; - - d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - k = manager_process_seat_device(m, d); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_enumerate_buttons(Manager *m) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - int r; - - assert(m); - - /* Loads buttons from udev */ - - if (m->handle_power_key == HANDLE_IGNORE && - m->handle_suspend_key == HANDLE_IGNORE && - m->handle_hibernate_key == HANDLE_IGNORE && - m->handle_lid_switch == HANDLE_IGNORE && - m->handle_lid_switch_docked == HANDLE_IGNORE) - return 0; - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_subsystem(e, "input"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_tag(e, "power-switch"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - int k; - - d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; - - k = manager_process_button_device(m, d); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_enumerate_seats(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - /* This loads data about seats stored on disk, but does not - * actually create any seats. Removes data of seats that no - * longer exist. */ - - d = opendir("/run/systemd/seats"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/seats: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - Seat *s; - int k; - - if (!dirent_is_file(de)) - continue; - - s = hashmap_get(m->seats, de->d_name); - if (!s) { - unlinkat(dirfd(d), de->d_name, 0); - continue; - } - - k = seat_load(s); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_enumerate_linger_users(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - d = opendir("/var/lib/systemd/linger"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /var/lib/systemd/linger/: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - int k; - - if (!dirent_is_file(de)) - continue; - - k = manager_add_user_by_name(m, de->d_name, NULL); - if (k < 0) { - log_notice_errno(k, "Couldn't add lingering user %s: %m", de->d_name); - r = k; - } - } - - return r; -} - -static int manager_enumerate_users(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r, k; - - assert(m); - - /* Add lingering users */ - r = manager_enumerate_linger_users(m); - - /* Read in user data stored on disk */ - d = opendir("/run/systemd/users"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/users: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - User *u; - - if (!dirent_is_file(de)) - continue; - - k = manager_add_user_by_name(m, de->d_name, &u); - if (k < 0) { - log_error_errno(k, "Failed to add user by file name %s: %m", de->d_name); - - r = k; - continue; - } - - user_add_to_gc_queue(u); - - k = user_load(u); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_enumerate_sessions(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - /* Read in session data stored on disk */ - d = opendir("/run/systemd/sessions"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/sessions: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - struct Session *s; - int k; - - if (!dirent_is_file(de)) - continue; - - if (!session_id_valid(de->d_name)) { - log_warning("Invalid session file name '%s', ignoring.", de->d_name); - r = -EINVAL; - continue; - } - - k = manager_add_session(m, de->d_name, &s); - if (k < 0) { - log_error_errno(k, "Failed to add session by file name %s: %m", de->d_name); - - r = k; - continue; - } - - session_add_to_gc_queue(s); - - k = session_load(s); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_enumerate_inhibitors(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - d = opendir("/run/systemd/inhibit"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - int k; - Inhibitor *i; - - if (!dirent_is_file(de)) - continue; - - k = manager_add_inhibitor(m, de->d_name, &i); - if (k < 0) { - log_notice_errno(k, "Couldn't add inhibitor %s: %m", de->d_name); - r = k; - continue; - } - - k = inhibitor_load(i); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_dispatch_seat_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - Manager *m = userdata; - - assert(m); - - d = udev_monitor_receive_device(m->udev_seat_monitor); - if (!d) - return -ENOMEM; - - manager_process_seat_device(m, d); - return 0; -} - -static int manager_dispatch_device_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - Manager *m = userdata; - - assert(m); - - d = udev_monitor_receive_device(m->udev_device_monitor); - if (!d) - return -ENOMEM; - - manager_process_seat_device(m, d); - return 0; -} - -static int manager_dispatch_vcsa_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - Manager *m = userdata; - const char *name; - - assert(m); - - d = udev_monitor_receive_device(m->udev_vcsa_monitor); - if (!d) - return -ENOMEM; - - name = udev_device_get_sysname(d); - - /* Whenever a VCSA device is removed try to reallocate our - * VTs, to make sure our auto VTs never go away. */ - - if (name && startswith(name, "vcsa") && streq_ptr(udev_device_get_action(d), "remove")) - seat_preallocate_vts(m->seat0); - - return 0; -} - -static int manager_dispatch_button_udev(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - Manager *m = userdata; - - assert(m); - - d = udev_monitor_receive_device(m->udev_button_monitor); - if (!d) - return -ENOMEM; - - manager_process_button_device(m, d); - return 0; -} - -static int manager_dispatch_console(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - - assert(m); - assert(m->seat0); - assert(m->console_active_fd == fd); - - seat_read_active_vt(m->seat0); - return 0; -} - -static int manager_reserve_vt(Manager *m) { - _cleanup_free_ char *p = NULL; - - assert(m); - - if (m->reserve_vt <= 0) - return 0; - - if (asprintf(&p, "/dev/tty%u", m->reserve_vt) < 0) - return log_oom(); - - m->reserve_vt_fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (m->reserve_vt_fd < 0) { - - /* Don't complain on VT-less systems */ - if (errno != ENOENT) - log_warning_errno(errno, "Failed to pin reserved VT: %m"); - return -errno; - } - - return 0; -} - -static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(m); - assert(!m->bus); - - r = sd_bus_default_system(&m->bus); - if (r < 0) - return log_error_errno(r, "Failed to connect to system bus: %m"); - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/login1", "org.freedesktop.login1.Manager", manager_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add seat object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/seat", seat_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add seat enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/session", "org.freedesktop.login1.Session", session_vtable, session_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add session object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/session", session_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add session enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/user", "org.freedesktop.login1.User", user_vtable, user_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add user object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/user", user_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add user enumerator: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, m); - if (r < 0) - return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, m); - if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'", - match_properties_changed, m); - if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, m); - if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); - - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - return 0; -} - -static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo *si, void *data) { - Manager *m = data; - Session *active, *iter; - - /* - * We got a VT-switch signal and we have to acknowledge it immediately. - * Preferably, we'd just use m->seat0->active->vtfd, but unfortunately, - * old user-space might run multiple sessions on a single VT, *sigh*. - * Therefore, we have to iterate all sessions and find one with a vtfd - * on the requested VT. - * As only VTs with active controllers have VT_PROCESS set, our current - * notion of the active VT might be wrong (for instance if the switch - * happens while we setup VT_PROCESS). Therefore, read the current VT - * first and then use s->active->vtnr as reference. Note that this is - * not racy, as no further VT-switch can happen as long as we're in - * synchronous VT_PROCESS mode. - */ - - assert(m->seat0); - seat_read_active_vt(m->seat0); - - active = m->seat0->active; - if (!active || active->vtnr < 1) { - log_warning("Received VT_PROCESS signal without a registered session on that VT."); - return 0; - } - - if (active->vtfd >= 0) { - session_leave_vt(active); - } else { - LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) { - if (iter->vtnr == active->vtnr && iter->vtfd >= 0) { - session_leave_vt(iter); - break; - } - } - } - - return 0; -} - -static int manager_connect_console(Manager *m) { - int r; - - assert(m); - assert(m->console_active_fd < 0); - - /* On certain architectures (S390 and Xen, and containers), - /dev/tty0 does not exist, so don't fail if we can't open - it. */ - if (access("/dev/tty0", F_OK) < 0) - return 0; - - m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (m->console_active_fd < 0) { - - /* On some systems the device node /dev/tty0 may exist - * even though /sys/class/tty/tty0 does not. */ - if (errno == ENOENT) - return 0; - - 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); - if (r < 0) { - log_error("Failed to watch foreground console"); - return r; - } - - /* - * SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used - * as VT-acquire signal. We ignore any acquire-events (yes, we still - * have to provide a valid signal-number for it!) and acknowledge all - * release events immediately. - */ - - if (SIGRTMIN + 1 > SIGRTMAX) { - log_error("Not enough real-time signals available: %u-%u", SIGRTMIN, SIGRTMAX); - return -EINVAL; - } - - assert_se(ignore_signals(SIGRTMIN + 1, -1) >= 0); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN, -1) >= 0); - - r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m); - if (r < 0) - return r; - - return 0; -} - -static int manager_connect_udev(Manager *m) { - int r; - - assert(m); - assert(!m->udev_seat_monitor); - assert(!m->udev_device_monitor); - assert(!m->udev_vcsa_monitor); - assert(!m->udev_button_monitor); - - m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_seat_monitor) - return -ENOMEM; - - r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "master-of-seat"); - if (r < 0) - return r; - - r = udev_monitor_enable_receiving(m->udev_seat_monitor); - if (r < 0) - return r; - - r = sd_event_add_io(m->event, &m->udev_seat_event_source, udev_monitor_get_fd(m->udev_seat_monitor), EPOLLIN, manager_dispatch_seat_udev, m); - if (r < 0) - return r; - - m->udev_device_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_device_monitor) - return -ENOMEM; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "input", NULL); - if (r < 0) - return r; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "graphics", NULL); - if (r < 0) - return r; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_device_monitor, "drm", NULL); - if (r < 0) - return r; - - r = udev_monitor_enable_receiving(m->udev_device_monitor); - if (r < 0) - return r; - - r = sd_event_add_io(m->event, &m->udev_device_event_source, udev_monitor_get_fd(m->udev_device_monitor), EPOLLIN, manager_dispatch_device_udev, m); - if (r < 0) - return r; - - /* Don't watch keys if nobody cares */ - if (m->handle_power_key != HANDLE_IGNORE || - m->handle_suspend_key != HANDLE_IGNORE || - m->handle_hibernate_key != HANDLE_IGNORE || - m->handle_lid_switch != HANDLE_IGNORE || - m->handle_lid_switch_docked != HANDLE_IGNORE) { - - m->udev_button_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_button_monitor) - return -ENOMEM; - - r = udev_monitor_filter_add_match_tag(m->udev_button_monitor, "power-switch"); - if (r < 0) - return r; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_button_monitor, "input", NULL); - if (r < 0) - return r; - - r = udev_monitor_enable_receiving(m->udev_button_monitor); - if (r < 0) - return r; - - r = sd_event_add_io(m->event, &m->udev_button_event_source, udev_monitor_get_fd(m->udev_button_monitor), EPOLLIN, manager_dispatch_button_udev, m); - if (r < 0) - return r; - } - - /* Don't bother watching VCSA devices, if nobody cares */ - if (m->n_autovts > 0 && m->console_active_fd >= 0) { - - m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_vcsa_monitor) - return -ENOMEM; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_vcsa_monitor, "vc", NULL); - if (r < 0) - return r; - - r = udev_monitor_enable_receiving(m->udev_vcsa_monitor); - if (r < 0) - return r; - - r = sd_event_add_io(m->event, &m->udev_vcsa_event_source, udev_monitor_get_fd(m->udev_vcsa_monitor), EPOLLIN, manager_dispatch_vcsa_udev, m); - if (r < 0) - return r; - } - - return 0; -} - -static void manager_gc(Manager *m, bool drop_not_started) { - Seat *seat; - Session *session; - User *user; - - assert(m); - - while ((seat = m->seat_gc_queue)) { - LIST_REMOVE(gc_queue, m->seat_gc_queue, seat); - seat->in_gc_queue = false; - - if (!seat_check_gc(seat, drop_not_started)) { - seat_stop(seat, false); - seat_free(seat); - } - } - - while ((session = m->session_gc_queue)) { - LIST_REMOVE(gc_queue, m->session_gc_queue, session); - session->in_gc_queue = false; - - /* First, if we are not closing yet, initiate stopping */ - if (!session_check_gc(session, drop_not_started) && - session_get_state(session) != SESSION_CLOSING) - session_stop(session, false); - - /* Normally, this should make the session referenced - * again, if it doesn't then let's get rid of it - * immediately */ - if (!session_check_gc(session, drop_not_started)) { - session_finalize(session); - session_free(session); - } - } - - while ((user = m->user_gc_queue)) { - LIST_REMOVE(gc_queue, m->user_gc_queue, user); - user->in_gc_queue = false; - - /* First step: queue stop jobs */ - if (!user_check_gc(user, drop_not_started)) - user_stop(user, false); - - /* Second step: finalize user */ - if (!user_check_gc(user, drop_not_started)) { - user_finalize(user); - user_free(user); - } - } -} - -static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *userdata) { - Manager *m = userdata; - struct dual_timestamp since; - usec_t n, elapse; - int r; - - assert(m); - - if (m->idle_action == HANDLE_IGNORE || - m->idle_action_usec <= 0) - return 0; - - n = now(CLOCK_MONOTONIC); - - r = manager_get_idle_hint(m, &since); - if (r <= 0) - /* Not idle. Let's check if after a timeout it might be idle then. */ - elapse = n + m->idle_action_usec; - else { - /* Idle! Let's see if it's time to do something, or if - * we shall sleep for longer. */ - - if (n >= since.monotonic + m->idle_action_usec && - (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) { - log_info("System idle. Taking action."); - - manager_handle_action(m, 0, m->idle_action, false, false); - m->idle_action_not_before_usec = n; - } - - elapse = MAX(since.monotonic, m->idle_action_not_before_usec) + m->idle_action_usec; - } - - if (!m->idle_action_event_source) { - - r = sd_event_add_time( - m->event, - &m->idle_action_event_source, - CLOCK_MONOTONIC, - elapse, USEC_PER_SEC*30, - manager_dispatch_idle_action, m); - if (r < 0) - return log_error_errno(r, "Failed to add idle event source: %m"); - - r = sd_event_source_set_priority(m->idle_action_event_source, SD_EVENT_PRIORITY_IDLE+10); - if (r < 0) - return log_error_errno(r, "Failed to set idle event source priority: %m"); - } else { - r = sd_event_source_set_time(m->idle_action_event_source, elapse); - if (r < 0) - return log_error_errno(r, "Failed to set idle event timer: %m"); - - r = sd_event_source_set_enabled(m->idle_action_event_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "Failed to enable idle event timer: %m"); - } - - return 0; -} - -static int manager_parse_config_file(Manager *m) { - assert(m); - - 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); -} - -static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *m = userdata; - int r; - - manager_reset_config(m); - r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse config file, using defaults: %m"); - else - log_info("Config file reloaded."); - - return 0; -} - -static int manager_startup(Manager *m) { - int r; - Seat *seat; - Session *session; - User *user; - Button *button; - Inhibitor *inhibitor; - Iterator i; - - assert(m); - - assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGHUP, -1) >= 0); - - r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m); - if (r < 0) - return log_error_errno(r, "Failed to register SIGHUP handler: %m"); - - /* Connect to console */ - r = manager_connect_console(m); - if (r < 0) - return r; - - /* Connect to udev */ - r = manager_connect_udev(m); - if (r < 0) - return log_error_errno(r, "Failed to create udev watchers: %m"); - - /* Connect to the bus */ - r = manager_connect_bus(m); - if (r < 0) - return r; - - /* Instantiate magic seat 0 */ - r = manager_add_seat(m, "seat0", &m->seat0); - if (r < 0) - return log_error_errno(r, "Failed to add seat0: %m"); - - r = manager_set_lid_switch_ignore(m, 0 + m->holdoff_timeout_usec); - if (r < 0) - log_warning_errno(r, "Failed to set up lid switch ignore event source: %m"); - - /* Deserialize state */ - r = manager_enumerate_devices(m); - if (r < 0) - log_warning_errno(r, "Device enumeration failed: %m"); - - r = manager_enumerate_seats(m); - if (r < 0) - log_warning_errno(r, "Seat enumeration failed: %m"); - - r = manager_enumerate_users(m); - if (r < 0) - log_warning_errno(r, "User enumeration failed: %m"); - - r = manager_enumerate_sessions(m); - if (r < 0) - log_warning_errno(r, "Session enumeration failed: %m"); - - r = manager_enumerate_inhibitors(m); - if (r < 0) - log_warning_errno(r, "Inhibitor enumeration failed: %m"); - - r = manager_enumerate_buttons(m); - if (r < 0) - log_warning_errno(r, "Button enumeration failed: %m"); - - /* Remove stale objects before we start them */ - manager_gc(m, false); - - /* Reserve the special reserved VT */ - manager_reserve_vt(m); - - /* And start everything */ - HASHMAP_FOREACH(seat, m->seats, i) - seat_start(seat); - - HASHMAP_FOREACH(user, m->users, i) - user_start(user); - - HASHMAP_FOREACH(session, m->sessions, i) - session_start(session); - - HASHMAP_FOREACH(inhibitor, m->inhibitors, i) - inhibitor_start(inhibitor); - - HASHMAP_FOREACH(button, m->buttons, i) - button_check_switches(button); - - manager_dispatch_idle_action(NULL, 0, m); - - return 0; -} - -static int manager_run(Manager *m) { - int r; - - assert(m); - - for (;;) { - r = sd_event_get_state(m->event); - if (r < 0) - return r; - if (r == SD_EVENT_FINISHED) - return 0; - - manager_gc(m, true); - - r = manager_dispatch_delayed(m, false); - if (r < 0) - return r; - if (r > 0) - continue; - - r = sd_event_run(m->event, (uint64_t) -1); - if (r < 0) - return r; - } -} - -int main(int argc, char *argv[]) { - Manager *m = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_set_facility(LOG_AUTH); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "Could not initialize labelling: %m"); - goto finish; - } - - /* Always create the directories people can create inotify - * watches in. Note that some applications might check for the - * existence of /run/systemd/seats/ to determine whether - * logind is available, so please always make sure this check - * stays in. */ - mkdir_label("/run/systemd/seats", 0755); - mkdir_label("/run/systemd/users", 0755); - mkdir_label("/run/systemd/sessions", 0755); - - m = manager_new(); - if (!m) { - r = log_oom(); - goto finish; - } - - manager_parse_config_file(m); - - r = manager_startup(m); - if (r < 0) { - log_error_errno(r, "Failed to fully start up daemon: %m"); - goto finish; - } - - log_debug("systemd-logind running as pid "PID_FMT, getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - r = manager_run(m); - - log_debug("systemd-logind stopped as pid "PID_FMT, getpid()); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - manager_free(m); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in deleted file mode 100644 index 6f720b7708..0000000000 --- a/src/login/logind.conf.in +++ /dev/null @@ -1,37 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See logind.conf(5) for details. - -[Login] -#NAutoVTs=6 -#ReserveVT=6 -#KillUserProcesses=@KILL_USER_PROCESSES@ -#KillOnlyUsers= -#KillExcludeUsers=root -#InhibitDelayMaxSec=5 -#HandlePowerKey=poweroff -#HandleSuspendKey=suspend -#HandleHibernateKey=hibernate -#HandleLidSwitch=suspend -#HandleLidSwitchDocked=ignore -#PowerKeyIgnoreInhibited=no -#SuspendKeyIgnoreInhibited=no -#HibernateKeyIgnoreInhibited=no -#LidSwitchIgnoreInhibited=yes -#HoldoffTimeoutSec=30s -#IdleAction=ignore -#IdleActionSec=30min -#RuntimeDirectorySize=10% -#RemoveIPC=yes -#InhibitorsMax=8192 -#SessionsMax=8192 -#UserTasksMax=33% diff --git a/src/login/logind.h b/src/login/logind.h deleted file mode 100644 index 086fa1eeb5..0000000000 --- a/src/login/logind.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "libudev.h" -#include "sd-bus.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" - -struct Manager { - sd_event *event; - sd_bus *bus; - - Hashmap *devices; - Hashmap *seats; - Hashmap *sessions; - Hashmap *users; - Hashmap *inhibitors; - Hashmap *buttons; - - LIST_HEAD(Seat, seat_gc_queue); - LIST_HEAD(Session, session_gc_queue); - LIST_HEAD(User, user_gc_queue); - - struct udev *udev; - struct udev_monitor *udev_seat_monitor, *udev_device_monitor, *udev_vcsa_monitor, *udev_button_monitor; - - sd_event_source *console_active_event_source; - sd_event_source *udev_seat_event_source; - sd_event_source *udev_device_event_source; - sd_event_source *udev_vcsa_event_source; - sd_event_source *udev_button_event_source; - - int console_active_fd; - - unsigned n_autovts; - - unsigned reserve_vt; - int reserve_vt_fd; - - Seat *seat0; - - char **kill_only_users, **kill_exclude_users; - bool kill_user_processes; - - unsigned long session_counter; - unsigned long inhibit_counter; - - Hashmap *session_units; - Hashmap *user_units; - - usec_t inhibit_delay_max; - - /* If an action is currently being executed or is delayed, - * this is != 0 and encodes what is being done */ - InhibitWhat action_what; - - /* If a shutdown/suspend was delayed due to a inhibitor this - contains the unit name we are supposed to start after the - delay is over */ - const char *action_unit; - - /* If a shutdown/suspend is currently executed, then this is - * the job of it */ - char *action_job; - sd_event_source *inhibit_timeout_source; - - char *scheduled_shutdown_type; - usec_t scheduled_shutdown_timeout; - sd_event_source *scheduled_shutdown_timeout_source; - uid_t scheduled_shutdown_uid; - char *scheduled_shutdown_tty; - sd_event_source *nologin_timeout_source; - bool unlink_nologin; - - char *wall_message; - unsigned enable_wall_messages; - sd_event_source *wall_message_timeout_source; - - bool shutdown_dry_run; - - sd_event_source *idle_action_event_source; - usec_t idle_action_usec; - usec_t idle_action_not_before_usec; - HandleAction idle_action; - - HandleAction handle_power_key; - HandleAction handle_suspend_key; - HandleAction handle_hibernate_key; - HandleAction handle_lid_switch; - HandleAction handle_lid_switch_docked; - - bool power_key_ignore_inhibited; - bool suspend_key_ignore_inhibited; - bool hibernate_key_ignore_inhibited; - bool lid_switch_ignore_inhibited; - - bool remove_ipc; - - Hashmap *polkit_registry; - - usec_t holdoff_timeout_usec; - sd_event_source *lid_switch_ignore_event_source; - - size_t runtime_dir_size; - uint64_t user_tasks_max; - uint64_t sessions_max; - uint64_t inhibitors_max; -}; - -int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); -int manager_add_button(Manager *m, const char *name, Button **_button); -int manager_add_seat(Manager *m, const char *id, Seat **_seat); -int manager_add_session(Manager *m, const char *id, Session **_session); -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); -int manager_add_user_by_name(Manager *m, const char *name, User **_user); -int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); -int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); - -int manager_process_seat_device(Manager *m, struct udev_device *d); -int manager_process_button_device(Manager *m, struct udev_device *d); - -int manager_spawn_autovt(Manager *m, unsigned int vtnr); - -bool manager_shall_kill(Manager *m, const char *user); - -int manager_get_idle_hint(Manager *m, dual_timestamp *t); - -int manager_get_user_by_pid(Manager *m, pid_t pid, User **user); -int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); - -bool manager_is_docked_or_external_displays(Manager *m); - -extern const sd_bus_vtable manager_vtable[]; - -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); - -int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error); - -int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; - -int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); -int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); -int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); -int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error); -int manager_unit_is_active(Manager *manager, const char *unit); -int manager_job_is_active(Manager *manager, const char *path); - -/* gperf lookup function */ -const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); - -int manager_set_lid_switch_ignore(Manager *m, usec_t until); - -int config_parse_tmpfs_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_user_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); - -int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret); -int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret); -int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret); - -int manager_setup_wall_message_timer(Manager *m); -bool logind_wall_tty_filter(const char *tty, void *userdata); - -int manager_dispatch_delayed(Manager *manager, bool timeout); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf deleted file mode 100644 index c89e40457e..0000000000 --- a/src/login/org.freedesktop.login1.conf +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in deleted file mode 100644 index 66cbce393c..0000000000 --- a/src/login/org.freedesktop.login1.policy.in +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Allow applications to inhibit system shutdown - <_message>Authentication is required for an application to inhibit system shutdown. - - no - yes - yes - - org.freedesktop.login1.inhibit-delay-shutdown org.freedesktop.login1.inhibit-block-sleep org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle - - - - <_description>Allow applications to delay system shutdown - <_message>Authentication is required for an application to delay system shutdown. - - yes - yes - yes - - org.freedesktop.login1.inhibit-delay-sleep - - - - <_description>Allow applications to inhibit system sleep - <_message>Authentication is required for an application to inhibit system sleep. - - no - yes - yes - - org.freedesktop.login1.inhibit-delay-sleep org.freedesktop.login1.inhibit-block-idle - - - - <_description>Allow applications to delay system sleep - <_message>Authentication is required for an application to delay system sleep. - - yes - yes - yes - - - - - <_description>Allow applications to inhibit automatic system suspend - <_message>Authentication is required for an application to inhibit automatic system suspend. - - yes - yes - yes - - - - - <_description>Allow applications to inhibit system handling of the power key - <_message>Authentication is required for an application to inhibit system handling of the power key. - - no - yes - yes - - org.freedesktop.login1.inhibit-handle-suspend-key org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch - - - - <_description>Allow applications to inhibit system handling of the suspend key - <_message>Authentication is required for an application to inhibit system handling of the suspend key. - - no - yes - yes - - org.freedesktop.login1.inhibit-handle-hibernate-key org.freedesktop.login1.inhibit-handle-lid-switch - - - - <_description>Allow applications to inhibit system handling of the hibernate key - <_message>Authentication is required for an application to inhibit system handling of the hibernate key. - - no - yes - yes - - - - - <_description>Allow applications to inhibit system handling of the lid switch - <_message>Authentication is required for an application to inhibit system handling of the lid switch. - - no - yes - yes - - - - - <_description>Allow non-logged-in user to run programs - <_message>Explicit request is required to run programs as a non-logged-in user. - - yes - yes - yes - - - - - <_description>Allow non-logged-in users to run programs - <_message>Authentication is required to run programs as a non-logged-in user. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Allow attaching devices to seats - <_message>Authentication is required for attaching a device to a seat. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.flush-devices - - - - <_description>Flush device to seat attachments - <_message>Authentication is required for resetting how devices are attached to seats. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Power off the system - <_message>Authentication is required for powering off the system. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.set-wall-message - - - - <_description>Power off the system while other users are logged in - <_message>Authentication is required for powering off the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.power-off - - - - <_description>Power off the system while an application asked to inhibit it - <_message>Authentication is required for powering off the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.power-off - - - - <_description>Reboot the system - <_message>Authentication is required for rebooting the system. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.set-wall-message - - - - <_description>Reboot the system while other users are logged in - <_message>Authentication is required for rebooting the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.reboot - - - - <_description>Reboot the system while an application asked to inhibit it - <_message>Authentication is required for rebooting the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.reboot - - - - <_description>Suspend the system - <_message>Authentication is required for suspending the system. - - auth_admin_keep - auth_admin_keep - yes - - - - - <_description>Suspend the system while other users are logged in - <_message>Authentication is required for suspending the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.suspend - - - - <_description>Suspend the system while an application asked to inhibit it - <_message>Authentication is required for suspending the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.suspend - - - - <_description>Hibernate the system - <_message>Authentication is required for hibernating the system. - - auth_admin_keep - auth_admin_keep - yes - - - - - <_description>Hibernate the system while other users are logged in - <_message>Authentication is required for hibernating the system while other users are logged in. - - auth_admin_keep - auth_admin_keep - yes - - org.freedesktop.login1.hibernate - - - - <_description>Hibernate the system while an application asked to inhibit it - <_message>Authentication is required for hibernating the system while an application asked to inhibit it. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.login1.hibernate - - - - <_description>Manage active sessions, users and seats - <_message>Authentication is required for managing active sessions, users and seats. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Lock or unlock active sessions - <_message>Authentication is required to lock or unlock active sessions. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Allow indication to the firmware to boot to setup interface - <_message>Authentication is required to indicate to the firmware to boot to setup interface. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set a wall message - <_message>Authentication is required to set a wall message - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/login/org.freedesktop.login1.service b/src/login/org.freedesktop.login1.service deleted file mode 100644 index 762dae2bb3..0000000000 --- a/src/login/org.freedesktop.login1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.login1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.login1.service diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c deleted file mode 100644 index 4f023640f6..0000000000 --- a/src/login/pam_systemd.c +++ /dev/null @@ -1,553 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "audit-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-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, - int argc, const char **argv, - const char **class, - const char **type, - bool *debug) { - - unsigned i; - - assert(argc >= 0); - assert(argc == 0 || argv); - - for (i = 0; i < (unsigned) argc; i++) { - if (startswith(argv[i], "class=")) { - if (class) - *class = argv[i] + 6; - - } else if (startswith(argv[i], "type=")) { - if (type) - *type = argv[i] + 5; - - } else if (streq(argv[i], "debug")) { - if (debug) - *debug = true; - - } else if (startswith(argv[i], "debug=")) { - int k; - - k = parse_boolean(argv[i] + 6); - if (k < 0) - pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring."); - else if (debug) - *debug = k; - - } else - pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]); - } - - return 0; -} - -static int get_user_data( - pam_handle_t *handle, - const char **ret_username, - struct passwd **ret_pw) { - - const char *username = NULL; - struct passwd *pw = NULL; - int r; - - assert(handle); - assert(ret_username); - assert(ret_pw); - - r = pam_get_user(handle, &username, NULL); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to get user name."); - return r; - } - - if (isempty(username)) { - pam_syslog(handle, LOG_ERR, "User name not valid."); - return PAM_AUTH_ERR; - } - - pw = pam_modutil_getpwnam(handle, username); - if (!pw) { - pam_syslog(handle, LOG_ERR, "Failed to get user data."); - return PAM_USER_UNKNOWN; - } - - *ret_pw = pw; - *ret_username = username; - - return PAM_SUCCESS; -} - -static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - }; - _cleanup_free_ char *p = NULL, *tty = NULL; - _cleanup_close_ int fd = -1; - struct ucred ucred; - int v, r; - - assert(display); - assert(vtnr); - - /* We deduce the X11 socket from the display name, then use - * SO_PEERCRED to determine the X11 server process, ask for - * the controlling tty of that and if it's a VC then we know - * the seat and the virtual terminal. Sounds ugly, is only - * semi-ugly. */ - - r = socket_from_display(display, &p); - if (r < 0) - return r; - strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1); - - fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - r = get_ctty(ucred.pid, NULL, &tty); - if (r < 0) - return r; - - v = vtnr_from_tty(tty); - if (v < 0) - return v; - else if (v == 0) - return -ENOENT; - - if (seat) - *seat = "seat0"; - *vtnr = (uint32_t) v; - - return 0; -} - -static int export_legacy_dbus_address( - pam_handle_t *handle, - uid_t uid, - const char *runtime) { - - _cleanup_free_ char *s = NULL; - int r = PAM_BUF_ERR; - - /* FIXME: We *really* should move the access() check into the - * daemons that spawn dbus-daemon, instead of forcing - * DBUS_SESSION_BUS_ADDRESS= here. */ - - s = strjoin(runtime, "/bus", NULL); - if (!s) - goto error; - - if (access(s, F_OK) < 0) - return PAM_SUCCESS; - - s = mfree(s); - if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) - goto error; - - r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); - if (r != PAM_SUCCESS) - goto error; - - return PAM_SUCCESS; - -error: - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return r; -} - -_public_ PAM_EXTERN int pam_sm_open_session( - pam_handle_t *handle, - int flags, - int argc, const char **argv) { - - _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, - *tty = NULL, *display = NULL, - *remote_user = NULL, *remote_host = NULL, - *seat = NULL, - *type = NULL, *class = NULL, - *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int session_fd = -1, existing, r; - bool debug = false, remote; - struct passwd *pw; - uint32_t vtnr = 0; - uid_t original_uid; - - assert(handle); - - /* Make this a NOP on non-logind systems */ - if (!logind_running()) - return PAM_SUCCESS; - - if (parse_argv(handle, - argc, argv, - &class_pam, - &type_pam, - &debug) < 0) - return PAM_SESSION_ERR; - - if (debug) - pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); - - r = get_user_data(handle, &username, &pw); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to get user data."); - return r; - } - - /* Make sure we don't enter a loop by talking to - * systemd-logind when it is actually waiting for the - * background to finish start-up. If the service is - * "systemd-user" we simply set XDG_RUNTIME_DIR and - * leave. */ - - pam_get_item(handle, PAM_SERVICE, (const void**) &service); - if (streq_ptr(service, "systemd-user")) { - _cleanup_free_ char *rt = NULL; - - if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0) - return PAM_BUF_ERR; - - 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; - } - - /* Otherwise, we ask logind to create a session for us */ - - pam_get_item(handle, PAM_XDISPLAY, (const void**) &display); - pam_get_item(handle, PAM_TTY, (const void**) &tty); - pam_get_item(handle, PAM_RUSER, (const void**) &remote_user); - pam_get_item(handle, PAM_RHOST, (const void**) &remote_host); - - seat = pam_getenv(handle, "XDG_SEAT"); - if (isempty(seat)) - seat = getenv("XDG_SEAT"); - - cvtnr = pam_getenv(handle, "XDG_VTNR"); - if (isempty(cvtnr)) - cvtnr = getenv("XDG_VTNR"); - - type = pam_getenv(handle, "XDG_SESSION_TYPE"); - if (isempty(type)) - type = getenv("XDG_SESSION_TYPE"); - if (isempty(type)) - type = type_pam; - - class = pam_getenv(handle, "XDG_SESSION_CLASS"); - if (isempty(class)) - class = getenv("XDG_SESSION_CLASS"); - if (isempty(class)) - class = class_pam; - - desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP"); - if (isempty(desktop)) - desktop = getenv("XDG_SESSION_DESKTOP"); - - tty = strempty(tty); - - if (strchr(tty, ':')) { - /* A tty with a colon is usually an X11 display, - * placed there to show up in utmp. We rearrange - * things and don't pretend that an X display was a - * tty. */ - - if (isempty(display)) - display = tty; - tty = NULL; - } else if (streq(tty, "cron")) { - /* cron has been setting PAM_TTY to "cron" for a very - * long time and it probably shouldn't stop doing that - * for compatibility reasons. */ - type = "unspecified"; - class = "background"; - tty = NULL; - } else if (streq(tty, "ssh")) { - /* ssh has been setting PAM_TTY to "ssh" for a very - * long time and probably shouldn't stop doing that - * for compatibility reasons. */ - type ="tty"; - class = "user"; - tty = NULL; - } - - /* If this fails vtnr will be 0, that's intended */ - if (!isempty(cvtnr)) - (void) safe_atou32(cvtnr, &vtnr); - - if (!isempty(display) && !vtnr) { - if (isempty(seat)) - get_seat_from_display(display, &seat, &vtnr); - else if (streq(seat, "seat0")) - get_seat_from_display(display, NULL, &vtnr); - } - - if (seat && !streq(seat, "seat0") && vtnr != 0) { - pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat); - vtnr = 0; - } - - if (isempty(type)) - type = !isempty(display) ? "x11" : - !isempty(tty) ? "tty" : "unspecified"; - - if (isempty(class)) - class = streq(type, "unspecified") ? "background" : "user"; - - remote = !isempty(remote_host) && !is_localhost(remote_host); - - /* Talk to logind over the message bus */ - - r = sd_bus_open_system(&bus); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); - return PAM_SESSION_ERR; - } - - if (debug) - pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " - "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s", - pw->pw_uid, getpid(), - strempty(service), - type, class, strempty(desktop), - strempty(seat), vtnr, strempty(tty), strempty(display), - yes_no(remote), strempty(remote_user), strempty(remote_host)); - - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CreateSession", - &error, - &reply, - "uusssssussbssa(sv)", - (uint32_t) pw->pw_uid, - (uint32_t) getpid(), - service, - type, - class, - desktop, - seat, - vtnr, - tty, - display, - remote, - remote_user, - remote_host, - 0); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) { - pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r)); - return PAM_SUCCESS; - } else { - pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); - return PAM_SYSTEM_ERR; - } - } - - r = sd_bus_message_read(reply, - "soshusub", - &id, - &object_path, - &runtime_path, - &session_fd, - &original_uid, - &seat, - &vtnr, - &existing); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r)); - return PAM_SESSION_ERR; - } - - if (debug) - pam_syslog(handle, LOG_DEBUG, "Reply from logind: " - "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", - id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); - - r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set session id."); - return r; - } - - if (original_uid == pw->pw_uid) { - /* Don't set $XDG_RUNTIME_DIR if the user we now - * authenticated for does not match the original user - * of the session. We do this in order not to result - * in privileged apps clobbering the runtime directory - * unnecessarily. */ - - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 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, runtime_path); - if (r != PAM_SUCCESS) - return r; - } - - if (!isempty(seat)) { - r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set seat."); - return r; - } - } - - if (vtnr > 0) { - char buf[DECIMAL_STR_MAX(vtnr)]; - sprintf(buf, "%u", vtnr); - - r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number."); - return r; - } - } - - r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to install existing flag."); - return r; - } - - if (session_fd >= 0) { - session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3); - if (session_fd < 0) { - pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m"); - return PAM_SESSION_ERR; - } - - 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); - return r; - } - } - - return PAM_SUCCESS; -} - -_public_ PAM_EXTERN int pam_sm_close_session( - pam_handle_t *handle, - int flags, - int argc, const char **argv) { - - _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; - - assert(handle); - - /* Only release session if it wasn't pre-existing when we - * tried to create it */ - pam_get_data(handle, "systemd.existing", &existing); - - id = pam_getenv(handle, "XDG_SESSION_ID"); - if (id && !existing) { - - /* Before we go and close the FIFO we need to tell - * logind that this is a clean session shutdown, so - * that it doesn't just go and slaughter us - * immediately after closing the fd */ - - r = sd_bus_open_system(&bus); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); - return PAM_SESSION_ERR; - } - - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ReleaseSession", - &error, - NULL, - "s", - id); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r)); - return PAM_SESSION_ERR; - } - } - - /* Note that we are knowingly leaking the FIFO fd here. This - * way, logind can watch us die. If we closed it here it would - * not have any clue when that is completed. Given that one - * cannot really have multiple PAM sessions open from the same - * process this means we will leak one FD at max. */ - - return PAM_SUCCESS; -} diff --git a/src/login/pam_systemd.sym b/src/login/pam_systemd.sym deleted file mode 100644 index 23ff75f688..0000000000 --- a/src/login/pam_systemd.sym +++ /dev/null @@ -1,15 +0,0 @@ -/*** - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. -***/ - -{ -global: - pam_sm_close_session; - pam_sm_open_session; -local: *; -}; diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c deleted file mode 100644 index 29785e2f11..0000000000 --- a/src/login/sysfs-show.c +++ /dev/null @@ -1,189 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "libudev.h" - -#include "alloc-util.h" -#include "locale-util.h" -#include "path-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, - const char *seat, - struct udev_list_entry **item, - const char *sub, - const char *prefix, - unsigned n_columns) { - - assert(udev); - assert(seat); - assert(item); - assert(prefix); - - while (*item) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - struct udev_list_entry *next, *lookahead; - const char *sn, *name, *sysfs, *subsystem, *sysname; - _cleanup_free_ char *k = NULL, *l = NULL; - bool is_master; - - sysfs = udev_list_entry_get_name(*item); - if (!path_startswith(sysfs, sub)) - return 0; - - d = udev_device_new_from_syspath(udev, sysfs); - if (!d) { - *item = udev_list_entry_get_next(*item); - continue; - } - - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) - sn = "seat0"; - - /* Explicitly also check for tag 'seat' here */ - if (!streq(seat, sn) || !udev_device_has_tag(d, "seat")) { - *item = udev_list_entry_get_next(*item); - continue; - } - - is_master = udev_device_has_tag(d, "master-of-seat"); - - name = udev_device_get_sysattr_value(d, "name"); - if (!name) - name = udev_device_get_sysattr_value(d, "id"); - subsystem = udev_device_get_subsystem(d); - sysname = udev_device_get_sysname(d); - - /* Look if there's more coming after this */ - lookahead = next = udev_list_entry_get_next(*item); - while (lookahead) { - const char *lookahead_sysfs; - - lookahead_sysfs = udev_list_entry_get_name(lookahead); - - if (path_startswith(lookahead_sysfs, sub) && - !path_startswith(lookahead_sysfs, sysfs)) { - _cleanup_udev_device_unref_ struct udev_device *lookahead_d = NULL; - - lookahead_d = udev_device_new_from_syspath(udev, lookahead_sysfs); - if (lookahead_d) { - const char *lookahead_sn; - - lookahead_sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(lookahead_sn)) - lookahead_sn = "seat0"; - - if (streq(seat, lookahead_sn) && udev_device_has_tag(lookahead_d, "seat")) - break; - } - } - - lookahead = udev_list_entry_get_next(lookahead); - } - - k = ellipsize(sysfs, n_columns, 20); - if (!k) - return -ENOMEM; - - printf("%s%s%s\n", prefix, special_glyph(lookahead ? TREE_BRANCH : TREE_RIGHT), k); - - if (asprintf(&l, - "%s%s:%s%s%s%s", - is_master ? "[MASTER] " : "", - subsystem, sysname, - name ? " \"" : "", strempty(name), name ? "\"" : "") < 0) - return -ENOMEM; - - free(k); - k = ellipsize(l, n_columns, 70); - if (!k) - return -ENOMEM; - - printf("%s%s%s\n", prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ", k); - - *item = next; - if (*item) { - _cleanup_free_ char *p = NULL; - - p = strappend(prefix, lookahead ? special_glyph(TREE_VERTICAL) : " "); - if (!p) - return -ENOMEM; - - show_sysfs_one(udev, seat, item, sysfs, p, n_columns - 2); - } - } - - return 0; -} - -int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; - struct udev_list_entry *first = NULL; - int r; - - if (n_columns <= 0) - n_columns = columns(); - - if (!prefix) - prefix = ""; - - if (isempty(seat)) - seat = "seat0"; - - udev = udev_new(); - if (!udev) - return -ENOMEM; - - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - if (!streq(seat, "seat0")) - r = udev_enumerate_add_match_tag(e, seat); - else - r = udev_enumerate_add_match_tag(e, "seat"); - if (r < 0) - return r; - - r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - first = udev_enumerate_get_list_entry(e); - if (first) - show_sysfs_one(udev, seat, &first, "/", prefix, n_columns); - else - printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), "(none)"); - - return r; -} diff --git a/src/login/sysfs-show.h b/src/login/sysfs-show.h deleted file mode 100644 index 3e94bc3ed5..0000000000 --- a/src/login/sysfs-show.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int show_sysfs(const char *seat, const char *prefix, unsigned columns); diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4 deleted file mode 100644 index f188a8e548..0000000000 --- a/src/login/systemd-user.m4 +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# Used by systemd --user instances. - -account include system-auth - -m4_ifdef(`HAVE_SELINUX', -session required pam_selinux.so close -session required pam_selinux.so nottys open -)m4_dnl -session required pam_loginuid.so -session include system-auth diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c deleted file mode 100644 index a3cf9d293b..0000000000 --- a/src/login/test-inhibit.c +++ /dev/null @@ -1,112 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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_(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; - - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "Inhibit", - &error, - &reply, - "ssss", what, who, reason, mode); - assert_se(r >= 0); - - r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); - assert_se(r >= 0); - assert_se(fd >= 0); - - return dup(fd); -} - -static void print_inhibitors(sd_bus *bus) { - _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; - int r; - - r = sd_bus_call_method(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "ListInhibitors", - &error, - &reply, - ""); - assert_se(r >= 0); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); - assert_se(r >= 0); - - while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { - printf("what=<%s> who=<%s> why=<%s> mode=<%s> uid=<%"PRIu32"> pid=<%"PRIu32">\n", - what, who, why, mode, uid, pid); - - n++; - } - assert_se(r >= 0); - - printf("%u inhibitors\n", n); -} - -int main(int argc, char*argv[]) { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - int fd1, fd2; - int r; - - r = sd_bus_open_system(&bus); - assert_se(r >= 0); - - print_inhibitors(bus); - - fd1 = inhibit(bus, "sleep"); - assert_se(fd1 >= 0); - print_inhibitors(bus); - - fd2 = inhibit(bus, "idle:shutdown"); - assert_se(fd2 >= 0); - print_inhibitors(bus); - - safe_close(fd1); - sleep(1); - print_inhibitors(bus); - - safe_close(fd2); - sleep(1); - print_inhibitors(bus); - - return 0; -} diff --git a/src/login/test-login-shared.c b/src/login/test-login-shared.c deleted file mode 100644 index 3d233f017c..0000000000 --- a/src/login/test-login-shared.c +++ /dev/null @@ -1,39 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "login-util.h" -#include "macro.h" - -static void test_session_id_valid(void) { - assert_se(session_id_valid("c1")); - assert_se(session_id_valid("1234")); - - assert_se(!session_id_valid("1-2")); - assert_se(!session_id_valid("")); - assert_se(!session_id_valid("\tid")); -} - -int main(int argc, char* argv[]) { - log_parse_environment(); - log_open(); - - test_session_id_valid(); - - return 0; -} diff --git a/src/login/test-login-tables.c b/src/login/test-login-tables.c deleted file mode 100644 index 4fbc893a9a..0000000000 --- a/src/login/test-login-tables.c +++ /dev/null @@ -1,34 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "logind-action.h" -#include "logind-session.h" -#include "test-tables.h" - -int main(int argc, char **argv) { - test_table(handle_action, HANDLE_ACTION); - test_table(inhibit_mode, INHIBIT_MODE); - test_table(kill_who, KILL_WHO); - test_table(session_class, SESSION_CLASS); - test_table(session_state, SESSION_STATE); - test_table(session_type, SESSION_TYPE); - test_table(user_state, USER_STATE); - - return EXIT_SUCCESS; -} diff --git a/src/machine-id-setup/Makefile b/src/machine-id-setup/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/machine-id-setup/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c deleted file mode 100644 index cc9b1b38fe..0000000000 --- a/src/machine-id-setup/machine-id-setup-main.c +++ /dev/null @@ -1,142 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "log.h" -#include "machine-id-setup.h" -#include "path-util.h" -#include "util.h" - -static char *arg_root = NULL; -static bool arg_commit = false; -static bool arg_print = 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" - " --commit Commit transient ID\n" - " --print Print used machine ID\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ROOT, - ARG_COMMIT, - ARG_PRINT, - }; - - 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 }, - { "print", no_argument, NULL, ARG_PRINT }, - {} - }; - - int c, r; - - 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: - return version(); - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case ARG_COMMIT: - arg_commit = true; - break; - - case ARG_PRINT: - arg_print = true; - 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[]) { - char buf[SD_ID128_STRING_MAX]; - sd_id128_t id; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_commit) { - r = machine_id_commit(arg_root); - if (r < 0) - goto finish; - - r = sd_id128_get_machine(&id); - if (r < 0) { - log_error_errno(r, "Failed to read machine ID back: %m"); - goto finish; - } - } else { - r = machine_id_setup(arg_root, SD_ID128_NULL, &id); - if (r < 0) - goto finish; - } - - if (arg_print) - puts(sd_id128_to_string(id, buf)); - -finish: - free(arg_root); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/machine/.gitignore b/src/machine/.gitignore deleted file mode 100644 index e1065b5894..0000000000 --- a/src/machine/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/org.freedesktop.machine1.policy diff --git a/src/machine/Makefile b/src/machine/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/machine/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c deleted file mode 100644 index 867bbc467b..0000000000 --- a/src/machine/image-dbus.c +++ /dev/null @@ -1,422 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "bus-label.h" -#include "bus-util.h" -#include "fd-util.h" -#include "image-dbus.h" -#include "io-util.h" -#include "machine-image.h" -#include "process-util.h" -#include "strv.h" -#include "user-util.h" - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType); - -int bus_image_method_remove( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; - Image *image = userdata; - Manager *m = image->userdata; - pid_t child; - int r; - - assert(message); - assert(image); - - if (m->n_operations >= OPERATIONS_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-images", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - if (child == 0) { - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - r = image_remove(image); - if (r < 0) { - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); - if (r < 0) { - (void) sigkill_wait(child); - return r; - } - - errno_pipe_fd[0] = -1; - - return 1; -} - -int bus_image_method_rename( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - Image *image = userdata; - Manager *m = image->userdata; - const char *new_name; - int r; - - assert(message); - assert(image); - - r = sd_bus_message_read(message, "s", &new_name); - if (r < 0) - return r; - - if (!image_name_is_valid(new_name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-images", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = image_rename(image, new_name); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_image_method_clone( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; - Image *image = userdata; - Manager *m = image->userdata; - const char *new_name; - int r, read_only; - pid_t child; - - assert(message); - assert(image); - assert(m); - - if (m->n_operations >= OPERATIONS_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); - - r = sd_bus_message_read(message, "sb", &new_name, &read_only); - if (r < 0) - return r; - - if (!image_name_is_valid(new_name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-images", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - if (child == 0) { - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - r = image_clone(image, new_name, read_only); - if (r < 0) { - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL); - if (r < 0) { - (void) sigkill_wait(child); - return r; - } - - errno_pipe_fd[0] = -1; - - return 1; -} - -int bus_image_method_mark_read_only( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - Image *image = userdata; - Manager *m = image->userdata; - int r, read_only; - - assert(message); - - r = sd_bus_message_read(message, "b", &read_only); - if (r < 0) - return r; - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-images", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = image_read_only(image, read_only); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_image_method_set_limit( - sd_bus_message *message, - void *userdata, - sd_bus_error *error) { - - Image *image = userdata; - Manager *m = image->userdata; - uint64_t limit; - int r; - - assert(message); - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (!FILE_SIZE_VALID_OR_INFINITY(limit)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-images", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = image_set_limit(image, limit); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable image_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0), - SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Image, type), 0), - SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0), - SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0), - SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0), - SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0), - SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0), - SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), - SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), - SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -static int image_flush_cache(sd_event_source *s, void *userdata) { - Manager *m = userdata; - Image *i; - - assert(s); - assert(m); - - while ((i = hashmap_steal_first(m->image_cache))) - image_unref(i); - - return 0; -} - -int image_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; - Image *image = NULL; - const char *p; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - - p = startswith(path, "/org/freedesktop/machine1/image/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - image = hashmap_get(m->image_cache, e); - if (image) { - *found = image; - return 1; - } - - r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops); - if (r < 0) - return r; - - if (!m->image_cache_defer_event) { - r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m); - if (r < 0) - return r; - - r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE); - if (r < 0) - return r; - } - - r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT); - if (r < 0) - return r; - - r = image_find(e, &image); - if (r <= 0) - return r; - - image->userdata = m; - - r = hashmap_put(m->image_cache, image->name, image); - if (r < 0) { - image_unref(image); - return r; - } - - *found = image; - return 1; -} - -char *image_bus_path(const char *name) { - _cleanup_free_ char *e = NULL; - - assert(name); - - e = bus_label_escape(name); - if (!e) - return NULL; - - return strappend("/org/freedesktop/machine1/image/", e); -} - -int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_(image_hashmap_freep) Hashmap *images = NULL; - _cleanup_strv_free_ char **l = NULL; - Image *image; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - images = hashmap_new(&string_hash_ops); - if (!images) - return -ENOMEM; - - r = image_discover(images); - if (r < 0) - return r; - - HASHMAP_FOREACH(image, images, i) { - char *p; - - p = image_bus_path(image->name); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - *nodes = l; - l = NULL; - - return 1; -} diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h deleted file mode 100644 index b62da996c6..0000000000 --- a/src/machine/image-dbus.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "machined.h" - -extern const sd_bus_vtable image_vtable[]; - -char *image_bus_path(const char *name); - -int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); - -int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_image_method_mark_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_image_method_set_limit(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c deleted file mode 100644 index ba7ac04b56..0000000000 --- a/src/machine/machine-dbus.c +++ /dev/null @@ -1,1472 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -/* 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 -#undef basename - -#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 "machine-dbus.h" -#include "machine.h" -#include "mkdir.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "user-util.h" - -static int property_get_id( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Machine *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - return sd_bus_message_append_array(reply, 'y', &m->id, 16); -} - -static int property_get_state( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Machine *m = userdata; - const char *state; - int r; - - assert(bus); - assert(reply); - assert(m); - - state = machine_state_to_string(machine_get_state(m)); - - r = sd_bus_message_append_basic(reply, 's', state); - if (r < 0) - return r; - - return 1; -} - -static int property_get_netif( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Machine *m = userdata; - - assert(bus); - assert(reply); - assert(m); - - assert_cc(sizeof(int) == sizeof(int32_t)); - - return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int)); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass); - -int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Machine *m = userdata; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = machine_stop(m); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Machine *m = userdata; - const char *swho; - int32_t signo; - KillWho who; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "si", &swho, &signo); - if (r < 0) - return r; - - if (isempty(swho)) - who = KILL_ALL; - else { - who = kill_who_from_string(swho); - if (who < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); - } - - if (!SIGNAL_VALID(signo)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); - - r = bus_verify_polkit_async( - message, - CAP_KILL, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = machine_kill(m, who, signo); - if (r < 0) - return r; - - return sd_bus_reply_method_return(message, NULL); -} - -int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Machine *m = userdata; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; - - switch (m->class) { - - case MACHINE_HOST: { - _cleanup_free_ struct local_address *addresses = NULL; - struct local_address *a; - int n, i; - - n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); - if (n < 0) - return n; - - for (a = addresses, i = 0; i < n; a++, i++) { - - r = sd_bus_message_open_container(reply, 'r', "iay"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "i", addresses[i].family); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - } - - break; - } - - case MACHINE_CONTAINER: { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; - const char *p; - siginfo_t si; - pid_t child; - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); - if (r < 0) - return r; - - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { - _cleanup_free_ struct local_address *addresses = NULL; - struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1, -1); - if (r < 0) - _exit(EXIT_FAILURE); - - n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); - if (n < 0) - _exit(EXIT_FAILURE); - - for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - - r = writev(pair[1], iov, 2); - if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); - - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); - - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; - - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; - - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; - - r = sd_bus_message_open_container(reply, 'r', "iay"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "i", family); - if (r < 0) - return r; - - switch (family) { - - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; - - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; - - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; - - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; - } - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - } - - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - break; - } - - default: - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_strv_free_ char **l = NULL; - Machine *m = userdata; - char **k, **v; - int r; - - assert(message); - assert(m); - - switch (m->class) { - - case MACHINE_HOST: - r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); - if (r < 0) - return r; - - break; - - case MACHINE_CONTAINER: { - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_fclose_ FILE *f = NULL; - siginfo_t si; - pid_t child; - - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { - _cleanup_close_ int fd = -1; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); - - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) - _exit(EXIT_FAILURE); - } - - r = copy_bytes(fd, pair[1], (uint64_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); - - f = fdopen(pair[0], "re"); - if (!f) - return -errno; - - pair[0] = -1; - - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; - - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - - break; - } - - default: - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); - } - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "{ss}"); - if (r < 0) - return r; - - STRV_FOREACH_PAIR(k, v, l) { - r = sd_bus_message_append(reply, "{ss}", *k, *v); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = NULL; - _cleanup_close_ int master = -1; - Machine *m = userdata; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (master < 0) - return master; - - r = ptsname_namespace(master, &pty_name); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "hs", master, pty_name); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) { - int r; - - assert(m); - assert(ret); - - switch (m->class) { - - case MACHINE_HOST: - *ret = NULL; - break; - - case MACHINE_CONTAINER: { - _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; - char *address; - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) - return -ENOMEM; - - bus->address = address; - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; - - r = sd_bus_start(bus); - if (r == -ENOENT) - return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name); - if (r < 0) - return r; - - *ret = bus; - bus = NULL; - break; - } - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = 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; - const char *p, *getty; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (master < 0) - return master; - - r = ptsname_namespace(master, &pty_name); - if (r < 0) - 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); - - r = container_bus_new(m, error, &allocated_bus); - if (r < 0) - return r; - - container_bus = allocated_bus ?: m->manager->bus; - - getty = strjoina("container-getty@", p, ".service"); - - r = sd_bus_call_method( - container_bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - error, NULL, - "ss", getty, "replace"); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "hs", master, pty_name); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL; - _cleanup_free_ char *pty_name = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL; - sd_bus *container_bus = NULL; - _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; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "ss", &user, &path); - if (r < 0) - return r; - user = empty_to_null(user); - if (isempty(path)) - path = "/bin/sh"; - if (!path_is_absolute(path)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); - - r = sd_bus_message_read_strv(message, &args); - if (r < 0) - return r; - if (strv_isempty(args)) { - args = strv_free(args); - - args = strv_new(path, NULL); - if (!args) - return -ENOMEM; - - args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ - } - - r = sd_bus_message_read_strv(message, &env); - if (r < 0) - return r; - if (!strv_env_is_valid(env)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (master < 0) - return master; - - r = ptsname_namespace(master, &pty_name); - if (r < 0) - return r; - - p = path_startswith(pty_name, "/dev/pts/"); - 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); - - r = container_bus_new(m, error, &allocated_bus); - if (r < 0) - return r; - - container_bus = allocated_bus ?: m->manager->bus; - - r = sd_bus_message_new_method_call( - container_bus, - &tm, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - /* Name and mode */ - unit = strjoina("container-shell@", p, ".service"); - r = sd_bus_message_append(tm, "ss", unit, "fail"); - if (r < 0) - return r; - - /* Properties */ - r = sd_bus_message_open_container(tm, 'a', "(sv)"); - if (r < 0) - return r; - - 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)", - "Description", "s", description, - "StandardInputFileDescriptor", "h", slave, - "StandardOutputFileDescriptor", "h", slave, - "StandardErrorFileDescriptor", "h", slave, - "SendSIGHUP", "b", true, - "IgnoreSIGPIPE", "b", false, - "KillMode", "s", "mixed", - "TTYReset", "b", true, - "UtmpIdentifier", "s", utmp_id, - "UtmpMode", "s", "user", - "PAMName", "s", "login", - "WorkingDirectory", "s", "-~"); - if (r < 0) - return r; - - r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); - if (r < 0) - return r; - - if (!strv_isempty(env)) { - r = sd_bus_message_open_container(tm, 'r', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(tm, "s", "Environment"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(tm, 'v', "as"); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(tm, env); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - } - - /* Exec container */ - r = sd_bus_message_open_container(tm, 'r', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(tm, "s", "ExecStart"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(tm, 'a', "(sasb)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(tm, 'r', "sasb"); - if (r < 0) - return r; - - r = sd_bus_message_append(tm, "s", path); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(tm, args); - if (r < 0) - return r; - - r = sd_bus_message_append(tm, "b", true); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - r = sd_bus_message_close_container(tm); - if (r < 0) - return r; - - /* Auxiliary units */ - r = sd_bus_message_append(tm, "a(sa(sv))", 0); - if (r < 0) - return r; - - r = sd_bus_call(container_bus, tm, 0, error, NULL); - if (r < 0) - return r; - - slave = safe_close(slave); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "hs", master, pty_name); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; - char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; - bool mount_slave_created = false, mount_slave_mounted = false, - mount_tmp_created = false, mount_tmp_mounted = false, - mount_outside_created = false, mount_outside_mounted = false; - const char *dest, *src; - Machine *m = userdata; - int read_only, make_directory; - pid_t child; - siginfo_t si; - int r; - - assert(message); - assert(m); - - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); - - r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory); - if (r < 0) - return r; - - if (!path_is_absolute(src) || !path_is_safe(src)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../."); - - if (isempty(dest)) - dest = src; - else if (!path_is_absolute(dest) || !path_is_safe(dest)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../."); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - /* One day, when bind mounting /proc/self/fd/n works across - * namespace boundaries we should rework this logic to make - * use of it... */ - - p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/"); - if (laccess(p, F_OK) < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points."); - - /* Our goal is to install a new bind mount into the container, - possibly read-only. This is irritatingly complex - unfortunately, currently. - - First, we start by creating a private playground in /tmp, - that we can mount MS_SLAVE. (Which is necessary, since - MS_MOVE cannot be applied to mounts with MS_SHARED parent - mounts.) */ - - if (!mkdtemp(mount_slave)) - return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave); - - mount_slave_created = true; - - if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave); - goto finish; - } - - mount_slave_mounted = true; - - if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave); - goto finish; - } - - /* Second, we mount the source directory to a directory inside - of our MS_SLAVE playground. */ - mount_tmp = strjoina(mount_slave, "/mount"); - if (mkdir(mount_tmp, 0700) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp); - goto finish; - } - - mount_tmp_created = true; - - if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to overmount %s: %m", mount_tmp); - goto finish; - } - - mount_tmp_mounted = true; - - /* Third, we remount the new bind mount read-only if requested. */ - if (read_only) - if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp); - goto finish; - } - - /* Fourth, we move the new bind mount into the propagation - * directory. This way it will appear there read-only - * right-away. */ - - mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX"); - if (!mkdtemp(mount_outside)) { - r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside); - goto finish; - } - - mount_outside_created = true; - - if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside); - goto finish; - } - - mount_outside_mounted = true; - mount_tmp_mounted = false; - - (void) rmdir(mount_tmp); - mount_tmp_created = false; - - (void) umount(mount_slave); - mount_slave_mounted = false; - - (void) rmdir(mount_slave); - mount_slave_created = false; - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - goto finish; - } - - child = fork(); - if (child < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - goto finish; - } - - if (child == 0) { - const char *mount_inside; - int mntfd; - const char *q; - - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - q = procfs_file_alloca(m->leader, "ns/mnt"); - mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntfd < 0) { - r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); - goto child_fail; - } - - if (setns(mntfd, CLONE_NEWNS) < 0) { - r = log_error_errno(errno, "Failed to join namespace of leader: %m"); - goto child_fail; - } - - if (make_directory) - (void) mkdir_p(dest, 0755); - - /* Fifth, move the mount to the right place inside */ - mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside)); - if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) { - r = log_error_errno(errno, "Failed to mount: %m"); - goto child_fail; - } - - _exit(EXIT_SUCCESS); - - child_fail: - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - _exit(EXIT_FAILURE); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) { - r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - goto finish; - } - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto finish; - } - if (si.si_status != EXIT_SUCCESS) { - - if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) - r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); - else - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed."); - goto finish; - } - - r = sd_bus_reply_method_return(message, NULL); - -finish: - if (mount_outside_mounted) - umount(mount_outside); - if (mount_outside_created) - rmdir(mount_outside); - - if (mount_tmp_mounted) - umount(mount_tmp); - if (mount_tmp_created) - rmdir(mount_tmp); - - if (mount_slave_mounted) - umount(mount_slave); - if (mount_slave_created) - rmdir(mount_slave); - - return r; -} - -int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; - _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; - _cleanup_close_ int hostfd = -1; - Machine *m = userdata; - bool copy_from; - pid_t child; - char *t; - int r; - - assert(message); - assert(m); - - if (m->manager->n_operations >= OPERATIONS_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); - - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines."); - - r = sd_bus_message_read(message, "ss", &src, &dest); - if (r < 0) - return r; - - if (!path_is_absolute(src)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute."); - - if (isempty(dest)) - dest = src; - else if (!path_is_absolute(dest)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); - - if (copy_from) { - container_path = src; - host_path = dest; - } else { - host_path = src; - container_path = dest; - } - - host_basename = basename(host_path); - t = strdupa(host_path); - host_dirname = dirname(t); - - container_basename = basename(container_path); - t = strdupa(container_path); - container_dirname = dirname(t); - - hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY); - if (hostfd < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname); - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { - int containerfd; - const char *q; - int mntfd; - - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - q = procfs_file_alloca(m->leader, "ns/mnt"); - mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntfd < 0) { - r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); - goto child_fail; - } - - if (setns(mntfd, CLONE_NEWNS) < 0) { - r = log_error_errno(errno, "Failed to join namespace of leader: %m"); - goto child_fail; - } - - containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY); - if (containerfd < 0) { - r = log_error_errno(errno, "Failed top open destination directory: %m"); - goto child_fail; - } - - if (copy_from) - r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true); - else - r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true); - - hostfd = safe_close(hostfd); - containerfd = safe_close(containerfd); - - if (r < 0) { - r = log_error_errno(r, "Failed to copy tree: %m"); - goto child_fail; - } - - _exit(EXIT_SUCCESS); - - child_fail: - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - _exit(EXIT_FAILURE); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - /* Copying might take a while, hence install a watch on the child, and return */ - - r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL); - if (r < 0) { - (void) sigkill_wait(child); - return r; - } - errno_pipe_fd[0] = -1; - - return 1; -} - -int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_close_ int fd = -1; - Machine *m = userdata; - int r; - - assert(message); - assert(m); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - switch (m->class) { - - case MACHINE_HOST: - fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - break; - - case MACHINE_CONTAINER: { - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; - pid_t child; - - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { - _cleanup_close_ int dfd = -1; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); - - dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (dfd < 0) - _exit(EXIT_FAILURE); - - r = send_one_fd(pair[1], dfd, 0); - dfd = safe_close(dfd); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - - fd = receive_one_fd(pair[0], MSG_DONTWAIT); - if (fd < 0) - return fd; - - break; - } - - default: - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); - } - - return sd_bus_reply_method_return(message, "h", fd); -} - -const sd_bus_vtable machine_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), - BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - if (streq(path, "/org/freedesktop/machine1/machine/self")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - sd_bus_message *message; - pid_t pid; - - message = sd_bus_get_current_message(bus); - if (!message) - return 0; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - r = manager_get_machine_by_pid(m, pid, &machine); - if (r <= 0) - return 0; - } else { - _cleanup_free_ char *e = NULL; - const char *p; - - p = startswith(path, "/org/freedesktop/machine1/machine/"); - if (!p) - return 0; - - e = bus_label_unescape(p); - if (!e) - return -ENOMEM; - - machine = hashmap_get(m->machines, e); - if (!machine) - return 0; - } - - *found = machine; - return 1; -} - -char *machine_bus_path(Machine *m) { - _cleanup_free_ char *e = NULL; - - assert(m); - - e = bus_label_escape(m->name); - if (!e) - return NULL; - - return strappend("/org/freedesktop/machine1/machine/", e); -} - -int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - Machine *machine = NULL; - Manager *m = userdata; - Iterator i; - int r; - - assert(bus); - assert(path); - assert(nodes); - - HASHMAP_FOREACH(machine, m->machines, i) { - char *p; - - p = machine_bus_path(machine); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - *nodes = l; - l = NULL; - - return 1; -} - -int machine_send_signal(Machine *m, bool new_machine) { - _cleanup_free_ char *p = NULL; - - assert(m); - - p = machine_bus_path(m); - if (!p) - return -ENOMEM; - - return sd_bus_emit_signal( - m->manager->bus, - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - new_machine ? "MachineNew" : "MachineRemoved", - "so", m->name, p); -} - -int machine_send_create_reply(Machine *m, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; - _cleanup_free_ char *p = NULL; - - assert(m); - - if (!m->create_message) - return 0; - - c = m->create_message; - m->create_message = NULL; - - if (error) - return sd_bus_reply_method_error(c, error); - - /* Update the machine state file before we notify the client - * about the result. */ - machine_save(m); - - p = machine_bus_path(m); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(c, "o", p); -} diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h deleted file mode 100644 index 241b23c7ec..0000000000 --- a/src/machine/machine-dbus.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-bus.h" - -#include "machine.h" - -extern const sd_bus_vtable machine_vtable[]; - -char *machine_bus_path(Machine *s); -int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); - -int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); - -int machine_send_signal(Machine *m, bool new_machine); -int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c deleted file mode 100644 index dd046d6563..0000000000 --- a/src/machine/machine.c +++ /dev/null @@ -1,630 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-messages.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 "util.h" - -Machine* machine_new(Manager *manager, MachineClass class, const char *name) { - Machine *m; - - assert(manager); - assert(class < _MACHINE_CLASS_MAX); - assert(name); - - /* Passing class == _MACHINE_CLASS_INVALID here is fine. It - * means as much as "we don't know yet", and that we'll figure - * it out later when loading the state file. */ - - m = new0(Machine, 1); - if (!m) - return NULL; - - m->name = strdup(name); - if (!m->name) - goto fail; - - if (class != MACHINE_HOST) { - m->state_file = strappend("/run/systemd/machines/", m->name); - if (!m->state_file) - goto fail; - } - - m->class = class; - - if (hashmap_put(manager->machines, m->name, m) < 0) - goto fail; - - m->manager = manager; - - return m; - -fail: - free(m->state_file); - free(m->name); - free(m); - - return NULL; -} - -void machine_free(Machine *m) { - assert(m); - - while (m->operations) - operation_free(m->operations); - - if (m->in_gc_queue) - LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); - - machine_release_unit(m); - - free(m->scope_job); - - (void) hashmap_remove(m->manager->machines, m->name); - - if (m->manager->host_machine == m) - m->manager->host_machine = NULL; - - if (m->leader > 0) - (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); - - sd_bus_message_unref(m->create_message); - - free(m->name); - free(m->state_file); - free(m->service); - free(m->root_directory); - free(m->netif); - free(m); -} - -int machine_save(Machine *m) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(m); - - if (!m->state_file) - return 0; - - if (!m->started) - return 0; - - r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0); - if (r < 0) - goto fail; - - r = fopen_temporary(m->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - (void) fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "NAME=%s\n", - m->name); - - if (m->unit) { - _cleanup_free_ char *escaped; - - escaped = cescape(m->unit); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - - fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */ - } - - if (m->scope_job) - fprintf(f, "SCOPE_JOB=%s\n", m->scope_job); - - if (m->service) { - _cleanup_free_ char *escaped; - - escaped = cescape(m->service); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - fprintf(f, "SERVICE=%s\n", escaped); - } - - if (m->root_directory) { - _cleanup_free_ char *escaped; - - escaped = cescape(m->root_directory); - if (!escaped) { - r = -ENOMEM; - goto fail; - } - fprintf(f, "ROOT=%s\n", escaped); - } - - if (!sd_id128_is_null(m->id)) - fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id)); - - if (m->leader != 0) - fprintf(f, "LEADER="PID_FMT"\n", m->leader); - - if (m->class != _MACHINE_CLASS_INVALID) - fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class)); - - if (dual_timestamp_is_set(&m->timestamp)) - fprintf(f, - "REALTIME="USEC_FMT"\n" - "MONOTONIC="USEC_FMT"\n", - m->timestamp.realtime, - m->timestamp.monotonic); - - if (m->n_netif > 0) { - unsigned i; - - fputs("NETIF=", f); - - for (i = 0; i < m->n_netif; i++) { - if (i != 0) - fputc(' ', f); - - fprintf(f, "%i", m->netif[i]); - } - - fputc('\n', f); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, m->state_file) < 0) { - r = -errno; - goto fail; - } - - if (m->unit) { - char *sl; - - /* Create a symlink from the unit name to the machine - * name, so that we can quickly find the machine for - * each given unit. Ignore error. */ - sl = strjoina("/run/systemd/machines/unit:", m->unit); - (void) symlink(m->name, sl); - } - - return 0; - -fail: - (void) unlink(m->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file); -} - -static void machine_unlink(Machine *m) { - assert(m); - - if (m->unit) { - - char *sl; - - sl = strjoina("/run/systemd/machines/unit:", m->unit); - (void) unlink(sl); - } - - if (m->state_file) - (void) unlink(m->state_file); -} - -int machine_load(Machine *m) { - _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL; - int r; - - assert(m); - - if (!m->state_file) - return 0; - - r = parse_env_file(m->state_file, NEWLINE, - "SCOPE", &m->unit, - "SCOPE_JOB", &m->scope_job, - "SERVICE", &m->service, - "ROOT", &m->root_directory, - "ID", &id, - "LEADER", &leader, - "CLASS", &class, - "REALTIME", &realtime, - "MONOTONIC", &monotonic, - "NETIF", &netif, - NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to read %s: %m", m->state_file); - } - - if (id) - sd_id128_from_string(id, &m->id); - - if (leader) - parse_pid(leader, &m->leader); - - if (class) { - MachineClass c; - - c = machine_class_from_string(class); - if (c >= 0) - m->class = c; - } - - if (realtime) - timestamp_deserialize(realtime, &m->timestamp.realtime); - if (monotonic) - timestamp_deserialize(monotonic, &m->timestamp.monotonic); - - if (netif) { - size_t allocated = 0, nr = 0; - const char *p; - int *ni = NULL; - - p = netif; - for (;;) { - _cleanup_free_ char *word = NULL; - int ifi; - - 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 (parse_ifindex(word, &ifi) < 0) - continue; - - if (!GREEDY_REALLOC(ni, allocated, nr+1)) { - free(ni); - return log_oom(); - } - - ni[nr++] = ifi; - } - - free(m->netif); - m->netif = ni; - m->n_netif = nr; - } - - return r; -} - -static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) { - int r = 0; - - assert(m); - assert(m->class != MACHINE_HOST); - - if (!m->unit) { - _cleanup_free_ char *escaped = NULL; - char *scope, *description, *job = NULL; - - escaped = unit_name_escape(m->name); - if (!escaped) - return log_oom(); - - scope = strjoin("machine-", escaped, ".scope", NULL); - if (!scope) - return log_oom(); - - description = strjoina(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name); - - r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job); - if (r < 0) { - log_error("Failed to start machine scope: %s", bus_error_message(error, r)); - free(scope); - return r; - } else { - m->unit = scope; - - free(m->scope_job); - m->scope_job = job; - } - } - - if (m->unit) - hashmap_put(m->manager->machine_units, m->unit, m); - - return r; -} - -int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { - int r; - - assert(m); - - if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) - return -EOPNOTSUPP; - - if (m->started) - return 0; - - r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); - if (r < 0) - return r; - - /* Create cgroup */ - r = machine_start_scope(m, properties, error); - if (r < 0) - return r; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START), - "NAME=%s", m->name, - "LEADER="PID_FMT, m->leader, - LOG_MESSAGE("New machine %s.", m->name), - NULL); - - if (!dual_timestamp_is_set(&m->timestamp)) - dual_timestamp_get(&m->timestamp); - - m->started = true; - - /* Save new machine data */ - machine_save(m); - - machine_send_signal(m, true); - - return 0; -} - -static int machine_stop_scope(Machine *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *job = NULL; - int r; - - assert(m); - assert(m->class != MACHINE_HOST); - - if (!m->unit) - return 0; - - r = manager_stop_unit(m->manager, m->unit, &error, &job); - if (r < 0) { - log_error("Failed to stop machine scope: %s", bus_error_message(&error, r)); - return r; - } - - free(m->scope_job); - m->scope_job = job; - - return 0; -} - -int machine_stop(Machine *m) { - int r; - assert(m); - - if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) - return -EOPNOTSUPP; - - r = machine_stop_scope(m); - - m->stopping = true; - - machine_save(m); - - return r; -} - -int machine_finalize(Machine *m) { - assert(m); - - if (m->started) - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP), - "NAME=%s", m->name, - "LEADER="PID_FMT, m->leader, - LOG_MESSAGE("Machine %s terminated.", m->name), - NULL); - - machine_unlink(m); - machine_add_to_gc_queue(m); - - if (m->started) { - machine_send_signal(m, false); - m->started = false; - } - - return 0; -} - -bool machine_check_gc(Machine *m, bool drop_not_started) { - assert(m); - - if (m->class == MACHINE_HOST) - return true; - - if (drop_not_started && !m->started) - return false; - - if (m->scope_job && manager_job_is_active(m->manager, m->scope_job)) - return true; - - if (m->unit && manager_unit_is_active(m->manager, m->unit)) - return true; - - return false; -} - -void machine_add_to_gc_queue(Machine *m) { - assert(m); - - if (m->in_gc_queue) - return; - - LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m); - m->in_gc_queue = true; -} - -MachineState machine_get_state(Machine *s) { - assert(s); - - if (s->class == MACHINE_HOST) - return MACHINE_RUNNING; - - if (s->stopping) - return MACHINE_CLOSING; - - if (s->scope_job) - return MACHINE_OPENING; - - return MACHINE_RUNNING; -} - -int machine_kill(Machine *m, KillWho who, int signo) { - assert(m); - - if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) - return -EOPNOTSUPP; - - if (!m->unit) - return -ESRCH; - - if (who == KILL_LEADER) { - /* If we shall simply kill the leader, do so directly */ - - if (kill(m->leader, signo) < 0) - return -errno; - - return 0; - } - - /* Otherwise, make PID 1 do it for us, for the entire cgroup */ - return manager_kill_unit(m->manager, m->unit, signo, NULL); -} - -int machine_openpt(Machine *m, int flags) { - assert(m); - - switch (m->class) { - - case MACHINE_HOST: { - int fd; - - fd = posix_openpt(flags); - if (fd < 0) - return -errno; - - if (unlockpt(fd) < 0) - return -errno; - - return fd; - } - - case MACHINE_CONTAINER: - if (m->leader <= 0) - return -EINVAL; - - return openpt_in_namespace(m->leader, flags); - - default: - return -EOPNOTSUPP; - } -} - -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; - } -} - -void machine_release_unit(Machine *m) { - assert(m); - - if (!m->unit) - return; - - (void) hashmap_remove(m->manager->machine_units, m->unit); - m->unit = mfree(m->unit); -} - -static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { - [MACHINE_CONTAINER] = "container", - [MACHINE_VM] = "vm", - [MACHINE_HOST] = "host", -}; - -DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); - -static const char* const machine_state_table[_MACHINE_STATE_MAX] = { - [MACHINE_OPENING] = "opening", - [MACHINE_RUNNING] = "running", - [MACHINE_CLOSING] = "closing" -}; - -DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState); - -static const char* const kill_who_table[_KILL_WHO_MAX] = { - [KILL_LEADER] = "leader", - [KILL_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/machine/machine.h b/src/machine/machine.h deleted file mode 100644 index e5d75361a9..0000000000 --- a/src/machine/machine.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Machine Machine; -typedef enum KillWho KillWho; - -#include "list.h" -#include "machined.h" -#include "operation.h" - -typedef enum MachineState { - MACHINE_OPENING, /* Machine is being registered */ - MACHINE_RUNNING, /* Machine is running */ - MACHINE_CLOSING, /* Machine is terminating */ - _MACHINE_STATE_MAX, - _MACHINE_STATE_INVALID = -1 -} MachineState; - -typedef enum MachineClass { - MACHINE_CONTAINER, - MACHINE_VM, - MACHINE_HOST, - _MACHINE_CLASS_MAX, - _MACHINE_CLASS_INVALID = -1 -} MachineClass; - -enum KillWho { - KILL_LEADER, - KILL_ALL, - _KILL_WHO_MAX, - _KILL_WHO_INVALID = -1 -}; - -struct Machine { - Manager *manager; - - char *name; - sd_id128_t id; - - MachineClass class; - - char *state_file; - char *service; - char *root_directory; - - char *unit; - char *scope_job; - - pid_t leader; - - dual_timestamp timestamp; - - bool in_gc_queue:1; - bool started:1; - bool stopping:1; - - sd_bus_message *create_message; - - int *netif; - unsigned n_netif; - - LIST_HEAD(Operation, operations); - - LIST_FIELDS(Machine, gc_queue); -}; - -Machine* machine_new(Manager *manager, MachineClass class, const char *name); -void machine_free(Machine *m); -bool machine_check_gc(Machine *m, bool drop_not_started); -void machine_add_to_gc_queue(Machine *m); -int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error); -int machine_stop(Machine *m); -int machine_finalize(Machine *m); -int machine_save(Machine *m); -int machine_load(Machine *m); -int machine_kill(Machine *m, KillWho who, int signo); - -void machine_release_unit(Machine *m); - -MachineState machine_get_state(Machine *u); - -const char* machine_class_to_string(MachineClass t) _const_; -MachineClass machine_class_from_string(const char *s) _pure_; - -const char* machine_state_to_string(MachineState t) _const_; -MachineState machine_state_from_string(const char *s) _pure_; - -const char *kill_who_to_string(KillWho k) _const_; -KillWho kill_who_from_string(const char *s) _pure_; - -int 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 deleted file mode 100644 index ddec6cb4d6..0000000000 --- a/src/machine/machinectl.c +++ /dev/null @@ -1,2875 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-unit-util.h" -#include "bus-util.h" -#include "cgroup-show.h" -#include "cgroup-util.h" -#include "copy.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 "ptyfwd.h" -#include "signal-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; -static bool arg_value = false; -static bool arg_full = false; -static bool arg_no_pager = false; -static bool arg_legend = true; -static const char *arg_kill_who = NULL; -static int arg_signal = SIGTERM; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_read_only = false; -static bool arg_mkdir = false; -static bool arg_quiet = false; -static bool arg_ask_password = true; -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_format = NULL; -static const char *arg_uid = NULL; -static char **arg_setenv = NULL; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -static OutputFlags get_output_flags(void) { - return - arg_all * OUTPUT_SHOW_ALL | - arg_full * OUTPUT_FULL_WIDTH | - (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - colors_enabled() * OUTPUT_COLOR | - !arg_quiet * OUTPUT_WARN_CUTOFF; -} - -typedef struct MachineInfo { - const char *name; - const char *class; - const char *service; -} MachineInfo; - -static int compare_machine_info(const void *a, const void *b) { - const MachineInfo *x = a, *y = b; - - return strcmp(x->name, y->name); -} - -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_(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; - sd_bus *bus = userdata; - int r; - - assert(bus); - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListMachines", - &error, - &reply, - NULL); - if (r < 0) { - log_error("Could not get machines: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "(ssso)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { - size_t l; - - if (name[0] == '.' && !arg_all) - continue; - - if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) - return log_oom(); - - machines[n_machines].name = name; - machines[n_machines].class = class; - machines[n_machines].service = service; - - l = strlen(name); - if (l > max_name) - max_name = l; - - l = strlen(class); - if (l > max_class) - max_class = l; - - l = strlen(service); - if (l > max_service) - max_service = l; - - n_machines++; - } - 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); - - qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info); - - if (arg_legend) - printf("%-*s %-*s %-*s\n", - (int) max_name, "MACHINE", - (int) max_class, "CLASS", - (int) max_service, "SERVICE"); - - for (j = 0; j < n_machines; j++) - printf("%-*s %-*s %-*s\n", - (int) max_name, machines[j].name, - (int) max_class, machines[j].class, - (int) max_service, machines[j].service); - - if (arg_legend) - printf("\n%zu machines listed.\n", n_machines); - - return 0; -} - -typedef struct ImageInfo { - const char *name; - const char *type; - bool read_only; - usec_t crtime; - usec_t mtime; - uint64_t size; -} ImageInfo; - -static int compare_image_info(const void *a, const void *b) { - const ImageInfo *x = a, *y = b; - - return strcmp(x->name, y->name); -} - -static int list_images(int argc, char *argv[], void *userdata) { - - _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_(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; - sd_bus *bus = userdata; - uint64_t crtime, mtime, size; - int read_only, r; - - assert(bus); - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListImages", - &error, - &reply, - ""); - if (r < 0) { - log_error("Could not get images: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) { - char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)]; - size_t l; - - if (name[0] == '.' && !arg_all) - continue; - - if (!GREEDY_REALLOC(images, n_allocated, n_images + 1)) - return log_oom(); - - images[n_images].name = name; - images[n_images].type = type; - images[n_images].read_only = read_only; - images[n_images].crtime = crtime; - images[n_images].mtime = mtime; - images[n_images].size = size; - - l = strlen(name); - if (l > max_name) - max_name = l; - - l = strlen(type); - if (l > max_type) - max_type = l; - - if (crtime != 0) { - l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime))); - if (l > max_crtime) - max_crtime = l; - } - - if (mtime != 0) { - l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime))); - if (l > max_mtime) - max_mtime = l; - } - - if (size != (uint64_t) -1) { - l = strlen(strna(format_bytes(buf, sizeof(buf), size))); - if (l > max_size) - max_size = l; - } - - n_images++; - } - 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); - - qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info); - - if (arg_legend) - printf("%-*s %-*s %-3s %-*s %-*s %-*s\n", - (int) max_name, "NAME", - (int) max_type, "TYPE", - "RO", - (int) max_size, "USAGE", - (int) max_crtime, "CREATED", - (int) max_mtime, "MODIFIED"); - - for (j = 0; j < n_images; j++) { - char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX]; - - printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n", - (int) max_name, images[j].name, - (int) max_type, images[j].type, - images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_normal() : "", - (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)), - (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)), - (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime))); - } - - if (arg_legend) - printf("\n%zu images listed.\n", n_images); - - return 0; -} - -static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { - _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; - unsigned c; - - assert(bus); - assert(unit); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - &reply, - "s"); - if (r < 0) - return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) - return bus_log_parse_error(r); - - if (isempty(cgroup)) - return 0; - - c = columns(); - if (c > 18) - c -= 18; - else - c = 0; - - r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); - if (r == -EBADR) { - - if (arg_transport == BUS_TRANSPORT_REMOTE) - return 0; - - /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ - - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) - return 0; - - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); - } else if (r < 0) - return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); - - return 0; -} - -static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(bus); - assert(name); - assert(prefix); - assert(prefix2); - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; - const void *a; - size_t sz; - char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; - - r = sd_bus_message_read(reply, "i", &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); - - fputs(prefix, stdout); - fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout); - if (family == AF_INET6 && ifi > 0) - printf("%%%i", ifi); - fputc('\n', stdout); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - if (prefix != prefix2) - prefix = prefix2; - } - 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); - - return 0; -} - -static int print_os_release(sd_bus *bus, const char *name, const char *prefix) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *k, *v, *pretty = NULL; - int r; - - assert(bus); - assert(name); - assert(prefix); - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineOSRelease", - NULL, - &reply, - "s", name); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, 'a', "{ss}"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) { - if (streq(k, "PRETTY_NAME")) - pretty = v; - - } - 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 (pretty) - printf("%s%s\n", prefix, pretty); - - return 0; -} - -typedef struct MachineStatusInfo { - char *name; - sd_id128_t id; - char *class; - char *service; - char *unit; - char *root_directory; - pid_t leader; - struct dual_timestamp timestamp; - int *netif; - unsigned n_netif; -} MachineStatusInfo; - -static void machine_status_info_clear(MachineStatusInfo *info) { - if (info) { - free(info->name); - free(info->class); - free(info->service); - free(info->unit); - free(info->root_directory); - free(info->netif); - zero(*info); - } -} - -static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { - char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; - char since2[FORMAT_TIMESTAMP_MAX], *s2; - int ifi = -1; - - assert(bus); - assert(i); - - fputs(strna(i->name), stdout); - - if (!sd_id128_is_null(i->id)) - printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id)); - else - putchar('\n'); - - s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime); - s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime); - - if (s1) - printf("\t Since: %s; %s\n", s2, s1); - else if (s2) - printf("\t Since: %s\n", s2); - - if (i->leader > 0) { - _cleanup_free_ char *t = NULL; - - printf("\t Leader: %u", (unsigned) i->leader); - - get_process_comm(i->leader, &t); - if (t) - printf(" (%s)", t); - - putchar('\n'); - } - - if (i->service) { - printf("\t Service: %s", i->service); - - if (i->class) - printf("; class %s", i->class); - - putchar('\n'); - } else if (i->class) - printf("\t Class: %s\n", i->class); - - if (i->root_directory) - printf("\t Root: %s\n", i->root_directory); - - if (i->n_netif > 0) { - unsigned c; - - fputs("\t Iface:", stdout); - - for (c = 0; c < i->n_netif; c++) { - char name[IF_NAMESIZE+1] = ""; - - if (if_indextoname(i->netif[c], name)) { - fputc(' ', stdout); - fputs(name, stdout); - - if (ifi < 0) - ifi = i->netif[c]; - else - ifi = 0; - } else - printf(" %i", i->netif[c]); - } - - fputc('\n', stdout); - } - - print_addresses(bus, i->name, ifi, - "\t Address: ", - "\t "); - - print_os_release(bus, i->name, "\t OS: "); - - if (i->unit) { - printf("\t Unit: %s\n", i->unit); - show_unit_cgroup(bus, i->unit, i->leader); - - if (arg_transport == BUS_TRANSPORT_LOCAL) - - show_journal_by_unit( - stdout, - i->unit, - arg_output, - 0, - i->timestamp.monotonic, - arg_lines, - 0, - get_output_flags() | OUTPUT_BEGIN_NEWLINE, - SD_JOURNAL_LOCAL_ONLY, - true, - NULL); - } -} - -static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - MachineStatusInfo *i = userdata; - size_t l; - const void *v; - int r; - - assert_cc(sizeof(int32_t) == sizeof(int)); - r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l); - if (r < 0) - return r; - if (r == 0) - return -EBADMSG; - - i->n_netif = l / sizeof(int32_t); - i->netif = memdup(v, l); - if (!i->netif) - return -ENOMEM; - - return 0; -} - -static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) { - - static const struct bus_properties_map map[] = { - { "Name", "s", NULL, offsetof(MachineStatusInfo, name) }, - { "Class", "s", NULL, offsetof(MachineStatusInfo, class) }, - { "Service", "s", NULL, offsetof(MachineStatusInfo, service) }, - { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) }, - { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) }, - { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) }, - { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) }, - { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) }, - { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) }, - { "NetworkInterfaces", "ai", map_netif, 0 }, - {} - }; - - _cleanup_(machine_status_info_clear) MachineStatusInfo info = {}; - int r; - - assert(verb); - assert(bus); - assert(path); - assert(new_line); - - r = bus_map_all_properties(bus, - "org.freedesktop.machine1", - path, - map, - &info); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - if (*new_line) - printf("\n"); - *new_line = true; - - print_machine_status_info(bus, &info); - - return r; -} - -static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) { - int r; - - assert(bus); - assert(path); - assert(new_line); - - if (*new_line) - printf("\n"); - - *new_line = true; - - r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); - if (r < 0) - log_error_errno(r, "Could not get properties: %m"); - - return r; -} - -static int show_machine(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; - bool properties, new_line = false; - sd_bus *bus = userdata; - int r = 0, i; - - assert(bus); - - properties = !strstr(argv[0], "status"); - - pager_open(arg_no_pager, false); - - if (properties && argc <= 1) { - - /* If no argument is specified, inspect the manager - * itself */ - r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line); - if (r < 0) - return r; - } - - for (i = 1; i < argc; i++) { - const char *path = NULL; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachine", - &error, - &reply, - "s", argv[i]); - if (r < 0) { - log_error("Could not get path to machine: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - if (properties) - r = show_machine_properties(bus, path, &new_line); - else - r = show_machine_info(argv[0], bus, path, &new_line); - } - - return r; -} - -typedef struct ImageStatusInfo { - char *name; - char *path; - char *type; - int read_only; - usec_t crtime; - usec_t mtime; - uint64_t usage; - uint64_t limit; - uint64_t usage_exclusive; - uint64_t limit_exclusive; -} ImageStatusInfo; - -static void image_status_info_clear(ImageStatusInfo *info) { - if (info) { - free(info->name); - free(info->path); - free(info->type); - zero(*info); - } -} - -static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) { - char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; - char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2; - char bs[FORMAT_BYTES_MAX], *s3; - char bs_exclusive[FORMAT_BYTES_MAX], *s4; - - assert(bus); - assert(i); - - if (i->name) { - fputs(i->name, stdout); - putchar('\n'); - } - - if (i->type) - printf("\t Type: %s\n", i->type); - - if (i->path) - printf("\t Path: %s\n", i->path); - - printf("\t RO: %s%s%s\n", - i->read_only ? ansi_highlight_red() : "", - i->read_only ? "read-only" : "writable", - i->read_only ? ansi_normal() : ""); - - s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime); - s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime); - if (s1 && s2) - printf("\t Created: %s; %s\n", s2, s1); - else if (s2) - printf("\t Created: %s\n", s2); - - s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime); - s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime); - if (s1 && s2) - printf("\tModified: %s; %s\n", s2, s1); - else if (s2) - printf("\tModified: %s\n", s2); - - s3 = format_bytes(bs, sizeof(bs), i->usage); - s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL; - if (s3 && s4) - printf("\t Usage: %s (exclusive: %s)\n", s3, s4); - else if (s3) - printf("\t Usage: %s\n", s3); - - s3 = format_bytes(bs, sizeof(bs), i->limit); - s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL; - if (s3 && s4) - printf("\t Limit: %s (exclusive: %s)\n", s3, s4); - else if (s3) - printf("\t Limit: %s\n", s3); -} - -static int show_image_info(sd_bus *bus, const char *path, bool *new_line) { - - static const struct bus_properties_map map[] = { - { "Name", "s", NULL, offsetof(ImageStatusInfo, name) }, - { "Path", "s", NULL, offsetof(ImageStatusInfo, path) }, - { "Type", "s", NULL, offsetof(ImageStatusInfo, type) }, - { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) }, - { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) }, - { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) }, - { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) }, - { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) }, - { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) }, - { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) }, - {} - }; - - _cleanup_(image_status_info_clear) ImageStatusInfo info = {}; - int r; - - assert(bus); - assert(path); - assert(new_line); - - r = bus_map_all_properties(bus, - "org.freedesktop.machine1", - path, - map, - &info); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - if (*new_line) - printf("\n"); - *new_line = true; - - print_image_status_info(bus, &info); - - return r; -} - -typedef struct PoolStatusInfo { - char *path; - uint64_t usage; - uint64_t limit; -} PoolStatusInfo; - -static void pool_status_info_clear(PoolStatusInfo *info) { - if (info) { - free(info->path); - zero(*info); - info->usage = -1; - info->limit = -1; - } -} - -static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) { - char bs[FORMAT_BYTES_MAX], *s; - - if (i->path) - printf("\t Path: %s\n", i->path); - - s = format_bytes(bs, sizeof(bs), i->usage); - if (s) - printf("\t Usage: %s\n", s); - - s = format_bytes(bs, sizeof(bs), i->limit); - if (s) - printf("\t Limit: %s\n", s); -} - -static int show_pool_info(sd_bus *bus) { - - static const struct bus_properties_map map[] = { - { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) }, - { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) }, - { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) }, - {} - }; - - _cleanup_(pool_status_info_clear) PoolStatusInfo info = { - .usage = (uint64_t) -1, - .limit = (uint64_t) -1, - }; - int r; - - assert(bus); - - r = bus_map_all_properties(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - map, - &info); - if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); - - print_pool_status_info(bus, &info); - - return 0; -} - - -static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) { - int r; - - assert(bus); - assert(path); - assert(new_line); - - if (*new_line) - printf("\n"); - - *new_line = true; - - r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); - if (r < 0) - log_error_errno(r, "Could not get properties: %m"); - - return r; -} - -static int show_image(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; - bool properties, new_line = false; - sd_bus *bus = userdata; - int r = 0, i; - - assert(bus); - - properties = !strstr(argv[0], "status"); - - pager_open(arg_no_pager, false); - - if (argc <= 1) { - - /* If no argument is specified, inspect the manager - * itself */ - - if (properties) - r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line); - else - r = show_pool_info(bus); - if (r < 0) - return r; - } - - for (i = 1; i < argc; i++) { - const char *path = NULL; - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImage", - &error, - &reply, - "s", argv[i]); - if (r < 0) { - log_error("Could not get path to image: %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); - - if (properties) - r = show_image_properties(bus, path, &new_line); - else - r = show_image_info(bus, path, &new_line); - } - - return r; -} - -static int kill_machine(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - - polkit_agent_open_if_enabled(); - - if (!arg_kill_who) - arg_kill_who = "all"; - - for (i = 1; i < argc; i++) { - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "KillMachine", - &error, - NULL, - "ssi", argv[i], arg_kill_who, arg_signal); - if (r < 0) { - log_error("Could not kill machine: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int reboot_machine(int argc, char *argv[], void *userdata) { - arg_kill_who = "leader"; - arg_signal = SIGINT; /* sysvinit + systemd */ - - return kill_machine(argc, argv, userdata); -} - -static int poweroff_machine(int argc, char *argv[], void *userdata) { - arg_kill_who = "leader"; - arg_signal = SIGRTMIN+4; /* only systemd */ - - return kill_machine(argc, argv, userdata); -} - -static int terminate_machine(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - - polkit_agent_open_if_enabled(); - - for (i = 1; i < argc; i++) { - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "TerminateMachine", - &error, - NULL, - "s", argv[i]); - if (r < 0) { - log_error("Could not terminate machine: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int copy_files(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; - _cleanup_free_ char *abs_host_path = NULL; - char *dest, *host_path, *container_path; - sd_bus *bus = userdata; - bool copy_from; - int r; - - assert(bus); - - polkit_agent_open_if_enabled(); - - copy_from = streq(argv[0], "copy-from"); - dest = argv[3] ?: argv[2]; - host_path = copy_from ? dest : argv[2]; - container_path = copy_from ? argv[2] : dest; - - if (!path_is_absolute(host_path)) { - 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; - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - copy_from ? "CopyFromMachine" : "CopyToMachine"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "sss", - argv[1], - copy_from ? container_path : host_path, - copy_from ? host_path : container_path); - if (r < 0) - return bus_log_create_error(r); - - /* This is a slow operation, hence turn off any method call timeouts */ - r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r; - - assert(bus); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "BindMountMachine", - &error, - NULL, - "sssbb", - argv[1], - argv[2], - argv[3], - arg_read_only, - arg_mkdir); - if (r < 0) { - log_error("Failed to bind mount: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - PTYForward ** forward = (PTYForward**) userdata; - int r; - - assert(m); - assert(forward); - - if (*forward) { - /* If the forwarder is already initialized, tell it to - * exit on the next vhangup(), so that we still flush - * out what might be queued and exit then. */ - - r = pty_forward_set_ignore_vhangup(*forward, false); - if (r >= 0) - return 0; - - log_error_errno(r, "Failed to set ignore_vhangup flag: %m"); - } - - /* On error, or when the forwarder is not initialized yet, quit immediately */ - sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE); - return 0; -} - -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; - - assert(event); - assert(master >= 0); - assert(name); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - - if (streq(name, ".host")) - log_info("Connected to the local host. Press ^] three times within 1s to exit session."); - else - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); - - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - - r = pty_forward_new(event, master, flags, forward); - if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - pty_forward_get_last_char(*forward, &last_char); - - machine_died = - (flags & PTY_FORWARD_IGNORE_VHANGUP) && - pty_forward_get_ignore_vhangup(*forward) == 0; - - *forward = pty_forward_free(*forward); - - if (last_char != '\n') - fputc('\n', stdout); - - if (machine_died) - log_info("Machine %s terminated.", name); - else if (streq(name, ".host")) - log_info("Connection to the local host terminated."); - else - log_info("Connection to machine %s terminated.", name); - - sd_event_get_exit_code(event, &ret); - return ret; -} - -static int login_machine(int argc, char *argv[], void *userdata) { - _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_(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; - - assert(bus); - - if (!strv_isempty(arg_setenv) || arg_uid) { - log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); - return -EINVAL; - } - - if (arg_transport != BUS_TRANSPORT_LOCAL && - arg_transport != BUS_TRANSPORT_MACHINE) { - log_error("Login only supported on local machines."); - return -EOPNOTSUPP; - } - - polkit_agent_open_if_enabled(); - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to get event loop: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; - - match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", machine, "'"); - - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); - if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachineLogin", - &error, - &reply, - "s", machine); - if (r < 0) { - log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "hs", &master, &pty); - if (r < 0) - return bus_log_parse_error(r); - - return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine); -} - -static int shell_machine(int argc, char *argv[], void *userdata) { - _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_(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; - - assert(bus); - - if (arg_transport != BUS_TRANSPORT_LOCAL && - arg_transport != BUS_TRANSPORT_MACHINE) { - log_error("Shell only supported on local machines."); - return -EOPNOTSUPP; - } - - /* Pass $TERM to shell session, if not explicitly specified. */ - if (!strv_find_prefix(arg_setenv, "TERM=")) { - const char *t; - - t = strv_find_prefix(environ, "TERM="); - if (t) { - if (strv_extend(&arg_setenv, t) < 0) - return log_oom(); - } - } - - polkit_agent_open_if_enabled(); - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to get event loop: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; - - if (arg_uid) - uid = arg_uid; - else if (machine) { - const char *at; - - at = strchr(machine, '@'); - if (at) { - uid = strndupa(machine, at - machine); - machine = at + 1; - } - } - - if (isempty(machine)) - machine = ".host"; - - match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", machine, "'"); - - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); - if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachineShell"); - if (r < 0) - return bus_log_create_error(r); - - path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; - - r = sd_bus_message_append(m, "sss", machine, uid, path); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, arg_setenv); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "hs", &master, &pty); - if (r < 0) - return bus_log_parse_error(r); - - return process_forward(event, &forward, master, 0, machine); -} - -static int remove_image(int argc, char *argv[], void *userdata) { - sd_bus *bus = userdata; - int r, i; - - assert(bus); - - polkit_agent_open_if_enabled(); - - for (i = 1; i < argc; i++) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "RemoveImage"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "s", argv[i]); - if (r < 0) - return bus_log_create_error(r); - - /* This is a slow operation, hence turn off any method call timeouts */ - r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); - if (r < 0) - return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r)); - } - - return 0; -} - -static int rename_image(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "RenameImage", - &error, - NULL, - "ss", argv[1], argv[2]); - if (r < 0) { - log_error("Could not rename image: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int clone_image(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 = userdata; - int r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CloneImage"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only); - if (r < 0) - return bus_log_create_error(r); - - /* This is a slow operation, hence turn off any method call timeouts */ - r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); - if (r < 0) - return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r)); - - return 0; -} - -static int read_only_image(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int b = true, r; - - if (argc > 2) { - b = parse_boolean(argv[2]); - if (b < 0) { - log_error("Failed to parse boolean argument: %s", argv[2]); - return -EINVAL; - } - } - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MarkImageReadOnly", - &error, - NULL, - "sb", argv[1], b); - if (r < 0) { - log_error("Could not mark image read-only: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int image_exists(sd_bus *bus, const char *name) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(name); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetImage", - &error, - NULL, - "s", name); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE)) - return 0; - - return log_error_errno(r, "Failed to check whether image %s exists: %s", name, bus_error_message(&error, -r)); - } - - return 1; -} - -static int make_service_name(const char *name, char **ret) { - int r; - - assert(name); - assert(ret); - - if (!machine_name_is_valid(name)) { - log_error("Invalid machine name %s.", name); - return -EINVAL; - } - - r = unit_name_build("systemd-nspawn", name, ".service", ret); - if (r < 0) - return log_error_errno(r, "Failed to build unit name: %m"); - - return 0; -} - -static int start_machine(int argc, char *argv[], void *userdata) { - _cleanup_(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; - - assert(bus); - - polkit_agent_open_if_enabled(); - - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_oom(); - - for (i = 1; i < argc; i++) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *unit = NULL; - const char *object; - - r = make_service_name(argv[i], &unit); - if (r < 0) - return r; - - r = image_exists(bus, argv[i]); - if (r < 0) - return r; - if (r == 0) { - log_error("Machine image '%s' does not exist.", argv[1]); - return -ENXIO; - } - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit", - &error, - &reply, - "ss", unit, "fail"); - if (r < 0) { - log_error("Failed to start unit: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_wait_for_jobs_add(w, object); - if (r < 0) - return log_oom(); - } - - r = bus_wait_for_jobs(w, arg_quiet, NULL); - if (r < 0) - return r; - - return 0; -} - -static int enable_machine(int argc, char *argv[], void *userdata) { - _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; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int carries_install_info = 0; - const char *method = NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - - polkit_agent_open_if_enabled(); - - method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles"; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (i = 1; i < argc; i++) { - _cleanup_free_ char *unit = NULL; - - r = make_service_name(argv[i], &unit); - if (r < 0) - return r; - - r = image_exists(bus, argv[i]); - if (r < 0) - return r; - if (r == 0) { - log_error("Machine image '%s' does not exist.", argv[1]); - return -ENXIO; - } - - r = sd_bus_message_append(m, "s", unit); - 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); - - if (streq(argv[0], "enable")) - r = sd_bus_message_append(m, "bb", false, false); - else - r = sd_bus_message_append(m, "b", false); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r)); - return r; - } - - if (streq(argv[0], "enable")) { - r = sd_bus_message_read(reply, "b", carries_install_info); - if (r < 0) - return bus_log_parse_error(r); - } - - r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); - if (r < 0) - goto finish; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reload", - &error, - NULL, - NULL); - if (r < 0) { - log_error("Failed to reload daemon: %s", bus_error_message(&error, -r)); - goto finish; - } - - r = 0; - -finish: - unit_file_changes_free(changes, n_changes); - - return r; -} - -static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char **our_path = userdata, *line; - unsigned priority; - int r; - - assert(m); - assert(our_path); - - r = sd_bus_message_read(m, "us", &priority, &line); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (!streq_ptr(*our_path, sd_bus_message_get_path(m))) - return 0; - - if (arg_quiet && LOG_PRI(priority) >= LOG_INFO) - return 0; - - log_full(priority, "%s", line); - return 0; -} - -static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char **our_path = userdata, *path, *result; - uint32_t id; - int r; - - assert(m); - assert(our_path); - - r = sd_bus_message_read(m, "uos", &id, &path, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - if (!streq_ptr(*our_path, path)) - return 0; - - sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done")); - return 0; -} - -static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - assert(s); - assert(si); - - if (!arg_quiet) - log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata)); - - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { - _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; - - assert(bus); - assert(m); - - polkit_agent_open_if_enabled(); - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to get event loop: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - r = sd_bus_add_match( - bus, - &slot_job_removed, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Manager'," - "member='TransferRemoved'," - "path='/org/freedesktop/import1'", - match_transfer_removed, &path); - if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); - - r = sd_bus_add_match( - bus, - &slot_log_message, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Transfer'," - "member='LogMessage'", - match_log_message, &path); - if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to transfer image: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "uo", &id, &path); - if (r < 0) - return bus_log_parse_error(r); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - - if (!arg_quiet) - log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id); - - sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id)); - sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id)); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - return -r; -} - -static int import_tar(int argc, char *argv[], void *userdata) { - _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; - sd_bus *bus = userdata; - int r; - - assert(bus); - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (!local) { - log_error("Need either path or local name."); - return -EINVAL; - } - - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local name %s is not a suitable machine name.", local); - return -EINVAL; - } - - if (path) { - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ImportTar"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "hsbb", - fd >= 0 ? fd : STDIN_FILENO, - local, - arg_force, - arg_read_only); - if (r < 0) - return bus_log_create_error(r); - - return transfer_image_common(bus, m); -} - -static int import_raw(int argc, char *argv[], void *userdata) { - _cleanup_(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; - sd_bus *bus = userdata; - int r; - - assert(bus); - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (!local) { - log_error("Need either path or local name."); - return -EINVAL; - } - - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local name %s is not a suitable machine name.", local); - return -EINVAL; - } - - if (path) { - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ImportRaw"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "hsbb", - fd >= 0 ? fd : STDIN_FILENO, - local, - arg_force, - arg_read_only); - if (r < 0) - return bus_log_create_error(r); - - return transfer_image_common(bus, m); -} - -static void determine_compression_from_filename(const char *p) { - if (arg_format) - return; - - if (!p) - return; - - if (endswith(p, ".xz")) - arg_format = "xz"; - else if (endswith(p, ".gz")) - arg_format = "gzip"; - else if (endswith(p, ".bz2")) - arg_format = "bzip2"; -} - -static int export_tar(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_close_ int fd = -1; - const char *local = NULL, *path = NULL; - sd_bus *bus = userdata; - int r; - - assert(bus); - - local = argv[1]; - if (!machine_name_is_valid(local)) { - log_error("Machine name %s is not valid.", local); - return -EINVAL; - } - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (path) { - determine_compression_from_filename(path); - - fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ExportTar"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "shs", - local, - fd >= 0 ? fd : STDOUT_FILENO, - arg_format); - if (r < 0) - return bus_log_create_error(r); - - return transfer_image_common(bus, m); -} - -static int export_raw(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_close_ int fd = -1; - const char *local = NULL, *path = NULL; - sd_bus *bus = userdata; - int r; - - assert(bus); - - local = argv[1]; - if (!machine_name_is_valid(local)) { - log_error("Machine name %s is not valid.", local); - return -EINVAL; - } - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (path) { - determine_compression_from_filename(path); - - fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ExportRaw"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "shs", - local, - fd >= 0 ? fd : STDOUT_FILENO, - arg_format); - if (r < 0) - return bus_log_create_error(r); - - return transfer_image_common(bus, m); -} - -static int pull_tar(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_free_ char *l = NULL, *ll = NULL; - const char *local, *remote; - sd_bus *bus = userdata; - int r; - - assert(bus); - - remote = argv[1]; - if (!http_url_is_valid(remote)) { - log_error("URL '%s' is not valid.", remote); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(remote, &l); - if (r < 0) - return log_error_errno(r, "Failed to get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local 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", - "PullTar"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "sssb", - remote, - local, - import_verify_to_string(arg_verify), - arg_force); - if (r < 0) - return bus_log_create_error(r); - - return transfer_image_common(bus, m); -} - -static int pull_raw(int argc, char *argv[], void *userdata) { - _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; - int r; - - assert(bus); - - remote = argv[1]; - if (!http_url_is_valid(remote)) { - log_error("URL '%s' is not valid.", remote); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(remote, &l); - if (r < 0) - return log_error_errno(r, "Failed to get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local 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", - "PullRaw"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "sssb", - remote, - 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; - const char *remote; - const char *local; - double progress; -} TransferInfo; - -static int compare_transfer_info(const void *a, const void *b) { - const TransferInfo *x = a, *y = b; - - return strcmp(x->local, y->local); -} - -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_(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; - sd_bus *bus = userdata; - uint32_t id, max_id = 0; - double progress; - int r; - - pager_open(arg_no_pager, false); - - r = sd_bus_call_method(bus, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ListTransfers", - &error, - &reply, - NULL); - if (r < 0) { - log_error("Could not get transfers: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_enter_container(reply, 'a', "(usssdo)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) { - size_t l; - - if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1)) - return log_oom(); - - transfers[n_transfers].id = id; - transfers[n_transfers].type = type; - transfers[n_transfers].remote = remote; - transfers[n_transfers].local = local; - transfers[n_transfers].progress = progress; - - l = strlen(type); - if (l > max_type) - max_type = l; - - l = strlen(remote); - if (l > max_remote) - max_remote = l; - - l = strlen(local); - if (l > max_local) - max_local = l; - - if (id > max_id) - max_id = id; - - n_transfers++; - } - 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); - - qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info); - - if (arg_legend) - printf("%-*s %-*s %-*s %-*s %-*s\n", - (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID", - (int) 7, "PERCENT", - (int) max_type, "TYPE", - (int) max_local, "LOCAL", - (int) max_remote, "REMOTE"); - - for (j = 0; j < n_transfers; j++) - printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n", - (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id, - (int) 6, (unsigned) (transfers[j].progress * 100), - (int) max_type, transfers[j].type, - (int) max_local, transfers[j].local, - (int) max_remote, transfers[j].remote); - - if (arg_legend) - printf("\n%zu transfers listed.\n", n_transfers); - - return 0; -} - -static int cancel_transfer(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - int r, i; - - assert(bus); - - polkit_agent_open_if_enabled(); - - for (i = 1; i < argc; i++) { - uint32_t id; - - r = safe_atou32(argv[i], &id); - if (r < 0) - return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]); - - r = sd_bus_call_method( - bus, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "CancelTransfer", - &error, - NULL, - "u", id); - if (r < 0) { - log_error("Could not cancel transfer: %s", bus_error_message(&error, -r)); - return r; - } - } - - return 0; -} - -static int set_limit(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - sd_bus *bus = userdata; - uint64_t limit; - int r; - - if (STR_IN_SET(argv[argc-1], "-", "none", "infinity")) - limit = (uint64_t) -1; - else { - r = parse_size(argv[argc-1], 1024, &limit); - if (r < 0) - return log_error("Failed to parse size: %s", argv[argc-1]); - } - - if (argc > 2) - /* With two arguments changes the quota limit of the - * specified image */ - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "SetImageLimit", - &error, - NULL, - "st", argv[1], limit); - else - /* With one argument changes the pool quota limit */ - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "SetPoolLimit", - &error, - NULL, - "t", limit); - - if (r < 0) { - log_error("Could not set limit: %s", bus_error_message(&error, -r)); - return r; - } - - return 0; -} - -static int clean_images(int argc, char *argv[], void *userdata) { - _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; - uint64_t usage, total = 0; - char fb[FORMAT_BYTES_MAX]; - sd_bus *bus = userdata; - const char *name; - unsigned c = 0; - int r; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CleanPool"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden"); - if (r < 0) - return bus_log_create_error(r); - - /* This is a slow operation, hence permit a longer time for completion. */ - r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply); - if (r < 0) - return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, 'a', "(st)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) { - log_info("Removed image '%s'. Freed exclusive disk space: %s", - name, format_bytes(fb, sizeof(fb), usage)); - - total += usage; - c++; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - log_info("Removed %u images in total. Total freed exclusive disk space %s.", - c, format_bytes(fb, sizeof(fb), total)); - - return 0; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Send control commands to or query the virtual machine and container\n" - "registration manager.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --no-ask-password Do not ask for system passwords\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " -p --property=NAME Show only properties by this name\n" - " -q --quiet Suppress output\n" - " -a --all Show all properties, including empty ones\n" - " --value When showing properties, only print the value\n" - " -l --full Do not ellipsize output\n" - " --kill-who=WHO Who to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" - " --uid=USER Specify user ID to invoke shell as\n" - " -E --setenv=VAR=VALUE Add an environment variable for shell\n" - " --read-only Create read-only bind mount\n" - " --mkdir Create directory before bind mounting, if missing\n" - " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short,\n" - " short-monotonic, verbose, export, json,\n" - " 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\n" - "Machine Commands:\n" - " list List running VMs and containers\n" - " status NAME... Show VM/container details\n" - " show [NAME...] Show properties of one or more VMs/containers\n" - " start NAME... Start container as a service\n" - " login [NAME] Get a login prompt in a container or on the\n" - " local host\n" - " shell [[USER@]NAME [COMMAND...]]\n" - " Invoke a shell (or other command) in a container\n" - " or on the local host\n" - " enable NAME... Enable automatic container start at boot\n" - " disable NAME... Disable automatic container start at boot\n" - " poweroff NAME... Power off one or more containers\n" - " reboot NAME... Reboot one or more containers\n" - " terminate NAME... Terminate one or more VMs/containers\n" - " kill NAME... Send signal to processes of a VM/container\n" - " copy-to NAME PATH [PATH] Copy files from the host to a container\n" - " copy-from NAME PATH [PATH] Copy files from a container to the host\n" - " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" - "Image Commands:\n" - " list-images Show available container and VM images\n" - " image-status [NAME...] Show image details\n" - " show-image [NAME...] Show properties of image\n" - " clone NAME NAME Clone an image\n" - " rename NAME NAME Rename an image\n" - " read-only NAME [BOOL] Mark or unmark image read-only\n" - " remove NAME... Remove an image\n" - " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n" - " clean Remove hidden (or all) images\n\n" - "Image Transfer Commands:\n" - " pull-tar URL [NAME] Download a TAR container image\n" - " pull-raw URL [NAME] Download a RAW container or VM image\n" - " import-tar FILE [NAME] Import a local TAR container image\n" - " import-raw FILE [NAME] Import a local RAW container or VM image\n" - " export-tar NAME [FILE] Export a TAR container image locally\n" - " export-raw NAME [FILE] Export a RAW container or VM image locally\n" - " list-transfers Show list of downloads in progress\n" - " cancel-transfer Cancel a download\n" - , program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_LEGEND, - ARG_VALUE, - ARG_KILL_WHO, - ARG_READ_ONLY, - ARG_MKDIR, - ARG_NO_ASK_PASSWORD, - ARG_VERIFY, - ARG_FORCE, - ARG_FORMAT, - ARG_UID, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "value", no_argument, NULL, ARG_VALUE }, - { "full", no_argument, NULL, 'l' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "kill-who", required_argument, NULL, ARG_KILL_WHO }, - { "signal", required_argument, NULL, 's' }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - { "mkdir", no_argument, NULL, ARG_MKDIR }, - { "quiet", no_argument, NULL, 'q' }, - { "lines", required_argument, NULL, 'n' }, - { "output", required_argument, NULL, 'o' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "verify", required_argument, NULL, ARG_VERIFY }, - { "force", no_argument, NULL, ARG_FORCE }, - { "format", required_argument, NULL, ARG_FORMAT }, - { "uid", required_argument, NULL, ARG_UID }, - { "setenv", required_argument, NULL, 'E' }, - {} - }; - - bool reorder = false; - int c, r, shell = -1; - - assert(argc >= 0); - assert(argv); - - for (;;) { - static const char option_string[] = "-hp:als:H:M:qn:o:"; - - c = getopt_long(argc, argv, option_string + reorder, options, NULL); - if (c < 0) - break; - - switch (c) { - - case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a - * non-option argument was discovered. */ - - assert(!reorder); - - /* We generally are fine with the fact that getopt_long() reorders the command line, and looks - * for switches after the main verb. However, for "shell" we really don't want that, since we - * want that switches specified after the machine name are passed to the program to execute, - * and not processed by us. To make this possible, we'll first invoke getopt_long() with - * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first - * non-option parameter. If it's the verb "shell" we remember its position and continue - * processing options. In this case, as soon as we hit the next non-option argument we found - * the machine name, and stop further processing. If the first non-option argument is any other - * verb than "shell" we switch to normal reordering mode and continue processing arguments - * normally. */ - - if (shell >= 0) { - /* If we already found the "shell" verb on the command line, and now found the next - * non-option argument, then this is the machine name and we should stop processing - * further arguments. */ - optind --; /* don't process this argument, go one step back */ - goto done; - } - if (streq(optarg, "shell")) - /* Remember the position of the "shell" verb, and continue processing normally. */ - shell = optind - 1; - else { - int saved_optind; - - /* OK, this is some other verb. In this case, turn on reordering again, and continue - * processing normally. */ - reorder = true; - - /* We changed the option string. getopt_long() only looks at it again if we invoke it - * at least once with a reset option index. Hence, let's reset the option index here, - * then invoke getopt_long() again (ignoring what it has to say, after all we most - * likely already processed it), and the bump the option index so that we read the - * intended argument again. */ - saved_optind = optind; - optind = 0; - (void) getopt_long(argc, argv, option_string + reorder, options, NULL); - optind = saved_optind - 1; /* go one step back, process this argument again */ - } - - break; - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case 'p': - r = strv_extend(&arg_property, optarg); - if (r < 0) - return log_oom(); - - /* If the user asked for a particular - * property, show it to him, even if it is - * empty. */ - arg_all = true; - break; - - case 'a': - arg_all = true; - break; - - case ARG_VALUE: - arg_value = true; - break; - - case 'l': - arg_full = true; - break; - - case 'n': - if (safe_atou(optarg, &arg_lines) < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } - break; - - case 'o': - arg_output = output_mode_from_string(optarg); - if (arg_output < 0) { - log_error("Unknown output '%s'.", optarg); - return -EINVAL; - } - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_legend = false; - break; - - case ARG_KILL_WHO: - arg_kill_who = optarg; - break; - - case 's': - arg_signal = signal_from_string_try_harder(optarg); - if (arg_signal < 0) { - log_error("Failed to parse signal string %s.", optarg); - return -EINVAL; - } - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_READ_ONLY: - arg_read_only = true; - break; - - case ARG_MKDIR: - arg_mkdir = true; - break; - - case 'q': - arg_quiet = true; - break; - - case ARG_VERIFY: - arg_verify = import_verify_from_string(optarg); - if (arg_verify < 0) { - log_error("Failed to parse --verify= setting: %s", optarg); - return -EINVAL; - } - break; - - case ARG_FORCE: - arg_force = true; - break; - - case ARG_FORMAT: - if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) { - log_error("Unknown format: %s", optarg); - return -EINVAL; - } - - arg_format = optarg; - break; - - case ARG_UID: - arg_uid = optarg; - break; - - case 'E': - if (!env_assignment_is_valid(optarg)) { - log_error("Environment assignment invalid: %s", optarg); - return -EINVAL; - } - - r = strv_extend(&arg_setenv, optarg); - if (r < 0) - return log_oom(); - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - } - -done: - if (shell >= 0) { - char *t; - int i; - - /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the - * argument list initially let's readjust it now, and move the "shell" verb to the back. */ - - optind -= 1; /* place the option index where the "shell" verb will be placed */ - - t = argv[shell]; - for (i = shell; i < optind; i++) - argv[i] = argv[i+1]; - argv[optind] = t; - } - - return 1; -} - -static int machinectl_main(int argc, char *argv[], sd_bus *bus) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines }, - { "list-images", VERB_ANY, 1, 0, list_images }, - { "status", 2, VERB_ANY, 0, show_machine }, - { "image-status", VERB_ANY, VERB_ANY, 0, show_image }, - { "show", VERB_ANY, VERB_ANY, 0, show_machine }, - { "show-image", VERB_ANY, VERB_ANY, 0, show_image }, - { "terminate", 2, VERB_ANY, 0, terminate_machine }, - { "reboot", 2, VERB_ANY, 0, reboot_machine }, - { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, - { "stop", 2, VERB_ANY, 0, poweroff_machine }, /* Convenience alias */ - { "kill", 2, VERB_ANY, 0, kill_machine }, - { "login", VERB_ANY, 2, 0, login_machine }, - { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, - { "bind", 3, 4, 0, bind_mount }, - { "copy-to", 3, 4, 0, copy_files }, - { "copy-from", 3, 4, 0, copy_files }, - { "remove", 2, VERB_ANY, 0, remove_image }, - { "rename", 3, 3, 0, rename_image }, - { "clone", 3, 3, 0, clone_image }, - { "read-only", 2, 3, 0, read_only_image }, - { "start", 2, VERB_ANY, 0, start_machine }, - { "enable", 2, VERB_ANY, 0, enable_machine }, - { "disable", 2, VERB_ANY, 0, enable_machine }, - { "import-tar", 2, 3, 0, import_tar }, - { "import-raw", 2, 3, 0, import_raw }, - { "export-tar", 2, 3, 0, export_tar }, - { "export-raw", 2, 3, 0, export_raw }, - { "pull-tar", 2, 3, 0, pull_tar }, - { "pull-raw", 2, 3, 0, pull_raw }, - { "list-transfers", VERB_ANY, 1, 0, list_transfers }, - { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer }, - { "set-limit", 2, 3, 0, set_limit }, - { "clean", VERB_ANY, 1, 0, clean_images }, - {} - }; - - return dispatch_verb(argc, argv, verbs, bus); -} - -int main(int argc, char*argv[]) { - sd_bus *bus = NULL; - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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; - } - - sd_bus_set_allow_interactive_authorization(bus, arg_ask_password); - - r = machinectl_main(argc, argv, bus); - -finish: - sd_bus_flush_close_unref(bus); - pager_close(); - polkit_agent_close(); - - strv_free(arg_property); - strv_free(arg_setenv); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c deleted file mode 100644 index 1923e8b971..0000000000 --- a/src/machine/machined-dbus.c +++ /dev/null @@ -1,1804 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-id128.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "cgroup-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "hostname-util.h" -#include "image-dbus.h" -#include "io-util.h" -#include "machine-dbus.h" -#include "machine-image.h" -#include "machine-pool.h" -#include "machined.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, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - assert(bus); - assert(reply); - - return sd_bus_message_append(reply, "s", "/var/lib/machines"); -} - -static int property_get_pool_usage( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_close_ int fd = -1; - uint64_t usage = (uint64_t) -1; - struct stat st; - - assert(bus); - assert(reply); - - /* We try to read the quota info from /var/lib/machines, as - * well as the usage of the loopback file - * /var/lib/machines.raw, and pick the larger value. */ - - fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (fd >= 0) { - BtrfsQuotaInfo q; - - if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0) - usage = q.referenced; - } - - if (stat("/var/lib/machines.raw", &st) >= 0) { - if (usage == (uint64_t) -1 || st.st_blocks * 512ULL > usage) - usage = st.st_blocks * 512ULL; - } - - return sd_bus_message_append(reply, "t", usage); -} - -static int property_get_pool_limit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - _cleanup_close_ int fd = -1; - uint64_t size = (uint64_t) -1; - struct stat st; - - assert(bus); - assert(reply); - - /* We try to read the quota limit from /var/lib/machines, as - * well as the size of the loopback file - * /var/lib/machines.raw, and pick the smaller value. */ - - fd = open("/var/lib/machines", O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (fd >= 0) { - BtrfsQuotaInfo q; - - if (btrfs_subvol_get_subtree_quota_fd(fd, 0, &q) >= 0) - size = q.referenced_max; - } - - if (stat("/var/lib/machines.raw", &st) >= 0) { - if (size == (uint64_t) -1 || (uint64_t) st.st_size < size) - size = st.st_size; - } - - return sd_bus_message_append(reply, "t", size); -} - -static int method_get_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - p = machine_bus_path(machine); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - r = image_find(name, NULL); - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); - if (r < 0) - return r; - - p = image_bus_path(name); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *p = NULL; - Manager *m = userdata; - Machine *machine = NULL; - pid_t pid; - int r; - - assert(message); - assert(m); - - assert_cc(sizeof(pid_t) == sizeof(uint32_t)); - - r = sd_bus_message_read(message, "u", &pid); - if (r < 0) - return r; - - if (pid < 0) - return -EINVAL; - - if (pid == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - } - - r = manager_get_machine_by_pid(m, pid, &machine); - if (r < 0) - return r; - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid); - - p = machine_bus_path(machine); - if (!p) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", p); -} - -static int method_list_machines(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Machine *machine; - Iterator i; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - r = sd_bus_message_open_container(reply, 'a', "(ssso)"); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_free_ char *p = NULL; - - p = machine_bus_path(machine); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(ssso)", - machine->name, - strempty(machine_class_to_string(machine->class)), - machine->service, - p); - if (r < 0) - return sd_bus_error_set_errno(error, r); - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, bool read_network, Machine **_m, sd_bus_error *error) { - const char *name, *service, *class, *root_directory; - const int32_t *netif = NULL; - MachineClass c; - uint32_t leader; - sd_id128_t id; - const void *v; - Machine *m; - size_t n, n_netif = 0; - int r; - - assert(manager); - assert(message); - assert(_m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - if (!machine_name_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name"); - - r = sd_bus_message_read_array(message, 'y', &v, &n); - if (r < 0) - return r; - if (n == 0) - id = SD_ID128_NULL; - else if (n == 16) - memcpy(&id, v, n); - else - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter"); - - r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory); - if (r < 0) - return r; - - if (read_network) { - size_t i; - - r = sd_bus_message_read_array(message, 'i', (const void**) &netif, &n_netif); - if (r < 0) - return r; - - n_netif /= sizeof(int32_t); - - for (i = 0; i < n_netif; i++) { - if (netif[i] <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid network interface index %i", netif[i]); - } - } - - if (isempty(class)) - c = _MACHINE_CLASS_INVALID; - else { - c = machine_class_from_string(class); - if (c < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter"); - } - - if (leader == 1) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); - - if (!isempty(root_directory) && !path_is_absolute(root_directory)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path"); - - if (leader == 0) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); - if (r < 0) - return r; - - assert_cc(sizeof(uint32_t) == sizeof(pid_t)); - - r = sd_bus_creds_get_pid(creds, (pid_t*) &leader); - if (r < 0) - return r; - } - - if (hashmap_get(manager->machines, name)) - return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name); - - r = manager_add_machine(manager, name, &m); - if (r < 0) - return r; - - m->leader = leader; - m->class = c; - m->id = id; - - if (!isempty(service)) { - m->service = strdup(service); - if (!m->service) { - r = -ENOMEM; - goto fail; - } - } - - if (!isempty(root_directory)) { - m->root_directory = strdup(root_directory); - if (!m->root_directory) { - r = -ENOMEM; - goto fail; - } - } - - if (n_netif > 0) { - assert_cc(sizeof(int32_t) == sizeof(int)); - m->netif = memdup(netif, sizeof(int32_t) * n_netif); - if (!m->netif) { - r = -ENOMEM; - goto fail; - } - - m->n_netif = n_netif; - } - - *_m = m; - - return 1; - -fail: - machine_add_to_gc_queue(m); - return r; -} - -static int method_create_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) { - Manager *manager = userdata; - Machine *m = NULL; - int r; - - assert(message); - assert(manager); - - r = method_create_or_register_machine(manager, message, read_network, &m, error); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(message, 'a', "(sv)"); - if (r < 0) - goto fail; - - r = machine_start(m, message, error); - if (r < 0) - goto fail; - - m->create_message = sd_bus_message_ref(message); - return 1; - -fail: - machine_add_to_gc_queue(m); - return r; -} - -static int method_create_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_create_machine_internal(message, true, userdata, error); -} - -static int method_create_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_create_machine_internal(message, false, userdata, error); -} - -static int method_register_machine_internal(sd_bus_message *message, bool read_network, void *userdata, sd_bus_error *error) { - Manager *manager = userdata; - _cleanup_free_ char *p = NULL; - Machine *m = NULL; - int r; - - assert(message); - assert(manager); - - r = method_create_or_register_machine(manager, message, read_network, &m, error); - if (r < 0) - return r; - - r = cg_pid_get_unit(m->leader, &m->unit); - if (r < 0) { - r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r)); - goto fail; - } - - r = machine_start(m, NULL, error); - if (r < 0) - goto fail; - - p = machine_bus_path(m); - if (!p) { - r = -ENOMEM; - goto fail; - } - - return sd_bus_reply_method_return(message, "o", p); - -fail: - machine_add_to_gc_queue(m); - return r; -} - -static int method_register_machine_with_network(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_register_machine_internal(message, true, userdata, error); -} - -static int method_register_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_register_machine_internal(message, false, userdata, error); -} - -static int method_terminate_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_terminate(message, machine, error); -} - -static int method_kill_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_kill(message, machine, error); -} - -static int method_get_machine_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_get_addresses(message, machine, error); -} - -static int method_get_machine_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_get_os_release(message, machine, error); -} - -static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(image_hashmap_freep) Hashmap *images = NULL; - Manager *m = userdata; - Image *image; - Iterator i; - int r; - - assert(message); - assert(m); - - images = hashmap_new(&string_hash_ops); - if (!images) - return -ENOMEM; - - r = image_discover(images); - if (r < 0) - return r; - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(ssbttto)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(image, images, i) { - _cleanup_free_ char *p = NULL; - - p = image_bus_path(image->name); - if (!p) - return -ENOMEM; - - r = sd_bus_message_append(reply, "(ssbttto)", - image->name, - image_type_to_string(image->type), - image->read_only, - image->crtime, - image->mtime, - image->usage, - p); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_open_machine_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_open_pty(message, machine, error); -} - -static int method_open_machine_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_open_login(message, machine, error); -} - -static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_open_shell(message, machine, error); -} - -static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_bind_mount(message, machine, error); -} - -static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_copy(message, machine, error); -} - -static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - const char *name; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - return bus_machine_method_open_root_directory(message, machine, error); -} - -static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(image_unrefp) Image* i = NULL; - const char *name; - int r; - - assert(message); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (!image_name_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); - - r = image_find(name, &i); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); - - i->userdata = userdata; - return bus_image_method_remove(message, i, error); -} - -static int method_rename_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(image_unrefp) Image* i = NULL; - const char *old_name; - int r; - - assert(message); - - r = sd_bus_message_read(message, "s", &old_name); - if (r < 0) - return r; - - if (!image_name_is_valid(old_name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name); - - r = image_find(old_name, &i); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name); - - i->userdata = userdata; - return bus_image_method_rename(message, i, error); -} - -static int method_clone_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(image_unrefp) Image *i = NULL; - const char *old_name; - int r; - - assert(message); - - r = sd_bus_message_read(message, "s", &old_name); - if (r < 0) - return r; - - if (!image_name_is_valid(old_name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name); - - r = image_find(old_name, &i); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name); - - i->userdata = userdata; - return bus_image_method_clone(message, i, error); -} - -static int method_mark_image_read_only(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(image_unrefp) Image *i = NULL; - const char *name; - int r; - - assert(message); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (!image_name_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); - - r = image_find(name, &i); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); - - i->userdata = userdata; - return bus_image_method_mark_read_only(message, i, error); -} - -static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_fclose_ FILE *f = NULL; - bool success; - size_t n; - int r; - - assert(operation); - assert(operation->extra_fd >= 0); - - if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1) - return -errno; - - f = fdopen(operation->extra_fd, "re"); - if (!f) - return -errno; - - operation->extra_fd = -1; - - /* The resulting temporary file starts with a boolean value that indicates success or not. */ - errno = 0; - n = fread(&success, 1, sizeof(success), f); - if (n != sizeof(success)) - return ret < 0 ? ret : (errno != 0 ? -errno : -EIO); - - if (ret < 0) { - _cleanup_free_ char *name = NULL; - - /* The clean-up operation failed. In this case the resulting temporary file should contain a boolean - * set to false followed by the name of the failed image. Let's try to read this and use it for the - * error message. If we can't read it, don't mind, and return the naked error. */ - - if (success) /* The resulting temporary file could not be updated, ignore it. */ - return ret; - - r = read_nul_string(f, &name); - if (r < 0 || isempty(name)) /* Same here... */ - return ret; - - return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name); - } - - assert(success); - - r = sd_bus_message_new_method_return(operation->message, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(st)"); - if (r < 0) - return r; - - /* On success the resulting temporary file will contain a list of image names that were removed followed by - * their size on disk. Let's read that and turn it into a bus message. */ - for (;;) { - _cleanup_free_ char *name = NULL; - uint64_t size; - - r = read_nul_string(f, &name); - if (r < 0) - return r; - if (isempty(name)) /* reached the end */ - break; - - errno = 0; - n = fread(&size, 1, sizeof(size), f); - if (n != sizeof(size)) - return errno != 0 ? -errno : -EIO; - - r = sd_bus_message_append(reply, "(st)", name, size); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) { - enum { - REMOVE_ALL, - REMOVE_HIDDEN, - } mode; - - _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; - _cleanup_close_ int result_fd = -1; - Manager *m = userdata; - Operation *operation; - const char *mm; - pid_t child; - int r; - - assert(message); - - if (m->n_operations >= OPERATIONS_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); - - r = sd_bus_message_read(message, "s", &mm); - if (r < 0) - return r; - - if (streq(mm, "all")) - mode = REMOVE_ALL; - else if (streq(mm, "hidden")) - mode = REMOVE_HIDDEN; - else - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - - /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this - * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this - * continuously */ - result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC); - if (result_fd < 0) - return -errno; - - /* This might be a slow operation, run it asynchronously in a background process */ - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { - _cleanup_(image_hashmap_freep) Hashmap *images = NULL; - bool success = true; - Image *image; - Iterator i; - ssize_t l; - - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - images = hashmap_new(&string_hash_ops); - if (!images) { - r = -ENOMEM; - goto child_fail; - } - - r = image_discover(images); - if (r < 0) - goto child_fail; - - l = write(result_fd, &success, sizeof(success)); - if (l < 0) { - r = -errno; - goto child_fail; - } - - HASHMAP_FOREACH(image, images, i) { - - /* We can't remove vendor images (i.e. those in /usr) */ - if (IMAGE_IS_VENDOR(image)) - continue; - - if (IMAGE_IS_HOST(image)) - continue; - - if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) - continue; - - r = image_remove(image); - if (r == -EBUSY) /* keep images that are currently being used. */ - continue; - if (r < 0) { - /* If the operation failed, let's override everything we wrote, and instead write there at which image we failed. */ - success = false; - (void) ftruncate(result_fd, 0); - (void) lseek(result_fd, 0, SEEK_SET); - (void) write(result_fd, &success, sizeof(success)); - (void) write(result_fd, image->name, strlen(image->name)+1); - goto child_fail; - } - - l = write(result_fd, image->name, strlen(image->name)+1); - if (l < 0) { - r = -errno; - goto child_fail; - } - - l = write(result_fd, &image->usage_exclusive, sizeof(image->usage_exclusive)); - if (l < 0) { - r = -errno; - goto child_fail; - } - } - - result_fd = safe_close(result_fd); - _exit(EXIT_SUCCESS); - - child_fail: - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - _exit(EXIT_FAILURE); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - /* The clean-up might take a while, hence install a watch on the child and return */ - - r = operation_new(m, NULL, child, message, errno_pipe_fd[0], &operation); - if (r < 0) { - (void) sigkill_wait(child); - return r; - } - - operation->extra_fd = result_fd; - operation->done = clean_pool_done; - - result_fd = -1; - errno_pipe_fd[0] = -1; - - return 1; -} - -static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - uint64_t limit; - int r; - - assert(message); - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (!FILE_SIZE_VALID_OR_INFINITY(limit)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); - - r = bus_verify_polkit_async( - message, - CAP_SYS_ADMIN, - "org.freedesktop.machine1.manage-machines", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - /* Set up the machine directory if necessary */ - r = setup_machine_directory(limit, error); - if (r < 0) - return r; - - /* Resize the backing loopback device, if there is one, except if we asked to drop any limit */ - if (limit != (uint64_t) -1) { - r = btrfs_resize_loopback("/var/lib/machines", limit, false); - if (r == -ENOTTY) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs."); - if (r < 0 && r != -ENODEV) /* ignore ENODEV, as that's what is returned if the file system is not on loopback */ - return sd_bus_error_set_errnof(error, r, "Failed to adjust loopback limit: %m"); - } - - (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) - return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m"); - - return sd_bus_reply_method_return(message, NULL); -} - -static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(image_unrefp) Image *i = NULL; - const char *name; - int r; - - assert(message); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - if (!image_name_is_valid(name)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name); - - r = image_find(name, &i); - if (r < 0) - return r; - if (r == 0) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); - - i->userdata = userdata; - return bus_image_method_set_limit(message, i, error); -} - -static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; - Manager *m = userdata; - const char *name, *p; - Machine *machine; - uint32_t uid; - int r; - - r = sd_bus_message_read(message, "su", &name, &uid); - if (r < 0) - return r; - - if (!uid_is_valid(uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - if (machine->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - - p = procfs_file_alloca(machine->leader, "uid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; - - for (;;) { - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f) && errno > 0) - return -errno; - - return -EIO; - } - - if (uid < uid_base || uid >= uid_base + uid_range) - continue; - - converted = uid - uid_base + uid_shift; - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); -} - -static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - uid_t uid; - Iterator i; - int r; - - r = sd_bus_message_read(message, "u", &uid); - if (r < 0) - return r; - if (!uid_is_valid(uid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - if (uid < 0x10000) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid); - - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; - - if (machine->class != MACHINE_CONTAINER) - continue; - - xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed top open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f) && errno > 0) - return -errno; - - return -EIO; - } - - if (uid < uid_shift || uid >= uid_shift + uid_range) - continue; - - converted = (uid - uid_shift + uid_base); - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); -} - -static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; - Manager *m = groupdata; - const char *name, *p; - Machine *machine; - uint32_t gid; - int r; - - r = sd_bus_message_read(message, "su", &name, &gid); - if (r < 0) - return r; - - if (!gid_is_valid(gid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - machine = hashmap_get(m->machines, name); - if (!machine) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); - - if (machine->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - - p = procfs_file_alloca(machine->leader, "gid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; - - for (;;) { - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f) && errno > 0) - return -errno; - - return -EIO; - } - - if (gid < gid_base || gid >= gid_base + gid_range) - continue; - - converted = gid - gid_base + gid_shift; - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name); -} - -static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - Manager *m = groupdata; - Machine *machine; - gid_t gid; - Iterator i; - int r; - - r = sd_bus_message_read(message, "u", &gid); - if (r < 0) - return r; - if (!gid_is_valid(gid)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - if (gid < 0x10000) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid); - - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; - - if (machine->class != MACHINE_CONTAINER) - continue; - - xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed top open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f) && errno > 0) - return -errno; - - return -EIO; - } - - if (gid < gid_shift || gid >= gid_shift + gid_range) - continue; - - converted = (gid - gid_shift + gid_base); - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } - - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); -} - -const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0), - SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0), - SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0), - SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0), - SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0), - SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), - SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0), - SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), - SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("MachineNew", "so", 0), - SD_BUS_SIGNAL("MachineRemoved", "so", 0), - SD_BUS_VTABLE_END -}; - -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *result, *unit; - Manager *m = userdata; - Machine *machine; - uint32_t id; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - machine = hashmap_get(m->machine_units, unit); - if (!machine) - return 0; - - if (streq_ptr(path, machine->scope_job)) { - machine->scope_job = mfree(machine->scope_job); - - if (machine->started) { - if (streq(result, "done")) - machine_send_create_reply(machine, NULL); - else { - _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL; - - sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); - - machine_send_create_reply(machine, &e); - } - } - - machine_save(machine); - } - - machine_add_to_gc_queue(machine); - return 0; -} - -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *unit = NULL; - const char *path; - Manager *m = userdata; - Machine *machine; - int r; - - assert(message); - assert(m); - - path = sd_bus_message_get_path(message); - if (!path) - return 0; - - r = unit_name_from_dbus_path(path, &unit); - if (r == -EINVAL) /* not for a unit */ - return 0; - if (r < 0) { - log_oom(); - return 0; - } - - machine = hashmap_get(m->machine_units, unit); - if (!machine) - return 0; - - machine_add_to_gc_queue(machine); - return 0; -} - -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *path, *unit; - Manager *m = userdata; - Machine *machine; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "so", &unit, &path); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - machine = hashmap_get(m->machine_units, unit); - if (!machine) - return 0; - - machine_add_to_gc_queue(machine); - return 0; -} - -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Machine *machine; - Iterator i; - int b, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - if (b) - return 0; - - /* systemd finished reloading, let's recheck all our machines */ - log_debug("System manager has been reloaded, rechecking machines..."); - - HASHMAP_FOREACH(machine, m->machines, i) - machine_add_to_gc_queue(machine); - - return 0; -} - -int manager_start_scope( - Manager *manager, - const char *scope, - pid_t pid, - const char *slice, - const char *description, - sd_bus_message *more_properties, - sd_bus_error *error, - char **job) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - int r; - - assert(manager); - assert(scope); - assert(pid > 1); - - r = sd_bus_message_new_method_call( - manager->bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", strempty(scope), "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - if (!isempty(slice)) { - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return r; - } - - if (!isempty(description)) { - r = sd_bus_message_append(m, "(sv)", "Description", "s", description); - if (r < 0) - return r; - } - - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "Delegate", "b", 1); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384)); - if (r < 0) - return bus_log_create_error(r); - - if (more_properties) { - r = sd_bus_message_copy(m, more_properties, true); - 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; - - 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; -} - -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(unit); - - r = sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StopUnit", - error, - &reply, - "ss", unit, "fail"); - if (r < 0) { - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) { - - if (job) - *job = NULL; - - sd_bus_error_free(error); - return 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; -} - -int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) { - assert(manager); - assert(unit); - - return sd_bus_call_method( - manager->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - error, - NULL, - "ssi", unit, "all", signo); -} - -int manager_unit_is_active(Manager *manager, const char *unit) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *path = NULL; - const char *state; - int r; - - assert(manager); - assert(unit); - - path = unit_dbus_path_from_name(unit); - if (!path) - return -ENOMEM; - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &reply, - "s"); - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) || - sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) - return false; - - return r; - } - - r = sd_bus_message_read(reply, "s", &state); - if (r < 0) - return -EINVAL; - - return !STR_IN_SET(state, "inactive", "failed"); -} - -int manager_job_is_active(Manager *manager, const char *path) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - int r; - - assert(manager); - assert(path); - - r = sd_bus_get_property( - manager->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "State", - &error, - &reply, - "s"); - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) - return true; - - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT)) - return false; - - return r; - } - - /* We don't actually care about the state really. The fact - * that we could read the job state is enough for us */ - - return true; -} - -int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - Machine *mm; - int r; - - assert(m); - assert(pid >= 1); - assert(machine); - - mm = hashmap_get(m->machine_leaders, PID_TO_PTR(pid)); - if (!mm) { - _cleanup_free_ char *unit = NULL; - - r = cg_pid_get_unit(pid, &unit); - if (r >= 0) - mm = hashmap_get(m->machine_units, unit); - } - if (!mm) - return 0; - - *machine = mm; - return 1; -} - -int manager_add_machine(Manager *m, const char *name, Machine **_machine) { - Machine *machine; - - assert(m); - assert(name); - - machine = hashmap_get(m->machines, name); - if (!machine) { - machine = machine_new(m, _MACHINE_CLASS_INVALID, name); - if (!machine) - return -ENOMEM; - } - - if (_machine) - *_machine = machine; - - return 0; -} diff --git a/src/machine/machined.c b/src/machine/machined.c deleted file mode 100644 index 57121945f3..0000000000 --- a/src/machine/machined.c +++ /dev/null @@ -1,415 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-util.h" -#include "cgroup-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "formats-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; - int r; - - m = new0(Manager, 1); - if (!m) - return NULL; - - m->machines = hashmap_new(&string_hash_ops); - m->machine_units = hashmap_new(&string_hash_ops); - m->machine_leaders = hashmap_new(NULL); - - if (!m->machines || !m->machine_units || !m->machine_leaders) { - manager_free(m); - return NULL; - } - - r = sd_event_default(&m->event); - if (r < 0) { - manager_free(m); - return NULL; - } - - sd_event_set_watchdog(m->event, true); - - return m; -} - -void manager_free(Manager *m) { - Machine *machine; - Image *i; - - assert(m); - - while (m->operations) - operation_free(m->operations); - - assert(m->n_operations == 0); - - while ((machine = hashmap_first(m->machines))) - machine_free(machine); - - hashmap_free(m->machines); - hashmap_free(m->machine_units); - hashmap_free(m->machine_leaders); - - while ((i = hashmap_steal_first(m->image_cache))) - image_unref(i); - - hashmap_free(m->image_cache); - - sd_event_source_unref(m->image_cache_defer_event); - - bus_verify_polkit_async_registry_free(m->polkit_registry); - - sd_bus_unref(m->bus); - sd_event_unref(m->event); - - free(m); -} - -static int manager_add_host_machine(Manager *m) { - _cleanup_free_ char *rd = NULL, *unit = NULL; - sd_id128_t mid; - Machine *t; - int r; - - if (m->host_machine) - return 0; - - r = sd_id128_get_machine(&mid); - if (r < 0) - return log_error_errno(r, "Failed to get machine ID: %m"); - - rd = strdup("/"); - if (!rd) - return log_oom(); - - unit = strdup("-.slice"); - if (!unit) - return log_oom(); - - t = machine_new(m, MACHINE_HOST, ".host"); - if (!t) - return log_oom(); - - t->leader = 1; - t->id = mid; - - t->root_directory = rd; - t->unit = unit; - rd = unit = NULL; - - dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); - - m->host_machine = t; - - return 0; -} - -int manager_enumerate_machines(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - r = manager_add_host_machine(m); - if (r < 0) - return r; - - /* Read in machine data stored on disk */ - d = opendir("/run/systemd/machines"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/machines: %m"); - } - - FOREACH_DIRENT(de, d, return -errno) { - struct Machine *machine; - int k; - - if (!dirent_is_file(de)) - continue; - - /* Ignore symlinks that map the unit name to the machine */ - if (startswith(de->d_name, "unit:")) - continue; - - if (!machine_name_is_valid(de->d_name)) - continue; - - k = manager_add_machine(m, de->d_name, &machine); - if (k < 0) { - r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); - continue; - } - - machine_add_to_gc_queue(machine); - - k = machine_load(machine); - if (k < 0) - r = k; - } - - return r; -} - -static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(m); - assert(!m->bus); - - r = sd_bus_default_system(&m->bus); - if (r < 0) - return log_error_errno(r, "Failed to connect to system bus: %m"); - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add machine object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add machine enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add image object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add image enumerator: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, - m); - if (r < 0) - return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, - m); - if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'," - "arg0='org.freedesktop.systemd1.Unit'", - match_properties_changed, - m); - if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, - m); - if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); - - r = sd_bus_call_method( - m->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } - - r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - return 0; -} - -void manager_gc(Manager *m, bool drop_not_started) { - Machine *machine; - - assert(m); - - while ((machine = m->machine_gc_queue)) { - LIST_REMOVE(gc_queue, m->machine_gc_queue, machine); - machine->in_gc_queue = false; - - /* First, if we are not closing yet, initiate stopping */ - if (!machine_check_gc(machine, drop_not_started) && - machine_get_state(machine) != MACHINE_CLOSING) - machine_stop(machine); - - /* Now, the stop probably made this referenced - * again, but if it didn't, then it's time to let it - * go entirely. */ - if (!machine_check_gc(machine, drop_not_started)) { - machine_finalize(machine); - machine_free(machine); - } - } -} - -int manager_startup(Manager *m) { - Machine *machine; - Iterator i; - int r; - - assert(m); - - /* Connect to the bus */ - r = manager_connect_bus(m); - if (r < 0) - return r; - - /* Deserialize state */ - manager_enumerate_machines(m); - - /* Remove stale objects before we start them */ - manager_gc(m, false); - - /* And start everything */ - HASHMAP_FOREACH(machine, m->machines, i) - machine_start(machine, NULL, NULL); - - return 0; -} - -static bool check_idle(void *userdata) { - Manager *m = userdata; - - if (m->operations) - return false; - - manager_gc(m, true); - - return hashmap_isempty(m->machines); -} - -int manager_run(Manager *m) { - assert(m); - - return bus_event_loop_with_idle( - m->event, - m->bus, - "org.freedesktop.machine1", - DEFAULT_EXIT_USEC, - check_idle, m); -} - -int main(int argc, char *argv[]) { - Manager *m = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_set_facility(LOG_AUTH); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - /* Always create the directories people can create inotify - * watches in. Note that some applications might check for the - * existence of /run/systemd/machines/ to determine whether - * machined is available, so please always make sure this - * check stays in. */ - mkdir_label("/run/systemd/machines", 0755); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - m = manager_new(); - if (!m) { - r = log_oom(); - goto finish; - } - - r = manager_startup(m); - if (r < 0) { - log_error_errno(r, "Failed to fully start up daemon: %m"); - goto finish; - } - - log_debug("systemd-machined running as pid "PID_FMT, getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - r = manager_run(m); - - log_debug("systemd-machined stopped as pid "PID_FMT, getpid()); - -finish: - manager_free(m); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/machine/machined.h b/src/machine/machined.h deleted file mode 100644 index 7b9b148044..0000000000 --- a/src/machine/machined.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-bus.h" -#include "sd-event.h" - -#include "hashmap.h" -#include "list.h" - -typedef struct Manager Manager; - -#include "image-dbus.h" -#include "machine-dbus.h" -#include "machine.h" -#include "operation.h" - -struct Manager { - sd_event *event; - sd_bus *bus; - - Hashmap *machines; - Hashmap *machine_units; - Hashmap *machine_leaders; - - Hashmap *polkit_registry; - - Hashmap *image_cache; - sd_event_source *image_cache_defer_event; - - LIST_HEAD(Machine, machine_gc_queue); - - Machine *host_machine; - - LIST_HEAD(Operation, operations); - unsigned n_operations; -}; - -Manager *manager_new(void); -void manager_free(Manager *m); - -int manager_add_machine(Manager *m, const char *name, Machine **_machine); -int manager_enumerate_machines(Manager *m); - -int manager_startup(Manager *m); -int manager_run(Manager *m); - -void manager_gc(Manager *m, bool drop_not_started); - -int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine); - -extern const sd_bus_vtable manager_vtable[]; - -int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error); -int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error); - -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, sd_bus_message *more_properties, sd_bus_error *error, char **job); -int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); -int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error); -int manager_unit_is_active(Manager *manager, const char *unit); -int manager_job_is_active(Manager *manager, const char *path); diff --git a/src/machine/operation.c b/src/machine/operation.c deleted file mode 100644 index 2bf93cb493..0000000000 --- a/src/machine/operation.c +++ /dev/null @@ -1,152 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "fd-util.h" -#include "operation.h" -#include "process-util.h" - -static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - Operation *o = userdata; - int r; - - assert(o); - assert(si); - - log_debug("Operating " PID_FMT " is now complete with code=%s status=%i", - o->pid, - sigchld_code_to_string(si->si_code), si->si_status); - - o->pid = 0; - - if (si->si_code != CLD_EXITED) { - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto fail; - } - - if (si->si_status == EXIT_SUCCESS) - r = 0; - else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */ - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - goto fail; - } - - if (o->done) { - /* A completion routine is set for this operation, call it. */ - r = o->done(o, r, &error); - if (r < 0) { - if (!sd_bus_error_is_set(&error)) - sd_bus_error_set_errno(&error, r); - - goto fail; - } - - } else { - /* The default operation when done is to simply return an error on failure or an empty success - * message on success. */ - if (r < 0) - goto fail; - - r = sd_bus_reply_method_return(o->message, NULL); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - } - - operation_free(o); - return 0; - -fail: - r = sd_bus_reply_method_error(o->message, &error); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - operation_free(o); - return 0; -} - -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) { - Operation *o; - int r; - - assert(manager); - assert(child > 1); - assert(message); - assert(errno_fd >= 0); - - o = new0(Operation, 1); - if (!o) - return -ENOMEM; - - o->extra_fd = -1; - - r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); - if (r < 0) { - free(o); - return r; - } - - o->pid = child; - o->message = sd_bus_message_ref(message); - o->errno_fd = errno_fd; - - LIST_PREPEND(operations, manager->operations, o); - manager->n_operations++; - o->manager = manager; - - if (machine) { - LIST_PREPEND(operations_by_machine, machine->operations, o); - o->machine = machine; - } - - log_debug("Started new operation " PID_FMT ".", child); - - /* At this point we took ownership of both the child and the errno file descriptor! */ - - if (ret) - *ret = o; - - return 0; -} - -Operation *operation_free(Operation *o) { - if (!o) - return NULL; - - sd_event_source_unref(o->event_source); - - safe_close(o->errno_fd); - safe_close(o->extra_fd); - - if (o->pid > 1) - (void) sigkill_wait(o->pid); - - sd_bus_message_unref(o->message); - - if (o->manager) { - LIST_REMOVE(operations, o->manager->operations, o); - o->manager->n_operations--; - } - - if (o->machine) - LIST_REMOVE(operations_by_machine, o->machine->operations, o); - - free(o); - return NULL; -} diff --git a/src/machine/operation.h b/src/machine/operation.h deleted file mode 100644 index 9831b123d7..0000000000 --- a/src/machine/operation.h +++ /dev/null @@ -1,49 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-bus.h" -#include "sd-event.h" - -#include "list.h" - -typedef struct Operation Operation; - -#include "machined.h" - -#define OPERATIONS_MAX 64 - -struct Operation { - Manager *manager; - Machine *machine; - pid_t pid; - sd_bus_message *message; - int errno_fd; - int extra_fd; - sd_event_source *event_source; - int (*done)(Operation *o, int ret, sd_bus_error *error); - LIST_FIELDS(Operation, operations); - LIST_FIELDS(Operation, operations_by_machine); -}; - -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret); -Operation *operation_free(Operation *o); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf deleted file mode 100644 index 562b9d3cc0..0000000000 --- a/src/machine/org.freedesktop.machine1.conf +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in deleted file mode 100644 index 69f78a5c25..0000000000 --- a/src/machine/org.freedesktop.machine1.policy.in +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Log into a local container - <_message>Authentication is required to log into a local container. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Log into the local host - <_message>Authentication is required to log into the local host. - - auth_admin - auth_admin - yes - - - - - <_description>Acquire a shell in a local container - <_message>Authentication is required to acquire a shell in a local container. - - auth_admin - auth_admin - auth_admin_keep - - org.freedesktop.login1.login - - - - <_description>Acquire a shell on the local host - <_message>Authentication is required to acquire a shell on the local host. - - auth_admin - auth_admin - auth_admin_keep - - org.freedesktop.login1.host-login - - - - <_description>Acquire a pseudo TTY in a local container - <_message>Authentication is required to acquire a pseudo TTY in a local container. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Acquire a pseudo TTY on the local host - <_message>Authentication is required to acquire a pseudo TTY on the local host. - - auth_admin - auth_admin - auth_admin_keep - - - - - <_description>Manage local virtual machines and containers - <_message>Authentication is required to manage local virtual machines and containers. - - auth_admin - auth_admin - auth_admin_keep - - org.freedesktop.login1.shell org.freedesktop.login1.login - - - - <_description>Manage local virtual machine and container images - <_message>Authentication is required to manage local virtual machine and container images. - - auth_admin - auth_admin - auth_admin_keep - - - - diff --git a/src/machine/org.freedesktop.machine1.service b/src/machine/org.freedesktop.machine1.service deleted file mode 100644 index d3dc99852b..0000000000 --- a/src/machine/org.freedesktop.machine1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.machine1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.machine1.service diff --git a/src/machine/test-machine-tables.c b/src/machine/test-machine-tables.c deleted file mode 100644 index f851a4d37d..0000000000 --- a/src/machine/test-machine-tables.c +++ /dev/null @@ -1,29 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "machine.h" -#include "test-tables.h" - -int main(int argc, char **argv) { - test_table(machine_class, MACHINE_CLASS); - test_table(machine_state, MACHINE_STATE); - test_table(kill_who, KILL_WHO); - - return EXIT_SUCCESS; -} diff --git a/src/manpages/daemon.xml b/src/manpages/daemon.xml new file mode 100644 index 0000000000..a649749683 --- /dev/null +++ b/src/manpages/daemon.xml @@ -0,0 +1,763 @@ + + + + + + + + + daemon + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + daemon + 7 + + + + daemon + Writing and packaging system daemons + + + + Description + + A daemon is a service process that runs in the background + and supervises the system or provides functionality to other + processes. Traditionally, daemons are implemented following a + scheme originating in SysV Unix. Modern daemons should follow a + simpler yet more powerful scheme (here called "new-style" + daemons), as implemented by + systemd1. + This manual page covers both schemes, and in particular includes + recommendations for daemons that shall be included in the systemd + init system. + + + SysV Daemons + + When a traditional SysV daemon starts, it should execute + the following steps as part of the initialization. Note that + these steps are unnecessary for new-style daemons (see below), + and should only be implemented if compatibility with SysV is + essential. + + + Close all open file descriptors except + standard input, output, and error (i.e. the first three file + descriptors 0, 1, 2). This ensures that no accidentally passed + file descriptor stays around in the daemon process. On Linux, + this is best implemented by iterating through + /proc/self/fd, with a fallback of + iterating from file descriptor 3 to the value returned by + getrlimit() for + RLIMIT_NOFILE. + + Reset all signal handlers to their default. + This is best done by iterating through the available signals + up to the limit of _NSIG and resetting + them to SIG_DFL. + + Reset the signal mask + using + sigprocmask(). + + Sanitize the environment block, removing or + resetting environment variables that might negatively impact + daemon runtime. + + Call fork(), to create a + background process. + + In the child, call + setsid() to detach from any terminal and + create an independent session. + + In the child, call fork() + again, to ensure that the daemon can never re-acquire a + terminal again. + + Call exit() in the first + child, so that only the second child (the actual daemon + process) stays around. This ensures that the daemon process is + re-parented to init/PID 1, as all daemons should + be. + + In the daemon process, connect + /dev/null to standard input, output, and + error. + + In the daemon process, reset the umask to 0, + so that the file modes passed to open(), + mkdir() and suchlike directly control the + access mode of the created files and + directories. + + In the daemon process, change the current + directory to the root directory (/), in order to avoid that + the daemon involuntarily blocks mount points from being + unmounted. + + In the daemon process, write the daemon PID + (as returned by getpid()) to a PID file, + for example /run/foobar.pid (for a + hypothetical daemon "foobar") to ensure that the daemon cannot + be started more than once. This must be implemented in + race-free fashion so that the PID file is only updated when it + is verified at the same time that the PID previously stored in + the PID file no longer exists or belongs to a foreign + process. + + In the daemon process, drop privileges, if + possible and applicable. + + From the daemon process, notify the original + process started that initialization is complete. This can be + implemented via an unnamed pipe or similar communication + channel that is created before the first + fork() and hence available in both the + original and the daemon process. + + Call exit() in the + original process. The process that invoked the daemon must be + able to rely on that this exit() happens + after initialization is complete and all external + communication channels are established and + accessible. + + + The BSD daemon() function should not + be used, as it implements only a subset of these steps. + + A daemon that needs to provide compatibility with SysV + systems should implement the scheme pointed out above. However, + it is recommended to make this behavior optional and + configurable via a command line argument to ease debugging as + well as to simplify integration into systems using + systemd. + + + + New-Style Daemons + + Modern services for GNU/Linux should be implemented as + new-style daemons. This makes it easier to supervise and control + them at runtime and simplifies their implementation. + + For developing a new-style daemon, none of the + initialization steps recommended for SysV daemons need to be + implemented. New-style init systems such as systemd make all of + them redundant. Moreover, since some of these steps interfere + with process monitoring, file descriptor passing and other + functionality of the init system, it is recommended not to + execute them when run as new-style service. + + Note that new-style init systems guarantee execution of daemon processes in a clean process context: it is + guaranteed that the environment block is sanitized, that the signal handlers and mask is reset and that no + left-over file descriptors are passed. Daemons will be executed in their own session, with standard input + connected to /dev/null and standard output/error connected to the + systemd-journald.service8 + logging service, unless otherwise configured. The umask is reset. + + + It is recommended for new-style daemons to implement the + following: + + + If SIGTERM is received, + shut down the daemon and exit cleanly. + + If SIGHUP is received, + reload the configuration files, if this + applies. + + Provide a correct exit code from the main + daemon process, as this is used by the init system to detect + service errors and problems. It is recommended to follow the + exit code scheme as defined in the LSB + recommendations for SysV init + scripts. + + If possible and applicable, expose the + daemon's control interface via the D-Bus IPC system and grab a + bus name as last step of initialization. + + For integration in systemd, provide a + .service unit file that carries + information about starting, stopping and otherwise maintaining + the daemon. See + systemd.service5 + for details. + + As much as possible, rely on the init system's + functionality to limit the access of the daemon to files, + services and other resources, i.e. in the case of systemd, + rely on systemd's resource limit control instead of + implementing your own, rely on systemd's privilege dropping + code instead of implementing it in the daemon, and similar. + See + systemd.exec5 + for the available controls. + + If D-Bus is used, make your daemon + bus-activatable by supplying a D-Bus service activation + configuration file. This has multiple advantages: your daemon + may be started lazily on-demand; it may be started in parallel + to other daemons requiring it — which maximizes + parallelization and boot-up speed; your daemon can be + restarted on failure without losing any bus requests, as the + bus queues requests for activatable services. See below for + details. + + If your daemon provides services to other + local processes or remote clients via a socket, it should be + made socket-activatable following the scheme pointed out + below. Like D-Bus activation, this enables on-demand starting + of services as well as it allows improved parallelization of + service start-up. Also, for state-less protocols (such as + syslog, DNS), a daemon implementing socket-based activation + can be restarted without losing a single request. See below + for details. + + If applicable, a daemon should notify the init + system about startup completion or status updates via the + sd_notify3 + interface. + + Instead of using the + syslog() call to log directly to the + system syslog service, a new-style daemon may choose to simply + log to standard error via fprintf(), + which is then forwarded to syslog by the init system. If log + levels are necessary, these can be encoded by prefixing + individual log lines with strings like + <4> (for log level 4 "WARNING" in the + syslog priority scheme), following a similar style as the + Linux kernel's printk() level system. For + details, see + sd-daemon3 + and + systemd.exec5. + + + + These recommendations are similar but not identical to the + Apple + MacOS X Daemon Requirements. + + + + + Activation + + New-style init systems provide multiple additional + mechanisms to activate services, as detailed below. It is common + that services are configured to be activated via more than one + mechanism at the same time. An example for systemd: + bluetoothd.service might get activated either + when Bluetooth hardware is plugged in, or when an application + accesses its programming interfaces via D-Bus. Or, a print server + daemon might get activated when traffic arrives at an IPP port, or + when a printer is plugged in, or when a file is queued in the + printer spool directory. Even for services that are intended to be + started on system bootup unconditionally, it is a good idea to + implement some of the various activation schemes outlined below, + in order to maximize parallelization. If a daemon implements a + D-Bus service or listening socket, implementing the full bus and + socket activation scheme allows starting of the daemon with its + clients in parallel (which speeds up boot-up), since all its + communication channels are established already, and no request is + lost because client requests will be queued by the bus system (in + case of D-Bus) or the kernel (in case of sockets) until the + activation is completed. + + + Activation on Boot + + Old-style daemons are usually activated exclusively on + boot (and manually by the administrator) via SysV init scripts, + as detailed in the LSB + Linux Standard Base Core Specification. This method of + activation is supported ubiquitously on GNU/Linux init systems, both + old-style and new-style systems. Among other issues, SysV init + scripts have the disadvantage of involving shell scripts in the + boot process. New-style init systems generally employ updated + versions of activation, both during boot-up and during runtime + and using more minimal service description files. + + In systemd, if the developer or administrator wants to + make sure that a service or other unit is activated + automatically on boot, it is recommended to place a symlink to + the unit file in the .wants/ directory of + either multi-user.target or + graphical.target, which are normally used + as boot targets at system startup. See + systemd.unit5 + for details about the .wants/ directories, + and + systemd.special7 + for details about the two boot targets. + + + + + Socket-Based Activation + + In order to maximize the possible parallelization and + robustness and simplify configuration and development, it is + recommended for all new-style daemons that communicate via + listening sockets to employ socket-based activation. In a + socket-based activation scheme, the creation and binding of the + listening socket as primary communication channel of daemons to + local (and sometimes remote) clients is moved out of the daemon + code and into the init system. Based on per-daemon + configuration, the init system installs the sockets and then + hands them off to the spawned process as soon as the respective + daemon is to be started. Optionally, activation of the service + can be delayed until the first inbound traffic arrives at the + socket to implement on-demand activation of daemons. However, + the primary advantage of this scheme is that all providers and + all consumers of the sockets can be started in parallel as soon + as all sockets are established. In addition to that, daemons can + be restarted with losing only a minimal number of client + transactions, or even any client request at all (the latter is + particularly true for state-less protocols, such as DNS or + syslog), because the socket stays bound and accessible during + the restart, and all requests are queued while the daemon cannot + process them. + + New-style daemons which support socket activation must be + able to receive their sockets from the init system instead of + creating and binding them themselves. For details about the + programming interfaces for this scheme provided by systemd, see + sd_listen_fds3 + and + sd-daemon3. + For details about porting existing daemons to socket-based + activation, see below. With minimal effort, it is possible to + implement socket-based activation in addition to traditional + internal socket creation in the same codebase in order to + support both new-style and old-style init systems from the same + daemon binary. + + systemd implements socket-based activation via + .socket units, which are described in + systemd.socket5. + When configuring socket units for socket-based activation, it is + essential that all listening sockets are pulled in by the + special target unit sockets.target. It is + recommended to place a + WantedBy=sockets.target directive in the + [Install] section to automatically add such a + dependency on installation of a socket unit. Unless + DefaultDependencies=no is set, the necessary + ordering dependencies are implicitly created for all socket + units. For more information about + sockets.target, see + systemd.special7. + It is not necessary or recommended to place any additional + dependencies on socket units (for example from + multi-user.target or suchlike) when one is + installed in sockets.target. + + + + Bus-Based Activation + + When the D-Bus IPC system is used for communication with + clients, new-style daemons should employ bus activation so that + they are automatically activated when a client application + accesses their IPC interfaces. This is configured in D-Bus + service files (not to be confused with systemd service unit + files!). To ensure that D-Bus uses systemd to start-up and + maintain the daemon, use the SystemdService= + directive in these service files to configure the matching + systemd service for a D-Bus service. e.g.: For a D-Bus service + whose D-Bus activation file is named + org.freedesktop.RealtimeKit.service, make + sure to set + SystemdService=rtkit-daemon.service in that + file to bind it to the systemd service + rtkit-daemon.service. This is needed to + make sure that the daemon is started in a race-free fashion when + activated via multiple mechanisms simultaneously. + + + + Device-Based Activation + + Often, daemons that manage a particular type of hardware + should be activated only when the hardware of the respective + kind is plugged in or otherwise becomes available. In a + new-style init system, it is possible to bind activation to + hardware plug/unplug events. In systemd, kernel devices + appearing in the sysfs/udev device tree can be exposed as units + if they are tagged with the string systemd. + Like any other kind of unit, they may then pull in other units + when activated (i.e. plugged in) and thus implement device-based + activation. systemd dependencies may be encoded in the udev + database via the SYSTEMD_WANTS= property. See + systemd.device5 + for details. Often, it is nicer to pull in services from devices + only indirectly via dedicated targets. Example: Instead of + pulling in bluetoothd.service from all the + various bluetooth dongles and other hardware available, pull in + bluetooth.target from them and + bluetoothd.service from that target. This + provides for nicer abstraction and gives administrators the + option to enable bluetoothd.service via + controlling a bluetooth.target.wants/ + symlink uniformly with a command like enable + of + systemctl1 + instead of manipulating the udev ruleset. + + + + Path-Based Activation + + Often, runtime of daemons processing spool files or + directories (such as a printing system) can be delayed until + these file system objects change state, or become non-empty. + New-style init systems provide a way to bind service activation + to file system changes. systemd implements this scheme via + path-based activation configured in .path + units, as outlined in + systemd.path5. + + + + Timer-Based Activation + + Some daemons that implement clean-up jobs that are + intended to be executed in regular intervals benefit from + timer-based activation. In systemd, this is implemented via + .timer units, as described in + systemd.timer5. + + + + Other Forms of Activation + + Other forms of activation have been suggested and + implemented in some systems. However, there are often simpler or + better alternatives, or they can be put together of combinations + of the schemes above. Example: Sometimes, it appears useful to + start daemons or .socket units when a + specific IP address is configured on a network interface, + because network sockets shall be bound to the address. However, + an alternative to implement this is by utilizing the Linux + IP_FREEBIND socket option, as accessible + via FreeBind=yes in systemd socket files (see + systemd.socket5 + for details). This option, when enabled, allows sockets to be + bound to a non-local, not configured IP address, and hence + allows bindings to a particular IP address before it actually + becomes available, making such an explicit dependency to the + configured address redundant. Another often suggested trigger + for service activation is low system load. However, here too, a + more convincing approach might be to make proper use of features + of the operating system, in particular, the CPU or I/O scheduler + of Linux. Instead of scheduling jobs from userspace based on + monitoring the OS scheduler, it is advisable to leave the + scheduling of processes to the OS scheduler itself. systemd + provides fine-grained access to the CPU and I/O schedulers. If a + process executed by the init system shall not negatively impact + the amount of CPU or I/O bandwidth available to other processes, + it should be configured with + CPUSchedulingPolicy=idle and/or + IOSchedulingClass=idle. Optionally, this may + be combined with timer-based activation to schedule background + jobs during runtime and with minimal impact on the system, and + remove it from the boot phase itself. + + + + + Integration with Systemd + + + Writing Systemd Unit Files + + When writing systemd unit files, it is recommended to + consider the following suggestions: + + + If possible, do not use the + Type=forking setting in service files. But + if you do, make sure to set the PID file path using + PIDFile=. See + systemd.service5 + for details. + + If your daemon registers a D-Bus name on the + bus, make sure to use Type=dbus in the + service file if possible. + + Make sure to set a good human-readable + description string with + Description=. + + Do not disable + DefaultDependencies=, unless you really + know what you do and your unit is involved in early boot or + late system shutdown. + + Normally, little if any dependencies should + need to be defined explicitly. However, if you do configure + explicit dependencies, only refer to unit names listed on + systemd.special7 + or names introduced by your own package to keep the unit file + operating system-independent. + + Make sure to include an + [Install] section including installation + information for the unit file. See + systemd.unit5 + for details. To activate your service on boot, make sure to + add a WantedBy=multi-user.target or + WantedBy=graphical.target directive. To + activate your socket on boot, make sure to add + WantedBy=sockets.target. Usually, you also + want to make sure that when your service is installed, your + socket is installed too, hence add + Also=foo.socket in your service file + foo.service, for a hypothetical program + foo. + + + + + + Installing Systemd Service Files + + At the build installation time (e.g. make + install during package build), packages are + recommended to install their systemd unit files in the directory + returned by pkg-config systemd + --variable=systemdsystemunitdir (for system services) + or pkg-config systemd + --variable=systemduserunitdir (for user services). + This will make the services available in the system on explicit + request but not activate them automatically during boot. + Optionally, during package installation (e.g. rpm + -i by the administrator), symlinks should be created + in the systemd configuration directories via the + enable command of the + systemctl1 + tool to activate them automatically on boot. + + Packages using + autoconf1 + are recommended to use a configure script + excerpt like the following to determine the + unit installation path during source + configuration: + + PKG_PROG_PKG_CONFIG +AC_ARG_WITH([systemdsystemunitdir], + [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, + [with_systemdsystemunitdir=auto]) +AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ + def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) + + AS_IF([test "x$def_systemdsystemunitdir" = "x"], + [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], + [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) + with_systemdsystemunitdir=no], + [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) +AS_IF([test "x$with_systemdsystemunitdir" != "xno"], + [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) +AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) + + This snippet allows automatic + installation of the unit files on systemd + machines, and optionally allows their + installation even on machines lacking + systemd. (Modification of this snippet for the + user unit directory is left as an exercise for the + reader.) + + Additionally, to ensure that + make distcheck continues to + work, it is recommended to add the following + to the top-level Makefile.am + file in + automake1-based + projects: + + DISTCHECK_CONFIGURE_FLAGS = \ + --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) + + Finally, unit files should be installed in the system with an automake excerpt like the following: + + if HAVE_SYSTEMD +systemdsystemunit_DATA = \ + foobar.socket \ + foobar.service +endif + + In the + rpm8 + .spec file, use snippets like the following + to enable/disable the service during + installation/deinstallation. This makes use of the RPM macros + shipped along systemd. Consult the packaging guidelines of your + distribution for details and the equivalent for other package + managers. + + At the top of the file: + + BuildRequires: systemd +%{?systemd_requires} + + And as scriptlets, further down: + + %post +%systemd_post foobar.service foobar.socket + +%preun +%systemd_preun foobar.service foobar.socket + +%postun +%systemd_postun + + If the service shall be restarted during upgrades, replace + the %postun scriptlet above with the + following: + + %postun +%systemd_postun_with_restart foobar.service + + Note that %systemd_post and + %systemd_preun expect the names of all units + that are installed/removed as arguments, separated by spaces. + %systemd_postun expects no arguments. + %systemd_postun_with_restart expects the + units to restart as arguments. + + To facilitate upgrades from a package version that shipped + only SysV init scripts to a package version that ships both a + SysV init script and a native systemd service file, use a + fragment like the following: + + %triggerun -- foobar < 0.47.11-1 +if /sbin/chkconfig --level 5 foobar ; then + /bin/systemctl --no-reload enable foobar.service foobar.socket >/dev/null 2>&1 || : +fi + + Where 0.47.11-1 is the first package version that includes + the native unit file. This fragment will ensure that the first + time the unit file is installed, it will be enabled if and only + if the SysV init script is enabled, thus making sure that the + enable status is not changed. Note that + chkconfig is a command specific to Fedora + which can be used to check whether a SysV init script is + enabled. Other operating systems will have to use different + commands here. + + + + + Porting Existing Daemons + + Since new-style init systems such as systemd are compatible + with traditional SysV init systems, it is not strictly necessary + to port existing daemons to the new style. However, doing so + offers additional functionality to the daemons as well as + simplifying integration into new-style init systems. + + To port an existing SysV compatible daemon, the following + steps are recommended: + + + If not already implemented, add an optional + command line switch to the daemon to disable daemonization. This + is useful not only for using the daemon in new-style init + systems, but also to ease debugging. + + If the daemon offers interfaces to other + software running on the local system via local + AF_UNIX sockets, consider implementing + socket-based activation (see above). Usually, a minimal patch is + sufficient to implement this: Extend the socket creation in the + daemon code so that + sd_listen_fds3 + is checked for already passed sockets first. If sockets are + passed (i.e. when sd_listen_fds() returns a + positive value), skip the socket creation step and use the + passed sockets. Secondly, ensure that the file system socket + nodes for local AF_UNIX sockets used in the + socket-based activation are not removed when the daemon shuts + down, if sockets have been passed. Third, if the daemon normally + closes all remaining open file descriptors as part of its + initialization, the sockets passed from the init system must be + spared. Since new-style init systems guarantee that no left-over + file descriptors are passed to executed processes, it might be a + good choice to simply skip the closing of all remaining open + file descriptors if sockets are passed. + + Write and install a systemd unit file for the + service (and the sockets if socket-based activation is used, as + well as a path unit file, if the daemon processes a spool + directory), see above for details. + + If the daemon exposes interfaces via D-Bus, + write and install a D-Bus activation file for the service, see + above for details. + + + + + Placing Daemon Data + + It is recommended to follow the general guidelines for + placing package files, as discussed in + file-hierarchy7. + + + + See Also + + systemd1, + sd-daemon3, + sd_listen_fds3, + sd_notify3, + daemon3, + systemd.service5, + file-hierarchy7 + + + + diff --git a/src/manpages/file-hierarchy.xml b/src/manpages/file-hierarchy.xml new file mode 100644 index 0000000000..538a592f8d --- /dev/null +++ b/src/manpages/file-hierarchy.xml @@ -0,0 +1,815 @@ + + + + + + + + + file-hierarchy + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + file-hierarchy + 7 + + + + file-hierarchy + File system hierarchy overview + + + + Description + + Operating systems using the + systemd1 + system and service manager are organized based on a file system + hierarchy inspired by UNIX, more specifically the hierarchy + described in the File + System Hierarchy specification and + hier7. + This manual page describes a more minimal, modernized subset of + these specifications that defines more strictly the suggestions + and restrictions systemd makes on the file system + hierarchy. + + Many of the paths described here can be queried + with the + systemd-path1 + tool. + + + + General Structure + + + + / + The file system root. Usually writable, but + this is not required. Possibly a temporary file system + (tmpfs). Not shared with other hosts + (unless read-only). + + + + /boot + The boot partition used for bringing up the + system. On EFI systems, this is possibly the EFI System + Partition, also see + systemd-gpt-auto-generator8. + This directory is usually strictly local to the host, and + should be considered read-only, except when a new kernel or + boot loader is installed. This directory only exists on + systems that run on physical or emulated hardware that + requires boot loaders. + + + + /etc + System-specific configuration. This directory + may or may not be read-only. Frequently, this directory is + pre-populated with vendor-supplied configuration files, but + applications should not make assumptions about this directory + being fully populated or populated at all, and should fall + back to defaults if configuration is + missing. + + + + /home + The location for normal user's home + directories. Possibly shared with other systems, and never + read-only. This directory should only be used for normal + users, never for system users. This directory and possibly the + directories contained within it might only become available or + writable in late boot or even only after user authentication. + This directory might be placed on limited-functionality + network file systems, hence applications should not assume the + full set of file API is available on this directory. + Applications should generally not reference this directory + directly, but via the per-user $HOME + environment variable, or via the home directory field of the + user database. + + + + /root + The home directory of the root user. The root + user's home directory is located outside of + /home in order to make sure the root user + may log in even without /home being + available and mounted. + + + + /srv + The place to store general server payload, + managed by the administrator. No restrictions are made how + this directory is organized internally. Generally writable, + and possibly shared among systems. This directory might become + available or writable only very late during + boot. + + + + /tmp + The place for small temporary files. This + directory is usually mounted as a tmpfs + instance, and should hence not be used for larger files. (Use + /var/tmp for larger files.) Since the + directory is accessible to other users of the system, it is + essential that this directory is only written to with the + mkstemp3, + mkdtemp3 + and related calls. This directory is usually flushed at + boot-up. Also, files that are not accessed within a certain + time are usually automatically deleted. If applications find + the environment variable $TMPDIR set, they + should prefer using the directory specified in it over + directly referencing /tmp (see + environ7 + and + IEEE + Std 1003.1 for details). + + + + + + + Runtime Data + + + + /run + A tmpfs file system for + system packages to place runtime data in. This directory is + flushed on boot, and generally writable for privileged + programs only. Always writable. + + + + /run/log + Runtime system logs. System components may + place private logs in this directory. Always writable, even + when /var/log might not be accessible + yet. + + + + /run/user + Contains per-user runtime directories, each + usually individually mounted tmpfs + instances. Always writable, flushed at each reboot and when + the user logs out. User code should not reference this + directory directly, but via the + $XDG_RUNTIME_DIR environment variable, as + documented in the XDG + Base Directory Specification. + + + + + + Vendor-supplied Operating System Resources + + + + + /usr + Vendor-supplied operating system resources. + Usually read-only, but this is not required. Possibly shared + between multiple hosts. This directory should not be modified + by the administrator, except when installing or removing + vendor-supplied packages. + + + + /usr/bin + Binaries and executables for user commands + that shall appear in the $PATH search path. + It is recommended not to place binaries in this directory that + are not useful for invocation from a shell (such as daemon + binaries); these should be placed in a subdirectory of + /usr/lib instead. + + + + /usr/include + C and C++ API header files of system + libraries. + + + + /usr/lib + Static, private vendor data that is compatible + with all architectures (though not necessarily + architecture-independent). Note that this includes internal + executables or other binaries that are not regularly invoked + from a shell. Such binaries may be for any architecture + supported by the system. Do not place public libraries in this + directory, use $libdir (see below), + instead. + + + + /usr/lib/arch-id + Location for placing dynamic libraries into, also + called $libdir. The architecture identifier + to use is defined on Multiarch + Architecture Specifiers (Tuples) list. Legacy + locations of $libdir are + /usr/lib, + /usr/lib64. This directory should not be + used for package-specific data, unless this data is + architecture-dependent, too. To query + $libdir for the primary architecture of the + system, invoke: + # systemd-path system-library-arch + + + + + /usr/share + Resources shared between multiple packages, + such as documentation, man pages, time zone information, fonts + and other resources. Usually, the precise location and format + of files stored below this directory is subject to + specifications that ensure interoperability. + + + + /usr/share/doc + Documentation for the operating system or + system packages. + + + + /usr/share/factory/etc + Repository for vendor-supplied default + configuration files. This directory should be populated with + pristine vendor versions of all configuration files that may + be placed in /etc. This is useful to + compare the local configuration of a system with vendor + defaults and to populate the local configuration with + defaults. + + + + /usr/share/factory/var + + Similar to + /usr/share/factory/etc, but for vendor + versions of files in the variable, persistent data directory + /var. + + + + + + + Persistent Variable System Data + + + + /var + Persistent, variable system data. Must be + writable. This directory might be pre-populated with + vendor-supplied data, but applications should be able to + reconstruct necessary files and directories in this + subhierarchy should they be missing, as the system might start + up without this directory being populated. Persistency is + recommended, but optional, to support ephemeral systems. This + directory might become available or writable only very late + during boot. Components that are required to operate during + early boot hence shall not unconditionally rely on this + directory. + + + + /var/cache + Persistent system cache data. System + components may place non-essential data in this directory. + Flushing this directory should have no effect on operation of + programs, except for increased runtimes necessary to rebuild + these caches. + + + + /var/lib + Persistent system data. System components may + place private data in this directory. + + + + /var/log + Persistent system logs. System components may + place private logs in this directory, though it is recommended + to do most logging via the + syslog3 + and + sd_journal_print3 + calls. + + + + /var/spool + Persistent system spool data, such as printer + or mail queues. + + + + /var/tmp + The place for larger and persistent temporary + files. In contrast to /tmp, this directory + is usually mounted from a persistent physical file system and + can thus accept larger files. (Use /tmp + for smaller files.) This directory is generally not flushed at + boot-up, but time-based cleanup of files that have not been + accessed for a certain time is applied. The same security + restrictions as with /tmp apply, and + hence only + mkstemp3, + mkdtemp3 + or similar calls should be used to make use of this directory. + If applications find the environment variable + $TMPDIR set, they should prefer using the + directory specified in it over directly referencing + /var/tmp (see + environ7 + for details). + + + + + + + Virtual Kernel and API File Systems + + + + /dev + The root directory for device nodes. Usually, + this directory is mounted as a devtmpfs + instance, but might be of a different type in + sandboxed/containerized setups. This directory is managed + jointly by the kernel and + systemd-udevd8, + and should not be written to by other components. A number of + special purpose virtual file systems might be mounted below + this directory. + + + + /dev/shm + Place for POSIX shared memory segments, as + created via + shm_open3. + This directory is flushed on boot, and is a + tmpfs file system. Since all users have + write access to this directory, special care should be taken + to avoid name clashes and vulnerabilities. For normal users, + shared memory segments in this directory are usually deleted + when the user logs out. Usually, it is a better idea to use + memory mapped files in /run (for system + programs) or $XDG_RUNTIME_DIR (for user + programs) instead of POSIX shared memory segments, since these + directories are not world-writable and hence not vulnerable to + security-sensitive name clashes. + + + + /proc + A virtual kernel file system exposing the + process list and other functionality. This file system is + mostly an API to interface with the kernel and not a place + where normal files may be stored. For details, see + proc5. + A number of special purpose virtual file systems might be + mounted below this directory. + + + + /proc/sys + A hierarchy below /proc + that exposes a number of kernel tunables. The primary way to + configure the settings in this API file tree is via + sysctl.d5 + files. In sandboxed/containerized setups, this directory is + generally mounted read-only. + + + + /sys + A virtual kernel file system exposing + discovered devices and other functionality. This file system + is mostly an API to interface with the kernel and not a place + where normal files may be stored. In sandboxed/containerized + setups, this directory is generally mounted read-only. A number + of special purpose virtual file systems might be mounted below + this directory. + + + + + + + Compatibility Symlinks + + + + /bin + /sbin + /usr/sbin + + These compatibility symlinks point to + /usr/bin, ensuring that scripts and + binaries referencing these legacy paths correctly find their + binaries. + + + + /lib + + This compatibility symlink points to + /usr/lib, ensuring that programs + referencing this legacy path correctly find their + resources. + + + + /lib64 + + On some architecture ABIs, this compatibility + symlink points to $libdir, ensuring that + binaries referencing this legacy path correctly find their + dynamic loader. This symlink only exists on architectures + whose ABI places the dynamic loader in this + path. + + + + /var/run + + This compatibility symlink points to + /run, ensuring that programs referencing + this legacy path correctly find their runtime + data. + + + + + + + Home Directory + + User applications may want to place files and directories in + the user's home directory. They should follow the following basic + structure. Note that some of these directories are also + standardized (though more weakly) by the XDG + Base Directory Specification. Additional locations for + high-level user resources are defined by xdg-user-dirs. + + + + ~/.cache + + Persistent user cache data. User programs may + place non-essential data in this directory. Flushing this + directory should have no effect on operation of programs, + except for increased runtimes necessary to rebuild these + caches. If an application finds + $XDG_CACHE_HOME set, it should use the + directory specified in it instead of this + directory. + + + + ~/.config + + Application configuration and state. When a + new user is created, this directory will be empty or not exist + at all. Applications should fall back to defaults should their + configuration or state in this directory be missing. If an + application finds $XDG_CONFIG_HOME set, it + should use the directory specified in it instead of this + directory. + + + + ~/.local/bin + + Executables that shall appear in the user's + $PATH search path. It is recommended not to + place executables in this directory that are not useful for + invocation from a shell; these should be placed in a + subdirectory of ~/.local/lib instead. + Care should be taken when placing architecture-dependent + binaries in this place, which might be problematic if the home + directory is shared between multiple hosts with different + architectures. + + + + ~/.local/lib + + Static, private vendor data that is compatible + with all architectures. + + + + ~/.local/lib/arch-id + + Location for placing public dynamic libraries. + The architecture identifier to use is defined on Multiarch + Architecture Specifiers (Tuples) + list. + + + + ~/.local/share + + Resources shared between multiple packages, + such as fonts or artwork. Usually, the precise location and + format of files stored below this directory is subject to + specifications that ensure interoperability. If an application + finds $XDG_DATA_HOME set, it should use the + directory specified in it instead of this + directory. + + + + + + + + Unprivileged Write Access + + Unprivileged processes generally lack write access to most + of the hierarchy. + + The exceptions for normal users are + /tmp, + /var/tmp, + /dev/shm, as well as the home directory + $HOME (usually found below + /home) and the runtime directory + $XDG_RUNTIME_DIR (found below + /run/user) of the user, which are all + writable. + + For unprivileged system processes, only + /tmp, + /var/tmp and + /dev/shm are writable. If an + unprivileged system process needs a private writable directory in + /var or /run, it is + recommended to either create it before dropping privileges in the + daemon code, to create it via + tmpfiles.d5 + fragments during boot, or via the + RuntimeDirectory= directive of service units + (see + systemd.unit5 + for details). + + + + Node Types + + Unix file systems support different types of file nodes, + including regular files, directories, symlinks, character and + block device nodes, sockets and FIFOs. + + It is strongly recommended that /dev is + the only location below which device nodes shall be placed. + Similarly, /run shall be the only location to + place sockets and FIFOs. Regular files, directories and symlinks + may be used in all directories. + + + + System Packages + + Developers of system packages should follow strict rules + when placing their own files in the file system. The following + table lists recommended locations for specific types of files + supplied by the vendor. + + + System Package Vendor Files Locations + + + + + + Directory + Purpose + + + + + /usr/bin + Package executables that shall appear in the $PATH executable search path, compiled for any of the supported architectures compatible with the operating system. It is not recommended to place internal binaries or binaries that are not commonly invoked from the shell in this directory, such as daemon binaries. As this directory is shared with most other packages of the system, special care should be taken to pick unique names for files placed here, that are unlikely to clash with other package's files. + + + /usr/lib/arch-id + Public shared libraries of the package. As above, be careful with using too generic names, and pick unique names for your libraries to place here to avoid name clashes. + + + /usr/lib/package + Private static vendor resources of the package, including private binaries and libraries, or any other kind of read-only vendor data. + + + /usr/lib/arch-id/package + Private other vendor resources of the package that are architecture-specific and cannot be shared between architectures. Note that this generally does not include private executables since binaries of a specific architecture may be freely invoked from any other supported system architecture. + + + /usr/include/package + Public C/C++ APIs of public shared libraries of the package. + + + +
+ + Additional static vendor files may be installed in the + /usr/share hierarchy to the locations + defined by the various relevant specifications. + + During runtime, and for local configuration and state, + additional directories are defined: + + + System Package Variable Files Locations + + + + + + Directory + Purpose + + + + + /etc/package + System-specific configuration for the package. It is recommended to default to safe fallbacks if this configuration is missing, if this is possible. Alternatively, a tmpfiles.d5 fragment may be used to copy or symlink the necessary files and directories from /usr/share/factory during boot, via the L or C directives. + + + /run/package + Runtime data for the package. Packages must be able to create the necessary subdirectories in this tree on their own, since the directory is flushed automatically on boot. Alternatively, a tmpfiles.d5 fragment may be used to create the necessary directories during boot. Alternatively, the RuntimeDirectory= directive of service units may be used (see systemd.unit5 for details.) + + + /run/log/package + Runtime log data for the package. As above, the package needs to make sure to create this directory if necessary, as it will be flushed on every boot. + + + /var/cache/package + Persistent cache data of the package. If this directory is flushed, the application should work correctly on next invocation, though possibly slowed down due to the need to rebuild any local cache files. The application must be capable of recreating this directory should it be missing and necessary. + + + /var/lib/package + Persistent private data of the package. This is the primary place to put persistent data that does not fall into the other categories listed. Packages should be able to create the necessary subdirectories in this tree on their own, since the directory might be missing on boot. Alternatively, a tmpfiles.d5 fragment may be used to create the necessary directories during boot. + + + /var/log/package + Persistent log data of the package. As above, the package should make sure to create this directory if necessary, as it might be missing. + + + /var/spool/package + Persistent spool/queue data of the package. As above, the package should make sure to create this directory if necessary, as it might be missing. + + + +
+
+ + + User Packages + + Programs running in user context should follow strict rules + when placing their own files in the user's home directory. The + following table lists recommended locations in the home directory + for specific types of files supplied by the vendor if the + application is installed in the home directory. (Note, however, + that user applications installed system-wide should follow the + rules outlined above regarding placing vendor files.) + + + User Package Vendor File Locations + + + + + + Directory + Purpose + + + + + ~/.local/bin + Package executables that shall appear in the $PATH executable search path. It is not recommended to place internal executables or executables that are not commonly invoked from the shell in this directory, such as daemon executables. As this directory is shared with most other packages of the user, special care should be taken to pick unique names for files placed here, that are unlikely to clash with other package's files. + + + ~/.local/lib/arch-id + Public shared libraries of the package. As above, be careful with using too generic names, and pick unique names for your libraries to place here to avoid name clashes. + + + ~/.local/lib/package + Private, static vendor resources of the package, compatible with any architecture, or any other kind of read-only vendor data. + + + ~/.local/lib/arch-id/package + Private other vendor resources of the package that are architecture-specific and cannot be shared between architectures. + + + +
+ + Additional static vendor files may be installed in the + ~/.local/share hierarchy to the locations + defined by the various relevant specifications. + + During runtime, and for local configuration and state, + additional directories are defined: + + + User Package Variable File Locations + + + + + + Directory + Purpose + + + + + ~/.config/package + User-specific configuration and state for the package. It is required to default to safe fallbacks if this configuration is missing. + + + $XDG_RUNTIME_DIR/package + User runtime data for the package. + + + ~/.cache/package + Persistent cache data of the package. If this directory is flushed, the application should work correctly on next invocation, though possibly slowed down due to the need to rebuild any local cache files. The application must be capable of recreating this directory should it be missing and necessary. + + + +
+
+ + + See Also + + systemd1, + hier7, + systemd-path1, + systemd-gpt-auto-generator8, + sysctl.d5, + tmpfiles.d5, + pkg-config1, + systemd.unit5 + + + +
diff --git a/src/manpages/hostname.xml b/src/manpages/hostname.xml new file mode 100644 index 0000000000..8a4c0d5ac0 --- /dev/null +++ b/src/manpages/hostname.xml @@ -0,0 +1,98 @@ + + + + + + + + hostname + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + hostname + 5 + + + + hostname + Local hostname configuration file + + + + /etc/hostname + + + + Description + + The /etc/hostname file configures the + name of the local system that is set during boot using the + sethostname2 + system call. It should contain a single newline-terminated + hostname string. Comments (lines starting with a `#') are ignored. + The hostname may be a free-form string up to 64 characters in length; + however, it is recommended that it consists only of 7-bit ASCII lower-case + characters and no spaces or dots, and limits itself to the format allowed + for DNS domain name labels, even though this is not a strict + requirement. + + You may use + hostnamectl1 + to change the value of this file during runtime from the command + line. Use + systemd-firstboot1 + to initialize it on mounted (but not booted) system images. + + + + History + + The simple configuration file format of + /etc/hostname originates from Debian + GNU/Linux. + + + + See Also + + systemd1, + sethostname2, + hostname1, + hostname7, + machine-id5, + machine-info5, + hostnamectl1, + systemd-hostnamed.service8, + systemd-firstboot1 + + + + diff --git a/src/manpages/locale.conf.xml b/src/manpages/locale.conf.xml new file mode 100644 index 0000000000..2fe731113a --- /dev/null +++ b/src/manpages/locale.conf.xml @@ -0,0 +1,152 @@ + + + + + + + + locale.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + locale.conf + 5 + + + + locale.conf + Configuration file for locale settings + + + + /etc/locale.conf + + + + Description + + The /etc/locale.conf file configures + system-wide locale settings. It is read at early boot by + systemd1. + + The basic file format of locale.conf is + a newline-separated list of environment-like shell-compatible + variable assignments. It is possible to source the configuration + from shell scripts, however, beyond mere variable assignments, no + shell features are supported, allowing applications to read the + file without implementing a shell compatible execution + engine. + + Note that the kernel command line options + locale.LANG=, + locale.LANGUAGE=, + locale.LC_CTYPE=, + locale.LC_NUMERIC=, + locale.LC_TIME=, + locale.LC_COLLATE=, + locale.LC_MONETARY=, + locale.LC_MESSAGES=, + locale.LC_PAPER=, + locale.LC_NAME=, + locale.LC_ADDRESS=, + locale.LC_TELEPHONE=, + locale.LC_MEASUREMENT=, + locale.LC_IDENTIFICATION= may be + used to override the locale settings at boot. + + The locale settings configured in + /etc/locale.conf are system-wide and are + inherited by every service or user, unless overridden or unset by + individual programs or individual users. + + Depending on the operating system, other configuration files + might be checked for locale configuration as well, however only as + fallback. + + localectl1 + may be used to alter the settings in this file during runtime from + the command line. Use + systemd-firstboot1 + to initialize them on mounted (but not booted) system + images. + + + + Options + + The following locale settings may be set using + /etc/locale.conf: + LANG=, + LANGUAGE=, + LC_CTYPE=, + LC_NUMERIC=, + LC_TIME=, + LC_COLLATE=, + LC_MONETARY=, + LC_MESSAGES=, + LC_PAPER=, + LC_NAME=, + LC_ADDRESS=, + LC_TELEPHONE=, + LC_MEASUREMENT=, + LC_IDENTIFICATION=. + Note that LC_ALL may not be configured in this + file. For details about the meaning and semantics of these + settings, refer to + locale7. + + + + Example + + + German locale with English messages + + /etc/locale.conf: + + LANG=de_DE.UTF-8 +LC_MESSAGES=en_US.UTF-8 + + + + + + See Also + + systemd1, + locale7, + localectl1, + systemd-localed.service8, + systemd-firstboot1 + + + + diff --git a/src/manpages/localtime.xml b/src/manpages/localtime.xml new file mode 100644 index 0000000000..2827da6e93 --- /dev/null +++ b/src/manpages/localtime.xml @@ -0,0 +1,103 @@ + + + + + + + + localtime + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + Developer + Shawn + Landden + shawnlandden@gmail.com + + + + + + localtime + 5 + + + + localtime + Local timezone configuration file + + + + /etc/localtime -> ../usr/share/zoneinfo/… + + + + Description + + The /etc/localtime file configures the + system-wide timezone of the local system that is used by + applications for presentation to the user. It should be an + absolute or relative symbolic link pointing to + /usr/share/zoneinfo/, followed by a timezone + identifier such as Europe/Berlin or + Etc/UTC. The resulting link should lead to the + corresponding binary + tzfile5 + timezone data for the configured timezone. + + Because the timezone identifier is extracted from the + symlink target name of /etc/localtime, this + file may not be a normal file or hardlink. + + The timezone may be overridden for individual programs by + using the $TZ environment variable. See + environ7. + + You may use + timedatectl1 + to change the settings of this file from the command line during + runtime. Use + systemd-firstboot1 + to initialize the time zone on mounted (but not booted) system + images. + + + + See Also + + systemd1, + tzset3, + localtime3, + timedatectl1, + systemd-timedated.service8, + systemd-firstboot1 + + + + diff --git a/src/manpages/machine-id.xml b/src/manpages/machine-id.xml new file mode 100644 index 0000000000..d318ec54ec --- /dev/null +++ b/src/manpages/machine-id.xml @@ -0,0 +1,146 @@ + + + + + + + + machine-id + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + machine-id + 5 + + + + machine-id + Local machine ID configuration file + + + + /etc/machine-id + + + + Description + + The /etc/machine-id file contains the + unique machine ID of the local system that is set during + installation. The machine ID is a single newline-terminated, + hexadecimal, 32-character, lowercase machine ID string. When + decoded from hexadecimal, this corresponds with a 16-byte/128-bit + string. + + The machine ID is usually generated from a random source + during system installation and stays constant for all subsequent + boots. Optionally, for stateless systems, it is generated during + runtime at early boot if it is found to be empty. + + The machine ID does not change based on user configuration + or when hardware is replaced. + + This machine ID adheres to the same format and logic as the + D-Bus machine ID. + + Programs may use this ID to identify the host with a + globally unique ID in the network, which does not change even if + the local network configuration changes. Due to this and its + greater length, it is a more useful replacement for the + gethostid3 + call that POSIX specifies. + + The + systemd-machine-id-setup1 + tool may be used by installer tools to initialize the machine ID + at install time. Use + systemd-firstboot1 + to initialize it on mounted (but not booted) system images. + + The machine-id may also be set, for example when network + booting, by setting the systemd.machine_id= + kernel command line parameter or passing the option + to systemd. A machine-id may not + be set to all zeros. + + + + Relation to OSF UUIDs + + Note that the machine ID historically is not an OSF UUID as + defined by RFC + 4122, nor a Microsoft GUID; however, starting with systemd + v30, newly generated machine IDs do qualify as v4 UUIDs. + + In order to maintain compatibility with existing + installations, an application requiring a UUID should decode the + machine ID, and then apply the following operations to turn it + into a valid OSF v4 UUID. With id being an + unsigned character array: + + /* Set UUID version to 4 --- truly random generation */ +id[6] = (id[6] & 0x0F) | 0x40; +/* Set the UUID variant to DCE */ +id[8] = (id[8] & 0x3F) | 0x80; + + (This code is inspired by + generate_random_uuid() of + drivers/char/random.c from the Linux kernel + sources.) + + + + + History + + The simple configuration file format of + /etc/machine-id originates in the + /var/lib/dbus/machine-id file introduced by + D-Bus. In fact, this latter file might be a symlink to + /etc/machine-id. + + + + See Also + + systemd1, + systemd-machine-id-setup1, + gethostid3, + hostname5, + machine-info5, + os-release5, + sd-id1283, + sd_id128_get_machine3, + systemd-firstboot1 + + + + diff --git a/src/manpages/machine-info.xml b/src/manpages/machine-info.xml new file mode 100644 index 0000000000..351133670b --- /dev/null +++ b/src/manpages/machine-info.xml @@ -0,0 +1,185 @@ + + + + + + + + machine-info + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + machine-info + 5 + + + + machine-info + Local machine information file + + + + /etc/machine-info + + + + Description + + The /etc/machine-info file contains + machine metadata. + + The basic file format of machine-info + is a newline-separated list of environment-like shell-compatible + variable assignments. It is possible to source the configuration + from shell scripts, however, beyond mere variable assignments no + shell features are supported, allowing applications to read the + file without implementing a shell compatible execution + engine. + + /etc/machine-info contains metadata + about the machine that is set by the user or administrator. + + Depending on the operating system other configuration files + might be checked for machine information as well, however only as + fallback. + + You may use + hostnamectl1 + to change the settings of this file from the command line. + + + + Options + + The following machine metadata parameters may be set using + /etc/machine-info: + + + + + PRETTY_HOSTNAME= + + A pretty human-readable UTF-8 machine + identifier string. This should contain a name like + Lennart's Laptop which is useful to present + to the user and does not suffer by the syntax limitations of + internet domain names. If possible, the internet hostname as + configured in /etc/hostname should be + kept similar to this one. Example: if this value is + Lennart's Computer an Internet hostname of + lennarts-computer might be a good choice. + If this parameter is not set, an application should fall back + to the Internet host name for presentation + purposes. + + + + ICON_NAME= + + An icon identifying this machine according to + the XDG + Icon Naming Specification. If this parameter is not + set, an application should fall back to + computer or a similar icon + name. + + + + CHASSIS= + + The chassis type. Currently, the following + chassis types are defined: + desktop, + laptop, + server, + tablet, + handset, + watch, and + embedded, + as well as the special chassis types + vm and + container for + virtualized systems that lack an immediate physical chassis. + Note that many systems allow detection of the chassis type + automatically (based on firmware information or suchlike). + This setting (if set) shall take precedence over automatically + detected information and is useful to override misdetected + configuration or to manually configure the chassis type where + automatic detection is not available. + + + + DEPLOYMENT= + + Describes the system deployment environment. + One of the following is suggested: + development, + integration, + staging, + production. + + + + + LOCATION= + + Describes the system location if applicable + and known. Takes a human-friendly, free-form string. This may + be as generic as Berlin, Germany or as + specific as Left Rack, 2nd Shelf. + + + + + + + Example + + PRETTY_HOSTNAME="Lennart's Tablet" +ICON_NAME=computer-tablet +CHASSIS=tablet +DEPLOYMENT=production + + + + See Also + + systemd1, + os-release5, + hostname5, + machine-id5, + hostnamectl1, + systemd-hostnamed.service8 + + + + diff --git a/src/manpages/os-release.xml b/src/manpages/os-release.xml new file mode 100644 index 0000000000..2811f434c5 --- /dev/null +++ b/src/manpages/os-release.xml @@ -0,0 +1,378 @@ + + + + + + + + os-release + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + os-release + 5 + + + + os-release + Operating system identification + + + + /etc/os-release + /usr/lib/os-release + + + + Description + + The /etc/os-release and + /usr/lib/os-release files contain operating + system identification data. + + The basic file format of os-release is + a newline-separated list of environment-like shell-compatible + variable assignments. It is possible to source the configuration + from shell scripts, however, beyond mere variable assignments, no + shell features are supported (this means variable expansion is + explicitly not supported), allowing applications to read the file + without implementing a shell compatible execution engine. Variable + assignment values must be enclosed in double or single quotes if + they include spaces, semicolons or other special characters + outside of A–Z, a–z, 0–9. Shell special characters ("$", quotes, + backslash, backtick) must be escaped with backslashes, following + shell style. All strings should be in UTF-8 format, and + non-printable characters should not be used. It is not supported + to concatenate multiple individually quoted strings. Lines + beginning with "#" shall be ignored as comments. + + The file /etc/os-release takes + precedence over /usr/lib/os-release. + Applications should check for the former, and exclusively use its + data if it exists, and only fall back to + /usr/lib/os-release if it is missing. + Applications should not read data from both files at the same + time. /usr/lib/os-release is the recommended + place to store OS release information as part of vendor trees. + /etc/os-release should be a relative symlink + to /usr/lib/os-release, to provide + compatibility with applications only looking at + /etc. A relative symlink instead of an + absolute symlink is necessary to avoid breaking the link in a + chroot or initrd environment such as dracut. + + os-release contains data that is + defined by the operating system vendor and should generally not be + changed by the administrator. + + As this file only encodes names and identifiers it should + not be localized. + + The /etc/os-release and + /usr/lib/os-release files might be symlinks + to other files, but it is important that the file is available + from earliest boot on, and hence must be located on the root file + system. + + For a longer rationale for os-release + please refer to the Announcement of /etc/os-release. + + + + Options + + The following OS identifications parameters may be set using + os-release: + + + + + NAME= + + A string identifying the operating system, + without a version component, and suitable for presentation to + the user. If not set, defaults to + NAME=GNU/Linux. Example: + NAME=BLAG or NAME="gNewSense + GNU/Linux". + + + + VERSION= + + A string identifying the operating system + version, excluding any OS name information, possibly including + a release code name, and suitable for presentation to the + user. This field is optional. Example: + VERSION=210k or VERSION="210k + (Spartakus)". + + + + ID= + + A lower-case string (no spaces or other + characters outside of 0–9, a–z, ".", "_" and "-") identifying + the operating system, excluding any version information and + suitable for processing by scripts or usage in generated + filenames. If not set, defaults to + ID=gnu-linux. Example: + ID=blag or + ID=gnewsense. + + + + ID_LIKE= + + A space-separated list of operating system + identifiers in the same syntax as the ID= + setting. It should list identifiers of operating systems that + are closely related to the local operating system in regards + to packaging and programming interfaces, for example listing + one or more OS identifiers the local OS is a derivative from. + An OS should generally only list other OS identifiers it + itself is a derivative of, and not any OSes that are derived + from it, though symmetric relationships are possible. Build + scripts and similar should check this variable if they need to + identify the local operating system and the value of + ID= is not recognized. Operating systems + should be listed in order of how closely the local operating + system relates to the listed ones, starting with the closest. + This field is optional. Example: for an operating system with + ID=blag, an assignment of + ID_LIKE="rhel fedora" would be appropriate. + For an operating system with ID=gnewsense, an + assignment of ID_LIKE=debian is + appropriate. + + + + VERSION_CODENAME= + + + A lower-case string (no spaces or other characters outside of + 0–9, a–z, ".", "_" and "-") identifying the operating system + release code name, excluding any OS name information or + release version, and suitable for processing by scripts or + usage in generated filenames. This field is optional and may + not be implemented on all systems. + Examples: + VERSION_CODENAME=buster, + VERSION_CODENAME=xenial + + + + + VERSION_ID= + + A lower-case string (mostly numeric, no spaces + or other characters outside of 0–9, a–z, ".", "_" and "-") + identifying the operating system version, excluding any OS + name information or release code name, and suitable for + processing by scripts or usage in generated filenames. This + field is optional. Example: VERSION_ID=210k + or VERSION_ID=7.0. + + + + PRETTY_NAME= + + A pretty operating system name in a format + suitable for presentation to the user. May or may not contain + a release code name or OS version of some kind, as suitable. + If not set, defaults to + PRETTY_NAME="GNU/Linux". Example: + PRETTY_NAME="BLAG 210k + (Spartakus)". + + + + ANSI_COLOR= + + A suggested presentation color when showing + the OS name on the console. This should be specified as string + suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code + for setting graphical rendition. This field is optional. + Example: ANSI_COLOR="0;31" for red, or + ANSI_COLOR="1;34" for light + blue. + + + + CPE_NAME= + + A CPE name for the operating system, in URI + binding syntax, following the + Common + Platform Enumeration Specification as proposed by the + NIST. This field is optional. Example: + CPE_NAME="cpe:/o:blagblagblag:blag:210k" + + + + + HOME_URL= + SUPPORT_URL= + BUG_REPORT_URL= + PRIVACY_POLICY_URL= + + Links to resources on the Internet related the + operating system. HOME_URL= should refer to + the homepage of the operating system, or alternatively some + homepage of the specific version of the operating system. + SUPPORT_URL= should refer to the main + support page for the operating system, if there is any. This + is primarily intended for operating systems which vendors + provide support for. BUG_REPORT_URL= should + refer to the main bug reporting page for the operating system, + if there is any. This is primarily intended for operating + systems that rely on community QA. + PRIVACY_POLICY_URL= should refer to the + main privacy policy page for the operation system, if there is + any. These settings are optional, and providing only some of + these settings is common. These URLs are intended to be + exposed in "About this system" UIs behind links with captions + such as "About this Operating System", "Obtain Support", + "Report a Bug", or "Privacy Policy". The values should be in + RFC3986 + format, and should be http: or + https: URLs, and possibly + mailto: or tel:. Only + one URL shall be listed in each setting. If multiple resources + need to be referenced, it is recommended to provide an online + landing page linking all available resources. Examples: + HOME_URL="https://www.blagblagblag.org/" and + BUG_REPORT_URL="https://blag.fsf.org/" + + + + BUILD_ID= + + A string uniquely identifying the system image + used as the origin for a distribution (it is not updated with + system updates). The field can be identical between different + VERSION_IDs as BUILD_ID is an only a unique identifier to a + specific version. Distributions that release each update as a + new version would only need to use VERSION_ID as each build is + already distinct based on the VERSION_ID. This field is + optional. Example: BUILD_ID="2013-03-20.3" + or BUILD_ID=201303203. + + + + + + VARIANT= + + + A string identifying a specific variant or edition of the + operating system suitable for presentation to the user. This + field may be used to inform the user that the configuration of + this system is subject to a specific divergent set of rules or + default configuration settings. This field is optional and may + not be implemented on all systems. + Examples: + VARIANT="Server Edition", + VARIANT="Smart Refrigerator Edition" + Note: this field is for display purposes only. The + VARIANT_ID field should be used for making + programmatic decisions. + + + + + VARIANT_ID= + + + A lower-case string (no spaces or other characters outside of + 0–9, a–z, ".", "_" and "-"), identifying a specific variant or + edition of the operating system. This may be interpreted by + other packages in order to determine a divergent default + configuration. This field is optional and may not be + implemented on all systems. + Examples: + VARIANT_ID=server, + VARIANT_ID=embedded + + + + + + If you are reading this file from C code or a shell script + to determine the OS or a specific version of it, use the + ID and VERSION_ID fields, + possibly with ID_LIKE as fallback for + ID. When looking for an OS identification + string for presentation to the user use the + PRETTY_NAME field. + + Note that operating system vendors may choose not to provide + version information, for example to accommodate for rolling + releases. In this case, VERSION and + VERSION_ID may be unset. Applications should + not rely on these fields to be set. + + Operating system vendors may extend the file + format and introduce new fields. It is highly + recommended to prefix new fields with an OS specific + name in order to avoid name clashes. Applications + reading this file must ignore unknown fields. Example: + DEBIAN_BTS="debbugs://bugs.gnewsense.org/" + + + + Example + + NAME=Parabola +VERSION="rolling-release" +ID=parabola +ID_LIKE=arch +VERSION_ID=rolling-release +PRETTY_NAME="Parabola GNU/Linux-libre" +ANSI_COLOR="1;35" +CPE_NAME="cpe:/o:parabola:parabola:rolling-release" +HOME_URL="https://www.parabola.nu/" +BUG_REPORT_URL="https://labs.parabola.nu/" + + + + See Also + + systemd1, + lsb_release1, + hostname5, + machine-id5, + machine-info5 + + + + diff --git a/src/modules-load/Makefile b/src/modules-load/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/modules-load/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c deleted file mode 100644 index f75015d8c3..0000000000 --- a/src/modules-load/modules-load.c +++ /dev/null @@ -1,283 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "conf-files.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "proc-cmdline.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static char **arg_proc_cmdline_modules = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d"); - -static void systemd_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) { - - DISABLE_WARNING_FORMAT_NONLITERAL; - log_internalv(priority, 0, file, line, fn, format, args); - REENABLE_WARNING; -} - -static int add_modules(const char *p) { - _cleanup_strv_free_ char **k = NULL; - - k = strv_split(p, ","); - if (!k) - return log_oom(); - - if (strv_extend_strv(&arg_proc_cmdline_modules, k, true) < 0) - return log_oom(); - - return 0; -} - -static int parse_proc_cmdline_item(const char *key, const char *value) { - int r; - - if (STR_IN_SET(key, "modules-load", "rd.modules-load") && value) { - r = add_modules(value); - if (r < 0) - return r; - } - - return 0; -} - -static int load_module(struct kmod_ctx *ctx, const char *m) { - const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST; - struct kmod_list *itr, *modlist = NULL; - int r = 0; - - log_debug("load: %s", m); - - r = kmod_module_new_from_lookup(ctx, m, &modlist); - if (r < 0) - return log_error_errno(r, "Failed to lookup alias '%s': %m", m); - - if (!modlist) { - log_error("Failed to find module '%s'", m); - return -ENOENT; - } - - kmod_list_foreach(itr, modlist) { - struct kmod_module *mod; - int state, err; - - mod = kmod_module_get_module(itr); - state = kmod_module_get_initstate(mod); - - switch (state) { - case KMOD_MODULE_BUILTIN: - log_info("Module '%s' is builtin", kmod_module_get_name(mod)); - break; - - case KMOD_MODULE_LIVE: - log_debug("Module '%s' is already loaded", kmod_module_get_name(mod)); - break; - - default: - err = kmod_module_probe_insert_module(mod, probe_flags, - NULL, NULL, NULL, NULL); - - if (err == 0) - log_info("Inserted module '%s'", kmod_module_get_name(mod)); - else if (err == KMOD_PROBE_APPLY_BLACKLIST) - log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); - else { - log_error_errno(err, "Failed to insert '%s': %m", kmod_module_get_name(mod)); - r = err; - } - } - - kmod_module_unref(mod); - } - - kmod_module_unref_list(modlist); - - return r; -} - -static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(ctx); - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open %s, ignoring: %m", path); - } - - log_debug("apply: %s", path); - for (;;) { - char line[LINE_MAX], *l; - int k; - - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - l = strstrip(line); - if (!*l) - continue; - if (strchr(COMMENTS "\n", *l)) - continue; - - k = load_module(ctx, l); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Loads statically configured kernel modules.\n\n" - " -h --help Show this help\n" - " --version Show package version\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, k; - struct kmod_ctx *ctx; - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - ctx = kmod_new(NULL, NULL); - if (!ctx) { - log_error("Failed to allocate memory for kmod."); - goto finish; - } - - kmod_load_resources(ctx); - kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = apply_file(ctx, argv[i], false); - if (k < 0 && r == 0) - r = k; - } - - } else { - _cleanup_strv_free_ char **files = NULL; - char **fn, **i; - - STRV_FOREACH(i, arg_proc_cmdline_modules) { - k = load_module(ctx, *i); - if (k < 0 && r == 0) - r = k; - } - - k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (k < 0) { - log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); - if (r == 0) - r = k; - goto finish; - } - - STRV_FOREACH(fn, files) { - k = apply_file(ctx, *fn, true); - if (k < 0 && r == 0) - r = k; - } - } - -finish: - kmod_unref(ctx); - strv_free(arg_proc_cmdline_modules); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/network/.gitignore b/src/network/.gitignore deleted file mode 100644 index aca55206b7..0000000000 --- a/src/network/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/networkd-network-gperf.c -/networkd-netdev-gperf.c -/networkd-gperf.c diff --git a/src/network/Makefile b/src/network/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/network/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/network/networkctl.c b/src/network/networkctl.c deleted file mode 100644 index d2df9b7560..0000000000 --- a/src/network/networkctl.c +++ /dev/null @@ -1,1142 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-device.h" -#include "sd-hwdb.h" -#include "sd-lldp.h" -#include "sd-netlink.h" -#include "sd-network.h" - -#include "alloc-util.h" -#include "arphrd-list.h" -#include "device-util.h" -#include "ether-addr-util.h" -#include "fd-util.h" -#include "hwdb-util.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 "sparse-endian.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "strxcpyx.h" -#include "terminal-util.h" -#include "util.h" -#include "verbs.h" - -static bool arg_no_pager = false; -static bool arg_legend = true; -static bool arg_all = false; - -static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) { - const char *t; - char *p; - - assert(ret); - - if (iftype == ARPHRD_ETHER && d) { - const char *devtype = NULL, *id = NULL; - /* WLANs have iftype ARPHRD_ETHER, but we want - * to show a more useful type string for - * them */ - - (void) sd_device_get_devtype(d, &devtype); - - if (streq_ptr(devtype, "wlan")) - id = "wlan"; - else if (streq_ptr(devtype, "wwan")) - id = "wwan"; - - if (id) { - p = strdup(id); - if (!p) - return -ENOMEM; - - *ret = p; - return 1; - } - } - - t = arphrd_to_name(iftype); - if (!t) { - *ret = NULL; - return 0; - } - - p = strdup(t); - if (!p) - return -ENOMEM; - - ascii_strlower(p); - *ret = p; - - return 0; -} - -static void operational_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "routable")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "degraded")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -static void setup_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "configured")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "configuring")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { - *on = ansi_highlight_red(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -typedef struct LinkInfo { - char name[IFNAMSIZ+1]; - int ifindex; - unsigned short iftype; - struct ether_addr mac_address; - uint32_t mtu; - - bool has_mac_address:1; - bool has_mtu:1; -} LinkInfo; - -static int link_info_compare(const void *a, const void *b) { - const LinkInfo *x = a, *y = b; - - return x->ifindex - y->ifindex; -} - -static int decode_link(sd_netlink_message *m, LinkInfo *info) { - const char *name; - uint16_t type; - int r; - - assert(m); - assert(info); - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) - return r; - - if (type != RTM_NEWLINK) - return 0; - - r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex); - if (r < 0) - return r; - - r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name); - if (r < 0) - return r; - - r = sd_rtnl_message_link_get_type(m, &info->iftype); - if (r < 0) - return r; - - strscpy(info->name, sizeof info->name, name); - - info->has_mac_address = - sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && - memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; - - info->has_mtu = - sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) && - info->mtu > 0; - - return 1; -} - -static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) { - _cleanup_free_ LinkInfo *links = NULL; - char **i; - size_t c = 0; - int r; - - assert(rtnl); - assert(ret); - - links = new(LinkInfo, strv_length(l)); - if (!links) - return log_oom(); - - STRV_FOREACH(i, l) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - int ifindex; - - if (parse_ifindex(*i, &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); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i); - } - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to request link: %m"); - - r = decode_link(reply, links + c); - if (r < 0) - return r; - if (r > 0) - c++; - } - - qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); - - *ret = links; - links = NULL; - - return (int) c; -} - -static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_free_ LinkInfo *links = NULL; - size_t allocated = 0, c = 0; - sd_netlink_message *i; - int r; - - assert(rtnl); - assert(ret); - - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); - - for (i = reply; i; i = sd_netlink_message_next(i)) { - if (!GREEDY_REALLOC(links, allocated, c+1)) - return -ENOMEM; - - r = decode_link(i, links + c); - if (r < 0) - return r; - if (r > 0) - c++; - } - - qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); - - *ret = links; - links = NULL; - - return (int) c; -} - -static int list_links(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_free_ LinkInfo *links = NULL; - int c, i, r; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - if (argc > 1) - c = acquire_link_info_strv(rtnl, argv + 1, &links); - else - c = acquire_link_info_all(rtnl, &links); - if (c < 0) - return c; - - pager_open(arg_no_pager, false); - - if (arg_legend) - printf("%3s %-16s %-18s %-11s %-10s\n", - "IDX", - "LINK", - "TYPE", - "OPERATIONAL", - "SETUP"); - - for (i = 0; i < c; i++) { - _cleanup_free_ char *setup_state = NULL, *operational_state = 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)]; - _cleanup_free_ char *t = NULL; - - (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state); - operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - - r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state); - if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ - setup_state = strdup("unmanaged"); - setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - - xsprintf(devid, "n%i", links[i].ifindex); - (void) sd_device_new_from_device_id(&d, devid); - - (void) link_get_type_string(links[i].iftype, d, &t); - - printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n", - links[i].ifindex, links[i].name, strna(t), - on_color_operational, strna(operational_state), off_color_operational, - on_color_setup, strna(setup_state), off_color_setup); - } - - if (arg_legend) - printf("\n%i links listed.\n", c); - - return 0; -} - -/* IEEE Organizationally Unique Identifier vendor string */ -static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) { - const char *description; - char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc; - int r; - - assert(ret); - - if (!hwdb) - return -EINVAL; - - if (!mac) - return -EINVAL; - - /* skip commonly misused 00:00:00 (Xerox) prefix */ - if (memcmp(mac, "\0\0\0", 3) == 0) - return -EINVAL; - - 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) - return r; - - desc = strdup(description); - if (!desc) - return -ENOMEM; - - *ret = desc; - - return 0; -} - -static int get_gateway_description( - sd_netlink *rtnl, - sd_hwdb *hwdb, - int ifindex, - int family, - union in_addr_union *gateway, - char **gateway_description) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *m; - int r; - - assert(rtnl); - assert(ifindex >= 0); - assert(family == AF_INET || family == AF_INET6); - assert(gateway); - assert(gateway_description); - - r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (m = reply; m; m = sd_netlink_message_next(m)) { - union in_addr_union gw = {}; - struct ether_addr mac = {}; - uint16_t type; - int ifi, fam; - - r = sd_netlink_message_get_errno(m); - if (r < 0) { - log_error_errno(r, "got error: %m"); - continue; - } - - r = sd_netlink_message_get_type(m, &type); - if (r < 0) { - log_error_errno(r, "could not get type: %m"); - continue; - } - - if (type != RTM_NEWNEIGH) { - log_error("type is not RTM_NEWNEIGH"); - continue; - } - - r = sd_rtnl_message_neigh_get_family(m, &fam); - if (r < 0) { - log_error_errno(r, "could not get family: %m"); - continue; - } - - if (fam != family) { - log_error("family is not correct"); - continue; - } - - r = sd_rtnl_message_neigh_get_ifindex(m, &ifi); - if (r < 0) { - log_error_errno(r, "could not get ifindex: %m"); - continue; - } - - if (ifindex > 0 && ifi != ifindex) - continue; - - switch (fam) { - case AF_INET: - r = sd_netlink_message_read_in_addr(m, NDA_DST, &gw.in); - if (r < 0) - continue; - - break; - case AF_INET6: - r = sd_netlink_message_read_in6_addr(m, NDA_DST, &gw.in6); - if (r < 0) - continue; - - break; - default: - continue; - } - - if (!in_addr_equal(fam, &gw, gateway)) - continue; - - r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac); - if (r < 0) - continue; - - r = ieee_oui(hwdb, &mac, gateway_description); - if (r < 0) - continue; - - return 0; - } - - return -ENODATA; -} - -static int dump_gateways( - sd_netlink *rtnl, - sd_hwdb *hwdb, - const char *prefix, - int ifindex) { - _cleanup_free_ struct local_address *local = NULL; - int r, n, i; - - assert(rtnl); - assert(prefix); - - n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); - if (n < 0) - return n; - - for (i = 0; i < n; i++) { - _cleanup_free_ char *gateway = NULL, *description = NULL; - - r = in_addr_to_string(local[i].family, &local[i].address, &gateway); - if (r < 0) - return r; - - r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description); - if (r < 0) - log_debug_errno(r, "Could not get description of gateway: %m"); - - printf("%*s%s", - (int) strlen(prefix), - i == 0 ? prefix : "", - gateway); - - if (description) - printf(" (%s)", description); - - /* Show interface name for the entry if we show - * entries for all interfaces */ - if (ifindex <= 0) { - char name[IF_NAMESIZE+1]; - - if (if_indextoname(local[i].ifindex, name)) { - fputs(" on ", stdout); - fputs(name, stdout); - } else - printf(" on %%%i", local[i].ifindex); - } - - fputc('\n', stdout); - } - - return 0; -} - -static int dump_addresses( - sd_netlink *rtnl, - const char *prefix, - int ifindex) { - - _cleanup_free_ struct local_address *local = NULL; - int r, n, i; - - assert(rtnl); - assert(prefix); - - n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); - if (n < 0) - return n; - - for (i = 0; i < n; i++) { - _cleanup_free_ char *pretty = NULL; - - r = in_addr_to_string(local[i].family, &local[i].address, &pretty); - if (r < 0) - return r; - - printf("%*s%s", - (int) strlen(prefix), - i == 0 ? prefix : "", - pretty); - - if (ifindex <= 0) { - char name[IF_NAMESIZE+1]; - - if (if_indextoname(local[i].ifindex, name)) { - fputs(" on ", stdout); - fputs(name, stdout); - } else - printf(" on %%%i", local[i].ifindex); - } - - fputc('\n', stdout); - } - - return 0; -} - -static int open_lldp_neighbors(int ifindex, FILE **ret) { - _cleanup_free_ char *p = NULL; - FILE *f; - - if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0) - return -ENOMEM; - - f = fopen(p, "re"); - if (!f) - return -errno; - - *ret = f; - return 0; -} - -static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) { - _cleanup_free_ void *raw = NULL; - size_t l; - le64_t u; - int r; - - assert(f); - assert(ret); - - l = fread(&u, 1, sizeof(u), f); - if (l == 0 && feof(f)) - return 0; - if (l != sizeof(u)) - return -EBADMSG; - - raw = new(uint8_t, le64toh(u)); - if (!raw) - return -ENOMEM; - - if (fread(raw, 1, le64toh(u), f) != le64toh(u)) - return -EBADMSG; - - r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u)); - if (r < 0) - return r; - - return 1; -} - -static int dump_lldp_neighbors(const char *prefix, int ifindex) { - _cleanup_fclose_ FILE *f = NULL; - int r, c = 0; - - assert(prefix); - assert(ifindex > 0); - - r = open_lldp_neighbors(ifindex, &f); - if (r < 0) - return r; - - for (;;) { - const char *system_name = NULL, *port_id = NULL, *port_description = NULL; - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - - r = next_lldp_neighbor(f, &n); - if (r < 0) - return r; - if (r == 0) - break; - - printf("%*s", - (int) strlen(prefix), - c == 0 ? prefix : ""); - - (void) sd_lldp_neighbor_get_system_name(n, &system_name); - (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); - (void) sd_lldp_neighbor_get_port_description(n, &port_description); - - printf("%s on port %s", strna(system_name), strna(port_id)); - - if (!isempty(port_description)) - printf(" (%s)", port_description); - - putchar('\n'); - - c++; - } - - return c; -} - -static void dump_ifindexes(const char *prefix, const int *ifindexes) { - unsigned c; - - assert(prefix); - - if (!ifindexes || ifindexes[0] <= 0) - return; - - for (c = 0; ifindexes[c] > 0; c++) { - char name[IF_NAMESIZE+1]; - - printf("%*s", - (int) strlen(prefix), - c == 0 ? prefix : ""); - - if (if_indextoname(ifindexes[c], name)) - fputs(name, stdout); - else - printf("%i", ifindexes[c]); - - fputc('\n', stdout); - } -} - -static void dump_list(const char *prefix, char **l) { - char **i; - - if (strv_isempty(l)) - return; - - STRV_FOREACH(i, l) { - printf("%*s%s\n", - (int) strlen(prefix), - i == l ? prefix : "", - *i); - } -} - -static int link_status_one( - sd_netlink *rtnl, - sd_hwdb *hwdb, - const LinkInfo *info) { - - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; - _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = 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; - const char *on_color_operational, *off_color_operational, - *on_color_setup, *off_color_setup; - _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; - int r; - - assert(rtnl); - assert(info); - - (void) sd_network_link_get_operational_state(info->ifindex, &operational_state); - operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - - r = sd_network_link_get_setup_state(info->ifindex, &setup_state); - if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ - setup_state = strdup("unmanaged"); - setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - - (void) sd_network_link_get_dns(info->ifindex, &dns); - (void) sd_network_link_get_search_domains(info->ifindex, &search_domains); - (void) sd_network_link_get_route_domains(info->ifindex, &route_domains); - (void) sd_network_link_get_ntp(info->ifindex, &ntp); - - xsprintf(devid, "n%i", info->ifindex); - - (void) sd_device_new_from_device_id(&d, devid); - - if (d) { - (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link); - (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver); - (void) sd_device_get_property_value(d, "ID_PATH", &path); - - r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor); - if (r < 0) - (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor); - - r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model); - if (r < 0) - (void) sd_device_get_property_value(d, "ID_MODEL", &model); - } - - (void) link_get_type_string(info->iftype, d, &t); - - (void) sd_network_link_get_network_file(info->ifindex, &network); - - (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); - (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); - - printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); - - printf(" Link File: %s\n" - " Network File: %s\n" - " Type: %s\n" - " State: %s%s%s (%s%s%s)\n", - strna(link), - strna(network), - strna(t), - on_color_operational, strna(operational_state), off_color_operational, - on_color_setup, strna(setup_state), off_color_setup); - - if (path) - printf(" Path: %s\n", path); - if (driver) - printf(" Driver: %s\n", driver); - if (vendor) - printf(" Vendor: %s\n", vendor); - if (model) - printf(" Model: %s\n", model); - - if (info->has_mac_address) { - _cleanup_free_ char *description = NULL; - char ea[ETHER_ADDR_TO_STRING_MAX]; - - (void) ieee_oui(hwdb, &info->mac_address, &description); - - if (description) - printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description); - else - printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea)); - } - - if (info->has_mtu) - printf(" MTU: %u\n", info->mtu); - - (void) dump_addresses(rtnl, " Address: ", info->ifindex); - (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex); - - dump_list(" DNS: ", dns); - dump_list(" Search Domains: ", search_domains); - dump_list(" Route Domains: ", route_domains); - - dump_list(" NTP: ", ntp); - - dump_ifindexes("Carrier Bound To: ", carrier_bound_to); - dump_ifindexes("Carrier Bound By: ", carrier_bound_by); - - (void) sd_network_link_get_timezone(info->ifindex, &tz); - if (tz) - printf(" Time Zone: %s\n", tz); - - (void) dump_lldp_neighbors(" Connected To: ", info->ifindex); - - return 0; -} - -static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { - _cleanup_free_ char *operational_state = NULL; - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; - const char *on_color_operational, *off_color_operational; - - assert(rtnl); - - (void) sd_network_get_operational_state(&operational_state); - operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - - printf("%s%s%s State: %s%s%s\n", - on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, - on_color_operational, strna(operational_state), off_color_operational); - - (void) dump_addresses(rtnl, " Address: ", 0); - (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0); - - (void) sd_network_get_dns(&dns); - dump_list(" DNS: ", dns); - - (void) sd_network_get_search_domains(&search_domains); - dump_list("Search Domains: ", search_domains); - - (void) sd_network_get_route_domains(&route_domains); - dump_list(" Route Domains: ", route_domains); - - (void) sd_network_get_ntp(&ntp); - dump_list(" NTP: ", ntp); - - return 0; -} - -static int link_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - _cleanup_free_ LinkInfo *links = NULL; - int r, c, i; - - pager_open(arg_no_pager, false); - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_hwdb_new(&hwdb); - if (r < 0) - log_debug_errno(r, "Failed to open hardware database: %m"); - - if (arg_all) - c = acquire_link_info_all(rtnl, &links); - else if (argc <= 1) - return system_status(rtnl, hwdb); - else - c = acquire_link_info_strv(rtnl, argv + 1, &links); - if (c < 0) - return c; - - for (i = 0; i < c; i++) { - if (i > 0) - fputc('\n', stdout); - - link_status_one(rtnl, hwdb, links + i); - } - - return 0; -} - -static char *lldp_capabilities_to_string(uint16_t x) { - static const char characters[] = { - 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm', - }; - char *ret; - unsigned i; - - ret = new(char, ELEMENTSOF(characters) + 1); - if (!ret) - return NULL; - - for (i = 0; i < ELEMENTSOF(characters); i++) - ret[i] = (x & (1U << i)) ? characters[i] : '.'; - - ret[i] = 0; - return ret; -} - -static void lldp_capabilities_legend(uint16_t x) { - unsigned w, i, cols = columns(); - static const char* const table[] = { - "o - Other", - "p - Repeater", - "b - Bridge", - "w - WLAN Access Point", - "r - Router", - "t - Telephone", - "d - DOCSIS cable device", - "a - Station", - "c - Customer VLAN", - "s - Service VLAN", - "m - Two-port MAC Relay (TPMR)", - }; - - if (x == 0) - return; - - printf("\nCapability Flags:\n"); - for (w = 0, i = 0; i < ELEMENTSOF(table); i++) - if (x & (1U << i) || arg_all) { - bool newline; - - newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols; - if (newline) - w = 0; - w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]); - } - puts(""); -} - -static int link_lldp_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - _cleanup_free_ LinkInfo *links = NULL; - int i, r, c, m = 0; - uint16_t all = 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - if (argc > 1) - c = acquire_link_info_strv(rtnl, argv + 1, &links); - else - c = acquire_link_info_all(rtnl, &links); - if (c < 0) - return c; - - pager_open(arg_no_pager, false); - - if (arg_legend) - printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", - "LINK", - "CHASSIS ID", - "SYSTEM NAME", - "CAPS", - "PORT ID", - "PORT DESCRIPTION"); - - for (i = 0; i < c; i++) { - _cleanup_fclose_ FILE *f = NULL; - - r = open_lldp_neighbors(links[i].ifindex, &f); - if (r == -ENOENT) - continue; - if (r < 0) { - log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex); - continue; - } - - for (;;) { - _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL; - const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL; - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - uint16_t cc; - - r = next_lldp_neighbor(f, &n); - if (r < 0) { - log_warning_errno(r, "Failed to read neighbor data: %m"); - break; - } - if (r == 0) - break; - - (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id); - (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); - (void) sd_lldp_neighbor_get_system_name(n, &system_name); - (void) sd_lldp_neighbor_get_port_description(n, &port_description); - - if (chassis_id) { - cid = ellipsize(chassis_id, 17, 100); - if (cid) - chassis_id = cid; - } - - if (port_id) { - pid = ellipsize(port_id, 17, 100); - if (pid) - port_id = pid; - } - - if (system_name) { - sname = ellipsize(system_name, 16, 100); - if (sname) - system_name = sname; - } - - if (port_description) { - pdesc = ellipsize(port_description, 16, 100); - if (pdesc) - port_description = pdesc; - } - - if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) { - capabilities = lldp_capabilities_to_string(cc); - all |= cc; - } - - printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", - links[i].name, - strna(chassis_id), - strna(system_name), - strna(capabilities), - strna(port_id), - strna(port_description)); - - m++; - } - } - - if (arg_legend) { - lldp_capabilities_legend(all); - printf("\n%i neighbors listed.\n", m); - } - - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Query and control the networking subsystem.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " -a --all Show status for all links\n\n" - "Commands:\n" - " list [LINK...] List links\n" - " status [LINK...] Show link status\n" - " lldp [LINK...] Show LLDP neighbors\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_NO_LEGEND, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "all", no_argument, NULL, 'a' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) { - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_LEGEND: - arg_legend = false; - break; - - case 'a': - arg_all = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - } - - return 1; -} - -static int networkctl_main(int argc, char *argv[]) { - const Verb verbs[] = { - { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links }, - { "status", VERB_ANY, VERB_ANY, 0, link_status }, - { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -static void warn_networkd_missing(void) { - - if (access("/run/systemd/netif/state", F_OK) >= 0) - return; - - fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n"); -} - -int main(int argc, char* argv[]) { - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - warn_networkd_missing(); - - r = networkctl_main(argc, argv); - -finish: - pager_close(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c deleted file mode 100644 index ebc6c9eb9e..0000000000 --- a/src/network/networkd-address-pool.c +++ /dev/null @@ -1,171 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "networkd-address-pool.h" -#include "networkd.h" -#include "set.h" -#include "string-util.h" - -int address_pool_new( - Manager *m, - AddressPool **ret, - int family, - const union in_addr_union *u, - unsigned prefixlen) { - - AddressPool *p; - - assert(m); - assert(ret); - assert(u); - - p = new0(AddressPool, 1); - if (!p) - return -ENOMEM; - - p->manager = m; - p->family = family; - p->prefixlen = prefixlen; - p->in_addr = *u; - - LIST_PREPEND(address_pools, m->address_pools, p); - - *ret = p; - return 0; -} - -int address_pool_new_from_string( - Manager *m, - AddressPool **ret, - int family, - const char *p, - unsigned prefixlen) { - - union in_addr_union u; - int r; - - assert(m); - assert(ret); - assert(p); - - r = in_addr_from_string(family, p, &u); - if (r < 0) - return r; - - return address_pool_new(m, ret, family, &u, prefixlen); -} - -void address_pool_free(AddressPool *p) { - - if (!p) - return; - - if (p->manager) - LIST_REMOVE(address_pools, p->manager->address_pools, p); - - free(p); -} - -static bool address_pool_prefix_is_taken( - AddressPool *p, - const union in_addr_union *u, - unsigned prefixlen) { - - Iterator i; - Link *l; - Network *n; - - assert(p); - assert(u); - - HASHMAP_FOREACH(l, p->manager->links, i) { - Address *a; - Iterator j; - - /* Don't clash with assigned addresses */ - SET_FOREACH(a, l->addresses, j) { - if (a->family != p->family) - continue; - - if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) - return true; - } - - /* Don't clash with addresses already pulled from the pool, but not assigned yet */ - LIST_FOREACH(addresses, a, l->pool_addresses) { - if (a->family != p->family) - continue; - - if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) - return true; - } - } - - /* And don't clash with configured but un-assigned addresses either */ - LIST_FOREACH(networks, n, p->manager->networks) { - Address *a; - - LIST_FOREACH(addresses, a, n->static_addresses) { - if (a->family != p->family) - continue; - - if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) - return true; - } - } - - return false; -} - -int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) { - union in_addr_union u; - - assert(p); - assert(prefixlen > 0); - assert(found); - - if (p->prefixlen > prefixlen) - return 0; - - u = p->in_addr; - for (;;) { - if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { - _cleanup_free_ char *s = NULL; - int r; - - r = in_addr_to_string(p->family, &u, &s); - if (r < 0) - return r; - - log_debug("Found range %s/%u", strna(s), prefixlen); - - *found = u; - return 1; - } - - if (!in_addr_prefix_next(p->family, &u, prefixlen)) - return 0; - - if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen)) - return 0; - } - - return 0; -} diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h deleted file mode 100644 index af30decfe0..0000000000 --- a/src/network/networkd-address-pool.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct AddressPool AddressPool; - -#include "in-addr-util.h" -#include "list.h" - -typedef struct Manager Manager; - -struct AddressPool { - Manager *manager; - - int family; - unsigned prefixlen; - - union in_addr_union in_addr; - - LIST_FIELDS(AddressPool, address_pools); -}; - -int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen); -int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); -void address_pool_free(AddressPool *p); - -int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c deleted file mode 100644 index 5498e352d8..0000000000 --- a/src/network/networkd-address.c +++ /dev/null @@ -1,863 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "alloc-util.h" -#include "conf-parser.h" -#include "firewall-util.h" -#include "netlink-util.h" -#include "networkd-address.h" -#include "networkd.h" -#include "parse-util.h" -#include "set.h" -#include "socket-util.h" -#include "string-util.h" -#include "utf8.h" -#include "util.h" - -#define ADDRESSES_PER_LINK_MAX 2048U -#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U - -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; - - assert(network); - assert(ret); - - if (section) { - address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section)); - if (address) { - *ret = address; - address = NULL; - - return 0; - } - } - - if (network->n_static_addresses >= STATIC_ADDRESSES_PER_NETWORK_MAX) - return -E2BIG; - - r = address_new(&address); - if (r < 0) - return r; - - if (section) { - address->section = section; - hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); - } - - address->network = network; - LIST_APPEND(addresses, network->static_addresses, address); - network->n_static_addresses++; - - *ret = address; - address = NULL; - - return 0; -} - -void address_free(Address *address) { - if (!address) - return; - - if (address->network) { - LIST_REMOVE(addresses, address->network->static_addresses, address); - assert(address->network->n_static_addresses > 0); - address->network->n_static_addresses--; - - if (address->section) - hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section)); - } - - if (address->link) { - set_remove(address->link->addresses, address); - set_remove(address->link->addresses_foreign, address); - - if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) - memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); - } - - free(address); -} - -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; - - assert(address); - assert(link); - - masq = link->network && - 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) { - union in_addr_union masked = address->in_addr; - in_addr_mask(address->family, &masked, address->prefixlen); - - r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); - if (r < 0) - log_link_warning_errno(link, r, "Could not enable IP masquerading: %m"); - - address->ip_masquerade_done = masq; - } - - return 0; -} - -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(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) { - union in_addr_union masked = address->in_addr; - in_addr_mask(address->family, &masked, address->prefixlen); - - r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); - if (r < 0) - log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m"); - - address->ip_masquerade_done = false; - } - - return 0; -} - -int address_update( - Address *address, - unsigned char flags, - unsigned char scope, - const struct ifa_cacheinfo *cinfo) { - - bool ready; - int r; - - assert(address); - assert(cinfo); - assert_return(address->link, 1); - - if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - ready = address_is_ready(address); - - address->flags = flags; - address->scope = scope; - address->cinfo = *cinfo; - - link_update_operstate(address->link); - - if (!ready && address_is_ready(address)) { - link_check_ready(address->link); - - 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; - } - } - - 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_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); - - address = (Address) { - .family = family, - .in_addr = *in_addr, - .prefixlen = prefixlen, - }; - - existing = set_get(link->addresses, &address); - if (existing) { - if (ret) - *ret = existing; - return 1; - } - - existing = set_get(link->addresses_foreign, &address); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - - return -ENOENT; -} - -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(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"); - - 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->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"); - - 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"); - - link_ref(link); - - return 0; -} - -static int address_acquire(Link *link, Address *original, Address **ret) { - union in_addr_union in_addr = {}; - struct in_addr broadcast = {}; - _cleanup_address_free_ Address *na = NULL; - int r; - - assert(link); - assert(original); - assert(ret); - - /* Something useful was configured? just use it */ - if (in_addr_is_null(original->family, &original->in_addr) <= 0) - return 0; - - /* The address is configured to be 0.0.0.0 or [::] by the user? - * Then let's acquire something more useful from the pool. */ - r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); - if (r < 0) - return log_link_error_errno(link, r, "Failed to acquire address from pool: %m"); - if (r == 0) { - log_link_error(link, "Couldn't find free address for interface, all taken."); - return -EBUSY; - } - - if (original->family == AF_INET) { - /* Pick first address in range for ourselves ... */ - in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1); - - /* .. and use last as broadcast address */ - broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen); - } else if (original->family == AF_INET6) - in_addr.in6.s6_addr[15] |= 1; - - r = address_new(&na); - if (r < 0) - return r; - - na->family = original->family; - na->prefixlen = original->prefixlen; - na->scope = original->scope; - na->cinfo = original->cinfo; - - if (original->label) { - na->label = strdup(original->label); - if (!na->label) - return -ENOMEM; - } - - na->broadcast = broadcast; - na->in_addr = in_addr; - - LIST_PREPEND(addresses, link->pool_addresses, na); - - *ret = na; - na = NULL; - - return 0; -} - -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); - assert(address->family == AF_INET || address->family == AF_INET6); - assert(link); - assert(link->ifindex > 0); - assert(link->manager); - assert(link->manager->rtnl); - - /* If this is a new address, then refuse adding more than the limit */ - if (address_get(link, address->family, &address->in_addr, address->prefixlen, NULL) <= 0 && - set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return -E2BIG; - - r = address_acquire(link, address, &address); - if (r < 0) - return r; - - 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"); - - 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) { - 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) - 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 (!in_addr_is_null(address->family, &address->in_addr_peer)) { - if (address->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in); - else if (address->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6); - if (r < 0) - return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m"); - } else { - 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 = 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); - - 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; -} - -int config_parse_broadcast( - 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_address_free_ Address *n = NULL; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = address_new_static(network, section_line, &n); - if (r < 0) - return r; - - if (n->family == AF_INET6) { - 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, r, "Broadcast is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - n->family = AF_INET; - n = NULL; - - return 0; -} - -int config_parse_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - _cleanup_address_free_ Address *n = NULL; - const char *address, *e; - union in_addr_union buffer; - int r, f; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - if (streq(section, "Network")) { - /* we are not in an Address section, so treat - * this as the special '0' section */ - section_line = 0; - } - - r = address_new_static(network, section_line, &n); - if (r < 0) - return r; - - /* Address=address/prefixlen */ - - /* prefixlen */ - e = strchr(rvalue, '/'); - if (e) { - unsigned i; - - r = safe_atou(e + 1, &i); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length is invalid, ignoring assignment: %s", e + 1); - return 0; - } - - n->prefixlen = (unsigned char) i; - - address = strndupa(rvalue, e - rvalue); - } else - address = rvalue; - - r = in_addr_from_string_auto(address, &f, &buffer); - if (r < 0) { - 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, 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, 0, "Address is incompatible, ignoring assignment: %s", address); - return 0; - } - - n->family = f; - - if (streq(lvalue, "Address")) - n->in_addr = buffer; - else - n->in_addr_peer = buffer; - - if (n->family == AF_INET && n->broadcast.s_addr == 0) - n->broadcast.s_addr = n->in_addr.in.s_addr | htonl(0xfffffffflu >> n->prefixlen); - - n = NULL; - - return 0; -} - -int config_parse_label( - 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_address_free_ Address *n = NULL; - Network *network = userdata; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = address_new_static(network, section_line, &n); - if (r < 0) - return r; - - if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); - return 0; - } - - r = free_and_strdup(&n->label, rvalue); - if (r < 0) - return log_oom(); - - n = NULL; - - return 0; -} - -int config_parse_lifetime(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_address_free_ Address *n = NULL; - unsigned k; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = address_new_static(network, section_line, &n); - if (r < 0) - return r; - - if (STR_IN_SET(rvalue, "forever", "infinity")) { - n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; - n = NULL; - - return 0; - } - - r = safe_atou(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); - return 0; - } - - if (k != 0) - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); - else { - n->cinfo.ifa_prefered = k; - n = NULL; - } - - return 0; -} - -bool address_is_ready(const Address *a) { - assert(a); - - return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); -} diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h deleted file mode 100644 index 03c4bea7c6..0000000000 --- a/src/network/networkd-address.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include - -#include "in-addr-util.h" - -typedef struct Address Address; - -#include "networkd-link.h" -#include "networkd-network.h" - -#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU - -typedef struct Network Network; -typedef struct Link Link; - -struct Address { - Network *network; - unsigned section; - - Link *link; - - int family; - unsigned char prefixlen; - unsigned char scope; - uint32_t flags; - char *label; - - struct in_addr broadcast; - struct ifa_cacheinfo cinfo; - - union in_addr_union in_addr; - union in_addr_union in_addr_peer; - - bool ip_masquerade_done:1; - - LIST_FIELDS(Address, addresses); -}; - -int address_new_static(Network *network, unsigned section, Address **ret); -int address_new(Address **ret); -void address_free(Address *address); -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, const 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) - -int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_broadcast(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_label(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_lifetime(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-brvlan.c b/src/network/networkd-brvlan.c deleted file mode 100644 index 8bc330ebae..0000000000 --- a/src/network/networkd-brvlan.c +++ /dev/null @@ -1,329 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2016 BISDN GmbH. 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "conf-parser.h" -#include "netlink-util.h" -#include "networkd-brvlan.h" -#include "networkd.h" -#include "parse-util.h" -#include "vlan-util.h" - -static bool is_bit_set(unsigned bit, uint32_t scope) { - assert(bit < sizeof(scope)*8); - return scope & (1 << bit); -} - -static inline void set_bit(unsigned nr, uint32_t *addr) { - if (nr < BRIDGE_VLAN_BITMAP_MAX) - addr[nr / 32] |= (((uint32_t) 1) << (nr % 32)); -} - -static int find_next_bit(int i, uint32_t x) { - int j; - - if (i >= 32) - return -1; - - /* find first bit */ - if (i < 0) - return BUILTIN_FFS_U32(x); - - /* mask off prior finds to get next */ - j = __builtin_ffs(x >> i); - return j ? j + i : 0; -} - -static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint16_t pvid, const uint32_t *br_vid_bitmap, const uint32_t *br_untagged_bitmap) { - struct bridge_vlan_info br_vlan; - int i, j, k, r, done, cnt; - uint16_t begin, end; - bool untagged = false; - - assert(link); - assert(req); - assert(br_vid_bitmap); - assert(br_untagged_bitmap); - - i = cnt = -1; - - begin = end = UINT16_MAX; - for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) { - unsigned base_bit; - uint32_t vid_map = br_vid_bitmap[k]; - uint32_t untagged_map = br_untagged_bitmap[k]; - - base_bit = k * 32; - i = -1; - done = 0; - do { - j = find_next_bit(i, vid_map); - if (j > 0) { - /* first hit of any bit */ - if (begin == UINT16_MAX && end == UINT16_MAX) { - begin = end = j - 1 + base_bit; - untagged = is_bit_set(j - 1, untagged_map); - goto next; - } - - /* this bit is a continuation of prior bits */ - if (j - 2 + base_bit == end && untagged == is_bit_set(j - 1, untagged_map) && (uint16_t)j - 1 + base_bit != pvid && (uint16_t)begin != pvid) { - end++; - goto next; - } - } else - done = 1; - - if (begin != UINT16_MAX) { - cnt++; - if (done && k < BRIDGE_VLAN_BITMAP_LEN - 1) - break; - - br_vlan.flags = 0; - if (untagged) - br_vlan.flags |= BRIDGE_VLAN_INFO_UNTAGGED; - - if (begin == end) { - br_vlan.vid = begin; - - if (begin == pvid) - br_vlan.flags |= BRIDGE_VLAN_INFO_PVID; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); - } else { - br_vlan.vid = begin; - br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); - - br_vlan.vid = end; - br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; - br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_END; - - r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); - } - - if (done) - break; - } - if (j > 0) { - begin = end = j - 1 + base_bit; - untagged = is_bit_set(j - 1, untagged_map); - } - - next: - i = j; - } while(!done); - } - if (!cnt) - return -EINVAL; - - return cnt; -} - -static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - Link *link = userdata; - int r; - - assert(link); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_error_errno(link, r, "Could not add VLAN to bridge port: %m"); - - return 1; -} - -int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - uint16_t flags; - sd_netlink *rtnl; - - assert(link); - assert(link->manager); - assert(br_vid_bitmap); - assert(br_untagged_bitmap); - assert(link->network); - - /* pvid might not be in br_vid_bitmap yet */ - if (pvid) - set_bit(pvid, br_vid_bitmap); - - rtnl = link->manager->rtnl; - - /* create new RTM message */ - r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); - if (r < 0) - return log_link_error_errno(link, r, "Could not set message family: %m"); - - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - - /* master needs flag self */ - if (!link->network->bridge) { - flags = BRIDGE_FLAGS_SELF; - sd_netlink_message_append_data(req, IFLA_BRIDGE_FLAGS, &flags, sizeof(uint16_t)); - } - - /* add vlan info */ - r = append_vlan_info_data(link, req, pvid, br_vid_bitmap, br_untagged_bitmap); - if (r < 0) - return log_link_error_errno(link, r, "Could not append VLANs: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); - - /* send message to the kernel */ - r = sd_netlink_call_async(rtnl, req, set_brvlan_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - return 0; -} - -static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) { - int r; - char *p; - char *_rvalue = NULL; - uint16_t _vid = UINT16_MAX; - uint16_t _vid_end = UINT16_MAX; - - assert(rvalue); - assert(vid); - assert(vid_end); - - _rvalue = strdupa(rvalue); - p = strchr(_rvalue, '-'); - if (p) { - *p = '\0'; - p++; - r = parse_vlanid(_rvalue, &_vid); - if (r < 0) - return r; - - if (_vid == 0) - return -ERANGE; - - r = parse_vlanid(p, &_vid_end); - if (r < 0) - return r; - - if (_vid_end == 0) - return -ERANGE; - } else { - r = parse_vlanid(_rvalue, &_vid); - if (r < 0) - return r; - - if (_vid == 0) - return -ERANGE; - } - - *vid = _vid; - *vid_end = _vid_end; - return r; -} - -int config_parse_brvlan_vlan(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; - int r; - uint16_t vid, vid_end; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_vid_range(rvalue, &vid, &vid_end); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue); - return 0; - } - - if (UINT16_MAX == vid_end) - set_bit(vid++, network->br_vid_bitmap); - else { - if (vid >= vid_end) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue); - return 0; - } - for (; vid <= vid_end; vid++) - set_bit(vid, network->br_vid_bitmap); - } - return 0; -} - -int config_parse_brvlan_untagged(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; - int r; - uint16_t vid, vid_end; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_vid_range(rvalue, &vid, &vid_end); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue); - return 0; - } - - if (UINT16_MAX == vid_end) { - set_bit(vid, network->br_vid_bitmap); - set_bit(vid, network->br_untagged_bitmap); - } else { - if (vid >= vid_end) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue); - return 0; - } - for (; vid <= vid_end; vid++) { - set_bit(vid, network->br_vid_bitmap); - set_bit(vid, network->br_untagged_bitmap); - } - } - return 0; -} diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h deleted file mode 100644 index 6aa6883bfc..0000000000 --- a/src/network/networkd-brvlan.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2016 BISDN GmbH. 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 . -***/ - -#include - -typedef struct Link Link; - -int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap); - -int config_parse_brvlan_vlan(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_brvlan_untagged(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-conf.c b/src/network/networkd-conf.c deleted file mode 100644 index c03e2b2ebf..0000000000 --- a/src/network/networkd-conf.c +++ /dev/null @@ -1,111 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Vinay Kulkarni - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include - -#include "conf-parser.h" -#include "def.h" -#include "dhcp-identifier.h" -#include "hexdecoct.h" -#include "networkd-conf.h" -#include "string-table.h" - -int manager_parse_config_file(Manager *m) { - assert(m); - - return config_parse_many(PKGSYSCONFDIR "/networkd.conf", - CONF_PATHS_NULSTR("systemd/networkd.conf.d"), - "DHCP\0", - config_item_perf_lookup, networkd_gperf_lookup, - false, m); -} - -static const char* const duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "link-layer-time", - [DUID_TYPE_EN] = "vendor", - [DUID_TYPE_LL] = "link-layer", - [DUID_TYPE_UUID] = "uuid", -}; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType); -DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type"); - -int config_parse_duid_rawdata( - 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) { - - DUID *ret = data; - uint8_t raw_data[MAX_DUID_LEN]; - unsigned count = 0; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(ret); - - /* RawData contains DUID in format "NN:NN:NN..." */ - for (;;) { - int n1, n2, len, r; - uint32_t byte; - _cleanup_free_ char *cbyte = NULL; - - r = extract_first_word(&rvalue, &cbyte, ":", 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); - return 0; - } - if (r == 0) - break; - if (count >= MAX_DUID_LEN) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); - return 0; - } - - len = strlen(cbyte); - if (len != 1 && len != 2) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); - return 0; - } - n1 = unhexchar(cbyte[0]); - if (len == 2) - n2 = unhexchar(cbyte[1]); - else - n2 = 0; - - if (n1 < 0 || n2 < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); - return 0; - } - - byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; - raw_data[count++] = byte; - } - - assert_cc(sizeof(raw_data) == sizeof(ret->raw_data)); - memcpy(ret->raw_data, raw_data, count); - ret->raw_data_len = count; - return 0; -} diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h deleted file mode 100644 index c7bfb42a72..0000000000 --- a/src/network/networkd-conf.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Vinay Kulkarni - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "networkd.h" - -int manager_parse_config_file(Manager *m); - -const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length); - -int config_parse_duid_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_duid_rawdata( - 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-dhcp4.c b/src/network/networkd-dhcp4.c deleted file mode 100644 index 12fb8e3fce..0000000000 --- a/src/network/networkd-dhcp4.c +++ /dev/null @@ -1,657 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013-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 . -***/ - -#include -#include - -#include "alloc-util.h" -#include "dhcp-lease-internal.h" -#include "hostname-util.h" -#include "network-internal.h" -#include "networkd.h" - -static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, - void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - assert(link->dhcp4_messages > 0); - - link->dhcp4_messages--; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "Could not set DHCPv4 route: %m"); - link_enter_failed(link); - } - - if (link->dhcp4_messages == 0) { - link->dhcp4_configured = true; - link_check_ready(link); - } - - return 1; -} - -static int link_set_dhcp_routes(Link *link) { - struct in_addr gateway; - _cleanup_free_ sd_dhcp_route **static_routes = NULL; - int r, n, i; - - assert(link); - assert(link->dhcp_lease); - assert(link->network); - - if (!link->network->dhcp_use_routes) - return 0; - - r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); - if (r < 0 && r != -ENODATA) - return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); - - if (r >= 0) { - struct in_addr address; - _cleanup_route_free_ Route *route = NULL; - _cleanup_route_free_ Route *route_gw = NULL; - - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); - - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - route->protocol = RTPROT_DHCP; - - r = route_new(&route_gw); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - /* The dhcp netmask may mask out the gateway. Add an explicit - * 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.in = gateway; - route_gw->dst_prefixlen = 32; - route_gw->prefsrc.in = address; - route_gw->scope = RT_SCOPE_LINK; - 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) - return log_link_warning_errno(link, r, "Could not set host route: %m"); - - link->dhcp4_messages++; - - route->family = AF_INET; - 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) { - log_link_warning_errno(link, r, "Could not set routes: %m"); - link_enter_failed(link); - return r; - } - - link->dhcp4_messages++; - } - - n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); - if (n == -ENODATA) - return 0; - if (n < 0) - return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m"); - - for (i = 0; i < n; i++) { - _cleanup_route_free_ Route *route = NULL; - - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - route->family = AF_INET; - 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) - return log_link_warning_errno(link, r, "Could not set host route: %m"); - - link->dhcp4_messages++; - } - - return 0; -} - -static int dhcp_lease_lost(Link *link) { - _cleanup_address_free_ Address *address = NULL; - struct in_addr addr; - struct in_addr netmask; - struct in_addr gateway; - unsigned prefixlen = 0; - int r; - - assert(link); - assert(link->dhcp_lease); - - log_link_warning(link, "DHCP lease lost"); - - if (link->network->dhcp_use_routes) { - _cleanup_free_ sd_dhcp_route **routes = NULL; - int n, i; - - n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes); - if (n >= 0) { - for (i = 0; i < n; i++) { - _cleanup_route_free_ Route *route = NULL; - - r = route_new(&route); - if (r >= 0) { - route->family = AF_INET; - 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_remove(route, link, - link_route_remove_handler); - } - } - } - } - - 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(&route_gw); - if (r >= 0) { - route_gw->family = AF_INET; - route_gw->dst.in = gateway; - route_gw->dst_prefixlen = 32; - route_gw->scope = RT_SCOPE_LINK; - - route_remove(route_gw, link, - link_route_remove_handler); - } - - r = route_new(&route); - if (r >= 0) { - route->family = AF_INET; - route->gw.in = gateway; - - route_remove(route, link, - link_route_remove_handler); - } - } - - r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr); - if (r >= 0) { - r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask); - if (r >= 0) - prefixlen = in_addr_netmask_to_prefixlen(&netmask); - - address->family = AF_INET; - address->in_addr.in = addr; - address->prefixlen = prefixlen; - - address_remove(address, link, link_address_remove_handler); - } - } - - if (link->network->dhcp_use_mtu) { - uint16_t mtu; - - r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); - if (r >= 0 && link->original_mtu != mtu) { - r = link_set_mtu(link, link->original_mtu); - if (r < 0) { - log_link_warning(link, - "DHCP error: could not reset MTU"); - link_enter_failed(link); - return r; - } - } - } - - if (link->network->dhcp_use_hostname) { - const char *hostname = NULL; - - if (link->network->dhcp_hostname) - hostname = link->network->dhcp_hostname; - else - (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); - - if (hostname) { - /* If a hostname was set due to the lease, then unset it now. */ - r = link_set_hostname(link, NULL); - if (r < 0) - log_link_warning_errno(link, r, "Failed to reset transient hostname: %m"); - } - } - - link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); - link_dirty(link); - link->dhcp4_configured = false; - - return 0; -} - -static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, - void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "Could not set DHCPv4 address: %m"); - link_enter_failed(link); - } else if (r >= 0) - manager_rtnl_process_address(rtnl, m, link->manager); - - link_set_dhcp_routes(link); - - return 1; -} - -static int dhcp4_update_address(Link *link, - struct in_addr *address, - struct in_addr *netmask, - uint32_t lifetime) { - _cleanup_address_free_ Address *addr = NULL; - unsigned prefixlen; - int r; - - assert(address); - assert(netmask); - assert(lifetime); - - prefixlen = in_addr_netmask_to_prefixlen(netmask); - - r = address_new(&addr); - if (r < 0) - return r; - - addr->family = AF_INET; - addr->in_addr.in.s_addr = address->s_addr; - addr->cinfo.ifa_prefered = lifetime; - addr->cinfo.ifa_valid = lifetime; - addr->prefixlen = prefixlen; - addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr; - - /* 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; - - return 0; -} - -static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { - sd_dhcp_lease *lease; - struct in_addr address; - struct in_addr netmask; - uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; - int r; - - assert(link); - assert(client); - assert(link->network); - - r = sd_dhcp_client_get_lease(client, &lease); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: no lease: %m"); - - 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) - return log_link_warning_errno(link, r, "DHCP error: no address: %m"); - - r = sd_dhcp_lease_get_netmask(lease, &netmask); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: no netmask: %m"); - - if (!link->network->dhcp_critical) { - r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); - } - - r = dhcp4_update_address(link, &address, &netmask, lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Could not update IP address: %m"); - link_enter_failed(link); - return r; - } - - return 0; -} - -static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { - sd_dhcp_lease *lease; - struct in_addr address; - struct in_addr netmask; - struct in_addr gateway; - unsigned prefixlen; - uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; - int r; - - assert(client); - assert(link); - - r = sd_dhcp_client_get_lease(client, &lease); - if (r < 0) - return log_link_error_errno(link, r, "DHCP error: No lease: %m"); - - r = sd_dhcp_lease_get_address(lease, &address); - if (r < 0) - return log_link_error_errno(link, r, "DHCP error: No address: %m"); - - r = sd_dhcp_lease_get_netmask(lease, &netmask); - if (r < 0) - return log_link_error_errno(link, r, "DHCP error: No netmask: %m"); - - prefixlen = in_addr_netmask_to_prefixlen(&netmask); - - r = sd_dhcp_lease_get_router(lease, &gateway); - if (r < 0 && r != -ENODATA) - return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); - - if (r >= 0) - log_struct(LOG_INFO, - LOG_LINK_INTERFACE(link), - LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", - ADDRESS_FMT_VAL(address), - prefixlen, - ADDRESS_FMT_VAL(gateway)), - "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), - "PREFIXLEN=%u", prefixlen, - "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway), - NULL); - else - log_struct(LOG_INFO, - LOG_LINK_INTERFACE(link), - LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u", - ADDRESS_FMT_VAL(address), - prefixlen), - "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), - "PREFIXLEN=%u", prefixlen, - NULL); - - link->dhcp_lease = sd_dhcp_lease_ref(lease); - link_dirty(link); - - if (link->network->dhcp_use_mtu) { - uint16_t mtu; - - r = sd_dhcp_lease_get_mtu(lease, &mtu); - if (r >= 0) { - r = link_set_mtu(link, mtu); - if (r < 0) - log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu); - } - } - - if (link->network->dhcp_use_hostname) { - const char *hostname = NULL; - - if (link->network->dhcp_hostname) - hostname = link->network->dhcp_hostname; - else - (void) sd_dhcp_lease_get_hostname(lease, &hostname); - - if (hostname) { - r = link_set_hostname(link, hostname); - if (r < 0) - log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname); - } - } - - if (link->network->dhcp_use_timezone) { - const char *tz = NULL; - - (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); - - if (tz) { - r = link_set_timezone(link, tz); - if (r < 0) - log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz); - } - } - - if (!link->network->dhcp_critical) { - r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); - return r; - } - } - - r = dhcp4_update_address(link, &address, &netmask, lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Could not update IP address: %m"); - link_enter_failed(link); - return r; - } - - return 0; -} -static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { - Link *link = userdata; - int r = 0; - - assert(link); - assert(link->network); - assert(link->manager); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - switch (event) { - case SD_DHCP_CLIENT_EVENT_EXPIRED: - case SD_DHCP_CLIENT_EVENT_STOP: - case SD_DHCP_CLIENT_EVENT_IP_CHANGE: - if (link->network->dhcp_critical) { - log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); - return; - } - - if (link->dhcp_lease) { - r = dhcp_lease_lost(link); - if (r < 0) { - link_enter_failed(link); - return; - } - } - - if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) { - r = dhcp_lease_acquired(client, link); - if (r < 0) { - link_enter_failed(link); - return; - } - } - - break; - case SD_DHCP_CLIENT_EVENT_RENEW: - r = dhcp_lease_renew(client, link); - if (r < 0) { - link_enter_failed(link); - return; - } - break; - case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: - r = dhcp_lease_acquired(client, link); - if (r < 0) { - link_enter_failed(link); - return; - } - break; - default: - if (event < 0) - log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); - else - log_link_warning(link, "DHCP unknown event: %i", event); - break; - } - - return; -} - -int dhcp4_configure(Link *link) { - int r; - - assert(link); - assert(link->network); - assert(link->network->dhcp & ADDRESS_FAMILY_IPV4); - - 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) - return r; - - r = sd_dhcp_client_set_mac(link->dhcp_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); - if (r < 0) - return r; - - r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex); - if (r < 0) - return r; - - r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link); - if (r < 0) - return r; - - r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, - link->network->dhcp_broadcast); - if (r < 0) - return r; - - if (link->mtu) { - r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu); - if (r < 0) - return r; - } - - if (link->network->dhcp_use_mtu) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, - SD_DHCP_OPTION_INTERFACE_MTU); - if (r < 0) - return r; - } - - if (link->network->dhcp_use_routes) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, - SD_DHCP_OPTION_STATIC_ROUTE); - if (r < 0) - return r; - r = sd_dhcp_client_set_request_option(link->dhcp_client, - 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, SD_DHCP_OPTION_NTP_SERVER); - if (r < 0) - return r; - - r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE); - if (r < 0) - return r; - - if (link->network->dhcp_send_hostname) { - _cleanup_free_ char *hostname = NULL; - const char *hn = NULL; - - if (!link->network->dhcp_hostname) { - hostname = gethostname_malloc(); - if (!hostname) - return -ENOMEM; - - hn = hostname; - } else - hn = link->network->dhcp_hostname; - - if (!is_localhost(hn)) { - r = sd_dhcp_client_set_hostname(link->dhcp_client, hn); - if (r < 0) - return r; - } - } - - if (link->network->dhcp_vendor_class_identifier) { - r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client, - link->network->dhcp_vendor_class_identifier); - if (r < 0) - return r; - } - - switch (link->network->dhcp_client_identifier) { - case DHCP_CLIENT_ID_DUID: { - /* If configured, apply user specified DUID and/or IAID */ - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, - link->network->iaid, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return r; - break; - } - case DHCP_CLIENT_ID_MAC: - r = sd_dhcp_client_set_client_id(link->dhcp_client, - ARPHRD_ETHER, - (const uint8_t *) &link->mac, - sizeof(link->mac)); - if (r < 0) - return r; - break; - default: - assert_not_reached("Unknown client identifier type."); - } - - return 0; -} diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c deleted file mode 100644 index 15acf56a5f..0000000000 --- a/src/network/networkd-dhcp6.c +++ /dev/null @@ -1,265 +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 . -***/ - -#include -#include - -#include "sd-dhcp6-client.h" - -#include "network-internal.h" -#include "networkd.h" - -static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); - -static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, - Link *link) { - return 0; -} - -static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, - void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - if (link->rtnl_extended_attrs) { - log_link_warning(link, "Could not set extended netlink attributes, reverting to fallback mechanism"); - - link->rtnl_extended_attrs = false; - dhcp6_lease_address_acquired(link->dhcp6_client, link); - - return 1; - } - - log_link_error_errno(link, r, "Could not set DHCPv6 address: %m"); - - link_enter_failed(link); - - } else if (r >= 0) - manager_rtnl_process_address(rtnl, m, link->manager); - - return 1; -} - -static int dhcp6_address_change( - Link *link, - struct in6_addr *ip6_addr, - uint32_t lifetime_preferred, - uint32_t lifetime_valid) { - - _cleanup_address_free_ Address *addr = NULL; - char buffer[INET6_ADDRSTRLEN]; - int r; - - r = address_new(&addr); - if (r < 0) - return r; - - addr->family = AF_INET6; - memcpy(&addr->in_addr.in6, ip6_addr, sizeof(*ip6_addr)); - - addr->flags = IFA_F_NOPREFIXROUTE; - addr->prefixlen = 128; - - addr->cinfo.ifa_prefered = lifetime_preferred; - addr->cinfo.ifa_valid = lifetime_valid; - - log_link_info(link, - "DHCPv6 address %s/%d timeout preferred %d valid %d", - inet_ntop(AF_INET6, &addr->in_addr.in6, buffer, sizeof(buffer)), - addr->prefixlen, lifetime_preferred, lifetime_valid); - - r = address_configure(addr, link, dhcp6_address_handler, true); - if (r < 0) - log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m"); - - return r; -} - -static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { - int r; - sd_dhcp6_lease *lease; - struct in6_addr ip6_addr; - uint32_t lifetime_preferred, lifetime_valid; - - r = sd_dhcp6_client_get_lease(client, &lease); - if (r < 0) - return r; - - sd_dhcp6_lease_reset_address_iter(lease); - - while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { - - r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); - if (r < 0) - return r; - } - - return 0; -} - -static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { - int r; - Link *link = userdata; - - assert(link); - assert(link->network); - assert(link->manager); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - switch(event) { - case SD_DHCP6_CLIENT_EVENT_STOP: - case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: - case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - if (sd_dhcp6_client_get_lease(client, NULL) >= 0) - log_link_warning(link, "DHCPv6 lease lost"); - - link->dhcp6_configured = false; - break; - - case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: - r = dhcp6_lease_address_acquired(client, link); - if (r < 0) { - link_enter_failed(link); - return; - } - - /* fall through */ - case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: - r = dhcp6_lease_information_acquired(client, link); - if (r < 0) { - link_enter_failed(link); - return; - } - - link->dhcp6_configured = true; - break; - - default: - if (event < 0) - log_link_warning_errno(link, event, "DHCPv6 error: %m"); - else - log_link_warning(link, "DHCPv6 unknown event: %d", event); - return; - } - - link_check_ready(link); -} - -int dhcp6_request_address(Link *link, int ir) { - int r, inf_req; - bool running; - - assert(link); - assert(link->dhcp6_client); - assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); - - r = sd_dhcp6_client_is_running(link->dhcp6_client); - if (r < 0) - return r; - else - running = !!r; - - if (running) { - r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); - if (r < 0) - return r; - - if (inf_req == ir) - return 0; - - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) - return r; - } else { - r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); - if (r < 0) - return r; - } - - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir); - if (r < 0) - return r; - - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - return r; - - return 0; -} - -int dhcp6_configure(Link *link) { - sd_dhcp6_client *client = NULL; - int r; - const DUID *duid; - - assert(link); - - if (link->dhcp6_client) - return 0; - - r = sd_dhcp6_client_new(&client); - if (r < 0) - return r; - - r = sd_dhcp6_client_attach_event(client, NULL, 0); - if (r < 0) - goto error; - - r = sd_dhcp6_client_set_mac(client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); - if (r < 0) - goto error; - - r = sd_dhcp6_client_set_iaid(client, link->network->iaid); - if (r < 0) - goto error; - - duid = link_duid(link); - r = sd_dhcp6_client_set_duid(client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - goto error; - - r = sd_dhcp6_client_set_ifindex(client, link->ifindex); - if (r < 0) - goto error; - - r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); - if (r < 0) - goto error; - - link->dhcp6_client = client; - - return 0; - -error: - sd_dhcp6_client_unref(client); - return r; -} diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c deleted file mode 100644 index be8aebee2d..0000000000 --- a/src/network/networkd-fdb.c +++ /dev/null @@ -1,253 +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 . -***/ - -#include -#include - -#include "alloc-util.h" -#include "conf-parser.h" -#include "netlink-util.h" -#include "networkd-fdb.h" -#include "networkd.h" -#include "util.h" -#include "vlan-util.h" - -#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U - -/* create a new FDB entry or get an existing one. */ -int fdb_entry_new_static( - Network *network, - unsigned section, - FdbEntry **ret) { - - _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; - struct ether_addr *mac_addr = NULL; - - assert(network); - assert(ret); - - /* search entry in hashmap first. */ - if (section) { - fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section)); - if (fdb_entry) { - *ret = fdb_entry; - fdb_entry = NULL; - - return 0; - } - } - - if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX) - return -E2BIG; - - /* allocate space for MAC address. */ - mac_addr = new0(struct ether_addr, 1); - if (!mac_addr) - return -ENOMEM; - - /* allocate space for and FDB entry. */ - fdb_entry = new0(FdbEntry, 1); - if (!fdb_entry) { - /* free previously allocated space for mac_addr. */ - free(mac_addr); - return -ENOMEM; - } - - /* init FDB structure. */ - fdb_entry->network = network; - fdb_entry->mac_addr = mac_addr; - - LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry); - network->n_static_fdb_entries++; - - if (section) { - fdb_entry->section = section; - hashmap_put(network->fdb_entries_by_section, - UINT_TO_PTR(fdb_entry->section), fdb_entry); - } - - /* return allocated FDB structure. */ - *ret = fdb_entry; - fdb_entry = NULL; - - return 0; -} - -static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - Link *link = userdata; - int r; - - assert(link); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_error_errno(link, r, "Could not add FDB entry: %m"); - - return 1; -} - -/* send a request to the kernel to add a FDB entry in its static MAC table. */ -int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - sd_netlink *rtnl; - int r; - - assert(link); - assert(link->manager); - assert(fdb_entry); - - rtnl = link->manager->rtnl; - - /* create new RTM message */ - r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE); - if (r < 0) - return rtnl_log_create_error(r); - - /* only NTF_SELF flag supported. */ - r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF); - if (r < 0) - return rtnl_log_create_error(r); - - /* only NUD_PERMANENT state supported. */ - r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr); - if (r < 0) - return rtnl_log_create_error(r); - - /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */ - if (0 != fdb_entry->vlan_id) { - r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id); - if (r < 0) - return rtnl_log_create_error(r); - } - - /* send message to the kernel to update its internal static MAC table. */ - r = sd_netlink_call_async(rtnl, req, set_fdb_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - return 0; -} - -/* remove and FDB entry. */ -void fdb_entry_free(FdbEntry *fdb_entry) { - if (!fdb_entry) - return; - - if (fdb_entry->network) { - LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry); - - assert(fdb_entry->network->n_static_fdb_entries > 0); - fdb_entry->network->n_static_fdb_entries--; - - if (fdb_entry->section) - hashmap_remove(fdb_entry->network->fdb_entries_by_section, UINT_TO_PTR(fdb_entry->section)); - } - - free(fdb_entry->mac_addr); - - free(fdb_entry); -} - -/* parse the HW address from config files. */ -int config_parse_fdb_hwaddr( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = fdb_entry_new_static(network, section_line, &fdb_entry); - if (r < 0) - return log_oom(); - - /* read in the MAC address for the FDB table. */ - r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &fdb_entry->mac_addr->ether_addr_octet[0], - &fdb_entry->mac_addr->ether_addr_octet[1], - &fdb_entry->mac_addr->ether_addr_octet[2], - &fdb_entry->mac_addr->ether_addr_octet[3], - &fdb_entry->mac_addr->ether_addr_octet[4], - &fdb_entry->mac_addr->ether_addr_octet[5]); - - if (ETHER_ADDR_LEN != r) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); - return 0; - } - - fdb_entry = NULL; - - return 0; -} - -/* parse the VLAN Id from config files. */ -int config_parse_fdb_vlan_id( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = fdb_entry_new_static(network, section_line, &fdb_entry); - if (r < 0) - return log_oom(); - - r = config_parse_vlanid(unit, filename, line, section, - section_line, lvalue, ltype, - rvalue, &fdb_entry->vlan_id, userdata); - if (r < 0) - return r; - - fdb_entry = NULL; - - return 0; -} diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h deleted file mode 100644 index 2d7d28735c..0000000000 --- a/src/network/networkd-fdb.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "list.h" -#include "macro.h" - -typedef struct Network Network; -typedef struct FdbEntry FdbEntry; -typedef struct Link Link; - -struct FdbEntry { - Network *network; - unsigned section; - - struct ether_addr *mac_addr; - uint16_t vlan_id; - - LIST_FIELDS(FdbEntry, static_fdb_entries); -}; - -int fdb_entry_new_static(Network *network, unsigned section, FdbEntry **ret); -void fdb_entry_free(FdbEntry *fdb_entry); -int fdb_entry_configure(Link *link, FdbEntry *fdb_entry); - -DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free); -#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep) - -int config_parse_fdb_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_fdb_vlan_id(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf deleted file mode 100644 index 3fdfe74955..0000000000 --- a/src/network/networkd-gperf.gperf +++ /dev/null @@ -1,18 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "networkd-conf.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name networkd_gperf_hash -%define lookup-function-name networkd_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type) -DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c deleted file mode 100644 index 2d81311e81..0000000000 --- a/src/network/networkd-ipv4ll.c +++ /dev/null @@ -1,241 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013-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 . -***/ - -#include -#include - -#include "network-internal.h" -#include "networkd.h" - -static int ipv4ll_address_lost(Link *link) { - _cleanup_address_free_ Address *address = NULL; - _cleanup_route_free_ Route *route = NULL; - struct in_addr addr; - int r; - - assert(link); - - link->ipv4ll_route = false; - link->ipv4ll_address = false; - - r = sd_ipv4ll_get_address(link->ipv4ll, &addr); - if (r < 0) - return 0; - - log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr)); - - r = address_new(&address); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate address: %m"); - return r; - } - - address->family = AF_INET; - address->in_addr.in = addr; - address->prefixlen = 16; - address->scope = RT_SCOPE_LINK; - - address_remove(address, link, link_address_remove_handler); - - r = route_new(&route); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate route: %m"); - return r; - } - - route->family = AF_INET; - route->scope = RT_SCOPE_LINK; - route->priority = IPV4LL_ROUTE_METRIC; - - route_remove(route, link, link_route_remove_handler); - - link_check_ready(link); - - return 0; -} - -static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - assert(!link->ipv4ll_route); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - 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_check_ready(link); - - return 1; -} - -static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - assert(!link->ipv4ll_address); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "could not set ipv4ll address: %m"); - link_enter_failed(link); - } else if (r >= 0) - manager_rtnl_process_address(rtnl, m, link->manager); - - link->ipv4ll_address = true; - - if (link->ipv4ll_route == true) - link_check_ready(link); - - return 1; -} - -static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { - _cleanup_address_free_ Address *ll_addr = NULL; - _cleanup_route_free_ Route *route = NULL; - struct in_addr address; - int r; - - assert(ll); - assert(link); - - r = sd_ipv4ll_get_address(ll, &address); - if (r == -ENOENT) - return 0; - else if (r < 0) - return r; - - log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u", - ADDRESS_FMT_VAL(address)); - - r = address_new(&ll_addr); - if (r < 0) - return r; - - ll_addr->family = AF_INET; - ll_addr->in_addr.in = address; - ll_addr->prefixlen = 16; - ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); - ll_addr->scope = RT_SCOPE_LINK; - - r = address_configure(ll_addr, link, ipv4ll_address_handler, false); - if (r < 0) - return r; - - link->ipv4ll_address = false; - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET; - route->scope = RT_SCOPE_LINK; - route->protocol = RTPROT_STATIC; - route->priority = IPV4LL_ROUTE_METRIC; - - r = route_configure(route, link, ipv4ll_route_handler); - if (r < 0) - return r; - - link->ipv4ll_route = false; - - return 0; -} - -static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { - Link *link = userdata; - 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_IPV4LL_EVENT_STOP: - case SD_IPV4LL_EVENT_CONFLICT: - r = ipv4ll_address_lost(link); - if (r < 0) { - link_enter_failed(link); - return; - } - break; - case SD_IPV4LL_EVENT_BIND: - r = ipv4ll_address_claimed(ll, link); - if (r < 0) { - link_enter_failed(link); - return; - } - break; - default: - log_link_warning(link, "IPv4 link-local unknown event: %d", event); - break; - } -} - -int ipv4ll_configure(Link *link) { - uint64_t seed; - int r; - - assert(link); - assert(link->network); - assert(link->network->link_local & ADDRESS_FAMILY_IPV4); - - 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); - if (r >= 0) { - r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); - if (r < 0) - return r; - } - } - - r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); - if (r < 0) - return r; - - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); - if (r < 0) - return r; - - r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); - if (r < 0) - return r; - - r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); - if (r < 0) - return r; - - return 0; -} diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c deleted file mode 100644 index 532557ed6c..0000000000 --- a/src/network/networkd-link-bus.c +++ /dev/null @@ -1,140 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-util.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); - -const sd_bus_vtable link_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_VTABLE_END -}; - -static char *link_bus_path(Link *link) { - _cleanup_free_ char *ifindex = NULL; - char *p; - int r; - - assert(link); - assert(link->ifindex > 0); - - if (asprintf(&ifindex, "%d", link->ifindex) < 0) - return NULL; - - r = sd_bus_path_encode("/org/freedesktop/network1/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; - unsigned c = 0; - Link *link; - Iterator i; - - 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; -} - -int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - _cleanup_free_ char *identifier = NULL; - Manager *m = userdata; - Link *link; - int ifindex, r; - - assert(bus); - assert(path); - assert(interface); - assert(m); - assert(found); - - r = sd_bus_path_decode(path, "/org/freedesktop/network1/link", &identifier); - if (r <= 0) - return 0; - - r = parse_ifindex(identifier, &ifindex); - if (r < 0) - return 0; - - r = link_get(m, ifindex, &link); - if (r < 0) - return 0; - - *found = link; - - return 1; -} - -int link_send_changed(Link *link, const char *property, ...) { - _cleanup_free_ char *p = NULL; - char **l; - - assert(link); - assert(link->manager); - - if (!link->manager->bus) - return 0; /* replace with assert when we have kdbus */ - - l = strv_from_stdarg_alloca(property); - - p = link_bus_path(link); - if (!p) - return -ENOMEM; - - return sd_bus_emit_properties_changed_strv( - link->manager->bus, - p, - "org.freedesktop.network1.Link", - l); -} diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c deleted file mode 100644 index 82f56158be..0000000000 --- a/src/network/networkd-link.c +++ /dev/null @@ -1,3410 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "bus-util.h" -#include "dhcp-lease-internal.h" -#include "fd-util.h" -#include "fileio.h" -#include "netlink-util.h" -#include "network-internal.h" -#include "networkd-lldp-tx.h" -#include "networkd-ndisc.h" -#include "networkd.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" - -static bool link_dhcp6_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - return link->network->dhcp & ADDRESS_FAMILY_IPV6; -} - -static bool link_dhcp4_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - return link->network->dhcp & ADDRESS_FAMILY_IPV4; -} - -static bool link_dhcp4_server_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - return link->network->dhcp_server; -} - -static bool link_ipv4ll_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - return link->network->link_local & ADDRESS_FAMILY_IPV4; -} - -static bool link_ipv6ll_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - return link->network->link_local & ADDRESS_FAMILY_IPV6; -} - -static bool link_ipv6_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->network->bridge) - return false; - - /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */ - return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); -} - -static bool link_lldp_rx_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (link->iftype != ARPHRD_ETHER) - return false; - - if (!link->network) - return false; - - if (link->network->bridge) - return false; - - return link->network->lldp_mode != LLDP_MODE_NO; -} - -static bool link_lldp_emit_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (link->iftype != ARPHRD_ETHER) - return false; - - if (!link->network) - return false; - - return link->network->lldp_emit != LLDP_EMIT_NO; -} - -static bool link_ipv4_forward_enabled(Link *link) { - assert(link); - - 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_IPV4; -} - -static bool link_ipv6_forward_enabled(Link *link) { - assert(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; -} - -static bool link_proxy_arp_enabled(Link *link) { - assert(link); - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->network->proxy_arp < 0) - return false; - - return true; -} - -static bool link_ipv6_accept_ra_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - 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) { - assert(link); - - if (!socket_ipv6_is_supported()) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - if (link->flags & IFF_LOOPBACK) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - if (!link->network) - return _IPV6_PRIVACY_EXTENSIONS_INVALID; - - return link->network->ipv6_privacy_extensions; -} - -static int link_enable_ipv6(Link *link) { - const char *p = NULL; - bool disabled; - int r; - - if (link->flags & IFF_LOOPBACK) - return 0; - - disabled = !link_ipv6_enabled(link); - - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); - - r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); - if (r < 0) - log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); - else { - if (disabled) - log_link_info(link, "IPv6 disabled for interface: %m"); - else - log_link_info(link, "IPv6 enabled for interface: %m"); - } - - return 0; -} - -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); - } -} - -#define FLAG_STRING(string, flag, old, new) \ - (((old ^ new) & flag) \ - ? ((old & flag) ? (" -" string) : (" +" string)) \ - : "") - -static int link_update_flags(Link *link, sd_netlink_message *m) { - unsigned flags, unknown_flags_added, unknown_flags_removed, unknown_flags; - uint8_t operstate; - int r; - - assert(link); - - r = sd_rtnl_message_link_get_flags(m, &flags); - if (r < 0) - return log_link_warning_errno(link, r, "Could not get link flags: %m"); - - r = sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &operstate); - if (r < 0) - /* if we got a message without operstate, take it to mean - the state was unchanged */ - operstate = link->kernel_operstate; - - if ((link->flags == flags) && (link->kernel_operstate == operstate)) - return 0; - - if (link->flags != flags) { - log_link_debug(link, "Flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags), - FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags), - FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags), - FLAG_STRING("UP", IFF_UP, link->flags, flags), - FLAG_STRING("DORMANT", IFF_DORMANT, link->flags, flags), - FLAG_STRING("LOWER_UP", IFF_LOWER_UP, link->flags, flags), - FLAG_STRING("RUNNING", IFF_RUNNING, link->flags, flags), - FLAG_STRING("MULTICAST", IFF_MULTICAST, link->flags, flags), - FLAG_STRING("BROADCAST", IFF_BROADCAST, link->flags, flags), - FLAG_STRING("POINTOPOINT", IFF_POINTOPOINT, link->flags, flags), - FLAG_STRING("PROMISC", IFF_PROMISC, link->flags, flags), - FLAG_STRING("ALLMULTI", IFF_ALLMULTI, link->flags, flags), - FLAG_STRING("PORTSEL", IFF_PORTSEL, link->flags, flags), - FLAG_STRING("AUTOMEDIA", IFF_AUTOMEDIA, link->flags, flags), - FLAG_STRING("DYNAMIC", IFF_DYNAMIC, link->flags, flags), - FLAG_STRING("NOARP", IFF_NOARP, link->flags, flags), - FLAG_STRING("NOTRAILERS", IFF_NOTRAILERS, link->flags, flags), - FLAG_STRING("DEBUG", IFF_DEBUG, link->flags, flags), - FLAG_STRING("ECHO", IFF_ECHO, link->flags, flags)); - - unknown_flags = ~(IFF_LOOPBACK | IFF_MASTER | IFF_SLAVE | IFF_UP | - IFF_DORMANT | IFF_LOWER_UP | IFF_RUNNING | - IFF_MULTICAST | IFF_BROADCAST | IFF_POINTOPOINT | - IFF_PROMISC | IFF_ALLMULTI | IFF_PORTSEL | - IFF_AUTOMEDIA | IFF_DYNAMIC | IFF_NOARP | - IFF_NOTRAILERS | IFF_DEBUG | IFF_ECHO); - unknown_flags_added = ((link->flags ^ flags) & flags & unknown_flags); - unknown_flags_removed = ((link->flags ^ flags) & link->flags & unknown_flags); - - /* link flags are currently at most 18 bits, let's align to - * printing 20 */ - if (unknown_flags_added) - log_link_debug(link, - "Unknown link flags gained: %#.5x (ignoring)", - unknown_flags_added); - - if (unknown_flags_removed) - log_link_debug(link, - "Unknown link flags lost: %#.5x (ignoring)", - unknown_flags_removed); - } - - link->flags = flags; - link->kernel_operstate = operstate; - - link_update_operstate(link); - - return 0; -} - -static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { - _cleanup_link_unref_ Link *link = NULL; - uint16_t type; - const char *ifname, *kind = NULL; - int r, ifindex; - unsigned short iftype; - - assert(manager); - assert(message); - assert(ret); - - /* check for link kind */ - r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); - if (r == 0) { - (void)sd_netlink_message_read_string(message, IFLA_INFO_KIND, &kind); - r = sd_netlink_message_exit_container(message); - if (r < 0) - return r; - } - - r = sd_netlink_message_get_type(message, &type); - if (r < 0) - return r; - else if (type != RTM_NEWLINK) - return -EINVAL; - - r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0) - return r; - else if (ifindex <= 0) - return -EINVAL; - - r = sd_rtnl_message_link_get_type(message, &iftype); - if (r < 0) - return r; - - r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname); - if (r < 0) - return r; - - link = new0(Link, 1); - if (!link) - return -ENOMEM; - - link->n_ref = 1; - link->manager = manager; - link->state = LINK_STATE_PENDING; - link->rtnl_extended_attrs = true; - link->ifindex = ifindex; - link->iftype = iftype; - link->ifname = strdup(ifname); - if (!link->ifname) - return -ENOMEM; - - if (kind) { - link->kind = strdup(kind); - if (!link->kind) - return -ENOMEM; - } - - r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); - if (r < 0) - log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); - - if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) - return -ENOMEM; - - if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) - return -ENOMEM; - - if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) - return -ENOMEM; - - r = hashmap_ensure_allocated(&manager->links, NULL); - if (r < 0) - return r; - - r = hashmap_put(manager->links, INT_TO_PTR(link->ifindex), link); - if (r < 0) - return r; - - r = link_update_flags(link, message); - if (r < 0) - return r; - - *ret = link; - link = NULL; - - return 0; -} - -static void link_free(Link *link) { - Address *address; - Iterator i; - Link *carrier; - - if (!link) - return; - - 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); - address_free(address); - } - - sd_dhcp_server_unref(link->dhcp_server); - sd_dhcp_client_unref(link->dhcp_client); - sd_dhcp_lease_unref(link->dhcp_lease); - - link_lldp_emit_stop(link); - - free(link->lease_file); - - sd_lldp_unref(link->lldp); - free(link->lldp_file); - - sd_ipv4ll_unref(link->ipv4ll); - sd_dhcp6_client_unref(link->dhcp6_client); - sd_ndisc_unref(link->ndisc); - - set_free_free(link->ndisc_rdnss); - set_free_free(link->ndisc_dnssl); - - if (link->manager) - hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); - - free(link->ifname); - - free(link->kind); - - (void)unlink(link->state_file); - free(link->state_file); - - udev_device_unref(link->udev_device); - - HASHMAP_FOREACH (carrier, link->bound_to_links, i) - hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex)); - hashmap_free(link->bound_to_links); - - HASHMAP_FOREACH (carrier, link->bound_by_links, i) - hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); - hashmap_free(link->bound_by_links); - - free(link); -} - -Link *link_unref(Link *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) - return NULL; - - assert(link->n_ref > 0); - - link->n_ref++; - - return link; -} - -int link_get(Manager *m, int ifindex, Link **ret) { - Link *link; - - assert(m); - assert(ifindex); - assert(ret); - - link = hashmap_get(m->links, INT_TO_PTR(ifindex)); - if (!link) - return -ENODEV; - - *ret = link; - - return 0; -} - -static void link_set_state(Link *link, LinkState state) { - assert(link); - - if (link->state == state) - return; - - link->state = state; - - link_send_changed(link, "AdministrativeState", NULL); -} - -static void link_enter_unmanaged(Link *link) { - assert(link); - - log_link_debug(link, "Unmanaged"); - - link_set_state(link, LINK_STATE_UNMANAGED); - - link_dirty(link); -} - -static int link_stop_clients(Link *link) { - int r = 0, k; - - assert(link); - assert(link->manager); - assert(link->manager->event); - - if (link->dhcp_client) { - k = sd_dhcp_client_stop(link->dhcp_client); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); - } - - if (link->ipv4ll) { - k = sd_ipv4ll_stop(link->ipv4ll); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); - } - - if (link->dhcp6_client) { - k = sd_dhcp6_client_stop(link->dhcp6_client); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); - } - - if (link->ndisc) { - k = sd_ndisc_stop(link->ndisc); - if (k < 0) - r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); - } - - link_lldp_emit_stop(link); - return r; -} - -void link_enter_failed(Link *link) { - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - log_link_warning(link, "Failed"); - - link_set_state(link, LINK_STATE_FAILED); - - link_stop_clients(link); - - link_dirty(link); -} - -static Address* link_find_dhcp_server_address(Link *link) { - Address *address; - - assert(link); - assert(link->network); - - /* The first statically configured address if there is any */ - LIST_FOREACH(addresses, address, link->network->static_addresses) { - - if (address->family != AF_INET) - continue; - - if (in_addr_is_null(address->family, &address->in_addr)) - continue; - - return address; - } - - /* If that didn't work, find a suitable address we got from the pool */ - LIST_FOREACH(addresses, address, link->pool_addresses) { - if (address->family != AF_INET) - continue; - - return address; - } - - return NULL; -} - -static int link_enter_configured(Link *link) { - assert(link); - assert(link->network); - assert(link->state == LINK_STATE_SETTING_ROUTES); - - log_link_info(link, "Configured"); - - link_set_state(link, LINK_STATE_CONFIGURED); - - link_dirty(link); - - return 0; -} - -void link_check_ready(Link *link) { - Address *a; - Iterator i; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - if (!link->network) - return; - - if (!link->static_configured) - return; - - if (link_ipv4ll_enabled(link)) - if (!link->ipv4ll_address || - !link->ipv4ll_route) - return; - - if (link_ipv6ll_enabled(link)) - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) - return; - - 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); - - return; -} - -static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link->link_messages > 0); - assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, - LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED, - LINK_STATE_LINGER)); - - link->link_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - 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_check_ready(link); - } - - return 1; -} - -static int link_enter_set_routes(Link *link) { - Route *rt; - int r; - - assert(link); - assert(link->network); - assert(link->state == LINK_STATE_SETTING_ADDRESSES); - - link_set_state(link, LINK_STATE_SETTING_ROUTES); - - LIST_FOREACH(routes, rt, link->network->static_routes) { - r = route_configure(rt, link, route_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set routes: %m"); - link_enter_failed(link); - return r; - } - - link->link_messages++; - } - - if (link->link_messages == 0) { - link->static_configured = true; - link_check_ready(link); - } else - log_link_debug(link, "Setting routes"); - - return 0; -} - -int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(m); - assert(link); - assert(link->ifname); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -ESRCH) - log_link_warning_errno(link, r, "Could not drop route: %m"); - - return 1; -} - -static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(rtnl); - assert(m); - assert(link); - assert(link->ifname); - assert(link->link_messages > 0); - assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, - LINK_STATE_FAILED, LINK_STATE_LINGER)); - - link->link_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_warning_errno(link, r, "could not set address: %m"); - else if (r >= 0) - manager_rtnl_process_address(rtnl, m, link->manager); - - if (link->link_messages == 0) { - log_link_debug(link, "Addresses set"); - link_enter_set_routes(link); - } - - return 1; -} - -static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { - _cleanup_free_ struct in_addr *addresses = NULL; - size_t n_addresses = 0, n_allocated = 0; - char **a; - - log_debug("Copying DNS server information from %s", link->ifname); - - if (!link->network) - return 0; - - STRV_FOREACH(a, link->network->dns) { - struct in_addr ia; - - /* Only look for IPv4 addresses */ - if (inet_pton(AF_INET, *a, &ia) <= 0) - continue; - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) - return log_oom(); - - addresses[n_addresses++] = ia; - } - - if (link->network->dhcp_use_dns && - link->dhcp_lease) { - const struct in_addr *da = NULL; - int n; - - n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); - if (n > 0) { - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) - return log_oom(); - - memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); - n_addresses += n; - } - } - - if (n_addresses <= 0) - return 0; - - return sd_dhcp_server_set_dns(s, addresses, n_addresses); -} - -static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { - _cleanup_free_ struct in_addr *addresses = NULL; - size_t n_addresses = 0, n_allocated = 0; - char **a; - - if (!link->network) - return 0; - - log_debug("Copying NTP server information from %s", link->ifname); - - STRV_FOREACH(a, link->network->ntp) { - struct in_addr ia; - - /* Only look for IPv4 addresses */ - if (inet_pton(AF_INET, *a, &ia) <= 0) - continue; - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) - return log_oom(); - - addresses[n_addresses++] = ia; - } - - if (link->network->dhcp_use_ntp && - link->dhcp_lease) { - const struct in_addr *da = NULL; - int n; - - n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da); - if (n > 0) { - - if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) - return log_oom(); - - memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); - n_addresses += n; - } - } - - if (n_addresses <= 0) - return 0; - - return sd_dhcp_server_set_ntp(s, addresses, n_addresses); -} - -static int link_enter_set_addresses(Link *link) { - Address *ad; - int r; - - assert(link); - assert(link->network); - assert(link->state != _LINK_STATE_INVALID); - - link_set_state(link, LINK_STATE_SETTING_ADDRESSES); - - LIST_FOREACH(addresses, ad, link->network->static_addresses) { - 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); - return r; - } - - link->link_messages++; - } - - /* now that we can figure out a default address for the dhcp server, - start it */ - if (link_dhcp4_server_enabled(link)) { - Address *address; - Link *uplink = NULL; - bool acquired_uplink = false; - - address = link_find_dhcp_server_address(link); - if (!address) { - log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance."); - link_enter_failed(link); - return 0; - } - - /* use the server address' subnet as the pool */ - r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, - link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); - if (r < 0) - return r; - - /* TODO: - r = sd_dhcp_server_set_router(link->dhcp_server, - &main_address->in_addr.in); - if (r < 0) - return r; - */ - - if (link->network->dhcp_server_max_lease_time_usec > 0) { - r = sd_dhcp_server_set_max_lease_time( - link->dhcp_server, - DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); - if (r < 0) - return r; - } - - if (link->network->dhcp_server_default_lease_time_usec > 0) { - r = sd_dhcp_server_set_default_lease_time( - link->dhcp_server, - DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); - if (r < 0) - return r; - } - - if (link->network->dhcp_server_emit_dns) { - - if (link->network->n_dhcp_server_dns > 0) - r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns); - else { - uplink = manager_find_uplink(link->manager, link); - acquired_uplink = true; - - if (!uplink) { - log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); - r = 0; - } else - r = link_push_dns_to_dhcp_server(uplink, link->dhcp_server); - } - if (r < 0) - log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); - } - - - if (link->network->dhcp_server_emit_ntp) { - - if (link->network->n_dhcp_server_ntp > 0) - r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp); - else { - if (!acquired_uplink) - uplink = manager_find_uplink(link->manager, link); - - if (!uplink) { - log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); - r = 0; - } else - r = link_push_ntp_to_dhcp_server(uplink, link->dhcp_server); - - } - if (r < 0) - log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); - } - - r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m"); - return r; - } - - if (link->network->dhcp_server_emit_timezone) { - _cleanup_free_ char *buffer = NULL; - const char *tz = NULL; - - if (link->network->dhcp_server_timezone) - tz = link->network->dhcp_server_timezone; - else { - r = get_timezone(&buffer); - if (r < 0) - log_warning_errno(r, "Failed to determine timezone: %m"); - else - tz = buffer; - } - - if (tz) { - r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); - if (r < 0) - return r; - } - } - - r = sd_dhcp_server_start(link->dhcp_server); - if (r < 0) { - log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m"); - - link_enter_failed(link); - - return 0; - } - - log_link_debug(link, "Offering DHCPv4 leases"); - } - - if (link->link_messages == 0) - link_enter_set_routes(link); - else - log_link_debug(link, "Setting addresses"); - - return 0; -} - -int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(m); - assert(link); - assert(link->ifname); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EADDRNOTAVAIL) - log_link_warning_errno(link, r, "Could not drop address: %m"); - - return 1; -} - -static int link_set_bridge_vlan(Link *link) { - int r = 0; - - r = br_vlan_configure(link, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); - if (r < 0) - log_link_error_errno(link, r, "Failed to assign VLANs to bridge port: %m"); - - return r; -} - -static int link_set_bridge_fdb(Link *link) { - FdbEntry *fdb_entry; - int r = 0; - - LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { - r = fdb_entry_configure(link, fdb_entry); - if (r < 0) { - log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); - break; - } - } - - return r; -} - -static int link_set_proxy_arp(Link *link) { - const char *p = NULL; - int r; - - if (!link_proxy_arp_enabled(link)) - return 0; - - p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); - - r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE); - if (r < 0) - log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); - - return 0; -} - -static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - log_link_debug(link, "Set link"); - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "Could not join netdev: %m"); - link_enter_failed(link); - return 1; - } - - return 0; -} - -static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - _cleanup_link_unref_ Link *link = userdata; - const sd_bus_error *e; - - assert(m); - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - e = sd_bus_message_get_error(m); - if (e) - log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message); - - return 1; -} - -int link_set_hostname(Link *link, const char *hostname) { - int r; - - assert(link); - assert(link->manager); - - log_link_debug(link, "Setting transient hostname: '%s'", strna(hostname)); - - if (!link->manager->bus) { - /* TODO: replace by assert when we can rely on kdbus */ - log_link_info(link, "Not connected to system bus, ignoring transient hostname."); - return 0; - } - - r = sd_bus_call_method_async( - link->manager->bus, - NULL, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - "org.freedesktop.hostname1", - "SetHostname", - set_hostname_handler, - link, - "sb", - hostname, - false); - - if (r < 0) - return log_link_error_errno(link, r, "Could not set transient hostname: %m"); - - link_ref(link); - - return 0; -} - -static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - _cleanup_link_unref_ Link *link = userdata; - const sd_bus_error *e; - - assert(m); - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - e = sd_bus_message_get_error(m); - if (e) - log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message); - - return 1; -} - -int link_set_timezone(Link *link, const char *tz) { - int r; - - assert(link); - assert(link->manager); - assert(tz); - - log_link_debug(link, "Setting system timezone: '%s'", tz); - - if (!link->manager->bus) { - log_link_info(link, "Not connected to system bus, ignoring timezone."); - return 0; - } - - r = sd_bus_call_method_async( - link->manager->bus, - NULL, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetTimezone", - set_timezone_handler, - link, - "sb", - tz, - false); - if (r < 0) - return log_link_error_errno(link, r, "Could not set timezone: %m"); - - link_ref(link); - - return 0; -} - -static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(m); - assert(link); - assert(link->ifname); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_warning_errno(link, r, "Could not set MTU: %m"); - - return 1; -} - -int link_set_mtu(Link *link, uint32_t mtu) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - - log_link_debug(link, "Setting MTU: %" PRIu32, mtu); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_netlink_message_append_u32(req, IFLA_MTU, mtu); - if (r < 0) - return log_link_error_errno(link, r, "Could not append MTU: %m"); - - r = sd_netlink_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -static int link_set_bridge(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->network); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); - if (r < 0) - return log_link_error_errno(link, r, "Could not set message family: %m"); - - r = sd_netlink_message_open_container(req, IFLA_PROTINFO); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_PROTINFO attribute: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); - - if (link->network->cost != 0) { - r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = sd_netlink_call_async(link->manager->rtnl, req, link_set_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return r; -} - -static int link_lldp_save(Link *link) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - sd_lldp_neighbor **l = NULL; - int n = 0, r, i; - - assert(link); - assert(link->lldp_file); - - if (!link->lldp) { - (void) unlink(link->lldp_file); - return 0; - } - - r = sd_lldp_get_neighbors(link->lldp, &l); - if (r < 0) - goto finish; - if (r == 0) { - (void) unlink(link->lldp_file); - goto finish; - } - - n = r; - - r = fopen_temporary(link->lldp_file, &f, &temp_path); - if (r < 0) - goto finish; - - fchmod(fileno(f), 0644); - - for (i = 0; i < n; i++) { - const void *p; - le64_t u; - size_t sz; - - r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); - if (r < 0) - goto finish; - - u = htole64(sz); - (void) fwrite(&u, 1, sizeof(u), f); - (void) fwrite(p, 1, sz, f); - } - - r = fflush_and_check(f); - if (r < 0) - goto finish; - - if (rename(temp_path, link->lldp_file) < 0) { - r = -errno; - goto finish; - } - -finish: - if (r < 0) { - (void) unlink(link->lldp_file); - if (temp_path) - (void) unlink(temp_path); - - log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); - } - - if (l) { - for (i = 0; i < n; i++) - sd_lldp_neighbor_unref(l[i]); - free(l); - } - - return r; -} - -static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { - Link *link = userdata; - int r; - - assert(link); - - (void) link_lldp_save(link); - - if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { - /* If we received information about a new neighbor, restart the LLDP "fast" logic */ - - log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); - - r = link_lldp_emit_start(link); - if (r < 0) - log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); - } -} - -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); - - /* start DHCPv6 client in stateless mode */ - r = dhcp6_request_address(link, true); - if (r < 0 && r != -EBUSY) - return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); - else - log_link_debug(link, "Acquiring DHCPv6 lease"); - } - - if (link_ipv6_accept_ra_enabled(link)) { - assert(link->ndisc); - - log_link_debug(link, "Discovering IPv6 routers"); - - r = sd_ndisc_start(link->ndisc); - 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_ipv4_conf(Link *link) { - int r; - - assert(link); - assert(link->network); - assert(link->manager); - assert(link->manager->event); - - if (link_ipv4ll_enabled(link)) { - assert(link->ipv4ll); - - log_link_debug(link, "Acquiring IPv4 link-local address"); - - r = sd_ipv4ll_start(link->ipv4ll); - if (r < 0) - return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); - } - - if (link_dhcp4_enabled(link)) { - assert(link->dhcp_client); - - log_link_debug(link, "Acquiring DHCPv4 lease"); - - r = sd_dhcp_client_start(link->dhcp_client); - if (r < 0) - return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); - } - - return 0; -} - -static int link_acquire_conf(Link *link) { - int r; - - assert(link); - - r = link_acquire_ipv4_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; - } - - if (link_lldp_emit_enabled(link)) { - r = link_lldp_emit_start(link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); - } - - return 0; -} - -bool link_has_carrier(Link *link) { - /* see Documentation/networking/operstates.txt in the kernel sources */ - - if (link->kernel_operstate == IF_OPER_UP) - return true; - - if (link->kernel_operstate == IF_OPER_UNKNOWN) - /* operstate may not be implemented, so fall back to flags */ - if ((link->flags & IFF_LOWER_UP) && !(link->flags & IFF_DORMANT)) - return true; - - return false; -} - -static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - /* we warn but don't fail the link, as it may be - brought up later */ - log_link_warning_errno(link, r, "Could not bring up interface: %m"); - - return 1; -} - -static int link_up(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - uint8_t ipv6ll_mode; - int r; - - assert(link); - assert(link->network); - assert(link->manager); - assert(link->manager->rtnl); - - log_link_debug(link, "Bringing link up"); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - /* set it free if not enslaved with networkd */ - if (!link->network->bridge && !link->network->bond && !link->network->vrf) { - r = sd_netlink_message_append_u32(req, IFLA_MASTER, 0); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_MASTER attribute: %m"); - } - - r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); - if (r < 0) - return log_link_error_errno(link, r, "Could not set link flags: %m"); - - if (link->network->mac) { - r = sd_netlink_message_append_ether_addr(req, IFLA_ADDRESS, link->network->mac); - if (r < 0) - return log_link_error_errno(link, r, "Could not set MAC address: %m"); - } - - /* If IPv6 not configured (no static IPv6 address and IPv6LL autoconfiguration is disabled) - for this interface, or if it is a bridge slave, then disable IPv6 else enable it. */ - (void) link_enable_ipv6(link); - - if (link->network->mtu) { - /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes - on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */ - if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) { - - log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " - "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); - - link->network->mtu = IPV6_MIN_MTU; - } - - r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu); - if (r < 0) - return log_link_error_errno(link, r, "Could not set MTU: %m"); - } - - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - - if (link_ipv6_enabled(link)) { - /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ - r = sd_netlink_message_open_container(req, AF_INET6); - if (r < 0) - return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m"); - - if (!link_ipv6ll_enabled(link)) - ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE; - else { - const char *p = NULL; - _cleanup_free_ char *stable_secret = NULL; - - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/stable_secret"); - r = read_one_line_file(p, &stable_secret); - - if (r < 0) - ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; - else - ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; - } - r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m"); - - if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) { - r = sd_netlink_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); - - r = sd_netlink_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_warning_errno(link, r, "Could not bring down interface: %m"); - - return 1; -} - -static int link_down(Link *link) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - - log_link_debug(link, "Bringing link down"); - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, - RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); - if (r < 0) - return log_link_error_errno(link, r, "Could not set link flags: %m"); - - r = sd_netlink_call_async(link->manager->rtnl, req, link_down_handler, link, 0, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -static int link_handle_bound_to_list(Link *link) { - Link *l; - Iterator i; - int r; - bool required_up = false; - bool link_is_up = false; - - assert(link); - - if (hashmap_isempty(link->bound_to_links)) - return 0; - - if (link->flags & IFF_UP) - link_is_up = true; - - HASHMAP_FOREACH (l, link->bound_to_links, i) - if (link_has_carrier(l)) { - required_up = true; - break; - } - - if (!required_up && link_is_up) { - r = link_down(link); - if (r < 0) - return r; - } else if (required_up && !link_is_up) { - r = link_up(link); - if (r < 0) - return r; - } - - return 0; -} - -static int link_handle_bound_by_list(Link *link) { - Iterator i; - Link *l; - int r; - - assert(link); - - if (hashmap_isempty(link->bound_by_links)) - return 0; - - HASHMAP_FOREACH (l, link->bound_by_links, i) { - r = link_handle_bound_to_list(l); - if (r < 0) - return r; - } - - return 0; -} - -static int link_put_carrier(Link *link, Link *carrier, Hashmap **h) { - int r; - - assert(link); - assert(carrier); - - if (link == carrier) - return 0; - - if (hashmap_get(*h, INT_TO_PTR(carrier->ifindex))) - return 0; - - r = hashmap_ensure_allocated(h, NULL); - if (r < 0) - return r; - - r = hashmap_put(*h, INT_TO_PTR(carrier->ifindex), carrier); - if (r < 0) - return r; - - return 0; -} - -static int link_new_bound_by_list(Link *link) { - Manager *m; - Link *carrier; - Iterator i; - int r; - bool list_updated = false; - - assert(link); - assert(link->manager); - - m = link->manager; - - HASHMAP_FOREACH(carrier, m->links, i) { - if (!carrier->network) - continue; - - if (strv_isempty(carrier->network->bind_carrier)) - continue; - - if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) { - r = link_put_carrier(link, carrier, &link->bound_by_links); - if (r < 0) - return r; - - list_updated = true; - } - } - - if (list_updated) - 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_dirty(carrier); - } - - return 0; -} - -static int link_new_bound_to_list(Link *link) { - Manager *m; - Link *carrier; - Iterator i; - int r; - bool list_updated = false; - - assert(link); - assert(link->manager); - - if (!link->network) - return 0; - - if (strv_isempty(link->network->bind_carrier)) - return 0; - - m = link->manager; - - HASHMAP_FOREACH (carrier, m->links, i) { - if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) { - r = link_put_carrier(link, carrier, &link->bound_to_links); - if (r < 0) - return r; - - list_updated = true; - } - } - - if (list_updated) - 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_dirty(carrier); - } - - return 0; -} - -static int link_new_carrier_maps(Link *link) { - int r; - - r = link_new_bound_by_list(link); - if (r < 0) - return r; - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - r = link_handle_bound_to_list(link); - if (r < 0) - return r; - - return 0; -} - -static void link_free_bound_to_list(Link *link) { - Link *bound_to; - Iterator i; - - HASHMAP_FOREACH (bound_to, link->bound_to_links, i) { - hashmap_remove(link->bound_to_links, INT_TO_PTR(bound_to->ifindex)); - - if (hashmap_remove(bound_to->bound_by_links, INT_TO_PTR(link->ifindex))) - link_dirty(bound_to); - } - - return; -} - -static void link_free_bound_by_list(Link *link) { - Link *bound_by; - Iterator i; - - HASHMAP_FOREACH (bound_by, link->bound_by_links, i) { - hashmap_remove(link->bound_by_links, INT_TO_PTR(bound_by->ifindex)); - - if (hashmap_remove(bound_by->bound_to_links, INT_TO_PTR(link->ifindex))) { - link_dirty(bound_by); - link_handle_bound_to_list(bound_by); - } - } - - return; -} - -static void link_free_carrier_maps(Link *link) { - bool list_updated = false; - - assert(link); - - if (!hashmap_isempty(link->bound_to_links)) { - link_free_bound_to_list(link); - list_updated = true; - } - - if (!hashmap_isempty(link->bound_by_links)) { - link_free_bound_by_list(link); - list_updated = true; - } - - if (list_updated) - link_dirty(link); - - return; -} - -void link_drop(Link *link) { - if (!link || link->state == LINK_STATE_LINGER) - return; - - link_set_state(link, LINK_STATE_LINGER); - - link_free_carrier_maps(link); - - log_link_debug(link, "Link removed"); - - (void)unlink(link->state_file); - link_unref(link); - - return; -} - -static int link_joined(Link *link) { - int r; - - assert(link); - assert(link->network); - - if (!hashmap_isempty(link->bound_to_links)) { - r = link_handle_bound_to_list(link); - if (r < 0) - return r; - } else if (!(link->flags & IFF_UP)) { - r = link_up(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - } - - if (link->network->bridge) { - r = link_set_bridge(link); - if (r < 0) - log_link_error_errno(link, r, "Could not set bridge message: %m"); - } - - if (link->network->bridge || streq_ptr("bridge", link->kind)) { - r = link_set_bridge_vlan(link); - if (r < 0) - log_link_error_errno(link, r, "Could not set bridge vlan: %m"); - } - - return link_enter_set_addresses(link); -} - -static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(link); - assert(link->network); - - link->enslaving--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "Could not join netdev: %m"); - link_enter_failed(link); - return 1; - } else - log_link_debug(link, "Joined netdev"); - - if (link->enslaving <= 0) - link_joined(link); - - return 1; -} - -static int link_enter_join_netdev(Link *link) { - NetDev *netdev; - Iterator i; - int r; - - assert(link); - assert(link->network); - assert(link->state == LINK_STATE_PENDING); - - link_set_state(link, LINK_STATE_ENSLAVING); - - link_dirty(link); - - if (!link->network->bridge && - !link->network->bond && - !link->network->vrf && - hashmap_isempty(link->network->stacked_netdevs)) - return link_joined(link); - - if (link->network->bond) { - log_struct(LOG_DEBUG, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->bond), - LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname), - NULL); - - r = netdev_join(link->network->bond, link, netdev_join_handler); - if (r < 0) { - log_struct_errno(LOG_WARNING, r, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->bond), - LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bond->ifname), - NULL); - - link_enter_failed(link); - return r; - } - - link->enslaving++; - } - - if (link->network->bridge) { - log_struct(LOG_DEBUG, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->bridge), - LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname), - NULL); - - r = netdev_join(link->network->bridge, link, netdev_join_handler); - if (r < 0) { - log_struct_errno(LOG_WARNING, r, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->bridge), - LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->bridge->ifname), - NULL), - link_enter_failed(link); - return r; - } - - link->enslaving++; - } - - if (link->network->vrf) { - log_struct(LOG_DEBUG, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->vrf), - LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->vrf->ifname), - NULL); - r = netdev_join(link->network->vrf, link, netdev_join_handler); - if (r < 0) { - log_struct_errno(LOG_WARNING, r, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(link->network->vrf), - LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", link->network->vrf->ifname), - NULL); - link_enter_failed(link); - return r; - } - - link->enslaving++; - } - - HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { - - log_struct(LOG_DEBUG, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(netdev), - LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname), - NULL); - - r = netdev_join(netdev, link, netdev_join_handler); - if (r < 0) { - log_struct_errno(LOG_WARNING, r, - LOG_LINK_INTERFACE(link), - LOG_NETDEV_INTERFACE(netdev), - LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname), - NULL); - link_enter_failed(link); - return r; - } - - link->enslaving++; - } - - return 0; -} - -static int link_set_ipv4_forward(Link *link) { - int r; - - if (!link_ipv4_forward_enabled(link)) - return 0; - - /* 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; - - /* On Linux, the IPv6 stack does 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("/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"); - - 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_accept_ra(Link *link) { - const char *p = NULL; - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) - return 0; - - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - - /* We handle router advertisements 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_dad_transmits(Link *link) { - char buf[DECIMAL_STR_MAX(int) + 1]; - const char *p = NULL; - int r; - - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 0; - - if (!link->network) - return 0; - - if (link->network->ipv6_dad_transmits < 0) - return 0; - - 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_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 */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) - return 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; - } - - SET_FOREACH(route, link->routes_foreign, i) { - /* do not touch routes managed by the kernel */ - if (route->protocol == RTPROT_KERNEL) - continue; - - r = route_remove(route, link, link_route_remove_handler); - if (r < 0) - return r; - } - - return 0; -} - -static int link_update_lldp(Link *link) { - int r; - - assert(link); - - if (!link->lldp) - return 0; - - if (link->flags & IFF_UP) { - r = sd_lldp_start(link->lldp); - if (r > 0) - log_link_debug(link, "Started LLDP."); - } else { - r = sd_lldp_stop(link->lldp); - if (r > 0) - log_link_debug(link, "Stopped LLDP."); - } - - return r; -} - -static int link_configure(Link *link) { - int r; - - assert(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; - - r = link_set_proxy_arp(link); - if (r < 0) - return r; - - r = link_set_ipv4_forward(link); - if (r < 0) - return r; - - r = link_set_ipv6_forward(link); - if (r < 0) - return r; - - r = link_set_ipv6_privacy_extensions(link); - if (r < 0) - return r; - - r = link_set_ipv6_accept_ra(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) - return r; - } - - if (link_dhcp4_enabled(link)) { - r = dhcp4_configure(link); - if (r < 0) - return r; - } - - if (link_dhcp4_server_enabled(link)) { - r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex); - if (r < 0) - return r; - - r = sd_dhcp_server_attach_event(link->dhcp_server, NULL, 0); - if (r < 0) - return r; - } - - 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; - } - - if (link_lldp_rx_enabled(link)) { - r = sd_lldp_new(&link->lldp); - if (r < 0) - return r; - - r = sd_lldp_set_ifindex(link->lldp, link->ifindex); - if (r < 0) - return r; - - r = sd_lldp_match_capabilities(link->lldp, - link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? - SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : - SD_LLDP_SYSTEM_CAPABILITIES_ALL); - if (r < 0) - return r; - - r = sd_lldp_set_filter_address(link->lldp, &link->mac); - if (r < 0) - return r; - - r = sd_lldp_attach_event(link->lldp, NULL, 0); - if (r < 0) - return r; - - r = sd_lldp_set_callback(link->lldp, lldp_handler, link); - if (r < 0) - return r; - - r = link_update_lldp(link); - if (r < 0) - return r; - } - - if (link_has_carrier(link)) { - r = link_acquire_conf(link); - if (r < 0) - return r; - } - - return link_enter_join_netdev(link); -} - -static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m, - void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - Network *network; - int r; - - assert(link); - assert(link->ifname); - assert(link->manager); - - if (link->state != LINK_STATE_PENDING) - return 1; - - log_link_debug(link, "Link state is up-to-date"); - - r = link_new_bound_by_list(link); - if (r < 0) - return r; - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - 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 (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"); - } - - r = network_apply(link->manager, network, link); - if (r < 0) - return r; - } - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - r = link_configure(link); - if (r < 0) - return r; - - return 1; -} - -int link_initialized(Link *link, struct udev_device *device) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(device); - - if (link->state != LINK_STATE_PENDING) - return 0; - - if (link->udev_device) - return 0; - - log_link_debug(link, "udev initialized link"); - - link->udev_device = udev_device_ref(device); - - /* udev has initialized the link, but we don't know if we have yet - * processed the NEWLINK messages with the latest state. Do a GETLINK, - * when it returns we know that the pending NEWLINKs have already been - * processed and that we are up-to-date */ - - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, - link->ifindex); - if (r < 0) - return r; - - r = sd_netlink_call_async(link->manager->rtnl, req, - link_initialized_and_synced, link, 0, NULL); - if (r < 0) - return r; - - link_ref(link); - - return 0; -} - -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); - - 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'; - - 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; - } - - r = network_apply(link->manager, network, link); - if (r < 0) - return log_link_error_errno(link, r, "Failed to apply network %s: %m", basename(network_file)); - } - -network_file_fail: - - if (addresses) { - p = addresses; - - for (;;) { - _cleanup_free_ char *address_str = NULL; - char *prefixlen_str; - int family; - unsigned char prefixlen; - - 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; - - prefixlen_str = strchr(address_str, '/'); - if (!prefixlen_str) { - log_link_debug(link, "Failed to parse address and prefix length %s", address_str); - continue; - } - - *prefixlen_str++ = '\0'; - - r = sscanf(prefixlen_str, "%hhu", &prefixlen); - if (r != 1) { - log_link_error(link, "Failed to parse prefixlen %s", prefixlen_str); - continue; - } - - 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; - } - - r = address_add(link, family, &address, prefixlen, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Failed to add address: %m"); - } - } - - if (routes) { - p = routes; - - 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; - - prefixlen_str = strchr(route_str, '/'); - if (!prefixlen_str) { - log_link_debug(link, "Failed to parse route %s", route_str); - continue; - } - - *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; - } - - 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; - } - - 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"); - - 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"); - } - - route->lifetime = lifetime; - sd_event_source_unref(route->expire); - route->expire = expire; - expire = NULL; - } - } - - 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; - } - - r = sd_dhcp_client_new(&link->dhcp_client); - if (r < 0) - return log_link_error_errno(link, r, "Falied to create DHCPv4 client: %m"); - - 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 initial DHCPv4 address %s: %m", dhcp4_address); - } - -dhcp4_address_fail: - - 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 initial IPv4LL address %s: %m", ipv4ll_address); - } - -ipv4ll_address_fail: - - return 0; -} - -int link_add(Manager *m, sd_netlink_message *message, Link **ret) { - Link *link; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - char ifindex_str[2 + DECIMAL_STR_MAX(int)]; - int r; - - assert(m); - assert(m->rtnl); - assert(message); - assert(ret); - - r = link_new(m, message, ret); - if (r < 0) - return r; - - 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) { - 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 */ - log_link_debug(link, "link pending udev initialization..."); - return 0; - } - - r = link_initialized(link, device); - if (r < 0) - 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; -} - -static int link_carrier_gained(Link *link) { - int r; - - assert(link); - - 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); - return r; - } - - r = link_enter_set_addresses(link); - if (r < 0) - return r; - } - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - return 0; -} - -static int link_carrier_lost(Link *link) { - int r; - - assert(link); - - r = link_stop_clients(link); - if (r < 0) { - link_enter_failed(link); - return r; - } - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - return 0; -} - -int link_carrier_reset(Link *link) { - int r; - - assert(link); - - if (link_has_carrier(link)) { - r = link_carrier_lost(link); - if (r < 0) - return r; - - r = link_carrier_gained(link); - if (r < 0) - return r; - - log_link_info(link, "Reset carrier"); - } - - return 0; -} - -int link_update(Link *link, sd_netlink_message *m) { - struct ether_addr mac; - const char *ifname; - uint32_t mtu; - bool had_carrier, carrier_gained, carrier_lost; - int r; - - assert(link); - assert(link->ifname); - assert(m); - - if (link->state == LINK_STATE_LINGER) { - link_ref(link); - log_link_info(link, "Link readded"); - link_set_state(link, LINK_STATE_ENSLAVING); - - r = link_new_carrier_maps(link); - if (r < 0) - return r; - } - - r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname); - if (r >= 0 && !streq(ifname, link->ifname)) { - log_link_info(link, "Renamed to %s", ifname); - - link_free_carrier_maps(link); - - r = free_and_strdup(&link->ifname, ifname); - if (r < 0) - return r; - - r = link_new_carrier_maps(link); - if (r < 0) - return r; - } - - r = sd_netlink_message_read_u32(m, IFLA_MTU, &mtu); - if (r >= 0 && mtu > 0) { - link->mtu = mtu; - if (!link->original_mtu) { - link->original_mtu = mtu; - log_link_debug(link, "Saved original MTU: %" PRIu32, link->original_mtu); - } - - if (link->dhcp_client) { - r = sd_dhcp_client_set_mtu(link->dhcp_client, - link->mtu); - if (r < 0) { - log_link_warning_errno(link, r, "Could not update MTU in DHCP client: %m"); - return r; - } - } - } - - /* The kernel may broadcast NEWLINK messages without the MAC address - set, simply ignore them. */ - r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac); - if (r >= 0) { - if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, - ETH_ALEN)) { - - memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, - ETH_ALEN); - - log_link_debug(link, "MAC address: " - "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - 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]); - - if (link->ipv4ll) { - r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m"); - } - - if (link->dhcp_client) { - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_mac(link->dhcp_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); - - r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, - link->network->iaid, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m"); - } - - if (link->dhcp6_client) { - const DUID* duid = link_duid(link); - - r = sd_dhcp6_client_set_mac(link->dhcp6_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), - ARPHRD_ETHER); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); - - r = sd_dhcp6_client_set_iaid(link->dhcp6_client, - link->network->iaid); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); - - r = sd_dhcp6_client_set_duid(link->dhcp6_client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); - } - } - } - - had_carrier = link_has_carrier(link); - - r = link_update_flags(link, m); - if (r < 0) - return r; - - r = link_update_lldp(link); - if (r < 0) - return r; - - carrier_gained = !had_carrier && link_has_carrier(link); - carrier_lost = had_carrier && !link_has_carrier(link); - - if (carrier_gained) { - log_link_info(link, "Gained carrier"); - - r = link_carrier_gained(link); - if (r < 0) - return r; - } else if (carrier_lost) { - log_link_info(link, "Lost carrier"); - - r = link_carrier_lost(link); - if (r < 0) - return r; - } - - return 0; -} - -static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { - bool space = false; - Iterator i; - Link *link; - - assert(f); - assert(prefix); - - if (hashmap_isempty(h)) - return; - - fputs(prefix, f); - HASHMAP_FOREACH(link, h, i) { - if (space) - fputc(' ', f); - - fprintf(f, "%i", link->ifindex); - space = true; - } - - fputc('\n', f); -} - -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); - assert(link->state_file); - assert(link->lease_file); - assert(link->manager); - - if (link->state == LINK_STATE_LINGER) { - unlink(link->state_file); - return 0; - } - - link_lldp_save(link); - - admin_state = link_state_to_string(link->state); - assert(admin_state); - - oper_state = link_operstate_to_string(link->operstate); - assert(oper_state); - - r = fopen_temporary(link->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - fprintf(f, - "# This is private data. Do not parse.\n" - "ADMIN_STATE=%s\n" - "OPER_STATE=%s\n", - admin_state, oper_state); - - if (link->network) { - bool space; - sd_dhcp6_lease *dhcp6_lease = NULL; - const char *dhcp_domainname = NULL; - char **dhcp6_domains = NULL; - - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); - if (r < 0 && r != -ENOMSG) - log_link_debug(link, "No DHCPv6 lease"); - } - - fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); - - fputs("DNS=", f); - space = false; - fputstrv(f, link->network->dns, NULL, &space); - - if (link->network->dhcp_use_dns && - link->dhcp_lease) { - const struct in_addr *addresses; - - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in_addrs(f, addresses, r); - space = true; - } - } - - if (link->network->dhcp_use_dns && dhcp6_lease) { - struct in6_addr *in6_addrs; - - r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in6_addrs(f, in6_addrs, r); - space = true; - } - } - - /* Make sure to flush out old entries before we use the NDISC data */ - ndisc_vacuum(link); - - if (link->network->dhcp_use_dns && link->ndisc_rdnss) { - NDiscRDNSS *dd; - - SET_FOREACH(dd, link->ndisc_rdnss, i) { - if (space) - fputc(' ', f); - - serialize_in6_addrs(f, &dd->address, 1); - space = true; - } - } - - fputc('\n', f); - - fputs("NTP=", f); - space = false; - fputstrv(f, link->network->ntp, NULL, &space); - - if (link->network->dhcp_use_ntp && - link->dhcp_lease) { - const struct in_addr *addresses; - - r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in_addrs(f, addresses, r); - space = true; - } - } - - if (link->network->dhcp_use_ntp && dhcp6_lease) { - struct in6_addr *in6_addrs; - char **hosts; - - r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease, - &in6_addrs); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in6_addrs(f, in6_addrs, r); - space = true; - } - - r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts); - if (r > 0) - fputstrv(f, hosts, NULL, &space); - } - - fputc('\n', f); - - if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - if (link->dhcp_lease) - (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); - if (dhcp6_lease) - (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); - } - - fputs("DOMAINS=", f); - fputstrv(f, link->network->search_domains, NULL, &space); - - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { - NDiscDNSSL *dd; - - if (dhcp_domainname) - fputs_with_space(f, dhcp_domainname, NULL, &space); - if (dhcp6_domains) - fputstrv(f, dhcp6_domains, NULL, &space); - - SET_FOREACH(dd, link->ndisc_dnssl, i) - fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); - } - - fputc('\n', f); - - fputs("ROUTE_DOMAINS=", f); - fputstrv(f, link->network->route_domains, NULL, NULL); - - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { - NDiscDNSSL *dd; - - if (dhcp_domainname) - fputs_with_space(f, dhcp_domainname, NULL, &space); - if (dhcp6_domains) - fputstrv(f, dhcp6_domains, NULL, &space); - - SET_FOREACH(dd, link->ndisc_dnssl, i) - fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); - } - - fputc('\n', f); - - 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) - fputs_with_space(f, n, NULL, &space); - 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); - } - - print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); - print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); - - 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); - - 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) - goto fail; - - fprintf(f, - "DHCP_LEASE=%s\n", - link->lease_file); - } 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); - } - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, link->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(link->state_file); - if (temp_path) - (void) unlink(temp_path); - - 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); - - /* mark manager dirty as link is dirty */ - manager_dirty(link->manager); - - 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) - /* don't take another ref if the link was already dirty */ - 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", - [LINK_STATE_SETTING_ADDRESSES] = "configuring", - [LINK_STATE_SETTING_ROUTES] = "configuring", - [LINK_STATE_CONFIGURED] = "configured", - [LINK_STATE_UNMANAGED] = "unmanaged", - [LINK_STATE_FAILED] = "failed", - [LINK_STATE_LINGER] = "linger", -}; - -DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState); - -static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { - [LINK_OPERSTATE_OFF] = "off", - [LINK_OPERSTATE_NO_CARRIER] = "no-carrier", - [LINK_OPERSTATE_DORMANT] = "dormant", - [LINK_OPERSTATE_CARRIER] = "carrier", - [LINK_OPERSTATE_DEGRADED] = "degraded", - [LINK_OPERSTATE_ROUTABLE] = "routable", -}; - -DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h deleted file mode 100644 index 2809b1fe0b..0000000000 --- a/src/network/networkd-link.h +++ /dev/null @@ -1,213 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "sd-bus.h" -#include "sd-dhcp-client.h" -#include "sd-dhcp-server.h" -#include "sd-dhcp6-client.h" -#include "sd-ipv4ll.h" -#include "sd-lldp.h" -#include "sd-ndisc.h" -#include "sd-netlink.h" - -#include "list.h" -#include "set.h" - -typedef enum LinkState { - LINK_STATE_PENDING, - LINK_STATE_ENSLAVING, - LINK_STATE_SETTING_ADDRESSES, - LINK_STATE_SETTING_ROUTES, - LINK_STATE_CONFIGURED, - LINK_STATE_UNMANAGED, - LINK_STATE_FAILED, - LINK_STATE_LINGER, - _LINK_STATE_MAX, - _LINK_STATE_INVALID = -1 -} LinkState; - -typedef enum LinkOperationalState { - LINK_OPERSTATE_OFF, - LINK_OPERSTATE_NO_CARRIER, - LINK_OPERSTATE_DORMANT, - LINK_OPERSTATE_CARRIER, - LINK_OPERSTATE_DEGRADED, - LINK_OPERSTATE_ROUTABLE, - _LINK_OPERSTATE_MAX, - _LINK_OPERSTATE_INVALID = -1 -} LinkOperationalState; - -typedef struct Manager Manager; -typedef struct Network Network; -typedef struct Address Address; - -typedef struct Link { - Manager *manager; - - int n_ref; - - int ifindex; - char *ifname; - char *kind; - unsigned short iftype; - char *state_file; - struct ether_addr mac; - struct in6_addr ipv6ll_address; - uint32_t mtu; - struct udev_device *udev_device; - - unsigned flags; - uint8_t kernel_operstate; - - Network *network; - - LinkState state; - LinkOperationalState operstate; - - unsigned link_messages; - unsigned enslaving; - - Set *addresses; - Set *addresses_foreign; - Set *routes; - Set *routes_foreign; - - sd_dhcp_client *dhcp_client; - sd_dhcp_lease *dhcp_lease; - char *lease_file; - uint16_t original_mtu; - unsigned dhcp4_messages; - bool dhcp4_configured; - bool dhcp6_configured; - - unsigned ndisc_messages; - bool ndisc_configured; - - sd_ipv4ll *ipv4ll; - bool ipv4ll_address:1; - bool ipv4ll_route:1; - - bool static_configured; - - LIST_HEAD(Address, pool_addresses); - - sd_dhcp_server *dhcp_server; - - sd_ndisc *ndisc; - Set *ndisc_rdnss; - Set *ndisc_dnssl; - - sd_dhcp6_client *dhcp6_client; - bool rtnl_extended_attrs; - - /* This is about LLDP reception */ - sd_lldp *lldp; - char *lldp_file; - - /* This is about LLDP transmission */ - unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ - sd_event_source *lldp_emit_event_source; - - Hashmap *bound_by_links; - Hashmap *bound_to_links; -} Link; - -Link *link_unref(Link *link); -Link *link_ref(Link *link); -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_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_check_ready(Link *link); - -void link_update_operstate(Link *link); -int link_update(Link *link, sd_netlink_message *message); - -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 dhcp6_configure(Link *link); -int dhcp6_request_address(Link *link, int ir); - -const char* link_state_to_string(LinkState s) _const_; -LinkState link_state_from_string(const char *s) _pure_; - -const char* link_operstate_to_string(LinkOperationalState s) _const_; -LinkOperationalState link_operstate_from_string(const char *s) _pure_; - -extern const sd_bus_vtable link_vtable[]; - -int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); -int link_send_changed(Link *link, const char *property, ...) _sentinel_; - -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); -#define _cleanup_link_unref_ _cleanup_(link_unrefp) - -/* Macros which append INTERFACE= to the message */ - -#define log_link_full(link, level, error, ...) \ - ({ \ - Link *_l = (link); \ - _l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, ##__VA_ARGS__) : \ - log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ - }) \ - -#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) -#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__) -#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) -#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__) -#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__) - -#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__) -#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__) -#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__) -#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__) -#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__) - -#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ -#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname - -#define ADDRESS_FMT_VAL(address) \ - be32toh((address).s_addr) >> 24, \ - (be32toh((address).s_addr) >> 16) & 0xFFu, \ - (be32toh((address).s_addr) >> 8) & 0xFFu, \ - be32toh((address).s_addr) & 0xFFu diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c deleted file mode 100644 index 3aa768388b..0000000000 --- a/src/network/networkd-lldp-tx.c +++ /dev/null @@ -1,416 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "networkd-lldp-tx.h" -#include "networkd.h" -#include "parse-util.h" -#include "random-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "unaligned.h" - -/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ -#define LLDP_TX_FAST_INIT 4U - -/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ -#define LLDP_TX_HOLD 4U - -/* The jitter range to add, see 9.2.2. */ -#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC) - -/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ -#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) - -/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ -#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) - -static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { - [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, - [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, - [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, -}; - -static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { - assert(p); - - if (id > 127) - return -EBADMSG; - if (sz > 511) - return -ENOBUFS; - - (*p)[0] = (id << 1) | !!(sz & 256); - (*p)[1] = sz & 255; - - *p = *p + 2; - return 0; -} - -static int lldp_make_packet( - LLDPEmit mode, - const struct ether_addr *hwaddr, - const char *machine_id, - const char *ifname, - uint16_t ttl, - const char *port_description, - const char *hostname, - const char *pretty_hostname, - uint16_t system_capabilities, - uint16_t enabled_capabilities, - void **ret, size_t *sz) { - - size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0; - _cleanup_free_ void *packet = NULL; - struct ether_header *h; - uint8_t *p; - size_t l; - int r; - - assert(mode > LLDP_EMIT_NO); - assert(mode < _LLDP_EMIT_MAX); - assert(hwaddr); - assert(machine_id); - assert(ifname); - assert(ret); - assert(sz); - - machine_id_length = strlen(machine_id); - ifname_length = strlen(ifname); - - if (port_description) - port_description_length = strlen(port_description); - - if (hostname) - hostname_length = strlen(hostname); - - if (pretty_hostname) - pretty_hostname_length = strlen(pretty_hostname); - - l = sizeof(struct ether_header) + - /* Chassis ID */ - 2 + 1 + machine_id_length + - /* Port ID */ - 2 + 1 + ifname_length + - /* TTL */ - 2 + 2 + - /* System Capabilities */ - 2 + 4 + - /* End */ - 2; - - /* Port Description */ - if (port_description) - l += 2 + port_description_length; - - /* System Name */ - if (hostname) - l += 2 + hostname_length; - - /* System Description */ - if (pretty_hostname) - l += 2 + pretty_hostname_length; - - packet = malloc(l); - if (!packet) - return -ENOMEM; - - h = (struct ether_header*) packet; - h->ether_type = htobe16(ETHERTYPE_LLDP); - memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); - memcpy(h->ether_shost, hwaddr, ETH_ALEN); - - p = (uint8_t*) packet + sizeof(struct ether_header); - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length); - if (r < 0) - return r; - *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; - p = mempcpy(p, machine_id, machine_id_length); - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length); - if (r < 0) - return r; - *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; - p = mempcpy(p, ifname, ifname_length); - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2); - if (r < 0) - return r; - unaligned_write_be16(p, ttl); - p += 2; - - if (port_description) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length); - if (r < 0) - return r; - p = mempcpy(p, port_description, port_description_length); - } - - if (hostname) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length); - if (r < 0) - return r; - p = mempcpy(p, hostname, hostname_length); - } - - if (pretty_hostname) { - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length); - if (r < 0) - return r; - p = mempcpy(p, pretty_hostname, pretty_hostname_length); - } - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); - if (r < 0) - return r; - unaligned_write_be16(p, system_capabilities); - p += 2; - unaligned_write_be16(p, enabled_capabilities); - p += 2; - - r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0); - if (r < 0) - return r; - - assert(p == (uint8_t*) packet + l); - - *ret = packet; - *sz = l; - - packet = NULL; - return 0; -} - -static int lldp_send_packet( - int ifindex, - const struct ether_addr *address, - const void *packet, - size_t packet_size) { - - union sockaddr_union sa = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - }; - - _cleanup_close_ int fd = -1; - ssize_t l; - - assert(ifindex > 0); - assert(address); - assert(packet || packet_size <= 0); - - memcpy(sa.ll.sll_addr, address, ETH_ALEN); - - fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); - if (fd < 0) - return -errno; - - l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); - if (l < 0) - return -errno; - - if ((size_t) l != packet_size) - return -EIO; - - return 0; -} - -static int link_send_lldp(Link *link) { - char machine_id_string[SD_ID128_STRING_MAX]; - _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; - _cleanup_free_ void *packet = NULL; - size_t packet_size = 0; - sd_id128_t machine_id; - uint16_t caps; - usec_t ttl; - int r; - - assert(link); - - if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) - return 0; - - assert(link->network->lldp_emit < _LLDP_EMIT_MAX); - - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; - - (void) gethostname_strict(&hostname); - (void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL); - - assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC); - ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC); - - caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : - SD_LLDP_SYSTEM_CAPABILITIES_STATION; - - r = lldp_make_packet(link->network->lldp_emit, - &link->mac, - sd_id128_to_string(machine_id, machine_id_string), - link->ifname, - (uint16_t) ttl, - link->network ? link->network->description : NULL, - hostname, - pretty_hostname, - SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, - caps, - &packet, &packet_size); - if (r < 0) - return r; - - return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); -} - -static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { - Link *link = userdata; - usec_t current, delay, next; - int r; - - assert(s); - assert(userdata); - - log_link_debug(link, "Sending LLDP packet..."); - - r = link_send_lldp(link); - if (r < 0) - log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m"); - - if (link->lldp_tx_fast > 0) - link->lldp_tx_fast--; - - assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), ¤t) >= 0); - - delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC; - next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC); - - r = sd_event_source_set_time(s, next); - if (r < 0) - return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m"); - - r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); - if (r < 0) - return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m"); - - return 0; -} - -int link_lldp_emit_start(Link *link) { - usec_t next; - int r; - - assert(link); - - if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { - link_lldp_emit_stop(link); - return 0; - } - - /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ - - link->lldp_tx_fast = LLDP_TX_FAST_INIT; - - next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), - (usec_t) random_u64() % LLDP_JITTER_USEC); - - if (link->lldp_emit_event_source) { - usec_t old; - - /* Lower the timeout, maybe */ - r = sd_event_source_get_time(link->lldp_emit_event_source, &old); - if (r < 0) - return r; - - if (old <= next) - return 0; - - return sd_event_source_set_time(link->lldp_emit_event_source, next); - } else { - r = sd_event_add_time( - link->manager->event, - &link->lldp_emit_event_source, - clock_boottime_or_monotonic(), - next, - 0, - on_lldp_timer, - link); - if (r < 0) - return r; - - (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); - } - - return 0; -} - -void link_lldp_emit_stop(Link *link) { - assert(link); - - link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source); -} - -int config_parse_lldp_emit( - 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) { - - LLDPEmit *emit = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) - *emit = LLDP_EMIT_NO; - else if (streq(rvalue, "nearest-bridge")) - *emit = LLDP_EMIT_NEAREST_BRIDGE; - else if (streq(rvalue, "non-tpmr-bridge")) - *emit = LLDP_EMIT_NON_TPMR_BRIDGE; - else if (streq(rvalue, "customer-bridge")) - *emit = LLDP_EMIT_CUSTOMER_BRIDGE; - else { - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); - return 0; - } - - *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO; - } - - return 0; -} diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h deleted file mode 100644 index 4680c9d950..0000000000 --- a/src/network/networkd-lldp-tx.h +++ /dev/null @@ -1,35 +0,0 @@ -#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 . -***/ - -#include "networkd-link.h" - -typedef enum LLDPEmit { - LLDP_EMIT_NO, - LLDP_EMIT_NEAREST_BRIDGE, - LLDP_EMIT_NON_TPMR_BRIDGE, - LLDP_EMIT_CUSTOMER_BRIDGE, - _LLDP_EMIT_MAX, -} LLDPEmit; - -int link_lldp_emit_start(Link *link); -void link_lldp_emit_stop(Link *link); - -int config_parse_lldp_emit(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-manager-bus.c b/src/network/networkd-manager-bus.c deleted file mode 100644 index 0c429b9471..0000000000 --- a/src/network/networkd-manager-bus.c +++ /dev/null @@ -1,49 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-util.h" -#include "networkd.h" - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState); - -const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - - SD_BUS_VTABLE_END -}; - -int manager_send_changed(Manager *manager, const char *property, ...) { - char **l; - - assert(manager); - - if (!manager->bus) - return 0; /* replace by assert when we have kdbus */ - - l = strv_from_stdarg_alloca(property); - - return sd_bus_emit_properties_changed_strv( - manager->bus, - "/org/freedesktop/network1", - "org.freedesktop.network1.Manager", - l); -} diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c deleted file mode 100644 index 9174dcc7f4..0000000000 --- a/src/network/networkd-manager.c +++ /dev/null @@ -1,1329 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . - ***/ - -#include -#include - -#include "sd-daemon.h" -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "bus-util.h" -#include "conf-parser.h" -#include "def.h" -#include "dns-domain.h" -#include "fd-util.h" -#include "fileio.h" -#include "libudev-private.h" -#include "local-addresses.h" -#include "netlink-util.h" -#include "networkd.h" -#include "ordered-set.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) - -const char* const network_dirs[] = { - "/etc/systemd/network", - "/run/systemd/network", - "/usr/lib/systemd/network", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/network", -#endif - NULL}; - -static int setup_default_address_pool(Manager *m) { - AddressPool *p; - int r; - - assert(m); - - /* Add in the well-known private address ranges. */ - - r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12); - if (r < 0) - return r; - - r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8); - if (r < 0) - return r; - - return 0; -} - -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - - return 0; -} - -static int manager_reset_all(Manager *m) { - Link *link; - Iterator i; - int r; - - assert(m); - - HASHMAP_FOREACH(link, m->links, i) { - r = link_carrier_reset(link); - if (r < 0) - log_link_warning_errno(link, r, "Could not reset carrier: %m"); - } - - return 0; -} - -static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { - Manager *m = userdata; - int b, r; - - assert(message); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); - return 0; - } - - if (b) - return 0; - - log_debug("Coming back from suspend, resetting all connections..."); - - manager_reset_all(m); - - return 0; -} - -int manager_connect_bus(Manager *m) { - int r; - - assert(m); - - r = sd_bus_default_system(&m->bus); - if (r == -ENOENT) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. As soon as we have - * kdbus we can stop doing this... */ - - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); - - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); - - return 0; - } - - if (r < 0) - return r; - - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); - if (r < 0) - return log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to add manager object vtable: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.Link", link_vtable, link_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add link object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add link enumerator: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/network", "org.freedesktop.network1.Network", network_vtable, network_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to add network object vtable: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/network", network_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add network enumerator: %m"); - - r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - return 0; -} - -static int manager_udev_process_link(Manager *m, struct udev_device *device) { - Link *link = NULL; - int r, ifindex; - - assert(m); - assert(device); - - if (!streq_ptr(udev_device_get_action(device), "add")) - return 0; - - ifindex = udev_device_get_ifindex(device); - if (ifindex <= 0) { - log_debug("Ignoring udev ADD event for device with invalid ifindex"); - return 0; - } - - r = link_get(m, ifindex, &link); - if (r == -ENODEV) - return 0; - else if (r < 0) - return r; - - r = link_initialized(link, device); - if (r < 0) - return r; - - return 0; -} - -static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - struct udev_monitor *monitor = m->udev_monitor; - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - - device = udev_monitor_receive_device(monitor); - if (!device) - return -ENOMEM; - - manager_udev_process_link(m, device); - return 0; -} - -static int manager_connect_udev(Manager *m) { - int r; - - /* udev does not initialize devices inside containers, - * so we rely on them being already initialized before - * entering the container */ - if (detect_container() > 0) - return 0; - - m->udev = udev_new(); - if (!m->udev) - return -ENOMEM; - - m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); - if (!m->udev_monitor) - return -ENOMEM; - - r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL); - if (r < 0) - return log_error_errno(r, "Could not add udev monitor filter: %m"); - - r = udev_monitor_enable_receiving(m->udev_monitor); - if (r < 0) { - log_error("Could not enable udev monitor"); - return r; - } - - r = sd_event_add_io(m->event, - &m->udev_event_source, - udev_monitor_get_fd(m->udev_monitor), - EPOLLIN, manager_dispatch_link_udev, - m); - if (r < 0) - return r; - - r = sd_event_source_set_description(m->udev_event_source, "networkd-udev"); - if (r < 0) - return r; - - 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: - route_free(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; - NetDev *netdev = NULL; - uint16_t type; - const char *name; - 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: Could not receive link: %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_NEWLINK && type != RTM_DELLINK) { - log_warning("rtnl: Received unexpected message type when processing link"); - return 0; - } - - r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0) { - log_warning_errno(r, "rtnl: Could not get ifindex from link: %m"); - return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); - return 0; - } - - 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; - } - - (void) link_get(m, ifindex, &link); - (void) netdev_get(m, name, &netdev); - - switch (type) { - case RTM_NEWLINK: - if (!link) { - /* link is new, so add it */ - r = link_add(m, message, &link); - if (r < 0) { - log_warning_errno(r, "Could not add new link: %m"); - return 0; - } - } - - if (netdev) { - /* netdev exists, so make sure the ifindex matches */ - r = netdev_set_ifindex(netdev, message); - if (r < 0) { - log_warning_errno(r, "Could not set ifindex on netdev: %m"); - return 0; - } - } - - r = link_update(link, message); - if (r < 0) - return 0; - - break; - - case RTM_DELLINK: - link_drop(link); - netdev_drop(netdev); - - break; - - default: - assert_not_reached("Received invalid RTNL message type."); - } - - return 1; -} - -static int systemd_netlink_fd(void) { - int n, fd, rtnl_fd = -EINVAL; - - n = sd_listen_fds(true); - if (n <= 0) - return -EINVAL; - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { - if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { - if (rtnl_fd >= 0) - return -EINVAL; - - rtnl_fd = fd; - } - } - - return rtnl_fd; -} - -static int manager_connect_rtnl(Manager *m) { - int fd, r; - - assert(m); - - fd = systemd_netlink_fd(); - if (fd < 0) - r = sd_netlink_open(&m->rtnl); - else - r = sd_netlink_open_fd(&m->rtnl, fd); - if (r < 0) - return r; - - r = sd_netlink_inc_rcvbuf(m->rtnl, RCVBUF_SIZE); - if (r < 0) - return r; - - r = sd_netlink_attach_event(m->rtnl, m->event, 0); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, &manager_rtnl_process_link, m); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, &manager_rtnl_process_link, m); - if (r < 0) - return r; - - 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, &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 ordered_set_put_in_addr(OrderedSet *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 = ordered_set_consume(s, p); - if (r == -EEXIST) - return 0; - - return r; -} - -static int ordered_set_put_in_addrv(OrderedSet *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 = ordered_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, OrderedSet *s) { - bool space = false; - Iterator i; - char *p; - - if (ordered_set_isempty(s)) - return; - - fputs(field, f); - - ORDERED_SET_FOREACH(p, s, i) - fputs_with_space(f, p, NULL, &space); - - fputc('\n', f); -} - -static int manager_save(Manager *m) { - _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_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 = ordered_set_new(&string_hash_ops); - if (!dns) - return -ENOMEM; - - ntp = ordered_set_new(&string_hash_ops); - if (!ntp) - return -ENOMEM; - - search_domains = ordered_set_new(&dns_name_hash_ops); - if (!search_domains) - return -ENOMEM; - - route_domains = ordered_set_new(&dns_name_hash_ops); - if (!route_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 = ordered_set_put_strdupv(dns, link->network->dns); - if (r < 0) - return r; - - r = ordered_set_put_strdupv(ntp, link->network->ntp); - if (r < 0) - return r; - - r = ordered_set_put_strdupv(search_domains, link->network->search_domains); - if (r < 0) - return r; - - r = ordered_set_put_strdupv(route_domains, link->network->route_domains); - if (r < 0) - return r; - - if (!link->dhcp_lease) - continue; - - /* Secondly, add the entries acquired via DHCP */ - if (link->network->dhcp_use_dns) { - const struct in_addr *addresses; - - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); - if (r > 0) { - r = ordered_set_put_in_addrv(dns, addresses, r); - if (r < 0) - return r; - } else if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp_use_ntp) { - const struct in_addr *addresses; - - r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); - if (r > 0) { - r = ordered_set_put_in_addrv(ntp, addresses, r); - if (r < 0) - return r; - } else if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - const char *domainname; - - r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); - if (r >= 0) { - - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) - r = ordered_set_put_strdup(search_domains, domainname); - else - r = ordered_set_put_strdup(route_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=", search_domains); - print_string_set(f, "ROUTE_DOMAINS=", route_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; - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - m->state_file = strdup("/run/systemd/netif/state"); - if (!m->state_file) - return -ENOMEM; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_set_watchdog(m->event, true); - - 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; - - r = manager_connect_udev(m); - if (r < 0) - return r; - - m->netdevs = hashmap_new(&string_hash_ops); - if (!m->netdevs) - return -ENOMEM; - - LIST_HEAD_INIT(m->networks); - - r = setup_default_address_pool(m); - if (r < 0) - return r; - - m->duid.type = DUID_TYPE_EN; - - *ret = m; - m = NULL; - - return 0; -} - -void manager_free(Manager *m) { - Network *network; - NetDev *netdev; - Link *link; - AddressPool *pool; - - if (!m) - return; - - free(m->state_file); - - while ((link = hashmap_first(m->links))) - link_unref(link); - hashmap_free(m->links); - - while ((network = m->networks)) - network_free(network); - - hashmap_free(m->networks_by_name); - - while ((netdev = hashmap_first(m->netdevs))) - netdev_unref(netdev); - hashmap_free(m->netdevs); - - while ((pool = m->address_pools)) - address_pool_free(pool); - - 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); -} - -static bool manager_check_idle(void *userdata) { - Manager *m = userdata; - Link *link; - Iterator i; - - assert(m); - - /* Check whether we are idle now. The only case when we decide to be idle is when there's only a loopback - * device around, for which we have no configuration, and which already left the PENDING state. In all other - * cases we are not idle. */ - - HASHMAP_FOREACH(link, m->links, i) { - /* We are not woken on udev activity, so let's just wait for the pending udev event */ - if (link->state == LINK_STATE_PENDING) - return false; - - if ((link->flags & IFF_LOOPBACK) == 0) - return false; - - if (link->network) - return false; - } - - return true; -} - -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, - m->bus, - "org.freedesktop.network1", - DEFAULT_EXIT_USEC, - manager_check_idle, - m); - else - /* failed to connect to the bus, so we lose exit-on-idle logic, - this should not happen except if dbus is not around at all */ - return sd_event_loop(m->event); -} - -int manager_load_config(Manager *m) { - int r; - - /* update timestamp */ - paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true); - - r = netdev_load(m); - if (r < 0) - return r; - - r = network_load(m); - if (r < 0) - return r; - - return 0; -} - -bool manager_should_reload(Manager *m) { - return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false); -} - -int manager_rtnl_enumerate_links(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *link; - int r; - - assert(m); - assert(m->rtnl); - - r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (link = reply; link; link = sd_netlink_message_next(link)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_link(m->rtnl, link, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; -} - -int manager_rtnl_enumerate_addresses(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *addr; - int r; - - assert(m); - assert(m->rtnl); - - r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (addr = reply; addr; addr = sd_netlink_message_next(addr)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_address(m->rtnl, addr, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; -} - -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(m); - assert(m->rtnl); - - r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (route = reply; route; route = sd_netlink_message_next(route)) { - int k; - - m->enumerating = true; - - k = manager_rtnl_process_route(m->rtnl, route, m); - if (k < 0) - r = k; - - m->enumerating = false; - } - - return r; -} - -int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) { - AddressPool *p; - int r; - - assert(m); - assert(prefixlen > 0); - assert(found); - - LIST_FOREACH(address_pools, p, m->address_pools) { - if (p->family != family) - continue; - - r = address_pool_acquire(p, prefixlen, found); - if (r != 0) - return r; - } - - return 0; -} - -Link* manager_find_uplink(Manager *m, Link *exclude) { - _cleanup_free_ struct local_address *gateways = NULL; - int n, i; - - assert(m); - - /* Looks for a suitable "uplink", via black magic: an - * interface that is up and where the default route with the - * highest priority points to. */ - - n = local_gateways(m->rtnl, 0, AF_UNSPEC, &gateways); - if (n < 0) { - log_warning_errno(n, "Failed to determine list of default gateways: %m"); - return NULL; - } - - for (i = 0; i < n; i++) { - Link *link; - - link = hashmap_get(m->links, INT_TO_PTR(gateways[i].ifindex)); - if (!link) { - log_debug("Weird, found a gateway for a link we don't know. Ignoring."); - continue; - } - - if (link == exclude) - continue; - - if (link->operstate < LINK_OPERSTATE_ROUTABLE) - continue; - - return link; - } - - 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 deleted file mode 100644 index d9c18b32a5..0000000000 --- a/src/network/networkd-ndisc.c +++ /dev/null @@ -1,664 +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 . -***/ - -#include - -#include "sd-ndisc.h" - -#include "networkd.h" -#include "networkd-ndisc.h" - -#define NDISC_DNSSL_MAX 64U -#define NDISC_RDNSS_MAX 64U - -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_router_process_default(Link *link, sd_ndisc_router *rt) { - _cleanup_route_free_ Route *route = NULL; - struct in6_addr gateway; - uint16_t lifetime; - unsigned preference; - usec_t time_now; - int r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_get_lifetime(rt, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - return; - } - if (lifetime == 0) /* not a default router */ - return; - - r = sd_ndisc_router_get_address(rt, &gateway); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - return; - } - - r = sd_ndisc_router_get_preference(rt, &preference); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); - return; - } - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } - - r = route_new(&route); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate route: %m"); - return; - } - - route->family = AF_INET6; - route->table = RT_TABLE_MAIN; - route->protocol = RTPROT_RA; - route->pref = preference; - 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_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { - _cleanup_address_free_ Address *address = NULL; - uint32_t lifetime_valid, lifetime_preferred; - unsigned prefixlen; - int r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix length: %m"); - return; - } - - r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m"); - return; - } - - r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m"); - return; - } - - r = address_new(&address); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate address: %m"); - return; - } - - address->family = AF_INET6; - r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix address: %m"); - return; - } - - 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.s6_addr[8] = link->mac.ether_addr_octet[0]; - address->in_addr.in6.s6_addr[8] ^= 1 << 1; - address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; - address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; - address->in_addr.in6.s6_addr[11] = 0xff; - address->in_addr.in6.s6_addr[12] = 0xfe; - address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; - address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; - address->in_addr.in6.s6_addr[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_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { - _cleanup_route_free_ Route *route = NULL; - usec_t time_now; - uint32_t lifetime; - unsigned prefixlen; - int r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } - - r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix length: %m"); - return; - } - - r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix lifetime: %m"); - return; - } - - r = route_new(&route); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate route: %m"); - return; - } - - route->family = AF_INET6; - route->table = RT_TABLE_MAIN; - route->protocol = RTPROT_RA; - route->flags = RTM_F_PREFIX; - route->dst_prefixlen = prefixlen; - route->lifetime = time_now + lifetime * USEC_PER_SEC; - - r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get prefix address: %m"); - return; - } - - 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_process_route(Link *link, sd_ndisc_router *rt) { - _cleanup_route_free_ Route *route = NULL; - struct in6_addr gateway; - uint32_t lifetime; - unsigned preference, prefixlen; - usec_t time_now; - int r; - - assert(link); - - r = sd_ndisc_router_route_get_lifetime(rt, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - return; - } - if (lifetime == 0) - return; - - r = sd_ndisc_router_get_address(rt, &gateway); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m"); - return; - } - - r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get route prefix length: %m"); - return; - } - - r = sd_ndisc_router_route_get_preference(rt, &preference); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m"); - return; - } - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } - - r = route_new(&route); - if (r < 0) { - log_link_error_errno(link, r, "Could not allocate route: %m"); - return; - } - - route->family = AF_INET6; - route->table = RT_TABLE_MAIN; - route->protocol = RTPROT_RA; - route->pref = preference; - route->gw.in6 = gateway; - route->dst_prefixlen = prefixlen; - route->lifetime = time_now + lifetime * USEC_PER_SEC; - - r = sd_ndisc_router_route_get_address(rt, &route->dst.in6); - if (r < 0) { - log_link_error_errno(link, r, "Failed to get route address: %m"); - return; - } - - r = route_configure(route, link, ndisc_netlink_handler); - if (r < 0) { - log_link_warning_errno(link, r, "Could not set additional route: %m"); - link_enter_failed(link); - return; - } - - link->ndisc_messages++; -} - -static void ndisc_rdnss_hash_func(const void *p, struct siphash *state) { - const NDiscRDNSS *x = p; - - siphash24_compress(&x->address, sizeof(x->address), state); -} - -static int ndisc_rdnss_compare_func(const void *_a, const void *_b) { - const NDiscRDNSS *a = _a, *b = _b; - - return memcmp(&a->address, &b->address, sizeof(a->address)); -} - -static const struct hash_ops ndisc_rdnss_hash_ops = { - .hash = ndisc_rdnss_hash_func, - .compare = ndisc_rdnss_compare_func -}; - -static void ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { - uint32_t lifetime; - const struct in6_addr *a; - usec_t time_now; - int i, n, r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } - - r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); - return; - } - - n = sd_ndisc_router_rdnss_get_addresses(rt, &a); - if (n < 0) { - log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m"); - return; - } - - for (i = 0; i < n; i++) { - NDiscRDNSS d = { - .address = a[i] - }, *x; - - if (lifetime == 0) { - (void) set_remove(link->ndisc_rdnss, &d); - link_dirty(link); - continue; - } - - x = set_get(link->ndisc_rdnss, &d); - if (x) { - x->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) { - log_link_warning(link, "Too many RDNSS records per link, ignoring."); - continue; - } - - r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops); - if (r < 0) { - log_oom(); - return; - } - - x = new0(NDiscRDNSS, 1); - if (!x) { - log_oom(); - return; - } - - x->address = a[i]; - x->valid_until = time_now + lifetime * USEC_PER_SEC; - - r = set_put(link->ndisc_rdnss, x); - if (r < 0) { - free(x); - log_oom(); - return; - } - - assert(r > 0); - link_dirty(link); - } -} - -static void ndisc_dnssl_hash_func(const void *p, struct siphash *state) { - const NDiscDNSSL *x = p; - - siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state); -} - -static int ndisc_dnssl_compare_func(const void *_a, const void *_b) { - const NDiscDNSSL *a = _a, *b = _b; - - return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b)); -} - -static const struct hash_ops ndisc_dnssl_hash_ops = { - .hash = ndisc_dnssl_hash_func, - .compare = ndisc_dnssl_compare_func -}; - -static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { - _cleanup_strv_free_ char **l = NULL; - uint32_t lifetime; - usec_t time_now; - char **i; - int r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); - return; - } - - r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m"); - return; - } - - r = sd_ndisc_router_dnssl_get_domains(rt, &l); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m"); - return; - } - - STRV_FOREACH(i, l) { - _cleanup_free_ NDiscDNSSL *s; - NDiscDNSSL *x; - - s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1); - if (!s) { - log_oom(); - return; - } - - strcpy(NDISC_DNSSL_DOMAIN(s), *i); - - if (lifetime == 0) { - (void) set_remove(link->ndisc_dnssl, s); - link_dirty(link); - continue; - } - - x = set_get(link->ndisc_dnssl, s); - if (x) { - x->valid_until = time_now + lifetime * USEC_PER_SEC; - continue; - } - - ndisc_vacuum(link); - - if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) { - log_link_warning(link, "Too many DNSSL records per link, ignoring."); - continue; - } - - r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops); - if (r < 0) { - log_oom(); - return; - } - - s->valid_until = time_now + lifetime * USEC_PER_SEC; - - r = set_put(link->ndisc_dnssl, s); - if (r < 0) { - log_oom(); - return; - } - - s = NULL; - assert(r > 0); - link_dirty(link); - } -} - -static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { - int r; - - assert(link); - assert(rt); - - r = sd_ndisc_router_option_rewind(rt); - for (;;) { - uint8_t type; - - if (r < 0) { - log_link_warning_errno(link, r, "Failed to iterate through options: %m"); - return; - } - if (r == 0) /* EOF */ - break; - - r = sd_ndisc_router_option_get_type(rt, &type); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA option type: %m"); - return; - } - - switch (type) { - - case SD_NDISC_OPTION_PREFIX_INFORMATION: { - uint8_t flags; - - r = sd_ndisc_router_prefix_get_flags(rt, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); - return; - } - - if (flags & ND_OPT_PI_FLAG_ONLINK) - ndisc_router_process_onlink_prefix(link, rt); - if (flags & ND_OPT_PI_FLAG_AUTO) - ndisc_router_process_autonomous_prefix(link, rt); - - break; - } - - case SD_NDISC_OPTION_ROUTE_INFORMATION: - ndisc_router_process_route(link, rt); - break; - - case SD_NDISC_OPTION_RDNSS: - ndisc_router_process_rdnss(link, rt); - break; - - case SD_NDISC_OPTION_DNSSL: - ndisc_router_process_dnssl(link, rt); - break; - } - - r = sd_ndisc_router_option_next(rt); - } -} - -static void ndisc_router_handler(Link *link, sd_ndisc_router *rt) { - uint64_t flags; - int r; - - assert(link); - assert(link->network); - assert(link->manager); - assert(rt); - - r = sd_ndisc_router_get_flags(rt, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA flags: %m"); - return; - } - - if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { - /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */ - r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED)); - if (r < 0 && r != -EBUSY) - log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m"); - else - log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request"); - } - - ndisc_router_process_default(link, rt); - ndisc_router_process_options(link, rt); -} - -static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) { - Link *link = userdata; - - assert(link); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - switch (event) { - - case SD_NDISC_EVENT_ROUTER: - ndisc_router_handler(link, rt); - break; - - case SD_NDISC_EVENT_TIMEOUT: - link->ndisc_configured = true; - link_check_ready(link); - - break; - default: - log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); - } -} - -int ndisc_configure(Link *link) { - int r; - - assert(link); - - r = sd_ndisc_new(&link->ndisc); - if (r < 0) - return r; - - r = sd_ndisc_attach_event(link->ndisc, NULL, 0); - if (r < 0) - return r; - - r = sd_ndisc_set_mac(link->ndisc, &link->mac); - if (r < 0) - return r; - - r = sd_ndisc_set_ifindex(link->ndisc, link->ifindex); - if (r < 0) - return r; - - r = sd_ndisc_set_callback(link->ndisc, ndisc_handler, link); - if (r < 0) - return r; - - return 0; -} - -void ndisc_vacuum(Link *link) { - NDiscRDNSS *r; - NDiscDNSSL *d; - Iterator i; - usec_t time_now; - - assert(link); - - /* Removes all RDNSS and DNSSL entries whose validity time has passed */ - - time_now = now(clock_boottime_or_monotonic()); - - SET_FOREACH(r, link->ndisc_rdnss, i) - if (r->valid_until < time_now) { - (void) set_remove(link->ndisc_rdnss, r); - link_dirty(link); - } - - SET_FOREACH(d, link->ndisc_dnssl, i) - if (d->valid_until < time_now) { - (void) set_remove(link->ndisc_dnssl, d); - link_dirty(link); - } -} diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h deleted file mode 100644 index 2002f55107..0000000000 --- a/src/network/networkd-ndisc.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "networkd-link.h" - -typedef struct NDiscRDNSS { - usec_t valid_until; - struct in6_addr address; -} NDiscRDNSS; - -typedef struct NDiscDNSSL { - usec_t valid_until; - /* The domain name follows immediately. */ -} NDiscDNSSL; - -static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) { - return ((char*) n) + ALIGN(sizeof(NDiscDNSSL)); -} - -int ndisc_configure(Link *link); -void ndisc_vacuum(Link *link); diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c deleted file mode 100644 index 7913b0088e..0000000000 --- a/src/network/networkd-netdev-bond.c +++ /dev/null @@ -1,444 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "conf-parser.h" -#include "extract-word.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 - * driver sends learning packets to each slaves peer switch - */ -#define LEARNING_PACKETS_INTERVAL_MIN_SEC (1 * USEC_PER_SEC) -#define LEARNING_PACKETS_INTERVAL_MAX_SEC (0x7fffffff * USEC_PER_SEC) - -/* Number of IGMP membership reports to be issued after - * a failover event. - */ -#define RESEND_IGMP_MIN 0 -#define RESEND_IGMP_MAX 255 -#define RESEND_IGMP_DEFAULT 1 - -/* - * Number of packets to transmit through a slave before - * moving to the next one. - */ -#define PACKETS_PER_SLAVE_MIN 0 -#define PACKETS_PER_SLAVE_MAX 65535 -#define PACKETS_PER_SLAVE_DEFAULT 1 - -/* - * Number of peer notifications (gratuitous ARPs and - * unsolicited IPv6 Neighbor Advertisements) to be issued after a - * failover event. - */ -#define GRATUITOUS_ARP_MIN 0 -#define GRATUITOUS_ARP_MAX 255 -#define GRATUITOUS_ARP_DEFAULT 1 - -static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = { - [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr", - [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup", - [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor", - [NETDEV_BOND_MODE_BROADCAST] = "broadcast", - [NETDEV_BOND_MODE_802_3AD] = "802.3ad", - [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb", - [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode"); - -static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = { - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2", - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4", - [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3", - [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3", - [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_xmit_hash_policy, - bond_xmit_hash_policy, - BondXmitHashPolicy, - "Failed to parse bond transmit hash policy") - -static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = { - [NETDEV_BOND_LACP_RATE_SLOW] = "slow", - [NETDEV_BOND_LACP_RATE_FAST] = "fast", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate") - -static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = { - [NETDEV_BOND_AD_SELECT_STABLE] = "stable", - [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth", - [NETDEV_BOND_AD_SELECT_COUNT] = "count", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select"); - -static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = { - [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none", - [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active", - [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC"); - -static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = { - [NETDEV_BOND_ARP_VALIDATE_NONE] = "none", - [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active", - [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup", - [NETDEV_BOND_ARP_VALIDATE_ALL]= "all", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate"); - -static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = { - [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any", - [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets"); - -static const char *bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { - [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always", - [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better", - [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure", -}; - -DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect); -DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect"); - -static uint8_t bond_mode_to_kernel(BondMode mode) { - switch (mode) { - case NETDEV_BOND_MODE_BALANCE_RR: - return BOND_MODE_ROUNDROBIN; - case NETDEV_BOND_MODE_ACTIVE_BACKUP: - return BOND_MODE_ACTIVEBACKUP; - case NETDEV_BOND_MODE_BALANCE_XOR: - return BOND_MODE_XOR; - case NETDEV_BOND_MODE_BROADCAST: - return BOND_MODE_BROADCAST; - case NETDEV_BOND_MODE_802_3AD: - return BOND_MODE_8023AD; - case NETDEV_BOND_MODE_BALANCE_TLB: - return BOND_MODE_TLB; - case NETDEV_BOND_MODE_BALANCE_ALB: - return BOND_MODE_ALB; - default: - return (uint8_t) -1; - } -} - -static uint8_t bond_xmit_hash_policy_to_kernel(BondXmitHashPolicy policy) { - switch (policy) { - case NETDEV_BOND_XMIT_HASH_POLICY_LAYER2: - return BOND_XMIT_POLICY_LAYER2; - case NETDEV_BOND_XMIT_HASH_POLICY_LAYER34: - return BOND_XMIT_POLICY_LAYER34; - case NETDEV_BOND_XMIT_HASH_POLICY_LAYER23: - return BOND_XMIT_POLICY_LAYER23; - case NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23: - return BOND_XMIT_POLICY_ENCAP23; - case NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34: - return BOND_XMIT_POLICY_ENCAP34; - default: - return (uint8_t) -1; - } -} - -static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Bond *b; - ArpIpTarget *target = NULL; - int r, i = 0; - - assert(netdev); - assert(!link); - 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)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MODE attribute: %m"); - } - - if (b->xmit_hash_policy != _NETDEV_BOND_XMIT_HASH_POLICY_INVALID) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, - bond_xmit_hash_policy_to_kernel(b->xmit_hash_policy)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_XMIT_HASH_POLICY attribute: %m"); - } - - if (b->lacp_rate != _NETDEV_BOND_LACP_RATE_INVALID && - b->mode == NETDEV_BOND_MODE_802_3AD) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate ); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_LACP_RATE attribute: %m"); - } - - if (b->miimon != 0) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_MIIMON, b->miimon / USEC_PER_MSEC); - if (r < 0) - log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_BOND_MIIMON attribute: %m"); - } - - if (b->downdelay != 0) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_DOWNDELAY, b->downdelay / USEC_PER_MSEC); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_DOWNDELAY attribute: %m"); - } - - if (b->updelay != 0) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_UPDELAY, b->updelay / USEC_PER_MSEC); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_UPDELAY attribute: %m"); - } - - if (b->arp_interval != 0) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_INTERVAL, b->arp_interval / USEC_PER_MSEC); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_INTERVAL attribute: %m"); - - if ((b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC) && - (b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC)) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_LP_INTERVAL attribute: %m"); - } - } - - if (b->ad_select != _NETDEV_BOND_AD_SELECT_INVALID && - b->mode == NETDEV_BOND_MODE_802_3AD) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_SELECT attribute: %m"); - } - - if (b->fail_over_mac != _NETDEV_BOND_FAIL_OVER_MAC_INVALID && - b->mode == NETDEV_BOND_MODE_ACTIVE_BACKUP) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_FAIL_OVER_MAC, b->fail_over_mac); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_FAIL_OVER_MAC attribute: %m"); - } - - if (b->arp_validate != _NETDEV_BOND_ARP_VALIDATE_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_VALIDATE, b->arp_validate); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_VALIDATE attribute: %m"); - } - - if (b->arp_all_targets != _NETDEV_BOND_ARP_ALL_TARGETS_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->arp_all_targets); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_VALIDATE attribute: %m"); - } - - if (b->primary_reselect != _NETDEV_BOND_PRIMARY_RESELECT_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->primary_reselect); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); - } - - if (b->resend_igmp <= RESEND_IGMP_MAX) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_RESEND_IGMP, b->resend_igmp); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_RESEND_IGMP attribute: %m"); - } - - if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX && - b->mode == NETDEV_BOND_MODE_BALANCE_RR) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_PACKETS_PER_SLAVE attribute: %m"); - } - - if (b->num_grat_arp <= GRATUITOUS_ARP_MAX) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_NUM_PEER_NOTIF, b->num_grat_arp); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_NUM_PEER_NOTIF attribute: %m"); - } - - if (b->min_links != 0) { - r = sd_netlink_message_append_u32(m, IFLA_BOND_MIN_LINKS, b->min_links); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MIN_LINKS attribute: %m"); - } - - r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m"); - - if (b->arp_interval > 0) { - if (b->n_arp_ip_targets > 0) { - - r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %m"); - - LIST_FOREACH(arp_ip_target, target, b->arp_ip_targets) { - r = sd_netlink_message_append_u32(m, i++, target->ip.in.s_addr); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %m"); - } - } - - return 0; -} - -int config_parse_arp_ip_target_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Bond *b = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - for (;;) { - _cleanup_free_ ArpIpTarget *buffer = NULL; - _cleanup_free_ char *n = NULL; - int f; - - 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) - return -ENOMEM; - - r = in_addr_from_string_auto(n, &f, &buffer->ip); - if (r < 0) { - 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, 0, "Bond ARP ip target address is invalid, ignoring assignment: %s", n); - return 0; - } - - LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer); - b->n_arp_ip_targets++; - - buffer = NULL; - } - - if (b->n_arp_ip_targets > 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; - - assert(netdev); - - b = BOND(netdev); - - assert(b); - - LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets) - free(t); - - b->arp_ip_targets = NULL; -} - -static void bond_init(NetDev *netdev) { - Bond *b; - - assert(netdev); - - b = BOND(netdev); - - assert(b); - - b->mode = _NETDEV_BOND_MODE_INVALID; - b->xmit_hash_policy = _NETDEV_BOND_XMIT_HASH_POLICY_INVALID; - b->lacp_rate = _NETDEV_BOND_LACP_RATE_INVALID; - b->ad_select = _NETDEV_BOND_AD_SELECT_INVALID; - b->fail_over_mac = _NETDEV_BOND_FAIL_OVER_MAC_INVALID; - b->arp_validate = _NETDEV_BOND_ARP_VALIDATE_INVALID; - b->arp_all_targets = _NETDEV_BOND_ARP_ALL_TARGETS_INVALID; - b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID; - - b->all_slaves_active = false; - - b->resend_igmp = RESEND_IGMP_DEFAULT; - b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT; - b->num_grat_arp = GRATUITOUS_ARP_DEFAULT; - b->lp_interval = LEARNING_PACKETS_INTERVAL_MIN_SEC; - - LIST_HEAD_INIT(b->arp_ip_targets); - b->n_arp_ip_targets = 0; -} - -const NetDevVTable bond_vtable = { - .object_size = sizeof(Bond), - .init = bond_init, - .done = bond_done, - .sections = "Match\0NetDev\0Bond\0", - .fill_message_create = netdev_bond_fill_message_create, - .create_type = NETDEV_CREATE_MASTER, -}; diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h deleted file mode 100644 index b941edb344..0000000000 --- a/src/network/networkd-netdev-bond.h +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "in-addr-util.h" -#include "list.h" - -#include "networkd-netdev.h" - -/* - * Maximum number of targets supported by the kernel for a single - * bond netdev. - */ -#define NETDEV_BOND_ARP_TARGETS_MAX 16 - -typedef enum BondMode { - NETDEV_BOND_MODE_BALANCE_RR, - NETDEV_BOND_MODE_ACTIVE_BACKUP, - NETDEV_BOND_MODE_BALANCE_XOR, - NETDEV_BOND_MODE_BROADCAST, - NETDEV_BOND_MODE_802_3AD, - NETDEV_BOND_MODE_BALANCE_TLB, - NETDEV_BOND_MODE_BALANCE_ALB, - _NETDEV_BOND_MODE_MAX, - _NETDEV_BOND_MODE_INVALID = -1 -} BondMode; - -typedef enum BondXmitHashPolicy { - NETDEV_BOND_XMIT_HASH_POLICY_LAYER2, - NETDEV_BOND_XMIT_HASH_POLICY_LAYER34, - NETDEV_BOND_XMIT_HASH_POLICY_LAYER23, - NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23, - NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34, - _NETDEV_BOND_XMIT_HASH_POLICY_MAX, - _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1 -} BondXmitHashPolicy; - -typedef enum BondLacpRate { - NETDEV_BOND_LACP_RATE_SLOW, - NETDEV_BOND_LACP_RATE_FAST, - _NETDEV_BOND_LACP_RATE_MAX, - _NETDEV_BOND_LACP_RATE_INVALID = -1, -} BondLacpRate; - -typedef enum BondAdSelect { - NETDEV_BOND_AD_SELECT_STABLE, - NETDEV_BOND_AD_SELECT_BANDWIDTH, - NETDEV_BOND_AD_SELECT_COUNT, - _NETDEV_BOND_AD_SELECT_MAX, - _NETDEV_BOND_AD_SELECT_INVALID = -1, -} BondAdSelect; - -typedef enum BondFailOverMac { - NETDEV_BOND_FAIL_OVER_MAC_NONE, - NETDEV_BOND_FAIL_OVER_MAC_ACTIVE, - NETDEV_BOND_FAIL_OVER_MAC_FOLLOW, - _NETDEV_BOND_FAIL_OVER_MAC_MAX, - _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1, -} BondFailOverMac; - -typedef enum BondArpValidate { - NETDEV_BOND_ARP_VALIDATE_NONE, - NETDEV_BOND_ARP_VALIDATE_ACTIVE, - NETDEV_BOND_ARP_VALIDATE_BACKUP, - NETDEV_BOND_ARP_VALIDATE_ALL, - _NETDEV_BOND_ARP_VALIDATE_MAX, - _NETDEV_BOND_ARP_VALIDATE_INVALID = -1, -} BondArpValidate; - -typedef enum BondArpAllTargets { - NETDEV_BOND_ARP_ALL_TARGETS_ANY, - NETDEV_BOND_ARP_ALL_TARGETS_ALL, - _NETDEV_BOND_ARP_ALL_TARGETS_MAX, - _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1, -} BondArpAllTargets; - -typedef enum BondPrimaryReselect { - NETDEV_BOND_PRIMARY_RESELECT_ALWAYS, - NETDEV_BOND_PRIMARY_RESELECT_BETTER, - NETDEV_BOND_PRIMARY_RESELECT_FAILURE, - _NETDEV_BOND_PRIMARY_RESELECT_MAX, - _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1, -} BondPrimaryReselect; - -typedef struct ArpIpTarget { - union in_addr_union ip; - - LIST_FIELDS(struct ArpIpTarget, arp_ip_target); -} ArpIpTarget; - -typedef struct Bond { - NetDev meta; - - BondMode mode; - BondXmitHashPolicy xmit_hash_policy; - BondLacpRate lacp_rate; - BondAdSelect ad_select; - BondFailOverMac fail_over_mac; - BondArpValidate arp_validate; - BondArpAllTargets arp_all_targets; - BondPrimaryReselect primary_reselect; - - bool all_slaves_active; - - unsigned resend_igmp; - unsigned packets_per_slave; - unsigned num_grat_arp; - unsigned min_links; - - usec_t miimon; - usec_t updelay; - usec_t downdelay; - usec_t arp_interval; - usec_t lp_interval; - - int n_arp_ip_targets; - ArpIpTarget *arp_ip_targets; -} Bond; - -DEFINE_NETDEV_CAST(BOND, Bond); -extern const NetDevVTable bond_vtable; - -const char *bond_mode_to_string(BondMode d) _const_; -BondMode bond_mode_from_string(const char *d) _pure_; - -const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_; -BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_; - -const char *bond_lacp_rate_to_string(BondLacpRate d) _const_; -BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_; - -const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_; -BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_; - -const char *bond_ad_select_to_string(BondAdSelect d) _const_; -BondAdSelect bond_ad_select_from_string(const char *d) _pure_; - -const char *bond_arp_validate_to_string(BondArpValidate d) _const_; -BondArpValidate bond_arp_validate_from_string(const char *d) _pure_; - -const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_; -BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_; - -const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_; -BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_; - -int config_parse_bond_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_xmit_hash_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_lacp_rate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_ad_select(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_fail_over_mac(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_arp_validate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_arp_all_targets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_primary_reselect(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_arp_ip_target_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c deleted file mode 100644 index a5085d2b19..0000000000 --- a/src/network/networkd-netdev-bridge.c +++ /dev/null @@ -1,146 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "missing.h" -#include "netlink-util.h" -#include "networkd.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"); - } - - if (b->mcast_querier >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); - } - - if (b->mcast_snooping >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); - } - - if (b->vlan_filtering >= 0) { - r = sd_netlink_message_append_u8(req, IFLA_BR_VLAN_FILTERING, b->vlan_filtering); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING 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; -} - -static void bridge_init(NetDev *n) { - Bridge *b; - - b = BRIDGE(n); - - assert(b); - - b->mcast_querier = -1; - b->mcast_snooping = -1; - b->vlan_filtering = -1; -} - -const NetDevVTable bridge_vtable = { - .object_size = sizeof(Bridge), - .init = bridge_init, - .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 deleted file mode 100644 index a637aea0a3..0000000000 --- a/src/network/networkd-netdev-bridge.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "networkd-netdev.h" - -typedef struct Bridge { - NetDev meta; - - int mcast_querier; - int mcast_snooping; - int vlan_filtering; - - usec_t forward_delay; - usec_t hello_time; - usec_t max_age; -} Bridge; - -DEFINE_NETDEV_CAST(BRIDGE, Bridge); -extern const NetDevVTable bridge_vtable; diff --git a/src/network/networkd-netdev-dummy.c b/src/network/networkd-netdev-dummy.c deleted file mode 100644 index 6617a86c20..0000000000 --- a/src/network/networkd-netdev-dummy.c +++ /dev/null @@ -1,28 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Susant Sahani - 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 . -***/ - - -#include "networkd-netdev-dummy.h" - -const NetDevVTable dummy_vtable = { - .object_size = sizeof(Dummy), - .sections = "Match\0NetDev\0", - .create_type = NETDEV_CREATE_INDEPENDENT, -}; diff --git a/src/network/networkd-netdev-dummy.h b/src/network/networkd-netdev-dummy.h deleted file mode 100644 index efe302267e..0000000000 --- a/src/network/networkd-netdev-dummy.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "networkd-netdev.h" - -typedef struct Dummy { - NetDev meta; -} Dummy; - -DEFINE_NETDEV_CAST(DUMMY, Dummy); -extern const NetDevVTable dummy_vtable; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf deleted file mode 100644 index 9d69f61376..0000000000 --- a/src/network/networkd-netdev-gperf.gperf +++ /dev/null @@ -1,109 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "network-internal.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-vxlan.h" -#include "networkd-netdev-vrf.h" -#include "networkd-netdev.h" -#include "vlan-util.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name network_netdev_gperf_hash -%define lookup-function-name network_netdev_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) -NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) -NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) -NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) -NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) -NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) -VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) -MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) -Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) -Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) -Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) -Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) -Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) -Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) -Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) -Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) -Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) -Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group) -VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) -VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) -VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) -VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) -VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) -VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) -VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) -VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) -VXLAN.PortRange, config_parse_port_range, 0, 0 -VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port) -Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) -Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) -Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) -Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) -Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) -Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 -Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) -Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) -Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) -Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) -Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) -Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) -Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) -Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) -Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) -Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) -Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) -Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) -Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) -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) -Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) -Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) -Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) -VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) diff --git a/src/network/networkd-netdev-ipvlan.c b/src/network/networkd-netdev-ipvlan.c deleted file mode 100644 index af4177e43a..0000000000 --- a/src/network/networkd-netdev-ipvlan.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013-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 . -***/ - -#include - -#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", - [NETDEV_IPVLAN_MODE_L3] = "L3", -}; - -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; - int r; - - assert(netdev); - 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) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m"); - } - - return 0; -} - -static void ipvlan_init(NetDev *n) { - IPVlan *m; - - assert(n); - - m = IPVLAN(n); - - assert(m); - - m->mode = _NETDEV_IPVLAN_MODE_INVALID; -} - -const NetDevVTable ipvlan_vtable = { - .object_size = sizeof(IPVlan), - .init = ipvlan_init, - .sections = "Match\0NetDev\0IPVLAN\0", - .fill_message_create = netdev_ipvlan_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, -}; diff --git a/src/network/networkd-netdev-ipvlan.h b/src/network/networkd-netdev-ipvlan.h deleted file mode 100644 index 10d4079844..0000000000 --- a/src/network/networkd-netdev-ipvlan.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014-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 . -***/ - -#include "missing.h" -#include "networkd-netdev.h" - -typedef enum IPVlanMode { - NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, - NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3, - _NETDEV_IPVLAN_MODE_MAX, - _NETDEV_IPVLAN_MODE_INVALID = -1 -} IPVlanMode; - -typedef struct IPVlan { - NetDev meta; - - IPVlanMode mode; -} IPVlan; - -DEFINE_NETDEV_CAST(IPVLAN, IPVlan); -extern const NetDevVTable ipvlan_vtable; - -const char *ipvlan_mode_to_string(IPVlanMode d) _const_; -IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; - -int config_parse_ipvlan_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); diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c deleted file mode 100644 index 48e98aa51b..0000000000 --- a/src/network/networkd-netdev-macvlan.c +++ /dev/null @@ -1,89 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#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", - [NETDEV_MACVLAN_MODE_VEPA] = "vepa", - [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge", - [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru", -}; - -DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode"); - -static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { - MacVlan *m; - int r; - - assert(netdev); - assert(link); - assert(netdev->ifname); - - if (netdev->kind == NETDEV_KIND_MACVLAN) - m = MACVLAN(netdev); - else - m = MACVTAP(netdev); - - assert(m); - - if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { - r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m"); - } - - return 0; -} - -static void macvlan_init(NetDev *n) { - MacVlan *m; - - assert(n); - - if (n->kind == NETDEV_KIND_MACVLAN) - m = MACVLAN(n); - else - m = MACVTAP(n); - - assert(m); - - m->mode = _NETDEV_MACVLAN_MODE_INVALID; -} - -const NetDevVTable macvtap_vtable = { - .object_size = sizeof(MacVlan), - .init = macvlan_init, - .sections = "Match\0NetDev\0MACVTAP\0", - .fill_message_create = netdev_macvlan_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, -}; - -const NetDevVTable macvlan_vtable = { - .object_size = sizeof(MacVlan), - .init = macvlan_init, - .sections = "Match\0NetDev\0MACVLAN\0", - .fill_message_create = netdev_macvlan_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, -}; diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h deleted file mode 100644 index 3663f4f051..0000000000 --- a/src/network/networkd-netdev-macvlan.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct MacVlan MacVlan; - -#include "networkd-netdev.h" - -typedef enum MacVlanMode { - NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE, - NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA, - NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE, - NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU, - _NETDEV_MACVLAN_MODE_MAX, - _NETDEV_MACVLAN_MODE_INVALID = -1 -} MacVlanMode; - -struct MacVlan { - NetDev meta; - - MacVlanMode mode; -}; - -DEFINE_NETDEV_CAST(MACVLAN, MacVlan); -DEFINE_NETDEV_CAST(MACVTAP, MacVlan); -extern const NetDevVTable macvlan_vtable; -extern const NetDevVTable macvtap_vtable; - -const char *macvlan_mode_to_string(MacVlanMode d) _const_; -MacVlanMode macvlan_mode_from_string(const char *d) _pure_; - -int config_parse_macvlan_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); diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c deleted file mode 100644 index 77a4734df8..0000000000 --- a/src/network/networkd-netdev-tunnel.c +++ /dev/null @@ -1,725 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-netlink.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" - -#define DEFAULT_TNL_HOP_LIMIT 64 -#define IP6_FLOWINFO_FLOWLABEL htobe32(0x000FFFFF) - -static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = { - [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6", - [NETDEV_IP6_TNL_MODE_IPIP6] = "ipip6", - [NETDEV_IP6_TNL_MODE_ANYIP6] = "any", -}; - -DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode, "Failed to parse ip6 tunnel Mode"); - -static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = IPIP(netdev); - int r; - - assert(netdev); - assert(link); - assert(m); - assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); - - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); - - return r; -} - -static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = SIT(netdev); - int r; - - assert(netdev); - assert(link); - assert(m); - assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); - - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); - - return r; -} - -static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t; - int r; - - assert(netdev); - - if (netdev->kind == NETDEV_KIND_GRE) - t = GRE(netdev); - else - t = GRETAP(netdev); - - assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); - assert(link); - assert(m); - - r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in); - if (r < 0) - log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_TOS, t->tos); - if (r < 0) - log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m"); - - return r; -} - -static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t; - int r; - - assert(netdev); - - if (netdev->kind == NETDEV_KIND_IP6GRE) - t = IP6GRE(netdev); - else - t = IP6GRETAP(netdev); - - assert(t); - assert(t->family == AF_INET6); - assert(link); - assert(m); - - r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_REMOTE, &t->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); - - if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_GRE_FLOWINFO, t->ipv6_flowlabel); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLOWINFO attribute: %m"); - } - - r = sd_netlink_message_append_u32(m, IFLA_GRE_FLAGS, t->flags); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLAGS attribute: %m"); - - return r; -} - -static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = VTI(netdev); - uint32_t ikey, okey; - int r; - - assert(link); - assert(m); - assert(t); - - if (t->key != 0) - ikey = okey = htobe32(t->key); - else { - ikey = htobe32(t->ikey); - okey = htobe32(t->okey); - } - - r = sd_netlink_message_append_u32(m, IFLA_VTI_IKEY, ikey); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_IKEY attribute: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_VTI_OKEY, okey); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m"); - - return 0; -} - -static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = VTI(netdev); - int r; - - assert(netdev); - assert(link); - assert(m); - assert(t); - assert(t->family == AF_INET); - - r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - - r = netdev_vti_fill_message_key(netdev, link, m); - if (r < 0) - return r; - - r = sd_netlink_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - return r; -} - -static int netdev_vti6_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = VTI6(netdev); - int r; - - assert(netdev); - assert(link); - assert(m); - assert(t); - assert(t->family == AF_INET6); - - r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - - r = netdev_vti_fill_message_key(netdev, link, m); - if (r < 0) - return r; - - r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_LOCAL, &t->local.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_REMOTE, &t->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - return r; -} - -static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = IP6TNL(netdev); - uint8_t proto; - int r; - - assert(netdev); - assert(link); - assert(m); - assert(t); - assert(t->family == AF_INET6); - - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLOWINFO, t->ipv6_flowlabel); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLOWINFO attribute: %m"); - } - - if (t->copy_dscp) - t->flags |= IP6_TNL_F_RCV_DSCP_COPY; - - if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m"); - } - - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); - - switch (t->ip6tnl_mode) { - case NETDEV_IP6_TNL_MODE_IP6IP6: - proto = IPPROTO_IPV6; - break; - case NETDEV_IP6_TNL_MODE_IPIP6: - proto = IPPROTO_IPIP; - break; - case NETDEV_IP6_TNL_MODE_ANYIP6: - default: - proto = 0; - break; - } - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_MODE attribute: %m"); - - return r; -} - -static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { - Tunnel *t = NULL; - - assert(netdev); - assert(filename); - - switch (netdev->kind) { - case NETDEV_KIND_IPIP: - t = IPIP(netdev); - break; - case NETDEV_KIND_SIT: - t = SIT(netdev); - break; - case NETDEV_KIND_GRE: - t = GRE(netdev); - break; - case NETDEV_KIND_GRETAP: - t = GRETAP(netdev); - break; - case NETDEV_KIND_IP6GRE: - t = IP6GRE(netdev); - break; - case NETDEV_KIND_IP6GRETAP: - t = IP6GRETAP(netdev); - break; - case NETDEV_KIND_VTI: - t = VTI(netdev); - break; - case NETDEV_KIND_VTI6: - t = VTI6(netdev); - break; - case NETDEV_KIND_IP6TNL: - t = IP6TNL(netdev); - break; - default: - assert_not_reached("Invalid tunnel kind"); - } - - assert(t); - - 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; - } - - if (netdev->kind == NETDEV_KIND_IP6TNL) { - if (t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) { - log_warning("IP6 Tunnel without mode configured in %s. Ignoring", filename); - return -EINVAL; - } - } - - return 0; -} - -int config_parse_tunnel_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Tunnel *t = userdata; - union in_addr_union *addr = data, buffer; - int r, f; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (streq(rvalue, "any")) { - t->family = 0; - return 0; - } else { - - 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; - *addr = buffer; - - return 0; -} - -int config_parse_tunnel_key(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) { - union in_addr_union buffer; - Tunnel *t = userdata; - uint32_t k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = in_addr_from_string(AF_INET, rvalue, &buffer); - if (r < 0) { - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue); - return 0; - } - } else - k = be32toh(buffer.in.s_addr); - - if (streq(lvalue, "Key")) - t->key = k; - else if (streq(lvalue, "InputKey")) - t->ikey = k; - else - t->okey = k; - - return 0; -} - -int config_parse_ipv6_flowlabel(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) { - IPv6FlowLabel *ipv6_flowlabel = data; - Tunnel *t = userdata; - int k = 0; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(ipv6_flowlabel); - - if (streq(rvalue, "inherit")) { - *ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; - 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) - 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 = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; - t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; - } - } - - return 0; -} - -int config_parse_encap_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) { - Tunnel *t = userdata; - int k = 0; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (streq(rvalue, "none")) - t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; - 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); - return 0; - } - - if (k > 255 || k < 0) - 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; - } - } - - return 0; -} - -static void ipip_init(NetDev *n) { - Tunnel *t = IPIP(n); - - assert(n); - assert(t); - - t->pmtudisc = true; - t->family = AF_UNSPEC; -} - -static void sit_init(NetDev *n) { - Tunnel *t = SIT(n); - - assert(n); - assert(t); - - t->pmtudisc = true; - t->family = AF_UNSPEC; -} - -static void vti_init(NetDev *n) { - Tunnel *t; - - assert(n); - - if (n->kind == NETDEV_KIND_VTI) - t = VTI(n); - else - t = VTI6(n); - - assert(t); - - t->pmtudisc = true; -} - -static void gre_init(NetDev *n) { - Tunnel *t; - - assert(n); - - if (n->kind == NETDEV_KIND_GRE) - t = GRE(n); - else - t = GRETAP(n); - - assert(t); - - t->pmtudisc = true; - t->family = AF_UNSPEC; -} - -static void ip6gre_init(NetDev *n) { - Tunnel *t; - - assert(n); - - if (n->kind == NETDEV_KIND_IP6GRE) - t = IP6GRE(n); - else - t = IP6GRETAP(n); - - assert(t); - - t->ttl = DEFAULT_TNL_HOP_LIMIT; -} - -static void ip6tnl_init(NetDev *n) { - Tunnel *t = IP6TNL(n); - - assert(n); - assert(t); - - t->ttl = DEFAULT_TNL_HOP_LIMIT; - t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; - t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID; - t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID; -} - -const NetDevVTable ipip_vtable = { - .object_size = sizeof(Tunnel), - .init = ipip_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_ipip_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable sit_vtable = { - .object_size = sizeof(Tunnel), - .init = sit_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_sit_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable vti_vtable = { - .object_size = sizeof(Tunnel), - .init = vti_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_vti_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable vti6_vtable = { - .object_size = sizeof(Tunnel), - .init = vti_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_vti6_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable gre_vtable = { - .object_size = sizeof(Tunnel), - .init = gre_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_gre_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable gretap_vtable = { - .object_size = sizeof(Tunnel), - .init = gre_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_gre_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable ip6gre_vtable = { - .object_size = sizeof(Tunnel), - .init = ip6gre_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_ip6gre_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable ip6gretap_vtable = { - .object_size = sizeof(Tunnel), - .init = ip6gre_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_ip6gre_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; - -const NetDevVTable ip6tnl_vtable = { - .object_size = sizeof(Tunnel), - .init = ip6tnl_init, - .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_ip6tnl_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_tunnel_verify, -}; diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h deleted file mode 100644 index 32a46bd82f..0000000000 --- a/src/network/networkd-netdev-tunnel.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "in-addr-util.h" - -#include "networkd-netdev.h" - -typedef enum Ip6TnlMode { - NETDEV_IP6_TNL_MODE_IP6IP6, - NETDEV_IP6_TNL_MODE_IPIP6, - NETDEV_IP6_TNL_MODE_ANYIP6, - _NETDEV_IP6_TNL_MODE_MAX, - _NETDEV_IP6_TNL_MODE_INVALID = -1, -} Ip6TnlMode; - -typedef enum IPv6FlowLabel { - NETDEV_IPV6_FLOWLABEL_INHERIT = 0xFFFFF + 1, - _NETDEV_IPV6_FLOWLABEL_MAX, - _NETDEV_IPV6_FLOWLABEL_INVALID = -1, -} IPv6FlowLabel; - -typedef struct Tunnel { - NetDev meta; - - uint8_t encap_limit; - - int family; - int ipv6_flowlabel; - - unsigned ttl; - unsigned tos; - unsigned flags; - - uint32_t key; - uint32_t ikey; - uint32_t okey; - - union in_addr_union local; - union in_addr_union remote; - - Ip6TnlMode ip6tnl_mode; - - bool pmtudisc; - bool copy_dscp; -} Tunnel; - -DEFINE_NETDEV_CAST(IPIP, Tunnel); -DEFINE_NETDEV_CAST(GRE, Tunnel); -DEFINE_NETDEV_CAST(GRETAP, Tunnel); -DEFINE_NETDEV_CAST(IP6GRE, Tunnel); -DEFINE_NETDEV_CAST(IP6GRETAP, Tunnel); -DEFINE_NETDEV_CAST(SIT, Tunnel); -DEFINE_NETDEV_CAST(VTI, Tunnel); -DEFINE_NETDEV_CAST(VTI6, Tunnel); -DEFINE_NETDEV_CAST(IP6TNL, Tunnel); -extern const NetDevVTable ipip_vtable; -extern const NetDevVTable sit_vtable; -extern const NetDevVTable vti_vtable; -extern const NetDevVTable vti6_vtable; -extern const NetDevVTable gre_vtable; -extern const NetDevVTable gretap_vtable; -extern const NetDevVTable ip6gre_vtable; -extern const NetDevVTable ip6gretap_vtable; -extern const NetDevVTable ip6tnl_vtable; - -const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_; -Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_; - -int config_parse_ip6tnl_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_tunnel_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_ipv6_flowlabel(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_encap_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_tunnel_key(const char *unit, const char *filename, - unsigned line, const char *section, - unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, - void *userdata); diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c deleted file mode 100644 index 088a4d8d32..0000000000 --- a/src/network/networkd-netdev-tuntap.c +++ /dev/null @@ -1,183 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "networkd-netdev-tuntap.h" -#include "user-util.h" - -#define TUN_DEV "/dev/net/tun" - -static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) { - TunTap *t; - - assert(netdev); - assert(netdev->ifname); - assert(ifr); - - if (netdev->kind == NETDEV_KIND_TAP) { - t = TAP(netdev); - ifr->ifr_flags |= IFF_TAP; - } else { - t = TUN(netdev); - ifr->ifr_flags |= IFF_TUN; - } - - if (!t->packet_info) - ifr->ifr_flags |= IFF_NO_PI; - - if (t->one_queue) - ifr->ifr_flags |= IFF_ONE_QUEUE; - - if (t->multi_queue) - ifr->ifr_flags |= IFF_MULTI_QUEUE; - - if (t->vnet_hdr) - ifr->ifr_flags |= IFF_VNET_HDR; - - strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1); - - return 0; -} - -static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { - _cleanup_close_ int fd; - TunTap *t = NULL; - const char *user; - const char *group; - uid_t uid; - gid_t gid; - int r; - - assert(netdev); - assert(ifr); - - fd = open(TUN_DEV, O_RDWR); - if (fd < 0) - return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); - - r = ioctl(fd, TUNSETIFF, ifr); - if (r < 0) - return log_netdev_error_errno(netdev, -errno, "TUNSETIFF failed on tun dev: %m"); - - if (netdev->kind == NETDEV_KIND_TAP) - t = TAP(netdev); - else - t = TUN(netdev); - - assert(t); - - if (t->user_name) { - - user = t->user_name; - - r = get_user_creds(&user, &uid, NULL, NULL, NULL); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); - - r = ioctl(fd, TUNSETOWNER, uid); - if (r < 0) - return log_netdev_error_errno(netdev, -errno, "TUNSETOWNER failed on tun dev: %m"); - } - - if (t->group_name) { - - group = t->group_name; - - r = get_group_creds(&group, &gid); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); - - r = ioctl(fd, TUNSETGROUP, gid); - if (r < 0) - return log_netdev_error_errno(netdev, -errno, "TUNSETGROUP failed on tun dev: %m"); - - } - - r = ioctl(fd, TUNSETPERSIST, 1); - if (r < 0) - return log_netdev_error_errno(netdev, -errno, "TUNSETPERSIST failed on tun dev: %m"); - - return 0; -} - -static int netdev_create_tuntap(NetDev *netdev) { - struct ifreq ifr = {}; - int r; - - r = netdev_fill_tuntap_message(netdev, &ifr); - if (r < 0) - return r; - - return netdev_tuntap_add(netdev, &ifr); -} - -static void tuntap_done(NetDev *netdev) { - TunTap *t = NULL; - - assert(netdev); - - if (netdev->kind == NETDEV_KIND_TUN) - t = TUN(netdev); - else - t = TAP(netdev); - - assert(t); - - t->user_name = mfree(t->user_name); - t->group_name = mfree(t->group_name); -} - -static int tuntap_verify(NetDev *netdev, const char *filename) { - assert(netdev); - - if (netdev->mtu) - log_netdev_warning(netdev, "MTU configured for %s, ignoring", netdev_kind_to_string(netdev->kind)); - - if (netdev->mac) - log_netdev_warning(netdev, "MAC configured for %s, ignoring", netdev_kind_to_string(netdev->kind)); - - return 0; -} - -const NetDevVTable tun_vtable = { - .object_size = sizeof(TunTap), - .sections = "Match\0NetDev\0Tun\0", - .config_verify = tuntap_verify, - .done = tuntap_done, - .create = netdev_create_tuntap, - .create_type = NETDEV_CREATE_INDEPENDENT, -}; - -const NetDevVTable tap_vtable = { - .object_size = sizeof(TunTap), - .sections = "Match\0NetDev\0Tap\0", - .config_verify = tuntap_verify, - .done = tuntap_done, - .create = netdev_create_tuntap, - .create_type = NETDEV_CREATE_INDEPENDENT, -}; diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h deleted file mode 100644 index 120f00a353..0000000000 --- a/src/network/networkd-netdev-tuntap.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct TunTap TunTap; - -#include "networkd-netdev.h" - -struct TunTap { - NetDev meta; - - char *user_name; - char *group_name; - bool one_queue; - bool multi_queue; - bool packet_info; - bool vnet_hdr; -}; - -DEFINE_NETDEV_CAST(TUN, TunTap); -DEFINE_NETDEV_CAST(TAP, TunTap); -extern const NetDevVTable tun_vtable; -extern const NetDevVTable tap_vtable; diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c deleted file mode 100644 index b122a06c25..0000000000 --- a/src/network/networkd-netdev-veth.c +++ /dev/null @@ -1,111 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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; - int r; - - assert(netdev); - assert(!link); - 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"); - - if (v->ifname_peer) { - r = sd_netlink_message_append_string(m, IFLA_IFNAME, v->ifname_peer); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - } - - if (v->mac_peer) { - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, v->mac_peer); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - return r; -} - -static int netdev_veth_verify(NetDev *netdev, const char *filename) { - Veth *v; - int r; - - assert(netdev); - assert(filename); - - v = VETH(netdev); - - assert(v); - - if (!v->ifname_peer) { - log_warning("Veth NetDev without peer name configured in %s. Ignoring", - filename); - return -EINVAL; - } - - if (!v->mac_peer) { - r = netdev_get_mac(v->ifname_peer, &v->mac_peer); - if (r < 0) { - log_warning("Failed to generate predictable MAC address for %s. Ignoring", - v->ifname_peer); - return -EINVAL; - } - } - - return 0; -} - -static void veth_done(NetDev *n) { - Veth *v; - - assert(n); - - v = VETH(n); - - assert(v); - - free(v->ifname_peer); - free(v->mac_peer); -} - -const NetDevVTable veth_vtable = { - .object_size = sizeof(Veth), - .sections = "Match\0NetDev\0Peer\0", - .done = veth_done, - .fill_message_create = netdev_veth_fill_message_create, - .create_type = NETDEV_CREATE_INDEPENDENT, - .config_verify = netdev_veth_verify, -}; diff --git a/src/network/networkd-netdev-veth.h b/src/network/networkd-netdev-veth.h deleted file mode 100644 index e69bfbc8f0..0000000000 --- a/src/network/networkd-netdev-veth.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct Veth Veth; - -#include "networkd-netdev.h" - -struct Veth { - NetDev meta; - - char *ifname_peer; - struct ether_addr *mac_peer; -}; - -DEFINE_NETDEV_CAST(VETH, Veth); -extern const NetDevVTable veth_vtable; diff --git a/src/network/networkd-netdev-vlan.c b/src/network/networkd-netdev-vlan.c deleted file mode 100644 index 3cc072388f..0000000000 --- a/src/network/networkd-netdev-vlan.c +++ /dev/null @@ -1,78 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "networkd-netdev-vlan.h" -#include "vlan-util.h" - -static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { - VLan *v; - int r; - - assert(netdev); - assert(link); - assert(req); - - v = VLAN(netdev); - - assert(v); - - r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_ID attribute: %m"); - - return 0; -} - -static int netdev_vlan_verify(NetDev *netdev, const char *filename) { - VLan *v; - - assert(netdev); - assert(filename); - - v = VLAN(netdev); - - assert(v); - - if (v->id == VLANID_INVALID) { - log_warning("VLAN without valid Id (%"PRIu16") configured in %s.", v->id, filename); - return -EINVAL; - } - - return 0; -} - -static void vlan_init(NetDev *netdev) { - VLan *v = VLAN(netdev); - - assert(netdev); - assert(v); - - v->id = VLANID_INVALID; -} - -const NetDevVTable vlan_vtable = { - .object_size = sizeof(VLan), - .init = vlan_init, - .sections = "Match\0NetDev\0VLAN\0", - .fill_message_create = netdev_vlan_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_vlan_verify, -}; diff --git a/src/network/networkd-netdev-vlan.h b/src/network/networkd-netdev-vlan.h deleted file mode 100644 index 2dfe314b6e..0000000000 --- a/src/network/networkd-netdev-vlan.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct VLan VLan; - -#include "networkd-netdev.h" - -struct VLan { - NetDev meta; - - uint16_t id; -}; - -DEFINE_NETDEV_CAST(VLAN, VLan); -extern const NetDevVTable vlan_vtable; diff --git a/src/network/networkd-netdev-vrf.c b/src/network/networkd-netdev-vrf.c deleted file mode 100644 index 89bd142e8c..0000000000 --- a/src/network/networkd-netdev-vrf.c +++ /dev/null @@ -1,50 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2016 Andreas Rammhold - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-netlink.h" -#include "missing.h" -#include "networkd-netdev-vrf.h" - -static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Vrf *v; - int r; - - assert(netdev); - assert(!link); - assert(m); - - v = VRF(netdev); - - assert(v); - - r = sd_netlink_message_append_u32(m, IFLA_VRF_TABLE, v->table_id); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IPLA_VRF_TABLE attribute: %m"); - - return r; -} - -const NetDevVTable vrf_vtable = { - .object_size = sizeof(Vrf), - .sections = "NetDev\0VRF\0", - .fill_message_create = netdev_vrf_fill_message_create, - .create_type = NETDEV_CREATE_MASTER, -}; diff --git a/src/network/networkd-netdev-vrf.h b/src/network/networkd-netdev-vrf.h deleted file mode 100644 index 3d92a26a4d..0000000000 --- a/src/network/networkd-netdev-vrf.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2016 Andreas Rammhold - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef struct Vrf Vrf; - -#include "networkd-netdev.h" - -struct Vrf { - NetDev meta; - - uint32_t table_id; -}; - -DEFINE_NETDEV_CAST(VRF, Vrf); -extern const NetDevVTable vrf_vtable; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c deleted file mode 100644 index 724f9861be..0000000000 --- a/src/network/networkd-netdev-vxlan.c +++ /dev/null @@ -1,296 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-netlink.h" - -#include "conf-parser.h" -#include "alloc-util.h" -#include "extract-word.h" -#include "parse-util.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; - int r; - - assert(netdev); - 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); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); - } - - r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); - - if (v->ttl) { - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); - } - - if (v->tos) { - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); - } - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LEARNING attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_RSC, v->route_short_circuit); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_RSC attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_PROXY, v->arp_proxy); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PROXY attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L2MISS, v->l2miss); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L2MISS attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L3MISS, v->l3miss); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); - - if (v->fdb_ageing) { - r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); - if (r < 0) - 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"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_TX attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m"); - - r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m"); - - if (v->port_range.low || v->port_range.high) { - struct ifla_vxlan_port_range port_range; - - port_range.low = htobe16(v->port_range.low); - port_range.high = htobe16(v->port_range.high); - - r = sd_netlink_message_append_data(m, IFLA_VXLAN_PORT_RANGE, &port_range, sizeof(port_range)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT_RANGE attribute: %m"); - } - - if (v->group_policy) { - r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GBP attribute: %m"); - } - - return r; -} - -int config_parse_vxlan_group_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - VxLan *v = userdata; - union in_addr_union *addr = data, buffer; - int r, f; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = in_addr_from_string_auto(rvalue, &f, &buffer); - if (r < 0) { - 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, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue); - return 0; - } - - v->family = f; - *addr = buffer; - - return 0; -} - -int config_parse_port_range(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 *word = NULL; - VxLan *v = userdata; - unsigned low, high; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract VXLAN port range, ignoring: %s", rvalue); - return 0; - } - - if (r == 0) - return 0; - - r = parse_range(word, &low, &high); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN port range '%s'", word); - return 0; - } - - if (low <= 0 || low > 65535 || high <= 0 || high > 65535) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", word); - return 0; - } - - if (high < low) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse VXLAN port range '%s'. Port range %u .. %u not valid", word, low, high); - return 0; - } - - v->port_range.low = low; - v->port_range.high = high; - - return 0; -} - -int config_parse_destination_port(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) { - VxLan *v = userdata; - uint16_t port; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou16(rvalue, &port); - if (r < 0 || port <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN destination port '%s'.", rvalue); - return 0; - } - - v->dest_port = port; - - return 0; -} - -static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { - VxLan *v = VXLAN(netdev); - - assert(netdev); - assert(v); - assert(filename); - - if (v->id > VXLAN_VID_MAX) { - log_warning("VXLAN without valid Id configured in %s. Ignoring", filename); - return -EINVAL; - } - - return 0; -} - -static void vxlan_init(NetDev *netdev) { - VxLan *v; - - assert(netdev); - - v = VXLAN(netdev); - - assert(v); - - v->id = VXLAN_VID_MAX + 1; - v->learning = true; - v->udpcsum = false; - v->udp6zerocsumtx = false; - v->udp6zerocsumrx = false; -} - -const NetDevVTable vxlan_vtable = { - .object_size = sizeof(VxLan), - .init = vxlan_init, - .sections = "Match\0NetDev\0VXLAN\0", - .fill_message_create = netdev_vxlan_fill_message_create, - .create_type = NETDEV_CREATE_STACKED, - .config_verify = netdev_vxlan_verify, -}; diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h deleted file mode 100644 index 4614c66fd1..0000000000 --- a/src/network/networkd-netdev-vxlan.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct VxLan VxLan; - -#include "in-addr-util.h" -#include "networkd-netdev.h" - -#define VXLAN_VID_MAX (1u << 24) - 1 - -struct VxLan { - NetDev meta; - - uint64_t id; - - int family; - union in_addr_union group; - - unsigned tos; - unsigned ttl; - unsigned max_fdb; - - uint16_t dest_port; - - usec_t fdb_ageing; - - bool learning; - bool arp_proxy; - bool route_short_circuit; - bool l2miss; - bool l3miss; - bool udpcsum; - bool udp6zerocsumtx; - bool udp6zerocsumrx; - bool group_policy; - - struct ifla_vxlan_port_range port_range; -}; - -DEFINE_NETDEV_CAST(VXLAN, VxLan); -extern const NetDevVTable vxlan_vtable; - -int config_parse_vxlan_group_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); -int config_parse_port_range(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_port(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c deleted file mode 100644 index e7edc366af..0000000000 --- a/src/network/networkd-netdev.c +++ /dev/null @@ -1,716 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "fd-util.h" -#include "list.h" -#include "netlink-util.h" -#include "network-internal.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] = { - - [NETDEV_KIND_BRIDGE] = &bridge_vtable, - [NETDEV_KIND_BOND] = &bond_vtable, - [NETDEV_KIND_VLAN] = &vlan_vtable, - [NETDEV_KIND_MACVLAN] = &macvlan_vtable, - [NETDEV_KIND_MACVTAP] = &macvtap_vtable, - [NETDEV_KIND_IPVLAN] = &ipvlan_vtable, - [NETDEV_KIND_VXLAN] = &vxlan_vtable, - [NETDEV_KIND_IPIP] = &ipip_vtable, - [NETDEV_KIND_GRE] = &gre_vtable, - [NETDEV_KIND_GRETAP] = &gretap_vtable, - [NETDEV_KIND_IP6GRE] = &ip6gre_vtable, - [NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable, - [NETDEV_KIND_SIT] = &sit_vtable, - [NETDEV_KIND_VTI] = &vti_vtable, - [NETDEV_KIND_VTI6] = &vti6_vtable, - [NETDEV_KIND_VETH] = &veth_vtable, - [NETDEV_KIND_DUMMY] = &dummy_vtable, - [NETDEV_KIND_TUN] = &tun_vtable, - [NETDEV_KIND_TAP] = &tap_vtable, - [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable, - [NETDEV_KIND_VRF] = &vrf_vtable, - -}; - -static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { - [NETDEV_KIND_BRIDGE] = "bridge", - [NETDEV_KIND_BOND] = "bond", - [NETDEV_KIND_VLAN] = "vlan", - [NETDEV_KIND_MACVLAN] = "macvlan", - [NETDEV_KIND_MACVTAP] = "macvtap", - [NETDEV_KIND_IPVLAN] = "ipvlan", - [NETDEV_KIND_VXLAN] = "vxlan", - [NETDEV_KIND_IPIP] = "ipip", - [NETDEV_KIND_GRE] = "gre", - [NETDEV_KIND_GRETAP] = "gretap", - [NETDEV_KIND_IP6GRE] = "ip6gre", - [NETDEV_KIND_IP6GRETAP] = "ip6gretap", - [NETDEV_KIND_SIT] = "sit", - [NETDEV_KIND_VETH] = "veth", - [NETDEV_KIND_VTI] = "vti", - [NETDEV_KIND_VTI6] = "vti6", - [NETDEV_KIND_DUMMY] = "dummy", - [NETDEV_KIND_TUN] = "tun", - [NETDEV_KIND_TAP] = "tap", - [NETDEV_KIND_IP6TNL] = "ip6tnl", - [NETDEV_KIND_VRF] = "vrf", - -}; - -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_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - netdev_join_callback *callback; - - if (!netdev) - return; - - rtnl_message_new_synthetic_error(-ENODEV, 0, &m); - - while ((callback = netdev->callbacks)) { - if (m) { - assert(callback->link); - assert(callback->callback); - assert(netdev->manager); - assert(netdev->manager->rtnl); - - callback->callback(netdev->manager->rtnl, m, callback->link); - } - - LIST_REMOVE(callbacks, netdev->callbacks, callback); - link_unref(callback->link); - free(callback); - } -} - -static void netdev_free(NetDev *netdev) { - if (!netdev) - return; - - netdev_cancel_callbacks(netdev); - - if (netdev->ifname) - hashmap_remove(netdev->manager->netdevs, netdev->ifname); - - free(netdev->filename); - - free(netdev->description); - free(netdev->ifname); - free(netdev->mac); - - condition_free_list(netdev->match_host); - condition_free_list(netdev->match_virt); - condition_free_list(netdev->match_kernel); - condition_free_list(netdev->match_arch); - - if (NETDEV_VTABLE(netdev) && - NETDEV_VTABLE(netdev)->done) - NETDEV_VTABLE(netdev)->done(netdev); - - free(netdev); -} - -NetDev *netdev_unref(NetDev *netdev) { - if (netdev && (-- netdev->n_ref <= 0)) - netdev_free(netdev); - - return NULL; -} - -NetDev *netdev_ref(NetDev *netdev) { - if (netdev) - assert_se(++ netdev->n_ref >= 2); - - return netdev; -} - -void netdev_drop(NetDev *netdev) { - if (!netdev || netdev->state == NETDEV_STATE_LINGER) - return; - - netdev->state = NETDEV_STATE_LINGER; - - log_netdev_debug(netdev, "netdev removed"); - - netdev_cancel_callbacks(netdev); - - netdev_unref(netdev); - - return; -} - -int netdev_get(Manager *manager, const char *name, NetDev **ret) { - NetDev *netdev; - - assert(manager); - assert(name); - assert(ret); - - netdev = hashmap_get(manager->netdevs, name); - if (!netdev) { - *ret = NULL; - return -ENOENT; - } - - *ret = netdev; - - return 0; -} - -static int netdev_enter_failed(NetDev *netdev) { - netdev->state = NETDEV_STATE_FAILED; - - netdev_cancel_callbacks(netdev); - - return 0; -} - -static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(netdev); - assert(netdev->state == NETDEV_STATE_READY); - assert(netdev->manager); - assert(netdev->manager->rtnl); - assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)); - assert(link); - assert(callback); - - r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_netlink_message_append_u32(req, IFLA_MASTER, netdev->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m"); - - r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) - return log_netdev_error(netdev, "Could not send rtnetlink message: %m"); - - link_ref(link); - - log_netdev_debug(netdev, "Enslaving link '%s'", link->ifname); - - return 0; -} - -static int netdev_enter_ready(NetDev *netdev) { - netdev_join_callback *callback, *callback_next; - int r; - - assert(netdev); - assert(netdev->ifname); - - if (netdev->state != NETDEV_STATE_CREATING) - return 0; - - netdev->state = NETDEV_STATE_READY; - - log_netdev_info(netdev, "netdev ready"); - - LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) { - /* enslave the links that were attempted to be enslaved before the - * link was ready */ - r = netdev_enslave_ready(netdev, callback->link, callback->callback); - if (r < 0) - return r; - - LIST_REMOVE(callbacks, netdev->callbacks, callback); - link_unref(callback->link); - free(callback); - } - - if (NETDEV_VTABLE(netdev)->post_create) - NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL); - - return 0; -} - -/* callback for netdev's created without a backing Link */ -static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_netdev_unref_ NetDev *netdev = userdata; - int r; - - assert(netdev->state != _NETDEV_STATE_INVALID); - - r = sd_netlink_message_get_errno(m); - if (r == -EEXIST) - log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); - else if (r < 0) { - log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); - netdev_drop(netdev); - - return 1; - } - - log_netdev_debug(netdev, "Created"); - - return 1; -} - -int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { - int r; - - assert(netdev); - assert(netdev->manager); - assert(netdev->manager->rtnl); - assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND, NETDEV_KIND_VRF)); - - if (netdev->state == NETDEV_STATE_READY) { - r = netdev_enslave_ready(netdev, link, callback); - if (r < 0) - return r; - } else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - - r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m); - if (r >= 0) - callback(netdev->manager->rtnl, m, link); - } else { - /* the netdev is not yet read, save this request for when it is */ - netdev_join_callback *cb; - - cb = new0(netdev_join_callback, 1); - if (!cb) - return log_oom(); - - cb->callback = callback; - cb->link = link; - link_ref(link); - - LIST_PREPEND(callbacks, netdev->callbacks, cb); - - log_netdev_debug(netdev, "Will enslave '%s', when ready", link->ifname); - } - - return 0; -} - -int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) { - uint16_t type; - const char *kind; - const char *received_kind; - const char *received_name; - int r, ifindex; - - assert(netdev); - assert(message); - - r = sd_netlink_message_get_type(message, &type); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get rtnl message type: %m"); - - if (type != RTM_NEWLINK) { - log_netdev_error(netdev, "Cannot set ifindex from unexpected rtnl message type."); - return -EINVAL; - } - - r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0) { - log_netdev_error_errno(netdev, r, "Could not get ifindex: %m"); - netdev_enter_failed(netdev); - return r; - } else if (ifindex <= 0) { - log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex); - netdev_enter_failed(netdev); - return -EINVAL; - } - - if (netdev->ifindex > 0) { - if (netdev->ifindex != ifindex) { - log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d", - ifindex, netdev->ifindex); - netdev_enter_failed(netdev); - return -EEXIST; - } else - /* ifindex already set to the same for this netdev */ - return 0; - } - - r = sd_netlink_message_read_string(message, IFLA_IFNAME, &received_name); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get IFNAME: %m"); - - if (!streq(netdev->ifname, received_name)) { - log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name); - netdev_enter_failed(netdev); - return r; - } - - r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m"); - - r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get KIND: %m"); - - r = sd_netlink_message_exit_container(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not exit container: %m"); - - if (netdev->kind == NETDEV_KIND_TAP) - /* the kernel does not distinguish between tun and tap */ - kind = "tun"; - else { - kind = netdev_kind_to_string(netdev->kind); - if (!kind) { - log_netdev_error(netdev, "Could not get kind"); - netdev_enter_failed(netdev); - return -EINVAL; - } - } - - if (!streq(kind, received_kind)) { - log_netdev_error(netdev, - "Received newlink with wrong KIND %s, " - "expected %s", received_kind, kind); - netdev_enter_failed(netdev); - return r; - } - - netdev->ifindex = ifindex; - - log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex); - - netdev_enter_ready(netdev); - - return 0; -} - -#define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48) - -int netdev_get_mac(const char *ifname, struct ether_addr **ret) { - _cleanup_free_ struct ether_addr *mac = NULL; - uint64_t result; - size_t l, sz; - uint8_t *v; - int r; - - assert(ifname); - assert(ret); - - mac = new0(struct ether_addr, 1); - if (!mac) - return -ENOMEM; - - l = strlen(ifname); - sz = sizeof(sd_id128_t) + l; - v = alloca(sz); - - /* fetch some persistent data unique to the machine */ - r = sd_id128_get_machine((sd_id128_t*) v); - if (r < 0) - return r; - - /* combine with some data unique (on this machine) to this - * netdev */ - memcpy(v + sizeof(sd_id128_t), ifname, l); - - /* Let's hash the host machine ID plus the container name. We - * use a fixed, but originally randomly created hash key here. */ - result = siphash24(v, sz, HASH_KEY.bytes); - - assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, &result, ETH_ALEN); - - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - *ret = mac; - mac = NULL; - - return 0; -} - -static int netdev_create(NetDev *netdev, Link *link, - sd_netlink_message_handler_t callback) { - int r; - - assert(netdev); - assert(!link || callback); - - /* create netdev */ - if (NETDEV_VTABLE(netdev)->create) { - assert(!link); - - r = NETDEV_VTABLE(netdev)->create(netdev); - if (r < 0) - return r; - - log_netdev_debug(netdev, "Created"); - } else { - _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) - return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m"); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m"); - - if (netdev->mac) { - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); - } - - if (netdev->mtu) { - r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m"); - } - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m"); - } - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - if (NETDEV_VTABLE(netdev)->fill_message_create) { - r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m); - if (r < 0) - return r; - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); - - if (link) { - r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - } else { - r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); - - netdev_ref(netdev); - } - - netdev->state = NETDEV_STATE_CREATING; - - log_netdev_debug(netdev, "Creating"); - } - - return 0; -} - -/* the callback must be called, possibly after a timeout, as otherwise the Link will hang */ -int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { - int r; - - assert(netdev); - assert(netdev->manager); - assert(netdev->manager->rtnl); - assert(NETDEV_VTABLE(netdev)); - - switch (NETDEV_VTABLE(netdev)->create_type) { - case NETDEV_CREATE_MASTER: - r = netdev_enslave(netdev, link, callback); - if (r < 0) - return r; - - break; - case NETDEV_CREATE_STACKED: - r = netdev_create(netdev, link, callback); - if (r < 0) - return r; - - break; - default: - assert_not_reached("Can not join independent netdev"); - } - - return 0; -} - -static int netdev_load_one(Manager *manager, const char *filename) { - _cleanup_netdev_unref_ NetDev *netdev = NULL; - _cleanup_free_ NetDev *netdev_raw = NULL; - _cleanup_fclose_ FILE *file = NULL; - int r; - - assert(manager); - assert(filename); - - file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - else - return -errno; - } - - if (null_or_empty_fd(fileno(file))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } - - netdev_raw = new0(NetDev, 1); - if (!netdev_raw) - return log_oom(); - - netdev_raw->kind = _NETDEV_KIND_INVALID; - - r = config_parse(NULL, filename, file, - "Match\0NetDev\0", - config_item_perf_lookup, network_netdev_gperf_lookup, - true, false, true, netdev_raw); - if (r < 0) - return r; - - r = fseek(file, 0, SEEK_SET); - if (r < 0) - return -errno; - - /* skip out early if configuration does not match the environment */ - if (net_match_config(NULL, NULL, NULL, NULL, NULL, - netdev_raw->match_host, netdev_raw->match_virt, - netdev_raw->match_kernel, netdev_raw->match_arch, - NULL, NULL, NULL, NULL, NULL, NULL) <= 0) - return 0; - - if (netdev_raw->kind == _NETDEV_KIND_INVALID) { - log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename); - return 0; - } - - if (!netdev_raw->ifname) { - log_warning("NetDev without Name configured in %s. Ignoring", filename); - return 0; - } - - netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size); - if (!netdev) - return log_oom(); - - netdev->n_ref = 1; - netdev->manager = manager; - netdev->state = _NETDEV_STATE_INVALID; - netdev->kind = netdev_raw->kind; - netdev->ifname = netdev_raw->ifname; - - if (NETDEV_VTABLE(netdev)->init) - NETDEV_VTABLE(netdev)->init(netdev); - - r = config_parse(NULL, filename, file, - NETDEV_VTABLE(netdev)->sections, - config_item_perf_lookup, network_netdev_gperf_lookup, - false, false, false, netdev); - if (r < 0) - return r; - - /* verify configuration */ - if (NETDEV_VTABLE(netdev)->config_verify) { - r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename); - if (r < 0) - return 0; - } - - netdev->filename = strdup(filename); - if (!netdev->filename) - return log_oom(); - - if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { - r = netdev_get_mac(netdev->ifname, &netdev->mac); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); - } - - r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev); - if (r < 0) - return r; - - LIST_HEAD_INIT(netdev->callbacks); - - log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); - - switch (NETDEV_VTABLE(netdev)->create_type) { - case NETDEV_CREATE_MASTER: - case NETDEV_CREATE_INDEPENDENT: - r = netdev_create(netdev, NULL, NULL); - if (r < 0) - return 0; - - break; - default: - break; - } - - netdev = NULL; - - return 0; -} - -int netdev_load(Manager *manager) { - _cleanup_strv_free_ char **files = NULL; - NetDev *netdev; - char **f; - int r; - - assert(manager); - - while ((netdev = hashmap_first(manager->netdevs))) - netdev_unref(netdev); - - r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); - if (r < 0) - return log_error_errno(r, "Failed to enumerate netdev files: %m"); - - STRV_FOREACH_BACKWARDS(f, files) { - r = netdev_load_one(manager, *f); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h deleted file mode 100644 index b92a973b85..0000000000 --- a/src/network/networkd-netdev.h +++ /dev/null @@ -1,201 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "sd-netlink.h" - -#include "list.h" -#include "time-util.h" - -typedef struct netdev_join_callback netdev_join_callback; -typedef struct Link Link; - -struct netdev_join_callback { - sd_netlink_message_handler_t callback; - Link *link; - - LIST_FIELDS(netdev_join_callback, callbacks); -}; - -typedef enum NetDevKind { - NETDEV_KIND_BRIDGE, - NETDEV_KIND_BOND, - NETDEV_KIND_VLAN, - NETDEV_KIND_MACVLAN, - NETDEV_KIND_MACVTAP, - NETDEV_KIND_IPVLAN, - NETDEV_KIND_VXLAN, - NETDEV_KIND_IPIP, - NETDEV_KIND_GRE, - NETDEV_KIND_GRETAP, - NETDEV_KIND_IP6GRE, - NETDEV_KIND_IP6GRETAP, - NETDEV_KIND_SIT, - NETDEV_KIND_VETH, - NETDEV_KIND_VTI, - NETDEV_KIND_VTI6, - NETDEV_KIND_IP6TNL, - NETDEV_KIND_DUMMY, - NETDEV_KIND_TUN, - NETDEV_KIND_TAP, - NETDEV_KIND_VRF, - _NETDEV_KIND_MAX, - _NETDEV_KIND_INVALID = -1 -} NetDevKind; - -typedef enum NetDevState { - NETDEV_STATE_FAILED, - NETDEV_STATE_CREATING, - NETDEV_STATE_READY, - NETDEV_STATE_LINGER, - _NETDEV_STATE_MAX, - _NETDEV_STATE_INVALID = -1, -} NetDevState; - -typedef enum NetDevCreateType { - NETDEV_CREATE_INDEPENDENT, - NETDEV_CREATE_MASTER, - NETDEV_CREATE_STACKED, - _NETDEV_CREATE_MAX, - _NETDEV_CREATE_INVALID = -1, -} NetDevCreateType; - -typedef struct Manager Manager; -typedef struct Condition Condition; - -typedef struct NetDev { - Manager *manager; - - int n_ref; - - char *filename; - - Condition *match_host; - Condition *match_virt; - Condition *match_kernel; - Condition *match_arch; - - NetDevState state; - NetDevKind kind; - char *description; - char *ifname; - struct ether_addr *mac; - size_t mtu; - int ifindex; - - LIST_HEAD(netdev_join_callback, callbacks); -} NetDev; - -typedef struct NetDevVTable { - /* How much memory does an object of this unit type need */ - size_t object_size; - - /* Config file sections this netdev kind understands, separated - * by NUL chars */ - const char *sections; - - /* This should reset all type-specific variables. This should - * not allocate memory, and is called with zero-initialized - * data. It should hence only initialize variables that need - * to be set != 0. */ - void (*init)(NetDev *n); - - /* This should free all kind-specific variables. It should be - * idempotent. */ - void (*done)(NetDev *n); - - /* fill in message to create netdev */ - int (*fill_message_create)(NetDev *netdev, Link *link, sd_netlink_message *message); - - /* specifies if netdev is independent, or a master device or a stacked device */ - NetDevCreateType create_type; - - /* 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); -} NetDevVTable; - -extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; - -#define NETDEV_VTABLE(n) netdev_vtable[(n)->kind] - -/* For casting a netdev into the various netdev kinds */ -#define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* UPPERCASE(NetDev *n) { \ - if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ - return NULL; \ - \ - return (MixedCase*) n; \ - } - -/* For casting the various netdev kinds into a netdev */ -#define NETDEV(n) (&(n)->meta) - -int netdev_load(Manager *manager); -void netdev_drop(NetDev *netdev); - -NetDev *netdev_unref(NetDev *netdev); -NetDev *netdev_ref(NetDev *netdev); - -DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref); -#define _cleanup_netdev_unref_ _cleanup_(netdev_unrefp) - -int netdev_get(Manager *manager, const char *name, NetDev **ret); -int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); -int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback); -int netdev_get_mac(const char *ifname, struct ether_addr **ret); -int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t cb); - -const char *netdev_kind_to_string(NetDevKind d) _const_; -NetDevKind netdev_kind_from_string(const char *d) _pure_; - -int config_parse_netdev_kind(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 */ -const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsigned length); - -/* Macros which append INTERFACE= to the message */ - -#define log_netdev_full(netdev, level, error, ...) \ - ({ \ - NetDev *_n = (netdev); \ - _n ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _n->ifname, ##__VA_ARGS__) : \ - log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ - }) - -#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, 0, ##__VA_ARGS__) -#define log_netdev_info(netdev, ...) log_netdev_full(netdev, LOG_INFO, 0, ##__VA_ARGS__) -#define log_netdev_notice(netdev, ...) log_netdev_full(netdev, LOG_NOTICE, 0, ##__VA_ARGS__) -#define log_netdev_warning(netdev, ...) log_netdev_full(netdev, LOG_WARNING, 0, ## __VA_ARGS__) -#define log_netdev_error(netdev, ...) log_netdev_full(netdev, LOG_ERR, 0, ##__VA_ARGS__) - -#define log_netdev_debug_errno(netdev, error, ...) log_netdev_full(netdev, LOG_DEBUG, error, ##__VA_ARGS__) -#define log_netdev_info_errno(netdev, error, ...) log_netdev_full(netdev, LOG_INFO, error, ##__VA_ARGS__) -#define log_netdev_notice_errno(netdev, error, ...) log_netdev_full(netdev, LOG_NOTICE, error, ##__VA_ARGS__) -#define log_netdev_warning_errno(netdev, error, ...) log_netdev_full(netdev, LOG_WARNING, error, ##__VA_ARGS__) -#define log_netdev_error_errno(netdev, error, ...) log_netdev_full(netdev, LOG_ERR, error, ##__VA_ARGS__) - -#define LOG_NETDEV_MESSAGE(netdev, fmt, ...) "MESSAGE=%s: " fmt, (netdev)->ifname, ##__VA_ARGS__ -#define LOG_NETDEV_INTERFACE(netdev) "INTERFACE=%s", (netdev)->ifname diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c deleted file mode 100644 index 6e21676d23..0000000000 --- a/src/network/networkd-network-bus.c +++ /dev/null @@ -1,153 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "networkd.h" -#include "string-util.h" -#include "strv.h" - -static int property_get_ether_addrs( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Network *n = userdata; - const char *ether = NULL; - int r; - - assert(bus); - assert(reply); - assert(n); - - if (n->match_mac) - ether = ether_ntoa(n->match_mac); - - r = sd_bus_message_open_container(reply, 'a', "s"); - if (r < 0) - return r; - - if (ether) { - r = sd_bus_message_append(reply, "s", strempty(ether)); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -const sd_bus_vtable network_vtable[] = { - SD_BUS_VTABLE_START(0), - - SD_BUS_PROPERTY("Description", "s", NULL, offsetof(Network, description), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Network, filename), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MatchMAC", "as", property_get_ether_addrs, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MatchPath", "as", NULL, offsetof(Network, match_path), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MatchDriver", "as", NULL, offsetof(Network, match_driver), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MatchType", "as", NULL, offsetof(Network, match_type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("MatchName", "as", NULL, offsetof(Network, match_name), SD_BUS_VTABLE_PROPERTY_CONST), - - SD_BUS_VTABLE_END -}; - -static char *network_bus_path(Network *network) { - _cleanup_free_ char *name = NULL; - char *networkname, *d, *path; - int r; - - assert(network); - assert(network->filename); - - name = strdup(network->filename); - if (!name) - return NULL; - - networkname = basename(name); - - d = strrchr(networkname, '.'); - if (!d) - return NULL; - - assert(streq(d, ".network")); - - *d = '\0'; - - r = sd_bus_path_encode("/org/freedesktop/network1/network", networkname, &path); - if (r < 0) - return NULL; - - return path; -} - -int network_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; - Network *network; - int r; - - assert(bus); - assert(path); - assert(m); - assert(nodes); - - LIST_FOREACH(networks, network, m->networks) { - char *p; - - p = network_bus_path(network); - if (!p) - return -ENOMEM; - - r = strv_consume(&l, p); - if (r < 0) - return r; - } - - *nodes = l; - l = NULL; - - return 1; -} - -int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Network *network; - _cleanup_free_ char *name = NULL; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(m); - assert(found); - - r = sd_bus_path_decode(path, "/org/freedesktop/network1/network", &name); - if (r < 0) - return 0; - - r = network_get_by_name(m, name, &network); - if (r < 0) - return 0; - - *found = network; - - return 1; -} diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf deleted file mode 100644 index 5172a7b5e9..0000000000 --- a/src/network/networkd-network-gperf.gperf +++ /dev/null @@ -1,127 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "networkd.h" -#include "networkd-conf.h" -#include "network-internal.h" -#include "vlan-util.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name network_network_gperf_hash -%define lookup-function-name network_network_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Match.MACAddress, config_parse_hwaddr, 0, offsetof(Network, match_mac) -Match.Path, config_parse_strv, 0, offsetof(Network, match_path) -Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) -Match.Type, config_parse_strv, 0, offsetof(Network, match_type) -Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) -Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) -Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) -Network.Description, config_parse_string, 0, offsetof(Network, description) -Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) -Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) -Network.VLAN, config_parse_netdev, 0, 0 -Network.MACVLAN, config_parse_netdev, 0, 0 -Network.MACVTAP, config_parse_netdev, 0, 0 -Network.IPVLAN, config_parse_netdev, 0, 0 -Network.VXLAN, config_parse_netdev, 0, 0 -Network.Tunnel, config_parse_tunnel, 0, 0 -Network.VRF, config_parse_netdev, 0, 0 -Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) -Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) -Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) -Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) -Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) -Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) -Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) -Network.Address, config_parse_address, 0, 0 -Network.Gateway, config_parse_gateway, 0, 0 -Network.Domains, config_parse_domains, 0, 0 -Network.DNS, config_parse_strv, 0, offsetof(Network, dns) -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, 0 -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.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) -/* legacy alias for the above */ -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.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) -Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) -Address.Address, config_parse_address, 0, 0 -Address.Peer, config_parse_address, 0, 0 -Address.Broadcast, config_parse_broadcast, 0, 0 -Address.Label, config_parse_label, 0, 0 -Address.PreferredLifetime, config_parse_lifetime, 0, 0 -Route.Gateway, config_parse_gateway, 0, 0 -Route.Destination, config_parse_destination, 0, 0 -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 -Route.Table, config_parse_route_table, 0, 0 -DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) -DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) -DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) -DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) -DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) -DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes) -DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname) -DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) -DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) -DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) -DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) -DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) -DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) -DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) -DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) -DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) -IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) -IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) -DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) -DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) -DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) -DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 -DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) -DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 -DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) -DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) -DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) -DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) -DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) -Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) -Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) -Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) -Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave) -Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root) -Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood) -BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 -BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 -BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid) -BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 -BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 -/* backwards compatibility: do not add new entries to this section */ -Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) -DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) -DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu) -DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname) -DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains) -DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c deleted file mode 100644 index 2b764d4f24..0000000000 --- a/src/network/networkd-network.c +++ /dev/null @@ -1,1051 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "dns-domain.h" -#include "fd-util.h" -#include "hostname-util.h" -#include "network-internal.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; - _cleanup_fclose_ FILE *file = NULL; - char *d; - Route *route; - Address *address; - int r; - - assert(manager); - assert(filename); - - file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - - return -errno; - } - - if (null_or_empty_fd(fileno(file))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } - - network = new0(Network, 1); - if (!network) - return log_oom(); - - network->manager = manager; - - LIST_HEAD_INIT(network->static_addresses); - LIST_HEAD_INIT(network->static_routes); - LIST_HEAD_INIT(network->static_fdb_entries); - - network->stacked_netdevs = hashmap_new(&string_hash_ops); - if (!network->stacked_netdevs) - return log_oom(); - - network->addresses_by_section = hashmap_new(NULL); - if (!network->addresses_by_section) - return log_oom(); - - network->routes_by_section = hashmap_new(NULL); - if (!network->routes_by_section) - return log_oom(); - - network->fdb_entries_by_section = hashmap_new(NULL); - if (!network->fdb_entries_by_section) - return log_oom(); - - network->filename = strdup(filename); - if (!network->filename) - return log_oom(); - - network->name = strdup(basename(filename)); - if (!network->name) - return log_oom(); - - d = strrchr(network->name, '.'); - if (!d) - return -EINVAL; - - assert(streq(d, ".network")); - - *d = '\0'; - - network->dhcp = ADDRESS_FAMILY_NO; - network->dhcp_use_ntp = true; - network->dhcp_use_dns = true; - network->dhcp_use_hostname = true; - network->dhcp_use_routes = true; - network->dhcp_send_hostname = true; - network->dhcp_route_metric = DHCP_ROUTE_METRIC; - network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID; - - network->dhcp_server_emit_dns = true; - network->dhcp_server_emit_ntp = true; - network->dhcp_server_emit_router = true; - network->dhcp_server_emit_timezone = true; - - network->use_bpdu = true; - network->allow_port_to_be_root = true; - network->unicast_flood = true; - - network->lldp_mode = LLDP_MODE_ROUTERS_ONLY; - - 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; - network->duid.type = _DUID_TYPE_INVALID; - network->proxy_arp = -1; - network->ipv6_accept_ra_use_dns = true; - - r = config_parse(NULL, filename, file, - "Match\0" - "Link\0" - "Network\0" - "Address\0" - "Route\0" - "DHCP\0" - "DHCPv4\0" /* compat */ - "DHCPServer\0" - "IPv6AcceptRA\0" - "Bridge\0" - "BridgeFDB\0" - "BridgeVLAN\0", - config_item_perf_lookup, network_network_gperf_lookup, - false, false, true, network); - if (r < 0) - return r; - - /* IPMasquerade=yes implies IPForward=yes */ - if (network->ip_masquerade) - network->ip_forward |= ADDRESS_FAMILY_IPV4; - - LIST_PREPEND(networks, manager->networks, network); - - r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops); - if (r < 0) - return r; - - r = hashmap_put(manager->networks_by_name, network->name, network); - if (r < 0) - return r; - - LIST_FOREACH(routes, route, network->static_routes) { - if (!route->family) { - log_warning("Route section without Gateway field configured in %s. " - "Ignoring", filename); - return 0; - } - } - - LIST_FOREACH(addresses, address, network->static_addresses) { - if (!address->family) { - log_warning("Address section without Address field configured in %s. " - "Ignoring", filename); - return 0; - } - } - - network = NULL; - - return 0; -} - -int network_load(Manager *manager) { - Network *network; - _cleanup_strv_free_ char **files = NULL; - char **f; - int r; - - assert(manager); - - while ((network = manager->networks)) - network_free(network); - - r = conf_files_list_strv(&files, ".network", NULL, network_dirs); - if (r < 0) - return log_error_errno(r, "Failed to enumerate network files: %m"); - - STRV_FOREACH_BACKWARDS(f, files) { - r = network_load_one(manager, *f); - if (r < 0) - return r; - } - - return 0; -} - -void network_free(Network *network) { - NetDev *netdev; - Route *route; - Address *address; - FdbEntry *fdb_entry; - Iterator i; - - if (!network) - return; - - free(network->filename); - - free(network->match_mac); - strv_free(network->match_path); - strv_free(network->match_driver); - strv_free(network->match_type); - strv_free(network->match_name); - - free(network->description); - free(network->dhcp_vendor_class_identifier); - free(network->dhcp_hostname); - - free(network->mac); - - strv_free(network->ntp); - strv_free(network->dns); - strv_free(network->search_domains); - strv_free(network->route_domains); - strv_free(network->bind_carrier); - - netdev_unref(network->bridge); - netdev_unref(network->bond); - netdev_unref(network->vrf); - - HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) { - hashmap_remove(network->stacked_netdevs, netdev->ifname); - netdev_unref(netdev); - } - hashmap_free(network->stacked_netdevs); - - while ((route = network->static_routes)) - route_free(route); - - while ((address = network->static_addresses)) - address_free(address); - - while ((fdb_entry = network->static_fdb_entries)) - fdb_entry_free(fdb_entry); - - hashmap_free(network->addresses_by_section); - hashmap_free(network->routes_by_section); - hashmap_free(network->fdb_entries_by_section); - - if (network->manager) { - if (network->manager->networks) - LIST_REMOVE(networks, network->manager->networks, network); - - if (network->manager->networks_by_name) - hashmap_remove(network->manager->networks_by_name, network->name); - } - - free(network->name); - - condition_free_list(network->match_host); - condition_free_list(network->match_virt); - condition_free_list(network->match_kernel); - condition_free_list(network->match_arch); - - free(network->dhcp_server_timezone); - free(network->dhcp_server_dns); - free(network->dhcp_server_ntp); - - set_free_free(network->dnssec_negative_trust_anchors); - - free(network); -} - -int network_get_by_name(Manager *manager, const char *name, Network **ret) { - Network *network; - - assert(manager); - assert(name); - assert(ret); - - network = hashmap_get(manager->networks_by_name, name); - if (!network) - return -ENOENT; - - *ret = network; - - return 0; -} - -int network_get(Manager *manager, struct udev_device *device, - const char *ifname, const struct ether_addr *address, - Network **ret) { - Network *network; - struct udev_device *parent; - const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL; - - assert(manager); - assert(ret); - - if (device) { - path = udev_device_get_property_value(device, "ID_PATH"); - - parent = udev_device_get_parent(device); - if (parent) - parent_driver = udev_device_get_driver(parent); - - driver = udev_device_get_property_value(device, "ID_NET_DRIVER"); - - devtype = udev_device_get_devtype(device); - } - - LIST_FOREACH(networks, network, manager->networks) { - if (net_match_config(network->match_mac, network->match_path, - network->match_driver, network->match_type, - network->match_name, network->match_host, - network->match_virt, network->match_kernel, - network->match_arch, - address, path, parent_driver, driver, - devtype, ifname)) { - if (network->match_name && device) { - const char *attr; - uint8_t name_assign_type = NET_NAME_UNKNOWN; - - attr = udev_device_get_sysattr_value(device, "name_assign_type"); - if (attr) - (void) safe_atou8(attr, &name_assign_type); - - if (name_assign_type == NET_NAME_ENUM) - log_warning("%s: found matching network '%s', based on potentially unpredictable ifname", - ifname, network->filename); - else - log_debug("%s: found matching network '%s'", ifname, network->filename); - } else - log_debug("%s: found matching network '%s'", ifname, network->filename); - - *ret = network; - return 0; - } - } - - *ret = NULL; - - return -ENOENT; -} - -int network_apply(Manager *manager, Network *network, Link *link) { - int r; - - assert(manager); - assert(network); - assert(link); - - link->network = network; - - if (network->ipv4ll_route) { - Route *route; - - r = route_new_static(network, 0, &route); - if (r < 0) - return r; - - r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in); - if (r == 0) - return -EINVAL; - if (r < 0) - return -errno; - - route->family = AF_INET; - route->dst_prefixlen = 16; - route->scope = RT_SCOPE_LINK; - route->priority = IPV4LL_ROUTE_METRIC; - route->protocol = RTPROT_STATIC; - } - - if (!strv_isempty(network->dns) || - !strv_isempty(network->ntp) || - !strv_isempty(network->search_domains) || - !strv_isempty(network->route_domains)) { - manager_dirty(manager); - link_dirty(link); - } - - return 0; -} - -bool network_has_static_ipv6_addresses(Network *network) { - Address *address; - - assert(network); - - LIST_FOREACH(addresses, address, network->static_addresses) { - if (address->family == AF_INET6) - return true; - } - - return false; -} - -int config_parse_netdev(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_free_ char *kind_string = NULL; - char *p; - NetDev *netdev; - NetDevKind kind; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - kind_string = strdup(lvalue); - if (!kind_string) - return log_oom(); - - /* the keys are CamelCase versions of the kind */ - for (p = kind_string; *p; p++) - *p = tolower(*p); - - kind = netdev_kind_from_string(kind_string); - if (kind == _NETDEV_KIND_INVALID) { - 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, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - if (netdev->kind != kind) { - log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - switch (kind) { - case NETDEV_KIND_BRIDGE: - network->bridge = netdev; - - break; - case NETDEV_KIND_BOND: - network->bond = netdev; - - break; - case NETDEV_KIND_VRF: - network->vrf = netdev; - - break; - case NETDEV_KIND_VLAN: - case NETDEV_KIND_MACVLAN: - case NETDEV_KIND_MACVTAP: - case NETDEV_KIND_IPVLAN: - case NETDEV_KIND_VXLAN: - r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Can not add VLAN '%s' to network: %m", rvalue); - return 0; - } - - break; - default: - assert_not_reached("Can not parse NetDev"); - } - - netdev_ref(netdev); - - return 0; -} - -int config_parse_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) { - - const char *p; - Network *n = data; - int r; - - assert(n); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) { - n->search_domains = strv_free(n->search_domains); - n->route_domains = strv_free(n->route_domains); - return 0; - } - - p = rvalue; - for (;;) { - _cleanup_free_ char *w = NULL, *normalized = NULL; - const char *domain; - bool is_route; - - r = extract_first_word(&p, &w, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue); - break; - } - if (r == 0) - break; - - is_route = w[0] == '~'; - domain = is_route ? w + 1 : w; - - if (dns_name_is_root(domain) || streq(domain, "*")) { - /* If the root domain appears as is, or the special token "*" is found, we'll consider this as - * routing domain, unconditionally. */ - is_route = true; - domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */ - - } else { - r = dns_name_normalize(domain, &normalized); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain); - continue; - } - - domain = normalized; - - if (is_localhost(domain)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain); - continue; - } - } - - if (is_route) { - r = strv_extend(&n->route_domains, domain); - if (r < 0) - return log_oom(); - - } else { - r = strv_extend(&n->search_domains, domain); - if (r < 0) - return log_oom(); - } - } - - strv_uniq(n->route_domains); - strv_uniq(n->search_domains); - - return 0; -} - -int config_parse_tunnel(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; - NetDev *netdev; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = netdev_get(network->manager, rvalue, &netdev); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - if (netdev->kind != NETDEV_KIND_IPIP && - netdev->kind != NETDEV_KIND_SIT && - netdev->kind != NETDEV_KIND_GRE && - netdev->kind != NETDEV_KIND_GRETAP && - netdev->kind != NETDEV_KIND_IP6GRE && - netdev->kind != NETDEV_KIND_IP6GRETAP && - netdev->kind != NETDEV_KIND_VTI && - netdev->kind != NETDEV_KIND_VTI6 && - netdev->kind != NETDEV_KIND_IP6TNL - ) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "NetDev is not a tunnel, ignoring assignment: %s", rvalue); - return 0; - } - - r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Cannot add VLAN '%s' to network, ignoring: %m", rvalue); - return 0; - } - - netdev_ref(netdev); - - return 0; -} - -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) { - - AddressFamilyBoolean *link_local = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* Note that this is mostly like - * config_parse_address_family_boolean(), except that it - * applies only to IPv4 */ - - SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue)); - - return 0; -} - -int config_parse_dhcp( - const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - AddressFamilyBoolean *dhcp = data, s; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* Note that this is mostly like - * config_parse_address_family_boolean(), except that it - * understands some old names for the enum values */ - - s = address_family_boolean_from_string(rvalue); - if (s < 0) { - - /* Previously, we had a slightly different enum here, - * support its values for compatbility. */ - - if (streq(rvalue, "none")) - s = ADDRESS_FAMILY_NO; - else if (streq(rvalue, "v4")) - s = ADDRESS_FAMILY_IPV4; - else if (streq(rvalue, "v6")) - s = ADDRESS_FAMILY_IPV6; - else if (streq(rvalue, "both")) - s = ADDRESS_FAMILY_YES; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue); - return 0; - } - } - - *dhcp = s; - return 0; -} - -static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { - [DHCP_CLIENT_ID_MAC] = "mac", - [DHCP_CLIENT_ID_DUID] = "duid" -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); - -int config_parse_ipv6token( - 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) { - - union in_addr_union buffer; - struct in6_addr *token = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(token); - - r = in_addr_from_string(AF_INET6, rvalue, &buffer); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); - return 0; - } - - r = in_addr_is_null(AF_INET6, &buffer); - if (r != 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token can not be the ANY address, ignoring: %s", rvalue); - return 0; - } - - if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token can not be longer than 64 bits, ignoring: %s", rvalue); - return 0; - } - - *token = buffer.in6; - - return 0; -} - -static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { - [IPV6_PRIVACY_EXTENSIONS_NO] = "no", - [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", - [IPV6_PRIVACY_EXTENSIONS_YES] = "yes", -}; - -DEFINE_STRING_TABLE_LOOKUP(ipv6_privacy_extensions, IPv6PrivacyExtensions); - -int config_parse_ipv6_privacy_extensions( - 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) { - - IPv6PrivacyExtensions *ipv6_privacy_extensions = data; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(ipv6_privacy_extensions); - - /* 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) - *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_YES; - else if (k == 0) - *ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO; - else { - IPv6PrivacyExtensions s; - - s = ipv6_privacy_extensions_from_string(rvalue); - if (s < 0) { - - if (streq(rvalue, "kernel")) - s = _IPV6_PRIVACY_EXTENSIONS_INVALID; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); - return 0; - } - } - - *ipv6_privacy_extensions = s; - } - - return 0; -} - -int config_parse_hostname( - 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) { - - char **hostname = data, *hn = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata); - if (r < 0) - return r; - - if (!hostname_is_valid(hn, false)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue); - free(hn); - return 0; - } - - free(*hostname); - *hostname = hostname_cleanup(hn); - return 0; -} - -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) { - - char **datap = data, *tz = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata); - if (r < 0) - return r; - - if (!timezone_is_valid(tz)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue); - free(tz); - return 0; - } - - free(*datap); - *datap = tz; - - return 0; -} - -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) { - - Network *n = data; - const char *p = rvalue; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *w = NULL; - struct in_addr a, *m; - - r = extract_first_word(&p, &w, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); - return 0; - } - - if (r == 0) - return 0; - - if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); - continue; - } - - m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr)); - if (!m) - return log_oom(); - - m[n->n_dhcp_server_dns++] = a; - n->dhcp_server_dns = m; - } -} - -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) { - - Network *n = data; - const char *p = rvalue; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *w = NULL; - struct in_addr a, *m; - - r = extract_first_word(&p, &w, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); - return 0; - } - - if (r == 0) - return 0; - - if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w); - continue; - } - - m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr)); - if (!m) - return log_oom(); - - m[n->n_dhcp_server_ntp++] = a; - 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(n); - 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_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops); - if (r < 0) - return log_oom(); - - r = set_put(n->dnssec_negative_trust_anchors, w); - if (r < 0) - return log_oom(); - if (r > 0) - w = NULL; - } - - return 0; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); - -static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { - [DHCP_USE_DOMAINS_NO] = "no", - [DHCP_USE_DOMAINS_ROUTE] = "route", - [DHCP_USE_DOMAINS_YES] = "yes", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); - -DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); - -static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { - [LLDP_MODE_NO] = "no", - [LLDP_MODE_YES] = "yes", - [LLDP_MODE_ROUTERS_ONLY] = "routers-only", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h deleted file mode 100644 index 08ee939faa..0000000000 --- a/src/network/networkd-network.h +++ /dev/null @@ -1,247 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "sd-bus.h" -#include "udev.h" - -#include "condition.h" -#include "dhcp-identifier.h" -#include "hashmap.h" -#include "resolve-util.h" - -#include "networkd-address.h" -#include "networkd-brvlan.h" -#include "networkd-fdb.h" -#include "networkd-lldp-tx.h" -#include "networkd-netdev.h" -#include "networkd-route.h" -#include "networkd-util.h" - -#define DHCP_ROUTE_METRIC 1024 -#define IPV4LL_ROUTE_METRIC 2048 - -#define BRIDGE_VLAN_BITMAP_MAX 4096 -#define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32) - -typedef enum DCHPClientIdentifier { - DHCP_CLIENT_ID_MAC, - DHCP_CLIENT_ID_DUID, - _DHCP_CLIENT_ID_MAX, - _DHCP_CLIENT_ID_INVALID = -1, -} DCHPClientIdentifier; - -typedef enum IPv6PrivacyExtensions { - /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ - IPV6_PRIVACY_EXTENSIONS_NO, - IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, - IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ - _IPV6_PRIVACY_EXTENSIONS_MAX, - _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, -} IPv6PrivacyExtensions; - -typedef enum DHCPUseDomains { - DHCP_USE_DOMAINS_NO, - DHCP_USE_DOMAINS_YES, - DHCP_USE_DOMAINS_ROUTE, - _DHCP_USE_DOMAINS_MAX, - _DHCP_USE_DOMAINS_INVALID = -1, -} DHCPUseDomains; - -typedef enum LLDPMode { - LLDP_MODE_NO = 0, - LLDP_MODE_YES = 1, - LLDP_MODE_ROUTERS_ONLY = 2, - _LLDP_MODE_MAX, - _LLDP_MODE_INVALID = -1, -} LLDPMode; - -typedef struct DUID { - /* Value of Type in [DHCP] section */ - DUIDType type; - - uint8_t raw_data_len; - uint8_t raw_data[MAX_DUID_LEN]; -} DUID; - -typedef struct Manager Manager; - -struct Network { - Manager *manager; - - char *filename; - char *name; - - struct ether_addr *match_mac; - char **match_path; - char **match_driver; - char **match_type; - char **match_name; - - Condition *match_host; - Condition *match_virt; - Condition *match_kernel; - Condition *match_arch; - - char *description; - - NetDev *bridge; - NetDev *bond; - NetDev *vrf; - Hashmap *stacked_netdevs; - - /* DHCP Client Support */ - AddressFamilyBoolean dhcp; - DCHPClientIdentifier dhcp_client_identifier; - char *dhcp_vendor_class_identifier; - char *dhcp_hostname; - bool dhcp_use_dns; - bool dhcp_use_ntp; - bool dhcp_use_mtu; - bool dhcp_use_hostname; - DHCPUseDomains dhcp_use_domains; - bool dhcp_send_hostname; - bool dhcp_broadcast; - bool dhcp_critical; - bool dhcp_use_routes; - bool dhcp_use_timezone; - unsigned dhcp_route_metric; - - /* DHCP Server Support */ - bool dhcp_server; - bool dhcp_server_emit_dns; - struct in_addr *dhcp_server_dns; - unsigned n_dhcp_server_dns; - bool dhcp_server_emit_ntp; - struct in_addr *dhcp_server_ntp; - unsigned n_dhcp_server_ntp; - bool dhcp_server_emit_router; - bool dhcp_server_emit_timezone; - char *dhcp_server_timezone; - usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; - uint32_t dhcp_server_pool_offset; - uint32_t dhcp_server_pool_size; - - /* IPV4LL Support */ - AddressFamilyBoolean link_local; - bool ipv4ll_route; - - /* Bridge Support */ - bool use_bpdu; - bool hairpin; - bool fast_leave; - bool allow_port_to_be_root; - bool unicast_flood; - unsigned cost; - - uint16_t pvid; - uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN]; - uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; - - AddressFamilyBoolean ip_forward; - bool ip_masquerade; - - int ipv6_accept_ra; - int ipv6_dad_transmits; - int ipv6_hop_limit; - int proxy_arp; - - bool ipv6_accept_ra_use_dns; - DHCPUseDomains ipv6_accept_ra_use_domains; - - union in_addr_union ipv6_token; - IPv6PrivacyExtensions ipv6_privacy_extensions; - - struct ether_addr *mac; - unsigned mtu; - uint32_t iaid; - DUID duid; - - LLDPMode lldp_mode; /* LLDP reception */ - LLDPEmit lldp_emit; /* LLDP transmission */ - - LIST_HEAD(Address, static_addresses); - LIST_HEAD(Route, static_routes); - LIST_HEAD(FdbEntry, static_fdb_entries); - - unsigned n_static_addresses; - unsigned n_static_routes; - unsigned n_static_fdb_entries; - - Hashmap *addresses_by_section; - Hashmap *routes_by_section; - Hashmap *fdb_entries_by_section; - - char **search_domains, **route_domains, **dns, **ntp, **bind_carrier; - - ResolveSupport llmnr; - ResolveSupport mdns; - DnssecMode dnssec_mode; - Set *dnssec_negative_trust_anchors; - - LIST_FIELDS(Network, networks); -}; - -void network_free(Network *network); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); -#define _cleanup_network_free_ _cleanup_(network_freep) - -int network_load(Manager *manager); - -int network_get_by_name(Manager *manager, const char *name, Network **ret); -int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); -int network_apply(Manager *manager, Network *network, Link *link); - -bool network_has_static_ipv6_addresses(Network *network); - -int config_parse_netdev(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_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_tunnel(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(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_dhcp_client_identifier(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_ipv6token(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_ipv6_privacy_extensions(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_hostname(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_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); -int config_parse_dhcp_use_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_lldp_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); - -/* 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); - -const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length); - -extern const sd_bus_vtable network_vtable[]; - -int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); - -const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; -IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; - -const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; -DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; - -const char* lldp_mode_to_string(LLDPMode m) _const_; -LLDPMode lldp_mode_from_string(const char *s) _pure_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c deleted file mode 100644 index cedaf47cf8..0000000000 --- a/src/network/networkd-route.c +++ /dev/null @@ -1,901 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "alloc-util.h" -#include "conf-parser.h" -#include "in-addr-util.h" -#include "netlink-util.h" -#include "networkd-route.h" -#include "networkd.h" -#include "parse-util.h" -#include "set.h" -#include "string-util.h" -#include "util.h" - -#define ROUTES_PER_LINK_MAX 2048U -#define STATIC_ROUTES_PER_NETWORK_MAX 1024U - -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; - - assert(network); - assert(ret); - - if (section) { - route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); - if (route) { - *ret = route; - route = NULL; - - return 0; - } - } - - if (network->n_static_routes >= STATIC_ROUTES_PER_NETWORK_MAX) - return -E2BIG; - - r = route_new(&route); - if (r < 0) - return r; - - route->protocol = RTPROT_STATIC; - - if (section) { - route->section = section; - - r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); - if (r < 0) - return r; - } - - route->network = network; - LIST_PREPEND(routes, network->static_routes, route); - network->n_static_routes++; - - *ret = route; - route = NULL; - - return 0; -} - -void route_free(Route *route) { - if (!route) - return; - - if (route->network) { - LIST_REMOVE(routes, route->network->static_routes, route); - - assert(route->network->n_static_routes > 0); - route->network->n_static_routes--; - - if (route->section) - hashmap_remove(route->network->routes_by_section, 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); -} - -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, - const union in_addr_union *dst, - unsigned char dst_prefixlen, - unsigned char tos, - uint32_t priority, - unsigned char table, - Route **ret) { - - Route route, *existing; - - assert(link); - assert(dst); - - route = (Route) { - .family = family, - .dst = *dst, - .dst_prefixlen = dst_prefixlen, - .tos = tos, - .priority = priority, - .table = table, - }; - - existing = set_get(link->routes, &route); - if (existing) { - if (ret) - *ret = existing; - return 1; - } - - existing = set_get(link->routes_foreign, &route); - if (existing) { - if (ret) - *ret = existing; - return 0; - } - - return -ENOENT; -} - -static int route_add_internal( - Link *link, - Set **routes, - int family, - const 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, - const 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, - const 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, - const union in_addr_union *src, - unsigned char src_prefixlen, - const union in_addr_union *gw, - const 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; -} - -int route_remove(Route *route, Link *link, - sd_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; - int r; - - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->ifindex > 0); - assert(route->family == AF_INET || route->family == AF_INET6); - - r = sd_rtnl_message_new_route(link->manager->rtnl, &req, - RTM_DELROUTE, route->family, - route->protocol); - if (r < 0) - return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); - - 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->gw.in); - else if (route->family == AF_INET6) - 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.in); - else if (route->family == AF_INET6) - 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"); - - r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); - if (r < 0) - return log_error_errno(r, "Could not set destination prefix length: %m"); - } - - if (route->src_prefixlen) { - if (route->family == AF_INET) - 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.in6); - if (r < 0) - return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); - - r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); - if (r < 0) - return log_error_errno(r, "Could not set source prefix length: %m"); - } - - 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.in); - else if (route->family == AF_INET6) - 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"); - } - - r = sd_rtnl_message_route_set_scope(req, route->scope); - if (r < 0) - return log_error_errno(r, "Could not set scope: %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_u32(req, RTA_OIF, link->ifindex); - if (r < 0) - return log_error_errno(r, "Could not append RTA_OIF 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"); - - link_ref(link); - - return 0; -} - -static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - Link *link = userdata; - int r; - - assert(rtnl); - assert(m); - assert(link); - assert(link->ifname); - assert(link->link_messages > 0); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; - - link->link_messages--; - - r = sd_netlink_message_get_errno(m); - if (r < 0 && r != -EEXIST) - log_link_warning_errno(link, r, "could not remove route: %m"); - - if (link->link_messages == 0) - log_link_debug(link, "route removed"); - - return 1; -} - -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, route_expire_callback); - if (r < 0) - log_warning_errno(r, "Could not remove route: %m"); - else { - /* route may not be exist in kernel. If we fail still remove it */ - route->link->link_messages++; - route_free(route); - } - - return 1; -} - -int route_configure( - Route *route, - Link *link, - sd_netlink_message_handler_t callback) { - - _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); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->ifindex > 0); - assert(route->family == AF_INET || route->family == AF_INET6); - - if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 && - set_size(link->routes) >= ROUTES_PER_LINK_MAX) - return -E2BIG; - - r = sd_rtnl_message_new_route(link->manager->rtnl, &req, - RTM_NEWROUTE, route->family, - route->protocol); - if (r < 0) - return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); - - 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->gw.in); - else if (route->family == AF_INET6) - 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"); - - r = sd_rtnl_message_route_set_family(req, route->family); - if (r < 0) - return log_error_errno(r, "Could not set route family: %m"); - } - - if (route->dst_prefixlen) { - if (route->family == AF_INET) - 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.in6); - if (r < 0) - return log_error_errno(r, "Could not append RTA_DST attribute: %m"); - - r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); - if (r < 0) - return log_error_errno(r, "Could not set destination prefix length: %m"); - } - - if (route->src_prefixlen) { - if (route->family == AF_INET) - 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.in6); - if (r < 0) - return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); - - r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); - if (r < 0) - return log_error_errno(r, "Could not set source prefix length: %m"); - } - - 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.in); - else if (route->family == AF_INET6) - 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"); - } - - r = sd_rtnl_message_route_set_scope(req, route->scope); - if (r < 0) - return log_error_errno(r, "Could not set scope: %m"); - - r = sd_rtnl_message_route_set_flags(req, route->flags); - if (r < 0) - return log_error_errno(r, "Could not set flags: %m"); - - if (route->table != RT_TABLE_DEFAULT) { - - if (route->table < 256) { - r = sd_rtnl_message_route_set_table(req, route->table); - if (r < 0) - return log_error_errno(r, "Could not set route table: %m"); - } else { - - r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); - if (r < 0) - return log_error_errno(r, "Could not set route table: %m"); - - /* Table attribute to allow more than 256. */ - r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); - if (r < 0) - return log_error_errno(r, "Could not append RTA_TABLE attribute: %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"); - - 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"); - - 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; -} - -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) { - - 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); - - if (streq(section, "Network")) { - /* we are not in an Route section, so treat - * this as the special '0' section */ - section_line = 0; - } - - 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, 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, - "Preferred source is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - n->family = f; - n->prefsrc = buffer; - n = NULL; - - return 0; -} - -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) { - - Network *network = userdata; - _cleanup_route_free_ Route *n = NULL; - const char *address, *e; - union in_addr_union buffer; - unsigned char prefixlen; - 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; - - /* Destination|Source=address/prefixlen */ - - /* address */ - e = strchr(rvalue, '/'); - if (e) - address = strndupa(rvalue, e - rvalue); - else - address = rvalue; - - r = in_addr_from_string_auto(address, &f, &buffer); - if (r < 0) { - 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, 0, "Unknown address family, ignoring assignment: %s", address); - return 0; - } - - /* prefixlen */ - if (e) { - r = safe_atou8(e + 1, &prefixlen); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1); - return 0; - } - } else { - switch (f) { - case AF_INET: - prefixlen = 32; - break; - case AF_INET6: - prefixlen = 128; - break; - } - } - - n->family = f; - if (streq(lvalue, "Destination")) { - n->dst = buffer; - n->dst_prefixlen = prefixlen; - } else if (streq(lvalue, "Source")) { - n->src = buffer; - n->src_prefixlen = prefixlen; - } else - assert_not_reached(lvalue); - - n = NULL; - - return 0; -} - -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) { - Network *network = userdata; - _cleanup_route_free_ Route *n = NULL; - uint32_t k; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, section_line, &n); - if (r < 0) - return r; - - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue); - return 0; - } - - n->priority = k; - n = NULL; - - return 0; -} - -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) { - Network *network = userdata; - _cleanup_route_free_ Route *n = NULL; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, section_line, &n); - if (r < 0) - return r; - - if (streq(rvalue, "host")) - n->scope = RT_SCOPE_HOST; - else if (streq(rvalue, "link")) - n->scope = RT_SCOPE_LINK; - else if (streq(rvalue, "global")) - n->scope = RT_SCOPE_UNIVERSE; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue); - return 0; - } - - n = NULL; - - return 0; -} - -int config_parse_route_table(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_route_free_ Route *n = NULL; - Network *network = userdata; - uint32_t k; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = route_new_static(network, section_line, &n); - if (r < 0) - return r; - - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); - return 0; - } - - n->table = k; - - n = NULL; - - return 0; -} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h deleted file mode 100644 index d4e4dbac0b..0000000000 --- a/src/network/networkd-route.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -typedef struct Route Route; - -#include "networkd-network.h" - -struct Route { - Network *network; - unsigned section; - - Link *link; - - int family; - unsigned char dst_prefixlen; - unsigned char src_prefixlen; - unsigned char scope; - unsigned char protocol; /* RTPROT_* */ - unsigned char tos; - uint32_t priority; /* note that ip(8) calls this 'metric' */ - uint32_t table; - unsigned char pref; - unsigned flags; - - 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(Route **ret); -void route_free(Route *route); -int route_configure(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, const 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, const 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, const 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, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); - -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); -int config_parse_route_table(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 deleted file mode 100644 index 555a7c68a1..0000000000 --- a/src/network/networkd-util.c +++ /dev/null @@ -1,101 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#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 || - b == ADDRESS_FAMILY_NO) - return yes_no(b == ADDRESS_FAMILY_YES); - - if (b == ADDRESS_FAMILY_IPV4) - return "ipv4"; - if (b == ADDRESS_FAMILY_IPV6) - return "ipv6"; - - return NULL; -} - -AddressFamilyBoolean address_family_boolean_from_string(const char *s) { - int r; - - /* Make this a true superset of a boolean */ - - r = parse_boolean(s); - if (r > 0) - return ADDRESS_FAMILY_YES; - if (r == 0) - return ADDRESS_FAMILY_NO; - - if (streq(s, "ipv4")) - return ADDRESS_FAMILY_IPV4; - if (streq(s, "ipv6")) - return ADDRESS_FAMILY_IPV6; - - return _ADDRESS_FAMILY_BOOLEAN_INVALID; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option"); - -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) { - - AddressFamilyBoolean *fwd = data, s; - - assert(filename); - assert(lvalue); - 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_NO; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue); - return 0; - } - } - - *fwd = s; - - return 0; -} diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h deleted file mode 100644 index d5c385bea4..0000000000 --- a/src/network/networkd-util.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "macro.h" - -typedef enum AddressFamilyBoolean { - /* This is a bitmask, though it usually doesn't feel that way! */ - ADDRESS_FAMILY_NO = 0, - ADDRESS_FAMILY_IPV4 = 1, - ADDRESS_FAMILY_IPV6 = 2, - ADDRESS_FAMILY_YES = 3, - _ADDRESS_FAMILY_BOOLEAN_MAX, - _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, -} AddressFamilyBoolean; - -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 *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 deleted file mode 100644 index 5727422e3d..0000000000 --- a/src/network/networkd-wait-online-link.c +++ /dev/null @@ -1,131 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - 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 . -***/ - -#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; - int r; - - assert(m); - assert(ifindex > 0); - - r = hashmap_ensure_allocated(&m->links, NULL); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&m->links_by_name, &string_hash_ops); - if (r < 0) - return r; - - l = new0(Link, 1); - if (!l) - return -ENOMEM; - - l->manager = m; - - l->ifname = strdup(ifname); - if (!l->ifname) - return -ENOMEM; - - r = hashmap_put(m->links_by_name, l->ifname, l); - if (r < 0) - return r; - - l->ifindex = ifindex; - - r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); - if (r < 0) - return r; - - if (ret) - *ret = l; - l = NULL; - - return 0; -} - -Link *link_free(Link *l) { - - if (!l) - return NULL; - - if (l->manager) { - hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - hashmap_remove(l->manager->links_by_name, l->ifname); - } - - free(l->ifname); - free(l); - return NULL; - } - -int link_update_rtnl(Link *l, sd_netlink_message *m) { - const char *ifname; - int r; - - assert(l); - assert(l->manager); - assert(m); - - r = sd_rtnl_message_link_get_flags(m, &l->flags); - if (r < 0) - return r; - - r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname); - if (r < 0) - return r; - - if (!streq(l->ifname, ifname)) { - char *new_ifname; - - new_ifname = strdup(ifname); - if (!new_ifname) - return -ENOMEM; - - hashmap_remove(l->manager->links_by_name, l->ifname); - free(l->ifname); - l->ifname = new_ifname; - - r = hashmap_put(l->manager->links_by_name, l->ifname, l); - if (r < 0) - return r; - } - - return 0; -} - -int link_update_monitor(Link *l) { - assert(l); - - l->operational_state = mfree(l->operational_state); - - sd_network_link_get_operational_state(l->ifindex, &l->operational_state); - - l->state = mfree(l->state); - - sd_network_link_get_setup_state(l->ifindex, &l->state); - - return 0; -} diff --git a/src/network/networkd-wait-online-link.h b/src/network/networkd-wait-online-link.h deleted file mode 100644 index dc35085c55..0000000000 --- a/src/network/networkd-wait-online-link.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - 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 . -***/ - -typedef struct Link Link; - -#include "networkd-wait-online.h" - -struct Link { - Manager *manager; - - int ifindex; - char *ifname; - unsigned flags; - - char *operational_state; - char *state; -}; - -int link_new(Manager *m, Link **ret, int ifindex, const char *ifname); -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); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c deleted file mode 100644 index 725b3310dd..0000000000 --- a/src/network/networkd-wait-online-manager.c +++ /dev/null @@ -1,329 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "netlink-util.h" -#include "network-internal.h" -#include "networkd-wait-online-link.h" -#include "networkd-wait-online.h" -#include "time-util.h" -#include "util.h" - -bool manager_ignore_link(Manager *m, Link *link) { - assert(m); - assert(link); - - /* always ignore the loopback interface */ - if (link->flags & IFF_LOOPBACK) - return true; - - /* if interfaces are given on the command line, ignore all others */ - if (m->interfaces && !strv_contains(m->interfaces, link->ifname)) - return true; - - /* ignore interfaces we explicitly are asked to ignore */ - return strv_fnmatch(m->ignore, link->ifname, 0); -} - -bool manager_all_configured(Manager *m) { - Iterator i; - Link *l; - char **ifname; - bool one_ready = false; - - /* wait for all the links given on the command line to appear */ - STRV_FOREACH(ifname, m->interfaces) { - l = hashmap_get(m->links_by_name, *ifname); - if (!l) { - log_debug("still waiting for %s", *ifname); - return false; - } - } - - /* wait for all links networkd manages to be in admin state 'configured' - and at least one link to gain a carrier */ - HASHMAP_FOREACH(l, m->links, i) { - if (manager_ignore_link(m, l)) { - log_info("ignoring: %s", l->ifname); - continue; - } - - if (!l->state) { - log_debug("link %s has not yet been processed by udev", - l->ifname); - return false; - } - - if (STR_IN_SET(l->state, "configuring", "pending")) { - log_debug("link %s is being processed by networkd", - l->ifname); - return false; - } - - if (l->operational_state && - STR_IN_SET(l->operational_state, "degraded", "routable")) - /* we wait for at least one link to be ready, - regardless of who manages it */ - one_ready = true; - } - - return one_ready; -} - -static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { - Manager *m = userdata; - uint16_t type; - Link *l; - const char *ifname; - int ifindex, r; - - assert(rtnl); - assert(m); - assert(mm); - - r = sd_netlink_message_get_type(mm, &type); - if (r < 0) - goto fail; - - r = sd_rtnl_message_link_get_ifindex(mm, &ifindex); - if (r < 0) - goto fail; - - r = sd_netlink_message_read_string(mm, IFLA_IFNAME, &ifname); - if (r < 0) - goto fail; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - - switch (type) { - - case RTM_NEWLINK: - if (!l) { - log_debug("Found link %i", ifindex); - - r = link_new(m, &l, ifindex, ifname); - if (r < 0) - goto fail; - - r = link_update_monitor(l); - if (r < 0) - goto fail; - } - - r = link_update_rtnl(l, mm); - if (r < 0) - goto fail; - - break; - - case RTM_DELLINK: - if (l) { - log_debug("Removing link %i", l->ifindex); - link_free(l); - } - - break; - } - - return 0; - -fail: - log_warning_errno(r, "Failed to process RTNL link message: %m"); - return 0; -} - -static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { - Manager *m = userdata; - int r; - - r = manager_process_link(rtnl, mm, m); - if (r < 0) - return r; - - if (manager_all_configured(m)) - sd_event_exit(m->event, 0); - - return 1; -} - -static int manager_rtnl_listen(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *i; - int r; - - assert(m); - - /* First, subscribe to interfaces coming and going */ - r = sd_netlink_open(&m->rtnl); - if (r < 0) - return r; - - r = sd_netlink_attach_event(m->rtnl, m->event, 0); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, on_rtnl_event, m); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, on_rtnl_event, m); - if (r < 0) - return r; - - /* Then, enumerate all links */ - r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (i = reply; i; i = sd_netlink_message_next(i)) { - r = manager_process_link(m->rtnl, i, m); - if (r < 0) - return r; - } - - return r; -} - -static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - Iterator i; - Link *l; - int r; - - assert(m); - - sd_network_monitor_flush(m->network_monitor); - - HASHMAP_FOREACH(l, m->links, i) { - r = link_update_monitor(l); - if (r < 0) - log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); - } - - if (manager_all_configured(m)) - sd_event_exit(m->event, 0); - - return 0; -} - -static int manager_network_monitor_listen(Manager *m) { - int r, fd, events; - - assert(m); - - r = sd_network_monitor_new(&m->network_monitor, NULL); - if (r < 0) - return r; - - fd = sd_network_monitor_get_fd(m->network_monitor); - if (fd < 0) - return fd; - - events = sd_network_monitor_get_events(m->network_monitor); - if (events < 0) - return events; - - r = sd_event_add_io(m->event, &m->network_monitor_event_source, - fd, events, &on_network_event, m); - if (r < 0) - return r; - - return 0; -} - -int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout) { - _cleanup_(manager_freep) Manager *m = NULL; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - m->interfaces = interfaces; - m->ignore = ignore; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); - sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); - - if (timeout > 0) { - usec_t usec; - - usec = now(clock_boottime_or_monotonic()) + timeout; - - r = sd_event_add_time(m->event, NULL, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); - if (r < 0) - return r; - } - - sd_event_set_watchdog(m->event, true); - - r = manager_network_monitor_listen(m); - if (r < 0) - return r; - - r = manager_rtnl_listen(m); - if (r < 0) - return r; - - *ret = m; - m = NULL; - - return 0; -} - -void manager_free(Manager *m) { - Link *l; - - if (!m) - return; - - while ((l = hashmap_first(m->links))) - link_free(l); - hashmap_free(m->links); - hashmap_free(m->links_by_name); - - sd_event_source_unref(m->network_monitor_event_source); - sd_network_monitor_unref(m->network_monitor); - - sd_event_source_unref(m->rtnl_event_source); - sd_netlink_unref(m->rtnl); - - sd_event_unref(m->event); - free(m); - - return; -} diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c deleted file mode 100644 index 3220c4b7ef..0000000000 --- a/src/network/networkd-wait-online.c +++ /dev/null @@ -1,166 +0,0 @@ - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "sd-daemon.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; -static char **arg_interfaces = NULL; -static char **arg_ignore = NULL; - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Block until network is configured.\n\n" - " -h --help Show this help\n" - " --version Print version string\n" - " -q --quiet Do not show status information\n" - " -i --interface=INTERFACE Block until at least these interfaces have appeared\n" - " --ignore=INTERFACE Don't take these interfaces into account\n" - " --timeout=SECS Maximum time to wait for network connectivity\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_IGNORE, - ARG_TIMEOUT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "quiet", no_argument, NULL, 'q' }, - { "interface", required_argument, NULL, 'i' }, - { "ignore", required_argument, NULL, ARG_IGNORE }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+hi:q", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case 'q': - arg_quiet = true; - break; - - case ARG_VERSION: - return version(); - - case 'i': - if (strv_extend(&arg_interfaces, optarg) < 0) - return log_oom(); - - break; - - case ARG_IGNORE: - if (strv_extend(&arg_ignore, optarg) < 0) - return log_oom(); - - break; - - case ARG_TIMEOUT: - r = parse_sec(optarg, &arg_timeout); - if (r < 0) - return r; - - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - _cleanup_(manager_freep) Manager *m = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_argv(argc, argv); - if (r <= 0) - return r; - - if (arg_quiet) - log_set_max_level(LOG_WARNING); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - - r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto finish; - } - - if (manager_all_configured(m)) { - r = 0; - goto finish; - } - - sd_notify(false, - "READY=1\n" - "STATUS=Waiting for network connections..."); - - r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto finish; - } - -finish: - strv_free(arg_interfaces); - strv_free(arg_ignore); - - if (r >= 0) { - sd_notify(false, "STATUS=All interfaces configured..."); - - return EXIT_SUCCESS; - } else { - sd_notify(false, "STATUS=Failed waiting for network connectivity..."); - - return EXIT_FAILURE; - } -} diff --git a/src/network/networkd-wait-online.h b/src/network/networkd-wait-online.h deleted file mode 100644 index f91995c306..0000000000 --- a/src/network/networkd-wait-online.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-event.h" -#include "sd-netlink.h" -#include "sd-network.h" - -#include "hashmap.h" - -typedef struct Manager Manager; - -#include "networkd-wait-online-link.h" - -struct Manager { - Hashmap *links; - Hashmap *links_by_name; - - char **interfaces; - char **ignore; - - sd_netlink *rtnl; - sd_event_source *rtnl_event_source; - - sd_network_monitor *network_monitor; - sd_event_source *network_monitor_event_source; - - sd_event *event; -}; - -void manager_free(Manager *m); -int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); - -bool manager_all_configured(Manager *m); -bool manager_ignore_link(Manager *m, Link *link); diff --git a/src/network/networkd.c b/src/network/networkd.c deleted file mode 100644 index c8f81a2ca6..0000000000 --- a/src/network/networkd.c +++ /dev/null @@ -1,139 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "sd-daemon.h" - -#include "capability-util.h" -#include "networkd.h" -#include "networkd-conf.h" -#include "signal-util.h" -#include "user-util.h" - -int main(int argc, char *argv[]) { - _cleanup_manager_free_ Manager *m = NULL; - const char *user = "systemd-network"; - uid_t uid; - gid_t gid; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto out; - } - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto out; - } - - /* Always create the directories people can create inotify - * watches in. */ - r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory: %m"); - - r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'links': %m"); - - r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'leases': %m"); - - r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'lldp': %m"); - - r = drop_privileges(uid, gid, - (1ULL << CAP_NET_ADMIN) | - (1ULL << CAP_NET_BIND_SERVICE) | - (1ULL << CAP_NET_BROADCAST) | - (1ULL << CAP_NET_RAW)); - if (r < 0) - goto out; - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto out; - } - - r = manager_connect_bus(m); - if (r < 0) { - log_error_errno(r, "Could not connect to bus: %m"); - goto out; - } - - r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse configuration file: %m"); - - r = manager_load_config(m); - if (r < 0) { - log_error_errno(r, "Could not load configuration files: %m"); - goto out; - } - - r = manager_rtnl_enumerate_links(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate links: %m"); - goto out; - } - - r = manager_rtnl_enumerate_addresses(m); - if (r < 0) { - log_error_errno(r, "Could not enumerate addresses: %m"); - 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, - "READY=1\n" - "STATUS=Processing requests..."); - - r = manager_run(m); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto out; - } - -out: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/network/networkd.h b/src/network/networkd.h deleted file mode 100644 index c4bd712147..0000000000 --- a/src/network/networkd.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include - -#include "sd-bus.h" -#include "sd-event.h" -#include "sd-netlink.h" -#include "udev.h" - -#include "dhcp-identifier.h" -#include "hashmap.h" -#include "list.h" - -#include "networkd-address-pool.h" -#include "networkd-link.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-dummy.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-vrf.h" -#include "networkd-netdev-vxlan.h" -#include "networkd-network.h" -#include "networkd-util.h" - -extern const char* const network_dirs[]; - -struct Manager { - sd_netlink *rtnl; - sd_event *event; - sd_event_source *bus_retry_event_source; - sd_bus *bus; - sd_bus_slot *prepare_for_sleep_slot; - struct udev *udev; - struct udev_monitor *udev_monitor; - sd_event_source *udev_event_source; - - bool enumerating:1; - bool dirty:1; - - Set *dirty_links; - - char *state_file; - LinkOperationalState operational_state; - - Hashmap *links; - Hashmap *netdevs; - Hashmap *networks_by_name; - LIST_HEAD(Network, networks); - LIST_HEAD(AddressPool, address_pools); - - usec_t network_dirs_ts_usec; - - DUID duid; -}; - -static inline const DUID* link_duid(const Link *link) { - if (link->network->duid.type != _DUID_TYPE_INVALID) - return &link->network->duid; - else - return &link->manager->duid; -} - -extern const sd_bus_vtable manager_vtable[]; - -int manager_new(Manager **ret); -void manager_free(Manager *m); - -int manager_connect_bus(Manager *m); -int manager_run(Manager *m); - -int manager_load_config(Manager *m); -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_; -void manager_dirty(Manager *m); - -int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); - -Link* manager_find_uplink(Manager *m, Link *exclude); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); -#define _cleanup_manager_free_ _cleanup_(manager_freep) diff --git a/src/network/org.freedesktop.network1.conf b/src/network/org.freedesktop.network1.conf deleted file mode 100644 index 52dad33668..0000000000 --- a/src/network/org.freedesktop.network1.conf +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/network/org.freedesktop.network1.service b/src/network/org.freedesktop.network1.service deleted file mode 100644 index bea885fe53..0000000000 --- a/src/network/org.freedesktop.network1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.network1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.network1.service diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c deleted file mode 100644 index adbe09a5e1..0000000000 --- a/src/network/test-network-tables.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "dhcp6-internal.h" -#include "dhcp6-protocol.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) { - test_table(bond_mode, NETDEV_BOND_MODE); - /* test_table(link_state, LINK_STATE); — not a reversible mapping */ - test_table(link_operstate, LINK_OPERSTATE); - test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); - test_table(netdev_kind, NETDEV_KIND); - test_table(dhcp6_message_status, DHCP6_STATUS); - test_table(duplex, DUP); - test_table(wol, WOL); - test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA); - - test_table_sparse(macvlan_mode, NETDEV_MACVLAN_MODE); - test_table_sparse(ipvlan_mode, NETDEV_IPVLAN_MODE); - test_table_sparse(dhcp6_message_type, DHCP6_MESSAGE); - - return EXIT_SUCCESS; -} diff --git a/src/network/test-network.c b/src/network/test-network.c deleted file mode 100644 index 855646173f..0000000000 --- a/src/network/test-network.c +++ /dev/null @@ -1,216 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#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; - _cleanup_free_ struct in6_addr *addresses6 = NULL; - struct in_addr a, b, c; - struct in6_addr d, e, f; - int size; - const char *addresses_string = "192.168.0.1 0:0:0:0:0:FFFF:204.152.189.116 192.168.0.2 ::1 192.168.0.3 1:0:0:0:0:0:0:8"; - - assert_se(inet_pton(AF_INET, "0:0:0:0:0:FFFF:204.152.189.116", &a) == 0); - assert_se(inet_pton(AF_INET6, "192.168.0.1", &d) == 0); - - assert_se(inet_pton(AF_INET, "192.168.0.1", &a) == 1); - assert_se(inet_pton(AF_INET, "192.168.0.2", &b) == 1); - assert_se(inet_pton(AF_INET, "192.168.0.3", &c) == 1); - assert_se(inet_pton(AF_INET6, "0:0:0:0:0:FFFF:204.152.189.116", &d) == 1); - assert_se(inet_pton(AF_INET6, "::1", &e) == 1); - assert_se(inet_pton(AF_INET6, "1:0:0:0:0:0:0:8", &f) == 1); - - assert_se((size = deserialize_in_addrs(&addresses, addresses_string)) >= 0); - assert_se(size == 3); - assert_se(!memcmp(&a, &addresses[0], sizeof(struct in_addr))); - assert_se(!memcmp(&b, &addresses[1], sizeof(struct in_addr))); - assert_se(!memcmp(&c, &addresses[2], sizeof(struct in_addr))); - - assert_se((size = deserialize_in6_addrs(&addresses6, addresses_string)) >= 0); - assert_se(size == 3); - assert_se(!memcmp(&d, &addresses6[0], sizeof(struct in6_addr))); - assert_se(!memcmp(&e, &addresses6[1], sizeof(struct in6_addr))); - assert_se(!memcmp(&f, &addresses6[2], sizeof(struct in6_addr))); -} - -static void test_deserialize_dhcp_routes(void) { - size_t size, allocated; - - { - _cleanup_free_ struct sd_dhcp_route *routes = NULL; - assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, "") >= 0); - assert_se(size == 0); - } - - { - /* no errors */ - _cleanup_free_ struct sd_dhcp_route *routes = NULL; - const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0/24,10.1.2.1 0.0.0.0/0,10.0.1.1"; - - assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); - - assert_se(size == 3); - assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0")); - assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1")); - assert_se(routes[0].dst_prefixlen == 16); - - assert_se(routes[1].dst_addr.s_addr == inet_addr("10.1.2.0")); - assert_se(routes[1].gw_addr.s_addr == inet_addr("10.1.2.1")); - assert_se(routes[1].dst_prefixlen == 24); - - assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0")); - assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1")); - assert_se(routes[2].dst_prefixlen == 0); - } - - { - /* error in second word */ - _cleanup_free_ struct sd_dhcp_route *routes = NULL; - const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.1"; - - assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); - - assert_se(size == 2); - assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0")); - assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1")); - assert_se(routes[0].dst_prefixlen == 16); - - assert_se(routes[1].dst_addr.s_addr == inet_addr("0.0.0.0")); - assert_se(routes[1].gw_addr.s_addr == inet_addr("10.0.1.1")); - assert_se(routes[1].dst_prefixlen == 0); - } - - { - /* error in every word */ - _cleanup_free_ struct sd_dhcp_route *routes = NULL; - const char *routes_string = "192.168.0.0/55,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.X"; - - assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0); - assert_se(size == 0); - } -} - -static int test_load_config(Manager *manager) { - int r; -/* TODO: should_reload, is false if the config dirs do not exist, so - * so we can't do this test here, move it to a test for paths_check_timestamps - * directly - * - * assert_se(network_should_reload(manager) == true); -*/ - - r = manager_load_config(manager); - if (r == -EPERM) - return r; - assert_se(r >= 0); - - assert_se(manager_should_reload(manager) == false); - - return 0; -} - -static void test_network_get(Manager *manager, struct udev_device *loopback) { - Network *network; - const struct ether_addr mac = {}; - - /* let's assume that the test machine does not have a .network file - that applies to the loopback device... */ - assert_se(network_get(manager, loopback, "lo", &mac, &network) == -ENOENT); - assert_se(!network); -} - -static void test_address_equality(void) { - _cleanup_address_free_ Address *a1 = NULL, *a2 = NULL; - - assert_se(address_new(&a1) >= 0); - assert_se(address_new(&a2) >= 0); - - assert_se(address_equal(NULL, NULL)); - assert_se(!address_equal(a1, NULL)); - assert_se(!address_equal(NULL, a2)); - assert_se(address_equal(a1, a2)); - - a1->family = AF_INET; - assert_se(!address_equal(a1, a2)); - - a2->family = AF_INET; - 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(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)); - - a1->family = AF_INET6; - assert_se(!address_equal(a1, a2)); - - a2->family = AF_INET6; - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr.in6)); - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr.in6)); - assert_se(address_equal(a1, a2)); - - a2->prefixlen = 8; - assert_se(address_equal(a1, a2)); - - assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr.in6)); - assert_se(!address_equal(a1, a2)); -} - -int main(void) { - _cleanup_manager_free_ Manager *manager = NULL; - struct udev *udev; - struct udev_device *loopback; - int r; - - test_deserialize_in_addr(); - test_deserialize_dhcp_routes(); - test_address_equality(); - - assert_se(manager_new(&manager) >= 0); - - r = test_load_config(manager); - if (r == -EPERM) - return EXIT_TEST_SKIP; - - udev = udev_new(); - assert_se(udev); - - loopback = udev_device_new_from_syspath(udev, "/sys/class/net/lo"); - assert_se(loopback); - assert_se(udev_device_get_ifindex(loopback) == 1); - - test_network_get(manager, loopback); - - assert_se(manager_rtnl_enumerate_links(manager) >= 0); - - udev_device_unref(loopback); - udev_unref(udev); -} diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c deleted file mode 100644 index 0e1a18457d..0000000000 --- a/src/network/test-networkd-conf.c +++ /dev/null @@ -1,142 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2016 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "hexdecoct.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "ether-addr-util.h" - -#include "networkd-conf.h" -#include "networkd-network.h" -#include "network-internal.h" - -static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) { - DUIDType actual = 0; - int r; - - r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); - log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual); - assert_se(r == ret); - assert_se(expected == actual); -} - -static void test_config_parse_duid_type(void) { - test_config_parse_duid_type_one("", 0, 0); - test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT); - test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN); - test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL); - test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID); - test_config_parse_duid_type_one("foo", 0, 0); -} - -static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { - DUID actual = {}; - int r; - _cleanup_free_ char *d = NULL; - - r = config_parse_duid_rawdata("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); - d = hexmem(actual.raw_data, actual.raw_data_len); - log_info_errno(r, "\"%s\" → \"%s\" (%m)", - rvalue, strnull(d)); - assert_se(r == ret); - if (expected) { - assert_se(actual.raw_data_len == expected->raw_data_len); - assert_se(memcmp(actual.raw_data, expected->raw_data, expected->raw_data_len) == 0); - } -} - -static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const struct ether_addr* expected) { - struct ether_addr *actual = NULL; - int r; - - r = config_parse_hwaddr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); - assert_se(ret == r); - if (expected) { - assert_se(actual); - assert(ether_addr_equal(expected, actual)); - } else { - assert_se(actual == NULL); - } - free(actual); -} - -#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80" - -#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80} - -static void test_config_parse_duid_rawdata(void) { - test_config_parse_duid_rawdata_one("", 0, &(DUID){}); - test_config_parse_duid_rawdata_one("00:11:22:33:44:55:66:77", 0, - &(DUID){0, 8, {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}}); - test_config_parse_duid_rawdata_one("00:11:22:", 0, - &(DUID){0, 3, {0x00,0x11,0x22}}); - test_config_parse_duid_rawdata_one("000:11:22", 0, &(DUID){}); /* error, output is all zeros */ - test_config_parse_duid_rawdata_one("00:111:22", 0, &(DUID){}); - test_config_parse_duid_rawdata_one("0:1:2:3:4:5:6:7", 0, - &(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}}); - test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}}); /* FIXME: should this be an error? */ - test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){}); - test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){}); - test_config_parse_duid_rawdata_one(BYTES_0_128 + 2, 0, &(DUID){0, 128, BYTES_1_128}); -} - -static void test_config_parse_hwaddr(void) { - const struct ether_addr t[] = { - { .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } }, - { .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } }, - }; - test_config_parse_hwaddr_one("", 0, NULL); - test_config_parse_hwaddr_one("no:ta:ma:ca:dd:re", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:fx", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]); - test_config_parse_hwaddr_one(" aa:bb:cc:dd:ee:ff", 0, &t[0]); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\n", 0, &t[0]); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc: dd:ee:ff", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc:d d:ee:ff", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee", 0, NULL); - test_config_parse_hwaddr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL); - test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL); - test_config_parse_hwaddr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]); - test_config_parse_hwaddr_one("01:23:45:67:89:aB", 0, &t[1]); - test_config_parse_hwaddr_one("1:23:45:67:89:aB", 0, &t[1]); - test_config_parse_hwaddr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]); - test_config_parse_hwaddr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]); - test_config_parse_hwaddr_one("01-23-45-67-89-ab", 0, &t[1]); - test_config_parse_hwaddr_one("aabb.ccdd.eeff", 0, &t[0]); - test_config_parse_hwaddr_one("0123.4567.89ab", 0, &t[1]); - test_config_parse_hwaddr_one("123.4567.89ab.", 0, NULL); - test_config_parse_hwaddr_one("aabbcc.ddeeff", 0, NULL); - test_config_parse_hwaddr_one("aabbccddeeff", 0, NULL); - test_config_parse_hwaddr_one("aabbccddee:ff", 0, NULL); - test_config_parse_hwaddr_one("012345.6789ab", 0, NULL); - test_config_parse_hwaddr_one("123.4567.89ab", 0, &t[1]); -} - -int main(int argc, char **argv) { - log_parse_environment(); - log_open(); - - test_config_parse_duid_type(); - test_config_parse_duid_rawdata(); - test_config_parse_hwaddr(); - - return 0; -} diff --git a/src/notify/Makefile b/src/notify/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/notify/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/notify/notify.c b/src/notify/notify.c deleted file mode 100644 index 49f97c61d9..0000000000 --- a/src/notify/notify.c +++ /dev/null @@ -1,203 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-daemon.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" - -static bool arg_ready = false; -static pid_t arg_pid = 0; -static const char *arg_status = NULL; -static bool arg_booted = false; - -static void help(void) { - printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n" - "Notify the init system about service status updates.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --ready Inform the init system about service start-up completion\n" - " --pid[=PID] Set main pid of daemon\n" - " --status=TEXT Set status text\n" - " --booted Check if the system was booted up with systemd\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_READY = 0x100, - ARG_VERSION, - ARG_PID, - ARG_STATUS, - ARG_BOOTED, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "ready", no_argument, NULL, ARG_READY }, - { "pid", optional_argument, NULL, ARG_PID }, - { "status", required_argument, NULL, ARG_STATUS }, - { "booted", no_argument, NULL, ARG_BOOTED }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_READY: - arg_ready = true; - break; - - case ARG_PID: - - if (optarg) { - if (parse_pid(optarg, &arg_pid) < 0) { - log_error("Failed to parse PID %s.", optarg); - return -EINVAL; - } - } else - arg_pid = getppid(); - - break; - - case ARG_STATUS: - arg_status = optarg; - break; - - case ARG_BOOTED: - arg_booted = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - } - - if (optind >= argc && - !arg_ready && - !arg_status && - !arg_pid && - !arg_booted) { - help(); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char* argv[]) { - _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL; - _cleanup_strv_free_ char **final_env = NULL; - char* our_env[4]; - unsigned i = 0; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_booted) - return sd_booted() <= 0; - - if (arg_ready) - our_env[i++] = (char*) "READY=1"; - - if (arg_status) { - status = strappend("STATUS=", arg_status); - if (!status) { - r = log_oom(); - goto finish; - } - - our_env[i++] = status; - } - - if (arg_pid > 0) { - if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0) { - r = log_oom(); - goto finish; - } - - our_env[i++] = cpid; - } - - our_env[i++] = NULL; - - final_env = strv_env_merge(2, our_env, argv + optind); - if (!final_env) { - r = log_oom(); - goto finish; - } - - if (strv_length(final_env) <= 0) { - r = 0; - goto finish; - } - - n = strv_join(final_env, "\n"); - if (!n) { - r = log_oom(); - goto finish; - } - - r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n); - if (r < 0) { - log_error_errno(r, "Failed to notify init system: %m"); - goto finish; - } else if (r == 0) { - log_error("No status data could be sent: $NOTIFY_SOCKET was not set"); - r = -EOPNOTSUPP; - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/nspawn/.gitignore b/src/nspawn/.gitignore deleted file mode 100644 index 85c81fff24..0000000000 --- a/src/nspawn/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/nspawn-gperf.c diff --git a/src/nspawn/Makefile b/src/nspawn/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/nspawn/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c deleted file mode 100644 index b1580236c9..0000000000 --- a/src/nspawn/nspawn-cgroup.c +++ /dev/null @@ -1,162 +0,0 @@ -/*** - 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 . -***/ - -#include - -#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; - _cleanup_close_ int fd = -1; - const char *fn; - int r; - - r = cg_pid_get_path(NULL, pid, &path); - if (r < 0) - return log_error_errno(r, "Failed to get container cgroup path: %m"); - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); - if (r < 0) - return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); - - fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", fs); - - FOREACH_STRING(fn, - ".", - "tasks", - "notify_on_release", - "cgroup.procs", - "cgroup.events", - "cgroup.clone_children", - "cgroup.controllers", - "cgroup.subtree_control") - if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0) - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to chown() cgroup file %s, ignoring: %m", fn); - - return 0; -} - -int sync_cgroup(pid_t pid, bool unified_requested) { - _cleanup_free_ char *cgroup = NULL; - char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1]; - bool undo_mount = false; - const char *fn; - int unified, r; - - unified = cg_unified(); - if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); - - if ((unified > 0) == unified_requested) - return 0; - - /* When the host uses the legacy cgroup setup, but the - * container shall use the unified hierarchy, let's make sure - * we copy the path from the name=systemd hierarchy into the - * unified hierarchy. Similar for the reverse situation. */ - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); - if (r < 0) - return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid); - - /* In order to access the unified hierarchy we need to mount it */ - if (!mkdtemp(tree)) - return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m"); - - if (unified) - r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); - else - r = mount("cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); - if (r < 0) { - r = log_error_errno(errno, "Failed to mount unified hierarchy: %m"); - goto finish; - } - - undo_mount = true; - - fn = strjoina(tree, cgroup, "/cgroup.procs"); - (void) mkdir_parents(fn, 0755); - - sprintf(pid_string, PID_FMT, pid); - r = write_string_file(fn, pid_string, 0); - if (r < 0) - log_error_errno(r, "Failed to move process: %m"); - -finish: - if (undo_mount) - (void) umount(tree); - - (void) rmdir(tree); - return r; -} - -int create_subcgroup(pid_t pid, bool unified_requested) { - _cleanup_free_ char *cgroup = NULL; - const char *child; - int unified, r; - CGroupMask supported; - - /* In the unified hierarchy inner nodes may only contain - * subgroups, but not processes. Hence, if we running in the - * unified hierarchy and the container does the same, and we - * did not create a scope unit for the container move us and - * the container into two separate subcgroups. */ - - if (!unified_requested) - return 0; - - unified = cg_unified(); - if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); - if (unified == 0) - return 0; - - r = cg_mask_supported(&supported); - if (r < 0) - return log_error_errno(r, "Failed to determine supported controllers: %m"); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); - if (r < 0) - return log_error_errno(r, "Failed to get our control group: %m"); - - child = strjoina(cgroup, "/payload"); - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, pid); - if (r < 0) - return log_error_errno(r, "Failed to create %s subcgroup: %m", child); - - child = strjoina(cgroup, "/supervisor"); - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, 0); - if (r < 0) - return log_error_errno(r, "Failed to create %s subcgroup: %m", child); - - /* Try to enable as many controllers as possible for the new payload. */ - (void) cg_enable_everywhere(supported, supported, cgroup); - return 0; -} diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h deleted file mode 100644 index 1ff35a299a..0000000000 --- a/src/nspawn/nspawn-cgroup.h +++ /dev/null @@ -1,27 +0,0 @@ -#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 . -***/ - -#include -#include - -int chown_cgroup(pid_t pid, uid_t uid_shift); -int sync_cgroup(pid_t pid, bool unified_requested); -int create_subcgroup(pid_t pid, bool unified_requested); diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c deleted file mode 100644 index 86124b8779..0000000000 --- a/src/nspawn/nspawn-expose-ports.c +++ /dev/null @@ -1,245 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-netlink.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) { - - const char *split, *e; - uint16_t container_port, host_port; - int protocol; - ExposePort *p; - int r; - - assert(l); - assert(s); - - if ((e = startswith(s, "tcp:"))) - protocol = IPPROTO_TCP; - else if ((e = startswith(s, "udp:"))) - protocol = IPPROTO_UDP; - else { - e = s; - protocol = IPPROTO_TCP; - } - - split = strchr(e, ':'); - if (split) { - char v[split - e + 1]; - - memcpy(v, e, split - e); - v[split - e] = 0; - - r = safe_atou16(v, &host_port); - if (r < 0 || host_port <= 0) - return -EINVAL; - - r = safe_atou16(split + 1, &container_port); - } else { - r = safe_atou16(e, &container_port); - host_port = container_port; - } - - if (r < 0 || container_port <= 0) - return -EINVAL; - - LIST_FOREACH(ports, p, *l) - if (p->protocol == protocol && p->host_port == host_port) - return -EEXIST; - - p = new(ExposePort, 1); - if (!p) - return -ENOMEM; - - p->protocol = protocol; - p->host_port = host_port; - p->container_port = container_port; - - LIST_PREPEND(ports, *l, p); - - return 0; -} - -void expose_port_free_all(ExposePort *p) { - - while (p) { - ExposePort *q = p; - LIST_REMOVE(ports, p, q); - free(q); - } -} - -int expose_port_flush(ExposePort* l, union in_addr_union *exposed) { - ExposePort *p; - int r, af = AF_INET; - - assert(exposed); - - if (!l) - return 0; - - if (in_addr_is_null(af, exposed)) - return 0; - - log_debug("Lost IP address."); - - LIST_FOREACH(ports, p, l) { - r = fw_add_local_dnat(false, - af, - p->protocol, - NULL, - NULL, 0, - NULL, 0, - p->host_port, - exposed, - p->container_port, - NULL); - if (r < 0) - log_warning_errno(r, "Failed to modify firewall: %m"); - } - - *exposed = IN_ADDR_NULL; - return 0; -} - -int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) { - _cleanup_free_ struct local_address *addresses = NULL; - _cleanup_free_ char *pretty = NULL; - union in_addr_union new_exposed; - ExposePort *p; - bool add; - int af = AF_INET, r; - - assert(exposed); - - /* Invoked each time an address is added or removed inside the - * container */ - - if (!l) - return 0; - - r = local_addresses(rtnl, 0, af, &addresses); - if (r < 0) - return log_error_errno(r, "Failed to enumerate local addresses: %m"); - - add = r > 0 && - addresses[0].family == af && - addresses[0].scope < RT_SCOPE_LINK; - - if (!add) - return expose_port_flush(l, exposed); - - new_exposed = addresses[0].address; - if (in_addr_equal(af, exposed, &new_exposed)) - return 0; - - in_addr_to_string(af, &new_exposed, &pretty); - log_debug("New container IP is %s.", strna(pretty)); - - LIST_FOREACH(ports, p, l) { - - r = fw_add_local_dnat(true, - af, - p->protocol, - NULL, - NULL, 0, - NULL, 0, - p->host_port, - &new_exposed, - p->container_port, - in_addr_is_null(af, exposed) ? NULL : exposed); - if (r < 0) - log_warning_errno(r, "Failed to modify firewall: %m"); - } - - *exposed = new_exposed; - return 0; -} - -int expose_port_send_rtnl(int send_fd) { - _cleanup_close_ int fd = -1; - int r; - - assert(send_fd >= 0); - - fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate container netlink: %m"); - - /* 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, 0); - if (r < 0) - return log_error_errno(r, "Failed to send netlink fd: %m"); - - return 0; -} - -int expose_port_watch_rtnl( - sd_event *event, - int recv_fd, - sd_netlink_message_handler_t handler, - union in_addr_union *exposed, - sd_netlink **ret) { - _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, 0); - if (fd < 0) - return log_error_errno(fd, "Failed to recv netlink fd: %m"); - - r = sd_netlink_open_fd(&rtnl, fd); - if (r < 0) { - safe_close(fd); - return log_error_errno(r, "Failed to create rtnl object: %m"); - } - - r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed); - if (r < 0) - return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m"); - - r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed); - if (r < 0) - return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m"); - - r = sd_netlink_attach_event(rtnl, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to add to even loop: %m"); - - *ret = rtnl; - rtnl = NULL; - - return 0; -} diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h deleted file mode 100644 index 741ad9765c..0000000000 --- a/src/nspawn/nspawn-expose-ports.h +++ /dev/null @@ -1,44 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-event.h" -#include "sd-netlink.h" - -#include "in-addr-util.h" -#include "list.h" - -typedef struct ExposePort { - int protocol; - uint16_t host_port; - uint16_t container_port; - LIST_FIELDS(struct ExposePort, ports); -} ExposePort; - -void expose_port_free_all(ExposePort *p); -int expose_port_parse(ExposePort **l, const char *s); - -int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret); -int expose_port_send_rtnl(int send_fd); - -int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed); -int expose_port_flush(ExposePort* l, union in_addr_union *exposed); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf deleted file mode 100644 index 3231a48d5a..0000000000 --- a/src/nspawn/nspawn-gperf.gperf +++ /dev/null @@ -1,45 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "nspawn-settings.h" -#include "nspawn-expose-ports.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name nspawn_gperf_hash -%define lookup-function-name nspawn_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Exec.Boot, config_parse_boot, 0, 0 -Exec.ProcessTwo, config_parse_pid2, 0, 0 -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) -Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) -Exec.PrivateUsers, config_parse_private_users, 0, 0 -Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) -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 -Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) -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_ifname, 0, offsetof(Settings, network_bridge) -Network.Zone, config_parse_network_zone, 0, 0 -Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c deleted file mode 100644 index 85e2c943e3..0000000000 --- a/src/nspawn/nspawn-mount.c +++ /dev/null @@ -1,944 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#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; - - assert(l); - assert(n); - assert(t >= 0); - assert(t < _CUSTOM_MOUNT_TYPE_MAX); - - c = realloc(*l, (*n + 1) * sizeof(CustomMount)); - if (!c) - return NULL; - - *l = c; - ret = *l + *n; - (*n)++; - - *ret = (CustomMount) { .type = t }; - - return ret; -} - -void custom_mount_free_all(CustomMount *l, unsigned n) { - unsigned i; - - for (i = 0; i < n; i++) { - CustomMount *m = l + i; - - free(m->source); - free(m->destination); - free(m->options); - - if (m->work_dir) { - (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL); - free(m->work_dir); - } - - strv_free(m->lower); - } - - free(l); -} - -int custom_mount_compare(const void *a, const void *b) { - const CustomMount *x = a, *y = b; - int r; - - r = path_compare(x->destination, y->destination); - if (r != 0) - return r; - - if (x->type < y->type) - return -1; - if (x->type > y->type) - return 1; - - return 0; -} - -int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) { - _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; - const char *p = s; - CustomMount *m; - int r; - - assert(l); - assert(n); - - r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - if (r == 1) { - destination = strdup(source); - if (!destination) - return -ENOMEM; - } - - if (r == 2 && !isempty(p)) { - opts = strdup(p); - if (!opts) - return -ENOMEM; - } - - if (!path_is_absolute(source)) - return -EINVAL; - - if (!path_is_absolute(destination)) - return -EINVAL; - - m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); - if (!m) - return log_oom(); - - m->source = source; - m->destination = destination; - m->read_only = read_only; - m->options = opts; - - source = destination = opts = NULL; - return 0; -} - -int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) { - _cleanup_free_ char *path = NULL, *opts = NULL; - const char *p = s; - CustomMount *m; - int r; - - assert(l); - assert(n); - assert(s); - - r = extract_first_word(&p, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - if (isempty(p)) - opts = strdup("mode=0755"); - else - opts = strdup(p); - if (!opts) - return -ENOMEM; - - if (!path_is_absolute(path)) - return -EINVAL; - - m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS); - if (!m) - return -ENOMEM; - - m->destination = path; - m->options = opts; - - path = opts = NULL; - return 0; -} - -static int tmpfs_patch_options( - const char *options, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context, - char **ret) { - - char *buf = NULL; - - if (userns && uid_shift != 0) { - assert(uid_shift != UID_INVALID); - - if (options) - (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, uid_shift, uid_shift); - else - (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, uid_shift, uid_shift); - if (!buf) - return -ENOMEM; - - options = buf; - } - -#ifdef HAVE_SELINUX - if (selinux_apifs_context) { - char *t; - - if (options) - t = strjoin(options, ",context=\"", selinux_apifs_context, "\"", NULL); - else - t = strjoin("context=\"", selinux_apifs_context, "\"", NULL); - if (!t) { - free(buf); - return -ENOMEM; - } - - free(buf); - buf = t; - } -#endif - - *ret = buf; - 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 use_userns, bool in_userns, - bool use_netns, - uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - typedef struct MountPoint { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; - bool fatal; - bool in_userns; - bool use_netns; - } MountPoint; - - static const MountPoint mount_table[] = { - { "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 ...*/ - { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */ - { 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, 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 - }; - - unsigned k; - int r; - - for (k = 0; k < ELEMENTSOF(mount_table); k++) { - _cleanup_free_ char *where = NULL, *options = NULL; - const char *o; - - 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); - if (!where) - return log_oom(); - - r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); - - /* Skip this entry if it is not a remount. */ - if (mount_table[k].what && r > 0) - continue; - - r = mkdir_p(where, 0755); - if (r < 0) { - if (mount_table[k].fatal) - return log_error_errno(r, "Failed to create directory %s: %m", where); - - log_debug_errno(r, "Failed to create directory %s: %m", where); - continue; - } - - o = mount_table[k].options; - if (streq_ptr(mount_table[k].type, "tmpfs")) { - r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, selinux_apifs_context, &options); - if (r < 0) - return log_oom(); - if (r > 0) - o = options; - } - - if (mount(mount_table[k].what, - where, - mount_table[k].type, - mount_table[k].flags, - o) < 0) { - - if (mount_table[k].fatal) - return log_error_errno(errno, "mount(%s) failed: %m", where); - - log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where); - } - } - - return 0; -} - -static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) { - const char *p = options; - unsigned long flags = *mount_flags; - char *opts = NULL; - - assert(options); - - for (;;) { - _cleanup_free_ char *word = NULL; - int r = extract_first_word(&p, &word, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to extract mount option: %m"); - if (r == 0) - break; - - if (streq(word, "rbind")) - flags |= MS_REC; - else if (streq(word, "norbind")) - flags &= ~MS_REC; - else { - log_error("Invalid bind mount option: %s", word); - return -EINVAL; - } - } - - *mount_flags = flags; - /* in the future mount_opts will hold string options for mount(2) */ - *mount_opts = opts; - - return 0; -} - -static int mount_bind(const char *dest, CustomMount *m) { - struct stat source_st, dest_st; - const char *where; - unsigned long mount_flags = MS_BIND | MS_REC; - _cleanup_free_ char *mount_opts = NULL; - int r; - - assert(m); - - if (m->options) { - r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts); - if (r < 0) - return r; - } - - if (stat(m->source, &source_st) < 0) - return log_error_errno(errno, "Failed to stat %s: %m", m->source); - - where = prefix_roota(dest, m->destination); - - if (stat(where, &dest_st) >= 0) { - if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { - log_error("Cannot bind mount directory %s on file %s.", m->source, where); - return -EINVAL; - } - - if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) { - log_error("Cannot bind mount file %s on directory %s.", m->source, where); - return -EINVAL; - } - - } else if (errno == ENOENT) { - r = mkdir_parents_label(where, 0755); - if (r < 0) - return log_error_errno(r, "Failed to make parents of %s: %m", where); - - /* Create the mount point. Any non-directory file can be - * mounted on any non-directory file (regular, fifo, socket, - * char, block). - */ - if (S_ISDIR(source_st.st_mode)) - r = mkdir_label(where, 0755); - else - r = touch(where); - if (r < 0) - return log_error_errno(r, "Failed to create mount point %s: %m", where); - - } else { - return log_error_errno(errno, "Failed to stat %s: %m", where); - } - - if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) - return log_error_errno(errno, "mount(%s) failed: %m", where); - - if (m->read_only) { - r = bind_remount_recursive(where, true); - if (r < 0) - return log_error_errno(r, "Read-only bind mount failed: %m"); - } - - return 0; -} - -static int mount_tmpfs( - const char *dest, - CustomMount *m, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - const char *where, *options; - _cleanup_free_ char *buf = NULL; - int r; - - assert(dest); - assert(m); - - where = prefix_roota(dest, m->destination); - - r = mkdir_p_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); - - r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); - if (r < 0) - return log_oom(); - options = r > 0 ? buf : m->options; - - if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0) - return log_error_errno(errno, "tmpfs mount to %s failed: %m", where); - - return 0; -} - -static char *joined_and_escaped_lower_dirs(char * const *lower) { - _cleanup_strv_free_ char **sv = NULL; - - sv = strv_copy(lower); - if (!sv) - return NULL; - - strv_reverse(sv); - - if (!strv_shell_escape(sv, ",:")) - return NULL; - - return strv_join(sv, ":"); -} - -static int mount_overlay(const char *dest, CustomMount *m) { - _cleanup_free_ char *lower = NULL; - const char *where, *options; - int r; - - assert(dest); - assert(m); - - where = prefix_roota(dest, m->destination); - - r = mkdir_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); - - (void) mkdir_p_label(m->source, 0755); - - lower = joined_and_escaped_lower_dirs(m->lower); - if (!lower) - return log_oom(); - - if (m->read_only) { - _cleanup_free_ char *escaped_source = NULL; - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); - - options = strjoina("lowerdir=", escaped_source, ":", lower); - } else { - _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL; - - assert(m->work_dir); - (void) mkdir_label(m->work_dir, 0700); - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); - escaped_work_dir = shell_escape(m->work_dir, ",:"); - if (!escaped_work_dir) - return log_oom(); - - options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir); - } - - if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0) - return log_error_errno(errno, "overlay mount to %s failed: %m", where); - - return 0; -} - -int mount_custom( - const char *dest, - CustomMount *mounts, unsigned n, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - unsigned i; - int r; - - assert(dest); - - for (i = 0; i < n; i++) { - CustomMount *m = mounts + i; - - switch (m->type) { - - case CUSTOM_MOUNT_BIND: - r = mount_bind(dest, m); - break; - - case CUSTOM_MOUNT_TMPFS: - r = mount_tmpfs(dest, m, userns, uid_shift, uid_range, selinux_apifs_context); - break; - - case CUSTOM_MOUNT_OVERLAY: - r = mount_overlay(dest, m); - break; - - default: - assert_not_reached("Unknown custom mount type"); - } - - if (r < 0) - return r; - } - - return 0; -} - -static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) { - char *to; - int r; - - to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy); - - r = path_is_mount_point(to, 0); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to); - if (r > 0) - return 0; - - mkdir_p(to, 0755); - - /* The superblock mount options of the mount point need to be - * identical to the hosts', and hence writable... */ - if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0) - return log_error_errno(errno, "Failed to mount to %s: %m", to); - - /* ... hence let's only make the bind mount read-only, not the - * superblock. */ - if (read_only) { - if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) - return log_error_errno(errno, "Failed to remount %s read-only: %m", to); - } - return 1; -} - -static int mount_legacy_cgroups( - const char *dest, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - _cleanup_set_free_free_ Set *controllers = NULL; - const char *cgroup_root; - int r; - - 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) - return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); - if (r == 0) { - _cleanup_free_ char *options = NULL; - - r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, selinux_apifs_context, &options); - if (r < 0) - return log_oom(); - - if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options) < 0) - return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m"); - } - - if (cg_unified() > 0) - goto skip_controllers; - - controllers = set_new(&string_hash_ops); - if (!controllers) - return log_oom(); - - r = cg_kernel_controllers(controllers); - if (r < 0) - return log_error_errno(r, "Failed to determine cgroup controllers: %m"); - - for (;;) { - _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL; - - controller = set_steal_first(controllers); - if (!controller) - break; - - origin = prefix_root("/sys/fs/cgroup/", controller); - if (!origin) - return log_oom(); - - r = readlink_malloc(origin, &combined); - if (r == -EINVAL) { - /* Not a symbolic link, but directly a single cgroup hierarchy */ - - r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true); - if (r < 0) - return r; - - } else if (r < 0) - return log_error_errno(r, "Failed to read link %s: %m", origin); - else { - _cleanup_free_ char *target = NULL; - - target = prefix_root(dest, origin); - if (!target) - return log_oom(); - - /* A symbolic link, a combination of controllers in one hierarchy */ - - if (!filename_is_valid(combined)) { - log_warning("Ignoring invalid combined hierarchy %s.", combined); - continue; - } - - r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true); - if (r < 0) - return r; - - r = symlink_idempotent(combined, target); - if (r == -EINVAL) { - log_error("Invalid existing symlink for combined hierarchy"); - return r; - } - if (r < 0) - return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); - } - } - -skip_controllers: - r = mount_legacy_cgroup_hierarchy(dest, "none,name=systemd,xattr", "systemd", false); - if (r < 0) - return r; - - if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0) - return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root); - - return 0; -} - -static int mount_unified_cgroups(const char *dest) { - const char *p; - int r; - - assert(dest); - - 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 = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs"); - if (access(p, F_OK) >= 0) - return 0; - if (errno != ENOENT) - return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p); - - log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p); - return -EINVAL; - } - - if (mount("cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0) - return log_error_errno(errno, "Failed to mount unified cgroup hierarchy to %s: %m", p); - - return 0; -} - -int mount_cgroups( - const char *dest, - bool unified_requested, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - if (unified_requested) - return mount_unified_cgroups(dest); - else - return mount_legacy_cgroups(dest, userns, uid_shift, uid_range, selinux_apifs_context); -} - -int mount_systemd_cgroup_writable( - const char *dest, - bool unified_requested) { - - _cleanup_free_ char *own_cgroup_path = NULL; - const char *systemd_root, *systemd_own; - int r; - - assert(dest); - - r = cg_pid_get_path(NULL, 0, &own_cgroup_path); - if (r < 0) - return log_error_errno(r, "Failed to determine our own cgroup path: %m"); - - /* If we are living in the top-level, then there's nothing to do... */ - if (path_equal(own_cgroup_path, "/")) - return 0; - - if (unified_requested) { - systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path); - systemd_root = prefix_roota(dest, "/sys/fs/cgroup"); - } else { - systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path); - systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd"); - } - - /* Make our own cgroup a (writable) bind mount */ - if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path); - - /* And then remount the systemd cgroup root read-only */ - if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) - return log_error_errno(errno, "Failed to mount cgroup root read-only: %m"); - - return 0; -} - -int setup_volatile_state( - const char *directory, - VolatileMode mode, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - _cleanup_free_ char *buf = NULL; - const char *p, *options; - int r; - - assert(directory); - - if (mode != VOLATILE_STATE) - return 0; - - /* --volatile=state means we simply overmount /var - with a tmpfs, and the rest read-only. */ - - r = bind_remount_recursive(directory, true); - if (r < 0) - return log_error_errno(r, "Failed to remount %s read-only: %m", directory); - - p = prefix_roota(directory, "/var"); - r = mkdir(p, 0755); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create %s: %m", directory); - - options = "mode=755"; - r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); - if (r < 0) - return log_oom(); - if (r > 0) - options = buf; - - if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0) - return log_error_errno(errno, "Failed to mount tmpfs to /var: %m"); - - return 0; -} - -int setup_volatile( - const char *directory, - VolatileMode mode, - bool userns, uid_t uid_shift, uid_t uid_range, - const char *selinux_apifs_context) { - - bool tmpfs_mounted = false, bind_mounted = false; - char template[] = "/tmp/nspawn-volatile-XXXXXX"; - _cleanup_free_ char *buf = NULL; - const char *f, *t, *options; - int r; - - assert(directory); - - if (mode != VOLATILE_YES) - return 0; - - /* --volatile=yes means we mount a tmpfs to the root dir, and - the original /usr to use inside it, and that read-only. */ - - if (!mkdtemp(template)) - return log_error_errno(errno, "Failed to create temporary directory: %m"); - - options = "mode=755"; - r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); - if (r < 0) - return log_oom(); - if (r > 0) - options = buf; - - if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) { - r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m"); - goto fail; - } - - tmpfs_mounted = true; - - f = prefix_roota(directory, "/usr"); - t = prefix_roota(template, "/usr"); - - r = mkdir(t, 0755); - if (r < 0 && errno != EEXIST) { - r = log_error_errno(errno, "Failed to create %s: %m", t); - goto fail; - } - - if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) { - r = log_error_errno(errno, "Failed to create /usr bind mount: %m"); - goto fail; - } - - bind_mounted = true; - - r = bind_remount_recursive(t, true); - if (r < 0) { - log_error_errno(r, "Failed to remount %s read-only: %m", t); - goto fail; - } - - if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) { - r = log_error_errno(errno, "Failed to move root mount: %m"); - goto fail; - } - - (void) rmdir(template); - - return 0; - -fail: - if (bind_mounted) - (void) umount(t); - - if (tmpfs_mounted) - (void) umount(template); - (void) rmdir(template); - return r; -} - -VolatileMode volatile_mode_from_string(const char *s) { - int b; - - if (isempty(s)) - return _VOLATILE_MODE_INVALID; - - b = parse_boolean(s); - if (b > 0) - return VOLATILE_YES; - if (b == 0) - return VOLATILE_NO; - - if (streq(s, "state")) - return VOLATILE_STATE; - - return _VOLATILE_MODE_INVALID; -} diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h deleted file mode 100644 index 0daf145412..0000000000 --- a/src/nspawn/nspawn-mount.h +++ /dev/null @@ -1,69 +0,0 @@ -#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 . -***/ - -#include - -typedef enum VolatileMode { - VOLATILE_NO, - VOLATILE_YES, - VOLATILE_STATE, - _VOLATILE_MODE_MAX, - _VOLATILE_MODE_INVALID = -1 -} VolatileMode; - -typedef enum CustomMountType { - CUSTOM_MOUNT_BIND, - CUSTOM_MOUNT_TMPFS, - CUSTOM_MOUNT_OVERLAY, - _CUSTOM_MOUNT_TYPE_MAX, - _CUSTOM_MOUNT_TYPE_INVALID = -1 -} CustomMountType; - -typedef struct CustomMount { - CustomMountType type; - bool read_only; - char *source; /* for overlayfs this is the upper directory */ - char *destination; - char *options; - char *work_dir; - char **lower; -} CustomMount; - -CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); - -void custom_mount_free_all(CustomMount *l, unsigned n); -int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); -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 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); - -int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); - -int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); -int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); - -VolatileMode volatile_mode_from_string(const char *s); diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c deleted file mode 100644 index 428cc04de0..0000000000 --- a/src/nspawn/nspawn-network.c +++ /dev/null @@ -1,694 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "libudev.h" -#include "sd-id128.h" -#include "sd-netlink.h" - -#include "alloc-util.h" -#include "ether-addr-util.h" -#include "lockfile-util.h" -#include "netlink-util.h" -#include "nspawn-network.h" -#include "siphash24.h" -#include "socket-util.h" -#include "stat-util.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 remove_one_link(sd_netlink *rtnl, const char *name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - if (isempty(name)) - return 0; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r == -ENODEV) /* Already gone */ - return 0; - if (r < 0) - return log_error_errno(r, "Failed to remove interface %s: %m", name); - - return 1; -} - -static int generate_mac( - const char *machine_name, - struct ether_addr *mac, - sd_id128_t hash_key, - uint64_t idx) { - - uint64_t result; - size_t l, sz; - uint8_t *v, *i; - int r; - - l = strlen(machine_name); - sz = sizeof(sd_id128_t) + l; - if (idx > 0) - sz += sizeof(idx); - - v = alloca(sz); - - /* fetch some persistent data unique to the host */ - r = sd_id128_get_machine((sd_id128_t*) v); - if (r < 0) - return r; - - /* combine with some data unique (on this host) to this - * container instance */ - i = mempcpy(v + sizeof(sd_id128_t), machine_name, l); - if (idx > 0) { - idx = htole64(idx); - memcpy(i, &idx, sizeof(idx)); - } - - /* Let's hash the host machine ID plus the container name. We - * use a fixed, but originally randomly created hash key here. */ - result = htole64(siphash24(v, sz, hash_key.bytes)); - - assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, &result, ETH_ALEN); - - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - return 0; -} - -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) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - 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, 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); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container(m, VETH_INFO_PEER); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - 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); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - 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) - return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_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; -} - -static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r, bridge_ifi; - - assert(rtnl); - assert(veth_name); - assert(bridge_name); - - bridge_ifi = (int) if_nametoindex(bridge_name); - if (bridge_ifi <= 0) - return -errno; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); - if (r < 0) - return r; - - r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name); - if (r < 0) - return r; - - r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return r; - - return bridge_ifi; -} - -static int create_bridge(sd_netlink *rtnl, const char *bridge_name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) - return r; - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name); - if (r < 0) - return r; - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return r; - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge"); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(m); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(m); - if (r < 0) - return r; - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return r; - - return 0; -} - -int setup_bridge(const char *veth_name, const char *bridge_name, bool create) { - _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - int r, bridge_ifi; - unsigned n = 0; - - assert(veth_name); - assert(bridge_name); - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - if (create) { - /* We take a system-wide lock here, so that we can safely check whether there's still a member in the - * bridge before removing it, without risking interference from other nspawn instances. */ - - r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); - if (r < 0) - return log_error_errno(r, "Failed to take network zone lock: %m"); - } - - for (;;) { - bridge_ifi = join_bridge(rtnl, veth_name, bridge_name); - if (bridge_ifi >= 0) - return bridge_ifi; - if (bridge_ifi != -ENODEV || !create || n > 10) - return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name); - - /* Count attempts, so that we don't enter an endless loop here. */ - n++; - - /* The bridge doesn't exist yet. Let's create it */ - r = create_bridge(rtnl, bridge_name); - if (r < 0) - return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name); - - /* Try again, now that the bridge exists */ - } -} - -int remove_bridge(const char *bridge_name) { - _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - const char *path; - int r; - - /* Removes the specified bridge, but only if it is currently empty */ - - if (isempty(bridge_name)) - return 0; - - r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); - if (r < 0) - return log_error_errno(r, "Failed to take network zone lock: %m"); - - path = strjoina("/sys/class/net/", bridge_name, "/brif"); - - r = dir_is_empty(path); - if (r == -ENOENT) /* Already gone? */ - return 0; - if (r < 0) - return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name); - if (r == 0) /* Still populated, leave it around */ - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - return remove_one_link(rtnl, bridge_name); -} - -static int parse_interface(struct udev *udev, const char *name) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - char ifi_str[2 + DECIMAL_STR_MAX(int)]; - int ifi; - - ifi = (int) if_nametoindex(name); - if (ifi <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", name); - - sprintf(ifi_str, "n%i", ifi); - d = udev_device_new_from_device_id(udev, ifi_str); - if (!d) - return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name); - - if (udev_device_get_is_initialized(d) <= 0) { - log_error("Network interface %s is not initialized yet.", name); - return -EBUSY; - } - - return ifi; -} - -int move_network_interfaces(pid_t pid, char **ifaces) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **i; - int r; - - if (strv_isempty(ifaces)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, ifaces) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i); - } - - return 0; -} - -int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - unsigned idx = 0; - char **i; - int r; - - if (strv_isempty(ifaces)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, ifaces) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - _cleanup_free_ char *n = NULL; - struct ether_addr mac; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++); - if (r < 0) - return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); - - 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_u32(m, IFLA_LINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface index: %m"); - - n = strappend("mv-", *i); - if (!n) - return log_oom(); - - strshorten(n, IFNAMSIZ-1); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); - 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); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); - if (r < 0) - return log_error_errno(r, "Failed to append macvlan mode: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); - } - - return 0; -} - -int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **i; - int r; - - if (strv_isempty(ifaces)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, ifaces) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - _cleanup_free_ char *n = NULL; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - 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_u32(m, IFLA_LINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface index: %m"); - - n = strappend("iv-", *i); - if (!n) - return log_oom(); - - strshorten(n, IFNAMSIZ-1); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); - if (r < 0) - return log_error_errno(r, "Failed to add ipvlan mode: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); - } - - 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 || !ifname_valid(a)) - return -EINVAL; - - r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0 || !ifname_valid(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; -} - -int remove_veth_links(const char *primary, char **pairs) { - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **a, **b; - int r; - - /* In some cases the kernel might pin the veth links between host and container even after the namespace - * died. Hence, let's better remove them explicitly too. */ - - if (isempty(primary) && strv_isempty(pairs)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - remove_one_link(rtnl, primary); - - STRV_FOREACH_PAIR(a, b, pairs) - remove_one_link(rtnl, *a); - - return 0; -} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h deleted file mode 100644 index 3d8861e1e5..0000000000 --- a/src/nspawn/nspawn-network.h +++ /dev/null @@ -1,39 +0,0 @@ -#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 . -***/ - -#include -#include -#include - -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, bool create); -int remove_bridge(const char *bridge_name); - -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); - -int remove_veth_links(const char *primary, char **pairs); diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c deleted file mode 100644 index ded5866d05..0000000000 --- a/src/nspawn/nspawn-patch-uid.c +++ /dev/null @@ -1,481 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#ifdef HAVE_ACL -#include -#endif -#include -#include -#include - -#include "acl-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "missing.h" -#include "nspawn-patch-uid.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" - -#ifdef HAVE_ACL - -static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - acl_t acl; - - assert(fd >= 0); - assert(ret); - - if (name) { - _cleanup_close_ int child_fd = -1; - - child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (child_fd < 0) - return -errno; - - xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); - acl = acl_get_file(procfs_path, type); - } else if (type == ACL_TYPE_ACCESS) - acl = acl_get_fd(fd); - else { - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - acl = acl_get_file(procfs_path, type); - } - if (!acl) - return -errno; - - *ret = acl; - return 0; -} - -static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; - - assert(fd >= 0); - assert(acl); - - if (name) { - _cleanup_close_ int child_fd = -1; - - child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (child_fd < 0) - return -errno; - - xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); - r = acl_set_file(procfs_path, type, acl); - } else if (type == ACL_TYPE_ACCESS) - r = acl_set_fd(fd, acl); - else { - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - r = acl_set_file(procfs_path, type, acl); - } - if (r < 0) - return -errno; - - return 0; -} - -static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) { - _cleanup_(acl_freep) acl_t copy = NULL; - acl_entry_t i; - int r; - - assert(acl); - assert(ret); - - r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - if (r < 0) - return -errno; - while (r > 0) { - uid_t *old_uid, new_uid; - bool modify = false; - acl_tag_t tag; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (IN_SET(tag, ACL_USER, ACL_GROUP)) { - - /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that - * this is actually OK */ - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - - old_uid = acl_get_qualifier(i); - if (!old_uid) - return -errno; - - new_uid = shift | (*old_uid & UINT32_C(0xFFFF)); - if (!uid_is_valid(new_uid)) - return -EINVAL; - - modify = new_uid != *old_uid; - if (modify && !copy) { - int n; - - /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the - * beginning, so that we copy all entries, starting from the first, this time. */ - - n = acl_entries(acl); - if (n < 0) - return -errno; - - copy = acl_init(n); - if (!copy) - return -errno; - - /* Seek back to the beginning */ - r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - if (r < 0) - return -errno; - continue; - } - } - - if (copy) { - acl_entry_t new_entry; - - if (acl_create_entry(©, &new_entry) < 0) - return -errno; - - if (acl_copy_entry(new_entry, i) < 0) - return -errno; - - if (modify) - if (acl_set_qualifier(new_entry, &new_uid) < 0) - return -errno; - } - - r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i); - if (r < 0) - return -errno; - } - - *ret = copy; - copy = NULL; - - return !!*ret; -} - -static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { - _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL; - bool changed = false; - int r; - - assert(fd >= 0); - assert(st); - - /* ACLs are not supported on symlinks, there's no point in trying */ - if (S_ISLNK(st->st_mode)) - return 0; - - r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl); - if (r == -EOPNOTSUPP) - return 0; - if (r < 0) - return r; - - r = shift_acl(acl, shift, &shifted); - if (r < 0) - return r; - if (r > 0) { - r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted); - if (r < 0) - return r; - - changed = true; - } - - if (S_ISDIR(st->st_mode)) { - acl_free(acl); - acl_free(shifted); - - acl = shifted = NULL; - - r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl); - if (r < 0) - return r; - - r = shift_acl(acl, shift, &shifted); - if (r < 0) - return r; - if (r > 0) { - r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted); - if (r < 0) - return r; - - changed = true; - } - } - - return changed; -} - -#else - -static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { - return 0; -} - -#endif - -static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) { - uid_t new_uid; - gid_t new_gid; - bool changed = false; - int r; - - assert(fd >= 0); - assert(st); - - new_uid = shift | (st->st_uid & UINT32_C(0xFFFF)); - new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF)); - - if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid)) - return -EINVAL; - - if (st->st_uid != new_uid || st->st_gid != new_gid) { - if (name) - r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW); - else - r = fchown(fd, new_uid, new_gid); - if (r < 0) - return -errno; - - /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */ - if (name) { - if (!S_ISLNK(st->st_mode)) - r = fchmodat(fd, name, st->st_mode, 0); - else /* AT_SYMLINK_NOFOLLOW is not available for fchmodat() */ - r = 0; - } else - r = fchmod(fd, st->st_mode); - if (r < 0) - return -errno; - - changed = true; - } - - r = patch_acls(fd, name, st, shift); - if (r < 0) - return r; - - return r > 0 || changed; -} - -/* - * Check if the filesystem is fully compatible with user namespaces or - * UID/GID patching. Some filesystems in this list can be fully mounted inside - * user namespaces, however their inodes may relate to host resources or only - * valid in the global user namespace, therefore no patching should be applied. - */ -static int is_fs_fully_userns_compatible(int fd) { - struct statfs sfs; - - assert(fd >= 0); - - if (fstatfs(fd, &sfs) < 0) - return -errno; - - return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SECURITYFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, BPF_FS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, TRACEFS_MAGIC) || - F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC); -} - -static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) { - bool changed = false; - int r; - - assert(fd >= 0); - - /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we - * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's - * stop the recursion when we hit procfs, sysfs or some other special file systems. */ - r = is_fs_fully_userns_compatible(fd); - if (r < 0) - goto finish; - if (r > 0) { - r = 0; /* don't recurse */ - goto finish; - } - - r = patch_fd(fd, NULL, st, shift); - if (r == -EROFS) { - _cleanup_free_ char *name = NULL; - - if (!is_toplevel) { - /* When we hit a ready-only subtree we simply skip it, but log about it. */ - (void) fd_get_path(fd, &name); - log_debug("Skippping read-only file or directory %s.", strna(name)); - r = 0; - } - - goto finish; - } - if (r < 0) - goto finish; - - if (S_ISDIR(st->st_mode)) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - if (!donate_fd) { - int copy; - - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (copy < 0) { - r = -errno; - goto finish; - } - - fd = copy; - donate_fd = true; - } - - d = fdopendir(fd); - if (!d) { - r = -errno; - goto finish; - } - fd = -1; - - FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { - struct stat fst; - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { - r = -errno; - goto finish; - } - - if (S_ISDIR(fst.st_mode)) { - int subdir_fd; - - subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (subdir_fd < 0) { - r = -errno; - goto finish; - - } - - r = recurse_fd(subdir_fd, true, &fst, shift, false); - if (r < 0) - goto finish; - if (r > 0) - changed = true; - - } else { - r = patch_fd(dirfd(d), de->d_name, &fst, shift); - if (r < 0) - goto finish; - if (r > 0) - changed = true; - } - } - } - - r = changed; - -finish: - if (donate_fd) - safe_close(fd); - - return r; -} - -static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) { - struct stat st; - int r; - - assert(fd >= 0); - - /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an - * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges - * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual - * UID within the container. */ - - if ((shift & 0xFFFF) != 0) { - /* We only support containers where the shift starts at a 2^16 boundary */ - r = -EOPNOTSUPP; - goto finish; - } - - if (range != 0x10000) { - /* We only support containers with 16bit UID ranges for the patching logic */ - r = -EOPNOTSUPP; - goto finish; - } - - if (fstat(fd, &st) < 0) { - r = -errno; - goto finish; - } - - if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) { - /* We only support containers where the uid/gid container ID match */ - r = -EBADE; - goto finish; - } - - /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume - * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */ - if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0) - return 0; - - return recurse_fd(fd, donate_fd, &st, shift, true); - -finish: - if (donate_fd) - safe_close(fd); - - return r; -} - -int fd_patch_uid(int fd, uid_t shift, uid_t range) { - return fd_patch_uid_internal(fd, false, shift, range); -} - -int path_patch_uid(const char *path, uid_t shift, uid_t range) { - int fd; - - fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (fd < 0) - return -errno; - - return fd_patch_uid_internal(fd, true, shift, range); -} diff --git a/src/nspawn/nspawn-patch-uid.h b/src/nspawn/nspawn-patch-uid.h deleted file mode 100644 index 55d0990016..0000000000 --- a/src/nspawn/nspawn-patch-uid.h +++ /dev/null @@ -1,23 +0,0 @@ -/*** - 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 . -***/ - -#include - -int fd_patch_uid(int fd, uid_t shift, uid_t range); -int path_patch_uid(const char *path, uid_t shift, uid_t range); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c deleted file mode 100644 index e5b76a0c5d..0000000000 --- a/src/nspawn/nspawn-register.c +++ /dev/null @@ -1,229 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-bus.h" - -#include "bus-error.h" -#include "bus-unit-util.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, - 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) { - - _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); - if (r < 0) - return log_error_errno(r, "Failed to open system bus: %m"); - - if (keep_unit) { - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "RegisterMachineWithNetwork", - &error, - NULL, - "sayssusai", - machine_name, - SD_BUS_MESSAGE_APPEND_ID128(uuid), - service, - "container", - (uint32_t) pid, - strempty(directory), - local_ifindex > 0 ? 1 : 0, local_ifindex); - } else { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - char **i; - unsigned j; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CreateMachineWithNetwork"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "sayssusai", - machine_name, - SD_BUS_MESSAGE_APPEND_ID128(uuid), - service, - "container", - (uint32_t) pid, - strempty(directory), - local_ifindex > 0 ? 1 : 0, local_ifindex); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - if (!isempty(slice)) { - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "closed"); - if (r < 0) - return bus_log_create_error(r); - - /* If you make changes here, also make sure to update - * systemd-nspawn@.service, to keep the device - * policies in sync regardless if we are run with or - * without the --keep-unit switch. */ - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 2, - /* Allow the container to - * access and create the API - * device nodes, so that - * PrivateDevices= in the - * container can work - * fine */ - "/dev/net/tun", "rwm", - /* Allow the container - * access to ptys. However, - * do not permit the - * container to ever create - * these device nodes. */ - "char-pts", "rw"); - if (r < 0) - return bus_log_create_error(r); - - for (j = 0; j < n_mounts; j++) { - CustomMount *cm = mounts + j; - - if (cm->type != CUSTOM_MOUNT_BIND) - continue; - - r = is_device_node(cm->source); - if (r < 0) - return log_error_errno(r, "Failed to stat %s: %m", cm->source); - - if (r) { - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, - cm->source, cm->read_only ? "r" : "rw"); - if (r < 0) - return log_error_errno(r, "Failed to append message arguments: %m"); - } - } - - if (kill_signal != 0) { - r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH(i, properties) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, NULL); - } - - if (r < 0) { - log_error("Failed to register machine: %s", bus_error_message(&error, r)); - return r; - } - - return 0; -} - -int terminate_machine(pid_t pid) { - _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; - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to open system bus: %m"); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineByPID", - &error, - &reply, - "u", - (uint32_t) pid); - if (r < 0) { - /* Note that the machine might already have been - * cleaned up automatically, hence don't consider it a - * failure if we cannot get the machine object. */ - log_debug("Failed to get machine: %s", bus_error_message(&error, r)); - return 0; - } - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - path, - "org.freedesktop.machine1.Machine", - "Terminate", - &error, - NULL, - NULL); - if (r < 0) { - log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); - return 0; - } - - return 0; -} diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h deleted file mode 100644 index 304c5a485b..0000000000 --- a/src/nspawn/nspawn-register.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-id128.h" - -#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, const char *service); -int terminate_machine(pid_t pid); diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c deleted file mode 100644 index 3ab7160ebe..0000000000 --- a/src/nspawn/nspawn-seccomp.c +++ /dev/null @@ -1,197 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include - -#ifdef HAVE_SECCOMP -#include -#endif - -#include "log.h" - -#ifdef HAVE_SECCOMP -#include "seccomp-util.h" -#endif - -#include "nspawn-seccomp.h" - -#ifdef HAVE_SECCOMP - -static int seccomp_add_default_syscall_filter(scmp_filter_ctx ctx, - uint64_t cap_list_retain) { - unsigned i; - int r; - static const struct { - uint64_t capability; - int syscall_num; - } blacklist[] = { - { 0, SCMP_SYS(_sysctl) }, /* obsolete syscall */ - { 0, SCMP_SYS(add_key) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(afs_syscall) }, /* obsolete syscall */ - { 0, SCMP_SYS(bdflush) }, -#ifdef __NR_bpf - { 0, SCMP_SYS(bpf) }, -#endif - { 0, SCMP_SYS(break) }, /* obsolete syscall */ - { 0, SCMP_SYS(create_module) }, /* obsolete syscall */ - { 0, SCMP_SYS(ftime) }, /* obsolete syscall */ - { 0, SCMP_SYS(get_kernel_syms) }, /* obsolete syscall */ - { 0, SCMP_SYS(getpmsg) }, /* obsolete syscall */ - { 0, SCMP_SYS(gtty) }, /* obsolete syscall */ -#ifdef __NR_kexec_file_load - { 0, SCMP_SYS(kexec_file_load) }, -#endif - { 0, SCMP_SYS(kexec_load) }, - { 0, SCMP_SYS(keyctl) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(lock) }, /* obsolete syscall */ - { 0, SCMP_SYS(lookup_dcookie) }, - { 0, SCMP_SYS(mpx) }, /* obsolete syscall */ - { 0, SCMP_SYS(nfsservctl) }, /* obsolete syscall */ - { 0, SCMP_SYS(open_by_handle_at) }, - { 0, SCMP_SYS(perf_event_open) }, - { 0, SCMP_SYS(prof) }, /* obsolete syscall */ - { 0, SCMP_SYS(profil) }, /* obsolete syscall */ - { 0, SCMP_SYS(putpmsg) }, /* obsolete syscall */ - { 0, SCMP_SYS(query_module) }, /* obsolete syscall */ - { 0, SCMP_SYS(quotactl) }, - { 0, SCMP_SYS(request_key) }, /* keyring is not namespaced */ - { 0, SCMP_SYS(security) }, /* obsolete syscall */ - { 0, SCMP_SYS(sgetmask) }, /* obsolete syscall */ - { 0, SCMP_SYS(ssetmask) }, /* obsolete syscall */ - { 0, SCMP_SYS(stty) }, /* obsolete syscall */ - { 0, SCMP_SYS(swapoff) }, - { 0, SCMP_SYS(swapon) }, - { 0, SCMP_SYS(sysfs) }, /* obsolete syscall */ - { 0, SCMP_SYS(tuxcall) }, /* obsolete syscall */ - { 0, SCMP_SYS(ulimit) }, /* obsolete syscall */ - { 0, SCMP_SYS(uselib) }, /* obsolete syscall */ - { 0, SCMP_SYS(ustat) }, /* obsolete syscall */ - { 0, SCMP_SYS(vserver) }, /* obsolete syscall */ - { CAP_SYSLOG, SCMP_SYS(syslog) }, - { CAP_SYS_MODULE, SCMP_SYS(delete_module) }, - { CAP_SYS_MODULE, SCMP_SYS(finit_module) }, - { CAP_SYS_MODULE, SCMP_SYS(init_module) }, - { CAP_SYS_PACCT, SCMP_SYS(acct) }, - { CAP_SYS_PTRACE, SCMP_SYS(process_vm_readv) }, - { CAP_SYS_PTRACE, SCMP_SYS(process_vm_writev) }, - { CAP_SYS_PTRACE, SCMP_SYS(ptrace) }, - { CAP_SYS_RAWIO, SCMP_SYS(ioperm) }, - { CAP_SYS_RAWIO, SCMP_SYS(iopl) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_iobase) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_read) }, - { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_write) }, -#ifdef __NR_s390_pci_mmio_read - { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_read) }, -#endif -#ifdef __NR_s390_pci_mmio_write - { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_write) }, -#endif - { CAP_SYS_TIME, SCMP_SYS(adjtimex) }, - { CAP_SYS_TIME, SCMP_SYS(clock_adjtime) }, - { CAP_SYS_TIME, SCMP_SYS(clock_settime) }, - { CAP_SYS_TIME, SCMP_SYS(settimeofday) }, - { CAP_SYS_TIME, SCMP_SYS(stime) }, - }; - - for (i = 0; i < ELEMENTSOF(blacklist); i++) { - if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability))) - continue; - - r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0); - if (r == -EFAULT) - continue; /* unknown syscall */ - if (r < 0) - return log_error_errno(r, "Failed to block syscall: %m"); - } - - return 0; -} - -int setup_seccomp(uint64_t cap_list_retain) { - scmp_filter_ctx seccomp; - int r; - - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return log_oom(); - - r = seccomp_add_secondary_archs(seccomp); - if (r < 0) { - log_error_errno(r, "Failed to add secondary archs to seccomp filter: %m"); - goto finish; - } - - r = seccomp_add_default_syscall_filter(seccomp, cap_list_retain); - if (r < 0) - goto finish; - - /* - Audit is broken in containers, much of the userspace audit - hookup will fail if running inside a container. We don't - care and just turn off creation of audit sockets. - - This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail - with EAFNOSUPPORT which audit userspace uses as indication - that audit is disabled in the kernel. - */ - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EAFNOSUPPORT), - SCMP_SYS(socket), - 2, - SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), - SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); - if (r < 0) { - log_error_errno(r, "Failed to add audit seccomp rule: %m"); - goto finish; - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) { - log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); - goto finish; - } - - r = seccomp_load(seccomp); - if (r == -EINVAL) { - log_debug_errno(r, "Kernel is probably not configured with CONFIG_SECCOMP. Disabling seccomp audit filter: %m"); - r = 0; - goto finish; - } - if (r < 0) { - log_error_errno(r, "Failed to install seccomp audit filter: %m"); - goto finish; - } - -finish: - seccomp_release(seccomp); - return r; -} - -#else - -int setup_seccomp(uint64_t cap_list_retain) { - return 0; -} - -#endif diff --git a/src/nspawn/nspawn-seccomp.h b/src/nspawn/nspawn-seccomp.h deleted file mode 100644 index 5bde16faf9..0000000000 --- a/src/nspawn/nspawn-seccomp.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - -#include - -int setup_seccomp(uint64_t cap_list_retain); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c deleted file mode 100644 index 5f1522cfb6..0000000000 --- a/src/nspawn/nspawn-settings.c +++ /dev/null @@ -1,516 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "cap-list.h" -#include "conf-parser.h" -#include "nspawn-network.h" -#include "nspawn-settings.h" -#include "parse-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" - -int settings_load(FILE *f, const char *path, Settings **ret) { - _cleanup_(settings_freep) Settings *s = NULL; - int r; - - assert(path); - assert(ret); - - s = new0(Settings, 1); - if (!s) - return -ENOMEM; - - s->start_mode = _START_MODE_INVALID; - s->personality = PERSONALITY_INVALID; - s->userns_mode = _USER_NAMESPACE_MODE_INVALID; - s->uid_shift = UID_INVALID; - s->uid_range = UID_INVALID; - - s->read_only = -1; - s->volatile_mode = _VOLATILE_MODE_INVALID; - s->userns_chown = -1; - - s->private_network = -1; - s->network_veth = -1; - - r = config_parse(NULL, path, f, - "Exec\0" - "Network\0" - "Files\0", - config_item_perf_lookup, nspawn_gperf_lookup, - false, - false, - true, - s); - if (r < 0) - return r; - - /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either - * both fields shall be initialized or neither. */ - if (s->userns_mode == USER_NAMESPACE_PICK) - s->userns_chown = true; - else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) - s->userns_chown = false; - - if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID) - s->userns_mode = USER_NAMESPACE_NO; - - *ret = s; - s = NULL; - - return 0; -} - -Settings* settings_free(Settings *s) { - - if (!s) - return NULL; - - strv_free(s->parameters); - strv_free(s->environment); - free(s->user); - free(s->working_directory); - - 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); - free(s->network_zone); - expose_port_free_all(s->expose_ports); - - custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); - free(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_zone || - 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 || - s->network_zone; -} - -DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); - -int config_parse_expose_port( - 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 *s = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = expose_port_parse(&s->expose_ports, rvalue); - if (r == -EEXIST) { - log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue); - return 0; - } - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue); - return 0; - } - - return 0; -} - -int config_parse_capability( - 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 u = 0, *result = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *word = NULL; - int cap; - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue); - return 0; - } - if (r == 0) - break; - - cap = capability_from_name(word); - if (cap < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word); - continue; - } - - u |= 1 << ((uint64_t) cap); - } - - if (u == 0) - return 0; - - *result |= u; - return 0; -} - -int config_parse_id128( - 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) { - - sd_id128_t t, *result = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = sd_id128_from_string(rvalue, &t); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); - return 0; - } - - *result = t; - return 0; -} - -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) { - - Settings *settings = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue); - return 0; - } - - return 0; -} - -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) { - - Settings *settings = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue); - return 0; - } - - return 0; -} - -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; -} - -int config_parse_network_zone( - 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; - _cleanup_free_ char *j = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - - j = strappend("vz-", rvalue); - if (!ifname_valid(j)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue); - return 0; - } - - free(settings->network_zone); - settings->network_zone = j; - j = NULL; - - return 0; -} - -int config_parse_boot( - 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 = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue); - return 0; - } - - if (r > 0) { - if (settings->start_mode == START_PID2) - goto conflict; - - settings->start_mode = START_BOOT; - } else { - if (settings->start_mode == START_BOOT) - goto conflict; - - if (settings->start_mode < 0) - settings->start_mode = START_PID1; - } - - return 0; - -conflict: - log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); - return 0; -} - -int config_parse_pid2( - 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 = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue); - return 0; - } - - if (r > 0) { - if (settings->start_mode == START_BOOT) - goto conflict; - - settings->start_mode = START_PID2; - } else { - if (settings->start_mode == START_PID2) - goto conflict; - - if (settings->start_mode < 0) - settings->start_mode = START_PID1; - } - - return 0; - -conflict: - log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); - return 0; -} - -int config_parse_private_users( - 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 = parse_boolean(rvalue); - if (r == 0) { - /* no: User namespacing off */ - settings->userns_mode = USER_NAMESPACE_NO; - settings->uid_shift = UID_INVALID; - settings->uid_range = UINT32_C(0x10000); - } else if (r > 0) { - /* yes: User namespacing on, UID range is read from root dir */ - settings->userns_mode = USER_NAMESPACE_FIXED; - settings->uid_shift = UID_INVALID; - settings->uid_range = UINT32_C(0x10000); - } else if (streq(rvalue, "pick")) { - /* pick: User namespacing on, UID range is picked randomly */ - settings->userns_mode = USER_NAMESPACE_PICK; - settings->uid_shift = UID_INVALID; - settings->uid_range = UINT32_C(0x10000); - } else { - const char *range, *shift; - uid_t sh, rn; - - /* anything else: User namespacing on, UID range is explicitly configured */ - - range = strchr(rvalue, ':'); - if (range) { - shift = strndupa(rvalue, range - rvalue); - range++; - - r = safe_atou32(range, &rn); - if (r < 0 || rn <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range); - return 0; - } - } else { - shift = rvalue; - rn = UINT32_C(0x10000); - } - - r = parse_uid(shift, &sh); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range); - return 0; - } - - settings->userns_mode = USER_NAMESPACE_FIXED; - settings->uid_shift = sh; - settings->uid_range = rn; - } - - return 0; -} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h deleted file mode 100644 index 231e6d7266..0000000000 --- a/src/nspawn/nspawn-settings.h +++ /dev/null @@ -1,118 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" -#include "nspawn-expose-ports.h" -#include "nspawn-mount.h" - -typedef enum StartMode { - START_PID1, /* Run parameters as command line as process 1 */ - START_PID2, /* Use stub init process as PID 1, run parameters as command line as process 2 */ - START_BOOT, /* Search for init system, pass arguments as parameters */ - _START_MODE_MAX, - _START_MODE_INVALID = -1 -} StartMode; - -typedef enum UserNamespaceMode { - USER_NAMESPACE_NO, - USER_NAMESPACE_FIXED, - USER_NAMESPACE_PICK, - _USER_NAMESPACE_MODE_MAX, - _USER_NAMESPACE_MODE_INVALID = -1, -} UserNamespaceMode; - -typedef enum SettingsMask { - SETTING_START_MODE = 1 << 0, - SETTING_ENVIRONMENT = 1 << 1, - SETTING_USER = 1 << 2, - SETTING_CAPABILITY = 1 << 3, - SETTING_KILL_SIGNAL = 1 << 4, - SETTING_PERSONALITY = 1 << 5, - SETTING_MACHINE_ID = 1 << 6, - SETTING_NETWORK = 1 << 7, - SETTING_EXPOSE_PORTS = 1 << 8, - SETTING_READ_ONLY = 1 << 9, - SETTING_VOLATILE_MODE = 1 << 10, - SETTING_CUSTOM_MOUNTS = 1 << 11, - SETTING_WORKING_DIRECTORY = 1 << 12, - SETTING_USERNS = 1 << 13, - SETTING_NOTIFY_READY = 1 << 14, - _SETTINGS_MASK_ALL = (1 << 15) -1 -} SettingsMask; - -typedef struct Settings { - /* [Run] */ - StartMode start_mode; - char **parameters; - char **environment; - char *user; - uint64_t capability; - uint64_t drop_capability; - int kill_signal; - unsigned long personality; - sd_id128_t machine_id; - char *working_directory; - UserNamespaceMode userns_mode; - uid_t uid_shift, uid_range; - bool notify_ready; - - /* [Image] */ - int read_only; - VolatileMode volatile_mode; - CustomMount *custom_mounts; - unsigned n_custom_mounts; - int userns_chown; - - /* [Network] */ - int private_network; - int network_veth; - char *network_bridge; - char *network_zone; - 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); - -int config_parse_capability(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_id128(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_expose_port(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_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); -int config_parse_network_zone(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_boot(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_pid2(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_private_users(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 deleted file mode 100644 index b8e8e091c8..0000000000 --- a/src/nspawn/nspawn-setuid.c +++ /dev/null @@ -1,270 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "mkdir.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]; - pid_t pid; - - assert(database); - assert(key); - assert(rpid); - - if (pipe2(pipe_fds, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to allocate pipe: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork getent child: %m"); - else if (pid == 0) { - int nullfd; - char *empty_env = NULL; - - if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (pipe_fds[0] > 2) - safe_close(pipe_fds[0]); - if (pipe_fds[1] > 2) - safe_close(pipe_fds[1]); - - nullfd = open("/dev/null", O_RDWR); - if (nullfd < 0) - _exit(EXIT_FAILURE); - - if (dup3(nullfd, STDIN_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (dup3(nullfd, STDERR_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (nullfd > 2) - safe_close(nullfd); - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - close_all_fds(NULL, 0); - - execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); - execle("/bin/getent", "getent", database, key, NULL, &empty_env); - _exit(EXIT_FAILURE); - } - - pipe_fds[1] = safe_close(pipe_fds[1]); - - *rpid = pid; - - return pipe_fds[0]; -} - -int change_uid_gid(const char *user, char **_home) { - char line[LINE_MAX], *x, *u, *g, *h; - const char *word, *state; - _cleanup_free_ uid_t *uids = NULL; - _cleanup_free_ char *home = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; - unsigned n_uids = 0; - size_t sz = 0, l; - uid_t uid; - gid_t gid; - pid_t pid; - int r; - - assert(_home); - - if (!user || streq(user, "root") || streq(user, "0")) { - /* Reset everything fully to 0, just in case */ - - r = reset_uid_gid(); - if (r < 0) - return log_error_errno(r, "Failed to become root: %m"); - - *_home = NULL; - return 0; - } - - /* First, get user credentials */ - fd = spawn_getent("passwd", user, &pid); - if (fd < 0) - return fd; - - f = fdopen(fd, "r"); - if (!f) - return log_oom(); - fd = -1; - - if (!fgets(line, sizeof(line), f)) { - if (!ferror(f)) { - log_error("Failed to resolve user %s.", user); - return -ESRCH; - } - - return log_error_errno(errno, "Failed to read from getent: %m"); - } - - truncate_nl(line); - - wait_for_terminate_and_warn("getent passwd", pid, true); - - x = strchr(line, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid user field."); - return -EIO; - } - - u = strchr(x+1, ':'); - if (!u) { - log_error("/etc/passwd entry has invalid password field."); - return -EIO; - } - - u++; - g = strchr(u, ':'); - if (!g) { - log_error("/etc/passwd entry has invalid UID field."); - return -EIO; - } - - *g = 0; - g++; - x = strchr(g, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid GID field."); - return -EIO; - } - - *x = 0; - h = strchr(x+1, ':'); - if (!h) { - log_error("/etc/passwd entry has invalid GECOS field."); - return -EIO; - } - - h++; - x = strchr(h, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid home directory field."); - return -EIO; - } - - *x = 0; - - r = parse_uid(u, &uid); - if (r < 0) { - log_error("Failed to parse UID of user."); - return -EIO; - } - - r = parse_gid(g, &gid); - if (r < 0) { - log_error("Failed to parse GID of user."); - return -EIO; - } - - home = strdup(h); - if (!home) - return log_oom(); - - /* Second, get group memberships */ - fd = spawn_getent("initgroups", user, &pid); - if (fd < 0) - return fd; - - fclose(f); - f = fdopen(fd, "r"); - if (!f) - return log_oom(); - fd = -1; - - if (!fgets(line, sizeof(line), f)) { - if (!ferror(f)) { - log_error("Failed to resolve user %s.", user); - return -ESRCH; - } - - return log_error_errno(errno, "Failed to read from getent: %m"); - } - - truncate_nl(line); - - wait_for_terminate_and_warn("getent initgroups", pid, true); - - /* Skip over the username and subsequent separator whitespace */ - x = line; - x += strcspn(x, WHITESPACE); - x += strspn(x, WHITESPACE); - - FOREACH_WORD(word, l, x, state) { - char c[l+1]; - - memcpy(c, word, l); - c[l] = 0; - - if (!GREEDY_REALLOC(uids, sz, n_uids+1)) - return log_oom(); - - r = parse_uid(c, &uids[n_uids++]); - if (r < 0) { - log_error("Failed to parse group data from getent."); - return -EIO; - } - } - - r = mkdir_parents(home, 0775); - if (r < 0) - return log_error_errno(r, "Failed to make home root directory: %m"); - - r = mkdir_safe(home, 0755, uid, gid); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to make home directory: %m"); - - (void) fchown(STDIN_FILENO, uid, gid); - (void) fchown(STDOUT_FILENO, uid, gid); - (void) fchown(STDERR_FILENO, uid, gid); - - if (setgroups(n_uids, uids) < 0) - return log_error_errno(errno, "Failed to set auxiliary groups: %m"); - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "setresgid() failed: %m"); - - if (setresuid(uid, uid, uid) < 0) - return log_error_errno(errno, "setresuid() failed: %m"); - - if (_home) { - *_home = home; - home = NULL; - } - - return 0; -} diff --git a/src/nspawn/nspawn-setuid.h b/src/nspawn/nspawn-setuid.h deleted file mode 100644 index b4968ba1fc..0000000000 --- a/src/nspawn/nspawn-setuid.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int change_uid_gid(const char *user, char **ret); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c deleted file mode 100644 index 2de87e3c63..0000000000 --- a/src/nspawn/nspawn-stub-pid1.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "fd-util.h" -#include "log.h" -#include "nspawn-stub-pid1.h" -#include "process-util.h" -#include "signal-util.h" -#include "time-util.h" -#include "def.h" - -int stub_pid1(void) { - enum { - STATE_RUNNING, - STATE_REBOOT, - STATE_POWEROFF, - } state = STATE_RUNNING; - - sigset_t fullmask, oldmask, waitmask; - usec_t quit_usec = USEC_INFINITY; - pid_t pid; - int r; - - /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful - * for allowing arbitrary processes run in a container, and still have all zombies reaped. */ - - assert_se(sigfillset(&fullmask) >= 0); - assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork child pid: %m"); - - if (pid == 0) { - /* Return in the child */ - assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0); - setsid(); - return 0; - } - - reset_all_signal_handlers(); - - log_close(); - close_all_fds(NULL, 0); - log_open(); - - rename_process("STUBINIT"); - - assert_se(sigemptyset(&waitmask) >= 0); - assert_se(sigset_add_many(&waitmask, - SIGCHLD, /* posix: process died */ - SIGINT, /* sysv: ctrl-alt-del */ - SIGRTMIN+3, /* systemd: halt */ - SIGRTMIN+4, /* systemd: poweroff */ - SIGRTMIN+5, /* systemd: reboot */ - SIGRTMIN+6, /* systemd: kexec */ - SIGRTMIN+13, /* systemd: halt */ - SIGRTMIN+14, /* systemd: poweroff */ - SIGRTMIN+15, /* systemd: reboot */ - SIGRTMIN+16, /* systemd: kexec */ - -1) >= 0); - - /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't - * support reexec/reloading in this stub process. */ - - for (;;) { - siginfo_t si; - usec_t current_usec; - - si.si_pid = 0; - r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); - if (r < 0) { - r = log_error_errno(errno, "Failed to reap children: %m"); - goto finish; - } - - current_usec = now(CLOCK_MONOTONIC); - - if (si.si_pid == pid || current_usec >= quit_usec) { - - /* The child we started ourselves died or we reached a timeout. */ - - if (state == STATE_REBOOT) { /* dispatch a queued reboot */ - (void) reboot(RB_AUTOBOOT); - r = log_error_errno(errno, "Failed to reboot: %m"); - goto finish; - - } else if (state == STATE_POWEROFF) - (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */ - - if (si.si_pid == pid && si.si_code == CLD_EXITED) - r = si.si_status; /* pass on exit code */ - else - r = 255; /* signal, coredump, timeout, … */ - - goto finish; - } - if (si.si_pid != 0) - /* We reaped something. Retry until there's nothing more to reap. */ - continue; - - if (quit_usec == USEC_INFINITY) - r = sigwaitinfo(&waitmask, &si); - else { - struct timespec ts; - r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec)); - } - if (r < 0) { - if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */ - continue; - if (errno == EAGAIN) /* timeout reached */ - continue; - - r = log_error_errno(errno, "Failed to wait for signal: %m"); - goto finish; - } - - if (si.si_signo == SIGCHLD) - continue; /* Let's reap this */ - - if (state != STATE_RUNNING) - continue; - - /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a - * constant… */ - - if (si.si_signo == SIGRTMIN+3 || - si.si_signo == SIGRTMIN+4 || - si.si_signo == SIGRTMIN+13 || - si.si_signo == SIGRTMIN+14) - - state = STATE_POWEROFF; - - else if (si.si_signo == SIGINT || - si.si_signo == SIGRTMIN+5 || - si.si_signo == SIGRTMIN+6 || - si.si_signo == SIGRTMIN+15 || - si.si_signo == SIGRTMIN+16) - - state = STATE_REBOOT; - else - assert_not_reached("Got unexpected signal"); - - /* (void) kill_and_sigcont(pid, SIGTERM); */ - quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; - } - -finish: - _exit(r < 0 ? EXIT_FAILURE : r); -} diff --git a/src/nspawn/nspawn-stub-pid1.h b/src/nspawn/nspawn-stub-pid1.h deleted file mode 100644 index 36c1aaf5dd..0000000000 --- a/src/nspawn/nspawn-stub-pid1.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -int stub_pid1(void); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c deleted file mode 100644 index b1c012a9e4..0000000000 --- a/src/nspawn/nspawn.c +++ /dev/null @@ -1,4188 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_BLKID -#include -#endif -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SELINUX -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "cap-list.h" -#include "capability-util.h" -#include "cgroup-util.h" -#include "copy.h" -#include "dev-setup.h" -#include "env-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 "id128-util.h" -#include "log.h" -#include "loopback-setup.h" -#include "machine-image.h" -#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-patch-uid.h" -#include "nspawn-register.h" -#include "nspawn-seccomp.h" -#include "nspawn-settings.h" -#include "nspawn-setuid.h" -#include "nspawn-stub-pid1.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "ptyfwd.h" -#include "random-util.h" -#include "raw-clone.h" -#include "rm-rf.h" -#include "selinux-util.h" -#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" - -/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit - * UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems - * may have their own allocation ranges too. */ -#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) -#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) - -/* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path - * nspawn_notify_socket_path is relative to the container - * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ -#define NSPAWN_NOTIFY_SOCKET_PATH "/run/systemd/nspawn/notify" - -typedef enum ContainerStatus { - CONTAINER_TERMINATED, - CONTAINER_REBOOTED -} ContainerStatus; - -typedef enum LinkJournal { - LINK_NO, - LINK_AUTO, - LINK_HOST, - LINK_GUEST -} LinkJournal; - -static char *arg_directory = NULL; -static char *arg_template = NULL; -static char *arg_chdir = NULL; -static char *arg_user = NULL; -static sd_id128_t arg_uuid = {}; -static char *arg_machine = NULL; -static const char *arg_selinux_context = NULL; -static const char *arg_selinux_apifs_context = NULL; -static const char *arg_slice = NULL; -static bool arg_private_network = false; -static bool arg_read_only = false; -static StartMode arg_start_mode = START_PID1; -static bool arg_ephemeral = false; -static LinkJournal arg_link_journal = LINK_AUTO; -static bool arg_link_journal_try = false; -static uint64_t arg_caps_retain = - (1ULL << CAP_AUDIT_CONTROL) | - (1ULL << CAP_AUDIT_WRITE) | - (1ULL << CAP_CHOWN) | - (1ULL << CAP_DAC_OVERRIDE) | - (1ULL << CAP_DAC_READ_SEARCH) | - (1ULL << CAP_FOWNER) | - (1ULL << CAP_FSETID) | - (1ULL << CAP_IPC_OWNER) | - (1ULL << CAP_KILL) | - (1ULL << CAP_LEASE) | - (1ULL << CAP_LINUX_IMMUTABLE) | - (1ULL << CAP_MKNOD) | - (1ULL << CAP_NET_BIND_SERVICE) | - (1ULL << CAP_NET_BROADCAST) | - (1ULL << CAP_NET_RAW) | - (1ULL << CAP_SETFCAP) | - (1ULL << CAP_SETGID) | - (1ULL << CAP_SETPCAP) | - (1ULL << CAP_SETUID) | - (1ULL << CAP_SYS_ADMIN) | - (1ULL << CAP_SYS_BOOT) | - (1ULL << CAP_SYS_CHROOT) | - (1ULL << CAP_SYS_NICE) | - (1ULL << CAP_SYS_PTRACE) | - (1ULL << CAP_SYS_RESOURCE) | - (1ULL << CAP_SYS_TTY_CONFIG); -static CustomMount *arg_custom_mounts = NULL; -static unsigned arg_n_custom_mounts = 0; -static char **arg_setenv = NULL; -static bool arg_quiet = false; -static bool arg_share_system = false; -static bool arg_register = true; -static bool arg_keep_unit = false; -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 char *arg_network_zone = NULL; -static unsigned long arg_personality = PERSONALITY_INVALID; -static char *arg_image = NULL; -static VolatileMode arg_volatile_mode = VOLATILE_NO; -static ExposePort *arg_expose_ports = NULL; -static char **arg_property = NULL; -static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; -static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; -static bool arg_userns_chown = false; -static int arg_kill_signal = 0; -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 bool arg_notify_ready = false; - -static void help(void) { - printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" - "Spawn a minimal namespace container for debugging, testing and building.\n\n" - " -h --help Show this help\n" - " --version Print version string\n" - " -q --quiet Do not show status information\n" - " -D --directory=PATH Root directory for the container\n" - " --template=PATH Initialize root directory from template directory,\n" - " if missing\n" - " -x --ephemeral Run container with snapshot of root directory, and\n" - " remove it after exit\n" - " -i --image=PATH File system device or disk image for the container\n" - " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" - " -b --boot Boot up full system (i.e. invoke init)\n" - " --chdir=PATH Set working directory in the container\n" - " -u --user=USER Run the command under specified user or uid\n" - " -M --machine=NAME Set the machine name for the container\n" - " --uuid=UUID Set a specific machine UUID for the container\n" - " -S --slice=SLICE Place the container in the specified slice\n" - " --property=NAME=VALUE Set scope unit property\n" - " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" - " --private-users[=UIDBASE[:NUIDS]]\n" - " Run within user namespace, user configured UID/GID range\n" - " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" - " --private-network Disable network in container\n" - " --network-interface=INTERFACE\n" - " Assign an existing network interface to the\n" - " container\n" - " --network-macvlan=INTERFACE\n" - " Create a macvlan network interface based on an\n" - " existing network interface to the container\n" - " --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" - " 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" - " and container and add it to an existing bridge on\n" - " the host\n" - " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" - " and add it to an automatically managed bridge interface\n" - " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" - " Expose a container IP port on the host\n" - " -Z --selinux-context=SECLABEL\n" - " Set the SELinux security context to be used by\n" - " processes in the container\n" - " -L --selinux-apifs-context=SECLABEL\n" - " Set the SELinux security context to be used by\n" - " API/tmpfs file systems in the container\n" - " --capability=CAP In addition to the default, retain specified\n" - " capability\n" - " --drop-capability=CAP Drop the specified capability from the default set\n" - " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" - " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" - " host, try-guest, try-host\n" - " -j Equivalent to --link-journal=try-guest\n" - " --read-only Mount the root directory read-only\n" - " --bind=PATH[:PATH[:OPTIONS]]\n" - " Bind mount a file or directory from the host into\n" - " the container\n" - " --bind-ro=PATH[:PATH[:OPTIONS]\n" - " Similar, but creates a read-only bind mount\n" - " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" - " --overlay=PATH[:PATH...]:PATH\n" - " Create an overlay mount from the host to \n" - " the container\n" - " --overlay-ro=PATH[:PATH...]:PATH\n" - " Similar, but creates a read-only overlay mount\n" - " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" - " --share-system Share system namespaces with host\n" - " --register=BOOLEAN Register container as machine\n" - " --keep-unit Do not register a scope for the machine, reuse\n" - " the service unit nspawn is running in\n" - " --volatile[=MODE] Run the system in volatile mode\n" - " --settings=BOOLEAN Load additional settings from .nspawn file\n" - " --notify-ready=BOOLEAN Receive notifications from the container's init process,\n" - " accepted values: yes and no\n" - , program_invocation_short_name); -} - -static int custom_mounts_prepare(void) { - unsigned i; - int r; - - /* Ensure the mounts are applied prefix first. */ - qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare); - - /* Allocate working directories for the overlay file systems that need it */ - for (i = 0; i < arg_n_custom_mounts; i++) { - CustomMount *m = &arg_custom_mounts[i]; - - if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) { - - if (arg_userns_chown) { - log_error("--private-users-chown may not be combined with custom root mounts."); - return -EINVAL; - } else if (arg_uid_shift == UID_INVALID) { - log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); - return -EINVAL; - } - } - - if (m->type != CUSTOM_MOUNT_OVERLAY) - continue; - - if (m->work_dir) - continue; - - if (m->read_only) - continue; - - r = tempfn_random(m->source, NULL, &m->work_dir); - if (r < 0) - return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source); - } - - return 0; -} - -static int detect_unified_cgroup_hierarchy(void) { - const char *e; - int r; - - /* Allow the user to control whether the unified hierarchy is used */ - e = getenv("UNIFIED_CGROUP_HIERARCHY"); - if (e) { - r = parse_boolean(e); - if (r < 0) - return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY."); - - arg_unified_cgroup_hierarchy = r; - return 0; - } - - /* Otherwise inherit the default from the host system */ - r = cg_unified(); - if (r < 0) - return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); - - arg_unified_cgroup_hierarchy = r; - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_PRIVATE_NETWORK, - ARG_UUID, - ARG_READ_ONLY, - ARG_CAPABILITY, - ARG_DROP_CAPABILITY, - ARG_LINK_JOURNAL, - ARG_BIND, - ARG_BIND_RO, - ARG_TMPFS, - ARG_OVERLAY, - ARG_OVERLAY_RO, - ARG_SHARE_SYSTEM, - ARG_REGISTER, - ARG_KEEP_UNIT, - ARG_NETWORK_INTERFACE, - ARG_NETWORK_MACVLAN, - ARG_NETWORK_IPVLAN, - ARG_NETWORK_BRIDGE, - ARG_NETWORK_ZONE, - ARG_NETWORK_VETH_EXTRA, - ARG_PERSONALITY, - ARG_VOLATILE, - ARG_TEMPLATE, - ARG_PROPERTY, - ARG_PRIVATE_USERS, - ARG_KILL_SIGNAL, - ARG_SETTINGS, - ARG_CHDIR, - ARG_PRIVATE_USERS_CHOWN, - ARG_NOTIFY_READY, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "directory", required_argument, NULL, 'D' }, - { "template", required_argument, NULL, ARG_TEMPLATE }, - { "ephemeral", no_argument, NULL, 'x' }, - { "user", required_argument, NULL, 'u' }, - { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, - { "as-pid2", no_argument, NULL, 'a' }, - { "boot", no_argument, NULL, 'b' }, - { "uuid", required_argument, NULL, ARG_UUID }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - { "capability", required_argument, NULL, ARG_CAPABILITY }, - { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, - { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, - { "bind", required_argument, NULL, ARG_BIND }, - { "bind-ro", required_argument, NULL, ARG_BIND_RO }, - { "tmpfs", required_argument, NULL, ARG_TMPFS }, - { "overlay", required_argument, NULL, ARG_OVERLAY }, - { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, - { "machine", required_argument, NULL, 'M' }, - { "slice", required_argument, NULL, 'S' }, - { "setenv", required_argument, NULL, 'E' }, - { "selinux-context", required_argument, NULL, 'Z' }, - { "selinux-apifs-context", required_argument, NULL, 'L' }, - { "quiet", no_argument, NULL, 'q' }, - { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, - { "register", required_argument, NULL, ARG_REGISTER }, - { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, - { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, - { "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 }, - { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, - { "personality", required_argument, NULL, ARG_PERSONALITY }, - { "image", required_argument, NULL, 'i' }, - { "volatile", optional_argument, NULL, ARG_VOLATILE }, - { "port", required_argument, NULL, 'p' }, - { "property", required_argument, NULL, ARG_PROPERTY }, - { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, - { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN}, - { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, - { "settings", required_argument, NULL, ARG_SETTINGS }, - { "chdir", required_argument, NULL, ARG_CHDIR }, - { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, - {} - }; - - int c, r; - const char *p, *e; - uint64_t plus = 0, minus = 0; - bool mask_all_settings = false, mask_no_settings = false; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'D': - r = parse_path_argument_and_warn(optarg, false, &arg_directory); - if (r < 0) - return r; - break; - - case ARG_TEMPLATE: - r = parse_path_argument_and_warn(optarg, false, &arg_template); - if (r < 0) - return r; - break; - - case 'i': - r = parse_path_argument_and_warn(optarg, false, &arg_image); - if (r < 0) - return r; - break; - - case 'x': - arg_ephemeral = true; - break; - - case 'u': - r = free_and_strdup(&arg_user, optarg); - if (r < 0) - return log_oom(); - - arg_settings_mask |= SETTING_USER; - break; - - case ARG_NETWORK_ZONE: { - char *j; - - j = strappend("vz-", optarg); - if (!j) - return log_oom(); - - if (!ifname_valid(j)) { - log_error("Network zone name not valid: %s", j); - free(j); - return -EINVAL; - } - - free(arg_network_zone); - arg_network_zone = j; - - arg_network_veth = true; - arg_private_network = true; - arg_settings_mask |= SETTING_NETWORK; - break; - } - - case ARG_NETWORK_BRIDGE: - - if (!ifname_valid(optarg)) { - log_error("Bridge interface name not valid: %s", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_network_bridge, optarg); - if (r < 0) - return log_oom(); - - /* fall through */ - - case 'n': - arg_network_veth = true; - arg_private_network = true; - 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 (!ifname_valid(optarg)) { - log_error("Network interface name not valid: %s", optarg); - return -EINVAL; - } - - if (strv_extend(&arg_network_interfaces, optarg) < 0) - return log_oom(); - - arg_private_network = true; - arg_settings_mask |= SETTING_NETWORK; - break; - - case ARG_NETWORK_MACVLAN: - - if (!ifname_valid(optarg)) { - log_error("MACVLAN network interface name not valid: %s", optarg); - return -EINVAL; - } - - if (strv_extend(&arg_network_macvlan, optarg) < 0) - return log_oom(); - - arg_private_network = true; - arg_settings_mask |= SETTING_NETWORK; - break; - - case ARG_NETWORK_IPVLAN: - - if (!ifname_valid(optarg)) { - log_error("IPVLAN network interface name not valid: %s", optarg); - return -EINVAL; - } - - if (strv_extend(&arg_network_ipvlan, optarg) < 0) - return log_oom(); - - /* fall through */ - - case ARG_PRIVATE_NETWORK: - arg_private_network = true; - arg_settings_mask |= SETTING_NETWORK; - break; - - case 'b': - if (arg_start_mode == START_PID2) { - log_error("--boot and --as-pid2 may not be combined."); - return -EINVAL; - } - - arg_start_mode = START_BOOT; - arg_settings_mask |= SETTING_START_MODE; - break; - - case 'a': - if (arg_start_mode == START_BOOT) { - log_error("--boot and --as-pid2 may not be combined."); - return -EINVAL; - } - - arg_start_mode = START_PID2; - arg_settings_mask |= SETTING_START_MODE; - break; - - case ARG_UUID: - r = sd_id128_from_string(optarg, &arg_uuid); - if (r < 0) - return log_error_errno(r, "Invalid UUID: %s", optarg); - - if (sd_id128_is_null(arg_uuid)) { - log_error("Machine UUID may not be all zeroes."); - return -EINVAL; - } - - arg_settings_mask |= SETTING_MACHINE_ID; - break; - - case 'S': - arg_slice = optarg; - break; - - case 'M': - if (isempty(optarg)) - arg_machine = mfree(arg_machine); - else { - if (!machine_name_is_valid(optarg)) { - log_error("Invalid machine name: %s", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_machine, optarg); - if (r < 0) - return log_oom(); - - break; - } - - case 'Z': - arg_selinux_context = optarg; - break; - - case 'L': - arg_selinux_apifs_context = optarg; - break; - - case ARG_READ_ONLY: - arg_read_only = true; - arg_settings_mask |= SETTING_READ_ONLY; - break; - - case ARG_CAPABILITY: - case ARG_DROP_CAPABILITY: { - p = optarg; - for (;;) { - _cleanup_free_ char *t = NULL; - - r = extract_first_word(&p, &t, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse capability %s.", t); - - if (r == 0) - break; - - if (streq(t, "all")) { - if (c == ARG_CAPABILITY) - plus = (uint64_t) -1; - else - minus = (uint64_t) -1; - } else { - int cap; - - cap = capability_from_name(t); - if (cap < 0) { - log_error("Failed to parse capability %s.", t); - return -EINVAL; - } - - if (c == ARG_CAPABILITY) - plus |= 1ULL << (uint64_t) cap; - else - minus |= 1ULL << (uint64_t) cap; - } - } - - arg_settings_mask |= SETTING_CAPABILITY; - break; - } - - case 'j': - arg_link_journal = LINK_GUEST; - arg_link_journal_try = true; - break; - - case ARG_LINK_JOURNAL: - if (streq(optarg, "auto")) { - arg_link_journal = LINK_AUTO; - arg_link_journal_try = false; - } else if (streq(optarg, "no")) { - arg_link_journal = LINK_NO; - arg_link_journal_try = false; - } else if (streq(optarg, "guest")) { - arg_link_journal = LINK_GUEST; - arg_link_journal_try = false; - } else if (streq(optarg, "host")) { - arg_link_journal = LINK_HOST; - arg_link_journal_try = false; - } else if (streq(optarg, "try-guest")) { - arg_link_journal = LINK_GUEST; - arg_link_journal_try = true; - } else if (streq(optarg, "try-host")) { - arg_link_journal = LINK_HOST; - arg_link_journal_try = true; - } else { - log_error("Failed to parse link journal mode %s", optarg); - return -EINVAL; - } - - break; - - case ARG_BIND: - case ARG_BIND_RO: - r = bind_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_BIND_RO); - if (r < 0) - return log_error_errno(r, "Failed to parse --bind(-ro)= argument %s: %m", optarg); - - arg_settings_mask |= SETTING_CUSTOM_MOUNTS; - break; - - case ARG_TMPFS: - r = tmpfs_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --tmpfs= argument %s: %m", optarg); - - arg_settings_mask |= SETTING_CUSTOM_MOUNTS; - break; - - case ARG_OVERLAY: - case ARG_OVERLAY_RO: { - _cleanup_free_ char *upper = NULL, *destination = NULL; - _cleanup_strv_free_ char **lower = NULL; - CustomMount *m; - unsigned n = 0; - char **i; - - r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - else if (r < 0) { - log_error("Invalid overlay specification: %s", optarg); - return r; - } - - STRV_FOREACH(i, lower) { - if (!path_is_absolute(*i)) { - log_error("Overlay path %s is not absolute.", *i); - return -EINVAL; - } - - n++; - } - - if (n < 2) { - log_error("--overlay= needs at least two colon-separated directories specified."); - return -EINVAL; - } - - if (n == 2) { - /* If two parameters are specified, - * the first one is the lower, the - * second one the upper directory. And - * we'll also define the destination - * mount point the same as the upper. */ - upper = lower[1]; - lower[1] = NULL; - - destination = strdup(upper); - if (!destination) - return log_oom(); - - } else { - upper = lower[n - 2]; - destination = lower[n - 1]; - lower[n - 2] = NULL; - } - - m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY); - if (!m) - return log_oom(); - - m->destination = destination; - m->source = upper; - m->lower = lower; - m->read_only = c == ARG_OVERLAY_RO; - - upper = destination = NULL; - lower = NULL; - - arg_settings_mask |= SETTING_CUSTOM_MOUNTS; - break; - } - - case 'E': { - char **n; - - if (!env_assignment_is_valid(optarg)) { - log_error("Environment variable assignment '%s' is not valid.", optarg); - return -EINVAL; - } - - n = strv_env_set(arg_setenv, optarg); - if (!n) - return log_oom(); - - strv_free(arg_setenv); - arg_setenv = n; - - arg_settings_mask |= SETTING_ENVIRONMENT; - break; - } - - case 'q': - arg_quiet = true; - break; - - case ARG_SHARE_SYSTEM: - arg_share_system = true; - break; - - case ARG_REGISTER: - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --register= argument: %s", optarg); - return r; - } - - arg_register = r; - break; - - case ARG_KEEP_UNIT: - arg_keep_unit = true; - break; - - case ARG_PERSONALITY: - - arg_personality = personality_from_string(optarg); - if (arg_personality == PERSONALITY_INVALID) { - log_error("Unknown or unsupported personality '%s'.", optarg); - return -EINVAL; - } - - arg_settings_mask |= SETTING_PERSONALITY; - break; - - case ARG_VOLATILE: - - if (!optarg) - arg_volatile_mode = VOLATILE_YES; - else { - VolatileMode m; - - m = volatile_mode_from_string(optarg); - if (m < 0) { - log_error("Failed to parse --volatile= argument: %s", optarg); - return -EINVAL; - } else - arg_volatile_mode = m; - } - - arg_settings_mask |= SETTING_VOLATILE_MODE; - break; - - case 'p': - r = expose_port_parse(&arg_expose_ports, optarg); - if (r == -EEXIST) - return log_error_errno(r, "Duplicate port specification: %s", optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse host port %s: %m", optarg); - - arg_settings_mask |= SETTING_EXPOSE_PORTS; - break; - - case ARG_PROPERTY: - if (strv_extend(&arg_property, optarg) < 0) - return log_oom(); - - break; - - case ARG_PRIVATE_USERS: - - r = optarg ? parse_boolean(optarg) : 1; - if (r == 0) { - /* no: User namespacing off */ - arg_userns_mode = USER_NAMESPACE_NO; - arg_uid_shift = UID_INVALID; - arg_uid_range = UINT32_C(0x10000); - } else if (r > 0) { - /* yes: User namespacing on, UID range is read from root dir */ - arg_userns_mode = USER_NAMESPACE_FIXED; - arg_uid_shift = UID_INVALID; - arg_uid_range = UINT32_C(0x10000); - } else if (streq(optarg, "pick")) { - /* pick: User namespacing on, UID range is picked randomly */ - arg_userns_mode = USER_NAMESPACE_PICK; - arg_uid_shift = UID_INVALID; - arg_uid_range = UINT32_C(0x10000); - } else { - _cleanup_free_ char *buffer = NULL; - const char *range, *shift; - - /* anything else: User namespacing on, UID range is explicitly configured */ - - range = strchr(optarg, ':'); - if (range) { - buffer = strndup(optarg, range - optarg); - if (!buffer) - return log_oom(); - shift = buffer; - - range++; - if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) { - log_error("Failed to parse UID range: %s", range); - return -EINVAL; - } - } else - shift = optarg; - - if (parse_uid(shift, &arg_uid_shift) < 0) { - log_error("Failed to parse UID: %s", optarg); - return -EINVAL; - } - - arg_userns_mode = USER_NAMESPACE_FIXED; - } - - arg_settings_mask |= SETTING_USERNS; - break; - - case 'U': - if (userns_supported()) { - arg_userns_mode = USER_NAMESPACE_PICK; - arg_uid_shift = UID_INVALID; - arg_uid_range = UINT32_C(0x10000); - - arg_settings_mask |= SETTING_USERNS; - } - - break; - - case ARG_PRIVATE_USERS_CHOWN: - arg_userns_chown = true; - - arg_settings_mask |= SETTING_USERNS; - break; - - case ARG_KILL_SIGNAL: - arg_kill_signal = signal_from_string_try_harder(optarg); - if (arg_kill_signal < 0) { - log_error("Cannot parse signal: %s", optarg); - return -EINVAL; - } - - arg_settings_mask |= SETTING_KILL_SIGNAL; - break; - - case ARG_SETTINGS: - - /* no → do not read files - * yes → read files, do not override cmdline, trust only subset - * override → read files, override cmdline, trust only subset - * trusted → read files, do not override cmdline, trust all - */ - - r = parse_boolean(optarg); - if (r < 0) { - if (streq(optarg, "trusted")) { - mask_all_settings = false; - mask_no_settings = false; - arg_settings_trusted = true; - - } else if (streq(optarg, "override")) { - mask_all_settings = false; - mask_no_settings = true; - arg_settings_trusted = -1; - } else - return log_error_errno(r, "Failed to parse --settings= argument: %s", optarg); - } else if (r > 0) { - /* yes */ - mask_all_settings = false; - mask_no_settings = false; - arg_settings_trusted = -1; - } else { - /* no */ - mask_all_settings = true; - mask_no_settings = false; - arg_settings_trusted = false; - } - - break; - - case ARG_CHDIR: - if (!path_is_absolute(optarg)) { - log_error("Working directory %s is not an absolute path.", optarg); - return -EINVAL; - } - - r = free_and_strdup(&arg_chdir, optarg); - if (r < 0) - return log_oom(); - - arg_settings_mask |= SETTING_WORKING_DIRECTORY; - break; - - case ARG_NOTIFY_READY: - r = parse_boolean(optarg); - if (r < 0) { - log_error("%s is not a valid notify mode. Valid modes are: yes, no, and ready.", optarg); - return -EINVAL; - } - arg_notify_ready = r; - arg_settings_mask |= SETTING_NOTIFY_READY; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_share_system) - arg_register = false; - - if (arg_userns_mode == USER_NAMESPACE_PICK) - arg_userns_chown = true; - - if (arg_start_mode != START_PID1 && arg_share_system) { - log_error("--boot and --share-system may not be combined."); - return -EINVAL; - } - - if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) { - log_error("--keep-unit may not be used when invoked from a user session."); - return -EINVAL; - } - - if (arg_directory && arg_image) { - log_error("--directory= and --image= may not be combined."); - return -EINVAL; - } - - if (arg_template && arg_image) { - log_error("--template= and --image= may not be combined."); - return -EINVAL; - } - - if (arg_template && !(arg_directory || arg_machine)) { - log_error("--template= needs --directory= or --machine=."); - return -EINVAL; - } - - if (arg_ephemeral && arg_template) { - log_error("--ephemeral and --template= may not be combined."); - return -EINVAL; - } - - if (arg_ephemeral && arg_image) { - log_error("--ephemeral and --image= may not be combined."); - return -EINVAL; - } - - if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO)) { - log_error("--ephemeral and --link-journal= may not be combined."); - return -EINVAL; - } - - if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) { - log_error("--private-users= is not supported, kernel compiled without user namespace support."); - return -EOPNOTSUPP; - } - - if (arg_userns_chown && arg_read_only) { - log_error("--read-only and --private-users-chown may not be combined."); - return -EINVAL; - } - - if (arg_network_bridge && arg_network_zone) { - log_error("--network-bridge= and --network-zone= may not be combined."); - return -EINVAL; - } - - if (argc > optind) { - arg_parameters = strv_copy(argv + optind); - if (!arg_parameters) - return log_oom(); - - arg_settings_mask |= SETTING_START_MODE; - } - - /* Load all settings from .nspawn files */ - if (mask_no_settings) - arg_settings_mask = 0; - - /* Don't load any settings from .nspawn files */ - if (mask_all_settings) - arg_settings_mask = _SETTINGS_MASK_ALL; - - arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; - - r = detect_unified_cgroup_hierarchy(); - if (r < 0) - return r; - - e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE"); - if (e) - arg_container_service_name = e; - - return 1; -} - -static int verify_arguments(void) { - - if (arg_volatile_mode != VOLATILE_NO && arg_read_only) { - log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy."); - return -EINVAL; - } - - if (arg_expose_ports && !arg_private_network) { - log_error("Cannot use --port= without private networking."); - return -EINVAL; - } - -#ifndef HAVE_LIBIPTC - if (arg_expose_ports) { - log_error("--port= is not supported, compiled without libiptc support."); - return -EOPNOTSUPP; - } -#endif - - if (arg_start_mode == START_BOOT && arg_kill_signal <= 0) - arg_kill_signal = SIGRTMIN+3; - - return 0; -} - -static int userns_lchown(const char *p, uid_t uid, gid_t gid) { - assert(p); - - if (arg_userns_mode == USER_NAMESPACE_NO) - return 0; - - if (uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (uid != UID_INVALID) { - uid += arg_uid_shift; - - if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range) - return -EOVERFLOW; - } - - if (gid != GID_INVALID) { - gid += (gid_t) arg_uid_shift; - - if (gid < (gid_t) arg_uid_shift || gid >= (gid_t) (arg_uid_shift + arg_uid_range)) - return -EOVERFLOW; - } - - if (lchown(p, uid, gid) < 0) - return -errno; - - return 0; -} - -static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) { - const char *q; - - q = prefix_roota(root, path); - if (mkdir(q, mode) < 0) { - if (errno == EEXIST) - return 0; - return -errno; - } - - return userns_lchown(q, uid, gid); -} - -static int setup_timezone(const char *dest) { - _cleanup_free_ char *p = NULL, *q = NULL; - const char *where, *check, *what; - char *z, *y; - int r; - - assert(dest); - - /* Fix the timezone, if possible */ - r = readlink_malloc("/etc/localtime", &p); - if (r < 0) { - log_warning("/etc/localtime is not a symlink, not updating container timezone."); - return 0; - } - - z = path_startswith(p, "../usr/share/zoneinfo/"); - if (!z) - z = path_startswith(p, "/usr/share/zoneinfo/"); - if (!z) { - log_warning("/etc/localtime does not point into /usr/share/zoneinfo/, not updating container timezone."); - return 0; - } - - where = prefix_roota(dest, "/etc/localtime"); - r = readlink_malloc(where, &q); - if (r >= 0) { - y = path_startswith(q, "../usr/share/zoneinfo/"); - if (!y) - y = path_startswith(q, "/usr/share/zoneinfo/"); - - /* Already pointing to the right place? Then do nothing .. */ - if (y && streq(y, z)) - return 0; - } - - check = strjoina("/usr/share/zoneinfo/", z); - check = prefix_roota(dest, check); - if (laccess(check, F_OK) < 0) { - log_warning("Timezone %s does not exist in container, not updating container timezone.", z); - return 0; - } - - r = unlink(where); - if (r < 0 && errno != ENOENT) { - log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where); - return 0; - } - - what = strjoina("../usr/share/zoneinfo/", z); - if (symlink(what, where) < 0) { - log_error_errno(errno, "Failed to correct timezone of container: %m"); - return 0; - } - - r = userns_lchown(where, 0, 0); - if (r < 0) - return log_warning_errno(r, "Failed to chown /etc/localtime: %m"); - - return 0; -} - -static int setup_resolv_conf(const char *dest) { - const char *where = NULL; - int r; - - assert(dest); - - if (arg_private_network) - return 0; - - /* Fix resolv.conf, if possible */ - where = prefix_roota(dest, "/etc/resolv.conf"); - - r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0); - if (r < 0) { - /* If the file already exists as symlink, let's - * suppress the warning, under the assumption that - * resolved or something similar runs inside and the - * symlink points there. - * - * If the disk image is read-only, there's also no - * point in complaining. - */ - log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to copy /etc/resolv.conf to %s: %m", where); - return 0; - } - - r = userns_lchown(where, 0, 0); - if (r < 0) - log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m"); - - return 0; -} - -static int setup_boot_id(const char *dest) { - sd_id128_t rnd = SD_ID128_NULL; - const char *from, *to; - int r; - - if (arg_share_system) - return 0; - - /* Generate a new randomized boot ID, so that each boot-up of - * the container gets a new one */ - - from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id"); - to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id"); - - r = sd_id128_randomize(&rnd); - if (r < 0) - return log_error_errno(r, "Failed to generate random boot id: %m"); - - r = id128_write(from, ID128_UUID, rnd, false); - if (r < 0) - return log_error_errno(r, "Failed to write boot id: %m"); - - if (mount(from, to, NULL, MS_BIND, NULL) < 0) - r = log_error_errno(errno, "Failed to bind mount boot id: %m"); - else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) - log_warning_errno(errno, "Failed to make boot id read-only, ignoring: %m"); - - (void) unlink(from); - return r; -} - -static int copy_devnodes(const char *dest) { - - static const char devnodes[] = - "null\0" - "zero\0" - "full\0" - "random\0" - "urandom\0" - "tty\0" - "net/tun\0"; - - const char *d; - int r = 0; - _cleanup_umask_ mode_t u; - - assert(dest); - - u = umask(0000); - - /* Create /dev/net, so that we can create /dev/net/tun in it */ - if (userns_mkdir(dest, "/dev/net", 0755, 0, 0) < 0) - return log_error_errno(r, "Failed to create /dev/net directory: %m"); - - NULSTR_FOREACH(d, devnodes) { - _cleanup_free_ char *from = NULL, *to = NULL; - struct stat st; - - from = strappend("/dev/", d); - to = prefix_root(dest, from); - - if (stat(from, &st) < 0) { - - if (errno != ENOENT) - return log_error_errno(errno, "Failed to stat %s: %m", from); - - } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { - - log_error("%s is not a char or block device, cannot copy.", from); - return -EIO; - - } else { - if (mknod(to, st.st_mode, st.st_rdev) < 0) { - if (errno != EPERM) - return log_error_errno(errno, "mknod(%s) failed: %m", to); - - /* Some systems abusively restrict mknod but - * allow bind mounts. */ - r = touch(to); - if (r < 0) - return log_error_errno(r, "touch (%s) failed: %m", to); - if (mount(from, to, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Both mknod and bind mount (%s) failed: %m", to); - } - - r = userns_lchown(to, 0, 0); - if (r < 0) - return log_error_errno(r, "chown() of device node %s failed: %m", to); - } - } - - return r; -} - -static int setup_pts(const char *dest) { - _cleanup_free_ char *options = NULL; - const char *p; - int r; - -#ifdef HAVE_SELINUX - if (arg_selinux_apifs_context) - (void) asprintf(&options, - "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT ",context=\"%s\"", - arg_uid_shift + TTY_GID, - arg_selinux_apifs_context); - else -#endif - (void) asprintf(&options, - "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT, - arg_uid_shift + TTY_GID); - - if (!options) - return log_oom(); - - /* Mount /dev/pts itself */ - p = prefix_roota(dest, "/dev/pts"); - if (mkdir(p, 0755) < 0) - return log_error_errno(errno, "Failed to create /dev/pts: %m"); - if (mount("devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options) < 0) - return log_error_errno(errno, "Failed to mount /dev/pts: %m"); - 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"); - 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"); - r = userns_lchown(p, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to chown /dev/pts/ptmx: %m"); - - return 0; -} - -static int setup_dev_console(const char *dest, const char *console) { - _cleanup_umask_ mode_t u; - const char *to; - int r; - - assert(dest); - assert(console); - - u = umask(0000); - - r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift); - if (r < 0) - return log_error_errno(r, "Failed to correct access mode for TTY: %m"); - - /* We need to bind mount the right tty to /dev/console since - * ptys can only exist on pts file systems. To have something - * to bind mount things on we create a empty regular file. */ - - to = prefix_roota(dest, "/dev/console"); - r = touch(to); - if (r < 0) - return log_error_errno(r, "touch() for /dev/console failed: %m"); - - if (mount(console, to, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Bind mount for /dev/console failed: %m"); - - return 0; -} - -static int setup_kmsg(const char *dest, int kmsg_socket) { - const char *from, *to; - _cleanup_umask_ mode_t u; - int fd, r; - - assert(kmsg_socket >= 0); - - u = umask(0000); - - /* We create the kmsg FIFO as /run/kmsg, but immediately - * delete it after bind mounting it to /proc/kmsg. While FIFOs - * on the reading side behave very similar to /proc/kmsg, - * their writing side behaves differently from /dev/kmsg in - * that writing blocks when nothing is reading. In order to - * avoid any problems with containers deadlocking due to this - * we simply make /dev/kmsg unavailable to the container. */ - from = prefix_roota(dest, "/run/kmsg"); - to = prefix_roota(dest, "/proc/kmsg"); - - if (mkfifo(from, 0600) < 0) - return log_error_errno(errno, "mkfifo() for /run/kmsg failed: %m"); - if (mount(from, to, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Bind mount for /proc/kmsg failed: %m"); - - fd = open(from, O_RDWR|O_NDELAY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open fifo: %m"); - - /* 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, 0); - safe_close(fd); - - if (r < 0) - return log_error_errno(r, "Failed to send FIFO fd: %m"); - - /* And now make the FIFO unavailable as /run/kmsg... */ - (void) unlink(from); - - return 0; -} - -static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - union in_addr_union *exposed = userdata; - - assert(rtnl); - assert(m); - assert(exposed); - - expose_port_execute(rtnl, arg_expose_ports, exposed); - return 0; -} - -static int setup_hostname(void) { - - if (arg_share_system) - return 0; - - if (sethostname_idempotent(arg_machine) < 0) - return -errno; - - return 0; -} - -static int setup_journal(const char *directory) { - sd_id128_t this_id; - _cleanup_free_ char *d = NULL; - const char *p, *q; - bool try; - char id[33]; - int r; - - /* Don't link journals in ephemeral mode */ - if (arg_ephemeral) - return 0; - - if (arg_link_journal == LINK_NO) - return 0; - - try = arg_link_journal_try || arg_link_journal == LINK_AUTO; - - r = sd_id128_get_machine(&this_id); - if (r < 0) - return log_error_errno(r, "Failed to retrieve machine ID: %m"); - - if (sd_id128_equal(arg_uuid, this_id)) { - log_full(try ? LOG_WARNING : LOG_ERR, - "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id)); - if (try) - return 0; - return -EEXIST; - } - - r = userns_mkdir(directory, "/var", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /var: %m"); - - r = userns_mkdir(directory, "/var/log", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /var/log: %m"); - - r = userns_mkdir(directory, "/var/log/journal", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /var/log/journal: %m"); - - (void) sd_id128_to_string(arg_uuid, id); - - p = strjoina("/var/log/journal/", id); - q = prefix_roota(directory, p); - - if (path_is_mount_point(p, 0) > 0) { - if (try) - return 0; - - log_error("%s: already a mount point, refusing to use for journal", p); - return -EEXIST; - } - - if (path_is_mount_point(q, 0) > 0) { - if (try) - return 0; - - log_error("%s: already a mount point, refusing to use for journal", q); - return -EEXIST; - } - - r = readlink_and_make_absolute(p, &d); - if (r >= 0) { - if ((arg_link_journal == LINK_GUEST || - arg_link_journal == LINK_AUTO) && - path_equal(d, q)) { - - r = userns_mkdir(directory, p, 0755, 0, 0); - if (r < 0) - log_warning_errno(r, "Failed to create directory %s: %m", q); - return 0; - } - - if (unlink(p) < 0) - return log_error_errno(errno, "Failed to remove symlink %s: %m", p); - } else if (r == -EINVAL) { - - if (arg_link_journal == LINK_GUEST && - rmdir(p) < 0) { - - if (errno == ENOTDIR) { - log_error("%s already exists and is neither a symlink nor a directory", p); - return r; - } else - return log_error_errno(errno, "Failed to remove %s: %m", p); - } - } else if (r != -ENOENT) - return log_error_errno(r, "readlink(%s) failed: %m", p); - - if (arg_link_journal == LINK_GUEST) { - - if (symlink(q, p) < 0) { - if (try) { - log_debug_errno(errno, "Failed to symlink %s to %s, skipping journal setup: %m", q, p); - return 0; - } 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(r, "Failed to create directory %s: %m", q); - return 0; - } - - if (arg_link_journal == LINK_HOST) { - /* don't create parents here — if the host doesn't have - * permanent journal set up, don't force it here */ - - if (mkdir(p, 0755) < 0 && errno != EEXIST) { - if (try) { - log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p); - return 0; - } else - return log_error_errno(errno, "Failed to create %s: %m", p); - } - - } else if (access(p, F_OK) < 0) - return 0; - - if (dir_is_empty(q) == 0) - log_warning("%s is not empty, proceeding anyway.", q); - - r = userns_mkdir(directory, p, 0755, 0, 0); - 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"); - - return 0; -} - -static int drop_capabilities(void) { - return capability_bounding_set_drop(arg_caps_retain, false); -} - -static int reset_audit_loginuid(void) { - _cleanup_free_ char *p = NULL; - int r; - - if (arg_share_system) - return 0; - - r = read_one_line_file("/proc/self/loginuid", &p); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_error_errno(r, "Failed to read /proc/self/loginuid: %m"); - - /* Already reset? */ - if (streq(p, "4294967295")) - return 0; - - r = write_string_file("/proc/self/loginuid", "4294967295", 0); - if (r < 0) { - log_error_errno(r, - "Failed to reset audit login UID. This probably means that your kernel is too\n" - "old and you have audit enabled. Note that the auditing subsystem is known to\n" - "be incompatible with containers on old kernels. Please make sure to upgrade\n" - "your kernel or to off auditing with 'audit=0' on the kernel command line before\n" - "using systemd-nspawn. Sleeping for 5s... (%m)"); - - sleep(5); - } - - return 0; -} - - -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); - - r = userns_mkdir(root, "/run/systemd", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create /run/systemd: %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"); - - 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) - return log_error_errno(errno, "Failed to install propagation bind mount."); - - if (mount(NULL, q, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) - return log_error_errno(errno, "Failed to make propagation mount read-only"); - - return 0; -} - -static int setup_image(char **device_path, int *loop_nr) { - struct loop_info64 info = { - .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN - }; - _cleanup_close_ int fd = -1, control = -1, loop = -1; - _cleanup_free_ char* loopdev = NULL; - struct stat st; - int r, nr; - - assert(device_path); - assert(loop_nr); - assert(arg_image); - - fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", arg_image); - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Failed to stat %s: %m", arg_image); - - if (S_ISBLK(st.st_mode)) { - char *p; - - p = strdup(arg_image); - if (!p) - return log_oom(); - - *device_path = p; - - *loop_nr = -1; - - r = fd; - fd = -1; - - return r; - } - - if (!S_ISREG(st.st_mode)) { - log_error("%s is not a regular file or block device.", arg_image); - return -EINVAL; - } - - control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (control < 0) - return log_error_errno(errno, "Failed to open /dev/loop-control: %m"); - - nr = ioctl(control, LOOP_CTL_GET_FREE); - if (nr < 0) - return log_error_errno(errno, "Failed to allocate loop device: %m"); - - if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) - return log_oom(); - - loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); - if (loop < 0) - return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev); - - if (ioctl(loop, LOOP_SET_FD, fd) < 0) - return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev); - - if (arg_read_only) - info.lo_flags |= LO_FLAGS_READ_ONLY; - - if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) - return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev); - - *device_path = loopdev; - loopdev = NULL; - - *loop_nr = nr; - - r = loop; - loop = -1; - - return r; -} - -#define PARTITION_TABLE_BLURB \ - "Note that the disk image needs to either contain only a single MBR partition of\n" \ - "type 0x83 that is marked bootable, or a single GPT partition of type " \ - "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \ - " http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \ - "to be bootable with systemd-nspawn." - -static int dissect_image( - int fd, - char **root_device, bool *root_device_rw, - char **home_device, bool *home_device_rw, - char **srv_device, bool *srv_device_rw, - bool *secondary) { - -#ifdef HAVE_BLKID - int home_nr = -1, srv_nr = -1; -#ifdef GPT_ROOT_NATIVE - int root_nr = -1; -#endif -#ifdef GPT_ROOT_SECONDARY - int secondary_root_nr = -1; -#endif - _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL; - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - _cleanup_udev_unref_ struct udev *udev = NULL; - struct udev_list_entry *first, *item; - bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true; - bool is_gpt, is_mbr, multiple_generic = false; - const char *pttype = NULL; - blkid_partlist pl; - struct stat st; - unsigned i; - int r; - - assert(fd >= 0); - assert(root_device); - assert(home_device); - assert(srv_device); - assert(secondary); - assert(arg_image); - - b = blkid_new_probe(); - if (!b) - return log_oom(); - - errno = 0; - r = blkid_probe_set_device(b, fd, 0, 0); - if (r != 0) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "Failed to set device on blkid probe: %m"); - } - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) { - log_error("Failed to identify any partition table on\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } else if (r != 0) { - if (errno == 0) - errno = EIO; - return log_error_errno(errno, "Failed to probe: %m"); - } - - (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); - - is_gpt = streq_ptr(pttype, "gpt"); - is_mbr = streq_ptr(pttype, "dos"); - - if (!is_gpt && !is_mbr) { - log_error("No GPT or MBR partition table discovered on\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } - - errno = 0; - pl = blkid_probe_get_partitions(b); - if (!pl) { - if (errno == 0) - return log_oom(); - - log_error("Failed to list partitions of %s", arg_image); - return -errno; - } - - udev = udev_new(); - if (!udev) - return log_oom(); - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Failed to stat block device: %m"); - - d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); - if (!d) - return log_oom(); - - for (i = 0;; i++) { - int n, m; - - if (i >= 10) { - log_error("Kernel partitions never appeared."); - return -ENXIO; - } - - e = udev_enumerate_new(udev); - if (!e) - return log_oom(); - - r = udev_enumerate_add_match_parent(e, d); - if (r < 0) - return log_oom(); - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image); - - /* Count the partitions enumerated by the kernel */ - n = 0; - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) - n++; - - /* Count the partitions enumerated by blkid */ - m = blkid_partlist_numof_partitions(pl); - if (n == m + 1) - break; - if (n > m + 1) { - log_error("blkid and kernel partition list do not match."); - return -EIO; - } - if (n < m + 1) { - unsigned j; - - /* The kernel has probed fewer partitions than - * blkid? Maybe the kernel prober is still - * running or it got EBUSY because udev - * already opened the device. Let's reprobe - * the device, which is a synchronous call - * that waits until probing is complete. */ - - for (j = 0; j < 20; j++) { - - r = ioctl(fd, BLKRRPART, 0); - if (r < 0) - r = -errno; - if (r >= 0 || r != -EBUSY) - break; - - /* If something else has the device - * open, such as an udev rule, the - * ioctl will return EBUSY. Since - * there's no way to wait until it - * isn't busy anymore, let's just wait - * a bit, and try again. - * - * This is really something they - * should fix in the kernel! */ - - usleep(50 * USEC_PER_MSEC); - } - - if (r < 0) - return log_error_errno(r, "Failed to reread partition table: %m"); - } - - e = udev_enumerate_unref(e); - } - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *q; - const char *node; - unsigned long long flags; - blkid_partition pp; - dev_t qn; - int nr; - - errno = 0; - q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!q) { - if (!errno) - errno = ENOMEM; - - return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image); - } - - qn = udev_device_get_devnum(q); - if (major(qn) == 0) - continue; - - if (st.st_rdev == qn) - continue; - - node = udev_device_get_devnode(q); - if (!node) - continue; - - pp = blkid_partlist_devno_to_partition(pl, qn); - if (!pp) - continue; - - flags = blkid_partition_get_flags(pp); - - nr = blkid_partition_get_partno(pp); - if (nr < 0) - continue; - - if (is_gpt) { - sd_id128_t type_id; - const char *stype; - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - stype = blkid_partition_get_type_string(pp); - if (!stype) - continue; - - if (sd_id128_from_string(stype, &type_id) < 0) - continue; - - if (sd_id128_equal(type_id, GPT_HOME)) { - - if (home && nr >= home_nr) - continue; - - home_nr = nr; - home_rw = !(flags & GPT_FLAG_READ_ONLY); - - r = free_and_strdup(&home, node); - if (r < 0) - return log_oom(); - - } else if (sd_id128_equal(type_id, GPT_SRV)) { - - if (srv && nr >= srv_nr) - continue; - - srv_nr = nr; - srv_rw = !(flags & GPT_FLAG_READ_ONLY); - - r = free_and_strdup(&srv, node); - if (r < 0) - return log_oom(); - } -#ifdef GPT_ROOT_NATIVE - else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { - - if (root && nr >= root_nr) - continue; - - root_nr = nr; - root_rw = !(flags & GPT_FLAG_READ_ONLY); - - r = free_and_strdup(&root, node); - if (r < 0) - return log_oom(); - } -#endif -#ifdef GPT_ROOT_SECONDARY - else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) { - - if (secondary_root && nr >= secondary_root_nr) - continue; - - secondary_root_nr = nr; - secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY); - - r = free_and_strdup(&secondary_root, node); - if (r < 0) - return log_oom(); - } -#endif - else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) { - - if (generic) - multiple_generic = true; - else { - generic_rw = !(flags & GPT_FLAG_READ_ONLY); - - r = free_and_strdup(&generic, node); - if (r < 0) - return log_oom(); - } - } - - } else if (is_mbr) { - int type; - - if (flags != 0x80) /* Bootable flag */ - continue; - - type = blkid_partition_get_type(pp); - if (type != 0x83) /* Linux partition */ - continue; - - if (generic) - multiple_generic = true; - else { - generic_rw = true; - - r = free_and_strdup(&root, node); - if (r < 0) - return log_oom(); - } - } - } - - if (root) { - *root_device = root; - root = NULL; - - *root_device_rw = root_rw; - *secondary = false; - } else if (secondary_root) { - *root_device = secondary_root; - secondary_root = NULL; - - *root_device_rw = secondary_root_rw; - *secondary = true; - } else if (generic) { - - /* There were no partitions with precise meanings - * around, but we found generic partitions. In this - * case, if there's only one, we can go ahead and boot - * it, otherwise we bail out, because we really cannot - * make any sense of it. */ - - if (multiple_generic) { - log_error("Identified multiple bootable Linux partitions on\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } - - *root_device = generic; - generic = NULL; - - *root_device_rw = generic_rw; - *secondary = false; - } else { - log_error("Failed to identify root partition in disk image\n" - " %s\n" - PARTITION_TABLE_BLURB, arg_image); - return -EINVAL; - } - - if (home) { - *home_device = home; - home = NULL; - - *home_device_rw = home_rw; - } - - if (srv) { - *srv_device = srv; - srv = NULL; - - *srv_device_rw = srv_rw; - } - - return 0; -#else - log_error("--image= is not supported, compiled without blkid support."); - return -EOPNOTSUPP; -#endif -} - -static int mount_device(const char *what, const char *where, const char *directory, bool rw) { -#ifdef HAVE_BLKID - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype, *p; - int r; - - assert(what); - assert(where); - - if (arg_read_only) - rw = false; - - if (directory) - p = strjoina(where, directory); - else - p = where; - - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober for %s: %m", what); - } - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -1 || r == 1) { - log_error("Cannot determine file system type of %s", what); - return -EINVAL; - } else if (r != 0) { - if (errno == 0) - errno = EIO; - return log_error_errno(errno, "Failed to probe %s: %m", what); - } - - errno = 0; - if (blkid_probe_lookup_value(b, "TYPE", &fstype, NULL) < 0) { - if (errno == 0) - errno = EINVAL; - log_error("Failed to determine file system type of %s", what); - return -errno; - } - - if (streq(fstype, "crypto_LUKS")) { - log_error("nspawn currently does not support LUKS disk images."); - return -EOPNOTSUPP; - } - - if (mount(what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL) < 0) - return log_error_errno(errno, "Failed to mount %s: %m", what); - - return 0; -#else - log_error("--image= is not supported, compiled without blkid support."); - return -EOPNOTSUPP; -#endif -} - -static int setup_machine_id(const char *directory) { - const char *etc_machine_id; - sd_id128_t id; - int r; - - /* If the UUID in the container is already set, then that's what counts, and we use. If it isn't set, and the - * caller passed --uuid=, then we'll pass it in the $container_uuid env var to PID 1 of the container. The - * assumption is that PID 1 will then write it to /etc/machine-id to make it persistent. If --uuid= is not - * passed we generate a random UUID, and pass it via $container_uuid. In effect this means that /etc/machine-id - * in the container and our idea of the container UUID will always be in sync (at least if PID 1 in the - * container behaves nicely). */ - - etc_machine_id = prefix_roota(directory, "/etc/machine-id"); - - r = id128_read(etc_machine_id, ID128_PLAIN, &id); - if (r < 0) { - if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */ - return log_error_errno(r, "Failed to read machine ID from container image: %m"); - - if (sd_id128_is_null(arg_uuid)) { - r = sd_id128_randomize(&arg_uuid); - if (r < 0) - return log_error_errno(r, "Failed to acquire randomized machine UUID: %m"); - } - } else { - if (sd_id128_is_null(id)) { - log_error("Machine ID in container image is zero, refusing."); - return -EINVAL; - } - - arg_uuid = id; - } - - return 0; -} - -static int recursive_chown(const char *directory, uid_t shift, uid_t range) { - int r; - - assert(directory); - - if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown) - return 0; - - r = path_patch_uid(directory, arg_uid_shift, arg_uid_range); - if (r == -EOPNOTSUPP) - return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16."); - if (r == -EBADE) - return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match."); - if (r < 0) - return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m"); - if (r == 0) - log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation."); - else - log_debug("Patched directory tree to match UID/GID range."); - - return r; -} - -static int mount_devices( - const char *where, - const char *root_device, bool root_device_rw, - const char *home_device, bool home_device_rw, - const char *srv_device, bool srv_device_rw) { - int r; - - assert(where); - - if (root_device) { - r = mount_device(root_device, arg_directory, NULL, root_device_rw); - if (r < 0) - return log_error_errno(r, "Failed to mount root directory: %m"); - } - - if (home_device) { - r = mount_device(home_device, arg_directory, "/home", home_device_rw); - if (r < 0) - return log_error_errno(r, "Failed to mount home directory: %m"); - } - - if (srv_device) { - r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw); - if (r < 0) - return log_error_errno(r, "Failed to mount server data directory: %m"); - } - - return 0; -} - -static void loop_remove(int nr, int *image_fd) { - _cleanup_close_ int control = -1; - int r; - - if (nr < 0) - return; - - if (image_fd && *image_fd >= 0) { - r = ioctl(*image_fd, LOOP_CLR_FD); - if (r < 0) - log_debug_errno(errno, "Failed to close loop image: %m"); - *image_fd = safe_close(*image_fd); - } - - control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (control < 0) { - log_warning_errno(errno, "Failed to open /dev/loop-control: %m"); - return; - } - - r = ioctl(control, LOOP_CTL_REMOVE, nr); - if (r < 0) - log_debug_errno(errno, "Failed to remove loop %d: %m", nr); -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * container, the container was terminated by a signal, or - * failed for an unknown reason. No change is made to the - * container argument. - * > 0 : The program executed in the container terminated with an - * error. The exit code of the program executed in the - * container is returned. The container argument has been set - * to CONTAINER_TERMINATED. - * 0 : The container is being rebooted, has been shut down or exited - * successfully. The container argument has been set to either - * CONTAINER_TERMINATED or CONTAINER_REBOOTED. - * - * That is, success is indicated by a return value of zero, and an - * error is indicated by a non-zero value. - */ -static int wait_for_container(pid_t pid, ContainerStatus *container) { - siginfo_t status; - int r; - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_warning_errno(r, "Failed to wait for container: %m"); - - switch (status.si_code) { - - case CLD_EXITED: - if (status.si_status == 0) - log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s exited successfully.", arg_machine); - else - log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s failed with error code %i.", arg_machine, status.si_status); - - *container = CONTAINER_TERMINATED; - return status.si_status; - - case CLD_KILLED: - if (status.si_status == SIGINT) { - log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s has been shut down.", arg_machine); - *container = CONTAINER_TERMINATED; - return 0; - - } else if (status.si_status == SIGHUP) { - log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s is being rebooted.", arg_machine); - *container = CONTAINER_REBOOTED; - return 0; - } - - /* CLD_KILLED fallthrough */ - - case CLD_DUMPED: - log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); - return -EIO; - - default: - log_error("Container %s failed due to unknown reason.", arg_machine); - return -EIO; - } -} - -static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - pid_t pid; - - 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."); - sd_event_source_set_userdata(s, NULL); - return 0; - } - } - - sd_event_exit(sd_event_source_get_event(s), 0); - return 0; -} - -static int determine_names(void) { - int r; - - if (arg_template && !arg_directory && arg_machine) { - - /* If --template= was specified then we should not - * search for a machine, but instead create a new one - * in /var/lib/machine. */ - - arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL); - if (!arg_directory) - return log_oom(); - } - - if (!arg_image && !arg_directory) { - if (arg_machine) { - _cleanup_(image_unrefp) Image *i = NULL; - - r = image_find(arg_machine, &i); - if (r < 0) - return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine); - else if (r == 0) { - log_error("No image for machine '%s': %m", arg_machine); - return -ENOENT; - } - - if (i->type == IMAGE_RAW) - r = free_and_strdup(&arg_image, i->path); - else - r = free_and_strdup(&arg_directory, i->path); - if (r < 0) - return log_error_errno(r, "Invalid image directory: %m"); - - if (!arg_ephemeral) - arg_read_only = arg_read_only || i->read_only; - } else - arg_directory = get_current_dir_name(); - - if (!arg_directory && !arg_machine) { - log_error("Failed to determine path, please use -D or -i."); - return -EINVAL; - } - } - - if (!arg_machine) { - if (arg_directory && path_equal(arg_directory, "/")) - arg_machine = gethostname_malloc(); - else - arg_machine = strdup(basename(arg_image ?: arg_directory)); - - if (!arg_machine) - return log_oom(); - - hostname_cleanup(arg_machine); - if (!machine_name_is_valid(arg_machine)) { - log_error("Failed to determine machine name automatically, please use -M."); - return -EINVAL; - } - - if (arg_ephemeral) { - char *b; - - /* Add a random suffix when this is an - * ephemeral machine, so that we can run many - * instances at once without manually having - * to specify -M each time. */ - - if (asprintf(&b, "%s-%016" PRIx64, arg_machine, random_u64()) < 0) - return log_oom(); - - free(arg_machine); - arg_machine = b; - } - } - - return 0; -} - -static int determine_uid_shift(const char *directory) { - int r; - - if (arg_userns_mode == USER_NAMESPACE_NO) { - arg_uid_shift = 0; - return 0; - } - - if (arg_uid_shift == UID_INVALID) { - struct stat st; - - r = stat(directory, &st); - if (r < 0) - return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory); - - arg_uid_shift = st.st_uid & UINT32_C(0xffff0000); - - if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000))) { - log_error("UID and GID base of %s don't match.", directory); - return -EINVAL; - } - - arg_uid_range = UINT32_C(0x10000); - } - - if (arg_uid_shift > (uid_t) -1 - arg_uid_range) { - log_error("UID base too high for UID range."); - return -EINVAL; - } - - return 0; -} - -static int inner_child( - Barrier *barrier, - const char *directory, - bool secondary, - int kmsg_socket, - int rtnl_socket, - FDSet *fds) { - - _cleanup_free_ char *home = NULL; - char as_uuid[37]; - unsigned n_env = 1; - const char *envp[] = { - "PATH=" DEFAULT_PATH_SPLIT_USR, - NULL, /* container */ - NULL, /* TERM */ - NULL, /* HOME */ - NULL, /* USER */ - NULL, /* LOGNAME */ - NULL, /* container_uuid */ - NULL, /* LISTEN_FDS */ - NULL, /* LISTEN_PID */ - NULL, /* NOTIFY_SOCKET */ - NULL - }; - - _cleanup_strv_free_ char **env_use = NULL; - int r; - - assert(barrier); - assert(directory); - assert(kmsg_socket >= 0); - - cg_unified_flush(); - - if (arg_userns_mode != USER_NAMESPACE_NO) { - /* Tell the parent, that it now can write the UID map. */ - (void) barrier_place(barrier); /* #1 */ - - /* Wait until the parent wrote the UID map */ - if (!barrier_place_and_sync(barrier)) { /* #2 */ - log_error("Parent died too early"); - return -ESRCH; - } - } - - r = mount_all(NULL, - arg_userns_mode != USER_NAMESPACE_NO, - true, - arg_private_network, - arg_uid_shift, - arg_uid_range, - arg_selinux_apifs_context); - - if (r < 0) - return r; - - r = mount_sysfs(NULL); - if (r < 0) - return r; - - /* Wait until we are cgroup-ified, so that we - * can mount the right cgroup path writable */ - if (!barrier_place_and_sync(barrier)) { /* #3 */ - log_error("Parent died too early"); - return -ESRCH; - } - - r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); - if (r < 0) - return r; - - r = reset_uid_gid(); - if (r < 0) - return log_error_errno(r, "Couldn't become new root: %m"); - - r = setup_boot_id(NULL); - if (r < 0) - return r; - - r = setup_kmsg(NULL, kmsg_socket); - if (r < 0) - return r; - kmsg_socket = safe_close(kmsg_socket); - - umask(0022); - - if (setsid() < 0) - return log_error_errno(errno, "setsid() failed: %m"); - - if (arg_private_network) - loopback_setup(); - - if (arg_expose_ports) { - r = expose_port_send_rtnl(rtnl_socket); - if (r < 0) - return r; - rtnl_socket = safe_close(rtnl_socket); - } - - r = drop_capabilities(); - if (r < 0) - return log_error_errno(r, "drop_capabilities() failed: %m"); - - setup_hostname(); - - if (arg_personality != PERSONALITY_INVALID) { - if (personality(arg_personality) < 0) - return log_error_errno(errno, "personality() failed: %m"); - } else if (secondary) { - if (personality(PER_LINUX32) < 0) - return log_error_errno(errno, "personality() failed: %m"); - } - -#ifdef HAVE_SELINUX - if (arg_selinux_context) - if (setexeccon(arg_selinux_context) < 0) - return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context); -#endif - - r = change_uid_gid(arg_user, &home); - 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++; - - if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || - (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || - (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) - return log_oom(); - - assert(!sd_id128_is_null(arg_uuid)); - - if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0) - return log_oom(); - - if (fdset_size(fds) > 0) { - r = fdset_cloexec(fds, false); - if (r < 0) - return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors."); - - if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) || - (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) - return log_oom(); - } - if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) - return log_oom(); - - env_use = strv_env_merge(2, envp, arg_setenv); - if (!env_use) - return log_oom(); - - /* Let the parent know that we are ready and - * wait until the parent is ready with the - * setup, too... */ - if (!barrier_place_and_sync(barrier)) { /* #4 */ - log_error("Parent died too early"); - return -ESRCH; - } - - if (arg_chdir) - if (chdir(arg_chdir) < 0) - return log_error_errno(errno, "Failed to change to specified working directory %s: %m", arg_chdir); - - if (arg_start_mode == START_PID2) { - r = stub_pid1(); - if (r < 0) - return r; - } - - /* Now, explicitly close the log, so that we - * then can close all remaining fds. Closing - * the log explicitly first has the benefit - * that the logging subsystem knows about it, - * and is thus ready to be reopened should we - * need it again. Note that the other fds - * closed here are at least the locking and - * barrier fds. */ - log_close(); - (void) fdset_close_others(fds); - - if (arg_start_mode == START_BOOT) { - char **a; - size_t m; - - /* Automatically search for the init system */ - - m = strv_length(arg_parameters); - a = newa(char*, m + 2); - memcpy_safe(a + 1, arg_parameters, m * sizeof(char*)); - a[1 + m] = NULL; - - a[0] = (char*) "/usr/lib/systemd/systemd"; - execve(a[0], a, env_use); - - a[0] = (char*) "/lib/systemd/systemd"; - execve(a[0], a, env_use); - - a[0] = (char*) "/sbin/init"; - execve(a[0], a, env_use); - } else if (!strv_isempty(arg_parameters)) - execvpe(arg_parameters[0], arg_parameters, env_use); - else { - if (!arg_chdir) - /* If we cannot change the directory, we'll end up in /, that is expected. */ - (void) chdir(home ?: "/root"); - - execle("/bin/bash", "-bash", NULL, env_use); - execle("/bin/sh", "-sh", NULL, env_use); - } - - r = -errno; - (void) log_open(); - return log_error_errno(r, "execv() failed: %m"); -} - -static int setup_sd_notify_child(void) { - static const int one = 1; - int fd = -1; - union sockaddr_union sa = { - .sa.sa_family = AF_UNIX, - }; - int r; - - fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate notification socket: %m"); - - (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755); - (void) unlink(NSPAWN_NOTIFY_SOCKET_PATH); - - strncpy(sa.un.sun_path, NSPAWN_NOTIFY_SOCKET_PATH, sizeof(sa.un.sun_path)-1); - r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) { - safe_close(fd); - return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - } - - r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) { - safe_close(fd); - return log_error_errno(errno, "SO_PASSCRED failed: %m"); - } - - return fd; -} - -static int outer_child( - Barrier *barrier, - const char *directory, - const char *console, - const char *root_device, bool root_device_rw, - const char *home_device, bool home_device_rw, - const char *srv_device, bool srv_device_rw, - bool interactive, - bool secondary, - int pid_socket, - int uuid_socket, - int notify_socket, - int kmsg_socket, - int rtnl_socket, - int uid_shift_socket, - FDSet *fds) { - - pid_t pid; - ssize_t l; - int r; - _cleanup_close_ int fd = -1; - - assert(barrier); - assert(directory); - assert(console); - assert(pid_socket >= 0); - assert(uuid_socket >= 0); - assert(notify_socket >= 0); - assert(kmsg_socket >= 0); - - cg_unified_flush(); - - if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) - return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); - - if (interactive) { - close_nointr(STDIN_FILENO); - close_nointr(STDOUT_FILENO); - close_nointr(STDERR_FILENO); - - r = open_terminal(console, O_RDWR); - if (r != STDIN_FILENO) { - if (r >= 0) { - safe_close(r); - r = -EINVAL; - } - - return log_error_errno(r, "Failed to open console: %m"); - } - - if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || - dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) - return log_error_errno(errno, "Failed to duplicate console: %m"); - } - - r = reset_audit_loginuid(); - if (r < 0) - return r; - - /* Mark everything as slave, so that we still - * receive mounts from the real root, but don't - * propagate mounts to the real root. */ - if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - return log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m"); - - r = mount_devices(directory, - root_device, root_device_rw, - home_device, home_device_rw, - srv_device, srv_device_rw); - if (r < 0) - return r; - - r = determine_uid_shift(directory); - if (r < 0) - return r; - - if (arg_userns_mode != USER_NAMESPACE_NO) { - /* Let the parent know which UID shift we read from the image */ - l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); - if (l < 0) - return log_error_errno(errno, "Failed to send UID shift: %m"); - if (l != sizeof(arg_uid_shift)) { - log_error("Short write while sending UID shift."); - return -EIO; - } - - if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift - * we just read from the image is available. If yes, it will send the UID shift back to us, if - * not it will pick a different one, and send it back to us. */ - - l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0); - if (l < 0) - return log_error_errno(errno, "Failed to recv UID shift: %m"); - if (l != sizeof(arg_uid_shift)) { - log_error("Short read while receiving UID shift."); - return -EIO; - } - } - - log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); - } - - /* Turn directory into bind mount */ - if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0) - return log_error_errno(errno, "Failed to make bind mount: %m"); - - r = recursive_chown(directory, arg_uid_shift, arg_uid_range); - if (r < 0) - return r; - - r = setup_volatile( - directory, - arg_volatile_mode, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_context); - if (r < 0) - return r; - - r = setup_volatile_state( - directory, - arg_volatile_mode, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_context); - if (r < 0) - return r; - - r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift); - if (r < 0) - return r; - - if (arg_read_only) { - r = bind_remount_recursive(directory, true); - if (r < 0) - return log_error_errno(r, "Failed to make tree read-only: %m"); - } - - r = mount_all(directory, - arg_userns_mode != USER_NAMESPACE_NO, - false, - arg_private_network, - arg_uid_shift, - arg_uid_range, - arg_selinux_apifs_context); - if (r < 0) - return r; - - r = copy_devnodes(directory); - if (r < 0) - return r; - - dev_setup(directory, arg_uid_shift, arg_uid_shift); - - r = setup_pts(directory); - if (r < 0) - return r; - - r = setup_propagate(directory); - if (r < 0) - return r; - - r = setup_dev_console(directory, console); - if (r < 0) - return r; - - r = setup_seccomp(arg_caps_retain); - if (r < 0) - return r; - - r = setup_timezone(directory); - if (r < 0) - return r; - - r = setup_resolv_conf(directory); - if (r < 0) - return r; - - r = setup_machine_id(directory); - if (r < 0) - return r; - - r = setup_journal(directory); - if (r < 0) - return r; - - r = mount_custom( - directory, - arg_custom_mounts, - arg_n_custom_mounts, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_apifs_context); - if (r < 0) - return r; - - r = mount_cgroups( - directory, - arg_unified_cgroup_hierarchy, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_apifs_context); - if (r < 0) - return r; - - r = mount_move_root(directory); - if (r < 0) - return log_error_errno(r, "Failed to move root directory: %m"); - - fd = setup_sd_notify_child(); - if (fd < 0) - return fd; - - pid = raw_clone(SIGCHLD|CLONE_NEWNS| - (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) | - (arg_private_network ? CLONE_NEWNET : 0) | - (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0)); - if (pid < 0) - return log_error_errno(errno, "Failed to fork inner child: %m"); - if (pid == 0) { - pid_socket = safe_close(pid_socket); - uuid_socket = safe_close(uuid_socket); - notify_socket = safe_close(notify_socket); - uid_shift_socket = safe_close(uid_shift_socket); - - /* The inner child has all namespaces that are - * requested, so that we all are owned by the user if - * user namespaces are turned on. */ - - r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL); - if (l < 0) - return log_error_errno(errno, "Failed to send PID: %m"); - if (l != sizeof(pid)) { - log_error("Short write while sending PID."); - return -EIO; - } - - l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL); - if (l < 0) - return log_error_errno(errno, "Failed to send machine ID: %m"); - if (l != sizeof(arg_uuid)) { - log_error("Short write while sending machine ID."); - return -EIO; - } - - l = send_one_fd(notify_socket, fd, 0); - if (l < 0) - return log_error_errno(errno, "Failed to send notify fd: %m"); - - pid_socket = safe_close(pid_socket); - uuid_socket = safe_close(uuid_socket); - notify_socket = safe_close(notify_socket); - kmsg_socket = safe_close(kmsg_socket); - rtnl_socket = safe_close(rtnl_socket); - - return 0; -} - -static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { - unsigned n_tries = 100; - uid_t candidate; - int r; - - assert(shift); - assert(ret_lock_file); - assert(arg_userns_mode == USER_NAMESPACE_PICK); - assert(arg_uid_range == 0x10000U); - - candidate = *shift; - - (void) mkdir("/run/systemd/nspawn-uid", 0755); - - for (;;) { - char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; - _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT; - - if (--n_tries <= 0) - return -EBUSY; - - if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX) - goto next; - if ((candidate & UINT32_C(0xFFFF)) != 0) - goto next; - - xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate); - r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf); - if (r == -EBUSY) /* Range already taken by another nspawn instance */ - goto next; - if (r < 0) - return r; - - /* Make some superficial checks whether the range is currently known in the user database */ - if (getpwuid(candidate)) - goto next; - if (getpwuid(candidate + UINT32_C(0xFFFE))) - goto next; - if (getgrgid(candidate)) - goto next; - if (getgrgid(candidate + UINT32_C(0xFFFE))) - goto next; - - *ret_lock_file = lf; - lf = (struct LockFile) LOCK_FILE_INIT; - *shift = candidate; - return 0; - - next: - random_bytes(&candidate, sizeof(candidate)); - candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN; - candidate &= (uid_t) UINT32_C(0xFFFF0000); - } -} - -static int setup_uid_map(pid_t pid) { - char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1]; - int r; - - assert(pid > 1); - - xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid); - xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range); - r = write_string_file(uid_map, line, 0); - if (r < 0) - return log_error_errno(r, "Failed to write UID map: %m"); - - /* We always assign the same UID and GID ranges */ - xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid); - r = write_string_file(uid_map, line, 0); - if (r < 0) - return log_error_errno(r, "Failed to write GID map: %m"); - - return 0; -} - -static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - char buf[NOTIFY_BUFFER_MAX+1]; - char *p = NULL; - 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; - ssize_t n; - pid_t inner_child_pid; - _cleanup_strv_free_ char **tags = NULL; - - assert(userdata); - - inner_child_pid = PTR_TO_PID(userdata); - - if (revents != EPOLLIN) { - log_warning("Got unexpected poll event for notify fd."); - return 0; - } - - n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_warning_errno(errno, "Couldn't read notification socket: %m"); - } - 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))) { - - ucred = (struct ucred*) CMSG_DATA(cmsg); - } - } - - if (!ucred || ucred->pid != inner_child_pid) { - 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."); - return 0; - } - - buf[n] = 0; - tags = strv_split(buf, "\n\r"); - if (!tags) - return log_oom(); - - if (strv_find(tags, "READY=1")) - sd_notifyf(false, "READY=1\n"); - - p = strv_find_startswith(tags, "STATUS="); - if (p) - sd_notifyf(false, "STATUS=Container running: %s", p); - - return 0; -} - -static int setup_sd_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid) { - int r; - sd_event_source *notify_event_source; - - r = sd_event_add_io(event, ¬ify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid); - if (r < 0) - return log_error_errno(r, "Failed to allocate notify event source: %m"); - - (void) sd_event_source_set_description(notify_event_source, "nspawn-notify"); - - return 0; -} - -static int load_settings(void) { - _cleanup_(settings_freep) Settings *settings = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - const char *fn, *i; - int r; - - /* If all settings are masked, there's no point in looking for - * the settings file */ - if ((arg_settings_mask & _SETTINGS_MASK_ALL) == _SETTINGS_MASK_ALL) - return 0; - - fn = strjoina(arg_machine, ".nspawn"); - - /* We first look in the admin's directories in /etc and /run */ - FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") { - _cleanup_free_ char *j = NULL; - - j = strjoin(i, "/", fn, NULL); - if (!j) - return log_oom(); - - f = fopen(j, "re"); - if (f) { - p = j; - j = NULL; - - /* By default, we trust configuration from /etc and /run */ - if (arg_settings_trusted < 0) - arg_settings_trusted = true; - - break; - } - - if (errno != ENOENT) - return log_error_errno(errno, "Failed to open %s: %m", j); - } - - if (!f) { - /* After that, let's look for a file next to the - * actual image we shall boot. */ - - if (arg_image) { - p = file_in_same_dir(arg_image, fn); - if (!p) - return log_oom(); - } else if (arg_directory) { - p = file_in_same_dir(arg_directory, fn); - if (!p) - return log_oom(); - } - - if (p) { - f = fopen(p, "re"); - 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 */ - if (arg_settings_trusted < 0) - arg_settings_trusted = false; - } - } - - if (!f) - return 0; - - log_debug("Settings are trusted: %s", yes_no(arg_settings_trusted)); - - r = settings_load(f, p, &settings); - if (r < 0) - return r; - - /* Copy over bits from the settings, unless they have been - * explicitly masked by command line switches. */ - - if ((arg_settings_mask & SETTING_START_MODE) == 0 && - settings->start_mode >= 0) { - arg_start_mode = settings->start_mode; - - strv_free(arg_parameters); - arg_parameters = settings->parameters; - settings->parameters = NULL; - } - - if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && - settings->working_directory) { - free(arg_chdir); - arg_chdir = settings->working_directory; - settings->working_directory = NULL; - } - - if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && - settings->environment) { - strv_free(arg_setenv); - arg_setenv = settings->environment; - settings->environment = NULL; - } - - if ((arg_settings_mask & SETTING_USER) == 0 && - settings->user) { - free(arg_user); - arg_user = settings->user; - settings->user = NULL; - } - - if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { - uint64_t plus; - - 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_caps_retain |= plus; - - arg_caps_retain &= ~settings->drop_capability; - } - - if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && - settings->kill_signal > 0) - arg_kill_signal = settings->kill_signal; - - if ((arg_settings_mask & SETTING_PERSONALITY) == 0 && - settings->personality != PERSONALITY_INVALID) - arg_personality = settings->personality; - - if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 && - !sd_id128_is_null(settings->machine_id)) { - - if (!arg_settings_trusted) - log_warning("Ignoring MachineID= setting, file %s is not trusted.", p); - else - arg_uuid = settings->machine_id; - } - - if ((arg_settings_mask & SETTING_READ_ONLY) == 0 && - settings->read_only >= 0) - arg_read_only = settings->read_only; - - if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 && - settings->volatile_mode != _VOLATILE_MODE_INVALID) - arg_volatile_mode = settings->volatile_mode; - - if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 && - settings->n_custom_mounts > 0) { - - if (!arg_settings_trusted) - log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", p); - else { - custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); - arg_custom_mounts = settings->custom_mounts; - arg_n_custom_mounts = settings->n_custom_mounts; - - settings->custom_mounts = NULL; - settings->n_custom_mounts = 0; - } - } - - if ((arg_settings_mask & SETTING_NETWORK) == 0 && - (settings->private_network >= 0 || - settings->network_veth >= 0 || - settings->network_bridge || - settings->network_zone || - settings->network_interfaces || - settings->network_macvlan || - 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; - - strv_free(arg_network_macvlan); - arg_network_macvlan = settings->network_macvlan; - settings->network_macvlan = NULL; - - strv_free(arg_network_ipvlan); - 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; - - free(arg_network_zone); - arg_network_zone = settings->network_zone; - settings->network_zone = NULL; - } - } - - if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 && - settings->expose_ports) { - - if (!arg_settings_trusted) - log_warning("Ignoring Port= setting, file %s is not trusted.", p); - else { - expose_port_free_all(arg_expose_ports); - arg_expose_ports = settings->expose_ports; - settings->expose_ports = NULL; - } - } - - if ((arg_settings_mask & SETTING_USERNS) == 0 && - settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { - - if (!arg_settings_trusted) - log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p); - else { - arg_userns_mode = settings->userns_mode; - arg_uid_shift = settings->uid_shift; - arg_uid_range = settings->uid_range; - arg_userns_chown = settings->userns_chown; - } - } - - if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0) - arg_notify_ready = settings->notify_ready; - - return 0; -} - -int main(int argc, char *argv[]) { - - _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL; - bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; - _cleanup_close_ int master = -1, image_fd = -1; - _cleanup_fdset_free_ FDSet *fds = NULL; - int r, n_fd_passed, loop_nr = -1; - char veth_name[IFNAMSIZ] = ""; - bool secondary = false, remove_subvol = false; - sigset_t mask_chld; - pid_t pid = 0; - int ret = EXIT_SUCCESS; - union in_addr_union exposed = {}; - _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool interactive, veth_created = false; - - log_parse_environment(); - log_open(); - - /* Make sure rename_process() in the stub init process can work */ - saved_argv = argv; - saved_argc = argc; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (geteuid() != 0) { - log_error("Need to be root."); - r = -EPERM; - goto finish; - } - r = determine_names(); - if (r < 0) - goto finish; - - r = load_settings(); - if (r < 0) - goto finish; - - r = verify_arguments(); - if (r < 0) - goto finish; - - n_fd_passed = sd_listen_fds(false); - if (n_fd_passed > 0) { - r = fdset_new_listen_fds(&fds, false); - if (r < 0) { - log_error_errno(r, "Failed to collect file descriptors: %m"); - goto finish; - } - } - - if (arg_directory) { - assert(!arg_image); - - if (path_equal(arg_directory, "/") && !arg_ephemeral) { - log_error("Spawning container on root directory is not supported. Consider using --ephemeral."); - r = -EINVAL; - goto finish; - } - - if (arg_ephemeral) { - _cleanup_free_ char *np = NULL; - - /* If the specified path is a mount point we - * generate the new snapshot immediately - * inside it under a random name. However if - * the specified is not a mount point we - * create the new snapshot in the parent - * directory, just next to it. */ - r = path_is_mount_point(arg_directory, 0); - if (r < 0) { - log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory); - goto finish; - } - if (r > 0) - r = tempfn_random_child(arg_directory, "machine.", &np); - else - r = tempfn_random(arg_directory, "machine.", &np); - if (r < 0) { - log_error_errno(r, "Failed to generate name for snapshot: %m"); - goto finish; - } - - r = image_path_lock(np, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); - if (r < 0) { - log_error_errno(r, "Failed to lock %s: %m", np); - goto finish; - } - - 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; - } - - free(arg_directory); - arg_directory = np; - np = NULL; - - remove_subvol = true; - - } else { - r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); - if (r == -EBUSY) { - log_error_errno(r, "Directory tree %s is currently busy.", arg_directory); - goto finish; - } - if (r < 0) { - log_error_errno(r, "Failed to lock %s: %m", arg_directory); - goto finish; - } - - 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 | BTRFS_SNAPSHOT_QUOTA); - if (r == -EEXIST) { - if (!arg_quiet) - log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template); - } else if (r < 0) { - log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template); - goto finish; - } else { - if (!arg_quiet) - log_info("Populated %s from template %s.", arg_directory, arg_template); - } - } - } - - if (arg_start_mode == START_BOOT) { - if (path_is_os_tree(arg_directory) <= 0) { - log_error("Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", arg_directory); - r = -EINVAL; - goto finish; - } - } else { - const char *p; - - 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; - } - } - - } else { - char template[] = "/tmp/nspawn-root-XXXXXX"; - - assert(arg_image); - assert(!arg_template); - - r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); - if (r == -EBUSY) { - r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); - goto finish; - } - if (r < 0) { - r = log_error_errno(r, "Failed to create image lock: %m"); - goto finish; - } - - if (!mkdtemp(template)) { - log_error_errno(errno, "Failed to create temporary directory: %m"); - r = -errno; - goto finish; - } - - arg_directory = strdup(template); - if (!arg_directory) { - r = log_oom(); - goto finish; - } - - image_fd = setup_image(&device_path, &loop_nr); - if (image_fd < 0) { - r = image_fd; - goto finish; - } - - r = dissect_image(image_fd, - &root_device, &root_device_rw, - &home_device, &home_device_rw, - &srv_device, &srv_device_rw, - &secondary); - if (r < 0) - goto finish; - } - - r = custom_mounts_prepare(); - if (r < 0) - goto finish; - - interactive = - isatty(STDIN_FILENO) > 0 && - isatty(STDOUT_FILENO) > 0; - - master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); - if (master < 0) { - r = log_error_errno(errno, "Failed to acquire pseudo tty: %m"); - goto finish; - } - - r = ptsname_malloc(master, &console); - if (r < 0) { - r = log_error_errno(r, "Failed to determine tty name: %m"); - goto finish; - } - - if (arg_selinux_apifs_context) { - r = mac_selinux_apply(console, arg_selinux_apifs_context); - if (r < 0) - goto finish; - } - - if (unlockpt(master) < 0) { - r = log_error_errno(errno, "Failed to unlock tty: %m"); - goto finish; - } - - if (!arg_quiet) - log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.", - arg_machine, arg_image ?: arg_directory); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - - assert_se(sigemptyset(&mask_chld) == 0); - assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); - - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { - r = log_error_errno(errno, "Failed to become subreaper: %m"); - goto finish; - } - - for (;;) { - static const struct sigaction sa = { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP, - }; - - _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; - _cleanup_close_ int etc_passwd_lock = -1; - _cleanup_close_pair_ int - kmsg_socket_pair[2] = { -1, -1 }, - rtnl_socket_pair[2] = { -1, -1 }, - pid_socket_pair[2] = { -1, -1 }, - uuid_socket_pair[2] = { -1, -1 }, - notify_socket_pair[2] = { -1, -1 }, - uid_shift_socket_pair[2] = { -1, -1 }; - _cleanup_close_ int notify_socket= -1; - _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(pty_forward_freep) PTYForward *forward = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - ContainerStatus container_status; - char last_char = 0; - int ifi = 0; - ssize_t l; - - if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely - * check with getpwuid() if the specific user already exists. Note that /etc might be - * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we - * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are - * really just an extra safety net. We kinda assume that the UID range we allocate from is - * really ours. */ - - etc_passwd_lock = take_etc_passwd_lock(NULL); - if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) { - log_error_errno(r, "Failed to take /etc/passwd lock: %m"); - goto finish; - } - } - - r = barrier_create(&barrier); - if (r < 0) { - log_error_errno(r, "Cannot initialize IPC barrier: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create kmsg socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create rtnl socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create pid socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create id socket pair: %m"); - goto finish; - } - - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create notify socket pair: %m"); - goto finish; - } - - if (arg_userns_mode != USER_NAMESPACE_NO) - if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { - r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); - goto finish; - } - - /* Child can be killed before execv(), so handle SIGCHLD - * in order to interrupt parent's blocking calls and - * give it a chance to call wait() and terminate. */ - r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL); - if (r < 0) { - r = log_error_errno(errno, "Failed to change the signal mask: %m"); - goto finish; - } - - r = sigaction(SIGCHLD, &sa, NULL); - if (r < 0) { - r = log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); - goto finish; - } - - pid = raw_clone(SIGCHLD|CLONE_NEWNS); - if (pid < 0) { - if (errno == EINVAL) - r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); - else - r = log_error_errno(errno, "clone() failed: %m"); - - goto finish; - } - - if (pid == 0) { - /* The outer child only has a file system namespace. */ - barrier_set_role(&barrier, BARRIER_CHILD); - - master = safe_close(master); - - kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); - rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); - pid_socket_pair[0] = safe_close(pid_socket_pair[0]); - uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); - notify_socket_pair[0] = safe_close(notify_socket_pair[0]); - uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - r = outer_child(&barrier, - arg_directory, - console, - root_device, root_device_rw, - home_device, home_device_rw, - srv_device, srv_device_rw, - interactive, - secondary, - pid_socket_pair[1], - uuid_socket_pair[1], - notify_socket_pair[1], - kmsg_socket_pair[1], - rtnl_socket_pair[1], - uid_shift_socket_pair[1], - fds); - if (r < 0) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - barrier_set_role(&barrier, BARRIER_PARENT); - - fds = fdset_free(fds); - - kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); - rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); - pid_socket_pair[1] = safe_close(pid_socket_pair[1]); - uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); - notify_socket_pair[1] = safe_close(notify_socket_pair[1]); - uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); - - if (arg_userns_mode != USER_NAMESPACE_NO) { - /* The child just let us know the UID shift it might have read from the image. */ - l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short read while reading UID shift."); - r = EIO; - goto finish; - } - - if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* If we are supposed to pick the UID shift, let's try to use the shift read from the - * image, but if that's already in use, pick a new one, and report back to the child, - * which one we now picked. */ - - r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); - if (r < 0) { - log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); - goto finish; - } - - l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); - if (l < 0) { - r = log_error_errno(errno, "Failed to send UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short write while writing UID shift."); - r = -EIO; - goto finish; - } - } - } - - /* Wait for the outer child. */ - r = wait_for_terminate_and_warn("namespace helper", pid, NULL); - if (r < 0) - goto finish; - if (r != 0) { - r = -EIO; - goto finish; - } - pid = 0; - - /* And now retrieve the PID of the inner child. */ - l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read inner child PID: %m"); - goto finish; - } - if (l != sizeof(pid)) { - log_error("Short read while reading inner child PID."); - r = EIO; - goto finish; - } - - /* We also retrieve container UUID in case it was generated by outer child */ - l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read container machine ID: %m"); - goto finish; - } - if (l != sizeof(arg_uuid)) { - log_error("Short read while reading container machined ID."); - r = EIO; - goto finish; - } - - /* We also retrieve the socket used for notifications generated by outer child */ - notify_socket = receive_one_fd(notify_socket_pair[0], 0); - if (notify_socket < 0) { - r = log_error_errno(errno, "Failed to receive notification socket from the outer child: %m"); - goto finish; - } - - log_debug("Init process invoked as PID " PID_FMT, pid); - - if (arg_userns_mode != USER_NAMESPACE_NO) { - if (!barrier_place_and_sync(&barrier)) { /* #1 */ - log_error("Child died too early."); - r = -ESRCH; - goto finish; - } - - r = setup_uid_map(pid); - if (r < 0) - goto finish; - - (void) barrier_place(&barrier); /* #2 */ - } - - if (arg_private_network) { - - r = move_network_interfaces(pid, arg_network_interfaces); - if (r < 0) - goto finish; - - if (arg_network_veth) { - r = setup_veth(arg_machine, pid, veth_name, - arg_network_bridge || arg_network_zone); - if (r < 0) - goto finish; - else if (r > 0) - ifi = r; - - if (arg_network_bridge) { - /* Add the interface to a bridge */ - r = setup_bridge(veth_name, arg_network_bridge, false); - if (r < 0) - goto finish; - if (r > 0) - ifi = r; - } else if (arg_network_zone) { - /* Add the interface to a bridge, possibly creating it */ - r = setup_bridge(veth_name, arg_network_zone, true); - if (r < 0) - goto finish; - if (r > 0) - ifi = r; - } - } - - r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra); - if (r < 0) - goto finish; - - /* We created the primary and extra veth links now; let's remember this, so that we know to - remove them later on. Note that we don't bother with removing veth links that were created - here when their setup failed half-way, because in that case the kernel should be able to - remove them on its own, since they cannot be referenced by anything yet. */ - veth_created = true; - - r = setup_macvlan(arg_machine, pid, arg_network_macvlan); - if (r < 0) - goto finish; - - r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan); - if (r < 0) - goto finish; - } - - if (arg_register) { - r = register_machine( - arg_machine, - pid, - arg_directory, - arg_uuid, - ifi, - arg_slice, - arg_custom_mounts, arg_n_custom_mounts, - arg_kill_signal, - arg_property, - arg_keep_unit, - arg_container_service_name); - if (r < 0) - goto finish; - } - - r = sync_cgroup(pid, arg_unified_cgroup_hierarchy); - if (r < 0) - goto finish; - - if (arg_keep_unit) { - r = create_subcgroup(pid, arg_unified_cgroup_hierarchy); - if (r < 0) - goto finish; - } - - r = chown_cgroup(pid, arg_uid_shift); - if (r < 0) - goto finish; - - /* Notify the child that the parent is ready with all - * its setup (including cgroup-ification), and that - * the child can now hand over control to the code to - * run inside the container. */ - (void) barrier_place(&barrier); /* #3 */ - - /* Block SIGCHLD here, before notifying child. - * process_pty() will handle it with the other signals. */ - assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0); - - /* Reset signal to default */ - r = default_signals(SIGCHLD, -1); - if (r < 0) { - log_error_errno(r, "Failed to reset SIGCHLD: %m"); - goto finish; - } - - r = sd_event_new(&event); - if (r < 0) { - log_error_errno(r, "Failed to get default event source: %m"); - goto finish; - } - - r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(pid)); - if (r < 0) - goto finish; - - /* Let the child know that we are ready and wait that the child is completely ready now. */ - if (!barrier_place_and_sync(&barrier)) { /* #4 */ - log_error("Child died too early."); - r = -ESRCH; - goto finish; - } - - /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear - * in getpwuid(), thus we can release the /etc/passwd lock. */ - etc_passwd_lock = safe_close(etc_passwd_lock); - - sd_notifyf(false, - "STATUS=Container running.\n" - "X_NSPAWN_LEADER_PID=" PID_FMT, pid); - if (!arg_notify_ready) - sd_notify(false, "READY=1\n"); - - 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, 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); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - } - - /* simply exit on sigchld */ - sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); - - if (arg_expose_ports) { - r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl); - if (r < 0) - goto finish; - - (void) expose_port_execute(rtnl, arg_expose_ports, &exposed); - } - - rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); - - 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; - } - - r = sd_event_loop(event); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - - pty_forward_get_last_char(forward, &last_char); - - forward = pty_forward_free(forward); - - if (!arg_quiet && last_char != '\n') - putc('\n', stdout); - - /* Kill if it is not dead yet anyway */ - if (arg_register && !arg_keep_unit) - terminate_machine(pid); - - /* Normally redundant, but better safe than sorry */ - kill(pid, SIGKILL); - - r = wait_for_container(pid, &container_status); - pid = 0; - - if (r < 0) - /* We failed to wait for the container, or the - * container exited abnormally */ - goto finish; - else if (r > 0 || container_status == CONTAINER_TERMINATED) { - /* The container exited with a non-zero - * status, or with zero status and no reboot - * was requested. */ - ret = r; - break; - } - - /* CONTAINER_REBOOTED, loop again */ - - if (arg_keep_unit) { - /* Special handling if we are running as a - * service: instead of simply restarting the - * machine we want to restart the entire - * service, so let's inform systemd about this - * with the special exit code 133. The service - * file uses RestartForceExitStatus=133 so - * that this results in a full nspawn - * restart. This is necessary since we might - * have cgroup parameters set we want to have - * flushed out. */ - ret = 133; - r = 0; - break; - } - - expose_port_flush(arg_expose_ports, &exposed); - - (void) remove_veth_links(veth_name, arg_network_veth_extra); - veth_created = false; - } - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Terminating..."); - - if (pid > 0) - kill(pid, SIGKILL); - - /* Try to flush whatever is still queued in the pty */ - if (master >= 0) - (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); - - loop_remove(loop_nr, &image_fd); - - if (remove_subvol && arg_directory) { - int k; - - 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); - } - - if (arg_machine) { - const char *p; - - p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); - (void) rm_rf(p, REMOVE_ROOT); - } - - expose_port_flush(arg_expose_ports, &exposed); - - if (veth_created) - (void) remove_veth_links(veth_name, arg_network_veth_extra); - (void) remove_bridge(arg_network_zone); - - free(arg_directory); - free(arg_template); - free(arg_image); - free(arg_machine); - free(arg_user); - free(arg_chdir); - strv_free(arg_setenv); - free(arg_network_bridge); - 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); - - return r < 0 ? EXIT_FAILURE : ret; -} diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c deleted file mode 100644 index 11c5321788..0000000000 --- a/src/nspawn/test-patch-uid.c +++ /dev/null @@ -1,61 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "log.h" -#include "nspawn-patch-uid.h" -#include "user-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - uid_t shift, range; - int r; - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - if (argc != 4) { - log_error("Expected PATH SHIFT RANGE parameters."); - return EXIT_FAILURE; - } - - r = parse_uid(argv[2], &shift); - if (r < 0) { - log_error_errno(r, "Failed to parse UID shift %s.", argv[2]); - return EXIT_FAILURE; - } - - r = parse_gid(argv[3], &range); - if (r < 0) { - log_error_errno(r, "Failed to parse UID range %s.", argv[3]); - return EXIT_FAILURE; - } - - r = path_patch_uid(argv[1], shift, range); - if (r < 0) { - log_error_errno(r, "Failed to patch directory tree: %m"); - return EXIT_FAILURE; - } - - log_info("Changed: %s", yes_no(r)); - - return EXIT_SUCCESS; -} diff --git a/src/nss-myhostname/Makefile b/src/nss-myhostname/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/nss-myhostname/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/nss-myhostname/Makefile b/src/nss-myhostname/Makefile new file mode 100644 index 0000000000..4aa0c45cdd --- /dev/null +++ b/src/nss-myhostname/Makefile @@ -0,0 +1,47 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_MYHOSTNAME),) +libnss_myhostname_la_SOURCES = \ + src/nss-myhostname/nss-myhostname.sym \ + src/nss-myhostname/nss-myhostname.c + +libnss_myhostname_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -shrext .so.2 \ + -Wl,--version-script=$(srcdir)/nss-myhostname.sym + +libnss_myhostname_la_LIBADD = \ + libsystemd-internal.la \ + libbasic.la + +lib_LTLIBRARIES += \ + libnss_myhostname.la +endif # HAVE_MYHOSTNAME + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 11c27575c0..6983587265 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -24,14 +24,14 @@ #include #include -#include "alloc-util.h" -#include "hostname-util.h" -#include "local-addresses.h" -#include "macro.h" -#include "nss-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/nss-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-netlink/local-addresses.h" /* We use 127.0.0.2 as IPv4 address. This has the advantage over * 127.0.0.1 that it can be translated back to the local hostname. For diff --git a/src/nss-myhostname/nss-myhostname.xml b/src/nss-myhostname/nss-myhostname.xml new file mode 100644 index 0000000000..a920ec334f --- /dev/null +++ b/src/nss-myhostname/nss-myhostname.xml @@ -0,0 +1,148 @@ + + + + + + + + + nss-myhostname + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + nss-myhostname + 8 + + + + nss-myhostname + libnss_myhostname.so.2 + Provide hostname resolution for the locally + configured system hostname. + + + + libnss_myhostname.so.2 + + + + Description + + nss-myhostname is a plug-in module for the GNU Name Service Switch (NSS) functionality of + the GNU C Library (glibc), primarily providing hostname resolution for the locally configured + system hostname as returned by + gethostname2. The precise + hostnames resolved by this module are: + + + The local, configured hostname is resolved to + all locally configured IP addresses ordered by their scope, or + — if none are configured — the IPv4 address 127.0.0.2 (which + is on the local loopback) and the IPv6 address ::1 (which is the + local host). + + The hostnames localhost and + localhost.localdomain (as well as any hostname + ending in .localhost or .localhost.localdomain) + are resolved to the IP addresses 127.0.0.1 and ::1. + + The hostname gateway is + resolved to all current default routing gateway addresses, + ordered by their metric. This assigns a stable hostname to the + current gateway, useful for referencing it independently of the + current network configuration state. + + + Various software relies on an always-resolvable local + hostname. When using dynamic hostnames, this is traditionally + achieved by patching /etc/hosts at the same + time as changing the hostname. This is problematic since it + requires a writable /etc file system and is + fragile because the file might be edited by the administrator at + the same time. With nss-myhostname enabled, + changing /etc/hosts is unnecessary, and on + many systems, the file becomes entirely optional. + + To activate the NSS modules, add myhostname to the line starting with + hosts: in /etc/nsswitch.conf. + + It is recommended to place myhostname last in the nsswitch.conf' + hosts: line to make sure that this mapping is only used as fallback, and that any DNS or + /etc/hosts based mapping takes precedence. + + + + Example + + Here is an example /etc/nsswitch.conf file that enables + nss-myhostname correctly: + +passwd: compat mymachines +group: compat mymachines +shadow: compat + +hosts: files mymachines resolve myhostname +networks: files + +protocols: db files +services: db files +ethers: db files +rpc: db files + +netgroup: nis + + To test, use glibc's getent tool: + + $ getent ahosts `hostname` +::1 STREAM omega +::1 DGRAM +::1 RAW +127.0.0.2 STREAM +127.0.0.2 DGRAM +127.0.0.2 RAW + + In this case, the local hostname is omega. + + + + + See Also + + systemd1, + nss-resolve8, + nss-mymachines8, + nsswitch.conf5, + getent1 + + + + diff --git a/src/nss-mymachines/Makefile b/src/nss-mymachines/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/nss-mymachines/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c deleted file mode 100644 index 8d57b26cbc..0000000000 --- a/src/nss-mymachines/nss-mymachines.c +++ /dev/null @@ -1,738 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-bus.h" -#include "sd-login.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "hostname-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "nss-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "user-util.h" -#include "util.h" - -NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); -NSS_GETPW_PROTOTYPES(mymachines); -NSS_GETGR_PROTOTYPES(mymachines); - -#define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000)) -#define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000)) - -static int count_addresses(sd_bus_message *m, int af, unsigned *ret) { - unsigned c = 0; - int r; - - assert(m); - assert(ret); - - while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) { - int family; - - r = sd_bus_message_read(m, "i", &family); - if (r < 0) - return r; - - r = sd_bus_message_skip(m, "ay"); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - if (af != AF_UNSPEC && family != af) - continue; - - c++; - } - if (r < 0) - return r; - - r = sd_bus_message_rewind(m, false); - if (r < 0) - return r; - - *ret = c; - return 0; -} - -enum nss_status _nss_mymachines_gethostbyname4_r( - const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp) { - - struct gaih_addrtuple *r_tuple, *r_tuple_first = 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; - unsigned i = 0, c = 0; - char *r_name; - int n_ifindices, r; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(pat); - assert(buffer); - assert(errnop); - assert(h_errnop); - - r = sd_machine_get_class(name, &class); - if (r < 0) - goto fail; - if (!streq(class, "container")) { - r = -ENOTTY; - goto fail; - } - - n_ifindices = sd_machine_get_ifindices(name, &ifindices); - if (n_ifindices < 0) { - r = n_ifindices; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); - if (r < 0) - goto fail; - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - goto fail; - - r = count_addresses(reply, AF_UNSPEC, &c); - if (r < 0) - goto fail; - - if (c <= 0) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - l = strlen(name); - ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; - if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; - return NSS_STATUS_TRYAGAIN; - } - - /* First, append name */ - r_name = buffer; - memcpy(r_name, name, l+1); - idx = ALIGN(l+1); - - /* Second, append addresses */ - r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; - const void *a; - size_t sz; - - r = sd_bus_message_read(reply, "i", &family); - if (r < 0) - goto fail; - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (!IN_SET(family, AF_INET, AF_INET6)) { - r = -EAFNOSUPPORT; - goto fail; - } - - if (sz != FAMILY_ADDRESS_SIZE(family)) { - r = -EINVAL; - goto fail; - } - - r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); - r_tuple->name = r_name; - r_tuple->family = family; - r_tuple->scopeid = n_ifindices == 1 ? ifindices[0] : 0; - memcpy(r_tuple->addr, a, sz); - - idx += ALIGN(sizeof(struct gaih_addrtuple)); - i++; - } - - assert(i == c); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - assert(idx == ms); - - if (*pat) - **pat = *r_tuple_first; - else - *pat = r_tuple_first; - - if (ttlp) - *ttlp = 0; - - /* Explicitly reset all error variables */ - *errnop = 0; - *h_errnop = NETDB_SUCCESS; - h_errno = 0; - - return NSS_STATUS_SUCCESS; - -fail: - *errnop = -r; - *h_errnop = NO_DATA; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_mymachines_gethostbyname3_r( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp) { - - _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; - size_t l, idx, ms, alen; - int r; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(result); - assert(buffer); - assert(errnop); - assert(h_errnop); - - if (af == AF_UNSPEC) - af = AF_INET; - - if (af != AF_INET && af != AF_INET6) { - r = -EAFNOSUPPORT; - goto fail; - } - - r = sd_machine_get_class(name, &class); - if (r < 0) - goto fail; - if (!streq(class, "container")) { - r = -ENOTTY; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineAddresses", - NULL, - &reply, - "s", name); - if (r < 0) - goto fail; - - r = sd_bus_message_enter_container(reply, 'a', "(iay)"); - if (r < 0) - goto fail; - - r = count_addresses(reply, af, &c); - if (r < 0) - goto fail; - - if (c <= 0) { - *errnop = ENOENT; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - alen = FAMILY_ADDRESS_SIZE(af); - l = strlen(name); - - ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); - - if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_TRYAGAIN; - } - - /* First, append name */ - r_name = buffer; - memcpy(r_name, name, l+1); - idx = ALIGN(l+1); - - /* Second, create aliases array */ - r_aliases = buffer + idx; - ((char**) r_aliases)[0] = NULL; - idx += sizeof(char*); - - /* Third, append addresses */ - r_addr = buffer + idx; - while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) { - int family; - const void *a; - size_t sz; - - r = sd_bus_message_read(reply, "i", &family); - if (r < 0) - goto fail; - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (family != af) - continue; - - if (sz != alen) { - r = -EINVAL; - goto fail; - } - - memcpy(r_addr + i*ALIGN(alen), a, alen); - i++; - } - - assert(i == c); - idx += c * ALIGN(alen); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - /* Third, append address pointer array */ - r_addr_list = buffer + idx; - for (i = 0; i < c; i++) - ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen); - - ((char**) r_addr_list)[i] = NULL; - idx += (c+1) * sizeof(char*); - - assert(idx == ms); - - result->h_name = r_name; - result->h_aliases = (char**) r_aliases; - result->h_addrtype = af; - result->h_length = alen; - result->h_addr_list = (char**) r_addr_list; - - if (ttlp) - *ttlp = 0; - - if (canonp) - *canonp = r_name; - - /* Explicitly reset all error variables */ - *errnop = 0; - *h_errnop = NETDB_SUCCESS; - h_errno = 0; - - return NSS_STATUS_SUCCESS; - -fail: - *errnop = -r; - *h_errnop = NO_DATA; - return NSS_STATUS_UNAVAIL; -} - -NSS_GETHOSTBYNAME_FALLBACKS(mymachines); - -enum nss_status _nss_mymachines_getpwnam_r( - const char *name, - struct passwd *pwd, - char *buffer, size_t buflen, - int *errnop) { - - _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; - size_t l; - int r; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(pwd); - - p = startswith(name, "vu-"); - if (!p) - goto not_found; - - e = strrchr(p, '-'); - 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; - - machine = strndupa(p, e - p); - if (!machine_name_is_valid(machine)) - goto not_found; - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapFromMachineUser", - &error, - &reply, - "su", - machine, (uint32_t) uid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) - goto not_found; - - goto fail; - } - - r = sd_bus_message_read(reply, "u", &mapped); - if (r < 0) - goto fail; - - /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */ - if (mapped < HOST_UID_LIMIT || mapped == uid) - goto not_found; - - l = strlen(name); - if (buflen < l+1) { - *errnop = ENOMEM; - return NSS_STATUS_TRYAGAIN; - } - - memcpy(buffer, name, l+1); - - pwd->pw_name = buffer; - pwd->pw_uid = mapped; - pwd->pw_gid = 65534; /* nobody */ - pwd->pw_gecos = buffer; - pwd->pw_passwd = (char*) "*"; /* locked */ - pwd->pw_dir = (char*) "/"; - pwd->pw_shell = (char*) "/sbin/nologin"; - - *errnop = 0; - return NSS_STATUS_SUCCESS; - -not_found: - *errnop = 0; - return NSS_STATUS_NOTFOUND; - -fail: - *errnop = -r; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_mymachines_getpwuid_r( - uid_t uid, - struct passwd *pwd, - char *buffer, size_t buflen, - int *errnop) { - - _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; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - if (!uid_is_valid(uid)) { - r = -EINVAL; - goto fail; - } - - /* We consider all uids < 65536 host uids */ - if (uid < HOST_UID_LIMIT) - goto not_found; - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapToMachineUser", - &error, - &reply, - "u", - (uint32_t) uid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) - goto not_found; - - goto fail; - } - - r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped); - if (r < 0) - goto fail; - - if (mapped == uid) - goto not_found; - - if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { - *errnop = ENOMEM; - return NSS_STATUS_TRYAGAIN; - } - - pwd->pw_name = buffer; - pwd->pw_uid = uid; - pwd->pw_gid = 65534; /* nobody */ - pwd->pw_gecos = buffer; - pwd->pw_passwd = (char*) "*"; /* locked */ - pwd->pw_dir = (char*) "/"; - pwd->pw_shell = (char*) "/sbin/nologin"; - - *errnop = 0; - return NSS_STATUS_SUCCESS; - -not_found: - *errnop = 0; - return NSS_STATUS_NOTFOUND; - -fail: - *errnop = -r; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_mymachines_getgrnam_r( - const char *name, - struct group *gr, - char *buffer, size_t buflen, - int *errnop) { - - _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; - size_t l; - int r; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(gr); - - p = startswith(name, "vg-"); - if (!p) - goto not_found; - - e = strrchr(p, '-'); - 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; - - machine = strndupa(p, e - p); - if (!machine_name_is_valid(machine)) - goto not_found; - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapFromMachineGroup", - &error, - &reply, - "su", - machine, (uint32_t) gid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) - goto not_found; - - goto fail; - } - - r = sd_bus_message_read(reply, "u", &mapped); - if (r < 0) - goto fail; - - if (mapped < HOST_GID_LIMIT || mapped == gid) - goto not_found; - - l = sizeof(char*) + strlen(name) + 1; - if (buflen < l) { - *errnop = ENOMEM; - return NSS_STATUS_TRYAGAIN; - } - - memzero(buffer, sizeof(char*)); - strcpy(buffer + sizeof(char*), name); - - gr->gr_name = buffer + sizeof(char*); - gr->gr_gid = gid; - gr->gr_passwd = (char*) "*"; /* locked */ - gr->gr_mem = (char**) buffer; - - *errnop = 0; - return NSS_STATUS_SUCCESS; - -not_found: - *errnop = 0; - return NSS_STATUS_NOTFOUND; - -fail: - *errnop = -r; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_mymachines_getgrgid_r( - gid_t gid, - struct group *gr, - char *buffer, size_t buflen, - int *errnop) { - - _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; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - if (!gid_is_valid(gid)) { - r = -EINVAL; - goto fail; - } - - /* We consider all gids < 65536 host gids */ - if (gid < HOST_GID_LIMIT) - goto not_found; - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fail; - - r = sd_bus_call_method(bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "MapToMachineGroup", - &error, - &reply, - "u", - (uint32_t) gid); - if (r < 0) { - if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) - goto not_found; - - goto fail; - } - - r = sd_bus_message_read(reply, "sou", &machine, &object, &mapped); - if (r < 0) - goto fail; - - if (mapped == gid) - goto not_found; - - if (buflen < sizeof(char*) + 1) { - *errnop = ENOMEM; - return NSS_STATUS_TRYAGAIN; - } - - memzero(buffer, sizeof(char*)); - if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { - *errnop = ENOMEM; - return NSS_STATUS_TRYAGAIN; - } - - gr->gr_name = buffer + sizeof(char*); - gr->gr_gid = gid; - gr->gr_passwd = (char*) "*"; /* locked */ - gr->gr_mem = (char**) buffer; - - *errnop = 0; - return NSS_STATUS_SUCCESS; - -not_found: - *errnop = 0; - return NSS_STATUS_NOTFOUND; - -fail: - *errnop = -r; - return NSS_STATUS_UNAVAIL; -} diff --git a/src/nss-mymachines/nss-mymachines.sym b/src/nss-mymachines/nss-mymachines.sym deleted file mode 100644 index 0728ac3ba7..0000000000 --- a/src/nss-mymachines/nss-mymachines.sym +++ /dev/null @@ -1,21 +0,0 @@ -/*** - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. -***/ - -{ -global: - _nss_mymachines_gethostbyname_r; - _nss_mymachines_gethostbyname2_r; - _nss_mymachines_gethostbyname3_r; - _nss_mymachines_gethostbyname4_r; - _nss_mymachines_getpwnam_r; - _nss_mymachines_getpwuid_r; - _nss_mymachines_getgrnam_r; - _nss_mymachines_getgrgid_r; -local: *; -}; diff --git a/src/nss-resolve/Makefile b/src/nss-resolve/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/nss-resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c deleted file mode 100644 index 5ce10f1cbd..0000000000 --- a/src/nss-resolve/nss-resolve.c +++ /dev/null @@ -1,675 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-bus.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 "signal-util.h" - -NSS_GETHOSTBYNAME_PROTOTYPES(resolve); -NSS_GETHOSTBYADDR_PROTOTYPES(resolve); - -#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) - -typedef void (*voidfunc_t)(void); - -static voidfunc_t find_fallback(const char *module, const char *symbol) { - void *dl; - - /* Try to find a fallback NSS module symbol */ - - dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE); - if (!dl) - return NULL; - - return dlsym(dl, symbol); -} - -static bool bus_error_shall_fallback(sd_bus_error *e) { - return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) || - sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) || - sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED); -} - -static int count_addresses(sd_bus_message *m, int af, const char **canonical) { - int c = 0, r; - - assert(m); - assert(canonical); - - r = sd_bus_message_enter_container(m, 'a', "(iiay)"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) { - int family, ifindex; - - assert_cc(sizeof(int32_t) == sizeof(int)); - - r = sd_bus_message_read(m, "ii", &ifindex, &family); - if (r < 0) - return r; - - r = sd_bus_message_skip(m, "ay"); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - if (af != AF_UNSPEC && family != af) - continue; - - c++; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - r = sd_bus_message_read(m, "s", canonical); - if (r < 0) - return r; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - return c; -} - -enum nss_status _nss_resolve_gethostbyname4_r( - const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp) { - - _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - const char *canonical = NULL; - size_t l, ms, idx; - char *r_name; - int c, r, i = 0; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(pat); - assert(buffer); - assert(errnop); - assert(h_errnop); - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fallback; - - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveHostname"); - if (r < 0) - goto fail; - - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - if (bus_error_shall_fallback(&error)) - goto fallback; - - goto fail; - } - - c = count_addresses(reply, AF_UNSPEC, &canonical); - if (c < 0) { - r = c; - goto fail; - } - if (c == 0) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - if (isempty(canonical)) - canonical = name; - - l = strlen(canonical); - ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; - if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; - return NSS_STATUS_TRYAGAIN; - } - - /* First, append name */ - r_name = buffer; - memcpy(r_name, canonical, l+1); - idx = ALIGN(l+1); - - /* Second, append addresses */ - r_tuple_first = (struct gaih_addrtuple*) (buffer + idx); - - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); - if (r < 0) - goto fail; - - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { - int family, ifindex; - const void *a; - size_t sz; - - assert_cc(sizeof(int32_t) == sizeof(int)); - - r = sd_bus_message_read(reply, "ii", &ifindex, &family); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (!IN_SET(family, AF_INET, AF_INET6)) - continue; - - if (sz != FAMILY_ADDRESS_SIZE(family)) { - r = -EINVAL; - goto fail; - } - - r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); - r_tuple->name = r_name; - r_tuple->family = family; - r_tuple->scopeid = ifindex; - memcpy(r_tuple->addr, a, sz); - - idx += ALIGN(sizeof(struct gaih_addrtuple)); - i++; - } - if (r < 0) - goto fail; - - assert(i == c); - assert(idx == ms); - - if (*pat) - **pat = *r_tuple_first; - else - *pat = r_tuple_first; - - if (ttlp) - *ttlp = 0; - - /* Explicitly reset all error variables */ - *errnop = 0; - *h_errnop = NETDB_SUCCESS; - h_errno = 0; - - return NSS_STATUS_SUCCESS; - -fallback: - { - _nss_gethostbyname4_r_t fallback; - - fallback = (_nss_gethostbyname4_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r"); - - if (fallback) - return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp); - } - -fail: - *errnop = -r; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_resolve_gethostbyname3_r( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp) { - - _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - size_t l, idx, ms, alen; - const char *canonical; - int c, r, i = 0; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(name); - assert(result); - assert(buffer); - assert(errnop); - assert(h_errnop); - - if (af == AF_UNSPEC) - af = AF_INET; - - if (af != AF_INET && af != AF_INET6) { - r = -EAFNOSUPPORT; - goto fail; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fallback; - - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveHostname"); - if (r < 0) - goto fail; - - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - if (bus_error_shall_fallback(&error)) - goto fallback; - - goto fail; - } - - c = count_addresses(reply, af, &canonical); - if (c < 0) { - r = c; - goto fail; - } - if (c == 0) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - if (isempty(canonical)) - canonical = name; - - alen = FAMILY_ADDRESS_SIZE(af); - l = strlen(canonical); - - ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); - - if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; - return NSS_STATUS_TRYAGAIN; - } - - /* First, append name */ - r_name = buffer; - memcpy(r_name, canonical, l+1); - idx = ALIGN(l+1); - - /* Second, create empty aliases array */ - r_aliases = buffer + idx; - ((char**) r_aliases)[0] = NULL; - idx += sizeof(char*); - - /* Third, append addresses */ - r_addr = buffer + idx; - - r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); - if (r < 0) - goto fail; - - while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { - int ifindex, family; - const void *a; - size_t sz; - - r = sd_bus_message_read(reply, "ii", &ifindex, &family); - if (r < 0) - goto fail; - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - r = sd_bus_message_read_array(reply, 'y', &a, &sz); - if (r < 0) - goto fail; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto fail; - - if (family != af) - continue; - - if (sz != alen) { - r = -EINVAL; - goto fail; - } - - memcpy(r_addr + i*ALIGN(alen), a, alen); - i++; - } - if (r < 0) - goto fail; - - assert(i == c); - idx += c * ALIGN(alen); - - /* Fourth, append address pointer array */ - r_addr_list = buffer + idx; - for (i = 0; i < c; i++) - ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen); - - ((char**) r_addr_list)[i] = NULL; - idx += (c+1) * sizeof(char*); - - assert(idx == ms); - - result->h_name = r_name; - result->h_aliases = (char**) r_aliases; - result->h_addrtype = af; - result->h_length = alen; - result->h_addr_list = (char**) r_addr_list; - - /* Explicitly reset all error variables */ - *errnop = 0; - *h_errnop = NETDB_SUCCESS; - h_errno = 0; - - if (ttlp) - *ttlp = 0; - - if (canonp) - *canonp = r_name; - - return NSS_STATUS_SUCCESS; - -fallback: - { - _nss_gethostbyname3_r_t fallback; - - fallback = (_nss_gethostbyname3_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r"); - if (fallback) - return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp); - } - -fail: - *errnop = -r; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; -} - -enum nss_status _nss_resolve_gethostbyaddr2_r( - const void* addr, socklen_t len, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp) { - - _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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - unsigned c = 0, i = 0; - size_t ms = 0, idx; - const char *n; - int r, ifindex; - - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); - - assert(addr); - assert(result); - assert(buffer); - assert(errnop); - assert(h_errnop); - - if (!IN_SET(af, AF_INET, AF_INET6)) { - *errnop = EAFNOSUPPORT; - *h_errnop = NO_DATA; - return NSS_STATUS_UNAVAIL; - } - - if (len != FAMILY_ADDRESS_SIZE(af)) { - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; - } - - r = sd_bus_open_system(&bus); - if (r < 0) - goto fallback; - - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveAddress"); - if (r < 0) - goto fail; - - r = sd_bus_message_set_auto_start(req, false); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "ii", 0, af); - if (r < 0) - goto fail; - - r = sd_bus_message_append_array(req, 'y', addr, len); - if (r < 0) - goto fail; - - r = sd_bus_message_append(req, "t", (uint64_t) 0); - if (r < 0) - goto fail; - - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - if (bus_error_shall_fallback(&error)) - goto fallback; - - - *errnop = -r; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; - } - - r = sd_bus_message_enter_container(reply, 'a', "(is)"); - if (r < 0) - goto fail; - - while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - - if (ifindex < 0) { - r = -EINVAL; - goto fail; - } - - c++; - ms += ALIGN(strlen(n) + 1); - } - if (r < 0) - goto fail; - - r = sd_bus_message_rewind(reply, false); - if (r < 0) - return r; - - if (c <= 0) { - *errnop = ESRCH; - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } - - ms += ALIGN(len) + /* the address */ - 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ - c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ - - if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; - return NSS_STATUS_TRYAGAIN; - } - - /* First, place address */ - r_addr = buffer; - memcpy(r_addr, addr, len); - idx = ALIGN(len); - - /* Second, place address list */ - r_addr_list = buffer + idx; - ((char**) r_addr_list)[0] = r_addr; - ((char**) r_addr_list)[1] = NULL; - idx += sizeof(char*) * 2; - - /* Third, reserve space for the aliases array */ - r_aliases = buffer + idx; - idx += sizeof(char*) * c; - - /* Fourth, place aliases */ - i = 0; - r_name = buffer + idx; - while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) { - char *p; - size_t l; - - l = strlen(n); - p = buffer + idx; - memcpy(p, n, l+1); - - if (i > 0) - ((char**) r_aliases)[i-1] = p; - i++; - - idx += ALIGN(l+1); - } - if (r < 0) - goto fail; - - ((char**) r_aliases)[c-1] = NULL; - assert(idx == ms); - - result->h_name = r_name; - result->h_aliases = (char**) r_aliases; - result->h_addrtype = af; - result->h_length = len; - result->h_addr_list = (char**) r_addr_list; - - if (ttlp) - *ttlp = 0; - - /* Explicitly reset all error variables */ - *errnop = 0; - *h_errnop = NETDB_SUCCESS; - h_errno = 0; - - return NSS_STATUS_SUCCESS; - -fallback: - { - _nss_gethostbyaddr2_r_t fallback; - - fallback = (_nss_gethostbyaddr2_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r"); - - if (fallback) - return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp); - } - -fail: - *errnop = -r; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; -} - -NSS_GETHOSTBYNAME_FALLBACKS(resolve); -NSS_GETHOSTBYADDR_FALLBACKS(resolve); diff --git a/src/nss-resolve/nss-resolve.sym b/src/nss-resolve/nss-resolve.sym deleted file mode 100644 index df8dff2a20..0000000000 --- a/src/nss-resolve/nss-resolve.sym +++ /dev/null @@ -1,19 +0,0 @@ -/*** - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. -***/ - -{ -global: - _nss_resolve_gethostbyname_r; - _nss_resolve_gethostbyname2_r; - _nss_resolve_gethostbyname3_r; - _nss_resolve_gethostbyname4_r; - _nss_resolve_gethostbyaddr_r; - _nss_resolve_gethostbyaddr2_r; -local: *; -}; diff --git a/src/path/Makefile b/src/path/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/path/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/path/path.c b/src/path/path.c deleted file mode 100644 index 61d877fcf8..0000000000 --- a/src/path/path.c +++ /dev/null @@ -1,198 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-path.h" - -#include "alloc-util.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" - -static const char *arg_suffix = NULL; - -static const char* const path_table[_SD_PATH_MAX] = { - [SD_PATH_TEMPORARY] = "temporary", - [SD_PATH_TEMPORARY_LARGE] = "temporary-large", - [SD_PATH_SYSTEM_BINARIES] = "system-binaries", - [SD_PATH_SYSTEM_INCLUDE] = "system-include", - [SD_PATH_SYSTEM_LIBRARY_PRIVATE] = "system-library-private", - [SD_PATH_SYSTEM_LIBRARY_ARCH] = "system-library-arch", - [SD_PATH_SYSTEM_SHARED] = "system-shared", - [SD_PATH_SYSTEM_CONFIGURATION_FACTORY] = "system-configuration-factory", - [SD_PATH_SYSTEM_STATE_FACTORY] = "system-state-factory", - [SD_PATH_SYSTEM_CONFIGURATION] = "system-configuration", - [SD_PATH_SYSTEM_RUNTIME] = "system-runtime", - [SD_PATH_SYSTEM_RUNTIME_LOGS] = "system-runtime-logs", - [SD_PATH_SYSTEM_STATE_PRIVATE] = "system-state-private", - [SD_PATH_SYSTEM_STATE_LOGS] = "system-state-logs", - [SD_PATH_SYSTEM_STATE_CACHE] = "system-state-cache", - [SD_PATH_SYSTEM_STATE_SPOOL] = "system-state-spool", - [SD_PATH_USER_BINARIES] = "user-binaries", - [SD_PATH_USER_LIBRARY_PRIVATE] = "user-library-private", - [SD_PATH_USER_LIBRARY_ARCH] = "user-library-arch", - [SD_PATH_USER_SHARED] = "user-shared", - [SD_PATH_USER_CONFIGURATION] = "user-configuration", - [SD_PATH_USER_RUNTIME] = "user-runtime", - [SD_PATH_USER_STATE_CACHE] = "user-state-cache", - [SD_PATH_USER] = "user", - [SD_PATH_USER_DOCUMENTS] = "user-documents", - [SD_PATH_USER_MUSIC] = "user-music", - [SD_PATH_USER_PICTURES] = "user-pictures", - [SD_PATH_USER_VIDEOS] = "user-videos", - [SD_PATH_USER_DOWNLOAD] = "user-download", - [SD_PATH_USER_PUBLIC] = "user-public", - [SD_PATH_USER_TEMPLATES] = "user-templates", - [SD_PATH_USER_DESKTOP] = "user-desktop", - [SD_PATH_SEARCH_BINARIES] = "search-binaries", - [SD_PATH_SEARCH_LIBRARY_PRIVATE] = "search-library-private", - [SD_PATH_SEARCH_LIBRARY_ARCH] = "search-library-arch", - [SD_PATH_SEARCH_SHARED] = "search-shared", - [SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory", - [SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory", - [SD_PATH_SEARCH_CONFIGURATION] = "search-configuration", -}; - -static int list_homes(void) { - uint64_t i = 0; - int r = 0; - - for (i = 0; i < ELEMENTSOF(path_table); i++) { - _cleanup_free_ char *p = NULL; - int q; - - q = sd_path_home(i, arg_suffix, &p); - if (q == -ENXIO) - continue; - if (q < 0) { - log_error_errno(r, "Failed to query %s: %m", path_table[i]); - r = q; - continue; - } - - printf("%s: %s\n", path_table[i], p); - } - - return r; -} - -static int print_home(const char *n) { - uint64_t i = 0; - int r; - - for (i = 0; i < ELEMENTSOF(path_table); i++) { - if (streq(path_table[i], n)) { - _cleanup_free_ char *p = NULL; - - r = sd_path_home(i, arg_suffix, &p); - if (r < 0) - return log_error_errno(r, "Failed to query %s: %m", n); - - printf("%s\n", p); - return 0; - } - } - - log_error("Path %s not known.", n); - return -EOPNOTSUPP; -} - -static void help(void) { - printf("%s [OPTIONS...] [NAME...]\n\n" - "Show system and user paths.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --suffix=SUFFIX Suffix to append to paths\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_SUFFIX, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "suffix", required_argument, NULL, ARG_SUFFIX }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_SUFFIX: - arg_suffix = optarg; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char* argv[]) { - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (argc > optind) { - int i, q; - - for (i = optind; i < argc; i++) { - q = print_home(argv[i]); - if (q < 0) - r = q; - } - } else - r = list_homes(); - - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/quotacheck/Makefile b/src/quotacheck/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/quotacheck/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c deleted file mode 100644 index 6d8c05f046..0000000000 --- a/src/quotacheck/quotacheck.c +++ /dev/null @@ -1,124 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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; - -static int parse_proc_cmdline_item(const char *key, const char *value) { - - if (streq(key, "quotacheck.mode") && value) { - - if (streq(value, "auto")) - arg_force = arg_skip = false; - else if (streq(value, "force")) - arg_force = true; - else if (streq(value, "skip")) - arg_skip = true; - else - log_warning("Invalid quotacheck.mode= parameter '%s'. Ignoring.", value); - } - -#ifdef HAVE_SYSV_COMPAT - else if (streq(key, "forcequotacheck") && !value) { - log_warning("Please use 'quotacheck.mode=force' rather than 'forcequotacheck' on the kernel command line."); - arg_force = true; - } -#endif - - return 0; -} - -static void test_files(void) { - -#ifdef HAVE_SYSV_COMPAT - if (access("/forcequotacheck", F_OK) >= 0) { - log_error("Please pass 'quotacheck.mode=force' on the kernel command line rather than creating /forcequotacheck on the root file system."); - arg_force = true; - } -#endif -} - -int main(int argc, char *argv[]) { - - static const char * const cmdline[] = { - QUOTACHECK, - "-anug", - NULL - }; - - pid_t pid; - int r; - - if (argc > 1) { - log_error("This program takes no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - - test_files(); - - if (!arg_force) { - if (arg_skip) - return EXIT_SUCCESS; - - if (access("/run/systemd/quotacheck", F_OK) < 0) - return EXIT_SUCCESS; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "fork(): %m"); - return EXIT_FAILURE; - } else if (pid == 0) { - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - execv(cmdline[0], (char**) cmdline); - _exit(1); /* Operational error */ - } - - r = wait_for_terminate_and_warn("quotacheck", pid, true); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/random-seed/Makefile b/src/random-seed/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/random-seed/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c deleted file mode 100644 index 6748bb9dd3..0000000000 --- a/src/random-seed/random-seed.c +++ /dev/null @@ -1,176 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "log.h" -#include "mkdir.h" -#include "string-util.h" -#include "util.h" - -#define POOL_SIZE_MIN 512 - -int main(int argc, char *argv[]) { - _cleanup_close_ int seed_fd = -1, random_fd = -1; - _cleanup_free_ void* buf = NULL; - size_t buf_size = 0; - ssize_t k; - int r, open_rw_error; - FILE *f; - bool refresh_seed_file = true; - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - /* Read pool size, if possible */ - f = fopen("/proc/sys/kernel/random/poolsize", "re"); - if (f) { - if (fscanf(f, "%zu", &buf_size) > 0) - /* poolsize is in bits on 2.6, but we want bytes */ - buf_size /= 8; - - fclose(f); - } - - if (buf_size <= POOL_SIZE_MIN) - buf_size = POOL_SIZE_MIN; - - buf = malloc(buf_size); - if (!buf) { - r = log_oom(); - goto finish; - } - - r = mkdir_parents_label(RANDOM_SEED, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); - goto finish; - } - - /* When we load the seed we read it and write it to the device - * and then immediately update the saved seed with new data, - * to make sure the next boot gets seeded differently. */ - - if (streq(argv[1], "load")) { - - seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); - open_rw_error = -errno; - if (seed_fd < 0) { - refresh_seed_file = false; - - seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (seed_fd < 0) { - bool missing = errno == ENOENT; - - log_full_errno(missing ? LOG_DEBUG : LOG_ERR, - open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m"); - r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR, - errno, "Failed to open " RANDOM_SEED " for reading: %m"); - if (missing) - r = 0; - - goto finish; - } - } - - random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); - if (random_fd < 0) { - random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600); - if (random_fd < 0) { - r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); - goto finish; - } - } - - k = loop_read(seed_fd, buf, buf_size, false); - if (k < 0) - r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m"); - else if (k == 0) { - r = 0; - log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding."); - } else { - (void) lseek(seed_fd, 0, SEEK_SET); - - r = loop_write(random_fd, buf, (size_t) k, false); - if (r < 0) - log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); - } - - } else if (streq(argv[1], "save")) { - - seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); - if (seed_fd < 0) { - r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); - goto finish; - } - - random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (random_fd < 0) { - r = log_error_errno(errno, "Failed to open /dev/urandom: %m"); - goto finish; - } - - } else { - log_error("Unknown verb '%s'.", argv[1]); - r = -EINVAL; - goto finish; - } - - if (refresh_seed_file) { - - /* This is just a safety measure. Given that we are root and - * most likely created the file ourselves the mode and owner - * should be correct anyway. */ - (void) fchmod(seed_fd, 0600); - (void) fchown(seed_fd, 0, 0); - - k = loop_read(random_fd, buf, buf_size, false); - if (k < 0) { - r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m"); - goto finish; - } - if (k == 0) { - log_error("Got EOF while reading from /dev/urandom."); - r = -EIO; - goto finish; - } - - r = loop_write(seed_fd, buf, (size_t) k, false); - if (r < 0) - log_error_errno(r, "Failed to write new random seed file: %m"); - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/rc-local-generator/Makefile b/src/rc-local-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/rc-local-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c deleted file mode 100644 index 618bbe428d..0000000000 --- a/src/rc-local-generator/rc-local-generator.c +++ /dev/null @@ -1,101 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2011 Michal Schmidt - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "log.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" -#endif - -#ifndef RC_LOCAL_SCRIPT_PATH_STOP -#define RC_LOCAL_SCRIPT_PATH_STOP "/sbin/halt.local" -#endif - -static const char *arg_dest = "/tmp"; - -static int add_symlink(const char *service, const char *where) { - _cleanup_free_ char *from = NULL, *to = NULL; - int r; - - assert(service); - assert(where); - - from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service, NULL); - if (!from) - return log_oom(); - - to = strjoin(arg_dest, "/", where, ".wants/", service, NULL); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); - - r = symlink(from, to); - if (r < 0) { - if (errno == EEXIST) - return 0; - - return log_error_errno(errno, "Failed to create symlink %s: %m", to); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r = EXIT_SUCCESS; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[1]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - if (access(RC_LOCAL_SCRIPT_PATH_START, X_OK) >= 0) { - log_debug("Automatically adding rc-local.service."); - - if (add_symlink("rc-local.service", "multi-user.target") < 0) - r = EXIT_FAILURE; - } - - if (access(RC_LOCAL_SCRIPT_PATH_STOP, X_OK) >= 0) { - log_debug("Automatically adding halt-local.service."); - - if (add_symlink("halt-local.service", "final.target") < 0) - r = EXIT_FAILURE; - } - - return r; -} diff --git a/src/remount-fs/Makefile b/src/remount-fs/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/remount-fs/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c deleted file mode 100644 index 6468d1eecd..0000000000 --- a/src/remount-fs/remount-fs.c +++ /dev/null @@ -1,155 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "exit-status.h" -#include "log.h" -#include "mount-setup.h" -#include "mount-util.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.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[]) { - _cleanup_hashmap_free_free_ Hashmap *pids = NULL; - _cleanup_endmntent_ FILE *f = NULL; - struct mntent* me; - int r; - - if (argc > 1) { - log_error("This program takes no argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - f = setmntent("/etc/fstab", "r"); - if (!f) { - if (errno == ENOENT) { - r = 0; - goto finish; - } - - r = log_error_errno(errno, "Failed to open /etc/fstab: %m"); - goto finish; - } - - pids = hashmap_new(NULL); - if (!pids) { - r = log_oom(); - goto finish; - } - - while ((me = getmntent(f))) { - pid_t pid; - int k; - char *s; - - /* Remount the root fs, /usr and all API VFS */ - if (!mount_point_is_api(me->mnt_dir) && - !path_equal(me->mnt_dir, "/") && - !path_equal(me->mnt_dir, "/usr")) - continue; - - log_debug("Remounting %s", me->mnt_dir); - - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "Failed to fork: %m"); - goto finish; - } - - if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - - 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); - } - - /* Parent */ - - s = strdup(me->mnt_dir); - if (!s) { - r = log_oom(); - goto finish; - } - - k = hashmap_put(pids, PID_TO_PTR(pid), s); - if (k < 0) { - free(s); - r = log_oom(); - goto finish; - } - } - - r = 0; - while (!hashmap_isempty(pids)) { - siginfo_t si = {}; - char *s; - - if (waitid(P_ALL, 0, &si, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - r = log_error_errno(errno, "waitid() failed: %m"); - goto finish; - } - - 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) - log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status); - else - log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status)); - - r = -ENOEXEC; - } - - free(s); - } - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/reply-password/Makefile b/src/reply-password/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/reply-password/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c deleted file mode 100644 index 17eab9772e..0000000000 --- a/src/reply-password/reply-password.c +++ /dev/null @@ -1,96 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "socket-util.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) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - }; - - assert(fd >= 0); - assert(socket_name); - assert(packet); - - strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - - if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return log_error_errno(errno, "Failed to send: %m"); - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_close_ int fd = -1; - char packet[LINE_MAX]; - size_t length; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (argc != 3) { - log_error("Wrong number of arguments."); - return EXIT_FAILURE; - } - - if (streq(argv[1], "1")) { - - packet[0] = '+'; - if (!fgets(packet+1, sizeof(packet)-1, stdin)) { - r = log_error_errno(errno, "Failed to read password: %m"); - goto finish; - } - - truncate_nl(packet+1); - length = 1 + strlen(packet+1) + 1; - } else if (streq(argv[1], "0")) { - packet[0] = '-'; - 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) { - r = log_error_errno(errno, "socket() failed: %m"); - goto finish; - } - - r = send_on_socket(fd, argv[2], packet, length); - -finish: - memory_erase(packet, sizeof(packet)); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/resolve/.gitignore b/src/resolve/.gitignore deleted file mode 100644 index f0835923b7..0000000000 --- a/src/resolve/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/resolved-gperf.c -/resolved.conf -/dns_type-from-name.gperf -/dns_type-from-name.h -/dns_type-list.txt -/dns_type-to-name.h diff --git a/src/resolve/Makefile b/src/resolve/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/resolve/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/resolve/RFCs b/src/resolve/RFCs deleted file mode 100644 index 09c85f9518..0000000000 --- a/src/resolve/RFCs +++ /dev/null @@ -1,59 +0,0 @@ -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 deleted file mode 100644 index aaf5ed62c1..0000000000 --- a/src/resolve/dns-type.c +++ /dev/null @@ -1,332 +0,0 @@ -/*** - 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 . -***/ - -#include - -#include "dns-type.h" -#include "parse-util.h" -#include "string-util.h" - -typedef const struct { - uint16_t type; - const char *name; -} dns_type; - -static const struct dns_type_name * -lookup_dns_type (register const char *str, register unsigned int len); - -#include "dns_type-from-name.h" -#include "dns_type-to-name.h" - -int dns_type_from_string(const char *s) { - const struct dns_type_name *sc; - - assert(s); - - sc = lookup_dns_type(s, strlen(s)); - if (sc) - return sc->id; - - s = startswith_no_case(s, "TYPE"); - if (s) { - unsigned x; - - if (safe_atou(s, &x) >= 0 && - x <= UINT16_MAX) - return (int) x; - } - - return _DNS_TYPE_INVALID; -} - -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_zone_transer(uint16_t type) { - - /* Zone transfers, either normal or incremental */ - - return IN_SET(type, - DNS_TYPE_AXFR, - DNS_TYPE_IXFR); -} - -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 - * . */ - - 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); -} - -bool dns_type_needs_authentication(uint16_t type) { - - /* Returns true for all (non-obsolete) RR types where records are not useful if they aren't - * authenticated. I.e. everything that contains crypto keys. */ - - return IN_SET(type, - DNS_TYPE_CERT, - DNS_TYPE_SSHFP, - DNS_TYPE_IPSECKEY, - DNS_TYPE_DS, - DNS_TYPE_DNSKEY, - DNS_TYPE_TLSA, - DNS_TYPE_CDNSKEY, - DNS_TYPE_OPENPGPKEY, - DNS_TYPE_CAA); -} - -int dns_type_to_af(uint16_t t) { - switch (t) { - - case DNS_TYPE_A: - return AF_INET; - - case DNS_TYPE_AAAA: - return AF_INET6; - - case DNS_TYPE_ANY: - return AF_UNSPEC; - - default: - return -EINVAL; - } -} - -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; -} - -const char* tlsa_cert_usage_to_string(uint8_t cert_usage) { - - switch (cert_usage) { - - case 0: - return "CA constraint"; - - case 1: - return "Service certificate constraint"; - - case 2: - return "Trust anchor assertion"; - - case 3: - return "Domain-issued certificate"; - - case 4 ... 254: - return "Unassigned"; - - case 255: - return "Private use"; - } - - return NULL; /* clang cannot count that we covered everything */ -} - -const char* tlsa_selector_to_string(uint8_t selector) { - switch (selector) { - - case 0: - return "Full Certificate"; - - case 1: - return "SubjectPublicKeyInfo"; - - case 2 ... 254: - return "Unassigned"; - - case 255: - return "Private use"; - } - - return NULL; -} - -const char* tlsa_matching_type_to_string(uint8_t selector) { - - switch (selector) { - - case 0: - return "No hash used"; - - case 1: - return "SHA-256"; - - case 2: - return "SHA-512"; - - case 3 ... 254: - return "Unassigned"; - - case 255: - return "Private use"; - } - - return NULL; -} diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h deleted file mode 100644 index e675fe4ea3..0000000000 --- a/src/resolve/dns-type.h +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "macro.h" - -/* DNS record types, taken from - * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml. - */ -enum { - /* Normal records */ - DNS_TYPE_A = 0x01, - DNS_TYPE_NS, - DNS_TYPE_MD, - DNS_TYPE_MF, - DNS_TYPE_CNAME, - DNS_TYPE_SOA, - DNS_TYPE_MB, - DNS_TYPE_MG, - DNS_TYPE_MR, - DNS_TYPE_NULL, - DNS_TYPE_WKS, - DNS_TYPE_PTR, - DNS_TYPE_HINFO, - DNS_TYPE_MINFO, - DNS_TYPE_MX, - DNS_TYPE_TXT, - DNS_TYPE_RP, - DNS_TYPE_AFSDB, - DNS_TYPE_X25, - DNS_TYPE_ISDN, - DNS_TYPE_RT, - DNS_TYPE_NSAP, - DNS_TYPE_NSAP_PTR, - DNS_TYPE_SIG, - DNS_TYPE_KEY, - DNS_TYPE_PX, - DNS_TYPE_GPOS, - DNS_TYPE_AAAA, - DNS_TYPE_LOC, - DNS_TYPE_NXT, - DNS_TYPE_EID, - DNS_TYPE_NIMLOC, - DNS_TYPE_SRV, - DNS_TYPE_ATMA, - DNS_TYPE_NAPTR, - DNS_TYPE_KX, - DNS_TYPE_CERT, - DNS_TYPE_A6, - DNS_TYPE_DNAME, - DNS_TYPE_SINK, - DNS_TYPE_OPT, /* EDNS0 option */ - DNS_TYPE_APL, - DNS_TYPE_DS, - DNS_TYPE_SSHFP, - DNS_TYPE_IPSECKEY, - DNS_TYPE_RRSIG, - DNS_TYPE_NSEC, - DNS_TYPE_DNSKEY, - DNS_TYPE_DHCID, - DNS_TYPE_NSEC3, - DNS_TYPE_NSEC3PARAM, - DNS_TYPE_TLSA, - - DNS_TYPE_HIP = 0x37, - DNS_TYPE_NINFO, - DNS_TYPE_RKEY, - DNS_TYPE_TALINK, - DNS_TYPE_CDS, - DNS_TYPE_CDNSKEY, - DNS_TYPE_OPENPGPKEY, - - DNS_TYPE_SPF = 0x63, - DNS_TYPE_NID, - DNS_TYPE_L32, - DNS_TYPE_L64, - DNS_TYPE_LP, - DNS_TYPE_EUI48, - DNS_TYPE_EUI64, - - DNS_TYPE_TKEY = 0xF9, - DNS_TYPE_TSIG, - DNS_TYPE_IXFR, - DNS_TYPE_AXFR, - DNS_TYPE_MAILB, - DNS_TYPE_MAILA, - DNS_TYPE_ANY, - DNS_TYPE_URI, - DNS_TYPE_CAA, - DNS_TYPE_TA = 0x8000, - DNS_TYPE_DLV, - - _DNS_TYPE_MAX, - _DNS_TYPE_INVALID = -1 -}; - -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 -}; - -#define _DNS_CLASS_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) -#define _DNS_TYPE_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) - -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_type_needs_authentication(uint16_t type); -bool dns_type_is_zone_transer(uint16_t type); -int dns_type_to_af(uint16_t type); - -bool dns_class_is_pseudo(uint16_t class); -bool dns_class_is_valid_rr(uint16_t class); - -/* TYPE?? follows http://tools.ietf.org/html/rfc3597#section-5 */ -const char *dns_type_to_string(int type); -int dns_type_from_string(const char *s); - -const char *dns_class_to_string(uint16_t class); -int dns_class_from_string(const char *name); - -/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.2 */ -const char *tlsa_cert_usage_to_string(uint8_t cert_usage); - -/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.3 */ -const char *tlsa_selector_to_string(uint8_t selector); - -/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */ -const char *tlsa_matching_type_to_string(uint8_t selector); - -/* https://tools.ietf.org/html/rfc6844#section-5.1 */ -#define CAA_FLAG_CRITICAL (1u << 7) diff --git a/src/resolve/org.freedesktop.resolve1.conf b/src/resolve/org.freedesktop.resolve1.conf deleted file mode 100644 index 25b09774e5..0000000000 --- a/src/resolve/org.freedesktop.resolve1.conf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/resolve/org.freedesktop.resolve1.service b/src/resolve/org.freedesktop.resolve1.service deleted file mode 100644 index 7ac5c323f0..0000000000 --- a/src/resolve/org.freedesktop.resolve1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.resolve1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.resolve1.service diff --git a/src/resolve/resolv.conf b/src/resolve/resolv.conf deleted file mode 100644 index b8034d6829..0000000000 --- a/src/resolve/resolv.conf +++ /dev/null @@ -1,11 +0,0 @@ -# This is a static resolv.conf file for connecting local clients to -# systemd-resolved via its DNS stub listener on 127.0.0.53. -# -# Third party programs must not access this file directly, but only through the -# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way, -# replace this symlink by a static file or a different symlink. -# -# See systemd-resolved.service(8) for details about the supported modes of -# operation for /etc/resolv.conf. - -nameserver 127.0.0.53 diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c deleted file mode 100644 index 6ae3750417..0000000000 --- a/src/resolve/resolve-tool.c +++ /dev/null @@ -1,2007 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "sd-bus.h" -#include "sd-netlink.h" - -#include "af-list.h" -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-util.h" -#include "escape.h" -#include "gcrypt-util.h" -#include "in-addr-util.h" -#include "netlink-util.h" -#include "pager.h" -#include "parse-util.h" -#include "resolved-def.h" -#include "resolved-dns-packet.h" -#include "strv.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 uint16_t arg_type = 0; -static uint16_t arg_class = 0; -static bool arg_legend = true; -static uint64_t arg_flags = 0; -static bool arg_no_pager = false; - -typedef enum ServiceFamily { - SERVICE_FAMILY_TCP, - SERVICE_FAMILY_UDP, - SERVICE_FAMILY_SCTP, - _SERVICE_FAMILY_INVALID = -1, -} ServiceFamily; -static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP; - -typedef enum RawType { - RAW_NONE, - RAW_PAYLOAD, - RAW_PACKET, -} RawType; -static RawType arg_raw = RAW_NONE; - -static enum { - MODE_RESOLVE_HOST, - MODE_RESOLVE_RECORD, - MODE_RESOLVE_SERVICE, - MODE_RESOLVE_OPENPGP, - MODE_RESOLVE_TLSA, - MODE_STATISTICS, - MODE_RESET_STATISTICS, - MODE_FLUSH_CACHES, - MODE_STATUS, -} arg_mode = MODE_RESOLVE_HOST; - -static ServiceFamily service_family_from_string(const char *s) { - if (s == NULL || streq(s, "tcp")) - return SERVICE_FAMILY_TCP; - if (streq(s, "udp")) - return SERVICE_FAMILY_UDP; - if (streq(s, "sctp")) - return SERVICE_FAMILY_SCTP; - return _SERVICE_FAMILY_INVALID; -} - -static const char* service_family_to_string(ServiceFamily service) { - switch(service) { - case SERVICE_FAMILY_TCP: - return "_tcp"; - case SERVICE_FAMILY_UDP: - return "_udp"; - case SERVICE_FAMILY_SCTP: - return "_sctp"; - default: - assert_not_reached("invalid service"); - } -} - -static void print_source(uint64_t flags, usec_t rtt) { - char rtt_str[FORMAT_TIMESTAMP_MAX]; - - if (!arg_legend) - return; - - if (flags == 0) - return; - - fputs("\n-- Information acquired via", stdout); - - if (flags != 0) - 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_MDNS_IPV4 ? "mDNS/IPv4" : "", - flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : ""); - - assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); - - printf(" in %s", rtt_str); - - 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_(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; - int r; - uint64_t flags; - usec_t ts; - - assert(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 (family %s, interface %s).", name, 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", - "ResolveHostname"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "isit", arg_ifindex, name, 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, "%s: resolve call failed: %s", name, bus_error_message(&error, r)); - - ts = now(CLOCK_MONOTONIC) - ts; - - 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; - size_t sz; - - 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_ifindex_to_string(family, a, ifindex, &pretty); - if (r < 0) - return log_error_errno(r, "Failed to print address for %s: %m", name); - - printf("%*s%s %s%s%s\n", - (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", - pretty, - isempty(ifname) ? "" : "%", ifname); - - 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, "st", &canonical, &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (!streq(name, canonical)) - printf("%*s%s (%s)\n", - (int) strlen(name), c == 0 ? name : "", c == 0 ? ":" : " ", - canonical); - - if (c == 0) { - log_error("%s: no addresses found", name); - return -ESRCH; - } - - print_source(flags, ts); - - return 0; -} - -static int resolve_address(sd_bus *bus, int family, const union in_addr_union *address, int ifindex) { - _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; - unsigned c = 0; - usec_t ts; - int r; - - assert(bus); - assert(IN_SET(family, AF_INET, AF_INET6)); - assert(address); - - if (ifindex <= 0) - ifindex = arg_ifindex; - - r = in_addr_ifindex_to_string(family, address, ifindex, &pretty); - if (r < 0) - return log_oom(); - - if (ifindex > 0 && !if_indextoname(ifindex, ifname)) - return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); - - log_debug("Resolving %s%s%s.", pretty, isempty(ifname) ? "" : "%", ifname); - - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveAddress"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "ii", ifindex, family); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(req, 'y', address, FAMILY_ADDRESS_SIZE(family)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "t", 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) { - log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); - return r; - } - - ts = now(CLOCK_MONOTONIC) - ts; - - r = sd_bus_message_enter_container(reply, 'a', "(is)"); - if (r < 0) - return bus_log_create_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "is")) > 0) { - const char *n; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "is", &ifindex, &n); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - ifname[0] = 0; - if (ifindex > 0 && !if_indextoname(ifindex, ifname)) - log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); - - printf("%*s%*s%*s%s %s\n", - (int) strlen(pretty), c == 0 ? pretty : "", - isempty(ifname) ? 0 : 1, c > 0 || isempty(ifname) ? "" : "%", - (int) strlen(ifname), c == 0 ? ifname : "", - c == 0 ? ":" : " ", - n); - - 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, "t", &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (c == 0) { - log_error("%s: no names found", pretty); - return -ESRCH; - } - - print_source(flags, ts); - - return 0; -} - -static int output_rr_packet(const void *d, size_t l, int ifindex) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - char ifname[IF_NAMESIZE] = ""; - - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - return log_oom(); - - p->refuse_compression = true; - - r = dns_packet_append_blob(p, d, l, NULL); - if (r < 0) - return log_oom(); - - r = dns_packet_read_rr(p, &rr, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to parse RR: %m"); - - if (arg_raw == RAW_PAYLOAD) { - void *data; - ssize_t k; - - k = dns_resource_record_payload(rr, &data); - if (k < 0) - return log_error_errno(k, "Cannot dump RR: %m"); - fwrite(data, 1, k, stdout); - } else { - const char *s; - - s = dns_resource_record_to_string(rr); - if (!s) - return log_oom(); - - if (ifindex > 0 && !if_indextoname(ifindex, ifname)) - log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); - - printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); - } - - return 0; -} - -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; - int r; - usec_t ts; - bool needs_authentication = false; - - assert(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(class), dns_type_to_string(type), isempty(ifname) ? "*" : ifname); - - r = sd_bus_message_new_method_call( - bus, - &req, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - "org.freedesktop.resolve1.Manager", - "ResolveRecord"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, class, type, 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) { - log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); - return r; - } - - ts = now(CLOCK_MONOTONIC) - ts; - - r = sd_bus_message_enter_container(reply, 'a', "(iqqay)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { - uint16_t c, t; - int ifindex; - const void *d; - size_t l; - - assert_cc(sizeof(int) == sizeof(int32_t)); - - r = sd_bus_message_read(reply, "iqq", &ifindex, &c, &t); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read_array(reply, 'y', &d, &l); - 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 (arg_raw == RAW_PACKET) { - uint64_t u64 = htole64(l); - - fwrite(&u64, sizeof(u64), 1, stdout); - fwrite(d, 1, l, stdout); - } else { - r = output_rr_packet(d, l, ifindex); - if (r < 0) - return r; - } - - if (dns_type_needs_authentication(t)) - needs_authentication = true; - - n++; - } - 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, "t", &flags); - if (r < 0) - return bus_log_parse_error(r); - - if (n == 0) { - log_error("%s: no records found", name); - return -ESRCH; - } - - print_source(flags, ts); - - if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { - fflush(stdout); - - fprintf(stderr, "\n%s" - "WARNING: The resources shown contain cryptographic key data which could not be\n" - " authenticated. It is not suitable to authenticate any communication.\n" - " This is usually indication that DNSSEC authentication was not enabled\n" - " or is not available for the selected protocol or DNS servers.%s\n", - ansi_highlight_red(), - ansi_normal()); - } - - 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 (class == 0) - class = arg_class ?: DNS_CLASS_IN; - if (type == 0) - type = arg_type ?: DNS_TYPE_A; - - 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); - - name = empty_to_null(name); - type = empty_to_null(type); - - 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); - - canonical_name = empty_to_null(canonical_name); - canonical_type = empty_to_null(canonical_type); - - 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 resolve_openpgp(sd_bus *bus, const char *address) { - const char *domain, *full; - int r; - _cleanup_free_ char *hashed = NULL; - - assert(bus); - assert(address); - - domain = strrchr(address, '@'); - if (!domain) { - log_error("Address does not contain '@': \"%s\"", address); - return -EINVAL; - } else if (domain == address || domain[1] == '\0') { - log_error("Address starts or ends with '@': \"%s\"", address); - return -EINVAL; - } - domain++; - - r = string_hashsum_sha224(address, domain - 1 - address, &hashed); - if (r < 0) - return log_error_errno(r, "Hashing failed: %m"); - - full = strjoina(hashed, "._openpgpkey.", domain); - log_debug("Looking up \"%s\".", full); - - return resolve_record(bus, full, - arg_class ?: DNS_CLASS_IN, - arg_type ?: DNS_TYPE_OPENPGPKEY); -} - -static int resolve_tlsa(sd_bus *bus, const char *address) { - const char *port; - uint16_t port_num = 443; - _cleanup_free_ char *full = NULL; - int r; - - assert(bus); - assert(address); - - port = strrchr(address, ':'); - if (port) { - r = safe_atou16(port + 1, &port_num); - if (r < 0 || port_num == 0) - return log_error_errno(r, "Invalid port \"%s\".", port + 1); - - address = strndupa(address, port - address); - } - - r = asprintf(&full, "_%u.%s.%s", - port_num, - service_family_to_string(arg_service_family), - address); - if (r < 0) - return log_oom(); - - log_debug("Looking up \"%s\".", full); - - return resolve_record(bus, full, - arg_class ?: DNS_CLASS_IN, - arg_type ?: DNS_TYPE_TLSA); -} - -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 Verdicts%s\n" - " Secure: %" PRIu64 "\n" - " Insecure: %" PRIu64 "\n" - " Bogus: %" PRIu64 "\n" - " Indeterminate: %" 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 int flush_caches(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", - "FlushCaches", - &error, - NULL, - NULL); - if (r < 0) - return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r)); - - return 0; -} - -static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char ***l = userdata; - int r; - - assert(bus); - assert(member); - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "(iay)"); - if (r < 0) - return r; - - for (;;) { - const void *a; - char *pretty; - int family; - size_t sz; - - r = sd_bus_message_enter_container(m, 'r', "iay"); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_read(m, "i", &family); - if (r < 0) - return r; - - r = sd_bus_message_read_array(m, 'y', &a, &sz); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("Unexpected family, ignoring."); - continue; - } - - if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_debug("Address size mismatch, ignoring."); - continue; - } - - r = in_addr_to_string(family, a, &pretty); - if (r < 0) - return r; - - r = strv_consume(l, pretty); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char ***l = userdata; - int r; - - assert(bus); - assert(member); - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "(sb)"); - if (r < 0) - return r; - - for (;;) { - const char *domain; - int route_only; - char *pretty; - - r = sd_bus_message_read(m, "(sb)", &domain, &route_only); - if (r < 0) - return r; - if (r == 0) - break; - - if (route_only) - pretty = strappend("~", domain); - else - pretty = strdup(domain); - if (!pretty) - return -ENOMEM; - - r = strv_consume(l, pretty); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empty_line) { - - struct link_info { - uint64_t scopes_mask; - char *llmnr; - char *mdns; - char *dnssec; - char **dns; - char **domains; - char **ntas; - int dnssec_supported; - } link_info = {}; - - static const struct bus_properties_map property_map[] = { - { "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) }, - { "DNS", "a(iay)", map_link_dns_servers, offsetof(struct link_info, dns) }, - { "Domains", "a(sb)", map_link_domains, offsetof(struct link_info, domains) }, - { "LLMNR", "s", NULL, offsetof(struct link_info, llmnr) }, - { "MulticastDNS", "s", NULL, offsetof(struct link_info, mdns) }, - { "DNSSEC", "s", NULL, offsetof(struct link_info, dnssec) }, - { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct link_info, ntas) }, - { "DNSSECSupported", "b", NULL, offsetof(struct link_info, dnssec_supported) }, - {} - }; - - _cleanup_free_ char *ifi = NULL, *p = NULL; - char ifname[IF_NAMESIZE] = ""; - char **i; - int r; - - assert(bus); - assert(ifindex > 0); - assert(empty_line); - - if (!name) { - if (!if_indextoname(ifindex, ifname)) - return log_error_errno(errno, "Failed to resolve interface name for %i: %m", ifindex); - - name = ifname; - } - - if (asprintf(&ifi, "%i", ifindex) < 0) - return log_oom(); - - r = sd_bus_path_encode("/org/freedesktop/resolve1/link", ifi, &p); - if (r < 0) - return log_oom(); - - r = bus_map_all_properties(bus, - "org.freedesktop.resolve1", - p, - property_map, - &link_info); - if (r < 0) { - log_error_errno(r, "Failed to get link data for %i: %m", ifindex); - goto finish; - } - - pager_open(arg_no_pager, false); - - if (*empty_line) - fputc('\n', stdout); - - printf("%sLink %i (%s)%s\n", - ansi_highlight(), ifindex, name, ansi_normal()); - - if (link_info.scopes_mask == 0) - printf(" Current Scopes: none\n"); - else - printf(" Current Scopes:%s%s%s%s%s\n", - link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "", - link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", - link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "", - link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "", - link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : ""); - - printf(" LLMNR setting: %s\n" - "MulticastDNS setting: %s\n" - " DNSSEC setting: %s\n" - " DNSSEC supported: %s\n", - strna(link_info.llmnr), - strna(link_info.mdns), - strna(link_info.dnssec), - yes_no(link_info.dnssec_supported)); - - STRV_FOREACH(i, link_info.dns) { - printf(" %s %s\n", - i == link_info.dns ? "DNS Servers:" : " ", - *i); - } - - STRV_FOREACH(i, link_info.domains) { - printf(" %s %s\n", - i == link_info.domains ? "DNS Domain:" : " ", - *i); - } - - STRV_FOREACH(i, link_info.ntas) { - printf(" %s %s\n", - i == link_info.ntas ? "DNSSEC NTA:" : " ", - *i); - } - - *empty_line = true; - - r = 0; - -finish: - strv_free(link_info.dns); - strv_free(link_info.domains); - free(link_info.llmnr); - free(link_info.mdns); - free(link_info.dnssec); - strv_free(link_info.ntas); - return r; -} - -static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char ***l = userdata; - int r; - - assert(bus); - assert(member); - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "(iiay)"); - if (r < 0) - return r; - - for (;;) { - const void *a; - char *pretty; - int family, ifindex; - size_t sz; - - r = sd_bus_message_enter_container(m, 'r', "iiay"); - if (r < 0) - return r; - if (r == 0) - break; - - r = sd_bus_message_read(m, "ii", &ifindex, &family); - if (r < 0) - return r; - - r = sd_bus_message_read_array(m, 'y', &a, &sz); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - if (ifindex != 0) /* only show the global ones here */ - continue; - - if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("Unexpected family, ignoring."); - continue; - } - - if (sz != FAMILY_ADDRESS_SIZE(family)) { - log_debug("Address size mismatch, ignoring."); - continue; - } - - r = in_addr_to_string(family, a, &pretty); - if (r < 0) - return r; - - r = strv_consume(l, pretty); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char ***l = userdata; - int r; - - assert(bus); - assert(member); - assert(m); - assert(l); - - r = sd_bus_message_enter_container(m, 'a', "(isb)"); - if (r < 0) - return r; - - for (;;) { - const char *domain; - int route_only, ifindex; - char *pretty; - - r = sd_bus_message_read(m, "(isb)", &ifindex, &domain, &route_only); - if (r < 0) - return r; - if (r == 0) - break; - - if (ifindex != 0) /* only show the global ones here */ - continue; - - if (route_only) - pretty = strappend("~", domain); - else - pretty = strdup(domain); - if (!pretty) - return -ENOMEM; - - r = strv_consume(l, pretty); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return 0; -} - -static int status_global(sd_bus *bus, bool *empty_line) { - - struct global_info { - char **dns; - char **domains; - char **ntas; - } global_info = {}; - - static const struct bus_properties_map property_map[] = { - { "DNS", "a(iiay)", map_global_dns_servers, offsetof(struct global_info, dns) }, - { "Domains", "a(isb)", map_global_domains, offsetof(struct global_info, domains) }, - { "DNSSECNegativeTrustAnchors", "as", NULL, offsetof(struct global_info, ntas) }, - {} - }; - - char **i; - int r; - - assert(bus); - assert(empty_line); - - r = bus_map_all_properties(bus, - "org.freedesktop.resolve1", - "/org/freedesktop/resolve1", - property_map, - &global_info); - if (r < 0) { - log_error_errno(r, "Failed to get global data: %m"); - goto finish; - } - - if (strv_isempty(global_info.dns) && strv_isempty(global_info.domains) && strv_isempty(global_info.ntas)) { - r = 0; - goto finish; - } - - pager_open(arg_no_pager, false); - - printf("%sGlobal%s\n", ansi_highlight(), ansi_normal()); - STRV_FOREACH(i, global_info.dns) { - printf(" %s %s\n", - i == global_info.dns ? "DNS Servers:" : " ", - *i); - } - - STRV_FOREACH(i, global_info.domains) { - printf(" %s %s\n", - i == global_info.domains ? "DNS Domain:" : " ", - *i); - } - - strv_sort(global_info.ntas); - STRV_FOREACH(i, global_info.ntas) { - printf(" %s %s\n", - i == global_info.ntas ? "DNSSEC NTA:" : " ", - *i); - } - - *empty_line = true; - - r = 0; - -finish: - strv_free(global_info.dns); - strv_free(global_info.domains); - strv_free(global_info.ntas); - - return r; -} - -static int status_all(sd_bus *bus) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - sd_netlink_message *i; - bool empty_line = false; - int r; - - assert(bus); - - r = status_global(bus, &empty_line); - if (r < 0) - return r; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); - - r = 0; - for (i = reply; i; i = sd_netlink_message_next(i)) { - const char *name; - int ifindex, q; - uint16_t type; - - q = sd_netlink_message_get_type(i, &type); - if (q < 0) - return rtnl_log_parse_error(q); - - if (type != RTM_NEWLINK) - continue; - - q = sd_rtnl_message_link_get_ifindex(i, &ifindex); - if (q < 0) - return rtnl_log_parse_error(q); - - if (ifindex == LOOPBACK_IFINDEX) - continue; - - q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); - if (q < 0) - return rtnl_log_parse_error(q); - - q = status_ifindex(bus, ifindex, name, &empty_line); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -static void help_protocol_types(void) { - if (arg_legend) - puts("Known protocol types:"); - puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6"); -} - -static void help_dns_types(void) { - const char *t; - int i; - - if (arg_legend) - puts("Known DNS RR types:"); - for (i = 0; i < _DNS_TYPE_MAX; i++) { - t = dns_type_to_string(i); - if (t) - puts(t); - } -} - -static void help_dns_classes(void) { - const char *t; - int i; - - if (arg_legend) - puts("Known DNS RR classes:"); - for (i = 0; i < _DNS_CLASS_MAX; i++) { - t = dns_class_to_string(i); - if (t) - puts(t); - } -} - -static void help(void) { - printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n" - "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n" - "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n" - "%1$s [OPTIONS...] --statistics\n" - "%1$s [OPTIONS...] --reset-statistics\n" - "\n" - "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i --interface=INTERFACE Look on interface\n" - " -p --protocol=PROTO|help Look via protocol\n" - " -t --type=TYPE|help Query RR with DNS type\n" - " -c --class=CLASS|help Query RR with DNS class\n" - " --service Resolve service (SRV)\n" - " --service-address=BOOL Resolve address for services (default: yes)\n" - " --service-txt=BOOL Resolve TXT records for services (default: yes)\n" - " --openpgp Query OpenPGP public key\n" - " --tlsa Query TLS public key\n" - " --cname=BOOL Follow CNAME redirects (default: yes)\n" - " --search=BOOL Use search domains for single-label names\n" - " (default: yes)\n" - " --raw[=payload|packet] Dump the answer as binary data\n" - " --legend=BOOL Print headers and additional info (default: yes)\n" - " --statistics Show resolver statistics\n" - " --reset-statistics Reset resolver statistics\n" - " --status Show link and server status\n" - " --flush-caches Flush all local DNS caches\n" - , 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_OPENPGP, - ARG_TLSA, - ARG_RAW, - ARG_SEARCH, - ARG_STATISTICS, - ARG_RESET_STATISTICS, - ARG_STATUS, - ARG_FLUSH_CACHES, - ARG_NO_PAGER, - }; - - 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", 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 }, - { "openpgp", no_argument, NULL, ARG_OPENPGP }, - { "tlsa", optional_argument, NULL, ARG_TLSA }, - { "raw", optional_argument, NULL, ARG_RAW }, - { "search", required_argument, NULL, ARG_SEARCH }, - { "statistics", no_argument, NULL, ARG_STATISTICS, }, - { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, - { "status", no_argument, NULL, ARG_STATUS }, - { "flush-caches", no_argument, NULL, ARG_FLUSH_CACHES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h46i:t:c:p:", options, NULL)) >= 0) - switch(c) { - - case 'h': - help(); - return 0; /* done */; - - case ARG_VERSION: - return version(); - - case '4': - arg_family = AF_INET; - break; - - case '6': - arg_family = AF_INET6; - break; - - case 'i': { - int ifi; - - if (parse_ifindex(optarg, &ifi) >= 0) - arg_ifindex = ifi; - else { - ifi = if_nametoindex(optarg); - if (ifi <= 0) - return log_error_errno(errno, "Unknown interface %s: %m", optarg); - - arg_ifindex = ifi; - } - - break; - } - - case 't': - if (streq(optarg, "help")) { - help_dns_types(); - return 0; - } - - r = dns_type_from_string(optarg); - if (r < 0) { - log_error("Failed to parse RR record type %s", optarg); - return r; - } - arg_type = (uint16_t) r; - assert((int) arg_type == r); - - arg_mode = MODE_RESOLVE_RECORD; - break; - - case 'c': - if (streq(optarg, "help")) { - help_dns_classes(); - return 0; - } - - 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: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --legend= argument"); - - arg_legend = r; - break; - - case 'p': - if (streq(optarg, "help")) { - help_protocol_types(); - return 0; - } else if (streq(optarg, "dns")) - arg_flags |= SD_RESOLVED_DNS; - else if (streq(optarg, "llmnr")) - arg_flags |= SD_RESOLVED_LLMNR; - else if (streq(optarg, "llmnr-ipv4")) - arg_flags |= SD_RESOLVED_LLMNR_IPV4; - else if (streq(optarg, "llmnr-ipv6")) - arg_flags |= SD_RESOLVED_LLMNR_IPV6; - else { - log_error("Unknown protocol specifier: %s", optarg); - return -EINVAL; - } - - break; - - case ARG_SERVICE: - arg_mode = MODE_RESOLVE_SERVICE; - break; - - case ARG_OPENPGP: - arg_mode = MODE_RESOLVE_OPENPGP; - break; - - case ARG_TLSA: - arg_mode = MODE_RESOLVE_TLSA; - arg_service_family = service_family_from_string(optarg); - if (arg_service_family < 0) { - log_error("Unknown service family \"%s\".", optarg); - return -EINVAL; - } - break; - - case ARG_RAW: - if (on_tty()) { - log_error("Refusing to write binary data to tty."); - return -ENOTTY; - } - - if (optarg == NULL || streq(optarg, "payload")) - arg_raw = RAW_PAYLOAD; - else if (streq(optarg, "packet")) - arg_raw = RAW_PACKET; - else { - log_error("Unknown --raw specifier \"%s\".", optarg); - return -EINVAL; - } - - arg_legend = false; - break; - - case ARG_CNAME: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --cname= argument."); - SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); - break; - - case ARG_SERVICE_ADDRESS: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --service-address= argument."); - SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); - break; - - case ARG_SERVICE_TXT: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --service-txt= argument."); - SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); - break; - - case ARG_SEARCH: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --search argument."); - SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); - break; - - case ARG_STATISTICS: - arg_mode = MODE_STATISTICS; - break; - - case ARG_RESET_STATISTICS: - arg_mode = MODE_RESET_STATISTICS; - break; - - case ARG_FLUSH_CACHES: - arg_mode = MODE_FLUSH_CACHES; - break; - - case ARG_STATUS: - arg_mode = MODE_STATUS; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_type == 0 && arg_class != 0) { - log_error("--class= may only be used in conjunction with --type=."); - return -EINVAL; - } - - if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) { - 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_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_bus_open_system(&bus); - if (r < 0) { - log_error_errno(r, "sd_bus_open_system: %m"); - goto finish; - } - - switch (arg_mode) { - - 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 = in_addr_ifindex_from_string_auto(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 { - log_error("Too many arguments."); - r = -EINVAL; - goto finish; - } - - break; - - case MODE_RESOLVE_OPENPGP: - if (argc < optind + 1) { - log_error("E-mail address required."); - r = -EINVAL; - goto finish; - - } - - r = 0; - while (optind < argc) { - int k; - - k = resolve_openpgp(bus, argv[optind++]); - if (k < 0) - r = k; - } - break; - - case MODE_RESOLVE_TLSA: - if (argc < optind + 1) { - log_error("Domain name required."); - r = -EINVAL; - goto finish; - - } - - r = 0; - while (optind < argc) { - int k; - - k = resolve_tlsa(bus, argv[optind++]); - if (k < 0) - r = k; - } - break; - - case MODE_STATISTICS: - if (argc > optind) { - log_error("Too many arguments."); - r = -EINVAL; - goto finish; - } - - r = show_statistics(bus); - break; - - case MODE_RESET_STATISTICS: - if (argc > optind) { - log_error("Too many arguments."); - r = -EINVAL; - goto finish; - } - - r = reset_statistics(bus); - break; - - case MODE_FLUSH_CACHES: - if (argc > optind) { - log_error("Too many arguments."); - r = -EINVAL; - goto finish; - } - - r = flush_caches(bus); - break; - - case MODE_STATUS: - - if (argc > optind) { - char **ifname; - bool empty_line = false; - - r = 0; - STRV_FOREACH(ifname, argv + optind) { - int ifindex, q; - - q = parse_ifindex(argv[optind], &ifindex); - if (q < 0) { - ifindex = if_nametoindex(argv[optind]); - if (ifindex <= 0) { - log_error_errno(errno, "Failed to resolve interface name: %s", argv[optind]); - continue; - } - } - - q = status_ifindex(bus, ifindex, NULL, &empty_line); - if (q < 0 && r >= 0) - r = q; - } - } else - r = status_all(bus); - - break; - } - -finish: - pager_close(); - - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c deleted file mode 100644 index 2ca65e6953..0000000000 --- a/src/resolve/resolved-bus.c +++ /dev/null @@ -1,1689 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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-dns-synthesize.h" -#include "resolved-link-bus.h" - -static int reply_query_state(DnsQuery *q) { - - switch (q->state) { - - case DNS_TRANSACTION_NO_SERVERS: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - - case DNS_TRANSACTION_TIMEOUT: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); - - case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: - return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); - - case DNS_TRANSACTION_INVALID_REPLY: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); - - case DNS_TRANSACTION_ERRNO: - return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m"); - - case DNS_TRANSACTION_ABORTED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); - - 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_NETWORK_DOWN: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down"); - - case DNS_TRANSACTION_NOT_FOUND: - /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we - * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ - return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); - - 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", dns_query_string(q)); - else { - const char *rc, *n; - char p[DECIMAL_STR_MAX(q->answer_rcode)]; - - rc = dns_rcode_to_string(q->answer_rcode); - if (!rc) { - sprintf(p, "%i", q->answer_rcode); - rc = p; - } - - n = strjoina(_BUS_ERROR_DNS, 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); - } - - case DNS_TRANSACTION_NULL: - case DNS_TRANSACTION_PENDING: - case DNS_TRANSACTION_VALIDATING: - case DNS_TRANSACTION_SUCCESS: - default: - assert_not_reached("Impossible state"); - } -} - -static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) { - int r; - - assert(reply); - assert(rr); - - r = sd_bus_message_open_container(reply, 'r', "iiay"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "i", ifindex); - if (r < 0) - return r; - - if (rr->key->type == DNS_TYPE_A) { - r = sd_bus_message_append(reply, "i", AF_INET); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr)); - - } else if (rr->key->type == DNS_TYPE_AAAA) { - r = sd_bus_message_append(reply, "i", AF_INET6); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr)); - } else - return -EAFNOSUPPORT; - - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return 0; -} - -static void bus_method_resolve_hostname_complete(DnsQuery *q) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *normalized = NULL; - DnsResourceRecord *rr; - unsigned added = 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; - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(iiay)"); - if (r < 0) - goto finish; - - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - DnsQuestion *question; - - question = dns_query_question_for_protocol(q, q->answer_protocol); - - 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, rr, ifindex); - if (r < 0) - goto finish; - - 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; - - /* The key names are not necessarily normalized, make sure that they are when we return them to our bus - * clients. */ - r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); - if (r < 0) - goto finish; - - /* Return the precise spelling and uppercasing and CNAME target reported by the server */ - assert(canonical); - r = sd_bus_message_append( - reply, "st", - normalized, - 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) { - log_error_errno(r, "Failed to send hostname reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); - } - - dns_query_free(q); -} - -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_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); - - if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */ - *flags |= SD_RESOLVED_PROTOCOLS_ALL; - - return 0; -} - -static int parse_as_address(sd_bus_message *m, int ifindex, const char *hostname, int family, uint64_t flags) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *canonical = NULL; - union in_addr_union parsed; - int r, ff, parsed_ifindex = 0; - - /* Check if the hostname is actually already an IP address formatted as string. In that case just parse it, - * let's not attempt to look it up. */ - - r = in_addr_ifindex_from_string_auto(hostname, &ff, &parsed, &parsed_ifindex); - if (r < 0) /* not an address */ - return 0; - - if (family != AF_UNSPEC && ff != family) - return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address is not of the requested family."); - if (ifindex > 0 && parsed_ifindex > 0 && parsed_ifindex != ifindex) - return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address interface index does not match requested interface."); - - if (parsed_ifindex > 0) - ifindex = parsed_ifindex; - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(iiay)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'r', "iiay"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "ii", ifindex, ff); - if (r < 0) - return r; - - r = sd_bus_message_append_array(reply, 'y', &parsed, FAMILY_ADDRESS_SIZE(ff)); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - /* When an IP address is specified we just return it as canonical name, in order to avoid a DNS - * look-up. However, we reformat it to make sure it's in a truly canonical form (i.e. on IPv6 the inner - * omissions are always done the same way). */ - r = in_addr_ifindex_to_string(ff, &parsed, ifindex, &canonical); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "st", canonical, - SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(flags), ff, true)); - if (r < 0) - return r; - - return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL); -} - -static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; - Manager *m = userdata; - const char *hostname; - 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, "isit", &ifindex, &hostname, &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); - - r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); - if (r < 0) - return r; - - r = parse_as_address(message, ifindex, hostname, family, flags); - if (r != 0) - return r; - - 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 = dns_question_new_address(&question_utf8, family, hostname, false); - 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_utf8, question_idna, ifindex, flags); - if (r < 0) - return r; - - q->request = sd_bus_message_ref(message); - q->request_family = family; - q->complete = bus_method_resolve_hostname_complete; - q->suppress_unroutable_family = family == AF_UNSPEC; - - 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; -} - -static void bus_method_resolve_address_complete(DnsQuery *q) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - DnsQuestion *question; - DnsResourceRecord *rr; - unsigned added = 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; - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(is)"); - if (r < 0) - goto finish; - - question = dns_query_question_for_protocol(q, q->answer_protocol); - - DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { - _cleanup_free_ char *normalized = NULL; - - r = dns_question_matches_rr(question, rr, NULL); - if (r < 0) - goto finish; - if (r == 0) - continue; - - r = dns_name_normalize(rr->ptr.name, &normalized); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "(is)", ifindex, normalized); - if (r < 0) - goto finish; - - added++; - } - - if (added <= 0) { - _cleanup_free_ char *ip = NULL; - - (void) 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", strnull(ip)); - goto finish; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - 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; - - r = sd_bus_send(q->manager->bus, reply, NULL); - -finish: - if (r < 0) { - log_error_errno(r, "Failed to send address reply: %m"); - 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_question_unrefp) DnsQuestion *question = NULL; - Manager *m = userdata; - int family, ifindex; - uint64_t flags; - const void *d; - DnsQuery *q; - size_t sz; - int r; - - 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; - - 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_read(message, "t", &flags); - if (r < 0) - return r; - - r = check_ifindex_flags(ifindex, &flags, 0, error); - if (r < 0) - return r; - - r = dns_question_new_reverse(&question, family, d); - if (r < 0) - return r; - - 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_family = family; - memcpy(&q->request_address, d, sz); - q->complete = bus_method_resolve_address_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; -} - -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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - DnsResourceRecord *rr; - DnsQuestion *question; - unsigned added = 0; - int ifindex; - int 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; - - r = sd_bus_message_new_method_return(q->request, &reply); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'a', "(iqqay)"); - if (r < 0) - goto finish; - - 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; - - r = bus_message_append_rr(reply, rr, ifindex); - if (r < 0) - goto finish; - - 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", dns_query_string(q)); - goto finish; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto finish; - - 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; - - r = sd_bus_send(q->manager->bus, reply, NULL); - -finish: - if (r < 0) { - log_error_errno(r, "Failed to send record reply: %m"); - sd_bus_reply_method_errno(q->request, r, NULL); - } - - dns_query_free(q); -} - -static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - Manager *m = userdata; - uint16_t class, type; - const char *name; - int r, ifindex; - uint64_t flags; - DnsQuery *q; - - 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_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); - - 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_zone_transer(type)) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface."); - 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; - - question = dns_question_new(1); - if (!question) - return -ENOMEM; - - key = dns_resource_key_new(class, type, name); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - - r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); - if (r < 0) - return r; - - /* Let's request that the TTL is fixed up for locally cached entries, after all we return it in the wire format - * blob */ - q->clamp_ttl = true; - - q->request = sd_bus_message_ref(message); - q->complete = bus_method_resolve_record_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; -} - -static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; - _cleanup_free_ char *normalized = 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 = dns_name_normalize(rr->srv.name, &normalized); - if (r < 0) - return r; - - r = sd_bus_message_append( - reply, - "qqqs", - rr->srv.priority, rr->srv.weight, rr->srv.port, normalized); - 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; - - if (canonical) { - normalized = mfree(normalized); - - r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); - 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", normalized); - 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) { - 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; - 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", dns_server_ifindex(s)); - 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_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', "(isb)"); - if (r < 0) - return r; - - LIST_FOREACH(domains, d, m->search_domains) { - r = sd_bus_message_append(reply, "(isb)", 0, d->name, d->route_only); - if (r < 0) - return r; - } - - HASHMAP_FOREACH(l, m->links, i) { - LIST_FOREACH(domains, d, l->search_domains) { - r = sd_bus_message_append(reply, "(isb)", l->ifindex, d->name, d->route_only); - 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_verdict[DNSSEC_SECURE], - (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE], - (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS], - (uint64_t) m->n_dnssec_verdict[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_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) { - - Manager *m = userdata; - const char *domain; - Iterator i; - int r; - - assert(reply); - assert(m); - - r = sd_bus_message_open_container(reply, 'a', "s"); - if (r < 0) - return r; - - SET_FOREACH(domain, m->trust_anchor.negative_by_name, i) { - r = sd_bus_message_append(reply, "s", domain); - if (r < 0) - return r; - } - - return sd_bus_message_close_container(reply); -} - -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; - zero(m->n_dnssec_verdict); - - 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 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_any_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_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return call_link_method(userdata, message, bus_link_method_set_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 int bus_method_flush_caches(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - - assert(message); - assert(m); - - manager_flush_caches(m); - - return sd_bus_reply_method_return(message, NULL); -} - -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(isb)", bus_property_get_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_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 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("FlushCaches", NULL, NULL, bus_method_flush_caches, 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", "ia(sb)", NULL, bus_method_set_link_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, -}; - -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - return 0; -} - -static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { - Manager *m = userdata; - int b, r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) { - log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); - return 0; - } - - if (b) - return 0; - - log_debug("Coming back from suspend, verifying all RRs..."); - - manager_verify_all(m); - return 0; -} - -int manager_connect_bus(Manager *m) { - int r; - - assert(m); - - if (m->bus) - return 0; - - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. As soon as we have - * kdbus we can stop doing this... */ - - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); - - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, 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; - } - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, 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"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); - if (r < 0) - log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); - - return 0; -} diff --git a/src/resolve/resolved-bus.h b/src/resolve/resolved-bus.h deleted file mode 100644 index f49e1337d2..0000000000 --- a/src/resolve/resolved-bus.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 deleted file mode 100644 index dd233e7c4a..0000000000 --- a/src/resolve/resolved-conf.c +++ /dev/null @@ -1,240 +0,0 @@ -/*** - 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 . - ***/ - -#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, ifindex = 0; - DnsServer *s; - - assert(m); - assert(word); - - r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex); - if (r < 0) - return r; - - /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */ - if (!dns_server_address_valid(family, &address)) - return 0; - - /* Filter out duplicates */ - s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex); - 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, ifindex); -} - -int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { - int r; - - assert(m); - assert(string); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&string, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - 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: %m", word); - } - - return 0; -} - -int manager_add_search_domain_by_string(Manager *m, const char *domain) { - DnsSearchDomain *d; - bool route_only; - int r; - - assert(m); - assert(domain); - - route_only = *domain == '~'; - if (route_only) - domain++; - - if (dns_name_is_root(domain) || streq(domain, "*")) { - route_only = true; - 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); - else { - r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain); - if (r < 0) - return r; - } - - d->route_only = route_only; - return 0; -} - -int manager_parse_search_domains_and_warn(Manager *m, const char *string) { - int r; - - assert(m); - assert(string); - - for (;;) { - _cleanup_free_ char *word = NULL; - - 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: %m", word); - } - - return 0; -} - -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) { - - Manager *m = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(m); - - if (isempty(rvalue)) - /* Empty assignment means clear the list */ - dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); - else { - /* 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); - return 0; - } - } - - /* If we have a manual setting, then we stop reading - * /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_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) { - - Manager *m = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(m); - - 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 search domains string '%s'. Ignoring.", rvalue); - return 0; - } - } - - /* 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); - - 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 deleted file mode 100644 index e1fd2cceec..0000000000 --- a/src/resolve/resolved-conf.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "resolved-manager.h" - -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_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 deleted file mode 100644 index c4c1915b18..0000000000 --- a/src/resolve/resolved-def.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#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_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 deleted file mode 100644 index ab85754bf7..0000000000 --- a/src/resolve/resolved-dns-answer.c +++ /dev/null @@ -1,858 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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; - - a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n); - if (!a) - return NULL; - - a->n_ref = 1; - a->n_allocated = n; - - return a; -} - -DnsAnswer *dns_answer_ref(DnsAnswer *a) { - if (!a) - return NULL; - - assert(a->n_ref > 0); - a->n_ref++; - 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; - - assert(a->n_ref > 0); - - if (a->n_ref == 1) { - dns_answer_flush(a); - free(a); - } else - a->n_ref--; - - return NULL; -} - -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; - - assert(rr); - - 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) - continue; - - r = dns_resource_record_equal(a->items[i].rr, rr); - if (r < 0) - return r; - if (r > 0) { - /* Don't mix contradicting TTLs (see below) */ - if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) - return -EINVAL; - - /* 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; - } - } - - return dns_answer_add_raw(a, rr, ifindex, flags); -} - -static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) { - DnsResourceRecord *rr; - DnsAnswerFlags flags; - int ifindex, r; - - 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, int ifindex) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL; - - soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name); - if (!soa) - return -ENOMEM; - - soa->ttl = ttl; - - soa->soa.mname = strdup(name); - if (!soa->soa.mname) - return -ENOMEM; - - soa->soa.rname = strappend("root.", name); - if (!soa->soa.rname) - return -ENOMEM; - - soa->soa.serial = 1; - soa->soa.refresh = 1; - soa->soa.retry = 1; - soa->soa.expire = 1; - soa->soa.minimum = ttl; - - return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED); -} - -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); - - 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; - - 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) - continue; - - 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_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); - - /* For a SOA record we can never find a matching SOA record */ - if (key->type == DNS_TYPE_SOA) - return 0; - - 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_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 (dns_answer_size(a) <= 0) { - *ret = dns_answer_ref(b); - return 0; - } - - if (dns_answer_size(b) <= 0) { - *ret = dns_answer_ref(a); - return 0; - } - - k = dns_answer_new(a->n_rrs + b->n_rrs); - if (!k) - return -ENOMEM; - - 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; -} - -int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) { - bool found = false, other = false; - DnsResourceRecord *rr; - unsigned i; - int r; - - assert(a); - assert(key); - - /* 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; - - r = dns_answer_add_raw(copy, rr, ifindex, flags); - if (r < 0) - return r; - } - - dns_answer_unref(*a); - *a = copy; - copy = NULL; - - return 1; - } - - /* 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 r; - } - - dns_answer_unref(*a); - *a = copy; - copy = NULL; - - return 1; - } - - /* Only a single reference, edit in-place */ - - 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) { - DnsAnswerItem *items; - unsigned i, start, end; - - if (!a) - return; - - if (a->n_rrs <= 1) - return; - - start = 0; - end = a->n_rrs-1; - - /* RFC 4795, Section 2.6 suggests we should order entries - * depending on whether the sender is a link-local address. */ - - items = newa(DnsAnswerItem, a->n_rrs); - for (i = 0; i < a->n_rrs; i++) { - - if (a->items[i].rr->key->class == DNS_CLASS_IN && - ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) || - (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local))) - /* Order address records that are not preferred to the end of the array */ - items[end--] = a->items[i]; - else - /* Order all other records to the beginning of the array */ - items[start++] = a->items[i]; - } - - assert(start == end+1); - memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs); -} - -int dns_answer_reserve(DnsAnswer **a, unsigned n_free) { - DnsAnswer *n; - - assert(a); - - if (n_free <= 0) - return 0; - - if (*a) { - unsigned ns; - - if ((*a)->n_ref > 1) - return -EBUSY; - - ns = (*a)->n_rrs + 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; - - n->n_allocated = ns; - } else { - n = dns_answer_new(n_free); - if (!n) - return -ENOMEM; - } - - *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 - * possible, 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 deleted file mode 100644 index 4a92bd1150..0000000000 --- a/src/resolve/resolved-dns-answer.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -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. - * - * Note that we usually encode 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 { - unsigned n_ref; - unsigned n_rrs, n_allocated; - DnsAnswerItem items[0]; -}; - -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, 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 ifindex); - -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; -} - -static inline bool dns_answer_isempty(DnsAnswer *a) { - return dns_answer_size(a) <= 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 deleted file mode 100644 index 9233fb0ac1..0000000000 --- a/src/resolve/resolved-dns-cache.c +++ /dev/null @@ -1,1064 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "af-list.h" -#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 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 2h in our cache */ -#define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR) - -typedef enum DnsCacheItemType DnsCacheItemType; -typedef struct DnsCacheItem DnsCacheItem; - -enum DnsCacheItemType { - DNS_CACHE_POSITIVE, - DNS_CACHE_NODATA, - DNS_CACHE_NXDOMAIN, -}; - -struct DnsCacheItem { - DnsCacheItemType type; - DnsResourceKey *key; - DnsResourceRecord *rr; - - usec_t until; - bool authenticated:1; - bool shared_owner:1; - - int ifindex; - int owner_family; - union in_addr_union owner_address; - - unsigned prioq_idx; - LIST_FIELDS(DnsCacheItem, by_key); -}; - -static void dns_cache_item_free(DnsCacheItem *i) { - if (!i) - return; - - dns_resource_record_unref(i->rr); - dns_resource_key_unref(i->key); - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsCacheItem*, dns_cache_item_free); - -static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) { - DnsCacheItem *first; - - assert(c); - - if (!i) - return; - - first = hashmap_get(c->by_key, i->key); - LIST_REMOVE(by_key, first, i); - - if (first) - assert_se(hashmap_replace(c->by_key, first->key, first) >= 0); - else - hashmap_remove(c->by_key, i->key); - - prioq_remove(c->by_expiry, i, &i->prioq_idx); - - dns_cache_item_free(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); - - first = hashmap_remove(c->by_key, key); - if (!first) - return false; - - LIST_FOREACH_SAFE(by_key, i, n, first) { - prioq_remove(c->by_expiry, i, &i->prioq_idx); - dns_cache_item_free(i); - } - - return true; -} - -void dns_cache_flush(DnsCache *c) { - DnsResourceKey *key; - - assert(c); - - 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); - - 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) { - assert(c); - - if (add <= 0) - return; - - /* Makes space for n new entries. Note that we actually allow - * the cache to grow beyond CACHE_MAX, but only when we shall - * add more RRs to the cache than CACHE_MAX at once. In that - * case the cache will be emptied completely otherwise. */ - - for (;;) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - DnsCacheItem *i; - - if (prioq_size(c->by_expiry) <= 0) - break; - - if (prioq_size(c->by_expiry) + add < CACHE_MAX) - break; - - i = prioq_peek(c->by_expiry); - assert(i); - - /* 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); - } -} - -void dns_cache_prune(DnsCache *c) { - usec_t t = 0; - - assert(c); - - /* Remove all entries that are past their TTL */ - - for (;;) { - DnsCacheItem *i; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - i = prioq_peek(c->by_expiry); - if (!i) - break; - - if (t <= 0) - t = now(clock_boottime_or_monotonic()); - - if (i->until > t) - break; - - /* Depending whether this is an mDNS shared entry - * either remove only this one RR or the whole RRset */ - log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)", - i->shared_owner ? "shared " : "", - dns_resource_key_to_string(i->key, key_str, sizeof key_str), - (t - i->until) / USEC_PER_SEC); - - 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); - } - } -} - -static int dns_cache_item_prioq_compare_func(const void *a, const void *b) { - const DnsCacheItem *x = a, *y = b; - - if (x->until < y->until) - return -1; - if (x->until > y->until) - return 1; - return 0; -} - -static int dns_cache_init(DnsCache *c) { - int r; - - assert(c); - - r = prioq_ensure_allocated(&c->by_expiry, dns_cache_item_prioq_compare_func); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&c->by_key, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - return r; -} - -static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) { - DnsCacheItem *first; - int r; - - assert(c); - assert(i); - - r = prioq_put(c->by_expiry, i, &i->prioq_idx); - if (r < 0) - return r; - - 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 { - r = hashmap_put(c->by_key, i->key, i); - if (r < 0) { - prioq_remove(c->by_expiry, i, &i->prioq_idx); - return r; - } - } - - return 0; -} - -static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { - DnsCacheItem *i; - - assert(c); - assert(rr); - - LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key)) - if (i->rr && dns_resource_record_equal(i->rr, rr) > 0) - return i; - - return NULL; -} - -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 ifindex, - int owner_family, - const union in_addr_union *owner_address) { - - assert(c); - assert(i); - assert(rr); - assert(owner_address); - - i->type = DNS_CACHE_POSITIVE; - - if (!i->by_key_prev) - /* We are the first item in the list, we need to - * update the key used in the hashmap */ - - assert_se(hashmap_replace(c->by_key, rr->key, i) >= 0); - - dns_resource_record_ref(rr); - dns_resource_record_unref(i->rr); - i->rr = rr; - - dns_resource_key_unref(i->key); - i->key = dns_resource_key_ref(rr->key); - - i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); - i->authenticated = authenticated; - i->shared_owner = shared_owner; - - i->ifindex = ifindex; - - i->owner_family = owner_family; - i->owner_address = *owner_address; - - prioq_reshuffle(c->by_expiry, i, &i->prioq_idx); -} - -static int dns_cache_put_positive( - DnsCache *c, - DnsResourceRecord *rr, - bool authenticated, - bool shared_owner, - usec_t timestamp, - int ifindex, - int owner_family, - const union in_addr_union *owner_address) { - - _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - DnsCacheItem *existing; - char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE]; - int r, k; - - assert(c); - assert(rr); - assert(owner_address); - - /* 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) { - k = dns_cache_remove_by_rr(c, rr); - log_debug("%s: %s", - k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry", - dns_resource_key_to_string(rr->key, key_str, sizeof key_str)); - return 0; - } - - /* Entry exists already? Update TTL, timestamp and owner*/ - existing = dns_cache_get(c, rr); - if (existing) { - dns_cache_item_update_positive( - c, - existing, - rr, - authenticated, - shared_owner, - timestamp, - ifindex, - owner_family, - owner_address); - return 0; - } - - /* Otherwise, add the new RR */ - r = dns_cache_init(c); - if (r < 0) - return r; - - dns_cache_make_space(c, 1); - - i = new0(DnsCacheItem, 1); - if (!i) - return -ENOMEM; - - i->type = DNS_CACHE_POSITIVE; - i->key = dns_resource_key_ref(rr->key); - i->rr = dns_resource_record_ref(rr); - i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); - i->authenticated = authenticated; - i->shared_owner = shared_owner; - i->ifindex = ifindex; - 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; - - if (log_get_max_level() >= LOG_DEBUG) { - _cleanup_free_ char *t = NULL; - - (void) in_addr_to_string(i->owner_family, &i->owner_address, &t); - - log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s", - i->authenticated ? "authenticated" : "unauthenticated", - i->shared_owner ? " shared" : "", - dns_resource_key_to_string(i->key, key_str, sizeof key_str), - (i->until - timestamp) / USEC_PER_SEC, - i->ifindex == 0 ? "*" : strna(if_indextoname(i->ifindex, ifname)), - af_to_name_short(i->owner_family), - strna(t)); - } - - i = NULL; - return 0; -} - -static int dns_cache_put_negative( - DnsCache *c, - DnsResourceKey *key, - int rcode, - bool authenticated, - uint32_t nsec_ttl, - usec_t timestamp, - DnsResourceRecord *soa, - int owner_family, - const union in_addr_union *owner_address) { - - _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - int r; - - assert(c); - assert(key); - assert(soa); - assert(owner_address); - - /* 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 (dns_type_is_pseudo(key->type)) - return 0; - - if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { - log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - return 0; - } - - if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) - return 0; - - r = dns_cache_init(c); - if (r < 0) - return r; - - dns_cache_make_space(c, 1); - - i = new0(DnsCacheItem, 1); - if (!i) - return -ENOMEM; - - i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN; - 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; - - log_debug("Added %s cache entry for %s "USEC_FMT"s", - i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", - dns_resource_key_to_string(i->key, key_str, sizeof key_str), - (i->until - timestamp) / USEC_PER_SEC); - - 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); - } -} - -static bool rr_eligible(DnsResourceRecord *rr) { - assert(rr); - - /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since - * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS - * existence from any cached NSEC/NSEC3, but that should be fine. */ - - switch (rr->key->type) { - - case DNS_TYPE_NSEC: - return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) || - bitmap_isset(rr->nsec.types, DNS_TYPE_SOA); - - case DNS_TYPE_NSEC3: - return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) || - bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA); - - default: - return true; - } -} - -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) { - - DnsResourceRecord *soa = NULL, *rr; - DnsAnswerFlags flags; - unsigned cache_keys; - int r, ifindex; - - assert(c); - assert(owner_address); - - dns_cache_remove_previous(c, key, answer); - - /* 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; - - if (dns_answer_size(answer) <= 0) { - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - log_debug("Not caching negative entry without a SOA record: %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - return 0; - } - - cache_keys = dns_answer_size(answer); - if (key) - cache_keys++; - - /* Make some space for our new entries */ - dns_cache_make_space(c, cache_keys); - - if (timestamp <= 0) - timestamp = now(clock_boottime_or_monotonic()); - - /* Second, add in positive entries for all contained RRs */ - DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) { - if ((flags & DNS_ANSWER_CACHEABLE) == 0) - continue; - - r = rr_eligible(rr); - if (r < 0) - return r; - if (r == 0) - continue; - - r = dns_cache_put_positive( - c, - rr, - flags & DNS_ANSWER_AUTHENTICATED, - flags & DNS_ANSWER_SHARED_OWNER, - timestamp, - ifindex, - owner_family, owner_address); - if (r < 0) - goto fail; - } - - 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_match_key(answer, key, NULL); - if (r < 0) - goto fail; - if (r > 0) - return 0; - - /* 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; - - /* See https://tools.ietf.org/html/rfc2308, which say that a - * matching SOA record in the packet is used to enable - * negative caching. */ - r = dns_answer_find_soa(answer, key, &soa, &flags); - if (r < 0) - goto fail; - if (r == 0) - return 0; - - /* 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, - authenticated, - nsec_ttl, - timestamp, - soa, - owner_family, owner_address); - if (r < 0) - goto fail; - - return 0; - -fail: - /* Adding all RRs failed. Let's clean up what we already - * added, just in case */ - - if (key) - dns_cache_remove_by_key(c, 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_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) - return i; - - 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; - - /* 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 NULL; -} - -int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **ret, bool *authenticated) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - unsigned n = 0; - int r; - bool nxdomain = false; - DnsCacheItem *j, *first, *nsec = NULL; - bool have_authenticated = false, have_non_authenticated = false; - usec_t current; - - 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 don't use the cache, so - * that the caller refreshes via the network. */ - - log_debug("Ignoring cache for ANY lookup: %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - - c->n_miss++; - - *ret = NULL; - *rcode = DNS_RCODE_SUCCESS; - return 0; - } - - first = dns_cache_get_by_key_follow_cname_dname_nsec(c, key); - if (!first) { - /* If one question cannot be answered we need to refresh */ - - log_debug("Cache miss for %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - - c->n_miss++; - - *ret = NULL; - *rcode = DNS_RCODE_SUCCESS; - return 0; - } - - LIST_FOREACH(by_key, j, first) { - if (j->rr) { - if (j->rr->key->type == DNS_TYPE_NSEC) - nsec = j; - - n++; - } else if (j->type == DNS_CACHE_NXDOMAIN) - nxdomain = true; - - if (j->authenticated) - have_authenticated = true; - else - have_non_authenticated = true; - } - - if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) { - /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from - * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */ - - log_debug("NSEC NODATA cache hit for %s", - dns_resource_key_to_string(key, key_str, sizeof 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. */ - - *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; - } - - log_debug("%s cache hit for %s", - n > 0 ? "Positive" : - nxdomain ? "NXDOMAIN" : "NODATA", - dns_resource_key_to_string(key, key_str, sizeof 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; - } - - answer = dns_answer_new(n); - if (!answer) - return -ENOMEM; - - if (clamp_ttl) - current = now(clock_boottime_or_monotonic()); - - LIST_FOREACH(by_key, j, first) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - if (!j->rr) - continue; - - if (clamp_ttl) { - rr = dns_resource_record_ref(j->rr); - - r = dns_resource_record_clamp_ttl(&rr, LESS_BY(j->until, current) / USEC_PER_SEC); - if (r < 0) - return r; - } - - r = dns_answer_add(answer, rr ?: j->rr, j->ifindex, 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; -} - -int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) { - DnsCacheItem *i, *first; - bool same_owner = true; - - assert(cache); - assert(rr); - - dns_cache_prune(cache); - - /* See if there's a cache entry for the same key. If there - * isn't there's no conflict */ - first = hashmap_get(cache->by_key, rr->key); - if (!first) - return 0; - - /* See if the RR key is owned by the same owner, if so, there - * isn't a conflict either */ - LIST_FOREACH(by_key, i, first) { - if (i->owner_family != owner_family || - !in_addr_equal(owner_family, &i->owner_address, owner_address)) { - same_owner = false; - break; - } - } - if (same_owner) - return 0; - - /* See if there's the exact same RR in the cache. If yes, then - * there's no conflict. */ - if (dns_cache_get(cache, rr)) - return 0; - - /* There's a conflict */ - 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; - - if (!cache) - return; - - if (!f) - f = stdout; - - HASHMAP_FOREACH(i, cache->by_key, iterator) { - DnsCacheItem *j; - - LIST_FOREACH(by_key, j, i) { - - fputc('\t', f); - - if (j->rr) { - const char *t; - t = dns_resource_record_to_string(j->rr); - if (!t) { - log_oom(); - continue; - } - - fputs(t, f); - fputc('\n', f); - } else { - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f); - fputs(" -- ", f); - fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); - fputc('\n', f); - } - } - } -} - -bool dns_cache_is_empty(DnsCache *cache) { - if (!cache) - return true; - - 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 deleted file mode 100644 index 22a7c17377..0000000000 --- a/src/resolve/resolved-dns-cache.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "hashmap.h" -#include "list.h" -#include "prioq.h" -#include "time-util.h" - -typedef struct DnsCache { - Hashmap *by_key; - Prioq *by_expiry; - unsigned n_hit; - unsigned n_miss; -} DnsCache; - -#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, 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, bool clamp_ttl, 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 deleted file mode 100644 index d4a267c89f..0000000000 --- a/src/resolve/resolved-dns-dnssec.c +++ /dev/null @@ -1,2199 +0,0 @@ -/*** - 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 . -***/ - -#ifdef HAVE_GCRYPT -#include -#endif - -#include "alloc-util.h" -#include "dns-domain.h" -#include "gcrypt-util.h" -#include "hexdecoct.h" -#include "resolved-dns-dnssec.h" -#include "resolved-dns-packet.h" -#include "string-table.h" - -#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 - */ - -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); -} - -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; -} - -#ifdef HAVE_GCRYPT - -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 that the RRSet matches the specified "key" in "a", - * using the signature "rrsig" and the key "dnskey". It's - * assumed that 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(false); - - 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; -} - -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(false); - - 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(false); - - 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 these NSEC3 RRs weren't correctly signed we can't make this - * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */ - if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1) - return 0; - /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */ - if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -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' */ - - if (!pp) { - /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR - * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are - * appropriately set. */ - - if (key->type == DNS_TYPE_DS) { - if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) - return -EBADMSG; - } else { - if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) && - !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA)) - return -EBADMSG; - } - - /* 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; - } - - /* 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; - - /* 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 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); - 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; -} - -static 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 "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); -} - -#else - -int dnssec_verify_rrset( - DnsAnswer *a, - const DnsResourceKey *key, - DnsResourceRecord *rrsig, - DnsResourceRecord *dnskey, - usec_t realtime, - DnssecResult *result) { - - return -EOPNOTSUPP; -} - -int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) { - - return -EOPNOTSUPP; -} - -int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { - - return -EOPNOTSUPP; -} - -int dnssec_verify_rrset_search( - DnsAnswer *a, - const DnsResourceKey *key, - DnsAnswer *validated_dnskeys, - usec_t realtime, - DnssecResult *result, - DnsResourceRecord **ret_rrsig) { - - return -EOPNOTSUPP; -} - -int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { - - return -EOPNOTSUPP; -} - -int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { - - return -EOPNOTSUPP; -} - -int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { - - return -EOPNOTSUPP; -} - -int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { - - return -EOPNOTSUPP; -} - -int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { - - return -EOPNOTSUPP; -} - -int dnssec_test_positive_wildcard( - DnsAnswer *answer, - const char *name, - const char *source, - const char *zone, - bool *authenticated) { - - return -EOPNOTSUPP; -} - -#endif - -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); - -static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = { - [DNSSEC_SECURE] = "secure", - [DNSSEC_INSECURE] = "insecure", - [DNSSEC_BOGUS] = "bogus", - [DNSSEC_INDETERMINATE] = "indeterminate", -}; -DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict); diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h deleted file mode 100644 index 77bd4d71bf..0000000000 --- a/src/resolve/resolved-dns-dnssec.h +++ /dev/null @@ -1,102 +0,0 @@ -#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 . -***/ - -typedef enum DnssecResult DnssecResult; -typedef enum DnssecVerdict DnssecVerdict; - -#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 -}; - -enum DnssecVerdict { - DNSSEC_SECURE, - DNSSEC_INSECURE, - DNSSEC_BOGUS, - DNSSEC_INDETERMINATE, - - _DNSSEC_VERDICT_MAX, - _DNSSEC_VERDICT_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_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_; - -const char* dnssec_verdict_to_string(DnssecVerdict m) _const_; -DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c deleted file mode 100644 index a8ad8fe342..0000000000 --- a/src/resolve/resolved-dns-packet.c +++ /dev/null @@ -1,2300 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include "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) - -typedef struct DnsPacketRewinder { - DnsPacket *packet; - size_t saved_rindex; -} DnsPacketRewinder; - -static void rewind_dns_packet(DnsPacketRewinder *rewinder) { - if (rewinder->packet) - dns_packet_rewind(rewinder->packet, rewinder->saved_rindex); -} - -#define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0) -#define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0) - -int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { - DnsPacket *p; - size_t a; - - assert(ret); - - if (mtu <= UDP_PACKET_HEADER_SIZE) - a = DNS_PACKET_SIZE_START; - else - a = mtu - UDP_PACKET_HEADER_SIZE; - - if (a < DNS_PACKET_HEADER_SIZE) - a = DNS_PACKET_HEADER_SIZE; - - /* round up to next page size */ - a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket)); - - /* make sure we never allocate more than useful */ - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; - - p = malloc0(ALIGN(sizeof(DnsPacket)) + a); - if (!p) - return -ENOMEM; - - 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; - - return 0; -} - -void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated) { - - DnsPacketHeader *h; - - assert(p); - - h = DNS_PACKET_HEADER(p); - - switch(p->protocol) { - case DNS_PROTOCOL_LLMNR: - assert(!truncated); - - h->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0 /* qr */, - 0 /* opcode */, - 0 /* c */, - 0 /* tc */, - 0 /* t */, - 0 /* ra */, - 0 /* ad */, - 0 /* cd */, - 0 /* rcode */)); - 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 */, - 0 /* tc */, - 1 /* rd (ask for recursion) */, - 0 /* ra */, - 0 /* ad */, - 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; -} - -DnsPacket *dns_packet_ref(DnsPacket *p) { - - if (!p) - return NULL; - - assert(!p->on_stack); - - assert(p->n_ref > 0); - p->n_ref++; - return p; -} - -static void dns_packet_free(DnsPacket *p) { - char *s; - - assert(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); - - if (!p->on_stack) - free(p); -} - -DnsPacket *dns_packet_unref(DnsPacket *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - - dns_packet_unref(p->more); - - if (p->n_ref == 1) - dns_packet_free(p); - else - p->n_ref--; - - return NULL; -} - -int dns_packet_validate(DnsPacket *p) { - assert(p); - - if (p->size < DNS_PACKET_HEADER_SIZE) - return -EBADMSG; - - if (p->size > DNS_PACKET_SIZE_MAX) - return -EBADMSG; - - return 1; -} - -int dns_packet_validate_reply(DnsPacket *p) { - int r; - - assert(p); - - r = dns_packet_validate(p); - if (r < 0) - return r; - - if (DNS_PACKET_QR(p) != 1) - return 0; - - if (DNS_PACKET_OPCODE(p) != 0) - 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) - return -EBADMSG; - - break; - - case DNS_PROTOCOL_MDNS: - /* RFC 6762, Section 18 */ - if (DNS_PACKET_RCODE(p) != 0) - return -EBADMSG; - - break; - - default: - break; - } - - return 1; -} - -int dns_packet_validate_query(DnsPacket *p) { - int r; - - assert(p); - - r = dns_packet_validate(p); - if (r < 0) - return r; - - if (DNS_PACKET_QR(p) != 0) - return 0; - - if (DNS_PACKET_OPCODE(p) != 0) - return -EBADMSG; - - if (DNS_PACKET_TC(p)) - return -EBADMSG; - - switch (p->protocol) { - - case DNS_PROTOCOL_LLMNR: - case DNS_PROTOCOL_DNS: - /* RFC 4795, Section 2.1.1. says to discard all queries with QDCOUNT != 1 */ - if (DNS_PACKET_QDCOUNT(p) != 1) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all queries with ANCOUNT != 0 */ - if (DNS_PACKET_ANCOUNT(p) > 0) - return -EBADMSG; - - /* RFC 4795, Section 2.1.1. says to discard all queries with NSCOUNT != 0 */ - if (DNS_PACKET_NSCOUNT(p) > 0) - return -EBADMSG; - - 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; - } - - return 1; -} - -static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start) { - assert(p); - - if (p->size + add > p->allocated) { - size_t a; - - a = PAGE_ALIGN((p->size + add) * 2); - if (a > DNS_PACKET_SIZE_MAX) - a = DNS_PACKET_SIZE_MAX; - - if (p->size + add > a) - return -EMSGSIZE; - - if (p->_data) { - void *d; - - d = realloc(p->_data, a); - if (!d) - return -ENOMEM; - - p->_data = d; - } else { - p->_data = malloc(a); - if (!p->_data) - return -ENOMEM; - - memcpy(p->_data, (uint8_t*) p + ALIGN(sizeof(DnsPacket)), p->size); - memzero((uint8_t*) p->_data + p->size, a - p->size); - } - - p->allocated = a; - } - - if (start) - *start = p->size; - - if (ret) - *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->size; - - p->size += add; - return 0; -} - -void dns_packet_truncate(DnsPacket *p, size_t sz) { - Iterator i; - char *s; - void *n; - - assert(p); - - if (p->size <= sz) - return; - - HASHMAP_FOREACH_KEY(n, s, p->names, i) { - - if (PTR_TO_SIZE(n) < sz) - continue; - - hashmap_remove(p->names, s); - free(s); - } - - p->size = sz; -} - -int dns_packet_append_blob(DnsPacket *p, const void *d, size_t l, size_t *start) { - void *q; - int r; - - assert(p); - - r = dns_packet_extend(p, l, &q, start); - if (r < 0) - return r; - - memcpy(q, d, l); - return 0; -} - -int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint8_t), &d, start); - if (r < 0) - return r; - - ((uint8_t*) d)[0] = v; - - return 0; -} - -int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint16_t), &d, start); - if (r < 0) - return r; - - unaligned_write_be16(d, v); - - return 0; -} - -int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) { - void *d; - int r; - - assert(p); - - r = dns_packet_extend(p, sizeof(uint32_t), &d, start); - if (r < 0) - return r; - - unaligned_write_be32(d, v); - - return 0; -} - -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; - int r; - - assert(p); - assert(s || size == 0); - - if (size > 255) - return -E2BIG; - - r = dns_packet_extend(p, 1 + size, &d, start); - if (r < 0) - return r; - - ((uint8_t*) d)[0] = (uint8_t) size; - - memcpy_safe(((uint8_t*) d) + 1, s, size); - - return 0; -} - -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, (void**) &w, start); - if (r < 0) - return r; - - *(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; -} - -int dns_packet_append_name( - DnsPacket *p, - const char *name, - bool allow_compression, - bool canonical_candidate, - size_t *start) { - - size_t saved_size; - int r; - - assert(p); - assert(name); - - if (p->refuse_compression) - allow_compression = false; - - saved_size = p->size; - - while (!dns_name_is_root(name)) { - const char *z = name; - char label[DNS_LABEL_MAX]; - size_t n = 0; - - if (allow_compression) - n = PTR_TO_SIZE(hashmap_get(p->names, name)); - if (n > 0) { - assert(n < p->size); - - if (n < 0x4000) { - r = dns_packet_append_uint16(p, 0xC000 | n, NULL); - if (r < 0) - goto fail; - - goto done; - } - } - - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - goto fail; - - 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; - - r = hashmap_put(p->names, s, SIZE_TO_PTR(n)); - if (r < 0) - goto fail; - - s = NULL; - } - } - - r = dns_packet_append_uint8(p, 0, NULL); - if (r < 0) - return r; - -done: - if (start) - *start = saved_size; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) { - size_t saved_size; - int r; - - assert(p); - assert(k); - - saved_size = p->size; - - r = dns_packet_append_name(p, dns_resource_key_name(k), true, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, k->type, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, k->class, NULL); - if (r < 0) - goto fail; - - if (start) - *start = saved_size; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -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; - - assert(p); - assert(types); - assert(length > 0); - - saved_size = p->size; - - r = dns_packet_append_uint8(p, window, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, length, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, types, length, NULL); - if (r < 0) - goto fail; - - if (start) - *start = saved_size; - - return 0; -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -static int dns_packet_append_types(DnsPacket *p, Bitmap *types, size_t *start) { - Iterator i; - uint8_t window = 0; - uint8_t entry = 0; - uint8_t bitmaps[32] = {}; - unsigned n; - size_t saved_size; - int r; - - assert(p); - - saved_size = p->size; - - BITMAP_FOREACH(n, types, i) { - assert(n <= 0xffff); - - if ((n >> 8) != window && bitmaps[entry / 8] != 0) { - r = dns_packet_append_type_window(p, window, entry / 8 + 1, bitmaps, NULL); - if (r < 0) - goto fail; - - zero(bitmaps); - } - - window = n >> 8; - entry = n & 255; - - bitmaps[entry / 8] |= 1 << (7 - (entry % 8)); - } - - 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, int rcode, 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); - assert(rcode >= 0); - assert(rcode <= _DNS_RCODE_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; - - /* class: 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, ((uint16_t) rcode & 0x0FF0) << 4, 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 && !DNS_PACKET_QR(p)) { - /* If DO is on and this is not a reply, 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_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); - assert(rr); - - saved_size = p->size; - - r = dns_packet_append_key(p, rr->key, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->ttl, NULL); - if (r < 0) - goto fail; - - /* Initially we write 0 here */ - r = dns_packet_append_uint16(p, 0, &rdlength_offset); - if (r < 0) - goto fail; - - rds = p->size - saved_size; - - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - - case DNS_TYPE_SRV: - r = dns_packet_append_uint16(p, rr->srv.priority, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->srv.weight, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->srv.port, NULL); - if (r < 0) - goto fail; - - 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, false, NULL); - break; - - case DNS_TYPE_HINFO: - r = dns_packet_append_string(p, rr->hinfo.cpu, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_string(p, rr->hinfo.os, NULL); - break; - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - - if (!rr->txt.items) { - /* RFC 6763, section 6.1 suggests to generate - * single empty string for an empty array. */ - - r = dns_packet_append_raw_string(p, NULL, 0, NULL); - if (r < 0) - goto fail; - } else { - 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; - } - } - - r = 0; - break; - - case DNS_TYPE_A: - r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); - break; - - case DNS_TYPE_AAAA: - r = dns_packet_append_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); - break; - - case DNS_TYPE_SOA: - 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, false, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.serial, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.refresh, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.retry, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.expire, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->soa.minimum, NULL); - break; - - case DNS_TYPE_MX: - r = dns_packet_append_uint16(p, rr->mx.priority, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL); - break; - - case DNS_TYPE_LOC: - r = dns_packet_append_uint8(p, rr->loc.version, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.horiz_pre, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->loc.vert_pre, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.latitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.longitude, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->loc.altitude, NULL); - break; - - case DNS_TYPE_DS: - r = dns_packet_append_uint16(p, rr->ds.key_tag, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->ds.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->ds.digest_type, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->ds.digest, rr->ds.digest_size, NULL); - break; - - case DNS_TYPE_SSHFP: - r = dns_packet_append_uint8(p, rr->sshfp.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->sshfp.fptype, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->sshfp.fingerprint, rr->sshfp.fingerprint_size, NULL); - break; - - case DNS_TYPE_DNSKEY: - r = dns_packet_append_uint16(p, rr->dnskey.flags, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->dnskey.protocol, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->dnskey.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->dnskey.key, rr->dnskey.key_size, NULL); - break; - - case DNS_TYPE_RRSIG: - r = dns_packet_append_uint16(p, rr->rrsig.type_covered, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->rrsig.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->rrsig.labels, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.original_ttl, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.expiration, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint32(p, rr->rrsig.inception, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->rrsig.key_tag, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_name(p, rr->rrsig.signer, false, true, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->rrsig.signature, rr->rrsig.signature_size, NULL); - break; - - case DNS_TYPE_NSEC: - r = dns_packet_append_name(p, rr->nsec.next_domain_name, false, false, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_types(p, rr->nsec.types, NULL); - if (r < 0) - goto fail; - - break; - - case DNS_TYPE_NSEC3: - r = dns_packet_append_uint8(p, rr->nsec3.algorithm, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->nsec3.flags, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint16(p, rr->nsec3.iterations, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->nsec3.salt_size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->nsec3.salt, rr->nsec3.salt_size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->nsec3.next_hashed_name_size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_types(p, rr->nsec3.types, NULL); - if (r < 0) - goto fail; - - break; - - case DNS_TYPE_TLSA: - r = dns_packet_append_uint8(p, rr->tlsa.cert_usage, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->tlsa.selector, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_uint8(p, rr->tlsa.matching_type, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL); - break; - - case DNS_TYPE_CAA: - r = dns_packet_append_uint8(p, rr->caa.flags, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_string(p, rr->caa.tag, NULL); - if (r < 0) - goto fail; - - r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL); - break; - - case DNS_TYPE_OPT: - case DNS_TYPE_OPENPGPKEY: - case _DNS_TYPE_INVALID: /* unparseable */ - default: - - r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL); - break; - } - if (r < 0) - goto fail; - - /* Let's calculate the actual data size and update the field */ - rdlength = p->size - rdlength_offset - sizeof(uint16_t); - if (rdlength > 0xFFFF) { - r = -ENOSPC; - goto fail; - } - - end = p->size; - p->size = rdlength_offset; - r = dns_packet_append_uint16(p, rdlength, NULL); - if (r < 0) - goto fail; - p->size = end; - - if (start) - *start = saved_size; - - if (rdata_start) - *rdata_start = rds; - - return 0; - -fail: - dns_packet_truncate(p, saved_size); - return r; -} - -int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) { - DnsResourceKey *key; - int r; - - assert(p); - - DNS_QUESTION_FOREACH(key, q) { - r = dns_packet_append_key(p, key, NULL); - if (r < 0) - return r; - } - - return 0; -} - -int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) { - DnsResourceRecord *rr; - int r; - - assert(p); - - DNS_ANSWER_FOREACH(rr, a) { - r = dns_packet_append_rr(p, rr, NULL, NULL); - if (r < 0) - return r; - } - - return 0; -} - -int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start) { - assert(p); - - if (p->rindex + sz > p->size) - return -EMSGSIZE; - - if (ret) - *ret = (uint8_t*) DNS_PACKET_DATA(p) + p->rindex; - - if (start) - *start = p->rindex; - - p->rindex += sz; - return 0; -} - -void dns_packet_rewind(DnsPacket *p, size_t idx) { - assert(p); - assert(idx <= p->size); - assert(idx >= DNS_PACKET_HEADER_SIZE); - - p->rindex = idx; -} - -int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start) { - const void *q; - int r; - - assert(p); - assert(d); - - r = dns_packet_read(p, sz, &q, start); - if (r < 0) - return r; - - memcpy(d, q, sz); - return 0; -} - -static int dns_packet_read_memdup( - DnsPacket *p, size_t size, - void **ret, size_t *ret_size, - size_t *ret_start) { - - const void *src; - size_t start; - int r; - - assert(p); - assert(ret); - - r = dns_packet_read(p, size, &src, &start); - if (r < 0) - return r; - - if (size <= 0) - *ret = NULL; - else { - void *copy; - - copy = memdup(src, size); - if (!copy) - return -ENOMEM; - - *ret = copy; - } - - if (ret_size) - *ret_size = size; - if (ret_start) - *ret_start = start; - - return 0; -} - -int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint8_t), &d, start); - if (r < 0) - return r; - - *ret = ((uint8_t*) d)[0]; - return 0; -} - -int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint16_t), &d, start); - if (r < 0) - return r; - - *ret = unaligned_read_be16(d); - - return 0; -} - -int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { - const void *d; - int r; - - assert(p); - - r = dns_packet_read(p, sizeof(uint32_t), &d, start); - if (r < 0) - return r; - - *ret = unaligned_read_be32(d); - - return 0; -} - -int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - const void *d; - char *t; - uint8_t c; - int r; - - assert(p); - INIT_REWINDER(rewinder, p); - - r = dns_packet_read_uint8(p, &c, NULL); - if (r < 0) - return r; - - r = dns_packet_read(p, c, &d, NULL); - if (r < 0) - return r; - - if (memchr(d, 0, c)) - return -EBADMSG; - - t = strndup(d, c); - if (!t) - return -ENOMEM; - - if (!utf8_is_valid(t)) { - free(t); - return -EBADMSG; - } - - *ret = t; - - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - uint8_t c; - int r; - - assert(p); - INIT_REWINDER(rewinder, p); - - r = dns_packet_read_uint8(p, &c, NULL); - if (r < 0) - return r; - - r = dns_packet_read(p, c, ret, NULL); - if (r < 0) - return r; - - if (size) - *size = c; - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -int dns_packet_read_name( - DnsPacket *p, - char **_ret, - bool allow_compression, - size_t *start) { - - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - size_t after_rindex = 0, jump_barrier; - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - bool first = true; - int r; - - assert(p); - assert(_ret); - INIT_REWINDER(rewinder, p); - jump_barrier = p->rindex; - - if (p->refuse_compression) - allow_compression = false; - - for (;;) { - uint8_t c, d; - - r = dns_packet_read_uint8(p, &c, NULL); - if (r < 0) - return r; - - if (c == 0) - /* End of name */ - break; - else if (c <= 63) { - const char *label; - - /* Literal label */ - r = dns_packet_read(p, c, (const void**) &label, NULL); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; - - if (first) - first = false; - else - ret[n++] = '.'; - - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - n += r; - continue; - } else if (allow_compression && (c & 0xc0) == 0xc0) { - uint16_t ptr; - - /* Pointer */ - r = dns_packet_read_uint8(p, &d, NULL); - if (r < 0) - return r; - - ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) - return -EBADMSG; - - if (after_rindex == 0) - after_rindex = p->rindex; - - /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ - jump_barrier = ptr; - p->rindex = ptr; - } else - return -EBADMSG; - } - - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - - ret[n] = 0; - - if (after_rindex != 0) - p->rindex= after_rindex; - - *_ret = ret; - ret = NULL; - - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { - uint8_t window; - uint8_t length; - const uint8_t *bitmap; - uint8_t bit = 0; - unsigned i; - bool found = false; - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - int r; - - assert(p); - assert(types); - INIT_REWINDER(rewinder, p); - - r = bitmap_ensure_allocated(types); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &window, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &length, NULL); - if (r < 0) - return r; - - if (length == 0 || length > 32) - return -EBADMSG; - - r = dns_packet_read(p, length, (const void **)&bitmap, NULL); - if (r < 0) - return r; - - for (i = 0; i < length; i++) { - uint8_t bitmask = 1 << 7; - - if (!bitmap[i]) { - found = false; - bit += 8; - continue; - } - - found = true; - - while (bitmask) { - if (bitmap[i] & bitmask) { - uint16_t n; - - n = (uint16_t) window << 8 | (uint16_t) bit; - - /* Ignore pseudo-types. see RFC4034 section 4.1.2 */ - if (dns_type_is_pseudo(n)) - continue; - - r = bitmap_set(*types, n); - if (r < 0) - return r; - } - - bit++; - bitmask >>= 1; - } - } - - if (!found) - return -EBADMSG; - - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - int r; - - INIT_REWINDER(rewinder, p); - - while (p->rindex < rewinder.saved_rindex + size) { - r = dns_packet_read_type_window(p, types, NULL); - if (r < 0) - return r; - - /* don't read past end of current RR */ - if (p->rindex > rewinder.saved_rindex + size) - return -EBADMSG; - } - - if (p->rindex != rewinder.saved_rindex + size) - return -EBADMSG; - - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) { - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - _cleanup_free_ char *name = NULL; - bool cache_flush = false; - uint16_t class, type; - DnsResourceKey *key; - int r; - - assert(p); - assert(ret); - INIT_REWINDER(rewinder, p); - - r = dns_packet_read_name(p, &name, true, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint16(p, &type, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint16(p, &class, NULL); - if (r < 0) - return r; - - 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) - return -ENOMEM; - - name = NULL; - *ret = key; - - if (ret_cache_flush) - *ret_cache_flush = cache_flush; - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -static bool loc_size_ok(uint8_t size) { - uint8_t m = size >> 4, e = size & 0xF; - - return m <= 9 && e <= 9 && (m > 0 || e == 0); -} - -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; - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; - size_t offset; - uint16_t rdlength; - bool cache_flush; - int r; - - assert(p); - assert(ret); - - INIT_REWINDER(rewinder, p); - - r = dns_packet_read_key(p, &key, &cache_flush, NULL); - if (r < 0) - return r; - - if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type)) - return -EBADMSG; - - rr = dns_resource_record_new(key); - if (!rr) - return -ENOMEM; - - r = dns_packet_read_uint32(p, &rr->ttl, NULL); - if (r < 0) - return r; - - /* 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) - return r; - - if (p->rindex + rdlength > p->size) - return -EBADMSG; - - offset = p->rindex; - - switch (rr->key->type) { - - case DNS_TYPE_SRV: - r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); - if (r < 0) - return r; - r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); - if (r < 0) - return r; - r = dns_packet_read_uint16(p, &rr->srv.port, NULL); - if (r < 0) - return r; - r = dns_packet_read_name(p, &rr->srv.name, true, NULL); - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - r = dns_packet_read_name(p, &rr->ptr.name, true, NULL); - break; - - case DNS_TYPE_HINFO: - r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); - if (r < 0) - return r; - - r = dns_packet_read_string(p, &rr->hinfo.os, NULL); - break; - - 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. */ - - 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) { - DnsTxtItem *i; - const void *data; - size_t sz; - - r = dns_packet_read_raw_string(p, &data, &sz, NULL); - if (r < 0) - return r; - - 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; - } - } - - r = 0; - break; - - case DNS_TYPE_A: - r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); - break; - - case DNS_TYPE_AAAA: - r = dns_packet_read_blob(p, &rr->aaaa.in6_addr, sizeof(struct in6_addr), NULL); - break; - - case DNS_TYPE_SOA: - r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); - if (r < 0) - return r; - - r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); - break; - - case DNS_TYPE_MX: - r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); - if (r < 0) - return r; - - r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); - break; - - case DNS_TYPE_LOC: { - uint8_t t; - size_t pos; - - r = dns_packet_read_uint8(p, &t, &pos); - if (r < 0) - return r; - - if (t == 0) { - rr->loc.version = t; - - r = dns_packet_read_uint8(p, &rr->loc.size, NULL); - if (r < 0) - return r; - - if (!loc_size_ok(rr->loc.size)) - return -EBADMSG; - - r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); - if (r < 0) - return r; - - if (!loc_size_ok(rr->loc.horiz_pre)) - return -EBADMSG; - - r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); - if (r < 0) - return r; - - if (!loc_size_ok(rr->loc.vert_pre)) - return -EBADMSG; - - r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); - if (r < 0) - return r; - - break; - } else { - dns_packet_rewind(p, pos); - rr->unparseable = true; - goto unparseable; - } - } - - case DNS_TYPE_DS: - r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, rdlength - 4, - &rr->ds.digest, &rr->ds.digest_size, - NULL); - if (r < 0) - return r; - - if (rr->ds.digest_size <= 0) - /* the accepted size depends on the algorithm, but for now - just ensure that the value is greater than zero */ - return -EBADMSG; - - break; - - case DNS_TYPE_SSHFP: - r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, rdlength - 2, - &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, - NULL); - - if (rr->sshfp.fingerprint_size <= 0) - /* the accepted size depends on the algorithm, but for now - just ensure that the value is greater than zero */ - return -EBADMSG; - - break; - - case DNS_TYPE_DNSKEY: - r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, rdlength - 4, - &rr->dnskey.key, &rr->dnskey.key_size, - NULL); - - if (rr->dnskey.key_size <= 0) - /* the accepted size depends on the algorithm, but for now - just ensure that the value is greater than zero */ - return -EBADMSG; - - break; - - case DNS_TYPE_RRSIG: - r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); - if (r < 0) - return r; - - r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, - &rr->rrsig.signature, &rr->rrsig.signature_size, - NULL); - - if (rr->rrsig.signature_size <= 0) - /* the accepted size depends on the algorithm, but for now - just ensure that the value is greater than zero */ - return -EBADMSG; - - break; - - 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) - return r; - - r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); - - /* 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; - - r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); - if (r < 0) - return r; - - /* this may be zero */ - r = dns_packet_read_uint8(p, &size, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &size, NULL); - if (r < 0) - return r; - - if (size <= 0) - return -EBADMSG; - - r = dns_packet_read_memdup(p, size, - &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, - NULL); - if (r < 0) - return r; - - r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); - - /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ - - break; - } - - case DNS_TYPE_TLSA: - r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL); - if (r < 0) - return r; - - r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, rdlength - 3, - &rr->tlsa.data, &rr->tlsa.data_size, - NULL); - - if (rr->tlsa.data_size <= 0) - /* the accepted size depends on the algorithm, but for now - just ensure that the value is greater than zero */ - return -EBADMSG; - - break; - - case DNS_TYPE_CAA: - r = dns_packet_read_uint8(p, &rr->caa.flags, NULL); - if (r < 0) - return r; - - r = dns_packet_read_string(p, &rr->caa.tag, NULL); - if (r < 0) - return r; - - r = dns_packet_read_memdup(p, - rdlength + offset - p->rindex, - &rr->caa.value, &rr->caa.value_size, NULL); - - break; - - case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */ - case DNS_TYPE_OPENPGPKEY: - default: - unparseable: - r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); - - break; - } - if (r < 0) - return r; - if (p->rindex != offset + rdlength) - return -EBADMSG; - - *ret = rr; - rr = NULL; - - if (ret_cache_flush) - *ret_cache_flush = cache_flush; - if (start) - *start = rewinder.saved_rindex; - CANCEL_REWINDER(rewinder); - - return 0; -} - -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) { - *rfc6975 = false; - return true; /* if it's not version 0, it's OK, but we will ignore the OPT field contents */ - } - - p = rr->opt.data; - l = rr->opt.data_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; - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; - unsigned n, i; - int r; - - if (p->extracted) - return 0; - - INIT_REWINDER(rewinder, p); - dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); - - n = DNS_PACKET_QDCOUNT(p); - if (n > 0) { - question = dns_question_new(n); - if (!question) - return -ENOMEM; - - for (i = 0; i < n; i++) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - bool cache_flush; - - r = dns_packet_read_key(p, &key, &cache_flush, NULL); - if (r < 0) - return r; - - if (cache_flush) - return -EBADMSG; - - if (!dns_type_is_valid_query(key->type)) - return -EBADMSG; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } - } - - n = DNS_PACKET_RRCOUNT(p); - if (n > 0) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL; - bool bad_opt = false; - - answer = dns_answer_new(n); - if (!answer) - return -ENOMEM; - - for (i = 0; i < n; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - bool cache_flush; - - r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); - if (r < 0) - return r; - - /* 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 is 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 (DNS_PACKET_QR(p)) { - /* Additional checks for responses */ - - if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) { - /* If this is a reply and we don't know the EDNS version then something - * is weird... */ - log_debug("EDNS version newer that our request, bad server."); - return -EBADMSG; - } - - if (has_rfc6975) { - /* If the 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 routers 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) - return r; - } - - /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note - * that we only do this if we actually decided to keep the RR around. */ - dns_resource_record_unref(previous); - previous = dns_resource_record_ref(rr); - } - - if (bad_opt) - p->opt = dns_resource_record_unref(p->opt); - } - - p->question = question; - question = NULL; - - p->answer = answer; - answer = NULL; - - p->extracted = true; - - /* no CANCEL, always rewind */ - return 0; -} - -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", - [DNS_RCODE_SERVFAIL] = "SERVFAIL", - [DNS_RCODE_NXDOMAIN] = "NXDOMAIN", - [DNS_RCODE_NOTIMP] = "NOTIMP", - [DNS_RCODE_REFUSED] = "REFUSED", - [DNS_RCODE_YXDOMAIN] = "YXDOMAIN", - [DNS_RCODE_YXRRSET] = "YRRSET", - [DNS_RCODE_NXRRSET] = "NXRRSET", - [DNS_RCODE_NOTAUTH] = "NOTAUTH", - [DNS_RCODE_NOTZONE] = "NOTZONE", - [DNS_RCODE_BADVERS] = "BADVERS", - [DNS_RCODE_BADKEY] = "BADKEY", - [DNS_RCODE_BADTIME] = "BADTIME", - [DNS_RCODE_BADMODE] = "BADMODE", - [DNS_RCODE_BADNAME] = "BADNAME", - [DNS_RCODE_BADALG] = "BADALG", - [DNS_RCODE_BADTRUNC] = "BADTRUNC", -}; -DEFINE_STRING_TABLE_LOOKUP(dns_rcode, int); - -static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { - [DNS_PROTOCOL_DNS] = "dns", - [DNS_PROTOCOL_MDNS] = "mdns", - [DNS_PROTOCOL_LLMNR] = "llmnr", -}; -DEFINE_STRING_TABLE_LOOKUP(dns_protocol, DnsProtocol); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h deleted file mode 100644 index 7b7d4e14c9..0000000000 --- a/src/resolve/resolved-dns-packet.h +++ /dev/null @@ -1,302 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include -#include - -#include "hashmap.h" -#include "in-addr-util.h" -#include "macro.h" -#include "sparse-endian.h" - -typedef struct DnsPacketHeader DnsPacketHeader; -typedef struct DnsPacket DnsPacket; - -#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, - DNS_PROTOCOL_MDNS, - DNS_PROTOCOL_LLMNR, - _DNS_PROTOCOL_MAX, - _DNS_PROTOCOL_INVALID = -1 -} DnsProtocol; - -struct DnsPacketHeader { - uint16_t id; - be16_t flags; - be16_t qdcount; - be16_t ancount; - be16_t nscount; - be16_t arcount; -}; - -#define DNS_PACKET_HEADER_SIZE sizeof(DnsPacketHeader) -#define UDP_PACKET_HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr)) - -/* The various DNS protocols deviate in how large a packet can grow, - but the TCP transport has a 16bit size field, hence that appears to - be the absolute maximum. */ -#define DNS_PACKET_SIZE_MAX 0xFFFF - -/* 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 { - int n_ref; - DnsProtocol protocol; - 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; - int family, ipproto; - union in_addr_union sender, destination; - uint16_t sender_port, destination_port; - uint32_t ttl; - - /* 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) { - if (_unlikely_(!p)) - return NULL; - - if (p->_data) - return p->_data; - - return ((uint8_t*) p) + ALIGN(sizeof(DnsPacket)); -} - -#define DNS_PACKET_HEADER(p) ((DnsPacketHeader*) DNS_PACKET_DATA(p)) -#define DNS_PACKET_ID(p) DNS_PACKET_HEADER(p)->id -#define DNS_PACKET_QR(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 15) & 1) -#define DNS_PACKET_OPCODE(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 11) & 15) -#define DNS_PACKET_AA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 10) & 1) -#define DNS_PACKET_TC(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 9) & 1) -#define DNS_PACKET_RD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 8) & 1) -#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_FLAG_TC (UINT16_C(1) << 9) - -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) & 0xF); -} - -static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) { - - /* Returns the advertised maximum datagram size for replies, or the DNS default if there's nothing defined. */ - - if (p->opt) - return MAX(DNS_PACKET_UNICAST_SIZE_MAX, p->opt->key->class); - - return DNS_PACKET_UNICAST_SIZE_MAX; -} - -static inline bool DNS_PACKET_DO(DnsPacket *p) { - if (!p->opt) - return false; - - return !!(p->opt->ttl & (1U << 15)); -} - -static inline bool DNS_PACKET_VERSION_SUPPORTED(DnsPacket *p) { - /* Returns true if this packet is in a version we support. Which means either non-EDNS or EDNS(0), but not EDNS - * of any newer versions */ - - if (!p->opt) - return true; - - return DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(p->opt); -} - -/* LLMNR defines some bits differently */ -#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p) -#define DNS_PACKET_LLMNR_T(p) DNS_PACKET_RD(p) - -#define DNS_PACKET_QDCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->qdcount) -#define DNS_PACKET_ANCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->ancount) -#define DNS_PACKET_NSCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->nscount) -#define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount) - -#define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \ - (((uint16_t) !!(qr) << 15) | \ - ((uint16_t) ((opcode) & 15) << 11) | \ - ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \ - ((uint16_t) !!(tc) << 9) | \ - ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \ - ((uint16_t) !!(ra) << 7) | \ - ((uint16_t) !!(ad) << 5) | \ - ((uint16_t) !!(cd) << 4) | \ - ((uint16_t) ((rcode) & 15))) - -static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { - return - (unsigned) DNS_PACKET_ANCOUNT(p) + - (unsigned) DNS_PACKET_NSCOUNT(p) + - (unsigned) DNS_PACKET_ARCOUNT(p); -} - -int dns_packet_new(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); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref); - -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_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, size_t *rdata_start); -int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start); -int dns_packet_append_question(DnsPacket *p, DnsQuestion *q); -int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a); - -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); -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_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, - DNS_RCODE_SERVFAIL = 2, - DNS_RCODE_NXDOMAIN = 3, - DNS_RCODE_NOTIMP = 4, - DNS_RCODE_REFUSED = 5, - DNS_RCODE_YXDOMAIN = 6, - DNS_RCODE_YXRRSET = 7, - DNS_RCODE_NXRRSET = 8, - DNS_RCODE_NOTAUTH = 9, - DNS_RCODE_NOTZONE = 10, - DNS_RCODE_BADVERS = 16, - DNS_RCODE_BADSIG = 16, /* duplicate value! */ - DNS_RCODE_BADKEY = 17, - DNS_RCODE_BADTIME = 18, - DNS_RCODE_BADMODE = 19, - DNS_RCODE_BADNAME = 20, - DNS_RCODE_BADALG = 21, - DNS_RCODE_BADTRUNC = 22, - _DNS_RCODE_MAX_DEFINED, - _DNS_RCODE_MAX = 4095 /* 4 bit rcode in the header plus 8 bit rcode in OPT, makes 12 bit */ -}; - -const char* dns_rcode_to_string(int i) _const_; -int dns_rcode_from_string(const char *s) _pure_; - -const char* dns_protocol_to_string(DnsProtocol p) _const_; -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 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 uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, bool authenticated) { - uint64_t f; - - /* Converts a protocol + family into a flags field as used in queries and responses */ - - f = authenticated ? SD_RESOLVED_AUTHENTICATED : 0; - - switch (protocol) { - case DNS_PROTOCOL_DNS: - return f|SD_RESOLVED_DNS; - - case DNS_PROTOCOL_LLMNR: - return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); - - case DNS_PROTOCOL_MDNS: - return f|(family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4); - - default: - return f; - } -} diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c deleted file mode 100644 index 53be18efc6..0000000000 --- a/src/resolve/resolved-dns-query.c +++ /dev/null @@ -1,1121 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "dns-domain.h" -#include "dns-type.h" -#include "hostname-util.h" -#include "local-addresses.h" -#include "resolved-dns-query.h" -#include "resolved-dns-synthesize.h" -#include "resolved-etc-hosts.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 int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) { - DnsQueryCandidate *c; - - assert(ret); - assert(q); - assert(s); - - 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; -} - -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); - set_remove(t->notify_query_candidates_done, 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; - else - next = dns_scope_get_search_domains(c->scope); - - for (;;) { - if (!next) /* We hit the end of the list */ - return 0; - - if (!next->route_only) - break; - - /* Skip over route-only domains */ - next = next->domains_next; - } - - 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_ensure_allocated(&t->notify_query_candidates_done, 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; - } - - t->clamp_ttl = c->query->clamp_ttl; - return 1; - -gc: - dns_transaction_gc(t); - return r; -} - -static int dns_query_candidate_go(DnsQueryCandidate *c) { - DnsTransaction *t; - Iterator i; - int r; - unsigned n = 0; - - 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; - - n++; - } - - /* If there was nothing to start, then let's proceed immediately */ - if (n == 0) - dns_query_candidate_notify(c); - - 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_ERRNO; - - 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 bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) { - int family; - - assert(c); - - /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of - * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR, - * or a routable IPv6 address if we query an AAAA RR. */ - - if (!c->query->suppress_unroutable_family) - return true; - - if (c->scope->protocol != DNS_PROTOCOL_DNS) - return true; - - family = dns_type_to_af(type); - if (family < 0) - return true; - - if (c->scope->link) - return link_relevant(c->scope->link, family, false); - else - return manager_routable(c->scope->manager, family); -} - -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; - DnsResourceKey *qkey; - - if (!dns_query_candidate_is_routable(c, key->type)) - continue; - - if (c->search_domain) { - r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name); - if (r < 0) - goto fail; - - qkey = new_key; - } else - qkey = key; - - if (!dns_scope_good_key(c->scope, qkey)) - continue; - - r = dns_query_candidate_add_transaction(c, qkey); - 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_errno = 0; - 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; - - while (q->auxiliary_queries) - dns_query_free(q->auxiliary_queries); - - 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); - - dns_packet_unref(q->request_dns_packet); - - if (q->request_dns_stream) { - /* Detach the stream from our query, in case something else keeps a reference to it. */ - q->request_dns_stream->complete = NULL; - q->request_dns_stream->on_packet = NULL; - q->request_dns_stream->query = NULL; - dns_stream_unref(q->request_dns_stream); - } - - free(q->request_address_string); - - if (q->manager) { - LIST_REMOVE(queries, q->manager->dns_queries, q); - q->manager->n_dns_queries--; - } - - free(q); - - return NULL; -} - -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; - DnsResourceKey *key; - bool good = false; - int r; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - assert(m); - - 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; - - q = new0(DnsQuery, 1); - if (!q) - return -ENOMEM; - - 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; - - /* First dump UTF8 question */ - DNS_QUESTION_FOREACH(key, question_utf8) - log_debug("Looking up RR for %s.", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - - /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */ - DNS_QUESTION_FOREACH(key, question_idna) { - r = dns_question_contains(question_utf8, key); - if (r < 0) - return r; - if (r > 0) - continue; - - log_debug("Looking up IDNA RR for %s.", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - } - - LIST_PREPEND(queries, m->dns_queries, q); - m->n_dns_queries++; - q->manager = m; - - if (ret) - *ret = q; - q = NULL; - - return 0; -} - -int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) { - assert(q); - assert(auxiliary_for); - - /* Ensure 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(!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 - * after calling this function. */ - - q->state = state; - - dns_query_stop(q); - if (q->complete) - q->complete(q); -} - -static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { - DnsQuery *q = userdata; - - assert(s); - assert(q); - - dns_query_complete(q, DNS_TRANSACTION_TIMEOUT); - return 0; -} - -static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { - DnsQueryCandidate *c; - int r; - - assert(q); - assert(s); - - r = dns_query_candidate_new(&c, q, s); - if (r < 0) - return r; - - /* 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) - goto fail; - if (r > 0) { - /* OK, we need a search domain now. Let's find one for this scope */ - - 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 = dns_query_candidate_setup_transactions(c); - if (r < 0) - goto fail; - - return 0; - -fail: - dns_query_candidate_free(c); - return r; -} - -static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - int r; - - assert(q); - assert(state); - - /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the - * the normal lookup finished. The data from the network hence takes precedence over the data we - * synthesize. (But note that many scopes refuse to resolve certain domain names) */ - - if (!IN_SET(*state, - DNS_TRANSACTION_RCODE_FAILURE, - DNS_TRANSACTION_NO_SERVERS, - DNS_TRANSACTION_TIMEOUT, - DNS_TRANSACTION_ATTEMPTS_MAX_REACHED, - DNS_TRANSACTION_NETWORK_DOWN, - DNS_TRANSACTION_NOT_FOUND)) - return 0; - - r = dns_synthesize_answer( - q->manager, - q->question_utf8, - q->ifindex, - &answer); - - if (r <= 0) - return r; - - dns_query_reset_answer(q); - - q->answer = answer; - answer = NULL; - q->answer_rcode = DNS_RCODE_SUCCESS; - q->answer_protocol = dns_synthesize_protocol(q->flags); - q->answer_family = dns_synthesize_family(q->flags); - q->answer_authenticated = true; - - *state = DNS_TRANSACTION_SUCCESS; - - return 1; -} - -static int dns_query_try_etc_hosts(DnsQuery *q) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - int r; - - assert(q); - - /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The - * data from /etc/hosts hence takes precedence over the network. */ - - r = manager_etc_hosts_lookup( - q->manager, - q->question_utf8, - &answer); - if (r <= 0) - return r; - - dns_query_reset_answer(q); - - q->answer = answer; - answer = NULL; - q->answer_rcode = DNS_RCODE_SUCCESS; - q->answer_protocol = dns_synthesize_protocol(q->flags); - q->answer_family = dns_synthesize_family(q->flags); - q->answer_authenticated = true; - - return 1; -} - -int dns_query_go(DnsQuery *q) { - DnsScopeMatch found = DNS_SCOPE_NO; - DnsScope *s, *first = NULL; - DnsQueryCandidate *c; - int r; - - assert(q); - - if (q->state != DNS_TRANSACTION_NULL) - return 0; - - r = dns_query_try_etc_hosts(q); - if (r < 0) - return r; - if (r > 0) { - dns_query_complete(q, DNS_TRANSACTION_SUCCESS); - return 1; - } - - 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) - return match; - - if (match == DNS_SCOPE_NO) - continue; - - found = match; - - if (match == DNS_SCOPE_YES) { - first = s; - break; - } else { - assert(match == DNS_SCOPE_MAYBE); - - if (!first) - first = s; - } - } - - if (found == DNS_SCOPE_NO) { - DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; - - r = dns_query_synthesize_reply(q, &state); - if (r < 0) - return r; - - dns_query_complete(q, state); - return 1; - } - - 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) - goto fail; - - if (match != found) - continue; - - r = dns_query_add_candidate(q, s); - if (r < 0) - goto fail; - } - - dns_query_reset_answer(q); - - r = sd_event_add_time( - q->manager->event, - &q->timeout_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0, - on_query_timeout, 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 */ - 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--; - dns_query_ready(q); - - return 1; - -fail: - dns_query_stop(q); - return r; -} - -static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { - DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; - 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); - - if (!c) { - r = dns_query_synthesize_reply(q, &state); - if (r < 0) - goto fail; - - dns_query_complete(q, state); - return; - } - - if (c->error_code != 0) { - /* If the candidate had an error condition of its own, start with that. */ - state = DNS_TRANSACTION_ERRNO; - q->answer = dns_answer_unref(q->answer); - q->answer_rcode = 0; - q->answer_dnssec_result = _DNSSEC_RESULT_INVALID; - q->answer_errno = c->error_code; - } - - SET_FOREACH(t, c->transactions, i) { - - switch (t->state) { - - case DNS_TRANSACTION_SUCCESS: { - /* We found a successfully reply, merge it into the answer */ - r = dns_answer_extend(&q->answer, t->answer); - if (r < 0) - goto fail; - - q->answer_rcode = t->answer_rcode; - q->answer_errno = 0; - - if (t->answer_authenticated) { - has_authenticated = true; - dnssec_result_authenticated = t->answer_dnssec_result; - } else { - has_non_authenticated = true; - dnssec_result_non_authenticated = t->answer_dnssec_result; - } - - state = DNS_TRANSACTION_SUCCESS; - break; - } - - case DNS_TRANSACTION_NULL: - case DNS_TRANSACTION_PENDING: - case DNS_TRANSACTION_VALIDATING: - case DNS_TRANSACTION_ABORTED: - /* Ignore transactions that didn't complete */ - continue; - - default: - /* Any kind of failure? Store the data away, - * if there's nothing stored yet. */ - - if (state == DNS_TRANSACTION_SUCCESS) - continue; - - q->answer = dns_answer_unref(q->answer); - q->answer_rcode = t->answer_rcode; - q->answer_dnssec_result = t->answer_dnssec_result; - q->answer_errno = t->answer_errno; - - 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; - } - - q->answer_protocol = c->scope->protocol; - q->answer_family = c->scope->family; - - dns_search_domain_unref(q->answer_search_domain); - q->answer_search_domain = dns_search_domain_ref(c->search_domain); - - r = dns_query_synthesize_reply(q, &state); - if (r < 0) - goto fail; - - dns_query_complete(q, state); - return; - -fail: - q->answer_errno = -r; - dns_query_complete(q, DNS_TRANSACTION_ERRNO); -} - -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; - - 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; - - default: - /* Any kind of failure */ - bad = c; - break; - } - } - - if (pending) - return; - - dns_query_accept(q, bad); -} - -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_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)); - - 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)); - } - - if (r == 0 && k == 0) /* No actual cname happened? */ - return -ELOOP; - - if (q->answer_protocol == DNS_PROTOCOL_DNS) { - /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources - * cannot invade the local namespace. The opposite way we permit: local names may redirect to global - * ones. */ - - q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */ - } - - /* Turn off searching for the new name */ - q->flags |= SD_RESOLVED_NO_SEARCH; - - 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; - - 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; - - assert(t); - assert(q); - - log_debug("Client of active query vanished, aborting query."); - dns_query_complete(q, DNS_TRANSACTION_ABORTED); - return 0; -} - -int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { - int r; - - assert(q); - assert(m); - - if (!q->bus_track) { - r = sd_bus_track_new(sd_bus_message_get_bus(m), &q->bus_track, on_bus_track, q); - if (r < 0) - return r; - } - - r = sd_bus_track_add_sender(q->bus_track, m); - if (r < 0) - return r; - - 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 deleted file mode 100644 index 49a35b846b..0000000000 --- a/src/resolve/resolved-dns-query.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - - -#include "sd-bus.h" - -#include "set.h" - -typedef struct DnsQueryCandidate DnsQueryCandidate; -typedef struct DnsQuery DnsQuery; - -#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; - - /* 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; - - /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address - * family */ - bool suppress_unroutable_family; - - - /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */ - bool clamp_ttl; - - DnsTransactionState state; - unsigned n_cname_redirects; - - LIST_HEAD(DnsQueryCandidate, candidates); - sd_event_source *timeout_event_source; - - /* Discovered data */ - DnsAnswer *answer; - int answer_rcode; - DnssecResult answer_dnssec_result; - bool answer_authenticated; - DnsProtocol answer_protocol; - int answer_family; - DnsSearchDomain *answer_search_domain; - int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ - - /* Bus client information */ - sd_bus_message *request; - int request_family; - bool request_address_valid; - union in_addr_union request_address; - unsigned block_all_complete; - char *request_address_string; - - /* DNS stub information */ - DnsPacket *request_dns_packet; - DnsStream *request_dns_stream; - - /* Completion callback */ - void (*complete)(DnsQuery* q); - unsigned block_ready; - - sd_bus_track *bus_track; - - LIST_FIELDS(DnsQuery, queries); - LIST_FIELDS(DnsQuery, auxiliary_queries); -}; - -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_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 deleted file mode 100644 index c8b502d1cd..0000000000 --- a/src/resolve/resolved-dns-question.c +++ /dev/null @@ -1,468 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "dns-domain.h" -#include "dns-type.h" -#include "resolved-dns-question.h" - -DnsQuestion *dns_question_new(unsigned n) { - DnsQuestion *q; - - assert(n > 0); - - q = malloc0(offsetof(DnsQuestion, keys) + sizeof(DnsResourceKey*) * n); - if (!q) - return NULL; - - q->n_ref = 1; - q->n_allocated = n; - - return q; -} - -DnsQuestion *dns_question_ref(DnsQuestion *q) { - if (!q) - return NULL; - - assert(q->n_ref > 0); - q->n_ref++; - return q; -} - -DnsQuestion *dns_question_unref(DnsQuestion *q) { - if (!q) - return NULL; - - assert(q->n_ref > 0); - - if (q->n_ref == 1) { - unsigned i; - - for (i = 0; i < q->n_keys; i++) - dns_resource_key_unref(q->keys[i]); - free(q); - } else - q->n_ref--; - - return NULL; -} - -int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { - unsigned i; - int r; - - assert(key); - - if (!q) - return -ENOSPC; - - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_equal(q->keys[i], key); - if (r < 0) - return r; - if (r > 0) - return 0; - } - - if (q->n_keys >= q->n_allocated) - return -ENOSPC; - - q->keys[q->n_keys++] = dns_resource_key_ref(key); - return 0; -} - -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { - unsigned i; - int r; - - assert(rr); - - if (!q) - return 0; - - for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_rr(q->keys[i], rr, search_domain); - if (r != 0) - return r; - } - - return 0; -} - -int dns_question_matches_cname_or_dname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { - unsigned i; - int r; - - assert(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++) { - /* 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; - } - - return 0; -} - -int dns_question_is_valid_for_query(DnsQuestion *q) { - const char *name; - unsigned i; - int r; - - if (!q) - return 0; - - if (q->n_keys <= 0) - return 0; - - if (q->n_keys > 65535) - return 0; - - name = dns_resource_key_name(q->keys[0]); - if (!name) - return 0; - - /* Check that all keys in this question bear the same name */ - for (i = 0; i < q->n_keys; i++) { - assert(q->keys[i]); - - if (i > 0) { - r = dns_name_equal(dns_resource_key_name(q->keys[i]), name); - if (r <= 0) - return r; - } - - if (!dns_type_is_valid_query(q->keys[i]->type)) - return 0; - } - - return 1; -} - -int dns_question_contains(DnsQuestion *a, const DnsResourceKey *k) { - unsigned j; - int r; - - assert(k); - - if (!a) - return 0; - - for (j = 0; j < a->n_keys; j++) { - r = dns_resource_key_equal(a->keys[j], k); - if (r != 0) - return r; - } - - return 0; -} - -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) - return a->n_keys == 0; - - /* Checks if all keys in a are also contained b, and vice versa */ - - for (j = 0; j < a->n_keys; j++) { - r = dns_question_contains(b, a->keys[j]); - if (r <= 0) - return r; - } - - for (j = 0; j < b->n_keys; j++) { - r = dns_question_contains(a, b->keys[j]); - if (r <= 0) - return r; - } - - return 1; -} - -int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) { - _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; - DnsResourceKey *key; - bool same = true; - int r; - - assert(cname); - assert(ret); - assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)); - - if (dns_question_size(q) <= 0) { - *ret = NULL; - return 0; - } - - 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; - - if (r == 0) { - same = false; - break; - } - } - - /* Fully the same, indicate we didn't do a thing */ - if (same) { - *ret = NULL; - return 0; - } - - n = dns_question_new(q->n_keys); - if (!n) - return -ENOMEM; - - /* Create a new question, and patch in the new name */ - DNS_QUESTION_FOREACH(key, q) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; - - k = dns_resource_key_new_redirect(key, cname); - if (!k) - return -ENOMEM; - - r = dns_question_add(n, k); - if (r < 0) - return r; - } - - *ret = n; - n = NULL; - - 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 deleted file mode 100644 index a9a1863b1e..0000000000 --- a/src/resolve/resolved-dns-question.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct DnsQuestion DnsQuestion; - -#include "macro.h" -#include "resolved-dns-rr.h" - -/* A simple array of resource keys */ - -struct DnsQuestion { - unsigned n_ref; - unsigned n_keys, n_allocated; - DnsResourceKey* keys[0]; -}; - -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, 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; -} - -static inline bool dns_question_isempty(DnsQuestion *q) { - return dns_question_size(q) <= 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 deleted file mode 100644 index 5687588a7d..0000000000 --- a/src/resolve/resolved-dns-rr.c +++ /dev/null @@ -1,1839 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "dns-domain.h" -#include "dns-type.h" -#include "escape.h" -#include "hexdecoct.h" -#include "resolved-dns-dnssec.h" -#include "resolved-dns-packet.h" -#include "resolved-dns-rr.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" - -DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) { - DnsResourceKey *k; - size_t l; - - assert(name); - - l = strlen(name); - k = malloc0(sizeof(DnsResourceKey) + l + 1); - if (!k) - return NULL; - - k->n_ref = 1; - k->class = class; - k->type = type; - - strcpy((char*) k + sizeof(DnsResourceKey), name); - - return k; -} - -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; - - 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; - } -} - -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(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; - - 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) { - DnsResourceKey *k; - - assert(name); - - k = new0(DnsResourceKey, 1); - if (!k) - return NULL; - - k->n_ref = 1; - k->class = class; - k->type = type; - k->_name = name; - - return k; -} - -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++; - - return k; -} - -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) { - free(k->_name); - free(k); - } else - k->n_ref--; - - return NULL; -} - -const char* dns_resource_key_name(const DnsResourceKey *key) { - const char *name; - - if (!key) - return NULL; - - if (key->_name) - name = key->_name; - else - name = (char*) key + sizeof(DnsResourceKey); - - if (dns_name_is_root(name)) - return "."; - else - return name; -} - -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; - - if (a->class != b->class) - return 0; - - if (a->type != b->type) - return 0; - - return 1; -} - -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; - - 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_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain) { - int r; - - assert(key); - assert(cname); - - if (cname->class != key->class && key->class != DNS_CLASS_ANY) - return 0; - - 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; - - 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; -} - -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; - - assert(k); - - 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) { - const DnsResourceKey *x = a, *y = b; - int ret; - - ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y)); - if (ret != 0) - return ret; - - if (x->type < y->type) - return -1; - if (x->type > y->type) - return 1; - - if (x->class < y->class) - return -1; - if (x->class > y->class) - return 1; - - return 0; -} - -const struct hash_ops dns_resource_key_hash_ops = { - .hash = dns_resource_key_hash_func, - .compare = dns_resource_key_compare_func -}; - -char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) { - const char *c, *t; - char *ans = buf; - - /* 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); - t = dns_type_to_string(key->type); - - snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u", - dns_resource_key_name(key), - c ?: "", c ? "" : "CLASS", c ? 0 : key->class, - t ?: "", t ? "" : "TYPE", t ? 0 : key->class); - - return ans; -} - -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; - - rr = new0(DnsResourceRecord, 1); - if (!rr) - return NULL; - - 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; -} - -DnsResourceRecord* dns_resource_record_new_full(uint16_t class, uint16_t type, const char *name) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(class, type, name); - if (!key) - return NULL; - - return dns_resource_record_new(key); -} - -DnsResourceRecord* dns_resource_record_ref(DnsResourceRecord *rr) { - if (!rr) - return NULL; - - assert(rr->n_ref > 0); - rr->n_ref++; - - return rr; -} - -DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { - if (!rr) - return NULL; - - assert(rr->n_ref > 0); - - if (rr->n_ref > 1) { - rr->n_ref--; - return NULL; - } - - if (rr->key) { - switch(rr->key->type) { - - case DNS_TYPE_SRV: - free(rr->srv.name); - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - free(rr->ptr.name); - break; - - case DNS_TYPE_HINFO: - free(rr->hinfo.cpu); - free(rr->hinfo.os); - break; - - case DNS_TYPE_TXT: - case DNS_TYPE_SPF: - dns_txt_item_free_all(rr->txt.items); - break; - - case DNS_TYPE_SOA: - free(rr->soa.mname); - free(rr->soa.rname); - break; - - case DNS_TYPE_MX: - free(rr->mx.exchange); - break; - - case DNS_TYPE_DS: - free(rr->ds.digest); - break; - - case DNS_TYPE_SSHFP: - free(rr->sshfp.fingerprint); - break; - - case DNS_TYPE_DNSKEY: - free(rr->dnskey.key); - break; - - case DNS_TYPE_RRSIG: - free(rr->rrsig.signer); - free(rr->rrsig.signature); - break; - - case DNS_TYPE_NSEC: - free(rr->nsec.next_domain_name); - bitmap_free(rr->nsec.types); - break; - - case DNS_TYPE_NSEC3: - free(rr->nsec3.next_hashed_name); - free(rr->nsec3.salt); - bitmap_free(rr->nsec3.types); - break; - - case DNS_TYPE_LOC: - case DNS_TYPE_A: - case DNS_TYPE_AAAA: - break; - - case DNS_TYPE_TLSA: - free(rr->tlsa.data); - break; - - case DNS_TYPE_CAA: - free(rr->caa.tag); - free(rr->caa.value); - break; - - case DNS_TYPE_OPENPGPKEY: - default: - free(rr->generic.data); - } - - free(rr->wire_format); - dns_resource_key_unref(rr->key); - } - - free(rr->to_string); - free(rr); - - return NULL; -} - -int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *hostname) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_free_ char *ptr = NULL; - int r; - - assert(ret); - assert(address); - assert(hostname); - - r = dns_name_reverse(family, address, &ptr); - if (r < 0) - return r; - - key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, ptr); - if (!key) - return -ENOMEM; - - ptr = NULL; - - rr = dns_resource_record_new(key); - if (!rr) - return -ENOMEM; - - rr->ptr.name = strdup(hostname); - if (!rr->ptr.name) - return -ENOMEM; - - *ret = rr; - rr = NULL; - - return 0; -} - -int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name) { - DnsResourceRecord *rr; - - assert(ret); - assert(address); - assert(family); - - if (family == AF_INET) { - - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name); - if (!rr) - return -ENOMEM; - - rr->a.in_addr = address->in; - - } else if (family == AF_INET6) { - - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name); - if (!rr) - return -ENOMEM; - - rr->aaaa.in6_addr = address->in6; - } else - return -EAFNOSUPPORT; - - *ret = rr; - - return 0; -} - -#define FIELD_EQUAL(a, b, field) \ - ((a).field ## _size == (b).field ## _size && \ - memcmp((a).field, (b).field, (a).field ## _size) == 0) - -int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { - int r; - - assert(a); - assert(b); - - if (a == b) - return 1; - - r = dns_resource_key_equal(a->key, b->key); - if (r <= 0) - return r; - - if (a->unparseable != b->unparseable) - return 0; - - switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) { - - case DNS_TYPE_SRV: - r = dns_name_equal(a->srv.name, b->srv.name); - if (r <= 0) - return r; - - return a->srv.priority == b->srv.priority && - a->srv.weight == b->srv.weight && - a->srv.port == b->srv.port; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - return dns_name_equal(a->ptr.name, b->ptr.name); - - case DNS_TYPE_HINFO: - return strcaseeq(a->hinfo.cpu, b->hinfo.cpu) && - strcaseeq(a->hinfo.os, b->hinfo.os); - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - 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; - - case DNS_TYPE_AAAA: - return memcmp(&a->aaaa.in6_addr, &b->aaaa.in6_addr, sizeof(struct in6_addr)) == 0; - - case DNS_TYPE_SOA: - r = dns_name_equal(a->soa.mname, b->soa.mname); - if (r <= 0) - return r; - r = dns_name_equal(a->soa.rname, b->soa.rname); - if (r <= 0) - return r; - - return a->soa.serial == b->soa.serial && - a->soa.refresh == b->soa.refresh && - a->soa.retry == b->soa.retry && - a->soa.expire == b->soa.expire && - a->soa.minimum == b->soa.minimum; - - case DNS_TYPE_MX: - if (a->mx.priority != b->mx.priority) - return 0; - - return dns_name_equal(a->mx.exchange, b->mx.exchange); - - case DNS_TYPE_LOC: - assert(a->loc.version == b->loc.version); - - return a->loc.size == b->loc.size && - a->loc.horiz_pre == b->loc.horiz_pre && - a->loc.vert_pre == b->loc.vert_pre && - a->loc.latitude == b->loc.latitude && - a->loc.longitude == b->loc.longitude && - a->loc.altitude == b->loc.altitude; - - case DNS_TYPE_DS: - return a->ds.key_tag == b->ds.key_tag && - a->ds.algorithm == b->ds.algorithm && - a->ds.digest_type == b->ds.digest_type && - FIELD_EQUAL(a->ds, b->ds, digest); - - case DNS_TYPE_SSHFP: - return a->sshfp.algorithm == b->sshfp.algorithm && - a->sshfp.fptype == b->sshfp.fptype && - FIELD_EQUAL(a->sshfp, b->sshfp, fingerprint); - - case DNS_TYPE_DNSKEY: - return a->dnskey.flags == b->dnskey.flags && - a->dnskey.protocol == b->dnskey.protocol && - a->dnskey.algorithm == b->dnskey.algorithm && - FIELD_EQUAL(a->dnskey, b->dnskey, key); - - case DNS_TYPE_RRSIG: - /* do the fast comparisons first */ - return a->rrsig.type_covered == b->rrsig.type_covered && - a->rrsig.algorithm == b->rrsig.algorithm && - a->rrsig.labels == b->rrsig.labels && - a->rrsig.original_ttl == b->rrsig.original_ttl && - a->rrsig.expiration == b->rrsig.expiration && - a->rrsig.inception == b->rrsig.inception && - a->rrsig.key_tag == b->rrsig.key_tag && - FIELD_EQUAL(a->rrsig, b->rrsig, signature) && - dns_name_equal(a->rrsig.signer, b->rrsig.signer); - - case DNS_TYPE_NSEC: - return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) && - bitmap_equal(a->nsec.types, b->nsec.types); - - case DNS_TYPE_NSEC3: - return a->nsec3.algorithm == b->nsec3.algorithm && - a->nsec3.flags == b->nsec3.flags && - a->nsec3.iterations == b->nsec3.iterations && - FIELD_EQUAL(a->nsec3, b->nsec3, salt) && - FIELD_EQUAL(a->nsec3, b->nsec3, next_hashed_name) && - bitmap_equal(a->nsec3.types, b->nsec3.types); - - case DNS_TYPE_TLSA: - return a->tlsa.cert_usage == b->tlsa.cert_usage && - a->tlsa.selector == b->tlsa.selector && - a->tlsa.matching_type == b->tlsa.matching_type && - FIELD_EQUAL(a->tlsa, b->tlsa, data); - - case DNS_TYPE_CAA: - return a->caa.flags == b->caa.flags && - streq(a->caa.tag, b->caa.tag) && - FIELD_EQUAL(a->caa, b->caa, value); - - case DNS_TYPE_OPENPGPKEY: - default: - return FIELD_EQUAL(a->generic, b->generic, data); - } -} - -static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude, - uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) { - char *s; - char NS = latitude >= 1U<<31 ? 'N' : 'S'; - char EW = longitude >= 1U<<31 ? 'E' : 'W'; - - int lat = latitude >= 1U<<31 ? (int) (latitude - (1U<<31)) : (int) ((1U<<31) - latitude); - int lon = longitude >= 1U<<31 ? (int) (longitude - (1U<<31)) : (int) ((1U<<31) - longitude); - double alt = altitude >= 10000000u ? altitude - 10000000u : -(double)(10000000u - altitude); - double siz = (size >> 4) * exp10((double) (size & 0xF)); - double hor = (horiz_pre >> 4) * exp10((double) (horiz_pre & 0xF)); - double ver = (vert_pre >> 4) * exp10((double) (vert_pre & 0xF)); - - if (asprintf(&s, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm", - (lat / 60000 / 60), - (lat / 60000) % 60, - (lat % 60000) / 1000., - NS, - (lon / 60000 / 60), - (lon / 60000) % 60, - (lon % 60000) / 1000., - EW, - alt / 100., - siz / 100., - hor / 100., - ver / 100.) < 0) - return NULL; - - return s; -} - -static int format_timestamp_dns(char *buf, size_t l, time_t sec) { - struct tm tm; - - assert(buf); - assert(l > strlen("YYYYMMDDHHmmSS")); - - if (!gmtime_r(&sec, &tm)) - return -EINVAL; - - if (strftime(buf, l, "%Y%m%d%H%M%S", &tm) <= 0) - return -EINVAL; - - return 0; -} - -static char *format_types(Bitmap *types) { - _cleanup_strv_free_ char **strv = NULL; - _cleanup_free_ char *str = NULL; - Iterator i; - unsigned type; - int r; - - BITMAP_FOREACH(type, types, i) { - if (dns_type_to_string(type)) { - r = strv_extend(&strv, dns_type_to_string(type)); - if (r < 0) - return NULL; - } else { - char *t; - - r = asprintf(&t, "TYPE%u", type); - if (r < 0) - return NULL; - - r = strv_consume(&strv, t); - if (r < 0) - return NULL; - } - } - - str = strv_join(strv, " "); - if (!str) - return NULL; - - return strjoin("( ", str, " )", NULL); -} - -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 *t = NULL; - char *s, k[DNS_RESOURCE_KEY_STRING_MAX]; - int r; - - assert(rr); - - if (rr->to_string) - return rr->to_string; - - dns_resource_key_to_string(rr->key, k, sizeof(k)); - - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - - case DNS_TYPE_SRV: - r = asprintf(&s, "%s %u %u %u %s", - k, - rr->srv.priority, - rr->srv.weight, - rr->srv.port, - strna(rr->srv.name)); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - s = strjoin(k, " ", rr->ptr.name, NULL); - if (!s) - return NULL; - - break; - - case DNS_TYPE_HINFO: - s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL); - if (!s) - return NULL; - break; - - case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: - t = format_txt(rr->txt.items); - if (!t) - return NULL; - - s = strjoin(k, " ", t, NULL); - if (!s) - return NULL; - break; - - case DNS_TYPE_A: { - _cleanup_free_ char *x = NULL; - - r = in_addr_to_string(AF_INET, (const union in_addr_union*) &rr->a.in_addr, &x); - if (r < 0) - return NULL; - - s = strjoin(k, " ", x, NULL); - if (!s) - 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 NULL; - - s = strjoin(k, " ", t, NULL); - if (!s) - return NULL; - break; - - case DNS_TYPE_SOA: - r = asprintf(&s, "%s %s %s %u %u %u %u %u", - k, - strna(rr->soa.mname), - strna(rr->soa.rname), - rr->soa.serial, - rr->soa.refresh, - rr->soa.retry, - rr->soa.expire, - rr->soa.minimum); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_MX: - r = asprintf(&s, "%s %u %s", - k, - rr->mx.priority, - rr->mx.exchange); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_LOC: - assert(rr->loc.version == 0); - - t = format_location(rr->loc.latitude, - rr->loc.longitude, - rr->loc.altitude, - rr->loc.size, - rr->loc.horiz_pre, - rr->loc.vert_pre); - if (!t) - return NULL; - - s = strjoin(k, " ", t, NULL); - if (!s) - return NULL; - break; - - case DNS_TYPE_DS: - t = hexmem(rr->ds.digest, rr->ds.digest_size); - if (!t) - return NULL; - - r = asprintf(&s, "%s %u %u %u %s", - k, - rr->ds.key_tag, - rr->ds.algorithm, - rr->ds.digest_type, - t); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_SSHFP: - t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); - if (!t) - return NULL; - - r = asprintf(&s, "%s %u %u %s", - k, - rr->sshfp.algorithm, - rr->sshfp.fptype, - t); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_DNSKEY: { - _cleanup_free_ char *alg = NULL; - char *ss; - int n; - uint16_t key_tag; - - key_tag = dnssec_keytag(rr, true); - - r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg); - if (r < 0) - return NULL; - - r = asprintf(&s, "%s %u %u %s %n", - k, - rr->dnskey.flags, - rr->dnskey.protocol, - alg, - &n); - if (r < 0) - return NULL; - - r = base64_append(&s, n, - rr->dnskey.key, rr->dnskey.key_size, - 8, columns()); - if (r < 0) - return NULL; - - r = asprintf(&ss, "%s\n" - " -- Flags:%s%s%s\n" - " -- Key tag: %u", - s, - rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "", - rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "", - rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "", - key_tag); - if (r < 0) - return NULL; - free(s); - s = ss; - - break; - } - - case DNS_TYPE_RRSIG: { - _cleanup_free_ char *alg = NULL; - char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1]; - const char *type; - int n; - - type = dns_type_to_string(rr->rrsig.type_covered); - - r = dnssec_algorithm_to_string_alloc(rr->rrsig.algorithm, &alg); - if (r < 0) - return NULL; - - r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration); - if (r < 0) - return NULL; - - r = format_timestamp_dns(inception, sizeof(inception), rr->rrsig.inception); - if (r < 0) - return NULL; - - /* TYPE?? follows - * http://tools.ietf.org/html/rfc3597#section-5 */ - - r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %n", - k, - type ?: "TYPE", - type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered, - alg, - rr->rrsig.labels, - rr->rrsig.original_ttl, - expiration, - inception, - rr->rrsig.key_tag, - rr->rrsig.signer, - &n); - if (r < 0) - return NULL; - - r = base64_append(&s, n, - rr->rrsig.signature, rr->rrsig.signature_size, - 8, columns()); - if (r < 0) - return NULL; - - break; - } - - case DNS_TYPE_NSEC: - t = format_types(rr->nsec.types); - if (!t) - return NULL; - - r = asprintf(&s, "%s %s %s", - k, - rr->nsec.next_domain_name, - t); - if (r < 0) - return NULL; - break; - - case DNS_TYPE_NSEC3: { - _cleanup_free_ char *salt = NULL, *hash = NULL; - - if (rr->nsec3.salt_size > 0) { - salt = hexmem(rr->nsec3.salt, rr->nsec3.salt_size); - if (!salt) - return NULL; - } - - hash = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false); - if (!hash) - return NULL; - - t = format_types(rr->nsec3.types); - if (!t) - return NULL; - - r = asprintf(&s, "%s %"PRIu8" %"PRIu8" %"PRIu16" %s %s %s", - k, - rr->nsec3.algorithm, - rr->nsec3.flags, - rr->nsec3.iterations, - rr->nsec3.salt_size > 0 ? salt : "-", - hash, - t); - if (r < 0) - return NULL; - - break; - } - - case DNS_TYPE_TLSA: { - const char *cert_usage, *selector, *matching_type; - - cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage); - selector = tlsa_selector_to_string(rr->tlsa.selector); - matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type); - - t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); - if (!t) - return NULL; - - r = asprintf(&s, - "%s %u %u %u %s\n" - " -- Cert. usage: %s\n" - " -- Selector: %s\n" - " -- Matching type: %s", - k, - rr->tlsa.cert_usage, - rr->tlsa.selector, - rr->tlsa.matching_type, - t, - cert_usage, - selector, - matching_type); - if (r < 0) - return NULL; - - break; - } - - case DNS_TYPE_CAA: { - _cleanup_free_ char *value; - - value = octescape(rr->caa.value, rr->caa.value_size); - if (!value) - return NULL; - - r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u", - k, - rr->caa.flags, - rr->caa.tag, - value, - rr->caa.flags ? "\n -- Flags:" : "", - rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "", - rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "", - rr->caa.flags & ~CAA_FLAG_CRITICAL); - if (r < 0) - return NULL; - - break; - } - - case DNS_TYPE_OPENPGPKEY: { - int n; - - r = asprintf(&s, "%s %n", - k, - &n); - if (r < 0) - return NULL; - - r = base64_append(&s, n, - rr->generic.data, rr->generic.data_size, - 8, columns()); - if (r < 0) - return NULL; - break; - } - - default: - t = hexmem(rr->generic.data, rr->generic.data_size); - if (!t) - return NULL; - - /* Format as documented in RFC 3597, Section 5 */ - r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t); - if (r < 0) - return NULL; - break; - } - - rr->to_string = s; - return s; -} - -ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) { - assert(rr); - assert(out); - - switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - case DNS_TYPE_SRV: - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - case DNS_TYPE_HINFO: - case DNS_TYPE_SPF: - case DNS_TYPE_TXT: - case DNS_TYPE_A: - case DNS_TYPE_AAAA: - case DNS_TYPE_SOA: - case DNS_TYPE_MX: - case DNS_TYPE_LOC: - case DNS_TYPE_DS: - case DNS_TYPE_DNSKEY: - case DNS_TYPE_RRSIG: - case DNS_TYPE_NSEC: - case DNS_TYPE_NSEC3: - return -EINVAL; - - case DNS_TYPE_SSHFP: - *out = rr->sshfp.fingerprint; - return rr->sshfp.fingerprint_size; - - case DNS_TYPE_TLSA: - *out = rr->tlsa.data; - return rr->tlsa.data_size; - - - case DNS_TYPE_OPENPGPKEY: - default: - *out = rr->generic.data; - return rr->generic.data_size; - } -} - -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; -} - -int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) { - const char *n; - int r; - - assert(rr); - assert(ret); - - /* Returns the RRset's signer, if it is known. */ - - if (rr->n_skip_labels_signer == (unsigned) -1) - return -ENODATA; - - 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_resource_record_source(DnsResourceRecord *rr, const char **ret) { - const char *n; - int r; - - 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; -} - -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; - - case DNS_TYPE_TLSA: - siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state); - siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state); - siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state); - siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state); - break; - - case DNS_TYPE_CAA: - siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state); - string_hash_func(rr->caa.tag, state); - siphash24_compress(rr->caa.value, rr->caa.value_size, state); - break; - - case DNS_TYPE_OPENPGPKEY: - default: - siphash24_compress(rr->generic.data, rr->generic.data_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 ordering, 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, -}; - -DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL; - DnsResourceRecord *t; - - assert(rr); - - copy = dns_resource_record_new(rr->key); - if (!copy) - return NULL; - - copy->ttl = rr->ttl; - copy->expiry = rr->expiry; - copy->n_skip_labels_signer = rr->n_skip_labels_signer; - copy->n_skip_labels_source = rr->n_skip_labels_source; - copy->unparseable = rr->unparseable; - - switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { - - case DNS_TYPE_SRV: - copy->srv.priority = rr->srv.priority; - copy->srv.weight = rr->srv.weight; - copy->srv.port = rr->srv.port; - copy->srv.name = strdup(rr->srv.name); - if (!copy->srv.name) - return NULL; - break; - - case DNS_TYPE_PTR: - case DNS_TYPE_NS: - case DNS_TYPE_CNAME: - case DNS_TYPE_DNAME: - copy->ptr.name = strdup(rr->ptr.name); - if (!copy->ptr.name) - return NULL; - break; - - case DNS_TYPE_HINFO: - copy->hinfo.cpu = strdup(rr->hinfo.cpu); - if (!copy->hinfo.cpu) - return NULL; - - copy->hinfo.os = strdup(rr->hinfo.os); - if(!copy->hinfo.os) - return NULL; - break; - - case DNS_TYPE_TXT: - case DNS_TYPE_SPF: - copy->txt.items = dns_txt_item_copy(rr->txt.items); - if (!copy->txt.items) - return NULL; - break; - - case DNS_TYPE_A: - copy->a = rr->a; - break; - - case DNS_TYPE_AAAA: - copy->aaaa = rr->aaaa; - break; - - case DNS_TYPE_SOA: - copy->soa.mname = strdup(rr->soa.mname); - if (!copy->soa.mname) - return NULL; - copy->soa.rname = strdup(rr->soa.rname); - if (!copy->soa.rname) - return NULL; - copy->soa.serial = rr->soa.serial; - copy->soa.refresh = rr->soa.refresh; - copy->soa.retry = rr->soa.retry; - copy->soa.expire = rr->soa.expire; - copy->soa.minimum = rr->soa.minimum; - break; - - case DNS_TYPE_MX: - copy->mx.priority = rr->mx.priority; - copy->mx.exchange = strdup(rr->mx.exchange); - if (!copy->mx.exchange) - return NULL; - break; - - case DNS_TYPE_LOC: - copy->loc = rr->loc; - break; - - case DNS_TYPE_SSHFP: - copy->sshfp.algorithm = rr->sshfp.algorithm; - copy->sshfp.fptype = rr->sshfp.fptype; - copy->sshfp.fingerprint = memdup(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); - if (!copy->sshfp.fingerprint) - return NULL; - copy->sshfp.fingerprint_size = rr->sshfp.fingerprint_size; - break; - - case DNS_TYPE_DNSKEY: - copy->dnskey.flags = rr->dnskey.flags; - copy->dnskey.protocol = rr->dnskey.protocol; - copy->dnskey.algorithm = rr->dnskey.algorithm; - copy->dnskey.key = memdup(rr->dnskey.key, rr->dnskey.key_size); - if (!copy->dnskey.key) - return NULL; - copy->dnskey.key_size = rr->dnskey.key_size; - break; - - case DNS_TYPE_RRSIG: - copy->rrsig.type_covered = rr->rrsig.type_covered; - copy->rrsig.algorithm = rr->rrsig.algorithm; - copy->rrsig.labels = rr->rrsig.labels; - copy->rrsig.original_ttl = rr->rrsig.original_ttl; - copy->rrsig.expiration = rr->rrsig.expiration; - copy->rrsig.inception = rr->rrsig.inception; - copy->rrsig.key_tag = rr->rrsig.key_tag; - copy->rrsig.signer = strdup(rr->rrsig.signer); - if (!copy->rrsig.signer) - return NULL; - copy->rrsig.signature = memdup(rr->rrsig.signature, rr->rrsig.signature_size); - if (!copy->rrsig.signature) - return NULL; - copy->rrsig.signature_size = rr->rrsig.signature_size; - break; - - case DNS_TYPE_NSEC: - copy->nsec.next_domain_name = strdup(rr->nsec.next_domain_name); - if (!copy->nsec.next_domain_name) - return NULL; - copy->nsec.types = bitmap_copy(rr->nsec.types); - if (!copy->nsec.types) - return NULL; - break; - - case DNS_TYPE_DS: - copy->ds.key_tag = rr->ds.key_tag; - copy->ds.algorithm = rr->ds.algorithm; - copy->ds.digest_type = rr->ds.digest_type; - copy->ds.digest = memdup(rr->ds.digest, rr->ds.digest_size); - if (!copy->ds.digest) - return NULL; - copy->ds.digest_size = rr->ds.digest_size; - break; - - case DNS_TYPE_NSEC3: - copy->nsec3.algorithm = rr->nsec3.algorithm; - copy->nsec3.flags = rr->nsec3.flags; - copy->nsec3.iterations = rr->nsec3.iterations; - copy->nsec3.salt = memdup(rr->nsec3.salt, rr->nsec3.salt_size); - if (!copy->nsec3.salt) - return NULL; - copy->nsec3.salt_size = rr->nsec3.salt_size; - copy->nsec3.next_hashed_name = memdup(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size); - if (!copy->nsec3.next_hashed_name_size) - return NULL; - copy->nsec3.next_hashed_name_size = rr->nsec3.next_hashed_name_size; - copy->nsec3.types = bitmap_copy(rr->nsec3.types); - if (!copy->nsec3.types) - return NULL; - break; - - case DNS_TYPE_TLSA: - copy->tlsa.cert_usage = rr->tlsa.cert_usage; - copy->tlsa.selector = rr->tlsa.selector; - copy->tlsa.matching_type = rr->tlsa.matching_type; - copy->tlsa.data = memdup(rr->tlsa.data, rr->tlsa.data_size); - if (!copy->tlsa.data) - return NULL; - copy->tlsa.data_size = rr->tlsa.data_size; - break; - - case DNS_TYPE_CAA: - copy->caa.flags = rr->caa.flags; - copy->caa.tag = strdup(rr->caa.tag); - if (!copy->caa.tag) - return NULL; - copy->caa.value = memdup(rr->caa.value, rr->caa.value_size); - if (!copy->caa.value) - return NULL; - copy->caa.value_size = rr->caa.value_size; - break; - - case DNS_TYPE_OPT: - default: - copy->generic.data = memdup(rr->generic.data, rr->generic.data_size); - if (!copy->generic.data) - return NULL; - copy->generic.data_size = rr->generic.data_size; - break; - } - - t = copy; - copy = NULL; - - return t; -} - -int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) { - DnsResourceRecord *old_rr, *new_rr; - uint32_t new_ttl; - - assert(rr); - old_rr = *rr; - - if (old_rr->key->type == DNS_TYPE_OPT) - return -EINVAL; - - new_ttl = MIN(old_rr->ttl, max_ttl); - if (new_ttl == old_rr->ttl) - return 0; - - if (old_rr->n_ref == 1) { - /* Patch in place */ - old_rr->ttl = new_ttl; - return 1; - } - - new_rr = dns_resource_record_copy(old_rr); - if (!new_rr) - return -ENOMEM; - - new_rr->ttl = new_ttl; - - dns_resource_record_unref(*rr); - *rr = new_rr; - - return 1; -} - -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); -} - -DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) { - DnsTxtItem *i, *copy = NULL, *end = NULL; - - LIST_FOREACH(items, i, first) { - DnsTxtItem *j; - - j = memdup(i, offsetof(DnsTxtItem, data) + i->length + 1); - if (!j) { - dns_txt_item_free_all(copy); - return NULL; - } - - LIST_INSERT_AFTER(items, copy, end, j); - end = j; - } - - return copy; -} - -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 deleted file mode 100644 index 42d39a1251..0000000000 --- a/src/resolve/resolved-dns-rr.h +++ /dev/null @@ -1,353 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include - -#include "bitmap.h" -#include "dns-type.h" -#include "hashmap.h" -#include "in-addr-util.h" -#include "list.h" -#include "string-util.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 -}; - -/* DNSSEC digest identifiers, see - * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */ -enum { - 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 -1) for const keys, see below */ - uint16_t class, type; - char *_name; /* don't access directly, 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; - 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 data_size; - } generic, opt; - - struct { - uint16_t priority; - uint16_t weight; - uint16_t port; - char *name; - } srv; - - struct { - char *name; - } ptr, ns, cname, dname; - - struct { - char *cpu; - char *os; - } hinfo; - - struct { - DnsTxtItem *items; - } txt, spf; - - struct { - struct in_addr in_addr; - } a; - - struct { - struct in6_addr in6_addr; - } aaaa; - - struct { - char *mname; - char *rname; - uint32_t serial; - uint32_t refresh; - uint32_t retry; - uint32_t expire; - uint32_t minimum; - } soa; - - struct { - uint16_t priority; - char *exchange; - } mx; - - /* https://tools.ietf.org/html/rfc1876 */ - struct { - uint8_t version; - uint8_t size; - uint8_t horiz_pre; - uint8_t vert_pre; - uint32_t latitude; - uint32_t longitude; - uint32_t altitude; - } loc; - - /* https://tools.ietf.org/html/rfc4255#section-3.1 */ - struct { - uint8_t algorithm; - uint8_t fptype; - void *fingerprint; - size_t fingerprint_size; - } sshfp; - - /* http://tools.ietf.org/html/rfc4034#section-2.1 */ - struct { - uint16_t flags; - uint8_t protocol; - uint8_t algorithm; - void* key; - size_t key_size; - } dnskey; - - /* http://tools.ietf.org/html/rfc4034#section-3.1 */ - struct { - uint16_t type_covered; - uint8_t algorithm; - uint8_t labels; - uint32_t original_ttl; - uint32_t expiration; - uint32_t inception; - uint16_t key_tag; - char *signer; - void *signature; - 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; - uint16_t iterations; - void *salt; - size_t salt_size; - void *next_hashed_name; - size_t next_hashed_name_size; - Bitmap *types; - } nsec3; - - /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23 */ - struct { - uint8_t cert_usage; - uint8_t selector; - uint8_t matching_type; - void *data; - size_t data_size; - } tlsa; - - /* https://tools.ietf.org/html/rfc6844 */ - struct { - uint8_t flags; - char *tag; - void *value; - size_t value_size; - } caa; - }; -}; - -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; -} - -static inline uint8_t DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(DnsResourceRecord *rr) { - assert(rr); - assert(rr->key->type == DNS_TYPE_OPT); - - return ((rr->ttl >> 16) & 0xFF) == 0; -} - -DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); -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); -const char* dns_resource_key_name(const 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, 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); - -/* _DNS_{CLASS,TYPE}_STRING_MAX include one byte for NUL, which we use for space instead below. - * DNS_HOSTNAME_MAX does not include the NUL byte, so we need to add 1. */ -#define DNS_RESOURCE_KEY_STRING_MAX (_DNS_CLASS_STRING_MAX + _DNS_TYPE_STRING_MAX + DNS_HOSTNAME_MAX + 1) - -char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size); -ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out); - -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); -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); -const char* dns_resource_record_to_string(DnsResourceRecord *rr); -DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr); -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); - -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); - -int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl); - -DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); -bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); -DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i); - -void dns_resource_record_hash_func(const void *i, struct siphash *state); - -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 deleted file mode 100644 index ed0c6aa105..0000000000 --- a/src/resolve/resolved-dns-scope.c +++ /dev/null @@ -1,1037 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "af-list.h" -#include "alloc-util.h" -#include "dns-domain.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 - -/* After how much time to repeat LLMNR requests, see RFC 4795 Section 7 */ -#define MULTICAST_RESEND_TIMEOUT_MIN_USEC (100 * USEC_PER_MSEC) -#define MULTICAST_RESEND_TIMEOUT_MAX_USEC (1 * USEC_PER_SEC) - -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) { - DnsScope *s; - - assert(m); - assert(ret); - - s = new0(DnsScope, 1); - if (!s) - return -ENOMEM; - - s->manager = m; - s->link = l; - s->protocol = protocol; - s->family = family; - s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC; - - 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); - } else - s->dnssec_mode = DNSSEC_NO; - - 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)); - - /* Enforce ratelimiting for the multicast protocols */ - RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST); - - *ret = s; - return 0; -} - -static void dns_scope_abort_transactions(DnsScope *s) { - assert(s); - - while (s->transactions) { - DnsTransaction *t = s->transactions; - - /* Abort the transaction, but make sure it is not - * freed while we still look at it */ - - t->block_gc++; - 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; - - 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); - - ordered_hashmap_free(s->conflict_queue); - sd_event_source_unref(s->conflict_event_source); - - dns_cache_flush(&s->cache); - dns_zone_flush(&s->zone); - - LIST_REMOVE(scopes, s->manager->dns_scopes, s); - free(s); - - return NULL; -} - -DnsServer *dns_scope_get_dns_server(DnsScope *s) { - assert(s); - - if (s->protocol != DNS_PROTOCOL_DNS) - return NULL; - - if (s->link) - return link_get_dns_server(s->link); - else - return manager_get_dns_server(s->manager); -} - -void dns_scope_next_dns_server(DnsScope *s) { - assert(s); - - if (s->protocol != DNS_PROTOCOL_DNS) - return; - - if (s->link) - link_next_dns_server(s->link); - else - manager_next_dns_server(s->manager); -} - -void dns_scope_packet_received(DnsScope *s, usec_t rtt) { - assert(s); - - 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) { - assert(s); - - if (s->resend_timeout <= usec) - s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC); -} - -static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) { - union in_addr_union addr; - int ifindex = 0, r; - int family; - uint32_t mtu; - - assert(s); - assert(p); - assert(p->protocol == s->protocol); - - if (s->link) { - mtu = s->link->mtu; - ifindex = s->link->ifindex; - } else - mtu = manager_find_mtu(s->manager); - - switch (s->protocol) { - - case DNS_PROTOCOL_DNS: - assert(fd >= 0); - - if (DNS_PACKET_QDCOUNT(p) > 1) - return -EOPNOTSUPP; - - if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) - return -EMSGSIZE; - - if (p->size + UDP_PACKET_HEADER_SIZE > mtu) - return -EMSGSIZE; - - r = manager_write(s->manager, fd, p); - if (r < 0) - return r; - - break; - - case DNS_PROTOCOL_LLMNR: - assert(fd < 0); - - if (DNS_PACKET_QDCOUNT(p) > 1) - return -EOPNOTSUPP; - - if (!ratelimit_test(&s->ratelimit)) - return -EBUSY; - - family = s->family; - - if (family == AF_INET) { - addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; - fd = manager_llmnr_ipv4_udp_fd(s->manager); - } else if (family == AF_INET6) { - addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS; - fd = manager_llmnr_ipv6_udp_fd(s->manager); - } else - return -EAFNOSUPPORT; - if (fd < 0) - return fd; - - r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, 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, NULL, p); - if (r < 0) - return r; - - break; - - default: - return -EAFNOSUPPORT; - } - - return 1; -} - -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; - static const int one = 1; - int ret, r, ifindex; - - assert(s); - - if (server) { - assert(family == AF_UNSPEC); - assert(!address); - - ifindex = dns_server_ifindex(server); - - sa.sa.sa_family = server->family; - if (server->family == AF_INET) { - sa.in.sin_port = htobe16(port); - sa.in.sin_addr = server->address.in; - salen = sizeof(sa.in); - } else if (server->family == AF_INET6) { - sa.in6.sin6_port = htobe16(port); - sa.in6.sin6_addr = server->address.in6; - sa.in6.sin6_scope_id = ifindex; - salen = sizeof(sa.in6); - } else - return -EAFNOSUPPORT; - } else { - assert(family != AF_UNSPEC); - assert(address); - - sa.sa.sa_family = family; - ifindex = s->link ? s->link->ifindex : 0; - - if (family == AF_INET) { - sa.in.sin_port = htobe16(port); - sa.in.sin_addr = address->in; - salen = sizeof(sa.in); - } else if (family == AF_INET6) { - sa.in6.sin6_port = htobe16(port); - sa.in6.sin6_addr = address->in6; - sa.in6.sin6_scope_id = ifindex; - salen = sizeof(sa.in6); - } else - return -EAFNOSUPPORT; - } - - fd = socket(sa.sa.sa_family, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return -errno; - - if (type == SOCK_STREAM) { - r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); - if (r < 0) - return -errno; - } - - if (s->link) { - be32_t ifindex_be = htobe32(ifindex); - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); - if (r < 0) - return -errno; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)); - if (r < 0) - return -errno; - } - } - - if (s->protocol == DNS_PROTOCOL_LLMNR) { - /* RFC 4795, section 2.5 requires the TTL to be set to 1 */ - - if (sa.sa.sa_family == AF_INET) { - r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) - return -errno; - } else if (sa.sa.sa_family == AF_INET6) { - r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); - if (r < 0) - return -errno; - } - } - - r = connect(fd, &sa.sa, salen); - if (r < 0 && errno != EINPROGRESS) - return -errno; - - ret = fd; - fd = -1; - - return ret; -} - -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_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) { - DnsSearchDomain *d; - - assert(s); - assert(domain); - - /* 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 (ifindex != 0 && (!s->link || s->link->ifindex != ifindex)) - return DNS_SCOPE_NO; - - 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, - * LLMNR or mDNS. Instead, always rely on synthesized RRs for - * these. */ - if (is_localhost(domain) || - dns_name_endswith(domain, "127.in-addr.arpa") > 0 || - 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; - - /* 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, "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; - - case DNS_PROTOCOL_MDNS: - 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_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ - dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */ - manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */ - return DNS_SCOPE_MAYBE; - - return DNS_SCOPE_NO; - - 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_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; - - return DNS_SCOPE_NO; - - default: - assert_not_reached("Unknown scope protocol"); - } -} - -bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) { - int key_family; - - assert(s); - assert(key); - - /* 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 (key->class != DNS_CLASS_IN) - return false; - - 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 */ - - key_family = dns_type_to_af(key->type); - if (key_family < 0) - return true; - - return key_family == s->family; -} - -static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) { - int fd; - - assert(s); - assert(s->link); - - if (s->family == AF_INET) { - struct ip_mreqn mreqn = { - .imr_multiaddr = in, - .imr_ifindex = s->link->ifindex, - }; - - fd = manager_llmnr_ipv4_udp_fd(s->manager); - if (fd < 0) - return fd; - - /* Always first try to drop membership before we add - * one. This is necessary on some devices, such as - * veth. */ - if (b) - (void) setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); - - if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) - return -errno; - - } else if (s->family == AF_INET6) { - struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = in6, - .ipv6mr_interface = s->link->ifindex, - }; - - fd = manager_llmnr_ipv6_udp_fd(s->manager); - if (fd < 0) - return fd; - - if (b) - (void) setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return -errno; - } else - return -EAFNOSUPPORT; - - return 0; -} - -int dns_scope_llmnr_membership(DnsScope *s, bool b) { - assert(s); - - 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) { - assert(s); - - 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, - int rcode, - DnsQuestion *q, - DnsAnswer *answer, - DnsAnswer *soa, - bool tentative, - DnsPacket **ret) { - - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - - assert(s); - assert(ret); - - if (dns_question_isempty(q) && - dns_answer_isempty(answer) && - dns_answer_isempty(soa)) - return -EINVAL; - - r = dns_packet_new(&p, s->protocol, 0); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->id = id; - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 1 /* qr */, - 0 /* opcode */, - 0 /* c */, - 0 /* tc */, - tentative, - 0 /* (ra) */, - 0 /* (ad) */, - 0 /* (cd) */, - rcode)); - - r = dns_packet_append_question(p, q); - if (r < 0) - return r; - DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); - - r = dns_packet_append_answer(p, answer); - if (r < 0) - return r; - DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer)); - - r = dns_packet_append_answer(p, soa); - if (r < 0) - return r; - DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa)); - - *ret = p; - p = NULL; - - return 0; -} - -static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { - DnsResourceRecord *rr; - DnsResourceKey *key; - - assert(s); - assert(p); - - DNS_QUESTION_FOREACH(key, p->question) - dns_zone_verify_conflicts(&s->zone, key); - - DNS_ANSWER_FOREACH(rr, p->answer) - dns_zone_verify_conflicts(&s->zone, rr->key); -} - -void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; - DnsResourceKey *key = NULL; - bool tentative = false; - int r; - - assert(s); - assert(p); - - if (p->protocol != DNS_PROTOCOL_LLMNR) - return; - - if (p->ipproto == IPPROTO_UDP) { - /* Don't accept UDP queries directed to anything but - * the LLMNR multicast addresses. See RFC 4795, - * section 2.5. */ - - if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS)) - return; - - if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS)) - return; - } - - r = dns_packet_extract(p); - if (r < 0) { - log_debug_errno(r, "Failed to extract resource records from incoming packet: %m"); - return; - } - - if (DNS_PACKET_LLMNR_C(p)) { - /* Somebody notified us about a possible conflict */ - dns_scope_verify_conflicts(s, p); - return; - } - - assert(dns_question_size(p->question) == 1); - key = p->question->keys[0]; - - r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); - if (r < 0) { - log_debug_errno(r, "Failed to lookup key: %m"); - return; - } - if (r == 0) - return; - - if (answer) - dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0); - - r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply); - if (r < 0) { - log_debug_errno(r, "Failed to build reply packet: %m"); - return; - } - - if (stream) { - r = dns_stream_write_packet(stream, reply); - if (r < 0) { - log_debug_errno(r, "Failed to enqueue reply packet: %m"); - return; - } - - /* Let's take an extra reference on this stream, so that it stays around after returning. The reference - * will be dangling until the stream is disconnected, and the default completion handler of the stream - * will then unref the stream and destroy it */ - if (DNS_STREAM_QUEUED(stream)) - dns_stream_ref(stream); - } else { - int fd; - - if (!ratelimit_test(&s->ratelimit)) - return; - - if (p->family == AF_INET) - fd = manager_llmnr_ipv4_udp_fd(s->manager); - else if (p->family == AF_INET6) - fd = manager_llmnr_ipv6_udp_fd(s->manager); - else { - log_debug("Unknown protocol"); - return; - } - if (fd < 0) { - log_debug_errno(fd, "Failed to get reply socket: %m"); - return; - } - - /* Note that we always immediately reply to all LLMNR - * requests, and do not wait any time, since we - * verified uniqueness for all records. Also see RFC - * 4795, Section 2.7 */ - - r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, NULL, reply); - if (r < 0) { - log_debug_errno(r, "Failed to send reply packet: %m"); - return; - } - } -} - -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok) { - DnsTransaction *t; - - assert(scope); - assert(key); - - /* Try to find an ongoing transaction that is a equal to the - * specified question */ - 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_RCODE_FAILURE) && - t->answer_source != DNS_TRANSACTION_NETWORK) - return NULL; - - return t; -} - -static int dns_scope_make_conflict_packet( - DnsScope *s, - DnsResourceRecord *rr, - DnsPacket **ret) { - - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - int r; - - assert(s); - assert(rr); - assert(ret); - - r = dns_packet_new(&p, s->protocol, 0); - if (r < 0) - return r; - - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 0 /* qr */, - 0 /* opcode */, - 1 /* conflict */, - 0 /* tc */, - 0 /* t */, - 0 /* (ra) */, - 0 /* (ad) */, - 0 /* (cd) */, - 0)); - - /* 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); - - r = dns_packet_append_key(p, rr->key, NULL); - if (r < 0) - return r; - - r = dns_packet_append_rr(p, rr, NULL, NULL); - if (r < 0) - return r; - - *ret = p; - p = NULL; - - return 0; -} - -static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) { - DnsScope *scope = userdata; - int r; - - assert(es); - assert(scope); - - scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source); - - for (;;) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - - rr = ordered_hashmap_steal_first(scope->conflict_queue); - if (!rr) - break; - - r = dns_scope_make_conflict_packet(scope, rr, &p); - if (r < 0) { - log_error_errno(r, "Failed to make conflict packet: %m"); - return 0; - } - - r = dns_scope_emit_udp(scope, -1, p); - if (r < 0) - log_debug_errno(r, "Failed to send conflict packet: %m"); - } - - return 0; -} - -int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { - usec_t jitter; - int r; - - assert(scope); - assert(rr); - - /* We don't send these queries immediately. Instead, we queue - * them, and send them after some jitter delay. */ - r = ordered_hashmap_ensure_allocated(&scope->conflict_queue, &dns_resource_key_hash_ops); - if (r < 0) { - log_oom(); - return r; - } - - /* We only place one RR per key in the conflict - * messages, not all of them. That should be enough to - * indicate where there might be a conflict */ - r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr); - if (r == -EEXIST || r == 0) - return 0; - if (r < 0) - return log_debug_errno(r, "Failed to queue conflicting RR: %m"); - - dns_resource_record_ref(rr); - - if (scope->conflict_event_source) - return 0; - - random_bytes(&jitter, sizeof(jitter)); - jitter %= LLMNR_JITTER_INTERVAL_USEC; - - r = sd_event_add_time(scope->manager->event, - &scope->conflict_event_source, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + jitter, - LLMNR_JITTER_INTERVAL_USEC, - on_conflict_dispatch, scope); - 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; -} - -void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { - unsigned i; - int r; - - assert(scope); - assert(p); - - if (p->protocol != DNS_PROTOCOL_LLMNR) - return; - - if (DNS_PACKET_RRCOUNT(p) <= 0) - return; - - if (DNS_PACKET_LLMNR_C(p) != 0) - return; - - if (DNS_PACKET_LLMNR_T(p) != 0) - return; - - if (manager_our_packet(scope->manager, p)) - return; - - r = dns_packet_extract(p); - if (r < 0) { - log_debug_errno(r, "Failed to extract packet: %m"); - return; - } - - log_debug("Checking for conflicts..."); - - for (i = 0; i < p->answer->n_rrs; i++) { - - /* Check for conflicts against the local zone. If we - * found one, we won't check any further */ - r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr); - if (r != 0) - continue; - - /* Check for conflicts against the local cache. If so, - * send out an advisory query, to inform everybody */ - r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender); - if (r <= 0) - continue; - - dns_scope_notify_conflict(scope, p->answer->items[i].rr); - } -} - -void dns_scope_dump(DnsScope *s, FILE *f) { - assert(s); - - if (!f) - f = stdout; - - fputs("[Scope protocol=", f); - fputs(dns_protocol_to_string(s->protocol), f); - - if (s->link) { - fputs(" interface=", f); - fputs(s->link->name, f); - } - - if (s->family != AF_UNSPEC) { - fputs(" family=", f); - fputs(af_to_name(s->family), f); - } - - fputs("]\n", f); - - if (!dns_zone_is_empty(&s->zone)) { - fputs("ZONE:\n", f); - dns_zone_dump(&s->zone, f); - } - - if (!dns_cache_is_empty(&s->cache)) { - fputs("CACHE:\n", 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); -} - -bool dns_scope_network_good(DnsScope *s) { - /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes - * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global - * DNS scope we check whether there are any links that are up and have an address. */ - - if (s->link) - return true; - - return manager_routable(s->manager, AF_UNSPEC); -} - -int dns_scope_ifindex(DnsScope *s) { - assert(s); - - if (s->link) - return s->link->ifindex; - - return 0; -} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h deleted file mode 100644 index 538bc61f81..0000000000 --- a/src/resolve/resolved-dns-scope.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "list.h" - -typedef struct DnsScope DnsScope; - -#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" - -typedef enum DnsScopeMatch { - DNS_SCOPE_NO, - DNS_SCOPE_MAYBE, - DNS_SCOPE_YES, - _DNS_SCOPE_MATCH_MAX, - _DNS_SCOPE_INVALID = -1 -} DnsScopeMatch; - -struct DnsScope { - Manager *manager; - - DnsProtocol protocol; - int family; - DnssecMode dnssec_mode; - - Link *link; - - DnsCache cache; - DnsZone zone; - - OrderedHashmap *conflict_queue; - sd_event_source *conflict_event_source; - - RateLimit ratelimit; - - usec_t resend_timeout; - usec_t max_rtt; - - 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); -}; - -int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol p, int family); -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_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); -bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key); - -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); - -DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok); - -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); - -bool dns_scope_network_good(DnsScope *s); - -int dns_scope_ifindex(DnsScope *s); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c deleted file mode 100644 index 732471027b..0000000000 --- a/src/resolve/resolved-dns-search-domain.c +++ /dev/null @@ -1,227 +0,0 @@ -/*** - 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 . -***/ - -#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 (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 deleted file mode 100644 index eaacef4edc..0000000000 --- a/src/resolve/resolved-dns-search-domain.h +++ /dev/null @@ -1,74 +0,0 @@ -#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 . -***/ - -#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 route_only: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 deleted file mode 100644 index 9b7b471600..0000000000 --- a/src/resolve/resolved-dns-server.c +++ /dev/null @@ -1,777 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "resolved-dns-server.h" -#include "resolved-dns-stub.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, - DnsServerType type, - Link *l, - int family, - const union in_addr_union *in_addr, - int ifindex) { - - 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->ifindex = ifindex; - s->resend_timeout = DNS_TIMEOUT_MIN_USEC; - - switch (type) { - - case DNS_SERVER_LINK: - s->link = l; - 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->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 - * the new one */ - if (type != DNS_SERVER_FALLBACK && - m->current_dns_server && - m->current_dns_server->type == DNS_SERVER_FALLBACK) - manager_set_dns_server(m, NULL); - - if (ret) - *ret = s; - - return 0; -} - -DnsServer* dns_server_ref(DnsServer *s) { - if (!s) - return NULL; - - assert(s->n_ref > 0); - s->n_ref++; - - return 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); - s->link->n_dns_servers--; - 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->current_dns_server == s) - manager_set_dns_server(s->manager, NULL); - - dns_server_unref(s); -} - -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"); - } -} - -static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { - assert(s); - - if (s->verified_feature_level > level) - return; - - 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; - } - - assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); -} - -static void dns_server_reset_counters(DnsServer *s) { - assert(s); - - s->n_failed_udp = 0; - s->n_failed_tcp = 0; - 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. */ -} - -void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) { - assert(s); - - 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 = 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_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_bad_opt(DnsServer *s, DnsServerFeatureLevel level) { - assert(s); - - 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; -} - -void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level) { - assert(s); - - /* Invoked whenever we got a FORMERR, SERVFAIL or NOTIMP rcode from a server and downgrading the feature level - * for the transaction made it go away. In this case we immediately downgrade to the feature level that made - * things work. */ - - if (s->verified_feature_level > level) - s->verified_feature_level = level; - - if (s->possible_feature_level > level) { - s->possible_feature_level = level; - dns_server_reset_counters(s); - } - - log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s)); -} - -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; -} - -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->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, 0, NULL); -} - -int dns_server_ifindex(const DnsServer *s) { - assert(s); - - /* The link ifindex always takes precedence */ - if (s->link) - return s->link->ifindex; - - if (s->ifindex > 0) - return s->ifindex; - - return 0; -} - -const char *dns_server_string(DnsServer *server) { - assert(server); - - if (!server->server_string) - (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &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; -} - -void dns_server_warn_downgrade(DnsServer *server) { - assert(server); - - if (server->warned_downgrade) - return; - - log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE), - LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)), - "DNS_SERVER=%s", dns_server_string(server), - "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level), - NULL); - - server->warned_downgrade = true; -} - -static void dns_server_hash_func(const void *p, struct siphash *state) { - const DnsServer *s = p; - - assert(s); - - siphash24_compress(&s->family, sizeof(s->family), state); - siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state); - siphash24_compress(&s->ifindex, sizeof(s->ifindex), state); -} - -static int dns_server_compare_func(const void *a, const void *b) { - const DnsServer *x = a, *y = b; - int r; - - if (x->family < y->family) - return -1; - if (x->family > y->family) - return 1; - - r = memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); - if (r != 0) - return r; - - if (x->ifindex < y->ifindex) - return -1; - if (x->ifindex > y->ifindex) - return 1; - - return 0; -} - -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, int ifindex) { - DnsServer *s; - - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex) - 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 %s DNS server %s.", - dns_server_type_to_string(s->type), - 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 chosen 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); -} - -bool dns_server_address_valid(int family, const union in_addr_union *sa) { - - /* Refuses the 0 IP addresses as well as 127.0.0.53 (which is our own DNS stub) */ - - if (in_addr_is_null(family, sa)) - return false; - - if (family == AF_INET && sa->in.s_addr == htobe32(INADDR_DNS_STUB)) - return false; - - return true; -} - -static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { - [DNS_SERVER_SYSTEM] = "system", - [DNS_SERVER_FALLBACK] = "fallback", - [DNS_SERVER_LINK] = "link", -}; -DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType); - -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 deleted file mode 100644 index c1732faffd..0000000000 --- a/src/resolve/resolved-dns-server.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "in-addr-util.h" - -typedef struct DnsServer DnsServer; - -typedef enum DnsServerType { - DNS_SERVER_SYSTEM, - DNS_SERVER_FALLBACK, - DNS_SERVER_LINK, -} DnsServerType; -#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1) - -const char* dns_server_type_to_string(DnsServerType i) _const_; -DnsServerType dns_server_type_from_string(const char *s) _pure_; - -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; - - unsigned n_ref; - - DnsServerType type; - Link *link; - - int family; - union in_addr_union address; - int ifindex; /* for IPv6 link-local DNS servers */ - - 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_truncated:1; - bool packet_bad_opt:1; - bool packet_rrsig_missing:1; - - usec_t verified_usec; - usec_t features_grace_period_usec; - - /* Whether we already warned about downgrading to non-DNSSEC mode for this server */ - bool warned_downgrade:1; - - /* 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 **ret, - DnsServerType type, - Link *link, - int family, - const union in_addr_union *address, - int ifindex); - -DnsServer* dns_server_ref(DnsServer *s); -DnsServer* dns_server_unref(DnsServer *s); - -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_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); -void dns_server_packet_rcode_downgrade(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); -int dns_server_ifindex(const DnsServer *s); - -bool dns_server_dnssec_supported(DnsServer *server); - -void dns_server_warn_downgrade(DnsServer *server); - -DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex); - -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); - -bool dns_server_address_valid(int family, const union in_addr_union *sa); - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); - -extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c deleted file mode 100644 index dd0e0b90e3..0000000000 --- a/src/resolve/resolved-dns-stream.c +++ /dev/null @@ -1,420 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "missing.h" -#include "resolved-dns-stream.h" - -#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC) -#define DNS_STREAMS_MAX 128 - -static void dns_stream_stop(DnsStream *s) { - assert(s); - - s->io_event_source = sd_event_source_unref(s->io_event_source); - s->timeout_event_source = sd_event_source_unref(s->timeout_event_source); - s->fd = safe_close(s->fd); -} - -static int dns_stream_update_io(DnsStream *s) { - int f = 0; - - assert(s); - - if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size) - f |= EPOLLOUT; - if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) - f |= EPOLLIN; - - return sd_event_source_set_io_events(s->io_event_source, f); -} - -static int dns_stream_complete(DnsStream *s, int error) { - assert(s); - - dns_stream_stop(s); - - if (s->complete) - s->complete(s, error); - else /* the default action if no completion function is set is to close the stream */ - dns_stream_unref(s); - - return 0; -} - -static int dns_stream_identify(DnsStream *s) { - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; - } control; - struct msghdr mh = {}; - struct cmsghdr *cmsg; - socklen_t sl; - int r; - - assert(s); - - if (s->identified) - return 0; - - /* Query the local side */ - s->local_salen = sizeof(s->local); - r = getsockname(s->fd, &s->local.sa, &s->local_salen); - if (r < 0) - return -errno; - if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0) - s->ifindex = s->local.in6.sin6_scope_id; - - /* Query the remote side */ - s->peer_salen = sizeof(s->peer); - r = getpeername(s->fd, &s->peer.sa, &s->peer_salen); - if (r < 0) - return -errno; - if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0) - s->ifindex = s->peer.in6.sin6_scope_id; - - /* Check consistency */ - assert(s->peer.sa.sa_family == s->local.sa.sa_family); - assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); - - /* Query connection meta information */ - sl = sizeof(control); - if (s->peer.sa.sa_family == AF_INET) { - r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl); - if (r < 0) - return -errno; - } else if (s->peer.sa.sa_family == AF_INET6) { - - r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl); - if (r < 0) - return -errno; - } else - return -EAFNOSUPPORT; - - mh.msg_control = &control; - mh.msg_controllen = sl; - - CMSG_FOREACH(cmsg, &mh) { - - if (cmsg->cmsg_level == IPPROTO_IPV6) { - assert(s->peer.sa.sa_family == AF_INET6); - - switch (cmsg->cmsg_type) { - - case IPV6_PKTINFO: { - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (s->ifindex <= 0) - s->ifindex = i->ipi6_ifindex; - break; - } - - case IPV6_HOPLIMIT: - s->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - - } else if (cmsg->cmsg_level == IPPROTO_IP) { - assert(s->peer.sa.sa_family == AF_INET); - - switch (cmsg->cmsg_type) { - - case IP_PKTINFO: { - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (s->ifindex <= 0) - s->ifindex = i->ipi_ifindex; - break; - } - - case IP_TTL: - s->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - } - } - - /* The Linux kernel sets the interface index to the loopback - * device if the connection came from the local host since it - * avoids the routing table in such a case. Let's unset the - * interface index in such a case. */ - if (s->ifindex == LOOPBACK_IFINDEX) - s->ifindex = 0; - - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (s->ifindex <= 0) - s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr); - - if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) { - uint32_t ifindex = htobe32(s->ifindex); - - /* Make sure all packets for this connection are sent on the same interface */ - if (s->local.sa.sa_family == AF_INET) { - r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m"); - } else if (s->local.sa.sa_family == AF_INET6) { - r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m"); - } - } - - s->identified = true; - - return 0; -} - -static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) { - DnsStream *s = userdata; - - assert(s); - - return dns_stream_complete(s, ETIMEDOUT); -} - -static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - DnsStream *s = userdata; - int r; - - assert(s); - - r = dns_stream_identify(s); - if (r < 0) - return dns_stream_complete(s, -r); - - if ((revents & EPOLLOUT) && - s->write_packet && - s->n_written < sizeof(s->write_size) + s->write_packet->size) { - - struct iovec iov[2]; - ssize_t ss; - - iov[0].iov_base = &s->write_size; - iov[0].iov_len = sizeof(s->write_size); - iov[1].iov_base = DNS_PACKET_DATA(s->write_packet); - iov[1].iov_len = s->write_packet->size; - - IOVEC_INCREMENT(iov, 2, s->n_written); - - ss = writev(fd, iov, 2); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else - s->n_written += ss; - - /* Are we done? If so, disable the event source for EPOLLOUT */ - if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) { - r = dns_stream_update_io(s); - if (r < 0) - return dns_stream_complete(s, -r); - } - } - - if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) && - (!s->read_packet || - s->n_read < sizeof(s->read_size) + s->read_packet->size)) { - - if (s->n_read < sizeof(s->read_size)) { - ssize_t ss; - - ss = read(fd, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else if (ss == 0) - return dns_stream_complete(s, ECONNRESET); - else - s->n_read += ss; - } - - if (s->n_read >= sizeof(s->read_size)) { - - if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE) - return dns_stream_complete(s, EBADMSG); - - if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) { - ssize_t ss; - - if (!s->read_packet) { - r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size)); - if (r < 0) - return dns_stream_complete(s, -r); - - s->read_packet->size = be16toh(s->read_size); - s->read_packet->ipproto = IPPROTO_TCP; - s->read_packet->family = s->peer.sa.sa_family; - s->read_packet->ttl = s->ttl; - s->read_packet->ifindex = s->ifindex; - - if (s->read_packet->family == AF_INET) { - s->read_packet->sender.in = s->peer.in.sin_addr; - s->read_packet->sender_port = be16toh(s->peer.in.sin_port); - s->read_packet->destination.in = s->local.in.sin_addr; - s->read_packet->destination_port = be16toh(s->local.in.sin_port); - } else { - assert(s->read_packet->family == AF_INET6); - s->read_packet->sender.in6 = s->peer.in6.sin6_addr; - s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port); - s->read_packet->destination.in6 = s->local.in6.sin6_addr; - s->read_packet->destination_port = be16toh(s->local.in6.sin6_port); - - if (s->read_packet->ifindex == 0) - s->read_packet->ifindex = s->peer.in6.sin6_scope_id; - if (s->read_packet->ifindex == 0) - s->read_packet->ifindex = s->local.in6.sin6_scope_id; - } - } - - ss = read(fd, - (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size), - sizeof(s->read_size) + be16toh(s->read_size) - s->n_read); - if (ss < 0) { - if (errno != EINTR && errno != EAGAIN) - return dns_stream_complete(s, errno); - } else if (ss == 0) - return dns_stream_complete(s, ECONNRESET); - else - s->n_read += ss; - } - - /* Are we done? If so, disable the event source for EPOLLIN */ - if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) { - r = dns_stream_update_io(s); - if (r < 0) - return dns_stream_complete(s, -r); - - /* If there's a packet handler - * installed, call that. Note that - * this is optional... */ - if (s->on_packet) - return s->on_packet(s); - } - } - } - - if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && - (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) - return dns_stream_complete(s, 0); - - return 0; -} - -DnsStream *dns_stream_unref(DnsStream *s) { - if (!s) - return NULL; - - assert(s->n_ref > 0); - s->n_ref--; - - if (s->n_ref > 0) - return NULL; - - dns_stream_stop(s); - - if (s->manager) { - LIST_REMOVE(streams, s->manager->dns_streams, s); - s->manager->n_dns_streams--; - } - - dns_packet_unref(s->write_packet); - dns_packet_unref(s->read_packet); - - free(s); - - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsStream*, dns_stream_unref); - -DnsStream *dns_stream_ref(DnsStream *s) { - if (!s) - return NULL; - - assert(s->n_ref > 0); - s->n_ref++; - - return s; -} - -int dns_stream_new(Manager *m, DnsStream **ret, DnsProtocol protocol, int fd) { - _cleanup_(dns_stream_unrefp) DnsStream *s = NULL; - int r; - - assert(m); - assert(fd >= 0); - - if (m->n_dns_streams > DNS_STREAMS_MAX) - return -EBUSY; - - s = new0(DnsStream, 1); - if (!s) - return -ENOMEM; - - s->n_ref = 1; - s->fd = -1; - s->protocol = protocol; - - 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, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0, - on_stream_timeout, s); - 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; - m->n_dns_streams++; - - *ret = s; - s = NULL; - - return 0; -} - -int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { - assert(s); - - if (s->write_packet) - return -EBUSY; - - s->write_packet = dns_packet_ref(p); - s->write_size = htobe16(p->size); - s->n_written = 0; - - return dns_stream_update_io(s); -} diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h deleted file mode 100644 index e6569678fa..0000000000 --- a/src/resolve/resolved-dns-stream.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "socket-util.h" - -typedef struct DnsStream DnsStream; - -#include "resolved-dns-packet.h" -#include "resolved-dns-transaction.h" - -/* Streams are used by three subsystems: - * - * 1. The normal transaction logic when doing a DNS or LLMNR lookup via TCP - * 2. The LLMNR logic when accepting a TCP-based lookup - * 3. The DNS stub logic when accepting a TCP-based lookup - */ - -struct DnsStream { - Manager *manager; - int n_ref; - - DnsProtocol protocol; - - int fd; - union sockaddr_union peer; - socklen_t peer_salen; - union sockaddr_union local; - socklen_t local_salen; - int ifindex; - uint32_t ttl; - bool identified; - - sd_event_source *io_event_source; - sd_event_source *timeout_event_source; - - be16_t write_size, read_size; - DnsPacket *write_packet, *read_packet; - size_t n_written, n_read; - - int (*on_packet)(DnsStream *s); - int (*complete)(DnsStream *s, int error); - - DnsTransaction *transaction; /* when used by the transaction logic */ - DnsQuery *query; /* when used by the DNS stub logic */ - - LIST_FIELDS(DnsStream, streams); -}; - -int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd); -DnsStream *dns_stream_unref(DnsStream *s); -DnsStream *dns_stream_ref(DnsStream *s); - -int dns_stream_write_packet(DnsStream *s, DnsPacket *p); - -static inline bool DNS_STREAM_QUEUED(DnsStream *s) { - assert(s); - - if (s->fd < 0) /* already stopped? */ - return false; - - return !!s->write_packet; -} diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c deleted file mode 100644 index d263cedcd9..0000000000 --- a/src/resolve/resolved-dns-stub.c +++ /dev/null @@ -1,572 +0,0 @@ -/*** - 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 . -***/ - -#include "fd-util.h" -#include "resolved-dns-stub.h" -#include "socket-util.h" - -/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet, - * IP and UDP header sizes */ -#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U) - -static int dns_stub_make_reply_packet( - uint16_t id, - int rcode, - DnsQuestion *q, - DnsAnswer *answer, - bool add_opt, /* add an OPT RR to this packet */ - bool edns0_do, /* set the EDNS0 DNSSEC OK bit */ - bool ad, /* set the DNSSEC authenticated data bit */ - DnsPacket **ret) { - - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsResourceRecord *rr; - unsigned c = 0; - int r; - - /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence - * roundtrips aren't expensive. */ - - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - return r; - - /* If the client didn't do EDNS, clamp the rcode to 4 bit */ - if (!add_opt && rcode > 0xF) - rcode = DNS_RCODE_SERVFAIL; - - DNS_PACKET_HEADER(p)->id = id; - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 1 /* qr */, - 0 /* opcode */, - 0 /* aa */, - 0 /* tc */, - 1 /* rd */, - 1 /* ra */, - ad /* ad */, - 0 /* cd */, - rcode)); - - r = dns_packet_append_question(p, q); - if (r < 0) - return r; - DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); - - DNS_ANSWER_FOREACH(rr, answer) { - r = dns_question_matches_rr(q, rr, NULL); - if (r < 0) - return r; - if (r > 0) - goto add; - - r = dns_question_matches_cname_or_dname(q, rr, NULL); - if (r < 0) - return r; - if (r > 0) - goto add; - - continue; - add: - r = dns_packet_append_rr(p, rr, NULL, NULL); - if (r < 0) - return r; - - c++; - } - DNS_PACKET_HEADER(p)->ancount = htobe16(c); - - if (add_opt) { - r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); - if (r < 0) - return r; - } - - *ret = p; - p = NULL; - - return 0; -} - -static void dns_stub_detach_stream(DnsStream *s) { - assert(s); - - s->complete = NULL; - s->on_packet = NULL; - s->query = NULL; -} - -static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) { - int r; - - assert(m); - assert(p); - assert(reply); - - if (s) - r = dns_stream_write_packet(s, reply); - else { - int fd; - - /* Truncate the message to the right size */ - if (reply->size > DNS_PACKET_PAYLOAD_SIZE_MAX(p)) { - dns_packet_truncate(reply, DNS_PACKET_UNICAST_SIZE_MAX); - DNS_PACKET_HEADER(reply)->flags = htobe16(be16toh(DNS_PACKET_HEADER(reply)->flags) | DNS_PACKET_FLAG_TC); - } - - fd = manager_dns_stub_udp_fd(m); - if (fd < 0) - return log_debug_errno(fd, "Failed to get reply socket: %m"); - - /* Note that it is essential here that we explicitly choose the source IP address for this packet. This - * is because otherwise the kernel will choose it automatically based on the routing table and will - * thus pick 127.0.0.1 rather than 127.0.0.53. */ - - r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply); - } - if (r < 0) - return log_debug_errno(r, "Failed to send reply packet: %m"); - - return 0; -} - -static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) { - _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; - int r; - - assert(m); - assert(p); - - r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply); - if (r < 0) - return log_debug_errno(r, "Failed to build failure packet: %m"); - - return dns_stub_send(m, s, p, reply); -} - -static void dns_stub_query_complete(DnsQuery *q) { - int r; - - assert(q); - assert(q->request_dns_packet); - - switch (q->state) { - - case DNS_TRANSACTION_SUCCESS: { - _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; - - r = dns_stub_make_reply_packet( - DNS_PACKET_ID(q->request_dns_packet), - q->answer_rcode, - q->question_idna, - q->answer, - !!q->request_dns_packet->opt, - DNS_PACKET_DO(q->request_dns_packet), - DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated, - &reply); - if (r < 0) { - log_debug_errno(r, "Failed to build reply packet: %m"); - break; - } - - (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply); - break; - } - - case DNS_TRANSACTION_RCODE_FAILURE: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode); - break; - - case DNS_TRANSACTION_NOT_FOUND: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN); - break; - - case DNS_TRANSACTION_TIMEOUT: - case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: - /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */ - break; - - case DNS_TRANSACTION_NO_SERVERS: - case DNS_TRANSACTION_INVALID_REPLY: - case DNS_TRANSACTION_ERRNO: - case DNS_TRANSACTION_ABORTED: - case DNS_TRANSACTION_DNSSEC_FAILED: - case DNS_TRANSACTION_NO_TRUST_ANCHOR: - case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: - case DNS_TRANSACTION_NETWORK_DOWN: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL); - break; - - case DNS_TRANSACTION_NULL: - case DNS_TRANSACTION_PENDING: - case DNS_TRANSACTION_VALIDATING: - default: - assert_not_reached("Impossible state"); - } - - /* If there's a packet to write set, let's leave the stream around */ - if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) { - - /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The - * default completion action of the stream will drop the reference. */ - - dns_stub_detach_stream(q->request_dns_stream); - q->request_dns_stream = NULL; - } - - dns_query_free(q); -} - -static int dns_stub_stream_complete(DnsStream *s, int error) { - assert(s); - - log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m"); - - assert(s->query); - dns_query_free(s->query); - - return 0; -} - -static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { - DnsQuery *q = NULL; - int r; - - assert(m); - assert(p); - assert(p->protocol == DNS_PROTOCOL_DNS); - - /* Takes ownership of the *s stream object */ - - if (in_addr_is_localhost(p->family, &p->sender) <= 0 || - in_addr_is_localhost(p->family, &p->destination) <= 0) { - log_error("Got packet on unexpected IP range, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); - goto fail; - } - - r = dns_packet_extract(p); - if (r < 0) { - log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR); - goto fail; - } - - if (!DNS_PACKET_VERSION_SUPPORTED(p)) { - log_debug("Got EDNS OPT field with unsupported version number."); - dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS); - goto fail; - } - - if (dns_type_is_obsolete(p->question->keys[0]->type)) { - log_debug("Got message with obsolete key type, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); - goto fail; - } - - if (dns_type_is_zone_transer(p->question->keys[0]->type)) { - log_debug("Got request for zone transfer, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); - goto fail; - } - - if (!DNS_PACKET_RD(p)) { - /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */ - log_debug("Got request with recursion disabled, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED); - goto fail; - } - - if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) { - log_debug("Got request with DNSSEC CD bit set, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); - goto fail; - } - - r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME); - if (r < 0) { - log_error_errno(r, "Failed to generate query object: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); - goto fail; - } - - /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */ - q->clamp_ttl = true; - - q->request_dns_packet = dns_packet_ref(p); - q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */ - q->complete = dns_stub_query_complete; - - if (s) { - s->on_packet = NULL; - s->complete = dns_stub_stream_complete; - s->query = q; - } - - r = dns_query_go(q); - if (r < 0) { - log_error_errno(r, "Failed to start query: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); - goto fail; - } - - log_info("Processing query..."); - return; - -fail: - if (s && DNS_STREAM_QUEUED(s)) - dns_stub_detach_stream(s); - - dns_query_free(q); -} - -static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - Manager *m = userdata; - int r; - - r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p); - if (r <= 0) - return r; - - if (dns_packet_validate_query(p) > 0) { - log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p)); - - dns_stub_process_query(m, NULL, p); - } else - log_debug("Invalid DNS stub UDP packet, ignoring."); - - return 0; -} - -int manager_dns_stub_udp_fd(Manager *m) { - static const int one = 1; - - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(53), - .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), - }; - - int r; - - if (m->dns_stub_udp_fd >= 0) - return m->dns_stub_udp_fd; - - m->dns_stub_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_stub_udp_fd < 0) - return -errno; - - r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->dns_stub_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - /* Make sure no traffic from outside the local host can leak to onto this socket */ - r = setsockopt(m->dns_stub_udp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->dns_stub_udp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, m->dns_stub_udp_fd, EPOLLIN, on_dns_stub_packet, m); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp"); - - return m->dns_stub_udp_fd; - -fail: - m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd); - return r; -} - -static int on_dns_stub_stream_packet(DnsStream *s) { - assert(s); - assert(s->read_packet); - - if (dns_packet_validate_query(s->read_packet) > 0) { - log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); - - dns_stub_process_query(s->manager, s, s->read_packet); - } else - log_debug("Invalid DNS stub TCP packet, ignoring."); - - /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now, - * or that didn't happen in which case we want to free the stream */ - dns_stream_unref(s); - - return 0; -} - -static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - DnsStream *stream; - Manager *m = userdata; - int cfd, r; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd); - if (r < 0) { - safe_close(cfd); - return r; - } - - stream->on_packet = on_dns_stub_stream_packet; - - /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action - * of the stream, or by our packet callback, or when the manager is shut down. */ - - return 0; -} - -int manager_dns_stub_tcp_fd(Manager *m) { - static const int one = 1; - - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), - .in.sin_port = htobe16(53), - }; - - int r; - - if (m->dns_stub_tcp_fd >= 0) - return m->dns_stub_tcp_fd; - - m->dns_stub_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_stub_tcp_fd < 0) - return -errno; - - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->dns_stub_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - /* Make sure no traffic from outside the local host can leak to onto this socket */ - r = setsockopt(m->dns_stub_tcp_fd, SOL_SOCKET, SO_BINDTODEVICE, "lo", 3); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->dns_stub_tcp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = listen(m->dns_stub_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, m->dns_stub_tcp_fd, EPOLLIN, on_dns_stub_stream, m); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp"); - - return m->dns_stub_tcp_fd; - -fail: - m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd); - return r; -} - -int manager_dns_stub_start(Manager *m) { - int r; - - assert(m); - - r = manager_dns_stub_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_dns_stub_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - return 0; - -eaddrinuse: - log_warning("Another process is already listening on 127.0.0.53:53. Turning off local DNS stub support."); - manager_dns_stub_stop(m); - - return 0; -} - -void manager_dns_stub_stop(Manager *m) { - assert(m); - - m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source); - m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source); - - m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd); - m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd); -} diff --git a/src/resolve/resolved-dns-stub.h b/src/resolve/resolved-dns-stub.h deleted file mode 100644 index fce4d25ede..0000000000 --- a/src/resolve/resolved-dns-stub.h +++ /dev/null @@ -1,31 +0,0 @@ -#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 . -***/ - -#include "resolved-manager.h" - -/* 127.0.0.53 in native endian */ -#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U) - -int manager_dns_stub_udp_fd(Manager *m); -int manager_dns_stub_tcp_fd(Manager *m); - -void manager_dns_stub_stop(Manager *m); -int manager_dns_stub_start(Manager *m); diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c deleted file mode 100644 index e3003411f7..0000000000 --- a/src/resolve/resolved-dns-synthesize.c +++ /dev/null @@ -1,413 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "hostname-util.h" -#include "local-addresses.h" -#include "resolved-dns-synthesize.h" - -int dns_synthesize_ifindex(int ifindex) { - - /* When the caller asked for resolving on a specific - * interface, we synthesize the answer for that - * interface. However, if nothing specific was claimed and we - * only return localhost RRs, we synthesize the answer for - * localhost. */ - - if (ifindex > 0) - return ifindex; - - return LOOPBACK_IFINDEX; -} - -int dns_synthesize_family(uint64_t flags) { - - /* Picks an address family depending on set flags. This is - * purely for synthesized answers, where the family we return - * for the reply should match what was requested in the - * question, even though we are synthesizing the answer - * here. */ - - if (!(flags & SD_RESOLVED_DNS)) { - if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4)) - return AF_INET; - if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6)) - return AF_INET6; - } - - return AF_UNSPEC; -} - -DnsProtocol dns_synthesize_protocol(uint64_t flags) { - - /* Similar as dns_synthesize_family() but does this for the - * protocol. If resolving via DNS was requested, we claim it - * was DNS. Similar, if nothing specific was - * requested. However, if only resolving via LLMNR was - * requested we return that. */ - - if (flags & SD_RESOLVED_DNS) - return DNS_PROTOCOL_DNS; - if (flags & SD_RESOLVED_LLMNR) - return DNS_PROTOCOL_LLMNR; - if (flags & SD_RESOLVED_MDNS) - return DNS_PROTOCOL_MDNS; - - return DNS_PROTOCOL_DNS; -} - -static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { - int r; - - assert(m); - assert(key); - assert(answer); - - r = dns_answer_reserve(answer, 2); - if (r < 0) - return r; - - if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key)); - if (!rr) - return -ENOMEM; - - rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); - - r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key)); - if (!rr) - return -ENOMEM; - - rr->aaaa.in6_addr = in6addr_loopback; - - r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - return 0; -} - -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); - if (!rr) - return -ENOMEM; - - rr->ptr.name = strdup(to); - if (!rr->ptr.name) - return -ENOMEM; - - return dns_answer_add(*answer, rr, ifindex, flags); -} - -static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { - int r; - - assert(m); - assert(key); - assert(answer); - - if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) { - r = dns_answer_reserve(answer, 1); - if (r < 0) - return r; - - r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - return 0; -} - -static int answer_add_addresses_rr( - DnsAnswer **answer, - const char *name, - struct local_address *addresses, - unsigned n_addresses) { - - unsigned j; - int r; - - assert(answer); - assert(name); - - r = dns_answer_reserve(answer, n_addresses); - if (r < 0) - return r; - - for (j = 0; j < n_addresses; j++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name); - if (r < 0) - return r; - - r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - return 0; -} - -static int answer_add_addresses_ptr( - DnsAnswer **answer, - const char *name, - struct local_address *addresses, - unsigned n_addresses, - int af, const union in_addr_union *match) { - - unsigned j; - int r; - - assert(answer); - assert(name); - - for (j = 0; j < n_addresses; j++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - if (af != AF_UNSPEC) { - - if (addresses[j].family != af) - continue; - - if (match && !in_addr_equal(af, match, &addresses[j].address)) - continue; - } - - r = dns_answer_reserve(answer, 1); - if (r < 0) - return r; - - r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name); - if (r < 0) - return r; - - r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - return 0; -} - -static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { - _cleanup_free_ struct local_address *addresses = NULL; - int n = 0, af; - - assert(m); - assert(key); - assert(answer); - - af = dns_type_to_af(key->type); - if (af >= 0) { - n = local_addresses(m->rtnl, ifindex, af, &addresses); - if (n < 0) - return n; - - if (n == 0) { - struct local_address buffer[2]; - - /* If we have no local addresses then use ::1 - * and 127.0.0.2 as local ones. */ - - if (af == AF_INET || af == AF_UNSPEC) - buffer[n++] = (struct local_address) { - .family = AF_INET, - .ifindex = dns_synthesize_ifindex(ifindex), - .address.in.s_addr = htobe32(0x7F000002), - }; - - if (af == AF_INET6 || af == AF_UNSPEC) - buffer[n++] = (struct local_address) { - .family = AF_INET6, - .ifindex = dns_synthesize_ifindex(ifindex), - .address.in6 = in6addr_loopback, - }; - - return answer_add_addresses_rr(answer, dns_resource_key_name(key), buffer, n); - } - } - - return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); -} - -static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { - _cleanup_free_ struct local_address *addresses = NULL; - int n, r; - - assert(m); - assert(address); - assert(answer); - - if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { - - /* Always map the IPv4 address 127.0.0.2 to the local - * hostname, in addition to "localhost": */ - - r = dns_answer_reserve(answer, 3); - if (r < 0) - return r; - - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - - return 0; - } - - n = local_addresses(m->rtnl, ifindex, af, &addresses); - if (n < 0) - return n; - - r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address); - if (r < 0) - return r; - - return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address); -} - -static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { - _cleanup_free_ struct local_address *addresses = NULL; - int n = 0, af; - - assert(m); - assert(key); - assert(answer); - - af = dns_type_to_af(key->type); - if (af >= 0) { - n = local_gateways(m->rtnl, ifindex, af, &addresses); - if (n < 0) - return n; - } - - return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); -} - -static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { - _cleanup_free_ struct local_address *addresses = NULL; - int n; - - assert(m); - assert(address); - assert(answer); - - n = local_gateways(m->rtnl, ifindex, af, &addresses); - if (n < 0) - return n; - - return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address); -} - -int dns_synthesize_answer( - Manager *m, - DnsQuestion *q, - int ifindex, - DnsAnswer **ret) { - - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - DnsResourceKey *key; - bool found = false; - int r; - - assert(m); - assert(q); - - DNS_QUESTION_FOREACH(key, q) { - union in_addr_union address; - const char *name; - int af; - - if (key->class != DNS_CLASS_IN && - key->class != DNS_CLASS_ANY) - continue; - - name = dns_resource_key_name(key); - - if (is_localhost(name)) { - - r = synthesize_localhost_rr(m, key, ifindex, &answer); - if (r < 0) - return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); - - } else if (manager_is_own_hostname(m, name)) { - - r = synthesize_system_hostname_rr(m, key, ifindex, &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(m, key, ifindex, &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(m, key, ifindex, &answer); - if (r < 0) - return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); - - } else if (dns_name_address(name, &af, &address) > 0) { - - r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer); - if (r < 0) - return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m"); - - r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer); - if (r < 0) - return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m"); - } else - continue; - - found = true; - } - - r = found; - - if (ret) { - *ret = answer; - answer = NULL; - } - - return r; -} diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h deleted file mode 100644 index 5d829bb2e7..0000000000 --- a/src/resolve/resolved-dns-synthesize.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include "resolved-dns-answer.h" -#include "resolved-dns-question.h" -#include "resolved-manager.h" - -int dns_synthesize_ifindex(int ifindex); -int dns_synthesize_family(uint64_t flags); -DnsProtocol dns_synthesize_protocol(uint64_t flags); - -int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c deleted file mode 100644 index d455b6b1fa..0000000000 --- a/src/resolve/resolved-dns-transaction.c +++ /dev/null @@ -1,3107 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "af-list.h" -#include "alloc-util.h" -#include "dns-domain.h" -#include "errno-list.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; - t->answer_errno = 0; -} - -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); - set_remove(z->notify_transactions_done, t); - dns_transaction_gc(z); - } -} - -static void dns_transaction_close_connection(DnsTransaction *t) { - assert(t); - - if (t->stream) { - /* Let's detach the stream from our transaction, in case something else keeps a reference to it. */ - t->stream->complete = NULL; - t->stream->on_packet = NULL; - t->stream->transaction = NULL; - t->stream = dns_stream_unref(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) { - DnsQueryCandidate *c; - DnsZoneItem *i; - DnsTransaction *z; - - if (!t) - return NULL; - - log_debug("Freeing transaction %" PRIu16 ".", t->id); - - dns_transaction_close_connection(t); - dns_transaction_stop_timeout(t); - - dns_packet_unref(t->sent); - dns_transaction_reset_answer(t); - - dns_server_unref(t->server); - - if (t->scope) { - 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)); - } - - while ((c = set_steal_first(t->notify_query_candidates))) - set_remove(c->transactions, t); - set_free(t->notify_query_candidates); - - while ((c = set_steal_first(t->notify_query_candidates_done))) - set_remove(c->transactions, t); - set_free(t->notify_query_candidates_done); - - while ((i = set_steal_first(t->notify_zone_items))) - i->probe_transaction = NULL; - set_free(t->notify_zone_items); - - while ((i = set_steal_first(t->notify_zone_items_done))) - i->probe_transaction = NULL; - set_free(t->notify_zone_items_done); - - while ((z = set_steal_first(t->notify_transactions))) - set_remove(z->dnssec_transactions, t); - set_free(t->notify_transactions); - - while ((z = set_steal_first(t->notify_transactions_done))) - set_remove(z->dnssec_transactions, t); - set_free(t->notify_transactions_done); - - 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); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free); - -bool dns_transaction_gc(DnsTransaction *t) { - assert(t); - - if (t->block_gc > 0) - return true; - - if (set_isempty(t->notify_query_candidates) && - set_isempty(t->notify_query_candidates_done) && - set_isempty(t->notify_zone_items) && - set_isempty(t->notify_zone_items_done) && - set_isempty(t->notify_transactions) && - set_isempty(t->notify_transactions_done)) { - 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) { - _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL; - int r; - - assert(ret); - 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_by_key, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - t = new0(DnsTransaction, 1); - if (!t) - 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; - t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; - - t->id = pick_new_id(s->manager); - - r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t); - if (r < 0) { - t->id = 0; - return r; - } - - 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; - - t = NULL; - - return 0; -} - -static void dns_transaction_shuffle_id(DnsTransaction *t) { - uint16_t new_id; - assert(t); - - /* 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) { - _cleanup_free_ char *pretty = NULL; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - DnsZoneItem *z; - - assert(t); - assert(p); - - if (manager_our_packet(t->scope->manager, p) != 0) - return; - - (void) in_addr_to_string(p->family, &p->sender, &pretty); - - log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", - t->id, - dns_resource_key_to_string(t->key, key_str, sizeof key_str), - dns_protocol_to_string(t->scope->protocol), - t->scope->link ? t->scope->link->name : "*", - af_to_name_short(t->scope->family), - strnull(pretty)); - - /* RFC 4795, Section 4.1 says that the peer with the - * lexicographically smaller IP address loses */ - if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) { - log_debug("Peer has lexicographically larger IP address and thus lost in the conflict."); - return; - } - - log_debug("We have the lexicographically larger IP address and thus lost in the conflict."); - - t->block_gc++; - - 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); - - /* Secondly, report this as conflict, so that we might - * look for a different hostname */ - dns_zone_item_conflict(z); - } - t->block_gc--; - - dns_transaction_gc(t); -} - -void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { - DnsQueryCandidate *c; - DnsZoneItem *z; - DnsTransaction *d; - const char *st; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - assert(t); - assert(!DNS_TRANSACTION_IS_LIVE(state)); - - if (state == DNS_TRANSACTION_DNSSEC_FAILED) { - dns_resource_key_to_string(t->key, key_str, sizeof key_str); - - log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE), - LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)), - "DNS_TRANSACTION=%" PRIu16, t->id, - "DNS_QUESTION=%s", key_str, - "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result), - "DNS_SERVER=%s", dns_server_string(t->server), - "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level), - NULL); - } - - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function. */ - - if (state == DNS_TRANSACTION_ERRNO) - st = errno_to_name(t->answer_errno); - else - st = dns_transaction_state_to_string(state); - - log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).", - t->id, - dns_resource_key_to_string(t->key, key_str, sizeof key_str), - dns_protocol_to_string(t->scope->protocol), - t->scope->link ? t->scope->link->name : "*", - af_to_name_short(t->scope->family), - st, - t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source), - t->answer_authenticated ? "authenticated" : "unsigned"); - - t->state = state; - - 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_MOVE(c, t->notify_query_candidates_done, t->notify_query_candidates) - dns_query_candidate_notify(c); - SWAP_TWO(t->notify_query_candidates, t->notify_query_candidates_done); - - SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) - dns_zone_item_notify(z); - SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); - - SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) - dns_transaction_notify(d, t); - SWAP_TWO(t->notify_transactions, t->notify_transactions_done); - - 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); - - /* Pick a DNS server and a feature level for it. */ - - server = dns_scope_get_dns_server(t->scope); - if (!server) - return -ESRCH; - - /* If we changed the server invalidate the feature level clamping, as the new server might have completely - * different properties. */ - if (server != t->server) - t->clamp_feature_level = _DNS_SERVER_FEATURE_LEVEL_INVALID; - - t->current_feature_level = dns_server_possible_feature_level(server); - - /* Clamp the feature level if that is requested. */ - if (t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID && - t->current_feature_level > t->clamp_feature_level) - t->current_feature_level = t->clamp_feature_level; - - log_debug("Using feature level %s for transaction %u.", dns_server_feature_level_to_string(t->current_feature_level), t->id); - - if (server == t->server) - return 0; - - dns_server_unref(t->server); - t->server = dns_server_ref(server); - - log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id); - - return 1; -} - -static void dns_transaction_retry(DnsTransaction *t, bool next_server) { - int r; - - assert(t); - - log_debug("Retrying transaction %" PRIu16 ".", t->id); - - /* Before we try again, switch to a new server. */ - if (next_server) - dns_scope_next_dns_server(t->scope); - - r = dns_transaction_go(t); - if (r < 0) { - t->answer_errno = -r; - dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); - } -} - -static int dns_transaction_maybe_restart(DnsTransaction *t) { - int r; - - assert(t); - - /* Returns > 0 if the transaction was restarted, 0 if not */ - - 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); - - r = dns_transaction_go(t); - if (r < 0) - return r; - - return 1; -} - -static int on_stream_complete(DnsStream *s, int error) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t; - - assert(s); - assert(s->transaction); - - /* Copy the data we care about out of the stream before we - * destroy it. */ - t = s->transaction; - p = dns_packet_ref(s->read_packet); - - dns_transaction_close_connection(t); - - if (ERRNO_IS_DISCONNECT(error)) { - usec_t usec; - - if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { - /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the - * question on this scope. */ - dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND); - return 0; - } - - 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, true); - return 0; - } - if (error != 0) { - t->answer_errno = error; - dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); - return 0; - } - - if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid TCP reply packet."); - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return 0; - } - - dns_scope_check_conflicts(t->scope, p); - - t->block_gc++; - dns_transaction_process_reply(t, p); - t->block_gc--; - - /* 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) { - _cleanup_close_ int fd = -1; - int r; - - assert(t); - - dns_transaction_close_connection(t); - - switch (t->scope->protocol) { - - case DNS_PROTOCOL_DNS: - 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_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; - - /* Otherwise, try to talk to the owner of a - * the IP address, in case this is a reverse - * PTR lookup */ - - r = dns_name_address(dns_resource_key_name(t->key), &family, &address); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - if (family != t->scope->family) - return -ESRCH; - - fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT); - } - - break; - - default: - return -EAFNOSUPPORT; - } - - if (fd < 0) - return fd; - - 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); - if (r < 0) { - t->stream = dns_stream_unref(t->stream); - return r; - } - - t->stream->complete = on_stream_complete; - t->stream->transaction = t; - - /* The interface index is difficult to determine if we are - * connecting to the local host, hence fill this in right away - * instead of determining it from the socket */ - t->stream->ifindex = dns_scope_ifindex(t->scope); - - dns_transaction_reset_answer(t); - - t->tried_stream = true; - - return 0; -} - -static void dns_transaction_cache_answer(DnsTransaction *t) { - assert(t); - - /* 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; - - /* Caching disabled? */ - if (!t->scope->manager->enable_cache) - return; - - /* 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 (!IN_SET(dt->answer_rcode, DNS_RCODE_NXDOMAIN, DNS_RCODE_SERVFAIL)) { - log_debug("Auxiliary DNSSEC RR query failed with rcode=%s.", dns_rcode_to_string(dt->answer_rcode)); - goto fail; - } - - /* Fall-through: NXDOMAIN/SERVFAIL is good enough for us. This is because some DNS servers - * erronously return NXDOMAIN/SERVFAIL for empty non-terminals (Akamai...) or missing DS - * records (Facebook), 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) - goto fail; - 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) - goto fail; - 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 == -EBADMSG) { - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - if (r < 0) - goto fail; - - 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; - } - - if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER) - dns_server_warn_downgrade(t->server); - - 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); - - return; - -fail: - t->answer_errno = -r; - dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); -} - -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_fix_rcode(DnsTransaction *t) { - int r; - - assert(t); - - /* Fix up the RCODE to SUCCESS if we get at least one matching RR in a response. Note that this contradicts the - * DNS RFCs a bit. Specifically, RFC 6604 Section 3 clarifies that the RCODE shall say something about a - * CNAME/DNAME chain element coming after the last chain element contained in the message, and not the first - * one included. However, it also indicates that not all DNS servers implement this correctly. Moreover, when - * using DNSSEC we usually only can prove the first element of a CNAME/DNAME chain anyway, hence let's settle - * on always processing the RCODE as referring to the immediate look-up we do, i.e. the first element of a - * CNAME/DNAME chain. This way, we uniformly handle CNAME/DNAME chains, regardless if the DNS server - * incorrectly implements RCODE, whether DNSSEC is in use, or whether the DNS server only supplied us with an - * incomplete CNAME/DNAME chain. - * - * Or in other words: if we get at least one positive reply in a message we patch NXDOMAIN to become SUCCESS, - * and then rely on the CNAME chasing logic to figure out that there's actually a CNAME error with a new - * lookup. */ - - if (t->answer_rcode != DNS_RCODE_NXDOMAIN) - return 0; - - r = dns_transaction_has_positive_answer(t, NULL); - if (r <= 0) - return r; - - t->answer_rcode = DNS_RCODE_SUCCESS; - return 0; -} - -void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { - usec_t ts; - int r; - - assert(t); - assert(p); - 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: - /* For LLMNR we will not accept any packets from other interfaces */ - - if (p->ifindex != dns_scope_ifindex(t->scope)) - return; - - if (p->family != t->scope->family) - return; - - /* Tentative packets are not full responses but still - * useful for identifying uniqueness conflicts during - * probing. */ - if (DNS_PACKET_LLMNR_T(p)) { - dns_transaction_tentative(t, p); - return; - } - - break; - - case DNS_PROTOCOL_MDNS: - /* For mDNS we will not accept any packets from other interfaces */ - - if (p->ifindex != dns_scope_ifindex(t->scope)) - 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: - assert_not_reached("Invalid DNS protocol."); - } - - if (t->received != p) { - dns_packet_unref(t->received); - 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 */ - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - - if (DNS_PACKET_ID(p) != t->id) { - /* Not the reply to our query? Somebody must be fucking with us */ - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; - } - } - - 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); - - if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_FORMERR, DNS_RCODE_SERVFAIL, DNS_RCODE_NOTIMP)) { - - /* Request failed, immediately try again with reduced features */ - - if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) { - /* This was already at the lowest possible feature level? If so, we can't downgrade - * this transaction anymore, hence let's process the response, and accept the rcode. */ - log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p))); - break; - } - - /* Reduce this feature level by one and try again. */ - t->clamp_feature_level = t->current_feature_level - 1; - - log_debug("Server returned error %s, retrying transaction with reduced feature level %s.", - dns_rcode_to_string(DNS_PACKET_RCODE(p)), - dns_server_feature_level_to_string(t->clamp_feature_level)); - - dns_transaction_retry(t, false /* use the same server */); - 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: - 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) { - /* No servers found? Damn! */ - 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_DNS) - goto fail; - - /* On DNS, couldn't send? Try immediately again, with a new server */ - dns_transaction_retry(t, true); - } - - return; - } - - /* 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; - } - - if (t->server) { - /* Report that we successfully received a valid packet with a good rcode after we initially got a bad - * rcode and subsequently downgraded the protocol */ - - if (IN_SET(DNS_PACKET_RCODE(p), DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN) && - t->clamp_feature_level != _DNS_SERVER_FEATURE_LEVEL_INVALID) - dns_server_packet_rcode_downgrade(t->server, t->clamp_feature_level); - - /* Report that the OPT RR was missing */ - if (!p->opt) - dns_server_packet_bad_opt(t->server, t->current_feature_level); - - /* Report that we successfully received a packet */ - 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) - goto fail; - if (r > 0) /* Transaction got restarted... */ - return; - - if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) { - - /* Only consider responses with equivalent query section to the request */ - r = dns_packet_is_reply_for(p, t->key); - if (r < 0) - goto fail; - 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; - - r = dns_transaction_fix_rcode(t); - if (r < 0) - goto fail; - - /* 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) - goto fail; - 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); - return; - -fail: - t->answer_errno = -r; - dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); -} - -static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t = userdata; - int r; - - assert(t); - assert(t->scope); - - r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); - if (ERRNO_IS_DISCONNECT(-r)) { - usec_t usec; - - /* 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, true); - return 0; - } - if (r < 0) { - dns_transaction_complete(t, DNS_TRANSACTION_ERRNO); - t->answer_errno = -r; - 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_udp(DnsTransaction *t) { - int r; - - assert(t); - - if (t->scope->protocol == DNS_PROTOCOL_DNS) { - - r = dns_transaction_pick_server(t); - if (r < 0) - return r; - - if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP) - return -EAGAIN; - - if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type)) - return -EOPNOTSUPP; - - if (r > 0 || t->dns_udp_fd < 0) { /* Server changed, or no connection yet. */ - int fd; - - dns_transaction_close_connection(t); - - fd = dns_scope_socket_udp(t->scope, t->server, 53); - if (fd < 0) - return fd; - - 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; - } - - (void) sd_event_source_set_description(t->dns_udp_event_source, "dns-transaction-udp"); - t->dns_udp_fd = fd; - } - - 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) - return r; - - dns_transaction_reset_answer(t); - - return 0; -} - -static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) { - DnsTransaction *t = userdata; - - assert(s); - assert(t); - - if (!t->initial_jitter_scheduled || t->initial_jitter_elapsed) { - /* Timeout reached? Increase the timeout for the server used */ - switch (t->scope->protocol) { - - 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; - - case DNS_PROTOCOL_LLMNR: - case DNS_PROTOCOL_MDNS: - dns_scope_packet_lost(t->scope, usec - t->start_usec); - break; - - default: - assert_not_reached("Invalid DNS protocol."); - } - - if (t->initial_jitter_scheduled) - t->initial_jitter_elapsed = true; - } - - log_debug("Timeout reached on transaction %" PRIu16 ".", t->id); - - dns_transaction_retry(t, true); - return 0; -} - -static usec_t transaction_get_resend_timeout(DnsTransaction *t) { - assert(t); - assert(t->scope); - - switch (t->scope->protocol) { - - case DNS_PROTOCOL_DNS: - assert(t->server); - return t->server->resend_timeout; - - 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."); - } -} - -static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { - int r; - - assert(t); - - dns_transaction_stop_timeout(t); - - r = dns_scope_network_good(t->scope); - if (r < 0) - return r; - if (r == 0) { - dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN); - return 0; - } - - 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 && 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; - } - - t->n_attempts++; - t->start_usec = ts; - - 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 and trust them. */ - 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, dns_scope_ifindex(t->scope), &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->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 - * might flush the cache. */ - dns_scope_get_dns_server(t->scope); - - /* Let's then prune all outdated entries */ - dns_cache_prune(&t->scope->cache); - - r = dns_cache_lookup(&t->scope->cache, t->key, t->clamp_ttl, &t->answer_rcode, &t->answer, &t->answer_authenticated); - if (r < 0) - return r; - if (r > 0) { - 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_RCODE_FAILURE); - return 0; - } - } - - 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_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; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - assert(t); - - /* Returns > 0 if the transaction is now pending, returns 0 if could be processed immediately and has finished - * now. */ - - 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("Transaction %" PRIu16 " for <%s> scope %s on %s/%s.", - t->id, - dns_resource_key_to_string(t->key, key_str, sizeof key_str), - dns_protocol_to_string(t->scope->protocol), - t->scope->link ? t->scope->link->name : "*", - af_to_name_short(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_scheduled = true; - - random_bytes(&jitter, sizeof(jitter)); - - 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, 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 %s transaction for " USEC_FMT "us.", dns_protocol_to_string(t->scope->protocol), jitter); - return 0; - } - - /* Otherwise, we need to ask the network */ - r = dns_transaction_make_packet(t); - if (r < 0) - return r; - - if (t->scope->protocol == DNS_PROTOCOL_LLMNR && - (dns_name_endswith(dns_resource_key_name(t->key), "in-addr.arpa") > 0 || - dns_name_endswith(dns_resource_key_name(t->key), "ip6.arpa") > 0)) { - - /* RFC 4795, Section 2.4. says reverse lookups shall - * 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 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; - } - 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 (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) { - /* On LLMNR, if we cannot connect to a host via TCP when doing reverse lookups. This means we cannot - * answer this request with this protocol. */ - dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND); - return 0; - } - if (r < 0) { - if (t->scope->protocol != DNS_PROTOCOL_DNS) - return r; - - /* Couldn't send? Try immediately again, with a new server */ - 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, 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) { - char s[DNS_RESOURCE_KEY_STRING_MAX], saux[DNS_RESOURCE_KEY_STRING_MAX]; - - log_debug("Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", - aux->id, - dns_resource_key_to_string(t->key, s, sizeof s), - t->id, - dns_resource_key_to_string(aux->key, saux, sizeof saux)); - - 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_ensure_allocated(&aux->notify_transactions_done, 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_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) { - int r; - - assert(t); - - /* Check whether the specified name is in 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. */ - - r = dns_resource_key_match_rr(t->key, rr, NULL); - if (r != 0) - return r; - - return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL); -} - -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; - - /* Note that we do not check the feature level actually used for the transaction but instead the feature level - * the server is known to support currently, as the transaction feature level might be lower than what the - * server actually supports, since we might have downgraded this transaction's feature level because we got a - * SERVFAIL earlier and wanted to check whether downgrading fixes it. */ - - 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 queries with no matching response RR, and no NSEC/NSEC3, the DS RR - * - For 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; - uint16_t type = 0; - - name = dns_resource_key_name(t->key); - - /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this - * could also be used as indication that we are not at a zone apex, but in real world setups there are - * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even - * though they have further children. If this was a DS request, then it's signed when the parent zone - * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR, - * to see if that is signed. */ - - if (t->key->type == DNS_TYPE_DS) { - r = dns_name_parent(&name); - if (r > 0) { - type = DNS_TYPE_SOA; - log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).", - t->id, dns_resource_key_name(t->key)); - } else - name = NULL; - - } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) { - - type = DNS_TYPE_DS; - log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).", - t->id, dns_resource_key_name(t->key)); - - } else { - type = DNS_TYPE_SOA; - 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, type, 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) { - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - DnsTransaction *dt; - const char *name; - uint16_t type = 0; - 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_resource_key_to_string(t->key, key_str, sizeof key_str)); - return false; - } - - name = dns_resource_key_name(t->key); - - if (t->key->type == DNS_TYPE_DS) { - - /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed, - * hence check the parent SOA in this case. */ - - r = dns_name_parent(&name); - if (r < 0) - return r; - if (r == 0) - return true; - - type = DNS_TYPE_SOA; - - } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) - /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */ - type = DNS_TYPE_DS; - else - /* For all other negative replies, check for the SOA lookup */ - type = DNS_TYPE_SOA; - - /* 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 != type) - 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; -} - -typedef enum { - DNSSEC_PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */ - DNSSEC_PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */ - DNSSEC_PHASE_ALL, /* Phase #3, validate everything else */ -} Phase; - -static int dnssec_validate_records( - DnsTransaction *t, - Phase phase, - bool *have_nsec, - DnsAnswer **validated) { - - DnsResourceRecord *rr; - int r; - - /* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */ - - 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 == DNSSEC_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 == DNSSEC_PHASE_DNSKEY) - continue; - break; - - default: - /* We validate all other RRs only in the ALL phases */ - if (phase != DNSSEC_PHASE_ALL) - continue; - } - - 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; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key); - - /* Exit the loop, we dropped something from the answer, start from the beginning */ - return 1; - } - - /* 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 != DNSSEC_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; - - manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key); - - /* Exit the loop, we dropped something from the answer, start from the beginning */ - return 1; - } - } - - 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; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - return 1; - } - - 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; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - return 1; - } - - /* 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) { - char s[DNS_RESOURCE_KEY_STRING_MAX]; - - /* 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. */ - - log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", - dns_resource_key_to_string(rr->key, s, sizeof s)); - - r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); - if (r < 0) - return r; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - return 1; - } - } - - 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; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - return 1; - } - } - - 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) { - if (IN_SET(result, - DNSSEC_INVALID, - DNSSEC_SIGNATURE_EXPIRED, - DNSSEC_NO_SIGNATURE)) - manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key); - else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */ - manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key); - - /* 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; - - /* We dropped something from the answer, start from the beginning. */ - return 1; - } - - return 2; /* Finito. */ -} - -int dns_transaction_validate_dnssec(DnsTransaction *t) { - _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; - Phase phase; - DnsAnswerFlags flags; - int r; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - - 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 for %" PRIu16 ", used server feature level does not support DNSSEC.", t->id); - return 0; - } - - log_debug("Validating response from transaction %" PRIu16 " (%s).", - t->id, - dns_resource_key_to_string(t->key, key_str, sizeof key_str)); - - /* 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 = DNSSEC_PHASE_DNSKEY; - for (;;) { - bool have_nsec = false; - - r = dnssec_validate_records(t, phase, &have_nsec, &validated); - if (r <= 0) - return r; - - /* Try again as long as we managed to achieve something */ - if (r == 1) - continue; - - if (phase == DNSSEC_PHASE_DNSKEY && have_nsec) { - /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */ - phase = DNSSEC_PHASE_NSEC; - continue; - } - - if (phase != DNSSEC_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 = DNSSEC_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, key_str); - t->answer_dnssec_result = DNSSEC_VALIDATED; - t->answer_rcode = DNS_RCODE_NXDOMAIN; - t->answer_authenticated = authenticated; - - manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key); - 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, key_str); - t->answer_dnssec_result = DNSSEC_VALIDATED; - t->answer_rcode = DNS_RCODE_SUCCESS; - t->answer_authenticated = authenticated; - - manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key); - 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, key_str); - t->answer_dnssec_result = DNSSEC_UNSIGNED; - t->answer_authenticated = false; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key); - 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; - manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key); - } else { - t->answer_dnssec_result = DNSSEC_UNSIGNED; - t->answer_authenticated = false; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key); - } - - break; - - case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM: - /* We don't know the NSEC3 algorithm used? */ - t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key); - 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; - manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key); - break; - - default: - assert_not_reached("Unexpected NSEC result."); - } - } - - return 1; -} - -static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { - [DNS_TRANSACTION_NULL] = "null", - [DNS_TRANSACTION_PENDING] = "pending", - [DNS_TRANSACTION_VALIDATING] = "validating", - [DNS_TRANSACTION_RCODE_FAILURE] = "rcode-failure", - [DNS_TRANSACTION_SUCCESS] = "success", - [DNS_TRANSACTION_NO_SERVERS] = "no-servers", - [DNS_TRANSACTION_TIMEOUT] = "timeout", - [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached", - [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply", - [DNS_TRANSACTION_ERRNO] = "errno", - [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", - [DNS_TRANSACTION_NETWORK_DOWN] = "network-down", - [DNS_TRANSACTION_NOT_FOUND] = "not-found", -}; -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 deleted file mode 100644 index 96b066845d..0000000000 --- a/src/resolve/resolved-dns-transaction.h +++ /dev/null @@ -1,179 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -typedef struct DnsTransaction DnsTransaction; -typedef enum DnsTransactionState DnsTransactionState; -typedef enum DnsTransactionSource DnsTransactionSource; - -enum DnsTransactionState { - DNS_TRANSACTION_NULL, - DNS_TRANSACTION_PENDING, - DNS_TRANSACTION_VALIDATING, - DNS_TRANSACTION_RCODE_FAILURE, - DNS_TRANSACTION_SUCCESS, - DNS_TRANSACTION_NO_SERVERS, - DNS_TRANSACTION_TIMEOUT, - DNS_TRANSACTION_ATTEMPTS_MAX_REACHED, - DNS_TRANSACTION_INVALID_REPLY, - DNS_TRANSACTION_ERRNO, - DNS_TRANSACTION_ABORTED, - DNS_TRANSACTION_DNSSEC_FAILED, - DNS_TRANSACTION_NO_TRUST_ANCHOR, - DNS_TRANSACTION_RR_TYPE_UNSUPPORTED, - DNS_TRANSACTION_NETWORK_DOWN, - DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */ - _DNS_TRANSACTION_STATE_MAX, - _DNS_TRANSACTION_STATE_INVALID = -1 -}; - -#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-scope.h" - -struct DnsTransaction { - DnsScope *scope; - - DnsResourceKey *key; - - DnsTransactionState state; - - uint16_t id; - - bool tried_stream:1; - - bool initial_jitter_scheduled:1; - bool initial_jitter_elapsed:1; - - bool clamp_ttl:1; - - DnsPacket *sent, *received; - - DnsAnswer *answer; - int answer_rcode; - DnssecResult answer_dnssec_result; - DnsTransactionSource answer_source; - uint32_t answer_nsec_ttl; - int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ - - /* 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; - - /* The features of the DNS server at time of transaction start */ - DnsServerFeatureLevel current_feature_level; - - /* If we got SERVFAIL back, we retry the lookup, using a lower feature level than we used before. */ - DnsServerFeatureLevel clamp_feature_level; - - /* Query candidates this transaction is referenced by and that - * shall be notified about this specific transaction - * completing. */ - Set *notify_query_candidates, *notify_query_candidates_done; - - /* Zone items this transaction is referenced by and that shall - * be notified about completion. */ - Set *notify_zone_items, *notify_zone_items_done; - - /* 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, *notify_transactions_done; - - /* The opposite direction: the transactions this transaction - * created in order to request DNSKEY or DS RRs. */ - Set *dnssec_transactions; - - unsigned block_gc; - - LIST_FIELDS(DnsTransaction, transactions_by_scope); -}; - -int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key); -DnsTransaction* dns_transaction_free(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_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) diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c deleted file mode 100644 index 77370e7dd5..0000000000 --- a/src/resolve/resolved-dns-trust-anchor.c +++ /dev/null @@ -1,743 +0,0 @@ -/*** - 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 . -***/ - -#include - -#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 deleted file mode 100644 index 635c75fde5..0000000000 --- a/src/resolve/resolved-dns-trust-anchor.h +++ /dev/null @@ -1,43 +0,0 @@ -#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 . -***/ - -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 deleted file mode 100644 index 746a979f47..0000000000 --- a/src/resolve/resolved-dns-zone.c +++ /dev/null @@ -1,664 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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 - -void dns_zone_item_probe_stop(DnsZoneItem *i) { - DnsTransaction *t; - assert(i); - - if (!i->probe_transaction) - return; - - t = i->probe_transaction; - i->probe_transaction = NULL; - - set_remove(t->notify_zone_items, i); - set_remove(t->notify_zone_items_done, i); - dns_transaction_gc(t); -} - -static void dns_zone_item_free(DnsZoneItem *i) { - if (!i) - return; - - dns_zone_item_probe_stop(i); - dns_resource_record_unref(i->rr); - - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(DnsZoneItem*, dns_zone_item_free); - -static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { - DnsZoneItem *first; - - assert(z); - - if (!i) - return; - - first = hashmap_get(z->by_key, i->rr->key); - LIST_REMOVE(by_key, first, i); - if (first) - assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); - else - hashmap_remove(z->by_key, i->rr->key); - - first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); - LIST_REMOVE(by_name, first, i); - if (first) - assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); - else - hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key)); - - dns_zone_item_free(i); -} - -void dns_zone_flush(DnsZone *z) { - DnsZoneItem *i; - - assert(z); - - while ((i = hashmap_first(z->by_key))) - dns_zone_item_remove_and_free(z, i); - - assert(hashmap_size(z->by_key) == 0); - assert(hashmap_size(z->by_name) == 0); - - z->by_key = hashmap_free(z->by_key); - z->by_name = hashmap_free(z->by_name); -} - -static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { - DnsZoneItem *i; - - assert(z); - assert(rr); - - LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key)) - if (dns_resource_record_equal(i->rr, rr) > 0) - return i; - - return NULL; -} - -void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr) { - DnsZoneItem *i; - - assert(z); - assert(rr); - - i = dns_zone_get(z, rr); - if (i) - dns_zone_item_remove_and_free(z, i); -} - -static int dns_zone_init(DnsZone *z) { - int r; - - assert(z); - - r = hashmap_ensure_allocated(&z->by_key, &dns_resource_key_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&z->by_name, &dns_name_hash_ops); - if (r < 0) - return r; - - return 0; -} - -static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { - DnsZoneItem *first; - int r; - - first = hashmap_get(z->by_key, i->rr->key); - if (first) { - LIST_PREPEND(by_key, first, i); - assert_se(hashmap_replace(z->by_key, first->rr->key, first) >= 0); - } else { - r = hashmap_put(z->by_key, i->rr->key, i); - if (r < 0) - return r; - } - - first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); - if (first) { - LIST_PREPEND(by_name, first, i); - assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); - } else { - r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i); - if (r < 0) - return r; - } - - return 0; -} - -static int dns_zone_item_probe_start(DnsZoneItem *i) { - DnsTransaction *t; - int r; - - assert(i); - - if (i->probe_transaction) - return 0; - - 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->notify_zone_items, NULL); - if (r < 0) - goto gc; - - r = set_ensure_allocated(&t->notify_zone_items_done, NULL); - if (r < 0) - goto gc; - - r = set_put(t->notify_zone_items, i); - if (r < 0) - goto gc; - - i->probe_transaction = t; - - if (t->state == DNS_TRANSACTION_NULL) { - - i->block_ready++; - r = dns_transaction_go(t); - i->block_ready--; - - if (r < 0) { - dns_zone_item_probe_stop(i); - return r; - } - } - - dns_zone_item_notify(i); - return 0; - -gc: - dns_transaction_gc(t); - return r; -} - -int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { - _cleanup_(dns_zone_item_freep) DnsZoneItem *i = NULL; - DnsZoneItem *existing; - int r; - - assert(z); - assert(s); - assert(rr); - - if (dns_class_is_pseudo(rr->key->class)) - return -EINVAL; - if (dns_type_is_pseudo(rr->key->type)) - return -EINVAL; - - existing = dns_zone_get(z, rr); - if (existing) - return 0; - - r = dns_zone_init(z); - if (r < 0) - return r; - - i = new0(DnsZoneItem, 1); - if (!i) - return -ENOMEM; - - i->scope = s; - i->rr = dns_resource_record_ref(rr); - i->probing_enabled = probe; - - r = dns_zone_link_item(z, i); - if (r < 0) - return r; - - if (probe) { - DnsZoneItem *first, *j; - bool established = false; - - /* Check if there's already an RR with the same name - * established. If so, it has been probed already, and - * we don't ned to probe again. */ - - LIST_FIND_HEAD(by_name, i, first); - LIST_FOREACH(by_name, j, first) { - if (i == j) - continue; - - if (j->state == DNS_ZONE_ITEM_ESTABLISHED) - established = true; - } - - if (established) - i->state = DNS_ZONE_ITEM_ESTABLISHED; - else { - i->state = DNS_ZONE_ITEM_PROBING; - - r = dns_zone_item_probe_start(i); - if (r < 0) { - dns_zone_item_remove_and_free(z, i); - i = NULL; - return r; - } - } - } else - i->state = DNS_ZONE_ITEM_ESTABLISHED; - - i = NULL; - return 0; -} - -int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - unsigned n_answer = 0; - DnsZoneItem *j, *first; - bool tentative = true, need_soa = false; - int r; - - /* Note that we don't actually need the ifindex for anything. However when it is passed we'll initialize the - * ifindex field in the answer with it */ - - assert(z); - assert(key); - assert(ret_answer); - - /* First iteration, count what we have */ - - if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; - - /* If this is a generic match, then we have to - * go through the list by the name and look - * for everything manually */ - - 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; - - k = dns_resource_key_match_rr(key, j->rr, NULL); - if (k < 0) - return k; - if (k > 0) { - n_answer++; - added = true; - } - - } - - if (found && !added) - need_soa = true; - - } else { - bool found = false; - - /* If this is a specific match, then look for - * the right key immediately */ - - 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; - - found = true; - n_answer++; - } - - 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; - - need_soa = true; - break; - } - } - } - - if (n_answer <= 0 && !need_soa) - goto return_empty; - - if (n_answer > 0) { - answer = dns_answer_new(n_answer); - if (!answer) - return -ENOMEM; - } - - if (need_soa) { - soa = dns_answer_new(1); - if (!soa) - return -ENOMEM; - } - - /* Second iteration, actually add the RRs to the answers */ - if (key->type == DNS_TYPE_ANY || key->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; - - found = true; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - 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, ifindex, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - - added = true; - } - } - - if (found && !added) { - r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex); - if (r < 0) - return r; - } - } 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; - - found = true; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - r = dns_answer_add(answer, j->rr, ifindex, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - if (!found) { - bool add_soa = 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; - - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; - - add_soa = true; - } - - if (add_soa) { - r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL, ifindex); - 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; - - 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) { - assert(i); - - if (!IN_SET(i->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_VERIFYING, DNS_ZONE_ITEM_ESTABLISHED)) - return; - - log_info("Detected conflict on %s", strna(dns_resource_record_to_string(i->rr))); - - dns_zone_item_probe_stop(i); - - /* Withdraw the conflict item */ - i->state = DNS_ZONE_ITEM_WITHDRAWN; - - /* Maybe change the hostname */ - if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0) - manager_next_hostname(i->scope->manager); -} - -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, DNS_TRANSACTION_VALIDATING)) - return; - - if (i->probe_transaction->state == DNS_TRANSACTION_SUCCESS) { - bool we_lost = false; - - /* The probe got a successful reply. If we so far - * weren't established we just give up. If we already - * were established, and the peer has the - * lexicographically larger IP address we continue - * and defend it. */ - - if (!IN_SET(i->state, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) { - log_debug("Got a successful probe for not yet established RR, we lost."); - we_lost = true; - } else { - assert(i->probe_transaction->received); - we_lost = memcmp(&i->probe_transaction->received->sender, &i->probe_transaction->received->destination, FAMILY_ADDRESS_SIZE(i->probe_transaction->received->family)) < 0; - if (we_lost) - log_debug("Got a successful probe reply for an established RR, and we have a lexicographically larger IP address and thus lost."); - } - - if (we_lost) { - dns_zone_item_conflict(i); - return; - } - - log_debug("Got a successful probe reply, but peer has lexicographically lower IP address and thus lost."); - } - - 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) { - int r; - - assert(i); - - if (i->state != DNS_ZONE_ITEM_ESTABLISHED) - return 0; - - 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); - if (r < 0) { - log_error_errno(r, "Failed to start probing for verifying RR: %m"); - i->state = DNS_ZONE_ITEM_ESTABLISHED; - return r; - } - - return 0; -} - -int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { - DnsZoneItem *i, *first; - int c = 0; - - assert(zone); - assert(rr); - - /* This checks whether a response RR we received from somebody - * else is one that we actually thought was uniquely ours. If - * so, we'll verify our RRs. */ - - /* No conflict if we don't have the name at all. */ - first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key)); - if (!first) - return 0; - - /* No conflict if we have the exact same RR */ - if (dns_zone_get(zone, rr)) - return 0; - - /* OK, somebody else has RRs for the same name. Yuck! Let's - * start probing again */ - - LIST_FOREACH(by_name, i, first) { - if (dns_resource_record_equal(i->rr, rr)) - continue; - - dns_zone_item_verify(i); - c++; - } - - return c; -} - -int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { - DnsZoneItem *i, *first; - int c = 0; - - assert(zone); - - /* Somebody else notified us about a possible conflict. Let's - * verify if that's true. */ - - first = hashmap_get(zone->by_name, dns_resource_key_name(key)); - if (!first) - return 0; - - LIST_FOREACH(by_name, i, first) { - dns_zone_item_verify(i); - c++; - } - - return c; -} - -void dns_zone_verify_all(DnsZone *zone) { - DnsZoneItem *i; - Iterator iterator; - - assert(zone); - - HASHMAP_FOREACH(i, zone->by_key, iterator) { - DnsZoneItem *j; - - LIST_FOREACH(by_key, j, i) - dns_zone_item_verify(j); - } -} - -void dns_zone_dump(DnsZone *zone, FILE *f) { - Iterator iterator; - DnsZoneItem *i; - - if (!zone) - return; - - if (!f) - f = stdout; - - HASHMAP_FOREACH(i, zone->by_key, iterator) { - DnsZoneItem *j; - - LIST_FOREACH(by_key, j, i) { - const char *t; - - t = dns_resource_record_to_string(j->rr); - if (!t) { - log_oom(); - continue; - } - - fputc('\t', f); - fputs(t, f); - fputc('\n', f); - } - } -} - -bool dns_zone_is_empty(DnsZone *zone) { - if (!zone) - return true; - - return hashmap_isempty(zone->by_key); -} diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h deleted file mode 100644 index a41df37e6b..0000000000 --- a/src/resolve/resolved-dns-zone.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "hashmap.h" - -typedef struct DnsZone { - Hashmap *by_key; - Hashmap *by_name; -} DnsZone; - -typedef struct DnsZoneItem DnsZoneItem; -typedef enum DnsZoneItemState DnsZoneItemState; - -#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 */ -#define LLMNR_DEFAULT_TTL (30) - -enum DnsZoneItemState { - DNS_ZONE_ITEM_PROBING, - DNS_ZONE_ITEM_ESTABLISHED, - DNS_ZONE_ITEM_VERIFYING, - DNS_ZONE_ITEM_WITHDRAWN, -}; - -struct DnsZoneItem { - DnsScope *scope; - DnsResourceRecord *rr; - - DnsZoneItemState state; - - unsigned block_ready; - - bool probing_enabled; - - LIST_FIELDS(DnsZoneItem, by_key); - LIST_FIELDS(DnsZoneItem, by_name); - - DnsTransaction *probe_transaction; -}; - -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, DnsResourceKey *key, int ifindex, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); - -void dns_zone_item_conflict(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); - -void dns_zone_verify_all(DnsZone *zone); - -void dns_zone_item_probe_stop(DnsZoneItem *i); - -void dns_zone_dump(DnsZone *zone, FILE *f); -bool dns_zone_is_empty(DnsZone *zone); diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c deleted file mode 100644 index 40d650949d..0000000000 --- a/src/resolve/resolved-etc-hosts.c +++ /dev/null @@ -1,448 +0,0 @@ -/*** - 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 . -***/ - -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "resolved-etc-hosts.h" -#include "resolved-dns-synthesize.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -/* Recheck /etc/hosts at most once every 2s */ -#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC) - -typedef struct EtcHostsItem { - int family; - union in_addr_union address; - - char **names; -} EtcHostsItem; - -typedef struct EtcHostsItemByName { - char *name; - - EtcHostsItem **items; - size_t n_items, n_allocated; -} EtcHostsItemByName; - -void manager_etc_hosts_flush(Manager *m) { - EtcHostsItem *item; - EtcHostsItemByName *bn; - - while ((item = set_steal_first(m->etc_hosts_by_address))) { - strv_free(item->names); - free(item); - } - - while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) { - free(bn->name); - free(bn->items); - free(bn); - } - - m->etc_hosts_by_address = set_free(m->etc_hosts_by_address); - m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name); - - m->etc_hosts_mtime = USEC_INFINITY; -} - -static void etc_hosts_item_hash_func(const void *p, struct siphash *state) { - const EtcHostsItem *item = p; - - siphash24_compress(&item->family, sizeof(item->family), state); - - if (item->family == AF_INET) - siphash24_compress(&item->address.in, sizeof(item->address.in), state); - else if (item->family == AF_INET6) - siphash24_compress(&item->address.in6, sizeof(item->address.in6), state); -} - -static int etc_hosts_item_compare_func(const void *a, const void *b) { - const EtcHostsItem *x = a, *y = b; - - if (x->family != y->family) - return x->family - y->family; - - if (x->family == AF_INET) - return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr)); - - if (x->family == AF_INET6) - return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr)); - - return trivial_compare_func(a, b); -} - -static const struct hash_ops etc_hosts_item_ops = { - .hash = etc_hosts_item_hash_func, - .compare = etc_hosts_item_compare_func, -}; - -static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) { - - EtcHostsItem key = { - .family = family, - .address = *address, - }; - EtcHostsItem *item; - char **n; - int r; - - assert(m); - assert(address); - - r = in_addr_is_null(family, address); - if (r < 0) - return r; - if (r > 0) - /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to - * nothing. */ - item = NULL; - else { - /* If this is a normal address, then, simply add entry mapping it to the specified names */ - - item = set_get(m->etc_hosts_by_address, &key); - if (item) { - r = strv_extend_strv(&item->names, names, true); - if (r < 0) - return log_oom(); - } else { - - r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops); - if (r < 0) - return log_oom(); - - item = new0(EtcHostsItem, 1); - if (!item) - return log_oom(); - - item->family = family; - item->address = *address; - item->names = names; - - r = set_put(m->etc_hosts_by_address, item); - if (r < 0) { - free(item); - return log_oom(); - } - } - } - - STRV_FOREACH(n, names) { - EtcHostsItemByName *bn; - - bn = hashmap_get(m->etc_hosts_by_name, *n); - if (!bn) { - r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops); - if (r < 0) - return log_oom(); - - bn = new0(EtcHostsItemByName, 1); - if (!bn) - return log_oom(); - - bn->name = strdup(*n); - if (!bn->name) { - free(bn); - return log_oom(); - } - - r = hashmap_put(m->etc_hosts_by_name, bn->name, bn); - if (r < 0) { - free(bn->name); - free(bn); - return log_oom(); - } - } - - if (item) { - if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1)) - return log_oom(); - - bn->items[bn->n_items++] = item; - } - } - - return 0; -} - -static int parse_line(Manager *m, unsigned nr, const char *line) { - _cleanup_free_ char *address = NULL; - _cleanup_strv_free_ char **names = NULL; - union in_addr_union in; - bool suppressed = false; - int family, r; - - assert(m); - assert(line); - - r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX); - if (r < 0) - return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr); - if (r == 0) { - log_error("Premature end of line, in line /etc/hosts:%u.", nr); - return -EINVAL; - } - - r = in_addr_from_string_auto(address, &family, &in); - if (r < 0) - return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr); - - for (;;) { - _cleanup_free_ char *name = NULL; - - r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX); - if (r < 0) - return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr); - if (r == 0) - break; - - r = dns_name_is_valid(name); - if (r <= 0) - return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr); - - if (is_localhost(name)) { - /* Suppress the "localhost" line that is often seen */ - suppressed = true; - continue; - } - - r = strv_push(&names, name); - if (r < 0) - return log_oom(); - - name = NULL; - } - - if (strv_isempty(names)) { - - if (suppressed) - return 0; - - log_error("Line is missing any host names, in line /etc/hosts:%u.", nr); - return -EINVAL; - } - - /* Takes possession of the names strv */ - r = add_item(m, family, &in, names); - if (r < 0) - return r; - - names = NULL; - return r; -} - -int manager_etc_hosts_read(Manager *m) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - struct stat st; - usec_t ts; - unsigned nr = 0; - int r; - - assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0); - - /* See if we checked /etc/hosts recently already */ - if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts) - return 0; - - m->etc_hosts_last = ts; - - if (m->etc_hosts_mtime != USEC_INFINITY) { - if (stat("/etc/hosts", &st) < 0) { - if (errno == ENOENT) { - r = 0; - goto clear; - } - - return log_error_errno(errno, "Failed to stat /etc/hosts: %m"); - } - - /* Did the mtime change? If not, there's no point in re-reading the file. */ - if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime) - return 0; - } - - f = fopen("/etc/hosts", "re"); - if (!f) { - if (errno == ENOENT) { - r = 0; - goto clear; - } - - return log_error_errno(errno, "Failed to open /etc/hosts: %m"); - } - - /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next - * invocation */ - r = fstat(fileno(f), &st); - if (r < 0) - return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m"); - - manager_etc_hosts_flush(m); - - FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) { - char *l; - - nr++; - - l = strstrip(line); - if (isempty(l)) - continue; - if (l[0] == '#') - continue; - - r = parse_line(m, nr, l); - if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */ - goto clear; - } - - m->etc_hosts_mtime = timespec_load(&st.st_mtim); - m->etc_hosts_last = ts; - - return 1; - -clear: - manager_etc_hosts_flush(m); - return r; -} - -int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { - bool found_a = false, found_aaaa = false; - EtcHostsItemByName *bn; - EtcHostsItem k = {}; - DnsResourceKey *t; - const char *name; - unsigned i; - int r; - - assert(m); - assert(q); - assert(answer); - - r = manager_etc_hosts_read(m); - if (r < 0) - return r; - - name = dns_question_first_name(q); - if (!name) - return 0; - - r = dns_name_address(name, &k.family, &k.address); - if (r > 0) { - EtcHostsItem *item; - DnsResourceKey *found_ptr = NULL; - - item = set_get(m->etc_hosts_by_address, &k); - if (!item) - return 0; - - /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data - * we'll only return if the request was for PTR. */ - - DNS_QUESTION_FOREACH(t, q) { - if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) - continue; - if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) - continue; - - r = dns_name_equal(dns_resource_key_name(t), name); - if (r < 0) - return r; - if (r > 0) { - found_ptr = t; - break; - } - } - - if (found_ptr) { - char **n; - - r = dns_answer_reserve(answer, strv_length(item->names)); - if (r < 0) - return r; - - STRV_FOREACH(n, item->names) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - rr = dns_resource_record_new(found_ptr); - if (!rr) - return -ENOMEM; - - rr->ptr.name = strdup(*n); - if (!rr->ptr.name) - return -ENOMEM; - - r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - } - - return 1; - } - - bn = hashmap_get(m->etc_hosts_by_name, name); - if (!bn) - return 0; - - r = dns_answer_reserve(answer, bn->n_items); - if (r < 0) - return r; - - DNS_QUESTION_FOREACH(t, q) { - if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY)) - continue; - if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) - continue; - - r = dns_name_equal(dns_resource_key_name(t), name); - if (r < 0) - return r; - if (r == 0) - continue; - - if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY)) - found_a = true; - if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) - found_aaaa = true; - - if (found_a && found_aaaa) - break; - } - - for (i = 0; i < bn->n_items; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - - if ((found_a && bn->items[i]->family != AF_INET) && - (found_aaaa && bn->items[i]->family != AF_INET6)) - continue; - - r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name); - if (r < 0) - return r; - - r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; - } - - return 1; -} diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h deleted file mode 100644 index 9d5a175f18..0000000000 --- a/src/resolve/resolved-etc-hosts.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 . -***/ - -#include "resolved-manager.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" - -void manager_etc_hosts_flush(Manager *m); -int manager_etc_hosts_read(Manager *m); -int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf deleted file mode 100644 index 2fd56bce26..0000000000 --- a/src/resolve/resolved-gperf.gperf +++ /dev/null @@ -1,22 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "resolved-conf.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name resolved_gperf_hash -%define lookup-function-name resolved_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -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) -Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c deleted file mode 100644 index 364812250f..0000000000 --- a/src/resolve/resolved-link-bus.c +++ /dev/null @@ -1,629 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "parse-util.h" -#include "resolve-util.h" -#include "resolved-bus.h" -#include "resolved-link-bus.h" -#include "resolved-resolv-conf.h" -#include "strv.h" - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_resolve_support, resolve_support, ResolveSupport); - -static int property_get_dnssec_mode( - 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, "s", dnssec_mode_to_string(link_get_dnssec_mode(l))); -} - -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', "(sb)"); - if (r < 0) - return r; - - LIST_FOREACH(domains, d, l->search_domains) { - r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only); - 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)); -} - -static int verify_unmanaged_link(Link *l, sd_bus_error *error) { - assert(l); - - 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); - - return 0; -} - -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 = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - 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"); - - if (!dns_server_address_valid(family, d)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address"); - - 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, 0); - 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, 0); - if (r < 0) - goto clear; - } - - } - - dns_server_unlink_marked(l->dns_servers); - link_allocate_scopes(l); - - (void) link_save_user(l); - (void) manager_write_resolv_conf(l->manager); - - return sd_bus_reply_method_return(message, NULL); - -clear: - dns_server_unlink_all(l->dns_servers); - return r; -} - -int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Link *l = userdata; - int r; - - assert(message); - assert(l); - - r = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(message, 'a', "(sb)"); - if (r < 0) - return r; - - for (;;) { - const char *name; - int route_only; - - r = sd_bus_message_read(message, "(sb)", &name, &route_only); - if (r < 0) - return r; - if (r == 0) - break; - - 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 search domain %s", name); - if (!route_only && dns_name_is_root(name)) - 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); - - r = sd_bus_message_rewind(message, false); - if (r < 0) - return r; - - for (;;) { - DnsSearchDomain *d; - const char *name; - int route_only; - - r = sd_bus_message_read(message, "(sb)", &name, &route_only); - if (r < 0) - goto clear; - if (r == 0) - break; - - r = dns_search_domain_find(l->search_domains, name, &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, &d, DNS_SEARCH_DOMAIN_LINK, l, name); - if (r < 0) - goto clear; - } - - d->route_only = route_only; - } - - r = sd_bus_message_exit_container(message); - if (r < 0) - goto clear; - - dns_search_domain_unlink_marked(l->search_domains); - - (void) link_save_user(l); - (void) manager_write_resolv_conf(l->manager); - - 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 = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - 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); - - (void) link_save_user(l); - - 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 = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - 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); - - (void) link_save_user(l); - - 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 = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - 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); - - (void) link_save_user(l); - - 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 = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - 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 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; - - (void) link_save_user(l); - - 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; - int r; - - assert(message); - assert(l); - - r = verify_unmanaged_link(l, error); - if (r < 0) - return r; - - link_flush_settings(l); - link_allocate_scopes(l); - link_add_rrs(l, false); - - (void) link_save_user(l); - (void) manager_write_resolv_conf(l->manager); - - 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", "a(sb)", 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, 0, 0), - SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), - SD_BUS_PROPERTY("DNSSECSupported", "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", "a(sb)", NULL, bus_link_method_set_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 deleted file mode 100644 index 646031b631..0000000000 --- a/src/resolve/resolved-link-bus.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 . -***/ - -#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_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 deleted file mode 100644 index ea4a007139..0000000000 --- a/src/resolve/resolved-link.c +++ /dev/null @@ -1,1115 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-network.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "missing.h" -#include "mkdir.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; - int r; - - assert(m); - assert(ifindex > 0); - - r = hashmap_ensure_allocated(&m->links, NULL); - if (r < 0) - return r; - - l = new0(Link, 1); - if (!l) - return -ENOMEM; - - l->ifindex = ifindex; - l->llmnr_support = RESOLVE_SUPPORT_YES; - l->mdns_support = RESOLVE_SUPPORT_NO; - l->dnssec_mode = _DNSSEC_MODE_INVALID; - l->operstate = IF_OPER_UNKNOWN; - - if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0) - return -ENOMEM; - - r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); - if (r < 0) - return r; - - l->manager = m; - - if (ret) - *ret = l; - l = NULL; - - 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) - (void) link_address_free(l->addresses); - - if (l->manager) - hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - - 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->state_file); - - free(l); - return NULL; -} - -void link_allocate_scopes(Link *l) { - int r; - - assert(l); - - 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) - log_warning_errno(r, "Failed to allocate DNS scope: %m"); - } - } else - l->unicast_scope = dns_scope_free(l->unicast_scope); - - 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) - log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m"); - } - } else - l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); - - 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); - if (r < 0) - log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m"); - } - } 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) { - LinkAddress *a; - - LIST_FOREACH(addresses, a, l->addresses) - link_address_add_rrs(a, force_remove); -} - -int link_process_rtnl(Link *l, sd_netlink_message *m) { - const char *n = NULL; - int r; - - assert(l); - assert(m); - - r = sd_rtnl_message_link_get_flags(m, &l->flags); - if (r < 0) - return r; - - (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu); - (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate); - - if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) { - strncpy(l->name, n, sizeof(l->name)-1); - char_array_0(l->name); - } - - link_allocate_scopes(l); - link_add_rrs(l, false); - - return 0; -} - -static int link_update_dns_server_one(Link *l, const char *name) { - union in_addr_union a; - DnsServer *s; - int family, r; - - assert(l); - assert(name); - - r = in_addr_from_string_auto(name, &family, &a); - if (r < 0) - return r; - - s = dns_server_find(l->dns_servers, family, &a, 0); - if (s) { - dns_server_move_back_and_unmark(s); - return 0; - } - - return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0); -} - -static int link_update_dns_servers(Link *l) { - _cleanup_strv_free_ char **nameservers = NULL; - char **nameserver; - 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; - - dns_server_mark_all(l->dns_servers); - - STRV_FOREACH(nameserver, nameservers) { - r = link_update_dns_server_one(l, *nameserver); - if (r < 0) - goto clear; - } - - dns_server_unlink_marked(l->dns_servers); - return 0; - -clear: - 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; - - 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_mdns_support(Link *l) { - _cleanup_free_ char *b = NULL; - int r; - - assert(l); - - r = sd_network_link_get_mdns(l->ifindex, &b); - if (r == -ENODATA) { - r = 0; - goto clear; - } - if (r < 0) - 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; - int r; - - assert(l); - - 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; - - r = set_put_strdupv(ns, ntas); - 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->dnssec_negative_trust_anchors = set_free_free(l->dnssec_negative_trust_anchors); - return r; -} - -static int link_update_search_domain_one(Link *l, const char *name, bool route_only) { - DnsSearchDomain *d; - int r; - - assert(l); - assert(name); - - r = dns_search_domain_find(l->search_domains, name, &d); - if (r < 0) - return r; - if (r > 0) - dns_search_domain_move_back_and_unmark(d); - else { - r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name); - if (r < 0) - return r; - } - - d->route_only = route_only; - return 0; -} - -static int link_update_search_domains(Link *l) { - _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL; - char **i; - int r, q; - - assert(l); - - r = sd_network_link_get_search_domains(l->ifindex, &sdomains); - if (r < 0 && r != -ENODATA) - goto clear; - - q = sd_network_link_get_route_domains(l->ifindex, &rdomains); - if (q < 0 && q != -ENODATA) { - r = q; - goto clear; - } - - if (r == -ENODATA && q == -ENODATA) { - /* networkd knows nothing about this interface, and that's fine. */ - r = 0; - goto clear; - } - - dns_search_domain_mark_all(l->search_domains); - - STRV_FOREACH(i, sdomains) { - r = link_update_search_domain_one(l, *i, false); - if (r < 0) - goto clear; - } - - STRV_FOREACH(i, rdomains) { - r = link_update_search_domain_one(l, *i, true); - 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_managed(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 0; - if (r < 0) - return r; - - 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_managed(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(Link *l) { - assert(l); - - link_read_settings(l); - link_load_user(l); - link_allocate_scopes(l); - link_add_rrs(l, false); - - return 0; -} - -bool link_relevant(Link *l, int family, bool local_multicast) { - _cleanup_free_ char *state = NULL; - LinkAddress *a; - - assert(l); - - /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link - * beat, can do multicast and has at least one link-local (or better) IP address. - * - * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at - * least one routable address.*/ - - if (l->flags & (IFF_LOOPBACK|IFF_DORMANT)) - return false; - - if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP)) - return false; - - if (local_multicast) { - if (l->flags & IFF_POINTOPOINT) - return false; - - if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST) - return false; - } - - /* Check kernel operstate - * https://www.kernel.org/doc/Documentation/networking/operstates.txt */ - if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP)) - return false; - - (void) 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 ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a, local_multicast)) - return true; - - return false; -} - -LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - - assert(l); - - LIST_FOREACH(addresses, a, l->addresses) - if (a->family == family && in_addr_equal(family, &a->in_addr, in_addr)) - return a; - - return NULL; -} - -DnsServer* link_set_dns_server(Link *l, DnsServer *s) { - assert(l); - - if (l->current_dns_server == s) - return s; - - if (s) - log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name); - - 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); - - return s; -} - -DnsServer *link_get_dns_server(Link *l) { - assert(l); - - if (!l->current_dns_server) - link_set_dns_server(l, l->dns_servers); - - return l->current_dns_server; -} - -void link_next_dns_server(Link *l) { - assert(l); - - if (!l->current_dns_server) - return; - - /* 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; - } - - 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; - - assert(l); - assert(in_addr); - - a = new0(LinkAddress, 1); - if (!a) - return -ENOMEM; - - a->family = family; - a->in_addr = *in_addr; - - a->link = l; - LIST_PREPEND(addresses, l->addresses, a); - - if (ret) - *ret = a; - - return 0; -} - -LinkAddress *link_address_free(LinkAddress *a) { - if (!a) - return NULL; - - if (a->link) { - LIST_REMOVE(addresses, a->link->addresses, a); - - if (a->llmnr_address_rr) { - if (a->family == AF_INET && a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); - else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->family == AF_INET && a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); - else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); - } - } - - dns_resource_record_unref(a->llmnr_address_rr); - dns_resource_record_unref(a->llmnr_ptr_rr); - - free(a); - return NULL; -} - -void link_address_add_rrs(LinkAddress *a, bool force_remove) { - int r; - - assert(a); - - if (a->family == AF_INET) { - - if (!force_remove && - link_address_relevant(a, true) && - a->link->llmnr_ipv4_scope && - 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); - if (!a->link->manager->llmnr_host_ipv4_key) { - r = -ENOMEM; - goto fail; - } - } - - if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv4_key); - if (!a->llmnr_address_rr) { - r = -ENOMEM; - goto fail; - } - - a->llmnr_address_rr->a.in_addr = a->in_addr.in; - a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; - } - - if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); - if (r < 0) - goto fail; - - a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; - } - - r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true); - if (r < 0) - log_warning_errno(r, "Failed to add A record to LLMNR zone: %m"); - - r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); - if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); - } else { - if (a->llmnr_address_rr) { - if (a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); - a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->link->llmnr_ipv4_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_ptr_rr); - a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); - } - } - } - - if (a->family == AF_INET6) { - - if (!force_remove && - link_address_relevant(a, true) && - a->link->llmnr_ipv6_scope && - 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); - if (!a->link->manager->llmnr_host_ipv6_key) { - r = -ENOMEM; - goto fail; - } - } - - if (!a->llmnr_address_rr) { - a->llmnr_address_rr = dns_resource_record_new(a->link->manager->llmnr_host_ipv6_key); - if (!a->llmnr_address_rr) { - r = -ENOMEM; - goto fail; - } - - a->llmnr_address_rr->aaaa.in6_addr = a->in_addr.in6; - a->llmnr_address_rr->ttl = LLMNR_DEFAULT_TTL; - } - - if (!a->llmnr_ptr_rr) { - r = dns_resource_record_new_reverse(&a->llmnr_ptr_rr, a->family, &a->in_addr, a->link->manager->llmnr_hostname); - if (r < 0) - goto fail; - - a->llmnr_ptr_rr->ttl = LLMNR_DEFAULT_TTL; - } - - r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true); - if (r < 0) - log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m"); - - r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false); - if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); - } else { - if (a->llmnr_address_rr) { - if (a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_address_rr); - a->llmnr_address_rr = dns_resource_record_unref(a->llmnr_address_rr); - } - - if (a->llmnr_ptr_rr) { - if (a->link->llmnr_ipv6_scope) - dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); - a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); - } - } - } - - return; - -fail: - log_debug_errno(r, "Failed to update address RRs: %m"); -} - -int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) { - int r; - assert(a); - assert(m); - - r = sd_rtnl_message_addr_get_flags(m, &a->flags); - if (r < 0) - return r; - - sd_rtnl_message_addr_get_scope(m, &a->scope); - - link_allocate_scopes(a->link); - link_add_rrs(a->link, false); - - return 0; -} - -bool link_address_relevant(LinkAddress *a, bool local_multicast) { - assert(a); - - if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE)) - return false; - - if (a->scope >= (local_multicast ? RT_SCOPE_HOST : RT_SCOPE_LINK)) - return false; - - return true; -} - -static bool link_needs_save(Link *l) { - assert(l); - - /* Returns true if any of the settings where set different from the default */ - - if (l->is_managed) - return false; - - if (l->llmnr_support != RESOLVE_SUPPORT_YES || - l->mdns_support != RESOLVE_SUPPORT_NO || - l->dnssec_mode != _DNSSEC_MODE_INVALID) - return true; - - if (l->dns_servers || - l->search_domains) - return true; - - if (!set_isempty(l->dnssec_negative_trust_anchors)) - return true; - - return false; -} - -int link_save_user(Link *l) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - const char *v; - int r; - - assert(l); - assert(l->state_file); - - if (!link_needs_save(l)) { - (void) unlink(l->state_file); - return 0; - } - - r = mkdir_parents(l->state_file, 0700); - if (r < 0) - goto fail; - - r = fopen_temporary(l->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - fputs("# This is private data. Do not parse.\n", f); - - v = resolve_support_to_string(l->llmnr_support); - if (v) - fprintf(f, "LLMNR=%s\n", v); - - v = resolve_support_to_string(l->mdns_support); - if (v) - fprintf(f, "MDNS=%s\n", v); - - v = dnssec_mode_to_string(l->dnssec_mode); - if (v) - fprintf(f, "DNSSEC=%s\n", v); - - if (l->dns_servers) { - DnsServer *server; - - fputs("SERVERS=", f); - LIST_FOREACH(servers, server, l->dns_servers) { - - if (server != l->dns_servers) - fputc(' ', f); - - v = dns_server_string(server); - if (!v) { - r = -ENOMEM; - goto fail; - } - - fputs(v, f); - } - fputc('\n', f); - } - - if (l->search_domains) { - DnsSearchDomain *domain; - - fputs("DOMAINS=", f); - LIST_FOREACH(domains, domain, l->search_domains) { - - if (domain != l->search_domains) - fputc(' ', f); - - if (domain->route_only) - fputc('~', f); - - fputs(DNS_SEARCH_DOMAIN_NAME(domain), f); - } - fputc('\n', f); - } - - if (!set_isempty(l->dnssec_negative_trust_anchors)) { - bool space = false; - Iterator i; - char *nta; - - fputs("NTAS=", f); - SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) { - - if (space) - fputc(' ', f); - - fputs(nta, f); - space = true; - } - fputc('\n', f); - } - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, l->state_file) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(l->state_file); - - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save link data %s: %m", l->state_file); -} - -int link_load_user(Link *l) { - _cleanup_free_ char - *llmnr = NULL, - *mdns = NULL, - *dnssec = NULL, - *servers = NULL, - *domains = NULL, - *ntas = NULL; - - ResolveSupport s; - int r; - - assert(l); - assert(l->state_file); - - /* Try to load only a single time */ - if (l->loaded) - return 0; - l->loaded = true; - - if (l->is_managed) - return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */ - - r = parse_env_file(l->state_file, NEWLINE, - "LLMNR", &llmnr, - "MDNS", &mdns, - "DNSSEC", &dnssec, - "SERVERS", &servers, - "DOMAINS", &domains, - "NTAS", &ntas, - NULL); - if (r == -ENOENT) - return 0; - if (r < 0) - goto fail; - - link_flush_settings(l); - - /* If we can't recognize the LLMNR or MDNS setting we don't override the default */ - s = resolve_support_from_string(llmnr); - if (s >= 0) - l->llmnr_support = s; - - s = resolve_support_from_string(mdns); - if (s >= 0) - l->mdns_support = s; - - /* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */ - l->dnssec_mode = dnssec_mode_from_string(dnssec); - - if (servers) { - const char *p = servers; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, 0); - if (r < 0) - goto fail; - if (r == 0) - break; - - r = link_update_dns_server_one(l, word); - if (r < 0) { - log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word); - continue; - } - } - } - - if (domains) { - const char *p = domains; - - for (;;) { - _cleanup_free_ char *word = NULL; - const char *n; - bool is_route; - - r = extract_first_word(&p, &word, NULL, 0); - if (r < 0) - goto fail; - if (r == 0) - break; - - is_route = word[0] == '~'; - n = is_route ? word + 1 : word; - - r = link_update_search_domain_one(l, n, is_route); - if (r < 0) { - log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word); - continue; - } - } - } - - if (ntas) { - _cleanup_set_free_free_ Set *ns = NULL; - - ns = set_new(&dns_name_hash_ops); - if (!ns) { - r = -ENOMEM; - goto fail; - } - - r = set_put_strsplit(ns, ntas, NULL, 0); - if (r < 0) - goto fail; - - l->dnssec_negative_trust_anchors = ns; - ns = NULL; - } - - return 0; - -fail: - return log_error_errno(r, "Failed to load link data %s: %m", l->state_file); -} - -void link_remove_user(Link *l) { - assert(l); - assert(l->state_file); - - (void) unlink(l->state_file); -} diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h deleted file mode 100644 index 6a2343f9f7..0000000000 --- a/src/resolve/resolved-link.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "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; - - int family; - union in_addr_union in_addr; - - unsigned char flags, scope; - - DnsResourceRecord *llmnr_address_rr; - DnsResourceRecord *llmnr_ptr_rr; - - LIST_FIELDS(LinkAddress, addresses); -}; - -struct Link { - Manager *manager; - - int ifindex; - unsigned flags; - - LIST_HEAD(LinkAddress, addresses); - - LIST_HEAD(DnsServer, dns_servers); - DnsServer *current_dns_server; - unsigned n_dns_servers; - - LIST_HEAD(DnsSearchDomain, search_domains); - unsigned n_search_domains; - - 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; - uint8_t operstate; - - bool loaded; - char *state_file; -}; - -int link_new(Manager *m, Link **ret, int ifindex); -Link *link_free(Link *l); -int link_process_rtnl(Link *l, sd_netlink_message *m); -int link_update(Link *l); -bool link_relevant(Link *l, int family, bool local_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_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_save_user(Link *l); -int link_load_user(Link *l); -void link_remove_user(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); -bool link_address_relevant(LinkAddress *l, bool local_multicast); -void link_address_add_rrs(LinkAddress *a, bool force_remove); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c deleted file mode 100644 index 3516af58ee..0000000000 --- a/src/resolve/resolved-llmnr.c +++ /dev/null @@ -1,471 +0,0 @@ -/*** - 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 . - ***/ - -#include -#include - -#include "fd-util.h" -#include "resolved-llmnr.h" -#include "resolved-manager.h" - -void manager_llmnr_stop(Manager *m) { - assert(m); - - m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source); - m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); - - m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source); - m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); - - m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source); - m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); - - m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source); - m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); -} - -int manager_llmnr_start(Manager *m) { - int r; - - assert(m); - - if (m->llmnr_support == RESOLVE_SUPPORT_NO) - return 0; - - r = manager_llmnr_ipv4_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_llmnr_ipv4_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - if (socket_ipv6_is_supported()) { - r = manager_llmnr_ipv6_udp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - - r = manager_llmnr_ipv6_tcp_fd(m); - if (r == -EADDRINUSE) - goto eaddrinuse; - if (r < 0) - return r; - } - - return 0; - -eaddrinuse: - log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support."); - m->llmnr_support = RESOLVE_SUPPORT_NO; - manager_llmnr_stop(m); - - return 0; -} - -static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsTransaction *t = NULL; - Manager *m = userdata; - DnsScope *scope; - int r; - - assert(s); - assert(fd >= 0); - assert(m); - - r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); - if (r <= 0) - return r; - - scope = manager_find_scope(m, p); - if (!scope) - log_warning("Got LLMNR UDP packet on unknown scope. Ignoring."); - else if (dns_packet_validate_reply(p) > 0) { - log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p)); - - dns_scope_check_conflicts(scope, p); - - t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (t) - dns_transaction_process_reply(t, p); - - } else if (dns_packet_validate_query(p) > 0) { - log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p)); - - dns_scope_process_query(scope, NULL, p); - } else - log_debug("Invalid LLMNR UDP packet, ignoring."); - - return 0; -} - -int manager_llmnr_ipv4_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(LLMNR_PORT), - }; - static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255; - int r; - - assert(m); - - if (m->llmnr_ipv4_udp_fd >= 0) - return m->llmnr_ipv4_udp_fd; - - m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv4_udp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_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->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, 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: - m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); - return r; -} - -int manager_llmnr_ipv6_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(LLMNR_PORT), - }; - static const int one = 1, ttl = 255; - int r; - - assert(m); - - if (m->llmnr_ipv6_udp_fd >= 0) - return m->llmnr_ipv6_udp_fd; - - m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv6_udp_fd < 0) - return -errno; - - r = setsockopt(m->llmnr_ipv6_udp_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->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); - if (r < 0) { - r = -errno; - goto fail; - } - - 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) - goto fail; - - (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp"); - - return m->llmnr_ipv6_udp_fd; - -fail: - m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); - return r; -} - -static int on_llmnr_stream_packet(DnsStream *s) { - DnsScope *scope; - - assert(s); - assert(s->read_packet); - - scope = manager_find_scope(s->manager, s->read_packet); - if (!scope) - log_warning("Got LLMNR TCP packet on unknown scope. Ignoring."); - else if (dns_packet_validate_query(s->read_packet) > 0) { - log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(s->read_packet)); - - dns_scope_process_query(scope, s, s->read_packet); - } else - log_debug("Invalid LLMNR TCP packet, ignoring."); - - dns_stream_unref(s); - return 0; -} - -static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - DnsStream *stream; - Manager *m = userdata; - int cfd, r; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd); - if (r < 0) { - safe_close(cfd); - return r; - } - - stream->on_packet = on_llmnr_stream_packet; - return 0; -} - -int manager_llmnr_ipv4_tcp_fd(Manager *m) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(LLMNR_PORT), - }; - static const int one = 1, pmtu = IP_PMTUDISC_DONT; - int r; - - assert(m); - - if (m->llmnr_ipv4_tcp_fd >= 0) - return m->llmnr_ipv4_tcp_fd; - - m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv4_tcp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_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->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, 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: - m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); - return r; -} - -int manager_llmnr_ipv6_tcp_fd(Manager *m) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(LLMNR_PORT), - }; - static const int one = 1; - int r; - - assert(m); - - if (m->llmnr_ipv6_tcp_fd >= 0) - return m->llmnr_ipv6_tcp_fd; - - m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv6_tcp_fd < 0) - return -errno; - - /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN); - if (r < 0) { - r = -errno; - goto fail; - } - - 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) - goto fail; - - (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp"); - - return m->llmnr_ipv6_tcp_fd; - -fail: - m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd); - return r; -} diff --git a/src/resolve/resolved-llmnr.h b/src/resolve/resolved-llmnr.h deleted file mode 100644 index 8133582fa7..0000000000 --- a/src/resolve/resolved-llmnr.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "resolved-manager.h" - -#define LLMNR_PORT 5355 - -int manager_llmnr_ipv4_udp_fd(Manager *m); -int manager_llmnr_ipv6_udp_fd(Manager *m); -int manager_llmnr_ipv4_tcp_fd(Manager *m); -int manager_llmnr_ipv6_tcp_fd(Manager *m); - -void manager_llmnr_stop(Manager *m); -int manager_llmnr_start(Manager *m); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c deleted file mode 100644 index 9bb623c321..0000000000 --- a/src/resolve/resolved-manager.c +++ /dev/null @@ -1,1378 +0,0 @@ -/*** - 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 . - ***/ - -#include -#include -#include - -#include "af-list.h" -#include "alloc-util.h" -#include "dirent-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 "resolved-bus.h" -#include "resolved-conf.h" -#include "resolved-dns-stub.h" -#include "resolved-etc-hosts.h" -#include "resolved-llmnr.h" -#include "resolved-manager.h" -#include "resolved-mdns.h" -#include "resolved-resolv-conf.h" -#include "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "utf8.h" - -#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) - -static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { - Manager *m = userdata; - uint16_t type; - Link *l; - int ifindex, r; - - assert(rtnl); - assert(m); - assert(mm); - - r = sd_netlink_message_get_type(mm, &type); - if (r < 0) - goto fail; - - r = sd_rtnl_message_link_get_ifindex(mm, &ifindex); - if (r < 0) - goto fail; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - - switch (type) { - - case RTM_NEWLINK:{ - bool is_new = !l; - - if (!l) { - r = link_new(m, &l, ifindex); - if (r < 0) - goto fail; - } - - r = link_process_rtnl(l, mm); - if (r < 0) - goto fail; - - r = link_update(l); - if (r < 0) - goto fail; - - if (is_new) - log_debug("Found new link %i/%s", ifindex, l->name); - - break; - } - - case RTM_DELLINK: - if (l) { - log_debug("Removing link %i/%s", l->ifindex, l->name); - link_remove_user(l); - link_free(l); - } - - break; - } - - return 0; - -fail: - log_warning_errno(r, "Failed to process RTNL link message: %m"); - return 0; -} - -static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { - Manager *m = userdata; - union in_addr_union address; - uint16_t type; - int r, ifindex, family; - LinkAddress *a; - Link *l; - - assert(rtnl); - assert(mm); - assert(m); - - r = sd_netlink_message_get_type(mm, &type); - if (r < 0) - goto fail; - - r = sd_rtnl_message_addr_get_ifindex(mm, &ifindex); - if (r < 0) - goto fail; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - if (!l) - return 0; - - r = sd_rtnl_message_addr_get_family(mm, &family); - if (r < 0) - goto fail; - - switch (family) { - - case AF_INET: - r = sd_netlink_message_read_in_addr(mm, IFA_LOCAL, &address.in); - if (r < 0) { - r = sd_netlink_message_read_in_addr(mm, IFA_ADDRESS, &address.in); - if (r < 0) - goto fail; - } - - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(mm, IFA_LOCAL, &address.in6); - if (r < 0) { - r = sd_netlink_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6); - if (r < 0) - goto fail; - } - - break; - - default: - return 0; - } - - a = link_find_address(l, family, &address); - - switch (type) { - - case RTM_NEWADDR: - - if (!a) { - r = link_address_new(l, &a, family, &address); - if (r < 0) - return r; - } - - r = link_address_update_rtnl(a, mm); - if (r < 0) - return r; - - break; - - case RTM_DELADDR: - link_address_free(a); - break; - } - - return 0; - -fail: - log_warning_errno(r, "Failed to process RTNL address message: %m"); - return 0; -} - -static int manager_rtnl_listen(Manager *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - sd_netlink_message *i; - int r; - - assert(m); - - /* First, subscribe to interfaces coming and going */ - r = sd_netlink_open(&m->rtnl); - if (r < 0) - return r; - - r = sd_netlink_attach_event(m->rtnl, m->event, SD_EVENT_PRIORITY_IMPORTANT); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m); - if (r < 0) - return r; - - r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m); - if (r < 0) - return r; - - /* Then, enumerate all links */ - r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (i = reply; i; i = sd_netlink_message_next(i)) { - r = manager_process_link(m->rtnl, i, m); - if (r < 0) - return r; - } - - req = sd_netlink_message_unref(req); - reply = sd_netlink_message_unref(reply); - - /* Finally, enumerate all addresses, too */ - r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC); - if (r < 0) - return r; - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return r; - - r = sd_netlink_call(m->rtnl, req, 0, &reply); - if (r < 0) - return r; - - for (i = reply; i; i = sd_netlink_message_next(i)) { - r = manager_process_address(m->rtnl, i, m); - if (r < 0) - return r; - } - - return r; -} - -static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - Iterator i; - Link *l; - int r; - - assert(m); - - sd_network_monitor_flush(m->network_monitor); - - HASHMAP_FOREACH(l, m->links, i) { - r = link_update(l); - if (r < 0) - log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); - } - - (void) manager_write_resolv_conf(m); - - return 0; -} - -static int manager_network_monitor_listen(Manager *m) { - int r, fd, events; - - assert(m); - - r = sd_network_monitor_new(&m->network_monitor, NULL); - if (r < 0) - return r; - - fd = sd_network_monitor_get_fd(m->network_monitor); - if (fd < 0) - return fd; - - events = sd_network_monitor_get_events(m->network_monitor); - if (events < 0) - return events; - - r = sd_event_add_io(m->event, &m->network_event_source, fd, events, &on_network_event, m); - if (r < 0) - return r; - - r = sd_event_source_set_priority(m->network_event_source, SD_EVENT_PRIORITY_IMPORTANT+5); - if (r < 0) - return r; - - (void) sd_event_source_set_description(m->network_event_source, "network-monitor"); - - return 0; -} - -static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { - _cleanup_free_ char *h = NULL, *n = NULL; - char label[DNS_LABEL_MAX]; - const char *p; - int r, k; - - assert(llmnr_hostname); - assert(mdns_hostname); - - /* Extract and normalize the first label of the locally - * configured hostname, and check it's not "localhost". */ - - h = gethostname_malloc(); - if (!h) - return log_oom(); - - p = h; - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - return log_error_errno(r, "Failed to unescape host name: %m"); - if (r == 0) { - log_error("Couldn't find a single label in hosntame."); - return -EINVAL; - } - - k = dns_label_undo_idna(label, r, label, sizeof(label)); - if (k < 0) - return log_error_errno(k, "Failed to undo IDNA: %m"); - if (k > 0) - r = k; - - if (!utf8_is_valid(label)) { - log_error("System hostname is not UTF-8 clean."); - return -EINVAL; - } - - r = dns_label_escape_new(label, r, &n); - if (r < 0) - return log_error_errno(r, "Failed to escape host name: %m"); - - if (is_localhost(n)) { - log_debug("System hostname is 'localhost', ignoring."); - return -EINVAL; - } - - r = dns_name_concat(n, "local", mdns_hostname); - if (r < 0) - return log_error_errno(r, "Failed to determine mDNS hostname: %m"); - - *llmnr_hostname = n; - n = NULL; - - return 0; -} - -static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; - Manager *m = userdata; - int r; - - assert(m); - - r = determine_hostname(&llmnr_hostname, &mdns_hostname); - if (r < 0) - return 0; /* ignore invalid hostnames */ - - if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) - return 0; - - log_info("System hostname changed to '%s'.", llmnr_hostname); - - free(m->llmnr_hostname); - free(m->mdns_hostname); - - m->llmnr_hostname = llmnr_hostname; - m->mdns_hostname = mdns_hostname; - - llmnr_hostname = mdns_hostname = NULL; - - manager_refresh_rrs(m); - - return 0; -} - -static int manager_watch_hostname(Manager *m) { - int r; - - assert(m); - - m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); - if (m->hostname_fd < 0) { - log_warning_errno(errno, "Failed to watch hostname: %m"); - return 0; - } - - r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m); - if (r < 0) { - if (r == -EPERM) - /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */ - m->hostname_fd = safe_close(m->hostname_fd); - else - 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 'gnu-linux'."); - m->llmnr_hostname = strdup("gnu-linux"); - if (!m->llmnr_hostname) - return log_oom(); - - m->mdns_hostname = strdup("gnu-linux.local"); - if (!m->mdns_hostname) - return log_oom(); - } else - log_info("Using system hostname '%s'.", m->llmnr_hostname); - - return 0; -} - -static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - _cleanup_free_ char *buffer = NULL; - _cleanup_fclose_ FILE *f = NULL; - Manager *m = userdata; - size_t size = 0; - DnsScope *scope; - - assert(s); - assert(si); - assert(m); - - f = open_memstream(&buffer, &size); - if (!f) - return log_oom(); - - LIST_FOREACH(scopes, scope, m->dns_scopes) - dns_scope_dump(scope, f); - - if (fflush_and_check(f) < 0) - return log_oom(); - - log_dump(LOG_INFO, buffer); - return 0; -} - -static int manager_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(si); - assert(m); - - manager_flush_caches(m); - - return 0; -} - -int manager_new(Manager **ret) { - _cleanup_(manager_freep) Manager *m = NULL; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - 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->dns_stub_udp_fd = m->dns_stub_tcp_fd = -1; - m->hostname_fd = -1; - - m->llmnr_support = RESOLVE_SUPPORT_YES; - m->mdns_support = RESOLVE_SUPPORT_NO; - m->dnssec_mode = DEFAULT_DNSSEC_MODE; - m->enable_cache = true; - m->read_resolv_conf = true; - m->need_builtin_fallbacks = true; - m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY; - - r = dns_trust_anchor_load(&m->trust_anchor); - if (r < 0) - return r; - - r = manager_parse_config_file(m); - if (r < 0) - return r; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); - sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); - - sd_event_set_watchdog(m->event, true); - - r = manager_watch_hostname(m); - if (r < 0) - return r; - - r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC); - if (r < 0) - return r; - - r = manager_network_monitor_listen(m); - if (r < 0) - return r; - - r = manager_rtnl_listen(m); - if (r < 0) - return r; - - r = manager_connect_bus(m); - if (r < 0) - return r; - - (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); - (void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m); - - manager_cleanup_saved_user(m); - - *ret = m; - m = NULL; - - return 0; -} - -int manager_start(Manager *m) { - int r; - - assert(m); - - r = manager_dns_stub_start(m); - if (r < 0) - return r; - - r = manager_llmnr_start(m); - if (r < 0) - return r; - - r = manager_mdns_start(m); - if (r < 0) - return r; - - return 0; -} - -Manager *manager_free(Manager *m) { - Link *l; - - 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); - - dns_scope_free(m->unicast_scope); - - /* At this point only orphaned streams should remain. All others should have been freed already by their - * owners */ - while (m->dns_streams) - dns_stream_unref(m->dns_streams); - - hashmap_free(m->links); - hashmap_free(m->dns_transactions); - - 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); - manager_dns_stub_stop(m); - - sd_bus_slot_unref(m->prepare_for_sleep_slot); - sd_event_source_unref(m->bus_retry_event_source); - sd_bus_unref(m->bus); - - sd_event_source_unref(m->sigusr1_event_source); - sd_event_source_unref(m->sigusr2_event_source); - - sd_event_unref(m->event); - - dns_resource_key_unref(m->llmnr_host_ipv4_key); - dns_resource_key_unref(m->llmnr_host_ipv6_key); - - sd_event_source_unref(m->hostname_event_source); - safe_close(m->hostname_fd); - free(m->llmnr_hostname); - free(m->mdns_hostname); - - dns_trust_anchor_flush(&m->trust_anchor); - manager_etc_hosts_flush(m); - - free(m); - - return NULL; -} - -int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) - + CMSG_SPACE(int) /* ttl/hoplimit */ - + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */]; - } control; - union sockaddr_union sa; - struct msghdr mh = {}; - struct cmsghdr *cmsg; - struct iovec iov; - ssize_t ms, l; - int r; - - assert(m); - assert(fd >= 0); - assert(ret); - - ms = next_datagram_size_fd(fd); - if (ms < 0) - return ms; - - r = dns_packet_new(&p, protocol, ms); - if (r < 0) - return r; - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->allocated; - - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa); - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - l = recvmsg(fd, &mh, 0); - if (l == 0) - return 0; - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - assert(!(mh.msg_flags & MSG_CTRUNC)); - assert(!(mh.msg_flags & MSG_TRUNC)); - - p->size = (size_t) l; - - p->family = sa.sa.sa_family; - p->ipproto = IPPROTO_UDP; - if (p->family == AF_INET) { - p->sender.in = sa.in.sin_addr; - p->sender_port = be16toh(sa.in.sin_port); - } else if (p->family == AF_INET6) { - p->sender.in6 = sa.in6.sin6_addr; - p->sender_port = be16toh(sa.in6.sin6_port); - p->ifindex = sa.in6.sin6_scope_id; - } else - return -EAFNOSUPPORT; - - CMSG_FOREACH(cmsg, &mh) { - - if (cmsg->cmsg_level == IPPROTO_IPV6) { - assert(p->family == AF_INET6); - - switch (cmsg->cmsg_type) { - - case IPV6_PKTINFO: { - struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - - if (p->ifindex <= 0) - p->ifindex = i->ipi6_ifindex; - - p->destination.in6 = i->ipi6_addr; - break; - } - - case IPV6_HOPLIMIT: - p->ttl = *(int *) CMSG_DATA(cmsg); - break; - - } - } else if (cmsg->cmsg_level == IPPROTO_IP) { - assert(p->family == AF_INET); - - switch (cmsg->cmsg_type) { - - case IP_PKTINFO: { - struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - - if (p->ifindex <= 0) - p->ifindex = i->ipi_ifindex; - - p->destination.in = i->ipi_addr; - break; - } - - case IP_TTL: - p->ttl = *(int *) CMSG_DATA(cmsg); - break; - } - } - } - - /* The Linux kernel sets the interface index to the loopback - * device if the packet came from the local host since it - * avoids the routing table in such a case. Let's unset the - * interface index in such a case. */ - if (p->ifindex == LOOPBACK_IFINDEX) - p->ifindex = 0; - - if (protocol != DNS_PROTOCOL_DNS) { - /* If we don't know the interface index still, we look for the - * first local interface with a matching address. Yuck! */ - if (p->ifindex <= 0) - p->ifindex = manager_find_ifindex(m, p->family, &p->destination); - } - - *ret = p; - p = NULL; - - return 1; -} - -static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { - int r; - - assert(fd >= 0); - assert(mh); - - for (;;) { - if (sendmsg(fd, mh, flags) >= 0) - return 0; - - if (errno == EINTR) - continue; - - if (errno != EAGAIN) - return -errno; - - r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); - if (r < 0) - return r; - if (r == 0) - return -ETIMEDOUT; - } -} - -static int write_loop(int fd, void *message, size_t length) { - int r; - - assert(fd >= 0); - assert(message); - - for (;;) { - if (write(fd, message, length) >= 0) - return 0; - - if (errno == EINTR) - continue; - - if (errno != EAGAIN) - return -errno; - - r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); - if (r < 0) - return r; - if (r == 0) - return -ETIMEDOUT; - } -} - -int manager_write(Manager *m, int fd, DnsPacket *p) { - int r; - - 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) - return r; - - return 0; -} - -static int manager_ipv4_send( - Manager *m, - int fd, - int ifindex, - const struct in_addr *destination, - uint16_t port, - const struct in_addr *source, - DnsPacket *p) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - }; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))]; - } control; - struct msghdr mh = {}; - struct iovec iov; - - assert(m); - assert(fd >= 0); - assert(destination); - assert(port > 0); - assert(p); - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->size; - - sa.in.sin_addr = *destination; - sa.in.sin_port = htobe16(port), - - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa.in); - - if (ifindex > 0) { - struct cmsghdr *cmsg; - struct in_pktinfo *pi; - - zero(control); - - mh.msg_control = &control; - mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_len = mh.msg_controllen; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - - pi = (struct in_pktinfo*) CMSG_DATA(cmsg); - pi->ipi_ifindex = ifindex; - - if (source) - pi->ipi_spec_dst = *source; - } - - return sendmsg_loop(fd, &mh, 0); -} - -static int manager_ipv6_send( - Manager *m, - int fd, - int ifindex, - const struct in6_addr *destination, - uint16_t port, - const struct in6_addr *source, - DnsPacket *p) { - - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - }; - union { - struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))]; - } control; - struct msghdr mh = {}; - struct iovec iov; - - assert(m); - assert(fd >= 0); - assert(destination); - assert(port > 0); - assert(p); - - iov.iov_base = DNS_PACKET_DATA(p); - iov.iov_len = p->size; - - sa.in6.sin6_addr = *destination; - sa.in6.sin6_port = htobe16(port), - sa.in6.sin6_scope_id = ifindex; - - mh.msg_iov = &iov; - mh.msg_iovlen = 1; - mh.msg_name = &sa.sa; - mh.msg_namelen = sizeof(sa.in6); - - if (ifindex > 0) { - struct cmsghdr *cmsg; - struct in6_pktinfo *pi; - - zero(control); - - mh.msg_control = &control; - mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo)); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_len = mh.msg_controllen; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - - pi = (struct in6_pktinfo*) CMSG_DATA(cmsg); - pi->ipi6_ifindex = ifindex; - - if (source) - pi->ipi6_addr = *source; - } - - return sendmsg_loop(fd, &mh, 0); -} - -int manager_send( - Manager *m, - int fd, - int ifindex, - int family, - const union in_addr_union *destination, - uint16_t port, - const union in_addr_union *source, - DnsPacket *p) { - - assert(m); - assert(fd >= 0); - assert(destination); - assert(port > 0); - assert(p); - - 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, &destination->in, port, &source->in, p); - if (family == AF_INET6) - return manager_ipv6_send(m, fd, ifindex, &destination->in6, port, &source->in6, p); - - return -EAFNOSUPPORT; -} - -uint32_t manager_find_mtu(Manager *m) { - uint32_t mtu = 0; - Link *l; - Iterator i; - - /* If we don't know on which link a DNS packet would be - * delivered, let's find the largest MTU that works on all - * interfaces we know of */ - - HASHMAP_FOREACH(l, m->links, i) { - if (l->mtu <= 0) - continue; - - if (mtu <= 0 || l->mtu < mtu) - mtu = l->mtu; - } - - return mtu; -} - -int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - - assert(m); - - a = manager_find_link_address(m, family, in_addr); - if (a) - return a->link->ifindex; - - return 0; -} - -void manager_refresh_rrs(Manager *m) { - Iterator i; - Link *l; - - assert(m); - - m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); - m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); - - HASHMAP_FOREACH(l, m->links, i) { - link_add_rrs(l, true); - link_add_rrs(l, false); - } -} - -int manager_next_hostname(Manager *m) { - const char *p; - uint64_t u, a; - char *h, *k; - int r; - - assert(m); - - p = strchr(m->llmnr_hostname, 0); - assert(p); - - while (p > m->llmnr_hostname) { - if (!strchr("0123456789", p[-1])) - break; - - p--; - } - - if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0) - u = 1; - - /* Add a random number to the old value. This way we can avoid - * that two hosts pick the same hostname, win on IPv4 and lose - * on IPv6 (or vice versa), and pick the same hostname - * replacement hostname, ad infinitum. We still want the - * numbers to go up monotonically, hence we just add a random - * value 1..10 */ - - random_bytes(&a, sizeof(a)); - u += 1 + a % 10; - - if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0) - return -ENOMEM; - - r = dns_name_concat(h, "local", &k); - if (r < 0) { - free(h); - return r; - } - - log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h); - - free(m->llmnr_hostname); - m->llmnr_hostname = h; - - free(m->mdns_hostname); - m->mdns_hostname = k; - - manager_refresh_rrs(m); - - return 0; -} - -LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr) { - Iterator i; - Link *l; - - assert(m); - - HASHMAP_FOREACH(l, m->links, i) { - LinkAddress *a; - - a = link_find_address(l, family, in_addr); - if (a) - return a; - } - - return NULL; -} - -bool manager_our_packet(Manager *m, DnsPacket *p) { - assert(m); - assert(p); - - return !!manager_find_link_address(m, p->family, &p->sender); -} - -DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { - Link *l; - - assert(m); - assert(p); - - l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); - if (!l) - return NULL; - - 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; -} - -void manager_verify_all(Manager *m) { - DnsScope *s; - - assert(m); - - LIST_FOREACH(scopes, s, m->dns_scopes) - dns_zone_verify_all(&s->zone); -} - -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); - - r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops); - if (r < 0) - return r; - - /* 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; - } - - /* 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; - } - } - - /* 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; -} - -/* filter_route is a tri-state: - * < 0: no filtering - * = 0 or false: return only domains which should be used for searching - * > 0 or true: return only domains which are for routing only - */ -int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) { - DnsSearchDomain *d; - Iterator i; - Link *l; - int r; - - assert(m); - assert(domains); - - r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops); - if (r < 0) - return r; - - LIST_FOREACH(domains, d, m->search_domains) { - - if (filter_route >= 0 && - d->route_only != !!filter_route) - continue; - - r = ordered_set_put(*domains, d->name); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - - HASHMAP_FOREACH(l, m->links, i) { - - LIST_FOREACH(domains, d, l->search_domains) { - - if (filter_route >= 0 && - d->route_only != !!filter_route) - continue; - - r = ordered_set_put(*domains, d->name); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - return 0; -} - -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; -} - -void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) { - - assert(verdict >= 0); - assert(verdict < _DNSSEC_VERDICT_MAX); - - if (log_get_max_level() >= LOG_DEBUG) { - char s[DNS_RESOURCE_KEY_STRING_MAX]; - - log_debug("Found verdict for lookup %s: %s", - dns_resource_key_to_string(key, s, sizeof s), - dnssec_verdict_to_string(verdict)); - } - - m->n_dnssec_verdict[verdict]++; -} - -bool manager_routable(Manager *m, int family) { - Iterator i; - Link *l; - - assert(m); - - /* Returns true if the host has at least one interface with a routable address of the specified type */ - - HASHMAP_FOREACH(l, m->links, i) - if (link_relevant(l, family, false)) - return true; - - return false; -} - -void manager_flush_caches(Manager *m) { - DnsScope *scope; - - assert(m); - - LIST_FOREACH(scopes, scope, m->dns_scopes) - dns_cache_flush(&scope->cache); - - log_info("Flushed all caches."); -} - -void manager_cleanup_saved_user(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r; - - assert(m); - - /* Clean up all saved per-link files in /run/systemd/resolve/netif/ that don't have a matching interface - * anymore. These files are created to persist settings pushed in by the user via the bus, so that resolved can - * be restarted without losing this data. */ - - d = opendir("/run/systemd/resolve/netif/"); - if (!d) { - if (errno == ENOENT) - return; - - log_warning_errno(errno, "Failed to open interface directory: %m"); - return; - } - - FOREACH_DIRENT_ALL(de, d, log_error_errno(errno, "Failed to read interface directory: %m")) { - _cleanup_free_ char *p = NULL; - int ifindex; - Link *l; - - if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG)) - continue; - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - r = parse_ifindex(de->d_name, &ifindex); - if (r < 0) /* Probably some temporary file from a previous run. Delete it */ - goto rm; - - l = hashmap_get(m->links, INT_TO_PTR(ifindex)); - if (!l) /* link vanished */ - goto rm; - - if (l->is_managed) /* now managed by networkd, hence the bus settings are useless */ - goto rm; - - continue; - - rm: - p = strappend("/run/systemd/resolve/netif/", de->d_name); - if (!p) { - log_oom(); - return; - } - - (void) unlink(p); - } -} diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h deleted file mode 100644 index deebd8e484..0000000000 --- a/src/resolve/resolved-manager.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "sd-event.h" -#include "sd-netlink.h" -#include "sd-network.h" - -#include "hashmap.h" -#include "list.h" -#include "ordered-set.h" -#include "resolve-util.h" - -typedef struct Manager Manager; - -#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; - - ResolveSupport llmnr_support; - ResolveSupport mdns_support; - DnssecMode dnssec_mode; - bool enable_cache; - - /* Network */ - Hashmap *links; - - sd_netlink *rtnl; - sd_event_source *rtnl_event_source; - - sd_network_monitor *network_monitor; - sd_event_source *network_event_source; - - /* DNS query management */ - Hashmap *dns_transactions; - LIST_HEAD(DnsQuery, dns_queries); - unsigned n_dns_queries; - - LIST_HEAD(DnsStream, dns_streams); - unsigned n_dns_streams; - - /* 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; - - 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; - - /* LLMNR */ - int llmnr_ipv4_udp_fd; - int llmnr_ipv6_udp_fd; - int llmnr_ipv4_tcp_fd; - int llmnr_ipv6_tcp_fd; - - sd_event_source *llmnr_ipv4_udp_event_source; - sd_event_source *llmnr_ipv6_udp_event_source; - 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; - - /* The hostname we publish on LLMNR and mDNS */ - char *llmnr_hostname; - char *mdns_hostname; - DnsResourceKey *llmnr_host_ipv4_key; - DnsResourceKey *llmnr_host_ipv6_key; - - /* Watch the system hostname */ - int hostname_fd; - sd_event_source *hostname_event_source; - - /* Watch for system suspends */ - sd_bus_slot *prepare_for_sleep_slot; - - sd_event_source *sigusr1_event_source; - sd_event_source *sigusr2_event_source; - - unsigned n_transactions_total; - unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX]; - - /* Data from /etc/hosts */ - Set* etc_hosts_by_address; - Hashmap* etc_hosts_by_name; - usec_t etc_hosts_last, etc_hosts_mtime; - - /* Local DNS stub on 127.0.0.53:53 */ - int dns_stub_udp_fd; - int dns_stub_tcp_fd; - - sd_event_source *dns_stub_udp_event_source; - sd_event_source *dns_stub_tcp_event_source; -}; - -/* Manager */ - -int manager_new(Manager **ret); -Manager* manager_free(Manager *m); - -int manager_start(Manager *m); - -uint32_t manager_find_mtu(Manager *m); - -int manager_write(Manager *m, int fd, DnsPacket *p); -int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *destination, uint16_t port, const union in_addr_union *source, DnsPacket *p); -int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret); - -int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr); -LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr); - -void manager_refresh_rrs(Manager *m); -int manager_next_hostname(Manager *m); - -bool manager_our_packet(Manager *m, DnsPacket *p); -DnsScope* manager_find_scope(Manager *m, DnsPacket *p); - -void manager_verify_all(Manager *m); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); - -#define EXTRA_CMSG_SPACE 1024 - -int manager_is_own_hostname(Manager *m, const char *name); - -int manager_compile_dns_servers(Manager *m, OrderedSet **servers); -int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route); - -DnssecMode manager_get_dnssec_mode(Manager *m); -bool manager_dnssec_supported(Manager *m); - -void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key); - -bool manager_routable(Manager *m, int family); - -void manager_flush_caches(Manager *m); - -void manager_cleanup_saved_user(Manager *m); diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c deleted file mode 100644 index b13b1d0144..0000000000 --- a/src/resolve/resolved-mdns.c +++ /dev/null @@ -1,287 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include -#include -#include - -#include "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 deleted file mode 100644 index 5d274648f4..0000000000 --- a/src/resolve/resolved-mdns.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index 31b25ca50f..0000000000 --- a/src/resolve/resolved-resolv-conf.c +++ /dev/null @@ -1,266 +0,0 @@ -/*** - 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 . - ***/ - -#include - -#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_parse_dns_server_string_and_warn(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); - - if (!dns_server_string(s)) { - 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", dns_server_string(s)); -} - -static void write_resolv_conf_search( - OrderedSet *domains, - FILE *f) { - unsigned length = 0, count = 0; - Iterator i; - char *domain; - - assert(domains); - assert(f); - - fputs("search", f); - - ORDERED_SET_FOREACH(domain, domains, i) { - if (++count > MAXDNSRCH) { - fputs("\n# Too many search domains configured, remaining ones ignored.", f); - break; - } - length += strlen(domain) + 1; - if (length > 256) { - fputs("\n# Total length of all search domains is too long, remaining ones ignored.", f); - break; - } - fputc(' ', f); - fputs(domain, f); - } - - fputs("\n", 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" - "# This is a dynamic resolv.conf file for connecting local clients directly to\n" - "# all known DNS servers.\n#\n" - "# Third party programs must not access this file directly, but only through the\n" - "# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n" - "# replace this symlink by a static file or a different symlink.\n#\n" - "# See systemd-resolved.service(8) for details about the supported modes of\n" - "# operation for /etc/resolv.conf.\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)) - write_resolv_conf_search(domains, f); - - return fflush_and_check(f); -} - -int manager_write_resolv_conf(Manager *m) { - - _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 */ - (void) 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 log_warning_errno(r, "Failed to compile list of DNS servers: %m"); - - r = manager_compile_search_domains(m, &domains, false); - if (r < 0) - return log_warning_errno(r, "Failed to compile list of search domains: %m"); - - r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path); - if (r < 0) - return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m"); - - (void) fchmod(fileno(f), 0644); - - r = write_resolv_conf_contents(f, dns, domains); - if (r < 0) { - log_error_errno(r, "Failed to write private resolv.conf contents: %m"); - goto fail; - } - - if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) { - r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m"); - goto fail; - } - - return 0; - -fail: - (void) unlink(PRIVATE_RESOLV_CONF); - (void) unlink(temp_path); - - return r; -} diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h deleted file mode 100644 index 75fa080e4c..0000000000 --- a/src/resolve/resolved-resolv-conf.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "resolved-manager.h" - -#define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf" - -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 deleted file mode 100644 index deb75f9ae5..0000000000 --- a/src/resolve/resolved.c +++ /dev/null @@ -1,120 +0,0 @@ -/*** - 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 . -***/ - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "capability-util.h" -#include "mkdir.h" -#include "resolved-conf.h" -#include "resolved-manager.h" -#include "resolved-resolv-conf.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "user-util.h" - -int main(int argc, char *argv[]) { - _cleanup_(manager_freep) Manager *m = NULL; - const char *user = "systemd-resolve"; - uid_t uid; - gid_t gid; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - umask(0022); - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } - - /* Always create the directory where resolv.conf will live */ - r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid); - if (r < 0) { - log_error_errno(r, "Could not create runtime directory: %m"); - goto finish; - } - - /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ - r = drop_privileges(uid, gid, - (UINT64_C(1) << CAP_NET_RAW)| /* needed for SO_BINDTODEVICE */ - (UINT64_C(1) << CAP_NET_BIND_SERVICE)| /* needed to bind on port 53 */ - (UINT64_C(1) << CAP_SETPCAP) /* needed in order to drop the caps later */); - if (r < 0) - goto finish; - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, -1) >= 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Could not create manager: %m"); - goto finish; - } - - r = manager_start(m); - if (r < 0) { - log_error_errno(r, "Failed to start manager: %m"); - goto finish; - } - - /* Write finish default resolv.conf to avoid a dangling symlink */ - (void) manager_write_resolv_conf(m); - - /* Let's drop the remaining caps now */ - r = capability_bounding_set_drop(0, true); - if (r < 0) { - log_error_errno(r, "Failed to drop remaining caps: %m"); - goto finish; - } - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Event loop failed: %m"); - goto finish; - } - - sd_event_get_exit_code(m->event, &r); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in deleted file mode 100644 index 3bd8389c88..0000000000 --- a/src/resolve/resolved.conf.in +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See resolved.conf(5) for details - -[Resolve] -#DNS= -#FallbackDNS=@DNS_SERVERS@ -#Domains= -#LLMNR=yes -#DNSSEC=@DEFAULT_DNSSEC_MODE@ -#Cache=yes diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts deleted file mode 100644 index a383c6286d..0000000000 Binary files a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts and /dev/null differ diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts deleted file mode 100644 index 15de02e997..0000000000 Binary files a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts and /dev/null differ diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts deleted file mode 100644 index 1c3ecc5491..0000000000 Binary files a/src/resolve/test-data/fake-caa.pkts and /dev/null differ diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts deleted file mode 100644 index 17874844d9..0000000000 Binary files a/src/resolve/test-data/fedoraproject.org.pkts and /dev/null differ diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts deleted file mode 100644 index 5ef51e0c8e..0000000000 Binary files a/src/resolve/test-data/gandi.net.pkts and /dev/null differ diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts deleted file mode 100644 index f98c4cd855..0000000000 Binary files a/src/resolve/test-data/google.com.pkts and /dev/null differ diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts deleted file mode 100644 index e28a725c9a..0000000000 Binary files a/src/resolve/test-data/kyhwana.org.pkts and /dev/null differ diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts deleted file mode 100644 index 54ba668c75..0000000000 Binary files a/src/resolve/test-data/root.pkts and /dev/null differ diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts deleted file mode 100644 index a854249532..0000000000 Binary files a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts and /dev/null differ diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts deleted file mode 100644 index 11deb39677..0000000000 Binary files a/src/resolve/test-data/teamits.com.pkts and /dev/null differ diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts deleted file mode 100644 index f0a6f982df..0000000000 Binary files a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts and /dev/null differ diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c deleted file mode 100644 index 956b155872..0000000000 --- a/src/resolve/test-dns-packet.c +++ /dev/null @@ -1,132 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2016 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "alloc-util.h" -#include "fileio.h" -#include "glob-util.h" -#include "log.h" -#include "macro.h" -#include "resolved-dns-packet.h" -#include "resolved-dns-rr.h" -#include "string-util.h" -#include "strv.h" -#include "unaligned.h" - -#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1) - -static void verify_rr_copy(DnsResourceRecord *rr) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *copy = NULL; - const char *a, *b; - - assert_se(copy = dns_resource_record_copy(rr)); - assert_se(dns_resource_record_equal(copy, rr) > 0); - - assert_se(a = dns_resource_record_to_string(rr)); - assert_se(b = dns_resource_record_to_string(copy)); - - assert_se(streq(a, b)); -} - -static uint64_t hash(DnsResourceRecord *rr) { - struct siphash state; - - siphash24_init(&state, HASH_KEY.bytes); - dns_resource_record_hash_func(rr, &state); - return siphash24_finalize(&state); -} - -static void test_packet_from_file(const char* filename, bool canonical) { - _cleanup_free_ char *data = NULL; - size_t data_size, packet_size, offset; - - assert_se(read_full_file(filename, &data, &data_size) >= 0); - assert_se(data); - assert_se(data_size > 8); - - log_info("============== %s %s==============", filename, canonical ? "canonical " : ""); - - for (offset = 0; offset < data_size; offset += 8 + packet_size) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL; - const char *s, *s2; - uint64_t hash1, hash2; - - packet_size = unaligned_read_le64(data + offset); - assert_se(packet_size > 0); - assert_se(offset + 8 + packet_size <= data_size); - - assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0); - - assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0); - assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0); - - verify_rr_copy(rr); - - s = dns_resource_record_to_string(rr); - assert_se(s); - puts(s); - - hash1 = hash(rr); - - assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0); - - assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0); - assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0); - assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0); - - verify_rr_copy(rr); - - s2 = dns_resource_record_to_string(rr); - assert_se(s2); - assert_se(streq(s, s2)); - - hash2 = hash(rr); - assert_se(hash1 == hash2); - } -} - -int main(int argc, char **argv) { - int i, N; - _cleanup_globfree_ glob_t g = {}; - char **fnames; - - log_parse_environment(); - - if (argc >= 2) { - N = argc - 1; - fnames = argv + 1; - } else { - assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0); - N = g.gl_pathc; - fnames = g.gl_pathv; - } - - for (i = 0; i < N; i++) { - test_packet_from_file(fnames[i], false); - puts(""); - test_packet_from_file(fnames[i], true); - if (i + 1 < N) - puts(""); - } - - return EXIT_SUCCESS; -} diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c deleted file mode 100644 index 58c089eb40..0000000000 --- a/src/resolve/test-dnssec-complex.c +++ /dev/null @@ -1,236 +0,0 @@ -/*** - 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 . -***/ - -#include - -#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); - -#ifdef 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 deleted file mode 100644 index b3018e8239..0000000000 --- a/src/resolve/test-dnssec.c +++ /dev/null @@ -1,343 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#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_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); -} - -#ifdef HAVE_GCRYPT - -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_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_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_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); -} - -#endif - -int main(int argc, char*argv[]) { - - test_dnssec_canonicalize(); - -#ifdef HAVE_GCRYPT - test_dnssec_verify_dns_key(); - test_dnssec_verify_rrset(); - test_dnssec_verify_rrset2(); - test_dnssec_nsec3_hash(); -#endif - - return 0; -} diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c deleted file mode 100644 index 2d615130e1..0000000000 --- a/src/resolve/test-resolve-tables.c +++ /dev/null @@ -1,64 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "dns-type.h" -#include "test-tables.h" - -int main(int argc, char **argv) { - uint16_t i; - - test_table_sparse(dns_type, DNS_TYPE); - - log_info("/* DNS_TYPE */"); - for (i = 0; i < _DNS_TYPE_MAX; i++) { - const char *s; - - s = dns_type_to_string(i); - assert_se(s == NULL || strlen(s) < _DNS_TYPE_STRING_MAX); - - if (s) - log_info("%-*s %s%s%s%s%s%s%s%s%s", - (int) _DNS_TYPE_STRING_MAX - 1, s, - dns_type_is_pseudo(i) ? "pseudo " : "", - dns_type_is_valid_query(i) ? "valid_query " : "", - dns_type_is_valid_rr(i) ? "is_valid_rr " : "", - dns_type_may_redirect(i) ? "may_redirect " : "", - dns_type_is_dnssec(i) ? "dnssec " : "", - dns_type_is_obsolete(i) ? "obsolete " : "", - dns_type_may_wildcard(i) ? "wildcard " : "", - dns_type_apex_only(i) ? "apex_only " : "", - dns_type_needs_authentication(i) ? "needs_authentication" : ""); - } - - log_info("/* DNS_CLASS */"); - for (i = 0; i < _DNS_CLASS_MAX; i++) { - const char *s; - - s = dns_class_to_string(i); - assert_se(s == NULL || strlen(s) < _DNS_CLASS_STRING_MAX); - - if (s) - log_info("%-*s %s%s", - (int) _DNS_CLASS_STRING_MAX - 1, s, - dns_class_is_pseudo(i) ? "is_pseudo " : "", - dns_class_is_valid_rr(i) ? "is_valid_rr " : ""); - } - - return EXIT_SUCCESS; -} diff --git a/src/rfkill/Makefile b/src/rfkill/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/rfkill/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c deleted file mode 100644 index 0acdf229ed..0000000000 --- a/src/rfkill/rfkill.c +++ /dev/null @@ -1,426 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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" - -#define EXIT_USEC (5 * USEC_PER_SEC) - -static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { - [RFKILL_TYPE_ALL] = "all", - [RFKILL_TYPE_WLAN] = "wlan", - [RFKILL_TYPE_BLUETOOTH] = "bluetooth", - [RFKILL_TYPE_UWB] = "uwb", - [RFKILL_TYPE_WIMAX] = "wimax", - [RFKILL_TYPE_WWAN] = "wwan", - [RFKILL_TYPE_GPS] = "gps", - [RFKILL_TYPE_FM] = "fm", - [RFKILL_TYPE_NFC] = "nfc", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int); - -static int find_device( - struct udev *udev, - const struct rfkill_event *event, - struct udev_device **ret) { - - _cleanup_free_ char *sysname = NULL; - struct udev_device *device; - const char *name; - - assert(udev); - assert(event); - assert(ret); - - if (asprintf(&sysname, "rfkill%i", event->idx) < 0) - return log_oom(); - - device = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); - if (!device) - return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); - - name = udev_device_get_sysattr_value(device, "name"); - if (!name) { - log_debug("Device has no name, ignoring."); - udev_device_unref(device); - return -ENOENT; - } - - log_debug("Operating on rfkill device '%s'.", name); - - *ret = device; - return 0; -} - -static int wait_for_initialized( - struct udev *udev, - struct udev_device *device, - struct udev_device **ret) { - - _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; - struct udev_device *d; - const char *sysname; - int watch_fd, r; - - assert(udev); - assert(device); - assert(ret); - - if (udev_device_get_is_initialized(device) != 0) { - *ret = udev_device_ref(device); - return 0; - } - - assert_se(sysname = udev_device_get_sysname(device)); - - /* Wait until the device is initialized, so that we can get - * access to the ID_PATH property */ - - monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (!monitor) - return log_error_errno(errno, "Failed to acquire monitor: %m"); - - r = udev_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL); - if (r < 0) - return log_error_errno(r, "Failed to add rfkill udev match to monitor: %m"); - - r = udev_monitor_enable_receiving(monitor); - if (r < 0) - return log_error_errno(r, "Failed to enable udev receiving: %m"); - - watch_fd = udev_monitor_get_fd(monitor); - if (watch_fd < 0) - return log_error_errno(watch_fd, "Failed to get watch fd: %m"); - - /* Check again, maybe things changed */ - d = udev_device_new_from_subsystem_sysname(udev, "rfkill", sysname); - if (!d) - return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open device: %m"); - - if (udev_device_get_is_initialized(d) != 0) { - *ret = d; - return 0; - } - - for (;;) { - _cleanup_udev_device_unref_ struct udev_device *t = NULL; - - r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); - if (r == -EINTR) - continue; - if (r < 0) - return log_error_errno(r, "Failed to watch udev monitor: %m"); - - t = udev_monitor_receive_device(monitor); - if (!t) - continue; - - if (streq_ptr(udev_device_get_sysname(device), sysname)) { - *ret = udev_device_ref(t); - return 0; - } - } -} - -static int determine_state_file( - struct udev *udev, - const struct rfkill_event *event, - struct udev_device *d, - char **ret) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - const char *path_id, *type; - char *state_file; - int r; - - assert(event); - assert(d); - assert(ret); - - r = wait_for_initialized(udev, d, &device); - if (r < 0) - return r; - - assert_se(type = rfkill_type_to_string(event->type)); - - path_id = udev_device_get_property_value(device, "ID_PATH"); - if (path_id) { - _cleanup_free_ char *escaped_path_id = NULL; - - escaped_path_id = cescape(path_id); - if (!escaped_path_id) - return log_oom(); - - state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL); - } else - state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL); - - if (!state_file) - return log_oom(); - - *ret = state_file; - return 0; -} - -static int load_state( - int rfkill_fd, - struct udev *udev, - const struct rfkill_event *event) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *state_file = NULL, *value = NULL; - struct rfkill_event we; - ssize_t l; - int b, r; - - assert(rfkill_fd >= 0); - assert(udev); - assert(event); - - if (shall_restore_state() == 0) - return 0; - - r = find_device(udev, event, &device); - if (r < 0) - return r; - - r = determine_state_file(udev, event, device, &state_file); - if (r < 0) - return r; - - r = read_one_line_file(state_file, &value); - if (r == -ENOENT) { - /* No state file? Then save the current state */ - - r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - if (r < 0) - return log_error_errno(r, "Failed to write state file %s: %m", state_file); - - log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); - return 0; - } - if (r < 0) - return log_error_errno(r, "Failed to read state file %s: %m", state_file); - - b = parse_boolean(value); - if (b < 0) - return log_error_errno(b, "Failed to parse state file %s: %m", state_file); - - we = (struct rfkill_event) { - .op = RFKILL_OP_CHANGE, - .idx = event->idx, - .soft = b, - }; - - l = write(rfkill_fd, &we, sizeof(we)); - if (l < 0) - return log_error_errno(errno, "Failed to restore rfkill state for %i: %m", event->idx); - if (l != sizeof(we)) { - log_error("Couldn't write rfkill event structure, too short."); - return -EIO; - } - - log_debug("Loaded state '%s' from %s.", one_zero(b), state_file); - return 0; -} - -static int save_state( - int rfkill_fd, - struct udev *udev, - const struct rfkill_event *event) { - - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *state_file = NULL; - int r; - - assert(rfkill_fd >= 0); - assert(udev); - assert(event); - - r = find_device(udev, event, &device); - if (r < 0) - return r; - - r = determine_state_file(udev, event, device, &state_file); - if (r < 0) - return r; - - r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - if (r < 0) - return log_error_errno(r, "Failed to write state file %s: %m", state_file); - - log_debug("Saved state '%s' to %s.", one_zero(event->soft), state_file); - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_close_ int rfkill_fd = -1; - bool ready = false; - int r, n; - - if (argc > 1) { - log_error("This program requires no arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - udev = udev_new(); - if (!udev) { - r = log_oom(); - goto finish; - } - - r = mkdir_p("/var/lib/systemd/rfkill", 0755); - if (r < 0) { - log_error_errno(r, "Failed to create rfkill directory: %m"); - goto finish; - } - - n = sd_listen_fds(false); - if (n < 0) { - r = log_error_errno(n, "Failed to determine whether we got any file descriptors passed: %m"); - goto finish; - } - if (n > 1) { - log_error("Got too many file descriptors."); - r = -EINVAL; - goto finish; - } - - if (n == 0) { - rfkill_fd = open("/dev/rfkill", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (rfkill_fd < 0) { - if (errno == ENOENT) { - log_debug_errno(errno, "Missing rfkill subsystem, or no device present, exiting."); - r = 0; - goto finish; - } - - r = log_error_errno(errno, "Failed to open /dev/rfkill: %m"); - goto finish; - } - } else { - rfkill_fd = SD_LISTEN_FDS_START; - - r = fd_nonblock(rfkill_fd, 1); - if (r < 0) { - log_error_errno(r, "Failed to make /dev/rfkill socket non-blocking: %m"); - goto finish; - } - } - - for (;;) { - struct rfkill_event event; - const char *type; - ssize_t l; - - l = read(rfkill_fd, &event, sizeof(event)); - if (l < 0) { - if (errno == EAGAIN) { - - if (!ready) { - /* Notify manager that we are - * now finished with - * processing whatever was - * queued */ - (void) sd_notify(false, "READY=1"); - ready = true; - } - - /* Hang around for a bit, maybe there's more coming */ - - r = fd_wait_for_event(rfkill_fd, POLLIN, EXIT_USEC); - if (r == -EINTR) - continue; - if (r < 0) { - log_error_errno(r, "Failed to poll() on device: %m"); - goto finish; - } - if (r > 0) - continue; - - log_debug("All events read and idle, exiting."); - break; - } - - log_error_errno(errno, "Failed to read from /dev/rfkill: %m"); - } - - if (l != RFKILL_EVENT_SIZE_V1) { - log_error("Read event structure of invalid size."); - r = -EIO; - goto finish; - } - - type = rfkill_type_to_string(event.type); - if (!type) { - log_debug("An rfkill device of unknown type %i discovered, ignoring.", event.type); - continue; - } - - switch (event.op) { - - case RFKILL_OP_ADD: - log_debug("A new rfkill device has been added with index %i and type %s.", event.idx, type); - (void) load_state(rfkill_fd, udev, &event); - break; - - case RFKILL_OP_DEL: - log_debug("An rfkill device has been removed with index %i and type %s", event.idx, type); - break; - - case RFKILL_OP_CHANGE: - log_debug("An rfkill device has changed state with index %i and type %s", event.idx, type); - (void) save_state(rfkill_fd, udev, &event); - break; - - default: - log_debug("Unknown event %i from /dev/rfkill for index %i and type %s, ignoring.", event.op, event.idx, type); - break; - } - } - - r = 0; - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/run/Makefile b/src/run/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/run/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/run/run.c b/src/run/run.c deleted file mode 100644 index 58fa49a4d1..0000000000 --- a/src/run/run.c +++ /dev/null @@ -1,1261 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "sd-bus.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-unit-util.h" -#include "bus-util.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 "ptyfwd.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; -static bool arg_remain_after_exit = false; -static bool arg_no_block = false; -static const char *arg_unit = NULL; -static const char *arg_description = NULL; -static const char *arg_slice = NULL; -static bool arg_send_sighup = false; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static const char *arg_host = NULL; -static bool arg_user = false; -static const char *arg_service_type = NULL; -static const char *arg_exec_user = NULL; -static const char *arg_exec_group = NULL; -static int arg_nice = 0; -static bool arg_nice_set = false; -static char **arg_environment = NULL; -static char **arg_property = NULL; -static bool arg_pty = false; -static usec_t arg_on_active = 0; -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 const char *arg_on_calendar = NULL; -static char **arg_timer_property = NULL; -static bool arg_quiet = false; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -static void help(void) { - printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" - "Run the specified command in a transient scope or service or timer\n" - "unit. If a timer option is specified and the unit specified with\n" - "the --unit option exists, the command can be omitted.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-ask-password Do not prompt for password\n" - " --user Run as user unit\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --scope Run this as scope rather than service\n" - " --unit=UNIT Run under the specified unit name\n" - " -p --property=NAME=VALUE Set unit property\n" - " --description=TEXT Description for unit\n" - " --slice=SLICE Run in the specified slice\n" - " --no-block Do not wait until operation finished\n" - " -r --remain-after-exit Leave service around until explicitly stopped\n" - " --send-sighup Send SIGHUP when terminating\n" - " --service-type=TYPE Service type\n" - " --uid=USER Run as system user\n" - " --gid=GROUP Run as system group\n" - " --nice=NICE Nice level\n" - " -E --setenv=NAME=VALUE Set environment\n" - " -t --pty Run service on pseudo tty\n" - " -q --quiet Suppress information messages during runtime\n\n" - "Timer options:\n\n" - " --on-active=SECONDS Run after SECONDS delay\n" - " --on-boot=SECONDS Run SECONDS after machine was booted up\n" - " --on-startup=SECONDS Run SECONDS after systemd activation\n" - " --on-unit-active=SECONDS Run SECONDS after the last activation\n" - " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" - " --on-calendar=SPEC Realtime timer\n" - " --timer-property=NAME=VALUE Set timer unit property\n", - program_invocation_short_name); -} - -static bool with_timer(void) { - return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_USER, - ARG_SYSTEM, - ARG_SCOPE, - ARG_UNIT, - ARG_DESCRIPTION, - ARG_SLICE, - ARG_SEND_SIGHUP, - ARG_SERVICE_TYPE, - ARG_EXEC_USER, - ARG_EXEC_GROUP, - ARG_NICE, - ARG_ON_ACTIVE, - ARG_ON_BOOT, - ARG_ON_STARTUP, - ARG_ON_UNIT_ACTIVE, - ARG_ON_UNIT_INACTIVE, - ARG_ON_CALENDAR, - ARG_TIMER_PROPERTY, - ARG_NO_BLOCK, - ARG_NO_ASK_PASSWORD, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "scope", no_argument, NULL, ARG_SCOPE }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "description", required_argument, NULL, ARG_DESCRIPTION }, - { "slice", required_argument, NULL, ARG_SLICE }, - { "remain-after-exit", no_argument, NULL, 'r' }, - { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, - { "uid", required_argument, NULL, ARG_EXEC_USER }, - { "gid", required_argument, NULL, ARG_EXEC_GROUP }, - { "nice", required_argument, NULL, ARG_NICE }, - { "setenv", required_argument, NULL, 'E' }, - { "property", required_argument, NULL, 'p' }, - { "tty", no_argument, NULL, 't' }, /* deprecated */ - { "pty", no_argument, NULL, 't' }, - { "quiet", no_argument, NULL, 'q' }, - { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, - { "on-boot", required_argument, NULL, ARG_ON_BOOT }, - { "on-startup", required_argument, NULL, ARG_ON_STARTUP }, - { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE }, - { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, - { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, - { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, - { "no-block", no_argument, NULL, ARG_NO_BLOCK }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - {}, - }; - - int r, c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case ARG_VERSION: - return version(); - - case ARG_USER: - arg_user = true; - break; - - case ARG_SYSTEM: - arg_user = false; - break; - - case ARG_SCOPE: - arg_scope = true; - break; - - case ARG_UNIT: - arg_unit = optarg; - break; - - case ARG_DESCRIPTION: - arg_description = optarg; - break; - - case ARG_SLICE: - arg_slice = optarg; - break; - - case ARG_SEND_SIGHUP: - arg_send_sighup = true; - break; - - case 'r': - arg_remain_after_exit = true; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_SERVICE_TYPE: - arg_service_type = optarg; - break; - - case ARG_EXEC_USER: - arg_exec_user = optarg; - break; - - case ARG_EXEC_GROUP: - arg_exec_group = optarg; - break; - - case ARG_NICE: - r = safe_atoi(optarg, &arg_nice); - if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) { - log_error("Failed to parse nice value"); - return -EINVAL; - } - - arg_nice_set = true; - break; - - case 'E': - if (strv_extend(&arg_environment, optarg) < 0) - return log_oom(); - - break; - - case 'p': - if (strv_extend(&arg_property, optarg) < 0) - return log_oom(); - - break; - - case 't': - arg_pty = true; - break; - - case 'q': - arg_quiet = true; - break; - - case ARG_ON_ACTIVE: - - r = parse_sec(optarg, &arg_on_active); - if (r < 0) { - log_error("Failed to parse timer value: %s", optarg); - return r; - } - - break; - - case ARG_ON_BOOT: - - r = parse_sec(optarg, &arg_on_boot); - if (r < 0) { - log_error("Failed to parse timer value: %s", optarg); - return r; - } - - break; - - case ARG_ON_STARTUP: - - r = parse_sec(optarg, &arg_on_startup); - if (r < 0) { - log_error("Failed to parse timer value: %s", optarg); - return r; - } - - break; - - case ARG_ON_UNIT_ACTIVE: - - r = parse_sec(optarg, &arg_on_unit_active); - if (r < 0) { - log_error("Failed to parse timer value: %s", optarg); - return r; - } - - break; - - case ARG_ON_UNIT_INACTIVE: - - r = parse_sec(optarg, &arg_on_unit_inactive); - if (r < 0) { - log_error("Failed to parse timer value: %s", optarg); - return r; - } - - break; - - 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; - } - - calendar_spec_free(spec); - arg_on_calendar = optarg; - break; - } - - case ARG_TIMER_PROPERTY: - - if (strv_extend(&arg_timer_property, optarg) < 0) - return log_oom(); - - break; - - case ARG_NO_BLOCK: - arg_no_block = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if ((optind >= argc) && (!arg_unit || !with_timer())) { - log_error("Command line to execute required."); - return -EINVAL; - } - - if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Execution in user context is not supported on non-local systems."); - return -EINVAL; - } - - if (arg_scope && arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Scope execution is not supported on non-local systems."); - return -EINVAL; - } - - if (arg_scope && (arg_remain_after_exit || arg_service_type)) { - log_error("--remain-after-exit and --service-type= are not supported in --scope mode."); - return -EINVAL; - } - - if (arg_pty && (with_timer() || arg_scope)) { - log_error("--pty is not compatible in timer or --scope mode."); - 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; - } - - if (arg_timer_property && !with_timer()) { - log_error("--timer-property= has no effect without any other timer options."); - return -EINVAL; - } - - return 1; -} - -static int transient_unit_set_properties(sd_bus_message *m, char **properties) { - char **i; - int r; - - r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); - if (r < 0) - return r; - - STRV_FOREACH(i, properties) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } - - return 0; -} - -static int transient_cgroup_set_properties(sd_bus_message *m) { - int r; - assert(m); - - if (!isempty(arg_slice)) { - _cleanup_free_ char *slice; - - r = unit_name_mangle_with_suffix(arg_slice, UNIT_NAME_NOGLOB, ".slice", &slice); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); - if (r < 0) - return r; - } - - return 0; -} - -static int transient_kill_set_properties(sd_bus_message *m) { - assert(m); - - if (arg_send_sighup) - return sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup); - else - return 0; -} - -static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) { - int r; - - assert(m); - - r = transient_unit_set_properties(m, arg_property); - if (r < 0) - return r; - - r = transient_kill_set_properties(m); - if (r < 0) - return r; - - r = transient_cgroup_set_properties(m); - if (r < 0) - return r; - - if (arg_remain_after_exit) { - r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit); - if (r < 0) - return r; - } - - if (arg_service_type) { - r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type); - if (r < 0) - return r; - } - - if (arg_exec_user) { - r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user); - if (r < 0) - return r; - } - - if (arg_exec_group) { - r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group); - if (r < 0) - return r; - } - - if (arg_nice_set) { - r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); - if (r < 0) - return r; - } - - if (pty_path) { - const char *e; - - r = sd_bus_message_append(m, - "(sv)(sv)(sv)(sv)", - "StandardInput", "s", "tty", - "StandardOutput", "s", "tty", - "StandardError", "s", "tty", - "TTYPath", "s", pty_path); - if (r < 0) - return r; - - e = getenv("TERM"); - if (e) { - char *n; - - n = strjoina("TERM=", e); - r = sd_bus_message_append(m, - "(sv)", - "Environment", "as", 1, n); - if (r < 0) - return r; - } - } - - if (!strv_isempty(arg_environment)) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", "Environment"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, arg_environment); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - /* Exec container */ - { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", "ExecStart"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'v', "a(sasb)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sasb)"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'r', "sasb"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "s", argv[0]); - if (r < 0) - return r; - - r = sd_bus_message_append_strv(m, argv); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "b", false); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return r; - } - - return 0; -} - -static int transient_scope_set_properties(sd_bus_message *m) { - int r; - - assert(m); - - r = transient_unit_set_properties(m, arg_property); - if (r < 0) - return r; - - r = transient_kill_set_properties(m); - if (r < 0) - return r; - - r = transient_cgroup_set_properties(m); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); - if (r < 0) - return r; - - return 0; -} - -static int transient_timer_set_properties(sd_bus_message *m) { - int r; - - assert(m); - - r = transient_unit_set_properties(m, arg_timer_property); - 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) - return r; - } - - if (arg_on_boot) { - r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot); - if (r < 0) - return r; - } - - if (arg_on_startup) { - r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup); - if (r < 0) - return r; - } - - if (arg_on_unit_active) { - r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active); - if (r < 0) - return r; - } - - if (arg_on_unit_inactive) { - r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive); - if (r < 0) - return r; - } - - if (arg_on_calendar) { - r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar); - if (r < 0) - return r; - } - - 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_(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; - int r; - - assert(bus); - assert(argv); - - if (arg_pty) { - - if (arg_transport == BUS_TRANSPORT_LOCAL) { - master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); - if (master < 0) - return log_error_errno(errno, "Failed to acquire pseudo tty: %m"); - - r = ptsname_malloc(master, &pty_path); - if (r < 0) - return log_error_errno(r, "Failed to determine tty name: %m"); - - if (unlockpt(master) < 0) - return log_error_errno(errno, "Failed to unlock tty: %m"); - - } else if (arg_transport == BUS_TRANSPORT_MACHINE) { - _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL; - const char *s; - - r = sd_bus_default_system(&system_bus); - if (r < 0) - return log_error_errno(r, "Failed to connect to system bus: %m"); - - r = sd_bus_call_method(system_bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "OpenMachinePTY", - &error, - &pty_reply, - "s", arg_host); - if (r < 0) { - log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(pty_reply, "hs", &master, &s); - if (r < 0) - return bus_log_parse_error(r); - - master = fcntl(master, F_DUPFD_CLOEXEC, 3); - if (master < 0) - return log_error_errno(errno, "Failed to duplicate master fd: %m"); - - pty_path = strdup(s); - if (!pty_path) - return log_oom(); - } else - assert_not_reached("Can't allocate tty via ssh"); - } - - if (!arg_no_block) { - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_error_errno(r, "Could not watch jobs: %m"); - } - - if (arg_unit) { - 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 { - r = make_unit_name(bus, UNIT_SERVICE, &service); - if (r < 0) - return r; - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); - if (r < 0) - return bus_log_create_error(r); - - /* Name and mode */ - r = sd_bus_message_append(m, "ss", service, "fail"); - if (r < 0) - return bus_log_create_error(r); - - /* Properties */ - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = transient_service_set_properties(m, argv, pty_path); - 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); - - /* Auxiliary units */ - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return bus_log_create_error(r); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call(bus, m, 0, &error, &reply); - 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; - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_wait_for_jobs_one(w, object, arg_quiet); - if (r < 0) - return r; - } - - if (master >= 0) { - _cleanup_(pty_forward_freep) PTYForward *forward = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - char last_char = 0; - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to get event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - - (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); - - if (!arg_quiet) - log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service); - - 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"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - pty_forward_get_last_char(forward, &last_char); - - forward = pty_forward_free(forward); - - if (!arg_quiet && last_char != '\n') - fputc('\n', stdout); - - } else if (!arg_quiet) - log_info("Running as unit: %s", service); - - return 0; -} - -static int start_transient_scope( - sd_bus *bus, - char **argv) { - - _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; - const char *object = NULL; - int r; - - assert(bus); - assert(argv); - - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_oom(); - - if (arg_unit) { - 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 { - r = make_unit_name(bus, UNIT_SCOPE, &scope); - if (r < 0) - return r; - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); - if (r < 0) - return bus_log_create_error(r); - - /* Name and Mode */ - r = sd_bus_message_append(m, "ss", scope, "fail"); - if (r < 0) - return bus_log_create_error(r); - - /* Properties */ - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = transient_scope_set_properties(m); - 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); - - /* Auxiliary units */ - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return bus_log_create_error(r); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); - return r; - } - - if (arg_nice_set) { - if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) - return log_error_errno(errno, "Failed to set nice level: %m"); - } - - if (arg_exec_group) { - gid_t gid; - - r = get_group_creds(&arg_exec_group, &gid); - if (r < 0) - return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); - } - - if (arg_exec_user) { - const char *home, *shell; - uid_t uid; - gid_t gid; - - r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell); - if (r < 0) - return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); - - r = strv_extendf(&user_env, "HOME=%s", home); - if (r < 0) - return log_oom(); - - r = strv_extendf(&user_env, "SHELL=%s", shell); - if (r < 0) - return log_oom(); - - r = strv_extendf(&user_env, "USER=%s", arg_exec_user); - if (r < 0) - return log_oom(); - - r = strv_extendf(&user_env, "LOGNAME=%s", arg_exec_user); - if (r < 0) - return log_oom(); - - if (!arg_exec_group) { - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); - } - - if (setresuid(uid, uid, uid) < 0) - return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); - } - - env = strv_env_merge(3, environ, user_env, arg_environment); - if (!env) - return log_oom(); - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_wait_for_jobs_one(w, object, arg_quiet); - if (r < 0) - return r; - - if (!arg_quiet) - log_info("Running scope as unit: %s", scope); - - execvpe(argv[0], argv, env); - - return log_error_errno(errno, "Failed to execute: %m"); -} - -static int start_transient_timer( - sd_bus *bus, - char **argv) { - - _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; - int r; - - assert(bus); - assert(argv); - - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_oom(); - - if (arg_unit) { - switch (unit_name_to_type(arg_unit)) { - - case UNIT_SERVICE: - service = strdup(arg_unit); - if (!service) - return log_oom(); - - r = unit_name_change_suffix(service, ".timer", &timer); - if (r < 0) - return log_error_errno(r, "Failed to change unit suffix: %m"); - break; - - case UNIT_TIMER: - timer = strdup(arg_unit); - if (!timer) - return log_oom(); - - r = unit_name_change_suffix(timer, ".service", &service); - if (r < 0) - return log_error_errno(r, "Failed to change unit suffix: %m"); - break; - - default: - 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"); - - r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer); - if (r < 0) - return log_error_errno(r, "Failed to mangle unit name: %m"); - - break; - } - } 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, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); - if (r < 0) - return bus_log_create_error(r); - - /* Name and Mode */ - r = sd_bus_message_append(m, "ss", timer, "fail"); - if (r < 0) - return bus_log_create_error(r); - - /* Properties */ - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = transient_timer_set_properties(m); - 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_open_container(m, 'a', "(sa(sv))"); - if (r < 0) - return bus_log_create_error(r); - - if (argv[0]) { - r = sd_bus_message_open_container(m, 'r', "sa(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "s", service); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - r = transient_service_set_properties(m, argv, NULL); - 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); - 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); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); - return r; - } - - r = sd_bus_message_read(reply, "o", &object); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_wait_for_jobs_one(w, object, arg_quiet); - if (r < 0) - return r; - - log_info("Running timer as unit: %s", timer); - if (argv[0]) - log_info("Will run service as unit: %s", service); - - return 0; -} - -int main(int argc, char* argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *description = NULL, *command = NULL; - int r; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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: %m", argv[optind]); - goto finish; - } - - argv[optind] = command; - } - - if (!arg_description) { - description = strv_join(argv + optind, " "); - if (!description) { - r = log_oom(); - goto finish; - } - - if (arg_unit && isempty(description)) { - r = free_and_strdup(&description, arg_unit); - if (r < 0) - goto finish; - } - - arg_description = description; - } - - 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; - } - - if (arg_scope) - r = start_transient_scope(bus, argv + optind); - else if (with_timer()) - r = start_transient_timer(bus, argv + optind); - else - r = start_transient_service(bus, argv + optind); - -finish: - strv_free(arg_environment); - strv_free(arg_property); - strv_free(arg_timer_property); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/shared/Makefile b/src/shared/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/shared/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c deleted file mode 100644 index 2aa951fce9..0000000000 --- a/src/shared/acl-util.c +++ /dev/null @@ -1,429 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011,2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "acl-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; - int r; - - assert(acl); - assert(entry); - - for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - acl_tag_t tag; - uid_t *u; - bool b; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - u = acl_get_qualifier(i); - if (!u) - return -errno; - - b = *u == uid; - acl_free(u); - - if (b) { - *entry = i; - return 1; - } - } - if (r < 0) - return -errno; - - return 0; -} - -int calc_acl_mask_if_needed(acl_t *acl_p) { - acl_entry_t i; - int r; - bool need = false; - - assert(acl_p); - - for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { - acl_tag_t tag; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag == ACL_MASK) - return 0; - - if (IN_SET(tag, ACL_USER, ACL_GROUP)) - need = true; - } - if (r < 0) - return -errno; - - 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) { - acl_entry_t i; - int r; - bool have_user_obj = false, have_group_obj = false, have_other = false; - struct stat st; - _cleanup_(acl_freep) acl_t basic = NULL; - - assert(acl_p); - - for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { - acl_tag_t tag; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag == ACL_USER_OBJ) - have_user_obj = true; - else if (tag == ACL_GROUP_OBJ) - have_group_obj = true; - else if (tag == ACL_OTHER) - have_other = true; - if (have_user_obj && have_group_obj && have_other) - return 0; - } - if (r < 0) - return -errno; - - r = stat(path, &st); - if (r < 0) - return -errno; - - basic = acl_from_mode(st.st_mode); - if (!basic) - return -errno; - - for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) { - acl_tag_t tag; - acl_entry_t dst; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if ((tag == ACL_USER_OBJ && have_user_obj) || - (tag == ACL_GROUP_OBJ && have_group_obj) || - (tag == ACL_OTHER && have_other)) - continue; - - r = acl_create_entry(acl_p, &dst); - if (r < 0) - return -errno; - - r = acl_copy_entry(dst, i); - if (r < 0) - return -errno; - } - if (r < 0) - return -errno; - return 0; -} - -int acl_search_groups(const char *path, char ***ret_groups) { - _cleanup_strv_free_ char **g = NULL; - _cleanup_(acl_free) acl_t acl = NULL; - bool ret = false; - acl_entry_t entry; - int r; - - assert(path); - - acl = acl_get_file(path, ACL_TYPE_DEFAULT); - if (!acl) - return -errno; - - r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); - for (;;) { - _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL; - acl_tag_t tag; - - if (r < 0) - return -errno; - if (r == 0) - break; - - if (acl_get_tag_type(entry, &tag) < 0) - return -errno; - - if (tag != ACL_GROUP) - goto next; - - gid = acl_get_qualifier(entry); - if (!gid) - return -errno; - - if (in_gid(*gid) > 0) { - if (!ret_groups) - return true; - - ret = true; - } - - if (ret_groups) { - char *name; - - name = gid_to_name(*gid); - if (!name) - return -ENOMEM; - - r = strv_consume(&g, name); - if (r < 0) - return r; - } - - next: - r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); - } - - if (ret_groups) { - *ret_groups = g; - g = NULL; - } - - return ret; -} - -int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) { - _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */ - _cleanup_strv_free_ char **split; - char **entry; - int r = -EINVAL; - _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL; - - split = strv_split(text, ","); - if (!split) - return -ENOMEM; - - STRV_FOREACH(entry, split) { - char *p; - - p = startswith(*entry, "default:"); - if (!p) - p = startswith(*entry, "d:"); - - if (p) - r = strv_push(&d, p); - else - r = strv_push(&a, *entry); - if (r < 0) - return r; - } - - if (!strv_isempty(a)) { - _cleanup_free_ char *join; - - join = strv_join(a, ","); - if (!join) - return -ENOMEM; - - a_acl = acl_from_text(join); - if (!a_acl) - return -errno; - - if (want_mask) { - r = calc_acl_mask_if_needed(&a_acl); - if (r < 0) - return r; - } - } - - if (!strv_isempty(d)) { - _cleanup_free_ char *join; - - join = strv_join(d, ","); - if (!join) - return -ENOMEM; - - d_acl = acl_from_text(join); - if (!d_acl) - return -errno; - - if (want_mask) { - r = calc_acl_mask_if_needed(&d_acl); - if (r < 0) - return r; - } - } - - *acl_access = a_acl; - *acl_default = d_acl; - a_acl = d_acl = NULL; - - return 0; -} - -static int acl_entry_equal(acl_entry_t a, acl_entry_t b) { - acl_tag_t tag_a, tag_b; - - if (acl_get_tag_type(a, &tag_a) < 0) - return -errno; - - if (acl_get_tag_type(b, &tag_b) < 0) - return -errno; - - if (tag_a != tag_b) - return false; - - switch (tag_a) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - /* can have only one of those */ - return true; - case ACL_USER: { - _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL; - - uid_a = acl_get_qualifier(a); - if (!uid_a) - return -errno; - - uid_b = acl_get_qualifier(b); - if (!uid_b) - return -errno; - - return *uid_a == *uid_b; - } - case ACL_GROUP: { - _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL; - - gid_a = acl_get_qualifier(a); - if (!gid_a) - return -errno; - - gid_b = acl_get_qualifier(b); - if (!gid_b) - return -errno; - - return *gid_a == *gid_b; - } - default: - assert_not_reached("Unknown acl tag type"); - } -} - -static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *out) { - acl_entry_t i; - int r; - - for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - r = acl_entry_equal(i, entry); - if (r < 0) - return r; - if (r > 0) { - *out = i; - return 1; - } - } - if (r < 0) - return -errno; - return 0; -} - -int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) { - _cleanup_(acl_freep) acl_t old; - acl_entry_t i; - int r; - - old = acl_get_file(path, type); - if (!old) - return -errno; - - for (r = acl_get_entry(new, ACL_FIRST_ENTRY, &i); - r > 0; - r = acl_get_entry(new, ACL_NEXT_ENTRY, &i)) { - - acl_entry_t j; - - r = find_acl_entry(old, i, &j); - if (r < 0) - return r; - if (r == 0) - if (acl_create_entry(&old, &j) < 0) - return -errno; - - if (acl_copy_entry(j, i) < 0) - return -errno; - } - if (r < 0) - return -errno; - - *acl = old; - 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 deleted file mode 100644 index 396e9e067e..0000000000 --- a/src/shared/acl-util.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_ACL - -#include -#include -#include - -#include "macro.h" - -int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry); -int calc_acl_mask_if_needed(acl_t *acl_p); -int add_base_acls_if_needed(acl_t *acl_p, const char *path); -int 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. */ -DEFINE_TRIVIAL_CLEANUP_FUNC(acl_t, acl_free); -#define acl_free_charp acl_free -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, acl_free_charp); -#define acl_free_uid_tp acl_free -DEFINE_TRIVIAL_CLEANUP_FUNC(uid_t*, acl_free_uid_tp); -#define acl_free_gid_tp acl_free -DEFINE_TRIVIAL_CLEANUP_FUNC(gid_t*, acl_free_gid_tp); - -#endif diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c deleted file mode 100644 index 6779691c28..0000000000 --- a/src/shared/acpi-fpdt.c +++ /dev/null @@ -1,164 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#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]; - uint32_t length; - uint8_t revision; - uint8_t checksum; - char oem_id[6]; - char oem_table_id[8]; - uint32_t oem_revision; - char asl_compiler_id[4]; - uint32_t asl_compiler_revision; -}; - -enum { - ACPI_FPDT_TYPE_BOOT = 0, - ACPI_FPDT_TYPE_S3PERF = 1, -}; - -struct acpi_fpdt_header { - uint16_t type; - uint8_t length; - uint8_t revision; - uint8_t reserved[4]; - uint64_t ptr; -}; - -struct acpi_fpdt_boot_header { - char signature[4]; - uint32_t length; -}; - -enum { - ACPI_FPDT_S3PERF_RESUME_REC = 0, - ACPI_FPDT_S3PERF_SUSPEND_REC = 1, - ACPI_FPDT_BOOT_REC = 2, -}; - -struct acpi_fpdt_boot { - uint16_t type; - uint8_t length; - uint8_t revision; - uint8_t reserved[4]; - uint64_t reset_end; - uint64_t load_start; - uint64_t startup_start; - uint64_t exit_services_entry; - uint64_t exit_services_exit; -}; - -int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { - _cleanup_free_ char *buf = NULL; - struct acpi_table_header *tbl; - size_t l = 0; - struct acpi_fpdt_header *rec; - int r; - uint64_t ptr = 0; - _cleanup_close_ int fd = -1; - struct acpi_fpdt_boot_header hbrec; - struct acpi_fpdt_boot brec; - - r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l); - if (r < 0) - return r; - - if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header)) - return -EINVAL; - - tbl = (struct acpi_table_header *)buf; - if (l != tbl->length) - return -EINVAL; - - if (memcmp(tbl->signature, "FPDT", 4) != 0) - return -EINVAL; - - /* find Firmware Basic Boot Performance Pointer Record */ - for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header)); - (char *)rec < buf + l; - rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) { - if (rec->length <= 0) - break; - if (rec->type != ACPI_FPDT_TYPE_BOOT) - continue; - if (rec->length != sizeof(struct acpi_fpdt_header)) - continue; - - ptr = rec->ptr; - break; - } - - if (ptr == 0) - return -ENODATA; - - /* read Firmware Basic Boot Performance Data Record */ - fd = open("/dev/mem", O_CLOEXEC|O_RDONLY); - if (fd < 0) - return -errno; - - l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr); - if (l != sizeof(struct acpi_fpdt_boot_header)) - return -EINVAL; - - if (memcmp(hbrec.signature, "FBPT", 4) != 0) - return -EINVAL; - - if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot)) - return -EINVAL; - - l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header)); - if (l != sizeof(struct acpi_fpdt_boot)) - return -EINVAL; - - if (brec.length != sizeof(struct acpi_fpdt_boot)) - return -EINVAL; - - if (brec.type != ACPI_FPDT_BOOT_REC) - return -EINVAL; - - if (brec.exit_services_exit == 0) - /* Non-UEFI compatible boot. */ - return -ENODATA; - - if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start) - return -EINVAL; - if (brec.exit_services_exit > NSEC_PER_HOUR) - return -EINVAL; - - if (loader_start) - *loader_start = brec.startup_start / 1000; - if (loader_exit) - *loader_exit = brec.exit_services_exit / 1000; - - return 0; -} diff --git a/src/shared/acpi-fpdt.h b/src/shared/acpi-fpdt.h deleted file mode 100644 index fc28175d0a..0000000000 --- a/src/shared/acpi-fpdt.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit); diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c deleted file mode 100644 index edd695fd23..0000000000 --- a/src/shared/apparmor-util.c +++ /dev/null @@ -1,39 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "alloc-util.h" -#include "apparmor-util.h" -#include "fileio.h" -#include "parse-util.h" - -bool mac_apparmor_use(void) { - static int cached_use = -1; - - if (cached_use < 0) { - _cleanup_free_ char *p = NULL; - - cached_use = - read_one_line_file("/sys/module/apparmor/parameters/enabled", &p) >= 0 && - parse_boolean(p) > 0; - } - - return cached_use; -} diff --git a/src/shared/apparmor-util.h b/src/shared/apparmor-util.h deleted file mode 100644 index 524f740152..0000000000 --- a/src/shared/apparmor-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -bool mac_apparmor_use(void); diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c deleted file mode 100644 index 65151b19a6..0000000000 --- a/src/shared/ask-password-api.c +++ /dev/null @@ -1,734 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "random-util.h" -#include "signal-util.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 "utf8.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; - - serial = add_key("user", keyname, p, n, 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) { - - if (ttyfd < 0) - return; - - while (p > 0) { - p--; - - loop_write(ttyfd, "\b \b", 3, false); - } -} - -int ask_password_tty( - const char *message, - const char *keyname, - usec_t until, - AskPasswordFlags flags, - const char *flag_file, - char **ret) { - - struct termios old_termios, new_termios; - char passphrase[LINE_MAX + 1] = {}, *x; - size_t p = 0, codepoint = 0; - int r; - _cleanup_close_ int ttyfd = -1, notify = -1; - struct pollfd pollfd[2]; - bool reset_tty = false; - bool dirty = false; - enum { - POLL_TTY, - POLL_INOTIFY - }; - - assert(ret); - - if (flags & ASK_PASSWORD_NO_TTY) - return -EUNATCH; - - if (!message) - message = "Password:"; - - if (flag_file) { - notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if (notify < 0) { - r = -errno; - goto finish; - } - - if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) { - r = -errno; - goto finish; - } - } - - ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (ttyfd >= 0) { - - if (tcgetattr(ttyfd, &old_termios) < 0) { - r = -errno; - goto finish; - } - - if (colors_enabled()) - loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false); - loop_write(ttyfd, message, strlen(message), false); - loop_write(ttyfd, " ", 1, false); - if (colors_enabled()) - loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false); - - new_termios = old_termios; - new_termios.c_lflag &= ~(ICANON|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; - - if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) { - r = -errno; - goto finish; - } - - reset_tty = true; - } - - zero(pollfd); - pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO; - pollfd[POLL_TTY].events = POLLIN; - pollfd[POLL_INOTIFY].fd = notify; - pollfd[POLL_INOTIFY].events = POLLIN; - - for (;;) { - char c; - int sleep_for = -1, k; - ssize_t n; - - if (until > 0) { - usec_t y; - - y = now(CLOCK_MONOTONIC); - - if (y > until) { - r = -ETIME; - goto finish; - } - - sleep_for = (int) ((until - y) / USEC_PER_MSEC); - } - - if (flag_file) - if (access(flag_file, F_OK) < 0) { - r = -errno; - goto finish; - } - - k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); - if (k < 0) { - if (errno == EINTR) - continue; - - r = -errno; - goto finish; - } else if (k == 0) { - r = -ETIME; - goto finish; - } - - if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); - - if (pollfd[POLL_TTY].revents == 0) - continue; - - n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1); - if (n < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - r = -errno; - goto finish; - - } else if (n == 0) - break; - - if (c == '\n') - break; - else if (c == 21) { /* C-u */ - - if (!(flags & ASK_PASSWORD_SILENT)) - backspace_chars(ttyfd, p); - p = 0; - - } else if (c == '\b' || c == 127) { - - if (p > 0) { - - if (!(flags & ASK_PASSWORD_SILENT)) - backspace_chars(ttyfd, 1); - - p--; - } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) { - - 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 ... */ - if (ttyfd >= 0) - loop_write(ttyfd, "(no echo) ", 10, false); - - } else if (ttyfd >= 0) - loop_write(ttyfd, "\a", 1, false); - - } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) { - - backspace_chars(ttyfd, p); - flags |= ASK_PASSWORD_SILENT; - - /* ... or by pressing TAB at any time. */ - - if (ttyfd >= 0) - loop_write(ttyfd, "(no echo) ", 10, false); - } else { - if (p >= sizeof(passphrase)-1) { - loop_write(ttyfd, "\a", 1, false); - continue; - } - - passphrase[p++] = c; - - if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) { - n = utf8_encoded_valid_unichar(passphrase + codepoint); - if (n >= 0) { - codepoint = p; - 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; - } - - if (keyname) - (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x)); - - *ret = x; - r = 0; - -finish: - if (ttyfd >= 0 && reset_tty) { - loop_write(ttyfd, "\n", 1, false); - tcsetattr(ttyfd, TCSADRAIN, &old_termios); - } - - return r; -} - -static int create_socket(char **name) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - }; - _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 -errno; - - snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); - - RUN_WITH_UMASK(0177) { - if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - } - - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - return -errno; - - c = strdup(sa.un.sun_path); - if (!c) - return -ENOMEM; - - *name = c; - - r = fd; - fd = -1; - - return r; -} - -int ask_password_agent( - const char *message, - const char *icon, - const char *id, - const char *keyname, - usec_t until, - AskPasswordFlags flags, - char ***ret) { - - enum { - FD_SOCKET, - FD_SIGNAL, - _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_free_ char *socket_name = NULL; - _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; - struct pollfd pollfd[_FD_MAX]; - sigset_t mask, oldmask; - int r; - - 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); - - (void) mkdir_p_label("/run/systemd/ask-password", 0755); - - fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); - if (fd < 0) { - r = fd; - goto finish; - } - - (void) fchmod(fd, 0644); - - f = fdopen(fd, "w"); - if (!f) { - r = -errno; - goto finish; - } - - fd = -1; - - signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (signal_fd < 0) { - r = -errno; - goto finish; - } - - socket_fd = create_socket(&socket_name); - if (socket_fd < 0) { - r = socket_fd; - goto finish; - } - - fprintf(f, - "[Ask]\n" - "PID="PID_FMT"\n" - "Socket=%s\n" - "AcceptCached=%i\n" - "Echo=%i\n" - "NotAfter="USEC_FMT"\n", - getpid(), - socket_name, - (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0, - (flags & ASK_PASSWORD_ECHO) ? 1 : 0, - until); - - if (message) - fprintf(f, "Message=%s\n", message); - - if (icon) - fprintf(f, "Icon=%s\n", icon); - - if (id) - fprintf(f, "Id=%s\n", id); - - r = fflush_and_check(f); - if (r < 0) - goto finish; - - memcpy(final, temp, sizeof(temp)); - - final[sizeof(final)-11] = 'a'; - final[sizeof(final)-10] = 's'; - final[sizeof(final)-9] = 'k'; - - if (rename(temp, final) < 0) { - r = -errno; - goto finish; - } - - zero(pollfd); - pollfd[FD_SOCKET].fd = socket_fd; - pollfd[FD_SOCKET].events = POLLIN; - pollfd[FD_SIGNAL].fd = signal_fd; - pollfd[FD_SIGNAL].events = POLLIN; - - for (;;) { - char passphrase[LINE_MAX+1]; - struct msghdr msghdr; - struct iovec iovec; - struct ucred *ucred; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - ssize_t n; - int k; - usec_t t; - - t = now(CLOCK_MONOTONIC); - - if (until > 0 && until <= t) { - r = -ETIME; - goto finish; - } - - k = poll(pollfd, _FD_MAX, until > 0 ? (int) ((until-t)/USEC_PER_MSEC) : -1); - if (k < 0) { - if (errno == EINTR) - continue; - - r = -errno; - goto finish; - } - - if (k <= 0) { - r = -ETIME; - goto finish; - } - - if (pollfd[FD_SIGNAL].revents & POLLIN) { - r = -EINTR; - goto finish; - } - - if (pollfd[FD_SOCKET].revents != POLLIN) { - r = -EIO; - goto finish; - } - - zero(iovec); - iovec.iov_base = passphrase; - iovec.iov_len = sizeof(passphrase); - - zero(control); - zero(msghdr); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - msghdr.msg_control = &control; - msghdr.msg_controllen = sizeof(control); - - n = recvmsg(socket_fd, &msghdr, 0); - if (n < 0) { - if (errno == EAGAIN || - errno == EINTR) - continue; - - r = -errno; - goto finish; - } - - cmsg_close_all(&msghdr); - - if (n <= 0) { - log_debug("Message too short"); - continue; - } - - if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || - control.cmsghdr.cmsg_level != SOL_SOCKET || - control.cmsghdr.cmsg_type != SCM_CREDENTIALS || - control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_debug("Received message without credentials. Ignoring."); - continue; - } - - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - if (ucred->uid != 0) { - log_debug("Got request from unprivileged user. Ignoring."); - continue; - } - - if (passphrase[0] == '+') { - /* An empty message refers to the empty password */ - if (n == 1) - l = strv_new("", NULL); - else - l = strv_parse_nulstr(passphrase+1, n-1); - memory_erase(passphrase, n); - if (!l) { - r = -ENOMEM; - goto finish; - } - - if (strv_length(l) <= 0) { - l = strv_free(l); - log_debug("Invalid packet"); - continue; - } - - break; - } - - if (passphrase[0] == '-') { - r = -ECANCELED; - goto finish; - } - - 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) - (void) unlink(socket_name); - - (void) unlink(temp); - - if (final[0]) - (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, - const char *keyname, - usec_t until, - AskPasswordFlags flags, - char ***ret) { - - int r; - - assert(ret); - - if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) { - r = ask_password_keyring(keyname, flags, ret); - if (r != -ENOKEY) - return r; - } - - 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; - - 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 deleted file mode 100644 index 9d7f65130c..0000000000 --- a/src/shared/ask-password-api.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 . -***/ - -#include - -#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 deleted file mode 100644 index 59a34a9d11..0000000000 --- a/src/shared/base-filesystem.c +++ /dev/null @@ -1,129 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "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 { - const char *dir; - 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, true }, - { "sbin", 0, "usr/sbin\0", NULL }, - { "usr", 0755, NULL, NULL }, - { "var", 0755, NULL, NULL }, - { "etc", 0755, NULL, NULL }, -#if defined(__i386__) || defined(__x86_64__) - { "lib64", 0, "usr/lib/x86_64-linux-gnu\0" - "usr/lib64\0", "ld-linux-x86-64.so.2" }, -#endif -}; - -int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { - _cleanup_close_ int fd = -1; - unsigned i; - int r = 0; - - fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) - return log_error_errno(errno, "Failed to open root file system: %m"); - - for (i = 0; i < ELEMENTSOF(table); i ++) { - if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) - continue; - - if (table[i].target) { - const char *target = NULL, *s; - - /* check if one of the targets exists */ - NULSTR_FOREACH(s, table[i].target) { - if (faccessat(fd, s, F_OK, AT_SYMLINK_NOFOLLOW) < 0) - continue; - - /* check if a specific file exists at the target path */ - if (table[i].exists) { - _cleanup_free_ char *p = NULL; - - p = strjoin(s, "/", table[i].exists, NULL); - if (!p) - return log_oom(); - - if (faccessat(fd, p, F_OK, AT_SYMLINK_NOFOLLOW) < 0) - continue; - } - - target = s; - break; - } - - if (!target) - continue; - - r = symlinkat(target, fd, table[i].dir); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir); - - if (uid != UID_INVALID || gid != UID_INVALID) { - if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) - return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir); - } - - continue; - } - - RUN_WITH_UMASK(0000) - r = mkdirat(fd, table[i].dir, table[i].mode); - 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) - return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir); - } - } - - return 0; -} diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h deleted file mode 100644 index 49599f0a60..0000000000 --- a/src/shared/base-filesystem.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int base_filesystem_create(const char *root, uid_t uid, gid_t gid); diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c deleted file mode 100644 index 7e0152761c..0000000000 --- a/src/shared/boot-timestamps.c +++ /dev/null @@ -1,64 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#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; - int r; - dual_timestamp _n; - - assert(firmware); - assert(loader); - - if (!n) { - dual_timestamp_get(&_n); - n = &_n; - } - - r = acpi_get_boot_usec(&x, &y); - if (r < 0) { - r = efi_loader_get_boot_usec(&x, &y); - if (r < 0) - return r; - } - - /* Let's convert this to timestamps where the firmware - * began/loader began working. To make this more confusing: - * since usec_t is unsigned and the kernel's monotonic clock - * begins at kernel initialization we'll actually initialize - * the monotonic timestamps here as negative of the actual - * value. */ - - firmware->monotonic = y; - loader->monotonic = y - x; - - a = n->monotonic + firmware->monotonic; - firmware->realtime = n->realtime > a ? n->realtime - a : 0; - - a = n->monotonic + loader->monotonic; - loader->realtime = n->realtime > a ? n->realtime - a : 0; - - return 0; -} diff --git a/src/shared/boot-timestamps.h b/src/shared/boot-timestamps.h deleted file mode 100644 index 6f691026be..0000000000 --- a/src/shared/boot-timestamps.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c deleted file mode 100644 index ea020b517b..0000000000 --- a/src/shared/bus-unit-util.c +++ /dev/null @@ -1,1324 +0,0 @@ -/*** - 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 . -***/ - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-unit-util.h" -#include "bus-util.h" -#include "cgroup-util.h" -#include "env-util.h" -#include "escape.h" -#include "hashmap.h" -#include "list.h" -#include "locale-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "rlimit-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "syslog-util.h" -#include "terminal-util.h" -#include "utf8.h" -#include "util.h" - -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { - assert(message); - assert(u); - - u->machine = NULL; - - return sd_bus_message_read( - message, - "(ssssssouso)", - &u->id, - &u->description, - &u->load_state, - &u->active_state, - &u->sub_state, - &u->following, - &u->unit_path, - &u->job_id, - &u->job_type, - &u->job_path); -} - -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { - const char *eq, *field; - int r, rl; - - assert(m); - assert(assignment); - - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - field = strndupa(assignment, eq - assignment); - eq++; - - if (streq(field, "CPUQuota")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); - else { - r = parse_percent(eq); - if (r <= 0) { - log_error_errno(r, "CPU quota '%s' invalid.", eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); - } - - goto finish; - - } else if (streq(field, "EnvironmentFile")) { - - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); - goto finish; - - } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { - char *n; - usec_t t; - size_t l; - - r = parse_sec(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - - l = strlen(field); - n = newa(char, l + 2); - if (!n) - return log_oom(); - - /* Change suffix Sec → USec */ - strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_append(m, "sv", n, "t", t); - goto finish; - - } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) { - uint64_t bytes; - - if (isempty(eq) || streq(eq, "infinity")) - bytes = CGROUP_LIMIT_MAX; - else { - r = parse_percent(eq); - if (r >= 0) { - char *n; - - /* When this is a percentage we'll convert this into a relative value in the range - * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related - * ones). This way the physical memory size can be determined server-side */ - - n = strjoina(field, "Scale"); - r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; - - } else { - r = parse_size(eq, 1024, &bytes); - if (r < 0) - return log_error_errno(r, "Failed to parse bytes specification %s", assignment); - } - } - - r = sd_bus_message_append(m, "sv", field, "t", bytes); - goto finish; - } else if (streq(field, "TasksMax")) { - uint64_t t; - - if (isempty(eq) || streq(eq, "infinity")) - t = (uint64_t) -1; - else { - r = parse_percent(eq); - if (r >= 0) { - r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; - } else { - r = safe_atou64(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment); - } - - } - - r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); - goto finish; - } - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - rl = rlimit_from_string(field); - if (rl >= 0) { - const char *sn; - struct rlimit l; - - r = rlimit_parse(rl, eq, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", eq); - - r = sd_bus_message_append(m, "v", "t", l.rlim_max); - 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_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); - - } else if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute")) { - - r = parse_boolean(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); - - r = sd_bus_message_append(m, "v", "b", r); - - } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) { - uint64_t u; - - r = cg_weight_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { - uint64_t u; - - r = cg_blkio_weight_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, - "User", "Group", "DevicePolicy", "KillMode", - "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "StandardInput", "StandardOutput", "StandardError", - "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome", "SELinuxContext")) - r = sd_bus_message_append(m, "v", "s", eq); - - 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); - else { - const char *path, *rwm, *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); - } - - } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *bandwidth, *e; - uint64_t bytes; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - bandwidth = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - if (streq(bandwidth, "infinity")) { - bytes = CGROUP_LIMIT_MAX; - } else { - r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) { - log_error("Failed to parse byte value %s.", bandwidth); - return -EINVAL; - } - } - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); - } - - } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *weight, *e; - uint64_t u; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - weight = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = safe_atou64(weight, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, weight); - return -EINVAL; - } - r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); - } - - } else if (streq(field, "Nice")) { - int32_t i; - - r = safe_atoi32(eq, &i); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", i); - - } 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_close_container(m); - - } else if (streq(field, "KillSignal")) { - int sig; - - sig = signal_from_string_try_harder(eq); - if (sig < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", sig); - - } 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", - "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { - 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); - return -EINVAL; - } - -finish: - 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); - - return 0; -} - -typedef struct BusWaitForJobs { - sd_bus *bus; - Set *jobs; - - char *name; - char *result; - - sd_bus_slot *slot_job_removed; - sd_bus_slot *slot_disconnected; -} BusWaitForJobs; - -static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(m); - - log_error("Warning! D-Bus connection terminated."); - sd_bus_close(sd_bus_message_get_bus(m)); - - return 0; -} - -static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char *path, *unit, *result; - BusWaitForJobs *d = userdata; - uint32_t id; - char *found; - int r; - - assert(m); - assert(d); - - r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - found = set_remove(d->jobs, (char*) path); - if (!found) - return 0; - - free(found); - - if (!isempty(result)) - d->result = strdup(result); - - if (!isempty(unit)) - d->name = strdup(unit); - - return 0; -} - -void bus_wait_for_jobs_free(BusWaitForJobs *d) { - if (!d) - return; - - set_free_free(d->jobs); - - sd_bus_slot_unref(d->slot_disconnected); - sd_bus_slot_unref(d->slot_job_removed); - - sd_bus_unref(d->bus); - - free(d->name); - free(d->result); - - free(d); -} - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { - _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; - int r; - - assert(bus); - assert(ret); - - d = new0(BusWaitForJobs, 1); - if (!d) - return -ENOMEM; - - d->bus = sd_bus_ref(bus); - - /* When we are a bus client we match by sender. Direct - * connections OTOH have no initialized sender field, and - * hence we ignore the sender then */ - r = sd_bus_add_match( - bus, - &d->slot_job_removed, - bus->bus_client ? - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'" : - "type='signal'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, d); - if (r < 0) - return r; - - r = sd_bus_add_match( - bus, - &d->slot_disconnected, - "type='signal'," - "sender='org.freedesktop.DBus.Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - match_disconnected, d); - if (r < 0) - return r; - - *ret = d; - d = NULL; - - return 0; -} - -static int bus_process_wait(sd_bus *bus) { - int r; - - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (r > 0) - return 0; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } -} - -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { - _cleanup_free_ char *dbus_path = NULL; - - assert(d); - assert(d->name); - assert(result); - - dbus_path = unit_dbus_path_from_name(d->name); - if (!dbus_path) - return -ENOMEM; - - return sd_bus_get_property_string(d->bus, - "org.freedesktop.systemd1", - dbus_path, - "org.freedesktop.systemd1.Service", - "Result", - NULL, - result); -} - -static const struct { - const char *result, *explanation; -} explanations [] = { - { "resources", "of unavailable resources or another system error" }, - { "timeout", "a timeout was exceeded" }, - { "exit-code", "the control process exited with error code" }, - { "signal", "a fatal signal was delivered to the control process" }, - { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, - { "watchdog", "the service failed to send watchdog ping" }, - { "start-limit", "start of the service was attempted too often" } -}; - -static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { - _cleanup_free_ char *service_shell_quoted = NULL; - const char *systemctl = "systemctl", *journalctl = "journalctl"; - - assert(service); - - service_shell_quoted = shell_maybe_quote(service); - - if (extra_args && extra_args[1]) { - _cleanup_free_ char *t; - - t = strv_join((char**) extra_args, " "); - systemctl = strjoina("systemctl ", t ? : ""); - journalctl = strjoina("journalctl ", t ? : ""); - } - - if (!isempty(result)) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(explanations); ++i) - if (streq(result, explanations[i].result)) - break; - - if (i < ELEMENTSOF(explanations)) { - log_error("Job for %s failed because %s.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - explanations[i].explanation, - systemctl, - service_shell_quoted ?: "", - journalctl); - goto finish; - } - } - - log_error("Job for %s failed.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - systemctl, - service_shell_quoted ?: "", - journalctl); - -finish: - /* For some results maybe additional explanation is required */ - if (streq_ptr(result, "start-limit")) - log_info("To force a start use \"%1$s reset-failed %2$s\"\n" - "followed by \"%1$s start %2$s\" again.", - systemctl, - service_shell_quoted ?: ""); -} - -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { - int r = 0; - - assert(d->result); - - if (!quiet) { - if (streq(d->result, "canceled")) - log_error("Job for %s canceled.", strna(d->name)); - else if (streq(d->result, "timeout")) - log_error("Job for %s timed out.", strna(d->name)); - else if (streq(d->result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); - else if (streq(d->result, "invalid")) - log_error("%s is not active, cannot reload.", strna(d->name)); - else if (streq(d->result, "assert")) - log_error("Assertion failed on job for %s.", strna(d->name)); - else if (streq(d->result, "unsupported")) - log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { - if (d->name) { - int q; - _cleanup_free_ char *result = NULL; - - q = bus_job_get_service_result(d, &result); - if (q < 0) - log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - - log_job_error_with_service_result(d->name, result, extra_args); - } else - log_error("Job failed. See \"journalctl -xe\" for details."); - } - } - - if (streq(d->result, "canceled")) - r = -ECANCELED; - else if (streq(d->result, "timeout")) - r = -ETIME; - else if (streq(d->result, "dependency")) - r = -EIO; - else if (streq(d->result, "invalid")) - r = -ENOEXEC; - else if (streq(d->result, "assert")) - r = -EPROTO; - else if (streq(d->result, "unsupported")) - r = -EOPNOTSUPP; - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) - r = -EIO; - - return r; -} - -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { - int r = 0; - - assert(d); - - while (!set_isempty(d->jobs)) { - int q; - - q = bus_process_wait(d->bus); - if (q < 0) - return log_error_errno(q, "Failed to wait for response: %m"); - - if (d->result) { - 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) - r = q; - - log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); - } - - d->name = mfree(d->name); - d->result = mfree(d->result); - } - - return r; -} - -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { - int r; - - assert(d); - - r = set_ensure_allocated(&d->jobs, &string_hash_ops); - if (r < 0) - return r; - - return set_put_strdup(d->jobs, path); -} - -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { - int r; - - r = bus_wait_for_jobs_add(d, path); - if (r < 0) - return log_oom(); - - return bus_wait_for_jobs(d, quiet, NULL); -} - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { - const char *type, *path, *source; - int r; - - /* changes is dereferenced when calling unit_file_dump_changes() later, - * so we have to make sure this is not NULL. */ - assert(changes); - assert(n_changes); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { - /* We expect only "success" changes to be sent over the bus. - Hence, reject anything negative. */ - UnitFileChangeType ch = unit_file_change_type_from_string(type); - - if (ch < 0) { - log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); - continue; - } - - r = unit_file_changes_add(changes, n_changes, ch, path, source); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - unit_file_dump_changes(0, NULL, *changes, *n_changes, false); - return 0; -} - -struct CGroupInfo { - char *cgroup_path; - bool is_const; /* If false, cgroup_path should be free()'d */ - - Hashmap *pids; /* PID → process name */ - bool done; - - struct CGroupInfo *parent; - LIST_FIELDS(struct CGroupInfo, siblings); - LIST_HEAD(struct CGroupInfo, children); - size_t n_children; -}; - -static bool IS_ROOT(const char *p) { - return isempty(p) || streq(p, "/"); -} - -static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { - struct CGroupInfo *parent = NULL, *cg; - int r; - - assert(cgroups); - assert(ret); - - if (IS_ROOT(path)) - path = "/"; - - cg = hashmap_get(cgroups, path); - if (cg) { - *ret = cg; - return 0; - } - - if (!IS_ROOT(path)) { - const char *e, *pp; - - e = strrchr(path, '/'); - if (!e) - return -EINVAL; - - pp = strndupa(path, e - path); - if (!pp) - return -ENOMEM; - - r = add_cgroup(cgroups, pp, false, &parent); - if (r < 0) - return r; - } - - cg = new0(struct CGroupInfo, 1); - if (!cg) - return -ENOMEM; - - if (is_const) - cg->cgroup_path = (char*) path; - else { - cg->cgroup_path = strdup(path); - if (!cg->cgroup_path) { - free(cg); - return -ENOMEM; - } - } - - cg->is_const = is_const; - cg->parent = parent; - - r = hashmap_put(cgroups, cg->cgroup_path, cg); - if (r < 0) { - if (!is_const) - free(cg->cgroup_path); - free(cg); - return r; - } - - if (parent) { - LIST_PREPEND(siblings, parent->children, cg); - parent->n_children++; - } - - *ret = cg; - return 1; -} - -static int add_process( - Hashmap *cgroups, - const char *path, - pid_t pid, - const char *name) { - - struct CGroupInfo *cg; - int r; - - assert(cgroups); - assert(name); - assert(pid > 0); - - r = add_cgroup(cgroups, path, true, &cg); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); - if (r < 0) - return r; - - return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); -} - -static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { - assert(cgroups); - assert(cg); - - while (cg->children) - remove_cgroup(cgroups, cg->children); - - hashmap_remove(cgroups, cg->cgroup_path); - - if (!cg->is_const) - free(cg->cgroup_path); - - hashmap_free(cg->pids); - - if (cg->parent) - LIST_REMOVE(siblings, cg->parent->children, cg); - - free(cg); -} - -static int cgroup_info_compare_func(const void *a, const void *b) { - const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b; - - assert(x); - assert(y); - - return strcmp(x->cgroup_path, y->cgroup_path); -} - -static int dump_processes( - Hashmap *cgroups, - const char *cgroup_path, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - - struct CGroupInfo *cg; - int r; - - assert(prefix); - - if (IS_ROOT(cgroup_path)) - cgroup_path = "/"; - - cg = hashmap_get(cgroups, cgroup_path); - if (!cg) - return 0; - - if (!hashmap_isempty(cg->pids)) { - const char *name; - size_t n = 0, i; - pid_t *pids; - void *pidp; - Iterator j; - int width; - - /* Order processes by their PID */ - pids = newa(pid_t, hashmap_size(cg->pids)); - - HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) - pids[n++] = PTR_TO_PID(pidp); - - assert(n == hashmap_size(cg->pids)); - qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); - - width = DECIMAL_STR_WIDTH(pids[n-1]); - - for (i = 0; i < n; i++) { - _cleanup_free_ char *e = NULL; - const char *special; - bool more; - - name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); - assert(name); - - if (n_columns != 0) { - unsigned k; - - k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); - - e = ellipsize(name, k, 100); - if (e) - name = e; - } - - more = i+1 < n || cg->children; - special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); - - fprintf(stdout, "%s%s%*"PID_PRI" %s\n", - prefix, - special, - width, pids[i], - name); - } - } - - if (cg->children) { - struct CGroupInfo **children, *child; - size_t n = 0, i; - - /* Order subcgroups by their name */ - children = newa(struct CGroupInfo*, cg->n_children); - LIST_FOREACH(siblings, child, cg->children) - children[n++] = child; - assert(n == cg->n_children); - qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func); - - if (n_columns != 0) - n_columns = MAX(LESS_BY(n_columns, 2U), 20U); - - for (i = 0; i < n; i++) { - _cleanup_free_ char *pp = NULL; - const char *name, *special; - bool more; - - child = children[i]; - - name = strrchr(child->cgroup_path, '/'); - if (!name) - return -EINVAL; - name++; - - more = i+1 < n; - special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); - - fputs(prefix, stdout); - fputs(special, stdout); - fputs(name, stdout); - fputc('\n', stdout); - - special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE); - - pp = strappend(prefix, special); - if (!pp) - return -ENOMEM; - - r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); - if (r < 0) - return r; - } - } - - cg->done = true; - return 0; -} - -static int dump_extra_processes( - Hashmap *cgroups, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - - _cleanup_free_ pid_t *pids = NULL; - _cleanup_hashmap_free_ Hashmap *names = NULL; - struct CGroupInfo *cg; - size_t n_allocated = 0, n = 0, k; - Iterator i; - int width, r; - - /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as - * combined, sorted, linear list. */ - - HASHMAP_FOREACH(cg, cgroups, i) { - const char *name; - void *pidp; - Iterator j; - - if (cg->done) - continue; - - if (hashmap_isempty(cg->pids)) - continue; - - r = hashmap_ensure_allocated(&names, &trivial_hash_ops); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) - return -ENOMEM; - - HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { - pids[n++] = PTR_TO_PID(pidp); - - r = hashmap_put(names, pidp, (void*) name); - if (r < 0) - return r; - } - } - - if (n == 0) - return 0; - - qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); - width = DECIMAL_STR_WIDTH(pids[n-1]); - - for (k = 0; k < n; k++) { - _cleanup_free_ char *e = NULL; - const char *name; - - name = hashmap_get(names, PID_TO_PTR(pids[k])); - assert(name); - - if (n_columns != 0) { - unsigned z; - - z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); - - e = ellipsize(name, z, 100); - if (e) - name = e; - } - - fprintf(stdout, "%s%s %*" PID_PRI " %s\n", - prefix, - special_glyph(TRIANGULAR_BULLET), - width, pids[k], - name); - } - - return 0; -} - -int unit_show_processes( - sd_bus *bus, - const char *unit, - const char *cgroup_path, - const char *prefix, - unsigned n_columns, - OutputFlags flags, - sd_bus_error *error) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Hashmap *cgroups = NULL; - struct CGroupInfo *cg; - int r; - - assert(bus); - assert(unit); - - if (flags & OUTPUT_FULL_WIDTH) - n_columns = 0; - else if (n_columns <= 0) - n_columns = columns(); - - prefix = strempty(prefix); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitProcesses", - error, - &reply, - "s", - unit); - if (r < 0) - return r; - - cgroups = hashmap_new(&string_hash_ops); - if (!cgroups) - return -ENOMEM; - - r = sd_bus_message_enter_container(reply, 'a', "(sus)"); - if (r < 0) - goto finish; - - for (;;) { - const char *path = NULL, *name = NULL; - uint32_t pid; - - r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); - if (r < 0) - goto finish; - if (r == 0) - break; - - r = add_process(cgroups, path, pid, name); - if (r < 0) - goto finish; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto finish; - - r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); - if (r < 0) - goto finish; - - r = dump_extra_processes(cgroups, prefix, n_columns, flags); - -finish: - while ((cg = hashmap_first(cgroups))) - remove_cgroup(cgroups, cg); - - hashmap_free(cgroups); - - return r; -} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h deleted file mode 100644 index c0c172f336..0000000000 --- a/src/shared/bus-unit-util.h +++ /dev/null @@ -1,57 +0,0 @@ -#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 . -***/ - -#include "sd-bus.h" - -#include "output-mode.h" -#include "install.h" - -typedef struct UnitInfo { - const char *machine; - const char *id; - const char *description; - const char *load_state; - const char *active_state; - const char *sub_state; - const char *following; - const char *unit_path; - uint32_t job_id; - const char *job_type; - const char *job_path; -} UnitInfo; - -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); - -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); - -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, const char* const* 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); - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); - -int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c deleted file mode 100644 index 52410999cf..0000000000 --- a/src/shared/bus-util.c +++ /dev/null @@ -1,1550 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus-protocol.h" -#include "sd-bus.h" -#include "sd-daemon.h" -#include "sd-event.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-label.h" -#include "bus-message.h" -#include "bus-util.h" -#include "def.h" -#include "escape.h" -#include "fd-util.h" -#include "missing.h" -#include "parse-util.h" -#include "proc-cmdline.h" -#include "rlimit-util.h" -#include "stdio-util.h" -#include "strv.h" -#include "user-util.h" - -static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - sd_event *e = userdata; - - assert(m); - assert(e); - - sd_bus_close(sd_bus_message_get_bus(m)); - sd_event_exit(e, 0); - - return 1; -} - -int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { - _cleanup_free_ char *match = NULL; - const char *unique; - int r; - - assert(e); - assert(bus); - assert(name); - - /* We unregister the name here and then wait for the - * NameOwnerChanged signal for this event to arrive before we - * quit. We do this in order to make sure that any queued - * requests are still processed before we really exit. */ - - r = sd_bus_get_unique_name(bus, &unique); - if (r < 0) - return r; - - r = asprintf(&match, - "sender='org.freedesktop.DBus'," - "type='signal'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "path='/org/freedesktop/DBus'," - "arg0='%s'," - "arg1='%s'," - "arg2=''", name, unique); - if (r < 0) - return -ENOMEM; - - r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e); - if (r < 0) - return r; - - r = sd_bus_release_name(bus, name); - if (r < 0) - return r; - - return 0; -} - -int bus_event_loop_with_idle( - sd_event *e, - sd_bus *bus, - const char *name, - usec_t timeout, - check_idle_t check_idle, - void *userdata) { - bool exiting = false; - int r, code; - - assert(e); - assert(bus); - assert(name); - - for (;;) { - bool idle; - - r = sd_event_get_state(e); - if (r < 0) - return r; - if (r == SD_EVENT_FINISHED) - break; - - if (check_idle) - idle = check_idle(userdata); - else - idle = true; - - r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout); - if (r < 0) - return r; - - if (r == 0 && !exiting && idle) { - - r = sd_bus_try_close(bus); - if (r == -EBUSY) - continue; - - /* Fallback for dbus1 connections: we - * unregister the name and wait for the - * response to come through for it */ - if (r == -EOPNOTSUPP) { - - /* Inform the service manager that we - * are going down, so that it will - * queue all further start requests, - * instead of assuming we are already - * running. */ - sd_notify(false, "STOPPING=1"); - - r = bus_async_unregister_and_exit(e, bus, name); - if (r < 0) - return r; - - exiting = true; - continue; - } - - if (r < 0) - return r; - - sd_event_exit(e, 0); - break; - } - } - - r = sd_event_get_exit_code(e, &code); - if (r < 0) - return r; - - return code; -} - -int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL; - int r, has_owner = 0; - - assert(c); - assert(name); - - r = sd_bus_call_method(c, - "org.freedesktop.DBus", - "/org/freedesktop/dbus", - "org.freedesktop.DBus", - "NameHasOwner", - error, - &rep, - "s", - name); - if (r < 0) - return r; - - r = sd_bus_message_read_basic(rep, 'b', &has_owner); - if (r < 0) - return sd_bus_error_set_errno(error, r); - - return has_owner; -} - -static int check_good_user(sd_bus_message *m, uid_t good_user) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - uid_t sender_uid; - int r; - - assert(m); - - if (good_user == UID_INVALID) - return 0; - - r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds); - if (r < 0) - return r; - - /* Don't trust augmented credentials for authorization */ - assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); - - r = sd_bus_creds_get_euid(creds, &sender_uid); - if (r < 0) - return r; - - return sender_uid == good_user; -} - -int bus_test_polkit( - sd_bus_message *call, - int capability, - const char *action, - const char **details, - uid_t good_user, - bool *_challenge, - sd_bus_error *e) { - - int r; - - assert(call); - assert(action); - - /* Tests non-interactively! */ - - r = check_good_user(call, good_user); - if (r != 0) - return r; - - r = sd_bus_query_sender_privilege(call, capability); - if (r < 0) - return r; - else if (r > 0) - return 1; -#ifdef ENABLE_POLKIT - else { - _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; - - sender = sd_bus_message_get_sender(call); - if (!sender) - return -EBADMSG; - - r = sd_bus_message_new_method_call( - call->bus, - &request, - "org.freedesktop.PolicyKit1", - "/org/freedesktop/PolicyKit1/Authority", - "org.freedesktop.PolicyKit1.Authority", - "CheckAuthorization"); - if (r < 0) - return r; - - r = sd_bus_message_append( - request, - "(sa{sv})s", - "system-bus-name", 1, "name", "s", sender, - action); - if (r < 0) - return r; - - r = sd_bus_message_open_container(request, 'a', "{ss}"); - if (r < 0) - return r; - - STRV_FOREACH_PAIR(k, v, details) { - r = sd_bus_message_append(request, "{ss}", *k, *v); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(request); - if (r < 0) - return r; - - r = sd_bus_message_append(request, "us", 0, NULL); - if (r < 0) - return r; - - r = sd_bus_call(call->bus, request, 0, e, &reply); - if (r < 0) { - /* Treat no PK available as access denied */ - if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { - sd_bus_error_free(e); - return -EACCES; - } - - return r; - } - - r = sd_bus_message_enter_container(reply, 'r', "bba{ss}"); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "bb", &authorized, &challenge); - if (r < 0) - return r; - - if (authorized) - return 1; - - if (_challenge) { - *_challenge = challenge; - return 0; - } - } -#endif - - return -EACCES; -} - -#ifdef ENABLE_POLKIT - -typedef struct AsyncPolkitQuery { - sd_bus_message *request, *reply; - sd_bus_message_handler_t callback; - void *userdata; - sd_bus_slot *slot; - Hashmap *registry; -} AsyncPolkitQuery; - -static void async_polkit_query_free(AsyncPolkitQuery *q) { - - if (!q) - return; - - sd_bus_slot_unref(q->slot); - - if (q->registry && q->request) - hashmap_remove(q->registry, q->request); - - sd_bus_message_unref(q->request); - sd_bus_message_unref(q->reply); - - free(q); -} - -static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - AsyncPolkitQuery *q = userdata; - int r; - - assert(reply); - assert(q); - - q->slot = sd_bus_slot_unref(q->slot); - q->reply = sd_bus_message_ref(reply); - - r = sd_bus_message_rewind(q->request, true); - if (r < 0) { - r = sd_bus_reply_method_errno(q->request, r, NULL); - goto finish; - } - - r = q->callback(q->request, q->userdata, &error_buffer); - r = bus_maybe_reply_error(q->request, r, &error_buffer); - -finish: - async_polkit_query_free(q); - - return r; -} - -#endif - -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) { - -#ifdef ENABLE_POLKIT - _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL; - AsyncPolkitQuery *q; - const char *sender, **k, **v; - sd_bus_message_handler_t callback; - void *userdata; - int c; -#endif - int r; - - assert(call); - assert(action); - assert(registry); - - r = check_good_user(call, good_user); - if (r != 0) - return r; - -#ifdef ENABLE_POLKIT - q = hashmap_get(*registry, call); - if (q) { - int authorized, challenge; - - /* This is the second invocation of this function, and - * there's already a response from polkit, let's - * process it */ - assert(q->reply); - - if (sd_bus_message_is_method_error(q->reply, NULL)) { - const sd_bus_error *e; - - /* Copy error from polkit reply */ - e = sd_bus_message_get_error(q->reply); - sd_bus_error_copy(error, e); - - /* Treat no PK available as access denied */ - if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) - return -EACCES; - - return -sd_bus_error_get_errno(e); - } - - r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}"); - if (r >= 0) - r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge); - - if (r < 0) - return r; - - if (authorized) - return 1; - - if (challenge) - return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required."); - - return -EACCES; - } -#endif - - r = sd_bus_query_sender_privilege(call, capability); - if (r < 0) - return r; - else if (r > 0) - return 1; - -#ifdef ENABLE_POLKIT - if (sd_bus_get_current_message(call->bus) != call) - return -EINVAL; - - callback = sd_bus_get_current_handler(call->bus); - if (!callback) - return -EINVAL; - - userdata = sd_bus_get_current_userdata(call->bus); - - sender = sd_bus_message_get_sender(call); - if (!sender) - return -EBADMSG; - - c = sd_bus_message_get_allow_interactive_authorization(call); - if (c < 0) - return c; - if (c > 0) - interactive = true; - - r = hashmap_ensure_allocated(registry, NULL); - if (r < 0) - return r; - - r = sd_bus_message_new_method_call( - call->bus, - &pk, - "org.freedesktop.PolicyKit1", - "/org/freedesktop/PolicyKit1/Authority", - "org.freedesktop.PolicyKit1.Authority", - "CheckAuthorization"); - if (r < 0) - return r; - - r = sd_bus_message_append( - pk, - "(sa{sv})s", - "system-bus-name", 1, "name", "s", sender, - action); - if (r < 0) - return r; - - r = sd_bus_message_open_container(pk, 'a', "{ss}"); - if (r < 0) - return r; - - STRV_FOREACH_PAIR(k, v, details) { - r = sd_bus_message_append(pk, "{ss}", *k, *v); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(pk); - if (r < 0) - return r; - - r = sd_bus_message_append(pk, "us", !!interactive, NULL); - if (r < 0) - return r; - - q = new0(AsyncPolkitQuery, 1); - if (!q) - return -ENOMEM; - - q->request = sd_bus_message_ref(call); - q->callback = callback; - q->userdata = userdata; - - r = hashmap_put(*registry, call, q); - if (r < 0) { - async_polkit_query_free(q); - return r; - } - - q->registry = *registry; - - r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0); - if (r < 0) { - async_polkit_query_free(q); - return r; - } - - return 0; -#endif - - return -EACCES; -} - -void bus_verify_polkit_async_registry_free(Hashmap *registry) { -#ifdef ENABLE_POLKIT - AsyncPolkitQuery *q; - - while ((q = hashmap_steal_first(registry))) - async_polkit_query_free(q); - - hashmap_free(registry); -#endif -} - -int bus_check_peercred(sd_bus *c) { - struct ucred ucred; - socklen_t l; - int fd; - - assert(c); - - fd = sd_bus_get_fd(c); - if (fd < 0) - return fd; - - l = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) - return -errno; - - if (l != sizeof(struct ucred)) - return -E2BIG; - - if (ucred.uid != 0 && ucred.uid != geteuid()) - return -EPERM; - - return 1; -} - -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_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 - * bus */ - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS); - if (r < 0) - return r; - - bus->bus_client = true; - - r = sd_bus_start(bus); - if (r >= 0) { - *_bus = bus; - bus = NULL; - return 0; - } - - bus = sd_bus_unref(bus); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - r = sd_bus_set_address(bus, "unix:path=/run/systemd/private"); - if (r < 0) - return r; - - r = sd_bus_start(bus); - if (r < 0) - return sd_bus_default_system(_bus); - - r = bus_check_peercred(bus); - if (r < 0) - return r; - - *_bus = bus; - bus = NULL; - - return 0; -} - -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; - - /* Try via kdbus first, and then directly */ - - assert(_bus); - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0) - return -ENOMEM; - - bus->bus_client = true; - - r = sd_bus_start(bus); - if (r >= 0) { - *_bus = bus; - bus = NULL; - return 0; - } - - bus = sd_bus_unref(bus); - - e = secure_getenv("XDG_RUNTIME_DIR"); - if (!e) - return sd_bus_default_user(_bus); - - ee = bus_address_escape(e); - if (!ee) - return -ENOMEM; - - r = sd_bus_new(&bus); - if (r < 0) - return r; - - bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL); - if (!bus->address) - return -ENOMEM; - - r = sd_bus_start(bus); - if (r < 0) - return sd_bus_default_user(_bus); - - r = bus_check_peercred(bus); - if (r < 0) - return r; - - *_bus = bus; - bus = NULL; - - return 0; -} - -#define print_property(name, fmt, ...) \ - do { \ - if (value) \ - printf(fmt "\n", __VA_ARGS__); \ - else \ - printf("%s=" fmt "\n", name, __VA_ARGS__); \ - } while(0) - -int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) { - char type; - const char *contents; - int r; - - assert(name); - assert(property); - - r = sd_bus_message_peek_type(property, &type, &contents); - if (r < 0) - return r; - - switch (type) { - - case SD_BUS_TYPE_STRING: { - const char *s; - - r = sd_bus_message_read_basic(property, type, &s); - if (r < 0) - return r; - - if (all || !isempty(s)) { - _cleanup_free_ char *escaped = NULL; - - escaped = xescape(s, "\n"); - if (!escaped) - return -ENOMEM; - - print_property(name, "%s", escaped); - } - - return 1; - } - - case SD_BUS_TYPE_BOOLEAN: { - int b; - - r = sd_bus_message_read_basic(property, type, &b); - if (r < 0) - return r; - - print_property(name, "%s", yes_no(b)); - - return 1; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t u; - - r = sd_bus_message_read_basic(property, type, &u); - if (r < 0) - return r; - - /* Yes, heuristics! But we can change this check - * should it turn out to not be sufficient */ - - if (endswith(name, "Timestamp")) { - char timestamp[FORMAT_TIMESTAMP_MAX], *t; - - t = format_timestamp(timestamp, sizeof(timestamp), u); - if (t || all) - print_property(name, "%s", strempty(t)); - - } else if (strstr(name, "USec")) { - char timespan[FORMAT_TIMESPAN_MAX]; - - print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0)); - } else - print_property(name, "%"PRIu64, u); - - return 1; - } - - case SD_BUS_TYPE_INT64: { - int64_t i; - - r = sd_bus_message_read_basic(property, type, &i); - if (r < 0) - return r; - - print_property(name, "%"PRIi64, i); - - return 1; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t u; - - r = sd_bus_message_read_basic(property, type, &u); - if (r < 0) - return r; - - if (strstr(name, "UMask") || strstr(name, "Mode")) - print_property(name, "%04o", u); - else - print_property(name, "%"PRIu32, u); - - return 1; - } - - case SD_BUS_TYPE_INT32: { - int32_t i; - - r = sd_bus_message_read_basic(property, type, &i); - if (r < 0) - return r; - - print_property(name, "%"PRIi32, i); - return 1; - } - - case SD_BUS_TYPE_DOUBLE: { - double d; - - r = sd_bus_message_read_basic(property, type, &d); - if (r < 0) - return r; - - print_property(name, "%g", d); - return 1; - } - - case SD_BUS_TYPE_ARRAY: - if (streq(contents, "s")) { - bool first = true; - const char *str; - - r = sd_bus_message_enter_container(property, SD_BUS_TYPE_ARRAY, contents); - if (r < 0) - return r; - - while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { - _cleanup_free_ char *escaped = NULL; - - if (first && !value) - printf("%s=", name); - - escaped = xescape(str, "\n "); - if (!escaped) - return -ENOMEM; - - printf("%s%s", first ? "" : " ", escaped); - - first = false; - } - if (r < 0) - return r; - - if (first && all && !value) - printf("%s=", name); - if (!first || all) - puts(""); - - r = sd_bus_message_exit_container(property); - if (r < 0) - return r; - - return 1; - - } else if (streq(contents, "y")) { - const uint8_t *u; - size_t n; - - r = sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, (const void**) &u, &n); - if (r < 0) - return r; - - if (all || n > 0) { - unsigned int i; - - if (!value) - printf("%s=", name); - - for (i = 0; i < n; i++) - printf("%02x", u[i]); - - puts(""); - } - - return 1; - - } else if (streq(contents, "u")) { - uint32_t *u; - size_t n; - - r = sd_bus_message_read_array(property, SD_BUS_TYPE_UINT32, (const void**) &u, &n); - if (r < 0) - return r; - - if (all || n > 0) { - unsigned int i; - - if (!value) - printf("%s=", name); - - for (i = 0; i < n; i++) - printf("%08x", u[i]); - - puts(""); - } - - return 1; - } - - break; - } - - return 0; -} - -int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) { - _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); - assert(path); - - r = sd_bus_call_method(bus, - dest, - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *name; - const char *contents; - - r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name); - if (r < 0) - return r; - - if (!filter || strv_find(filter, name)) { - r = sd_bus_message_peek_type(reply, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return r; - - r = bus_print_property(name, reply, value, all); - if (r < 0) - return r; - if (r == 0) { - if (all) - printf("%s=[unprintable]\n", name); - /* skip what we didn't read */ - r = sd_bus_message_skip(reply, contents); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - } else { - r = sd_bus_message_skip(reply, "v"); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return r; - - return 0; -} - -int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - sd_id128_t *p = userdata; - const void *v; - size_t n; - int r; - - r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n); - if (r < 0) - return r; - - if (n == 0) - *p = SD_ID128_NULL; - else if (n == 16) - memcpy((*p).bytes, v, n); - else - return -EINVAL; - - return 0; -} - -static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char type; - int r; - - r = sd_bus_message_peek_type(m, &type, NULL); - if (r < 0) - return r; - - switch (type) { - case SD_BUS_TYPE_STRING: { - const char *s; - char **p = userdata; - - r = sd_bus_message_read_basic(m, type, &s); - if (r < 0) - break; - - if (isempty(s)) - break; - - r = free_and_strdup(p, s); - break; - } - - case SD_BUS_TYPE_ARRAY: { - _cleanup_strv_free_ char **l = NULL; - char ***p = userdata; - - r = bus_message_read_strv_extend(m, &l); - if (r < 0) - break; - - strv_free(*p); - *p = l; - l = NULL; - - break; - } - - case SD_BUS_TYPE_BOOLEAN: { - unsigned b; - int *p = userdata; - - r = sd_bus_message_read_basic(m, type, &b); - if (r < 0) - break; - - *p = b; - - break; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t u; - uint32_t *p = userdata; - - r = sd_bus_message_read_basic(m, type, &u); - if (r < 0) - break; - - *p = u; - - break; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t t; - uint64_t *p = userdata; - - r = sd_bus_message_read_basic(m, type, &t); - if (r < 0) - break; - - *p = t; - - break; - } - - case SD_BUS_TYPE_DOUBLE: { - double d; - double *p = userdata; - - r = sd_bus_message_read_basic(m, type, &d); - if (r < 0) - break; - - *p = d; - - break; - } - - default: - break; - } - - return r; -} - -int bus_message_map_all_properties( - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(m); - assert(map); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return r; - - while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const struct bus_properties_map *prop; - const char *member; - const char *contents; - void *v; - unsigned i; - - r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member); - if (r < 0) - return r; - - for (i = 0, prop = NULL; map[i].member; i++) - if (streq(map[i].member, member)) { - prop = &map[i]; - break; - } - - if (prop) { - r = sd_bus_message_peek_type(m, NULL, &contents); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return r; - - v = (uint8_t *)userdata + prop->offset; - if (map[i].set) - r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v); - else - r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } else { - r = sd_bus_message_skip(m, "v"); - if (r < 0) - return r; - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - } - if (r < 0) - return r; - - return sd_bus_message_exit_container(m); -} - -int bus_message_map_properties_changed( - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { - - const char *member; - int r, invalidated, i; - - assert(m); - assert(map); - - r = bus_message_map_all_properties(m, map, userdata); - if (r < 0) - return r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); - if (r < 0) - return r; - - invalidated = 0; - while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0) - for (i = 0; map[i].member; i++) - if (streq(map[i].member, member)) { - ++invalidated; - break; - } - if (r < 0) - return r; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return r; - - return invalidated; -} - -int bus_map_all_properties( - sd_bus *bus, - const char *destination, - const char *path, - const struct bus_properties_map *map, - 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; - int r; - - assert(bus); - assert(destination); - assert(path); - assert(map); - - r = sd_bus_call_method( - bus, - destination, - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &m, - "s", ""); - if (r < 0) - return r; - - return bus_message_map_all_properties(m, map, userdata); -} - -int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { - int r; - - assert(transport >= 0); - assert(transport < _BUS_TRANSPORT_MAX); - assert(bus); - - assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); - assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); - - switch (transport) { - - case BUS_TRANSPORT_LOCAL: - if (user) - r = sd_bus_default_user(bus); - else - r = sd_bus_default_system(bus); - - break; - - case BUS_TRANSPORT_REMOTE: - r = sd_bus_open_system_remote(bus, host); - break; - - case BUS_TRANSPORT_MACHINE: - r = sd_bus_open_system_machine(bus, host); - break; - - default: - assert_not_reached("Hmm, unknown transport type."); - } - - return r; -} - -int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) { - int r; - - assert(transport >= 0); - assert(transport < _BUS_TRANSPORT_MAX); - assert(bus); - - assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL); - assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP); - - switch (transport) { - - case BUS_TRANSPORT_LOCAL: - if (user) - r = bus_connect_user_systemd(bus); - else - r = bus_connect_system_systemd(bus); - - break; - - case BUS_TRANSPORT_REMOTE: - r = sd_bus_open_system_remote(bus, host); - break; - - case BUS_TRANSPORT_MACHINE: - r = sd_bus_open_system_machine(bus, host); - break; - - default: - assert_not_reached("Hmm, unknown transport type."); - } - - return r; -} - -int bus_property_get_bool( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - int b = *(bool*) userdata; - - return sd_bus_message_append_basic(reply, 'b', &b); -} - -#if __SIZEOF_SIZE_T__ != 8 -int bus_property_get_size( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t sz = *(size_t*) userdata; - - return sd_bus_message_append_basic(reply, 't', &sz); -} -#endif - -#if __SIZEOF_LONG__ != 8 -int bus_property_get_long( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - int64_t l = *(long*) userdata; - - return sd_bus_message_append_basic(reply, 'x', &l); -} - -int bus_property_get_ulong( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - uint64_t ul = *(unsigned long*) userdata; - - return sd_bus_message_append_basic(reply, 't', &ul); -} -#endif - -int bus_log_parse_error(int r) { - return log_error_errno(r, "Failed to parse bus message: %m"); -} - -int bus_log_create_error(int r) { - return log_error_errno(r, "Failed to create bus message: %m"); -} - -/** - * bus_path_encode_unique() - encode unique object path - * @b: bus connection or NULL - * @prefix: object path prefix - * @sender_id: unique-name of client, or NULL - * @external_id: external ID to be chosen by client, or NULL - * @ret_path: storage for encoded object path pointer - * - * Whenever we provide a bus API that allows clients to create and manage - * server-side objects, we need to provide a unique name for these objects. If - * we let the server choose the name, we suffer from a race condition: If a - * client creates an object asynchronously, it cannot destroy that object until - * it received the method reply. It cannot know the name of the new object, - * thus, it cannot destroy it. Furthermore, it enforces a round-trip. - * - * Therefore, many APIs allow the client to choose the unique name for newly - * created objects. There're two problems to solve, though: - * 1) Object names are usually defined via dbus object paths, which are - * usually globally namespaced. Therefore, multiple clients must be able - * to choose unique object names without interference. - * 2) If multiple libraries share the same bus connection, they must be - * able to choose unique object names without interference. - * The first problem is solved easily by prefixing a name with the - * unique-bus-name of a connection. The server side must enforce this and - * reject any other name. The second problem is solved by providing unique - * suffixes from within sd-bus. - * - * This helper allows clients to create unique object-paths. It uses the - * template '/prefix/sender_id/external_id' and returns the new path in - * @ret_path (must be freed by the caller). - * If @sender_id is NULL, the unique-name of @b is used. If @external_id is - * NULL, this function allocates a unique suffix via @b (by requesting a new - * cookie). If both @sender_id and @external_id are given, @b can be passed as - * NULL. - * - * Returns: 0 on success, negative error code on failure. - */ -int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) { - _cleanup_free_ char *sender_label = NULL, *external_label = NULL; - char external_buf[DECIMAL_STR_MAX(uint64_t)], *p; - int r; - - assert_return(b || (sender_id && external_id), -EINVAL); - assert_return(object_path_is_valid(prefix), -EINVAL); - assert_return(ret_path, -EINVAL); - - if (!sender_id) { - r = sd_bus_get_unique_name(b, &sender_id); - if (r < 0) - return r; - } - - if (!external_id) { - xsprintf(external_buf, "%"PRIu64, ++b->cookie); - external_id = external_buf; - } - - sender_label = bus_label_escape(sender_id); - if (!sender_label) - return -ENOMEM; - - external_label = bus_label_escape(external_id); - if (!external_label) - return -ENOMEM; - - p = strjoin(prefix, "/", sender_label, "/", external_label, NULL); - if (!p) - return -ENOMEM; - - *ret_path = p; - return 0; -} - -/** - * bus_path_decode_unique() - decode unique object path - * @path: object path to decode - * @prefix: object path prefix - * @ret_sender: output parameter for sender-id label - * @ret_external: output parameter for external-id label - * - * This does the reverse of bus_path_encode_unique() (see its description for - * details). Both trailing labels, sender-id and external-id, are unescaped and - * returned in the given output parameters (the caller must free them). - * - * Note that this function returns 0 if the path does not match the template - * (see bus_path_encode_unique()), 1 if it matched. - * - * Returns: Negative error code on failure, 0 if the given object path does not - * match the template (return parameters are set to NULL), 1 if it was - * parsed successfully (return parameters contain allocated labels). - */ -int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) { - const char *p, *q; - char *sender, *external; - - assert(object_path_is_valid(path)); - assert(object_path_is_valid(prefix)); - assert(ret_sender); - assert(ret_external); - - p = object_path_startswith(path, prefix); - if (!p) { - *ret_sender = NULL; - *ret_external = NULL; - return 0; - } - - q = strchr(p, '/'); - if (!q) { - *ret_sender = NULL; - *ret_external = NULL; - return 0; - } - - sender = bus_label_unescape_n(p, q - p); - external = bus_label_unescape(q + 1); - if (!sender || !external) { - free(sender); - free(external); - return -ENOMEM; - } - - *ret_sender = sender; - *ret_external = external; - return 1; -} - -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 deleted file mode 100644 index db6b1acba2..0000000000 --- a/src/shared/bus-util.h +++ /dev/null @@ -1,160 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-event.h" - -#include "hashmap.h" -#include "macro.h" -#include "string-util.h" - -typedef enum BusTransport { - BUS_TRANSPORT_LOCAL, - BUS_TRANSPORT_REMOTE, - BUS_TRANSPORT_MACHINE, - _BUS_TRANSPORT_MAX, - _BUS_TRANSPORT_INVALID = -1 -} BusTransport; - -typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); - -struct bus_properties_map { - const char *member; - const char *signature; - bus_property_set_t set; - size_t offset; -}; - -int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); - -int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); -int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); -int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata); - -int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name); - -typedef bool (*check_idle_t)(void *userdata); - -int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout, check_idle_t check_idle, void *userdata); - -int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error); - -int bus_check_peercred(sd_bus *c); - -int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e); - -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_connect_system_systemd(sd_bus **_bus); -int bus_connect_user_systemd(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 value, bool all); -int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all); - -int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); - -#define bus_property_get_usec ((sd_bus_property_get_t) NULL) -#define bus_property_set_usec ((sd_bus_property_set_t) NULL) - -assert_cc(sizeof(int) == sizeof(int32_t)); -#define bus_property_get_int ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(unsigned) == sizeof(unsigned)); -#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL) - -/* On 64bit machines we can use the default serializer for size_t and - * friends, otherwise we need to cast this manually */ -#if __SIZEOF_SIZE_T__ == 8 -#define bus_property_get_size ((sd_bus_property_get_t) NULL) -#else -int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -#endif - -#if __SIZEOF_LONG__ == 8 -#define bus_property_get_long ((sd_bus_property_get_t) NULL) -#define bus_property_get_ulong ((sd_bus_property_get_t) NULL) -#else -int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); -#endif - -/* uid_t and friends on Linux 32 bit. This means we can just use the - * default serializer for 32bit unsigned, for serializing it, and map - * it to NULL here */ -assert_cc(sizeof(uid_t) == sizeof(uint32_t)); -#define bus_property_get_uid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(gid_t) == sizeof(uint32_t)); -#define bus_property_get_gid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(pid_t) == sizeof(uint32_t)); -#define bus_property_get_pid ((sd_bus_property_get_t) NULL) - -assert_cc(sizeof(mode_t) == sizeof(uint32_t)); -#define bus_property_get_mode ((sd_bus_property_get_t) NULL) - -int bus_log_parse_error(int r); -int bus_log_create_error(int r); - -#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ - int function(sd_bus *bus, \ - const char *path, \ - const char *interface, \ - const char *property, \ - sd_bus_message *reply, \ - void *userdata, \ - sd_bus_error *error) { \ - \ - const char *value; \ - type *field = userdata; \ - int r; \ - \ - assert(bus); \ - assert(reply); \ - assert(field); \ - \ - value = strempty(name##_to_string(*field)); \ - \ - r = sd_bus_message_append_basic(reply, 's', value); \ - if (r < 0) \ - return r; \ - \ - return 1; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \ - SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ - SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) - -int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); -int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); - -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 deleted file mode 100644 index 3e451db715..0000000000 --- a/src/shared/cgroup-show.c +++ /dev/null @@ -1,312 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "cgroup-show.h" -#include "cgroup-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "locale-util.h" -#include "macro.h" -#include "output-mode.h" -#include "path-util.h" -#include "process-util.h" -#include "string-util.h" -#include "terminal-util.h" - -static void show_pid_array( - pid_t pids[], - unsigned n_pids, - const char *prefix, - unsigned n_columns, - bool extra, - bool more, - OutputFlags flags) { - - unsigned i, j, pid_width; - - if (n_pids == 0) - return; - - qsort(pids, n_pids, sizeof(pid_t), pid_compare_func); - - /* Filter duplicates */ - for (j = 0, i = 1; i < n_pids; i++) { - if (pids[i] == pids[j]) - continue; - pids[++j] = pids[i]; - } - n_pids = j + 1; - pid_width = DECIMAL_STR_WIDTH(pids[j]); - - if (flags & OUTPUT_FULL_WIDTH) - n_columns = 0; - else { - if (n_columns > pid_width+2) - n_columns -= pid_width+2; - else - n_columns = 20; - } - for (i = 0; i < n_pids; i++) { - _cleanup_free_ char *t = NULL; - - get_process_cmdline(pids[i], n_columns, true, &t); - - if (extra) - printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET)); - else - printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT))); - - printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t)); - } -} - -static int show_cgroup_one_by_path( - const char *path, - const char *prefix, - unsigned n_columns, - bool more, - OutputFlags flags) { - - char *fn; - _cleanup_fclose_ FILE *f = NULL; - size_t n = 0, n_allocated = 0; - _cleanup_free_ pid_t *pids = NULL; - _cleanup_free_ char *p = NULL; - pid_t pid; - int r; - - r = cg_mangle_path(path, &p); - if (r < 0) - return r; - - fn = strjoina(p, "/cgroup.procs"); - f = fopen(fn, "re"); - if (!f) - return -errno; - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0) - continue; - - if (!GREEDY_REALLOC(pids, n_allocated, n + 1)) - return -ENOMEM; - - assert(n < n_allocated); - pids[n++] = pid; - } - - if (r < 0) - return r; - - show_pid_array(pids, n, prefix, n_columns, false, more, flags); - - return 0; -} - -int show_cgroup_by_path( - const char *path, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - - _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL; - _cleanup_closedir_ DIR *d = NULL; - char *gn = NULL; - bool shown_pids = false; - int r; - - assert(path); - - if (n_columns <= 0) - n_columns = columns(); - - prefix = strempty(prefix); - - r = cg_mangle_path(path, &fn); - if (r < 0) - return r; - - d = opendir(fn); - if (!d) - return -errno; - - while ((r = cg_read_subgroup(d, &gn)) > 0) { - _cleanup_free_ char *k = NULL; - - k = strjoin(fn, "/", gn, NULL); - free(gn); - if (!k) - return -ENOMEM; - - if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0) - continue; - - if (!shown_pids) { - show_cgroup_one_by_path(path, prefix, n_columns, true, flags); - shown_pids = true; - } - - if (last) { - printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last))); - - if (!p1) { - p1 = strappend(prefix, special_glyph(TREE_VERTICAL)); - if (!p1) - return -ENOMEM; - } - - show_cgroup_by_path(last, p1, n_columns-2, flags); - free(last); - } - - last = k; - k = NULL; - } - - if (r < 0) - return r; - - if (!shown_pids) - show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags); - - if (last) { - printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last))); - - if (!p2) { - p2 = strappend(prefix, " "); - if (!p2) - return -ENOMEM; - } - - show_cgroup_by_path(last, p2, n_columns-2, flags); - } - - return 0; -} - -int show_cgroup(const char *controller, - const char *path, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - _cleanup_free_ char *p = NULL; - int r; - - assert(path); - - r = cg_get_path(controller, path, NULL, &p); - if (r < 0) - return r; - - return show_cgroup_by_path(p, prefix, n_columns, flags); -} - -static int show_extra_pids( - const char *controller, - const char *path, - const char *prefix, - unsigned n_columns, - const pid_t pids[], - unsigned n_pids, - OutputFlags flags) { - - _cleanup_free_ pid_t *copy = NULL; - unsigned i, j; - int r; - - assert(path); - - if (n_pids <= 0) - return 0; - - if (n_columns <= 0) - n_columns = columns(); - - prefix = strempty(prefix); - - copy = new(pid_t, n_pids); - if (!copy) - return -ENOMEM; - - for (i = 0, j = 0; i < n_pids; i++) { - _cleanup_free_ char *k = NULL; - - r = cg_pid_get_path(controller, pids[i], &k); - if (r < 0) - return r; - - if (path_startswith(k, path)) - continue; - - copy[j++] = pids[i]; - } - - show_pid_array(copy, j, prefix, n_columns, true, false, flags); - - return 0; -} - -int show_cgroup_and_extra( - const char *controller, - const char *path, - const char *prefix, - unsigned n_columns, - const pid_t extra_pids[], - unsigned n_extra_pids, - OutputFlags flags) { - - int r; - - assert(path); - - r = show_cgroup(controller, path, prefix, n_columns, flags); - if (r < 0) - return r; - - return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); -} - -int show_cgroup_and_extra_by_spec( - const char *spec, - const char *prefix, - unsigned n_columns, - const pid_t extra_pids[], - unsigned n_extra_pids, - OutputFlags flags) { - - _cleanup_free_ char *controller = NULL, *path = NULL; - int r; - - assert(spec); - - r = cg_split_spec(spec, &controller, &path); - if (r < 0) - return r; - - return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); -} diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h deleted file mode 100644 index 5c1d6e6d98..0000000000 --- a/src/shared/cgroup-show.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "logs-show.h" -#include "output-mode.h" - -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags); - -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c deleted file mode 100644 index a3ac7aeb82..0000000000 --- a/src/shared/clean-ipc.c +++ /dev/null @@ -1,365 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" - -static int clean_sysvipc_shm(uid_t delete_uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - bool first = true; - int ret = 0; - - f = fopen("/proc/sysvipc/shm", "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); - } - - FOREACH_LINE(line, f, goto fail) { - unsigned n_attached; - pid_t cpid, lpid; - uid_t uid, cuid; - gid_t gid, cgid; - int shmid; - - if (first) { - first = false; - continue; - } - - truncate_nl(line); - - if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, - &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8) - continue; - - if (n_attached > 0) - continue; - - if (uid != delete_uid) - continue; - - if (shmctl(shmid, IPC_RMID, NULL) < 0) { - - /* Ignore entries that are already deleted */ - if (errno == EIDRM || errno == EINVAL) - continue; - - ret = log_warning_errno(errno, - "Failed to remove SysV shared memory segment %i: %m", - shmid); - } - } - - return ret; - -fail: - return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); -} - -static int clean_sysvipc_sem(uid_t delete_uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - bool first = true; - int ret = 0; - - f = fopen("/proc/sysvipc/sem", "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); - } - - FOREACH_LINE(line, f, goto fail) { - uid_t uid, cuid; - gid_t gid, cgid; - int semid; - - if (first) { - first = false; - continue; - } - - truncate_nl(line); - - if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, - &semid, &uid, &gid, &cuid, &cgid) != 5) - continue; - - if (uid != delete_uid) - continue; - - if (semctl(semid, 0, IPC_RMID) < 0) { - - /* Ignore entries that are already deleted */ - if (errno == EIDRM || errno == EINVAL) - continue; - - ret = log_warning_errno(errno, - "Failed to remove SysV semaphores object %i: %m", - semid); - } - } - - return ret; - -fail: - return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); -} - -static int clean_sysvipc_msg(uid_t delete_uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - bool first = true; - int ret = 0; - - f = fopen("/proc/sysvipc/msg", "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); - } - - FOREACH_LINE(line, f, goto fail) { - uid_t uid, cuid; - gid_t gid, cgid; - pid_t cpid, lpid; - int msgid; - - if (first) { - first = false; - continue; - } - - truncate_nl(line); - - if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT, - &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7) - continue; - - if (uid != delete_uid) - continue; - - if (msgctl(msgid, IPC_RMID, NULL) < 0) { - - /* Ignore entries that are already deleted */ - if (errno == EIDRM || errno == EINVAL) - continue; - - ret = log_warning_errno(errno, - "Failed to remove SysV message queue %i: %m", - msgid); - } - } - - return ret; - -fail: - return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); -} - -static int clean_posix_shm_internal(DIR *dir, uid_t uid) { - struct dirent *de; - int ret = 0, r; - - assert(dir); - - FOREACH_DIRENT(de, dir, goto fail) { - struct stat st; - - if (STR_IN_SET(de->d_name, "..", ".")) - continue; - - if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == ENOENT) - continue; - - log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); - ret = -errno; - continue; - } - - if (st.st_uid != uid) - continue; - - if (S_ISDIR(st.st_mode)) { - _cleanup_closedir_ DIR *kid; - - kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); - if (!kid) { - if (errno != ENOENT) { - log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); - ret = -errno; - } - } else { - r = clean_posix_shm_internal(kid, uid); - if (r < 0) - ret = r; - } - - if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) { - - if (errno == ENOENT) - continue; - - log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); - ret = -errno; - } - } else { - - if (unlinkat(dirfd(dir), de->d_name, 0) < 0) { - - if (errno == ENOENT) - continue; - - log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); - ret = -errno; - } - } - } - - return ret; - -fail: - log_warning_errno(errno, "Failed to read /dev/shm: %m"); - return -errno; -} - -static int clean_posix_shm(uid_t uid) { - _cleanup_closedir_ DIR *dir = NULL; - - dir = opendir("/dev/shm"); - if (!dir) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open /dev/shm: %m"); - } - - return clean_posix_shm_internal(dir, uid); -} - -static int clean_posix_mq(uid_t uid) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *de; - int ret = 0; - - dir = opendir("/dev/mqueue"); - if (!dir) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); - } - - FOREACH_DIRENT(de, dir, goto fail) { - struct stat st; - char fn[1+strlen(de->d_name)+1]; - - if (STR_IN_SET(de->d_name, "..", ".")) - continue; - - if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == ENOENT) - continue; - - ret = log_warning_errno(errno, - "Failed to stat() MQ segment %s: %m", - de->d_name); - continue; - } - - if (st.st_uid != uid) - continue; - - fn[0] = '/'; - strcpy(fn+1, de->d_name); - - if (mq_unlink(fn) < 0) { - if (errno == ENOENT) - continue; - - ret = log_warning_errno(errno, - "Failed to unlink POSIX message queue %s: %m", - fn); - } - } - - return ret; - -fail: - return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); -} - -int clean_ipc(uid_t uid) { - int ret = 0, r; - - /* Refuse to clean IPC of the root and system users */ - if (uid <= SYSTEM_UID_MAX) - return 0; - - r = clean_sysvipc_shm(uid); - if (r < 0) - ret = r; - - r = clean_sysvipc_sem(uid); - if (r < 0) - ret = r; - - r = clean_sysvipc_msg(uid); - if (r < 0) - ret = r; - - r = clean_posix_shm(uid); - if (r < 0) - ret = r; - - r = clean_posix_mq(uid); - if (r < 0) - ret = r; - - return ret; -} diff --git a/src/shared/clean-ipc.h b/src/shared/clean-ipc.h deleted file mode 100644 index 44a83afcf7..0000000000 --- a/src/shared/clean-ipc.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int clean_ipc(uid_t uid); diff --git a/src/shared/condition.c b/src/shared/condition.c deleted file mode 100644 index 6bb42c0692..0000000000 --- a/src/shared/condition.c +++ /dev/null @@ -1,542 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-id128.h" - -#include "alloc-util.h" -#include "apparmor-util.h" -#include "architecture.h" -#include "audit-util.h" -#include "cap-list.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; - int r; - - assert(type >= 0); - assert(type < _CONDITION_TYPE_MAX); - assert((!parameter) == (type == CONDITION_NULL)); - - c = new0(Condition, 1); - if (!c) - return NULL; - - c->type = type; - c->trigger = trigger; - c->negate = negate; - - r = free_and_strdup(&c->parameter, parameter); - if (r < 0) { - free(c); - return NULL; - } - - return c; -} - -void condition_free(Condition *c) { - assert(c); - - free(c->parameter); - free(c); -} - -Condition* condition_free_list(Condition *first) { - Condition *c, *n; - - LIST_FOREACH_SAFE(conditions, c, n, first) - condition_free(c); - - return NULL; -} - -static int condition_test_kernel_command_line(Condition *c) { - _cleanup_free_ char *line = NULL; - const char *p; - bool equal; - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_KERNEL_COMMAND_LINE); - - r = proc_cmdline(&line); - if (r < 0) - return r; - - equal = !!strchr(c->parameter, '='); - p = line; - - for (;;) { - _cleanup_free_ char *word = NULL; - bool found; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); - if (r < 0) - return r; - if (r == 0) - break; - - if (equal) - found = streq(word, c->parameter); - else { - const char *f; - - f = startswith(word, c->parameter); - found = f && (*f == '=' || *f == 0); - } - - if (found) - return true; - } - - return false; -} - -static int condition_test_virtualization(Condition *c) { - int b, v; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_VIRTUALIZATION); - - v = detect_virtualization(); - if (v < 0) - return v; - - /* First, compare with yes/no */ - b = parse_boolean(c->parameter); - - if (v > 0 && b > 0) - return true; - - if (v == 0 && b == 0) - return true; - - /* Then, compare categorization */ - if (VIRTUALIZATION_IS_VM(v) && streq(c->parameter, "vm")) - return true; - - if (VIRTUALIZATION_IS_CONTAINER(v) && streq(c->parameter, "container")) - return true; - - /* Finally compare id */ - return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v)); -} - -static int condition_test_architecture(Condition *c) { - int a, b; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_ARCHITECTURE); - - a = uname_architecture(); - if (a < 0) - return a; - - if (streq(c->parameter, "native")) - b = native_architecture(); - else { - b = architecture_from_string(c->parameter); - if (b < 0) /* unknown architecture? Then it's definitely not ours */ - return false; - } - - return a == b; -} - -static int condition_test_host(Condition *c) { - _cleanup_free_ char *h = NULL; - sd_id128_t x, y; - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_HOST); - - if (sd_id128_from_string(c->parameter, &x) >= 0) { - - r = sd_id128_get_machine(&y); - if (r < 0) - return r; - - return sd_id128_equal(x, y); - } - - h = gethostname_malloc(); - if (!h) - return -ENOMEM; - - return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0; -} - -static int condition_test_ac_power(Condition *c) { - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_AC_POWER); - - r = parse_boolean(c->parameter); - if (r < 0) - return r; - - return (on_ac_power() != 0) == !!r; -} - -static int condition_test_security(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_SECURITY); - - if (streq(c->parameter, "selinux")) - return mac_selinux_have(); - if (streq(c->parameter, "smack")) - return mac_smack_use(); - if (streq(c->parameter, "apparmor")) - return mac_apparmor_use(); - if (streq(c->parameter, "audit")) - return use_audit(); - if (streq(c->parameter, "ima")) - return use_ima(); - - return false; -} - -static int condition_test_capability(Condition *c) { - _cleanup_fclose_ FILE *f = NULL; - int value; - char line[LINE_MAX]; - unsigned long long capabilities = -1; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_CAPABILITY); - - /* If it's an invalid capability, we don't have it */ - value = capability_from_name(c->parameter); - if (value < 0) - return -EINVAL; - - /* If it's a valid capability we default to assume - * that we have it */ - - f = fopen("/proc/self/status", "re"); - if (!f) - return -errno; - - while (fgets(line, sizeof(line), f)) { - truncate_nl(line); - - if (startswith(line, "CapBnd:")) { - (void) sscanf(line+7, "%llx", &capabilities); - break; - } - } - - return !!(capabilities & (1ULL << value)); -} - -static int condition_test_needs_update(Condition *c) { - const char *p; - struct stat usr, other; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_NEEDS_UPDATE); - - /* If the file system is read-only we shouldn't suggest an update */ - if (path_is_read_only_fs(c->parameter) > 0) - return false; - - /* Any other failure means we should allow the condition to be true, - * so that we rather invoke too many update tools than too - * few. */ - - if (!path_is_absolute(c->parameter)) - return true; - - p = strjoina(c->parameter, "/.updated"); - if (lstat(p, &other) < 0) - return true; - - if (lstat("/usr/", &usr) < 0) - return true; - - return usr.st_mtim.tv_sec > other.st_mtim.tv_sec || - (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec); -} - -static int condition_test_first_boot(Condition *c) { - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_FIRST_BOOT); - - r = parse_boolean(c->parameter); - if (r < 0) - return r; - - return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r; -} - -static int condition_test_path_exists(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_EXISTS); - - return access(c->parameter, F_OK) >= 0; -} - -static int condition_test_path_exists_glob(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_EXISTS_GLOB); - - return glob_exists(c->parameter) > 0; -} - -static int condition_test_path_is_directory(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_IS_DIRECTORY); - - return is_dir(c->parameter, true) > 0; -} - -static int condition_test_path_is_symbolic_link(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK); - - return is_symlink(c->parameter) > 0; -} - -static int condition_test_path_is_mount_point(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); - - return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0; -} - -static int condition_test_path_is_read_write(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_PATH_IS_READ_WRITE); - - return path_is_read_only_fs(c->parameter) <= 0; -} - -static int condition_test_directory_not_empty(Condition *c) { - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY); - - r = dir_is_empty(c->parameter); - return r <= 0 && r != -ENOENT; -} - -static int condition_test_file_not_empty(Condition *c) { - struct stat st; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_FILE_NOT_EMPTY); - - return (stat(c->parameter, &st) >= 0 && - S_ISREG(st.st_mode) && - st.st_size > 0); -} - -static int condition_test_file_is_executable(Condition *c) { - struct stat st; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_FILE_IS_EXECUTABLE); - - return (stat(c->parameter, &st) >= 0 && - S_ISREG(st.st_mode) && - (st.st_mode & 0111)); -} - -static int condition_test_null(Condition *c) { - assert(c); - assert(c->type == CONDITION_NULL); - - /* Note that during parsing we already evaluate the string and - * store it in c->negate */ - return true; -} - -int condition_test(Condition *c) { - - static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = { - [CONDITION_PATH_EXISTS] = condition_test_path_exists, - [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob, - [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory, - [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link, - [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point, - [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write, - [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty, - [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, - [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, - [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, - [CONDITION_VIRTUALIZATION] = condition_test_virtualization, - [CONDITION_SECURITY] = condition_test_security, - [CONDITION_CAPABILITY] = condition_test_capability, - [CONDITION_HOST] = condition_test_host, - [CONDITION_AC_POWER] = condition_test_ac_power, - [CONDITION_ARCHITECTURE] = condition_test_architecture, - [CONDITION_NEEDS_UPDATE] = condition_test_needs_update, - [CONDITION_FIRST_BOOT] = condition_test_first_boot, - [CONDITION_NULL] = condition_test_null, - }; - - int r, b; - - assert(c); - assert(c->type >= 0); - assert(c->type < _CONDITION_TYPE_MAX); - - r = condition_tests[c->type](c); - if (r < 0) { - c->result = CONDITION_ERROR; - return r; - } - - b = (r > 0) == !c->negate; - c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED; - return b; -} - -void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { - assert(c); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%s\t%s: %s%s%s %s\n", - prefix, - to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter, - condition_result_to_string(c->result)); -} - -void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { - Condition *c; - - LIST_FOREACH(conditions, c, first) - condition_dump(c, f, prefix, to_string); -} - -static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { - [CONDITION_ARCHITECTURE] = "ConditionArchitecture", - [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", - [CONDITION_HOST] = "ConditionHost", - [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", - [CONDITION_SECURITY] = "ConditionSecurity", - [CONDITION_CAPABILITY] = "ConditionCapability", - [CONDITION_AC_POWER] = "ConditionACPower", - [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate", - [CONDITION_FIRST_BOOT] = "ConditionFirstBoot", - [CONDITION_PATH_EXISTS] = "ConditionPathExists", - [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", - [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", - [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", - [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", - [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", - [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", - [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", - [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", - [CONDITION_NULL] = "ConditionNull" -}; - -DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); - -static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { - [CONDITION_ARCHITECTURE] = "AssertArchitecture", - [CONDITION_VIRTUALIZATION] = "AssertVirtualization", - [CONDITION_HOST] = "AssertHost", - [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", - [CONDITION_SECURITY] = "AssertSecurity", - [CONDITION_CAPABILITY] = "AssertCapability", - [CONDITION_AC_POWER] = "AssertACPower", - [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate", - [CONDITION_FIRST_BOOT] = "AssertFirstBoot", - [CONDITION_PATH_EXISTS] = "AssertPathExists", - [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob", - [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory", - [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink", - [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint", - [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite", - [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", - [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", - [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", - [CONDITION_NULL] = "AssertNull" -}; - -DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); - -static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { - [CONDITION_UNTESTED] = "untested", - [CONDITION_SUCCEEDED] = "succeeded", - [CONDITION_FAILED] = "failed", - [CONDITION_ERROR] = "error", -}; - -DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult); diff --git a/src/shared/condition.h b/src/shared/condition.h deleted file mode 100644 index bdda04b770..0000000000 --- a/src/shared/condition.h +++ /dev/null @@ -1,94 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "list.h" -#include "macro.h" - -typedef enum ConditionType { - CONDITION_ARCHITECTURE, - CONDITION_VIRTUALIZATION, - CONDITION_HOST, - CONDITION_KERNEL_COMMAND_LINE, - CONDITION_SECURITY, - CONDITION_CAPABILITY, - CONDITION_AC_POWER, - - CONDITION_NEEDS_UPDATE, - CONDITION_FIRST_BOOT, - - CONDITION_PATH_EXISTS, - CONDITION_PATH_EXISTS_GLOB, - CONDITION_PATH_IS_DIRECTORY, - CONDITION_PATH_IS_SYMBOLIC_LINK, - CONDITION_PATH_IS_MOUNT_POINT, - CONDITION_PATH_IS_READ_WRITE, - CONDITION_DIRECTORY_NOT_EMPTY, - CONDITION_FILE_NOT_EMPTY, - CONDITION_FILE_IS_EXECUTABLE, - - CONDITION_NULL, - - _CONDITION_TYPE_MAX, - _CONDITION_TYPE_INVALID = -1 -} ConditionType; - -typedef enum ConditionResult { - CONDITION_UNTESTED, - CONDITION_SUCCEEDED, - CONDITION_FAILED, - CONDITION_ERROR, - _CONDITION_RESULT_MAX, - _CONDITION_RESULT_INVALID = -1 -} ConditionResult; - -typedef struct Condition { - ConditionType type:8; - - bool trigger:1; - bool negate:1; - - ConditionResult result:6; - - char *parameter; - - LIST_FIELDS(struct Condition, conditions); -} Condition; - -Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate); -void condition_free(Condition *c); -Condition* condition_free_list(Condition *c); - -int condition_test(Condition *c); - -void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); -void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); - -const char* condition_type_to_string(ConditionType t) _const_; -ConditionType condition_type_from_string(const char *s) _pure_; - -const char* assert_type_to_string(ConditionType t) _const_; -ConditionType assert_type_from_string(const char *s) _pure_; - -const char* condition_result_to_string(ConditionResult r) _const_; -ConditionResult condition_result_from_string(const char *s) _pure_; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c deleted file mode 100644 index 7cf222e4d2..0000000000 --- a/src/shared/conf-parser.c +++ /dev/null @@ -1,914 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "extract-word.h" -#include "fd-util.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "socket-util.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, - const char *section, - const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, - void *userdata) { - - const ConfigTableItem *t; - - assert(table); - assert(lvalue); - assert(func); - assert(ltype); - assert(data); - - for (t = table; t->lvalue; t++) { - - if (!streq(lvalue, t->lvalue)) - continue; - - if (!streq_ptr(section, t->section)) - continue; - - *func = t->parse; - *ltype = t->ltype; - *data = t->data; - return 1; - } - - return 0; -} - -int config_item_perf_lookup( - const void *table, - const char *section, - const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, - void *userdata) { - - ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; - const ConfigPerfItem *p; - - assert(table); - assert(lvalue); - assert(func); - assert(ltype); - assert(data); - - if (!section) - p = lookup(lvalue, strlen(lvalue)); - else { - char *key; - - key = strjoin(section, ".", lvalue, NULL); - if (!key) - return -ENOMEM; - - p = lookup(key, strlen(key)); - free(key); - } - - if (!p) - return 0; - - *func = p->parse; - *ltype = p->ltype; - *data = (uint8_t*) userdata + p->offset; - return 1; -} - -/* Run the user supplied parser for an assignment */ -static int next_assignment(const char *unit, - const char *filename, - unsigned line, - ConfigItemLookup lookup, - const void *table, - const char *section, - unsigned section_line, - const char *lvalue, - const char *rvalue, - bool relaxed, - void *userdata) { - - ConfigParserCallback func = NULL; - int ltype = 0; - void *data = NULL; - int r; - - assert(filename); - assert(line > 0); - assert(lookup); - assert(lvalue); - assert(rvalue); - - r = lookup(table, section, lvalue, &func, <ype, &data, userdata); - if (r < 0) - return r; - - if (r > 0) { - if (func) - return func(unit, filename, line, section, section_line, - lvalue, ltype, rvalue, data, userdata); - - return 0; - } - - /* Warn about unknown non-extension fields. */ - if (!relaxed && !startswith(lvalue, "X-")) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section); - - return 0; -} - -/* Parse a variable assignment line */ -static int parse_line(const char* unit, - const char *filename, - unsigned line, - const char *sections, - ConfigItemLookup lookup, - const void *table, - bool relaxed, - bool allow_include, - char **section, - unsigned *section_line, - bool *section_ignored, - char *l, - void *userdata) { - - char *e; - - assert(filename); - assert(line > 0); - assert(lookup); - assert(l); - - l = strstrip(l); - - if (!*l) - return 0; - - if (strchr(COMMENTS "\n", *l)) - return 0; - - if (startswith(l, ".include ")) { - _cleanup_free_ char *fn = NULL; - - /* .includes are a bad idea, we only support them here - * for historical reasons. They create cyclic include - * problems and make it difficult to detect - * configuration file changes with an easy - * stat(). Better approaches, such as .d/ drop-in - * snippets exist. - * - * Support for them should be eventually removed. */ - - if (!allow_include) { - log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); - return 0; - } - - fn = file_in_same_dir(filename, strstrip(l+9)); - if (!fn) - return -ENOMEM; - - return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata); - } - - if (*l == '[') { - size_t k; - char *n; - - k = strlen(l); - assert(k > 0); - - if (l[k-1] != ']') { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); - return -EBADMSG; - } - - n = strndup(l+1, k-2); - if (!n) - return -ENOMEM; - - if (sections && !nulstr_contains(sections, n)) { - - if (!relaxed && !startswith(n, "X-")) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); - - free(n); - *section = mfree(*section); - *section_line = 0; - *section_ignored = true; - } else { - free(*section); - *section = n; - *section_line = line; - *section_ignored = false; - } - - return 0; - } - - if (sections && !*section) { - - if (!relaxed && !*section_ignored) - 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, 0, "Missing '='."); - return -EINVAL; - } - - *e = 0; - e++; - - return next_assignment(unit, - filename, - line, - lookup, - table, - *section, - *section_line, - strstrip(l), - strstrip(e), - relaxed, - userdata); -} - -/* Go through the file and parse each line */ -int config_parse(const char *unit, - const char *filename, - FILE *f, - const char *sections, - ConfigItemLookup lookup, - const void *table, - bool relaxed, - bool allow_include, - bool warn, - void *userdata) { - - _cleanup_free_ char *section = NULL, *continuation = NULL; - _cleanup_fclose_ FILE *ours = NULL; - unsigned line = 0, section_line = 0; - bool section_ignored = false, allow_bom = true; - int r; - - assert(filename); - assert(lookup); - - if (!f) { - f = ours = fopen(filename, "re"); - if (!f) { - /* Only log on request, except for ENOENT, - * since we return 0 to the caller. */ - if (warn || errno == ENOENT) - log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR, - "Failed to open configuration file '%s': %m", filename); - return errno == ENOENT ? 0 : -errno; - } - } - - fd_warn_permissions(filename, fileno(f)); - - for (;;) { - char buf[LINE_MAX], *l, *p, *c = NULL, *e; - bool escaped = false; - - if (!fgets(buf, sizeof buf, f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read configuration file '%s': %m", filename); - } - - l = buf; - if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK)) - l += strlen(UTF8_BYTE_ORDER_MARK); - allow_bom = false; - - truncate_nl(l); - - if (continuation) { - c = strappend(continuation, l); - if (!c) { - if (warn) - log_oom(); - return -ENOMEM; - } - - continuation = mfree(continuation); - p = c; - } else - p = l; - - for (e = p; *e; e++) { - if (escaped) - escaped = false; - else if (*e == '\\') - escaped = true; - } - - if (escaped) { - *(e-1) = ' '; - - if (c) - continuation = c; - else { - continuation = strdup(l); - if (!continuation) { - if (warn) - log_oom(); - return -ENOMEM; - } - } - - continue; - } - - r = parse_line(unit, - filename, - ++line, - sections, - lookup, - table, - relaxed, - allow_include, - §ion, - §ion_line, - §ion_ignored, - p, - userdata); - free(c); - - if (r < 0) { - if (warn) - log_warning_errno(r, "Failed to parse file '%s': %m", - filename); - return r; - } - } - - return 0; -} - -/* Parse each config file in the specified directories. */ -int config_parse_many(const char *conf_file, - const char *conf_file_dirs, - const char *sections, - ConfigItemLookup lookup, - const void *table, - bool relaxed, - void *userdata) { - _cleanup_strv_free_ char **files = NULL; - char **fn; - int r; - - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) - return r; - - if (conf_file) { - r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); - if (r < 0) - return r; - } - - STRV_FOREACH(fn, files) { - r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata); - if (r < 0) - return r; - } - - return 0; -} - -#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) { \ - \ - vartype *i = data; \ - int r; \ - \ - assert(filename); \ - assert(lvalue); \ - assert(rvalue); \ - assert(data); \ - \ - r = conv_func(rvalue, i); \ - if (r < 0) \ - log_syntax(unit, LOG_ERR, filename, line, r, \ - "Failed to parse %s value, ignoring: %s", \ - #type, rvalue); \ - \ - return 0; \ - } \ - 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, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t *sz = data; - uint64_t v; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_size(rvalue, 1024, &v); - if (r < 0 || (uint64_t) (size_t) v != v) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); - return 0; - } - - *sz = (size_t) v; - return 0; -} - -int config_parse_si_size(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - size_t *sz = data; - uint64_t v; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_size(rvalue, 1000, &v); - if (r < 0 || (uint64_t) (size_t) v != v) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); - return 0; - } - - *sz = (size_t) v; - return 0; -} - -int config_parse_iec_uint64(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 *bytes = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_size(rvalue, 1024, bytes); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); - - return 0; -} - -int config_parse_bool(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 k; - bool *b = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); - return 0; - } - - *b = !!k; - return 0; -} - -int config_parse_tristate( - 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 k, *t = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - /* A tristate is pretty much a boolean, except that it can - * also take the special value -1, indicating "uninitialized", - * much like NULL is for a pointer type. */ - - k = parse_boolean(rvalue); - if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); - return 0; - } - - *t = !!k; - return 0; -} - -int config_parse_string( - 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) { - - char **s = data, *n; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (!utf8_is_valid(rvalue)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - return 0; - } - - if (isempty(rvalue)) - n = NULL; - else { - n = strdup(rvalue); - if (!n) - return log_oom(); - } - - free(*s); - *s = n; - - return 0; -} - -int config_parse_path( - 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) { - - char **s = data, *n; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (!utf8_is_valid(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, 0, "Not an absolute path, ignoring: %s", rvalue); - return 0; - } - - n = strdup(rvalue); - if (!n) - return log_oom(); - - path_kill_slashes(n); - - free(*s); - *s = n; - - return 0; -} - -int config_parse_strv(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) { - - char ***sv = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - char **empty; - - /* Empty assignment resets the list. As a special rule - * we actually fill in a real empty array here rather - * than NULL, since some code wants to know if - * something was set at all... */ - empty = new0(char*, 1); - if (!empty) - return log_oom(); - - strv_free(*sv); - *sv = empty; - - return 0; - } - - for (;;) { - char *word = NULL; - - r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES|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; - } - - if (!utf8_is_valid(word)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - free(word); - continue; - } - r = strv_consume(sv, word); - if (r < 0) - return log_oom(); - } - - return 0; -} - -int config_parse_log_facility( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - - int *o = data, x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = log_facility_unshifted_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue); - return 0; - } - - *o = (x << 3) | LOG_PRI(*o); - - return 0; -} - -int config_parse_log_level( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - - int *o = data, x; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - x = log_level_from_string(rvalue); - if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue); - return 0; - } - - *o = (*o & LOG_FACMASK) | x; - return 0; -} - -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 *sig = data, r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(sig); - - r = signal_from_string_try_harder(rvalue); - if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue); - return 0; - } - - *sig = r; - return 0; -} - -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) { - - unsigned long *personality = data, p; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(personality); - - p = personality_from_string(rvalue); - if (p == PERSONALITY_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); - return 0; - } - - *personality = p; - return 0; -} - -int config_parse_ifname( - 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) { - - char **s = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - *s = mfree(*s); - return 0; - } - - if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); - return 0; - } - - r = free_and_strdup(s, rvalue); - if (r < 0) - return log_oom(); - - return 0; -} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h deleted file mode 100644 index f6964e3fd4..0000000000 --- a/src/shared/conf-parser.h +++ /dev/null @@ -1,229 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "log.h" -#include "macro.h" - -/* An abstract parser for simple, line based, shallow configuration - * files consisting of variable assignments only. */ - -/* Prototype for a parser for a specific configuration setting */ -typedef int (*ConfigParserCallback)(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -/* Wraps information for parsing a specific configuration variable, to - * be stored in a simple array */ -typedef struct ConfigTableItem { - const char *section; /* Section */ - const char *lvalue; /* Name of the variable */ - ConfigParserCallback parse; /* Function that is called to parse the variable's value */ - int ltype; /* Distinguish different variables passed to the same callback */ - void *data; /* Where to store the variable's data */ -} ConfigTableItem; - -/* Wraps information for parsing a specific configuration variable, to - * be stored in a gperf perfect hashtable */ -typedef struct ConfigPerfItem { - const char *section_and_lvalue; /* Section + "." + name of the variable */ - ConfigParserCallback parse; /* Function that is called to parse the variable's value */ - int ltype; /* Distinguish different variables passed to the same callback */ - size_t offset; /* Offset where to store data, from the beginning of userdata */ -} ConfigPerfItem; - -/* Prototype for a low-level gperf lookup function */ -typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length); - -/* Prototype for a generic high-level lookup function */ -typedef int (*ConfigItemLookup)( - const void *table, - const char *section, - const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, - void *userdata); - -/* Linear table search implementation of ConfigItemLookup, based on - * ConfigTableItem arrays */ -int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); - -/* gperf implementation of ConfigItemLookup, based on gperf - * ConfigPerfItem tables */ -int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); - -int config_parse(const char *unit, - const char *filename, - FILE *f, - const char *sections, /* nulstr */ - ConfigItemLookup lookup, - const void *table, - bool relaxed, - bool allow_include, - bool warn, - void *userdata); - -int config_parse_many(const char *conf_file, /* possibly NULL */ - const char *conf_file_dirs, /* nulstr */ - const char *sections, /* nulstr */ - ConfigItemLookup lookup, - const void *table, - bool relaxed, - void *userdata); - -/* Generic parsers */ -int config_parse_int(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_unsigned(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_long(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_uint32(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_uint64(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_double(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_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_iec_uint64(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_bool(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_tristate(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_string(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_path(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_strv(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(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_nsec(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_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_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int 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); -int config_parse_ifname(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 DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ - int function(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) { \ - \ - type *i = data, x; \ - \ - assert(filename); \ - assert(lvalue); \ - assert(rvalue); \ - assert(data); \ - \ - if ((x = name##_from_string(rvalue)) < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, -x, \ - msg ", ignoring: %s", rvalue); \ - return 0; \ - } \ - \ - *i = x; \ - return 0; \ - } - -#define DEFINE_CONFIG_PARSE_ENUMV(function,name,type,invalid,msg) \ - int function(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) { \ - \ - type **enums = data, x, *ys; \ - _cleanup_free_ type *xs = NULL; \ - const char *word, *state; \ - size_t l, i = 0; \ - \ - assert(filename); \ - assert(lvalue); \ - assert(rvalue); \ - assert(data); \ - \ - xs = new0(type, 1); \ - if (!xs) \ - return -ENOMEM; \ - \ - *xs = invalid; \ - \ - FOREACH_WORD(word, l, rvalue, state) { \ - _cleanup_free_ char *en = NULL; \ - type *new_xs; \ - \ - en = strndup(word, l); \ - if (!en) \ - return -ENOMEM; \ - \ - if ((x = name##_from_string(en)) < 0) { \ - log_syntax(unit, LOG_ERR, filename, line, \ - -x, msg ", ignoring: %s", en); \ - continue; \ - } \ - \ - for (ys = xs; x != invalid && *ys != invalid; ys++) { \ - if (*ys == x) { \ - log_syntax(unit, LOG_ERR, filename, \ - line, -x, \ - "Duplicate entry, ignoring: %s", \ - en); \ - x = invalid; \ - } \ - } \ - \ - if (x == invalid) \ - continue; \ - \ - *(xs + i) = x; \ - new_xs = realloc(xs, (++i + 1) * sizeof(type)); \ - if (new_xs) \ - xs = new_xs; \ - else \ - return -ENOMEM; \ - \ - *(xs + i) = invalid; \ - } \ - \ - free(*enums); \ - *enums = xs; \ - xs = NULL; \ - \ - return 0; \ - } diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c deleted file mode 100644 index b2d464c117..0000000000 --- a/src/shared/dev-setup.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include - -#include "alloc-util.h" -#include "dev-setup.h" -#include "label.h" -#include "log.h" -#include "path-util.h" -#include "user-util.h" -#include "util.h" - -int dev_setup(const char *prefix, uid_t uid, gid_t gid) { - static const char symlinks[] = - "-/proc/kcore\0" "/dev/core\0" - "/proc/self/fd\0" "/dev/fd\0" - "/proc/self/fd/0\0" "/dev/stdin\0" - "/proc/self/fd/1\0" "/dev/stdout\0" - "/proc/self/fd/2\0" "/dev/stderr\0"; - - const char *j, *k; - int r; - - NULSTR_FOREACH_PAIR(j, k, symlinks) { - _cleanup_free_ char *link_name = NULL; - const char *n; - - if (j[0] == '-') { - j++; - - if (access(j, F_OK) < 0) - continue; - } - - if (prefix) { - link_name = prefix_root(prefix, k); - if (!link_name) - return -ENOMEM; - - n = link_name; - } else - n = k; - - r = symlink_label(j, n); - if (r < 0) - log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n); - - if (uid != UID_INVALID || gid != GID_INVALID) - if (lchown(n, uid, gid) < 0) - log_debug_errno(errno, "Failed to chown %s: %m", n); - } - - return 0; -} diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h deleted file mode 100644 index 5766a62060..0000000000 --- a/src/shared/dev-setup.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 . -***/ - -#include - -int dev_setup(const char *prefix, uid_t uid, gid_t gid); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c deleted file mode 100644 index 835557c6b2..0000000000 --- a/src/shared/dns-domain.c +++ /dev/null @@ -1,1322 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#ifdef HAVE_LIBIDN -#include -#include -#endif - -#include -#include -#include -#include -#include - -#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; - char *d; - int r = 0; - - assert(name); - assert(*name); - - n = *name; - d = dest; - - for (;;) { - if (*n == '.') { - n++; - break; - } - - if (*n == 0) - break; - - if (r >= DNS_LABEL_MAX) - return -EINVAL; - - if (sz <= 0) - return -ENOBUFS; - - if (*n == '\\') { - /* Escaped character */ - - n++; - - if (*n == 0) - /* Ending NUL */ - return -EINVAL; - - else if (*n == '\\' || *n == '.') { - /* Escaped backslash or dot */ - - if (d) - *(d++) = *n; - sz--; - r++; - n++; - - } else if (n[0] >= '0' && n[0] <= '9') { - unsigned k; - - /* Escaped literal ASCII character */ - - if (!(n[1] >= '0' && n[1] <= '9') || - !(n[2] >= '0' && n[2] <= '9')) - return -EINVAL; - - k = ((unsigned) (n[0] - '0') * 100) + - ((unsigned) (n[1] - '0') * 10) + - ((unsigned) (n[2] - '0')); - - /* 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; - - if (d) - *(d++) = (char) k; - sz--; - r++; - - n += 3; - } else - return -EINVAL; - - } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { - - /* Normal character */ - - if (d) - *(d++) = *n; - sz--; - r++; - n++; - } else - return -EINVAL; - } - - /* Empty label that is not at the end? */ - if (r == 0 && *n) - return -EINVAL; - - if (sz >= 1 && d) - *d = 0; - - *name = n; - return r; -} - -/* @label_terminal: terminal character of a label, updated to point to the terminal character of - * the previous label (always skipping one dot) or to NULL if there are no more - * labels. */ -int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { - const char *terminal; - int r; - - assert(name); - assert(label_terminal); - assert(dest); - - /* no more labels */ - if (!*label_terminal) { - if (sz >= 1) - *dest = 0; - - return 0; - } - - terminal = *label_terminal; - assert(*terminal == '.' || *terminal == 0); - - /* 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) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - - /* Find the start of the last label */ - if (*terminal == '.') { - const char *y; - unsigned slashes = 0; - - for (y = terminal - 1; y >= name && *y == '\\'; y--) - slashes++; - - if (slashes % 2 == 0) { - /* The '.' was not escaped */ - name = terminal + 1; - break; - } else { - terminal = y; - continue; - } - } - - terminal--; - } - - r = dns_label_unescape(&name, dest, sz); - if (r < 0) - return r; - - *label_terminal = terminal; - - return r; -} - -int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { - char *q; - - /* DNS labels must be between 1 and 63 characters long. A - * zero-length label does not exist. See RFC 2182, Section - * 11. */ - - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - if (sz < 1) - return -ENOBUFS; - - assert(p); - assert(dest); - - 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') || - (*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z')) { - - /* Proper character */ - - if (sz < 2) - return -ENOBUFS; - - *(q++) = *p; - 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); - - 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; - s = NULL; - - return r; -} - -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, l; - const char *p; - bool contains_8bit = false; - char buffer[DNS_LABEL_MAX+1]; - - assert(encoded); - assert(decoded); - - /* Converts an U-label into an A-label */ - - if (encoded_size <= 0) - return -EINVAL; - - for (p = encoded; p < encoded + encoded_size; p++) - if ((uint8_t) *p > 127) - contains_8bit = true; - - 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, buffer, 0) != 0) - return -EINVAL; - - l = strlen(buffer); - - /* Verify that 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 (int) l; -#else - return 0; -#endif -} - -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN - size_t input_size, output_size; - _cleanup_free_ uint32_t *input = NULL; - _cleanup_free_ char *result = NULL; - uint32_t *output = NULL; - size_t w; - - /* 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; - - if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0) - return 0; - - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - output_size = input_size; - output = newa(uint32_t, output_size); - - idna_to_unicode_44i(input, input_size, output, &output_size, 0); - - result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); - if (!result) - return -ENOMEM; - if (w <= 0) - 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; - - return w; -#else - return 0; -#endif -} - -int dns_name_concat(const char *a, const char *b, char **_ret) { - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - const char *p; - bool first = true; - int r; - - if (a) - p = a; - else if (b) { - p = b; - b = NULL; - } else - goto finish; - - for (;;) { - char label[DNS_LABEL_MAX]; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) { - if (*p != 0) - return -EINVAL; - - if (b) { - /* Now continue with the second string, if there is one */ - p = b; - b = NULL; - continue; - } - - break; - } - - if (_ret) { - 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 { - char escaped[DNS_LABEL_ESCAPED_MAX]; - - r = dns_label_escape(label, r, escaped, sizeof(escaped)); - if (r < 0) - return r; - } - - if (!first) - n++; - else - first = false; - - n += r; - } - -finish: - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; - - if (_ret) { - if (n == 0) { - /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (!GREEDY_REALLOC(ret, allocated, 2)) - return -ENOMEM; - - ret[n++] = '.'; - } else { - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - } - - ret[n] = 0; - *_ret = ret; - ret = NULL; - } - - return 0; -} - -void dns_name_hash_func(const void *s, struct siphash *state) { - const char *p = s; - int r; - - assert(p); - - for (;;) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - break; - if (r == 0) - break; - - 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 */ - } - - /* 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; - - assert(a); - assert(b); - - x = (const char *) a + strlen(a); - y = (const char *) b + strlen(b); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - if (x == NULL && y == NULL) - return 0; - - r = dns_label_unescape_suffix(a, &x, la, sizeof(la)); - q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb)); - if (r < 0 || q < 0) - return r - q; - - r = ascii_strcasecmp_nn(la, r, lb, q); - if (r != 0) - return r; - } -} - -const struct hash_ops dns_name_hash_ops = { - .hash = dns_name_hash_func, - .compare = dns_name_compare_func -}; - -int dns_name_equal(const char *x, const char *y) { - int r, q; - - assert(x); - assert(y); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - r = dns_label_unescape(&x, la, sizeof(la)); - if (r < 0) - return r; - - q = dns_label_unescape(&y, lb, sizeof(lb)); - if (q < 0) - return q; - - 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; - - assert(name); - assert(suffix); - - n = name; - s = suffix; - - for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - - r = dns_label_unescape(&n, ln, sizeof(ln)); - if (r < 0) - return r; - - if (!saved_n) - saved_n = n; - - q = dns_label_unescape(&s, ls, sizeof(ls)); - if (q < 0) - return q; - - if (r == 0 && q == 0) - return true; - if (r == 0 && saved_n == n) - return false; - - 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; - n = saved_n; - saved_n = NULL; - } - } -} - -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; - - /* Determine if b is strictly greater than a and strictly smaller than c. - We consider the order of names to be circular, so that if a is - strictly greater than c, we consider b to be between them if it is - either greater than a or smaller than c. This is how the canonical - DNS name order used in NSEC records work. */ - - n = dns_name_compare_func(a, c); - if (n == 0) - return -EINVAL; - else if (n < 0) - /* a<---b--->c */ - return dns_name_compare_func(a, b) < 0 && - dns_name_compare_func(b, c) < 0; - else - /* <--b--c a--b--> */ - return dns_name_compare_func(b, c) < 0 || - dns_name_compare_func(a, b) < 0; -} - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { - const uint8_t *p; - int r; - - assert(a); - assert(ret); - - p = (const uint8_t*) a; - - if (family == AF_INET) - r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); - else if (family == AF_INET6) - r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", - hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), - hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), - hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), - hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), - hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), - hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), - hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), - hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); - else - return -EAFNOSUPPORT; - if (r < 0) - return -ENOMEM; - - return 0; -} - -int dns_name_address(const char *p, int *family, union in_addr_union *address) { - int r; - - assert(p); - assert(family); - assert(address); - - r = dns_name_endswith(p, "in-addr.arpa"); - if (r < 0) - return r; - if (r > 0) { - uint8_t a[4]; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a); i++) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - if (r > 3) - return -EINVAL; - - r = safe_atou8(label, &a[i]); - if (r < 0) - return r; - } - - r = dns_name_equal(p, "in-addr.arpa"); - if (r <= 0) - return r; - - *family = AF_INET; - address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | - ((uint32_t) a[2] << 16) | - ((uint32_t) a[1] << 8) | - (uint32_t) a[0]); - - return 1; - } - - r = dns_name_endswith(p, "ip6.arpa"); - if (r < 0) - return r; - if (r > 0) { - struct in6_addr a; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { - char label[DNS_LABEL_MAX+1]; - int x, y; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - x = unhexchar(label[0]); - if (x < 0) - return -EINVAL; - - r = dns_label_unescape(&p, label, sizeof(label)); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - y = unhexchar(label[0]); - if (y < 0) - return -EINVAL; - - a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; - } - - r = dns_name_equal(p, "ip6.arpa"); - if (r <= 0) - return r; - - *family = AF_INET6; - address->in6 = a; - return 1; - } - - return 0; -} - -bool dns_name_is_root(const char *name) { - - assert(name); - - /* There are exactly two ways to encode the root domain name: - * as empty string, or with a single dot. */ - - return STR_IN_SET(name, "", "."); -} - -bool dns_name_is_single_label(const char *name) { - int r; - - assert(name); - - 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; - - 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 = 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 . . */ - - 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; - - 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 deleted file mode 100644 index af780f0b8b..0000000000 --- a/src/shared/dns-domain.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include -#include -#include -#include - -#include "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 - -/* 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 *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); - -int dns_name_concat(const char *a, const char *b, char **ret); - -static inline int dns_name_normalize(const char *s, char **ret) { - /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, ret); -} - -static inline int dns_name_is_valid(const char *s) { - int r; - - /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, NULL); - if (r == -EINVAL) - return 0; - if (r < 0) - return r; - return 1; -} - -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); - -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 deleted file mode 100644 index b9cd952ac8..0000000000 --- a/src/shared/dropin.c +++ /dev/null @@ -1,251 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "dropin.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio-label.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) { - - _cleanup_free_ char *b = NULL; - char *p, *q; - - char prefix[DECIMAL_STR_MAX(unsigned)]; - - assert(unit); - assert(name); - assert(_p); - assert(_q); - - sprintf(prefix, "%u", level); - - b = xescape(name, "/."); - if (!b) - return -ENOMEM; - - if (!filename_is_valid(b)) - return -EINVAL; - - p = strjoin(dir, "/", unit, ".d", NULL); - if (!p) - return -ENOMEM; - - q = strjoin(p, "/", prefix, "-", b, ".conf", NULL); - if (!q) { - free(p); - return -ENOMEM; - } - - *_p = p; - *_q = q; - return 0; -} - -int write_drop_in(const char *dir, const char *unit, unsigned level, - const char *name, const char *data) { - - _cleanup_free_ char *p = NULL, *q = NULL; - int r; - - assert(dir); - assert(unit); - assert(name); - assert(data); - - r = drop_in_file(dir, unit, level, name, &p, &q); - if (r < 0) - return r; - - (void) mkdir_p(p, 0755); - return write_string_file_atomic_label(q, data); -} - -int write_drop_in_format(const char *dir, const char *unit, unsigned level, - const char *name, const char *format, ...) { - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - assert(dir); - assert(unit); - assert(name); - assert(format); - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return write_drop_in(dir, unit, level, name, p); -} - -static int iterate_dir( - const char *path, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv) { - - _cleanup_closedir_ DIR *d = NULL; - int r; - - assert(path); - - /* The config directories are special, since the order of the - * drop-ins matters */ - if (dependency < 0) { - r = strv_extend(strv, path); - if (r < 0) - return log_oom(); - - return 0; - } - - assert(consumer); - - d = opendir(path); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open directory %s: %m", path); - } - - for (;;) { - struct dirent *de; - _cleanup_free_ char *f = NULL; - - errno = 0; - de = readdir(d); - if (!de && errno > 0) - return log_error_errno(errno, "Failed to read directory %s: %m", path); - - if (!de) - break; - - if (hidden_or_backup_file(de->d_name)) - continue; - - f = strjoin(path, "/", de->d_name, NULL); - if (!f) - return log_oom(); - - r = consumer(dependency, de->d_name, f, arg); - if (r < 0) - return r; - } - - return 0; -} - -int unit_file_process_dir( - Set *unit_path_cache, - const char *unit_path, - const char *name, - const char *suffix, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv) { - - _cleanup_free_ char *path = NULL; - int r; - - assert(unit_path); - assert(name); - assert(suffix); - - path = strjoin(unit_path, "/", name, suffix, NULL); - if (!path) - return log_oom(); - - if (!unit_path_cache || set_get(unit_path_cache, path)) - (void) iterate_dir(path, dependency, consumer, arg, strv); - - if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { - _cleanup_free_ char *template = NULL, *p = NULL; - /* Also try the template dir */ - - r = unit_name_template(name, &template); - if (r < 0) - return log_error_errno(r, "Failed to generate template from unit name: %m"); - - p = strjoin(unit_path, "/", template, suffix, NULL); - if (!p) - return log_oom(); - - if (!unit_path_cache || set_get(unit_path_cache, p)) - (void) iterate_dir(p, dependency, consumer, arg, strv); - } - - return 0; -} - -int unit_file_find_dropin_paths( - char **lookup_path, - Set *unit_path_cache, - Set *names, - char ***paths) { - - _cleanup_strv_free_ char **strv = NULL, **ans = NULL; - Iterator i; - char *t; - int r; - - assert(paths); - - SET_FOREACH(t, names, i) { - char **p; - - STRV_FOREACH(p, lookup_path) - unit_file_process_dir(unit_path_cache, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, NULL, NULL, &strv); - } - - if (strv_isempty(strv)) - return 0; - - r = conf_files_list_strv(&ans, ".conf", NULL, (const char**) strv); - if (r < 0) - return log_warning_errno(r, "Failed to get list of configuration files: %m"); - - *paths = ans; - ans = NULL; - return 1; -} diff --git a/src/shared/dropin.h b/src/shared/dropin.h deleted file mode 100644 index c1936f397b..0000000000 --- a/src/shared/dropin.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/*** - 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 . -***/ - -#include "hashmap.h" -#include "macro.h" -#include "set.h" -#include "unit-name.h" - -int drop_in_file(const char *dir, const char *unit, unsigned level, - const char *name, char **_p, char **_q); - -int write_drop_in(const char *dir, const char *unit, unsigned level, - const char *name, const char *data); - -int write_drop_in_format(const char *dir, const char *unit, unsigned level, - const char *name, const char *format, ...) _printf_(5, 6); - -/** - * This callback will be called for each directory entry @entry, - * with @filepath being the full path to the entry. - * - * If return value is negative, loop will be aborted. - */ -typedef int (*dependency_consumer_t)(UnitDependency dependency, - const char *entry, - const char* filepath, - void *arg); - -int unit_file_process_dir( - Set * unit_path_cache, - const char *unit_path, - const char *name, - const char *suffix, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv); - -int unit_file_find_dropin_paths( - char **lookup_path, - Set *unit_path_cache, - Set *names, - char ***paths); diff --git a/src/shared/efivars.c b/src/shared/efivars.c deleted file mode 100644 index 8631a5a5d9..0000000000 --- a/src/shared/efivars.c +++ /dev/null @@ -1,715 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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" - -#ifdef ENABLE_EFI - -#define LOAD_OPTION_ACTIVE 0x00000001 -#define MEDIA_DEVICE_PATH 0x04 -#define MEDIA_HARDDRIVE_DP 0x01 -#define MEDIA_FILEPATH_DP 0x04 -#define SIGNATURE_TYPE_GUID 0x02 -#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 -#define END_DEVICE_PATH_TYPE 0x7f -#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff -#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 - -struct boot_option { - uint32_t attr; - uint16_t path_len; - uint16_t title[]; -} _packed_; - -struct drive_path { - uint32_t part_nr; - uint64_t part_start; - uint64_t part_size; - char signature[16]; - uint8_t mbr_type; - uint8_t signature_type; -} _packed_; - -struct device_path { - uint8_t type; - uint8_t sub_type; - uint16_t length; - union { - uint16_t path[0]; - struct drive_path drive; - }; -} _packed_; - -bool is_efi_boot(void) { - return access("/sys/firmware/efi", F_OK) >= 0; -} - -static int read_flag(const char *varname) { - int r; - _cleanup_free_ void *v = NULL; - size_t s; - uint8_t b; - - r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s); - if (r < 0) - return r; - - if (s != 1) - return -EINVAL; - - b = *(uint8_t *)v; - r = b > 0; - return r; -} - -bool is_efi_secure_boot(void) { - return read_flag("SecureBoot") > 0; -} - -bool is_efi_secure_boot_setup_mode(void) { - return read_flag("SetupMode") > 0; -} - -int efi_reboot_to_firmware_supported(void) { - int r; - size_t s; - uint64_t b; - _cleanup_free_ void *v = NULL; - - if (!is_efi_boot() || detect_container() > 0) - return -EOPNOTSUPP; - - r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s); - if (r < 0) - return r; - else if (s != sizeof(uint64_t)) - return -EINVAL; - - b = *(uint64_t *)v; - b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - return b > 0 ? 0 : -EOPNOTSUPP; -} - -static int get_os_indications(uint64_t *os_indication) { - int r; - size_t s; - _cleanup_free_ void *v = NULL; - - r = efi_reboot_to_firmware_supported(); - if (r < 0) - return r; - - r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s); - if (r == -ENOENT) { - /* Some firmware implementations that do support - * OsIndications and report that with - * OsIndicationsSupported will remove the - * OsIndications variable when it is unset. Let's - * pretend it's 0 then, to hide this implementation - * detail. Note that this call will return -ENOENT - * then only if the support for OsIndications is - * missing entirely, as determined by - * efi_reboot_to_firmware_supported() above. */ - *os_indication = 0; - return 0; - } else if (r < 0) - return r; - else if (s != sizeof(uint64_t)) - return -EINVAL; - - *os_indication = *(uint64_t *)v; - return 0; -} - -int efi_get_reboot_to_firmware(void) { - int r; - uint64_t b; - - r = get_os_indications(&b); - if (r < 0) - return r; - - return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI); -} - -int efi_set_reboot_to_firmware(bool value) { - int r; - uint64_t b, b_new; - - r = get_os_indications(&b); - if (r < 0) - return r; - - if (value) - b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - else - b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - - /* Avoid writing to efi vars store if we can due to firmware bugs. */ - if (b != b_new) - return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b_new, sizeof(uint64_t)); - - return 0; -} - -int efi_get_variable( - sd_id128_t vendor, - const char *name, - uint32_t *attribute, - void **value, - size_t *size) { - - _cleanup_close_ int fd = -1; - _cleanup_free_ char *p = NULL; - uint32_t a; - ssize_t n; - struct stat st; - _cleanup_free_ void *buf = NULL; - - assert(name); - assert(value); - assert(size); - - if (asprintf(&p, - "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - name, SD_ID128_FORMAT_VAL(vendor)) < 0) - return -ENOMEM; - - fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (fstat(fd, &st) < 0) - return -errno; - if (st.st_size < 4) - return -EIO; - if (st.st_size > 4*1024*1024 + 4) - return -E2BIG; - - n = read(fd, &a, sizeof(a)); - if (n < 0) - return -errno; - if (n != sizeof(a)) - return -EIO; - - buf = malloc(st.st_size - 4 + 2); - if (!buf) - return -ENOMEM; - - n = read(fd, buf, (size_t) st.st_size - 4); - if (n < 0) - return -errno; - if (n != (ssize_t) st.st_size - 4) - return -EIO; - - /* Always NUL terminate (2 bytes, to protect UTF-16) */ - ((char*) buf)[st.st_size - 4] = 0; - ((char*) buf)[st.st_size - 4 + 1] = 0; - - *value = buf; - buf = NULL; - *size = (size_t) st.st_size - 4; - - if (attribute) - *attribute = a; - - return 0; -} - -int efi_set_variable( - sd_id128_t vendor, - const char *name, - const void *value, - size_t size) { - - struct var { - uint32_t attr; - char buf[]; - } _packed_ * _cleanup_free_ buf = NULL; - _cleanup_free_ char *p = NULL; - _cleanup_close_ int fd = -1; - - assert(name); - - if (asprintf(&p, - "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - name, SD_ID128_FORMAT_VAL(vendor)) < 0) - return -ENOMEM; - - if (size == 0) { - if (unlink(p) < 0) - return -errno; - return 0; - } - - fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644); - if (fd < 0) - return -errno; - - buf = malloc(sizeof(uint32_t) + size); - if (!buf) - return -ENOMEM; - - buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; - memcpy(buf->buf, value, size); - - return loop_write(fd, buf, sizeof(uint32_t) + size, false); -} - -int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) { - _cleanup_free_ void *s = NULL; - size_t ss = 0; - int r; - char *x; - - r = efi_get_variable(vendor, name, NULL, &s, &ss); - if (r < 0) - return r; - - x = utf16_to_utf8(s, ss); - if (!x) - return -ENOMEM; - - *p = x; - return 0; -} - -static size_t utf16_size(const uint16_t *s) { - size_t l = 0; - - while (s[l] > 0) - l++; - - return (l+1) * sizeof(uint16_t); -} - -static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) { - struct uuid { - uint32_t u1; - uint16_t u2; - uint16_t u3; - uint8_t u4[8]; - } _packed_; - const struct uuid *uuid = guid; - - id128->bytes[0] = (uuid->u1 >> 24) & 0xff; - id128->bytes[1] = (uuid->u1 >> 16) & 0xff; - id128->bytes[2] = (uuid->u1 >> 8) & 0xff; - id128->bytes[3] = (uuid->u1) & 0xff; - id128->bytes[4] = (uuid->u2 >> 8) & 0xff; - id128->bytes[5] = (uuid->u2) & 0xff; - id128->bytes[6] = (uuid->u3 >> 8) & 0xff; - id128->bytes[7] = (uuid->u3) & 0xff; - memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4)); -} - -int efi_get_boot_option( - uint16_t id, - char **title, - sd_id128_t *part_uuid, - char **path, - bool *active) { - - char boot_id[9]; - _cleanup_free_ uint8_t *buf = NULL; - size_t l; - struct boot_option *header; - size_t title_size; - _cleanup_free_ char *s = NULL, *p = NULL; - sd_id128_t p_uuid = SD_ID128_NULL; - int r; - - xsprintf(boot_id, "Boot%04X", id); - r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l); - if (r < 0) - return r; - if (l < sizeof(struct boot_option)) - return -ENOENT; - - header = (struct boot_option *)buf; - title_size = utf16_size(header->title); - if (title_size > l - offsetof(struct boot_option, title)) - return -EINVAL; - - if (title) { - s = utf16_to_utf8(header->title, title_size); - if (!s) - return -ENOMEM; - } - - if (header->path_len > 0) { - uint8_t *dbuf; - size_t dnext; - - dbuf = buf + offsetof(struct boot_option, title) + title_size; - dnext = 0; - while (dnext < header->path_len) { - struct device_path *dpath; - - dpath = (struct device_path *)(dbuf + dnext); - if (dpath->length < 4) - break; - - /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ - if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) - break; - - dnext += dpath->length; - - /* Type 0x04 – Media Device Path */ - if (dpath->type != MEDIA_DEVICE_PATH) - continue; - - /* Sub-Type 1 – Hard Drive */ - if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { - /* 0x02 – GUID Partition Table */ - if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) - continue; - - /* 0x02 – GUID signature */ - if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) - continue; - - if (part_uuid) - efi_guid_to_id128(dpath->drive.signature, &p_uuid); - continue; - } - - /* Sub-Type 4 – File Path */ - if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) { - p = utf16_to_utf8(dpath->path, dpath->length-4); - efi_tilt_backslashes(p); - continue; - } - } - } - - if (title) { - *title = s; - s = NULL; - } - if (part_uuid) - *part_uuid = p_uuid; - if (path) { - *path = p; - p = NULL; - } - if (active) - *active = !!(header->attr & LOAD_OPTION_ACTIVE); - - return 0; -} - -static void to_utf16(uint16_t *dest, const char *src) { - int i; - - for (i = 0; src[i] != '\0'; i++) - dest[i] = src[i]; - dest[i] = '\0'; -} - -struct guid { - uint32_t u1; - uint16_t u2; - uint16_t u3; - uint8_t u4[8]; -} _packed_; - -static void id128_to_efi_guid(sd_id128_t id, void *guid) { - struct guid *uuid = guid; - - uuid->u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3]; - uuid->u2 = id.bytes[4] << 8 | id.bytes[5]; - uuid->u3 = id.bytes[6] << 8 | id.bytes[7]; - memcpy(uuid->u4, id.bytes+8, sizeof(uuid->u4)); -} - -static uint16_t *tilt_slashes(uint16_t *s) { - uint16_t *p; - - for (p = s; *p; p++) - if (*p == '/') - *p = '\\'; - - return s; -} - -int efi_add_boot_option(uint16_t id, const char *title, - uint32_t part, uint64_t pstart, uint64_t psize, - sd_id128_t part_uuid, const char *path) { - char boot_id[9]; - size_t size; - size_t title_len; - size_t path_len; - struct boot_option *option; - struct device_path *devicep; - _cleanup_free_ char *buf = NULL; - - title_len = (strlen(title)+1) * 2; - path_len = (strlen(path)+1) * 2; - - buf = calloc(sizeof(struct boot_option) + title_len + - sizeof(struct drive_path) + - sizeof(struct device_path) + path_len, 1); - if (!buf) - return -ENOMEM; - - /* header */ - option = (struct boot_option *)buf; - option->attr = LOAD_OPTION_ACTIVE; - option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + - offsetof(struct device_path, path) + path_len + - offsetof(struct device_path, path); - to_utf16(option->title, title); - size = offsetof(struct boot_option, title) + title_len; - - /* partition info */ - devicep = (struct device_path *)(buf + size); - devicep->type = MEDIA_DEVICE_PATH; - devicep->sub_type = MEDIA_HARDDRIVE_DP; - devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); - devicep->drive.part_nr = part; - devicep->drive.part_start = pstart; - devicep->drive.part_size = psize; - devicep->drive.signature_type = SIGNATURE_TYPE_GUID; - devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; - id128_to_efi_guid(part_uuid, devicep->drive.signature); - size += devicep->length; - - /* path to loader */ - devicep = (struct device_path *)(buf + size); - devicep->type = MEDIA_DEVICE_PATH; - devicep->sub_type = MEDIA_FILEPATH_DP; - devicep->length = offsetof(struct device_path, path) + path_len; - to_utf16(devicep->path, path); - tilt_slashes(devicep->path); - size += devicep->length; - - /* end of path */ - devicep = (struct device_path *)(buf + size); - devicep->type = END_DEVICE_PATH_TYPE; - devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; - devicep->length = offsetof(struct device_path, path); - size += devicep->length; - - xsprintf(boot_id, "Boot%04X", id); - return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size); -} - -int efi_remove_boot_option(uint16_t id) { - char boot_id[9]; - - xsprintf(boot_id, "Boot%04X", id); - return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0); -} - -int efi_get_boot_order(uint16_t **order) { - _cleanup_free_ void *buf = NULL; - size_t l; - int r; - - r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l); - if (r < 0) - return r; - - if (l <= 0) - return -ENOENT; - - if (l % sizeof(uint16_t) > 0 || - l / sizeof(uint16_t) > INT_MAX) - return -EINVAL; - - *order = buf; - buf = NULL; - return (int) (l / sizeof(uint16_t)); -} - -int efi_set_boot_order(uint16_t *order, size_t n) { - return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t)); -} - -static int boot_id_hex(const char s[4]) { - int i; - int id = 0; - - for (i = 0; i < 4; i++) - if (s[i] >= '0' && s[i] <= '9') - id |= (s[i] - '0') << (3 - i) * 4; - else if (s[i] >= 'A' && s[i] <= 'F') - id |= (s[i] - 'A' + 10) << (3 - i) * 4; - else - return -EINVAL; - - return id; -} - -static int cmp_uint16(const void *_a, const void *_b) { - const uint16_t *a = _a, *b = _b; - - return (int)*a - (int)*b; -} - -int efi_get_boot_options(uint16_t **options) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *de; - _cleanup_free_ uint16_t *list = NULL; - size_t alloc = 0; - int count = 0; - - assert(options); - - dir = opendir("/sys/firmware/efi/efivars/"); - if (!dir) - return -errno; - - FOREACH_DIRENT(de, dir, return -errno) { - int id; - - if (strncmp(de->d_name, "Boot", 4) != 0) - continue; - - if (strlen(de->d_name) != 45) - continue; - - if (strcmp(de->d_name + 8, "-8be4df61-93ca-11d2-aa0d-00e098032b8c") != 0) - continue; - - id = boot_id_hex(de->d_name + 4); - if (id < 0) - continue; - - if (!GREEDY_REALLOC(list, alloc, count + 1)) - return -ENOMEM; - - list[count++] = id; - } - - qsort_safe(list, count, sizeof(uint16_t), cmp_uint16); - - *options = list; - list = NULL; - return count; -} - -static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) { - _cleanup_free_ char *j = NULL; - int r; - uint64_t x = 0; - - assert(name); - assert(u); - - r = efi_get_variable_string(EFI_VENDOR_LOADER, name, &j); - if (r < 0) - return r; - - r = safe_atou64(j, &x); - if (r < 0) - return r; - - *u = x; - return 0; -} - -int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { - uint64_t x, y; - int r; - - assert(firmware); - assert(loader); - - r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x); - if (r < 0) - return r; - - r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeExecUSec", &y); - if (r < 0) - return r; - - if (y == 0 || y < x) - return -EIO; - - if (y > USEC_PER_HOUR) - return -EIO; - - *firmware = x; - *loader = y; - - return 0; -} - -int efi_loader_get_device_part_uuid(sd_id128_t *u) { - _cleanup_free_ char *p = NULL; - int r, parsed[16]; - - r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p); - if (r < 0) - return r; - - if (sscanf(p, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - &parsed[0], &parsed[1], &parsed[2], &parsed[3], - &parsed[4], &parsed[5], &parsed[6], &parsed[7], - &parsed[8], &parsed[9], &parsed[10], &parsed[11], - &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) - return -EIO; - - if (u) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(parsed); i++) - u->bytes[i] = parsed[i]; - } - - return 0; -} - -#endif - -char *efi_tilt_backslashes(char *s) { - char *p; - - for (p = s; *p; p++) - if (*p == '\\') - *p = '/'; - - return s; -} diff --git a/src/shared/efivars.h b/src/shared/efivars.h deleted file mode 100644 index b61d14c4ec..0000000000 --- a/src/shared/efivars.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-id128.h" - -#include "time-util.h" - -#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f) -#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c) -#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 -#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 -#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 - -#ifdef ENABLE_EFI - -bool is_efi_boot(void); -bool is_efi_secure_boot(void); -bool is_efi_secure_boot_setup_mode(void); -int efi_reboot_to_firmware_supported(void); -int efi_get_reboot_to_firmware(void); -int efi_set_reboot_to_firmware(bool value); - -int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size); -int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size); -int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p); - -int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active); -int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path); -int efi_remove_boot_option(uint16_t id); -int efi_get_boot_order(uint16_t **order); -int efi_set_boot_order(uint16_t *order, size_t n); -int efi_get_boot_options(uint16_t **options); - -int efi_loader_get_device_part_uuid(sd_id128_t *u); -int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader); - -#else - -static inline bool is_efi_boot(void) { - return false; -} - -static inline bool is_efi_secure_boot(void) { - return false; -} - -static inline bool is_efi_secure_boot_setup_mode(void) { - return false; -} - -static inline int efi_reboot_to_firmware_supported(void) { - return -EOPNOTSUPP; -} - -static inline int efi_get_reboot_to_firmware(void) { - return -EOPNOTSUPP; -} - -static inline int efi_set_reboot_to_firmware(bool value) { - return -EOPNOTSUPP; -} - -static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) { - return -EOPNOTSUPP; -} - -static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) { - return -EOPNOTSUPP; -} - -static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) { - return -EOPNOTSUPP; -} - -static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) { - return -EOPNOTSUPP; -} - -static inline int efi_remove_boot_option(uint16_t id) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_order(uint16_t **order) { - return -EOPNOTSUPP; -} - -static inline int efi_set_boot_order(uint16_t *order, size_t n) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_options(uint16_t **options) { - return -EOPNOTSUPP; -} - -static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) { - return -EOPNOTSUPP; -} - -static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { - return -EOPNOTSUPP; -} - -#endif - -char *efi_tilt_backslashes(char *s); diff --git a/src/shared/fdset.c b/src/shared/fdset.c deleted file mode 100644 index 527f27bc67..0000000000 --- a/src/shared/fdset.c +++ /dev/null @@ -1,273 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "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) - -FDSet *fdset_new(void) { - return MAKE_FDSET(set_new(NULL)); -} - -int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) { - unsigned i; - FDSet *s; - int r; - - assert(ret); - - s = fdset_new(); - if (!s) - return -ENOMEM; - - for (i = 0; i < n_fds; i++) { - - r = fdset_put(s, fds[i]); - if (r < 0) { - set_free(MAKE_SET(s)); - return r; - } - } - - *ret = s; - return 0; -} - -FDSet* fdset_free(FDSet *s) { - void *p; - - while ((p = set_steal_first(MAKE_SET(s)))) { - /* Valgrind's fd might have ended up in this set here, - * due to fdset_new_fill(). We'll ignore all failures - * here, so that the EBADFD that valgrind will return - * us on close() doesn't influence us */ - - /* When reloading duplicates of the private bus - * connection fds and suchlike are closed here, which - * has no effect at all, since they are only - * duplicates. So don't be surprised about these log - * messages. */ - - log_debug("Closing left-over fd %i", PTR_TO_FD(p)); - close_nointr(PTR_TO_FD(p)); - } - - set_free(MAKE_SET(s)); - return NULL; -} - -int fdset_put(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return set_put(MAKE_SET(s), FD_TO_PTR(fd)); -} - -int fdset_put_dup(FDSet *s, int fd) { - int copy, r; - - assert(s); - assert(fd >= 0); - - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (copy < 0) - return -errno; - - r = fdset_put(s, copy); - if (r < 0) { - safe_close(copy); - return r; - } - - return copy; -} - -bool fdset_contains(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return !!set_get(MAKE_SET(s), FD_TO_PTR(fd)); -} - -int fdset_remove(FDSet *s, int fd) { - assert(s); - assert(fd >= 0); - - return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT; -} - -int fdset_new_fill(FDSet **_s) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - FDSet *s; - - assert(_s); - - /* Creates an fdset and fills in all currently open file - * descriptors. */ - - d = opendir("/proc/self/fd"); - if (!d) - return -errno; - - s = fdset_new(); - if (!s) { - r = -ENOMEM; - goto finish; - } - - while ((de = readdir(d))) { - int fd = -1; - - if (hidden_or_backup_file(de->d_name)) - continue; - - r = safe_atoi(de->d_name, &fd); - if (r < 0) - goto finish; - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - r = fdset_put(s, fd); - if (r < 0) - goto finish; - } - - r = 0; - *_s = s; - s = NULL; - -finish: - /* We won't close the fds here! */ - if (s) - set_free(MAKE_SET(s)); - - return r; -} - -int fdset_cloexec(FDSet *fds, bool b) { - Iterator i; - void *p; - int r; - - assert(fds); - - SET_FOREACH(p, MAKE_SET(fds), i) { - r = fd_cloexec(PTR_TO_FD(p), b); - if (r < 0) - return r; - } - - return 0; -} - -int fdset_new_listen_fds(FDSet **_s, bool unset) { - int n, fd, r; - FDSet *s; - - assert(_s); - - /* Creates an fdset and fills in all passed file descriptors */ - - s = fdset_new(); - if (!s) { - r = -ENOMEM; - goto fail; - } - - n = sd_listen_fds(unset); - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { - r = fdset_put(s, fd); - if (r < 0) - goto fail; - } - - *_s = s; - return 0; - - -fail: - if (s) - set_free(MAKE_SET(s)); - - return r; -} - -int fdset_close_others(FDSet *fds) { - void *e; - Iterator i; - int *a; - unsigned j, m; - - j = 0, m = fdset_size(fds); - a = alloca(sizeof(int) * m); - SET_FOREACH(e, MAKE_SET(fds), i) - a[j++] = PTR_TO_FD(e); - - assert(j == m); - - return close_all_fds(a, j); -} - -unsigned fdset_size(FDSet *fds) { - return set_size(MAKE_SET(fds)); -} - -bool fdset_isempty(FDSet *fds) { - return set_isempty(MAKE_SET(fds)); -} - -int fdset_iterate(FDSet *s, Iterator *i) { - void *p; - - if (!set_iterate(MAKE_SET(s), i, &p)) - return -ENOENT; - - return PTR_TO_FD(p); -} - -int fdset_steal_first(FDSet *fds) { - void *p; - - p = set_steal_first(MAKE_SET(fds)); - if (!p) - return -ENOENT; - - return PTR_TO_FD(p); -} diff --git a/src/shared/fdset.h b/src/shared/fdset.h deleted file mode 100644 index 16efe5bdf2..0000000000 --- a/src/shared/fdset.h +++ /dev/null @@ -1,58 +0,0 @@ -#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 . -***/ - -#include - -#include "hashmap.h" -#include "macro.h" -#include "set.h" - -typedef struct FDSet FDSet; - -FDSet* fdset_new(void); -FDSet* fdset_free(FDSet *s); - -int fdset_put(FDSet *s, int fd); -int fdset_put_dup(FDSet *s, int fd); - -bool fdset_contains(FDSet *s, int fd); -int fdset_remove(FDSet *s, int fd); - -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); - -int fdset_cloexec(FDSet *fds, bool b); - -int fdset_close_others(FDSet *fds); - -unsigned fdset_size(FDSet *fds); -bool fdset_isempty(FDSet *fds); - -int fdset_iterate(FDSet *s, Iterator *i); - -int fdset_steal_first(FDSet *fds); - -#define FDSET_FOREACH(fd, fds, i) \ - for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i))) - -DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free); -#define _cleanup_fdset_free_ _cleanup_(fdset_freep) diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c deleted file mode 100644 index f73108eaa3..0000000000 --- a/src/shared/firewall-util.c +++ /dev/null @@ -1,357 +0,0 @@ -/*** - 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 . -***/ - -#warning "Temporary work-around for broken glibc vs. linux kernel header definitions" -#warning "This really should be removed sooner rather than later, when this is fixed upstream" -#define _NET_IF_H 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "firewall-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "socket-util.h" - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); - -static int entry_fill_basics( - struct ipt_entry *entry, - int protocol, - const char *in_interface, - const union in_addr_union *source, - unsigned source_prefixlen, - const char *out_interface, - const union in_addr_union *destination, - unsigned destination_prefixlen) { - - assert(entry); - - if (out_interface && !ifname_valid(out_interface)) - return -EINVAL; - if (in_interface && !ifname_valid(in_interface)) - return -EINVAL; - - entry->ip.proto = protocol; - - if (in_interface) { - strcpy(entry->ip.iniface, in_interface); - memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1); - } - if (source) { - entry->ip.src = source->in; - in_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen); - } - - if (out_interface) { - strcpy(entry->ip.outiface, out_interface); - memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1); - } - if (destination) { - entry->ip.dst = destination->in; - in_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen); - } - - return 0; -} - -int fw_add_masquerade( - bool add, - int af, - int protocol, - const union in_addr_union *source, - unsigned source_prefixlen, - const char *out_interface, - const union in_addr_union *destination, - unsigned destination_prefixlen) { - - _cleanup_(iptc_freep) struct xtc_handle *h = NULL; - struct ipt_entry *entry, *mask; - struct ipt_entry_target *t; - size_t sz; - struct nf_nat_ipv4_multi_range_compat *mr; - int r; - - if (af != AF_INET) - return -EOPNOTSUPP; - - if (protocol != 0 && protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) - return -EOPNOTSUPP; - - h = iptc_init("nat"); - if (!h) - return -errno; - - sz = XT_ALIGN(sizeof(struct ipt_entry)) + - XT_ALIGN(sizeof(struct ipt_entry_target)) + - XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); - - /* Put together the entry we want to add or remove */ - entry = alloca0(sz); - entry->next_offset = sz; - entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry)); - r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen); - if (r < 0) - return r; - - /* Fill in target part */ - t = ipt_get_target(entry); - t->u.target_size = - XT_ALIGN(sizeof(struct ipt_entry_target)) + - XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); - strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name)); - mr = (struct nf_nat_ipv4_multi_range_compat*) t->data; - mr->rangesize = 1; - - /* Create a search mask entry */ - mask = alloca(sz); - memset(mask, 0xFF, sz); - - if (add) { - if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h)) - return 0; - if (errno != ENOENT) /* if other error than not existing yet, fail */ - return -errno; - - if (!iptc_insert_entry("POSTROUTING", entry, 0, h)) - return -errno; - } else { - if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) { - if (errno == ENOENT) /* if it's already gone, all is good! */ - return 0; - - return -errno; - } - } - - if (!iptc_commit(h)) - return -errno; - - return 0; -} - -int fw_add_local_dnat( - bool add, - int af, - int protocol, - const char *in_interface, - const union in_addr_union *source, - unsigned source_prefixlen, - const union in_addr_union *destination, - unsigned destination_prefixlen, - uint16_t local_port, - const union in_addr_union *remote, - uint16_t remote_port, - const union in_addr_union *previous_remote) { - - - _cleanup_(iptc_freep) struct xtc_handle *h = NULL; - struct ipt_entry *entry, *mask; - struct ipt_entry_target *t; - struct ipt_entry_match *m; - struct xt_addrtype_info_v1 *at; - struct nf_nat_ipv4_multi_range_compat *mr; - size_t sz, msz; - int r; - - assert(add || !previous_remote); - - if (af != AF_INET) - return -EOPNOTSUPP; - - if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) - return -EOPNOTSUPP; - - if (local_port <= 0) - return -EINVAL; - - if (remote_port <= 0) - return -EINVAL; - - h = iptc_init("nat"); - if (!h) - return -errno; - - sz = XT_ALIGN(sizeof(struct ipt_entry)) + - XT_ALIGN(sizeof(struct ipt_entry_match)) + - XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) + - XT_ALIGN(sizeof(struct ipt_entry_target)) + - XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); - - if (protocol == IPPROTO_TCP) - msz = XT_ALIGN(sizeof(struct ipt_entry_match)) + - XT_ALIGN(sizeof(struct xt_tcp)); - else - msz = XT_ALIGN(sizeof(struct ipt_entry_match)) + - XT_ALIGN(sizeof(struct xt_udp)); - - sz += msz; - - /* Fill in basic part */ - entry = alloca0(sz); - entry->next_offset = sz; - entry->target_offset = - XT_ALIGN(sizeof(struct ipt_entry)) + - XT_ALIGN(sizeof(struct ipt_entry_match)) + - XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) + - msz; - r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen); - if (r < 0) - return r; - - /* Fill in first match */ - m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry))); - m->u.match_size = msz; - if (protocol == IPPROTO_TCP) { - struct xt_tcp *tcp; - - strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name)); - tcp = (struct xt_tcp*) m->data; - tcp->dpts[0] = tcp->dpts[1] = local_port; - tcp->spts[0] = 0; - tcp->spts[1] = 0xFFFF; - - } else { - struct xt_udp *udp; - - strncpy(m->u.user.name, "udp", sizeof(m->u.user.name)); - udp = (struct xt_udp*) m->data; - udp->dpts[0] = udp->dpts[1] = local_port; - udp->spts[0] = 0; - udp->spts[1] = 0xFFFF; - } - - /* Fill in second match */ - m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz); - m->u.match_size = - XT_ALIGN(sizeof(struct ipt_entry_match)) + - XT_ALIGN(sizeof(struct xt_addrtype_info_v1)); - strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name)); - m->u.user.revision = 1; - at = (struct xt_addrtype_info_v1*) m->data; - at->dest = XT_ADDRTYPE_LOCAL; - - /* Fill in target part */ - t = ipt_get_target(entry); - t->u.target_size = - XT_ALIGN(sizeof(struct ipt_entry_target)) + - XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)); - strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name)); - mr = (struct nf_nat_ipv4_multi_range_compat*) t->data; - mr->rangesize = 1; - mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS; - mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr; - if (protocol == IPPROTO_TCP) - mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port); - else - mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port); - - mask = alloca0(sz); - memset(mask, 0xFF, sz); - - if (add) { - /* Add the PREROUTING rule, if it is missing so far */ - if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -EINVAL; - - if (!iptc_insert_entry("PREROUTING", entry, 0, h)) - return -errno; - } - - /* If a previous remote is set, remove its entry */ - if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { - mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; - - if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -errno; - } - - mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr; - } - - /* Add the OUTPUT rule, if it is missing so far */ - if (!in_interface) { - - /* Don't apply onto loopback addresses */ - if (!destination) { - entry->ip.dst.s_addr = htobe32(0x7F000000); - entry->ip.dmsk.s_addr = htobe32(0xFF000000); - entry->ip.invflags = IPT_INV_DSTIP; - } - - if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -errno; - - if (!iptc_insert_entry("OUTPUT", entry, 0, h)) - return -errno; - } - - /* If a previous remote is set, remove its entry */ - if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) { - mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr; - - if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -errno; - } - } - } - } else { - if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -errno; - } - - if (!in_interface) { - if (!destination) { - entry->ip.dst.s_addr = htobe32(0x7F000000); - entry->ip.dmsk.s_addr = htobe32(0xFF000000); - entry->ip.invflags = IPT_INV_DSTIP; - } - - if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) { - if (errno != ENOENT) - return -errno; - } - } - } - - if (!iptc_commit(h)) - return -errno; - - return 0; -} diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h deleted file mode 100644 index c39b34cf8f..0000000000 --- a/src/shared/firewall-util.h +++ /dev/null @@ -1,83 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "in-addr-util.h" - -#ifdef HAVE_LIBIPTC - -int fw_add_masquerade( - bool add, - int af, - int protocol, - const union in_addr_union *source, - unsigned source_prefixlen, - const char *out_interface, - const union in_addr_union *destination, - unsigned destination_prefixlen); - -int fw_add_local_dnat( - bool add, - int af, - int protocol, - const char *in_interface, - const union in_addr_union *source, - unsigned source_prefixlen, - const union in_addr_union *destination, - unsigned destination_prefixlen, - uint16_t local_port, - const union in_addr_union *remote, - uint16_t remote_port, - const union in_addr_union *previous_remote); - -#else - -static inline int fw_add_masquerade( - bool add, - int af, - int protocol, - const union in_addr_union *source, - unsigned source_prefixlen, - const char *out_interface, - const union in_addr_union *destination, - unsigned destination_prefixlen) { - return -EOPNOTSUPP; -} - -static inline int fw_add_local_dnat( - bool add, - int af, - int protocol, - const char *in_interface, - const union in_addr_union *source, - unsigned source_prefixlen, - const union in_addr_union *destination, - unsigned destination_prefixlen, - uint16_t local_port, - const union in_addr_union *remote, - uint16_t remote_port, - const union in_addr_union *previous_remote) { - return -EOPNOTSUPP; -} - -#endif diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c deleted file mode 100644 index a4e0cd3267..0000000000 --- a/src/shared/fstab-util.c +++ /dev/null @@ -1,263 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include - -#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; - _cleanup_strv_free_ char **stor = NULL; - _cleanup_free_ char *v = NULL, **strv = NULL; - - assert(names && *names); - - if (!opts) - goto answer; - - /* If !value and !filtered, this function is not allowed to fail. */ - - if (!filtered) { - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, opts, ",", state) - NULSTR_FOREACH(name, names) { - if (l < strlen(name)) - continue; - if (!strneq(word, name, strlen(name))) - continue; - - /* we know that the string is NUL - * terminated, so *x is valid */ - x = word + strlen(name); - if (IN_SET(*x, '\0', '=', ',')) { - n = name; - if (value) { - free(v); - if (IN_SET(*x, '\0', ',')) - v = NULL; - else { - assert(*x == '='); - x++; - v = strndup(x, l - strlen(name) - 1); - if (!v) - return -ENOMEM; - } - } - } - } - } else { - char **t, **s; - - stor = strv_split(opts, ","); - if (!stor) - return -ENOMEM; - strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1)); - if (!strv) - return -ENOMEM; - - for (s = t = strv; *s; s++) { - NULSTR_FOREACH(name, names) { - x = startswith(*s, name); - if (x && IN_SET(*x, '\0', '=')) - goto found; - } - - *t = *s; - t++; - continue; - found: - /* Keep the last occurence found */ - n = name; - if (value) { - free(v); - if (*x == '\0') - v = NULL; - else { - assert(*x == '='); - x++; - v = strdup(x); - if (!v) - return -ENOMEM; - } - } - } - *t = NULL; - } - -answer: - if (namefound) - *namefound = n; - if (filtered) { - char *f; - - f = strv_join(strv, ","); - if (!f) - return -ENOMEM; - - *filtered = f; - } - if (value) { - *value = v; - v = NULL; - } - - return !!n; -} - -int fstab_extract_values(const char *opts, const char *name, char ***values) { - _cleanup_strv_free_ char **optsv = NULL, **res = NULL; - char **s; - - assert(opts); - assert(name); - assert(values); - - optsv = strv_split(opts, ","); - if (!optsv) - return -ENOMEM; - - STRV_FOREACH(s, optsv) { - char *arg; - int r; - - arg = startswith(*s, name); - if (!arg || *arg != '=') - continue; - r = strv_extend(&res, arg + 1); - if (r < 0) - return r; - } - - *values = res; - res = NULL; - - return !!*values; -} - -int fstab_find_pri(const char *options, int *ret) { - _cleanup_free_ char *opt = NULL; - int r; - unsigned pri; - - assert(ret); - - r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL); - if (r < 0) - return r; - if (r == 0 || !opt) - return 0; - - r = safe_atou(opt, &pri); - if (r < 0) - return r; - - if ((int) pri < 0) - return -ERANGE; - - *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 deleted file mode 100644 index 679f6902f7..0000000000 --- a/src/shared/fstab-util.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "macro.h" - -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); - -static inline bool fstab_test_option(const char *opts, const char *names) { - return !!fstab_filter_options(opts, names, NULL, NULL, NULL); -} - -int fstab_find_pri(const char *options, int *ret); - -static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no) { - int r; - const char *opt; - - /* If first name given is last, return 1. - * If second name given is last or neither is found, return 0. */ - - r = fstab_filter_options(opts, yes_no, &opt, NULL, NULL); - assert(r >= 0); - - return opt == yes_no; -} - -char *fstab_node_to_udev_node(const char *p); diff --git a/src/shared/gcrypt-util.c b/src/shared/gcrypt-util.c deleted file mode 100644 index 39b544b6f0..0000000000 --- a/src/shared/gcrypt-util.c +++ /dev/null @@ -1,71 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#ifdef HAVE_GCRYPT -#include - -#include "gcrypt-util.h" -#include "hexdecoct.h" - -void initialize_libgcrypt(bool secmem) { - const char *p; - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - /* Turn off "secmem". Clients which wish to make use of this - * feature should initialize the library manually */ - if (!secmem) - gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - -int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { - gcry_md_hd_t md = NULL; - size_t hash_size; - void *hash; - char *enc; - - initialize_libgcrypt(false); - - hash_size = gcry_md_get_algo_dlen(md_algorithm); - assert(hash_size > 0); - - gcry_md_open(&md, md_algorithm, 0); - if (!md) - return -EIO; - - gcry_md_write(md, s, len); - - hash = gcry_md_read(md, 0); - if (!hash) - return -EIO; - - enc = hexmem(hash, hash_size); - if (!enc) - return -ENOMEM; - - *out = enc; - return 0; -} -#endif diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h deleted file mode 100644 index cf33b3c59c..0000000000 --- a/src/shared/gcrypt-util.h +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2016 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_GCRYPT -#include - -void initialize_libgcrypt(bool secmem); -int string_hashsum(const char *s, size_t len, int md_algorithm, char **out); -#endif - -static inline int string_hashsum_sha224(const char *s, size_t len, char **out) { -#ifdef HAVE_GCRYPT - return string_hashsum(s, len, GCRY_MD_SHA224, out); -#else - return -EOPNOTSUPP; -#endif -} diff --git a/src/shared/generator.c b/src/shared/generator.c deleted file mode 100644 index 70afc6a285..0000000000 --- a/src/shared/generator.c +++ /dev/null @@ -1,207 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "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 "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) { - _cleanup_free_ char *device = NULL, *escaped = NULL; - _cleanup_fclose_ FILE *f = NULL; - const char *unit; - int r; - - escaped = cescape(what); - if (!escaped) - return log_oom(); - - unit = strjoina(dir, "/systemd-fsck-root.service"); - log_debug("Creating %s", unit); - - r = unit_name_from_path(what, ".device", &device); - if (r < 0) - return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); - - fprintf(f, - "# Automatically generated by %1$s\n\n" - "[Unit]\n" - "Documentation=man:systemd-fsck-root.service(8)\n" - "Description=File System Check on %2$s\n" - "DefaultDependencies=no\n" - "BindsTo=%3$s\n" - "After=initrd-root-device.target local-fs-pre.target\n" - "Before=shutdown.target\n" - "\n" - "[Service]\n" - "Type=oneshot\n" - "RemainAfterExit=yes\n" - "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n" - "TimeoutSec=0\n", - program_invocation_short_name, - what, - device, - escaped); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); - - return 0; -} - -int generator_write_fsck_deps( - FILE *f, - const char *dir, - const char *what, - const char *where, - const char *fstype) { - - int r; - - assert(f); - assert(dir); - assert(what); - assert(where); - - if (!is_device_path(what)) { - log_warning("Checking was requested for \"%s\", but it is not a device.", what); - return 0; - } - - if (!isempty(fstype) && !streq(fstype, "auto")) { - r = fsck_exists(fstype); - 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("Checking was requested for %s, but fsck.%s does not exist.", what, fstype); - return 0; - } - } - - if (path_equal(where, "/")) { - const char *lnk; - - lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); - - mkdir_parents(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); - - } else { - _cleanup_free_ char *_fsck = NULL; - const char *fsck; - - if (in_initrd() && path_equal(where, "/sysroot")) { - r = write_fsck_sysroot_service(dir, what); - if (r < 0) - return r; - - fsck = "systemd-fsck-root.service"; - } else { - r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck); - if (r < 0) - return log_error_errno(r, "Failed to create fsck service name: %m"); - - fsck = _fsck; - } - - fprintf(f, - "Requires=%1$s\n" - "After=%1$s\n", - fsck); - } - - return 0; -} - -int generator_write_timeouts( - const char *dir, - const char *what, - const char *where, - const char *opts, - char **filtered) { - - /* Allow configuration how long we wait for a device that - * backs a mount point to show up. This is useful to support - * endless device timeouts for devices that show up only after - * user input, like crypto devices. */ - - _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL; - usec_t u; - int r; - - r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" "x-systemd.device-timeout\0", - NULL, &timeout, filtered); - if (r <= 0) - return r; - - r = parse_sec(timeout, &u); - if (r < 0) { - log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); - return 0; - } - - node = fstab_node_to_udev_node(what); - if (!node) - return log_oom(); - - r = unit_name_from_path(node, ".device", &unit); - if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); - - return write_drop_in_format(dir, unit, 50, "device-timeout", - "# Automatically generated by %s\n\n" - "[Unit]\nJobTimeoutSec=%s", - program_invocation_short_name, timeout); -} - -int generator_write_initrd_root_device_deps(const char *dir, const char *what) { - _cleanup_free_ char *unit = NULL; - int r; - - r = unit_name_from_path(what, ".device", &unit); - if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); - - return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", - "# Automatically generated by %s\n\n" - "[Unit]\nRequires=%s\nAfter=%s", - program_invocation_short_name, unit, unit); -} diff --git a/src/shared/generator.h b/src/shared/generator.h deleted file mode 100644 index a6017c1b76..0000000000 --- a/src/shared/generator.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -int generator_write_fsck_deps( - FILE *f, - const char *dir, - const char *what, - const char *where, - const char *type); - -int generator_write_timeouts( - const char *dir, - const char *what, - const char *where, - const char *opts, - char **filtered); - -int generator_write_initrd_root_device_deps( - const char *dir, - const char *what); diff --git a/src/shared/gpt.h b/src/shared/gpt.h deleted file mode 100644 index 55b41bbcd8..0000000000 --- a/src/shared/gpt.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-id128.h" - -/* 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. */ - -#define GPT_ROOT_X86 SD_ID128_MAKE(44,47,95,40,f2,97,41,b2,9a,f7,d1,31,d5,f0,45,8a) -#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) -#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) -#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) - -#if defined(__x86_64__) -# define GPT_ROOT_NATIVE GPT_ROOT_X86_64 -# define GPT_ROOT_SECONDARY GPT_ROOT_X86 -#elif defined(__i386__) -# 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 -#elif defined(__arm__) && (__BYTE_ORDER != __BIG_ENDIAN) -# define GPT_ROOT_NATIVE GPT_ROOT_ARM -#endif - -/* Flags we recognize on the root, swap, home and srv partitions when - * doing auto-discovery. These happen to be identical to what - * Microsoft defines for its own Basic Data Partitions, but that's - * just because we saw no point in defining any other values here. */ -#define GPT_FLAG_READ_ONLY (1ULL << 60) -#define GPT_FLAG_NO_AUTO (1ULL << 63) - -#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) diff --git a/src/shared/ima-util.c b/src/shared/ima-util.c deleted file mode 100644 index 789064d653..0000000000 --- a/src/shared/ima-util.c +++ /dev/null @@ -1,32 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "ima-util.h" - -static int use_ima_cached = -1; - -bool use_ima(void) { - - if (use_ima_cached < 0) - use_ima_cached = access("/sys/kernel/security/ima/", F_OK) >= 0; - - return use_ima_cached; -} diff --git a/src/shared/ima-util.h b/src/shared/ima-util.h deleted file mode 100644 index 5be94761fd..0000000000 --- a/src/shared/ima-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -bool use_ima(void); diff --git a/src/shared/import-util.c b/src/shared/import-util.c deleted file mode 100644 index ab701ad8b2..0000000000 --- a/src/shared/import-util.c +++ /dev/null @@ -1,185 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#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; - char *s; - - e = strchrnul(url, '?'); - - while (e > url && e[-1] == '/') - e--; - - p = e; - while (p > url && p[-1] != '/') - p--; - - if (e <= p) - return -EINVAL; - - s = strndup(p, e - p); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - - -int import_url_change_last_component(const char *url, const char *suffix, char **ret) { - const char *e; - char *s; - - assert(url); - assert(ret); - - e = strchrnul(url, '?'); - - while (e > url && e[-1] == '/') - e--; - - while (e > url && e[-1] != '/') - e--; - - if (e <= url) - return -EINVAL; - - s = new(char, (e - url) + strlen(suffix) + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(s, url, e - url), suffix); - *ret = s; - return 0; -} - -static const char* const import_verify_table[_IMPORT_VERIFY_MAX] = { - [IMPORT_VERIFY_NO] = "no", - [IMPORT_VERIFY_CHECKSUM] = "checksum", - [IMPORT_VERIFY_SIGNATURE] = "signature", -}; - -DEFINE_STRING_TABLE_LOOKUP(import_verify, ImportVerify); - -int tar_strip_suffixes(const char *name, char **ret) { - const char *e; - char *s; - - e = endswith(name, ".tar"); - if (!e) - e = endswith(name, ".tar.xz"); - if (!e) - e = endswith(name, ".tar.gz"); - if (!e) - e = endswith(name, ".tar.bz2"); - if (!e) - e = endswith(name, ".tgz"); - if (!e) - e = strchr(name, 0); - - if (e <= name) - return -EINVAL; - - s = strndup(name, e - name); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int raw_strip_suffixes(const char *p, char **ret) { - - static const char suffixes[] = - ".xz\0" - ".gz\0" - ".bz2\0" - ".raw\0" - ".qcow2\0" - ".img\0" - ".bin\0"; - - _cleanup_free_ char *q = NULL; - - q = strdup(p); - if (!q) - return -ENOMEM; - - for (;;) { - const char *sfx; - bool changed = false; - - NULSTR_FOREACH(sfx, suffixes) { - char *e; - - e = endswith(q, sfx); - if (e) { - *e = 0; - changed = true; - } - } - - if (!changed) - break; - } - - *ret = q; - q = NULL; - - return 0; -} - -int import_assign_pool_quota_and_warn(const char *path) { - int r; - - 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 0; -} diff --git a/src/shared/import-util.h b/src/shared/import-util.h deleted file mode 100644 index 77b17d91f3..0000000000 --- a/src/shared/import-util.h +++ /dev/null @@ -1,43 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -typedef enum ImportVerify { - IMPORT_VERIFY_NO, - IMPORT_VERIFY_CHECKSUM, - IMPORT_VERIFY_SIGNATURE, - _IMPORT_VERIFY_MAX, - _IMPORT_VERIFY_INVALID = -1, -} ImportVerify; - -int import_url_last_component(const char *url, char **ret); -int import_url_change_last_component(const char *url, const char *suffix, char **ret); - -const char* import_verify_to_string(ImportVerify v) _const_; -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); - -int import_assign_pool_quota_and_warn(const char *path); diff --git a/src/shared/initreq.h b/src/shared/initreq.h deleted file mode 100644 index 710037d84b..0000000000 --- a/src/shared/initreq.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * initreq.h Interface to talk to init through /dev/initctl. - * - * Copyright (C) 1995-2004 Miquel van Smoorenburg - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS - * - */ -#ifndef _INITREQ_H -#define _INITREQ_H - -#include - -#if defined(__FreeBSD_kernel__) -# define INIT_FIFO "/etc/.initctl" -#else -# define INIT_FIFO "/dev/initctl" -#endif - -#define INIT_MAGIC 0x03091969 -#define INIT_CMD_START 0 -#define INIT_CMD_RUNLVL 1 -#define INIT_CMD_POWERFAIL 2 -#define INIT_CMD_POWERFAILNOW 3 -#define INIT_CMD_POWEROK 4 -#define INIT_CMD_BSD 5 -#define INIT_CMD_SETENV 6 -#define INIT_CMD_UNSETENV 7 - -#define INIT_CMD_CHANGECONS 12345 - -#ifdef MAXHOSTNAMELEN -# define INITRQ_HLEN MAXHOSTNAMELEN -#else -# define INITRQ_HLEN 64 -#endif - -/* - * This is what BSD 4.4 uses when talking to init. - * Linux doesn't use this right now. - */ -struct init_request_bsd { - char gen_id[8]; /* Beats me.. telnetd uses "fe" */ - char tty_id[16]; /* Tty name minus /dev/tty */ - char host[INITRQ_HLEN]; /* Hostname */ - char term_type[16]; /* Terminal type */ - int signal; /* Signal to send */ - int pid; /* Process to send to */ - char exec_name[128]; /* Program to execute */ - char reserved[128]; /* For future expansion. */ -}; - - -/* - * Because of legacy interfaces, "runlevel" and "sleeptime" - * aren't in a separate struct in the union. - * - * The weird sizes are because init expects the whole - * struct to be 384 bytes. - */ -struct init_request { - int magic; /* Magic number */ - int cmd; /* What kind of request */ - int runlevel; /* Runlevel to change to */ - int sleeptime; /* Time between TERM and KILL */ - union { - struct init_request_bsd bsd; - char data[368]; - } i; -}; - -#endif diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c deleted file mode 100644 index 88143361da..0000000000 --- a/src/shared/install-printf.c +++ /dev/null @@ -1,133 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "formats-util.h" -#include "install-printf.h" -#include "install.h" -#include "macro.h" -#include "specifier.h" -#include "unit-name.h" -#include "user-util.h" - -static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - - assert(i); - - return unit_name_to_prefix_and_instance(i->name, ret); -} - -static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - - assert(i); - - return unit_name_to_prefix(i->name, ret); -} - -static int specifier_instance(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - char *instance; - int r; - - assert(i); - - r = unit_name_to_instance(i->name, &instance); - if (r < 0) - return r; - - if (!instance) { - instance = strdup(""); - if (!instance) - return -ENOMEM; - } - - *ret = instance; - return 0; -} - -static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *t; - - /* 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 = 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) { - - /* This is similar to unit_full_printf() but does not support - * anything path-related. - * - * %n: the full id of the unit (foo@bar.waldo) - * %N: the id of the unit without the suffix (foo@bar) - * %p: the prefix (foo) - * %i: the instance (bar) - - * %U the UID of the 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 - * %v `uname -r` of the running system - */ - - const Specifier table[] = { - { 'n', specifier_string, i->name }, - { 'N', specifier_prefix_and_instance, NULL }, - { 'p', specifier_prefix, NULL }, - { 'i', specifier_instance, NULL }, - - { 'U', specifier_user_id, NULL }, - { 'u', specifier_user_name, NULL }, - - { 'm', specifier_machine_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} - }; - - assert(i); - assert(format); - assert(ret); - - return specifier_printf(format, table, i, ret); -} diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h deleted file mode 100644 index 8a570fc265..0000000000 --- a/src/shared/install-printf.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "install.h" - -int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret); diff --git a/src/shared/install.c b/src/shared/install.c deleted file mode 100644 index 7b49e1ece9..0000000000 --- a/src/shared/install.c +++ /dev/null @@ -1,2957 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "install-printf.h" -#include "install.h" -#include "locale-util.h" -#include "log.h" -#include "macro.h" -#include "mkdir.h" -#include "path-lookup.h" -#include "path-util.h" -#include "rm-rf.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" - -#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 - -typedef enum SearchFlags { - SEARCH_LOAD = 1, - SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, -} SearchFlags; - -typedef struct { - OrderedHashmap *will_process; - OrderedHashmap *have_processed; -} InstallContext; - -typedef enum { - PRESET_UNKNOWN, - PRESET_ENABLE, - PRESET_DISABLE, -} PresetAction; - -typedef struct { - char *pattern; - PresetAction action; -} PresetRule; - -typedef struct { - PresetRule *rules; - size_t n_rules; -} Presets; - -static inline void presets_freep(Presets *p) { - size_t i; - - if (!p) - return; - - for (i = 0; i < p->n_rules; i++) - free(p->rules[i].pattern); - - free(p->rules); - p->n_rules = 0; -} - -static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); - -bool unit_type_may_alias(UnitType type) { - return IN_SET(type, - UNIT_SERVICE, - UNIT_SOCKET, - UNIT_TARGET, - UNIT_DEVICE, - UNIT_TIMER, - UNIT_PATH); -} - -bool unit_type_may_template(UnitType type) { - return IN_SET(type, - UNIT_SERVICE, - UNIT_SOCKET, - UNIT_TARGET, - UNIT_TIMER, - UNIT_PATH); -} - -static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { - [UNIT_FILE_TYPE_REGULAR] = "regular", - [UNIT_FILE_TYPE_SYMLINK] = "symlink", - [UNIT_FILE_TYPE_MASKED] = "masked", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); - -static int in_search_path(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - char **i; - - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - STRV_FOREACH(i, p->search_path) - if (path_equal(parent, *i)) - return true; - - return false; -} - -static const char* skip_root(const LookupPaths *p, const char *path) { - char *e; - - assert(p); - assert(path); - - if (!p->root_dir) - return path; - - e = path_startswith(path, p->root_dir); - if (!e) - return NULL; - - /* Make sure the returned path starts with a slash */ - if (e[0] != '/') { - if (e == path || e[-1] != '/') - return NULL; - - e--; - } - - return e; -} - -static int path_is_generator(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late); -} - -static int path_is_transient(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->transient); -} - -static int path_is_control(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->persistent_control) || - path_equal_ptr(parent, p->runtime_control); -} - -static int path_is_config(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern - * configuration from transient or generated units */ - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->persistent_config) || - path_equal_ptr(parent, p->runtime_config); -} - -static int path_is_runtime(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - const char *rpath; - - assert(p); - assert(path); - - /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime - * directories, as safety net. */ - - rpath = skip_root(p, path); - if (rpath && path_startswith(rpath, "/run")) - return true; - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->runtime_config) || - path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late) || - path_equal_ptr(parent, p->transient) || - path_equal_ptr(parent, p->runtime_control); -} - -static int path_is_vendor(const LookupPaths *p, const char *path) { - const char *rpath; - - assert(p); - assert(path); - - rpath = skip_root(p, path); - if (!rpath) - return 0; - - if (path_startswith(rpath, "/usr")) - return true; - -#ifdef HAVE_SPLIT_USR - if (path_startswith(rpath, "/lib")) - return true; -#endif - - return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); -} - -int unit_file_changes_add( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - _cleanup_free_ char *p = NULL, *s = NULL; - UnitFileChange *c; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - *changes = c; - - p = strdup(path); - if (source) - s = strdup(source); - - if (!p || (source && !s)) - return -ENOMEM; - - path_kill_slashes(p); - if (s) - path_kill_slashes(s); - - c[*n_changes] = (UnitFileChange) { type, p, s }; - p = s = NULL; - (*n_changes) ++; - return 0; -} - -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); - - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) { - unsigned i; - bool logged = false; - - assert(changes || n_changes == 0); - /* If verb is not specified, errors are not allowed! */ - assert(verb || r >= 0); - - for (i = 0; i < n_changes; i++) { - assert(verb || changes[i].type >= 0); - - switch(changes[i].type) { - case UNIT_FILE_SYMLINK: - if (!quiet) - log_info("Created symlink %s %s %s.", - changes[i].path, - special_glyph(ARROW), - changes[i].source); - break; - case UNIT_FILE_UNLINK: - if (!quiet) - log_info("Removed %s.", changes[i].path); - break; - case UNIT_FILE_IS_MASKED: - if (!quiet) - log_info("Unit %s is masked, ignoring.", changes[i].path); - break; - case UNIT_FILE_IS_DANGLING: - if (!quiet) - log_info("Unit %s is an alias to a unit that is not present, ignoring.", - changes[i].path); - break; - case -EEXIST: - if (changes[i].source) - log_error_errno(changes[i].type, - "Failed to %s unit, file %s already exists and is a symlink to %s.", - verb, changes[i].path, changes[i].source); - else - log_error_errno(changes[i].type, - "Failed to %s unit, file %s already exists.", - verb, changes[i].path); - logged = true; - break; - case -ERFKILL: - log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.", - verb, changes[i].path); - logged = true; - break; - case -EADDRNOTAVAIL: - log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.", - verb, changes[i].path); - logged = true; - break; - case -ELOOP: - log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s", - verb, changes[i].path); - logged = true; - break; - default: - assert(changes[i].type < 0); - log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", - verb, changes[i].path); - logged = true; - } - } - - if (r < 0 && !logged) - log_error_errno(r, "Failed to %s: %m.", verb); -} - -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. - * - * Returns 1 if a symlink was created or already exists and points to - * the right place, or negative on error. - */ - - 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 1; - } - - if (errno != EEXIST) { - unit_file_changes_add(changes, n_changes, -errno, new_path, NULL); - return -errno; - } - - r = readlink_malloc(new_path, &dest); - if (r < 0) { - /* translate EINVAL (non-symlink exists) to EEXIST */ - if (r == -EINVAL) - r = -EEXIST; - - unit_file_changes_add(changes, n_changes, r, new_path, NULL); - return r; - } - - if (path_equal(dest, old_path)) - return 1; - - if (!force) { - unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); - return -EEXIST; - } - - r = symlink_atomic(old_path, new_path); - if (r < 0) { - unit_file_changes_add(changes, n_changes, r, new_path, NULL); - 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 1; -} - -static int mark_symlink_for_removal( - Set **remove_symlinks_to, - const char *p) { - - char *n; - int r; - - assert(p); - - r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops); - if (r < 0) - return r; - - n = strdup(p); - if (!n) - return -ENOMEM; - - path_kill_slashes(n); - - r = set_consume(*remove_symlinks_to, n); - if (r == -EEXIST) - return 0; - if (r < 0) - return r; - - return 1; -} - -static int remove_marked_symlinks_fd( - Set *remove_symlinks_to, - int fd, - const char *path, - const char *config_path, - const LookupPaths *lp, - bool *restart, - UnitFileChange **changes, - 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(lp); - assert(restart); - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return -errno; - } - - rewinddir(d); - - FOREACH_DIRENT(de, d, return -errno) { - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - _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) { - if (errno == ENOENT) - continue; - - if (r == 0) - r = -errno; - continue; - } - - p = path_make_absolute(de->d_name, path); - if (!p) { - safe_close(nfd); - return -ENOMEM; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, 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; - const char *rp; - bool found; - int q; - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - p = path_make_absolute(de->d_name, path); - if (!p) - return -ENOMEM; - path_kill_slashes(p); - - q = readlink_malloc(p, &dest); - if (q == -ENOENT) - continue; - if (q < 0) { - if (r == 0) - r = q; - 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_contains(remove_symlinks_to, dest) || - set_contains(remove_symlinks_to, basename(dest)) || - set_contains(remove_symlinks_to, de->d_name); - - if (!found) - continue; - - if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { - if (r == 0) - r = -errno; - unit_file_changes_add(changes, n_changes, -errno, p, NULL); - continue; - } - - (void) rmdir_parents(p, config_path); - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - - /* Now, remember the full path (but with the root prefix removed) of - * the symlink we just removed, and remove any symlinks to it, too. */ - - rp = skip_root(lp, p); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); - if (q < 0) - return q; - if (q > 0) - *restart = true; - } - } - - return r; -} - -static int remove_marked_symlinks( - Set *remove_symlinks_to, - const char *config_path, - const LookupPaths *lp, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_close_ int fd = -1; - bool restart; - int r = 0; - - assert(config_path); - assert(lp); - - if (set_size(remove_symlinks_to) <= 0) - return 0; - - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) - return -errno; - - do { - int q, cfd; - restart = false; - - cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - 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, lp, &restart, changes, n_changes); - if (r == 0) - r = q; - } 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, - const LookupPaths *lp, - bool *same_name_link) { - - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(name); - assert(fd >= 0); - assert(path); - assert(config_path); - assert(lp); - assert(same_name_link); - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - _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) { - if (errno == ENOENT) - continue; - - if (r == 0) - r = -errno; - continue; - } - - p = path_make_absolute(de->d_name, path); - if (!p) { - safe_close(nfd); - return -ENOMEM; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); - if (q > 0) - return 1; - if (r == 0) - r = q; - - } else if (de->d_type == DT_LNK) { - _cleanup_free_ char *p = NULL, *dest = NULL; - bool found_path, found_dest, b = false; - int q; - - /* Acquire symlink name */ - p = path_make_absolute(de->d_name, path); - if (!p) - return -ENOMEM; - - /* Acquire symlink destination */ - q = readlink_malloc(p, &dest); - if (q == -ENOENT) - continue; - if (q < 0) { - 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)) - found_path = path_equal(p, name); - else - found_path = streq(de->d_name, name); - - /* Check if what the symlink points to - * matches what we are looking for */ - if (path_is_absolute(name)) - found_dest = path_equal(dest, name); - else - found_dest = streq(basename(dest), name); - - if (found_path && found_dest) { - _cleanup_free_ char *t = NULL; - - /* Filter out same name links in the main - * config path */ - t = path_make_absolute(name, config_path); - if (!t) - return -ENOMEM; - - b = path_equal(t, p); - } - - if (b) - *same_name_link = true; - else if (found_path || found_dest) - return 1; - } - } - - return r; -} - -static int find_symlinks( - const char *root_dir, - const char *name, - const char *config_path, - const LookupPaths *lp, - bool *same_name_link) { - - int fd; - - assert(name); - assert(config_path); - assert(same_name_link); - - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR, EACCES)) - return 0; - return -errno; - } - - /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); -} - -static int find_symlinks_in_scope( - UnitFileScope scope, - const LookupPaths *paths, - const char *name, - UnitFileState *state) { - - bool same_name_link_runtime = false, same_name_link = false; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(paths); - assert(name); - - /* First look in the persistent config path */ - r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); - if (r < 0) - return r; - if (r > 0) { - *state = UNIT_FILE_ENABLED; - return r; - } - - /* Then look in runtime config path */ - r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); - if (r < 0) - return r; - 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) { - *state = UNIT_FILE_LINKED; - return 1; - } - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } - - return 0; -} - -static void install_info_free(UnitFileInstallInfo *i) { - - if (!i) - return; - - free(i->name); - free(i->path); - strv_free(i->aliases); - strv_free(i->wanted_by); - strv_free(i->required_by); - strv_free(i->also); - free(i->default_instance); - free(i->symlink_target); - free(i); -} - -static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { - UnitFileInstallInfo *i; - - if (!m) - return NULL; - - while ((i = ordered_hashmap_steal_first(m))) - install_info_free(i); - - return ordered_hashmap_free(m); -} - -static void install_context_done(InstallContext *c) { - assert(c); - - c->will_process = install_info_hashmap_free(c->will_process); - c->have_processed = install_info_hashmap_free(c->have_processed); -} - -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_may_process( - UnitFileInstallInfo *i, - const LookupPaths *paths, - UnitFileChange **changes, - unsigned *n_changes) { - assert(i); - assert(paths); - - /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus - * not subject to enable/disable operations. */ - - if (i->type == UNIT_FILE_TYPE_MASKED) { - unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); - return -ERFKILL; - } - if (path_is_generator(paths, i->path) || - path_is_transient(paths, i->path)) { - unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); - return -EADDRNOTAVAIL; - } - - return 0; -} - -static int install_info_add( - InstallContext *c, - const char *name, - const char *path, - UnitFileInstallInfo **ret) { - - UnitFileInstallInfo *i = NULL; - int r; - - assert(c); - assert(name || path); - - if (!name) - name = basename(path); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - i = install_info_find(c, name); - if (i) { - if (ret) - *ret = i; - return 0; - } - - 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) { - r = -ENOMEM; - goto fail; - } - - if (path) { - i->path = strdup(path); - if (!i->path) { - r = -ENOMEM; - goto fail; - } - } - - r = ordered_hashmap_put(c->will_process, i->name, i); - if (r < 0) - goto fail; - - if (ret) - *ret = i; - - return 0; - -fail: - install_info_free(i); - return r; -} - -static int config_parse_alias( - 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 *name; - UnitType type; - - assert(filename); - assert(lvalue); - assert(rvalue); - - name = basename(filename); - type = unit_name_to_type(name); - if (!unit_type_may_alias(type)) - return log_syntax(unit, LOG_WARNING, filename, line, 0, - "Aliases are not allowed for %s units, ignoring.", - unit_type_to_string(type)); - - return config_parse_strv(unit, filename, line, section, section_line, - lvalue, ltype, rvalue, data, userdata); -} - -static int config_parse_also( - 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 = userdata; - InstallContext *c = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - r = install_info_add(c, word, NULL, NULL); - if (r < 0) - return r; - - r = strv_push(&i->also, word); - if (r < 0) - return r; - - word = NULL; - } - - return 0; -} - -static int config_parse_default_instance( - 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; - const char *name; - char *printed; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - name = basename(filename); - if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) - /* When enabling an instance, we might be using a template unit file, - * but we should ignore DefaultInstance silently. */ - return 0; - if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) - return log_syntax(unit, LOG_WARNING, filename, line, 0, - "DefaultInstance only makes sense for template units, ignoring."); - - r = install_full_printf(i, rvalue, &printed); - if (r < 0) - return r; - - if (!unit_instance_is_valid(printed)) { - free(printed); - return -EINVAL; - } - - free(i->default_instance); - i->default_instance = printed; - - return 0; -} - -static int unit_file_load( - InstallContext *c, - UnitFileInstallInfo *info, - const char *path, - SearchFlags flags) { - - const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_alias, 0, &info->aliases }, - { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, - { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, - { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, - { "Install", "Also", config_parse_also, 0, c }, - {} - }; - - const char *name; - UnitType type; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; - struct stat st; - int r; - - assert(c); - assert(info); - assert(path); - - name = basename(path); - type = unit_name_to_type(name); - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && - !unit_type_may_template(type)) - return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); - - 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|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) - return -errno; - fd = -1; - - r = config_parse(NULL, path, f, - NULL, - config_item_table_lookup, items, - true, true, false, info); - if (r < 0) - return r; - - info->type = UNIT_FILE_TYPE_REGULAR; - - return - (int) strv_length(info->aliases) + - (int) strv_length(info->wanted_by) + - (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 *target = NULL; - int r; - - r = unit_file_load(c, info, path, flags); - if (r != -ELOOP) - return r; - - /* This is a symlink, let's read it. */ - - r = readlink_malloc(path, &target); - if (r < 0) - return r; - - if (path_equal(target, "/dev/null")) - info->type = UNIT_FILE_TYPE_MASKED; - else { - const char *bn; - UnitType a, b; - - bn = basename(target); - - 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; - - if (path_is_absolute(target)) - /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ - info->symlink_target = prefix_root(root_dir, target); - else - /* This is a relative path, take it relative to the dir the symlink is located in. */ - info->symlink_target = file_in_same_dir(path, target); - if (!info->symlink_target) - return -ENOMEM; - - info->type = UNIT_FILE_TYPE_SYMLINK; - } - - return 0; -} - -static int unit_file_search( - InstallContext *c, - UnitFileInstallInfo *info, - const LookupPaths *paths, - SearchFlags flags) { - - _cleanup_free_ char *template = NULL; - char **p; - int r; - - assert(c); - 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_or_readlink(c, info, info->path, paths->root_dir, flags); - - assert(info->name); - - STRV_FOREACH(p, paths->search_path) { - _cleanup_free_ char *path = NULL; - - path = strjoin(*p, "/", info->name, NULL); - if (!path) - return -ENOMEM; - - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r >= 0) { - info->path = path; - path = NULL; - return r; - } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) - return r; - } - - if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - /* Unit file doesn't exist, however instance - * enablement was requested. We will check if it is - * possible to load template unit file. */ - - r = unit_name_template(info->name, &template); - if (r < 0) - return r; - - STRV_FOREACH(p, paths->search_path) { - _cleanup_free_ char *path = NULL; - - path = strjoin(*p, "/", template, NULL); - if (!path) - return -ENOMEM; - - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r >= 0) { - info->path = path; - path = NULL; - return r; - } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) - return r; - } - } - - log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); - return -ENOENT; -} - -static int install_info_follow( - InstallContext *c, - UnitFileInstallInfo *i, - const char *root_dir, - 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); -} - -/** - * Search for the unit file. If the unit name is a symlink, - * follow the symlink to the target, maybe more than once. - * Propagate the instance name if present. - */ -static int install_info_traverse( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - UnitFileInstallInfo *start, - SearchFlags flags, - UnitFileInstallInfo **ret) { - - UnitFileInstallInfo *i; - unsigned k = 0; - int r; - - assert(paths); - assert(start); - assert(c); - - r = unit_file_search(c, start, paths, flags); - if (r < 0) - return r; - - i = start; - while (i->type == UNIT_FILE_TYPE_SYMLINK) { - /* Follow the symlink */ - - if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) - return -ELOOP; - - if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { - r = path_is_config(paths, i->path); - if (r < 0) - return r; - if (r > 0) - return -ELOOP; - } - - r = install_info_follow(c, i, paths->root_dir, flags); - if (r == -EXDEV) { - _cleanup_free_ char *buffer = NULL; - const char *bn; - - /* Target has a different name, create a new - * install info object for that, and continue - * with that. */ - - bn = basename(i->symlink_target); - - if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && - unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { - - _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; - - /* Try again, with the new target we found. */ - r = unit_file_search(c, i, paths, flags); - if (r == -ENOENT) - /* Translate error code to highlight this specific case */ - return -ENOLINK; - } - - if (r < 0) - return r; - } - - if (ret) - *ret = i; - - return 0; -} - -static int install_info_add_auto( - InstallContext *c, - const LookupPaths *paths, - const char *name_or_path, - UnitFileInstallInfo **ret) { - - assert(c); - assert(name_or_path); - - if (path_is_absolute(name_or_path)) { - const char *pp; - - pp = prefix_roota(paths->root_dir, name_or_path); - - return install_info_add(c, NULL, pp, ret); - } else - return install_info_add(c, name_or_path, NULL, ret); -} - -static int install_info_discover( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - const char *name, - SearchFlags flags, - UnitFileInstallInfo **ret) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(name); - - r = install_info_add_auto(c, paths, name, &i); - if (r < 0) - return r; - - return install_info_traverse(scope, c, paths, i, flags, ret); -} - -static int install_info_symlink_alias( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **s; - int r = 0, q; - - assert(i); - assert(paths); - assert(config_path); - - STRV_FOREACH(s, i->aliases) { - _cleanup_free_ char *alias_path = NULL, *dst = NULL; - const char *rp; - - q = install_full_printf(i, *s, &dst); - if (q < 0) - return q; - - alias_path = path_make_absolute(dst, config_path); - if (!alias_path) - return -ENOMEM; - - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_wants( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - char **list, - const char *suffix, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_free_ char *buf = NULL; - const char *n; - char **s; - int r = 0, q; - - assert(i); - assert(paths); - assert(config_path); - - if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { - - /* Don't install any symlink if there's no default - * instance configured */ - - if (!i->default_instance) - return 0; - - r = unit_name_replace_instance(i->name, i->default_instance, &buf); - if (r < 0) - return r; - - n = buf; - } else - n = i->name; - - STRV_FOREACH(s, list) { - _cleanup_free_ char *path = NULL, *dst = NULL; - const char *rp; - - q = install_full_printf(i, *s, &dst); - if (q < 0) - return q; - - if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) { - r = -EINVAL; - continue; - } - - path = strjoin(config_path, "/", dst, suffix, n, NULL); - if (!path) - return -ENOMEM; - - rp = skip_root(paths, i->path); - - q = create_symlink(rp ?: i->path, path, true, changes, n_changes); - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_link( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_free_ char *path = NULL; - const char *rp; - int r; - - assert(i); - assert(paths); - assert(config_path); - assert(i->path); - - r = in_search_path(paths, i->path); - if (r < 0) - return r; - if (r > 0) - return 0; - - path = strjoin(config_path, "/", i->name, NULL); - if (!path) - return -ENOMEM; - - rp = skip_root(paths, i->path); - - return create_symlink(rp ?: i->path, path, force, changes, n_changes); -} - -static int install_info_apply( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - int r, q; - - assert(i); - assert(paths); - assert(config_path); - - if (i->type != UNIT_FILE_TYPE_REGULAR) - return 0; - - r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - - q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); - /* Do not count links to the unit file towards the "carries_install_info" count */ - if (r == 0 && q < 0) - r = q; - - return r; -} - -static int install_context_apply( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - const char *config_path, - bool force, - SearchFlags flags, - UnitFileChange **changes, - unsigned *n_changes) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(config_path); - - 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; - - r = 0; - while ((i = ordered_hashmap_first(c->will_process))) { - int 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, paths, i, flags, NULL); - if (r < 0) - return r; - - if (i->type != UNIT_FILE_TYPE_REGULAR) - continue; - - q = install_info_apply(i, paths, config_path, force, changes, n_changes); - 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, - const char *config_path) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(config_path); - - /* Marks all items for removal */ - - 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 = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); - if (r == -ENOLINK) - return 0; - else if (r < 0) - return r; - - if (i->type != UNIT_FILE_TYPE_REGULAR) { - log_debug("Unit %s has type %s, ignoring.", - i->name, - unit_file_type_to_string(i->type) ?: "invalid"); - continue; - } - - r = mark_symlink_for_removal(remove_symlinks_to, i->name); - if (r < 0) - return r; - } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - 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, config_path); - 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_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - const char *config_path; - char **i; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - 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; - const char *rp; - - path = path_make_absolute(*i, config_path); - if (!path) - return -ENOMEM; - - if (unlink(path) < 0) { - if (errno != ENOENT) { - if (r >= 0) - r = -errno; - unit_file_changes_add(changes, n_changes, -errno, path, NULL); - } - - continue; - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - - rp = skip_root(&paths, path); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); - if (q < 0) - return q; - } - - q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); - if (r >= 0) - r = q; - - 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 = {}; - _cleanup_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - const char *config_path; - char **i; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *full = NULL; - struct stat st; - char *fn; - - if (!path_is_absolute(*i)) - return -EINVAL; - - fn = basename(*i); - if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) - return -EINVAL; - - full = prefix_root(paths.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(&paths, *i); - if (q < 0) - return q; - if (q > 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 *new_path = NULL; - const char *old_path; - - old_path = skip_root(&paths, *i); - new_path = path_make_absolute(basename(*i), config_path); - if (!new_path) - return -ENOMEM; - - q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -static int path_shall_revert(const LookupPaths *paths, const char *path) { - int r; - - assert(paths); - assert(path); - - /* Checks whether the path is one where the drop-in directories shall be removed. */ - - r = path_is_config(paths, path); - if (r != 0) - return r; - - r = path_is_control(paths, path); - if (r != 0) - return r; - - return path_is_transient(paths, path); -} - -int unit_file_revert( - UnitFileScope scope, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - /* _cleanup_(install_context_done) InstallContext c = {}; */ - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_strv_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - char **i; - int r, q; - - /* Puts a unit file back into vendor state. This means: - * - * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and - * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). - * - * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in - * "config", but not in "transient" or "control" or even "generated"). - * - * We remove all that in both the runtime and the persistent directories, if that applies. - */ - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - bool has_vendor = false; - char **p; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; - - STRV_FOREACH(p, paths.search_path) { - _cleanup_free_ char *path = NULL, *dropin = NULL; - struct stat st; - - path = path_make_absolute(*i, *p); - if (!path) - return -ENOMEM; - - r = lstat(path, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISREG(st.st_mode)) { - /* Check if there's a vendor version */ - r = path_is_vendor(&paths, path); - if (r < 0) - return r; - if (r > 0) - has_vendor = true; - } - - dropin = strappend(path, ".d"); - if (!dropin) - return -ENOMEM; - - r = lstat(dropin, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISDIR(st.st_mode)) { - /* Remove the drop-ins */ - r = path_shall_revert(&paths, dropin); - if (r < 0) - return r; - if (r > 0) { - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = dropin; - dropin = NULL; - } - } - } - - if (!has_vendor) - continue; - - /* OK, there's a vendor version, hence drop all configuration versions */ - STRV_FOREACH(p, paths.search_path) { - _cleanup_free_ char *path = NULL; - struct stat st; - - path = path_make_absolute(*i, *p); - if (!path) - return -ENOMEM; - - r = lstat(path, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - r = path_is_config(&paths, path); - if (r < 0) - return r; - if (r > 0) { - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = path; - path = NULL; - } - } - } - } - - strv_uniq(todo); - - r = 0; - STRV_FOREACH(i, todo) { - _cleanup_strv_free_ char **fs = NULL; - const char *rp; - char **j; - - (void) get_files_in_directory(*i, &fs); - - q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); - if (q < 0 && q != -ENOENT && r >= 0) { - r = q; - continue; - } - - STRV_FOREACH(j, fs) { - _cleanup_free_ char *t = NULL; - - t = strjoin(*i, "/", *j, NULL); - if (!t) - return -ENOMEM; - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); - - rp = skip_root(&paths, *i); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); - if (q < 0) - return q; - } - - q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); - if (r >= 0) - r = q; - - q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); - if (r >= 0) - r = q; - - 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 = {}; - UnitFileInstallInfo *i, *target_info; - const char *config_path; - 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 = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); - if (r < 0) - return r; - r = install_info_may_process(target_info, &paths, changes, n_changes); - if (r < 0) - return r; - - assert(target_info->type == UNIT_FILE_TYPE_REGULAR); - - STRV_FOREACH(f, files) { - char ***l; - - r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - 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) - l = &i->wanted_by; - else - l = &i->required_by; - - strv_free(*l); - *l = strv_new(target_info->name, NULL); - if (!*l) - return -ENOMEM; - } - - return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); -} - -int unit_file_enable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - const char *config_path; - UnitFileInstallInfo *i; - char **f; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(f, files) { - r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - 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. */ - - return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes); -} - -int unit_file_disable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(i, files) { - 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(scope, &c, &paths, &remove_symlinks_to, config_path); - if (r < 0) - return r; - - return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); -} - -int unit_file_reenable( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - 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, n, changes, n_changes); - if (r < 0) - return r; - - /* 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 *name, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; - const char *new_path, *old_path; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ - return -EINVAL; - if (streq(name, SPECIAL_DEFAULT_TARGET)) - return -EINVAL; - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, &paths, name, 0, &i); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - old_path = skip_root(&paths, i->path); - new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - - return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); -} - -int unit_file_get_default( - UnitFileScope scope, - const char *root_dir, - char **name) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _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(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, NULL, 0); - if (r < 0) - return r; - - n = strdup(i->name); - if (!n) - return -ENOMEM; - - *name = n; - return 0; -} - -static int unit_file_lookup_state( - UnitFileScope scope, - const LookupPaths *paths, - const char *name, - UnitFileState *ret) { - - _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; - - r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - if (r < 0) - return r; - - /* Shortcut things, if the caller just wants to know if this unit exists. */ - if (!ret) - return 0; - - switch (i->type) { - - case UNIT_FILE_TYPE_MASKED: - r = path_is_runtime(paths, i->path); - if (r < 0) - return r; - - state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - break; - - case UNIT_FILE_TYPE_REGULAR: - r = path_is_generator(paths, i->path); - if (r < 0) - return r; - if (r > 0) { - state = UNIT_FILE_GENERATED; - break; - } - - r = path_is_transient(paths, i->path); - if (r < 0) - return r; - if (r > 0) { - state = UNIT_FILE_TRANSIENT; - break; - } - - r = find_symlinks_in_scope(scope, paths, i->name, &state); - if (r < 0) - return r; - 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."); - } - - *ret = state; - return 0; -} - -int unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *name, - UnitFileState *ret) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - return unit_file_lookup_state(scope, &paths, name, ret); -} - -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { - _cleanup_(install_context_done) InstallContext c = {}; - int r; - - assert(paths); - assert(name); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - r = install_info_discover(scope, &c, paths, name, 0, NULL); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - return 1; -} - -static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { - _cleanup_(presets_freep) Presets ps = {}; - size_t n_allocated = 0; - _cleanup_strv_free_ char **files = NULL; - char **p; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(presets); - - if (scope == UNIT_FILE_SYSTEM) - r = conf_files_list(&files, ".preset", root_dir, - "/etc/systemd/system-preset", - "/usr/local/lib/systemd/system-preset", - "/usr/lib/systemd/system-preset", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system-preset", -#endif - NULL); - else if (scope == UNIT_FILE_GLOBAL) - r = conf_files_list(&files, ".preset", root_dir, - "/etc/systemd/user-preset", - "/usr/local/lib/systemd/user-preset", - "/usr/lib/systemd/user-preset", - NULL); - else { - *presets = (Presets){}; - - return 0; - } - - if (r < 0) - return r; - - STRV_FOREACH(p, files) { - _cleanup_fclose_ FILE *f; - char line[LINE_MAX]; - int n = 0; - - f = fopen(*p, "re"); - if (!f) { - if (errno == ENOENT) - continue; - - return -errno; - } - - FOREACH_LINE(line, f, return -errno) { - PresetRule rule = {}; - const char *parameter; - char *l; - - l = strstrip(line); - n++; - - if (isempty(l)) - continue; - if (strchr(COMMENTS, *l)) - continue; - - parameter = first_word(l, "enable"); - if (parameter) { - char *pattern; - - pattern = strdup(parameter); - if (!pattern) - return -ENOMEM; - - rule = (PresetRule) { - .pattern = pattern, - .action = PRESET_ENABLE, - }; - } - - parameter = first_word(l, "disable"); - if (parameter) { - char *pattern; - - pattern = strdup(parameter); - if (!pattern) - return -ENOMEM; - - rule = (PresetRule) { - .pattern = pattern, - .action = PRESET_DISABLE, - }; - } - - if (rule.action) { - if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1)) - return -ENOMEM; - - ps.rules[ps.n_rules++] = rule; - continue; - } - - log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); - } - } - - *presets = ps; - ps = (Presets){}; - - return 0; -} - -static int query_presets(const char *name, const Presets presets) { - PresetAction action = PRESET_UNKNOWN; - size_t i; - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - for (i = 0; i < presets.n_rules; i++) - if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { - action = presets.rules[i].action; - break; - } - - switch (action) { - case PRESET_UNKNOWN: - log_debug("Preset files don't specify rule for %s. Enabling.", name); - return 1; - case PRESET_ENABLE: - log_debug("Preset files say enable %s.", name); - return 1; - case PRESET_DISABLE: - log_debug("Preset files say disable %s.", name); - return 0; - default: - assert_not_reached("invalid preset action"); - } -} - -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { - _cleanup_(presets_freep) Presets presets = {}; - int r; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - return query_presets(name, presets); -} - -static int execute_preset( - UnitFileScope scope, - InstallContext *plus, - InstallContext *minus, - const LookupPaths *paths, - const char *config_path, - 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); - if (r < 0) - return r; - - r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, 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, 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, - UnitFilePresetMode mode, - const char *name, - Presets presets, - UnitFileChange **changes, - unsigned *n_changes) { - - UnitFileInstallInfo *i; - int r; - - if (install_info_find(plus, name) || - install_info_find(minus, name)) - return 0; - - r = query_presets(name, presets); - if (r < 0) - return r; - - if (r > 0) { - r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - if (r < 0) - return r; - - r = install_info_may_process(i, paths, changes, n_changes); - if (r < 0) - return r; - } else - r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); - - return r; -} - -int unit_file_preset( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFilePresetMode mode, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); - if (r < 0) - return r; - } - - return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes); -} - -int unit_file_preset_all( - UnitFileScope scope, - bool runtime, - const char *root_dir, - UnitFilePresetMode mode, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; - const char *config_path = NULL; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = runtime ? paths.runtime_config : paths.persistent_config; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*i); - if (!d) { - if (errno == ENOENT) - continue; - - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - dirent_ensure_type(d, de); - - if (!IN_SET(de->d_type, DT_LNK, DT_REG)) - continue; - - /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); - if (r == -ERFKILL) - r = unit_file_changes_add(changes, n_changes, - UNIT_FILE_IS_MASKED, de->d_name, NULL); - else if (r == -ENOLINK) - r = unit_file_changes_add(changes, n_changes, - UNIT_FILE_IS_DANGLING, de->d_name, NULL); - if (r < 0) - return r; - } - } - - return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes); -} - -static void unit_file_list_free_one(UnitFileList *f) { - if (!f) - return; - - free(f->path); - 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( - UnitFileScope scope, - const char *root_dir, - Hashmap *h, - char **states, - char **patterns) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(h); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*i); - if (!d) { - if (errno == ENOENT) - continue; - if (IN_SET(errno, ENOTDIR, EACCES)) { - log_debug("Failed to open \"%s\": %m", *i); - continue; - } - - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE)) - continue; - - if (hashmap_get(h, de->d_name)) - continue; - - dirent_ensure_type(d, de); - - if (!IN_SET(de->d_type, DT_LNK, DT_REG)) - continue; - - f = new0(UnitFileList, 1); - if (!f) - return -ENOMEM; - - f->path = path_make_absolute(de->d_name, *i); - if (!f->path) - return -ENOMEM; - - r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); - if (r < 0) - f->state = UNIT_FILE_BAD; - - if (!strv_isempty(states) && - !strv_contains(states, unit_file_state_to_string(f->state))) - continue; - - r = hashmap_put(h, basename(f->path), f); - if (r < 0) - return r; - - f = NULL; /* prevent cleanup */ - } - } - - return 0; -} - -static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { - [UNIT_FILE_ENABLED] = "enabled", - [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime", - [UNIT_FILE_LINKED] = "linked", - [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", - [UNIT_FILE_MASKED] = "masked", - [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", - [UNIT_FILE_STATIC] = "static", - [UNIT_FILE_DISABLED] = "disabled", - [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_GENERATED] = "generated", - [UNIT_FILE_TRANSIENT] = "transient", - [UNIT_FILE_BAD] = "bad", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); - -static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { - [UNIT_FILE_SYMLINK] = "symlink", - [UNIT_FILE_UNLINK] = "unlink", - [UNIT_FILE_IS_MASKED] = "masked", - [UNIT_FILE_IS_DANGLING] = "dangling", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); - -static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { - [UNIT_FILE_PRESET_FULL] = "full", - [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", - [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode); diff --git a/src/shared/install.h b/src/shared/install.h deleted file mode 100644 index c6aa4f6ef1..0000000000 --- a/src/shared/install.h +++ /dev/null @@ -1,256 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -typedef 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 - -#include "hashmap.h" -#include "macro.h" -#include "path-lookup.h" -#include "strv.h" -#include "unit-name.h" - -enum UnitFileScope { - UNIT_FILE_SYSTEM, - UNIT_FILE_GLOBAL, - UNIT_FILE_USER, - _UNIT_FILE_SCOPE_MAX, - _UNIT_FILE_SCOPE_INVALID = -1 -}; - -enum UnitFileState { - UNIT_FILE_ENABLED, - UNIT_FILE_ENABLED_RUNTIME, - UNIT_FILE_LINKED, - UNIT_FILE_LINKED_RUNTIME, - UNIT_FILE_MASKED, - UNIT_FILE_MASKED_RUNTIME, - UNIT_FILE_STATIC, - UNIT_FILE_DISABLED, - UNIT_FILE_INDIRECT, - UNIT_FILE_GENERATED, - UNIT_FILE_TRANSIENT, - UNIT_FILE_BAD, - _UNIT_FILE_STATE_MAX, - _UNIT_FILE_STATE_INVALID = -1 -}; - -enum UnitFilePresetMode { - UNIT_FILE_PRESET_FULL, - UNIT_FILE_PRESET_ENABLE_ONLY, - UNIT_FILE_PRESET_DISABLE_ONLY, - _UNIT_FILE_PRESET_MAX, - _UNIT_FILE_PRESET_INVALID = -1 -}; - -enum UnitFileChangeType { - UNIT_FILE_SYMLINK, - UNIT_FILE_UNLINK, - UNIT_FILE_IS_MASKED, - UNIT_FILE_IS_DANGLING, - _UNIT_FILE_CHANGE_TYPE_MAX, - _UNIT_FILE_CHANGE_INVALID = INT_MIN -}; - -/* type can either one of the UnitFileChangeTypes listed above, or a negative error. - * If source is specified, it should be the contents of the path symlink. - * In case of an error, source should be the existing symlink contents or NULL - */ -struct UnitFileChange { - int type; /* UnitFileChangeType or bust */ - char *path; - char *source; -}; - -static inline bool unit_file_changes_have_modification(const UnitFileChange* changes, unsigned n_changes) { - unsigned i; - for (i = 0; i < n_changes; i++) - if (IN_SET(changes[i].type, UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK)) - return true; - return false; -} - -struct UnitFileList { - char *path; - 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 **aliases; - char **wanted_by; - char **required_by; - 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); -} - -bool unit_type_may_alias(UnitType type) _const_; -bool unit_type_may_template(UnitType type) _const_; - -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); -int unit_file_preset( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFilePresetMode mode, - bool force, - UnitFileChange **changes, - unsigned *n_changes); -int unit_file_preset_all( - UnitFileScope scope, - bool runtime, - const char *root_dir, - UnitFilePresetMode mode, - bool force, - UnitFileChange **changes, - unsigned *n_changes); -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes); -int unit_file_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes); -int unit_file_link( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes); -int unit_file_revert( - UnitFileScope scope, - 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, - const char *target, - UnitDependency dep, - bool force, - UnitFileChange **changes, - unsigned *n_changes); - -int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); - -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); -Hashmap* 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); -void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet); - -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); - -const char *unit_file_state_to_string(UnitFileState s) _const_; -UnitFileState unit_file_state_from_string(const char *s) _pure_; -/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */ - -const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_; -UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_; - -const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_; -UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_; diff --git a/src/shared/linux/auto_dev-ioctl.h b/src/shared/linux/auto_dev-ioctl.h deleted file mode 100644 index aeaeb3ea7a..0000000000 --- a/src/shared/linux/auto_dev-ioctl.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2008 Red Hat, Inc. All rights reserved. - * Copyright 2008 Ian Kent - * - * This file is part of the Linux kernel and is made available under - * the terms of the GNU General Public License, version 2, or at your - * option, any later version, incorporated herein by reference. - */ - -#ifndef _LINUX_AUTO_DEV_IOCTL_H -#define _LINUX_AUTO_DEV_IOCTL_H - -#include - -#ifdef __KERNEL__ -#include -#else -#include -#endif /* __KERNEL__ */ - -#define AUTOFS_DEVICE_NAME "autofs" - -#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1 -#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0 - -#define AUTOFS_DEVID_LEN 16 - -#define AUTOFS_DEV_IOCTL_SIZE sizeof(struct autofs_dev_ioctl) - -/* - * An ioctl interface for autofs mount point control. - */ - -struct args_protover { - __u32 version; -}; - -struct args_protosubver { - __u32 sub_version; -}; - -struct args_openmount { - __u32 devid; -}; - -struct args_ready { - __u32 token; -}; - -struct args_fail { - __u32 token; - __s32 status; -}; - -struct args_setpipefd { - __s32 pipefd; -}; - -struct args_timeout { - __u64 timeout; -}; - -struct args_requester { - __u32 uid; - __u32 gid; -}; - -struct args_expire { - __u32 how; -}; - -struct args_askumount { - __u32 may_umount; -}; - -struct args_ismountpoint { - union { - struct args_in { - __u32 type; - } in; - struct args_out { - __u32 devid; - __u32 magic; - } out; - }; -}; - -/* - * All the ioctls use this structure. - * When sending a path size must account for the total length - * of the chunk of memory otherwise is is the size of the - * structure. - */ - -struct autofs_dev_ioctl { - __u32 ver_major; - __u32 ver_minor; - __u32 size; /* total size of data passed in - * including this struct */ - __s32 ioctlfd; /* automount command fd */ - - /* Command parameters */ - - union { - struct args_protover protover; - struct args_protosubver protosubver; - struct args_openmount openmount; - struct args_ready ready; - struct args_fail fail; - struct args_setpipefd setpipefd; - struct args_timeout timeout; - struct args_requester requester; - struct args_expire expire; - struct args_askumount askumount; - struct args_ismountpoint ismountpoint; - }; - - char path[0]; -}; - -static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in) { - memset(in, 0, sizeof(struct autofs_dev_ioctl)); - in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR; - in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR; - in->size = sizeof(struct autofs_dev_ioctl); - in->ioctlfd = -1; - return; -} - -/* - * If you change this make sure you make the corresponding change - * to autofs-dev-ioctl.c:lookup_ioctl() - */ -enum { - /* Get various version info */ - AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71, - AUTOFS_DEV_IOCTL_PROTOVER_CMD, - AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, - - /* Open mount ioctl fd */ - AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, - - /* Close mount ioctl fd */ - AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, - - /* Mount/expire status returns */ - AUTOFS_DEV_IOCTL_READY_CMD, - AUTOFS_DEV_IOCTL_FAIL_CMD, - - /* Activate/deactivate autofs mount */ - AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, - AUTOFS_DEV_IOCTL_CATATONIC_CMD, - - /* Expiry timeout */ - AUTOFS_DEV_IOCTL_TIMEOUT_CMD, - - /* Get mount last requesting uid and gid */ - AUTOFS_DEV_IOCTL_REQUESTER_CMD, - - /* Check for eligible expire candidates */ - AUTOFS_DEV_IOCTL_EXPIRE_CMD, - - /* Request busy status */ - AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, - - /* Check if path is a mountpoint */ - AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, -}; - -#define AUTOFS_IOCTL 0x93 - -#define AUTOFS_DEV_IOCTL_VERSION \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_PROTOVER \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_PROTOSUBVER \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_OPENMOUNT \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_READY \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_FAIL \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_SETPIPEFD \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_CATATONIC \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_TIMEOUT \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_REQUESTER \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_EXPIRE \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_ASKUMOUNT \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl) - -#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \ - _IOWR(AUTOFS_IOCTL, \ - AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl) - -#endif /* _LINUX_AUTO_DEV_IOCTL_H */ diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c deleted file mode 100644 index d04728f505..0000000000 --- a/src/shared/logs-show.c +++ /dev/null @@ -1,1310 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "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 "time-util.h" -#include "utf8.h" -#include "util.h" - -/* 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 - -#define JSON_THRESHOLD 4096 - -static int print_catalog(FILE *f, sd_journal *j) { - int r; - _cleanup_free_ char *t = NULL, *z = NULL; - - - r = sd_journal_get_catalog(j, &t); - if (r < 0) - return r; - - z = strreplace(strstrip(t), "\n", "\n-- "); - if (!z) - return log_oom(); - - fputs("-- ", f); - fputs(z, f); - fputc('\n', f); - - return 0; -} - -static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { - size_t fl, nl; - char *buf; - - assert(data); - assert(field); - assert(target); - - fl = strlen(field); - if (length < fl) - return 0; - - if (memcmp(data, field, fl)) - return 0; - - nl = length - fl; - buf = new(char, nl+1); - if (!buf) - return log_oom(); - - memcpy(buf, (const char*) data + fl, nl); - buf[nl] = 0; - - free(*target); - *target = buf; - - if (target_size) - *target_size = nl; - - return 1; -} - -static bool shall_print(const char *p, size_t l, OutputFlags flags) { - assert(p); - - if (flags & OUTPUT_SHOW_ALL) - return true; - - if (l >= PRINT_CHAR_THRESHOLD) - return false; - - if (!utf8_is_printable(p, l)) - return false; - - return true; -} - -static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) { - const char *color_on = "", *color_off = ""; - const char *pos, *end; - bool ellipsized = false; - int line = 0; - - if (flags & OUTPUT_COLOR) { - if (priority <= LOG_ERR) { - color_on = ANSI_HIGHLIGHT_RED; - color_off = ANSI_NORMAL; - } else if (priority <= LOG_NOTICE) { - color_on = ANSI_HIGHLIGHT; - color_off = ANSI_NORMAL; - } - } - - /* A special case: make sure that we print a newline when - the message is empty. */ - if (message_len == 0) - fputs("\n", f); - - for (pos = message; - pos < message + message_len; - pos = end + 1, line++) { - bool continuation = line > 0; - bool tail_line; - int len; - for (end = pos; end < message + message_len && *end != '\n'; end++) - ; - len = end - pos; - assert(len >= 0); - - /* We need to figure out when we are showing not-last line, *and* - * will skip subsequent lines. In that case, we will put the dots - * at the end of the line, instead of putting dots in the middle - * or not at all. - */ - tail_line = - line + 1 == PRINT_LINE_THRESHOLD || - end + 1 >= message + PRINT_CHAR_THRESHOLD; - - if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || - (prefix + len + 1 < n_columns && !tail_line)) { - fprintf(f, "%*s%s%.*s%s\n", - continuation * prefix, "", - color_on, len, pos, color_off); - continue; - } - - /* Beyond this point, ellipsization will happen. */ - ellipsized = true; - - if (prefix < n_columns && n_columns - prefix >= 3) { - if (n_columns - prefix > (unsigned) len + 3) - fprintf(f, "%*s%s%.*s...%s\n", - continuation * prefix, "", - color_on, len, pos, color_off); - else { - _cleanup_free_ char *e; - - e = ellipsize_mem(pos, len, n_columns - prefix, - tail_line ? 100 : 90); - if (!e) - fprintf(f, "%*s%s%.*s%s\n", - continuation * prefix, "", - color_on, len, pos, color_off); - else - fprintf(f, "%*s%s%s%s\n", - continuation * prefix, "", - color_on, e, color_off); - } - } else - fputs("...\n", f); - - if (tail_line) - break; - } - - return ellipsized; -} - -static int output_short( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) { - - int r; - const void *data; - size_t length; - size_t n = 0; - _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL; - size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0; - int p = LOG_INFO; - bool ellipsized = false; - - assert(f); - assert(j); - - /* Set the threshold to one bigger than the actual print - * threshold, so that if the line is actually longer than what - * we're willing to print, ellipsization will occur. This way - * we won't output a misleading line without any indication of - * truncation. - */ - sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1); - - JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { - - r = parse_field(data, length, "PRIORITY=", &priority, &priority_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_COMM=", &comm, &comm_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_PID=", &pid, &pid_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "MESSAGE=", &message, &message_len); - if (r < 0) - return r; - } - if (r == -EBADMSG) { - log_debug_errno(r, "Skipping message we can't read: %m"); - return 0; - } - if (r < 0) - return log_error_errno(r, "Failed to get journal fields: %m"); - - if (!message) { - log_debug("Skipping message without MESSAGE= field."); - return 0; - } - - if (!(flags & OUTPUT_SHOW_ALL)) - strip_tab_ansi(&message, &message_len); - - if (priority_len == 1 && *priority >= '0' && *priority <= '7') - p = *priority - '0'; - - if (mode == OUTPUT_SHORT_MONOTONIC) { - uint64_t t; - sd_id128_t boot_id; - - r = -ENOENT; - - if (monotonic) - r = safe_atou64(monotonic, &t); - - if (r < 0) - r = sd_journal_get_monotonic_usec(j, &t, &boot_id); - - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - fprintf(f, "[%5llu.%06llu]", - (unsigned long long) (t / USEC_PER_SEC), - (unsigned long long) (t % USEC_PER_SEC)); - - n += 1 + 5 + 1 + 6 + 1; - - } else { - char buf[64]; - uint64_t x; - time_t t; - struct tm tm; - struct tm *(*gettime_r)(const time_t *, struct tm *); - - r = -ENOENT; - gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; - - if (realtime) - r = safe_atou64(realtime, &x); - - if (r < 0) - r = sd_journal_get_realtime_usec(j, &x); - - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - t = (time_t) (x / USEC_PER_SEC); - - switch (mode) { - - case OUTPUT_SHORT_UNIX: - r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); - break; - - case OUTPUT_SHORT_ISO: - r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); - break; - - case OUTPUT_SHORT_PRECISE: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - if (r > 0) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); - break; - - default: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - } - - if (r <= 0) { - log_error("Failed to format time."); - return -EINVAL; - } - - fputs(buf, f); - n += strlen(buf); - } - - if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { - /* Suppress display of the hostname if this is requested. */ - hostname = NULL; - hostname_len = 0; - } - - if (hostname && shall_print(hostname, hostname_len, flags)) { - fprintf(f, " %.*s", (int) hostname_len, hostname); - n += hostname_len + 1; - } - - if (identifier && shall_print(identifier, identifier_len, flags)) { - fprintf(f, " %.*s", (int) identifier_len, identifier); - n += identifier_len + 1; - } else if (comm && shall_print(comm, comm_len, flags)) { - fprintf(f, " %.*s", (int) comm_len, comm); - n += comm_len + 1; - } else - fputs(" unknown", f); - - if (pid && shall_print(pid, pid_len, flags)) { - fprintf(f, "[%.*s]", (int) pid_len, pid); - n += pid_len + 2; - } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) { - fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid); - n += fake_pid_len + 2; - } - - if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) { - char bytes[FORMAT_BYTES_MAX]; - fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len)); - } else { - fputs(": ", f); - ellipsized |= - print_multiline(f, n + 2, n_columns, flags, p, message, message_len); - } - - if (flags & OUTPUT_CATALOG) - print_catalog(f, j); - - return ellipsized; -} - -static int output_verbose( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) { - - const void *data; - size_t length; - _cleanup_free_ char *cursor = NULL; - uint64_t realtime = 0; - char ts[FORMAT_TIMESTAMP_MAX + 7]; - int r; - - assert(f); - assert(j); - - sd_journal_set_data_threshold(j, 0); - - r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length); - if (r == -ENOENT) - log_debug("Source realtime timestamp not found"); - else if (r < 0) - return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m"); - else { - _cleanup_free_ char *value = NULL; - - r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, NULL); - if (r < 0) - return r; - assert(r > 0); - - r = safe_atou64(value, &realtime); - if (r < 0) - log_debug_errno(r, "Failed to parse realtime timestamp: %m"); - } - - if (r < 0) { - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) - return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m"); - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) - return log_error_errno(r, "Failed to get cursor: %m"); - - fprintf(f, "%s [%s]\n", - flags & OUTPUT_UTC ? - format_timestamp_us_utc(ts, sizeof(ts), realtime) : - format_timestamp_us(ts, sizeof(ts), realtime), - cursor); - - JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { - const char *c; - int fieldlen; - const char *on = "", *off = ""; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - fieldlen = c - (const char*) data; - - if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) { - on = ANSI_HIGHLIGHT; - off = ANSI_NORMAL; - } - - if ((flags & OUTPUT_SHOW_ALL) || - (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH) - && utf8_is_printable(data, length))) { - fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data); - print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1); - fputs(off, f); - } else { - char bytes[FORMAT_BYTES_MAX]; - - fprintf(f, " %s%.*s=[%s blob data]%s\n", - on, - (int) (c - (const char*) data), - (const char*) data, - format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1), - off); - } - } - - if (r < 0) - return r; - - if (flags & OUTPUT_CATALOG) - print_catalog(f, j); - - return 0; -} - -static int output_export( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) { - - sd_id128_t boot_id; - char sid[33]; - int r; - usec_t realtime, monotonic; - _cleanup_free_ char *cursor = NULL; - const void *data; - size_t length; - - assert(j); - - sd_journal_set_data_threshold(j, 0); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) - return log_error_errno(r, "Failed to get cursor: %m"); - - fprintf(f, - "__CURSOR=%s\n" - "__REALTIME_TIMESTAMP="USEC_FMT"\n" - "__MONOTONIC_TIMESTAMP="USEC_FMT"\n" - "_BOOT_ID=%s\n", - cursor, - realtime, - monotonic, - sd_id128_to_string(boot_id, sid)); - - JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { - - /* We already printed the boot id, from the data in - * the header, hence let's suppress it here */ - if (length >= 9 && - startswith(data, "_BOOT_ID=")) - continue; - - if (utf8_is_printable_newline(data, length, false)) - fwrite(data, length, 1, f); - else { - const char *c; - uint64_t le64; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fwrite(data, c - (const char*) data, 1, f); - fputc('\n', f); - le64 = htole64(length - (c - (const char*) data) - 1); - fwrite(&le64, sizeof(le64), 1, f); - fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f); - } - - fputc('\n', f); - } - - if (r < 0) - return r; - - fputc('\n', f); - - return 0; -} - -void json_escape( - FILE *f, - const char* p, - size_t l, - OutputFlags flags) { - - assert(f); - assert(p); - - if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD) - fputs("null", f); - - else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) { - bool not_first = false; - - fputs("[ ", f); - - while (l > 0) { - if (not_first) - fprintf(f, ", %u", (uint8_t) *p); - else { - not_first = true; - fprintf(f, "%u", (uint8_t) *p); - } - - p++; - l--; - } - - fputs(" ]", f); - } else { - fputc('\"', f); - - while (l > 0) { - if (*p == '"' || *p == '\\') { - fputc('\\', f); - fputc(*p, f); - } else if (*p == '\n') - fputs("\\n", f); - else if ((uint8_t) *p < ' ') - fprintf(f, "\\u%04x", (uint8_t) *p); - else - fputc(*p, f); - - p++; - l--; - } - - fputc('\"', f); - } -} - -static int output_json( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) { - - uint64_t realtime, monotonic; - _cleanup_free_ char *cursor = NULL; - const void *data; - size_t length; - sd_id128_t boot_id; - char sid[33], *k; - int r; - Hashmap *h = NULL; - bool done, separator; - - assert(j); - - sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) - return log_error_errno(r, "Failed to get cursor: %m"); - - if (mode == OUTPUT_JSON_PRETTY) - fprintf(f, - "{\n" - "\t\"__CURSOR\" : \"%s\",\n" - "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n" - "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n" - "\t\"_BOOT_ID\" : \"%s\"", - cursor, - realtime, - monotonic, - sd_id128_to_string(boot_id, sid)); - else { - if (mode == OUTPUT_JSON_SSE) - fputs("data: ", f); - - fprintf(f, - "{ \"__CURSOR\" : \"%s\", " - "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", " - "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", " - "\"_BOOT_ID\" : \"%s\"", - cursor, - realtime, - monotonic, - sd_id128_to_string(boot_id, sid)); - } - - h = hashmap_new(&string_hash_ops); - if (!h) - return log_oom(); - - /* First round, iterate through the entry and count how often each field appears */ - JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { - const char *eq; - char *n; - unsigned u; - - if (length >= 9 && - memcmp(data, "_BOOT_ID=", 9) == 0) - continue; - - eq = memchr(data, '=', length); - if (!eq) - continue; - - n = strndup(data, eq - (const char*) data); - if (!n) { - r = log_oom(); - goto finish; - } - - u = PTR_TO_UINT(hashmap_get(h, n)); - if (u == 0) { - r = hashmap_put(h, n, UINT_TO_PTR(1)); - if (r < 0) { - free(n); - log_oom(); - goto finish; - } - } else { - r = hashmap_update(h, n, UINT_TO_PTR(u + 1)); - free(n); - if (r < 0) { - log_oom(); - goto finish; - } - } - } - - if (r < 0) - return r; - - separator = true; - do { - done = true; - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - const char *eq; - char *kk, *n; - size_t m; - unsigned u; - - /* We already printed the boot id, from the data in - * the header, hence let's suppress it here */ - if (length >= 9 && - memcmp(data, "_BOOT_ID=", 9) == 0) - continue; - - eq = memchr(data, '=', length); - if (!eq) - continue; - - if (separator) { - if (mode == OUTPUT_JSON_PRETTY) - fputs(",\n\t", f); - else - fputs(", ", f); - } - - m = eq - (const char*) data; - - n = strndup(data, m); - if (!n) { - r = log_oom(); - goto finish; - } - - u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk)); - if (u == 0) { - /* We already printed this, let's jump to the next */ - free(n); - separator = false; - - continue; - } else if (u == 1) { - /* Field only appears once, output it directly */ - - json_escape(f, data, m, flags); - fputs(" : ", f); - - json_escape(f, eq + 1, length - m - 1, flags); - - hashmap_remove(h, n); - free(kk); - free(n); - - separator = true; - - continue; - - } else { - /* Field appears multiple times, output it as array */ - json_escape(f, data, m, flags); - fputs(" : [ ", f); - json_escape(f, eq + 1, length - m - 1, flags); - - /* Iterate through the end of the list */ - - while (sd_journal_enumerate_data(j, &data, &length) > 0) { - if (length < m + 1) - continue; - - if (memcmp(data, n, m) != 0) - continue; - - if (((const char*) data)[m] != '=') - continue; - - fputs(", ", f); - json_escape(f, (const char*) data + m + 1, length - m - 1, flags); - } - - fputs(" ]", f); - - hashmap_remove(h, n); - free(kk); - free(n); - - /* Iterate data fields form the beginning */ - done = false; - separator = true; - - break; - } - } - - } while (!done); - - if (mode == OUTPUT_JSON_PRETTY) - fputs("\n}\n", f); - else if (mode == OUTPUT_JSON_SSE) - fputs("}\n\n", f); - else - fputs(" }\n", f); - - r = 0; - -finish: - while ((k = hashmap_steal_first_key(h))) - free(k); - - hashmap_free(h); - - return r; -} - -static int output_cat( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) { - - const void *data; - size_t l; - int r; - - assert(j); - assert(f); - - sd_journal_set_data_threshold(j, 0); - - r = sd_journal_get_data(j, "MESSAGE", &data, &l); - if (r < 0) { - /* An entry without MESSAGE=? */ - if (r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to get data: %m"); - } - - assert(l >= 8); - - fwrite((const char*) data + 8, 1, l - 8, f); - fputc('\n', f); - - return 0; -} - -static int (*output_funcs[_OUTPUT_MODE_MAX])( - FILE *f, - sd_journal*j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags) = { - - [OUTPUT_SHORT] = output_short, - [OUTPUT_SHORT_ISO] = output_short, - [OUTPUT_SHORT_PRECISE] = output_short, - [OUTPUT_SHORT_MONOTONIC] = output_short, - [OUTPUT_SHORT_UNIX] = output_short, - [OUTPUT_VERBOSE] = output_verbose, - [OUTPUT_EXPORT] = output_export, - [OUTPUT_JSON] = output_json, - [OUTPUT_JSON_PRETTY] = output_json, - [OUTPUT_JSON_SSE] = output_json, - [OUTPUT_CAT] = output_cat -}; - -int output_journal( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags, - bool *ellipsized) { - - int ret; - assert(mode >= 0); - assert(mode < _OUTPUT_MODE_MAX); - - if (n_columns <= 0) - n_columns = columns(); - - ret = output_funcs[mode](f, j, mode, n_columns, flags); - fflush(stdout); - - if (ellipsized && ret > 0) - *ellipsized = true; - - return ret; -} - -static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) { - assert(f); - assert(flags); - - if (!(*flags & OUTPUT_BEGIN_NEWLINE)) - return 0; - - /* Print a beginning new line if that's request, but only once - * on the first line we print. */ - - fputc('\n', f); - *flags &= ~OUTPUT_BEGIN_NEWLINE; - return 0; -} - -static int show_journal(FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - OutputFlags flags, - bool *ellipsized) { - - int r; - unsigned line = 0; - bool need_seek = false; - int warn_cutoff = flags & OUTPUT_WARN_CUTOFF; - - assert(j); - assert(mode >= 0); - assert(mode < _OUTPUT_MODE_MAX); - - /* Seek to end */ - r = sd_journal_seek_tail(j); - if (r < 0) - return log_error_errno(r, "Failed to seek to tail: %m"); - - r = sd_journal_previous_skip(j, how_many); - if (r < 0) - return log_error_errno(r, "Failed to skip previous: %m"); - - for (;;) { - for (;;) { - usec_t usec; - - if (need_seek) { - r = sd_journal_next(j); - if (r < 0) - return log_error_errno(r, "Failed to iterate through journal: %m"); - } - - if (r == 0) - break; - - need_seek = true; - - if (not_before > 0) { - r = sd_journal_get_monotonic_usec(j, &usec, NULL); - - /* -ESTALE is returned if the - timestamp is not from this boot */ - if (r == -ESTALE) - continue; - else if (r < 0) - return log_error_errno(r, "Failed to get journal time: %m"); - - if (usec < not_before) - continue; - } - - line++; - maybe_print_begin_newline(f, &flags); - - r = output_journal(f, j, mode, n_columns, flags, ellipsized); - if (r < 0) - return r; - } - - if (warn_cutoff && line < how_many && not_before > 0) { - sd_id128_t boot_id; - usec_t cutoff = 0; - - /* Check whether the cutoff line is too early */ - - r = sd_id128_get_boot(&boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get boot id: %m"); - - r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL); - if (r < 0) - return log_error_errno(r, "Failed to get journal cutoff time: %m"); - - if (r > 0 && not_before < cutoff) { - maybe_print_begin_newline(f, &flags); - fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n"); - } - - warn_cutoff = false; - } - - if (!(flags & OUTPUT_FOLLOW)) - break; - - r = sd_journal_wait(j, USEC_INFINITY); - if (r < 0) - return log_error_errno(r, "Failed to wait for journal: %m"); - - } - - return 0; -} - -int add_matches_for_unit(sd_journal *j, const char *unit) { - const char *m1, *m2, *m3, *m4; - int r; - - assert(j); - assert(unit); - - m1 = strjoina("_SYSTEMD_UNIT=", unit); - m2 = strjoina("COREDUMP_UNIT=", unit); - m3 = strjoina("UNIT=", unit); - m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit); - - (void)( - /* Look for messages from the service itself */ - (r = sd_journal_add_match(j, m1, 0)) || - - /* Look for coredumps of the service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) || - (r = sd_journal_add_match(j, "_UID=0", 0)) || - (r = sd_journal_add_match(j, m2, 0)) || - - /* Look for messages from PID 1 about this service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, "_PID=1", 0)) || - (r = sd_journal_add_match(j, m3, 0)) || - - /* Look for messages from authorized daemons about this service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, "_UID=0", 0)) || - (r = sd_journal_add_match(j, m4, 0)) - ); - - if (r == 0 && endswith(unit, ".slice")) { - const char *m5; - - m5 = strjoina("_SYSTEMD_SLICE=", unit); - - /* Show all messages belonging to a slice */ - (void)( - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, m5, 0)) - ); - } - - return r; -} - -int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { - int r; - char *m1, *m2, *m3, *m4; - char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)]; - - assert(j); - assert(unit); - - m1 = strjoina("_SYSTEMD_USER_UNIT=", unit); - m2 = strjoina("USER_UNIT=", unit); - m3 = strjoina("COREDUMP_USER_UNIT=", unit); - m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit); - sprintf(muid, "_UID="UID_FMT, uid); - - (void) ( - /* Look for messages from the user service itself */ - (r = sd_journal_add_match(j, m1, 0)) || - (r = sd_journal_add_match(j, muid, 0)) || - - /* Look for messages from systemd about this service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, m2, 0)) || - (r = sd_journal_add_match(j, muid, 0)) || - - /* Look for coredumps of the service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, m3, 0)) || - (r = sd_journal_add_match(j, muid, 0)) || - (r = sd_journal_add_match(j, "_UID=0", 0)) || - - /* Look for messages from authorized daemons about this service */ - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, m4, 0)) || - (r = sd_journal_add_match(j, muid, 0)) || - (r = sd_journal_add_match(j, "_UID=0", 0)) - ); - - if (r == 0 && endswith(unit, ".slice")) { - const char *m5; - - m5 = strjoina("_SYSTEMD_SLICE=", unit); - - /* Show all messages belonging to a slice */ - (void)( - (r = sd_journal_add_disjunction(j)) || - (r = sd_journal_add_match(j, m5, 0)) || - (r = sd_journal_add_match(j, muid, 0)) - ); - } - - return r; -} - -static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; - pid_t pid, child; - siginfo_t si; - char buf[37]; - ssize_t k; - int r; - - assert(machine); - assert(boot_id); - - if (!machine_name_is_valid(machine)) - return -EINVAL; - - r = container_get_leader(machine, &pid); - if (r < 0) - return r; - - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &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 fd; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd); - if (r < 0) - _exit(EXIT_FAILURE); - - fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - _exit(EXIT_FAILURE); - - r = loop_read_exact(fd, buf, 36, false); - safe_close(fd); - if (r < 0) - _exit(EXIT_FAILURE); - - k = send(pair[1], buf, 36, MSG_NOSIGNAL); - if (k != 36) - _exit(EXIT_FAILURE); - - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); - - r = wait_for_terminate(child, &si); - if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return r < 0 ? r : -EIO; - - k = recv(pair[0], buf, 36, 0); - if (k != 36) - return -EIO; - - buf[36] = 0; - r = sd_id128_from_string(buf, boot_id); - if (r < 0) - return r; - - return 0; -} - -int add_match_this_boot(sd_journal *j, const char *machine) { - char match[9+32+1] = "_BOOT_ID="; - sd_id128_t boot_id; - int r; - - assert(j); - - if (machine) { - r = get_boot_id_for_machine(machine, &boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get boot id of container %s: %m", machine); - } else { - r = sd_id128_get_boot(&boot_id); - if (r < 0) - return log_error_errno(r, "Failed to get boot id: %m"); - } - - sd_id128_to_string(boot_id, match + 9); - r = sd_journal_add_match(j, match, strlen(match)); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - r = sd_journal_add_conjunction(j); - if (r < 0) - return log_error_errno(r, "Failed to add conjunction: %m"); - - return 0; -} - -int show_journal_by_unit( - FILE *f, - const char *unit, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - uid_t uid, - OutputFlags flags, - int journal_open_flags, - bool system_unit, - bool *ellipsized) { - - _cleanup_(sd_journal_closep) sd_journal *j = NULL; - int r; - - assert(mode >= 0); - assert(mode < _OUTPUT_MODE_MAX); - assert(unit); - - if (how_many <= 0) - return 0; - - r = sd_journal_open(&j, journal_open_flags); - if (r < 0) - return log_error_errno(r, "Failed to open journal: %m"); - - r = add_match_this_boot(j, NULL); - if (r < 0) - return r; - - if (system_unit) - r = add_matches_for_unit(j, unit); - else - r = add_matches_for_user_unit(j, unit, uid); - if (r < 0) - return log_error_errno(r, "Failed to add unit matches: %m"); - - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { - _cleanup_free_ char *filter; - - filter = journal_make_match_string(j); - if (!filter) - return log_oom(); - - log_debug("Journal filter: %s", filter); - } - - return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized); -} diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h deleted file mode 100644 index 6643440881..0000000000 --- a/src/shared/logs-show.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-journal.h" - -#include "macro.h" -#include "output-mode.h" -#include "time-util.h" -#include "util.h" - -int output_journal( - FILE *f, - sd_journal *j, - OutputMode mode, - unsigned n_columns, - OutputFlags flags, - bool *ellipsized); - -int add_match_this_boot(sd_journal *j, const char *machine); - -int add_matches_for_unit( - sd_journal *j, - const char *unit); - -int add_matches_for_user_unit( - sd_journal *j, - const char *unit, - uid_t uid); - -int show_journal_by_unit( - FILE *f, - const char *unit, - OutputMode mode, - unsigned n_columns, - usec_t not_before, - unsigned how_many, - uid_t uid, - OutputFlags flags, - int journal_open_flags, - bool system_unit, - bool *ellipsized); - -void json_escape( - FILE *f, - const char* p, - size_t l, - OutputFlags flags); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c deleted file mode 100644 index 529d89ee2a..0000000000 --- a/src/shared/machine-image.c +++ /dev/null @@ -1,818 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 "util.h" -#include "xattr-util.h" - -static const char image_search_path[] = - "/var/lib/machines\0" - "/var/lib/container\0" /* legacy */ - "/usr/local/lib/machines\0" - "/usr/lib/machines\0"; - -Image *image_unref(Image *i) { - if (!i) - return NULL; - - free(i->name); - free(i->path); - free(i); - return NULL; -} - -static char **image_settings_path(Image *image) { - _cleanup_strv_free_ char **l = NULL; - char **ret; - const char *fn, *s; - unsigned i = 0; - - assert(image); - - l = new0(char*, 4); - if (!l) - return NULL; - - fn = strjoina(image->name, ".nspawn"); - - FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") { - l[i] = strappend(s, fn); - if (!l[i]) - return NULL; - - i++; - } - - l[i] = file_in_same_dir(image->path, fn); - if (!l[i]) - return NULL; - - ret = l; - l = NULL; - - return ret; -} - -static int image_new( - ImageType t, - const char *pretty, - const char *path, - const char *filename, - bool read_only, - usec_t crtime, - usec_t mtime, - Image **ret) { - - _cleanup_(image_unrefp) Image *i = NULL; - - assert(t >= 0); - assert(t < _IMAGE_TYPE_MAX); - assert(pretty); - assert(filename); - assert(ret); - - i = new0(Image, 1); - if (!i) - return -ENOMEM; - - i->type = t; - i->read_only = read_only; - i->crtime = crtime; - i->mtime = mtime; - i->usage = i->usage_exclusive = (uint64_t) -1; - i->limit = i->limit_exclusive = (uint64_t) -1; - - i->name = strdup(pretty); - if (!i->name) - return -ENOMEM; - - if (path) - i->path = strjoin(path, "/", filename, NULL); - else - i->path = strdup(filename); - - if (!i->path) - return -ENOMEM; - - path_kill_slashes(i->path); - - *ret = i; - i = NULL; - - return 0; -} - -static int image_make( - const char *pretty, - int dfd, - const char *path, - const char *filename, - Image **ret) { - - struct stat st; - bool read_only; - int r; - - assert(filename); - - /* We explicitly *do* follow symlinks here, since we want to - * allow symlinking trees into /var/lib/machines/, and treat - * them normally. */ - - if (fstatat(dfd, filename, &st, 0) < 0) - return -errno; - - read_only = - (path && path_startswith(path, "/usr")) || - (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS); - - if (S_ISDIR(st.st_mode)) { - _cleanup_close_ int fd = -1; - unsigned file_attr = 0; - - if (!ret) - return 1; - - if (!pretty) - pretty = filename; - - fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (fd < 0) - return -errno; - - /* btrfs subvolumes have inode 256 */ - if (st.st_ino == 256) { - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (r) { - BtrfsSubvolInfo info; - - /* It's a btrfs subvolume */ - - r = btrfs_subvol_get_info_fd(fd, 0, &info); - if (r < 0) - return r; - - r = image_new(IMAGE_SUBVOLUME, - pretty, - path, - filename, - info.read_only || read_only, - info.otime, - 0, - ret); - if (r < 0) - return r; - - if (btrfs_quota_scan_ongoing(fd) == 0) { - BtrfsQuotaInfo quota; - - r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); - if (r >= 0) { - (*ret)->usage = quota.referenced; - (*ret)->usage_exclusive = quota.exclusive; - - (*ret)->limit = quota.referenced_max; - (*ret)->limit_exclusive = quota.exclusive_max; - } - } - - return 1; - } - } - - /* If the IMMUTABLE bit is set, we consider the - * directory read-only. Since the ioctl is not - * supported everywhere we ignore failures. */ - (void) read_attr_fd(fd, &file_attr); - - /* It's just a normal directory. */ - r = image_new(IMAGE_DIRECTORY, - pretty, - path, - filename, - read_only || (file_attr & FS_IMMUTABLE_FL), - 0, - 0, - ret); - if (r < 0) - return r; - - return 1; - - } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) { - usec_t crtime = 0; - - /* It's a RAW disk image */ - - if (!ret) - return 1; - - fd_getcrtime_at(dfd, filename, &crtime, 0); - - if (!pretty) - pretty = strndupa(filename, strlen(filename) - 4); - - r = image_new(IMAGE_RAW, - pretty, - path, - filename, - !(st.st_mode & 0222) || read_only, - crtime, - timespec_load(&st.st_mtim), - ret); - if (r < 0) - return r; - - (*ret)->usage = (*ret)->usage_exclusive = st.st_blocks * 512; - (*ret)->limit = (*ret)->limit_exclusive = st.st_size; - - return 1; - } - - return 0; -} - -int image_find(const char *name, Image **ret) { - const char *path; - int r; - - assert(name); - - /* There are no images with invalid names */ - if (!image_name_is_valid(name)) - return 0; - - NULSTR_FOREACH(path, image_search_path) { - _cleanup_closedir_ DIR *d = NULL; - - d = opendir(path); - if (!d) { - if (errno == ENOENT) - continue; - - return -errno; - } - - r = image_make(NULL, dirfd(d), path, name, ret); - if (r == 0 || r == -ENOENT) { - _cleanup_free_ char *raw = NULL; - - raw = strappend(name, ".raw"); - if (!raw) - return -ENOMEM; - - r = image_make(NULL, dirfd(d), path, raw, ret); - if (r == 0 || r == -ENOENT) - continue; - } - if (r < 0) - return r; - - return 1; - } - - if (streq(name, ".host")) - return image_make(".host", AT_FDCWD, NULL, "/", ret); - - return 0; -}; - -int image_discover(Hashmap *h) { - const char *path; - int r; - - assert(h); - - NULSTR_FOREACH(path, image_search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(path); - if (!d) { - if (errno == ENOENT) - continue; - - return -errno; - } - - FOREACH_DIRENT_ALL(de, d, return -errno) { - _cleanup_(image_unrefp) Image *image = NULL; - - if (!image_name_is_valid(de->d_name)) - continue; - - if (hashmap_contains(h, de->d_name)) - continue; - - r = image_make(NULL, dirfd(d), path, de->d_name, &image); - if (r == 0 || r == -ENOENT) - continue; - if (r < 0) - return r; - - r = hashmap_put(h, image->name, image); - if (r < 0) - return r; - - image = NULL; - } - } - - if (!hashmap_contains(h, ".host")) { - _cleanup_(image_unrefp) Image *image = NULL; - - r = image_make(".host", AT_FDCWD, NULL, "/", &image); - if (r < 0) - return r; - - r = hashmap_put(h, image->name, image); - if (r < 0) - return r; - - image = NULL; - - } - - return 0; -} - -void image_hashmap_free(Hashmap *map) { - Image *i; - - while ((i = hashmap_steal_first(map))) - image_unref(i); - - hashmap_free(map); -} - -int image_remove(Image *i) { - _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; - _cleanup_strv_free_ char **settings = NULL; - char **j; - int r; - - assert(i); - - if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) - return -EROFS; - - settings = image_settings_path(i); - if (!settings) - return -ENOMEM; - - /* Make sure we don't interfere with a running nspawn */ - r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); - if (r < 0) - return r; - - switch (i->type) { - - case IMAGE_SUBVOLUME: - r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r < 0) - return r; - break; - - case IMAGE_DIRECTORY: - /* Allow deletion of read-only directories */ - (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); - r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - if (r < 0) - return r; - - break; - - case IMAGE_RAW: - if (unlink(i->path) < 0) - return -errno; - break; - - default: - return -EOPNOTSUPP; - } - - STRV_FOREACH(j, settings) { - if (unlink(*j) < 0 && errno != ENOENT) - log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); - } - - return 0; -} - -static int rename_settings_file(const char *path, const char *new_name) { - _cleanup_free_ char *rs = NULL; - const char *fn; - - fn = strjoina(new_name, ".nspawn"); - - rs = file_in_same_dir(path, fn); - if (!rs) - return -ENOMEM; - - return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs); -} - -int image_rename(Image *i, const char *new_name) { - _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; - _cleanup_free_ char *new_path = NULL, *nn = NULL; - _cleanup_strv_free_ char **settings = NULL; - unsigned file_attr = 0; - char **j; - int r; - - assert(i); - - if (!image_name_is_valid(new_name)) - return -EINVAL; - - if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) - return -EROFS; - - settings = image_settings_path(i); - if (!settings) - return -ENOMEM; - - /* Make sure we don't interfere with a running nspawn */ - r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); - if (r < 0) - return r; - - /* Make sure nobody takes the new name, between the time we - * checked it is currently unused in all search paths, and the - * time we take possession of it */ - r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); - if (r < 0) - return r; - - r = image_find(new_name, NULL); - if (r < 0) - return r; - if (r > 0) - return -EEXIST; - - switch (i->type) { - - case IMAGE_DIRECTORY: - /* Turn of the immutable bit while we rename the image, so that we can rename it */ - (void) read_attr_path(i->path, &file_attr); - - if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); - - /* fall through */ - - case IMAGE_SUBVOLUME: - new_path = file_in_same_dir(i->path, new_name); - break; - - case IMAGE_RAW: { - const char *fn; - - fn = strjoina(new_name, ".raw"); - new_path = file_in_same_dir(i->path, fn); - break; - } - - default: - return -EOPNOTSUPP; - } - - if (!new_path) - return -ENOMEM; - - nn = strdup(new_name); - if (!nn) - return -ENOMEM; - - r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path); - if (r < 0) - return r; - - /* Restore the immutable bit, if it was set before */ - if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); - - free(i->path); - i->path = new_path; - new_path = NULL; - - free(i->name); - i->name = nn; - nn = NULL; - - STRV_FOREACH(j, settings) { - r = rename_settings_file(*j, new_name); - if (r < 0 && r != -ENOENT) - log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); - } - - return 0; -} - -static int clone_settings_file(const char *path, const char *new_name) { - _cleanup_free_ char *rs = NULL; - const char *fn; - - fn = strjoina(new_name, ".nspawn"); - - rs = file_in_same_dir(path, fn); - if (!rs) - return -ENOMEM; - - return copy_file_atomic(path, rs, 0664, false, 0); -} - -int image_clone(Image *i, const char *new_name, bool read_only) { - _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; - _cleanup_strv_free_ char **settings = NULL; - const char *new_path; - char **j; - int r; - - assert(i); - - if (!image_name_is_valid(new_name)) - return -EINVAL; - - settings = image_settings_path(i); - if (!settings) - return -ENOMEM; - - /* Make sure nobody takes the new name, between the time we - * checked it is currently unused in all search paths, and the - * time we take possession of it */ - r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); - if (r < 0) - return r; - - r = image_find(new_name, NULL); - if (r < 0) - return r; - if (r > 0) - return -EEXIST; - - switch (i->type) { - - case IMAGE_SUBVOLUME: - case IMAGE_DIRECTORY: - /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain - * 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 | BTRFS_SNAPSHOT_QUOTA); - if (r == -EOPNOTSUPP) { - /* No btrfs snapshots supported, create a normal directory then. */ - - r = copy_directory(i->path, new_path, false); - if (r >= 0) - (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); - } else if (r >= 0) - /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ - (void) btrfs_subvol_auto_qgroup(new_path, 0, true); - - break; - - case IMAGE_RAW: - new_path = strjoina("/var/lib/machines/", new_name, ".raw"); - - r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL); - break; - - default: - return -EOPNOTSUPP; - } - - if (r < 0) - return r; - - STRV_FOREACH(j, settings) { - r = clone_settings_file(*j, new_name); - if (r < 0 && r != -ENOENT) - log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); - } - - return 0; -} - -int image_read_only(Image *i, bool b) { - _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; - int r; - assert(i); - - if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) - return -EROFS; - - /* Make sure we don't interfere with a running nspawn */ - r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); - if (r < 0) - return r; - - 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; - - break; - - case IMAGE_DIRECTORY: - /* For simple directory trees we cannot use the access - mode of the top-level directory, since it has an - effect on the container itself. However, we can - use the "immutable" flag, to at least make the - top-level directory read-only. It's not as good as - a read-only subvolume, but at least something, and - we can read the value back.*/ - - r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); - if (r < 0) - return r; - - break; - - case IMAGE_RAW: { - struct stat st; - - if (stat(i->path, &st) < 0) - return -errno; - - if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0) - return -errno; - - /* If the images is now read-only, it's a good time to - * defrag it, given that no write patterns will - * fragment it again. */ - if (b) - (void) btrfs_defrag(i->path); - break; - } - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local) { - _cleanup_free_ char *p = NULL; - LockFile t = LOCK_FILE_INIT; - struct stat st; - int r; - - assert(path); - assert(global); - assert(local); - - /* Locks an image path. This actually creates two locks: one - * "local" one, next to the image path itself, which might be - * shared via NFS. And another "global" one, in /run, that - * uses the device/inode number. This has the benefit that we - * can even lock a tree that is a mount point, correctly. */ - - if (path_equal(path, "/")) - return -EBUSY; - - if (!path_is_absolute(path)) - return -EINVAL; - - if (stat(path, &st) >= 0) { - if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) - return -ENOMEM; - } - - r = make_lock_file_for(path, operation, &t); - if (r < 0) - return r; - - if (p) { - mkdir_p("/run/systemd/nspawn/locks", 0700); - - r = make_lock_file(p, operation, global); - if (r < 0) { - release_lock_file(&t); - return r; - } - } - - *local = t; - return 0; -} - -int image_set_limit(Image *i, uint64_t referenced_max) { - assert(i); - - if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) - return -EROFS; - - if (i->type != IMAGE_SUBVOLUME) - return -EOPNOTSUPP; - - /* 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) { - const char *p; - - assert(name); - assert(ret); - - /* Locks an image name, regardless of the precise path used. */ - - if (!image_name_is_valid(name)) - return -EINVAL; - - if (streq(name, ".host")) - return -EBUSY; - - mkdir_p("/run/systemd/nspawn/locks", 0700); - p = strjoina("/run/systemd/nspawn/locks/name-", name); - - return make_lock_file(p, operation, ret); -} - -bool image_name_is_valid(const char *s) { - if (!filename_is_valid(s)) - return false; - - if (string_has_cc(s, NULL)) - return false; - - if (!utf8_is_valid(s)) - return false; - - /* Temporary files for atomically creating new files */ - if (startswith(s, ".#")) - return false; - - return true; -} - -static const char* const image_type_table[_IMAGE_TYPE_MAX] = { - [IMAGE_DIRECTORY] = "directory", - [IMAGE_SUBVOLUME] = "subvolume", - [IMAGE_RAW] = "raw", -}; - -DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h deleted file mode 100644 index 7410168c4f..0000000000 --- a/src/shared/machine-image.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "hashmap.h" -#include "lockfile-util.h" -#include "macro.h" -#include "path-util.h" -#include "string-util.h" -#include "time-util.h" - -typedef enum ImageType { - IMAGE_DIRECTORY, - IMAGE_SUBVOLUME, - IMAGE_RAW, - _IMAGE_TYPE_MAX, - _IMAGE_TYPE_INVALID = -1 -} ImageType; - -typedef struct Image { - ImageType type; - char *name; - char *path; - bool read_only; - - usec_t crtime; - usec_t mtime; - - uint64_t usage; - uint64_t usage_exclusive; - uint64_t limit; - uint64_t limit_exclusive; - - void *userdata; -} Image; - -Image *image_unref(Image *i); -void image_hashmap_free(Hashmap *map); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free); - -int image_find(const char *name, Image **ret); -int image_discover(Hashmap *map); - -int image_remove(Image *i); -int image_rename(Image *i, const char *new_name); -int image_clone(Image *i, const char *new_name, bool read_only); -int image_read_only(Image *i, bool b); - -const char* image_type_to_string(ImageType t) _const_; -ImageType image_type_from_string(const char *s) _pure_; - -bool image_name_is_valid(const char *s) _pure_; - -int image_path_lock(const char *path, int operation, LockFile *global, LockFile *local); -int image_name_lock(const char *name, int operation, LockFile *ret); - -int image_set_limit(Image *i, uint64_t referenced_max); - -static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { - assert(i); - - return i->name && i->name[0] == '.'; -} - -static inline bool IMAGE_IS_VENDOR(const struct Image *i) { - assert(i); - - return i->path && path_startswith(i->path, "/usr"); -} - -static inline bool IMAGE_IS_HOST(const struct Image *i) { - assert(i); - - if (i->name && streq(i->name, ".host")) - return true; - - if (i->path && path_equal(i->path, "/")) - return true; - - return false; -} diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c deleted file mode 100644 index 23890c63a0..0000000000 --- a/src/shared/machine-pool.c +++ /dev/null @@ -1,426 +0,0 @@ -/*** - 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 . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "mount-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.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) - -static int check_btrfs(void) { - struct statfs sfs; - - if (statfs("/var/lib/machines", &sfs) < 0) { - if (errno != ENOENT) - return -errno; - - if (statfs("/var/lib", &sfs) < 0) - return -errno; - } - - return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); -} - -static int setup_machine_raw(uint64_t size, sd_bus_error *error) { - _cleanup_free_ char *tmp = NULL; - _cleanup_close_ int fd = -1; - struct statvfs ss; - pid_t pid = 0; - siginfo_t si; - int r; - - /* We want to be able to make use of btrfs-specific file - * system features, in particular subvolumes, reflinks and - * quota. Hence, if we detect that /var/lib/machines.raw is - * not located on btrfs, let's create a loopback file, place a - * btrfs file system into it, and mount it to - * /var/lib/machines. */ - - fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (fd >= 0) { - r = fd; - fd = -1; - return r; - } - - if (errno != ENOENT) - return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m"); - - r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp); - if (r < 0) - return r; - - (void) mkdir_p_label("/var/lib", 0755); - fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600); - if (fd < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m"); - - if (fstatvfs(fd, &ss) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m"); - goto fail; - } - - if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines."); - goto fail; - } - - if (ftruncate(fd, size) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m"); - goto fail; - } - - pid = fork(); - if (pid < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m"); - goto fail; - } - - if (pid == 0) { - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - fd = safe_close(fd); - - execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); - if (errno == ENOENT) - _exit(99); - - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate(pid, &si); - if (r < 0) { - sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); - goto fail; - } - - pid = 0; - - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); - goto fail; - } - if (si.si_status == 99) { - r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); - goto fail; - } - if (si.si_status != 0) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); - goto fail; - } - - r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw"); - if (r < 0) { - sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m"); - goto fail; - } - - r = fd; - fd = -1; - - return r; - -fail: - unlink_noerrno(tmp); - - if (pid > 1) - kill_and_sigcont(pid, SIGKILL); - - return r; -} - -int setup_machine_directory(uint64_t size, sd_bus_error *error) { - _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT; - struct loop_info64 info = { - .lo_flags = LO_FLAGS_AUTOCLEAR, - }; - _cleanup_close_ int fd = -1, control = -1, loop = -1; - _cleanup_free_ char* loopdev = NULL; - char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL; - bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false; - char buf[FORMAT_BYTES_MAX]; - int r, nr = -1; - - /* btrfs cannot handle file systems < 16M, hence use this as minimum */ - if (size == (uint64_t) -1) - size = VAR_LIB_MACHINES_SIZE_START; - else if (size < 16*1024*1024) - size = 16*1024*1024; - - /* Make sure we only set the directory up once at a time */ - r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file); - if (r < 0) - return r; - - r = check_btrfs(); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m"); - if (r > 0) { - (void) btrfs_subvol_make_label("/var/lib/machines"); - - r = btrfs_quota_enable("/var/lib/machines", true); - if (r < 0) - log_warning_errno(r, "Failed to enable quota 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; - } - - r = mkfs_exists("btrfs"); - if (r == 0) - return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); - if (r < 0) - return r; - - fd = setup_machine_raw(size, error); - if (fd < 0) - return fd; - - control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (control < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m"); - - nr = ioctl(control, LOOP_CTL_GET_FREE); - if (nr < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m"); - - if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) { - r = -ENOMEM; - goto fail; - } - - loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK); - if (loop < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m"); - goto fail; - } - - if (ioctl(loop, LOOP_SET_FD, fd) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m"); - goto fail; - } - - if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m"); - goto fail; - } - - /* We need to make sure the new /var/lib/machines directory - * has an access mode of 0700 at the time it is first made - * available. mkfs will create it with 0755 however. Hence, - * let's mount the directory into an inaccessible directory - * below /tmp first, fix the access mode, and move it to the - * public place then. */ - - if (!mkdtemp(tmpdir)) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m"); - goto fail; - } - tmpdir_made = true; - - mntdir = strjoina(tmpdir, "/mnt"); - if (mkdir(mntdir, 0700) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m"); - goto fail; - } - mntdir_made = true; - - if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m"); - goto fail; - } - mntdir_mounted = true; - - r = btrfs_quota_enable(mntdir, true); - if (r < 0) - log_warning_errno(r, "Failed to enable quota, ignoring: %m"); - - 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; - } - - (void) mkdir_p_label("/var/lib/machines", 0700); - - if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m"); - goto fail; - } - - (void) syncfs(fd); - - log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size)); - - (void) umount2(mntdir, MNT_DETACH); - (void) rmdir(mntdir); - (void) rmdir(tmpdir); - - return 1; - -fail: - if (mntdir_mounted) - (void) umount2(mntdir, MNT_DETACH); - - if (mntdir_made) - (void) rmdir(mntdir); - if (tmpdir_made) - (void) rmdir(tmpdir); - - if (loop >= 0) { - (void) ioctl(loop, LOOP_CLR_FD); - loop = safe_close(loop); - } - - if (control >= 0 && nr >= 0) - (void) ioctl(control, LOOP_CTL_REMOVE, nr); - - return r; -} - -static int sync_path(const char *p) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - if (syncfs(fd) < 0) - return -errno; - - return 0; -} - -int grow_machine_directory(void) { - char buf[FORMAT_BYTES_MAX]; - struct statvfs a, b; - uint64_t old_size, new_size, max_add; - int r; - - /* Ensure the disk space data is accurate */ - sync_path("/var/lib/machines"); - sync_path("/var/lib/machines.raw"); - - if (statvfs("/var/lib/machines.raw", &a) < 0) - return -errno; - - if (statvfs("/var/lib/machines", &b) < 0) - return -errno; - - /* Don't grow if not enough disk space is available on the host */ - if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN) - return 0; - - /* Don't grow if at least 1/3th of the fs is still free */ - if (b.f_bavail > b.f_blocks / 3) - return 0; - - /* Calculate how much we are willing to add at most */ - max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN; - - /* Calculate the old size */ - old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize; - - /* Calculate the new size as three times the size of what is used right now */ - new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3; - - /* Always, grow at least to the start size */ - if (new_size < VAR_LIB_MACHINES_SIZE_START) - new_size = VAR_LIB_MACHINES_SIZE_START; - - /* If the new size is smaller than the old size, don't grow */ - if (new_size < old_size) - return 0; - - /* Ensure we never add more than the maximum */ - if (new_size > old_size + max_add) - new_size = old_size + max_add; - - r = btrfs_resize_loopback("/var/lib/machines", new_size, true); - if (r <= 0) - return r; - - /* 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 deleted file mode 100644 index 40fe5ecb3a..0000000000 --- a/src/shared/machine-pool.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include - -#include "sd-bus.h" - -/* Grow the /var/lib/machines directory after each 10MiB written */ -#define GROW_INTERVAL_BYTES (UINT64_C(10) * UINT64_C(1024) * UINT64_C(1024)) - -int setup_machine_directory(uint64_t size, sd_bus_error *error); -int grow_machine_directory(void); diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c deleted file mode 100644 index bec53ee0ae..0000000000 --- a/src/shared/output-mode.c +++ /dev/null @@ -1,37 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "output-mode.h" -#include "string-table.h" - -static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { - [OUTPUT_SHORT] = "short", - [OUTPUT_SHORT_ISO] = "short-iso", - [OUTPUT_SHORT_PRECISE] = "short-precise", - [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", - [OUTPUT_SHORT_UNIX] = "short-unix", - [OUTPUT_VERBOSE] = "verbose", - [OUTPUT_EXPORT] = "export", - [OUTPUT_JSON] = "json", - [OUTPUT_JSON_PRETTY] = "json-pretty", - [OUTPUT_JSON_SSE] = "json-sse", - [OUTPUT_CAT] = "cat" -}; - -DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h deleted file mode 100644 index f37189e57f..0000000000 --- a/src/shared/output-mode.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "macro.h" - -typedef enum OutputMode { - OUTPUT_SHORT, - OUTPUT_SHORT_ISO, - OUTPUT_SHORT_PRECISE, - OUTPUT_SHORT_MONOTONIC, - OUTPUT_SHORT_UNIX, - OUTPUT_VERBOSE, - OUTPUT_EXPORT, - OUTPUT_JSON, - OUTPUT_JSON_PRETTY, - OUTPUT_JSON_SSE, - OUTPUT_CAT, - _OUTPUT_MODE_MAX, - _OUTPUT_MODE_INVALID = -1 -} OutputMode; - -/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the - * logs output, others only to the process tree output. */ - -typedef enum OutputFlags { - OUTPUT_SHOW_ALL = 1 << 0, - OUTPUT_FOLLOW = 1 << 1, - OUTPUT_WARN_CUTOFF = 1 << 2, - OUTPUT_FULL_WIDTH = 1 << 3, - OUTPUT_COLOR = 1 << 4, - OUTPUT_CATALOG = 1 << 5, - OUTPUT_BEGIN_NEWLINE = 1 << 6, - OUTPUT_UTC = 1 << 7, - OUTPUT_KERNEL_THREADS = 1 << 8, - OUTPUT_NO_HOSTNAME = 1 << 9, -} OutputFlags; - -const char* output_mode_to_string(OutputMode m) _const_; -OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/pager.c b/src/shared/pager.c deleted file mode 100644 index a2524d4420..0000000000 --- a/src/shared/pager.c +++ /dev/null @@ -1,226 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "copy.h" -#include "fd-util.h" -#include "locale-util.h" -#include "log.h" -#include "macro.h" -#include "pager.h" -#include "process-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "terminal-util.h" - -static pid_t pager_pid = 0; - -noreturn static void pager_fallback(void) { - int r; - - r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false); - if (r < 0) { - log_error_errno(r, "Internal pager failed: %m"); - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); -} - -int pager_open(bool no_pager, bool jump_to_end) { - _cleanup_close_pair_ int fd[2] = { -1, -1 }; - const char *pager; - pid_t parent_pid; - - if (no_pager) - return 0; - - if (pager_pid > 0) - return 1; - - if (terminal_is_dumb()) - 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 */ - (void) columns(); - - if (pipe(fd) < 0) - return log_error_errno(errno, "Failed to create pager pipe: %m"); - - parent_pid = getpid(); - - pager_pid = fork(); - if (pager_pid < 0) - return log_error_errno(errno, "Failed to fork pager: %m"); - - /* In the child start the pager */ - if (pager_pid == 0) { - const char* less_opts, *less_charset; - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - (void) dup2(fd[0], STDIN_FILENO); - safe_close_pair(fd); - - /* Initialize a good set of less options */ - less_opts = getenv("SYSTEMD_LESS"); - if (!less_opts) - less_opts = "FRSXMK"; - if (jump_to_end) - less_opts = strjoina(less_opts, " +G"); - setenv("LESS", less_opts, 1); - - /* Initialize a good charset for less. This is - * particularly important if we output UTF-8 - * characters. */ - less_charset = getenv("SYSTEMD_LESSCHARSET"); - if (!less_charset && is_locale_utf8()) - less_charset = "utf-8"; - if (less_charset) - setenv("LESSCHARSET", less_charset, 1); - - /* Make sure the pager goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - if (pager) { - execlp(pager, pager, NULL); - execl("/bin/sh", "sh", "-c", pager, NULL); - } - - /* Debian's alternatives command for pagers is - * called 'pager'. Note that we do not call - * sensible-pagers here, since that is just a - * shell script that implements a logic that - * is similar to this one anyway, but is - * Debian-specific. */ - execlp("pager", "pager", NULL); - - execlp("less", "less", NULL); - execlp("more", "more", NULL); - - pager_fallback(); - /* not reached */ - } - - /* Return in the parent */ - if (dup2(fd[1], STDOUT_FILENO) < 0) - return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); - if (dup2(fd[1], STDERR_FILENO) < 0) - return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); - - return 1; -} - -void pager_close(void) { - - if (pager_pid <= 0) - return; - - /* Inform pager that we are done */ - stdout = safe_fclose(stdout); - stderr = safe_fclose(stderr); - - (void) kill(pager_pid, SIGCONT); - (void) wait_for_terminate(pager_pid, NULL); - pager_pid = 0; -} - -bool pager_have(void) { - return pager_pid > 0; -} - -int show_man_page(const char *desc, bool null_stdio) { - const char *args[4] = { "man", NULL, NULL, NULL }; - char *e = NULL; - pid_t pid; - size_t k; - int r; - siginfo_t status; - - k = strlen(desc); - - if (desc[k-1] == ')') - e = strrchr(desc, '('); - - if (e) { - char *page = NULL, *section = NULL; - - page = strndupa(desc, e - desc); - section = strndupa(e + 1, desc + k - e - 2); - - args[1] = section; - args[2] = page; - } else - args[1] = desc; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - if (null_stdio) { - r = make_null_stdio(); - if (r < 0) { - log_error_errno(r, "Failed to kill stdio: %m"); - _exit(EXIT_FAILURE); - } - } - - execvp(args[0], (char**) args); - log_error_errno(errno, "Failed to execute man: %m"); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate(pid, &status); - if (r < 0) - return r; - - log_debug("Exit code %i status %i", status.si_code, status.si_status); - return status.si_status; -} diff --git a/src/shared/pager.h b/src/shared/pager.h deleted file mode 100644 index 893e1d2bb6..0000000000 --- a/src/shared/pager.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 . -***/ - -#include - -#include "macro.h" - -int pager_open(bool no_pager, bool jump_to_end); -void pager_close(void); -bool pager_have(void) _pure_; - -int show_man_page(const char *page, bool null_stdio); diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c deleted file mode 100644 index 862096ae7b..0000000000 --- a/src/shared/path-lookup.c +++ /dev/null @@ -1,822 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "install.h" -#include "log.h" -#include "macro.h" -#include "mkdir.h" -#include "path-lookup.h" -#include "path-util.h" -#include "rm-rf.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static int user_runtime_dir(char **ret, const char *suffix) { - const char *e; - char *j; - - assert(ret); - assert(suffix); - - e = getenv("XDG_RUNTIME_DIR"); - if (!e) - return -ENXIO; - - j = strappend(e, suffix); - if (!j) - return -ENOMEM; - - *ret = j; - return 0; -} - -static int user_config_dir(char **ret, const char *suffix) { - const char *e; - char *j; - - assert(ret); - - e = getenv("XDG_CONFIG_HOME"); - if (e) - j = strappend(e, suffix); - else { - const char *home; - - home = getenv("HOME"); - if (!home) - return -ENXIO; - - j = strjoin(home, "/.config", suffix, NULL); - } - - if (!j) - return -ENOMEM; - - *ret = j; - return 0; -} - -static int user_data_dir(char **ret, const char *suffix) { - const char *e; - char *j; - - assert(ret); - assert(suffix); - - /* We don't treat /etc/xdg/systemd here as the spec - * suggests because we assume that is a link to - * /etc/systemd/ anyway. */ - - e = getenv("XDG_DATA_HOME"); - if (e) - j = strappend(e, suffix); - else { - const char *home; - - home = getenv("HOME"); - if (!home) - return -ENXIO; - - - j = strjoin(home, "/.local/share", suffix, NULL); - } - if (!j) - return -ENOMEM; - - *ret = j; - return 1; -} - -static char** user_dirs( - const char *persistent_config, - const char *runtime_config, - const char *generator, - const char *generator_early, - const char *generator_late, - const char *transient, - const char *persistent_control, - const char *runtime_control) { - - const char * const config_unit_paths[] = { - USER_CONFIG_UNIT_PATH, - "/etc/systemd/user", - NULL - }; - - const char * const data_unit_paths[] = { - "/usr/local/lib/systemd/user", - "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, - "/usr/lib/systemd/user", - "/usr/share/systemd/user", - NULL - }; - - const char *e; - _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; - _cleanup_free_ char *data_home = NULL; - _cleanup_free_ char **res = NULL; - char **tmp; - int r; - - /* Implement the mechanisms defined in - * - * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html - * - * We look in both the config and the data dirs because we - * want to encourage that distributors ship their unit files - * as data, and allow overriding as configuration. - */ - - e = getenv("XDG_CONFIG_DIRS"); - if (e) { - config_dirs = strv_split(e, ":"); - if (!config_dirs) - return NULL; - } - - r = user_data_dir(&data_home, "/systemd/user"); - if (r < 0 && r != -ENXIO) - return NULL; - - e = getenv("XDG_DATA_DIRS"); - if (e) - data_dirs = strv_split(e, ":"); - else - data_dirs = strv_new("/usr/local/share", - "/usr/share", - NULL); - if (!data_dirs) - return NULL; - - /* Now merge everything we found. */ - if (strv_extend(&res, persistent_control) < 0) - return NULL; - - if (strv_extend(&res, runtime_control) < 0) - return NULL; - - if (strv_extend(&res, transient) < 0) - return NULL; - - if (strv_extend(&res, generator_early) < 0) - return NULL; - - if (!strv_isempty(config_dirs)) - if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) - return NULL; - - if (strv_extend(&res, persistent_config) < 0) - return NULL; - - if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) - return NULL; - - if (strv_extend(&res, runtime_config) < 0) - return NULL; - - if (strv_extend(&res, generator) < 0) - return NULL; - - if (strv_extend(&res, data_home) < 0) - return NULL; - - if (!strv_isempty(data_dirs)) - if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) - return NULL; - - if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) - return NULL; - - if (strv_extend(&res, generator_late) < 0) - return NULL; - - if (path_strv_make_absolute_cwd(res) < 0) - return NULL; - - tmp = res; - res = NULL; - return tmp; -} - -static int acquire_generator_dirs( - UnitFileScope scope, - char **generator, - char **generator_early, - char **generator_late) { - - _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; - const char *prefix; - - assert(generator); - assert(generator_early); - assert(generator_late); - - switch (scope) { - - case UNIT_FILE_SYSTEM: - prefix = "/run/systemd/"; - break; - - case UNIT_FILE_USER: { - const char *e; - - e = getenv("XDG_RUNTIME_DIR"); - if (!e) - return -ENXIO; - - prefix = strjoina(e, "/systemd/"); - break; - } - - case UNIT_FILE_GLOBAL: - return -EOPNOTSUPP; - - default: - assert_not_reached("Hmm, unexpected scope value."); - } - - x = strappend(prefix, "generator"); - if (!x) - return -ENOMEM; - - y = strappend(prefix, "generator.early"); - if (!y) - return -ENOMEM; - - z = strappend(prefix, "generator.late"); - if (!z) - return -ENOMEM; - - *generator = x; - *generator_early = y; - *generator_late = z; - - x = y = z = NULL; - return 0; -} - -static int acquire_transient_dir(UnitFileScope scope, char **ret) { - assert(ret); - - switch (scope) { - - case UNIT_FILE_SYSTEM: { - char *transient; - - transient = strdup("/run/systemd/transient"); - if (!transient) - return -ENOMEM; - - *ret = transient; - return 0; - } - - case UNIT_FILE_USER: - return user_runtime_dir(ret, "/systemd/transient"); - - case UNIT_FILE_GLOBAL: - return -EOPNOTSUPP; - - default: - assert_not_reached("Hmm, unexpected scope value."); - } -} - -static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { - _cleanup_free_ char *a = NULL, *b = NULL; - int r; - - assert(persistent); - assert(runtime); - - switch (scope) { - - case UNIT_FILE_SYSTEM: - a = strdup(SYSTEM_CONFIG_UNIT_PATH); - b = strdup("/run/systemd/system"); - break; - - case UNIT_FILE_GLOBAL: - a = strdup(USER_CONFIG_UNIT_PATH); - b = strdup("/run/systemd/user"); - break; - - case UNIT_FILE_USER: - r = user_config_dir(&a, "/systemd/user"); - if (r < 0) - return r; - - r = user_runtime_dir(runtime, "/systemd/user"); - if (r < 0) - return r; - - *persistent = a; - a = NULL; - - return 0; - - default: - assert_not_reached("Hmm, unexpected scope value."); - } - - if (!a || !b) - return -ENOMEM; - - *persistent = a; - *runtime = b; - a = b = NULL; - - return 0; -} - -static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { - _cleanup_free_ char *a = NULL; - int r; - - assert(persistent); - assert(runtime); - - switch (scope) { - - case UNIT_FILE_SYSTEM: { - _cleanup_free_ char *b = NULL; - - a = strdup("/etc/systemd/system.control"); - if (!a) - return -ENOMEM; - - b = strdup("/run/systemd/system.control"); - if (!b) - return -ENOMEM; - - *runtime = b; - b = NULL; - - break; - } - - case UNIT_FILE_USER: - r = user_config_dir(&a, "/systemd/system.control"); - if (r < 0) - return r; - - r = user_runtime_dir(runtime, "/systemd/system.control"); - if (r < 0) - return r; - - break; - - case UNIT_FILE_GLOBAL: - return -EOPNOTSUPP; - - default: - assert_not_reached("Hmm, unexpected scope value."); - } - - *persistent = a; - a = NULL; - - return 0; -} - -static int patch_root_prefix(char **p, const char *root_dir) { - char *c; - - assert(p); - - if (!*p) - return 0; - - c = prefix_root(root_dir, *p); - if (!c) - return -ENOMEM; - - free(*p); - *p = c; - - return 0; -} - -static int patch_root_prefix_strv(char **l, const char *root_dir) { - char **i; - int r; - - if (!root_dir) - return 0; - - STRV_FOREACH(i, l) { - r = patch_root_prefix(i, root_dir); - if (r < 0) - return r; - } - - return 0; -} - -int lookup_paths_init( - LookupPaths *p, - UnitFileScope scope, - LookupPathsFlags flags, - const char *root_dir) { - - _cleanup_free_ char - *root = NULL, - *persistent_config = NULL, *runtime_config = NULL, - *generator = NULL, *generator_early = NULL, *generator_late = NULL, - *transient = NULL, - *persistent_control = NULL, *runtime_control = NULL; - bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ - _cleanup_strv_free_ char **paths = NULL; - const char *e; - int r; - - assert(p); - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - if (!isempty(root_dir) && !path_equal(root_dir, "/")) { - if (scope == UNIT_FILE_USER) - return -EINVAL; - - r = is_dir(root_dir, true); - if (r < 0) - return r; - if (r == 0) - return -ENOTDIR; - - root = strdup(root_dir); - if (!root) - return -ENOMEM; - } - - r = acquire_config_dirs(scope, &persistent_config, &runtime_config); - if (r < 0 && r != -ENXIO) - return r; - - if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { - r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); - if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) - return r; - } - - r = acquire_transient_dir(scope, &transient); - if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) - return r; - - r = acquire_control_dirs(scope, &persistent_control, &runtime_control); - if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) - return r; - - /* First priority is whatever has been passed to us via env vars */ - e = getenv("SYSTEMD_UNIT_PATH"); - if (e) { - const char *k; - - k = endswith(e, ":"); - if (k) { - e = strndupa(e, k - e); - append = true; - } - - /* FIXME: empty components in other places should be - * rejected. */ - - r = path_split_and_make_absolute(e, &paths); - if (r < 0) - return r; - } - - if (!paths || append) { - /* Let's figure something out. */ - - _cleanup_strv_free_ char **add = NULL; - - /* For the user units we include share/ in the search - * path in order to comply with the XDG basedir spec. - * For the system stuff we avoid such nonsense. OTOH - * we include /lib in the search path for the system - * stuff but avoid it for user stuff. */ - - switch (scope) { - - case UNIT_FILE_SYSTEM: - add = strv_new( - /* If you modify this you also want to modify - * systemdsystemunitpath= in systemd.pc.in! */ - STRV_IFNOTNULL(persistent_control), - STRV_IFNOTNULL(runtime_control), - STRV_IFNOTNULL(transient), - STRV_IFNOTNULL(generator_early), - persistent_config, - SYSTEM_CONFIG_UNIT_PATH, - "/etc/systemd/system", - runtime_config, - "/run/systemd/system", - STRV_IFNOTNULL(generator), - "/usr/local/lib/systemd/system", - SYSTEM_DATA_UNIT_PATH, - "/usr/lib/systemd/system", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system", -#endif - STRV_IFNOTNULL(generator_late), - NULL); - break; - - case UNIT_FILE_GLOBAL: - add = strv_new( - /* If you modify this you also want to modify - * systemduserunitpath= in systemd.pc.in, and - * the arrays in user_dirs() above! */ - STRV_IFNOTNULL(persistent_control), - STRV_IFNOTNULL(runtime_control), - STRV_IFNOTNULL(transient), - STRV_IFNOTNULL(generator_early), - persistent_config, - USER_CONFIG_UNIT_PATH, - "/etc/systemd/user", - runtime_config, - "/run/systemd/user", - STRV_IFNOTNULL(generator), - "/usr/local/lib/systemd/user", - "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, - "/usr/lib/systemd/user", - "/usr/share/systemd/user", - STRV_IFNOTNULL(generator_late), - NULL); - break; - - case UNIT_FILE_USER: - add = user_dirs(persistent_config, runtime_config, - generator, generator_early, generator_late, - transient, - persistent_config, runtime_control); - break; - - default: - assert_not_reached("Hmm, unexpected scope?"); - } - - if (!add) - return -ENOMEM; - - if (paths) { - r = strv_extend_strv(&paths, add, true); - if (r < 0) - return r; - } else { - /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, - * and don't have to copy anything */ - paths = add; - add = NULL; - } - } - - r = patch_root_prefix(&persistent_config, root); - if (r < 0) - return r; - r = patch_root_prefix(&runtime_config, root); - if (r < 0) - return r; - - r = patch_root_prefix(&generator, root); - if (r < 0) - return r; - r = patch_root_prefix(&generator_early, root); - if (r < 0) - return r; - r = patch_root_prefix(&generator_late, root); - if (r < 0) - return r; - - r = patch_root_prefix(&transient, root); - if (r < 0) - return r; - - r = patch_root_prefix(&persistent_control, root); - if (r < 0) - return r; - - r = patch_root_prefix(&runtime_control, root); - if (r < 0) - return r; - - r = patch_root_prefix_strv(paths, root); - if (r < 0) - return -ENOMEM; - - p->search_path = strv_uniq(paths); - paths = NULL; - - p->persistent_config = persistent_config; - p->runtime_config = runtime_config; - persistent_config = runtime_config = NULL; - - p->generator = generator; - p->generator_early = generator_early; - p->generator_late = generator_late; - generator = generator_early = generator_late = NULL; - - p->transient = transient; - transient = NULL; - - p->persistent_control = persistent_control; - p->runtime_control = runtime_control; - persistent_control = runtime_control = NULL; - - p->root_dir = root; - root = NULL; - - return 0; -} - -void lookup_paths_free(LookupPaths *p) { - if (!p) - return; - - p->search_path = strv_free(p->search_path); - - p->persistent_config = mfree(p->persistent_config); - p->runtime_config = mfree(p->runtime_config); - - p->generator = mfree(p->generator); - p->generator_early = mfree(p->generator_early); - p->generator_late = mfree(p->generator_late); - - p->transient = mfree(p->transient); - - p->persistent_control = mfree(p->persistent_control); - p->runtime_control = mfree(p->runtime_control); - - p->root_dir = mfree(p->root_dir); -} - -int lookup_paths_reduce(LookupPaths *p) { - _cleanup_free_ struct stat *stats = NULL; - size_t n_stats = 0, allocated = 0; - unsigned c = 0; - int r; - - assert(p); - - /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are - * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set, - * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into - * account when following symlinks. When we have no root path set this restriction does not apply however. */ - - if (!p->search_path) - return 0; - - while (p->search_path[c]) { - struct stat st; - unsigned k; - - if (p->root_dir) - r = lstat(p->search_path[c], &st); - else - r = stat(p->search_path[c], &st); - if (r < 0) { - if (errno == ENOENT) - goto remove_item; - - /* If something we don't grok happened, let's better leave it in. */ - log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]); - c++; - continue; - } - - for (k = 0; k < n_stats; k++) { - if (stats[k].st_dev == st.st_dev && - stats[k].st_ino == st.st_ino) - break; - } - - if (k < n_stats) /* Is there already an entry with the same device/inode? */ - goto remove_item; - - if (!GREEDY_REALLOC(stats, allocated, n_stats+1)) - return -ENOMEM; - - stats[n_stats++] = st; - c++; - continue; - - remove_item: - free(p->search_path[c]); - memmove(p->search_path + c, - p->search_path + c + 1, - (strv_length(p->search_path + c + 1) + 1) * sizeof(char*)); - } - - if (strv_isempty(p->search_path)) { - log_debug("Ignoring unit files."); - p->search_path = strv_free(p->search_path); - } else { - _cleanup_free_ char *t; - - t = strv_join(p->search_path, "\n\t"); - if (!t) - return -ENOMEM; - - log_debug("Looking for unit files in (higher priority first):\n\t%s", t); - } - - return 0; -} - -int lookup_paths_mkdir_generator(LookupPaths *p) { - int r, q; - - assert(p); - - if (!p->generator || !p->generator_early || !p->generator_late) - return -EINVAL; - - r = mkdir_p_label(p->generator, 0755); - - q = mkdir_p_label(p->generator_early, 0755); - if (q < 0 && r >= 0) - r = q; - - q = mkdir_p_label(p->generator_late, 0755); - if (q < 0 && r >= 0) - r = q; - - return r; -} - -void lookup_paths_trim_generator(LookupPaths *p) { - assert(p); - - /* Trim empty dirs */ - - if (p->generator) - (void) rmdir(p->generator); - if (p->generator_early) - (void) rmdir(p->generator_early); - if (p->generator_late) - (void) rmdir(p->generator_late); -} - -void lookup_paths_flush_generator(LookupPaths *p) { - assert(p); - - /* Flush the generated unit files in full */ - - if (p->generator) - (void) rm_rf(p->generator, REMOVE_ROOT); - if (p->generator_early) - (void) rm_rf(p->generator_early, REMOVE_ROOT); - if (p->generator_late) - (void) rm_rf(p->generator_late, REMOVE_ROOT); -} - -char **generator_binary_paths(UnitFileScope scope) { - - switch (scope) { - - case UNIT_FILE_SYSTEM: - return strv_new("/run/systemd/system-generators", - "/etc/systemd/system-generators", - "/usr/local/lib/systemd/system-generators", - SYSTEM_GENERATOR_PATH, - NULL); - - case UNIT_FILE_GLOBAL: - case UNIT_FILE_USER: - return strv_new("/run/systemd/user-generators", - "/etc/systemd/user-generators", - "/usr/local/lib/systemd/user-generators", - USER_GENERATOR_PATH, - NULL); - - default: - assert_not_reached("Hmm, unexpected scope."); - } -} diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h deleted file mode 100644 index f9bb2fe237..0000000000 --- a/src/shared/path-lookup.h +++ /dev/null @@ -1,76 +0,0 @@ -#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 . -***/ - -#include - -typedef struct LookupPaths LookupPaths; - -#include "install.h" -#include "macro.h" - -typedef enum LookupPathsFlags { - LOOKUP_PATHS_EXCLUDE_GENERATED = 1, -} LookupPathsFlags; - -struct LookupPaths { - /* Where we look for unit files. This includes the individual special paths below, but also any vendor - * supplied, static unit file paths. */ - char **search_path; - - /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin - * shall place his own unit files. */ - char *persistent_config; - char *runtime_config; - - /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of - * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should - * not alter these directories directly. */ - char *generator; - char *generator_early; - char *generator_late; - - /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special - * semantics of this directory: all units created transiently have their unit files removed as the transient - * unit is unloaded. The user should not alter this directory directly. */ - char *transient; - - /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the - * snippets are placed in the transient directory though (see above). The user should not alter this directory - * directly. */ - char *persistent_control; - char *runtime_control; - - /* The root directory prepended to all items above, or NULL */ - char *root_dir; -}; - -int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); - -int lookup_paths_reduce(LookupPaths *p); - -int lookup_paths_mkdir_generator(LookupPaths *p); -void lookup_paths_trim_generator(LookupPaths *p); -void lookup_paths_flush_generator(LookupPaths *p); - -void lookup_paths_free(LookupPaths *p); -#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free) - -char **generator_binary_paths(UnitFileScope scope); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c deleted file mode 100644 index 02c03b98d8..0000000000 --- a/src/shared/ptyfwd.c +++ /dev/null @@ -1,484 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - - sd_event_source *sigwinch_event_source; - - struct termios saved_stdin_attr; - struct termios saved_stdout_attr; - - bool saved_stdin:1; - bool saved_stdout:1; - - bool stdin_readable:1; - bool stdin_hangup:1; - bool stdout_writable:1; - bool stdout_hangup:1; - bool master_readable:1; - bool master_writable:1; - bool master_hangup:1; - - bool read_from_master:1; - - bool last_char_set:1; - char last_char; - - char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; - size_t in_buffer_full, out_buffer_full; - - usec_t escape_timestamp; - unsigned escape_counter; -}; - -#define ESCAPE_USEC (1*USEC_PER_SEC) - -static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { - const char *p; - - assert(f); - assert(buffer); - assert(n > 0); - - for (p = buffer; p < buffer + n; p++) { - - /* Check for ^] */ - if (*p == 0x1D) { - usec_t nw = now(CLOCK_MONOTONIC); - - if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) { - f->escape_timestamp = nw; - f->escape_counter = 1; - } else { - (f->escape_counter)++; - - if (f->escape_counter >= 3) - return true; - } - } else { - f->escape_timestamp = 0; - f->escape_counter = 0; - } - } - - 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; - - assert(f); - - while ((f->stdin_readable && f->in_buffer_full <= 0) || - (f->master_writable && f->in_buffer_full > 0) || - (f->master_readable && f->out_buffer_full <= 0) || - (f->stdout_writable && f->out_buffer_full > 0)) { - - if (f->stdin_readable && f->in_buffer_full < LINE_MAX) { - - k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full); - if (k < 0) { - - if (errno == EAGAIN) - f->stdin_readable = false; - else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { - f->stdin_readable = false; - f->stdin_hangup = true; - - f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); - } else { - log_error_errno(errno, "read(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); - } - } else if (k == 0) { - /* EOF on stdin */ - f->stdin_readable = false; - f->stdin_hangup = true; - - f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); - } else { - /* Check if ^] has been - * pressed three times within - * one second. If we get this - * we quite immediately. */ - if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) - return sd_event_exit(f->event, EXIT_FAILURE); - - f->in_buffer_full += (size_t) k; - } - } - - if (f->master_writable && f->in_buffer_full > 0) { - - k = write(f->master, f->in_buffer, f->in_buffer_full); - if (k < 0) { - - if (errno == EAGAIN || errno == EIO) - f->master_writable = false; - else if (errno == EPIPE || errno == ECONNRESET) { - f->master_writable = f->master_readable = false; - f->master_hangup = true; - - f->master_event_source = sd_event_source_unref(f->master_event_source); - } else { - log_error_errno(errno, "write(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); - } - } else { - assert(f->in_buffer_full >= (size_t) k); - memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k); - f->in_buffer_full -= k; - } - } - - if (f->master_readable && f->out_buffer_full < LINE_MAX) { - - k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); - if (k < 0) { - - /* Note that EIO on the master device - * might be caused by vhangup() or - * temporary closing of everything on - * the other side, we treat it like - * EAGAIN here and try again, unless - * ignore_vhangup is off. */ - - 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; - f->master_hangup = true; - - f->master_event_source = sd_event_source_unref(f->master_event_source); - } else { - log_error_errno(errno, "read(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); - } - } else { - f->read_from_master = true; - f->out_buffer_full += (size_t) k; - } - } - - if (f->stdout_writable && f->out_buffer_full > 0) { - - k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full); - if (k < 0) { - - if (errno == EAGAIN) - f->stdout_writable = false; - else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { - f->stdout_writable = false; - f->stdout_hangup = true; - f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); - } else { - log_error_errno(errno, "write(): %m"); - return sd_event_exit(f->event, EXIT_FAILURE); - } - - } else { - - if (k > 0) { - f->last_char = f->out_buffer[k-1]; - f->last_char_set = true; - } - - assert(f->out_buffer_full >= (size_t) k); - memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); - f->out_buffer_full -= k; - } - } - } - - if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) { - /* Exit the loop if any side hung up and if there's - * nothing more to write or nothing we could write. */ - - if ((f->out_buffer_full <= 0 || f->stdout_hangup) && - (f->in_buffer_full <= 0 || f->master_hangup)) - return sd_event_exit(f->event, EXIT_SUCCESS); - } - - return 0; -} - -static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { - PTYForward *f = userdata; - - assert(f); - assert(e); - assert(e == f->master_event_source); - assert(fd >= 0); - assert(fd == f->master); - - if (revents & (EPOLLIN|EPOLLHUP)) - f->master_readable = true; - - if (revents & (EPOLLOUT|EPOLLHUP)) - f->master_writable = true; - - return shovel(f); -} - -static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { - PTYForward *f = userdata; - - assert(f); - assert(e); - assert(e == f->stdin_event_source); - assert(fd >= 0); - assert(fd == STDIN_FILENO); - - if (revents & (EPOLLIN|EPOLLHUP)) - f->stdin_readable = true; - - return shovel(f); -} - -static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { - PTYForward *f = userdata; - - assert(f); - assert(e); - assert(e == f->stdout_event_source); - assert(fd >= 0); - assert(fd == STDOUT_FILENO); - - if (revents & (EPOLLOUT|EPOLLHUP)) - f->stdout_writable = true; - - return shovel(f); -} - -static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) { - PTYForward *f = userdata; - struct winsize ws; - - assert(f); - assert(e); - assert(e == f->sigwinch_event_source); - - /* The window size changed, let's forward that. */ - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) - (void) ioctl(f->master, TIOCSWINSZ, &ws); - - return 0; -} - -int pty_forward_new( - sd_event *event, - int master, - PTYForwardFlags flags, - PTYForward **ret) { - - _cleanup_(pty_forward_freep) PTYForward *f = NULL; - struct winsize ws; - int r; - - f = new0(PTYForward, 1); - if (!f) - return -ENOMEM; - - f->flags = flags; - - if (event) - f->event = sd_event_ref(event); - else { - r = sd_event_default(&f->event); - if (r < 0) - return r; - } - - if (!(flags & PTY_FORWARD_READ_ONLY)) { - r = fd_nonblock(STDIN_FILENO, true); - if (r < 0) - return r; - - r = fd_nonblock(STDOUT_FILENO, true); - if (r < 0) - return r; - } - - r = fd_nonblock(master, true); - if (r < 0) - return r; - - f->master = master; - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) - (void) ioctl(master, TIOCSWINSZ, &ws); - - if (!(flags & PTY_FORWARD_READ_ONLY)) { - if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) { - struct termios raw_stdin_attr; - - f->saved_stdin = true; - - raw_stdin_attr = f->saved_stdin_attr; - cfmakeraw(&raw_stdin_attr); - raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; - tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr); - } - - if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) { - struct termios raw_stdout_attr; - - f->saved_stdout = true; - - raw_stdout_attr = f->saved_stdout_attr; - cfmakeraw(&raw_stdout_attr); - raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag; - raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag; - tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr); - } - - r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f); - if (r < 0 && r != -EPERM) - return r; - } - - r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f); - if (r == -EPERM) - /* stdout without epoll support. Likely redirected to regular file. */ - f->stdout_writable = true; - else if (r < 0) - return r; - - r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f); - if (r < 0) - return r; - - r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f); - if (r < 0) - return r; - - *ret = f; - f = NULL; - - return 0; -} - -PTYForward *pty_forward_free(PTYForward *f) { - - if (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) - tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr); - if (f->saved_stdin) - tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr); - - free(f); - } - - /* STDIN/STDOUT should not be nonblocking normally, so let's - * unconditionally reset it */ - fd_nonblock(STDIN_FILENO, false); - fd_nonblock(STDOUT_FILENO, false); - - return NULL; -} - -int pty_forward_get_last_char(PTYForward *f, char *ch) { - assert(f); - assert(ch); - - if (!f->last_char_set) - return -ENXIO; - - *ch = f->last_char; - return 0; -} - -int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { - int r; - - assert(f); - - if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) - return 0; - - SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b); - - if (!ignore_vhangup(f)) { - - /* We shall now react to vhangup()s? Let's check - * immediately if we might be in one */ - - f->master_readable = true; - r = shovel(f); - if (r < 0) - return r; - } - - return 0; -} - -int pty_forward_get_ignore_vhangup(PTYForward *f) { - assert(f); - - return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP); -} diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h deleted file mode 100644 index a046eb4e5e..0000000000 --- a/src/shared/ptyfwd.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-event.h" - -#include "macro.h" - -typedef struct PTYForward PTYForward; - -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); - -int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup); -int pty_forward_get_ignore_vhangup(PTYForward *f); - -DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c deleted file mode 100644 index e2da81bab7..0000000000 --- a/src/shared/resolve-util.c +++ /dev/null @@ -1,39 +0,0 @@ -/*** - 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 . -***/ - -#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 deleted file mode 100644 index 8636a6c134..0000000000 --- a/src/shared/resolve-util.h +++ /dev/null @@ -1,60 +0,0 @@ -#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 . -***/ - -#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 deleted file mode 100644 index 8656d112b8..0000000000 --- a/src/shared/seccomp-util.c +++ /dev/null @@ -1,323 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "macro.h" -#include "seccomp-util.h" -#include "string-util.h" - -const char* seccomp_arch_to_string(uint32_t c) { - - if (c == SCMP_ARCH_NATIVE) - return "native"; - if (c == SCMP_ARCH_X86) - return "x86"; - if (c == SCMP_ARCH_X86_64) - return "x86-64"; - if (c == SCMP_ARCH_X32) - return "x32"; - if (c == SCMP_ARCH_ARM) - return "arm"; - - return NULL; -} - -int seccomp_arch_from_string(const char *n, uint32_t *ret) { - if (!n) - return -EINVAL; - - assert(ret); - - if (streq(n, "native")) - *ret = SCMP_ARCH_NATIVE; - else if (streq(n, "x86")) - *ret = SCMP_ARCH_X86; - else if (streq(n, "x86-64")) - *ret = SCMP_ARCH_X86_64; - else if (streq(n, "x32")) - *ret = SCMP_ARCH_X32; - else if (streq(n, "arm")) - *ret = SCMP_ARCH_ARM; - else - return -EINVAL; - - return 0; -} - -int seccomp_add_secondary_archs(scmp_filter_ctx *c) { - -#if defined(__i386__) || defined(__x86_64__) - int r; - - /* Add in all possible secondary archs we are aware of that - * this kernel might support. */ - - r = seccomp_arch_add(c, SCMP_ARCH_X86); - if (r < 0 && r != -EEXIST) - return r; - - r = seccomp_arch_add(c, SCMP_ARCH_X86_64); - if (r < 0 && r != -EEXIST) - return r; - - r = seccomp_arch_add(c, SCMP_ARCH_X32); - if (r < 0 && r != -EEXIST) - return r; - -#endif - - return 0; - -} - -const SystemCallFilterSet syscall_filter_sets[] = { - { - /* Clock */ - .set_name = "@clock", - .value = - "adjtimex\0" - "clock_adjtime\0" - "clock_settime\0" - "settimeofday\0" - "stime\0" - }, { - /* CPU emulation calls */ - .set_name = "@cpu-emulation", - .value = - "modify_ldt\0" - "subpage_prot\0" - "switch_endian\0" - "vm86\0" - "vm86old\0" - }, { - /* Debugging/Performance Monitoring/Tracing */ - .set_name = "@debug", - .value = - "lookup_dcookie\0" - "perf_event_open\0" - "process_vm_readv\0" - "process_vm_writev\0" - "ptrace\0" - "rtas\0" - "s390_runtime_instr\0" - "sys_debug_setcontext\0" - }, { - /* Default list */ - .set_name = "@default", - .value = - "execve\0" - "exit\0" - "exit_group\0" - "rt_sigreturn\0" - "sigreturn\0" - }, { - /* Event loop use */ - .set_name = "@io-event", - .value = - "_newselect\0" - "epoll_create1\0" - "epoll_create\0" - "epoll_ctl\0" - "epoll_ctl_old\0" - "epoll_pwait\0" - "epoll_wait\0" - "epoll_wait_old\0" - "eventfd2\0" - "eventfd\0" - "poll\0" - "ppoll\0" - "pselect6\0" - "select\0" - }, { - /* Message queues, SYSV IPC or other IPC: unusual */ - .set_name = "@ipc", - .value = "ipc\0" - "mq_getsetattr\0" - "mq_notify\0" - "mq_open\0" - "mq_timedreceive\0" - "mq_timedsend\0" - "mq_unlink\0" - "msgctl\0" - "msgget\0" - "msgrcv\0" - "msgsnd\0" - "process_vm_readv\0" - "process_vm_writev\0" - "semctl\0" - "semget\0" - "semop\0" - "semtimedop\0" - "shmat\0" - "shmctl\0" - "shmdt\0" - "shmget\0" - }, { - /* Keyring */ - .set_name = "@keyring", - .value = - "add_key\0" - "keyctl\0" - "request_key\0" - }, { - /* Kernel module control */ - .set_name = "@module", - .value = - "delete_module\0" - "finit_module\0" - "init_module\0" - }, { - /* Mounting */ - .set_name = "@mount", - .value = - "chroot\0" - "mount\0" - "oldumount\0" - "pivot_root\0" - "umount2\0" - "umount\0" - }, { - /* Network or Unix socket IO, should not be needed if not network facing */ - .set_name = "@network-io", - .value = - "accept4\0" - "accept\0" - "bind\0" - "connect\0" - "getpeername\0" - "getsockname\0" - "getsockopt\0" - "listen\0" - "recv\0" - "recvfrom\0" - "recvmmsg\0" - "recvmsg\0" - "send\0" - "sendmmsg\0" - "sendmsg\0" - "sendto\0" - "setsockopt\0" - "shutdown\0" - "socket\0" - "socketcall\0" - "socketpair\0" - }, { - /* Unusual, obsolete or unimplemented, some unknown even to libseccomp */ - .set_name = "@obsolete", - .value = - "_sysctl\0" - "afs_syscall\0" - "break\0" - "create_module\0" - "ftime\0" - "get_kernel_syms\0" - "getpmsg\0" - "gtty\0" - "lock\0" - "mpx\0" - "prof\0" - "profil\0" - "putpmsg\0" - "query_module\0" - "security\0" - "sgetmask\0" - "ssetmask\0" - "stty\0" - "sysfs\0" - "tuxcall\0" - "ulimit\0" - "uselib\0" - "ustat\0" - "vserver\0" - }, { - /* Nice grab-bag of all system calls which need superuser capabilities */ - .set_name = "@privileged", - .value = - "@clock\0" - "@module\0" - "@raw-io\0" - "acct\0" - "bdflush\0" - "bpf\0" - "capset\0" - "chown32\0" - "chown\0" - "chroot\0" - "fchown32\0" - "fchown\0" - "fchownat\0" - "kexec_file_load\0" - "kexec_load\0" - "lchown32\0" - "lchown\0" - "nfsservctl\0" - "pivot_root\0" - "quotactl\0" - "reboot\0" - "setdomainname\0" - "setfsuid32\0" - "setfsuid\0" - "setgroups32\0" - "setgroups\0" - "sethostname\0" - "setresuid32\0" - "setresuid\0" - "setreuid32\0" - "setreuid\0" - "setuid32\0" - "setuid\0" - "swapoff\0" - "swapon\0" - "sysctl\0" - "vhangup\0" - }, { - /* Process control, execution, namespaces */ - .set_name = "@process", - .value = - "arch_prctl\0" - "clone\0" - "execve\0" - "execveat\0" - "fork\0" - "kill\0" - "prctl\0" - "setns\0" - "tgkill\0" - "tkill\0" - "unshare\0" - "vfork\0" - }, { - /* Raw I/O ports */ - .set_name = "@raw-io", - .value = - "ioperm\0" - "iopl\0" - "pciconfig_iobase\0" - "pciconfig_read\0" - "pciconfig_write\0" - "s390_pci_mmio_read\0" - "s390_pci_mmio_write\0" - }, { - .set_name = NULL, - .value = NULL - } -}; diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h deleted file mode 100644 index be33eecb85..0000000000 --- a/src/shared/seccomp-util.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -const char* seccomp_arch_to_string(uint32_t c); -int seccomp_arch_from_string(const char *n, uint32_t *ret); - -int seccomp_add_secondary_archs(scmp_filter_ctx *c); - -typedef struct SystemCallFilterSet { - const char *set_name; - const char *value; -} SystemCallFilterSet; - -extern const SystemCallFilterSet syscall_filter_sets[]; diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c deleted file mode 100644 index f00624d0f2..0000000000 --- a/src/shared/sleep-config.c +++ /dev/null @@ -1,278 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-parser.h" -#include "def.h" -#include "env-util.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" - -#define USE(x, y) do { (x) = (y); (y) = NULL; } while (0) - -int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { - - _cleanup_strv_free_ char - **suspend_mode = NULL, **suspend_state = NULL, - **hibernate_mode = NULL, **hibernate_state = NULL, - **hybrid_mode = NULL, **hybrid_state = NULL; - char **modes, **states; - - const ConfigTableItem items[] = { - { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode }, - { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state }, - { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode }, - { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state }, - { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode }, - { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state }, - {} - }; - - config_parse_many(PKGSYSCONFDIR "/sleep.conf", - CONF_PATHS_NULSTR("systemd/sleep.conf.d"), - "Sleep\0", config_item_table_lookup, items, - false, NULL); - - if (streq(verb, "suspend")) { - /* empty by default */ - USE(modes, suspend_mode); - - if (suspend_state) - USE(states, suspend_state); - else - states = strv_new("mem", "standby", "freeze", NULL); - - } else if (streq(verb, "hibernate")) { - if (hibernate_mode) - USE(modes, hibernate_mode); - else - modes = strv_new("platform", "shutdown", NULL); - - if (hibernate_state) - USE(states, hibernate_state); - else - states = strv_new("disk", NULL); - - } else if (streq(verb, "hybrid-sleep")) { - if (hybrid_mode) - USE(modes, hybrid_mode); - else - modes = strv_new("suspend", "platform", "shutdown", NULL); - - if (hybrid_state) - USE(states, hybrid_state); - else - states = strv_new("disk", NULL); - - } else - assert_not_reached("what verb"); - - if ((!modes && !streq(verb, "suspend")) || !states) { - strv_free(modes); - strv_free(states); - return log_oom(); - } - - *_modes = modes; - *_states = states; - return 0; -} - -int can_sleep_state(char **types) { - char **type; - int r; - _cleanup_free_ char *p = NULL; - - if (strv_isempty(types)) - return true; - - /* If /sys is read-only we cannot sleep */ - if (access("/sys/power/state", W_OK) < 0) - return false; - - r = read_one_line_file("/sys/power/state", &p); - if (r < 0) - return false; - - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; - - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) - if (l == k && memcmp(word, *type, l) == 0) - return true; - } - - return false; -} - -int can_sleep_disk(char **types) { - char **type; - int r; - _cleanup_free_ char *p = NULL; - - if (strv_isempty(types)) - return true; - - /* If /sys is read-only we cannot sleep */ - if (access("/sys/power/disk", W_OK) < 0) - return false; - - r = read_one_line_file("/sys/power/disk", &p); - if (r < 0) - return false; - - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; - - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) { - if (l == k && memcmp(word, *type, l) == 0) - return true; - - if (l == k + 2 && - word[0] == '[' && - memcmp(word + 1, *type, l - 2) == 0 && - word[l-1] == ']') - return true; - } - } - - return false; -} - -#define HIBERNATION_SWAP_THRESHOLD 0.98 - -static int hibernation_partition_size(size_t *size, size_t *used) { - _cleanup_fclose_ FILE *f; - unsigned i; - - assert(size); - assert(used); - - f = fopen("/proc/swaps", "re"); - if (!f) { - log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, - "Failed to retrieve open /proc/swaps: %m"); - assert(errno > 0); - return -errno; - } - - (void) fscanf(f, "%*s %*s %*s %*s %*s\n"); - - for (i = 1;; i++) { - _cleanup_free_ char *dev = NULL, *type = NULL; - size_t size_field, used_field; - int k; - - k = fscanf(f, - "%ms " /* device/file */ - "%ms " /* type of swap */ - "%zu " /* swap size */ - "%zu " /* used */ - "%*i\n", /* priority */ - &dev, &type, &size_field, &used_field); - if (k != 4) { - if (k == EOF) - break; - - log_warning("Failed to parse /proc/swaps:%u", i); - continue; - } - - if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) { - log_warning("Ignoring deleted swapfile '%s'.", dev); - continue; - } - - *size = size_field; - *used = used_field; - return 0; - } - - log_debug("No swap partitions were found."); - return -ENOSYS; -} - -static bool enough_memory_for_hibernation(void) { - _cleanup_free_ char *active = NULL; - unsigned long long act = 0; - size_t size = 0, used = 0; - int r; - - if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) - return true; - - r = hibernation_partition_size(&size, &used); - if (r < 0) - return false; - - 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; - } - - r = safe_atollu(active, &act); - if (r < 0) { - log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", - active); - return false; - } - - r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; - log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", - r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); - - return r; -} - -int can_sleep(const char *verb) { - _cleanup_strv_free_ char **modes = NULL, **states = NULL; - int r; - - assert(streq(verb, "suspend") || - streq(verb, "hibernate") || - streq(verb, "hybrid-sleep")); - - r = parse_sleep_config(verb, &modes, &states); - if (r < 0) - return false; - - if (!can_sleep_state(states) || !can_sleep_disk(modes)) - return false; - - return streq(verb, "suspend") || enough_memory_for_hibernation(); -} diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h deleted file mode 100644 index ad10039ff4..0000000000 --- a/src/shared/sleep-config.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int parse_sleep_config(const char *verb, char ***modes, char ***states); - -int can_sleep(const char *verb); -int can_sleep_disk(char **types); -int can_sleep_state(char **types); diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c deleted file mode 100644 index a46b7525f0..0000000000 --- a/src/shared/spawn-ask-password-agent.c +++ /dev/null @@ -1,62 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "log.h" -#include "process-util.h" -#include "spawn-ask-password-agent.h" -#include "util.h" - -static pid_t agent_pid = 0; - -int ask_password_agent_open(void) { - int r; - - if (agent_pid > 0) - return 0; - - /* We check STDIN here, not STDOUT, since this is about input, - * not output */ - if (!isatty(STDIN_FILENO)) - return 0; - - r = fork_agent(&agent_pid, - NULL, 0, - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); - if (r < 0) - return log_error_errno(r, "Failed to fork TTY ask password agent: %m"); - - return 1; -} - -void ask_password_agent_close(void) { - - if (agent_pid <= 0) - return; - - /* Inform agent that we are done */ - (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-ask-password-agent.h b/src/shared/spawn-ask-password-agent.h deleted file mode 100644 index fb0749b13f..0000000000 --- a/src/shared/spawn-ask-password-agent.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int ask_password_agent_open(void); -void ask_password_agent_close(void); diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c deleted file mode 100644 index 7dae4d14fe..0000000000 --- a/src/shared/spawn-polkit-agent.c +++ /dev/null @@ -1,102 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "io-util.h" -#include "log.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; - -int polkit_agent_open(void) { - int r; - int pipe_fd[2]; - char notify_fd[DECIMAL_STR_MAX(int) + 1]; - - if (agent_pid > 0) - return 0; - - /* Clients that run as root don't need to activate/query polkit */ - if (geteuid() == 0) - return 0; - - /* We check STDIN here, not STDOUT, since this is about input, - * not output */ - if (!isatty(STDIN_FILENO)) - return 0; - - if (pipe2(pipe_fd, 0) < 0) - return -errno; - - xsprintf(notify_fd, "%i", pipe_fd[1]); - - r = fork_agent(&agent_pid, - &pipe_fd[1], 1, - POLKIT_AGENT_BINARY_PATH, - POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback", NULL); - - /* Close the writing side, because that's the one for the agent */ - safe_close(pipe_fd[1]); - - if (r < 0) - log_error_errno(r, "Failed to fork TTY ask password agent: %m"); - else - /* Wait until the agent closes the fd */ - fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); - - safe_close(pipe_fd[0]); - - return r; -} - -void polkit_agent_close(void) { - - if (agent_pid <= 0) - return; - - /* Inform agent that we are done */ - (void) kill(agent_pid, SIGTERM); - (void) kill(agent_pid, SIGCONT); - - (void) wait_for_terminate(agent_pid, NULL); - agent_pid = 0; -} - -#else - -int polkit_agent_open(void) { - return 0; -} - -void polkit_agent_close(void) { -} - -#endif diff --git a/src/shared/spawn-polkit-agent.h b/src/shared/spawn-polkit-agent.h deleted file mode 100644 index 42b2989ded..0000000000 --- a/src/shared/spawn-polkit-agent.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -int polkit_agent_open(void); -void polkit_agent_close(void); diff --git a/src/shared/specifier.c b/src/shared/specifier.c deleted file mode 100644 index 1c17eb5251..0000000000 --- a/src/shared/specifier.c +++ /dev/null @@ -1,188 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "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 - * strings. Will call a callback for each replacement. - * - */ - -int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) { - char *ret, *t; - const char *f; - bool percent = false; - size_t l; - int r; - - assert(text); - assert(table); - - l = strlen(text); - ret = new(char, l+1); - if (!ret) - return -ENOMEM; - - t = ret; - - for (f = text; *f; f++, l--) { - - if (percent) { - if (*f == '%') - *(t++) = '%'; - else { - const Specifier *i; - - for (i = table; i->specifier; i++) - if (i->specifier == *f) - break; - - if (i->lookup) { - _cleanup_free_ char *w = NULL; - char *n; - size_t k, j; - - r = i->lookup(i->specifier, i->data, userdata, &w); - if (r < 0) { - free(ret); - return r; - } - - j = t - ret; - k = strlen(w); - - n = new(char, j + k + l + 1); - if (!n) { - free(ret); - return -ENOMEM; - } - - memcpy(n, ret, j); - memcpy(n + j, w, k); - - free(ret); - - ret = n; - t = n + j + k; - } else { - *(t++) = '%'; - *(t++) = *f; - } - } - - percent = false; - } else if (*f == '%') - percent = true; - else - *(t++) = *f; - } - - *t = 0; - *_ret = ret; - return 0; -} - -/* Generic handler for simple string replacements */ - -int specifier_string(char specifier, void *data, void *userdata, char **ret) { - char *n; - - n = strdup(strempty(data)); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) { - sd_id128_t id; - char *n; - int r; - - r = sd_id128_get_machine(&id); - if (r < 0) - return r; - - n = new(char, 33); - if (!n) - return -ENOMEM; - - *ret = sd_id128_to_string(id, n); - return 0; -} - -int specifier_boot_id(char specifier, void *data, void *userdata, char **ret) { - sd_id128_t id; - char *n; - int r; - - r = sd_id128_get_boot(&id); - if (r < 0) - return r; - - n = new(char, 33); - if (!n) - return -ENOMEM; - - *ret = sd_id128_to_string(id, n); - return 0; -} - -int specifier_host_name(char specifier, void *data, void *userdata, char **ret) { - char *n; - - n = gethostname_malloc(); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} - -int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret) { - struct utsname uts; - char *n; - int r; - - r = uname(&uts); - if (r < 0) - return -errno; - - n = strdup(uts.release); - if (!n) - return -ENOMEM; - - *ret = n; - return 0; -} diff --git a/src/shared/specifier.h b/src/shared/specifier.h deleted file mode 100644 index 6b1623ee61..0000000000 --- a/src/shared/specifier.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 . -***/ - -typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret); - -typedef struct Specifier { - const char specifier; - const SpecifierCallback lookup; - void *data; -} Specifier; - -int specifier_printf(const char *text, const Specifier table[], void *userdata, char **ret); - -int specifier_string(char specifier, void *data, void *userdata, char **ret); - -int specifier_machine_id(char specifier, void *data, void *userdata, char **ret); -int specifier_boot_id(char specifier, void *data, void *userdata, char **ret); -int specifier_host_name(char specifier, void *data, void *userdata, char **ret); -int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret); diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c deleted file mode 100644 index 47d3a5a1fa..0000000000 --- a/src/shared/switch-root.c +++ /dev/null @@ -1,156 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Harald Hoyer, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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) { - - /* Don't try to unmount/move the old "/", there's no way to do it. */ - static const char move_mounts[] = - "/dev\0" - "/proc\0" - "/sys\0" - "/run\0"; - - _cleanup_close_ int old_root_fd = -1; - struct stat new_root_stat; - bool old_root_remove; - const char *i, *temporary_old_root; - - if (path_equal(new_root, "/")) - return 0; - - temporary_old_root = strjoina(new_root, oldroot); - mkdir_p_label(temporary_old_root, 0755); - - old_root_remove = in_initrd(); - - if (stat(new_root, &new_root_stat) < 0) - return log_error_errno(errno, "Failed to stat directory %s: %m", new_root); - - /* Work-around for kernel design: the kernel refuses switching - * root if any file systems are mounted MS_SHARED. Hence - * remount them MS_PRIVATE here as a work-around. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ - if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) - log_warning_errno(errno, "Failed to make \"/\" private mount: %m"); - - NULSTR_FOREACH(i, move_mounts) { - char new_mount[PATH_MAX]; - struct stat sb; - - xsprintf(new_mount, "%s%s", new_root, i); - - mkdir_p_label(new_mount, 0755); - - if ((stat(new_mount, &sb) < 0) || - sb.st_dev != new_root_stat.st_dev) { - - /* Mount point seems to be mounted already or - * stat failed. Unmount the old mount - * point. */ - if (umount2(i, MNT_DETACH) < 0) - log_warning_errno(errno, "Failed to unmount %s: %m", i); - continue; - } - - if (mount(i, new_mount, NULL, mountflags, NULL) < 0) { - if (mountflags & MS_MOVE) { - log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); - - if (umount2(i, MNT_FORCE) < 0) - log_warning_errno(errno, "Failed to unmount %s: %m", i); - } - if (mountflags & MS_BIND) - log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount); - - } - } - - /* Do not fail, if base_filesystem_create() fails. Not all - * switch roots are like base_filesystem_create() wants them - * to look like. They might even boot, if they are RO and - * don't have the FS layout. Just ignore the error and - * switch_root() nevertheless. */ - (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID); - - if (chdir(new_root) < 0) - return log_error_errno(errno, "Failed to change directory to %s: %m", new_root); - - if (old_root_remove) { - old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (old_root_fd < 0) - log_warning_errno(errno, "Failed to open root directory: %m"); - } - - /* We first try a pivot_root() so that we can umount the old - * root dir. In many cases (i.e. where rootfs is /), that's - * not possible however, and hence we simply overmount root */ - if (pivot_root(new_root, temporary_old_root) >= 0) { - - /* Immediately get rid of the old root, if detach_oldroot is set. - * Since we are running off it we need to do this lazily. */ - if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0) - log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m", - oldroot, - errno == ENOENT ? "ignoring" : "leaving it around"); - - } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) - return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root); - - if (chroot(".") < 0) - return log_error_errno(errno, "Failed to change root: %m"); - - if (chdir("/") < 0) - return log_error_errno(errno, "Failed to change directory: %m"); - - if (old_root_fd >= 0) { - struct stat rb; - - if (fstat(old_root_fd, &rb) < 0) - log_warning_errno(errno, "Failed to stat old root directory, leaving: %m"); - else { - (void) rm_rf_children(old_root_fd, 0, &rb); - old_root_fd = -1; - } - } - - return 0; -} diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h deleted file mode 100644 index a7a080b3e8..0000000000 --- a/src/shared/switch-root.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -/*** - This file is part of systemd. - - Copyright 2012 Harald Hoyer, 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 . -***/ - -int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags); diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c deleted file mode 100644 index e1ccb3294c..0000000000 --- a/src/shared/sysctl-util.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "sysctl-util.h" - -char *sysctl_normalize(char *s) { - char *n; - - n = strpbrk(s, "/."); - /* If the first separator is a slash, the path is - * assumed to be normalized and slashes remain slashes - * and dots remains dots. */ - if (!n || *n == '/') - return s; - - /* Otherwise, dots become slashes and slashes become - * dots. Fun. */ - while (n) { - if (*n == '.') - *n = '/'; - else - *n = '.'; - - n = strpbrk(n + 1, "/."); - } - - return s; -} - -int sysctl_write(const char *property, const char *value) { - char *p; - - assert(property); - assert(value); - - log_debug("Setting '%s' to '%s'", property, value); - - p = strjoina("/proc/sys/", property); - return write_string_file(p, value, 0); -} - -int sysctl_read(const char *property, char **content) { - char *p; - - assert(property); - assert(content); - - p = strjoina("/proc/sys/", property); - return read_full_file(p, content, NULL); -} diff --git a/src/shared/sysctl-util.h b/src/shared/sysctl-util.h deleted file mode 100644 index 2decb39f58..0000000000 --- a/src/shared/sysctl-util.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -char *sysctl_normalize(char *s); -int sysctl_read(const char *property, char **value); -int sysctl_write(const char *property, const char *value); - diff --git a/src/shared/test-tables.h b/src/shared/test-tables.h deleted file mode 100644 index 228e510104..0000000000 --- a/src/shared/test-tables.h +++ /dev/null @@ -1,60 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -typedef const char* (*lookup_t)(int); -typedef int (*reverse_t)(const char*); - -static inline void _test_table(const char *name, - lookup_t lookup, - reverse_t reverse, - int size, - bool sparse) { - int i, boring = 0; - - for (i = -1; i < size + 1; i++) { - const char* val = lookup(i); - int rev; - - if (val) { - rev = reverse(val); - boring = 0; - } else { - rev = reverse("--no-such--value----"); - boring += i >= 0; - } - - if (boring < 1 || i == size) - printf("%s: %d → %s → %d\n", name, i, val, rev); - else if (boring == 1) - printf("%*s ...\n", (int) strlen(name), ""); - - assert_se(!(i >= 0 && i < size ? - sparse ? rev != i && rev != -1 : val == NULL || rev != i : - val != NULL || rev != -1)); - } -} - -#define test_table(lower, upper) \ - _test_table(STRINGIFY(lower), lower##_to_string, lower##_from_string, _##upper##_MAX, false) - -#define test_table_sparse(lower, upper) \ - _test_table(STRINGIFY(lower), lower##_to_string, lower##_from_string, _##upper##_MAX, true) diff --git a/src/shared/tests.c b/src/shared/tests.c deleted file mode 100644 index 409116290d..0000000000 --- a/src/shared/tests.c +++ /dev/null @@ -1,33 +0,0 @@ -/*** - 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 . -***/ - -#include -#include - -#include "tests.h" - -char* setup_fake_runtime_dir(void) { - char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; - - assert_se(mkdtemp(t)); - assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0); - assert_se(p = strdup(t)); - - return p; -} diff --git a/src/shared/tests.h b/src/shared/tests.h deleted file mode 100644 index 93f09013a1..0000000000 --- a/src/shared/tests.h +++ /dev/null @@ -1,22 +0,0 @@ -#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 . -***/ - -char* setup_fake_runtime_dir(void); diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h deleted file mode 100644 index ca0889f8a6..0000000000 --- a/src/shared/udev-util.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "udev.h" -#include "util.h" - -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_connection*, udev_ctrl_connection_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl_msg*, udev_ctrl_msg_unref); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref); - -#define _cleanup_udev_unref_ _cleanup_(udev_unrefp) -#define _cleanup_udev_device_unref_ _cleanup_(udev_device_unrefp) -#define _cleanup_udev_enumerate_unref_ _cleanup_(udev_enumerate_unrefp) -#define _cleanup_udev_event_unref_ _cleanup_(udev_event_unrefp) -#define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp) -#define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp) -#define _cleanup_udev_ctrl_connection_unref_ _cleanup_(udev_ctrl_connection_unrefp) -#define _cleanup_udev_ctrl_msg_unref_ _cleanup_(udev_ctrl_msg_unrefp) -#define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp) -#define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup) diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c deleted file mode 100644 index b6ec474390..0000000000 --- a/src/shared/uid-range.c +++ /dev/null @@ -1,208 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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); - - return range->start <= start + nr && - range->start + range->nr >= start; -} - -static void uid_range_coalesce(UidRange **p, unsigned *n) { - unsigned i, j; - - assert(p); - assert(n); - - for (i = 0; i < *n; i++) { - for (j = i + 1; j < *n; j++) { - UidRange *x = (*p)+i, *y = (*p)+j; - - if (uid_range_intersect(x, y->start, y->nr)) { - uid_t begin, end; - - begin = MIN(x->start, y->start); - end = MAX(x->start + x->nr, y->start + y->nr); - - x->start = begin; - x->nr = end - begin; - - if (*n > j+1) - memmove(y, y+1, sizeof(UidRange) * (*n - j -1)); - - (*n)--; - j--; - } - } - } - -} - -static int uid_range_compare(const void *a, const void *b) { - const UidRange *x = a, *y = b; - - if (x->start < y->start) - return -1; - if (x->start > y->start) - return 1; - - if (x->nr < y->nr) - return -1; - if (x->nr > y->nr) - return 1; - - return 0; -} - -int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) { - bool found = false; - UidRange *x; - unsigned i; - - assert(p); - assert(n); - - if (nr <= 0) - return 0; - - for (i = 0; i < *n; i++) { - x = (*p) + i; - if (uid_range_intersect(x, start, nr)) { - found = true; - break; - } - } - - if (found) { - uid_t begin, end; - - begin = MIN(x->start, start); - end = MAX(x->start + x->nr, start + nr); - - x->start = begin; - x->nr = end - begin; - } else { - UidRange *t; - - t = realloc(*p, sizeof(UidRange) * (*n + 1)); - if (!t) - return -ENOMEM; - - *p = t; - x = t + ((*n) ++); - - x->start = start; - x->nr = nr; - } - - qsort(*p, *n, sizeof(UidRange), uid_range_compare); - uid_range_coalesce(p, n); - - return *n; -} - -int uid_range_add_str(UidRange **p, unsigned *n, const char *s) { - uid_t start, nr; - const char *t; - int r; - - assert(p); - assert(n); - assert(s); - - t = strchr(s, '-'); - if (t) { - char *b; - uid_t end; - - b = strndupa(s, t - s); - r = parse_uid(b, &start); - if (r < 0) - return r; - - r = parse_uid(t+1, &end); - if (r < 0) - return r; - - if (end < start) - return -EINVAL; - - nr = end - start + 1; - } else { - r = parse_uid(s, &start); - if (r < 0) - return r; - - nr = 1; - } - - return uid_range_add(p, n, start, nr); -} - -int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { - uid_t closest = UID_INVALID, candidate; - unsigned i; - - assert(p); - assert(uid); - - candidate = *uid - 1; - - for (i = 0; i < n; i++) { - uid_t begin, end; - - begin = p[i].start; - end = p[i].start + p[i].nr - 1; - - if (candidate >= begin && candidate <= end) { - *uid = candidate; - return 1; - } - - if (end < candidate) - closest = end; - } - - if (closest == UID_INVALID) - return -EBUSY; - - *uid = closest; - return 1; -} - -bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) { - unsigned i; - - assert(p); - assert(uid); - - for (i = 0; i < n; i++) - if (uid >= p[i].start && uid < p[i].start + p[i].nr) - return true; - - return false; -} diff --git a/src/shared/uid-range.h b/src/shared/uid-range.h deleted file mode 100644 index 4044eb4c9c..0000000000 --- a/src/shared/uid-range.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -typedef struct UidRange { - uid_t start, nr; -} UidRange; - -int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr); -int uid_range_add_str(UidRange **p, unsigned *n, const char *s); - -int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid); -bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c deleted file mode 100644 index 9750dcd817..0000000000 --- a/src/shared/utmp-wtmp.c +++ /dev/null @@ -1,445 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "time-util.h" -#include "user-util.h" -#include "util.h" -#include "utmp-wtmp.h" - -int utmp_get_runlevel(int *runlevel, int *previous) { - struct utmpx *found, lookup = { .ut_type = RUN_LVL }; - int r; - const char *e; - - assert(runlevel); - - /* If these values are set in the environment this takes - * precedence. Presumably, sysvinit does this to work around a - * race condition that would otherwise exist where we'd always - * go to disk and hence might read runlevel data that might be - * very new and does not apply to the current script being - * executed. */ - - e = getenv("RUNLEVEL"); - if (e && e[0] > 0) { - *runlevel = e[0]; - - if (previous) { - /* $PREVLEVEL seems to be an Upstart thing */ - - e = getenv("PREVLEVEL"); - if (e && e[0] > 0) - *previous = e[0]; - else - *previous = 0; - } - - return 0; - } - - if (utmpxname(_PATH_UTMPX) < 0) - return -errno; - - setutxent(); - - found = getutxid(&lookup); - if (!found) - r = -errno; - else { - int a, b; - - a = found->ut_pid & 0xFF; - b = (found->ut_pid >> 8) & 0xFF; - - *runlevel = a; - if (previous) - *previous = b; - - r = 0; - } - - endutxent(); - - return r; -} - -static void init_timestamp(struct utmpx *store, usec_t t) { - assert(store); - - if (t <= 0) - t = now(CLOCK_REALTIME); - - store->ut_tv.tv_sec = t / USEC_PER_SEC; - store->ut_tv.tv_usec = t % USEC_PER_SEC; -} - -static void init_entry(struct utmpx *store, usec_t t) { - struct utsname uts = {}; - - assert(store); - - init_timestamp(store, t); - - if (uname(&uts) >= 0) - strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); - - strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */ - strncpy(store->ut_id, "~~", sizeof(store->ut_id)); -} - -static int write_entry_utmp(const struct utmpx *store) { - int r; - - assert(store); - - /* utmp is similar to wtmp, but there is only one entry for - * each entry type resp. user; i.e. basically a key/value - * table. */ - - if (utmpxname(_PATH_UTMPX) < 0) - return -errno; - - setutxent(); - - if (!pututxline(store)) - r = -errno; - else - r = 0; - - endutxent(); - - return r; -} - -static int write_entry_wtmp(const struct utmpx *store) { - assert(store); - - /* wtmp is a simple append-only file where each entry is - simply appended to the end; i.e. basically a log. */ - - errno = 0; - updwtmpx(_PATH_WTMPX, store); - return -errno; -} - -static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) { - int r, s; - - r = write_entry_utmp(store_utmp); - s = write_entry_wtmp(store_wtmp); - - if (r >= 0) - r = s; - - /* If utmp/wtmp have been disabled, that's a good thing, hence - * ignore the errors */ - if (r == -ENOENT) - r = 0; - - return r; -} - -static int write_entry_both(const struct utmpx *store) { - return write_utmp_wtmp(store, store); -} - -int utmp_put_shutdown(void) { - struct utmpx store = {}; - - init_entry(&store, 0); - - store.ut_type = RUN_LVL; - strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -int utmp_put_reboot(usec_t t) { - struct utmpx store = {}; - - init_entry(&store, t); - - store.ut_type = BOOT_TIME; - strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -_pure_ static const char *sanitize_id(const char *id) { - size_t l; - - assert(id); - l = strlen(id); - - if (l <= sizeof(((struct utmpx*) NULL)->ut_id)) - return id; - - return id + l - sizeof(((struct utmpx*) NULL)->ut_id); -} - -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { - struct utmpx store = { - .ut_type = INIT_PROCESS, - .ut_pid = pid, - .ut_session = sid, - }; - int r; - - assert(id); - - init_timestamp(&store, 0); - - /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ - strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id)); - - if (line) - strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); - - r = write_entry_both(&store); - if (r < 0) - return r; - - if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { - store.ut_type = LOGIN_PROCESS; - r = write_entry_both(&store); - if (r < 0) - return r; - } - - if (ut_type == USER_PROCESS) { - store.ut_type = USER_PROCESS; - strncpy(store.ut_user, user, sizeof(store.ut_user)-1); - r = write_entry_both(&store); - if (r < 0) - return r; - } - - return 0; -} - -int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { - struct utmpx lookup = { - .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */ - }, store, store_wtmp, *found; - - assert(id); - - setutxent(); - - /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */ - strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id)); - - found = getutxid(&lookup); - if (!found) - return 0; - - if (found->ut_pid != pid) - return 0; - - memcpy(&store, found, sizeof(store)); - store.ut_type = DEAD_PROCESS; - store.ut_exit.e_termination = code; - store.ut_exit.e_exit = status; - - zero(store.ut_user); - zero(store.ut_host); - zero(store.ut_tv); - - memcpy(&store_wtmp, &store, sizeof(store_wtmp)); - /* wtmp wants the current time */ - init_timestamp(&store_wtmp, 0); - - return write_utmp_wtmp(&store, &store_wtmp); -} - - -int utmp_put_runlevel(int runlevel, int previous) { - struct utmpx store = {}; - int r; - - assert(runlevel > 0); - - if (previous <= 0) { - /* Find the old runlevel automatically */ - - r = utmp_get_runlevel(&previous, NULL); - if (r < 0) { - if (r != -ESRCH) - return r; - - previous = 0; - } - } - - if (previous == runlevel) - return 0; - - init_entry(&store, 0); - - store.ut_type = RUN_LVL; - store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); - strncpy(store.ut_user, "runlevel", sizeof(store.ut_user)); - - return write_entry_both(&store); -} - -#define TIMEOUT_MSEC 50 - -static int write_to_terminal(const char *tty, const char *message) { - _cleanup_close_ int fd = -1; - const char *p; - size_t left; - usec_t end; - - assert(tty); - assert(message); - - fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC); - if (fd < 0 || !isatty(fd)) - return -errno; - - p = message; - left = strlen(message); - - end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; - - while (left > 0) { - ssize_t n; - struct pollfd pollfd = { - .fd = fd, - .events = POLLOUT, - }; - usec_t t; - int k; - - t = now(CLOCK_MONOTONIC); - - if (t >= end) - return -ETIME; - - k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC); - if (k < 0) - return -errno; - - if (k == 0) - return -ETIME; - - n = write(fd, p, left); - if (n < 0) { - if (errno == EAGAIN) - continue; - - return -errno; - } - - assert((size_t) n <= left); - - p += n; - left -= n; - } - - return 0; -} - -int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, void *userdata), - void *userdata) { - - _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL; - char date[FORMAT_TIMESTAMP_MAX]; - struct utmpx *u; - int r; - - hn = gethostname_malloc(); - if (!hn) - return -ENOMEM; - if (!username) { - un = getlogname_malloc(); - if (!un) - return -ENOMEM; - } - - if (!origin_tty) { - getttyname_harder(STDIN_FILENO, &stdin_tty); - origin_tty = stdin_tty; - } - - if (asprintf(&text, - "\a\r\n" - "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" - "%s\r\n\r\n", - un ?: username, hn, - origin_tty ? " on " : "", strempty(origin_tty), - format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)), - message) < 0) - return -ENOMEM; - - setutxent(); - - r = 0; - - while ((u = getutxent())) { - _cleanup_free_ char *buf = NULL; - const char *path; - int q; - - if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) - continue; - - /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */ - if (path_startswith(u->ut_line, "/dev/")) - path = u->ut_line; - else { - if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) - return -ENOMEM; - - path = buf; - } - - if (!match_tty || match_tty(path, userdata)) { - q = write_to_terminal(path, text); - if (q < 0) - r = q; - } - } - - return r; -} diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h deleted file mode 100644 index 438e270a26..0000000000 --- a/src/shared/utmp-wtmp.h +++ /dev/null @@ -1,74 +0,0 @@ -#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 . -***/ - -#include -#include - -#include "time-util.h" -#include "util.h" - -#ifdef HAVE_UTMP -int utmp_get_runlevel(int *runlevel, int *previous); - -int utmp_put_shutdown(void); -int utmp_put_reboot(usec_t timestamp); -int utmp_put_runlevel(int runlevel, int previous); - -int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); - -int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, void *userdata), - void *userdata); - -#else /* HAVE_UTMP */ - -static inline int utmp_get_runlevel(int *runlevel, int *previous) { - return -ESRCH; -} -static inline int utmp_put_shutdown(void) { - return 0; -} -static inline int utmp_put_reboot(usec_t timestamp) { - return 0; -} -static inline int utmp_put_runlevel(int runlevel, int previous) { - return 0; -} -static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { - return 0; -} -static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { - return 0; -} -static inline int utmp_wall( - const char *message, - const char *username, - const char *origin_tty, - bool (*match_tty)(const char *tty, void *userdata), - void *userdata) { - return 0; -} - -#endif /* HAVE_UTMP */ diff --git a/src/shared/vlan-util.c b/src/shared/vlan-util.c deleted file mode 100644 index 78d66dd3d9..0000000000 --- a/src/shared/vlan-util.c +++ /dev/null @@ -1,69 +0,0 @@ -/*** - 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 . -***/ - -#include "vlan-util.h" -#include "parse-util.h" -#include "conf-parser.h" - -int parse_vlanid(const char *p, uint16_t *ret) { - uint16_t id; - int r; - - r = safe_atou16(p, &id); - if (r < 0) - return r; - if (!vlanid_is_valid(id)) - return -ERANGE; - - *ret = id; - return 0; -} - -int config_parse_vlanid( - 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) { - - uint16_t *id = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = parse_vlanid(rvalue, id); - if (r == -ERANGE) { - log_syntax(unit, LOG_ERR, filename, line, r, "VLAN identifier outside of valid range 0…4094, ignoring: %s", rvalue); - return 0; - } - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN identifier value, ignoring: %s", rvalue); - return 0; - } - - return 0; -} diff --git a/src/shared/vlan-util.h b/src/shared/vlan-util.h deleted file mode 100644 index ce6763b3a3..0000000000 --- a/src/shared/vlan-util.h +++ /dev/null @@ -1,35 +0,0 @@ -#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 . -***/ - -#include -#include - -#define VLANID_MAX 4094 -#define VLANID_INVALID UINT16_MAX - -/* Note that we permit VLAN Id 0 here, as that is apparently OK by the Linux kernel */ -static inline bool vlanid_is_valid(uint16_t id) { - return id <= VLANID_MAX; -} - -int parse_vlanid(const char *p, uint16_t *ret); - -int config_parse_vlanid(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/shared/watchdog.c b/src/shared/watchdog.c deleted file mode 100644 index 4f3e0125f3..0000000000 --- a/src/shared/watchdog.c +++ /dev/null @@ -1,164 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "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; - -static int update_timeout(void) { - int r; - - if (watchdog_fd < 0) - return 0; - - if (watchdog_timeout == USEC_INFINITY) - return 0; - else if (watchdog_timeout == 0) { - int flags; - - flags = WDIOS_DISABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) - return log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); - } else { - int sec, flags; - char buf[FORMAT_TIMESPAN_MAX]; - - sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); - if (r < 0) - return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec); - - watchdog_timeout = (usec_t) sec * USEC_PER_SEC; - log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0)); - - flags = WDIOS_ENABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) { - /* ENOTTY means the watchdog is always enabled so we're fine */ - log_full(errno == ENOTTY ? LOG_DEBUG : LOG_WARNING, - "Failed to enable hardware watchdog: %m"); - if (errno != ENOTTY) - return -errno; - } - - r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) - return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); - } - - return 0; -} - -static int open_watchdog(void) { - struct watchdog_info ident; - - if (watchdog_fd >= 0) - return 0; - - watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); - if (watchdog_fd < 0) - return -errno; - - if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) - log_info("Hardware watchdog '%s', version %x", - ident.identity, - ident.firmware_version); - - return update_timeout(); -} - -int watchdog_set_timeout(usec_t *usec) { - int r; - - watchdog_timeout = *usec; - - /* If we didn't open the watchdog yet and didn't get any - * explicit timeout value set, don't do anything */ - if (watchdog_fd < 0 && watchdog_timeout == USEC_INFINITY) - return 0; - - if (watchdog_fd < 0) - r = open_watchdog(); - else - r = update_timeout(); - - *usec = watchdog_timeout; - - return r; -} - -int watchdog_ping(void) { - int r; - - if (watchdog_fd < 0) { - r = open_watchdog(); - if (r < 0) - return r; - } - - r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) - return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); - - return 0; -} - -void watchdog_close(bool disarm) { - int r; - - if (watchdog_fd < 0) - return; - - if (disarm) { - int flags; - - /* Explicitly disarm it */ - flags = WDIOS_DISABLECARD; - r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) - log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); - - /* To be sure, use magic close logic, too */ - for (;;) { - static const char v = 'V'; - - if (write(watchdog_fd, &v, 1) > 0) - break; - - if (errno != EINTR) { - log_error_errno(errno, "Failed to disarm watchdog timer: %m"); - break; - } - } - } - - watchdog_fd = safe_close(watchdog_fd); -} diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h deleted file mode 100644 index f6ec178ea1..0000000000 --- a/src/shared/watchdog.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "time-util.h" -#include "util.h" - -int watchdog_set_timeout(usec_t *usec); -int watchdog_ping(void); -void watchdog_close(bool disarm); diff --git a/src/sleep/Makefile b/src/sleep/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/sleep/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c deleted file mode 100644 index c8f0742183..0000000000 --- a/src/sleep/sleep.c +++ /dev/null @@ -1,215 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-messages.h" - -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "sleep-config.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static char* arg_verb = NULL; - -static int write_mode(char **modes) { - int r = 0; - char **mode; - - STRV_FOREACH(mode, modes) { - int k; - - k = write_string_file("/sys/power/disk", *mode, 0); - if (k == 0) - return 0; - - log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", - *mode); - if (r == 0) - r = k; - } - - if (r < 0) - log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); - - return r; -} - -static int write_state(FILE **f, char **states) { - char **state; - int r = 0; - - STRV_FOREACH(state, states) { - int k; - - k = write_string_stream(*f, *state, true); - if (k == 0) - return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", - *state); - if (r == 0) - r = k; - - fclose(*f); - *f = fopen("/sys/power/state", "we"); - if (!*f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - } - - return r; -} - -static int execute(char **modes, char **states) { - - char *arguments[] = { - NULL, - (char*) "pre", - arg_verb, - NULL - }; - static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; - - int r; - _cleanup_fclose_ FILE *f = NULL; - - /* This file is opened first, so that if we hit an error, - * we can abort before modifying any state. */ - f = fopen("/sys/power/state", "we"); - if (!f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - - /* Configure the hibernation mode */ - r = write_mode(modes); - if (r < 0) - return r; - - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), - LOG_MESSAGE("Suspending system..."), - "SLEEP=%s", arg_verb, - NULL); - - r = write_state(&f, states); - if (r < 0) - return r; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), - LOG_MESSAGE("System resumed."), - "SLEEP=%s", arg_verb, - NULL); - - arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - return r; -} - -static void help(void) { - printf("%s COMMAND\n\n" - "Suspend the system, hibernate the system, or both.\n\n" - "Commands:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " suspend Suspend the system\n" - " hibernate Hibernate the system\n" - " hybrid-sleep Both hibernate and suspend the system\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0; /* done */ - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc - optind != 1) { - log_error("Usage: %s COMMAND", - program_invocation_short_name); - return -EINVAL; - } - - arg_verb = argv[optind]; - - if (!streq(arg_verb, "suspend") && - !streq(arg_verb, "hibernate") && - !streq(arg_verb, "hybrid-sleep")) { - log_error("Unknown command '%s'.", arg_verb); - return -EINVAL; - } - - return 1 /* work to do */; -} - -int main(int argc, char *argv[]) { - _cleanup_strv_free_ char **modes = NULL, **states = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = parse_sleep_config(arg_verb, &modes, &states); - if (r < 0) - goto finish; - - r = execute(modes, states); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/socket-proxy/Makefile b/src/socket-proxy/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/socket-proxy/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c deleted file mode 100644 index 52b4db8875..0000000000 --- a/src/socket-proxy/socket-proxyd.c +++ /dev/null @@ -1,679 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 David Strauss - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . - ***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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" - -#define BUFFER_SIZE (256 * 1024) -#define CONNECTIONS_MAX 256 - -static const char *arg_remote_host = NULL; - -typedef struct Context { - sd_event *event; - sd_resolve *resolve; - - Set *listen; - Set *connections; -} Context; - -typedef struct Connection { - Context *context; - - int server_fd, client_fd; - int server_to_client_buffer[2]; /* a pipe */ - int client_to_server_buffer[2]; /* a pipe */ - - size_t server_to_client_buffer_full, client_to_server_buffer_full; - size_t server_to_client_buffer_size, client_to_server_buffer_size; - - sd_event_source *server_event_source, *client_event_source; - - sd_resolve_query *resolve_query; -} Connection; - -static void connection_free(Connection *c) { - assert(c); - - if (c->context) - set_remove(c->context->connections, c); - - sd_event_source_unref(c->server_event_source); - sd_event_source_unref(c->client_event_source); - - safe_close(c->server_fd); - safe_close(c->client_fd); - - safe_close_pair(c->server_to_client_buffer); - safe_close_pair(c->client_to_server_buffer); - - sd_resolve_query_unref(c->resolve_query); - - free(c); -} - -static void context_free(Context *context) { - sd_event_source *es; - Connection *c; - - assert(context); - - while ((es = set_steal_first(context->listen))) - sd_event_source_unref(es); - - while ((c = set_first(context->connections))) - connection_free(c); - - set_free(context->listen); - set_free(context->connections); - - sd_event_unref(context->event); - sd_resolve_unref(context->resolve); -} - -static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) { - int r; - - assert(c); - assert(buffer); - assert(sz); - - if (buffer[0] >= 0) - return 0; - - r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK); - if (r < 0) - return log_error_errno(errno, "Failed to allocate pipe buffer: %m"); - - (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE); - - r = fcntl(buffer[0], F_GETPIPE_SZ); - if (r < 0) - return log_error_errno(errno, "Failed to get pipe buffer size: %m"); - - assert(r > 0); - *sz = r; - - return 0; -} - -static int connection_shovel( - Connection *c, - int *from, int buffer[2], int *to, - size_t *full, size_t *sz, - sd_event_source **from_source, sd_event_source **to_source) { - - bool shoveled; - - assert(c); - assert(from); - assert(buffer); - assert(buffer[0] >= 0); - assert(buffer[1] >= 0); - assert(to); - assert(full); - assert(sz); - assert(from_source); - assert(to_source); - - do { - ssize_t z; - - shoveled = false; - - if (*full < *sz && *from >= 0 && *to >= 0) { - z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); - if (z > 0) { - *full += z; - shoveled = true; - } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { - *from_source = sd_event_source_unref(*from_source); - *from = safe_close(*from); - } else if (errno != EAGAIN && errno != EINTR) - return log_error_errno(errno, "Failed to splice: %m"); - } - - if (*full > 0 && *to >= 0) { - z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); - if (z > 0) { - *full -= z; - shoveled = true; - } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { - *to_source = sd_event_source_unref(*to_source); - *to = safe_close(*to); - } else if (errno != EAGAIN && errno != EINTR) - return log_error_errno(errno, "Failed to splice: %m"); - } - } while (shoveled); - - return 0; -} - -static int connection_enable_event_sources(Connection *c); - -static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Connection *c = userdata; - int r; - - assert(s); - assert(fd >= 0); - assert(c); - - r = connection_shovel(c, - &c->server_fd, c->server_to_client_buffer, &c->client_fd, - &c->server_to_client_buffer_full, &c->server_to_client_buffer_size, - &c->server_event_source, &c->client_event_source); - if (r < 0) - goto quit; - - r = connection_shovel(c, - &c->client_fd, c->client_to_server_buffer, &c->server_fd, - &c->client_to_server_buffer_full, &c->client_to_server_buffer_size, - &c->client_event_source, &c->server_event_source); - if (r < 0) - goto quit; - - /* EOF on both sides? */ - if (c->server_fd == -1 && c->client_fd == -1) - goto quit; - - /* Server closed, and all data written to client? */ - if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0) - goto quit; - - /* Client closed, and all data written to server? */ - if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0) - goto quit; - - r = connection_enable_event_sources(c); - if (r < 0) - goto quit; - - return 1; - -quit: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int connection_enable_event_sources(Connection *c) { - uint32_t a = 0, b = 0; - int r; - - assert(c); - - if (c->server_to_client_buffer_full > 0) - b |= EPOLLOUT; - if (c->server_to_client_buffer_full < c->server_to_client_buffer_size) - a |= EPOLLIN; - - if (c->client_to_server_buffer_full > 0) - a |= EPOLLOUT; - if (c->client_to_server_buffer_full < c->client_to_server_buffer_size) - b |= EPOLLIN; - - if (c->server_event_source) - r = sd_event_source_set_io_events(c->server_event_source, a); - else if (c->server_fd >= 0) - r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c); - else - r = 0; - - if (r < 0) - return log_error_errno(r, "Failed to set up server event source: %m"); - - if (c->client_event_source) - r = sd_event_source_set_io_events(c->client_event_source, b); - else if (c->client_fd >= 0) - r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c); - else - r = 0; - - if (r < 0) - return log_error_errno(r, "Failed to set up client event source: %m"); - - return 0; -} - -static int connection_complete(Connection *c) { - int r; - - assert(c); - - r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size); - if (r < 0) - goto fail; - - r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size); - if (r < 0) - goto fail; - - r = connection_enable_event_sources(c); - if (r < 0) - goto fail; - - return 0; - -fail: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Connection *c = userdata; - socklen_t solen; - int error, r; - - assert(s); - assert(fd >= 0); - assert(c); - - solen = sizeof(error); - r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen); - if (r < 0) { - log_error_errno(errno, "Failed to issue SO_ERROR: %m"); - goto fail; - } - - if (error != 0) { - log_error_errno(error, "Failed to connect to remote host: %m"); - goto fail; - } - - c->client_event_source = sd_event_source_unref(c->client_event_source); - - return connection_complete(c); - -fail: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) { - int r; - - assert(c); - assert(sa); - assert(salen); - - c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (c->client_fd < 0) { - log_error_errno(errno, "Failed to get remote socket: %m"); - goto fail; - } - - r = connect(c->client_fd, sa, salen); - if (r < 0) { - if (errno == EINPROGRESS) { - r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c); - if (r < 0) { - log_error_errno(r, "Failed to add connection socket: %m"); - goto fail; - } - - r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT); - if (r < 0) { - log_error_errno(r, "Failed to enable oneshot event source: %m"); - goto fail; - } - } else { - log_error_errno(errno, "Failed to connect to remote host: %m"); - goto fail; - } - } else { - r = connection_complete(c); - if (r < 0) - goto fail; - } - - return 0; - -fail: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { - Connection *c = userdata; - - assert(q); - assert(c); - - if (ret != 0) { - log_error("Failed to resolve host: %s", gai_strerror(ret)); - goto fail; - } - - c->resolve_query = sd_resolve_query_unref(c->resolve_query); - - return connection_start(c, ai->ai_addr, ai->ai_addrlen); - -fail: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int resolve_remote(Connection *c) { - - static const struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_ADDRCONFIG - }; - - union sockaddr_union sa = {}; - const char *node, *service; - int r; - - if (path_is_absolute(arg_remote_host)) { - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)); - return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - } - - if (arg_remote_host[0] == '@') { - sa.un.sun_family = AF_UNIX; - sa.un.sun_path[0] = 0; - strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1); - return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - } - - service = strrchr(arg_remote_host, ':'); - if (service) { - node = strndupa(arg_remote_host, service - arg_remote_host); - service++; - } else { - node = arg_remote_host; - service = "80"; - } - - log_debug("Looking up address info for %s:%s", node, service); - r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c); - if (r < 0) { - log_error_errno(r, "Failed to resolve remote host: %m"); - goto fail; - } - - return 0; - -fail: - connection_free(c); - return 0; /* ignore errors, continue serving */ -} - -static int add_connection_socket(Context *context, int fd) { - Connection *c; - int r; - - assert(context); - assert(fd >= 0); - - if (set_size(context->connections) > CONNECTIONS_MAX) { - log_warning("Hit connection limit, refusing connection."); - safe_close(fd); - return 0; - } - - r = set_ensure_allocated(&context->connections, NULL); - if (r < 0) { - log_oom(); - return 0; - } - - c = new0(Connection, 1); - if (!c) { - log_oom(); - return 0; - } - - c->context = context; - c->server_fd = fd; - c->client_fd = -1; - c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1; - c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1; - - r = set_put(context->connections, c); - if (r < 0) { - free(c); - log_oom(); - return 0; - } - - return resolve_remote(c); -} - -static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *peer = NULL; - Context *context = userdata; - int nfd = -1, r; - - assert(s); - assert(fd >= 0); - assert(revents & EPOLLIN); - assert(context); - - nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (nfd < 0) { - if (errno != -EAGAIN) - log_warning_errno(errno, "Failed to accept() socket: %m"); - } else { - getpeername_pretty(nfd, true, &peer); - log_debug("New connection from %s", strna(peer)); - - r = add_connection_socket(context, nfd); - if (r < 0) { - log_error_errno(r, "Failed to accept connection, ignoring: %m"); - safe_close(fd); - } - } - - r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); - if (r < 0) { - log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m"); - sd_event_exit(context->event, r); - return r; - } - - return 1; -} - -static int add_listen_socket(Context *context, int fd) { - sd_event_source *source; - int r; - - assert(context); - assert(fd >= 0); - - r = set_ensure_allocated(&context->listen, NULL); - if (r < 0) { - log_oom(); - return r; - } - - r = sd_is_socket(fd, 0, SOCK_STREAM, 1); - if (r < 0) - return log_error_errno(r, "Failed to determine socket type: %m"); - if (r == 0) { - log_error("Passed in socket is not a stream socket."); - return -EINVAL; - } - - r = fd_nonblock(fd, true); - if (r < 0) - return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m"); - - r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context); - if (r < 0) - return log_error_errno(r, "Failed to add event source: %m"); - - r = set_put(context->listen, source); - if (r < 0) { - log_error_errno(r, "Failed to add source to set: %m"); - sd_event_source_unref(source); - return r; - } - - /* Set the watcher to oneshot in case other processes are also - * watching to accept(). */ - r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "Failed to enable oneshot mode: %m"); - - return 0; -} - -static void help(void) { - printf("%1$s [HOST:PORT]\n" - "%1$s [SOCKET]\n\n" - "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n" - " -h --help Show this help\n" - " --version Show package version\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_IGNORE_ENV - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind >= argc) { - log_error("Not enough parameters."); - return -EINVAL; - } - - if (argc != optind+1) { - log_error("Too many parameters."); - return -EINVAL; - } - - arg_remote_host = argv[optind]; - return 1; -} - -int main(int argc, char *argv[]) { - Context context = {}; - int r, n, fd; - - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_event_default(&context.event); - if (r < 0) { - log_error_errno(r, "Failed to allocate event loop: %m"); - goto finish; - } - - r = sd_resolve_default(&context.resolve); - if (r < 0) { - log_error_errno(r, "Failed to allocate resolver: %m"); - goto finish; - } - - r = sd_resolve_attach_event(context.resolve, context.event, 0); - if (r < 0) { - log_error_errno(r, "Failed to attach resolver: %m"); - goto finish; - } - - sd_event_set_watchdog(context.event, true); - - n = sd_listen_fds(1); - if (n < 0) { - log_error("Failed to receive sockets from parent."); - r = n; - goto finish; - } else if (n == 0) { - log_error("Didn't get any sockets passed in."); - r = -EINVAL; - goto finish; - } - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - r = add_listen_socket(&context, fd); - if (r < 0) - goto finish; - } - - r = sd_event_loop(context.event); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - -finish: - context_free(&context); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c deleted file mode 100644 index ce8efce3d5..0000000000 --- a/src/stdio-bridge/stdio-bridge.c +++ /dev/null @@ -1,302 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "bus-internal.h" -#include "bus-util.h" -#include "build.h" -#include "log.h" -#include "util.h" - -#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket" - -const char *arg_bus_path = DEFAULT_BUS_PATH; - -static int help(void) { - - printf("%s [OPTIONS...]\n\n" - "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --bus-path=PATH Path to the kernel bus (default: %s)\n", - program_invocation_short_name, DEFAULT_BUS_PATH); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "bus-path", required_argument, NULL, 'p' }, - { NULL, 0, NULL, 0 } - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) { - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case '?': - return -EINVAL; - - case 'p': - arg_bus_path = optarg; - break; - - default: - log_error("Unknown option code %c", c); - return -EINVAL; - } - } - - return 1; -} - -int main(int argc, char *argv[]) { - _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL; - sd_id128_t server_id; - bool is_unix; - int r, in_fd, out_fd; - - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_listen_fds(0); - if (r == 0) { - in_fd = STDIN_FILENO; - out_fd = STDOUT_FILENO; - } else if (r == 1) { - in_fd = SD_LISTEN_FDS_START; - out_fd = SD_LISTEN_FDS_START; - } else { - log_error("Illegal number of file descriptors passed\n"); - goto finish; - } - - is_unix = - sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && - sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; - - r = sd_bus_new(&a); - if (r < 0) { - log_error_errno(r, "Failed to allocate bus: %m"); - goto finish; - } - - r = sd_bus_set_address(a, arg_bus_path); - if (r < 0) { - log_error_errno(r, "Failed to set address to connect to: %m"); - goto finish; - } - - r = sd_bus_negotiate_fds(a, is_unix); - if (r < 0) { - log_error_errno(r, "Failed to set FD negotiation: %m"); - goto finish; - } - - r = sd_bus_start(a); - if (r < 0) { - log_error_errno(r, "Failed to start bus client: %m"); - goto finish; - } - - r = sd_bus_get_bus_id(a, &server_id); - if (r < 0) { - log_error_errno(r, "Failed to get server ID: %m"); - goto finish; - } - - r = sd_bus_new(&b); - if (r < 0) { - log_error_errno(r, "Failed to allocate bus: %m"); - goto finish; - } - - r = sd_bus_set_fd(b, in_fd, out_fd); - if (r < 0) { - log_error_errno(r, "Failed to set fds: %m"); - goto finish; - } - - r = sd_bus_set_server(b, 1, server_id); - if (r < 0) { - log_error_errno(r, "Failed to set server mode: %m"); - goto finish; - } - - r = sd_bus_negotiate_fds(b, is_unix); - if (r < 0) { - log_error_errno(r, "Failed to set FD negotiation: %m"); - goto finish; - } - - r = sd_bus_set_anonymous(b, true); - if (r < 0) { - log_error_errno(r, "Failed to set anonymous authentication: %m"); - goto finish; - } - - r = sd_bus_start(b); - if (r < 0) { - log_error_errno(r, "Failed to start bus client: %m"); - goto finish; - } - - for (;;) { - _cleanup_(sd_bus_message_unrefp)sd_bus_message *m = NULL; - int events_a, events_b, fd; - uint64_t timeout_a, timeout_b, t; - struct timespec _ts, *ts; - - r = sd_bus_process(a, &m); - if (r < 0) { - log_error_errno(r, "Failed to process bus a: %m"); - goto finish; - } - - if (m) { - r = sd_bus_send(b, m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send message: %m"); - goto finish; - } - } - - if (r > 0) - continue; - - r = sd_bus_process(b, &m); - if (r < 0) { - /* treat 'connection reset by peer' as clean exit condition */ - if (r == -ECONNRESET) - r = 0; - - goto finish; - } - - if (m) { - r = sd_bus_send(a, m, NULL); - if (r < 0) { - log_error_errno(r, "Failed to send message: %m"); - goto finish; - } - } - - if (r > 0) - continue; - - fd = sd_bus_get_fd(a); - if (fd < 0) { - r = fd; - log_error_errno(r, "Failed to get fd: %m"); - goto finish; - } - - events_a = sd_bus_get_events(a); - if (events_a < 0) { - r = events_a; - log_error_errno(r, "Failed to get events mask: %m"); - goto finish; - } - - r = sd_bus_get_timeout(a, &timeout_a); - if (r < 0) { - log_error_errno(r, "Failed to get timeout: %m"); - goto finish; - } - - events_b = sd_bus_get_events(b); - if (events_b < 0) { - r = events_b; - log_error_errno(r, "Failed to get events mask: %m"); - goto finish; - } - - r = sd_bus_get_timeout(b, &timeout_b); - if (r < 0) { - log_error_errno(r, "Failed to get timeout: %m"); - goto finish; - } - - t = timeout_a; - if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a)) - t = timeout_b; - - if (t == (uint64_t) -1) - ts = NULL; - else { - usec_t nw; - - nw = now(CLOCK_MONOTONIC); - if (t > nw) - t -= nw; - else - t = 0; - - ts = timespec_store(&_ts, t); - } - - { - struct pollfd p[3] = { - {.fd = fd, .events = events_a, }, - {.fd = STDIN_FILENO, .events = events_b & POLLIN, }, - {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }}; - - r = ppoll(p, ELEMENTSOF(p), ts, NULL); - } - if (r < 0) { - log_error("ppoll() failed: %m"); - goto finish; - } - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/sysctl/Makefile b/src/sysctl/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/sysctl/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c deleted file mode 100644 index ce7c26e7d3..0000000000 --- a/src/sysctl/sysctl.c +++ /dev/null @@ -1,287 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "conf-files.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.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_PATHS_NULSTR("sysctl.d"); - -static int apply_all(Hashmap *sysctl_options) { - char *property, *value; - Iterator i; - int r = 0; - - HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { - int k; - - k = sysctl_write(property, value); - if (k < 0) { - log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k, - "Couldn't write '%s' to '%s', ignoring: %m", value, property); - - if (r == 0 && k != -ENOENT) - r = k; - } - } - - return r; -} - -static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(path); - - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); - } - - log_debug("Parsing %s", path); - while (!feof(f)) { - char l[LINE_MAX], *p, *value, *new_value, *property, *existing; - void *v; - int k; - - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); - } - - p = strstrip(l); - if (!*p) - continue; - - if (strchr(COMMENTS "\n", *p)) - continue; - - value = strchr(p, '='); - if (!value) { - log_error("Line is not an assignment in file '%s': %s", path, value); - - if (r == 0) - r = -EINVAL; - continue; - } - - *value = 0; - value++; - - p = sysctl_normalize(strstrip(p)); - value = strstrip(value); - - if (!strv_isempty(arg_prefixes)) { - char **i, *t; - STRV_FOREACH(i, arg_prefixes) { - t = path_startswith(*i, "/proc/sys/"); - if (t == NULL) - t = *i; - if (path_startswith(p, t)) - goto found; - } - /* not found */ - continue; - } - -found: - existing = hashmap_get2(sysctl_options, p, &v); - if (existing) { - if (streq(value, existing)) - continue; - - log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path); - free(hashmap_remove(sysctl_options, p)); - free(v); - } - - property = strdup(p); - if (!property) - return log_oom(); - - new_value = strdup(value); - if (!new_value) { - free(property); - return log_oom(); - } - - k = hashmap_put(sysctl_options, property, new_value); - if (k < 0) { - log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); - free(property); - free(new_value); - return k; - } - } - - return r; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Applies kernel sysctl settings.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --prefix=PATH Only apply rules with the specified prefix\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_PREFIX - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "prefix", required_argument, NULL, ARG_PREFIX }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_PREFIX: { - char *p; - - /* We used to require people to specify absolute paths - * in /proc/sys in the past. This is kinda useless, but - * we need to keep compatibility. We now support any - * sysctl name available. */ - sysctl_normalize(optarg); - - if (startswith(optarg, "/proc/sys")) - p = strdup(optarg); - else - p = strappend("/proc/sys/", optarg); - if (!p) - return log_oom(); - - if (strv_consume(&arg_prefixes, p) < 0) - return log_oom(); - - break; - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r = 0, k; - Hashmap *sysctl_options; - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - sysctl_options = hashmap_new(&string_hash_ops); - if (!sysctl_options) { - r = log_oom(); - goto finish; - } - - r = 0; - - if (argc > optind) { - int i; - - for (i = optind; i < argc; i++) { - k = parse_file(sysctl_options, argv[i], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = parse_file(sysctl_options, *f, true); - if (k < 0 && r == 0) - r = k; - } - } - - k = apply_all(sysctl_options); - if (k < 0 && r == 0) - r = k; - -finish: - hashmap_free_free_free(sysctl_options); - strv_free(arg_prefixes); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/system-update-generator/Makefile b/src/system-update-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/system-update-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c deleted file mode 100644 index a3d677f068..0000000000 --- a/src/system-update-generator/system-update-generator.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fs-util.h" -#include "log.h" -#include "string-util.h" -#include "util.h" - -/* - * Implements the logic described in - * http://freedesktop.org/wiki/Software/systemd/SystemUpdates - */ - -static const char *arg_dest = "/tmp"; - -static int generate_symlink(void) { - const char *p = NULL; - - if (laccess("/system-update", F_OK) < 0) { - if (errno == ENOENT) - return 0; - - log_error_errno(errno, "Failed to check for system update: %m"); - return -EINVAL; - } - - p = strjoina(arg_dest, "/default.target"); - if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", p); - - return 0; -} - -int main(int argc, char *argv[]) { - int r; - - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[2]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - r = generate_symlink(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/systemctl/Makefile b/src/systemctl/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/systemctl/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c deleted file mode 100644 index 6a0ed79a53..0000000000 --- a/src/systemctl/systemctl.c +++ /dev/null @@ -1,8075 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Marc-Antoine Perennou - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.h" -#include "sd-login.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-message.h" -#include "bus-unit-util.h" -#include "bus-util.h" -#include "cgroup-show.h" -#include "cgroup-util.h" -#include "copy.h" -#include "dropin.h" -#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 "sigbus.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" - -/* The init script exit status codes - 0 program is running or service is OK - 1 program is dead and /var/run pid file exists - 2 program is dead and /var/lock lock file exists - 3 program is not running - 4 program or service status is unknown - 5-99 reserved for future LSB use - 100-149 reserved for distribution use - 150-199 reserved for application use - 200-254 reserved -*/ -enum { - EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, - EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, - EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, - EXIT_PROGRAM_NOT_RUNNING = 3, - EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, -}; - -static char **arg_types = NULL; -static char **arg_states = NULL; -static char **arg_properties = NULL; -static bool arg_all = false; -static enum dependency { - DEPENDENCY_FORWARD, - DEPENDENCY_REVERSE, - DEPENDENCY_AFTER, - DEPENDENCY_BEFORE, - _DEPENDENCY_MAX -} arg_dependency = DEPENDENCY_FORWARD; -static const char *arg_job_mode = "replace"; -static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; -static bool arg_no_block = false; -static bool arg_no_legend = false; -static bool arg_no_pager = false; -static bool arg_no_wtmp = false; -static bool arg_no_sync = false; -static bool arg_no_wall = false; -static bool arg_no_reload = false; -static bool arg_value = false; -static bool arg_show_types = false; -static bool arg_ignore_inhibitors = false; -static bool arg_dry = false; -static bool arg_quiet = false; -static bool arg_full = false; -static bool arg_recursive = false; -static int arg_force = 0; -static bool arg_ask_password = false; -static bool arg_runtime = false; -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 char *arg_root = NULL; -static usec_t arg_when = 0; -static enum action { - _ACTION_INVALID, - ACTION_SYSTEMCTL, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_KEXEC, - ACTION_EXIT, - ACTION_SUSPEND, - ACTION_HIBERNATE, - ACTION_HYBRID_SLEEP, - ACTION_RUNLEVEL2, - ACTION_RUNLEVEL3, - ACTION_RUNLEVEL4, - ACTION_RUNLEVEL5, - ACTION_RESCUE, - ACTION_EMERGENCY, - ACTION_DEFAULT, - ACTION_RELOAD, - ACTION_REEXEC, - ACTION_RUNLEVEL, - ACTION_CANCEL_SHUTDOWN, - _ACTION_MAX -} arg_action = ACTION_SYSTEMCTL; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -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 int daemon_reload(int argc, char *argv[], void* userdata); -static int trivial_method(int argc, char *argv[], void *userdata); -static int halt_now(enum action a); -static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state); - -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 int map_string_no_copy(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { - char *s; - const char **p = userdata; - int r; - - r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &s); - if (r < 0) - return r; - - if (!isempty(s)) - *p = s; - - return 0; -} - -static void ask_password_agent_open_if_enabled(void) { - - /* Open the password agent as a child process if necessary */ - - if (!arg_ask_password) - return; - - if (arg_scope != UNIT_FILE_SYSTEM) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - ask_password_agent_open(); -} - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - - if (!arg_ask_password) - return; - - if (arg_scope != UNIT_FILE_SYSTEM) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -static OutputFlags get_output_flags(void) { - return - arg_all * OUTPUT_SHOW_ALL | - arg_full * OUTPUT_FULL_WIDTH | - (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | - colors_enabled() * OUTPUT_COLOR | - !arg_quiet * OUTPUT_WARN_CUTOFF; -} - -static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) { - assert(error); - - if (!sd_bus_error_is_set(error)) - return r; - - if (sd_bus_error_has_name(error, SD_BUS_ERROR_ACCESS_DENIED) || - sd_bus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) || - sd_bus_error_has_name(error, BUS_ERROR_NO_ISOLATION) || - sd_bus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE)) - return EXIT_NOPERMISSION; - - if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT)) - return EXIT_NOTINSTALLED; - - if (sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) || - sd_bus_error_has_name(error, SD_BUS_ERROR_NOT_SUPPORTED)) - return EXIT_NOTIMPLEMENTED; - - if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) - return EXIT_NOTCONFIGURED; - - if (r != 0) - return r; - - return EXIT_FAILURE; -} - -static bool install_client_side(void) { - - /* Decides when to execute enable/disable/... operations - * client-side rather than server-side. */ - - if (running_in_chroot() > 0) - return true; - - if (sd_booted() <= 0) - return true; - - if (!isempty(arg_root)) - return true; - - 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; -} - -static int compare_unit_info(const void *a, const void *b) { - const UnitInfo *u = a, *v = b; - const char *d1, *d2; - int r; - - /* First, order by machine */ - if (!u->machine && v->machine) - return -1; - if (u->machine && !v->machine) - return 1; - if (u->machine && v->machine) { - r = strcasecmp(u->machine, v->machine); - if (r != 0) - return r; - } - - /* Second, order by unit type */ - d1 = strrchr(u->id, '.'); - d2 = strrchr(v->id, '.'); - if (d1 && d2) { - r = strcasecmp(d1, d2); - if (r != 0) - return r; - } - - /* Third, order by name */ - return strcasecmp(u->id, v->id); -} - -static bool output_show_unit(const UnitInfo *u, char **patterns) { - assert(u); - - if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) - return false; - - if (arg_types) { - const char *dot; - - dot = strrchr(u->id, '.'); - if (!dot) - return false; - - if (!strv_find(arg_types, dot+1)) - return false; - } - - if (arg_all) - return true; - - /* Note that '--all' is not purely a state filter, but also a - * filter that hides units that "follow" other units (which is - * used for device units that appear under different names). */ - if (!isempty(u->following)) - return false; - - if (!strv_isempty(arg_states)) - return true; - - /* By default show all units except the ones in inactive - * state and with no pending job */ - if (u->job_id > 0) - return true; - - if (streq(u->active_state, "inactive")) - return false; - - return true; -} - -static int output_units_list(const UnitInfo *unit_infos, unsigned c) { - unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len; - const UnitInfo *u; - unsigned n_shown = 0; - int job_count = 0; - - max_id_len = strlen("UNIT"); - load_len = strlen("LOAD"); - active_len = strlen("ACTIVE"); - sub_len = strlen("SUB"); - job_len = strlen("JOB"); - desc_len = 0; - - for (u = unit_infos; u < unit_infos + c; u++) { - max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0)); - load_len = MAX(load_len, strlen(u->load_state)); - active_len = MAX(active_len, strlen(u->active_state)); - sub_len = MAX(sub_len, strlen(u->sub_state)); - - if (u->job_id != 0) { - job_len = MAX(job_len, strlen(u->job_type)); - job_count++; - } - - if (!arg_no_legend && - (streq(u->active_state, "failed") || - STR_IN_SET(u->load_state, "error", "not-found", "masked"))) - circle_len = 2; - } - - if (!arg_full && original_stdout_is_tty) { - unsigned basic_len; - - id_len = MIN(max_id_len, 25u); - basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len; - - if (job_count) - basic_len += job_len + 1; - - if (basic_len < (unsigned) columns()) { - unsigned extra_len, incr; - extra_len = columns() - basic_len; - - /* Either UNIT already got 25, or is fully satisfied. - * Grant up to 25 to DESC now. */ - incr = MIN(extra_len, 25u); - desc_len += incr; - extra_len -= incr; - - /* split the remaining space between UNIT and DESC, - * but do not give UNIT more than it needs. */ - if (extra_len > 0) { - incr = MIN(extra_len / 2, max_id_len - id_len); - id_len += incr; - desc_len += extra_len - incr; - } - } - } else - id_len = max_id_len; - - for (u = unit_infos; u < unit_infos + c; u++) { - _cleanup_free_ char *e = NULL, *j = NULL; - const char *on_loaded = "", *off_loaded = ""; - const char *on_active = "", *off_active = ""; - const char *on_circle = "", *off_circle = ""; - const char *id; - bool circle = false; - - if (!n_shown && !arg_no_legend) { - - if (circle_len > 0) - fputs(" ", stdout); - - printf("%-*s %-*s %-*s %-*s ", - id_len, "UNIT", - load_len, "LOAD", - active_len, "ACTIVE", - sub_len, "SUB"); - - if (job_count) - printf("%-*s ", job_len, "JOB"); - - if (!arg_full && arg_no_pager) - printf("%.*s\n", desc_len, "DESCRIPTION"); - else - printf("%s\n", "DESCRIPTION"); - } - - n_shown++; - - if (STR_IN_SET(u->load_state, "error", "not-found", "masked") && !arg_plain) { - on_loaded = ansi_highlight_red(); - on_circle = ansi_highlight_yellow(); - off_loaded = off_circle = ansi_normal(); - circle = true; - } else if (streq(u->active_state, "failed") && !arg_plain) { - on_circle = on_active = ansi_highlight_red(); - off_circle = off_active = ansi_normal(); - circle = true; - } - - if (u->machine) { - j = strjoin(u->machine, ":", u->id, NULL); - if (!j) - return log_oom(); - - id = j; - } else - id = u->id; - - if (arg_full) { - e = ellipsize(id, id_len, 33); - if (!e) - return log_oom(); - - id = e; - } - - if (circle_len > 0) - printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle); - - printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", - on_active, id_len, id, off_active, - on_loaded, load_len, u->load_state, off_loaded, - on_active, active_len, u->active_state, - sub_len, u->sub_state, off_active, - job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); - - if (desc_len > 0) - printf("%.*s\n", desc_len, u->description); - else - printf("%s\n", u->description); - } - - if (!arg_no_legend) { - const char *on, *off; - - if (n_shown) { - puts("\n" - "LOAD = Reflects whether the unit definition was properly loaded.\n" - "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" - "SUB = The low-level unit activation state, values depend on unit type."); - puts(job_count ? "JOB = Pending job for the unit.\n" : ""); - on = ansi_highlight(); - off = ansi_normal(); - } else { - on = ansi_highlight_red(); - off = ansi_normal(); - } - - if (arg_all) - printf("%s%u loaded units listed.%s\n" - "To show all installed unit files use 'systemctl list-unit-files'.\n", - on, n_shown, off); - else - printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n" - "To show all installed unit files use 'systemctl list-unit-files'.\n", - on, n_shown, off); - } - - return 0; -} - -static int get_unit_list( - sd_bus *bus, - const char *machine, - char **patterns, - UnitInfo **unit_infos, - int c, - sd_bus_message **_reply) { - - _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; - bool fallback = false; - - assert(bus); - assert(unit_infos); - assert(_reply); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitsByPatterns"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, arg_states); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, patterns); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0 && (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || - sd_bus_error_has_name(&error, SD_BUS_ERROR_ACCESS_DENIED))) { - /* Fallback to legacy ListUnitsFiltered method */ - fallback = true; - log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); - m = sd_bus_message_unref(m); - sd_bus_error_free(&error); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitsFiltered"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, arg_states); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - } - if (r < 0) - return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - u.machine = machine; - - if (!output_show_unit(&u, fallback ? patterns : NULL)) - continue; - - if (!GREEDY_REALLOC(*unit_infos, size, c+1)) - return log_oom(); - - (*unit_infos)[c++] = u; - } - 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); - - *_reply = reply; - reply = NULL; - - return c; -} - -static void message_set_freep(Set **set) { - sd_bus_message *m; - - while ((m = set_steal_first(*set))) - sd_bus_message_unref(m); - - set_free(*set); -} - -static int get_unit_list_recursive( - sd_bus *bus, - char **patterns, - UnitInfo **_unit_infos, - Set **_replies, - char ***_machines) { - - _cleanup_free_ UnitInfo *unit_infos = NULL; - _cleanup_(message_set_freep) Set *replies; - sd_bus_message *reply; - int c, r; - - assert(bus); - assert(_replies); - assert(_unit_infos); - assert(_machines); - - replies = set_new(NULL); - if (!replies) - return log_oom(); - - c = get_unit_list(bus, NULL, patterns, &unit_infos, 0, &reply); - if (c < 0) - return c; - - r = set_put(replies, reply); - if (r < 0) { - sd_bus_message_unref(reply); - return log_oom(); - } - - if (arg_recursive) { - _cleanup_strv_free_ char **machines = NULL; - char **i; - - r = sd_get_machine_names(&machines); - if (r < 0) - return log_error_errno(r, "Failed to get machine names: %m"); - - STRV_FOREACH(i, machines) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL; - int k; - - r = sd_bus_open_system_machine(&container, *i); - if (r < 0) { - log_warning_errno(r, "Failed to connect to container %s, ignoring: %m", *i); - continue; - } - - k = get_unit_list(container, *i, patterns, &unit_infos, c, &reply); - if (k < 0) - return k; - - c = k; - - r = set_put(replies, reply); - if (r < 0) { - sd_bus_message_unref(reply); - return log_oom(); - } - } - - *_machines = machines; - machines = NULL; - } else - *_machines = NULL; - - *_unit_infos = unit_infos; - unit_infos = NULL; - - *_replies = replies; - replies = NULL; - - return c; -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); - if (r < 0) - return r; - - qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info); - return output_units_list(unit_infos, r); -} - -static int get_triggered_units( - sd_bus *bus, - const char* path, - char*** ret) { - - _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", - path, - "org.freedesktop.systemd1.Unit", - "Triggers", - &error, - ret); - if (r < 0) - return log_error_errno(r, "Failed to determine triggers: %s", bus_error_message(&error, r)); - - return 0; -} - -static int get_listening( - sd_bus *bus, - const char* unit_path, - char*** listening) { - - _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; - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - unit_path, - "org.freedesktop.systemd1.Socket", - "Listen", - &error, - &reply, - "a(ss)"); - 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) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ss)", &type, &path)) > 0) { - - r = strv_extend(listening, type); - if (r < 0) - return log_oom(); - - r = strv_extend(listening, path); - if (r < 0) - return log_oom(); - - n++; - } - 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); - - return n; -} - -struct socket_info { - const char *machine; - const char* id; - - char* type; - char* path; - - /* Note: triggered is a list here, although it almost certainly - * will always be one unit. Nevertheless, dbus API allows for multiple - * values, so let's follow that. */ - char** triggered; - - /* The strv above is shared. free is set only in the first one. */ - bool own_triggered; -}; - -static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) { - int o; - - assert(a); - assert(b); - - if (!a->machine && b->machine) - return -1; - if (a->machine && !b->machine) - return 1; - if (a->machine && b->machine) { - o = strcasecmp(a->machine, b->machine); - if (o != 0) - return o; - } - - o = strcmp(a->path, b->path); - if (o == 0) - o = strcmp(a->type, b->type); - - return o; -} - -static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { - struct socket_info *s; - unsigned pathlen = strlen("LISTEN"), - typelen = strlen("TYPE") * arg_show_types, - socklen = strlen("UNIT"), - servlen = strlen("ACTIVATES"); - const char *on, *off; - - for (s = socket_infos; s < socket_infos + cs; s++) { - unsigned tmp = 0; - char **a; - - socklen = MAX(socklen, strlen(s->id)); - if (arg_show_types) - typelen = MAX(typelen, strlen(s->type)); - pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0)); - - STRV_FOREACH(a, s->triggered) - tmp += strlen(*a) + 2*(a != s->triggered); - servlen = MAX(servlen, tmp); - } - - if (cs) { - if (!arg_no_legend) - printf("%-*s %-*.*s%-*s %s\n", - pathlen, "LISTEN", - typelen + arg_show_types, typelen + arg_show_types, "TYPE ", - socklen, "UNIT", - "ACTIVATES"); - - for (s = socket_infos; s < socket_infos + cs; s++) { - _cleanup_free_ char *j = NULL; - const char *path; - char **a; - - if (s->machine) { - j = strjoin(s->machine, ":", s->path, NULL); - if (!j) - return log_oom(); - path = j; - } else - path = s->path; - - if (arg_show_types) - printf("%-*s %-*s %-*s", - pathlen, path, typelen, s->type, socklen, s->id); - else - printf("%-*s %-*s", - pathlen, path, socklen, s->id); - STRV_FOREACH(a, s->triggered) - printf("%s %s", - a == s->triggered ? "" : ",", *a); - printf("\n"); - } - - on = ansi_highlight(); - off = ansi_normal(); - if (!arg_no_legend) - printf("\n"); - } else { - on = ansi_highlight_red(); - off = ansi_normal(); - } - - if (!arg_no_legend) { - printf("%s%u sockets listed.%s\n", on, cs, off); - if (!arg_all) - printf("Pass --all to see loaded but inactive sockets, too.\n"); - } - - return 0; -} - -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; - _cleanup_free_ struct socket_info *socket_infos = NULL; - const UnitInfo *u; - struct socket_info *s; - unsigned cs = 0; - size_t size = 0; - int r = 0, n; - sd_bus *bus; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); - if (n < 0) - return n; - - for (u = unit_infos; u < unit_infos + n; u++) { - _cleanup_strv_free_ char **listening = NULL, **triggered = NULL; - int i, c; - - if (!endswith(u->id, ".socket")) - continue; - - r = get_triggered_units(bus, u->unit_path, &triggered); - if (r < 0) - goto cleanup; - - c = get_listening(bus, u->unit_path, &listening); - if (c < 0) { - r = c; - goto cleanup; - } - - if (!GREEDY_REALLOC(socket_infos, size, cs + c)) { - r = log_oom(); - goto cleanup; - } - - for (i = 0; i < c; i++) - socket_infos[cs + i] = (struct socket_info) { - .machine = u->machine, - .id = u->id, - .type = listening[i*2], - .path = listening[i*2 + 1], - .triggered = triggered, - .own_triggered = i==0, - }; - - /* from this point on we will cleanup those socket_infos */ - cs += c; - free(listening); - listening = triggered = NULL; /* avoid cleanup */ - } - - qsort_safe(socket_infos, cs, sizeof(struct socket_info), - (__compar_fn_t) socket_info_compare); - - output_sockets_list(socket_infos, cs); - - cleanup: - assert(cs == 0 || socket_infos); - for (s = socket_infos; s < socket_infos + cs; s++) { - free(s->type); - free(s->path); - if (s->own_triggered) - strv_free(s->triggered); - } - - return r; -} - -static int get_next_elapse( - sd_bus *bus, - const char *path, - dual_timestamp *next) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - dual_timestamp t; - int r; - - assert(bus); - assert(path); - assert(next); - - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Timer", - "NextElapseUSecMonotonic", - &error, - 't', - &t.monotonic); - 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, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Timer", - "NextElapseUSecRealtime", - &error, - 't', - &t.realtime); - if (r < 0) - return log_error_errno(r, "Failed to get next elapsation time: %s", bus_error_message(&error, r)); - - *next = t; - return 0; -} - -static int get_last_trigger( - sd_bus *bus, - const char *path, - usec_t *last) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(path); - assert(last); - - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Timer", - "LastTriggerUSec", - &error, - 't', - last); - if (r < 0) - return log_error_errno(r, "Failed to get last trigger time: %s", bus_error_message(&error, r)); - - return 0; -} - -struct timer_info { - const char* machine; - const char* id; - usec_t next_elapse; - usec_t last_trigger; - char** triggered; -}; - -static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) { - int o; - - assert(a); - assert(b); - - if (!a->machine && b->machine) - return -1; - if (a->machine && !b->machine) - return 1; - if (a->machine && b->machine) { - o = strcasecmp(a->machine, b->machine); - if (o != 0) - return o; - } - - if (a->next_elapse < b->next_elapse) - return -1; - if (a->next_elapse > b->next_elapse) - return 1; - - return strcmp(a->id, b->id); -} - -static int output_timers_list(struct timer_info *timer_infos, unsigned n) { - struct timer_info *t; - unsigned - nextlen = strlen("NEXT"), - leftlen = strlen("LEFT"), - lastlen = strlen("LAST"), - passedlen = strlen("PASSED"), - unitlen = strlen("UNIT"), - activatelen = strlen("ACTIVATES"); - - const char *on, *off; - - assert(timer_infos || n == 0); - - for (t = timer_infos; t < timer_infos + n; t++) { - unsigned ul = 0; - char **a; - - if (t->next_elapse > 0) { - char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = ""; - - format_timestamp(tstamp, sizeof(tstamp), t->next_elapse); - nextlen = MAX(nextlen, strlen(tstamp) + 1); - - format_timestamp_relative(trel, sizeof(trel), t->next_elapse); - leftlen = MAX(leftlen, strlen(trel)); - } - - if (t->last_trigger > 0) { - char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = ""; - - format_timestamp(tstamp, sizeof(tstamp), t->last_trigger); - lastlen = MAX(lastlen, strlen(tstamp) + 1); - - format_timestamp_relative(trel, sizeof(trel), t->last_trigger); - passedlen = MAX(passedlen, strlen(trel)); - } - - unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0)); - - STRV_FOREACH(a, t->triggered) - ul += strlen(*a) + 2*(a != t->triggered); - - activatelen = MAX(activatelen, ul); - } - - if (n > 0) { - if (!arg_no_legend) - printf("%-*s %-*s %-*s %-*s %-*s %s\n", - nextlen, "NEXT", - leftlen, "LEFT", - lastlen, "LAST", - passedlen, "PASSED", - unitlen, "UNIT", - "ACTIVATES"); - - for (t = timer_infos; t < timer_infos + n; t++) { - _cleanup_free_ char *j = NULL; - const char *unit; - char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a"; - char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a"; - char **a; - - format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse); - format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse); - - format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger); - format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger); - - if (t->machine) { - j = strjoin(t->machine, ":", t->id, NULL); - if (!j) - return log_oom(); - unit = j; - } else - unit = t->id; - - printf("%-*s %-*s %-*s %-*s %-*s", - nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit); - - STRV_FOREACH(a, t->triggered) - printf("%s %s", - a == t->triggered ? "" : ",", *a); - printf("\n"); - } - - on = ansi_highlight(); - off = ansi_normal(); - if (!arg_no_legend) - printf("\n"); - } else { - on = ansi_highlight_red(); - off = ansi_normal(); - } - - if (!arg_no_legend) { - printf("%s%u timers listed.%s\n", on, n, off); - if (!arg_all) - printf("Pass --all to see loaded but inactive timers, too.\n"); - } - - return 0; -} - -static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) { - usec_t next_elapse; - - assert(nw); - assert(next); - - if (next->monotonic != USEC_INFINITY && next->monotonic > 0) { - usec_t converted; - - if (next->monotonic > nw->monotonic) - converted = nw->realtime + (next->monotonic - nw->monotonic); - else - converted = nw->realtime - (nw->monotonic - next->monotonic); - - if (next->realtime != USEC_INFINITY && next->realtime > 0) - next_elapse = MIN(converted, next->realtime); - else - next_elapse = converted; - - } else - next_elapse = next->realtime; - - return next_elapse; -} - -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; - _cleanup_free_ UnitInfo *unit_infos = NULL; - struct timer_info *t; - const UnitInfo *u; - size_t size = 0; - int n, c = 0; - dual_timestamp nw; - sd_bus *bus; - int r = 0; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); - if (n < 0) - return n; - - dual_timestamp_get(&nw); - - for (u = unit_infos; u < unit_infos + n; u++) { - _cleanup_strv_free_ char **triggered = NULL; - dual_timestamp next = DUAL_TIMESTAMP_NULL; - usec_t m, last = 0; - - if (!endswith(u->id, ".timer")) - continue; - - r = get_triggered_units(bus, u->unit_path, &triggered); - if (r < 0) - goto cleanup; - - r = get_next_elapse(bus, u->unit_path, &next); - if (r < 0) - goto cleanup; - - get_last_trigger(bus, u->unit_path, &last); - - if (!GREEDY_REALLOC(timer_infos, size, c+1)) { - r = log_oom(); - goto cleanup; - } - - m = calc_next_elapse(&nw, &next); - - timer_infos[c++] = (struct timer_info) { - .machine = u->machine, - .id = u->id, - .next_elapse = m, - .last_trigger = last, - .triggered = triggered, - }; - - triggered = NULL; /* avoid cleanup */ - } - - qsort_safe(timer_infos, c, sizeof(struct timer_info), - (__compar_fn_t) timer_info_compare); - - output_timers_list(timer_infos, c); - - cleanup: - for (t = timer_infos; t < timer_infos + c; t++) - strv_free(t->triggered); - - return r; -} - -static int compare_unit_file_list(const void *a, const void *b) { - const char *d1, *d2; - const UnitFileList *u = a, *v = b; - - d1 = strrchr(u->path, '.'); - d2 = strrchr(v->path, '.'); - - if (d1 && d2) { - int r; - - r = strcasecmp(d1, d2); - if (r != 0) - return r; - } - - return strcasecmp(basename(u->path), basename(v->path)); -} - -static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) { - assert(u); - - if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE)) - return false; - - if (!strv_isempty(arg_types)) { - const char *dot; - - dot = strrchr(u->path, '.'); - if (!dot) - return false; - - if (!strv_find(arg_types, dot+1)) - return false; - } - - if (!strv_isempty(states) && - !strv_find(states, unit_file_state_to_string(u->state))) - return false; - - return true; -} - -static void output_unit_file_list(const UnitFileList *units, unsigned c) { - unsigned max_id_len, id_cols, state_cols; - const UnitFileList *u; - - max_id_len = strlen("UNIT FILE"); - state_cols = strlen("STATE"); - - for (u = units; u < units + c; u++) { - max_id_len = MAX(max_id_len, strlen(basename(u->path))); - state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state))); - } - - if (!arg_full) { - unsigned basic_cols; - - id_cols = MIN(max_id_len, 25u); - basic_cols = 1 + id_cols + state_cols; - if (basic_cols < (unsigned) columns()) - id_cols += MIN(columns() - basic_cols, max_id_len - id_cols); - } else - id_cols = max_id_len; - - if (!arg_no_legend && c > 0) - printf("%-*s %-*s\n", - id_cols, "UNIT FILE", - state_cols, "STATE"); - - for (u = units; u < units + c; u++) { - _cleanup_free_ char *e = NULL; - const char *on, *off; - const char *id; - - if (IN_SET(u->state, - UNIT_FILE_MASKED, - UNIT_FILE_MASKED_RUNTIME, - UNIT_FILE_DISABLED, - UNIT_FILE_BAD)) { - on = ansi_highlight_red(); - off = ansi_normal(); - } else if (u->state == UNIT_FILE_ENABLED) { - on = ansi_highlight_green(); - off = ansi_normal(); - } else - on = off = ""; - - id = basename(u->path); - - e = arg_full ? NULL : ellipsize(id, id_cols, 33); - - printf("%-*s %s%-*s%s\n", - id_cols, e ? e : id, - on, state_cols, unit_file_state_to_string(u->state), off); - } - - if (!arg_no_legend) - printf("\n%u unit files listed.\n", c); -} - -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; - unsigned c = 0; - const char *state; - char *path; - int r; - bool fallback = false; - - if (install_client_side()) { - Hashmap *h; - UnitFileList *u; - Iterator i; - unsigned n_units; - - h = hashmap_new(&string_hash_ops); - if (!h) - return log_oom(); - - r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1)); - if (r < 0) { - unit_file_list_free(h); - return log_error_errno(r, "Failed to get unit file list: %m"); - } - - n_units = hashmap_size(h); - - units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */ - if (!units) { - unit_file_list_free(h); - return log_oom(); - } - - HASHMAP_FOREACH(u, h, i) { - if (!output_show_unit_file(u, NULL, NULL)) - continue; - - units[c++] = *u; - free(u); - } - - assert(c <= n_units); - hashmap_free(h); - - r = 0; - } else { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = 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_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitFilesByPatterns"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, arg_states); - if (r < 0) - return bus_log_create_error(r); - - 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, &reply); - if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { - /* Fallback to legacy ListUnitFiles method */ - fallback = true; - log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r)); - m = sd_bus_message_unref(m); - sd_bus_error_free(&error); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnitFiles"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - } - 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) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) { - - if (!GREEDY_REALLOC(units, size, c + 1)) - return log_oom(); - - units[c] = (struct UnitFileList) { - path, - unit_file_state_from_string(state) - }; - - if (output_show_unit_file(&units[c], - fallback ? arg_states : NULL, - fallback ? strv_skip(argv, 1) : NULL)) - 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); - } - - pager_open(arg_no_pager, false); - - qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list); - output_unit_file_list(units, c); - - if (install_client_side()) - for (unit = units; unit < units + c; unit++) - free(unit->path); - - return 0; -} - -static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) { - _cleanup_free_ char *n = NULL; - size_t max_len = MAX(columns(),20u); - size_t len = 0; - int i; - - if (!arg_plain) { - - for (i = level - 1; i >= 0; i--) { - len += 2; - if (len > max_len - 3 && !arg_full) { - printf("%s...\n",max_len % 2 ? "" : " "); - return 0; - } - printf("%s", special_glyph(branches & (1 << i) ? TREE_VERTICAL : TREE_SPACE)); - } - len += 2; - - if (len > max_len - 3 && !arg_full) { - printf("%s...\n",max_len % 2 ? "" : " "); - return 0; - } - - printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); - } - - if (arg_full) { - printf("%s\n", name); - return 0; - } - - n = ellipsize(name, max_len-len, 100); - if (!n) - return log_oom(); - - printf("%s\n", n); - return 0; -} - -static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { - - static const char *dependencies[_DEPENDENCY_MAX] = { - [DEPENDENCY_FORWARD] = "Requires\0" - "Requisite\0" - "Wants\0" - "ConsistsOf\0" - "BindsTo\0", - [DEPENDENCY_REVERSE] = "RequiredBy\0" - "RequisiteOf\0" - "WantedBy\0" - "PartOf\0" - "BoundBy\0", - [DEPENDENCY_AFTER] = "After\0", - [DEPENDENCY_BEFORE] = "Before\0", - }; - - _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; - - assert(bus); - assert(name); - assert(deps); - assert_cc(ELEMENTSOF(dependencies) == _DEPENDENCY_MAX); - - path = unit_dbus_path_from_name(name); - if (!path) - return log_oom(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", "org.freedesktop.systemd1.Unit"); - 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) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *prop; - - r = sd_bus_message_read(reply, "s", &prop); - if (r < 0) - return bus_log_parse_error(r); - - if (!nulstr_contains(dependencies[arg_dependency], prop)) { - r = sd_bus_message_skip(reply, "v"); - if (r < 0) - return bus_log_parse_error(r); - } else { - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, "as"); - if (r < 0) - return bus_log_parse_error(r); - - r = bus_message_read_strv_extend(reply, &ret); - 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_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); - - } - 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); - - *deps = ret; - ret = NULL; - - return 0; -} - -static int list_dependencies_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - - if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) - return 1; - if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET) - return -1; - - return strcasecmp(*a, *b); -} - -static int list_dependencies_one( - sd_bus *bus, - const char *name, - int level, - char ***units, - unsigned int branches) { - - _cleanup_strv_free_ char **deps = NULL; - char **c; - int r = 0; - - assert(bus); - assert(name); - assert(units); - - r = strv_extend(units, name); - if (r < 0) - return log_oom(); - - r = list_dependencies_get_dependencies(bus, name, &deps); - if (r < 0) - return r; - - qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); - - STRV_FOREACH(c, deps) { - if (strv_contains(*units, *c)) { - if (!arg_plain) { - r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); - if (r < 0) - return r; - } - continue; - } - - if (arg_plain) - printf(" "); - else { - UnitActiveState active_state = _UNIT_ACTIVE_STATE_INVALID; - const char *on; - - (void) get_state_one_unit(bus, *c, &active_state); - - switch (active_state) { - case UNIT_ACTIVE: - case UNIT_RELOADING: - case UNIT_ACTIVATING: - on = ansi_highlight_green(); - break; - - case UNIT_INACTIVE: - case UNIT_DEACTIVATING: - on = ansi_normal(); - break; - - default: - on = ansi_highlight_red(); - break; - } - - printf("%s%s%s ", on, special_glyph(BLACK_CIRCLE), ansi_normal()); - } - - r = list_dependencies_print(*c, level, branches, c[1] == NULL); - if (r < 0) - return r; - - if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { - r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1)); - if (r < 0) - return r; - } - } - - if (!arg_plain) - strv_remove(*units, name); - - return 0; -} - -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; - - 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"); - - u = unit; - } else - u = SPECIAL_DEFAULT_TARGET; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - puts(u); - - return list_dependencies_one(bus, u, 0, &units, 0); -} - -struct machine_info { - bool is_host; - char *name; - char *state; - char *control_group; - uint32_t n_failed_units; - uint32_t n_jobs; - usec_t timestamp; -}; - -static const struct bus_properties_map machine_info_property_map[] = { - { "SystemState", "s", NULL, offsetof(struct machine_info, state) }, - { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) }, - { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) }, - { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) }, - { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) }, - {} -}; - -static void machine_info_clear(struct machine_info *info) { - assert(info); - - free(info->name); - free(info->state); - free(info->control_group); - zero(*info); -} - -static void free_machines_list(struct machine_info *machine_infos, int n) { - int i; - - if (!machine_infos) - return; - - for (i = 0; i < n; i++) - machine_info_clear(&machine_infos[i]); - - free(machine_infos); -} - -static int compare_machine_info(const void *a, const void *b) { - const struct machine_info *u = a, *v = b; - - if (u->is_host != v->is_host) - return u->is_host > v->is_host ? -1 : 1; - - return strcasecmp(u->name, v->name); -} - -static int get_machine_properties(sd_bus *bus, struct machine_info *mi) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *container = NULL; - int r; - - assert(mi); - - if (!bus) { - r = sd_bus_open_system_machine(&container, mi->name); - if (r < 0) - return r; - - bus = container; - } - - r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi); - if (r < 0) - return r; - - return 0; -} - -static bool output_show_machine(const char *name, char **patterns) { - return strv_fnmatch_or_empty(patterns, name, FNM_NOESCAPE); -} - -static int get_machine_list( - sd_bus *bus, - struct machine_info **_machine_infos, - char **patterns) { - - struct machine_info *machine_infos = NULL; - _cleanup_strv_free_ char **m = NULL; - _cleanup_free_ char *hn = NULL; - size_t sz = 0; - char **i; - int c = 0, r; - - hn = gethostname_malloc(); - if (!hn) - return log_oom(); - - if (output_show_machine(hn, patterns)) { - if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) - return log_oom(); - - machine_infos[c].is_host = true; - machine_infos[c].name = hn; - hn = NULL; - - get_machine_properties(bus, &machine_infos[c]); - c++; - } - - r = sd_get_machine_names(&m); - if (r < 0) - return log_error_errno(r, "Failed to get machine list: %m"); - - STRV_FOREACH(i, m) { - _cleanup_free_ char *class = NULL; - - if (!output_show_machine(*i, patterns)) - continue; - - sd_machine_get_class(*i, &class); - if (!streq_ptr(class, "container")) - continue; - - if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) { - free_machines_list(machine_infos, c); - return log_oom(); - } - - machine_infos[c].is_host = false; - machine_infos[c].name = strdup(*i); - if (!machine_infos[c].name) { - free_machines_list(machine_infos, c); - return log_oom(); - } - - get_machine_properties(NULL, &machine_infos[c]); - c++; - } - - *_machine_infos = machine_infos; - return c; -} - -static void output_machines_list(struct machine_info *machine_infos, unsigned n) { - struct machine_info *m; - unsigned - circle_len = 0, - namelen = sizeof("NAME") - 1, - statelen = sizeof("STATE") - 1, - failedlen = sizeof("FAILED") - 1, - jobslen = sizeof("JOBS") - 1; - - assert(machine_infos || n == 0); - - for (m = machine_infos; m < machine_infos + n; m++) { - namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0)); - statelen = MAX(statelen, m->state ? strlen(m->state) : 0); - failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units)); - jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs)); - - if (!arg_plain && !streq_ptr(m->state, "running")) - circle_len = 2; - } - - if (!arg_no_legend) { - if (circle_len > 0) - fputs(" ", stdout); - - printf("%-*s %-*s %-*s %-*s\n", - namelen, "NAME", - statelen, "STATE", - failedlen, "FAILED", - jobslen, "JOBS"); - } - - for (m = machine_infos; m < machine_infos + n; m++) { - const char *on_state = "", *off_state = ""; - const char *on_failed = "", *off_failed = ""; - bool circle = false; - - if (streq_ptr(m->state, "degraded")) { - on_state = ansi_highlight_red(); - off_state = ansi_normal(); - circle = true; - } else if (!streq_ptr(m->state, "running")) { - on_state = ansi_highlight_yellow(); - off_state = ansi_normal(); - circle = true; - } - - if (m->n_failed_units > 0) { - on_failed = ansi_highlight_red(); - off_failed = ansi_normal(); - } else - on_failed = off_failed = ""; - - if (circle_len > 0) - printf("%s%s%s ", on_state, circle ? special_glyph(BLACK_CIRCLE) : " ", off_state); - - if (m->is_host) - printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", - (int) (namelen - (sizeof(" (host)")-1)), strna(m->name), - on_state, statelen, strna(m->state), off_state, - on_failed, failedlen, m->n_failed_units, off_failed, - jobslen, m->n_jobs); - else - printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", - namelen, strna(m->name), - on_state, statelen, strna(m->state), off_state, - on_failed, failedlen, m->n_failed_units, off_failed, - jobslen, m->n_jobs); - } - - if (!arg_no_legend) - printf("\n%u machines listed.\n", n); -} - -static int list_machines(int argc, char *argv[], void *userdata) { - struct machine_info *machine_infos = NULL; - sd_bus *bus; - int r; - - if (geteuid() != 0) { - log_error("Must be root."); - return -EPERM; - } - - 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; - - pager_open(arg_no_pager, false); - - qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info); - output_machines_list(machine_infos, r); - free_machines_list(machine_infos, r); - - return 0; -} - -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 (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; - - r = 0; - } 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; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetDefaultTarget", - &error, - &reply, - NULL); - 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) - return bus_log_parse_error(r); - } - - if (path) - printf("%s\n", path); - - return 0; -} - -static int set_default(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *unit = NULL; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int r; - - 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 (install_client_side()) { - r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); - unit_file_dump_changes(r, "set default", changes, n_changes, arg_quiet); - - if (r > 0) - r = 0; - } else { - _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", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetDefaultTarget", - &error, - &reply, - "sb", unit, 1); - 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, &changes, &n_changes); - if (r < 0) - goto finish; - - /* Try to reload if enabled */ - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; - } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; -} - -struct job_info { - uint32_t id; - const char *name, *type, *state; -}; - -static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) { - unsigned id_len, unit_len, type_len, state_len; - const struct job_info *j; - const char *on, *off; - bool shorten = false; - - assert(n == 0 || jobs); - - if (n == 0) { - if (!arg_no_legend) { - on = ansi_highlight_green(); - off = ansi_normal(); - - printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off); - } - return; - } - - pager_open(arg_no_pager, false); - - id_len = strlen("JOB"); - unit_len = strlen("UNIT"); - type_len = strlen("TYPE"); - state_len = strlen("STATE"); - - for (j = jobs; j < jobs + n; j++) { - uint32_t id = j->id; - assert(j->name && j->type && j->state); - - id_len = MAX(id_len, DECIMAL_STR_WIDTH(id)); - unit_len = MAX(unit_len, strlen(j->name)); - type_len = MAX(type_len, strlen(j->type)); - state_len = MAX(state_len, strlen(j->state)); - } - - if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) { - unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3); - shorten = true; - } - - if (!arg_no_legend) - printf("%*s %-*s %-*s %-*s\n", - id_len, "JOB", - unit_len, "UNIT", - type_len, "TYPE", - state_len, "STATE"); - - for (j = jobs; j < jobs + n; j++) { - _cleanup_free_ char *e = NULL; - - if (streq(j->state, "running")) { - on = ansi_highlight(); - off = ansi_normal(); - } else - on = off = ""; - - e = shorten ? ellipsize(j->name, unit_len, 33) : NULL; - printf("%*u %s%-*s%s %-*s %s%-*s%s\n", - id_len, j->id, - on, unit_len, e ? e : j->name, off, - type_len, j->type, - on, state_len, j->state, off); - } - - if (!arg_no_legend) { - on = ansi_highlight(); - off = ansi_normal(); - - printf("\n%s%u jobs listed%s.\n", on, n, off); - } -} - -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(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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListJobs", - &error, - &reply, - NULL); - 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) - return bus_log_parse_error(r); - - 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(argv, 1))) { - skipped = true; - continue; - } - - if (!GREEDY_REALLOC(jobs, size, c + 1)) - return log_oom(); - - jobs[c++] = job; - } - 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); - - pager_open(arg_no_pager, false); - - output_jobs_list(jobs, c, skipped); - return 0; -} - -static int cancel_job(int argc, char *argv[], void *userdata) { - sd_bus *bus; - char **name; - int r = 0; - - if (argc <= 1) - return trivial_method(argc, argv, userdata); - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - 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; - - q = safe_atou32(*name, &id); - if (q < 0) - return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name); - - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CancelJob", - &error, - NULL, - "u", id); - if (q < 0) { - log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q)); - if (r == 0) - r = q; - } - } - - return r; -} - -static int need_daemon_reload(sd_bus *bus, const char *unit) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *path; - int b, r; - - /* We ignore all errors here, since this is used to show a - * warning only */ - - /* We don't use unit_dbus_path_from_name() directly since we - * don't want to load the unit if it isn't loaded. */ - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit", - NULL, - &reply, - "s", unit); - if (r < 0) - return r; - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return r; - - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "NeedDaemonReload", - NULL, - 'b', &b); - if (r < 0) - return r; - - return b; -} - -static void warn_unit_file_changed(const char *name) { - assert(name); - - log_warning("%sWarning:%s %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", - ansi_highlight_red(), - ansi_normal(), - name, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); -} - -static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) { - char **p; - - assert(lp); - assert(unit_name); - assert(unit_path); - - STRV_FOREACH(p, lp->search_path) { - _cleanup_free_ char *path; - - path = path_join(arg_root, *p, unit_name); - if (!path) - return log_oom(); - - if (access(path, F_OK) == 0) { - *unit_path = path; - path = NULL; - return 1; - } - } - - return 0; -} - -static int unit_find_paths( - sd_bus *bus, - const char *unit_name, - LookupPaths *lp, - char **fragment_path, - char ***dropin_paths) { - - _cleanup_free_ char *path = NULL; - _cleanup_strv_free_ char **dropins = NULL; - int r; - - /** - * Finds where the unit is defined on disk. Returns 0 if the unit - * is not found. Returns 1 if it is found, and sets - * - the path to the unit in *path, if it exists on disk, - * - and a strv of existing drop-ins in *dropins, - * if the arg is not NULL and any dropins were found. - */ - - assert(unit_name); - assert(fragment_path); - assert(lp); - - 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_free_ char *unit = NULL; - - unit = unit_dbus_path_from_name(unit_name); - if (!unit) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - unit, - "org.freedesktop.systemd1.Unit", - "FragmentPath", - &error, - &path); - if (r < 0) - return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r)); - - if (dropin_paths) { - r = sd_bus_get_property_strv( - bus, - "org.freedesktop.systemd1", - unit, - "org.freedesktop.systemd1.Unit", - "DropInPaths", - &error, - &dropins); - if (r < 0) - return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r)); - } - } else { - _cleanup_set_free_ Set *names; - - names = set_new(NULL); - if (!names) - return log_oom(); - - r = set_put(names, unit_name); - if (r < 0) - return log_error_errno(r, "Failed to add unit name: %m"); - - r = unit_file_find_path(lp, unit_name, &path); - if (r < 0) - return r; - - if (r == 0) { - _cleanup_free_ char *template = NULL; - - r = unit_name_template(unit_name, &template); - if (r < 0 && r != -EINVAL) - return log_error_errno(r, "Failed to determine template name: %m"); - if (r >= 0) { - r = unit_file_find_path(lp, template, &path); - if (r < 0) - return r; - } - } - - if (dropin_paths) { - r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); - if (r < 0) - return r; - } - } - - r = 0; - - if (!isempty(path)) { - *fragment_path = path; - path = NULL; - r = 1; - } - - if (dropin_paths && !strv_isempty(dropins)) { - *dropin_paths = dropins; - dropins = NULL; - r = 1; - } - - if (r == 0 && !arg_force) - log_error("No files found for %s.", unit_name); - - return r; -} - -static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state) { - _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 *buf = NULL; - UnitActiveState state; - const char *path; - int r; - - assert(name); - assert(active_state); - - /* We don't use unit_dbus_path_from_name() directly since we don't want to load the unit unnecessarily, if it - * isn't loaded. */ - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit", - &error, - &reply, - "s", name); - if (r < 0) { - if (!sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT)) - return log_error_errno(r, "Failed to retrieve unit: %s", bus_error_message(&error, r)); - - /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are - * considered inactive. */ - state = UNIT_INACTIVE; - - } else { - 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", - "ActiveState", - &error, - &buf); - if (r < 0) - return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); - - state = unit_active_state_from_string(buf); - if (state == _UNIT_ACTIVE_STATE_INVALID) { - log_error("Invalid unit state '%s' for: %s", buf, name); - return -EINVAL; - } - } - - *active_state = state; - return 0; -} - -static int check_triggering_units( - sd_bus *bus, - const char *name) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *path = NULL, *n = NULL, *load_state = NULL; - _cleanup_strv_free_ char **triggered_by = NULL; - bool print_warning_label = true; - UnitActiveState active_state; - char **i; - int r; - - r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to mangle unit name: %m"); - - path = unit_dbus_path_from_name(n); - if (!path) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "LoadState", - &error, - &load_state); - if (r < 0) - return log_error_errno(r, "Failed to get load state of %s: %s", n, bus_error_message(&error, r)); - - if (streq(load_state, "masked")) - return 0; - - r = sd_bus_get_property_strv( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "TriggeredBy", - &error, - &triggered_by); - 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 = get_state_one_unit(bus, *i, &active_state); - if (r < 0) - return r; - - if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING)) - continue; - - if (print_warning_label) { - log_warning("Warning: Stopping %s, but it can still be activated by:", n); - print_warning_label = false; - } - - log_warning(" %s", *i); - } - - return 0; -} - -static const struct { - const char *verb; - const char *method; -} unit_actions[] = { - { "start", "StartUnit" }, - { "stop", "StopUnit" }, - { "condstop", "StopUnit" }, - { "reload", "ReloadUnit" }, - { "restart", "RestartUnit" }, - { "try-restart", "TryRestartUnit" }, - { "condrestart", "TryRestartUnit" }, - { "reload-or-restart", "ReloadOrRestartUnit" }, - { "try-reload-or-restart", "ReloadOrTryRestartUnit" }, - { "reload-or-try-restart", "ReloadOrTryRestartUnit" }, - { "condreload", "ReloadOrTryRestartUnit" }, - { "force-reload", "ReloadOrTryRestartUnit" } -}; - -static const char *verb_to_method(const char *verb) { - uint i; - - for (i = 0; i < ELEMENTSOF(unit_actions); i++) - if (streq_ptr(unit_actions[i].verb, verb)) - return unit_actions[i].method; - - return "StartUnit"; -} - -static const char *method_to_verb(const char *method) { - uint i; - - for (i = 0; i < ELEMENTSOF(unit_actions); i++) - if (streq_ptr(unit_actions[i].method, method)) - return unit_actions[i].verb; - - return "n/a"; -} - -static int start_unit_one( - sd_bus *bus, - const char *method, - const char *name, - const char *mode, - sd_bus_error *error, - BusWaitForJobs *w) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *path; - int r; - - assert(method); - assert(name); - assert(mode); - assert(error); - - log_debug("Calling manager for %s on %s, %s", method, name, mode); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - error, - &reply, - "ss", name, mode); - if (r < 0) { - const char *verb; - - /* There's always a fallback possible for legacy actions. */ - if (arg_action != ACTION_SYSTEMCTL) - return r; - - 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 %s logs and 'systemctl%s status %s' for details.", - arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", - name); - - return r; - } - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - if (need_daemon_reload(bus, name) > 0) - warn_unit_file_changed(name); - - if (w) { - log_debug("Adding %s to the set", path); - r = bus_wait_for_jobs_add(w, path); - if (r < 0) - return log_oom(); - } - - return 0; -} - -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; - - if (suffix) - r = unit_name_mangle_with_suffix(*name, UNIT_NAME_GLOB, suffix, &t); - else - r = unit_name_mangle(*name, UNIT_NAME_GLOB, &t); - if (r < 0) - return log_error_errno(r, "Failed to mangle name: %m"); - - if (string_is_glob(t)) - r = strv_consume(&globs, t); - else - r = strv_consume(&mangled, t); - if (r < 0) - return log_oom(); - } - - /* Query the manager only if any of the names are a glob, since - * this is fairly expensive */ - if (!strv_isempty(globs)) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ UnitInfo *unit_infos = NULL; - size_t allocated, n; - - r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply); - if (r < 0) - return r; - - n = strv_length(mangled); - allocated = n + 1; - - for (i = 0; i < r; i++) { - if (!GREEDY_REALLOC(mangled, allocated, n+2)) - return log_oom(); - - mangled[n] = strdup(unit_infos[i].id); - if (!mangled[n]) - return log_oom(); - - mangled[++n] = NULL; - } - } - - *ret = mangled; - mangled = NULL; /* do not free */ - - return 0; -} - -static const struct { - const char *target; - const char *verb; - const char *mode; -} action_table[_ACTION_MAX] = { - [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, - [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, - [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, - [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, - [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, - [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, - [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, - [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, - [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, - [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, - [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, - [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, -}; - -static enum action verb_to_action(const char *verb) { - enum action i; - - for (i = _ACTION_INVALID; i < _ACTION_MAX; i++) - if (streq_ptr(action_table[i].verb, verb)) - return i; - - return _ACTION_INVALID; -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - ask_password_agent_open_if_enabled(); - polkit_agent_open_if_enabled(); - - if (arg_action == ACTION_SYSTEMCTL) { - enum action action; - - method = verb_to_method(argv[0]); - action = verb_to_action(argv[0]); - - if (streq(argv[0], "isolate")) { - mode = "isolate"; - suffix = ".target"; - } else - mode = action_table[action].mode ?: arg_job_mode; - - one_name = action_table[action].target; - } else { - assert(arg_action < ELEMENTSOF(action_table)); - assert(action_table[arg_action].target); - - method = "StartUnit"; - - mode = action_table[arg_action].mode; - one_name = action_table[arg_action].target; - } - - if (one_name) - names = strv_new(one_name, NULL); - else { - r = expand_names(bus, strv_skip(argv, 1), suffix, &names); - if (r < 0) - return log_error_errno(r, "Failed to expand names: %m"); - } - - if (!arg_no_block) { - r = bus_wait_for_jobs_new(bus, &w); - if (r < 0) - return log_error_errno(r, "Could not watch jobs: %m"); - } - - STRV_FOREACH(name, names) { - _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); - if (r >= 0 && q < 0) - r = translate_bus_error_to_exit_status(q, &error); - } - - if (!arg_no_block) { - int q, arg_count = 0; - const char* extra_args[4] = {}; - - if (arg_scope != UNIT_FILE_SYSTEM) - extra_args[arg_count++] = "--user"; - - assert(IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_REMOTE, BUS_TRANSPORT_MACHINE)); - if (arg_transport == BUS_TRANSPORT_REMOTE) { - extra_args[arg_count++] = "-H"; - extra_args[arg_count++] = arg_host; - } else if (arg_transport == BUS_TRANSPORT_MACHINE) { - extra_args[arg_count++] = "-M"; - extra_args[arg_count++] = arg_host; - } - - q = bus_wait_for_jobs(w, arg_quiet, extra_args); - if (q < 0) - return q; - - /* When stopping units, warn if they can still be triggered by - * another active unit (socket, path, timer) */ - if (!arg_quiet && streq(method, "StopUnit")) - STRV_FOREACH(name, names) - check_triggering_units(bus, *name); - } - - 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 logind_reboot(enum action a) { -#ifdef HAVE_LOGIND - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *method, *description; - sd_bus *bus; - int r; - - r = acquire_bus(BUS_FULL, &bus); - if (r < 0) - return r; - - switch (a) { - - case ACTION_REBOOT: - method = "Reboot"; - description = "reboot system"; - break; - - case ACTION_POWEROFF: - method = "PowerOff"; - description = "power off system"; - break; - - case ACTION_SUSPEND: - method = "Suspend"; - description = "suspend system"; - break; - - case ACTION_HIBERNATE: - method = "Hibernate"; - description = "hibernate system"; - break; - - case ACTION_HYBRID_SLEEP: - method = "HybridSleep"; - description = "put system into hybrid sleep"; - break; - - default: - return -EINVAL; - } - - polkit_agent_open_if_enabled(); - (void) logind_set_wall_message(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - method, - &error, - NULL, - "b", arg_ask_password); - if (r < 0) - return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r)); - - return 0; -#else - return -ENOSYS; -#endif -} - -static int logind_check_inhibitors(enum action a) { -#ifdef HAVE_LOGIND - _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 (arg_ignore_inhibitors || arg_force > 0) - return 0; - - if (arg_when > 0) - return 0; - - if (geteuid() == 0) - return 0; - - if (!on_tty()) - return 0; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return 0; - - 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", - "ListInhibitors", - NULL, - &reply, - NULL); - if (r < 0) - /* If logind is not around, then there are no inhibitors... */ - return 0; - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { - _cleanup_free_ char *comm = NULL, *user = NULL; - _cleanup_strv_free_ char **sv = NULL; - - if (!streq(mode, "block")) - continue; - - sv = strv_split(what, ":"); - if (!sv) - return log_oom(); - - if ((pid_t) pid < 0) - return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid); - - if (!strv_contains(sv, - IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_KEXEC) ? "shutdown" : "sleep")) - continue; - - get_process_comm(pid, &comm); - user = uid_to_name(uid); - - log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".", - who, (pid_t) pid, strna(comm), strna(user), why); - - 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); - - /* Check for current sessions */ - sd_get_sessions(&sessions); - STRV_FOREACH(s, sessions) { - _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL; - - if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) - continue; - - if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) - continue; - - if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty"))) - continue; - - sd_session_get_tty(*s, &tty); - sd_session_get_seat(*s, &seat); - sd_session_get_service(*s, &service); - user = uid_to_name(uid); - - log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty); - c++; - } - - if (c <= 0) - return 0; - - log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.", - action_table[a].verb); - - return -EPERM; -#else - return 0; -#endif -} - -static int logind_prepare_firmware_setup(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; - - 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) - return 0; - - if (arg_transport == BUS_TRANSPORT_LOCAL) { - - r = efi_set_reboot_to_firmware(true); - if (r < 0) - log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m"); - else - return r; - } - - 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.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetExitCode", - &error, - NULL, - "y", code); - if (r < 0) - return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r)); - - return 0; -} - -static int start_special(int argc, char *argv[], void *userdata) { - enum action a; - int r; - - assert(argv); - - a = verb_to_action(argv[0]); - - r = logind_check_inhibitors(a); - if (r < 0) - return r; - - if (arg_force >= 2 && geteuid() != 0) { - log_error("Must be root."); - return -EPERM; - } - - r = prepare_firmware_setup(); - if (r < 0) - return r; - - if (a == ACTION_REBOOT && argc > 1) { - r = update_reboot_parameter_and_warn(argv[1]); - if (r < 0) - return r; - - } else if (a == ACTION_EXIT && argc > 1) { - uint8_t code; - - /* 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 && - IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT)) - return halt_now(a); - - if (arg_force >= 1 && - IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_KEXEC, - ACTION_EXIT)) - return trivial_method(argc, argv, userdata); - - /* First try logind, to allow authentication with polkit */ - if (IN_SET(a, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_SUSPEND, - ACTION_HIBERNATE, - ACTION_HYBRID_SLEEP)) { - 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 */ - } - - return start_unit(argc, argv, userdata); -} - -static int start_system_special(int argc, char *argv[], void *userdata) { - /* Like start_special above, but raises an error when running in user mode */ - - if (arg_scope != UNIT_FILE_SYSTEM) { - log_error("Bad action for %s mode.", - arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user"); - return -EINVAL; - } - - return start_special(argc, argv, userdata); -} - -static int check_unit_generic(int code, const UnitActiveState good_states[], int nb_states, char **args) { - _cleanup_strv_free_ char **names = NULL; - UnitActiveState active_state; - sd_bus *bus; - char **name; - int r, i; - bool found = false; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - r = expand_names(bus, args, NULL, &names); - if (r < 0) - return log_error_errno(r, "Failed to expand names: %m"); - - STRV_FOREACH(name, names) { - r = get_state_one_unit(bus, *name, &active_state); - if (r < 0) - return r; - - if (!arg_quiet) - puts(unit_active_state_to_string(active_state)); - - for (i = 0; i < nb_states; ++i) - if (good_states[i] == active_state) - found = true; - } - - /* use the given return code for the case that we won't find - * any unit which matches the list */ - return found ? 0 : code; -} - -static int check_unit_active(int argc, char *argv[], void *userdata) { - const UnitActiveState states[] = { UNIT_ACTIVE, UNIT_RELOADING }; - /* According to LSB: 3, "program is not running" */ - return check_unit_generic(EXIT_PROGRAM_NOT_RUNNING, states, ELEMENTSOF(states), strv_skip(argv, 1)); -} - -static int check_unit_failed(int argc, char *argv[], void *userdata) { - const UnitActiveState states[] = { UNIT_FAILED }; - return check_unit_generic(EXIT_PROGRAM_DEAD_AND_PID_EXISTS, states, ELEMENTSOF(states), strv_skip(argv, 1)); -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - if (!arg_kill_who) - arg_kill_who = "all"; - - /* --fail was specified */ - if (streq(arg_job_mode, "fail")) - kill_who = strjoina(arg_kill_who, "-fail"); - - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - &error, - NULL, - "ssi", *names, kill_who ? kill_who : arg_kill_who, arg_signal); - if (q < 0) { - log_error_errno(q, "Failed to kill unit %s: %s", *names, bus_error_message(&error, q)); - if (r == 0) - r = q; - } - } - - return r; -} - -typedef struct ExecStatusInfo { - char *name; - - char *path; - char **argv; - - bool ignore; - - usec_t start_timestamp; - usec_t exit_timestamp; - pid_t pid; - int code; - int status; - - LIST_FIELDS(struct ExecStatusInfo, exec); -} ExecStatusInfo; - -static void exec_status_info_free(ExecStatusInfo *i) { - assert(i); - - free(i->name); - free(i->path); - strv_free(i->argv); - free(i); -} - -static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) { - uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic; - const char *path; - uint32_t pid; - int32_t code, status; - int ignore, r; - - assert(m); - assert(i); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii"); - if (r < 0) - return bus_log_parse_error(r); - else if (r == 0) - return 0; - - r = sd_bus_message_read(m, "s", &path); - if (r < 0) - return bus_log_parse_error(r); - - i->path = strdup(path); - if (!i->path) - return log_oom(); - - r = sd_bus_message_read_strv(m, &i->argv); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(m, - "bttttuii", - &ignore, - &start_timestamp, &start_timestamp_monotonic, - &exit_timestamp, &exit_timestamp_monotonic, - &pid, - &code, &status); - if (r < 0) - return bus_log_parse_error(r); - - i->ignore = ignore; - i->start_timestamp = (usec_t) start_timestamp; - i->exit_timestamp = (usec_t) exit_timestamp; - i->pid = (pid_t) pid; - i->code = code; - i->status = status; - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 1; -} - -typedef struct UnitCondition { - char *name; - char *param; - bool trigger; - bool negate; - int tristate; - - LIST_FIELDS(struct UnitCondition, conditions); -} UnitCondition; - -static void unit_condition_free(UnitCondition *c) { - if (!c) - return; - - free(c->name); - free(c->param); - free(c); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(UnitCondition*, unit_condition_free); - -typedef struct UnitStatusInfo { - const char *id; - const char *load_state; - const char *active_state; - const char *sub_state; - const char *unit_file_state; - const char *unit_file_preset; - - const char *description; - const char *following; - - char **documentation; - - const char *fragment_path; - const char *source_path; - const char *control_group; - - char **dropin_paths; - - const char *load_error; - const char *result; - - usec_t inactive_exit_timestamp; - usec_t inactive_exit_timestamp_monotonic; - usec_t active_enter_timestamp; - usec_t active_exit_timestamp; - usec_t inactive_enter_timestamp; - - bool need_daemon_reload; - bool transient; - - /* Service */ - pid_t main_pid; - pid_t control_pid; - const char *status_text; - const char *pid_file; - bool running:1; - int status_errno; - - usec_t start_timestamp; - usec_t exit_timestamp; - - int exit_code, exit_status; - - usec_t condition_timestamp; - bool condition_result; - LIST_HEAD(UnitCondition, conditions); - - usec_t assert_timestamp; - bool assert_result; - bool failed_assert_trigger; - bool failed_assert_negate; - const char *failed_assert; - const char *failed_assert_parameter; - - /* Socket */ - unsigned n_accepted; - unsigned n_connections; - bool accept; - - /* Pairs of type, path */ - char **listen; - - /* Device */ - const char *sysfs_path; - - /* Mount, Automount */ - const char *where; - - /* Swap */ - const char *what; - - /* CGroup */ - uint64_t memory_current; - uint64_t memory_low; - uint64_t memory_high; - uint64_t memory_max; - uint64_t memory_limit; - uint64_t cpu_usage_nsec; - uint64_t tasks_current; - uint64_t tasks_max; - - LIST_HEAD(ExecStatusInfo, exec); -} UnitStatusInfo; - -static void unit_status_info_free(UnitStatusInfo *info) { - ExecStatusInfo *p; - UnitCondition *c; - - strv_free(info->documentation); - strv_free(info->dropin_paths); - strv_free(info->listen); - - while ((c = info->conditions)) { - LIST_REMOVE(conditions, info->conditions, c); - unit_condition_free(c); - } - - while ((p = info->exec)) { - LIST_REMOVE(exec, info->exec, p); - exec_status_info_free(p); - } -} - -static void print_status_info( - sd_bus *bus, - UnitStatusInfo *i, - bool *ellipsized) { - - ExecStatusInfo *p; - const char *active_on, *active_off, *on, *off, *ss; - usec_t timestamp; - char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; - char since2[FORMAT_TIMESTAMP_MAX], *s2; - const char *path; - char **t, **t2; - int r; - - assert(i); - - /* This shows pretty information about a unit. See - * print_property() for a low-level property printer */ - - if (streq_ptr(i->active_state, "failed")) { - active_on = ansi_highlight_red(); - active_off = ansi_normal(); - } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) { - active_on = ansi_highlight_green(); - active_off = ansi_normal(); - } else - active_on = active_off = ""; - - printf("%s%s%s %s", active_on, special_glyph(BLACK_CIRCLE), active_off, strna(i->id)); - - if (i->description && !streq_ptr(i->id, i->description)) - printf(" - %s", i->description); - - printf("\n"); - - if (i->following) - printf(" Follow: unit currently follows state of %s\n", i->following); - - if (streq_ptr(i->load_state, "error")) { - on = ansi_highlight_red(); - off = ansi_normal(); - } else - on = off = ""; - - path = i->source_path ? i->source_path : i->fragment_path; - - 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)) - printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); - else if (path && !isempty(i->unit_file_state)) - printf(" Loaded: %s%s%s (%s; %s)\n", - on, strna(i->load_state), off, path, i->unit_file_state); - else if (path) - printf(" Loaded: %s%s%s (%s)\n", - on, strna(i->load_state), off, path); - else - 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; - char ** dropin; - - STRV_FOREACH(dropin, i->dropin_paths) { - if (! dir || last) { - printf(dir ? " " : " Drop-In: "); - - dir = mfree(dir); - - dir = dirname_malloc(*dropin); - if (!dir) { - log_oom(); - return; - } - - printf("%s\n %s", dir, - special_glyph(TREE_RIGHT)); - } - - last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); - - printf("%s%s", basename(*dropin), last ? "\n" : ", "); - } - } - - ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state; - if (ss) - printf(" Active: %s%s (%s)%s", - active_on, strna(i->active_state), ss, active_off); - else - printf(" Active: %s%s%s", - active_on, strna(i->active_state), active_off); - - if (!isempty(i->result) && !streq(i->result, "success")) - printf(" (Result: %s)", i->result); - - timestamp = (streq_ptr(i->active_state, "active") || - streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp : - (streq_ptr(i->active_state, "inactive") || - streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp : - streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp : - i->active_exit_timestamp; - - s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); - s2 = format_timestamp(since2, sizeof(since2), timestamp); - - if (s1) - printf(" since %s; %s\n", s2, s1); - else if (s2) - printf(" since %s\n", s2); - else - printf("\n"); - - if (!i->condition_result && i->condition_timestamp > 0) { - UnitCondition *c; - int n = 0; - - s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); - s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); - - printf("Condition: start %scondition failed%s at %s%s%s\n", - ansi_highlight_yellow(), ansi_normal(), - s2, s1 ? "; " : "", strempty(s1)); - - LIST_FOREACH(conditions, c, i->conditions) - if (c->tristate < 0) - n++; - - LIST_FOREACH(conditions, c, i->conditions) - if (c->tristate < 0) - printf(" %s %s=%s%s%s was not met\n", - --n ? special_glyph(TREE_BRANCH) : special_glyph(TREE_RIGHT), - c->name, - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->param); - } - - if (!i->assert_result && i->assert_timestamp > 0) { - s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp); - s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp); - - printf(" Assert: start %sassertion failed%s at %s%s%s\n", - ansi_highlight_red(), ansi_normal(), - s2, s1 ? "; " : "", strempty(s1)); - if (i->failed_assert_trigger) - printf(" none of the trigger assertions were met\n"); - else if (i->failed_assert) - printf(" %s=%s%s was not met\n", - i->failed_assert, - i->failed_assert_negate ? "!" : "", - i->failed_assert_parameter); - } - - if (i->sysfs_path) - printf(" Device: %s\n", i->sysfs_path); - if (i->where) - printf(" Where: %s\n", i->where); - if (i->what) - printf(" What: %s\n", i->what); - - STRV_FOREACH(t, i->documentation) - printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t); - - STRV_FOREACH_PAIR(t, t2, i->listen) - printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t); - - if (i->accept) - printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); - - LIST_FOREACH(exec, p, i->exec) { - _cleanup_free_ char *argv = NULL; - bool good; - - /* Only show exited processes here */ - if (p->code == 0) - continue; - - argv = strv_join(p->argv, " "); - printf(" Process: "PID_FMT" %s=%s ", p->pid, p->name, strna(argv)); - - good = is_clean_exit_lsb(p->code, p->status, NULL); - if (!good) { - on = ansi_highlight_red(); - off = ansi_normal(); - } else - on = off = ""; - - printf("%s(code=%s, ", on, sigchld_code_to_string(p->code)); - - if (p->code == CLD_EXITED) { - const char *c; - - printf("status=%i", p->status); - - c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD); - if (c) - printf("/%s", c); - - } else - printf("signal=%s", signal_to_string(p->status)); - - printf(")%s\n", off); - - if (i->main_pid == p->pid && - i->start_timestamp == p->start_timestamp && - i->exit_timestamp == p->start_timestamp) - /* Let's not show this twice */ - i->main_pid = 0; - - if (p->pid == i->control_pid) - i->control_pid = 0; - } - - if (i->main_pid > 0 || i->control_pid > 0) { - if (i->main_pid > 0) { - printf(" Main PID: "PID_FMT, i->main_pid); - - if (i->running) { - _cleanup_free_ char *comm = NULL; - (void) get_process_comm(i->main_pid, &comm); - if (comm) - printf(" (%s)", comm); - } else if (i->exit_code > 0) { - printf(" (code=%s, ", sigchld_code_to_string(i->exit_code)); - - if (i->exit_code == CLD_EXITED) { - const char *c; - - printf("status=%i", i->exit_status); - - c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD); - if (c) - printf("/%s", c); - - } else - printf("signal=%s", signal_to_string(i->exit_status)); - printf(")"); - } - } - - if (i->control_pid > 0) { - _cleanup_free_ char *c = NULL; - - if (i->main_pid > 0) - fputs("; Control PID: ", stdout); - else - fputs("Cntrl PID: ", stdout); /* if first in column, abbreviated so it fits alignment */ - - printf(PID_FMT, i->control_pid); - - (void) get_process_comm(i->control_pid, &c); - if (c) - printf(" (%s)", c); - } - - printf("\n"); - } - - if (i->status_text) - printf(" Status: \"%s\"\n", i->status_text); - if (i->status_errno > 0) - printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno)); - - if (i->tasks_current != (uint64_t) -1) { - printf(" Tasks: %" PRIu64, i->tasks_current); - - if (i->tasks_max != (uint64_t) -1) - printf(" (limit: %" PRIu64 ")\n", i->tasks_max); - else - printf("\n"); - } - - if (i->memory_current != (uint64_t) -1) { - char buf[FORMAT_BYTES_MAX]; - - printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current)); - - if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX || - i->memory_limit != CGROUP_LIMIT_MAX) { - const char *prefix = ""; - - printf(" ("); - if (i->memory_low > 0) { - printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low)); - prefix = " "; - } - if (i->memory_high != CGROUP_LIMIT_MAX) { - printf("%shigh: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_high)); - prefix = " "; - } - if (i->memory_max != CGROUP_LIMIT_MAX) { - printf("%smax: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_max)); - prefix = " "; - } - if (i->memory_limit != CGROUP_LIMIT_MAX) { - printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit)); - prefix = " "; - } - printf(")"); - } - printf("\n"); - } - - if (i->cpu_usage_nsec != (uint64_t) -1) { - char buf[FORMAT_TIMESPAN_MAX]; - printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC)); - } - - if (i->control_group) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - static const char prefix[] = " "; - unsigned c; - - printf(" CGroup: %s\n", i->control_group); - - c = columns(); - if (c > sizeof(prefix) - 1) - c -= sizeof(prefix) - 1; - else - c = 0; - - r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error); - if (r == -EBADR) { - unsigned k = 0; - pid_t extra[2]; - - /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ - - if (i->main_pid > 0) - extra[k++] = i->main_pid; - - if (i->control_pid > 0) - extra[k++] = i->control_pid; - - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags()); - } else if (r < 0) - log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r)); - } - - if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) - show_journal_by_unit( - stdout, - i->id, - arg_output, - 0, - i->inactive_exit_timestamp_monotonic, - arg_lines, - getuid(), - get_output_flags() | OUTPUT_BEGIN_NEWLINE, - SD_JOURNAL_LOCAL_ONLY, - arg_scope == UNIT_FILE_SYSTEM, - ellipsized); - - if (i->need_daemon_reload) - warn_unit_file_changed(i->id); -} - -static void show_unit_help(UnitStatusInfo *i) { - char **p; - - assert(i); - - if (!i->documentation) { - log_info("Documentation for %s not known.", i->id); - return; - } - - STRV_FOREACH(p, i->documentation) - if (startswith(*p, "man:")) - show_man_page(*p + 4, false); - else - log_info("Can't show: %s", *p); -} - -static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) { - int r; - - assert(name); - assert(m); - assert(i); - - switch (contents[0]) { - - case SD_BUS_TYPE_STRING: { - const char *s; - - r = sd_bus_message_read(m, "s", &s); - if (r < 0) - return bus_log_parse_error(r); - - if (!isempty(s)) { - if (streq(name, "Id")) - i->id = s; - else if (streq(name, "LoadState")) - i->load_state = s; - else if (streq(name, "ActiveState")) - i->active_state = s; - else if (streq(name, "SubState")) - i->sub_state = s; - else if (streq(name, "Description")) - i->description = s; - else if (streq(name, "FragmentPath")) - i->fragment_path = s; - else if (streq(name, "SourcePath")) - i->source_path = s; -#ifndef NOLEGACY - else if (streq(name, "DefaultControlGroup")) { - const char *e; - e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":"); - if (e) - i->control_group = e; - } -#endif - else if (streq(name, "ControlGroup")) - i->control_group = s; - else if (streq(name, "StatusText")) - i->status_text = s; - else if (streq(name, "PIDFile")) - i->pid_file = s; - else if (streq(name, "SysFSPath")) - i->sysfs_path = s; - else if (streq(name, "Where")) - i->where = s; - else if (streq(name, "What")) - i->what = s; - else if (streq(name, "Following")) - i->following = s; - else if (streq(name, "UnitFileState")) - i->unit_file_state = s; - else if (streq(name, "UnitFilePreset")) - i->unit_file_preset = s; - else if (streq(name, "Result")) - i->result = s; - } - - break; - } - - case SD_BUS_TYPE_BOOLEAN: { - int b; - - r = sd_bus_message_read(m, "b", &b); - if (r < 0) - return bus_log_parse_error(r); - - if (streq(name, "Accept")) - i->accept = b; - else if (streq(name, "NeedDaemonReload")) - i->need_daemon_reload = b; - else if (streq(name, "ConditionResult")) - i->condition_result = b; - else if (streq(name, "AssertResult")) - i->assert_result = b; - else if (streq(name, "Transient")) - i->transient = b; - - break; - } - - case SD_BUS_TYPE_UINT32: { - uint32_t u; - - r = sd_bus_message_read(m, "u", &u); - if (r < 0) - return bus_log_parse_error(r); - - if (streq(name, "MainPID")) { - if (u > 0) { - i->main_pid = (pid_t) u; - i->running = true; - } - } else if (streq(name, "ControlPID")) - i->control_pid = (pid_t) u; - else if (streq(name, "ExecMainPID")) { - if (u > 0) - i->main_pid = (pid_t) u; - } else if (streq(name, "NAccepted")) - i->n_accepted = u; - else if (streq(name, "NConnections")) - i->n_connections = u; - - break; - } - - case SD_BUS_TYPE_INT32: { - int32_t j; - - r = sd_bus_message_read(m, "i", &j); - if (r < 0) - return bus_log_parse_error(r); - - if (streq(name, "ExecMainCode")) - i->exit_code = (int) j; - else if (streq(name, "ExecMainStatus")) - i->exit_status = (int) j; - else if (streq(name, "StatusErrno")) - i->status_errno = (int) j; - - break; - } - - case SD_BUS_TYPE_UINT64: { - uint64_t u; - - r = sd_bus_message_read(m, "t", &u); - if (r < 0) - return bus_log_parse_error(r); - - if (streq(name, "ExecMainStartTimestamp")) - i->start_timestamp = (usec_t) u; - else if (streq(name, "ExecMainExitTimestamp")) - i->exit_timestamp = (usec_t) u; - else if (streq(name, "ActiveEnterTimestamp")) - i->active_enter_timestamp = (usec_t) u; - else if (streq(name, "InactiveEnterTimestamp")) - i->inactive_enter_timestamp = (usec_t) u; - else if (streq(name, "InactiveExitTimestamp")) - i->inactive_exit_timestamp = (usec_t) u; - else if (streq(name, "InactiveExitTimestampMonotonic")) - i->inactive_exit_timestamp_monotonic = (usec_t) u; - else if (streq(name, "ActiveExitTimestamp")) - i->active_exit_timestamp = (usec_t) u; - else if (streq(name, "ConditionTimestamp")) - i->condition_timestamp = (usec_t) u; - else if (streq(name, "AssertTimestamp")) - i->assert_timestamp = (usec_t) u; - else if (streq(name, "MemoryCurrent")) - i->memory_current = u; - else if (streq(name, "MemoryLow")) - i->memory_low = u; - else if (streq(name, "MemoryHigh")) - i->memory_high = u; - else if (streq(name, "MemoryMax")) - i->memory_max = u; - else if (streq(name, "MemoryLimit")) - i->memory_limit = u; - else if (streq(name, "TasksCurrent")) - i->tasks_current = u; - else if (streq(name, "TasksMax")) - i->tasks_max = u; - else if (streq(name, "CPUUsageNSec")) - i->cpu_usage_nsec = u; - - break; - } - - case SD_BUS_TYPE_ARRAY: - - if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) { - _cleanup_free_ ExecStatusInfo *info = NULL; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)"); - if (r < 0) - return bus_log_parse_error(r); - - info = new0(ExecStatusInfo, 1); - if (!info) - return log_oom(); - - while ((r = exec_status_info_deserialize(m, info)) > 0) { - - info->name = strdup(name); - if (!info->name) - return log_oom(); - - LIST_PREPEND(exec, i->exec, info); - - info = new0(ExecStatusInfo, 1); - if (!info) - return log_oom(); - } - - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) { - const char *type, *path; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) { - - r = strv_extend(&i->listen, type); - if (r < 0) - return r; - - r = strv_extend(&i->listen, path); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "DropInPaths")) { - - r = sd_bus_message_read_strv(m, &i->dropin_paths); - if (r < 0) - return bus_log_parse_error(r); - - } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Documentation")) { - - r = sd_bus_message_read_strv(m, &i->documentation); - if (r < 0) - return bus_log_parse_error(r); - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Conditions")) { - const char *cond, *param; - int trigger, negate; - int32_t state; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { - _cleanup_(unit_condition_freep) UnitCondition *c = NULL; - - log_debug("%s trigger=%d negate=%d %s →%d", cond, trigger, negate, param, state); - - c = new0(UnitCondition, 1); - if (!c) - return log_oom(); - - c->name = strdup(cond); - c->param = strdup(param); - if (!c->name || !c->param) - return log_oom(); - - c->trigger = trigger; - c->negate = negate; - c->tristate = state; - - LIST_PREPEND(conditions, i->conditions, c); - c = NULL; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) { - const char *cond, *param; - int trigger, negate; - int32_t state; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { - log_debug("%s %d %d %s %d", cond, trigger, negate, param, state); - if (state < 0 && (!trigger || !i->failed_assert)) { - i->failed_assert = cond; - i->failed_assert_trigger = trigger; - i->failed_assert_negate = negate; - i->failed_assert_parameter = param; - } - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - } else - goto skip; - - break; - - case SD_BUS_TYPE_STRUCT_BEGIN: - - if (streq(name, "LoadError")) { - const char *n, *message; - - r = sd_bus_message_read(m, "(ss)", &n, &message); - if (r < 0) - return bus_log_parse_error(r); - - if (!isempty(message)) - i->load_error = message; - } else - goto skip; - - break; - - default: - goto skip; - } - - return 0; - -skip: - r = sd_bus_message_skip(m, contents); - if (r < 0) - return bus_log_parse_error(r); - - return 0; -} - -#define print_prop(name, fmt, ...) \ - do { \ - if (arg_value) \ - printf(fmt "\n", __VA_ARGS__); \ - else \ - printf("%s=" fmt "\n", name, __VA_ARGS__); \ - } while(0) - -static int print_property(const char *name, sd_bus_message *m, const char *contents) { - int r; - - assert(name); - assert(m); - - /* This is a low-level property printer, see - * print_status_info() for the nicer output */ - - if (arg_properties && !strv_find(arg_properties, name)) { - /* skip what we didn't read */ - r = sd_bus_message_skip(m, contents); - return r; - } - - switch (contents[0]) { - - case SD_BUS_TYPE_STRUCT_BEGIN: - - if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) { - uint32_t u; - - r = sd_bus_message_read(m, "(uo)", &u, NULL); - if (r < 0) - return bus_log_parse_error(r); - - if (u > 0) - print_prop(name, "%"PRIu32, u); - else if (arg_all) - print_prop(name, "%s", ""); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Unit")) { - const char *s; - - r = sd_bus_message_read(m, "(so)", &s, NULL); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all || !isempty(s)) - print_prop(name, "%s", s); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "LoadError")) { - const char *a = NULL, *b = NULL; - - r = sd_bus_message_read(m, "(ss)", &a, &b); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all || !isempty(a) || !isempty(b)) - print_prop(name, "%s \"%s\"", strempty(a), strempty(b)); - - return 0; - } else if (streq_ptr(name, "SystemCallFilter")) { - _cleanup_strv_free_ char **l = NULL; - int whitelist; - - r = sd_bus_message_enter_container(m, 'r', "bas"); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read(m, "b", &whitelist); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_read_strv(m, &l); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all || whitelist || !strv_isempty(l)) { - bool first = true; - char **i; - - if (!arg_value) { - fputs(name, stdout); - fputc('=', stdout); - } - - if (!whitelist) - fputc('~', stdout); - - STRV_FOREACH(i, l) { - if (first) - first = false; - else - fputc(' ', stdout); - - fputs(*i, stdout); - } - fputc('\n', stdout); - } - - return 0; - } - - break; - - case SD_BUS_TYPE_ARRAY: - - if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "EnvironmentFiles")) { - const char *path; - int ignore; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sb)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0) - print_prop("EnvironmentFile", "%s (ignore_errors=%s)", path, yes_no(ignore)); - - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Paths")) { - const char *type, *path; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - print_prop(type, "%s", path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) { - const char *type, *path; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - if (arg_value) - puts(path); - else - printf("Listen%s=%s\n", type, path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Timers")) { - const char *base; - uint64_t value, next_elapse; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(stt)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) { - char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; - - print_prop(base, "{ value=%s ; next_elapse=%s }", - format_timespan(timespan1, sizeof(timespan1), value, 0), - format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) { - ExecStatusInfo info = {}; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = exec_status_info_deserialize(m, &info)) > 0) { - char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX]; - _cleanup_free_ char *tt; - - tt = strv_join(info.argv, " "); - - print_prop(name, - "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }", - strna(info.path), - strna(tt), - yes_no(info.ignore), - strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), - strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), - info.pid, - sigchld_code_to_string(info.code), - info.status, - info.code == CLD_EXITED ? "" : "/", - strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); - - free(info.path); - strv_free(info.argv); - zero(info); - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "DeviceAllow")) { - const char *path, *rwm; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0) - print_prop(name, "%s %s", strna(path), strna(rwm)); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "IODeviceWeight") || streq(name, "BlockIODeviceWeight"))) { - const char *path; - uint64_t weight; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0) - print_prop(name, "%s %"PRIu64, strna(path), weight); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (cgroup_io_limit_type_from_string(name) >= 0 || - streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { - const char *path; - uint64_t bandwidth; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0) - print_prop(name, "%s %"PRIu64, strna(path), bandwidth); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; - } - - break; - } - - r = bus_print_property(name, m, arg_value, arg_all); - if (r < 0) - return bus_log_parse_error(r); - - if (r == 0) { - r = sd_bus_message_skip(m, contents); - if (r < 0) - return bus_log_parse_error(r); - - if (arg_all) - printf("%s=[unprintable]\n", name); - } - - return 0; -} - -static int show_one( - const char *verb, - sd_bus *bus, - const char *path, - const char *unit, - bool show_properties, - bool *new_line, - bool *ellipsized) { - - static const struct bus_properties_map property_map[] = { - { "LoadState", "s", map_string_no_copy, offsetof(UnitStatusInfo, load_state) }, - { "ActiveState", "s", map_string_no_copy, offsetof(UnitStatusInfo, active_state) }, - {} - }; - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_set_free_ Set *found_properties = NULL; - _cleanup_(unit_status_info_free) UnitStatusInfo info = { - .memory_current = (uint64_t) -1, - .memory_high = CGROUP_LIMIT_MAX, - .memory_max = CGROUP_LIMIT_MAX, - .memory_limit = (uint64_t) -1, - .cpu_usage_nsec = (uint64_t) -1, - .tasks_current = (uint64_t) -1, - .tasks_max = (uint64_t) -1, - }; - int r; - - assert(path); - assert(new_line); - - log_debug("Showing one %s", path); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); - if (r < 0) - return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); - - if (unit) { - r = bus_message_map_all_properties(reply, property_map, &info); - if (r < 0) - return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); - - if (streq_ptr(info.load_state, "not-found") && streq_ptr(info.active_state, "inactive")) { - log_error("Unit %s could not be found.", unit); - - if (streq(verb, "status")) - return EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN; - - return -ENOENT; - } - - r = sd_bus_message_rewind(reply, true); - if (r < 0) - return log_error_errno(r, "Failed to rewind: %s", bus_error_message(&error, r)); - } - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); - if (r < 0) - return bus_log_parse_error(r); - - if (*new_line) - printf("\n"); - - *new_line = true; - - while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { - const char *name, *contents; - - r = sd_bus_message_read(reply, "s", &name); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_peek_type(reply, NULL, &contents); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); - if (r < 0) - return bus_log_parse_error(r); - - if (show_properties) { - r = set_ensure_allocated(&found_properties, &string_hash_ops); - if (r < 0) - return log_oom(); - - r = set_put(found_properties, name); - if (r < 0 && r != EEXIST) - return log_oom(); - - r = print_property(name, reply, contents); - } else - r = status_property(name, reply, &info, contents); - if (r < 0) - return r; - - r = sd_bus_message_exit_container(reply); - 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 (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 = 0; - if (show_properties) { - char **pp; - - STRV_FOREACH(pp, arg_properties) - if (!set_contains(found_properties, *pp)) { - log_warning("Property %s does not exist.", *pp); - r = -ENXIO; - } - - } else if (streq(verb, "help")) - show_unit_help(&info); - else if (streq(verb, "status")) { - print_status_info(bus, &info, ellipsized); - - if (info.active_state && STR_IN_SET(info.active_state, "inactive", "failed")) - r = EXIT_PROGRAM_NOT_RUNNING; - else - r = EXIT_PROGRAM_RUNNING_OR_SERVICE_OK; - } - - return r; -} - -static int get_unit_dbus_path_by_pid( - sd_bus *bus, - uint32_t pid, - char **unit) { - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - char *u; - int r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitByPID", - &error, - &reply, - "u", pid); - 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) - return bus_log_parse_error(r); - - u = strdup(u); - if (!u) - return log_oom(); - - *unit = u; - return 0; -} - -static int show_all( - const char* verb, - sd_bus *bus, - bool show_properties, - bool *new_line, - bool *ellipsized) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ UnitInfo *unit_infos = NULL; - const UnitInfo *u; - unsigned c; - int r, ret = 0; - - r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - c = (unsigned) r; - - qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info); - - for (u = unit_infos; u < unit_infos + c; u++) { - _cleanup_free_ char *p = NULL; - - p = unit_dbus_path_from_name(u->id); - if (!p) - return log_oom(); - - r = show_one(verb, bus, p, u->id, show_properties, new_line, ellipsized); - if (r < 0) - return r; - else if (r > 0 && ret == 0) - ret = r; - } - - return ret; -} - -static int show_system_status(sd_bus *bus) { - char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; - _cleanup_free_ char *hn = NULL; - _cleanup_(machine_info_clear) struct machine_info mi = {}; - const char *on, *off; - int r; - - hn = gethostname_malloc(); - if (!hn) - return log_oom(); - - r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi); - if (r < 0) - return log_error_errno(r, "Failed to read server status: %m"); - - if (streq_ptr(mi.state, "degraded")) { - on = ansi_highlight_red(); - off = ansi_normal(); - } else if (!streq_ptr(mi.state, "running")) { - on = ansi_highlight_yellow(); - off = ansi_normal(); - } else - on = off = ""; - - printf("%s%s%s %s\n", on, special_glyph(BLACK_CIRCLE), off, arg_host ? arg_host : hn); - - printf(" State: %s%s%s\n", - on, strna(mi.state), off); - - printf(" Jobs: %" PRIu32 " queued\n", mi.n_jobs); - printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units); - - printf(" Since: %s; %s\n", - format_timestamp(since2, sizeof(since2), mi.timestamp), - format_timestamp_relative(since1, sizeof(since1), mi.timestamp)); - - printf(" CGroup: %s\n", mi.control_group ?: "/"); - if (IN_SET(arg_transport, - BUS_TRANSPORT_LOCAL, - BUS_TRANSPORT_MACHINE)) { - static const char prefix[] = " "; - unsigned c; - - c = columns(); - if (c > sizeof(prefix) - 1) - c -= sizeof(prefix) - 1; - else - c = 0; - - show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags()); - } - - return 0; -} - -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(argv); - - show_properties = streq(argv[0], "show"); - show_status = streq(argv[0], "status"); - show_help = streq(argv[0], "help"); - - if (show_help && argc <= 1) { - log_error("This command expects one or more unit names. Did you mean --help?"); - return -EINVAL; - } - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - if (show_status) - /* Increase max number of open files to 16K if we can, we - * might needs this when browsing journal files, which might - * be split up into many files. */ - setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); - - /* If no argument is specified inspect the manager itself */ - if (show_properties && argc <= 1) - return show_one(argv[0], bus, "/org/freedesktop/systemd1", NULL, show_properties, &new_line, &ellipsized); - - if (show_status && argc <= 1) { - - show_system_status(bus); - new_line = true; - - if (arg_all) - ret = show_all(argv[0], bus, false, &new_line, &ellipsized); - } else { - _cleanup_free_ char **patterns = NULL; - char **name; - - STRV_FOREACH(name, strv_skip(argv, 1)) { - _cleanup_free_ char *path = NULL, *unit = NULL; - uint32_t id; - - if (safe_atou32(*name, &id) < 0) { - if (strv_push(&patterns, *name) < 0) - return log_oom(); - - continue; - } else if (show_properties) { - /* Interpret as job id */ - if (asprintf(&path, "/org/freedesktop/systemd1/job/%u", id) < 0) - return log_oom(); - - } else { - /* Interpret as PID */ - r = get_unit_dbus_path_by_pid(bus, id, &path); - if (r < 0) { - ret = r; - continue; - } - - r = unit_name_from_dbus_path(path, &unit); - if (r < 0) - return log_oom(); - } - - r = show_one(argv[0], bus, path, unit, show_properties, &new_line, &ellipsized); - if (r < 0) - return r; - else if (r > 0 && ret == 0) - ret = r; - } - - if (!strv_isempty(patterns)) { - _cleanup_strv_free_ char **names = NULL; - - r = expand_names(bus, patterns, NULL, &names); - if (r < 0) - return log_error_errno(r, "Failed to expand names: %m"); - - STRV_FOREACH(name, names) { - _cleanup_free_ char *path; - - path = unit_dbus_path_from_name(*name); - if (!path) - return log_oom(); - - r = show_one(argv[0], bus, path, *name, show_properties, &new_line, &ellipsized); - if (r < 0) - return r; - if (r > 0 && ret == 0) - ret = r; - } - } - } - - if (ellipsized && !arg_quiet) - printf("Hint: Some lines were ellipsized, use -l to show in full.\n"); - - return ret; -} - -static int cat_file(const char *filename, bool newline) { - _cleanup_close_ int fd; - - fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - printf("%s%s# %s%s\n", - newline ? "\n" : "", - ansi_highlight_blue(), - filename, - ansi_normal()); - fflush(stdout); - - return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false); -} - -static int cat(int argc, char *argv[], void *userdata) { - _cleanup_lookup_paths_free_ LookupPaths lp = {}; - _cleanup_strv_free_ char **names = NULL; - char **name; - sd_bus *bus; - bool first = true; - int r; - - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot remotely cat units."); - return -EINVAL; - } - - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); - if (r < 0) - return log_error_errno(r, "Failed to determine unit paths: %m"); - - 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"); - - pager_open(arg_no_pager, false); - - STRV_FOREACH(name, names) { - _cleanup_free_ char *fragment_path = NULL; - _cleanup_strv_free_ char **dropin_paths = NULL; - char **path; - - r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths); - if (r < 0) - return r; - else if (r == 0) - return -ENOENT; - - if (first) - first = false; - else - puts(""); - - if (fragment_path) { - r = cat_file(fragment_path, false); - if (r < 0) - return log_warning_errno(r, "Failed to cat %s: %m", fragment_path); - } - - STRV_FOREACH(path, dropin_paths) { - r = cat_file(*path, path == dropin_paths); - if (r < 0) - return log_warning_errno(r, "Failed to cat %s: %m", *path); - } - } - - return 0; -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetUnitProperties"); - if (r < 0) - return bus_log_create_error(r); - - r = unit_name_mangle(argv[1], UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to mangle unit name: %m"); - - r = sd_bus_message_append(m, "sb", n, arg_runtime); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - STRV_FOREACH(i, strv_skip(argv, 2)) { - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) - return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r)); - - return 0; -} - -static int daemon_reload(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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - switch (arg_action) { - - case ACTION_RELOAD: - method = "Reload"; - break; - - case ACTION_REEXEC: - method = "Reexecute"; - break; - - case ACTION_SYSTEMCTL: - method = streq(argv[0], "daemon-reexec") ? "Reexecute" : - /* "daemon-reload" */ "Reload"; - break; - - default: - assert_not_reached("Unexpected action"); - } - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); - if (r < 0) - return bus_log_create_error(r); - - /* Note we use an extra-long timeout here. This is because a reload or reexec means generators are rerun which - * are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can have - * their timeout, and for everything else there's the same time budget in place. */ - - r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); - - /* On reexecution, we expect a disconnect, not a reply */ - if (IN_SET(r, -ETIMEDOUT, -ECONNRESET) && streq(method, "Reexecute")) - r = 0; - - if (r < 0 && arg_action == ACTION_SYSTEMCTL) - return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); - - /* Note that for the legacy commands (i.e. those with action != ACTION_SYSTEMCTL) we support fallbacks to the - * old ways of doing things, hence don't log any error in that case here. */ - - return r < 0 ? r : 0; -} - -static int trivial_method(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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - method = - streq(argv[0], "clear-jobs") || - streq(argv[0], "cancel") ? "ClearJobs" : - streq(argv[0], "reset-failed") ? "ResetFailed" : - streq(argv[0], "halt") ? "Halt" : - streq(argv[0], "reboot") ? "Reboot" : - streq(argv[0], "kexec") ? "KExec" : - streq(argv[0], "exit") ? "Exit" : - /* poweroff */ "PowerOff"; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - &error, - NULL, - NULL); - if (r < 0 && arg_action == ACTION_SYSTEMCTL) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); - - /* Note that for the legacy commands (i.e. those with action != ACTION_SYSTEMCTL) we support fallbacks to the - * old ways of doing things, hence don't log any error in that case here. */ - - return r < 0 ? r : 0; -} - -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 (argc <= 1) - return trivial_method(argc, argv, userdata); - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ResetFailedUnit", - &error, - NULL, - "s", *name); - if (q < 0) { - log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q)); - if (r == 0) - r = q; - } - } - - return r; -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - pager_open(arg_no_pager, false); - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Environment", - &error, - &reply, - "as"); - 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) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) - puts(text); - 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); - - return 0; -} - -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; - sd_bus *bus; - int r; - - 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 = argv[1]; - - if (argc >= 3) - init = argv[2]; - else { - r = parse_env_file("/proc/cmdline", WHITESPACE, - "init", &cmdline_init, - NULL); - if (r < 0) - log_debug_errno(r, "Failed to parse /proc/cmdline: %m"); - - init = cmdline_init; - } - - init = empty_to_null(init); - if (init) { - const char *root_systemd_path = NULL, *root_init_path = NULL; - - root_systemd_path = strjoina(root, "/" SYSTEMD_BINARY_PATH); - root_init_path = strjoina(root, "/", init); - - /* If the passed init is actually the same as the - * systemd binary, then let's suppress it. */ - if (files_same(root_init_path, root_systemd_path) > 0) - 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( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SwitchRoot", - &error, - NULL, - "ss", root, init); - if (r < 0) - return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r)); - - return 0; -} - -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(argc > 1); - assert(argv); - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - method = streq(argv[0], "set-environment") - ? "SetEnvironment" - : "UnsetEnvironment"; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_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) - return log_error_errno(r, "Failed to set environment: %s", bus_error_message(&error, r)); - - return 0; -} - -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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetEnvironment"); - if (r < 0) - return bus_log_create_error(r); - - if (argc < 2) - r = sd_bus_message_append_strv(m, environ); - else { - char **a, **b; - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - STRV_FOREACH(a, strv_skip(argv, 1)) { - - if (!env_name_is_valid(*a)) { - log_error("Not a valid environment variable name: %s", *a); - return -EINVAL; - } - - STRV_FOREACH(b, environ) { - const char *eq; - - eq = startswith(*b, *a); - if (eq && *eq == '=') { - - r = sd_bus_message_append(m, "s", *b); - if (r < 0) - return bus_log_create_error(r); - - break; - } - } - } - - r = sd_bus_message_close_container(m); - } - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, NULL); - if (r < 0) - return log_error_errno(r, "Failed to import environment: %s", bus_error_message(&error, r)); - - return 0; -} - -static int enable_sysv_units(const char *verb, char **args) { - int r = 0; - -#if defined(HAVE_SYSV_COMPAT) - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - unsigned f = 0; - - /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ - - if (arg_scope != UNIT_FILE_SYSTEM) - return 0; - - if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) - return 0; - - if (!STR_IN_SET(verb, - "enable", - "disable", - "is-enabled")) - return 0; - - r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); - if (r < 0) - return r; - - r = 0; - while (args[f]) { - - const char *argv[] = { - ROOTLIBEXECDIR "/systemd-sysv-install", - NULL, - NULL, - NULL, - NULL, - }; - - _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; - bool found_native = false, found_sysv; - siginfo_t status; - const char *name; - unsigned c = 1; - pid_t pid; - int j; - - name = args[f++]; - - if (!endswith(name, ".service")) - continue; - - if (path_is_absolute(name)) - continue; - - j = unit_file_exists(arg_scope, &paths, name); - if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) - return log_error_errno(j, "Failed to lookup unit file state: %m"); - found_native = j != 0; - - /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled, - * prefer the native unit */ - if (found_native && streq(verb, "is-enabled")) - continue; - - p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name); - if (!p) - return log_oom(); - - p[strlen(p) - strlen(".service")] = 0; - found_sysv = access(p, F_OK) >= 0; - if (!found_sysv) - continue; - - if (found_native) - log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); - else - log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); - - if (!isempty(arg_root)) - argv[c++] = q = strappend("--root=", arg_root); - - argv[c++] = verb; - argv[c++] = basename(p); - argv[c] = NULL; - - l = strv_join((char**)argv, " "); - if (!l) - return log_oom(); - - log_info("Executing: %s", l); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execv(argv[0], (char**) argv); - log_error_errno(errno, "Failed to execute %s: %m", argv[0]); - _exit(EXIT_FAILURE); - } - - j = wait_for_terminate(pid, &status); - if (j < 0) - return log_error_errno(j, "Failed to wait for child: %m"); - - if (status.si_code == CLD_EXITED) { - if (streq(verb, "is-enabled")) { - if (status.si_status == 0) { - if (!arg_quiet) - puts("enabled"); - r = 1; - } else { - if (!arg_quiet) - puts("disabled"); - } - - } else if (status.si_status != 0) - return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ - } else { - log_error("Unexpected waitid() result."); - return -EPROTO; - } - - if (found_native) - continue; - - /* Remove this entry, so that we don't try enabling it as native unit */ - assert(f > 0); - f--; - assert(args[f] == name); - strv_remove(args, name); - } - -#endif - return r; -} - -static int mangle_names(char **original_names, char ***mangled_names) { - char **i, **l, **name; - int r; - - l = i = new(char*, strv_length(original_names) + 1); - if (!l) - return log_oom(); - - STRV_FOREACH(name, original_names) { - - /* When enabling units qualified path names are OK, - * too, hence allow them explicitly. */ - - if (is_path(*name)) { - *i = strdup(*name); - if (!*i) { - strv_free(l); - return log_oom(); - } - } else { - r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i); - if (r < 0) { - strv_free(l); - return log_error_errno(r, "Failed to mangle unit name: %m"); - } - } - - i++; - } - - *i = NULL; - *mangled_names = l; - - return 0; -} - -static int unit_exists(const char *unit) { - _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; - static const struct bus_properties_map property_map[] = { - { "LoadState", "s", map_string_no_copy, offsetof(UnitStatusInfo, load_state) }, - { "ActiveState", "s", map_string_no_copy, offsetof(UnitStatusInfo, active_state)}, - {}, - }; - UnitStatusInfo info = {}; - sd_bus *bus; - int r; - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); - if (r < 0) - return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); - - r = bus_message_map_all_properties(reply, property_map, &info); - if (r < 0) - return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); - - return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive"); -} - -static int enable_unit(int argc, char *argv[], void *userdata) { - _cleanup_strv_free_ char **names = NULL; - const char *verb = argv[0]; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int carries_install_info = -1; - bool ignore_carries_install_info = arg_quiet; - int r; - - if (!argv[1]) - return 0; - - r = mangle_names(strv_skip(argv, 1), &names); - if (r < 0) - return r; - - r = enable_sysv_units(verb, names); - if (r < 0) - return r; - - /* If the operation was fully executed by the SysV compat, let's finish early */ - if (strv_isempty(names)) { - if (arg_no_reload || install_client_side()) - return 0; - return daemon_reload(argc, argv, userdata); - } - - 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; - } else if (streq(verb, "disable")) - r = unit_file_disable(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); - else if (streq(verb, "reenable")) { - r = unit_file_reenable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); - carries_install_info = r; - } else if (streq(verb, "link")) - r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); - else if (streq(verb, "preset")) { - r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes); - } else if (streq(verb, "mask")) - r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); - else if (streq(verb, "unmask")) - r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); - else if (streq(verb, "revert")) - r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes); - else - assert_not_reached("Unknown verb"); - - unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet); - if (r < 0) - goto finish; - r = 0; - } else { - _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; - bool expect_carries_install_info = false; - bool send_runtime = true, send_force = true, send_preset_mode = false; - const char *method; - sd_bus *bus; - - if (STR_IN_SET(verb, "mask", "unmask")) { - r = unit_exists(*names); - if (r < 0) - return r; - if (r == 0) - log_notice("Unit %s does not exist, proceeding anyway.", *names); - } - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - if (streq(verb, "enable")) { - method = "EnableUnitFiles"; - expect_carries_install_info = true; - } else if (streq(verb, "disable")) { - method = "DisableUnitFiles"; - send_force = false; - } else if (streq(verb, "reenable")) { - method = "ReenableUnitFiles"; - expect_carries_install_info = true; - } else if (streq(verb, "link")) - method = "LinkUnitFiles"; - else if (streq(verb, "preset")) { - - if (arg_preset_mode != UNIT_FILE_PRESET_FULL) { - method = "PresetUnitFilesWithMode"; - send_preset_mode = true; - } else - method = "PresetUnitFiles"; - - expect_carries_install_info = true; - ignore_carries_install_info = true; - } else if (streq(verb, "mask")) - method = "MaskUnitFiles"; - else if (streq(verb, "unmask")) { - method = "UnmaskUnitFiles"; - send_force = false; - } else if (streq(verb, "revert")) { - method = "RevertUnitFiles"; - send_runtime = send_force = false; - } else - assert_not_reached("Unknown verb"); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, names); - if (r < 0) - return bus_log_create_error(r); - - if (send_preset_mode) { - r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode)); - if (r < 0) - return bus_log_create_error(r); - } - - if (send_runtime) { - r = sd_bus_message_append(m, "b", arg_runtime); - if (r < 0) - return bus_log_create_error(r); - } - - if (send_force) { - r = sd_bus_message_append(m, "b", arg_force); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) - return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r)); - - if (expect_carries_install_info) { - r = sd_bus_message_read(reply, "b", &carries_install_info); - if (r < 0) - return bus_log_parse_error(r); - } - - r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); - if (r < 0) - goto finish; - - /* Try to reload if enabled */ - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; - } - - if (carries_install_info == 0 && !ignore_carries_install_info) - log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n" - "settings in the [Install] section, and DefaultInstance for template units).\n" - "This means they are not meant to be enabled using systemctl.\n" - "Possible reasons for having this kind of units are:\n" - "1) A unit may be statically enabled by being symlinked from another unit's\n" - " .wants/ or .requires/ directory.\n" - "2) A unit's purpose may be to act as a helper for some other unit which has\n" - " a requirement dependency on it.\n" - "3) A unit may be started when needed via activation (socket, path, timer,\n" - " D-Bus, udev, scripted systemctl call, ...).\n" - "4) In case of template units, the unit is meant to be enabled with some\n" - " instance name specified."); - - 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; - - 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(strv_length(new_args), new_args, userdata); - } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; -} - -static int add_dependency(int argc, char *argv[], void *userdata) { - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *target = NULL; - const char *verb = argv[0]; - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - UnitDependency dep; - int r = 0; - - if (!argv[1]) - return 0; - - 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(strv_skip(argv, 2), &names); - if (r < 0) - return r; - - if (streq(verb, "add-wants")) - dep = UNIT_WANTS; - else if (streq(verb, "add-requires")) - dep = UNIT_REQUIRES; - else - assert_not_reached("Unknown verb"); - - if (install_client_side()) { - r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - unit_file_dump_changes(r, "add dependency on", changes, n_changes, arg_quiet); - - if (r > 0) - r = 0; - } else { - _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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "AddDependencyUnitFiles"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, names); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "ssbb", target, unit_dependency_to_string(dep), arg_runtime, arg_force); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) - return log_error_errno(r, "Failed to add dependency: %s", bus_error_message(&error, r)); - - r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); - if (r < 0) - goto finish; - - if (arg_no_reload) { - r = 0; - goto finish; - } - - r = daemon_reload(argc, argv, userdata); - } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; -} - -static int preset_all(int argc, char *argv[], void *userdata) { - UnitFileChange *changes = NULL; - unsigned n_changes = 0; - int r; - - if (install_client_side()) { - r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); - unit_file_dump_changes(r, "preset", changes, n_changes, arg_quiet); - - if (r > 0) - r = 0; - } else { - _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; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "PresetAllUnitFiles", - &error, - &reply, - "sbb", - unit_file_preset_mode_to_string(arg_preset_mode), - arg_runtime, - arg_force); - if (r < 0) - return log_error_errno(r, "Failed to preset all units: %s", bus_error_message(&error, r)); - - r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes); - if (r < 0) - goto finish; - - if (arg_no_reload) { - r = 0; - goto finish; - } - - r = daemon_reload(argc, argv, userdata); - } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; -} - -static int unit_is_enabled(int argc, char *argv[], void *userdata) { - - _cleanup_strv_free_ char **names = NULL; - bool enabled; - char **name; - int r; - - r = mangle_names(strv_skip(argv, 1), &names); - if (r < 0) - return r; - - r = enable_sysv_units(argv[0], names); - if (r < 0) - return r; - - enabled = r > 0; - - if (install_client_side()) { - - STRV_FOREACH(name, names) { - UnitFileState state; - - 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, - UNIT_FILE_ENABLED, - UNIT_FILE_ENABLED_RUNTIME, - UNIT_FILE_STATIC, - UNIT_FILE_INDIRECT, - UNIT_FILE_GENERATED)) - enabled = true; - - if (!arg_quiet) - puts(unit_file_state_to_string(state)); - } - - r = 0; - } 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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *s; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileState", - &error, - &reply, - "s", *name); - 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) - return bus_log_parse_error(r); - - if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated")) - enabled = true; - - if (!arg_quiet) - puts(s); - } - } - - return enabled ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int is_system_running(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *state = NULL; - sd_bus *bus; - int r; - - if (running_in_chroot() > 0 || (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", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SystemState", - NULL, - &state); - if (r < 0) { - if (!arg_quiet) - puts("unknown"); - return 0; - } - - if (!arg_quiet) - puts(state); - - return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) { - _cleanup_free_ char *t = NULL; - int r; - - assert(new_path); - assert(original_path); - assert(ret_tmp_fn); - - r = tempfn_random(new_path, NULL, &t); - if (r < 0) - return log_error_errno(r, "Failed to determine temporary filename for \"%s\": %m", new_path); - - r = mkdir_parents(new_path, 0755); - 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) - return log_error_errno(r, "Failed to create temporary file \"%s\": %m", t); - - } else if (r < 0) - return log_error_errno(r, "Failed to create temporary file for \"%s\": %m", new_path); - - *ret_tmp_fn = t; - t = NULL; - - return 0; -} - -static int get_file_to_edit( - const LookupPaths *paths, - const char *name, - char **ret_path) { - - _cleanup_free_ char *path = NULL, *run = NULL; - - assert(name); - assert(ret_path); - - path = strjoin(paths->persistent_config, "/", name, NULL); - if (!path) - return log_oom(); - - if (arg_runtime) { - run = strjoin(paths->runtime_config, "/", name, NULL); - if (!run) - return log_oom(); - } - - if (arg_runtime) { - if (access(path, F_OK) >= 0) { - log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path); - return -EEXIST; - } - - *ret_path = run; - run = NULL; - } else { - *ret_path = path; - path = NULL; - } - - return 0; -} - -static int unit_file_create_new( - const LookupPaths *paths, - const char *unit_name, - const char *suffix, - char **ret_new_path, - char **ret_tmp_path) { - - char *tmp_new_path, *tmp_tmp_path, *ending; - int r; - - assert(unit_name); - assert(ret_new_path); - assert(ret_tmp_path); - - ending = strjoina(unit_name, suffix); - r = get_file_to_edit(paths, ending, &tmp_new_path); - if (r < 0) - return r; - - r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path); - if (r < 0) { - free(tmp_new_path); - return r; - } - - *ret_new_path = tmp_new_path; - *ret_tmp_path = tmp_tmp_path; - - return 0; -} - -static int unit_file_create_copy( - const LookupPaths *paths, - const char *unit_name, - const char *fragment_path, - char **ret_new_path, - char **ret_tmp_path) { - - char *tmp_new_path, *tmp_tmp_path; - int r; - - assert(fragment_path); - assert(unit_name); - assert(ret_new_path); - assert(ret_tmp_path); - - r = get_file_to_edit(paths, unit_name, &tmp_new_path); - if (r < 0) - return r; - - if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) { - char response; - - r = ask_char(&response, "yn", "\"%s\" already exists. Overwrite with \"%s\"? [(y)es, (n)o] ", tmp_new_path, fragment_path); - if (r < 0) { - free(tmp_new_path); - return r; - } - if (response != 'y') { - log_warning("%s ignored", unit_name); - free(tmp_new_path); - return -EKEYREJECTED; - } - } - - r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path); - if (r < 0) { - free(tmp_new_path); - return r; - } - - *ret_new_path = tmp_new_path; - *ret_tmp_path = tmp_tmp_path; - - return 0; -} - -static int run_editor(char **paths) { - pid_t pid; - int r; - - assert(paths); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { - const char **args; - char *editor, **editor_args = NULL; - char **tmp_path, **original_path, *p; - unsigned n_editor_args = 0, i = 1; - size_t argc; - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - argc = strv_length(paths)/2 + 1; - - /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL - * If neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present, - * we try to execute well known editors - */ - editor = getenv("SYSTEMD_EDITOR"); - if (!editor) - editor = getenv("EDITOR"); - if (!editor) - editor = getenv("VISUAL"); - - if (!isempty(editor)) { - editor_args = strv_split(editor, WHITESPACE); - if (!editor_args) { - (void) log_oom(); - _exit(EXIT_FAILURE); - } - n_editor_args = strv_length(editor_args); - argc += n_editor_args - 1; - } - args = newa(const char*, argc + 1); - - if (n_editor_args > 0) { - args[0] = editor_args[0]; - for (; i < n_editor_args; i++) - args[i] = editor_args[i]; - } - - STRV_FOREACH_PAIR(original_path, tmp_path, paths) { - args[i] = *tmp_path; - i++; - } - args[i] = NULL; - - if (n_editor_args > 0) - execvp(args[0], (char* const*) args); - - FOREACH_STRING(p, "editor", "nano", "vim", "vi") { - args[0] = p; - execvp(p, (char* const*) args); - /* We do not fail if the editor doesn't exist - * because we want to try each one of them before - * failing. - */ - if (errno != ENOENT) { - log_error_errno(errno, "Failed to execute %s: %m", editor); - _exit(EXIT_FAILURE); - } - } - - log_error("Cannot edit unit(s), no editor available. Please set either $SYSTEMD_EDITOR, $EDITOR or $VISUAL."); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn("editor", pid, true); - if (r < 0) - return log_error_errno(r, "Failed to wait for child: %m"); - - return 0; -} - -static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { - _cleanup_lookup_paths_free_ LookupPaths lp = {}; - char **name; - int r; - - assert(names); - assert(paths); - - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); - if (r < 0) - return r; - - STRV_FOREACH(name, names) { - _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL; - - r = unit_find_paths(bus, *name, &lp, &path, NULL); - if (r < 0) - return r; - else if (!arg_force) { - if (r == 0) { - log_error("Run 'systemctl edit --force %s' to create a new unit.", *name); - return -ENOENT; - } else if (!path) { - // FIXME: support units with path==NULL (no FragmentPath) - log_error("No fragment exists for %s.", *name); - return -ENOENT; - } - } - - if (path) { - if (arg_full) - r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path); - else - r = unit_file_create_new(&lp, *name, ".d/override.conf", &new_path, &tmp_path); - } else - r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path); - if (r < 0) - return r; - - r = strv_push_pair(paths, new_path, tmp_path); - if (r < 0) - return log_oom(); - new_path = tmp_path = NULL; - } - - return 0; -} - -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; - - if (!on_tty()) { - log_error("Cannot edit units if not on a tty."); - return -EINVAL; - } - - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot edit units remotely."); - return -EINVAL; - } - - 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"); - - r = find_paths_to_edit(bus, names, &paths); - if (r < 0) - return r; - - if (strv_isempty(paths)) - return -ENOENT; - - r = run_editor(paths); - if (r < 0) - 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 (null_or_empty_path(*tmp)) { - 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); - goto end; - } - } - - r = 0; - - if (!arg_no_reload && !install_client_side()) - r = daemon_reload(argc, argv, userdata); - -end: - STRV_FOREACH_PAIR(original, tmp, paths) { - (void) unlink(*tmp); - - /* Removing empty dropin dirs */ - if (!arg_full) { - _cleanup_free_ char *dir; - - dir = dirname_malloc(*original); - if (!dir) - return log_oom(); - - /* no need to check if the dir is empty, rmdir - * does nothing if it is not the case. - */ - (void) rmdir(dir); - } - } - - return r; -} - -static void systemctl_help(void) { - - pager_open(arg_no_pager, false); - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Query or send control commands to the systemd manager.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --system Connect to system manager\n" - " --user Connect to user service manager\n" - " -H --host=[USER@]HOST\n" - " Operate on remote host\n" - " -M --machine=CONTAINER\n" - " Operate on local container\n" - " -t --type=TYPE List units of a particular type\n" - " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n" - " -p --property=NAME Show only properties by this name\n" - " -a --all Show all loaded units/properties, including dead/empty\n" - " ones. To list all units installed on the system, use\n" - " the 'list-unit-files' command instead.\n" - " -l --full Don't ellipsize unit names on output\n" - " -r --recursive Show unit list of host and local containers\n" - " --reverse Show reverse dependencies with 'list-dependencies'\n" - " --job-mode=MODE Specify how to deal with already queued jobs, when\n" - " queueing a new job\n" - " --show-types When showing sockets, explicitly show their type\n" - " --value When showing properties, only print the value\n" - " -i --ignore-inhibitors\n" - " When shutting down or sleeping, ignore inhibitors\n" - " --kill-who=WHO Who to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" - " --now Start or stop unit in addition to enabling or disabling it\n" - " -q --quiet Suppress output\n" - " --no-block Do not wait until operation finished\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n" - " --no-reload Don't reload daemon after en-/dis-abling unit files\n" - " --no-legend Do not print a legend (column headers and hints)\n" - " --no-pager Do not pipe output into a pager\n" - " --no-ask-password\n" - " Do not ask for system passwords\n" - " --global Enable/disable unit files globally\n" - " --runtime Enable unit files only temporarily until next reboot\n" - " -f --force When enabling unit files, override existing symlinks\n" - " When shutting down, execute action immediately\n" - " --preset-mode= Apply only enable, only disable, or all presets\n" - " --root=PATH Enable unit files in the specified root directory\n" - " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" - " --firmware-setup Tell the firmware to show the setup menu on next boot\n" - " --plain Print unit dependencies as a list instead of a tree\n\n" - "Unit Commands:\n" - " list-units [PATTERN...] List loaded units\n" - " list-sockets [PATTERN...] List loaded sockets ordered by address\n" - " list-timers [PATTERN...] List loaded timers ordered by next elapse\n" - " start NAME... Start (activate) one or more units\n" - " stop NAME... Stop (deactivate) one or more units\n" - " reload NAME... Reload one or more units\n" - " restart NAME... Start or restart one or more units\n" - " try-restart NAME... Restart one or more units if active\n" - " reload-or-restart NAME... Reload one or more units if possible,\n" - " otherwise start or restart\n" - " try-reload-or-restart NAME... If active, reload one or more units,\n" - " if supported, otherwise restart\n" - " isolate NAME Start one unit and stop all others\n" - " kill NAME... Send signal to processes of a unit\n" - " is-active PATTERN... Check whether units are active\n" - " is-failed PATTERN... Check whether units are failed\n" - " status [PATTERN...|PID...] Show runtime status of one or more units\n" - " show [PATTERN...|JOB...] Show properties of one or more\n" - " units/jobs or the manager\n" - " cat PATTERN... Show files and drop-ins of one or more units\n" - " set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n" - " help PATTERN...|PID... Show manual for one or more units\n" - " reset-failed [PATTERN...] Reset failed state for all, one, or more\n" - " units\n" - " list-dependencies [NAME] Recursively show units which are required\n" - " or wanted by this unit or by which this\n" - " unit is required or wanted\n\n" - "Unit File Commands:\n" - " list-unit-files [PATTERN...] List installed unit files\n" - " enable [NAME...|PATH...] Enable one or more unit files\n" - " disable NAME... Disable one or more unit files\n" - " reenable NAME... Reenable one or more unit files\n" - " preset NAME... Enable/disable one or more unit files\n" - " based on preset configuration\n" - " preset-all Enable/disable all unit files based on\n" - " preset configuration\n" - " is-enabled NAME... Check whether unit files are enabled\n" - " mask NAME... Mask one or more units\n" - " unmask NAME... Unmask one or more units\n" - " link PATH... Link one or more units files into\n" - " the search path\n" - " revert NAME... Revert one or more unit files to vendor\n" - " version\n" - " add-wants TARGET NAME... Add 'Wants' dependency for the target\n" - " on specified one or more units\n" - " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" - " on specified one or more units\n" - " edit NAME... Edit one or more unit files\n" - " get-default Get the name of the default target\n" - " set-default NAME Set the default target\n\n" - "Machine Commands:\n" - " list-machines [PATTERN...] List local containers and host\n\n" - "Job Commands:\n" - " list-jobs [PATTERN...] List jobs\n" - " cancel [JOB...] Cancel all, one, or more jobs\n\n" - "Environment Commands:\n" - " show-environment Dump environment\n" - " set-environment NAME=VALUE... Set one or more environment variables\n" - " unset-environment NAME... Unset one or more environment variables\n" - " import-environment [NAME...] Import all or some environment variables\n\n" - "Manager Lifecycle Commands:\n" - " daemon-reload Reload systemd manager configuration\n" - " daemon-reexec Reexecute systemd manager\n\n" - "System Commands:\n" - " is-system-running Check whether system is fully running\n" - " default Enter system default mode\n" - " rescue Enter system rescue mode\n" - " emergency Enter system emergency mode\n" - " halt Shut down and halt the system\n" - " poweroff Shut down and power-off the system\n" - " reboot [ARG] Shut down and reboot the system\n" - " kexec Shut down and reboot the system with kexec\n" - " exit [EXIT_CODE] Request user instance or container exit\n" - " switch-root ROOT [INIT] Change to a different root file system\n" - " suspend Suspend the system\n" - " hibernate Hibernate the system\n" - " hybrid-sleep Hibernate and suspend the system\n", - program_invocation_short_name); -} - -static void halt_help(void) { - printf("%s [OPTIONS...]%s\n\n" - "%s the system.\n\n" - " --help Show this help\n" - " --halt Halt the machine\n" - " -p --poweroff Switch off the machine\n" - " --reboot Reboot the machine\n" - " -f --force Force immediate halt/power-off/reboot\n" - " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n" - " -d --no-wtmp Don't write wtmp record\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n", - program_invocation_short_name, - arg_action == ACTION_REBOOT ? " [ARG]" : "", - arg_action == ACTION_REBOOT ? "Reboot" : - arg_action == ACTION_POWEROFF ? "Power off" : - "Halt"); -} - -static void shutdown_help(void) { - printf("%s [OPTIONS...] [TIME] [WALL...]\n\n" - "Shut down the system.\n\n" - " --help Show this help\n" - " -H --halt Halt the machine\n" - " -P --poweroff Power-off the machine\n" - " -r --reboot Reboot the machine\n" - " -h Equivalent to --poweroff, overridden by --halt\n" - " -k Don't halt/power-off/reboot, just send warnings\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n" - " -c Cancel a pending shutdown\n", - program_invocation_short_name); -} - -static void telinit_help(void) { - printf("%s [OPTIONS...] {COMMAND}\n\n" - "Send control commands to the init daemon.\n\n" - " --help Show this help\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n\n" - "Commands:\n" - " 0 Power-off the machine\n" - " 6 Reboot the machine\n" - " 2, 3, 4, 5 Start runlevelX.target unit\n" - " 1, s, S Enter rescue mode\n" - " q, Q Reload init daemon configuration\n" - " u, U Reexecute init daemon\n", - program_invocation_short_name); -} - -static void runlevel_help(void) { - printf("%s [OPTIONS...]\n\n" - "Prints the previous and current runlevel of the init system.\n\n" - " --help Show this help\n", - program_invocation_short_name); -} - -static void help_types(void) { - int i; - - if (!arg_no_legend) - puts("Available unit types:"); - 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[]) { - - enum { - ARG_FAIL = 0x100, - ARG_REVERSE, - ARG_AFTER, - ARG_BEFORE, - ARG_SHOW_TYPES, - ARG_IRREVERSIBLE, - ARG_IGNORE_DEPENDENCIES, - ARG_VALUE, - ARG_VERSION, - ARG_USER, - ARG_SYSTEM, - ARG_GLOBAL, - ARG_NO_BLOCK, - ARG_NO_LEGEND, - ARG_NO_PAGER, - ARG_NO_WALL, - ARG_ROOT, - ARG_NO_RELOAD, - ARG_KILL_WHO, - ARG_NO_ASK_PASSWORD, - ARG_FAILED, - ARG_RUNTIME, - ARG_FORCE, - ARG_PLAIN, - ARG_STATE, - ARG_JOB_MODE, - ARG_PRESET_MODE, - ARG_FIRMWARE_SETUP, - ARG_NOW, - ARG_MESSAGE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "reverse", no_argument, NULL, ARG_REVERSE }, - { "after", no_argument, NULL, ARG_AFTER }, - { "before", no_argument, NULL, ARG_BEFORE }, - { "show-types", no_argument, NULL, ARG_SHOW_TYPES }, - { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */ - { "full", no_argument, NULL, 'l' }, - { "job-mode", required_argument, NULL, ARG_JOB_MODE }, - { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */ - { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ - { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ - { "ignore-inhibitors", no_argument, NULL, 'i' }, - { "value", no_argument, NULL, ARG_VALUE }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "global", no_argument, NULL, ARG_GLOBAL }, - { "no-block", no_argument, NULL, ARG_NO_BLOCK }, - { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { "quiet", no_argument, NULL, 'q' }, - { "root", required_argument, NULL, ARG_ROOT }, - { "force", no_argument, NULL, ARG_FORCE }, - { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, - { "kill-who", required_argument, NULL, ARG_KILL_WHO }, - { "signal", required_argument, NULL, 's' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "runtime", no_argument, NULL, ARG_RUNTIME }, - { "lines", required_argument, NULL, 'n' }, - { "output", required_argument, NULL, 'o' }, - { "plain", no_argument, NULL, ARG_PLAIN }, - { "state", required_argument, NULL, ARG_STATE }, - { "recursive", no_argument, NULL, 'r' }, - { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, - { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, - { "now", no_argument, NULL, ARG_NOW }, - { "message", required_argument, NULL, ARG_MESSAGE }, - {} - }; - - const char *p; - int c, r; - - assert(argc >= 0); - assert(argv); - - /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ - arg_ask_password = true; - - while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0) - - switch (c) { - - case 'h': - systemctl_help(); - return 0; - - case ARG_VERSION: - return version(); - - case 't': { - if (isempty(optarg)) { - log_error("--type requires arguments."); - return -EINVAL; - } - - p = optarg; - for (;;) { - _cleanup_free_ char *type = NULL; - - r = extract_first_word(&p, &type, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse type: %s", optarg); - - if (r == 0) - break; - - if (streq(type, "help")) { - help_types(); - return 0; - } - - if (unit_type_from_string(type) >= 0) { - if (strv_push(&arg_types, type) < 0) - return log_oom(); - type = NULL; - continue; - } - - /* It's much nicer to use --state= for - * load states, but let's support this - * in --types= too for compatibility - * with old versions */ - if (unit_load_state_from_string(type) >= 0) { - if (strv_push(&arg_states, type) < 0) - return log_oom(); - type = NULL; - continue; - } - - log_error("Unknown unit type or load state '%s'.", type); - log_info("Use -t help to see a list of allowed values."); - return -EINVAL; - } - - break; - } - - case 'p': { - /* Make sure that if the empty property list - was specified, we won't show any properties. */ - if (isempty(optarg) && !arg_properties) { - arg_properties = new0(char*, 1); - if (!arg_properties) - return log_oom(); - } else { - p = optarg; - for (;;) { - _cleanup_free_ char *prop = NULL; - - r = extract_first_word(&p, &prop, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse property: %s", optarg); - - if (r == 0) - break; - - if (strv_push(&arg_properties, prop) < 0) - return log_oom(); - - prop = NULL; - } - } - - /* If the user asked for a particular - * property, show it to him, even if it is - * empty. */ - arg_all = true; - - break; - } - - case 'a': - arg_all = true; - break; - - case ARG_REVERSE: - arg_dependency = DEPENDENCY_REVERSE; - break; - - case ARG_AFTER: - arg_dependency = DEPENDENCY_AFTER; - break; - - case ARG_BEFORE: - arg_dependency = DEPENDENCY_BEFORE; - break; - - case ARG_SHOW_TYPES: - arg_show_types = true; - break; - - case ARG_VALUE: - arg_value = true; - break; - - case ARG_JOB_MODE: - arg_job_mode = optarg; - break; - - case ARG_FAIL: - arg_job_mode = "fail"; - break; - - case ARG_IRREVERSIBLE: - arg_job_mode = "replace-irreversibly"; - break; - - case ARG_IGNORE_DEPENDENCIES: - arg_job_mode = "ignore-dependencies"; - break; - - case ARG_USER: - arg_scope = UNIT_FILE_USER; - break; - - case ARG_SYSTEM: - arg_scope = UNIT_FILE_SYSTEM; - break; - - case ARG_GLOBAL: - arg_scope = UNIT_FILE_GLOBAL; - break; - - case ARG_NO_BLOCK: - arg_no_block = true; - break; - - case ARG_NO_LEGEND: - arg_no_legend = true; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case ARG_NO_WALL: - arg_no_wall = true; - break; - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, false, &arg_root); - if (r < 0) - return r; - break; - - case 'l': - arg_full = true; - break; - - case ARG_FAILED: - if (strv_extend(&arg_states, "failed") < 0) - return log_oom(); - - break; - - case 'q': - arg_quiet = true; - break; - - case ARG_FORCE: - arg_force++; - break; - - case 'f': - arg_force++; - break; - - case ARG_NO_RELOAD: - arg_no_reload = true; - break; - - case ARG_KILL_WHO: - arg_kill_who = optarg; - break; - - case 's': - arg_signal = signal_from_string_try_harder(optarg); - if (arg_signal < 0) { - log_error("Failed to parse signal string %s.", optarg); - return -EINVAL; - } - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_RUNTIME: - arg_runtime = true; - break; - - case 'n': - if (safe_atou(optarg, &arg_lines) < 0) { - log_error("Failed to parse lines '%s'", optarg); - return -EINVAL; - } - break; - - case 'o': - arg_output = output_mode_from_string(optarg); - if (arg_output < 0) { - log_error("Unknown output '%s'.", optarg); - return -EINVAL; - } - break; - - case 'i': - arg_ignore_inhibitors = true; - break; - - case ARG_PLAIN: - arg_plain = true; - break; - - case ARG_FIRMWARE_SETUP: - arg_firmware_setup = true; - break; - - case ARG_STATE: { - if (isempty(optarg)) { - log_error("--signal requires arguments."); - return -EINVAL; - } - - p = optarg; - for (;;) { - _cleanup_free_ char *s = NULL; - - r = extract_first_word(&p, &s, ",", 0); - if (r < 0) - return log_error_errno(r, "Failed to parse signal: %s", optarg); - - 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; - } - - case 'r': - if (geteuid() != 0) { - log_error("--recursive requires root privileges."); - return -EPERM; - } - - arg_recursive = true; - break; - - case ARG_PRESET_MODE: - - arg_preset_mode = unit_file_preset_mode_from_string(optarg); - if (arg_preset_mode < 0) { - log_error("Failed to parse preset mode: %s.", optarg); - return -EINVAL; - } - - break; - - case ARG_NOW: - arg_now = true; - break; - - case ARG_MESSAGE: - if (strv_extend(&arg_wall, optarg) < 0) - return log_oom(); - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) { - log_error("Cannot access user instance remotely."); - return -EINVAL; - } - - return 1; -} - -static int halt_parse_argv(int argc, char *argv[]) { - - enum { - ARG_HELP = 0x100, - ARG_HALT, - ARG_REBOOT, - ARG_NO_WALL - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - { "halt", no_argument, NULL, ARG_HALT }, - { "poweroff", no_argument, NULL, 'p' }, - { "reboot", no_argument, NULL, ARG_REBOOT }, - { "force", no_argument, NULL, 'f' }, - { "wtmp-only", no_argument, NULL, 'w' }, - { "no-wtmp", no_argument, NULL, 'd' }, - { "no-sync", no_argument, NULL, 'n' }, - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - {} - }; - - int c, r, runlevel; - - assert(argc >= 0); - assert(argv); - - if (utmp_get_runlevel(&runlevel, NULL) >= 0) - if (runlevel == '0' || runlevel == '6') - arg_force = 2; - - while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - halt_help(); - return 0; - - case ARG_HALT: - arg_action = ACTION_HALT; - break; - - case 'p': - if (arg_action != ACTION_REBOOT) - arg_action = ACTION_POWEROFF; - break; - - case ARG_REBOOT: - arg_action = ACTION_REBOOT; - break; - - case 'f': - arg_force = 2; - break; - - case 'w': - arg_dry = true; - break; - - case 'd': - arg_no_wtmp = true; - break; - - case 'n': - arg_no_sync = true; - break; - - case ARG_NO_WALL: - arg_no_wall = true; - break; - - case 'i': - case 'h': - /* Compatibility nops */ - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { - r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL); - if (r < 0) - return r; - } else if (optind < argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - return 1; -} - -static int parse_shutdown_time_spec(const char *t, usec_t *_u) { - assert(t); - assert(_u); - - if (streq(t, "now")) - *_u = 0; - else if (!strchr(t, ':')) { - uint64_t u; - - if (safe_atou64(t, &u) < 0) - return -EINVAL; - - *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; - } else { - char *e = NULL; - long hour, minute; - struct tm tm = {}; - time_t s; - usec_t n; - - errno = 0; - hour = strtol(t, &e, 10); - if (errno > 0 || *e != ':' || hour < 0 || hour > 23) - return -EINVAL; - - minute = strtol(e+1, &e, 10); - if (errno > 0 || *e != 0 || minute < 0 || minute > 59) - return -EINVAL; - - n = now(CLOCK_REALTIME); - s = (time_t) (n / USEC_PER_SEC); - - assert_se(localtime_r(&s, &tm)); - - tm.tm_hour = (int) hour; - tm.tm_min = (int) minute; - tm.tm_sec = 0; - - assert_se(s = mktime(&tm)); - - *_u = (usec_t) s * USEC_PER_SEC; - - while (*_u <= n) - *_u += USEC_PER_DAY; - } - - return 0; -} - -static int shutdown_parse_argv(int argc, char *argv[]) { - - enum { - ARG_HELP = 0x100, - ARG_NO_WALL - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - { "halt", no_argument, NULL, 'H' }, - { "poweroff", no_argument, NULL, 'P' }, - { "reboot", no_argument, NULL, 'r' }, - { "kexec", no_argument, NULL, 'K' }, /* not documented extension */ - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - {} - }; - - char **wall = NULL; - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "HPrhkKtafFc", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - shutdown_help(); - return 0; - - case 'H': - arg_action = ACTION_HALT; - break; - - case 'P': - arg_action = ACTION_POWEROFF; - break; - - case 'r': - if (kexec_loaded()) - arg_action = ACTION_KEXEC; - else - arg_action = ACTION_REBOOT; - break; - - case 'K': - arg_action = ACTION_KEXEC; - break; - - case 'h': - if (arg_action != ACTION_HALT) - arg_action = ACTION_POWEROFF; - break; - - case 'k': - arg_dry = true; - break; - - case ARG_NO_WALL: - arg_no_wall = true; - break; - - case 't': - case 'a': - case 'f': - case 'F': - /* Compatibility nops */ - break; - - case 'c': - arg_action = ACTION_CANCEL_SHUTDOWN; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { - r = parse_shutdown_time_spec(argv[optind], &arg_when); - if (r < 0) { - log_error("Failed to parse time specification: %s", argv[optind]); - return r; - } - } else - arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; - - if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) - /* No time argument for shutdown cancel */ - wall = argv + optind; - else if (argc > optind + 1) - /* We skip the time argument */ - wall = argv + optind + 1; - - if (wall) { - arg_wall = strv_copy(wall); - if (!arg_wall) - return log_oom(); - } - - optind = argc; - - return 1; -} - -static int telinit_parse_argv(int argc, char *argv[]) { - - enum { - ARG_HELP = 0x100, - ARG_NO_WALL - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - {} - }; - - static const struct { - char from; - enum action to; - } table[] = { - { '0', ACTION_POWEROFF }, - { '6', ACTION_REBOOT }, - { '1', ACTION_RESCUE }, - { '2', ACTION_RUNLEVEL2 }, - { '3', ACTION_RUNLEVEL3 }, - { '4', ACTION_RUNLEVEL4 }, - { '5', ACTION_RUNLEVEL5 }, - { 's', ACTION_RESCUE }, - { 'S', ACTION_RESCUE }, - { 'q', ACTION_RELOAD }, - { 'Q', ACTION_RELOAD }, - { 'u', ACTION_REEXEC }, - { 'U', ACTION_REEXEC } - }; - - unsigned i; - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - telinit_help(); - return 0; - - case ARG_NO_WALL: - arg_no_wall = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind >= argc) { - log_error("%s: required argument missing.", program_invocation_short_name); - return -EINVAL; - } - - if (optind + 1 < argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - if (strlen(argv[optind]) != 1) { - log_error("Expected single character argument."); - return -EINVAL; - } - - for (i = 0; i < ELEMENTSOF(table); i++) - if (table[i].from == argv[optind][0]) - break; - - if (i >= ELEMENTSOF(table)) { - log_error("Unknown command '%s'.", argv[optind]); - return -EINVAL; - } - - arg_action = table[i].to; - - optind++; - - return 1; -} - -static int runlevel_parse_argv(int argc, char *argv[]) { - - enum { - ARG_HELP = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - runlevel_help(); - return 0; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind < argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - return 1; -} - -static int parse_argv(int argc, char *argv[]) { - assert(argc >= 0); - assert(argv); - - if (program_invocation_short_name) { - - if (strstr(program_invocation_short_name, "halt")) { - arg_action = ACTION_HALT; - return halt_parse_argv(argc, argv); - } else if (strstr(program_invocation_short_name, "poweroff")) { - arg_action = ACTION_POWEROFF; - return halt_parse_argv(argc, argv); - } else if (strstr(program_invocation_short_name, "reboot")) { - if (kexec_loaded()) - arg_action = ACTION_KEXEC; - else - arg_action = ACTION_REBOOT; - return halt_parse_argv(argc, argv); - } else if (strstr(program_invocation_short_name, "shutdown")) { - arg_action = ACTION_POWEROFF; - return shutdown_parse_argv(argc, argv); - } else if (strstr(program_invocation_short_name, "init")) { - - if (sd_booted() > 0) { - arg_action = _ACTION_INVALID; - return telinit_parse_argv(argc, argv); - } else { - /* Hmm, so some other init system is - * running, we need to forward this - * request to it. For now we simply - * guess that it is Upstart. */ - - execv(TELINIT, argv); - - log_error("Couldn't find an alternative telinit implementation to spawn."); - return -EIO; - } - - } else if (strstr(program_invocation_short_name, "runlevel")) { - arg_action = ACTION_RUNLEVEL; - return runlevel_parse_argv(argc, argv); - } - } - - arg_action = ACTION_SYSTEMCTL; - return systemctl_parse_argv(argc, argv); -} - -#ifdef HAVE_SYSV_COMPAT -_pure_ static int action_to_runlevel(void) { - - static const char table[_ACTION_MAX] = { - [ACTION_HALT] = '0', - [ACTION_POWEROFF] = '0', - [ACTION_REBOOT] = '6', - [ACTION_RUNLEVEL2] = '2', - [ACTION_RUNLEVEL3] = '3', - [ACTION_RUNLEVEL4] = '4', - [ACTION_RUNLEVEL5] = '5', - [ACTION_RESCUE] = '1' - }; - - assert(arg_action < _ACTION_MAX); - - return table[arg_action]; -} -#endif - -static int talk_initctl(void) { -#ifdef HAVE_SYSV_COMPAT - struct init_request request = { - .magic = INIT_MAGIC, - .sleeptime = 0, - .cmd = INIT_CMD_RUNLVL - }; - - _cleanup_close_ int fd = -1; - char rl; - int r; - - rl = action_to_runlevel(); - if (!rl) - return 0; - - request.runlevel = rl; - - fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); - } - - r = loop_write(fd, &request, sizeof(request), false); - if (r < 0) - return log_error_errno(r, "Failed to write to "INIT_FIFO": %m"); - - return 1; -#else - return 0; -#endif -} - -static int systemctl_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_NOCHROOT, list_units }, - { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files }, - { "list-sockets", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_sockets }, - { "list-timers", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_timers }, - { "list-jobs", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_jobs }, - { "list-machines", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_machines }, - { "clear-jobs", VERB_ANY, 1, VERB_NOCHROOT, trivial_method }, - { "cancel", VERB_ANY, VERB_ANY, VERB_NOCHROOT, cancel_job }, - { "start", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "stop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "condstop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ - { "reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "reload-or-try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatbility with old systemctl <= 228 */ - { "try-reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "force-reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with SysV */ - { "condreload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ - { "condrestart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with RH */ - { "isolate", 2, 2, VERB_NOCHROOT, start_unit }, - { "kill", 2, VERB_ANY, VERB_NOCHROOT, kill_unit }, - { "is-active", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, - { "check", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, - { "is-failed", 2, VERB_ANY, VERB_NOCHROOT, check_unit_failed }, - { "show", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "cat", 2, VERB_ANY, VERB_NOCHROOT, cat }, - { "status", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "help", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "daemon-reload", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, - { "daemon-reexec", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, - { "show-environment", VERB_ANY, 1, VERB_NOCHROOT, show_environment }, - { "set-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, - { "unset-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, - { "import-environment", VERB_ANY, VERB_ANY, VERB_NOCHROOT, import_environment }, - { "halt", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "poweroff", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "reboot", VERB_ANY, 2, VERB_NOCHROOT, start_system_special }, - { "kexec", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "suspend", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "hibernate", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "hybrid-sleep", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "default", VERB_ANY, 1, VERB_NOCHROOT, start_special }, - { "rescue", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "emergency", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "exit", VERB_ANY, 2, VERB_NOCHROOT, start_special }, - { "reset-failed", VERB_ANY, VERB_ANY, VERB_NOCHROOT, 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 }, - { "revert", 2, VERB_ANY, 0, enable_unit }, - { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root }, - { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies }, - { "set-default", 2, 2, 0, set_default }, - { "get-default", VERB_ANY, 1, 0, get_default }, - { "set-property", 3, VERB_ANY, VERB_NOCHROOT, 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, VERB_NOCHROOT, edit }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -static int reload_with_fallback(void) { - - /* First, try systemd via D-Bus. */ - if (daemon_reload(0, NULL, NULL) >= 0) - return 0; - - /* Nothing else worked, so let's try signals */ - assert(IN_SET(arg_action, ACTION_RELOAD, ACTION_REEXEC)); - - if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) - return log_error_errno(errno, "kill() failed: %m"); - - return 0; -} - -static int start_with_fallback(void) { - - /* 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) - return 0; - - log_error("Failed to talk to init daemon."); - return -EIO; -} - -static int halt_now(enum action a) { - int r; - - /* The kernel will automaticall flush ATA disks and suchlike - * on reboot(), but the file systems need to be synce'd - * explicitly in advance. */ - if (!arg_no_sync) - (void) sync(); - - /* Make sure C-A-D is handled by the kernel from this point - * on... */ - (void) reboot(RB_ENABLE_CAD); - - switch (a) { - - case ACTION_HALT: - log_info("Halting."); - (void) reboot(RB_HALT_SYSTEM); - return -errno; - - case ACTION_POWEROFF: - log_info("Powering off."); - (void) reboot(RB_POWER_OFF); - return -errno; - - case ACTION_KEXEC: - case ACTION_REBOOT: { - _cleanup_free_ char *param = NULL; - - r = read_one_line_file("/run/systemd/reboot-param", ¶m); - if (r < 0) - log_warning_errno(r, "Failed to read reboot parameter file: %m"); - - if (!isempty(param)) { - log_info("Rebooting with argument '%s'.", param); - (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); - log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); - } - - log_info("Rebooting."); - (void) reboot(RB_AUTOBOOT); - return -errno; - } - - default: - assert_not_reached("Unknown action."); - } -} - -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 = 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); - - (void) logind_set_wall_message(); - - 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_dry || arg_force > 0) { - log_error("Must be root."); - return -EPERM; - } - - /* 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 = logind_reboot(arg_action); - if (r >= 0) - return r; - if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) - /* 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_dry && !arg_force) - return start_with_fallback(); - - assert(geteuid() == 0); - - if (!arg_no_wtmp) { - if (sd_booted() > 0) - log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); - else { - r = utmp_put_shutdown(); - if (r < 0) - log_warning_errno(r, "Failed to write utmp record: %m"); - } - } - - if (arg_dry) - return 0; - - r = halt_now(arg_action); - return log_error_errno(r, "Failed to reboot: %m"); -} - -static int runlevel_main(void) { - int r, runlevel, previous; - - r = utmp_get_runlevel(&runlevel, &previous); - if (r < 0) { - puts("unknown"); - return r; - } - - printf("%c %c\n", - previous <= 0 ? 'N' : previous, - runlevel <= 0 ? 'N' : runlevel); - - 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[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - sigbus_install(); - - /* Explicitly not on_tty() to avoid setting cached value. - * This becomes relevant for piping output which might be - * ellipsized. */ - original_stdout_is_tty = isatty(STDOUT_FILENO); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); - r = 0; - goto finish; - } - - /* 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(argc, argv); - break; - - case ACTION_HALT: - case ACTION_POWEROFF: - case ACTION_REBOOT: - case ACTION_KEXEC: - r = halt_main(); - break; - - case ACTION_RUNLEVEL2: - case ACTION_RUNLEVEL3: - case ACTION_RUNLEVEL4: - case ACTION_RUNLEVEL5: - case ACTION_RESCUE: - case ACTION_EMERGENCY: - case ACTION_DEFAULT: - r = start_with_fallback(); - break; - - case ACTION_RELOAD: - case ACTION_REEXEC: - r = reload_with_fallback(); - break; - - 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"); - } - -finish: - release_busses(); - - pager_close(); - ask_password_agent_close(); - polkit_agent_close(); - - strv_free(arg_types); - strv_free(arg_states); - strv_free(arg_properties); - - strv_free(arg_wall); - free(arg_root); - - /* 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/systemctl/systemd-sysv-install.SKELETON b/src/systemctl/systemd-sysv-install.SKELETON deleted file mode 100755 index a53a3e6221..0000000000 --- a/src/systemctl/systemd-sysv-install.SKELETON +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -# This script is called by "systemctl enable/disable" when the given unit is a -# SysV init.d script. It needs to call the distribution's mechanism for -# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This -# can optionally take a --root argument for enabling a SysV init script -# in a chroot or similar. -set -e - -usage() { - echo "Usage: $0 [--root=path] enable|disable|is-enabled " >&2 - exit 1 -} - -# parse options -eval set -- "$(getopt -o r: --long root: -- "$@")" -while true; do - case "$1" in - -r|--root) - ROOT="$2" - shift 2 ;; - --) shift ; break ;; - *) usage ;; - esac -done - -NAME="$2" -[ -n "$NAME" ] || usage - -case "$1" in - enable) - # call the command to enable SysV init script $NAME here - # (consider optional $ROOT) - echo "IMPLEMENT ME: enabling SysV init.d script $NAME" - ;; - disable) - # call the command to disable SysV init script $NAME here - # (consider optional $ROOT) - echo "IMPLEMENT ME: disabling SysV init.d script $NAME" - ;; - is-enabled) - # exit with 0 if $NAME is enabled, non-zero if it is disabled - # (consider optional $ROOT) - echo "IMPLEMENT ME: checking SysV init.d script $NAME" - ;; - *) - usage ;; -esac diff --git a/src/systemd-ask-password/Makefile b/src/systemd-ask-password/Makefile new file mode 100644 index 0000000000..9b23b41513 --- /dev/null +++ b/src/systemd-ask-password/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemd-ask-password +systemd_ask_password_SOURCES = \ + src/ask-password/ask-password.c + +systemd_ask_password_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-ask-password/ask-password.c b/src/systemd-ask-password/ask-password.c new file mode 100644 index 0000000000..a30833b6b3 --- /dev/null +++ b/src/systemd-ask-password/ask-password.c @@ -0,0 +1,188 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/def.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/strv.h" +#include "shared/ask-password-api.h" + +static const char *arg_icon = NULL; +static const char *arg_id = NULL; +static const char *arg_keyname = NULL; +static char *arg_message = NULL; +static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; +static bool arg_multiple = false; +static bool arg_no_output = 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" + " --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" + " --no-output Do not print password to standard output\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_ICON = 0x100, + ARG_TIMEOUT, + ARG_ECHO, + ARG_NO_TTY, + ARG_ACCEPT_CACHED, + ARG_MULTIPLE, + ARG_ID, + ARG_KEYNAME, + ARG_NO_OUTPUT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "icon", required_argument, NULL, ARG_ICON }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "echo", no_argument, NULL, ARG_ECHO }, + { "no-tty", no_argument, NULL, ARG_NO_TTY }, + { "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 }, + { "no-output", no_argument, NULL, ARG_NO_OUTPUT }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_ICON: + arg_icon = optarg; + break; + + case ARG_TIMEOUT: + if (parse_sec(optarg, &arg_timeout) < 0) { + log_error("Failed to parse --timeout parameter %s", optarg); + return -EINVAL; + } + break; + + case ARG_ECHO: + arg_flags |= ASK_PASSWORD_ECHO; + break; + + case ARG_NO_TTY: + arg_flags |= ASK_PASSWORD_NO_TTY; + break; + + case ARG_ACCEPT_CACHED: + arg_flags |= ASK_PASSWORD_ACCEPT_CACHED; + break; + + case ARG_MULTIPLE: + arg_multiple = true; + break; + + case ARG_ID: + arg_id = optarg; + break; + + case ARG_KEYNAME: + arg_keyname = optarg; + break; + + case ARG_NO_OUTPUT: + arg_no_output = true; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc > optind) { + arg_message = strv_join(argv + optind, " "); + if (!arg_message) + return log_oom(); + } + + return 1; +} + +int main(int argc, char *argv[]) { + _cleanup_strv_free_erase_ char **l = NULL; + usec_t timeout; + char **p; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_timeout > 0) + timeout = now(CLOCK_MONOTONIC) + arg_timeout; + else + timeout = 0; + + 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; + } + + STRV_FOREACH(p, l) { + if (!arg_no_output) + puts(*p); + + if (!arg_multiple) + break; + } + +finish: + free(arg_message); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-ask-password/systemd-ask-password.completion.zsh b/src/systemd-ask-password/systemd-ask-password.completion.zsh new file mode 100644 index 0000000000..fa68159256 --- /dev/null +++ b/src/systemd-ask-password/systemd-ask-password.completion.zsh @@ -0,0 +1,12 @@ +#compdef systemd-ask-password + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--icon=[Icon name]:icon name:' \ + '--timeout=[Timeout in sec]:timeout (seconds):' \ + '--no-tty[Ask question via agent even on TTY]' \ + '--accept-cached[Accept cached passwords]' \ + '--multiple[List multiple passwords if available]' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/systemd-ask-password/systemd-ask-password.xml b/src/systemd-ask-password/systemd-ask-password.xml new file mode 100644 index 0000000000..2b6fb5a82f --- /dev/null +++ b/src/systemd-ask-password/systemd-ask-password.xml @@ -0,0 +1,227 @@ + + + + + + + + + systemd-ask-password + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-ask-password + 1 + + + + systemd-ask-password + Query the user for a system password + + + + + systemd-ask-password OPTIONS MESSAGE + + + + + Description + + systemd-ask-password may be used to query + a system password or passphrase from the user, using a question + message specified on the command line. When run from a TTY it will + query a password on the TTY and print it to standard output. When + run with no TTY or with it will query + the password system-wide and allow active users to respond via + several agents. The latter is only available to privileged + processes. + + The purpose of this tool is to query system-wide passwords + — that is passwords not attached to a specific user account. + Examples include: unlocking encrypted hard disks when they are + plugged in or at boot, entering an SSL certificate passphrase for + web and VPN servers. + + Existing agents are: + + + A boot-time password agent asking the user for + passwords using Plymouth + + A boot-time password agent querying the user + directly on the console + + An agent requesting password input via a + wall1 + message + + A command line agent which can be started + temporarily to process queued password + requests + + A TTY agent that is temporarily spawned during + systemctl1 + invocations + + + Additional password agents may be implemented according to + the systemd + Password Agent Specification. + + If a password is queried on a TTY, the user may press TAB to + hide the asterisks normally shown for each character typed. + Pressing Backspace as first key achieves the same effect. + + + + + Options + + The following options are understood: + + + + + + Specify an icon name alongside the password + query, which may be used in all agents supporting graphical + display. The icon name should follow the XDG + Icon Naming Specification. + + + + + Specify an identifier for this password + query. This identifier is freely choosable and allows + recognition of queries by involved agents. It should include + the subsystem doing the query and the specific object the + query is done for. Example: + --id=cryptsetup:/dev/sda5. + + + + + Configure a kernel keyring key name to use as + cache for the password. If set, then the tool will try to push + any collected passwords into the kernel keyring of the root + user, as a key of the specified name. If combined with + , it will also try to retrieve + such cached passwords from the key in the kernel keyring + instead of querying the user right away. By using this option, + the kernel keyring may be used as effective cache to avoid + repeatedly asking users for passwords, if there are multiple + objects that may be unlocked with the same password. The + cached key will have a timeout of 2.5min set, after which it + will be purged from the kernel keyring. Note that it is + possible to cache multiple passwords under the same keyname, + in which case they will be stored as NUL-separated list of + passwords. Use + keyctl1 + to access the cached key via the kernel keyring + directly. Example: --keyname=cryptsetup + + + + + + Specify the query timeout in seconds. Defaults + to 90s. A timeout of 0 waits indefinitely. + + + + + + Echo the user input instead of masking it. + This is useful when using + systemd-ask-password to query for + usernames. + + + + + + Never ask for password on current TTY even if + one is available. Always use agent system. + + + + + + If passed, accept cached passwords, i.e. + passwords previously entered. + + + + + + When used in conjunction with + accept multiple passwords. + This will output one password per line. + + + + + + Do not print passwords to standard output. + This is useful if you want to store a password in kernel + keyring with but do not want it + to show up on screen or in logs. + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemctl1, + keyctl1, + plymouth8, + wall1 + + + + diff --git a/src/systemd-cgls/Makefile b/src/systemd-cgls/Makefile new file mode 100644 index 0000000000..1ef82d8aef --- /dev/null +++ b/src/systemd-cgls/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-cgls +systemd_cgls_SOURCES = \ + src/cgls/cgls.c + +systemd_cgls_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-cgls/cgls.c b/src/systemd-cgls/cgls.c new file mode 100644 index 0000000000..0d2459133d --- /dev/null +++ b/src/systemd-cgls/cgls.c @@ -0,0 +1,288 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/cgroup-show.h" +#include "shared/output-mode.h" +#include "shared/pager.h" + +static bool arg_no_pager = false; +static bool arg_kernel_threads = false; +static bool arg_all = false; +static int arg_full = -1; +static char* arg_machine = NULL; + +static void help(void) { + printf("%s [OPTIONS...] [CGROUP...]\n\n" + "Recursively show control group contents.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -a --all Show all groups, including empty\n" + " -l --full Do not ellipsize output\n" + " -k Include kernel threads in output\n" + " -M --machine= Show container\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_NO_PAGER = 0x100, + ARG_VERSION, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, 'l' }, + { "machine", required_argument, NULL, 'M' }, + {} + }; + + int c; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "hkalM:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'a': + arg_all = true; + break; + + case 'l': + arg_full = true; + break; + + case 'k': + arg_kernel_threads = true; + break; + + case 'M': + arg_machine = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + return 1; +} + +static int get_cgroup_root(char **ret) { + _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 == -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; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + 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"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); + + return 0; +} + +static void show_cg_info(const char *controller, const char *path) { + + if (cg_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + printf("Controller %s; ", controller); + + printf("Control group %s:\n", isempty(path) ? "/" : path); + fflush(stdout); +} + +int main(int argc, char *argv[]) { + int r, output_flags; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (!arg_no_pager) { + r = pager_open(arg_no_pager, false); + if (r > 0 && arg_full < 0) + arg_full = true; + } + + output_flags = + arg_all * OUTPUT_SHOW_ALL | + (arg_full > 0) * OUTPUT_FULL_WIDTH | + arg_kernel_threads * OUTPUT_KERNEL_THREADS; + + if (optind < argc) { + _cleanup_free_ char *root = NULL; + int i; + + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + + for (i = optind; i < argc; i++) { + int q; + + if (path_startswith(argv[i], "/sys/fs/cgroup")) { + + printf("Directory %s:\n", argv[i]); + fflush(stdout); + + q = show_cgroup_by_path(argv[i], NULL, 0, output_flags); + } else { + _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; + const char *controller, *path; + + r = cg_split_spec(argv[i], &c, &p); + if (r < 0) { + log_error_errno(r, "Failed to split argument %s: %m", argv[i]); + goto finish; + } + + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { + j = strjoin(root, "/", p, NULL); + if (!j) { + r = log_oom(); + goto finish; + } + + path_kill_slashes(j); + path = j; + } else + path = root; + + show_cg_info(controller, path); + + q = show_cgroup(controller, path, NULL, 0, output_flags); + } + + if (q < 0) + r = q; + } + + } else { + bool done = false; + + if (!arg_machine) { + _cleanup_free_ char *cwd = NULL; + + cwd = get_current_dir_name(); + if (!cwd) { + r = log_error_errno(errno, "Cannot determine current working directory: %m"); + goto finish; + } + + if (path_startswith(cwd, "/sys/fs/cgroup")) { + printf("Working directory %s:\n", cwd); + fflush(stdout); + + r = show_cgroup_by_path(cwd, NULL, 0, output_flags); + done = true; + } + } + + if (!done) { + _cleanup_free_ char *root = NULL; + + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + + show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); + + printf("-.slice\n"); + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); + } + } + + if (r < 0) + log_error_errno(r, "Failed to list cgroup tree: %m"); + +finish: + pager_close(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-cgls/systemd-cgls.completion.bash b/src/systemd-cgls/systemd-cgls.completion.bash new file mode 100644 index 0000000000..0570438660 --- /dev/null +++ b/src/systemd-cgls/systemd-cgls.completion.bash @@ -0,0 +1,56 @@ +# systemd-cgls(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +_systemd_cgls() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version --all -l --full -k --no-pager' + [ARG]='-M --machine' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --machine|-M) + comps=$( __get_machines ) + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_cgls systemd-cgls diff --git a/src/systemd-cgls/systemd-cgls.completion.zsh b/src/systemd-cgls/systemd-cgls.completion.zsh new file mode 100644 index 0000000000..c8f93fa732 --- /dev/null +++ b/src/systemd-cgls/systemd-cgls.completion.zsh @@ -0,0 +1,12 @@ +#compdef systemd-cgls + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' \ + '--no-pager[Do not pipe output into a pager]' \ + {-a,--all}'[Show all groups, including empty]' \ + '-k[Include kernel threads in output]' \ + ':cgroups:(cpuset cpu cpuacct memory devices freezer blkio)' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/systemd-cgls/systemd-cgls.xml b/src/systemd-cgls/systemd-cgls.xml new file mode 100644 index 0000000000..e8f0368f48 --- /dev/null +++ b/src/systemd-cgls/systemd-cgls.xml @@ -0,0 +1,139 @@ + + + + + + + + + systemd-cgls + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cgls + 1 + + + + systemd-cgls + Recursively show control group contents + + + + + systemd-cgls + OPTIONS + CGROUP + + + + + Description + + systemd-cgls recursively shows the + contents of the selected Linux control group hierarchy in a tree. + If arguments are specified, shows all member processes of the + specified control groups plus all their subgroups and their + members. The control groups may either be specified by their full + file paths or are assumed in the systemd control group hierarchy. + If no argument is specified and the current working directory is + beneath the control group mount point + /sys/fs/cgroup, shows the contents of the + control group the working directory refers to. Otherwise, the full + systemd control group hierarchy is shown. + + By default, empty control groups are not shown. + + + + Options + + The following options are understood: + + + + + + Do not hide empty control groups in the + output. + + + + + + + Do not ellipsize process tree members. + + + + + + + Include kernel threads in output. + + + + + + + + Limit control groups shown to the part + corresponding to the container + MACHINE. + + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemctl1, + systemd-cgtop1, + systemd-nspawn1, + ps1 + + + + diff --git a/src/systemd-cgroups-agent/Makefile b/src/systemd-cgroups-agent/Makefile new file mode 100644 index 0000000000..ae40bb0163 --- /dev/null +++ b/src/systemd-cgroups-agent/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-cgroups-agent +systemd_cgroups_agent_SOURCES = \ + src/cgroups-agent/cgroups-agent.c + +systemd_cgroups_agent_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-cgroups-agent/cgroups-agent.c b/src/systemd-cgroups-agent/cgroups-agent.c new file mode 100644 index 0000000000..a09333d82c --- /dev/null +++ b/src/systemd-cgroups-agent/cgroups-agent.c @@ -0,0 +1,67 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/socket-util.h" + +int main(int argc, char *argv[]) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + + _cleanup_close_ int fd = -1; + ssize_t n; + size_t l; + + if (argc != 2) { + log_error("Incorrect number of arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) { + log_debug_errno(errno, "Failed to allocate socket: %m"); + return EXIT_FAILURE; + } + + l = strlen(argv[1]); + + n = sendto(fd, argv[1], l, 0, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (n < 0) { + log_debug_errno(errno, "Failed to send cgroups agent message: %m"); + return EXIT_FAILURE; + } + + if ((size_t) n != l) { + log_debug("Datagram size mismatch"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/systemd-cgtop/Makefile b/src/systemd-cgtop/Makefile new file mode 100644 index 0000000000..abebe7f3d0 --- /dev/null +++ b/src/systemd-cgtop/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-cgtop +systemd_cgtop_SOURCES = \ + src/cgtop/cgtop.c + +systemd_cgtop_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-cgtop/cgtop.c b/src/systemd-cgtop/cgtop.c new file mode 100644 index 0000000000..f23ebf4f57 --- /dev/null +++ b/src/systemd-cgtop/cgtop.c @@ -0,0 +1,1133 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" + +typedef struct Group { + char *path; + + bool n_tasks_valid:1; + bool cpu_valid:1; + bool memory_valid:1; + bool io_valid:1; + + uint64_t n_tasks; + + unsigned cpu_iteration; + nsec_t cpu_usage; + nsec_t cpu_timestamp; + double cpu_fraction; + + uint64_t memory; + + unsigned io_iteration; + uint64_t io_input, io_output; + nsec_t io_timestamp; + uint64_t io_input_bps, io_output_bps; +} Group; + +static unsigned arg_depth = 3; +static unsigned arg_iterations = (unsigned) -1; +static bool arg_batch = false; +static bool arg_raw = false; +static usec_t arg_delay = 1*USEC_PER_SEC; +static char* arg_machine = NULL; +static char* arg_root = NULL; +static bool arg_recursive = true; + +static enum { + COUNT_PIDS, + COUNT_USERSPACE_PROCESSES, + COUNT_ALL_PROCESSES, +} arg_count = COUNT_PIDS; + +static enum { + ORDER_PATH, + ORDER_TASKS, + ORDER_CPU, + ORDER_MEMORY, + ORDER_IO, +} arg_order = ORDER_CPU; + +static enum { + CPU_PERCENT, + CPU_TIME, +} arg_cpu_type = CPU_PERCENT; + +static void group_free(Group *g) { + assert(g); + + free(g->path); + free(g); +} + +static void group_hashmap_clear(Hashmap *h) { + Group *g; + + while ((g = hashmap_steal_first(h))) + group_free(g); +} + +static void group_hashmap_free(Hashmap *h) { + group_hashmap_clear(h); + hashmap_free(h); +} + +static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) { + if (!is_valid) + return "-"; + if (arg_raw) { + snprintf(buf, l, "%jd", t); + return buf; + } + return format_bytes(buf, l, t); +} + +static int process( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + Group **ret) { + + Group *g; + int r; + + assert(controller); + assert(path); + assert(a); + + g = hashmap_get(a, path); + if (!g) { + g = hashmap_get(b, path); + if (!g) { + g = new0(Group, 1); + if (!g) + return -ENOMEM; + + g->path = strdup(path); + if (!g->path) { + group_free(g); + return -ENOMEM; + } + + r = hashmap_put(a, g->path, g); + if (r < 0) { + group_free(g); + return r; + } + } else { + r = hashmap_move_one(a, b, path); + if (r < 0) + return r; + + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; + } + } + + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; + + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + g->n_tasks = 0; + while (cg_read_pid(f, &pid) > 0) { + + if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) + continue; + + g->n_tasks++; + } + + if (g->n_tasks > 0) + g->n_tasks_valid = true; + + } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { + _cleanup_free_ char *p = NULL, *v = NULL; + + r = cg_get_path(controller, path, "pids.current", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &g->n_tasks); + if (r < 0) + return r; + + if (g->n_tasks > 0) + g->n_tasks_valid = true; + + } else if (streq(controller, "cpuacct") && cg_unified() <= 0) { + _cleanup_free_ char *p = NULL, *v = NULL; + uint64_t new_usage; + nsec_t timestamp; + + r = cg_get_path(controller, path, "cpuacct.usage", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &new_usage); + if (r < 0) + return r; + + timestamp = now_nsec(CLOCK_MONOTONIC); + + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { + + nsec_t x, y; + + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; + + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; + } + + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; + g->cpu_iteration = iteration; + + } else if (streq(controller, "memory")) { + _cleanup_free_ char *p = NULL, *v = NULL; + + if (cg_unified() <= 0) + r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); + else + r = cg_get_path(controller, path, "memory.current", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &g->memory); + if (r < 0) + return r; + + if (g->memory > 0) + g->memory_valid = true; + + } else if ((streq(controller, "io") && cg_unified() > 0) || + (streq(controller, "blkio") && cg_unified() <= 0)) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + bool unified = cg_unified() > 0; + uint64_t wr = 0, rd = 0; + nsec_t timestamp; + + r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); + if (r < 0) + return r; + + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + return -errno; + } + + for (;;) { + char line[LINE_MAX], *l; + uint64_t k, *q; + + if (!fgets(line, sizeof(line), f)) + break; + + /* Trim and skip the device */ + l = strstrip(line); + l += strcspn(l, WHITESPACE); + l += strspn(l, WHITESPACE); + + if (unified) { + while (!isempty(l)) { + if (sscanf(l, "rbytes=%" SCNu64, &k)) + rd += k; + else if (sscanf(l, "wbytes=%" SCNu64, &k)) + wr += k; + + l += strcspn(l, WHITESPACE); + l += strspn(l, WHITESPACE); + } + } else { + if (first_word(l, "Read")) { + l += 4; + q = &rd; + } else if (first_word(l, "Write")) { + l += 5; + q = ≀ + } else + continue; + + l += strspn(l, WHITESPACE); + r = safe_atou64(l, &k); + if (r < 0) + continue; + + *q += k; + } + } + + timestamp = now_nsec(CLOCK_MONOTONIC); + + if (g->io_iteration == iteration - 1) { + uint64_t x, yr, yw; + + x = (uint64_t) (timestamp - g->io_timestamp); + if (x < 1) + x = 1; + + if (rd > g->io_input) + yr = rd - g->io_input; + else + yr = 0; + + if (wr > g->io_output) + yw = wr - g->io_output; + else + yw = 0; + + if (yr > 0 || yw > 0) { + g->io_input_bps = (yr * 1000000000ULL) / x; + g->io_output_bps = (yw * 1000000000ULL) / x; + g->io_valid = true; + } + } + + g->io_input = rd; + g->io_output = wr; + g->io_timestamp = timestamp; + g->io_iteration = iteration; + } + + if (ret) + *ret = g; + + return 0; +} + +static int refresh_one( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + unsigned depth, + Group **ret) { + + _cleanup_closedir_ DIR *d = NULL; + Group *ours = NULL; + int r; + + assert(controller); + assert(path); + assert(a); + + if (depth > arg_depth) + return 0; + + r = process(controller, path, a, b, iteration, &ours); + if (r < 0) + return r; + + r = cg_enumerate_subgroups(controller, path, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *fn = NULL, *p = NULL; + Group *child = NULL; + + r = cg_read_subgroup(d, &fn); + if (r < 0) + return r; + if (r == 0) + break; + + p = strjoin(path, "/", fn, NULL); + if (!p) + return -ENOMEM; + + path_kill_slashes(p); + + r = refresh_one(controller, p, a, b, iteration, depth + 1, &child); + if (r < 0) + return r; + + if (arg_recursive && + IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) && + child && + child->n_tasks_valid && + streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + + /* Recursively sum up processes */ + + if (ours->n_tasks_valid) + ours->n_tasks += child->n_tasks; + else { + ours->n_tasks = child->n_tasks; + ours->n_tasks_valid = true; + } + } + } + + if (ret) + *ret = ours; + + return 1; +} + +static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) { + int r; + + assert(a); + + r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + r = refresh_one("memory", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + r = refresh_one("io", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + r = refresh_one("blkio", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + r = refresh_one("pids", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +static int group_compare(const void*a, const void *b) { + const Group *x = *(Group**)a, *y = *(Group**)b; + + if (arg_order != ORDER_TASKS || arg_recursive) { + /* Let's make sure that the parent is always before + * the child. Except when ordering by tasks and + * recursive summing is off, since that is actually + * not accumulative for all children. */ + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + } + + switch (arg_order) { + + case ORDER_PATH: + break; + + case ORDER_CPU: + if (arg_cpu_type == CPU_PERCENT) { + if (x->cpu_valid && y->cpu_valid) { + if (x->cpu_fraction > y->cpu_fraction) + return -1; + else if (x->cpu_fraction < y->cpu_fraction) + return 1; + } else if (x->cpu_valid) + return -1; + else if (y->cpu_valid) + return 1; + } else { + if (x->cpu_usage > y->cpu_usage) + return -1; + else if (x->cpu_usage < y->cpu_usage) + return 1; + } + + break; + + case ORDER_TASKS: + if (x->n_tasks_valid && y->n_tasks_valid) { + if (x->n_tasks > y->n_tasks) + return -1; + else if (x->n_tasks < y->n_tasks) + return 1; + } else if (x->n_tasks_valid) + return -1; + else if (y->n_tasks_valid) + return 1; + + break; + + case ORDER_MEMORY: + if (x->memory_valid && y->memory_valid) { + if (x->memory > y->memory) + return -1; + else if (x->memory < y->memory) + return 1; + } else if (x->memory_valid) + return -1; + else if (y->memory_valid) + return 1; + + break; + + case ORDER_IO: + if (x->io_valid && y->io_valid) { + if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) + return -1; + else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps) + return 1; + } else if (x->io_valid) + return -1; + else if (y->io_valid) + return 1; + } + + return path_compare(x->path, y->path); +} + +static void display(Hashmap *a) { + Iterator i; + Group *g; + Group **array; + signed path_columns; + unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 3; /* 3 for ellipsize() to work properly */ + char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)]; + + assert(a); + + if (!terminal_is_dumb()) + fputs(ANSI_HOME_CLEAR, stdout); + + array = alloca(sizeof(Group*) * hashmap_size(a)); + + HASHMAP_FOREACH(g, a, i) + if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid) + array[n++] = g; + + qsort_safe(array, n, sizeof(Group*), group_compare); + + /* Find the longest names in one run */ + for (j = 0; j < n; j++) { + unsigned cputlen, pathtlen; + + format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); + cputlen = strlen(buffer); + maxtcpu = MAX(maxtcpu, cputlen); + + pathtlen = strlen(array[j]->path); + maxtpath = MAX(maxtpath, pathtlen); + } + + if (arg_cpu_type == CPU_PERCENT) + xsprintf(buffer, "%6s", "%CPU"); + else + xsprintf(buffer, "%*s", maxtcpu, "CPU Time"); + + rows = lines(); + if (rows <= 10) + rows = 10; + + if (on_tty()) { + const char *on, *off; + + path_columns = columns() - 36 - strlen(buffer); + if (path_columns < 10) + path_columns = 10; + + on = ansi_highlight_underline(); + off = ansi_underline(); + + printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n", + ansi_underline(), + arg_order == ORDER_PATH ? on : "", path_columns, "Control Group", + arg_order == ORDER_PATH ? off : "", + arg_order == ORDER_TASKS ? on : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+", + arg_order == ORDER_TASKS ? off : "", + arg_order == ORDER_CPU ? on : "", buffer, + arg_order == ORDER_CPU ? off : "", + arg_order == ORDER_MEMORY ? on : "", "Memory", + arg_order == ORDER_MEMORY ? off : "", + arg_order == ORDER_IO ? on : "", "Input/s", + arg_order == ORDER_IO ? off : "", + arg_order == ORDER_IO ? on : "", "Output/s", + arg_order == ORDER_IO ? off : "", + ansi_normal()); + } else + path_columns = maxtpath; + + for (j = 0; j < n; j++) { + _cleanup_free_ char *ellipsized = NULL; + const char *path; + + if (on_tty() && j + 6 > rows) + break; + + g = array[j]; + + path = isempty(g->path) ? "/" : g->path; + ellipsized = ellipsize(path, path_columns, 33); + printf("%-*s", path_columns, ellipsized ?: path); + + if (g->n_tasks_valid) + printf(" %7" PRIu64, g->n_tasks); + else + fputs(" -", stdout); + + if (arg_cpu_type == CPU_PERCENT) { + if (g->cpu_valid) + printf(" %6.1f", g->cpu_fraction*100); + else + fputs(" -", stdout); + } else + printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); + + printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); + printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); + printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_output_bps)); + + putchar('\n'); + } +} + +static void help(void) { + printf("%s [OPTIONS...] [CGROUP]\n\n" + "Show top control groups by their resource usage.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -p --order=path Order by path\n" + " -t --order=tasks Order by number of tasks/processes\n" + " -c --order=cpu Order by CPU load (default)\n" + " -m --order=memory Order by memory load\n" + " -i --order=io Order by IO load\n" + " -r --raw Provide raw (not human-readable) numbers\n" + " --cpu=percentage Show CPU usage as percentage (default)\n" + " --cpu=time Show CPU usage as time\n" + " -P Count userspace processes instead of tasks (excl. kernel)\n" + " -k Count all processes instead of tasks (incl. kernel)\n" + " --recursive=BOOL Sum up process count recursively\n" + " -d --delay=DELAY Delay between updates\n" + " -n --iterations=N Run for N iterations before exiting\n" + " -b --batch Run in batch mode, accepting no input\n" + " --depth=DEPTH Maximum traversal depth (default: %u)\n" + " -M --machine= Show container\n" + , program_invocation_short_name, arg_depth); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_DEPTH, + ARG_CPU_TYPE, + ARG_ORDER, + ARG_RECURSIVE, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "delay", required_argument, NULL, 'd' }, + { "iterations", required_argument, NULL, 'n' }, + { "batch", no_argument, NULL, 'b' }, + { "raw", no_argument, NULL, 'r' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, + { "order", required_argument, NULL, ARG_ORDER }, + { "recursive", required_argument, NULL, ARG_RECURSIVE }, + { "machine", required_argument, NULL, 'M' }, + {} + }; + + bool recursive_unset = false; + int c, r; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "hptcmin:brd:kPM:", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_CPU_TYPE: + if (optarg) { + if (streq(optarg, "time")) + arg_cpu_type = CPU_TIME; + else if (streq(optarg, "percentage")) + arg_cpu_type = CPU_PERCENT; + else { + log_error("Unknown argument to --cpu=: %s", optarg); + return -EINVAL; + } + } else + arg_cpu_type = CPU_TIME; + + break; + + case ARG_DEPTH: + r = safe_atou(optarg, &arg_depth); + if (r < 0) { + log_error("Failed to parse depth parameter."); + return -EINVAL; + } + + break; + + case 'd': + r = parse_sec(optarg, &arg_delay); + if (r < 0 || arg_delay <= 0) { + log_error("Failed to parse delay parameter."); + return -EINVAL; + } + + break; + + case 'n': + r = safe_atou(optarg, &arg_iterations); + if (r < 0) { + log_error("Failed to parse iterations parameter."); + return -EINVAL; + } + + break; + + case 'b': + arg_batch = true; + break; + + case 'r': + arg_raw = true; + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case ARG_ORDER: + if (streq(optarg, "path")) + arg_order = ORDER_PATH; + else if (streq(optarg, "tasks")) + arg_order = ORDER_TASKS; + else if (streq(optarg, "cpu")) + arg_order = ORDER_CPU; + else if (streq(optarg, "memory")) + arg_order = ORDER_MEMORY; + else if (streq(optarg, "io")) + arg_order = ORDER_IO; + else { + log_error("Invalid argument to --order=: %s", optarg); + return -EINVAL; + } + break; + + case 'k': + arg_count = COUNT_ALL_PROCESSES; + break; + + case 'P': + arg_count = COUNT_USERSPACE_PROCESSES; + break; + + case ARG_RECURSIVE: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --recursive= argument: %s", optarg); + return r; + } + + arg_recursive = r; + recursive_unset = r == 0; + break; + + case 'M': + arg_machine = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind == argc-1) { + if (arg_machine) { + log_error("Specifying a control group path together with the -M option is not allowed"); + return -EINVAL; + } + arg_root = argv[optind]; + } else if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + if (recursive_unset && arg_count == COUNT_PIDS) { + log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); + return -EINVAL; + } + + return 1; +} + +static const char* counting_what(void) { + if (arg_count == COUNT_PIDS) + return "tasks"; + else if (arg_count == COUNT_ALL_PROCESSES) + return "all processes (incl. kernel)"; + else + return "userspace processes (excl. kernel)"; +} + +static int get_cgroup_root(char **ret) { + _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_root) { + char *aux; + + aux = strdup(arg_root); + if (!aux) + return log_oom(); + + *ret = aux; + return 0; + } + + if (!arg_machine) { + r = cg_get_root_path(ret); + if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + + return 0; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + 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"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); + + return 0; +} + +int main(int argc, char *argv[]) { + int r; + Hashmap *a = NULL, *b = NULL; + unsigned iteration = 0; + usec_t last_refresh = 0; + bool quit = false, immediate_refresh = false; + _cleanup_free_ char *root = NULL; + CGroupMask mask; + + log_parse_environment(); + log_open(); + + r = cg_mask_supported(&mask); + if (r < 0) { + log_error_errno(r, "Failed to determine supported controllers: %m"); + goto finish; + } + + arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = get_cgroup_root(&root); + if (r < 0) { + log_error_errno(r, "Failed to get root control group path: %m"); + goto finish; + } + + a = hashmap_new(&string_hash_ops); + b = hashmap_new(&string_hash_ops); + if (!a || !b) { + r = log_oom(); + goto finish; + } + + signal(SIGWINCH, columns_lines_cache_reset); + + if (arg_iterations == (unsigned) -1) + arg_iterations = on_tty() ? 0 : 1; + + while (!quit) { + Hashmap *c; + usec_t t; + char key; + char h[FORMAT_TIMESPAN_MAX]; + + t = now(CLOCK_MONOTONIC); + + if (t >= last_refresh + arg_delay || immediate_refresh) { + + r = refresh(root, a, b, iteration++); + if (r < 0) { + log_error_errno(r, "Failed to refresh: %m"); + goto finish; + } + + group_hashmap_clear(b); + + c = a; + a = b; + b = c; + + last_refresh = t; + immediate_refresh = false; + } + + display(b); + + if (arg_iterations && iteration >= arg_iterations) + break; + + if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */ + fputs("\n", stdout); + fflush(stdout); + + if (arg_batch) + (void) usleep(last_refresh + arg_delay - t); + else { + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); + if (r == -ETIMEDOUT) + continue; + if (r < 0) { + log_error_errno(r, "Couldn't read key: %m"); + goto finish; + } + } + + if (on_tty()) { /* TTY: Clear any user keystroke */ + fputs("\r \r", stdout); + fflush(stdout); + } + + if (arg_batch) + continue; + + switch (key) { + + case ' ': + immediate_refresh = true; + break; + + case 'q': + quit = true; + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case '%': + arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; + break; + + case 'k': + arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); + fflush(stdout); + sleep(1); + break; + + case 'P': + arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); + fflush(stdout); + sleep(1); + break; + + case 'r': + if (arg_count == COUNT_PIDS) + fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode."); + else { + arg_recursive = !arg_recursive; + fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive)); + } + fflush(stdout); + sleep(1); + break; + + case '+': + if (arg_delay < USEC_PER_SEC) + arg_delay += USEC_PER_MSEC*250; + else + arg_delay += USEC_PER_SEC; + + fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); + fflush(stdout); + sleep(1); + break; + + case '-': + if (arg_delay <= USEC_PER_MSEC*500) + arg_delay = USEC_PER_MSEC*250; + else if (arg_delay < USEC_PER_MSEC*1250) + arg_delay -= USEC_PER_MSEC*250; + else + arg_delay -= USEC_PER_SEC; + + fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); + fflush(stdout); + sleep(1); + break; + + case '?': + case 'h': + +#define ON ANSI_HIGHLIGHT +#define OFF ANSI_NORMAL + + fprintf(stdout, + "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" + "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" + "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n" + "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit"); + fflush(stdout); + sleep(3); + break; + + default: + if (key < ' ') + fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); + else + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + fflush(stdout); + sleep(1); + break; + } + } + + r = 0; + +finish: + group_hashmap_free(a); + group_hashmap_free(b); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-cgtop/systemd-cgtop.completion.bash b/src/systemd-cgtop/systemd-cgtop.completion.bash new file mode 100644 index 0000000000..f1ed22fd55 --- /dev/null +++ b/src/systemd-cgtop/systemd-cgtop.completion.bash @@ -0,0 +1,62 @@ +# systemd-cgtop(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +_systemd_cgtop() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local comps + + local -A OPTS=( + [STANDALONE]='-h --help --version -p -t -c -m -i -b --batch -r --raw -k -P' + [ARG]='--cpu --depth -M --machine --recursive -n --iterations -d --delay --order' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --machine|-M) + comps=$( __get_machines ) + ;; + --recursive) + comps='yes no' + ;; + --order) + comps='path tasks cpu memory io' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_cgtop systemd-cgtop diff --git a/src/systemd-cgtop/systemd-cgtop.completion.zsh b/src/systemd-cgtop/systemd-cgtop.completion.zsh new file mode 100644 index 0000000000..f6e1b2422a --- /dev/null +++ b/src/systemd-cgtop/systemd-cgtop.completion.zsh @@ -0,0 +1,17 @@ +#compdef systemd-cgtop + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Print version and exit]' \ + '(-c -m -i -t)-p[Order by path]' \ + '(-c -p -m -i)-t[Order by number of tasks]' \ + '(-m -p -i -t)-c[Order by CPU load]' \ + '(-c -p -i -t)-m[Order by memory load]' \ + '(-c -m -p -t)-i[Order by IO load]' \ + {-d+,--delay=}'[Specify delay]:delay:' \ + {-n+,--iterations=}'[Run for N iterations before exiting]:number of iterations:' \ + {-b,--batch}'[Run in batch mode, accepting no input]' \ + '--depth=[Maximum traversal depth]:maximum depth:' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/systemd-cgtop/systemd-cgtop.xml b/src/systemd-cgtop/systemd-cgtop.xml new file mode 100644 index 0000000000..be13631239 --- /dev/null +++ b/src/systemd-cgtop/systemd-cgtop.xml @@ -0,0 +1,377 @@ + + + + + + + + + systemd-cgtop + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cgtop + 1 + + + + systemd-cgtop + Show top control groups by their resource usage + + + + + systemd-cgtop + OPTIONS + GROUP + + + + + Description + + systemd-cgtop shows the top control + groups of the local Linux control group hierarchy, ordered by + their CPU, memory, or disk I/O load. The display is refreshed in + regular intervals (by default every 1s), similar in style to + top1. + If a control group path is specified, shows only the services of + the specified control group. + + If systemd-cgtop is not connected to a + tty, no column headers are printed and the default is to only run + one iteration. The --iterations= argument, if + given, is honored. This mode is suitable for scripting. + + Resource usage is only accounted for control groups in the + relevant hierarchy, i.e. CPU usage is only accounted for control + groups in the cpuacct hierarchy, memory usage + only for those in memory and disk I/O usage for + those in blkio. If resource monitoring for + these resources is required, it is recommended to add the + CPUAccounting=1, + MemoryAccounting=1 and + BlockIOAccounting=1 settings in the unit files + in question. See + systemd.resource-control5 + for details. + + The CPU load value can be between 0 and 100 times the number of + processors the system has. For example, if the system has 8 processors, + the CPU load value is going to be between 0% and 800%. The number of + processors can be found in /proc/cpuinfo. + + To emphasize this: unless + CPUAccounting=1, + MemoryAccounting=1 and + BlockIOAccounting=1 are enabled for the + services in question, no resource accounting will be available for + system services and the data shown by + systemd-cgtop will be incomplete. + + + + Options + + The following options are understood: + + + + + + + Order by control group + path name. + + + + + + + Order by number of tasks/processes in the control group. + + + + + + + Order by CPU load. + + + + + + + Order by memory usage. + + + + + + + Order by disk I/O load. + + + + + + + Run in "batch" mode: do not accept input and + run until the iteration limit set with + is exhausted or until killed. + This mode could be useful for sending output from + systemd-cgtop to other programs or to a + file. + + + + + + + Format byte counts (as in memory usage and I/O metrics) + with raw numeric values rather than human-readable + numbers. + + + + + + + Controls whether the CPU usage is shown as + percentage or time. By default, the CPU usage is shown as + percentage. This setting may also be toggled at runtime by + pressing the % key. + + + + + + Count only userspace processes instead of all + tasks. By default, all tasks are counted: each kernel thread + and each userspace thread individually. With this setting, + kernel threads are excluded from the counting and each + userspace process only counts as one, regardless how many + threads it consists of. This setting may also be toggled at + runtime by pressing the P key. This option + may not be combined with + . + + + + + + Count only userspace processes and kernel + threads instead of all tasks. By default, all tasks are + counted: each kernel thread and each userspace thread + individually. With this setting, kernel threads are included in + the counting and each userspace process only counts as on one, + regardless how many threads it consists of. This setting may + also be toggled at runtime by pressing the k + key. This option may not be combined with + . + + + + + + Controls whether the number of processes shown + for a control group shall include all processes that are + contained in any of the child control groups as well. Takes a + boolean argument, which defaults to yes. If + enabled, the processes in child control groups are included, if + disabled, only the processes in the control group itself are + counted. This setting may also be toggled at runtime by + pressing the r key. Note that this setting + only applies to process counting, i.e. when the + or options are + used. It has not effect if all tasks are counted, in which + case the counting is always recursive. + + + + + + + Perform only this many iterations. A value of + 0 indicates that the program should run + indefinitely. + + + + + + + Specify refresh delay in seconds (or if one of + ms, us, + min is specified as unit in this time + unit). This setting may also be increased and decreased at + runtime by pressing the + and + - keys. + + + + + + Maximum control group tree traversal depth. + Specifies how deep systemd-cgtop shall + traverse the control group hierarchies. If 0 is specified, + only the root group is monitored. For 1, only the first level + of control groups is monitored, and so on. Defaults to + 3. + + + + + + + Limit control groups shown to the part + corresponding to the container + MACHINE. + This option may not be used when a control group path is specified. + + + + + + + + + + Keys + + systemd-cgtop is an interactive tool and + may be controlled via user input using the following keys: + + + + h + + Shows a short help text. + + + + + + Immediately refresh output. + + + + q + + Terminate the program. + + + + p + t + c + m + i + + Sort the control groups by path, number of + tasks, CPU load, memory usage, or I/O load, respectively. This + setting may also be controlled using the + command line + switch. + + + + % + + Toggle between showing CPU time as time or + percentage. This setting may also be controlled using the + command line switch. + + + + + + - + + Increase or decrease refresh delay, + respectively. This setting may also be controlled using the + command line + switch. + + + + P + + Toggle between counting all tasks, or only + userspace processes. This setting may also be controlled using + the command line switch (see + above). + + + + k + + Toggle between counting all tasks, or only + userspace processes and kernel threads. This setting may also + be controlled using the command line + switch (see above). + + + + r + + Toggle between recursively including or + excluding processes in child control groups in control group + process counts. This setting may also be controlled using the + command line switch. This key is + not available if all tasks are counted, it is only available + if processes are counted, as enabled with the + P or k + keys. + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + systemctl1, + systemd-cgls1, + systemd.resource-control5, + top1 + + + + diff --git a/src/systemd-cryptsetup/Makefile b/src/systemd-cryptsetup/Makefile new file mode 100644 index 0000000000..f4c8afad96 --- /dev/null +++ b/src/systemd-cryptsetup/Makefile @@ -0,0 +1,58 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_LIBCRYPTSETUP),) +rootlibexec_PROGRAMS += \ + systemd-cryptsetup + +systemgenerator_PROGRAMS += \ + systemd-cryptsetup-generator + +dist_systemunit_DATA += \ + units/cryptsetup.target \ + units/cryptsetup-pre.target + +systemd_cryptsetup_SOURCES = \ + src/cryptsetup/cryptsetup.c + +systemd_cryptsetup_CFLAGS = \ + $(LIBCRYPTSETUP_CFLAGS) + +systemd_cryptsetup_LDADD = \ + libsystemd-shared.la \ + $(LIBCRYPTSETUP_LIBS) + +systemd_cryptsetup_generator_SOURCES = \ + src/cryptsetup/cryptsetup-generator.c + +systemd_cryptsetup_generator_LDADD = \ + libsystemd-shared.la + +SYSINIT_TARGET_WANTS += \ + cryptsetup.target + +endif # HAVE_LIBCRYPTSETUP + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-cryptsetup/cryptsetup-generator.c b/src/systemd-cryptsetup/cryptsetup-generator.c new file mode 100644 index 0000000000..3ad6d34a98 --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup-generator.c @@ -0,0 +1,509 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "shared/dropin.h" +#include "shared/fstab-util.h" +#include "shared/generator.h" + +typedef struct crypto_device { + char *uuid; + char *keyfile; + char *name; + char *options; + bool create; +} crypto_device; + +static const char *arg_dest = "/tmp"; +static bool arg_enabled = true; +static bool arg_read_crypttab = true; +static bool arg_whitelist = false; +static Hashmap *arg_disks = NULL; +static char *arg_default_options = NULL; +static char *arg_default_keyfile = NULL; + +static int create_disk( + const char *name, + const char *device, + const char *password, + const char *options) { + + _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL, + *filtered = NULL; + _cleanup_fclose_ FILE *f = NULL; + bool noauto, nofail, tmp, swap; + char *from; + int r; + + assert(name); + assert(device); + + noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0"); + nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0"); + tmp = fstab_test_option(options, "tmp\0"); + swap = fstab_test_option(options, "swap\0"); + + if (tmp && swap) { + log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); + return -EINVAL; + } + + e = unit_name_escape(name); + if (!e) + return log_oom(); + + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", n, NULL); + if (!p) + return log_oom(); + + u = fstab_node_to_udev_node(device); + if (!u) + return log_oom(); + + r = unit_name_from_path(u, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); + + fputs( + "# Automatically generated by systemd-cryptsetup-generator\n\n" + "[Unit]\n" + "Description=Cryptography Setup for %I\n" + "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n" + "SourcePath=/etc/crypttab\n" + "DefaultDependencies=no\n" + "Conflicts=umount.target\n" + "BindsTo=dev-mapper-%i.device\n" + "IgnoreOnIsolate=true\n" + "After=cryptsetup-pre.target\n", + f); + + if (!nofail) + fprintf(f, + "Before=cryptsetup.target\n"); + + if (password) { + if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) + fputs("After=systemd-random-seed.service\n", f); + else if (!streq(password, "-") && !streq(password, "none")) { + _cleanup_free_ char *uu; + + uu = fstab_node_to_udev_node(password); + if (!uu) + return log_oom(); + + if (!path_equal(uu, "/dev/null")) { + + if (is_device_path(uu)) { + _cleanup_free_ char *dd = NULL; + + r = unit_name_from_path(uu, ".device", &dd); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + fprintf(f, "After=%1$s\nRequires=%1$s\n", dd); + } else + fprintf(f, "RequiresMountsFor=%s\n", password); + } + } + } + + if (is_device_path(u)) + fprintf(f, + "BindsTo=%s\n" + "After=%s\n" + "Before=umount.target\n", + d, d); + else + fprintf(f, + "RequiresMountsFor=%s\n", + u); + + r = generator_write_timeouts(arg_dest, device, name, options, &filtered); + if (r < 0) + return r; + + fprintf(f, + "\n[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "TimeoutSec=0\n" /* the binary handles timeouts anyway */ + "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" + "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", + name, u, strempty(password), strempty(filtered), + name); + + if (tmp) + fprintf(f, + "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n", + name); + + if (swap) + fprintf(f, + "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", + name); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write file %s: %m", p); + + from = strjoina("../", n); + + if (!noauto) { + + to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); + if (!nofail) + to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); + else + to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + } + + free(to); + to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + if (!noauto && !nofail) { + _cleanup_free_ char *dmname; + dmname = strjoin("dev-mapper-", e, ".device", NULL); + if (!dmname) + return log_oom(); + + r = write_drop_in(arg_dest, dmname, 90, "device-timeout", + "# Automatically generated by systemd-cryptsetup-generator \n\n" + "[Unit]\nJobTimeoutSec=0"); + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + } + + return 0; +} + +static void free_arg_disks(void) { + crypto_device *d; + + while ((d = hashmap_steal_first(arg_disks))) { + free(d->uuid); + free(d->keyfile); + free(d->name); + free(d->options); + free(d); + } + + hashmap_free(arg_disks); +} + +static crypto_device *get_crypto_device(const char *uuid) { + int r; + crypto_device *d; + + assert(uuid); + + d = hashmap_get(arg_disks, uuid); + if (!d) { + d = new0(struct crypto_device, 1); + if (!d) + return NULL; + + d->create = false; + d->keyfile = d->options = d->name = NULL; + + d->uuid = strdup(uuid); + if (!d->uuid) { + free(d); + return NULL; + } + + r = hashmap_put(arg_disks, d->uuid, d); + if (r < 0) { + free(d->uuid); + free(d); + return NULL; + } + } + + return d; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + crypto_device *d; + _cleanup_free_ char *uuid = NULL, *uuid_value = NULL; + + if (STR_IN_SET(key, "luks", "rd.luks") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse luks switch %s. Ignoring.", value); + else + arg_enabled = r; + + } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value); + else + arg_read_crypttab = r; + + } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) { + + d = get_crypto_device(startswith(value, "luks-") ? value+5 : value); + if (!d) + return log_oom(); + + d->create = arg_whitelist = true; + + } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) { + + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + free(d->options); + d->options = uuid_value; + uuid_value = NULL; + } else if (free_and_strdup(&arg_default_options, value) < 0) + return log_oom(); + + } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) { + + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + free(d->keyfile); + d->keyfile = uuid_value; + uuid_value = NULL; + } else if (free_and_strdup(&arg_default_keyfile, value) < 0) + return log_oom(); + + } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) { + + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + d->create = arg_whitelist = true; + + free(d->name); + d->name = uuid_value; + uuid_value = NULL; + } else + log_warning("Failed to parse luks name switch %s. Ignoring.", value); + + } + + return 0; +} + +static int add_crypttab_devices(void) { + struct stat st; + unsigned crypttab_line = 0; + _cleanup_fclose_ FILE *f = NULL; + + if (!arg_read_crypttab) + return 0; + + f = fopen("/etc/crypttab", "re"); + if (!f) { + if (errno != ENOENT) + log_error_errno(errno, "Failed to open /etc/crypttab: %m"); + return 0; + } + + if (fstat(fileno(f), &st) < 0) { + log_error_errno(errno, "Failed to stat /etc/crypttab: %m"); + return 0; + } + + for (;;) { + int r, k; + char line[LINE_MAX], *l, *uuid; + crypto_device *d = NULL; + _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL; + + if (!fgets(line, sizeof(line), f)) + break; + + crypttab_line++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options); + if (k < 2 || k > 4) { + log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line); + continue; + } + + uuid = startswith(device, "UUID="); + if (!uuid) + uuid = path_startswith(device, "/dev/disk/by-uuid/"); + if (!uuid) + uuid = startswith(name, "luks-"); + if (uuid) + d = hashmap_get(arg_disks, uuid); + + if (arg_whitelist && !d) { + log_info("Not creating device '%s' because it was not specified on the kernel command line.", name); + continue; + } + + r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options); + if (r < 0) + return r; + + if (d) + d->create = false; + } + + return 0; +} + +static int add_proc_cmdline_devices(void) { + int r; + Iterator i; + crypto_device *d; + + HASHMAP_FOREACH(d, arg_disks, i) { + const char *options; + _cleanup_free_ char *device = NULL; + + if (!d->create) + continue; + + if (!d->name) { + d->name = strappend("luks-", d->uuid); + if (!d->name) + return log_oom(); + } + + device = strappend("UUID=", d->uuid); + if (!device) + return log_oom(); + + if (d->options) + options = d->options; + else if (arg_default_options) + options = arg_default_options; + else + options = "timeout=0"; + + r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options); + if (r < 0) + return r; + } + + return 0; +} + +int main(int argc, char *argv[]) { + int r = EXIT_FAILURE; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + arg_disks = hashmap_new(&string_hash_ops); + if (!arg_disks) + goto cleanup; + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) { + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + r = EXIT_FAILURE; + } + + if (!arg_enabled) { + r = EXIT_SUCCESS; + goto cleanup; + } + + if (add_crypttab_devices() < 0) + goto cleanup; + + if (add_proc_cmdline_devices() < 0) + goto cleanup; + + r = EXIT_SUCCESS; + +cleanup: + free_arg_disks(); + free(arg_default_options); + free(arg_default_keyfile); + + return r; +} diff --git a/src/systemd-cryptsetup/cryptsetup-pre.target b/src/systemd-cryptsetup/cryptsetup-pre.target new file mode 100644 index 0000000000..65353419fc --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup-pre.target @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Encrypted Volumes (Pre) +Documentation=man:systemd.special(7) +RefuseManualStart=yes diff --git a/src/systemd-cryptsetup/cryptsetup.c b/src/systemd-cryptsetup/cryptsetup.c new file mode 100644 index 0000000000..eef2b1954c --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup.c @@ -0,0 +1,756 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "sd-device/device-util.h" +#include "sd-device/sd-device.h" +#include "shared/ask-password-api.h" + +static const char *arg_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */ +static char *arg_cipher = NULL; +static unsigned arg_key_size = 0; +static int arg_key_slot = CRYPT_ANY_SLOT; +static unsigned arg_keyfile_size = 0; +static unsigned arg_keyfile_offset = 0; +static char *arg_hash = NULL; +static char *arg_header = NULL; +static unsigned arg_tries = 3; +static bool arg_readonly = false; +static bool arg_verify = false; +static bool arg_discards = false; +static bool arg_tcrypt_hidden = false; +static bool arg_tcrypt_system = false; +static char **arg_tcrypt_keyfiles = NULL; +static uint64_t arg_offset = 0; +static uint64_t arg_skip = 0; +static usec_t arg_timeout = 0; + +/* Options Debian's crypttab knows we don't: + + precheck= + check= + checkargs= + noearly= + loud= + keyscript= +*/ + +static int parse_one_option(const char *option) { + assert(option); + + /* Handled outside of this tool */ + if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail")) + return 0; + + if (startswith(option, "cipher=")) { + char *t; + + t = strdup(option+7); + if (!t) + return log_oom(); + + free(arg_cipher); + arg_cipher = t; + + } else if (startswith(option, "size=")) { + + if (safe_atou(option+5, &arg_key_size) < 0) { + log_error("size= parse failure, ignoring."); + return 0; + } + + if (arg_key_size % 8) { + log_error("size= not a multiple of 8, ignoring."); + return 0; + } + + arg_key_size /= 8; + + } else if (startswith(option, "key-slot=")) { + + arg_type = CRYPT_LUKS1; + if (safe_atoi(option+9, &arg_key_slot) < 0) { + log_error("key-slot= parse failure, ignoring."); + return 0; + } + + } else if (startswith(option, "tcrypt-keyfile=")) { + + arg_type = CRYPT_TCRYPT; + if (path_is_absolute(option+15)) { + if (strv_extend(&arg_tcrypt_keyfiles, option + 15) < 0) + return log_oom(); + } else + log_error("Key file path '%s' is not absolute. Ignoring.", option+15); + + } else if (startswith(option, "keyfile-size=")) { + + if (safe_atou(option+13, &arg_keyfile_size) < 0) { + log_error("keyfile-size= parse failure, ignoring."); + return 0; + } + + } else if (startswith(option, "keyfile-offset=")) { + + if (safe_atou(option+15, &arg_keyfile_offset) < 0) { + log_error("keyfile-offset= parse failure, ignoring."); + return 0; + } + + } else if (startswith(option, "hash=")) { + char *t; + + t = strdup(option+5); + if (!t) + return log_oom(); + + free(arg_hash); + arg_hash = t; + + } else if (startswith(option, "header=")) { + arg_type = CRYPT_LUKS1; + + if (!path_is_absolute(option+7)) { + log_error("Header path '%s' is not absolute, refusing.", option+7); + return -EINVAL; + } + + if (arg_header) { + log_error("Duplicate header= options, refusing."); + return -EINVAL; + } + + arg_header = strdup(option+7); + if (!arg_header) + return log_oom(); + + } else if (startswith(option, "tries=")) { + + if (safe_atou(option+6, &arg_tries) < 0) { + log_error("tries= parse failure, ignoring."); + return 0; + } + + } else if (STR_IN_SET(option, "readonly", "read-only")) + arg_readonly = true; + else if (streq(option, "verify")) + arg_verify = true; + else if (STR_IN_SET(option, "allow-discards", "discard")) + arg_discards = true; + else if (streq(option, "luks")) + arg_type = CRYPT_LUKS1; + else if (streq(option, "tcrypt")) + arg_type = CRYPT_TCRYPT; + else if (streq(option, "tcrypt-hidden")) { + arg_type = CRYPT_TCRYPT; + arg_tcrypt_hidden = true; + } else if (streq(option, "tcrypt-system")) { + arg_type = CRYPT_TCRYPT; + arg_tcrypt_system = true; + } else if (STR_IN_SET(option, "plain", "swap", "tmp")) + arg_type = CRYPT_PLAIN; + else if (startswith(option, "timeout=")) { + + if (parse_sec(option+8, &arg_timeout) < 0) { + log_error("timeout= parse failure, ignoring."); + return 0; + } + + } else if (startswith(option, "offset=")) { + + if (safe_atou64(option+7, &arg_offset) < 0) { + log_error("offset= parse failure, refusing."); + return -EINVAL; + } + + } else if (startswith(option, "skip=")) { + + if (safe_atou64(option+5, &arg_skip) < 0) { + log_error("skip= parse failure, refusing."); + return -EINVAL; + } + + } else if (!streq(option, "none")) + log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option); + + return 0; +} + +static int parse_options(const char *options) { + const char *word, *state; + size_t l; + int r; + + assert(options); + + FOREACH_WORD_SEPARATOR(word, l, options, ",", state) { + _cleanup_free_ char *o; + + o = strndup(word, l); + if (!o) + return -ENOMEM; + r = parse_one_option(o); + if (r < 0) + return r; + } + + /* sanity-check options */ + if (arg_type != NULL && !streq(arg_type, CRYPT_PLAIN)) { + if (arg_offset) + log_warning("offset= ignored with type %s", arg_type); + if (arg_skip) + log_warning("skip= ignored with type %s", arg_type); + } + + return 0; +} + +static void log_glue(int level, const char *msg, void *usrptr) { + log_debug("%s", msg); +} + +static int disk_major_minor(const char *path, char **ret) { + struct stat st; + + assert(path); + + if (stat(path, &st) < 0) + return -errno; + + if (!S_ISBLK(st.st_mode)) + return -EINVAL; + + if (asprintf(ret, "/dev/block/%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) + return -errno; + + return 0; +} + +static char* disk_description(const char *path) { + + static const char name_fields[] = + "ID_PART_ENTRY_NAME\0" + "DM_NAME\0" + "ID_MODEL_FROM_DATABASE\0" + "ID_MODEL\0"; + + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + struct stat st; + const char *i; + int r; + + assert(path); + + if (stat(path, &st) < 0) + return NULL; + + if (!S_ISBLK(st.st_mode)) + return NULL; + + r = sd_device_new_from_devnum(&device, 'b', st.st_rdev); + if (r < 0) + return NULL; + + NULSTR_FOREACH(i, name_fields) { + const char *name; + + r = sd_device_get_property_value(device, i, &name); + if (r >= 0 && !isempty(name)) + return strdup(name); + } + + return NULL; +} + +static char *disk_mount_point(const char *label) { + _cleanup_free_ char *device = NULL; + _cleanup_endmntent_ FILE *f = NULL; + struct mntent *m; + + /* Yeah, we don't support native systemd unit files here for now */ + + if (asprintf(&device, "/dev/mapper/%s", label) < 0) + return NULL; + + f = setmntent("/etc/fstab", "r"); + if (!f) + return NULL; + + while ((m = getmntent(f))) + if (path_equal(m->mnt_fsname, device)) + return strdup(m->mnt_dir); + + return NULL; +} + +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; + char **p, *id; + int r = 0; + + assert(vol); + assert(src); + assert(ret); + + description = disk_description(src); + mount_point = disk_mount_point(vol); + + if (description && streq(vol, description)) + /* If the description string is simply the + * volume name, then let's not show this + * twice */ + description = mfree(description); + + if (mount_point && description) + r = asprintf(&name_buffer, "%s (%s) on %s", description, vol, mount_point); + else if (mount_point) + r = asprintf(&name_buffer, "%s on %s", vol, mount_point); + else if (description) + r = asprintf(&name_buffer, "%s (%s)", description, vol); + + if (r < 0) + return log_oom(); + + name = name_buffer ? name_buffer : vol; + + if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) + return log_oom(); + + if (src) + (void) disk_major_minor(src, &maj_min); + + if (maj_min) { + escaped_name = maj_min; + maj_min = NULL; + } else + escaped_name = cescape(name); + + if (!escaped_name) + return log_oom(); + + id = strjoina("cryptsetup:", escaped_name); + + 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_erase_ char **passwords2 = NULL; + + 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, "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])) { + log_warning("Passwords did not match, retrying."); + return -EAGAIN; + } + } + + strv_uniq(passwords); + + STRV_FOREACH(p, passwords) { + char *c; + + if (strlen(*p)+1 >= arg_key_size) + continue; + + /* Pad password if necessary */ + c = new(char, arg_key_size); + if (!c) + return log_oom(); + + strncpy(c, *p, arg_key_size); + free(*p); + *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) { + + int r = 0; + _cleanup_free_ char *passphrase = NULL; + struct crypt_params_tcrypt params = { + .flags = CRYPT_TCRYPT_LEGACY_MODES, + .keyfiles = (const char **)arg_tcrypt_keyfiles, + .keyfiles_count = strv_length(arg_tcrypt_keyfiles) + }; + + assert(cd); + assert(name); + assert(key_file || (passwords && passwords[0])); + + if (arg_tcrypt_hidden) + params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER; + + if (arg_tcrypt_system) + params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER; + + if (key_file) { + r = read_one_line_file(key_file, &passphrase); + if (r < 0) { + log_error_errno(r, "Failed to read password file '%s': %m", key_file); + return -EAGAIN; + } + + params.passphrase = passphrase; + } else + params.passphrase = passwords[0]; + params.passphrase_size = strlen(params.passphrase); + + r = crypt_load(cd, CRYPT_TCRYPT, ¶ms); + if (r < 0) { + if (key_file && r == -EPERM) { + log_error("Failed to activate using password file '%s'.", key_file); + return -EAGAIN; + } + return r; + } + + return crypt_activate_by_volume_key(cd, name, NULL, 0, flags); +} + +static int attach_luks_or_plain(struct crypt_device *cd, + const char *name, + const char *key_file, + const char *data_device, + char **passwords, + uint32_t flags) { + int r = 0; + bool pass_volume_key = false; + + assert(cd); + assert(name); + assert(key_file || passwords); + + if (!arg_type || streq(arg_type, CRYPT_LUKS1)) { + r = crypt_load(cd, CRYPT_LUKS1, NULL); + if (r < 0) { + log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd)); + return r; + } + + if (data_device) + r = crypt_set_data_device(cd, data_device); + } + + if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) { + struct crypt_params_plain params = { + .offset = arg_offset, + .skip = arg_skip, + }; + const char *cipher, *cipher_mode; + _cleanup_free_ char *truncated_cipher = NULL; + + if (arg_hash) { + /* plain isn't a real hash type. it just means "use no hash" */ + if (!streq(arg_hash, "plain")) + params.hash = arg_hash; + } else if (!key_file) + /* for CRYPT_PLAIN, the behaviour of cryptsetup + * package is to not hash when a key file is provided */ + params.hash = "ripemd160"; + + if (arg_cipher) { + size_t l; + + l = strcspn(arg_cipher, "-"); + truncated_cipher = strndup(arg_cipher, l); + if (!truncated_cipher) + return log_oom(); + + cipher = truncated_cipher; + cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain"; + } else { + cipher = "aes"; + cipher_mode = "cbc-essiv:sha256"; + } + + /* for CRYPT_PLAIN limit reads + * from keyfile to key length, and + * ignore keyfile-size */ + arg_keyfile_size = arg_key_size; + + /* In contrast to what the name + * crypt_setup() might suggest this + * doesn't actually format anything, + * it just configures encryption + * parameters when used for plain + * mode. */ + r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, ¶ms); + + /* hash == NULL implies the user passed "plain" */ + pass_volume_key = (params.hash == NULL); + } + + if (r < 0) + return log_error_errno(r, "Loading of cryptographic parameters failed: %m"); + + log_info("Set cipher %s, mode %s, key size %i bits for device %s.", + crypt_get_cipher(cd), + crypt_get_cipher_mode(cd), + crypt_get_volume_key_size(cd)*8, + 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); + if (r < 0) { + log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); + return -EAGAIN; + } + } else { + char **p; + + STRV_FOREACH(p, passwords) { + if (pass_volume_key) + r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags); + else + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags); + + if (r >= 0) + break; + } + } + + return r; +} + +static int help(void) { + + printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n" + "%s detach VOLUME\n\n" + "Attaches or detaches an encrypted block device.\n", + program_invocation_short_name, + program_invocation_short_name); + + return 0; +} + +int main(int argc, char *argv[]) { + int r = EXIT_FAILURE; + struct crypt_device *cd = NULL; + + if (argc <= 1) { + help(); + return EXIT_SUCCESS; + } + + if (argc < 3) { + log_error("This program requires at least two arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (streq(argv[1], "attach")) { + uint32_t flags = 0; + int k; + unsigned tries; + usec_t until; + crypt_status_info status; + const char *key_file = NULL; + + /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ + + if (argc < 4) { + log_error("attach requires at least two arguments."); + goto finish; + } + + if (argc >= 5 && + argv[4][0] && + !streq(argv[4], "-") && + !streq(argv[4], "none")) { + + if (!path_is_absolute(argv[4])) + log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]); + else + key_file = argv[4]; + } + + if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) { + if (parse_options(argv[5]) < 0) + goto finish; + } + + /* A delicious drop of snake oil */ + mlockall(MCL_FUTURE); + + if (arg_header) { + log_debug("LUKS header: %s", arg_header); + 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; + } + + crypt_set_log_callback(cd, log_glue, NULL); + + status = crypt_status(cd, argv[2]); + if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) { + log_info("Volume %s already active.", argv[2]); + r = EXIT_SUCCESS; + goto finish; + } + + if (arg_readonly) + flags |= CRYPT_ACTIVATE_READONLY; + + if (arg_discards) + flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + + if (arg_timeout > 0) + until = now(CLOCK_MONOTONIC) + arg_timeout; + else + until = 0; + + arg_key_size = (arg_key_size > 0 ? arg_key_size : (256 / 8)); + + if (key_file) { + struct stat st; + + /* Ideally we'd do this on the open fd, but since this is just a + * warning it's OK to do this in two steps. */ + if (stat(key_file, &st) >= 0 && S_ISREG(st.st_mode) && (st.st_mode & 0005)) + log_warning("Key file %s is world-readable. This is not a good idea!", key_file); + } + + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { + _cleanup_strv_free_erase_ char **passwords = NULL; + + if (!key_file) { + k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); + if (k == -EAGAIN) + continue; + else if (k < 0) + goto finish; + } + + if (streq_ptr(arg_type, CRYPT_TCRYPT)) + k = attach_tcrypt(cd, argv[2], key_file, passwords, flags); + else + k = attach_luks_or_plain(cd, + argv[2], + key_file, + arg_header ? argv[3] : NULL, + passwords, + flags); + if (k >= 0) + break; + else if (k == -EAGAIN) { + key_file = NULL; + continue; + } else if (k != -EPERM) { + log_error_errno(k, "Failed to activate: %m"); + goto finish; + } + + log_warning("Invalid passphrase."); + } + + if (arg_tries != 0 && tries >= arg_tries) { + log_error("Too many attempts; giving up."); + r = EXIT_FAILURE; + goto finish; + } + + } else if (streq(argv[1], "detach")) { + int k; + + k = crypt_init_by_name(&cd, argv[2]); + if (k == -ENODEV) { + log_info("Volume %s already inactive.", argv[2]); + r = EXIT_SUCCESS; + goto finish; + } else if (k) { + log_error_errno(k, "crypt_init_by_name() failed: %m"); + goto finish; + } + + crypt_set_log_callback(cd, log_glue, NULL); + + k = crypt_deactivate(cd, argv[2]); + if (k < 0) { + log_error_errno(k, "Failed to deactivate: %m"); + goto finish; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + goto finish; + } + + r = EXIT_SUCCESS; + +finish: + + if (cd) + crypt_free(cd); + + free(arg_cipher); + free(arg_hash); + free(arg_header); + strv_free(arg_tcrypt_keyfiles); + + return r; +} diff --git a/src/systemd-cryptsetup/cryptsetup.target b/src/systemd-cryptsetup/cryptsetup.target new file mode 100644 index 0000000000..25d3e33f6a --- /dev/null +++ b/src/systemd-cryptsetup/cryptsetup.target @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Encrypted Volumes +Documentation=man:systemd.special(7) diff --git a/src/systemd-cryptsetup/crypttab.xml b/src/systemd-cryptsetup/crypttab.xml new file mode 100644 index 0000000000..4b8d4aa3d6 --- /dev/null +++ b/src/systemd-cryptsetup/crypttab.xml @@ -0,0 +1,416 @@ + + + + + + + + crypttab + systemd + + + + Documentation + Miloslav + Trmac + mitr@redhat.com + + + Documentation + Lennart + Poettering + lennart@poettering.net + + + + + + crypttab + 5 + + + + crypttab + Configuration for encrypted block devices + + + + /etc/crypttab + + + + Description + + The /etc/crypttab file describes + encrypted block devices that are set up during system boot. + + Empty lines and lines starting with the # + character are ignored. Each of the remaining lines describes one + encrypted block device, fields on the line are delimited by white + space. The first two fields are mandatory, the remaining two are + optional. + + Setting up encrypted block devices using this file supports + three encryption modes: LUKS, TrueCrypt and plain. See + cryptsetup8 + for more information about each mode. When no mode is specified in + the options field and the block device contains a LUKS signature, + it is opened as a LUKS device; otherwise, it is assumed to be in + raw dm-crypt (plain mode) format. + + The first field contains the name of the resulting encrypted + block device; the device is set up within + /dev/mapper/. + + The second field contains a path to the underlying block + device or file, or a specification of a block device via + UUID= followed by the UUID. + + The third field specifies the encryption password. If the + field is not present or the password is set to + none or -, the password has + to be manually entered during system boot. Otherwise, the field is + interpreted as an absolute path to a file containing the encryption + password. For swap encryption, /dev/urandom + or the hardware device /dev/hw_random can be + used as the password file; using /dev/random + may prevent boot completion if the system does not have enough + entropy to generate a truly random encryption key. + + The fourth field, if present, is a comma-delimited list of + options. The following options are recognized: + + + + + + + Allow discard requests to be passed through + the encrypted block device. This improves performance on SSD + storage but has security implications. + + + + + + Specifies the cipher to use. See + cryptsetup8 + for possible values and the default value of this option. A + cipher with unpredictable IV values, such as + aes-cbc-essiv:sha256, is + recommended. + + + + + + Specifies the hash to use for password + hashing. See + cryptsetup8 + for possible values and the default value of this + option. + + + + + + Use a detached (separated) metadata device or + file where the LUKS header is stored. This option is only + relevant for LUKS devices. See + cryptsetup8 + for possible values and the default value of this + option. + + + + + + Start offset in the backend device, in 512-byte sectors. + This option is only relevant for plain devices. + + + + + + + How many 512-byte sectors of the encrypted data to skip + at the beginning. This is different from the + option with respect to the sector numbers used in initialization vector + (IV) calculation. Using will shift the IV + calculation by the same negative amount. Hence, if is given, + sector n will get a sector number of 0 for the IV calculation. + Using causes sector n to also be the first + sector of the mapped device, but with its number for IV generation being n. + + This option is only relevant for plain devices. + + + + + + + Specifies the number of bytes to skip at the + start of the key file. See + cryptsetup8 + for possible values and the default value of this + option. + + + + + + Specifies the maximum number of bytes to read + from the key file. See + cryptsetup8 + for possible values and the default value of this option. This + option is ignored in plain encryption mode, as the key file + size is then given by the key size. + + + + + + Specifies the key slot to compare the + passphrase or key against. If the key slot does not match the + given passphrase or key, but another would, the setup of the + device will fail regardless. This option implies + . See + cryptsetup8 + for possible values. The default is to try all key slots in + sequential order. + + + + + + Force LUKS mode. When this mode is used, the + following options are ignored since they are provided by the + LUKS header on the device: , + , + . + + + + + + This device will not be automatically unlocked + on boot. + + + + + + The system will not wait for the device to + show up and be unlocked at boot, and not fail the boot if it + does not show up. + + + + + + Force plain encryption mode. + + + + + + Set up the encrypted block device in read-only + mode. + + + + + + Specifies the key size in bits. See + cryptsetup8 + for possible values and the default value of this + option. + + + + + + The encrypted block device will be used as a + swap device, and will be formatted accordingly after setting + up the encrypted block device, with + mkswap8. + This option implies . + + WARNING: Using the option will + destroy the contents of the named partition during every boot, + so make sure the underlying block device is specified + correctly. + + + + + + Use TrueCrypt encryption mode. When this mode + is used, the following options are ignored since they are + provided by the TrueCrypt header on the device or do not + apply: + , + , + , + , + . + + When this mode is used, the passphrase is read from the + key file given in the third field. Only the first line of this + file is read, excluding the new line character. + + Note that the TrueCrypt format uses both passphrase and + key files to derive a password for the volume. Therefore, the + passphrase and all key files need to be provided. Use + to provide the absolute path + to all key files. When using an empty passphrase in + combination with one or more key files, use + /dev/null as the password file in the third + field. + + + + + + Use the hidden TrueCrypt volume. This option + implies . + + This will map the hidden volume that is inside of the + volume provided in the second field. Please note that there is + no protection for the hidden volume if the outer volume is + mounted instead. See + cryptsetup8 + for more information on this limitation. + + + + + + Specifies the absolute path to a key file to + use for a TrueCrypt volume. This implies + and can be used more than once to + provide several key files. + + See the entry for on the + behavior of the passphrase and key files when using TrueCrypt + encryption mode. + + + + + + Use TrueCrypt in system encryption mode. This + option implies . + + + + + + Specifies the timeout for querying for a + password. If no unit is specified, seconds is used. Supported + units are s, ms, us, min, h, d. A timeout of 0 waits + indefinitely (which is the default). + + + + + + Specifies how long systemd should wait for a + device to show up before giving up on the entry. The argument + is a time in seconds or explicitly specified units of + s, + min, + h, + ms. + + + + + + + The encrypted block device will be prepared + for using it as /tmp; it will be + formatted using + mke2fs8. + This option implies . + + WARNING: Using the option will + destroy the contents of the named partition during every boot, + so make sure the underlying block device is specified + correctly. + + + + + + Specifies the maximum number of times the user + is queried for a password. The default is 3. If set to 0, the + user is queried for a password indefinitely. + + + + + + If the encryption password is read from + console, it has to be entered twice to prevent + typos. + + + + + At early boot and when the system manager configuration is + reloaded, this file is translated into native systemd units by + systemd-cryptsetup-generator8. + + + + Example + + /etc/crypttab example + Set up four encrypted block devices. One using LUKS for + normal storage, another one for usage as a swap device and two + TrueCrypt volumes. + + luks UUID=2505567a-9e27-4efe-a4d5-15ad146c258b +swap /dev/sda7 /dev/urandom swap +truecrypt /dev/sda2 /etc/container_password tcrypt +hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile + + + + + See Also + + systemd1, + systemd-cryptsetup@.service8, + systemd-cryptsetup-generator8, + cryptsetup8, + mkswap8, + mke2fs8 + + + + diff --git a/src/systemd-cryptsetup/systemd-cryptsetup-generator.xml b/src/systemd-cryptsetup/systemd-cryptsetup-generator.xml new file mode 100644 index 0000000000..f036ab9744 --- /dev/null +++ b/src/systemd-cryptsetup/systemd-cryptsetup-generator.xml @@ -0,0 +1,193 @@ + + + + + + + + systemd-cryptsetup-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cryptsetup-generator + 8 + + + + systemd-cryptsetup-generator + Unit generator for /etc/crypttab + + + + /usr/lib/systemd/system-generators/systemd-cryptsetup-generator + + + + Description + + systemd-cryptsetup-generator is a + generator that translates /etc/crypttab into + native systemd units early at boot and when configuration of the + system manager is reloaded. This will create + systemd-cryptsetup@.service8 + units as necessary. + + systemd-cryptsetup-generator implements + systemd.generator7. + + + + Kernel Command Line + + systemd-cryptsetup-generator + understands the following kernel command line parameters: + + + + luks= + rd.luks= + + Takes a boolean argument. Defaults to + yes. If no, disables the + generator entirely. rd.luks= is honored + only by initial RAM disk (initrd) while + luks= is honored by both the main system + and the initrd. + + + + luks.crypttab= + rd.luks.crypttab= + + Takes a boolean argument. Defaults to + yes. If no, causes the + generator to ignore any devices configured in + /etc/crypttab + (luks.uuid= will still work however). + rd.luks.crypttab= is honored only by + initial RAM disk (initrd) while + luks.crypttab= is honored by both the main + system and the initrd. + + + + luks.uuid= + rd.luks.uuid= + + Takes a LUKS superblock UUID as argument. This + will activate the specified device as part of the boot process + as if it was listed in /etc/crypttab. + This option may be specified more than once in order to set up + multiple devices. rd.luks.uuid= is honored + only by initial RAM disk (initrd) while + luks.uuid= is honored by both the main + system and the initrd. + If /etc/crypttab contains entries with the same UUID, + then the name, keyfile and options specified there will be + used. Otherwise, the device will have the name + luks-UUID. + If /etc/crypttab exists, only those UUIDs + specified on the kernel command line + will be activated in the initrd or the real root. + + + + + luks.name= + rd.luks.name= + + Takes a LUKS super block UUID followed by an + = and a name. This implies + rd.luks.uuid= or + luks.uuid= and will additionally make the + LUKS device given by the UUID appear under the provided + name. + + rd.luks.name= is honored only by + initial RAM disk (initrd) while luks.name= + is honored by both the main system and the initrd. + + + + + luks.options= + rd.luks.options= + + Takes a LUKS super block UUID followed by an + = and a string of options separated by + commas as argument. This will override the options for the + given UUID. + If only a list of options, without an UUID, is + specified, they apply to any UUIDs not specified elsewhere, + and without an entry in + /etc/crypttab. + rd.luks.options= is honored only by initial + RAM disk (initrd) while luks.options= is + honored by both the main system and the initrd. + + + + + luks.key= + rd.luks.key= + + Takes a password file name as argument or a + LUKS super block UUID followed by a = and a + password file name. + + For those entries specified with + rd.luks.uuid= or + luks.uuid=, the password file will be set + to the one specified by rd.luks.key= or + luks.key= of the corresponding UUID, or the + password file that was specified without a UUID. + rd.luks.key= + is honored only by initial RAM disk + (initrd) while + luks.key= is + honored by both the main system and + the initrd. + + + + + + + See Also + + systemd1, + crypttab5, + systemd-cryptsetup@.service8, + cryptsetup8, + systemd-fstab-generator8 + + + + diff --git a/src/systemd-cryptsetup/systemd-cryptsetup@.service.xml b/src/systemd-cryptsetup/systemd-cryptsetup@.service.xml new file mode 100644 index 0000000000..ea524851eb --- /dev/null +++ b/src/systemd-cryptsetup/systemd-cryptsetup@.service.xml @@ -0,0 +1,85 @@ + + + + + + + + systemd-cryptsetup@.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cryptsetup@.service + 8 + + + + systemd-cryptsetup@.service + systemd-cryptsetup + Full disk decryption logic + + + + systemd-cryptsetup@.service + /usr/lib/systemd/systemd-cryptsetup + + + + Description + + systemd-cryptsetup@.service is a + service responsible for setting up encrypted block devices. It is + instantiated for each device that requires decryption for + access. + + systemd-cryptsetup@.service will ask + for hard disk passwords via the + password agent logic, in order to query the user for the + password using the right mechanism at boot and during + runtime. + + At early boot and when the system manager configuration is + reloaded this /etc/crypttab is translated + into systemd-cryptsetup@.service units by + systemd-cryptsetup-generator8. + + + + See Also + + systemd1, + systemd-cryptsetup-generator8, + crypttab5, + cryptsetup8 + + + + diff --git a/src/systemd-debug-generator/Makefile b/src/systemd-debug-generator/Makefile new file mode 100644 index 0000000000..542d8b5a5a --- /dev/null +++ b/src/systemd-debug-generator/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemgenerator_PROGRAMS += systemd-debug-generator + +systemd_debug_generator_SOURCES = \ + src/debug-generator/debug-generator.c + +systemd_debug_generator_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-debug-generator/debug-generator.c b/src/systemd-debug-generator/debug-generator.c new file mode 100644 index 0000000000..2db31e5de6 --- /dev/null +++ b/src/systemd-debug-generator/debug-generator.c @@ -0,0 +1,202 @@ +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/mkdir.h" +#include "basic/parse-util.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/unit-name.h" +#include "basic/util.h" + +static char *arg_default_unit = NULL; +static const char *arg_dest = "/tmp"; +static char **arg_mask = NULL; +static char **arg_wants = NULL; +static bool arg_debug_shell = false; + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + assert(key); + + if (streq(key, "systemd.mask")) { + + if (!value) + log_error("Missing argument for systemd.mask= kernel command line parameter."); + else { + char *n; + + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); + + r = strv_consume(&arg_mask, n); + if (r < 0) + return log_oom(); + } + + } else if (streq(key, "systemd.wants")) { + + if (!value) + log_error("Missing argument for systemd.want= kernel command line parameter."); + else { + char *n; + + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); + + r = strv_consume(&arg_wants, n); + if (r < 0) + return log_oom(); + } + + } else if (streq(key, "systemd.debug-shell")) { + + if (value) { + r = parse_boolean(value); + if (r < 0) + log_error("Failed to parse systemd.debug-shell= argument '%s', ignoring.", value); + else + 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; +} + +static int generate_mask_symlinks(void) { + char **u; + int r = 0; + + if (strv_isempty(arg_mask)) + return 0; + + STRV_FOREACH(u, arg_mask) { + _cleanup_free_ char *p = NULL; + + p = strjoin(arg_dest, "/", *u, NULL); + if (!p) + return log_oom(); + + if (symlink("/dev/null", p) < 0) + r = log_error_errno(errno, + "Failed to create mask symlink %s: %m", + p); + } + + return r; +} + +static int generate_wants_symlinks(void) { + char **u; + int r = 0; + + if (strv_isempty(arg_wants)) + return 0; + + STRV_FOREACH(u, arg_wants) { + _cleanup_free_ char *p = NULL, *f = NULL; + + p = strjoin(arg_dest, "/", arg_default_unit, ".wants/", *u, NULL); + if (!p) + return log_oom(); + + f = strappend(SYSTEM_DATA_UNIT_PATH "/", *u); + if (!f) + return log_oom(); + + mkdir_parents_label(p, 0755); + + if (symlink(f, p) < 0) + r = log_error_errno(errno, + "Failed to create wants symlink %s: %m", + p); + } + + return r; +} + +int main(int argc, char *argv[]) { + int r, q; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[2]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + 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"); + + if (arg_debug_shell) { + r = strv_extend(&arg_wants, "debug-shell.service"); + if (r < 0) { + r = log_oom(); + goto finish; + } + } + + r = generate_mask_symlinks(); + + q = generate_wants_symlinks(); + if (q < 0) + r = q; + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + +} diff --git a/src/systemd-debug-generator/systemd-debug-generator.xml b/src/systemd-debug-generator/systemd-debug-generator.xml new file mode 100644 index 0000000000..5c5e9fc4a1 --- /dev/null +++ b/src/systemd-debug-generator/systemd-debug-generator.xml @@ -0,0 +1,95 @@ + + + + + + + + systemd-debug-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-debug-generator + 8 + + + + systemd-debug-generator + Generator for enabling a runtime debug shell and + masking specific units at boot + + + + /usr/lib/systemd/system-generators/systemd-debug-generator + + + + Description + + systemd-debug-generator is a generator + that reads the kernel command line and understands three + options: + + If the option is specified + and followed by a unit name, this unit is masked for the runtime, + similar to the effect of + systemctl1's + mask command. This is useful to boot with + certain units removed from the initial boot transaction for + debugging system startup. May be specified more than once. + + If the option is specified + and followed by a unit name, a start job for this unit is added to + the initial transaction. This is useful to start one or more + additional units at boot. May be specified more than once. + + If the option is + specified, the debug shell service + debug-shell.service is pulled into the boot + transaction. It will spawn a debug shell on tty9 during early + system startup. Note that the shell may also be turned on + persistently by enabling it with + systemctl1's + enable command. + + systemd-debug-generator implements + systemd.generator7. + + + + See Also + + systemd1, + systemctl1, + kernel-command-line7 + + + + diff --git a/src/systemd-getty-generator/Makefile b/src/systemd-getty-generator/Makefile new file mode 100644 index 0000000000..645dc189f7 --- /dev/null +++ b/src/systemd-getty-generator/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemgenerator_PROGRAMS += systemd-getty-generator +systemd_getty_generator_SOURCES = \ + src/getty-generator/getty-generator.c + +systemd_getty_generator_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-getty-generator/getty-generator.c b/src/systemd-getty-generator/getty-generator.c new file mode 100644 index 0000000000..c78fac7694 --- /dev/null +++ b/src/systemd-getty-generator/getty-generator.c @@ -0,0 +1,233 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/virt.h" + +static const char *arg_dest = "/tmp"; + +static int add_symlink(const char *fservice, const char *tservice) { + char *from, *to; + int r; + + assert(fservice); + assert(tservice); + + from = strjoina(SYSTEM_DATA_UNIT_PATH "/", fservice); + to = strjoina(arg_dest, "/getty.target.wants/", tservice); + + mkdir_parents_label(to, 0755); + + r = symlink(from, to); + if (r < 0) { + /* In case console=hvc0 is passed this will very likely result in EEXIST */ + if (errno == EEXIST) + return 0; + + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + } + + return 0; +} + +static int add_serial_getty(const char *tty) { + _cleanup_free_ char *n = NULL; + int r; + + assert(tty); + + log_debug("Automatically adding serial getty for /dev/%s.", tty); + + r = unit_name_from_path_instance("serial-getty", tty, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate service name: %m"); + + return add_symlink("serial-getty@.service", n); +} + +static int add_container_getty(const char *tty) { + _cleanup_free_ char *n = NULL; + int r; + + assert(tty); + + log_debug("Automatically adding container getty for /dev/pts/%s.", tty); + + r = unit_name_from_path_instance("container-getty", tty, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate service name: %m"); + + return add_symlink("container-getty@.service", n); +} + +static int verify_tty(const char *name) { + _cleanup_close_ int fd = -1; + const char *p; + + /* Some TTYs are weird and have been enumerated but don't work + * when you try to use them, such as classic ttyS0 and + * friends. Let's check that and open the device and run + * isatty() on it. */ + + p = strjoina("/dev/", name); + + /* O_NONBLOCK is essential here, to make sure we don't wait + * for DCD */ + fd = open(p, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + errno = 0; + if (isatty(fd) <= 0) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +int main(int argc, char *argv[]) { + + static const char virtualization_consoles[] = + "hvc0\0" + "xvc0\0" + "hvsi0\0" + "sclp_line0\0" + "ttysclp0\0" + "3270!tty1\0"; + + _cleanup_free_ char *active = NULL; + const char *j; + int r; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + if (detect_container() > 0) { + _cleanup_free_ char *container_ttys = NULL; + + log_debug("Automatically adding console shell."); + + if (add_symlink("console-getty.service", "console-getty.service") < 0) + return EXIT_FAILURE; + + /* When $container_ttys is set for PID 1, spawn + * gettys on all ptys named therein. Note that despite + * the variable name we only support ptys here. */ + + r = getenv_for_pid(1, "container_ttys", &container_ttys); + if (r > 0) { + const char *word, *state; + size_t l; + + FOREACH_WORD(word, l, container_ttys, state) { + const char *t; + char tty[l + 1]; + + memcpy(tty, word, l); + tty[l] = 0; + + /* First strip off /dev/ if it is specified */ + t = path_startswith(tty, "/dev/"); + if (!t) + t = tty; + + /* Then, make sure it's actually a pty */ + t = path_startswith(t, "pts/"); + if (!t) + continue; + + if (add_container_getty(t) < 0) + return EXIT_FAILURE; + } + } + + /* Don't add any further magic if we are in a container */ + return EXIT_SUCCESS; + } + + if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { + const char *word, *state; + size_t l; + + /* Automatically add in a serial getty on all active + * kernel consoles */ + FOREACH_WORD(word, l, active, state) { + _cleanup_free_ char *tty = NULL; + + tty = strndup(word, l); + if (!tty) { + log_oom(); + return EXIT_FAILURE; + } + + if (isempty(tty) || tty_is_vc(tty)) + continue; + + if (verify_tty(tty) < 0) + continue; + + /* We assume that gettys on virtual terminals are + * started via manual configuration and do this magic + * only for non-VC terminals. */ + + if (add_serial_getty(tty) < 0) + return EXIT_FAILURE; + } + } + + /* Automatically add in a serial getty on the first + * virtualizer console */ + NULSTR_FOREACH(j, virtualization_consoles) { + char *p; + + p = strjoina("/sys/class/tty/", j); + if (access(p, F_OK) < 0) + continue; + + if (add_serial_getty(j) < 0) + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/systemd-getty-generator/systemd-getty-generator.xml b/src/systemd-getty-generator/systemd-getty-generator.xml new file mode 100644 index 0000000000..338925964d --- /dev/null +++ b/src/systemd-getty-generator/systemd-getty-generator.xml @@ -0,0 +1,96 @@ + + + + + + + + systemd-getty-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-getty-generator + 8 + + + + systemd-getty-generator + Generator for enabling getty instances on the + console + + + + /usr/lib/systemd/system-generators/systemd-getty-generator + + + + Description + + systemd-getty-generator is a generator + that automatically instantiates + serial-getty@.service on the kernel console + /dev/console if that is not directed to the + virtual console subsystem. It will also instantiate + serial-getty@.service instances for + virtualizer consoles, if execution in a virtualized environment is + detected. Finally, it will instantiate + container-getty@.service instances for + additional container pseudo TTYs as requested by the container + manager (see Container + Interface). This should ensure that the user is + shown a login prompt at the right place, regardless of which + environment the system is started in. For example, it is + sufficient to redirect the kernel console with a kernel command + line argument such as console= to get both + kernel messages and a getty prompt on a serial TTY. See kernel-parameters.txt + for more information on the console= kernel + parameter. + + systemd-getty-generator implements + systemd.generator7. + + Further information about configuration of gettys you may + find in + systemd + for Administrators, Part XVI: Gettys on Serial Consoles (and + Elsewhere). + + + + See Also + + systemd1, + agetty8 + + + + diff --git a/src/systemd-gpt-auto-generator/Makefile b/src/systemd-gpt-auto-generator/Makefile new file mode 100644 index 0000000000..ce6b56f998 --- /dev/null +++ b/src/systemd-gpt-auto-generator/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_BLKID),) +systemgenerator_PROGRAMS += \ + systemd-gpt-auto-generator + +systemd_gpt_auto_generator_SOURCES = \ + src/gpt-auto-generator/gpt-auto-generator.c \ + src/basic/blkid-util.h + +systemd_gpt_auto_generator_LDADD = \ + libsystemd-shared.la \ + $(BLKID_LIBS) + +systemd_gpt_auto_generator_CFLAGS = \ + $(BLKID_CFLAGS) +endif # HAVE_BLKID + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-gpt-auto-generator/gpt-auto-generator.c b/src/systemd-gpt-auto-generator/gpt-auto-generator.c new file mode 100644 index 0000000000..fe598da6e1 --- /dev/null +++ b/src/systemd-gpt-auto-generator/gpt-auto-generator.c @@ -0,0 +1,1040 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/blkid-util.h" +#include "basic/btrfs-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/efivars.h" +#include "shared/fstab-util.h" +#include "shared/generator.h" +#include "shared/gpt.h" +#include "shared/udev-util.h" + +static const char *arg_dest = "/tmp"; +static bool arg_enabled = true; +static bool arg_root_enabled = true; +static bool arg_root_rw = false; + +static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) { + _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL; + _cleanup_fclose_ FILE *f = NULL; + char *from, *ret; + int r; + + assert(id); + assert(what); + assert(device); + + r = unit_name_from_path(what, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + e = unit_name_escape(id); + if (!e) + return log_oom(); + + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", n, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=Cryptography Setup for %%I\n" + "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n" + "DefaultDependencies=no\n" + "Conflicts=umount.target\n" + "BindsTo=dev-mapper-%%i.device %s\n" + "Before=umount.target cryptsetup.target\n" + "After=%s\n" + "IgnoreOnIsolate=true\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "TimeoutSec=0\n" /* the binary handles timeouts anyway */ + "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n" + "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", + d, d, + id, what, rw ? "" : "read-only", + id); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write file %s: %m", p); + + from = strjoina("../", n); + + to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); + to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); + to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(p); + p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); + if (!p) + return log_oom(); + + mkdir_parents_label(p, 0755); + r = write_string_file(p, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "JobTimeoutSec=0\n", + WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */ + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + + ret = strappend("/dev/mapper/", id); + if (!ret) + return log_oom(); + + *device = ret; + return 0; +} + +static int add_mount( + const char *id, + const char *what, + const char *where, + const char *fstype, + bool rw, + const char *options, + const char *description, + const char *post) { + + _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(id); + assert(what); + assert(where); + assert(description); + + log_debug("Adding %s: %s %s", where, what, strna(fstype)); + + if (streq_ptr(fstype, "crypto_LUKS")) { + + r = add_cryptsetup(id, what, rw, &crypto_what); + if (r < 0) + return r; + + what = crypto_what; + fstype = NULL; + } + + r = unit_name_from_path(where, ".mount", &unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", unit, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=%s\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n", + description); + + if (post) + fprintf(f, "Before=%s\n", post); + + r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); + if (r < 0) + return r; + + fprintf(f, + "\n" + "[Mount]\n" + "What=%s\n" + "Where=%s\n", + what, where); + + if (fstype) + fprintf(f, "Type=%s\n", fstype); + + if (options) + fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); + else + fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", p); + + if (post) { + lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(p, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + } + + return 0; +} + +static bool path_is_busy(const char *where) { + int r; + + /* already a mountpoint; generators run during reload */ + r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + if (r > 0) + return false; + + /* the directory might not exist on a stateless system */ + if (r == -ENOENT) + return false; + + if (r < 0) + return true; + + /* not a mountpoint but it contains files */ + if (dir_is_empty(where) <= 0) + return true; + + return false; +} + +static int probe_and_add_mount( + const char *id, + const char *what, + const char *where, + bool rw, + const char *description, + const char *post) { + + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL; + int r; + + assert(id); + assert(what); + assert(where); + assert(description); + + if (path_is_busy(where)) { + log_debug("%s already populated, ignoring.", where); + return 0; + } + + /* Let's check the partition type here, so that we know + * whether to do LUKS magic. */ + + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober: %m"); + } + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) /* no result or uncertain */ + return 0; + else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); + + /* add_mount is OK with fstype being NULL. */ + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + + return add_mount( + id, + what, + where, + fstype, + rw, + NULL, + description, + post); +} + +static int add_swap(const char *path) { + _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + log_debug("Adding swap: %s", path); + + r = unit_name_from_path(path, ".swap", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + unit = strjoin(arg_dest, "/", name, NULL); + if (!unit) + return log_oom(); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=Swap Partition\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n\n" + "[Swap]\n" + "What=%s\n", + path); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +#ifdef ENABLE_EFI +static int add_automount( + const char *id, + const char *what, + const char *where, + const char *fstype, + bool rw, + const char *options, + const char *description, + usec_t timeout) { + + _cleanup_free_ char *unit = NULL, *lnk = NULL; + _cleanup_free_ char *opt, *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(id); + assert(where); + assert(description); + + if (options) + opt = strjoin(options, ",noauto", NULL); + else + opt = strdup("noauto"); + if (!opt) + return log_oom(); + + r = add_mount(id, + what, + where, + fstype, + rw, + opt, + description, + NULL); + if (r < 0) + return r; + + r = unit_name_from_path(where, ".automount", &unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", unit, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=%s\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n" + "[Automount]\n" + "Where=%s\n" + "TimeoutIdleSec=%lld\n", + description, + where, + (unsigned long long)timeout / USEC_PER_SEC); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", p); + + lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL); + if (!lnk) + return log_oom(); + mkdir_parents_label(lnk, 0755); + + if (symlink(p, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +static int add_boot(const char *what) { + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL, *uuid = NULL; + sd_id128_t id, type_id; + int r; + + assert(what); + + if (!is_efi_boot()) { + log_debug("Not an EFI boot, ignoring /boot."); + return 0; + } + + if (in_initrd()) { + log_debug("In initrd, ignoring /boot."); + return 0; + } + + if (detect_container() > 0) { + log_debug("In a container, ignoring /boot."); + 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; + } + + r = efi_loader_get_device_part_uuid(&id); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown."); + return 0; + } + + if (r < 0) + return log_error_errno(r, "Failed to read ESP partition UUID: %m"); + + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober: %m"); + } + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) /* no result or uncertain */ + return 0; + else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); + + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + if (!streq_ptr(fstype, "vfat")) { + log_debug("Partition for /boot is not a FAT filesystem, ignoring."); + return 0; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); + if (r != 0) { + log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring."); + return 0; + } + + if (sd_id128_from_string(uuid, &type_id) < 0) { + log_debug("Partition for /boot does not have a valid UUID, ignoring."); + return 0; + } + + if (!sd_id128_equal(type_id, id)) { + log_debug("Partition for /boot does not appear to be the partition we are booted from."); + return 0; + } + + r = add_automount("boot", + what, + "/boot", + "vfat", + true, + "umask=0077", + "EFI System Partition Automount", + 120 * USEC_PER_SEC); + + return r; +} +#else +static int add_boot(const char *what) { + return 0; +} +#endif + +static int enumerate_partitions(dev_t devnum) { + + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL; + struct udev_list_entry *first, *item; + struct udev_device *parent = NULL; + const char *name, *node, *pttype, *devtype; + int boot_nr = -1, home_nr = -1, srv_nr = -1; + bool home_rw = true, srv_rw = true; + blkid_partlist pl; + int r, k; + dev_t pn; + + udev = udev_new(); + if (!udev) + return log_oom(); + + d = udev_device_new_from_devnum(udev, 'b', devnum); + if (!d) + return log_oom(); + + name = udev_device_get_devnode(d); + if (!name) + name = udev_device_get_syspath(d); + if (!name) { + log_debug("Device %u:%u does not have a name, ignoring.", + major(devnum), minor(devnum)); + return 0; + } + + parent = udev_device_get_parent(d); + if (!parent) { + log_debug("%s: not a partitioned device, ignoring.", name); + return 0; + } + + /* Does it have a devtype? */ + devtype = udev_device_get_devtype(parent); + if (!devtype) { + log_debug("%s: parent doesn't have a device type, ignoring.", name); + return 0; + } + + /* Is this a disk or a partition? We only care for disks... */ + if (!streq(devtype, "disk")) { + log_debug("%s: parent isn't a raw disk, ignoring.", name); + return 0; + } + + /* Does it have a device node? */ + node = udev_device_get_devnode(parent); + if (!node) { + log_debug("%s: parent device does not have device node, ignoring.", name); + return 0; + } + + log_debug("%s: root device %s.", name, node); + + pn = udev_device_get_devnum(parent); + if (major(pn) == 0) + return 0; + + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "%s: failed to allocate prober: %m", node); + } + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == 1) + return 0; /* no results */ + else if (r == -2) { + log_warning("%s: probe gave ambiguous results, ignoring.", node); + return 0; + } else if (r != 0) + return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); + if (r != 0) { + if (errno == 0) + return 0; /* No partition table found. */ + + return log_error_errno(errno, "%s: failed to determine partition table type: %m", node); + } + + /* We only do this all for GPT... */ + if (!streq_ptr(pttype, "gpt")) { + log_debug("%s: not a GPT partition table, ignoring.", node); + return 0; + } + + errno = 0; + pl = blkid_probe_get_partitions(b); + if (!pl) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "%s: failed to list partitions: %m", node); + } + + e = udev_enumerate_new(udev); + if (!e) + return log_oom(); + + r = udev_enumerate_add_match_parent(e, parent); + if (r < 0) + return log_oom(); + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return log_oom(); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return log_error_errno(r, "%s: failed to enumerate partitions: %m", node); + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *q; + unsigned long long flags; + const char *stype, *subnode; + sd_id128_t type_id; + blkid_partition pp; + dev_t qn; + int nr; + + q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!q) + continue; + + qn = udev_device_get_devnum(q); + if (major(qn) == 0) + continue; + + if (qn == devnum) + continue; + + if (qn == pn) + continue; + + subnode = udev_device_get_devnode(q); + if (!subnode) + continue; + + pp = blkid_partlist_devno_to_partition(pl, qn); + if (!pp) + continue; + + nr = blkid_partition_get_partno(pp); + if (nr < 0) + continue; + + stype = blkid_partition_get_type_string(pp); + if (!stype) + continue; + + if (sd_id128_from_string(stype, &type_id) < 0) + continue; + + flags = blkid_partition_get_flags(pp); + + if (sd_id128_equal(type_id, GPT_SWAP)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + if (flags & GPT_FLAG_READ_ONLY) { + log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode); + continue; + } + + k = add_swap(subnode); + if (k < 0) + r = k; + + } else if (sd_id128_equal(type_id, GPT_ESP)) { + + /* We only care for the first /boot partition */ + if (boot && nr >= boot_nr) + continue; + + /* Note that we do not honour the "no-auto" + * flag for the ESP, as it is often unset, to + * hide it from Windows. */ + + boot_nr = nr; + + r = free_and_strdup(&boot, subnode); + if (r < 0) + return log_oom(); + + } else if (sd_id128_equal(type_id, GPT_HOME)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + /* We only care for the first /home partition */ + if (home && nr >= home_nr) + continue; + + home_nr = nr; + home_rw = !(flags & GPT_FLAG_READ_ONLY), + + r = free_and_strdup(&home, subnode); + if (r < 0) + return log_oom(); + + } else if (sd_id128_equal(type_id, GPT_SRV)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + /* We only care for the first /srv partition */ + if (srv && nr >= srv_nr) + continue; + + srv_nr = nr; + srv_rw = !(flags & GPT_FLAG_READ_ONLY), + + r = free_and_strdup(&srv, subnode); + if (r < 0) + return log_oom(); + } + } + + if (boot) { + k = add_boot(boot); + if (k < 0) + r = k; + } + + if (home) { + k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } + + if (srv) { + k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } + + return r; +} + +static int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +static int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) /* Don't try to support multiple backing block devices */ + goto fallback; + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + assert(key); + + if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value); + else + arg_enabled = r; + + } else if (streq(key, "root") && value) { + + /* Disable root disk logic if there's a root= value + * specified (unless it happens to be "gpt-auto") */ + + arg_root_enabled = streq(value, "gpt-auto"); + + } else if (streq(key, "rw") && !value) + arg_root_rw = true; + else if (streq(key, "ro") && !value) + arg_root_rw = false; + + return 0; +} + +static int add_root_mount(void) { + +#ifdef ENABLE_EFI + int r; + + if (!is_efi_boot()) { + log_debug("Not a EFI boot, not creating root mount."); + return 0; + } + + r = efi_loader_get_device_part_uuid(NULL); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown, exiting."); + return 0; + } else if (r < 0) + return log_error_errno(r, "Failed to read ESP partition UUID: %m"); + + /* OK, we have an ESP partition, this is fantastic, so let's + * wait for a root device to show up. A udev rule will create + * the link for us under the right name. */ + + if (in_initrd()) { + r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); + if (r < 0) + return 0; + } + + return add_mount( + "root", + "/dev/gpt-auto-root", + in_initrd() ? "/sysroot" : "/", + NULL, + arg_root_rw, + NULL, + "Root Partition", + in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); +#else + return 0; +#endif +} + +static int add_mounts(void) { + dev_t devno; + int r; + + r = get_block_device_harder("/", &devno); + if (r < 0) + return log_error_errno(r, "Failed to determine block device of root file system: %m"); + else if (r == 0) { + r = get_block_device_harder("/usr", &devno); + if (r < 0) + return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); + else if (r == 0) { + log_debug("Neither root nor /usr file system are on a (single) block device."); + return 0; + } + } + + return enumerate_partitions(devno); +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[3]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + if (detect_container() > 0) { + log_debug("In a container, exiting."); + return EXIT_SUCCESS; + } + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + if (!arg_enabled) { + log_debug("Disabled, exiting."); + return EXIT_SUCCESS; + } + + if (arg_root_enabled) + r = add_root_mount(); + + if (!in_initrd()) { + int k; + + k = add_mounts(); + if (k < 0) + r = k; + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml b/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml new file mode 100644 index 0000000000..e890c4dce2 --- /dev/null +++ b/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml @@ -0,0 +1,186 @@ + + + + + + + + systemd-gpt-auto-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-gpt-auto-generator + 8 + + + + systemd-gpt-auto-generator + Generator for automatically discovering + and mounting root, /home and + /srv partitions, as well as + discovering and enabling swap partitions, based on GPT + partition type GUIDs. + + + + /usr/lib/systemd/system-generators/systemd-gpt-auto-generator + + + + Description + + systemd-gpt-auto-generator is a unit + generator that automatically discovers root, + /home, /srv and swap + partitions and creates mount and swap units for them, based on the + partition type GUIDs of GUID partition tables (GPT). It implements + the Discoverable + Partitions Specification. Note that this generator has no + effect on non-GPT systems, or where the directories under the + mount points are already non-empty. Also, on systems where the + units are explicitly configured (for example, listed in + fstab5), + the units this generator creates are overridden, but additional + automatic dependencies might be created. + + This generator will only look for root partitions on the + same physical disk the EFI System Partition (ESP) is located on. + It will only look for the other partitions on the same physical + disk the root file system is located on. These partitions will not + be searched on systems where the root file system is distributed + on multiple disks, for example via btrfs RAID. + + systemd-gpt-auto-generator is useful + for centralizing file system configuration in the partition table + and making manual configuration in /etc/fstab + or suchlike unnecessary. + + This generator looks for the partitions based on their + partition type GUID. The following partition type GUIDs are + identified: + + + Partition Type GUIDs + + + + + + + Partition Type GUID + Name + Explanation + + + + + 44479540-f297-41b2-9af7-d131d5f0458a + Root Partition (x86) + On 32-bit x86 systems, the first x86 root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + Root Partition (x86-64) + On 64-bit x86 systems, the first x86-64 root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 69dad710-2ce4-4e3c-b16c-21a1d49abed3 + Root Partition (32-bit ARM) + On 32-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + b921b045-1df0-41c3-af44-4c6f280d3fae + Root Partition (64-bit ARM) + On 64-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 933ac7e1-2eb4-4f13-b844-0e14e2aef915 + Home Partition + The first home partition on the disk the root partition is located on is mounted to /home. + + + 3b8f8425-20e0-4f3b-907f-1a25a76f98e8 + Server Data Partition + The first server data partition on the disk the root partition is located on is mounted to /srv. + + + 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f + Swap + All swap partitions located on the disk the root partition is located on are enabled. + + + +
+ + The /home and /srv + partitions may be encrypted in LUKS format. In this case, a device + mapper device is set up under the names + /dev/mapper/home and + /dev/mapper/srv. Note that this might create + conflicts if the same partition is listed in + /etc/crypttab with a different device mapper + device name. + + Mount and automount units for the EFI System Partition (ESP), + mounting it to /boot, are generated on EFI + systems where the boot loader communicates the used ESP to the operating + system. Since this generator creates an automount unit, the mount will + only be activated on-demand, when accessed. On systems where + /boot is an explicitly configured mount + (for example, listed in + fstab5) + or where the /boot mount point is non-empty, no + mount units are generated. + + When using this generator in conjunction with btrfs file + systems, make sure to set the correct default subvolumes on them, + using btrfs subvolume set-default. + + systemd-gpt-auto-generator implements + systemd.generator7. +
+ + + See Also + + systemd1, + systemd.mount5, + systemd.swap5, + systemd-fstab-generator8, + systemd-cryptsetup@.service8, + cryptsetup8, + fstab5, + btrfs8 + + + +
diff --git a/src/systemd-initctl/Makefile b/src/systemd-initctl/Makefile new file mode 100644 index 0000000000..18d66993b7 --- /dev/null +++ b/src/systemd-initctl/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-initctl +systemd_initctl_SOURCES = \ + src/initctl/initctl.c + +systemd_initctl_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-initctl/initctl.c b/src/systemd-initctl/initctl.c new file mode 100644 index 0000000000..66b40ce6f8 --- /dev/null +++ b/src/systemd-initctl/initctl.c @@ -0,0 +1,428 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/list.h" +#include "basic/log.h" +#include "basic/special.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-util.h" +#include "shared/initreq.h" + +#define SERVER_FD_MAX 16 +#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) + +typedef struct Fifo Fifo; + +typedef struct Server { + int epoll_fd; + + LIST_HEAD(Fifo, fifos); + unsigned n_fifos; + + sd_bus *bus; + + bool quit; +} Server; + +struct Fifo { + Server *server; + + int fd; + + struct init_request buffer; + size_t bytes_read; + + LIST_FIELDS(Fifo, fifo); +}; + +static const char *translate_runlevel(int runlevel, bool *isolate) { + static const struct { + const int runlevel; + const char *special; + bool isolate; + } table[] = { + { '0', SPECIAL_POWEROFF_TARGET, false }, + { '1', SPECIAL_RESCUE_TARGET, true }, + { 's', SPECIAL_RESCUE_TARGET, true }, + { 'S', SPECIAL_RESCUE_TARGET, true }, + { '2', SPECIAL_MULTI_USER_TARGET, true }, + { '3', SPECIAL_MULTI_USER_TARGET, true }, + { '4', SPECIAL_MULTI_USER_TARGET, true }, + { '5', SPECIAL_GRAPHICAL_TARGET, true }, + { '6', SPECIAL_REBOOT_TARGET, false }, + }; + + unsigned i; + + assert(isolate); + + for (i = 0; i < ELEMENTSOF(table); i++) + if (table[i].runlevel == runlevel) { + *isolate = table[i].isolate; + if (runlevel == '6' && kexec_loaded()) + return SPECIAL_KEXEC_TARGET; + return table[i].special; + } + + return NULL; +} + +static void change_runlevel(Server *s, int runlevel) { + const char *target; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + const char *mode; + bool isolate = false; + int r; + + assert(s); + + target = translate_runlevel(runlevel, &isolate); + if (!target) { + log_warning("Got request for unknown runlevel %c, ignoring.", runlevel); + return; + } + + if (isolate) + mode = "isolate"; + else + mode = "replace-irreversibly"; + + log_debug("Running request %s/start/%s", target, mode); + + r = sd_bus_call_method( + s->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + &error, + NULL, + "ss", target, mode); + if (r < 0) { + log_error("Failed to change runlevel: %s", bus_error_message(&error, -r)); + return; + } +} + +static void request_process(Server *s, const struct init_request *req) { + assert(s); + assert(req); + + if (req->magic != INIT_MAGIC) { + log_error("Got initctl request with invalid magic. Ignoring."); + return; + } + + switch (req->cmd) { + + case INIT_CMD_RUNLVL: + if (!isprint(req->runlevel)) + log_error("Got invalid runlevel. Ignoring."); + else + switch (req->runlevel) { + + /* we are async anyway, so just use kill for reexec/reload */ + case 'u': + case 'U': + if (kill(1, SIGTERM) < 0) + log_error_errno(errno, "kill() failed: %m"); + + /* The bus connection will be + * terminated if PID 1 is reexecuted, + * hence let's just exit here, and + * rely on that we'll be restarted on + * the next request */ + s->quit = true; + break; + + case 'q': + case 'Q': + if (kill(1, SIGHUP) < 0) + log_error_errno(errno, "kill() failed: %m"); + break; + + default: + change_runlevel(s, req->runlevel); + } + return; + + case INIT_CMD_POWERFAIL: + case INIT_CMD_POWERFAILNOW: + case INIT_CMD_POWEROK: + log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!"); + return; + + case INIT_CMD_CHANGECONS: + log_warning("Received console change initctl request. This is not implemented in systemd."); + return; + + case INIT_CMD_SETENV: + case INIT_CMD_UNSETENV: + log_warning("Received environment initctl request. This is not implemented in systemd."); + return; + + default: + log_warning("Received unknown initctl request. Ignoring."); + return; + } +} + +static int fifo_process(Fifo *f) { + ssize_t l; + + assert(f); + + errno = EIO; + l = read(f->fd, + ((uint8_t*) &f->buffer) + f->bytes_read, + sizeof(f->buffer) - f->bytes_read); + if (l <= 0) { + if (errno == EAGAIN) + return 0; + + return log_warning_errno(errno, "Failed to read from fifo: %m"); + } + + f->bytes_read += l; + assert(f->bytes_read <= sizeof(f->buffer)); + + if (f->bytes_read == sizeof(f->buffer)) { + request_process(f->server, &f->buffer); + f->bytes_read = 0; + } + + return 0; +} + +static void fifo_free(Fifo *f) { + assert(f); + + if (f->server) { + assert(f->server->n_fifos > 0); + f->server->n_fifos--; + LIST_REMOVE(fifo, f->server->fifos, f); + } + + if (f->fd >= 0) { + if (f->server) + epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL); + + safe_close(f->fd); + } + + free(f); +} + +static void server_done(Server *s) { + assert(s); + + while (s->fifos) + fifo_free(s->fifos); + + safe_close(s->epoll_fd); + + if (s->bus) { + sd_bus_flush(s->bus); + sd_bus_unref(s->bus); + } +} + +static int server_init(Server *s, unsigned n_sockets) { + int r; + unsigned i; + + assert(s); + assert(n_sockets > 0); + + zero(*s); + + s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (s->epoll_fd < 0) { + r = log_error_errno(errno, + "Failed to create epoll object: %m"); + goto fail; + } + + for (i = 0; i < n_sockets; i++) { + struct epoll_event ev; + Fifo *f; + int fd; + + fd = SD_LISTEN_FDS_START+i; + + r = sd_is_fifo(fd, NULL); + if (r < 0) { + log_error_errno(r, "Failed to determine file descriptor type: %m"); + goto fail; + } + + if (!r) { + log_error("Wrong file descriptor type."); + r = -EINVAL; + goto fail; + } + + f = new0(Fifo, 1); + if (!f) { + r = -ENOMEM; + log_error_errno(errno, "Failed to create fifo object: %m"); + goto fail; + } + + f->fd = -1; + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = f; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + r = -errno; + fifo_free(f); + log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); + goto fail; + } + + f->fd = fd; + LIST_PREPEND(fifo, s->fifos, f); + f->server = s; + s->n_fifos++; + } + + r = bus_connect_system_systemd(&s->bus); + if (r < 0) { + log_error_errno(r, "Failed to get D-Bus connection: %m"); + r = -EIO; + goto fail; + } + + return 0; + +fail: + server_done(s); + + return r; +} + +static int process_event(Server *s, struct epoll_event *ev) { + int r; + Fifo *f; + + assert(s); + + if (!(ev->events & EPOLLIN)) { + log_info("Got invalid event from epoll. (3)"); + return -EIO; + } + + f = (Fifo*) ev->data.ptr; + r = fifo_process(f); + if (r < 0) { + log_info_errno(r, "Got error on fifo: %m"); + fifo_free(f); + return r; + } + + return 0; +} + +int main(int argc, char *argv[]) { + Server server; + int r = EXIT_FAILURE, n; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return EXIT_FAILURE; + } + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + n = sd_listen_fds(true); + if (n < 0) { + log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); + return EXIT_FAILURE; + } + + if (n <= 0 || n > SERVER_FD_MAX) { + log_error("No or too many file descriptors passed."); + return EXIT_FAILURE; + } + + if (server_init(&server, (unsigned) n) < 0) + return EXIT_FAILURE; + + log_debug("systemd-initctl running as pid "PID_FMT, getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + while (!server.quit) { + struct epoll_event event; + int k; + + k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC); + if (k < 0) { + if (errno == EINTR) + continue; + log_error_errno(errno, "epoll_wait() failed: %m"); + goto fail; + } + + if (k <= 0) + break; + + if (process_event(&server, &event) < 0) + goto fail; + } + + r = EXIT_SUCCESS; + + log_debug("systemd-initctl stopped as pid "PID_FMT, getpid()); + +fail: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + server_done(&server); + + return r; +} diff --git a/src/systemd-initctl/systemd-initctl.service.in b/src/systemd-initctl/systemd-initctl.service.in new file mode 100644 index 0000000000..27e663c8dc --- /dev/null +++ b/src/systemd-initctl/systemd-initctl.service.in @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=/dev/initctl Compatibility Daemon +Documentation=man:systemd-initctl.service(8) +DefaultDependencies=no + +[Service] +ExecStart=@rootlibexecdir@/systemd-initctl +NotifyAccess=all diff --git a/src/systemd-initctl/systemd-initctl.service.xml b/src/systemd-initctl/systemd-initctl.service.xml new file mode 100644 index 0000000000..5c7f9a4a16 --- /dev/null +++ b/src/systemd-initctl/systemd-initctl.service.xml @@ -0,0 +1,76 @@ + + + + + + + + + systemd-initctl.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-initctl.service + 8 + + + + systemd-initctl.service + systemd-initctl.socket + systemd-initctl + /dev/initctl compatibility + + + + systemd-initctl.service + systemd-initctl.socket + /usr/lib/systemd/systemd-initctl + + + + Description + + systemd-initctl is a system service + that implements compatibility with the + /dev/initctl FIFO file system object, as + implemented by the SysV init system. + systemd-initctl is automatically activated on + request and terminates itself when it is unused. + + + + See Also + + systemd1 + + + + diff --git a/src/systemd-initctl/systemd-initctl.socket b/src/systemd-initctl/systemd-initctl.socket new file mode 100644 index 0000000000..f628c2e867 --- /dev/null +++ b/src/systemd-initctl/systemd-initctl.socket @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=/dev/initctl Compatibility Named Pipe +Documentation=man:systemd-initctl.service(8) +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenFIFO=/run/systemd/initctl/fifo +Symlinks=/dev/initctl +SocketMode=0600 diff --git a/src/systemd-machine-id-setup/Makefile b/src/systemd-machine-id-setup/Makefile new file mode 100644 index 0000000000..4cbba418ff --- /dev/null +++ b/src/systemd-machine-id-setup/Makefile @@ -0,0 +1,38 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemd-machine-id-setup +systemd_machine_id_setup_SOURCES = \ + src/machine-id-setup/machine-id-setup-main.c \ + src/core/machine-id-setup.c \ + src/core/machine-id-setup.h + +systemd_machine_id_setup_LDADD = \ + libsystemd-shared.la + +SYSINIT_TARGET_WANTS += \ + systemd-machine-id-commit.service + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-machine-id-setup/machine-id-setup-main.c b/src/systemd-machine-id-setup/machine-id-setup-main.c new file mode 100644 index 0000000000..9cc90b6efd --- /dev/null +++ b/src/systemd-machine-id-setup/machine-id-setup-main.c @@ -0,0 +1,143 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/util.h" + +#include "machine-id-setup.h" + +static char *arg_root = NULL; +static bool arg_commit = false; +static bool arg_print = 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" + " --commit Commit transient ID\n" + " --print Print used machine ID\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + ARG_COMMIT, + ARG_PRINT, + }; + + 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 }, + { "print", no_argument, NULL, ARG_PRINT }, + {} + }; + + int c, r; + + 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: + return version(); + + case ARG_ROOT: + r = parse_path_argument_and_warn(optarg, true, &arg_root); + if (r < 0) + return r; + break; + + case ARG_COMMIT: + arg_commit = true; + break; + + case ARG_PRINT: + arg_print = true; + 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[]) { + char buf[SD_ID128_STRING_MAX]; + sd_id128_t id; + int r; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_commit) { + r = machine_id_commit(arg_root); + if (r < 0) + goto finish; + + r = sd_id128_get_machine(&id); + if (r < 0) { + log_error_errno(r, "Failed to read machine ID back: %m"); + goto finish; + } + } else { + r = machine_id_setup(arg_root, SD_ID128_NULL, &id); + if (r < 0) + goto finish; + } + + if (arg_print) + puts(sd_id128_to_string(id, buf)); + +finish: + free(arg_root); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-machine-id-setup/machine-id-setup.c b/src/systemd-machine-id-setup/machine-id-setup.c new file mode 120000 index 0000000000..78f80e2b73 --- /dev/null +++ b/src/systemd-machine-id-setup/machine-id-setup.c @@ -0,0 +1 @@ +../grp-system/libcore/machine-id-setup.c \ No newline at end of file diff --git a/src/systemd-machine-id-setup/machine-id-setup.h b/src/systemd-machine-id-setup/machine-id-setup.h new file mode 120000 index 0000000000..d2659724ce --- /dev/null +++ b/src/systemd-machine-id-setup/machine-id-setup.h @@ -0,0 +1 @@ +../grp-system/libcore/machine-id-setup.h \ No newline at end of file diff --git a/src/systemd-machine-id-setup/systemd-machine-id-commit.service.xml b/src/systemd-machine-id-setup/systemd-machine-id-commit.service.xml new file mode 100644 index 0000000000..39da1922cc --- /dev/null +++ b/src/systemd-machine-id-setup/systemd-machine-id-commit.service.xml @@ -0,0 +1,95 @@ + + + + + + + + systemd-machine-id-commit.service + systemd + + + + Developer + Didier + Roche + didrocks@ubuntu.com + + + + + + systemd-machine-id-commit.service + 8 + + + + systemd-machine-id-commit.service + Commit a transient machine ID to disk + + + + systemd-machine-id-commit.service + + + + Description + + systemd-machine-id-commit.service is an + early boot service responsible for committing transient + /etc/machine-id files to a writable disk file + system. See + machine-id5 + for more information about machine IDs. + + This service is started after + local-fs.target in case + /etc/machine-id is a mount point of its own + (usually from a memory file system such as + tmpfs) and /etc is writable. The service will + invoke systemd-machine-id-setup --commit, which + writes the current transient machine ID to disk and unmount the + /etc/machine-id file in a race-free manner to + ensure that file is always valid and accessible for other + processes. See + systemd-machine-id-setup1 + for details. + + The main use case of this service are systems where + /etc/machine-id is read-only and initially + not initialized. In this case, the system manager will generate a + transient machine ID file on a memory file system, and mount it + over /etc/machine-id, during the early boot + phase. This service is then invoked in a later boot phase, as soon + as /etc has been remounted writable and the + ID may thus be committed to disk to make it permanent. + + + + See Also + + systemd1, + systemd-machine-id-setup1, + machine-id5, + systemd-firstboot1 + + + + diff --git a/src/systemd-machine-id-setup/systemd-machine-id-setup.completion.zsh b/src/systemd-machine-id-setup/systemd-machine-id-setup.completion.zsh new file mode 100644 index 0000000000..d575649394 --- /dev/null +++ b/src/systemd-machine-id-setup/systemd-machine-id-setup.completion.zsh @@ -0,0 +1,8 @@ +#compdef systemd-machine-id-setup + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Show this help]' \ + '--version[Show package version]' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/systemd-machine-id-setup/systemd-machine-id-setup.xml b/src/systemd-machine-id-setup/systemd-machine-id-setup.xml new file mode 100644 index 0000000000..749987a937 --- /dev/null +++ b/src/systemd-machine-id-setup/systemd-machine-id-setup.xml @@ -0,0 +1,184 @@ + + + + + + + + + systemd-machine-id-setup + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + Developer + Didier + Roche + didrocks@ubuntu.com + + + + + + systemd-machine-id-setup + 1 + + + + systemd-machine-id-setup + Initialize the machine ID in /etc/machine-id + + + + + systemd-machine-id-setup + + + + + Description + + systemd-machine-id-setup may be used by + system installer tools to initialize the machine ID stored in + /etc/machine-id at install time, with a + provisioned or randomly generated ID. See + machine-id5 + for more information about this file. + + If the tool is invoked without the + switch, /etc/machine-id is initialized with a + valid, new machined ID if it is missing or empty. The new machine + ID will be acquired in the following fashion: + + + If a valid D-Bus machine ID is already + configured for the system, the D-Bus machine ID is copied and + used to initialize the machine ID in + /etc/machine-id. + + If run inside a KVM virtual machine and a UUID + is was configured (via the + option), this UUID is used to initialize the machine ID. The + caller must ensure that the UUID passed is sufficiently unique + and is different for every booted instance of the + VM. + + Similarly, if run inside a Linux container + environment and a UUID is configured for the container, this is + used to initialize the machine ID. For details, see the + documentation of the Container + Interface. + + Otherwise, a new ID is randomly + generated. + + + The switch may be used to commit a + transient machined ID to disk, making it persistent. For details, + see below. + + Use + systemd-firstboot1 + to initialize the machine ID on mounted (but not booted) system + images. + + + + + Options + + The following options are understood: + + + + + + Takes a directory path as argument. All paths + operated will be prefixed with the given alternate + root path, including the path for + /etc/machine-id itself. + + + + + Commit a transient machine ID to disk. This + command may be used to convert a transient machine ID into a + persistent one. A transient machine ID file is one that was + bind mounted from a memory file system (usually + tmpfs) to + /etc/machine-id during the early phase of + the boot process. This may happen because + /etc is initially read-only and was + missing a valid machine ID file at that point. + + This command will execute no operation if + /etc/machine-id is not mounted from a + memory file system, or if /etc is + read-only. The command will write the current transient + machine ID to disk and unmount the + /etc/machine-id mount point in a + race-free manner to ensure that this file is always valid and + accessible for other processes. + + This command is primarily used by the + systemd-machine-id-commit.service8 + early boot service. + + + + + + Print the machine ID generated or commited after the operation is complete. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code + otherwise. + + + + See Also + + systemd1, + machine-id5, + systemd-machine-id-commit.service8, + dbus-uuidgen1, + systemd-firstboot1 + + + + diff --git a/src/systemd-nspawn/.gitignore b/src/systemd-nspawn/.gitignore new file mode 100644 index 0000000000..85c81fff24 --- /dev/null +++ b/src/systemd-nspawn/.gitignore @@ -0,0 +1 @@ +/nspawn-gperf.c diff --git a/src/systemd-nspawn/Makefile b/src/systemd-nspawn/Makefile new file mode 100644 index 0000000000..a173fd204a --- /dev/null +++ b/src/systemd-nspawn/Makefile @@ -0,0 +1,93 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-nspawn +systemd_nspawn_SOURCES = \ + src/nspawn/nspawn.c \ + src/nspawn/nspawn-settings.c \ + src/nspawn/nspawn-settings.h \ + src/nspawn/nspawn-mount.c \ + src/nspawn/nspawn-mount.h \ + src/nspawn/nspawn-network.c \ + src/nspawn/nspawn-network.h \ + src/nspawn/nspawn-expose-ports.c \ + src/nspawn/nspawn-expose-ports.h \ + src/nspawn/nspawn-cgroup.c \ + src/nspawn/nspawn-cgroup.h \ + src/nspawn/nspawn-seccomp.c \ + src/nspawn/nspawn-seccomp.h \ + src/nspawn/nspawn-register.c \ + src/nspawn/nspawn-register.h \ + src/nspawn/nspawn-setuid.c \ + src/nspawn/nspawn-setuid.h \ + src/nspawn/nspawn-stub-pid1.c \ + src/nspawn/nspawn-stub-pid1.h \ + src/nspawn/nspawn-patch-uid.c \ + src/nspawn/nspawn-patch-uid.h \ + src/core/mount-setup.c \ + src/core/mount-setup.h \ + src/core/loopback-setup.c \ + src/core/loopback-setup.h + +nodist_systemd_nspawn_SOURCES = \ + src/nspawn/nspawn-gperf.c + +gperf_gperf_sources += \ + src/nspawn/nspawn-gperf.gperf + +systemd_nspawn_CFLAGS = \ + $(ACL_CFLAGS) \ + $(BLKID_CFLAGS) \ + $(SECCOMP_CFLAGS) \ + $(SELINUX_CFLAGS) + +systemd_nspawn_LDADD = \ + libsystemd-shared.la \ + $(ACL_LIBS) \ + $(BLKID_LIBS) \ + $(SECCOMP_LIBS) \ + $(SELINUX_LIBS) + +ifneq ($(HAVE_LIBIPTC),) +systemd_nspawn_LDADD += \ + libfirewall.la +endif # HAVE_LIBIPTC + +test_patch_uid_SOURCES = \ + src/nspawn/nspawn-patch-uid.c \ + src/nspawn/nspawn-patch-uid.h \ + src/nspawn/test-patch-uid.c + +test_patch_uid_CFLAGS = \ + $(ACL_CFLAGS) + +test_patch_uid_LDADD = \ + libsystemd-shared.la \ + $(ACL_LIBS) + +manual_tests += \ + test-patch-uid + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-nspawn/loopback-setup.c b/src/systemd-nspawn/loopback-setup.c new file mode 120000 index 0000000000..da633f4936 --- /dev/null +++ b/src/systemd-nspawn/loopback-setup.c @@ -0,0 +1 @@ +../grp-system/libcore/loopback-setup.c \ No newline at end of file diff --git a/src/systemd-nspawn/loopback-setup.h b/src/systemd-nspawn/loopback-setup.h new file mode 120000 index 0000000000..dc051ab1bd --- /dev/null +++ b/src/systemd-nspawn/loopback-setup.h @@ -0,0 +1 @@ +../grp-system/libcore/loopback-setup.h \ No newline at end of file diff --git a/src/systemd-nspawn/machine-id-setup.c b/src/systemd-nspawn/machine-id-setup.c new file mode 120000 index 0000000000..78f80e2b73 --- /dev/null +++ b/src/systemd-nspawn/machine-id-setup.c @@ -0,0 +1 @@ +../grp-system/libcore/machine-id-setup.c \ No newline at end of file diff --git a/src/systemd-nspawn/machine-id-setup.h b/src/systemd-nspawn/machine-id-setup.h new file mode 120000 index 0000000000..d2659724ce --- /dev/null +++ b/src/systemd-nspawn/machine-id-setup.h @@ -0,0 +1 @@ +../grp-system/libcore/machine-id-setup.h \ No newline at end of file diff --git a/src/systemd-nspawn/mount-setup.c b/src/systemd-nspawn/mount-setup.c new file mode 120000 index 0000000000..67cb74c218 --- /dev/null +++ b/src/systemd-nspawn/mount-setup.c @@ -0,0 +1 @@ +../grp-system/libcore/mount-setup.c \ No newline at end of file diff --git a/src/systemd-nspawn/mount-setup.h b/src/systemd-nspawn/mount-setup.h new file mode 120000 index 0000000000..bae54ba700 --- /dev/null +++ b/src/systemd-nspawn/mount-setup.h @@ -0,0 +1 @@ +../grp-system/libcore/mount-setup.h \ No newline at end of file diff --git a/src/systemd-nspawn/nspawn-cgroup.c b/src/systemd-nspawn/nspawn-cgroup.c new file mode 100644 index 0000000000..9fa46fadbd --- /dev/null +++ b/src/systemd-nspawn/nspawn-cgroup.c @@ -0,0 +1,163 @@ +/*** + 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 . +***/ + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/mkdir.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "nspawn-cgroup.h" + +int chown_cgroup(pid_t pid, uid_t uid_shift) { + _cleanup_free_ char *path = NULL, *fs = NULL; + _cleanup_close_ int fd = -1; + const char *fn; + int r; + + r = cg_pid_get_path(NULL, pid, &path); + if (r < 0) + return log_error_errno(r, "Failed to get container cgroup path: %m"); + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); + if (r < 0) + return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); + + fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", fs); + + FOREACH_STRING(fn, + ".", + "tasks", + "notify_on_release", + "cgroup.procs", + "cgroup.events", + "cgroup.clone_children", + "cgroup.controllers", + "cgroup.subtree_control") + if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0) + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to chown() cgroup file %s, ignoring: %m", fn); + + return 0; +} + +int sync_cgroup(pid_t pid, bool unified_requested) { + _cleanup_free_ char *cgroup = NULL; + char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1]; + bool undo_mount = false; + const char *fn; + int unified, r; + + unified = cg_unified(); + if (unified < 0) + return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); + + if ((unified > 0) == unified_requested) + return 0; + + /* When the host uses the legacy cgroup setup, but the + * container shall use the unified hierarchy, let's make sure + * we copy the path from the name=systemd hierarchy into the + * unified hierarchy. Similar for the reverse situation. */ + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); + if (r < 0) + return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid); + + /* In order to access the unified hierarchy we need to mount it */ + if (!mkdtemp(tree)) + return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m"); + + if (unified) + r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); + else + r = mount("cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); + if (r < 0) { + r = log_error_errno(errno, "Failed to mount unified hierarchy: %m"); + goto finish; + } + + undo_mount = true; + + fn = strjoina(tree, cgroup, "/cgroup.procs"); + (void) mkdir_parents(fn, 0755); + + sprintf(pid_string, PID_FMT, pid); + r = write_string_file(fn, pid_string, 0); + if (r < 0) + log_error_errno(r, "Failed to move process: %m"); + +finish: + if (undo_mount) + (void) umount(tree); + + (void) rmdir(tree); + return r; +} + +int create_subcgroup(pid_t pid, bool unified_requested) { + _cleanup_free_ char *cgroup = NULL; + const char *child; + int unified, r; + CGroupMask supported; + + /* In the unified hierarchy inner nodes may only contain + * subgroups, but not processes. Hence, if we running in the + * unified hierarchy and the container does the same, and we + * did not create a scope unit for the container move us and + * the container into two separate subcgroups. */ + + if (!unified_requested) + return 0; + + unified = cg_unified(); + if (unified < 0) + return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); + if (unified == 0) + return 0; + + r = cg_mask_supported(&supported); + if (r < 0) + return log_error_errno(r, "Failed to determine supported controllers: %m"); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); + if (r < 0) + return log_error_errno(r, "Failed to get our control group: %m"); + + child = strjoina(cgroup, "/payload"); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, pid); + if (r < 0) + return log_error_errno(r, "Failed to create %s subcgroup: %m", child); + + child = strjoina(cgroup, "/supervisor"); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, 0); + if (r < 0) + return log_error_errno(r, "Failed to create %s subcgroup: %m", child); + + /* Try to enable as many controllers as possible for the new payload. */ + (void) cg_enable_everywhere(supported, supported, cgroup); + return 0; +} diff --git a/src/systemd-nspawn/nspawn-cgroup.h b/src/systemd-nspawn/nspawn-cgroup.h new file mode 100644 index 0000000000..1ff35a299a --- /dev/null +++ b/src/systemd-nspawn/nspawn-cgroup.h @@ -0,0 +1,27 @@ +#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 . +***/ + +#include +#include + +int chown_cgroup(pid_t pid, uid_t uid_shift); +int sync_cgroup(pid_t pid, bool unified_requested); +int create_subcgroup(pid_t pid, bool unified_requested); diff --git a/src/systemd-nspawn/nspawn-expose-ports.c b/src/systemd-nspawn/nspawn-expose-ports.c new file mode 100644 index 0000000000..6c9dbf4508 --- /dev/null +++ b/src/systemd-nspawn/nspawn-expose-ports.c @@ -0,0 +1,245 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/parse-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "firewall-util.h" +#include "sd-netlink/local-addresses.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" + +#include "nspawn-expose-ports.h" + +int expose_port_parse(ExposePort **l, const char *s) { + + const char *split, *e; + uint16_t container_port, host_port; + int protocol; + ExposePort *p; + int r; + + assert(l); + assert(s); + + if ((e = startswith(s, "tcp:"))) + protocol = IPPROTO_TCP; + else if ((e = startswith(s, "udp:"))) + protocol = IPPROTO_UDP; + else { + e = s; + protocol = IPPROTO_TCP; + } + + split = strchr(e, ':'); + if (split) { + char v[split - e + 1]; + + memcpy(v, e, split - e); + v[split - e] = 0; + + r = safe_atou16(v, &host_port); + if (r < 0 || host_port <= 0) + return -EINVAL; + + r = safe_atou16(split + 1, &container_port); + } else { + r = safe_atou16(e, &container_port); + host_port = container_port; + } + + if (r < 0 || container_port <= 0) + return -EINVAL; + + LIST_FOREACH(ports, p, *l) + if (p->protocol == protocol && p->host_port == host_port) + return -EEXIST; + + p = new(ExposePort, 1); + if (!p) + return -ENOMEM; + + p->protocol = protocol; + p->host_port = host_port; + p->container_port = container_port; + + LIST_PREPEND(ports, *l, p); + + return 0; +} + +void expose_port_free_all(ExposePort *p) { + + while (p) { + ExposePort *q = p; + LIST_REMOVE(ports, p, q); + free(q); + } +} + +int expose_port_flush(ExposePort* l, union in_addr_union *exposed) { + ExposePort *p; + int r, af = AF_INET; + + assert(exposed); + + if (!l) + return 0; + + if (in_addr_is_null(af, exposed)) + return 0; + + log_debug("Lost IP address."); + + LIST_FOREACH(ports, p, l) { + r = fw_add_local_dnat(false, + af, + p->protocol, + NULL, + NULL, 0, + NULL, 0, + p->host_port, + exposed, + p->container_port, + NULL); + if (r < 0) + log_warning_errno(r, "Failed to modify firewall: %m"); + } + + *exposed = IN_ADDR_NULL; + return 0; +} + +int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) { + _cleanup_free_ struct local_address *addresses = NULL; + _cleanup_free_ char *pretty = NULL; + union in_addr_union new_exposed; + ExposePort *p; + bool add; + int af = AF_INET, r; + + assert(exposed); + + /* Invoked each time an address is added or removed inside the + * container */ + + if (!l) + return 0; + + r = local_addresses(rtnl, 0, af, &addresses); + if (r < 0) + return log_error_errno(r, "Failed to enumerate local addresses: %m"); + + add = r > 0 && + addresses[0].family == af && + addresses[0].scope < RT_SCOPE_LINK; + + if (!add) + return expose_port_flush(l, exposed); + + new_exposed = addresses[0].address; + if (in_addr_equal(af, exposed, &new_exposed)) + return 0; + + in_addr_to_string(af, &new_exposed, &pretty); + log_debug("New container IP is %s.", strna(pretty)); + + LIST_FOREACH(ports, p, l) { + + r = fw_add_local_dnat(true, + af, + p->protocol, + NULL, + NULL, 0, + NULL, 0, + p->host_port, + &new_exposed, + p->container_port, + in_addr_is_null(af, exposed) ? NULL : exposed); + if (r < 0) + log_warning_errno(r, "Failed to modify firewall: %m"); + } + + *exposed = new_exposed; + return 0; +} + +int expose_port_send_rtnl(int send_fd) { + _cleanup_close_ int fd = -1; + int r; + + assert(send_fd >= 0); + + fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate container netlink: %m"); + + /* 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, 0); + if (r < 0) + return log_error_errno(r, "Failed to send netlink fd: %m"); + + return 0; +} + +int expose_port_watch_rtnl( + sd_event *event, + int recv_fd, + sd_netlink_message_handler_t handler, + union in_addr_union *exposed, + sd_netlink **ret) { + _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, 0); + if (fd < 0) + return log_error_errno(fd, "Failed to recv netlink fd: %m"); + + r = sd_netlink_open_fd(&rtnl, fd); + if (r < 0) { + safe_close(fd); + return log_error_errno(r, "Failed to create rtnl object: %m"); + } + + r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m"); + + r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m"); + + r = sd_netlink_attach_event(rtnl, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to add to even loop: %m"); + + *ret = rtnl; + rtnl = NULL; + + return 0; +} diff --git a/src/systemd-nspawn/nspawn-expose-ports.h b/src/systemd-nspawn/nspawn-expose-ports.h new file mode 100644 index 0000000000..2c0a5ecc88 --- /dev/null +++ b/src/systemd-nspawn/nspawn-expose-ports.h @@ -0,0 +1,44 @@ +#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 . +***/ + +#include + +#include + +#include "basic/in-addr-util.h" +#include "basic/list.h" +#include "sd-netlink/sd-netlink.h" + +typedef struct ExposePort { + int protocol; + uint16_t host_port; + uint16_t container_port; + LIST_FIELDS(struct ExposePort, ports); +} ExposePort; + +void expose_port_free_all(ExposePort *p); +int expose_port_parse(ExposePort **l, const char *s); + +int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret); +int expose_port_send_rtnl(int send_fd); + +int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed); +int expose_port_flush(ExposePort* l, union in_addr_union *exposed); diff --git a/src/systemd-nspawn/nspawn-gperf.gperf b/src/systemd-nspawn/nspawn-gperf.gperf new file mode 100644 index 0000000000..332063e19b --- /dev/null +++ b/src/systemd-nspawn/nspawn-gperf.gperf @@ -0,0 +1,47 @@ +%{ +#include + +#include "shared/conf-parser.h" + +#include "nspawn-expose-ports.h" +#include "nspawn-settings.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name nspawn_gperf_hash +%define lookup-function-name nspawn_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Exec.Boot, config_parse_boot, 0, 0 +Exec.ProcessTwo, config_parse_pid2, 0, 0 +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) +Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PrivateUsers, config_parse_private_users, 0, 0 +Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) +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 +Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) +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_ifname, 0, offsetof(Settings, network_bridge) +Network.Zone, config_parse_network_zone, 0, 0 +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/systemd-nspawn/nspawn-mount.c b/src/systemd-nspawn/nspawn-mount.c new file mode 100644 index 0000000000..ea1d591415 --- /dev/null +++ b/src/systemd-nspawn/nspawn-mount.c @@ -0,0 +1,946 @@ +/*** + 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 . +***/ + +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/escape.h" +#include "basic/fs-util.h" +#include "basic/label.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/set.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "nspawn-mount.h" + +CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) { + CustomMount *c, *ret; + + assert(l); + assert(n); + assert(t >= 0); + assert(t < _CUSTOM_MOUNT_TYPE_MAX); + + c = realloc(*l, (*n + 1) * sizeof(CustomMount)); + if (!c) + return NULL; + + *l = c; + ret = *l + *n; + (*n)++; + + *ret = (CustomMount) { .type = t }; + + return ret; +} + +void custom_mount_free_all(CustomMount *l, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) { + CustomMount *m = l + i; + + free(m->source); + free(m->destination); + free(m->options); + + if (m->work_dir) { + (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL); + free(m->work_dir); + } + + strv_free(m->lower); + } + + free(l); +} + +int custom_mount_compare(const void *a, const void *b) { + const CustomMount *x = a, *y = b; + int r; + + r = path_compare(x->destination, y->destination); + if (r != 0) + return r; + + if (x->type < y->type) + return -1; + if (x->type > y->type) + return 1; + + return 0; +} + +int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) { + _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; + const char *p = s; + CustomMount *m; + int r; + + assert(l); + assert(n); + + r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + if (r == 1) { + destination = strdup(source); + if (!destination) + return -ENOMEM; + } + + if (r == 2 && !isempty(p)) { + opts = strdup(p); + if (!opts) + return -ENOMEM; + } + + if (!path_is_absolute(source)) + return -EINVAL; + + if (!path_is_absolute(destination)) + return -EINVAL; + + m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); + if (!m) + return log_oom(); + + m->source = source; + m->destination = destination; + m->read_only = read_only; + m->options = opts; + + source = destination = opts = NULL; + return 0; +} + +int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) { + _cleanup_free_ char *path = NULL, *opts = NULL; + const char *p = s; + CustomMount *m; + int r; + + assert(l); + assert(n); + assert(s); + + r = extract_first_word(&p, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + if (isempty(p)) + opts = strdup("mode=0755"); + else + opts = strdup(p); + if (!opts) + return -ENOMEM; + + if (!path_is_absolute(path)) + return -EINVAL; + + m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS); + if (!m) + return -ENOMEM; + + m->destination = path; + m->options = opts; + + path = opts = NULL; + return 0; +} + +static int tmpfs_patch_options( + const char *options, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context, + char **ret) { + + char *buf = NULL; + + if (userns && uid_shift != 0) { + assert(uid_shift != UID_INVALID); + + if (options) + (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, uid_shift, uid_shift); + else + (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, uid_shift, uid_shift); + if (!buf) + return -ENOMEM; + + options = buf; + } + +#ifdef HAVE_SELINUX + if (selinux_apifs_context) { + char *t; + + if (options) + t = strjoin(options, ",context=\"", selinux_apifs_context, "\"", NULL); + else + t = strjoin("context=\"", selinux_apifs_context, "\"", NULL); + if (!t) { + free(buf); + return -ENOMEM; + } + + free(buf); + buf = t; + } +#endif + + *ret = buf; + 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 use_userns, bool in_userns, + bool use_netns, + uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + bool fatal; + bool in_userns; + bool use_netns; + } MountPoint; + + static const MountPoint mount_table[] = { + { "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 ...*/ + { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, true, true, true }, /* (except for this) */ + { 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, 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 + }; + + unsigned k; + int r; + + for (k = 0; k < ELEMENTSOF(mount_table); k++) { + _cleanup_free_ char *where = NULL, *options = NULL; + const char *o; + + 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); + if (!where) + return log_oom(); + + r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); + + /* Skip this entry if it is not a remount. */ + if (mount_table[k].what && r > 0) + continue; + + r = mkdir_p(where, 0755); + if (r < 0) { + if (mount_table[k].fatal) + return log_error_errno(r, "Failed to create directory %s: %m", where); + + log_debug_errno(r, "Failed to create directory %s: %m", where); + continue; + } + + o = mount_table[k].options; + if (streq_ptr(mount_table[k].type, "tmpfs")) { + r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, selinux_apifs_context, &options); + if (r < 0) + return log_oom(); + if (r > 0) + o = options; + } + + if (mount(mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + o) < 0) { + + if (mount_table[k].fatal) + return log_error_errno(errno, "mount(%s) failed: %m", where); + + log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where); + } + } + + return 0; +} + +static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) { + const char *p = options; + unsigned long flags = *mount_flags; + char *opts = NULL; + + assert(options); + + for (;;) { + _cleanup_free_ char *word = NULL; + int r = extract_first_word(&p, &word, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to extract mount option: %m"); + if (r == 0) + break; + + if (streq(word, "rbind")) + flags |= MS_REC; + else if (streq(word, "norbind")) + flags &= ~MS_REC; + else { + log_error("Invalid bind mount option: %s", word); + return -EINVAL; + } + } + + *mount_flags = flags; + /* in the future mount_opts will hold string options for mount(2) */ + *mount_opts = opts; + + return 0; +} + +static int mount_bind(const char *dest, CustomMount *m) { + struct stat source_st, dest_st; + const char *where; + unsigned long mount_flags = MS_BIND | MS_REC; + _cleanup_free_ char *mount_opts = NULL; + int r; + + assert(m); + + if (m->options) { + r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts); + if (r < 0) + return r; + } + + if (stat(m->source, &source_st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", m->source); + + where = prefix_roota(dest, m->destination); + + if (stat(where, &dest_st) >= 0) { + if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount directory %s on file %s.", m->source, where); + return -EINVAL; + } + + if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount file %s on directory %s.", m->source, where); + return -EINVAL; + } + + } else if (errno == ENOENT) { + r = mkdir_parents_label(where, 0755); + if (r < 0) + return log_error_errno(r, "Failed to make parents of %s: %m", where); + + /* Create the mount point. Any non-directory file can be + * mounted on any non-directory file (regular, fifo, socket, + * char, block). + */ + if (S_ISDIR(source_st.st_mode)) + r = mkdir_label(where, 0755); + else + r = touch(where); + if (r < 0) + return log_error_errno(r, "Failed to create mount point %s: %m", where); + + } else { + return log_error_errno(errno, "Failed to stat %s: %m", where); + } + + if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) + return log_error_errno(errno, "mount(%s) failed: %m", where); + + if (m->read_only) { + r = bind_remount_recursive(where, true); + if (r < 0) + return log_error_errno(r, "Read-only bind mount failed: %m"); + } + + return 0; +} + +static int mount_tmpfs( + const char *dest, + CustomMount *m, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + const char *where, *options; + _cleanup_free_ char *buf = NULL; + int r; + + assert(dest); + assert(m); + + where = prefix_roota(dest, m->destination); + + r = mkdir_p_label(where, 0755); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); + + r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + options = r > 0 ? buf : m->options; + + if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "tmpfs mount to %s failed: %m", where); + + return 0; +} + +static char *joined_and_escaped_lower_dirs(char * const *lower) { + _cleanup_strv_free_ char **sv = NULL; + + sv = strv_copy(lower); + if (!sv) + return NULL; + + strv_reverse(sv); + + if (!strv_shell_escape(sv, ",:")) + return NULL; + + return strv_join(sv, ":"); +} + +static int mount_overlay(const char *dest, CustomMount *m) { + _cleanup_free_ char *lower = NULL; + const char *where, *options; + int r; + + assert(dest); + assert(m); + + where = prefix_roota(dest, m->destination); + + r = mkdir_label(where, 0755); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); + + (void) mkdir_p_label(m->source, 0755); + + lower = joined_and_escaped_lower_dirs(m->lower); + if (!lower) + return log_oom(); + + if (m->read_only) { + _cleanup_free_ char *escaped_source = NULL; + + escaped_source = shell_escape(m->source, ",:"); + if (!escaped_source) + return log_oom(); + + options = strjoina("lowerdir=", escaped_source, ":", lower); + } else { + _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL; + + assert(m->work_dir); + (void) mkdir_label(m->work_dir, 0700); + + escaped_source = shell_escape(m->source, ",:"); + if (!escaped_source) + return log_oom(); + escaped_work_dir = shell_escape(m->work_dir, ",:"); + if (!escaped_work_dir) + return log_oom(); + + options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir); + } + + if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0) + return log_error_errno(errno, "overlay mount to %s failed: %m", where); + + return 0; +} + +int mount_custom( + const char *dest, + CustomMount *mounts, unsigned n, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + unsigned i; + int r; + + assert(dest); + + for (i = 0; i < n; i++) { + CustomMount *m = mounts + i; + + switch (m->type) { + + case CUSTOM_MOUNT_BIND: + r = mount_bind(dest, m); + break; + + case CUSTOM_MOUNT_TMPFS: + r = mount_tmpfs(dest, m, userns, uid_shift, uid_range, selinux_apifs_context); + break; + + case CUSTOM_MOUNT_OVERLAY: + r = mount_overlay(dest, m); + break; + + default: + assert_not_reached("Unknown custom mount type"); + } + + if (r < 0) + return r; + } + + return 0; +} + +static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) { + char *to; + int r; + + to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy); + + r = path_is_mount_point(to, 0); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to); + if (r > 0) + return 0; + + mkdir_p(to, 0755); + + /* The superblock mount options of the mount point need to be + * identical to the hosts', and hence writable... */ + if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0) + return log_error_errno(errno, "Failed to mount to %s: %m", to); + + /* ... hence let's only make the bind mount read-only, not the + * superblock. */ + if (read_only) { + if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", to); + } + return 1; +} + +static int mount_legacy_cgroups( + const char *dest, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_set_free_free_ Set *controllers = NULL; + const char *cgroup_root; + int r; + + 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) + return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); + if (r == 0) { + _cleanup_free_ char *options = NULL; + + r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, selinux_apifs_context, &options); + if (r < 0) + return log_oom(); + + if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m"); + } + + if (cg_unified() > 0) + goto skip_controllers; + + controllers = set_new(&string_hash_ops); + if (!controllers) + return log_oom(); + + r = cg_kernel_controllers(controllers); + if (r < 0) + return log_error_errno(r, "Failed to determine cgroup controllers: %m"); + + for (;;) { + _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + origin = prefix_root("/sys/fs/cgroup/", controller); + if (!origin) + return log_oom(); + + r = readlink_malloc(origin, &combined); + if (r == -EINVAL) { + /* Not a symbolic link, but directly a single cgroup hierarchy */ + + r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true); + if (r < 0) + return r; + + } else if (r < 0) + return log_error_errno(r, "Failed to read link %s: %m", origin); + else { + _cleanup_free_ char *target = NULL; + + target = prefix_root(dest, origin); + if (!target) + return log_oom(); + + /* A symbolic link, a combination of controllers in one hierarchy */ + + if (!filename_is_valid(combined)) { + log_warning("Ignoring invalid combined hierarchy %s.", combined); + continue; + } + + r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true); + if (r < 0) + return r; + + r = symlink_idempotent(combined, target); + if (r == -EINVAL) { + log_error("Invalid existing symlink for combined hierarchy"); + return r; + } + if (r < 0) + return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); + } + } + +skip_controllers: + r = mount_legacy_cgroup_hierarchy(dest, "none,name=systemd,xattr", "systemd", false); + if (r < 0) + return r; + + if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root); + + return 0; +} + +static int mount_unified_cgroups(const char *dest) { + const char *p; + int r; + + assert(dest); + + 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 = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs"); + if (access(p, F_OK) >= 0) + return 0; + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p); + + log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p); + return -EINVAL; + } + + if (mount("cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0) + return log_error_errno(errno, "Failed to mount unified cgroup hierarchy to %s: %m", p); + + return 0; +} + +int mount_cgroups( + const char *dest, + bool unified_requested, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + if (unified_requested) + return mount_unified_cgroups(dest); + else + return mount_legacy_cgroups(dest, userns, uid_shift, uid_range, selinux_apifs_context); +} + +int mount_systemd_cgroup_writable( + const char *dest, + bool unified_requested) { + + _cleanup_free_ char *own_cgroup_path = NULL; + const char *systemd_root, *systemd_own; + int r; + + assert(dest); + + r = cg_pid_get_path(NULL, 0, &own_cgroup_path); + if (r < 0) + return log_error_errno(r, "Failed to determine our own cgroup path: %m"); + + /* If we are living in the top-level, then there's nothing to do... */ + if (path_equal(own_cgroup_path, "/")) + return 0; + + if (unified_requested) { + systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path); + systemd_root = prefix_roota(dest, "/sys/fs/cgroup"); + } else { + systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path); + systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd"); + } + + /* Make our own cgroup a (writable) bind mount */ + if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path); + + /* And then remount the systemd cgroup root read-only */ + if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to mount cgroup root read-only: %m"); + + return 0; +} + +int setup_volatile_state( + const char *directory, + VolatileMode mode, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_free_ char *buf = NULL; + const char *p, *options; + int r; + + assert(directory); + + if (mode != VOLATILE_STATE) + return 0; + + /* --volatile=state means we simply overmount /var + with a tmpfs, and the rest read-only. */ + + r = bind_remount_recursive(directory, true); + if (r < 0) + return log_error_errno(r, "Failed to remount %s read-only: %m", directory); + + p = prefix_roota(directory, "/var"); + r = mkdir(p, 0755); + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create %s: %m", directory); + + options = "mode=755"; + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + if (r > 0) + options = buf; + + if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "Failed to mount tmpfs to /var: %m"); + + return 0; +} + +int setup_volatile( + const char *directory, + VolatileMode mode, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + bool tmpfs_mounted = false, bind_mounted = false; + char template[] = "/tmp/nspawn-volatile-XXXXXX"; + _cleanup_free_ char *buf = NULL; + const char *f, *t, *options; + int r; + + assert(directory); + + if (mode != VOLATILE_YES) + return 0; + + /* --volatile=yes means we mount a tmpfs to the root dir, and + the original /usr to use inside it, and that read-only. */ + + if (!mkdtemp(template)) + return log_error_errno(errno, "Failed to create temporary directory: %m"); + + options = "mode=755"; + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + if (r > 0) + options = buf; + + if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) { + r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m"); + goto fail; + } + + tmpfs_mounted = true; + + f = prefix_roota(directory, "/usr"); + t = prefix_roota(template, "/usr"); + + r = mkdir(t, 0755); + if (r < 0 && errno != EEXIST) { + r = log_error_errno(errno, "Failed to create %s: %m", t); + goto fail; + } + + if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) { + r = log_error_errno(errno, "Failed to create /usr bind mount: %m"); + goto fail; + } + + bind_mounted = true; + + r = bind_remount_recursive(t, true); + if (r < 0) { + log_error_errno(r, "Failed to remount %s read-only: %m", t); + goto fail; + } + + if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) { + r = log_error_errno(errno, "Failed to move root mount: %m"); + goto fail; + } + + (void) rmdir(template); + + return 0; + +fail: + if (bind_mounted) + (void) umount(t); + + if (tmpfs_mounted) + (void) umount(template); + (void) rmdir(template); + return r; +} + +VolatileMode volatile_mode_from_string(const char *s) { + int b; + + if (isempty(s)) + return _VOLATILE_MODE_INVALID; + + b = parse_boolean(s); + if (b > 0) + return VOLATILE_YES; + if (b == 0) + return VOLATILE_NO; + + if (streq(s, "state")) + return VOLATILE_STATE; + + return _VOLATILE_MODE_INVALID; +} diff --git a/src/systemd-nspawn/nspawn-mount.h b/src/systemd-nspawn/nspawn-mount.h new file mode 100644 index 0000000000..0daf145412 --- /dev/null +++ b/src/systemd-nspawn/nspawn-mount.h @@ -0,0 +1,69 @@ +#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 . +***/ + +#include + +typedef enum VolatileMode { + VOLATILE_NO, + VOLATILE_YES, + VOLATILE_STATE, + _VOLATILE_MODE_MAX, + _VOLATILE_MODE_INVALID = -1 +} VolatileMode; + +typedef enum CustomMountType { + CUSTOM_MOUNT_BIND, + CUSTOM_MOUNT_TMPFS, + CUSTOM_MOUNT_OVERLAY, + _CUSTOM_MOUNT_TYPE_MAX, + _CUSTOM_MOUNT_TYPE_INVALID = -1 +} CustomMountType; + +typedef struct CustomMount { + CustomMountType type; + bool read_only; + char *source; /* for overlayfs this is the upper directory */ + char *destination; + char *options; + char *work_dir; + char **lower; +} CustomMount; + +CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); + +void custom_mount_free_all(CustomMount *l, unsigned n); +int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); +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 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); + +int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); +int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +VolatileMode volatile_mode_from_string(const char *s); diff --git a/src/systemd-nspawn/nspawn-network.c b/src/systemd-nspawn/nspawn-network.c new file mode 100644 index 0000000000..14865734c8 --- /dev/null +++ b/src/systemd-nspawn/nspawn-network.c @@ -0,0 +1,696 @@ +/*** + 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 . +***/ + +#include + +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/ether-addr-util.h" +#include "basic/lockfile-util.h" +#include "basic/siphash24.h" +#include "basic/socket-util.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-netlink/netlink-util.h" +#include "sd-netlink/sd-netlink.h" +#include "shared/udev-util.h" + +#include "nspawn-network.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 remove_one_link(sd_netlink *rtnl, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + if (isempty(name)) + return 0; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r == -ENODEV) /* Already gone */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to remove interface %s: %m", name); + + return 1; +} + +static int generate_mac( + const char *machine_name, + struct ether_addr *mac, + sd_id128_t hash_key, + uint64_t idx) { + + uint64_t result; + size_t l, sz; + uint8_t *v, *i; + int r; + + l = strlen(machine_name); + sz = sizeof(sd_id128_t) + l; + if (idx > 0) + sz += sizeof(idx); + + v = alloca(sz); + + /* fetch some persistent data unique to the host */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + + /* combine with some data unique (on this host) to this + * container instance */ + i = mempcpy(v + sizeof(sd_id128_t), machine_name, l); + if (idx > 0) { + idx = htole64(idx); + memcpy(i, &idx, sizeof(idx)); + } + + /* Let's hash the host machine ID plus the container name. We + * use a fixed, but originally randomly created hash key here. */ + result = htole64(siphash24(v, sz, hash_key.bytes)); + + assert_cc(ETH_ALEN <= sizeof(result)); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); + + /* see eth_random_addr in the kernel */ + mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + return 0; +} + +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) { + + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + 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, 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); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container(m, VETH_INFO_PEER); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + 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); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + 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) + return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_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; +} + +static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r, bridge_ifi; + + assert(rtnl); + assert(veth_name); + assert(bridge_name); + + bridge_ifi = (int) if_nametoindex(bridge_name); + if (bridge_ifi <= 0) + return -errno; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); + if (r < 0) + return r; + + r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return r; + + return bridge_ifi; +} + +static int create_bridge(sd_netlink *rtnl, const char *bridge_name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return r; + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge"); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int setup_bridge(const char *veth_name, const char *bridge_name, bool create) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r, bridge_ifi; + unsigned n = 0; + + assert(veth_name); + assert(bridge_name); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (create) { + /* We take a system-wide lock here, so that we can safely check whether there's still a member in the + * bridge before removing it, without risking interference from other nspawn instances. */ + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + } + + for (;;) { + bridge_ifi = join_bridge(rtnl, veth_name, bridge_name); + if (bridge_ifi >= 0) + return bridge_ifi; + if (bridge_ifi != -ENODEV || !create || n > 10) + return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name); + + /* Count attempts, so that we don't enter an endless loop here. */ + n++; + + /* The bridge doesn't exist yet. Let's create it */ + r = create_bridge(rtnl, bridge_name); + if (r < 0) + return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name); + + /* Try again, now that the bridge exists */ + } +} + +int remove_bridge(const char *bridge_name) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + const char *path; + int r; + + /* Removes the specified bridge, but only if it is currently empty */ + + if (isempty(bridge_name)) + return 0; + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + + path = strjoina("/sys/class/net/", bridge_name, "/brif"); + + r = dir_is_empty(path); + if (r == -ENOENT) /* Already gone? */ + return 0; + if (r < 0) + return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name); + if (r == 0) /* Still populated, leave it around */ + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + return remove_one_link(rtnl, bridge_name); +} + +static int parse_interface(struct udev *udev, const char *name) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + char ifi_str[2 + DECIMAL_STR_MAX(int)]; + int ifi; + + ifi = (int) if_nametoindex(name); + if (ifi <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", name); + + sprintf(ifi_str, "n%i", ifi); + d = udev_device_new_from_device_id(udev, ifi_str); + if (!d) + return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name); + + if (udev_device_get_is_initialized(d) <= 0) { + log_error("Network interface %s is not initialized yet.", name); + return -EBUSY; + } + + return ifi; +} + +int move_network_interfaces(pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i); + } + + return 0; +} + +int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + unsigned idx = 0; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + _cleanup_free_ char *n = NULL; + struct ether_addr mac; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++); + if (r < 0) + return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); + + 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_u32(m, IFLA_LINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); + + n = strappend("mv-", *i); + if (!n) + return log_oom(); + + strshorten(n, IFNAMSIZ-1); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); + 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); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); + if (r < 0) + return log_error_errno(r, "Failed to append macvlan mode: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); + } + + return 0; +} + +int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + _cleanup_free_ char *n = NULL; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + 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_u32(m, IFLA_LINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); + + n = strappend("iv-", *i); + if (!n) + return log_oom(); + + strshorten(n, IFNAMSIZ-1); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); + if (r < 0) + return log_error_errno(r, "Failed to add ipvlan mode: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); + } + + 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 || !ifname_valid(a)) + return -EINVAL; + + r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0 || !ifname_valid(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; +} + +int remove_veth_links(const char *primary, char **pairs) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + char **a, **b; + int r; + + /* In some cases the kernel might pin the veth links between host and container even after the namespace + * died. Hence, let's better remove them explicitly too. */ + + if (isempty(primary) && strv_isempty(pairs)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + remove_one_link(rtnl, primary); + + STRV_FOREACH_PAIR(a, b, pairs) + remove_one_link(rtnl, *a); + + return 0; +} diff --git a/src/systemd-nspawn/nspawn-network.h b/src/systemd-nspawn/nspawn-network.h new file mode 100644 index 0000000000..3d8861e1e5 --- /dev/null +++ b/src/systemd-nspawn/nspawn-network.h @@ -0,0 +1,39 @@ +#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 . +***/ + +#include +#include +#include + +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, bool create); +int remove_bridge(const char *bridge_name); + +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); + +int remove_veth_links(const char *primary, char **pairs); diff --git a/src/systemd-nspawn/nspawn-patch-uid.c b/src/systemd-nspawn/nspawn-patch-uid.c new file mode 100644 index 0000000000..dc703165ff --- /dev/null +++ b/src/systemd-nspawn/nspawn-patch-uid.c @@ -0,0 +1,483 @@ +/*** + 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 . +***/ + +#include + +#include +#ifdef HAVE_ACL +#include +#endif +#include +#include +#include + +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/missing.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "shared/acl-util.h" + +#include "nspawn-patch-uid.h" + +#ifdef HAVE_ACL + +static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + acl_t acl; + + assert(fd >= 0); + assert(ret); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + acl = acl_get_file(procfs_path, type); + } else if (type == ACL_TYPE_ACCESS) + acl = acl_get_fd(fd); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + acl = acl_get_file(procfs_path, type); + } + if (!acl) + return -errno; + + *ret = acl; + return 0; +} + +static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + assert(fd >= 0); + assert(acl); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + r = acl_set_file(procfs_path, type, acl); + } else if (type == ACL_TYPE_ACCESS) + r = acl_set_fd(fd, acl); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + r = acl_set_file(procfs_path, type, acl); + } + if (r < 0) + return -errno; + + return 0; +} + +static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) { + _cleanup_(acl_freep) acl_t copy = NULL; + acl_entry_t i; + int r; + + assert(acl); + assert(ret); + + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + while (r > 0) { + uid_t *old_uid, new_uid; + bool modify = false; + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (IN_SET(tag, ACL_USER, ACL_GROUP)) { + + /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that + * this is actually OK */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + old_uid = acl_get_qualifier(i); + if (!old_uid) + return -errno; + + new_uid = shift | (*old_uid & UINT32_C(0xFFFF)); + if (!uid_is_valid(new_uid)) + return -EINVAL; + + modify = new_uid != *old_uid; + if (modify && !copy) { + int n; + + /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the + * beginning, so that we copy all entries, starting from the first, this time. */ + + n = acl_entries(acl); + if (n < 0) + return -errno; + + copy = acl_init(n); + if (!copy) + return -errno; + + /* Seek back to the beginning */ + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + continue; + } + } + + if (copy) { + acl_entry_t new_entry; + + if (acl_create_entry(©, &new_entry) < 0) + return -errno; + + if (acl_copy_entry(new_entry, i) < 0) + return -errno; + + if (modify) + if (acl_set_qualifier(new_entry, &new_uid) < 0) + return -errno; + } + + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i); + if (r < 0) + return -errno; + } + + *ret = copy; + copy = NULL; + + return !!*ret; +} + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + /* ACLs are not supported on symlinks, there's no point in trying */ + if (S_ISLNK(st->st_mode)) + return 0; + + r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl); + if (r == -EOPNOTSUPP) + return 0; + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted); + if (r < 0) + return r; + + changed = true; + } + + if (S_ISDIR(st->st_mode)) { + acl_free(acl); + acl_free(shifted); + + acl = shifted = NULL; + + r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl); + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted); + if (r < 0) + return r; + + changed = true; + } + } + + return changed; +} + +#else + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + return 0; +} + +#endif + +static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) { + uid_t new_uid; + gid_t new_gid; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + new_uid = shift | (st->st_uid & UINT32_C(0xFFFF)); + new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF)); + + if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid)) + return -EINVAL; + + if (st->st_uid != new_uid || st->st_gid != new_gid) { + if (name) + r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW); + else + r = fchown(fd, new_uid, new_gid); + if (r < 0) + return -errno; + + /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */ + if (name) { + if (!S_ISLNK(st->st_mode)) + r = fchmodat(fd, name, st->st_mode, 0); + else /* AT_SYMLINK_NOFOLLOW is not available for fchmodat() */ + r = 0; + } else + r = fchmod(fd, st->st_mode); + if (r < 0) + return -errno; + + changed = true; + } + + r = patch_acls(fd, name, st, shift); + if (r < 0) + return r; + + return r > 0 || changed; +} + +/* + * Check if the filesystem is fully compatible with user namespaces or + * UID/GID patching. Some filesystems in this list can be fully mounted inside + * user namespaces, however their inodes may relate to host resources or only + * valid in the global user namespace, therefore no patching should be applied. + */ +static int is_fs_fully_userns_compatible(int fd) { + struct statfs sfs; + + assert(fd >= 0); + + if (fstatfs(fd, &sfs) < 0) + return -errno; + + return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SECURITYFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, BPF_FS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, TRACEFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC); +} + +static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) { + bool changed = false; + int r; + + assert(fd >= 0); + + /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we + * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's + * stop the recursion when we hit procfs, sysfs or some other special file systems. */ + r = is_fs_fully_userns_compatible(fd); + if (r < 0) + goto finish; + if (r > 0) { + r = 0; /* don't recurse */ + goto finish; + } + + r = patch_fd(fd, NULL, st, shift); + if (r == -EROFS) { + _cleanup_free_ char *name = NULL; + + if (!is_toplevel) { + /* When we hit a ready-only subtree we simply skip it, but log about it. */ + (void) fd_get_path(fd, &name); + log_debug("Skippping read-only file or directory %s.", strna(name)); + r = 0; + } + + goto finish; + } + if (r < 0) + goto finish; + + if (S_ISDIR(st->st_mode)) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + if (!donate_fd) { + int copy; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) { + r = -errno; + goto finish; + } + + fd = copy; + donate_fd = true; + } + + d = fdopendir(fd); + if (!d) { + r = -errno; + goto finish; + } + fd = -1; + + FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { + struct stat fst; + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { + r = -errno; + goto finish; + } + + if (S_ISDIR(fst.st_mode)) { + int subdir_fd; + + subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (subdir_fd < 0) { + r = -errno; + goto finish; + + } + + r = recurse_fd(subdir_fd, true, &fst, shift, false); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else { + r = patch_fd(dirfd(d), de->d_name, &fst, shift); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + } + } + } + + r = changed; + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) { + struct stat st; + int r; + + assert(fd >= 0); + + /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an + * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges + * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual + * UID within the container. */ + + if ((shift & 0xFFFF) != 0) { + /* We only support containers where the shift starts at a 2^16 boundary */ + r = -EOPNOTSUPP; + goto finish; + } + + if (range != 0x10000) { + /* We only support containers with 16bit UID ranges for the patching logic */ + r = -EOPNOTSUPP; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) { + /* We only support containers where the uid/gid container ID match */ + r = -EBADE; + goto finish; + } + + /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume + * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */ + if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0) + return 0; + + return recurse_fd(fd, donate_fd, &st, shift, true); + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +int fd_patch_uid(int fd, uid_t shift, uid_t range) { + return fd_patch_uid_internal(fd, false, shift, range); +} + +int path_patch_uid(const char *path, uid_t shift, uid_t range) { + int fd; + + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) + return -errno; + + return fd_patch_uid_internal(fd, true, shift, range); +} diff --git a/src/systemd-nspawn/nspawn-patch-uid.h b/src/systemd-nspawn/nspawn-patch-uid.h new file mode 100644 index 0000000000..55d0990016 --- /dev/null +++ b/src/systemd-nspawn/nspawn-patch-uid.h @@ -0,0 +1,23 @@ +/*** + 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 . +***/ + +#include + +int fd_patch_uid(int fd, uid_t shift, uid_t range); +int path_patch_uid(const char *path, uid_t shift, uid_t range); diff --git a/src/systemd-nspawn/nspawn-register.c b/src/systemd-nspawn/nspawn-register.c new file mode 100644 index 0000000000..9660ced5b9 --- /dev/null +++ b/src/systemd-nspawn/nspawn-register.c @@ -0,0 +1,230 @@ +/*** + 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 . +***/ + +#include + +#include "basic/stat-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "sd-bus/bus-error.h" +#include "shared/bus-unit-util.h" +#include "shared/bus-util.h" + +#include "nspawn-register.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, + const char *service) { + + _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); + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); + + if (keep_unit) { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "RegisterMachineWithNetwork", + &error, + NULL, + "sayssusai", + machine_name, + SD_BUS_MESSAGE_APPEND_ID128(uuid), + service, + "container", + (uint32_t) pid, + strempty(directory), + local_ifindex > 0 ? 1 : 0, local_ifindex); + } else { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + char **i; + unsigned j; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CreateMachineWithNetwork"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "sayssusai", + machine_name, + SD_BUS_MESSAGE_APPEND_ID128(uuid), + service, + "container", + (uint32_t) pid, + strempty(directory), + local_ifindex > 0 ? 1 : 0, local_ifindex); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(slice)) { + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "closed"); + if (r < 0) + return bus_log_create_error(r); + + /* If you make changes here, also make sure to update + * systemd-nspawn@.service, to keep the device + * policies in sync regardless if we are run with or + * without the --keep-unit switch. */ + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 2, + /* Allow the container to + * access and create the API + * device nodes, so that + * PrivateDevices= in the + * container can work + * fine */ + "/dev/net/tun", "rwm", + /* Allow the container + * access to ptys. However, + * do not permit the + * container to ever create + * these device nodes. */ + "char-pts", "rw"); + if (r < 0) + return bus_log_create_error(r); + + for (j = 0; j < n_mounts; j++) { + CustomMount *cm = mounts + j; + + if (cm->type != CUSTOM_MOUNT_BIND) + continue; + + r = is_device_node(cm->source); + if (r < 0) + return log_error_errno(r, "Failed to stat %s: %m", cm->source); + + if (r) { + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, + cm->source, cm->read_only ? "r" : "rw"); + if (r < 0) + return log_error_errno(r, "Failed to append message arguments: %m"); + } + } + + if (kill_signal != 0) { + r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); + if (r < 0) + return bus_log_create_error(r); + } + + STRV_FOREACH(i, properties) { + r = bus_append_unit_property_assignment(m, *i); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); + } + + if (r < 0) { + log_error("Failed to register machine: %s", bus_error_message(&error, r)); + return r; + } + + return 0; +} + +int terminate_machine(pid_t pid) { + _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; + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineByPID", + &error, + &reply, + "u", + (uint32_t) pid); + if (r < 0) { + /* Note that the machine might already have been + * cleaned up automatically, hence don't consider it a + * failure if we cannot get the machine object. */ + log_debug("Failed to get machine: %s", bus_error_message(&error, r)); + return 0; + } + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + path, + "org.freedesktop.machine1.Machine", + "Terminate", + &error, + NULL, + NULL); + if (r < 0) { + log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); + return 0; + } + + return 0; +} diff --git a/src/systemd-nspawn/nspawn-register.h b/src/systemd-nspawn/nspawn-register.h new file mode 100644 index 0000000000..c7a50f7477 --- /dev/null +++ b/src/systemd-nspawn/nspawn-register.h @@ -0,0 +1,29 @@ +#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 . +***/ + +#include + +#include + +#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, const char *service); +int terminate_machine(pid_t pid); diff --git a/src/systemd-nspawn/nspawn-seccomp.c b/src/systemd-nspawn/nspawn-seccomp.c new file mode 100644 index 0000000000..3efdf16a80 --- /dev/null +++ b/src/systemd-nspawn/nspawn-seccomp.c @@ -0,0 +1,198 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include + +#ifdef HAVE_SECCOMP +#include +#endif + +#include "basic/log.h" + +#ifdef HAVE_SECCOMP +#include "shared/seccomp-util.h" +#endif + +#include "nspawn-seccomp.h" + +#ifdef HAVE_SECCOMP + +static int seccomp_add_default_syscall_filter(scmp_filter_ctx ctx, + uint64_t cap_list_retain) { + unsigned i; + int r; + static const struct { + uint64_t capability; + int syscall_num; + } blacklist[] = { + { 0, SCMP_SYS(_sysctl) }, /* obsolete syscall */ + { 0, SCMP_SYS(add_key) }, /* keyring is not namespaced */ + { 0, SCMP_SYS(afs_syscall) }, /* obsolete syscall */ + { 0, SCMP_SYS(bdflush) }, +#ifdef __NR_bpf + { 0, SCMP_SYS(bpf) }, +#endif + { 0, SCMP_SYS(break) }, /* obsolete syscall */ + { 0, SCMP_SYS(create_module) }, /* obsolete syscall */ + { 0, SCMP_SYS(ftime) }, /* obsolete syscall */ + { 0, SCMP_SYS(get_kernel_syms) }, /* obsolete syscall */ + { 0, SCMP_SYS(getpmsg) }, /* obsolete syscall */ + { 0, SCMP_SYS(gtty) }, /* obsolete syscall */ +#ifdef __NR_kexec_file_load + { 0, SCMP_SYS(kexec_file_load) }, +#endif + { 0, SCMP_SYS(kexec_load) }, + { 0, SCMP_SYS(keyctl) }, /* keyring is not namespaced */ + { 0, SCMP_SYS(lock) }, /* obsolete syscall */ + { 0, SCMP_SYS(lookup_dcookie) }, + { 0, SCMP_SYS(mpx) }, /* obsolete syscall */ + { 0, SCMP_SYS(nfsservctl) }, /* obsolete syscall */ + { 0, SCMP_SYS(open_by_handle_at) }, + { 0, SCMP_SYS(perf_event_open) }, + { 0, SCMP_SYS(prof) }, /* obsolete syscall */ + { 0, SCMP_SYS(profil) }, /* obsolete syscall */ + { 0, SCMP_SYS(putpmsg) }, /* obsolete syscall */ + { 0, SCMP_SYS(query_module) }, /* obsolete syscall */ + { 0, SCMP_SYS(quotactl) }, + { 0, SCMP_SYS(request_key) }, /* keyring is not namespaced */ + { 0, SCMP_SYS(security) }, /* obsolete syscall */ + { 0, SCMP_SYS(sgetmask) }, /* obsolete syscall */ + { 0, SCMP_SYS(ssetmask) }, /* obsolete syscall */ + { 0, SCMP_SYS(stty) }, /* obsolete syscall */ + { 0, SCMP_SYS(swapoff) }, + { 0, SCMP_SYS(swapon) }, + { 0, SCMP_SYS(sysfs) }, /* obsolete syscall */ + { 0, SCMP_SYS(tuxcall) }, /* obsolete syscall */ + { 0, SCMP_SYS(ulimit) }, /* obsolete syscall */ + { 0, SCMP_SYS(uselib) }, /* obsolete syscall */ + { 0, SCMP_SYS(ustat) }, /* obsolete syscall */ + { 0, SCMP_SYS(vserver) }, /* obsolete syscall */ + { CAP_SYSLOG, SCMP_SYS(syslog) }, + { CAP_SYS_MODULE, SCMP_SYS(delete_module) }, + { CAP_SYS_MODULE, SCMP_SYS(finit_module) }, + { CAP_SYS_MODULE, SCMP_SYS(init_module) }, + { CAP_SYS_PACCT, SCMP_SYS(acct) }, + { CAP_SYS_PTRACE, SCMP_SYS(process_vm_readv) }, + { CAP_SYS_PTRACE, SCMP_SYS(process_vm_writev) }, + { CAP_SYS_PTRACE, SCMP_SYS(ptrace) }, + { CAP_SYS_RAWIO, SCMP_SYS(ioperm) }, + { CAP_SYS_RAWIO, SCMP_SYS(iopl) }, + { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_iobase) }, + { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_read) }, + { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_write) }, +#ifdef __NR_s390_pci_mmio_read + { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_read) }, +#endif +#ifdef __NR_s390_pci_mmio_write + { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_write) }, +#endif + { CAP_SYS_TIME, SCMP_SYS(adjtimex) }, + { CAP_SYS_TIME, SCMP_SYS(clock_adjtime) }, + { CAP_SYS_TIME, SCMP_SYS(clock_settime) }, + { CAP_SYS_TIME, SCMP_SYS(settimeofday) }, + { CAP_SYS_TIME, SCMP_SYS(stime) }, + }; + + for (i = 0; i < ELEMENTSOF(blacklist); i++) { + if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability))) + continue; + + r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0); + if (r == -EFAULT) + continue; /* unknown syscall */ + if (r < 0) + return log_error_errno(r, "Failed to block syscall: %m"); + } + + return 0; +} + +int setup_seccomp(uint64_t cap_list_retain) { + scmp_filter_ctx seccomp; + int r; + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return log_oom(); + + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) { + log_error_errno(r, "Failed to add secondary archs to seccomp filter: %m"); + goto finish; + } + + r = seccomp_add_default_syscall_filter(seccomp, cap_list_retain); + if (r < 0) + goto finish; + + /* + Audit is broken in containers, much of the userspace audit + hookup will fail if running inside a container. We don't + care and just turn off creation of audit sockets. + + This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail + with EAFNOSUPPORT which audit userspace uses as indication + that audit is disabled in the kernel. + */ + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EAFNOSUPPORT), + SCMP_SYS(socket), + 2, + SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), + SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); + if (r < 0) { + log_error_errno(r, "Failed to add audit seccomp rule: %m"); + goto finish; + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) { + log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); + goto finish; + } + + r = seccomp_load(seccomp); + if (r == -EINVAL) { + log_debug_errno(r, "Kernel is probably not configured with CONFIG_SECCOMP. Disabling seccomp audit filter: %m"); + r = 0; + goto finish; + } + if (r < 0) { + log_error_errno(r, "Failed to install seccomp audit filter: %m"); + goto finish; + } + +finish: + seccomp_release(seccomp); + return r; +} + +#else + +int setup_seccomp(uint64_t cap_list_retain) { + return 0; +} + +#endif diff --git a/src/systemd-nspawn/nspawn-seccomp.h b/src/systemd-nspawn/nspawn-seccomp.h new file mode 100644 index 0000000000..5bde16faf9 --- /dev/null +++ b/src/systemd-nspawn/nspawn-seccomp.h @@ -0,0 +1,24 @@ +#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 . +***/ + +#include + +int setup_seccomp(uint64_t cap_list_retain); diff --git a/src/systemd-nspawn/nspawn-settings.c b/src/systemd-nspawn/nspawn-settings.c new file mode 100644 index 0000000000..f05c671946 --- /dev/null +++ b/src/systemd-nspawn/nspawn-settings.c @@ -0,0 +1,517 @@ +/*** + 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 . +***/ + +#include "basic/alloc-util.h" +#include "basic/cap-list.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/conf-parser.h" + +#include "nspawn-network.h" +#include "nspawn-settings.h" + +int settings_load(FILE *f, const char *path, Settings **ret) { + _cleanup_(settings_freep) Settings *s = NULL; + int r; + + assert(path); + assert(ret); + + s = new0(Settings, 1); + if (!s) + return -ENOMEM; + + s->start_mode = _START_MODE_INVALID; + s->personality = PERSONALITY_INVALID; + s->userns_mode = _USER_NAMESPACE_MODE_INVALID; + s->uid_shift = UID_INVALID; + s->uid_range = UID_INVALID; + + s->read_only = -1; + s->volatile_mode = _VOLATILE_MODE_INVALID; + s->userns_chown = -1; + + s->private_network = -1; + s->network_veth = -1; + + r = config_parse(NULL, path, f, + "Exec\0" + "Network\0" + "Files\0", + config_item_perf_lookup, nspawn_gperf_lookup, + false, + false, + true, + s); + if (r < 0) + return r; + + /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either + * both fields shall be initialized or neither. */ + if (s->userns_mode == USER_NAMESPACE_PICK) + s->userns_chown = true; + else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) + s->userns_chown = false; + + if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID) + s->userns_mode = USER_NAMESPACE_NO; + + *ret = s; + s = NULL; + + return 0; +} + +Settings* settings_free(Settings *s) { + + if (!s) + return NULL; + + strv_free(s->parameters); + strv_free(s->environment); + free(s->user); + free(s->working_directory); + + 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); + free(s->network_zone); + expose_port_free_all(s->expose_ports); + + custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); + free(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_zone || + 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 || + s->network_zone; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); + +int config_parse_expose_port( + 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 *s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = expose_port_parse(&s->expose_ports, rvalue); + if (r == -EEXIST) { + log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue); + return 0; + } + + return 0; +} + +int config_parse_capability( + 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 u = 0, *result = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *word = NULL; + int cap; + + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + break; + + cap = capability_from_name(word); + if (cap < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word); + continue; + } + + u |= 1 << ((uint64_t) cap); + } + + if (u == 0) + return 0; + + *result |= u; + return 0; +} + +int config_parse_id128( + 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) { + + sd_id128_t t, *result = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = sd_id128_from_string(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); + return 0; + } + + *result = t; + return 0; +} + +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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue); + return 0; + } + + return 0; +} + +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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue); + return 0; + } + + return 0; +} + +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; +} + +int config_parse_network_zone( + 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; + _cleanup_free_ char *j = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + + j = strappend("vz-", rvalue); + if (!ifname_valid(j)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue); + return 0; + } + + free(settings->network_zone); + settings->network_zone = j; + j = NULL; + + return 0; +} + +int config_parse_boot( + 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 = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue); + return 0; + } + + if (r > 0) { + if (settings->start_mode == START_PID2) + goto conflict; + + settings->start_mode = START_BOOT; + } else { + if (settings->start_mode == START_BOOT) + goto conflict; + + if (settings->start_mode < 0) + settings->start_mode = START_PID1; + } + + return 0; + +conflict: + log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); + return 0; +} + +int config_parse_pid2( + 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 = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue); + return 0; + } + + if (r > 0) { + if (settings->start_mode == START_BOOT) + goto conflict; + + settings->start_mode = START_PID2; + } else { + if (settings->start_mode == START_PID2) + goto conflict; + + if (settings->start_mode < 0) + settings->start_mode = START_PID1; + } + + return 0; + +conflict: + log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); + return 0; +} + +int config_parse_private_users( + 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 = parse_boolean(rvalue); + if (r == 0) { + /* no: User namespacing off */ + settings->userns_mode = USER_NAMESPACE_NO; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (streq(rvalue, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + settings->userns_mode = USER_NAMESPACE_PICK; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else { + const char *range, *shift; + uid_t sh, rn; + + /* anything else: User namespacing on, UID range is explicitly configured */ + + range = strchr(rvalue, ':'); + if (range) { + shift = strndupa(rvalue, range - rvalue); + range++; + + r = safe_atou32(range, &rn); + if (r < 0 || rn <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range); + return 0; + } + } else { + shift = rvalue; + rn = UINT32_C(0x10000); + } + + r = parse_uid(shift, &sh); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range); + return 0; + } + + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = sh; + settings->uid_range = rn; + } + + return 0; +} diff --git a/src/systemd-nspawn/nspawn-settings.h b/src/systemd-nspawn/nspawn-settings.h new file mode 100644 index 0000000000..8a88647df2 --- /dev/null +++ b/src/systemd-nspawn/nspawn-settings.h @@ -0,0 +1,119 @@ +#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 . +***/ + +#include + +#include "basic/macro.h" + +#include "nspawn-expose-ports.h" +#include "nspawn-mount.h" + +typedef enum StartMode { + START_PID1, /* Run parameters as command line as process 1 */ + START_PID2, /* Use stub init process as PID 1, run parameters as command line as process 2 */ + START_BOOT, /* Search for init system, pass arguments as parameters */ + _START_MODE_MAX, + _START_MODE_INVALID = -1 +} StartMode; + +typedef enum UserNamespaceMode { + USER_NAMESPACE_NO, + USER_NAMESPACE_FIXED, + USER_NAMESPACE_PICK, + _USER_NAMESPACE_MODE_MAX, + _USER_NAMESPACE_MODE_INVALID = -1, +} UserNamespaceMode; + +typedef enum SettingsMask { + SETTING_START_MODE = 1 << 0, + SETTING_ENVIRONMENT = 1 << 1, + SETTING_USER = 1 << 2, + SETTING_CAPABILITY = 1 << 3, + SETTING_KILL_SIGNAL = 1 << 4, + SETTING_PERSONALITY = 1 << 5, + SETTING_MACHINE_ID = 1 << 6, + SETTING_NETWORK = 1 << 7, + SETTING_EXPOSE_PORTS = 1 << 8, + SETTING_READ_ONLY = 1 << 9, + SETTING_VOLATILE_MODE = 1 << 10, + SETTING_CUSTOM_MOUNTS = 1 << 11, + SETTING_WORKING_DIRECTORY = 1 << 12, + SETTING_USERNS = 1 << 13, + SETTING_NOTIFY_READY = 1 << 14, + _SETTINGS_MASK_ALL = (1 << 15) -1 +} SettingsMask; + +typedef struct Settings { + /* [Run] */ + StartMode start_mode; + char **parameters; + char **environment; + char *user; + uint64_t capability; + uint64_t drop_capability; + int kill_signal; + unsigned long personality; + sd_id128_t machine_id; + char *working_directory; + UserNamespaceMode userns_mode; + uid_t uid_shift, uid_range; + bool notify_ready; + + /* [Image] */ + int read_only; + VolatileMode volatile_mode; + CustomMount *custom_mounts; + unsigned n_custom_mounts; + int userns_chown; + + /* [Network] */ + int private_network; + int network_veth; + char *network_bridge; + char *network_zone; + 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); + +int config_parse_capability(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_id128(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_expose_port(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_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); +int config_parse_network_zone(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_boot(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_pid2(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_private_users(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/systemd-nspawn/nspawn-setuid.c b/src/systemd-nspawn/nspawn-setuid.c new file mode 100644 index 0000000000..360781c24c --- /dev/null +++ b/src/systemd-nspawn/nspawn-setuid.c @@ -0,0 +1,271 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/mkdir.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "nspawn-setuid.h" + +static int spawn_getent(const char *database, const char *key, pid_t *rpid) { + int pipe_fds[2]; + pid_t pid; + + assert(database); + assert(key); + assert(rpid); + + if (pipe2(pipe_fds, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to allocate pipe: %m"); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork getent child: %m"); + else if (pid == 0) { + int nullfd; + char *empty_env = NULL; + + if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (pipe_fds[0] > 2) + safe_close(pipe_fds[0]); + if (pipe_fds[1] > 2) + safe_close(pipe_fds[1]); + + nullfd = open("/dev/null", O_RDWR); + if (nullfd < 0) + _exit(EXIT_FAILURE); + + if (dup3(nullfd, STDIN_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (dup3(nullfd, STDERR_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (nullfd > 2) + safe_close(nullfd); + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + close_all_fds(NULL, 0); + + execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); + execle("/bin/getent", "getent", database, key, NULL, &empty_env); + _exit(EXIT_FAILURE); + } + + pipe_fds[1] = safe_close(pipe_fds[1]); + + *rpid = pid; + + return pipe_fds[0]; +} + +int change_uid_gid(const char *user, char **_home) { + char line[LINE_MAX], *x, *u, *g, *h; + const char *word, *state; + _cleanup_free_ uid_t *uids = NULL; + _cleanup_free_ char *home = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + unsigned n_uids = 0; + size_t sz = 0, l; + uid_t uid; + gid_t gid; + pid_t pid; + int r; + + assert(_home); + + if (!user || streq(user, "root") || streq(user, "0")) { + /* Reset everything fully to 0, just in case */ + + r = reset_uid_gid(); + if (r < 0) + return log_error_errno(r, "Failed to become root: %m"); + + *_home = NULL; + return 0; + } + + /* First, get user credentials */ + fd = spawn_getent("passwd", user, &pid); + if (fd < 0) + return fd; + + f = fdopen(fd, "r"); + if (!f) + return log_oom(); + fd = -1; + + if (!fgets(line, sizeof(line), f)) { + if (!ferror(f)) { + log_error("Failed to resolve user %s.", user); + return -ESRCH; + } + + return log_error_errno(errno, "Failed to read from getent: %m"); + } + + truncate_nl(line); + + wait_for_terminate_and_warn("getent passwd", pid, true); + + x = strchr(line, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid user field."); + return -EIO; + } + + u = strchr(x+1, ':'); + if (!u) { + log_error("/etc/passwd entry has invalid password field."); + return -EIO; + } + + u++; + g = strchr(u, ':'); + if (!g) { + log_error("/etc/passwd entry has invalid UID field."); + return -EIO; + } + + *g = 0; + g++; + x = strchr(g, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid GID field."); + return -EIO; + } + + *x = 0; + h = strchr(x+1, ':'); + if (!h) { + log_error("/etc/passwd entry has invalid GECOS field."); + return -EIO; + } + + h++; + x = strchr(h, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid home directory field."); + return -EIO; + } + + *x = 0; + + r = parse_uid(u, &uid); + if (r < 0) { + log_error("Failed to parse UID of user."); + return -EIO; + } + + r = parse_gid(g, &gid); + if (r < 0) { + log_error("Failed to parse GID of user."); + return -EIO; + } + + home = strdup(h); + if (!home) + return log_oom(); + + /* Second, get group memberships */ + fd = spawn_getent("initgroups", user, &pid); + if (fd < 0) + return fd; + + fclose(f); + f = fdopen(fd, "r"); + if (!f) + return log_oom(); + fd = -1; + + if (!fgets(line, sizeof(line), f)) { + if (!ferror(f)) { + log_error("Failed to resolve user %s.", user); + return -ESRCH; + } + + return log_error_errno(errno, "Failed to read from getent: %m"); + } + + truncate_nl(line); + + wait_for_terminate_and_warn("getent initgroups", pid, true); + + /* Skip over the username and subsequent separator whitespace */ + x = line; + x += strcspn(x, WHITESPACE); + x += strspn(x, WHITESPACE); + + FOREACH_WORD(word, l, x, state) { + char c[l+1]; + + memcpy(c, word, l); + c[l] = 0; + + if (!GREEDY_REALLOC(uids, sz, n_uids+1)) + return log_oom(); + + r = parse_uid(c, &uids[n_uids++]); + if (r < 0) { + log_error("Failed to parse group data from getent."); + return -EIO; + } + } + + r = mkdir_parents(home, 0775); + if (r < 0) + return log_error_errno(r, "Failed to make home root directory: %m"); + + r = mkdir_safe(home, 0755, uid, gid); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to make home directory: %m"); + + (void) fchown(STDIN_FILENO, uid, gid); + (void) fchown(STDOUT_FILENO, uid, gid); + (void) fchown(STDERR_FILENO, uid, gid); + + if (setgroups(n_uids, uids) < 0) + return log_error_errno(errno, "Failed to set auxiliary groups: %m"); + + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "setresgid() failed: %m"); + + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "setresuid() failed: %m"); + + if (_home) { + *_home = home; + home = NULL; + } + + return 0; +} diff --git a/src/systemd-nspawn/nspawn-setuid.h b/src/systemd-nspawn/nspawn-setuid.h new file mode 100644 index 0000000000..b4968ba1fc --- /dev/null +++ b/src/systemd-nspawn/nspawn-setuid.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int change_uid_gid(const char *user, char **ret); diff --git a/src/systemd-nspawn/nspawn-stub-pid1.c b/src/systemd-nspawn/nspawn-stub-pid1.c new file mode 100644 index 0000000000..86783b6136 --- /dev/null +++ b/src/systemd-nspawn/nspawn-stub-pid1.c @@ -0,0 +1,171 @@ +/*** + 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 . +***/ + +#include +#include +#include + +#include "basic/def.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/time-util.h" + +#include "nspawn-stub-pid1.h" + +int stub_pid1(void) { + enum { + STATE_RUNNING, + STATE_REBOOT, + STATE_POWEROFF, + } state = STATE_RUNNING; + + sigset_t fullmask, oldmask, waitmask; + usec_t quit_usec = USEC_INFINITY; + pid_t pid; + int r; + + /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful + * for allowing arbitrary processes run in a container, and still have all zombies reaped. */ + + assert_se(sigfillset(&fullmask) >= 0); + assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork child pid: %m"); + + if (pid == 0) { + /* Return in the child */ + assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0); + setsid(); + return 0; + } + + reset_all_signal_handlers(); + + log_close(); + close_all_fds(NULL, 0); + log_open(); + + rename_process("STUBINIT"); + + assert_se(sigemptyset(&waitmask) >= 0); + assert_se(sigset_add_many(&waitmask, + SIGCHLD, /* posix: process died */ + SIGINT, /* sysv: ctrl-alt-del */ + SIGRTMIN+3, /* systemd: halt */ + SIGRTMIN+4, /* systemd: poweroff */ + SIGRTMIN+5, /* systemd: reboot */ + SIGRTMIN+6, /* systemd: kexec */ + SIGRTMIN+13, /* systemd: halt */ + SIGRTMIN+14, /* systemd: poweroff */ + SIGRTMIN+15, /* systemd: reboot */ + SIGRTMIN+16, /* systemd: kexec */ + -1) >= 0); + + /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't + * support reexec/reloading in this stub process. */ + + for (;;) { + siginfo_t si; + usec_t current_usec; + + si.si_pid = 0; + r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); + if (r < 0) { + r = log_error_errno(errno, "Failed to reap children: %m"); + goto finish; + } + + current_usec = now(CLOCK_MONOTONIC); + + if (si.si_pid == pid || current_usec >= quit_usec) { + + /* The child we started ourselves died or we reached a timeout. */ + + if (state == STATE_REBOOT) { /* dispatch a queued reboot */ + (void) reboot(RB_AUTOBOOT); + r = log_error_errno(errno, "Failed to reboot: %m"); + goto finish; + + } else if (state == STATE_POWEROFF) + (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */ + + if (si.si_pid == pid && si.si_code == CLD_EXITED) + r = si.si_status; /* pass on exit code */ + else + r = 255; /* signal, coredump, timeout, … */ + + goto finish; + } + if (si.si_pid != 0) + /* We reaped something. Retry until there's nothing more to reap. */ + continue; + + if (quit_usec == USEC_INFINITY) + r = sigwaitinfo(&waitmask, &si); + else { + struct timespec ts; + r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec)); + } + if (r < 0) { + if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */ + continue; + if (errno == EAGAIN) /* timeout reached */ + continue; + + r = log_error_errno(errno, "Failed to wait for signal: %m"); + goto finish; + } + + if (si.si_signo == SIGCHLD) + continue; /* Let's reap this */ + + if (state != STATE_RUNNING) + continue; + + /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a + * constant… */ + + if (si.si_signo == SIGRTMIN+3 || + si.si_signo == SIGRTMIN+4 || + si.si_signo == SIGRTMIN+13 || + si.si_signo == SIGRTMIN+14) + + state = STATE_POWEROFF; + + else if (si.si_signo == SIGINT || + si.si_signo == SIGRTMIN+5 || + si.si_signo == SIGRTMIN+6 || + si.si_signo == SIGRTMIN+15 || + si.si_signo == SIGRTMIN+16) + + state = STATE_REBOOT; + else + assert_not_reached("Got unexpected signal"); + + /* (void) kill_and_sigcont(pid, SIGTERM); */ + quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; + } + +finish: + _exit(r < 0 ? EXIT_FAILURE : r); +} diff --git a/src/systemd-nspawn/nspawn-stub-pid1.h b/src/systemd-nspawn/nspawn-stub-pid1.h new file mode 100644 index 0000000000..36c1aaf5dd --- /dev/null +++ b/src/systemd-nspawn/nspawn-stub-pid1.h @@ -0,0 +1,22 @@ +#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 . +***/ + +int stub_pid1(void); diff --git a/src/systemd-nspawn/nspawn.c b/src/systemd-nspawn/nspawn.c new file mode 100644 index 0000000000..f921967487 --- /dev/null +++ b/src/systemd-nspawn/nspawn.c @@ -0,0 +1,4190 @@ +/*** + 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 . +***/ + +#ifdef HAVE_BLKID +#include +#endif +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_SELINUX +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/barrier.h" +#include "basic/blkid-util.h" +#include "basic/btrfs-util.h" +#include "basic/cap-list.h" +#include "basic/capability-util.h" +#include "basic/cgroup-util.h" +#include "basic/copy.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/random-util.h" +#include "basic/raw-clone.h" +#include "basic/rm-rf.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/stat-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/umask-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "sd-id128/id128-util.h" +#include "sd-netlink/netlink-util.h" +#include "shared/base-filesystem.h" +#include "shared/dev-setup.h" +#include "shared/fdset.h" +#include "shared/gpt.h" +#include "shared/machine-image.h" +#include "shared/ptyfwd.h" +#include "shared/udev-util.h" + +#include "loopback-setup.h" +#include "nspawn-cgroup.h" +#include "nspawn-expose-ports.h" +#include "nspawn-mount.h" +#include "nspawn-network.h" +#include "nspawn-patch-uid.h" +#include "nspawn-register.h" +#include "nspawn-seccomp.h" +#include "nspawn-settings.h" +#include "nspawn-setuid.h" +#include "nspawn-stub-pid1.h" + +/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit + * UID range here. We leave a bit of room at the lower end and a lot of room at the upper end, so that other subsystems + * may have their own allocation ranges too. */ +#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) +#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) + +/* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path + * nspawn_notify_socket_path is relative to the container + * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */ +#define NSPAWN_NOTIFY_SOCKET_PATH "/run/systemd/nspawn/notify" + +typedef enum ContainerStatus { + CONTAINER_TERMINATED, + CONTAINER_REBOOTED +} ContainerStatus; + +typedef enum LinkJournal { + LINK_NO, + LINK_AUTO, + LINK_HOST, + LINK_GUEST +} LinkJournal; + +static char *arg_directory = NULL; +static char *arg_template = NULL; +static char *arg_chdir = NULL; +static char *arg_user = NULL; +static sd_id128_t arg_uuid = {}; +static char *arg_machine = NULL; +static const char *arg_selinux_context = NULL; +static const char *arg_selinux_apifs_context = NULL; +static const char *arg_slice = NULL; +static bool arg_private_network = false; +static bool arg_read_only = false; +static StartMode arg_start_mode = START_PID1; +static bool arg_ephemeral = false; +static LinkJournal arg_link_journal = LINK_AUTO; +static bool arg_link_journal_try = false; +static uint64_t arg_caps_retain = + (1ULL << CAP_AUDIT_CONTROL) | + (1ULL << CAP_AUDIT_WRITE) | + (1ULL << CAP_CHOWN) | + (1ULL << CAP_DAC_OVERRIDE) | + (1ULL << CAP_DAC_READ_SEARCH) | + (1ULL << CAP_FOWNER) | + (1ULL << CAP_FSETID) | + (1ULL << CAP_IPC_OWNER) | + (1ULL << CAP_KILL) | + (1ULL << CAP_LEASE) | + (1ULL << CAP_LINUX_IMMUTABLE) | + (1ULL << CAP_MKNOD) | + (1ULL << CAP_NET_BIND_SERVICE) | + (1ULL << CAP_NET_BROADCAST) | + (1ULL << CAP_NET_RAW) | + (1ULL << CAP_SETFCAP) | + (1ULL << CAP_SETGID) | + (1ULL << CAP_SETPCAP) | + (1ULL << CAP_SETUID) | + (1ULL << CAP_SYS_ADMIN) | + (1ULL << CAP_SYS_BOOT) | + (1ULL << CAP_SYS_CHROOT) | + (1ULL << CAP_SYS_NICE) | + (1ULL << CAP_SYS_PTRACE) | + (1ULL << CAP_SYS_RESOURCE) | + (1ULL << CAP_SYS_TTY_CONFIG); +static CustomMount *arg_custom_mounts = NULL; +static unsigned arg_n_custom_mounts = 0; +static char **arg_setenv = NULL; +static bool arg_quiet = false; +static bool arg_share_system = false; +static bool arg_register = true; +static bool arg_keep_unit = false; +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 char *arg_network_zone = NULL; +static unsigned long arg_personality = PERSONALITY_INVALID; +static char *arg_image = NULL; +static VolatileMode arg_volatile_mode = VOLATILE_NO; +static ExposePort *arg_expose_ports = NULL; +static char **arg_property = NULL; +static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; +static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; +static bool arg_userns_chown = false; +static int arg_kill_signal = 0; +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 bool arg_notify_ready = false; + +static void help(void) { + printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" + "Spawn a minimal namespace container for debugging, testing and building.\n\n" + " -h --help Show this help\n" + " --version Print version string\n" + " -q --quiet Do not show status information\n" + " -D --directory=PATH Root directory for the container\n" + " --template=PATH Initialize root directory from template directory,\n" + " if missing\n" + " -x --ephemeral Run container with snapshot of root directory, and\n" + " remove it after exit\n" + " -i --image=PATH File system device or disk image for the container\n" + " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" + " -b --boot Boot up full system (i.e. invoke init)\n" + " --chdir=PATH Set working directory in the container\n" + " -u --user=USER Run the command under specified user or uid\n" + " -M --machine=NAME Set the machine name for the container\n" + " --uuid=UUID Set a specific machine UUID for the container\n" + " -S --slice=SLICE Place the container in the specified slice\n" + " --property=NAME=VALUE Set scope unit property\n" + " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" + " --private-users[=UIDBASE[:NUIDS]]\n" + " Run within user namespace, user configured UID/GID range\n" + " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" + " --private-network Disable network in container\n" + " --network-interface=INTERFACE\n" + " Assign an existing network interface to the\n" + " container\n" + " --network-macvlan=INTERFACE\n" + " Create a macvlan network interface based on an\n" + " existing network interface to the container\n" + " --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" + " 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" + " and container and add it to an existing bridge on\n" + " the host\n" + " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" + " and add it to an automatically managed bridge interface\n" + " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" + " Expose a container IP port on the host\n" + " -Z --selinux-context=SECLABEL\n" + " Set the SELinux security context to be used by\n" + " processes in the container\n" + " -L --selinux-apifs-context=SECLABEL\n" + " Set the SELinux security context to be used by\n" + " API/tmpfs file systems in the container\n" + " --capability=CAP In addition to the default, retain specified\n" + " capability\n" + " --drop-capability=CAP Drop the specified capability from the default set\n" + " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" + " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" + " host, try-guest, try-host\n" + " -j Equivalent to --link-journal=try-guest\n" + " --read-only Mount the root directory read-only\n" + " --bind=PATH[:PATH[:OPTIONS]]\n" + " Bind mount a file or directory from the host into\n" + " the container\n" + " --bind-ro=PATH[:PATH[:OPTIONS]\n" + " Similar, but creates a read-only bind mount\n" + " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" + " --overlay=PATH[:PATH...]:PATH\n" + " Create an overlay mount from the host to \n" + " the container\n" + " --overlay-ro=PATH[:PATH...]:PATH\n" + " Similar, but creates a read-only overlay mount\n" + " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" + " --share-system Share system namespaces with host\n" + " --register=BOOLEAN Register container as machine\n" + " --keep-unit Do not register a scope for the machine, reuse\n" + " the service unit nspawn is running in\n" + " --volatile[=MODE] Run the system in volatile mode\n" + " --settings=BOOLEAN Load additional settings from .nspawn file\n" + " --notify-ready=BOOLEAN Receive notifications from the container's init process,\n" + " accepted values: yes and no\n" + , program_invocation_short_name); +} + +static int custom_mounts_prepare(void) { + unsigned i; + int r; + + /* Ensure the mounts are applied prefix first. */ + qsort_safe(arg_custom_mounts, arg_n_custom_mounts, sizeof(CustomMount), custom_mount_compare); + + /* Allocate working directories for the overlay file systems that need it */ + for (i = 0; i < arg_n_custom_mounts; i++) { + CustomMount *m = &arg_custom_mounts[i]; + + if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) { + + if (arg_userns_chown) { + log_error("--private-users-chown may not be combined with custom root mounts."); + return -EINVAL; + } else if (arg_uid_shift == UID_INVALID) { + log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); + return -EINVAL; + } + } + + if (m->type != CUSTOM_MOUNT_OVERLAY) + continue; + + if (m->work_dir) + continue; + + if (m->read_only) + continue; + + r = tempfn_random(m->source, NULL, &m->work_dir); + if (r < 0) + return log_error_errno(r, "Failed to generate work directory from %s: %m", m->source); + } + + return 0; +} + +static int detect_unified_cgroup_hierarchy(void) { + const char *e; + int r; + + /* Allow the user to control whether the unified hierarchy is used */ + e = getenv("UNIFIED_CGROUP_HIERARCHY"); + if (e) { + r = parse_boolean(e); + if (r < 0) + return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY."); + + arg_unified_cgroup_hierarchy = r; + return 0; + } + + /* Otherwise inherit the default from the host system */ + r = cg_unified(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); + + arg_unified_cgroup_hierarchy = r; + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_PRIVATE_NETWORK, + ARG_UUID, + ARG_READ_ONLY, + ARG_CAPABILITY, + ARG_DROP_CAPABILITY, + ARG_LINK_JOURNAL, + ARG_BIND, + ARG_BIND_RO, + ARG_TMPFS, + ARG_OVERLAY, + ARG_OVERLAY_RO, + ARG_SHARE_SYSTEM, + ARG_REGISTER, + ARG_KEEP_UNIT, + ARG_NETWORK_INTERFACE, + ARG_NETWORK_MACVLAN, + ARG_NETWORK_IPVLAN, + ARG_NETWORK_BRIDGE, + ARG_NETWORK_ZONE, + ARG_NETWORK_VETH_EXTRA, + ARG_PERSONALITY, + ARG_VOLATILE, + ARG_TEMPLATE, + ARG_PROPERTY, + ARG_PRIVATE_USERS, + ARG_KILL_SIGNAL, + ARG_SETTINGS, + ARG_CHDIR, + ARG_PRIVATE_USERS_CHOWN, + ARG_NOTIFY_READY, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "directory", required_argument, NULL, 'D' }, + { "template", required_argument, NULL, ARG_TEMPLATE }, + { "ephemeral", no_argument, NULL, 'x' }, + { "user", required_argument, NULL, 'u' }, + { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, + { "as-pid2", no_argument, NULL, 'a' }, + { "boot", no_argument, NULL, 'b' }, + { "uuid", required_argument, NULL, ARG_UUID }, + { "read-only", no_argument, NULL, ARG_READ_ONLY }, + { "capability", required_argument, NULL, ARG_CAPABILITY }, + { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, + { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, + { "bind", required_argument, NULL, ARG_BIND }, + { "bind-ro", required_argument, NULL, ARG_BIND_RO }, + { "tmpfs", required_argument, NULL, ARG_TMPFS }, + { "overlay", required_argument, NULL, ARG_OVERLAY }, + { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, + { "machine", required_argument, NULL, 'M' }, + { "slice", required_argument, NULL, 'S' }, + { "setenv", required_argument, NULL, 'E' }, + { "selinux-context", required_argument, NULL, 'Z' }, + { "selinux-apifs-context", required_argument, NULL, 'L' }, + { "quiet", no_argument, NULL, 'q' }, + { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, + { "register", required_argument, NULL, ARG_REGISTER }, + { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, + { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, + { "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 }, + { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, + { "personality", required_argument, NULL, ARG_PERSONALITY }, + { "image", required_argument, NULL, 'i' }, + { "volatile", optional_argument, NULL, ARG_VOLATILE }, + { "port", required_argument, NULL, 'p' }, + { "property", required_argument, NULL, ARG_PROPERTY }, + { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, + { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN}, + { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, + { "settings", required_argument, NULL, ARG_SETTINGS }, + { "chdir", required_argument, NULL, ARG_CHDIR }, + { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, + {} + }; + + int c, r; + const char *p, *e; + uint64_t plus = 0, minus = 0; + bool mask_all_settings = false, mask_no_settings = false; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case 'D': + r = parse_path_argument_and_warn(optarg, false, &arg_directory); + if (r < 0) + return r; + break; + + case ARG_TEMPLATE: + r = parse_path_argument_and_warn(optarg, false, &arg_template); + if (r < 0) + return r; + break; + + case 'i': + r = parse_path_argument_and_warn(optarg, false, &arg_image); + if (r < 0) + return r; + break; + + case 'x': + arg_ephemeral = true; + break; + + case 'u': + r = free_and_strdup(&arg_user, optarg); + if (r < 0) + return log_oom(); + + arg_settings_mask |= SETTING_USER; + break; + + case ARG_NETWORK_ZONE: { + char *j; + + j = strappend("vz-", optarg); + if (!j) + return log_oom(); + + if (!ifname_valid(j)) { + log_error("Network zone name not valid: %s", j); + free(j); + return -EINVAL; + } + + free(arg_network_zone); + arg_network_zone = j; + + arg_network_veth = true; + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + } + + case ARG_NETWORK_BRIDGE: + + if (!ifname_valid(optarg)) { + log_error("Bridge interface name not valid: %s", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_network_bridge, optarg); + if (r < 0) + return log_oom(); + + /* fall through */ + + case 'n': + arg_network_veth = true; + arg_private_network = true; + 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 (!ifname_valid(optarg)) { + log_error("Network interface name not valid: %s", optarg); + return -EINVAL; + } + + if (strv_extend(&arg_network_interfaces, optarg) < 0) + return log_oom(); + + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + + case ARG_NETWORK_MACVLAN: + + if (!ifname_valid(optarg)) { + log_error("MACVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + + if (strv_extend(&arg_network_macvlan, optarg) < 0) + return log_oom(); + + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + + case ARG_NETWORK_IPVLAN: + + if (!ifname_valid(optarg)) { + log_error("IPVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + + if (strv_extend(&arg_network_ipvlan, optarg) < 0) + return log_oom(); + + /* fall through */ + + case ARG_PRIVATE_NETWORK: + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + + case 'b': + if (arg_start_mode == START_PID2) { + log_error("--boot and --as-pid2 may not be combined."); + return -EINVAL; + } + + arg_start_mode = START_BOOT; + arg_settings_mask |= SETTING_START_MODE; + break; + + case 'a': + if (arg_start_mode == START_BOOT) { + log_error("--boot and --as-pid2 may not be combined."); + return -EINVAL; + } + + arg_start_mode = START_PID2; + arg_settings_mask |= SETTING_START_MODE; + break; + + case ARG_UUID: + r = sd_id128_from_string(optarg, &arg_uuid); + if (r < 0) + return log_error_errno(r, "Invalid UUID: %s", optarg); + + if (sd_id128_is_null(arg_uuid)) { + log_error("Machine UUID may not be all zeroes."); + return -EINVAL; + } + + arg_settings_mask |= SETTING_MACHINE_ID; + break; + + case 'S': + arg_slice = optarg; + break; + + case 'M': + if (isempty(optarg)) + arg_machine = mfree(arg_machine); + else { + if (!machine_name_is_valid(optarg)) { + log_error("Invalid machine name: %s", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_machine, optarg); + if (r < 0) + return log_oom(); + + break; + } + + case 'Z': + arg_selinux_context = optarg; + break; + + case 'L': + arg_selinux_apifs_context = optarg; + break; + + case ARG_READ_ONLY: + arg_read_only = true; + arg_settings_mask |= SETTING_READ_ONLY; + break; + + case ARG_CAPABILITY: + case ARG_DROP_CAPABILITY: { + p = optarg; + for (;;) { + _cleanup_free_ char *t = NULL; + + r = extract_first_word(&p, &t, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse capability %s.", t); + + if (r == 0) + break; + + if (streq(t, "all")) { + if (c == ARG_CAPABILITY) + plus = (uint64_t) -1; + else + minus = (uint64_t) -1; + } else { + int cap; + + cap = capability_from_name(t); + if (cap < 0) { + log_error("Failed to parse capability %s.", t); + return -EINVAL; + } + + if (c == ARG_CAPABILITY) + plus |= 1ULL << (uint64_t) cap; + else + minus |= 1ULL << (uint64_t) cap; + } + } + + arg_settings_mask |= SETTING_CAPABILITY; + break; + } + + case 'j': + arg_link_journal = LINK_GUEST; + arg_link_journal_try = true; + break; + + case ARG_LINK_JOURNAL: + if (streq(optarg, "auto")) { + arg_link_journal = LINK_AUTO; + arg_link_journal_try = false; + } else if (streq(optarg, "no")) { + arg_link_journal = LINK_NO; + arg_link_journal_try = false; + } else if (streq(optarg, "guest")) { + arg_link_journal = LINK_GUEST; + arg_link_journal_try = false; + } else if (streq(optarg, "host")) { + arg_link_journal = LINK_HOST; + arg_link_journal_try = false; + } else if (streq(optarg, "try-guest")) { + arg_link_journal = LINK_GUEST; + arg_link_journal_try = true; + } else if (streq(optarg, "try-host")) { + arg_link_journal = LINK_HOST; + arg_link_journal_try = true; + } else { + log_error("Failed to parse link journal mode %s", optarg); + return -EINVAL; + } + + break; + + case ARG_BIND: + case ARG_BIND_RO: + r = bind_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_BIND_RO); + if (r < 0) + return log_error_errno(r, "Failed to parse --bind(-ro)= argument %s: %m", optarg); + + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; + break; + + case ARG_TMPFS: + r = tmpfs_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --tmpfs= argument %s: %m", optarg); + + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; + break; + + case ARG_OVERLAY: + case ARG_OVERLAY_RO: { + _cleanup_free_ char *upper = NULL, *destination = NULL; + _cleanup_strv_free_ char **lower = NULL; + CustomMount *m; + unsigned n = 0; + char **i; + + r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); + else if (r < 0) { + log_error("Invalid overlay specification: %s", optarg); + return r; + } + + STRV_FOREACH(i, lower) { + if (!path_is_absolute(*i)) { + log_error("Overlay path %s is not absolute.", *i); + return -EINVAL; + } + + n++; + } + + if (n < 2) { + log_error("--overlay= needs at least two colon-separated directories specified."); + return -EINVAL; + } + + if (n == 2) { + /* If two parameters are specified, + * the first one is the lower, the + * second one the upper directory. And + * we'll also define the destination + * mount point the same as the upper. */ + upper = lower[1]; + lower[1] = NULL; + + destination = strdup(upper); + if (!destination) + return log_oom(); + + } else { + upper = lower[n - 2]; + destination = lower[n - 1]; + lower[n - 2] = NULL; + } + + m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY); + if (!m) + return log_oom(); + + m->destination = destination; + m->source = upper; + m->lower = lower; + m->read_only = c == ARG_OVERLAY_RO; + + upper = destination = NULL; + lower = NULL; + + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; + break; + } + + case 'E': { + char **n; + + if (!env_assignment_is_valid(optarg)) { + log_error("Environment variable assignment '%s' is not valid.", optarg); + return -EINVAL; + } + + n = strv_env_set(arg_setenv, optarg); + if (!n) + return log_oom(); + + strv_free(arg_setenv); + arg_setenv = n; + + arg_settings_mask |= SETTING_ENVIRONMENT; + break; + } + + case 'q': + arg_quiet = true; + break; + + case ARG_SHARE_SYSTEM: + arg_share_system = true; + break; + + case ARG_REGISTER: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --register= argument: %s", optarg); + return r; + } + + arg_register = r; + break; + + case ARG_KEEP_UNIT: + arg_keep_unit = true; + break; + + case ARG_PERSONALITY: + + arg_personality = personality_from_string(optarg); + if (arg_personality == PERSONALITY_INVALID) { + log_error("Unknown or unsupported personality '%s'.", optarg); + return -EINVAL; + } + + arg_settings_mask |= SETTING_PERSONALITY; + break; + + case ARG_VOLATILE: + + if (!optarg) + arg_volatile_mode = VOLATILE_YES; + else { + VolatileMode m; + + m = volatile_mode_from_string(optarg); + if (m < 0) { + log_error("Failed to parse --volatile= argument: %s", optarg); + return -EINVAL; + } else + arg_volatile_mode = m; + } + + arg_settings_mask |= SETTING_VOLATILE_MODE; + break; + + case 'p': + r = expose_port_parse(&arg_expose_ports, optarg); + if (r == -EEXIST) + return log_error_errno(r, "Duplicate port specification: %s", optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse host port %s: %m", optarg); + + arg_settings_mask |= SETTING_EXPOSE_PORTS; + break; + + case ARG_PROPERTY: + if (strv_extend(&arg_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_PRIVATE_USERS: + + r = optarg ? parse_boolean(optarg) : 1; + if (r == 0) { + /* no: User namespacing off */ + arg_userns_mode = USER_NAMESPACE_NO; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + arg_userns_mode = USER_NAMESPACE_FIXED; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (streq(optarg, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else { + _cleanup_free_ char *buffer = NULL; + const char *range, *shift; + + /* anything else: User namespacing on, UID range is explicitly configured */ + + range = strchr(optarg, ':'); + if (range) { + buffer = strndup(optarg, range - optarg); + if (!buffer) + return log_oom(); + shift = buffer; + + range++; + if (safe_atou32(range, &arg_uid_range) < 0 || arg_uid_range <= 0) { + log_error("Failed to parse UID range: %s", range); + return -EINVAL; + } + } else + shift = optarg; + + if (parse_uid(shift, &arg_uid_shift) < 0) { + log_error("Failed to parse UID: %s", optarg); + return -EINVAL; + } + + arg_userns_mode = USER_NAMESPACE_FIXED; + } + + arg_settings_mask |= SETTING_USERNS; + break; + + case 'U': + if (userns_supported()) { + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + + arg_settings_mask |= SETTING_USERNS; + } + + break; + + case ARG_PRIVATE_USERS_CHOWN: + arg_userns_chown = true; + + arg_settings_mask |= SETTING_USERNS; + break; + + case ARG_KILL_SIGNAL: + arg_kill_signal = signal_from_string_try_harder(optarg); + if (arg_kill_signal < 0) { + log_error("Cannot parse signal: %s", optarg); + return -EINVAL; + } + + arg_settings_mask |= SETTING_KILL_SIGNAL; + break; + + case ARG_SETTINGS: + + /* no → do not read files + * yes → read files, do not override cmdline, trust only subset + * override → read files, override cmdline, trust only subset + * trusted → read files, do not override cmdline, trust all + */ + + r = parse_boolean(optarg); + if (r < 0) { + if (streq(optarg, "trusted")) { + mask_all_settings = false; + mask_no_settings = false; + arg_settings_trusted = true; + + } else if (streq(optarg, "override")) { + mask_all_settings = false; + mask_no_settings = true; + arg_settings_trusted = -1; + } else + return log_error_errno(r, "Failed to parse --settings= argument: %s", optarg); + } else if (r > 0) { + /* yes */ + mask_all_settings = false; + mask_no_settings = false; + arg_settings_trusted = -1; + } else { + /* no */ + mask_all_settings = true; + mask_no_settings = false; + arg_settings_trusted = false; + } + + break; + + case ARG_CHDIR: + if (!path_is_absolute(optarg)) { + log_error("Working directory %s is not an absolute path.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_chdir, optarg); + if (r < 0) + return log_oom(); + + arg_settings_mask |= SETTING_WORKING_DIRECTORY; + break; + + case ARG_NOTIFY_READY: + r = parse_boolean(optarg); + if (r < 0) { + log_error("%s is not a valid notify mode. Valid modes are: yes, no, and ready.", optarg); + return -EINVAL; + } + arg_notify_ready = r; + arg_settings_mask |= SETTING_NOTIFY_READY; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (arg_share_system) + arg_register = false; + + if (arg_userns_mode == USER_NAMESPACE_PICK) + arg_userns_chown = true; + + if (arg_start_mode != START_PID1 && arg_share_system) { + log_error("--boot and --share-system may not be combined."); + return -EINVAL; + } + + if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) { + log_error("--keep-unit may not be used when invoked from a user session."); + return -EINVAL; + } + + if (arg_directory && arg_image) { + log_error("--directory= and --image= may not be combined."); + return -EINVAL; + } + + if (arg_template && arg_image) { + log_error("--template= and --image= may not be combined."); + return -EINVAL; + } + + if (arg_template && !(arg_directory || arg_machine)) { + log_error("--template= needs --directory= or --machine=."); + return -EINVAL; + } + + if (arg_ephemeral && arg_template) { + log_error("--ephemeral and --template= may not be combined."); + return -EINVAL; + } + + if (arg_ephemeral && arg_image) { + log_error("--ephemeral and --image= may not be combined."); + return -EINVAL; + } + + if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO)) { + log_error("--ephemeral and --link-journal= may not be combined."); + return -EINVAL; + } + + if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) { + log_error("--private-users= is not supported, kernel compiled without user namespace support."); + return -EOPNOTSUPP; + } + + if (arg_userns_chown && arg_read_only) { + log_error("--read-only and --private-users-chown may not be combined."); + return -EINVAL; + } + + if (arg_network_bridge && arg_network_zone) { + log_error("--network-bridge= and --network-zone= may not be combined."); + return -EINVAL; + } + + if (argc > optind) { + arg_parameters = strv_copy(argv + optind); + if (!arg_parameters) + return log_oom(); + + arg_settings_mask |= SETTING_START_MODE; + } + + /* Load all settings from .nspawn files */ + if (mask_no_settings) + arg_settings_mask = 0; + + /* Don't load any settings from .nspawn files */ + if (mask_all_settings) + arg_settings_mask = _SETTINGS_MASK_ALL; + + arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; + + r = detect_unified_cgroup_hierarchy(); + if (r < 0) + return r; + + e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE"); + if (e) + arg_container_service_name = e; + + return 1; +} + +static int verify_arguments(void) { + + if (arg_volatile_mode != VOLATILE_NO && arg_read_only) { + log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy."); + return -EINVAL; + } + + if (arg_expose_ports && !arg_private_network) { + log_error("Cannot use --port= without private networking."); + return -EINVAL; + } + +#ifndef HAVE_LIBIPTC + if (arg_expose_ports) { + log_error("--port= is not supported, compiled without libiptc support."); + return -EOPNOTSUPP; + } +#endif + + if (arg_start_mode == START_BOOT && arg_kill_signal <= 0) + arg_kill_signal = SIGRTMIN+3; + + return 0; +} + +static int userns_lchown(const char *p, uid_t uid, gid_t gid) { + assert(p); + + if (arg_userns_mode == USER_NAMESPACE_NO) + return 0; + + if (uid == UID_INVALID && gid == GID_INVALID) + return 0; + + if (uid != UID_INVALID) { + uid += arg_uid_shift; + + if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range) + return -EOVERFLOW; + } + + if (gid != GID_INVALID) { + gid += (gid_t) arg_uid_shift; + + if (gid < (gid_t) arg_uid_shift || gid >= (gid_t) (arg_uid_shift + arg_uid_range)) + return -EOVERFLOW; + } + + if (lchown(p, uid, gid) < 0) + return -errno; + + return 0; +} + +static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) { + const char *q; + + q = prefix_roota(root, path); + if (mkdir(q, mode) < 0) { + if (errno == EEXIST) + return 0; + return -errno; + } + + return userns_lchown(q, uid, gid); +} + +static int setup_timezone(const char *dest) { + _cleanup_free_ char *p = NULL, *q = NULL; + const char *where, *check, *what; + char *z, *y; + int r; + + assert(dest); + + /* Fix the timezone, if possible */ + r = readlink_malloc("/etc/localtime", &p); + if (r < 0) { + log_warning("/etc/localtime is not a symlink, not updating container timezone."); + return 0; + } + + z = path_startswith(p, "../usr/share/zoneinfo/"); + if (!z) + z = path_startswith(p, "/usr/share/zoneinfo/"); + if (!z) { + log_warning("/etc/localtime does not point into /usr/share/zoneinfo/, not updating container timezone."); + return 0; + } + + where = prefix_roota(dest, "/etc/localtime"); + r = readlink_malloc(where, &q); + if (r >= 0) { + y = path_startswith(q, "../usr/share/zoneinfo/"); + if (!y) + y = path_startswith(q, "/usr/share/zoneinfo/"); + + /* Already pointing to the right place? Then do nothing .. */ + if (y && streq(y, z)) + return 0; + } + + check = strjoina("/usr/share/zoneinfo/", z); + check = prefix_roota(dest, check); + if (laccess(check, F_OK) < 0) { + log_warning("Timezone %s does not exist in container, not updating container timezone.", z); + return 0; + } + + r = unlink(where); + if (r < 0 && errno != ENOENT) { + log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where); + return 0; + } + + what = strjoina("../usr/share/zoneinfo/", z); + if (symlink(what, where) < 0) { + log_error_errno(errno, "Failed to correct timezone of container: %m"); + return 0; + } + + r = userns_lchown(where, 0, 0); + if (r < 0) + return log_warning_errno(r, "Failed to chown /etc/localtime: %m"); + + return 0; +} + +static int setup_resolv_conf(const char *dest) { + const char *where = NULL; + int r; + + assert(dest); + + if (arg_private_network) + return 0; + + /* Fix resolv.conf, if possible */ + where = prefix_roota(dest, "/etc/resolv.conf"); + + r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0); + if (r < 0) { + /* If the file already exists as symlink, let's + * suppress the warning, under the assumption that + * resolved or something similar runs inside and the + * symlink points there. + * + * If the disk image is read-only, there's also no + * point in complaining. + */ + log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to copy /etc/resolv.conf to %s: %m", where); + return 0; + } + + r = userns_lchown(where, 0, 0); + if (r < 0) + log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m"); + + return 0; +} + +static int setup_boot_id(const char *dest) { + sd_id128_t rnd = SD_ID128_NULL; + const char *from, *to; + int r; + + if (arg_share_system) + return 0; + + /* Generate a new randomized boot ID, so that each boot-up of + * the container gets a new one */ + + from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id"); + to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id"); + + r = sd_id128_randomize(&rnd); + if (r < 0) + return log_error_errno(r, "Failed to generate random boot id: %m"); + + r = id128_write(from, ID128_UUID, rnd, false); + if (r < 0) + return log_error_errno(r, "Failed to write boot id: %m"); + + if (mount(from, to, NULL, MS_BIND, NULL) < 0) + r = log_error_errno(errno, "Failed to bind mount boot id: %m"); + else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) + log_warning_errno(errno, "Failed to make boot id read-only, ignoring: %m"); + + (void) unlink(from); + return r; +} + +static int copy_devnodes(const char *dest) { + + static const char devnodes[] = + "null\0" + "zero\0" + "full\0" + "random\0" + "urandom\0" + "tty\0" + "net/tun\0"; + + const char *d; + int r = 0; + _cleanup_umask_ mode_t u; + + assert(dest); + + u = umask(0000); + + /* Create /dev/net, so that we can create /dev/net/tun in it */ + if (userns_mkdir(dest, "/dev/net", 0755, 0, 0) < 0) + return log_error_errno(r, "Failed to create /dev/net directory: %m"); + + NULSTR_FOREACH(d, devnodes) { + _cleanup_free_ char *from = NULL, *to = NULL; + struct stat st; + + from = strappend("/dev/", d); + to = prefix_root(dest, from); + + if (stat(from, &st) < 0) { + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to stat %s: %m", from); + + } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { + + log_error("%s is not a char or block device, cannot copy.", from); + return -EIO; + + } else { + if (mknod(to, st.st_mode, st.st_rdev) < 0) { + if (errno != EPERM) + return log_error_errno(errno, "mknod(%s) failed: %m", to); + + /* Some systems abusively restrict mknod but + * allow bind mounts. */ + r = touch(to); + if (r < 0) + return log_error_errno(r, "touch (%s) failed: %m", to); + if (mount(from, to, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Both mknod and bind mount (%s) failed: %m", to); + } + + r = userns_lchown(to, 0, 0); + if (r < 0) + return log_error_errno(r, "chown() of device node %s failed: %m", to); + } + } + + return r; +} + +static int setup_pts(const char *dest) { + _cleanup_free_ char *options = NULL; + const char *p; + int r; + +#ifdef HAVE_SELINUX + if (arg_selinux_apifs_context) + (void) asprintf(&options, + "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT ",context=\"%s\"", + arg_uid_shift + TTY_GID, + arg_selinux_apifs_context); + else +#endif + (void) asprintf(&options, + "newinstance,ptmxmode=0666,mode=620,gid=" GID_FMT, + arg_uid_shift + TTY_GID); + + if (!options) + return log_oom(); + + /* Mount /dev/pts itself */ + p = prefix_roota(dest, "/dev/pts"); + if (mkdir(p, 0755) < 0) + return log_error_errno(errno, "Failed to create /dev/pts: %m"); + if (mount("devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options) < 0) + return log_error_errno(errno, "Failed to mount /dev/pts: %m"); + 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"); + 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"); + r = userns_lchown(p, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to chown /dev/pts/ptmx: %m"); + + return 0; +} + +static int setup_dev_console(const char *dest, const char *console) { + _cleanup_umask_ mode_t u; + const char *to; + int r; + + assert(dest); + assert(console); + + u = umask(0000); + + r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift); + if (r < 0) + return log_error_errno(r, "Failed to correct access mode for TTY: %m"); + + /* We need to bind mount the right tty to /dev/console since + * ptys can only exist on pts file systems. To have something + * to bind mount things on we create a empty regular file. */ + + to = prefix_roota(dest, "/dev/console"); + r = touch(to); + if (r < 0) + return log_error_errno(r, "touch() for /dev/console failed: %m"); + + if (mount(console, to, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Bind mount for /dev/console failed: %m"); + + return 0; +} + +static int setup_kmsg(const char *dest, int kmsg_socket) { + const char *from, *to; + _cleanup_umask_ mode_t u; + int fd, r; + + assert(kmsg_socket >= 0); + + u = umask(0000); + + /* We create the kmsg FIFO as /run/kmsg, but immediately + * delete it after bind mounting it to /proc/kmsg. While FIFOs + * on the reading side behave very similar to /proc/kmsg, + * their writing side behaves differently from /dev/kmsg in + * that writing blocks when nothing is reading. In order to + * avoid any problems with containers deadlocking due to this + * we simply make /dev/kmsg unavailable to the container. */ + from = prefix_roota(dest, "/run/kmsg"); + to = prefix_roota(dest, "/proc/kmsg"); + + if (mkfifo(from, 0600) < 0) + return log_error_errno(errno, "mkfifo() for /run/kmsg failed: %m"); + if (mount(from, to, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Bind mount for /proc/kmsg failed: %m"); + + fd = open(from, O_RDWR|O_NDELAY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open fifo: %m"); + + /* 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, 0); + safe_close(fd); + + if (r < 0) + return log_error_errno(r, "Failed to send FIFO fd: %m"); + + /* And now make the FIFO unavailable as /run/kmsg... */ + (void) unlink(from); + + return 0; +} + +static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + union in_addr_union *exposed = userdata; + + assert(rtnl); + assert(m); + assert(exposed); + + expose_port_execute(rtnl, arg_expose_ports, exposed); + return 0; +} + +static int setup_hostname(void) { + + if (arg_share_system) + return 0; + + if (sethostname_idempotent(arg_machine) < 0) + return -errno; + + return 0; +} + +static int setup_journal(const char *directory) { + sd_id128_t this_id; + _cleanup_free_ char *d = NULL; + const char *p, *q; + bool try; + char id[33]; + int r; + + /* Don't link journals in ephemeral mode */ + if (arg_ephemeral) + return 0; + + if (arg_link_journal == LINK_NO) + return 0; + + try = arg_link_journal_try || arg_link_journal == LINK_AUTO; + + r = sd_id128_get_machine(&this_id); + if (r < 0) + return log_error_errno(r, "Failed to retrieve machine ID: %m"); + + if (sd_id128_equal(arg_uuid, this_id)) { + log_full(try ? LOG_WARNING : LOG_ERR, + "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id)); + if (try) + return 0; + return -EEXIST; + } + + r = userns_mkdir(directory, "/var", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /var: %m"); + + r = userns_mkdir(directory, "/var/log", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /var/log: %m"); + + r = userns_mkdir(directory, "/var/log/journal", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /var/log/journal: %m"); + + (void) sd_id128_to_string(arg_uuid, id); + + p = strjoina("/var/log/journal/", id); + q = prefix_roota(directory, p); + + if (path_is_mount_point(p, 0) > 0) { + if (try) + return 0; + + log_error("%s: already a mount point, refusing to use for journal", p); + return -EEXIST; + } + + if (path_is_mount_point(q, 0) > 0) { + if (try) + return 0; + + log_error("%s: already a mount point, refusing to use for journal", q); + return -EEXIST; + } + + r = readlink_and_make_absolute(p, &d); + if (r >= 0) { + if ((arg_link_journal == LINK_GUEST || + arg_link_journal == LINK_AUTO) && + path_equal(d, q)) { + + r = userns_mkdir(directory, p, 0755, 0, 0); + if (r < 0) + log_warning_errno(r, "Failed to create directory %s: %m", q); + return 0; + } + + if (unlink(p) < 0) + return log_error_errno(errno, "Failed to remove symlink %s: %m", p); + } else if (r == -EINVAL) { + + if (arg_link_journal == LINK_GUEST && + rmdir(p) < 0) { + + if (errno == ENOTDIR) { + log_error("%s already exists and is neither a symlink nor a directory", p); + return r; + } else + return log_error_errno(errno, "Failed to remove %s: %m", p); + } + } else if (r != -ENOENT) + return log_error_errno(r, "readlink(%s) failed: %m", p); + + if (arg_link_journal == LINK_GUEST) { + + if (symlink(q, p) < 0) { + if (try) { + log_debug_errno(errno, "Failed to symlink %s to %s, skipping journal setup: %m", q, p); + return 0; + } 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(r, "Failed to create directory %s: %m", q); + return 0; + } + + if (arg_link_journal == LINK_HOST) { + /* don't create parents here — if the host doesn't have + * permanent journal set up, don't force it here */ + + if (mkdir(p, 0755) < 0 && errno != EEXIST) { + if (try) { + log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p); + return 0; + } else + return log_error_errno(errno, "Failed to create %s: %m", p); + } + + } else if (access(p, F_OK) < 0) + return 0; + + if (dir_is_empty(q) == 0) + log_warning("%s is not empty, proceeding anyway.", q); + + r = userns_mkdir(directory, p, 0755, 0, 0); + 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"); + + return 0; +} + +static int drop_capabilities(void) { + return capability_bounding_set_drop(arg_caps_retain, false); +} + +static int reset_audit_loginuid(void) { + _cleanup_free_ char *p = NULL; + int r; + + if (arg_share_system) + return 0; + + r = read_one_line_file("/proc/self/loginuid", &p); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to read /proc/self/loginuid: %m"); + + /* Already reset? */ + if (streq(p, "4294967295")) + return 0; + + r = write_string_file("/proc/self/loginuid", "4294967295", 0); + if (r < 0) { + log_error_errno(r, + "Failed to reset audit login UID. This probably means that your kernel is too\n" + "old and you have audit enabled. Note that the auditing subsystem is known to\n" + "be incompatible with containers on old kernels. Please make sure to upgrade\n" + "your kernel or to off auditing with 'audit=0' on the kernel command line before\n" + "using systemd-nspawn. Sleeping for 5s... (%m)"); + + sleep(5); + } + + return 0; +} + + +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); + + r = userns_mkdir(root, "/run/systemd", 0755, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to create /run/systemd: %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"); + + 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) + return log_error_errno(errno, "Failed to install propagation bind mount."); + + if (mount(NULL, q, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to make propagation mount read-only"); + + return 0; +} + +static int setup_image(char **device_path, int *loop_nr) { + struct loop_info64 info = { + .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN + }; + _cleanup_close_ int fd = -1, control = -1, loop = -1; + _cleanup_free_ char* loopdev = NULL; + struct stat st; + int r, nr; + + assert(device_path); + assert(loop_nr); + assert(arg_image); + + fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", arg_image); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", arg_image); + + if (S_ISBLK(st.st_mode)) { + char *p; + + p = strdup(arg_image); + if (!p) + return log_oom(); + + *device_path = p; + + *loop_nr = -1; + + r = fd; + fd = -1; + + return r; + } + + if (!S_ISREG(st.st_mode)) { + log_error("%s is not a regular file or block device.", arg_image); + return -EINVAL; + } + + control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (control < 0) + return log_error_errno(errno, "Failed to open /dev/loop-control: %m"); + + nr = ioctl(control, LOOP_CTL_GET_FREE); + if (nr < 0) + return log_error_errno(errno, "Failed to allocate loop device: %m"); + + if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) + return log_oom(); + + loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); + if (loop < 0) + return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev); + + if (ioctl(loop, LOOP_SET_FD, fd) < 0) + return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev); + + if (arg_read_only) + info.lo_flags |= LO_FLAGS_READ_ONLY; + + if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) + return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev); + + *device_path = loopdev; + loopdev = NULL; + + *loop_nr = nr; + + r = loop; + loop = -1; + + return r; +} + +#define PARTITION_TABLE_BLURB \ + "Note that the disk image needs to either contain only a single MBR partition of\n" \ + "type 0x83 that is marked bootable, or a single GPT partition of type " \ + "0FC63DAF-8483-4772-8E79-3D69D8477DE4 or follow\n" \ + " http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \ + "to be bootable with systemd-nspawn." + +static int dissect_image( + int fd, + char **root_device, bool *root_device_rw, + char **home_device, bool *home_device_rw, + char **srv_device, bool *srv_device_rw, + bool *secondary) { + +#ifdef HAVE_BLKID + int home_nr = -1, srv_nr = -1; +#ifdef GPT_ROOT_NATIVE + int root_nr = -1; +#endif +#ifdef GPT_ROOT_SECONDARY + int secondary_root_nr = -1; +#endif + _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL, *generic = NULL; + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + struct udev_list_entry *first, *item; + bool home_rw = true, root_rw = true, secondary_root_rw = true, srv_rw = true, generic_rw = true; + bool is_gpt, is_mbr, multiple_generic = false; + const char *pttype = NULL; + blkid_partlist pl; + struct stat st; + unsigned i; + int r; + + assert(fd >= 0); + assert(root_device); + assert(home_device); + assert(srv_device); + assert(secondary); + assert(arg_image); + + b = blkid_new_probe(); + if (!b) + return log_oom(); + + errno = 0; + r = blkid_probe_set_device(b, fd, 0, 0); + if (r != 0) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "Failed to set device on blkid probe: %m"); + } + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) { + log_error("Failed to identify any partition table on\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; + } else if (r != 0) { + if (errno == 0) + errno = EIO; + return log_error_errno(errno, "Failed to probe: %m"); + } + + (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); + + is_gpt = streq_ptr(pttype, "gpt"); + is_mbr = streq_ptr(pttype, "dos"); + + if (!is_gpt && !is_mbr) { + log_error("No GPT or MBR partition table discovered on\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; + } + + errno = 0; + pl = blkid_probe_get_partitions(b); + if (!pl) { + if (errno == 0) + return log_oom(); + + log_error("Failed to list partitions of %s", arg_image); + return -errno; + } + + udev = udev_new(); + if (!udev) + return log_oom(); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat block device: %m"); + + d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); + if (!d) + return log_oom(); + + for (i = 0;; i++) { + int n, m; + + if (i >= 10) { + log_error("Kernel partitions never appeared."); + return -ENXIO; + } + + e = udev_enumerate_new(udev); + if (!e) + return log_oom(); + + r = udev_enumerate_add_match_parent(e, d); + if (r < 0) + return log_oom(); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image); + + /* Count the partitions enumerated by the kernel */ + n = 0; + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) + n++; + + /* Count the partitions enumerated by blkid */ + m = blkid_partlist_numof_partitions(pl); + if (n == m + 1) + break; + if (n > m + 1) { + log_error("blkid and kernel partition list do not match."); + return -EIO; + } + if (n < m + 1) { + unsigned j; + + /* The kernel has probed fewer partitions than + * blkid? Maybe the kernel prober is still + * running or it got EBUSY because udev + * already opened the device. Let's reprobe + * the device, which is a synchronous call + * that waits until probing is complete. */ + + for (j = 0; j < 20; j++) { + + r = ioctl(fd, BLKRRPART, 0); + if (r < 0) + r = -errno; + if (r >= 0 || r != -EBUSY) + break; + + /* If something else has the device + * open, such as an udev rule, the + * ioctl will return EBUSY. Since + * there's no way to wait until it + * isn't busy anymore, let's just wait + * a bit, and try again. + * + * This is really something they + * should fix in the kernel! */ + + usleep(50 * USEC_PER_MSEC); + } + + if (r < 0) + return log_error_errno(r, "Failed to reread partition table: %m"); + } + + e = udev_enumerate_unref(e); + } + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *q; + const char *node; + unsigned long long flags; + blkid_partition pp; + dev_t qn; + int nr; + + errno = 0; + q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!q) { + if (!errno) + errno = ENOMEM; + + return log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image); + } + + qn = udev_device_get_devnum(q); + if (major(qn) == 0) + continue; + + if (st.st_rdev == qn) + continue; + + node = udev_device_get_devnode(q); + if (!node) + continue; + + pp = blkid_partlist_devno_to_partition(pl, qn); + if (!pp) + continue; + + flags = blkid_partition_get_flags(pp); + + nr = blkid_partition_get_partno(pp); + if (nr < 0) + continue; + + if (is_gpt) { + sd_id128_t type_id; + const char *stype; + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + stype = blkid_partition_get_type_string(pp); + if (!stype) + continue; + + if (sd_id128_from_string(stype, &type_id) < 0) + continue; + + if (sd_id128_equal(type_id, GPT_HOME)) { + + if (home && nr >= home_nr) + continue; + + home_nr = nr; + home_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&home, node); + if (r < 0) + return log_oom(); + + } else if (sd_id128_equal(type_id, GPT_SRV)) { + + if (srv && nr >= srv_nr) + continue; + + srv_nr = nr; + srv_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&srv, node); + if (r < 0) + return log_oom(); + } +#ifdef GPT_ROOT_NATIVE + else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { + + if (root && nr >= root_nr) + continue; + + root_nr = nr; + root_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&root, node); + if (r < 0) + return log_oom(); + } +#endif +#ifdef GPT_ROOT_SECONDARY + else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) { + + if (secondary_root && nr >= secondary_root_nr) + continue; + + secondary_root_nr = nr; + secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&secondary_root, node); + if (r < 0) + return log_oom(); + } +#endif + else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) { + + if (generic) + multiple_generic = true; + else { + generic_rw = !(flags & GPT_FLAG_READ_ONLY); + + r = free_and_strdup(&generic, node); + if (r < 0) + return log_oom(); + } + } + + } else if (is_mbr) { + int type; + + if (flags != 0x80) /* Bootable flag */ + continue; + + type = blkid_partition_get_type(pp); + if (type != 0x83) /* Linux partition */ + continue; + + if (generic) + multiple_generic = true; + else { + generic_rw = true; + + r = free_and_strdup(&root, node); + if (r < 0) + return log_oom(); + } + } + } + + if (root) { + *root_device = root; + root = NULL; + + *root_device_rw = root_rw; + *secondary = false; + } else if (secondary_root) { + *root_device = secondary_root; + secondary_root = NULL; + + *root_device_rw = secondary_root_rw; + *secondary = true; + } else if (generic) { + + /* There were no partitions with precise meanings + * around, but we found generic partitions. In this + * case, if there's only one, we can go ahead and boot + * it, otherwise we bail out, because we really cannot + * make any sense of it. */ + + if (multiple_generic) { + log_error("Identified multiple bootable Linux partitions on\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; + } + + *root_device = generic; + generic = NULL; + + *root_device_rw = generic_rw; + *secondary = false; + } else { + log_error("Failed to identify root partition in disk image\n" + " %s\n" + PARTITION_TABLE_BLURB, arg_image); + return -EINVAL; + } + + if (home) { + *home_device = home; + home = NULL; + + *home_device_rw = home_rw; + } + + if (srv) { + *srv_device = srv; + srv = NULL; + + *srv_device_rw = srv_rw; + } + + return 0; +#else + log_error("--image= is not supported, compiled without blkid support."); + return -EOPNOTSUPP; +#endif +} + +static int mount_device(const char *what, const char *where, const char *directory, bool rw) { +#ifdef HAVE_BLKID + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype, *p; + int r; + + assert(what); + assert(where); + + if (arg_read_only) + rw = false; + + if (directory) + p = strjoina(where, directory); + else + p = where; + + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober for %s: %m", what); + } + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -1 || r == 1) { + log_error("Cannot determine file system type of %s", what); + return -EINVAL; + } else if (r != 0) { + if (errno == 0) + errno = EIO; + return log_error_errno(errno, "Failed to probe %s: %m", what); + } + + errno = 0; + if (blkid_probe_lookup_value(b, "TYPE", &fstype, NULL) < 0) { + if (errno == 0) + errno = EINVAL; + log_error("Failed to determine file system type of %s", what); + return -errno; + } + + if (streq(fstype, "crypto_LUKS")) { + log_error("nspawn currently does not support LUKS disk images."); + return -EOPNOTSUPP; + } + + if (mount(what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL) < 0) + return log_error_errno(errno, "Failed to mount %s: %m", what); + + return 0; +#else + log_error("--image= is not supported, compiled without blkid support."); + return -EOPNOTSUPP; +#endif +} + +static int setup_machine_id(const char *directory) { + const char *etc_machine_id; + sd_id128_t id; + int r; + + /* If the UUID in the container is already set, then that's what counts, and we use. If it isn't set, and the + * caller passed --uuid=, then we'll pass it in the $container_uuid env var to PID 1 of the container. The + * assumption is that PID 1 will then write it to /etc/machine-id to make it persistent. If --uuid= is not + * passed we generate a random UUID, and pass it via $container_uuid. In effect this means that /etc/machine-id + * in the container and our idea of the container UUID will always be in sync (at least if PID 1 in the + * container behaves nicely). */ + + etc_machine_id = prefix_roota(directory, "/etc/machine-id"); + + r = id128_read(etc_machine_id, ID128_PLAIN, &id); + if (r < 0) { + if (!IN_SET(r, -ENOENT, -ENOMEDIUM)) /* If the file is missing or empty, we don't mind */ + return log_error_errno(r, "Failed to read machine ID from container image: %m"); + + if (sd_id128_is_null(arg_uuid)) { + r = sd_id128_randomize(&arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to acquire randomized machine UUID: %m"); + } + } else { + if (sd_id128_is_null(id)) { + log_error("Machine ID in container image is zero, refusing."); + return -EINVAL; + } + + arg_uuid = id; + } + + return 0; +} + +static int recursive_chown(const char *directory, uid_t shift, uid_t range) { + int r; + + assert(directory); + + if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown) + return 0; + + r = path_patch_uid(directory, arg_uid_shift, arg_uid_range); + if (r == -EOPNOTSUPP) + return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16."); + if (r == -EBADE) + return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match."); + if (r < 0) + return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m"); + if (r == 0) + log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation."); + else + log_debug("Patched directory tree to match UID/GID range."); + + return r; +} + +static int mount_devices( + const char *where, + const char *root_device, bool root_device_rw, + const char *home_device, bool home_device_rw, + const char *srv_device, bool srv_device_rw) { + int r; + + assert(where); + + if (root_device) { + r = mount_device(root_device, arg_directory, NULL, root_device_rw); + if (r < 0) + return log_error_errno(r, "Failed to mount root directory: %m"); + } + + if (home_device) { + r = mount_device(home_device, arg_directory, "/home", home_device_rw); + if (r < 0) + return log_error_errno(r, "Failed to mount home directory: %m"); + } + + if (srv_device) { + r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw); + if (r < 0) + return log_error_errno(r, "Failed to mount server data directory: %m"); + } + + return 0; +} + +static void loop_remove(int nr, int *image_fd) { + _cleanup_close_ int control = -1; + int r; + + if (nr < 0) + return; + + if (image_fd && *image_fd >= 0) { + r = ioctl(*image_fd, LOOP_CLR_FD); + if (r < 0) + log_debug_errno(errno, "Failed to close loop image: %m"); + *image_fd = safe_close(*image_fd); + } + + control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + if (control < 0) { + log_warning_errno(errno, "Failed to open /dev/loop-control: %m"); + return; + } + + r = ioctl(control, LOOP_CTL_REMOVE, nr); + if (r < 0) + log_debug_errno(errno, "Failed to remove loop %d: %m", nr); +} + +/* + * Return values: + * < 0 : wait_for_terminate() failed to get the state of the + * container, the container was terminated by a signal, or + * failed for an unknown reason. No change is made to the + * container argument. + * > 0 : The program executed in the container terminated with an + * error. The exit code of the program executed in the + * container is returned. The container argument has been set + * to CONTAINER_TERMINATED. + * 0 : The container is being rebooted, has been shut down or exited + * successfully. The container argument has been set to either + * CONTAINER_TERMINATED or CONTAINER_REBOOTED. + * + * That is, success is indicated by a return value of zero, and an + * error is indicated by a non-zero value. + */ +static int wait_for_container(pid_t pid, ContainerStatus *container) { + siginfo_t status; + int r; + + r = wait_for_terminate(pid, &status); + if (r < 0) + return log_warning_errno(r, "Failed to wait for container: %m"); + + switch (status.si_code) { + + case CLD_EXITED: + if (status.si_status == 0) + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s exited successfully.", arg_machine); + else + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s failed with error code %i.", arg_machine, status.si_status); + + *container = CONTAINER_TERMINATED; + return status.si_status; + + case CLD_KILLED: + if (status.si_status == SIGINT) { + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s has been shut down.", arg_machine); + *container = CONTAINER_TERMINATED; + return 0; + + } else if (status.si_status == SIGHUP) { + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s is being rebooted.", arg_machine); + *container = CONTAINER_REBOOTED; + return 0; + } + + /* CLD_KILLED fallthrough */ + + case CLD_DUMPED: + log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); + return -EIO; + + default: + log_error("Container %s failed due to unknown reason.", arg_machine); + return -EIO; + } +} + +static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + pid_t pid; + + 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."); + sd_event_source_set_userdata(s, NULL); + return 0; + } + } + + sd_event_exit(sd_event_source_get_event(s), 0); + return 0; +} + +static int determine_names(void) { + int r; + + if (arg_template && !arg_directory && arg_machine) { + + /* If --template= was specified then we should not + * search for a machine, but instead create a new one + * in /var/lib/machine. */ + + arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL); + if (!arg_directory) + return log_oom(); + } + + if (!arg_image && !arg_directory) { + if (arg_machine) { + _cleanup_(image_unrefp) Image *i = NULL; + + r = image_find(arg_machine, &i); + if (r < 0) + return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine); + else if (r == 0) { + log_error("No image for machine '%s': %m", arg_machine); + return -ENOENT; + } + + if (i->type == IMAGE_RAW) + r = free_and_strdup(&arg_image, i->path); + else + r = free_and_strdup(&arg_directory, i->path); + if (r < 0) + return log_error_errno(r, "Invalid image directory: %m"); + + if (!arg_ephemeral) + arg_read_only = arg_read_only || i->read_only; + } else + arg_directory = get_current_dir_name(); + + if (!arg_directory && !arg_machine) { + log_error("Failed to determine path, please use -D or -i."); + return -EINVAL; + } + } + + if (!arg_machine) { + if (arg_directory && path_equal(arg_directory, "/")) + arg_machine = gethostname_malloc(); + else + arg_machine = strdup(basename(arg_image ?: arg_directory)); + + if (!arg_machine) + return log_oom(); + + hostname_cleanup(arg_machine); + if (!machine_name_is_valid(arg_machine)) { + log_error("Failed to determine machine name automatically, please use -M."); + return -EINVAL; + } + + if (arg_ephemeral) { + char *b; + + /* Add a random suffix when this is an + * ephemeral machine, so that we can run many + * instances at once without manually having + * to specify -M each time. */ + + if (asprintf(&b, "%s-%016" PRIx64, arg_machine, random_u64()) < 0) + return log_oom(); + + free(arg_machine); + arg_machine = b; + } + } + + return 0; +} + +static int determine_uid_shift(const char *directory) { + int r; + + if (arg_userns_mode == USER_NAMESPACE_NO) { + arg_uid_shift = 0; + return 0; + } + + if (arg_uid_shift == UID_INVALID) { + struct stat st; + + r = stat(directory, &st); + if (r < 0) + return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory); + + arg_uid_shift = st.st_uid & UINT32_C(0xffff0000); + + if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000))) { + log_error("UID and GID base of %s don't match.", directory); + return -EINVAL; + } + + arg_uid_range = UINT32_C(0x10000); + } + + if (arg_uid_shift > (uid_t) -1 - arg_uid_range) { + log_error("UID base too high for UID range."); + return -EINVAL; + } + + return 0; +} + +static int inner_child( + Barrier *barrier, + const char *directory, + bool secondary, + int kmsg_socket, + int rtnl_socket, + FDSet *fds) { + + _cleanup_free_ char *home = NULL; + char as_uuid[37]; + unsigned n_env = 1; + const char *envp[] = { + "PATH=" DEFAULT_PATH_SPLIT_USR, + NULL, /* container */ + NULL, /* TERM */ + NULL, /* HOME */ + NULL, /* USER */ + NULL, /* LOGNAME */ + NULL, /* container_uuid */ + NULL, /* LISTEN_FDS */ + NULL, /* LISTEN_PID */ + NULL, /* NOTIFY_SOCKET */ + NULL + }; + + _cleanup_strv_free_ char **env_use = NULL; + int r; + + assert(barrier); + assert(directory); + assert(kmsg_socket >= 0); + + cg_unified_flush(); + + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* Tell the parent, that it now can write the UID map. */ + (void) barrier_place(barrier); /* #1 */ + + /* Wait until the parent wrote the UID map */ + if (!barrier_place_and_sync(barrier)) { /* #2 */ + log_error("Parent died too early"); + return -ESRCH; + } + } + + r = mount_all(NULL, + arg_userns_mode != USER_NAMESPACE_NO, + true, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + + if (r < 0) + return r; + + r = mount_sysfs(NULL); + if (r < 0) + return r; + + /* Wait until we are cgroup-ified, so that we + * can mount the right cgroup path writable */ + if (!barrier_place_and_sync(barrier)) { /* #3 */ + log_error("Parent died too early"); + return -ESRCH; + } + + r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); + if (r < 0) + return r; + + r = reset_uid_gid(); + if (r < 0) + return log_error_errno(r, "Couldn't become new root: %m"); + + r = setup_boot_id(NULL); + if (r < 0) + return r; + + r = setup_kmsg(NULL, kmsg_socket); + if (r < 0) + return r; + kmsg_socket = safe_close(kmsg_socket); + + umask(0022); + + if (setsid() < 0) + return log_error_errno(errno, "setsid() failed: %m"); + + if (arg_private_network) + loopback_setup(); + + if (arg_expose_ports) { + r = expose_port_send_rtnl(rtnl_socket); + if (r < 0) + return r; + rtnl_socket = safe_close(rtnl_socket); + } + + r = drop_capabilities(); + if (r < 0) + return log_error_errno(r, "drop_capabilities() failed: %m"); + + setup_hostname(); + + if (arg_personality != PERSONALITY_INVALID) { + if (personality(arg_personality) < 0) + return log_error_errno(errno, "personality() failed: %m"); + } else if (secondary) { + if (personality(PER_LINUX32) < 0) + return log_error_errno(errno, "personality() failed: %m"); + } + +#ifdef HAVE_SELINUX + if (arg_selinux_context) + if (setexeccon(arg_selinux_context) < 0) + return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context); +#endif + + r = change_uid_gid(arg_user, &home); + 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++; + + if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || + (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || + (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) + return log_oom(); + + assert(!sd_id128_is_null(arg_uuid)); + + if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0) + return log_oom(); + + if (fdset_size(fds) > 0) { + r = fdset_cloexec(fds, false); + if (r < 0) + return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors."); + + if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) || + (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) + return log_oom(); + } + if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) + return log_oom(); + + env_use = strv_env_merge(2, envp, arg_setenv); + if (!env_use) + return log_oom(); + + /* Let the parent know that we are ready and + * wait until the parent is ready with the + * setup, too... */ + if (!barrier_place_and_sync(barrier)) { /* #4 */ + log_error("Parent died too early"); + return -ESRCH; + } + + if (arg_chdir) + if (chdir(arg_chdir) < 0) + return log_error_errno(errno, "Failed to change to specified working directory %s: %m", arg_chdir); + + if (arg_start_mode == START_PID2) { + r = stub_pid1(); + if (r < 0) + return r; + } + + /* Now, explicitly close the log, so that we + * then can close all remaining fds. Closing + * the log explicitly first has the benefit + * that the logging subsystem knows about it, + * and is thus ready to be reopened should we + * need it again. Note that the other fds + * closed here are at least the locking and + * barrier fds. */ + log_close(); + (void) fdset_close_others(fds); + + if (arg_start_mode == START_BOOT) { + char **a; + size_t m; + + /* Automatically search for the init system */ + + m = strv_length(arg_parameters); + a = newa(char*, m + 2); + memcpy_safe(a + 1, arg_parameters, m * sizeof(char*)); + a[1 + m] = NULL; + + a[0] = (char*) "/usr/lib/systemd/systemd"; + execve(a[0], a, env_use); + + a[0] = (char*) "/lib/systemd/systemd"; + execve(a[0], a, env_use); + + a[0] = (char*) "/sbin/init"; + execve(a[0], a, env_use); + } else if (!strv_isempty(arg_parameters)) + execvpe(arg_parameters[0], arg_parameters, env_use); + else { + if (!arg_chdir) + /* If we cannot change the directory, we'll end up in /, that is expected. */ + (void) chdir(home ?: "/root"); + + execle("/bin/bash", "-bash", NULL, env_use); + execle("/bin/sh", "-sh", NULL, env_use); + } + + r = -errno; + (void) log_open(); + return log_error_errno(r, "execv() failed: %m"); +} + +static int setup_sd_notify_child(void) { + static const int one = 1; + int fd = -1; + union sockaddr_union sa = { + .sa.sa_family = AF_UNIX, + }; + int r; + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate notification socket: %m"); + + (void) mkdir_parents(NSPAWN_NOTIFY_SOCKET_PATH, 0755); + (void) unlink(NSPAWN_NOTIFY_SOCKET_PATH); + + strncpy(sa.un.sun_path, NSPAWN_NOTIFY_SOCKET_PATH, sizeof(sa.un.sun_path)-1); + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) { + safe_close(fd); + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + } + + r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { + safe_close(fd); + return log_error_errno(errno, "SO_PASSCRED failed: %m"); + } + + return fd; +} + +static int outer_child( + Barrier *barrier, + const char *directory, + const char *console, + const char *root_device, bool root_device_rw, + const char *home_device, bool home_device_rw, + const char *srv_device, bool srv_device_rw, + bool interactive, + bool secondary, + int pid_socket, + int uuid_socket, + int notify_socket, + int kmsg_socket, + int rtnl_socket, + int uid_shift_socket, + FDSet *fds) { + + pid_t pid; + ssize_t l; + int r; + _cleanup_close_ int fd = -1; + + assert(barrier); + assert(directory); + assert(console); + assert(pid_socket >= 0); + assert(uuid_socket >= 0); + assert(notify_socket >= 0); + assert(kmsg_socket >= 0); + + cg_unified_flush(); + + if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) + return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); + + if (interactive) { + close_nointr(STDIN_FILENO); + close_nointr(STDOUT_FILENO); + close_nointr(STDERR_FILENO); + + r = open_terminal(console, O_RDWR); + if (r != STDIN_FILENO) { + if (r >= 0) { + safe_close(r); + r = -EINVAL; + } + + return log_error_errno(r, "Failed to open console: %m"); + } + + if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || + dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) + return log_error_errno(errno, "Failed to duplicate console: %m"); + } + + r = reset_audit_loginuid(); + if (r < 0) + return r; + + /* Mark everything as slave, so that we still + * receive mounts from the real root, but don't + * propagate mounts to the real root. */ + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) + return log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m"); + + r = mount_devices(directory, + root_device, root_device_rw, + home_device, home_device_rw, + srv_device, srv_device_rw); + if (r < 0) + return r; + + r = determine_uid_shift(directory); + if (r < 0) + return r; + + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* Let the parent know which UID shift we read from the image */ + l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send UID shift: %m"); + if (l != sizeof(arg_uid_shift)) { + log_error("Short write while sending UID shift."); + return -EIO; + } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift + * we just read from the image is available. If yes, it will send the UID shift back to us, if + * not it will pick a different one, and send it back to us. */ + + l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) + return log_error_errno(errno, "Failed to recv UID shift: %m"); + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while receiving UID shift."); + return -EIO; + } + } + + log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); + } + + /* Turn directory into bind mount */ + if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0) + return log_error_errno(errno, "Failed to make bind mount: %m"); + + r = recursive_chown(directory, arg_uid_shift, arg_uid_range); + if (r < 0) + return r; + + r = setup_volatile( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); + if (r < 0) + return r; + + r = setup_volatile_state( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); + if (r < 0) + return r; + + r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift); + if (r < 0) + return r; + + if (arg_read_only) { + r = bind_remount_recursive(directory, true); + if (r < 0) + return log_error_errno(r, "Failed to make tree read-only: %m"); + } + + r = mount_all(directory, + arg_userns_mode != USER_NAMESPACE_NO, + false, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) + return r; + + r = copy_devnodes(directory); + if (r < 0) + return r; + + dev_setup(directory, arg_uid_shift, arg_uid_shift); + + r = setup_pts(directory); + if (r < 0) + return r; + + r = setup_propagate(directory); + if (r < 0) + return r; + + r = setup_dev_console(directory, console); + if (r < 0) + return r; + + r = setup_seccomp(arg_caps_retain); + if (r < 0) + return r; + + r = setup_timezone(directory); + if (r < 0) + return r; + + r = setup_resolv_conf(directory); + if (r < 0) + return r; + + r = setup_machine_id(directory); + if (r < 0) + return r; + + r = setup_journal(directory); + if (r < 0) + return r; + + r = mount_custom( + directory, + arg_custom_mounts, + arg_n_custom_mounts, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) + return r; + + r = mount_cgroups( + directory, + arg_unified_cgroup_hierarchy, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) + return r; + + r = mount_move_root(directory); + if (r < 0) + return log_error_errno(r, "Failed to move root directory: %m"); + + fd = setup_sd_notify_child(); + if (fd < 0) + return fd; + + pid = raw_clone(SIGCHLD|CLONE_NEWNS| + (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) | + (arg_private_network ? CLONE_NEWNET : 0) | + (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0)); + if (pid < 0) + return log_error_errno(errno, "Failed to fork inner child: %m"); + if (pid == 0) { + pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); + notify_socket = safe_close(notify_socket); + uid_shift_socket = safe_close(uid_shift_socket); + + /* The inner child has all namespaces that are + * requested, so that we all are owned by the user if + * user namespaces are turned on. */ + + r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send PID: %m"); + if (l != sizeof(pid)) { + log_error("Short write while sending PID."); + return -EIO; + } + + l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send machine ID: %m"); + if (l != sizeof(arg_uuid)) { + log_error("Short write while sending machine ID."); + return -EIO; + } + + l = send_one_fd(notify_socket, fd, 0); + if (l < 0) + return log_error_errno(errno, "Failed to send notify fd: %m"); + + pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); + notify_socket = safe_close(notify_socket); + kmsg_socket = safe_close(kmsg_socket); + rtnl_socket = safe_close(rtnl_socket); + + return 0; +} + +static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { + unsigned n_tries = 100; + uid_t candidate; + int r; + + assert(shift); + assert(ret_lock_file); + assert(arg_userns_mode == USER_NAMESPACE_PICK); + assert(arg_uid_range == 0x10000U); + + candidate = *shift; + + (void) mkdir("/run/systemd/nspawn-uid", 0755); + + for (;;) { + char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT; + + if (--n_tries <= 0) + return -EBUSY; + + if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX) + goto next; + if ((candidate & UINT32_C(0xFFFF)) != 0) + goto next; + + xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate); + r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf); + if (r == -EBUSY) /* Range already taken by another nspawn instance */ + goto next; + if (r < 0) + return r; + + /* Make some superficial checks whether the range is currently known in the user database */ + if (getpwuid(candidate)) + goto next; + if (getpwuid(candidate + UINT32_C(0xFFFE))) + goto next; + if (getgrgid(candidate)) + goto next; + if (getgrgid(candidate + UINT32_C(0xFFFE))) + goto next; + + *ret_lock_file = lf; + lf = (struct LockFile) LOCK_FILE_INIT; + *shift = candidate; + return 0; + + next: + random_bytes(&candidate, sizeof(candidate)); + candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN; + candidate &= (uid_t) UINT32_C(0xFFFF0000); + } +} + +static int setup_uid_map(pid_t pid) { + char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1]; + int r; + + assert(pid > 1); + + xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid); + xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range); + r = write_string_file(uid_map, line, 0); + if (r < 0) + return log_error_errno(r, "Failed to write UID map: %m"); + + /* We always assign the same UID and GID ranges */ + xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid); + r = write_string_file(uid_map, line, 0); + if (r < 0) + return log_error_errno(r, "Failed to write GID map: %m"); + + return 0; +} + +static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + char buf[NOTIFY_BUFFER_MAX+1]; + char *p = NULL; + 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; + ssize_t n; + pid_t inner_child_pid; + _cleanup_strv_free_ char **tags = NULL; + + assert(userdata); + + inner_child_pid = PTR_TO_PID(userdata); + + if (revents != EPOLLIN) { + log_warning("Got unexpected poll event for notify fd."); + return 0; + } + + n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return log_warning_errno(errno, "Couldn't read notification socket: %m"); + } + 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))) { + + ucred = (struct ucred*) CMSG_DATA(cmsg); + } + } + + if (!ucred || ucred->pid != inner_child_pid) { + 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."); + return 0; + } + + buf[n] = 0; + tags = strv_split(buf, "\n\r"); + if (!tags) + return log_oom(); + + if (strv_find(tags, "READY=1")) + sd_notifyf(false, "READY=1\n"); + + p = strv_find_startswith(tags, "STATUS="); + if (p) + sd_notifyf(false, "STATUS=Container running: %s", p); + + return 0; +} + +static int setup_sd_notify_parent(sd_event *event, int fd, pid_t *inner_child_pid) { + int r; + sd_event_source *notify_event_source; + + r = sd_event_add_io(event, ¬ify_event_source, fd, EPOLLIN, nspawn_dispatch_notify_fd, inner_child_pid); + if (r < 0) + return log_error_errno(r, "Failed to allocate notify event source: %m"); + + (void) sd_event_source_set_description(notify_event_source, "nspawn-notify"); + + return 0; +} + +static int load_settings(void) { + _cleanup_(settings_freep) Settings *settings = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + const char *fn, *i; + int r; + + /* If all settings are masked, there's no point in looking for + * the settings file */ + if ((arg_settings_mask & _SETTINGS_MASK_ALL) == _SETTINGS_MASK_ALL) + return 0; + + fn = strjoina(arg_machine, ".nspawn"); + + /* We first look in the admin's directories in /etc and /run */ + FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") { + _cleanup_free_ char *j = NULL; + + j = strjoin(i, "/", fn, NULL); + if (!j) + return log_oom(); + + f = fopen(j, "re"); + if (f) { + p = j; + j = NULL; + + /* By default, we trust configuration from /etc and /run */ + if (arg_settings_trusted < 0) + arg_settings_trusted = true; + + break; + } + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to open %s: %m", j); + } + + if (!f) { + /* After that, let's look for a file next to the + * actual image we shall boot. */ + + if (arg_image) { + p = file_in_same_dir(arg_image, fn); + if (!p) + return log_oom(); + } else if (arg_directory) { + p = file_in_same_dir(arg_directory, fn); + if (!p) + return log_oom(); + } + + if (p) { + f = fopen(p, "re"); + 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 */ + if (arg_settings_trusted < 0) + arg_settings_trusted = false; + } + } + + if (!f) + return 0; + + log_debug("Settings are trusted: %s", yes_no(arg_settings_trusted)); + + r = settings_load(f, p, &settings); + if (r < 0) + return r; + + /* Copy over bits from the settings, unless they have been + * explicitly masked by command line switches. */ + + if ((arg_settings_mask & SETTING_START_MODE) == 0 && + settings->start_mode >= 0) { + arg_start_mode = settings->start_mode; + + strv_free(arg_parameters); + arg_parameters = settings->parameters; + settings->parameters = NULL; + } + + if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && + settings->working_directory) { + free(arg_chdir); + arg_chdir = settings->working_directory; + settings->working_directory = NULL; + } + + if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && + settings->environment) { + strv_free(arg_setenv); + arg_setenv = settings->environment; + settings->environment = NULL; + } + + if ((arg_settings_mask & SETTING_USER) == 0 && + settings->user) { + free(arg_user); + arg_user = settings->user; + settings->user = NULL; + } + + if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { + uint64_t plus; + + 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_caps_retain |= plus; + + arg_caps_retain &= ~settings->drop_capability; + } + + if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && + settings->kill_signal > 0) + arg_kill_signal = settings->kill_signal; + + if ((arg_settings_mask & SETTING_PERSONALITY) == 0 && + settings->personality != PERSONALITY_INVALID) + arg_personality = settings->personality; + + if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 && + !sd_id128_is_null(settings->machine_id)) { + + if (!arg_settings_trusted) + log_warning("Ignoring MachineID= setting, file %s is not trusted.", p); + else + arg_uuid = settings->machine_id; + } + + if ((arg_settings_mask & SETTING_READ_ONLY) == 0 && + settings->read_only >= 0) + arg_read_only = settings->read_only; + + if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 && + settings->volatile_mode != _VOLATILE_MODE_INVALID) + arg_volatile_mode = settings->volatile_mode; + + if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 && + settings->n_custom_mounts > 0) { + + if (!arg_settings_trusted) + log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", p); + else { + custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); + arg_custom_mounts = settings->custom_mounts; + arg_n_custom_mounts = settings->n_custom_mounts; + + settings->custom_mounts = NULL; + settings->n_custom_mounts = 0; + } + } + + if ((arg_settings_mask & SETTING_NETWORK) == 0 && + (settings->private_network >= 0 || + settings->network_veth >= 0 || + settings->network_bridge || + settings->network_zone || + settings->network_interfaces || + settings->network_macvlan || + 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; + + strv_free(arg_network_macvlan); + arg_network_macvlan = settings->network_macvlan; + settings->network_macvlan = NULL; + + strv_free(arg_network_ipvlan); + 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; + + free(arg_network_zone); + arg_network_zone = settings->network_zone; + settings->network_zone = NULL; + } + } + + if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 && + settings->expose_ports) { + + if (!arg_settings_trusted) + log_warning("Ignoring Port= setting, file %s is not trusted.", p); + else { + expose_port_free_all(arg_expose_ports); + arg_expose_ports = settings->expose_ports; + settings->expose_ports = NULL; + } + } + + if ((arg_settings_mask & SETTING_USERNS) == 0 && + settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { + + if (!arg_settings_trusted) + log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p); + else { + arg_userns_mode = settings->userns_mode; + arg_uid_shift = settings->uid_shift; + arg_uid_range = settings->uid_range; + arg_userns_chown = settings->userns_chown; + } + } + + if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0) + arg_notify_ready = settings->notify_ready; + + return 0; +} + +int main(int argc, char *argv[]) { + + _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL; + bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; + _cleanup_close_ int master = -1, image_fd = -1; + _cleanup_fdset_free_ FDSet *fds = NULL; + int r, n_fd_passed, loop_nr = -1; + char veth_name[IFNAMSIZ] = ""; + bool secondary = false, remove_subvol = false; + sigset_t mask_chld; + pid_t pid = 0; + int ret = EXIT_SUCCESS; + union in_addr_union exposed = {}; + _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; + bool interactive, veth_created = false; + + log_parse_environment(); + log_open(); + + /* Make sure rename_process() in the stub init process can work */ + saved_argv = argv; + saved_argc = argc; + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (geteuid() != 0) { + log_error("Need to be root."); + r = -EPERM; + goto finish; + } + r = determine_names(); + if (r < 0) + goto finish; + + r = load_settings(); + if (r < 0) + goto finish; + + r = verify_arguments(); + if (r < 0) + goto finish; + + n_fd_passed = sd_listen_fds(false); + if (n_fd_passed > 0) { + r = fdset_new_listen_fds(&fds, false); + if (r < 0) { + log_error_errno(r, "Failed to collect file descriptors: %m"); + goto finish; + } + } + + if (arg_directory) { + assert(!arg_image); + + if (path_equal(arg_directory, "/") && !arg_ephemeral) { + log_error("Spawning container on root directory is not supported. Consider using --ephemeral."); + r = -EINVAL; + goto finish; + } + + if (arg_ephemeral) { + _cleanup_free_ char *np = NULL; + + /* If the specified path is a mount point we + * generate the new snapshot immediately + * inside it under a random name. However if + * the specified is not a mount point we + * create the new snapshot in the parent + * directory, just next to it. */ + r = path_is_mount_point(arg_directory, 0); + if (r < 0) { + log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory); + goto finish; + } + if (r > 0) + r = tempfn_random_child(arg_directory, "machine.", &np); + else + r = tempfn_random(arg_directory, "machine.", &np); + if (r < 0) { + log_error_errno(r, "Failed to generate name for snapshot: %m"); + goto finish; + } + + r = image_path_lock(np, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r < 0) { + log_error_errno(r, "Failed to lock %s: %m", np); + goto finish; + } + + 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; + } + + free(arg_directory); + arg_directory = np; + np = NULL; + + remove_subvol = true; + + } else { + r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r == -EBUSY) { + log_error_errno(r, "Directory tree %s is currently busy.", arg_directory); + goto finish; + } + if (r < 0) { + log_error_errno(r, "Failed to lock %s: %m", arg_directory); + goto finish; + } + + 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 | BTRFS_SNAPSHOT_QUOTA); + if (r == -EEXIST) { + if (!arg_quiet) + log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template); + } else if (r < 0) { + log_error_errno(r, "Couldn't create snapshot %s from %s: %m", arg_directory, arg_template); + goto finish; + } else { + if (!arg_quiet) + log_info("Populated %s from template %s.", arg_directory, arg_template); + } + } + } + + if (arg_start_mode == START_BOOT) { + if (path_is_os_tree(arg_directory) <= 0) { + log_error("Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", arg_directory); + r = -EINVAL; + goto finish; + } + } else { + const char *p; + + 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; + } + } + + } else { + char template[] = "/tmp/nspawn-root-XXXXXX"; + + assert(arg_image); + assert(!arg_template); + + r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r == -EBUSY) { + r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); + goto finish; + } + if (r < 0) { + r = log_error_errno(r, "Failed to create image lock: %m"); + goto finish; + } + + if (!mkdtemp(template)) { + log_error_errno(errno, "Failed to create temporary directory: %m"); + r = -errno; + goto finish; + } + + arg_directory = strdup(template); + if (!arg_directory) { + r = log_oom(); + goto finish; + } + + image_fd = setup_image(&device_path, &loop_nr); + if (image_fd < 0) { + r = image_fd; + goto finish; + } + + r = dissect_image(image_fd, + &root_device, &root_device_rw, + &home_device, &home_device_rw, + &srv_device, &srv_device_rw, + &secondary); + if (r < 0) + goto finish; + } + + r = custom_mounts_prepare(); + if (r < 0) + goto finish; + + interactive = + isatty(STDIN_FILENO) > 0 && + isatty(STDOUT_FILENO) > 0; + + master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); + if (master < 0) { + r = log_error_errno(errno, "Failed to acquire pseudo tty: %m"); + goto finish; + } + + r = ptsname_malloc(master, &console); + if (r < 0) { + r = log_error_errno(r, "Failed to determine tty name: %m"); + goto finish; + } + + if (arg_selinux_apifs_context) { + r = mac_selinux_apply(console, arg_selinux_apifs_context); + if (r < 0) + goto finish; + } + + if (unlockpt(master) < 0) { + r = log_error_errno(errno, "Failed to unlock tty: %m"); + goto finish; + } + + if (!arg_quiet) + log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.", + arg_machine, arg_image ?: arg_directory); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + assert_se(sigemptyset(&mask_chld) == 0); + assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); + + if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { + r = log_error_errno(errno, "Failed to become subreaper: %m"); + goto finish; + } + + for (;;) { + static const struct sigaction sa = { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP, + }; + + _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; + _cleanup_close_ int etc_passwd_lock = -1; + _cleanup_close_pair_ int + kmsg_socket_pair[2] = { -1, -1 }, + rtnl_socket_pair[2] = { -1, -1 }, + pid_socket_pair[2] = { -1, -1 }, + uuid_socket_pair[2] = { -1, -1 }, + notify_socket_pair[2] = { -1, -1 }, + uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_close_ int notify_socket= -1; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + ContainerStatus container_status; + char last_char = 0; + int ifi = 0; + ssize_t l; + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely + * check with getpwuid() if the specific user already exists. Note that /etc might be + * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we + * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are + * really just an extra safety net. We kinda assume that the UID range we allocate from is + * really ours. */ + + etc_passwd_lock = take_etc_passwd_lock(NULL); + if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) { + log_error_errno(r, "Failed to take /etc/passwd lock: %m"); + goto finish; + } + } + + r = barrier_create(&barrier); + if (r < 0) { + log_error_errno(r, "Cannot initialize IPC barrier: %m"); + goto finish; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create kmsg socket pair: %m"); + goto finish; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create rtnl socket pair: %m"); + goto finish; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create pid socket pair: %m"); + goto finish; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create id socket pair: %m"); + goto finish; + } + + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create notify socket pair: %m"); + goto finish; + } + + if (arg_userns_mode != USER_NAMESPACE_NO) + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); + goto finish; + } + + /* Child can be killed before execv(), so handle SIGCHLD + * in order to interrupt parent's blocking calls and + * give it a chance to call wait() and terminate. */ + r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL); + if (r < 0) { + r = log_error_errno(errno, "Failed to change the signal mask: %m"); + goto finish; + } + + r = sigaction(SIGCHLD, &sa, NULL); + if (r < 0) { + r = log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); + goto finish; + } + + pid = raw_clone(SIGCHLD|CLONE_NEWNS); + if (pid < 0) { + if (errno == EINVAL) + r = log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); + else + r = log_error_errno(errno, "clone() failed: %m"); + + goto finish; + } + + if (pid == 0) { + /* The outer child only has a file system namespace. */ + barrier_set_role(&barrier, BARRIER_CHILD); + + master = safe_close(master); + + kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); + rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); + pid_socket_pair[0] = safe_close(pid_socket_pair[0]); + uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); + notify_socket_pair[0] = safe_close(notify_socket_pair[0]); + uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + r = outer_child(&barrier, + arg_directory, + console, + root_device, root_device_rw, + home_device, home_device_rw, + srv_device, srv_device_rw, + interactive, + secondary, + pid_socket_pair[1], + uuid_socket_pair[1], + notify_socket_pair[1], + kmsg_socket_pair[1], + rtnl_socket_pair[1], + uid_shift_socket_pair[1], + fds); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + barrier_set_role(&barrier, BARRIER_PARENT); + + fds = fdset_free(fds); + + kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); + rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); + pid_socket_pair[1] = safe_close(pid_socket_pair[1]); + uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); + notify_socket_pair[1] = safe_close(notify_socket_pair[1]); + uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); + + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* The child just let us know the UID shift it might have read from the image. */ + l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while reading UID shift."); + r = EIO; + goto finish; + } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* If we are supposed to pick the UID shift, let's try to use the shift read from the + * image, but if that's already in use, pick a new one, and report back to the child, + * which one we now picked. */ + + r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); + if (r < 0) { + log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); + goto finish; + } + + l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); + if (l < 0) { + r = log_error_errno(errno, "Failed to send UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short write while writing UID shift."); + r = -EIO; + goto finish; + } + } + } + + /* Wait for the outer child. */ + r = wait_for_terminate_and_warn("namespace helper", pid, NULL); + if (r < 0) + goto finish; + if (r != 0) { + r = -EIO; + goto finish; + } + pid = 0; + + /* And now retrieve the PID of the inner child. */ + l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read inner child PID: %m"); + goto finish; + } + if (l != sizeof(pid)) { + log_error("Short read while reading inner child PID."); + r = EIO; + goto finish; + } + + /* We also retrieve container UUID in case it was generated by outer child */ + l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read container machine ID: %m"); + goto finish; + } + if (l != sizeof(arg_uuid)) { + log_error("Short read while reading container machined ID."); + r = EIO; + goto finish; + } + + /* We also retrieve the socket used for notifications generated by outer child */ + notify_socket = receive_one_fd(notify_socket_pair[0], 0); + if (notify_socket < 0) { + r = log_error_errno(errno, "Failed to receive notification socket from the outer child: %m"); + goto finish; + } + + log_debug("Init process invoked as PID " PID_FMT, pid); + + if (arg_userns_mode != USER_NAMESPACE_NO) { + if (!barrier_place_and_sync(&barrier)) { /* #1 */ + log_error("Child died too early."); + r = -ESRCH; + goto finish; + } + + r = setup_uid_map(pid); + if (r < 0) + goto finish; + + (void) barrier_place(&barrier); /* #2 */ + } + + if (arg_private_network) { + + r = move_network_interfaces(pid, arg_network_interfaces); + if (r < 0) + goto finish; + + if (arg_network_veth) { + r = setup_veth(arg_machine, pid, veth_name, + arg_network_bridge || arg_network_zone); + if (r < 0) + goto finish; + else if (r > 0) + ifi = r; + + if (arg_network_bridge) { + /* Add the interface to a bridge */ + r = setup_bridge(veth_name, arg_network_bridge, false); + if (r < 0) + goto finish; + if (r > 0) + ifi = r; + } else if (arg_network_zone) { + /* Add the interface to a bridge, possibly creating it */ + r = setup_bridge(veth_name, arg_network_zone, true); + if (r < 0) + goto finish; + if (r > 0) + ifi = r; + } + } + + r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra); + if (r < 0) + goto finish; + + /* We created the primary and extra veth links now; let's remember this, so that we know to + remove them later on. Note that we don't bother with removing veth links that were created + here when their setup failed half-way, because in that case the kernel should be able to + remove them on its own, since they cannot be referenced by anything yet. */ + veth_created = true; + + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); + if (r < 0) + goto finish; + + r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan); + if (r < 0) + goto finish; + } + + if (arg_register) { + r = register_machine( + arg_machine, + pid, + arg_directory, + arg_uuid, + ifi, + arg_slice, + arg_custom_mounts, arg_n_custom_mounts, + arg_kill_signal, + arg_property, + arg_keep_unit, + arg_container_service_name); + if (r < 0) + goto finish; + } + + r = sync_cgroup(pid, arg_unified_cgroup_hierarchy); + if (r < 0) + goto finish; + + if (arg_keep_unit) { + r = create_subcgroup(pid, arg_unified_cgroup_hierarchy); + if (r < 0) + goto finish; + } + + r = chown_cgroup(pid, arg_uid_shift); + if (r < 0) + goto finish; + + /* Notify the child that the parent is ready with all + * its setup (including cgroup-ification), and that + * the child can now hand over control to the code to + * run inside the container. */ + (void) barrier_place(&barrier); /* #3 */ + + /* Block SIGCHLD here, before notifying child. + * process_pty() will handle it with the other signals. */ + assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0); + + /* Reset signal to default */ + r = default_signals(SIGCHLD, -1); + if (r < 0) { + log_error_errno(r, "Failed to reset SIGCHLD: %m"); + goto finish; + } + + r = sd_event_new(&event); + if (r < 0) { + log_error_errno(r, "Failed to get default event source: %m"); + goto finish; + } + + r = setup_sd_notify_parent(event, notify_socket, PID_TO_PTR(pid)); + if (r < 0) + goto finish; + + /* Let the child know that we are ready and wait that the child is completely ready now. */ + if (!barrier_place_and_sync(&barrier)) { /* #4 */ + log_error("Child died too early."); + r = -ESRCH; + goto finish; + } + + /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear + * in getpwuid(), thus we can release the /etc/passwd lock. */ + etc_passwd_lock = safe_close(etc_passwd_lock); + + sd_notifyf(false, + "STATUS=Container running.\n" + "X_NSPAWN_LEADER_PID=" PID_FMT, pid); + if (!arg_notify_ready) + sd_notify(false, "READY=1\n"); + + 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, 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); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + } + + /* simply exit on sigchld */ + sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); + + if (arg_expose_ports) { + r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl); + if (r < 0) + goto finish; + + (void) expose_port_execute(rtnl, arg_expose_ports, &exposed); + } + + rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); + + 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; + } + + r = sd_event_loop(event); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + + pty_forward_get_last_char(forward, &last_char); + + forward = pty_forward_free(forward); + + if (!arg_quiet && last_char != '\n') + putc('\n', stdout); + + /* Kill if it is not dead yet anyway */ + if (arg_register && !arg_keep_unit) + terminate_machine(pid); + + /* Normally redundant, but better safe than sorry */ + kill(pid, SIGKILL); + + r = wait_for_container(pid, &container_status); + pid = 0; + + if (r < 0) + /* We failed to wait for the container, or the + * container exited abnormally */ + goto finish; + else if (r > 0 || container_status == CONTAINER_TERMINATED) { + /* The container exited with a non-zero + * status, or with zero status and no reboot + * was requested. */ + ret = r; + break; + } + + /* CONTAINER_REBOOTED, loop again */ + + if (arg_keep_unit) { + /* Special handling if we are running as a + * service: instead of simply restarting the + * machine we want to restart the entire + * service, so let's inform systemd about this + * with the special exit code 133. The service + * file uses RestartForceExitStatus=133 so + * that this results in a full nspawn + * restart. This is necessary since we might + * have cgroup parameters set we want to have + * flushed out. */ + ret = 133; + r = 0; + break; + } + + expose_port_flush(arg_expose_ports, &exposed); + + (void) remove_veth_links(veth_name, arg_network_veth_extra); + veth_created = false; + } + +finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Terminating..."); + + if (pid > 0) + kill(pid, SIGKILL); + + /* Try to flush whatever is still queued in the pty */ + if (master >= 0) + (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); + + loop_remove(loop_nr, &image_fd); + + if (remove_subvol && arg_directory) { + int k; + + 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); + } + + if (arg_machine) { + const char *p; + + p = strjoina("/run/systemd/nspawn/propagate/", arg_machine); + (void) rm_rf(p, REMOVE_ROOT); + } + + expose_port_flush(arg_expose_ports, &exposed); + + if (veth_created) + (void) remove_veth_links(veth_name, arg_network_veth_extra); + (void) remove_bridge(arg_network_zone); + + free(arg_directory); + free(arg_template); + free(arg_image); + free(arg_machine); + free(arg_user); + free(arg_chdir); + strv_free(arg_setenv); + free(arg_network_bridge); + 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); + + return r < 0 ? EXIT_FAILURE : ret; +} diff --git a/src/systemd-nspawn/systemd-nspawn.completion.bash b/src/systemd-nspawn/systemd-nspawn.completion.bash new file mode 100644 index 0000000000..ea4a5e1f43 --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn.completion.bash @@ -0,0 +1,155 @@ +# systemd-nspawn(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# Copyright 2014 Thomas H.P. Andersen +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word() { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +__get_users() { + local a b + loginctl list-users --no-legend --no-pager | { while read a b; do echo " $b"; done; }; +} + +__get_slices() { + local a b + systemctl list-units -t slice --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +__get_machines() { + local a b + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; +} + +__get_env() { + local a + env | { while read a; do echo " ${a%%=*}"; done; }; +} + +__get_interfaces(){ + { cd /sys/class/net && echo *; } | \ + while read -d' ' -r name; do + [[ "$name" != "lo" ]] && echo "$name" + done +} + +_systemd_nspawn() { + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local i verb comps + + local -A OPTS=( + [STANDALONE]='-h --help --version --private-network -b --boot --read-only -q --quiet --share-system --keep-unit --network-veth -j' + [ARG]='-D --directory -u --user --uuid --capability --drop-capability --link-journal --bind --bind-ro -M --machine + -S --slice --setenv -Z --selinux-context -L --selinux-apifs-context --register --network-interface --network-bridge + --personality -i --image --tmpfs --volatile + --network-macvlan --kill-signal --template + --notify-ready' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --directory|-D|--template) + compopt -o nospace + comps=$(compgen -S/ -A directory -- "$cur" ) + ;; + --user|-u) + comps=$( __get_users ) + ;; + --uuid) + comps='' + ;; + --capability) + comps='CAP_BLOCK_SUSPEND CAP_IPC_LOCK CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_SYS_MODULE CAP_SYS_PACCT CAP_SYS_RAWIO + CAP_SYS_TIME CAP_SYSLOG CAP_WAKE_ALARM CAP_NET_ADMIN' + ;; + --drop-capability) + comps='CAP_AUDIT_CONTROL CAP_AUDIT_WRITE CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_FSETID + CAP_IPC_OWNER CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE CAP_MKNOD CAP_NET_ADMIN CAP_NET_BIND_SERVICE + CAP_NET_BROADCAST CAP_NET_RAW CAP_SETFCAP CAP_SETGID CAP_SETPCAP CAP_SETUID CAP_SYS_ADMIN CAP_SYS_BOOT + CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_PTRACE CAP_SYS_RESOURCE CAP_SYS_TTY_CONFIG' + ;; + --link-journal) + comps='no auto guest try-guest host try-host' + ;; + --bind|--bind-ro) + compopt -o nospace + comps=$(compgen -S/ -A directory -- "$cur" ) + ;; + --tmpfs) + compopt -o nospace + comps=$(compgen -S/ -A directory -- "$cur" ) + ;; + --machine|-M) + comps=$( __get_machines ) + ;; + --slice|-S) + comps=$( __get_slices ) + ;; + --setenv) + comps=$( __get_env ) + ;; + --selinux-context|-Z) + comps='' + ;; + --selinux-apifs-context|-L) + comps='' + ;; + --register) + comps='yes no' + ;; + --network-interface) + comps=$(__get_interfaces) + ;; + --network-bridge) + comps='' + ;; + --network-macvlan) + comps='' + ;; + --personality) + comps='x86 x86-64' + ;; + --volatile) + comps='yes state no' + ;; + --image|-i) + compopt -o nospace + comps=$( compgen -A file -- "$cur" ) + ;; + --kill-signal) + _signals + return + ;; + --notify-ready) + comps='yes no' + return + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) +} + +complete -F _systemd_nspawn systemd-nspawn diff --git a/src/systemd-nspawn/systemd-nspawn.completion.zsh b/src/systemd-nspawn/systemd-nspawn.completion.zsh new file mode 100644 index 0000000000..77b2e7cd7c --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn.completion.zsh @@ -0,0 +1,50 @@ +#compdef systemd-nspawn + +_nspawn-caps(){ + local -a _caps + _caps=( CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH + CAP_FOWNER CAP_FSETID CAP_IPC_OWNER CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE + CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETGID CAP_SETFCAP CAP_SETPCAP + CAP_SETUID CAP_SYS_ADMIN CAP_SYS_CHROOT CAP_SYS_NICE CAP_SYS_PTRACE CAP_SYS_TTY_CONFIG + CAP_SYS_RESOURCE CAP_SYS_BOOT ) + _values -s , 'capabilities' "$_caps[@]" +} + +_arguments \ + {-h,--help}'[Show this help.]' \ + '--version[Print a short version string and exit.]' \ + {--quiet,-q}'[Turns off any status output by the tool itself.]' \ + {--directory=,-D+}'[Directory to use as file system root for the namespace container. If omitted the current directory will be used.]:directories:_directories' \ + '--template=[Initialize root directory from template directory, if missing.]:template:_directories' \ + {--ephemeral,-x}'[Run container with snapshot of root directory, and remove it after exit.]' \ + {--image=,-i+}'[Disk image to mount the root directory for the container from.]:disk image: _files' \ + {--boot,-b}'[Automatically search for an init binary and invoke it instead of a shell or a user supplied program.]' \ + {--user=,-u+}'[Run the command under specified user, create home directory and cd into it.]:user:_users' \ + {--machine=,-M+}'[Sets the machine name for this container.]: : _message "container name"' \ + '--uuid=[Set the specified uuid for the container.]: : _message "container UUID"' \ + {--slice=,-S+}'[Make the container part of the specified slice, instead of the default machine.slice.]: : _message slice' \ + '--private-network[Disconnect networking of the container from the host.]' \ + '--network-interface=[Assign the specified network interface to the container.]: : _net_interfaces' \ + '--network-macvlan=[Create a "macvlan" interface of the specified Ethernet network interface and add it to the container.]: : _net_interfaces' \ + '--network-ipvlan=[Create a ipvlan network interface based on an existing network interface to the container.]: : _net_interfaces' \ + {--network-veth,-n}'[Create a virtual Ethernet link (veth) between host and container.]' \ + '--network-bridge=[Adds the host side of the Ethernet link created with --network-veth to the specified bridge.]: : _net_interfaces' \ + {--port=,-p+}'[Expose a container IP port on the host.]: : _message port' \ + {--selinux-context=,-Z+}'[Sets the SELinux security context to be used to label processes in the container.]: : _message "SELinux context"' \ + {--selinux-apifs-context=,-L+}'[Sets the SELinux security context to be used to label files in the virtual API file systems in the container.]: : _message "SELinux context"' \ + '--capability=[List one or more additional capabilities to grant the container.]:capabilities:_nspawn-caps' \ + '--drop-capability=[Specify one or more additional capabilities to drop for the containerm]:capabilities:_nspawn-caps' \ + "--link-journal=[Control whether the container's journal shall be made visible to the host system.]:options:(no host guest auto)" \ + '-j[Equivalent to --link-journal=guest.]' \ + '--read-only[Mount the root file system read only for the container.]' \ + '--bind=[Bind mount a file or directory from the host into the container.]: : _files' \ + '--bind-ro=[Bind mount a file or directory from the host into the container (read-only).]: : _files' \ + '--tmpfs=[Mount an empty tmpfs to the specified directory.]: : _files' \ + '--setenv=[Specifies an environment variable assignment to pass to the init process in the container, in the format "NAME=VALUE".]: : _message "environment variables"' \ + '--share-system[Allows the container to share certain system facilities with the host.]' \ + '--register=[Controls whether the container is registered with systemd-machined(8).]:systemd-machined registration:( yes no )' \ + '--keep-unit[Instead of creating a transient scope unit to run the container in, simply register the service or scope unit systemd-nspawn has been invoked in with systemd-machined(8).]' \ + '--personality=[Control the architecture ("personality") reported by uname(2) in the container.]:architecture:(x86 x86-64)' \ + '--volatile=[Run the system in volatile mode.]:volatile:(no yes state)' \ + "--notify-ready=[Control when the ready notification is sent]:options:(yes no)" \ + '*:: : _normal' diff --git a/src/systemd-nspawn/systemd-nspawn.tmpfiles b/src/systemd-nspawn/systemd-nspawn.tmpfiles new file mode 100644 index 0000000000..78bd1c670e --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn.tmpfiles @@ -0,0 +1,23 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +Q /var/lib/machines 0700 - - - + +# Remove old temporary snapshots, but only at boot. Ideally we'd have +# "self-destroying" btrfs snapshots that go away if the last +# reference to it does. To mimic a scheme like this at least remove +# the old snapshots on fresh boots, where we know they cannot be +# referenced anymore. Note that we actually remove all temporary files +# in /var/lib/machines/ at boot, which should be safe since the +# directory has defined semantics. In the root directory (where +# systemd-nspawn --ephemeral places snapshots) we are more strict, to +# avoid removing unrelated temporary files. + +R! /var/lib/machines/.#* +R! /.#machine.* diff --git a/src/systemd-nspawn/systemd-nspawn.xml b/src/systemd-nspawn/systemd-nspawn.xml new file mode 100644 index 0000000000..69d2f6ff7d --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn.xml @@ -0,0 +1,1102 @@ + + + + + + + + + systemd-nspawn + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-nspawn + 1 + + + + systemd-nspawn + Spawn a namespace container for debugging, testing and building + + + + + systemd-nspawn + OPTIONS + COMMAND + ARGS + + + + systemd-nspawn + --boot + OPTIONS + ARGS + + + + + Description + + systemd-nspawn may be used to run a command or OS in a light-weight namespace + container. In many ways it is similar to chroot1, but more powerful + since it fully virtualizes the file system hierarchy, as well as the process tree, the various IPC subsystems and + the host and domain name. + + systemd-nspawn may be invoked on any directory tree containing an operating system tree, + using the command line option. By using the option an OS + tree is automatically searched for in a couple of locations, most importantly in + /var/lib/machines, the suggested directory to place container images installed on the + system. + + In contrast to chroot1 systemd-nspawn + may be used to boot full Linux-based operating systems in a container. + + systemd-nspawn limits access to various kernel interfaces in the container to read-only, + such as /sys, /proc/sys or /sys/fs/selinux. The + host's network interfaces and the system clock may not be changed from within the container. Device nodes may not + be created. The host system cannot be rebooted and kernel modules may not be loaded from within the + container. + + Use a tool like dnf8, debootstrap8, or + pacman8 to + set up an OS directory tree suitable as file system hierarchy for systemd-nspawn containers. See + the Examples section below for details on suitable invocation of these commands. + + As a safety check systemd-nspawn will verify the existence of + /usr/lib/os-release or /etc/os-release in the container tree before + starting the container (see + os-release5). It might be + necessary to add this file to the container tree manually if the OS of the container is too old to contain this + file out-of-the-box. + + systemd-nspawn may be invoked directly from the interactive command line or run as system + service in the background. In this mode each container instance runs as its own service instance; a default + template unit file systemd-nspawn@.service is provided to make this easy, taking the container + name as instance identifier. Note that different default options apply when systemd-nspawn is + invoked by the template unit file than interactively on the command line. Most importantly the template unit file + makes use of the which is not the default in case systemd-nspawn is + invoked from the interactive command line. Further differences with the defaults are documented along with the + various supported options below. + + The machinectl1 tool may + be used to execute a number of operations on containers. In particular it provides easy-to-use commands to run + containers as system services using the systemd-nspawn@.service template unit + file. + + Along with each container a settings file with the .nspawn suffix may exist, containing + additional settings to apply when running the container. See + systemd.nspawn5 for + details. Settings files override the default options used by the systemd-nspawn@.service + template unit file, making it usually unnecessary to alter this template file directly. + + Note that systemd-nspawn will mount file systems private to the container to + /dev, /run and similar. These will not be visible outside of the + container, and their contents will be lost when the container exits. + + Note that running two systemd-nspawn containers from the same directory tree will not make + processes in them see each other. The PID namespace separation of the two containers is complete and the containers + will share very few runtime objects except for the underlying file system. Use + machinectl1's + login or shell commands to request an additional login session in a running + container. + + systemd-nspawn implements the Container Interface + specification. + + While running, containers invoked with systemd-nspawn are registered with the + systemd-machined8 service that + keeps track of running containers, and provides programming interfaces to interact with them. + + + + Options + + If option is specified, the arguments + are used as arguments for the init binary. Otherwise, + COMMAND specifies the program to launch + in the container, and the remaining arguments are used as + arguments for this program. If is not used and + no arguments are specified, a shell is launched in the + container. + + The following options are understood: + + + + + + + Directory to use as file system root for the + container. + + If neither , nor + is specified the directory is + determined by searching for a directory named the same as the + machine name specified with . See + machinectl1 + section "Files and Directories" for the precise search path. + + If neither , + , nor + are specified, the current directory will + be used. May not be specified together with + . + + + + + + Directory or btrfs + subvolume to use as template for the container's root + directory. If this is specified and the container's root + directory (as configured by ) + does not yet exist it is created as btrfs + subvolume and populated from this template tree. Ideally, the + specified template path refers to the root of a + btrfs subvolume, in which case a simple + copy-on-write snapshot is taken, and populating the root + directory is instant. If the specified template path does not + refer to the root of a btrfs subvolume (or + not even to a btrfs file system at all), + the tree is copied, which can be substantially more + time-consuming. Note that if this option is used the + container's root directory (in contrast to the template + directory!) must be located on a btrfs file + system, so that the btrfs subvolume may be + created. May not be specified together with + or + . + + Note that this switch leaves host name, machine ID and + all other settings that could identify the instance + unmodified. + + + + + + + If specified, the container is run with a + temporary btrfs snapshot of its root + directory (as configured with ), + that is removed immediately when the container terminates. + This option is only supported if the root file system is + btrfs. May not be specified together with + or + . + Note that this switch leaves host name, machine ID and + all other settings that could identify the instance + unmodified. + + + + + + + Disk image to mount the root directory for the + container from. Takes a path to a regular file or to a block + device node. The file or block device must contain + either: + + + An MBR partition table with a single + partition of type 0x83 that is marked + bootable. + + A GUID partition table (GPT) with a single + partition of type + 0fc63daf-8483-4772-8e79-3d69d8477de4. + + A GUID partition table (GPT) with a marked + root partition which is mounted as the root directory of the + container. Optionally, GPT images may contain a home and/or + a server data partition which are mounted to the appropriate + places in the container. All these partitions must be + identified by the partition types defined by the Discoverable + Partitions Specification. + + + Any other partitions, such as foreign partitions, swap + partitions or EFI system partitions are not mounted. May not + be specified together with , + or + . + + + + + + + Invoke the shell or specified program as process ID (PID) 2 instead of PID 1 (init). By + default, if neither this option nor is used, the selected binary is run as process with + PID 1, a mode only suitable for programs that are aware of the special semantics that the process with PID 1 + has on UNIX. For example, it needs to reap all processes reparented to it, and should implement + sysvinit compatible signal handling (specifically: it needs to reboot on SIGINT, reexecute + on SIGTERM, reload configuration on SIGHUP, and so on). With a minimal stub init + process is run as PID 1 and the selected binary is executed as PID 2 (and hence does not need to implement any + special semantics). The stub init process will reap processes as necessary and react appropriately to + signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been + modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands, + except when the command refers to an init or shell implementation, as these are generally capable of running + correctly as PID 1. This option may not be combined with or + . + + + + + + + + Automatically search for an init binary and invoke it as PID 1, instead of a shell or a user + supplied program. If this option is used, arguments specified on the command line are used as arguments for the + init binary. This option may not be combined with or + . + + The following table explains the different modes of invocation and relationship to + (see above): + + + Invocation Mode + + + + + + Switch + Explanation + + + + + Neither nor specified + The passed parameters are interpreted as the command line, which is executed as PID 1 in the container. + + + + specified + The passed parameters are interpreted as the command line, which is executed as PID 2 in the container. A stub init process is run as PID 1. + + + + specified + An init binary as automatically searched and run as PID 1 in the container. The passed parameters are used as invocation parameters for this process. + + + + +
+ + Note that is the default mode of operation if the + systemd-nspawn@.service template unit file is used. +
+
+ + + + + Change to the specified working directory before invoking the process in the container. Expects + an absolute path in the container's file system namespace. + + + + + + + After transitioning into the container, change + to the specified user-defined in the container's user + database. Like all other systemd-nspawn features, this is not + a security feature and provides protection against accidental + destructive operations only. + + + + + + + Sets the machine name for this container. This + name may be used to identify this container during its runtime + (for example in tools like + machinectl1 + and similar), and is used to initialize the container's + hostname (which the container can choose to override, + however). If not specified, the last component of the root + directory path of the container is used, possibly suffixed + with a random identifier in case + mode is selected. If the root directory selected is the host's + root directory the host's hostname is used as default + instead. + + + + + + Set the specified UUID for the container. The + init system will initialize + /etc/machine-id from this if this file is + not set yet. Note that this option takes effect only if + /etc/machine-id in the container is + unpopulated. + + + + + + Make the container part of the specified + slice, instead of the default + machine.slice. This is only applies if + the machine is run in its own scope unit, i.e. if + is not used. + + + + + + + Set a unit property on the scope unit to + register for the machine. This only applies if the machine is + run in its own scope unit, i.e. if + is not used. Takes unit property + assignments in the same format as systemctl + set-property. This is useful to set memory limits + and similar for machines. + + + + + + + Controls user namespacing. If enabled, the container will run with its own private set of UNIX + user and group ids (UIDs and GIDs). This involves mapping the private UIDs/GIDs used in the container (starting + with the container's root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other + purposes (usually in the range beyond the host's UID/GID 65536). The parameter may be specified as follows: + + + The value no turns off user namespacing. This is the default. + + The value yes (or the omission of a parameter) turns on user + namespacing. The UID/GID range to use is determined automatically from the file ownership of the root + directory of the container's directory tree. To use this option, make sure to prepare the directory tree in + advance, and ensure that all files and directories in it are owned by UIDs/GIDs in the range you'd like to + use. Also, make sure that used file ACLs exclusively reference UIDs/GIDs in the appropriate range. If this + mode is used the number of UIDs/GIDs assigned to the container for use is 65536, and the UID/GID of the + root directory must be a multiple of 65536. + + The value "pick" turns on user namespacing. In this case the UID/GID range is automatically + chosen. As first step, the file owner of the root directory of the container's directory tree is read, and it + is checked that it is currently not used by the system otherwise (in particular, that no other container is + using it). If this check is successful, the UID/GID range determined this way is used, similar to the + behaviour if "yes" is specified. If the check is not successful (and thus the UID/GID range indicated in the + root directory's file owner is already used elsewhere) a new – currently unused – UID/GID range of 65536 + UIDs/GIDs is randomly chosen between the host UID/GIDs of 524288 and 1878982656, always starting at a + multiple of 65536. This setting implies (see below), which has the + effect that the files and directories in the container's directory tree will be owned by the appropriate + users of the range picked. Using this option makes user namespace behaviour fully automatic. Note that the + first invocation of a previously unused container image might result in picking a new UID/GID range for it, + and thus in the (possibly expensive) file ownership adjustment operation. However, subsequent invocations of + the container will be cheap (unless of course the picked UID/GID range is assigned to a different use by + then). + + Finally if one or two colon-separated numeric parameters are specified, user namespacing is + turned on, too. The first parameter specifies the first host UID/GID to assign to the container, the second + parameter specifies the number of host UIDs/GIDs to assign to the container. If the second parameter is + omitted, 65536 UIDs/GIDs are assigned. + + + It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the + container covers 16 bit. For best security, do not assign overlapping UID/GID ranges to multiple containers. It is + hence a good idea to use the upper 16 bit of the host 32-bit UIDs/GIDs as container identifier, while the lower 16 + bit encode the container UID/GID used. This is in fact the behaviour enforced by the + option. + + When user namespaces are used, the GID range assigned to each container is always chosen identical to the + UID range. + + In most cases, using is the recommended option as it enhances + container security massively and operates fully automatically in most cases. + + Note that the picked UID/GID range is not written to /etc/passwd or + /etc/group. In fact, the allocation of the range is not stored persistently anywhere, + except in the file ownership of the files and directories of the container. + + + + + + If the kernel supports the user namespaces feature, equivalent to + , otherwise equivalent to + . + + Note that is the default if the systemd-nspawn@.service template unit + file is used. + + + + + + If specified, all files and directories in the container's directory tree will adjusted so that + they are owned to the appropriate UIDs/GIDs selected for the container (see above). This operation is + potentially expensive, as it involves descending and iterating through the full directory tree of the + container. Besides actual file ownership, file ACLs are adjusted as well. + + This option is implied if is used. This option has no effect if + user namespacing is not used. + + + + + + Disconnect networking of the container from + the host. This makes all network interfaces unavailable in the + container, with the exception of the loopback device and those + specified with and + configured with . If this + option is specified, the CAP_NET_ADMIN capability will be + added to the set of capabilities the container retains. The + latter may be disabled by using + . + + + + + + Assign the specified network interface to the + container. This will remove the specified interface from the + calling namespace and place it in the container. When the + container terminates, it is moved back to the host namespace. + Note that implies + . This option may be used + more than once to add multiple network interfaces to the + container. + + + + + + Create a macvlan interface + of the specified Ethernet network interface and add it to the + container. A macvlan interface is a virtual + interface that adds a second MAC address to an existing + physical Ethernet link. The interface in the container will be + named after the interface on the host, prefixed with + mv-. Note that + implies + . This option may be used + more than once to add multiple network interfaces to the + container. + + + + + + Create an ipvlan interface + of the specified Ethernet network interface and add it to the + container. An ipvlan interface is a virtual + interface, similar to a macvlan interface, + which uses the same MAC address as the underlying interface. + The interface in the container will be named after the + interface on the host, prefixed with iv-. + Note that implies + . This option may be used + more than once to add multiple network interfaces to the + container. + + + + + + + Create a virtual Ethernet link (veth) between host and container. The host + side of the Ethernet link will be available as a network interface named after the container's name (as + specified with ), prefixed with ve-. The container side of the + Ethernet link will be named host0. The option implies + . + + Note that + systemd-networkd.service8 + includes by default a network file /usr/lib/systemd/network/80-container-ve.network + matching the host-side interfaces created this way, which contains settings to enable automatic address + provisioning on the created virtual link via DHCP, as well as automatic IP routing onto the host's external + network interfaces. It also contains /usr/lib/systemd/network/80-container-host0.network + matching the container-side interface created this way, containing settings to enable client side address + assignment via DHCP. In case systemd-networkd is running on both the host and inside the + container, automatic IP communication from the container to the host is thus available, with further + connectivity to the external network. + + Note that is the default if the + systemd-nspawn@.service template unit file is used. + + + + + + + Adds an additional virtual Ethernet link + between host and container. Takes a colon-separated pair of + host interface name and container interface name. The latter + may be omitted in which case the container and host sides will + be assigned the same name. This switch is independent of + , and — in contrast — may be + used multiple times, and allows configuration of the network + interface names. Note that + has no effect on interfaces created with + . + + + + + + Adds the host side of the Ethernet link created with to the + specified Ethernet bridge interface. Expects a valid network interface name of a bridge device as + argument. Note that implies . If this option + is used, the host side of the Ethernet link will use the vb- prefix instead of + ve-. + + + + + + Creates a virtual Ethernet link (veth) to the container and adds it to an + automatically managed Ethernet bridge interface. The bridge interface is named after the passed argument, + prefixed with vz-. The bridge interface is automatically created when the first container + configured for its name is started, and is automatically removed when the last container configured for its + name exits. Hence, each bridge interface configured this way exists only as long as there's at least one + container referencing it running. This option is very similar to , besides + this automatic creation/removal of the bridge device. + + This setting makes it easy to place multiple related containers on a common, virtual Ethernet-based + broadcast domain, here called a "zone". Each container may only be part of one zone, but each zone may contain + any number of containers. Each zone is referenced by its name. Names may be chosen freely (as long as they form + valid network interface names when prefixed with vz-), and it is sufficient to pass the same + name to the switch of the various concurrently running containers to join + them in one zone. + + Note that + systemd-networkd.service8 + includes by default a network file /usr/lib/systemd/network/80-container-vz.network + matching the bridge interfaces created this way, which contains settings to enable automatic address + provisioning on the created virtual network via DHCP, as well as automatic IP routing onto the host's external + network interfaces. Using is hence in most cases fully automatic and + sufficient to connect multiple local containers in a joined broadcast domain to the host, with further + connectivity to the external network. + + + + + + + + If private networking is enabled, maps an IP + port on the host onto an IP port on the container. Takes a + protocol specifier (either tcp or + udp), separated by a colon from a host port + number in the range 1 to 65535, separated by a colon from a + container port number in the range from 1 to 65535. The + protocol specifier and its separating colon may be omitted, in + which case tcp is assumed. The container + port number and its colon may be omitted, in which case the + same port as the host port is implied. This option is only + supported if private networking is used, such as with + , + . + + + + + + + Sets the SELinux security context to be used + to label processes in the container. + + + + + + + + Sets the SELinux security context to be used + to label files in the virtual API file systems in the + container. + + + + + + + List one or more additional capabilities to + grant the container. Takes a comma-separated list of + capability names, see + capabilities7 + for more information. Note that the following capabilities + will be granted in any way: CAP_CHOWN, CAP_DAC_OVERRIDE, + CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, CAP_IPC_OWNER, + CAP_KILL, CAP_LEASE, CAP_LINUX_IMMUTABLE, + CAP_NET_BIND_SERVICE, CAP_NET_BROADCAST, CAP_NET_RAW, + CAP_SETGID, CAP_SETFCAP, CAP_SETPCAP, CAP_SETUID, + CAP_SYS_ADMIN, CAP_SYS_CHROOT, CAP_SYS_NICE, CAP_SYS_PTRACE, + CAP_SYS_TTY_CONFIG, CAP_SYS_RESOURCE, CAP_SYS_BOOT, + CAP_AUDIT_WRITE, CAP_AUDIT_CONTROL. Also CAP_NET_ADMIN is + retained if is specified. + If the special value all is passed, all + capabilities are retained. + + + + + + Specify one or more additional capabilities to + drop for the container. This allows running the container with + fewer capabilities than the default (see + above). + + + + + + Specify the process signal to send to the + container's PID 1 when nspawn itself receives SIGTERM, in + order to trigger an orderly shutdown of the + container. Defaults to SIGRTMIN+3 if + is used (on systemd-compatible init systems SIGRTMIN+3 + triggers an orderly shutdown). For a list of valid signals, see + signal7. + + + + + + Control whether the container's journal shall + be made visible to the host system. If enabled, allows viewing + the container's journal files from the host (but not vice + versa). Takes one of no, + host, try-host, + guest, try-guest, + auto. If no, the journal + is not linked. If host, the journal files + are stored on the host file system (beneath + /var/log/journal/machine-id) + and the subdirectory is bind-mounted into the container at the + same location. If guest, the journal files + are stored on the guest file system (beneath + /var/log/journal/machine-id) + and the subdirectory is symlinked into the host at the same + location. try-host and + try-guest do the same but do not fail if + the host does not have persistent journalling enabled. If + auto (the default), and the right + subdirectory of /var/log/journal exists, + it will be bind mounted into the container. If the + subdirectory does not exist, no linking is performed. + Effectively, booting a container once with + guest or host will link + the journal persistently if further on the default of + auto is used. + + Note that is the default if the + systemd-nspawn@.service template unit file is used. + + + + + + Equivalent to + . + + + + + + Mount the root file system read-only for the + container. + + + + + + + Bind mount a file or directory from the host + into the container. Takes one of: a path argument — in which + case the specified path will be mounted from the host to the + same path in the container —, or a colon-separated pair of + paths — in which case the first specified path is the source + in the host, and the second path is the destination in the + container —, or a colon-separated triple of source path, + destination path and mount options. Mount options are + comma-separated and currently, only "rbind" and "norbind" + are allowed. Defaults to "rbind". Backslash escapes are interpreted, so + \: may be used to embed colons in either path. + This option may be specified multiple times for + creating multiple independent bind mount points. The + option creates read-only bind + mounts. + + + + + + Mount a tmpfs file system into the container. + Takes a single absolute path argument that specifies where to + mount the tmpfs instance to (in which case the directory + access mode will be chosen as 0755, owned by root/root), or + optionally a colon-separated pair of path and mount option + string that is used for mounting (in which case the kernel + default for access mode and owner will be chosen, unless + otherwise specified). This option is particularly useful for + mounting directories such as /var as + tmpfs, to allow state-less systems, in particular when + combined with . + Backslash escapes are interpreted in the path, so + \: may be used to embed colons in the path. + + + + + + + + Combine multiple directory trees into one + overlay file system and mount it into the container. Takes a + list of colon-separated paths to the directory trees to + combine and the destination mount point. + + Backslash escapes are interpreted in the paths, so + \: may be used to embed colons in the paths. + + + If three or more paths are specified, then the last + specified path is the destination mount point in the + container, all paths specified before refer to directory trees + on the host and are combined in the specified order into one + overlay file system. The left-most path is hence the lowest + directory tree, the second-to-last path the highest directory + tree in the stacking order. If + is used instead of , a read-only + overlay file system is created. If a writable overlay file + system is created, all changes made to it are written to the + highest directory tree in the stacking order, i.e. the + second-to-last specified. + + If only two paths are specified, then the second + specified path is used both as the top-level directory tree in + the stacking order as seen from the host, as well as the mount + point for the overlay file system in the container. At least + two paths have to be specified. + + For details about overlay file systems, see overlayfs.txt. Note + that the semantics of overlay file systems are substantially + different from normal file systems, in particular regarding + reported device and inode information. Device and inode + information may change for a file while it is being written + to, and processes might see out-of-date versions of files at + times. Note that this switch automatically derives the + workdir= mount option for the overlay file + system from the top-level directory tree, making it a sibling + of it. It is hence essential that the top-level directory tree + is not a mount point itself (since the working directory must + be on the same file system as the top-most directory + tree). Also note that the lowerdir= mount + option receives the paths to stack in the opposite order of + this switch. + + + + + + + Specifies an environment variable assignment + to pass to the init process in the container, in the format + NAME=VALUE. This may be used to override + the default variables or to set additional variables. This + parameter may be used more than once. + + + + + + Allows the container to share certain system + facilities with the host. More specifically, this turns off + PID namespacing, UTS namespacing and IPC namespacing, and thus + allows the guest to see and interact more easily with + processes outside of the container. Note that using this + option makes it impossible to start up a full Operating System + in the container, as an init system cannot operate in this + mode. It is only useful to run specific programs or + applications this way, without involving an init system in the + container. This option implies . + This option may not be combined with + . + + + + + + Controls whether the container is registered + with + systemd-machined8. + Takes a boolean argument, which defaults to yes. + This option should be enabled when the container runs a full + Operating System (more specifically: an init system), and is + useful to ensure that the container is accessible via + machinectl1 + and shown by tools such as + ps1. + If the container does not run an init system, it is + recommended to set this option to no. Note + that implies + . + + + + + + Instead of creating a transient scope unit to + run the container in, simply register the service or scope + unit systemd-nspawn has been invoked in + with + systemd-machined8. + This has no effect if is used. + This switch should be used if + systemd-nspawn is invoked from within a + service unit, and the service unit's sole purpose is to run a + single systemd-nspawn container. This + option is not available if run from a user + session. + + + + + + Control the architecture ("personality") + reported by + uname2 + in the container. Currently, only x86 and + x86-64 are supported. This is useful when + running a 32-bit container on a 64-bit host. If this setting + is not used, the personality reported in the container is the + same as the one reported on the host. + + + + + + + Turns off any status output by the tool + itself. When this switch is used, the only output from nspawn + will be the console output of the container OS + itself. + + + + + MODE + + Boots the container in volatile mode. When no + mode parameter is passed or when mode is specified as + , full volatile mode is enabled. This + means the root directory is mounted as a mostly unpopulated + tmpfs instance, and + /usr from the OS tree is mounted into it + in read-only mode (the system thus starts up with read-only OS + image, but pristine state and configuration, any changes + are lost on shutdown). When the mode parameter + is specified as , the OS tree is + mounted read-only, but /var is mounted as + a tmpfs instance into it (the system thus + starts up with read-only OS resources and configuration, but + pristine state, and any changes to the latter are lost on + shutdown). When the mode parameter is specified as + (the default), the whole OS tree is made + available writable. + + Note that setting this to or + will only work correctly with + operating systems in the container that can boot up with only + /usr mounted, and are able to populate + /var automatically, as + needed. + + + + MODE + + Controls whether + systemd-nspawn shall search for and use + additional per-container settings from + .nspawn files. Takes a boolean or the + special values or + . + + If enabled (the default), a settings file named after the + machine (as specified with the + setting, or derived from the directory or image file name) + with the suffix .nspawn is searched in + /etc/systemd/nspawn/ and + /run/systemd/nspawn/. If it is found + there, its settings are read and used. If it is not found + there, it is subsequently searched in the same directory as the + image file or in the immediate parent of the root directory of + the container. In this case, if the file is found, its settings + will be also read and used, but potentially unsafe settings + are ignored. Note that in both these cases, settings on the + command line take precedence over the corresponding settings + from loaded .nspawn files, if both are + specified. Unsafe settings are considered all settings that + elevate the container's privileges or grant access to + additional resources such as files or directories of the + host. For details about the format and contents of + .nspawn files, consult + systemd.nspawn5. + + If this option is set to , the + file is searched, read and used the same way, however, the order of + precedence is reversed: settings read from the + .nspawn file will take precedence over + the corresponding command line options, if both are + specified. + + If this option is set to , the + file is searched, read and used the same way, but regardless + of being found in /etc/systemd/nspawn/, + /run/systemd/nspawn/ or next to the image + file or container root directory, all settings will take + effect, however, command line arguments still take precedence + over corresponding settings. + + If disabled, no .nspawn file is read + and no settings except the ones on the command line are in + effect. + + + + + + Configures support for notifications from the container's init process. + takes a boolean ( and ). + With option systemd-nspawn notifies systemd + with a READY=1 message when the init process is created. + With option systemd-nspawn waits for the + READY=1 message from the init process in the container + before sending its own to systemd. For more details about notifications + see sd_notify3). + + + + +
+ +
+ + + Examples + + + Build and boot a minimal BLAG distribution in a container + + # dnf -y --releasever=210k --installroot=/srv/mycontainer --disablerepo='*' --enablerepo=blag --enablerepo=updates install systemd passwd dnf blag-release vim-minimal +# systemd-nspawn -bD /srv/mycontainer + + This installs a minimal BLAG distribution into the + directory /srv/mycontainer/ + and then boots an OS in a namespace container in it. + + + + Spawn a shell in a container of a minimal gNewSense unstable distribution + + # debootstrap --arch=amd64 unstable ~/gnewsense-tree/ +# systemd-nspawn -D ~/gnewsense-tree/ + + This installs a minimal gNewSense unstable distribution into + the directory ~/gnewsense-tree/ and then + spawns a shell in a namespace container in it. + + + + Boot a minimal Parabola GNU/Linux-libre distribution in a container + + # pacstrap -c -d ~/parabola-tree/ base +# systemd-nspawn -bD ~/parabola-tree/ + + This installs a minimal Parabola GNU/Linux-libre distribution into the + directory ~/parabola-tree/ and then boots an OS + in a namespace container in it. + + + + Boot into an ephemeral <literal>btrfs</literal> snapshot of the host system + + # systemd-nspawn -D / -xb + + This runs a copy of the host system in a + btrfs snapshot which is removed immediately + when the container exits. All file system changes made during + runtime will be lost on shutdown, hence. + + + + Run a container with SELinux sandbox security contexts + + # chcon system_u:object_r:svirt_sandbox_file_t:s0:c0,c1 -R /srv/container +# systemd-nspawn -L system_u:object_r:svirt_sandbox_file_t:s0:c0,c1 -Z system_u:system_r:svirt_lxc_net_t:s0:c0,c1 -D /srv/container /bin/sh + + + + + Exit status + + The exit code of the program executed in the container is + returned. + + + + See Also + + systemd1, + systemd.nspawn5, + chroot1, + dnf8, + debootstrap8, + pacman8, + systemd.slice5, + machinectl1, + btrfs8 + + + +
diff --git a/src/systemd-nspawn/systemd-nspawn@.service.in b/src/systemd-nspawn/systemd-nspawn@.service.in new file mode 100644 index 0000000000..c8141639b6 --- /dev/null +++ b/src/systemd-nspawn/systemd-nspawn@.service.in @@ -0,0 +1,39 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Container %i +Documentation=man:systemd-nspawn(1) +PartOf=machines.target +Before=machines.target +After=network.target + +[Service] +ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i +KillMode=mixed +Type=notify +RestartForceExitStatus=133 +SuccessExitStatus=133 +Slice=machine.slice +Delegate=yes +TasksMax=16384 + +# Enforce a strict device policy, similar to the one nspawn configures +# when it allocates its own scope unit. Make sure to keep these +# policies in sync if you change them! +DevicePolicy=closed +DeviceAllow=/dev/net/tun rwm +DeviceAllow=char-pts rw + +# nspawn itself needs access to /dev/loop-control and /dev/loop, to +# implement the --image= option. Add these here, too. +DeviceAllow=/dev/loop-control rw +DeviceAllow=block-loop rw +DeviceAllow=block-blkext rw + +[Install] +WantedBy=machines.target diff --git a/src/systemd-nspawn/test-patch-uid.c b/src/systemd-nspawn/test-patch-uid.c new file mode 100644 index 0000000000..e751e0ec95 --- /dev/null +++ b/src/systemd-nspawn/test-patch-uid.c @@ -0,0 +1,62 @@ +/*** + 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 . +***/ + +#include + +#include "basic/log.h" +#include "basic/user-util.h" +#include "basic/util.h" + +#include "nspawn-patch-uid.h" + +int main(int argc, char *argv[]) { + uid_t shift, range; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc != 4) { + log_error("Expected PATH SHIFT RANGE parameters."); + return EXIT_FAILURE; + } + + r = parse_uid(argv[2], &shift); + if (r < 0) { + log_error_errno(r, "Failed to parse UID shift %s.", argv[2]); + return EXIT_FAILURE; + } + + r = parse_gid(argv[3], &range); + if (r < 0) { + log_error_errno(r, "Failed to parse UID range %s.", argv[3]); + return EXIT_FAILURE; + } + + r = path_patch_uid(argv[1], shift, range); + if (r < 0) { + log_error_errno(r, "Failed to patch directory tree: %m"); + return EXIT_FAILURE; + } + + log_info("Changed: %s", yes_no(r)); + + return EXIT_SUCCESS; +} diff --git a/src/systemd-rc-local-generator/Makefile b/src/systemd-rc-local-generator/Makefile new file mode 100644 index 0000000000..0e17ae7d94 --- /dev/null +++ b/src/systemd-rc-local-generator/Makefile @@ -0,0 +1,32 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemd_rc_local_generator_SOURCES = \ + src/rc-local-generator/rc-local-generator.c + +systemd_rc_local_generator_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-rc-local-generator/rc-local-generator.c b/src/systemd-rc-local-generator/rc-local-generator.c new file mode 100644 index 0000000000..2c2fbd21e1 --- /dev/null +++ b/src/systemd-rc-local-generator/rc-local-generator.c @@ -0,0 +1,101 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2011 Michal Schmidt + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/mkdir.h" +#include "basic/string-util.h" +#include "basic/util.h" + +#ifndef RC_LOCAL_SCRIPT_PATH_START +#define RC_LOCAL_SCRIPT_PATH_START "/etc/rc.d/rc.local" +#endif + +#ifndef RC_LOCAL_SCRIPT_PATH_STOP +#define RC_LOCAL_SCRIPT_PATH_STOP "/sbin/halt.local" +#endif + +static const char *arg_dest = "/tmp"; + +static int add_symlink(const char *service, const char *where) { + _cleanup_free_ char *from = NULL, *to = NULL; + int r; + + assert(service); + assert(where); + + from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service, NULL); + if (!from) + return log_oom(); + + to = strjoin(arg_dest, "/", where, ".wants/", service, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + + r = symlink(from, to); + if (r < 0) { + if (errno == EEXIST) + return 0; + + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r = EXIT_SUCCESS; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + if (access(RC_LOCAL_SCRIPT_PATH_START, X_OK) >= 0) { + log_debug("Automatically adding rc-local.service."); + + if (add_symlink("rc-local.service", "multi-user.target") < 0) + r = EXIT_FAILURE; + } + + if (access(RC_LOCAL_SCRIPT_PATH_STOP, X_OK) >= 0) { + log_debug("Automatically adding halt-local.service."); + + if (add_symlink("halt-local.service", "final.target") < 0) + r = EXIT_FAILURE; + } + + return r; +} diff --git a/src/systemd-remount-fs/Makefile b/src/systemd-remount-fs/Makefile new file mode 100644 index 0000000000..55cc776cdb --- /dev/null +++ b/src/systemd-remount-fs/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-remount-fs +systemd_remount_fs_SOURCES = \ + src/remount-fs/remount-fs.c \ + src/core/mount-setup.c \ + src/core/mount-setup.h + +systemd_remount_fs_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-remount-fs/mount-setup.c b/src/systemd-remount-fs/mount-setup.c new file mode 120000 index 0000000000..67cb74c218 --- /dev/null +++ b/src/systemd-remount-fs/mount-setup.c @@ -0,0 +1 @@ +../grp-system/libcore/mount-setup.c \ No newline at end of file diff --git a/src/systemd-remount-fs/mount-setup.h b/src/systemd-remount-fs/mount-setup.h new file mode 120000 index 0000000000..bae54ba700 --- /dev/null +++ b/src/systemd-remount-fs/mount-setup.h @@ -0,0 +1 @@ +../grp-system/libcore/mount-setup.h \ No newline at end of file diff --git a/src/systemd-remount-fs/remount-fs.c b/src/systemd-remount-fs/remount-fs.c new file mode 100644 index 0000000000..116c370ef7 --- /dev/null +++ b/src/systemd-remount-fs/remount-fs.c @@ -0,0 +1,156 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "basic/exit-status.h" +#include "basic/log.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/strv.h" +#include "basic/util.h" + +#include "mount-setup.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[]) { + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; + _cleanup_endmntent_ FILE *f = NULL; + struct mntent* me; + int r; + + if (argc > 1) { + log_error("This program takes no argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + f = setmntent("/etc/fstab", "r"); + if (!f) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + r = log_error_errno(errno, "Failed to open /etc/fstab: %m"); + goto finish; + } + + pids = hashmap_new(NULL); + if (!pids) { + r = log_oom(); + goto finish; + } + + while ((me = getmntent(f))) { + pid_t pid; + int k; + char *s; + + /* Remount the root fs, /usr and all API VFS */ + if (!mount_point_is_api(me->mnt_dir) && + !path_equal(me->mnt_dir, "/") && + !path_equal(me->mnt_dir, "/usr")) + continue; + + log_debug("Remounting %s", me->mnt_dir); + + pid = fork(); + if (pid < 0) { + r = log_error_errno(errno, "Failed to fork: %m"); + goto finish; + } + + if (pid == 0) { + /* Child */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); + + 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); + } + + /* Parent */ + + s = strdup(me->mnt_dir); + if (!s) { + r = log_oom(); + goto finish; + } + + k = hashmap_put(pids, PID_TO_PTR(pid), s); + if (k < 0) { + free(s); + r = log_oom(); + goto finish; + } + } + + r = 0; + while (!hashmap_isempty(pids)) { + siginfo_t si = {}; + char *s; + + if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + r = log_error_errno(errno, "waitid() failed: %m"); + goto finish; + } + + 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) + log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status); + else + log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status)); + + r = -ENOEXEC; + } + + free(s); + } + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-remount-fs/systemd-remount-fs.service.in b/src/systemd-remount-fs/systemd-remount-fs.service.in new file mode 100644 index 0000000000..8d9daacaa5 --- /dev/null +++ b/src/systemd-remount-fs/systemd-remount-fs.service.in @@ -0,0 +1,22 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Remount Root and Kernel File Systems +Documentation=man:systemd-remount-fs.service(8) +Documentation=http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-fsck-root.service +Before=local-fs-pre.target local-fs.target shutdown.target +Wants=local-fs-pre.target +ConditionPathExists=/etc/fstab + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-remount-fs diff --git a/src/systemd-remount-fs/systemd-remount-fs.service.xml b/src/systemd-remount-fs/systemd-remount-fs.service.xml new file mode 100644 index 0000000000..176f2b2d20 --- /dev/null +++ b/src/systemd-remount-fs/systemd-remount-fs.service.xml @@ -0,0 +1,88 @@ + + + + + + + + systemd-remount-fs.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-remount-fs.service + 8 + + + + systemd-remount-fs.service + systemd-remount-fs + Remount root and kernel file systems + + + + systemd-remount-fs.service + /usr/lib/systemd/systemd-remount-fs + + + + Description + + systemd-remount-fs.service is an + early boot service that applies mount options listed in + fstab5 + to the root file system, the /usr file system, + and the kernel API file systems. This is required so that the + mount options of these file systems — which are pre-mounted by + the kernel, the initial RAM disk, container environments or system + manager code — are updated to those listed in + /etc/fstab. This service ignores normal file + systems and only changes the root file system (i.e. + /), /usr and the virtual + kernel API file systems such as /proc, + /sys or /dev. This + service executes no operation if /etc/fstab + does not exist or lists no entries for the mentioned file + systems. + + For a longer discussion of kernel API file systems see + API + File Systems. + + + + See Also + + systemd1, + fstab5, + mount8 + + + + diff --git a/src/systemd-reply-password/Makefile b/src/systemd-reply-password/Makefile new file mode 100644 index 0000000000..18514df027 --- /dev/null +++ b/src/systemd-reply-password/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-reply-password +systemd_reply_password_SOURCES = \ + src/reply-password/reply-password.c + +systemd_reply_password_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-reply-password/reply-password.c b/src/systemd-reply-password/reply-password.c new file mode 100644 index 0000000000..7a5cea0e1e --- /dev/null +++ b/src/systemd-reply-password/reply-password.c @@ -0,0 +1,96 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" + +static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + + assert(fd >= 0); + assert(socket_name); + assert(packet); + + strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); + + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return log_error_errno(errno, "Failed to send: %m"); + + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_close_ int fd = -1; + char packet[LINE_MAX]; + size_t length; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 3) { + log_error("Wrong number of arguments."); + return EXIT_FAILURE; + } + + if (streq(argv[1], "1")) { + + packet[0] = '+'; + if (!fgets(packet+1, sizeof(packet)-1, stdin)) { + r = log_error_errno(errno, "Failed to read password: %m"); + goto finish; + } + + truncate_nl(packet+1); + length = 1 + strlen(packet+1) + 1; + } else if (streq(argv[1], "0")) { + packet[0] = '-'; + 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) { + r = log_error_errno(errno, "socket() failed: %m"); + goto finish; + } + + r = send_on_socket(fd, argv[2], packet, length); + +finish: + memory_erase(packet, sizeof(packet)); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-socket-proxyd/Makefile b/src/systemd-socket-proxyd/Makefile new file mode 100644 index 0000000000..31261392f1 --- /dev/null +++ b/src/systemd-socket-proxyd/Makefile @@ -0,0 +1,34 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootlibexec_PROGRAMS += systemd-socket-proxyd + +systemd_socket_proxyd_SOURCES = \ + src/socket-proxy/socket-proxyd.c + +systemd_socket_proxyd_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-socket-proxyd/socket-proxyd.c b/src/systemd-socket-proxyd/socket-proxyd.c new file mode 100644 index 0000000000..888850595b --- /dev/null +++ b/src/systemd-socket-proxyd/socket-proxyd.c @@ -0,0 +1,679 @@ +/*** + This file is part of systemd. + + Copyright 2013 David Strauss + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/path-util.h" +#include "basic/set.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-resolve/sd-resolve.h" + +#define BUFFER_SIZE (256 * 1024) +#define CONNECTIONS_MAX 256 + +static const char *arg_remote_host = NULL; + +typedef struct Context { + sd_event *event; + sd_resolve *resolve; + + Set *listen; + Set *connections; +} Context; + +typedef struct Connection { + Context *context; + + int server_fd, client_fd; + int server_to_client_buffer[2]; /* a pipe */ + int client_to_server_buffer[2]; /* a pipe */ + + size_t server_to_client_buffer_full, client_to_server_buffer_full; + size_t server_to_client_buffer_size, client_to_server_buffer_size; + + sd_event_source *server_event_source, *client_event_source; + + sd_resolve_query *resolve_query; +} Connection; + +static void connection_free(Connection *c) { + assert(c); + + if (c->context) + set_remove(c->context->connections, c); + + sd_event_source_unref(c->server_event_source); + sd_event_source_unref(c->client_event_source); + + safe_close(c->server_fd); + safe_close(c->client_fd); + + safe_close_pair(c->server_to_client_buffer); + safe_close_pair(c->client_to_server_buffer); + + sd_resolve_query_unref(c->resolve_query); + + free(c); +} + +static void context_free(Context *context) { + sd_event_source *es; + Connection *c; + + assert(context); + + while ((es = set_steal_first(context->listen))) + sd_event_source_unref(es); + + while ((c = set_first(context->connections))) + connection_free(c); + + set_free(context->listen); + set_free(context->connections); + + sd_event_unref(context->event); + sd_resolve_unref(context->resolve); +} + +static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) { + int r; + + assert(c); + assert(buffer); + assert(sz); + + if (buffer[0] >= 0) + return 0; + + r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK); + if (r < 0) + return log_error_errno(errno, "Failed to allocate pipe buffer: %m"); + + (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE); + + r = fcntl(buffer[0], F_GETPIPE_SZ); + if (r < 0) + return log_error_errno(errno, "Failed to get pipe buffer size: %m"); + + assert(r > 0); + *sz = r; + + return 0; +} + +static int connection_shovel( + Connection *c, + int *from, int buffer[2], int *to, + size_t *full, size_t *sz, + sd_event_source **from_source, sd_event_source **to_source) { + + bool shoveled; + + assert(c); + assert(from); + assert(buffer); + assert(buffer[0] >= 0); + assert(buffer[1] >= 0); + assert(to); + assert(full); + assert(sz); + assert(from_source); + assert(to_source); + + do { + ssize_t z; + + shoveled = false; + + if (*full < *sz && *from >= 0 && *to >= 0) { + z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); + if (z > 0) { + *full += z; + shoveled = true; + } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { + *from_source = sd_event_source_unref(*from_source); + *from = safe_close(*from); + } else if (errno != EAGAIN && errno != EINTR) + return log_error_errno(errno, "Failed to splice: %m"); + } + + if (*full > 0 && *to >= 0) { + z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK); + if (z > 0) { + *full -= z; + shoveled = true; + } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { + *to_source = sd_event_source_unref(*to_source); + *to = safe_close(*to); + } else if (errno != EAGAIN && errno != EINTR) + return log_error_errno(errno, "Failed to splice: %m"); + } + } while (shoveled); + + return 0; +} + +static int connection_enable_event_sources(Connection *c); + +static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Connection *c = userdata; + int r; + + assert(s); + assert(fd >= 0); + assert(c); + + r = connection_shovel(c, + &c->server_fd, c->server_to_client_buffer, &c->client_fd, + &c->server_to_client_buffer_full, &c->server_to_client_buffer_size, + &c->server_event_source, &c->client_event_source); + if (r < 0) + goto quit; + + r = connection_shovel(c, + &c->client_fd, c->client_to_server_buffer, &c->server_fd, + &c->client_to_server_buffer_full, &c->client_to_server_buffer_size, + &c->client_event_source, &c->server_event_source); + if (r < 0) + goto quit; + + /* EOF on both sides? */ + if (c->server_fd == -1 && c->client_fd == -1) + goto quit; + + /* Server closed, and all data written to client? */ + if (c->server_fd == -1 && c->server_to_client_buffer_full <= 0) + goto quit; + + /* Client closed, and all data written to server? */ + if (c->client_fd == -1 && c->client_to_server_buffer_full <= 0) + goto quit; + + r = connection_enable_event_sources(c); + if (r < 0) + goto quit; + + return 1; + +quit: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int connection_enable_event_sources(Connection *c) { + uint32_t a = 0, b = 0; + int r; + + assert(c); + + if (c->server_to_client_buffer_full > 0) + b |= EPOLLOUT; + if (c->server_to_client_buffer_full < c->server_to_client_buffer_size) + a |= EPOLLIN; + + if (c->client_to_server_buffer_full > 0) + a |= EPOLLOUT; + if (c->client_to_server_buffer_full < c->client_to_server_buffer_size) + b |= EPOLLIN; + + if (c->server_event_source) + r = sd_event_source_set_io_events(c->server_event_source, a); + else if (c->server_fd >= 0) + r = sd_event_add_io(c->context->event, &c->server_event_source, c->server_fd, a, traffic_cb, c); + else + r = 0; + + if (r < 0) + return log_error_errno(r, "Failed to set up server event source: %m"); + + if (c->client_event_source) + r = sd_event_source_set_io_events(c->client_event_source, b); + else if (c->client_fd >= 0) + r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, b, traffic_cb, c); + else + r = 0; + + if (r < 0) + return log_error_errno(r, "Failed to set up client event source: %m"); + + return 0; +} + +static int connection_complete(Connection *c) { + int r; + + assert(c); + + r = connection_create_pipes(c, c->server_to_client_buffer, &c->server_to_client_buffer_size); + if (r < 0) + goto fail; + + r = connection_create_pipes(c, c->client_to_server_buffer, &c->client_to_server_buffer_size); + if (r < 0) + goto fail; + + r = connection_enable_event_sources(c); + if (r < 0) + goto fail; + + return 0; + +fail: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Connection *c = userdata; + socklen_t solen; + int error, r; + + assert(s); + assert(fd >= 0); + assert(c); + + solen = sizeof(error); + r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen); + if (r < 0) { + log_error_errno(errno, "Failed to issue SO_ERROR: %m"); + goto fail; + } + + if (error != 0) { + log_error_errno(error, "Failed to connect to remote host: %m"); + goto fail; + } + + c->client_event_source = sd_event_source_unref(c->client_event_source); + + return connection_complete(c); + +fail: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) { + int r; + + assert(c); + assert(sa); + assert(salen); + + c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (c->client_fd < 0) { + log_error_errno(errno, "Failed to get remote socket: %m"); + goto fail; + } + + r = connect(c->client_fd, sa, salen); + if (r < 0) { + if (errno == EINPROGRESS) { + r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c); + if (r < 0) { + log_error_errno(r, "Failed to add connection socket: %m"); + goto fail; + } + + r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT); + if (r < 0) { + log_error_errno(r, "Failed to enable oneshot event source: %m"); + goto fail; + } + } else { + log_error_errno(errno, "Failed to connect to remote host: %m"); + goto fail; + } + } else { + r = connection_complete(c); + if (r < 0) + goto fail; + } + + return 0; + +fail: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int resolve_cb(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { + Connection *c = userdata; + + assert(q); + assert(c); + + if (ret != 0) { + log_error("Failed to resolve host: %s", gai_strerror(ret)); + goto fail; + } + + c->resolve_query = sd_resolve_query_unref(c->resolve_query); + + return connection_start(c, ai->ai_addr, ai->ai_addrlen); + +fail: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int resolve_remote(Connection *c) { + + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_ADDRCONFIG + }; + + union sockaddr_union sa = {}; + const char *node, *service; + int r; + + if (path_is_absolute(arg_remote_host)) { + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + } + + if (arg_remote_host[0] == '@') { + sa.un.sun_family = AF_UNIX; + sa.un.sun_path[0] = 0; + strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + } + + service = strrchr(arg_remote_host, ':'); + if (service) { + node = strndupa(arg_remote_host, service - arg_remote_host); + service++; + } else { + node = arg_remote_host; + service = "80"; + } + + log_debug("Looking up address info for %s:%s", node, service); + r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c); + if (r < 0) { + log_error_errno(r, "Failed to resolve remote host: %m"); + goto fail; + } + + return 0; + +fail: + connection_free(c); + return 0; /* ignore errors, continue serving */ +} + +static int add_connection_socket(Context *context, int fd) { + Connection *c; + int r; + + assert(context); + assert(fd >= 0); + + if (set_size(context->connections) > CONNECTIONS_MAX) { + log_warning("Hit connection limit, refusing connection."); + safe_close(fd); + return 0; + } + + r = set_ensure_allocated(&context->connections, NULL); + if (r < 0) { + log_oom(); + return 0; + } + + c = new0(Connection, 1); + if (!c) { + log_oom(); + return 0; + } + + c->context = context; + c->server_fd = fd; + c->client_fd = -1; + c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1; + c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1; + + r = set_put(context->connections, c); + if (r < 0) { + free(c); + log_oom(); + return 0; + } + + return resolve_remote(c); +} + +static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_free_ char *peer = NULL; + Context *context = userdata; + int nfd = -1, r; + + assert(s); + assert(fd >= 0); + assert(revents & EPOLLIN); + assert(context); + + nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (nfd < 0) { + if (errno != -EAGAIN) + log_warning_errno(errno, "Failed to accept() socket: %m"); + } else { + getpeername_pretty(nfd, true, &peer); + log_debug("New connection from %s", strna(peer)); + + r = add_connection_socket(context, nfd); + if (r < 0) { + log_error_errno(r, "Failed to accept connection, ignoring: %m"); + safe_close(fd); + } + } + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) { + log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m"); + sd_event_exit(context->event, r); + return r; + } + + return 1; +} + +static int add_listen_socket(Context *context, int fd) { + sd_event_source *source; + int r; + + assert(context); + assert(fd >= 0); + + r = set_ensure_allocated(&context->listen, NULL); + if (r < 0) { + log_oom(); + return r; + } + + r = sd_is_socket(fd, 0, SOCK_STREAM, 1); + if (r < 0) + return log_error_errno(r, "Failed to determine socket type: %m"); + if (r == 0) { + log_error("Passed in socket is not a stream socket."); + return -EINVAL; + } + + r = fd_nonblock(fd, true); + if (r < 0) + return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m"); + + r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context); + if (r < 0) + return log_error_errno(r, "Failed to add event source: %m"); + + r = set_put(context->listen, source); + if (r < 0) { + log_error_errno(r, "Failed to add source to set: %m"); + sd_event_source_unref(source); + return r; + } + + /* Set the watcher to oneshot in case other processes are also + * watching to accept(). */ + r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); + if (r < 0) + return log_error_errno(r, "Failed to enable oneshot mode: %m"); + + return 0; +} + +static void help(void) { + printf("%1$s [HOST:PORT]\n" + "%1$s [SOCKET]\n\n" + "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n" + " -h --help Show this help\n" + " --version Show package version\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_IGNORE_ENV + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind >= argc) { + log_error("Not enough parameters."); + return -EINVAL; + } + + if (argc != optind+1) { + log_error("Too many parameters."); + return -EINVAL; + } + + arg_remote_host = argv[optind]; + return 1; +} + +int main(int argc, char *argv[]) { + Context context = {}; + int r, n, fd; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_event_default(&context.event); + if (r < 0) { + log_error_errno(r, "Failed to allocate event loop: %m"); + goto finish; + } + + r = sd_resolve_default(&context.resolve); + if (r < 0) { + log_error_errno(r, "Failed to allocate resolver: %m"); + goto finish; + } + + r = sd_resolve_attach_event(context.resolve, context.event, 0); + if (r < 0) { + log_error_errno(r, "Failed to attach resolver: %m"); + goto finish; + } + + sd_event_set_watchdog(context.event, true); + + n = sd_listen_fds(1); + if (n < 0) { + log_error("Failed to receive sockets from parent."); + r = n; + goto finish; + } else if (n == 0) { + log_error("Didn't get any sockets passed in."); + r = -EINVAL; + goto finish; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + r = add_listen_socket(&context, fd); + if (r < 0) + goto finish; + } + + r = sd_event_loop(context.event); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + +finish: + context_free(&context); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-socket-proxyd/systemd-socket-proxyd.xml b/src/systemd-socket-proxyd/systemd-socket-proxyd.xml new file mode 100644 index 0000000000..ae4217b910 --- /dev/null +++ b/src/systemd-socket-proxyd/systemd-socket-proxyd.xml @@ -0,0 +1,190 @@ + + + + + + + + systemd-socket-proxyd + systemd + + + Developer + David + Strauss + david@davidstrauss.net + + + + + systemd-socket-proxyd + 8 + + + systemd-socket-proxyd + Bidirectionally proxy local sockets to another (possibly remote) socket. + + + + systemd-socket-proxyd + OPTIONS + HOST:PORT + + + systemd-socket-proxyd + OPTIONS + UNIX-DOMAIN-SOCKET-PATH + + + + + Description + + systemd-socket-proxyd is a generic + socket-activated network socket forwarder proxy daemon for IPv4, + IPv6 and UNIX stream sockets. It may be used to bi-directionally + forward traffic from a local listening socket to a local or remote + destination socket. + + One use of this tool is to provide socket activation support + for services that do not natively support socket activation. On + behalf of the service to activate, the proxy inherits the socket + from systemd, accepts each client connection, opens a connection + to a configured server for each client, and then bidirectionally + forwards data between the two. + This utility's behavior is similar to + socat1. + The main differences for systemd-socket-proxyd + are support for socket activation with + Accept=false and an event-driven + design that scales better with the number of + connections. + + + Options + The following options are understood: + + + + + + + Exit status + On success, 0 is returned, a non-zero failure + code otherwise. + + + Examples + + Simple Example + Use two services with a dependency and no namespace + isolation. + + proxy-to-nginx.socket + + + + proxy-to-nginx.service + + + + nginx.conf + + + + + + Enabling the proxy + + + + + Namespace Example + Similar as above, but runs the socket proxy and the main + service in the same private namespace, assuming that + nginx.service has + PrivateTmp= and + PrivateNetwork= set, too. + + proxy-to-nginx.socket + + + + proxy-to-nginx.service + + + + nginx.conf + + + + Enabling the proxy + + + + + + See Also + + systemd1, + systemd.socket5, + systemd.service5, + systemctl1, + socat1, + nginx1, + curl1 + + + diff --git a/src/systemd-stdio-bridge/Makefile b/src/systemd-stdio-bridge/Makefile new file mode 100644 index 0000000000..096d94b90a --- /dev/null +++ b/src/systemd-stdio-bridge/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +bin_PROGRAMS += systemd-stdio-bridge +systemd_stdio_bridge_SOURCES = \ + src/stdio-bridge/stdio-bridge.c + +systemd_stdio_bridge_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-stdio-bridge/stdio-bridge.c b/src/systemd-stdio-bridge/stdio-bridge.c new file mode 100644 index 0000000000..33fe2f7e28 --- /dev/null +++ b/src/systemd-stdio-bridge/stdio-bridge.c @@ -0,0 +1,302 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "basic/build.h" +#include "basic/log.h" +#include "basic/util.h" +#include "sd-bus/bus-internal.h" +#include "shared/bus-util.h" + +#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket" + +const char *arg_bus_path = DEFAULT_BUS_PATH; + +static int help(void) { + + printf("%s [OPTIONS...]\n\n" + "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --bus-path=PATH Path to the kernel bus (default: %s)\n", + program_invocation_short_name, DEFAULT_BUS_PATH); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "bus-path", required_argument, NULL, 'p' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + case 'p': + arg_bus_path = optarg; + break; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL; + sd_id128_t server_id; + bool is_unix; + int r, in_fd, out_fd; + + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_listen_fds(0); + if (r == 0) { + in_fd = STDIN_FILENO; + out_fd = STDOUT_FILENO; + } else if (r == 1) { + in_fd = SD_LISTEN_FDS_START; + out_fd = SD_LISTEN_FDS_START; + } else { + log_error("Illegal number of file descriptors passed\n"); + goto finish; + } + + is_unix = + sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && + sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; + + r = sd_bus_new(&a); + if (r < 0) { + log_error_errno(r, "Failed to allocate bus: %m"); + goto finish; + } + + r = sd_bus_set_address(a, arg_bus_path); + if (r < 0) { + log_error_errno(r, "Failed to set address to connect to: %m"); + goto finish; + } + + r = sd_bus_negotiate_fds(a, is_unix); + if (r < 0) { + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_start(a); + if (r < 0) { + log_error_errno(r, "Failed to start bus client: %m"); + goto finish; + } + + r = sd_bus_get_bus_id(a, &server_id); + if (r < 0) { + log_error_errno(r, "Failed to get server ID: %m"); + goto finish; + } + + r = sd_bus_new(&b); + if (r < 0) { + log_error_errno(r, "Failed to allocate bus: %m"); + goto finish; + } + + r = sd_bus_set_fd(b, in_fd, out_fd); + if (r < 0) { + log_error_errno(r, "Failed to set fds: %m"); + goto finish; + } + + r = sd_bus_set_server(b, 1, server_id); + if (r < 0) { + log_error_errno(r, "Failed to set server mode: %m"); + goto finish; + } + + r = sd_bus_negotiate_fds(b, is_unix); + if (r < 0) { + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_set_anonymous(b, true); + if (r < 0) { + log_error_errno(r, "Failed to set anonymous authentication: %m"); + goto finish; + } + + r = sd_bus_start(b); + if (r < 0) { + log_error_errno(r, "Failed to start bus client: %m"); + goto finish; + } + + for (;;) { + _cleanup_(sd_bus_message_unrefp)sd_bus_message *m = NULL; + int events_a, events_b, fd; + uint64_t timeout_a, timeout_b, t; + struct timespec _ts, *ts; + + r = sd_bus_process(a, &m); + if (r < 0) { + log_error_errno(r, "Failed to process bus a: %m"); + goto finish; + } + + if (m) { + r = sd_bus_send(b, m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send message: %m"); + goto finish; + } + } + + if (r > 0) + continue; + + r = sd_bus_process(b, &m); + if (r < 0) { + /* treat 'connection reset by peer' as clean exit condition */ + if (r == -ECONNRESET) + r = 0; + + goto finish; + } + + if (m) { + r = sd_bus_send(a, m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send message: %m"); + goto finish; + } + } + + if (r > 0) + continue; + + fd = sd_bus_get_fd(a); + if (fd < 0) { + r = fd; + log_error_errno(r, "Failed to get fd: %m"); + goto finish; + } + + events_a = sd_bus_get_events(a); + if (events_a < 0) { + r = events_a; + log_error_errno(r, "Failed to get events mask: %m"); + goto finish; + } + + r = sd_bus_get_timeout(a, &timeout_a); + if (r < 0) { + log_error_errno(r, "Failed to get timeout: %m"); + goto finish; + } + + events_b = sd_bus_get_events(b); + if (events_b < 0) { + r = events_b; + log_error_errno(r, "Failed to get events mask: %m"); + goto finish; + } + + r = sd_bus_get_timeout(b, &timeout_b); + if (r < 0) { + log_error_errno(r, "Failed to get timeout: %m"); + goto finish; + } + + t = timeout_a; + if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a)) + t = timeout_b; + + if (t == (uint64_t) -1) + ts = NULL; + else { + usec_t nw; + + nw = now(CLOCK_MONOTONIC); + if (t > nw) + t -= nw; + else + t = 0; + + ts = timespec_store(&_ts, t); + } + + { + struct pollfd p[3] = { + {.fd = fd, .events = events_a, }, + {.fd = STDIN_FILENO, .events = events_b & POLLIN, }, + {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }}; + + r = ppoll(p, ELEMENTSOF(p), ts, NULL); + } + if (r < 0) { + log_error("ppoll() failed: %m"); + goto finish; + } + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-system-update-generator/Makefile b/src/systemd-system-update-generator/Makefile new file mode 100644 index 0000000000..5e66309753 --- /dev/null +++ b/src/systemd-system-update-generator/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +systemgenerator_PROGRAMS += systemd-system-update-generator +systemd_system_update_generator_SOURCES = \ + src/system-update-generator/system-update-generator.c + +systemd_system_update_generator_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-system-update-generator/system-update-generator.c b/src/systemd-system-update-generator/system-update-generator.c new file mode 100644 index 0000000000..e95ab39bb5 --- /dev/null +++ b/src/systemd-system-update-generator/system-update-generator.c @@ -0,0 +1,73 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/util.h" + +/* + * Implements the logic described in + * http://freedesktop.org/wiki/Software/systemd/SystemUpdates + */ + +static const char *arg_dest = "/tmp"; + +static int generate_symlink(void) { + const char *p = NULL; + + if (laccess("/system-update", F_OK) < 0) { + if (errno == ENOENT) + return 0; + + log_error_errno(errno, "Failed to check for system update: %m"); + return -EINVAL; + } + + p = strjoina(arg_dest, "/default.target"); + if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", p); + + return 0; +} + +int main(int argc, char *argv[]) { + int r; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[2]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = generate_symlink(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-system-update-generator/systemd-system-update-generator.xml b/src/systemd-system-update-generator/systemd-system-update-generator.xml new file mode 100644 index 0000000000..833ed79646 --- /dev/null +++ b/src/systemd-system-update-generator/systemd-system-update-generator.xml @@ -0,0 +1,75 @@ + + + + + + + + systemd-system-update-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-system-update-generator + 8 + + + + systemd-system-update-generator + Generator for redirecting boot to offline update mode + + + + /usr/lib/systemd/system-generators/systemd-system-update-generator + + + + Description + + systemd-system-update-generator is a + generator that automatically redirects the boot process to + system-update.target, if + /system-update exists. This is required to + implement the logic explained in the + systemd.offline-updates7. + + + systemd-system-update-generator implements + systemd.generator7. + + + + See Also + + systemd1, + systemd.special7 + + + + diff --git a/src/systemd-timesyncd/.gitignore b/src/systemd-timesyncd/.gitignore new file mode 100644 index 0000000000..35f4d76f79 --- /dev/null +++ b/src/systemd-timesyncd/.gitignore @@ -0,0 +1,2 @@ +/timesyncd.conf +/timesyncd-gperf.c diff --git a/src/systemd-timesyncd/90-timesyncd.preset b/src/systemd-timesyncd/90-timesyncd.preset new file mode 100644 index 0000000000..d40b418358 --- /dev/null +++ b/src/systemd-timesyncd/90-timesyncd.preset @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +enable systemd-timesyncd.service diff --git a/src/systemd-timesyncd/Makefile b/src/systemd-timesyncd/Makefile new file mode 100644 index 0000000000..8bea585e8c --- /dev/null +++ b/src/systemd-timesyncd/Makefile @@ -0,0 +1,65 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(ENABLE_TIMESYNCD),) +systemd_timesyncd_SOURCES = \ + src/timesync/timesyncd.c \ + src/timesync/timesyncd-manager.c \ + src/timesync/timesyncd-manager.h \ + src/timesync/timesyncd-conf.c \ + src/timesync/timesyncd-conf.h \ + src/timesync/timesyncd-server.c \ + src/timesync/timesyncd-server.h + +nodist_systemd_timesyncd_SOURCES = \ + src/timesync/timesyncd-gperf.c + +systemd_timesyncd_LDADD = \ + libsystemd-network.la \ + libsystemd-shared.la \ + -lm + +rootlibexec_PROGRAMS += \ + systemd-timesyncd + +nodist_systemunit_DATA += \ + units/systemd-timesyncd.service + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-timesyncd.service $(pkgsysconfdir)/system/sysinit.target.wants/systemd-timesyncd.service + +nodist_pkgsysconf_DATA += \ + src/timesync/timesyncd.conf + +endif # ENABLE_TIMESYNCD + +gperf_gperf_sources += \ + src/timesync/timesyncd-gperf.gperf + +EXTRA_DIST += \ + units/systemd-timesyncd.service.in \ + src/timesync/timesyncd.conf.in + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-timesyncd/systemd-timesyncd.service.in b/src/systemd-timesyncd/systemd-timesyncd.service.in new file mode 100644 index 0000000000..df1e339196 --- /dev/null +++ b/src/systemd-timesyncd/systemd-timesyncd.service.in @@ -0,0 +1,35 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Time Synchronization +Documentation=man:systemd-timesyncd.service(8) +ConditionCapability=CAP_SYS_TIME +ConditionVirtualization=!container +DefaultDependencies=no +RequiresMountsFor=/var/lib/systemd/clock +After=systemd-remount-fs.service systemd-tmpfiles-setup.service systemd-sysusers.service +Before=time-sync.target sysinit.target shutdown.target +Conflicts=shutdown.target +Wants=time-sync.target + +[Service] +Type=notify +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-timesyncd +CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +ProtectHome=yes +WatchdogSec=3min +MemoryDenyWriteExecute=yes +SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io + +[Install] +WantedBy=sysinit.target diff --git a/src/systemd-timesyncd/systemd-timesyncd.service.xml b/src/systemd-timesyncd/systemd-timesyncd.service.xml new file mode 100644 index 0000000000..6ec384313b --- /dev/null +++ b/src/systemd-timesyncd/systemd-timesyncd.service.xml @@ -0,0 +1,108 @@ + + + + + + + + + systemd-timesyncd.service + systemd + + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + systemd-timesyncd.service + 8 + + + + systemd-timesyncd.service + systemd-timesyncd + Network Time Synchronization + + + + systemd-timesyncd.service + /usr/lib/systemd/systemd-timesyncd + + + + Description + + systemd-timesyncd is a system service + that may be used to synchronize the local system clock with a + remote Network Time Protocol server. It also saves the local time + to disk every time the clock has been synchronized and uses this + to possibly advance the system realtime clock on subsequent + reboots to ensure it monotonically advances even if the system + lacks a battery-buffered RTC chip. + + The NTP servers contacted are determined from the global + settings in + timesyncd.conf5, + the per-link static settings in .network + files, and the per-link dynamic settings received over DHCP. See + systemd.network5 + for more details. + + timedatectl1's + set-ntp command may be used to enable and + start, or disable and stop this service. + + + + Files + + + + /var/lib/systemd/clock + + + This file contains the timestamp of the last successful + synchronization. + + + + + + + See Also + + systemd1, + timesyncd.conf5, + systemd.network5, + systemd-networkd.service8, + timedatectl1, + localtime5, + hwclock8 + + + + diff --git a/src/systemd-timesyncd/systemd-timesyncd.sysusers b/src/systemd-timesyncd/systemd-timesyncd.sysusers new file mode 100644 index 0000000000..4d7af7b3ae --- /dev/null +++ b/src/systemd-timesyncd/systemd-timesyncd.sysusers @@ -0,0 +1,8 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +u systemd-timesync - "systemd Time Synchronization" diff --git a/src/systemd-timesyncd/timesyncd-conf.c b/src/systemd-timesyncd/timesyncd-conf.c new file mode 100644 index 0000000000..f2c6efd8e2 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-conf.c @@ -0,0 +1,107 @@ +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/extract-word.h" +#include "basic/string-util.h" + +#include "timesyncd-conf.h" +#include "timesyncd-manager.h" +#include "timesyncd-server.h" + +int manager_parse_server_string(Manager *m, ServerType type, const char *string) { + ServerName *first; + int r; + + assert(m); + assert(string); + + first = type == SERVER_FALLBACK ? m->fallback_servers : m->system_servers; + + for (;;) { + _cleanup_free_ char *word = NULL; + bool found = false; + ServerName *n; + + 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, word)) { + found = true; + break; + } + + if (found) + continue; + + r = server_name_new(m, NULL, type, word); + if (r < 0) + return r; + } + + return 0; +} + +int config_parse_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) { + + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + manager_flush_server_names(m, ltype); + 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); + return 0; + } + } + + return 0; +} + +int manager_parse_config_file(Manager *m) { + assert(m); + + 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/systemd-timesyncd/timesyncd-conf.h b/src/systemd-timesyncd/timesyncd-conf.h new file mode 100644 index 0000000000..c24d54a1e3 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-conf.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "shared/conf-parser.h" + +#include "timesyncd-manager.h" + +const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length); + +int manager_parse_server_string(Manager *m, ServerType type, const char *string); + +int config_parse_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 manager_parse_config_file(Manager *m); diff --git a/src/systemd-timesyncd/timesyncd-gperf.gperf b/src/systemd-timesyncd/timesyncd-gperf.gperf new file mode 100644 index 0000000000..7e347a033c --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-gperf.gperf @@ -0,0 +1,21 @@ +%{ +#include + +#include "shared/conf-parser.h" + +#include "timesyncd-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name timesyncdd_gperf_hash +%define lookup-function-name timesyncd_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Time.NTP, config_parse_servers, SERVER_SYSTEM, 0 +Time.Servers, config_parse_servers, SERVER_SYSTEM, 0 +Time.FallbackNTP, config_parse_servers, SERVER_FALLBACK, 0 diff --git a/src/systemd-timesyncd/timesyncd-manager.c b/src/systemd-timesyncd/timesyncd-manager.c new file mode 100644 index 0000000000..9b10081961 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-manager.c @@ -0,0 +1,1157 @@ +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/list.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/ratelimit.h" +#include "basic/socket-util.h" +#include "basic/sparse-endian.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" +#include "basic/util.h" +#include "sd-network/network-util.h" + +#include "timesyncd-conf.h" +#include "timesyncd-manager.h" + +#ifndef ADJ_SETOFFSET +#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ +#endif + +/* expected accuracy of time synchronization; used to adjust the poll interval */ +#define NTP_ACCURACY_SEC 0.2 + +/* + * "A client MUST NOT under any conditions use a poll interval less + * than 15 seconds." + */ +#define NTP_POLL_INTERVAL_MIN_SEC 32 +#define NTP_POLL_INTERVAL_MAX_SEC 2048 + +/* + * Maximum delta in seconds which the system clock is gradually adjusted + * (slew) to approach the network time. Deltas larger that this are set by + * letting the system time jump. The kernel's limit for adjtime is 0.5s. + */ +#define NTP_MAX_ADJUST 0.4 + +/* NTP protocol, packet header */ +#define NTP_LEAP_PLUSSEC 1 +#define NTP_LEAP_MINUSSEC 2 +#define NTP_LEAP_NOTINSYNC 3 +#define NTP_MODE_CLIENT 3 +#define NTP_MODE_SERVER 4 +#define NTP_FIELD_LEAP(f) (((f) >> 6) & 3) +#define NTP_FIELD_VERSION(f) (((f) >> 3) & 7) +#define NTP_FIELD_MODE(f) ((f) & 7) +#define NTP_FIELD(l, v, m) (((l) << 6) | ((v) << 3) | (m)) + +/* Maximum acceptable root distance in seconds. */ +#define NTP_MAX_ROOT_DISTANCE 5.0 + +/* Maximum number of missed replies before selecting another source. */ +#define NTP_MAX_MISSED_REPLIES 2 + +/* + * "NTP timestamps are represented as a 64-bit unsigned fixed-point number, + * in seconds relative to 0h on 1 January 1900." + */ +#define OFFSET_1900_1970 UINT64_C(2208988800) + +#define RETRY_USEC (30*USEC_PER_SEC) +#define RATELIMIT_INTERVAL_USEC (10*USEC_PER_SEC) +#define RATELIMIT_BURST 10 + +#define TIMEOUT_USEC (10*USEC_PER_SEC) + +struct ntp_ts { + be32_t sec; + be32_t frac; +} _packed_; + +struct ntp_ts_short { + be16_t sec; + be16_t frac; +} _packed_; + +struct ntp_msg { + uint8_t field; + uint8_t stratum; + int8_t poll; + int8_t precision; + struct ntp_ts_short root_delay; + struct ntp_ts_short root_dispersion; + char refid[4]; + struct ntp_ts reference_time; + struct ntp_ts origin_time; + struct ntp_ts recv_time; + struct ntp_ts trans_time; +} _packed_; + +static int manager_arm_timer(Manager *m, usec_t next); +static int manager_clock_watch_setup(Manager *m); +static int manager_listen_setup(Manager *m); +static void manager_listen_stop(Manager *m); + +static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) { + return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0); +} + +static double ntp_ts_to_d(const struct ntp_ts *ts) { + return be32toh(ts->sec) + ((double)be32toh(ts->frac) / UINT_MAX); +} + +static double ts_to_d(const struct timespec *ts) { + return ts->tv_sec + (1.0e-9 * ts->tv_nsec); +} + +static int manager_timeout(sd_event_source *source, usec_t usec, void *userdata) { + _cleanup_free_ char *pretty = NULL; + Manager *m = userdata; + + assert(m); + assert(m->current_server_name); + assert(m->current_server_address); + + server_address_pretty(m->current_server_address, &pretty); + log_info("Timed out waiting for reply from %s (%s).", strna(pretty), m->current_server_name->string); + + return manager_connect(m); +} + +static int manager_send_request(Manager *m) { + _cleanup_free_ char *pretty = NULL; + struct ntp_msg ntpmsg = { + /* + * "The client initializes the NTP message header, sends the request + * to the server, and strips the time of day from the Transmit + * Timestamp field of the reply. For this purpose, all the NTP + * header fields are set to 0, except the Mode, VN, and optional + * Transmit Timestamp fields." + */ + .field = NTP_FIELD(0, 4, NTP_MODE_CLIENT), + }; + ssize_t len; + int r; + + assert(m); + assert(m->current_server_name); + assert(m->current_server_address); + + m->event_timeout = sd_event_source_unref(m->event_timeout); + + r = manager_listen_setup(m); + if (r < 0) + return log_warning_errno(r, "Failed to setup connection socket: %m"); + + /* + * Set transmit timestamp, remember it; the server will send that back + * as the origin timestamp and we have an indication that this is the + * matching answer to our request. + * + * The actual value does not matter, We do not care about the correct + * NTP UINT_MAX fraction; we just pass the plain nanosecond value. + */ + assert_se(clock_gettime(clock_boottime_or_monotonic(), &m->trans_time_mon) >= 0); + assert_se(clock_gettime(CLOCK_REALTIME, &m->trans_time) >= 0); + ntpmsg.trans_time.sec = htobe32(m->trans_time.tv_sec + OFFSET_1900_1970); + ntpmsg.trans_time.frac = htobe32(m->trans_time.tv_nsec); + + server_address_pretty(m->current_server_address, &pretty); + + len = sendto(m->server_socket, &ntpmsg, sizeof(ntpmsg), MSG_DONTWAIT, &m->current_server_address->sockaddr.sa, m->current_server_address->socklen); + if (len == sizeof(ntpmsg)) { + m->pending = true; + log_debug("Sent NTP request to %s (%s).", strna(pretty), m->current_server_name->string); + } else { + log_debug_errno(errno, "Sending NTP request to %s (%s) failed: %m", strna(pretty), m->current_server_name->string); + return manager_connect(m); + } + + /* re-arm timer with increasing timeout, in case the packets never arrive back */ + if (m->retry_interval > 0) { + if (m->retry_interval < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) + m->retry_interval *= 2; + } else + m->retry_interval = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + + r = manager_arm_timer(m, m->retry_interval); + if (r < 0) + return log_error_errno(r, "Failed to rearm timer: %m"); + + m->missed_replies++; + if (m->missed_replies > NTP_MAX_MISSED_REPLIES) { + r = sd_event_add_time( + m->event, + &m->event_timeout, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + TIMEOUT_USEC, 0, + manager_timeout, m); + if (r < 0) + return log_error_errno(r, "Failed to arm timeout timer: %m"); + } + + return 0; +} + +static int manager_timer(sd_event_source *source, usec_t usec, void *userdata) { + Manager *m = userdata; + + assert(m); + + return manager_send_request(m); +} + +static int manager_arm_timer(Manager *m, usec_t next) { + int r; + + assert(m); + + if (next == 0) { + m->event_timer = sd_event_source_unref(m->event_timer); + return 0; + } + + if (m->event_timer) { + r = sd_event_source_set_time(m->event_timer, now(clock_boottime_or_monotonic()) + next); + if (r < 0) + return r; + + return sd_event_source_set_enabled(m->event_timer, SD_EVENT_ONESHOT); + } + + return sd_event_add_time( + m->event, + &m->event_timer, + clock_boottime_or_monotonic(), + now(clock_boottime_or_monotonic()) + next, 0, + manager_timer, m); +} + +static int manager_clock_watch(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(m); + + /* rearm timer */ + manager_clock_watch_setup(m); + + /* skip our own jumps */ + if (m->jumped) { + m->jumped = false; + return 0; + } + + /* resync */ + log_debug("System time changed. Resyncing."); + m->poll_resync = true; + + return manager_send_request(m); +} + +/* wake up when the system time changes underneath us */ +static int manager_clock_watch_setup(Manager *m) { + + struct itimerspec its = { + .it_value.tv_sec = TIME_T_MAX + }; + + int r; + + assert(m); + + m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); + safe_close(m->clock_watch_fd); + + m->clock_watch_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->clock_watch_fd < 0) + return log_error_errno(errno, "Failed to create timerfd: %m"); + + if (timerfd_settime(m->clock_watch_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) + return log_error_errno(errno, "Failed to set up timerfd: %m"); + + r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m); + if (r < 0) + return log_error_errno(r, "Failed to create clock watch event source: %m"); + + return 0; +} + +static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { + struct timex tmx = {}; + int r; + + assert(m); + + /* + * For small deltas, tell the kernel to gradually adjust the system + * clock to the NTP time, larger deltas are just directly set. + */ + if (fabs(offset) < NTP_MAX_ADJUST) { + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; + tmx.status = STA_PLL; + tmx.offset = offset * NSEC_PER_SEC; + tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4; + tmx.maxerror = 0; + tmx.esterror = 0; + log_debug(" adjust (slew): %+.3f sec", offset); + } else { + tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; + + /* ADJ_NANO uses nanoseconds in the microseconds field */ + tmx.time.tv_sec = (long)offset; + tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; + + /* the kernel expects -0.3s as {-1, 7000.000.000} */ + if (tmx.time.tv_usec < 0) { + tmx.time.tv_sec -= 1; + tmx.time.tv_usec += NSEC_PER_SEC; + } + + m->jumped = true; + log_debug(" adjust (jump): %+.3f sec", offset); + } + + /* + * An unset STA_UNSYNC will enable the kernel's 11-minute mode, + * which syncs the system time periodically to the RTC. + * + * In case the RTC runs in local time, never touch the RTC, + * we have no way to properly handle daylight saving changes and + * mobile devices moving between time zones. + */ + if (m->rtc_local_time) + tmx.status |= STA_UNSYNC; + + switch (leap_sec) { + case 1: + tmx.status |= STA_INS; + break; + case -1: + tmx.status |= STA_DEL; + break; + } + + r = clock_adjtime(CLOCK_REALTIME, &tmx); + if (r < 0) + return -errno; + + /* 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; + + log_debug(" status : %04i %s\n" + " time now : %li.%03llu\n" + " constant : %li\n" + " offset : %+.3f sec\n" + " freq offset : %+li (%i ppm)\n", + tmx.status, tmx.status & STA_UNSYNC ? "unsync" : "sync", + tmx.time.tv_sec, (unsigned long long) (tmx.time.tv_usec / NSEC_PER_MSEC), + tmx.constant, + (double)tmx.offset / NSEC_PER_SEC, + tmx.freq, m->drift_ppm); + + return 0; +} + +static bool manager_sample_spike_detection(Manager *m, double offset, double delay) { + unsigned int i, idx_cur, idx_new, idx_min; + double jitter; + double j; + + assert(m); + + m->packet_count++; + + /* ignore initial sample */ + if (m->packet_count == 1) + return false; + + /* store the current data in our samples array */ + idx_cur = m->samples_idx; + idx_new = (idx_cur + 1) % ELEMENTSOF(m->samples); + m->samples_idx = idx_new; + m->samples[idx_new].offset = offset; + m->samples[idx_new].delay = delay; + + /* calculate new jitter value from the RMS differences relative to the lowest delay sample */ + jitter = m->samples_jitter; + for (idx_min = idx_cur, i = 0; i < ELEMENTSOF(m->samples); i++) + if (m->samples[i].delay > 0 && m->samples[i].delay < m->samples[idx_min].delay) + idx_min = i; + + j = 0; + for (i = 0; i < ELEMENTSOF(m->samples); i++) + j += pow(m->samples[i].offset - m->samples[idx_min].offset, 2); + m->samples_jitter = sqrt(j / (ELEMENTSOF(m->samples) - 1)); + + /* ignore samples when resyncing */ + if (m->poll_resync) + return false; + + /* always accept offset if we are farther off than the round-trip delay */ + if (fabs(offset) > delay) + return false; + + /* we need a few samples before looking at them */ + if (m->packet_count < 4) + return false; + + /* do not accept anything worse than the maximum possible error of the best sample */ + if (fabs(offset) > m->samples[idx_min].delay) + return true; + + /* compare the difference between the current offset to the previous offset and jitter */ + return fabs(offset - m->samples[idx_cur].offset) > 3 * jitter; +} + +static void manager_adjust_poll(Manager *m, double offset, bool spike) { + assert(m); + + if (m->poll_resync) { + m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + m->poll_resync = false; + return; + } + + /* set to minimal poll interval */ + if (!spike && fabs(offset) > NTP_ACCURACY_SEC) { + m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + return; + } + + /* increase polling interval */ + if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) { + if (m->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) + m->poll_interval_usec *= 2; + return; + } + + /* decrease polling interval */ + if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) { + if (m->poll_interval_usec > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC) + m->poll_interval_usec /= 2; + return; + } +} + +static int manager_receive_response(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + struct ntp_msg ntpmsg; + + struct iovec iov = { + .iov_base = &ntpmsg, + .iov_len = sizeof(ntpmsg), + }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct timeval))]; + } control; + union sockaddr_union server_addr; + struct msghdr msghdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + .msg_name = &server_addr, + .msg_namelen = sizeof(server_addr), + }; + struct cmsghdr *cmsg; + struct timespec *recv_time; + ssize_t len; + double origin, receive, trans, dest; + double delay, offset; + double root_distance; + bool spike; + int leap_sec; + int r; + + assert(source); + assert(m); + + if (revents & (EPOLLHUP|EPOLLERR)) { + log_warning("Server connection returned error."); + return manager_connect(m); + } + + len = recvmsg(fd, &msghdr, MSG_DONTWAIT); + if (len < 0) { + if (errno == EAGAIN) + return 0; + + log_warning("Error receiving message. Disconnecting."); + return manager_connect(m); + } + + /* Too short or too long packet? */ + if (iov.iov_len < sizeof(struct ntp_msg) || (msghdr.msg_flags & MSG_TRUNC)) { + log_warning("Invalid response from server. Disconnecting."); + return manager_connect(m); + } + + if (!m->current_server_name || + !m->current_server_address || + !sockaddr_equal(&server_addr, &m->current_server_address->sockaddr)) { + log_debug("Response from unknown server."); + return 0; + } + + recv_time = NULL; + CMSG_FOREACH(cmsg, &msghdr) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + switch (cmsg->cmsg_type) { + case SCM_TIMESTAMPNS: + recv_time = (struct timespec *) CMSG_DATA(cmsg); + break; + } + } + if (!recv_time) { + log_error("Invalid packet timestamp."); + return -EINVAL; + } + + if (!m->pending) { + log_debug("Unexpected reply. Ignoring."); + return 0; + } + + m->missed_replies = 0; + + /* check our "time cookie" (we just stored nanoseconds in the fraction field) */ + if (be32toh(ntpmsg.origin_time.sec) != m->trans_time.tv_sec + OFFSET_1900_1970 || + be32toh(ntpmsg.origin_time.frac) != m->trans_time.tv_nsec) { + log_debug("Invalid reply; not our transmit time. Ignoring."); + return 0; + } + + m->event_timeout = sd_event_source_unref(m->event_timeout); + + if (be32toh(ntpmsg.recv_time.sec) < TIME_EPOCH + OFFSET_1900_1970 || + be32toh(ntpmsg.trans_time.sec) < TIME_EPOCH + OFFSET_1900_1970) { + log_debug("Invalid reply, returned times before epoch. Ignoring."); + return manager_connect(m); + } + + if (NTP_FIELD_LEAP(ntpmsg.field) == NTP_LEAP_NOTINSYNC || + ntpmsg.stratum == 0 || ntpmsg.stratum >= 16) { + log_debug("Server is not synchronized. Disconnecting."); + return manager_connect(m); + } + + if (!IN_SET(NTP_FIELD_VERSION(ntpmsg.field), 3, 4)) { + log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg.field)); + return manager_connect(m); + } + + if (NTP_FIELD_MODE(ntpmsg.field) != NTP_MODE_SERVER) { + log_debug("Unsupported mode %d. Disconnecting.", NTP_FIELD_MODE(ntpmsg.field)); + return manager_connect(m); + } + + root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 + ntp_ts_short_to_d(&ntpmsg.root_dispersion); + if (root_distance > NTP_MAX_ROOT_DISTANCE) { + log_debug("Server has too large root distance. Disconnecting."); + return manager_connect(m); + } + + /* valid packet */ + m->pending = false; + m->retry_interval = 0; + + /* Stop listening */ + manager_listen_stop(m); + + /* announce leap seconds */ + if (NTP_FIELD_LEAP(ntpmsg.field) & NTP_LEAP_PLUSSEC) + leap_sec = 1; + else if (NTP_FIELD_LEAP(ntpmsg.field) & NTP_LEAP_MINUSSEC) + leap_sec = -1; + else + leap_sec = 0; + + /* + * "Timestamp Name ID When Generated + * ------------------------------------------------------------ + * Originate Timestamp T1 time request sent by client + * Receive Timestamp T2 time request received by server + * Transmit Timestamp T3 time reply sent by server + * Destination Timestamp T4 time reply received by client + * + * The round-trip delay, d, and system clock offset, t, are defined as: + * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2" + */ + origin = ts_to_d(&m->trans_time) + OFFSET_1900_1970; + receive = ntp_ts_to_d(&ntpmsg.recv_time); + trans = ntp_ts_to_d(&ntpmsg.trans_time); + dest = ts_to_d(recv_time) + OFFSET_1900_1970; + + offset = ((receive - origin) + (trans - dest)) / 2; + delay = (dest - origin) - (trans - receive); + + spike = manager_sample_spike_detection(m, offset, delay); + + manager_adjust_poll(m, offset, spike); + + log_debug("NTP response:\n" + " leap : %u\n" + " version : %u\n" + " mode : %u\n" + " stratum : %u\n" + " precision : %.6f sec (%d)\n" + " root distance: %.6f sec\n" + " reference : %.4s\n" + " origin : %.3f\n" + " receive : %.3f\n" + " transmit : %.3f\n" + " dest : %.3f\n" + " offset : %+.3f sec\n" + " delay : %+.3f sec\n" + " packet count : %"PRIu64"\n" + " jitter : %.3f%s\n" + " poll interval: " USEC_FMT "\n", + NTP_FIELD_LEAP(ntpmsg.field), + NTP_FIELD_VERSION(ntpmsg.field), + NTP_FIELD_MODE(ntpmsg.field), + ntpmsg.stratum, + exp2(ntpmsg.precision), ntpmsg.precision, + root_distance, + ntpmsg.stratum == 1 ? ntpmsg.refid : "n/a", + origin - OFFSET_1900_1970, + receive - OFFSET_1900_1970, + trans - OFFSET_1900_1970, + dest - OFFSET_1900_1970, + offset, delay, + m->packet_count, + m->samples_jitter, spike ? " spike" : "", + m->poll_interval_usec / USEC_PER_SEC); + + if (!spike) { + m->sync = true; + r = manager_adjust_clock(m, offset, leap_sec); + if (r < 0) + 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", + m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_ppm, + spike ? " (ignored)" : ""); + + if (!m->good) { + _cleanup_free_ char *pretty = NULL; + + m->good = true; + + server_address_pretty(m->current_server_address, &pretty); + log_info("Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string); + sd_notifyf(false, "STATUS=Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string); + } + + r = manager_arm_timer(m, m->poll_interval_usec); + if (r < 0) + return log_error_errno(r, "Failed to rearm timer: %m"); + + return 0; +} + +static int manager_listen_setup(Manager *m) { + union sockaddr_union addr = {}; + static const int tos = IPTOS_LOWDELAY; + static const int on = 1; + int r; + + assert(m); + + if (m->server_socket >= 0) + return 0; + + assert(!m->event_receive); + assert(m->current_server_address); + + addr.sa.sa_family = m->current_server_address->sockaddr.sa.sa_family; + + m->server_socket = socket(addr.sa.sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (m->server_socket < 0) + return -errno; + + r = bind(m->server_socket, &addr.sa, m->current_server_address->socklen); + if (r < 0) + return -errno; + + r = setsockopt(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on)); + if (r < 0) + return -errno; + + (void) setsockopt(m->server_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + + return sd_event_add_io(m->event, &m->event_receive, m->server_socket, EPOLLIN, manager_receive_response, m); +} + +static void manager_listen_stop(Manager *m) { + assert(m); + + m->event_receive = sd_event_source_unref(m->event_receive); + m->server_socket = safe_close(m->server_socket); +} + +static int manager_begin(Manager *m) { + _cleanup_free_ char *pretty = NULL; + int r; + + assert(m); + assert_return(m->current_server_name, -EHOSTUNREACH); + assert_return(m->current_server_address, -EHOSTUNREACH); + + m->good = false; + m->missed_replies = NTP_MAX_MISSED_REPLIES; + if (m->poll_interval_usec == 0) + m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + + server_address_pretty(m->current_server_address, &pretty); + log_debug("Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); + sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); + + r = manager_clock_watch_setup(m); + if (r < 0) + return r; + + return manager_send_request(m); +} + +void manager_set_server_name(Manager *m, ServerName *n) { + assert(m); + + if (m->current_server_name == n) + return; + + m->current_server_name = n; + m->current_server_address = NULL; + + manager_disconnect(m); + + if (n) + log_debug("Selected server %s.", n->string); +} + +void manager_set_server_address(Manager *m, ServerAddress *a) { + assert(m); + + if (m->current_server_address == a) + return; + + m->current_server_address = a; + /* If a is NULL, we are just clearing the address, without + * changing the name. Keep the existing name in that case. */ + if (a) + m->current_server_name = a->name; + + manager_disconnect(m); + + if (a) { + _cleanup_free_ char *pretty = NULL; + server_address_pretty(a, &pretty); + log_debug("Selected address %s of server %s.", strna(pretty), a->name->string); + } +} + +static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { + Manager *m = userdata; + int r; + + assert(q); + assert(m); + assert(m->current_server_name); + + m->resolve_query = sd_resolve_query_unref(m->resolve_query); + + if (ret != 0) { + log_debug("Failed to resolve %s: %s", m->current_server_name->string, gai_strerror(ret)); + + /* Try next host */ + return manager_connect(m); + } + + for (; ai; ai = ai->ai_next) { + _cleanup_free_ char *pretty = NULL; + ServerAddress *a; + + assert(ai->ai_addr); + assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data)); + + if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) { + log_warning("Unsuitable address protocol for %s", m->current_server_name->string); + continue; + } + + r = server_address_new(m->current_server_name, &a, (const union sockaddr_union*) ai->ai_addr, ai->ai_addrlen); + if (r < 0) + return log_error_errno(r, "Failed to add server address: %m"); + + server_address_pretty(a, &pretty); + log_debug("Resolved address %s for %s.", pretty, m->current_server_name->string); + } + + if (!m->current_server_name->addresses) { + log_error("Failed to find suitable address for host %s.", m->current_server_name->string); + + /* Try next host */ + return manager_connect(m); + } + + manager_set_server_address(m, m->current_server_name->addresses); + + return manager_begin(m); +} + +static int manager_retry_connect(sd_event_source *source, usec_t usec, void *userdata) { + Manager *m = userdata; + + assert(m); + + return manager_connect(m); +} + +int manager_connect(Manager *m) { + int r; + + assert(m); + + manager_disconnect(m); + + m->event_retry = sd_event_source_unref(m->event_retry); + if (!ratelimit_test(&m->ratelimit)) { + log_debug("Slowing down attempts to contact servers."); + + r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + RETRY_USEC, 0, manager_retry_connect, m); + if (r < 0) + return log_error_errno(r, "Failed to create retry timer: %m"); + + return 0; + } + + /* If we already are operating on some address, switch to the + * next one. */ + if (m->current_server_address && m->current_server_address->addresses_next) + manager_set_server_address(m, m->current_server_address->addresses_next); + else { + struct addrinfo hints = { + .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, + .ai_socktype = SOCK_DGRAM, + }; + + /* Hmm, we are through all addresses, let's look for the next host instead */ + if (m->current_server_name && m->current_server_name->names_next) + manager_set_server_name(m, m->current_server_name->names_next); + else { + ServerName *f; + bool restart = true; + + /* Our current server name list is exhausted, + * let's find the next one to iterate. First + * we try the system list, then the link list. + * After having processed the link list we + * jump back to the system list. However, if + * both lists are empty, we change to the + * fallback list. */ + if (!m->current_server_name || m->current_server_name->type == SERVER_LINK) { + f = m->system_servers; + if (!f) + f = m->link_servers; + } else { + f = m->link_servers; + if (!f) + f = m->system_servers; + else + restart = false; + } + + if (!f) + f = m->fallback_servers; + + if (!f) { + manager_set_server_name(m, NULL); + log_debug("No server found."); + return 0; + } + + if (restart && !m->exhausted_servers && m->poll_interval_usec) { + log_debug("Waiting after exhausting servers."); + r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + m->poll_interval_usec, 0, manager_retry_connect, m); + if (r < 0) + return log_error_errno(r, "Failed to create retry timer: %m"); + + m->exhausted_servers = true; + + /* Increase the polling interval */ + if (m->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) + m->poll_interval_usec *= 2; + + return 0; + } + + m->exhausted_servers = false; + + manager_set_server_name(m, f); + } + + /* Tell the resolver to reread /etc/resolv.conf, in + * case it changed. */ + res_init(); + + /* Flush out any previously resolved addresses */ + server_name_flush_addresses(m->current_server_name); + + log_debug("Resolving %s...", m->current_server_name->string); + + r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, m); + if (r < 0) + return log_error_errno(r, "Failed to create resolver: %m"); + + return 1; + } + + r = manager_begin(m); + if (r < 0) + return r; + + return 1; +} + +void manager_disconnect(Manager *m) { + assert(m); + + m->resolve_query = sd_resolve_query_unref(m->resolve_query); + + m->event_timer = sd_event_source_unref(m->event_timer); + + manager_listen_stop(m); + + m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); + m->clock_watch_fd = safe_close(m->clock_watch_fd); + + m->event_timeout = sd_event_source_unref(m->event_timeout); + + sd_notifyf(false, "STATUS=Idle."); +} + +void manager_flush_server_names(Manager *m, ServerType t) { + assert(m); + + if (t == SERVER_SYSTEM) + while (m->system_servers) + server_name_free(m->system_servers); + + if (t == SERVER_LINK) + while (m->link_servers) + server_name_free(m->link_servers); + + if (t == SERVER_FALLBACK) + while (m->fallback_servers) + server_name_free(m->fallback_servers); +} + +void manager_free(Manager *m) { + if (!m) + return; + + manager_disconnect(m); + manager_flush_server_names(m, SERVER_SYSTEM); + manager_flush_server_names(m, SERVER_LINK); + manager_flush_server_names(m, SERVER_FALLBACK); + + sd_event_source_unref(m->event_retry); + + sd_event_source_unref(m->network_event_source); + sd_network_monitor_unref(m->network_monitor); + + sd_resolve_unref(m->resolve); + sd_event_unref(m->event); + + free(m); +} + +static int manager_network_read_link_servers(Manager *m) { + _cleanup_strv_free_ char **ntp = NULL; + ServerName *n, *nx; + char **i; + int r; + + assert(m); + + r = sd_network_get_ntp(&ntp); + if (r < 0) + goto clear; + + LIST_FOREACH(names, n, m->link_servers) + n->marked = true; + + STRV_FOREACH(i, ntp) { + bool found = false; + + LIST_FOREACH(names, n, m->link_servers) + if (streq(n->string, *i)) { + n->marked = false; + found = true; + break; + } + + if (!found) { + r = server_name_new(m, NULL, SERVER_LINK, *i); + if (r < 0) + goto clear; + } + } + + LIST_FOREACH_SAFE(names, n, nx, m->link_servers) + if (n->marked) + server_name_free(n); + + return 0; + +clear: + manager_flush_server_names(m, SERVER_LINK); + return r; +} + +static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + bool connected, online; + int r; + + assert(m); + + sd_network_monitor_flush(m->network_monitor); + + manager_network_read_link_servers(m); + + /* check if the machine is online */ + online = network_is_online(); + + /* check if the client is currently connected */ + connected = m->server_socket >= 0 || m->resolve_query || m->exhausted_servers; + + if (connected && !online) { + log_info("No network connectivity, watching for changes."); + manager_disconnect(m); + + } else if (!connected && online) { + log_info("Network configuration changed, trying to establish connection."); + + if (m->current_server_address) + r = manager_begin(m); + else + r = manager_connect(m); + if (r < 0) + return r; + } + + return 0; +} + +static int manager_network_monitor_listen(Manager *m) { + int r, fd, events; + + assert(m); + + r = sd_network_monitor_new(&m->network_monitor, NULL); + if (r < 0) + return r; + + fd = sd_network_monitor_get_fd(m->network_monitor); + if (fd < 0) + return fd; + + events = sd_network_monitor_get_events(m->network_monitor); + if (events < 0) + return events; + + r = sd_event_add_io(m->event, &m->network_event_source, fd, events, manager_network_event_handler, m); + if (r < 0) + return r; + + return 0; +} + +int manager_new(Manager **ret) { + _cleanup_(manager_freep) Manager *m = NULL; + int r; + + assert(ret); + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + m->server_socket = m->clock_watch_fd = -1; + + RATELIMIT_INIT(m->ratelimit, RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST); + + r = manager_parse_server_string(m, SERVER_FALLBACK, NTP_SERVERS); + if (r < 0) + return r; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); + + sd_event_set_watchdog(m->event, true); + + r = sd_resolve_default(&m->resolve); + if (r < 0) + return r; + + r = sd_resolve_attach_event(m->resolve, m->event, 0); + if (r < 0) + return r; + + r = manager_network_monitor_listen(m); + if (r < 0) + return r; + + manager_network_read_link_servers(m); + + *ret = m; + m = NULL; + + return 0; +} diff --git a/src/systemd-timesyncd/timesyncd-manager.h b/src/systemd-timesyncd/timesyncd-manager.h new file mode 100644 index 0000000000..a70b187089 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-manager.h @@ -0,0 +1,104 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "basic/list.h" +#include "basic/ratelimit.h" +#include "sd-network/sd-network.h" +#include "sd-resolve/sd-resolve.h" + +typedef struct Manager Manager; + +#include "timesyncd-server.h" + +struct Manager { + sd_event *event; + sd_resolve *resolve; + + LIST_HEAD(ServerName, system_servers); + LIST_HEAD(ServerName, link_servers); + LIST_HEAD(ServerName, fallback_servers); + + RateLimit ratelimit; + bool exhausted_servers; + + /* network */ + sd_event_source *network_event_source; + sd_network_monitor *network_monitor; + + /* peer */ + sd_resolve_query *resolve_query; + sd_event_source *event_receive; + ServerName *current_server_name; + ServerAddress *current_server_address; + int server_socket; + int missed_replies; + uint64_t packet_count; + sd_event_source *event_timeout; + bool good; + + /* last sent packet */ + struct timespec trans_time_mon; + struct timespec trans_time; + usec_t retry_interval; + bool pending; + + /* poll timer */ + sd_event_source *event_timer; + usec_t poll_interval_usec; + bool poll_resync; + + /* history data */ + struct { + double offset; + double delay; + } samples[8]; + unsigned int samples_idx; + double samples_jitter; + + /* last change */ + bool jumped; + bool sync; + int drift_ppm; + + /* watch for time changes */ + sd_event_source *event_clock_watch; + int clock_watch_fd; + + /* Retry connections */ + sd_event_source *event_retry; + + /* RTC runs in local time, leave it alone */ + bool rtc_local_time; +}; + +int manager_new(Manager **ret); +void manager_free(Manager *m); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); + +void manager_set_server_name(Manager *m, ServerName *n); +void manager_set_server_address(Manager *m, ServerAddress *a); +void manager_flush_server_names(Manager *m, ServerType t); + +int manager_connect(Manager *m); +void manager_disconnect(Manager *m); diff --git a/src/systemd-timesyncd/timesyncd-server.c b/src/systemd-timesyncd/timesyncd-server.c new file mode 100644 index 0000000000..42e5c8e429 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-server.c @@ -0,0 +1,151 @@ +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/alloc-util.h" + +#include "timesyncd-server.h" + +int server_address_new( + ServerName *n, + ServerAddress **ret, + const union sockaddr_union *sockaddr, + socklen_t socklen) { + + ServerAddress *a, *tail; + + assert(n); + assert(sockaddr); + assert(socklen >= offsetof(struct sockaddr, sa_data)); + assert(socklen <= sizeof(union sockaddr_union)); + + a = new0(ServerAddress, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->sockaddr, sockaddr, socklen); + a->socklen = socklen; + + LIST_FIND_TAIL(addresses, n->addresses, tail); + LIST_INSERT_AFTER(addresses, n->addresses, tail, a); + a->name = n; + + if (ret) + *ret = a; + + return 0; +} + +ServerAddress* server_address_free(ServerAddress *a) { + if (!a) + return NULL; + + if (a->name) { + LIST_REMOVE(addresses, a->name->addresses, a); + + if (a->name->manager && a->name->manager->current_server_address == a) + manager_set_server_address(a->name->manager, NULL); + } + + free(a); + return NULL; +} + +int server_name_new( + Manager *m, + ServerName **ret, + ServerType type, + const char *string) { + + ServerName *n, *tail; + + assert(m); + assert(string); + + n = new0(ServerName, 1); + if (!n) + return -ENOMEM; + + n->type = type; + n->string = strdup(string); + if (!n->string) { + free(n); + return -ENOMEM; + } + + if (type == SERVER_SYSTEM) { + LIST_FIND_TAIL(names, m->system_servers, tail); + LIST_INSERT_AFTER(names, m->system_servers, tail, n); + } else if (type == SERVER_LINK) { + LIST_FIND_TAIL(names, m->link_servers, tail); + LIST_INSERT_AFTER(names, m->link_servers, tail, n); + } else if (type == SERVER_FALLBACK) { + LIST_FIND_TAIL(names, m->fallback_servers, tail); + LIST_INSERT_AFTER(names, m->fallback_servers, tail, n); + } else + assert_not_reached("Unknown server type"); + + n->manager = m; + + if (type != SERVER_FALLBACK && + m->current_server_name && + m->current_server_name->type == SERVER_FALLBACK) + manager_set_server_name(m, NULL); + + log_debug("Added new server %s.", string); + + if (ret) + *ret = n; + + return 0; +} + +ServerName *server_name_free(ServerName *n) { + if (!n) + return NULL; + + server_name_flush_addresses(n); + + if (n->manager) { + if (n->type == SERVER_SYSTEM) + LIST_REMOVE(names, n->manager->system_servers, n); + else if (n->type == SERVER_LINK) + LIST_REMOVE(names, n->manager->link_servers, n); + else if (n->type == SERVER_FALLBACK) + LIST_REMOVE(names, n->manager->fallback_servers, n); + else + assert_not_reached("Unknown server type"); + + if (n->manager->current_server_name == n) + manager_set_server_name(n->manager, NULL); + } + + log_debug("Removed server %s.", n->string); + + free(n->string); + free(n); + + return NULL; +} + +void server_name_flush_addresses(ServerName *n) { + assert(n); + + while (n->addresses) + server_address_free(n->addresses); +} diff --git a/src/systemd-timesyncd/timesyncd-server.h b/src/systemd-timesyncd/timesyncd-server.h new file mode 100644 index 0000000000..438146c8f7 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd-server.h @@ -0,0 +1,65 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "basic/list.h" +#include "basic/socket-util.h" + +typedef struct ServerAddress ServerAddress; +typedef struct ServerName ServerName; + +typedef enum ServerType { + SERVER_SYSTEM, + SERVER_FALLBACK, + SERVER_LINK, +} ServerType; + +#include "timesyncd-manager.h" + +struct ServerAddress { + ServerName *name; + + union sockaddr_union sockaddr; + socklen_t socklen; + + LIST_FIELDS(ServerAddress, addresses); +}; + +struct ServerName { + Manager *manager; + + ServerType type; + char *string; + + bool marked:1; + + LIST_HEAD(ServerAddress, addresses); + LIST_FIELDS(ServerName, names); +}; + +int server_address_new(ServerName *n, ServerAddress **ret, const union sockaddr_union *sockaddr, socklen_t socklen); +ServerAddress* server_address_free(ServerAddress *a); +static inline int server_address_pretty(ServerAddress *a, char **pretty) { + return sockaddr_pretty(&a->sockaddr.sa, a->socklen, true, true, pretty); +} + +int server_name_new(Manager *m, ServerName **ret, ServerType type,const char *string); +ServerName *server_name_free(ServerName *n); +void server_name_flush_addresses(ServerName *n); diff --git a/src/systemd-timesyncd/timesyncd.c b/src/systemd-timesyncd/timesyncd.c new file mode 100644 index 0000000000..7a357c2296 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd.c @@ -0,0 +1,165 @@ +/*** + This file is part of systemd. + + Copyright 2014 Kay Sievers, Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "basic/capability-util.h" +#include "basic/clock-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/signal-util.h" +#include "basic/user-util.h" +#include "sd-network/network-util.h" + +#include "timesyncd-conf.h" +#include "timesyncd-manager.h" + +static int load_clock_timestamp(uid_t uid, gid_t gid) { + _cleanup_close_ int fd = -1; + usec_t min = TIME_EPOCH * USEC_PER_SEC; + usec_t ct; + int r; + + /* Let's try to make sure that the clock is always + * monotonically increasing, by saving the clock whenever we + * have a new NTP time, or when we shut down, and restoring it + * when we start again. This is particularly helpful on + * systems lacking a battery backed RTC. We also will adjust + * the time to at least the build time of systemd. */ + + fd = open("/var/lib/systemd/clock", O_RDWR|O_CLOEXEC, 0644); + if (fd >= 0) { + struct stat st; + usec_t stamp; + + /* check if the recorded time is later than the compiled-in one */ + r = fstat(fd, &st); + if (r >= 0) { + stamp = timespec_load(&st.st_mtim); + if (stamp > min) + min = stamp; + } + + /* Try to fix the access mode, so that we can still + touch the file after dropping priviliges */ + (void) fchmod(fd, 0644); + (void) fchown(fd, uid, gid); + + } else + /* create stamp file with the compiled-in date */ + (void) touch_file("/var/lib/systemd/clock", true, min, uid, gid, 0644); + + ct = now(CLOCK_REALTIME); + if (ct < min) { + struct timespec ts; + char date[FORMAT_TIMESTAMP_MAX]; + + log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s", + format_timestamp(date, sizeof(date), min)); + + if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0) + log_error_errno(errno, "Failed to restore system clock: %m"); + } + + return 0; +} + +int main(int argc, char *argv[]) { + _cleanup_(manager_freep) Manager *m = NULL; + const char *user = "systemd-timesync"; + uid_t uid; + gid_t gid; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_set_facility(LOG_CRON); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program does not take arguments."); + r = -EINVAL; + goto finish; + } + + r = get_user_creds(&user, &uid, &gid, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Cannot resolve user name %s: %m", user); + goto finish; + } + + r = load_clock_timestamp(uid, gid); + if (r < 0) + goto finish; + + r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME)); + if (r < 0) + goto finish; + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + + r = manager_new(&m); + if (r < 0) { + log_error_errno(r, "Failed to allocate manager: %m"); + goto finish; + } + + if (clock_is_localtime(NULL) > 0) { + log_info("The system is configured to read the RTC time in the local time zone. " + "This mode can not be fully supported. All system time to RTC updates are disabled."); + m->rtc_local_time = true; + } + + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse configuration file: %m"); + + log_debug("systemd-timesyncd running as pid " PID_FMT, getpid()); + sd_notify(false, + "READY=1\n" + "STATUS=Daemon is running"); + + if (network_is_online()) { + r = manager_connect(m); + if (r < 0) + goto finish; + } + + r = sd_event_loop(m->event); + if (r < 0) { + log_error_errno(r, "Failed to run event loop: %m"); + goto finish; + } + + /* if we got an authoritative time, store it in the file system */ + if (m->sync) + (void) touch("/var/lib/systemd/clock"); + + sd_event_get_exit_code(m->event, &r); + +finish: + sd_notify(false, + "STOPPING=1\n" + "STATUS=Shutting down..."); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-timesyncd/timesyncd.conf.in b/src/systemd-timesyncd/timesyncd.conf.in new file mode 100644 index 0000000000..b6a2ada273 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd.conf.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# Entries in this file show the compile time defaults. +# You can change settings by editing this file. +# Defaults can be restored by simply deleting this file. +# +# See timesyncd.conf(5) for details. + +[Time] +#NTP= +#FallbackNTP=@NTP_SERVERS@ diff --git a/src/systemd-timesyncd/timesyncd.conf.xml b/src/systemd-timesyncd/timesyncd.conf.xml new file mode 100644 index 0000000000..8c86fd0074 --- /dev/null +++ b/src/systemd-timesyncd/timesyncd.conf.xml @@ -0,0 +1,112 @@ + + + + + + + + timesyncd.conf + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + timesyncd.conf + 5 + + + + timesyncd.conf + timesyncd.conf.d + Network Time Synchronization configuration files + + + + /etc/systemd/timesyncd.conf + /etc/systemd/timesyncd.conf.d/*.conf + /run/systemd/timesyncd.conf.d/*.conf + /usr/lib/systemd/timesyncd.conf.d/*.conf + + + + Description + + These configuration files control NTP network time + synchronization. + + + + + + + Options + + The following settings are configured in the [Time] section: + + + + + NTP= + A space-separated list of NTP server host + names or IP addresses. During runtime this list is combined + with any per-interface NTP servers acquired from + systemd-networkd.service8. + systemd-timesyncd will contact all configured system or + per-interface servers in turn until one is found that + responds. This setting defaults to an empty + list. + + + + FallbackNTP= + A space-separated list of NTP server host + names or IP addresses to be used as the fallback NTP servers. + Any per-interface NTP servers obtained from + systemd-networkd.service8 + take precedence over this setting, as do any servers set via + NTP= above. This setting is hence only used + if no other NTP server information is known. If this option is + not given, a compiled-in list of NTP servers is used + instead. + + + + + + + See Also + + systemd1, + systemd-timesyncd.service8, + systemd-networkd.service8 + + + + diff --git a/src/systemd-tty-ask-password-agent/Makefile b/src/systemd-tty-ask-password-agent/Makefile new file mode 100644 index 0000000000..3091527a7f --- /dev/null +++ b/src/systemd-tty-ask-password-agent/Makefile @@ -0,0 +1,33 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +rootbin_PROGRAMS += systemd-tty-ask-password-agent +systemd_tty_ask_password_agent_SOURCES = \ + src/tty-ask-password-agent/tty-ask-password-agent.c + +systemd_tty_ask_password_agent_LDADD = \ + libsystemd-shared.la + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-tty-ask-password-agent/systemd-ask-password-console.service.xml b/src/systemd-tty-ask-password-agent/systemd-ask-password-console.service.xml new file mode 100644 index 0000000000..479e5f2e5b --- /dev/null +++ b/src/systemd-tty-ask-password-agent/systemd-ask-password-console.service.xml @@ -0,0 +1,93 @@ + + + + + + + + systemd-ask-password-console.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-ask-password-console.service + 8 + + + + systemd-ask-password-console.service + systemd-ask-password-console.path + systemd-ask-password-wall.service + systemd-ask-password-wall.path + Query the user for system passwords on the + console and via wall + + + + systemd-ask-password-console.service + systemd-ask-password-console.path + systemd-ask-password-wall.service + systemd-ask-password-wall.path + + + + Description + + systemd-ask-password-console.service is + a system service that queries the user for system passwords (such + as hard disk encryption keys and SSL certificate passphrases) on + the console. It is intended to be used during boot to ensure + proper handling of passwords necessary for boot. + systemd-ask-password-wall.service is a system + service that informs all logged in users for system passwords via + wall1. + It is intended to be used after boot to ensure that users are + properly notified. + + See the + developer documentation for more information about the + system password logic. + + Note that these services invoke + systemd-tty-ask-password-agent1 + with either the --watch --console or + --watch --wall command line parameters. + + + + See Also + + systemd1, + systemd-tty-ask-password-agent1, + wall1 + + + + diff --git a/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.completion.zsh b/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.completion.zsh new file mode 100644 index 0000000000..e7c0684996 --- /dev/null +++ b/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.completion.zsh @@ -0,0 +1,14 @@ +#compdef systemd-tty-ask-password-agent + +local curcontext="$curcontext" state lstate line +_arguments \ + {-h,--help}'[Prints a short help text and exits.]' \ + '--version[Prints a short version string and exits.]' \ + '--list[Lists all currently pending system password requests.]' \ + '--query[Process all currently pending system password requests by querying the user on the calling TTY.]' \ + '--watch[Continuously process password requests.]' \ + '--wall[Forward password requests to wall(1).]' \ + '--plymouth[Ask question with plymouth(8).]' \ + '--console[Ask question on /dev/console.]' + +#vim: set ft=zsh sw=4 ts=4 et diff --git a/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.xml b/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.xml new file mode 100644 index 0000000000..2876fab644 --- /dev/null +++ b/src/systemd-tty-ask-password-agent/systemd-tty-ask-password-agent.xml @@ -0,0 +1,149 @@ + + + + + + + + + systemd-tty-ask-password-agent + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-tty-ask-password-agent + 1 + + + + systemd-tty-ask-password-agent + List or process pending systemd password requests + + + + + systemd-tty-ask-password-agent OPTIONS VARIABLE=VALUE + + + + + Description + + systemd-tty-ask-password-agent is a + password agent that handles password requests of the system, for + example for hard disk encryption passwords or SSL certificate + passwords that need to be queried at boot-time or during + runtime. + + systemd-tty-ask-password-agent implements + the Password + Agents Specification. + + + + + Options + + The following options are understood: + + + + + + Lists all currently pending system password requests. + + + + + + Process all currently pending system password + requests by querying the user on the calling + TTY. + + + + + + Continuously process password + requests. + + + + + + Forward password requests to + wall1 + instead of querying the user on the calling + TTY. + + + + + + Ask question with + plymouth8 + instead of querying the user on the calling + TTY. + + + + + + Ask question on + /dev/console instead of querying the user + on the calling TTY. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + systemd1, + systemctl1, + systemd-ask-password-console.service8, + wall1, + plymouth8 + + + + diff --git a/src/systemd-tty-ask-password-agent/tty-ask-password-agent.c b/src/systemd-tty-ask-password-agent/tty-ask-password-agent.c new file mode 100644 index 0000000000..713f2025a4 --- /dev/null +++ b/src/systemd-tty-ask-password-agent/tty-ask-password-agent.c @@ -0,0 +1,875 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2015 Werner Fink + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/dirent-util.h" +#include "basic/exit-status.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/io-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/process-util.h" +#include "basic/signal-util.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "shared/ask-password-api.h" +#include "shared/conf-parser.h" +#include "shared/utmp-wtmp.h" + +static enum { + ACTION_LIST, + ACTION_QUERY, + ACTION_WATCH, + ACTION_WALL +} arg_action = ACTION_QUERY; + +static bool arg_plymouth = false; +static bool arg_console = false; +static const char *arg_device = NULL; + +static int ask_password_plymouth( + const char *message, + usec_t until, + AskPasswordFlags flags, + const char *flag_file, + char ***ret) { + + static const union sockaddr_union sa = PLYMOUTH_SOCKET; + _cleanup_close_ int fd = -1, notify = -1; + _cleanup_free_ char *packet = NULL; + ssize_t k; + int r, n; + struct pollfd pollfd[2] = {}; + char buffer[LINE_MAX]; + size_t p = 0; + enum { + POLL_SOCKET, + POLL_INOTIFY + }; + + assert(ret); + + if (flag_file) { + notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); + if (notify < 0) + return -errno; + + r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */ + if (r < 0) + return -errno; + } + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return -errno; + + 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) + packet = NULL; + if (!packet) + return -ENOMEM; + + r = loop_write(fd, packet, n + 1, true); + if (r < 0) + return r; + + pollfd[POLL_SOCKET].fd = fd; + pollfd[POLL_SOCKET].events = POLLIN; + pollfd[POLL_INOTIFY].fd = notify; + pollfd[POLL_INOTIFY].events = POLLIN; + + for (;;) { + int sleep_for = -1, j; + + if (until > 0) { + usec_t y; + + y = now(CLOCK_MONOTONIC); + + if (y > until) { + r = -ETIME; + goto finish; + } + + sleep_for = (int) ((until - y) / USEC_PER_MSEC); + } + + if (flag_file && access(flag_file, F_OK) < 0) { + r = -errno; + goto finish; + } + + j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); + if (j < 0) { + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } else if (j == 0) { + r = -ETIME; + goto finish; + } + + 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) { + if (errno == EINTR || errno == EAGAIN) + continue; + + r = -errno; + goto finish; + } else if (k == 0) { + r = -EIO; + goto finish; + } + + p += k; + + if (p < 1) + continue; + + if (buffer[0] == 5) { + + 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) { + r = -ENOMEM; + goto finish; + } + + r = loop_write(fd, packet, n+1, true); + if (r < 0) + goto finish; + + flags &= ~ASK_PASSWORD_ACCEPT_CACHED; + p = 0; + continue; + } + + /* No password, because UI not shown */ + r = -ENOENT; + goto finish; + + } else if (buffer[0] == 2 || buffer[0] == 9) { + uint32_t size; + char **l; + + /* One or more answers */ + if (p < 5) + continue; + + memcpy(&size, buffer+1, sizeof(size)); + size = le32toh(size); + if (size + 5 > sizeof(buffer)) { + r = -EIO; + goto finish; + } + + if (p-5 < size) + continue; + + l = strv_parse_nulstr(buffer + 5, size); + if (!l) { + r = -ENOMEM; + goto finish; + } + + *ret = l; + break; + + } else { + /* Unknown packet */ + r = -EIO; + goto finish; + } + } + + r = 0; + +finish: + memory_erase(buffer, sizeof(buffer)); + return r; +} + +static int send_passwords(const char *socket_name, char **passwords) { + _cleanup_free_ char *packet = NULL; + _cleanup_close_ int socket_fd = -1; + union sockaddr_union sa = { .un.sun_family = AF_UNIX }; + size_t packet_length = 1; + char **p, *d; + int r; + + assert(socket_name); + + STRV_FOREACH(p, passwords) + packet_length += strlen(*p) + 1; + + packet = new(char, packet_length); + if (!packet) + return -ENOMEM; + + packet[0] = '+'; + + d = packet + 1; + STRV_FOREACH(p, passwords) + d = stpcpy(d, *p) + 1; + + socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (socket_fd < 0) { + r = log_debug_errno(errno, "socket(): %m"); + goto finish; + } + + strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); + + r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + r = log_debug_errno(errno, "sendto(): %m"); + +finish: + memory_erase(packet, packet_length); + return r; +} + +static int parse_password(const char *filename, char **wall) { + _cleanup_free_ char *socket_name = NULL, *message = NULL; + bool accept_cached = false, echo = false; + uint64_t not_after = 0; + unsigned pid = 0; + + const ConfigTableItem items[] = { + { "Ask", "Socket", config_parse_string, 0, &socket_name }, + { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after }, + { "Ask", "Message", config_parse_string, 0, &message }, + { "Ask", "PID", config_parse_unsigned, 0, &pid }, + { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached }, + { "Ask", "Echo", config_parse_bool, 0, &echo }, + {} + }; + + int r; + + assert(filename); + + r = config_parse(NULL, filename, NULL, + NULL, + config_item_table_lookup, items, + true, false, true, NULL); + if (r < 0) + return r; + + if (!socket_name) { + log_error("Invalid password file %s", filename); + return -EBADMSG; + } + + if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after) + return 0; + + if (pid > 0 && !pid_is_alive(pid)) + return 0; + + if (arg_action == ACTION_LIST) + printf("'%s' (PID %u)\n", message, pid); + + else if (arg_action == ACTION_WALL) { + 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!", + strempty(*wall), + *wall ? "\r\n\r\n" : "", + message, + pid) < 0) + return log_oom(); + + free(*wall); + *wall = _wall; + + } else { + _cleanup_strv_free_erase_ char **passwords = NULL; + + assert(arg_action == ACTION_QUERY || + arg_action == ACTION_WATCH); + + if (access(socket_name, W_OK) < 0) { + if (arg_action == ACTION_QUERY) + log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid); + + return 0; + } + + if (arg_plymouth) + r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords); + else { + char *password = NULL; + int tty_fd = -1; + + if (arg_console) { + const char *con = arg_device ? arg_device : "/dev/console"; + + tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY); + if (tty_fd < 0) + 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, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password); + + if (arg_console) { + tty_fd = safe_close(tty_fd); + release_terminal(); + } + + if (r >= 0) + r = strv_push(&passwords, password); + + if (r < 0) + string_free_erase(password); + } + + /* If the query went away, that's OK */ + if (IN_SET(r, -ETIME, -ENOENT)) + return 0; + + if (r < 0) + return log_error_errno(r, "Failed to query password: %m"); + + r = send_passwords(socket_name, passwords); + if (r < 0) + return log_error_errno(r, "Failed to send: %m"); + } + + return 0; +} + +static int wall_tty_block(void) { + _cleanup_free_ char *p = NULL; + 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 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 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 log_debug_errno(errno, "Failed to open %s: %m", p); + + return fd; +} + +static bool wall_tty_match(const char *path, void *userdata) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + struct stat st; + + if (!path_is_absolute(path)) + path = strjoina("/dev/", path); + + if (lstat(path, &st) < 0) { + log_debug_errno(errno, "Failed to stat %s: %m", path); + return true; + } + + 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 + * already shown. We use the fact here that opening a pipe in + * non-blocking mode for write-only will succeed only if + * there's some writer behind it. Using pipes has the + * 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) { + log_oom(); + return true; + } + + fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + 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. */ + return 0; +} + +static int show_passwords(void) { + _cleanup_closedir_ DIR *d; + struct dirent *de; + int r = 0; + + d = opendir("/run/systemd/ask-password"); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m"); + } + + FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) { + _cleanup_free_ char *p = NULL, *wall = NULL; + int q; + + /* We only support /dev on tmpfs, hence we can rely on + * d_type to be reliable */ + + if (de->d_type != DT_REG) + continue; + + if (hidden_or_backup_file(de->d_name)) + continue; + + if (!startswith(de->d_name, "ask.")) + continue; + + p = strappend("/run/systemd/ask-password/", de->d_name); + if (!p) + return log_oom(); + + q = parse_password(p, &wall); + if (q < 0 && r == 0) + r = q; + + if (wall) + (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL); + } + + return r; +} + +static int watch_passwords(void) { + enum { + FD_INOTIFY, + FD_SIGNAL, + _FD_MAX + }; + + _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1; + struct pollfd pollfd[_FD_MAX] = {}; + sigset_t mask; + int r; + + tty_block_fd = wall_tty_block(); + + (void) mkdir_p_label("/run/systemd/ask-password", 0755); + + notify = inotify_init1(IN_CLOEXEC); + if (notify < 0) + 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 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); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0); + + signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (signal_fd < 0) + return log_error_errno(errno, "Failed to allocate signal file descriptor: %m"); + + pollfd[FD_INOTIFY].fd = notify; + pollfd[FD_INOTIFY].events = POLLIN; + pollfd[FD_SIGNAL].fd = signal_fd; + pollfd[FD_SIGNAL].events = POLLIN; + + for (;;) { + r = show_passwords(); + if (r < 0) + log_error_errno(r, "Failed to show password: %m"); + + if (poll(pollfd, _FD_MAX, -1) < 0) { + if (errno == EINTR) + continue; + + return -errno; + } + + if (pollfd[FD_INOTIFY].revents != 0) + (void) flush_fd(notify); + + if (pollfd[FD_SIGNAL].revents != 0) + break; + } + + return 0; +} + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Process system password requests.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --list Show pending password requests\n" + " --query Process pending password requests\n" + " --watch Continuously process password requests\n" + " --wall Continuously forward password requests to wall\n" + " --plymouth Ask question with Plymouth instead of on TTY\n" + " --console Ask question on /dev/console instead of current TTY\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_LIST = 0x100, + ARG_QUERY, + ARG_WATCH, + ARG_WALL, + ARG_PLYMOUTH, + ARG_CONSOLE, + ARG_VERSION + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "list", no_argument, NULL, ARG_LIST }, + { "query", no_argument, NULL, ARG_QUERY }, + { "watch", no_argument, NULL, ARG_WATCH }, + { "wall", no_argument, NULL, ARG_WALL }, + { "plymouth", no_argument, NULL, ARG_PLYMOUTH }, + { "console", optional_argument, NULL, ARG_CONSOLE }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case ARG_LIST: + arg_action = ACTION_LIST; + break; + + case ARG_QUERY: + arg_action = ACTION_QUERY; + break; + + case ARG_WATCH: + arg_action = ACTION_WATCH; + break; + + case ARG_WALL: + arg_action = ACTION_WALL; + break; + + case ARG_PLYMOUTH: + arg_plymouth = true; + break; + + case ARG_CONSOLE: + arg_console = true; + if (optarg) { + + if (isempty(optarg)) { + log_error("Empty console device path is not allowed."); + return -EINVAL; + } + + arg_device = optarg; + } + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind != argc) { + log_error("%s takes no arguments.", program_invocation_short_name); + return -EINVAL; + } + + if (arg_plymouth || arg_console) { + + if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) { + log_error("Options --query and --watch conflict."); + return -EINVAL; + } + + if (arg_plymouth && arg_console) { + log_error("Options --plymouth and --console conflict."); + return -EINVAL; + } + } + + return 1; +} + +/* + * To be able to ask on all terminal devices of /dev/console + * the devices are collected. If more than one device is found, + * then on each of the terminals a inquiring task is forked. + * Every task has its own session and its own controlling terminal. + * If one of the tasks does handle a password, the remaining tasks + * will be terminated. + */ +static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) { + struct sigaction sig = { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP | SA_RESTART, + }; + + assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0); + + assert_se(sigemptyset(&sig.sa_mask) >= 0); + assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0); + + sig.sa_handler = SIG_DFL; + assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); + + *pid = fork(); + if (*pid < 0) + return log_error_errno(errno, "Failed to fork process: %m"); + + if (*pid == 0) { + int ac; + + assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); + + reset_signal_mask(); + reset_all_signal_handlers(); + + for (ac = 0; ac < argc; ac++) { + if (streq(argv[ac], "--console")) { + argv[ac] = strjoina("--console=", tty, NULL); + break; + } + } + + assert(ac < argc); + + execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); + _exit(EXIT_FAILURE); + } + return 0; +} + +static void terminate_agents(Set *pids) { + struct timespec ts; + siginfo_t status = {}; + sigset_t set; + Iterator i; + void *p; + int r, signum; + + /* + * Request termination of the remaining processes as those + * are not required anymore. + */ + SET_FOREACH(p, pids, i) + (void) kill(PTR_TO_PID(p), SIGTERM); + + /* + * Collect the processes which have go away. + */ + assert_se(sigemptyset(&set) >= 0); + assert_se(sigaddset(&set, SIGCHLD) >= 0); + timespec_store(&ts, 50 * USEC_PER_MSEC); + + while (!set_isempty(pids)) { + + zero(status); + r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG); + if (r < 0 && errno == EINTR) + continue; + + if (r == 0 && status.si_pid > 0) { + set_remove(pids, PID_TO_PTR(status.si_pid)); + continue; + } + + signum = sigtimedwait(&set, NULL, &ts); + if (signum < 0) { + if (errno != EAGAIN) + log_error_errno(errno, "sigtimedwait() failed: %m"); + break; + } + assert(signum == SIGCHLD); + } + + /* + * Kill hanging processes. + */ + SET_FOREACH(p, pids, i) { + log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p)); + (void) kill(PTR_TO_PID(p), SIGKILL); + } +} + +static int ask_on_consoles(int argc, char *argv[]) { + _cleanup_set_free_ Set *pids = NULL; + _cleanup_strv_free_ char **consoles = NULL; + siginfo_t status = {}; + char **tty; + pid_t pid; + int r; + + r = get_kernel_consoles(&consoles); + if (r < 0) + return log_error_errno(r, "Failed to determine devices of /dev/console: %m"); + + pids = set_new(NULL); + if (!pids) + return log_oom(); + + /* Start an agent on each console. */ + STRV_FOREACH(tty, consoles) { + r = ask_on_this_console(*tty, &pid, argc, argv); + if (r < 0) + return r; + + if (set_put(pids, PID_TO_PTR(pid)) < 0) + return log_oom(); + } + + /* Wait for an agent to exit. */ + for (;;) { + zero(status); + + if (waitid(P_ALL, 0, &status, WEXITED) < 0) { + if (errno == EINTR) + continue; + + return log_error_errno(errno, "waitid() failed: %m"); + } + + set_remove(pids, PID_TO_PTR(status.si_pid)); + break; + } + + if (!is_clean_exit(status.si_code, status.si_status, NULL)) + log_error("Password agent failed with: %d", status.si_status); + + terminate_agents(pids); + return 0; +} + +int main(int argc, char *argv[]) { + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + if (arg_console && !arg_device) + /* + * Spawn for each console device a separate process. + */ + r = ask_on_consoles(argc, argv); + else { + + if (arg_device) { + /* + * Later on, a controlling terminal will be acquired, + * therefore the current process has to become a session + * leader and should not have a controlling terminal already. + */ + (void) setsid(); + (void) release_terminal(); + } + + if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) + r = watch_passwords(); + else + r = show_passwords(); + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd/Makefile b/src/systemd/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/systemd/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h deleted file mode 100644 index 3bb886be75..0000000000 --- a/src/systemd/_sd-common.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef foosdcommonhfoo -#define foosdcommonhfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* This is a private header; never even think of including this directly! */ - -#if __INCLUDE_LEVEL__ <= 1 -#error "Do not include _sd-common.h directly; it is a private header." -#endif - -#ifndef _sd_printf_ -# if __GNUC__ >= 4 -# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b))) -# else -# define _sd_printf_(a,b) -# endif -#endif - -#ifndef _sd_sentinel_ -# define _sd_sentinel_ __attribute__((sentinel)) -#endif - -#ifndef _sd_packed_ -# define _sd_packed_ __attribute__((packed)) -#endif - -#ifndef _sd_pure_ -# define _sd_pure_ __attribute__((pure)) -#endif - -#ifndef _SD_STRINGIFY -# define _SD_XSTRINGIFY(x) #x -# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x) -#endif - -#ifndef _SD_BEGIN_DECLARATIONS -# ifdef __cplusplus -# define _SD_BEGIN_DECLARATIONS \ - extern "C" { \ - struct _sd_useless_struct_to_allow_trailing_semicolon_ -# else -# define _SD_BEGIN_DECLARATIONS \ - struct _sd_useless_struct_to_allow_trailing_semicolon_ -# endif -#endif - -#ifndef _SD_END_DECLARATIONS -# ifdef __cplusplus -# define _SD_END_DECLARATIONS \ - } \ - struct _sd_useless_cpp_struct_to_allow_trailing_semicolon_ -# else -# define _SD_END_DECLARATIONS \ - 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-protocol.h b/src/systemd/sd-bus-protocol.h deleted file mode 100644 index 623cee0c50..0000000000 --- a/src/systemd/sd-bus-protocol.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef foosdbusprotocolhfoo -#define foosdbusprotocolhfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* Types of message */ - -enum { - _SD_BUS_MESSAGE_TYPE_INVALID = 0, - SD_BUS_MESSAGE_METHOD_CALL, - SD_BUS_MESSAGE_METHOD_RETURN, - SD_BUS_MESSAGE_METHOD_ERROR, - SD_BUS_MESSAGE_SIGNAL, - _SD_BUS_MESSAGE_TYPE_MAX -}; - -/* Primitive types */ - -enum { - _SD_BUS_TYPE_INVALID = 0, - SD_BUS_TYPE_BYTE = 'y', - SD_BUS_TYPE_BOOLEAN = 'b', - SD_BUS_TYPE_INT16 = 'n', - SD_BUS_TYPE_UINT16 = 'q', - SD_BUS_TYPE_INT32 = 'i', - SD_BUS_TYPE_UINT32 = 'u', - SD_BUS_TYPE_INT64 = 'x', - SD_BUS_TYPE_UINT64 = 't', - SD_BUS_TYPE_DOUBLE = 'd', - SD_BUS_TYPE_STRING = 's', - SD_BUS_TYPE_OBJECT_PATH = 'o', - SD_BUS_TYPE_SIGNATURE = 'g', - SD_BUS_TYPE_UNIX_FD = 'h', - SD_BUS_TYPE_ARRAY = 'a', - SD_BUS_TYPE_VARIANT = 'v', - SD_BUS_TYPE_STRUCT = 'r', /* not actually used in signatures */ - SD_BUS_TYPE_STRUCT_BEGIN = '(', - SD_BUS_TYPE_STRUCT_END = ')', - SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */ - SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{', - SD_BUS_TYPE_DICT_ENTRY_END = '}' -}; - -/* Well-known errors. Note that this is only a sanitized subset of the - * errors that the reference implementation generates. */ - -#define SD_BUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed" -#define SD_BUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory" -#define SD_BUS_ERROR_SERVICE_UNKNOWN "org.freedesktop.DBus.Error.ServiceUnknown" -#define SD_BUS_ERROR_NAME_HAS_NO_OWNER "org.freedesktop.DBus.Error.NameHasNoOwner" -#define SD_BUS_ERROR_NO_REPLY "org.freedesktop.DBus.Error.NoReply" -#define SD_BUS_ERROR_IO_ERROR "org.freedesktop.DBus.Error.IOError" -#define SD_BUS_ERROR_BAD_ADDRESS "org.freedesktop.DBus.Error.BadAddress" -#define SD_BUS_ERROR_NOT_SUPPORTED "org.freedesktop.DBus.Error.NotSupported" -#define SD_BUS_ERROR_LIMITS_EXCEEDED "org.freedesktop.DBus.Error.LimitsExceeded" -#define SD_BUS_ERROR_ACCESS_DENIED "org.freedesktop.DBus.Error.AccessDenied" -#define SD_BUS_ERROR_AUTH_FAILED "org.freedesktop.DBus.Error.AuthFailed" -#define SD_BUS_ERROR_NO_SERVER "org.freedesktop.DBus.Error.NoServer" -#define SD_BUS_ERROR_TIMEOUT "org.freedesktop.DBus.Error.Timeout" -#define SD_BUS_ERROR_NO_NETWORK "org.freedesktop.DBus.Error.NoNetwork" -#define SD_BUS_ERROR_ADDRESS_IN_USE "org.freedesktop.DBus.Error.AddressInUse" -#define SD_BUS_ERROR_DISCONNECTED "org.freedesktop.DBus.Error.Disconnected" -#define SD_BUS_ERROR_INVALID_ARGS "org.freedesktop.DBus.Error.InvalidArgs" -#define SD_BUS_ERROR_FILE_NOT_FOUND "org.freedesktop.DBus.Error.FileNotFound" -#define SD_BUS_ERROR_FILE_EXISTS "org.freedesktop.DBus.Error.FileExists" -#define SD_BUS_ERROR_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod" -#define SD_BUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" -#define SD_BUS_ERROR_UNKNOWN_INTERFACE "org.freedesktop.DBus.Error.UnknownInterface" -#define SD_BUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" -#define SD_BUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" -#define SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN "org.freedesktop.DBus.Error.UnixProcessIdUnknown" -#define SD_BUS_ERROR_INVALID_SIGNATURE "org.freedesktop.DBus.Error.InvalidSignature" -#define SD_BUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" -#define SD_BUS_ERROR_MATCH_RULE_NOT_FOUND "org.freedesktop.DBus.Error.MatchRuleNotFound" -#define SD_BUS_ERROR_MATCH_RULE_INVALID "org.freedesktop.DBus.Error.MatchRuleInvalid" -#define SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED \ - "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h deleted file mode 100644 index e8f84eb545..0000000000 --- a/src/systemd/sd-bus-vtable.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef foosdbusvtablehfoo -#define foosdbusvtablehfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_bus_vtable sd_bus_vtable; - -#include "sd-bus.h" - -enum { - _SD_BUS_VTABLE_START = '<', - _SD_BUS_VTABLE_END = '>', - _SD_BUS_VTABLE_METHOD = 'M', - _SD_BUS_VTABLE_SIGNAL = 'S', - _SD_BUS_VTABLE_PROPERTY = 'P', - _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W' -}; - -enum { - SD_BUS_VTABLE_DEPRECATED = 1ULL << 0, - SD_BUS_VTABLE_HIDDEN = 1ULL << 1, - SD_BUS_VTABLE_UNPRIVILEGED = 1ULL << 2, - SD_BUS_VTABLE_METHOD_NO_REPLY = 1ULL << 3, - SD_BUS_VTABLE_PROPERTY_CONST = 1ULL << 4, - SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE = 1ULL << 5, - SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION = 1ULL << 6, - SD_BUS_VTABLE_PROPERTY_EXPLICIT = 1ULL << 7, - _SD_BUS_VTABLE_CAPABILITY_MASK = 0xFFFFULL << 40 -}; - -#define SD_BUS_VTABLE_CAPABILITY(x) ((uint64_t) (((x)+1) & 0xFFFF) << 40) - -struct sd_bus_vtable { - /* Please do not initialize this structure directly, use the - * macros below instead */ - - uint8_t type:8; - uint64_t flags:56; - union { - struct { - size_t element_size; - } start; - struct { - const char *member; - const char *signature; - const char *result; - sd_bus_message_handler_t handler; - size_t offset; - } method; - struct { - const char *member; - const char *signature; - } signal; - struct { - const char *member; - const char *signature; - sd_bus_property_get_t get; - sd_bus_property_set_t set; - size_t offset; - } property; - } x; -}; - -#define SD_BUS_VTABLE_START(_flags) \ - { \ - .type = _SD_BUS_VTABLE_START, \ - .flags = _flags, \ - .x.start.element_size = sizeof(sd_bus_vtable), \ - } - -#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ - { \ - .type = _SD_BUS_VTABLE_METHOD, \ - .flags = _flags, \ - .x.method.member = _member, \ - .x.method.signature = _signature, \ - .x.method.result = _result, \ - .x.method.handler = _handler, \ - .x.method.offset = _offset, \ - } -#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ - SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) - -#define SD_BUS_SIGNAL(_member, _signature, _flags) \ - { \ - .type = _SD_BUS_VTABLE_SIGNAL, \ - .flags = _flags, \ - .x.signal.member = _member, \ - .x.signal.signature = _signature, \ - } - -#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ - { \ - .type = _SD_BUS_VTABLE_PROPERTY, \ - .flags = _flags, \ - .x.property.member = _member, \ - .x.property.signature = _signature, \ - .x.property.get = _get, \ - .x.property.offset = _offset, \ - } - -#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \ - { \ - .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \ - .flags = _flags, \ - .x.property.member = _member, \ - .x.property.signature = _signature, \ - .x.property.get = _get, \ - .x.property.set = _set, \ - .x.property.offset = _offset, \ - } - -#define SD_BUS_VTABLE_END \ - { \ - .type = _SD_BUS_VTABLE_END, \ - } - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h deleted file mode 100644 index 295989cd69..0000000000 --- a/src/systemd/sd-bus.h +++ /dev/null @@ -1,456 +0,0 @@ -#ifndef foosdbushfoo -#define foosdbushfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-event.h" -#include "sd-id128.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* Types */ - -typedef struct sd_bus sd_bus; -typedef struct sd_bus_message sd_bus_message; -typedef struct sd_bus_slot sd_bus_slot; -typedef struct sd_bus_creds sd_bus_creds; -typedef struct sd_bus_track sd_bus_track; - -typedef struct { - const char *name; - const char *message; - int _need_free; -} sd_bus_error; - -typedef struct { - const char* name; - int code; -} sd_bus_error_map; - -/* Flags */ - -enum { - SD_BUS_CREDS_PID = 1ULL << 0, - SD_BUS_CREDS_TID = 1ULL << 1, - SD_BUS_CREDS_PPID = 1ULL << 2, - SD_BUS_CREDS_UID = 1ULL << 3, - SD_BUS_CREDS_EUID = 1ULL << 4, - SD_BUS_CREDS_SUID = 1ULL << 5, - SD_BUS_CREDS_FSUID = 1ULL << 6, - SD_BUS_CREDS_GID = 1ULL << 7, - SD_BUS_CREDS_EGID = 1ULL << 8, - SD_BUS_CREDS_SGID = 1ULL << 9, - SD_BUS_CREDS_FSGID = 1ULL << 10, - SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 11, - SD_BUS_CREDS_COMM = 1ULL << 12, - SD_BUS_CREDS_TID_COMM = 1ULL << 13, - SD_BUS_CREDS_EXE = 1ULL << 14, - SD_BUS_CREDS_CMDLINE = 1ULL << 15, - SD_BUS_CREDS_CGROUP = 1ULL << 16, - SD_BUS_CREDS_UNIT = 1ULL << 17, - SD_BUS_CREDS_SLICE = 1ULL << 18, - SD_BUS_CREDS_USER_UNIT = 1ULL << 19, - SD_BUS_CREDS_USER_SLICE = 1ULL << 20, - SD_BUS_CREDS_SESSION = 1ULL << 21, - SD_BUS_CREDS_OWNER_UID = 1ULL << 22, - SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 23, - SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 24, - SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 25, - SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 26, - SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 27, - SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 28, - SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 29, - SD_BUS_CREDS_TTY = 1ULL << 30, - SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 31, - SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32, - SD_BUS_CREDS_DESCRIPTION = 1ULL << 33, - SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ - _SD_BUS_CREDS_ALL = (1ULL << 34) -1 -}; - -enum { - SD_BUS_NAME_REPLACE_EXISTING = 1ULL << 0, - SD_BUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, - SD_BUS_NAME_QUEUE = 1ULL << 2 -}; - -/* Callbacks */ - -typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); -typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); -typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error); -typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error); -typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *prefix, void *userdata, char ***ret_nodes, sd_bus_error *ret_error); -typedef int (*sd_bus_track_handler_t) (sd_bus_track *track, void *userdata); - -#include "sd-bus-protocol.h" -#include "sd-bus-vtable.h" - -/* Connections */ - -int sd_bus_default(sd_bus **ret); -int sd_bus_default_user(sd_bus **ret); -int sd_bus_default_system(sd_bus **ret); - -int sd_bus_open(sd_bus **ret); -int sd_bus_open_user(sd_bus **ret); -int sd_bus_open_system(sd_bus **ret); -int sd_bus_open_system_remote(sd_bus **ret, const char *host); -int sd_bus_open_system_machine(sd_bus **ret, const char *machine); - -int sd_bus_new(sd_bus **ret); - -int sd_bus_set_address(sd_bus *bus, const char *address); -int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); -int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]); -int sd_bus_get_address(sd_bus *bus, const char **address); -int sd_bus_set_bus_client(sd_bus *bus, int b); -int sd_bus_is_bus_client(sd_bus *bus); -int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t bus_id); -int sd_bus_is_server(sd_bus *bus); -int sd_bus_set_anonymous(sd_bus *bus, int b); -int sd_bus_is_anonymous(sd_bus *bus); -int sd_bus_set_trusted(sd_bus *bus, int b); -int sd_bus_is_trusted(sd_bus *bus); -int sd_bus_set_monitor(sd_bus *bus, int b); -int sd_bus_is_monitor(sd_bus *bus); -int sd_bus_set_description(sd_bus *bus, const char *description); -int sd_bus_get_description(sd_bus *bus, const char **description); -int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask); -int sd_bus_negotiate_timestamp(sd_bus *bus, int b); -int sd_bus_negotiate_fds(sd_bus *bus, int b); -int sd_bus_can_send(sd_bus *bus, char type); -int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask); -int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); -int sd_bus_get_allow_interactive_authorization(sd_bus *bus); - -int sd_bus_start(sd_bus *ret); - -int sd_bus_try_close(sd_bus *bus); -void sd_bus_close(sd_bus *bus); - -sd_bus *sd_bus_ref(sd_bus *bus); -sd_bus *sd_bus_unref(sd_bus *bus); -sd_bus *sd_bus_flush_close_unref(sd_bus *bus); - -void sd_bus_default_flush_close(void); - -int sd_bus_is_open(sd_bus *bus); - -int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); -int sd_bus_get_scope(sd_bus *bus, const char **scope); -int sd_bus_get_tid(sd_bus *bus, pid_t *tid); -int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); - -int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie); -int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie); -int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply); -int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec); - -int sd_bus_get_fd(sd_bus *bus); -int sd_bus_get_events(sd_bus *bus); -int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec); -int sd_bus_process(sd_bus *bus, sd_bus_message **r); -int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); -int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); -int sd_bus_flush(sd_bus *bus); - -sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); -sd_bus_message* sd_bus_get_current_message(sd_bus *bus); -sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus); -void* sd_bus_get_current_userdata(sd_bus *bus); - -int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority); -int sd_bus_detach_event(sd_bus *bus); -sd_event *sd_bus_get_event(sd_bus *bus); - -int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); -int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); -int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); -int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); -int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); -int sd_bus_add_fallback_vtable(sd_bus *bus, sd_bus_slot **slot, const char *prefix, const char *interface, const sd_bus_vtable *vtable, sd_bus_object_find_t find, void *userdata); -int sd_bus_add_node_enumerator(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_node_enumerator_t callback, void *userdata); -int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path); - -/* Slot object */ - -sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); -sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); - -sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); -void *sd_bus_slot_get_userdata(sd_bus_slot *slot); -void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); -int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); -int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description); - -sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); -sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus); -void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot); - -/* Message object */ - -int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member); -int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); -int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m); -int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e); -int sd_bus_message_new_method_errorf(sd_bus_message *call, sd_bus_message **m, const char *name, const char *format, ...) _sd_printf_(4, 5); -int sd_bus_message_new_method_errno(sd_bus_message *call, sd_bus_message **m, int error, const sd_bus_error *e); -int sd_bus_message_new_method_errnof(sd_bus_message *call, sd_bus_message **m, int error, const char *format, ...) _sd_printf_(4, 5); - -sd_bus_message* sd_bus_message_ref(sd_bus_message *m); -sd_bus_message* sd_bus_message_unref(sd_bus_message *m); - -int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type); -int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie); -int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie); -int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority); - -int sd_bus_message_get_expect_reply(sd_bus_message *m); -int sd_bus_message_get_auto_start(sd_bus_message *m); -int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m); - -const char *sd_bus_message_get_signature(sd_bus_message *m, int complete); -const char *sd_bus_message_get_path(sd_bus_message *m); -const char *sd_bus_message_get_interface(sd_bus_message *m); -const char *sd_bus_message_get_member(sd_bus_message *m); -const char *sd_bus_message_get_destination(sd_bus_message *m); -const char *sd_bus_message_get_sender(sd_bus_message *m); -const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m); -int sd_bus_message_get_errno(sd_bus_message *m); - -int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec); -int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec); -int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t* seqnum); - -sd_bus* sd_bus_message_get_bus(sd_bus_message *m); -sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */ - -int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); -int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); -int sd_bus_message_is_method_error(sd_bus_message *m, const char *name); -int sd_bus_message_is_empty(sd_bus_message *m); -int sd_bus_message_has_signature(sd_bus_message *m, const char *signature); - -int sd_bus_message_set_expect_reply(sd_bus_message *m, int b); -int sd_bus_message_set_auto_start(sd_bus_message *m, int b); -int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); - -int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); -int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); - -int sd_bus_message_append(sd_bus_message *m, const char *types, ...); -int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); -int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); -int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); -int sd_bus_message_append_array_iovec(sd_bus_message *m, char type, const struct iovec *iov, unsigned n); -int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd, uint64_t offset, uint64_t size); -int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); -int sd_bus_message_append_string_iovec(sd_bus_message *m, const struct iovec *iov, unsigned n); -int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd, uint64_t offset, uint64_t size); -int sd_bus_message_append_strv(sd_bus_message *m, char **l); -int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); -int sd_bus_message_close_container(sd_bus_message *m); -int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all); - -int sd_bus_message_read(sd_bus_message *m, const char *types, ...); -int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p); -int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, size_t *size); -int sd_bus_message_read_strv(sd_bus_message *m, char ***l); /* free the result! */ -int sd_bus_message_skip(sd_bus_message *m, const char *types); -int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents); -int sd_bus_message_exit_container(sd_bus_message *m); -int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents); -int sd_bus_message_verify_type(sd_bus_message *m, char type, const char *contents); -int sd_bus_message_at_end(sd_bus_message *m, int complete); -int sd_bus_message_rewind(sd_bus_message *m, int complete); - -/* Bus management */ - -int sd_bus_get_unique_name(sd_bus *bus, const char **unique); -int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); -int sd_bus_release_name(sd_bus *bus, const char *name); -int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ -int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ -int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); - -/* Convenience calls */ - -int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...); -int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...); -int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type); -int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr); -int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */ -int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */ -int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...); - -int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...); -int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e); -int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); -int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e); -int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); - -int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...); - -int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); -int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) _sd_sentinel_; - -int sd_bus_emit_object_added(sd_bus *bus, const char *path); -int sd_bus_emit_object_removed(sd_bus *bus, const char *path); -int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); -int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; -int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); -int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_; - -int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); -int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); - -/* Credential handling */ - -int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); -sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c); -sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c); -uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); -uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c); - -int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid); -int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid); -int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid); -int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid); -int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid); -int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid); -int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid); -int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid); -int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid); -int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid); -int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid); -int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids); -int sd_bus_creds_get_comm(sd_bus_creds *c, const char **comm); -int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **comm); -int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe); -int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline); -int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **cgroup); -int sd_bus_creds_get_unit(sd_bus_creds *c, const char **unit); -int sd_bus_creds_get_slice(sd_bus_creds *c, const char **slice); -int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **unit); -int sd_bus_creds_get_user_slice(sd_bus_creds *c, const char **slice); -int sd_bus_creds_get_session(sd_bus_creds *c, const char **session); -int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid); -int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability); -int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability); -int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability); -int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability); -int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **context); -int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid); -int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid); -int sd_bus_creds_get_tty(sd_bus_creds *c, const char **tty); -int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name); -int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names); -int sd_bus_creds_get_description(sd_bus_creds *c, const char **name); - -/* Error structures */ - -#define SD_BUS_ERROR_MAKE_CONST(name, message) ((const sd_bus_error) {(name), (message), 0}) -#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE_CONST(NULL, NULL) - -void sd_bus_error_free(sd_bus_error *e); -int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message); -int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_(3, 4); -int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message); -int sd_bus_error_set_errno(sd_bus_error *e, int error); -int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) _sd_printf_(3, 4); -int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0); -int sd_bus_error_get_errno(const sd_bus_error *e); -int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); -int sd_bus_error_is_set(const sd_bus_error *e); -int sd_bus_error_has_name(const sd_bus_error *e, const char *name); - -#define SD_BUS_ERROR_MAP(_name, _code) \ - { \ - .name = _name, \ - .code = _code, \ - } -#define SD_BUS_ERROR_MAP_END \ - { \ - .name = NULL, \ - .code = - 'x', \ - } - -int sd_bus_error_add_map(const sd_bus_error_map *map); - -/* Auxiliary macros */ - -#define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ - (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ - (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ - (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ - (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] - -#define SD_BUS_MESSAGE_READ_ID128(x) 16, \ - &(x).bytes[0], &(x).bytes[1], &(x).bytes[2], &(x).bytes[3], \ - &(x).bytes[4], &(x).bytes[5], &(x).bytes[6], &(x).bytes[7], \ - &(x).bytes[8], &(x).bytes[9], &(x).bytes[10], &(x).bytes[11], \ - &(x).bytes[12], &(x).bytes[13], &(x).bytes[14], &(x).bytes[15] - -/* 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 */ - -int sd_bus_track_new(sd_bus *bus, sd_bus_track **track, sd_bus_track_handler_t handler, void *userdata); -sd_bus_track* sd_bus_track_ref(sd_bus_track *track); -sd_bus_track* sd_bus_track_unref(sd_bus_track *track); - -sd_bus* sd_bus_track_get_bus(sd_bus_track *track); -void *sd_bus_track_get_userdata(sd_bus_track *track); -void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata); - -int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m); -int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m); -int sd_bus_track_add_name(sd_bus_track *track, const char *name); -int sd_bus_track_remove_name(sd_bus_track *track, const char *name); - -unsigned sd_bus_track_count(sd_bus_track *track); -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 deleted file mode 100644 index 740b176903..0000000000 --- a/src/systemd/sd-daemon.h +++ /dev/null @@ -1,294 +0,0 @@ -#ifndef foosddaemonhfoo -#define foosddaemonhfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* - The following functionality is provided: - - - Support for logging with log levels on stderr - - File descriptor passing for socket-based activation - - Daemon startup and status notification - - Detection of systemd boots - - See sd-daemon(3) for more information. -*/ - -/* - Log levels for usage on stderr: - - fprintf(stderr, SD_NOTICE "Hello World!\n"); - - This is similar to printk() usage in the kernel. -*/ -#define SD_EMERG "<0>" /* system is unusable */ -#define SD_ALERT "<1>" /* action must be taken immediately */ -#define SD_CRIT "<2>" /* critical conditions */ -#define SD_ERR "<3>" /* error conditions */ -#define SD_WARNING "<4>" /* warning conditions */ -#define SD_NOTICE "<5>" /* normal but significant condition */ -#define SD_INFO "<6>" /* informational */ -#define SD_DEBUG "<7>" /* debug-level messages */ - -/* The first passed file descriptor is fd 3 */ -#define SD_LISTEN_FDS_START 3 - -/* - Returns how many file descriptors have been passed, or a negative - errno code on failure. Optionally, removes the $LISTEN_FDS and - $LISTEN_PID file descriptors from the environment (recommended, but - problematic in threaded environments). If r is the return value of - this function you'll find the file descriptors passed as fds - SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative - errno style error code on failure. This function call ensures that - the FD_CLOEXEC flag is set for the passed file descriptors, to make - sure they are not passed on to child processes. If FD_CLOEXEC shall - not be set, the caller needs to unset it after this call for all file - descriptors that are used. - - See sd_listen_fds(3) for more information. -*/ -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 - specified path, 0 otherwise. If path is NULL a path name check will - not be done and the call only verifies if the file descriptor - refers to a FIFO. Returns a negative errno style error code on - failure. - - See sd_is_fifo(3) for more information. -*/ -int sd_is_fifo(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a special character device on the file - system stored under the specified path, 0 otherwise. - If path is NULL a path name check will not be done and the call - only verifies if the file descriptor refers to a special character. - Returns a negative errno style error code on failure. - - See sd_is_special(3) for more information. -*/ -int sd_is_special(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a socket of the specified family (AF_INET, - ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If - family is 0 a socket family check will not be done. If type is 0 a - socket type check will not be done and the call only verifies if - the file descriptor refers to a socket. If listening is > 0 it is - verified that the socket is in listening mode. (i.e. listen() has - been called) If listening is == 0 it is verified that the socket is - not in listening mode. If listening is < 0 no listening mode check - is done. Returns a negative errno style error code on failure. - - See sd_is_socket(3) for more information. -*/ -int sd_is_socket(int fd, int family, int type, int listening); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an Internet socket, of the specified family - (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, - SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version - check is not done. If type is 0 a socket type check will not be - done. If port is 0 a socket port check will not be done. The - listening flag is used the same way as in sd_is_socket(). Returns a - negative errno style error code on failure. - - See sd_is_socket_inet(3) for more information. -*/ -int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an AF_UNIX socket of the specified type - (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 - a socket type check will not be done. If path is NULL a socket path - check will not be done. For normal AF_UNIX sockets set length to - 0. For abstract namespace sockets set length to the length of the - socket name (including the initial 0 byte), and pass the full - socket path in path (including the initial 0 byte). The listening - flag is used the same way as in sd_is_socket(). Returns a negative - errno style error code on failure. - - See sd_is_socket_unix(3) for more information. -*/ -int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a POSIX Message Queue of the specified name, - 0 otherwise. If path is NULL a message queue name check is not - done. Returns a negative errno style error code on failure. - - See sd_is_mq(3) for more information. -*/ -int sd_is_mq(int fd, const char *path); - -/* - Informs systemd about changed daemon state. This takes a number of - newline separated environment-style variable assignments in a - string. The following variables are known: - - READY=1 Tells systemd that daemon startup is finished (only - relevant for services of Type=notify). The passed - argument is a boolean "1" or "0". Since there is - little value in signaling non-readiness the only - value daemons should send is "READY=1". - - STATUS=... Passes a single-line status string back to systemd - that describes the daemon state. This is free-form - and can be used for various purposes: general state - feedback, fsck-like programs could pass completion - percentages and failing programs could pass a human - readable error message. Example: "STATUS=Completed - 66% of file system check..." - - ERRNO=... If a daemon fails, the errno-style error code, - formatted as string. Example: "ERRNO=2" for ENOENT. - - BUSERROR=... If a daemon fails, the D-Bus error-style error - code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" - - MAINPID=... The main pid of a daemon, in case systemd did not - fork off the process itself. Example: "MAINPID=4711" - - WATCHDOG=1 Tells systemd to update the watchdog timestamp. - Services using this feature should do this in - regular intervals. A watchdog framework can use the - timestamps to detect failed services. Also see - sd_watchdog_enabled() below. - - FDSTORE=1 Store the file descriptors passed along with the - message in the per-service file descriptor store, - and pass them to the main process again on next - invocation. This variable is only supported with - sd_pid_notify_with_fds(). - - WATCHDOG_USEC=... - Reset watchdog_usec value during runtime. - To reset watchdog_usec value, start the service again. - Example: "WATCHDOG_USEC=20000000" - - Daemons can choose to send additional variables. However, it is - recommended to prefix variable names not listed above with X_. - - Returns a negative errno-style error code on failure. Returns > 0 - if systemd could be notified, 0 if it couldn't possibly because - systemd is not running. - - Example: When a daemon finished starting up, it could issue this - call to notify systemd about it: - - sd_notify(0, "READY=1"); - - See sd_notifyf() for more complete examples. - - See sd_notify(3) for more information. -*/ -int sd_notify(int unset_environment, const char *state); - -/* - Similar to sd_notify() but takes a format string. - - Example 1: A daemon could send the following after initialization: - - sd_notifyf(0, "READY=1\n" - "STATUS=Processing requests...\n" - "MAINPID=%lu", - (unsigned long) getpid()); - - Example 2: A daemon could send the following shortly before - exiting, on failure: - - sd_notifyf(0, "STATUS=Failed to start up: %s\n" - "ERRNO=%i", - strerror(errno), - errno); - - See sd_notifyf(3) for more information. -*/ -int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_(2,3); - -/* - Similar to sd_notify(), but send the message on behalf of another - process, if the appropriate permissions are available. -*/ -int sd_pid_notify(pid_t pid, int unset_environment, const char *state); - -/* - Similar to sd_notifyf(), but send the message on behalf of another - process, if the appropriate permissions are available. -*/ -int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _sd_printf_(3,4); - -/* - Similar to sd_pid_notify(), but also passes the specified fd array - to the service manager for storage. This is particularly useful for - FDSTORE=1 messages. -*/ -int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds); - -/* - Returns > 0 if the system was booted with systemd. Returns < 0 on - error. Returns 0 if the system was not booted with systemd. Note - that all of the functions above handle non-systemd boots just - fine. You should NOT protect them with a call to this function. Also - note that this function checks whether the system, not the user - session is controlled by systemd. However the functions above work - for both user and system services. - - See sd_booted(3) for more information. -*/ -int sd_booted(void); - -/* - Returns > 0 if the service manager expects watchdog keep-alive - events to be sent regularly via sd_notify(0, "WATCHDOG=1"). Returns - 0 if it does not expect this. If the usec argument is non-NULL - returns the watchdog timeout in µs after which the service manager - will act on a process that has not sent a watchdog keep alive - message. This function is useful to implement services that - recognize automatically if they are being run under supervision of - systemd with WatchdogSec= set. It is recommended for clients to - generate keep-alive pings via sd_notify(0, "WATCHDOG=1") every half - of the returned time. - - See sd_watchdog_enabled(3) for more information. -*/ -int sd_watchdog_enabled(int unset_environment, uint64_t *usec); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h deleted file mode 100644 index c1d07561d7..0000000000 --- a/src/systemd/sd-device.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef foosddevicehfoo -#define foosddevicehfoo - -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - Copyright 2014-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 . -***/ - -#include -#include -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_device sd_device; -typedef struct sd_device_enumerator sd_device_enumerator; - -/* device */ - -sd_device *sd_device_ref(sd_device *device); -sd_device *sd_device_unref(sd_device *device); - -int sd_device_new_from_syspath(sd_device **ret, const char *syspath); -int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); -int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); -int sd_device_new_from_device_id(sd_device **ret, const char *id); - -int sd_device_get_parent(sd_device *child, sd_device **ret); -int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); - -int sd_device_get_syspath(sd_device *device, const char **ret); -int sd_device_get_subsystem(sd_device *device, const char **ret); -int sd_device_get_devtype(sd_device *device, const char **ret); -int sd_device_get_devnum(sd_device *device, dev_t *devnum); -int sd_device_get_ifindex(sd_device *device, int *ifindex); -int sd_device_get_driver(sd_device *device, const char **ret); -int sd_device_get_devpath(sd_device *device, const char **ret); -int sd_device_get_devname(sd_device *device, const char **ret); -int sd_device_get_sysname(sd_device *device, const char **ret); -int sd_device_get_sysnum(sd_device *device, const char **ret); - -int sd_device_get_is_initialized(sd_device *device, int *initialized); -int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec); - -const char *sd_device_get_tag_first(sd_device *device); -const char *sd_device_get_tag_next(sd_device *device); -const char *sd_device_get_devlink_first(sd_device *device); -const char *sd_device_get_devlink_next(sd_device *device); -const char *sd_device_get_property_first(sd_device *device, const char **value); -const char *sd_device_get_property_next(sd_device *device, const char **value); -const char *sd_device_get_sysattr_first(sd_device *device); -const char *sd_device_get_sysattr_next(sd_device *device); - -int sd_device_has_tag(sd_device *device, const char *tag); -int sd_device_get_property_value(sd_device *device, const char *key, const char **value); -int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); - -int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, char *value); - -/* device enumerator */ - -int sd_device_enumerator_new(sd_device_enumerator **ret); -sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); -sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); - -sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); -sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); -sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); -sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); - -int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); -int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); -int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); -int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); -int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); -int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); -int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); - -_SD_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 deleted file mode 100644 index 9a90c2ed42..0000000000 --- a/src/systemd/sd-dhcp-client.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef foosddhcpclienthfoo -#define foosddhcpclienthfoo - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include - -#include "sd-dhcp-lease.h" -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_DHCP_CLIENT_EVENT_STOP = 0, - SD_DHCP_CLIENT_EVENT_IP_ACQUIRE = 1, - SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, - SD_DHCP_CLIENT_EVENT_EXPIRED = 3, - 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_callback_t)(sd_dhcp_client *client, int event, void *userdata); -int sd_dhcp_client_set_callback( - sd_dhcp_client *client, - sd_dhcp_client_callback_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); -int sd_dhcp_client_set_request_broadcast( - sd_dhcp_client *client, - int broadcast); -int sd_dhcp_client_set_ifindex( - sd_dhcp_client *client, - int interface_index); -int sd_dhcp_client_set_mac( - sd_dhcp_client *client, - const uint8_t *addr, - size_t addr_len, - uint16_t arp_type); -int sd_dhcp_client_set_client_id( - sd_dhcp_client *client, - uint8_t type, - const uint8_t *data, - size_t data_len); -int sd_dhcp_client_set_iaid_duid( - sd_dhcp_client *client, - uint32_t iaid, - uint16_t duid_type, - const void *duid, - size_t duid_len); -int sd_dhcp_client_get_client_id( - sd_dhcp_client *client, - uint8_t *type, - const uint8_t **data, - size_t *data_len); -int sd_dhcp_client_set_mtu( - sd_dhcp_client *client, - uint32_t mtu); -int sd_dhcp_client_set_hostname( - sd_dhcp_client *client, - const char *hostname); -int sd_dhcp_client_set_vendor_class_identifier( - sd_dhcp_client *client, - const char *vci); -int sd_dhcp_client_get_lease( - sd_dhcp_client *client, - sd_dhcp_lease **ret); - -int sd_dhcp_client_stop(sd_dhcp_client *client); -int sd_dhcp_client_start(sd_dhcp_client *client); - -sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); -sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); - -int sd_dhcp_client_new(sd_dhcp_client **ret); - -int sd_dhcp_client_attach_event( - sd_dhcp_client *client, - sd_event *event, - int64_t priority); -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 deleted file mode 100644 index 2f565ca825..0000000000 --- a/src/systemd/sd-dhcp-lease.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef foosddhcpleasehfoo -#define foosddhcpleasehfoo - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include -#include -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_dhcp_lease sd_dhcp_lease; -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); - -int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); -int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); -int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); -int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); -int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); -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, 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 deleted file mode 100644 index d4517a26d6..0000000000 --- a/src/systemd/sd-dhcp-server.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef foosddhcpserverhfoo -#define foosddhcpserverhfoo - -/*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - 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 . -***/ - -#include -#include - -#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); - -sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); -sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server); - -int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t priority); -int sd_dhcp_server_detach_event(sd_dhcp_server *client); -sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); - -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); - -int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); - -int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); -int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); -int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); -int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); - -int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); -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 deleted file mode 100644 index 7819f0d2de..0000000000 --- a/src/systemd/sd-dhcp6-client.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef foosddhcp6clienthfoo -#define foosddhcp6clienthfoo - -/*** - 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 . -***/ - -#include -#include -#include - -#include "sd-dhcp6-lease.h" -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_DHCP6_CLIENT_EVENT_STOP = 0, - SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE = 10, - SD_DHCP6_CLIENT_EVENT_RETRANS_MAX = 11, - SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE = 12, - 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_callback_t)(sd_dhcp6_client *client, int event, void *userdata); -int sd_dhcp6_client_set_callback( - sd_dhcp6_client *client, - sd_dhcp6_client_callback_t cb, - void *userdata); - -int sd_dhcp6_client_set_ifindex( - 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 duid_type, - const void *duid, - size_t duid_len); -int sd_dhcp6_client_set_iaid( - sd_dhcp6_client *client, - uint32_t iaid); -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); - -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, - int64_t priority); -int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); -sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); -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 deleted file mode 100644 index 184fbb8e0d..0000000000 --- a/src/systemd/sd-dhcp6-lease.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef foosddhcp6leasehfoo -#define foosddhcp6leasehfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - 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 . -***/ - -#include -#include - -#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); -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid); - -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); -int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - struct in6_addr **addrs); -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 deleted file mode 100644 index cc26b7df55..0000000000 --- a/src/systemd/sd-event.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef foosdeventhfoo -#define foosdeventhfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "_sd-common.h" - -/* - Why is this better than pure epoll? - - - Supports event source prioritization - - Scales better with a large number of time events because it does not require one timerfd each - - Automatically tries to coalesce timer events system-wide - - Handles signals and child PIDs -*/ - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_event sd_event; -typedef struct sd_event_source sd_event_source; - -enum { - SD_EVENT_OFF = 0, - SD_EVENT_ON = 1, - SD_EVENT_ONESHOT = -1 -}; - -enum { - SD_EVENT_INITIAL, - SD_EVENT_ARMED, - SD_EVENT_PENDING, - SD_EVENT_RUNNING, - SD_EVENT_EXITING, - SD_EVENT_FINISHED, - SD_EVENT_PREPARING -}; - -enum { - /* And everything in-between and outside is good too */ - SD_EVENT_PRIORITY_IMPORTANT = -100, - SD_EVENT_PRIORITY_NORMAL = 0, - SD_EVENT_PRIORITY_IDLE = 100 -}; - -typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); -typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); -typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); -typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); -#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED -typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); -#else -typedef void* sd_event_child_handler_t; -#endif - -int sd_event_default(sd_event **e); - -int sd_event_new(sd_event **e); -sd_event* sd_event_ref(sd_event *e); -sd_event* sd_event_unref(sd_event *e); - -int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, sd_event_io_handler_t callback, void *userdata); -int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); -int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata); -int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); -int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); -int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); -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 usec); -int sd_event_dispatch(sd_event *e); -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); - -int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec); - -int sd_event_get_fd(sd_event *e); -int sd_event_get_state(sd_event *e); -int sd_event_get_tid(sd_event *e, pid_t *tid); -int sd_event_get_exit_code(sd_event *e, int *code); -int sd_event_set_watchdog(sd_event *e, int b); -int sd_event_get_watchdog(sd_event *e); -int sd_event_get_iteration(sd_event *e, uint64_t *ret); - -sd_event_source* sd_event_source_ref(sd_event_source *s); -sd_event_source* sd_event_source_unref(sd_event_source *s); - -sd_event *sd_event_source_get_event(sd_event_source *s); -void* sd_event_source_get_userdata(sd_event_source *s); -void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); - -int sd_event_source_set_description(sd_event_source *s, const char *description); -int sd_event_source_get_description(sd_event_source *s, const char **description); -int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback); -int sd_event_source_get_pending(sd_event_source *s); -int sd_event_source_get_priority(sd_event_source *s, int64_t *priority); -int sd_event_source_set_priority(sd_event_source *s, int64_t priority); -int sd_event_source_get_enabled(sd_event_source *s, int *enabled); -int sd_event_source_set_enabled(sd_event_source *s, int enabled); -int sd_event_source_get_io_fd(sd_event_source *s); -int sd_event_source_set_io_fd(sd_event_source *s, int fd); -int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); -int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); -int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); -int sd_event_source_get_time(sd_event_source *s, uint64_t *usec); -int sd_event_source_set_time(sd_event_source *s, uint64_t usec); -int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec); -int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec); -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 deleted file mode 100644 index 7105920492..0000000000 --- a/src/systemd/sd-hwdb.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef foosdhwdbhfoo -#define foosdhwdbhfoo - -/*** - This file is part of systemd. - - Copyright 2008-2012 Kay Sievers - 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 . -***/ - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_hwdb sd_hwdb; - -sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb); -sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb); - -int sd_hwdb_new(sd_hwdb **ret); - -int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **value); - -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 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-id128.h b/src/systemd/sd-id128.h deleted file mode 100644 index 4dff0b9b81..0000000000 --- a/src/systemd/sd-id128.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef foosdid128hfoo -#define foosdid128hfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* 128-bit ID APIs. See sd-id128(3) for more information. */ - -typedef union sd_id128 sd_id128_t; - -union sd_id128 { - uint8_t bytes[16]; - uint64_t qwords[2]; -}; - -#define SD_ID128_STRING_MAX 33 - -char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]); - -int sd_id128_from_string(const char *s, sd_id128_t *ret); - -int sd_id128_randomize(sd_id128_t *ret); - -int sd_id128_get_machine(sd_id128_t *ret); - -int sd_id128_get_boot(sd_id128_t *ret); - -#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ - ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ - 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) - -#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ - { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ - 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} - -/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 - * times. It is hence not a good idea to call this macro with an - * expensive function as parameter or an expression with side - * effects */ - -#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" -#define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] - -#define SD_ID128_CONST_STR(x) \ - ((const char[SD_ID128_STRING_MAX]) { \ - ((x).bytes[0] >> 4) >= 10 ? 'a' + ((x).bytes[0] >> 4) - 10 : '0' + ((x).bytes[0] >> 4), \ - ((x).bytes[0] & 15) >= 10 ? 'a' + ((x).bytes[0] & 15) - 10 : '0' + ((x).bytes[0] & 15), \ - ((x).bytes[1] >> 4) >= 10 ? 'a' + ((x).bytes[1] >> 4) - 10 : '0' + ((x).bytes[1] >> 4), \ - ((x).bytes[1] & 15) >= 10 ? 'a' + ((x).bytes[1] & 15) - 10 : '0' + ((x).bytes[1] & 15), \ - ((x).bytes[2] >> 4) >= 10 ? 'a' + ((x).bytes[2] >> 4) - 10 : '0' + ((x).bytes[2] >> 4), \ - ((x).bytes[2] & 15) >= 10 ? 'a' + ((x).bytes[2] & 15) - 10 : '0' + ((x).bytes[2] & 15), \ - ((x).bytes[3] >> 4) >= 10 ? 'a' + ((x).bytes[3] >> 4) - 10 : '0' + ((x).bytes[3] >> 4), \ - ((x).bytes[3] & 15) >= 10 ? 'a' + ((x).bytes[3] & 15) - 10 : '0' + ((x).bytes[3] & 15), \ - ((x).bytes[4] >> 4) >= 10 ? 'a' + ((x).bytes[4] >> 4) - 10 : '0' + ((x).bytes[4] >> 4), \ - ((x).bytes[4] & 15) >= 10 ? 'a' + ((x).bytes[4] & 15) - 10 : '0' + ((x).bytes[4] & 15), \ - ((x).bytes[5] >> 4) >= 10 ? 'a' + ((x).bytes[5] >> 4) - 10 : '0' + ((x).bytes[5] >> 4), \ - ((x).bytes[5] & 15) >= 10 ? 'a' + ((x).bytes[5] & 15) - 10 : '0' + ((x).bytes[5] & 15), \ - ((x).bytes[6] >> 4) >= 10 ? 'a' + ((x).bytes[6] >> 4) - 10 : '0' + ((x).bytes[6] >> 4), \ - ((x).bytes[6] & 15) >= 10 ? 'a' + ((x).bytes[6] & 15) - 10 : '0' + ((x).bytes[6] & 15), \ - ((x).bytes[7] >> 4) >= 10 ? 'a' + ((x).bytes[7] >> 4) - 10 : '0' + ((x).bytes[7] >> 4), \ - ((x).bytes[7] & 15) >= 10 ? 'a' + ((x).bytes[7] & 15) - 10 : '0' + ((x).bytes[7] & 15), \ - ((x).bytes[8] >> 4) >= 10 ? 'a' + ((x).bytes[8] >> 4) - 10 : '0' + ((x).bytes[8] >> 4), \ - ((x).bytes[8] & 15) >= 10 ? 'a' + ((x).bytes[8] & 15) - 10 : '0' + ((x).bytes[8] & 15), \ - ((x).bytes[9] >> 4) >= 10 ? 'a' + ((x).bytes[9] >> 4) - 10 : '0' + ((x).bytes[9] >> 4), \ - ((x).bytes[9] & 15) >= 10 ? 'a' + ((x).bytes[9] & 15) - 10 : '0' + ((x).bytes[9] & 15), \ - ((x).bytes[10] >> 4) >= 10 ? 'a' + ((x).bytes[10] >> 4) - 10 : '0' + ((x).bytes[10] >> 4), \ - ((x).bytes[10] & 15) >= 10 ? 'a' + ((x).bytes[10] & 15) - 10 : '0' + ((x).bytes[10] & 15), \ - ((x).bytes[11] >> 4) >= 10 ? 'a' + ((x).bytes[11] >> 4) - 10 : '0' + ((x).bytes[11] >> 4), \ - ((x).bytes[11] & 15) >= 10 ? 'a' + ((x).bytes[11] & 15) - 10 : '0' + ((x).bytes[11] & 15), \ - ((x).bytes[12] >> 4) >= 10 ? 'a' + ((x).bytes[12] >> 4) - 10 : '0' + ((x).bytes[12] >> 4), \ - ((x).bytes[12] & 15) >= 10 ? 'a' + ((x).bytes[12] & 15) - 10 : '0' + ((x).bytes[12] & 15), \ - ((x).bytes[13] >> 4) >= 10 ? 'a' + ((x).bytes[13] >> 4) - 10 : '0' + ((x).bytes[13] >> 4), \ - ((x).bytes[13] & 15) >= 10 ? 'a' + ((x).bytes[13] & 15) - 10 : '0' + ((x).bytes[13] & 15), \ - ((x).bytes[14] >> 4) >= 10 ? 'a' + ((x).bytes[14] >> 4) - 10 : '0' + ((x).bytes[14] >> 4), \ - ((x).bytes[14] & 15) >= 10 ? 'a' + ((x).bytes[14] & 15) - 10 : '0' + ((x).bytes[14] & 15), \ - ((x).bytes[15] >> 4) >= 10 ? 'a' + ((x).bytes[15] >> 4) - 10 : '0' + ((x).bytes[15] >> 4), \ - ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ - 0 }) - -_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { - return memcmp(&a, &b, 16) == 0; -} - -_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { - return a.qwords[0] == 0 && a.qwords[1] == 0; -} - -#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h deleted file mode 100644 index 16d99983a8..0000000000 --- a/src/systemd/sd-ipv4acd.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef foosdipv4acdfoo -#define foosdipv4acdfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 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 . -***/ - -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_IPV4ACD_EVENT_STOP = 0, - SD_IPV4ACD_EVENT_BIND = 1, - SD_IPV4ACD_EVENT_CONFLICT = 2, -}; - -typedef struct sd_ipv4acd sd_ipv4acd; -typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata); - -int sd_ipv4acd_detach_event(sd_ipv4acd *acd); -int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority); -int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address); -int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata); -int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr); -int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); -int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address); -int sd_ipv4acd_is_running(sd_ipv4acd *acd); -int sd_ipv4acd_start(sd_ipv4acd *acd); -int sd_ipv4acd_stop(sd_ipv4acd *acd); -sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd); -sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd); -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 deleted file mode 100644 index 1109ec52e0..0000000000 --- a/src/systemd/sd-ipv4ll.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef foosdipv4llfoo -#define foosdipv4llfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. 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 . -***/ - -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - SD_IPV4LL_EVENT_STOP = 0, - SD_IPV4LL_EVENT_BIND = 1, - SD_IPV4LL_EVENT_CONFLICT = 2, -}; - -typedef struct sd_ipv4ll sd_ipv4ll; -typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); - -int sd_ipv4ll_detach_event(sd_ipv4ll *ll); -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); -int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); -int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); -int sd_ipv4ll_set_ifindex(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, uint64_t seed); -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 deleted file mode 100644 index 9c36b27157..0000000000 --- a/src/systemd/sd-journal.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef foosdjournalhfoo -#define foosdjournalhfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#include "sd-id128.h" - -#include "_sd-common.h" - -/* Journal APIs. See sd-journal(3) for more information. */ - -_SD_BEGIN_DECLARATIONS; - -/* Write to daemon */ -int sd_journal_print(int priority, const char *format, ...) _sd_printf_(2, 3); -int sd_journal_printv(int priority, const char *format, va_list ap) _sd_printf_(2, 0); -int sd_journal_send(const char *format, ...) _sd_printf_(1, 0) _sd_sentinel_; -int sd_journal_sendv(const struct iovec *iov, int n); -int sd_journal_perror(const char *message); - -/* Used by the macros below. You probably don't want to call this directly. */ -int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(5, 6); -int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) _sd_printf_(5, 0); -int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) _sd_printf_(4, 0) _sd_sentinel_; -int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n); -int sd_journal_perror_with_location(const char *file, const char *line, const char *func, const char *message); - -/* implicitly add code location to messages sent, if this is enabled */ -#ifndef SD_JOURNAL_SUPPRESS_LOCATION - -#define sd_journal_print(priority, ...) sd_journal_print_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) -#define sd_journal_printv(priority, format, ap) sd_journal_printv_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, format, ap) -#define sd_journal_send(...) sd_journal_send_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, __VA_ARGS__) -#define sd_journal_sendv(iovec, n) sd_journal_sendv_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, iovec, n) -#define sd_journal_perror(message) sd_journal_perror_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _SD_STRINGIFY(__LINE__), __func__, message) - -#endif - -int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); - -/* Browse journal stream */ - -typedef struct sd_journal sd_journal; - -/* Open flags */ -enum { - SD_JOURNAL_LOCAL_ONLY = 1 << 0, - SD_JOURNAL_RUNTIME_ONLY = 1 << 1, - SD_JOURNAL_SYSTEM = 1 << 2, - SD_JOURNAL_CURRENT_USER = 1 << 3, - SD_JOURNAL_OS_ROOT = 1 << 4, - - SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ -}; - -/* Wakeup event types */ -enum { - SD_JOURNAL_NOP, - SD_JOURNAL_APPEND, - SD_JOURNAL_INVALIDATE -}; - -int sd_journal_open(sd_journal **ret, int flags); -int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); -int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); -int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); -int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ -void sd_journal_close(sd_journal *j); - -int sd_journal_previous(sd_journal *j); -int sd_journal_next(sd_journal *j); - -int sd_journal_previous_skip(sd_journal *j, uint64_t skip); -int sd_journal_next_skip(sd_journal *j, uint64_t skip); - -int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); -int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id); - -int sd_journal_set_data_threshold(sd_journal *j, size_t sz); -int sd_journal_get_data_threshold(sd_journal *j, size_t *sz); - -int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l); -int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); -void sd_journal_restart_data(sd_journal *j); - -int sd_journal_add_match(sd_journal *j, const void *data, size_t size); -int sd_journal_add_disjunction(sd_journal *j); -int sd_journal_add_conjunction(sd_journal *j); -void sd_journal_flush_matches(sd_journal *j); - -int sd_journal_seek_head(sd_journal *j); -int sd_journal_seek_tail(sd_journal *j); -int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec); -int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); -int sd_journal_seek_cursor(sd_journal *j, const char *cursor); - -int sd_journal_get_cursor(sd_journal *j, char **cursor); -int sd_journal_test_cursor(sd_journal *j, const char *cursor); - -int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to); -int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, const sd_id128_t boot_id, uint64_t *from, uint64_t *to); - -int sd_journal_get_usage(sd_journal *j, uint64_t *bytes); - -int sd_journal_query_unique(sd_journal *j, const char *field); -int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l); -void sd_journal_restart_unique(sd_journal *j); - -int sd_journal_enumerate_fields(sd_journal *j, const char **field); -void sd_journal_restart_fields(sd_journal *j); - -int sd_journal_get_fd(sd_journal *j); -int sd_journal_get_events(sd_journal *j); -int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec); -int sd_journal_process(sd_journal *j); -int sd_journal_wait(sd_journal *j, uint64_t timeout_usec); -int sd_journal_reliable_fd(sd_journal *j); - -int sd_journal_get_catalog(sd_journal *j, char **text); -int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text); - -int sd_journal_has_runtime_files(sd_journal *j); -int sd_journal_has_persistent_files(sd_journal *j); - -/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ -#define SD_JOURNAL_FOREACH(j) \ - if (sd_journal_seek_head(j) < 0) { } \ - else while (sd_journal_next(j) > 0) - -/* The inverse condition avoids ambiguity of dangling 'else' after the macro */ -#define SD_JOURNAL_FOREACH_BACKWARDS(j) \ - if (sd_journal_seek_tail(j) < 0) { } \ - else while (sd_journal_previous(j) > 0) - -/* Iterate through the data fields of the current journal entry */ -#define SD_JOURNAL_FOREACH_DATA(j, data, l) \ - for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; ) - -/* Iterate through the all known values of a specific field */ -#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \ - for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; ) - -/* Iterate through all known field names */ -#define SD_JOURNAL_FOREACH_FIELD(j, field) \ - for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 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 deleted file mode 100644 index 3f35eebea3..0000000000 --- a/src/systemd/sd-lldp.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef foosdlldphfoo -#define foosdlldphfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* IEEE 802.3AB Clause 9: TLV Types */ -enum { - SD_LLDP_TYPE_END = 0, - SD_LLDP_TYPE_CHASSIS_ID = 1, - SD_LLDP_TYPE_PORT_ID = 2, - SD_LLDP_TYPE_TTL = 3, - SD_LLDP_TYPE_PORT_DESCRIPTION = 4, - SD_LLDP_TYPE_SYSTEM_NAME = 5, - SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, - SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, - SD_LLDP_TYPE_MGMT_ADDRESS = 8, - SD_LLDP_TYPE_PRIVATE = 127, -}; - -/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ -enum { - SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, - SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, - SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, - SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, - SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, - SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, - SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, - SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, -}; - -/* IEEE 802.3AB Clause 9.5.3: Port subtype */ -enum { - SD_LLDP_PORT_SUBTYPE_RESERVED = 0, - SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, - SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, - SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, - SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, - SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, - SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, - SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, -}; - -enum { - SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, - SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, - SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, - SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, - SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, -}; - -#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) - -#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ - ((uint16_t) \ - (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ - SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) - -#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } -#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } - -enum { - SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, - SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, - SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, - SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, - SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, - SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, - SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, -}; - -typedef struct sd_lldp sd_lldp; -typedef struct sd_lldp_neighbor sd_lldp_neighbor; - -typedef enum sd_lldp_event { - SD_LLDP_EVENT_ADDED = 'a', - SD_LLDP_EVENT_REMOVED = 'r', - SD_LLDP_EVENT_UPDATED = 'u', - SD_LLDP_EVENT_REFRESHED = 'f', -} sd_lldp_event; - -typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); - -int sd_lldp_new(sd_lldp **ret); -sd_lldp* sd_lldp_ref(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); - -int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); -int sd_lldp_detach_event(sd_lldp *lldp); -sd_event *sd_lldp_get_event(sd_lldp *lldp); - -int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); -int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex); - -/* Controls how much and what to store in the neighbors database */ -int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); -int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); -int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); - -int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); - -int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); -sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); -sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); - -/* Access to LLDP frame metadata */ -int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); -int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ -int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); -int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); -int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); - -/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs - * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ -int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); -int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); -int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h deleted file mode 100644 index e3ecbd8378..0000000000 --- a/src/systemd/sd-login.h +++ /dev/null @@ -1,245 +0,0 @@ -#ifndef foosdloginhfoo -#define foosdloginhfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "_sd-common.h" - -/* - * A few points: - * - * Instead of returning an empty string array or empty uid array, we - * may return NULL. - * - * Free the data the library returns with libc free(). String arrays - * are NULL terminated, and you need to free the array itself, in - * addition to the strings contained. - * - * We return error codes as negative errno, kernel-style. On success, we - * return 0 or positive. - * - * These functions access data in /proc, /sys/fs/cgroup, and /run. All - * of these are virtual file systems; therefore, accesses are - * relatively cheap. - * - * See sd-login(3) for more information. - */ - -_SD_BEGIN_DECLARATIONS; - -/* Get session from PID. Note that 'shared' processes of a user are - * not attached to a session, but only attached to a user. This will - * return an error for system processes and 'shared' processes of a - * user. */ -int sd_pid_get_session(pid_t pid, char **session); - -/* Get UID of the owner of the session of the PID (or in case the - * process is a 'shared' user process, the UID of that user is - * returned). This will not return the UID of the process, but rather - * the UID of the owner of the cgroup that the process is in. This will - * return an error for system processes. */ -int sd_pid_get_owner_uid(pid_t pid, uid_t *uid); - -/* Get systemd non-slice unit (i.e. service) name from PID, for system - * services. This will return an error for non-service processes. */ -int sd_pid_get_unit(pid_t pid, char **unit); - -/* Get systemd non-slice unit (i.e. service) name from PID, for user - * services. This will return an error for non-user-service - * processes. */ -int sd_pid_get_user_unit(pid_t pid, char **unit); - -/* Get slice name from PID. */ -int sd_pid_get_slice(pid_t pid, char **slice); - -/* Get user slice name from PID. */ -int sd_pid_get_user_slice(pid_t pid, char **slice); - -/* Get machine name from PID, for processes assigned to a VM or - * container. This will return an error for non-machine processes. */ -int sd_pid_get_machine_name(pid_t pid, char **machine); - -/* Get the control group from a PID, relative to the root of the - * hierarchy. */ -int sd_pid_get_cgroup(pid_t pid, char **cgroup); - -/* Similar to sd_pid_get_session(), but retrieves data about the peer - * of a connected AF_UNIX socket */ -int sd_peer_get_session(int fd, char **session); - -/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of - * a connected AF_UNIX socket */ -int sd_peer_get_owner_uid(int fd, uid_t *uid); - -/* Similar to sd_pid_get_unit(), but retrieves data about the peer of - * a connected AF_UNIX socket */ -int sd_peer_get_unit(int fd, char **unit); - -/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of - * a connected AF_UNIX socket */ -int sd_peer_get_user_unit(int fd, char **unit); - -/* Similar to sd_pid_get_slice(), but retrieves data about the peer of - * a connected AF_UNIX socket */ -int sd_peer_get_slice(int fd, char **slice); - -/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of - * a connected AF_UNIX socket */ -int sd_peer_get_user_slice(int fd, char **slice); - -/* Similar to sd_pid_get_machine_name(), but retrieves data about the - * peer of a connected AF_UNIX socket */ -int sd_peer_get_machine_name(int fd, char **machine); - -/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer - * of a connected AF_UNIX socket. */ -int sd_peer_get_cgroup(pid_t pid, char **cgroup); - -/* Get state from UID. Possible states: offline, lingering, online, active, closing */ -int sd_uid_get_state(uid_t uid, char **state); - -/* Return primary session of user, if there is any */ -int sd_uid_get_display(uid_t uid, char **session); - -/* Return 1 if UID has session on seat. If require_active is true, this will - * look for active sessions only. */ -int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat); - -/* Return sessions of user. If require_active is true, this will look for - * active sessions only. Returns the number of sessions. - * If sessions is NULL, this will just return the number of sessions. */ -int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions); - -/* Return seats of user is on. If require_active is true, this will look for - * active seats only. Returns the number of seats. - * If seats is NULL, this will just return the number of seats. */ -int sd_uid_get_seats(uid_t uid, int require_active, char ***seats); - -/* Return 1 if the session is active. */ -int sd_session_is_active(const char *session); - -/* Return 1 if the session is remote. */ -int sd_session_is_remote(const char *session); - -/* Get state from session. Possible states: online, active, closing. - * This function is a more generic version of sd_session_is_active(). */ -int sd_session_get_state(const char *session, char **state); - -/* Determine user ID of session */ -int sd_session_get_uid(const char *session, uid_t *uid); - -/* Determine seat of session */ -int sd_session_get_seat(const char *session, char **seat); - -/* Determine the (PAM) service name this session was registered by. */ -int sd_session_get_service(const char *session, char **service); - -/* Determine the type of this session, i.e. one of "tty", "x11", "wayland", "mir" or "unspecified". */ -int sd_session_get_type(const char *session, char **type); - -/* Determine the class of this session, i.e. one of "user", "greeter" or "lock-screen". */ -int sd_session_get_class(const char *session, char **clazz); - -/* Determine the desktop brand of this session, i.e. something like "GNOME", "KDE" or "systemd-console". */ -int sd_session_get_desktop(const char *session, char **desktop); - -/* Determine the X11 display of this session. */ -int sd_session_get_display(const char *session, char **display); - -/* Determine the remote host of this session. */ -int sd_session_get_remote_host(const char *session, char **remote_host); - -/* Determine the remote user of this session (if provided by PAM). */ -int sd_session_get_remote_user(const char *session, char **remote_user); - -/* Determine the TTY of this session. */ -int sd_session_get_tty(const char *session, char **display); - -/* Determine the VT number of this session. */ -int sd_session_get_vt(const char *session, unsigned *vtnr); - -/* Return active session and user of seat */ -int sd_seat_get_active(const char *seat, char **session, uid_t *uid); - -/* Return sessions and users on seat. Returns number of sessions. - * If sessions is NULL, this returns only the number of sessions. */ -int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); - -/* Return whether the seat is multi-session capable */ -int sd_seat_can_multi_session(const char *seat); - -/* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */ -int sd_seat_can_tty(const char *seat); - -/* Return whether the seat is graphics capable, i.e. suitable for showing graphical UIs */ -int sd_seat_can_graphical(const char *seat); - -/* Return the class of machine */ -int sd_machine_get_class(const char *machine, char **clazz); - -/* Return the list if host-side network interface indices of a machine */ -int sd_machine_get_ifindices(const char *machine, int **ifindices); - -/* Get all seats, store in *seats. Returns the number of seats. If - * seats is NULL, this only returns the number of seats. */ -int sd_get_seats(char ***seats); - -/* Get all sessions, store in *sessions. Returns the number of - * sessions. If sessions is NULL, this only returns the number of sessions. */ -int sd_get_sessions(char ***sessions); - -/* Get all logged in users, store in *users. Returns the number of - * users. If users is NULL, this only returns the number of users. */ -int sd_get_uids(uid_t **users); - -/* Get all running virtual machines/containers */ -int sd_get_machine_names(char ***machines); - -/* Monitor object */ -typedef struct sd_login_monitor sd_login_monitor; - -/* Create a new monitor. Category must be NULL, "seat", "session", - * "uid", or "machine" to get monitor events for the specific category - * (or all). */ -int sd_login_monitor_new(const char *category, sd_login_monitor** ret); - -/* Destroys the passed monitor. Returns NULL. */ -sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m); - -/* Flushes the monitor */ -int sd_login_monitor_flush(sd_login_monitor *m); - -/* Get FD from monitor */ -int sd_login_monitor_get_fd(sd_login_monitor *m); - -/* Get poll() mask to monitor */ -int sd_login_monitor_get_events(sd_login_monitor *m); - -/* Get timeout for poll(), as usec value relative to CLOCK_MONOTONIC's epoch */ -int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec); - -_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 deleted file mode 100644 index 3c44d63021..0000000000 --- a/src/systemd/sd-messages.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef foosdmessageshfoo -#define foosdmessageshfoo - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-id128.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* Hey! If you add a new message here, you *must* also update the - * message catalog with an appropriate explanation */ - -/* And if you add a new ID here, make sure to generate a random one - * with journalctl --new-id128. Do not use any other IDs, and do not - * count them up manually. */ - -#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) -#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) -#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) -#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) -#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) - -#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) - -#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) -#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) -#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) -#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) -#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) -#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) - -#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) -#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) - -#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) - -#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) -#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) - -#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) - -#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) -#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) -#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) -#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) -#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) -#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) -#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) - -#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) - -#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) - -#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) - -#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) -#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) -#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) -#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) -#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) -#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) -#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) - -#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) - -#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) -#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h deleted file mode 100644 index 9f7d4ef71a..0000000000 --- a/src/systemd/sd-ndisc.h +++ /dev/null @@ -1,130 +0,0 @@ -#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 . -***/ - -#include -#include -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* Neightbor Discovery Options, RFC 4861, Section 4.6 and - * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */ -enum { - SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, - SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2, - SD_NDISC_OPTION_PREFIX_INFORMATION = 3, - SD_NDISC_OPTION_MTU = 5, - SD_NDISC_OPTION_ROUTE_INFORMATION = 24, - SD_NDISC_OPTION_RDNSS = 25, - SD_NDISC_OPTION_FLAGS_EXTENSION = 26, - SD_NDISC_OPTION_DNSSL = 31, - SD_NDISC_OPTION_CAPTIVE_PORTAL = 37, -}; - -/* Route preference, RFC 4191, Section 2.1 */ -enum { - SD_NDISC_PREFERENCE_LOW = 3U, - SD_NDISC_PREFERENCE_MEDIUM = 0U, - SD_NDISC_PREFERENCE_HIGH = 1U, -}; - -typedef struct sd_ndisc sd_ndisc; -typedef struct sd_ndisc_router sd_ndisc_router; - -typedef enum sd_ndisc_event { - SD_NDISC_EVENT_TIMEOUT = 't', - SD_NDISC_EVENT_ROUTER = 'r', -} sd_ndisc_event; - -typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata); - -int sd_ndisc_new(sd_ndisc **ret); -sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); -sd_ndisc *sd_ndisc_unref(sd_ndisc *nd); - -int sd_ndisc_start(sd_ndisc *nd); -int sd_ndisc_stop(sd_ndisc *nd); - -int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority); -int sd_ndisc_detach_event(sd_ndisc *nd); -sd_event *sd_ndisc_get_event(sd_ndisc *nd); - -int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata); -int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index); -int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); - -int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret); -int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret); - -int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size); -sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt); -sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt); - -int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); -int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); - -int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags); -int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret); -int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime); -int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret); - -/* Generic option access */ -int sd_ndisc_router_option_rewind(sd_ndisc_router *rt); -int sd_ndisc_router_option_next(sd_ndisc_router *rt); -int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type); -int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); - -/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */ -int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); - -/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */ -int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); -int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret); - -/* Specific option access: SD_NDISC_OPTION_RDNSS */ -int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret); -int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); - -/* Specific option access: SD_NDISC_OPTION_DNSSL */ -int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret); -int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h deleted file mode 100644 index 7efa8ebe5a..0000000000 --- a/src/systemd/sd-netlink.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef foosdnetlinkhfoo -#define foosdnetlinkhfoo - -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include -#include -#include -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -typedef struct sd_netlink sd_netlink; -typedef struct sd_netlink_message sd_netlink_message; - -/* callback */ - -typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata); - -/* bus */ -int sd_netlink_new_from_netlink(sd_netlink **nl, int fd); -int sd_netlink_open(sd_netlink **nl); -int sd_netlink_open_fd(sd_netlink **nl, int fd); -int sd_netlink_inc_rcvbuf(sd_netlink *nl, const size_t size); - -sd_netlink *sd_netlink_ref(sd_netlink *nl); -sd_netlink *sd_netlink_unref(sd_netlink *nl); - -int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial); -int sd_netlink_call_async(sd_netlink *nl, sd_netlink_message *message, - sd_netlink_message_handler_t callback, - void *userdata, uint64_t usec, uint32_t *serial); -int sd_netlink_call_async_cancel(sd_netlink *nl, uint32_t serial); -int sd_netlink_call(sd_netlink *nl, sd_netlink_message *message, uint64_t timeout, - sd_netlink_message **reply); - -int sd_netlink_get_events(sd_netlink *nl); -int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout); -int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret); -int sd_netlink_wait(sd_netlink *nl, uint64_t timeout); - -int sd_netlink_add_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); -int sd_netlink_remove_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); - -int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); -int sd_netlink_detach_event(sd_netlink *nl); - -int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data); -int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type); -int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data); -int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data); -int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data); -int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len); -int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data); -int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data); -int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data); -int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info); - -int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type); -int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key); -int sd_netlink_message_close_container(sd_netlink_message *m); - -int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data); -int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data); -int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data); -int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data); -int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data); -int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info); -int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data); -int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data); -int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type); -int sd_netlink_message_exit_container(sd_netlink_message *m); - -int sd_netlink_message_rewind(sd_netlink_message *m); - -sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m); - -sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m); -sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m); - -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 */ - -int sd_rtnl_message_new_link(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index); -int sd_rtnl_message_new_addr_update(sd_netlink *nl, sd_netlink_message **ret, int index, int family); -int sd_rtnl_message_new_addr(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int family); -int sd_rtnl_message_new_route(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol); -int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int nda_family); - -int sd_rtnl_message_get_family(sd_netlink_message *m, int *family); - -int sd_rtnl_message_addr_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen); -int sd_rtnl_message_addr_set_scope(sd_netlink_message *m, unsigned char scope); -int sd_rtnl_message_addr_set_flags(sd_netlink_message *m, unsigned char flags); -int sd_rtnl_message_addr_get_family(sd_netlink_message *m, int *family); -int sd_rtnl_message_addr_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen); -int sd_rtnl_message_addr_get_scope(sd_netlink_message *m, unsigned char *scope); -int sd_rtnl_message_addr_get_flags(sd_netlink_message *m, unsigned char *flags); -int sd_rtnl_message_addr_get_ifindex(sd_netlink_message *m, int *ifindex); - -int sd_rtnl_message_link_set_flags(sd_netlink_message *m, unsigned flags, unsigned change); -int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type); -int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family); -int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex); -int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags); -int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *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_set_table(sd_netlink_message *m, unsigned char table); -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_set_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); - -int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags); -int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state); -int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family); -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 deleted file mode 100644 index 0f13e2bae7..0000000000 --- a/src/systemd/sd-network.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef foosdnetworkhfoo -#define foosdnetworkhfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - 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 . -***/ - -#include -#include - -#include "_sd-common.h" - -/* - * A few points: - * - * Instead of returning an empty string array or empty integer array, we - * may return NULL. - * - * Free the data the library returns with libc free(). String arrays - * are NULL terminated, and you need to free the array itself in - * addition to the strings contained. - * - * We return error codes as negative errno, kernel-style. On success, we - * return 0 or positive. - * - * These functions access data in /run. This is a virtual file system; - * therefore, accesses are relatively cheap. - * - * See sd-network(3) for more information. - */ - -_SD_BEGIN_DECLARATIONS; - -/* Get overall operational state - * Possible states: down, up, dormant, carrier, degraded, routable - * Possible return codes: - * -ENODATA: networkd is not aware of any links - */ -int sd_network_get_operational_state(char **state); - -/* Get DNS entries for all links. These are string representations of - * IP addresses */ -int sd_network_get_dns(char ***dns); - -/* Get NTP entries for all links. These are domain names or string - * representations of IP addresses */ -int sd_network_get_ntp(char ***ntp); - -/* Get the search domains for all links. */ -int sd_network_get_search_domains(char ***domains); - -/* Get the search domains for all links. */ -int sd_network_get_route_domains(char ***domains); - -/* Get setup state from ifindex. - * Possible states: - * pending: udev is still processing the link, we don't yet know if we will manage it - * failed: networkd failed to manage the link - * configuring: in the process of retrieving configuration or configuring the link - * configured: link configured successfully - * unmanaged: networkd is not handling the link - * linger: the link is gone, but has not yet been dropped by networkd - * Possible return codes: - * -ENODATA: networkd is not aware of the link - */ -int sd_network_link_get_setup_state(int ifindex, char **state); - -/* Get operational state from ifindex. - * Possible states: - * off: the device is powered down - * no-carrier: the device is powered up, but it does not yet have a carrier - * dormant: the device has a carrier, but is not yet ready for normal traffic - * carrier: the link has a carrier - * degraded: the link has carrier and addresses valid on the local link configured - * routable: the link has carrier and routable address configured - * Possible return codes: - * -ENODATA: networkd is not aware of the link - */ -int sd_network_link_get_operational_state(int ifindex, char **state); - -/* Get path to .network file applied to link */ -int sd_network_link_get_network_file(int ifindex, char **filename); - -/* Get DNS entries for a given link. These are string representations of - * IP addresses */ -int sd_network_link_get_dns(int ifindex, char ***ret); - -/* Get NTP entries for a given link. These are domain names or string - * representations of IP addresses */ -int sd_network_link_get_ntp(int ifindex, char ***ret); - -/* Indicates whether or not LLMNR 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_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); - -/* Get the search DNS domain names for a given link. */ -int sd_network_link_get_search_domains(int ifindex, char ***domains); - -/* Get the route DNS domain names for a given link. */ -int sd_network_link_get_route_domains(int ifindex, char ***domains); - -/* Get the carrier interface indexes to which current link is bound to. */ -int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); - -/* Get the CARRIERS that are bound to current link. */ -int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); - -/* Get the timezone that was learnt on a specific link. */ -int sd_network_link_get_timezone(int ifindex, char **timezone); - -/* Monitor object */ -typedef struct sd_network_monitor sd_network_monitor; - -/* Create a new monitor. Category must be NULL, "links" or "leases". */ -int sd_network_monitor_new(sd_network_monitor **ret, const char *category); - -/* Destroys the passed monitor. Returns NULL. */ -sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m); - -/* Flushes the monitor */ -int sd_network_monitor_flush(sd_network_monitor *m); - -/* Get FD from monitor */ -int sd_network_monitor_get_fd(sd_network_monitor *m); - -/* Get poll() mask to monitor */ -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 deleted file mode 100644 index be6abdcd03..0000000000 --- a/src/systemd/sd-path.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef foosdpathhfoo -#define foosdpathhfoo - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -enum { - /* Temporary files */ - SD_PATH_TEMPORARY = 0x0ULL, - SD_PATH_TEMPORARY_LARGE, - - /* Vendor supplied data */ - SD_PATH_SYSTEM_BINARIES, - SD_PATH_SYSTEM_INCLUDE, - SD_PATH_SYSTEM_LIBRARY_PRIVATE, - SD_PATH_SYSTEM_LIBRARY_ARCH, - SD_PATH_SYSTEM_SHARED, - SD_PATH_SYSTEM_CONFIGURATION_FACTORY, - SD_PATH_SYSTEM_STATE_FACTORY, - - /* System configuration, runtime, state, ... */ - SD_PATH_SYSTEM_CONFIGURATION, - SD_PATH_SYSTEM_RUNTIME, - SD_PATH_SYSTEM_RUNTIME_LOGS, - SD_PATH_SYSTEM_STATE_PRIVATE, - SD_PATH_SYSTEM_STATE_LOGS, - SD_PATH_SYSTEM_STATE_CACHE, - SD_PATH_SYSTEM_STATE_SPOOL, - - /* Vendor supplied data */ - SD_PATH_USER_BINARIES, - SD_PATH_USER_LIBRARY_PRIVATE, - SD_PATH_USER_LIBRARY_ARCH, - SD_PATH_USER_SHARED, - - /* User configuration, state, runtime ... */ - SD_PATH_USER_CONFIGURATION, /* takes both actual configuration (like /etc) and state (like /var/lib) */ - SD_PATH_USER_RUNTIME, - SD_PATH_USER_STATE_CACHE, - - /* User resources */ - SD_PATH_USER, /* $HOME itself */ - SD_PATH_USER_DOCUMENTS, - SD_PATH_USER_MUSIC, - SD_PATH_USER_PICTURES, - SD_PATH_USER_VIDEOS, - SD_PATH_USER_DOWNLOAD, - SD_PATH_USER_PUBLIC, - SD_PATH_USER_TEMPLATES, - SD_PATH_USER_DESKTOP, - - /* Search paths */ - SD_PATH_SEARCH_BINARIES, - SD_PATH_SEARCH_LIBRARY_PRIVATE, - SD_PATH_SEARCH_LIBRARY_ARCH, - SD_PATH_SEARCH_SHARED, - SD_PATH_SEARCH_CONFIGURATION_FACTORY, - SD_PATH_SEARCH_STATE_FACTORY, - SD_PATH_SEARCH_CONFIGURATION, - - _SD_PATH_MAX, -}; - -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-resolve.h b/src/systemd/sd-resolve.h deleted file mode 100644 index 1c792dab39..0000000000 --- a/src/systemd/sd-resolve.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef foosdresolvehfoo -#define foosdresolvehfoo - -/*** - This file is part of systemd. - - Copyright 2005-2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-event.h" - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -/* An opaque sd-resolve session structure */ -typedef struct sd_resolve sd_resolve; - -/* An opaque sd-resolve query structure */ -typedef struct sd_resolve_query sd_resolve_query; - -/* A callback on completion */ -typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); -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 = UINT64_C(1), - SD_RESOLVE_GET_SERVICE = UINT64_C(2), - SD_RESOLVE_GET_BOTH = UINT64_C(3), -}; - -int sd_resolve_default(sd_resolve **ret); - -/* Allocate a new sd-resolve session. */ -int sd_resolve_new(sd_resolve **ret); - -/* Free a sd-resolve session. This destroys all attached - * sd_resolve_query objects automatically. */ -sd_resolve* sd_resolve_unref(sd_resolve *resolve); -sd_resolve* sd_resolve_ref(sd_resolve *resolve); - -/* Return the UNIX file descriptor to poll() for events on. Use this - * function to integrate sd-resolve with your custom main loop. */ -int sd_resolve_get_fd(sd_resolve *resolve); - -/* Return the poll() events (a combination of flags like POLLIN, - * POLLOUT, ...) to check for. */ -int sd_resolve_get_events(sd_resolve *resolve); - -/* Return the poll() timeout to pass. Returns (uint64_t) -1 as - * timeout if no timeout is needed. */ -int sd_resolve_get_timeout(sd_resolve *resolve, uint64_t *timeout_usec); - -/* Process pending responses. After this function is called, you can - * get the next completed query object(s) using - * sd_resolve_get_next(). */ -int sd_resolve_process(sd_resolve *resolve); - -/* Wait for a resolve event to complete. */ -int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec); - -int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid); - -int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int64_t priority); -int sd_resolve_detach_event(sd_resolve *resolve); -sd_event *sd_resolve_get_event(sd_resolve *resolve); - -/* Issue a name-to-address query on the specified session. The - * arguments are compatible with those of libc's - * getaddrinfo(3). The function returns a new query object. When the - * query is completed, you may retrieve the results using - * sd_resolve_getaddrinfo_done(). */ -int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char *node, const char *service, const struct addrinfo *hints, sd_resolve_getaddrinfo_handler_t callback, void *userdata); - -/* Issue an address-to-name query on the specified session. The - * arguments are compatible with those of libc's - * getnameinfo(3). The function returns a new query object. When the - * query is completed, you may retrieve the results using - * sd_resolve_getnameinfo_done(). Set gethost (resp. getserv) to non-zero - * if you want to query the hostname (resp. the service name). */ -int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata); - -sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q); -sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q); - -/* Returns non-zero when the query operation specified by q has been completed. */ -int sd_resolve_query_is_done(sd_resolve_query*q); - -void *sd_resolve_query_get_userdata(sd_resolve_query *q); -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/systemd/sd-utf8.h b/src/systemd/sd-utf8.h deleted file mode 100644 index 6781983878..0000000000 --- a/src/systemd/sd-utf8.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef foosdutf8hfoo -#define foosdutf8hfoo - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "_sd-common.h" - -_SD_BEGIN_DECLARATIONS; - -_sd_pure_ const char *sd_utf8_is_valid(const char *s); -_sd_pure_ const char *sd_ascii_is_valid(const char *s); - -_SD_END_DECLARATIONS; - -#endif diff --git a/src/sysusers/Makefile b/src/sysusers/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/sysusers/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c deleted file mode 100644 index 787d68a009..0000000000 --- a/src/sysusers/sysusers.c +++ /dev/null @@ -1,1919 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "copy.h" -#include "def.h" -#include "fd-util.h" -#include "fileio-label.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', - ADD_GROUP = 'g', - ADD_MEMBER = 'm', - ADD_RANGE = 'r', -} ItemType; -typedef struct Item { - ItemType type; - - char *name; - char *uid_path; - char *gid_path; - char *description; - char *home; - - gid_t gid; - uid_t uid; - - bool gid_set:1; - bool uid_set:1; - - bool todo_user:1; - bool todo_group:1; -} Item; - -static char *arg_root = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); - -static Hashmap *users = NULL, *groups = NULL; -static Hashmap *todo_uids = NULL, *todo_gids = NULL; -static Hashmap *members = NULL; - -static Hashmap *database_uid = NULL, *database_user = NULL; -static Hashmap *database_gid = NULL, *database_group = NULL; - -static uid_t search_uid = UID_INVALID; -static UidRange *uid_range = NULL; -static unsigned n_uid_range = 0; - -static int load_user_database(void) { - _cleanup_fclose_ FILE *f = NULL; - const char *passwd_path; - struct passwd *pw; - int r; - - passwd_path = prefix_roota(arg_root, "/etc/passwd"); - f = fopen(passwd_path, "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - r = hashmap_ensure_allocated(&database_user, &string_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&database_uid, NULL); - if (r < 0) - return r; - - errno = 0; - while ((pw = fgetpwent(f))) { - char *n; - int k, q; - - n = strdup(pw->pw_name); - if (!n) - return -ENOMEM; - - k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid)); - if (k < 0 && k != -EEXIST) { - free(n); - return k; - } - - q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n); - if (q < 0 && q != -EEXIST) { - if (k < 0) - free(n); - return q; - } - - if (q < 0 && k < 0) - free(n); - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - return 0; -} - -static int load_group_database(void) { - _cleanup_fclose_ FILE *f = NULL; - const char *group_path; - struct group *gr; - int r; - - group_path = prefix_roota(arg_root, "/etc/group"); - f = fopen(group_path, "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - r = hashmap_ensure_allocated(&database_group, &string_hash_ops); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&database_gid, NULL); - if (r < 0) - return r; - - errno = 0; - while ((gr = fgetgrent(f))) { - char *n; - int k, q; - - n = strdup(gr->gr_name); - if (!n) - return -ENOMEM; - - k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid)); - if (k < 0 && k != -EEXIST) { - free(n); - return k; - } - - q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n); - if (q < 0 && q != -EEXIST) { - if (k < 0) - free(n); - return q; - } - - if (q < 0 && k < 0) - free(n); - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - return 0; -} - -static int make_backup(const char *target, const char *x) { - _cleanup_close_ int src = -1; - _cleanup_fclose_ FILE *dst = NULL; - char *backup, *temp; - struct timespec ts[2]; - struct stat st; - int r; - - src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (src < 0) { - if (errno == ENOENT) /* No backup necessary... */ - return 0; - - return -errno; - } - - if (fstat(src, &st) < 0) - return -errno; - - r = fopen_temporary_label(target, x, &dst, &temp); - if (r < 0) - return r; - - r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); - if (r < 0) - goto fail; - - /* Don't fail on chmod() or chown(). If it stays owned by us - * and/or unreadable by others, then it isn't too bad... */ - - backup = strjoina(x, "-"); - - /* Copy over the access mask */ - if (fchmod(fileno(dst), st.st_mode & 07777) < 0) - log_warning_errno(errno, "Failed to change mode on %s: %m", backup); - - if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0) - log_warning_errno(errno, "Failed to change ownership of %s: %m", backup); - - ts[0] = st.st_atim; - ts[1] = st.st_mtim; - if (futimens(fileno(dst), ts) < 0) - log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup); - - if (rename(temp, backup) < 0) - goto fail; - - return 0; - -fail: - unlink(temp); - return r; -} - -static int putgrent_with_members(const struct group *gr, FILE *group) { - char **a; - - assert(gr); - assert(group); - - a = hashmap_get(members, gr->gr_name); - if (a) { - _cleanup_strv_free_ char **l = NULL; - bool added = false; - char **i; - - l = strv_copy(gr->gr_mem); - if (!l) - return -ENOMEM; - - STRV_FOREACH(i, a) { - if (strv_find(l, *i)) - continue; - - if (strv_extend(&l, *i) < 0) - return -ENOMEM; - - added = true; - } - - if (added) { - struct group t; - - strv_uniq(l); - strv_sort(l); - - t = *gr; - t.gr_mem = l; - - errno = 0; - if (putgrent(&t, group) != 0) - return errno > 0 ? -errno : -EIO; - - return 1; - } - } - - errno = 0; - if (putgrent(gr, group) != 0) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { - char **a; - - assert(sg); - assert(gshadow); - - a = hashmap_get(members, sg->sg_namp); - if (a) { - _cleanup_strv_free_ char **l = NULL; - bool added = false; - char **i; - - l = strv_copy(sg->sg_mem); - if (!l) - return -ENOMEM; - - STRV_FOREACH(i, a) { - if (strv_find(l, *i)) - continue; - - if (strv_extend(&l, *i) < 0) - return -ENOMEM; - - added = true; - } - - if (added) { - struct sgrp t; - - strv_uniq(l); - strv_sort(l); - - t = *sg; - t.sg_mem = l; - - errno = 0; - if (putsgent(&t, gshadow) != 0) - return errno > 0 ? -errno : -EIO; - - return 1; - } - } - - errno = 0; - if (putsgent(sg, gshadow) != 0) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -static int sync_rights(FILE *from, FILE *to) { - struct stat st; - - if (fstat(fileno(from), &st) < 0) - return -errno; - - if (fchmod(fileno(to), st.st_mode & 07777) < 0) - return -errno; - - if (fchown(fileno(to), st.st_uid, st.st_gid) < 0) - return -errno; - - return 0; -} - -static int rename_and_apply_smack(const char *temp_path, const char *dest_path) { - int r = 0; - if (rename(temp_path, dest_path) < 0) - return -errno; - -#ifdef SMACK_RUN_LABEL - r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} - -static int write_files(void) { - - _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; - _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; - const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; - bool group_changed = false; - Iterator iterator; - Item *i; - int r; - - if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) { - _cleanup_fclose_ FILE *original = NULL; - - /* First we update the actual group list file */ - group_path = prefix_roota(arg_root, "/etc/group"); - r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); - if (r < 0) - goto finish; - - original = fopen(group_path, "re"); - if (original) { - struct group *gr; - - r = sync_rights(original, group); - if (r < 0) - goto finish; - - errno = 0; - while ((gr = fgetgrent(original))) { - /* Safety checks against name and GID - * collisions. Normally, this should - * be unnecessary, but given that we - * look at the entries anyway here, - * let's make an extra verification - * step that we don't generate - * duplicate entries. */ - - i = hashmap_get(groups, gr->gr_name); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); - r = -EEXIST; - goto finish; - } - - if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { - log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); - r = -EEXIST; - goto finish; - } - - r = putgrent_with_members(gr, group); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(group), 0644) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct group n = { - .gr_name = i->name, - .gr_gid = i->gid, - .gr_passwd = (char*) "x", - }; - - r = putgrent_with_members(&n, group); - if (r < 0) - goto finish; - - group_changed = true; - } - - r = fflush_and_check(group); - if (r < 0) - goto finish; - - if (original) { - fclose(original); - original = NULL; - } - - /* OK, now also update the shadow file for the group list */ - gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); - r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); - if (r < 0) - goto finish; - - original = fopen(gshadow_path, "re"); - if (original) { - struct sgrp *sg; - - r = sync_rights(original, gshadow); - if (r < 0) - goto finish; - - errno = 0; - while ((sg = fgetsgent(original))) { - - i = hashmap_get(groups, sg->sg_namp); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); - r = -EEXIST; - goto finish; - } - - r = putsgent_with_members(sg, gshadow); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(gshadow), 0000) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct sgrp n = { - .sg_namp = i->name, - .sg_passwd = (char*) "!!", - }; - - r = putsgent_with_members(&n, gshadow); - if (r < 0) - goto finish; - - group_changed = true; - } - - r = fflush_and_check(gshadow); - if (r < 0) - goto finish; - } - - if (hashmap_size(todo_uids) > 0) { - _cleanup_fclose_ FILE *original = NULL; - long lstchg; - - /* First we update the user database itself */ - passwd_path = prefix_roota(arg_root, "/etc/passwd"); - r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); - if (r < 0) - goto finish; - - original = fopen(passwd_path, "re"); - if (original) { - struct passwd *pw; - - r = sync_rights(original, passwd); - if (r < 0) - goto finish; - - errno = 0; - while ((pw = fgetpwent(original))) { - - i = hashmap_get(users, pw->pw_name); - if (i && i->todo_user) { - log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); - r = -EEXIST; - goto finish; - } - - if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { - log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); - r = -EEXIST; - goto finish; - } - - errno = 0; - if (putpwent(pw, passwd) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(passwd), 0644) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct passwd n = { - .pw_name = i->name, - .pw_uid = i->uid, - .pw_gid = i->gid, - .pw_gecos = i->description, - - /* "x" means the password is stored in - * the shadow file */ - .pw_passwd = (char*) "x", - - /* We default to the root directory as home */ - .pw_dir = i->home ? i->home : (char*) "/", - - /* Initialize the shell to nologin, - * with one exception: for root we - * patch in something special */ - .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", - }; - - errno = 0; - if (putpwent(&n, passwd) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } - } - - r = fflush_and_check(passwd); - if (r < 0) - goto finish; - - if (original) { - fclose(original); - original = NULL; - } - - /* The we update the shadow database */ - shadow_path = prefix_roota(arg_root, "/etc/shadow"); - r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); - if (r < 0) - goto finish; - - lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - - original = fopen(shadow_path, "re"); - if (original) { - struct spwd *sp; - - r = sync_rights(original, shadow); - if (r < 0) - goto finish; - - errno = 0; - while ((sp = fgetspent(original))) { - - i = hashmap_get(users, sp->sp_namp); - if (i && i->todo_user) { - /* we will update the existing entry */ - sp->sp_lstchg = lstchg; - - /* only the /etc/shadow stage is left, so we can - * safely remove the item from the todo set */ - i->todo_user = false; - hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); - } - - errno = 0; - if (putspent(sp, shadow) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } - - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(shadow), 0000) < 0) { - r = -errno; - goto finish; - } - - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct spwd n = { - .sp_namp = i->name, - .sp_pwdp = (char*) "!!", - .sp_lstchg = lstchg, - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; - - errno = 0; - if (putspent(&n, shadow) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } - } - - r = fflush_and_check(shadow); - if (r < 0) - goto finish; - } - - /* Make a backup of the old files */ - if (group_changed) { - if (group) { - r = make_backup("/etc/group", group_path); - if (r < 0) - goto finish; - } - if (gshadow) { - r = make_backup("/etc/gshadow", gshadow_path); - if (r < 0) - goto finish; - } - } - - if (passwd) { - r = make_backup("/etc/passwd", passwd_path); - if (r < 0) - goto finish; - } - if (shadow) { - r = make_backup("/etc/shadow", shadow_path); - if (r < 0) - goto finish; - } - - /* And make the new files count */ - if (group_changed) { - if (group) { - r = rename_and_apply_smack(group_tmp, group_path); - if (r < 0) - goto finish; - - group_tmp = mfree(group_tmp); - } - if (gshadow) { - r = rename_and_apply_smack(gshadow_tmp, gshadow_path); - if (r < 0) - goto finish; - - gshadow_tmp = mfree(gshadow_tmp); - } - } - - if (passwd) { - r = rename_and_apply_smack(passwd_tmp, passwd_path); - if (r < 0) - goto finish; - - passwd_tmp = mfree(passwd_tmp); - } - if (shadow) { - r = rename_and_apply_smack(shadow_tmp, shadow_path); - if (r < 0) - goto finish; - - shadow_tmp = mfree(shadow_tmp); - } - - r = 0; - -finish: - if (passwd_tmp) - unlink(passwd_tmp); - if (shadow_tmp) - unlink(shadow_tmp); - if (group_tmp) - unlink(group_tmp); - if (gshadow_tmp) - unlink(gshadow_tmp); - - return r; -} - -static int uid_is_ok(uid_t uid, const char *name) { - struct passwd *p; - struct group *g; - const char *n; - Item *i; - - /* Let's see if we already have assigned the UID a second time */ - if (hashmap_get(todo_uids, UID_TO_PTR(uid))) - return 0; - - /* Try to avoid using uids that are already used by a group - * that doesn't have the same name as our new user. */ - i = hashmap_get(todo_gids, GID_TO_PTR(uid)); - if (i && !streq(i->name, name)) - return 0; - - /* Let's check the files directly */ - if (hashmap_contains(database_uid, UID_TO_PTR(uid))) - return 0; - - n = hashmap_get(database_gid, GID_TO_PTR(uid)); - if (n && !streq(n, name)) - return 0; - - /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */ - if (!arg_root) { - errno = 0; - p = getpwuid(uid); - if (p) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - errno = 0; - g = getgrgid((gid_t) uid); - if (g) { - if (!streq(g->gr_name, name)) - return 0; - } else if (!IN_SET(errno, 0, ENOENT)) - return -errno; - } - - return 1; -} - -static int root_stat(const char *p, struct stat *st) { - const char *fix; - - fix = prefix_roota(arg_root, p); - if (stat(fix, st) < 0) - return -errno; - - return 0; -} - -static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) { - struct stat st; - bool found_uid = false, found_gid = false; - uid_t uid = 0; - gid_t gid = 0; - - assert(i); - - /* First, try to get the gid directly */ - if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) { - gid = st.st_gid; - found_gid = true; - } - - /* Then, try to get the uid directly */ - if ((_uid || (_gid && !found_gid)) - && i->uid_path - && root_stat(i->uid_path, &st) >= 0) { - - uid = st.st_uid; - found_uid = true; - - /* If we need the gid, but had no success yet, also derive it from the uid path */ - if (_gid && !found_gid) { - gid = st.st_gid; - found_gid = true; - } - } - - /* If that didn't work yet, then let's reuse the gid as uid */ - if (_uid && !found_uid && i->gid_path) { - - if (found_gid) { - uid = (uid_t) gid; - found_uid = true; - } else if (root_stat(i->gid_path, &st) >= 0) { - uid = (uid_t) st.st_gid; - found_uid = true; - } - } - - if (_uid) { - if (!found_uid) - return 0; - - *_uid = uid; - } - - if (_gid) { - if (!found_gid) - return 0; - - *_gid = gid; - } - - return 1; -} - -static int add_user(Item *i) { - void *z; - int r; - - assert(i); - - /* Check the database directly */ - z = hashmap_get(database_user, i->name); - if (z) { - log_debug("User %s already exists.", i->name); - i->uid = PTR_TO_UID(z); - i->uid_set = true; - return 0; - } - - if (!arg_root) { - struct passwd *p; - - /* Also check NSS */ - errno = 0; - p = getpwnam(i->name); - if (p) { - log_debug("User %s already exists.", i->name); - i->uid = p->pw_uid; - i->uid_set = true; - - r = free_and_strdup(&i->description, p->pw_gecos); - if (r < 0) - return log_oom(); - - return 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name); - } - - /* Try to use the suggested numeric uid */ - if (i->uid_set) { - r = uid_is_ok(i->uid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - if (r == 0) { - log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name); - i->uid_set = false; - } - } - - /* If that didn't work, try to read it from the specified path */ - if (!i->uid_set) { - uid_t c; - - if (read_id_from_file(i, &c, NULL) > 0) { - - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) - log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); - else { - r = uid_is_ok(c, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - else if (r > 0) { - i->uid = c; - i->uid_set = true; - } else - log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name); - } - } - } - - /* Otherwise, try to reuse the group ID */ - if (!i->uid_set && i->gid_set) { - r = uid_is_ok((uid_t) i->gid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - if (r > 0) { - i->uid = (uid_t) i->gid; - i->uid_set = true; - } - } - - /* And if that didn't work either, let's try to find a free one */ - if (!i->uid_set) { - for (;;) { - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free user ID available for %s.", i->name); - return r; - } - - r = uid_is_ok(search_uid, i->name); - if (r < 0) - return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); - else if (r > 0) - break; - } - - i->uid_set = true; - i->uid = search_uid; - } - - r = hashmap_ensure_allocated(&todo_uids, NULL); - if (r < 0) - return log_oom(); - - r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); - if (r < 0) - return log_oom(); - - i->todo_user = true; - log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid); - - return 0; -} - -static int gid_is_ok(gid_t gid) { - struct group *g; - struct passwd *p; - - if (hashmap_get(todo_gids, GID_TO_PTR(gid))) - return 0; - - /* Avoid reusing gids that are already used by a different user */ - if (hashmap_get(todo_uids, UID_TO_PTR(gid))) - return 0; - - if (hashmap_contains(database_gid, GID_TO_PTR(gid))) - return 0; - - if (hashmap_contains(database_uid, UID_TO_PTR(gid))) - return 0; - - if (!arg_root) { - errno = 0; - g = getgrgid(gid); - if (g) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - - errno = 0; - p = getpwuid((uid_t) gid); - if (p) - return 0; - if (!IN_SET(errno, 0, ENOENT)) - return -errno; - } - - return 1; -} - -static int add_group(Item *i) { - void *z; - int r; - - assert(i); - - /* Check the database directly */ - z = hashmap_get(database_group, i->name); - if (z) { - log_debug("Group %s already exists.", i->name); - i->gid = PTR_TO_GID(z); - i->gid_set = true; - return 0; - } - - /* Also check NSS */ - if (!arg_root) { - struct group *g; - - errno = 0; - g = getgrnam(i->name); - if (g) { - log_debug("Group %s already exists.", i->name); - i->gid = g->gr_gid; - i->gid_set = true; - return 0; - } - if (!IN_SET(errno, 0, ENOENT)) - return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); - } - - /* Try to use the suggested numeric gid */ - if (i->gid_set) { - r = gid_is_ok(i->gid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - if (r == 0) { - log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); - i->gid_set = false; - } - } - - /* Try to reuse the numeric uid, if there's one */ - if (!i->gid_set && i->uid_set) { - r = gid_is_ok((gid_t) i->uid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - if (r > 0) { - i->gid = (gid_t) i->uid; - i->gid_set = true; - } - } - - /* If that didn't work, try to read it from the specified path */ - if (!i->gid_set) { - gid_t c; - - if (read_id_from_file(i, NULL, &c) > 0) { - - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) - log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); - else { - r = gid_is_ok(c); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - else if (r > 0) { - i->gid = c; - i->gid_set = true; - } else - log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name); - } - } - } - - /* And if that didn't work either, let's try to find a free one */ - if (!i->gid_set) { - for (;;) { - /* We look for new GIDs in the UID pool! */ - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free group ID available for %s.", i->name); - return r; - } - - r = gid_is_ok(search_uid); - if (r < 0) - return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); - else if (r > 0) - break; - } - - i->gid_set = true; - i->gid = search_uid; - } - - r = hashmap_ensure_allocated(&todo_gids, NULL); - if (r < 0) - return log_oom(); - - r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); - if (r < 0) - return log_oom(); - - i->todo_group = true; - log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid); - - return 0; -} - -static int process_item(Item *i) { - int r; - - assert(i); - - switch (i->type) { - - case ADD_USER: - r = add_group(i); - if (r < 0) - return r; - - return add_user(i); - - case ADD_GROUP: { - Item *j; - - j = hashmap_get(users, i->name); - if (j) { - /* There's already user to be created for this - * name, let's process that in one step */ - - if (i->gid_set) { - j->gid = i->gid; - j->gid_set = true; - } - - if (i->gid_path) { - r = free_and_strdup(&j->gid_path, i->gid_path); - if (r < 0) - return log_oom(); - } - - return 0; - } - - return add_group(i); - } - - default: - assert_not_reached("Unknown item type"); - } -} - -static void item_free(Item *i) { - - if (!i) - return; - - free(i->name); - free(i->uid_path); - free(i->gid_path); - free(i->description); - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free); - -static int add_implicit(void) { - char *g, **l; - Iterator iterator; - int r; - - /* Implicitly create additional users and groups, if they were listed in "m" lines */ - - HASHMAP_FOREACH_KEY(l, g, members, iterator) { - Item *i; - char **m; - - i = hashmap_get(groups, g); - if (!i) { - _cleanup_(item_freep) Item *j = NULL; - - r = hashmap_ensure_allocated(&groups, &string_hash_ops); - if (r < 0) - return log_oom(); - - j = new0(Item, 1); - if (!j) - return log_oom(); - - j->type = ADD_GROUP; - j->name = strdup(g); - if (!j->name) - return log_oom(); - - r = hashmap_put(groups, j->name, j); - if (r < 0) - return log_oom(); - - log_debug("Adding implicit group '%s' due to m line", j->name); - j = NULL; - } - - STRV_FOREACH(m, l) { - - i = hashmap_get(users, *m); - if (!i) { - _cleanup_(item_freep) Item *j = NULL; - - r = hashmap_ensure_allocated(&users, &string_hash_ops); - if (r < 0) - return log_oom(); - - j = new0(Item, 1); - if (!j) - return log_oom(); - - j->type = ADD_USER; - j->name = strdup(*m); - if (!j->name) - return log_oom(); - - r = hashmap_put(users, j->name, j); - if (r < 0) - return log_oom(); - - log_debug("Adding implicit user '%s' due to m line", j->name); - j = NULL; - } - } - } - - return 0; -} - -static bool item_equal(Item *a, Item *b) { - assert(a); - assert(b); - - if (a->type != b->type) - return false; - - if (!streq_ptr(a->name, b->name)) - return false; - - if (!streq_ptr(a->uid_path, b->uid_path)) - return false; - - if (!streq_ptr(a->gid_path, b->gid_path)) - return false; - - if (!streq_ptr(a->description, b->description)) - return false; - - if (a->uid_set != b->uid_set) - return false; - - if (a->uid_set && a->uid != b->uid) - return false; - - if (a->gid_set != b->gid_set) - return false; - - if (a->gid_set && a->gid != b->gid) - return false; - - if (!streq_ptr(a->home, b->home)) - return false; - - return true; -} - -static bool valid_user_group_name(const char *u) { - const char *i; - long sz; - - if (isempty(u)) - return false; - - if (!(u[0] >= 'a' && u[0] <= 'z') && - !(u[0] >= 'A' && u[0] <= 'Z') && - u[0] != '_') - return false; - - for (i = u+1; *i; i++) { - if (!(*i >= 'a' && *i <= 'z') && - !(*i >= 'A' && *i <= 'Z') && - !(*i >= '0' && *i <= '9') && - *i != '_' && - *i != '-') - return false; - } - - sz = sysconf(_SC_LOGIN_NAME_MAX); - assert_se(sz > 0); - - if ((size_t) (i-u) > (size_t) sz) - return false; - - if ((size_t) (i-u) > UT_NAMESIZE - 1) - return false; - - return true; -} - -static bool valid_gecos(const char *d) { - - if (!d) - return false; - - if (!utf8_is_valid(d)) - return false; - - if (string_has_cc(d, NULL)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(d, ':')) - return false; - - return true; -} - -static bool valid_home(const char *p) { - - if (isempty(p)) - return false; - - if (!utf8_is_valid(p)) - return false; - - if (string_has_cc(p, NULL)) - return false; - - if (!path_is_absolute(p)) - return false; - - if (!path_is_safe(p)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(p, ':')) - return false; - - return true; -} - -static int parse_line(const char *fname, unsigned line, const char *buffer) { - - static const Specifier specifier_table[] = { - { 'm', specifier_machine_id, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} - }; - - _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL; - _cleanup_(item_freep) Item *i = NULL; - Item *existing; - Hashmap *h; - int r; - const char *p; - - assert(fname); - assert(line >= 1); - assert(buffer); - - /* Parse columns */ - p = buffer; - r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL); - if (r < 0) { - log_error("[%s:%u] Syntax error.", fname, line); - return r; - } - if (r < 2) { - log_error("[%s:%u] Missing action and name columns.", fname, line); - return -EINVAL; - } - if (!isempty(p)) { - log_error("[%s:%u] Trailing garbage.", fname, line); - return -EINVAL; - } - - /* Verify action */ - if (strlen(action) != 1) { - log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); - return -EINVAL; - } - - if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { - log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]); - return -EBADMSG; - } - - /* Verify name */ - if (isempty(name) || streq(name, "-")) - name = mfree(name); - - if (name) { - r = specifier_printf(name, specifier_table, NULL, &resolved_name); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } - - if (!valid_user_group_name(resolved_name)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); - return -EINVAL; - } - } - - /* Verify id */ - if (isempty(id) || streq(id, "-")) - id = mfree(id); - - if (id) { - r = specifier_printf(id, specifier_table, NULL, &resolved_id); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } - } - - /* Verify description */ - if (isempty(description) || streq(description, "-")) - description = mfree(description); - - if (description) { - if (!valid_gecos(description)) { - log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description); - return -EINVAL; - } - } - - /* Verify home */ - if (isempty(home) || streq(home, "-")) - home = mfree(home); - - if (home) { - if (!valid_home(home)) { - log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home); - return -EINVAL; - } - } - - switch (action[0]) { - - case ADD_RANGE: - if (resolved_name) { - log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); - if (r < 0) { - log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); - return -EINVAL; - } - - return 0; - - case ADD_MEMBER: { - char **l; - - /* Try to extend an existing member or group item */ - if (!name) { - log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); - return -EINVAL; - } - - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); - return -EINVAL; - } - - if (!valid_user_group_name(resolved_id)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&members, &string_hash_ops); - if (r < 0) - return log_oom(); - - l = hashmap_get(members, resolved_id); - if (l) { - /* A list for this group name already exists, let's append to it */ - r = strv_push(&l, resolved_name); - if (r < 0) - return log_oom(); - - resolved_name = NULL; - - assert_se(hashmap_update(members, resolved_id, l) >= 0); - } else { - /* No list for this group name exists yet, create one */ - - l = new0(char *, 2); - if (!l) - return -ENOMEM; - - l[0] = resolved_name; - l[1] = NULL; - - r = hashmap_put(members, resolved_id, l); - if (r < 0) { - free(l); - return log_oom(); - } - - resolved_id = resolved_name = NULL; - } - - return 0; - } - - case ADD_USER: - if (!name) { - log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&users, &string_hash_ops); - if (r < 0) - return log_oom(); - - i = new0(Item, 1); - if (!i) - return log_oom(); - - if (resolved_id) { - if (path_is_absolute(resolved_id)) { - i->uid_path = resolved_id; - resolved_id = NULL; - - path_kill_slashes(i->uid_path); - } else { - r = parse_uid(resolved_id, &i->uid); - if (r < 0) { - log_error("Failed to parse UID: %s", id); - return -EBADMSG; - } - - i->uid_set = true; - } - } - - i->description = description; - description = NULL; - - i->home = home; - home = NULL; - - h = users; - break; - - case ADD_GROUP: - if (!name) { - log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); - return -EINVAL; - } - - if (description) { - log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line); - return -EINVAL; - } - - if (home) { - log_error("[%s:%u] Lines of type 'g' don't take a home directory field.", fname, line); - return -EINVAL; - } - - r = hashmap_ensure_allocated(&groups, &string_hash_ops); - if (r < 0) - return log_oom(); - - i = new0(Item, 1); - if (!i) - return log_oom(); - - if (resolved_id) { - if (path_is_absolute(resolved_id)) { - i->gid_path = resolved_id; - resolved_id = NULL; - - path_kill_slashes(i->gid_path); - } else { - r = parse_gid(resolved_id, &i->gid); - if (r < 0) { - log_error("Failed to parse GID: %s", id); - return -EBADMSG; - } - - i->gid_set = true; - } - } - - h = groups; - break; - - default: - return -EBADMSG; - } - - i->type = action[0]; - i->name = resolved_name; - resolved_name = NULL; - - existing = hashmap_get(h, i->name); - if (existing) { - - /* Two identical items are fine */ - if (!item_equal(existing, i)) - log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); - - return 0; - } - - r = hashmap_put(h, i->name, i); - if (r < 0) - return log_oom(); - - i = NULL; - return 0; -} - -static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *rf = NULL; - FILE *f = NULL; - char line[LINE_MAX]; - unsigned v = 0; - int r = 0; - - assert(fn); - - if (streq(fn, "-")) - f = stdin; - else { - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); - } - - f = rf; - } - - FOREACH_LINE(line, f, break) { - char *l; - int k; - - v++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = parse_line(fn, v, l); - if (k < 0 && r == 0) - r = k; - } - - if (ferror(f)) { - log_error_errno(errno, "Failed to read from file %s: %m", fn); - if (r == 0) - r = -EIO; - } - - return r; -} - -static void free_database(Hashmap *by_name, Hashmap *by_id) { - char *name; - - for (;;) { - name = hashmap_first(by_id); - if (!name) - break; - - hashmap_remove(by_name, name); - - hashmap_steal_first_key(by_id); - free(name); - } - - while ((name = hashmap_steal_first_key(by_name))) - free(name); - - hashmap_free(by_name); - hashmap_free(by_id); -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Creates system user accounts.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --root=PATH Operate on an alternate filesystem root\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ROOT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -int main(int argc, char *argv[]) { - - _cleanup_close_ int lock = -1; - Iterator iterator; - int r, k; - Item *i; - char *n; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - if (optind < argc) { - int j; - - for (j = optind; j < argc; j++) { - k = read_config_file(argv[j], false); - if (k < 0 && r == 0) - r = k; - } - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = read_config_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - - if (!uid_range) { - /* Default to default range of 1..SYSTEMD_UID_MAX */ - r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX); - if (r < 0) { - log_oom(); - goto finish; - } - } - - r = add_implicit(); - if (r < 0) - goto finish; - - lock = take_etc_passwd_lock(arg_root); - if (lock < 0) { - log_error_errno(lock, "Failed to take lock: %m"); - goto finish; - } - - r = load_user_database(); - if (r < 0) { - log_error_errno(r, "Failed to load user database: %m"); - goto finish; - } - - r = load_group_database(); - if (r < 0) { - log_error_errno(r, "Failed to read group database: %m"); - goto finish; - } - - HASHMAP_FOREACH(i, groups, iterator) - process_item(i); - - HASHMAP_FOREACH(i, users, iterator) - process_item(i); - - r = write_files(); - if (r < 0) - log_error_errno(r, "Failed to write files: %m"); - -finish: - while ((i = hashmap_steal_first(groups))) - item_free(i); - - while ((i = hashmap_steal_first(users))) - item_free(i); - - while ((n = hashmap_first_key(members))) { - strv_free(hashmap_steal_first(members)); - free(n); - } - - hashmap_free(groups); - hashmap_free(users); - hashmap_free(members); - hashmap_free(todo_uids); - hashmap_free(todo_gids); - - free_database(database_user, database_uid); - free_database(database_group, database_gid); - - free(arg_root); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/sysv-generator/Makefile b/src/sysv-generator/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/sysv-generator/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c deleted file mode 100644 index 3ed8f23ff9..0000000000 --- a/src/sysv-generator/sysv-generator.c +++ /dev/null @@ -1,982 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Thomas H.P. Andersen - Copyright 2010 Lennart Poettering - Copyright 2011 Michal Schmidt - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "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 "path-lookup.h" -#include "path-util.h" -#include "set.h" -#include "special.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" -#include "util.h" - -static const struct { - const char *path; - const char *target; -} rcnd_table[] = { - /* Standard SysV runlevels for start-up */ - { "rc1.d", SPECIAL_RESCUE_TARGET }, - { "rc2.d", SPECIAL_MULTI_USER_TARGET }, - { "rc3.d", SPECIAL_MULTI_USER_TARGET }, - { "rc4.d", SPECIAL_MULTI_USER_TARGET }, - { "rc5.d", SPECIAL_GRAPHICAL_TARGET }, - - /* We ignore the SysV runlevels for shutdown here, as SysV services get default dependencies anyway, and that - * means they are shut down anyway at system power off if running. */ -}; - -static const char *arg_dest = "/tmp"; - -typedef struct SysvStub { - char *name; - char *path; - char *description; - int sysv_start_priority; - char *pid_file; - char **before; - char **after; - char **wants; - char **wanted_by; - 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); - free(s->pid_file); - strv_free(s->before); - strv_free(s->after); - strv_free(s->wants); - strv_free(s->wanted_by); - free(s); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, free_sysvstub); - -static void free_sysvstub_hashmapp(Hashmap **h) { - SysvStub *stub; - - while ((stub = hashmap_steal_first(*h))) - free_sysvstub(stub); - - hashmap_free(*h); -} - -static int add_symlink(const char *service, const char *where) { - const char *from, *to; - int r; - - assert(service); - assert(where); - - from = strjoina(arg_dest, "/", service); - to = strjoina(arg_dest, "/", where, ".wants/", service); - - mkdir_parents_label(to, 0755); - - r = symlink(from, to); - if (r < 0) { - if (errno == EEXIST) - return 0; - - return -errno; - } - - return 1; -} - -static int add_alias(const char *service, const char *alias) { - const char *link; - int r; - - assert(service); - assert(alias); - - link = strjoina(arg_dest, "/", alias); - - r = symlink(service, link); - if (r < 0) { - if (errno == EEXIST) - return 0; - - return -errno; - } - - return 1; -} - -static int generate_unit_file(SysvStub *s) { - _cleanup_fclose_ FILE *f = NULL; - const char *unit; - char **p; - int r; - - 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); - (void) unlink(unit); - } - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); - - fprintf(f, - "# Automatically generated by systemd-sysv-generator\n\n" - "[Unit]\n" - "Documentation=man:systemd-sysv-generator(8)\n" - "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); - - fprintf(f, - "\n[Service]\n" - "Type=forking\n" - "Restart=no\n" - "TimeoutSec=5min\n" - "IgnoreSIGPIPE=no\n" - "KillMode=process\n" - "GuessMainPID=no\n" - "RemainAfterExit=%s\n", - yes_no(!s->pid_file)); - - if (s->pid_file) - fprintf(f, "PIDFile=%s\n", s->pid_file); - - fprintf(f, - "ExecStart=%s start\n" - "ExecStop=%s stop\n", - s->path, s->path); - - 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_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p); - } - - return 1; -} - -static bool usage_contains_reload(const char *line) { - return (strcasestr(line, "{reload|") || - strcasestr(line, "{reload}") || - strcasestr(line, "{reload\"") || - strcasestr(line, "|reload|") || - strcasestr(line, "|reload}") || - strcasestr(line, "|reload\"")); -} - -static char *sysv_translate_name(const char *name) { - _cleanup_free_ char *c = NULL; - char *res; - - c = strdup(name); - if (!c) - return NULL; - - res = endswith(c, ".sh"); - if (res) - *res = 0; - - if (unit_name_mangle(c, UNIT_NAME_NOGLOB, &res) < 0) - return NULL; - - return res; -} - -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 - * standardized name or a distribution-specific one. Since we - * just follow what already exists and do not introduce new - * uses or names we don't care who introduced a new name. */ - - static const char * const table[] = { - /* LSB defined facilities */ - "local_fs", NULL, - "network", SPECIAL_NETWORK_ONLINE_TARGET, - "named", SPECIAL_NSS_LOOKUP_TARGET, - "portmap", SPECIAL_RPCBIND_TARGET, - "remote_fs", SPECIAL_REMOTE_FS_TARGET, - "syslog", NULL, - "time", SPECIAL_TIME_SYNC_TARGET, - }; - - char *filename_no_sh, *e, *m; - const char *n; - unsigned i; - int r; - - assert(name); - 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; - - m = strdup(table[i+1]); - if (!m) - return log_oom(); - - *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 */ - filename_no_sh = strdupa(filename); - e = endswith(filename_no_sh, ".sh"); - if (e) { - *e = '\0'; - filename = filename_no_sh; - } - - /* Names equaling the file name of the services are redundant */ - if (streq_ptr(n, filename)) - return 0; - - /* 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) { - int r; - - assert(s); - assert(full_text); - assert(text); - - for (;;) { - _cleanup_free_ char *word = NULL, *m = NULL; - - 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; - - 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); - 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(); - } - - break; - - case _UNIT_TYPE_INVALID: - log_warning("Unit name '%s' is invalid", m); - break; - - default: - log_warning("Unknown unit type for unit '%s'", m); - } - } - - return 0; -} - -static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) { - int r; - - assert(s); - assert(full_text); - assert(text); - - for (;;) { - _cleanup_free_ char *word = NULL, *m = NULL; - bool is_before; - - 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:"); - - if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) { - /* the network-online target is special, as it needs to be actively pulled in */ - r = strv_extend(&s->after, m); - if (r < 0) - return log_oom(); - - r = strv_extend(&s->wants, m); - } else - r = strv_extend(is_before ? &s->before : &s->after, m); - if (r < 0) - return log_oom(); - } - - return 0; -} - -static int load_sysv(SysvStub *s) { - _cleanup_fclose_ FILE *f; - unsigned line = 0; - int r; - enum { - NORMAL, - DESCRIPTION, - LSB, - LSB_DESCRIPTION, - USAGE_CONTINUATION - } state = NORMAL; - _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) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open %s: %m", s->path); - } - - log_debug("Loading SysV script %s", s->path); - - FOREACH_LINE(l, f, goto fail) { - char *t; - - line++; - - t = strstrip(l); - if (*t != '#') { - /* Try to figure out whether this init script supports - * the reload operation. This heuristic looks for - * "Usage" lines which include the reload option. */ - if ( state == USAGE_CONTINUATION || - (state == NORMAL && strcasestr(t, "usage"))) { - if (usage_contains_reload(t)) { - supports_reload = true; - state = NORMAL; - } else if (t[strlen(t)-1] == '\\') - state = USAGE_CONTINUATION; - else - state = NORMAL; - } - - continue; - } - - if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) { - state = LSB; - s->has_lsb = true; - continue; - } - - if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) { - state = NORMAL; - continue; - } - - t++; - t += strspn(t, WHITESPACE); - - if (state == NORMAL) { - - /* Try to parse Red Hat style description */ - - if (startswith_no_case(t, "description:")) { - - size_t k; - const char *j; - - k = strlen(t); - if (k > 0 && t[k-1] == '\\') { - state = DESCRIPTION; - t[k-1] = 0; - } - - j = empty_to_null(strstrip(t+12)); - - r = free_and_strdup(&chkconfig_description, j); - if (r < 0) - return log_oom(); - - } else if (startswith_no_case(t, "pidfile:")) { - const char *fn; - - state = NORMAL; - - fn = strstrip(t+8); - if (!path_is_absolute(fn)) { - log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line); - continue; - } - - r = free_and_strdup(&s->pid_file, fn); - if (r < 0) - return log_oom(); - } - - } else if (state == DESCRIPTION) { - - /* Try to parse Red Hat style description - * continuation */ - - size_t k; - char *j; - - k = strlen(t); - if (k > 0 && t[k-1] == '\\') - t[k-1] = 0; - else - state = NORMAL; - - j = strstrip(t); - if (!isempty(j)) { - char *d = NULL; - - if (chkconfig_description) - d = strjoin(chkconfig_description, " ", j, NULL); - else - d = strdup(j); - if (!d) - return log_oom(); - - free(chkconfig_description); - chkconfig_description = d; - } - - } else if (state == LSB || state == LSB_DESCRIPTION) { - - if (startswith_no_case(t, "Provides:")) { - state = LSB; - - r = handle_provides(s, line, t, t + 9); - if (r < 0) - return r; - - } else if (startswith_no_case(t, "Required-Start:") || - startswith_no_case(t, "Should-Start:") || - startswith_no_case(t, "X-Start-Before:") || - startswith_no_case(t, "X-Start-After:")) { - - state = LSB; - - r = handle_dependencies(s, line, t, strchr(t, ':') + 1); - if (r < 0) - return r; - - } else if (startswith_no_case(t, "Description:")) { - const char *j; - - state = LSB_DESCRIPTION; - - j = empty_to_null(strstrip(t+12)); - - r = free_and_strdup(&long_description, j); - if (r < 0) - return log_oom(); - - } else if (startswith_no_case(t, "Short-Description:")) { - const char *j; - - state = LSB; - - j = empty_to_null(strstrip(t+18)); - - r = free_and_strdup(&short_description, j); - if (r < 0) - return log_oom(); - - } else if (state == LSB_DESCRIPTION) { - - if (startswith(l, "#\t") || startswith(l, "# ")) { - const char *j; - - j = strstrip(t); - if (!isempty(j)) { - char *d = NULL; - - if (long_description) - d = strjoin(long_description, " ", t, NULL); - else - d = strdup(j); - if (!d) - return log_oom(); - - free(long_description); - long_description = d; - } - - } else - state = LSB; - } - } - } - - s->reload = supports_reload; - - /* We use the long description only if - * no short description is set. */ - - if (short_description) - description = short_description; - else if (chkconfig_description) - description = chkconfig_description; - else if (long_description) - description = long_description; - else - description = NULL; - - if (description) { - char *d; - - d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description); - if (!d) - 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) { - SysvStub *other; - Iterator j; - int r; - - assert(s); - - if (!s->loaded) - return 0; - - if (s->sysv_start_priority < 0) - return 0; - - HASHMAP_FOREACH(other, all_services, j) { - if (s == other) - continue; - - if (!other->loaded) - continue; - - if (other->sysv_start_priority < 0) - continue; - - /* If both units have modern headers we don't care - * about the priorities */ - if (s->has_lsb && other->has_lsb) - continue; - - if (other->sysv_start_priority < s->sysv_start_priority) { - r = strv_extend(&s->after, other->name); - if (r < 0) - return log_oom(); - - } else if (other->sysv_start_priority > s->sysv_start_priority) { - r = strv_extend(&s->before, other->name); - if (r < 0) - return log_oom(); - } else - continue; - - /* FIXME: Maybe we should compare the name here lexicographically? */ - } - - return 0; -} - -static int acquire_search_path(const char *def, const char *envvar, char ***ret) { - _cleanup_strv_free_ char **l = NULL; - const char *e; - int r; - - assert(def); - assert(envvar); - - e = getenv(envvar); - if (e) { - r = path_split_and_make_absolute(e, &l); - if (r < 0) - return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar); - } - - if (strv_isempty(l)) { - strv_free(l); - - l = strv_new(def, NULL); - if (!l) - return log_oom(); - } - - if (!path_strv_resolve_uniq(l, NULL)) - return log_oom(); - - *ret = l; - l = NULL; - - return 0; -} - -static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { - _cleanup_strv_free_ char **sysvinit_path = NULL; - char **path; - int r; - - assert(lp); - - r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path); - if (r < 0) - return r; - - STRV_FOREACH(path, sysvinit_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*path); - if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path); - continue; - } - - 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; - - if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { - log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); - continue; - } - - if (!(st.st_mode & S_IXUSR)) - continue; - - if (!S_ISREG(st.st_mode)) - continue; - - name = sysv_translate_name(de->d_name); - if (!name) - return log_oom(); - - if (hashmap_contains(all_services, name)) - continue; - - r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); - if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { - 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(); - - service = new0(SysvStub, 1); - if (!service) - return log_oom(); - - 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(); - - service = NULL; - } - } - - return 0; -} - -static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) { - Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; - _cleanup_strv_free_ char **sysvrcnd_path = NULL; - SysvStub *service; - unsigned i; - Iterator j; - char **p; - int r; - - assert(lp); - - r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path); - if (r < 0) - return r; - - STRV_FOREACH(p, sysvrcnd_path) { - for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { - - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *path = NULL; - struct dirent *de; - - path = strjoin(*p, "/", rcnd_table[i].path, NULL); - if (!path) { - r = log_oom(); - goto finish; - } - - d = opendir(path); - if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Opening %s failed, ignoring: %m", path); - - continue; - } - - 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 (de->d_name[0] != 'S') - continue; - - if (strlen(de->d_name) < 4) - continue; - - a = undecchar(de->d_name[1]); - b = undecchar(de->d_name[2]); - - if (a < 0 || b < 0) - continue; - - fpath = strjoin(*p, "/", de->d_name, NULL); - if (!fpath) { - r = log_oom(); - goto finish; - } - - name = sysv_translate_name(de->d_name + 3); - if (!name) { - r = log_oom(); - goto finish; - } - - 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); - continue; - } - - service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority); - - r = set_ensure_allocated(&runlevel_services[i], NULL); - if (r < 0) { - log_oom(); - goto finish; - } - - r = set_put(runlevel_services[i], service); - 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) { - log_oom(); - goto finish; - } - r = strv_extend(&service->wanted_by, rcnd_table[i].target); - if (r < 0) { - log_oom(); - goto finish; - } - } - - r = 0; - -finish: - for (i = 0; i < ELEMENTSOF(rcnd_table); i++) - set_free(runlevel_services[i]); - - return r; -} - -int main(int argc, char *argv[]) { - _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."); - return EXIT_FAILURE; - } - - if (argc > 1) - arg_dest = argv[3]; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); - if (r < 0) { - log_error_errno(r, "Failed to find lookup paths: %m"); - goto finish; - } - - all_services = hashmap_new(&string_hash_ops); - if (!all_services) { - r = log_oom(); - goto finish; - } - - r = enumerate_sysv(&lp, all_services); - if (r < 0) - goto finish; - - r = set_dependencies_from_rcnd(&lp, all_services); - if (r < 0) - goto finish; - - HASHMAP_FOREACH(service, all_services, j) - (void) load_sysv(service); - - HASHMAP_FOREACH(service, all_services, j) { - (void) fix_order(service, all_services); - (void) generate_unit_file(service); - } - - r = 0; - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/test/Makefile b/src/test/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/test/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/test/Makefile b/src/test/Makefile new file mode 100644 index 0000000000..7c50a983ea --- /dev/null +++ b/src/test/Makefile @@ -0,0 +1,35 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +test_id128_SOURCES = \ + src/test/test-id128.c + +test_id128_LDADD = \ + libsystemd-shared.la + +tests += \ + test-id128 + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c index 430dda8e78..1417d596e4 100644 --- a/src/test/test-acl-util.c +++ b/src/test/test-acl-util.c @@ -22,11 +22,11 @@ #include #include -#include "acl-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "string-util.h" -#include "user-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "shared/acl-util.h" static void test_add_acls_for_user(void) { char fn[] = "/tmp/test-empty.XXXXXX"; diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c index aeaa0929b1..21115e677a 100644 --- a/src/test/test-af-list.c +++ b/src/test/test-af-list.c @@ -20,15 +20,15 @@ #include #include -#include "macro.h" -#include "string-util.h" -#include "util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/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 "basic/af-from-name.h" +#include "basic/af-list.h" +#include "basic/af-to-name.h" int main(int argc, const char *argv[]) { diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c index cc4821eaf5..520951495d 100644 --- a/src/test/test-alloc-util.c +++ b/src/test/test-alloc-util.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "macro.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/util.h" static void test_alloca(void) { static const uint8_t zero[997] = { }; diff --git a/src/test/test-architecture.c b/src/test/test-architecture.c index f41e488d99..6373b798b0 100644 --- a/src/test/test-architecture.c +++ b/src/test/test-architecture.c @@ -17,10 +17,10 @@ along with systemd; If not, see . ***/ -#include "architecture.h" -#include "log.h" -#include "util.h" -#include "virt.h" +#include "basic/architecture.h" +#include "basic/log.h" +#include "basic/util.h" +#include "basic/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 f3989ad201..976958e733 100644 --- a/src/test/test-arphrd-list.c +++ b/src/test/test-arphrd-list.c @@ -20,15 +20,15 @@ #include #include -#include "macro.h" -#include "string-util.h" -#include "util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/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 "basic/arphrd-from-name.h" +#include "basic/arphrd-list.h" +#include "basic/arphrd-to-name.h" int main(int argc, const char *argv[]) { diff --git a/src/test/test-ask-password-api.c b/src/test/test-ask-password-api.c index 86666597c7..a93e821c7d 100644 --- a/src/test/test-ask-password-api.c +++ b/src/test/test-ask-password-api.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "ask-password-api.h" -#include "log.h" +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "shared/ask-password-api.h" static void ask_password(void) { int r; diff --git a/src/test/test-async.c b/src/test/test-async.c index ada6d67c42..627768b392 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -19,10 +19,10 @@ #include -#include "async.h" -#include "fileio.h" -#include "macro.h" -#include "util.h" +#include "basic/async.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/util.h" static bool test_async = false; diff --git a/src/test/test-barrier.c b/src/test/test-barrier.c index e6aa3b5cfe..1123f33f16 100644 --- a/src/test/test-barrier.c +++ b/src/test/test-barrier.c @@ -31,8 +31,8 @@ #include #include -#include "barrier.h" -#include "util.h" +#include "basic/barrier.h" +#include "basic/util.h" /* 20ms to test deadlocks; All timings use multiples of this constant as * alarm/sleep timers. If this timeout is too small for slow machines to perform diff --git a/src/test/test-bitmap.c b/src/test/test-bitmap.c index ff22117745..b18bcef9ae 100644 --- a/src/test/test-bitmap.c +++ b/src/test/test-bitmap.c @@ -17,7 +17,7 @@ along with systemd; If not, see . ***/ -#include "bitmap.h" +#include "basic/bitmap.h" int main(int argc, const char *argv[]) { _cleanup_bitmap_free_ Bitmap *b = NULL, *b2 = NULL; diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c index 8e68d6510d..022d101957 100644 --- a/src/test/test-boot-timestamps.c +++ b/src/test/test-boot-timestamps.c @@ -18,11 +18,11 @@ along with systemd; If not, see . ***/ -#include "acpi-fpdt.h" -#include "boot-timestamps.h" -#include "efivars.h" -#include "log.h" -#include "util.h" +#include "basic/log.h" +#include "basic/util.h" +#include "shared/acpi-fpdt.h" +#include "shared/boot-timestamps.h" +#include "shared/efivars.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 ce29d88412..eb33a3937f 100644 --- a/src/test/test-btrfs.c +++ b/src/test/test-btrfs.c @@ -19,13 +19,13 @@ #include -#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 "basic/btrfs-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/parse-util.h" +#include "basic/string-util.h" +#include "basic/util.h" int main(int argc, char *argv[]) { BtrfsQuotaInfo quota; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 4a2b93de59..c7cbf99cf0 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -19,10 +19,10 @@ #include -#include "alloc-util.h" -#include "calendarspec.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/calendarspec.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_one(const char *input, const char *output) { CalendarSpec *c; diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c index 4132ec56fd..6af0a45b66 100644 --- a/src/test/test-cap-list.c +++ b/src/test/test-cap-list.c @@ -19,12 +19,12 @@ #include -#include "alloc-util.h" -#include "cap-list.h" -#include "capability-util.h" -#include "fileio.h" -#include "parse-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/cap-list.h" +#include "basic/capability-util.h" +#include "basic/fileio.h" +#include "basic/parse-util.h" +#include "basic/util.h" /* verify the capability parser */ static void test_cap_list(void) { diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 629bb63c81..07e52c2ba8 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -25,10 +25,10 @@ #include #include -#include "capability-util.h" -#include "fd-util.h" -#include "macro.h" -#include "util.h" +#include "basic/capability-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/util.h" static uid_t test_uid = -1; static gid_t test_gid = -1; diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index a027eb0fd2..6d889c98d2 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -19,13 +19,14 @@ #include -#include "macro.h" +#include "basic/macro.h" +#include "basic/rm-rf.h" #include "manager.h" -#include "rm-rf.h" -#include "test-helper.h" -#include "tests.h" +#include "shared/tests.h" #include "unit.h" +#include "test-helper.h" + static int test_cgroup_mask(void) { Manager *m = NULL; Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep; diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 43f8906172..394839bc83 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -17,17 +17,18 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "cgroup-util.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 "basic/alloc-util.h" +#include "basic/cgroup-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/formats-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/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 5336c19652..c969185337 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -20,10 +20,10 @@ #include #include -#include "cgroup-util.h" -#include "path-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/cgroup-util.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/util.h" int main(int argc, char*argv[]) { char *path; diff --git a/src/test/test-clock.c b/src/test/test-clock.c index 84f775e5bc..c6a8e7aa1c 100644 --- a/src/test/test-clock.c +++ b/src/test/test-clock.c @@ -17,14 +17,14 @@ along with systemd; If not, see . ***/ -#include #include +#include -#include "clock-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" +#include "basic/clock-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" static void test_clock_is_localtime(void) { char adjtime[] = "/tmp/test-adjtime.XXXXXX"; diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 987862f1c6..9d27a66a95 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -17,20 +17,20 @@ along with systemd; If not, see . ***/ -#include "sd-id128.h" - -#include "alloc-util.h" -#include "apparmor-util.h" -#include "architecture.h" -#include "audit-util.h" -#include "condition.h" -#include "hostname-util.h" -#include "ima-util.h" -#include "log.h" -#include "macro.h" -#include "selinux-util.h" -#include "smack-util.h" -#include "util.h" +#include + +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/audit-util.h" +#include "basic/hostname-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/selinux-util.h" +#include "basic/smack-util.h" +#include "basic/util.h" +#include "shared/apparmor-util.h" +#include "shared/condition.h" +#include "shared/ima-util.h" static void test_condition_test_path(void) { Condition *condition; diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index 03b3a9fa5c..336e980e07 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -20,16 +20,16 @@ #include #include -#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 "basic/alloc-util.h" +#include "basic/conf-files.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" static void setup_test_dir(char *tmp_dir, const char *files, ...) { va_list ap; diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index be5d2611f8..1536e7937d 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -17,12 +17,12 @@ along with systemd; If not, see . ***/ -#include "conf-parser.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/conf-parser.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 68154fc4e8..cd9def191c 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -19,19 +19,19 @@ #include -#include "alloc-util.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "mkdir.h" -#include "path-util.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/copy.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" static void test_copy_file(void) { _cleanup_free_ char *buf = NULL; diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c index 8818d1ffb7..0558f323ea 100644 --- a/src/test/test-cpu-set-util.c +++ b/src/test/test-cpu-set-util.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "cpu-set-util.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/cpu-set-util.h" +#include "basic/macro.h" static void test_parse_cpu_set(void) { cpu_set_t *c = NULL; diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c index a7cb426282..98988e1e31 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -19,9 +19,9 @@ #include -#include "sd-daemon.h" +#include -#include "strv.h" +#include "basic/strv.h" int main(int argc, char*argv[]) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/test/test-date.c b/src/test/test-date.c index 7f497bb7d5..a7a2e9087c 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -19,9 +19,9 @@ #include -#include "alloc-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_should_pass(const char *p) { usec_t t, q; diff --git a/src/test/test-device-nodes.c b/src/test/test-device-nodes.c index af75b38948..2310fbb9c3 100644 --- a/src/test/test-device-nodes.c +++ b/src/test/test-device-nodes.c @@ -19,10 +19,10 @@ #include -#include "alloc-util.h" -#include "device-nodes.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/device-nodes.h" +#include "basic/string-util.h" +#include "basic/util.h" /* helpers for test_encode_devnode_name */ static char *do_encode_string(const char *in) { diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index a9d09f59bc..930bd5dd62 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -17,10 +17,10 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "dns-domain.h" -#include "macro.h" -#include "string-util.h" +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "shared/dns-domain.h" static void test_dns_label_unescape_one(const char *what, const char *expect, size_t buffer_sz, int ret) { char buffer[buffer_sz]; diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c index d4f09b08a5..45e95fe597 100644 --- a/src/test/test-ellipsize.c +++ b/src/test/test-ellipsize.c @@ -19,11 +19,11 @@ #include -#include "alloc-util.h" -#include "def.h" -#include "string-util.h" -#include "terminal-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/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 23da10fa1a..9d21bb5723 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -21,11 +21,12 @@ #include #include -#include "bus-util.h" +#include "basic/rm-rf.h" #include "manager.h" -#include "rm-rf.h" +#include "shared/bus-util.h" +#include "shared/tests.h" + #include "test-helper.h" -#include "tests.h" int main(int argc, char *argv[]) { _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index 35bb62906e..30d99fcd51 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -20,10 +20,10 @@ #include -#include "env-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/env-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" static void test_strv_env_delete(void) { _cleanup_strv_free_ char **a = NULL, **b = NULL, **c = NULL, **d = NULL; diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 6cbb8443fe..874647541c 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "escape.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/escape.h" +#include "basic/macro.h" static void test_cescape(void) { _cleanup_free_ char *escaped; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 77ef4e8b2a..7cd63168c8 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -23,16 +23,17 @@ #include #include -#include "fileio.h" -#include "fs-util.h" -#include "macro.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/util.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" + +#include "test-helper.h" typedef void (*test_function_t)(Manager *m); diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index 7a23fa7b7b..3d6548b7c7 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -21,9 +21,9 @@ #include #include -#include "extract-word.h" -#include "log.h" -#include "string-util.h" +#include "basic/extract-word.h" +#include "basic/log.h" +#include "basic/string-util.h" static void test_extract_first_word(void) { const char *p, *original; diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 421d3bdeb3..f6c1f302c1 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -20,10 +20,10 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" static void test_close_many(void) { int fds[3]; diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c index 282aab1246..71b155ae8d 100644 --- a/src/test/test-fdset.c +++ b/src/test/test-fdset.c @@ -20,11 +20,11 @@ #include #include -#include "fd-util.h" -#include "fdset.h" -#include "fileio.h" -#include "macro.h" -#include "util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "shared/fdset.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 79609765e0..f5a99d668d 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -17,22 +17,22 @@ along with systemd; If not, see . ***/ +#include #include #include #include -#include "alloc-util.h" -#include "ctype.h" -#include "def.h" -#include "env-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" -#include "parse-util.h" -#include "process-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/def.h" +#include "basic/env-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/io-util.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" static void test_parse_env_file(void) { char t[] = "/tmp/test-fileio-in-XXXXXX", diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c index 77e809c5bf..28ab25dd78 100644 --- a/src/test/test-firewall-util.c +++ b/src/test/test-firewall-util.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ +#include "basic/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-fs-util.c b/src/test/test-fs-util.c index e0c040f39b..30b521c297 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -19,16 +19,16 @@ #include -#include "alloc-util.h" -#include "fileio.h" -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "mkdir.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" static void test_unlink_noerrno(void) { char name[] = "/tmp/test-close_nointr.XXXXXX"; diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c index 63a4b8c243..181ff230e0 100644 --- a/src/test/test-fstab-util.c +++ b/src/test/test-fstab-util.c @@ -17,11 +17,11 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "fstab-util.h" -#include "log.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/fstab-util.h" /* int fstab_filter_options(const char *opts, const char *names, diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c index 227d4290f0..fd6dde113a 100644 --- a/src/test/test-glob-util.c +++ b/src/test/test-glob-util.c @@ -20,10 +20,10 @@ #include #include -#include "alloc-util.h" -#include "fileio.h" -#include "glob-util.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/glob-util.h" +#include "basic/macro.h" static void test_glob_exists(void) { char name[] = "/tmp/test-glob_exists.XXXXXX"; diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index 1bd5c02f87..bbb24d885e 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -17,11 +17,11 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "hashmap.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/hashmap.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" void test_hashmap_funcs(void); diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c index 83cea360e6..c927d99079 100644 --- a/src/test/test-hashmap.c +++ b/src/test/test-hashmap.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "hashmap.h" -#include "util.h" +#include "basic/hashmap.h" +#include "basic/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 ddb10f88fd..0b1acde587 100644 --- a/src/test/test-helper.h +++ b/src/test/test-helper.h @@ -19,9 +19,9 @@ along with systemd; If not, see . ***/ -#include "sd-daemon.h" +#include -#include "macro.h" +#include "basic/macro.h" #define TEST_REQ_RUNNING_SYSTEMD(x) \ if (sd_booted() > 0) { \ diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index 276f25d091..f56035d84a 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -17,10 +17,10 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "string-util.h" +#include "basic/alloc-util.h" +#include "basic/hexdecoct.h" +#include "basic/macro.h" +#include "basic/string-util.h" static void test_hexchar(void) { assert_se(hexchar(0xa) == 'a'); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 17fde9f27e..a8e598c58e 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -19,11 +19,11 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/hostname-util.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_hostname_is_valid(void) { assert_se(hostname_is_valid("foobar", false)); diff --git a/src/test/test-hostname.c b/src/test/test-hostname.c index b38507df5d..7408d51725 100644 --- a/src/test/test-hostname.c +++ b/src/test/test-hostname.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ +#include "basic/util.h" #include "hostname-setup.h" -#include "util.h" int main(int argc, char* argv[]) { int r; diff --git a/src/test/test-id128.c b/src/test/test-id128.c index f01fbdd6b2..c9240cf40d 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -19,16 +19,16 @@ #include -#include "sd-daemon.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "id128-util.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" +#include +#include + +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "sd-id128/id128-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 index db1c928660..9a0a48131c 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -17,12 +17,12 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "fileio.h" -#include "install.h" -#include "mkdir.h" -#include "rm-rf.h" -#include "string-util.h" +#include "basic/alloc-util.h" +#include "basic/fileio.h" +#include "basic/mkdir.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "shared/install.h" static void test_basic_mask_and_enable(const char *root) { const char *p; diff --git a/src/test/test-install.c b/src/test/test-install.c index 0ac85f040a..784bcfdd5f 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -20,7 +20,7 @@ #include #include -#include "install.h" +#include "shared/install.h" static void dump_changes(UnitFileChange *c, unsigned n) { unsigned i; diff --git a/src/test/test-io-util.c b/src/test/test-io-util.c index 10bd3833bc..496b58eb95 100644 --- a/src/test/test-io-util.c +++ b/src/test/test-io-util.c @@ -21,10 +21,10 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/io-util.h" +#include "basic/macro.h" static void test_sparse_write_one(int fd, const char *buffer, size_t n) { char check[n]; diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c index c5bcaf47bb..a37aa4a10d 100644 --- a/src/test/test-ipcrm.c +++ b/src/test/test-ipcrm.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "clean-ipc.h" -#include "user-util.h" -#include "util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/clean-ipc.h" int main(int argc, char *argv[]) { uid_t uid; diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index e28de9b37b..83c8b81690 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -22,14 +22,14 @@ #include #include -#include "libudev.h" - -#include "fd-util.h" -#include "log.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev-util.h" -#include "util.h" +#include + +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "shared/udev-util.h" static void print_device(struct udev_device *device) { const char *str; diff --git a/src/test/test-list.c b/src/test/test-list.c index 160064d06a..ec32684d78 100644 --- a/src/test/test-list.c +++ b/src/test/test-list.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "list.h" -#include "util.h" +#include "basic/list.h" +#include "basic/util.h" int main(int argc, const char *argv[]) { size_t i; diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index 427c698d1d..702134ca46 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -18,9 +18,9 @@ ***/ -#include "locale-util.h" -#include "macro.h" -#include "strv.h" +#include "basic/locale-util.h" +#include "basic/macro.h" +#include "basic/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 55a2f9d23b..7a5f78a3d6 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -20,9 +20,9 @@ #include #include -#include "formats-util.h" -#include "log.h" -#include "util.h" +#include "basic/formats-util.h" +#include "basic/log.h" +#include "basic/util.h" int main(int argc, char* argv[]) { diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c index 7b67337331..479f74862f 100644 --- a/src/test/test-loopback.c +++ b/src/test/test-loopback.c @@ -20,7 +20,7 @@ #include #include -#include "log.h" +#include "basic/log.h" #include "loopback-setup.h" int main(int argc, char* argv[]) { diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index ff9f35cecd..d877c61756 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -19,12 +19,12 @@ #include -#include "alloc-util.h" -#include "fd-util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/process-util.h" +#include "basic/string-util.h" +#include "basic/util.h" #include "namespace.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 bc6dd0926c..49b659b3a9 100644 --- a/src/test/test-netlink-manual.c +++ b/src/test/test-netlink-manual.c @@ -19,14 +19,14 @@ #include #include -#include #include -#include -#include "sd-netlink.h" +#include +#include -#include "macro.h" -#include "util.h" +#include "basic/macro.h" +#include "basic/util.h" +#include "sd-netlink/sd-netlink.h" static int load_module(const char *mod_name) { struct kmod_ctx *ctx; diff --git a/src/test/test-ns.c b/src/test/test-ns.c index 9248f2987c..43cbbb69b0 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -20,7 +20,7 @@ #include #include -#include "log.h" +#include "basic/log.h" #include "namespace.h" int main(int argc, char *argv[]) { diff --git a/src/test/test-nss.c b/src/test/test-nss.c index c43bda5917..5be5c409d5 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -18,22 +18,22 @@ ***/ #include -#include #include +#include -#include "log.h" -#include "nss-util.h" -#include "path-util.h" -#include "string-util.h" -#include "alloc-util.h" -#include "in-addr-util.h" -#include "hexdecoct.h" -#include "af-list.h" -#include "stdio-util.h" -#include "strv.h" -#include "errno-list.h" -#include "hostname-util.h" -#include "local-addresses.h" +#include "basic/af-list.h" +#include "basic/alloc-util.h" +#include "basic/errno-list.h" +#include "basic/hexdecoct.h" +#include "basic/hostname-util.h" +#include "basic/in-addr-util.h" +#include "basic/log.h" +#include "basic/nss-util.h" +#include "basic/path-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "sd-netlink/local-addresses.h" static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) { switch (status) { diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 0a76308f72..1e90800a33 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -21,8 +21,8 @@ #include #include -#include "log.h" -#include "parse-util.h" +#include "basic/log.h" +#include "basic/parse-util.h" static void test_parse_boolean(void) { assert_se(parse_boolean("1") == 1); diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 096326d176..6c6315bf2d 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -20,11 +20,11 @@ #include #include -#include "log.h" -#include "path-lookup.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" +#include "basic/log.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "shared/path-lookup.h" static void test_paths(UnitFileScope scope) { char template[] = "/tmp/test-path-lookup.XXXXXXX"; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 6094d4c3e5..aa1ed1ec81 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -21,15 +21,15 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "macro.h" -#include "mount-util.h" -#include "path-util.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/macro.h" +#include "basic/mount-util.h" +#include "basic/path-util.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" #define test_path_compare(a, b, result) { \ assert_se(path_compare(a, b) == result); \ diff --git a/src/test/test-path.c b/src/test/test-path.c index 62181e22a0..1cd09c89d8 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -20,19 +20,20 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/mkdir.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" #include "manager.h" -#include "mkdir.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "test-helper.h" -#include "tests.h" +#include "shared/tests.h" #include "unit.h" -#include "util.h" + +#include "test-helper.h" typedef void (*test_function_t)(Manager *m); diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c index d81880a655..c0917d2187 100644 --- a/src/test/test-prioq.c +++ b/src/test/test-prioq.c @@ -19,11 +19,11 @@ #include -#include "alloc-util.h" -#include "prioq.h" -#include "set.h" -#include "siphash24.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/prioq.h" +#include "basic/set.h" +#include "basic/siphash24.h" +#include "basic/util.h" #define SET_SIZE 1024*4 diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 80ad5ed98b..35e6541150 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -17,13 +17,13 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "log.h" -#include "macro.h" -#include "proc-cmdline.h" -#include "special.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/string-util.h" +#include "basic/util.h" static int parse_item(const char *key, const char *value) { assert_se(key); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 562ad4acb8..65dc6a9140 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -30,18 +30,18 @@ #include #endif -#include "alloc-util.h" -#include "architecture.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "process-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "terminal-util.h" -#include "util.h" -#include "virt.h" +#include "basic/alloc-util.h" +#include "basic/architecture.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/parse-util.h" +#include "basic/process-util.h" +#include "basic/stdio-util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/util.h" +#include "basic/virt.h" static void test_get_process_comm(pid_t pid) { struct stat st; diff --git a/src/test/test-ratelimit.c b/src/test/test-ratelimit.c index 990b834c79..76bd254694 100644 --- a/src/test/test-ratelimit.c +++ b/src/test/test-ratelimit.c @@ -19,9 +19,9 @@ #include -#include "macro.h" -#include "ratelimit.h" -#include "time-util.h" +#include "basic/macro.h" +#include "basic/ratelimit.h" +#include "basic/time-util.h" static void test_ratelimit_test(void) { int i; diff --git a/src/test/test-replace-var.c b/src/test/test-replace-var.c index 297effce79..5dce1d81d7 100644 --- a/src/test/test-replace-var.c +++ b/src/test/test-replace-var.c @@ -19,10 +19,10 @@ #include -#include "macro.h" -#include "replace-var.h" -#include "string-util.h" -#include "util.h" +#include "basic/macro.h" +#include "basic/replace-var.h" +#include "basic/string-util.h" +#include "basic/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 index 62afd2de5e..7a3ed8354d 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -17,12 +17,12 @@ #include -#include "alloc-util.h" -#include "capability-util.h" -#include "macro.h" -#include "rlimit-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/macro.h" +#include "basic/rlimit-util.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_rlimit_parse_format(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) { _cleanup_free_ char *f = NULL; diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index c068f5c39e..a0f46ad1c9 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -19,11 +19,12 @@ #include -#include "macro.h" +#include "basic/macro.h" +#include "basic/rm-rf.h" #include "manager.h" -#include "rm-rf.h" +#include "shared/tests.h" + #include "test-helper.h" -#include "tests.h" int main(int argc, char *argv[]) { _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c index 7545ad3764..326c24e6c4 100644 --- a/src/test/test-selinux.c +++ b/src/test/test-selinux.c @@ -19,13 +19,13 @@ #include -#include "alloc-util.h" -#include "fd-util.h" -#include "log.h" -#include "selinux-util.h" -#include "string-util.h" -#include "time-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/log.h" +#include "basic/selinux-util.h" +#include "basic/string-util.h" +#include "basic/time-util.h" +#include "basic/util.h" static void test_testing(void) { bool b; diff --git a/src/test/test-set.c b/src/test/test-set.c index 0ee5ddcc9f..59f3366b44 100644 --- a/src/test/test-set.c +++ b/src/test/test-set.c @@ -17,7 +17,7 @@ along with systemd; If not, see . ***/ -#include "set.h" +#include "basic/set.h" static void test_set_steal_first(void) { _cleanup_set_free_ Set *m = NULL; diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c index 17b81747be..ecbb0f87ea 100644 --- a/src/test/test-sigbus.c +++ b/src/test/test-sigbus.c @@ -19,9 +19,9 @@ #include -#include "fd-util.h" -#include "sigbus.h" -#include "util.h" +#include "basic/fd-util.h" +#include "basic/sigbus.h" +#include "basic/util.h" int main(int argc, char *argv[]) { _cleanup_close_ int fd = -1; diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c index 671eb869cb..74e34394ab 100644 --- a/src/test/test-signal-util.c +++ b/src/test/test-signal-util.c @@ -20,8 +20,8 @@ #include #include -#include "macro.h" -#include "signal-util.h" +#include "basic/macro.h" +#include "basic/signal-util.h" static void test_block_signals(void) { sigset_t ss; diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index b74b7ad2dd..2c40776dd0 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "siphash24.h" -#include "util.h" +#include "basic/siphash24.h" +#include "basic/util.h" #define ITERATIONS 10000000ULL diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c index 8f99a13772..6869846e98 100644 --- a/src/test/test-sizeof.c +++ b/src/test/test-sizeof.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "log.h" -#include "time-util.h" +#include "basic/log.h" +#include "basic/time-util.h" /* Print information about various types. Useful when diagnosing * gcc diagnostics on an unfamiliar architecture. */ diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 97b6f3015d..b8ddc53ed0 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -19,10 +19,10 @@ #include -#include "log.h" -#include "sleep-config.h" -#include "strv.h" -#include "util.h" +#include "basic/log.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/sleep-config.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 1f853a7f16..44786ee262 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -17,15 +17,15 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "async.h" -#include "fd-util.h" -#include "in-addr-util.h" -#include "log.h" -#include "macro.h" -#include "socket-util.h" -#include "string-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/async.h" +#include "basic/fd-util.h" +#include "basic/in-addr-util.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/socket-util.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_ifname_valid(void) { assert(ifname_valid("foo")); diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index a10227f823..941a38b1fb 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -20,11 +20,11 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" -#include "stat-util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/macro.h" +#include "basic/stat-util.h" static void test_files_same(void) { _cleanup_close_ int fd = -1; diff --git a/src/test/test-strbuf.c b/src/test/test-strbuf.c index 513218c397..74b5c03f7b 100644 --- a/src/test/test-strbuf.c +++ b/src/test/test-strbuf.c @@ -20,10 +20,10 @@ #include #include -#include "strbuf.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/strbuf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" static ssize_t add_string(struct strbuf *sb, const char *s) { return strbuf_add_string(sb, s, strlen(s)); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index d0f84d70bc..7f867a2ced 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -17,10 +17,10 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "macro.h" -#include "string-util.h" -#include "strv.h" +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/strv.h" static void test_string_erase(void) { char *x; diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c index 72b0f6fc11..a9569e527b 100644 --- a/src/test/test-strip-tab-ansi.c +++ b/src/test/test-strip-tab-ansi.c @@ -19,9 +19,9 @@ #include -#include "string-util.h" -#include "terminal-util.h" -#include "util.h" +#include "basic/string-util.h" +#include "basic/terminal-util.h" +#include "basic/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 841a36782f..0ad23ef3bb 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -20,11 +20,11 @@ #include -#include "alloc-util.h" -#include "specifier.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/util.h" +#include "shared/specifier.h" static void test_specifier_printf(void) { static const Specifier table[] = { diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c index 9bea770131..b5c6d5f191 100644 --- a/src/test/test-strxcpyx.c +++ b/src/test/test-strxcpyx.c @@ -19,9 +19,9 @@ #include -#include "string-util.h" -#include "strxcpyx.h" -#include "util.h" +#include "basic/string-util.h" +#include "basic/strxcpyx.h" +#include "basic/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 0be74921fc..51ac9632f8 100644 --- a/src/test/test-tables.c +++ b/src/test/test-tables.c @@ -17,37 +17,37 @@ along with systemd; If not, see . ***/ -#include "architecture.h" #include "automount.h" +#include "basic/architecture.h" +#include "basic/locale-util.h" +#include "basic/log.h" +#include "basic/rlimit-util.h" +#include "basic/socket-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" #include "busname.h" #include "cgroup.h" -#include "compress.h" -#include "condition.h" #include "device.h" #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 "sd-journal/compress.h" #include "service.h" +#include "shared/condition.h" +#include "shared/install.h" +#include "shared/logs-show.h" +#include "shared/test-tables.h" #include "slice.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" int main(int argc, char **argv) { test_table(architecture, ARCHITECTURE); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 84b448a095..bcdc048196 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -21,12 +21,12 @@ #include #include -#include "fd-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "terminal-util.h" -#include "util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/log.h" +#include "basic/macro.h" +#include "basic/terminal-util.h" +#include "basic/util.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 ee7d55c5ab..d6ff1f958f 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "strv.h" -#include "time-util.h" +#include "basic/strv.h" +#include "basic/time-util.h" static void test_parse_sec(void) { usec_t u; diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index b34ebeefb2..df262e0b92 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -22,14 +22,14 @@ #include #include -#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" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/formats-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/string-util.h" +#include "basic/util.h" int main(int argc, char** argv) { _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL; diff --git a/src/test/test-udev.c b/src/test/test-udev.c index e965b4494a..1f6dc878c8 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -26,13 +26,13 @@ #include #include -#include "fs-util.h" -#include "log.h" -#include "missing.h" -#include "selinux-util.h" -#include "signal-util.h" -#include "string-util.h" -#include "udev-util.h" +#include "basic/fs-util.h" +#include "basic/log.h" +#include "basic/missing.h" +#include "basic/selinux-util.h" +#include "basic/signal-util.h" +#include "basic/string-util.h" +#include "shared/udev-util.h" #include "udev.h" static int fake_filesystems(void) { diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c index 41f06a5cec..bb79ae6c3e 100644 --- a/src/test/test-uid-range.c +++ b/src/test/test-uid-range.c @@ -19,10 +19,10 @@ #include -#include "alloc-util.h" -#include "uid-range.h" -#include "user-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/user-util.h" +#include "basic/util.h" +#include "shared/uid-range.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 4f64398943..0568211e0c 100644 --- a/src/test/test-unaligned.c +++ b/src/test/test-unaligned.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "sparse-endian.h" -#include "unaligned.h" -#include "util.h" +#include "basic/sparse-endian.h" +#include "basic/unaligned.h" +#include "basic/util.h" static uint8_t data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index ade0ff2a63..ba21dbcc83 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -25,24 +25,25 @@ #include #include -#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 "basic/alloc-util.h" +#include "basic/capability-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/hashmap.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/strv.h" +#include "basic/user-util.h" +#include "basic/util.h" #include "load-fragment.h" -#include "macro.h" -#include "rm-rf.h" -#include "specifier.h" -#include "string-util.h" -#include "strv.h" +#include "shared/install-printf.h" +#include "shared/install.h" +#include "shared/specifier.h" +#include "shared/tests.h" + #include "test-helper.h" -#include "tests.h" -#include "user-util.h" -#include "util.h" static int test_unit_file_get_set(void) { int r; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 2fd83f321c..e17355eac1 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -24,20 +24,21 @@ #include #include -#include "alloc-util.h" -#include "glob-util.h" -#include "hostname-util.h" -#include "macro.h" +#include "basic/alloc-util.h" +#include "basic/glob-util.h" +#include "basic/hostname-util.h" +#include "basic/macro.h" +#include "basic/path-util.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/user-util.h" +#include "basic/util.h" #include "manager.h" -#include "path-util.h" -#include "specifier.h" -#include "string-util.h" -#include "test-helper.h" -#include "unit-name.h" +#include "shared/specifier.h" #include "unit-printf.h" #include "unit.h" -#include "user-util.h" -#include "util.h" + +#include "test-helper.h" static void test_unit_name_is_valid(void) { assert_se(unit_name_is_valid("foo.service", UNIT_NAME_ANY)); diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 8d1ec19f17..8460396b2c 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -17,11 +17,11 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "macro.h" -#include "string-util.h" -#include "user-util.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/user-util.h" +#include "basic/util.h" static void test_uid_to_name_one(uid_t uid, const char *name) { _cleanup_free_ char *t = NULL; diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index 1ce5a5a24d..e6cfdac8ed 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -17,10 +17,10 @@ along with systemd; If not, see . ***/ -#include "alloc-util.h" -#include "string-util.h" -#include "utf8.h" -#include "util.h" +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/utf8.h" +#include "basic/util.h" static void test_utf8_is_printable(void) { assert_se(utf8_is_printable("ascii is valid\tunicode", 22)); diff --git a/src/test/test-util.c b/src/test/test-util.c index 1b5cba86c1..c0ac362c9d 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -23,14 +23,14 @@ #include #include -#include "def.h" -#include "fileio.h" -#include "fs-util.h" -#include "parse-util.h" -#include "raw-clone.h" -#include "rm-rf.h" -#include "string-util.h" -#include "util.h" +#include "basic/def.h" +#include "basic/fileio.h" +#include "basic/fs-util.h" +#include "basic/parse-util.h" +#include "basic/raw-clone.h" +#include "basic/rm-rf.h" +#include "basic/string-util.h" +#include "basic/util.h" static void test_align_power2(void) { unsigned long i, p2; diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 0fcdd9e78d..01a0ded37b 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -17,9 +17,9 @@ along with systemd; If not, see . ***/ -#include "macro.h" -#include "strv.h" -#include "verbs.h" +#include "basic/macro.h" +#include "basic/strv.h" +#include "basic/verbs.h" static int noop_dispatcher(int argc, char *argv[], void *userdata) { return 0; diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c index e3c19647fc..64814fc174 100644 --- a/src/test/test-watchdog.c +++ b/src/test/test-watchdog.c @@ -19,8 +19,8 @@ #include -#include "log.h" -#include "watchdog.h" +#include "basic/log.h" +#include "shared/watchdog.h" int main(int argc, char *argv[]) { usec_t t = 10 * USEC_PER_SEC; diff --git a/src/test/test-web-util.c b/src/test/test-web-util.c index 79a3a13af6..4907a5544c 100644 --- a/src/test/test-web-util.c +++ b/src/test/test-web-util.c @@ -17,8 +17,8 @@ along with systemd; If not, see . ***/ -#include "macro.h" -#include "web-util.h" +#include "basic/macro.h" +#include "basic/web-util.h" static void test_is_valid_documentation_url(void) { assert_se(documentation_url_is_valid("http://www.freedesktop.org/wiki/Software/systemd")); diff --git a/src/test/test-xattr-util.c b/src/test/test-xattr-util.c index 267f29426c..1a78e54d3c 100644 --- a/src/test/test-xattr-util.c +++ b/src/test/test-xattr-util.c @@ -23,12 +23,12 @@ #include #include -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "string-util.h" -#include "xattr-util.h" +#include "basic/alloc-util.h" +#include "basic/fd-util.h" +#include "basic/fs-util.h" +#include "basic/macro.h" +#include "basic/string-util.h" +#include "basic/xattr-util.h" static void test_fgetxattrat_fake(void) { char t[] = "/var/tmp/xattrtestXXXXXX"; diff --git a/src/test/test-xml.c b/src/test/test-xml.c index b0b72fa78a..22e92b2075 100644 --- a/src/test/test-xml.c +++ b/src/test/test-xml.c @@ -19,10 +19,10 @@ #include -#include "alloc-util.h" -#include "string-util.h" -#include "util.h" -#include "xml.h" +#include "basic/alloc-util.h" +#include "basic/string-util.h" +#include "basic/util.h" +#include "basic/xml.h" static void test_one(const char *data, ...) { void *state = NULL; diff --git a/src/timedate/.gitignore b/src/timedate/.gitignore deleted file mode 100644 index 48757f0968..0000000000 --- a/src/timedate/.gitignore +++ /dev/null @@ -1 +0,0 @@ -org.freedesktop.timedate1.policy diff --git a/src/timedate/Makefile b/src/timedate/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/timedate/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/timedate/org.freedesktop.timedate1.conf b/src/timedate/org.freedesktop.timedate1.conf deleted file mode 100644 index 36557d5841..0000000000 --- a/src/timedate/org.freedesktop.timedate1.conf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/timedate/org.freedesktop.timedate1.policy.in b/src/timedate/org.freedesktop.timedate1.policy.in deleted file mode 100644 index aa30b70831..0000000000 --- a/src/timedate/org.freedesktop.timedate1.policy.in +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Set system time - <_message>Authentication is required to set the system time. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - org.freedesktop.timedate1.set-timezone org.freedesktop.timedate1.set-ntp - - - - <_description>Set system timezone - <_message>Authentication is required to set the system timezone. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set RTC to local timezone or UTC - <_message>Authentication is required to control whether - the RTC stores the local or UTC time. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Turn network time synchronization on or off - <_message>Authentication is required to control whether - network time synchronization shall be enabled. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/timedate/org.freedesktop.timedate1.service b/src/timedate/org.freedesktop.timedate1.service deleted file mode 100644 index 875f4bec78..0000000000 --- a/src/timedate/org.freedesktop.timedate1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.timedate1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.timedate1.service diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c deleted file mode 100644 index 553ef67011..0000000000 --- a/src/timedate/timedatectl.c +++ /dev/null @@ -1,507 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "sd-bus.h" - -#include "bus-error.h" -#include "bus-util.h" -#include "pager.h" -#include "parse-util.h" -#include "spawn-polkit-agent.h" -#include "strv.h" -#include "terminal-util.h" -#include "util.h" - -static bool arg_no_pager = false; -static bool arg_ask_password = true; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; -static bool arg_adjust_system_clock = false; - -static void polkit_agent_open_if_enabled(void) { - - /* Open the polkit agent as a child process if necessary */ - if (!arg_ask_password) - return; - - if (arg_transport != BUS_TRANSPORT_LOCAL) - return; - - polkit_agent_open(); -} - -typedef struct StatusInfo { - usec_t time; - char *timezone; - - usec_t rtc_time; - int rtc_local; - - int ntp_enabled; - int ntp_capable; - int ntp_synced; -} StatusInfo; - -static void status_info_clear(StatusInfo *info) { - if (info) { - free(info->timezone); - zero(*info); - } -} - -static void print_status_info(const StatusInfo *i) { - char a[FORMAT_TIMESTAMP_MAX]; - struct tm tm; - time_t sec; - bool have_time = false; - const char *old_tz = NULL, *tz; - int r; - - assert(i); - - /* Save the old $TZ */ - tz = getenv("TZ"); - if (tz) - old_tz = strdupa(tz); - - /* Set the new $TZ */ - if (setenv("TZ", isempty(i->timezone) ? "UTC" : i->timezone, true) < 0) - log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); - else - tzset(); - - if (i->time != 0) { - sec = (time_t) (i->time / USEC_PER_SEC); - have_time = true; - } else if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE)) { - sec = time(NULL); - have_time = true; - } else - log_warning("Could not get time from timedated and not operating locally, ignoring."); - - if (have_time) { - xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)); - printf(" Local time: %.*s\n", (int) sizeof(a), a); - - xstrftime(a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)); - printf(" Universal time: %.*s\n", (int) sizeof(a), a); - } else { - printf(" Local time: %s\n", "n/a"); - printf(" Universal time: %s\n", "n/a"); - } - - if (i->rtc_time > 0) { - time_t rtc_sec; - - rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC); - xstrftime(a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)); - printf(" RTC time: %.*s\n", (int) sizeof(a), a); - } else - printf(" RTC time: %s\n", "n/a"); - - if (have_time) - xstrftime(a, "%Z, %z", localtime_r(&sec, &tm)); - - /* Restore the $TZ */ - if (old_tz) - r = setenv("TZ", old_tz, true); - else - r = unsetenv("TZ"); - if (r < 0) - log_warning_errno(errno, "Failed to set TZ environment variable, ignoring: %m"); - else - tzset(); - - printf(" Time zone: %s (%.*s)\n" - " Network time on: %s\n" - "NTP synchronized: %s\n" - " RTC in local TZ: %s\n", - strna(i->timezone), (int) sizeof(a), have_time ? a : "n/a", - i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a", - yes_no(i->ntp_synced), - yes_no(i->rtc_local)); - - if (i->rtc_local) - printf("\n%s" - "Warning: The system is configured to read the RTC time in the local time zone.\n" - " This mode can not be fully supported. It will create various problems\n" - " with time zone changes and daylight saving time adjustments. The RTC\n" - " time is never updated, it relies on external facilities to maintain it.\n" - " If at all possible, use RTC in UTC by calling\n" - " 'timedatectl set-local-rtc 0'.%s\n", ansi_highlight(), ansi_normal()); -} - -static int show_status(sd_bus *bus, char **args, unsigned n) { - _cleanup_(status_info_clear) StatusInfo info = {}; - static const struct bus_properties_map map[] = { - { "Timezone", "s", NULL, offsetof(StatusInfo, timezone) }, - { "LocalRTC", "b", NULL, offsetof(StatusInfo, rtc_local) }, - { "NTP", "b", NULL, offsetof(StatusInfo, ntp_enabled) }, - { "CanNTP", "b", NULL, offsetof(StatusInfo, ntp_capable) }, - { "NTPSynchronized", "b", NULL, offsetof(StatusInfo, ntp_synced) }, - { "TimeUSec", "t", NULL, offsetof(StatusInfo, time) }, - { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) }, - {} - }; - int r; - - assert(bus); - - r = bus_map_all_properties(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - map, - &info); - if (r < 0) - return log_error_errno(r, "Failed to query server: %m"); - - print_status_info(&info); - - return r; -} - -static int set_time(sd_bus *bus, char **args, unsigned n) { - _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; - - assert(args); - assert(n == 2); - - polkit_agent_open_if_enabled(); - - r = parse_timestamp(args[1], &t); - if (r < 0) { - log_error("Failed to parse time specification: %s", args[1]); - return r; - } - - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetTime", - &error, - NULL, - "xbb", (int64_t)t, relative, interactive); - if (r < 0) - log_error("Failed to set time: %s", bus_error_message(&error, -r)); - - return r; -} - -static int set_timezone(sd_bus *bus, char **args, unsigned n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(args); - assert(n == 2); - - polkit_agent_open_if_enabled(); - - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetTimezone", - &error, - NULL, - "sb", args[1], arg_ask_password); - if (r < 0) - log_error("Failed to set time zone: %s", bus_error_message(&error, -r)); - - return r; -} - -static int set_local_rtc(sd_bus *bus, char **args, unsigned n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r, b; - - assert(args); - assert(n == 2); - - polkit_agent_open_if_enabled(); - - b = parse_boolean(args[1]); - if (b < 0) { - log_error("Failed to parse local RTC setting: %s", args[1]); - return b; - } - - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetLocalRTC", - &error, - NULL, - "bbb", b, arg_adjust_system_clock, arg_ask_password); - if (r < 0) - log_error("Failed to set local RTC: %s", bus_error_message(&error, -r)); - - return r; -} - -static int set_ntp(sd_bus *bus, char **args, unsigned n) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int b, r; - - assert(args); - assert(n == 2); - - polkit_agent_open_if_enabled(); - - b = parse_boolean(args[1]); - if (b < 0) { - log_error("Failed to parse NTP setting: %s", args[1]); - return b; - } - - r = sd_bus_call_method(bus, - "org.freedesktop.timedate1", - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "SetNTP", - &error, - NULL, - "bb", b, arg_ask_password); - if (r < 0) - log_error("Failed to set ntp: %s", bus_error_message(&error, -r)); - - return r; -} - -static int list_timezones(sd_bus *bus, char **args, unsigned n) { - _cleanup_strv_free_ char **zones = NULL; - int r; - - assert(args); - assert(n == 1); - - r = get_timezones(&zones); - if (r < 0) - return log_error_errno(r, "Failed to read list of time zones: %m"); - - pager_open(arg_no_pager, false); - strv_print(zones); - - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...] COMMAND ...\n\n" - "Query or change system time and date settings.\n\n" - " -h --help Show this help message\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-ask-password Do not prompt for password\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --adjust-system-clock Adjust system clock when changing local RTC mode\n\n" - "Commands:\n" - " status Show current time settings\n" - " set-time TIME Set system time\n" - " set-timezone ZONE Set system time zone\n" - " list-timezones Show known time zones\n" - " set-local-rtc BOOL Control whether RTC is in local time\n" - " set-ntp BOOL Enable or disable network time synchronization\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_NO_PAGER, - ARG_ADJUST_SYSTEM_CLOCK, - ARG_NO_ASK_PASSWORD - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { "adjust-system-clock", no_argument, NULL, ARG_ADJUST_SYSTEM_CLOCK }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case 'H': - arg_transport = BUS_TRANSPORT_REMOTE; - arg_host = optarg; - break; - - case 'M': - arg_transport = BUS_TRANSPORT_MACHINE; - arg_host = optarg; - break; - - case ARG_NO_ASK_PASSWORD: - arg_ask_password = false; - break; - - case ARG_ADJUST_SYSTEM_CLOCK: - arg_adjust_system_clock = true; - break; - - case ARG_NO_PAGER: - arg_no_pager = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) { - - static const struct { - const char* verb; - const enum { - MORE, - LESS, - EQUAL - } argc_cmp; - const int argc; - int (* const dispatch)(sd_bus *bus, char **args, unsigned n); - } verbs[] = { - { "status", LESS, 1, show_status }, - { "set-time", EQUAL, 2, set_time }, - { "set-timezone", EQUAL, 2, set_timezone }, - { "list-timezones", EQUAL, 1, list_timezones }, - { "set-local-rtc", EQUAL, 2, set_local_rtc }, - { "set-ntp", EQUAL, 2, set_ntp, }, - }; - - int left; - unsigned i; - - assert(argc >= 0); - assert(argv); - - left = argc - optind; - - if (left <= 0) - /* Special rule: no arguments means "status" */ - i = 0; - else { - if (streq(argv[optind], "help")) { - help(); - return 0; - } - - for (i = 0; i < ELEMENTSOF(verbs); i++) - if (streq(argv[optind], verbs[i].verb)) - break; - - if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); - return -EINVAL; - } - } - - switch (verbs[i].argc_cmp) { - - case EQUAL: - if (left != verbs[i].argc) { - log_error("Invalid number of arguments."); - return -EINVAL; - } - - break; - - case MORE: - if (left < verbs[i].argc) { - log_error("Too few arguments."); - return -EINVAL; - } - - break; - - case LESS: - if (left > verbs[i].argc) { - log_error("Too many arguments."); - return -EINVAL; - } - - break; - - default: - assert_not_reached("Unknown comparison operator."); - } - - return verbs[i].dispatch(bus, argv + optind, left); -} - -int main(int argc, char *argv[]) { - sd_bus *bus = NULL; - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - 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; - } - - r = timedatectl_main(bus, argc, argv); - -finish: - sd_bus_flush_close_unref(bus); - pager_close(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c deleted file mode 100644 index ffec609c69..0000000000 --- a/src/timedate/timedated.c +++ /dev/null @@ -1,747 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "sd-bus.h" -#include "sd-event.h" -#include "sd-messages.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-error.h" -#include "bus-util.h" -#include "clock-util.h" -#include "def.h" -#include "fileio-label.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" - -static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = { - SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", EOPNOTSUPP), - SD_BUS_ERROR_MAP_END -}; - -typedef struct Context { - char *zone; - bool local_rtc; - bool can_ntp; - bool use_ntp; - Hashmap *polkit_registry; -} Context; - -static void context_free(Context *c) { - assert(c); - - free(c->zone); - bus_verify_polkit_async_registry_free(c->polkit_registry); -} - -static int context_read_data(Context *c) { - _cleanup_free_ char *t = NULL; - int r; - - assert(c); - - r = get_timezone(&t); - if (r == -EINVAL) - log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); - else if (r < 0) - log_warning_errno(r, "Failed to get target of /etc/localtime: %m"); - - free(c->zone); - c->zone = t; - t = NULL; - - c->local_rtc = clock_is_localtime(NULL) > 0; - - return 0; -} - -static int context_write_data_timezone(Context *c) { - _cleanup_free_ char *p = NULL; - int r = 0; - - assert(c); - - if (isempty(c->zone)) { - if (unlink("/etc/localtime") < 0 && errno != ENOENT) - r = -errno; - - return r; - } - - p = strappend("../usr/share/zoneinfo/", c->zone); - if (!p) - return log_oom(); - - r = symlink_atomic(p, "/etc/localtime"); - if (r < 0) - return r; - - return 0; -} - -static int context_write_data_local_rtc(Context *c) { - int r; - _cleanup_free_ char *s = NULL, *w = NULL; - - assert(c); - - r = read_full_file("/etc/adjtime", &s, NULL); - if (r < 0) { - if (r != -ENOENT) - return r; - - if (!c->local_rtc) - return 0; - - w = strdup(NULL_ADJTIME_LOCAL); - if (!w) - return -ENOMEM; - } else { - char *p; - const char *e = "\n"; /* default if there is less than 3 lines */ - const char *prepend = ""; - size_t a, b; - - p = strchrnul(s, '\n'); - if (*p == '\0') - /* only one line, no \n terminator */ - prepend = "\n0\n"; - else if (p[1] == '\0') { - /* only one line, with \n terminator */ - ++p; - prepend = "0\n"; - } else { - p = strchr(p+1, '\n'); - if (!p) { - /* only two lines, no \n terminator */ - prepend = "\n"; - p = s + strlen(s); - } else { - char *end; - /* third line might have a \n terminator or not */ - p++; - end = strchr(p, '\n'); - /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */ - if (end) - e = end; - } - } - - a = p - s; - b = strlen(e); - - w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1); - if (!w) - return -ENOMEM; - - *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; - - if (streq(w, NULL_ADJTIME_UTC)) { - if (unlink("/etc/adjtime") < 0) - if (errno != ENOENT) - return -errno; - - return 0; - } - } - - mac_selinux_init(); - return write_string_file_atomic_label("/etc/adjtime", w); -} - -static int context_read_ntp(Context *c, 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; - const char *s; - int r; - - assert(c); - assert(bus); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileState", - &error, - &reply, - "s", - "systemd-timesyncd.service"); - - if (r < 0) { - if (sd_bus_error_has_name(&error, SD_BUS_ERROR_FILE_NOT_FOUND) || - sd_bus_error_has_name(&error, "org.freedesktop.systemd1.LoadFailed") || - sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit")) - return 0; - - return r; - } - - r = sd_bus_message_read(reply, "s", &s); - if (r < 0) - return r; - - c->can_ntp = true; - c->use_ntp = STR_IN_SET(s, "enabled", "enabled-runtime"); - - return 0; -} - -static int context_start_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { - int r; - - assert(bus); - assert(error); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - enabled ? "StartUnit" : "StopUnit", - error, - NULL, - "ss", - "systemd-timesyncd.service", - "replace"); - if (r < 0) { - if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) || - sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") || - sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) - return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); - - return r; - } - - return 0; -} - -static int context_enable_ntp(sd_bus *bus, sd_bus_error *error, bool enabled) { - int r; - - assert(bus); - assert(error); - - if (enabled) - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "EnableUnitFiles", - error, - NULL, - "asbb", 1, - "systemd-timesyncd.service", - false, true); - else - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "DisableUnitFiles", - error, - NULL, - "asb", 1, - "systemd-timesyncd.service", - false); - - if (r < 0) { - if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) - return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); - - return r; - } - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reload", - error, - NULL, - NULL); - if (r < 0) - return r; - - return 0; -} - -static int property_get_rtc_time( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - struct tm tm; - usec_t t; - int r; - - zero(tm); - r = clock_get_hwclock(&tm); - if (r == -EBUSY) { - log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp."); - t = 0; - } else if (r == -ENOENT) { - log_debug("/dev/rtc not found."); - t = 0; /* no RTC found */ - } else if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m"); - else - t = (usec_t) timegm(&tm) * USEC_PER_SEC; - - return sd_bus_message_append(reply, "t", t); -} - -static int property_get_time( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - return sd_bus_message_append(reply, "t", now(CLOCK_REALTIME)); -} - -static int property_get_ntp_sync( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - return sd_bus_message_append(reply, "b", ntp_synced()); -} - -static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Context *c = userdata; - const char *z; - int interactive; - char *t; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "sb", &z, &interactive); - if (r < 0) - return r; - - if (!timezone_is_valid(z)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid time zone '%s'", z); - - if (streq_ptr(z, c->zone)) - return sd_bus_reply_method_return(m, NULL); - - r = bus_verify_polkit_async( - m, - CAP_SYS_TIME, - "org.freedesktop.timedate1.set-timezone", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - - t = strdup(z); - if (!t) - return -ENOMEM; - - free(c->zone); - c->zone = t; - - /* 1. Write new configuration file */ - r = context_write_data_timezone(c); - if (r < 0) { - log_error_errno(r, "Failed to set time zone: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %m"); - } - - /* 2. Tell the kernel our timezone */ - clock_set_timezone(NULL); - - if (c->local_rtc) { - struct timespec ts; - struct tm *tm; - - /* 3. Sync RTC from system clock, with the new delta */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - assert_se(tm = localtime(&ts.tv_sec)); - clock_set_hwclock(tm); - } - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), - "TIMEZONE=%s", c->zone, - LOG_MESSAGE("Changed time zone to '%s'.", c->zone), - NULL); - - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int lrtc, fix_system, interactive; - Context *c = userdata; - struct timespec ts; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "bbb", &lrtc, &fix_system, &interactive); - if (r < 0) - return r; - - if (lrtc == c->local_rtc) - return sd_bus_reply_method_return(m, NULL); - - r = bus_verify_polkit_async( - m, - CAP_SYS_TIME, - "org.freedesktop.timedate1.set-local-rtc", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; - - c->local_rtc = lrtc; - - /* 1. Write new configuration file */ - r = context_write_data_local_rtc(c); - if (r < 0) { - log_error_errno(r, "Failed to set RTC to local/UTC: %m"); - return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %m"); - } - - /* 2. Tell the kernel our timezone */ - clock_set_timezone(NULL); - - /* 3. Synchronize clocks */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - - if (fix_system) { - struct tm tm; - - /* Sync system clock from RTC; first, - * initialize the timezone fields of - * struct tm. */ - if (c->local_rtc) - tm = *localtime(&ts.tv_sec); - else - tm = *gmtime(&ts.tv_sec); - - /* Override the main fields of - * struct tm, but not the timezone - * fields */ - if (clock_get_hwclock(&tm) >= 0) { - - /* And set the system clock - * with this */ - if (c->local_rtc) - ts.tv_sec = mktime(&tm); - else - ts.tv_sec = timegm(&tm); - - clock_settime(CLOCK_REALTIME, &ts); - } - - } else { - struct tm *tm; - - /* Sync RTC from system clock */ - if (c->local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); - - clock_set_hwclock(tm); - } - - log_info("RTC configured to %s time.", c->local_rtc ? "local" : "UTC"); - - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "LocalRTC", NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int relative, interactive; - Context *c = userdata; - int64_t utc; - struct timespec ts; - usec_t start; - struct tm* tm; - int r; - - assert(m); - assert(c); - - if (c->use_ntp) - return sd_bus_error_setf(error, BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, "Automatic time synchronization is enabled"); - - /* this only gets used if dbus does not provide a timestamp */ - start = now(CLOCK_MONOTONIC); - - r = sd_bus_message_read(m, "xbb", &utc, &relative, &interactive); - if (r < 0) - return r; - - if (!relative && utc <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid absolute time"); - - if (relative && utc == 0) - return sd_bus_reply_method_return(m, NULL); - - if (relative) { - usec_t n, x; - - n = now(CLOCK_REALTIME); - x = n + utc; - - if ((utc > 0 && x < n) || - (utc < 0 && x > n)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Time value overflow"); - - timespec_store(&ts, x); - } else - timespec_store(&ts, (usec_t) utc); - - r = bus_verify_polkit_async( - m, - CAP_SYS_TIME, - "org.freedesktop.timedate1.set-time", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; - - /* adjust ts for time spent in program */ - r = sd_bus_message_get_monotonic_usec(m, &start); - /* when sd_bus_message_get_monotonic_usec() returns -ENODATA it does not modify &start */ - if (r < 0 && r != -ENODATA) - return r; - - timespec_store(&ts, timespec_load(&ts) + (now(CLOCK_MONOTONIC) - start)); - - /* Set system clock */ - if (clock_settime(CLOCK_REALTIME, &ts) < 0) { - log_error_errno(errno, "Failed to set local time: %m"); - return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m"); - } - - /* Sync down to RTC */ - if (c->local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); - clock_set_hwclock(tm); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), - "REALTIME="USEC_FMT, timespec_load(&ts), - LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)), - NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error) { - int enabled, interactive; - Context *c = userdata; - int r; - - assert(m); - assert(c); - - r = sd_bus_message_read(m, "bb", &enabled, &interactive); - if (r < 0) - return r; - - if ((bool)enabled == c->use_ntp) - return sd_bus_reply_method_return(m, NULL); - - r = bus_verify_polkit_async( - m, - CAP_SYS_TIME, - "org.freedesktop.timedate1.set-ntp", - NULL, - interactive, - UID_INVALID, - &c->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; - - r = context_enable_ntp(sd_bus_message_get_bus(m), error, enabled); - if (r < 0) - return r; - - r = context_start_ntp(sd_bus_message_get_bus(m), error, enabled); - if (r < 0) - return r; - - c->use_ntp = enabled; - log_info("Set NTP to %s", enabled ? "enabled" : "disabled"); - - (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); - - return sd_bus_reply_method_return(m, NULL); -} - -static const sd_bus_vtable timedate_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("CanNTP", "b", bus_property_get_bool, offsetof(Context, can_ntp), 0), - SD_BUS_PROPERTY("NTP", "b", bus_property_get_bool, offsetof(Context, use_ntp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0), - SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0), - SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0), - SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END, -}; - -static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(c); - assert(event); - assert(_bus); - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get system bus connection: %m"); - - r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(bus, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - *_bus = bus; - bus = NULL; - - return 0; -} - -int main(int argc, char *argv[]) { - Context context = {}; - _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); - log_parse_environment(); - log_open(); - - umask(0022); - - 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"); - goto finish; - } - - sd_event_set_watchdog(event, true); - - r = connect_bus(&context, event, &bus); - if (r < 0) - goto finish; - - (void) sd_bus_negotiate_timestamp(bus, true); - - r = context_read_data(&context); - if (r < 0) { - log_error_errno(r, "Failed to read time zone data: %m"); - goto finish; - } - - r = context_read_ntp(&context, bus); - if (r < 0) { - log_error_errno(r, "Failed to determine whether NTP is enabled: %m"); - goto finish; - } - - r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - -finish: - context_free(&context); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/timesync/.gitignore b/src/timesync/.gitignore deleted file mode 100644 index 35f4d76f79..0000000000 --- a/src/timesync/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/timesyncd.conf -/timesyncd-gperf.c diff --git a/src/timesync/Makefile b/src/timesync/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/timesync/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c deleted file mode 100644 index 20c64a3354..0000000000 --- a/src/timesync/timesyncd-conf.c +++ /dev/null @@ -1,106 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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" - -int manager_parse_server_string(Manager *m, ServerType type, const char *string) { - ServerName *first; - int r; - - assert(m); - assert(string); - - first = type == SERVER_FALLBACK ? m->fallback_servers : m->system_servers; - - for (;;) { - _cleanup_free_ char *word = NULL; - bool found = false; - ServerName *n; - - 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, word)) { - found = true; - break; - } - - if (found) - continue; - - r = server_name_new(m, NULL, type, word); - if (r < 0) - return r; - } - - return 0; -} - -int config_parse_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) { - - Manager *m = userdata; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - if (isempty(rvalue)) - manager_flush_server_names(m, ltype); - 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); - return 0; - } - } - - return 0; -} - -int manager_parse_config_file(Manager *m) { - assert(m); - - 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 deleted file mode 100644 index cba0724b1b..0000000000 --- a/src/timesync/timesyncd-conf.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "conf-parser.h" -#include "timesyncd-manager.h" - -const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length); - -int manager_parse_server_string(Manager *m, ServerType type, const char *string); - -int config_parse_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 manager_parse_config_file(Manager *m); diff --git a/src/timesync/timesyncd-gperf.gperf b/src/timesync/timesyncd-gperf.gperf deleted file mode 100644 index 29a2cfeef6..0000000000 --- a/src/timesync/timesyncd-gperf.gperf +++ /dev/null @@ -1,19 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "timesyncd-conf.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name timesyncdd_gperf_hash -%define lookup-function-name timesyncd_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Time.NTP, config_parse_servers, SERVER_SYSTEM, 0 -Time.Servers, config_parse_servers, SERVER_SYSTEM, 0 -Time.FallbackNTP, config_parse_servers, SERVER_FALLBACK, 0 diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c deleted file mode 100644 index d5e16db3a0..0000000000 --- a/src/timesync/timesyncd-manager.c +++ /dev/null @@ -1,1156 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "time-util.h" -#include "timesyncd-conf.h" -#include "timesyncd-manager.h" -#include "util.h" - -#ifndef ADJ_SETOFFSET -#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ -#endif - -/* expected accuracy of time synchronization; used to adjust the poll interval */ -#define NTP_ACCURACY_SEC 0.2 - -/* - * "A client MUST NOT under any conditions use a poll interval less - * than 15 seconds." - */ -#define NTP_POLL_INTERVAL_MIN_SEC 32 -#define NTP_POLL_INTERVAL_MAX_SEC 2048 - -/* - * Maximum delta in seconds which the system clock is gradually adjusted - * (slew) to approach the network time. Deltas larger that this are set by - * letting the system time jump. The kernel's limit for adjtime is 0.5s. - */ -#define NTP_MAX_ADJUST 0.4 - -/* NTP protocol, packet header */ -#define NTP_LEAP_PLUSSEC 1 -#define NTP_LEAP_MINUSSEC 2 -#define NTP_LEAP_NOTINSYNC 3 -#define NTP_MODE_CLIENT 3 -#define NTP_MODE_SERVER 4 -#define NTP_FIELD_LEAP(f) (((f) >> 6) & 3) -#define NTP_FIELD_VERSION(f) (((f) >> 3) & 7) -#define NTP_FIELD_MODE(f) ((f) & 7) -#define NTP_FIELD(l, v, m) (((l) << 6) | ((v) << 3) | (m)) - -/* Maximum acceptable root distance in seconds. */ -#define NTP_MAX_ROOT_DISTANCE 5.0 - -/* Maximum number of missed replies before selecting another source. */ -#define NTP_MAX_MISSED_REPLIES 2 - -/* - * "NTP timestamps are represented as a 64-bit unsigned fixed-point number, - * in seconds relative to 0h on 1 January 1900." - */ -#define OFFSET_1900_1970 UINT64_C(2208988800) - -#define RETRY_USEC (30*USEC_PER_SEC) -#define RATELIMIT_INTERVAL_USEC (10*USEC_PER_SEC) -#define RATELIMIT_BURST 10 - -#define TIMEOUT_USEC (10*USEC_PER_SEC) - -struct ntp_ts { - be32_t sec; - be32_t frac; -} _packed_; - -struct ntp_ts_short { - be16_t sec; - be16_t frac; -} _packed_; - -struct ntp_msg { - uint8_t field; - uint8_t stratum; - int8_t poll; - int8_t precision; - struct ntp_ts_short root_delay; - struct ntp_ts_short root_dispersion; - char refid[4]; - struct ntp_ts reference_time; - struct ntp_ts origin_time; - struct ntp_ts recv_time; - struct ntp_ts trans_time; -} _packed_; - -static int manager_arm_timer(Manager *m, usec_t next); -static int manager_clock_watch_setup(Manager *m); -static int manager_listen_setup(Manager *m); -static void manager_listen_stop(Manager *m); - -static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) { - return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0); -} - -static double ntp_ts_to_d(const struct ntp_ts *ts) { - return be32toh(ts->sec) + ((double)be32toh(ts->frac) / UINT_MAX); -} - -static double ts_to_d(const struct timespec *ts) { - return ts->tv_sec + (1.0e-9 * ts->tv_nsec); -} - -static int manager_timeout(sd_event_source *source, usec_t usec, void *userdata) { - _cleanup_free_ char *pretty = NULL; - Manager *m = userdata; - - assert(m); - assert(m->current_server_name); - assert(m->current_server_address); - - server_address_pretty(m->current_server_address, &pretty); - log_info("Timed out waiting for reply from %s (%s).", strna(pretty), m->current_server_name->string); - - return manager_connect(m); -} - -static int manager_send_request(Manager *m) { - _cleanup_free_ char *pretty = NULL; - struct ntp_msg ntpmsg = { - /* - * "The client initializes the NTP message header, sends the request - * to the server, and strips the time of day from the Transmit - * Timestamp field of the reply. For this purpose, all the NTP - * header fields are set to 0, except the Mode, VN, and optional - * Transmit Timestamp fields." - */ - .field = NTP_FIELD(0, 4, NTP_MODE_CLIENT), - }; - ssize_t len; - int r; - - assert(m); - assert(m->current_server_name); - assert(m->current_server_address); - - m->event_timeout = sd_event_source_unref(m->event_timeout); - - r = manager_listen_setup(m); - if (r < 0) - return log_warning_errno(r, "Failed to setup connection socket: %m"); - - /* - * Set transmit timestamp, remember it; the server will send that back - * as the origin timestamp and we have an indication that this is the - * matching answer to our request. - * - * The actual value does not matter, We do not care about the correct - * NTP UINT_MAX fraction; we just pass the plain nanosecond value. - */ - assert_se(clock_gettime(clock_boottime_or_monotonic(), &m->trans_time_mon) >= 0); - assert_se(clock_gettime(CLOCK_REALTIME, &m->trans_time) >= 0); - ntpmsg.trans_time.sec = htobe32(m->trans_time.tv_sec + OFFSET_1900_1970); - ntpmsg.trans_time.frac = htobe32(m->trans_time.tv_nsec); - - server_address_pretty(m->current_server_address, &pretty); - - len = sendto(m->server_socket, &ntpmsg, sizeof(ntpmsg), MSG_DONTWAIT, &m->current_server_address->sockaddr.sa, m->current_server_address->socklen); - if (len == sizeof(ntpmsg)) { - m->pending = true; - log_debug("Sent NTP request to %s (%s).", strna(pretty), m->current_server_name->string); - } else { - log_debug_errno(errno, "Sending NTP request to %s (%s) failed: %m", strna(pretty), m->current_server_name->string); - return manager_connect(m); - } - - /* re-arm timer with increasing timeout, in case the packets never arrive back */ - if (m->retry_interval > 0) { - if (m->retry_interval < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) - m->retry_interval *= 2; - } else - m->retry_interval = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; - - r = manager_arm_timer(m, m->retry_interval); - if (r < 0) - return log_error_errno(r, "Failed to rearm timer: %m"); - - m->missed_replies++; - if (m->missed_replies > NTP_MAX_MISSED_REPLIES) { - r = sd_event_add_time( - m->event, - &m->event_timeout, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + TIMEOUT_USEC, 0, - manager_timeout, m); - if (r < 0) - return log_error_errno(r, "Failed to arm timeout timer: %m"); - } - - return 0; -} - -static int manager_timer(sd_event_source *source, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(m); - - return manager_send_request(m); -} - -static int manager_arm_timer(Manager *m, usec_t next) { - int r; - - assert(m); - - if (next == 0) { - m->event_timer = sd_event_source_unref(m->event_timer); - return 0; - } - - if (m->event_timer) { - r = sd_event_source_set_time(m->event_timer, now(clock_boottime_or_monotonic()) + next); - if (r < 0) - return r; - - return sd_event_source_set_enabled(m->event_timer, SD_EVENT_ONESHOT); - } - - return sd_event_add_time( - m->event, - &m->event_timer, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + next, 0, - manager_timer, m); -} - -static int manager_clock_watch(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - - assert(m); - - /* rearm timer */ - manager_clock_watch_setup(m); - - /* skip our own jumps */ - if (m->jumped) { - m->jumped = false; - return 0; - } - - /* resync */ - log_debug("System time changed. Resyncing."); - m->poll_resync = true; - - return manager_send_request(m); -} - -/* wake up when the system time changes underneath us */ -static int manager_clock_watch_setup(Manager *m) { - - struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX - }; - - int r; - - assert(m); - - m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); - safe_close(m->clock_watch_fd); - - m->clock_watch_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (m->clock_watch_fd < 0) - return log_error_errno(errno, "Failed to create timerfd: %m"); - - if (timerfd_settime(m->clock_watch_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) - return log_error_errno(errno, "Failed to set up timerfd: %m"); - - r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m); - if (r < 0) - return log_error_errno(r, "Failed to create clock watch event source: %m"); - - return 0; -} - -static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { - struct timex tmx = {}; - int r; - - assert(m); - - /* - * For small deltas, tell the kernel to gradually adjust the system - * clock to the NTP time, larger deltas are just directly set. - */ - if (fabs(offset) < NTP_MAX_ADJUST) { - tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; - tmx.status = STA_PLL; - tmx.offset = offset * NSEC_PER_SEC; - tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4; - tmx.maxerror = 0; - tmx.esterror = 0; - log_debug(" adjust (slew): %+.3f sec", offset); - } else { - tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; - - /* ADJ_NANO uses nanoseconds in the microseconds field */ - tmx.time.tv_sec = (long)offset; - tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; - - /* the kernel expects -0.3s as {-1, 7000.000.000} */ - if (tmx.time.tv_usec < 0) { - tmx.time.tv_sec -= 1; - tmx.time.tv_usec += NSEC_PER_SEC; - } - - m->jumped = true; - log_debug(" adjust (jump): %+.3f sec", offset); - } - - /* - * An unset STA_UNSYNC will enable the kernel's 11-minute mode, - * which syncs the system time periodically to the RTC. - * - * In case the RTC runs in local time, never touch the RTC, - * we have no way to properly handle daylight saving changes and - * mobile devices moving between time zones. - */ - if (m->rtc_local_time) - tmx.status |= STA_UNSYNC; - - switch (leap_sec) { - case 1: - tmx.status |= STA_INS; - break; - case -1: - tmx.status |= STA_DEL; - break; - } - - r = clock_adjtime(CLOCK_REALTIME, &tmx); - if (r < 0) - return -errno; - - /* 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; - - log_debug(" status : %04i %s\n" - " time now : %li.%03llu\n" - " constant : %li\n" - " offset : %+.3f sec\n" - " freq offset : %+li (%i ppm)\n", - tmx.status, tmx.status & STA_UNSYNC ? "unsync" : "sync", - tmx.time.tv_sec, (unsigned long long) (tmx.time.tv_usec / NSEC_PER_MSEC), - tmx.constant, - (double)tmx.offset / NSEC_PER_SEC, - tmx.freq, m->drift_ppm); - - return 0; -} - -static bool manager_sample_spike_detection(Manager *m, double offset, double delay) { - unsigned int i, idx_cur, idx_new, idx_min; - double jitter; - double j; - - assert(m); - - m->packet_count++; - - /* ignore initial sample */ - if (m->packet_count == 1) - return false; - - /* store the current data in our samples array */ - idx_cur = m->samples_idx; - idx_new = (idx_cur + 1) % ELEMENTSOF(m->samples); - m->samples_idx = idx_new; - m->samples[idx_new].offset = offset; - m->samples[idx_new].delay = delay; - - /* calculate new jitter value from the RMS differences relative to the lowest delay sample */ - jitter = m->samples_jitter; - for (idx_min = idx_cur, i = 0; i < ELEMENTSOF(m->samples); i++) - if (m->samples[i].delay > 0 && m->samples[i].delay < m->samples[idx_min].delay) - idx_min = i; - - j = 0; - for (i = 0; i < ELEMENTSOF(m->samples); i++) - j += pow(m->samples[i].offset - m->samples[idx_min].offset, 2); - m->samples_jitter = sqrt(j / (ELEMENTSOF(m->samples) - 1)); - - /* ignore samples when resyncing */ - if (m->poll_resync) - return false; - - /* always accept offset if we are farther off than the round-trip delay */ - if (fabs(offset) > delay) - return false; - - /* we need a few samples before looking at them */ - if (m->packet_count < 4) - return false; - - /* do not accept anything worse than the maximum possible error of the best sample */ - if (fabs(offset) > m->samples[idx_min].delay) - return true; - - /* compare the difference between the current offset to the previous offset and jitter */ - return fabs(offset - m->samples[idx_cur].offset) > 3 * jitter; -} - -static void manager_adjust_poll(Manager *m, double offset, bool spike) { - assert(m); - - if (m->poll_resync) { - m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; - m->poll_resync = false; - return; - } - - /* set to minimal poll interval */ - if (!spike && fabs(offset) > NTP_ACCURACY_SEC) { - m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; - return; - } - - /* increase polling interval */ - if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) { - if (m->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) - m->poll_interval_usec *= 2; - return; - } - - /* decrease polling interval */ - if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) { - if (m->poll_interval_usec > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC) - m->poll_interval_usec /= 2; - return; - } -} - -static int manager_receive_response(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - struct ntp_msg ntpmsg; - - struct iovec iov = { - .iov_base = &ntpmsg, - .iov_len = sizeof(ntpmsg), - }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct timeval))]; - } control; - union sockaddr_union server_addr; - struct msghdr msghdr = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_name = &server_addr, - .msg_namelen = sizeof(server_addr), - }; - struct cmsghdr *cmsg; - struct timespec *recv_time; - ssize_t len; - double origin, receive, trans, dest; - double delay, offset; - double root_distance; - bool spike; - int leap_sec; - int r; - - assert(source); - assert(m); - - if (revents & (EPOLLHUP|EPOLLERR)) { - log_warning("Server connection returned error."); - return manager_connect(m); - } - - len = recvmsg(fd, &msghdr, MSG_DONTWAIT); - if (len < 0) { - if (errno == EAGAIN) - return 0; - - log_warning("Error receiving message. Disconnecting."); - return manager_connect(m); - } - - /* Too short or too long packet? */ - if (iov.iov_len < sizeof(struct ntp_msg) || (msghdr.msg_flags & MSG_TRUNC)) { - log_warning("Invalid response from server. Disconnecting."); - return manager_connect(m); - } - - if (!m->current_server_name || - !m->current_server_address || - !sockaddr_equal(&server_addr, &m->current_server_address->sockaddr)) { - log_debug("Response from unknown server."); - return 0; - } - - recv_time = NULL; - CMSG_FOREACH(cmsg, &msghdr) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - - switch (cmsg->cmsg_type) { - case SCM_TIMESTAMPNS: - recv_time = (struct timespec *) CMSG_DATA(cmsg); - break; - } - } - if (!recv_time) { - log_error("Invalid packet timestamp."); - return -EINVAL; - } - - if (!m->pending) { - log_debug("Unexpected reply. Ignoring."); - return 0; - } - - m->missed_replies = 0; - - /* check our "time cookie" (we just stored nanoseconds in the fraction field) */ - if (be32toh(ntpmsg.origin_time.sec) != m->trans_time.tv_sec + OFFSET_1900_1970 || - be32toh(ntpmsg.origin_time.frac) != m->trans_time.tv_nsec) { - log_debug("Invalid reply; not our transmit time. Ignoring."); - return 0; - } - - m->event_timeout = sd_event_source_unref(m->event_timeout); - - if (be32toh(ntpmsg.recv_time.sec) < TIME_EPOCH + OFFSET_1900_1970 || - be32toh(ntpmsg.trans_time.sec) < TIME_EPOCH + OFFSET_1900_1970) { - log_debug("Invalid reply, returned times before epoch. Ignoring."); - return manager_connect(m); - } - - if (NTP_FIELD_LEAP(ntpmsg.field) == NTP_LEAP_NOTINSYNC || - ntpmsg.stratum == 0 || ntpmsg.stratum >= 16) { - log_debug("Server is not synchronized. Disconnecting."); - return manager_connect(m); - } - - if (!IN_SET(NTP_FIELD_VERSION(ntpmsg.field), 3, 4)) { - log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg.field)); - return manager_connect(m); - } - - if (NTP_FIELD_MODE(ntpmsg.field) != NTP_MODE_SERVER) { - log_debug("Unsupported mode %d. Disconnecting.", NTP_FIELD_MODE(ntpmsg.field)); - return manager_connect(m); - } - - root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 + ntp_ts_short_to_d(&ntpmsg.root_dispersion); - if (root_distance > NTP_MAX_ROOT_DISTANCE) { - log_debug("Server has too large root distance. Disconnecting."); - return manager_connect(m); - } - - /* valid packet */ - m->pending = false; - m->retry_interval = 0; - - /* Stop listening */ - manager_listen_stop(m); - - /* announce leap seconds */ - if (NTP_FIELD_LEAP(ntpmsg.field) & NTP_LEAP_PLUSSEC) - leap_sec = 1; - else if (NTP_FIELD_LEAP(ntpmsg.field) & NTP_LEAP_MINUSSEC) - leap_sec = -1; - else - leap_sec = 0; - - /* - * "Timestamp Name ID When Generated - * ------------------------------------------------------------ - * Originate Timestamp T1 time request sent by client - * Receive Timestamp T2 time request received by server - * Transmit Timestamp T3 time reply sent by server - * Destination Timestamp T4 time reply received by client - * - * The round-trip delay, d, and system clock offset, t, are defined as: - * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2" - */ - origin = ts_to_d(&m->trans_time) + OFFSET_1900_1970; - receive = ntp_ts_to_d(&ntpmsg.recv_time); - trans = ntp_ts_to_d(&ntpmsg.trans_time); - dest = ts_to_d(recv_time) + OFFSET_1900_1970; - - offset = ((receive - origin) + (trans - dest)) / 2; - delay = (dest - origin) - (trans - receive); - - spike = manager_sample_spike_detection(m, offset, delay); - - manager_adjust_poll(m, offset, spike); - - log_debug("NTP response:\n" - " leap : %u\n" - " version : %u\n" - " mode : %u\n" - " stratum : %u\n" - " precision : %.6f sec (%d)\n" - " root distance: %.6f sec\n" - " reference : %.4s\n" - " origin : %.3f\n" - " receive : %.3f\n" - " transmit : %.3f\n" - " dest : %.3f\n" - " offset : %+.3f sec\n" - " delay : %+.3f sec\n" - " packet count : %"PRIu64"\n" - " jitter : %.3f%s\n" - " poll interval: " USEC_FMT "\n", - NTP_FIELD_LEAP(ntpmsg.field), - NTP_FIELD_VERSION(ntpmsg.field), - NTP_FIELD_MODE(ntpmsg.field), - ntpmsg.stratum, - exp2(ntpmsg.precision), ntpmsg.precision, - root_distance, - ntpmsg.stratum == 1 ? ntpmsg.refid : "n/a", - origin - OFFSET_1900_1970, - receive - OFFSET_1900_1970, - trans - OFFSET_1900_1970, - dest - OFFSET_1900_1970, - offset, delay, - m->packet_count, - m->samples_jitter, spike ? " spike" : "", - m->poll_interval_usec / USEC_PER_SEC); - - if (!spike) { - m->sync = true; - r = manager_adjust_clock(m, offset, leap_sec); - if (r < 0) - 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", - m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_ppm, - spike ? " (ignored)" : ""); - - if (!m->good) { - _cleanup_free_ char *pretty = NULL; - - m->good = true; - - server_address_pretty(m->current_server_address, &pretty); - log_info("Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string); - sd_notifyf(false, "STATUS=Synchronized to time server %s (%s).", strna(pretty), m->current_server_name->string); - } - - r = manager_arm_timer(m, m->poll_interval_usec); - if (r < 0) - return log_error_errno(r, "Failed to rearm timer: %m"); - - return 0; -} - -static int manager_listen_setup(Manager *m) { - union sockaddr_union addr = {}; - static const int tos = IPTOS_LOWDELAY; - static const int on = 1; - int r; - - assert(m); - - if (m->server_socket >= 0) - return 0; - - assert(!m->event_receive); - assert(m->current_server_address); - - addr.sa.sa_family = m->current_server_address->sockaddr.sa.sa_family; - - m->server_socket = socket(addr.sa.sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (m->server_socket < 0) - return -errno; - - r = bind(m->server_socket, &addr.sa, m->current_server_address->socklen); - if (r < 0) - return -errno; - - r = setsockopt(m->server_socket, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on)); - if (r < 0) - return -errno; - - (void) setsockopt(m->server_socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); - - return sd_event_add_io(m->event, &m->event_receive, m->server_socket, EPOLLIN, manager_receive_response, m); -} - -static void manager_listen_stop(Manager *m) { - assert(m); - - m->event_receive = sd_event_source_unref(m->event_receive); - m->server_socket = safe_close(m->server_socket); -} - -static int manager_begin(Manager *m) { - _cleanup_free_ char *pretty = NULL; - int r; - - assert(m); - assert_return(m->current_server_name, -EHOSTUNREACH); - assert_return(m->current_server_address, -EHOSTUNREACH); - - m->good = false; - m->missed_replies = NTP_MAX_MISSED_REPLIES; - if (m->poll_interval_usec == 0) - m->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; - - server_address_pretty(m->current_server_address, &pretty); - log_debug("Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); - sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); - - r = manager_clock_watch_setup(m); - if (r < 0) - return r; - - return manager_send_request(m); -} - -void manager_set_server_name(Manager *m, ServerName *n) { - assert(m); - - if (m->current_server_name == n) - return; - - m->current_server_name = n; - m->current_server_address = NULL; - - manager_disconnect(m); - - if (n) - log_debug("Selected server %s.", n->string); -} - -void manager_set_server_address(Manager *m, ServerAddress *a) { - assert(m); - - if (m->current_server_address == a) - return; - - m->current_server_address = a; - /* If a is NULL, we are just clearing the address, without - * changing the name. Keep the existing name in that case. */ - if (a) - m->current_server_name = a->name; - - manager_disconnect(m); - - if (a) { - _cleanup_free_ char *pretty = NULL; - server_address_pretty(a, &pretty); - log_debug("Selected address %s of server %s.", strna(pretty), a->name->string); - } -} - -static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata) { - Manager *m = userdata; - int r; - - assert(q); - assert(m); - assert(m->current_server_name); - - m->resolve_query = sd_resolve_query_unref(m->resolve_query); - - if (ret != 0) { - log_debug("Failed to resolve %s: %s", m->current_server_name->string, gai_strerror(ret)); - - /* Try next host */ - return manager_connect(m); - } - - for (; ai; ai = ai->ai_next) { - _cleanup_free_ char *pretty = NULL; - ServerAddress *a; - - assert(ai->ai_addr); - assert(ai->ai_addrlen >= offsetof(struct sockaddr, sa_data)); - - if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6)) { - log_warning("Unsuitable address protocol for %s", m->current_server_name->string); - continue; - } - - r = server_address_new(m->current_server_name, &a, (const union sockaddr_union*) ai->ai_addr, ai->ai_addrlen); - if (r < 0) - return log_error_errno(r, "Failed to add server address: %m"); - - server_address_pretty(a, &pretty); - log_debug("Resolved address %s for %s.", pretty, m->current_server_name->string); - } - - if (!m->current_server_name->addresses) { - log_error("Failed to find suitable address for host %s.", m->current_server_name->string); - - /* Try next host */ - return manager_connect(m); - } - - manager_set_server_address(m, m->current_server_name->addresses); - - return manager_begin(m); -} - -static int manager_retry_connect(sd_event_source *source, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(m); - - return manager_connect(m); -} - -int manager_connect(Manager *m) { - int r; - - assert(m); - - manager_disconnect(m); - - m->event_retry = sd_event_source_unref(m->event_retry); - if (!ratelimit_test(&m->ratelimit)) { - log_debug("Slowing down attempts to contact servers."); - - r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + RETRY_USEC, 0, manager_retry_connect, m); - if (r < 0) - return log_error_errno(r, "Failed to create retry timer: %m"); - - return 0; - } - - /* If we already are operating on some address, switch to the - * next one. */ - if (m->current_server_address && m->current_server_address->addresses_next) - manager_set_server_address(m, m->current_server_address->addresses_next); - else { - struct addrinfo hints = { - .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, - .ai_socktype = SOCK_DGRAM, - }; - - /* Hmm, we are through all addresses, let's look for the next host instead */ - if (m->current_server_name && m->current_server_name->names_next) - manager_set_server_name(m, m->current_server_name->names_next); - else { - ServerName *f; - bool restart = true; - - /* Our current server name list is exhausted, - * let's find the next one to iterate. First - * we try the system list, then the link list. - * After having processed the link list we - * jump back to the system list. However, if - * both lists are empty, we change to the - * fallback list. */ - if (!m->current_server_name || m->current_server_name->type == SERVER_LINK) { - f = m->system_servers; - if (!f) - f = m->link_servers; - } else { - f = m->link_servers; - if (!f) - f = m->system_servers; - else - restart = false; - } - - if (!f) - f = m->fallback_servers; - - if (!f) { - manager_set_server_name(m, NULL); - log_debug("No server found."); - return 0; - } - - if (restart && !m->exhausted_servers && m->poll_interval_usec) { - log_debug("Waiting after exhausting servers."); - r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + m->poll_interval_usec, 0, manager_retry_connect, m); - if (r < 0) - return log_error_errno(r, "Failed to create retry timer: %m"); - - m->exhausted_servers = true; - - /* Increase the polling interval */ - if (m->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) - m->poll_interval_usec *= 2; - - return 0; - } - - m->exhausted_servers = false; - - manager_set_server_name(m, f); - } - - /* Tell the resolver to reread /etc/resolv.conf, in - * case it changed. */ - res_init(); - - /* Flush out any previously resolved addresses */ - server_name_flush_addresses(m->current_server_name); - - log_debug("Resolving %s...", m->current_server_name->string); - - r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, m); - if (r < 0) - return log_error_errno(r, "Failed to create resolver: %m"); - - return 1; - } - - r = manager_begin(m); - if (r < 0) - return r; - - return 1; -} - -void manager_disconnect(Manager *m) { - assert(m); - - m->resolve_query = sd_resolve_query_unref(m->resolve_query); - - m->event_timer = sd_event_source_unref(m->event_timer); - - manager_listen_stop(m); - - m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); - m->clock_watch_fd = safe_close(m->clock_watch_fd); - - m->event_timeout = sd_event_source_unref(m->event_timeout); - - sd_notifyf(false, "STATUS=Idle."); -} - -void manager_flush_server_names(Manager *m, ServerType t) { - assert(m); - - if (t == SERVER_SYSTEM) - while (m->system_servers) - server_name_free(m->system_servers); - - if (t == SERVER_LINK) - while (m->link_servers) - server_name_free(m->link_servers); - - if (t == SERVER_FALLBACK) - while (m->fallback_servers) - server_name_free(m->fallback_servers); -} - -void manager_free(Manager *m) { - if (!m) - return; - - manager_disconnect(m); - manager_flush_server_names(m, SERVER_SYSTEM); - manager_flush_server_names(m, SERVER_LINK); - manager_flush_server_names(m, SERVER_FALLBACK); - - sd_event_source_unref(m->event_retry); - - sd_event_source_unref(m->network_event_source); - sd_network_monitor_unref(m->network_monitor); - - sd_resolve_unref(m->resolve); - sd_event_unref(m->event); - - free(m); -} - -static int manager_network_read_link_servers(Manager *m) { - _cleanup_strv_free_ char **ntp = NULL; - ServerName *n, *nx; - char **i; - int r; - - assert(m); - - r = sd_network_get_ntp(&ntp); - if (r < 0) - goto clear; - - LIST_FOREACH(names, n, m->link_servers) - n->marked = true; - - STRV_FOREACH(i, ntp) { - bool found = false; - - LIST_FOREACH(names, n, m->link_servers) - if (streq(n->string, *i)) { - n->marked = false; - found = true; - break; - } - - if (!found) { - r = server_name_new(m, NULL, SERVER_LINK, *i); - if (r < 0) - goto clear; - } - } - - LIST_FOREACH_SAFE(names, n, nx, m->link_servers) - if (n->marked) - server_name_free(n); - - return 0; - -clear: - manager_flush_server_names(m, SERVER_LINK); - return r; -} - -static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *m = userdata; - bool connected, online; - int r; - - assert(m); - - sd_network_monitor_flush(m->network_monitor); - - manager_network_read_link_servers(m); - - /* check if the machine is online */ - online = network_is_online(); - - /* check if the client is currently connected */ - connected = m->server_socket >= 0 || m->resolve_query || m->exhausted_servers; - - if (connected && !online) { - log_info("No network connectivity, watching for changes."); - manager_disconnect(m); - - } else if (!connected && online) { - log_info("Network configuration changed, trying to establish connection."); - - if (m->current_server_address) - r = manager_begin(m); - else - r = manager_connect(m); - if (r < 0) - return r; - } - - return 0; -} - -static int manager_network_monitor_listen(Manager *m) { - int r, fd, events; - - assert(m); - - r = sd_network_monitor_new(&m->network_monitor, NULL); - if (r < 0) - return r; - - fd = sd_network_monitor_get_fd(m->network_monitor); - if (fd < 0) - return fd; - - events = sd_network_monitor_get_events(m->network_monitor); - if (events < 0) - return events; - - r = sd_event_add_io(m->event, &m->network_event_source, fd, events, manager_network_event_handler, m); - if (r < 0) - return r; - - return 0; -} - -int manager_new(Manager **ret) { - _cleanup_(manager_freep) Manager *m = NULL; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - m->server_socket = m->clock_watch_fd = -1; - - RATELIMIT_INIT(m->ratelimit, RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST); - - r = manager_parse_server_string(m, SERVER_FALLBACK, NTP_SERVERS); - if (r < 0) - return r; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); - sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); - - sd_event_set_watchdog(m->event, true); - - r = sd_resolve_default(&m->resolve); - if (r < 0) - return r; - - r = sd_resolve_attach_event(m->resolve, m->event, 0); - if (r < 0) - return r; - - r = manager_network_monitor_listen(m); - if (r < 0) - return r; - - manager_network_read_link_servers(m); - - *ret = m; - m = NULL; - - return 0; -} diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h deleted file mode 100644 index efe3e60d3e..0000000000 --- a/src/timesync/timesyncd-manager.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-event.h" -#include "sd-network.h" -#include "sd-resolve.h" - -#include "list.h" -#include "ratelimit.h" - -typedef struct Manager Manager; - -#include "timesyncd-server.h" - -struct Manager { - sd_event *event; - sd_resolve *resolve; - - LIST_HEAD(ServerName, system_servers); - LIST_HEAD(ServerName, link_servers); - LIST_HEAD(ServerName, fallback_servers); - - RateLimit ratelimit; - bool exhausted_servers; - - /* network */ - sd_event_source *network_event_source; - sd_network_monitor *network_monitor; - - /* peer */ - sd_resolve_query *resolve_query; - sd_event_source *event_receive; - ServerName *current_server_name; - ServerAddress *current_server_address; - int server_socket; - int missed_replies; - uint64_t packet_count; - sd_event_source *event_timeout; - bool good; - - /* last sent packet */ - struct timespec trans_time_mon; - struct timespec trans_time; - usec_t retry_interval; - bool pending; - - /* poll timer */ - sd_event_source *event_timer; - usec_t poll_interval_usec; - bool poll_resync; - - /* history data */ - struct { - double offset; - double delay; - } samples[8]; - unsigned int samples_idx; - double samples_jitter; - - /* last change */ - bool jumped; - bool sync; - int drift_ppm; - - /* watch for time changes */ - sd_event_source *event_clock_watch; - int clock_watch_fd; - - /* Retry connections */ - sd_event_source *event_retry; - - /* RTC runs in local time, leave it alone */ - bool rtc_local_time; -}; - -int manager_new(Manager **ret); -void manager_free(Manager *m); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); - -void manager_set_server_name(Manager *m, ServerName *n); -void manager_set_server_address(Manager *m, ServerAddress *a); -void manager_flush_server_names(Manager *m, ServerType t); - -int manager_connect(Manager *m); -void manager_disconnect(Manager *m); diff --git a/src/timesync/timesyncd-server.c b/src/timesync/timesyncd-server.c deleted file mode 100644 index 6bda86fe6e..0000000000 --- a/src/timesync/timesyncd-server.c +++ /dev/null @@ -1,150 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "alloc-util.h" -#include "timesyncd-server.h" - -int server_address_new( - ServerName *n, - ServerAddress **ret, - const union sockaddr_union *sockaddr, - socklen_t socklen) { - - ServerAddress *a, *tail; - - assert(n); - assert(sockaddr); - assert(socklen >= offsetof(struct sockaddr, sa_data)); - assert(socklen <= sizeof(union sockaddr_union)); - - a = new0(ServerAddress, 1); - if (!a) - return -ENOMEM; - - memcpy(&a->sockaddr, sockaddr, socklen); - a->socklen = socklen; - - LIST_FIND_TAIL(addresses, n->addresses, tail); - LIST_INSERT_AFTER(addresses, n->addresses, tail, a); - a->name = n; - - if (ret) - *ret = a; - - return 0; -} - -ServerAddress* server_address_free(ServerAddress *a) { - if (!a) - return NULL; - - if (a->name) { - LIST_REMOVE(addresses, a->name->addresses, a); - - if (a->name->manager && a->name->manager->current_server_address == a) - manager_set_server_address(a->name->manager, NULL); - } - - free(a); - return NULL; -} - -int server_name_new( - Manager *m, - ServerName **ret, - ServerType type, - const char *string) { - - ServerName *n, *tail; - - assert(m); - assert(string); - - n = new0(ServerName, 1); - if (!n) - return -ENOMEM; - - n->type = type; - n->string = strdup(string); - if (!n->string) { - free(n); - return -ENOMEM; - } - - if (type == SERVER_SYSTEM) { - LIST_FIND_TAIL(names, m->system_servers, tail); - LIST_INSERT_AFTER(names, m->system_servers, tail, n); - } else if (type == SERVER_LINK) { - LIST_FIND_TAIL(names, m->link_servers, tail); - LIST_INSERT_AFTER(names, m->link_servers, tail, n); - } else if (type == SERVER_FALLBACK) { - LIST_FIND_TAIL(names, m->fallback_servers, tail); - LIST_INSERT_AFTER(names, m->fallback_servers, tail, n); - } else - assert_not_reached("Unknown server type"); - - n->manager = m; - - if (type != SERVER_FALLBACK && - m->current_server_name && - m->current_server_name->type == SERVER_FALLBACK) - manager_set_server_name(m, NULL); - - log_debug("Added new server %s.", string); - - if (ret) - *ret = n; - - return 0; -} - -ServerName *server_name_free(ServerName *n) { - if (!n) - return NULL; - - server_name_flush_addresses(n); - - if (n->manager) { - if (n->type == SERVER_SYSTEM) - LIST_REMOVE(names, n->manager->system_servers, n); - else if (n->type == SERVER_LINK) - LIST_REMOVE(names, n->manager->link_servers, n); - else if (n->type == SERVER_FALLBACK) - LIST_REMOVE(names, n->manager->fallback_servers, n); - else - assert_not_reached("Unknown server type"); - - if (n->manager->current_server_name == n) - manager_set_server_name(n->manager, NULL); - } - - log_debug("Removed server %s.", n->string); - - free(n->string); - free(n); - - return NULL; -} - -void server_name_flush_addresses(ServerName *n) { - assert(n); - - while (n->addresses) - server_address_free(n->addresses); -} diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h deleted file mode 100644 index 8a19e41d67..0000000000 --- a/src/timesync/timesyncd-server.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "list.h" -#include "socket-util.h" - -typedef struct ServerAddress ServerAddress; -typedef struct ServerName ServerName; - -typedef enum ServerType { - SERVER_SYSTEM, - SERVER_FALLBACK, - SERVER_LINK, -} ServerType; - -#include "timesyncd-manager.h" - -struct ServerAddress { - ServerName *name; - - union sockaddr_union sockaddr; - socklen_t socklen; - - LIST_FIELDS(ServerAddress, addresses); -}; - -struct ServerName { - Manager *manager; - - ServerType type; - char *string; - - bool marked:1; - - LIST_HEAD(ServerAddress, addresses); - LIST_FIELDS(ServerName, names); -}; - -int server_address_new(ServerName *n, ServerAddress **ret, const union sockaddr_union *sockaddr, socklen_t socklen); -ServerAddress* server_address_free(ServerAddress *a); -static inline int server_address_pretty(ServerAddress *a, char **pretty) { - return sockaddr_pretty(&a->sockaddr.sa, a->socklen, true, true, pretty); -} - -int server_name_new(Manager *m, ServerName **ret, ServerType type,const char *string); -ServerName *server_name_free(ServerName *n); -void server_name_flush_addresses(ServerName *n); diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c deleted file mode 100644 index b67d672a6a..0000000000 --- a/src/timesync/timesyncd.c +++ /dev/null @@ -1,164 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Kay Sievers, Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "sd-daemon.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-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; - usec_t min = TIME_EPOCH * USEC_PER_SEC; - usec_t ct; - int r; - - /* Let's try to make sure that the clock is always - * monotonically increasing, by saving the clock whenever we - * have a new NTP time, or when we shut down, and restoring it - * when we start again. This is particularly helpful on - * systems lacking a battery backed RTC. We also will adjust - * the time to at least the build time of systemd. */ - - fd = open("/var/lib/systemd/clock", O_RDWR|O_CLOEXEC, 0644); - if (fd >= 0) { - struct stat st; - usec_t stamp; - - /* check if the recorded time is later than the compiled-in one */ - r = fstat(fd, &st); - if (r >= 0) { - stamp = timespec_load(&st.st_mtim); - if (stamp > min) - min = stamp; - } - - /* Try to fix the access mode, so that we can still - touch the file after dropping priviliges */ - (void) fchmod(fd, 0644); - (void) fchown(fd, uid, gid); - - } else - /* create stamp file with the compiled-in date */ - (void) touch_file("/var/lib/systemd/clock", true, min, uid, gid, 0644); - - ct = now(CLOCK_REALTIME); - if (ct < min) { - struct timespec ts; - char date[FORMAT_TIMESTAMP_MAX]; - - log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s", - format_timestamp(date, sizeof(date), min)); - - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0) - log_error_errno(errno, "Failed to restore system clock: %m"); - } - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(manager_freep) Manager *m = NULL; - const char *user = "systemd-timesync"; - uid_t uid; - gid_t gid; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_set_facility(LOG_CRON); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program does not take arguments."); - r = -EINVAL; - goto finish; - } - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } - - r = load_clock_timestamp(uid, gid); - if (r < 0) - goto finish; - - r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME)); - if (r < 0) - goto finish; - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Failed to allocate manager: %m"); - goto finish; - } - - if (clock_is_localtime(NULL) > 0) { - log_info("The system is configured to read the RTC time in the local time zone. " - "This mode can not be fully supported. All system time to RTC updates are disabled."); - m->rtc_local_time = true; - } - - r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse configuration file: %m"); - - log_debug("systemd-timesyncd running as pid " PID_FMT, getpid()); - sd_notify(false, - "READY=1\n" - "STATUS=Daemon is running"); - - if (network_is_online()) { - r = manager_connect(m); - if (r < 0) - goto finish; - } - - r = sd_event_loop(m->event); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - - /* if we got an authoritative time, store it in the file system */ - if (m->sync) - (void) touch("/var/lib/systemd/clock"); - - sd_event_get_exit_code(m->event, &r); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/timesync/timesyncd.conf.in b/src/timesync/timesyncd.conf.in deleted file mode 100644 index b6a2ada273..0000000000 --- a/src/timesync/timesyncd.conf.in +++ /dev/null @@ -1,16 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See timesyncd.conf(5) for details. - -[Time] -#NTP= -#FallbackNTP=@NTP_SERVERS@ diff --git a/src/tmpfiles/Makefile b/src/tmpfiles/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/tmpfiles/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c deleted file mode 100644 index 954f4aa985..0000000000 --- a/src/tmpfiles/tmpfiles.c +++ /dev/null @@ -1,2342 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering, Kay Sievers - Copyright 2015 Zbigniew Jędrzejewski-Szmek - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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 "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "mount-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "rm-rf.h" -#include "selinux-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 - * properly owned directories beneath /tmp, /var/tmp, /run, which are - * volatile and hence need to be recreated on bootup. */ - -typedef enum ItemType { - /* These ones take file names */ - CREATE_FILE = 'f', - TRUNCATE_FILE = 'F', - CREATE_DIRECTORY = 'd', - TRUNCATE_DIRECTORY = 'D', - CREATE_SUBVOLUME = 'v', - CREATE_SUBVOLUME_INHERIT_QUOTA = 'q', - CREATE_SUBVOLUME_NEW_QUOTA = 'Q', - CREATE_FIFO = 'p', - CREATE_SYMLINK = 'L', - CREATE_CHAR_DEVICE = 'c', - CREATE_BLOCK_DEVICE = 'b', - COPY_FILES = 'C', - - /* These ones take globs */ - WRITE_FILE = 'w', - EMPTY_DIRECTORY = 'e', - SET_XATTR = 't', - RECURSIVE_SET_XATTR = 'T', - SET_ACL = 'a', - RECURSIVE_SET_ACL = 'A', - SET_ATTRIBUTE = 'h', - RECURSIVE_SET_ATTRIBUTE = 'H', - IGNORE_PATH = 'x', - IGNORE_DIRECTORY_PATH = 'X', - REMOVE_PATH = 'r', - RECURSIVE_REMOVE_PATH = 'R', - RELABEL_PATH = 'z', - RECURSIVE_RELABEL_PATH = 'Z', - ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */ -} ItemType; - -typedef struct Item { - ItemType type; - - char *path; - char *argument; - char **xattrs; -#ifdef HAVE_ACL - acl_t acl_access; - acl_t acl_default; -#endif - uid_t uid; - gid_t gid; - mode_t mode; - usec_t age; - - dev_t major_minor; - unsigned attribute_value; - unsigned attribute_mask; - - bool uid_set:1; - bool gid_set:1; - bool mode_set:1; - bool age_set:1; - bool mask_perms:1; - bool attribute_set:1; - - bool keep_first_level:1; - - bool force:1; - - bool done:1; -} Item; - -typedef struct ItemArray { - Item *items; - size_t count; - size_t size; -} ItemArray; - -static bool arg_create = false; -static bool arg_clean = false; -static bool arg_remove = false; -static bool arg_boot = false; - -static char **arg_include_prefixes = NULL; -static char **arg_exclude_prefixes = NULL; -static char *arg_root = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("tmpfiles.d"); - -#define MAX_DEPTH 256 - -static OrderedHashmap *items = NULL, *globs = NULL; -static Set *unix_sockets = NULL; - -static const Specifier specifier_table[] = { - { 'm', specifier_machine_id, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - {} -}; - -static bool needs_glob(ItemType t) { - return IN_SET(t, - WRITE_FILE, - IGNORE_PATH, - IGNORE_DIRECTORY_PATH, - REMOVE_PATH, - RECURSIVE_REMOVE_PATH, - EMPTY_DIRECTORY, - ADJUST_MODE, - RELABEL_PATH, - RECURSIVE_RELABEL_PATH, - SET_XATTR, - RECURSIVE_SET_XATTR, - SET_ACL, - RECURSIVE_SET_ACL, - SET_ATTRIBUTE, - RECURSIVE_SET_ATTRIBUTE); -} - -static bool takes_ownership(ItemType t) { - return IN_SET(t, - CREATE_FILE, - TRUNCATE_FILE, - CREATE_DIRECTORY, - EMPTY_DIRECTORY, - TRUNCATE_DIRECTORY, - CREATE_SUBVOLUME, - CREATE_SUBVOLUME_INHERIT_QUOTA, - CREATE_SUBVOLUME_NEW_QUOTA, - CREATE_FIFO, - CREATE_SYMLINK, - CREATE_CHAR_DEVICE, - CREATE_BLOCK_DEVICE, - COPY_FILES, - WRITE_FILE, - IGNORE_PATH, - IGNORE_DIRECTORY_PATH, - REMOVE_PATH, - RECURSIVE_REMOVE_PATH); -} - -static struct Item* find_glob(OrderedHashmap *h, const char *match) { - ItemArray *j; - Iterator i; - - ORDERED_HASHMAP_FOREACH(j, h, i) { - unsigned n; - - for (n = 0; n < j->count; n++) { - Item *item = j->items + n; - - if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0) - return item; - } - } - - return NULL; -} - -static void load_unix_sockets(void) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - - if (unix_sockets) - return; - - /* We maintain a cache of the sockets we found in - * /proc/net/unix to speed things up a little. */ - - unix_sockets = set_new(&string_hash_ops); - if (!unix_sockets) - return; - - f = fopen("/proc/net/unix", "re"); - if (!f) - return; - - /* Skip header */ - if (!fgets(line, sizeof(line), f)) - goto fail; - - for (;;) { - char *p, *s; - int k; - - if (!fgets(line, sizeof(line), f)) - break; - - truncate_nl(line); - - p = strchr(line, ':'); - if (!p) - continue; - - if (strlen(p) < 37) - continue; - - p += 37; - p += strspn(p, WHITESPACE); - p += strcspn(p, WHITESPACE); /* skip one more word */ - p += strspn(p, WHITESPACE); - - if (*p != '/') - continue; - - s = strdup(p); - if (!s) - goto fail; - - path_kill_slashes(s); - - k = set_consume(unix_sockets, s); - if (k < 0 && k != -EEXIST) - goto fail; - } - - return; - -fail: - set_free_free(unix_sockets); - unix_sockets = NULL; -} - -static bool unix_socket_alive(const char *fn) { - assert(fn); - - load_unix_sockets(); - - if (unix_sockets) - return !!set_get(unix_sockets, (char*) fn); - - /* We don't know, so assume yes */ - return true; -} - -static int dir_is_mount_point(DIR *d, const char *subdir) { - - union file_handle_union h = FILE_HANDLE_INIT; - int mount_id_parent, mount_id; - int r_p, r; - - r_p = name_to_handle_at(dirfd(d), ".", &h.handle, &mount_id_parent, 0); - if (r_p < 0) - r_p = -errno; - - h.handle.handle_bytes = MAX_HANDLE_SZ; - r = name_to_handle_at(dirfd(d), subdir, &h.handle, &mount_id, 0); - if (r < 0) - r = -errno; - - /* got no handle; make no assumptions, return error */ - if (r_p < 0 && r < 0) - return r_p; - - /* got both handles; if they differ, it is a mount point */ - if (r_p >= 0 && r >= 0) - return mount_id_parent != mount_id; - - /* got only one handle; assume different mount points if one - * of both queries was not supported by the filesystem */ - if (r_p == -ENOSYS || r_p == -EOPNOTSUPP || r == -ENOSYS || r == -EOPNOTSUPP) - return true; - - /* return error */ - if (r_p < 0) - return r_p; - return r; -} - -static DIR* xopendirat_nomod(int dirfd, const char *path) { - DIR *dir; - - dir = xopendirat(dirfd, path, O_NOFOLLOW|O_NOATIME); - if (dir) - return dir; - - log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); - if (errno != EPERM) - return NULL; - - dir = xopendirat(dirfd, path, O_NOFOLLOW); - if (!dir) - log_debug_errno(errno, "Cannot open %sdirectory \"%s\": %m", dirfd == AT_FDCWD ? "" : "sub", path); - - return dir; -} - -static DIR* opendir_nomod(const char *path) { - return xopendirat_nomod(AT_FDCWD, path); -} - -static int dir_cleanup( - Item *i, - const char *p, - DIR *d, - const struct stat *ds, - usec_t cutoff, - dev_t rootdev, - bool mountpoint, - int maxdepth, - bool keep_this_level) { - - struct dirent *dent; - struct timespec times[2]; - bool deleted = false; - int r = 0; - - while ((dent = readdir(d))) { - struct stat s; - usec_t age; - _cleanup_free_ char *sub_path = NULL; - - if (STR_IN_SET(dent->d_name, ".", "..")) - continue; - - if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == ENOENT) - continue; - - /* FUSE, NFS mounts, SELinux might return EACCES */ - if (errno == EACCES) - log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - else - log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - r = -errno; - continue; - } - - /* Stay on the same filesystem */ - if (s.st_dev != rootdev) { - log_debug("Ignoring \"%s/%s\": different filesystem.", p, dent->d_name); - continue; - } - - /* Try to detect bind mounts of the same filesystem instance; they - * do not differ in device major/minors. This type of query is not - * supported on all kernels or filesystem types though. */ - if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) { - log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", - p, dent->d_name); - continue; - } - - /* Do not delete read-only files owned by root */ - if (s.st_uid == 0 && !(s.st_mode & S_IWUSR)) { - log_debug("Ignoring \"%s/%s\": read-only and owner by root.", p, dent->d_name); - continue; - } - - sub_path = strjoin(p, "/", dent->d_name, NULL); - if (!sub_path) { - r = log_oom(); - goto finish; - } - - /* Is there an item configured for this path? */ - if (ordered_hashmap_get(items, sub_path)) { - log_debug("Ignoring \"%s\": a separate entry exists.", sub_path); - continue; - } - - if (find_glob(globs, sub_path)) { - log_debug("Ignoring \"%s\": a separate glob exists.", sub_path); - continue; - } - - if (S_ISDIR(s.st_mode)) { - - if (mountpoint && - streq(dent->d_name, "lost+found") && - s.st_uid == 0) { - log_debug("Ignoring \"%s\".", sub_path); - continue; - } - - if (maxdepth <= 0) - log_warning("Reached max depth on \"%s\".", sub_path); - else { - _cleanup_closedir_ DIR *sub_dir; - int q; - - sub_dir = xopendirat_nomod(dirfd(d), dent->d_name); - if (!sub_dir) { - if (errno != ENOENT) - r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path); - - continue; - } - - q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); - if (q < 0) - r = q; - } - - /* Note: if you are wondering why we don't - * support the sticky bit for excluding - * directories from cleaning like we do it for - * other file system objects: well, the sticky - * bit already has a meaning for directories, - * so we don't want to overload that. */ - - if (keep_this_level) { - log_debug("Keeping \"%s\".", sub_path); - continue; - } - - /* Ignore ctime, we change it when deleting */ - age = timespec_load(&s.st_mtim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - /* Follows spelling in stat(1). */ - log_debug("Directory \"%s\": modify time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_atim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("Directory \"%s\": access time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - log_debug("Removing directory \"%s\".", sub_path); - if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) - if (errno != ENOENT && errno != ENOTEMPTY) { - log_error_errno(errno, "rmdir(%s): %m", sub_path); - r = -errno; - } - - } else { - /* Skip files for which the sticky bit is - * set. These are semantics we define, and are - * unknown elsewhere. See XDG_RUNTIME_DIR - * specification for details. */ - if (s.st_mode & S_ISVTX) { - log_debug("Skipping \"%s\": sticky bit set.", sub_path); - continue; - } - - if (mountpoint && S_ISREG(s.st_mode)) - if (s.st_uid == 0 && STR_IN_SET(dent->d_name, - ".journal", - "aquota.user", - "aquota.group")) { - log_debug("Skipping \"%s\".", sub_path); - continue; - } - - /* Ignore sockets that are listed in /proc/net/unix */ - if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) { - log_debug("Skipping \"%s\": live socket.", sub_path); - continue; - } - - /* Ignore device nodes */ - if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode)) { - log_debug("Skipping \"%s\": a device.", sub_path); - continue; - } - - /* Keep files on this level around if this is - * requested */ - if (keep_this_level) { - log_debug("Keeping \"%s\".", sub_path); - continue; - } - - age = timespec_load(&s.st_mtim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - /* Follows spelling in stat(1). */ - log_debug("File \"%s\": modify time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_atim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("File \"%s\": access time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - age = timespec_load(&s.st_ctim); - if (age >= cutoff) { - char a[FORMAT_TIMESTAMP_MAX]; - log_debug("File \"%s\": change time %s is too new.", - sub_path, - format_timestamp_us(a, sizeof(a), age)); - continue; - } - - log_debug("unlink \"%s\"", sub_path); - - if (unlinkat(dirfd(d), dent->d_name, 0) < 0) - if (errno != ENOENT) - r = log_error_errno(errno, "unlink(%s): %m", sub_path); - - deleted = true; - } - } - -finish: - if (deleted) { - usec_t age1, age2; - char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; - - /* Restore original directory timestamps */ - times[0] = ds->st_atim; - times[1] = ds->st_mtim; - - age1 = timespec_load(&ds->st_atim); - age2 = timespec_load(&ds->st_mtim); - log_debug("Restoring access and modification time on \"%s\": %s, %s", - p, - format_timestamp_us(a, sizeof(a), age1), - format_timestamp_us(b, sizeof(b), age2)); - if (futimens(dirfd(d), times) < 0) - log_error_errno(errno, "utimensat(%s): %m", p); - } - - return r; -} - -static int path_set_perms(Item *i, const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; - - assert(i); - assert(path); - - /* We open the file with O_PATH here, to make the operation - * somewhat atomic. Also there's unfortunately no fchmodat() - * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via - * O_PATH. */ - - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); - if (fd < 0) - return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); - - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - - if (S_ISLNK(st.st_mode)) - log_debug("Skipping mode an owner fix for symlink %s.", path); - else { - char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - xsprintf(fn, "/proc/self/fd/%i", fd); - - /* not using i->path directly because it may be a glob */ - if (i->mode_set) { - mode_t m = i->mode; - - if (i->mask_perms) { - if (!(st.st_mode & 0111)) - m &= ~0111; - if (!(st.st_mode & 0222)) - m &= ~0222; - if (!(st.st_mode & 0444)) - m &= ~0444; - if (!S_ISDIR(st.st_mode)) - m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */ - } - - if (m == (st.st_mode & 07777)) - log_debug("\"%s\" has right mode %o", path, st.st_mode); - else { - log_debug("chmod \"%s\" to mode %o", path, m); - if (chmod(fn, m) < 0) - return log_error_errno(errno, "chmod(%s) failed: %m", path); - } - } - - if ((i->uid != st.st_uid || i->gid != st.st_gid) && - (i->uid_set || i->gid_set)) { - log_debug("chown \"%s\" to "UID_FMT"."GID_FMT, - path, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID); - if (chown(fn, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID) < 0) - return log_error_errno(errno, "chown(%s) failed: %m", path); - } - } - - fd = safe_close(fd); - - return label_fix(path, false, false); -} - -static int parse_xattrs_from_arg(Item *i) { - const char *p; - int r; - - assert(i); - assert(i->argument); - - p = i->argument; - - for (;;) { - _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL; - - r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p); - if (r <= 0) - break; - - r = specifier_printf(xattr, specifier_table, NULL, &xattr_replaced); - if (r < 0) - return log_error_errno(r, "Failed to replace specifiers in extended attribute '%s': %m", xattr); - - r = split_pair(xattr_replaced, "=", &name, &value); - if (r < 0) { - log_warning_errno(r, "Failed to parse extended attribute, ignoring: %s", xattr); - continue; - } - - if (isempty(name) || isempty(value)) { - log_warning("Malformed extended attribute found, ignoring: %s", xattr); - continue; - } - - if (strv_push_pair(&i->xattrs, name, value) < 0) - return log_oom(); - - name = value = NULL; - } - - return 0; -} - -static int path_set_xattrs(Item *i, const char *path) { - char **name, **value; - - assert(i); - assert(path); - - STRV_FOREACH_PAIR(name, value, i->xattrs) { - int n; - - n = strlen(*value); - log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); - if (lsetxattr(path, *name, *value, n, 0) < 0) { - log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); - return -errno; - } - } - return 0; -} - -static int parse_acls_from_arg(Item *item) { -#ifdef HAVE_ACL - int r; - - assert(item); - - /* If force (= modify) is set, we will not modify the acl - * afterwards, so the mask can be added now if necessary. */ - - r = parse_acl(item->argument, &item->acl_access, &item->acl_default, !item->force); - if (r < 0) - log_warning_errno(r, "Failed to parse ACL \"%s\": %m. Ignoring", item->argument); -#else - log_warning_errno(ENOSYS, "ACLs are not supported. Ignoring"); -#endif - - return 0; -} - -#ifdef HAVE_ACL -static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) { - _cleanup_(acl_free_charpp) char *t = NULL; - _cleanup_(acl_freep) acl_t dup = NULL; - int r; - - /* Returns 0 for success, positive error if already warned, - * negative error otherwise. */ - - if (modify) { - r = acls_for_file(path, type, acl, &dup); - if (r < 0) - return r; - - r = calc_acl_mask_if_needed(&dup); - if (r < 0) - return r; - } else { - dup = acl_dup(acl); - if (!dup) - return -errno; - - /* the mask was already added earlier if needed */ - } - - r = add_base_acls_if_needed(&dup, path); - if (r < 0) - return r; - - t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE); - log_debug("Setting %s ACL %s on %s.", - type == ACL_TYPE_ACCESS ? "access" : "default", - strna(t), pretty); - - r = acl_set_file(path, type, dup); - if (r < 0) - /* Return positive to indicate we already warned */ - return -log_error_errno(errno, - "Setting %s ACL \"%s\" on %s failed: %m", - type == ACL_TYPE_ACCESS ? "access" : "default", - strna(t), pretty); - - return 0; -} -#endif - -static int path_set_acls(Item *item, const char *path) { - int r = 0; -#ifdef HAVE_ACL - char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int fd = -1; - struct stat st; - - assert(item); - assert(path); - - fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); - if (fd < 0) - return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); - - if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) - return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - - if (S_ISLNK(st.st_mode)) { - log_debug("Skipping ACL fix for symlink %s.", path); - return 0; - } - - xsprintf(fn, "/proc/self/fd/%i", fd); - - if (item->acl_access) - r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force); - - if (r == 0 && item->acl_default) - r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force); - - if (r > 0) - return -r; /* already warned */ - else if (r == -EOPNOTSUPP) { - log_debug_errno(r, "ACLs not supported by file system at %s", path); - return 0; - } else if (r < 0) - log_error_errno(r, "ACL operation on \"%s\" failed: %m", path); -#endif - return r; -} - -#define ATTRIBUTES_ALL \ - (FS_NOATIME_FL | \ - FS_SYNC_FL | \ - FS_DIRSYNC_FL | \ - FS_APPEND_FL | \ - FS_COMPR_FL | \ - FS_NODUMP_FL | \ - FS_EXTENT_FL | \ - FS_IMMUTABLE_FL | \ - FS_JOURNAL_DATA_FL | \ - FS_SECRM_FL | \ - FS_UNRM_FL | \ - FS_NOTAIL_FL | \ - FS_TOPDIR_FL | \ - FS_NOCOW_FL) - -static int parse_attribute_from_arg(Item *item) { - - static const struct { - char character; - unsigned value; - } attributes[] = { - { 'A', FS_NOATIME_FL }, /* do not update atime */ - { 'S', FS_SYNC_FL }, /* Synchronous updates */ - { 'D', FS_DIRSYNC_FL }, /* dirsync behaviour (directories only) */ - { 'a', FS_APPEND_FL }, /* writes to file may only append */ - { 'c', FS_COMPR_FL }, /* Compress file */ - { 'd', FS_NODUMP_FL }, /* do not dump file */ - { 'e', FS_EXTENT_FL }, /* Extents */ - { 'i', FS_IMMUTABLE_FL }, /* Immutable file */ - { 'j', FS_JOURNAL_DATA_FL }, /* Reserved for ext3 */ - { 's', FS_SECRM_FL }, /* Secure deletion */ - { 'u', FS_UNRM_FL }, /* Undelete */ - { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ - { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/ - { 'C', FS_NOCOW_FL }, /* Do not cow file */ - }; - - enum { - MODE_ADD, - MODE_DEL, - MODE_SET - } mode = MODE_ADD; - - unsigned value = 0, mask = 0; - const char *p; - - assert(item); - - p = item->argument; - if (p) { - if (*p == '+') { - mode = MODE_ADD; - p++; - } else if (*p == '-') { - mode = MODE_DEL; - p++; - } else if (*p == '=') { - mode = MODE_SET; - p++; - } - } - - if (isempty(p) && mode != MODE_SET) { - log_error("Setting file attribute on '%s' needs an attribute specification.", item->path); - return -EINVAL; - } - - for (; p && *p ; p++) { - unsigned i, v; - - for (i = 0; i < ELEMENTSOF(attributes); i++) - if (*p == attributes[i].character) - break; - - if (i >= ELEMENTSOF(attributes)) { - log_error("Unknown file attribute '%c' on '%s'.", *p, item->path); - return -EINVAL; - } - - v = attributes[i].value; - - SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); - - mask |= v; - } - - if (mode == MODE_SET) - mask |= ATTRIBUTES_ALL; - - assert(mask != 0); - - item->attribute_mask = mask; - item->attribute_value = value; - item->attribute_set = true; - - return 0; -} - -static int path_set_attribute(Item *item, const char *path) { - _cleanup_close_ int fd = -1; - struct stat st; - unsigned f; - int r; - - if (!item->attribute_set || item->attribute_mask == 0) - return 0; - - fd = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOATIME|O_NOFOLLOW); - if (fd < 0) { - if (errno == ELOOP) - return log_error_errno(errno, "Skipping file attributes adjustment on symlink %s.", path); - - return log_error_errno(errno, "Cannot open '%s': %m", path); - } - - if (fstat(fd, &st) < 0) - return log_error_errno(errno, "Cannot stat '%s': %m", path); - - /* Issuing the file attribute ioctls on device nodes is not - * safe, as that will be delivered to the drivers, not the - * file system containing the device node. */ - if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { - log_error("Setting file flags is only supported on regular files and directories, cannot set on '%s'.", path); - return -EINVAL; - } - - f = item->attribute_value & item->attribute_mask; - - /* Mask away directory-specific flags */ - if (!S_ISDIR(st.st_mode)) - f &= ~FS_DIRSYNC_FL; - - r = chattr_fd(fd, f, item->attribute_mask); - if (r < 0) - log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, - r, - "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", - path, item->attribute_value, item->attribute_mask); - - return 0; -} - -static int write_one_file(Item *i, const char *path) { - _cleanup_close_ int fd = -1; - int flags, r = 0; - struct stat st; - - assert(i); - assert(path); - - flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : - i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(path, S_IFREG); - fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY, i->mode); - mac_selinux_create_file_clear(); - } - - if (fd < 0) { - if (i->type == WRITE_FILE && errno == ENOENT) { - log_debug_errno(errno, "Not writing \"%s\": %m", path); - return 0; - } - - r = -errno; - if (!i->argument && errno == EROFS && stat(path, &st) == 0 && - (i->type == CREATE_FILE || st.st_size == 0)) - goto check_mode; - - return log_error_errno(r, "Failed to create file %s: %m", path); - } - - if (i->argument) { - _cleanup_free_ char *unescaped = NULL, *replaced = NULL; - - log_debug("%s to \"%s\".", i->type == CREATE_FILE ? "Appending" : "Writing", path); - - r = cunescape(i->argument, 0, &unescaped); - if (r < 0) - return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument); - - r = specifier_printf(unescaped, specifier_table, NULL, &replaced); - if (r < 0) - return log_error_errno(r, "Failed to replace specifiers in parameter to write '%s': %m", unescaped); - - r = loop_write(fd, replaced, strlen(replaced), false); - if (r < 0) - return log_error_errno(r, "Failed to write file \"%s\": %m", path); - } else - log_debug("\"%s\" has been created.", path); - - fd = safe_close(fd); - - if (stat(path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", path); - - check_mode: - if (!S_ISREG(st.st_mode)) { - log_error("%s is not a file.", path); - return -EEXIST; - } - - r = path_set_perms(i, path); - if (r < 0) - return r; - - return 0; -} - -typedef int (*action_t)(Item *, const char *); - -static int item_do_children(Item *i, const char *path, action_t action) { - _cleanup_closedir_ DIR *d; - int r = 0; - - assert(i); - assert(path); - - /* This returns the first error we run into, but nevertheless - * tries to go on */ - - d = opendir_nomod(path); - if (!d) - return errno == ENOENT || errno == ENOTDIR ? 0 : -errno; - - for (;;) { - _cleanup_free_ char *p = NULL; - struct dirent *de; - int q; - - errno = 0; - de = readdir(d); - if (!de) { - if (errno > 0 && r == 0) - r = -errno; - - break; - } - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - p = strjoin(path, "/", de->d_name, NULL); - if (!p) - return -ENOMEM; - - q = action(i, p); - if (q < 0 && q != -ENOENT && r == 0) - r = q; - - if (IN_SET(de->d_type, DT_UNKNOWN, DT_DIR)) { - q = item_do_children(i, p, action); - if (q < 0 && r == 0) - r = q; - } - } - - return r; -} - -static int glob_item(Item *i, action_t action, bool recursive) { - _cleanup_globfree_ glob_t g = { - .gl_closedir = (void (*)(void *)) closedir, - .gl_readdir = (struct dirent *(*)(void *)) readdir, - .gl_opendir = (void *(*)(const char *)) opendir_nomod, - .gl_lstat = lstat, - .gl_stat = stat, - }; - int r = 0, k; - char **fn; - - errno = 0; - k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); - if (k != 0 && k != GLOB_NOMATCH) - return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); - - STRV_FOREACH(fn, g.gl_pathv) { - k = action(i, *fn); - if (k < 0 && r == 0) - r = k; - - if (recursive) { - k = item_do_children(i, *fn, action); - if (k < 0 && r == 0) - r = k; - } - } - - return r; -} - -typedef enum { - CREATION_NORMAL, - CREATION_EXISTING, - CREATION_FORCE, - _CREATION_MODE_MAX, - _CREATION_MODE_INVALID = -1 -} CreationMode; - -static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { - [CREATION_NORMAL] = "Created", - [CREATION_EXISTING] = "Found existing", - [CREATION_FORCE] = "Created replacement", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(creation_mode_verb, CreationMode); - -static int create_item(Item *i) { - _cleanup_free_ char *resolved = NULL; - struct stat st; - int r = 0; - int q = 0; - CreationMode creation; - - assert(i); - - log_debug("Running create action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case REMOVE_PATH: - case RECURSIVE_REMOVE_PATH: - return 0; - - case CREATE_FILE: - case TRUNCATE_FILE: - r = write_one_file(i, i->path); - if (r < 0) - return r; - break; - - case COPY_FILES: { - r = specifier_printf(i->argument, specifier_table, NULL, &resolved); - if (r < 0) - return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument); - - log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path); - r = copy_tree(resolved, i->path, false); - - if (r == -EROFS && stat(i->path, &st) == 0) - r = -EEXIST; - - if (r < 0) { - struct stat a, b; - - if (r != -EEXIST) - return log_error_errno(r, "Failed to copy files to %s: %m", i->path); - - if (stat(resolved, &a) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", resolved); - - if (stat(i->path, &b) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if ((a.st_mode ^ b.st_mode) & S_IFMT) { - log_debug("Can't copy to %s, file exists already and is of different type", i->path); - return 0; - } - } - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - - case WRITE_FILE: - r = glob_item(i, write_one_file, false); - if (r < 0) - return r; - - break; - - case CREATE_DIRECTORY: - case TRUNCATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - RUN_WITH_UMASK(0000) - mkdir_parents_label(i->path, 0755); - - if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { - - if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) - - /* Don't create a subvolume unless the - * root directory is one, too. We do - * this under the assumption that if - * the root directory is just a plain - * directory (i.e. very light-weight), - * we shouldn't try to split it up - * into subvolumes (i.e. more - * heavy-weight). Thus, chroot() - * environments and suchlike will get - * a full brtfs subvolume set up below - * their tree only if they - * specifically set up a btrfs - * subvolume for the root dir too. */ - - r = -ENOTTY; - else { - RUN_WITH_UMASK((~i->mode) & 0777) - r = btrfs_subvol_make(i->path); - } - } else - r = 0; - - if (IN_SET(i->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY) || r == -ENOTTY) - RUN_WITH_UMASK(0000) - r = mkdir_label(i->path, i->mode); - - if (r < 0) { - int k; - - if (r != -EEXIST && r != -EROFS) - return log_error_errno(r, "Failed to create directory or subvolume \"%s\": %m", i->path); - - k = is_dir(i->path, false); - if (k == -ENOENT && r == -EROFS) - return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", i->path); - if (k < 0) - return log_error_errno(k, "Failed to check if %s exists: %m", i->path); - if (!k) { - log_warning("\"%s\" already exists and is not a directory.", i->path); - return 0; - } - - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - - log_debug("%s directory \"%s\".", creation_mode_verb_to_string(creation), i->path); - - if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { - r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); - if (r == -ENOTTY) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); - else if (r == -EROFS) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); - else if (r == -ENOPROTOOPT) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); - else if (r < 0) - q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); - else if (r > 0) - log_debug("Adjusted quota for subvolume \"%s\".", i->path); - else if (r == 0) - log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); - } - - /* fall through */ - - case EMPTY_DIRECTORY: - r = path_set_perms(i, i->path); - if (q < 0) - return q; - if (r < 0) - return r; - - break; - - case CREATE_FIFO: - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO); - r = mkfifo(i->path, i->mode); - mac_selinux_create_file_clear(); - } - - if (r < 0) { - if (errno != EEXIST) - return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); - - if (lstat(i->path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if (!S_ISFIFO(st.st_mode)) { - - if (i->force) { - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, S_IFIFO); - r = mkfifo_atomic(i->path, i->mode); - mac_selinux_create_file_clear(); - } - - if (r < 0) - return log_error_errno(r, "Failed to create fifo %s: %m", i->path); - creation = CREATION_FORCE; - } else { - log_warning("\"%s\" already exists and is not a fifo.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - log_debug("%s fifo \"%s\".", creation_mode_verb_to_string(creation), i->path); - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - } - - case CREATE_SYMLINK: { - r = specifier_printf(i->argument, specifier_table, NULL, &resolved); - if (r < 0) - return log_error_errno(r, "Failed to substitute specifiers in symlink target %s: %m", i->argument); - - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = symlink(resolved, i->path); - mac_selinux_create_file_clear(); - - if (r < 0) { - _cleanup_free_ char *x = NULL; - - if (errno != EEXIST) - return log_error_errno(errno, "symlink(%s, %s) failed: %m", resolved, i->path); - - r = readlink_malloc(i->path, &x); - if (r < 0 || !streq(resolved, x)) { - - if (i->force) { - mac_selinux_create_file_prepare(i->path, S_IFLNK); - r = symlink_atomic(resolved, i->path); - mac_selinux_create_file_clear(); - - if (r < 0) - return log_error_errno(r, "symlink(%s, %s) failed: %m", resolved, i->path); - - creation = CREATION_FORCE; - } else { - log_debug("\"%s\" is not a symlink or does not point to the correct path.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - - creation = CREATION_NORMAL; - log_debug("%s symlink \"%s\".", creation_mode_verb_to_string(creation), i->path); - break; - } - - case CREATE_BLOCK_DEVICE: - case CREATE_CHAR_DEVICE: { - mode_t file_type; - - if (have_effective_cap(CAP_MKNOD) == 0) { - /* In a container we lack CAP_MKNOD. We - shouldn't attempt to create the device node in - that case to avoid noise, and we don't support - virtualized devices in containers anyway. */ - - log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); - return 0; - } - - file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type); - r = mknod(i->path, i->mode | file_type, i->major_minor); - mac_selinux_create_file_clear(); - } - - if (r < 0) { - if (errno == EPERM) { - log_debug("We lack permissions, possibly because of cgroup configuration; " - "skipping creation of device node %s.", i->path); - return 0; - } - - if (errno != EEXIST) - return log_error_errno(errno, "Failed to create device node %s: %m", i->path); - - if (lstat(i->path, &st) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if ((st.st_mode & S_IFMT) != file_type) { - - if (i->force) { - - RUN_WITH_UMASK(0000) { - mac_selinux_create_file_prepare(i->path, file_type); - r = mknod_atomic(i->path, i->mode | file_type, i->major_minor); - mac_selinux_create_file_clear(); - } - - if (r < 0) - return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path); - creation = CREATION_FORCE; - } else { - log_debug("%s is not a device node.", i->path); - return 0; - } - } else - creation = CREATION_EXISTING; - } else - creation = CREATION_NORMAL; - - log_debug("%s %s device node \"%s\" %u:%u.", - creation_mode_verb_to_string(creation), - i->type == CREATE_BLOCK_DEVICE ? "block" : "char", - i->path, major(i->mode), minor(i->mode)); - - r = path_set_perms(i, i->path); - if (r < 0) - return r; - - break; - } - - case ADJUST_MODE: - case RELABEL_PATH: - r = glob_item(i, path_set_perms, false); - if (r < 0) - return r; - break; - - case RECURSIVE_RELABEL_PATH: - r = glob_item(i, path_set_perms, true); - if (r < 0) - return r; - break; - - case SET_XATTR: - r = glob_item(i, path_set_xattrs, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_XATTR: - r = glob_item(i, path_set_xattrs, true); - if (r < 0) - return r; - break; - - case SET_ACL: - r = glob_item(i, path_set_acls, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_ACL: - r = glob_item(i, path_set_acls, true); - if (r < 0) - return r; - break; - - case SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, false); - if (r < 0) - return r; - break; - - case RECURSIVE_SET_ATTRIBUTE: - r = glob_item(i, path_set_attribute, true); - if (r < 0) - return r; - break; - } - - return 0; -} - -static int remove_item_instance(Item *i, const char *instance) { - int r; - - assert(i); - - switch (i->type) { - - case REMOVE_PATH: - if (remove(instance) < 0 && errno != ENOENT) - return log_error_errno(errno, "rm(%s): %m", instance); - - break; - - case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: - /* FIXME: we probably should use dir_cleanup() here - * instead of rm_rf() so that 'x' is honoured. */ - log_debug("rm -rf \"%s\"", instance); - r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "rm_rf(%s): %m", instance); - - break; - - default: - assert_not_reached("wut?"); - } - - return 0; -} - -static int remove_item(Item *i) { - assert(i); - - log_debug("Running remove action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - - case REMOVE_PATH: - case TRUNCATE_DIRECTORY: - case RECURSIVE_REMOVE_PATH: - return glob_item(i, remove_item_instance, false); - - default: - return 0; - } -} - -static int clean_item_instance(Item *i, const char* instance) { - _cleanup_closedir_ DIR *d = NULL; - struct stat s, ps; - bool mountpoint; - usec_t cutoff, n; - char timestamp[FORMAT_TIMESTAMP_MAX]; - - assert(i); - - if (!i->age_set) - return 0; - - n = now(CLOCK_REALTIME); - if (n < i->age) - return 0; - - cutoff = n - i->age; - - d = opendir_nomod(instance); - if (!d) { - if (IN_SET(errno, ENOENT, ENOTDIR)) { - log_debug_errno(errno, "Directory \"%s\": %m", instance); - return 0; - } - - return log_error_errno(errno, "Failed to open directory %s: %m", instance); - } - - if (fstat(dirfd(d), &s) < 0) - return log_error_errno(errno, "stat(%s) failed: %m", i->path); - - if (!S_ISDIR(s.st_mode)) { - log_error("%s is not a directory.", i->path); - return -ENOTDIR; - } - - if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) - return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); - - mountpoint = s.st_dev != ps.st_dev || s.st_ino == ps.st_ino; - - log_debug("Cleanup threshold for %s \"%s\" is %s", - mountpoint ? "mount point" : "directory", - instance, - format_timestamp_us(timestamp, sizeof(timestamp), cutoff)); - - return dir_cleanup(i, instance, d, &s, cutoff, s.st_dev, mountpoint, - MAX_DEPTH, i->keep_first_level); -} - -static int clean_item(Item *i) { - assert(i); - - log_debug("Running clean action for entry %c %s", (char) i->type, i->path); - - switch (i->type) { - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case EMPTY_DIRECTORY: - case TRUNCATE_DIRECTORY: - case IGNORE_PATH: - case COPY_FILES: - clean_item_instance(i, i->path); - return 0; - case IGNORE_DIRECTORY_PATH: - return glob_item(i, clean_item_instance, false); - default: - return 0; - } -} - -static int process_item_array(ItemArray *array); - -static int process_item(Item *i) { - int r, q, p, t = 0; - _cleanup_free_ char *prefix = NULL; - - assert(i); - - if (i->done) - return 0; - - i->done = true; - - prefix = malloc(strlen(i->path) + 1); - if (!prefix) - return log_oom(); - - PATH_FOREACH_PREFIX(prefix, i->path) { - ItemArray *j; - - j = ordered_hashmap_get(items, prefix); - if (j) { - int s; - - s = process_item_array(j); - if (s < 0 && t == 0) - t = s; - } - } - - r = arg_create ? create_item(i) : 0; - q = arg_remove ? remove_item(i) : 0; - p = arg_clean ? clean_item(i) : 0; - - return t < 0 ? t : - r < 0 ? r : - q < 0 ? q : - p; -} - -static int process_item_array(ItemArray *array) { - unsigned n; - int r = 0, k; - - assert(array); - - for (n = 0; n < array->count; n++) { - k = process_item(array->items + n); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static void item_free_contents(Item *i) { - assert(i); - free(i->path); - free(i->argument); - strv_free(i->xattrs); - -#ifdef HAVE_ACL - acl_free(i->acl_access); - acl_free(i->acl_default); -#endif -} - -static void item_array_free(ItemArray *a) { - unsigned n; - - if (!a) - return; - - for (n = 0; n < a->count; n++) - item_free_contents(a->items + n); - free(a->items); - free(a); -} - -static int item_compare(const void *a, const void *b) { - const Item *x = a, *y = b; - - /* Make sure that the ownership taking item is put first, so - * that we first create the node, and then can adjust it */ - - if (takes_ownership(x->type) && !takes_ownership(y->type)) - return -1; - if (!takes_ownership(x->type) && takes_ownership(y->type)) - return 1; - - return (int) x->type - (int) y->type; -} - -static bool item_compatible(Item *a, Item *b) { - assert(a); - assert(b); - assert(streq(a->path, b->path)); - - if (takes_ownership(a->type) && takes_ownership(b->type)) - /* check if the items are the same */ - return streq_ptr(a->argument, b->argument) && - - a->uid_set == b->uid_set && - a->uid == b->uid && - - a->gid_set == b->gid_set && - a->gid == b->gid && - - a->mode_set == b->mode_set && - a->mode == b->mode && - - a->age_set == b->age_set && - a->age == b->age && - - a->mask_perms == b->mask_perms && - - a->keep_first_level == b->keep_first_level && - - a->major_minor == b->major_minor; - - return true; -} - -static bool should_include_path(const char *path) { - char **prefix; - - STRV_FOREACH(prefix, arg_exclude_prefixes) - if (path_startswith(path, *prefix)) { - log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.", - path, *prefix); - return false; - } - - STRV_FOREACH(prefix, arg_include_prefixes) - if (path_startswith(path, *prefix)) { - log_debug("Entry \"%s\" matches include prefix \"%s\".", path, *prefix); - return true; - } - - /* no matches, so we should include this path only if we - * have no whitelist at all */ - if (strv_length(arg_include_prefixes) == 0) - return true; - - log_debug("Entry \"%s\" does not match any include prefix, skipping.", path); - return false; -} - -static int parse_line(const char *fname, unsigned line, const char *buffer) { - - _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL; - _cleanup_(item_free_contents) Item i = {}; - ItemArray *existing; - OrderedHashmap *h; - int r, pos; - bool force = false, boot = false; - - assert(fname); - assert(line >= 1); - assert(buffer); - - r = extract_many_words( - &buffer, - NULL, - EXTRACT_QUOTES, - &action, - &path, - &mode, - &user, - &group, - &age, - NULL); - if (r < 0) - return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line); - else if (r < 2) { - log_error("[%s:%u] Syntax error.", fname, line); - return -EIO; - } - - if (!isempty(buffer) && !streq(buffer, "-")) { - i.argument = strdup(buffer); - if (!i.argument) - return log_oom(); - } - - if (isempty(action)) { - log_error("[%s:%u] Command too short '%s'.", fname, line, action); - return -EINVAL; - } - - for (pos = 1; action[pos]; pos++) { - if (action[pos] == '!' && !boot) - boot = true; - else if (action[pos] == '+' && !force) - force = true; - else { - log_error("[%s:%u] Unknown modifiers in command '%s'", - fname, line, action); - return -EINVAL; - } - } - - if (boot && !arg_boot) { - log_debug("Ignoring entry %s \"%s\" because --boot is not specified.", - action, path); - return 0; - } - - i.type = action[0]; - i.force = force; - - r = specifier_printf(path, specifier_table, NULL, &i.path); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, path); - return r; - } - - switch (i.type) { - - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case EMPTY_DIRECTORY: - case TRUNCATE_DIRECTORY: - case CREATE_FIFO: - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case REMOVE_PATH: - case RECURSIVE_REMOVE_PATH: - case ADJUST_MODE: - case RELABEL_PATH: - case RECURSIVE_RELABEL_PATH: - if (i.argument) - log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type); - - break; - - case CREATE_FILE: - case TRUNCATE_FILE: - break; - - case CREATE_SYMLINK: - if (!i.argument) { - i.argument = strappend("/usr/share/factory/", i.path); - if (!i.argument) - return log_oom(); - } - break; - - case WRITE_FILE: - if (!i.argument) { - log_error("[%s:%u] Write file requires argument.", fname, line); - return -EBADMSG; - } - break; - - case COPY_FILES: - if (!i.argument) { - i.argument = strappend("/usr/share/factory/", i.path); - if (!i.argument) - return log_oom(); - } else if (!path_is_absolute(i.argument)) { - log_error("[%s:%u] Source path is not absolute.", fname, line); - return -EBADMSG; - } - - path_kill_slashes(i.argument); - break; - - case CREATE_CHAR_DEVICE: - case CREATE_BLOCK_DEVICE: { - unsigned major, minor; - - if (!i.argument) { - log_error("[%s:%u] Device file requires argument.", fname, line); - return -EBADMSG; - } - - if (sscanf(i.argument, "%u:%u", &major, &minor) != 2) { - log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument); - return -EBADMSG; - } - - i.major_minor = makedev(major, minor); - break; - } - - case SET_XATTR: - case RECURSIVE_SET_XATTR: - if (!i.argument) { - log_error("[%s:%u] Set extended attribute requires argument.", fname, line); - return -EBADMSG; - } - r = parse_xattrs_from_arg(&i); - if (r < 0) - return r; - break; - - case SET_ACL: - case RECURSIVE_SET_ACL: - if (!i.argument) { - log_error("[%s:%u] Set ACLs requires argument.", fname, line); - return -EBADMSG; - } - r = parse_acls_from_arg(&i); - if (r < 0) - return r; - break; - - case SET_ATTRIBUTE: - case RECURSIVE_SET_ATTRIBUTE: - if (!i.argument) { - log_error("[%s:%u] Set file attribute requires argument.", fname, line); - return -EBADMSG; - } - r = parse_attribute_from_arg(&i); - if (r < 0) - return r; - break; - - default: - log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type); - return -EBADMSG; - } - - if (!path_is_absolute(i.path)) { - log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path); - return -EBADMSG; - } - - path_kill_slashes(i.path); - - if (!should_include_path(i.path)) - return 0; - - if (arg_root) { - char *p; - - p = prefix_root(arg_root, i.path); - if (!p) - return log_oom(); - - free(i.path); - i.path = p; - } - - if (!isempty(user) && !streq(user, "-")) { - const char *u = user; - - r = get_user_creds(&u, &i.uid, NULL, NULL, NULL); - if (r < 0) { - log_error("[%s:%u] Unknown user '%s'.", fname, line, user); - return r; - } - - i.uid_set = true; - } - - if (!isempty(group) && !streq(group, "-")) { - const char *g = group; - - r = get_group_creds(&g, &i.gid); - if (r < 0) { - log_error("[%s:%u] Unknown group '%s'.", fname, line, group); - return r; - } - - i.gid_set = true; - } - - if (!isempty(mode) && !streq(mode, "-")) { - const char *mm = mode; - unsigned m; - - if (*mm == '~') { - i.mask_perms = true; - mm++; - } - - if (parse_mode(mm, &m) < 0) { - log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode); - return -EBADMSG; - } - - i.mode = m; - i.mode_set = true; - } else - i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; - - if (!isempty(age) && !streq(age, "-")) { - const char *a = age; - - if (*a == '~') { - i.keep_first_level = true; - a++; - } - - if (parse_sec(a, &i.age) < 0) { - log_error("[%s:%u] Invalid age '%s'.", fname, line, age); - return -EBADMSG; - } - - i.age_set = true; - } - - h = needs_glob(i.type) ? globs : items; - - existing = ordered_hashmap_get(h, i.path); - if (existing) { - unsigned n; - - for (n = 0; n < existing->count; n++) { - if (!item_compatible(existing->items + n, &i)) { - log_warning("[%s:%u] Duplicate line for path \"%s\", ignoring.", - fname, line, i.path); - return 0; - } - } - } else { - existing = new0(ItemArray, 1); - r = ordered_hashmap_put(h, i.path, existing); - if (r < 0) - return log_oom(); - } - - if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1)) - return log_oom(); - - memcpy(existing->items + existing->count++, &i, sizeof(i)); - - /* Sort item array, to enforce stable ordering of application */ - qsort_safe(existing->items, existing->count, sizeof(Item), item_compare); - - zero(i); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" - "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --create Create marked files/directories\n" - " --clean Clean up marked directories\n" - " --remove Remove marked files/directories\n" - " --boot Execute actions only safe at boot\n" - " --prefix=PATH Only apply rules with the specified prefix\n" - " --exclude-prefix=PATH Ignore rules with the specified prefix\n" - " --root=PATH Operate on an alternate filesystem root\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_CREATE, - ARG_CLEAN, - ARG_REMOVE, - ARG_BOOT, - ARG_PREFIX, - ARG_EXCLUDE_PREFIX, - ARG_ROOT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "create", no_argument, NULL, ARG_CREATE }, - { "clean", no_argument, NULL, ARG_CLEAN }, - { "remove", no_argument, NULL, ARG_REMOVE }, - { "boot", no_argument, NULL, ARG_BOOT }, - { "prefix", required_argument, NULL, ARG_PREFIX }, - { "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX }, - { "root", required_argument, NULL, ARG_ROOT }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_CREATE: - arg_create = true; - break; - - case ARG_CLEAN: - arg_clean = true; - break; - - case ARG_REMOVE: - arg_remove = true; - break; - - case ARG_BOOT: - arg_boot = true; - break; - - case ARG_PREFIX: - if (strv_push(&arg_include_prefixes, optarg) < 0) - return log_oom(); - break; - - case ARG_EXCLUDE_PREFIX: - if (strv_push(&arg_exclude_prefixes, optarg) < 0) - return log_oom(); - break; - - case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); - if (r < 0) - return r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (!arg_clean && !arg_create && !arg_remove) { - log_error("You need to specify at least one of --clean, --create or --remove."); - return -EINVAL; - } - - return 1; -} - -static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *_f = NULL; - FILE *f; - char line[LINE_MAX]; - Iterator iterator; - unsigned v = 0; - Item *i; - int r = 0; - - assert(fn); - - if (streq(fn, "-")) { - log_debug("Reading config from stdin."); - fn = ""; - f = stdin; - } else { - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) { - log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); - return 0; - } - - return log_error_errno(r, "Failed to open '%s': %m", fn); - } - log_debug("Reading config file \"%s\".", fn); - f = _f; - } - - FOREACH_LINE(line, f, break) { - char *l; - int k; - - v++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = parse_line(fn, v, l); - if (k < 0 && r == 0) - r = k; - } - - /* we have to determine age parameter for each entry of type X */ - ORDERED_HASHMAP_FOREACH(i, globs, iterator) { - Iterator iter; - Item *j, *candidate_item = NULL; - - if (i->type != IGNORE_DIRECTORY_PATH) - continue; - - ORDERED_HASHMAP_FOREACH(j, items, iter) { - if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) - continue; - - if (path_equal(j->path, i->path)) { - candidate_item = j; - break; - } - - if ((!candidate_item && path_startswith(i->path, j->path)) || - (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0))) - candidate_item = j; - } - - if (candidate_item && candidate_item->age_set) { - i->age = candidate_item->age; - i->age_set = true; - } - } - - if (ferror(f)) { - log_error_errno(errno, "Failed to read from file %s: %m", fn); - if (r == 0) - r = -EIO; - } - - return r; -} - -int main(int argc, char *argv[]) { - int r, k; - ItemArray *a; - Iterator iterator; - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - mac_selinux_init(); - - items = ordered_hashmap_new(&string_hash_ops); - globs = ordered_hashmap_new(&string_hash_ops); - - if (!items || !globs) { - r = log_oom(); - goto finish; - } - - r = 0; - - if (optind < argc) { - int j; - - for (j = optind; j < argc; j++) { - k = read_config_file(argv[j], false); - if (k < 0 && r == 0) - r = k; - } - - } else { - _cleanup_strv_free_ char **files = NULL; - char **f; - - r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); - if (r < 0) { - log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); - goto finish; - } - - STRV_FOREACH(f, files) { - k = read_config_file(*f, true); - if (k < 0 && r == 0) - r = k; - } - } - - /* The non-globbing ones usually create things, hence we apply - * them first */ - ORDERED_HASHMAP_FOREACH(a, items, iterator) { - k = process_item_array(a); - if (k < 0 && r == 0) - r = k; - } - - /* The globbing ones usually alter things, hence we apply them - * second. */ - ORDERED_HASHMAP_FOREACH(a, globs, iterator) { - k = process_item_array(a); - if (k < 0 && r == 0) - r = k; - } - -finish: - while ((a = ordered_hashmap_steal_first(items))) - item_array_free(a); - - while ((a = ordered_hashmap_steal_first(globs))) - item_array_free(a); - - ordered_hashmap_free(items); - ordered_hashmap_free(globs); - - free(arg_include_prefixes); - free(arg_exclude_prefixes); - free(arg_root); - - set_free_free(unix_sockets); - - mac_selinux_finish(); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/tty-ask-password-agent/Makefile b/src/tty-ask-password-agent/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/tty-ask-password-agent/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c deleted file mode 100644 index 8851af449d..0000000000 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ /dev/null @@ -1,875 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2015 Werner Fink - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "ask-password-api.h" -#include "conf-parser.h" -#include "def.h" -#include "dirent-util.h" -#include "exit-status.h" -#include "fd-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "io-util.h" -#include "macro.h" -#include "mkdir.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "socket-util.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "util.h" -#include "utmp-wtmp.h" - -static enum { - ACTION_LIST, - ACTION_QUERY, - ACTION_WATCH, - ACTION_WALL -} arg_action = ACTION_QUERY; - -static bool arg_plymouth = false; -static bool arg_console = false; -static const char *arg_device = NULL; - -static int ask_password_plymouth( - const char *message, - usec_t until, - AskPasswordFlags flags, - const char *flag_file, - char ***ret) { - - static const union sockaddr_union sa = PLYMOUTH_SOCKET; - _cleanup_close_ int fd = -1, notify = -1; - _cleanup_free_ char *packet = NULL; - ssize_t k; - int r, n; - struct pollfd pollfd[2] = {}; - char buffer[LINE_MAX]; - size_t p = 0; - enum { - POLL_SOCKET, - POLL_INOTIFY - }; - - assert(ret); - - if (flag_file) { - notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); - if (notify < 0) - return -errno; - - r = inotify_add_watch(notify, flag_file, IN_ATTRIB); /* for the link count */ - if (r < 0) - return -errno; - } - - fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) - return -errno; - - r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - return -errno; - - 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) - packet = NULL; - if (!packet) - return -ENOMEM; - - r = loop_write(fd, packet, n + 1, true); - if (r < 0) - return r; - - pollfd[POLL_SOCKET].fd = fd; - pollfd[POLL_SOCKET].events = POLLIN; - pollfd[POLL_INOTIFY].fd = notify; - pollfd[POLL_INOTIFY].events = POLLIN; - - for (;;) { - int sleep_for = -1, j; - - if (until > 0) { - usec_t y; - - y = now(CLOCK_MONOTONIC); - - if (y > until) { - r = -ETIME; - goto finish; - } - - sleep_for = (int) ((until - y) / USEC_PER_MSEC); - } - - if (flag_file && access(flag_file, F_OK) < 0) { - r = -errno; - goto finish; - } - - j = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); - if (j < 0) { - if (errno == EINTR) - continue; - - r = -errno; - goto finish; - } else if (j == 0) { - r = -ETIME; - goto finish; - } - - 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) { - if (errno == EINTR || errno == EAGAIN) - continue; - - r = -errno; - goto finish; - } else if (k == 0) { - r = -EIO; - goto finish; - } - - p += k; - - if (p < 1) - continue; - - if (buffer[0] == 5) { - - 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) { - r = -ENOMEM; - goto finish; - } - - r = loop_write(fd, packet, n+1, true); - if (r < 0) - goto finish; - - flags &= ~ASK_PASSWORD_ACCEPT_CACHED; - p = 0; - continue; - } - - /* No password, because UI not shown */ - r = -ENOENT; - goto finish; - - } else if (buffer[0] == 2 || buffer[0] == 9) { - uint32_t size; - char **l; - - /* One or more answers */ - if (p < 5) - continue; - - memcpy(&size, buffer+1, sizeof(size)); - size = le32toh(size); - if (size + 5 > sizeof(buffer)) { - r = -EIO; - goto finish; - } - - if (p-5 < size) - continue; - - l = strv_parse_nulstr(buffer + 5, size); - if (!l) { - r = -ENOMEM; - goto finish; - } - - *ret = l; - break; - - } else { - /* Unknown packet */ - r = -EIO; - goto finish; - } - } - - r = 0; - -finish: - memory_erase(buffer, sizeof(buffer)); - return r; -} - -static int send_passwords(const char *socket_name, char **passwords) { - _cleanup_free_ char *packet = NULL; - _cleanup_close_ int socket_fd = -1; - union sockaddr_union sa = { .un.sun_family = AF_UNIX }; - size_t packet_length = 1; - char **p, *d; - int r; - - assert(socket_name); - - STRV_FOREACH(p, passwords) - packet_length += strlen(*p) + 1; - - packet = new(char, packet_length); - if (!packet) - return -ENOMEM; - - packet[0] = '+'; - - d = packet + 1; - STRV_FOREACH(p, passwords) - d = stpcpy(d, *p) + 1; - - socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (socket_fd < 0) { - r = log_debug_errno(errno, "socket(): %m"); - goto finish; - } - - strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - - r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)); - if (r < 0) - r = log_debug_errno(errno, "sendto(): %m"); - -finish: - memory_erase(packet, packet_length); - return r; -} - -static int parse_password(const char *filename, char **wall) { - _cleanup_free_ char *socket_name = NULL, *message = NULL; - bool accept_cached = false, echo = false; - uint64_t not_after = 0; - unsigned pid = 0; - - const ConfigTableItem items[] = { - { "Ask", "Socket", config_parse_string, 0, &socket_name }, - { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after }, - { "Ask", "Message", config_parse_string, 0, &message }, - { "Ask", "PID", config_parse_unsigned, 0, &pid }, - { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached }, - { "Ask", "Echo", config_parse_bool, 0, &echo }, - {} - }; - - int r; - - assert(filename); - - r = config_parse(NULL, filename, NULL, - NULL, - config_item_table_lookup, items, - true, false, true, NULL); - if (r < 0) - return r; - - if (!socket_name) { - log_error("Invalid password file %s", filename); - return -EBADMSG; - } - - if (not_after > 0 && now(CLOCK_MONOTONIC) > not_after) - return 0; - - if (pid > 0 && !pid_is_alive(pid)) - return 0; - - if (arg_action == ACTION_LIST) - printf("'%s' (PID %u)\n", message, pid); - - else if (arg_action == ACTION_WALL) { - 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!", - strempty(*wall), - *wall ? "\r\n\r\n" : "", - message, - pid) < 0) - return log_oom(); - - free(*wall); - *wall = _wall; - - } else { - _cleanup_strv_free_erase_ char **passwords = NULL; - - assert(arg_action == ACTION_QUERY || - arg_action == ACTION_WATCH); - - if (access(socket_name, W_OK) < 0) { - if (arg_action == ACTION_QUERY) - log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid); - - return 0; - } - - if (arg_plymouth) - r = ask_password_plymouth(message, not_after, accept_cached ? ASK_PASSWORD_ACCEPT_CACHED : 0, filename, &passwords); - else { - char *password = NULL; - int tty_fd = -1; - - if (arg_console) { - const char *con = arg_device ? arg_device : "/dev/console"; - - tty_fd = acquire_terminal(con, false, false, false, USEC_INFINITY); - if (tty_fd < 0) - 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, NULL, not_after, echo ? ASK_PASSWORD_ECHO : 0, filename, &password); - - if (arg_console) { - tty_fd = safe_close(tty_fd); - release_terminal(); - } - - if (r >= 0) - r = strv_push(&passwords, password); - - if (r < 0) - string_free_erase(password); - } - - /* If the query went away, that's OK */ - if (IN_SET(r, -ETIME, -ENOENT)) - return 0; - - if (r < 0) - return log_error_errno(r, "Failed to query password: %m"); - - r = send_passwords(socket_name, passwords); - if (r < 0) - return log_error_errno(r, "Failed to send: %m"); - } - - return 0; -} - -static int wall_tty_block(void) { - _cleanup_free_ char *p = NULL; - 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 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 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 log_debug_errno(errno, "Failed to open %s: %m", p); - - return fd; -} - -static bool wall_tty_match(const char *path, void *userdata) { - _cleanup_free_ char *p = NULL; - _cleanup_close_ int fd = -1; - struct stat st; - - if (!path_is_absolute(path)) - path = strjoina("/dev/", path); - - if (lstat(path, &st) < 0) { - log_debug_errno(errno, "Failed to stat %s: %m", path); - return true; - } - - 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 - * already shown. We use the fact here that opening a pipe in - * non-blocking mode for write-only will succeed only if - * there's some writer behind it. Using pipes has the - * 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) { - log_oom(); - return true; - } - - fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - 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. */ - return 0; -} - -static int show_passwords(void) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - int r = 0; - - d = opendir("/run/systemd/ask-password"); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m"); - } - - FOREACH_DIRENT_ALL(de, d, return log_error_errno(errno, "Failed to read directory: %m")) { - _cleanup_free_ char *p = NULL, *wall = NULL; - int q; - - /* We only support /dev on tmpfs, hence we can rely on - * d_type to be reliable */ - - if (de->d_type != DT_REG) - continue; - - if (hidden_or_backup_file(de->d_name)) - continue; - - if (!startswith(de->d_name, "ask.")) - continue; - - p = strappend("/run/systemd/ask-password/", de->d_name); - if (!p) - return log_oom(); - - q = parse_password(p, &wall); - if (q < 0 && r == 0) - r = q; - - if (wall) - (void) utmp_wall(wall, NULL, NULL, wall_tty_match, NULL); - } - - return r; -} - -static int watch_passwords(void) { - enum { - FD_INOTIFY, - FD_SIGNAL, - _FD_MAX - }; - - _cleanup_close_ int notify = -1, signal_fd = -1, tty_block_fd = -1; - struct pollfd pollfd[_FD_MAX] = {}; - sigset_t mask; - int r; - - tty_block_fd = wall_tty_block(); - - (void) mkdir_p_label("/run/systemd/ask-password", 0755); - - notify = inotify_init1(IN_CLOEXEC); - if (notify < 0) - 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 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); - assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0); - - signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (signal_fd < 0) - return log_error_errno(errno, "Failed to allocate signal file descriptor: %m"); - - pollfd[FD_INOTIFY].fd = notify; - pollfd[FD_INOTIFY].events = POLLIN; - pollfd[FD_SIGNAL].fd = signal_fd; - pollfd[FD_SIGNAL].events = POLLIN; - - for (;;) { - r = show_passwords(); - if (r < 0) - log_error_errno(r, "Failed to show password: %m"); - - if (poll(pollfd, _FD_MAX, -1) < 0) { - if (errno == EINTR) - continue; - - return -errno; - } - - if (pollfd[FD_INOTIFY].revents != 0) - (void) flush_fd(notify); - - if (pollfd[FD_SIGNAL].revents != 0) - break; - } - - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Process system password requests.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --list Show pending password requests\n" - " --query Process pending password requests\n" - " --watch Continuously process password requests\n" - " --wall Continuously forward password requests to wall\n" - " --plymouth Ask question with Plymouth instead of on TTY\n" - " --console Ask question on /dev/console instead of current TTY\n", - program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_LIST = 0x100, - ARG_QUERY, - ARG_WATCH, - ARG_WALL, - ARG_PLYMOUTH, - ARG_CONSOLE, - ARG_VERSION - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "list", no_argument, NULL, ARG_LIST }, - { "query", no_argument, NULL, ARG_QUERY }, - { "watch", no_argument, NULL, ARG_WATCH }, - { "wall", no_argument, NULL, ARG_WALL }, - { "plymouth", no_argument, NULL, ARG_PLYMOUTH }, - { "console", optional_argument, NULL, ARG_CONSOLE }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_LIST: - arg_action = ACTION_LIST; - break; - - case ARG_QUERY: - arg_action = ACTION_QUERY; - break; - - case ARG_WATCH: - arg_action = ACTION_WATCH; - break; - - case ARG_WALL: - arg_action = ACTION_WALL; - break; - - case ARG_PLYMOUTH: - arg_plymouth = true; - break; - - case ARG_CONSOLE: - arg_console = true; - if (optarg) { - - if (isempty(optarg)) { - log_error("Empty console device path is not allowed."); - return -EINVAL; - } - - arg_device = optarg; - } - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (optind != argc) { - log_error("%s takes no arguments.", program_invocation_short_name); - return -EINVAL; - } - - if (arg_plymouth || arg_console) { - - if (!IN_SET(arg_action, ACTION_QUERY, ACTION_WATCH)) { - log_error("Options --query and --watch conflict."); - return -EINVAL; - } - - if (arg_plymouth && arg_console) { - log_error("Options --plymouth and --console conflict."); - return -EINVAL; - } - } - - return 1; -} - -/* - * To be able to ask on all terminal devices of /dev/console - * the devices are collected. If more than one device is found, - * then on each of the terminals a inquiring task is forked. - * Every task has its own session and its own controlling terminal. - * If one of the tasks does handle a password, the remaining tasks - * will be terminated. - */ -static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) { - struct sigaction sig = { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP | SA_RESTART, - }; - - assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0); - - assert_se(sigemptyset(&sig.sa_mask) >= 0); - assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0); - - sig.sa_handler = SIG_DFL; - assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); - - *pid = fork(); - if (*pid < 0) - return log_error_errno(errno, "Failed to fork process: %m"); - - if (*pid == 0) { - int ac; - - assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); - - reset_signal_mask(); - reset_all_signal_handlers(); - - for (ac = 0; ac < argc; ac++) { - if (streq(argv[ac], "--console")) { - argv[ac] = strjoina("--console=", tty, NULL); - break; - } - } - - assert(ac < argc); - - execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); - _exit(EXIT_FAILURE); - } - return 0; -} - -static void terminate_agents(Set *pids) { - struct timespec ts; - siginfo_t status = {}; - sigset_t set; - Iterator i; - void *p; - int r, signum; - - /* - * Request termination of the remaining processes as those - * are not required anymore. - */ - SET_FOREACH(p, pids, i) - (void) kill(PTR_TO_PID(p), SIGTERM); - - /* - * Collect the processes which have go away. - */ - assert_se(sigemptyset(&set) >= 0); - assert_se(sigaddset(&set, SIGCHLD) >= 0); - timespec_store(&ts, 50 * USEC_PER_MSEC); - - while (!set_isempty(pids)) { - - zero(status); - r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG); - if (r < 0 && errno == EINTR) - continue; - - if (r == 0 && status.si_pid > 0) { - set_remove(pids, PID_TO_PTR(status.si_pid)); - continue; - } - - signum = sigtimedwait(&set, NULL, &ts); - if (signum < 0) { - if (errno != EAGAIN) - log_error_errno(errno, "sigtimedwait() failed: %m"); - break; - } - assert(signum == SIGCHLD); - } - - /* - * Kill hanging processes. - */ - SET_FOREACH(p, pids, i) { - log_warning("Failed to terminate child %d, killing it", PTR_TO_PID(p)); - (void) kill(PTR_TO_PID(p), SIGKILL); - } -} - -static int ask_on_consoles(int argc, char *argv[]) { - _cleanup_set_free_ Set *pids = NULL; - _cleanup_strv_free_ char **consoles = NULL; - siginfo_t status = {}; - char **tty; - pid_t pid; - int r; - - r = get_kernel_consoles(&consoles); - if (r < 0) - return log_error_errno(r, "Failed to determine devices of /dev/console: %m"); - - pids = set_new(NULL); - if (!pids) - return log_oom(); - - /* Start an agent on each console. */ - STRV_FOREACH(tty, consoles) { - r = ask_on_this_console(*tty, &pid, argc, argv); - if (r < 0) - return r; - - if (set_put(pids, PID_TO_PTR(pid)) < 0) - return log_oom(); - } - - /* Wait for an agent to exit. */ - for (;;) { - zero(status); - - if (waitid(P_ALL, 0, &status, WEXITED) < 0) { - if (errno == EINTR) - continue; - - return log_error_errno(errno, "waitid() failed: %m"); - } - - set_remove(pids, PID_TO_PTR(status.si_pid)); - break; - } - - if (!is_clean_exit(status.si_code, status.si_status, NULL)) - log_error("Password agent failed with: %d", status.si_status); - - terminate_agents(pids); - return 0; -} - -int main(int argc, char *argv[]) { - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - if (arg_console && !arg_device) - /* - * Spawn for each console device a separate process. - */ - r = ask_on_consoles(argc, argv); - else { - - if (arg_device) { - /* - * Later on, a controlling terminal will be acquired, - * therefore the current process has to become a session - * leader and should not have a controlling terminal already. - */ - (void) setsid(); - (void) release_terminal(); - } - - if (IN_SET(arg_action, ACTION_WATCH, ACTION_WALL)) - r = watch_passwords(); - else - r = show_passwords(); - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/udev/.gitignore b/src/udev/.gitignore deleted file mode 100644 index f5d8be3dc1..0000000000 --- a/src/udev/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/udev.pc -/keyboard-keys-from-name.gperf -/keyboard-keys-from-name.h -/keyboard-keys-list.txt diff --git a/src/udev/.vimrc b/src/udev/.vimrc deleted file mode 100644 index 366fbdca4b..0000000000 --- a/src/udev/.vimrc +++ /dev/null @@ -1,4 +0,0 @@ -" 'set exrc' in ~/.vimrc will read .vimrc from the current directory -set tabstop=8 -set shiftwidth=8 -set expandtab diff --git a/src/udev/Makefile b/src/udev/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/ata_id/Makefile b/src/udev/ata_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/ata_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c deleted file mode 100644 index 1e414664ce..0000000000 --- a/src/udev/ata_id/ata_id.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * ata_id - reads product/serial number from ATA drives - * - * Copyright (C) 2005-2008 Kay Sievers - * Copyright (C) 2009 Lennart Poettering - * Copyright (C) 2009-2010 David Zeuthen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "fd-util.h" -#include "libudev-private.h" -#include "log.h" -#include "udev-util.h" - -#define COMMAND_TIMEOUT_MSEC (30 * 1000) - -static int disk_scsi_inquiry_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[6] = { - /* - * INQUIRY, see SPC-4 section 6.4 - */ - [0] = 0x12, /* OPERATION CODE: INQUIRY */ - [3] = (buf_len >> 8), /* ALLOCATION LENGTH */ - [4] = (buf_len & 0xff), - }; - uint8_t sense[32] = {}; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof(cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof(sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof(sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_hdr.status == 0 && - io_hdr.host_status == 0 && - io_hdr.driver_status == 0)) { - errno = EIO; - return -1; - } - } else - return ret; - } - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_v4.device_status == 0 && - io_v4.transport_status == 0 && - io_v4.driver_status == 0)) { - errno = EIO; - return -1; - } - - return 0; -} - -static int disk_identify_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[12] = { - /* - * ATA Pass-Through 12 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - [0] = 0xa1, /* OPERATION CODE: 12 byte pass through */ - [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ - [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - [3] = 0, /* FEATURES */ - [4] = 1, /* SECTORS */ - [5] = 0, /* LBA LOW */ - [6] = 0, /* LBA MID */ - [7] = 0, /* LBA HIGH */ - [8] = 0 & 0x4F, /* SELECT */ - [9] = 0xEC, /* Command: ATA IDENTIFY DEVICE */ - }; - uint8_t sense[32] = {}; - uint8_t *desc = sense + 8; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof(cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof(sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof (sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - } else - return ret; - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - return -1; - } - - return 0; -} - -static int disk_identify_packet_device_command(int fd, - void *buf, - size_t buf_len) -{ - uint8_t cdb[16] = { - /* - * ATA Pass-Through 16 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - [0] = 0x85, /* OPERATION CODE: 16 byte pass through */ - [1] = 4 << 1, /* PROTOCOL: PIO Data-in */ - [2] = 0x2e, /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - [3] = 0, /* FEATURES */ - [4] = 0, /* FEATURES */ - [5] = 0, /* SECTORS */ - [6] = 1, /* SECTORS */ - [7] = 0, /* LBA LOW */ - [8] = 0, /* LBA LOW */ - [9] = 0, /* LBA MID */ - [10] = 0, /* LBA MID */ - [11] = 0, /* LBA HIGH */ - [12] = 0, /* LBA HIGH */ - [13] = 0, /* DEVICE */ - [14] = 0xA1, /* Command: ATA IDENTIFY PACKET DEVICE */ - [15] = 0, /* CONTROL */ - }; - uint8_t sense[32] = {}; - uint8_t *desc = sense + 8; - struct sg_io_v4 io_v4 = { - .guard = 'Q', - .protocol = BSG_PROTOCOL_SCSI, - .subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD, - .request_len = sizeof (cdb), - .request = (uintptr_t) cdb, - .max_response_len = sizeof (sense), - .response = (uintptr_t) sense, - .din_xfer_len = buf_len, - .din_xferp = (uintptr_t) buf, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - int ret; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr = { - .interface_id = 'S', - .cmdp = (unsigned char*) cdb, - .cmd_len = sizeof (cdb), - .dxferp = buf, - .dxfer_len = buf_len, - .sbp = sense, - .mx_sb_len = sizeof (sense), - .dxfer_direction = SG_DXFER_FROM_DEV, - .timeout = COMMAND_TIMEOUT_MSEC, - }; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - return ret; - } else - return ret; - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - return -1; - } - - return 0; -} - -/** - * disk_identify_get_string: - * @identify: A block of IDENTIFY data - * @offset_words: Offset of the string to get, in words. - * @dest: Destination buffer for the string. - * @dest_len: Length of destination buffer, in bytes. - * - * Copies the ATA string from @identify located at @offset_words into @dest. - */ -static void disk_identify_get_string(uint8_t identify[512], - unsigned int offset_words, - char *dest, - size_t dest_len) -{ - unsigned int c1; - unsigned int c2; - - while (dest_len > 0) { - c1 = identify[offset_words * 2 + 1]; - c2 = identify[offset_words * 2]; - *dest = c1; - dest++; - *dest = c2; - dest++; - offset_words++; - dest_len -= 2; - } -} - -static void disk_identify_fixup_string(uint8_t identify[512], - unsigned int offset_words, - size_t len) -{ - disk_identify_get_string(identify, offset_words, - (char *) identify + offset_words * 2, len); -} - -static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words) -{ - uint16_t *p; - - p = (uint16_t *) identify; - p[offset_words] = le16toh (p[offset_words]); -} - -/** - * disk_identify: - * @udev: The libudev context. - * @fd: File descriptor for the block device. - * @out_identify: Return location for IDENTIFY data. - * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. - * - * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the - * device represented by @fd. If successful, then the result will be - * copied into @out_identify and @out_is_packet_device. - * - * This routine is based on code from libatasmart, Copyright 2008 - * Lennart Poettering, LGPL v2.1. - * - * Returns: 0 if the data was successfully obtained, otherwise - * non-zero with errno set. - */ -static int disk_identify(struct udev *udev, - int fd, - uint8_t out_identify[512], - int *out_is_packet_device) -{ - int ret; - uint8_t inquiry_buf[36]; - int peripheral_device_type; - int all_nul_bytes; - int n; - int is_packet_device = 0; - - /* init results */ - memzero(out_identify, 512); - - /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device - * we could accidentally blank media. This is because MMC's BLANK - * command has the same op-code (0x61). - * - * To prevent this from happening we bail out if the device - * isn't a Direct Access Block Device, e.g. SCSI type 0x00 - * (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY - * command first... libata is handling this via its SCSI - * emulation layer. - * - * This also ensures that we're actually dealing with a device - * that understands SCSI commands. - * - * (Yes, it is a bit perverse that we're tunneling the ATA - * command through SCSI and relying on the ATA driver - * emulating SCSI well-enough...) - * - * (See commit 160b069c25690bfb0c785994c7c3710289179107 for - * the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635 - * for the original bug-report.) - */ - ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf)); - if (ret != 0) - goto out; - - /* SPC-4, section 6.4.2: Standard INQUIRY data */ - peripheral_device_type = inquiry_buf[0] & 0x1f; - if (peripheral_device_type == 0x05) - { - is_packet_device = 1; - ret = disk_identify_packet_device_command(fd, out_identify, 512); - goto check_nul_bytes; - } - if (peripheral_device_type != 0x00) { - ret = -1; - errno = EIO; - goto out; - } - - /* OK, now issue the IDENTIFY DEVICE command */ - ret = disk_identify_command(fd, out_identify, 512); - if (ret != 0) - goto out; - - check_nul_bytes: - /* Check if IDENTIFY data is all NUL bytes - if so, bail */ - all_nul_bytes = 1; - for (n = 0; n < 512; n++) { - if (out_identify[n] != '\0') { - all_nul_bytes = 0; - break; - } - } - - if (all_nul_bytes) { - ret = -1; - errno = EIO; - goto out; - } - -out: - if (out_is_packet_device != NULL) - *out_is_packet_device = is_packet_device; - return ret; -} - -int main(int argc, char *argv[]) -{ - _cleanup_udev_unref_ struct udev *udev = NULL; - struct hd_driveid id; - union { - uint8_t byte[512]; - uint16_t wyde[256]; - } identify; - char model[41]; - char model_enc[256]; - char serial[21]; - char revision[9]; - const char *node = NULL; - int export = 0; - _cleanup_close_ int fd = -1; - uint16_t word; - int is_packet_device = 0; - static const struct option options[] = { - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - return 0; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "xh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'x': - export = 1; - break; - case 'h': - printf("Usage: ata_id [--export] [--help] \n" - " -x,--export print values as environment keys\n" - " -h,--help print this help text\n\n"); - return 0; - } - } - - node = argv[optind]; - if (node == NULL) { - log_error("no node specified"); - return 1; - } - - fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC); - if (fd < 0) { - log_error("unable to open '%s'", node); - return 1; - } - - if (disk_identify(udev, fd, identify.byte, &is_packet_device) == 0) { - /* - * fix up only the fields from the IDENTIFY data that we are going to - * use and copy it into the hd_driveid struct for convenience - */ - disk_identify_fixup_string(identify.byte, 10, 20); /* serial */ - disk_identify_fixup_string(identify.byte, 23, 8); /* fwrev */ - disk_identify_fixup_string(identify.byte, 27, 40); /* model */ - disk_identify_fixup_uint16(identify.byte, 0); /* configuration */ - disk_identify_fixup_uint16(identify.byte, 75); /* queue depth */ - disk_identify_fixup_uint16(identify.byte, 76); /* SATA capabilities */ - disk_identify_fixup_uint16(identify.byte, 82); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 83); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 84); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 85); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 86); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 87); /* command set supported */ - disk_identify_fixup_uint16(identify.byte, 89); /* time required for SECURITY ERASE UNIT */ - disk_identify_fixup_uint16(identify.byte, 90); /* time required for enhanced SECURITY ERASE UNIT */ - disk_identify_fixup_uint16(identify.byte, 91); /* current APM values */ - disk_identify_fixup_uint16(identify.byte, 94); /* current AAM value */ - disk_identify_fixup_uint16(identify.byte, 108); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 109); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 110); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 111); /* WWN */ - disk_identify_fixup_uint16(identify.byte, 128); /* device lock function */ - disk_identify_fixup_uint16(identify.byte, 217); /* nominal media rotation rate */ - memcpy(&id, identify.byte, sizeof id); - } else { - /* If this fails, then try HDIO_GET_IDENTITY */ - if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { - log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node); - return 2; - } - } - - memcpy(model, id.model, 40); - model[40] = '\0'; - udev_util_encode_string(model, model_enc, sizeof(model_enc)); - util_replace_whitespace((char *) id.model, model, 40); - util_replace_chars(model, NULL); - util_replace_whitespace((char *) id.serial_no, serial, 20); - util_replace_chars(serial, NULL); - util_replace_whitespace((char *) id.fw_rev, revision, 8); - util_replace_chars(revision, NULL); - - if (export) { - /* Set this to convey the disk speaks the ATA protocol */ - printf("ID_ATA=1\n"); - - if ((id.config >> 8) & 0x80) { - /* This is an ATAPI device */ - switch ((id.config >> 8) & 0x1f) { - case 0: - printf("ID_TYPE=cd\n"); - break; - case 1: - printf("ID_TYPE=tape\n"); - break; - case 5: - printf("ID_TYPE=cd\n"); - break; - case 7: - printf("ID_TYPE=optical\n"); - break; - default: - printf("ID_TYPE=generic\n"); - break; - } - } else { - printf("ID_TYPE=disk\n"); - } - printf("ID_BUS=ata\n"); - printf("ID_MODEL=%s\n", model); - printf("ID_MODEL_ENC=%s\n", model_enc); - printf("ID_REVISION=%s\n", revision); - if (serial[0] != '\0') { - printf("ID_SERIAL=%s_%s\n", model, serial); - printf("ID_SERIAL_SHORT=%s\n", serial); - } else { - printf("ID_SERIAL=%s\n", model); - } - - if (id.command_set_1 & (1<<5)) { - printf("ID_ATA_WRITE_CACHE=1\n"); - printf("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0); - } - if (id.command_set_1 & (1<<10)) { - printf("ID_ATA_FEATURE_SET_HPA=1\n"); - printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0); - - /* - * TODO: use the READ NATIVE MAX ADDRESS command to get the native max address - * so it is easy to check whether the protected area is in use. - */ - } - if (id.command_set_1 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_PM=1\n"); - printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0); - } - if (id.command_set_1 & (1<<1)) { - printf("ID_ATA_FEATURE_SET_SECURITY=1\n"); - printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2); - if ((id.cfs_enable_1 & (1<<1))) /* enabled */ { - if (id.dlf & (1<<8)) - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n"); - else - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n"); - } - if (id.dlf & (1<<5)) - printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2); - if (id.dlf & (1<<4)) - printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n"); - if (id.dlf & (1<<3)) - printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n"); - if (id.dlf & (1<<2)) - printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n"); - } - if (id.command_set_1 & (1<<0)) { - printf("ID_ATA_FEATURE_SET_SMART=1\n"); - printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0); - } - if (id.command_set_2 & (1<<9)) { - printf("ID_ATA_FEATURE_SET_AAM=1\n"); - printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8); - printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff); - } - if (id.command_set_2 & (1<<5)) { - printf("ID_ATA_FEATURE_SET_PUIS=1\n"); - printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0); - } - if (id.command_set_2 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_APM=1\n"); - printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0); - if ((id.cfs_enable_2 & (1<<3))) - printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff); - } - if (id.command_set_2 & (1<<0)) - printf("ID_ATA_DOWNLOAD_MICROCODE=1\n"); - - /* - * Word 76 indicates the capabilities of a SATA device. A PATA device shall set - * word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then - * the device does not claim compliance with the Serial ATA specification and words - * 76 through 79 are not valid and shall be ignored. - */ - - word = identify.wyde[76]; - if (word != 0x0000 && word != 0xffff) { - printf("ID_ATA_SATA=1\n"); - /* - * If bit 2 of word 76 is set to one, then the device supports the Gen2 - * signaling rate of 3.0 Gb/s (see SATA 2.6). - * - * If bit 1 of word 76 is set to one, then the device supports the Gen1 - * signaling rate of 1.5 Gb/s (see SATA 2.6). - */ - if (word & (1<<2)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n"); - if (word & (1<<1)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n"); - } - - /* Word 217 indicates the nominal media rotation rate of the device */ - word = identify.wyde[217]; - if (word == 0x0001) - printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */ - else if (word >= 0x0401 && word <= 0xfffe) - printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word); - - /* - * Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier - * format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE. - * All other values are reserved. - */ - word = identify.wyde[108]; - if ((word & 0xf000) == 0x5000) { - uint64_t wwwn; - - wwwn = identify.wyde[108]; - wwwn <<= 16; - wwwn |= identify.wyde[109]; - wwwn <<= 16; - wwwn |= identify.wyde[110]; - wwwn <<= 16; - wwwn |= identify.wyde[111]; - printf("ID_WWN=0x%1$" PRIx64 "\n" - "ID_WWN_WITH_EXTENSION=0x%1$" PRIx64 "\n", - wwwn); - } - - /* from Linux's include/linux/ata.h */ - if (identify.wyde[0] == 0x848a || - identify.wyde[0] == 0x844a || - (identify.wyde[83] & 0xc004) == 0x4004) - printf("ID_ATA_CFA=1\n"); - } else { - if (serial[0] != '\0') - printf("%s_%s\n", model, serial); - else - printf("%s\n", model); - } - - return 0; -} diff --git a/src/udev/cdrom_id/Makefile b/src/udev/cdrom_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/cdrom_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c deleted file mode 100644 index 72f284f710..0000000000 --- a/src/udev/cdrom_id/cdrom_id.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * cdrom_id - optical drive and media information prober - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "libudev-private.h" -#include "random-util.h" - -/* device info */ -static unsigned int cd_cd_rom; -static unsigned int cd_cd_r; -static unsigned int cd_cd_rw; -static unsigned int cd_dvd_rom; -static unsigned int cd_dvd_r; -static unsigned int cd_dvd_rw; -static unsigned int cd_dvd_ram; -static unsigned int cd_dvd_plus_r; -static unsigned int cd_dvd_plus_rw; -static unsigned int cd_dvd_plus_r_dl; -static unsigned int cd_dvd_plus_rw_dl; -static unsigned int cd_bd; -static unsigned int cd_bd_r; -static unsigned int cd_bd_re; -static unsigned int cd_hddvd; -static unsigned int cd_hddvd_r; -static unsigned int cd_hddvd_rw; -static unsigned int cd_mo; -static unsigned int cd_mrw; -static unsigned int cd_mrw_w; - -/* media info */ -static unsigned int cd_media; -static unsigned int cd_media_cd_rom; -static unsigned int cd_media_cd_r; -static unsigned int cd_media_cd_rw; -static unsigned int cd_media_dvd_rom; -static unsigned int cd_media_dvd_r; -static unsigned int cd_media_dvd_rw; -static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */ -static unsigned int cd_media_dvd_rw_seq; /* sequential mode */ -static unsigned int cd_media_dvd_ram; -static unsigned int cd_media_dvd_plus_r; -static unsigned int cd_media_dvd_plus_rw; -static unsigned int cd_media_dvd_plus_r_dl; -static unsigned int cd_media_dvd_plus_rw_dl; -static unsigned int cd_media_bd; -static unsigned int cd_media_bd_r; -static unsigned int cd_media_bd_re; -static unsigned int cd_media_hddvd; -static unsigned int cd_media_hddvd_r; -static unsigned int cd_media_hddvd_rw; -static unsigned int cd_media_mo; -static unsigned int cd_media_mrw; -static unsigned int cd_media_mrw_w; - -static const char *cd_media_state = NULL; -static unsigned int cd_media_session_next; -static unsigned int cd_media_session_count; -static unsigned int cd_media_track_count; -static unsigned int cd_media_track_count_data; -static unsigned int cd_media_track_count_audio; -static unsigned long long int cd_media_session_last_offset; - -#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) -#define SK(errcode) (((errcode) >> 16) & 0xF) -#define ASC(errcode) (((errcode) >> 8) & 0xFF) -#define ASCQ(errcode) ((errcode) & 0xFF) - -static bool is_mounted(const char *device) -{ - struct stat statbuf; - FILE *fp; - int maj, min; - bool mounted = false; - - if (stat(device, &statbuf) < 0) - return false; - - fp = fopen("/proc/self/mountinfo", "re"); - if (fp == NULL) - return false; - while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { - if (makedev(maj, min) == statbuf.st_rdev) { - mounted = true; - break; - } - } - fclose(fp); - return mounted; -} - -static void info_scsi_cmd_err(struct udev *udev, const char *cmd, int err) -{ - if (err == -1) { - log_debug("%s failed", cmd); - return; - } - log_debug("%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh", cmd, SK(err), ASC(err), ASCQ(err)); -} - -struct scsi_cmd { - struct cdrom_generic_command cgc; - union { - struct request_sense s; - unsigned char u[18]; - } _sense; - struct sg_io_hdr sg_io; -}; - -static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) -{ - memzero(cmd, sizeof(struct scsi_cmd)); - cmd->cgc.quiet = 1; - cmd->cgc.sense = &cmd->_sense.s; - cmd->sg_io.interface_id = 'S'; - cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); - cmd->sg_io.cmdp = cmd->cgc.cmd; - cmd->sg_io.sbp = cmd->_sense.u; - cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; -} - -static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg) -{ - cmd->sg_io.cmd_len = i + 1; - cmd->cgc.cmd[i] = arg; -} - -#define CHECK_CONDITION 0x01 - -static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) -{ - int ret = 0; - - if (bufsize > 0) { - cmd->sg_io.dxferp = buf; - cmd->sg_io.dxfer_len = bufsize; - cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; - } else { - cmd->sg_io.dxfer_direction = SG_DXFER_NONE; - } - if (ioctl(fd, SG_IO, &cmd->sg_io)) - return -1; - - if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { - errno = EIO; - ret = -1; - if (cmd->sg_io.masked_status & CHECK_CONDITION) { - ret = ERRCODE(cmd->_sense.u); - if (ret == 0) - ret = -1; - } - } - return ret; -} - -static int media_lock(struct udev *udev, int fd, bool lock) -{ - int err; - - /* disable the kernel's lock logic */ - err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); - if (err < 0) - log_debug("CDROM_CLEAR_OPTIONS, CDO_LOCK failed"); - - err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); - if (err < 0) - log_debug("CDROM_LOCKDOOR failed"); - - return err; -} - -static int media_eject(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x1b); - scsi_cmd_set(udev, &sc, 4, 0x02); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, NULL, 0); - if ((err != 0)) { - info_scsi_cmd_err(udev, "START_STOP_UNIT", err); - return -1; - } - return 0; -} - -static int cd_capability_compat(struct udev *udev, int fd) -{ - int capability; - - capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); - if (capability < 0) { - log_debug("CDROM_GET_CAPABILITY failed"); - return -1; - } - - if (capability & CDC_CD_R) - cd_cd_r = 1; - if (capability & CDC_CD_RW) - cd_cd_rw = 1; - if (capability & CDC_DVD) - cd_dvd_rom = 1; - if (capability & CDC_DVD_R) - cd_dvd_r = 1; - if (capability & CDC_DVD_RAM) - cd_dvd_ram = 1; - if (capability & CDC_MRW) - cd_mrw = 1; - if (capability & CDC_MRW_W) - cd_mrw_w = 1; - return 0; -} - -static int cd_media_compat(struct udev *udev, int fd) -{ - if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { - log_debug("CDROM_DRIVE_STATUS != CDS_DISC_OK"); - return -1; - } - cd_media = 1; - return 0; -} - -static int cd_inquiry(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char inq[128]; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x12); - scsi_cmd_set(udev, &sc, 4, 36); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, inq, 36); - if ((err != 0)) { - info_scsi_cmd_err(udev, "INQUIRY", err); - return -1; - } - - if ((inq[0] & 0x1F) != 5) { - log_debug("not an MMC unit"); - return -1; - } - - log_debug("INQUIRY: [%.8s][%.16s][%.4s]", inq + 8, inq + 16, inq + 32); - return 0; -} - -static void feature_profile_media(struct udev *udev, int cur_profile) -{ - switch (cur_profile) { - case 0x03: - case 0x04: - case 0x05: - log_debug("profile 0x%02x ", cur_profile); - cd_media = 1; - cd_media_mo = 1; - break; - case 0x08: - log_debug("profile 0x%02x media_cd_rom", cur_profile); - cd_media = 1; - cd_media_cd_rom = 1; - break; - case 0x09: - log_debug("profile 0x%02x media_cd_r", cur_profile); - cd_media = 1; - cd_media_cd_r = 1; - break; - case 0x0a: - log_debug("profile 0x%02x media_cd_rw", cur_profile); - cd_media = 1; - cd_media_cd_rw = 1; - break; - case 0x10: - log_debug("profile 0x%02x media_dvd_ro", cur_profile); - cd_media = 1; - cd_media_dvd_rom = 1; - break; - case 0x11: - log_debug("profile 0x%02x media_dvd_r", cur_profile); - cd_media = 1; - cd_media_dvd_r = 1; - break; - case 0x12: - log_debug("profile 0x%02x media_dvd_ram", cur_profile); - cd_media = 1; - cd_media_dvd_ram = 1; - break; - case 0x13: - log_debug("profile 0x%02x media_dvd_rw_ro", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_ro = 1; - break; - case 0x14: - log_debug("profile 0x%02x media_dvd_rw_seq", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_seq = 1; - break; - case 0x1B: - log_debug("profile 0x%02x media_dvd_plus_r", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r = 1; - break; - case 0x1A: - log_debug("profile 0x%02x media_dvd_plus_rw", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw = 1; - break; - case 0x2A: - log_debug("profile 0x%02x media_dvd_plus_rw_dl", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw_dl = 1; - break; - case 0x2B: - log_debug("profile 0x%02x media_dvd_plus_r_dl", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r_dl = 1; - break; - case 0x40: - log_debug("profile 0x%02x media_bd", cur_profile); - cd_media = 1; - cd_media_bd = 1; - break; - case 0x41: - case 0x42: - log_debug("profile 0x%02x media_bd_r", cur_profile); - cd_media = 1; - cd_media_bd_r = 1; - break; - case 0x43: - log_debug("profile 0x%02x media_bd_re", cur_profile); - cd_media = 1; - cd_media_bd_re = 1; - break; - case 0x50: - log_debug("profile 0x%02x media_hddvd", cur_profile); - cd_media = 1; - cd_media_hddvd = 1; - break; - case 0x51: - log_debug("profile 0x%02x media_hddvd_r", cur_profile); - cd_media = 1; - cd_media_hddvd_r = 1; - break; - case 0x52: - log_debug("profile 0x%02x media_hddvd_rw", cur_profile); - cd_media = 1; - cd_media_hddvd_rw = 1; - break; - default: - log_debug("profile 0x%02x ", cur_profile); - break; - } -} - -static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) -{ - unsigned int i; - - for (i = 0; i+4 <= size; i += 4) { - int profile; - - profile = profiles[i] << 8 | profiles[i+1]; - switch (profile) { - case 0x03: - case 0x04: - case 0x05: - log_debug("profile 0x%02x mo", profile); - cd_mo = 1; - break; - case 0x08: - log_debug("profile 0x%02x cd_rom", profile); - cd_cd_rom = 1; - break; - case 0x09: - log_debug("profile 0x%02x cd_r", profile); - cd_cd_r = 1; - break; - case 0x0A: - log_debug("profile 0x%02x cd_rw", profile); - cd_cd_rw = 1; - break; - case 0x10: - log_debug("profile 0x%02x dvd_rom", profile); - cd_dvd_rom = 1; - break; - case 0x12: - log_debug("profile 0x%02x dvd_ram", profile); - cd_dvd_ram = 1; - break; - case 0x13: - case 0x14: - log_debug("profile 0x%02x dvd_rw", profile); - cd_dvd_rw = 1; - break; - case 0x1B: - log_debug("profile 0x%02x dvd_plus_r", profile); - cd_dvd_plus_r = 1; - break; - case 0x1A: - log_debug("profile 0x%02x dvd_plus_rw", profile); - cd_dvd_plus_rw = 1; - break; - case 0x2A: - log_debug("profile 0x%02x dvd_plus_rw_dl", profile); - cd_dvd_plus_rw_dl = 1; - break; - case 0x2B: - log_debug("profile 0x%02x dvd_plus_r_dl", profile); - cd_dvd_plus_r_dl = 1; - break; - case 0x40: - cd_bd = 1; - log_debug("profile 0x%02x bd", profile); - break; - case 0x41: - case 0x42: - cd_bd_r = 1; - log_debug("profile 0x%02x bd_r", profile); - break; - case 0x43: - cd_bd_re = 1; - log_debug("profile 0x%02x bd_re", profile); - break; - case 0x50: - cd_hddvd = 1; - log_debug("profile 0x%02x hddvd", profile); - break; - case 0x51: - cd_hddvd_r = 1; - log_debug("profile 0x%02x hddvd_r", profile); - break; - case 0x52: - cd_hddvd_rw = 1; - log_debug("profile 0x%02x hddvd_rw", profile); - break; - default: - log_debug("profile 0x%02x ", profile); - break; - } - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles_old_mmc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - unsigned char header[32]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - if (cd_media == 1) { - log_debug("no current profile, but disc is present; assuming CD-ROM"); - cd_media_cd_rom = 1; - cd_media_track_count = 1; - cd_media_track_count_data = 1; - return 0; - } else { - log_debug("no current profile, assuming no media"); - return -1; - } - }; - - cd_media = 1; - - if (header[2] & 16) { - cd_media_cd_rw = 1; - log_debug("profile 0x0a media_cd_rw"); - } else if ((header[2] & 3) < 2 && cd_cd_r) { - cd_media_cd_r = 1; - log_debug("profile 0x09 media_cd_r"); - } else { - cd_media_cd_rom = 1; - log_debug("profile 0x08 media_cd_rom"); - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char features[65530]; - unsigned int cur_profile = 0; - unsigned int len; - unsigned int i; - int err; - int ret; - - ret = -1; - - /* First query the current profile */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 8, 8); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, 8); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ - if (SK(err) == 0x5 && (ASC(err) == 0x20 || ASC(err) == 0x24)) { - log_debug("drive is pre-MMC2 and does not support 46h get configuration command"); - log_debug("trying to work around the problem"); - ret = cd_profiles_old_mmc(udev, fd); - } - goto out; - } - - cur_profile = features[6] << 8 | features[7]; - if (cur_profile > 0) { - log_debug("current profile 0x%02x", cur_profile); - feature_profile_media (udev, cur_profile); - ret = 0; /* we have media */ - } else { - log_debug("no current profile, assuming no media"); - } - - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); - - if (len > sizeof(features)) { - log_debug("can not get features in a single query, truncating"); - len = sizeof(features); - } else if (len <= 8) - len = sizeof(features); - - /* Now get the full feature buffer */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - return -1; - } - - /* parse the length once more, in case the drive decided to have other features suddenly :) */ - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - log_debug("GET CONFIGURATION: size of features buffer 0x%04x", len); - - if (len > sizeof(features)) { - log_debug("can not get features in a single query, truncating"); - len = sizeof(features); - } - - /* device features */ - for (i = 8; i+4 < len; i += (4 + features[i+3])) { - unsigned int feature; - - feature = features[i] << 8 | features[i+1]; - - switch (feature) { - case 0x00: - log_debug("GET CONFIGURATION: feature 'profiles', with %i entries", features[i+3] / 4); - feature_profiles(udev, &features[i]+4, MIN(features[i+3], len - i - 4)); - break; - default: - log_debug("GET CONFIGURATION: feature 0x%04x , with 0x%02x bytes", feature, features[i+3]); - break; - } - } -out: - return ret; -} - -static int cd_media_info(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[32]; - static const char *media_status[] = { - "blank", - "appendable", - "complete", - "other" - }; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - return -1; - }; - - cd_media = 1; - log_debug("disk type %02x", header[8]); - log_debug("hardware reported media status: %s", media_status[header[2] & 3]); - - /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ - if (!cd_media_cd_rom) - cd_media_state = media_status[header[2] & 3]; - - /* fresh DVD-RW in restricted overwite mode reports itself as - * "appendable"; change it to "blank" to make it consistent with what - * gets reported after blanking, and what userspace expects */ - if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) - cd_media_state = media_status[0]; - - /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are - * always "complete", DVD-RAM are "other" or "complete" if the disc is - * write protected; we need to check the contents if it is blank */ - if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { - unsigned char buffer[32 * 2048]; - unsigned char len; - int offset; - - if (cd_media_dvd_ram) { - /* a write protected dvd-ram may report "complete" status */ - - unsigned char dvdstruct[8]; - unsigned char format[12]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0xAD); - scsi_cmd_set(udev, &sc, 7, 0xC0); - scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); - scsi_cmd_set(udev, &sc, 11, 0); - err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err); - return -1; - } - if (dvdstruct[4] & 0x02) { - cd_media_state = media_status[2]; - log_debug("write-protected DVD-RAM media inserted"); - goto determined; - } - - /* let's make sure we don't try to read unformatted media */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x23); - scsi_cmd_set(udev, &sc, 8, sizeof(format)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err); - return -1; - } - - len = format[3]; - if (len & 7 || len < 16) { - log_debug("invalid format capacities length"); - return -1; - } - - switch(format[8] & 3) { - case 1: - log_debug("unformatted DVD-RAM media inserted"); - /* This means that last format was interrupted - * or failed, blank dvd-ram discs are factory - * formatted. Take no action here as it takes - * quite a while to reformat a dvd-ram and it's - * not automatically started */ - goto determined; - - case 2: - log_debug("formatted DVD-RAM media inserted"); - break; - - case 3: - cd_media = 0; //return no media - log_debug("format capacities returned no media"); - return -1; - } - } - - /* Take a closer look at formatted media (unformatted DVD+RW - * has "blank" status", DVD-RAM was examined earlier) and check - * for ISO and UDF PVDs or a fs superblock presence and do it - * in one ioctl (we need just sectors 0 and 16) */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x28); - scsi_cmd_set(udev, &sc, 5, 0); - scsi_cmd_set(udev, &sc, 8, 32); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); - if ((err != 0)) { - cd_media = 0; - info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); - return -1; - } - - /* if any non-zero data is found in sector 16 (iso and udf) or - * eventually 0 (fat32 boot sector, ext2 superblock, etc), disc - * is assumed non-blank */ - - for (offset = 32768; offset < (32768 + 2048); offset++) { - if (buffer [offset]) { - log_debug("data in block 16, assuming complete"); - goto determined; - } - } - - for (offset = 0; offset < 2048; offset++) { - if (buffer [offset]) { - log_debug("data in block 0, assuming complete"); - goto determined; - } - } - - cd_media_state = media_status[0]; - log_debug("no data in blocks 0 or 16, assuming blank"); - } - -determined: - /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in - * restricted overwrite mode can never append, only in sequential mode */ - if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) - cd_media_session_next = header[10] << 8 | header[5]; - cd_media_session_count = header[9] << 8 | header[4]; - cd_media_track_count = header[11] << 8 | header[6]; - - return 0; -} - -static int cd_media_toc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[12]; - unsigned char toc[65536]; - unsigned int len, i, num_tracks; - unsigned char *p; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, 1); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC", err); - return -1; - } - - len = (header[0] << 8 | header[1]) + 2; - log_debug("READ TOC: len: %d, start track: %d, end track: %d", len, header[2], header[3]); - if (len > sizeof(toc)) - return -1; - if (len < 2) - return -1; - /* 2: first track, 3: last track */ - num_tracks = header[3] - header[2] + 1; - - /* empty media has no tracks */ - if (len < 8) - return 0; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ - scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, toc, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (tracks)", err); - return -1; - } - - /* Take care to not iterate beyond the last valid track as specified in - * the TOC, but also avoid going beyond the TOC length, just in case - * the last track number is invalidly large */ - for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { - unsigned int block; - unsigned int is_data_track; - - is_data_track = (p[1] & 0x04) != 0; - - block = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; - log_debug("track=%u info=0x%x(%s) start_block=%u", - p[2], p[1] & 0x0f, is_data_track ? "data":"audio", block); - - if (is_data_track) - cd_media_track_count_data++; - else - cd_media_track_count_audio++; - } - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (multi session)", err); - return -1; - } - len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7]; - log_debug("last track %u starts at block %u", header[4+2], len); - cd_media_session_last_offset = (unsigned long long int)len * 2048; - return 0; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - static const struct option options[] = { - { "lock-media", no_argument, NULL, 'l' }, - { "unlock-media", no_argument, NULL, 'u' }, - { "eject-media", no_argument, NULL, 'e' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - bool eject = false; - bool lock = false; - bool unlock = false; - const char *node = NULL; - int fd = -1; - int cnt; - int rc = 0; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - goto exit; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "deluh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'l': - lock = true; - break; - case 'u': - unlock = true; - break; - case 'e': - eject = true; - break; - case 'd': - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - log_open(); - break; - case 'h': - printf("Usage: cdrom_id [options] \n" - " -l,--lock-media lock the media (to enable eject request events)\n" - " -u,--unlock-media unlock the media\n" - " -e,--eject-media eject the media\n" - " -d,--debug debug to stderr\n" - " -h,--help print this help text\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - node = argv[optind]; - if (!node) { - log_error("no device"); - fprintf(stderr, "no device\n"); - rc = 1; - goto exit; - } - - initialize_srand(); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|(is_mounted(node) ? 0 : O_EXCL)); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (100 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) { - log_debug("unable to open '%s'", node); - fprintf(stderr, "unable to open '%s'\n", node); - rc = 1; - goto exit; - } - log_debug("probing: '%s'", node); - - /* same data as original cdrom_id */ - if (cd_capability_compat(udev, fd) < 0) { - rc = 1; - goto exit; - } - - /* check for media - don't bail if there's no media as we still need to - * to read profiles */ - cd_media_compat(udev, fd); - - /* check if drive talks MMC */ - if (cd_inquiry(udev, fd) < 0) - goto work; - - /* read drive and possibly current profile */ - if (cd_profiles(udev, fd) != 0) - goto work; - - /* at this point we are guaranteed to have media in the drive - find out more about it */ - - /* get session/track info */ - cd_media_toc(udev, fd); - - /* get writable media state */ - cd_media_info(udev, fd); - -work: - /* lock the media, so we enable eject button events */ - if (lock && cd_media) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (lock)"); - media_lock(udev, fd, true); - } - - if (unlock && cd_media) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); - media_lock(udev, fd, false); - } - - if (eject) { - log_debug("PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)"); - media_lock(udev, fd, false); - log_debug("START_STOP_UNIT (eject)"); - media_eject(udev, fd); - } - - printf("ID_CDROM=1\n"); - if (cd_cd_rom) - printf("ID_CDROM_CD=1\n"); - if (cd_cd_r) - printf("ID_CDROM_CD_R=1\n"); - if (cd_cd_rw) - printf("ID_CDROM_CD_RW=1\n"); - if (cd_dvd_rom) - printf("ID_CDROM_DVD=1\n"); - if (cd_dvd_r) - printf("ID_CDROM_DVD_R=1\n"); - if (cd_dvd_rw) - printf("ID_CDROM_DVD_RW=1\n"); - if (cd_dvd_ram) - printf("ID_CDROM_DVD_RAM=1\n"); - if (cd_dvd_plus_r) - printf("ID_CDROM_DVD_PLUS_R=1\n"); - if (cd_dvd_plus_rw) - printf("ID_CDROM_DVD_PLUS_RW=1\n"); - if (cd_dvd_plus_r_dl) - printf("ID_CDROM_DVD_PLUS_R_DL=1\n"); - if (cd_dvd_plus_rw_dl) - printf("ID_CDROM_DVD_PLUS_RW_DL=1\n"); - if (cd_bd) - printf("ID_CDROM_BD=1\n"); - if (cd_bd_r) - printf("ID_CDROM_BD_R=1\n"); - if (cd_bd_re) - printf("ID_CDROM_BD_RE=1\n"); - if (cd_hddvd) - printf("ID_CDROM_HDDVD=1\n"); - if (cd_hddvd_r) - printf("ID_CDROM_HDDVD_R=1\n"); - if (cd_hddvd_rw) - printf("ID_CDROM_HDDVD_RW=1\n"); - if (cd_mo) - printf("ID_CDROM_MO=1\n"); - if (cd_mrw) - printf("ID_CDROM_MRW=1\n"); - if (cd_mrw_w) - printf("ID_CDROM_MRW_W=1\n"); - - if (cd_media) - printf("ID_CDROM_MEDIA=1\n"); - if (cd_media_mo) - printf("ID_CDROM_MEDIA_MO=1\n"); - if (cd_media_mrw) - printf("ID_CDROM_MEDIA_MRW=1\n"); - if (cd_media_mrw_w) - printf("ID_CDROM_MEDIA_MRW_W=1\n"); - if (cd_media_cd_rom) - printf("ID_CDROM_MEDIA_CD=1\n"); - if (cd_media_cd_r) - printf("ID_CDROM_MEDIA_CD_R=1\n"); - if (cd_media_cd_rw) - printf("ID_CDROM_MEDIA_CD_RW=1\n"); - if (cd_media_dvd_rom) - printf("ID_CDROM_MEDIA_DVD=1\n"); - if (cd_media_dvd_r) - printf("ID_CDROM_MEDIA_DVD_R=1\n"); - if (cd_media_dvd_ram) - printf("ID_CDROM_MEDIA_DVD_RAM=1\n"); - if (cd_media_dvd_rw) - printf("ID_CDROM_MEDIA_DVD_RW=1\n"); - if (cd_media_dvd_plus_r) - printf("ID_CDROM_MEDIA_DVD_PLUS_R=1\n"); - if (cd_media_dvd_plus_rw) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW=1\n"); - if (cd_media_dvd_plus_rw_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1\n"); - if (cd_media_dvd_plus_r_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_R_DL=1\n"); - if (cd_media_bd) - printf("ID_CDROM_MEDIA_BD=1\n"); - if (cd_media_bd_r) - printf("ID_CDROM_MEDIA_BD_R=1\n"); - if (cd_media_bd_re) - printf("ID_CDROM_MEDIA_BD_RE=1\n"); - if (cd_media_hddvd) - printf("ID_CDROM_MEDIA_HDDVD=1\n"); - if (cd_media_hddvd_r) - printf("ID_CDROM_MEDIA_HDDVD_R=1\n"); - if (cd_media_hddvd_rw) - printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); - - if (cd_media_state != NULL) - printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); - if (cd_media_session_next > 0) - printf("ID_CDROM_MEDIA_SESSION_NEXT=%u\n", cd_media_session_next); - if (cd_media_session_count > 0) - printf("ID_CDROM_MEDIA_SESSION_COUNT=%u\n", cd_media_session_count); - if (cd_media_session_count > 1 && cd_media_session_last_offset > 0) - printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu\n", cd_media_session_last_offset); - if (cd_media_track_count > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT=%u\n", cd_media_track_count); - if (cd_media_track_count_audio > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%u\n", cd_media_track_count_audio); - if (cd_media_track_count_data > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%u\n", cd_media_track_count_data); -exit: - if (fd >= 0) - close(fd); - udev_unref(udev); - log_close(); - return rc; -} diff --git a/src/udev/collect/Makefile b/src/udev/collect/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/collect/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c deleted file mode 100644 index 349585b634..0000000000 --- a/src/udev/collect/collect.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Collect variables across events. - * - * usage: collect [--add|--remove] - * - * Adds ID to the list governed by . - * must be part of the ID list . - * If all IDs given by are listed (ie collect has been - * invoked for each ID in ) collect returns 0, the - * number of missing IDs otherwise. - * A negative number is returned on error. - * - * Copyright(C) 2007, Hannes Reinecke - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include - -#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 - -enum collect_state { - STATE_NONE, - STATE_OLD, - STATE_CONFIRMED, -}; - -struct _mate { - struct udev_list_node node; - char *name; - enum collect_state state; -}; - -static struct udev_list_node bunch; -static int debug; - -/* This can increase dynamically */ -static size_t bufsize = BUFSIZE; - -static inline struct _mate *node_to_mate(struct udev_list_node *node) -{ - return container_of(node, struct _mate, node); -} - -noreturn static void sig_alrm(int signo) -{ - exit(4); -} - -static void usage(void) -{ - printf("%s [options] \n\n" - "Collect variables across events.\n\n" - " -h --help Print this message\n" - " -a --add Add ID to the list \n" - " -r --remove Remove ID from the list \n" - " -d --debug Debug to stderr\n\n" - " Adds ID to the list governed by .\n" - " must be part of the list .\n" - " If all IDs given by are listed (ie collect has been\n" - " invoked for each ID in ) collect returns 0, the\n" - " number of missing IDs otherwise.\n" - " On error a negative number is returned.\n\n" - , program_invocation_short_name); -} - -/* - * prepare - * - * Prepares the database file - */ -static int prepare(char *dir, char *filename) -{ - char buf[512]; - int r, fd; - - r = mkdir(dir, 0700); - if (r < 0 && errno != EEXIST) - return -errno; - - xsprintf(buf, "%s/%s", dir, filename); - - fd = open(buf,O_RDWR|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR); - if (fd < 0) - fprintf(stderr, "Cannot open %s: %m\n", buf); - - if (lockf(fd,F_TLOCK,0) < 0) { - if (debug) - fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT); - if (errno == EAGAIN || errno == EACCES) { - alarm(UDEV_ALARM_TIMEOUT); - lockf(fd, F_LOCK, 0); - if (debug) - fprintf(stderr, "Acquired lock on %s\n", buf); - } else { - if (debug) - fprintf(stderr, "Could not get lock on %s: %m\n", buf); - } - } - - return fd; -} - -/* - * Read checkpoint file - * - * Tricky reading this. We allocate a buffer twice as large - * as we're going to read. Then we read into the upper half - * of that buffer and start parsing. - * Once we do _not_ find end-of-work terminator (whitespace - * character) we move the upper half to the lower half, - * adjust the read pointer and read the next bit. - * Quite clever methinks :-) - * I should become a programmer ... - * - * Yes, one could have used fgets() for this. But then we'd - * have to use freopen etc which I found quite tedious. - */ -static int checkout(int fd) -{ - int len; - char *buf, *ptr, *word = NULL; - struct _mate *him; - - restart: - len = bufsize >> 1; - buf = malloc(bufsize + 1); - if (!buf) - return log_oom(); - memset(buf, ' ', bufsize); - buf[bufsize] = '\0'; - - ptr = buf + len; - while ((read(fd, buf + len, len)) > 0) { - while (ptr && *ptr) { - word = ptr; - ptr = strpbrk(word," \n\t\r"); - if (!ptr && word < (buf + len)) { - bufsize = bufsize << 1; - if (debug) - fprintf(stderr, "ID overflow, restarting with size %zu\n", bufsize); - free(buf); - lseek(fd, 0, SEEK_SET); - goto restart; - } - if (ptr) { - *ptr = '\0'; - ptr++; - if (!strlen(word)) - continue; - - if (debug) - fprintf(stderr, "Found word %s\n", word); - him = malloc(sizeof (struct _mate)); - if (!him) { - free(buf); - return log_oom(); - } - him->name = strdup(word); - if (!him->name) { - free(buf); - free(him); - return log_oom(); - } - him->state = STATE_OLD; - udev_list_node_append(&him->node, &bunch); - word = NULL; - } - } - memcpy(buf, buf + len, len); - memset(buf + len, ' ', len); - - if (!ptr) - ptr = word; - if (!ptr) - break; - ptr -= len; - } - - free(buf); - return 0; -} - -/* - * invite - * - * Adds a new ID 'us' to the internal list, - * marks it as confirmed. - */ -static void invite(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Adding ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, us)) { - him->state = STATE_CONFIRMED; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); - -} - -/* - * reject - * - * Marks the ID 'us' as invalid, - * causing it to be removed when the - * list is written out. - */ -static void reject(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Removing ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, us)) { - him->state = STATE_NONE; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); -} - -/* - * kickout - * - * Remove all IDs in the internal list which are not part - * of the list passed via the command line. - */ -static void kickout(void) -{ - struct udev_list_node *him_node; - struct udev_list_node *tmp; - - udev_list_node_foreach_safe(him_node, tmp, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_OLD) { - udev_list_node_remove(&him->node); - free(him->name); - free(him); - } - } -} - -/* - * missing - * - * Counts all missing IDs in the internal list. - */ -static int missing(int fd) -{ - char *buf; - int ret = 0; - struct udev_list_node *him_node; - - buf = malloc(bufsize); - if (!buf) - return log_oom(); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_NONE) { - ret++; - } else { - while (strlen(him->name)+1 >= bufsize) { - char *tmpbuf; - - bufsize = bufsize << 1; - tmpbuf = realloc(buf, bufsize); - if (!tmpbuf) { - free(buf); - return log_oom(); - } - buf = tmpbuf; - } - snprintf(buf, strlen(him->name)+2, "%s ", him->name); - if (write(fd, buf, strlen(buf)) < 0) { - free(buf); - return -1; - } - } - } - - free(buf); - return ret; -} - -/* - * everybody - * - * Prints out the status of the internal list. - */ -static void everybody(void) -{ - struct udev_list_node *him_node; - const char *state = ""; - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - switch (him->state) { - case STATE_NONE: - state = "none"; - break; - case STATE_OLD: - state = "old"; - break; - case STATE_CONFIRMED: - state = "confirmed"; - break; - } - fprintf(stderr, "ID: %s=%s\n", him->name, state); - } -} - -int main(int argc, char **argv) -{ - struct udev *udev; - static const struct option options[] = { - { "add", no_argument, NULL, 'a' }, - { "remove", no_argument, NULL, 'r' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - int argi; - char *checkpoint, *us; - int fd; - int i; - int ret = EXIT_SUCCESS; - int prune = 0; - char tmpdir[UTIL_PATH_SIZE]; - - udev = udev_new(); - if (udev == NULL) { - ret = EXIT_FAILURE; - goto exit; - } - - for (;;) { - int option; - - option = getopt_long(argc, argv, "ardh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'a': - prune = 0; - break; - case 'r': - prune = 1; - break; - case 'd': - debug = 1; - break; - case 'h': - usage(); - goto exit; - default: - ret = 1; - goto exit; - } - } - - argi = optind; - if (argi + 2 > argc) { - printf("Missing parameter(s)\n"); - ret = 1; - goto exit; - } - checkpoint = argv[argi++]; - us = argv[argi++]; - - if (signal(SIGALRM, sig_alrm) == SIG_ERR) { - fprintf(stderr, "Cannot set SIGALRM: %m\n"); - ret = 2; - goto exit; - } - - udev_list_node_init(&bunch); - - if (debug) - fprintf(stderr, "Using checkpoint '%s'\n", checkpoint); - - strscpyl(tmpdir, sizeof(tmpdir), "/run/udev/collect", NULL); - fd = prepare(tmpdir, checkpoint); - if (fd < 0) { - ret = 3; - goto out; - } - - if (checkout(fd) < 0) { - ret = 2; - goto out; - } - - for (i = argi; i < argc; i++) { - struct udev_list_node *him_node; - struct _mate *who; - - who = NULL; - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (streq(him->name, argv[i])) - who = him; - } - if (!who) { - struct _mate *him; - - if (debug) - fprintf(stderr, "ID %s: not in database\n", argv[i]); - him = new(struct _mate, 1); - if (!him) { - ret = ENOMEM; - goto out; - } - - him->name = strdup(argv[i]); - if (!him->name) { - free(him); - ret = ENOMEM; - goto out; - } - - him->state = STATE_NONE; - udev_list_node_append(&him->node, &bunch); - } else { - if (debug) - fprintf(stderr, "ID %s: found in database\n", argv[i]); - who->state = STATE_CONFIRMED; - } - } - - if (prune) - reject(us); - else - invite(us); - - if (debug) { - everybody(); - fprintf(stderr, "Prune lists\n"); - } - kickout(); - - lseek(fd, 0, SEEK_SET); - ftruncate(fd, 0); - ret = missing(fd); - - lockf(fd, F_ULOCK, 0); - close(fd); -out: - if (debug) - everybody(); - if (ret >= 0) - printf("COLLECT_%s=%d\n", checkpoint, ret); -exit: - udev_unref(udev); - return ret; -} diff --git a/src/udev/mtd_probe/Makefile b/src/udev/mtd_probe/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/mtd_probe/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c deleted file mode 100644 index 462fab7623..0000000000 --- a/src/udev/mtd_probe/mtd_probe.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtd_probe.h" - -int main(int argc, char** argv) -{ - int mtd_fd; - int error; - mtd_info_t mtd_info; - - if (argc != 2) { - printf("usage: mtd_probe /dev/mtd[n]\n"); - return 1; - } - - mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC); - if (mtd_fd == -1) { - perror("open"); - exit(-1); - } - - error = ioctl(mtd_fd, MEMGETINFO, &mtd_info); - if (error == -1) { - perror("ioctl"); - exit(-1); - } - - probe_smart_media(mtd_fd, &mtd_info); - return -1; -} diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h deleted file mode 100644 index 68e4954537..0000000000 --- a/src/udev/mtd_probe/mtd_probe.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include - -#include "macro.h" - -/* Full oob structure as written on the flash */ -struct sm_oob { - uint32_t reserved; - uint8_t data_status; - uint8_t block_status; - uint8_t lba_copy1[2]; - uint8_t ecc2[3]; - uint8_t lba_copy2[2]; - uint8_t ecc1[3]; -} _packed_; - -/* one sector is always 512 bytes, but it can consist of two nand pages */ -#define SM_SECTOR_SIZE 512 - -/* oob area is also 16 bytes, but might be from two pages */ -#define SM_OOB_SIZE 16 - -/* This is maximum zone size, and all devices that have more that one zone - have this size */ -#define SM_MAX_ZONE_SIZE 1024 - -/* support for small page nand */ -#define SM_SMALL_PAGE 256 -#define SM_SMALL_OOB_SIZE 8 - -void probe_smart_media(int mtd_fd, mtd_info_t *info); diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c deleted file mode 100644 index 2a7ba17637..0000000000 --- a/src/udev/mtd_probe/probe_smartmedia.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtd_probe.h" - -static const uint8_t cis_signature[] = { - 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 -}; - - -void probe_smart_media(int mtd_fd, mtd_info_t* info) -{ - int sector_size; - int block_size; - int size_in_megs; - int spare_count; - char* cis_buffer = malloc(SM_SECTOR_SIZE); - int offset; - int cis_found = 0; - - if (!cis_buffer) - return; - - if (info->type != MTD_NANDFLASH) - goto exit; - - sector_size = info->writesize; - block_size = info->erasesize; - size_in_megs = info->size / (1024 * 1024); - - if (sector_size != SM_SECTOR_SIZE && sector_size != SM_SMALL_PAGE) - goto exit; - - switch(size_in_megs) { - case 1: - case 2: - spare_count = 6; - break; - case 4: - spare_count = 12; - break; - default: - spare_count = 24; - break; - } - - for (offset = 0 ; offset < block_size * spare_count ; - offset += sector_size) { - lseek(mtd_fd, SEEK_SET, offset); - if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE) { - cis_found = 1; - break; - } - } - - if (!cis_found) - goto exit; - - if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 && - (memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, - sizeof(cis_signature)) != 0)) - goto exit; - - printf("MTD_FTL=smartmedia\n"); - free(cis_buffer); - exit(0); -exit: - free(cis_buffer); - return; -} diff --git a/src/udev/net/.gitignore b/src/udev/net/.gitignore deleted file mode 100644 index 9ca85bacc9..0000000000 --- a/src/udev/net/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/link-config-gperf.c diff --git a/src/udev/net/Makefile b/src/udev/net/Makefile deleted file mode 120000 index 94aaae2c4d..0000000000 --- a/src/udev/net/Makefile +++ /dev/null @@ -1 +0,0 @@ -../../Makefile \ No newline at end of file diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c deleted file mode 100644 index c00ff79123..0000000000 --- a/src/udev/net/ethtool-util.c +++ /dev/null @@ -1,208 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include -#include -#include -#include - -#include "conf-parser.h" -#include "ethtool-util.h" -#include "log.h" -#include "string-table.h" -#include "strxcpyx.h" -#include "util.h" - -static const char* const duplex_table[_DUP_MAX] = { - [DUP_FULL] = "full", - [DUP_HALF] = "half" -}; - -DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); -DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); - -static const char* const wol_table[_WOL_MAX] = { - [WOL_PHY] = "phy", - [WOL_MAGIC] = "magic", - [WOL_OFF] = "off" -}; - -DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); -DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); - -int ethtool_connect(int *ret) { - int fd; - - assert_return(ret, -EINVAL); - - fd = socket(PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - return -errno; - - *ret = fd; - - return 0; -} - -int ethtool_get_driver(int *fd, const char *ifname, char **ret) { - struct ethtool_drvinfo ecmd = { - .cmd = ETHTOOL_GDRVINFO - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - char *d; - int r; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - d = strdup(ecmd.driver); - if (!d) - return -ENOMEM; - - *ret = d; - return 0; -} - -int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { - struct ethtool_cmd ecmd = { - .cmd = ETHTOOL_GSET - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - bool need_update = false; - int r; - - if (speed == 0 && duplex == _DUP_INVALID) - return 0; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - if (ethtool_cmd_speed(&ecmd) != speed) { - ethtool_cmd_speed_set(&ecmd, speed); - need_update = true; - } - - switch (duplex) { - case DUP_HALF: - if (ecmd.duplex != DUPLEX_HALF) { - ecmd.duplex = DUPLEX_HALF; - need_update = true; - } - break; - case DUP_FULL: - if (ecmd.duplex != DUPLEX_FULL) { - ecmd.duplex = DUPLEX_FULL; - need_update = true; - } - break; - default: - break; - } - - if (need_update) { - ecmd.cmd = ETHTOOL_SSET; - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - } - - return 0; -} - -int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { - struct ethtool_wolinfo ecmd = { - .cmd = ETHTOOL_GWOL - }; - struct ifreq ifr = { - .ifr_data = (void*) &ecmd - }; - bool need_update = false; - int r; - - if (wol == _WOL_INVALID) - return 0; - - if (*fd < 0) { - r = ethtool_connect(fd); - if (r < 0) - return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); - } - - strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - - switch (wol) { - case WOL_PHY: - if (ecmd.wolopts != WAKE_PHY) { - ecmd.wolopts = WAKE_PHY; - need_update = true; - } - break; - case WOL_MAGIC: - if (ecmd.wolopts != WAKE_MAGIC) { - ecmd.wolopts = WAKE_MAGIC; - need_update = true; - } - break; - case WOL_OFF: - if (ecmd.wolopts != 0) { - ecmd.wolopts = 0; - need_update = true; - } - break; - default: - break; - } - - if (need_update) { - ecmd.cmd = ETHTOOL_SWOL; - - r = ioctl(*fd, SIOCETHTOOL, &ifr); - if (r < 0) - return -errno; - } - - return 0; -} diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h deleted file mode 100644 index 7716516e76..0000000000 --- a/src/udev/net/ethtool-util.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include - -/* we can't use DUPLEX_ prefix, as it - * clashes with */ -typedef enum Duplex { - DUP_FULL, - DUP_HALF, - _DUP_MAX, - _DUP_INVALID = -1 -} Duplex; - -typedef enum WakeOnLan { - WOL_PHY, - WOL_MAGIC, - WOL_OFF, - _WOL_MAX, - _WOL_INVALID = -1 -} WakeOnLan; - -int ethtool_connect(int *ret); - -int ethtool_get_driver(int *fd, const char *ifname, char **ret); -int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); -int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); - -const char *duplex_to_string(Duplex d) _const_; -Duplex duplex_from_string(const char *d) _pure_; - -const char *wol_to_string(WakeOnLan wol) _const_; -WakeOnLan wol_from_string(const char *wol) _pure_; - -int config_parse_duplex(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_wol(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/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf deleted file mode 100644 index b25e4b3344..0000000000 --- a/src/udev/net/link-config-gperf.gperf +++ /dev/null @@ -1,37 +0,0 @@ -%{ -#include -#include "conf-parser.h" -#include "network-internal.h" -#include "link-config.h" -#include "ethtool-util.h" -%} -struct ConfigPerfItem; -%null_strings -%language=ANSI-C -%define slot-name section_and_lvalue -%define hash-function-name link_config_gperf_hash -%define lookup-function-name link_config_gperf_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) -Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) -Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) -Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) -Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) -Link.Description, config_parse_string, 0, offsetof(link_config, description) -Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) -Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) -Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) -Link.Name, config_parse_ifname, 0, offsetof(link_config, name) -Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) -Link.MTUBytes, config_parse_iec_size, 0, offsetof(link_config, mtu) -Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) -Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) -Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c deleted file mode 100644 index c66504102f..0000000000 --- a/src/udev/net/link-config.c +++ /dev/null @@ -1,519 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include - -#include "sd-netlink.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 "link-config.h" -#include "log.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); - - int ethtool_fd; - - bool enable_name_policy; - - sd_netlink *rtnl; - - usec_t link_dirs_ts_usec; -}; - -static const char* const link_dirs[] = { - "/etc/systemd/network", - "/run/systemd/network", - "/usr/lib/systemd/network", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/network", -#endif - NULL}; - -static void link_config_free(link_config *link) { - if (!link) - return; - - free(link->filename); - - free(link->match_mac); - strv_free(link->match_path); - strv_free(link->match_driver); - strv_free(link->match_type); - free(link->match_name); - free(link->match_host); - free(link->match_virt); - free(link->match_kernel); - free(link->match_arch); - - free(link->description); - free(link->mac); - free(link->name_policy); - free(link->name); - free(link->alias); - - free(link); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(link_config*, link_config_free); - -static void link_configs_free(link_config_ctx *ctx) { - link_config *link, *link_next; - - if (!ctx) - return; - - LIST_FOREACH_SAFE(links, link, link_next, ctx->links) - link_config_free(link); -} - -void link_config_ctx_free(link_config_ctx *ctx) { - if (!ctx) - return; - - safe_close(ctx->ethtool_fd); - - sd_netlink_unref(ctx->rtnl); - - link_configs_free(ctx); - - free(ctx); - - return; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); - -int link_config_ctx_new(link_config_ctx **ret) { - _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; - - if (!ret) - return -EINVAL; - - ctx = new0(link_config_ctx, 1); - if (!ctx) - return -ENOMEM; - - LIST_HEAD_INIT(ctx->links); - - ctx->ethtool_fd = -1; - - ctx->enable_name_policy = true; - - *ret = ctx; - ctx = NULL; - - return 0; -} - -static int load_link(link_config_ctx *ctx, const char *filename) { - _cleanup_(link_config_freep) link_config *link = NULL; - _cleanup_fclose_ FILE *file = NULL; - int r; - - assert(ctx); - assert(filename); - - file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - else - return -errno; - } - - if (null_or_empty_fd(fileno(file))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } - - link = new0(link_config, 1); - if (!link) - return log_oom(); - - link->mac_policy = _MACPOLICY_INVALID; - link->wol = _WOL_INVALID; - link->duplex = _DUP_INVALID; - - r = config_parse(NULL, filename, file, - "Match\0Link\0Ethernet\0", - config_item_perf_lookup, link_config_gperf_lookup, - false, false, true, link); - if (r < 0) - return r; - else - log_debug("Parsed configuration file %s", filename); - - if (link->mtu > UINT_MAX || link->speed > UINT_MAX) - return -ERANGE; - - link->filename = strdup(filename); - - LIST_PREPEND(links, ctx->links, link); - link = NULL; - - return 0; -} - -static bool enable_name_policy(void) { - _cleanup_free_ char *line = NULL; - const char *word, *state; - int r; - size_t l; - - r = proc_cmdline(&line); - if (r < 0) { - log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); - return true; - } - - FOREACH_WORD_QUOTED(word, l, line, state) - if (strneq(word, "net.ifnames=0", l)) - return false; - - return true; -} - -int link_config_load(link_config_ctx *ctx) { - int r; - _cleanup_strv_free_ char **files; - char **f; - - link_configs_free(ctx); - - if (!enable_name_policy()) { - ctx->enable_name_policy = false; - log_info("Network interface NamePolicy= disabled on kernel command line, ignoring."); - } - - /* update timestamp */ - paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); - - r = conf_files_list_strv(&files, ".link", NULL, link_dirs); - if (r < 0) - return log_error_errno(r, "failed to enumerate link files: %m"); - - STRV_FOREACH_BACKWARDS(f, files) { - r = load_link(ctx, *f); - if (r < 0) - return r; - } - - return 0; -} - -bool link_config_should_reload(link_config_ctx *ctx) { - return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false); -} - -int link_config_get(link_config_ctx *ctx, struct udev_device *device, - link_config **ret) { - link_config *link; - - assert(ctx); - assert(device); - assert(ret); - - LIST_FOREACH(links, link, ctx->links) { - const char* attr_value; - - attr_value = udev_device_get_sysattr_value(device, "address"); - - if (net_match_config(link->match_mac, link->match_path, link->match_driver, - link->match_type, link->match_name, link->match_host, - link->match_virt, link->match_kernel, link->match_arch, - attr_value ? ether_aton(attr_value) : NULL, - udev_device_get_property_value(device, "ID_PATH"), - udev_device_get_driver(udev_device_get_parent(device)), - udev_device_get_property_value(device, "ID_NET_DRIVER"), - udev_device_get_devtype(device), - udev_device_get_sysname(device))) { - if (link->match_name) { - unsigned char name_assign_type = NET_NAME_UNKNOWN; - - attr_value = udev_device_get_sysattr_value(device, "name_assign_type"); - if (attr_value) - (void) safe_atou8(attr_value, &name_assign_type); - - if (name_assign_type == NET_NAME_ENUM) { - log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'", - link->filename, udev_device_get_sysname(device)); - *ret = link; - - return 0; - } else if (name_assign_type == NET_NAME_RENAMED) { - log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", - link->filename, udev_device_get_sysname(device)); - - continue; - } - } - - log_debug("Config file %s applies to device %s", - link->filename, udev_device_get_sysname(device)); - - *ret = link; - - return 0; - } - } - - *ret = NULL; - - return -ENOENT; -} - -static bool mac_is_random(struct udev_device *device) { - const char *s; - unsigned type; - int r; - - /* if we can't get the assign type, assume it is not random */ - s = udev_device_get_sysattr_value(device, "addr_assign_type"); - if (!s) - return false; - - r = safe_atou(s, &type); - if (r < 0) - return false; - - return type == NET_ADDR_RANDOM; -} - -static bool should_rename(struct udev_device *device, bool respect_predictable) { - const char *s; - unsigned type; - int r; - - /* if we can't get the assgin type, assume we should rename */ - s = udev_device_get_sysattr_value(device, "name_assign_type"); - if (!s) - return true; - - r = safe_atou(s, &type); - if (r < 0) - return true; - - switch (type) { - case NET_NAME_USER: - case NET_NAME_RENAMED: - /* these were already named by userspace, do not touch again */ - return false; - case NET_NAME_PREDICTABLE: - /* the kernel claims to have given a predictable name */ - if (respect_predictable) - return false; - /* fall through */ - case NET_NAME_ENUM: - default: - /* the name is known to be bad, or of an unknown type */ - return true; - } -} - -static int get_mac(struct udev_device *device, bool want_random, - struct ether_addr *mac) { - int r; - - if (want_random) - random_bytes(mac->ether_addr_octet, ETH_ALEN); - else { - uint64_t 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); - } - - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - return 0; -} - -int link_config_apply(link_config_ctx *ctx, link_config *config, - struct udev_device *device, const char **name) { - const char *old_name; - const char *new_name = NULL; - struct ether_addr generated_mac; - struct ether_addr *mac = NULL; - bool respect_predictable = false; - int r, ifindex; - - assert(ctx); - assert(config); - assert(device); - assert(name); - - old_name = udev_device_get_sysname(device); - if (!old_name) - return -EINVAL; - - r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); - if (r < 0) - log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m", - old_name, config->speed / 1024, - duplex_to_string(config->duplex)); - - r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); - if (r < 0) - log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m", - old_name, wol_to_string(config->wol)); - - ifindex = udev_device_get_ifindex(device); - if (ifindex <= 0) { - log_warning("Could not find ifindex"); - return -ENODEV; - } - - if (ctx->enable_name_policy && config->name_policy) { - NamePolicy *policy; - - for (policy = config->name_policy; - !new_name && *policy != _NAMEPOLICY_INVALID; policy++) { - switch (*policy) { - case NAMEPOLICY_KERNEL: - respect_predictable = true; - break; - case NAMEPOLICY_DATABASE: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_FROM_DATABASE"); - break; - case NAMEPOLICY_ONBOARD: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"); - break; - case NAMEPOLICY_SLOT: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT"); - break; - case NAMEPOLICY_PATH: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_PATH"); - break; - case NAMEPOLICY_MAC: - new_name = udev_device_get_property_value(device, "ID_NET_NAME_MAC"); - break; - default: - break; - } - } - } - - if (should_rename(device, respect_predictable)) { - /* if not set by policy, fall back manually set name */ - if (!new_name) - new_name = config->name; - } else - new_name = NULL; - - switch (config->mac_policy) { - case MACPOLICY_PERSISTENT: - if (mac_is_random(device)) { - r = get_mac(device, false, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_RANDOM: - if (!mac_is_random(device)) { - r = get_mac(device, true, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_NONE: - default: - mac = config->mac; - } - - r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); - if (r < 0) - return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name); - - *name = new_name; - - return 0; -} - -int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret) { - const char *name; - char *driver = NULL; - int r; - - name = udev_device_get_sysname(device); - if (!name) - return -EINVAL; - - r = ethtool_get_driver(&ctx->ethtool_fd, name, &driver); - if (r < 0) - return r; - - *ret = driver; - return 0; -} - -static const char* const mac_policy_table[_MACPOLICY_MAX] = { - [MACPOLICY_PERSISTENT] = "persistent", - [MACPOLICY_RANDOM] = "random", - [MACPOLICY_NONE] = "none" -}; - -DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); -DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, - "Failed to parse MAC address policy"); - -static const char* const name_policy_table[_NAMEPOLICY_MAX] = { - [NAMEPOLICY_KERNEL] = "kernel", - [NAMEPOLICY_DATABASE] = "database", - [NAMEPOLICY_ONBOARD] = "onboard", - [NAMEPOLICY_SLOT] = "slot", - [NAMEPOLICY_PATH] = "path", - [NAMEPOLICY_MAC] = "mac" -}; - -DEFINE_STRING_TABLE_LOOKUP(name_policy, NamePolicy); -DEFINE_CONFIG_PARSE_ENUMV(config_parse_name_policy, name_policy, NamePolicy, - _NAMEPOLICY_INVALID, - "Failed to parse interface name policy"); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h deleted file mode 100644 index 9df5529d05..0000000000 --- a/src/udev/net/link-config.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2013 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 . -***/ - -#include "libudev.h" - -#include "condition.h" -#include "ethtool-util.h" -#include "list.h" - -typedef struct link_config_ctx link_config_ctx; -typedef struct link_config link_config; - -typedef enum MACPolicy { - MACPOLICY_PERSISTENT, - MACPOLICY_RANDOM, - MACPOLICY_NONE, - _MACPOLICY_MAX, - _MACPOLICY_INVALID = -1 -} MACPolicy; - -typedef enum NamePolicy { - NAMEPOLICY_KERNEL, - NAMEPOLICY_DATABASE, - NAMEPOLICY_ONBOARD, - NAMEPOLICY_SLOT, - NAMEPOLICY_PATH, - NAMEPOLICY_MAC, - _NAMEPOLICY_MAX, - _NAMEPOLICY_INVALID = -1 -} NamePolicy; - -struct link_config { - char *filename; - - struct ether_addr *match_mac; - char **match_path; - char **match_driver; - char **match_type; - char **match_name; - Condition *match_host; - Condition *match_virt; - Condition *match_kernel; - Condition *match_arch; - - char *description; - struct ether_addr *mac; - MACPolicy mac_policy; - NamePolicy *name_policy; - char *name; - char *alias; - size_t mtu; - size_t speed; - Duplex duplex; - WakeOnLan wol; - - LIST_FIELDS(link_config, links); -}; - -int link_config_ctx_new(link_config_ctx **ret); -void link_config_ctx_free(link_config_ctx *ctx); - -int link_config_load(link_config_ctx *ctx); -bool link_config_should_reload(link_config_ctx *ctx); - -int link_config_get(link_config_ctx *ctx, struct udev_device *device, struct link_config **ret); -int link_config_apply(link_config_ctx *ctx, struct link_config *config, struct udev_device *device, const char **name); - -int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret); - -const char *name_policy_to_string(NamePolicy p) _const_; -NamePolicy name_policy_from_string(const char *p) _pure_; - -const char *mac_policy_to_string(MACPolicy p) _const_; -MACPolicy mac_policy_from_string(const char *p) _pure_; - -/* gperf lookup function */ -const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, unsigned length); - -int config_parse_mac_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_name_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/udev/scsi_id/.gitignore b/src/udev/scsi_id/.gitignore deleted file mode 100644 index 6aebddd809..0000000000 --- a/src/udev/scsi_id/.gitignore +++ /dev/null @@ -1 +0,0 @@ -scsi_id_version.h diff --git a/src/udev/scsi_id/Makefile b/src/udev/scsi_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/scsi_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/scsi_id/README b/src/udev/scsi_id/README deleted file mode 100644 index 9cfe73991c..0000000000 --- a/src/udev/scsi_id/README +++ /dev/null @@ -1,4 +0,0 @@ -scsi_id - generate a SCSI unique identifier for a given SCSI device - -Please send questions, comments or patches to or -. diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h deleted file mode 100644 index a27a84a40a..0000000000 --- a/src/udev/scsi_id/scsi.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -/* - * scsi.h - * - * General scsi and linux scsi specific defines and structs. - * - * Copyright (C) IBM Corp. 2003 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2 of the License. - */ - -#include - -struct scsi_ioctl_command { - unsigned int inlen; /* excluding scsi command length */ - unsigned int outlen; - unsigned char data[1]; - /* on input, scsi command starts here then opt. data */ -}; - -/* - * Default 5 second timeout - */ -#define DEF_TIMEOUT 5000 - -#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 - -/* - * SCSI INQUIRY vendor and model (really product) lengths. - */ -#define VENDOR_LENGTH 8 -#define MODEL_LENGTH 16 - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 - -/* - * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the - * SCSI Primary Commands specification for details. - */ - -/* - * 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_TGTGROUP 5 -#define SCSI_ID_LUNGROUP 6 -#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 0x05 -#define SCSI_ID_NAA_IEEE_REG_EXTENDED 0x06 - -/* - * Supported Code Set values. - */ -#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; -}; - -/* - * Following are the "true" SCSI status codes. Linux has traditionally - * used a 1 bit right and masked version of these. So now CHECK_CONDITION - * and friends (in ) are deprecated. - */ -#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 diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c deleted file mode 100644 index 4655691642..0000000000 --- a/src/udev/scsi_id/scsi_id.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * Copyright (C) SUSE Linux Products GmbH, 2006 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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[] = { - { "device", required_argument, NULL, 'd' }, - { "config", required_argument, NULL, 'f' }, - { "page", required_argument, NULL, 'p' }, - { "blacklisted", no_argument, NULL, 'b' }, - { "whitelisted", no_argument, NULL, 'g' }, - { "replace-whitespace", no_argument, NULL, 'u' }, - { "sg-version", required_argument, NULL, 's' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, /* don't advertise -V */ - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} -}; - -static bool all_good = false; -static bool dev_specified = false; -static char config_file[MAX_PATH_LEN] = "/etc/scsi_id.config"; -static enum page_code default_page_code = PAGE_UNSPECIFIED; -static int sg_version = 4; -static bool reformat_serial = false; -static bool export = false; -static char vendor_str[64]; -static char model_str[64]; -static char vendor_enc_str[256]; -static char model_enc_str[256]; -static char revision_str[16]; -static char type_str[16]; - -static void set_type(const char *from, char *to, size_t len) -{ - int type_num; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - type = "optical"; - break; - case 5: - type = "cd"; - break; - case 7: - type = "optical"; - break; - case 0xe: - type = "disk"; - break; - case 0xf: - type = "optical"; - break; - default: - break; - } - } - strscpy(to, len, type); -} - -/* - * get_value: - * - * buf points to an '=' followed by a quoted string ("foo") or a string ending - * with a space or ','. - * - * Return a pointer to the NUL terminated string, returns NULL if no - * matches. - */ -static char *get_value(char **buffer) -{ - static const char *quote_string = "\"\n"; - static const char *comma_string = ",\n"; - char *val; - const char *end; - - if (**buffer == '"') { - /* - * skip leading quote, terminate when quote seen - */ - (*buffer)++; - end = quote_string; - } else { - end = comma_string; - } - val = strsep(buffer, end); - if (val && end == quote_string) - /* - * skip trailing quote - */ - (*buffer)++; - - while (isspace(**buffer)) - (*buffer)++; - - return val; -} - -static int argc_count(char *opts) -{ - int i = 0; - while (*opts != '\0') - if (*opts++ == ' ') - i++; - return i; -} - -/* - * get_file_options: - * - * If vendor == NULL, find a line in the config file with only "OPTIONS="; - * if vendor and model are set find the first OPTIONS line in the config - * file that matches. Set argc and argv to match the OPTIONS string. - * - * vendor and model can end in '\n'. - */ -static int get_file_options(struct udev *udev, - const char *vendor, const char *model, - int *argc, char ***newargv) -{ - char *buffer; - _cleanup_fclose_ FILE *f; - char *buf; - char *str1; - char *vendor_in, *model_in, *options_in; /* read in from file */ - int lineno; - int c; - int retval = 0; - - f = fopen(config_file, "re"); - if (f == NULL) { - if (errno == ENOENT) - return 1; - else { - log_error_errno(errno, "can't open %s: %m", config_file); - return -1; - } - } - - /* - * Allocate a buffer rather than put it on the stack so we can - * keep it around to parse any options (any allocated newargv - * points into this buffer for its strings). - */ - buffer = malloc(MAX_BUFFER_LEN); - if (!buffer) - return log_oom(); - - *newargv = NULL; - lineno = 0; - for (;;) { - vendor_in = model_in = options_in = NULL; - - buf = fgets(buffer, MAX_BUFFER_LEN, f); - if (buf == NULL) - break; - lineno++; - if (buf[strlen(buffer) - 1] != '\n') { - log_error("Config file line %d too long", lineno); - break; - } - - while (isspace(*buf)) - buf++; - - /* blank or all whitespace line */ - if (*buf == '\0') - continue; - - /* comment line */ - if (*buf == '#') - continue; - - str1 = strsep(&buf, "="); - if (str1 && strcaseeq(str1, "VENDOR")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - vendor_in = str1; - - str1 = strsep(&buf, "="); - if (str1 && strcaseeq(str1, "MODEL")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - model_in = str1; - str1 = strsep(&buf, "="); - } - } - - if (str1 && strcaseeq(str1, "OPTIONS")) { - str1 = get_value(&buf); - if (!str1) { - retval = log_oom(); - break; - } - options_in = str1; - } - - /* - * Only allow: [vendor=foo[,model=bar]]options=stuff - */ - if (!options_in || (!vendor_in && model_in)) { - log_error("Error parsing config file line %d '%s'", lineno, buffer); - retval = -1; - break; - } - if (vendor == NULL) { - if (vendor_in == NULL) - break; - } else if (vendor_in && - strneq(vendor, vendor_in, strlen(vendor_in)) && - (!model_in || - (strneq(model, model_in, strlen(model_in))))) { - /* - * Matched vendor and optionally model. - * - * Note: a short vendor_in or model_in can - * give a partial match (that is FOO - * matches FOOBAR). - */ - break; - } - } - - if (retval == 0) { - if (vendor_in != NULL || model_in != NULL || - options_in != NULL) { - /* - * Something matched. Allocate newargv, and store - * values found in options_in. - */ - strcpy(buffer, options_in); - c = argc_count(buffer) + 2; - *newargv = calloc(c, sizeof(**newargv)); - if (!*newargv) - retval = log_oom(); - else { - *argc = c; - c = 0; - /* - * argv[0] at 0 is skipped by getopt, but - * store the buffer address there for - * later freeing - */ - (*newargv)[c] = buffer; - for (c = 1; c < *argc; c++) - (*newargv)[c] = strsep(&buffer, " \t"); - } - } else { - /* No matches */ - retval = 1; - } - } - if (retval != 0) - free(buffer); - return retval; -} - -static void help(void) { - printf("Usage: %s [OPTION...] DEVICE\n\n" - "SCSI device identification.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n\n" - " -d --device= Device node for SG_IO commands\n" - " -f --config= Location of config file\n" - " -p --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" - " -s --sg-version=3|4 Use SGv3 or SGv4\n" - " -b --blacklisted Treat device as blacklisted\n" - " -g --whitelisted Treat device as whitelisted\n" - " -u --replace-whitespace Replace all whitespace by underscores\n" - " -v --verbose Verbose logging\n" - " -x --export Print values as environment keys\n" - , program_invocation_short_name); - -} - -static int set_options(struct udev *udev, - int argc, char **argv, - char *maj_min_dev) -{ - int option; - - /* - * optind is a global extern used by getopt. Since we can call - * set_options twice (once for command line, and once for config - * file) we have to reset this back to 1. - */ - optind = 1; - while ((option = getopt_long(argc, argv, "d:f:gp:uvVxh", options, NULL)) >= 0) - switch (option) { - case 'b': - all_good = false; - break; - - case 'd': - dev_specified = true; - strscpy(maj_min_dev, MAX_PATH_LEN, optarg); - break; - - case 'f': - strscpy(config_file, MAX_PATH_LEN, optarg); - break; - - case 'g': - all_good = true; - break; - - case 'h': - help(); - exit(0); - - case 'p': - if (streq(optarg, "0x80")) - default_page_code = PAGE_80; - else if (streq(optarg, "0x83")) - default_page_code = PAGE_83; - else if (streq(optarg, "pre-spc3-83")) - default_page_code = PAGE_83_PRE_SPC3; - else { - log_error("Unknown page code '%s'", optarg); - return -1; - } - break; - - case 's': - sg_version = atoi(optarg); - if (sg_version < 3 || sg_version > 4) { - log_error("Unknown SG version '%s'", optarg); - return -1; - } - break; - - case 'u': - reformat_serial = true; - break; - - case 'v': - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - log_open(); - break; - - case 'V': - printf("%s\n", VERSION); - exit(0); - - case 'x': - export = true; - break; - - case '?': - return -1; - - default: - assert_not_reached("Unknown option"); - } - - if (optind < argc && !dev_specified) { - dev_specified = true; - strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]); - } - - return 0; -} - -static int per_dev_options(struct udev *udev, - struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) -{ - int retval; - int newargc; - char **newargv = NULL; - int option; - - *good_bad = all_good; - *page_code = default_page_code; - - retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); - - optind = 1; /* reset this global extern */ - while (retval == 0) { - option = getopt_long(newargc, newargv, "bgp:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'b': - *good_bad = 0; - break; - - case 'g': - *good_bad = 1; - break; - - case 'p': - if (streq(optarg, "0x80")) { - *page_code = PAGE_80; - } else if (streq(optarg, "0x83")) { - *page_code = PAGE_83; - } else if (streq(optarg, "pre-spc3-83")) { - *page_code = PAGE_83_PRE_SPC3; - } else { - log_error("Unknown page code '%s'", optarg); - retval = -1; - } - break; - - default: - log_error("Unknown or bad option '%c' (0x%x)", option, option); - retval = -1; - break; - } - } - - if (newargv) { - free(newargv[0]); - free(newargv); - } - return retval; -} - -static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) -{ - int retval; - - dev_scsi->use_sg = sg_version; - - retval = scsi_std_inquiry(udev, dev_scsi, path); - if (retval) - return retval; - - udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); - udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); - - util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); - util_replace_chars(vendor_str, NULL); - util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); - util_replace_chars(model_str, NULL); - set_type(dev_scsi->type, type_str, sizeof(type_str)); - util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); - util_replace_chars(revision_str, NULL); - return 0; -} - -/* - * scsi_id: try to get an id, if one is found, printf it to stdout. - * returns a value passed to exit() - 0 if printed an id, else 1. - */ -static int scsi_id(struct udev *udev, char *maj_min_dev) -{ - struct scsi_id_device dev_scsi = {}; - int good_dev; - int page_code; - int retval = 0; - - if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { - retval = 1; - goto out; - } - - /* get per device (vendor + model) options from the config file */ - per_dev_options(udev, &dev_scsi, &good_dev, &page_code); - if (!good_dev) { - retval = 1; - goto out; - } - - /* read serial number from mode pages (no values for optical drives) */ - scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); - - if (export) { - char serial_str[MAX_SERIAL_LEN]; - - printf("ID_SCSI=1\n"); - printf("ID_VENDOR=%s\n", vendor_str); - printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); - printf("ID_MODEL=%s\n", model_str); - printf("ID_MODEL_ENC=%s\n", model_enc_str); - printf("ID_REVISION=%s\n", revision_str); - printf("ID_TYPE=%s\n", type_str); - if (dev_scsi.serial[0] != '\0') { - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL=%s\n", serial_str); - util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL_SHORT=%s\n", serial_str); - } - if (dev_scsi.wwn[0] != '\0') { - printf("ID_WWN=0x%s\n", dev_scsi.wwn); - if (dev_scsi.wwn_vendor_extension[0] != '\0') { - printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); - printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); - } else - printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); - } - if (dev_scsi.tgpt_group[0] != '\0') - printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); - if (dev_scsi.unit_serial_number[0] != '\0') - printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); - goto out; - } - - if (dev_scsi.serial[0] == '\0') { - retval = 1; - goto out; - } - - if (reformat_serial) { - char serial_str[MAX_SERIAL_LEN]; - - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("%s\n", serial_str); - goto out; - } - - printf("%s\n", dev_scsi.serial); -out: - return retval; -} - -int main(int argc, char **argv) -{ - _cleanup_udev_unref_ struct udev *udev; - int retval = 0; - char maj_min_dev[MAX_PATH_LEN]; - int newargc; - char **newargv = NULL; - - log_parse_environment(); - log_open(); - - udev = udev_new(); - if (udev == NULL) - goto exit; - - /* - * Get config file options. - */ - retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); - if (retval < 0) { - retval = 1; - goto exit; - } - if (retval == 0) { - assert(newargv); - - if (set_options(udev, newargc, newargv, maj_min_dev) < 0) { - retval = 2; - goto exit; - } - } - - /* - * Get command line options (overriding any config file settings). - */ - if (set_options(udev, argc, argv, maj_min_dev) < 0) - exit(1); - - if (!dev_specified) { - log_error("No device specified."); - retval = 1; - goto exit; - } - - retval = scsi_id(udev, maj_min_dev); - -exit: - if (newargv) { - free(newargv[0]); - free(newargv); - } - log_close(); - return retval; -} diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h deleted file mode 100644 index 5c2e1c28ee..0000000000 --- a/src/udev/scsi_id/scsi_id.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -/* - * Copyright (C) IBM Corp. 2003 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define MAX_PATH_LEN 512 - -/* - * MAX_ATTR_LEN: maximum length of the result of reading a sysfs - * attribute. - */ -#define MAX_ATTR_LEN 256 - -/* - * MAX_SERIAL_LEN: the maximum length of the serial number, including - * added prefixes such as vendor and product (model) strings. - */ -#define MAX_SERIAL_LEN 256 - -/* - * MAX_BUFFER_LEN: maximum buffer size and line length used while reading - * the config file. - */ -#define MAX_BUFFER_LEN 256 - -struct scsi_id_device { - char vendor[9]; - char model[17]; - char revision[5]; - char type[33]; - char kernel[64]; - char serial[MAX_SERIAL_LEN]; - char serial_short[MAX_SERIAL_LEN]; - int use_sg; - - /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */ - char unit_serial_number[MAX_SERIAL_LEN]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */ - char wwn[17]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '0xe00000d80000' */ - char wwn_vendor_extension[17]; - - /* NULs if not set - otherwise decimal number */ - char tgpt_group[8]; -}; - -int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname); -int scsi_get_serial(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len); - -/* - * Page code values. - */ -enum page_code { - PAGE_83_PRE_SPC3 = -0x83, - PAGE_UNSPECIFIED = 0x00, - PAGE_80 = 0x80, - PAGE_83 = 0x83, -}; diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c deleted file mode 100644 index e079e28698..0000000000 --- a/src/udev/scsi_id/scsi_serial.c +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * - * Author: Patrick Mansfield - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#include "libudev-private.h" -#include "random-util.h" -#include "scsi.h" -#include "scsi_id.h" -#include "string-util.h" - -/* - * A priority based list of id, naa, and binary/ascii for the identifier - * descriptor in VPD page 0x83. - * - * Brute force search for a match starting with the first value in the - * following id_search_list. This is not a performance issue, since there - * 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 }, - /* - * Devices already exist using NAA values that are now marked - * reserved. These should not conflict with other values, or it is - * a bug in the device. As long as we find the IEEE extended one - * first, we really don't care what other ones are used. Using - * don't care here means that a device that returns multiple - * 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 }, -}; - -static const char hex_str[]="0123456789abcdef"; - -/* - * Values returned in the result/status, only the ones used by the code - * 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 */ - -/* 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 */ - -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len); - -static int sg_err_category_new(struct udev *udev, - int scsi_status, int msg_status, int - host_status, int driver_status, const - unsigned char *sense_buffer, int sb_len) -{ - scsi_status &= 0x7e; - - /* - * XXX change to return only two values - failed or OK. - */ - - if (!scsi_status && !host_status && !driver_status) - return SG_ERR_CAT_CLEAN; - - if ((scsi_status == SCSI_CHECK_CONDITION) || - (scsi_status == SCSI_COMMAND_TERMINATED) || - ((driver_status & 0xf) == DRIVER_SENSE)) { - if (sense_buffer && (sb_len > 2)) { - int sense_key; - unsigned char asc; - - if (sense_buffer[0] & 0x2) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - } else { - sense_key = sense_buffer[2] & 0xf; - asc = (sb_len > 12) ? sense_buffer[12] : 0; - } - - if (sense_key == RECOVERED_ERROR) - return SG_ERR_CAT_RECOVERED; - else if (sense_key == UNIT_ATTENTION) { - if (0x28 == asc) - return SG_ERR_CAT_MEDIA_CHANGED; - if (0x29 == asc) - return SG_ERR_CAT_RESET; - } else if (sense_key == ILLEGAL_REQUEST) - return SG_ERR_CAT_NOTSUPPORTED; - } - return SG_ERR_CAT_SENSE; - } - if (host_status) { - if ((host_status == DID_NO_CONNECT) || - (host_status == DID_BUS_BUSY) || - (host_status == DID_TIME_OUT)) - return SG_ERR_CAT_TIMEOUT; - } - if (driver_status) { - if (driver_status == DRIVER_TIMEOUT) - return SG_ERR_CAT_TIMEOUT; - } - return SG_ERR_CAT_OTHER; -} - -static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp) -{ - return sg_err_category_new(udev, - hp->status, hp->msg_status, - hp->host_status, hp->driver_status, - hp->sbp, hp->sb_len_wr); -} - -static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp) -{ - return sg_err_category_new(udev, hp->device_status, 0, - hp->transport_status, hp->driver_status, - (unsigned char *)(uintptr_t)hp->response, - hp->response_len); -} - -static int scsi_dump_sense(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *sense_buffer, int sb_len) -{ - int s; - int code; - int sense_class; - int sense_key; - int asc, ascq; -#ifdef DUMP_SENSE - char out_buffer[256]; - int i, j; -#endif - - /* - * Figure out and print the sense key, asc and ascq. - * - * If you want to suppress these for a particular drive model, add - * a black list entry in the scsi_id config file. - * - * XXX We probably need to: lookup the sense/asc/ascq in a retry - * table, and if found return 1 (after dumping the sense, asc, and - * ascq). So, if/when we get something like a power on/reset, - * we'll retry the command. - */ - - if (sb_len < 1) { - log_debug("%s: sense buffer empty", dev_scsi->kernel); - return -1; - } - - sense_class = (sense_buffer[0] >> 4) & 0x07; - code = sense_buffer[0] & 0xf; - - if (sense_class == 7) { - /* - * extended sense data. - */ - 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); - return -1; - } - if ((code == 0x0) || (code == 0x1)) { - sense_key = sense_buffer[2] & 0xf; - if (s < 14) { - /* - * Possible? - */ - log_debug("%s: sense result too" " small %d bytes", - dev_scsi->kernel, s); - return -1; - } - asc = sense_buffer[12]; - ascq = sense_buffer[13]; - } else if ((code == 0x2) || (code == 0x3)) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - ascq = sense_buffer[3]; - } else { - log_debug("%s: invalid sense code 0x%x", - 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); - } 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); - return -1; - } - - if (sense_buffer[0] < 15) - 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]); - log_debug("%s: non-extended sense class %d code 0x%0x", - dev_scsi->kernel, sense_class, code); - - } - -#ifdef DUMP_SENSE - for (i = 0, j = 0; (i < s) && (j < 254); i++) { - out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4]; - out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f]; - out_buffer[j++] = ' '; - } - out_buffer[j] = '\0'; - log_debug("%s: sense dump:", dev_scsi->kernel); - log_debug("%s: %s", dev_scsi->kernel, out_buffer); - -#endif - return -1; -} - -static int scsi_dump(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) -{ - if (!io->status && !io->host_status && !io->msg_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } - - 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); - if (io->status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); - else - return -1; -} - -static int scsi_dump_v4(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) -{ - if (!io->device_status && !io->transport_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - log_debug("%s: called with no error", __FUNCTION__); - return -1; - } - - 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); - if (io->device_status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, - io->response_len); - else - return -1; -} - -static int scsi_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char evpd, unsigned char page, - unsigned char *buf, unsigned int buflen) -{ - unsigned char inq_cmd[INQUIRY_CMDLEN] = - { INQUIRY_CMD, evpd, page, 0, buflen, 0 }; - unsigned char sense[SENSE_BUFF_LEN]; - void *io_buf; - struct sg_io_v4 io_v4; - struct sg_io_hdr io_hdr; - int retry = 3; /* rather random */ - int retval; - - if (buflen > SCSI_INQ_BUFF_LEN) { - log_debug("buflen %d too long", buflen); - return -1; - } - -resend: - if (dev_scsi->use_sg == 4) { - memzero(&io_v4, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof(inq_cmd); - io_v4.request = (uintptr_t)inq_cmd; - io_v4.max_response_len = sizeof(sense); - io_v4.response = (uintptr_t)sense; - io_v4.din_xfer_len = buflen; - io_v4.din_xferp = (uintptr_t)buf; - io_buf = (void *)&io_v4; - } else { - memzero(&io_hdr, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inq_cmd); - io_hdr.mx_sb_len = sizeof(sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = buflen; - io_hdr.dxferp = buf; - io_hdr.cmdp = inq_cmd; - io_hdr.sbp = sense; - io_hdr.timeout = DEF_TIMEOUT; - io_buf = (void *)&io_hdr; - } - - retval = ioctl(fd, SG_IO, io_buf); - if (retval < 0) { - if ((errno == EINVAL || errno == ENOSYS) && dev_scsi->use_sg == 4) { - dev_scsi->use_sg = 3; - goto resend; - } - log_debug_errno(errno, "%s: ioctl failed: %m", dev_scsi->kernel); - goto error; - } - - if (dev_scsi->use_sg == 4) - retval = sg_err_category4(udev, io_buf); - else - retval = sg_err_category3(udev, io_buf); - - switch (retval) { - case SG_ERR_CAT_NOTSUPPORTED: - buf[1] = 0; - /* Fallthrough */ - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - retval = 0; - break; - - default: - if (dev_scsi->use_sg == 4) - retval = scsi_dump_v4(udev, dev_scsi, io_buf); - else - retval = scsi_dump(udev, dev_scsi, io_buf); - } - - if (!retval) { - retval = buflen; - } else if (retval > 0) { - if (--retry > 0) - goto resend; - retval = -1; - } - -error: - if (retval < 0) - log_debug("%s: Unable to get INQUIRY vpd %d page 0x%x.", - dev_scsi->kernel, evpd, page); - - return retval; -} - -/* Get list of supported EVPD pages */ -static int do_scsi_page0_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char *buffer, unsigned int len) -{ - int retval; - - memzero(buffer, len); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len); - if (retval < 0) - return 1; - - if (buffer[1] != 0) { - log_debug("%s: page 0 not available.", dev_scsi->kernel); - return 1; - } - if (buffer[3] > len) { - log_debug("%s: page 0 buffer too long %d", dev_scsi->kernel, buffer[3]); - return 1; - } - - /* - * Following check is based on code once included in the 2.5.x - * kernel. - * - * Some ill behaved devices return the standard inquiry here - * rather than the evpd data, snoop the data to verify. - */ - if (buffer[3] > MODEL_LENGTH) { - /* - * If the vendor id appears in the page assume the page is - * invalid. - */ - if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { - log_debug("%s: invalid page0 data", dev_scsi->kernel); - return 1; - } - } - return 0; -} - -/* - * The caller checks that serial is long enough to include the vendor + - * model. - */ -static int prepend_vendor_model(struct udev *udev, - struct scsi_id_device *dev_scsi, char *serial) -{ - int ind; - - strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); - strncat(serial, dev_scsi->model, MODEL_LENGTH); - ind = strlen(serial); - - /* - * This is not a complete check, since we are using strncat/cpy - * above, ind will never be too large. - */ - if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { - log_debug("%s: expected length %d, got length %d", - dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); - return -1; - } - return ind; -} - -/* - * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill - * serial number. - */ -static int check_fill_0x83_id(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, - int max_len, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int i, j, s, len; - - /* - * ASSOCIATION must be with the device (value 0) - * or with the target port for SCSI_ID_TGTPORT - */ - if ((page_83[1] & 0x30) == 0x10) { - if (id_search->id_type != SCSI_ID_TGTGROUP) - return 1; - } else if ((page_83[1] & 0x30) != 0) - return 1; - - if ((page_83[1] & 0x0f) != id_search->id_type) - return 1; - - /* - * Possibly check NAA sub-type. - */ - if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && - (id_search->naa_type != (page_83[4] & 0xf0) >> 4)) - return 1; - - /* - * Check for matching code set - ASCII or BINARY. - */ - if ((page_83[0] & 0x0f) != id_search->code_set) - return 1; - - /* - * page_83[3]: identifier length - */ - len = page_83[3]; - if ((page_83[0] & 0x0f) != SCSI_ID_ASCII) - /* - * If not ASCII, use two bytes for each binary value. - */ - len *= 2; - - /* - * Add one byte for the NUL termination, and one for the id_type. - */ - len += 2; - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - len += VENDOR_LENGTH + MODEL_LENGTH; - - if (max_len < len) { - log_debug("%s: length %d too short - need %d", - dev_scsi->kernel, max_len, len); - return 1; - } - - if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) { - unsigned int group; - - group = ((unsigned int)page_83[6] << 8) | page_83[7]; - sprintf(tgpt_group,"%x", group); - return 1; - } - - serial[0] = hex_str[id_search->id_type]; - - /* - * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before - * the id since it is not unique across all vendors and models, - * this differs from SCSI_ID_T10_VENDOR, where the vendor is - * included in the identifier. - */ - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0) - return 1; - - i = 4; /* offset to the start of the identifier */ - s = j = strlen(serial); - if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) { - /* - * ASCII descriptor. - */ - while (i < (4 + page_83[3])) - serial[j++] = page_83[i++]; - } else { - /* - * Binary descriptor, convert to ASCII, using two bytes of - * ASCII for each byte in the page_83. - */ - while (i < (4 + page_83[3])) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - } - - strcpy(serial_short, &serial[s]); - - if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { - strncpy(wwn, &serial[s], 16); - if (wwn_vendor_extension != NULL) - strncpy(wwn_vendor_extension, &serial[s + 16], 16); - } - - return 0; -} - -/* Extract the raw binary from VPD 0x83 pre-SPC devices */ -static int check_fill_0x83_prespc3(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, int max_len) -{ - int i, j; - - serial[0] = hex_str[id_search->id_type]; - /* serial has been memset to zero before */ - j = strlen(serial); /* j = 1; */ - - for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { - serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4]; - serial[j++] = hex_str[ page_83[4+i] & 0x0f]; - } - serial[max_len-1] = 0; - strncpy(serial_short, serial, max_len-1); - return 0; -} - - -/* Get device identification VPD page */ -static int do_scsi_page83_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len, - char *unit_serial_number, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int retval; - unsigned int id_ind, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - /* also pick up the page 80 serial number */ - do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); - - memzero(page_83, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, - SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - log_debug("%s: Invalid page 0x83", dev_scsi->kernel); - return 1; - } - - /* - * XXX Some devices (IBM 3542) return all spaces for an identifier if - * the LUN is not actually configured. This leads to identifiers of - * the form: "1 ". - */ - - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - - if (page_83[6] != 0) - return check_fill_0x83_prespc3(udev, - dev_scsi, page_83, id_search_list, - serial, serial_short, len); - - /* - * Search for a match in the prioritized id_search_list - since WWN ids - * come first we can pick up the WWN in check_fill_0x83_id(). - */ - for (id_ind = 0; - id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]); - id_ind++) { - /* - * Examine each descriptor returned. There is normally only - * one or a small number of descriptors. - */ - for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { - retval = check_fill_0x83_id(udev, - dev_scsi, &page_83[j], - &id_search_list[id_ind], - serial, serial_short, len, - wwn, wwn_vendor_extension, - tgpt_group); - if (!retval) - return retval; - else if (retval < 0) - return retval; - } - } - return 1; -} - -/* - * Get device identification VPD page for older SCSI-2 device which is not - * compliant with either SPC-2 or SPC-3 format. - * - * Return the hard coded error code value 2 if the page 83 reply is not - * conformant to the SCSI-2 format. - */ -static int do_scsi_page83_prespc3_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len) -{ - int retval; - int i, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - memzero(page_83, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - log_debug("%s: Invalid page 0x83", dev_scsi->kernel); - return 1; - } - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - if (page_83[6] == 0) - return 2; - - serial[0] = hex_str[id_search_list[0].id_type]; - /* - * The first four bytes contain data, not a descriptor. - */ - i = 4; - j = strlen(serial); - /* - * Binary descriptor, convert to ASCII, - * using two bytes of ASCII for each byte - * in the page_83. - */ - while (i < (page_83[3]+4)) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - return 0; -} - -/* Get unit serial number VPD page */ -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len) -{ - int retval; - int ser_ind; - int i; - int len; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - - memzero(buf, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return retval; - - if (buf[1] != PAGE_80) { - log_debug("%s: Invalid page 0x80", dev_scsi->kernel); - return 1; - } - - 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); - return 1; - } - /* - * Prepend 'S' to avoid unlikely collision with page 0x83 vendor - * specific type where we prepend '0' + vendor + model. - */ - len = buf[3]; - if (serial != NULL) { - serial[0] = 'S'; - ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]); - if (ser_ind < 0) - return 1; - ser_ind++; /* for the leading 'S' */ - for (i = 4; i < len + 4; i++, ser_ind++) - serial[ser_ind] = buf[i]; - } - if (serial_short != NULL) { - memcpy(serial_short, &buf[4], len); - serial_short[len] = '\0'; - } - return 0; -} - -int scsi_std_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname) -{ - int fd; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - struct stat statbuf; - int err = 0; - - fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); - if (fd < 0) { - log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname); - return 1; - } - - if (fstat(fd, &statbuf) < 0) { - log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname); - err = 2; - goto out; - } - sprintf(dev_scsi->kernel,"%d:%d", major(statbuf.st_rdev), - minor(statbuf.st_rdev)); - - memzero(buf, SCSI_INQ_BUFF_LEN); - err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN); - if (err < 0) - goto out; - - err = 0; - memcpy(dev_scsi->vendor, buf + 8, 8); - dev_scsi->vendor[8] = '\0'; - memcpy(dev_scsi->model, buf + 16, 16); - dev_scsi->model[16] = '\0'; - memcpy(dev_scsi->revision, buf + 32, 4); - dev_scsi->revision[4] = '\0'; - sprintf(dev_scsi->type,"%x", buf[0] & 0x1f); - -out: - close(fd); - return err; -} - -int scsi_get_serial(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len) -{ - unsigned char page0[SCSI_INQ_BUFF_LEN]; - int fd = -1; - int cnt; - int ind; - int retval; - - memzero(dev_scsi->serial, len); - initialize_srand(); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) - return 1; - - if (page_code == PAGE_80) { - if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83_PRE_SPC3) { - retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len); - if (retval) { - /* - * Fallback to servicing a SPC-2/3 compliant page 83 - * inquiry if the page 83 reply format does not - * conform to pre-SPC3 expectations. - */ - if (retval == 2) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } - else { - retval = 1; - goto completed; - } - } else { - retval = 0; - goto completed; - } - } else if (page_code != 0x00) { - log_debug("%s: unsupported page code 0x%d", dev_scsi->kernel, page_code); - retval = 1; - goto completed; - } - - /* - * Get page 0, the page of the pages. By default, try from best to - * worst of supported pages: 0x83 then 0x80. - */ - if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) { - /* - * Don't try anything else. Black list if a specific page - * should be used for this vendor+model, or maybe have an - * optional fall-back to page 0x80 or page 0x83. - */ - retval = 1; - goto completed; - } - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_83) - if (!do_scsi_page83_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - /* - * Success - */ - retval = 0; - goto completed; - } - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_80) - if (!do_scsi_page80_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len)) { - /* - * Success - */ - retval = 0; - goto completed; - } - retval = 1; - -completed: - close(fd); - return retval; -} diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c deleted file mode 100644 index 3c58445836..0000000000 --- a/src/udev/udev-builtin-blkid.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * probe disks for filesystems and partitions - * - * Copyright (C) 2011 Kay Sievers - * Copyright (C) 2011 Karel Zak - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-id128.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) { - char s[256]; - - s[0] = '\0'; - - if (streq(name, "TYPE")) { - udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); - - } else if (streq(name, "USAGE")) { - udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); - - } else if (streq(name, "VERSION")) { - udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); - - } else if (streq(name, "UUID")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); - - } else if (streq(name, "UUID_SUB")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); - - } else if (streq(name, "LABEL")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); - - } else if (streq(name, "PTTYPE")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); - - } else if (streq(name, "PTUUID")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value); - - } else if (streq(name, "PART_ENTRY_NAME")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); - - } else if (streq(name, "PART_ENTRY_TYPE")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); - - } else if (startswith(name, "PART_ENTRY_")) { - strscpyl(s, sizeof(s), "ID_", name, NULL); - udev_builtin_add_property(dev, test, s, value); - - } else if (streq(name, "SYSTEM_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s); - - } else if (streq(name, "PUBLISHER_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s); - - } else if (streq(name, "APPLICATION_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s); - - } else if (streq(name, "BOOT_SYSTEM_ID")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s); - } -} - -static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) { - -#if defined(GPT_ROOT_NATIVE) && defined(ENABLE_EFI) - - _cleanup_free_ char *root_id = NULL; - bool found_esp = false; - blkid_partlist pl; - int i, nvals, r; - - assert(pr); - - /* Iterate through the partitions on this disk, and see if the - * EFI ESP we booted from is on it. If so, find the first root - * disk, and add a property indicating its partition UUID. */ - - errno = 0; - pl = blkid_probe_get_partitions(pr); - if (!pl) - return errno > 0 ? -errno : -ENOMEM; - - nvals = blkid_partlist_numof_partitions(pl); - for (i = 0; i < nvals; i++) { - blkid_partition pp; - const char *stype, *sid; - sd_id128_t type; - - pp = blkid_partlist_get_partition(pl, i); - if (!pp) - continue; - - sid = blkid_partition_get_uuid(pp); - if (!sid) - continue; - - stype = blkid_partition_get_type_string(pp); - if (!stype) - continue; - - if (sd_id128_from_string(stype, &type) < 0) - continue; - - if (sd_id128_equal(type, GPT_ESP)) { - sd_id128_t id, esp; - - /* We found an ESP, let's see if it matches - * the ESP we booted from. */ - - if (sd_id128_from_string(sid, &id) < 0) - continue; - - r = efi_loader_get_device_part_uuid(&esp); - if (r < 0) - return r; - - if (sd_id128_equal(id, esp)) - found_esp = true; - - } else if (sd_id128_equal(type, GPT_ROOT_NATIVE)) { - unsigned long long flags; - - flags = blkid_partition_get_flags(pp); - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We found a suitable root partition, let's - * remember the first one. */ - - if (!root_id) { - root_id = strdup(sid); - if (!root_id) - return -ENOMEM; - } - } - } - - /* We found the ESP on this disk, and also found a root - * partition, nice! Let's export its UUID */ - if (found_esp && root_id) - udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", root_id); -#endif - - return 0; -} - -static int probe_superblocks(blkid_probe pr) { - struct stat st; - int rc; - - if (fstat(blkid_probe_get_fd(pr), &st)) - return -1; - - blkid_probe_enable_partitions(pr, 1); - - if (!S_ISCHR(st.st_mode) && - blkid_probe_get_size(pr) <= 1024 * 1440 && - blkid_probe_is_wholedisk(pr)) { - /* - * check if the small disk is partitioned, if yes then - * don't probe for filesystems. - */ - blkid_probe_enable_superblocks(pr, 0); - - rc = blkid_do_fullprobe(pr); - if (rc < 0) - return rc; /* -1 = error, 1 = nothing, 0 = success */ - - if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) - return 0; /* partition table detected */ - } - - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); - blkid_probe_enable_superblocks(pr, 1); - - return blkid_do_safeprobe(pr); -} - -static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) { - const char *root_partition; - int64_t offset = 0; - bool noraid = false; - _cleanup_close_ int fd = -1; - blkid_probe pr; - const char *data; - const char *name; - const char *prtype = NULL; - int nvals; - int i; - int err = 0; - bool is_gpt = false; - - static const struct option options[] = { - { "offset", optional_argument, NULL, 'o' }, - { "noraid", no_argument, NULL, 'R' }, - {} - }; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "oR", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'o': - offset = strtoull(optarg, NULL, 0); - break; - case 'R': - noraid = true; - break; - } - } - - pr = blkid_new_probe(); - if (!pr) - return EXIT_FAILURE; - - blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | - BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION | - BLKID_SUBLKS_BADCSUM); - - if (noraid) - blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); - - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); - if (fd < 0) { - err = log_debug_errno(errno, "Failure opening block device %s: %m", udev_device_get_devnode(dev)); - goto out; - } - - err = blkid_probe_set_device(pr, fd, offset, 0); - if (err < 0) - goto out; - - log_debug("probe %s %sraid offset=%"PRIi64, - udev_device_get_devnode(dev), - noraid ? "no" : "", offset); - - err = probe_superblocks(pr); - if (err < 0) - goto out; - if (blkid_probe_has_value(pr, "SBBADCSUM")) { - if (!blkid_probe_lookup_value(pr, "TYPE", &prtype, NULL)) - log_warning("incorrect %s checksum on %s", - prtype, udev_device_get_devnode(dev)); - else - log_warning("incorrect checksum on %s", - udev_device_get_devnode(dev)); - goto out; - } - - /* If we are a partition then our parent passed on the root - * partition UUID to us */ - root_partition = udev_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID"); - - nvals = blkid_probe_numof_values(pr); - for (i = 0; i < nvals; i++) { - if (blkid_probe_get_value(pr, i, &name, &data, NULL)) - continue; - - print_property(dev, test, name, data); - - /* Is this a disk with GPT partition table? */ - if (streq(name, "PTTYPE") && streq(data, "gpt")) - is_gpt = true; - - /* Is this a partition that matches the root partition - * property we inherited from our parent? */ - if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition)) - udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1"); - } - - if (is_gpt) - find_gpt_root(dev, pr, test); - - blkid_free_probe(pr); -out: - if (err < 0) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_blkid = { - .name = "blkid", - .cmd = builtin_blkid, - .help = "Filesystem and partition probing", - .run_once = true, -}; diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c deleted file mode 100644 index cfaa463804..0000000000 --- a/src/udev/udev-builtin-btrfs.c +++ /dev/null @@ -1,58 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_LINUX_BTRFS_H -#include -#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) { - struct btrfs_ioctl_vol_args args = {}; - _cleanup_close_ int fd = -1; - int err; - - if (argc != 3 || !streq(argv[1], "ready")) - return EXIT_FAILURE; - - fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC); - if (fd < 0) - return EXIT_FAILURE; - - strscpy(args.name, sizeof(args.name), argv[2]); - err = ioctl(fd, BTRFS_IOC_DEVICES_READY, &args); - if (err < 0) - return EXIT_FAILURE; - - udev_builtin_add_property(dev, test, "ID_BTRFS_READY", one_zero(err == 0)); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_btrfs = { - .name = "btrfs", - .cmd = builtin_btrfs, - .help = "btrfs volume management", -}; diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c deleted file mode 100644 index f4a065a97d..0000000000 --- a/src/udev/udev-builtin-hwdb.c +++ /dev/null @@ -1,223 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#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; - -int udev_builtin_hwdb_lookup(struct udev_device *dev, - const char *prefix, const char *modalias, - const char *filter, bool test) { - _cleanup_free_ char *lookup = NULL; - const char *key, *value; - int n = 0; - - if (!hwdb) - return -ENOENT; - - if (prefix) { - lookup = strjoin(prefix, modalias, NULL); - if (!lookup) - return -ENOMEM; - modalias = lookup; - } - - SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) { - if (filter && fnmatch(filter, key, FNM_NOESCAPE) != 0) - continue; - - if (udev_builtin_add_property(dev, test, key, value) < 0) - return -ENOMEM; - n++; - } - return n; -} - -static const char *modalias_usb(struct udev_device *dev, char *s, size_t size) { - const char *v, *p; - int vn, pn; - - v = udev_device_get_sysattr_value(dev, "idVendor"); - if (!v) - return NULL; - p = udev_device_get_sysattr_value(dev, "idProduct"); - if (!p) - return NULL; - vn = strtol(v, NULL, 16); - if (vn <= 0) - return NULL; - pn = strtol(p, NULL, 16); - if (pn <= 0) - return NULL; - snprintf(s, size, "usb:v%04Xp%04X*", vn, pn); - return s; -} - -static int udev_builtin_hwdb_search(struct udev_device *dev, struct udev_device *srcdev, - const char *subsystem, const char *prefix, - const char *filter, bool test) { - struct udev_device *d; - char s[16]; - bool last = false; - int r = 0; - - assert(dev); - - if (!srcdev) - srcdev = dev; - - for (d = srcdev; d && !last; d = udev_device_get_parent(d)) { - const char *dsubsys; - const char *modalias = NULL; - - dsubsys = udev_device_get_subsystem(d); - if (!dsubsys) - continue; - - /* look only at devices of a specific subsystem */ - if (subsystem && !streq(dsubsys, subsystem)) - continue; - - modalias = udev_device_get_property_value(d, "MODALIAS"); - - if (streq(dsubsys, "usb") && streq_ptr(udev_device_get_devtype(d), "usb_device")) { - /* if the usb_device does not have a modalias, compose one */ - if (!modalias) - modalias = modalias_usb(d, s, sizeof(s)); - - /* avoid looking at any parent device, they are usually just a USB hub */ - last = true; - } - - if (!modalias) - continue; - - r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test); - if (r > 0) - break; - } - - return r; -} - -static int builtin_hwdb(struct udev_device *dev, int argc, char *argv[], bool test) { - static const struct option options[] = { - { "filter", required_argument, NULL, 'f' }, - { "device", required_argument, NULL, 'd' }, - { "subsystem", required_argument, NULL, 's' }, - { "lookup-prefix", required_argument, NULL, 'p' }, - {} - }; - const char *filter = NULL; - const char *device = NULL; - const char *subsystem = NULL; - const char *prefix = NULL; - _cleanup_udev_device_unref_ struct udev_device *srcdev = NULL; - - if (!hwdb) - return EXIT_FAILURE; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "f:d:s:p:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'f': - filter = optarg; - break; - - case 'd': - device = optarg; - break; - - case 's': - subsystem = optarg; - break; - - case 'p': - prefix = optarg; - break; - } - } - - /* query a specific key given as argument */ - if (argv[optind]) { - if (udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test) > 0) - return EXIT_SUCCESS; - return EXIT_FAILURE; - } - - /* read data from another device than the device we will store the data */ - if (device) { - srcdev = udev_device_new_from_device_id(udev_device_get_udev(dev), device); - if (!srcdev) - return EXIT_FAILURE; - } - - if (udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test) > 0) - return EXIT_SUCCESS; - return EXIT_FAILURE; -} - -/* called at udev startup and reload */ -static int builtin_hwdb_init(struct udev *udev) { - int r; - - if (hwdb) - return 0; - - r = sd_hwdb_new(&hwdb); - if (r < 0) - return r; - - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_hwdb_exit(struct udev *udev) { - hwdb = sd_hwdb_unref(hwdb); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_hwdb_validate(struct udev *udev) { - return hwdb_validate(hwdb); -} - -const struct udev_builtin udev_builtin_hwdb = { - .name = "hwdb", - .cmd = builtin_hwdb, - .init = builtin_hwdb_init, - .exit = builtin_hwdb_exit, - .validate = builtin_hwdb_validate, - .help = "Hardware database", -}; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c deleted file mode 100644 index 59b9804dc4..0000000000 --- a/src/udev/udev-builtin-input_id.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * expose input properties via udev - * - * Copyright (C) 2009 Martin Pitt - * Portions Copyright (C) 2004 David Zeuthen, - * Copyright (C) 2011 Kay Sievers - * Copyright (C) 2014 Carlos Garnacho - * Copyright (C) 2014 David Herrmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" -#include "util.h" - -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<> OFF(bit)) & 1) - -static inline int abs_size_mm(const struct input_absinfo *absinfo) { - /* Resolution is defined to be in units/mm for ABS_X/Y */ - return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; -} - -static void extract_info(struct udev_device *dev, const char *devpath, bool test) { - char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)]; - struct input_absinfo xabsinfo = {}, yabsinfo = {}; - _cleanup_close_ int fd = -1; - - fd = open(devpath, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return; - - if (ioctl(fd, EVIOCGABS(ABS_X), &xabsinfo) < 0 || - ioctl(fd, EVIOCGABS(ABS_Y), &yabsinfo) < 0) - return; - - if (xabsinfo.resolution <= 0 || yabsinfo.resolution <= 0) - return; - - 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); -} - -/* - * Read a capability attribute and return bitmask. - * @param dev udev_device - * @param attr sysfs attribute name (e. g. "capabilities/key") - * @param bitmask: Output array which has a sizeof of bitmask_size - */ -static void get_cap_mask(struct udev_device *dev, - struct udev_device *pdev, const char* attr, - unsigned long *bitmask, size_t bitmask_size, - bool test) { - const char *v; - char text[4096]; - unsigned i; - char* word; - unsigned long val; - - v = udev_device_get_sysattr_value(pdev, attr); - if (!v) - v = ""; - - xsprintf(text, "%s", v); - log_debug("%s raw kernel attribute: %s", attr, text); - - memzero(bitmask, bitmask_size); - i = 0; - while ((word = strrchr(text, ' ')) != NULL) { - val = strtoul (word+1, NULL, 16); - if (i < bitmask_size/sizeof(unsigned long)) - bitmask[i] = val; - else - log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); - *word = '\0'; - ++i; - } - val = strtoul (text, NULL, 16); - if (i < bitmask_size / sizeof(unsigned long)) - bitmask[i] = val; - else - log_debug("ignoring %s block %lX which is larger than maximum size", attr, val); - - if (test) { - /* printf pattern with the right unsigned long number of hex chars */ - 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 */ - while (bitmask[val-1] == 0 && val > 0) - --val; - for (i = 0; i < val; ++i) { - DISABLE_WARNING_FORMAT_NONLITERAL; - log_debug(text, i * BITS_PER_LONG, bitmask[i]); - REENABLE_WARNING; - } - } -} - -/* pointer devices */ -static bool test_pointers(struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_abs, - const unsigned long* bitmask_key, - const unsigned long* bitmask_rel, - const unsigned long* bitmask_props, - bool test) { - bool has_abs_coordinates = false; - bool has_rel_coordinates = false; - bool has_mt_coordinates = false; - bool has_joystick_axes_or_buttons = false; - bool is_direct = false; - bool has_touch = false; - bool has_3d_coordinates = false; - bool has_keys = false; - bool stylus_or_pen = false; - bool finger_but_no_pen = false; - bool has_mouse_button = false; - bool is_mouse = false; - bool is_touchpad = false; - bool is_touchscreen = false; - bool is_tablet = false; - bool is_joystick = false; - bool is_accelerometer = false; - bool is_pointing_stick= false; - - has_keys = test_bit(EV_KEY, bitmask_ev); - has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs); - has_3d_coordinates = has_abs_coordinates && test_bit(ABS_Z, bitmask_abs); - is_accelerometer = test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props); - - if (!has_keys && has_3d_coordinates) - is_accelerometer = true; - - if (is_accelerometer) { - udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); - return true; - } - - is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props); - stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key); - finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key); - has_mouse_button = test_bit(BTN_LEFT, bitmask_key); - has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel); - has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs); - - /* unset has_mt_coordinates if devices claims to have all abs axis */ - if (has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs)) - has_mt_coordinates = false; - is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props); - has_touch = test_bit(BTN_TOUCH, bitmask_key); - /* joysticks don't necessarily have buttons; e. g. - * rudders/pedals are joystick-like, but buttonless; they have - * other fancy axes */ - has_joystick_axes_or_buttons = test_bit(BTN_TRIGGER, bitmask_key) || - test_bit(BTN_A, bitmask_key) || - test_bit(BTN_1, bitmask_key) || - test_bit(ABS_RX, bitmask_abs) || - test_bit(ABS_RY, bitmask_abs) || - test_bit(ABS_RZ, bitmask_abs) || - test_bit(ABS_THROTTLE, bitmask_abs) || - test_bit(ABS_RUDDER, bitmask_abs) || - test_bit(ABS_WHEEL, bitmask_abs) || - test_bit(ABS_GAS, bitmask_abs) || - test_bit(ABS_BRAKE, bitmask_abs); - - if (has_abs_coordinates) { - if (stylus_or_pen) - is_tablet = true; - else if (finger_but_no_pen && !is_direct) - is_touchpad = true; - else if (has_mouse_button) - /* This path is taken by VMware's USB mouse, which has - * absolute axes, but no touch/pressure button. */ - is_mouse = true; - else if (has_touch || is_direct) - is_touchscreen = true; - else if (has_joystick_axes_or_buttons) - is_joystick = true; - } - if (has_mt_coordinates) { - if (stylus_or_pen) - is_tablet = true; - else if (finger_but_no_pen && !is_direct) - is_touchpad = true; - else if (has_touch || is_direct) - is_touchscreen = true; - } - - if (has_rel_coordinates && has_mouse_button) - is_mouse = true; - - if (is_pointing_stick) - udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1"); - if (is_mouse) - udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); - if (is_touchpad) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); - if (is_touchscreen) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); - if (is_joystick) - udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); - if (is_tablet) - udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); - - return is_tablet || is_mouse || is_touchpad || is_touchscreen || is_joystick || is_pointing_stick; -} - -/* key like devices */ -static bool test_key(struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_key, - bool test) { - unsigned i; - unsigned long found; - unsigned long mask; - bool ret = false; - - /* do we have any KEY_* capability? */ - if (!test_bit(EV_KEY, bitmask_ev)) { - log_debug("test_key: no EV_KEY capability"); - return false; - } - - /* only consider KEY_* here, not BTN_* */ - found = 0; - for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { - found |= bitmask_key[i]; - log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0); - } - /* If there are no keys in the lower block, check the higher block */ - if (!found) { - for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { - if (test_bit(i, bitmask_key)) { - log_debug("test_key: Found key %x in high block", i); - found = 1; - break; - } - } - } - - if (found > 0) { - udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); - ret = true; - } - - /* the first 32 bits are ESC, numbers, and Q to D; if we have all of - * those, consider it a full keyboard; do not test KEY_RESERVED, though */ - mask = 0xFFFFFFFE; - if ((bitmask_key[0] & mask) == mask) { - udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); - ret = true; - } - - return ret; -} - -static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev_device *pdev; - unsigned long bitmask_ev[NBITS(EV_MAX)]; - unsigned long bitmask_abs[NBITS(ABS_MAX)]; - unsigned long bitmask_key[NBITS(KEY_MAX)]; - unsigned long bitmask_rel[NBITS(REL_MAX)]; - unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)]; - const char *sysname, *devnode; - bool is_pointer; - bool is_key; - - assert(dev); - - /* walk up the parental chain until we find the real input device; the - * argument is very likely a subdevice of this, like eventN */ - pdev = dev; - while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) - pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); - - if (pdev) { - /* Use this as a flag that input devices were detected, so that this - * program doesn't need to be called more than once per device */ - udev_builtin_add_property(dev, test, "ID_INPUT", "1"); - get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); - get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); - get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); - get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); - get_cap_mask(dev, pdev, "properties", bitmask_props, sizeof(bitmask_props), test); - is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs, - bitmask_key, bitmask_rel, - bitmask_props, test); - is_key = test_key(dev, bitmask_ev, bitmask_key, test); - /* Some evdev nodes have only a scrollwheel */ - if (!is_pointer && !is_key && test_bit(EV_REL, bitmask_ev) && - (test_bit(REL_WHEEL, bitmask_rel) || test_bit(REL_HWHEEL, bitmask_rel))) - udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); - } - - devnode = udev_device_get_devnode(dev); - sysname = udev_device_get_sysname(dev); - if (devnode && sysname && startswith(sysname, "event")) - extract_info(dev, devnode, test); - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_input_id = { - .name = "input_id", - .cmd = builtin_input_id, - .help = "Input device properties", -}; diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c deleted file mode 100644 index aa10beafb0..0000000000 --- a/src/udev/udev-builtin-keyboard.c +++ /dev/null @@ -1,277 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include - -#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); -#include "keyboard-keys-from-name.h" - -static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) { - struct udev_device *atkbd; - const char *cur; - char codes[4096]; - char *s; - size_t l; - unsigned i; - int ret; - - assert(dev); - assert(release); - - atkbd = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); - if (!atkbd) - return -ENODEV; - - cur = udev_device_get_sysattr_value(atkbd, "force_release"); - if (!cur) - return -ENODEV; - - s = codes; - l = sizeof(codes); - - /* copy current content */ - l = strpcpy(&s, l, cur); - - /* append new codes */ - for (i = 0; i < release_count; i++) - l = strpcpyf(&s, l, ",%u", release[i]); - - log_debug("keyboard: updating force-release list with '%s'", codes); - ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); - if (ret < 0) - log_error_errno(ret, "Error writing force-release attribute: %m"); - return ret; -} - -static void map_keycode(int fd, const char *devnode, int scancode, const char *keycode) -{ - struct { - unsigned scan; - unsigned key; - } map; - char *endptr; - const struct key *k; - unsigned keycode_num; - - /* translate identifier to key code */ - k = keyboard_lookup_key(keycode, strlen(keycode)); - if (k) { - keycode_num = k->id; - } else { - /* check if it's a numeric code already */ - keycode_num = strtoul(keycode, &endptr, 0); - if (endptr[0] !='\0') { - log_error("Unknown key identifier '%s'", keycode); - return; - } - } - - map.scan = scancode; - map.key = keycode_num; - - log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)", - map.scan, map.scan, map.key, map.key); - - if (ioctl(fd, EVIOCSKEYCODE, &map) < 0) - log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", devnode, map.scan, map.key); -} - -static inline char* parse_token(const char *current, int32_t *val_out) { - char *next; - int32_t val; - - if (!current) - return NULL; - - val = strtol(current, &next, 0); - if (*next && *next != ':') - return NULL; - - if (next != current) - *val_out = val; - - if (*next) - next++; - - return next; -} - -static void override_abs(int fd, const char *devnode, - unsigned evcode, const char *value) { - struct input_absinfo absinfo; - int rc; - char *next; - - rc = ioctl(fd, EVIOCGABS(evcode), &absinfo); - if (rc < 0) { - log_error_errno(errno, "Unable to EVIOCGABS device \"%s\"", devnode); - return; - } - - next = parse_token(value, &absinfo.minimum); - next = parse_token(next, &absinfo.maximum); - next = parse_token(next, &absinfo.resolution); - next = parse_token(next, &absinfo.fuzz); - next = parse_token(next, &absinfo.flat); - if (!next) { - log_error("Unable to parse EV_ABS override '%s' for '%s'", value, devnode); - return; - } - - log_debug("keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32" for \"%s\"", - evcode, - absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat, - devnode); - rc = ioctl(fd, EVIOCSABS(evcode), &absinfo); - if (rc < 0) - log_error_errno(errno, "Unable to EVIOCSABS device \"%s\"", devnode); -} - -static void set_trackpoint_sensitivity(struct udev_device *dev, const char *value) -{ - struct udev_device *pdev; - char val_s[DECIMAL_STR_MAX(int)]; - int r, val_i; - - assert(dev); - assert(value); - - /* The sensitivity sysfs attr belongs to the serio parent device */ - pdev = udev_device_get_parent_with_subsystem_devtype(dev, "serio", NULL); - if (!pdev) { - log_warning("Failed to get serio parent for '%s'", udev_device_get_devnode(dev)); - return; - } - - r = safe_atoi(value, &val_i); - if (r < 0) { - log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev)); - return; - } - - xsprintf(val_s, "%d", val_i); - - r = udev_device_set_sysattr_value(pdev, "sensitivity", val_s); - if (r < 0) - log_error_errno(r, "Failed to write 'sensitivity' attribute for '%s': %m", udev_device_get_devnode(pdev)); -} - -static int open_device(const char *devnode) { - int fd; - - fd = open(devnode, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); - if (fd < 0) - return log_error_errno(errno, "Error opening device \"%s\": %m", devnode); - - return fd; -} - -static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev_list_entry *entry; - unsigned release[1024]; - unsigned release_count = 0; - _cleanup_close_ int fd = -1; - const char *node; - - node = udev_device_get_devnode(dev); - if (!node) { - log_error("No device node for \"%s\"", udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) { - const char *key; - char *endptr; - - key = udev_list_entry_get_name(entry); - if (startswith(key, "KEYBOARD_KEY_")) { - const char *keycode; - unsigned scancode; - - /* KEYBOARD_KEY_= */ - scancode = strtoul(key + 13, &endptr, 16); - if (endptr[0] != '\0') { - log_warning("Unable to parse scan code from \"%s\"", key); - continue; - } - - keycode = udev_list_entry_get_value(entry); - - /* a leading '!' needs a force-release entry */ - if (keycode[0] == '!') { - keycode++; - - release[release_count] = scancode; - if (release_count < ELEMENTSOF(release)-1) - release_count++; - - if (keycode[0] == '\0') - continue; - } - - if (fd == -1) { - fd = open_device(node); - if (fd < 0) - return EXIT_FAILURE; - } - - map_keycode(fd, node, scancode, keycode); - } else if (startswith(key, "EVDEV_ABS_")) { - unsigned evcode; - - /* EVDEV_ABS_=:::: */ - evcode = strtoul(key + 10, &endptr, 16); - if (endptr[0] != '\0') { - log_warning("Unable to parse EV_ABS code from \"%s\"", key); - continue; - } - - if (fd == -1) { - fd = open_device(node); - if (fd < 0) - return EXIT_FAILURE; - } - - override_abs(fd, node, evcode, udev_list_entry_get_value(entry)); - } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) - set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry)); - } - - /* install list of force-release codes */ - if (release_count > 0) - install_force_release(dev, release, release_count); - - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_keyboard = { - .name = "keyboard", - .cmd = builtin_keyboard, - .help = "Keyboard scan code to key mapping", -}; diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c deleted file mode 100644 index 9665f678fd..0000000000 --- a/src/udev/udev-builtin-kmod.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * load kernel modules - * - * Copyright (C) 2011-2012 Kay Sievers - * Copyright (C) 2011 ProFUSION embedded systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#include "string-util.h" -#include "udev.h" - -static struct kmod_ctx *ctx = NULL; - -static int load_module(struct udev *udev, const char *alias) { - struct kmod_list *list = NULL; - struct kmod_list *l; - int err; - - err = kmod_module_new_from_lookup(ctx, alias, &list); - if (err < 0) - return err; - - if (list == NULL) - log_debug("No module matches '%s'", alias); - - kmod_list_foreach(l, list) { - struct kmod_module *mod = kmod_module_get_module(l); - - err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (err == KMOD_PROBE_APPLY_BLACKLIST) - log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod)); - else if (err == 0) - log_debug("Inserted '%s'", kmod_module_get_name(mod)); - else - log_debug("Failed to insert '%s'", kmod_module_get_name(mod)); - - kmod_module_unref(mod); - } - - kmod_module_unref_list(list); - return err; -} - -_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { - log_internalv(priority, 0, file, line, fn, format, args); -} - -static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev *udev = udev_device_get_udev(dev); - int i; - - if (!ctx) - return 0; - - if (argc < 3 || !streq(argv[1], "load")) { - log_error("expect: %s load ", argv[0]); - return EXIT_FAILURE; - } - - for (i = 2; argv[i]; i++) { - log_debug("Execute '%s' '%s'", argv[1], argv[i]); - load_module(udev, argv[i]); - } - - return EXIT_SUCCESS; -} - -/* called at udev startup and reload */ -static int builtin_kmod_init(struct udev *udev) { - if (ctx) - return 0; - - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - log_debug("Load module index"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_kmod_exit(struct udev *udev) { - log_debug("Unload module index"); - ctx = kmod_unref(ctx); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_kmod_validate(struct udev *udev) { - log_debug("Validate module index"); - if (!ctx) - return false; - return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK); -} - -const struct udev_builtin udev_builtin_kmod = { - .name = "kmod", - .cmd = builtin_kmod, - .init = builtin_kmod_init, - .exit = builtin_kmod_exit, - .validate = builtin_kmod_validate, - .help = "Kernel module loader", - .run_once = false, -}; diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c deleted file mode 100644 index a7be2a4eed..0000000000 --- a/src/udev/udev-builtin-net_id.c +++ /dev/null @@ -1,623 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -/* - * Predictable network interface device names based on: - * - firmware/bios-provided index numbers for on-board devices - * - firmware-provided pci-express hotplug slot index number - * - physical/geographical location of the hardware - * - the interface's MAC address - * - * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames - * - * Two character prefixes based on the type of interface: - * en — Ethernet - * sl — serial line IP (slip) - * wl — wlan - * ww — wwan - * - * Type of names: - * b — BCMA bus core number - * c — CCW bus group name, without leading zeros [s390] - * o[d] — on-board device index number - * s[f][d] — hotplug slot index number - * x — MAC address - * [P]ps[f][d] - * — PCI geographical location - * [P]ps[f][u][..][c][i] - * — USB port number chain - * - * All multi-function PCI devices will carry the [f] number in the - * device name, including the function 0 device. - * - * When using PCI geography, The PCI domain is only prepended when it is not 0. - * - * For USB devices the full chain of port numbers of hubs is composed. If the - * name gets longer than the maximum number of 15 characters, the name is not - * exported. - * The usual USB configuration == 1 and interface == 0 values are suppressed. - * - * 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: - * /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: - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 - * ID_NET_NAME_MAC=enx78e7d1ea46da - * ID_NET_NAME_PATH=enp2s0f0 - * /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 - * ID_NET_NAME_MAC=enx78e7d1ea46dc - * ID_NET_NAME_PATH=enp2s0f1 - * - * PCI wlan card: - * /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 - * ID_NET_NAME_MAC=wlx0024d7e31130 - * ID_NET_NAME_PATH=wlp3s0 - * - * USB built-in 3G modem: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 - * ID_NET_NAME_MAC=wwx028037ec0200 - * ID_NET_NAME_PATH=wwp0s29u1u4i6 - * - * USB Android phone: - * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 - * ID_NET_NAME_MAC=enxd626b3450fb5 - * ID_NET_NAME_PATH=enp0s29u1u2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "fileio.h" -#include "stdio-util.h" -#include "string-util.h" -#include "udev.h" - -#define ONBOARD_INDEX_MAX (16*1024-1) - -enum netname_type{ - NET_UNDEF, - NET_PCI, - NET_USB, - NET_BCMA, - NET_VIRTIO, - NET_CCWGROUP, -}; - -struct netnames { - enum netname_type type; - - uint8_t mac[6]; - bool mac_valid; - - struct udev_device *pcidev; - char pci_slot[IFNAMSIZ]; - char pci_path[IFNAMSIZ]; - char pci_onboard[IFNAMSIZ]; - const char *pci_onboard_label; - - char usb_ports[IFNAMSIZ]; - char bcma_core[IFNAMSIZ]; - char ccw_group[IFNAMSIZ]; -}; - -/* retrieve on-board index number and label from firmware */ -static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { - unsigned dev_port = 0; - size_t l; - char *s; - const char *attr; - int idx; - - /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ - attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index"); - /* SMBIOS type 41 — Onboard Devices Extended Information */ - if (!attr) - attr = udev_device_get_sysattr_value(names->pcidev, "index"); - if (!attr) - return -ENOENT; - - idx = strtoul(attr, NULL, 0); - if (idx <= 0) - return -EINVAL; - - /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for - * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary - * cut-off, which is somewhere beyond the realistic number of physical network interface a system might - * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */ - if (idx > ONBOARD_INDEX_MAX) - return -ENOENT; - - /* kernel provided port index for multiple ports on a single PCI function */ - attr = udev_device_get_sysattr_value(dev, "dev_port"); - if (attr) - dev_port = strtol(attr, NULL, 10); - - s = names->pci_onboard; - l = sizeof(names->pci_onboard); - l = strpcpyf(&s, l, "o%d", idx); - if (dev_port > 0) - l = strpcpyf(&s, l, "d%d", dev_port); - if (l == 0) - names->pci_onboard[0] = '\0'; - - names->pci_onboard_label = udev_device_get_sysattr_value(names->pcidev, "label"); - - return 0; -} - -/* read the 256 bytes PCI configuration space to check the multi-function bit */ -static bool is_pci_multifunction(struct udev_device *dev) { - _cleanup_close_ int fd = -1; - const char *filename; - uint8_t config[64]; - - filename = strjoina(udev_device_get_syspath(dev), "/config"); - fd = open(filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) - return false; - if (read(fd, &config, sizeof(config)) != sizeof(config)) - return false; - - /* bit 0-6 header type, bit 7 multi/single function device */ - if ((config[PCI_HEADER_TYPE] & 0x80) != 0) - return true; - - return false; -} - -static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { - struct udev *udev = udev_device_get_udev(names->pcidev); - unsigned domain, bus, slot, func, dev_port = 0; - size_t l; - char *s; - const char *attr; - struct udev_device *pci = NULL; - char slots[256], str[256]; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - int hotplug_slot = 0, err = 0; - - if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4) - return -ENOENT; - - /* kernel provided port index for multiple ports on a single PCI function */ - attr = udev_device_get_sysattr_value(dev, "dev_port"); - if (attr) - dev_port = strtol(attr, NULL, 10); - - /* compose a name based on the raw kernel's PCI bus, slot numbers */ - s = names->pci_path; - l = sizeof(names->pci_path); - if (domain > 0) - l = strpcpyf(&s, l, "P%u", domain); - l = strpcpyf(&s, l, "p%us%u", bus, slot); - if (func > 0 || is_pci_multifunction(names->pcidev)) - l = strpcpyf(&s, l, "f%u", func); - if (dev_port > 0) - l = strpcpyf(&s, l, "d%u", dev_port); - if (l == 0) - names->pci_path[0] = '\0'; - - /* ACPI _SUN — slot user number */ - pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); - if (!pci) { - err = -ENOENT; - goto out; - } - xsprintf(slots, "%s/slots", udev_device_get_syspath(pci)); - dir = opendir(slots); - if (!dir) { - err = -errno; - goto out; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - int i; - char *rest; - char *address; - - if (dent->d_name[0] == '.') - continue; - i = strtol(dent->d_name, &rest, 10); - if (rest[0] != '\0') - continue; - if (i < 1) - continue; - 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))) - hotplug_slot = i; - free(address); - } - - if (hotplug_slot > 0) - break; - } - - if (hotplug_slot > 0) { - s = names->pci_slot; - l = sizeof(names->pci_slot); - if (domain > 0) - l = strpcpyf(&s, l, "P%d", domain); - l = strpcpyf(&s, l, "s%d", hotplug_slot); - if (func > 0 || is_pci_multifunction(names->pcidev)) - l = strpcpyf(&s, l, "f%d", func); - if (dev_port > 0) - l = strpcpyf(&s, l, "d%d", dev_port); - if (l == 0) - names->pci_slot[0] = '\0'; - } -out: - udev_device_unref(pci); - return err; -} - -static int names_pci(struct udev_device *dev, struct netnames *names) { - struct udev_device *parent; - - assert(dev); - assert(names); - - parent = udev_device_get_parent(dev); - - /* there can only ever be one virtio bus per parent device, so we can - safely ignore any virtio buses. see - */ - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); - - if (!parent) - return -ENOENT; - - /* check if our direct parent is a PCI device with no other bus in-between */ - if (streq_ptr("pci", udev_device_get_subsystem(parent))) { - names->type = NET_PCI; - names->pcidev = parent; - } else { - names->pcidev = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); - if (!names->pcidev) - return -ENOENT; - } - dev_pci_onboard(dev, names); - dev_pci_slot(dev, names); - return 0; -} - -static int names_usb(struct udev_device *dev, struct netnames *names) { - struct udev_device *usbdev; - char name[256]; - char *ports; - char *config; - char *interf; - size_t l; - char *s; - - assert(dev); - assert(names); - - usbdev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); - if (!usbdev) - return -ENOENT; - - /* get USB port number chain, configuration, interface */ - strscpy(name, sizeof(name), udev_device_get_sysname(usbdev)); - s = strchr(name, '-'); - if (!s) - return -EINVAL; - ports = s+1; - - s = strchr(ports, ':'); - if (!s) - return -EINVAL; - s[0] = '\0'; - config = s+1; - - s = strchr(config, '.'); - if (!s) - return -EINVAL; - s[0] = '\0'; - interf = s+1; - - /* prefix every port number in the chain with "u" */ - s = ports; - while ((s = strchr(s, '.'))) - s[0] = 'u'; - s = names->usb_ports; - l = strpcpyl(&s, sizeof(names->usb_ports), "u", ports, NULL); - - /* append USB config number, suppress the common config == 1 */ - if (!streq(config, "1")) - l = strpcpyl(&s, sizeof(names->usb_ports), "c", config, NULL); - - /* append USB interface number, suppress the interface == 0 */ - if (!streq(interf, "0")) - l = strpcpyl(&s, sizeof(names->usb_ports), "i", interf, NULL); - if (l == 0) - return -ENAMETOOLONG; - - names->type = NET_USB; - return 0; -} - -static int names_bcma(struct udev_device *dev, struct netnames *names) { - struct udev_device *bcmadev; - unsigned int core; - - assert(dev); - assert(names); - - bcmadev = udev_device_get_parent_with_subsystem_devtype(dev, "bcma", NULL); - if (!bcmadev) - return -ENOENT; - - /* bus num:core num */ - if (sscanf(udev_device_get_sysname(bcmadev), "bcma%*u:%u", &core) != 1) - return -EINVAL; - /* suppress the common core == 0 */ - if (core > 0) - xsprintf(names->bcma_core, "b%u", core); - - names->type = NET_BCMA; - return 0; -} - -static int names_ccw(struct udev_device *dev, struct netnames *names) { - struct udev_device *cdev; - const char *bus_id; - size_t bus_id_len; - int rc; - - assert(dev); - assert(names); - - /* Retrieve the associated CCW device */ - cdev = udev_device_get_parent(dev); - if (!cdev) - return -ENOENT; - - /* Network devices are always grouped CCW devices */ - if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev))) - return -ENOENT; - - /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely - * identifies the network device on the Linux on System z channel - * subsystem. Note that the bus-ID contains lowercase characters. - */ - bus_id = udev_device_get_sysname(cdev); - if (!bus_id) - return -ENOENT; - - /* Check the length of the bus-ID. Rely on that the kernel provides - * a correct bus-ID; alternatively, improve this check and parse and - * verify each bus-ID part... - */ - bus_id_len = strlen(bus_id); - if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) - return -EINVAL; - - /* Strip leading zeros from the bus id for aesthetic purposes. This - * keeps the ccw names stable, yet much shorter in general case of - * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is - * not prepended when it is zero. - */ - bus_id += strspn(bus_id, ".0"); - - /* Store the CCW bus-ID for use as network device name */ - rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id); - if (rc >= 0 && rc < (int)sizeof(names->ccw_group)) - names->type = NET_CCWGROUP; - return 0; -} - -static int names_mac(struct udev_device *dev, struct netnames *names) { - const char *s; - unsigned int i; - unsigned int a1, a2, a3, a4, a5, a6; - - /* check for NET_ADDR_PERM, skip random MAC addresses */ - s = udev_device_get_sysattr_value(dev, "addr_assign_type"); - if (!s) - return EXIT_FAILURE; - i = strtoul(s, NULL, 0); - if (i != 0) - return 0; - - s = udev_device_get_sysattr_value(dev, "address"); - if (!s) - return -ENOENT; - if (sscanf(s, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) - return -EINVAL; - - /* skip empty MAC addresses */ - if (a1 + a2 + a3 + a4 + a5 + a6 == 0) - return -EINVAL; - - names->mac[0] = a1; - names->mac[1] = a2; - names->mac[2] = a3; - names->mac[3] = a4; - names->mac[4] = a5; - names->mac[5] = a6; - names->mac_valid = true; - return 0; -} - -/* IEEE Organizationally Unique Identifier vendor string */ -static int ieee_oui(struct udev_device *dev, struct netnames *names, bool test) { - char str[32]; - - if (!names->mac_valid) - return -ENOENT; - /* skip commonly misused 00:00:00 (Xerox) prefix */ - if (memcmp(names->mac, "\0\0\0", 3) == 0) - return -EINVAL; - 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; -} - -static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool test) { - const char *s; - const char *p; - unsigned int i; - const char *devtype; - const char *prefix = "en"; - struct netnames names = {}; - int err; - - /* handle only ARPHRD_ETHER and ARPHRD_SLIP devices */ - s = udev_device_get_sysattr_value(dev, "type"); - if (!s) - return EXIT_FAILURE; - i = strtoul(s, NULL, 0); - switch (i) { - case ARPHRD_ETHER: - prefix = "en"; - break; - case ARPHRD_SLIP: - prefix = "sl"; - break; - default: - return 0; - } - - /* skip stacked devices, like VLANs, ... */ - s = udev_device_get_sysattr_value(dev, "ifindex"); - if (!s) - return EXIT_FAILURE; - p = udev_device_get_sysattr_value(dev, "iflink"); - if (!p) - return EXIT_FAILURE; - if (!streq(s, p)) - return 0; - - devtype = udev_device_get_devtype(dev); - if (devtype) { - if (streq("wlan", devtype)) - prefix = "wl"; - else if (streq("wwan", devtype)) - prefix = "ww"; - } - - err = names_mac(dev, &names); - if (err >= 0 && names.mac_valid) { - char str[IFNAMSIZ]; - - 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); - - ieee_oui(dev, &names, test); - } - - /* get path names for Linux on System z network devices */ - err = names_ccw(dev, &names); - if (err >= 0 && names.type == NET_CCWGROUP) { - char str[IFNAMSIZ]; - - if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - goto out; - } - - /* get PCI based path names, we compose only PCI based paths */ - err = names_pci(dev, &names); - if (err < 0) - goto out; - - /* plain PCI device */ - if (names.type == NET_PCI) { - char str[IFNAMSIZ]; - - if (names.pci_onboard[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str); - - if (names.pci_onboard_label) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_onboard_label) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_LABEL_ONBOARD", str); - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_path) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s", prefix, names.pci_slot) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } - - /* USB device */ - err = names_usb(dev, &names); - if (err >= 0 && names.type == NET_USB) { - char str[IFNAMSIZ]; - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.usb_ports) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.usb_ports) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } - - /* Broadcom bus */ - err = names_bcma(dev, &names); - if (err >= 0 && names.type == NET_BCMA) { - char str[IFNAMSIZ]; - - if (names.pci_path[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_path, names.bcma_core) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); - - if (names.pci_slot[0]) - if (snprintf(str, sizeof(str), "%s%s%s", prefix, names.pci_slot, names.bcma_core) < (int)sizeof(str)) - udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); - goto out; - } -out: - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_net_id = { - .name = "net_id", - .cmd = builtin_net_id, - .help = "Network device properties", -}; diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c deleted file mode 100644 index 8e47775135..0000000000 --- a/src/udev/udev-builtin-net_setup_link.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 . -***/ - -#include "alloc-util.h" -#include "link-config.h" -#include "log.h" -#include "udev.h" - -static link_config_ctx *ctx = NULL; - -static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv, bool test) { - _cleanup_free_ char *driver = NULL; - const char *name = NULL; - link_config *link; - int r; - - if (argc > 1) { - log_error("This program takes no arguments."); - return EXIT_FAILURE; - } - - r = link_get_driver(ctx, dev, &driver); - if (r >= 0) - udev_builtin_add_property(dev, test, "ID_NET_DRIVER", driver); - - r = link_config_get(ctx, dev, &link); - if (r < 0) { - if (r == -ENOENT) { - log_debug("No matching link configuration found."); - return EXIT_SUCCESS; - } else { - log_error_errno(r, "Could not get link config: %m"); - return EXIT_FAILURE; - } - } - - r = link_config_apply(ctx, link, dev, &name); - if (r < 0) { - log_error_errno(r, "Could not apply link config to %s: %m", udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->filename); - - if (name) - udev_builtin_add_property(dev, test, "ID_NET_NAME", name); - - return EXIT_SUCCESS; -} - -static int builtin_net_setup_link_init(struct udev *udev) { - int r; - - if (ctx) - return 0; - - r = link_config_ctx_new(&ctx); - if (r < 0) - return r; - - r = link_config_load(ctx); - if (r < 0) - return r; - - log_debug("Created link configuration context."); - return 0; -} - -static void builtin_net_setup_link_exit(struct udev *udev) { - link_config_ctx_free(ctx); - ctx = NULL; - log_debug("Unloaded link configuration context."); -} - -static bool builtin_net_setup_link_validate(struct udev *udev) { - log_debug("Check if link configuration needs reloading."); - if (!ctx) - return false; - - return link_config_should_reload(ctx); -} - -const struct udev_builtin udev_builtin_net_setup_link = { - .name = "net_setup_link", - .cmd = builtin_net_setup_link, - .init = builtin_net_setup_link_init, - .exit = builtin_net_setup_link_exit, - .validate = builtin_net_setup_link_validate, - .help = "Configure network link", - .run_once = false, -}; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c deleted file mode 100644 index 6e9adc6e96..0000000000 --- a/src/udev/udev-builtin-path_id.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009-2011 Kay Sievers - * - * Logic based on Hannes Reinecke's shell script. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "string-util.h" -#include "udev.h" - -_printf_(2,3) -static int path_prepend(char **path, const char *fmt, ...) { - va_list va; - char *pre; - int err = 0; - - va_start(va, fmt); - err = vasprintf(&pre, fmt, va); - va_end(va); - if (err < 0) - goto out; - - if (*path != NULL) { - char *new; - - err = asprintf(&new, "%s-%s", pre, *path); - free(pre); - if (err < 0) - goto out; - free(*path); - *path = new; - } else { - *path = pre; - } -out: - return err; -} - -/* -** Linux only supports 32 bit luns. -** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. -*/ -static int format_lun_number(struct udev_device *dev, char **path) { - unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); - - /* address method 0, peripheral device addressing with bus id of zero */ - if (lun < 256) - return path_prepend(path, "lun-%lu", lun); - /* handle all other lun addressing methods by using a variant of the original lun format */ - return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff); -} - -static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) { - struct udev_device *parent = dev; - - assert(dev); - assert(subsys); - - while (parent != NULL) { - const char *subsystem; - - subsystem = udev_device_get_subsystem(parent); - if (subsystem == NULL || !streq(subsystem, subsys)) - break; - dev = parent; - parent = udev_device_get_parent(parent); - } - return dev; -} - -static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *fcdev = NULL; - const char *port; - char *lun = NULL; - - assert(parent); - assert(path); - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); - if (fcdev == NULL) - return NULL; - port = udev_device_get_sysattr_value(fcdev, "port_name"); - if (port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "fc-%s-%s", port, lun); - free(lun); -out: - udev_device_unref(fcdev); - return parent; -} - -static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *sasdev; - const char *sas_address; - char *lun = NULL; - - assert(parent); - assert(path); - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", - udev_device_get_sysname(target_parent)); - if (sasdev == NULL) - return NULL; - - sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "sas-%s-%s", sas_address, lun); - free(lun); -out: - udev_device_unref(sasdev); - return parent; -} - -static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *port; - struct udev_device *expander; - struct udev_device *target_sasdev = NULL; - struct udev_device *expander_sasdev = NULL; - struct udev_device *port_sasdev = NULL; - const char *sas_address = NULL; - const char *phy_id; - const char *phy_count; - char *lun = NULL; - - assert(parent); - assert(path); - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - /* Get sas device */ - target_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_device", udev_device_get_sysname(target_parent)); - if (target_sasdev == NULL) - return NULL; - - /* The next parent is sas port */ - port = udev_device_get_parent(target_parent); - if (port == NULL) { - parent = NULL; - goto out; - } - - /* Get port device */ - port_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_port", udev_device_get_sysname(port)); - - phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys"); - if (phy_count == NULL) { - parent = NULL; - goto out; - } - - /* Check if we are simple disk */ - if (strncmp(phy_count, "1", 2) != 0) { - parent = handle_scsi_sas_wide_port(parent, path); - goto out; - } - - /* Get connected phy */ - phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier"); - if (phy_id == NULL) { - parent = NULL; - goto out; - } - - /* The port's parent is either hba or expander */ - expander = udev_device_get_parent(port); - if (expander == NULL) { - parent = NULL; - goto out; - } - - /* Get expander device */ - expander_sasdev = udev_device_new_from_subsystem_sysname(udev, - "sas_device", udev_device_get_sysname(expander)); - if (expander_sasdev != NULL) { - /* Get expander's address */ - sas_address = udev_device_get_sysattr_value(expander_sasdev, - "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - } - - format_lun_number(parent, &lun); - if (sas_address) - path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun); - else - path_prepend(path, "sas-phy%s-%s", phy_id, lun); - - free(lun); -out: - udev_device_unref(target_sasdev); - udev_device_unref(expander_sasdev); - udev_device_unref(port_sasdev); - return parent; -} - -static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *transportdev; - struct udev_device *sessiondev = NULL; - const char *target; - char *connname; - struct udev_device *conndev = NULL; - const char *addr; - const char *port; - char *lun = NULL; - - assert(parent); - assert(path); - - /* find iscsi session */ - transportdev = parent; - for (;;) { - transportdev = udev_device_get_parent(transportdev); - if (transportdev == NULL) - return NULL; - if (startswith(udev_device_get_sysname(transportdev), "session")) - break; - } - - /* find iscsi session device */ - sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); - if (sessiondev == NULL) - return NULL; - target = udev_device_get_sysattr_value(sessiondev, "targetname"); - if (target == NULL) { - parent = NULL; - goto out; - } - - if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { - parent = NULL; - goto out; - } - conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); - free(connname); - if (conndev == NULL) { - parent = NULL; - goto out; - } - addr = udev_device_get_sysattr_value(conndev, "persistent_address"); - port = udev_device_get_sysattr_value(conndev, "persistent_port"); - if (addr == NULL || port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); - free(lun); -out: - udev_device_unref(sessiondev); - udev_device_unref(conndev); - return parent; -} - -static struct udev_device *handle_scsi_ata(struct udev_device *parent, char **path) { - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *atadev; - const char *port_no; - - assert(parent); - assert(path); - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (!targetdev) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (!target_parent) - return NULL; - - atadev = udev_device_new_from_subsystem_sysname(udev, "ata_port", udev_device_get_sysname(target_parent)); - if (!atadev) - return NULL; - - port_no = udev_device_get_sysattr_value(atadev, "port_no"); - if (!port_no) { - parent = NULL; - goto out; - } - path_prepend(path, "ata-%s", port_no); -out: - udev_device_unref(atadev); - return parent; -} - -static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) { - struct udev_device *hostdev; - int host, bus, target, lun; - const char *name; - char *base; - char *pos; - DIR *dir; - struct dirent *dent; - int basenum; - - assert(parent); - assert(path); - - hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (hostdev == NULL) - return NULL; - - name = udev_device_get_sysname(parent); - if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) - return NULL; - - /* - * Rebase host offset to get the local relative number - * - * Note: This is by definition racy, unreliable and too simple. - * Please do not copy this model anywhere. It's just a left-over - * from the time we had no idea how things should look like in - * the end. - * - * Making assumptions about a global in-kernel counter and use - * that to calculate a local offset is a very broken concept. It - * can only work as long as things are in strict order. - * - * The kernel needs to export the instance/port number of a - * controller directly, without the need for rebase magic like - * this. Manual driver unbind/bind, parallel hotplug/unplug will - * get into the way of this "I hope it works" logic. - */ - basenum = -1; - base = strdup(udev_device_get_syspath(hostdev)); - if (base == NULL) - return NULL; - pos = strrchr(base, '/'); - if (pos == NULL) { - parent = NULL; - goto out; - } - pos[0] = '\0'; - dir = opendir(base); - if (dir == NULL) { - parent = NULL; - goto out; - } - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *rest; - int i; - - if (dent->d_name[0] == '.') - continue; - if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) - continue; - if (!startswith(dent->d_name, "host")) - continue; - i = strtoul(&dent->d_name[4], &rest, 10); - if (rest[0] != '\0') - continue; - /* - * find the smallest number; the host really needs to export its - * own instance number per parent device; relying on the global host - * enumeration and plainly rebasing the numbers sounds unreliable - */ - if (basenum == -1 || i < basenum) - basenum = i; - } - closedir(dir); - if (basenum == -1) { - parent = NULL; - goto out; - } - host -= basenum; - - path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); -out: - free(base); - return hostdev; -} - -static struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) { - struct udev_device *hostdev; - struct udev_device *vmbusdev; - const char *guid_str; - char *lun = NULL; - char guid[38]; - size_t i, k; - - assert(parent); - assert(path); - - hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (!hostdev) - return NULL; - - vmbusdev = udev_device_get_parent(hostdev); - if (!vmbusdev) - return NULL; - - guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id"); - if (!guid_str) - return NULL; - - if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}') - return NULL; - - for (i = 1, k = 0; i < 36; i++) { - if (guid_str[i] == '-') - continue; - guid[k++] = guid_str[i]; - } - guid[k] = '\0'; - - format_lun_number(parent, &lun); - path_prepend(path, "vmbus-%s-%s", guid, lun); - free(lun); - return parent; -} - -static struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) { - const char *devtype; - const char *name; - const char *id; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL || !streq(devtype, "scsi_device")) - return parent; - - /* firewire */ - id = udev_device_get_sysattr_value(parent, "ieee1394_id"); - if (id != NULL) { - parent = skip_subsystem(parent, "scsi"); - path_prepend(path, "ieee1394-0x%s", id); - *supported_parent = true; - goto out; - } - - /* scsi sysfs does not have a "subsystem" for the transport */ - name = udev_device_get_syspath(parent); - - if (strstr(name, "/rport-") != NULL) { - parent = handle_scsi_fibre_channel(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/end_device-") != NULL) { - parent = handle_scsi_sas(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/session") != NULL) { - parent = handle_scsi_iscsi(parent, path); - *supported_parent = true; - goto out; - } - - if (strstr(name, "/ata") != NULL) { - parent = handle_scsi_ata(parent, path); - goto out; - } - - if (strstr(name, "/vmbus_") != NULL) { - parent = handle_scsi_hyperv(parent, path); - goto out; - } - - parent = handle_scsi_default(parent, path); -out: - return parent; -} - -static struct udev_device *handle_cciss(struct udev_device *parent, char **path) { - const char *str; - unsigned int controller, disk; - - str = udev_device_get_sysname(parent); - if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) - return NULL; - - path_prepend(path, "cciss-disk%u", disk); - parent = skip_subsystem(parent, "cciss"); - return parent; -} - -static void handle_scsi_tape(struct udev_device *dev, char **path) { - const char *name; - - /* must be the last device in the syspath */ - if (*path != NULL) - return; - - name = udev_device_get_sysname(dev); - if (startswith(name, "nst") && strchr("lma", name[3]) != NULL) - path_prepend(path, "nst%c", name[3]); - else if (startswith(name, "st") && strchr("lma", name[2]) != NULL) - path_prepend(path, "st%c", name[2]); -} - -static struct udev_device *handle_usb(struct udev_device *parent, char **path) { - const char *devtype; - const char *str; - const char *port; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL) - return parent; - if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device")) - return parent; - - str = udev_device_get_sysname(parent); - port = strchr(str, '-'); - if (port == NULL) - return parent; - port++; - - parent = skip_subsystem(parent, "usb"); - path_prepend(path, "usb-0:%s", port); - return parent; -} - -static struct udev_device *handle_bcma(struct udev_device *parent, char **path) { - const char *sysname; - unsigned int core; - - sysname = udev_device_get_sysname(parent); - if (sscanf(sysname, "bcma%*u:%u", &core) != 1) - return NULL; - - path_prepend(path, "bcma-%u", core); - return parent; -} - -/* 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(path); - - type = udev_device_get_sysattr_value(parent, "type"); - func = udev_device_get_sysattr_value(parent, "ap_functions"); - - 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, "ap"); - return parent; -} - -static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev_device *parent; - char *path = NULL; - bool supported_transport = false; - bool supported_parent = false; - - assert(dev); - - /* walk up the chain of devices and compose path */ - parent = dev; - while (parent != NULL) { - const char *subsys; - - subsys = udev_device_get_subsystem(parent); - if (subsys == NULL) { - ; - } else if (streq(subsys, "scsi_tape")) { - handle_scsi_tape(parent, &path); - } else if (streq(subsys, "scsi")) { - parent = handle_scsi(parent, &path, &supported_parent); - supported_transport = true; - } else if (streq(subsys, "cciss")) { - parent = handle_cciss(parent, &path); - supported_transport = true; - } else if (streq(subsys, "usb")) { - parent = handle_usb(parent, &path); - supported_transport = true; - } else if (streq(subsys, "bcma")) { - parent = handle_bcma(parent, &path); - supported_transport = true; - } else if (streq(subsys, "serio")) { - path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); - parent = skip_subsystem(parent, "serio"); - } else if (streq(subsys, "pci")) { - path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "pci"); - supported_parent = true; - } else if (streq(subsys, "platform")) { - path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "platform"); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "acpi")) { - path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "acpi"); - supported_parent = true; - } else if (streq(subsys, "xen")) { - path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "xen"); - supported_parent = true; - } else if (streq(subsys, "virtio")) { - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); - path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); - supported_transport = true; - supported_parent = true; - } else if (streq(subsys, "scm")) { - path_prepend(&path, "scm-%s", udev_device_get_sysname(parent)); - 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) - parent = udev_device_get_parent(parent); - } - - /* - * Do not return devices with an unknown parent device type. They - * might produce conflicting IDs if the parent does not provide a - * unique and predictable name. - */ - if (!supported_parent) - path = mfree(path); - - /* - * Do not return block devices without a well-known transport. Some - * devices do not expose their buses and do not provide a unique - * and predictable name that way. - */ - if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport) - path = mfree(path); - - if (path != NULL) { - char tag[UTIL_NAME_SIZE]; - size_t i; - const char *p; - - /* compose valid udev tag name */ - for (p = path, i = 0; *p; p++) { - if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z') || - *p == '-') { - tag[i++] = *p; - continue; - } - - /* skip all leading '_' */ - if (i == 0) - continue; - - /* avoid second '_' */ - if (tag[i-1] == '_') - continue; - - tag[i++] = '_'; - } - /* strip trailing '_' */ - while (i > 0 && tag[i-1] == '_') - i--; - tag[i] = '\0'; - - udev_builtin_add_property(dev, test, "ID_PATH", path); - udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); - free(path); - return EXIT_SUCCESS; - } - return EXIT_FAILURE; -} - -const struct udev_builtin udev_builtin_path_id = { - .name = "path_id", - .cmd = builtin_path_id, - .help = "Compose persistent device path", - .run_once = true, -}; diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c deleted file mode 100644 index 3ebe36f043..0000000000 --- a/src/udev/udev-builtin-uaccess.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * manage device node user ACL - * - * Copyright 2010-2012 Kay Sievers - * Copyright 2010 Lennart Poettering - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "sd-login.h" - -#include "login-util.h" -#include "logind-acl.h" -#include "udev.h" -#include "util.h" - -static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool test) { - int r; - const char *path = NULL, *seat; - bool changed_acl = false; - uid_t uid; - - umask(0022); - - /* don't muck around with ACLs when the system is not running systemd */ - if (!logind_running()) - return 0; - - path = udev_device_get_devnode(dev); - seat = udev_device_get_property_value(dev, "ID_SEAT"); - if (!seat) - seat = "seat0"; - - r = sd_seat_get_active(seat, NULL, &uid); - if (r == -ENXIO || r == -ENODATA) { - /* No active session on this seat */ - r = 0; - goto finish; - } else if (r < 0) { - log_error("Failed to determine active user on seat %s.", seat); - goto finish; - } - - r = devnode_acl(path, true, false, 0, true, uid); - if (r < 0) { - log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r, "Failed to apply ACL on %s: %m", path); - goto finish; - } - - changed_acl = true; - r = 0; - -finish: - if (path && !changed_acl) { - int k; - - /* Better be safe than sorry and reset ACL */ - k = devnode_acl(path, true, false, 0, false, 0); - if (k < 0) { - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, k, "Failed to apply ACL on %s: %m", path); - if (r >= 0) - r = k; - } - } - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_uaccess = { - .name = "uaccess", - .cmd = builtin_uaccess, - .help = "Manage device node user ACL", -}; diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c deleted file mode 100644 index 587649eff0..0000000000 --- a/src/udev/udev-builtin-usb_id.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * USB device properties and persistent device path - * - * Copyright (c) 2005 SUSE Linux Products GmbH, Germany - * Author: Hannes Reinecke - * - * Copyright (C) 2005-2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#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) { - const char *type = "generic"; - - switch (if_class_num) { - case 1: - type = "audio"; - break; - case 2: /* CDC-Control */ - break; - case 3: - type = "hid"; - break; - case 5: /* Physical */ - break; - case 6: - type = "media"; - break; - case 7: - type = "printer"; - break; - case 8: - type = "storage"; - break; - case 9: - type = "hub"; - break; - case 0x0a: /* CDC-Data */ - break; - case 0x0b: /* Chip/Smart Card */ - break; - case 0x0d: /* Content Security */ - break; - case 0x0e: - type = "video"; - break; - case 0xdc: /* Diagnostic Device */ - break; - case 0xe0: /* Wireless Controller */ - break; - case 0xfe: /* Application-specific */ - break; - case 0xff: /* Vendor-specific */ - break; - default: - break; - } - strncpy(to, type, len); - to[len-1] = '\0'; -} - -static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) { - int type_num = 0; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 1: /* RBC devices */ - type = "rbc"; - break; - case 2: - type = "atapi"; - break; - case 3: - type = "tape"; - break; - case 4: /* UFI */ - type = "floppy"; - break; - case 6: /* Transparent SPC-2 devices */ - type = "scsi"; - break; - default: - break; - } - } - strscpy(to, len, type); - return type_num; -} - -static void set_scsi_type(char *to, const char *from, size_t len) { - int type_num; - char *eptr; - const char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - case 0xe: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - case 7: - case 0xf: - type = "optical"; - break; - case 5: - type = "cd"; - break; - default: - break; - } - } - strscpy(to, len, type); -} - -#define USB_DT_DEVICE 0x01 -#define USB_DT_INTERFACE 0x04 - -static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) { - _cleanup_free_ char *filename = NULL; - _cleanup_close_ int fd = -1; - ssize_t size; - unsigned char buf[18 + 65535]; - size_t pos = 0; - unsigned strpos = 0; - struct usb_interface_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bNumEndpoints; - uint8_t bInterfaceClass; - uint8_t bInterfaceSubClass; - uint8_t bInterfaceProtocol; - uint8_t iInterface; - } _packed_; - - if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) - return log_oom(); - - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_debug_errno(errno, "Error opening USB device 'descriptors' file: %m"); - - size = read(fd, buf, sizeof(buf)); - if (size < 18 || size == sizeof(buf)) - return -EIO; - - ifs_str[0] = '\0'; - while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size && - strpos + 7 < len - 2) { - - struct usb_interface_descriptor *desc; - char if_str[8]; - - desc = (struct usb_interface_descriptor *) &buf[pos]; - if (desc->bLength < 3) - break; - pos += desc->bLength; - - if (desc->bDescriptorType != USB_DT_INTERFACE) - continue; - - if (snprintf(if_str, 8, ":%02x%02x%02x", - desc->bInterfaceClass, - desc->bInterfaceSubClass, - desc->bInterfaceProtocol) != 7) - continue; - - if (strstr(ifs_str, if_str) != NULL) - continue; - - memcpy(&ifs_str[strpos], if_str, 8), - strpos += 7; - } - - if (strpos > 0) { - ifs_str[strpos++] = ':'; - ifs_str[strpos++] = '\0'; - } - - return 0; -} - -/* - * A unique USB identification is generated like this: - * - * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass - * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC', - * use the SCSI vendor and model as USB-Vendor and USB-model. - * 3.) Otherwise, use the USB manufacturer and product as - * USB-Vendor and USB-model. Any non-printable characters - * in those strings will be skipped; a slash '/' will be converted - * into a full stop '.'. - * 4.) If that fails, too, we will use idVendor and idProduct - * as USB-Vendor and USB-model. - * 5.) The USB identification is the USB-vendor and USB-model - * string concatenated with an underscore '_'. - * 6.) If the device supplies a serial number, this number - * is concatenated with the identification with an underscore '_'. - */ -static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) { - char vendor_str[64] = ""; - char vendor_str_enc[256]; - const char *vendor_id; - char model_str[64] = ""; - char model_str_enc[256]; - const char *product_id; - char serial_str[UTIL_NAME_SIZE] = ""; - char packed_if_str[UTIL_NAME_SIZE] = ""; - char revision_str[64] = ""; - char type_str[64] = ""; - char instance_str[64] = ""; - const char *ifnum = NULL; - const char *driver = NULL; - char serial[256]; - - struct udev_device *dev_interface = NULL; - struct udev_device *dev_usb = NULL; - const char *if_class, *if_subclass; - int if_class_num; - int protocol = 0; - size_t l; - char *s; - - assert(dev); - - /* shortcut, if we are called directly for a "usb_device" type */ - if (udev_device_get_devtype(dev) != NULL && streq(udev_device_get_devtype(dev), "usb_device")) { - dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); - dev_usb = dev; - goto fallback; - } - - /* usb interface directory */ - dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); - if (dev_interface == NULL) { - log_debug("unable to access usb_interface device of '%s'", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); - driver = udev_device_get_sysattr_value(dev_interface, "driver"); - - if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); - if (!if_class) { - log_debug("%s: cannot get bInterfaceClass attribute", - udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - if_class_num = strtoul(if_class, NULL, 16); - if (if_class_num == 8) { - /* mass storage */ - if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); - if (if_subclass != NULL) - protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); - } else { - set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); - } - - log_debug("%s: if_class %d protocol %d", - udev_device_get_syspath(dev_interface), if_class_num, protocol); - - /* usb device directory */ - dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); - if (!dev_usb) { - log_debug("unable to find parent 'usb' device of '%s'", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - /* all interfaces of the device in a single string */ - dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); - - /* mass storage : SCSI or ATAPI */ - if (protocol == 6 || protocol == 2) { - struct udev_device *dev_scsi; - const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; - int host, bus, target, lun; - - /* get scsi device */ - dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (dev_scsi == NULL) { - log_debug("unable to find parent 'scsi' device of '%s'", - udev_device_get_syspath(dev)); - goto fallback; - } - if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { - log_debug("invalid scsi device '%s'", udev_device_get_sysname(dev_scsi)); - goto fallback; - } - - /* Generic SPC-2 device */ - scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); - if (!scsi_vendor) { - log_debug("%s: cannot get SCSI vendor attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - - scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); - if (!scsi_model) { - log_debug("%s: cannot get SCSI model attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - - scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); - if (!scsi_type) { - log_debug("%s: cannot get SCSI type attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); - - scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); - if (!scsi_rev) { - log_debug("%s: cannot get SCSI revision attribute", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - - /* - * some broken devices have the same identifiers - * for all luns, export the target:lun number - */ - sprintf(instance_str, "%d:%d", target, lun); - } - -fallback: - vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); - product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); - - /* fallback to USB vendor & device */ - if (vendor_str[0] == '\0') { - const char *usb_vendor = NULL; - - usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); - if (!usb_vendor) - usb_vendor = vendor_id; - if (!usb_vendor) { - log_debug("No USB vendor information available"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - } - - if (model_str[0] == '\0') { - const char *usb_model = NULL; - - usb_model = udev_device_get_sysattr_value(dev_usb, "product"); - if (!usb_model) - usb_model = product_id; - if (!usb_model) - return EXIT_FAILURE; - udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - } - - if (revision_str[0] == '\0') { - const char *usb_rev; - - usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); - if (usb_rev) { - util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - } - } - - if (serial_str[0] == '\0') { - const char *usb_serial; - - usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); - if (usb_serial) { - const unsigned char *p; - - /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */ - for (p = (unsigned char *)usb_serial; *p != '\0'; p++) - if (*p < 0x20 || *p > 0x7f || *p == ',') { - usb_serial = NULL; - break; - } - } - - if (usb_serial) { - util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); - util_replace_chars(serial_str, NULL); - } - } - - s = serial; - l = strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); - if (!isempty(serial_str)) - l = strpcpyl(&s, l, "_", serial_str, NULL); - - if (!isempty(instance_str)) - strpcpyl(&s, l, "-", instance_str, NULL); - - udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); - udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); - udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); - udev_builtin_add_property(dev, test, "ID_MODEL", model_str); - udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); - udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); - udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); - udev_builtin_add_property(dev, test, "ID_SERIAL", serial); - if (!isempty(serial_str)) - udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); - if (!isempty(type_str)) - udev_builtin_add_property(dev, test, "ID_TYPE", type_str); - if (!isempty(instance_str)) - udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); - udev_builtin_add_property(dev, test, "ID_BUS", "usb"); - if (!isempty(packed_if_str)) - udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); - if (ifnum != NULL) - udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); - if (driver != NULL) - udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_usb_id = { - .name = "usb_id", - .cmd = builtin_usb_id, - .help = "USB device properties", - .run_once = true, -}; diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c deleted file mode 100644 index e6b36f124f..0000000000 --- a/src/udev/udev-builtin.c +++ /dev/null @@ -1,142 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2007-2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include "string-util.h" -#include "udev.h" - -static bool initialized; - -static const struct udev_builtin *builtins[] = { -#ifdef HAVE_BLKID - [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, -#endif - [UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs, - [UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb, - [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, - [UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard, -#ifdef HAVE_KMOD - [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, -#endif - [UDEV_BUILTIN_NET_ID] = &udev_builtin_net_id, - [UDEV_BUILTIN_NET_LINK] = &udev_builtin_net_setup_link, - [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, - [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, -#ifdef HAVE_ACL - [UDEV_BUILTIN_UACCESS] = &udev_builtin_uaccess, -#endif -}; - -void udev_builtin_init(struct udev *udev) { - unsigned int i; - - if (initialized) - return; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->init) - builtins[i]->init(udev); - - initialized = true; -} - -void udev_builtin_exit(struct udev *udev) { - unsigned int i; - - if (!initialized) - return; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->exit) - builtins[i]->exit(udev); - - initialized = false; -} - -bool udev_builtin_validate(struct udev *udev) { - unsigned int i; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && builtins[i]->validate && builtins[i]->validate(udev)) - return true; - return false; -} - -void udev_builtin_list(struct udev *udev) { - unsigned int i; - - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i]) - fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help); -} - -const char *udev_builtin_name(enum udev_builtin_cmd cmd) { - if (!builtins[cmd]) - return NULL; - - return builtins[cmd]->name; -} - -bool udev_builtin_run_once(enum udev_builtin_cmd cmd) { - if (!builtins[cmd]) - return false; - - return builtins[cmd]->run_once; -} - -enum udev_builtin_cmd udev_builtin_lookup(const char *command) { - char name[UTIL_PATH_SIZE]; - enum udev_builtin_cmd i; - char *pos; - - strscpy(name, sizeof(name), command); - pos = strchr(name, ' '); - if (pos) - pos[0] = '\0'; - for (i = 0; i < ELEMENTSOF(builtins); i++) - if (builtins[i] && streq(builtins[i]->name, name)) - return i; - return UDEV_BUILTIN_MAX; -} - -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) { - char arg[UTIL_PATH_SIZE]; - int argc; - char *argv[128]; - - if (!builtins[cmd]) - return -EOPNOTSUPP; - - /* we need '0' here to reset the internal state */ - optind = 0; - strscpy(arg, sizeof(arg), command); - udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); - return builtins[cmd]->cmd(dev, argc, argv, test); -} - -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) { - udev_device_add_property(dev, key, val); - - if (test) - printf("%s=%s\n", key, val); - return 0; -} diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c deleted file mode 100644 index f68a09d7a8..0000000000 --- a/src/udev/udev-ctrl.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "socket-util.h" -#include "udev.h" - -/* wire protocol magic must match */ -#define UDEV_CTRL_MAGIC 0xdead1dea - -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - -struct udev_ctrl_msg_wire { - char version[16]; - unsigned int magic; - enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - int refcount; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; -}; - -struct udev_ctrl { - int refcount; - struct udev *udev; - int sock; - union sockaddr_union saddr; - socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; -}; - -struct udev_ctrl_connection { - int refcount; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { - struct udev_ctrl *uctrl; - const int on = 1; - int r; - - uctrl = new0(struct udev_ctrl, 1); - if (uctrl == NULL) - return NULL; - uctrl->refcount = 1; - uctrl->udev = udev; - - if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - log_error_errno(errno, "error getting socket: %m"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; - } - - /* - * FIXME: remove it as soon as we can depend on this: - * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949 - */ - r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (r < 0) - log_warning_errno(errno, "could not set SO_PASSCRED: %m"); - - uctrl->saddr.un.sun_family = AF_LOCAL; - strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control"); - uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); - return uctrl; -} - -struct udev_ctrl *udev_ctrl_new(struct udev *udev) { - return udev_ctrl_new_from_fd(udev, -1); -} - -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { - int err; - - if (!uctrl->bound) { - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - unlink(uctrl->saddr.un.sun_path); - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - } - - if (err < 0) - return log_error_errno(errno, "bind failed: %m"); - - err = listen(uctrl->sock, 0); - if (err < 0) - return log_error_errno(errno, "listen failed: %m"); - - uctrl->bound = true; - uctrl->cleanup_socket = true; - } - return 0; -} - -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) { - return uctrl->udev; -} - -static struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) { - if (uctrl) - uctrl->refcount++; - - return uctrl; -} - -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) { - if (uctrl && -- uctrl->refcount == 0) { - if (uctrl->sock >= 0) - close(uctrl->sock); - free(uctrl); - } - - return NULL; -} - -int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { - if (uctrl == NULL) - return 0; - if (uctrl->cleanup_socket) - unlink(uctrl->saddr.un.sun_path); - return 0; -} - -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) { - if (uctrl == NULL) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { - struct udev_ctrl_connection *conn; - struct ucred ucred = {}; - const int on = 1; - int r; - - conn = new(struct udev_ctrl_connection, 1); - if (conn == NULL) - return NULL; - conn->refcount = 1; - conn->uctrl = uctrl; - - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - log_error_errno(errno, "unable to receive ctrl connection: %m"); - goto err; - } - - /* check peer credential of connection */ - r = getpeercred(conn->sock, &ucred); - if (r < 0) { - log_error_errno(r, "unable to receive credentials of ctrl connection: %m"); - goto err; - } - if (ucred.uid > 0) { - log_error("sender uid="UID_FMT", message ignored", ucred.uid); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (r < 0) - log_warning_errno(errno, "could not set SO_PASSCRED: %m"); - - udev_ctrl_ref(uctrl); - return conn; -err: - if (conn->sock >= 0) - close(conn->sock); - free(conn); - return NULL; -} - -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) { - if (conn == NULL) - return NULL; - conn->refcount++; - return conn; -} - -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) { - if (conn && -- conn->refcount == 0) { - if (conn->sock >= 0) - close(conn->sock); - - udev_ctrl_unref(conn->uctrl); - - free(conn); - } - - return NULL; -} - -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) { - struct udev_ctrl_msg_wire ctrl_msg_wire; - int err = 0; - - memzero(&ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); - strcpy(ctrl_msg_wire.version, "udev-" VERSION); - ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; - ctrl_msg_wire.type = type; - - if (buf != NULL) - strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) { - err = -errno; - goto out; - } - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { - err = -errno; - goto out; - } - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = uctrl->sock; - pfd[0].events = POLLIN; - r = poll(pfd, 1, timeout * MSEC_PER_SEC); - if (r < 0) { - if (errno == EINTR) - continue; - err = -errno; - break; - } - - if (r > 0 && pfd[0].revents & POLLERR) { - err = -EIO; - break; - } - - if (r == 0) - err = -ETIMEDOUT; - break; - } -out: - return err; -} - -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} - -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) { - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct cmsghdr *cmsg; - struct iovec iov; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - struct msghdr smsg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cred_msg, - .msg_controllen = sizeof(cred_msg), - }; - struct ucred *cred; - - uctrl_msg = new0(struct udev_ctrl_msg, 1); - if (uctrl_msg == NULL) - return NULL; - uctrl_msg->refcount = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); - - /* wait for the incoming message */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; - - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - log_error("timeout waiting for ctrl message"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - log_error_errno(errno, "ctrl connection error: %m"); - goto err; - } - } - - break; - } - - iov.iov_base = &uctrl_msg->ctrl_msg_wire; - iov.iov_len = sizeof(struct udev_ctrl_msg_wire); - - size = recvmsg(conn->sock, &smsg, 0); - if (size < 0) { - log_error_errno(errno, "unable to receive ctrl message: %m"); - goto err; - } - - cmsg_close_all(&smsg); - - cmsg = CMSG_FIRSTHDR(&smsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - log_error("no sender credentials received, message ignored"); - goto err; - } - - cred = (struct ucred *) CMSG_DATA(cmsg); - - if (cred->uid != 0) { - log_error("sender uid="UID_FMT", message ignored", cred->uid); - goto err; - } - - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - log_error("message magic 0x%08x doesn't match, ignore it", uctrl_msg->ctrl_msg_wire.magic); - goto err; - } - - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; -} - -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg && -- ctrl_msg->refcount == 0) { - udev_ctrl_connection_unref(ctrl_msg->conn); - free(ctrl_msg); - } - - return NULL; -} - -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} - -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} - -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} - -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; -} diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c deleted file mode 100644 index 54cd741bb1..0000000000 --- a/src/udev/udev-event.c +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (C) 2003-2013 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 { - const char *cmd; - pid_t pid; - usec_t timeout_warn; - usec_t timeout; - bool accept_failure; -} Spawn; - -struct udev_event *udev_event_new(struct udev_device *dev) { - struct udev *udev = udev_device_get_udev(dev); - struct udev_event *event; - - event = new0(struct udev_event, 1); - if (event == NULL) - return NULL; - event->dev = dev; - event->udev = udev; - udev_list_init(udev, &event->run_list, false); - udev_list_init(udev, &event->seclabel_list, false); - event->birth_usec = clock_boottime_or_monotonic(); - return event; -} - -void udev_event_unref(struct udev_event *event) { - if (event == NULL) - return; - sd_netlink_unref(event->rtnl); - udev_list_cleanup(&event->run_list); - udev_list_cleanup(&event->seclabel_list); - free(event->program_result); - free(event->name); - free(event); -} - -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) { - struct udev_device *dev = event->dev; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVNODE, - SUBST_ATTR, - SUBST_ENV, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_DRIVER, - SUBST_DEVPATH, - SUBST_ID, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_PARENT, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - }; - static const struct subst_map { - const char *name; - const char fmt; - enum subst_type type; - } map[] = { - { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - }; - const char *from; - char *s; - size_t l; - - assert(dev); - - from = src; - s = dest; - l = size; - - for (;;) { - enum subst_type type = SUBST_UNKNOWN; - char attrbuf[UTIL_PATH_SIZE]; - char *attr = NULL; - - while (from[0] != '\0') { - if (from[0] == '$') { - /* substitute named variable */ - unsigned int i; - - if (from[1] == '$') { - from++; - goto copy; - } - - for (i = 0; i < ELEMENTSOF(map); i++) { - if (startswith(&from[1], map[i].name)) { - type = map[i].type; - from += strlen(map[i].name)+1; - goto subst; - } - } - } else if (from[0] == '%') { - /* substitute format char */ - unsigned int i; - - if (from[1] == '%') { - from++; - goto copy; - } - - for (i = 0; i < ELEMENTSOF(map); i++) { - if (from[1] == map[i].fmt) { - type = map[i].type; - from += 2; - goto subst; - } - } - } -copy: - /* copy char */ - if (l == 0) - goto out; - s[0] = from[0]; - from++; - s++; - l--; - } - - goto out; -subst: - /* extract possible $format{attr} */ - if (from[0] == '{') { - unsigned int i; - - from++; - for (i = 0; from[i] != '}'; i++) { - if (from[i] == '\0') { - log_error("missing closing brace for format '%s'", src); - goto out; - } - } - if (i >= sizeof(attrbuf)) - goto out; - memcpy(attrbuf, from, i); - attrbuf[i] = '\0'; - from += i+1; - attr = attrbuf; - } else { - attr = NULL; - } - - switch (type) { - case SUBST_DEVPATH: - l = strpcpy(&s, l, udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); - break; - case SUBST_DRIVER: { - const char *driver; - - if (event->dev_parent == NULL) - break; - - driver = udev_device_get_driver(event->dev_parent); - if (driver == NULL) - break; - l = strpcpy(&s, l, driver); - break; - } - case SUBST_MAJOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", major(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_MINOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", minor(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_RESULT: { - char *rest; - int i; - - if (event->program_result == NULL) - break; - /* get part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - char result[UTIL_PATH_SIZE]; - char tmp[UTIL_PATH_SIZE]; - char *cpos; - - strscpy(result, sizeof(result), event->program_result); - cpos = result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - if (cpos[0] == '\0') - break; - } - if (i > 0) { - log_error("requested part of result string not found"); - break; - } - strscpy(tmp, sizeof(tmp), cpos); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(tmp, ' '); - if (cpos) - cpos[0] = '\0'; - } - l = strpcpy(&s, l, tmp); - } else { - l = strpcpy(&s, l, event->program_result); - } - break; - } - case SUBST_ATTR: { - const char *value = NULL; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - int count; - - if (attr == NULL) { - log_error("missing file parameter for attr"); - break; - } - - /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) - value = vbuf; - - /* try to read the attribute the device */ - if (value == NULL) - value = udev_device_get_sysattr_value(event->dev, attr); - - /* try to read the attribute of the parent device, other matches have selected */ - if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) - value = udev_device_get_sysattr_value(event->dev_parent, attr); - - if (value == NULL) - break; - - /* strip trailing whitespace, and replace unwanted characters */ - if (value != vbuf) - strscpy(vbuf, sizeof(vbuf), value); - len = strlen(vbuf); - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - log_debug("%i character(s) replaced" , count); - l = strpcpy(&s, l, vbuf); - break; - } - case SUBST_PARENT: { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) - l = strpcpy(&s, l, devnode + strlen("/dev/")); - break; - } - case SUBST_DEVNODE: - if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev)); - break; - case SUBST_NAME: - if (event->name != NULL) - l = strpcpy(&s, l, event->name); - else if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); - else - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_LINKS: { - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_devlinks_list_entry(dev); - if (list_entry == NULL) - break; - l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); - break; - } - case SUBST_ROOT: - l = strpcpy(&s, l, "/dev"); - break; - case SUBST_SYS: - l = strpcpy(&s, l, "/sys"); - break; - case SUBST_ENV: - if (attr == NULL) { - break; - } else { - const char *value; - - value = udev_device_get_property_value(event->dev, attr); - if (value == NULL) - break; - l = strpcpy(&s, l, value); - break; - } - default: - log_error("unknown substitution type=%i", type); - break; - } - } - -out: - s[0] = '\0'; - return l; -} - -static int spawn_exec(struct udev_event *event, - const char *cmd, char *const argv[], char **envp, - int fd_stdout, int fd_stderr) { - _cleanup_close_ int fd = -1; - int r; - - /* discard child output or connect to pipe */ - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - r = dup2(fd, STDIN_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdin failed: %m"); - - if (fd_stdout < 0) { - r = dup2(fd, STDOUT_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - } - - if (fd_stderr < 0) { - r = dup2(fd, STDERR_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stderr failed: %m"); - } - } else - log_warning_errno(errno, "open /dev/null failed: %m"); - - /* connect pipes to std{out,err} */ - if (fd_stdout >= 0) { - r = dup2(fd_stdout, STDOUT_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - - fd_stdout = safe_close(fd_stdout); - } - - if (fd_stderr >= 0) { - r = dup2(fd_stderr, STDERR_FILENO); - if (r < 0) - log_warning_errno(errno, "redirecting stdout failed: %m"); - - fd_stderr = safe_close(fd_stderr); - } - - /* terminate child in case parent goes away */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* restore sigmask before exec */ - (void) reset_signal_mask(); - - execve(argv[0], argv, envp); - - /* exec failed */ - return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); -} - -static void spawn_read(struct udev_event *event, - usec_t timeout_usec, - const char *cmd, - int fd_stdout, int fd_stderr, - char *result, size_t ressize) { - _cleanup_close_ int fd_ep = -1; - struct epoll_event ep_outpipe = { - .events = EPOLLIN, - .data.ptr = &fd_stdout, - }; - struct epoll_event ep_errpipe = { - .events = EPOLLIN, - .data.ptr = &fd_stderr, - }; - size_t respos = 0; - int r; - - /* read from child if requested */ - if (fd_stdout < 0 && fd_stderr < 0) - return; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - log_error_errno(errno, "error creating epoll fd: %m"); - return; - } - - if (fd_stdout >= 0) { - r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); - if (r < 0) { - log_error_errno(errno, "fail to add stdout fd to epoll: %m"); - return; - } - } - - if (fd_stderr >= 0) { - r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); - if (r < 0) { - log_error_errno(errno, "fail to add stderr fd to epoll: %m"); - return; - } - } - - /* read child output */ - while (fd_stdout >= 0 || fd_stderr >= 0) { - int timeout; - int fdcount; - struct epoll_event ev[4]; - int i; - - if (timeout_usec > 0) { - usec_t age_usec; - - age_usec = clock_boottime_or_monotonic() - event->birth_usec; - if (age_usec >= timeout_usec) { - log_error("timeout '%s'", cmd); - return; - } - timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC; - } else { - timeout = -1; - } - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - log_error_errno(errno, "failed to poll: %m"); - return; - } else if (fdcount == 0) { - log_error("timeout '%s'", cmd); - return; - } - - for (i = 0; i < fdcount; i++) { - int *fd = (int *)ev[i].data.ptr; - - if (*fd < 0) - continue; - - if (ev[i].events & EPOLLIN) { - ssize_t count; - char buf[4096]; - - count = read(*fd, buf, sizeof(buf)-1); - if (count <= 0) - continue; - buf[count] = '\0'; - - /* store stdout result */ - if (result != NULL && *fd == fd_stdout) { - if (respos + count < ressize) { - memcpy(&result[respos], buf, count); - respos += count; - } else { - log_error("'%s' ressize %zu too short", cmd, ressize); - } - } - - /* log debug output only if we watch stderr */ - if (fd_stderr >= 0) { - char *pos; - char *line; - - pos = buf; - while ((line = strsep(&pos, "\n"))) { - if (pos != NULL || line[0] != '\0') - log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line); - } - } - } else if (ev[i].events & EPOLLHUP) { - r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); - if (r < 0) { - log_error_errno(errno, "failed to remove fd from epoll: %m"); - return; - } - *fd = -1; - } - } - } - - /* return the child's stdout string */ - if (result != NULL) - result[respos] = '\0'; -} - -static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - Spawn *spawn = userdata; - char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; - - assert(spawn); - - kill_and_sigcont(spawn->pid, SIGKILL); - - log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, - format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); - - return 1; -} - -static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { - Spawn *spawn = userdata; - char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; - - assert(spawn); - - log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid, - format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); - - return 1; -} - -static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) { - Spawn *spawn = userdata; - - assert(spawn); - - switch (si->si_code) { - case CLD_EXITED: - if (si->si_status == 0) { - log_debug("Process '%s' succeeded.", spawn->cmd); - sd_event_exit(sd_event_source_get_event(s), 0); - - return 1; - } else if (spawn->accept_failure) - log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); - else - log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); - - break; - case CLD_KILLED: - case CLD_DUMPED: - log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status)); - - break; - default: - log_error("Process '%s' failed due to unknown reason.", spawn->cmd); - } - - sd_event_exit(sd_event_source_get_event(s), -EIO); - - return 1; -} - -static int spawn_wait(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - const char *cmd, pid_t pid, - bool accept_failure) { - Spawn spawn = { - .cmd = cmd, - .pid = pid, - .accept_failure = accept_failure, - }; - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - int r, ret; - - r = sd_event_new(&e); - if (r < 0) - return r; - - if (timeout_usec > 0) { - usec_t usec, age_usec; - - usec = now(clock_boottime_or_monotonic()); - age_usec = usec - event->birth_usec; - if (age_usec < timeout_usec) { - if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) { - spawn.timeout_warn = timeout_warn_usec - age_usec; - - r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), - usec + spawn.timeout_warn, USEC_PER_SEC, - on_spawn_timeout_warning, &spawn); - if (r < 0) - return r; - } - - spawn.timeout = timeout_usec - age_usec; - - r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), - usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn); - if (r < 0) - return r; - } - } - - r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn); - if (r < 0) - return r; - - r = sd_event_loop(e); - if (r < 0) - return r; - - r = sd_event_get_exit_code(e, &ret); - if (r < 0) - return r; - - return ret; -} - -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) { - int i = 0; - char *pos; - - if (strchr(cmd, ' ') == NULL) { - argv[i++] = cmd; - goto out; - } - - pos = cmd; - while (pos != NULL && pos[0] != '\0') { - if (pos[0] == '\'') { - /* do not separate quotes */ - pos++; - argv[i] = strsep(&pos, "\'"); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } - i++; - } -out: - argv[i] = NULL; - if (argc) - *argc = i; - return 0; -} - -int udev_event_spawn(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - bool accept_failure, - const char *cmd, - char *result, size_t ressize) { - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - int err = 0; - - /* pipes from child to parent */ - if (result != NULL || log_get_max_level() >= LOG_INFO) { - if (pipe2(outpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } - } - if (log_get_max_level() >= LOG_INFO) { - if (pipe2(errpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } - } - - pid = fork(); - switch(pid) { - case 0: - { - char arg[UTIL_PATH_SIZE]; - char *argv[128]; - char program[UTIL_PATH_SIZE]; - - /* child closes parent's ends of pipes */ - outpipe[READ_END] = safe_close(outpipe[READ_END]); - errpipe[READ_END] = safe_close(errpipe[READ_END]); - - strscpy(arg, sizeof(arg), cmd); - udev_build_argv(event->udev, arg, NULL, argv); - - /* allow programs in /usr/lib/udev/ to be called without the path */ - if (argv[0][0] != '/') { - strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); - argv[0] = program; - } - - log_debug("starting '%s'", cmd); - - spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev), - outpipe[WRITE_END], errpipe[WRITE_END]); - - _exit(2); - } - case -1: - log_error_errno(errno, "fork of '%s' failed: %m", cmd); - err = -1; - goto out; - default: - /* parent closed child's ends of pipes */ - outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); - errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); - - spawn_read(event, - timeout_usec, - cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); - - err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); - } - -out: - if (outpipe[READ_END] >= 0) - close(outpipe[READ_END]); - if (outpipe[WRITE_END] >= 0) - close(outpipe[WRITE_END]); - if (errpipe[READ_END] >= 0) - close(errpipe[READ_END]); - if (errpipe[WRITE_END] >= 0) - close(errpipe[WRITE_END]); - return err; -} - -static int rename_netif(struct udev_event *event) { - struct udev_device *dev = event->dev; - char name[IFNAMSIZ]; - const char *oldname; - int r; - - oldname = udev_device_get_sysname(dev); - - strscpy(name, IFNAMSIZ, event->name); - - r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); - if (r < 0) - return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); - - log_debug("renamed network interface '%s' to '%s'", oldname, name); - - return 0; -} - -void udev_event_execute_rules(struct udev_event *event, - usec_t timeout_usec, usec_t timeout_warn_usec, - struct udev_list *properties_list, - struct udev_rules *rules) { - struct udev_device *dev = event->dev; - - if (udev_device_get_subsystem(dev) == NULL) - return; - - if (streq(udev_device_get_action(dev), "remove")) { - udev_device_read_db(dev); - udev_device_tag_index(dev, NULL, false); - udev_device_delete_db(dev); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_watch_end(event->udev, dev); - - udev_rules_apply_to_event(rules, event, - timeout_usec, timeout_warn_usec, - properties_list); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_node_remove(dev); - } else { - event->dev_db = udev_device_clone_with_db(dev); - if (event->dev_db != NULL) { - /* 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); - } - - udev_rules_apply_to_event(rules, event, - timeout_usec, timeout_warn_usec, - properties_list); - - /* rename a new network interface, if needed */ - if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && - event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { - int r; - - r = rename_netif(event); - if (r < 0) - log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev), - udev_device_get_sysname(dev), event->name); - else { - r = udev_device_rename(dev, event->name); - if (r < 0) - log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m", - udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name); - else - log_debug("changed devpath to '%s'", udev_device_get_devpath(dev)); - } - } - - if (major(udev_device_get_devnum(dev)) > 0) { - bool apply; - - /* remove/update possible left-over symlinks from old database entry */ - if (event->dev_db != NULL) - udev_node_update_old_links(dev, event->dev_db); - - if (!event->owner_set) - event->uid = udev_device_get_devnode_uid(dev); - - if (!event->group_set) - event->gid = udev_device_get_devnode_gid(dev); - - if (!event->mode_set) { - if (udev_device_get_devnode_mode(dev) > 0) { - /* kernel supplied value */ - event->mode = udev_device_get_devnode_mode(dev); - } else if (event->gid > 0) { - /* default 0660 if a group is assigned */ - event->mode = 0660; - } else { - /* default 0600 */ - event->mode = 0600; - } - } - - apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; - udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); - } - - /* preserve old, or get new initialization timestamp */ - udev_device_ensure_usec_initialized(event->dev, event->dev_db); - - /* (re)write database file */ - udev_device_tag_index(dev, event->dev_db, true); - udev_device_update_db(dev); - udev_device_set_is_initialized(dev); - - event->dev_db = udev_device_unref(event->dev_db); - } -} - -void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { - char command[UTIL_PATH_SIZE]; - const char *cmd = udev_list_entry_get_name(list_entry); - enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry); - - udev_event_apply_format(event, cmd, command, sizeof(command)); - - if (builtin_cmd < UDEV_BUILTIN_MAX) - udev_builtin_run(event->dev, builtin_cmd, command, false); - else { - if (event->exec_delay > 0) { - log_debug("delay execution of '%s'", command); - sleep(event->exec_delay); - } - - udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0); - } - } -} diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c deleted file mode 100644 index 5d2997fd8f..0000000000 --- a/src/udev/udev-node.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2003-2013 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; - char target[UTIL_PATH_SIZE]; - char *s; - size_t l; - char slink_tmp[UTIL_PATH_SIZE + 32]; - int i = 0; - int tail = 0; - int err = 0; - - /* use relative link */ - target[0] = '\0'; - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - s = target; - l = sizeof(target); - while (slink[i] != '\0') { - if (slink[i] == '/') - l = strpcpy(&s, l, "../"); - i++; - } - l = strscpy(s, l, &node[tail]); - if (l == 0) { - err = -EINVAL; - goto exit; - } - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node); - goto exit; - } else if (S_ISLNK(stats.st_mode)) { - char buf[UTIL_PATH_SIZE]; - int len; - - len = readlink(slink, buf, sizeof(buf)); - if (len > 0 && len < (int)sizeof(buf)) { - buf[len] = '\0'; - if (streq(target, buf)) { - log_debug("preserve already existing symlink '%s' to '%s'", slink, target); - label_fix(slink, true, false); - utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - goto exit; - } - } - } - } else { - log_debug("creating symlink '%s' to '%s'", slink, target); - do { - err = mkdir_parents_label(slink, 0755); - if (err != 0 && err != -ENOENT) - break; - mac_selinux_create_file_prepare(slink, S_IFLNK); - err = symlink(target, slink); - if (err != 0) - err = -errno; - mac_selinux_create_file_clear(); - } while (err == -ENOENT); - if (err == 0) - goto exit; - } - - log_debug("atomically replace '%s'", slink); - strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); - unlink(slink_tmp); - do { - err = mkdir_parents_label(slink_tmp, 0755); - if (err != 0 && err != -ENOENT) - break; - mac_selinux_create_file_prepare(slink_tmp, S_IFLNK); - err = symlink(target, slink_tmp); - if (err != 0) - err = -errno; - mac_selinux_create_file_clear(); - } while (err == -ENOENT); - if (err != 0) { - log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp); - goto exit; - } - err = rename(slink_tmp, slink); - if (err != 0) { - log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); - unlink(slink_tmp); - } -exit: - return err; -} - -/* find device node of device with highest priority */ -static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { - struct udev *udev = udev_device_get_udev(dev); - DIR *dir; - int priority = 0; - const char *target = NULL; - - if (add) { - priority = udev_device_get_devlink_priority(dev); - strscpy(buf, bufsize, udev_device_get_devnode(dev)); - target = buf; - } - - dir = opendir(stackdir); - if (dir == NULL) - return target; - for (;;) { - struct udev_device *dev_db; - struct dirent *dent; - - dent = readdir(dir); - if (dent == NULL || dent->d_name[0] == '\0') - break; - if (dent->d_name[0] == '.') - continue; - - log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); - - /* did we find ourself? */ - if (streq(dent->d_name, udev_device_get_id_filename(dev))) - continue; - - dev_db = udev_device_new_from_device_id(udev, dent->d_name); - if (dev_db != NULL) { - const char *devnode; - - devnode = udev_device_get_devnode(dev_db); - if (devnode != NULL) { - if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { - log_debug("'%s' claims priority %i for '%s'", - udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); - priority = udev_device_get_devlink_priority(dev_db); - strscpy(buf, bufsize, devnode); - target = buf; - } - } - udev_device_unref(dev_db); - } - } - closedir(dir); - return target; -} - -/* manage "stack of names" with possibly specified device priorities */ -static void link_update(struct udev_device *dev, const char *slink, bool add) { - char name_enc[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; - char dirname[UTIL_PATH_SIZE]; - const char *target; - char buf[UTIL_PATH_SIZE]; - - util_path_encode(slink + strlen("/dev"), name_enc, sizeof(name_enc)); - strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); - strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); - - if (!add && unlink(filename) == 0) - rmdir(dirname); - - target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); - if (target == NULL) { - log_debug("no reference left, remove '%s'", slink); - if (unlink(slink) == 0) - rmdir_parents(slink, "/"); - } else { - log_debug("creating link '%s' to '%s'", slink, target); - node_symlink(dev, target, slink); - } - - if (add) { - int err; - - do { - int fd; - - err = mkdir_parents(filename, 0755); - if (err != 0 && err != -ENOENT) - break; - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - else - err = -errno; - } while (err == -ENOENT); - } -} - -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { - struct udev_list_entry *list_entry; - - /* update possible left-over symlinks */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { - const char *name = udev_list_entry_get_name(list_entry); - struct udev_list_entry *list_entry_current; - int found; - - /* check if old link name still belongs to this device */ - found = 0; - udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { - const char *name_current = udev_list_entry_get_name(list_entry_current); - - if (streq(name, name_current)) { - found = 1; - break; - } - } - if (found) - continue; - - log_debug("update old name, '%s' no longer belonging to '%s'", - name, udev_device_get_devpath(dev)); - link_update(dev, name, false); - } -} - -static int node_permissions_apply(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list) { - const char *devnode = udev_device_get_devnode(dev); - dev_t devnum = udev_device_get_devnum(dev); - struct stat stats; - struct udev_list_entry *entry; - int err = 0; - - if (streq(udev_device_get_subsystem(dev), "block")) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (lstat(devnode, &stats) != 0) { - err = log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode); - goto out; - } - - if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { - err = -EEXIST; - log_debug("found node '%s' with non-matching devnum %s, skip handling", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); - goto out; - } - - if (apply) { - bool selinux = false; - bool smack = false; - - if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { - log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); - err = chmod(devnode, mode); - if (err < 0) - log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode); - err = chown(devnode, uid, gid); - if (err < 0) - log_warning_errno(errno, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid); - } else { - log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); - } - - /* apply SECLABEL{$module}=$label */ - udev_list_entry_foreach(entry, udev_list_get_entry(seclabel_list)) { - const char *name, *label; - int r; - - name = udev_list_entry_get_name(entry); - label = udev_list_entry_get_value(entry); - - if (streq(name, "selinux")) { - selinux = true; - - r = mac_selinux_apply(devnode, label); - if (r < 0) - log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label); - else - log_debug("SECLABEL: set SELinux label '%s'", label); - - } else if (streq(name, "smack")) { - smack = true; - - r = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label); - if (r < 0) - log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label); - else - log_debug("SECLABEL: set SMACK label '%s'", label); - - } else - log_error("SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label); - } - - /* set the defaults */ - if (!selinux) - mac_selinux_fix(devnode, true, false); - if (!smack) - mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL); - } - - /* always update timestamp when we re-use the node, like on media change events */ - utimensat(AT_FDCWD, devnode, NULL, 0); -out: - return err; -} - -void udev_node_add(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list) { - char filename[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - - log_debug("handling device node '%s', devnum=%s, mode=%#o, uid="UID_FMT", gid="GID_FMT, - udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); - - if (node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list) < 0) - return; - - /* always add /dev/{block,char}/$major:$minor */ - 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))); - node_symlink(dev, udev_device_get_devnode(dev), filename); - - /* create/update symlinks, add symlinks to name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), true); -} - -void udev_node_remove(struct udev_device *dev) { - struct udev_list_entry *list_entry; - char filename[UTIL_PATH_SIZE]; - - /* remove/update symlinks, remove symlinks from name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), false); - - /* remove /dev/{block,char}/$major:$minor */ - 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))); - unlink(filename); -} diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c deleted file mode 100644 index 26fa52cf6c..0000000000 --- a/src/udev/udev-rules.c +++ /dev/null @@ -1,2577 +0,0 @@ -/* - * Copyright (C) 2003-2012 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "escape.h" -#include "fd-util.h" -#include "fs-util.h" -#include "glob-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "strbuf.h" -#include "string-util.h" -#include "strv.h" -#include "sysctl-util.h" -#include "udev.h" -#include "user-util.h" -#include "util.h" - -#define PREALLOC_TOKEN 2048 - -struct uid_gid { - unsigned int name_off; - union { - uid_t uid; - gid_t gid; - }; -}; - -static const char* const rules_dirs[] = { - "/etc/udev/rules.d", - "/run/udev/rules.d", - UDEVLIBEXECDIR "/rules.d", - NULL -}; - -struct udev_rules { - struct udev *udev; - usec_t dirs_ts_usec; - int resolve_names; - - /* every key in the rules file becomes a token */ - struct token *tokens; - unsigned int token_cur; - unsigned int token_max; - - /* all key strings are copied and de-duplicated in a single continuous string buffer */ - struct strbuf *strbuf; - - /* during rule parsing, uid/gid lookup results are cached */ - struct uid_gid *uids; - unsigned int uids_cur; - unsigned int uids_max; - struct uid_gid *gids; - unsigned int gids_cur; - unsigned int gids_max; -}; - -static char *rules_str(struct udev_rules *rules, unsigned int off) { - return rules->strbuf->buf + off; -} - -static unsigned int rules_add_string(struct udev_rules *rules, const char *s) { - return strbuf_add_string(rules->strbuf, s, strlen(s)); -} - -/* KEY=="", KEY!="", KEY+="", KEY-="", KEY="", KEY:="" */ -enum operation_type { - OP_UNSET, - - OP_MATCH, - OP_NOMATCH, - OP_MATCH_MAX, - - OP_ADD, - OP_REMOVE, - OP_ASSIGN, - OP_ASSIGN_FINAL, -}; - -enum string_glob_type { - GL_UNSET, - GL_PLAIN, /* no special chars */ - GL_GLOB, /* shell globs ?,*,[] */ - GL_SPLIT, /* multi-value A|B */ - GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ - GL_SOMETHING, /* commonly used "?*" */ -}; - -enum string_subst_type { - SB_UNSET, - SB_NONE, - SB_FORMAT, - SB_SUBSYS, -}; - -/* tokens of a rule are sorted/handled in this order */ -enum token_type { - TK_UNSET, - TK_RULE, - - TK_M_ACTION, /* val */ - TK_M_DEVPATH, /* val */ - TK_M_KERNEL, /* val */ - TK_M_DEVLINK, /* val */ - TK_M_NAME, /* val */ - TK_M_ENV, /* val, attr */ - TK_M_TAG, /* val */ - TK_M_SUBSYSTEM, /* val */ - TK_M_DRIVER, /* val */ - TK_M_WAITFOR, /* val */ - TK_M_ATTR, /* val, attr */ - TK_M_SYSCTL, /* val, attr */ - - TK_M_PARENTS_MIN, - TK_M_KERNELS, /* val */ - TK_M_SUBSYSTEMS, /* val */ - TK_M_DRIVERS, /* val */ - TK_M_ATTRS, /* val, attr */ - TK_M_TAGS, /* val */ - TK_M_PARENTS_MAX, - - TK_M_TEST, /* val, mode_t */ - TK_M_PROGRAM, /* val */ - TK_M_IMPORT_FILE, /* val */ - TK_M_IMPORT_PROG, /* val */ - TK_M_IMPORT_BUILTIN, /* val */ - TK_M_IMPORT_DB, /* val */ - TK_M_IMPORT_CMDLINE, /* val */ - TK_M_IMPORT_PARENT, /* val */ - TK_M_RESULT, /* val */ - TK_M_MAX, - - TK_A_STRING_ESCAPE_NONE, - TK_A_STRING_ESCAPE_REPLACE, - TK_A_DB_PERSIST, - TK_A_INOTIFY_WATCH, /* int */ - TK_A_DEVLINK_PRIO, /* int */ - TK_A_OWNER, /* val */ - TK_A_GROUP, /* val */ - TK_A_MODE, /* val */ - TK_A_OWNER_ID, /* uid_t */ - TK_A_GROUP_ID, /* gid_t */ - TK_A_MODE_ID, /* mode_t */ - TK_A_TAG, /* val */ - TK_A_STATIC_NODE, /* val */ - TK_A_SECLABEL, /* val, attr */ - TK_A_ENV, /* val, attr */ - TK_A_NAME, /* val */ - TK_A_DEVLINK, /* val */ - TK_A_ATTR, /* val, attr */ - TK_A_SYSCTL, /* val, attr */ - TK_A_RUN_BUILTIN, /* val, bool */ - TK_A_RUN_PROGRAM, /* val, bool */ - TK_A_GOTO, /* size_t */ - - TK_END, -}; - -/* we try to pack stuff in a way that we take only 12 bytes per token */ -struct token { - union { - unsigned char type; /* same in rule and key */ - struct { - enum token_type type:8; - bool can_set_name:1; - bool has_static_node:1; - unsigned int unused:6; - unsigned short token_count; - unsigned int label_off; - unsigned short filename_off; - unsigned short filename_line; - } rule; - struct { - enum token_type type:8; - enum operation_type op:8; - enum string_glob_type glob:8; - enum string_subst_type subst:4; - enum string_subst_type attrsubst:4; - unsigned int value_off; - union { - unsigned int attr_off; - unsigned int rule_goto; - mode_t mode; - uid_t uid; - gid_t gid; - int devlink_prio; - int watch; - enum udev_builtin_cmd builtin_cmd; - }; - } key; - }; -}; - -#define MAX_TK 64 -struct rule_tmp { - struct udev_rules *rules; - struct token rule; - struct token token[MAX_TK]; - unsigned int token_cur; -}; - -#ifdef DEBUG -static const char *operation_str(enum operation_type type) { - static const char *operation_strs[] = { - [OP_UNSET] = "UNSET", - [OP_MATCH] = "match", - [OP_NOMATCH] = "nomatch", - [OP_MATCH_MAX] = "MATCH_MAX", - - [OP_ADD] = "add", - [OP_REMOVE] = "remove", - [OP_ASSIGN] = "assign", - [OP_ASSIGN_FINAL] = "assign-final", -} ; - - return operation_strs[type]; -} - -static const char *string_glob_str(enum string_glob_type type) { - static const char *string_glob_strs[] = { - [GL_UNSET] = "UNSET", - [GL_PLAIN] = "plain", - [GL_GLOB] = "glob", - [GL_SPLIT] = "split", - [GL_SPLIT_GLOB] = "split-glob", - [GL_SOMETHING] = "split-glob", - }; - - return string_glob_strs[type]; -} - -static const char *token_str(enum token_type type) { - static const char *token_strs[] = { - [TK_UNSET] = "UNSET", - [TK_RULE] = "RULE", - - [TK_M_ACTION] = "M ACTION", - [TK_M_DEVPATH] = "M DEVPATH", - [TK_M_KERNEL] = "M KERNEL", - [TK_M_DEVLINK] = "M DEVLINK", - [TK_M_NAME] = "M NAME", - [TK_M_ENV] = "M ENV", - [TK_M_TAG] = "M TAG", - [TK_M_SUBSYSTEM] = "M SUBSYSTEM", - [TK_M_DRIVER] = "M DRIVER", - [TK_M_WAITFOR] = "M WAITFOR", - [TK_M_ATTR] = "M ATTR", - [TK_M_SYSCTL] = "M SYSCTL", - - [TK_M_PARENTS_MIN] = "M PARENTS_MIN", - [TK_M_KERNELS] = "M KERNELS", - [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", - [TK_M_DRIVERS] = "M DRIVERS", - [TK_M_ATTRS] = "M ATTRS", - [TK_M_TAGS] = "M TAGS", - [TK_M_PARENTS_MAX] = "M PARENTS_MAX", - - [TK_M_TEST] = "M TEST", - [TK_M_PROGRAM] = "M PROGRAM", - [TK_M_IMPORT_FILE] = "M IMPORT_FILE", - [TK_M_IMPORT_PROG] = "M IMPORT_PROG", - [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", - [TK_M_IMPORT_DB] = "M IMPORT_DB", - [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", - [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", - [TK_M_RESULT] = "M RESULT", - [TK_M_MAX] = "M MAX", - - [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", - [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", - [TK_A_DB_PERSIST] = "A DB_PERSIST", - [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", - [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", - [TK_A_OWNER] = "A OWNER", - [TK_A_GROUP] = "A GROUP", - [TK_A_MODE] = "A MODE", - [TK_A_OWNER_ID] = "A OWNER_ID", - [TK_A_GROUP_ID] = "A GROUP_ID", - [TK_A_STATIC_NODE] = "A STATIC_NODE", - [TK_A_SECLABEL] = "A SECLABEL", - [TK_A_MODE_ID] = "A MODE_ID", - [TK_A_ENV] = "A ENV", - [TK_A_TAG] = "A ENV", - [TK_A_NAME] = "A NAME", - [TK_A_DEVLINK] = "A DEVLINK", - [TK_A_ATTR] = "A ATTR", - [TK_A_SYSCTL] = "A SYSCTL", - [TK_A_RUN_BUILTIN] = "A RUN_BUILTIN", - [TK_A_RUN_PROGRAM] = "A RUN_PROGRAM", - [TK_A_GOTO] = "A GOTO", - - [TK_END] = "END", - }; - - return token_strs[type]; -} - -static void dump_token(struct udev_rules *rules, struct token *token) { - enum token_type type = token->type; - enum operation_type op = token->key.op; - enum string_glob_type glob = token->key.glob; - const char *value = rules_str(rules, token->key.value_off); - const char *attr = &rules->strbuf->buf[token->key.attr_off]; - - switch (type) { - case TK_RULE: - { - const char *tks_ptr = (char *)rules->tokens; - const char *tk_ptr = (char *)token; - unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); - - log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", - &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line, - idx, token->rule.token_count, - &rules->strbuf->buf[token->rule.label_off]); - break; - } - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_NAME: - case TK_A_DEVLINK: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: - log_debug("%s %s '%s'(%s)", - token_str(type), operation_str(op), value, string_glob_str(glob)); - break; - case TK_M_IMPORT_BUILTIN: - log_debug("%s %i '%s'", token_str(type), token->key.builtin_cmd, value); - break; - case TK_M_ATTR: - case TK_M_SYSCTL: - case TK_M_ATTRS: - case TK_M_ENV: - case TK_A_ATTR: - case TK_A_SYSCTL: - case TK_A_ENV: - log_debug("%s %s '%s' '%s'(%s)", - token_str(type), operation_str(op), attr, value, string_glob_str(glob)); - break; - case TK_M_TAG: - case TK_A_TAG: - log_debug("%s %s '%s'", token_str(type), operation_str(op), value); - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - log_debug("%s", token_str(type)); - break; - case TK_M_TEST: - log_debug("%s %s '%s'(%s) %#o", - token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); - break; - case TK_A_INOTIFY_WATCH: - log_debug("%s %u", token_str(type), token->key.watch); - break; - case TK_A_DEVLINK_PRIO: - log_debug("%s %u", token_str(type), token->key.devlink_prio); - break; - case TK_A_OWNER_ID: - log_debug("%s %s %u", token_str(type), operation_str(op), token->key.uid); - break; - case TK_A_GROUP_ID: - log_debug("%s %s %u", token_str(type), operation_str(op), token->key.gid); - break; - case TK_A_MODE_ID: - log_debug("%s %s %#o", token_str(type), operation_str(op), token->key.mode); - break; - case TK_A_STATIC_NODE: - log_debug("%s '%s'", token_str(type), value); - break; - case TK_A_SECLABEL: - log_debug("%s %s '%s' '%s'", token_str(type), operation_str(op), attr, value); - break; - case TK_A_GOTO: - log_debug("%s '%s' %u", token_str(type), value, token->key.rule_goto); - break; - case TK_END: - log_debug("* %s", token_str(type)); - break; - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - log_debug("unknown type %u", type); - break; - } -} - -static void dump_rules(struct udev_rules *rules) { - unsigned int i; - - log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", - rules->token_cur, - rules->token_cur * sizeof(struct token), - rules->strbuf->nodes_count, - rules->strbuf->len); - for (i = 0; i < rules->token_cur; i++) - dump_token(rules, &rules->tokens[i]); -} -#else -static inline void dump_token(struct udev_rules *rules, struct token *token) {} -static inline void dump_rules(struct udev_rules *rules) {} -#endif /* DEBUG */ - -static int add_token(struct udev_rules *rules, struct token *token) { - /* grow buffer if needed */ - if (rules->token_cur+1 >= rules->token_max) { - struct token *tokens; - unsigned int add; - - /* double the buffer size */ - add = rules->token_max; - if (add < 8) - add = 8; - - tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); - if (tokens == NULL) - return -1; - rules->tokens = tokens; - rules->token_max += add; - } - memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); - rules->token_cur++; - return 0; -} - -static uid_t add_uid(struct udev_rules *rules, const char *owner) { - unsigned int i; - uid_t uid = 0; - unsigned int off; - int r; - - /* lookup, if we know it already */ - for (i = 0; i < rules->uids_cur; i++) { - off = rules->uids[i].name_off; - if (streq(rules_str(rules, off), owner)) { - uid = rules->uids[i].uid; - return uid; - } - } - r = get_user_creds(&owner, &uid, NULL, NULL, NULL); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - } - - /* grow buffer if needed */ - if (rules->uids_cur+1 >= rules->uids_max) { - struct uid_gid *uids; - unsigned int add; - - /* double the buffer size */ - add = rules->uids_max; - if (add < 1) - add = 8; - - uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); - if (uids == NULL) - return uid; - rules->uids = uids; - rules->uids_max += add; - } - rules->uids[rules->uids_cur].uid = uid; - off = rules_add_string(rules, owner); - if (off <= 0) - return uid; - rules->uids[rules->uids_cur].name_off = off; - rules->uids_cur++; - return uid; -} - -static gid_t add_gid(struct udev_rules *rules, const char *group) { - unsigned int i; - gid_t gid = 0; - unsigned int off; - int r; - - /* lookup, if we know it already */ - for (i = 0; i < rules->gids_cur; i++) { - off = rules->gids[i].name_off; - if (streq(rules_str(rules, off), group)) { - gid = rules->gids[i].gid; - return gid; - } - } - r = get_group_creds(&group, &gid); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - } - - /* grow buffer if needed */ - if (rules->gids_cur+1 >= rules->gids_max) { - struct uid_gid *gids; - unsigned int add; - - /* double the buffer size */ - add = rules->gids_max; - if (add < 1) - add = 8; - - gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); - if (gids == NULL) - return gid; - rules->gids = gids; - rules->gids_max += add; - } - rules->gids[rules->gids_cur].gid = gid; - off = rules_add_string(rules, group); - if (off <= 0) - return gid; - rules->gids[rules->gids_cur].name_off = off; - rules->gids_cur++; - return gid; -} - -static int import_property_from_string(struct udev_device *dev, char *line) { - char *key; - char *val; - size_t len; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - return -1; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) - return -1; - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - return -1; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - return -1; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - return -1; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - log_debug("inconsistent quoting: '%s', skip", line); - return -1; - } - val[len-1] = '\0'; - val++; - } - - udev_device_add_property(dev, key, val); - - return 0; -} - -static int import_file_into_properties(struct udev_device *dev, const char *filename) { - FILE *f; - char line[UTIL_LINE_SIZE]; - - f = fopen(filename, "re"); - if (f == NULL) - return -1; - while (fgets(line, sizeof(line), f) != NULL) - import_property_from_string(dev, line); - fclose(f); - return 0; -} - -static int import_program_into_properties(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - const char *program) { - char result[UTIL_LINE_SIZE]; - char *line; - int err; - - err = udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)); - if (err < 0) - return err; - - line = result; - while (line != NULL) { - char *pos; - - pos = strchr(line, '\n'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - import_property_from_string(event->dev, line); - line = pos; - } - return 0; -} - -static int import_parent_into_properties(struct udev_device *dev, const char *filter) { - struct udev_device *dev_parent; - struct udev_list_entry *list_entry; - - assert(dev); - assert(filter); - - dev_parent = udev_device_get_parent(dev); - if (dev_parent == NULL) - return -1; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { - const char *key = udev_list_entry_get_name(list_entry); - const char *val = udev_list_entry_get_value(list_entry); - - if (fnmatch(filter, key, 0) == 0) - udev_device_add_property(dev, key, val); - } - return 0; -} - -static void attr_subst_subdir(char *attr, size_t len) { - const char *pos, *tail, *path; - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - - pos = strstr(attr, "/*/"); - if (!pos) - return; - - tail = pos + 2; - path = strndupa(attr, pos - attr + 1); /* include slash at end */ - dir = opendir(path); - if (dir == NULL) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) - if (dent->d_name[0] != '.') { - char n[strlen(dent->d_name) + strlen(tail) + 1]; - - strscpyl(n, sizeof n, dent->d_name, tail, NULL); - if (faccessat(dirfd(dir), n, F_OK, 0) == 0) { - strscpyl(attr, len, path, n, NULL); - break; - } - } -} - -static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) { - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL || linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - for (;;) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '-') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *op = OP_MATCH; - linepos += 2; - } else if (linepos[0] == '!' && linepos[1] == '=') { - *op = OP_NOMATCH; - linepos += 2; - } else if (linepos[0] == '+' && linepos[1] == '=') { - *op = OP_ADD; - linepos += 2; - } else if (linepos[0] == '-' && linepos[1] == '=') { - *op = OP_REMOVE; - linepos += 2; - } else if (linepos[0] == '=') { - *op = OP_ASSIGN; - linepos++; - } else if (linepos[0] == ':' && linepos[1] == '=') { - *op = OP_ASSIGN_FINAL; - linepos += 2; - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value */ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - /* terminate */ - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - - /* move line to next key */ - *line = temp; - return 0; -} - -/* extract possible KEY{attr} */ -static const char *get_key_attribute(struct udev *udev, char *str) { - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - log_error("missing closing brace for format"); - return NULL; - } - pos[0] = '\0'; - return attr; - } - return NULL; -} - -static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, - enum operation_type op, - const char *value, const void *data) { - struct token *token = rule_tmp->token + rule_tmp->token_cur; - const char *attr = NULL; - - assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token)); - memzero(token, sizeof(struct token)); - - switch (type) { - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_DEVLINK: - case TK_A_NAME: - case TK_A_GOTO: - case TK_M_TAG: - case TK_A_TAG: - case TK_A_STATIC_NODE: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - break; - case TK_M_IMPORT_BUILTIN: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - break; - case TK_M_ENV: - case TK_M_ATTR: - case TK_M_SYSCTL: - case TK_M_ATTRS: - case TK_A_ATTR: - case TK_A_SYSCTL: - case TK_A_ENV: - case TK_A_SECLABEL: - attr = data; - token->key.value_off = rules_add_string(rule_tmp->rules, value); - token->key.attr_off = rules_add_string(rule_tmp->rules, attr); - break; - case TK_M_TEST: - token->key.value_off = rules_add_string(rule_tmp->rules, value); - if (data != NULL) - token->key.mode = *(mode_t *)data; - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - break; - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - token->key.value_off = rules_add_string(rule_tmp->rules, value); - break; - case TK_A_INOTIFY_WATCH: - case TK_A_DEVLINK_PRIO: - token->key.devlink_prio = *(int *)data; - break; - case TK_A_OWNER_ID: - token->key.uid = *(uid_t *)data; - break; - case TK_A_GROUP_ID: - token->key.gid = *(gid_t *)data; - break; - case TK_A_MODE_ID: - token->key.mode = *(mode_t *)data; - break; - case TK_RULE: - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_END: - case TK_UNSET: - assert_not_reached("wrong type"); - } - - if (value != NULL && type < TK_M_MAX) { - /* check if we need to split or call fnmatch() while matching rules */ - enum string_glob_type glob; - int has_split; - int has_glob; - - has_split = (strchr(value, '|') != NULL); - has_glob = string_is_glob(value); - if (has_split && has_glob) { - glob = GL_SPLIT_GLOB; - } else if (has_split) { - glob = GL_SPLIT; - } else if (has_glob) { - if (streq(value, "?*")) - glob = GL_SOMETHING; - else - glob = GL_GLOB; - } else { - glob = GL_PLAIN; - } - token->key.glob = glob; - } - - if (value != NULL && type > TK_M_MAX) { - /* check if assigned value has substitution chars */ - if (value[0] == '[') - token->key.subst = SB_SUBSYS; - else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) - token->key.subst = SB_FORMAT; - else - token->key.subst = SB_NONE; - } - - if (attr != NULL) { - /* check if property/attribute name has substitution chars */ - if (attr[0] == '[') - token->key.attrsubst = SB_SUBSYS; - else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) - token->key.attrsubst = SB_FORMAT; - else - token->key.attrsubst = SB_NONE; - } - - token->key.type = type; - token->key.op = op; - rule_tmp->token_cur++; -} - -static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { - unsigned int i; - unsigned int start = 0; - unsigned int end = rule_tmp->token_cur; - - for (i = 0; i < rule_tmp->token_cur; i++) { - enum token_type next_val = TK_UNSET; - unsigned int next_idx = 0; - unsigned int j; - - /* find smallest value */ - for (j = start; j < end; j++) { - if (rule_tmp->token[j].type == TK_UNSET) - continue; - if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { - next_val = rule_tmp->token[j].type; - next_idx = j; - } - } - - /* add token and mark done */ - if (add_token(rules, &rule_tmp->token[next_idx]) != 0) - return -1; - rule_tmp->token[next_idx].type = TK_UNSET; - - /* shrink range */ - if (next_idx == start) - start++; - if (next_idx+1 == end) - end--; - } - return 0; -} - -#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) -#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; } - -static void add_rule(struct udev_rules *rules, char *line, - const char *filename, unsigned int filename_off, unsigned int lineno) { - char *linepos; - const char *attr; - struct rule_tmp rule_tmp = { - .rules = rules, - .rule.type = TK_RULE, - }; - - /* the offset in the rule is limited to unsigned short */ - if (filename_off < USHRT_MAX) - rule_tmp.rule.rule.filename_off = filename_off; - rule_tmp.rule.rule.filename_line = lineno; - - linepos = line; - for (;;) { - char *key; - char *value; - enum operation_type op; - - if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) { - /* Avoid erroring on trailing whitespace. This is probably rare - * so save the work for the error case instead of always trying - * to strip the trailing whitespace with strstrip(). */ - while (isblank(*linepos)) - linepos++; - - /* If we aren't at the end of the line, this is a parsing error. - * Make a best effort to describe where the problem is. */ - if (!strchr(NEWLINE, *linepos)) { - char buf[2] = {*linepos}; - _cleanup_free_ char *tmp; - - tmp = cescape(buf); - log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')", - filename, lineno, linepos - line + 1, tmp); - if (*linepos == '#') - log_error("hint: comments can only start at beginning of line"); - } - break; - } - - if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token)) - LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur); - - if (streq(key, "ACTION")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); - - } else if (streq(key, "DEVPATH")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); - - } else if (streq(key, "KERNEL")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); - - } else if (streq(key, "SUBSYSTEM")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - /* bus, class, subsystem events should all be the same */ - if (STR_IN_SET(value, "subsystem", "bus", "class")) { - if (!streq(value, "subsystem")) - LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value); - - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); - } else - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); - - } else if (streq(key, "DRIVER")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); - - } else if (startswith(key, "ATTR{")) { - attr = get_key_attribute(rules->udev, key + strlen("ATTR")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTR"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ATTR"); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); - else - rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); - - } else if (startswith(key, "SYSCTL{")) { - attr = get_key_attribute(rules->udev, key + strlen("SYSCTL")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTR"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ATTR"); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr); - else - rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr); - - } else if (startswith(key, "SECLABEL{")) { - attr = get_key_attribute(rules->udev, key + strlen("SECLABEL")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "SECLABEL"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "SECLABEL"); - - rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr); - - } else if (streq(key, "KERNELS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); - - } else if (streq(key, "SUBSYSTEMS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); - - } else if (streq(key, "DRIVERS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); - - } else if (startswith(key, "ATTRS{")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", "ATTRS"); - - attr = get_key_attribute(rules->udev, key + strlen("ATTRS")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ATTRS"); - - if (startswith(attr, "device/")) - LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix"); - if (strstr(attr, "../") != NULL) - LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix"); - rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); - - } else if (streq(key, "TAGS")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); - - } else if (startswith(key, "ENV{")) { - attr = get_key_attribute(rules->udev, key + strlen("ENV")); - if (attr == NULL) - LOG_AND_RETURN("error parsing %s attribute", "ENV"); - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "ENV"); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr); - else { - if (STR_IN_SET(attr, - "ACTION", - "SUBSYSTEM", - "DEVTYPE", - "MAJOR", - "MINOR", - "DRIVER", - "IFINDEX", - "DEVNAME", - "DEVLINKS", - "DEVPATH", - "TAGS")) - LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr); - - rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr); - } - - } else if (streq(key, "TAG")) { - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); - - } else if (streq(key, "PROGRAM")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); - - } else if (streq(key, "RESULT")) { - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); - - } else if (startswith(key, "IMPORT")) { - attr = get_key_attribute(rules->udev, key + strlen("IMPORT")); - if (attr == NULL) { - LOG_RULE_WARNING("ignoring IMPORT{} with missing type"); - continue; - } - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "IMPORT"); - - if (streq(attr, "program")) { - /* find known built-in command */ - if (value[0] != '/') { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd < UDEV_BUILTIN_MAX) { - LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value); - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - continue; - } - } - rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); - } else if (streq(attr, "builtin")) { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd >= UDEV_BUILTIN_MAX) - LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value); - else - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - } else if (streq(attr, "file")) - rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); - else if (streq(attr, "db")) - rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); - else if (streq(attr, "cmdline")) - rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); - else if (streq(attr, "parent")) - rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); - else - LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr); - - } else if (startswith(key, "TEST")) { - mode_t mode = 0; - - if (op > OP_MATCH_MAX) - LOG_AND_RETURN("invalid %s operation", "TEST"); - - attr = get_key_attribute(rules->udev, key + strlen("TEST")); - if (attr != NULL) { - mode = strtol(attr, NULL, 8); - rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); - } else - rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); - - } else if (startswith(key, "RUN")) { - attr = get_key_attribute(rules->udev, key + strlen("RUN")); - if (attr == NULL) - attr = "program"; - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", "RUN"); - - if (streq(attr, "builtin")) { - const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - if (cmd < UDEV_BUILTIN_MAX) - rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd); - else - LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value); - } else if (streq(attr, "program")) { - const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; - - rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); - } else - LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr); - - } else if (streq(key, "LABEL")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_tmp.rule.rule.label_off = rules_add_string(rules, value); - - } else if (streq(key, "GOTO")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); - - } else if (startswith(key, "NAME")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); - else { - if (streq(value, "%k")) { - LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove"); - continue; - } - if (isempty(value)) { - LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove"); - continue; - } - rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "SYMLINK")) { - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "OWNER")) { - uid_t uid; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) { - uid = add_uid(rules, value); - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if (rules->resolve_names >= 0) - rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); - - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "GROUP")) { - gid_t gid; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - gid = add_gid(rules, value); - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if (rules->resolve_names >= 0) - rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); - - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "MODE")) { - mode_t mode; - char *endptr; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - mode = strtol(value, &endptr, 8); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); - else - rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - - } else if (streq(key, "OPTIONS")) { - const char *pos; - - if (op == OP_REMOVE) - LOG_AND_RETURN("invalid %s operation", key); - - pos = strstr(value, "link_priority="); - if (pos != NULL) { - int prio = atoi(pos + strlen("link_priority=")); - - rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); - } - - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos += strlen("string_escape="); - if (startswith(pos, "none")) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); - else if (startswith(pos, "replace")) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); - } - - pos = strstr(value, "db_persist"); - if (pos != NULL) - rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); - - pos = strstr(value, "nowatch"); - if (pos != NULL) { - const int off = 0; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); - } else { - pos = strstr(value, "watch"); - if (pos != NULL) { - const int on = 1; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); - } - } - - pos = strstr(value, "static_node="); - if (pos != NULL) { - pos += strlen("static_node="); - rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL); - rule_tmp.rule.rule.has_static_node = true; - } - - } else - LOG_AND_RETURN("unknown key '%s'", key); - } - - /* add rule token and sort tokens */ - rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; - if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0) - LOG_RULE_ERROR("failed to add rule token"); -} - -static int parse_file(struct udev_rules *rules, const char *filename) { - _cleanup_fclose_ FILE *f = NULL; - unsigned int first_token; - unsigned int filename_off; - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - unsigned int i; - - f = fopen(filename, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - else - return -errno; - } - - if (null_or_empty_fd(fileno(f))) { - log_debug("Skipping empty file: %s", filename); - return 0; - } else - log_debug("Reading rules file: %s", filename); - - first_token = rules->token_cur; - filename_off = rules_add_string(rules, filename); - - while (fgets(line, sizeof(line), f) != NULL) { - char *key; - size_t len; - - /* skip whitespace */ - line_nr++; - key = line; - while (isspace(key[0])) - key++; - - /* comment */ - if (key[0] == '#') - continue; - - len = strlen(line); - if (len < 3) - continue; - - /* continue reading if backslash+newline is found */ - while (line[len-2] == '\\') { - if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) - break; - if (strlen(&line[len-2]) < 2) - break; - line_nr++; - len = strlen(line); - } - - if (len+1 >= sizeof(line)) { - log_error("line too long '%s':%u, ignored", filename, line_nr); - continue; - } - add_rule(rules, key, filename, filename_off, line_nr); - } - - /* link GOTOs to LABEL rules in this file to be able to fast-forward */ - for (i = first_token+1; i < rules->token_cur; i++) { - if (rules->tokens[i].type == TK_A_GOTO) { - char *label = rules_str(rules, rules->tokens[i].key.value_off); - unsigned int j; - - for (j = i+1; j < rules->token_cur; j++) { - if (rules->tokens[j].type != TK_RULE) - continue; - if (rules->tokens[j].rule.label_off == 0) - continue; - if (!streq(label, rules_str(rules, rules->tokens[j].rule.label_off))) - continue; - rules->tokens[i].key.rule_goto = j; - break; - } - if (rules->tokens[i].key.rule_goto == 0) - log_error("GOTO '%s' has no matching label in: '%s'", label, filename); - } - } - return 0; -} - -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { - struct udev_rules *rules; - struct udev_list file_list; - struct token end_token; - char **files, **f; - int r; - - rules = new0(struct udev_rules, 1); - if (rules == NULL) - return NULL; - rules->udev = udev; - rules->resolve_names = resolve_names; - udev_list_init(udev, &file_list, true); - - /* init token array and string buffer */ - rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); - if (rules->tokens == NULL) - return udev_rules_unref(rules); - rules->token_max = PREALLOC_TOKEN; - - rules->strbuf = strbuf_new(); - if (!rules->strbuf) - return udev_rules_unref(rules); - - udev_rules_check_timestamp(rules); - - r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); - if (r < 0) { - log_error_errno(r, "failed to enumerate rules files: %m"); - return udev_rules_unref(rules); - } - - /* - * The offset value in the rules strct is limited; add all - * rules file names to the beginning of the string buffer. - */ - STRV_FOREACH(f, files) - rules_add_string(rules, *f); - - STRV_FOREACH(f, files) - parse_file(rules, *f); - - strv_free(files); - - memzero(&end_token, sizeof(struct token)); - end_token.type = TK_END; - add_token(rules, &end_token); - log_debug("rules contain %zu bytes tokens (%u * %zu bytes), %zu bytes strings", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len); - - /* cleanup temporary strbuf data */ - log_debug("%zu strings (%zu bytes), %zu de-duplicated (%zu bytes), %zu trie nodes used", - rules->strbuf->in_count, rules->strbuf->in_len, - rules->strbuf->dedup_count, rules->strbuf->dedup_len, rules->strbuf->nodes_count); - strbuf_complete(rules->strbuf); - - /* cleanup uid/gid cache */ - rules->uids = mfree(rules->uids); - rules->uids_cur = 0; - rules->uids_max = 0; - rules->gids = mfree(rules->gids); - rules->gids_cur = 0; - rules->gids_max = 0; - - dump_rules(rules); - return rules; -} - -struct udev_rules *udev_rules_unref(struct udev_rules *rules) { - if (rules == NULL) - return NULL; - free(rules->tokens); - strbuf_cleanup(rules->strbuf); - free(rules->uids); - free(rules->gids); - free(rules); - return NULL; -} - -bool udev_rules_check_timestamp(struct udev_rules *rules) { - if (!rules) - return false; - - return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true); -} - -static int match_key(struct udev_rules *rules, struct token *token, const char *val) { - char *key_value = rules_str(rules, token->key.value_off); - char *pos; - bool match = false; - - if (val == NULL) - val = ""; - - switch (token->key.glob) { - case GL_PLAIN: - match = (streq(key_value, val)); - break; - case GL_GLOB: - match = (fnmatch(key_value, val, 0) == 0); - break; - case GL_SPLIT: - { - const char *s; - size_t len; - - s = rules_str(rules, token->key.value_off); - len = strlen(val); - for (;;) { - const char *next; - - next = strchr(s, '|'); - if (next != NULL) { - size_t matchlen = (size_t)(next - s); - - match = (matchlen == len && strneq(s, val, matchlen)); - if (match) - break; - } else { - match = (streq(s, val)); - break; - } - s = &next[1]; - } - break; - } - case GL_SPLIT_GLOB: - { - char value[UTIL_PATH_SIZE]; - - strscpy(value, sizeof(value), rules_str(rules, token->key.value_off)); - key_value = value; - while (key_value != NULL) { - pos = strchr(key_value, '|'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - key_value = pos; - } - break; - } - case GL_SOMETHING: - match = (val[0] != '\0'); - break; - case GL_UNSET: - return -1; - } - - if (match && (token->key.op == OP_MATCH)) - return 0; - if (!match && (token->key.op == OP_NOMATCH)) - return 0; - return -1; -} - -static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) { - const char *name; - char nbuf[UTIL_NAME_SIZE]; - const char *value; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - - name = rules_str(rules, cur->key.attr_off); - switch (cur->key.attrsubst) { - case SB_FORMAT: - udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); - name = nbuf; - /* fall through */ - case SB_NONE: - value = udev_device_get_sysattr_value(dev, name); - if (value == NULL) - return -1; - break; - case SB_SUBSYS: - if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) - return -1; - value = vbuf; - break; - default: - return -1; - } - - /* remove trailing whitespace, if not asked to match for it */ - len = strlen(value); - if (len > 0 && isspace(value[len-1])) { - const char *key_value; - size_t klen; - - key_value = rules_str(rules, cur->key.value_off); - klen = strlen(key_value); - if (klen > 0 && !isspace(key_value[klen-1])) { - if (value != vbuf) { - strscpy(vbuf, sizeof(vbuf), value); - value = vbuf; - } - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - } - } - - return match_key(rules, cur, value); -} - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -void udev_rules_apply_to_event(struct udev_rules *rules, - struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - struct udev_list *properties_list) { - struct token *cur; - struct token *rule; - enum escape_type esc = ESCAPE_UNSET; - bool can_set_name; - - if (rules->tokens == NULL) - return; - - can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) && - (major(udev_device_get_devnum(event->dev)) > 0 || - udev_device_get_ifindex(event->dev) > 0)); - - /* loop through token list, match, run actions or forward to next rule */ - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - dump_token(rules, cur); - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ - if (!can_set_name && rule->rule.can_set_name) - goto nomatch; - esc = ESCAPE_UNSET; - break; - case TK_M_ACTION: - if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVPATH: - if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) - goto nomatch; - break; - case TK_M_KERNEL: - if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVLINK: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { - const char *devlink; - - devlink = udev_list_entry_get_name(list_entry) + strlen("/dev/"); - if (match_key(rules, cur, devlink) == 0) { - match = true; - break; - } - } - if (!match) - goto nomatch; - break; - } - case TK_M_NAME: - if (match_key(rules, cur, event->name) != 0) - goto nomatch; - break; - case TK_M_ENV: { - const char *key_name = rules_str(rules, cur->key.attr_off); - const char *value; - - value = udev_device_get_property_value(event->dev, key_name); - - /* check global properties */ - if (!value && properties_list) { - struct udev_list_entry *list_entry; - - list_entry = udev_list_get_entry(properties_list); - list_entry = udev_list_entry_get_by_name(list_entry, key_name); - if (list_entry != NULL) - value = udev_list_entry_get_value(list_entry); - } - - if (!value) - value = ""; - if (match_key(rules, cur, value)) - goto nomatch; - break; - } - case TK_M_TAG: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { - if (streq(rules_str(rules, cur->key.value_off), udev_list_entry_get_name(list_entry))) { - match = true; - break; - } - } - if ((!match && (cur->key.op != OP_NOMATCH)) || - (match && (cur->key.op == OP_NOMATCH))) - goto nomatch; - break; - } - case TK_M_SUBSYSTEM: - if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DRIVER: - if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) - goto nomatch; - break; - case TK_M_ATTR: - if (match_attr(rules, event->dev, event, cur) != 0) - goto nomatch; - break; - case TK_M_SYSCTL: { - char filename[UTIL_PATH_SIZE]; - _cleanup_free_ char *value = NULL; - size_t len; - - udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename)); - sysctl_normalize(filename); - if (sysctl_read(filename, &value) < 0) - goto nomatch; - - len = strlen(value); - while (len > 0 && isspace(value[--len])) - value[len] = '\0'; - if (match_key(rules, cur, value) != 0) - goto nomatch; - break; - } - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_ATTRS: - case TK_M_TAGS: { - struct token *next; - - /* get whole sequence of parent matches */ - next = cur; - while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) - next++; - - /* loop over parents */ - event->dev_parent = event->dev; - for (;;) { - struct token *key; - - /* loop over sequence of parent match keys */ - for (key = cur; key < next; key++ ) { - dump_token(rules, key); - switch(key->type) { - case TK_M_KERNELS: - if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_SUBSYSTEMS: - if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_DRIVERS: - if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_ATTRS: - if (match_attr(rules, event->dev_parent, event, key) != 0) - goto try_parent; - break; - case TK_M_TAGS: { - bool match = udev_device_has_tag(event->dev_parent, rules_str(rules, cur->key.value_off)); - - if (match && key->key.op == OP_NOMATCH) - goto try_parent; - if (!match && key->key.op == OP_MATCH) - goto try_parent; - break; - } - default: - goto nomatch; - } - } - break; - - try_parent: - event->dev_parent = udev_device_get_parent(event->dev_parent); - if (event->dev_parent == NULL) - goto nomatch; - } - /* move behind our sequence of parent match keys */ - cur = next; - continue; - } - case TK_M_TEST: { - char filename[UTIL_PATH_SIZE]; - struct stat statbuf; - int match; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename)); - if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { - if (filename[0] != '/') { - char tmp[UTIL_PATH_SIZE]; - - strscpy(tmp, sizeof(tmp), filename); - strscpyl(filename, sizeof(filename), - udev_device_get_syspath(event->dev), "/", tmp, NULL); - } - } - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - if (match && cur->key.mode > 0) - match = ((statbuf.st_mode & cur->key.mode) > 0); - if (match && cur->key.op == OP_NOMATCH) - goto nomatch; - if (!match && cur->key.op == OP_MATCH) - goto nomatch; - break; - } - case TK_M_PROGRAM: { - char program[UTIL_PATH_SIZE]; - char result[UTIL_LINE_SIZE]; - - event->program_result = mfree(event->program_result); - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), program, sizeof(program)); - log_debug("PROGRAM '%s' %s:%u", - program, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (udev_event_spawn(event, timeout_usec, timeout_warn_usec, true, program, result, sizeof(result)) < 0) { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } else { - int count; - - util_remove_trailing_chars(result, '\n'); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - log_debug("%i character(s) replaced" , count); - } - event->program_result = strdup(result); - if (cur->key.op == OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_FILE: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - if (import_file_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PROG: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - log_debug("IMPORT '%s' %s:%u", - import, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (import_program_into_properties(event, timeout_usec, timeout_warn_usec, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_BUILTIN: { - char command[UTIL_PATH_SIZE]; - - if (udev_builtin_run_once(cur->key.builtin_cmd)) { - /* check if we ran already */ - if (event->builtin_run & (1 << cur->key.builtin_cmd)) { - log_debug("IMPORT builtin skip '%s' %s:%u", - udev_builtin_name(cur->key.builtin_cmd), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - /* return the result from earlier run */ - if (event->builtin_ret & (1 << cur->key.builtin_cmd)) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - /* mark as ran */ - event->builtin_run |= (1 << cur->key.builtin_cmd); - } - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), command, sizeof(command)); - log_debug("IMPORT builtin '%s' %s:%u", - udev_builtin_name(cur->key.builtin_cmd), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - - if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { - /* remember failure */ - log_debug("IMPORT builtin '%s' returned non-zero", - udev_builtin_name(cur->key.builtin_cmd)); - event->builtin_ret |= (1 << cur->key.builtin_cmd); - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_DB: { - const char *key = rules_str(rules, cur->key.value_off); - const char *value; - - value = udev_device_get_property_value(event->dev_db, key); - if (value != NULL) - udev_device_add_property(event->dev, key, value); - else { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_CMDLINE: { - _cleanup_fclose_ FILE *f = NULL; - bool imported = false; - - f = fopen("/proc/cmdline", "re"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - const char *key = rules_str(rules, cur->key.value_off); - char *pos; - - pos = strstr(cmdline, key); - if (pos != NULL) { - imported = true; - pos += strlen(key); - if (pos[0] == '\0' || isspace(pos[0])) - /* we import simple flags as 'FLAG=1' */ - udev_device_add_property(event->dev, key, "1"); - else if (pos[0] == '=') { - const char *value; - - pos++; - value = pos; - while (pos[0] != '\0' && !isspace(pos[0])) - pos++; - pos[0] = '\0'; - udev_device_add_property(event->dev, key, value); - } - } - } - } - if (!imported && cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PARENT: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), import, sizeof(import)); - if (import_parent_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_RESULT: - if (match_key(rules, cur, event->program_result) != 0) - goto nomatch; - break; - case TK_A_STRING_ESCAPE_NONE: - esc = ESCAPE_NONE; - break; - case TK_A_STRING_ESCAPE_REPLACE: - esc = ESCAPE_REPLACE; - break; - case TK_A_DB_PERSIST: - udev_device_set_db_persist(event->dev); - break; - case TK_A_INOTIFY_WATCH: - if (event->inotify_watch_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->inotify_watch_final = true; - event->inotify_watch = cur->key.watch; - break; - case TK_A_DEVLINK_PRIO: - udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); - break; - case TK_A_OWNER: { - char owner[UTIL_NAME_SIZE]; - const char *ow = owner; - int r; - - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), owner, sizeof(owner)); - event->owner_set = true; - r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - - event->uid = 0; - } - log_debug("OWNER %u %s:%u", - event->uid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_GROUP: { - char group[UTIL_NAME_SIZE]; - const char *gr = group; - int r; - - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), group, sizeof(group)); - event->group_set = true; - r = get_group_creds(&gr, &event->gid); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - - event->gid = 0; - } - log_debug("GROUP %u %s:%u", - event->gid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_MODE: { - char mode_str[UTIL_NAME_SIZE]; - mode_t mode; - char *endptr; - - if (event->mode_final) - break; - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), mode_str, sizeof(mode_str)); - mode = strtol(mode_str, &endptr, 8); - if (endptr[0] != '\0') { - log_error("ignoring invalid mode '%s'", mode_str); - break; - } - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = mode; - log_debug("MODE %#o %s:%u", - event->mode, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_OWNER_ID: - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - event->owner_set = true; - event->uid = cur->key.uid; - log_debug("OWNER %u %s:%u", - event->uid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_GROUP_ID: - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - event->group_set = true; - event->gid = cur->key.gid; - log_debug("GROUP %u %s:%u", - event->gid, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_MODE_ID: - if (event->mode_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = cur->key.mode; - log_debug("MODE %#o %s:%u", - event->mode, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - case TK_A_SECLABEL: { - const char *name, *label; - - name = rules_str(rules, cur->key.attr_off); - label = rules_str(rules, cur->key.value_off); - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->seclabel_list); - udev_list_entry_add(&event->seclabel_list, name, label); - log_debug("SECLABEL{%s}='%s' %s:%u", - name, label, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_ENV: { - const char *name = rules_str(rules, cur->key.attr_off); - char *value = rules_str(rules, cur->key.value_off); - char value_new[UTIL_NAME_SIZE]; - const char *value_old = NULL; - - if (value[0] == '\0') { - if (cur->key.op == OP_ADD) - break; - udev_device_add_property(event->dev, name, NULL); - break; - } - - if (cur->key.op == OP_ADD) - value_old = udev_device_get_property_value(event->dev, name); - if (value_old) { - char temp[UTIL_NAME_SIZE]; - - /* append value separated by space */ - udev_event_apply_format(event, value, temp, sizeof(temp)); - strscpyl(value_new, sizeof(value_new), value_old, " ", temp, NULL); - } else - udev_event_apply_format(event, value, value_new, sizeof(value_new)); - - udev_device_add_property(event->dev, name, value_new); - break; - } - case TK_A_TAG: { - char tag[UTIL_PATH_SIZE]; - const char *p; - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), tag, sizeof(tag)); - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_tags_list(event->dev); - for (p = tag; *p != '\0'; p++) { - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-' || *p == '_') - continue; - log_error("ignoring invalid tag name '%s'", tag); - break; - } - if (cur->key.op == OP_REMOVE) - udev_device_remove_tag(event->dev, tag); - else - udev_device_add_tag(event->dev, tag); - break; - } - case TK_A_NAME: { - const char *name = rules_str(rules, cur->key.value_off); - - char name_str[UTIL_PATH_SIZE]; - int count; - - if (event->name_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->name_final = true; - udev_event_apply_format(event, name, name_str, sizeof(name_str)); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(name_str, "/"); - if (count > 0) - log_debug("%i character(s) replaced", count); - } - if (major(udev_device_get_devnum(event->dev)) && - !streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/"))) { - log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n", - name, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - if (free_and_strdup(&event->name, name_str) < 0) { - log_oom(); - return; - } - log_debug("NAME '%s' %s:%u", - event->name, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - break; - } - case TK_A_DEVLINK: { - char temp[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - char *pos, *next; - int count = 0; - - if (event->devlink_final) - break; - if (major(udev_device_get_devnum(event->dev)) == 0) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->devlink_final = true; - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_devlinks_list(event->dev); - - /* allow multiple symlinks separated by spaces */ - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), temp, sizeof(temp)); - if (esc == ESCAPE_UNSET) - count = util_replace_chars(temp, "/ "); - else if (esc == ESCAPE_REPLACE) - count = util_replace_chars(temp, "/"); - if (count > 0) - log_debug("%i character(s) replaced" , count); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next != NULL) { - next[0] = '\0'; - log_debug("LINK '%s' %s:%u", pos, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); - udev_device_add_devlink(event->dev, filename); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - log_debug("LINK '%s' %s:%u", pos, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - strscpyl(filename, sizeof(filename), "/dev/", pos, NULL); - udev_device_add_devlink(event->dev, filename); - } - break; - } - case TK_A_ATTR: { - const char *key_name = rules_str(rules, cur->key.attr_off); - char attr[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - _cleanup_fclose_ FILE *f = NULL; - - if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) - strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); - attr_subst_subdir(attr, sizeof(attr)); - - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value)); - log_debug("ATTR '%s' writing '%s' %s:%u", attr, value, - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - f = fopen(attr, "we"); - if (f == NULL) - log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); - else if (fprintf(f, "%s", value) <= 0) - log_error_errno(errno, "error writing ATTR{%s}: %m", attr); - break; - } - case TK_A_SYSCTL: { - char filename[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - int r; - - udev_event_apply_format(event, rules_str(rules, cur->key.attr_off), filename, sizeof(filename)); - sysctl_normalize(filename); - udev_event_apply_format(event, rules_str(rules, cur->key.value_off), value, sizeof(value)); - log_debug("SYSCTL '%s' writing '%s' %s:%u", filename, value, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - r = sysctl_write(filename, value); - if (r < 0) - log_error_errno(r, "error writing SYSCTL{%s}='%s': %m", filename, value); - break; - } - case TK_A_RUN_BUILTIN: - case TK_A_RUN_PROGRAM: { - struct udev_list_entry *entry; - - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->run_list); - log_debug("RUN '%s' %s:%u", - rules_str(rules, cur->key.value_off), - rules_str(rules, rule->rule.filename_off), - rule->rule.filename_line); - entry = udev_list_entry_add(&event->run_list, rules_str(rules, cur->key.value_off), NULL); - udev_list_entry_set_num(entry, cur->key.builtin_cmd); - break; - } - case TK_A_GOTO: - if (cur->key.rule_goto == 0) - break; - cur = &rules->tokens[cur->key.rule_goto]; - continue; - case TK_END: - return; - - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - log_error("wrong type %u", cur->type); - goto nomatch; - } - - cur++; - continue; - nomatch: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - } -} - -int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { - struct token *cur; - struct token *rule; - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0; - _cleanup_strv_free_ char **tags = NULL; - char **t; - FILE *f = NULL; - _cleanup_free_ char *path = NULL; - int r; - - if (rules->tokens == NULL) - return 0; - - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - - /* skip rules without a static_node tag */ - if (!rule->rule.has_static_node) - goto next; - - uid = 0; - gid = 0; - mode = 0; - tags = strv_free(tags); - break; - case TK_A_OWNER_ID: - uid = cur->key.uid; - break; - case TK_A_GROUP_ID: - gid = cur->key.gid; - break; - case TK_A_MODE_ID: - mode = cur->key.mode; - break; - case TK_A_TAG: - r = strv_extend(&tags, rules_str(rules, cur->key.value_off)); - if (r < 0) - goto finish; - - break; - case TK_A_STATIC_NODE: { - char device_node[UTIL_PATH_SIZE]; - char tags_dir[UTIL_PATH_SIZE]; - char tag_symlink[UTIL_PATH_SIZE]; - struct stat stats; - - /* we assure, that the permissions tokens are sorted before the static token */ - - if (mode == 0 && uid == 0 && gid == 0 && tags == NULL) - goto next; - - strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL); - if (stat(device_node, &stats) != 0) - break; - if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) - break; - - /* export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */ - if (tags) { - STRV_FOREACH(t, tags) { - _cleanup_free_ char *unescaped_filename = NULL; - - strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL); - r = mkdir_p(tags_dir, 0755); - if (r < 0) - return log_error_errno(r, "failed to create %s: %m", tags_dir); - - unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/."); - - strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL); - r = symlink(device_node, tag_symlink); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "failed to create symlink %s -> %s: %m", - tag_symlink, device_node); - } - } - - /* don't touch the permissions if only the tags were set */ - if (mode == 0 && uid == 0 && gid == 0) - break; - - if (mode == 0) { - if (gid > 0) - mode = 0660; - else - mode = 0600; - } - if (mode != (stats.st_mode & 01777)) { - r = chmod(device_node, mode); - if (r < 0) { - log_error("failed to chmod '%s' %#o", device_node, mode); - return -errno; - } else - log_debug("chmod '%s' %#o", device_node, mode); - } - - if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { - r = chown(device_node, uid, gid); - if (r < 0) { - log_error("failed to chown '%s' %u %u ", device_node, uid, gid); - return -errno; - } else - log_debug("chown '%s' %u %u", device_node, uid, gid); - } - - utimensat(AT_FDCWD, device_node, NULL, 0); - break; - } - case TK_END: - goto finish; - } - - cur++; - continue; -next: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - continue; - } - -finish: - if (f) { - fflush(f); - fchmod(fileno(f), 0644); - if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) { - unlink_noerrno("/run/udev/static_node-tags"); - unlink_noerrno(path); - return -errno; - } - } - - return 0; -} diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c deleted file mode 100644 index 9ce5e975de..0000000000 --- a/src/udev/udev-watch.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2004-2012 Kay Sievers - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "stdio-util.h" -#include "udev.h" - -static int inotify_fd = -1; - -/* inotify descriptor, will be shared with rules directory; - * set to cloexec since we need our children to be able to add - * watches for us - */ -int udev_watch_init(struct udev *udev) { - inotify_fd = inotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - log_error_errno(errno, "inotify_init failed: %m"); - return inotify_fd; -} - -/* move any old watches directory out of the way, and then restore - * the watches - */ -void udev_watch_restore(struct udev *udev) { - if (inotify_fd < 0) - return; - - if (rename("/run/udev/watch", "/run/udev/watch.old") == 0) { - DIR *dir; - struct dirent *ent; - - dir = opendir("/run/udev/watch.old"); - if (dir == NULL) { - log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m"); - return; - } - - for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { - char device[UTIL_PATH_SIZE]; - ssize_t len; - struct udev_device *dev; - - if (ent->d_name[0] == '.') - continue; - - len = readlinkat(dirfd(dir), ent->d_name, device, sizeof(device)); - if (len <= 0 || len == (ssize_t)sizeof(device)) - goto unlink; - device[len] = '\0'; - - dev = udev_device_new_from_device_id(udev, device); - if (dev == NULL) - goto unlink; - - log_debug("restoring old watch on '%s'", udev_device_get_devnode(dev)); - udev_watch_begin(udev, dev); - udev_device_unref(dev); -unlink: - unlinkat(dirfd(dir), ent->d_name, 0); - } - - closedir(dir); - rmdir("/run/udev/watch.old"); - - } else if (errno != ENOENT) - log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m"); -} - -void udev_watch_begin(struct udev *udev, struct udev_device *dev) { - char filename[UTIL_PATH_SIZE]; - int wd; - int r; - - if (inotify_fd < 0) - return; - - log_debug("adding watch on '%s'", udev_device_get_devnode(dev)); - wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - if (wd < 0) { - log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m", - inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - return; - } - - xsprintf(filename, "/run/udev/watch/%d", wd); - mkdir_parents(filename, 0755); - unlink(filename); - r = symlink(udev_device_get_id_filename(dev), filename); - if (r < 0) - log_error_errno(errno, "Failed to create symlink %s: %m", filename); - - udev_device_set_watch_handle(dev, wd); -} - -void udev_watch_end(struct udev *udev, struct udev_device *dev) { - int wd; - char filename[UTIL_PATH_SIZE]; - - if (inotify_fd < 0) - return; - - wd = udev_device_get_watch_handle(dev); - if (wd < 0) - return; - - log_debug("removing watch on '%s'", udev_device_get_devnode(dev)); - inotify_rm_watch(inotify_fd, wd); - - xsprintf(filename, "/run/udev/watch/%d", wd); - unlink(filename); - - udev_device_set_watch_handle(dev, -1); -} - -struct udev_device *udev_watch_lookup(struct udev *udev, int wd) { - char filename[UTIL_PATH_SIZE]; - char device[UTIL_NAME_SIZE]; - ssize_t len; - - if (inotify_fd < 0 || wd < 0) - return NULL; - - xsprintf(filename, "/run/udev/watch/%d", wd); - len = readlink(filename, device, sizeof(device)); - if (len <= 0 || (size_t)len == sizeof(device)) - return NULL; - device[len] = '\0'; - - return udev_device_new_from_device_id(udev, device); -} diff --git a/src/udev/udev.conf b/src/udev/udev.conf deleted file mode 100644 index 47d1433002..0000000000 --- a/src/udev/udev.conf +++ /dev/null @@ -1,3 +0,0 @@ -# see udev.conf(5) for details - -#udev_log="info" diff --git a/src/udev/udev.h b/src/udev/udev.h deleted file mode 100644 index 8433e8d9f2..0000000000 --- a/src/udev/udev.h +++ /dev/null @@ -1,216 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include - -#include "libudev.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; - struct udev_device *dev; - struct udev_device *dev_parent; - struct udev_device *dev_db; - char *name; - char *program_result; - mode_t mode; - uid_t uid; - gid_t gid; - struct udev_list seclabel_list; - struct udev_list run_list; - int exec_delay; - usec_t birth_usec; - sd_netlink *rtnl; - unsigned int builtin_run; - unsigned int builtin_ret; - bool inotify_watch; - bool inotify_watch_final; - bool group_set; - bool group_final; - bool owner_set; - bool owner_final; - bool mode_set; - bool mode_final; - bool name_final; - bool devlink_final; - bool run_final; -}; - -struct udev_watch { - struct udev_list_node node; - int handle; - char *name; -}; - -/* udev-rules.c */ -struct udev_rules; -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); -struct udev_rules *udev_rules_unref(struct udev_rules *rules); -bool udev_rules_check_timestamp(struct udev_rules *rules); -void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, - usec_t timeout_usec, usec_t timeout_warn_usec, - struct udev_list *properties_list); -int udev_rules_apply_static_dev_perms(struct udev_rules *rules); - -/* udev-event.c */ -struct udev_event *udev_event_new(struct udev_device *dev); -void udev_event_unref(struct udev_event *event); -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); -int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, - char *result, size_t maxsize, int read_value); -int udev_event_spawn(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - bool accept_failure, - const char *cmd, char *result, size_t ressize); -void udev_event_execute_rules(struct udev_event *event, - usec_t timeout_usec, usec_t timeout_warn_usec, - struct udev_list *properties_list, - struct udev_rules *rules); -void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec); -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); - -/* udev-watch.c */ -int udev_watch_init(struct udev *udev); -void udev_watch_restore(struct udev *udev); -void udev_watch_begin(struct udev *udev, struct udev_device *dev); -void udev_watch_end(struct udev *udev, struct udev_device *dev); -struct udev_device *udev_watch_lookup(struct udev *udev, int wd); - -/* udev-node.c */ -void udev_node_add(struct udev_device *dev, bool apply, - mode_t mode, uid_t uid, gid_t gid, - struct udev_list *seclabel_list); -void udev_node_remove(struct udev_device *dev); -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); - -/* udev-ctrl.c */ -struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(struct udev *udev); -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); -int udev_ctrl_cleanup(struct udev_ctrl *uctrl); -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); -int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); -struct udev_ctrl_connection; -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg; -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); - -/* built-in commands */ -enum udev_builtin_cmd { -#ifdef HAVE_BLKID - UDEV_BUILTIN_BLKID, -#endif - UDEV_BUILTIN_BTRFS, - UDEV_BUILTIN_HWDB, - UDEV_BUILTIN_INPUT_ID, - UDEV_BUILTIN_KEYBOARD, -#ifdef HAVE_KMOD - UDEV_BUILTIN_KMOD, -#endif - UDEV_BUILTIN_NET_ID, - UDEV_BUILTIN_NET_LINK, - UDEV_BUILTIN_PATH_ID, - UDEV_BUILTIN_USB_ID, -#ifdef HAVE_ACL - UDEV_BUILTIN_UACCESS, -#endif - UDEV_BUILTIN_MAX -}; -struct udev_builtin { - const char *name; - int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); - const char *help; - int (*init)(struct udev *udev); - void (*exit)(struct udev *udev); - bool (*validate)(struct udev *udev); - bool run_once; -}; -#ifdef HAVE_BLKID -extern const struct udev_builtin udev_builtin_blkid; -#endif -extern const struct udev_builtin udev_builtin_btrfs; -extern const struct udev_builtin udev_builtin_hwdb; -extern const struct udev_builtin udev_builtin_input_id; -extern const struct udev_builtin udev_builtin_keyboard; -#ifdef HAVE_KMOD -extern const struct udev_builtin udev_builtin_kmod; -#endif -extern const struct udev_builtin udev_builtin_net_id; -extern const struct udev_builtin udev_builtin_net_setup_link; -extern const struct udev_builtin udev_builtin_path_id; -extern const struct udev_builtin udev_builtin_usb_id; -extern const struct udev_builtin udev_builtin_uaccess; -void udev_builtin_init(struct udev *udev); -void udev_builtin_exit(struct udev *udev); -enum udev_builtin_cmd udev_builtin_lookup(const char *command); -const char *udev_builtin_name(enum udev_builtin_cmd cmd); -bool udev_builtin_run_once(enum udev_builtin_cmd cmd); -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); -void udev_builtin_list(struct udev *udev); -bool udev_builtin_validate(struct udev *udev); -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); -int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias, - const char *filter, bool test); - -/* udevadm commands */ -struct udevadm_cmd { - const char *name; - int (*cmd)(struct udev *udev, int argc, char *argv[]); - const char *help; - int debug; -}; -extern const struct udevadm_cmd udevadm_info; -extern const struct udevadm_cmd udevadm_trigger; -extern const struct udevadm_cmd udevadm_settle; -extern const struct udevadm_cmd udevadm_control; -extern const struct udevadm_cmd udevadm_monitor; -extern const struct udevadm_cmd udevadm_hwdb; -extern const struct udevadm_cmd udevadm_test; -extern const struct udevadm_cmd udevadm_test_builtin; diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in deleted file mode 100644 index a0c2e82d47..0000000000 --- a/src/udev/udev.pc.in +++ /dev/null @@ -1,5 +0,0 @@ -Name: udev -Description: udev -Version: @VERSION@ - -udevdir=@udevlibexecdir@ diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c deleted file mode 100644 index 989decbe95..0000000000 --- a/src/udev/udevadm-control.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2005-2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "udev-util.h" -#include "udev.h" - -static void print_help(void) { - printf("%s control COMMAND\n\n" - "Control the udev daemon.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -e --exit Instruct the daemon to cleanup and exit\n" - " -l --log-priority=LEVEL Set the udev log level for the daemon\n" - " -s --stop-exec-queue Do not execute events, queue only\n" - " -S --start-exec-queue Execute events, flush queue\n" - " -R --reload Reload rules and databases\n" - " -p --property=KEY=VALUE Set a global property for all events\n" - " -m --children-max=N Maximum number of children\n" - " --timeout=SECONDS Maximum time to block for a reply\n" - , program_invocation_short_name); -} - -static int adm_control(struct udev *udev, int argc, char *argv[]) { - _cleanup_udev_ctrl_unref_ struct udev_ctrl *uctrl = NULL; - int timeout = 60; - int rc = 1, c; - - static const struct option options[] = { - { "exit", no_argument, NULL, 'e' }, - { "log-priority", required_argument, NULL, 'l' }, - { "stop-exec-queue", no_argument, NULL, 's' }, - { "start-exec-queue", no_argument, NULL, 'S' }, - { "reload", no_argument, NULL, 'R' }, - { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */ - { "property", required_argument, NULL, 'p' }, - { "env", required_argument, NULL, 'p' }, /* alias for -p */ - { "children-max", required_argument, NULL, 'm' }, - { "timeout", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - return 1; - } - - uctrl = udev_ctrl_new(udev); - if (uctrl == NULL) - return 2; - - while ((c = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL)) >= 0) - switch (c) { - case 'e': - if (udev_ctrl_send_exit(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'l': { - int i; - - i = util_log_priority(optarg); - if (i < 0) { - fprintf(stderr, "invalid number '%s'\n", optarg); - return rc; - } - if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 's': - if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'S': - if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'R': - if (udev_ctrl_send_reload(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'p': - if (strchr(optarg, '=') == NULL) { - fprintf(stderr, "expect = instead of '%s'\n", optarg); - return rc; - } - if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'm': { - char *endp; - int i; - - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); - return rc; - } - if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 't': { - int seconds; - - seconds = atoi(optarg); - if (seconds >= 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - break; - } - case 'h': - print_help(); - rc = 0; - break; - } - - if (optind < argc) - fprintf(stderr, "Extraneous argument: %s\n", argv[optind]); - else if (optind == 1) - fprintf(stderr, "Option missing\n"); - return rc; -} - -const struct udevadm_cmd udevadm_control = { - .name = "control", - .cmd = adm_control, - .help = "Control the udev daemon", -}; diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c deleted file mode 100644 index 1bffe8e8ab..0000000000 --- a/src/udev/udevadm-hwdb.c +++ /dev/null @@ -1,698 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "fileio.h" -#include "fs-util.h" -#include "hwdb-internal.h" -#include "hwdb-util.h" -#include "label.h" -#include "mkdir.h" -#include "strbuf.h" -#include "string-util.h" -#include "udev.h" -#include "util.h" - -/* - * Generic udev properties, key/value database based on modalias strings. - * Uses a Patricia/radix trie to index all matches for efficient lookup. - */ - -static const char * const conf_file_dirs[] = { - "/etc/udev/hwdb.d", - UDEVLIBEXECDIR "/hwdb.d", - NULL -}; - -/* in-memory trie objects */ -struct trie { - struct trie_node *root; - struct strbuf *strings; - - size_t nodes_count; - size_t children_count; - size_t values_count; -}; - -struct trie_node { - /* prefix, common part for all children of this node */ - size_t prefix_off; - - /* sorted array of pointers to children nodes */ - struct trie_child_entry *children; - uint8_t children_count; - - /* sorted array of key/value pairs */ - struct trie_value_entry *values; - size_t values_count; -}; - -/* children array item with char (0-255) index */ -struct trie_child_entry { - uint8_t c; - struct trie_node *child; -}; - -/* value array item with key/value pairs */ -struct trie_value_entry { - size_t key_off; - size_t value_off; -}; - -static int trie_children_cmp(const void *v1, const void *v2) { - const struct trie_child_entry *n1 = v1; - const struct trie_child_entry *n2 = v2; - - return n1->c - n2->c; -} - -static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) { - struct trie_child_entry *child; - - /* extend array, add new entry, sort for bisection */ - child = realloc(node->children, (node->children_count + 1) * sizeof(struct trie_child_entry)); - if (!child) - return -ENOMEM; - - node->children = child; - trie->children_count++; - node->children[node->children_count].c = c; - node->children[node->children_count].child = node_child; - node->children_count++; - qsort(node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - trie->nodes_count++; - - return 0; -} - -static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) { - struct trie_child_entry *child; - struct trie_child_entry search; - - search.c = c; - child = bsearch(&search, node->children, node->children_count, sizeof(struct trie_child_entry), trie_children_cmp); - if (child) - return child->child; - return NULL; -} - -static void trie_node_cleanup(struct trie_node *node) { - size_t i; - - for (i = 0; i < node->children_count; i++) - trie_node_cleanup(node->children[i].child); - free(node->children); - free(node->values); - free(node); -} - -static int trie_values_cmp(const void *v1, const void *v2, void *arg) { - const struct trie_value_entry *val1 = v1; - const struct trie_value_entry *val2 = v2; - struct trie *trie = arg; - - return strcmp(trie->strings->buf + val1->key_off, - trie->strings->buf + val2->key_off); -} - -static int trie_node_add_value(struct trie *trie, struct trie_node *node, - const char *key, const char *value) { - ssize_t k, v; - struct trie_value_entry *val; - - k = strbuf_add_string(trie->strings, key, strlen(key)); - if (k < 0) - return k; - v = strbuf_add_string(trie->strings, value, strlen(value)); - if (v < 0) - return v; - - if (node->values_count) { - struct trie_value_entry search = { - .key_off = k, - .value_off = v, - }; - - val = xbsearch_r(&search, node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - if (val) { - /* replace existing earlier key with new value */ - val->value_off = v; - return 0; - } - } - - /* extend array, add new entry, sort for bisection */ - val = realloc(node->values, (node->values_count + 1) * sizeof(struct trie_value_entry)); - if (!val) - return -ENOMEM; - trie->values_count++; - node->values = val; - node->values[node->values_count].key_off = k; - node->values[node->values_count].value_off = v; - node->values_count++; - qsort_r(node->values, node->values_count, sizeof(struct trie_value_entry), trie_values_cmp, trie); - return 0; -} - -static int trie_insert(struct trie *trie, struct trie_node *node, const char *search, - const char *key, const char *value) { - size_t i = 0; - int err = 0; - - for (;;) { - size_t p; - uint8_t c; - struct trie_node *child; - - for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) { - _cleanup_free_ char *s = NULL; - ssize_t off; - _cleanup_free_ struct trie_node *new_child = NULL; - - if (c == search[i + p]) - continue; - - /* split node */ - new_child = new0(struct trie_node, 1); - if (!new_child) - return -ENOMEM; - - /* move values from parent to child */ - new_child->prefix_off = node->prefix_off + p+1; - new_child->children = node->children; - new_child->children_count = node->children_count; - new_child->values = node->values; - new_child->values_count = node->values_count; - - /* update parent; use strdup() because the source gets realloc()d */ - s = strndup(trie->strings->buf + node->prefix_off, p); - if (!s) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, s, p); - if (off < 0) - return off; - - node->prefix_off = off; - node->children = NULL; - node->children_count = 0; - node->values = NULL; - node->values_count = 0; - err = node_add_child(trie, node, new_child, c); - if (err) - return err; - - new_child = NULL; /* avoid cleanup */ - break; - } - i += p; - - c = search[i]; - if (c == '\0') - return trie_node_add_value(trie, node, key, value); - - child = node_lookup(node, c); - if (!child) { - ssize_t off; - - /* new child */ - child = new0(struct trie_node, 1); - if (!child) - return -ENOMEM; - - off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1)); - if (off < 0) { - free(child); - return off; - } - - child->prefix_off = off; - err = node_add_child(trie, node, child, c); - if (err) { - free(child); - return err; - } - - return trie_node_add_value(trie, child, key, value); - } - - node = child; - i++; - } -} - -struct trie_f { - FILE *f; - struct trie *trie; - uint64_t strings_off; - - uint64_t nodes_count; - uint64_t children_count; - uint64_t values_count; -}; - -/* calculate the storage space for the nodes, children arrays, value arrays */ -static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - - for (i = 0; i < node->children_count; i++) - trie_store_nodes_size(trie, node->children[i].child); - - trie->strings_off += sizeof(struct trie_node_f); - for (i = 0; i < node->children_count; i++) - trie->strings_off += sizeof(struct trie_child_entry_f); - for (i = 0; i < node->values_count; i++) - trie->strings_off += sizeof(struct trie_value_entry_f); -} - -static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { - uint64_t i; - struct trie_node_f n = { - .prefix_off = htole64(trie->strings_off + node->prefix_off), - .children_count = node->children_count, - .values_count = htole64(node->values_count), - }; - struct trie_child_entry_f *children = NULL; - int64_t node_off; - - if (node->children_count) { - children = new0(struct trie_child_entry_f, node->children_count); - if (!children) - return -ENOMEM; - } - - /* post-order recursion */ - for (i = 0; i < node->children_count; i++) { - int64_t child_off; - - child_off = trie_store_nodes(trie, node->children[i].child); - if (child_off < 0) { - free(children); - return child_off; - } - children[i].c = node->children[i].c; - children[i].child_off = htole64(child_off); - } - - /* write node */ - node_off = ftello(trie->f); - fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); - trie->nodes_count++; - - /* append children array */ - if (node->children_count) { - fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); - trie->children_count += node->children_count; - free(children); - } - - /* append values array */ - for (i = 0; i < node->values_count; i++) { - struct trie_value_entry_f v = { - .key_off = htole64(trie->strings_off + node->values[i].key_off), - .value_off = htole64(trie->strings_off + node->values[i].value_off), - }; - - fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); - trie->values_count++; - } - - return node_off; -} - -static int trie_store(struct trie *trie, const char *filename) { - struct trie_f t = { - .trie = trie, - }; - _cleanup_free_ char *filename_tmp = NULL; - int64_t pos; - int64_t root_off; - int64_t size; - struct trie_header_f h = { - .signature = HWDB_SIG, - .tool_version = htole64(atoi(VERSION)), - .header_size = htole64(sizeof(struct trie_header_f)), - .node_size = htole64(sizeof(struct trie_node_f)), - .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), - .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), - }; - int err; - - /* calculate size of header, nodes, children entries, value entries */ - t.strings_off = sizeof(struct trie_header_f); - trie_store_nodes_size(&t, trie->root); - - err = fopen_temporary(filename , &t.f, &filename_tmp); - if (err < 0) - return err; - fchmod(fileno(t.f), 0444); - - /* write nodes */ - err = fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - root_off = trie_store_nodes(&t, trie->root); - h.nodes_root_off = htole64(root_off); - pos = ftello(t.f); - h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); - - /* write string buffer */ - fwrite(trie->strings->buf, trie->strings->len, 1, t.f); - h.strings_len = htole64(trie->strings->len); - - /* write header */ - size = ftello(t.f); - h.file_size = htole64(size); - err = fseeko(t.f, 0, SEEK_SET); - if (err < 0) { - fclose(t.f); - unlink_noerrno(filename_tmp); - return -errno; - } - fwrite(&h, sizeof(struct trie_header_f), 1, t.f); - err = ferror(t.f); - if (err) - err = -errno; - fclose(t.f); - if (err < 0 || rename(filename_tmp, filename) < 0) { - unlink_noerrno(filename_tmp); - return err < 0 ? err : -errno; - } - - log_debug("=== trie on-disk ==="); - log_debug("size: %8"PRIi64" bytes", size); - log_debug("header: %8zu bytes", sizeof(struct trie_header_f)); - log_debug("nodes: %8"PRIu64" bytes (%8"PRIu64")", - t.nodes_count * sizeof(struct trie_node_f), t.nodes_count); - log_debug("child pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.children_count * sizeof(struct trie_child_entry_f), t.children_count); - log_debug("value pointers: %8"PRIu64" bytes (%8"PRIu64")", - t.values_count * sizeof(struct trie_value_entry_f), t.values_count); - log_debug("string store: %8zu bytes", trie->strings->len); - log_debug("strings start: %8"PRIu64, t.strings_off); - - return 0; -} - -static int insert_data(struct trie *trie, struct udev_list *match_list, - char *line, const char *filename) { - char *value; - struct udev_list_entry *entry; - - value = strchr(line, '='); - if (!value) { - log_error("Error, key/value pair expected but got '%s' in '%s':", line, filename); - return -EINVAL; - } - - value[0] = '\0'; - value++; - - /* libudev requires properties to start with a space */ - while (isblank(line[0]) && isblank(line[1])) - line++; - - if (line[0] == '\0' || value[0] == '\0') { - log_error("Error, empty key or value '%s' in '%s':", line, filename); - return -EINVAL; - } - - udev_list_entry_foreach(entry, udev_list_get_entry(match_list)) - trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value); - - return 0; -} - -static int import_file(struct udev *udev, struct trie *trie, const char *filename) { - enum { - HW_MATCH, - HW_DATA, - HW_NONE, - } state = HW_NONE; - FILE *f; - char line[LINE_MAX]; - struct udev_list match_list; - - udev_list_init(udev, &match_list, false); - - f = fopen(filename, "re"); - if (f == NULL) - return -errno; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *pos; - - /* comment line */ - if (line[0] == '#') - continue; - - /* strip trailing comment */ - pos = strchr(line, '#'); - if (pos) - pos[0] = '\0'; - - /* strip trailing whitespace */ - len = strlen(line); - while (len > 0 && isspace(line[len-1])) - len--; - line[len] = '\0'; - - switch (state) { - case HW_NONE: - if (len == 0) - break; - - if (line[0] == ' ') { - log_error("Error, MATCH expected but got '%s' in '%s':", line, filename); - break; - } - - /* start of record, first match */ - state = HW_MATCH; - udev_list_entry_add(&match_list, line, NULL); - break; - - case HW_MATCH: - if (len == 0) { - log_error("Error, DATA expected but got empty line in '%s':", filename); - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - /* another match */ - if (line[0] != ' ') { - udev_list_entry_add(&match_list, line, NULL); - break; - } - - /* first data */ - state = HW_DATA; - insert_data(trie, &match_list, line, filename); - break; - - case HW_DATA: - /* end of record */ - if (len == 0) { - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - if (line[0] != ' ') { - log_error("Error, DATA expected but got '%s' in '%s':", line, filename); - state = HW_NONE; - udev_list_cleanup(&match_list); - break; - } - - insert_data(trie, &match_list, line, filename); - break; - }; - } - - fclose(f); - udev_list_cleanup(&match_list); - return 0; -} - -static void help(void) { - printf("Usage: udevadm hwdb OPTIONS\n" - " -u,--update update the hardware database\n" - " --usr generate in " UDEVLIBEXECDIR " instead of /etc/udev\n" - " -t,--test=MODALIAS query database and print result\n" - " -r,--root=PATH alternative root path in the filesystem\n" - " -h,--help\n\n"); -} - -static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { - enum { - ARG_USR = 0x100, - }; - - static const struct option options[] = { - { "update", no_argument, NULL, 'u' }, - { "usr", no_argument, NULL, ARG_USR }, - { "test", required_argument, NULL, 't' }, - { "root", required_argument, NULL, 'r' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - const char *test = NULL; - const char *root = ""; - const char *hwdb_bin_dir = "/etc/udev"; - bool update = false; - struct trie *trie = NULL; - int err, c; - int rc = EXIT_SUCCESS; - - while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0) - switch(c) { - case 'u': - update = true; - break; - case ARG_USR: - hwdb_bin_dir = UDEVLIBEXECDIR; - break; - case 't': - test = optarg; - break; - case 'r': - root = optarg; - break; - case 'h': - help(); - return EXIT_SUCCESS; - case '?': - return EXIT_FAILURE; - default: - assert_not_reached("Unknown option"); - } - - if (!update && !test) { - log_error("Either --update or --test must be used"); - return EXIT_FAILURE; - } - - if (update) { - char **files, **f; - _cleanup_free_ char *hwdb_bin = NULL; - - trie = new0(struct trie, 1); - if (!trie) { - rc = EXIT_FAILURE; - goto out; - } - - /* string store */ - trie->strings = strbuf_new(); - if (!trie->strings) { - rc = EXIT_FAILURE; - goto out; - } - - /* index */ - trie->root = new0(struct trie_node, 1); - if (!trie->root) { - rc = EXIT_FAILURE; - goto out; - } - trie->nodes_count++; - - err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); - if (err < 0) { - log_error_errno(err, "failed to enumerate hwdb files: %m"); - rc = EXIT_FAILURE; - goto out; - } - STRV_FOREACH(f, files) { - log_debug("reading file '%s'", *f); - import_file(udev, trie, *f); - } - strv_free(files); - - strbuf_complete(trie->strings); - - log_debug("=== trie in-memory ==="); - log_debug("nodes: %8zu bytes (%8zu)", - trie->nodes_count * sizeof(struct trie_node), trie->nodes_count); - log_debug("children arrays: %8zu bytes (%8zu)", - trie->children_count * sizeof(struct trie_child_entry), trie->children_count); - log_debug("values arrays: %8zu bytes (%8zu)", - trie->values_count * sizeof(struct trie_value_entry), trie->values_count); - log_debug("strings: %8zu bytes", - trie->strings->len); - log_debug("strings incoming: %8zu bytes (%8zu)", - trie->strings->in_len, trie->strings->in_count); - log_debug("strings dedup'ed: %8zu bytes (%8zu)", - trie->strings->dedup_len, trie->strings->dedup_count); - - hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin", NULL); - if (!hwdb_bin) { - rc = EXIT_FAILURE; - goto out; - } - - mkdir_parents_label(hwdb_bin, 0755); - - err = trie_store(trie, hwdb_bin); - if (err < 0) { - log_error_errno(err, "Failure writing database %s: %m", hwdb_bin); - rc = EXIT_FAILURE; - } - - label_fix(hwdb_bin, false, false); - } - - if (test) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - int r; - - r = sd_hwdb_new(&hwdb); - if (r >= 0) { - const char *key, *value; - - SD_HWDB_FOREACH_PROPERTY(hwdb, test, key, value) - printf("%s=%s\n", key, value); - } - } -out: - if (trie) { - if (trie->root) - trie_node_cleanup(trie->root); - strbuf_cleanup(trie->strings); - free(trie); - } - return rc; -} - -const struct udevadm_cmd udevadm_hwdb = { - .name = "hwdb", - .cmd = adm_hwdb, -}; diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c deleted file mode 100644 index 6753c52005..0000000000 --- a/src/udev/udevadm-info.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright (C) 2004-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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) { - static const char* const skip[] = { - "uevent", - "dev", - "modalias", - "resource", - "driver", - "subsystem", - "module", - }; - unsigned int i; - - for (i = 0; i < ELEMENTSOF(skip); i++) - if (streq(name, skip[i])) - return true; - return false; -} - -static void print_all_attributes(struct udev_device *device, const char *key) { - struct udev_list_entry *sysattr; - - udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { - const char *name; - const char *value; - size_t len; - - name = udev_list_entry_get_name(sysattr); - if (skip_attribute(name)) - continue; - - value = udev_device_get_sysattr_value(device, name); - if (value == NULL) - continue; - - /* skip any values that look like a path */ - if (value[0] == '/') - continue; - - /* skip nonprintable attributes */ - len = strlen(value); - while (len > 0 && isprint(value[len-1])) - len--; - if (len > 0) - continue; - - printf(" %s{%s}==\"%s\"\n", key, name, value); - } - printf("\n"); -} - -static int print_device_chain(struct udev_device *device) { - struct udev_device *device_parent; - const char *str; - - printf("\n" - "Udevadm info starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", udev_device_get_devpath(device)); - printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); - str = udev_device_get_subsystem(device); - if (str == NULL) - str = ""; - printf(" SUBSYSTEM==\"%s\"\n", str); - str = udev_device_get_driver(device); - if (str == NULL) - str = ""; - printf(" DRIVER==\"%s\"\n", str); - print_all_attributes(device, "ATTR"); - - device_parent = device; - do { - device_parent = udev_device_get_parent(device_parent); - if (device_parent == NULL) - break; - printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); - printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); - str = udev_device_get_subsystem(device_parent); - if (str == NULL) - str = ""; - printf(" SUBSYSTEMS==\"%s\"\n", str); - str = udev_device_get_driver(device_parent); - if (str == NULL) - str = ""; - printf(" DRIVERS==\"%s\"\n", str); - print_all_attributes(device_parent, "ATTRS"); - } while (device_parent != NULL); - - return 0; -} - -static void print_record(struct udev_device *device) { - const char *str; - int i; - struct udev_list_entry *list_entry; - - printf("P: %s\n", udev_device_get_devpath(device)); - - str = udev_device_get_devnode(device); - if (str != NULL) - printf("N: %s\n", str + strlen("/dev/")); - - i = udev_device_get_devlink_priority(device); - if (i != 0) - printf("L: %i\n", i); - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) - printf("S: %s\n", udev_list_entry_get_name(list_entry) + strlen("/dev/")); - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("E: %s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); -} - -static int stat_device(const char *name, bool export, const char *prefix) { - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -errno; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%u\n" - "%sMINOR=%u\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%u:%u\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -static int export_devices(struct udev *udev) { - _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -ENOMEM; - - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - _cleanup_udev_device_unref_ struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device != NULL) - print_record(device); - } - - return 0; -} - -static void cleanup_dir(DIR *dir, mode_t mask, int depth) { - struct dirent *dent; - - if (depth <= 0) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - if ((stats.st_mode & mask) != 0) - continue; - if (S_ISDIR(stats.st_mode)) { - _cleanup_closedir_ DIR *dir2; - - dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2 != NULL) - cleanup_dir(dir2, mask, depth-1); - - (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); - } else - (void) unlinkat(dirfd(dir), dent->d_name, 0); - } -} - -static void cleanup_db(struct udev *udev) { - _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL; - - (void) unlink("/run/udev/queue.bin"); - - dir1 = opendir("/run/udev/data"); - if (dir1 != NULL) - cleanup_dir(dir1, S_ISVTX, 1); - - dir2 = opendir("/run/udev/links"); - if (dir2 != NULL) - cleanup_dir(dir2, 0, 2); - - dir3 = opendir("/run/udev/tags"); - if (dir3 != NULL) - cleanup_dir(dir3, 0, 2); - - dir4 = opendir("/run/udev/static_node-tags"); - if (dir4 != NULL) - cleanup_dir(dir4, 0, 2); - - dir5 = opendir("/run/udev/watch"); - if (dir5 != NULL) - cleanup_dir(dir5, 0, 1); -} - -static void help(void) { - - printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n" - "Query sysfs or the udev database.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n" - " -q --query=TYPE Query device information:\n" - " name Name of device node\n" - " symlink Pointing to node\n" - " path sysfs device path\n" - " property The device properties\n" - " all All values\n" - " -p --path=SYSPATH sysfs device path used for query or attribute walk\n" - " -n --name=NAME Node or symlink name used for query or attribute walk\n" - " -r --root Prepend dev directory to path names\n" - " -a --attribute-walk Print all key matches walking along the chain\n" - " of parent devices\n" - " -d --device-id-of-file=FILE Print major:minor of device containing this file\n" - " -x --export Export key/value pairs\n" - " -P --export-prefix Export the key name with a prefix\n" - " -e --export-db Export the content of the udev database\n" - " -c --cleanup-db Clean up the udev database\n" - , program_invocation_short_name); -} - -static int uinfo(struct udev *udev, int argc, char *argv[]) { - _cleanup_udev_device_unref_ struct udev_device *device = NULL; - bool root = 0; - bool export = 0; - const char *export_prefix = NULL; - char name[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int c; - - static const struct option options[] = { - { "name", required_argument, NULL, 'n' }, - { "path", required_argument, NULL, 'p' }, - { "query", required_argument, NULL, 'q' }, - { "attribute-walk", no_argument, NULL, 'a' }, - { "cleanup-db", no_argument, NULL, 'c' }, - { "export-db", no_argument, NULL, 'e' }, - { "root", no_argument, NULL, 'r' }, - { "device-id-of-file", required_argument, NULL, 'd' }, - { "export", no_argument, NULL, 'x' }, - { "export-prefix", required_argument, NULL, 'P' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_DEVICE_ID_FILE, - } action = ACTION_QUERY; - - enum query_type { - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_PROPERTY, - QUERY_ALL, - } query = QUERY_ALL; - - while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0) - switch (c) { - case 'n': { - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - return 2; - } - - device = find_device(udev, optarg, "/dev/"); - if (device == NULL) { - fprintf(stderr, "device node not found\n"); - return 2; - } - break; - } - case 'p': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - return 2; - } - - device = find_device(udev, optarg, "/sys"); - if (device == NULL) { - fprintf(stderr, "syspath not found\n"); - return 2; - } - break; - case 'q': - action = ACTION_QUERY; - if (streq(optarg, "property") || streq(optarg, "env")) - query = QUERY_PROPERTY; - else if (streq(optarg, "name")) - query = QUERY_NAME; - else if (streq(optarg, "symlink")) - query = QUERY_SYMLINK; - else if (streq(optarg, "path")) - query = QUERY_PATH; - else if (streq(optarg, "all")) - query = QUERY_ALL; - else { - fprintf(stderr, "unknown query type\n"); - return 3; - } - break; - case 'r': - root = true; - break; - case 'd': - action = ACTION_DEVICE_ID_FILE; - strscpy(name, sizeof(name), optarg); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - if (export_devices(udev) < 0) - return 1; - return 0; - case 'c': - cleanup_db(udev); - return 0; - case 'x': - export = true; - break; - case 'P': - export_prefix = optarg; - break; - case 'V': - printf("%s\n", VERSION); - return 0; - case 'h': - help(); - return 0; - default: - return 1; - } - - switch (action) { - case ACTION_QUERY: - if (!device) { - if (!argv[optind]) { - help(); - return 2; - } - device = find_device(udev, argv[optind], NULL); - if (!device) { - fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); - return 4; - } - } - - switch(query) { - case QUERY_NAME: { - const char *node = udev_device_get_devnode(device); - - if (node == NULL) { - fprintf(stderr, "no device node found\n"); - return 5; - } - - if (root) - printf("%s\n", udev_device_get_devnode(device)); - else - printf("%s\n", udev_device_get_devnode(device) + strlen("/dev/")); - break; - } - case QUERY_SYMLINK: - list_entry = udev_device_get_devlinks_list_entry(device); - while (list_entry != NULL) { - if (root) - printf("%s", udev_list_entry_get_name(list_entry)); - else - printf("%s", udev_list_entry_get_name(list_entry) + strlen("/dev/")); - list_entry = udev_list_entry_get_next(list_entry); - if (list_entry != NULL) - printf(" "); - } - printf("\n"); - break; - case QUERY_PATH: - printf("%s\n", udev_device_get_devpath(device)); - return 0; - case QUERY_PROPERTY: - list_entry = udev_device_get_properties_list_entry(device); - while (list_entry != NULL) { - if (export) - printf("%s%s='%s'\n", strempty(export_prefix), - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - else - printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - - list_entry = udev_list_entry_get_next(list_entry); - } - break; - case QUERY_ALL: - print_record(device); - break; - default: - assert_not_reached("unknown query type"); - } - break; - case ACTION_ATTRIBUTE_WALK: - if (!device && argv[optind]) { - device = find_device(udev, argv[optind], NULL); - if (!device) { - fprintf(stderr, "Unknown device, absolute path in /dev/ or /sys expected.\n"); - return 4; - } - } - if (!device) { - fprintf(stderr, "Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected.\n"); - return 4; - } - print_device_chain(device); - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - return 1; - break; - } - - return 0; -} - -const struct udevadm_cmd udevadm_info = { - .name = "info", - .cmd = uinfo, - .help = "Query sysfs or the udev database", -}; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c deleted file mode 100644 index f656c2198e..0000000000 --- a/src/udev/udevadm-monitor.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "formats-util.h" -#include "udev-util.h" -#include "udev.h" - -static bool udev_exit; - -static void sig_handler(int signum) { - if (signum == SIGINT || signum == SIGTERM) - udev_exit = true; -} - -static void print_device(struct udev_device *device, const char *source, int prop) { - struct timespec ts; - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", - source, - ts.tv_sec, ts.tv_nsec/1000, - udev_device_get_action(device), - udev_device_get_devpath(device), - udev_device_get_subsystem(device)); - if (prop) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); - } -} - -static void help(void) { - printf("%s monitor [--property] [--kernel] [--udev] [--help]\n\n" - "Listen to kernel and udev events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -p --property Print the event properties\n" - " -k --kernel Print kernel uevents\n" - " -u --udev Print udev events\n" - " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n" - " -t --tag-match=TAG Filter events by tag\n" - , program_invocation_short_name); -} - -static int adm_monitor(struct udev *udev, int argc, char *argv[]) { - struct sigaction act = {}; - sigset_t mask; - bool prop = false; - bool print_kernel = false; - bool print_udev = false; - _cleanup_udev_list_cleanup_ struct udev_list subsystem_match_list; - _cleanup_udev_list_cleanup_ struct udev_list tag_match_list; - _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor = NULL; - _cleanup_udev_monitor_unref_ struct udev_monitor *kernel_monitor = NULL; - _cleanup_close_ int fd_ep = -1; - int fd_kernel = -1, fd_udev = -1; - struct epoll_event ep_kernel, ep_udev; - int c; - - static const struct option options[] = { - { "property", no_argument, NULL, 'p' }, - { "environment", no_argument, NULL, 'e' }, /* alias for -p */ - { "kernel", no_argument, NULL, 'k' }, - { "udev", no_argument, NULL, 'u' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "tag-match", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - udev_list_init(udev, &subsystem_match_list, true); - udev_list_init(udev, &tag_match_list, true); - - while ((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0) - switch (c) { - case 'p': - case 'e': - prop = true; - break; - case 'k': - print_kernel = true; - break; - case 'u': - print_udev = true; - break; - case 's': - { - char subsys[UTIL_NAME_SIZE]; - char *devtype; - - strscpy(subsys, sizeof(subsys), optarg); - devtype = strchr(subsys, '/'); - if (devtype != NULL) { - devtype[0] = '\0'; - devtype++; - } - udev_list_entry_add(&subsystem_match_list, subsys, devtype); - break; - } - case 't': - udev_list_entry_add(&tag_match_list, optarg, NULL); - break; - case 'h': - help(); - return 0; - default: - return 1; - } - - if (!print_kernel && !print_udev) { - print_kernel = true; - print_udev = true; - } - - /* set signal handlers */ - act.sa_handler = sig_handler; - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - /* Callers are expecting to see events as they happen: Line buffering */ - setlinebuf(stdout); - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - log_error_errno(errno, "error creating epoll fd: %m"); - return 1; - } - - printf("monitor will print the received events for:\n"); - if (print_udev) { - struct udev_list_entry *entry; - - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - return 1; - } - udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); - fd_udev = udev_monitor_get_fd(udev_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - const char *devtype = udev_list_entry_get_value(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { - const char *tag = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) - fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); - } - - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to udev events\n"); - return 2; - } - - memzero(&ep_udev, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - log_error_errno(errno, "fail to add fd to epoll: %m"); - return 2; - } - - printf("UDEV - the event which udev sends out after rule processing\n"); - } - - if (print_kernel) { - struct udev_list_entry *entry; - - kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (kernel_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - return 3; - } - udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - fd_kernel = udev_monitor_get_fd(kernel_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - if (udev_monitor_enable_receiving(kernel_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to kernel events\n"); - return 4; - } - - memzero(&ep_kernel, sizeof(struct epoll_event)); - ep_kernel.events = EPOLLIN; - ep_kernel.data.fd = fd_kernel; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { - log_error_errno(errno, "fail to add fd to epoll: %m"); - return 5; - } - - printf("KERNEL - the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - int fdcount; - struct epoll_event ev[4]; - int i; - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %m\n"); - continue; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(kernel_monitor); - if (device == NULL) - continue; - print_device(device, "KERNEL", prop); - udev_device_unref(device); - } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) - continue; - print_device(device, "UDEV", prop); - udev_device_unref(device); - } - } - } - - return 0; -} - -const struct udevadm_cmd udevadm_monitor = { - .name = "monitor", - .cmd = adm_monitor, - .help = "Listen to kernel and udev events", -}; diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c deleted file mode 100644 index 6a5dc6e9e4..0000000000 --- a/src/udev/udevadm-settle.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2006-2009 Kay Sievers - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "parse-util.h" -#include "udev.h" -#include "util.h" - -static void help(void) { - printf("%s settle OPTIONS\n\n" - "Wait for pending udev events.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -t --timeout=SECONDS Maximum time to wait for events\n" - " -E --exit-if-exists=FILE Stop waiting if file exists\n" - , program_invocation_short_name); -} - -static int adm_settle(struct udev *udev, int argc, char *argv[]) { - static const struct option options[] = { - { "timeout", required_argument, NULL, 't' }, - { "exit-if-exists", required_argument, NULL, 'E' }, - { "help", no_argument, NULL, 'h' }, - { "seq-start", required_argument, NULL, 's' }, /* removed */ - { "seq-end", required_argument, NULL, 'e' }, /* removed */ - { "quiet", no_argument, NULL, 'q' }, /* removed */ - {} - }; - usec_t deadline; - const char *exists = NULL; - unsigned int timeout = 120; - struct pollfd pfd[1] = { {.fd = -1}, }; - int c; - struct udev_queue *queue; - int rc = EXIT_FAILURE; - - while ((c = getopt_long(argc, argv, "t:E:hs:e:q", options, NULL)) >= 0) { - switch (c) { - - case 't': { - int r; - - r = safe_atou(optarg, &timeout); - if (r < 0) { - log_error_errno(r, "Invalid timeout value '%s': %m", optarg); - return EXIT_FAILURE; - } - break; - } - - case 'E': - exists = optarg; - break; - - case 'h': - help(); - return EXIT_SUCCESS; - - case 's': - case 'e': - case 'q': - log_info("Option -%c no longer supported.", c); - return EXIT_FAILURE; - - case '?': - return EXIT_FAILURE; - - default: - assert_not_reached("Unknown argument"); - } - } - - if (optind < argc) { - fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]); - return EXIT_FAILURE; - } - - deadline = now(CLOCK_MONOTONIC) + timeout * USEC_PER_SEC; - - /* guarantee that the udev daemon isn't pre-processing */ - if (getuid() == 0) { - struct udev_ctrl *uctrl; - - uctrl = udev_ctrl_new(udev); - if (uctrl != NULL) { - if (udev_ctrl_send_ping(uctrl, MAX(5U, timeout)) < 0) { - log_debug("no connection to daemon"); - udev_ctrl_unref(uctrl); - return EXIT_SUCCESS; - } - udev_ctrl_unref(uctrl); - } - } - - queue = udev_queue_new(udev); - if (!queue) { - log_error("unable to get udev queue"); - return EXIT_FAILURE; - } - - pfd[0].events = POLLIN; - pfd[0].fd = udev_queue_get_fd(queue); - if (pfd[0].fd < 0) { - log_debug("queue is empty, nothing to watch"); - rc = EXIT_SUCCESS; - goto out; - } - - for (;;) { - if (exists && access(exists, F_OK) >= 0) { - rc = EXIT_SUCCESS; - break; - } - - /* exit if queue is empty */ - if (udev_queue_get_queue_is_empty(queue)) { - rc = EXIT_SUCCESS; - break; - } - - if (now(CLOCK_MONOTONIC) >= deadline) - break; - - /* wake up when queue is empty */ - if (poll(pfd, 1, MSEC_PER_SEC) > 0 && pfd[0].revents & POLLIN) - udev_queue_flush(queue); - } - -out: - udev_queue_unref(queue); - return rc; -} - -const struct udevadm_cmd udevadm_settle = { - .name = "settle", - .cmd = adm_settle, - .help = "Wait for pending udev events", -}; diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c deleted file mode 100644 index 0b180d03eb..0000000000 --- a/src/udev/udevadm-test-builtin.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#include "string-util.h" -#include "udev.h" - -static void help(struct udev *udev) { - printf("%s builtin [--help] COMMAND SYSPATH\n\n" - "Test a built-in command.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n\n" - "Commands:\n" - , program_invocation_short_name); - - udev_builtin_list(udev); -} - -static int adm_builtin(struct udev *udev, int argc, char *argv[]) { - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - char *command = NULL; - char *syspath = NULL; - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev = NULL; - enum udev_builtin_cmd cmd; - int rc = EXIT_SUCCESS, c; - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch (c) { - case 'h': - help(udev); - goto out; - } - - command = argv[optind++]; - if (command == NULL) { - fprintf(stderr, "command missing\n"); - help(udev); - rc = 2; - goto out; - } - - syspath = argv[optind++]; - if (syspath == NULL) { - fprintf(stderr, "syspath missing\n"); - rc = 3; - goto out; - } - - udev_builtin_init(udev); - - cmd = udev_builtin_lookup(command); - if (cmd >= UDEV_BUILTIN_MAX) { - fprintf(stderr, "unknown command '%s'\n", command); - help(udev); - rc = 5; - goto out; - } - - /* add /sys if needed */ - if (!startswith(syspath, "/sys")) - strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); - else - strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n\n", filename); - rc = 4; - goto out; - } - - rc = udev_builtin_run(dev, cmd, command, true); - if (rc < 0) { - fprintf(stderr, "error executing '%s', exit code %i\n\n", command, rc); - rc = 6; - } -out: - udev_device_unref(dev); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test_builtin = { - .name = "test-builtin", - .cmd = adm_builtin, - .help = "Test a built-in command", - .debug = true, -}; diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c deleted file mode 100644 index 702dbe5282..0000000000 --- a/src/udev/udevadm-test.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" - -static void help(void) { - - printf("%s test OPTIONS \n\n" - "Test an event run.\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -a --action=ACTION Set action string\n" - " -N --resolve-names=early|late|never When to resolve names\n" - , program_invocation_short_name); -} - -static int adm_test(struct udev *udev, int argc, char *argv[]) { - int resolve_names = 1; - char filename[UTIL_PATH_SIZE]; - const char *action = "add"; - const char *syspath = NULL; - struct udev_list_entry *entry; - _cleanup_udev_rules_unref_ struct udev_rules *rules = NULL; - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - _cleanup_udev_event_unref_ struct udev_event *event = NULL; - sigset_t mask, sigmask_orig; - int rc = 0, c; - - static const struct option options[] = { - { "action", required_argument, NULL, 'a' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - log_debug("version %s", VERSION); - - while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) - switch (c) { - case 'a': - action = optarg; - break; - case 'N': - if (streq (optarg, "early")) { - resolve_names = 1; - } else if (streq (optarg, "late")) { - resolve_names = 0; - } else if (streq (optarg, "never")) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - log_error("resolve-names must be early, late or never"); - exit(EXIT_FAILURE); - } - break; - case 'h': - help(); - exit(EXIT_SUCCESS); - case '?': - exit(EXIT_FAILURE); - default: - assert_not_reached("Unknown option"); - } - - syspath = argv[optind]; - if (syspath == NULL) { - fprintf(stderr, "syspath parameter missing\n"); - rc = 2; - goto out; - } - - printf("This program is for debugging only, it does not run any program\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - fprintf(stderr, "error reading rules\n"); - rc = 3; - goto out; - } - - /* add /sys if needed */ - if (!startswith(syspath, "/sys")) - strscpyl(filename, sizeof(filename), "/sys", syspath, NULL); - else - strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_synthetic_event(udev, filename, action); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", filename); - rc = 4; - goto out; - } - - /* don't read info from the db */ - udev_device_set_info_loaded(dev); - - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - - udev_event_execute_rules(event, - 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, - NULL, - rules); - - udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) - printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); - - udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { - char program[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); - printf("run: '%s'\n", program); - } -out: - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test = { - .name = "test", - .cmd = adm_test, - .help = "Test an event run", - .debug = true, -}; diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c deleted file mode 100644 index 9d52345d92..0000000000 --- a/src/udev/udevadm-trigger.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "string-util.h" -#include "udev-util.h" -#include "udev.h" -#include "udevadm-util.h" -#include "util.h" - -static int verbose; -static int dry_run; - -static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { - struct udev_list_entry *entry; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { - char filename[UTIL_PATH_SIZE]; - int fd; - - if (verbose) - printf("%s\n", udev_list_entry_get_name(entry)); - if (dry_run) - continue; - strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); - fd = open(filename, O_WRONLY|O_CLOEXEC); - if (fd < 0) - continue; - if (write(fd, action, strlen(action)) < 0) - log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); - close(fd); - } -} - -static const char *keyval(const char *str, const char **val, char *buf, size_t size) { - char *pos; - - strscpy(buf, size,str); - pos = strchr(buf, '='); - if (pos != NULL) { - pos[0] = 0; - pos++; - } - *val = pos; - return buf; -} - -static void help(void) { - printf("%s trigger OPTIONS\n\n" - "Request events from the kernel.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -v --verbose Print the list of devices while running\n" - " -n --dry-run Do not actually trigger the events\n" - " -t --type= Type of events to trigger\n" - " devices sysfs devices (default)\n" - " subsystems sysfs subsystems and drivers\n" - " -c --action=ACTION Event action value, default is \"change\"\n" - " -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n" - " -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n" - " -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n" - " -A --attr-nomatch=FILE[=VALUE] Exclude devices with a matching attribute\n" - " -p --property-match=KEY=VALUE Trigger devices with a matching property\n" - " -g --tag-match=KEY=VALUE Trigger devices with a matching property\n" - " -y --sysname-match=NAME Trigger devices with this /sys path\n" - " --name-match=NAME Trigger devices with this /dev name\n" - " -b --parent-match=NAME Trigger devices with that parent device\n" - , program_invocation_short_name); -} - -static int adm_trigger(struct udev *udev, int argc, char *argv[]) { - enum { - ARG_NAME = 0x100, - }; - - static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "name-match", required_argument, NULL, ARG_NAME }, - { "parent-match", required_argument, NULL, 'b' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - enum { - TYPE_DEVICES, - TYPE_SUBSYSTEMS, - } device_type = TYPE_DEVICES; - const char *action = "change"; - _cleanup_udev_enumerate_unref_ struct udev_enumerate *udev_enumerate = NULL; - int c, r; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return 1; - - while ((c = getopt_long(argc, argv, "vno:t:c:s:S:a:A:p:g:y:b:h", options, NULL)) >= 0) { - const char *key; - const char *val; - char buf[UTIL_PATH_SIZE]; - - switch (c) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 't': - if (streq(optarg, "devices")) - device_type = TYPE_DEVICES; - else if (streq(optarg, "subsystems")) - device_type = TYPE_SUBSYSTEMS; - else { - log_error("unknown type --type=%s", optarg); - return 2; - } - break; - case 'c': - if (!nulstr_contains("add\0" "remove\0" "change\0", optarg)) { - log_error("unknown action '%s'", optarg); - return 2; - } else - action = optarg; - - break; - case 's': - r = udev_enumerate_add_match_subsystem(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add subsystem match '%s': %m", optarg); - return 2; - } - break; - case 'S': - r = udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add negative subsystem match '%s': %m", optarg); - return 2; - } - break; - case 'a': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_match_sysattr(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add sysattr match '%s=%s': %m", key, val); - return 2; - } - break; - case 'A': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add negative sysattr match '%s=%s': %m", key, val); - return 2; - } - break; - case 'p': - key = keyval(optarg, &val, buf, sizeof(buf)); - r = udev_enumerate_add_match_property(udev_enumerate, key, val); - if (r < 0) { - log_error_errno(r, "could not add property match '%s=%s': %m", key, val); - return 2; - } - break; - case 'g': - r = udev_enumerate_add_match_tag(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add tag match '%s': %m", optarg); - return 2; - } - break; - case 'y': - r = udev_enumerate_add_match_sysname(udev_enumerate, optarg); - if (r < 0) { - log_error_errno(r, "could not add sysname match '%s': %m", optarg); - return 2; - } - break; - case 'b': { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, optarg, "/sys"); - if (dev == NULL) { - log_error("unable to open the device '%s'", optarg); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add parent match '%s': %m", optarg); - return 2; - } - break; - } - - case ARG_NAME: { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, optarg, "/dev/"); - if (dev == NULL) { - log_error("unable to open the device '%s'", optarg); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add parent match '%s': %m", optarg); - return 2; - } - break; - } - - case 'h': - help(); - return 0; - case '?': - return 1; - default: - assert_not_reached("Unknown option"); - } - } - - for (; optind < argc; optind++) { - _cleanup_udev_device_unref_ struct udev_device *dev; - - dev = find_device(udev, argv[optind], NULL); - if (dev == NULL) { - log_error("unable to open the device '%s'", argv[optind]); - return 2; - } - - r = udev_enumerate_add_match_parent(udev_enumerate, dev); - if (r < 0) { - log_error_errno(r, "could not add tag match '%s': %m", optarg); - return 2; - } - } - - switch (device_type) { - case TYPE_SUBSYSTEMS: - udev_enumerate_scan_subsystems(udev_enumerate); - exec_list(udev_enumerate, action); - return 0; - case TYPE_DEVICES: - udev_enumerate_scan_devices(udev_enumerate); - exec_list(udev_enumerate, action); - return 0; - default: - assert_not_reached("device_type"); - } -} - -const struct udevadm_cmd udevadm_trigger = { - .name = "trigger", - .cmd = adm_trigger, - .help = "Request events from the kernel", -}; diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c deleted file mode 100644 index 3539c1d6ab..0000000000 --- a/src/udev/udevadm-util.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "string-util.h" -#include "udevadm-util.h" - -struct udev_device *find_device(struct udev *udev, - const char *id, - const char *prefix) { - - assert(udev); - assert(id); - - if (prefix && !startswith(id, prefix)) - id = strjoina(prefix, id); - - if (startswith(id, "/dev/")) { - struct stat statbuf; - char type; - - if (stat(id, &statbuf) < 0) - return NULL; - - if (S_ISBLK(statbuf.st_mode)) - type = 'b'; - else if (S_ISCHR(statbuf.st_mode)) - type = 'c'; - else - return NULL; - - return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); - } else if (startswith(id, "/sys/")) - return udev_device_new_from_syspath(udev, id); - else - return NULL; -} diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h deleted file mode 100644 index dc712b0d93..0000000000 --- a/src/udev/udevadm-util.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/* - * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "udev.h" - -struct udev_device *find_device(struct udev *udev, - const char *id, - const char *prefix); diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c deleted file mode 100644 index a6a873e5de..0000000000 --- a/src/udev/udevadm.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2007-2012 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include "selinux-util.h" -#include "string-util.h" -#include "udev.h" - -static int adm_version(struct udev *udev, int argc, char *argv[]) { - printf("%s\n", VERSION); - return 0; -} - -static const struct udevadm_cmd udevadm_version = { - .name = "version", - .cmd = adm_version, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]); - -static const struct udevadm_cmd udevadm_help = { - .name = "help", - .cmd = adm_help, -}; - -static const struct udevadm_cmd *udevadm_cmds[] = { - &udevadm_info, - &udevadm_trigger, - &udevadm_settle, - &udevadm_control, - &udevadm_monitor, - &udevadm_hwdb, - &udevadm_test, - &udevadm_test_builtin, - &udevadm_version, - &udevadm_help, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]) { - unsigned int i; - - printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n" - "Send control commands or test the device manager.\n\n" - "Commands:\n" - , program_invocation_short_name); - - for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) - if (udevadm_cmds[i]->help != NULL) - printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); - return 0; -} - -static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) { - if (cmd->debug) - log_set_max_level(LOG_DEBUG); - log_debug("calling: %s", cmd->name); - return cmd->cmd(udev, argc, argv); -} - -int main(int argc, char *argv[]) { - struct udev *udev; - static const struct option options[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - const char *command; - unsigned int i; - int rc = 1, c; - - udev = udev_new(); - if (udev == NULL) - goto out; - - log_parse_environment(); - log_open(); - mac_selinux_init(); - - while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0) - switch (c) { - - case 'd': - log_set_max_level(LOG_DEBUG); - break; - - case 'h': - rc = adm_help(udev, argc, argv); - goto out; - - case 'V': - rc = adm_version(udev, argc, argv); - goto out; - - default: - goto out; - } - - command = argv[optind]; - - if (command != NULL) - for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++) - if (streq(udevadm_cmds[i]->name, command)) { - argc -= optind; - argv += optind; - /* we need '0' here to reset the internal state */ - optind = 0; - rc = run_command(udev, udevadm_cmds[i], argc, argv); - goto out; - } - - fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name); - rc = 2; -out: - mac_selinux_finish(); - udev_unref(udev); - log_close(); - return rc; -} diff --git a/src/udev/udevd.c b/src/udev/udevd.c deleted file mode 100644 index a893a2b3d9..0000000000 --- a/src/udev/udevd.c +++ /dev/null @@ -1,1764 +0,0 @@ -/* - * Copyright (C) 2004-2012 Kay Sievers - * Copyright (C) 2004 Chris Friesen - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "cgroup-util.h" -#include "cpu-set-util.h" -#include "dev-setup.h" -#include "fd-util.h" -#include "fileio.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; -static int arg_resolve_names = 1; -static unsigned arg_children_max; -static int arg_exec_delay; -static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; -static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3; - -typedef struct Manager { - struct udev *udev; - sd_event *event; - Hashmap *workers; - struct udev_list_node events; - const char *cgroup; - pid_t pid; /* the process that originally allocated the manager object */ - - struct udev_rules *rules; - struct udev_list properties; - - struct udev_monitor *monitor; - struct udev_ctrl *ctrl; - struct udev_ctrl_connection *ctrl_conn_blocking; - int fd_inotify; - int worker_watch[2]; - - sd_event_source *ctrl_event; - sd_event_source *uevent_event; - sd_event_source *inotify_event; - - usec_t last_usec; - - bool stop_exec_queue:1; - bool exit:1; -} Manager; - -enum event_state { - EVENT_UNDEF, - EVENT_QUEUED, - EVENT_RUNNING, -}; - -struct event { - struct udev_list_node node; - Manager *manager; - struct udev *udev; - struct udev_device *dev; - struct udev_device *dev_kernel; - struct worker *worker; - enum event_state state; - unsigned long long int delaying_seqnum; - unsigned long long int seqnum; - const char *devpath; - size_t devpath_len; - const char *devpath_old; - dev_t devnum; - int ifindex; - bool is_block; - sd_event_source *timeout_warning; - sd_event_source *timeout; -}; - -static inline struct event *node_to_event(struct udev_list_node *node) { - return container_of(node, struct event, node); -} - -static void event_queue_cleanup(Manager *manager, enum event_state type); - -enum worker_state { - WORKER_UNDEF, - WORKER_RUNNING, - WORKER_IDLE, - WORKER_KILLED, -}; - -struct worker { - Manager *manager; - struct udev_list_node node; - int refcount; - pid_t pid; - struct udev_monitor *monitor; - enum worker_state state; - struct event *event; -}; - -/* passed from worker to main process */ -struct worker_message { -}; - -static void event_free(struct event *event) { - int r; - - if (!event) - return; - - udev_list_node_remove(&event->node); - udev_device_unref(event->dev); - udev_device_unref(event->dev_kernel); - - sd_event_source_unref(event->timeout_warning); - sd_event_source_unref(event->timeout); - - if (event->worker) - event->worker->event = NULL; - - assert(event->manager); - - if (udev_list_node_is_empty(&event->manager->events)) { - /* only clean up the queue from the process that created it */ - if (event->manager->pid == getpid()) { - r = unlink("/run/udev/queue"); - if (r < 0) - log_warning_errno(errno, "could not unlink /run/udev/queue: %m"); - } - } - - free(event); -} - -static void worker_free(struct worker *worker) { - if (!worker) - return; - - assert(worker->manager); - - hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid)); - udev_monitor_unref(worker->monitor); - event_free(worker->event); - - free(worker); -} - -static void manager_workers_free(Manager *manager) { - struct worker *worker; - Iterator i; - - assert(manager); - - HASHMAP_FOREACH(worker, manager->workers, i) - worker_free(worker); - - manager->workers = hashmap_free(manager->workers); -} - -static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor *worker_monitor, pid_t pid) { - _cleanup_free_ struct worker *worker = NULL; - int r; - - assert(ret); - assert(manager); - assert(worker_monitor); - assert(pid > 1); - - worker = new0(struct worker, 1); - if (!worker) - return -ENOMEM; - - worker->refcount = 1; - worker->manager = manager; - /* close monitor, but keep address around */ - udev_monitor_disconnect(worker_monitor); - worker->monitor = udev_monitor_ref(worker_monitor); - worker->pid = pid; - - r = hashmap_ensure_allocated(&manager->workers, NULL); - if (r < 0) - return r; - - r = hashmap_put(manager->workers, PID_TO_PTR(pid), worker); - if (r < 0) - return r; - - *ret = worker; - worker = NULL; - - return 0; -} - -static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - struct event *event = userdata; - - assert(event); - assert(event->worker); - - kill_and_sigcont(event->worker->pid, SIGKILL); - event->worker->state = WORKER_KILLED; - - log_error("seq %llu '%s' killed", udev_device_get_seqnum(event->dev), event->devpath); - - return 1; -} - -static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { - struct event *event = userdata; - - assert(event); - - log_warning("seq %llu '%s' is taking a long time", udev_device_get_seqnum(event->dev), event->devpath); - - return 1; -} - -static void worker_attach_event(struct worker *worker, struct event *event) { - sd_event *e; - uint64_t usec; - - assert(worker); - assert(worker->manager); - assert(event); - assert(!event->worker); - assert(!worker->event); - - worker->state = WORKER_RUNNING; - worker->event = event; - event->state = EVENT_RUNNING; - event->worker = worker; - - e = worker->manager->event; - - assert_se(sd_event_now(e, clock_boottime_or_monotonic(), &usec) >= 0); - - (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(), - usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event); - - (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(), - usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event); -} - -static void manager_free(Manager *manager) { - if (!manager) - return; - - udev_builtin_exit(manager->udev); - - sd_event_source_unref(manager->ctrl_event); - sd_event_source_unref(manager->uevent_event); - sd_event_source_unref(manager->inotify_event); - - udev_unref(manager->udev); - sd_event_unref(manager->event); - manager_workers_free(manager); - event_queue_cleanup(manager, EVENT_UNDEF); - - udev_monitor_unref(manager->monitor); - udev_ctrl_unref(manager->ctrl); - udev_ctrl_connection_unref(manager->ctrl_conn_blocking); - - udev_list_cleanup(&manager->properties); - udev_rules_unref(manager->rules); - - safe_close(manager->fd_inotify); - safe_close_pair(manager->worker_watch); - - free(manager); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); - -static int worker_send_message(int fd) { - struct worker_message message = {}; - - return loop_write(fd, &message, sizeof(message), false); -} - -static void worker_spawn(Manager *manager, struct event *event) { - struct udev *udev = event->udev; - _cleanup_udev_monitor_unref_ struct udev_monitor *worker_monitor = NULL; - pid_t pid; - int r = 0; - - /* listen for new events */ - worker_monitor = udev_monitor_new_from_netlink(udev, NULL); - if (worker_monitor == NULL) - return; - /* allow the main daemon netlink address to send devices to the worker */ - udev_monitor_allow_unicast_sender(worker_monitor, manager->monitor); - r = udev_monitor_enable_receiving(worker_monitor); - if (r < 0) - log_error_errno(r, "worker: could not enable receiving of device: %m"); - - pid = fork(); - switch (pid) { - case 0: { - struct udev_device *dev = 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 }; - struct epoll_event ep_monitor = { .events = EPOLLIN }; - sigset_t mask; - - /* take initial device from queue */ - dev = event->dev; - event->dev = NULL; - - unsetenv("NOTIFY_SOCKET"); - - manager_workers_free(manager); - event_queue_cleanup(manager, EVENT_UNDEF); - - manager->monitor = udev_monitor_unref(manager->monitor); - manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking); - manager->ctrl = udev_ctrl_unref(manager->ctrl); - manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]); - - manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); - manager->uevent_event = sd_event_source_unref(manager->uevent_event); - manager->inotify_event = sd_event_source_unref(manager->inotify_event); - - manager->event = sd_event_unref(manager->event); - - sigfillset(&mask); - fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd_signal < 0) { - r = log_error_errno(errno, "error creating signalfd %m"); - goto out; - } - ep_signal.data.fd = fd_signal; - - fd_monitor = udev_monitor_get_fd(worker_monitor); - ep_monitor.data.fd = fd_monitor; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - r = log_error_errno(errno, "error creating epoll fd: %m"); - goto out; - } - - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { - r = log_error_errno(errno, "fail to add fds to epoll: %m"); - goto out; - } - - /* Request TERM signal if parent exits. - Ignore error, not much we can do in that case. */ - (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* Reset OOM score, we only protect the main daemon. */ - write_string_file("/proc/self/oom_score_adj", "0", 0); - - for (;;) { - struct udev_event *udev_event; - int fd_lock = -1; - - assert(dev); - - log_debug("seq %llu running", udev_device_get_seqnum(dev)); - udev_event = udev_event_new(dev); - if (udev_event == NULL) { - r = -ENOMEM; - goto out; - } - - if (arg_exec_delay > 0) - udev_event->exec_delay = arg_exec_delay; - - /* - * Take a shared lock on the device node; this establishes - * a concept of device "ownership" to serialize device - * access. External processes holding an exclusive lock will - * cause udev to skip the event handling; in the case udev - * acquired the lock, the external process can block until - * udev has finished its event handling. - */ - if (!streq_ptr(udev_device_get_action(dev), "remove") && - streq_ptr("block", udev_device_get_subsystem(dev)) && - !startswith(udev_device_get_sysname(dev), "dm-") && - !startswith(udev_device_get_sysname(dev), "md")) { - struct udev_device *d = dev; - - if (streq_ptr("partition", udev_device_get_devtype(d))) - d = udev_device_get_parent(d); - - if (d) { - fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); - if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) { - log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d)); - fd_lock = safe_close(fd_lock); - goto skip; - } - } - } - - /* needed for renaming netifs */ - udev_event->rtnl = rtnl; - - /* apply rules, create node, symlinks */ - udev_event_execute_rules(udev_event, - arg_event_timeout_usec, arg_event_timeout_warn_usec, - &manager->properties, - manager->rules); - - udev_event_execute_run(udev_event, - arg_event_timeout_usec, arg_event_timeout_warn_usec); - - if (udev_event->rtnl) - /* in case rtnl was initialized */ - rtnl = sd_netlink_ref(udev_event->rtnl); - - /* apply/restore inotify watch */ - if (udev_event->inotify_watch) { - udev_watch_begin(udev, dev); - udev_device_update_db(dev); - } - - safe_close(fd_lock); - - /* send processed event back to libudev listeners */ - udev_monitor_send_device(worker_monitor, NULL, dev); - -skip: - log_debug("seq %llu processed", udev_device_get_seqnum(dev)); - - /* send udevd the result of the event execution */ - r = worker_send_message(manager->worker_watch[WRITE_END]); - if (r < 0) - log_error_errno(r, "failed to send result of seq %llu to main daemon: %m", - udev_device_get_seqnum(dev)); - - udev_device_unref(dev); - dev = NULL; - - udev_event_unref(udev_event); - - /* wait for more device messages from main udevd, or term signal */ - while (dev == NULL) { - struct epoll_event ev[4]; - int fdcount; - int i; - - fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); - if (fdcount < 0) { - if (errno == EINTR) - continue; - r = log_error_errno(errno, "failed to poll: %m"); - goto out; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - break; - } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - switch (fdsi.ssi_signo) { - case SIGTERM: - goto out; - } - } - } - } - } -out: - udev_device_unref(dev); - manager_free(manager); - log_close(); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); - } - case -1: - event->state = EVENT_QUEUED; - log_error_errno(errno, "fork of child failed: %m"); - break; - default: - { - struct worker *worker; - - r = worker_new(&worker, manager, worker_monitor, pid); - if (r < 0) - return; - - worker_attach_event(worker, event); - - log_debug("seq %llu forked new worker ["PID_FMT"]", udev_device_get_seqnum(event->dev), pid); - break; - } - } -} - -static void event_run(Manager *manager, struct event *event) { - struct worker *worker; - Iterator i; - - assert(manager); - assert(event); - - HASHMAP_FOREACH(worker, manager->workers, i) { - ssize_t count; - - if (worker->state != WORKER_IDLE) - continue; - - count = udev_monitor_send_device(manager->monitor, worker->monitor, event->dev); - if (count < 0) { - log_error_errno(errno, "worker ["PID_FMT"] did not accept message %zi (%m), kill it", - worker->pid, count); - kill(worker->pid, SIGKILL); - worker->state = WORKER_KILLED; - continue; - } - worker_attach_event(worker, event); - return; - } - - if (hashmap_size(manager->workers) >= arg_children_max) { - if (arg_children_max > 1) - log_debug("maximum number (%i) of children reached", hashmap_size(manager->workers)); - return; - } - - /* start new worker and pass initial device */ - worker_spawn(manager, event); -} - -static int event_queue_insert(Manager *manager, struct udev_device *dev) { - struct event *event; - int r; - - assert(manager); - assert(dev); - - /* only one process can add events to the queue */ - if (manager->pid == 0) - manager->pid = getpid(); - - assert(manager->pid == getpid()); - - event = new0(struct event, 1); - if (!event) - return -ENOMEM; - - event->udev = udev_device_get_udev(dev); - event->manager = manager; - event->dev = dev; - event->dev_kernel = udev_device_shallow_clone(dev); - udev_device_copy_properties(event->dev_kernel, dev); - event->seqnum = udev_device_get_seqnum(dev); - event->devpath = udev_device_get_devpath(dev); - event->devpath_len = strlen(event->devpath); - event->devpath_old = udev_device_get_devpath_old(dev); - event->devnum = udev_device_get_devnum(dev); - event->is_block = streq("block", udev_device_get_subsystem(dev)); - event->ifindex = udev_device_get_ifindex(dev); - - log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev), - udev_device_get_action(dev), udev_device_get_subsystem(dev)); - - event->state = EVENT_QUEUED; - - if (udev_list_node_is_empty(&manager->events)) { - r = touch("/run/udev/queue"); - if (r < 0) - log_warning_errno(r, "could not touch /run/udev/queue: %m"); - } - - udev_list_node_append(&event->node, &manager->events); - - return 0; -} - -static void manager_kill_workers(Manager *manager) { - struct worker *worker; - Iterator i; - - assert(manager); - - HASHMAP_FOREACH(worker, manager->workers, i) { - if (worker->state == WORKER_KILLED) - continue; - - worker->state = WORKER_KILLED; - kill(worker->pid, SIGTERM); - } -} - -/* lookup event for identical, parent, child device */ -static bool is_devpath_busy(Manager *manager, struct event *event) { - struct udev_list_node *loop; - size_t common; - - /* check if queue contains events we depend on */ - udev_list_node_foreach(loop, &manager->events) { - struct event *loop_event = node_to_event(loop); - - /* we already found a later event, earlier can not block us, no need to check again */ - if (loop_event->seqnum < event->delaying_seqnum) - continue; - - /* event we checked earlier still exists, no need to check again */ - if (loop_event->seqnum == event->delaying_seqnum) - return true; - - /* found ourself, no later event can block us */ - if (loop_event->seqnum >= event->seqnum) - break; - - /* check major/minor */ - if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) - return true; - - /* check network device ifindex */ - if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) - return true; - - /* check our old name */ - if (event->devpath_old != NULL && streq(loop_event->devpath, event->devpath_old)) { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* compare devpath */ - common = MIN(loop_event->devpath_len, event->devpath_len); - - /* one devpath is contained in the other? */ - if (memcmp(loop_event->devpath, event->devpath, common) != 0) - continue; - - /* identical device event found */ - if (loop_event->devpath_len == event->devpath_len) { - /* devices names might have changed/swapped in the meantime */ - if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) - continue; - if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) - continue; - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* parent device event found */ - if (event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* child device event found */ - if (loop_event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* no matching device */ - continue; - } - - return false; -} - -static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish"); - - sd_event_exit(manager->event, -ETIMEDOUT); - - return 1; -} - -static void manager_exit(Manager *manager) { - uint64_t usec; - int r; - - assert(manager); - - manager->exit = true; - - sd_notify(false, - "STOPPING=1\n" - "STATUS=Starting shutdown..."); - - /* close sources of new events and discard buffered events */ - manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); - manager->ctrl = udev_ctrl_unref(manager->ctrl); - - manager->inotify_event = sd_event_source_unref(manager->inotify_event); - manager->fd_inotify = safe_close(manager->fd_inotify); - - manager->uevent_event = sd_event_source_unref(manager->uevent_event); - manager->monitor = udev_monitor_unref(manager->monitor); - - /* discard queued events and kill workers */ - event_queue_cleanup(manager, EVENT_QUEUED); - manager_kill_workers(manager); - - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); - - r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(), - usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager); - if (r < 0) - return; -} - -/* reload requested, HUP signal received, rules changed, builtin changed */ -static void manager_reload(Manager *manager) { - - assert(manager); - - sd_notify(false, - "RELOADING=1\n" - "STATUS=Flushing configuration..."); - - manager_kill_workers(manager); - manager->rules = udev_rules_unref(manager->rules); - udev_builtin_exit(manager->udev); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing..."); -} - -static void event_queue_start(Manager *manager) { - struct udev_list_node *loop; - usec_t usec; - - assert(manager); - - if (udev_list_node_is_empty(&manager->events) || - manager->exit || manager->stop_exec_queue) - return; - - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); - /* check for changed config, every 3 seconds at most */ - if (manager->last_usec == 0 || - (usec - manager->last_usec) > 3 * USEC_PER_SEC) { - if (udev_rules_check_timestamp(manager->rules) || - udev_builtin_validate(manager->udev)) - manager_reload(manager); - - manager->last_usec = usec; - } - - udev_builtin_init(manager->udev); - - if (!manager->rules) { - manager->rules = udev_rules_new(manager->udev, arg_resolve_names); - if (!manager->rules) - return; - } - - udev_list_node_foreach(loop, &manager->events) { - struct event *event = node_to_event(loop); - - if (event->state != EVENT_QUEUED) - continue; - - /* do not start event if parent or child event is still running */ - if (is_devpath_busy(manager, event)) - continue; - - event_run(manager, event); - } -} - -static void event_queue_cleanup(Manager *manager, enum event_state match_type) { - struct udev_list_node *loop, *tmp; - - udev_list_node_foreach_safe(loop, tmp, &manager->events) { - struct event *event = node_to_event(loop); - - if (match_type != EVENT_UNDEF && match_type != event->state) - continue; - - event_free(event); - } -} - -static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - for (;;) { - struct worker_message msg; - struct iovec iovec = { - .iov_base = &msg, - .iov_len = sizeof(msg), - }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control = {}; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - ssize_t size; - struct ucred *ucred = NULL; - struct worker *worker; - - size = recvmsg(fd, &msghdr, MSG_DONTWAIT); - if (size < 0) { - if (errno == EINTR) - continue; - else if (errno == EAGAIN) - /* nothing more to read */ - break; - - return log_error_errno(errno, "failed to receive message: %m"); - } else if (size != sizeof(struct worker_message)) { - log_warning_errno(EIO, "ignoring worker message with invalid size %zi bytes", size); - continue; - } - - 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 (!ucred || ucred->pid <= 0) { - log_warning_errno(EIO, "ignoring worker message without valid PID"); - continue; - } - - /* lookup worker who sent the signal */ - worker = hashmap_get(manager->workers, PID_TO_PTR(ucred->pid)); - if (!worker) { - log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid); - continue; - } - - if (worker->state != WORKER_KILLED) - worker->state = WORKER_IDLE; - - /* worker returned */ - event_free(worker->event); - } - - /* we have free workers, try to schedule events */ - event_queue_start(manager); - - return 1; -} - -static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *manager = userdata; - struct udev_device *dev; - int r; - - assert(manager); - - dev = udev_monitor_receive_device(manager->monitor); - if (dev) { - udev_device_ensure_usec_initialized(dev, NULL); - r = event_queue_insert(manager, dev); - if (r < 0) - udev_device_unref(dev); - else - /* we have fresh events, try to schedule them */ - event_queue_start(manager); - } - - return 1; -} - -/* receive the udevd message from userspace */ -static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *manager = userdata; - _cleanup_udev_ctrl_connection_unref_ struct udev_ctrl_connection *ctrl_conn = NULL; - _cleanup_udev_ctrl_msg_unref_ struct udev_ctrl_msg *ctrl_msg = NULL; - const char *str; - int i; - - assert(manager); - - ctrl_conn = udev_ctrl_get_connection(manager->ctrl); - if (!ctrl_conn) - return 1; - - ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); - if (!ctrl_msg) - return 1; - - i = udev_ctrl_get_set_log_level(ctrl_msg); - if (i >= 0) { - log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i); - log_set_max_level(i); - manager_kill_workers(manager); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { - log_debug("udevd message (STOP_EXEC_QUEUE) received"); - manager->stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { - log_debug("udevd message (START_EXEC_QUEUE) received"); - manager->stop_exec_queue = false; - event_queue_start(manager); - } - - if (udev_ctrl_get_reload(ctrl_msg) > 0) { - log_debug("udevd message (RELOAD) received"); - manager_reload(manager); - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str != NULL) { - _cleanup_free_ char *key = NULL; - - key = strdup(str); - if (key) { - char *val; - - val = strchr(key, '='); - if (val != NULL) { - val[0] = '\0'; - val = &val[1]; - if (val[0] == '\0') { - log_debug("udevd message (ENV) received, unset '%s'", key); - udev_list_entry_add(&manager->properties, key, NULL); - } else { - log_debug("udevd message (ENV) received, set '%s=%s'", key, val); - udev_list_entry_add(&manager->properties, key, val); - } - } else - log_error("wrong key format '%s'", key); - } - manager_kill_workers(manager); - } - - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - log_debug("udevd message (SET_MAX_CHILDREN) received, children_max=%i", i); - arg_children_max = i; - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) - log_debug("udevd message (SYNC) received"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { - log_debug("udevd message (EXIT) received"); - manager_exit(manager); - /* keep reference to block the client until we exit - TODO: deal with several blocking exit requests */ - manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn); - } - - return 1; -} - -static int synthesize_change(struct udev_device *dev) { - char filename[UTIL_PATH_SIZE]; - int r; - - if (streq_ptr("block", udev_device_get_subsystem(dev)) && - streq_ptr("disk", udev_device_get_devtype(dev)) && - !startswith(udev_device_get_sysname(dev), "dm-")) { - bool part_table_read = false; - bool has_partitions = false; - int fd; - struct udev *udev = udev_device_get_udev(dev); - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; - struct udev_list_entry *item; - - /* - * Try to re-read the partition table. This only succeeds if - * none of the devices is busy. The kernel returns 0 if no - * partition table is found, and we will not get an event for - * the disk. - */ - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); - if (fd >= 0) { - r = flock(fd, LOCK_EX|LOCK_NB); - if (r >= 0) - r = ioctl(fd, BLKRRPART, 0); - - close(fd); - if (r >= 0) - part_table_read = true; - } - - /* search for partitions */ - e = udev_enumerate_new(udev); - if (!e) - return -ENOMEM; - - r = udev_enumerate_add_match_parent(e, dev); - if (r < 0) - return r; - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return r; - - r = udev_enumerate_scan_devices(e); - if (r < 0) - return r; - - udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - continue; - - if (!streq_ptr("partition", udev_device_get_devtype(d))) - continue; - - has_partitions = true; - break; - } - - /* - * We have partitions and re-read the table, the kernel already sent - * out a "change" event for the disk, and "remove/add" for all - * partitions. - */ - if (part_table_read && has_partitions) - return 0; - - /* - * We have partitions but re-reading the partition table did not - * work, synthesize "change" for the disk and all partitions. - */ - log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - - udev_list_entry_foreach(item, udev_enumerate_get_list_entry(e)) { - _cleanup_udev_device_unref_ struct udev_device *d = NULL; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - continue; - - if (!streq_ptr("partition", udev_device_get_devtype(d))) - continue; - - log_debug("device %s closed, synthesising partition '%s' 'change'", - udev_device_get_devnode(dev), udev_device_get_devnode(d)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(d), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - } - - return 0; - } - - log_debug("device %s closed, synthesising 'change'", udev_device_get_devnode(dev)); - strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - write_string_file(filename, "change", WRITE_STRING_FILE_CREATE); - - return 0; -} - -static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Manager *manager = userdata; - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - assert(manager); - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - return 1; - - return log_error_errno(errno, "Failed to read inotify fd: %m"); - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - _cleanup_udev_device_unref_ struct udev_device *dev = NULL; - - dev = udev_watch_lookup(manager->udev, e->wd); - if (!dev) - continue; - - log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev)); - if (e->mask & IN_CLOSE_WRITE) { - synthesize_change(dev); - - /* settle might be waiting on us to determine the queue - * state. If we just handled an inotify event, we might have - * generated a "change" event, but we won't have queued up - * the resultant uevent yet. Do that. - */ - on_uevent(NULL, -1, 0, manager); - } else if (e->mask & IN_IGNORED) - udev_watch_end(manager->udev, dev); - } - - return 1; -} - -static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - manager_exit(manager); - - return 1; -} - -static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - manager_reload(manager); - - return 1; -} - -static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - Manager *manager = userdata; - - assert(manager); - - for (;;) { - pid_t pid; - int status; - struct worker *worker; - - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - - worker = hashmap_get(manager->workers, PID_TO_PTR(pid)); - if (!worker) { - log_warning("worker ["PID_FMT"] is unknown, ignoring", pid); - continue; - } - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) == 0) - log_debug("worker ["PID_FMT"] exited", pid); - else - log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - } else if (WIFSTOPPED(status)) { - log_info("worker ["PID_FMT"] stopped", pid); - continue; - } else if (WIFCONTINUED(status)) { - log_info("worker ["PID_FMT"] continued", pid); - continue; - } else - log_warning("worker ["PID_FMT"] exit with status 0x%04x", pid, status); - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (worker->event) { - log_error("worker ["PID_FMT"] failed while handling '%s'", pid, worker->event->devpath); - /* delete state from disk */ - udev_device_delete_db(worker->event->dev); - udev_device_tag_index(worker->event->dev, NULL, false); - /* forward kernel event without amending it */ - udev_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel); - } - } - - worker_free(worker); - } - - /* we can start new workers, try to schedule events */ - event_queue_start(manager); - - return 1; -} - -static int on_post(sd_event_source *s, void *userdata) { - Manager *manager = userdata; - int r; - - assert(manager); - - if (udev_list_node_is_empty(&manager->events)) { - /* no pending events */ - if (!hashmap_isempty(manager->workers)) { - /* there are idle workers */ - log_debug("cleanup idle workers"); - manager_kill_workers(manager); - } else { - /* we are idle */ - if (manager->exit) { - r = sd_event_exit(manager->event, 0); - if (r < 0) - return r; - } else if (manager->cgroup) - /* cleanup possible left-over processes in our cgroup */ - cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL); - } - } - - return 1; -} - -static int listen_fds(int *rctrl, int *rnetlink) { - _cleanup_udev_unref_ struct udev *udev = NULL; - int ctrl_fd = -1, netlink_fd = -1; - int fd, n, r; - - assert(rctrl); - assert(rnetlink); - - n = sd_listen_fds(true); - if (n < 0) - return n; - - for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { - if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { - if (ctrl_fd >= 0) - return -EINVAL; - ctrl_fd = fd; - continue; - } - - if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { - if (netlink_fd >= 0) - return -EINVAL; - netlink_fd = fd; - continue; - } - - return -EINVAL; - } - - if (ctrl_fd < 0) { - _cleanup_udev_ctrl_unref_ struct udev_ctrl *ctrl = NULL; - - udev = udev_new(); - if (!udev) - return -ENOMEM; - - ctrl = udev_ctrl_new(udev); - if (!ctrl) - return log_error_errno(EINVAL, "error initializing udev control socket"); - - r = udev_ctrl_enable_receiving(ctrl); - if (r < 0) - return log_error_errno(EINVAL, "error binding udev control socket"); - - fd = udev_ctrl_get_fd(ctrl); - if (fd < 0) - return log_error_errno(EIO, "could not get ctrl fd"); - - ctrl_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (ctrl_fd < 0) - return log_error_errno(errno, "could not dup ctrl fd: %m"); - } - - if (netlink_fd < 0) { - _cleanup_udev_monitor_unref_ struct udev_monitor *monitor = NULL; - - if (!udev) { - udev = udev_new(); - if (!udev) - return -ENOMEM; - } - - monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (!monitor) - return log_error_errno(EINVAL, "error initializing netlink socket"); - - (void) udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024); - - r = udev_monitor_enable_receiving(monitor); - if (r < 0) - return log_error_errno(EINVAL, "error binding netlink socket"); - - fd = udev_monitor_get_fd(monitor); - if (fd < 0) - return log_error_errno(netlink_fd, "could not get uevent fd: %m"); - - netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (ctrl_fd < 0) - return log_error_errno(errno, "could not dup netlink fd: %m"); - } - - *rctrl = ctrl_fd; - *rnetlink = netlink_fd; - - return 0; -} - -/* - * read the kernel command line, in case we need to get into debug mode - * udev.log-priority= syslog priority - * udev.children-max= events are fully serialized if set to 1 - * udev.exec-delay= delay execution of every executed program - * udev.event-timeout= seconds to wait before terminating an event - */ -static int parse_proc_cmdline_item(const char *key, const char *value) { - const char *full_key = key; - int r; - - assert(key); - - if (!value) - return 0; - - if (startswith(key, "rd.")) - key += strlen("rd."); - - if (startswith(key, "udev.")) - key += strlen("udev."); - else - return 0; - - if (streq(key, "log-priority")) { - int prio; - - prio = util_log_priority(value); - if (prio < 0) - goto invalid; - log_set_max_level(prio); - } else if (streq(key, "children-max")) { - r = safe_atou(value, &arg_children_max); - if (r < 0) - goto invalid; - } else if (streq(key, "exec-delay")) { - r = safe_atoi(value, &arg_exec_delay); - if (r < 0) - goto invalid; - } else if (streq(key, "event-timeout")) { - r = safe_atou64(value, &arg_event_timeout_usec); - if (r < 0) - goto invalid; - arg_event_timeout_usec *= USEC_PER_SEC; - arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; - } - - return 0; -invalid: - log_warning("invalid %s ignored: %s", full_key, value); - return 0; -} - -static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Manages devices.\n\n" - " -h --help Print this message\n" - " --version Print version of the program\n" - " --daemon Detach and run in the background\n" - " --debug Enable debug output\n" - " --children-max=INT Set maximum number of workers\n" - " --exec-delay=SECONDS Seconds to wait before executing RUN=\n" - " --event-timeout=SECONDS Seconds to wait before terminating an event\n" - " --resolve-names=early|late|never\n" - " When to resolve users and groups\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "event-timeout", required_argument, NULL, 't' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) { - int r; - - switch (c) { - - case 'd': - arg_daemonize = true; - break; - case 'c': - r = safe_atou(optarg, &arg_children_max); - if (r < 0) - log_warning("Invalid --children-max ignored: %s", optarg); - break; - case 'e': - r = safe_atoi(optarg, &arg_exec_delay); - if (r < 0) - log_warning("Invalid --exec-delay ignored: %s", optarg); - break; - case 't': - r = safe_atou64(optarg, &arg_event_timeout_usec); - if (r < 0) - log_warning("Invalid --event-timeout ignored: %s", optarg); - else { - arg_event_timeout_usec *= USEC_PER_SEC; - arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; - } - break; - case 'D': - arg_debug = true; - break; - case 'N': - if (streq(optarg, "early")) { - arg_resolve_names = 1; - } else if (streq(optarg, "late")) { - arg_resolve_names = 0; - } else if (streq(optarg, "never")) { - arg_resolve_names = -1; - } else { - log_error("resolve-names must be early, late or never"); - return 0; - } - break; - case 'h': - help(); - return 0; - case 'V': - printf("%s\n", VERSION); - return 0; - case '?': - return -EINVAL; - default: - assert_not_reached("Unhandled option"); - - } - } - - return 1; -} - -static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) { - _cleanup_(manager_freep) Manager *manager = NULL; - int r, fd_worker, one = 1; - - assert(ret); - assert(fd_ctrl >= 0); - assert(fd_uevent >= 0); - - manager = new0(Manager, 1); - if (!manager) - return log_oom(); - - manager->fd_inotify = -1; - manager->worker_watch[WRITE_END] = -1; - manager->worker_watch[READ_END] = -1; - - manager->udev = udev_new(); - if (!manager->udev) - return log_error_errno(errno, "could not allocate udev context: %m"); - - udev_builtin_init(manager->udev); - - manager->rules = udev_rules_new(manager->udev, arg_resolve_names); - if (!manager->rules) - return log_error_errno(ENOMEM, "error reading rules"); - - udev_list_node_init(&manager->events); - udev_list_init(manager->udev, &manager->properties, true); - - manager->cgroup = cgroup; - - manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl); - if (!manager->ctrl) - return log_error_errno(EINVAL, "error taking over udev control socket"); - - manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent); - if (!manager->monitor) - return log_error_errno(EINVAL, "error taking over netlink socket"); - - /* unnamed socket from workers to the main daemon */ - r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch); - if (r < 0) - return log_error_errno(errno, "error creating socketpair: %m"); - - fd_worker = manager->worker_watch[READ_END]; - - r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) - return log_error_errno(errno, "could not enable SO_PASSCRED: %m"); - - manager->fd_inotify = udev_watch_init(manager->udev); - if (manager->fd_inotify < 0) - return log_error_errno(ENOMEM, "error initializing inotify"); - - udev_watch_restore(manager->udev); - - /* block and listen to all signals on signalfd */ - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0); - - r = sd_event_default(&manager->event); - if (r < 0) - 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) - return log_error_errno(r, "error creating sigint event source: %m"); - - r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager); - if (r < 0) - return log_error_errno(r, "error creating sigterm event source: %m"); - - r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager); - if (r < 0) - return log_error_errno(r, "error creating sighup event source: %m"); - - r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager); - if (r < 0) - return log_error_errno(r, "error creating sigchld event source: %m"); - - r = sd_event_set_watchdog(manager->event, true); - if (r < 0) - return log_error_errno(r, "error creating watchdog event source: %m"); - - r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager); - if (r < 0) - return log_error_errno(r, "error creating ctrl event source: %m"); - - /* This needs to be after the inotify and uevent handling, to make sure - * that the ping is send back after fully processing the pending uevents - * (including the synthetic ones we may create due to inotify events). - */ - r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE); - if (r < 0) - return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m"); - - r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager); - if (r < 0) - return log_error_errno(r, "error creating inotify event source: %m"); - - r = sd_event_add_io(manager->event, &manager->uevent_event, fd_uevent, EPOLLIN, on_uevent, manager); - if (r < 0) - return log_error_errno(r, "error creating uevent event source: %m"); - - r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager); - if (r < 0) - return log_error_errno(r, "error creating worker event source: %m"); - - r = sd_event_add_post(manager->event, NULL, on_post, manager); - if (r < 0) - return log_error_errno(r, "error creating post event source: %m"); - - *ret = manager; - manager = NULL; - - return 0; -} - -static int run(int fd_ctrl, int fd_uevent, const char *cgroup) { - _cleanup_(manager_freep) Manager *manager = NULL; - int r; - - r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup); - if (r < 0) { - r = log_error_errno(r, "failed to allocate manager object: %m"); - goto exit; - } - - r = udev_rules_apply_static_dev_perms(manager->rules); - if (r < 0) - log_error_errno(r, "failed to apply permissions on static device nodes: %m"); - - (void) sd_notify(false, - "READY=1\n" - "STATUS=Processing..."); - - r = sd_event_loop(manager->event); - if (r < 0) { - log_error_errno(r, "event loop failed: %m"); - goto exit; - } - - sd_event_get_exit_code(manager->event, &r); - -exit: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - if (manager) - udev_ctrl_cleanup(manager->ctrl); - return r; -} - -int main(int argc, char *argv[]) { - _cleanup_free_ char *cgroup = NULL; - int fd_ctrl = -1, fd_uevent = -1; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto exit; - - r = parse_proc_cmdline(parse_proc_cmdline_item); - if (r < 0) - log_warning_errno(r, "failed to parse kernel command line, ignoring: %m"); - - if (arg_debug) { - log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); - } - - if (getuid() != 0) { - r = log_error_errno(EPERM, "root privileges required"); - goto exit; - } - - if (arg_children_max == 0) { - cpu_set_t cpu_set; - - arg_children_max = 8; - - if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0) - arg_children_max += CPU_COUNT(&cpu_set) * 2; - - log_debug("set children_max to %u", arg_children_max); - } - - /* set umask before creating any file/directory */ - r = chdir("/"); - if (r < 0) { - r = log_error_errno(errno, "could not change dir to /: %m"); - goto exit; - } - - umask(022); - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "could not initialize labelling: %m"); - goto exit; - } - - r = mkdir("/run/udev", 0755); - if (r < 0 && errno != EEXIST) { - r = log_error_errno(errno, "could not create /run/udev: %m"); - goto exit; - } - - dev_setup(NULL, UID_INVALID, GID_INVALID); - - if (getppid() == 1) { - /* get our own cgroup, we regularly kill everything udev has left behind - we only do this on systemd systems, and only if we are directly spawned - by PID1. otherwise we are not guaranteed to have a dedicated cgroup */ - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); - if (r < 0) { - if (r == -ENOENT || r == -ENOMEDIUM) - log_debug_errno(r, "did not find dedicated cgroup: %m"); - else - log_warning_errno(r, "failed to get cgroup: %m"); - } - } - - r = listen_fds(&fd_ctrl, &fd_uevent); - if (r < 0) { - r = log_error_errno(r, "could not listen on fds: %m"); - goto exit; - } - - if (arg_daemonize) { - pid_t pid; - - log_info("starting version " VERSION); - - /* connect /dev/null to stdin, stdout, stderr */ - if (log_get_max_level() < LOG_DEBUG) - (void) make_null_stdio(); - - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - r = log_error_errno(errno, "fork of daemon failed: %m"); - goto exit; - default: - mac_selinux_finish(); - log_close(); - _exit(EXIT_SUCCESS); - } - - setsid(); - - write_string_file("/proc/self/oom_score_adj", "-1000", 0); - } - - r = run(fd_ctrl, fd_uevent, cgroup); - -exit: - mac_selinux_finish(); - log_close(); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/udev/v4l_id/Makefile b/src/udev/v4l_id/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/udev/v4l_id/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c deleted file mode 100644 index aec6676a33..0000000000 --- a/src/udev/v4l_id/v4l_id.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2009 Kay Sievers - * Copyright (c) 2009 Filippo Argiolas - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details: - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fd-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - _cleanup_close_ int fd = -1; - char *device; - struct v4l2_capability v2cap; - int c; - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - case 'h': - printf("%s [-h,--help] \n\n" - "Video4Linux device identification.\n\n" - " -h Print this message\n" - , program_invocation_short_name); - return 0; - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - device = argv[optind]; - if (device == NULL) - return 2; - - fd = open(device, O_RDONLY); - if (fd < 0) - return 3; - - if (ioctl(fd, VIDIOC_QUERYCAP, &v2cap) == 0) { - printf("ID_V4L_VERSION=2\n"); - printf("ID_V4L_PRODUCT=%s\n", v2cap.card); - printf("ID_V4L_CAPABILITIES=:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) - printf("capture:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) - printf("video_output:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) - printf("video_overlay:"); - if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) - printf("audio:"); - if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) - printf("tuner:"); - if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) - printf("radio:"); - printf("\n"); - } - - return 0; -} diff --git a/src/update-done/Makefile b/src/update-done/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/update-done/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c deleted file mode 100644 index da306a4444..0000000000 --- a/src/update-done/update-done.c +++ /dev/null @@ -1,115 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include "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" \ - "purpose is to hold a timestamp of the time this directory\n" \ - "was updated. See systemd-update-done.service(8).\n" - -static int apply_timestamp(const char *path, struct timespec *ts) { - struct timespec twice[2] = { - *ts, - *ts - }; - struct stat st; - - assert(path); - assert(ts); - - if (stat(path, &st) >= 0) { - /* Is the timestamp file already newer than the OS? If - * so, there's nothing to do. We ignore the nanosecond - * component of the timestamp, since some file systems - * do not support any better accuracy than 1s and we - * have no way to identify the accuracy - * available. Most notably ext4 on small disks (where - * 128 byte inodes are used) does not support better - * accuracy than 1s. */ - if (st.st_mtim.tv_sec > ts->tv_sec) - return 0; - - /* It is older? Then let's update it */ - if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { - - if (errno == EROFS) - return log_debug("Can't update timestamp file %s, file system is read-only.", path); - - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } - - } else if (errno == ENOENT) { - _cleanup_close_ int fd = -1; - int r; - - /* The timestamp file doesn't exist yet? Then let's create it. */ - - r = mac_selinux_create_file_prepare(path, S_IFREG); - if (r < 0) - return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - - fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); - mac_selinux_create_file_clear(); - - if (fd < 0) { - if (errno == EROFS) - return log_debug("Can't create timestamp file %s, file system is read-only.", path); - - return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); - } - - (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); - - if (futimens(fd, twice) < 0) - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - } else - log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); - - return 0; -} - -int main(int argc, char *argv[]) { - struct stat st; - int r, q = 0; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - if (stat("/usr", &st) < 0) { - log_error_errno(errno, "Failed to stat /usr: %m"); - return EXIT_FAILURE; - } - - r = mac_selinux_init(); - if (r < 0) { - log_error_errno(r, "SELinux setup failed: %m"); - goto finish; - } - - r = apply_timestamp("/etc/.updated", &st.st_mtim); - q = apply_timestamp("/var/.updated", &st.st_mtim); - -finish: - return r < 0 || q < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/update-utmp/Makefile b/src/update-utmp/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/update-utmp/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c deleted file mode 100644 index 8ae4a8a833..0000000000 --- a/src/update-utmp/update-utmp.c +++ /dev/null @@ -1,283 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#ifdef HAVE_AUDIT -#include -#endif - -#include "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 "special.h" -#include "unit-name.h" -#include "util.h" -#include "utmp-wtmp.h" - -typedef struct Context { - sd_bus *bus; -#ifdef HAVE_AUDIT - int audit_fd; -#endif -} Context; - -static usec_t get_startup_time(Context *c) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - usec_t t = 0; - int r; - - assert(c); - - r = sd_bus_get_property_trivial( - c->bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "UserspaceTimestamp", - &error, - 't', &t); - if (r < 0) { - log_error_errno(r, "Failed to get timestamp: %s", bus_error_message(&error, r)); - return 0; - } - - return t; -} - -static int get_current_runlevel(Context *c) { - static const struct { - const int runlevel; - const char *special; - } table[] = { - /* The first target of this list that is active or has - * a job scheduled wins. We prefer runlevels 5 and 3 - * here over the others, since these are the main - * runlevels used on Fedora. It might make sense to - * change the order on some distributions. */ - { '5', SPECIAL_GRAPHICAL_TARGET }, - { '3', SPECIAL_MULTI_USER_TARGET }, - { '1', SPECIAL_RESCUE_TARGET }, - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - unsigned i; - - assert(c); - - for (i = 0; i < ELEMENTSOF(table); i++) { - _cleanup_free_ char *state = NULL, *path = NULL; - - path = unit_dbus_path_from_name(table[i].special); - if (!path) - return log_oom(); - - r = sd_bus_get_property_string( - c->bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &state); - if (r < 0) - return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r)); - - if (streq(state, "active") || streq(state, "reloading")) - return table[i].runlevel; - } - - return 0; -} - -static int on_reboot(Context *c) { - int r = 0, q; - usec_t t; - - assert(c); - - /* We finished start-up, so let's write the utmp - * record and send the audit msg */ - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - /* If this call fails it will return 0, which - * utmp_put_reboot() will then fix to the current time */ - t = get_startup_time(c); - - q = utmp_put_reboot(t); - if (q < 0) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -static int on_shutdown(Context *c) { - int r = 0, q; - - assert(c); - - /* We started shut-down, so let's write the utmp - * record and send the audit msg */ - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && - errno != EPERM) { - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - q = utmp_put_shutdown(); - if (q < 0) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -static int on_runlevel(Context *c) { - int r = 0, q, previous, runlevel; - - assert(c); - - /* We finished changing runlevel, so let's write the - * utmp record and send the audit msg */ - - /* First, get last runlevel */ - q = utmp_get_runlevel(&previous, NULL); - - if (q < 0) { - if (q != -ESRCH && q != -ENOENT) - return log_error_errno(q, "Failed to get current runlevel: %m"); - - previous = 0; - } - - /* Secondly, get new runlevel */ - runlevel = get_current_runlevel(c); - - if (runlevel < 0) - return runlevel; - - if (previous == runlevel) - return 0; - -#ifdef HAVE_AUDIT - if (c->audit_fd >= 0) { - _cleanup_free_ char *s = NULL; - - if (asprintf(&s, "old-level=%c new-level=%c", - previous > 0 ? previous : 'N', - runlevel > 0 ? runlevel : 'N') < 0) - return log_oom(); - - if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) - r = log_error_errno(errno, "Failed to send audit message: %m"); - } -#endif - - q = utmp_put_runlevel(runlevel, previous); - if (q < 0 && q != -ESRCH && q != -ENOENT) { - log_error_errno(q, "Failed to write utmp record: %m"); - r = q; - } - - return r; -} - -int main(int argc, char *argv[]) { - Context c = { -#ifdef HAVE_AUDIT - .audit_fd = -1 -#endif - }; - int r; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - -#ifdef HAVE_AUDIT - /* If the kernel lacks netlink or audit support, - * don't worry about it. */ - c.audit_fd = audit_open(); - if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error_errno(errno, "Failed to connect to audit log: %m"); -#endif - r = bus_connect_system_systemd(&c.bus); - if (r < 0) { - log_error_errno(r, "Failed to get D-Bus connection: %m"); - r = -EIO; - goto finish; - } - - log_debug("systemd-update-utmp running as pid "PID_FMT, getpid()); - - if (streq(argv[1], "reboot")) - r = on_reboot(&c); - else if (streq(argv[1], "shutdown")) - r = on_shutdown(&c); - else if (streq(argv[1], "runlevel")) - r = on_runlevel(&c); - else { - log_error("Unknown command %s", argv[1]); - r = -EINVAL; - } - - log_debug("systemd-update-utmp stopped as pid "PID_FMT, getpid()); - -finish: -#ifdef HAVE_AUDIT - if (c.audit_fd >= 0) - audit_close(c.audit_fd); -#endif - - sd_bus_flush_close_unref(c.bus); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/user-sessions/Makefile b/src/user-sessions/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/user-sessions/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c deleted file mode 100644 index 9b29b5ba1d..0000000000 --- a/src/user-sessions/user-sessions.c +++ /dev/null @@ -1,84 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include "fileio.h" -#include "fileio-label.h" -#include "log.h" -#include "selinux-util.h" -#include "string-util.h" -#include "util.h" - -int main(int argc, char*argv[]) { - - if (argc != 2) { - log_error("This program requires one argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - mac_selinux_init(); - - if (streq(argv[1], "start")) { - int r = 0; - - if (unlink("/run/nologin") < 0 && errno != ENOENT) - r = log_error_errno(errno, - "Failed to remove /run/nologin file: %m"); - - if (unlink("/etc/nologin") < 0 && errno != ENOENT) { - /* If the file doesn't exist and /etc simply - * was read-only (in which case unlink() - * returns EROFS even if the file doesn't - * exist), don't complain */ - - if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) { - log_error_errno(errno, "Failed to remove /etc/nologin file: %m"); - return EXIT_FAILURE; - } - } - - if (r < 0) - return EXIT_FAILURE; - - } else if (streq(argv[1], "stop")) { - int r; - - r = write_string_file_atomic_label("/run/nologin", "System is going down."); - if (r < 0) { - log_error_errno(r, "Failed to create /run/nologin: %m"); - return EXIT_FAILURE; - } - - } else { - log_error("Unknown verb %s.", argv[1]); - return EXIT_FAILURE; - } - - mac_selinux_finish(); - - return EXIT_SUCCESS; -} diff --git a/src/vconsole/.gitignore b/src/vconsole/.gitignore deleted file mode 100644 index 82741b2fb3..0000000000 --- a/src/vconsole/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/90-vconsole.rules diff --git a/src/vconsole/90-vconsole.rules.in b/src/vconsole/90-vconsole.rules.in deleted file mode 100644 index 35b9ad5151..0000000000 --- a/src/vconsole/90-vconsole.rules.in +++ /dev/null @@ -1,10 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -# Each vtcon keeps its own state of fonts. -# -ACTION=="add", SUBSYSTEM=="vtconsole", KERNEL=="vtcon*", RUN+="@rootlibexecdir@/systemd-vconsole-setup" diff --git a/src/vconsole/Makefile b/src/vconsole/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/vconsole/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c deleted file mode 100644 index 1118118450..0000000000 --- a/src/vconsole/vconsole-setup.c +++ /dev/null @@ -1,332 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "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 "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]; - - data[0] = TIOCL_GETFGCONSOLE; - return ioctl(fd, TIOCLINUX, data) >= 0; -} - -static int disable_utf8(int fd) { - int r = 0, k; - - if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) - r = -errno; - - k = loop_write(fd, "\033%@", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); - if (k < 0) - r = k; - - if (r < 0) - log_warning_errno(r, "Failed to disable UTF-8: %m"); - - return r; -} - -static int enable_utf8(int fd) { - int r = 0, k; - long current = 0; - - if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { - /* - * Change the current keyboard to unicode, unless it - * is currently in raw or off mode anyway. We - * shouldn't interfere with X11's processing of the - * key events. - * - * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html - * - */ - - if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) - r = -errno; - } - - k = loop_write(fd, "\033%G", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); - if (k < 0) - r = k; - - if (r < 0) - log_warning_errno(r, "Failed to enable UTF-8: %m"); - - return r; -} - -static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { - const char *args[8]; - int i = 0, r; - pid_t pid; - - /* An empty map means kernel map */ - if (isempty(map)) - return 1; - - args[i++] = KBD_LOADKEYS; - args[i++] = "-q"; - args[i++] = "-C"; - args[i++] = vc; - if (utf8) - args[i++] = "-u"; - args[i++] = map; - if (map_toggle) - args[i++] = map_toggle; - args[i++] = NULL; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execv(args[0], (char **) args); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); - if (r < 0) - return r; - - return r == 0; -} - -static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { - const char *args[9]; - int i = 0, r; - pid_t pid; - - /* An empty font means kernel font */ - if (isempty(font)) - return 1; - - args[i++] = KBD_SETFONT; - args[i++] = "-C"; - args[i++] = vc; - args[i++] = font; - if (map) { - args[i++] = "-m"; - args[i++] = map; - } - if (unimap) { - args[i++] = "-u"; - args[i++] = unimap; - } - args[i++] = NULL; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - execv(args[0], (char **) args); - _exit(EXIT_FAILURE); - } - - r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); - if (r < 0) - return r; - - return r == 0; -} - -/* - * A newly allocated VT uses the font from the active VT. Here - * we update all possibly already allocated VTs with the configured - * font. It also allows to restart systemd-vconsole-setup.service, - * to apply a new font to all VTs. - */ -static void font_copy_to_all_vcs(int fd) { - struct vt_stat vcs = {}; - unsigned char map8[E_TABSZ]; - unsigned short map16[E_TABSZ]; - struct unimapdesc unimapd; - _cleanup_free_ struct unipair* unipairs = NULL; - int i, r; - - unipairs = new(struct unipair, USHRT_MAX); - if (!unipairs) { - log_oom(); - return; - } - - /* get active, and 16 bit mask of used VT numbers */ - r = ioctl(fd, VT_GETSTATE, &vcs); - if (r < 0) { - log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); - return; - } - - for (i = 1; i <= 15; i++) { - char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int vcfd = -1; - struct console_font_op cfo = {}; - - if (i == vcs.v_active) - continue; - - /* skip non-allocated ttys */ - xsprintf(vcname, "/dev/vcs%i", i); - if (access(vcname, F_OK) < 0) - continue; - - xsprintf(vcname, "/dev/tty%i", i); - vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); - if (vcfd < 0) - continue; - - /* copy font from active VT, where the font was uploaded to */ - cfo.op = KD_FONT_OP_COPY; - cfo.height = vcs.v_active-1; /* tty1 == index 0 */ - (void) ioctl(vcfd, KDFONTOP, &cfo); - - /* copy map of 8bit chars */ - if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) - (void) ioctl(vcfd, PIO_SCRNMAP, map8); - - /* copy map of 8bit chars -> 16bit Unicode values */ - if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) - (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); - - /* copy unicode translation table */ - /* unimapd is a ushort count and a pointer to an - array of struct unipair { ushort, ushort } */ - unimapd.entries = unipairs; - unimapd.entry_ct = USHRT_MAX; - if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { - struct unimapinit adv = { 0, 0, 0 }; - - (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); - (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); - } - } -} - -int main(int argc, char **argv) { - const char *vc; - _cleanup_free_ char - *vc_keymap = NULL, *vc_keymap_toggle = NULL, - *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; - _cleanup_close_ int fd = -1; - bool utf8, font_copy = false, font_ok, keyboard_ok; - int r = EXIT_FAILURE; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argv[1]) - vc = argv[1]; - else { - vc = "/dev/tty0"; - font_copy = true; - } - - fd = open_terminal(vc, O_RDWR|O_CLOEXEC); - if (fd < 0) { - log_error_errno(fd, "Failed to open %s: %m", vc); - return EXIT_FAILURE; - } - - if (!is_vconsole(fd)) { - log_error("Device %s is not a virtual console.", vc); - return EXIT_FAILURE; - } - - utf8 = is_locale_utf8(); - - r = parse_env_file("/etc/vconsole.conf", NEWLINE, - "KEYMAP", &vc_keymap, - "KEYMAP_TOGGLE", &vc_keymap_toggle, - "FONT", &vc_font, - "FONT_MAP", &vc_font_map, - "FONT_UNIMAP", &vc_font_unimap, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); - - /* Let the kernel command line override /etc/vconsole.conf */ - if (detect_container() <= 0) { - r = parse_env_file("/proc/cmdline", WHITESPACE, - "vconsole.keymap", &vc_keymap, - "vconsole.keymap.toggle", &vc_keymap_toggle, - "vconsole.font", &vc_font, - "vconsole.font.map", &vc_font_map, - "vconsole.font.unimap", &vc_font_unimap, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read /proc/cmdline: %m"); - } - - if (utf8) - (void) enable_utf8(fd); - else - (void) disable_utf8(fd); - - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; - keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; - - /* Only copy the font when we executed setfont successfully */ - if (font_copy && font_ok) - (void) font_copy_to_all_vcs(fd); - - return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/zsh-completion/_sd_hosts_or_user_at_host b/src/zsh-completion/_sd_hosts_or_user_at_host new file mode 100644 index 0000000000..282f7328e4 --- /dev/null +++ b/src/zsh-completion/_sd_hosts_or_user_at_host @@ -0,0 +1,5 @@ +#autoload + +_alternative \ + 'users-hosts:: _user_at_host' \ + 'hosts:: _hosts' diff --git a/src/zsh-completion/_sd_machines b/src/zsh-completion/_sd_machines new file mode 100644 index 0000000000..a0039ee0f8 --- /dev/null +++ b/src/zsh-completion/_sd_machines @@ -0,0 +1,13 @@ +#autoload +__get_machines () { + machinectl --full --no-legend --no-pager list | {while read -r a b; do echo $a; done;}; +} + +local -a _machines +_machines=("${(fo)$(__get_machines)}") +typeset -U _machines +if [[ -n "$_machines" ]]; then + _describe 'machines' _machines +else + _message 'no machines' +fi diff --git a/src/zsh-completion/_sd_outputmodes b/src/zsh-completion/_sd_outputmodes new file mode 100644 index 0000000000..3836f79b73 --- /dev/null +++ b/src/zsh-completion/_sd_outputmodes @@ -0,0 +1,5 @@ +#autoload + +local -a _output_opts +_output_opts=(short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat) +_describe -t output 'output mode' _output_opts || compadd "$@" diff --git a/src/zsh-completion/_sd_unit_files b/src/zsh-completion/_sd_unit_files new file mode 100644 index 0000000000..3e7a4ee803 --- /dev/null +++ b/src/zsh-completion/_sd_unit_files @@ -0,0 +1,9 @@ +#autoload + +_sd_unit_files() { + local files expl + files=( '*:files:->files' ) + + _description files expl 'unit file' + _files "$expl[@]" -g '*.(automount|busname|device|mount|path|service|socket|swap|target|timer)' +} -- cgit v1.2.3-54-g00ecf